pax_global_header00006660000000000000000000000064151070554010014510gustar00rootroot0000000000000052 comment=400484b7a6a8cc08e805b88e578134a066a801e0 cangjie_compiler-1.0.7/000077500000000000000000000000001510705540100150075ustar00rootroot00000000000000cangjie_compiler-1.0.7/.clang-format000066400000000000000000000065661510705540100173770ustar00rootroot00000000000000--- Language: Cpp AccessModifierOffset: -4 AlignAfterOpenBracket: DontAlign AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlines: Right AlignOperands: false AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: None AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: MultiLine BinPackArguments: true BinPackParameters: true BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false AfterFunction: true AfterNamespace: false AfterObjCDeclaration: false AfterStruct: false AfterUnion: false AfterExternBlock: false BeforeCatch: false BeforeElse: false IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom BreakBeforeInheritanceComma: false BreakInheritanceList: BeforeColon BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: BeforeColon BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true ColumnLimit: 120 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH IncludeBlocks: Preserve IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 - Regex: '^(<|"(gtest|gmock|isl|json)/)' Priority: 3 - Regex: '.*' Priority: 1 IncludeIsMainRegex: '(Test)?$' IndentCaseLabels: true IndentPPDirectives: None IndentWidth: 4 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBinPackProtocolList: Auto ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Left ReflowComments: true SortIncludes: true SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 StatementMacros: - Q_UNUSED - QT_REQUIRE_VERSION TabWidth: 8 UseTab: Never ... cangjie_compiler-1.0.7/.gitcode/000077500000000000000000000000001510705540100165035ustar00rootroot00000000000000cangjie_compiler-1.0.7/.gitcode/CODEOWNERS000066400000000000000000000002321510705540100200730ustar00rootroot00000000000000/.gitcode/ @xuanjz /cmake/ @neilliuistaken /demangler/ @neilliuistaken *.md @amy_yun @liujianing build.py @neilliuistaken /integration_build/ @zichexuelancangjie_compiler-1.0.7/.gitcode/ISSUE_TEMPLATE/000077500000000000000000000000001510705540100206665ustar00rootroot00000000000000cangjie_compiler-1.0.7/.gitcode/ISSUE_TEMPLATE/bug-report.yml000066400000000000000000000030621510705540100235000ustar00rootroot00000000000000name: 缺陷反馈|Bug description: 当您发现了一个缺陷,需要向社区反馈时,请使用此模板。 title: "[Bug]: " labels: ["bug"] body: - type: markdown attributes: value: | 感谢对仓颉社区的支持与关注,欢迎反馈缺陷。 - type: textarea attributes: label: 发生了什么问题? description: 提供尽可能多的信息描述产生了什么问题: placeholder: "" validations: required: true - type: textarea attributes: label: 期望行为是什么? description: 描述期望行为或效果应如何: placeholder: "" validations: required: true - type: textarea attributes: label: 如何复现该缺陷 description: 提供尽可能多的信息描述如何复现该缺陷: validations: required: true - type: textarea attributes: label: 其他补充信息 description: 补充下其他您认为需要提供的信息: validations: required: false - type: textarea attributes: label: cjc版本信息 description: 请将cjc -v的输出结果以图片或者文字的形式填写在下方: placeholder: "" validations: required: true - type: checkboxes attributes: label: 分支版本信息 description: 在哪些分支版本存在该缺陷? options: - label: dev required: false - label: main required: false - label: release/1.0 required: false validations: required: truecangjie_compiler-1.0.7/.gitcode/ISSUE_TEMPLATE/config.yml000066400000000000000000000000331510705540100226520ustar00rootroot00000000000000blank_issues_enabled: falsecangjie_compiler-1.0.7/.gitcode/ISSUE_TEMPLATE/feature-request.yml000066400000000000000000000017441510705540100245400ustar00rootroot00000000000000name: 新需求|Feature description: 您需要反馈或实现一个新需求时,使用此模板。 title: "[新需求]: " labels: ["enhancement"] body: - type: markdown attributes: value: | 感谢提出新需求。 - type: textarea attributes: label: 新需求提供了什么功能? description: 请描述下新需求的功能是什么,解决了什么问题: validations: required: true - type: textarea attributes: label: 该需求带来的价值、应用场景? description: 请描述下该需求的价值,应用场景等: validations: required: false - type: checkboxes id: version attributes: label: 新需求涉及的分支版本 description: 请选择涉及的分支版本: options: - label: dev required: false - label: main required: false - label: release/1.0 required: false validations: required: truecangjie_compiler-1.0.7/.gitcode/ISSUE_TEMPLATE/question.yml000066400000000000000000000013721510705540100232630ustar00rootroot00000000000000name: 问题咨询|Question description: 如果您对Cangjie社区有疑问,欢迎反馈咨询。 title: "[问题咨询]: " labels: ["question"] body: - type: markdown attributes: value: | 感谢提出问题,我们将安排人答复! - type: textarea attributes: label: 问题描述 description: 请描述下您的问题 validations: required: true - type: checkboxes id: version attributes: label: 受影响的分支版本 description: 请选择您的问题涉及的分支版本: options: - label: dev required: false - label: main required: false - label: release/1.0 required: false validations: required: true cangjie_compiler-1.0.7/.gitcode/PULL_REQUEST_TEMPLATE/000077500000000000000000000000001510705540100217625ustar00rootroot00000000000000cangjie_compiler-1.0.7/.gitcode/PULL_REQUEST_TEMPLATE/PULL_REQUEST_TEMPLATE.md000066400000000000000000000021031510705540100255570ustar00rootroot00000000000000## Change Details (Required) Please describe the changes in this Pull Request. ## Change Type (Required) Please describe the type of changes in this Pull Request (reason). **Simply save and click the checkbox, or when editing, change `[ ]` to `[x]` for the relevant item.** - [ ] Feature - [ ] Bugfix - [ ] Build Process or Auxiliary Tool Changes - [ ] Documentation Update ## Self-Check of Changes (Required) **Please do not modify or delete the following options. Simply save and click the checkbox, or when editing, change `[ ]` to `[x]` for the relevant item.** ### Local Compilation Verification Results: - [ ] Cangjie Compiler compiled successfully - [ ] Cangjie Runtime compiled successfully - [ ] Cangjie Standard Library compiled successfully - [ ] Not applicable (select this option for auxiliary tool changes or documentation updates) ### Local Test Case Verification Results: - [ ] Pass. Please provide screenshots below. - [ ] Not applicable. Please explain the reasons below. ## Related Issues (Required) Please provide links to issues related to this Pull Request.cangjie_compiler-1.0.7/.gitcode/PULL_REQUEST_TEMPLATE/PULL_REQUEST_TEMPLATE_chinese.md000066400000000000000000000016731510705540100272700ustar00rootroot00000000000000## 变更内容(必填) 请描述本次Pull Request的变更内容。 ## 变更类型(必填) 请描述本次Pull Request变更类型(原因),**请在保存后点击复选框或在编辑时将对应项前面的 `[ ]` 改为 `[X]` 。** - [ ] 新增需求 - [ ] 问题修复 - [ ] 构建过程或辅助工具变动 - [ ] 文档更新 ## 变更内容自检(必填) **请勿修改或删除以下选项内容,请在保存后点击复选框或在编辑时将对应项前面的 `[ ]` 改为 `[X]` 。** ### 编译本地自验证结果: - [ ] 仓颉编译器编译通过 - [ ] 仓颉运行时编译通过 - [ ] 仓颉标准库编译通过 - [ ] 不涉及,辅助工具变动或者文档更新可以直接选择此项 ### 测试用例本地自验证结果: - [ ] 通过,请在下方提供截图证明 - [ ] 不涉及,请在下方说明情况 ## 关联的issue(必填) 请填写此Pull Request要关联的issue链接。cangjie_compiler-1.0.7/.gitignore000066400000000000000000000015661510705540100170070ustar00rootroot00000000000000.idea/* **/dist/* **/build/* cmake-build-debug/* output output-* build *.swp *.swo .json **/__pycache__/ *.pyc *.exe # Cangjie generated files. **/*.cached/* **/.*_Cache/* *.ast *.cjo *.o *.bc *.cbc *.bchir *.map *.prof *.macrocall # Various tag programs /tags /TAGS /GPATH /GRTAGS /GSYMS /GTAGS .gitusers autom4te.cache cscope.files cscope.out autoconf/aclocal.m4 autoconf/autom4te.cache /compile_commands.json # VS2017 and VSCode config files. .vscode .vs # clangd index .clangd .ccls-cache .cache # emacs *~ \#*\# .\#* # ccls .ccls # gdb **/.gdb_history third_party/flatbuffers third_party/boundscheck* third_party/llvm-project third_party/runtime third_party/pcre2-10.44-h1 # flatc generated file libs/std/ast/NodeFormat_generated.cj libs/std/ast/nodeformat_generated.cj libs/std/ast/table.cj libs/std/ast/flatbuffer_object.cj libs/std/ast/decode.cj # vscode .VSCodeCounter/ cangjie_compiler-1.0.7/CMakeLists.txt000066400000000000000000000334511510705540100175550ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. cmake_minimum_required(VERSION 3.16.5) project(cangjie) set(CJ_SDK_VERSION "1.0.7") # Add path for custom CMake modules and add cmake modules list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_EXTENSIONS OFF) set(CJNATIVE_BACKEND "cjnative") if(NOT CMAKE_INCLUDE_SYSTEM_FLAG_CXX) set(CXX_SYSTEM_INCLUDE_CONFIGURATION_FLAG /experimental:external /external:W0) set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX /external:I) endif() if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "No build type selected, default to Debug") set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build type (default Debug)" FORCE) endif() string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) if(CMAKE_BUILD_TYPE AND NOT ("${uppercase_CMAKE_BUILD_TYPE}" MATCHES "^(DEBUG|RELEASE|RELWITHDEBINFO|MINSIZEREL|MINSIZERELWITHDEBINFO)$")) message(FATAL_ERROR "Invalid value for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") endif() if(CMAKE_BUILD_TYPE AND ("${uppercase_CMAKE_BUILD_TYPE}" MATCHES "^(RELEASE)$") AND NOT CMAKE_ENABLE_ASSERT) add_compile_definitions(CANGJIE_CHIR_WFC_OFF) endif() set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Because stdlib is currently compiled very slowly, add this to avoid recompiling stdlib when install. set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY TRUE) option(CMAKE_ENABLE_ASSERT "Enable the assert and checking" OFF) option(CANGJIE_CODEGEN_CJNATIVE_BACKEND "Build a version for CJNATIVE backend" ON) option(CANGJIE_BUILD_TESTS "Build cangjie tests" ON) option(CANGJIE_BUILD_CJC "Build cangjie compiler" ON) option(CANGJIE_SKIP_BUILD_CLANG_RT "Do not build clang_rt libraries, only for cross-compiling" OFF) option(CANGJIE_BUILD_STD_SUPPORT "Build cangjie depndency of libast" ON) option(CANGJIE_ENABLE_CCACHE "Build cangjie with ccache" OFF) option(CANGJIE_DOWNLOAD_FLATBUFFERS "Download flatbuffers" ON) option(COMPILER_EXPLORER_RACE_FIX "Enable patched behaviour specific to CE" OFF) option(CANGJIE_DISABLE_STACK_GROW_FEATURE "Disable stack grow feature for cjnative BE" OFF) option(CANGJIE_ENABLE_COMPILER_TSAN "Enable tsan for compiler and tools (relwithbebinfo or debug, Linux builds only)" OFF) option(CANGJIE_GENERATE_UNICODE_TABLE "Regenerated unicode data tables (should be used only when the Unicode standard cangjie conforms to changes)" OFF) option(CANGJIE_WRITE_PROFILE "`--profile-compile-time` and `--profile-compile-memory` options for cjc are supported even in release, and write result into PACKAGE_NAME.cj.prof and PACKAGE_NAME.cj.mem.prof." OFF) option(CANGJIE_ENABLE_ASAN_COV "build with asan and sanitize-coverage, used for cjc_fuzz and lsp_test" OFF) option(CANGJIE_VISIBLE_OPTIONS_ONLY "open CANGJIE_VISIBLE_OPTIONS_ONLY to only build options that are visible to users" ON) option(CANGJIE_USE_OH_LLVM_REPO "use OpenHarmony llvm repo with Cangjie llvm patch instead of cangjie llvm repo for building" OFF) set(CANGJIE_LLVM_BUILD_TYPE Default CACHE STRING "Build type for llvm") set(CANGJIE_CJDB_BUILD_TYPE Default CACHE STRING "Build type for cjdb") message(STATUS "Build type for the current project: ${CMAKE_BUILD_TYPE}") if(CANGJIE_LLVM_BUILD_TYPE MATCHES "^Default$") set(CANGJIE_LLVM_BUILD_TYPE Release) endif() message(STATUS "Build type for llvm: ${CANGJIE_LLVM_BUILD_TYPE}") if(CANGJIE_CJDB_BUILD_TYPE MATCHES "^Default$") set(CANGJIE_CJDB_BUILD_TYPE ${CMAKE_BUILD_TYPE}) endif() message(STATUS "Build type for cjdb (llvm excluded): ${CANGJIE_CJDB_BUILD_TYPE}") if(CANGJIE_BUILD_CJDB AND CANGJIE_CJDB_BUILD_TYPE MATCHES "^Debug$" AND NOT CANGJIE_LLVM_BUILD_TYPE MATCHES "^Debug$") message(WARNING "Build type of cjdb differs from build type of llvm, some parts of cjdb may not debuggable.") endif() if(NOT DEFINED CANGJIE_CJNATIVE_SOURCE_DIR) set(CANGJIE_CJNATIVE_SOURCE_DIR ${CMAKE_SOURCE_DIR}/third_party/llvm-project) endif() set(BOUNDSCHECK ${CMAKE_SOURCE_DIR}/third_party/boundscheck) if(NOT DEFINED CANGJIE_XML2_SOURCE_DIR) set(CANGJIE_XML2_SOURCE_DIR ${CMAKE_SOURCE_DIR}/third_party/libxml2-2.14.0) endif() if(NOT EXISTS ${BOUNDSCHECK}) set(REPOSITORY_PATH https://gitcode.com/openharmony/third_party_bounds_checking_function.git) message(STATUS "Set boundscheck REPOSITORY_PATH: ${REPOSITORY_PATH}") execute_process( COMMAND git clone --branch OpenHarmony-v6.0-Release ${REPOSITORY_PATH} ${BOUNDSCHECK} ) endif() file(COPY ${CMAKE_SOURCE_DIR}/third_party/cmake/CMakeLists.txt DESTINATION ${BOUNDSCHECK}/) if(CANGJIE_ENABLE_COMPILER_TSAN) if(NOT CANGJIE_BUILD_CJC) message(FATAL_ERROR "CANGJIE_ENABLE_COMPILER_TSAN option is only available while building CJC") endif() if(MINGW) message(FATAL_ERROR "CANGJIE_ENABLE_COMPILER_TSAN option does not support windows target currently") endif() endif() set(CANGJIE_ASAN_SUPPORT OFF) set(CANGJIE_TSAN_SUPPORT OFF) set(CANGJIE_HWASAN_SUPPORT OFF) set(CANGJIE_SANITIZER_SUPPORT_ENABLED OFF) if(CANGJIE_SANITIZER_SUPPORT_ENABLED) if(NOT CANGJIE_CODEGEN_CJNATIVE_BACKEND) message(FATAL_ERROR "Cangjie with sanitizer support is supposed to be built in cjnative backend") endif() if(CANGJIE_ENABLE_ASAN OR CANGJIE_ENABLE_ASAN_COV) message(FATAL_ERROR "Cangjie with sanitizer support is not compatible with asan build version") endif() if(CANGJIE_BUILD_CJC) message(FATAL_ERROR "Cangjie with sanitizer support is not compatible while building cjc") endif() if(NOT CANGJIE_BUILD_STD_SUPPORT) message(FATAL_ERROR "Please build sanitizer support version with CANGJIE_BUILD_STD_SUPPORT") endif() endif() if(CANGJIE_ENABLE_HWASAN) add_compile_options(-mllvm -hwasan-globals=0) endif() if(CANGJIE_ASAN_SUPPORT) if(WIN32) message(FATAL_ERROR "Cangjie with asan support only supports on Linux/OHOS platform") endif() # Here just use add_compile_options for asan instrumentation # do not use add_link_options(-fsanitize=address) which will link library while using gcc specifically add_compile_options(-fsanitize=address -fno-omit-frame-pointer) set(SANITIZER_SUBPATH /asan) elseif(CANGJIE_TSAN_SUPPORT) if(OHOS OR CMAKE_CROSSCOMPILING) message(FATAL_ERROR "Cangjie with tsan support only supports on Linux/Windows platform") endif() if(CANGJIE_SKIP_BUILD_CLANG_RT) message(FATAL_ERROR "You have to build compiler-rt locally") endif() set(SANITIZER_SUBPATH /tsan) elseif(CANGJIE_HWASAN_SUPPORT) if(WIN32) message(FATAL_ERROR "Cangjie with asan support only supports on Linux/OHOS platform") endif() add_compile_options(-fsanitize=hwaddress -fno-omit-frame-pointer) set(SANITIZER_SUBPATH /hwasan) endif() set(CANGJIE_CJ_SANCOV_USE_8BIT OFF) set(CANGJIE_BUILD_STDLIB_WITH_CJ_SANCOV OFF) if(CANGJIE_BUILD_STDLIB_WITH_CJ_SANCOV) if((OHOS OR CMAKE_CROSSCOMPILING) OR WIN32) message(FATAL_ERROR "Cangjie with Sanitizer Coverage support only supports on Linux platform") endif() if(NOT CANGJIE_CODEGEN_CJNATIVE_BACKEND) message(FATAL_ERROR "Cangjie with Sanitizer Coverage support only supports cjnative") endif() endif() if(CANGJIE_ENABLE_CCACHE OR CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES RelWithDebInfo) include(cmake/optional/CangjieCCache.cmake) endif() if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_compile_definitions(CANGJIE_CODEGEN_CJNATIVE_BACKEND) endif() if(CANGJIE_WRITE_PROFILE) # Enable the --profile-compile-time and --profile-compile-memory option. add_compile_definitions(CANGJIE_WRITE_PROFILE) endif() if(CANGJIE_BUILD_TESTS) add_compile_definitions(CANGJIE_BUILD_TESTS) endif() if(CJ_SDK_VERSION) add_definitions(-DCJ_SDK_VERSION="${CJ_SDK_VERSION}") endif() if(CMAKE_ENABLE_ASSERT) add_compile_definitions(CMAKE_ENABLE_ASSERT) endif() if(OHOS) # This is a CMake bug until 3.22, where MINGW is wrongly set # when targeting other platforms on a Windows machine with a MinGW toolchain. # See more at CMake issue tracker #22647. set(MINGW 0) endif() if(NOT TRIPLE) string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}-${CMAKE_SYSTEM_NAME}-gnu" TRIPLE) endif() if(MINGW) set(TRIPLE x86_64-w64-mingw32) endif() if(NOT TARGET_TRIPLE_DIRECTORY_PREFIX) if(IOS AND IOS_PLATFORM MATCHES "SIMULATOR") set(EXTRA_OS_SUFFIX _simulator) endif() string(REPLACE "-" "_" TARGET_TRIPLE_DIRECTORY_PREFIX "${CMAKE_SYSTEM_NAME}${EXTRA_OS_SUFFIX}_${CMAKE_SYSTEM_PROCESSOR}") string(TOLOWER "${TARGET_TRIPLE_DIRECTORY_PREFIX}" TARGET_TRIPLE_DIRECTORY_PREFIX) endif() if(CMAKE_CROSSCOMPILING) message(STATUS "CROSS COMPILING libs from ${CMAKE_HOST_SYSTEM_PROCESSOR}-${CMAKE_HOST_SYSTEM_NAME} to ${TRIPLE}") endif() # Always build clang_rt when it is native-compiling if(NOT CMAKE_CROSSCOMPILING) set(CANGJIE_SKIP_BUILD_CLANG_RT OFF) endif() if(OHOS) set(CANGJIE_SKIP_BUILD_CLANG_RT ON) endif() include(SetupAr) message(STATUS "Building with target=${TRIPLE}") if(MACOS) include(ReadDarwinSDKInfo) set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0.0) set(CANGJIE_DISABLE_STACK_GROW_FEATURE ON) endif() if(CANGJIE_INCLUDE) foreach(include_path ${CANGJIE_INCLUDE}) include_directories(${include_path}) endforeach() endif() if(NOT (CMAKE_BUILD_TYPE MATCHES Debug)) add_definitions(-DNDEBUG) endif() if(CMAKE_BUILD_TYPE MATCHES Release) add_definitions(-DRELEASE) endif() # BUILD_GCC_TOOLCHAIN specifies toolchain for cjc, stdlib and BE at the same time, which means # that it is used for both targets for HOST (e.g. cjc, llc) and targets for TARGET # (e.g. stdlib, runtime). In the case that we need to build a cjc for a host which is not a target, # we must build cjc and stdlib separately for specifying different toolchains for them. if(BUILD_GCC_TOOLCHAIN AND (CMAKE_C_COMPILER_ID STREQUAL "Clang" OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")) )# It is clang that takes --gcc-toolchain options. message(STATUS "Add compile option for clang, --gcc-toolchain=${BUILD_GCC_TOOLCHAIN}") add_compile_options(--gcc-toolchain=${BUILD_GCC_TOOLCHAIN}) add_link_options(--gcc-toolchain=${BUILD_GCC_TOOLCHAIN}) endif() foreach(libpath ${CANGJIE_TARGET_LIB}) add_link_options("-L${libpath}") endforeach() add_compile_options(${CXX_SYSTEM_INCLUDE_CONFIGURATION_FLAG}) if(CANGJIE_LINK_JOB_POOL AND CMAKE_GENERATOR MATCHES "Ninja") set_property(GLOBAL APPEND PROPERTY JOB_POOLS link_job_pool=${CANGJIE_LINK_JOB_POOL}) set(CMAKE_JOB_POOL_LINK link_job_pool) endif() if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) include_directories(${CMAKE_SOURCE_DIR}/include) else() include_directories(${CMAKE_SOURCE_DIR}/includeVM) include_directories(AFTER ${CMAKE_SOURCE_DIR}/include) endif() if(WIN32) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/lib) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/lib) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin) endif() set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) if(CANGJIE_VERSION) add_compile_definitions(VERSION_TAIL="${CANGJIE_VERSION}") endif() string(TOLOWER "${CMAKE_SYSTEM_NAME}" TARGET_OS) add_compile_definitions(__${TARGET_OS}__) if(CANGJIE_CODEGEN_CJNATIVE_BACKEND AND CANGJIE_BUILD_CJC) include(ExternalProject) set(CANGJIE_DEMANGLER_CMAKE_ARGS -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_SYSROOT=${CMAKE_SYSROOT} -DBUILD_GCC_TOOLCHAIN=${BUILD_GCC_TOOLCHAIN} -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/cangjie-demangler -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}) set(CANGJIE_DEMANGLER_CONFIGURE_COMMAND ${CMAKE_COMMAND} -G${CMAKE_GENERATOR} ${CANGJIE_DEMANGLER_CMAKE_ARGS}) ExternalProject_Add( cangjie-demangler SOURCE_DIR ${CMAKE_SOURCE_DIR}/demangler CONFIGURE_COMMAND ${CANGJIE_DEMANGLER_CONFIGURE_COMMAND} BUILD_COMMAND ${CMAKE_COMMAND} --build . INSTALL_COMMAND ${CMAKE_COMMAND} --install .) endif() if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_subdirectory(third_party/boundscheck) install( TARGETS boundscheck RUNTIME DESTINATION runtime/lib/${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND}${SANITIZER_SUBPATH} # RUNTIME includes DLLs. This takes effect on Windows. LIBRARY DESTINATION runtime/lib/${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND}${SANITIZER_SUBPATH} # LIBRARY includes shared libraries except DLLs. ) install(TARGETS boundscheck-static DESTINATION lib/${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND}${SANITIZER_SUBPATH}) if(WIN32 AND CANGJIE_BUILD_CJDB) install(TARGETS boundscheck RUNTIME DESTINATION third_party/llvm/bin) endif() install(FILES include/cangjie/MetaTransformation/MetaTransform.h DESTINATION include/) endif() add_subdirectory(third_party) if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_subdirectory(schema) add_subdirectory(src) endif() if(CANGJIE_BUILD_CJC) add_subdirectory(utils) endif() find_package(GTest) if(NOT GTest_FOUND) message(WARNING "GTest is not found. Unittests are disabled.") endif() if(CANGJIE_BUILD_CJC AND CANGJIE_BUILD_TESTS AND GTest_FOUND) enable_testing() add_subdirectory(unittests) endif() install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/cangjie DESTINATION include/) cangjie_compiler-1.0.7/LICENSE000066400000000000000000000301541510705540100160170ustar00rootroot00000000000000====================================================================== The Cangjie Project is under Apache-2.0 with Runtime Library Exception: ====================================================================== Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]"replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ## Runtime Library Exception to the Apache 2.0 License: ## As an exception, if you use this Software to compile your source code and portions of this Software are embedded into the binary product as a result, you may redistribute such product without providing attribution as would otherwise be required by Sections 4(a), 4(b) and 4(d) of the License. ====================================================================== Software from third parties included in the Cangjie Project: ====================================================================== The Cangjie Project contains third party software which is under different license terms. All such software will be identified clearly using at least one of following two mechanisms: 1) It will be in a separate directory tree with its own `LICENSE.txt` or `LICENSE` file at the top containing the specific license and restrictions which apply to that software, or 2) It will contain specific license and restriction terms at the header of every file. ====================================================================== cangjie_compiler-1.0.7/OAT.xml000066400000000000000000000104311510705540100161530ustar00rootroot00000000000000 cangjie_compiler-1.0.7/Open_Source_Software_Notice.docx000066400000000000000000001521211510705540100232640ustar00rootroot00000000000000PK!M) [Content_Types].xml (ĖMK@!U "Ҵ?*uݝTwҴAh yw& ;ɛ5 Ĥ+a>d8vӂ=_ NYP8%wP$6RF.l8OrVp)}6NyYL '\zp.s=D0eub*h)ũOS2'tHFBD ,˹g >}Yj M}ܚX&rGkFчtF҃~; 'Q 0Rn Ow j>F#jZ A-ܑ3~zzL;'x2Їt m_R'$e.@.^U t:"IoT[v}`ӹΗPK!U~ _rels/.rels (MK1!̽;*"^DMdC2(.Ե3y3C֛+4xW(A yX܂JBWpb#InJ*Eb=[JM%a B,o0f@=a noA;N<v"eӨbR1REF7ZnhYȐjy#1'<犦7 9m.3󭄓YPK!1Vword/_rels/document.xml.rels (Un0W? ߋC\JTՁ塂M[QiՃ9 όgf6HU <y˘/7Sx!&=(M60ԇTUw,\ŤB(UY-S/-C]ʒv,d%(T$9vyL.iEQ< ZAjF&K@9ԑW. c LF© jpBloDhmDMze3T_a߀فߥ|vP(6: 5Bkڥ9qk NH@ܹ?!5`º o]b;A$P5^< z'PK!x. word/document.xml}r˓wPĞc0ts!%d_6(_$k>̓ m?v"x@j@/U~rSKMһ&wOMn$߻̷ W~Wh訑?}ba-ƿXeLF2lw&EnP9}/Mfg[ͽ W339= `k+pX뻞oLzO8mGX;i\:=+rB_<(/G>w+.ɛ ەk,Xՙu# †5KOnK#tXXPw/&oCс,珝,髫^/ӢWs=7 iA,}r/굢f֨}m5[J&W*Եx cw1ZͫF UQj*U^&2jqh| k/.BׯSR &FFi  ꄳh{x =EupqG [9EsxYHWJ,bz@wrO:E^w@uoR^BK5)rDxH:N3xٽÞ]Zu#aPk'êX}M@wv @&K9/Z @oYݾ?NkSr o]zJ$e(RZ <#~s) S.GF#X,̦`i6to;i'βa`~"7Na})uP lD,(D4Q'Ϋ2 |AܣjnDnh۾?ώQt8.|E>b1tj\+mf.·m6ٙ*Sڟ , NT瑸$JVZ[Hm N=\ r`[qnV/ i晕)޺Rn)>3;8Hkذ("ZxУ"*kuRk`S8Jkm:p9:Zը޴jWuQk|X^T|yݸV; X.vM ܎8-7uߟ7~h j}3}\ݴ &r. U_uQ́<jCTnTMqZ,zrqsV {q3W\.k-*+x[P7eu5y9 .XES5k:YFV(7MY}zvlŭ|7?RbTLWOw1_aιв 3e)sp3Yx9o*r"Nh&FtO$ν:]iPkKq׌7?>?ek}&q= Q']e)\#Fi?/O>,+Vܞ}q %-q#lP*Ϟ;v8*a/>! t|<'<..* x;H_*GR+:x09q?46}Kݕ(pojwlx:k=zv5>V< ==3c҂Jg0{҅vMM3LE;Lwާa wv7 " @!lx% RjRٕpO>́>Tr96W}Ƥ\Q\uud[)Y ~Oz#uJWm,r4O3,@`- v r,@7PqKWTab* GFK(UmQ (l.OxLܸtwPp(%0{휀D*2sHSrhuA`: dyJ.U!R*24HUx CJL W#x\9)ʣQx"b/xLEȐ1g8UxmaL>.e , !Z<V @kaلe7(Gx)R"+y 9$vvH{Bq4KJxcЍc×X[rdQYe? z(}0B i3&҃T /m &c;3|OOC!I/ AI4:dbş)C&*w8=>w"䮞5$u#ٞɟdg NE 3P~ `Y`]R*?n1@ԁH;YOEf83g1Z%@:Izh6 4|T*;] K|3zB8ɮ` f x9. iX ք|@@/rHbwl$i{ozztƷ<8,'|%y r:$1E:'qq6 pX+0Bet%6D+9sOy {)(\[;IQra"hP Ǽ#5Ni ;dRzxt``E 3U58UIt ":Ce463+Wb˷I|ȡOw{A͇b4XcvY l2|(d1&xhcPC&z: DtNjЊVp C#JOeF<-Ȥ6`TdEd m'iLMbK1gv6b 1EFc}05)ϋLD0ϳ9]M@&m {`3ȼzG73]Z \e2a1GvRuNW)򢔃WSV1Sa 6G *d5(GP꿔WBV; >JKhrQ$c9F$dm "4S3;O\%Uv:fP}-áD ƿcIC8 򶧂G D`SR)\x*"F);d+ӺO\5,fIS`&ip**XU9NRKɟ, 6@nDJrQ}9 7K|Ȏ3 slx"G\'ĝND*'_־r(꽂SHZEꘂ*kݓ)`ɮ{nXQL[Qpnl=0ZIO@JuSG"QFqJ(|HIvSiQAyI6$O $WֺJfjϗmCJYu0 F;ٔFT[)c4)@X'B*B|jod^o6gBR6Vjz<+Y!8 jA*C \r()@&+ݗnGϩJ SvFT4SN2r9v=4>JI 22 n9=, i mJ%(&*IEzng2mv90V^U?y*{!zEc,QifHpLؑ2nF# sp=v"c{*uWhwa-QM|&~` $wJL\GЎF $CХVD$Aę-dp)"xVQ1hͤnD1# )Z8͐hLl#yRY Ž3!ʻ!3ѠʞtS~AbIW$ @Et(Nl3>YdLi-3t%p8 i$&v|z~&S]T Z*W8j' _B |iǽM{!,SzBDT4j!$-?t 3`/_P* řR",vC*2ₛVld$J uދ㼊~Yi&"ν3v|CqM!1oUT*a@>)*ޝi+cںZU0` ryD.ҟ%9qN,kl&\,F{joL }Fhh 'Bip[jf@v_^iHkPk2+ ^`'gW4"+V4h?m34).5TbV.a3{#a9FBRF1~oOx bCšj1I ȿ6W Ԣ9zw^E;5 cvN7 QET1^5j*i/WgƸMQ;i]$~U%]7UC./jpI!Jp\ꕋZsN "]ZU:3,3S4whi՚rzE[/勋eNxZAO/I0˳ZZit&Ur0h^W+5W&Sn٬ | >ggۻ%$TnT:м9mjVU|:#B7oJBҟf5Ohp | 4kDZUm4n{w ?EN:M(to4+v~R $(Q$h*8y@Vj^|Q\W{Y}5 5ex MyR#Nڹ(}/'Dʗoc2N;>-W_<( 5Mk6D9ZAMX,k^_k{eȯ Κ@rUT(RĴ?/dqx\3!val’DO%)R$hOV $i[/}@gx}ezI-RN\\|׾Cvf{ xLFupFuȁ/Sl> T|6澠a@`=s d_8bm>(}8J<;v{1GVU߾5RTNΞ7;K֧Q>0~{"`~oѷ8ێVQ쳁ʒ%+;jf1! E{ 9UCej2}D͉ d p/,_e{[2b=vdgiJum¼Cu$g:1<,eÖiKgZvTOcd,P$áhfl8yfygĤ1 ^'%=r~Vܺ9YMkhi*\ZXsttT.'cD7QO5p~#*!PJ7M\9;nNpt ,;oOvi5 g0z~ހSGj5 A]EMNؙP>L:j5m𯂽j007ndx޸ q?ElI}U5#7&]ɉj慝#zwaYh7>+7|#١n' _kȏuɭZfnyQ[ˌN4KB7kD?&2nң>M83Oc~䄎ټ3FҦ £m tHI#3Ap(C4z{b8͌8c݁CFfHbHʆ$Vl uF+6FF\+y"i%Hz$J("M$L$D@lT`I#Ŷobcm,X&fl3IkNX1ւ$SCI+ieF!L88b&c{1#(i$H*oH* @Rk @Tu;[Fs-gXPχX IxG5lFRv$1#cͣmV3{*iQgoI/RHZ<XE|)˞0i5d_pMjSElH2=wf5BjlHZ؆k&x]aیD\ Ifk!T;FH*u^^^OrK-9\eb/IaSqƃFH,,>a$LG+N0| MJgWfQ1`$eF656f5!TƻҺE NdbIHR'fL=ǾFڍ`#)ƾ#) o컭 I.9 }b.Y6#4Z# "qEHXi+Xi݌FƶѥٹFchw53l^6id֥xI7KII|tH2xDYeFѧI!)#)+6}7$'3غfeˈHtF:b?닑d+V5 $'wKHʆ$7Xi$hŭthrohd'qؔAD%4ypHj?;qFR6$|lgWB\P6$*ۢH8QԋϘz#)ic`nH2G5`楐66,oI&a ~Kr?;Q J[75ΦF"nIv5}o[ mOF{i64r&L`Q!F9l(dp*c`&>n1#Sk<-r 1/ x;A8,/H*2IkZ9傑I_{W{@kd'eC ksH{K 3 C~m#*%X358d%9՝MJ!! "ŵY;VZ+i1{zpA4펍<v,%k!30Ba9E&\Hϵ˘h[iHk|k&zDF.ZڒaZ3V-ϻy+A& 2@(#vX&muOŅh tVِdzwνi;fdh}xnd D 0=#)3*5Ի/p]bݐ lHJ=SO)nkwHʆ$3 Os2" Fҋ#C+hQ(mh񻁕|;yn:dIx -Y#lՓ C~=e懳j]G2ݸنnvmc{%FHJf$cp׆Uچd>s,^h(cP8">.ɞ9'שKtu[)Gu3&G+zgEDUgwUsCjnٖ=— 8Jޝ;a9p򣓐u͋Bŝ|C9_kEs^ Gey쪏C^KP)I+䐞1G+ -MZKĔzkLW.A ,%3k/EBj{w+pxz^Y~t-lo5VAk5q#,Š%zz'Ƶ$p.|^O g ō߶\Ce` Gw_"ڃF5mn'GDy4rYisiظL9q&-WTub{*pF@ η۠|&q`$uQH& leYX&Q HR7|ܽ܎K{ Ss2S{FnKmQnVŞfw@+A۷oN]Z ShN 뇿52hyib?BH?9֝նC&V<9C،\!Pw-"P-s" ٍH\3cu32U>+hCd1-a8u b)BQSVAe׼hx39ScBbJ|hdK9ekC/ar8ᬖֿ4#L]QiTjfW˜J?ޒ񹑭8O"_Nu֮+Kk+X|#N~L`ɟ#Yzn'+㍆ ފZZ`/=k`$mS _:zuȲezx<kNյ60'4 #1* .2Mcő$Cz@zDzD{Jz=#z=Wm~[,nHA ̋tl؎cT(_?H9kMYbMmma UFmAӐ}zNX|׶LX7(Ӿ!o91(;:2`ݮ4ts3!pTWouMccb94nK.-Nw+-y^%JֹJEvE%/.mӜR^"?j!l!QWs+⹮ 0EF1;9f.xPui=׻3a aGyUԸAL0r B(<}1?b^^@;a(1gBJrdziR"%sH3g8@Yb;R5מ<hnr5cvǖKՕ)8&ra>֤m^N+VXև*ߎ р*RLo^~ GMUD[ M Wj}x_ -&js؞ XYI4mq˓@X<`i#H4酪%޾Iy%̠+8VI$њAtpcy:`cl#2j` :Ky[̢CDۏMCUzUcN&Nfyˮ,%\G5V ;Xp`ݡx20{阁@Z57oC!x*X0`Z |J'DcIG,qvAd"8̴qfDvmvD"\X Q8Ss>pƑtf R݂ڴ4omĶ>ƪ\ºm|\E̐׶@| 0 vU?/7g7lKo^+0K, ߎ#ƞI:fv.eףm'A2Jrׁ~`yŠH]Za5a_<t4H-1a]Ȝ8fg9|5&z6=2hZ`q1pMVBIahv7S/:P0Tp`]< !٧в|7sT/Doh]ESnqKVDW8f۷`Hn=Oq bΆ? vq+ϓ^υlˮ{./K;iKggENzꭔ# @m@,M$Yu}]jÓ۲0qbBûz3;:L4".~t*pyQ_sc=kzy.c["u,]q#@~|Ɋ}iSI_K;", ;a˕TUJJS='Ma1F (a %ᒞUZ)4KAehTkVFc3ӕϦgັ|\?G3sܿslQÞ2_'dbj7AqB8_lO_Sik-^# }GZaNXjE޼r}QUx746ouzy~̎.Ώy\tUUae_O{n _zJo op=MXA.5\+ 0CQaލ|BeK?t>p,<924>0d(J@211P*D'%eDcmX10U§ q4W$Ah;z)ȁZȓ>4<"T\ А"4-ʫ0$QyVFŪ%oE37],iƑa8Uv;cS6L+z2:EQKR+drˠDb+to~H1}\'K0''w)@;YL;UXFCIز ] +.BlJ`:(r!S8ͽlid)Pz:m1+f($. H _9T?U6aoS5okPŠᕤbZk,,b4B U -&"uwgl o`FX &;”PjX#4^;suP_2/㧅] sb\In5́Gײٜ*{`"H+ikG\=%b yB S`Z nB,D>_Տyn:L يn*JK\fS1NgaIvES p@ҩ>s_Wǘ$[$JwГ`5YARRRPZ U63I G x<F HL TrT 4 a LwtiZccm+$ Ѱ`Ω(]:qeړ&':)i lŦM4;E/u ҉Uzw@>5 &G‘+B9105{]eq !g*[u[昱4a[ f&@MX @8BgeQ)O 8/Zb}ДaE`Uv4.fmҖ$֑/m.Mdy;&Ab`#.(S"Du0 [xt~𕿭ͻ/>v<aK{uǽn{x&@(tGg7,&7s1):VVlh+é4VPb!PJ('SDFϝt?UBh{%e0|:pB =&e$\|ʄ"BHbٮS!0nn`HW(b7GwIqf W0D-XK*#VOIm0&~We2ԣpDw8 -Wom6o&ZQ? $̴$hZDb‰T4?0ێ-[ flc;l=2vU}mv~c]vtq~/ k>Ϗ+LH k`#@6ayA@p5feLГi*1Dn\6]5[I~U|}~mZ ;ku>Cj; BwϘ+܎ZtF Ⱦyr$|GlU̐ODZaI`/X{MSɁY{V犣r)>=P@pq&mygȓHtA ֶ'}uS1p*@}dIp4JYy̠5'I1GmjFSȸL="liHV,{msCT`d_3 BX'Fiw) @ݏrogYlrZJAXHM4'9K/L2?+ѳ@-{(L7/ VEİb^拡16JKD#)$ҀA5̄D1fE!i4g%1scl|4'.I=SB +Y;/gp,( X/W$ P Cqa0NUUd;8QJFv:q|xr4k[ǝ<èh2d8:T8$Hv^1^y]Qʏ|&]s #k0ZiqU'Iq_mxap+(uMYv3wyb(RF4![ *lbCABOZڍg*d`LtUZgђH߉r-$Fd[h_#.cK\?2IVvcC lf2eZS+ͼ/ҀPKY5ɬԍGdOՄf{21Iv믹 N@IF<[6'(` 15,RxnȄx W w,s?a1'V4p4AwKͼc(It0e.g鹺zY8V1z1CI׹D Nukm:A*>WD,+5R ŔpGgdI H"̻KsטtsQ?D6R75+U%`+VCqh } X匵))hS4/o"':.TÊNx*Cࠧř*(V+ق)I%ʤn*&{Z㳋7D:.e Py*`\100Ȱ Q;I[Xk-롅0. *nMUn[Kn(&6㛍HՃ GjfivPs #Լs`G@n C (!s!"[ЕEf}}#6q):0BڬRWq- KR?&JdѕEm!Ё@Dt-PB98HrqÂ~܅n*c$eX!`#HSd0ڧҌ~)#ZYdj!VVS}=A(7jFt,<"|[kпas=J&T83G{Zz~n0bH7@o6Kc Ceف~m{~9oNz k#hKIȕFP<?vg'J+Ad/ރ' OOnj M;~2@/n߾e7F"A9Oo{"hbϺisf׹'LX/.L:{".~pݨw S|'*dewՒB rP^jم4jqHΟ;BMIlj ƂN*H~W`O5$I"uCo(lH^'x4hW|7+ mE.9"1 u$L F$sgw|h|Y;vlrEW{ȩnMe-JDg3)lsB+ށ}GIvGzKrG?wT\NAoCuX!YZV>ux$'9`y=¯0V[n(H7"x46{l_wxQǁKz? T<-xkB!`ьV02`zRlGc*$c0?avR l=c\pƾH1,,0+X;|5%:?6G~TVsbhe,hoҵkh\gG !CJ{BnqW?{;Gf5i@F.yu[ WiLnuv|]P$[7i$rRep~#£@ilgz>j%C(r\r {F4YrI~ʄ;=K~7z=I ,*fJaQrfFg-}^ꭑ^-}hwe 0/xfM14o,@A./[?ZqOikj(+FZ\;u #lZqY˚š߲Q|(.t5j`YiWI`>+f+aWe0t$IymEijW6U4ӔT* (}>sG9 1`InoLҚjdJְlɂ3FfYVJ {4f򅦏1`IT(hw4;`؋x|8h2"FnH<_$kC4'lU 's Ȫk8{G$ؾ` ~n6WزrϦYY (Y\벣*;'//_tt ZQ +7E.lZ3H, -Vjvv0ꊑWƤKY ܣ)͵uROVHG7oᰪ*$+i/CD]rh۵M@I';7W엢+oQε'Ј⡢ur'+OֈF8!@/2dmН6)ד(ގuQuvJU|vH2"c}4})*-lNo(Qy~/%II x%Ka!I qeR$26Y4`GU^FMRDf叱PDeMK幖>}Od]o^'RȀZY*SJx[晭@:/W5b;x"fSp|nJˆ2@&CC0 K:_c/1A',l,~& SHt7+:rwq>Eb:C@?$W}U"w-"l +_P3FI&dFho;N2YӪu1,eFVʕI}Y~8o-YXd<`]Atrny,ƺQ48J%; _uY-I}GuxT:BPDdgթ^v7Dr/Y,h*@4A@Sj .zfi;)2ӷmIGOp:/RG:,]X`I t +&Ⱦv]0˝;@63o5y|QzŝqGst {mZ| ǎT0U& RDw}(:.yH7Ѓ peJXzcqŵ`g;*ud޳LWtz+MHsdT Y.D#6enҋ} TxFbdԁ)}p)d'`4}reP+*0lb+W^Hw]4FC~4Y7…V@X 5 4{*5wK?<hL`7_>@g<ϲ=ɸ+$Y:^RDDO}}_"ϰ8$k[Eu1[ii,ORqLtr|\r<^{+daENG_7,e@/[\I-vjaX Qw\_KU`-Ln])KWmB_{0^HgJ<6;B,>^=Zjdߌ=؆mP(t uw)zbk? ZwA^9hTv*~ࠂWu|(F5[-I1ے1aD'5ehF>oup8l-ЬT*9A׿z 15Lr+RTY ǶRV Z<\F@p7ܾN9@H0 K>h}ď<"ȕuXZRlC2@eXm7Yc]IJ]a\ CC0 `1/L]P]9h'AKMKzV/Rˮ]v^ϸ C*&١x +a–OvP>;>ky^ x0 ! N=e ŞWકuvY$ܨ5<^ ]H hJ(U)x`E/MWDbT.X#AVT72 ?,MVp \GshvW6/:؊Ch:}x=F҂ʧNwW\8d9mO]|lrM켸PI,[Cr>]lEkX` *eZ0i{dS`Wٕ.*~J.{y'v^@g.(RKY"hnsiy&<.֨e);ԋ}L TxFb쓶zeGnFr)!tV[w൥1w #h@;ܿ $ ֋ g62veݬu(Tw$xi2N>P2hY$ݬ,i@ۙOnN"]E4w!I$Vw:\,/HY񬓯z׮ut f(Lcj_j⡶Kn}iTM"SC݌,סħ#9dSJD8"ΟuKeg}@g8elP+n2'S|)38j2>\2d^rr Fc1rh>%Xy.<ٟȘl 3q .WN|8pD#)|<2=0}JxîxlwU WQp42=@3e<qQz͝횠ݩŅ*^ˀ: ob T[H԰%yaR}HqHP+Z+8w/_04˸yrmHFVݱiLV@NMzl bS@@LxpAM"$\5`dA]eWȣ4і Ŕ0>Rܙ`?jz^dCf֦p+j-@"灵:<5# |OPOj%x:̧<\qajY]6-?_v/[ObGvۧEf>?Yn׃_e ?Ÿ6VGi^Eug>|Q߲kℝGiix÷&lvN.}{Gfu\ood/>}lv:ljvZOƎQ?ԩe먍?h?+xGyrpGW.6޻:~^\Z/Vwֹ* bp66|xkSj[e}q[ kl^uq^‘\t?gO¾~l]:X`qC3SuSUWz4ם#y֡nYaT[ JbDH%oZg1JRy9XϏ}xE>+5~'D[l=8{m5nQݥNÐfaz!fsUj=ЇrY]x!ַOj˅/ϋ -Q!{yvP?*ףp?6gzI&\zUGM%Z*e`*;8Ğd@MeiO@./HTZ]?:Xگ٩(bS-~d-@Eԓ5އ$(*`ΞuM@>?\KA( wΥ3V^|Mbsq*:Ϛu&ԷH8cj{#zLq~ЭfG)wc`ç,fD6 g~:Dl۞ ~;/҃,&볽_";_pGVM@4Mm{>Xs1ߕi. 8p +F':h/E+o2'!K"D '6Sr#۲ldF0K3nNc W}^_^tOe(SJɊ% DV lelww #Xlߙx98r5mٙ &MiX,^b28Vl.C vGF7TƼnc=CS|Ng"n>N1)/[❹m1O9l)tnrf|i2^$Ey״mIGTO0WɊs8+,K% /TE%po2.orVWi~GrER=*eqyn\u`f5,4k9C7Y-K mj` v$o%&cAXFUi$=*")5J- 9^A bx\1)קV&BqJ034,!UUrKb-3 8Jrb 5ܻwЉu~~ƱwbSĞTƮ Wmޭr׽7{v4N3Mι2&6k`cw\u 5mh-W$⿉K<@-.N@r >c[.:aGfC1ᢴ.@4Nh> +rv1ߢ;ĞjK {QϿV?e85ۭ׳CX؏j֘$>zI;x9o/Y~L`sM*R9՚jMҲ,vՉ"JCh55]yo8*Euf7[ul$hYPT\ϧHIgH7,XxE>5Odu𔝷Nʄ_$A %ZG:-49oMZ<9/RkGi,4ӺF,:ydZ5{ZM,wTvY%3(&s㾫&rGfho&,ݲN>hz&=2"W>/ /ܹnL2beL>֧;QH"Y&E\DMR|#Y1y$:C̈Z0ZQHIBPLc"J[譗B\ !XJUnYOWEBӟnwL!y8%js2KsvHV\-fDbL1G"9x~SXkY2,dمd^R;eccc{ZY2ZȞKԢL*z%ӃZhAFbaL )i R .31ZNfT)BaǂււvO6n$ĥӄNׄ .7xr~-<-<-<4<[t4!?dsxpSd܁d R R ҿ[ܠחnZEe!f<p!B_"ikL$Zm\#\D"-PRM^u^Dg|UhȌϠɌIhHw;|Bmь+#aa9 if Du3ی2h,ٗќtȥT@\ү+.`eeys|oAMhB\d5#Fslk- Y11Vs$^jKZIT()rI.%{kK25BQ37Q*F*̖}X/8m VVRYkti:WwH2{I}`j.f3 4t_fBj[^+&Cuј7($uC( .!=h%CϹ$5C)sK7hCu] 4}P aPE|hFU"]NGkf üZ%}|=:pM΅7;lV `BqWN¬[y ҅Fvud^X֫FPu]P-[-_?HFcz5VZ.aľ\-(WAe<Ԟ--v£h[0gAdrC9A7iSOP=o )m !:<%7<7ޠ?Nz\bVOzՓJr:sejF51[1)uRH4騟 PveA)2Yg_-ݛsy[j ^ߝS.#0 .I :UҌ\&(&Mga5G4ˡϮd:MGiIdr:a[3ևw'4#{|qmjޛcG &JNv:C%Y)õ)Qؾ)sJ-CNi$3m%09nK3!o~Vbp0H⽡. {Iݽdi)mElZ2nA!չETRjMҒxt/b<1 /H8(.fDoښ͍L~_sެ.͑jɧKsY 4^J[Zofp٘J?I8-Pi2R i k VD$<#<. WPK!OMiA word/footer2.xmlWYs0~LǏa@K9 !K^-YHB}Wd@y@jo-]F2e{`8Ӯ{=>]G*DcD]w(5%L)<p^FIeK4ޒ w\K .t \qL2a"C beHy 9R$%Z߲0ZIHt Bfb* Lr*ע̂, bAm58h K 0Hyߡ"؅}IRZ9~Uj֒4_>ӷT9ҷK?`"&Fj8,O)hB́; 5!ffYi> -8HQ8?0㠲@jk 6 G0# +GHrحE,IT#|}$Ȝ#LX{o eg0eӏTʸOEV(P׃/O]![@|#ÓfEce_EA!$p:)}['SME] \FG8AsM4VΈ+"AyV6HH9}L|;<| c}6Z16*2s]G}KPK!9X word/footer1.xml]o0?kAݲ Z 4+mw@P%Ə}[bq C0EuU '*%B+sh>{|qmjޛcG &JNv:C%Y)õ)Qؾ)sJ-CNi$3m%09nK3!o~Vbp0H⽡. {Iݽdi)mElZ2nA!չETRjMҒxt/b<1 /H8(.fDoښ͍L~_sެ.͑jɧKsY 4^J[Zofp٘J?I8-Pi2R i k VD$<#<. WPK!j word/header2.xmlVn0}_i!{ wTҋm+h&1Īc[e~ILz]Z+-c9cS,TYc=:J##o'nKDsETD+>O=>{+.c/? #5Bl[¥oѸ 6\H)^S&] Mfliax$G[B&[*!99Qb=)pL%DTi| 6 (eJJjp& pq҂LjGE 6b /ϴLRDXuy&n@@,+΅䙨ahWie'"?OMFf /0W %Q`%s@u\kFh9r i>4-Xva)rwtoUΒ,h`]s~KD{_ D*=Ҫ6Gfv.._nT_HF(w"ZgrXk+`%A<~{ G8;BLOBdyKIf *`iZ2^Gŷ";FC*dךM9"\qʡLsKʭؑ]:sQJQNb4]3NXlYP&b| g-l1d,V҃zRJ?kbzXB'@36]f"$VX.wƙ>LFcss{5sE°%zݻy^S?PK!il word/header1.xml]o0?kAGfw>~@-E; ráyC;K:UaN$eJЊ%hU6rqehrM9 %V;!,J(lތՔ9Dġ'Ӵa x n 3!~V-bp0TK⽡6t {Iݽdi!MElZ2nA2 ERt*M`iI~Iw~tFX]s" W7mFFfӯ9VHh+=C}m\?391p%W-Y p- `׃F3lLPpI $d)?47ۄ D$q8n޾ve7^1ưmt>(%ɗd ݕoO$ ۊk$uWH[Y2d(I+( QC:. uX7A0n<gkG5?ntǣzTZ %i-Fh(I8L IAicz(7V.6dS\za^bDFsvAG⨸9''+;'~֜lnus]לZ7u;td ufQ,x,]oM6uE<8 q#xTUE9Vݗ1yžnSDi+kx+~4XҞnX},65=JddXC@=d_ʶ*yәPK!Mword/footnotes.xmlQo ';X'h*+N-׵!q{v(_wgX?:k Bҹ`m>ݱ$D t:O6+#`!!u*gU.~@-E; ráyC;K:UaN$eJЊ%hU6rqehrM9 %V;!,J(lތՔ9Dġ'Ӵa x n 3!~V-bp0TK⽡6t {Iݽdi!MElZ2nA2 ERt*M`iI~Iw~tFX]s" W7mFFfӯ9VHh+=C}m\?391p%W-Y p- `׃F3lLPpI $d)?47ۄ D$*Nlo]F};E]S'`,!2=NT1ea!|6ġ{E ?tH.Їld #/g%6c1TKu&$Ct]EM{ܷ%0sNIv|#OT_YϤdp3B (m#rչ:V}//Ϋ%Pas]t{@(=oTUt8s0eę bFu,Ј au,[+ۥ2Ykvuȼ(^M-63MHX$6=$SHEvyLD3R (trYvמUt^Ss]OrsIX0/69"=e"ZDfn =YSnBb:RMq B9 Q"a+J$戒Tu; 4+U6zg\|"gv`C܉-ƊA>UkTt#"<. WӭUlQ'-9ܔtyrc>uO7h8@׏ww[ wXҽu͙֭:%@94cե 5|i:#},Z}@ֿV~G]WT CUs%0PK!quBword/settings.xml\[sF~ߪ.=wCyI*Jf!& ZQ@Zaʓy2__Nnݼ\ūfoKw0ԻuvSs0P+bwޮn.a}uuX=4]ovwն?nݴӕ`\n.4]ݡ!}jNG߲<$u ӊW}!γml>'6q9{O#qV@nW}Z;Zi*s?7x1a-;]_]]ynCZIyE]&붯M"ِN3vq5Y>f[p( ǾMMmpۡZo6nV&429 OzהilcMq`܎DxM/ovSmHH_#'hY}f*GCiqw&k纯i.?W^HŧMg:t{9J LCr 1jj$us_7xKSGZ}?Hc^Q3go]}U[ 'QG&`@ \VycdП'|XI~ciWu;3߷mͅ`gBS<^ߓH?i7|^Eĺc?^ey/c2 h#IdN{(N%3 #Z3+@/ S lW \'YYh%bDk b m "VU ~!AbUc*樗`  s4;A=H-:kkN%b-+,b\* g16-3Q.ELPr\!bRhgp㡾YKgsk<,mNh s4P@cݱYV)x?EC˧Br|<ĈX`G@' zKG%2G2 G&71N(SgDđ%B8:*h#3Ugx6K͉o8?ޏ s40[{gi.j#^1I%\/̄'.p%5xHAS}Q/Q Q,2x|((/p8 drz%A؀eJH>)1Պ'|GHHAAkôZjbp&Ps0&٤ oXڅ1R0E&wX2I'&tc jHR;DN;lIk%# k1o34qM%* KR% lYۅר)x=<4S#> 7<\g6Z U-w &fl^ާmY?ynmܦM?sd~7}sl6Y43a(~%{꽙_ΰ5'A5% }=L= ;Lnf=~>OHAKT\:*.\POw/^PK!@b(}7 word/fontTable.xmlܕn0'"ߗ8iV++Ҥk8`"@y]={=v$ jbDHg/RDKf,*GI)\rn|GuDMЊh,x|5,r6%ܹrǖΙ$KFfKb-eIp:N1ZƼDE죦 ɔ c(je缴%j+mєY {ғLINpL ZRlҍO3 +@ ]P w\2ݰUEKBBI,%9)<=|8 >Ή̋U Drn&膎;:oKb_\e :vst1NcTE r^Ց~:r`A'& :3Hyb͊o]4[a# l˨Lj,n(Nb(#JzAvFDg>%3pRڔ]qkەLdMJfP\Ae ^B)3Ot |`N~j.G{J D60o,W0H9X)ctl'_uݓ|P=;6w#w Ev d*yB1ߪ kPK! U(customXml/itemProps1.xml $( j MdKZZ5&5ki{if~|w H;&/=$ <>C<2ɘ|פ- յu'1-Ŝ1K^=e=+PV|& SZτD5k+胕)cGtfKDԖ.y~q_ HS?^PK! word/styles.xml]K y^Jk*>s8=;Ԓ J:# 8s%F\6Hb]=ymacw}U]͞}s'>z=DQŃn4uK}?{0I_$q@QoěMޢ 3n.Ѝ- 7'~/nO&(t6=rzːDߎIiE]]xGu: $^Q3bYjb⃾`ˈ$`g-qX) ҄_gTWs}&.1\;Oh^7I${{'l~'Jk{ ,O G=m==bo?EʐxVMKXWJoٌsi.PLSvbO>e$)J?%I4!ӢPTG<dzG ٓsU&;P>'.w5gr\w!q70;v!eoc\7h*⮓)=$6>4ET\qdFb6Lc ? kH眹{x}"­$E5?2DҸp1w?1W8¼']8tz>n4Mi)A.qYMXRsXwa%Kg34s'{˔>r4}wЌk=@Xs G"[8!鴧 8pYu fx]WLgˠ;fi0L4XQe^x]Cx"5? +XWN ZW`yir4qΚ&v`]ٙXWv&3֕9d6cdW6WnR.h/;KWb:4K@$>p-"2ivouw V ,H9 57g9 $ Ye ؉9OX\Jf~ER,SzEl vJ2*-"<2[ S䎡$|nd1HJgJ H6=75&ɓ95YQ.\&C%LpɊ*< 11^GدM<^-49Q/Y$z]=qlC=QъŽ8!`E$(HP&"AH0hGhG]c6@9*rTvTvTvT89*9*qTbwBBBS q m8*DA;*@;*@;*@;*@;*@9*rTvTvTvTCQhG_sYܑ 0Ǧ`MedeI䍛,, f[) v7@#KPM1$5KPM$*c|(@h2ž,!WY8aJ==)zZ-dӗ (f*R/EGӣ+pԉ=VP|Щ:J9 y@?{1 򊲆L)2#w_{e>(*,o&򛑬sSœ%?ݑ*%c]\P@ 1?TV:bQhE繖!e-GM܄L 4"/ҕ"~DXV481$Jc SN ?o,Rw޹6/C^Ry*@c+^/J 2[WJBśxqY} =Ciފu%Dx0'3*='?bEaZ-0[8R-(lYiYT;XYdbBりM~G{ c{4TS $liGѦXѶ-@\{jufVݬ+s][*5: jqQ/t!wx]y_>U+%i?[|f!M~ͱ|&-C)O-ZgSI5ʾ>v@>: \6pB7LԕB_5 3/+'X|,`-v1e8!ڠS5felPrᜪroC`*G,D32WpPpemu$emDTX8VN8jhKR788T# ۨ<8bI* '4s5u/>W}OiwoRG{m3o9oZs/NOxalC䂍k %LH̃* |JWc^jvojwc;ブ--e]]aӠ6Z:[;g9m^/_[eOK![ZוJ2+ j2 $ RZ>:~Y?5\Yf3/48pKNYMwk5Kit0Y^$EXyqf5yWW}3fE5rO'E҃[k}klKܥ1weL>'LJVIXYtX z⿴P~쨒*61Pt(U"/ emXP\W+j+{g+lC~<8{J~۪"B6a5nK^Y+_}c27(_YՓŇ߼cXBY]\,I$+5U)?ϜF5GMT3I5lnie2w}]52!]7ٽ6FON@Db3$/TbB y17 n)}|IDz2f7aAٵ۲5سO?'7uNq7m_gߴ~ߑRt] #_ tv<'qJ[b4Vm@84mxGnZ}lWv259{;SʽŎ4Uخ[eN屦:۹gp?|ǛϾ}oos\!*t]~Vb 6:/ *QxGQN.~]|O|?}q+>+?~SnkxN34,;^EPx,wjz+~ FfwPK!ߠ*O$docProps/custom.xml (YoH#;D~+@ccc+fbh8Isݚ Tտ9өa&|'l> 2~xv^x9,×9lG?}{XMEl_Fhk.e%Mu&Fa%~8~_eF~vxF}=tdWڜ޷Kѿ'0P0? c~ #*NHjv XOP%v8 %hD<2+MrUmO R7vrn@'c1:Z7}Oxt=Xоjanm3݆(}eM^d#7?pDф`nVksw '|T+цWkW+h-Չ7`'4B<Ľi {ksrIRVT)Q鰕ug3:V 1aK":F;AQ;ő0K]A͚G(0 *-7M_#Y}'bKݾp.hL~p ' mI>K U3C 7'iKTԾ:I:C®S0W:.ܭ)x3.캵F~"P? И94A v)=u)[)ÊZǙ/5u|3]8K-qr=dMP'5?S;5xߧ~_ JS7Kmyjs`/O5햗d|WeeYBhxX ֍l3[///;O +"GO [W*>'0@"1f( {oB/e~M&2ooQ>,@^F_ \2!ǟF_>//oPK!]uGdocProps/app.xml (Rn W?X>7]o,QQCDZ'9#ۨi;Uoļ7<] .bxҩ?z!얁,88& 3@gm_bqBQzTPK!^XdocProps/core.xml (|AO0&~ғ-L]t.;9c"F)o iޯh2YWceRD PK!,word/webSettings.xmlN0 ;P徦j.3i83qeٲd˛7 ; J,.x]= UG`mfrN.D ܠmՔslf;.)B@ M4MF]G'ub5 B2x)"i5L,0==4.Bg1 cN-ogm6J٤LPK!P:gI6(customXml/item1.xml $( Aj0EA>E(NY&]u#c[ iW'37%xuØSE rhl~Po<6@ bR-z}+wۡuL, x!%yPlB3 TnCDJd' &<#11#k@)˭\G/]PK-!M) [Content_Types].xmlPK-!U~ _rels/.relsPK-!1Vword/_rels/document.xml.relsPK-!x.  word/document.xmlPK-!9X Ђword/footer3.xmlPK-!OMiA word/footer2.xmlPK-!9X #word/footer1.xmlPK-!j ߉word/header2.xmlPK-!il word/header1.xmlPK-!AlMword/endnotes.xmlPK-!Mword/footnotes.xmlPK-!il word/header3.xmlPK-!To:_word/theme/theme1.xmlPK-!quBword/settings.xmlPK-!@b(}7 word/fontTable.xmlPK-!t?9z(RcustomXml/_rels/item1.xml.relsPK-! UXcustomXml/itemProps1.xmlPK-! word/styles.xmlPK-!ߠ*O$pdocProps/custom.xmlPK-!]uGdocProps/app.xmlPK-!^XdocProps/core.xmlPK-!,1word/webSettings.xmlPK-!P:gI6OcustomXml/item1.xmlPKscangjie_compiler-1.0.7/README.OpenSource000066400000000000000000000011001510705540100177400ustar00rootroot00000000000000[ { "Name": "cangjie_compiler", "License": "Apache-2.0 with Runtime Library Exceptions", "License File": "LICENSE", "Version Number": "1.0.7", "Owner": "xuanjiazhen@huawei.com", "Upstream URL": "https://cangjie-lang.cn/", "Description": "Cangjie is a general-purpose programming language designed for all-scenario application development. It balances development efficiency and runtime performance, providing an excellent programming experience. Cangjie features concise and efficient syntax, multi-paradigm programming, and type safety. " } ] cangjie_compiler-1.0.7/README.md000066400000000000000000000305411510705540100162710ustar00rootroot00000000000000# Cangjie Programming Language Compiler ## Introduction Cangjie is a general-purpose programming language designed for all-scenario application development, balancing development efficiency and runtime performance while providing a great programming experience. Cangjie features concise and efficient syntax, multi-paradigm programming, and type safety. For more information, please refer to the [Cangjie Language Development Guide](https://cangjie-lang.cn/docs?url=%2F1.0.0%2Fuser_manual%2Fsource_zh_cn%2Ffirst_understanding%2Fbasic.html) and the [Cangjie Programming Language White Paper](https://cangjie-lang.cn/docs?url=%2F0.53.18%2Fwhite_paper%2Fsource_zh_cn%2Fcj-wp-abstract.html). This repository provides the source code for the Cangjie compiler, which consists of two main parts: the compiler frontend and modified open-source LLVM components. The latter includes the LLVM backend, opt optimizer, llc, ld linker, and debugger. For details on third-party dependencies, see the [Third-Party Library Documentation](./third_party/README.md). ## Architecture The overall architecture is shown below: ![Architecture Diagram](figures/Compiler_Architecture_Diagram.png) **Architecture Description** - **Compiler Frontend**: Responsible for converting Cangjie source code from text to intermediate representation, including lexical, syntax, macro, and semantic analysis, ensuring code structure and semantics are correct, and preparing for backend code generation. This module depends on mingw-w64 to support Windows platform capabilities, enabling users to generate executable binaries that can call Windows APIs. It also relies on libboundscheck for safe function library access. - **Lexer** breaks down Cangjie source code into meaningful tokens. - **Parser** builds an Abstract Syntax Tree (AST) according to Cangjie grammar rules to reflect program structure. - **Semantic** performs type checking, type inference, and scope analysis on the AST to ensure semantic correctness. - **Mangler** handles symbol name mangling for Cangjie, and includes a demangler tool for reverse parsing. - **Package Management** manages and loads code modules, handles dependencies and namespace isolation, and supports multi-module collaborative development. This module uses the flatbuffer library for serialization and deserialization. - **Macro** handles macro expansion, processing macro definitions and calls for code generation and reuse. - **Condition Compile**: Conditional compilation allows compiling based on predefined or custom conditions; incremental compilation speeds up builds using previous compilation cache files. - **CHIR**: CHIR (Cangjie High Level IR) converts the AST to an intermediate representation and performs optimizations. - **Codegen**: Translates the intermediate representation (CHIR) to LLVM IR, preparing for target machine code (LLVM BitCode) generation. - **LLVM**: Includes the compiler backend and related LLVM toolchain. The backend receives the intermediate representation from the frontend, optimizes it, generates target platform machine code, and links it into executable files. - **opt**: Performs various optimizations on LLVM IR, such as constant folding and loop optimization, to improve code efficiency and quality. - **llc**: Converts optimized LLVM IR to target platform machine code, supporting different hardware architectures. - **ld**: Links multiple object files and libraries into the final executable, resolving symbol references and generating deployable program artifacts. - **debugger**: Provides debugging capabilities for the Cangjie language. For more details on the LLVM toolchain and backend tools, refer to the [LLVM Command Guide](https://llvm.org/docs/CommandGuide/). - **OS**: The Cangjie compiler and LLVM toolchain currently support Windows x86-64, Linux x86-64/AArch64, and Mac x86/arm64. OHOS support is under development. In addition to native compilation, the Cangjie compiler supports cross-compiling binaries for the ohos-aarch64 platform. For details, see the [Cangjie SDK Integration and Build Guide](https://gitcode.com/Cangjie/cangjie_build) and [Platform Support Roadmap](#platform-support-roadmap). ## Directory Structure ```text cangjie_compiler/ ├── cmake # CMake scripts for build assistance ├── demangler # Symbol demangling ├── doc # Documentation ├── figures # Documentation images ├── include # Header files ├── integration_build # Cangjie SDK integration build scripts ├── schema # FlatBuffers schema files for serialization ├── src # Compiler source code │ ├── AST # Abstract Syntax Tree │ ├── Basic # Compiler basic components │ ├── CHIR # Intermediate representation and optimization │ ├── CodeGen # Code generation (CHIR to LLVM IR) │ ├── ConditionalCompilation # Conditional compilation │ ├── Driver # Compiler driver (frontend/backend orchestration) │ ├── Frontend # Compiler instance and workflow │ ├── FrontendTool # Compiler instance for external tools │ ├── IncrementalCompilation # Incremental compilation │ ├── Lex # Lexical analysis │ ├── Macro # Macro expansion │ ├── main.cpp # Compiler entry point │ ├── Mangle # Symbol mangling │ ├── MetaTransformation # Metaprogramming plugins │ ├── Modules # Module management │ ├── Option # Compiler options │ ├── Parse # Syntax analysis │ ├── Sema # Semantic analysis │ └── Utils # Utilities ├── third_party # Third-party build scripts and patch files │ ├── cmake # Third-party CMake scripts │ ├── llvmPatch.diff # LLVM backend patch (includes llvm and cjdb sources) │ └── flatbufferPatch.diff # Flatbuffer source patch ├── unittests # Unit tests └── utils # Auxiliary tools ``` ## Constraints Currently, building Cangjie compiler artifacts directly in the Windows environment is not supported. Instead, you need to generate compiler artifacts that can run on Windows through cross-compilation in a Linux environment. For details, see the [Cangjie SDK Integration Build Guide](https://gitcode.com/Cangjie/cangjie_build/blob/main/README_zh.md). For future support plans, refer to the [Platform Support Roadmap](#platform-support-roadmap). ## Platform Support Roadmap - Build Platform Evolution: Planned support for Windows Native builds of compiler artifacts in 2025 Q4. - Compiler Runtime Platform Evolution: Planned support for running the compiler on the OHOS(PC) platform in 2026 Q2. - Cangjie Application Runtime Platform Evolution: Planned support for OHOS-ARM32 core features on 2025.10.20, reflection and dynamic loading、some compiler Optimization features will support on 2025 Q4. ## Building from Source > **Note:** > > This section describes how to build the Cangjie compiler from source. If you only want to use the compiler to build Cangjie code or projects, skip this section and download the release package from the [official website](https://cangjie-lang.cn/download) or refer to the [Integration Build Guide](#integration-build-guide). ### Preparation For environment requirements and software dependencies on each platform, see the [Standalone Build Guide](doc/Standalone_Build_Guide.md). Clone the source code: ```shell git clone https://gitcode.com/Cangjie/cangjie_compiler.git -b main; ``` ### Build Steps ```shell cd cangjie_compiler python3 build.py clean python3 build.py build -t release python3 build.py install ``` 1. The `clean` command removes temporary files from the workspace. 2. The `build` command starts compilation. The `-t` or `--build-type` option specifies the build type: `release`, `debug`, or `relwithdebinfo`. 3. The `install` command installs the build artifacts to the `output` directory. The `output` directory structure: ```text ./output ├── bin │ ├── cjc # Cangjie compiler executable │ └── cjc-frontend -> cjc # Cangjie compiler frontend executable (symlink) ├── envsetup.sh # Environment setup script ├── include # Public headers for the frontend ├── lib # Compiler libraries (by target platform) ├── modules # Standard library cjo files (by target platform) ├── runtime # Runtime libraries ├── third_party # Third-party binaries and libraries (e.g., LLVM) └── tools # Cangjie tools ``` On Linux, run `source ./output/envsetup.sh` to set up the environment, then use `cjc -v` to check the compiler version and platform info: ```shell source ./output/envsetup.sh cjc -v ``` Example output: ```text Cangjie Compiler: x.xx.xx (cjnative) Target: xxxx-xxxx-xxxx ``` ### Run Unittest Unit tests are built by default. After a successful build, run: ```shell python3 build.py test ``` ### More Build Options For more build options, please refer to the [build.py build script](./build.py) or use the `--help` option: ```shell python3 build.py --help ``` For more platform-specific build information, see the [Standalone Build Guide](doc/Standalone_Build_Guide.md). ### Integration Build Guide For integration builds, please refer to the [Cangjie SDK Integration Build Guide](https://gitcode.com/Cangjie/cangjie_build/blob/main/README_zh.md). ## License This project is licensed under [Apache-2.0 with Runtime Library Exception](./LICENSE). Feel free to use and contribute! ## Related Repositories - [cangjie_docs](https://gitcode.com/Cangjie/cangjie_docs/tree/main/docs/dev-guide) - [cangjie_runtime](https://gitcode.com/Cangjie/cangjie_runtime) - [cangjie_tools](https://gitcode.com/Cangjie/cangjie_tools) - [cangjie_stdx](https://gitcode.com/Cangjie/cangjie_stdx) - [cangjie_build](https://gitcode.com/Cangjie/cangjie_build) - [cangjie_test](https://gitcode.com/Cangjie/cangjie_test) ## Open Source Software Statement | Software Name | License | Usage Description | Main Component | Usage Methods | |---------------------|--------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|-----------------------------|---------------------------------------------| | mingw-w64 | Zope Public License V2.1 | The Cangjie Windows SDK includes some static libraries from Mingw, linked with Cangjie-generated objects to produce Windows executables | Compiler | Integrated into the Cangjie binary release | | LLVM | Apache 2.0 with LLVM Exception | Cangjie compiler backend is based on LLVM. | Compiler | Integrated into the Cangjie binary release | | flatbuffers | Apache License V2.0 | Used for serialization/deserialization of cjo files and macros | Compiler & StdLib(std.ast) | Integrated into the Cangjie binary release | | libboundscheck | Mulan Permissive Software License V2 | Used for safe function implementations in the compiler and related code | Compiler, StdLib, Extension | Integrated into the Cangjie binary release | For details on other build dependencies, see [Build Dependencies](https://gitcode.com/Cangjie/cangjie_build/blob/main/docs/env_zh.md) and the [Cangjie SDK Integration Build Guide](https://gitcode.com/Cangjie/cangjie_build/blob/main/README_zh.md). ## Contribution We welcome contributions from developers in any form, including but not limited to code, documentation, issues, and more. cangjie_compiler-1.0.7/README_zh.md000066400000000000000000000276171510705540100170040ustar00rootroot00000000000000# 仓颉编程语言编译器 ## 简介 仓颉编程语言是一种面向全场景应用开发的通用编程语言,可以兼顾开发效率和运行性能,并提供良好的编程体验。仓颉语言具有语法简明高效、多范式编程、类型安全等特点,了解更多仓颉语言的介绍,请参阅 [仓颉语言开发指南](https://cangjie-lang.cn/docs?url=%2F1.0.0%2Fuser_manual%2Fsource_zh_cn%2Ffirst_understanding%2Fbasic.html) 以及 [仓颉编程语言白皮书](https://cangjie-lang.cn/docs?url=%2F0.53.18%2Fwhite_paper%2Fsource_zh_cn%2Fcj-wp-abstract.html)。 本仓提供了仓颉编译器相关源码,整体包含两部分:编译器前端和 LLVM 开源修改部分,后者包括 LLVM 编译器后端 opt 优化器、llc、ld 链接器以及调试器等。开源组件依赖可参考[第三方库说明](./third_party/README.md)。 ## 系统架构 整体架构如下图展示: ![架构图](figures/Compiler_Architecture_Diagram_zh.png) **架构图说明** - **前端**:负责将仓颉源码从文本转换为中间表示,涵盖词法、语法、宏、语义等分析,确保代码结构和语义正确,为后端生成目标代码做好准备。此模块依赖 mingw-w64 用以支持 Windows 平台仓颉能力,为用户生成最终的可以调用 Windows API 的可执行二进制文件。同时依赖 libboundscheck 提供安全函数库访问。 - **词法分析**将仓颉源码分解为有意义的记号。 - **语法分析**根据仓颉语法规则,将记号序列构建为抽象语法树(AST),反映程序结构。 - **语义分析**对 AST 进行类型检查、类型推断、作用域分析等,确保程序语义正确。 - **名称修饰**负责对仓颉符号进行名称修饰,同时还包含 demangler 反向解析工具。 - **包管理**负责管理和加载代码模块,处理依赖关系与命名空间隔离,支持多模块协同开发,此模块依赖 flatbuffer 三方库能力进行序列化和反序列化。 - **元编程**宏展开处理,处理代码中的宏定义和宏调用,实现代码生成和复用。 - **条件编译**条件编译可以通过预定义或者自定的条件进行编译。 - **CHIR 生成及优化**CHIR为 Cangjie High Level IR,此模块将 AST 转换为编译器中间层表示,并进行优化。 - **LLVM IR 代码生成**将中间表示(CHIR)翻译为 LLVM IR,准备生成目标机器码(LLVM BitCode)。 - **LLVM**:包括编译器后端以及 LLVM 相关工具链,编译器后端接收前端生成的中间表示,经过优化、生成目标平台机器码,并通过链接器整合为可执行文件。 - **opt 优化器**:对 LLVM IR 进行多种优化处理,如常量折叠、循环优化等,提高生成代码的执行效率和质量。 - **llc 编译器**:将优化后的 LLVM IR 转换为目标平台的机器码,适配不同硬件架构。 - **ld 链接器**:将多个目标文件和依赖库链接为最终的可执行文件,解决符号引用,生成可部署的程序产物。 - **调试器**:提供仓颉语言相关的调试能力。 llvm 其他工具链以及更详细的后端工具说明,可以参考[llvm 命令指南](https://llvm.org/docs/CommandGuide/)。 - **OS**:仓颉编译器及 LLVM 相关工具链当前支持在如下平台运行 Windows x86-64、Linux x86-64/AArch64、Mac x86/arm64,鸿蒙平台正在开发中。同时,除可 native 编译出上述平台产物外,仓颉编译器还支持交叉编译出 ohos-aarch64 平台二进制产物,详细请参考[仓颉SDK集成构建指导书](https://gitcode.com/Cangjie/cangjie_build#%E4%BB%93%E9%A2%89sdk%E9%9B%86%E6%88%90%E6%9E%84%E5%BB%BA%E6%8C%87%E5%AF%BC%E4%B9%A6)和[平台支持计划](#平台支持计划)。 ## 目录结构 ```text /cangjie_compiler ├── cmake # cmake文件夹,用于保存构建辅助脚本 ├── demangler # 符号还原 ├── doc # 文档 ├── figures # 文档相关图片 ├── include # 头文件文件夹 ├── integration_build # 仓颉SDK集成构建脚本 ├── schema # 用于保存 FlatBuffers Schema 序列化数据结构文件 ├── src # 编译器源码文件夹 │ ├── AST # 抽象语法树相关内容 │ ├── Basic # 编译器基础组件 │ ├── CHIR # 编译器中间层,该阶段对代码进行优化分析 │ ├── CodeGen # 代码生成,将 CHIR 翻译为 LLVMIR │ ├── ConditionalCompilation # 条件编译 │ ├── Driver # 编译器流程驱动,用于启动前端以及调用后端命令 │ ├── Frontend # 编译器实例类,组织编译器流程 │ ├── FrontendTool # 提供给周边工具的编译器实例类 │ ├── IncrementalCompilation # 增量编译 │ ├── Lex # 词法分析 │ ├── Macro # 宏展开 │ ├── main.cpp # 编译器入口 │ ├── Mangle # 符号改名 │ ├── MetaTransformation # 元编程编译器插件 │ ├── Modules # 包管理模块 │ ├── Option # 编译器选项控制 │ ├── Parse # 语法分析 │ ├── Sema # 语义分析 │ └── Utils # 公共组件 ├── third_party # 依赖的三方库构建脚本及依赖库 patch 文件 │ ├── cmake # 三方库 cmake 构建辅助脚本 │ ├── llvmPatch.diff # llvm 后端 patch 文件,包含 llvm 及 cjdb 相关源码 │ └── flatbufferPatch.diff # flatbuffer 源码 patch 文件 ├── unittests # 单元测试用例 └── utils # 编译器周边组件 ``` ## 约束 当前暂不支持 Windows 环境内构建仓颉编译器产物,需要在 Linux 环境内通过交叉编译方式生成可在 Windows 平台运行的编译器产物,详见[仓颉 SDK 集成构建指导书](https://gitcode.com/Cangjie/cangjie_build/blob/main/README_zh.md)。未来支持计划具体见[平台支持计划](#平台支持计划)。 ## 平台支持计划 - 构建平台演进:计划 2025 Q4 支持 Windows Native 构建仓颉编译器产物。 - 编译器运行平台演进:计划 2026 Q2 支持 OHOS(PC)平台可运行仓颉编译器。 - 仓颉应用运行平台演进:计划 2025.10.20 支持仓颉应用在 OHOS-ARM32 平台运行,反射、动态加载、部分编译器优化等高级特性计划 2025 Q4 支持。 ## 编译构建 > **注意:** > > 此章节描述了如何从源码对仓颉编译器进行构建。如果您期望使用仓颉编译器编译仓颉源代码或项目,请忽略此章节并移步 [仓颉官方网站下载页](https://cangjie-lang.cn/download) 下载发布包或参阅 [集成构建指导](#集成构建指导) 章节进行集成构建。 ### 构建准备 在各个平台构建编译器所需的环境要求及软件依赖请参阅 [独立构建指导书](doc/Standalone_Build_Guide.md)。 下载源码: ```shell git clone https://gitcode.com/Cangjie/cangjie_compiler.git -b main; ``` ### 构建步骤 ```shell cd cangjie_compiler python3 build.py clean python3 build.py build -t release python3 build.py install ``` 1. `clean` 命令用于清空工作区临时文件; 2. `build` 命令开始执行编译,选项 `-t` 即 `--build-type`,指定编译产物类型,可以是 `release`、 `debug` 或 `relwithdebinfo`; 3. `install` 命令将编译产物安装到 `output` 目录下。 `output` 目录结构如下: ```text ./output ├── bin │ ├── cjc # 仓颉编译器可执行文件 │ └── cjc-frontend -> cjc # 仓颉编译器前端可执行文件 ├── envsetup.sh # 一键环境变量配置脚本 ├── include # 编译前端对外头文件 ├── lib # 仓颉编译产物依赖库,子文件夹按照目标平台拆分 ├── modules # 仓颉标准库 cjo 文件预留文件夹,子文件夹按照目标平台拆分 ├── runtime # 仓颉编译产物依赖运行时库 ├── third_party # llvm 等第三方依赖二进制及库 └── tools # 仓颉工具文件夹 ``` Linux 环境下可通过 `source ./output/envsetup.sh` 命令应用 cjc 环境,执行 `cjc -v` 查看当前编译器版本信息及 cjc 的平台信息: ```shell source ./output/envsetup.sh cjc -v ``` 输出如下: ```text Cangjie Compiler: x.xx.xx (cjnative) Target: xxxx-xxxx-xxxx ``` ### 执行 unittest 单元测试用例 单元测试用例在编译构建时默认构建,构建成功后通过以下指令进行验证: ```shell python3 build.py test ``` ### 更多构建选项 如需了解更多构建选项,请参阅 [build.py 构建脚本](./build.py) 或通过 `--help` 选项查看。 ```shell python3 build.py --help ``` 如需了解更多平台编译,请参阅 [独立构建指导书](doc/Standalone_Build_Guide.md)。 ### 集成构建指导 集成构建请参阅 [仓颉 SDK 集成构建指导书](https://gitcode.com/Cangjie/cangjie_build/blob/main/README_zh.md)。 ## 开源协议 本项目基于 [Apache-2.0 with Runtime Library Exception](./LICENSE),请自由地享受和参与开源。 ## 相关仓 - [cangjie_docs](https://gitcode.com/Cangjie/cangjie_docs/tree/main/docs/dev-guide) - [cangjie_runtime](https://gitcode.com/Cangjie/cangjie_runtime) - [cangjie_tools](https://gitcode.com/Cangjie/cangjie_tools) - [cangjie_stdx](https://gitcode.com/Cangjie/cangjie_stdx) - [cangjie_build](https://gitcode.com/Cangjie/cangjie_build) - [cangjie_test](https://gitcode.com/Cangjie/cangjie_test) ## 使用的开源软件声明 | 开源软件名称 | 开源许可协议 | 使用说明 | 使用主体 | 使用方式 | |---------------------|---------------------------------------------|-------------------------------------------------------------------------------------------|-----------------------|----------------------| | mingw-w64 | Zope Public License V2.1 | 仓颉 Windows 版本 SDK 携带 Mingw 中的部分静态库文件,与仓颉代码生成的目标文件链接在一起,为用户生成最终的可以调用 Windows API 的可执行二进制文件 | 编译器 | 集成到仓颉二进制发布包中 | | LLVM | Apache 2.0 with LLVM Exception | 仓颉编译器后端基于 llvm 开发实现 | 编译器 | 集成到仓颉二进制发布包中 | | flatbuffers | Apache License V2.0 | 仓颉的 cjo 文件和宏实现依赖该软件进行序列化和反序列化 | 编译器和标准库(std.ast) | 集成到仓颉二进制发布包中 | | libboundscheck | Mulan Permissive Software License Version 2 | 编译器等相关代码基于该软件实现 | 编译器、标准库、扩展库 | 集成到仓颉二进制发布包中 | 其他构建依赖使用情况可参考[构建依赖工具](https://gitcode.com/Cangjie/cangjie_build/blob/main/docs/env_zh.md)及[仓颉 SDK 集成构建指导书](https://gitcode.com/Cangjie/cangjie_build/blob/main/README_zh.md)。 ## 参与贡献 欢迎开发者们提供任何形式的贡献,包括但不限于代码、文档、issue 等。cangjie_compiler-1.0.7/build.py000077500000000000000000000663051510705540100164750ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. """cangjie compiler build entry""" import argparse import logging import multiprocessing import os import platform import re import shutil import stat import subprocess import sys from enum import Enum from logging.handlers import TimedRotatingFileHandler from pathlib import Path from subprocess import DEVNULL, PIPE HOME_DIR = os.path.dirname(os.path.abspath(__file__)) BUILD_DIR = os.path.join(HOME_DIR, "build") CMAKE_BUILD_DIR = os.path.join(BUILD_DIR, "build") CMAKE_OUTPUT_DIR = os.path.join(HOME_DIR, "output") OUTPUT_BIN_DIR = os.path.join(CMAKE_OUTPUT_DIR, "bin") LOG_DIR = os.path.join(BUILD_DIR, "logs") LOG_FILE = os.path.join(LOG_DIR, "cangjie.log") IS_WINDOWS = platform.system() == "Windows" IS_MACOS = platform.system() == "Darwin" IS_ARM = platform.uname().processor in ["aarch64", "arm", "arm64"] CJ_OS_NAME = platform.system().lower() CJ_ARCH_NAME = platform.machine().replace("AMD64", "x86_64").lower() # normalize AMD64 in Windows to x86_64 CAN_RUN_CJNATIVE = (platform.system(), platform.machine()) in [ ("Linux", "x86_64"), ("Linux", "aarch64"), ("Windows", "AMD64"), ("Darwin", "x86_64"), ("Darwin", "arm64") ] LD_LIBRARY_PATH = "Path" if IS_WINDOWS else "DYLD_LIBRARY_PATH" if IS_MACOS else "LD_LIBRARY_PATH" LIBRARY_PATH = "Path" if IS_WINDOWS else "LIBRARY_PATH" PATH_ENV_SEPERATOR = ";" if IS_WINDOWS else ":" # Wait for the version of aarch64 libcore to be ready. DELTA_JOBS = 2 MAKE_JOBS = multiprocessing.cpu_count() + DELTA_JOBS PYTHON_EXECUTABLE = sys.executable def resolve_path(path): if os.path.isabs(path): return path return os.path.abspath(path) def log_output(output, checker=None, args=[]): """log command output""" while True: line = output.stdout.readline() if not line: output.communicate() returncode = output.returncode if returncode != 0: LOG.error("build error: %d!\n", returncode) sys.exit(1) break try: LOG.info(line.decode("ascii", "ignore").rstrip()) except UnicodeEncodeError: LOG.info(line.decode("utf-8", "ignore").rstrip()) def generate_version_tail(target): """generate version string for cjc compiler""" res = "{}".format("cjnative") res = " (" + res + ")" arch_name = platform.machine().replace("AMD64", "x86_64").replace("arm64", "aarch64").lower() vendor_name = "apple" if IS_MACOS else "unknown" os_name = None if IS_WINDOWS: os_name = "windows-gnu" elif IS_MACOS: os_name = "darwin" else: os_name = "linux-gnu" res += "\\nTarget: {}".format(target) if target else "\\nTarget: {0}-{1}-{2}".format(arch_name, vendor_name, os_name) return str(res) def generate_cmake_defs(args): """convert args to cmake defs""" def bool_to_opt(value): return "ON" if value else "OFF" if args.target: if args.target == "aarch64-linux-ohos": toolchain_file = "ohos_aarch64_clang_toolchain.cmake" elif args.target == "arm-linux-ohos": toolchain_file = "ohos_arm_clang_toolchain.cmake" elif args.target == "x86_64-linux-ohos": toolchain_file = "ohos_x86_64_clang_toolchain.cmake" elif args.target == "x86_64-w64-mingw32": toolchain_file = "mingw_x86_64_toolchain.cmake" elif args.target == "arm64-apple-ios11-simulator": toolchain_file = "ios_simulator_arm64_toolchain.cmake" elif args.target == "arm64-apple-ios11": toolchain_file = "ios_arm64_toolchain.cmake" elif "aarch64-linux-android" in args.target: toolchain_file = "android_aarch64_toolchain.cmake" elif "x86_64-linux-android" in args.target: toolchain_file = "android_x86_64_toolchain.cmake" else: args.target = None if IS_WINDOWS: toolchain_file = "mingw_x86_64_toolchain.cmake" elif IS_MACOS: toolchain_file = "darwin_aarch64_toolchain.cmake" if IS_ARM else "darwin_x86_64_toolchain.cmake" elif IS_ARM: toolchain_file = "linux_aarch64_toolchain.cmake" else: toolchain_file = "linux_x86_64_toolchain.cmake" install_prefix = CMAKE_OUTPUT_DIR + ("-{}".format(args.target) if (args.target and args.product == "cjc") else "") result = [ "-DCMAKE_BUILD_TYPE=" + args.build_type.value, "-DCMAKE_ENABLE_ASSERT=" + bool_to_opt(args.enable_assert), "-DCMAKE_INSTALL_PREFIX=" + install_prefix, "-DCMAKE_TOOLCHAIN_FILE=" + os.path.join(HOME_DIR, "cmake/" + toolchain_file), "-DCMAKE_PREFIX_PATH=" + (args.target_toolchain + "/../x86_64-w64-mingw32" if args.target_toolchain else ""), "-DCANGJIE_BUILD_TESTS=" + bool_to_opt((not args.no_tests) and (args.product == "all" or args.product == "libs")), "-DCANGJIE_BUILD_CJC=" + bool_to_opt(args.product in ['all', 'cjc']), "-DCANGJIE_BUILD_STD_SUPPORT=" + bool_to_opt(args.product in ['all', 'libs']), "-DCANGJIE_BUILD_CJDB=" + bool_to_opt(args.build_cjdb), "-DCANGJIE_ENABLE_HWASAN=" + bool_to_opt(args.hwasan), "-DCANGJIE_VERSION=" + generate_version_tail(args.target), "-DBUILD_GCC_TOOLCHAIN=" + (args.gcc_toolchain if args.gcc_toolchain and args.target is None else ""), "-DCANGJIE_TARGET_LIB=" + (";".join(args.target_lib) if args.target_lib else ""), "-DCANGJIE_TARGET_TOOLCHAIN=" + (args.target_toolchain if args.target_toolchain else ""), "-DCANGJIE_INCLUDE=" + (";".join(args.include) if args.include else ""), "-DCANGJIE_TARGET_SYSROOT=" + (args.target_sysroot if args.target_sysroot else ""), "-DCANGJIE_LINK_JOB_POOL=" + (str(args.link_jobs) if args.link_jobs != 0 else ""), "-DCANGJIE_DISABLE_STACK_GROW_FEATURE=" + bool_to_opt(args.disable_stack_grow_feature), "-DCANGJIE_USE_OH_LLVM_REPO=" + bool_to_opt(args.use_oh_llvm_repo)] if args.target and "aarch64-linux-android" in args.target: android_api_level = re.match(r'aarch64-linux-android(\d{2})?', args.target).group(1) result.append("-DCMAKE_ANDROID_NDK=" + (args.android_ndk if args.android_ndk else "")) result.append("-DCMAKE_ANDROID_API=" + (android_api_level if android_api_level else "")) return result def build(args): # The target also affects the prefix concatenation of the compiler executable file; it is specific. if args.target: if args.target == "native": args.target = None elif args.target == "ohos-aarch64": args.target = "aarch64-linux-ohos" elif args.target == "ohos-arm": args.target = "arm-linux-ohos" elif args.target == "ohos-x86_64": args.target = "x86_64-linux-ohos" elif args.target == "windows-x86_64": args.target = "x86_64-w64-mingw32" elif args.target == "ios-simulator-aarch64": args.target = "arm64-apple-ios11-simulator" elif args.target == "ios-aarch64": args.target = "arm64-apple-ios11" elif args.target == "android-aarch64": args.target = "aarch64-linux-android31" elif args.target == "android-x86_64": args.target = "x86_64-linux-android31" if args.gcc_toolchain and args.target and args.product != "cjc": LOG.warning("There is no intermediate or product targeting the host platform in this build, so --gcc-toolchain won't take effect") check_compiler(args) """build cangjie compiler""" LOG.info("begin build...") set_cjnative_backend_env(args) if args.product is None: args.product = "all" if args.target is None else "libs" if args.target == "aarch64-linux-ohos" or args.target == "x86_64-linux-ohos": # Frontend supports cross compilation in a general way by asking path to required tools # and libraries. However, Runtime supports cross compilation in a speific way, which asks # for the root path of OHOS toolchain. Since we asked for a path to tools, the root path of # OHOS toolchain is relative to the tool path we get. Tool path normally looks like # ${OHOS_ROOT}/prebuilts/clang/ohos/linux-x86_64/llvm/bin/. Six /.. can bring us to the root. os.environ["OHOS_ROOT"] = os.path.join(args.target_toolchain, "../../../../../..") if args.android_ndk: os.environ["ANDROID_NDK_ROOT"] = args.android_ndk if IS_WINDOWS: # For Windows, try Ninja first, otherwise use Make instead. ninja_valid = True try: output = subprocess.Popen(["ninja", "--version"], stdout=DEVNULL, stderr=DEVNULL) output.communicate() ninja_valid = output.returncode == 0 except: ninja_valid = False if ninja_valid: generator = "Ninja" build_cmd = ["ninja"] if args.jobs > 0: build_cmd.extend(["-j", str(args.jobs)]) else: generator = "MinGW Makefiles" build_cmd = ["mingw32-make", "-j" + str(MAKE_JOBS)] else: # For Linux, just use Ninja. generator = "Ninja" build_cmd = ["ninja"] if args.jobs > 0: build_cmd.extend(["-j", str(args.jobs)]) cmake_command = ["cmake", HOME_DIR, "-G", generator] + generate_cmake_defs(args) if args.print_cmd: print(' '.join(cmake_command)) exit(0) if args.target == "x86_64-w64-mingw32": package_mingw_dependencies(args) if not os.path.exists(BUILD_DIR): os.makedirs(BUILD_DIR) cmake_build_dir = os.path.join(BUILD_DIR, "build-{}-{}".format(args.product, args.target)) if args.target else CMAKE_BUILD_DIR if not os.path.exists(os.path.join(cmake_build_dir, "build.ninja")): os.makedirs(cmake_build_dir, exist_ok=True) output = subprocess.Popen(cmake_command, cwd=cmake_build_dir, stdout=PIPE) log_output(output) output = subprocess.Popen(build_cmd, cwd=cmake_build_dir, stdout=PIPE) log_output(output) if output.returncode != 0: LOG.fatal("build failed") LOG.info("end build") def set_cangjie_env(args): os.environ["HOME_DIR"] = HOME_DIR os.environ[LD_LIBRARY_PATH] = PATH_ENV_SEPERATOR.join([ os.path.join(HOME_DIR, "output/lib"), os.path.join(HOME_DIR, "output/runtime/lib/{os}_{arch}_cjnative".format(os=CJ_OS_NAME, arch=CJ_ARCH_NAME)), os.environ.get(LD_LIBRARY_PATH, "") ]) LOG.info("set cangjie env: %s=%s", LD_LIBRARY_PATH, os.environ[LD_LIBRARY_PATH]) def set_cjnative_backend_env(args): os.environ[LD_LIBRARY_PATH] = PATH_ENV_SEPERATOR.join([ os.path.join(HOME_DIR, CMAKE_OUTPUT_DIR, "lib"), os.path.join(HOME_DIR, CMAKE_OUTPUT_DIR, "lib/{os}_{arch}_cjnative".format(os=CJ_OS_NAME, arch=CJ_ARCH_NAME)), os.path.join(HOME_DIR, "build/build/lib"), os.path.join(HOME_DIR, "build/build/lib/{os}_{arch}_cjnative".format(os=CJ_OS_NAME, arch=CJ_ARCH_NAME)), os.environ.get(LD_LIBRARY_PATH, "") ] + (args.target_lib if hasattr(args, 'target_lib') else [])) LOG.info("set cjnative backend env: %s=%s", LD_LIBRARY_PATH, os.environ[LD_LIBRARY_PATH]) os.environ[LIBRARY_PATH] = PATH_ENV_SEPERATOR.join([ os.path.join(HOME_DIR, "build/build/lib"), os.environ.get(LIBRARY_PATH, "") ] + (args.target_lib if hasattr(args, 'target_lib') else [])) LOG.info("set cjnative backend env: %s=%s", LIBRARY_PATH, os.environ[LIBRARY_PATH]) os.environ["PATH"] = PATH_ENV_SEPERATOR.join([ os.path.join(HOME_DIR, "output/tools/bin"), os.environ.get("PATH", "")]) if IS_MACOS: os.environ["ZERO_AR_DATE"] = "1" def test(args): """test""" LOG.info("begin test...") if platform.system() == "Linux" or platform.system() == "Darwin": set_cangjie_env(args) unit_test(args) LOG.info("end test...\n") def unit_test(args): """unit test""" LOG.info("begin unit test...\n") output = subprocess.Popen( ["ctest", "--output-on-failure"], cwd=CMAKE_BUILD_DIR, stdout=PIPE ) log_output(output) LOG.info("end unit test...\n") def install(args): """install targets""" if args.host: if args.host == "native": args.host = None elif args.host == "ohos-aarch64": args.host = "aarch64-linux-ohos" elif args.host == "ohos-arm": args.host = "arm-linux-ohos" elif args.host == "ohos-x86_64": args.host = "x86_64-linux-ohos" elif args.host == "windows-x86_64": args.host = "x86_64-w64-mingw32" elif args.host == "ios-simulator-aarch64": args.host = "arm64-apple-ios11-simulator" elif args.host == "ios-aarch64": args.host = "arm64-apple-ios11" elif args.host == "android-aarch64": args.host = "aarch64-linux-android" elif args.host == "android-x86_64": args.host = "x86_64-linux-android" LOG.info("begin install targets...") targets = [] # - If "build.py install" is invoked without "--host", # the native build directories and all cross-compiled libs # will be installed to the "output" directory. # - If "build.py install" is invoked with "--host ", # the build-cjc- directories and all cross-compiled libs # will be installed to a seperated "output-" directory. # Searching for cjc's build directory. if not args.host: if os.path.exists(CMAKE_BUILD_DIR): targets.append(("native", CMAKE_BUILD_DIR)) else: suffix = "cjc-{}".format(args.host) cross_build_path = os.path.join(BUILD_DIR, "build-{}".format(suffix)) if os.path.exists(cross_build_path): targets.append((suffix, cross_build_path)) # Searching for all libs' build directories. for directory in os.listdir(BUILD_DIR): build_prefix = "build-libs-" if directory.startswith(build_prefix): targets.append(("libs-{}".format(directory[len(build_prefix):]), os.path.join(BUILD_DIR, directory))) if len(targets) == 0: LOG.fatal("Nothing is built yet.") sys.exit(1) # install for all build directories in the list for target in targets: LOG.info("installing {} build...".format(target[0])) cmake_cmd = ["cmake", "--install", "."] if args.prefix: cmake_cmd += ["--prefix", os.path.abspath(args.prefix)] elif args.host: cmake_cmd += ["--prefix", os.path.join(HOME_DIR, "output-{}".format(args.host))] output = subprocess.Popen(cmake_cmd, cwd=target[1], stdout=PIPE) log_output(output) if output.returncode != 0: LOG.fatal("install {} build failed".format(target[0])) sys.exit(1) if args.host == "x86_64-w64-mingw32": mingw_bin_path = os.path.join(HOME_DIR, "output-x86_64-w64-mingw32/third_party/mingw/bin") if os.path.exists(mingw_bin_path): for bin in os.listdir(mingw_bin_path): bin_path = os.path.join(mingw_bin_path, bin) if os.path.isfile(bin_path) and not bin.endswith(".exe") and bin != "libssp-0.dll": os.remove(bin_path) LOG.info("end install targets...") def redo_with_write(redo_func, path, err): # Is the error an access error? if not os.access(path, os.W_OK): os.chmod(path, stat.S_IWUSR) redo_func(path) else: raise def clean(args): """clean build outputs and logs""" LOG.info("begin clean...\n") # In order to successfully delete all files in the build directory, # the file handlers (especially for cangjie.log) that this script holds must be closed first. handlerToBeRemoved = [] for handler in LOG.handlers: if isinstance(handler, logging.FileHandler): handler.close() handlerToBeRemoved.append(handler) for handler in handlerToBeRemoved: LOG.removeHandler(handler) output_dirs = [] # Remove entire build directory by default output_dirs.append("build") output_dirs.append("output") # Files in `cjnative_backend` directory may be used while installing if native build is built # from binary. It can only be removed when `build.py clean` is being applied to all build targets. output_dirs.append("third_party/binary/llvm_backend") # Collecting all cross-compiled cjcs' build directories. cross_output_suffix = [] for directory in os.listdir(BUILD_DIR): build_prefix = "build-cjc-" if directory.startswith(build_prefix): cross_output_suffix.append(directory[len(build_prefix):]) # Removing all cross-compiled output package. for suffix in cross_output_suffix: cross_output_path = os.path.join(HOME_DIR, "output-{}".format(suffix)) if os.path.isdir(cross_output_path): output_dirs.append(cross_output_path) for file_path in output_dirs: abs_file_path = os.path.join(HOME_DIR, file_path) if os.path.isdir(abs_file_path): # If some build files are read-only and not allowed to be deleted (especially in Windows), # Try to deal with the error by giving write permissions and deleting them again. shutil.rmtree(abs_file_path, ignore_errors=False, onerror=redo_with_write) if os.path.isfile(abs_file_path): os.remove(abs_file_path) LOG.info("end clean\n") def package_mingw_dependencies(args): if os.path.exists(os.path.join(HOME_DIR, "third_party/binary/windows-x86_64-mingw.tar.gz")): return LOG.info("Packaging mingw dependencies...") def copy_files_to(from_path, filename_list, to_directory): if not os.path.exists(to_directory): os.makedirs(to_directory, exist_ok=True) for filename in filename_list: filepath = os.path.join(from_path, filename) if os.path.exists(filepath): shutil.copy(filepath, to_directory + "/") else: LOG.info("%s not found, skipped packaging the file...", filepath) search_path = args.target_toolchain if args.target_toolchain else None mingw_path = str(os.path.dirname(os.path.dirname(os.path.abspath(shutil.which("x86_64-w64-mingw32-gcc", path=search_path))))) mingw_package_path = str(os.path.join(HOME_DIR, "third_party/binary/mingw")) dll_dependencies = ["bin/libc++.dll", "bin/libunwind.dll", "bin/libwinpthread-1.dll"] copy_files_to(os.path.join(mingw_path, "x86_64-w64-mingw32"), dll_dependencies, os.path.join(mingw_package_path, "dll")) lib_dependencies = ["crt2.o", "dllcrt2.o", "libadvapi32.a", "libkernel32.a", "libm.a", "libmingw32.a", "libmingwex.a", "libmoldname.a", "libmsvcrt.a", "libpthread.a", "libshell32.a", "libuser32.a", "libws2_32.a", "libcrypt32.a", "crtbegin.o", "crtend.o"] copy_files_to(os.path.join(mingw_path, "x86_64-w64-mingw32/lib"), lib_dependencies, os.path.join(mingw_package_path, "lib")) subprocess.run(["tar", "-C", "mingw", "-zcf", "windows-x86_64-mingw.tar.gz", "dll", "lib"], cwd=os.path.join(HOME_DIR, "third_party/binary")) def init_log(name): """init log config""" if not os.path.exists(LOG_DIR): os.makedirs(LOG_DIR) log = logging.getLogger(name) log.setLevel(logging.DEBUG) formatter = logging.Formatter( "[%(asctime)s] [%(module)s] [%(levelname)s] %(message)s", "%Y-%m-%d %H:%M:%S" ) streamhandler = logging.StreamHandler(sys.stdout) streamhandler.setLevel(logging.DEBUG) streamhandler.setFormatter(formatter) log.addHandler(streamhandler) filehandler = TimedRotatingFileHandler( LOG_FILE, when="W6", interval=1, backupCount=60 ) filehandler.setLevel(logging.DEBUG) filehandler.setFormatter(formatter) log.addHandler(filehandler) return log class BuildType(Enum): """CMAKE_BUILD_TYPE options""" debug = "Debug" release = "Release" relwithdebinfo = "RelWithDebInfo" def __str__(self): return self.name def __repr__(self): return str(self) @staticmethod def argparse(s): try: return BuildType[s] except KeyError: return s.build_type SupportedTarget = [ "native", "windows-x86_64", "ohos-aarch64", "ohos-arm", "ohos-x86_64", "ios-simulator-aarch64", "ios-aarch64", "android-aarch64", "android-x86_64" ] def main(): """build entry""" parser = argparse.ArgumentParser(description="build cangjie project") subparsers = parser.add_subparsers(help="sub command help") parser_clean = subparsers.add_parser("clean", help="clean build", add_help=False) parser_clean.set_defaults(func=clean) parser_build = subparsers.add_parser("build", help=" build cangjie") parser_build.add_argument( "-t", "--build-type", type=BuildType.argparse, dest="build_type", choices=list(BuildType), required=True, help="select target build type" ) parser_build.add_argument( "--print-cmd", action="store_true", help="print the command to cmake without running" ) parser_build.add_argument( "-j", "--jobs", dest="jobs", type=int, default=0, help="run N jobs in parallel (0 means default)" ) parser_build.add_argument( "--link-jobs", dest="link_jobs", type=int, default=0, help="max N link jobs in parallel (0 means default)" ) parser_build.add_argument( "--enable-assert", action="store_true", help="build with assert and self-checking, only effective in release mode" ) parser_build.add_argument( "--no-tests", action="store_true", help="build without unittests" ) # cjnative BE supports stack grow feature and cjc enables stack grow feature by default. However, the feature is # supported at a cost of code-size and performance. In code-size and performance sensitive scenario, the feature # can be disabled by specifying the following build option. parser_build.add_argument( "--disable-stack-grow-feature", action="store_true", help="disable default stack grow feature for cjnative BE" ) parser_build.add_argument( "--hwasan", action="store_true", help="build with hardware asan, only available when the target is ohos-aarch64 or ohos-x86_64" ) parser_build.add_argument( "--gcc-toolchain", dest="gcc_toolchain", help="Specify toolchain for cjc, stdlib & BE build" ) parser_build.add_argument( "--target", dest="target", type=str, choices=SupportedTarget, help="build a second stdlib for the target triple specified" ) parser_build.add_argument( "-L", "--target-lib", dest="target_lib", type=str, action='append', default=[], help="link libraries under this path for all products" ) parser_build.add_argument( "--target-toolchain", dest="target_toolchain", type=str, help="use the tools under the given path to cross-compile stdlib" ) parser_build.add_argument( "-I", "--include", dest="include", type=str, action='append', default=[], help="search header files in given paths" ) parser_build.add_argument( "--target-sysroot", dest="target_sysroot", type=str, help="pass this argument to C/CXX compiler as --sysroot" ) parser_build.add_argument( "--android-ndk", dest="android_ndk", type=str, help="Specify the path to the android NDK" ) product_types=['all', 'cjc', 'libs'] parser_build.add_argument( "--product", dest="product", choices=product_types, help="specify which part of Cangjie to build" ) parser_build.add_argument( "--build-cjdb", action="store_true", help="build cjc with cjdb" ) parser_build.add_argument( "--use-oh-llvm-repo", action="store_true", help="use OpenHarmony llvm repo with Cangjie llvm patch instead of cangjie llvm repo for building" ) parser_build.set_defaults(func=build) parser_install = subparsers.add_parser("install", help="install targets") parser_install.add_argument( "--host", dest="host", type=str, choices=SupportedTarget, help="Generate installation package for the host. When --prefix is specified, this option will not take effect" ) parser_install.add_argument( "--prefix", dest="prefix", help="target install prefix" ) parser_install.set_defaults(func=install) parser_test = subparsers.add_parser("test", help="run unittests", add_help=False) parser_test.set_defaults(func=test) args = parser.parse_args() args.func(args) def check_compiler(args): # If user didn't specify --target-toolchain, we search for an available compiler in $PATH. # If user did specify --target-toolchain, we search in user given path ONLY. By doing so # user could see a 'compiler not found' error if the given path is incorrect. toolchain_path = args.target_toolchain if args.target_toolchain else None if toolchain_path and (not os.path.exists(toolchain_path)): LOG.error("the given toolchain path does not exist, {}".format(toolchain_path)) # The gcc with the MinGW triple prefix is used for Windows native compiling. if IS_WINDOWS and args.target is None: c_compiler = shutil.which("x86_64-w64-mingw32-gcc.exe", path=toolchain_path) cxx_compiler = shutil.which("x86_64-w64-mingw32-g++.exe", path=toolchain_path) else: # On other platform, clang is always the first choice. c_compiler = shutil.which("clang", path=toolchain_path) cxx_compiler = shutil.which("clang++", path=toolchain_path) # If clang is not available and we are cross compiling, we check for gcc cross compiler. if (c_compiler == None or cxx_compiler == None) and args.target: c_compiler = shutil.which(args.target + "-gcc", path=toolchain_path) cxx_compiler = shutil.which(args.target + "-g++", path=toolchain_path) # If none of above is available, we search for generic gcc compiler. if c_compiler == None or cxx_compiler == None: c_compiler = shutil.which("gcc", path=toolchain_path) cxx_compiler = shutil.which("g++", path=toolchain_path) if c_compiler == None or cxx_compiler == None: if toolchain_path: LOG.error("cannot find available compiler in the given toolchain path") else: LOG.error("cannot find available compiler in $PATH") LOG.error("clang/clang++ or gcc/g++ is required to build cangjie compiler") # If cross-compiling cjc, a native compiler is also needed by cross-building LLVM, # because it needs some native tools like llvm-tblgen to generate codes. # In this case, CMake will use the value in $CC as the native compiler. if args.target and args.product == "cjc": os.environ["CC"] = shutil.which("clang") + (" --gcc-toolchain={}".format(args.gcc_toolchain) if args.gcc_toolchain else "") os.environ["CXX"] = shutil.which("clang++") + (" --gcc-toolchain={}".format(args.gcc_toolchain) if args.gcc_toolchain else "") else: os.environ["CC"] = c_compiler os.environ["CXX"] = cxx_compiler if __name__ == "__main__": LOG = init_log("root") os.environ["LANG"] = "C.UTF-8" main() cangjie_compiler-1.0.7/cmake/000077500000000000000000000000001510705540100160675ustar00rootroot00000000000000cangjie_compiler-1.0.7/cmake/android_aarch64_toolchain.cmake000066400000000000000000000020041510705540100240550ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. get_filename_component(CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) include("${CMAKE_DIR}/linux_toolchain.cmake") set(CMAKE_SYSTEM_NAME "Android") set(CMAKE_SYSTEM_PROCESSOR "aarch64") if(NOT CMAKE_ANDROID_API) set(CMAKE_ANDROID_API 31) message(STATUS "Android API level is not set, use default setting: ${CMAKE_ANDROID_API}") endif() set(CMAKE_ANDROID_ARCH_ABI "arm64-v8a") string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} HOST_OS) set(CANGJIE_TARGET_TOOLCHAIN "${CMAKE_ANDROID_NDK}/toolchains/llvm/prebuilt/${HOST_OS}-${CMAKE_HOST_SYSTEM_PROCESSOR}/bin") set(TRIPLE aarch64-linux-android${CMAKE_ANDROID_API}) set(TARGET_TRIPLE_DIRECTORY_PREFIX linux_android${CMAKE_ANDROID_API}_aarch64) # Variable ANDROID will be set by CMake. Custom variables are not necessary here.cangjie_compiler-1.0.7/cmake/android_x86_64_toolchain.cmake000066400000000000000000000017761510705540100236020ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. get_filename_component(CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) include("${CMAKE_DIR}/linux_toolchain.cmake") set(CMAKE_SYSTEM_NAME "Android") set(CMAKE_SYSTEM_PROCESSOR "x86_64") if(NOT CMAKE_ANDROID_API) set(CMAKE_ANDROID_API 31) message(STATUS "Android API level is not set, use default setting: ${CMAKE_ANDROID_API}") endif() set(CMAKE_ANDROID_ARCH_ABI "x86_64") string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} HOST_OS) set(CANGJIE_TARGET_TOOLCHAIN "${CMAKE_ANDROID_NDK}/toolchains/llvm/prebuilt/${HOST_OS}-${CMAKE_HOST_SYSTEM_PROCESSOR}/bin") set(TRIPLE x86_64-linux-android${CMAKE_ANDROID_API}) set(TARGET_TRIPLE_DIRECTORY_PREFIX linux_android${CMAKE_ANDROID_API}_x86_64) # Variable ANDROID will be set by CMake. Custom variables are not necessary here.cangjie_compiler-1.0.7/cmake/darwin_aarch64_toolchain.cmake000066400000000000000000000013071510705540100237260ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. get_filename_component(CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) include("${CMAKE_DIR}/darwin_toolchain.cmake") set(CMAKE_SYSTEM_PROCESSOR "aarch64") set(TRIPLE aarch64-apple-darwin) set(MACOS ON) if("${CMAKE_SYSTEM_NAME}" STREQUAL "${CMAKE_HOST_SYSTEM_NAME}") if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "${CMAKE_HOST_SYSTEM_PROCESSOR}" OR "arm64" STREQUAL "${CMAKE_HOST_SYSTEM_PROCESSOR}") set(CMAKE_CROSSCOMPILING OFF) endif() endif() cangjie_compiler-1.0.7/cmake/darwin_toolchain.cmake000066400000000000000000000030311510705540100224120ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set(CMAKE_SYSTEM_NAME Darwin) set(DARWIN ON) set(WARNINGS_SETTINGS "-Wall -Werror -Wdate-time ${CUSTOM_WARNING_SETTINGS}") set(C_OTHER_FLAGS "-fsigned-char") set(CXX_OTHER_FLAGS "-Weffc++") set(OTHER_FLAGS "-pipe -fno-common -fno-strict-aliasing -fPIC -fstack-protector-all") set(STRIP_FLAG "-s") set(C_FLAGS "${WARNINGS_SETTINGS} ${C_OTHER_FLAGS} ${OTHER_FLAGS}") set(CPP_FLAGS "${WARNINGS_SETTINGS} ${CXX_OTHER_FLAGS} ${OTHER_FLAGS}") set(CMAKE_C_FLAGS "${C_FLAGS}") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-D_FORTIFY_SOURCE=2 -O2 -g") set(CMAKE_C_FLAGS_RELEASE "-D_FORTIFY_SOURCE=2 -O2") set(CMAKE_C_FLAGS_DEBUG "-O0 -g") set(CMAKE_CXX_FLAGS "${CPP_FLAGS}") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-D_FORTIFY_SOURCE=2 -O2 -g") set(CMAKE_CXX_FLAGS_RELEASE "-D_FORTIFY_SOURCE=2 -O2") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -fstandalone-debug") if(CMAKE_BUILD_TYPE MATCHES Release) set(CMAKE_EXE_LINKER_FLAGS "${LINK_FLAGS} ${STRIP_FLAG} ") else() set(CMAKE_EXE_LINKER_FLAGS "${LINK_FLAGS}") endif() find_program(CMAKE_AR llvm-ar REQUIRED) find_program(CMAKE_RANLIB llvm-ranlib REQUIRED) set(MAKE_SO_STACK_PROTECTOR_OPTION) set(LLVM_BUILD_C_COMPILER ${CMAKE_C_COMPILER}) set(LLVM_BUILD_CXX_COMPILER ${CMAKE_CXX_COMPILER}) if(CANGJIE_TARGET_SYSROOT) set(CMAKE_SYSROOT ${CANGJIE_TARGET_SYSROOT}) endif() cangjie_compiler-1.0.7/cmake/darwin_x86_64_toolchain.cmake000066400000000000000000000012771510705540100234420ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. get_filename_component(CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) include("${CMAKE_DIR}/darwin_toolchain.cmake") set(CMAKE_SYSTEM_PROCESSOR "x86_64") set(TRIPLE x86_64-apple-darwin) set(MACOS ON) if("${CMAKE_SYSTEM_NAME}" STREQUAL "${CMAKE_HOST_SYSTEM_NAME}" AND "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "${CMAKE_HOST_SYSTEM_PROCESSOR}") set(CMAKE_CROSSCOMPILING OFF) endif() cangjie_compiler-1.0.7/cmake/ios_arm64_toolchain.cmake000066400000000000000000000017431510705540100227410ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. get_filename_component(CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) include("${CMAKE_DIR}/darwin_toolchain.cmake") set(CMAKE_SYSTEM_NAME "ios") set(CMAKE_SYSTEM_PROCESSOR "aarch64") set(TRIPLE arm64-apple-ios11) set(CXX_COMPATIABLE_TRIPLE arm64-apple-ios12) set(TARGET_TRIPLE_DIRECTORY_PREFIX ios_aarch64) add_compile_options(--target=${TRIPLE}) add_link_options(--target=${TRIPLE}) set(IOS ON) set(IOS_PLATFORM OS) set(IOS_PLATFORM_LOCATION "iPhoneOS.platform") set(CMAKE_IOS_DEVELOPER_ROOT "/Applications/Xcode.app/Contents/Developer/Platforms/${IOS_PLATFORM_LOCATION}/Developer") set(CMAKE_IOS_SDK_ROOT "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/iPhoneOS17.5.sdk") set(CMAKE_OSX_SYSROOT "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/iPhoneOS17.5.sdk")cangjie_compiler-1.0.7/cmake/ios_simulator_arm64_toolchain.cmake000066400000000000000000000020351510705540100250330ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. get_filename_component(CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) include("${CMAKE_DIR}/darwin_toolchain.cmake") set(CMAKE_SYSTEM_NAME "ios") set(CMAKE_SYSTEM_PROCESSOR "aarch64") set(TRIPLE arm64-apple-ios11-simulator) set(CXX_COMPATIABLE_TRIPLE arm64-apple-ios12-simulator) set(TARGET_TRIPLE_DIRECTORY_PREFIX ios_simulator_aarch64) add_compile_options(--target=${TRIPLE}) add_link_options(--target=${TRIPLE}) set(IOS ON) set(IOS_PLATFORM SIMULATOR) set(IOS_PLATFORM_LOCATION "iPhoneSimulator.platform") set(CMAKE_IOS_DEVELOPER_ROOT "/Applications/Xcode.app/Contents/Developer/Platforms/${IOS_PLATFORM_LOCATION}/Developer") set(CMAKE_IOS_SDK_ROOT "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/iPhoneSimulator17.5.sdk") set(CMAKE_OSX_SYSROOT "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/iPhoneSimulator17.5.sdk")cangjie_compiler-1.0.7/cmake/linux_aarch64_toolchain.cmake000066400000000000000000000016351510705540100236050ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. get_filename_component(CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) include("${CMAKE_DIR}/linux_toolchain.cmake") set(CMAKE_SYSTEM_PROCESSOR "aarch64") if("${CMAKE_SYSTEM_NAME}" STREQUAL "${CMAKE_HOST_SYSTEM_NAME}" AND "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "${CMAKE_HOST_SYSTEM_PROCESSOR}") set(CMAKE_CROSSCOMPILING OFF) endif() if(CMAKE_CROSSCOMPILING) add_compile_definitions(__aarch64_linux_gnu__) add_compile_definitions(OPENSSL_ARM64_PLATFORM) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-effc++ -Wno-unused-variable -Wno-missing-declarations -Wno-unused-result") endif() cangjie_compiler-1.0.7/cmake/linux_toolchain.cmake000066400000000000000000000063351510705540100222770ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_VERBOSE_MAKEFILEON ON) set(EXTRA_WARNING_SETTINGS "-Wsign-compare") set(WARNINGS_SETTINGS "-Wall ${EXTRA_WARNING_SETTINGS} -Werror -Wdate-time ${CUSTOM_WARNING_SETTINGS}") set(C_OTHER_FLAGS "-fsigned-char") set(CXX_OTHER_FLAGS "-Weffc++") set(OTHER_FLAGS "-fno-omit-frame-pointer -pipe -fno-common -fno-strict-aliasing -fstack-protector-all") set(GCOV_FLAGS "-fno-inline -O0 -fprofile-arcs -ftest-coverage") set(ASAN_FLAGS "-fsanitize=address -fno-omit-frame-pointer") set(TSAN_FLAGS "-fsanitize=thread") set(HWASAN_FLAGS "-shared-libsan -fsanitize=hwaddress -fno-omit-frame-pointer -fno-emulated-tls -fno-lto -fno-whole-program-vtables") set(LINK_FLAGS "-Wl,-z,relro,-z,now,-z,noexecstack") set(STRIP_FLAG "-s") set(SAFE_EXE_LINK_FLAG "-pie") set(LINK_FLAGS_BUILD_ID "-Wl,--build-id=none") set(C_FLAGS "${WARNINGS_SETTINGS} ${C_OTHER_FLAGS} ${OTHER_FLAGS}") set(CPP_FLAGS "${WARNINGS_SETTINGS} ${CXX_OTHER_FLAGS} ${OTHER_FLAGS}") option(CANGJIE_ENABLE_GCOV "Enable gcov (debug, Linux builds only)" OFF) if(CANGJIE_ENABLE_GCOV) set(C_FLAGS "${C_FLAGS} ${GCOV_FLAGS}") set(CPP_FLAGS "${CPP_FLAGS} ${GCOV_FLAGS}") endif() if(CANGJIE_ENABLE_ASAN_COV) set(C_FLAGS "${C_FLAGS} ${ASAN_FLAGS} -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div,trace-gep") set(CPP_FLAGS "${CPP_FLAGS} ${ASAN_FLAGS} -fsanitize-coverage=trace-pc-guard,trace-cmp,trace-div,trace-gep") endif() option(CANGJIE_ENABLE_ASAN "Enable asan (relwithbebinfo or debug, Linux builds only)" OFF) if(CANGJIE_ENABLE_ASAN) set(C_FLAGS "${C_FLAGS} ${ASAN_FLAGS}") set(CPP_FLAGS "${CPP_FLAGS} ${ASAN_FLAGS}") endif() option(CANGJIE_ENABLE_HWASAN "Enable hardware asan (relwithdebinfo or debug, OHOS builds only)" OFF) if(CANGJIE_ENABLE_HWASAN) set(C_FLAGS "${C_FLAGS} ${HWASAN_FLAGS}") set(CPP_FLAGS "${CPP_FLAGS} ${HWASAN_FLAGS}") endif() set(CMAKE_C_FLAGS "${C_FLAGS}") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g") set(CMAKE_C_FLAGS_RELEASE "-D_FORTIFY_SOURCE=2 -O2") set(CMAKE_C_FLAGS_DEBUG "-O0 -g") set(CMAKE_C_FLAGS_MINSIZEREL "-Os") set(CMAKE_C_FLAGS_MINSIZERELWITHDEBINFO "-Os -g") set(CMAKE_CXX_FLAGS "${CPP_FLAGS}") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") set(CMAKE_CXX_FLAGS_RELEASE "-D_FORTIFY_SOURCE=2 -O2") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -fstandalone-debug") set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os") set(CMAKE_CXX_FLAGS_MINSIZERELWITHDEBINFO "-Os -g") if(CMAKE_BUILD_TYPE MATCHES Release OR CMAKE_BUILD_TYPE MATCHES MinSizeRel) set(CMAKE_EXE_LINKER_FLAGS "${LINK_FLAGS} ${LINK_FLAGS_BUILD_ID} ${STRIP_FLAG}") set(CMAKE_SHARED_LINKER_FLAGS "${LINK_FLAGS} ${LINK_FLAGS_BUILD_ID} ${STRIP_FLAG}") else() set(CMAKE_EXE_LINKER_FLAGS "${LINK_FLAGS} ${LINK_FLAGS_BUILD_ID}") endif() set(LINKER_OPTION_PREFIX "-Wl,") set(MAKE_SO_STACK_PROTECTOR_OPTION) set(LLVM_BUILD_C_COMPILER ${CMAKE_C_COMPILER}) set(LLVM_BUILD_CXX_COMPILER ${CMAKE_CXX_COMPILER}) if(CANGJIE_TARGET_SYSROOT) set(CMAKE_SYSROOT ${CANGJIE_TARGET_SYSROOT}) endif() cangjie_compiler-1.0.7/cmake/linux_x86_64_toolchain.cmake000066400000000000000000000012171510705540100233070ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. get_filename_component(CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) include("${CMAKE_DIR}/linux_toolchain.cmake") set(CMAKE_SYSTEM_PROCESSOR "x86_64") if("${CMAKE_SYSTEM_NAME}" STREQUAL "${CMAKE_HOST_SYSTEM_NAME}" AND "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "${CMAKE_HOST_SYSTEM_PROCESSOR}") set(CMAKE_CROSSCOMPILING OFF) endif() cangjie_compiler-1.0.7/cmake/mingw_x86_64_toolchain.cmake000066400000000000000000000072331510705540100232750ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set(CMAKE_SYSTEM_NAME Windows) set(CMAKE_SYSTEM_PROCESSOR x86_64) # On Windows, x86_64 arch is named "AMD64". # Unify it to "x86_64" for convenience in later scripts. if("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "AMD64") set(CMAKE_HOST_SYSTEM_PROCESSOR x86_64) endif() if("${CMAKE_SYSTEM_NAME}" STREQUAL "${CMAKE_HOST_SYSTEM_NAME}") set(CMAKE_CROSSCOMPILING OFF) endif() if(CANGJIE_TARGET_SYSROOT) set(CMAKE_SYSROOT ${CANGJIE_TARGET_SYSROOT}) endif() set(TRIPLE "x86_64-w64-mingw32") set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(C_OTHER_FLAGS "-fsigned-char") set(CXX_OTHER_FLAGS "") set(OTHER_FLAGS "-fno-omit-frame-pointer -pipe -fno-common -fno-strict-aliasing -m64 -Wa,-mbig-obj -fstack-protector-all -Wl,--stack,16777216" ) set(WARNINGS_SETTINGS "-w -Wdate-time -Wno-int-conversion") # The ld in MinGW toolchain is very slow, especially when linking the debug version. lld is preferred for linking cjc.exe. # However, the gcc cross compiler doesn't search for the name "lld", but "read-ld", "ld.lld" or "-ld.lld", etc. # According to collect2's manual, cross-compilers search for linker in the following order: # - real-ld in the compiler’s search directories (which is specified by -B). # - target-real-ld in PATH. # - The file specified in the REAL_LD_FILE_NAME configuration macro, if specified. # - ld in the compiler’s search directories. # - target-ld in PATH. # Hence, we have to make a symlink to lld with the required name, and pass its directory to cross-gcc using "-B". find_program(lld_path lld${CMAKE_EXECUTABLE_SUFFIX}) if(NOT lld_path) message(WARNING "`lld` cannot be found. `ld` will be used to link cjc.exe instead, but it may take much longer.") else() file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/toolchain/) file( CREATE_LINK ${lld_path} ${CMAKE_BINARY_DIR}/toolchain/ld.lld${CMAKE_EXECUTABLE_SUFFIX} RESULT result SYMBOLIC) set(OTHER_FLAGS "${OTHER_FLAGS} -B${CMAKE_BINARY_DIR}/toolchain/ -fuse-ld=lld") endif() set(LINK_FLAGS "") set(STRIP_FLAG "-s") set(C_FLAGS "${WARNINGS_SETTINGS} ${C_OTHER_FLAGS} ${OTHER_FLAGS}") set(CPP_FLAGS "${WARNINGS_SETTINGS} ${CXX_OTHER_FLAGS} ${OTHER_FLAGS}") set(CMAKE_C_FLAGS "${C_FLAGS}") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g") set(CMAKE_C_FLAGS_RELEASE "-D_FORTIFY_SOURCE=2 -O2") set(CMAKE_C_FLAGS_DEBUG "-O0 -g") set(CMAKE_CXX_FLAGS "${CPP_FLAGS}") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") set(CMAKE_CXX_FLAGS_RELEASE "-D_FORTIFY_SOURCE=2 -O2") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") set(CMAKE_ASM_FLAGS "${CPP_FLAGS} -x assembler-with-cpp") set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-insert-timestamp") set(CMAKE_EXE_LINKER_FLAGS "-Wl,--no-insert-timestamp") if(CMAKE_BUILD_TYPE MATCHES Release) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LINK_FLAGS} ${STRIP_FLAG}") else() set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LINK_FLAGS}") endif() # Besides gcc and g++, there are also copies of them with the triple prefix # in a native MinGW toolchain, like x86_64-w64-mingw32-gcc. # Use those with the prefix here in order to be compatible with cross-compilation. set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) find_program(CMAKE_AR llvm-ar) find_program(CMAKE_RANLIB llvm-ranlib) set(LLVM_BUILD_C_COMPILER x86_64-w64-mingw32-gcc) set(LLVM_BUILD_CXX_COMPILER x86_64-w64-mingw32-g++) set(LINKER_OPTION_PREFIX "-Wl,") set(MAKE_SO_STACK_PROTECTOR_OPTION -fstack-protector) cangjie_compiler-1.0.7/cmake/modules/000077500000000000000000000000001510705540100175375ustar00rootroot00000000000000cangjie_compiler-1.0.7/cmake/modules/ApplyProperties.cmake000066400000000000000000000014521510705540100237050ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. function(apply_properties) set(oneValueArgs FROM_TARGET TO_TARGET) set(multiValueArgs PROPERTY_NAMES) cmake_parse_arguments( ARG "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) foreach(arg ${ARG_PROPERTY_NAMES}) get_target_property(PROPERTY_VALUE ${ARG_FROM_TARGET} ${arg}) if(NOT ("${PROPERTY_VALUE}" MATCHES "PROPERTY_VALUE-NOTFOUND")) set_target_properties(${ARG_TO_TARGET} PROPERTIES ${arg} "${PROPERTY_VALUE}") endif() endforeach(arg) endfunction() cangjie_compiler-1.0.7/cmake/modules/MergeArchives.cmake000066400000000000000000000121271510705540100232700ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # This script can merge multiple .a and .o files into a single .a file. # At least one output file name and one input file is required. # Usage: cmake -P MergeArchives.cmake [ARCHIVER ] OUTPUT_FILENAME INPUTS ... # Parse input arguments math(EXPR UPPER_INDEX "${CMAKE_ARGC}-1") set(ARGV) foreach(i RANGE ${UPPER_INDEX}) list(APPEND ARGV ${CMAKE_ARGV${i}}) endforeach() set(oneValueArgs ARCHIVER OUTPUT_FILENAME) set(multiValueArgs INPUTS) cmake_parse_arguments( MERGE_AR "" "${oneValueArgs}" "${multiValueArgs}" ${ARGV}) # Check illegal arguments list(LENGTH MERGE_AR_INPUTS MERGE_AR_INPUTS_LEN) if("${MERGE_AR_INPUTS_LEN}" LESS 1) message("Input archives are required!") message( FATAL_ERROR "Usage: cmake -P MergeArchives.cmake [ARCHIVER ] OUTPUT_FILENAME INPUTS ..." ) endif() if("${MERGE_AR_OUTPUT_FILENAME}" STREQUAL "") message("Name of the output archive is required!") message( FATAL_ERROR "Usage: cmake -P MergeArchives.cmake [ARCHIVER ] OUTPUT_FILENAME INPUTS ..." ) endif() if("${MERGE_AR_ARCHIVER}" STREQUAL "") set(MERGE_AR_ARCHIVER ar) endif() # Extract object files and copy single objject files to the temp folder string(RANDOM LENGTH 8 RANDOM_DIR_STR) set(TEMPDIR ${MERGE_AR_OUTPUT_FILENAME}_${RANDOM_DIR_STR}) file(MAKE_DIRECTORY ${TEMPDIR}) set(OBJ_LIST) foreach(f ${MERGE_AR_INPUTS}) get_filename_component(extension ${f} LAST_EXT) get_filename_component(full_path ${f} ABSOLUTE) if("${extension}" STREQUAL ".a") execute_process( COMMAND ${MERGE_AR_ARCHIVER} x ${full_path} WORKING_DIRECTORY ${TEMPDIR} RESULT_VARIABLE extract_ret OUTPUT_VARIABLE extract_stdout ERROR_VARIABLE extract_stderr) if(NOT ("${extract_ret}" STREQUAL "0")) message("${extract_stdout}") message("${extract_stderr}") message(FATAL_ERROR "Extract archive ${full_path} failed!") endif() execute_process( COMMAND ${MERGE_AR_ARCHIVER} t ${full_path} WORKING_DIRECTORY ${TEMPDIR} RESULT_VARIABLE list_ret OUTPUT_VARIABLE list_stdout ERROR_VARIABLE list_stderr OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT ("${list_ret}" STREQUAL "0")) message("${list_stdout}") message("${list_stderr}") message(FATAL_ERROR "List contents in the archive ${full_path} failed!") endif() string(REPLACE "\n" ";" temp_obj_list "${list_stdout}") foreach(o ${temp_obj_list}) get_filename_component(obj_filename ${o} NAME) # MacOS (BSD) ar generates __.SYMDEF files and these files will be archived in the .a file. # These files should be skipped. if("${obj_filename}" STREQUAL "__.SYMDEF SORTED" OR "${obj_filename}" STREQUAL "__.SYMDEF") continue() endif() get_filename_component(obj_extension ${o} LAST_EXT) if("${obj_extension}" STREQUAL ".obj") get_filename_component(obj_filename ${o} NAME_WLE) file(RENAME "${TEMPDIR}/${o}" "${TEMPDIR}/${obj_filename}.o") list(APPEND OBJ_LIST "${TEMPDIR}/${obj_filename}.o") else() list(APPEND OBJ_LIST "${TEMPDIR}/${o}") endif() endforeach() elseif("${extension}" STREQUAL ".o") file(COPY ${f} DESTINATION ${TEMPDIR}) list(APPEND OBJ_LIST "${full_path}") elseif("${extension}" STREQUAL ".obj") get_filename_component(filename ${f} NAME_WLE) file(COPY ${f} DESTINATION ${TEMPDIR}) file(RENAME "${TEMPDIR}/${filename}.obj" "${TEMPDIR}/${filename}.o") list(APPEND OBJ_LIST "${TEMPDIR}/${filename}.o") else() message("${f} seems neither an object file nor an archive file. Ignoring it.") endif() endforeach() # Test if ar supports -D option. set(AR_CREATE_OPTION cr) execute_process( COMMAND ${MERGE_AR_ARCHIVER} -crD dummy_archive.a WORKING_DIRECTORY ${TEMPDIR} RESULT_VARIABLE AR_SUPPORT_DETERMINISTIC_MODE OUTPUT_QUIET ERROR_QUIET) if(${AR_SUPPORT_DETERMINISTIC_MODE} EQUAL 0) set(AR_CREATE_OPTION crD) endif() # Generate the script and drive ar to produce the output .a file execute_process( COMMAND ${MERGE_AR_ARCHIVER} ${AR_CREATE_OPTION} ${MERGE_AR_OUTPUT_FILENAME} ${OBJ_LIST} RESULT_VARIABLE archive_ret OUTPUT_VARIABLE archive_stdout ERROR_VARIABLE archive_stderr) if(NOT ("${archive_ret}" STREQUAL "0")) message("${archive_stdout}") message("${archive_stderr}") message(FATAL_ERROR "Generate merged archive failed! Error code=${ERR_CODE}") endif() # Clean up temp files file(REMOVE_RECURSE ${TEMPDIR}) cangjie_compiler-1.0.7/cmake/modules/ReadDarwinSDKInfo.cmake000066400000000000000000000024171510705540100237430ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # Use `xcrun` to get MacOS SDK path and version. They are used for compiling Cangjie standard libraries. execute_process( COMMAND xcrun --sdk macosx --show-sdk-path WORKING_DIRECTORY ${CMAKE_BINARY_DIR} RESULT_VARIABLE CANGJIE_MACOSX_SDK_PATH_AVAILABLE OUTPUT_VARIABLE CANGJIE_MACOSX_SDK_PATH ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(${CANGJIE_MACOSX_SDK_PATH_AVAILABLE} EQUAL 0) message(STATUS "CANGJIE_MACOSX_SDK_PATH: ${CANGJIE_MACOSX_SDK_PATH}") else() message(STATUS "CANGJIE_MACOSX_SDK_PATH: Not Available") endif() execute_process( COMMAND xcrun --sdk macosx --show-sdk-version WORKING_DIRECTORY ${CMAKE_BINARY_DIR} RESULT_VARIABLE CANGJIE_MACOSX_SDK_VERSION_AVAILABLE OUTPUT_VARIABLE CANGJIE_MACOSX_SDK_VERSION ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(${CANGJIE_MACOSX_SDK_PATH_AVAILABLE} EQUAL 0) message(STATUS "CANGJIE_MACOSX_SDK_VERSION: ${CANGJIE_MACOSX_SDK_VERSION}") else() message(STATUS "CANGJIE_MACOSX_SDK_VERSION: Not Available") endif() cangjie_compiler-1.0.7/cmake/modules/SetupAr.cmake000066400000000000000000000040301510705540100221210ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # GNU ar and ranlib operates in non-deterministic mode by default on some systems. # To keep build consistency, we set -D option to force deterministic mode if it's # possible. Note that some tools do not support -D option, for example BSD `ar` and # `llvm-ranlib`. We test whether the tool supports -D option and only set the option # if it supports. execute_process( COMMAND ${CMAKE_AR} -crD dummy_archive.a WORKING_DIRECTORY ${CMAKE_BINARY_DIR} RESULT_VARIABLE AR_SUPPORT_DETERMINISTIC_MODE OUTPUT_QUIET ERROR_QUIET) if(${AR_SUPPORT_DETERMINISTIC_MODE} EQUAL 0) set(CMAKE_C_ARCHIVE_CREATE " -crD ") set(CMAKE_C_ARCHIVE_APPEND " -rD ") set(CMAKE_CXX_ARCHIVE_CREATE " -crD ") set(CMAKE_CXX_ARCHIVE_APPEND " -rD ") message(STATUS " deterministic mode (-D) is set.") endif() if(DARWIN) file(TOUCH ${CMAKE_BINARY_DIR}/dummy_object.o) execute_process( COMMAND ${CMAKE_AR} -cr dummy_archive.a dummy_object.o WORKING_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_QUIET ERROR_QUIET) else() execute_process( COMMAND ${CMAKE_AR} -cr dummy_archive.a WORKING_DIRECTORY ${CMAKE_BINARY_DIR} OUTPUT_QUIET ERROR_QUIET) endif() execute_process( COMMAND ${CMAKE_RANLIB} -D dummy_archive.a WORKING_DIRECTORY ${CMAKE_BINARY_DIR} RESULT_VARIABLE RANLIB_SUPPORT_DETERMINISTIC_MODE OUTPUT_QUIET ERROR_QUIET) if(${RANLIB_SUPPORT_DETERMINISTIC_MODE} EQUAL 0) set(CMAKE_C_ARCHIVE_FINISH " -D ") set(CMAKE_CXX_ARCHIVE_FINISH " -D ") message(STATUS " deterministic mode (-D) is set.") endif() cangjie_compiler-1.0.7/cmake/modules/make-symlink.cmake000066400000000000000000000024151510705540100231440ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. if(CMAKE_CROSSCOMPILING AND WIN32) # When cross-compiling cjc.exe to Windows, the symlink created on Linux is unusable on Windows, so just make a copy. execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${LINK_TARGET} ${LINK_NAME} WORKING_DIRECTORY ${WORKING_DIR}) else() execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${WORKING_DIR}) execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink ${LINK_TARGET} ${LINK_NAME} WORKING_DIRECTORY ${WORKING_DIR} ERROR_VARIABLE err_var) # In case of windows, symbolic link can only be created in cmd with administrator privilege or developer mode system. # We try to create symbolic link and create a copy of `cjc` if symbolic link couldn't be created. if(WIN32 AND err_var) message(WARNING "Symbolic link \"${LINK_NAME}\" could not be made. A copy of \"${LINK_TARGET}\" is created.") execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${LINK_TARGET} ${LINK_NAME} WORKING_DIRECTORY ${WORKING_DIR}) endif() endif() cangjie_compiler-1.0.7/cmake/ohos_aarch64_clang_toolchain.cmake000066400000000000000000000023161510705540100245570ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. get_filename_component(CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) include("${CMAKE_DIR}/linux_toolchain.cmake") set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR aarch64) if("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "AMD64") set(CMAKE_HOST_SYSTEM_PROCESSOR x86_64) endif() set(TRIPLE aarch64-linux-ohos) set(OHOS ON) # We add --target option for clang only since gcc does not support --target option. # In case of gcc, cross compilation requires a target-specific gcc (a cross compiler). add_compile_options(--target=${TRIPLE}) add_link_options(--target=${TRIPLE}) add_compile_definitions(__ohos__) add_compile_definitions(OPENSSL_ARM64_PLATFORM) add_compile_options(-mbranch-protection=pac-ret) set(CMAKE_C_FLAGS "-fno-emulated-tls ${CMAKE_C_FLAGS}") set(CMAKE_CXX_FLAGS "-fno-emulated-tls ${CMAKE_CXX_FLAGS}") set(CMAKE_RANLIB "${CANGJIE_TARGET_TOOLCHAIN}/llvm-ranlib") set(TARGET_TRIPLE_DIRECTORY_PREFIX "linux_ohos_aarch64") set(LINKER_OPTION_PREFIX) cangjie_compiler-1.0.7/cmake/ohos_arm_clang_toolchain.cmake000066400000000000000000000024541510705540100241110ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. get_filename_component(CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) include("${CMAKE_DIR}/linux_toolchain.cmake") set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm) if("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "AMD64") set(CMAKE_HOST_SYSTEM_PROCESSOR x86_64) endif() set(TRIPLE arm-linux-ohos) set(OHOS ON) # We add --target option for clang only since gcc does not support --target option. # In case of gcc, cross compilation requires a target-specific gcc (a cross compiler). add_compile_options(--target=${TRIPLE} -march=armv7a) add_link_options(--target=${TRIPLE} -march=armv7a) add_compile_definitions(__ohos__) add_compile_definitions(OPENSSL_ARM64_PLATFORM) string(REPLACE "-Werror" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") string(REPLACE "-Werror" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") set(CMAKE_C_FLAGS "-fno-emulated-tls ${CMAKE_C_FLAGS}") set(CMAKE_CXX_FLAGS "-fno-emulated-tls ${CMAKE_CXX_FLAGS}") set(CMAKE_RANLIB "${CANGJIE_TARGET_TOOLCHAIN}/llvm-ranlib") set(TARGET_TRIPLE_DIRECTORY_PREFIX "linux_ohos_arm") set(LINKER_OPTION_PREFIX) cangjie_compiler-1.0.7/cmake/ohos_x86_64_clang_toolchain.cmake000066400000000000000000000021361510705540100242650ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. get_filename_component(CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) include("${CMAKE_DIR}/linux_toolchain.cmake") set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR x86_64) if("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "AMD64") set(CMAKE_HOST_SYSTEM_PROCESSOR x86_64) endif() set(TRIPLE x86_64-linux-ohos) set(OHOS ON) # We add --target option for clang only since gcc does not support --target option. # In case of gcc, cross compilation requires a target-specific gcc (a cross compiler). add_compile_options(--target=${TRIPLE}) add_link_options(--target=${TRIPLE}) add_compile_definitions(__ohos__) add_compile_definitions(OPENSSL_ARM64_PLATFORM) set(CMAKE_C_FLAGS "-fno-emulated-tls ${CMAKE_C_FLAGS}") set(CMAKE_RANLIB "${CANGJIE_TARGET_TOOLCHAIN}/llvm-ranlib") set(TARGET_TRIPLE_DIRECTORY_PREFIX "linux_ohos_x86_64") set(LINKER_OPTION_PREFIX) cangjie_compiler-1.0.7/cmake/optional/000077500000000000000000000000001510705540100177145ustar00rootroot00000000000000cangjie_compiler-1.0.7/cmake/optional/CangjieCCache.cmake000066400000000000000000000010251510705540100233030ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. if(CANGJIE_ENABLE_CCACHE) find_program(FOUND ccache) if(FOUND) set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) message(STATUS "CCache found. Build with CCache.") endif() endif() cangjie_compiler-1.0.7/demangler/000077500000000000000000000000001510705540100167455ustar00rootroot00000000000000cangjie_compiler-1.0.7/demangler/CMakeLists.txt000066400000000000000000000042421510705540100215070ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. cmake_minimum_required(VERSION 3.16.5) project(cangjie-demangler) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_EXTENSIONS OFF) if(BUILD_GCC_TOOLCHAIN AND (CMAKE_C_COMPILER_ID STREQUAL "Clang" OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang"))) add_compile_options(--gcc-toolchain=${BUILD_GCC_TOOLCHAIN}) add_link_options(--gcc-toolchain=${BUILD_GCC_TOOLCHAIN}) endif() if(CANGJIE_DEMANGLER_USE_CSTRING) # A library for demangling, which using CString as its string implementation. add_library(cangjie-demangle STATIC ${CMAKE_CURRENT_SOURCE_DIR}/Demangler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DeCompression.cpp) else() # A library for demangling, which using std::string as its string implementation. add_library(cangjie-demangle STATIC ${CMAKE_CURRENT_SOURCE_DIR}/DeCompression.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Demangler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CangjieDemangle.cpp) target_compile_definitions(cangjie-demangle PRIVATE BUILD_LIB_CANGJIE_DEMANGLE) endif() install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/CangjieDemangle.h DESTINATION include) install(TARGETS cangjie-demangle DESTINATION lib) if(OHOS) add_library(cangjie-demangle-shared SHARED ${CMAKE_CURRENT_SOURCE_DIR}/DeCompression.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Demangler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CangjieDemangle.cpp) target_compile_definitions(cangjie-demangle-shared PRIVATE BUILD_LIB_CANGJIE_DEMANGLE) install(TARGETS cangjie-demangle-shared LIBRARY DESTINATION lib) set_target_properties(cangjie-demangle-shared PROPERTIES OUTPUT_NAME "cangjie-demangle") endif() # A command line tool for demangling Cangjie symbols. add_executable(cjfilt ${CMAKE_CURRENT_SOURCE_DIR}/Demangler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DeCompression.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Cjfilt.cpp) target_compile_definitions(cjfilt PRIVATE BUILD_LIB_CANGJIE_DEMANGLE) install(TARGETS cjfilt DESTINATION bin) cangjie_compiler-1.0.7/demangler/CangjieDemangle.cpp000066400000000000000000000062371510705540100224560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "CangjieDemangle.h" #ifndef BUILD_LIB_CANGJIE_DEMANGLE #define BUILD_LIB_CANGJIE_DEMANGLE #endif #include "Demangler.h" #include "StdString.h" namespace Cangjie { using DemangleMetaData = DemangleInfo; std::string DemangleData::GetPkgName() const { return pkgName; } std::string DemangleData::GetFullName() const { return fullName; } bool DemangleData::IsFunctionLike() const { return isFunctionLike; } bool DemangleData::IsValid() const { return isValid; } void DemangleData::SetPrivateDeclaration(bool isPrivate) { isPrivateDeclaration = isPrivate; } bool DemangleData::IsPrivateDeclaration() const { return isPrivateDeclaration; } DemangleData Demangle(const std::string& mangled, const std::string& scopeRes) { auto demangler = Demangler(mangled.c_str(), scopeRes); auto di = demangler.Demangle(); auto dd = DemangleData{ di.GetPkgName(), di.GetFullName(demangler.ScopeResolution()), di.IsFunctionLike(), di.IsValid() }; dd.SetPrivateDeclaration(di.isPrivateDeclaration); return dd; } DemangleData Demangle(const std::string& mangled) { return Demangle(mangled, "::"); } DemangleData Demangle(const std::string& mangled, const std::string& scopeRes, const std::vector& genericVec) { auto demangler = Demangler(mangled.c_str(), scopeRes); demangler.setGenericVec(genericVec); auto di = demangler.Demangle(); auto dd = DemangleData{ di.GetPkgName(), di.GetFullName(demangler.ScopeResolution()), di.IsFunctionLike(), di.IsValid() }; dd.SetPrivateDeclaration(di.isPrivateDeclaration); return dd; } DemangleData Demangle(const std::string& mangled, const std::vector& genericVec) { return Demangle(mangled, "::", genericVec); } DemangleData DemangleType(const std::string& mangled, const std::string& scopeRes) { auto demangler = Demangler(mangled, scopeRes); auto di = demangler.Demangle(true); auto dd = DemangleData{ di.GetPkgName(), di.GetFullName(demangler.ScopeResolution()), false, di.IsValid()}; dd.SetPrivateDeclaration(di.isPrivateDeclaration); return dd; } DemangleData DemangleType(const std::string& mangled) { return DemangleType(mangled, "::"); } #ifdef __OHOS__ char* CJ_MRT_Demangle(const char* functionName) { DemangleData demangleData = Demangle(functionName); std::string pkgName = demangleData.GetPkgName(); std::string demangledFunctionName = pkgName + std::string(pkgName.empty() ? "" : ".") + demangleData.GetFullName(); int32_t len = strlen(demangledFunctionName.c_str()); // demangleName should be freed by caller. char* demangleName = reinterpret_cast(malloc(len + 1)); if (demangleName == nullptr) { return nullptr; } for (int i = 0; i < len; ++i) { demangleName[i] = demangledFunctionName[i]; } demangleName[len] = '\0'; return demangleName; } #endif } // namespace Cangjiecangjie_compiler-1.0.7/demangler/CangjieDemangle.h000066400000000000000000000072501510705540100221170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef LIB_CANGJIE_DEMANGLE #define LIB_CANGJIE_DEMANGLE #include #include namespace Cangjie { struct DemangleData { /** * @brief The constructor of struct DemangleData. * * @param pkgName The package name. * @param fullName The full demangled name. * @param isFunctionLike Whether is function like. * @param isValid Whether The demangled name is valid. * @return DemangleData The instance of DemangleData. */ DemangleData(std::string pkgName, std::string fullName, bool isFunctionLike, bool isValid) : pkgName(pkgName), fullName(fullName), isFunctionLike(isFunctionLike), isValid(isValid) {} /** * @brief The destructor of struct DemangleData. */ ~DemangleData() {} /** * @brief Get package name. * * @return std::string The package name. */ std::string GetPkgName() const; /** * @brief Get full name. * * @return std::string The full name. */ std::string GetFullName() const; /** * @brief Check if it functions like a function. * * @return bool If yes, true is returned. Otherwise, false is returned. */ bool IsFunctionLike() const; /** * @brief Check if it is valid. * * @return bool If yes, true is returned. Otherwise, false is returned. */ bool IsValid() const; /** * @brief Set private declaration. * * @param isPrivate It is used to assign a value to isPrivateDeclaration. */ void SetPrivateDeclaration(bool isPrivate); /** * @brief Check if it is private declaration. * * @return bool If yes, true is returned. Otherwise, false is returned. */ bool IsPrivateDeclaration() const; private: std::string pkgName; std::string fullName; bool isFunctionLike = false; bool isValid; bool isPrivateDeclaration = false; }; /** * @brief Demangle the string. * * @param mangled The name to be demangled. * @param genericVec The generic vector. * @return DemangleData The demangled information. */ DemangleData Demangle(const std::string& mangled, const std::vector& genericVec); /** * @brief Demangle the string. * * @param mangled The name to be demangled. * @param scopeRes The scope resolution. * @param genericVec The generic vector. * @return DemangleData The demangled information. */ DemangleData Demangle(const std::string& mangled, const std::string& scopeRes, const std::vector& genericVec); /** * @brief Demangle the string. * * @param mangled The name to be demangled. * @return DemangleData The demangled information. */ DemangleData Demangle(const std::string& mangled); /** * @brief Demangle the string. * * @param mangled The name to be demangled. * @param scopeRes The scope resolution. * @return DemangleData The demangled information. */ DemangleData Demangle(const std::string& mangled, const std::string& scopeRes); /** * @brief Demangle the type. * * @param mangled The type name to demangle. * @return DemangleData The demangled information. */ DemangleData DemangleType(const std::string& mangled); /** * @brief Demangle the type. * * @param mangled The type name to demangle. * @param scopeRes The scope resolution. * @return DemangleData The demangled information. */ DemangleData DemangleType(const std::string& mangled, const std::string& scopeRes); } // namespace Cangjie #endif // LIB_CANGJIE_DEMANGLE cangjie_compiler-1.0.7/demangler/Cjfilt.cpp000066400000000000000000000115441510705540100206710ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include #include #include #include #include "CangjieDemangle.h" #include "Demangler.h" #include "StdString.h" using namespace Cangjie; namespace { std::map obfNames; static void Println(const std::string& msg) { std::cout << msg << std::endl; } void ReadMapFile(const std::string& name) { std::string line; std::ifstream file(name); const int elemNum = 2; if (!file.is_open()) { Println("Cannot open file " + name); return; } while (getline(file, line)) { std::istringstream iss(line); std::string obfName; std::string tmp; for (int i = 0; i < elemNum; i++) { if (!getline(iss, tmp, ' ')) { break; } switch (i) { case 0: obfName = tmp; break; case 1: obfNames[obfName] = tmp; break; default: break; } } } file.close(); } void ReadMapFiles(const std::string& names) { std::istringstream iss(names); std::string name; while (getline(iss, name, ',')) { ReadMapFile(name); } } void Usage() { Println("Usage:"); Println("\tcjfilt [option] name1 name2 ..."); Println("\tIf there is a '$' in a mangled name, use '' to quote it."); Println(""); Println("Options:"); Println("\t-l\t\tlist detailed information"); Println("\t-T\t\tdemangle type name"); Println("\t-f\t\tsymbol mapping files generated by obfuscator"); } bool CheckOption(const std::vector& args, const std::string& option) { for (const auto& arg : args) { if (arg == option) { return true; } } return false; } void Demangle(const std::string& mangledName, bool isDetailed) { auto demangler = Demangler(mangledName.c_str(), '.'); Println(demangler.ScopeResolution()); auto di = demangler.Demangle(); std::string pkgName = std::string(di.GetPkgName().Str()); std::string demangledName = pkgName + std::string(pkgName.empty() ? "" : ".") + di.GetFullName(demangler.ScopeResolution()).Str(); Println("demangled:\t\t" + demangledName); if (isDetailed) { auto identifier = std::string(di.GetIdentifier().Str()); auto argsInfo = std::string(di.GetArgTypesName().Str()); Println("package:\t\t" + pkgName); Println("identifier:\t\t" + identifier); Println("args:\t\t\t" + argsInfo); Println("validation:\t\t" + std::string(di.IsValid() ? "valid" : "invalid")); Println("is private declaration:\t" + std::string(di.isPrivateDeclaration ? "yes" : "no")); } } void DemangleType(const std::string& mangledName, bool isDetailed) { auto demangler = Demangler(mangledName.c_str(), '.'); auto di = demangler.Demangle(true); std::string demangledName{ di.GetFullName(demangler.ScopeResolution()).Str() }; Println("demangled:\t\t" + demangledName); if (isDetailed) { std::string pkgName = std::string(di.GetPkgName().Str()); auto identifier = std::string(di.GetIdentifier().Str()); auto argsInfo = std::string(di.GetArgTypesName().Str()); Println("package:\t\t" + pkgName); Println("identifier:\t\t" + identifier); Println("args:\t\t\t" + argsInfo); Println("validation:\t\t" + std::string(di.IsValid() ? "valid" : "invalid")); } } } // namespace int main(int argc, char* argv[]) { std::vector args; for (int i = 0; i < argc; ++i) { if (!argv[i]) { continue; } args.emplace_back(argv[i]); } if (args.size() <= 1) { Println("no arguments"); return 1; } if (CheckOption(args, "-h")) { Usage(); return 0; } bool isDetailed = CheckOption(args, "-l"); bool isObfuscated = CheckOption(args, "-f"); bool isType = CheckOption(args, "-T"); for (size_t i = 1; i < args.size(); ++i) { if (args[i] == "-l" || args[i] == "-f" || args[i] == "-T") { continue; } if (isObfuscated && i > 1 && args[i - 1] == "-f") { ReadMapFiles(args[i]); continue; } if (isObfuscated && obfNames.find(args[i]) != obfNames.end()) { Demangle(obfNames[args[i]], isDetailed); } else if (isType) { DemangleType(args[i], isDetailed); } else { Demangle(args[i], isDetailed); } } return 0; } cangjie_compiler-1.0.7/demangler/DeCompression.cpp000066400000000000000000001071301510705540100222250ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "DeCompression.h" #ifdef BUILD_LIB_CANGJIE_DEMANGLE // To reuse the code to compile CangjieDemangle.cpp #include #include #include "StdString.h" #else #include "Base/CString.h" #endif #include "Utils.h" namespace { constexpr char MANGLE_COMPRESS_PREFIX = 'Y'; constexpr char MANGLE_ANONYMOUS_PREFIX = '0'; constexpr char MANGLE_AT_PREFIX = '@'; constexpr size_t PREFIX_LEN = 2; const std::unordered_map base62Chars = {{'0', 0}, {'1', 1}, {'2', 2}, {'3', 3}, {'4', 4}, {'5', 5}, {'6', 6}, {'7', 7}, {'8', 8}, {'9', 9}, {'A', 10}, {'B', 11}, {'C', 12}, {'D', 13}, {'E', 14}, {'F', 15}, {'G', 16}, {'H', 17}, {'I', 18}, {'J', 19}, {'K', 20}, {'L', 21}, {'M', 22}, {'N', 23}, {'O', 24}, {'P', 25}, {'Q', 26}, {'R', 27}, {'S', 28}, {'T', 29}, {'U', 30}, {'V', 31}, {'W', 32}, {'X', 33}, {'Y', 34}, {'Z', 35}, {'a', 36}, {'b', 37}, {'c', 38}, {'d', 39}, {'e', 40}, {'f', 41}, {'g', 42}, {'h', 43}, {'i', 44}, {'j', 45}, {'k', 46}, {'l', 47}, {'m', 48}, {'n', 49}, {'o', 50}, {'p', 51}, {'q', 52}, {'r', 53}, {'s', 54}, {'t', 55}, {'u', 56}, {'v', 57}, {'w', 58}, {'x', 59}, {'y', 60}, {'z', 61}}; const char PRIMITIVE_PREFIX_SET[] = "nucbfdasilqhtjmrDv"; constexpr size_t PRIMITIVE_LEN = 18; } // namespace namespace Cangjie { // Get identifier without prefix "@" template inline size_t StripCangjieAt(T& identifier, size_t idx) { if (idx < identifier.Length() && identifier[idx] == MANGLE_AT_PREFIX) { return idx + MANGLE_CHAR_LEN; } return idx; } // Get identifier without prefix "_C" template inline size_t StripCangjiePrefix(T& identifier, size_t idx) { if (idx + MANGLE_CHAR_LEN < identifier.Length() && identifier[idx] == MANGLE_UNDERSCORE_PREFIX && identifier[idx + MANGLE_CHAR_LEN] == 'C') { return idx + PREFIX_LEN; } return idx; } // Get decimal number from base 62 number. template inline size_t GetNumber(T base62) { const size_t n = 62; // _ is the end of base 62 number. if (base62 == "_") { return 0; } T newBase62 = base62.SubStr(0, base62.Length() - 1); long long decimal = 0; long long power = 1; for (size_t i = newBase62.Length() - 1; i != NPOS; i--) { decimal += base62Chars.at(newBase62[i]) * power; power *= n; } return decimal + 1; } template bool DeCompression::IsGlobalEncode(T& mangled) { // "GV"/"GP"/"GF" is the global-mangled prefix. if (IsSamePrefix(mangled, "GV", 0) || IsSamePrefix(mangled, "GP", 0) || IsSamePrefix(mangled, "GF", 0)) { return true; } return false; } template bool DeCompression::IsSamePrefix(T& first, T second, size_t idx) { for (size_t i = idx; i < idx + PREFIX_LEN; i++) { if (first[i] != second[i]) { return false; } } return true; } template inline bool IsStdPkgName(T& mangled, size_t idx) { if (idx + MANGLE_CHAR_LEN >= mangled.Length()) { return false; } if (MANGLE_STDPKG_MAP.find(mangled.SubStr(idx, SPECIAL_NAME_LEN).Str()) != MANGLE_STDPKG_MAP.end()) { return true; } return false; } template inline bool IsOperatorName(T& mangled, size_t idx) { if (idx + MANGLE_CHAR_LEN >= mangled.Length()) { return false; } if (!((mangled[idx] >= 'a' && mangled[idx] <= 'z') && (mangled[idx + MANGLE_CHAR_LEN] >= 'a' && mangled[idx + MANGLE_CHAR_LEN] <= 'z'))) { return false; } // Move left by 5 digits is the way of calculating OperatorKind size_t t = ((static_cast(mangled[idx]) - static_cast('a')) << 5) + static_cast(mangled[idx + MANGLE_CHAR_LEN]) - static_cast('a'); OperatorKind result = static_cast(t); switch (result) { case OperatorKind::IX: case OperatorKind::NT: case OperatorKind::NG: case OperatorKind::PW: case OperatorKind::ML: case OperatorKind::DV: case OperatorKind::RM: case OperatorKind::PL: case OperatorKind::LS: case OperatorKind::RS: case OperatorKind::LT: case OperatorKind::GT: case OperatorKind::LE: case OperatorKind::GE: case OperatorKind::EQ: case OperatorKind::NE: case OperatorKind::AN: case OperatorKind::EO: case OperatorKind::OR: case OperatorKind::CL: return true; default: { return false; } } } /** * Determine whether the code is a var decl code. * _CNE * _CNKE * */ template bool DeCompression::IsVarDeclEncode(T& mangled) { this->isRecord = false; const size_t n = mangled.Length(); size_t idx = 0; if (mangled[0] == END) { return true; } else if (mangled[0] == MANGLE_COMPRESS_PREFIX) { idx = ForwardNumber(mangled, idx + MANGLE_CHAR_LEN); if (idx < n && mangled[idx] == END) { return true; } } else if (isdigit(mangled[0])) { idx = ForwardName(mangled); if (idx < n && mangled[idx] == MANGLE_COUNT_PREFIX) { idx = ForwardNumber(mangled, idx + MANGLE_CHAR_LEN); if (idx < n && mangled[idx] == END) { return true; } else { return false; } } else if (idx < n && mangled[idx] == END) { return true; } } else if (mangled[0] == MANGLE_COUNT_PREFIX) { idx = ForwardNumber(mangled, idx + MANGLE_CHAR_LEN); if (idx < n && mangled[idx] == END) { return true; } } return false; } template bool DeCompression::IsDefaultParamFuncEncode(T& mangled) { if (IsSamePrefix(mangled, "PI", 0)) { return true; } return false; } inline bool IsPrimitiveType(char ch) { for (size_t i = 0; i < PRIMITIVE_LEN; i++) { if (ch == PRIMITIVE_PREFIX_SET[i]) { return true; } } return false; } template inline bool IsPropName(T& mangled, size_t idx) { if (idx + MANGLE_CHAR_LEN >= mangled.Length()) { return false; } // The ps/pg is prop mangled name. if (mangled[idx] == 'p' && (mangled[idx + MANGLE_CHAR_LEN] == 's' || mangled[idx + MANGLE_CHAR_LEN] == 'g')) { return true; } return false; } template inline size_t ForwardPrimitiveType(T& mangled, size_t idx) { // The Dh is primitive type. if (mangled[idx] != 'D' && IsPrimitiveType(mangled[idx])) { return idx + MANGLE_CHAR_LEN; } if (idx + MANGLE_CHAR_LEN < mangled.Length() && mangled[idx] == 'D' && mangled[idx + MANGLE_CHAR_LEN] == 'h') { return idx + SPECIAL_NAME_LEN; } return idx; } template bool DeCompression::HasDuplicates(T& mangled, std::tuple& pos) { // The subscript indexes of the pos are as follows: // { 0: start, 1: length } T posStr = mangled.SubStr(std::get<0>(pos), std::get<1>(pos)); for (auto it = this->treeIdMap.begin(); it != this->treeIdMap.end(); ++it) { T itStr = mangled.SubStr(std::get<0>(*it), std::get<1>(*it)); if (itStr.IsEmpty()) { continue; } if (itStr == posStr) { return true; } } return false; } template bool DeCompression::HasDuplicates(T& mangled, size_t mid) { // The subscript indexes of the treeIdMap are as follows: // { 0: index, 1: pos } T curStr = mangled.SubStr(std::get<0>(treeIdMap[mid]), std::get<1>(treeIdMap[mid])); size_t tid = 0; for (auto it = treeIdMap.begin(); it != treeIdMap.end(); ++it, ++tid) { if (tid == mid) { continue; } T itStr = mangled.SubStr(std::get<0>(*it), std::get<1>(*it)); if (itStr.IsEmpty()) { continue; } if (itStr == curStr) { return true; } } return false; } /** * Forward filename number. * if filename length < 12, then $ * else then , the length of is 13 */ template inline size_t ForwardFileNameNumber(T& mangled, size_t idx) { size_t curIdx = idx; bool isValid = false; while (true) { if (curIdx >= mangled.Length()) { return idx; } if (mangled[curIdx] == MANGLE_FILE_NUMBER_END) { return ++curIdx; } if (curIdx + 1 - idx == FILE_HASH_LEN) { isValid = true; break; } curIdx++; } if (isValid) { for (int i = idx; i <= static_cast(curIdx); i++) { if (!isalnum(mangled[i])) { return idx; } } return ++curIdx; } return idx; } template size_t DeCompression::ForwardPackageName(T& mangled, size_t& cnt, size_t idx) { size_t curIdx = idx; if (IsStdPkgName(mangled, curIdx)) { curIdx += SPECIAL_NAME_LEN; } else { curIdx = ForwardName(mangled, idx); } if (this->isRecord && curIdx != idx) { std::tuple pos(idx, curIdx - idx); bool isPush = TreeIdMapPushBack(mangled, pos); cnt = isPush ? cnt + 1 : cnt; } if (curIdx < mangled.Length() && mangled[curIdx] == MANGLE_FILE_ID_PREFIX) { size_t fid = ForwardFileNameNumber(mangled, curIdx + MANGLE_CHAR_LEN); if (this->isRecord && fid != curIdx + MANGLE_CHAR_LEN) { std::tuple pos(curIdx, fid - curIdx); bool isPush = TreeIdMapPushBack(mangled, pos); cnt = isPush ? cnt + 1 : cnt; } curIdx = fid; } return curIdx; } template size_t DeCompression::ForwardGenericTypes(T& mangled, size_t& cnt, size_t idx) { size_t curCnt = cnt; size_t curIdx = ForwardTypes(mangled, cnt, idx + MANGLE_CHAR_LEN); if (curIdx != idx && mangled[curIdx] == END) { return curIdx + MANGLE_CHAR_LEN; } if (this->isRecord) { TreeIdMapPop(cnt, curCnt); } return idx; } /** * Forward class type, CNE. * CNI+E */ template size_t DeCompression::ForwardClassType(T& mangled, size_t& cnt, size_t idx) { size_t curIdx = idx; size_t curCnt = cnt; // Skip 'CN' 'RN' 'NN' if (idx + PREFIX_LEN < mangled.Length() && (mangled[idx] == 'C' || mangled[idx] == 'R' || mangled[idx] == 'N') && mangled[idx + MANGLE_CHAR_LEN] == MANGLE_NESTED_PREFIX[0]) { curIdx += PREFIX_LEN; } else { return idx; } UpdateCompressedName(mangled, curIdx); curIdx = ForwardPackageName(mangled, cnt, curIdx); size_t fid = curIdx; UpdateCompressedName(mangled, fid); curIdx = ForwardName(mangled, fid); if (this->isRecord && curIdx != fid) { std::tuple pos(fid, curIdx - fid); bool isPush = TreeIdMapPushBack(mangled, pos); cnt = isPush ? cnt + 1 : cnt; } if (mangled[curIdx] == MANGLE_GENERIC_PREFIX) { size_t genericIdx = ForwardGenericTypes(mangled, cnt, curIdx); if (curIdx != genericIdx) { return genericIdx; } } else if (mangled[curIdx] == END) { return curIdx + MANGLE_CHAR_LEN; } if (this->isRecord) { TreeIdMapPop(cnt, curCnt); } return idx; } /** * Forward function type, include general function and C function. * F0E * FCE */ template size_t DeCompression::ForwardFunctionType(T& mangled, size_t& cnt, size_t idx) { size_t curCnt = cnt; size_t curIdx = ForwardType(mangled, cnt, idx + PREFIX_LEN); curIdx = ForwardTypes(mangled, cnt, curIdx); if (curIdx != idx && mangled[curIdx] == END) { return curIdx + MANGLE_CHAR_LEN; } if (this->isRecord) { TreeIdMapPop(cnt, curCnt); } return idx; } // Forward tuple type, T+E. template size_t DeCompression::ForwardTupleType(T& mangled, size_t& cnt, size_t idx) { size_t curIdx = ForwardNumber(mangled, idx + MANGLE_CHAR_LEN); if (curIdx == idx + MANGLE_CHAR_LEN || curIdx >= mangled.Length() || idx + MANGLE_CHAR_LEN >= mangled.Length()) { return idx; } size_t curCnt = cnt; size_t tyCnt = GetNumber(mangled.SubStr(idx + MANGLE_CHAR_LEN, curIdx - idx - MANGLE_CHAR_LEN)); size_t endIdx = curIdx; while (endIdx < mangled.Length() && mangled[endIdx] != END && tyCnt) { size_t tyIdx = ForwardType(mangled, cnt, endIdx); if (tyIdx == endIdx) { break; } endIdx = tyIdx; --tyCnt; } if (curIdx != endIdx && mangled[endIdx] == END) { return endIdx + MANGLE_CHAR_LEN; } if (this->isRecord) { TreeIdMapPop(cnt, curCnt); } return idx; } // Forward CPointer type, P. template size_t DeCompression::ForwardCPointer(T& mangled, size_t& cnt, size_t idx) { size_t curCnt = cnt; size_t curIdx = ForwardType(mangled, cnt, idx + MANGLE_CHAR_LEN); if (curIdx != idx) { return curIdx; } if (this->isRecord) { TreeIdMapPop(cnt, curCnt); } return idx; } /** * Forward RawArray type or VArray type. * A * V */ template size_t DeCompression::ForwardArrayType(T& mangled, size_t& cnt, size_t idx) { size_t curCnt = cnt; size_t curIdx = idx + MANGLE_CHAR_LEN; if (curIdx < mangled.Length()) { curIdx = ForwardNumber(mangled, curIdx); if (curIdx < mangled.Length()) { size_t endIdx = ForwardType(mangled, cnt, curIdx); if (endIdx != curIdx) { return endIdx; } } } if (this->isRecord) { TreeIdMapPop(cnt, curCnt); } return idx; } // Forward generic type, G. template size_t DeCompression::ForwardGenericType(T& mangled, size_t& cnt, size_t idx) { size_t nextIdx = ForwardNumber(mangled, idx + MANGLE_CHAR_LEN); if (nextIdx != idx + MANGLE_CHAR_LEN) { return nextIdx; } return idx; } template size_t DeCompression::ForwardNumber(T& mangled, size_t idx) { size_t curIdx = idx; while (curIdx < mangled.Length() && isalnum(mangled[curIdx])) { curIdx++; } if (curIdx < mangled.Length() && mangled[curIdx] == MANGLE_UNDERSCORE_PREFIX) { return curIdx + MANGLE_CHAR_LEN; } return idx; } /** * Forward name: * 1. operator name * 2. prop name * 3. general name -- */ template size_t DeCompression::ForwardName(T& mangled, size_t idx) { if (idx >= mangled.Length()) { return idx; } if (IsOperatorName(mangled, idx) || IsPropName(mangled, idx)) { return idx + SPECIAL_NAME_LEN; } if (!isdigit(mangled[idx])) { return idx; } if (mangled[idx] == MANGLE_ANONYMOUS_PREFIX) { return idx + MANGLE_CHAR_LEN; } size_t numberLen = 0; while (idx < mangled.Length() && isdigit(mangled[idx + numberLen])) { numberLen++; } size_t number = atoi(mangled.SubStr(idx, numberLen).Str()); return idx + numberLen + number; } template void DeCompression::TreeIdMapPop(size_t& from, size_t to) { while (from > to) { this->treeIdMap.pop_back(); from--; } } template T DeCompression::TreeIdMapErase(T& mangled, size_t& cnt, size_t entityId, size_t sid) { treeIdMap.erase(treeIdMap.begin() + entityId); UpdateCompressedName(mangled, sid, mangled.Length()); cnt--; return mangled; } template bool DeCompression::TreeIdMapPushBack(T& mangled, std::tuple& pos) { if (std::get<1>(pos) == 0) { this->treeIdMap.push_back(pos); return true; } else if (std::get<1>(pos) != 0 && !HasDuplicates(mangled, pos)) { this->treeIdMap.push_back(pos); return true; } return false; } template void DeCompression::TreeIdMapAssign(T& mangled, T& mangledCopy, size_t mapId, size_t& cnt, std::tuple& eleInfo) { // The subscript indexes of the eleInfo are as follows: // { 0: idx, 1: nextIdx } size_t idx = std::get<0>(eleInfo); size_t nextIdx = std::get<1>(eleInfo); std::get<1>(treeIdMap[mapId]) = nextIdx - idx; if (HasDuplicates(mangled, mapId)) { TreeIdMapErase(mangledCopy, cnt, mapId, idx); mangled = mangledCopy; } } template size_t DeCompression::ForwardType(T& mangled, size_t& cnt, size_t idx) { char ch = mangled[idx]; size_t nextIdx = idx; size_t curCnt = cnt; T mangledCopy = mangled; // Record current treeIdMap's index after treeIdMap pushed type size_t mapId = this->treeIdMap.size(); if (this->isRecord) { std::tuple pos (idx, 0); bool isPush = TreeIdMapPushBack(mangled, pos); cnt = isPush ? cnt + 1 : cnt; } switch (ch) { case 'C': case 'R': case 'N': nextIdx = ForwardClassType(mangled, cnt, idx); break; case 'F': { // The function type prefix is F0/FC. if (idx + MANGLE_CHAR_LEN < mangled.Length() && (mangled[idx + MANGLE_CHAR_LEN] == '0' || mangled[idx + MANGLE_CHAR_LEN] == 'C')) { nextIdx = ForwardFunctionType(mangled, cnt, idx); } else { nextIdx = idx; } break; } case 'T': nextIdx = ForwardTupleType(mangled, cnt, idx); break; case 'P': nextIdx = ForwardCPointer(mangled, cnt, idx); break; case 'A': case 'V': nextIdx = ForwardArrayType(mangled, cnt, idx); break; case 'k': { nextIdx = idx + MANGLE_CHAR_LEN; if (this->isRecord) { TreeIdMapPop(cnt, curCnt); } return nextIdx; } case 'G': nextIdx = ForwardGenericType(mangled, cnt, idx); break; case 'Y': { nextIdx = ForwardNumber(mangled, idx + MANGLE_CHAR_LEN); std::tuple updateRes{0, nextIdx}; if (nextIdx != idx) { updateRes = UpdateCompressedName(mangled, idx); } if (this->isRecord) { TreeIdMapPop(cnt, curCnt); } return std::get<0>(updateRes) == 0 ? idx : std::get<1>(updateRes); } default: { if (IsPrimitiveType(ch)) { nextIdx = ForwardPrimitiveType(mangled, idx); if (this->isRecord) { TreeIdMapPop(cnt, curCnt); } return nextIdx; } else { nextIdx = idx; } break; } } if (this->isRecord && nextIdx != idx) { std::tuple eleInfo(idx, nextIdx); TreeIdMapAssign(mangled, mangledCopy, mapId, cnt, eleInfo); } else if (this->isRecord && nextIdx == idx) { TreeIdMapPop(cnt, curCnt); } return nextIdx; } template size_t DeCompression::ForwardTypes(T& mangled, size_t& cnt, size_t startId) { if (mangled.Length() - startId <= 0) { return startId; } size_t idx = startId; while (idx < mangled.Length() && mangled[idx] != END) { size_t curIdx = ForwardType(mangled, cnt, idx); if (curIdx == idx) { return curIdx; } idx = curIdx; } return idx; } template bool DeCompression::IsCompressed(T& mangled) { if (mangled.IsEmpty()) { return false; } size_t pos = 0; while ((pos = mangled.Find(MANGLE_COMPRESS_PREFIX, pos)) != NPOS) { size_t nextIdx = ForwardNumber(mangled, pos + MANGLE_CHAR_LEN); if (nextIdx != pos + MANGLE_CHAR_LEN) { return true; } else { pos += 1; } } return false; } /** * Generate a tree for var decl. * Iterates through the generated index mapping, and decompresses mangled name. */ template void DeCompression::SpanningVarDeclTree() { this->isRecord = true; size_t idx = this->currentIndex; size_t change = std::get<0>(UpdateCompressedName(this->decompressed, idx)); if (this->decompressed.Length() > 0 && isdigit(this->decompressed[idx])) { idx = ForwardName(this->decompressed, idx); if (idx != this->currentIndex && change == 0) { std::tuple pos(this->currentIndex, idx - this->currentIndex); TreeIdMapPushBack(this->decompressed, pos); } } this->isRecord = false; } /** * Generate a tree for func decl. * Iterates through the generated index mapping, and decompresses mangled name. */ template void DeCompression::SpanningFuncDeclTree() { size_t idx = this->currentIndex; if (this->decompressed.Length() == 0 || (this->decompressed.Length() > 0 && this->decompressed[idx] == MANGLE_LAMBDA_PREFIX)) { return; } this->isRecord = true; size_t change = std::get<0>(UpdateCompressedName(this->decompressed, idx)); if (isdigit(this->decompressed[idx]) || IsOperatorName(this->decompressed, idx) || IsPropName(this->decompressed, idx)) { idx = ForwardName(this->decompressed, idx); if (idx != this->currentIndex && change == 0) { std::tuple funcPos(this->currentIndex, this->decompressed.Length() - this->currentIndex); TreeIdMapPushBack(this->decompressed, funcPos); std::tuple pos(this->currentIndex, idx - this->currentIndex); TreeIdMapPushBack(this->decompressed, pos); } } change = std::get<0>(UpdateCompressedName(this->decompressed, idx)); if (idx < this->decompressed.Length() && this->decompressed[idx] == MANGLE_COUNT_PREFIX) { size_t nextIdx = ForwardNumber(this->decompressed, idx + MANGLE_CHAR_LEN); if (nextIdx != idx + MANGLE_CHAR_LEN && change == 0) { std::tuple pos(idx, nextIdx - idx); TreeIdMapPushBack(this->decompressed, pos); idx = nextIdx; } } if (idx < this->decompressed.Length() && this->decompressed[idx] == MANGLE_GENERIC_PREFIX) { size_t cnt = 0; size_t nextIdx = ForwardTypes(this->decompressed, cnt, idx + MANGLE_CHAR_LEN); if (nextIdx != idx + MANGLE_CHAR_LEN) { idx = nextIdx; } } if (idx < this->decompressed.Length() && this->decompressed[idx] == MANGLE_FUNCTION_PREFIX) { size_t cnt = 0; size_t nextIdx = ForwardTypes(this->decompressed, cnt, idx + MANGLE_CHAR_LEN); if (nextIdx != idx + MANGLE_CHAR_LEN) { idx = nextIdx; } } this->isRecord = false; } template T DeCompression::CJMangledDeCompression(bool isType) { if (!IsCompressed(this->mangledName) || this->mangledName.IsEmpty()) { return this->mangledName; } this->pid = 0; T demangled = this->mangledName.SubStr(0); this->pid = StripCangjieAt(demangled, this->pid); if (demangled.IsEmpty() || isType || !IsSamePrefix(demangled, MANGLE_CANGJIE_PREFIX, this->pid)) { return this->mangledName; } // Skip "_C" this->pid = StripCangjiePrefix(demangled, this->pid); demangled = this->mangledName.SubStr(this->pid); if (IsGlobalEncode(demangled)) { return this->mangledName; } if (IsDefaultParamFuncEncode(demangled)) { this->pid += PREFIX_LEN; demangled = demangled.SubStr(PREFIX_LEN); this->currentIndex = TryParsePath(demangled); if (IsCompressed(this->decompressed)) { SpanningFuncDeclTree(); } return this->pid == 0 ? this->decompressed : this->mangledName.SubStr(0, this->pid) + this->decompressed; } if (!demangled.IsEmpty() && demangled[0] == MANGLE_NESTED_PREFIX[0]) { this->pid += MANGLE_CHAR_LEN; demangled = demangled.SubStr(MANGLE_CHAR_LEN); this->currentIndex = TryParsePath(demangled); T rest = this->currentIndex >= decompressed.Length() ? "" : this->decompressed.SubStr(this->currentIndex); if (!IsCompressed(rest)) { return this->pid == 0 ? this->decompressed : this->mangledName.SubStr(0, this->pid) + this->decompressed; } if (IsVarDeclEncode(rest)) { SpanningVarDeclTree(); } else { // Determine whether the code is a function code. // _CN[I+]H+ // _CNK[I+]H+ // _CNLE SpanningFuncDeclTree(); } return this->pid == 0 ? this->decompressed : this->mangledName.SubStr(0, this->pid) + this->decompressed; } return demangled; } template std::tuple DeCompression::UpdateCompressedName(T& compressed, size_t idx) { size_t change = 0; char ch = compressed[idx]; while (ch == MANGLE_COMPRESS_PREFIX && !this->treeIdMap.empty()) { size_t curIdx = ForwardNumber(compressed, idx + MANGLE_CHAR_LEN); if (curIdx != idx + MANGLE_CHAR_LEN) { T numberStr = compressed.SubStr(idx + MANGLE_CHAR_LEN, curIdx - idx - MANGLE_CHAR_LEN); size_t mapIdx = GetNumber(numberStr); if (mapIdx >= this->treeIdMap.size() || std::get<1>(this->treeIdMap[mapIdx]) == 0) { break; } T curRest = curIdx < compressed.Length() ? compressed.SubStr(curIdx) : ""; compressed = compressed.SubStr(0, idx) + compressed.SubStr(std::get<0>(this->treeIdMap[mapIdx]), std::get<1>(this->treeIdMap[mapIdx])) + curRest; change++; idx += std::get<1>(this->treeIdMap[mapIdx]); if (idx >= compressed.Length()) { break; } ch = compressed[idx]; } else { break; } } return std::tuple(change, idx); } template size_t DeCompression::UpdateCompressedName(T& compressed, size_t sid, size_t eid) { size_t idx = sid; while (idx < eid) { if (compressed[idx] == MANGLE_COMPRESS_PREFIX && !this->treeIdMap.empty() && idx + MANGLE_CHAR_LEN < compressed.Length()) { size_t curIdx = ForwardNumber(compressed, idx + MANGLE_CHAR_LEN); if (curIdx == idx + MANGLE_CHAR_LEN) { idx++; continue; } T numberStr = compressed.SubStr(idx + MANGLE_CHAR_LEN, curIdx - idx - MANGLE_CHAR_LEN); size_t mapIdx = GetNumber(numberStr); if (mapIdx >= this->treeIdMap.size()) { return 0; } if (std::get<1>(this->treeIdMap[mapIdx]) == 0) { idx++; continue; } T curRest = curIdx < compressed.Length() ? compressed.SubStr(curIdx) : ""; compressed = compressed.SubStr(0, idx) + compressed.SubStr(std::get<0>(this->treeIdMap[mapIdx]), std::get<1>(this->treeIdMap[mapIdx])) + curRest; eid = compressed.Length(); idx += std::get<1>(this->treeIdMap[mapIdx]); } else { idx++; } } return 0; } template size_t DeCompression::TryExtendPath(T& mangled, size_t& count, size_t idx, size_t entityId, T& curMangled) { bool isPush = false; T mangledCopy = mangled; size_t nextIdx = ForwardType(mangled, count, idx + MANGLE_CHAR_LEN); size_t change = std::get<0>(UpdateCompressedName(mangled, nextIdx)); size_t tyId = nextIdx; if (nextIdx < mangled.Length() && mangled[nextIdx] == MANGLE_FILE_ID_PREFIX) { size_t fid = nextIdx; nextIdx = ForwardFileNameNumber(mangled, fid + MANGLE_CHAR_LEN); if (nextIdx != fid + MANGLE_CHAR_LEN && change == 0) { std::tuple pos(fid, nextIdx - fid); isPush = TreeIdMapPushBack(mangled, pos); count = isPush ? count + 1 : count; } change = std::get<0>(UpdateCompressedName(mangled, nextIdx)); if (nextIdx < mangled.Length() && mangled[nextIdx] == MANGLE_COUNT_PREFIX) { size_t lid = nextIdx; nextIdx = ForwardNumber(mangled, nextIdx + MANGLE_CHAR_LEN); std::tuple eleInfo(idx, nextIdx); TreeIdMapAssign(mangled, mangledCopy, entityId, count, eleInfo); if (nextIdx != lid + MANGLE_CHAR_LEN && change == 0) { std::tuple pos(lid, nextIdx - lid); isPush = TreeIdMapPushBack(mangled, pos); count = isPush ? count + 1 : count; } } else { nextIdx = idx; } } else { nextIdx = tyId; } return nextIdx; } template size_t DeCompression::TryLambdaPath(T& mangled, size_t& count, size_t idx, size_t entityId, size_t change) { T mangledCopy = mangled; size_t nextIdx = ForwardNumber(mangled, idx + MANGLE_CHAR_LEN); if (nextIdx == mangled.Length() - 1 && mangled[nextIdx] == END) { nextIdx = idx; } else { nextIdx = nextIdx == idx + MANGLE_CHAR_LEN ? idx : nextIdx; if (nextIdx != idx || change > 0) { std::tuple eleInfo(idx, nextIdx); TreeIdMapAssign(mangled, mangledCopy, entityId, count, eleInfo); } } return nextIdx; } template size_t DeCompression::TryGenericPrefixPath(T& mangled, size_t& count, T& curMangled, std::tuple rParams) { T mangledCopy = mangled; size_t idx = std::get<0>(rParams); size_t entityId = std::get<1>(rParams); size_t nextIdx = std::get<2>(rParams); nextIdx = ForwardTypes(mangled, count, nextIdx + MANGLE_CHAR_LEN); if (nextIdx < mangled.Length() && mangled[nextIdx] == END) { nextIdx++; std::tuple eleInfo(idx, nextIdx); TreeIdMapAssign(mangled, mangledCopy, entityId, count, eleInfo); } else if (nextIdx < mangled.Length() && mangled[nextIdx] == MANGLE_FUNCTION_PREFIX) { nextIdx = ForwardTypes(mangled, count, nextIdx + MANGLE_CHAR_LEN); if (nextIdx < mangled.Length() && mangled[nextIdx] == END) { nextIdx++; std::tuple eleInfo(idx, nextIdx); TreeIdMapAssign(mangled, mangledCopy, entityId, count, eleInfo); } else if (nextIdx == mangled.Length()) { std::tuple eleInfo(idx, nextIdx); TreeIdMapAssign(mangled, mangledCopy, entityId, count, eleInfo); } else { nextIdx = idx; } } else { nextIdx = idx; } return nextIdx; } template size_t DeCompression::TryNamePrefixPath(T& mangled, size_t& count, T& curMangled, std::tuple rParams) { T mangledCopy = mangled; size_t idx = std::get<0>(rParams); size_t entityId = std::get<1>(rParams); size_t change = std::get<2>(rParams); size_t idFlag = 0; size_t nextIdx = ForwardName(mangled, idx); size_t nameIdx = nextIdx; if (nextIdx < mangled.Length() && mangled[nextIdx] == MANGLE_COUNT_PREFIX) { idFlag = nextIdx; nextIdx = ForwardNumber(mangled, nextIdx + MANGLE_CHAR_LEN); } if (nextIdx != idx && change == 0) { std::tuple pos(idx, nextIdx - idx); bool isPush = TreeIdMapPushBack(mangled, pos); count = isPush ? count + 1 : count; } if (nextIdx < mangled.Length()) { idFlag = nextIdx; change = std::get<0>(UpdateCompressedName(mangled, idFlag)); nextIdx = idFlag; if (mangled[nextIdx] == MANGLE_FILE_ID_PREFIX) { nextIdx = ForwardFileNameNumber(mangled, nameIdx + MANGLE_CHAR_LEN); std::tuple eleInfo(idx, nextIdx); TreeIdMapAssign(mangled, mangledCopy, entityId, count, eleInfo); if (nextIdx != idFlag + MANGLE_CHAR_LEN && change == 0) { std::tuple pos(idFlag, nextIdx - idFlag); bool isPush = TreeIdMapPushBack(mangled, pos); count = isPush ? count + 1 : count; } nextIdx = nextIdx == nameIdx + 1 ? nameIdx : nextIdx; } else if (mangled[nextIdx] == MANGLE_GENERIC_PREFIX) { std::tuple rParams{idx, entityId, nextIdx}; nextIdx = TryGenericPrefixPath(mangled, count, curMangled, rParams); } else if (mangled[nextIdx] == MANGLE_FUNCTION_PREFIX) { nextIdx = ForwardTypes(mangled, count, nameIdx + MANGLE_CHAR_LEN); if (nextIdx < mangled.Length() && mangled[nextIdx] == END) { nextIdx++; std::tuple eleInfo(idx, nextIdx); TreeIdMapAssign(mangled, mangledCopy, entityId, count, eleInfo); } else if (nextIdx == mangled.Length()) { std::tuple eleInfo(idx, nextIdx); TreeIdMapAssign(mangled, mangledCopy, entityId, count, eleInfo); } else { nextIdx = idx; } } else { nextIdx = nameIdx; } } if (nextIdx == nameIdx) { std::get<1>(this->treeIdMap[entityId]) = nameIdx - idx; TreeIdMapPop(count, 1); } return nextIdx; } template size_t DeCompression::TryParsePath(T mangled) { this->isRecord = true; size_t idx = 0; while (idx < mangled.Length()) { char ch = mangled[idx]; size_t nextIdx = idx; size_t change = std::get<0>(UpdateCompressedName(mangled, nextIdx)); T curMangled = mangled; std::tuple pos(idx, 0); bool isPush = TreeIdMapPushBack(mangled, pos); size_t count = isPush ? 1 : 0; size_t entityId = this->treeIdMap.size() - 1; if (ch == MANGLE_ANONYMOUS_PREFIX) { nextIdx = ForwardNumber(mangled, idx + MANGLE_CHAR_LEN); if (nextIdx != idx + MANGLE_CHAR_LEN) { std::tuple eleInfo(idx, nextIdx); TreeIdMapAssign(mangled, curMangled, entityId, count, eleInfo); } if (nextIdx == idx + MANGLE_CHAR_LEN || change > 0) { nextIdx = idx; } } else if (isdigit(ch) || IsOperatorName(mangled, idx) || IsPropName(mangled, idx)) { std::tuple rParams{idx, entityId, change}; nextIdx = TryNamePrefixPath(mangled, count, curMangled, rParams); } else if (ch == MANGLE_EXTEND_PREFIX) { nextIdx = TryExtendPath(mangled, count, idx, entityId, curMangled); } else if (ch == MANGLE_LAMBDA_PREFIX) { nextIdx = TryLambdaPath(mangled, count, idx, entityId, change); } else if (IsStdPkgName(mangled, idx)) { nextIdx = ForwardPackageName(mangled, count, idx); if (nextIdx != idx || !change) { std::tuple eleInfo(idx, nextIdx); TreeIdMapAssign(mangled, curMangled, entityId, count, eleInfo); } } if (nextIdx != idx) { idx = nextIdx; } else { TreeIdMapPop(count, 0); break; } } this->decompressed = mangled; this->isRecord = false; return idx; } #ifdef BUILD_LIB_CANGJIE_DEMANGLE // to reuse the code to compile CangjieDemangle.cpp template class DeCompression; #else template class DeCompression; #endif } // namespace Cangjiecangjie_compiler-1.0.7/demangler/DeCompression.h000066400000000000000000000310111510705540100216640ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_DECOMPRESS_H #define CANGJIE_DECOMPRESS_H #include #include #include namespace Cangjie { constexpr size_t NPOS = static_cast(-1); template class DeCompression { public: /** * @brief The constructor of class DeCompression. * * @param mangled The name to decompress. * @return DeCompression The instance of DeCompression. */ explicit DeCompression(const T& mangled) : isRecord(false) { mangledName = mangled; } /** * @brief Main entry of mangler decompression. * * @param isType Whether the demangled name is type. * @return T The demangled name after decompression. */ T CJMangledDeCompression(bool isType = false); /** * @brief Get the index at the end of Class type. * * @param mangled The name needs to decompress. * @param cnt Record the number of new elements added to the treeIdMap. * @param idx The start index of the demangled name. * @return size_t The end index of the demangled name. */ size_t ForwardClassType(T& mangled, size_t& cnt, size_t idx); /** * @brief Get the index at the end of Generic types. e.g. I+E * * @param mangled The name needs to decompress. * @param cnt Record the number of new elements added to the treeIdMap. * @param idx The start index of the demangled name. * @return size_t The end index of the demangled name. */ size_t ForwardGenericTypes(T& mangled, size_t& cnt, size_t idx); /** * @brief Get the index at the end of Function type. * * @param mangled The name needs to decompress. * @param cnt Record the number of new elements added to the treeIdMap. * @param idx The start index of the demangled name. * @return size_t The end index of the demangled name. */ size_t ForwardFunctionType(T& mangled, size_t& cnt, size_t idx); /** * @brief Get the index at the end of Tuple type. * * @param mangled The name needs to decompress. * @param cnt Record the number of new elements added to the treeIdMap. * @param idx The start index of the demangled name. * @return size_t The end index of the demangled name. */ size_t ForwardTupleType(T& mangled, size_t& cnt, size_t idx); /** * @brief Get the index at the end of CPointer type. * * @param mangled The name needs to decompress. * @param cnt Record the number of new elements added to the treeIdMap. * @param idx The start index of the demangled name. * @return size_t The end index of the demangled name. */ size_t ForwardCPointer(T& mangled, size_t& cnt, size_t idx); /** * @brief Get the index at the end of Array type. * * @param mangled The name needs to decompress. * @param cnt Record the number of new elements added to the treeIdMap. * @param idx The start index of the demangled name. * @return size_t The end index of the demangled name. */ size_t ForwardArrayType(T& mangled, size_t& cnt, size_t idx); /** * @brief Get the index at the end of Generic type. e.g. G * * @param mangled The name needs to decompress. * @param cnt Record the number of new elements added to the treeIdMap. * @param idx The start index of the demangled name. * @return size_t The end index of the demangled name. */ size_t ForwardGenericType(T& mangled, size_t& cnt, size_t idx); /** * @brief Get the index at the end of package name. * * @param mangled The name needs to decompress. * @param cnt Record the number of new elements added to the treeIdMap. * @param idx The start index of the demangled name. * @return size_t The end index of the demangled name. */ size_t ForwardPackageName(T& mangled, size_t& cnt, size_t idx); /** * @brief Get the index at the end of the type. * * @param mangled The name needs to decompress. * @param cnt Record the number of new elements added to the treeIdMap. * @param idx The start index of the demangled name. * @return size_t The end index of the demangled name. */ size_t ForwardType(T& mangled, size_t& cnt, size_t idx = 0); /** * @brief Get the index at the end of the types. * * @param mangled The name needs to decompress. * @param cnt Record the number of new elements added to the treeIdMap. * @param idx The start index of the demangled name. * @return size_t The end index of the demangled name. */ size_t ForwardTypes(T& mangled, size_t& cnt, size_t idx = 0); /** * @brief Get the index at the end of the number. * * @param mangled The name needs to decompress. * @param idx The start index of the demangled name. * @return size_t The end index of the demangled name. */ size_t ForwardNumber(T& mangled, size_t idx = 0); /** * @brief Get the index at the end of the name. * * @param mangled The name needs to decompress. * @param idx The start index of the demangled name. * @return size_t The end index of the demangled name. */ size_t ForwardName(T& mangled, size_t idx = 0); /** * @brief Check whether the demangle name is compressed. * * @param mangled The name needs to decompress. * @return bool If yes, true is returned. Otherwise, false is returned. */ bool IsCompressed(T& mangled); /** * @brief Check whether the demangle name is variable decl. * * @param mangled The name needs to decompress. * @return bool If yes, true is returned. Otherwise, false is returned. */ bool IsVarDeclEncode(T& mangled); /** * @brief Check whether the demangle name is global name. * * @param mangled The name needs to decompress. * @return bool If yes, true is returned. Otherwise, false is returned. */ bool IsGlobalEncode(T& mangled); /** * @brief Check whether the demangle name is default param function. * * @param mangled The name needs to decompress. * @return bool If yes, true is returned. Otherwise, false is returned. */ bool IsDefaultParamFuncEncode(T& mangled); /** * @brief Check if the first and the second have the same prefix. * * @param first The first string. * @param second The second string. * @param idx The start index of the first and the second. * @return bool If yes, true is returned. Otherwise, false is returned. */ bool IsSamePrefix(T& first, T second, size_t idx); /** * @brief Check if the treeIdMap vector contains duplicates. * * @param mangled The name needs to decompress. * @param pos The position which is a pair consisting of start index and length. * @return bool If yes, true is returned. Otherwise, false is returned. */ bool HasDuplicates(T& mangled, std::tuple& pos); /** * @brief Check if the treeIdMap vector contains duplicates. * * @param mangled The name needs to decompress. * @param mid The mid-th element in the treeIdMap vector. * @return bool If yes, true is returned. Otherwise, false is returned. */ bool HasDuplicates(T& mangled, size_t mid); /** * @brief Update the compressed name. * * @param compressed The name needs to update. * @param idx The start index of the compressed name. * @return std::tuple A pair consisting of the change count and the end index. */ std::tuple UpdateCompressedName(T& compressed, size_t idx); /** * @brief Update the compressed name. * * @param compressed The name needs to update. * @param sid The start index which the compressed name has been updated. * @param eid The end index which the compressed name has been updated. * @return size_t The real end index which the compressed name has been updated. */ size_t UpdateCompressedName(T& compressed, size_t sid, size_t eid); /** * @brief Try to decompress extend path. * * @param mangled The compressed name will be updated. * @param count Record the number of new elements added to the treeIdMap. * @param idx The start index of the compressed name. * @param entityId The index of treeIdMap vector whose item needs to update the end index. * @param curMangled The origin compressed name. * @return size_t The end index which the compressed name has been updated. */ size_t TryExtendPath(T& mangled, size_t& count, size_t idx, size_t entityId, T& curMangled); /** * @brief Try to decompress lambda path. * * @param mangled The compressed name will be updated. * @param count Record the number of new elements added to the treeIdMap. * @param idx The start index of the compressed name. * @param entityId The index of treeIdMap vector whose item needs to update the end index. * @param change Indicates whether the compressed name has been updated. * @return size_t The end index which the compressed name has been updated. */ size_t TryLambdaPath(T& mangled, size_t& count, size_t idx, size_t entityId, size_t change); /** * @brief Try to decompress generic prefix path. * * @param mangled The compressed name will be updated. * @param count Record the number of new elements added to the treeIdMap. * @param curMangled The origin compressed name. * @param rParams A tuple consisting of the start index, entity id and the end index. * @return size_t The end index which the compressed name has been updated. */ size_t TryGenericPrefixPath(T& mangled, size_t& count, T& curMangled, std::tuple rParams); /** * @brief Try to decompress name prefix path. * * @param mangled The compressed name will be updated. * @param count Record the number of new elements added to the treeIdMap. * @param curMangled The origin compressed name. * @param rParams A tuple consisting of the start index, entity id and the change. * @return size_t The end index which the compressed name has been updated. */ size_t TryNamePrefixPath(T& mangled, size_t& count, T& curMangled, std::tuple rParams); /** * @brief Try to decompress path. * * @param mangled The compressed name will be updated. * @return size_t The end index which the compressed name has been updated. */ size_t TryParsePath(T mangled); /** * @brief Generate variable decl decompressed name. */ void SpanningVarDeclTree(); /** * @brief Generate function decl decompressed name. */ void SpanningFuncDeclTree(); /** * @brief Pop elements of treeIdMap vector. * * @param from The start index of treeIdMap vector. * @param to The end index of treeIdMap vector. */ void TreeIdMapPop(size_t& from, size_t to); /** * @brief Push elements of treeIdMap vector. * * @param mangled The compressed name. * @param pos The element needs to push. * @return bool If push success, true is returned. Otherwise, false is returned. */ bool TreeIdMapPushBack(T& mangled, std::tuple& pos); /** * @brief Erase elements of treeIdMap vector. * * @param mangled The compressed name. * @param cnt The treeIdMap size. * @param eid The entity id. * @param sid The start index of the compressed name(mangled). * @return T The compressed name(mangled) has been updated. */ T TreeIdMapErase(T& mangled, size_t& cnt, size_t eid, size_t sid); /** * @brief Assign elements of treeIdMap vector. * * @param mangled The compressed name. * @param mangledCopy The origin compressed name. * @param mapId The index of treeIdMap vector whose item needs to update the end index. * @param cnt The treeIdMap size. * @param eleInfo A pair consisting of start index and end index. */ void TreeIdMapAssign(T& mangled, T& mangledCopy, size_t mapId, size_t& cnt, std::tuple& eleInfo); private: size_t pid{}; bool isRecord; T mangledName; size_t currentIndex{}; T decompressed; std::vector> treeIdMap; }; } #endif // CANGJIE_DECOMPRESS_Hcangjie_compiler-1.0.7/demangler/Demangler.cpp000066400000000000000000001256521510705540100213620ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifdef BUILD_LIB_CANGJIE_DEMANGLE // to reuse the code to compile CangjieDemangle.cpp #include #include #include "StdString.h" #else #include "Base/CString.h" #endif #include "DeCompression.h" #include "Utils.h" #include "Demangler.h" namespace { constexpr char MANGLE_TUPLE_PREFIX = 'T'; constexpr char MANGLE_ARRAY_PREFIX = 'A'; constexpr char MANGLE_VARRAY_PREFIX = 'V'; constexpr char MANGLE_PTR_PREFIX = 'P'; constexpr char MANGLE_CSTRING_PREFIX = 'k'; constexpr char MANGLE_VOID_PREFIX = 'v'; constexpr char MANGLE_GTY_PREFIX = 'G'; constexpr char MANGLE_DEFAULT_PARAM_FUNCTION_PREFIX = '$'; const char ARGS_DELIMITER[] = ", "; const char ARGS_DELIMITER_TYPE[] = ","; const char LEFT_BRACKET[] = "("; const char RIGHT_BRACKET[] = ")"; const char MODULE_SPLIT[] = "/"; const char MANGLE_MODULE_SPLIT[] = "$"; const char MANGLE_FUNCTY_PREFIX[] = "F0"; const char MANGLE_C_FUNCTY_PREFIX[] = "FC"; const char MANGLE_FLOAT16_PREFIX[] = "Dh"; const char MANGLE_SET_PREFIX[] = "ps"; const char MANGLE_GET_PREFIX[] = "pg"; const char GLOBAL_VARIABLE_INIT_PREFIX[] = "GV"; const char MANGLE_GLOBAL_PACKAGE_INIT_PREFIX[] = "_CGP"; const char MANGLE_GLOBAL_FILE_INIT_PREFIX[] = "_CGF"; const char MANGLE_FUNC_PARA_INIT_PREFIX[] = "_CPI"; const char MANGLE_GLOBAL_INIT_SUFFIX[] = "iiHv"; const char MANGLE_GLOBAL_INIT_RESET_SUFFIX[] = "irHv"; const char MANGLE_GLOBAL_INIT_FLAG_RESET_SUFFIX[] = "ifHv"; const char MANGLE_GLOBAL_INIT_LITERAL_RESET_SUFFIX[] = "ilHv"; const char MANGLE_INNER_FUNCTION_PREFIX[] = "$lambda."; const char MANGLE_CFUNCTION_WRAPPER[] = "$real"; const char BOX_DECL_PREFIX[] = "$BOX_"; const char CJ_FILE_EXT[] = ".cj"; const char ERROR_EXCEED_THE_INPUT_STRING_LENGTH[] = "exceed the input string length"; const char MANGLE_TUPLE_STR[] = "Tuple"; const char MANGLE_ARRAY_STR[] = "RawArray"; const char MANGLE_VARRAY_STR[] = "VArray"; const char MANGLE_PTR_STR[] = "CPointer"; const char MANGLE_CSTRING_STR[] = "CString"; const char BASE62_CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; const char DECIMAL_CHARS[] = "0123456789"; constexpr size_t BASE62_CHARS_SIZE = 62; constexpr uint32_t MAX_ARGS_SIZE = 16; } // namespace namespace Cangjie { template T GetPrimitiveDemangleMap(char t) { switch (t) { case 'u': return "Unit"; case 'n': return "Nothing"; case 'b': return "Bool"; case 'D': return "Float16"; case 'f': return "Float32"; case 'd': return "Float64"; case 'c': return "Rune"; case 'a': return "Int8"; case 's': return "Int16"; case 'i': return "Int32"; case 'l': return "Int64"; case 'h': return "UInt8"; case 't': return "UInt16"; case 'j': return "UInt32"; case 'm': return "UInt64"; case 'q': return "IntNative"; case 'r': return "UIntNative"; default: { return ""; } } } /** * @brief Get operator name map. * * @return T return the if the pattern is operator name. */ template T GetOperatorNameMap(T replaced) { const size_t ch = 97; // The demangled operator name rule is [a-z][a-z] if (!((replaced[0] >= 'a' && replaced[0] <= 'z') && (replaced[1] >= 'a' && replaced[1] <= 'z'))) { return ""; } // Move left by 5 digits is the way of calculating OperatorKind size_t t = ((static_cast(replaced[0]) - ch) << 5) + static_cast(replaced[1]) - ch; OperatorKind result = static_cast(t); switch (result) { case OperatorKind::IX: return "[]"; case OperatorKind::NT: return "!"; case OperatorKind::NG: return "-"; case OperatorKind::PW: return "**"; case OperatorKind::ML: return "*"; case OperatorKind::DV: return "/"; case OperatorKind::RM: return "%"; case OperatorKind::PL: return "+"; case OperatorKind::LS: return "<<"; case OperatorKind::RS: return ">>"; case OperatorKind::LT: return "<"; case OperatorKind::GT: return ">"; case OperatorKind::LE: return "<="; case OperatorKind::GE: return ">="; case OperatorKind::EQ: return "=="; case OperatorKind::NE: return "!="; case OperatorKind::AN: return "&"; case OperatorKind::EO: return "^"; case OperatorKind::OR: return "|"; case OperatorKind::CL: return "()"; default: { return ""; } } } template T ReplaceString(T str, const char* pattern, const char* replacement) { if (str.IsEmpty()) { return str; } auto pos = str.Find(pattern); auto n = str.Length(); auto pLen = strlen(pattern); while (pos > -1 && n - pos - pLen > 0) { n = str.Length(); str = str.SubStr(0, pos) + replacement + str.SubStr(pos + pLen, n - pos - pLen); pos = str.Find(pattern); } return str; } template bool IsPrimitive(char ch) { return GetPrimitiveDemangleMap(ch) != T{ "" }; } inline bool IsCurrentCharDigit(char ch) { return ch >= '0' && ch <= '9'; } inline bool IsGeneric(char ch) { return ch == MANGLE_GENERIC_PREFIX; } inline bool IsFunction(char ch) { return ch == MANGLE_FUNCTION_PREFIX; } template bool Demangler::IsProp() const { if (currentIndex + MANGLE_CHAR_LEN < mangledName.Length()) { T str = mangledName.SubStr(currentIndex, SPECIAL_NAME_LEN); return str == T{ MANGLE_SET_PREFIX } || str == T{ MANGLE_GET_PREFIX }; } return false; } template bool Demangler::IsInnerFunction() const { if (mangledName.Length() - MANGLE_CHAR_LEN > strlen(MANGLE_INNER_FUNCTION_PREFIX)) { return mangledName.Find(MANGLE_INNER_FUNCTION_PREFIX) > -1; } return false; } template bool Demangler::IsOperatorName() const { const size_t id = currentIndex + SPECIAL_NAME_LEN; if (id < mangledName.Length() && GetOperatorNameMap(mangledName.SubStr(currentIndex, SPECIAL_NAME_LEN)) != T{ "" } && (mangledName[id] == MANGLE_GENERIC_PREFIX || mangledName[id] == MANGLE_FUNCTION_PREFIX || mangledName[id] == MANGLE_COUNT_PREFIX)) { return true; } return false; } template bool Demangler::IsMaybeDefaultFuncParamFunction() const { return currentIndex < mangledName.Length() && mangledName[currentIndex] == MANGLE_DEFAULT_PARAM_FUNCTION_PREFIX; } template bool Demangler::IsNotEndOfMangledName() const { return currentIndex < mangledName.Length() && mangledName[currentIndex] != END; } template uint32_t Demangler::DemangleLength() { if (currentIndex >= mangledName.Length()) { (void)Reject(ERROR_EXCEED_THE_INPUT_STRING_LENGTH); return 0; } // The decl added by compiler has no package. if (mangledName[currentIndex] == '0') { ++currentIndex; return 0; } T numStr; isValid = false; while (currentIndex < mangledName.Length() && IsCurrentCharDigit(mangledName[currentIndex])) { numStr += T{ mangledName[currentIndex] }; ++currentIndex; isValid = true; } return atoi(numStr.Str()); } template T Demangler::UIntToString(size_t value) const { T result = T{""}; if (value == 0) { return T{"0"}; } while (value != 0) { char current = DECIMAL_CHARS[value % 10]; // 10 based calculation. value /= 10; // 10 based calculation. result = T{current} + result; } return result; } template bool Demangler::IsGlobalInit() const { auto isPkgInit = MatchForward(MANGLE_GLOBAL_PACKAGE_INIT_PREFIX, strlen(MANGLE_GLOBAL_PACKAGE_INIT_PREFIX)); auto isFileInit = MatchForward(MANGLE_GLOBAL_FILE_INIT_PREFIX, strlen(MANGLE_GLOBAL_FILE_INIT_PREFIX)); return isPkgInit || isFileInit; } template bool Demangler::IsParamInit() const { return MatchForward(MANGLE_FUNC_PARA_INIT_PREFIX, strlen(MANGLE_FUNC_PARA_INIT_PREFIX)); } template bool Demangler::IsCFunctionWrapper() const { if (mangledName.Length() > strlen(MANGLE_CFUNCTION_WRAPPER)) { return mangledName.Find(MANGLE_CFUNCTION_WRAPPER, 0) > -1; } return false; } template bool Demangler::IsQualifiedType() const { if (currentIndex >= mangledName.Length()) { return false; } if (currentIndex + MANGLE_CHAR_LEN < mangledName.Length()) { // Dh, F0 and FC belong to the legal qualified type prefix. if ((mangledName[currentIndex] == 'D' && mangledName[currentIndex + MANGLE_CHAR_LEN] == 'h') || (mangledName[currentIndex] == 'F' && mangledName[currentIndex + MANGLE_CHAR_LEN] == '0') || (mangledName[currentIndex] == 'F' && mangledName[currentIndex + MANGLE_CHAR_LEN] == 'C')) { return true; } } if (IsPrimitive(mangledName[currentIndex])) { return true; } // The {'C', 'R', 'N', 'T', 'P', 'A', 'k', 'V'} belong to the legal qualified type prefix. for (auto prefix : { 'C', 'R', 'N', 'T', 'P', 'A', 'k', 'V' }) { if (mangledName[currentIndex] == prefix) { return true; } } return false; } template bool Demangler::IsDecl() const { return MatchForward(MANGLE_NESTED_PREFIX, strlen(MANGLE_NESTED_PREFIX)); } template void Demangler::ErrorLog(const char* msg) const { #if defined(MRT_DEBUG) && (MRT_DEBUG == 1) && !defined(BUILD_LIB_CANGJIE_DEMANGLE) PRINT_ERROR("'%s' is not a valid mangling name.\n ", mangledName.Str()); // Include the first single quote for (auto i = 0u; i < currentIndex; ++i) { PRINT_ERROR(" "); } PRINT_ERROR("^ %s\n", msg); #endif } template bool Demangler::MatchForward(const char pattern[], uint32_t len) const { auto maxLen = mangledName.Length() - currentIndex; len = len > maxLen ? maxLen : len; auto prefix = mangledName.SubStr(currentIndex, len); return prefix == T{ pattern }; } template bool Demangler::PeekChar(char& ch) const { if (currentIndex >= mangledName.Length()) { return false; } ch = mangledName[currentIndex]; return true; } template bool Demangler::GetChar(char& ch) { bool success = PeekChar(ch); if (success) { ++currentIndex; } return success; } template void Demangler::SkipChar(char ch) { if (currentIndex >= mangledName.Length()) { (void)Reject(ERROR_EXCEED_THE_INPUT_STRING_LENGTH); return; } if (mangledName[currentIndex] != ch) { (void)Reject(T{ "should be the character: " } + T{ ch }); return; } ++currentIndex; return; } template void Demangler::SkipOptionalChar(char ch) { if (currentIndex >= mangledName.Length()) { return; } if (mangledName[currentIndex] == ch) { ++currentIndex; } return; } template void Demangler::SkipString(const char pattern[]) { auto len = strlen(pattern); for (size_t i = 0; i < len; ++i) { SkipChar(pattern[i]); if (!isValid) { return; } } return; } template bool DemangleInfo::MatchSuffix(const char pattern[], uint32_t len) const { auto index = demangled.Length() > len ? demangled.Length() - len : 0; auto suffix = demangled.SubStr(index, demangled.Length() - index); return suffix == T{ pattern }; } template T DemangleInfo::GetFullName(const T& scopeRes, const uint32_t argsNum) const { T fullDemangledName; switch (this->type) { case TypeKind::FUNCTION: return GetFunctionName(); case TypeKind::GENERIC_CONSTRAINTS: return GetGenericConstraintsName(); case TypeKind::ENUM: case TypeKind::CLASS: case TypeKind::STRUCT: case TypeKind::INTERFACE: case TypeKind::FUNCTION_DECL: default: auto pkg = GetPkgName(); auto identifier = GetIdentifier(); if (!IsFunctionLike() && type != TypeKind::COMMON_DECL) { if (!pkg.IsEmpty() && !identifier.IsEmpty()) { fullDemangledName += pkg; fullDemangledName += T{ scopeRes }; fullDemangledName += identifier; } else { fullDemangledName += pkg; fullDemangledName += identifier; } } else { fullDemangledName += identifier; } fullDemangledName += GetGenericTypes(); fullDemangledName += GetArgTypesName(argsNum); fullDemangledName += GetGenericConstraints(); } return fullDemangledName; } template T DemangleInfo::GetPkgName() const { return pkgName; } template T DemangleInfo::GetReturnType() const { return demangled; } template T DemangleInfo::GetGenericConstraints() const { return genericConstraints; } template T DemangleInfo::GetGenericTypes() const { return genericTypes; } template T DemangleInfo::GetGenericConstraintsName() const { auto separatorFromDecl = T{ ", " }; auto gcName = separatorFromDecl + demangled; gcName += " <: "; gcName += ReplaceString(args, ",", " &"); return gcName; } template T GetArgTypesFullName(const std::array& args, uint32_t argSize, const T& delimiter) { if (argSize == 0) { return T{}; } auto argTypesFullName = args[0]; for (size_t i = 1; i < argSize; ++i) { argTypesFullName += delimiter + args[i]; } return argTypesFullName; } template T DemangleInfo::GetArgTypesName(const uint32_t argsNum) const { if (args.IsEmpty()) { return IsFunctionLike() ? "()" : T{}; } if (IsFunctionLike()) { return T{ "(" } + args + ")"; // Like (arg-type1, arg-type2, ...) } if (argsNum != 0) { T numStr; #ifdef BUILD_LIB_CANGJIE_DEMANGLE numStr = std::to_string(argsNum); #else numStr = MapleRuntime::CString(static_cast(argsNum)); #endif return T{ "<" } + args + ",$" + numStr + ">"; } // The generic-types like return T{ "<" } + args + ">"; } template bool DemangleInfo::IsFunctionLike() const { return type == TypeKind::FUNCTION_DECL || type == TypeKind::LAMBDA_FUNCTION || type == TypeKind::FUNCTION; } template bool DemangleInfo::IsValid() const { return isValid; } template T DemangleInfo::GetFunctionName() const { return GetArgTypesName() + " -> " + GetReturnType(); } template T DemangleInfo::GetIdentifier() const { if (!demangled.IsEmpty() && demangled[0] == '<' && demangled.EndsWith(">")) { // When identifier is bracketed by '<>', its original identifier is string inside. return demangled.SubStr(1, demangled.Length() - strlen("<>")); } return demangled; } template DemangleInfo Demangler::DemangleGlobalInit() { T prefix; if (MatchForward(MANGLE_GLOBAL_PACKAGE_INIT_PREFIX, strlen(MANGLE_GLOBAL_PACKAGE_INIT_PREFIX))) { SkipString(MANGLE_GLOBAL_PACKAGE_INIT_PREFIX); auto pkgDi = DemanglePackageName(); prefix = pkgDi.pkgName + T{ "::package" }; } else if (MatchForward(MANGLE_GLOBAL_FILE_INIT_PREFIX, strlen(MANGLE_GLOBAL_FILE_INIT_PREFIX))) { SkipString(MANGLE_GLOBAL_FILE_INIT_PREFIX); auto pkgDi = DemanglePackageName(); prefix = pkgDi.pkgName + T{ "::file" }; } else { return Reject("invalid global init function"); } if (mangledName.EndsWith(MANGLE_GLOBAL_INIT_RESET_SUFFIX)) { return DemangleInfo{ prefix + T{ "_global_reset" }, TypeKind::NAME, isValid }; } else if (mangledName.EndsWith(MANGLE_GLOBAL_INIT_FLAG_RESET_SUFFIX)) { return DemangleInfo{ prefix + T{ "_global_flag_reset" }, TypeKind::NAME, isValid }; } else if (mangledName.EndsWith(MANGLE_GLOBAL_INIT_LITERAL_RESET_SUFFIX)) { return DemangleInfo{ prefix + T{ "_global_init_literal" }, TypeKind::NAME, isValid }; } else if (!mangledName.EndsWith(MANGLE_GLOBAL_INIT_SUFFIX)) { return Reject("global init function should end with iiHv"); } return DemangleInfo{ prefix + T{ "_global_init" }, TypeKind::NAME, isValid }; } template DemangleInfo Demangler::DemangleParamInit() { SkipString(MANGLE_FUNC_PARA_INIT_PREFIX); DemangleInfo di{ "", TypeKind::COMMON_DECL }; auto pkgDi = DemanglePackageName(); di.pkgName = pkgDi.pkgName; di.isPrivateDeclaration = pkgDi.isPrivateDeclaration; // Take the part before E as identifier and concatenate with scope resolution. DemangleInfo nestedDi = DemangleNestedDecls(false, true); di.demangled = nestedDi.demangled; di.type = nestedDi.type; di.args = nestedDi.args; if (!isValid || di.demangled.IsEmpty()) { return Reject(); } if (di.type == TypeKind::FUNCTION_DECL) { di.args = nestedDi.functionParameterTypes; } return di; } template T Demangler::DemangleClassType() { return DemangleClass(TypeKind::CLASS).GetIdentifier(); } // return likes "pkg.className" template T Demangler::DemangleQualifiedName() { auto info = Demangle(true); auto pkgName = info.GetPkgName(); auto clsName = info.GetIdentifier(); if (pkgName.IsEmpty()) { return clsName; } return pkgName + "." + clsName; } template DemangleInfo Demangler::Demangle(bool isType) { if (isType) { if (!IsQualifiedType()) { return Reject("invalid mangling name"); } return DemangleNextUnit("invalid mangling name"); } if (mangledName.IsEmpty()) { return Reject("invalid mangling name"); } mangledName = DeCompression(mangledName.SubStr(0)).CJMangledDeCompression(); if (IsGlobalInit()) { return DemangleGlobalInit(); } if (IsParamInit()) { return DemangleParamInit(); } // Lambda expressions like "$pkglambda.0" if (IsInnerFunction()) { return DemangleInnerFunction(); } // C func wrapper like "cfuncInCJ$real" if (IsCFunctionWrapper()) { return DemangleInfo{ mangledName.SubStr(0, mangledName.Length() - strlen(MANGLE_CFUNCTION_WRAPPER)), TypeKind::NAME, isValid }; } return DemangleDecl(); } /** * @brief Parse a string with a pattern of "" or "S" * and could be zero. */ template DemangleInfo Demangler::DemanglePackageName() { if (currentIndex >= mangledName.Length()) { (void)Reject(ERROR_EXCEED_THE_INPUT_STRING_LENGTH); return DemangleInfo{ "", TypeKind::COMMON_DECL }; } T pkg{}; if (MANGLE_STDPKG_MAP.find(mangledName.SubStr(currentIndex, SPECIAL_NAME_LEN).Str()) != MANGLE_STDPKG_MAP.end()) { pkg = MANGLE_STDPKG_MAP.at(mangledName.SubStr(currentIndex, SPECIAL_NAME_LEN).Str()); currentIndex += SPECIAL_NAME_LEN; } else if (!IsCurrentCharDigit(mangledName[currentIndex])) { (void)Reject("should be a number"); return DemangleInfo{ "", TypeKind::COMMON_DECL }; } // Take the first string as package name, including '0'. if (mangledName[currentIndex] == '0') { ++currentIndex; return DemangleInfo{ "", TypeKind::COMMON_DECL }; } DemangleInfo di{ "", TypeKind::COMMON_DECL }; if (pkg.IsEmpty()) { pkg = DemangleStringName(); auto pos = pkg.Find(':'); if (pos > -1 && pkg.Length() - pos > 0) { pkg = pkg.SubStr(0, pos) + T{':'} + pkg.SubStr(pos, pkg.Length() - pos); } } if (IsFileName()) { di.isPrivateDeclaration = true; DemangleFileName(); } di.pkgName = ReplaceString(pkg, MANGLE_MODULE_SPLIT, MODULE_SPLIT); return di; } /** * @brief Parse a string with a pattern of "", and must not be zero. * * @return T return the if the pattern is "" */ template T Demangler::DemangleStringName() { auto identifyLength = DemangleLength(); if (identifyLength == 0) { (void)Reject("previous number should be non-zero"); return T{}; } if (identifyLength > mangledName.Length() - currentIndex) { (void)Reject("previous number is greater than the string size"); return T{}; } // length + string, eg. "3foo" T name = mangledName.SubStr(currentIndex, identifyLength); currentIndex += identifyLength; return name; } /** * @brief Parse a string "ps/pg". * * @return T return the "set/get" if it is "ps/pg" */ template T Demangler::DemangleProp() { T prop; if (mangledName.SubStr(currentIndex, SPECIAL_NAME_LEN) == T{ MANGLE_SET_PREFIX }) { prop = T{ "::set" }; } else { prop = T{ "::get" }; } currentIndex += SPECIAL_NAME_LEN; return prop; } template void Demangler::SkipPrivateTopLevelDeclHash() { if (currentIndex >= mangledName.Length() || !IsCurrentCharDigit(mangledName[currentIndex]) || mangledName[currentIndex] == '0') { return; } size_t oldIdx = currentIndex; uint32_t hashLen = DemangleLength(); if (currentIndex + hashLen >= mangledName.Length() || mangledName[currentIndex] != MANGLE_FILE_NUMBER_END) { currentIndex = oldIdx; return; } const bool isBoxedDecl = static_cast(mangledName.Find(BOX_DECL_PREFIX, currentIndex)) == currentIndex && !mangledName.SubStr(currentIndex, hashLen).EndsWith(CJ_FILE_EXT); if (isBoxedDecl) { currentIndex = oldIdx; return; } currentIndex += hashLen; } template size_t Demangler::DemangleManglingNumber() { // Get the entire mangling number string. We must have the length of the number (i.e. how many digits it has) to // compute its value. Mangling number must end with an underscore. T content = ""; while (true) { char c; if (!GetChar(c)) { (void)Reject("mangling number has no termination symbol"); return 0; } // Base 62 number rules: {<0-9A-Za-z>}* _, and _ is the end of the rule. if (c == MANGLE_UNDERSCORE_PREFIX) { break; } else { content += T{c}; } } // _ represents 0. Otherwise, mangling number represents Hex + 1. For example, 0_ represents 1, A_ represents 11. if (content == "") { return 0; } // Iterate from the least significant digit to the most significant digit. size_t value = 0; size_t power = 1; for (size_t index = content.Length() - 1;; index--) { bool matched = false; for (size_t i = 0; i <= BASE62_CHARS_SIZE; i++) { if (content[index] == BASE62_CHARS[i]) { value += i * power; power *= BASE62_CHARS_SIZE; matched = true; break; } } if (!matched) { (void)Reject("mangling number has invalid character"); return 0; } if (index == 0) { break; } } return value + 1; } template void Demangler::DemangleFileNameNumber() { T content = ""; while (true) { char c; if (content.Length() == FILE_HASH_LEN) { break; } else if (!GetChar(c)) { (void)Reject("mangling number has no termination symbol"); return; } if (c == MANGLE_FILE_NUMBER_END) { return; } else { content += T{ c }; } } for (size_t index = content.Length() - 1;; index--) { bool matched = false; for (size_t i = 0; i <= BASE62_CHARS_SIZE; i++) { if (content[index] == BASE62_CHARS[i]) { matched = true; break; } } if (!matched) { (void)Reject("mangling number has invalid character"); return; } if (index == 0) { break; } } } template bool Demangler::IsFileName() const { char c; if (!PeekChar(c)) { return false; } if (c != MANGLE_FILE_ID_PREFIX) { return false; } return true; } template void Demangler::DemangleFileName() { SkipChar(MANGLE_FILE_ID_PREFIX); DemangleFileNameNumber(); } template void Demangler::SkipLocalCounter() { char c; if (!PeekChar(c)) { return; } if (c != MANGLE_COUNT_PREFIX) { return; } SkipChar(MANGLE_COUNT_PREFIX); (void)DemangleManglingNumber(); } /** * @brief Parse the following arguments. * * @tparam T * @param delimiter to concatenate all args * @param size the number of args to be parsed, default -1(all) * @return return empty string if no args get parsed or input is invalid */ template T Demangler::DemangleArgTypes(const T& delimiter, uint32_t size) { if (currentIndex == mangledName.Length() - 1 && mangledName[currentIndex] == MANGLE_VOID_PREFIX) { SkipChar(MANGLE_VOID_PREFIX); return T{}; } auto i = 0u; T result; bool hasTrailingDelimiter = false; // In order to not use dynamic memory, the array is used in this function. // It is supposed that result is long enough. std::array argsArr; while (IsNotEndOfMangledName() && i < size) { // Generic constraints are separated with '_' if (mangledName[currentIndex] == MANGLE_UNDERSCORE_PREFIX) { SkipChar(MANGLE_UNDERSCORE_PREFIX); } if (IsMaybeDefaultFuncParamFunction()) { break; } auto curDi = DemangleNextUnit("invalid argument type"); if (!isValid) { return T{}; } auto j = i % MAX_ARGS_SIZE; if (j == 0 && i != 0) { for (auto& arg : argsArr) { result += arg + delimiter; arg.Truncate(0); } hasTrailingDelimiter = true; } ++i; argsArr[j] = curDi.GetFullName(scopeResolution); } if (i == 0) { return result; } uint32_t remainingLength = ((i - 1) % MAX_ARGS_SIZE) + 1; if (remainingLength == 0 && hasTrailingDelimiter) { result.Truncate(result.Length() - delimiter.Length()); } result += GetArgTypesFullName(argsArr, remainingLength, delimiter); return result; } template DemangleInfo Demangler::DemangleNestedDecls(bool isClass, bool isParamInit) { auto delimiter = T{ scopeResolution }; auto i = 0u; T result; // In order to not use dynamic memory, the array is used in this function. std::array argsArr; auto typeKind = TypeKind::COMMON_DECL; DemangleInfo lastElement; while (IsNotEndOfMangledName()) { typeKind = TypeKind::COMMON_DECL; if (mangledName[currentIndex] == MANGLE_LAMBDA_PREFIX) { ++currentIndex; T lambdaIDString = UIntToString(DemangleManglingNumber()); auto j = i % MAX_ARGS_SIZE; ++i; // Lambda demangled name prefix "lambda.". argsArr[j] = T{"lambda."} + lambdaIDString; typeKind = TypeKind::LAMBDA_FUNCTION; continue; } if (mangledName[currentIndex] == MANGLE_EXTEND_PREFIX) { // Something I don't really understand. Copied from the current implementation anyway. ++currentIndex; if (!IsQualifiedType()) { return Reject("invalid mangling name"); } auto curDi = DemangleNextUnit("invalid mangling name"); auto j = i % MAX_ARGS_SIZE; ++i; argsArr[j] = curDi.GetIdentifier(); if (IsFileName()) { (void)DemangleFileName(); } SkipLocalCounter(); continue; } DemangleInfo curDi; if (IsOperatorName()) { curDi = DemangleInfo{ GetOperatorNameMap(mangledName.SubStr(currentIndex, SPECIAL_NAME_LEN)), TypeKind::NAME, isValid }; currentIndex += SPECIAL_NAME_LEN; } else { curDi = DemangleInfo{ DemangleStringName(), TypeKind::NAME, isValid }; if (IsProp()) { curDi.demangled += DemangleProp(); } } if (currentIndex >= mangledName.Length()) { auto j = i % MAX_ARGS_SIZE; ++i; argsArr[j] = curDi.GetFullName(scopeResolution); break; } SkipLocalCounter(); if (!isValid) { return Reject("mangle name is invalid"); } // Some nested decl has generic param like "_CN1A3fooIlE3barHl" // "_CN1A3fooIlEixHl" if (IsGeneric(mangledName[currentIndex])) { SkipChar(MANGLE_GENERIC_PREFIX); auto generic = DemangleGenericTypes().GetFullName(scopeResolution); curDi.genericTypes = genericTypefilter(generic); if (!isClass) { SkipOptionalChar(END); } } char ch; if (PeekChar(ch) && IsFunction(ch)) { SkipChar(MANGLE_FUNCTION_PREFIX); typeKind = TypeKind::FUNCTION_DECL; auto funcTys = DemangleFunctionParameterTypes().args; curDi.functionParameterTypes = funcTys; SkipOptionalChar(END); } auto j = i % MAX_ARGS_SIZE; if (j == 0 && i != 0) { for (auto& arg : argsArr) { result += arg + delimiter; arg.Truncate(0); } result.Truncate(result.Length() - delimiter.Length()); } ++i; if (isParamInit && IsNotEndOfMangledName() && curDi.functionParameterTypes.Length() > 0) { argsArr[j] = curDi.GetFullName(scopeResolution) + LEFT_BRACKET + curDi.functionParameterTypes + RIGHT_BRACKET; } else { argsArr[j] = curDi.GetFullName(scopeResolution); } lastElement = curDi; } result += GetArgTypesFullName(argsArr, i % (MAX_ARGS_SIZE + 1), delimiter); DemangleInfo resDi = { result, typeKind, isValid }; resDi.functionParameterTypes = lastElement.functionParameterTypes; return resDi; } template DemangleInfo Demangler::DemangleClass(TypeKind typeKind) { ++currentIndex; // Skip one character representing the typeKind auto di = DemangleCommonDecl(true); di.type = typeKind; char ch; if (PeekChar(ch) && ch == END) { SkipChar(END); } return di; } template DemangleInfo Demangler::DemangleCFuncType() { SkipString(MANGLE_C_FUNCTY_PREFIX); DemangleInfo di{ DemangleNextUnit().GetFullName(scopeResolution), TypeKind::FUNCTION }; di.args = DemangleArgTypes(ARGS_DELIMITER); SkipChar(END); return di; } template DemangleInfo Demangler::DemangleFunction() { SkipString(MANGLE_FUNCTY_PREFIX); DemangleInfo di{ DemangleNextUnit().GetFullName(scopeResolution), TypeKind::FUNCTION }; di.args = DemangleArgTypes(ARGS_DELIMITER); SkipChar(END); return di; } template DemangleInfo Demangler::DemangleTuple() { DemangleInfo di{ MANGLE_TUPLE_STR, TypeKind::TUPLE }; SkipChar(MANGLE_TUPLE_PREFIX); (void)DemangleManglingNumber(); // Demangle to get an arr, then call this func to extract std::array argsArr; auto generic = DemangleGenericTypes().GetFullName(scopeResolution); di.genericTypes = genericTypefilter(generic); SkipChar(END); return di; } template DemangleInfo Demangler::DemangleRawArray() { DemangleInfo di{ MANGLE_ARRAY_STR, TypeKind::RAW_ARRAY }; SkipChar(MANGLE_ARRAY_PREFIX); auto dim = DemangleManglingNumber(); while (dim--) { di.genericConstraints += "[]"; } di.args = DemangleNextUnit().GetFullName(scopeResolution); return di; } template DemangleInfo Demangler::DemangleVArray() { DemangleInfo di{ MANGLE_VARRAY_STR, TypeKind::TUPLE }; SkipChar(MANGLE_VARRAY_PREFIX); uint32_t num = DemangleManglingNumber(); di.genericTypes = DemangleGenericTypes().GetFullName(scopeResolution, num); return di; } template DemangleInfo Demangler::DemangleCPointer() { DemangleInfo di{ MANGLE_PTR_STR, TypeKind::CPOINTER }; SkipChar(MANGLE_PTR_PREFIX); auto generic = DemangleType().GetFullName(scopeResolution); di.genericTypes = genericTypefilter(generic); return di; } template DemangleInfo Demangler::DemangleCommonDecl(bool isClass) { bool isGlobalVariableInitPrefix = false; if (mangledName[currentIndex] == MANGLE_NESTED_PREFIX[0]) { SkipString(MANGLE_NESTED_PREFIX); } else { SkipString(GLOBAL_VARIABLE_INIT_PREFIX); isGlobalVariableInitPrefix = true; } if (!isValid) { return Reject(); } DemangleInfo di{ "", TypeKind::COMMON_DECL }; auto pkgDi = DemanglePackageName(); di.pkgName = pkgDi.pkgName; di.isPrivateDeclaration = pkgDi.isPrivateDeclaration; // Take the part before E as identifier and concatenate with scope resolution. DemangleInfo nestedDi = DemangleNestedDecls(isClass); di.demangled = nestedDi.demangled; di.type = nestedDi.type; di.args = nestedDi.args; if (!isValid || di.demangled.IsEmpty()) { return Reject(); } if (di.type == TypeKind::FUNCTION_DECL) { di.args = nestedDi.functionParameterTypes; } // The cjdb currently expects global variable initialization symbol is demangled as a // variable (without parentheses). Here is a special demangle handling for keeping that behaviour. if (isGlobalVariableInitPrefix) { di.type = TypeKind::COMMON_DECL; } return di; } template DemangleInfo Demangler::DemangleDecl() { SkipString(MANGLE_CANGJIE_PREFIX); if (!isValid) { return Reject(); } auto di = DemangleCommonDecl(); if (di.type == TypeKind::LAMBDA_FUNCTION) { return di; } // Invalid input and non-function decl ends here if (!isValid || currentIndex >= mangledName.Length()) { return di; } if (mangledName[currentIndex] == END) { return di; } // Collect function param-types di.type = TypeKind::FUNCTION_DECL; di.args = DemangleArgTypes(ARGS_DELIMITER); if (!isValid) { return Reject(); } if (IsMaybeDefaultFuncParamFunction()) { auto dpf = DemangleDefaultParamFunction(); if (!isValid) { return Reject(); } di.demangled = di.GetFullName(scopeResolution) + scopeResolution + dpf.demangled; di.args = dpf.args; } return di; } template DemangleInfo Demangler::DemanglePrimitive() { if (currentIndex >= mangledName.Length()) { return Reject(ERROR_EXCEED_THE_INPUT_STRING_LENGTH); } char ch = mangledName[currentIndex]; if (MatchForward(MANGLE_FLOAT16_PREFIX, strlen(MANGLE_FLOAT16_PREFIX))) { SkipString(MANGLE_FLOAT16_PREFIX); // skip "Dh" } else { SkipChar(ch); } DemangleInfo di{ GetPrimitiveDemangleMap(ch), TypeKind::PRIMITIVE }; return di; } template DemangleInfo Demangler::DemangleCStringType() { SkipChar(MANGLE_CSTRING_PREFIX); return DemangleInfo{ MANGLE_CSTRING_STR, TypeKind::CSTRING, isValid }; } template DemangleInfo Demangler::DemangleGenericType() { SkipChar(MANGLE_GTY_PREFIX); size_t gNumber = DemangleManglingNumber(); T name; #ifdef BUILD_LIB_CANGJIE_DEMANGLE if (genericVec.empty()) { name = T{ MANGLE_TUPLE_PREFIX } + UIntToString(gNumber); } else { if (gNumber < genericVec.size()) { name = T{ genericVec[gNumber].c_str() }; } else { isValid = false; } } #else name = T{ MANGLE_TUPLE_PREFIX } + UIntToString(gNumber); #endif return DemangleInfo{ name, TypeKind::GENERIC_TYPES, isValid }; } template DemangleInfo Demangler::DemangleType() { DemangleInfo di{ "", TypeKind::GENERIC_TYPES }; if (IsQualifiedType()) { auto curDi = DemangleNextUnit(); if (isValid) { di.args = curDi.GetFullName(scopeResolution); return di; } } isValid = false; return di; } template DemangleInfo Demangler::DemangleGenericTypes() { DemangleInfo di{ "", TypeKind::GENERIC_TYPES }; bool firstIteration = true; T argTypesFullName{}; // Generic type may be next to a string, like "6" while (IsNotEndOfMangledName() && !IsCurrentCharDigit(mangledName[currentIndex]) && !IsFunction(mangledName[currentIndex]) && !IsOperatorName()) { auto curDi = DemangleNextUnit(); if (!isValid) { break; } if (!firstIteration) { argTypesFullName += T{ ARGS_DELIMITER_TYPE }; } argTypesFullName += curDi.GetFullName(scopeResolution); firstIteration = false; } di.args = argTypesFullName; return di; } template DemangleInfo Demangler::DemangleFunctionParameterTypes() { DemangleInfo di{ "", TypeKind::FUNCTION_PARAMETER_TYPES }; bool firstIteration = true; T argTypesFullName{}; if (IsNotEndOfMangledName() && mangledName[currentIndex] == MANGLE_VOID_PREFIX) { currentIndex += MANGLE_CHAR_LEN; } else { // Generic type may be next to a string, like "6" while (IsNotEndOfMangledName() && !IsCurrentCharDigit(mangledName[currentIndex])) { auto curDi = DemangleNextUnit(); if (!isValid) { break; } if (!firstIteration) { argTypesFullName += T{ ARGS_DELIMITER }; } argTypesFullName += curDi.GetFullName(scopeResolution); firstIteration = false; } } di.args = argTypesFullName; return di; } template DemangleInfo Demangler::DemangleDefaultParamFunction() { auto pos = mangledName.Find(MANGLE_UNDERSCORE_PREFIX, currentIndex); if (pos == -1 || pos == static_cast(mangledName.Length()) - 1 || !IsCurrentCharDigit(mangledName[pos + MANGLE_CHAR_LEN])) { return Reject("invalid decl"); } SkipChar(MANGLE_DEFAULT_PARAM_FUNCTION_PREFIX); auto identifier = mangledName.SubStr(currentIndex, pos - currentIndex); DemangleInfo di{ identifier, TypeKind::FUNCTION_DECL }; SkipString(identifier.Str()); SkipChar(MANGLE_UNDERSCORE_PREFIX); // Skip the number of this param. (void)DemangleLength(); if (!IsNotEndOfMangledName()) { return Reject("invalid decl"); } di.args = DemangleArgTypes(ARGS_DELIMITER); return di; } /** * @brief Parse pattern like "$pkglambda.0" */ template DemangleInfo Demangler::DemangleInnerFunction() { DemangleInfo di{ "", TypeKind::LAMBDA_FUNCTION }; // `IsInnerFunction()` has made true the substring exist. auto prefixIndex = mangledName.Find(MANGLE_INNER_FUNCTION_PREFIX); if (!isValid) { return Reject(); } auto pkgName = mangledName.SubStr(currentIndex, prefixIndex - currentIndex); di.pkgName = pkgName; auto funcName = mangledName.SubStr(prefixIndex + MANGLE_CHAR_LEN); // Skip "$" di.demangled = funcName; currentIndex = mangledName.Length(); return di; } template DemangleInfo Demangler::Reject(const T& reason) { // Quick return from a failed state if (currentIndex == mangledName.Length() && !isValid) { return dumpDi; } ErrorLog(reason.Str()); isValid = false; currentIndex = mangledName.Length(); return dumpDi; } template DemangleInfo Demangler::DemangleNextUnit(const T& message) { auto di = DemangleByPrefix(); if (!isValid) { return Reject(T{ "Demangle next token error: " } + message); } return di; } template DemangleInfo Demangler::DemangleByPrefix() { if (currentIndex >= mangledName.Length()) { return Reject(ERROR_EXCEED_THE_INPUT_STRING_LENGTH); } char ch = mangledName[currentIndex]; switch (ch) { case 'C': return DemangleClass(TypeKind::CLASS); case 'R': return DemangleClass(TypeKind::STRUCT); case 'F': if (currentIndex + MANGLE_CHAR_LEN < mangledName.Length()) { // The F0 and FC is the function type prefix. if (mangledName[currentIndex + MANGLE_CHAR_LEN] == '0') { return DemangleFunction(); } else if (mangledName[currentIndex + MANGLE_CHAR_LEN] == 'C') { return DemangleCFuncType(); } else { break; } } else { break; } case 'N': return DemangleClass(TypeKind::ENUM); case 'T': return DemangleTuple(); case 'P': return DemangleCPointer(); case '_': break; case 'A': return DemangleRawArray(); case 'V': return DemangleVArray(); case 'k': return DemangleCStringType(); case 'G': return DemangleGenericType(); default: if (IsCurrentCharDigit(ch)) { return DemangleInfo{ DemangleStringName(), TypeKind::NAME, isValid }; } else if (IsPrimitive(ch)) { return DemanglePrimitive(); } else if (IsOperatorName()) { auto di = DemangleInfo{ GetOperatorNameMap(mangledName.SubStr(currentIndex, SPECIAL_NAME_LEN)), TypeKind::NAME, isValid }; currentIndex += SPECIAL_NAME_LEN; return di; } } return Reject("unexpected character"); } #ifdef BUILD_LIB_CANGJIE_DEMANGLE // to reuse the code to compile CangjieDemangle.cpp template struct DemangleInfo; template class Demangler; template class DeCompression; #else template struct DemangleInfo; template class Demangler; template class DeCompression; #endif } // namespace Cangjie cangjie_compiler-1.0.7/demangler/Demangler.h000066400000000000000000000222271510705540100210210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_DEMANGLER_H #define CANGJIE_DEMANGLER_H #include #ifndef BUILD_LIB_CANGJIE_DEMANGLE #include #include "Base/CString.h" #endif #include #include namespace Cangjie { enum class TypeKind { NAME, PRIMITIVE, CSTRING, CLASS, STRUCT, INTERFACE, ENUM, RAW_ARRAY, VARRAY, TUPLE, CPOINTER, FUNCTION, COMMON_DECL, FUNCTION_DECL, FUNCTION_PARAMETER_TYPES, LAMBDA_FUNCTION, GENERIC_TYPES, GENERIC_CONSTRAINTS, }; template struct DemangleInfo { /** * @brief The constructor of struct DemangleInfo. * * @param name The name to be demangled. * @param kind The demangled kind. * @param pIsValid Whether The demangled name is valid. * @return DemangleInfo The instance of DemangleInfo. */ DemangleInfo(const T& name, TypeKind kind, bool pIsValid) : type(kind), demangled(name), isValid(pIsValid) {} /** * @brief The constructor of struct DemangleInfo. * * @param name The name to be demangled. * @param kind The demangled kind. * @return DemangleInfo the instance of DemangleInfo. */ DemangleInfo(const T& name, TypeKind kind) : type(kind), demangled(name) {} /** * @brief Use the default copy constructor to initialize a new DemangleInfo object. * * @param copying The DemangleInfo object to be copied. * @return DemangleInfo The instance of DemangleInfo. */ DemangleInfo(const DemangleInfo& copying) = default; /** * @brief The default constructor of struct DemangleInfo. * * @return DemangleInfo The instance of DemangleInfo. */ DemangleInfo() = default; DemangleInfo& operator=(const DemangleInfo& copying) = default; /** * @brief The destructor of struct DemangleInfo. */ virtual ~DemangleInfo() = default; TypeKind type; T pkgName; T args; T genericConstraints; T genericTypes; T functionParameterTypes; T demangled; // As identifier or function return type bool isPrivateDeclaration { false }; /** * @brief Get full name. * * @param scopeRes The scope resolution. * @param argsNum The args number. * @return T The full name. */ virtual T GetFullName(const T& scopeRes, const uint32_t argsNum = 0) const; /** * @brief Get package name. * * @return T The package name. */ virtual T GetPkgName() const; /** * @brief Get arg types name. * * @param argsNum The args number. * @return T The arg types name. */ virtual T GetArgTypesName(const uint32_t argsNum = 0) const; /** * @brief Get demangled identifier. * * @return T The identifier. */ T GetIdentifier() const; /** * @brief Get return type. * * @return T The return type name. */ T GetReturnType() const; /** * @brief Check if it functions like a function. * * @return bool If yes, true is returned. Otherwise, false is returned. */ bool IsFunctionLike() const; /** * @brief Check if the demangled name is valid. * * @return bool If yes, true is returned. Otherwise, false is returned. */ bool IsValid() const; private: bool MatchSuffix(const char pattern[], uint32_t len) const; T GetGenericConstraints() const; T GetGenericTypes() const; T GetFunctionName() const; T GetGenericConstraintsName() const; bool isValid{ true }; }; template class Demangler { public: /** * @brief The constructor of class Demangler. * * @param mangled The name to be demangled. * @param stripUnderscore Whether the mangled name needs to strip underscore. * @param scopeRes The scope resolution. * @param genericParamFilter The function to filter generic param. * @return Demangler The instance of Demangler. */ explicit Demangler( const T& mangled, const bool stripUnderscore, const T& scopeRes, std::function genericParamFilter) : currentIndex(0), genericTypefilter(genericParamFilter), scopeResolution(scopeRes) { mangledName = mangled; // Skip '_' if (stripUnderscore && !mangled.IsEmpty() && mangled[0] == '_') { mangledName = mangledName.SubStr(1); } dumpDi = DemangleInfo{ mangledName, TypeKind::NAME, false }; } /** * @brief The constructor of class Demangler. * * @param mangled The name to be demangled. * @param scopeRes The scope resolution. * @param genericParamFilter The function to filter generic param. * @return Demangler The instance of Demangler. */ explicit Demangler( const T& mangled, const T& scopeRes = "::", std::function genericParamFilter = [](const T& str) { return str; }) : Demangler(mangled, false, scopeRes, genericParamFilter) { } /** * @brief Demangle the string. * * @param isType Whether the demangled name is type. * @return DemangleInfo The demangled information. */ DemangleInfo Demangle(bool isType = false); /** * @brief Get demangled class type name. * * @return T The class type name. */ T DemangleClassType(); /** * @brief Get demangled qualified name. * * @return T The qualified name. */ T DemangleQualifiedName(); /** * @brief Get scope resolution. * * @return T The scope resolution. */ T ScopeResolution() const { return scopeResolution; }; #ifdef BUILD_LIB_CANGJIE_DEMANGLE /** * @brief Set generic vector. * * @param vec The generic vector. */ void setGenericVec(const std::vector& vec) { genericVec = vec; } #endif private: DemangleInfo DemanglePackageName(); T DemangleStringName(); T DemangleProp(); size_t DemangleManglingNumber(); void DemangleFileNameNumber(); T UIntToString(size_t value) const; void SkipPrivateTopLevelDeclHash(); void DemangleFileName(); void SkipLocalCounter(); T DemangleArgTypes(const T& delimiter, uint32_t size = -1); DemangleInfo DemangleNestedDecls(bool isClass = false, bool isParamInit = false); uint32_t DemangleLength(); DemangleInfo Reject(const T& reason = T{}); DemangleInfo DemangleNextUnit(const T& message = T{}); DemangleInfo DemangleByPrefix(); DemangleInfo DemangleClass(TypeKind typeKind); DemangleInfo DemangleCFuncType(); DemangleInfo DemangleTuple(); DemangleInfo DemangleCommonDecl(bool isClass = false); DemangleInfo DemangleDecl(); DemangleInfo DemangleRawArray(); DemangleInfo DemangleVArray(); DemangleInfo DemangleCPointer(); DemangleInfo DemangleFunction(); DemangleInfo DemanglePrimitive(); DemangleInfo DemangleCStringType(); DemangleInfo DemangleGenericType(); DemangleInfo DemangleDefaultParamFunction(); DemangleInfo DemangleInnerFunction(); DemangleInfo DemangleType(); DemangleInfo DemangleGenericTypes(); DemangleInfo DemangleFunctionParameterTypes(); DemangleInfo DemangleGlobalInit(); DemangleInfo DemangleParamInit(); bool IsFileName() const; bool IsProp() const; bool IsInnerFunction() const; bool IsMaybeDefaultFuncParamFunction() const; bool IsOperatorName() const; bool IsGlobalInit() const; bool IsParamInit() const; bool IsCFunctionWrapper() const; bool IsQualifiedType() const; bool IsDecl() const; bool IsNotEndOfMangledName() const; bool MatchForward(const char pattern[], uint32_t len) const; /** * @brief Get the current character without incrementing `currentIndex`. * * @param ch The current character, only set when the function returns true. * @return false if `currentIndex` is out of bound, otherwise true. */ bool PeekChar(char& ch) const; /** * @brief Get the current character and increment `currentIndex` by one. * * @param ch The current character, only set when the function returns true. * @return false if `currentIndex` is out of bound, otherwise true. */ bool GetChar(char& ch); void SkipChar(char ch); void SkipOptionalChar(char ch); void SkipString(const char pattern[]); void ErrorLog(const char* msg) const; // Record false and reject parsing if the input mangled name is invalid bool isValid{ true }; // Record which kind is being parsed TypeKind curType{ TypeKind::NAME }; size_t currentIndex; // Filter specified content in generic parameters std::function genericTypefilter; T mangledName; T scopeResolution; DemangleInfo dumpDi; #ifdef BUILD_LIB_CANGJIE_DEMANGLE // Generic name vector. e.g. {"T", "K", "U"} std::vector genericVec; #endif }; } // namespace Cangjie #endif // CANGJIE_DEMANGLER_H cangjie_compiler-1.0.7/demangler/StdString.h000066400000000000000000000116521510705540100210440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_DEMANGLER_STD_STRING_H #define CANGJIE_DEMANGLER_STD_STRING_H #include namespace Cangjie { /** * @brief This is a std::string proxy type. * Since the Demangler and DemanglerInfo template classes are based on the CString type we created in the Base library, * we need to maintain API consistency with the CString class. The following methods must all be implemented. This type * also helps remove the dependency on CString and securec (an indirect dependency) of the cangjie-demangle library that * we export to other users. */ class StdString : public std::string { public: /** * @brief The constructor of class StdString with none. * * @return StdString The instance of StdString. */ StdString() : std::string() {} /** * @brief The constructor of class StdString with character. * * @param c The character. * @return StdString The instance of StdString. */ StdString(char c) : std::string(1, c) {} /** * @brief The constructor of class StdString with char*. * * @param initStr The pointer to character. * @return StdString The instance of StdString. */ StdString(const char* initStr) : std::string(initStr) {} /** * @brief The constructor of class StdString with std::string. * * @param other The string. * @return StdString The instance of StdString. */ StdString(const std::string& other) : std::string(other) {} /** * @brief The constructor of class StdString with StdString. * * @param other The StdString. * @return StdString The instance of StdString. */ StdString(const StdString& other) : std::string(other.Str()) {} /** * @brief This function ensures that StdString self-assignment. * * @param other The StdString. * @return StdString The new instance of StdString. */ StdString& operator=(const StdString& other) { std::string::operator=(other.Str()); return *this; } /** * @brief Get the length of StdString object. * * @return size_t The length. */ size_t Length() const { return this->size(); } /** * @brief Get the "char *" format of StdString object. * * @return char* The string. */ const char* Str() const noexcept { return this->c_str(); } /** * @brief Determine if StdString object is empty. * * @return bool Return true if the StdString object is empty, Otherwise, false is returned. */ bool IsEmpty() const { return this->empty(); } /** * @brief Search for the first occurrence of the specified pattern in the string starting from the given position. * * @param pattern The specified pattern. * @param begin The string starting position. * @return int Return the index of the first match, or -1 if no match is found. */ int Find(const char* pattern, size_t begin = 0) const { return this->find(pattern, begin); } /** * @brief Search for the first occurrence of the specified character in the string starting from * the given position. * * @param pattern The specified character. * @param begin The string starting position. * @return int Return the index of the first match, or -1 if no match is found. */ int Find(const char pattern, size_t begin = 0) const { return this->find(pattern, begin); } /** * @brief Return a substring starting from the specified index. * * @param begin The string starting position. * @return StdString The substring. */ StdString SubStr(size_t index) const { return this->substr(index); } /** * @brief Return a substring starting from the specified index and with the specified length. * * @param begin The string starting position. * @param len The substring length. * @return StdString The substring. */ StdString SubStr(size_t index, size_t len) const { return this->substr(index, len); } /** * @brief Check whether the string ends with the specified suffix. * * @param suffix The specified suffix. * @return bool Return true if the string ends with the specified suffix, Otherwise, false is returned. */ bool EndsWith(const StdString& suffix) const { return size() >= suffix.size() && substr(size() - suffix.size()) == suffix; } /** * @brief Truncate the string to the specified index. * * @param index The truncate position. * @return StdString The truncated string. */ StdString& Truncate(size_t index) { this->resize(index); return *this; } }; } // namespace Cangjie #endif // CANGJIE_DEMANGLER_STD_STRING_Hcangjie_compiler-1.0.7/demangler/Utils.h000066400000000000000000000074541510705540100202300ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_DEMANGLER_UTILS_H #define CANGJIE_DEMANGLER_UTILS_H #include #include namespace Cangjie { const char MANGLE_CANGJIE_PREFIX[] = "_C"; const char MANGLE_NESTED_PREFIX[] = "N"; constexpr char MANGLE_GENERIC_PREFIX = 'I'; constexpr char MANGLE_FUNCTION_PREFIX = 'H'; constexpr char MANGLE_COUNT_PREFIX = 'K'; constexpr char MANGLE_FILE_ID_PREFIX = 'U'; constexpr char MANGLE_EXTEND_PREFIX = 'X'; constexpr char MANGLE_LAMBDA_PREFIX = 'L'; constexpr char MANGLE_UNDERSCORE_PREFIX = '_'; constexpr char END = 'E'; constexpr char MANGLE_FILE_NUMBER_END = '$'; constexpr size_t FILE_HASH_LEN = 13; constexpr size_t SPECIAL_NAME_LEN = 2; constexpr size_t MANGLE_CHAR_LEN = 1; struct StdPkgCompare { bool operator()(const char* pkg1, const char* pkg2) const { return strcmp(pkg1, pkg2) == 0; } }; struct StdPkgHash { size_t operator()(const char* pkg) const { std::string cPkg = pkg; size_t hash = 1013; size_t offset = 5; size_t id = 0; const size_t n = cPkg.size(); while (id < n) { hash = ((hash << offset) + hash) + cPkg[id++]; } return hash & (0x7FFFFFFF); } }; enum class OperatorKind : size_t { AN = 13, CL = 75, DV = 117, EO = 142, EQ = 144, GE = 196, GT = 211, IX = 279, LE = 356, LS = 370, LT = 371, ML = 395, NE = 420, NG = 422, NT = 435, OR = 465, PL = 491, PW = 502, RM = 556, RS = 562 }; const std::unordered_map MANGLE_STDPKG_MAP = { {"ab", "std.sync"}, {"ac", "std.collection"}, {"ad", "std.deriving"}, {"ae", "std.database"}, {"af", "std.net"}, {"ag", "std.chirad"}, {"ah", "std.io"}, {"ai", "std.convert"}, {"aj", "std.ffi"}, {"ak", "std.argopt"}, {"al", "std.objectpool"}, {"am", "std.reflect"}, {"an", "std.fs"}, {"ao", "std.runtime"}, {"ap", "std.overflow"}, {"aq", "std.unittest"}, {"ar", "std.binary"}, {"as", "std.unicode"}, {"at", "std.core"}, {"au", "std.console"}, {"av", "std.random"}, {"aw", "std.sort"}, {"ax", "std.ast"}, {"ay", "std.env"}, {"ba", "std.process"}, {"bb", "std.time"}, {"bc", "std.regex"}, {"bd", "std.posix"}, {"be", "std.crypto"}, {"bf", "std.ref"}, {"bg", "std.math"}, {"bh", "std.collection.concurrent"}, {"bi", "std.collection.concurrent.native"}, {"bj", "std.deriving.impl"}, {"bk", "std.deriving.builtins"}, {"bl", "std.deriving.api"}, {"bm", "std.deriving.resolve"}, {"bn", "std.database.sql"}, {"bo", "std.net.native"}, {"bq", "std.convert.native"}, {"bs", "std.objectpool.native"}, {"bt", "std.fs.native"}, {"bu", "std.runtime.native"}, {"bv", "std.unittest.mock"}, {"bw", "std.unittest.testmacro"}, {"bx", "std.unittest.prop_test"}, {"by", "std.unittest.common"}, {"ca", "std.unittest.native"}, {"cb", "std.unittest.diff"}, {"cc", "std.unittest.mock.mockmacro"}, {"cd", "std.unittest.mock.internal"}, {"ce", "std.core.native"}, {"cf", "std.random.native"}, {"cg", "std.ast.native"}, {"ch", "std.env.native"}, {"ci", "std.process.native"}, {"cj", "std.time.native"}, {"ck", "std.regex.native"}, {"cl", "std.posix.native"}, {"cm", "std.crypto.digest"}, {"cn", "std.crypto.cipher"}, {"co", "std.math.numeric"}, {"cp", "std.math.native"}, {"cq", "std.math.numeric.native"} }; } // namespace Cangjie #endif // CANGJIE_DEMANGLER_UTILS_Hcangjie_compiler-1.0.7/doc/000077500000000000000000000000001510705540100155545ustar00rootroot00000000000000cangjie_compiler-1.0.7/doc/COPYRIGHT000066400000000000000000000001061510705540100170440ustar00rootroot00000000000000版权所有 (c) 华为技术有限公司 2025。保留一切权利。cangjie_compiler-1.0.7/doc/COPYRIGHT_EN000066400000000000000000000001061510705540100174260ustar00rootroot00000000000000Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.cangjie_compiler-1.0.7/doc/LICENSE000066400000000000000000000443321510705540100165670ustar00rootroot00000000000000Attribution 4.0 International ======================================================================= Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an "as-is" basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC- licensed material, or material used under an exception or limitation to copyright. More considerations for licensors: wiki.creativecommons.org/Considerations_for_licensors Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor's permission is not necessary for any reason--for example, because of any applicable exception or limitation to copyright--then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More_considerations for the public: wiki.creativecommons.org/Considerations_for_licensees ======================================================================= Creative Commons Attribution 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 -- Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. h. Licensor means the individual(s) or entity(ies) granting rights under this Public License. i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 -- Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or in part; and b. produce, reproduce, and Share Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. 5. Downstream recipients. a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. b. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. Section 3 -- License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material (including in modified form), You must: a. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. Section 4 -- Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 -- Disclaimer of Warranties and Limitation of Liability. a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 -- Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 -- Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 -- Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. ======================================================================= Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” The text of the Creative Commons public licenses is dedicated to the public domain under the CC0 Public Domain Dedication. Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. cangjie_compiler-1.0.7/doc/Standalone_Build_Guide.md000066400000000000000000000437701510705540100224350ustar00rootroot00000000000000# 独立构建指导书 ## 规格 当前前端编译器构建支持: 1. [Linux 上构建 Linux 平台运行的编译器](#linux-上构建-linux-平台运行的编译器) 2. [MacOS 上构建 MacOS 平台运行的编译器](#macos-上构建-macos-平台运行的编译器) 3. [Linux 上构建 Windows 平台运行的编译器(交叉编译)](#linux-上构建-windows-平台运行的编译器交叉编译) 4. [Linux 上构建适用于 Android 平台的基础库(交叉编译)](#linux-上构建适用于-android-平台的基础库交叉编译) 5. [MacOS 上构建适用于 Android 平台的基础库(交叉编译)](#macos-上构建适用于-android-平台的基础库交叉编译) 6. [MacOS 上构建适用于 iOS 平台的基础库(交叉编译)](#macos-上构建适用于-ios-平台的基础库交叉编译) ## Linux 上构建 Linux 平台运行的编译器 ### 环境依赖 编译器独立构建环境除额外依赖 googletest 执行 UT 外,其他内容与集成构建环境基本一致,详细信息请参阅 [Cangjie 构建指导书 (Ubuntu 22.04)-环境准备](https://gitcode.com/Cangjie/cangjie_build/blob/main/docs/linux_zh.md#2-%E7%8E%AF%E5%A2%83%E5%87%86%E5%A4%87)。 googletest 依赖安装可参考 [通用构建指导](https://github.com/google/googletest/blob/main/googletest/README.md),也可以在构建时通过 [--no-test](#build-选项) 选项临时关闭 UT 构建。 ### 构建命令 下载源码: > **注意:** > > 请确保编译平台能够正常连接网络并且正常访问 Gitcode 或 Gitee 等代码托管平台。 ```shell export WORKSPACE=$HOME/cangjie_build; git clone https://gitcode.com/Cangjie/cangjie_compiler.git -b main; ``` 对源码进行编译: ```shell cd $WORKSPACE/cangjie_compiler; export CMAKE_PREFIX_PATH=/opt/buildtools/libedit-3.1:/opt/buildtools/ncurses-6.3/usr; python3 build.py clean; python3 build.py build -t release --build-cjdb; python3 build.py install; ``` 1. `build.py clean` 命令用于清空工作区临时文件。 2. `build.py build` 命令开始执行编译: - 二级选项 `-t` 即 `--build-type`,指定编译产物类型,可以是 `release`、`debug` 或 `relwithdebinfo`; - 二级选项 `--build-cjdb` 选项开启 cjdb(lldb) 编译,了解更多更多关于 cjdb 内容,请参阅 [`cjdb` 工具介绍](https://gitcode.com/Cangjie/cangjie_docs/blob/main/docs/tools/source_zh_cn/cmd-tools/cjdb_manual.md)。 3. `build.py install` 命令将编译产物安装到 `output` 目录下。 验证产物: ```shell source ./output/envsetup.sh cjc -v ``` 输出如下: ```text Cangjie Compiler: x.xx.xx (cjnative) Target: xxxx-xxxx-xxxx ``` ## MacOS 上构建 MacOS 平台运行的编译器 ### 环境准备 编译器独立构建环境除额外依赖 googletest 执行 UT 外,其他内容与集成构建环境基本一致,详细信息请参阅 [Cangjie 构建指导书 (Macos 14 Sonoma)-环境准备](https://gitcode.com/Cangjie/cangjie_build/blob/main/docs/macos_zh.md#2-%E7%8E%AF%E5%A2%83%E5%87%86%E5%A4%87)。 googletest 依赖安装可参考 [通用构建指导](https://github.com/google/googletest/blob/main/googletest/README.md),也可以在构建时通过 [--no-test](#build-选项) 选项临时关闭 UT 构建。 ### 构建命令 下载源码: > **注意:** > > 请确保编译平台能够正常连接网络并且正常访问 Gitcode 或 Gitee 等代码托管平台。 ```shell export WORKSPACE=$HOME/cangjie_build; git clone https://gitcode.com/Cangjie/cangjie_compiler.git -b main; ``` 对源码进行编译: ```shell cd $WORKSPACE/cangjie_compiler; python3 build.py clean; python3 build.py build -t release --build-cjdb; python3 build.py install; ``` 1. `build.py clean` 命令用于清空工作区临时文件。 2. `build.py build` 命令开始执行编译: - 二级选项 `-t` 即 `--build-type`,指定编译产物类型,可以是 `release`、`debug` 或 `relwithdebinfo`; - 二级选项 `--build-cjdb` 选项开启 cjdb(lldb) 编译,了解更多更多关于 cjdb 内容,请参阅 [`cjdb` 工具介绍](https://gitcode.com/Cangjie/cangjie_docs/blob/main/docs/tools/source_zh_cn/cmd-tools/cjdb_manual.md)。 3. `build.py install` 命令将编译产物安装到 `output` 目录下。 验证产物: ```shell source ./output/envsetup.sh cjc -v ``` 输出如下: ```text Cangjie Compiler: x.xx.xx (cjnative) Target: xxxx-xxxx-xxxx ``` ## Linux 上构建 Windows 平台运行的编译器(交叉编译) ### 环境准备 编译器独立构建环境除额外依赖 googletest 执行 UT 外,其他内容与集成构建环境基本一致,详细信息请参阅 [Cangjie 构建指导书 (Ubuntu 22.04)-环境准备](https://gitcode.com/Cangjie/cangjie_build/blob/main/docs/linux_cross_windows_zh.md#2-%E7%8E%AF%E5%A2%83%E5%87%86%E5%A4%87)。 googletest 依赖安装可参考 [通用构建指导](https://github.com/google/googletest/blob/main/googletest/README.md),也可以在构建时通过 [--no-test](#build-选项) 选项临时关闭 UT 构建。 > **注意:** > > 请确保编译平台能够正常连接网络并且正常访问 Gitcode 或 Gitee 等代码托管平台。 ### 构建命令 下载源码: ```shell export WORKSPACE=$HOME/cangjie_build; git clone https://gitcode.com/Cangjie/cangjie_compiler.git -b main; ``` 源码构建: ```shell cd $WORKSPACE/cangjie_compiler; export CMAKE_PREFIX_PATH=${MINGW_PATH}/x86_64-w64-mingw32; python3 build.py build -t release \ --product cjc \ --target windows-x86_64 \ --target-sysroot /opt/buildtools/mingw-w64/ \ --target-toolchain /opt/buildtools/mingw-w64/bin \ --build-cjdb; python3 build.py install --host windows-x86_64; ``` 1. `CMAKE_PREFIX_PATH` 环境变量用来指定 cmake 用于将产物生成到目标平台对应的文件夹中。 2. `build.py clean` 命令用于清空工作区临时文件。 3. `build.py build` 命令开始执行编译: - 二级选项 `-t` 即 `--build-type`,可以是 `release`、`debug` 或 `relwithdebinfo`。 - 二级选项 `--product` 指定构建目标产物, 可以是 `all`、`cjc` 或 `libs`。 - 二级选项 `--target` 选项指定目标平台描述,可以是 `native`(当前编译平台)、`windows-x86_64`、`ohos-aarch64`、`ohos-x86_64`、`ios-aarch64`、`android-aarch64`、`android-x86_64`、`ios-simulator-aarch64`、`ios-aarch64`。 - 二级选项 `--target-sysroot` 选项将后面的参数传递给 C/C++ 编译器作为其 `--sysroot` 参数。 - 二级选项 `--target-toolchain` 选项指定目标平台工具链路径,使用该路径下的编译器进行交叉编译。 - 二级选项 `--build-cjdb` 选项开启 cjdb(lldb) 编译,了解更多更多关于 cjdb 内容,请参阅 [`cjdb` 工具介绍](https://gitcode.com/Cangjie/cangjie_docs/blob/main/docs/tools/source_zh_cn/cmd-tools/cjdb_manual.md)。 4. `build.py install` 命令将编译产物安装到 `output` 目录下: - 二级选项 `--host` 选项指定目标平台安装策略,可以是 `native`(当前编译平台)、`windows-x86_64`、`ohos-aarch64`、`ohos-x86_64`、`android-aarch64`、`android-x86_64`、`ios-simulator-aarch64`、`ios-aarch64`。 验证产物: 由于编译产物为 Windows 平台可执行文件,需要将产物拷贝至 Windows,并使用 ./output/envsetup.bat 脚本应用 cjc 环境。 ```bash source ./output/envsetup.bat cjc.exe -v ``` 该步骤仅生成目标平台 cjc 可执行文件,如需构建周边依赖请参阅 [Cangjie 构建指导书 (Ubuntu 22.04)-源码构建](https://gitcode.com/Cangjie/cangjie_build/blob/main/docs/linux_cross_windows_zh.md#4-%E7%BC%96%E8%AF%91%E6%B5%81%E7%A8%8B)。 ## Linux 上构建适用于 Android 平台的基础库(交叉编译) ### 环境准备 编译器独立构建环境除额外依赖 googletest 执行 UT 外,其他内容与集成构建环境基本一致,详细信息请参阅 Cangjie 构建指导书 (Ubuntu 22.04)-环境准备。 googletest 依赖安装可参考 [通用构建指导](https://github.com/google/googletest/blob/main/googletest/README.md),也可以在构建时通过 [--no-test](#build-选项) 选项临时关闭 UT 构建。 > **注意:** > > 请确保编译平台能够正常连接网络并且正常访问 Gitcode 或 Gitee 等代码托管平台。 ### 构建命令 下载源码: ```shell export WORKSPACE=$HOME/cangjie_build; git clone https://gitcode.com/Cangjie/cangjie_compiler.git -b main; ``` 源码构建: ```shell cd $WORKSPACE/cangjie_compiler; export ANDROID_NDK_ROOT=/opt/Android-NDK-r25c/AndroidNDK9519653.app/Contents/NDK; python3 build.py build -t release --build-cjdb --no-tests; python3 build.py build -t release \ --target android-aarch64 \ --android-ndk ${ANDROID_NDK_ROOT} \ --no-tests; python3 build.py install --host android-aarch64; ``` 1. `ANDROID_NDK_ROOT` 环境变量用来设置 Android NDK 的路径。 2. `build.py clean` 命令用于清空工作区临时文件。 3. `build.py build` 命令开始执行编译: - 二级选项 `-t` 即 `--build-type`,可以是 `release`、`debug` 或 `relwithdebuginfo`。 - 二级选项 `--product` 指定构建目标产物, 可以是 `all`、`cjc` 或 `libs`。 - 二级选项 `--target` 选项指定目标平台描述,可以是 `native`(当前编译平台)、`windows-x86_64`、`ohos-aarch64`、`ohos-x86_64`、`ios-aarch64`、`android-aarch64`、`android-x86_64`、`ios-simulator-aarch64`、`ios-aarch64`。 - 二级选项 `--android-ndk` 选项指定 Android NDK 的路径。 - 二级选项 `--build-cjdb` 选项开启 cjdb(lldb) 编译,了解更多更多关于 cjdb 内容,请参阅 [`cjdb` 工具介绍](https://gitcode.com/Cangjie/cangjie_docs/blob/main/docs/tools/source_zh_cn/cmd-tools/cjdb_manual.md)。 4. `build.py install` 命令将编译产物安装到 `output` 目录下: - 二级选项 `--host` 选项指定目标平台安装策略,可以是 `native`(当前编译平台)、`windows-x86_64`、`ohos-aarch64`、`ohos-x86_64`、`android-aarch64`、`android-x86_64`、`ios-simulator-aarch64`、`ios-aarch64`。 ## MacOS 上构建适用于 Android 平台的基础库(交叉编译) ### 环境准备 编译器独立构建环境除额外依赖 googletest 执行 UT 外,其他内容与集成构建环境基本一致,详细信息请参阅 Cangjie 构建指导书 (Macos 14 Sonoma)-环境准备。 googletest 依赖安装可参考 [通用构建指导](https://github.com/google/googletest/blob/main/googletest/README.md),也可以在构建时通过 [--no-test](#build-选项) 选项临时关闭 UT 构建。 ### 构建命令 下载源码: > **注意:** > > 请确保编译平台能够正常连接网络并且正常访问 Gitcode 或 Gitee 等代码托管平台。 ```shell export WORKSPACE=$HOME/cangjie_build; git clone https://gitcode.com/Cangjie/cangjie_compiler.git -b main; ``` 源码构建: ```shell cd $WORKSPACE/cangjie_compiler; export ANDROID_NDK_ROOT=/opt/Android-NDK-r25c/AndroidNDK9519653.app/Contents/NDK python3 build.py build -t release --build-cjdb --no-tests; python3 build.py build -t release \ --target android-aarch64 \ --android-ndk ${ANDROID_NDK_ROOT} \ --no-tests; python3 build.py install --host android-aarch64 ``` 1. `ANDROID_NDK_ROOT` 环境变量用来设置 Android NDK 的路径。 2. `build.py clean` 命令用于清空工作区临时文件。 3. `build.py build` 命令开始执行编译: - 二级选项 `-t` 即 `--build-type`,可以是 `release`、`debug` 或 `relwithdebuginfo`。 - 二级选项 `--product` 指定构建目标产物, 可以是 `all`、`cjc` 或 `libs`。 - 二级选项 `--target` 选项指定目标平台描述,可以是 `native`(当前编译平台)、`windows-x86_64`、`ohos-aarch64`、`ohos-x86_64`、`ios-aarch64`、`android-aarch64`、`android-x86_64`、`ios-simulator-aarch64`、`ios-aarch64`。 - 二级选项 `--android-ndk` 选项指定 Android NDK 的路径。 - 二级选项 `--build-cjdb` 选项开启 cjdb(lldb) 编译,了解更多更多关于 cjdb 内容,请参阅 [`cjdb` 工具介绍](https://gitcode.com/Cangjie/cangjie_docs/blob/main/docs/tools/source_zh_cn/cmd-tools/cjdb_manual.md)。 4. `build.py install` 命令将编译产物安装到 `output` 目录下: - 二级选项 `--host` 选项指定目标平台安装策略,可以是 `native`(当前编译平台)、`windows-x86_64`、`ohos-aarch64`、`ohos-x86_64`、`android-aarch64`、`android-x86_64`、`ios-simulator-aarch64`、`ios-aarch64`。 ## MacOS 上构建适用于 iOS 平台的基础库(交叉编译) ### 环境准备 编译器独立构建环境除额外依赖 googletest 执行 UT 外,其他内容与集成构建环境基本一致,详细信息请参阅 Cangjie 构建指导书 (Macos 14 Sonoma)-环境准备。 googletest 依赖安装可参考 [通用构建指导](https://github.com/google/googletest/blob/main/googletest/README.md),也可以在构建时通过 [--no-test](#build-选项) 选项临时关闭 UT 构建。 ### 构建命令 下载源码: > **注意:** > > 请确保编译平台能够正常连接网络并且正常访问 Gitcode 或 Gitee 等代码托管平台。 ```shell export WORKSPACE=$HOME/cangjie_build; git clone https://gitcode.com/Cangjie/cangjie_compiler.git -b main; ``` 源码构建: ```shell # 编译 mac aarch64 host cd ${WORKSPACE}/cangjie_compiler; python3 build.py clean; python3 build.py build -t release --no-tests --build-cjdb; python3 build.py install; # 编译 mac ios libs export XCODE_DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer export IOS_SDKROOT=${XCODE_DEVELOPER_DIR}/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.5.sdk export IOS_SIMULATOR_SDKROOT=${XCODE_DEVELOPER_DIR}/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator17.5.sdk export TOOLCHAIN_BIN=${XCODE_DEVELOPER_DIR}/Toolchains/XcodeDefault.xctoolchain/usr/bin python3 build.py build -t release \ --product libs \ --target ios-aarch64 \ --target-sysroot ${IOS_SDKROOT} \ --target-toolchain ${TOOLCHAIN_BIN}; python3 build.py install; # 编译 mac ios simulator libs ​python3 build.py build -t release \ --product libs \ --target ios-simulator-aarch64 \ --target-sysroot ${IOS_SIMULATOR_SDKROOT} \ --target-toolchain ${TOOLCHAIN_BIN}; python3 build.py install; ``` 1. `XCODE_DEVELOPER_DIR` 环境变量用来设置 Xcode 的开发目录。 2. `IOS_SDKROOT` 环境变量用来设置 iPhoneOS 的 SDK 路径。 3. `IOS_SIMULATOR_SDKROOT` 环境变量用来设置 iOS 模拟器的 SDK 路径。 4. `TOOLCHAIN_BIN` 环境变量用来设置 Xcode 工具链的二进制文件路径。 5. `build.py clean` 命令用于清空工作区临时文件。 6. `build.py build` 命令开始执行编译: - 二级选项 `-t` 即 `--build-type`,可以是 `release`、`debug` 或 `relwithdebuginfo`。 - 二级选项 `--product` 指定构建目标产物, 可以是 `all`、`cjc` 或 `libs`。 - 二级选项 `--target` 选项指定目标平台描述,可以是 `native`(当前编译平台)、`windows-x86_64`、`ohos-aarch64`、`ohos-x86_64`、`ios-aarch64`、`android-aarch64`、`android-x86_64`、`ios-simulator-aarch64`、`ios-aarch64`。 - 二级选项 `--target-sysroot` 选项将后面的参数传递给 C/C++ 编译器作为其 `--sysroot` 参数。 - 二级选项 `--target-toolchain` 选项指定目标平台工具链路径,使用该路径下的编译器进行交叉编译。 - 二级选项 `--build-cjdb` 选项开启 cjdb(lldb) 编译,了解更多更多关于 cjdb 内容,请参阅 [`cjdb` 工具介绍](https://gitcode.com/Cangjie/cangjie_docs/blob/main/docs/tools/source_zh_cn/cmd-tools/cjdb_manual.md)。 7. `build.py install` 命令将编译产物安装到 `output` 目录下: - 二级选项 `--host` 选项指定目标平台安装策略,可以是 `native`(当前编译平台)、`windows-x86_64`、`ohos-aarch64`、`ohos-x86_64`、`ios-simulator-aarch64`、`ios-aarch64`。 ## build.py 选项帮助 ### `clean` 选项 `clean` 选项用于清理 build/output 等文件夹。 ### `build` 选项 `build` 选项用于构建工程文件。它提供了如下二级选项: - `-h, --help`:用于展示二级选项的帮助信息。 - `-t, --build-type`:用于指定编译产物类型,可以是 `release`、`debug` 或 `relwithdebinfo`。 - `--print-cmd`:用于展示构建脚本配置的完整 cmake 命令。 - `-j, --jobs JOBS`:并发执行构建任务数。 - `--link-jobs LINK_JOBS`:并发执行链接任务数。 - `--enable-assert`:使能编译器断言,开发调试编译器使用。 - `--no-tests`:不编译 unittest 用例代码。 - `--disable-stack-grow-feature`:关闭栈增长功能。 - `--hwasan`:开启编译器源码硬件 asan 功能,由于其依赖 hwasan 工具,目前仅支持 ohos 平台开启。 - `--gcc-toolchain`:指定 gcc 工具链,它用于交叉编译。 - `--target`:选项指定目标平台描述,可以是 `native`(当前编译平台)、`windows-x86_64`、`ohos-aarch64`、`ohos-x86_64`、`ios-simulator-aarch64`、`ios-aarch64`、`android-aarch64`、`android-x86_64`。 - `-L, --target-lib`:指定目标平台所需链接的依赖库路径。 - `--target-toolchain`:指定编译工具所在的路径。 - `-I, --include` 指定目标平台头文件查找路径。 - `--target-sysroot`:传递 sysroot 内容到 C/C++ 编译器的 sysroot 选项。 - `--product {all,cjc,libs}`:指定构建目标产物,可以是`all`(默认值,指定编译包含 `cjc` 和 `libs` 内容)、`cjc`(编译器二进制文件)、`libs`(标准库依赖的编译器库)。 - `--build-cjdb`:开启构建仓颉调试器。 ### `install` 选项 `install` 选项用于将编译产物组织到指定目录下。它提供了如下二级选项: - `-h, --help`:用于展示二级选项的帮助信息。 - `--host`:指定目标平台安装策略,可以是 `native`(当前编译平台)、`windows-x86_64`、`ohos-aarch64`、`ohos-x86_64`、`ios-simulator-aarch64`、`ios-aarch64`、`android-aarch64`、`android-x86_64`。 - `--prefix`:指定产物安装文件夹路径,该选项和 `--host` 均未指定时,安装到工程目录下 output 文件夹,与 `--host` 同时指定时,安装到 `--prefix` 指定路径。 ### `test` 选项 `test` 选项用于执行编译好的 unittest 用例,如果编译时指定 `--no-test` 时无作用。 cangjie_compiler-1.0.7/doc/Standalone_Build_Guide_EN.md000066400000000000000000000504021510705540100230050ustar00rootroot00000000000000# Standalone Build Guide ## Specifications The current frontend compiler build supports: 1. [Building a Linux platform compiler on Linux](#building-a-linux-platform-compiler-on-linux) 2. [Building a macOS platform compiler on macOS](#building-a-macos-platform-compiler-on-macos) 3. [Building a Windows platform compiler on Linux (cross-compilation)](#building-a-windows-platform-compiler-on-linux-cross-compilation) 4. [Building base libraries of the Android platform on Linux (cross-compilation)](#building-base-libraries-of-the-android-platform-on-linux-cross-compilation) 5. [Building base libraries of the Android platform on macOS (cross-compilation)](#building-base-libraries-of-the-android-platform-on-macos-cross-compilation) 6. [Building base libraries of the iOS platform on macOS (cross-compilation)](#building-base-libraries-of-the-ios-platform-on-macos-cross-compilation) ## Building a Linux Platform Compiler on Linux ### Environment Dependencies The standalone compiler build environment is largely consistent with the integrated build environment, except for the additional dependency on googletest for executing UTs. For detailed information, please refer to [Cangjie Build Guide (Ubuntu 22.04) - Environment Preparation]. For googletest dependency installation, refer to [General Build Guide](https://github.com/google/googletest/blob/main/googletest/README.md). Alternatively, you can temporarily disable UT builds during compilation using the [`--no-test`](#build-options) option. ### Build Commands Download the source code: > **Note:** > > Ensure the compilation platform has normal network connectivity and can access code hosting platforms like Gitcode or Gitee. ```shell export WORKSPACE=$HOME/cangjie_build; git clone https://gitcode.com/Cangjie/cangjie_compiler.git -b main; ``` Compile the source code: ```shell cd $WORKSPACE/cangjie_compiler; export CMAKE_PREFIX_PATH=/opt/buildtools/libedit-3.1:/opt/buildtools/ncurses-6.3/usr; python3 build.py clean; python3 build.py build -t release --build-cjdb; python3 build.py install; ``` 1. The `build.py clean` command clears temporary files in the workspace. 2. The `build.py build` command initiates compilation: - The secondary option `-t` (i.e., `--build-type`) specifies the build product type, which can be `release`, `debug`, or `relwithdebuginfo`. - The secondary option `--build-cjdb` enables cjdb (lldb) compilation. For more details about cjdb, refer to [cjdb Tool Introduction](https://gitcode.com/Cangjie/cangjie_docs/blob/main/docs/tools/source_en/cmd-tools/cjdb_manual.md). 3. The `build.py install` command installs the build products to the `output` directory. Verify the products: ```shell source ./output/envsetup.sh cjc -v ``` Output example: ```text Cangjie Compiler: x.xx.xx (cjnative) Target: xxxx-xxxx-xxxx ``` ## Building a macOS Platform Compiler on macOS ### Environment Preparation The standalone compiler build environment is largely consistent with the integrated build environment, except for the additional dependency on googletest for executing UTs. For detailed information, please refer to [Cangjie Build Guide (macOS 14 Sonoma) - Environment Preparation]. For googletest dependency installation, refer to [General Build Guide](https://github.com/google/googletest/blob/main/googletest/README.md). Alternatively, you can temporarily disable UT builds during compilation using the [`--no-test`](#build-options) option. ### Build Commands Download the source code: > **Note:** > > Ensure the compilation platform has normal network connectivity and can access code hosting platforms like Gitcode or Gitee. ```shell export WORKSPACE=$HOME/cangjie_build; git clone https://gitcode.com/Cangjie/cangjie_compiler.git -b main; ``` Compile the source code: ```shell cd $WORKSPACE/cangjie_compiler; python3 build.py clean; python3 build.py build -t release --build-cjdb; python3 build.py install; ``` 1. The `build.py clean` command clears temporary files in the workspace. 2. The `build.py build` command initiates compilation: - The secondary option `-t` (i.e., `--build-type`) specifies the build product type, which can be `release`, `debug`, or `relwithdebuginfo`. - The secondary option `--build-cjdb` enables cjdb (lldb) compilation. For more details about cjdb, refer to [cjdb Tool Introduction](https://gitcode.com/Cangjie/cangjie_docs/blob/main/docs/tools/source_en/cmd-tools/cjdb_manual.md). 3. The `build.py install` command installs the build products to the `output` directory. Verify the products: ```shell source ./output/envsetup.sh cjc -v ``` Output example: ```text Cangjie Compiler: x.xx.xx (cjnative) Target: xxxx-xxxx-xxxx ``` ## Building a Windows Platform Compiler on Linux (Cross-Compilation) ### Environment Preparation The standalone compiler build environment is largely consistent with the integrated build environment, except for the additional dependency on googletest for executing UTs. For detailed information, please refer to [Cangjie Build Guide (Ubuntu 22.04) - Environment Preparation]. For googletest dependency installation, refer to [General Build Guide](https://github.com/google/googletest/blob/main/googletest/README.md). Alternatively, you can temporarily disable UT builds during compilation using the [`--no-test`](#build-options) option. > **Note:** > > Ensure the compilation platform has normal network connectivity and can access code hosting platforms like Gitcode or Gitee. ### Build Commands Download the source code: ```shell export WORKSPACE=$HOME/cangjie_build; git clone https://gitcode.com/Cangjie/cangjie_compiler.git -b main; ``` Compile the source code: ```shell cd $WORKSPACE/cangjie_compiler; export CMAKE_PREFIX_PATH=${MINGW_PATH}/x86_64-w64-mingw32; python3 build.py build -t release \ --product cjc \ --target windows-x86_64 \ --target-sysroot /opt/buildtools/mingw-w64/ \ --target-toolchain /opt/buildtools/mingw-w64/bin \ --build-cjdb; python3 build.py install --host windows-x86_64; ``` 1. The `CMAKE_PREFIX_PATH` environment variable specifies the folder where cmake generates products for the target platform. 2. The `build.py clean` command clears temporary files in the workspace. 3. The `build.py build` command initiates compilation: - The secondary option `-t` (i.e., `--build-type`) specifies the build product type, which can be `release`, `debug`, or `relwithdebuginfo`. - The secondary option `--product` specifies the build target products, which can be `all`, `cjc`, or `libs`. - The secondary option `--target` specifies the target platform description, which can be `native` (current compilation platform), `windows-x86_64`, `ohos-aarch64`, `ohos-x86_64`, `ios-simulator-aarch64`, `ios-aarch64`, `android-aarch64`, `android-x86_64`. - The secondary option `--target-sysroot` passes the subsequent parameter to the C/C++ compiler as its `--sysroot` parameter. - The secondary option `--target-toolchain` specifies the path to the target platform toolchain, using the compiler in this path for cross-compilation. - The secondary option `--build-cjdb` enables cjdb (lldb) compilation. For more details about cjdb, refer to [cjdb Tool Introduction](https://gitcode.com/Cangjie/cangjie_docs/blob/main/docs/tools/source_en/cmd-tools/cjdb_manual.md). 4. The `build.py install` command installs the build products to the `output` directory: - The secondary option `--host` specifies the target platform installation strategy, which can be `native` (current compilation platform), `windows-x86_64`, `ohos-aarch64`, `ohos-x86_64`, `ios-simulator-aarch64`, `ios-aarch64`, `android-aarch64`, `android-x86_64`. Verify the products: Since the build products are Windows platform executables, copy them to a Windows machine and use the `./output/envsetup.bat` script to set up the cjc environment. ```bash source ./output/envsetup.bat cjc.exe -v ``` This step only generates the target platform cjc executable. For building peripheral dependencies, refer to [Cangjie Build Guide (Ubuntu 22.04) - Source Code Build]. ## Building base libraries of the Android platform on Linux (cross-compilation) ### Environment Preparation Except for the additional dependency on googletest for executing unit tests (UT), the standalone build environment for the compiler is basically consistent with the integrated build environment. For detailed information, please refer to the Cangjie Build Guide (Ubuntu 22.04) - Environment Preparation. For googletest dependency installation, refer to [General Build Guide](https://github.com/google/googletest/blob/main/googletest/README.md). Alternatively, you can temporarily disable UT builds during compilation using the [`--no-test`](#build-options) option. > **Note:** > > Ensure the build platform has normal network connectivity and can access code hosting platforms such as Gitcode or Gitee properly. ### Build Commands Download the source code: ```shell export WORKSPACE=$HOME/cangjie_build; git clone https://gitcode.com/Cangjie/cangjie_compiler.git -b main; ``` Compile the source code: ```shell cd $WORKSPACE/cangjie_compiler; export ANDROID_NDK_ROOT=/opt/Android-NDK-r25c/AndroidNDK9519653.app/Contents/NDK; python3 build.py build -t release --build-cjdb --no-tests; python3 build.py build -t release \ --target android-aarch64 \ --android-ndk ${ANDROID_NDK_ROOT} \ --no-tests; python3 build.py install --host android-aarch64; ``` 1. `ANDROID_NDK_ROOT` environment variable is used to set the path to the Android NDK. 2. `build.py clean` command is used to clear temporary files in the workspace. 3. `build.py build` command starts the compilation process: - The secondary option `-t` (i.e., `--build-type`) specifies the build product type, which can be `release`, `debug`, or `relwithdebuginfo`. - The secondary option `--product` specifies the build target products, which can be `all`, `cjc`, or `libs`. - The secondary option `--target` specifies the target platform description, which can be `native` (current compilation platform), `windows-x86_64`, `ohos-aarch64`, `ohos-x86_64`, `ios-simulator-aarch64`, `ios-aarch64`, `android-aarch64`, `android-x86_64`. - The secondary option `--android-ndk` specifies the path to the Android NDK. - The secondary option `--build-cjdb` enables cjdb (lldb) compilation. For more details about cjdb, refer to [cjdb Tool Introduction](https://gitcode.com/Cangjie/cangjie_docs/blob/main/docs/tools/source_en/cmd-tools/cjdb_manual.md). 4. `build.py install` command installs the compiled output to the output directory: - The secondary option `--host` specifies the target platform installation strategy, which can be `native` (current compilation platform), `windows-x86_64`, `ohos-aarch64`, `ohos-x86_64`, `ios-simulator-aarch64`, `ios-aarch64`, `android-aarch64`, `android-x86_64`. ## Building base libraries of the Android platform on macOS (cross-compilation) ### Environment Preparation Except for the additional dependency on googletest for executing unit tests (UT), the standalone build environment for the compiler is basically consistent with the integrated build environment. For detailed information, please refer to the Cangjie Build Guide (macOS 14 Sonoma) - Environment Preparation. For googletest dependency installation, refer to [General Build Guide](https://github.com/google/googletest/blob/main/googletest/README.md). Alternatively, you can temporarily disable UT builds during compilation using the [`--no-test`](#build-options) option. > **Note:** > > Ensure the build platform has normal network connectivity and can access code hosting platforms such as Gitcode or Gitee properly. ### Build Commands Download the source code: ```shell export WORKSPACE=$HOME/cangjie_build; git clone https://gitcode.com/Cangjie/cangjie_compiler.git -b main; ``` Compile the source code: ```shell cd $WORKSPACE/cangjie_compiler; export ANDROID_NDK_ROOT=/opt/Android-NDK-r25c/AndroidNDK9519653.app/Contents/NDK python3 build.py build -t release --build-cjdb --no-tests; python3 build.py build -t release \ --target android-aarch64 \ --android-ndk ${ANDROID_NDK_ROOT} \ --no-tests; python3 build.py install --host android-aarch64 ``` 1. `ANDROID_NDK_ROOT` environment variable is used to set the path to the Android NDK. 2. `build.py clean` command is used to clear temporary files in the workspace. 3. `build.py build` command starts the compilation process: - The secondary option `-t` (i.e., `--build-type`) specifies the build product type, which can be `release`, `debug`, or `relwithdebuginfo`. - The secondary option `--product` specifies the build target products, which can be `all`, `cjc`, or `libs`. - The secondary option `--target` specifies the target platform description, which can be `native` (current compilation platform), `windows-x86_64`, `ohos-aarch64`, `ohos-x86_64`, `ios-simulator-aarch64`, `ios-aarch64`, `android-aarch64`, `android-x86_64`. - The secondary option `--android-ndk` specifies the path to the Android NDK. - The secondary option `--build-cjdb` enables cjdb (lldb) compilation. For more details about cjdb, refer to [cjdb Tool Introduction](https://gitcode.com/Cangjie/cangjie_docs/blob/main/docs/tools/source_en/cmd-tools/cjdb_manual.md). 4. `build.py install` command installs the compiled output to the output directory: - The secondary option `--host` specifies the target platform installation strategy, which can be `native` (current compilation platform), `windows-x86_64`, `ohos-aarch64`, `ohos-x86_64`, `ios-simulator-aarch64`, `ios-aarch64`, `android-aarch64`, `android-x86_64`. ## Building base libraries of the iOS platform on macOS (cross-compilation) ### Environment Preparation Except for the additional dependency on googletest for executing unit tests (UT), the standalone build environment for the compiler is basically consistent with the integrated build environment. For detailed information, please refer to the Cangjie Build Guide (macOS 14 Sonoma) - Environment Preparation. For googletest dependency installation, refer to [General Build Guide](https://github.com/google/googletest/blob/main/googletest/README.md). Alternatively, you can temporarily disable UT builds during compilation using the [`--no-test`](#build-options) option. > **Note:** > > Ensure the build platform has normal network connectivity and can access code hosting platforms such as Gitcode or Gitee properly. ### Build Commands Download the source code: ```shell export WORKSPACE=$HOME/cangjie_build; git clone https://gitcode.com/Cangjie/cangjie_compiler.git -b main; ``` Compile the source code: ```shell # build mac aarch64 host cd ${WORKSPACE}/cangjie_compiler; python3 build.py clean; python3 build.py build -t release --no-tests --build-cjdb; python3 build.py install; # build mac ios libs export XCODE_DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer export IOS_SDKROOT=${XCODE_DEVELOPER_DIR}/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.5.sdk export IOS_SIMULATOR_SDKROOT=${XCODE_DEVELOPER_DIR}/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator17.5.sdk export TOOLCHAIN_BIN=${XCODE_DEVELOPER_DIR}/Toolchains/XcodeDefault.xctoolchain/usr/bin python3 build.py build -t release \ --product libs \ --target ios-aarch64 \ --target-sysroot ${IOS_SDKROOT} \ --target-toolchain ${TOOLCHAIN_BIN}; python3 build.py install; # build mac ios simulator libs ​python3 build.py build -t release \ --product libs \ --target ios-simulator-aarch64 \ --target-sysroot ${IOS_SIMULATOR_SDKROOT} \ --target-toolchain ${TOOLCHAIN_BIN}; python3 build.py install; ``` 1. `XCODE_DEVELOPER_DIR` environment variable is used to set the Xcode developer directory. 2. `IOS_SDKROOT` environment variable is used to set the path to the iPhoneOS SDK. 3. `IOS_SIMULATOR_SDKROOT` environment variable is used to set the path to the iOS Simulator SDK. 4. `TOOLCHAIN_BIN` environment variable is used to set the path to the binaries of the Xcode toolchain. 5. `build.py clean` command is used to clear temporary files in the workspace. 6. `build.py build` command starts the compilation process: - The secondary option `-t` (i.e., `--build-type`) specifies the build product type, which can be `release`, `debug`, or `relwithdebuginfo`. - The secondary option `--product` specifies the build target products, which can be `all`, `cjc`, or `libs`. - The secondary option `--target` specifies the target platform description, which can be `native` (current compilation platform), `windows-x86_64`, `ohos-aarch64`, `ohos-x86_64`, `ios-simulator-aarch64`, `ios-aarch64`, `android-aarch64`, `android-x86_64`. - The secondary option `--target-sysroot` passes the following parameter to the C/C++ compiler as its `--sysroot` parameter. - The secondary option `--target-toolchain` specifies the path to the target platform toolchain, and the compiler under this path is used for cross-compilation. - The secondary option `--build-cjdb` enables cjdb (lldb) compilation. For more details about cjdb, refer to [cjdb Tool Introduction](https://gitcode.com/Cangjie/cangjie_docs/blob/main/docs/tools/source_en/cmd-tools/cjdb_manual.md). 7. `build.py install` command installs the build products to the `output` directory: - The secondary option `--host` specifies the target platform installation strategy, which can be `native` (current compilation platform), `windows-x86_64`, `ohos-aarch64`, `ohos-x86_64`, `ios-simulator-aarch64`, `ios-aarch64`, `android-aarch64`, `android-x86_64`. ## build.py Option Help ### `clean` Option The `clean` option clears the build/output folders. ### `build` Option The `build` option builds the project files. It provides the following secondary options: - `-h, --help`: Displays help information for secondary options. - `-t, --build-type`: Specifies the build product type, which can be `release`, `debug`, or `relwithdebuginfo`. - `--print-cmd`: Displays the complete cmake command configured by the build script. - `-j, --jobs JOBS`: Specifies the number of concurrent build tasks. - `--link-jobs LINK_JOBS`: Specifies the number of concurrent linking tasks. - `--enable-assert`: Enables compiler assertions for development and debugging. - `--no-tests`: Skips compiling unittest code. - `--disable-stack-grow-feature`: Disables stack growth functionality. - `--hwasan`: Enables hardware asan functionality for compiler source code. Currently, this is only supported on the ohos platform due to dependency on hwasan tools. - `--gcc-toolchain`: Specifies the gcc toolchain for cross-compilation. - `--target`: Specifies the target platform description, which can be `native` (current compilation platform), `windows-x86_64`, `ohos-aarch64`, `ohos-x86_64`, `ios-simulator-aarch64`, `ios-aarch64`, `android-aarch64`, `android-x86_64`. - `-L, --target-lib`: Specifies the path to the target platform's required linked libraries. - `--target-toolchain`: Specifies the path to the compilation tools. - `-I, --include`: Specifies the target platform's header file search path. - `--target-sysroot`: Passes the sysroot content to the C/C++ compiler's sysroot option. - `--product {all,cjc,libs}`: Specifies the build target products, which can be `all` (default, includes `cjc` and `libs`), `cjc` (compiler binary), or `libs` (compiler libraries required by the standard library). - `--build-cjdb`: Enables building the Cangjie debugger. ### `install` Option The `install` option organizes the build products into the specified directory. It provides the following secondary options: - `-h, --help`: Displays help information for secondary options. - `--host`: Specifies the target platform installation strategy, which can be `native` (current compilation platform), `windows-x86_64`, `ohos-aarch64`, `ohos-x86_64`, `ios-simulator-aarch64`, `ios-aarch64`, `android-aarch64`, `android-x86_64`. - `--prefix`: Specifies the installation folder path for the products. If neither this option nor `--host` is specified, the products are installed in the `output` folder under the project directory. If both are specified, the products are installed in the path specified by `--prefix`. ### `test` Option The `test` option executes the compiled unittest cases. It has no effect if `--no-test` was specified during compilation.cangjie_compiler-1.0.7/figures/000077500000000000000000000000001510705540100164535ustar00rootroot00000000000000cangjie_compiler-1.0.7/figures/Compiler_Architecture_Diagram.png000066400000000000000000012332421510705540100250700ustar00rootroot00000000000000PNG  IHDR @(IDATx^ܡ 4iJdx0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0ΝQزE!(T"P1(("J )҂"j") . M a ( 317m|oHܙ3ݹ 0 `(P@ @&/x DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDz~v|L%""""""""""""""""""""""""""""""""g$X""""""""""""""""""""""""""""""""" ~v|L%""""""""""""""""""""""""""""""""g$X""""""""""""""""""""""""""""""""" ~v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`^  v|Le{)tS@72! ZM`$X,jB7t 8`@&q*yR_ǡn wv|LUbC@!K @)2VC@=%wSd,{Jzg$X%Y +8MΎIJWq( 8`@&q*yR_ǡn wv|LUbC@!K @)2VC@=%wSd,o…HW_}u:ӡ:o~3?LoSO=_:o闿e<}.i֬Y/sWރ=\;ӕW^N;ta>tUW|0-^8HOӥ^N>裏wv ė-{Jzg$X%Y].3gHIo&N,-&MJ;H9OWUq_UswCBlԩiVjҲ馛]{t}wǦ8R KҪR*W\qE<4aVެJ L23y[:k0\pAw6sxXVZBk7ki~cId@&q*yJi6Rﳟc=O</=f*~gKz'Ӯ8_{3[FU_<6gM7ݔ6p56(q@+ 8`hnli#?{IsMwuWk)wUW]ژv)-X .gԖUXrw_KfVK}{t駧'+Lw^:;0򻎯[Z>±J`Koz6lgt1Ǥs=7]q#=c~헶jkb˼y=أqL /^>u^;}C]wuN?ҽޛ4{쑟YU_+ . .(PUd6^Z2'ONtP馛KGIzHQW<ߒ9CKGmYȟ㎍?S};IgmzFY{_~yKfWOf//5IKG\h?;>@&q*yjcy߸:g҂ FGI篳nŗʲ ?%_ʍ|sϥګq%}-l^ؖ`*묲*//K/4暍kz'Lox֯G}tVp[n/곷רsqŗtFYt!@o߭K;h `$X%Ym,<S3Έ{whӟKz峟lZhQ<ŘTt;Su^py] 뮍y_r7߼q?4Wo}k<@DDDDDK/H@?Ŀw v|LUն<>L2%ŋ&lV` =pKKbƌJ[ރ?xZksYgŗٳ\ZVp޼yi5h*ny駗RqvI.DDDDDD5%"""ү(ރHΔ(ރHL⽈ ^|= v|LUu]n4a„tUW?O>3s}{Ko~?~ {J/""+%)y+3&_DDDDdy)I\xϠIJ\X nܹ#%oXsύ M_w_u{xxϺRX)=N;5Ueĉ/K<<VZiƹ|󟏇guI'5ְdV_fq̙3̘1q:|.[w6+yk)%%kɕv`.""""|)I\'3`$X%Y g}i~О,p5͛׸v>;3y=o|q:~{<<%"'xb|U&N{xx6˻c=6Zڬ-%w-y  ?cJ^;QwƒĞ '>A;>@&q*yR?3fhО,gF5_e̙О)̣7T|6RgE b.^җ6Q}{_<Y3Pk[JZn%1%~%Ɣv%=O|< v|LU7~U~xhOW8k֬xx6{wU:xhѯw8G#<2>rJc-uZx-4UgXmYڴi⡭JJ^;Rwגp+y)y+30%g,y0L0~nd,s饗6~UbxhOWxWó'?ٸ~C9$3y=xE5Q9s &4SegnnuT` ⡭JJ^;Rwגp+y)y+30%g,y0L0~nd,_s2uxhOWx뭷ó9ׯ~C{0~o5Qe/{YZxq<|`rƚOnʚk9.o*&MxmV T薒v[{Lk_ɟ1%(;ckabO? t 8`qMUZq:{S>:|qJJ^;Rwגp+y)y+30%g,y0L0~nd.Ν;7viikoΌ|^;)=;7Q|-+Y3Pk[JZn%1%~%Ɣv%=O|< v|LUo~{ZZ&O>OK/4L?pZhQ<Ua1W^yeWv}6λdFS$y.,=j5QeЁ k2J,sUw}駟>45kV\zk@<v%VSځWgLk`0JX`Sa3`$X%Ym,Wڸ)S3g'x"|LxtI'5Sek5y,,=F5QSOiXSn喍sU93 H]Y3Pk[JZn%1%~%Ɣv%=O|< v|LUն{Y2Ue].\_®|0\mxV?x*GuTƚp]vi1e@JJ^;Rwגp+y)y+30%g,y0L0~ndmokKiӦG}4oUVSu]׸~-l{@U:x@U׏k2=U2 wf%@%n)kk`ǔv`S3v&T? @72VCV U~Ve-LA;>@&q*yjKe]ָ*'ON/gqYg5_ek6oqfmi\X Wq*k<| >6Rgevix(mV T薒v[{Lk_ɟ1%(;ckabO? t 8`,Ƶ댵m/| T4iR?~<<~:ꍵ?jF,C2&D7UP 247]PRRR?"CB4܊Jd.tM3z~tkgY g}>q^Җgʕ+1D>@Hv\v7m@I>H.;C5 T:h<;X;YQIci@Q^=>g*m$_#QNhʖ-'D^uۑk@6I=/_-ǎ'=5c $n2):t' $$E@Inc$O9Fr% LЦ?Ax@ɊJ@SjժI=w^/_>m$_#QN8ydmyv|w|rW;vL)SF[&4'`յeQ}|rϘ$ i֬Lm۶C72} ex|*ne|\vkFeu xw$wwѶ`ѣ|r׍?^[&_r7j˳[n|rWM6M[gb0`e裏=ki4`~;3'OTX $$E@Inc$O9Fr% LЦ?Ax@ɊJUVi`ܹs:xX^;rMlٲ2)8 kza֭t:#Q<''OmyvZr%U6lPEi@kŊӖkǸq,;v옺\/^|D>@Hv\v7m@I>H.;C5 T:h<;X;YQIx!cv]կ_?mk'lݺL;z'wĉ.#Q<I׮]Ѹqckzɓ^N_r(TZz5U>)lѢZv-,2} ex|*ne|\vkFeu xw$wԫWO J駟]1sLk_u&Fעp2ț7Zlő޽{kI&$Q;ɁTʕeq׫?.StMڲX,XhQSGQ&$/]%Mr#~1|(a6 8Z|NVֶJ}v>#S#kQOHjժ-׎˫~ϒ#Gnݺi/U*QNFQ~}c>[N͛kE6)SF[ŋWK.9pBX뢠5k,@II.;ċkWepH.;s?$_3J.;@M#%]-vwy_eĉjN֑,rMƍӖKV+VelڵN:r)&LիQ;uE[nbTVM||J*ڲ):w>)JH̙-?1N?t5dua>kV7ׯʛ7;q@II.;ċkWepH.;s?$_3J.;@M#%dڶ$F"ET>}Ԟ={/UFRTZU]ve|- ;4 _bϟ_K߿n~zw.ZLuA(P@[&EՎ;|IH>m<*U&Ol%}ɓ'c/314h?g<LrHr ^$_J.;6Fr $c$!Qrm*@pxC,ɝ,S@ J@h۶-/%ԩ-<(f۷O͞=;e.JR_-[j5ZJwghQT)+1{g&vޭVXaM4Qy淃~њo4`AnժJ*1cƨE7'NXˠ7oެ.]jժ͛[N8њׯGU;w֓,ʔ):vf͚eLH|r㏫kAI"׭[ |_D>@Hv\v7m@I>H.;C5 T:h<;X;Y%׿btIզMm9"o޼rʪiӦk׮^PFRO?޽W_ǃ;S[̘1C[~(XoD꣏>sr6$*`2;vP5֑.g%ˣLq[džͯ6)/EB߳nݺγiR%r6m/Jle@"Ur $1'#׌&hS @<`IdE1 9v옺enܸq{Ѧ5ZI&Yz܊իkמ֭[kӹD49|j۶7xjҥPtim^Ĺ瞫VX+|D>@Hv\v7m@I>H.;C5 T:h<;X;YQMH?^xUhQmnkʕK.t&_[@ZF&M_:ծ];mZ`*z'_m}NbŊjժU|2 ټy_^7;P૎_LrHr ^$_J.;6Fr $c$!Qrm*@pxC,ɝ('mݺU~rs XI(A׽{wm\1 ٸq{ɓG[gA~ȑرc|5N:i&UnTBfgu4hڽ{7_whڴ)/VrI3fU?Q&$/]%Mr#~1|(a6 8Z|N̙3m [4i҄;'_zTյuduUGV{S=(W.(i]ŵpʔ)Z(}Q>dw|ݩ^wuj?=Eˋ6i`6vڥ)D>@Hv\v7m@I>H.;C5 T:h<;Xd?{Nw}V9JFI .*V5jZn~aK//|rAx@5kL]tE|oUR%u嗫:I&/"z >|X-YD_R{VZRuԱR oJ7n8E5J;R@-[f%&|UNsjذu|RBI*%~9rZx:y$_/Z=;0@}'y+"uI$E@Inc$O9Fr% LЦ?Ax@B' {>v$ ! @ ^x]Dr ^$_J.;6Fr $c$!Qrm*@pxC,tץKK1l0>)[p̙3Uʕx饗vRݻO $"Ur $1VqavbmjԨ|\vkFeu xw%\{)N'o m?S*THmذO.U'PC Q-[TvzgԞ={d2 ΃$"Ur $1Vqa+;8m_>)8$|(avնm[-:w'@ ^P->Gx ,oX\s}v YnڴI{ڶUPA^O<NNJ"/]%Mr#ajAX­dya $|(a 9^?Z|:Y駟7n䓃P1e>8 | ͢^eGÆ * ΃$ɧ~z:t蠚7o."U|yUT)UJUNuW-[w}Wmݺ/\"Ur $1V!Jpk]|J"aEV\ƍtZh.RUfMU\9UdIUreͺwm>@ܹ/ p% ̢$;?Ax@B' [cǎλ*T`qgݻwĠ~ | ͢˖-6_l" ΃$/zOz3o}zKu]V?~~&9շo_?|+i̙5-K/'_Զb޽|RSHnc$=ҍ#C,/L6!DrСCG5k)l> B_ @ $wA 6mڷoze]N:U-]T9r".^P->Gx ,o5nX;R}|R>ӀԩM{GHheuM</^g q/^%\v?UZUWcȐ!|b,kN˗O3nĥ^>cZ?J"A2= )Tڰa\׫ jIg!?oH"aej;).PĈ#xS6!DroQ;wVŊӠzzM7UV_H"N^DѢE߮f͚e%dž`;?Ax@B' E~gkǤI }} ȓ'裏G/S\&O8Ǝ~J̘1OꪯZ&= ^$y x]DrC/9 0J ѦM+ 䎟[%\ zҬY3>X\s}vYH.{XNɉM+%\ m޼Yo>1GN+%ܺu+//$?u.\XnZ͞= " |x}:h<;Xd@m߾]uû>}h\;0.Z t@5ԑ#GuQ+1ߎd̼~j˦'u%JkǙg?g q/^%\v?! @̚5K}Z:)y^?J"AJ,dʔ)|qhv%@:!䲇픜XWeÇc= (;I$)Sϸ+ڵky ~‡w#ND%yg繎ܹOꙍ7B i\zA8зo_>{h 8P+)қk˦: eY۷oW_|:u{Q%JЖc~@z\*)g!:uⳉA|{xYH.{XNɉM+%\v?}Ej4mӦMՓO>i=o]zu:tHW#Gr-Y-[oE@ Drٝ2)ڬqWʕvkV_}/&>xC,t jhP=:;&OgqAe?, }$K/4u+ʝ*қNh6ĉB6ƒuI$= ( (IYfY/oVW'OHjСꬳҖ,F!EcǎUÇ?%f̘'$LɓG}G|㏭g!?oH"aej;%'6ADrҿm?% +1سg4h*]dѶmۜ+d_H"NyܲegϞ~:LQpa$ @<`Q_yj; zXH/zeĉ)_>}:">Tꫯń+xy^z3~X  C>kNeQFtTb¢8zwߏB$LP;~>{h=zԺGķ#YYH.{XN7 WeCZ?sr!5bUrem=<x>;5$AkӢEjqgE0^?;?Ax@B' wqmY|D/Wtۈ54 U/^\m۶/*pvRJKaJ^J̼~! @.G)9YboJK/T[_b2J"AYH߾}5p@H7$\2^$ r$Ls9p1uwjoY\ů?$\v۷Ou-{=իW\^?;?Ax@B' ^6TvK5ks'Nٳ7ߨ+R[~b@;wY!" 3 T UV|QСVN;z!Kof^?K! @.GxU:?l7֛/2 BBAI$=Hg!"~kת h7@:!䲇2װM+%\v/ھQn]$ҽtc.]H.SA,X*T;16l:g!~‡w#ND￯Erc=~'#zXD_ν;T޼ye&FѢEժU" Bo3 ˧͎9s?g% ہ̼~C@xu]e$} 1;6m'UFQLeo^@8:(,^z}ݐ#j<, I$=Lmm\+{>._n݇SO=!1ڷogp Drٝ ?ck"_b׏Bx^P->Gx ,zJ}UPWO?7n5k?~z# rرpB> H.{LBRū'Nʛ., I$=Lm_m\+{a߾}Zj~I /ua>^z%,vP,H.Sas`Gɒ%l @u xw8Xf]v:.vZ^ ۃL4Fѣ[ \vm֚ s| H.{  y~ѳgO>͛ÎΝ;!Dp/ xDrكdz*/m]vRJi0%,H7$\2~&p r$vi$1ǏEZر#Cew*,u֩"Ehcȑ| |x}:h<;Xd@\W.U}СCQ ɒ%KT)H.{LB:ՠAhժ_T:t蠕ӎzH?;, I$=Lm߉܄m\+䲻mҥH JwI>6mڤ *L2|rWI$ݩ0 Î*U'NY ~‡w#N =DUv.t+6lϟW Ǐpi@b@rjQN@MIh^;ƍwʴv73w ^x]Dr٣ u5hbŊ|R߼ZYO'puPeRg!ʗ/7;̙?P+'8pvYH.{X  Wew%袋aGRݻlS胭۷W۶m_I"N?xQujecƌ|9 @<`qW_xzϋĥ^,XW1piOgUlYm:;z~Dv4ii07^?KE_ܰaZp3fիG׋'wjҥjҤI駟V?O<&O/^lIvء>c[#lWmC=c%]dڲeK4N iYF͝;W+gϞwVO_(g''|:VhI«>S5~xkT>uMH|H.{  U/MI}Ee(Pׯ:x 3H.{= 7ovTTJ#Gիk峃Y8H.{X  Wew="1tرcf͚Vի>s> $T4ŎoO!>xC,t %iӦ[oUoS%KΓɂۦM+I %Hx4'$3gԦPBV/l߾](QB+vSN:|RоexYqʔ)Z㪫Җm6eM^Ɔi[OԮ]ԀT5T|ߔif}^BE|UV˷!YжSJfi&Hי~OC!C2ehAI|ͬ֙Hا駟}g%^ʛ7V~~⋭]fNǫc c(cʕ|;y5?4H 3=zd3Tc=Q߈e˖dNãH"nSfDe$%6qڷo']K,HJ"AJ,}Mw;z?JeG۶mi|H.{XΠ Wew =O,W/hРg7|c=[ $T4.4vv|r9<xC,ttlO,^z8j(5uTh"wY0p mi@䦛nҦYf|rq4hӗ()_&?'m^z)4+NSB'>q9"9ݶ(2yIO~zխ[7+Y7ӠhkժU|… f,UPΝ;[+mΜ9|R+i٘1cR` J+i4SE*O @k$I$ݩ07np|R9HƖx}:h<;Xd8{XbvL2 h뵣VZV+˗ސ e1퓷~Ojemڴq-1͛ 6⣏>R5j4(O?Ic6}SۻwW6]kLIܧP her@'%/\gqt y#𜒜i zG|IZy?b2Gz=;?Ax@B' 93 /%5j6gyڽ{75RUVKAq>s>˗ސ e13fI͛y睧MVйT+XIxY :ٳ6a„?9pjԨ6MQ|yu[iTx ?~nw߭HӦMS Vu]#wq6 3fu}§s4P2ܹ6ׯ_x{I$= 0N8a%^p6ˊѣG[ ׯ_oM#%^b5k0R[EŋN6KTti~Q*UO !vZZtȑVgϞjjjV{?HOzcg''|R=CV_tÔd)駟ǫ޽{[>kݫxa> AI$=H> Le˖զcСL:rѤISYHZΩK.U&MbzvOXmɓsod˖-VN>@"#m6'믿nMy5b5g[ͳI}_|Ѻj*e+Sd"M+%\v7|w>D]#Y?uhر={@"},.>JrD2ڹ|?:Iew*A:ox(އ|l0qDc׬YΝk=qtB_K,Q'Px'^?Z|:YΡmi- jܸ6:u⳸ѽ{w>| ͐P>܉h0w J,ێ9:v쨭߫^%L%k%8+MlHߧ&E\{VF[T {ǖk4p&)!%{~ȍ9I(4߷WexY1o~RlIs |gVVew*Tֽx~S1jli{O;As[Ԏ#B3Gʕs/?zu[ɢ3X|q?/J@gfhݺĠy5G4wQGVڵlfQا%4;u& ۰aCվ}{SOY_ d4ǃ؃K'A6?zLQn]5a븤饁[ծ][[;]iHh;:vf͚k׮րٳg9sX_~ҥKF@hP2AuT/U)ƌ:t8 mz^o.GAP i %_]tWRŊrQ||S㓞guf f48RS[|Ygi&h:wk-} UjРA\rڲ? aB6${ӹ:ٵ2_Z`AmLE97nh%t~Drك̧Yf|rϤ6YH: Zw&/ zD]_?-^VlJ*=s/eRBtӧO^CkpߒH.{X  8m\+䲻!Շ nIFzFI}tϗMP2Cn c%co^RLE2ҹu>3+IJ"N?H)xy9r$<'Q8S;RnwE(Jи[o5ɂ! y͆kM:h<;Xd8{у3>܋&R2;(M*^An=/eSЗؼId'1K xŋUͭ$|Y< gwEy)h=ܣ> RP.7~7J>gio//;v찒f# 4\s. Vݻw_deqW[JOxkA-%t3{tTMIf7mTF}G*J*B뮻:kX*ȒGR2\(Q6vXm ֦M_$EGmXb2;e>%\(* R/_g 屃̦b:׿|Rˮ]ӁAm]mذ7t=HIgR %hnFkRW;(O?g Dq zbܹ|uFJs5M:5e+Ӡ%zG~aW%/@$lP>Uyʔ)|-X@[jղ^| }ʋv"]~;qd&M6}"W{UӦ4N5}/i}fzTYe+Sd"M+%\v#~;z1MHF3]Ϙ1Oj={qN`s,}~0Q8vWXʑN/G\B>ᙕ4|[%\vbj'[璍>s*j~=Ӡ>Deq2&75% @<`V^zKDa)z9-/ߎ-[5^An=Gp1sS/J H@ic.jJ[nb+VYy뭷$UW]vgmWʕ&}m)FD iPƄ b\}hĈ: 8P޽Ϛ1k, _umAgz~7>kFo|vI=s2ȴ,Ri}u81(Q kd_ oْS^;|K"QT@ҳgOmv<|rߴnZ+EJҾLc:'{gVT>[qg?st\'γQl^Ġn 6R몍3I.kЋ2to*Js533)s:8ٹsgm>ѯ_?*߷H.{ry2j(mz;-A8t萪Z^ :|b+Xo WJЬY3n]s4sd8p 凵EѣGo9c$K.> We+Sd"M+%\v m4>!pfsb}e\Paq#_򹄄|gVDrٝ c_cGcظ݇N94ի1|}v4iĕ$Q;ȴiTrwuW(ɘt]kJ?Ax@B' 93 H["JJdz: 䣤@ŋזOAmgx[1 ŴO苄 . Cu7<ZAzYgi밣[nשSG[4)VZ5_E"JԶ~_-ZU7@əN?tm4`>" l٢+-dIrA(S5.diߝQB(:rAի-ӎƍ[%KTIۜ5R2Tkժ-:202$A~$|vP_ϗVlvJڶR 0O1xS[jP6Auj731}tUP!^D%ҥKyB˟7|:|0-4XWɛoi7vPtARJs5%=3݊c&1r}iӻtUeR.B^{yԩ5O<>;w'B8zٮcǎzի[^[n]ʏ_tI tHÇgkڵL"`@r|J"aej;L$ r$TdM>oDt=yR^An=Gp1^z /PkV޽/Ƒ1cƤڼ[/wY[#G;aÆIо+>KVL"E?(SBFe˖u-M_뢯{T/`]W`)_jԨF}ǣvڎcUdImvqCc5h*4.YRE;-Zg}-Gi0 4isn^ ֤s\:s}OvO4ЖvIDATePQ{R"-[&m-gM(O>ںm;g}f Rvsԩ}X>/)SxqBgŊiO_~j޽|ЈZ]dAr7}̂UӱG &X i>/YPrT^uuM7~2)蚏OIY\Maô%F͚5/={3gzT.]TRyqJpR>^~e;XAC:t`%ҽ@pWI$=H> tAmV\il(tݼ|h#Y4jj7%ܹS]V-[:_ӽfJzAȈ /vtꫯ{JM%٘^JnZ[buO޽j(Avf5mT[guѶHOTFBS_}ȅ93|J"aej;L$ r$]>vizm,AԮLԷ k֬IB [nE=#hΝ&N d[J5v%1^VBHq(KH'Q{f%fI$ݩ0ݣ砹}01n-PkND8:zuiuZ}4.G ۿuw[\<9kI$= L/m֢E >iRs4^P A?58/V͛77^'tJTǃ'Nٓ~[o*W-+1˗C[o΃PLY)Jul^䶃Rgx~y業yU~-=cA IFr7跽… NA@/JMAÇ9pçp_$EGk*=@^ߧH.{< |v9[=zSYH"JB˗t f]0˱zuSǃ^t=9sf J )SSOY/sKS qރߴiSF )c=mcNY/}<|pߧH.{X  8m\+;yfmꪫk4GZ5ڵZf&MgwLRt7c\/G\Bz>3+6K"N?O?ieΛ}谞1n-:tH5nX[ʕsenԎ#BxP:vX]@˲>vh'Ldɘd$_kJ?Ax@B' 93 䥷Dg֖a d(G_5=\p!S^fAn=wM">A A|v8= o۶)˥sPjմmT<+n%IEiTXR䃉wN(%R'_gp2ȴG'ב,+nAk1J4Ze93o} /4ߎ{O\ /X7 7dԏlժĠA+V:F/lu%%Ңmt,Gr\;g _UUZU+{/Y/"PQB2oY2l2Utimv`\y/^Mjd{K*>conJVbE&tJ J Seg!TSg%˷e˖|rBlb_vbPB;w2FĦvP[&Jܝ~ %7qr}A ʖ--Z?cD60YEQ/BÆ u'Æ '|bʗ)䲇 *M+%\v'躍ovb85zw =Eed˖-I[$=nG9_a)G_ҹ|gVmDrٝ [yYiE>tɘIcKm4''ʼnvבtԫW/oY3B}C˵FoNrү5%u xwpmL2y鍣r٢|9vߟO/q[|mȴOxudh@E%u۱n:>KNh PONAbv+~7Lf>gOK|yvP?4kL[7&;vL;苭&{-YNUTiȐ!zxԬY3S&}cǎ;%\(: bD]~޽~ Jl:ӹ駟V .s<n)Udg4`CK|v 0:VA܉q[(_U* Nɓ,Y0R%{7o%#A Izo:WSwyYi.8ի-#Fh袋r0 {jҖi%U|J"Ar,U^ijn</-"-y,2:,mvt͕T@G:uAB]vС+/yQ2TmJ,IdLc믿pu'6leT)0M})䲇NtmT:t5cqڦ>D߿H.y[N$Q8vWXa娜KIYEnI$ݩ0]Ŏ?ϒRCK8svRB^l; *dCv*j*>}駫3fr2k,UhQmeʔQ9( ךm:h<;Xd8{h֭)ϙ3bߪN;M[|MKoq|K^oD}M4TIh[h{J( %54$~77dJ>mذ< D&TI0)š0)]w㫬:P8ldwvPr(QRKn ZWZǎO.Ye0$M*:/1p6JJm_?E62Mzz /ku=ۘ1cR2uiѢ|;L:x˗O}|С^O4ؚϞ=dوB]{L|lԞ={ʼn\y/$Q\ͣvo%.Yl;+BNK:^cѢE|ߗH.{xҵkWm~;^>hꫯɓYܹ\;F'wåzνnH~Y렫Z[.EiXH_Ŋ+qK+X H!{|?J"aej;\8mS _I$݉Tٌ],h^S<%ng*Tpު>JrtB⹄H8DUDrٝ S0}FJ<%9S;tl)yGAܹs,9qDmv.]gqdVx.;UF MkMxC,tC=l^zK*a>Kկ__}'||-xO|mȴOÏA6u@qE8smٳ'4c~ӧY"}i&@zO}jeH0&:~a>{9 diA@>.;踥/zJ*( ',GATMkӦMKAܩʔ\_@.b^>/jMJr]:F{վDʟIPB;C;ȑ#|ў^Wovm^ ͻzHG'HBlQsub1_YsBYMA ? :T[t})ɍg!{U˗זaGI&B蚅>Kq=]1qDm]v<$Ƨ߫o4lӦMɤ ?~KZt~4sz*M/͛7m۞\*1)'cT̛/^[jժe^q|6OЇXkcǎY\5x`mv*Uz2$V3"Y^6!DrٝhҤv7QFzL2cc t??W(q;~R_x.!'e{.NYEvI$ݩ0-ZdLnGA:'e+*I'cK'M2Y/gq$*ٽ{*V\;^u>N3Y8Kt)f#Nsh[gd %1}͙6}1^߻w/%#^AN' wM">͗3Ey9߿?iRn4@1ާuіEA .@+aLX^=mywbF}GSO]k^_~(>>)gI$= ’\rZ9(?H}>lڴI͛W[/{'O)չ>1guΝ;i'ƺu,;v<;|v<3|Pϊ/mK6ASq*UhRPr>/P26mX_ ^ROw|6G G͚5:>}hIB8QeBZl-ǎVZӢ|9vd{g!>3ՠAe%K?ym>ŋk˳^ws>Hmtm ]kY:FkG~ n\;Cm^ߎ'x)'䲇 :HtxM~&|%\v'֭m^{Iktt\P!mիWgLr%cr2s v>* K"N\`*\xlY~Z8ulܹsi-ώ'|Iu ָqcِh_7'crIv)f#Nsh[gKo-gz [oզD+/_z \sAId'vX SB91~xm;(5j'X߭w|qs`h;}-4hŎ%C|Yvu,6L+N̙3䮡|}v||rL6M[E֭%\(K@ҷo_v 4O~Z[EŊ|sM4e-I5u|]ve((={䮣߅OQvm>t5ʺV۔muY=<{iOr]-U6?Enz\Mǚ z9+1Hj*m=vPPߏH.{|uVugh˲cΜ9|o]OdRB8Zdԙئs ر<;^Hk5kgDUٓO)M 2m䲇 :HtxM~%| %\v'h,v\Jkt 6䳸>kǮ]G9;q,\B$O$?H.SA^$_6?O>>KZ^> ҹ瞫mE L>+-㓇N6^x릠6{?V;,X'$\2t *\iN7|J"N/_^v V]7k#5Hq~P_x.!R'YE~I$)ǎڛjժiѼysuqs>Nf;T%KԖcGV"p6\~z2$Q֔o/?Ax@B' 93 D}#}ɍe8j&hѢ_~IXr|-}oK^l[ƍc> K2>u/_?Ei@QF >iRn~>6l jD/2Q-K/-\r T͚52Q8ldw^{m}v|r5_}f|?K"Q,v,^O-ҟLu#Gj尃MggіCA_ߵk37n`%#GX۶m)mg6A_DnZWoVmtɫkn ^H}s5}+۷g%\'w-ܢW^|RߏH.{~B:͗gG޽@1_u*]TKV+:Jo$JVtA/%"$ķTt8):"#tJ.Tg{?_~>z-K=3{{f=3{\s\0fc9/  %g!Iݻ? /P&-ʴiӌuyE(luϨܶO?L9UV5ӧLN4ќQM$%Wy6DVdL)>Z0ƘQþm{繌kr; Ґr.gVY @y*+V8zs͛7/9i}h1RƖ⹽Dǎ_U.^<ƺQ ? AƓf_"_|5ќ 6lh;3dMʥV?|饗9C#pTگQq"/HC>ֺ4'ZYe,M4=( > vڢ[-? /8w}ۏC!_(10a'%КcŎ-ݰahѢډ"+q-[4օœpy"xR񢵯_"rdۗR^h}f 4ќl׃IDJ1~?la݇^,fl֭[6Y._^.aG`>.Ykj$#?DDD->QD/c=/={~/gC1QjU/%ʗqc1Kžey~[&UFLۓO>imu=q2yl#?^`uNj,իeَ:H&,L/Z_VXDf͌uƅ^lٲE.d/TW/\P&͢Eygr{$_L&󞤨NӦMz.OK峐$͘1đG)VPϛ7O&OBm` ${b2yl1>"#zn Ynh{ZNq/Yh{-[4݋;vPN}B8]v}W\!kr;˓t>r^Xu d>>Yh{Pc|.Cg>䧝v 6t{XuƐxo֬YN);4}MQ|s<'"EQpl[g7gϞ6* ?_1rXb%;Do:t0֗T=`s>ڵ3eR_0Nڵm~&~uʤX&׃xeX9"mO^yBla]8`y嬉gA'\v;)c}ywʤELΛe~@}]QF#wsƎk$-_ܹkݾ,[ zΙ3XzcrH^HBuu)ub=/g|`l~?5ќ$E,gʕu#j֬|w䴸䬹tQ> Iқoi='xXSg(PǽI&EO8$22_0Y&MyO+[۩yb9%WyG6:D9 l1nFc|YF)DZsq_(B}v4ќl׃q&y뮻/>tc[;Yh|$?XƋ;~K:G1փ_~}(uQF~[g_"#dIsSlu=c2?^2&1#ڴi#Ƣ׼׭['[GVQ.2 .k3_>UW],YD"vI y_ߢj*c{ϻwC_D_,GM4=IQ? 4hn/:w{뮻cƌY?Q> u2&Ko3gt&NZp={}Dve!$r.,'f,4ќl׃Q&nCY~Rt: ,4>rFz/jժ,^X.:i:ছn2փԩL+l_ u}% Qy`ODyŋ,""ضF6 r܎-~mx QV =/ER/CL0D.#_]ʤ%yꩧ(0 À֭[;~{YԱLwe Lh9syBi4ܹsi///m& LyO+[۩yb9%Wybƾ{˨SYL)|,HQ]YF)DZs8b_S#Dy$""*l""/xEDD[.xvI'ےarh{_^zM#[ v!6XsΑIfʼ ˝3f_W Аf޼yΝwNRX^=cg`%q,k/5jAkIzsu#㬳ΒE ^\w1ѠAdqH^H{[]~9C&8`"~/gC1c|ruD,׻VZۍ2x9:a%4?J&z[+KoѭADL`mڴIn6QZCg>.ıXG18-G`PeXa2OϭHkZ=#-[tzLQD/c=0_z˅/U^؞iÆ r|1{GR/CLWcթS'#OAɤEu8/;9r2裏ʤei66`-'FvYΚh{u@1b//&N( !P/bB lu="k|ڵkg q7ˤVv|Zc͚52e"Xl2XQ4"QGel=CԽ,r*,GM4=Iq= r-"W[BcԮ]^`ӦM ֭[W&D͍m#.4v@Fˋ's@&VSr'K&> |p.ʩfG}OHݫ-uҮ,\ع{IemXAQ'"EQpl[gKo Ko8Q>+!}V&cI:c fϞmF\ xܒj=maI b29 4ќ'>_|5ќ E`RN}4{š+ (; .,',QD/c=_z˅MեK44QA|}D1&;Do1k,[oe͋1bDXײcr~GgruK^H3o<;tիWXonhxdʔ)2ij%zWnlˤ1e~رcerdi9iek;5O$}"dj9Avi{1a\r%5iWQlc8`x/G'P3eAPj'{A߼c= L؎Sʤzb_S#Yik;8"ҲN>y (ED^`"(8ѳ ڠ(_z b\pLCduױ2 94Vw'D&u0XB w]vD#A&id76 ~M.RPF ̙#bQN^_N:yB 4H&DryOlܸQ. ^&9r{~^"6saFZaOXve6v'|rtªK I?JY&h{PY~: D4i7/w>oݺ7/Ա~h?C5փn{1#OϭHcn}r iY'e"z7leL0ȏׯɭ6md,E]$Gm# ~8嬕 0ڵkeXEeipƺ.m۶9{'FvYΚh{}@5j?/O.[ _G%$ar]'eR+$zLJ)l27ϋaMjx1_wo3@6WݓVW_NŐ娉')g!#F0?/"g!or3gΌ# :t0փ馛dJ xƶ7pLKdi9iek;5O$}"dj9A}7{QF g֭rʩt֤]YFK:B}"3rDsރ`Cg>ϻ1Zk4^`+,G\[nIc5l0#OϭHL2cnpڴirEK^H{[]~9C&$`|7wE& MBN:$c^iYn\$4a0׫W/c= .@&M@O߾}m##W_5E)*W4ќ'>_|5ќ0tAFx{4YRN}Þ&5J&#>vXIV]YOr|fY.h{PY},ǶvG}A\3f0F~>LY8Gz\rL+l_ uO3<' =M+""">DDX""" mkl|- Qv9Du]2idZhalAIQF2b„ rm۶ͩY_k@`L$3`#﹁׎;EI^H{[]~9C&󞤤,Y} WBճ k:ʽ% GRmgСƶihp ,^LJyrDsvjHD~Ds0h 3RS-'Ipb/I#csJ:B}O>,M4=,^#4݇B}lk'!rscrl#Bq\sDndXa2OןYkj$?v1tt=t5k?~uR/ ->QD/c=_z[T/^j˗"|YM#[x9sbܹ2yAF2ցq# ~8,u!Vj|4j؞+ֈMkI֭[`#Fʃ,gM4= LyIm/ } k$e~(EX";w6~GD:udҒh9WN>d##ŊbY c3x`#_?\&/&M$GjڴiFr`#Fʃ,gM4= LGyO~m!fϞ-bݻwc3a#?^_^&9s|LN?؝}4ˌQzug׮]2yI4Q5gϞ=}/3l߾].V!۽'yo/brDsޓg!r]^\tE2y$0i6=B.]*RRmW^1^nL2OOvXIV]YOlUdh9Aez0KPܨ];^"~;џ\ Lf{>Y:cz*Ur֮]+b۶ms,55eODA""("YDDDmm_z+s=gӋ?\&^FzAon߯|"}V&q"J*F_|L^!ۀ87UGnqY,S =!0ht֭rH [6i7nl*?MFUUYΚh{hG5믿.o1#6lx3V#U7EbqgA``5{b c^ >\&/kK.q*W^dL? aء\ږ-[<}&yo/brDsޓg!|ƺxweH̚56=BmadTJ YZ&ŋx@b4ќa\o&D~Ds /&MHzrS-6^Ipb{6?e42{6Hعb%B)/V]~'- YEK&T֮t: fz-ճ>k#7 /Y:?4O٠A,8cS7вe{H*>Z]گQ9#K&g+ 1/f]>,M4=]f>N"0K/$Pu冟uJY;g_|5ќ0/ƌ#I s1nphҥ2U0'~r饗FtM&(m{kZ4qY@IC}RYEG&z0kǶvOE_y\`gY;n0օXM6oXV-#/^yn]H;G<[|" ,^dǶ5z|4fvےA֭3ePEo$_Q矝}ئƍe۶mIz-o7~vwXjAN/P\lm拴NW\q^/ڵkY޽dQeW')5ќ,6ŋz]ٳg]vICay\R ڟ~M&\ۢ}L+Vb2/i?WҴiSc]vLZ!͛7ICVW_NŐ娉'B? 2d.D˖-et>=BmeI ̵e~Q|xSOd!NyO+[۩u">_|5ќ0?(/!1EVMf!7<.thkj$˝#?DDD->QD/c=V7xXpBH C5/4 bW~&URL8N*Wի&Mp>ArLbh"<6( ;u,gL~p(*E ÄK7A4OK>`c^`0-&Lqr[2(ʲ+YΚh{h9#:J&u8 #/nDVƽ+TR/ɋֹsgc}jժ9?L^v g})Rb2oi=W1qƾ'/ũjkI+dtˤyo/brDsޓg!q]6mrVjl}Ƕ zs#Y<"+8Blzur' Qy`ODyŋ,""ضF6/iӦƺhР|rHv 8XƿkxiG`/&JM23f믿>A}o /N.V_~X\;@nKF~8dL=Ǐ7֛ڵs6l S> Lsc5 7t'_kb-ҥ l6+`ΆYΚh{h'4|{:6lٵkWcsc=eP6g۶mkl L&Wz%G;Ja^oپ}\$x1$^\R.[%K;p9%ۀkV&7c=ڵkrd鼷S1d9j9IBf͚e QF _C7ac^8ydc}^nm駟:k֬\@ d8蠃͛7E"qW"MDsvjHO,_M4= 2 Lq9I9묳:x"6/=kj%2^\qr~|DEn>ܢE"T,CM4=lm։D~Dsޣi&3%7N>dzȋr裥?]QϺu떼b]'#Mp]4=YNh{PY}hq$Bf(7?㎳xmc0/ᄈu& Qy`ODyŋ,""ضF6/M—ժUsnƒ6lSvmc}N8M4 bCgL~zw~[)8M? w}cU dL>\ۇ5_|Xn`ι7EG坬 +AvT!ke _?տ^`Hd7|]6aZбcG& 6ۄT^LpBxAxq_~NՍu"k/!D#嬉gMpeyFv_D5mekmz1n8HpzQfMw(ry駗t]nݺe{vo. ⡇s3 2y(ϟolϋSr=^9Y9mu5T Yh{,m۶Y BB8m4c^Q$+t'a{hGy4|pv͜9s"ٰaCmzU@&VSD}"dj9QO=(i϶HZYe,+M4=]f>8vm^܆x?ԏ1f8L.EvJ.bab݈0[g,o"F_.oB-;t`l#708 ,X k<^zkڭZk/}Ӥ2;q;agK;vU^}UgȑΡj,/7o$ha@}pEۑ0703gtx?v>loAvE-ke*+ȻFgyywA;wtIJ^Lj+N;Ha@bwO>;9U~⋴;kӦMesmIe9`ph$^5ќ,: rg/ЦO)ѵkWPze?U;^D=$ݻw7ڙJ. yVN-09gn D F|}/gBrK+r;|p؃|1 DwzC%ٸdἷS1d9j9IⳐ`Bl?Bh"<{=@L"VE0aܖ'N I6u)Yf3 *,GM4=lm։D~Dsޣ4vXld{Z&'{/>ZZQMڥx cY/VdСv򅖱sIK><ZT$-ϬD&TzZc}W;#ȍ[oU.RGw]hڹ[۲EXϭH7G<[|" ,^dǶ5z|ͿB_E5b'7[`/&yF-1FE߼+׾iRl`r*oLno"+lp7M|Gpw;W]u;Xeзo_oAvE-KeO)79i1&„:,# /ǴmVE~/X@-p8Hd9k9Yu@hѢo&f͒ U~̘1ƿ#zw0|g΁hl LF)Âm܎`"0u'ǗBEIe>e`{&fvscܸqrФ\u܎x1~6`c^tQ&/Z / '㐅P]%˳A&$> ,_6í[N.>m &O.u{{ 4p6m$1$:R7|shd__|\ RyO+[۩y"%Wy-b wG_8 %\blSK/$-JhiG5ikOa]hhPDrqck;Wቪ.IE*+dYi9Aez05q~/0"&m"{NnmӃ@~'hĈF^^E$,/Wр;.k{ 9P_<΍)SEc\ϋN:Vq~~y6rDsޓg!`͚53E&M~A.V2sSJc"{uڰ|Нh/,ۯ_?cݹ%#6LQfM#OȠ }s=uC=$"ɲDsvjHD~Ds dSO=8G~W>ȸ%j--aۤaLڥŃqr~9K. clg&NwKMc IK>rr8K@k}R< Y^h{PY}hmq$&HsV-/8Y|_n;7?pwLEhNbLo"}Ӻukc}2ڷoﶩQˌ8蠃{_ꨰ_ 5ќ$YHg!/h7n(-G}mX/s_awQ^4>|xIgSNNRE>}VIy'Ǖg#SyO+[۩y"%WyСCrܱcGogR-[1\w8묳|/GKK8I4]xa|>g(]m ?Bqg|H\u hO*gVY Ky*׃Y>\pa+/ז1b^`238-R|$S⽘9 As_S#Dy$""*l""/xEDDp`"^zY'(%wKsEhĈI/0:hyiYn\Ľo) ~ׯo,Wj`P5kDnڵ*70uPUW]e2eZ 9s :*66mL4ZC,'`݉YK z{̚5Xq'ˤEK8Hy嬉gmp { ^.b; L>awB/QL9l~VT00_ Ό3|s7b+YI3_@ߣ Bcnyoˎ8|M+]v( 㐖s"rp^ڽ 77x‰UV5&%DŽ\wnw ~?ǐk~/g#-/ QyOu{!/w}Kh_3g| }?9쳍y/{&/~ 4ș2e;98v}:7t;Q\^Fz9),6'Nt37xԽ{we?wwqs 'dj9iek;5O$}"dj9q¹TsKyO;4>_Wwl>R &[n_J9jr=BώK>ZZQOڥ%ԭ[X |L Ν^³K&Gc믿v֭wmclҒ.IE* di9Aez05IPn/7be89)v`bxE7fE{8flnOq' Qy`ODyŋ,""ضF/ rFaYOL}v4txA/K1h_d\s|APr 6IC <̂8e -Ab$oidc@QE/I:dqF>-Z8O}8}cu:,g?,[L.N% kŋInVs;/c@r?awΝ;*RG~qx" +^={#<*²\7p'B8 w:`Eڷo~]_ }=?_E?tPsq7oNrWH1 Wm?^Cp:J+yj9Y}@(f`Yb(oLx[no\r%r%vޗRWf,GM4=I!9`a\؁pȃN 9D?x΁*6د>c؁kW^yEn|嫉Qic4= Ӄϕ:@ʹ*D!+}㜴+(`ܛm %Xb&2'/g]BJ3Yfh{P{Y~:qR$=]/5WF(>9HDDTED^`"(8DDIjQFakLHFXDs޳ N4n`]v"Qjxɒ%e/?crsId LeiU,Xnط_2d;X= i:W=0.V~΢Ef_N:M6MLy_j]N 5ќ$^.Frj׮ml'8#/ۼˍtQ0ATmx)TX* gXO?dk9D~yO=z0/T{Ϥ~dpܓvv' 'tRk޽{i9`xtq%>I3+di9Ayge@}4Io;&BnۋO?].RP֏#<[s_n3he&~LF,(>9HDDTED^`"(8DDIrQT BiӦM/;zj+p[X\ͅs=P#O~c=p/F/ :}[nhӦn:z4~0pl娉'2$' fڵN^mիrPKW]u><()Sڿˀƍ*IRm_H_q8O>&K&ND駹ќ4Xx KOfcpvUV׍A"p >sEpdrK@c}OZYi#Myv;Kz|:q$&΍ѣGE*; w{a׬Ye'|L }MdQ|s<'"EQpl[ "(nڨSNIRAh{&3gLj^{|_rKoڴ|EqA/r og3^L4l6l;UarG}TZ(Rc=#FNBZ|v̞=ԩJqb@?,W%KK3vY`\]h|E߽f1uuIMөV]B娉'^3qˤիWڵk;۷oIC;C q7nllhѢO`R^^Ȋڜ3fm5&(L޽{Oڗ.\\vey^Q߃Io'MN&ND駹ќ4ټy3~xO̍#<>|zjDh죥?Zd7tL]׍ݺus'J)8N;4g9n :`ZT\$XHi~f,;M4=(=o|0|:mq$>׾}{c^ԭ[Y>ϔ瞼ϊ*f͚9'Nt="t"F*}MdQ|s<'"EQpl[d"4/j25DX݈&UM43%^SX}ŋcu;iٲ;A{;t„ {rvAw}sE9'ts!8mڴq/aR* ~/pm.7(>yꩧK/ܹ;_&M<9ܯvaqqꩧv}9m6TH˹ZUV9suoѣ[{1CNr _$|롇#s:gu3x`w-[& {Yi9T+gȑ$"^GR۶mmy?H57o<箻rN?tNv~taBa+yp9Ae~E 1 iӦ9k׮hnc4睈Os9i!c:s?B yA'ۜW^yR}4]`H|+t#8p͈{Ws 3ydgʕr/렭>hyff4ќw &e8G0c^5oܝc$Zj0f rV/"ױ{Q|s<'"EQpl[$'DDi|rN8x92Ns/Yi9DDny'\h;Q1*8Yvh;V#F0_&J$IDA""("YDDDm%"'9!7nQ!Vǫ&NvZJ*ƱԠAg׮]2y7&KӹJD䗬4ќw""J7mQic4睈"|fY~h;V_~Q"z!'G<[|" ,^dǶ(>䄈]vF eRԐǫ&NpwnI#Ǿ1]U""dQinc4睈Os9DDDD3pDsމjݺQ"N*"$ Qy`ODyŋ,""ضŇQʕ+̋ɓ'D!WM4睒sNAqTreᅲ#Ǿ1Q~i;W&NDD馹ќw"J?ugVei{nVZFxdrRDDy$""*l""/xEDDV"p" Ӻu;*UrfΜ)aÆk׮ɉRCh;%oƌ18dXoL_U""d=Qinc4睈Os9DDDDUxdj9DD-_ܨ{XjLNߓ#?DDD->QD/cJDNrBDa~anݺ#s֯_/Fj֬ig-[D"YM4睒ױcGB+2i,7&/m*_Dsމ(41ND駹ќw"""B*< 5ќw""ƍgԽ ʤM(>9HDDTED^`"(8DD$'D͛#8¨G=z#ӹsgc^29QcVydЕ*U2;vc1)*_.Dsމ(41ND駹ќw""""> ,GM4睈Hvu/ /IIQ|s<'"EQpl[IN(;w:͚53/{9H&L`l \(UqS d?nM& D4DD~ɺLy'"th;:Fsމl*\5ќw""ʕ+z׋ɓ'䤌M(>9HDDTED^`"(8DD$'Dŋ;+W6ğ'fҤI6vx"D#[M4睒/8k6%Wcþ1\%"Kgh;6Fsމ(41NDDDYO&NDTu9w*U̜9S9rÆ 3]/֮]+27% Qy`ODyŋ,""ضŇQXj.ɍ=zuNƎl}ݻbD#]M4睒3j(AtU&Ds/Yi9DDny'\h;Q>|f>Yh;QvWu&NDD馹ќw"J?uI|f Yh;Q1v4k̨x"0a]/7 ["dZjƲN:믿.Ej8Ds)~+WtWn71ccߘIJD䗬4ќw""J7mQic4睈(YEG&NDTŋ[??y"4i ]/^|E)%["QD/cJDNrBDQٺusKTq:۶m RAϚh;qFOyy9NjO=\$SVe\%"Kkh;6Fsމ(41NDDDgV!Ty'"*Ŗ-[:Ȩr㨣r͛ڵK.^O?_b?79grQRLDy$""*l""/xEDDV"p";M65꙰M6fTǵ&NYƩVөS'?;NJGqo1eQU""dݦQinc4睈Os9DDD]|f.\5ќw"RF/ׯ 2Y|\I͞=ӧSrecQvmgٲer(>9HDDTED^`"(8DDٱcSfM$'D%K8W^y[:Oo$oM4睂8p~VZ9 p{1gܸq5\p NJ  )x%7M4睈Ms9D~y'""3te=eR"?A B:ml),+; *UR{`!yaXq~γ}],,<>쳳y/~￿}G}|+r6̛ݿʕ+W* _k`?\|"Y,8`_w=<7Tx0ٮ_ݸqcuvΝ_~߿58;Ps~oC6RԜg;o:ߘowLYO.x㍇o5?A(Ks[I:wo| ?y_~C_:SW~u[uv=cʿƝt~;_O?}K/m_~erO~|`H,% .m&܁:/{_SO=u=?/'T?wt~;t~;p|c:8M~xu'(}w[ou5wy/o_up`?\|"Y,8sv.ۧ~ ? +W\^|~ݻwa7|1hT?}t~;t~;p|c:8]~X{'P͛իW~{'<۵k׶>h  .>@,XJ\ Lҹu~;΃O?}'|}۷9%YkploL;3c޽{?pv_ׯo7nnݺݹsg_1py33E`)Ypqn+0Igo:ߘowL?3v~; '?>0P$ t@,vvv:t~;3cpy33E`)Ypqn+0Igo:ߘowL?3v~; '?>0P$ t@,vvv:t~;3cpy33E`)Ypqn+0Igo:ߘowL?3v~; '?>0P$ t@,vvv:t~;3cpy33E`)Ypqn+0Igo:ߘowL?3v~; '?>0P$ t@,vvv:t~;3cpy33E`)Ypqn+0Igo:ߘowL?3v~; '?>0P$ t@,vvv:t~;3cpy33E`)Ypqn+0Igo:ߘowL?3v~; '?>0P$ UDDDDDdR:ɷtI'vtoL'vJ'vy4?A(K*"""""2)EDDDD|HU:ɷT|?|<ϟ ɂdmn"""""GO7~toL'vJ'vx}gfpdR*"""""2!]埇QUy@,XJKRئ\|"Y,ܥM3E`)Y,KafpdRXr6ɂd.m (K`] 0P$ )`H,% %w)lS .>@,XJKRئ\|"Y,ܥM3E`)Y,KafpdRXr6ɂd.m (K`] 0P$ )`H,% %w)lS .>@,XJKRئ\|"Y,ܥM3E`)Y,KafpdRXr6ɂd.m (K`] 0P$ )`H,% %w)lS .>@,XJKRئ\|"Y,ܥM3E`)Y,KafpdRXr6ɂd.m (K`] 0P$ )`H,% %w)lS .>@,XJKRئ\|"Y,ܥM3E`)Y,KafpdRXr6ɂd.m (K`] 0P$ )`H,% %w)lS .>@,XJKRئ\|"Y,ܥM3E`)Y,KafpdRXr6ɂd.m (K`] 0P$ )`H,% %w)lS .>@,XJKRئ\|"Y,ܥM3E`)Y,KafpdRXr6ɂd.m (K`] 0P$ )`H,% %w)lS .>@,XJKRئ\|"Y,ܥM3E`)Y,KafpdRXr6ɂd.m (K`] 0P$ )`H,% %w)lS .>@,XJKRئ\|"Y,ܥM3E`)Y,KafpdRXr6ɂd.m (K`] 0P$ )`H,% %w)lS .>@,XJKRئ\|"Y,ܥM3E`)Y,KafpdRXr6ɂd.m (K`] 0P$ )`H,% %w)lS .>@,XJKRئ\|"Y,ܥM3E`)Y,KafpdRXr6ɂd.m (K`] 0P$ )`H,% %w)lS .>@,XJKRئ\|"Y,ܥM3E`)Y,KafpdRXr6ɂd.m (K`] 0P$ )`H,% %w)lS .>@,XJKRئ\|"Y,ܥM3E`)Y,KafpdRXr6ɂd.m (K`] 0P$ )`H,% %w)lS .>@,XJKRئ\|"Y,ܥM3E`)Y,KafpdRXr6ɂd.m (K`] 0P$ )`H,% %w)lS .>@,XJKRئ\|"Y,ܥM3E`)Y,KafpdRXr6ɂd.m (K`] 0P$ )`H,% %w)lS .>@,XJKRئ\|"Y,ܥM3E`]ɂ%""""""""""""""""""""""""""""""""wNP$ 8}.>@,X"""""""""""""""""""""""""""""""""E`]                                       _I8Y& A - K0@ l)P [dQQA hl$,!5,j,1PBPb 'PuSuϝg9=}^U\;=f@ `.f@ `.f@ `.f@ `.f@ `.f@ `.f@ `.f@ `.f@ `.f@ `.f@ `.f@ `.f@ `.f@ `.mnsI$I$I$I$I$I$I$I$ig(,I$I$I$I$I$I$I$I$HϊP$X$I$I$I$I$I$I$I$IґH$I$I$I$I$I$I$I$I#l?+>@<`I$I$I$I$I$I$I$I$IG ~V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr`R`(,,Fy/)V|"yr֕{IT˙$iA9I$pA9I$]P-gN$IY@E#$mJP%gMM I$IG *I$io*9k$I$eg9@E#$mJP%gMM I$IG I$i*9k$I$eg9@E#$mJP%gMM I$IG I$i*9k$I$eg9@E#Jr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! XGe$IK),(IR5 ,J$rUrr%I^JZ΢$I$I-9v =GHu!_Kr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! XGe$IK),(IR5 ,J$rUrr%I^JZ΢$I$I-9v =GHu!_Kr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! XGe$IK),(IR5 ,J$rUrr%I^JZ΢$I$I-9v =GHu!_Kr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! XGe$IK),(IR5 ,J$rUrr%I^JZ΢$I$I-9v =GHu!_Kr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! XGe$IK),(IR5 ,J$rUrr%I^JZ΢$I$I-9v =GHu!_Kr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! XGe$IK),(IR5 ,J$rUrr%I^JZ΢$I$I-9v =GHu!_Kr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! XGe$IK),(IR5 ,J$rUrr%I^JZ΢$I$I-9v =GHu!_Kr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! XGe$IK),(IR5 ,J$rUrr%I^JZ΢$I$I-9v =GHu!_Kr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! XGe$IK),(IR5 ,J$rUrr%I^JZ΢$I$I-9v =GHu!_Kr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! XGe$IK),(IR5 ,J$rUrr%I^JZ΢$I$I-9v =GHu!_Kr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! XGe$IK),(IR5 ,J$rUrr%I^JZ΢$I$I-9v =GHu!_Kr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! XGe$IK),(IR5 ,J$rUrr%I^JZ΢$I$I-9v =GHu!_Kr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! XGe$IK),(IR5 ,J$rUrr%I^JZ΢$I$I-9v =GHu!_Kr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! XGe$IK),(IR5 ,J$rUrr%I^JZ΢$I$I-9v =GHu!_Kr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! XGe$IK),(IR5 ,J$rUrr%I^JZ΢$I$I-9v =GHu!_Kr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! XGe$IK),(IR5 ,J$rUrr%I^JZ΢$I$I-9v =GHu!_Kr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! XGe$IK),(IR5 ,J$rUrr%I^JZ΢$I$I-9v =GHu!_Kr͂*9k9$-\JZ΢$I.P%g-gQ$I ,J$IR3is>X! |^W\qu5r?pIRYP%g-gQkTYY$IR~,J6w}CIz/M>Vޕ{,(iYٛI$i9v =GH-2Y w:C9D>|.IR5 ,Jr͂*9k9$I/BEIf{Nw9(iYٛI$i^v =GH w'W $i)Urr%IZJfAEi73/}'pG>vǹNÏ?YK'eoO$__*9k9%3JZ΢eeo&I{U3V|"yrZ>^tys pwyp-O;y֤;'&+IZYP%g-gQުn;s^O|\6|eW; m-BE{6r p70\|-<(op\:p≓yڋ ޏN~]IϮ7K5Ã{p<Nچrm*9k9UO:|ԮɧL~;S6xiSsɌ?9V+eo&mNG$I^g9@E吵>lN>|e/]({ TYYԱjoSI:< /M&ITYYVKr*zzeuHqYLOv37&/mz9P%g-gQuw{c'&1]۔Kf)AEwpe~ͤH#m.3V|"yrZ  w'W 2Z.S=,[ʏ|iI&۠JZ΢t^ϸd~;.G܇^84v2xi9*9k9ꯣk~ޕO>g=wmS.3Urrsg/7{3is:z$i[v =GH@/õVż %T~*9k9:VmI'OW w&_$mB} ,Jٳ_GY4G48mݎ;i/%Ist_|_t?M>OrơJZ΢hwo|ަw~:4]۔Kf)AEw\ًLڜx?"Ir=]>C`(,=Ae\;eѪwP%g-gQުN=⧛O]0q};n$ë?𷓯S^~?*9k9ҡ5w9\q1Oṯ{?7\o-WB ;6~들^ο|?._M>OrơJZ΢H_|ަsOkrɌ?9Ο8Z~B9i=~Dz@|#P$XY[u ' ]~0p u]CtM.ܝ|^2\h_̻PRO@cros_^p?EO_cU'rDq2(7ܺ˙'/χ_q7;~p+4oHw_~[g^j缘׽|/^g|CEתw͇vq /;ד]zޏ'ܵMdFڟTYYT{~B{9ZI6z+>@<`9dpx'&.v袋Ca\;eѪߋԒM)BE{sm^熋k)ne; Wt!IK-AEiW L.#M}[/3>umgEұu[?8~ӽu⷇Sp9{^L>OrΡJZ΢kջl,F/w3ξqwmS.3Urrު^do&mNփIҶz+>@<`9dpx'.vr K/õV|jIRP%g-gQ~mo =׎|{sx%i0( /ʡ=ϸik~ýɯwh;0e[R_[7^o|6uZp]'ԞWNUڦ\2#Og,Ua?{LڜVI6z+>@<`9dpx.dӸpwyp-U1_Z{ TYYԱ{߾ᴻ9I1[{:OW]Y1U]E?qm>Yq .UWy[8]dJָ-tTҥE+E\1@AAF| C,L !(J0x{pt/ Q B)q['n͊Uҥ(@\,2%nkAzt*SރD(YZ[ SmZ{}s: NjeF -ҡg'  3 SmGcf$xȿx! (ˆAdH|Iw! eBIo/|6j@!@2%nkAѷiڶ\J"-̂ Sme{~-ås"=D-Am"Hf;1]TR" AkҳFłSeuTP:piAf( 7Am" H#l6D;BAP: Ȑ ,B@.& &^|1݋Bn@2%nkAѷ=q=.&ByAm"H/kzK؇  *h52%nkAЭ5gp'?Lqi$D٦Cq1w͏; 05n?:{/@7 9>@AAF| C,L ?СCoW3gT3fP7oV{U7|ó_WLmݺU-[̹)S ׫{G])Vzꩧmݦ̙N,YmۦJr?\>|X=CjڵN^jgUGQGb8ۋ_}lٰak>;G}ۋ/x{1_ȍZd7Ԝ;PW.M <_l1gqZل&l냕Vݲ[ J>^vrݍj;ռO͇>y@g 5n  D\aյ7?F-ؠ^9]sj6:+myL1ovڰ%WspmjԪ}|q&+O+yAm"H7U)lRH6l ߧlz8wX5j{"[p܌JuՊ۝|A@ɒ_tڅL?=/Q_]4is-w٣/-52%nkAЭ5k32e}~³=vɄ mv t#>A! &Ye[@ 6oE*% ꪫR5t/˗W:tp_ !`0 ^rV JzJ3iol.SV:sycea΋l\ĥtߑkSgQ&QUkΩ۸|&T-0_ Ȕq[Gʥq|&>_h64VҘF4KQf}|M[clUr uPsM2&>VޮdJָ-tF\LUqĶX:oDUZ^UZu8SM(/ܹS+u6r EE:dA+4lJyxCn9AyyAyZ׾C-kpiw^k_: ӚIt0~`([T<8l9LE>tk;k |Y:(J+(*W3;\I~n=a8_swаU[2ld^}H畸y9>A. @)q["7Q~˺^!}3Cb%q>Q>b$<3t4$gd oCȿx! (ˆAdH|IwĽ{Id[nUKbȑ@oծ]T۶mE~T0ay5O; \sR /PxߢkeI&… UݺuE}~@scǎUo&"`/_*UJ\W(@%Kԧ~ʋ^Lb8ܨU \Q[ܻhcS /Q}Fr*/;c=缔@w S+ٌE\q_7<7nF8uPL_{cŵ Am"H7z]$lĥx"6q 꿢D/=$˻e?W:u鼲m۶DR:te ;w?ɫ Mv~yRuQ5qDU:VY7|ë$FD=@l /~:w,'}7!`0 ^rIV>~ykO9̞NN/NRB%u"_u4hFB_.y>С{:[~Jب)"WZ)LE?qm˥z\-&WqyBQ苳a|[viG@?iQ_$NJc oO2%nkA9)F\WYpBaN\h) @(2F =}|#yE&dHTuCsy}^@@ɦCGCT  cn 5n }֚u)uq:167+uߴs0)w͇mڋk-)肮(;ϓϡp]$ ];9>WxC)q[?&}Ԓ뺞0Bx>hG64=*tG! 3$SIٞ&d o;ȿx! (ˆAdH|Iw%]w}/Bq"W?x_Pimڴic^vtE}'PkgLWz鿎/ R_}Տ|WTFDaҴiSo{{G+AGv%f _ @bL1箟u&uӠv*lwB˓}<_ttץiێyop{dC2u伵ߧY[ߵO֮koAnx@)q['͠kq"\ ڰf|:MY trQoX$NJ[6 Sm?&l$֧ZEql' a z :\KD|9.|-3't~<]XЁ Ͼ#$!6E}aB1 ; 2%nkAЭ5C/]VV?:>'ç߶G! q~n~s2fͪ\yqaѹ`' g )?HеAPxC)q["8&|\y]OsRϥi]Mb2($Q>?I}fᶮ$rg{xl&S8xAAAF| C,L+-oX6l`z8q('pzWe-];oٲ4?N>d&lv([ڵKU[ԨQC={ zMлwo'+ &^|1݋Bn2}\>MPcյ7?( o(ˤǰDz7jC/<~HJpt/Si 6hE LE?qmBxz/l_D5q'4U*#(q_/_IaK">Vm Am"tn'[mx]/8aVk":p>,^9]L_}ZkߴH6MN>ELAd`/N Ȕq[C֜C:Uy\~a=u-~6D;BAP: Ȑ ,JK5k8:4f˖-՜9s߮ۧ}Q'HUrTZԗ_~jog܇\. TveŋϹM6S}FSre#Kv^nwyGխ[W=+3f͛7;O;vK>۱C/ƍcWX O5ki0~x~ ߪ믿^\K6W._R:rzΝ;~ &^|1݋Bn z!{y6~#&Vw6{r^2EExCDy.u{E|YZem"W^zuP-z!k$5|*5qO۝M\#}Yv3ozA~ SmO}:!*W.zmڋA_$.S6==sr+jմA֝EW<ˬAz{T%YNN;^pnOՒ_tw9_XCeAu!>V\䆷;5n̹78Z 7Iu+S9qe\*UEz;sۣ|ߧ롿߰:FmLWP ʐntgUq3TS;;}e5vziiTQAs< g !! p,ٔQؚNݜ{h\5vɖ[^<:< )<2W/g0/sμשYZ+5sZ`̵Ny86G5&C ۏNеAP@)q["^>j69ȿv&|~юld#{TÆgn ]g5/ކhG J0C`a]i H(w?_׿HD_ITVs.ժUSgV1/BٳGq<۰a/"tdsKǎ֭[JO2+U$<<{^EJ\Tx:zڽ{jܸ(+e˪$]/υ6TE/(z\_$A&yݥMb5DyP?҆ zxA)q['n͂kqMR<(@/+r.UsȫccU\|y΍Qy6l{=|Iv8ט+HQҥܟd+J?5nG u`4&IƱijÿsDQt(/Q 688yЦ Ͻ}>:4/M3uerӁԥ3 22f8o.4O@^zS& /3f"PZMOa$ p69^a:4ʥ:s͙ LE>tk%p' ,B~=hG6t=*tGa3C i}Ć~q}~[X)!mv t#>A! &YޕfR)]wŋ Uk׮Ljժ֭[D(/7ҥK;̙v!8yQG={239c_W5E 廜Z|Hω;d.gdBs˞ǫFڧr拼\sV;隵$ϡp]GIv= 2%nkt0^x#*5$||3 Cs7^~f=aюlplݣB7`}?<3tp[GlLl/q~[X)!mv t#>A! &Yޕ͛y -[&OTG|oU3ggҸqA3k#Gڵkѣȋ񥯿Z 0@Ɋ+xC?OYӅ^u,^g,>p1]V~i-o߮ʗ//hԨz'ſ[b8ڨt(8Wzq:w: x.}Ny+01EP нLݠ7z:E SmO| il2="O.h315Vx5n6gRq 5x m7c;:9~/ ~K5j}(ӥiێN:Z3;j٣/2@~xD.С /i;L%.u%Τ"_lЇ)3rW2ஹ~mom@)q[҇nw-?a_/CUr(u5/>s4C[0v/W.MU$UQK}Mgϡp]G6 ];a 5n?:{/꒴u=oFsW^ 틥@<_l#Gnʕ\.wvq\מ>нLkn&'5n tq.Uu$r7t>E΍_yPu;E].A7`ec׵hCQ< p*/#tE.cʡ<_WLy'v&qndJָ-[kz62ߥYNy/ҙEl>?W㗨\g](µ7?(Âsz4l*=C Ï&lv`x5n?:{/aGuIں79b(ׅIkws=/}RJ.xg Bܾ}ۥnݺ꣏>YBW_-s֭OI6-"t~!y睜 o/|!6jB_e/}MSE+r3]*҇e׋\k< =v9@E*],O3=*l6]2}H[X dJָ-3 p"5~vǿeGMv3uPH:dE]Ȯxz?oqj?=sUBEQ˘7<~5{q>:%hNct,7Y{xӟ<=H6 Ȕq[Co|z6(h/y2;lJQKcU)?2Bs(\ф-{BNpہ SmGcLDt>G٠-F߰Q vd|&=*tGc3C $m}Ķ~"lOֳo k2/ކhG J0C`a]i X\\̳ѴiD.'|ͳejժ?,奝ԩWz~+?3UJP`_WDO?̳\3AA<нzVXKvfkWy[nXRҦ&So/쁣DR%joWT S2%nkAZ7 e%,fjSoa@uj:Ez?z|mrR{QAy0|NkߡjE*to> SmclQj'l+TUx}Qi61A X%~fA!;!AԨ{HUkra+Xcq/$9jVmE.6]h|ՊE^=B)q[҇nwbAe,:ߔοYG0~5U~٫D0bfQg69|h¦N`5n?:{/cGM꺞7`C`O]$Mva#陡{g =۽&-!mv t#>A! &Yޥ Xti'WꫯzRIy-t}NE^{ /=N9Q˼yx*g}g MThٲ%OH xmz]f͚œ={z]/_Γ([eƍ<}FԧOQ7s_ @b>ʍZa2l q/.a4Ax.rkEy_uȣc#/e*QlY5sۣ"l냒^a$ ޯdJָ-)o3yý\s$*>il\@a20~AJW x$(8OIl _ Ȕq[ kጃ#lN\il'ks|0`Q^jg׈z\yF}bƝ"_Xг\N~1 /υiq$uĹ I2%nkAЭ5=NZ|(ǥ%sxQ Wsa 1暣6mѱ'lh P\m=k'p SmGcE0LI]A}Ikweʊ\|w(MN>M  uYU6w(eEz$5n }֚p_תb, yt~m`  ADu?yw?%қ`j5אI>uM4't{nCdJָ-a)5z:#o6=@PYF! !A'XdyWڵg1" &6mKܨO.婧=v;xе~Q˾}xr*T?څq ,V^-wy7y^rӢExrZ`L0x{pt/Mo2?1EzSP=^{齲LV*kl'\`IK>(eoy@ Am"ȟ}?pƂ :9mM ʈ;4BttHB7k-$Pf |B?2%nkˌm{Ytq'v|>ɇ {N OGPZ7&i@!s׈k G ];`5n?:{/u=7ﳪr^̣x$e>`# g{$u}Ox'^o k2/ކhG J0C`a]i XZ5g ]ul۶'7>Lk!F{RvZwӢul߾'B\|([Oyrc5,\')eժU>'7zK\C&[b86jY>{)S6ya5\0Zt蜗 @y\f*S6Q[{X}e*}< y SmOT>qP^W6<8~.OBGKWM|B?2%nk 'W.Mu_XYؚ&JM[aqzA>&'┳{NjI jByXnsV~-%[DY.S6ndJָ-[kzIDATs)2F F#Dz/z~w]8:rS|/M6'tD`5n?:{/u=7Տ/piL"_ ێ2w xd3=É$ d>s&Sx?@AAF| C,L+M{œѵ^+&}!*V%*W;7'7M5kUmڴuwOj\A <')}j׮-^|1݋7jEA-}'v&ҚZu/E.o2g Smη/.kWPoy@1oV}N>R(?0f٣/kM /=HB=GLPp@c7x@)q[@lx'i=r* aui/sH^2p|>,οb8p6 DHoZ l׭(CGDz&5n }֚p'm٢rLھWq5 |k!xsӨ),Nf"m4js|/M6'tD`5n?:{/u=7[kZ_hxb,(-$w xd3=É$ d>s&Sx?@AAF| C,L+M̙ÓG˾ ֻwo<2رC\ymƓӠADļyxR*D^{MWvuk߮]9e; hZ\0x{pt/p\Z)қ |Hz[]j7h"G_\z$}ejXm [2%nkA|B@AvA7ƅs׈{# z%[E;M)6X 7dJָ-6N0H_V>{(+i۩y.QÂӺ_(&(\g߽GMi`S@:([HEtBmLE>tkp'.7CҠEDB{sh!QKjM/%C G6 ]; lA)q[ ^H9fyqx^: ^l!L;J|V=*tGA 37 #&~³=Z;t٣g{V.~! !A'XdyWӦM+zR@cwC?/u;x矫ˋk"ϓ\OtUW'U;wɍoA4lؐ')e>|'DGۋ/x{1W,,ƟR7 }_QN}6\Ի}mwM~rLgo!Ȕq[mnjև%+&BJ͇>T(΄(6X 7dJָ-` 8[MKIDy^Hn'aQܟ ֝i@;DzS626QK}@.mB)q[҇n97m%u65f=<p r\fn{T%C=N/M8'tD`5n?:8j";QA%f|.WO-yE&´lݣB7`}$p=É(Ǡ0G Mg{r~[X)  mv t#>A! &Yޥ XL4h7C׎;DDQQxHEur OZr~;ܘfΜ)'F͓V!]VGTZU}ij VRe,xRߊa1︂N(4Qo<)z/'(7O^2}(5B΃Ȅ'5n }֚=NOYtiQ6QBEx/OK)~\}y҇%z E9DZu< MPIKqMD>.] ߃ p; SmGG^&z:#oF><=T+aQZQ> |fDRG Mg{r~[iqȿx! (ˆAdH|Iw)̓e˖zN:kÆ ⺈5k%*W;Gp/t˦B6mڵ+Ov)OOm2c QѧO4RC\ۋ/x{1/'hri"m؊Et9@Rbeul]}eG_A2} Am"ocڤӦs1gZqMƱws6[MV],>57Pyw?%$Nȇ$Q7.;|kD9DS ndJָ-[k;qŗ]hM+]\w(/q<?77a=GL'v&F ϯgޥ ~4aе{nGdJָ-a`w9rf=4QB%5c'n͎2w x{GVaC >s&Sx?@AAF| C,L+Mo5qDQ/1`4rڵK\W_}œ甮+Uē5~eS!^z饢>رW_}_Vˈ#DĨQxH5|pqMo/|v_܃Kn"u~qAY䛪D%qf᢬|rL5yD [2%nkA|m.^Bj;A4jJE1#԰Y+iS7g_%.+mdvF9<aXq7dJָ-P(@pQ͹ _Gvy0YoEDD0Y됨z8IU(Қd5 5)`CE9Ki(>!Ȕq[COW_rMFs7IC |s{zAS"mP|] G6 ];XAvAm"&}]Os|&i3z"Oe>`#g?ĽlO]"&Sx?@AAF| C,L+-˖-˓Q~DĄ xuP>{Ǔ甮WΓUXݲl^_\yket]A̘1'TӦMD `n ^'- W+3o-GG&-Նg䋭}{ZLY$޿dJָ-6JRg回;._hE`C[HC]i/$A6i|t:뢑ZplA57ݗa`zx@)q[@0qSΩtil ]@:$ӆImM>=!cDZ O2%nkAЭ5uQKjUz''OPs |q"mP|]a{BNXAAm"ދ &}]OsdxLOA+IlݣB7`}$p 7I#%~³=~x}~[X)/ mv t#>A! &YޕժUI} /I#s9kt˦Bl֬/|G^(/Xf OVZ%@A<нOZZ7W<|/zQTE0t/S+V*҂LE?6^{YB~ 38ݲ ̯+UT ߶G6Xq Am"q@wxy]oInmMGM7m%!O-P(}B)q[҇n9i/uDQE9A; ϭ۸(h\6J.4O\ϼ+q~O (ܖ Ȕq[{>A|Ԥ|MX})*RDI(-[}ЍXɏ$>3\>M$]šL}oC#AC! O0*XڵEĶmxȥksJWVT*nT+Vŕ/_V˱+ vF*_ۋ/x{1Z؎C\Y[6\, B [@25 ,!Ȕq[coSc׍"3~𝿴RY+6ߵO)>V\?dJָ-N["5=<6r֊ ndJָ-[k~QAwE9A;pϭXQAFɨ5̻?D%2%nkt0^OU7Hʺcև}\(=AHe>ǎ =*tGggx>=.-/ mv t#>A! &YޅVZnnI "]իW9UvvV@l:QW\)**◟S6K~zxHw^qMo/|6jm:tD\\)SXoNu~25 Sm MI\:s*Uǒ1FUvMNjj7hiA % 7?*T"+~Wt `m"q=ͺ*]Z-|y'bk@a-s=Ư.P(}B)q[҇n9ĈkD=:lz@χ ܜ;D(.qMD>0/W ?{"vAm"ދ|s\i=LcY4uqï#H3#QH6Oyx?! !A'Xdy I#7|+6[ns*n:ED#.2yذm6~9eiܹ'T<&s_ @b>it}N=H 0lJqa1u_[M [/S_2%nkA<qo(W:뢑e?=|CQ m4PN|FwUzMQn&~7euó僧K8W, uPݲ[}$Am"qbO9ݺ/p0?$g~M"-'5n }֚MpC-O*t;T͗( 0\?xŢ"mY|&"yWgAm"ދx:?.)z|%]WͥlQ9oP&^)l#Q>⟤?3\r=yZ>I >?/ކhG J0C`a]5k&&֭[ǓF?\}Q< ή MxI'-[𤉒mҤIQqF4RQ-^|1݋mԪפKEZY\*sZD025 Sm-M6]5Sfj׫5_@/8WC`us+}k7@fe>VR?m SmAƹݺF}=tLE?6) m,|X7_m}XG`b8_._l76X6Am"q{v0) }38Cũ]D9DDZ O2%nkAЭ5:N0[rM"}<? auGMiwsd~MD>=v;m LE? GMR?ʿx bcD;uz;J|V=*tGc3E zaK?ٞg{\~[x_@AAF| C,L ȑ#EĈ#xȵo>q]._x*t; +[6" ]73f O(f/eW^y%ON5[b8IۨE/k=]imcK߸|25 Sm- i]=494ba',`bJCװU[qO 8vQ},Vx@)q[@Lٲv :ps0M!?.6s(i>EZ O2%nkAЭ5$˻0\͙3GMtޝ'\۷oETP'-QngWat˦B;wѣO(f/'OeI#U߾_ `n ^'mVˮ@pHk3oL{<˔Uo#[/S_2%nkAdEkRmx'Lt`bJA/_/L((Maek2%nk#4KԨ{HM{ JC>lBLE>tk&uw~?HL~n~AԶgiDwa{"v5n?:{/?u=ϑNP}YA=hHɨD|#4;J|V=*tGI{fQ\ѵ~³=! !A'XdyoYMԯ__}}.yyk6J9U\tE6 nKdJָ-aN>jt>jjϋ.-ڈ|.+VV ? Mv{Txæg1 >?/ކhG J0C`a]#vyxHոqcqMDqq1OZ ή Mĉ_rlMwN.>tLE?6.ߋK׾CEzL% ƃӺ_(х6ox]c?dJָ-t3ǝcl+$g.l(*W^m:tDc{xY 6<_dv8'n'-A)q["8aI_TTY;s˹aHP#/{3`e>`#%c3E zmg{rqm}oC#AC! O0. W}*]2e O~_q={6O^ ή M\/_Γ'FˁD.[l#Ν;ŵ `n ^'m{p?~HtZ)ӥi?:l>~U.[/S_2%nkA۸̹g^\޼K7ED f.i_=V8q.^6c2%nkA:Y:ߥ'԰U[{]‹ω= \z .//=v;m LE?eNѢ}6̻)UTObjDi]Ktp }/5n lmݗJUlG\QV6MFOeu5i(6!Ȕq[C%?,a4eO?[Ӻ_(%ȼD6'LH7ܖ Ȕq[{}$|>5vV$*$H(M}ЍX)۞.gxưGl'<ۓlo ȿx! (ˆAdH|Iw!`ڱcFr`C@f GYN" n\Ϥ>#5.!Ȕq[C֌%?,a=]%"ʔ-C7 (W rI& nKdJָ-aN>j._$z:dR [\ߏkBv@KR}ЍX)1 d>?/ކhG J0C`a] Ureq yrzuz<'šIatl߾=O>%.r<^{9Qw͓]w%!-^|1݋$nԢCt^. H /د >C9I}/5n lmFLBЗyZSpHZi+qęٰJ['?dJָ-me.\.^rր"}60 %?]-OiMvz&tH@p SmZ3 \ξs(%y%[5ddbrI& nKdJָ-aN>j._$z| ST(%ehGi$w xAz% d>?/ކhG J0C`a]hFF~ܘ=7n,PƳxR\9̀n\O=(h޼9O#%ʕ+>c<^Un]QA/g1".]ks_ @b>t.B)*R}YO/$˅2DS.JVS6yb[ej: Am"|F &/җ_yD fDW\v MհY+¦usĽCƈ:ldx@)q[G蕫UW+yD ؞?6pе߃?eQY>xR J]B)q[҇nKFE/uM;dbl nKdJָ-a)5z&}aVr3'8va#%c33>+'g{qm}oC#AC! O0.4gyF\KÆ ?ňL"w6lOYqi0qK\jժoCcNdȅedDȪDA(ADDVC+A4$ ["B,MXVJ%"z~ftT}*C762ϽI~}`bo[d9< R_Q_OݭwvgO8&8XTZ8׹uqWZ|}VQKn6뮦Z$s[KPKkq,}bvպFj_s<ݴJ+S/PqAqԅ@nҮ~GVܤ3jmXL>~Qc1tElXG8mҥtiś ݗ+˿җo2Q9m~w%s=Pw.n7~8=OO;s뮻neR_WӢEwxV[mc6Y<^pF~1?oʿQ^n9v[nS|ۧ_⿷-8_4u9mf*8z6(i}]ܦMej7/Z{vmsG(-My^)q_xYm7_;@{\IZ_Zf\blLm}MWv{+-\|L$lznObqM]{5YϦcϸ0S__#Z^^k:n5sVIo w~kͺ7I\Zj_ah_kq}]5{w҆oUS>f[nEM8z 7iU#+nR6G&yrnCtn-=V|.h/CPI\`Yd |_MJ v[Wq38#ޤQ9m_6r-ˇ =%K6(鬳_yG2/i}Mo3_E;sH:{}eh~>ka]}M3UܦMej7/Z{vms]Fe = cNm.o0[x\~{>k/pxM'gjRWıǢN{\k+v:{{sU>~ۊ۬(sܚ ]mX]|׋l:]V|u|_yٔLB6(n__U7jc-Eu~5s[Z׹}6mӲj_s߃i](sfjM>W8űıǢz{/W575YߖhQMOu׸<&}=4IU#+nRqa>2Sι}NJc8@7* ,wҔ7̛ O~״N;_fqT{K8#nzě ŝw9/-Zn+4g?YZgě?N::蠴v gW|1\Q_̏vſ)m0:ʏO{ܣ&y+﹡5Y?]7_+n3h<5Ț]M&9n_%85&fR{ry;O_?ǜ\`Pk ]dmR M/fIZ_Zn%Z+~+mE~5m7z} g$ls_a}gZo~sO{wug_kyXM'׫{qıǢWך}}\֫uw<0wg>cvs) x~vݕM>W8{űıǢz{/Ui^כkcp~S{ΉYQ:&}=0Iv^I뮻n{;HnaX|^oB>^nW;W7_|W^9}Lkl8xb~ߨ3_Ok?97k7^{sqtm9}_R}קޛ}5mt6viqM寉Ixr~ڍ ıǢ$^ۜrOS~ŷ}\z7WЧ>v-߀ivHۿ/n39ryM?}ijc-Euι#ֱܴv)q@g~&a}}ԽXZXcQw=7רM|^s!4ʽkxq4끩Mʵ|o>`M9c>3\1G&yjrnCpn-=V|.h/CPI\`Yd }{KK,)wq__M.}OkfqSMgeTs w^qMrHzMn+{jySK.$M[yGz޼\|g C-wjyfw}iM6);wI'|fl8xb~ߨtQʢUӑ=MrWܓ{3P|f*vݳ~YqxM6m/SQ|~8XܛkÙ6xmӻk[O՛ʏ[xr~5Mlo}O_]fk! ^|~8X8-8K?l`Ǽ_7mNbo&s요 ]׫IfXY۴t}UͰ}|O/Z>+aqA˷}^TNC؀ZXcQݫk>>XsU:7Q'=xbux6󗷧 7۲^[?7uWn>q_8[űıǢz{/V|^Nm^כk9bA|ߨx~M8@l~Gkya>2ԜШGgK ڋqn0TXY)7r;{7Mo__9yNbVF8-⾧lٲ6u//~1r)cǶrя~s7~Ws6| ]/?OZkUy5o}-޼|y睽ۭg5X#?}ATw,bF~7jMm#^RbmY:z^7=qs_~6f9qjǞqaS{{n(nӦq}2jc-EͽIy/MS[m5ҡ'.қ?i^onͷJG f =c{j.^=stG(n[{Pk8_c-ZRYǝ\~3/}+ 7C88gnoߤls_a~'JߩmttM]oo*[@#^}Vqy?J'㚴œZvQmZXcQݫk>>xsT:7w7ټ8>U~mo\;]WP|=㼾qRwc jc-E^|_u׭3mKkIGqv^I;g9_~}dҞ9F>*?[z\^<#t χ>ꪫ[lVJGydoc{'g?no}+}SJ~tMO_f2*ǹn߿K.Iw_myC%/yIosy+_Ⱨk7N'tRo4OHsN|_ ~x7x뮻Һ[tp`{O~f~Ӷŋ[okl8xb>4 =#{_ Zk 3;6|џlM~kӺmRG,7(8L{`å6s-#>VӚ묟.q|早(>PKkq,j77]ޛ}^-Zκ3i[lWOW|mr-_x_uW|t*Ck~5XZXcQzS.~v}F:S+ymzǾ{cUtǼ<ә~k\O>7\W6N=f=1ıǢWך}}\֩un.7l1+_˾M:z߼~760vj}/}zzy}Zj_a([kظyxS=ަ_>&q=0]~>_>2xvXst I{bevn-=V|.h/CPI\`Yd OoEi VKX~!2oN7lrko}3)E-Z{%K'>iٲeiҥT޴nJ+]/w6lS<Ɗj67ONuG_g_ h&O|R}3/m{V]5¦[-6j/Tly^f*j _G0ʏk|9ej7/Z{mMF6]y3<9|4uoŇ|f'@7jm.o05 ov-,5mw޽w͖Aa (4Xkjc-Eij dqܴ-A7 <ʏ02$ms?ة~5wH|zX+*g8ͿOm&C.%8սr;j_6lӋYQ7zkX~Ƈ}n&88XT]{h]k_[k/}19θS5h35U#횤sBç#@%qe58οz(8Xct뭷ƇQ9[v1N>x{io~4̶n=~ umL{w8l6KC?Z,bF~`.A0h{<90{[GvNqs0]~ڍ ıǢ^kIxz{~{e E-!yg> f2к~G|/a4nXkıǢ֯>;U@x}5i\ˋmy?]}|9iu+om[\< Z^^knqt~c \w9qk|(|!q[8ic jc-E^5Lz u͑0ο_s*nׯIGXQx>_>ҾI9g,9dOį/k4QjX.WzԎ1vQ;({EմΆK>6Yǝ\?PKkq,J2m݊1TV^9R#s sWLo)I.Lj-~'?xa?h;W<jc-Eu~5r;:wjy<{g9܏Q<믾\] >PI[KPKkq,}buEz yq'>vS?=hR4nU#k yZG&y)6slsA{: f|J"kp6\X_җ~W|mM6I]w]ojF8nlo5']/~>WYeq۶;o1=aSN)ng_ h07lzUHwPq]LO7}{>sy~o:ѕw0mCSdenԞ~ej7/Z{m&MMմ#l[j7/>/,7DFq~qoͶ,ٶʢU/zŬ>kjc-EiErSs\O]<|dnߤnzԮ''fοzǧozq:@/?ꡮkǟuqX⸁ZXcQݫk>>\ν΍K1(=UoJ~[^}mr/ۧA[o>NXZXcQw=+n>Qi_[kQ|쩽YQ>}=Цqv^[|Xsx|>2 9ϭaGgK ڋqn0TXY[cl◎1ֺ[{{]}+*PYzVocw]Ũ}c;묳׾ަ{m> Kw_Oַ6ڨxAb-ҹ瞛~Ż.C=8㌴k8}.^M{{+wY\ZO~^=.Wҡ/{]Zs ui:{ݦu`qM,?wE-.6siT~]uϏks_NVZXcQs5X:n\|t ]/< Ar3/wS..)~ݠz}W熴t}[>1|}vgVyGFk=V|~8X-owޑ~n} 9Է}|gn/[ o-ZT2S}T~X񹠽x G3>@%qeŸt饗c9&-[,m6iO{W|ӎ;3L7|sy믿>-_.W"xi뭷-2>Ooy[z]wݕ~ŻL_u]GuTizni}M'tR袋__x C(u?Lg\/;̴a6`'w|ZZoҶ; og\K`yx_]-dq΂ZXcQky#ߣN;'}KN{{X~Ѫ6Bf=tЋ_yo/CO~)zziK[=eޛV[}ޱobngg77\W銻_dnvǣkݟ}Tf[9[kbҮ?s?rGqq*P513N{%kx\;sl>MO<އH38XT/׹Y!OxM-އOa4(z93>u\"{us ıǢԶIF4K&1IMܮ]`$.,Ab$IR8X$iTsZ$Ivzjc-EIuĩo)˹Fs ıǢ$i|˹]X`$.,Ab$IR8X$iTsZ$Ivzjc-EIQ'ѯ{[Fs ıǢ$i|˹]X`$.,Ab$IR8X$iTsZ$Ivzjc-EISw+˹qMFs ıǢ$i|˹]X`$.,Ab$IR8X$iTsZ$Ivzjc-EIt}U/)˹7^{sFs ıǢ$i<˹]Z`$.,Ab$IR8X$iTsZ$Ivzjc-EI]/_/i?PKkq,J#I5J{: f|J" D~/K4*9 jc-EIF8gA-qű(In8X$OǟuqqNέM4şe%8%I$M^☥x G3>@%qe "^?%IıǢ$IR8X$IR׋PKkq,JmvZVs{>k%~gjc-EIxd= IsƵ8fi/CPI\`YdpIF8gA-qű(IҨ,%8%I"Zԥ.o>:{q~EF ıǢ$i$irn,c8@7* ,,`!.IҨ,%8%IıǢ$I]^ZXcQŷ}==cJ+^vxzq>n֯_/i?PKkq,JV$M^c1tE0x_ $iTsZ$Jq΂ZXcQ$I.^/B-qű(I8}kZm.7;?ՋqnR|(@%qe "^?%IıǢ$IR8X$IR׋PKkq,JҤv?Nm86xUm \V=,jltgQVx8X$͜$M^܊c1tE0x_ $iTsZ$Jq΂ZXcQ$I.^/B-qű(I]8N:nY~Zz._!J+\/Y#;4~şo%8%I+z@&/viqL{: f|J" D~/K4*9 jc-EIF8gA-qű(In8X }(;6ۧyP鰓LϹ,|џߣN;'t)ie-ZTvfz>R|_Ƴ3ıǢ$i$irnfW˴`$.,Ab$IR8X$iTsZ$Ivzjc-EIR}ٶKsc6))I[9ZXcQ4x4y9K1tE0x_ $iTsZ$Jq΂ZXcQ$I.^/B-qű(I]}w~/Zcw}>$wgjc-EIR$irn4c8@7* ,,`!.IҨ,%8%IıǢ$I]^ZXcQKϽ@%qe "^?%IıǢ$IR8X$IR׋PKkq,J$} jc-EI$Iי`$.,Ab$IR8X$iTsZ$Ivzjc-EI$/^cA-qű(I$IR:1tE0x_ $iTsZ$Jq΂ZXcQ$I.^/B-qű(Ik,%8%I$IjS\g^<#t$Jq΂ZXcQQ)YPKkq,J$E%8%IԾxıǢ$I$ImLڋqn0TXY C|1\Q)YPKkq,J4*9 jc-EI$uxıǢ$I8X$I$MqI{: f|J" D~/K4*9 jc-EIF8gA-qű(In8X$IR5Z$I$)3i/CPI\`YdpIF8gA-qű(IҨ,%8%I"Z$Ij_ƂZXcQ$I6u&c8@7* ,,`!.IҨ,%8%IıǢ$I]^ZXcQ$IXPKkq,J$IԦΤx G3>@%qe "^?%IıǢ$IR8X$IR׋PKkq,J$} jc-EI$Iי`$.,Ab$IR8X$iTsZ$Ivzjc-EI$/^cA-qű(I$IR:1tE0x_ $iTsZ$Jq΂ZXcQ$I.^/B-qű(Ik,%8%I$IjS\g^<#t$I8$I8$IZX$IZX$I$i^<#t$I8$I8$IZX$IZX$I$i^<#t$I8$I8$IZX$IZX$I$i^<#t$I8$I8$Ix$Ix$I$i^<#t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t7t;<q(Ψ(QEEQQDEQApI4F=hQNN991MoNK׫}+]ߵ>˵wwUuWMDDDDDDDDDDDDDDDDDDDQ:/sS#DD'XdQKDDD=>QF 'YDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDR07Q9bODQzIEMADDTe`qEDDD9D93ұ tQgc4Psvd[#DD'XdQM$h 囎IM$i6d;#DD'XdQK#h嗎GM$idc#DD'XdQK#h嗎GM$idc#DD'XdQ__ݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎GGQ~x dMǤYsMJn?!Q9bODQzIKEpݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎GGQ~x dMǤYsMJn?!Q9bODQzIKEpݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎GGQ~x dMǤYsMJn?!Q9bODQzIKEpݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎GGQ~x dMǤYsMJn?!Q9bODQzIKEpݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎GGQ~x dMǤYsMJn?!Q9bODQzIKEpݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎GGQ~x dMǤYsMJn?!Q9bODQzIKEpݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎GGQ~x dMǤYsMJn?!Q9bODQzIKEpݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎GGQ~x dMǤYsMJn?!Q9bODQzIKEpݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎GGQ~x dMǤYsMJn?!Q9bODQzIKEpݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎GGQ~x dMǤYsMJn?!Q9bODQzIKEpݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎GGQ~x dMǤYsMJn?!Q9bODQzIKEpݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎GGQ~x dMǤYsMJn?!Q9bODQzIKEpݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎GGQ~x dMǤYsMJn?!Q9bODQzIKEpݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎GGQ~x dMǤYsMJn?!Q9bODQzIKEpݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎GGQ~x dMǤYsMJn?!Q9bODQzIKEpݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎GGQ~x dMǤYsMJn?!Q9bODQzIKEpݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎GGQ~x dMǤYsMJn?!Q9bODQzIKEpݧQ~x zy4嗎GHF(tL )=פdc#DD'XdQA_}*嗎G-~n,kιn[;=9 ݲ@qQ9Pnrc@Q~x @q囎I VlJn?!Q9bODQzIKEptv"u{ݲv?ٲnYO%_oXr0YaCs^\9|vt.*ʇ}KǣWk&r[cn٤.qo^ai3gg- /.=am/h&|1cڥ ^\@w}6%Kې'"(=$"8:ew=33ppkvw?0`A4+"/:^=V?pACU} xF"ˋ}pu O/_l[gCq[&D;嗎GZ׭_q }겝dۿ#7?#|RYogaFWt1h5"7:f kzNZ >gSt {|",N^z/ԺܾRVS>|&Щ嗎G|^A>}3mv].*' c&Nr1v|ݤ?}0 Iψ(t.EDcR,d ^Y]lJn?!Q9bODQzIKEptzznc?\՘@ҿWD_:us=_鋯 ǬD*{L@|#sL:|7)φbaGV&D;嗎G+WܵiMt&lW~h)"7:f mE9'D{euȆ)Y؆DDD=>QF 'YDDDT/=vY]\>lMg?\՘@ҿWD_:u3GaoLu_;7Ͼdyy o|֧xvݛ }+IDAT?}PY{gD_:uHH_q 5]V0|a?6ODo:&u@tNZ gSt {|",N^z/4rqsu]~:" ].-{Q'o^qSv? 4<_oXүu[~ݶ!vѲpIsY\T~c/4>|"l(y@da@Q~x qE;V R srqbw0n)aQc€5¾PϹ #"7:f -E;'D{eqȎ)Y؆DDD=>QF 'YDDDT/=vYj]\o\UwNwӯU< t*{EDQ+:YKoqYoXq3<n=l0oanݽ9rm?zvU[Z/u?ū6ok-lڲـV'}KǣW)WܵiMtNmGaɽ[) {p8 7=['"|1cPsBWj>ۏmHDDTe`qEDDDc}%r5nsTO{9z:"ыsrXͰof_xa~I .w)\qFuF~]?}NÏq˦- h<0 Iψ(tk1g>(=Lv3"/:^Vk.ML#}N>9oGm=ӱCDcR,h}6%Kې'"(=$"8:K7`@jN{_( {q@ҿWD_:u"{6w4هOg-w#Foh,/*vaIMi{gD_:u]οhDt`<Þߘ1CDcR,4 hv\ңlJn?!Q9bODQzIKEptkot=o=LS+"/:^G~?a}sTN Ww۬]^X"a{_Z7^TIybULv3"/:^xe9V]5 [WoG}yw;_1CDcR,$UsBW7^@>ۏmHDDTe`qEDDDc}%Z-<߹u.R >}a@J^Q~xu9f/Nw)2s"ۏmHDDTe`qEDDDc}%;-}ᗡ_"n~έ//7o}=Ȩ= &Щ嗎Gh?zvMdQa󻟹V'=~֍3 ʎ}0 Iψ(tSc\mpCi1 *9!ګtM6$""*G2JO8"""z鱃wqMh>犛ܿEmb=]g=oa^\ 5?:"w-yxfw<a+ky-K7^T(;yh'=#@]+DtIz vX"g^-NKǤYhT ^xelJn?!Q9bODQzIKEptˣ aZԭs}\#gϯ,SCLS+"/:^^O{8D6rq Ӎ3 ʌ}41 Iψ(t tEL2c MLv3"/:^|_q 5]j3q_x̤p|1cQsBW7^@>ۏmHDDTe`qEDDDc}% /vD&PnY m}ۏmHDDTe`qEDDDc}%rp?Ǻ"^֭;kWov#~mZk;6I~<̽ze8%a7o'\qϓ(slT&Xtۆpi- .,;osۦn}pɝٗ,]|MXxυ/2lOw,l~p/n0̻㘵p撛y7p׋ ?KǣWϚ#n#r;te9x/½|n|pKqū6}yʿ:Փ;~#0犛*xʱw/*g1s\'>/ {b.[X?,8ךzŶmϼ.r?dc 󯽳ud}:˕{-\ʶ\,f:{7\w‚UWyVonР5{FDQ+]+Dt?69y@8!|1c)9a\({4G^ozE*ga?Cm}7c&NlHחmǗ˗Q{c{pHB'vͭOeuQyshjɽ[ñs:貍PK+?[:y\7}^ci\Q c0dH({vn=Ikl3,Crv<}Dד; s^sӟAw^{vJ꾧S&O5Oo}<^SI/7{Nt}(t+38d޲>v7=nNCbL> ;"{n {gRO;_ >l©Eͣw疯>kn,0aʁ> SN8~Kg"/:^>#ƎwGϻ˦[/El$&%a>v9ЧOeZ۷_FۙV/*{P0z^1ϖur2[>/^ tloD_:u(WܵyND׈Z3`ss{QF 'YDDDT/=vY..>t^ξ5puM*$n] ue7?Ot}~(t;j÷ܲ[?"ಖ.Uً v_}Mwޗ簑 0yy4ϋč }\~[lR6f-\3]%l.qVmk6~-kVG6/f6sV_KK'c{U͞C̈́$J/"/:^+WܵyND׈Z3`s8xs2lm7>~!;oۘZjWO/M<9ldB!&:ia׻n&nlTva.T?`\q{<쵘cϾݮUg^W_o"'a>SܲiA^lU'ֲ_Q{3MO;o;}#_<Q~x ]Ͽs"Fz w F @7ӱBDcR,Rsz\"]QKޣYzSa]o3cYNmY{\o %Kې'"(=$"8:K&oI|Q6c.rI˜ot9+ZH{@޾/+c~qm;+gžeQK܅6iߓ8{6*B]gVF`{Q~xu!]˗AQ/.\I|&L>-IіBZz,L=$fuQysȇk׸N+ʸ0qc#z_{Z.^=&c?|Z^g3⶟^|?;-6Qo*luߓz,0p`wYuᖧ~嗎G@ٕ+Z<'kD0u/=QcR,Rs8Ez_7EV#Nޣ9{/Mǝ+@u&,G PwۏmHDDTe`qEDDDc}%Zu/}PWl{ݦUk^׿/c}佞 %T9Ʒ? N79g{nJXCmϼ^fQo,Iz7~{3tp ,^!k7?W+n|Kczcoۄx]H*~ĩgf)q;mAX{:᣾mĖ u}~ܫn KZy,.\d7<kf;IDQ+gs_?cnQ㋸8;&G;nƸ6`Aᰓ.&,w#/=-=|cv_cZl޾!7}v[w?a_[Ϙ 8Nu7guQysȄBv}ggnfnFع>ZZْb\]봱" 76.lU?iʇ|ܲ_sN81cU\z׬W VlSsg- s+;ߤ⶟en{h]}Hhʡ3*ڄKoc/0{Ygq{Ov덭kwڊ}OVUw^ͫF?| ɦ{aw WVӍ6ilA>!(]+Dt?&g,cz2<w:-:f N {SZ Uh}C+{stG*ׁmcμ LrkǾ}BI{n~{Ec(Y؆DDD=>QF 'YDDDT/=vY..7-YXBo,{sT؛z&E:6}3q. m Ԝpi@Lz. % ] ~ە}+}\o'B.t6sVoU $lJ'iC_:N_5EĶ7ցΧ%嗎GȞ}><-_tE;;o"vM@ezF1{}O۷ʶԣOrϿ2I7+zrsݺ"vmfuQysƄBvEl٪1{tӸE&nlx㷽~AǞnzejz}&HuT|<_u;])$?jM7hPjmF[tۆʿ{WG&g\vCX<^7Po߬}OVU6އm_u?{cWmx&?ѭysۣ!(]+Dt?&G_Ԝ pCi1 q~N\j"GcH}(~Ew}a]ǿ!n]{3 9{B}dc#DD'XdQA_Gg ýv6r撛me"Gu[^@ZjvAЦwnרu/}P۞yݮvK]sc/6&>z[o.&Bً M؅H6i'Mv}wGU>wo]d z7rz[t6}(t"j6w?sm}SO~W~_]\w2<9LcB2LȸJ7,JDžڅM~[~ͭo l@[^9vwk~ݭpiݺ{Zw$߂/>d^jS.FrO9˭}@fs6#luߓzts()D+WԜq)S+m^Q~x YϿc"$j_=O6Pt:^(tL8e?'V"EVZޣ·ݺc}[n=guū7Wr oiNY{\ %Kې'"(=$"8:KLh삄7[ñzCws76["1cQcܺ#'wy0߇v,wfٷN9hwf7= L=̭_?~ L?Yum5w6n)~n,j6d_!Q~x]b{"EeQ㋸s#Fok7{ox[wn,\. uH鉗acoǞbh0 0 ;uV}č=?mhģ^ɇUY< {l:#ms]ܫWV{­߼&jkq?g'{dKƱ׵mܫnsA{sBDQ+Pfe?V 蒨?67mt}S]n+:^(tL8e?'VLѯՈ=&qFl_[vͰw@cب#oiMX,G PۏmHDDTe`qEDDDc}%F'4'.>b|ʇzs\tCn@{nEmp˷.uaênӌ7sfi}`o\mT܅jCnϽ[fPMX_WTMڨA珈KǣWd1絛sUQ/=3^YZ g3}DF$>v>R~D;/9n8?Eqa }@uw,+0 S [>M?4 >ҭ;b U}MᚍuiB?]NhȄ'?y72tdzt}(t{Ez{u!]dc#DD'XdQA_Gg<օO9-_KDZz['OþӦ>}O=M n\~H+JiEM4W9WνnMow!A}+߬KMxi&m[?"#s.G7"/:^<= ݨLލecz^I\b[gNuga1}V/*{[Pu@u묕yWqaꍍ'D \ݏ g۶^lnF~o6mCu#_nӨ}OVxFHZƱ/_-炈KǣW|efi]Қ/>h_hL}:;Do:&u@}\Qhj,9YMڎ8mo~n{{k5qOPt {|",N^z/]\_aan]k7?n.*ۯ[^\Ӯ W+m}&ԟ\|t}=$.$i6YXp]#|VV} F :>oD_:u"{d%ݢ&|Mڎ9w[Qu}f.}-pEՋ4hQ'd\wZyEؘ{JwVEĽb¼^|O-?p73ql[>dj}@g5wsw N9hz<زOn٘=~3aʁny>D_:ueV/w@7KCq% ^Ďoxpĩg~S +e%@Q!|1c˕Z"Gn>O峰ccEz{p]nS(Y؆DDD=>QF 'YDDDT/=vY..of@SC#M7v+ iI1^|\د3{[Ի`Od.6O|NwAG_rˣsFDQ+7by0vA.[DE?wxwF&v¥ҙKnvZ=i|82s\CyEظmҰ甩"'6iNϜsjl#m?;oe^sCc0uF{Xo.s_?Ȟ>D_:ueV/w@7+[?"l歯Tmҿ {X/zN囎I',E~_{EVm< }u7gָP #\Jn?!Q9bODQzIKEpt˛>j0".uQ o.r9(G~y}.ۈ6|˭+Ƈ%VBC'v&m{wUY~bfq[E3"/:^!G;m[hp|Q8nЮC*I\zߑū7Q&Z_PMsʄB"Nȸqea\Zcci9zy"w밓3/YmDgFMݶ=uF{ZL~-^"g--@DQ+Pfe=]+xO;n2A_=ݯa6)MX2pЮn=I>a=NtQ1 q|NXrFޣ9僚m#Y}ZZ?ie"=g&Gu!Mdc#DD'XdQA_Gg @Nců6ACۙdž-nӈN |?-ۈCO<í >*l|u!ԣOrgEW68ǝˠ/-Ρ嗎Gވ`=-[4e8uwYo7~r2uvsTX"\TnV&q@uv(> g--,q[>-g^ݟ9~%nF~fkmfIjq?g+ḱ3Ntf^?au[灈KǣWzVud?ω .f:(tL8e>',reVH30w[cϴc2`3ޣNPt {|",N^z/]\euF&7-l wstG[QE5[c&r[~ e=B|'u!^O^9ݷznvlcDvΦ嗎GȞ]σ2ݲn=f'jU7V.*7qa+ E6 {uqWqaj[>-n~_^t˧e}O3Gz[Yܻ6Af?s6jk^iHIEy (2UVyZz/0`clb^|=61EDcR,)9W}\Qhֿ.Ҏk{sލc4;`H{~N.@ӿ),~lC""r(,""";,q:;&L>7rkm?}\Nw'Q ~;1 l=D.$heFԏFo-iul![|Q~x}ӳybEQZq<=~%͉ .sֳdVH^گc4{Qy$9lvBjEqݽ]ʸ+˸0mϼOmϼ/iL 5-.1}<>[}OVr6?[>ks^9n[灈KǣWxU-Z&J0ࡻc. +dt|Q1 qzNXrFޣYzv;[6Iӧ{< E{4\Jn?!Q9bODQzIKEpt[ЬOC}ݺA+4D>a n93h!7~֟Dt7=o1N W>cLmBG]պ`K .eT,:>_D_:u"{v> /_:[/Y}rw3/pmvޣ˶Ӹ}d<6;PMȸq.ee\Zcc;iqiLNh[]|x"m4~1f,s{p]nS(Y؆DDD=>QF 'YDDDT/=vY..Oc@sKܺ#ӎNj֛TumIK>.Xr}5n}p׋/:n{ f&7T.$m˧m5w5Ml=q!Ge9"ًmPZq<^vw BOS}<=&E呸 m@uv)>,o|Z{?bӲ4~0;僚b_'r~▯'lvߓzmE]tnvr6X"-ls@DQ+Pfe<w@Ze{>&4)Lr`YU;v@tQ1 qzNXrFޣZwsݲd4>gH{Bt;B6$""*G2JO8"""z鱃wqyZ>oMGܻ2 ],f&4=!@`‚ G>M}u<4ppMb۫n93.r1G6-۩j]H#'.ݯ93ܲQF 'YDDDT/=vY..Ok@c#׿X/QCYN?6op'Y e;U܅}wfg6'{[n}5"[A+"/:^MEY/㚝Yi^jz}ۼ.Nq(E,âMȸnn5+q.ee\Fl9[_q˷Әc2ڠ.[OI}Sy*!=Q~x YϿ]+DtY'Fí7“}n 9_Do:&u@}\Qh8xwsڗLm'}L9E~Yx]ΧS(Y؆DDD=>QF 'YDDDT/=vY..Os@S̹Ff@>oaUaЮCC3N|[nNw!ܲYAܫns˶]+O+"/:^x=/_uv,.l;{LG|6 㺹֬4uqWqaepKi!Gdθl=q?g'iw=qT=~dK"/:^2+WkZ.k/~icMǤYSs¢/Wk5MsfLms_i}"=g{lV7^Jn?!Q9bODQzIKEptӞfO1'?[Oj} ?0z^~ک 㞟K>Ty_H0thwߦ 6nۘ~-Π嗎Go=vM_ĭ]q4/QF 'YDDDT/=vY..O{@xw?qF@^tCO>>׿Ǟaʡ3´cfIMc&[y%puDo[S}!}Wmt!J]A+"/{w%IU't@#޲&&Ȏ 4Ⱦ H6(⠠ 33}ף/7=!VVfdedde<93g$32+nG̨q܂u^T_mvI-MǕ ϯ?OGe==ԠNuv:ָemvpI`ds&ƫnױ`Xf+3ζ`X ~;mOk/7uͶW>Og.Gm87pU]0Zv\kByn~߱tQZ|n3o\^Q|ǫF?{-ʖ-_~jEqeü w?v]xSsJ rQ| 'm@z6hףmǼTSccLE.GفKOϞSjݶW~ {~q[.`qMg`UhNqJmmuV`X L[o-by(蓳H8f1Ǭ$T焓\[՘hk6眓 AׅH)T}P82zMpW..c]ϿTZWv6 c/9nl-_b+?H+˗gdMmm'>ך`=W.}K5 hNqj4}ڻעlÎn? a^ζJ񶣬8:_I[иl 0(6VSxk˸H556ڰEvQˁg)ućn۫n?lO-)V#7ޗV+^͉1Wm5n s!: SSǘZz>+'YqL1+I39.זk5&3M&95;{sIzʆB$w>vpI`ds&ƫn׵`>=^Y] 9vkV=msjiΉ?tͿdm7|R,>K9Z/n;n}G*K-^Q|ǫF_YZo~?fa^ÞgI-UmGq^=ԠN`mІ9GYێymFge[g9N춽sd]V+^͉1Wm5n {!a7g|+>Nقu-ֿfԻ8f1Ǭ$T焓\[՘hu`sk}Sj9'5+g6W 4S&?&qe]\^Oִے#` 6˶c⁗~gX s95N?v\kB=vNn;._y=ok<М8xpף,-o?kEqeü t#?m'ugeexfR^T^Vk8i ׃Aum:e\mX~fsJ-춽sdMmFWc5Zkݰ cXezU?+c hVqJR6'r$}FO~qxsIzʆB$o>vpI`ds&ƫn׹`/Y8Hqw~nXGY|-ߩx3̺= oIeךeWdbavέe+5oZm5> hNqjtףl/kEqeü m'de{rTR^T^Vk8i ׃Aum:e\mX[n?{fRW>sd=+mvj_hNqJjkjݮc!a6g̻𞧲ǚ.6hVqJR6'r$}Fsf?8fsJ :V&5+g6 4S&?&qe]\^n?-92^jy_y%l;:gް"{leGuqR..Ƨz͉1W۟{-{=xd˵a^z%7gIm]ێmv3{NA/*/5۠ s\62.RM6,xE7fU)gUs'n;:Ū^*^͉1WIM۵u-D7fwӳǛ7|}$u/#YqL1+Ijۜ{\[:hNlɞSj9'5+g6 4S&?&qe]\nޭ|ٶ>u.ďdl[Qvq .ycG[.ʞSj}d_/9q<Ѷ>Kפl gd˵a^zvRZqn?~`9|6Sx 'm@z6hMԖc^[Eц:jY~ݾW~ {jct/UcV[_Sv@ >k,xwf4}q ͊c2YI6 's\1I|Dz챟XkusIzʆB$g>vpI`ds&ƫn[w~_m]7ѥ~){ vʖwuvq VbW?udU7=b|ʎ=4'8^5.5)K6}A0kEqeü 'm#7ޗ~]ps):_I[иl 0uזqjjla5+V>h×f'`u;K9ߓaowOjWwhNqJ[mM۵u.D7fݻou{\zzvIЬ8&㘕~jӜp?k˵͍_7]tR6眓 AׅH)T}P82zMpW..`}_Aw>OgdԶmmgBtaG|oV<_ۏ>rQ|n5> hNqj=[n.evܭX_5'jgS/<.5 BWbeJeE{~\\pLj㺉tkøH556ڰ`j}~`|.Asn^R;$UcV_eݮ{!6ghF|ܩת>dOmNrmVcR>Ie9|;칔 9gjR^:>{~],B5qه5,,xxr n϶tmċn?5l[e?Zvqk.$8G/off[f%nfx_39q<vpI`ds&ƫnXwǞsyԶݶvS.^mՋCOh{~m8԰/fٶ>vclmgaj`[kAe~oͿdNq͊c2YI6 's\1I|♗,-Xo_g_9Lm9Sq&IW7jj'X&Y@/! ]7]]v:Jh0>~jStDTz۞qvqj\.$8eϡGݾxEsH͛ۿC*n@sxUtS^o=eZk=lݾ[~~1.q 7ZkM~LSGylsl.*O^svS{ףi㺩r̛qQn;n3Uh/)_]-b5>ݾߺ=ktdՎМ8xev>`qOΫX3@☌cVԦ9$.צk5&3޽Cs6b둽f?4{SsβIzjr]*B5qه5,,xxr m+5oOvalf`v˸t!J %g)Vո\HpȞCنoU`}msYc⦧_'o14+8f%jmNrmVc>9벟s.n_Gg`رA?,׬6IׅH)T}P82zMpW..`>Wm]Y&qf#Xw myÊ>lwY,Xwag!t1Gom Eo8{e~nvawWcV[mϽGW|ǫk+/6z59OnT\qoݧ[~~1.qu\z羛ml5,n>,-BEuw|gv/~6z41 ǼIFLmxڿc 7.V>IM향}3xbXE/͉1WIym]+0ʅiq.X~{v],\ymhε~w`1Ǭ$U-sI\MjLg47gHoO {oZk=vl9&5ǪMu!Ƨo g@;8$NL^C|\U-ػ~pl sjY0ͶY.:rPK5.n:J7=LvaЏk :>k4'8^lxW~ݷN9Lmolq94,[{g@Lź1km>o]t:6MՆc$TScm 9fƅ2ξ>UsιleY7g{cyر]ϿN+͉1WI׆WkF]f6^V~{U/dcjKN8#$7qL1+IԆ9aj?kõI&-<<,-~'cNנsNmR^:?{Ҥ]"i@M$ %;75^u{gK'g*-p.9.J햭oތmʖpvظ\HPv…gϥ,]4.m(lŻ~4'8^|g޸"{bvܭXKﰋIy/*_?MŸuA}be-: ͦmKN<3{?t>ۋ~ pl۩:)g3뭮qdm8MH556ڰ j^\p6f۽Ō~^d_=ke&n,k}Տc/jYc5+jv{z͉1WIݛWkXJ3}o<{SK_#8Nf1Ǭ$ ڤ &s6\Q6)<\mlC*}fSZ.[~׬m |>vpI`ds&ƫn[.yl{e^=WvpI`ds&ƫn[v7Mmm7HU-]s7uM3>t c+m{j颔ӯ䬾i3w gd۞e}9t˅S=wɝ?ď߽XkumOWݞW]| ǫƧtq|k׃(n盽|GWm/< B]҅w=~U3.޵ѦٶS[mvs_\pm]FY~]cz7n 4]ٿv:Ojkq͊c2YIm:'ڤ~.W6jLm>9e6oeRZqɶJ~'S~9]s5g4ׅHjo g@;8$NL^C|\U-lSK,=b+O7^dm{7߇`Et)RgL(]p]0Nۭq vMg).HԚ.VyWzbtоU+@sxUqˮ^JּvIYWP~nm{97 Ҥ_y\xłuf=mp/ݿ[uH߀m )Wۧg{Qyݯm_Im^}]˿->t͝5[ʌz{\7ݤ&m\mX0b=7*o/ȶۭ0bͶK w9hum7v)#[D}@GZgmNmʿC͉1WI5n 4]?ٿ_=}Ь8&㘕a4s$~.W6j&346blSKu}7LEϛV U<ߌd\~Fc&IMChG| IKs.+nqvzn\|=n˅ӵ춇;Y|nt^׹)`#|˟}/'\x]ƛetJ9i%М8xs{};ҹ8⛊-^|p1G1:-|1v1v!GOQBəq9(ǘ]R|.} ӹy~']Z;- ;t%|!Q;$|$!'O 7_W*[c5;'Im,YqL1+Ij5iMmRՈMg4w|g}VIXJ׊m⻿,{[/jN-n{j^I/B5qه5,,xxr V+]ܑ1n{7nb+?ηuyċn,yŶݹ?/.DH}ӳ6Y {b3"oY#> e2-j_歵Xw WiP)@sxUϽVlݎWtHyR/HW|=k6ՖB$o g@;8$NL^C|\U-XMitLZ|CN>+M 7 rN[qw~=~BJ?֋w͞K1C57+М8x3.|0Z0&b\FyA/afoyq۳?cNn;ۋG?d1hi.?]٦f\Q릛c$TScm .{FGqAAc϶*?gLnPiE>94'8^%UoR_ݮfj-8ξMS>Gj[q\͊c2YIv2'I\n&ZhsJ,-ؘ5tI+B5qه5,,xxr ֪U~łu}^ =nO1;mYvL?^ctF@*>n?˅z?}róM+_}Ql1z8z\C|̛"h[RFdmfqud7Vv{⻿,mqY([_#9q<*i۵]fiCɞ.y>RchVqJR]9a&snMҵh{ǟwugK?WǼfmS~os5kX T>vpI`ds&ƫn[pv⯦A7BXG|u.JVlM؃.b9ǪҸ\Ho7|==A{Fgtg!XhNqjt/v.bL u w=ϼ=r7 bB񱫶;h_COhv^T>p՛)[vEgǫ{Wb g sS][:?J||gR hNqJ}suZqZnf)g6b ߨ'8&f1Ǭ$\\\WpFMg4w_}cCowo*e/eI|amh.fMXB$W7jj'X&Y@/! G^'}$\o 7~[ޮ҅*kΛ=^puO}+ we3 v۹gޫtWtУ1۶vc{=?,_Ycv;\zHiWsb\"99u~:B9*._6kE5`-[Rg.Re!t=93ζ׭&~2q=M1oTSc?.֘7/{4?"y,+޿WnUqUfۭn'=Եݪ~b,0)n@sxUkn ųmǩ^n_n%go;Hݶ?nɞ9}Ԗxd4ڜjss~jTmR>IAtMm@ZK춇;N;mɑml_n=0s]:PM!#>@M$ %;7vs]s̞Y/}>:?a:8_{WWmsitˉcwG.yoloMrvϻq;m\\7Ӯ't~G6j>~ 6ݢGB{mGS<͉1Wi!3o\Q,9bqxm~Ng}ιο/}?S/fiq{u oYxKN<8 sfhS|ߋsn}8ӊmwݻ;춻S,qb#3oX1?o:ZiRyE{S;syWsJK7wɝ?Y|eqgS[64M0ċn,~fnzMu`g%+X~\<_mihNqJIIw`1Ǭ$5$ 's6]1)<.}ꛝkF[vEwrN{knv~_Z/m[iԠ_t6&5Rjj'X&Y@/! .I+Sǫ$I/S9q<*Ij+YqL1+I4H\<#$M^>vpI`ds&$IxLc$I)S9q<*Ij+YqL1+I4H,;H}۳J&?g@;8$NL^C|\$W<͉1WI$Ig@i)М8x$IRЬ8&㘕$Iw7;H]pm%IWjj'X&Y@/! .I+Sǫ$I4whNqJ$Z hVqJ$U7Z_Nvoe$M^>vpI`ds&$IxLc$I)S9q<*Ij+YqL1+IT;zvQvϷ=$iSM!#>@M$ %;7%IRc*М8x$ITNq͉1WI$U+_͊c2YI}9FjͶn+I1jj'X&Y@/! .I+Sǫ$I4whNqJ$Z hVqJ$Um=1R}m%IY<PM!#>@M$ %;7%IRc*М8x$ITNq͉1WI$U+_͊c2YI47{_Oyv~Q;n/I1jj'X&Y@/! .I+Sǫ$I4whNqJ$Z hVqJ/XrjV\^wKϻ:;(_e$Mf@5qه5,,x$I@sxU$IRYP;q 4'8^%ITx~4+8f%Ih*>x q} 7.|mo{1Ej]n/Iqjj'X&Y@/! .I+Sǫ$I4whNqJ$Z hVqJbdԾ?1}]vБ]%I[<PM!#>@M$ %;7%IRc*М8x$ITNq͉1WI$U+_͊c2YI4[VwɎeDvalqs{__vIT}P82zMpI_ 4'8^%I$՟S@sxU$IՊW@☌cV$͍nzbWώW_8۲ soq.3}$I]<PM!#>@M$ %;7%IRc*М8x$ITNq͉1WI$U+_͊c2YI4w: c} fә7(V[m7O4g@;8$NL^C|\$W<͉1WI$Ig@i)М8x$IRЬ8&㘕$IsG^C[emN?[z/tiCOm-]\@M$ %;7%IRc*М8x$I$I@sxU$IՊW@☌cV$IŹ&g@;8$NL^C|\$W<͉1WI$INq 4'8^%ITx~4+8f%I$IZkRM!#>@M$ %;7%IRc*М8x$I$I@sxU$IՊW@☌cV$IŹ&g@;8$NL^C|\$W<͉1WI$INq 4'8^%ITx~4+8f%I$IZkRM!#>@M$ %;7%IRc*М8x$I$I@sxU$IՊW@☌cV$IŹ&g@;8$NL^C|\$W<͉1WI$INq 4'8^%ITx~4+8f%I$IZkRM!#>@M$ %;7%IRc*М8x$I$I@sxU$IՊW@☌cV$IŹ&g@;8$NL^C|\$W<͉1WI$INq 4'8^%ITx~4+8f%I$IZkRM!#>@M$ %;7%IRc*М8x$I$I@sxU$IՊW@☌cV$IŹ&g@;8$NL^C|\$W<͉1WI$INq 4'8^%ITx~4+8f%I$IZkRM!#>@M$ %;7%IRc*М8x$I$I@sxU$IՊW@☌cV$IŹ&g@;8$NL^C|\$W<͉1WI$INq 4'8^%ITx~4+8f%I$IZkRM!#>@M$ %;7%IRc*М8x$I$I@sxU$IՊW@☌cV$IŹ&g@;8$NL^C|\$W<͉1WI$INq 4'8^%ITx~4+8f%I$IZkRM!#>@M$ %;H4'd'IDATGI$I@sx$IpǤ$I$IÎjj'X&Y@/A$ '9q`<ı)I$I0b0q?ڗ5,,Jq] kS@;8$NL(u)MP82ץ6#>@M$ R\5,,Jq] kS@;8$NL(u)MP82ץ6#>@M$ R\5,,Jq] kS@;8$NL(u)MP82ץ6#>@M$ R\5,,Jq] kS@;8$NL(u)MP82ץ6#>@M$ R\5,,Jq] kS@;8$NL(u)MP82ץ6#>@M$ R\5,,Jq] kS@;8$NL(u)MP82ץ6#>@M$ R\5,,Jq] kS@;8$NL(u)MP82ץ6#>@M$ R\5,,Jq] kS@;8$NL(u)MP82ץ6#>@M$ R\5,,Jq] kS@;8$NL(u)MP82ץ6#>@M$ R\5,,Jq] kS@;8$NL(u)MP82ץ6#>@M$ R\5,,Jq] kS@;8$NL(u)MP82ץ6#>@M$ R\5,,Jq] kS@;8$NL(u)MP82ץ6#>@M$ R\5,,Jq] kS@;8$NL(u)MwђT¿0. pA* U$"Ts$JA1D$8w>}tWYJuU]P82)Kan %,,P82)Kan %,,P82)Kan %,,P82)Kan %,,P82)Kan %,,P82)Kan %,,P82)Kan %,,P82)Kan %,,P82)Kan %,,A$IT^O܇$IZ{0\JXYhI$IRQqI$I{pp(I`dQ I$I&#;I$IRPx}[%,, $I$ID}}'I$I*?ٺo $qeD{$I$I%_GwqJ$I/Ng- W|A AI$IRo/#o%I$I^T'޳u+>@I w$I$7]ܷ$Ic/ٺo $qeDB|F$Iԛ.[I$IRűՉlݷP82]!>h#I$IMqE}}$I$ {pp(I`dQ$I$8>⾋V$I}q@u=[m`8$ (~WH$IzSQq}+I$I8:0\JXY@+m$I$I)⾕$I$u_{Px}[%,, 6$I_GwqJ$I/Ng- W|A AI$IRo/#o%I$I^T'޳u+>@I w$I$7]ܷ$Ic/ٺo $qeDB|F$Iԛ.[I$IRűՉlݷP82]!>h#I$IMqE}}$I$ {pp(I`dQ$I$8>⾋V$I}q@u=[m`8$ (~WH$IzSQq}+I$I8:0\JXY@+m$I$I)⾕$I$u_{Px}[%,, 6$I_GwqJ$I/Ng- W|A AI$IRo/#o%I$I^T'޳u+>@I w$I$7]ܷ$Ic/ٺo $qeDB|F$Iԛ.[I$IRűՉlݷP82]!>h#I$IMqE}}$I$ {pp(I`dQ$I$8>⾋V$I}q@u=[m`8$ (~WH$IzSQq}+I$I8:0\JXY@+m$I$I)⾕$I$u_{Px}[%,, 6$I_GwqJ$I/Ng- W|A AI$IRo/#o%I$I^T'޳u+>@I w$I$7]ܷ$Ic/ٺo $qeDB|F$Iԛ.[I$IRűՉlݷP82]!>h#I$IMqE}}$I$ {pp(I`dQ$I$8>⾋V$I}q@u=[m`8$ (~WH$IzSQq}+I$I8:0\JXY@+m$I$I)⾕$I$u_{Px}[%,, 6$I_GwqJ$I/Ng- W|A AI$IRo/#o%I$I^T'޳u+>@I w$I$7]ܷcӞ(;#*~PϾW7w>bIz?-IT{Px}[%,, 6ooNկ}=+=`{A^{-[׆o^=;<8#e5U1V]ܷ^/Uv1*GwqJw⌳.Zhlfao/꿭>WQYz;sے4Tٺo $qeDB|Fn4a% rcL]ܷ[oMvN;]s}Fsے$f~lݷP82]!>hV~0g1Vc.[Iop|O+J~d_ vecmKy @u=[m`8$ (~Wڨ@+^vŗ6ݬ_)9⅗^ɖ2DiFIe\eS1A}}w+]s}mI?ONg- W|A Ah~l2|8[‹,Rf˷[Yw"5 2`}YsU>⾋VR734Sv.lUW-vy̳)n׿8 ٷ{Gs_xɶ]sUܶ$?{Ng- W|A Ahkglkd˷[Yw"5 2`}YsU>⾋VRy/fuSN^77ߞ^~YŊ+T\t%O~'j0\~n[`ٺo $qeDB|Fl}'uTh (LX|cjU<&⾕T^\w}v6[p[o#{xrO`Vչ>Zܶ$ߋT'޳u+>@I w[4_dWdk2DkFIe\eS1A}}xMs05iҤsxsO`Vչ>Zܶ$ߋT'޳u+>@I w[4fwک;QR{,W9ԪxLPq}+{\L.Y~qi.ϖuU%Iy{pp(I`dQQ+7zou6[pxa% rcL]ܷoοf{lnr]smo̖uU%Iy{pp(I`dQQ+/([gjg.VH (LX|cjU<&⾕TNzXvVXalns^*e@*X?-IʋT'޳u+>@I w[4_sG?bҤI^-ݞ-Ie߉0g1Vc.[IM7οlns,UymIR^<:0\JXY@+mߪx~*oQR{,W9ԪxLPq}+VXaKvٲY&T/\sےy @u=[m`8$ (~Wڨ溽4 2`}YsU>⾋VR9-;eL^V%Iy{pp(I`dQQu{iFIe\eS1A}}r2eJvylZl~w ˪Uv1*GwqJ}/Zv5Kڸ|~w ի>GmKF/~Px}[%,, 6ou{nw< (LX|cjU<&⾕k6ewwgPs}dܶ$i1ՉlݷP82]!>hV~0g1Vc.[Io}L^U>~n[4z{pp(I`dQQu{iFIe\eS1A}}W\ߝe@sےыT'޳u+>@I w[hO7Jj/7*;ǘZ #o%'r,WU}ۖ$^<:0\JXY@+m@s/[[<#ō?Eqɧ{M,~w^Wv7^x'rjq![~wG/,/VSO;cϽ~Ł\vŕ?&jo=_,☟[|?(g⌳.n'x{K.-N8*?ᮍ+*~g1{D)ICOwݵq~Q%^VyœO=]ʤ ,w>PqÍ7zZʍInq}'Le_Ql&ԩSj9j9[gI$!}+ ,Vf_nLѫ ,׋?X~務f)[hc"M<ߛߥ&I?JצZg~6[g?(Y7X]s5Y".j]ܷ:sB#|-sٶn-?Vi⅗^yn&/N]+}efuw4cӞ9.qdkN^e)m/Vu̠nsUy7F[aQ|w,/qOXQa-T'޳u+>@I w[4j7M]L<9{m{ö?llNJ$]reŊ+N<眍'z:NG?ԟlٲ뷿˶Tl:Flf]bd~ q|۞m҃iBg1[ץz=o]t!Ň>l[&;SINY={U;%\8úy2gyOI4$scMPU6M(B eﱓҵiws&q6cpMIvofQ}B}}K!UfiF֯wW_lŶ&= >cv:-}/%X2{ᆰo3E?~n0fku.T,SNoe(ҰCٺo $qeDB|FC3zyFMf_JWtҺ6Gm n~GW_kúqlP:F/lٷW흊g9[~ +v}b{ʶWvX-{Oݔ<(V yf4IuߘmIglݖ&ʫm[d4!^{41K~b-s}6 mD=:9zU9\6}K6TmqPq}+&\~|m|ߟ4iRf.n4`]-ysNcVb?2r_~XeUȶݪ:"ՉlݷP82]!>h!V4DXϖ2\󮻳v0t'4&z練SO?mW7u{>=m^yXozUh7_Ywl͖^64&LͲ'<{:AE,çzZ^def~4IMZ'^/6xl^V[o=={euX,eK.T=m&q1֫Sfkg>S;T}qPq}+&\U_Ӹ:M>^z%v; um-n /p:"=ٺo $qeDB|FC=I^ozPy˖us1Gqegﳛo4ڎ){eȢ}Ay>=SoiG>-V_}J&я϶]Fכ-s4i@lxrzGq;eF_hLhX:OF~?:bW̖uY{'3K./>>⾋VRM~Iyvl;e/[h!@ssˌ,=$7Ura8SO+vmKnV,F+xU/Jκff1ۖ[mU^\\u5qǟP|{/6cNV49眫U?O ʃVQG}b&O\|_*vmNl뮿8>,feu7^&|-m3?8 gU~[nY,̇>SO?~Y_:kX:N8/lǟxxiͷVuι+M_7Z;K>ӯn1@\h-źW|[;qdcŒ8Ӌٷj뭋~F++{?ju4?>>dM'gs^I\WI ҿgh+_ZoojL7;ﺻKP;8]g|?ٌ^/9k\3eS.[Iw‰'oIƚ4*.N~GFV;]?ղmk|}?}7K~^(1Kkc2MXvƙٿV]vR?l;k=}5 oƸ:7ԩS^3nοHʋՉlݷP82]!>h!n>zӃ;oߛIkCqݣ_Rl5능 0ش'z}y)vcɧK8_OM }1zA7k~}UV)N>ŗ_{{Q$n+>x~J˯(_|l]#KCnq6c['?6}C*x:⾕Dk廭w ,d{;|^۪4y.nc흲vS?n#&ŽAmfPU}nV1[s1i7Kݿ} ?g{οHxPx}[%,, 6o~wm[jigx85/Ŧ}9[{_N1.^}5lԩQGS)M&^Yz6-_;AV{UsZpn)[W;=ē:뮛sds=w /eO81fuknz]x#? q}҄7k_Y,z_ni⌹暒sdin<`fi{ju4aeSe'7_+e4`$ ~FۓH^bxIV\iw^;tueS>ovڙ,C]ܷz_s}Nqulu)yuƲ3+ DpP~-bU#Su:w}b͵pH=;s {pp(I`dQQkecwu.RŽcϾeYzpz61v!fr-W<شumݳ,Mf0Ic }ct-4?LNzo{dvx&Km42eJq/~n<~YϞmcofZ'4iߪ:ODu<79,}?eOgs^1,dhf鴺gnk2q&9gc":M>⾋VRzb2oYhu7i]=)ȞzXf`5mN8H sۃ0fku..2&MuE {pp(I`dQQkeW޹瞻+g{@'=hM'rpAk:iƑs}ŬΚmYzqǟP<ٶIkӠ>H|{I:Sb͵ʶlEɄդ~{E[,ZL;^x^z iҳ:cl8{M7ź뭗mϮ6{Md|gId7M,&0O_Ͽg{!{M7]t%NJב {M'8kuu]+{dm58}F}}W\e^~lN8lnJ(vM|xo3Es}d ZҘ3;ՉlݷP82]!>h!n>zꪟd`uOl@lVO^n76K }o=={]/JϞmY56ҷ{Z=hÏ>VLsl[.5vI'gm&|tkz_|h̿md0Sq]4!K-M؋!;oqɧdˎU:_c=O?5|m0.W4{Mf -POfE;KfԧۭYcZGz"oqe*7#o%'*['O֛aods=?V^M?':63[Ī>G֯mʘs̳^ۋ;iٺo $qeDB|FC>|^zci4V|}/v6!afm|xw94nS6&fA}c@~:ȣ5Kl\zbdmvŕWeecM>8NjgJ-.ۋfm}gˌU||啳۬L]vZgWg˴j"'oC|' ,Z'ib5,^zlf4ih8kuu].&ٱFߨ⾕wi׾o]|%_}=[V]llT ̠n\Y?=Hcv5Z+{](+T'޳u+>@I w[(}$Z^(ol|54 cɧ.felgx|M{{{J_>k:iPon^׫^|$ qxlNJ[u6pg˗g7 vl.RٺRl/JWq8Y|^zG&ϊ+=CiG~k|YwpE)56]}5&ng1[{KMltܾ+.}./S+bqw;ﺻ儑o~k5G}}W\~Uz=h41]nY眛-i}AmfPU}1یŲú"9 @u=[m`8$ (~Wڨzۇϫ^҄nue]yUf7kRK.Tof˗w5{~~/mP1zYggu7xSf^wC|;=6퉖Wxl~wWZ)[W3k-^bksn-?њuߘ-Nc'ib׸|Y~Qu[1wbl;d⾕W2|8{RO8 UA\˲:"9 @u=[m`8$ (~Wڨzۇϫ^o^&{Mٝxh͆o4;3.==묳f%[d˷۠>H?1:eʔuc=@ge˷aGO<-_fiBF6 Mu&9g㌺˳엿%[~5ϓN@xȡeJM4xigُ~||::E]4[GYx4.[IoKeLՋDo=&[&LjPU}mژmsqy)x5NHjxNPx}[%,, 6ol}^Sva>gϨadIU06IM|^z_T>콤>eegԠoOXpgsO<+[AN?#[҃q'[vF4яϖC=,{/;e_kdp`~&&mٲ~'bx?eԬ߶kdˎVݏVX;ߵ^{ƿS|me]xשI}}W\\'jg}NXٲ6'o3wXȪRc{W|(:+T'޳u+>@I w[(}V\qldYzqn^쌳Ζ_~}ٳ6ΖoA}~co̖/o|[S;~{luEgivϽeW#MKN85>GF6z*5fYX7 yrŗdiش'Gj˖g}J~n 5[xEi={SܧGwqJ}UN̕{"wO/-iuAm٠n\Y1[js1MuuERg{pp(I`dQQkeW-_Ui~=#c59瞟>5묳6WY/J_ٲ4ҏuwengO3ꨣ֓ZdEelSjl}\87ߞN:=gNȣF?OL+ēNK*]Oy?u?Zcc}J.[nUf7_(SܯGwqJ}UN̕{_ޜ=e]6[&LA"V>*=hcXbUi~GY:0\JXY@+mZ=DU7uqgWUz3f~GX x!fOٲUwOWjϖmA}~cI:lLlneI}v eluS S_ײuV:w;owO2gZs͵e'jxfIٲUtŗd?y.[>V16w:>L9\omկo⾕+5w{И+*:i3:gW7?ɧ.ޞ^;MyAm٠n\Y1[js1}.j" @u=[m`8$ (~Wڨzۇϫ^oK.͖y7{OK.,[vo;eOm&ٲU[3jPouNsl2;d!56bl=:[ʾ{Ju;`_zޱZzewߣ1AX')mvSDmϓ^u+J>qk]#ʖ 7ޔF,[#o%*'Juzu{#.͋[y晳2V>{;7cqg4&۲R< 6lPU}mژ-5ֹ[\&(:+T'޳u+>@I w[(}nl*Kɱeˎՠmol٪;ɖQ }cz9g^;[Ojv˖p=0λ7λvZxᅋvޥj<m뭿~wlى 'i}Scu?:9>bfʖ1?96{YܷGwqJ}UN̕ݫyO(>ղ O84-bU#rۃ6fK:'M-[vwIQٺo $qeDB|FC>|^zSUO}:{O3[vo>>fV]/fl5ҷ:F=; zRGytlvđ{JbfiVX!F-λ|󮻳u|#vRvxDmϓZ:[D'Y^ט#.mيl;_qRq}+U91Ww/zwC=2eJ* ᷙfEs}dUn{lV<̓-[UwIKٺo $qeDB|FC>|^zS/J|}iͲj뭳ejX {}Ȗ=l5ҷw^Nl6fIsٲUSz9jBvK|w Kmұם:se'jxL<9[D^Y;w& U.[IʉR\{ϮXb%T O84-bU#r۝|wR^nvDERgs{pp(I`dQQ+!ʪ;3gV;|#{_maX 8uSO;=[zX#ʖQ }cz5;゚Y8#._eW_{]Rpdv{vs=wv2eJq!6Qc폫&[~6LDkYgl.ٌKI+_Z-իO⾕+5w/:ӊf)ƌJߡ[|XlPK/xoYߠN8(4-bU#jcq&Zٚ:{mERs{pp(I`dQQ+!ʪכz퍷oԗlٱq9^𢋳en{*fe콥NI}}W\ƺ~e1?96[hMs[;~WwœO=5KePl\0[u 6lPU}mژ-\f/(:+{T'޳u+>@I w[YQV#M˖u]7{O~ٲc5RK->udV?L]u53jPouv{~wZ/'6[b%=l*Kۏ)U#{ͷV[orBV4Lũs^zl=A%[8ku[nkƛ4V[=od@Mգ?⾕+݋rh/Qq /~GVm̖ju.s̪ERg{pp(I`dQQ+!ʪכ󮻳lŕVSÏȖA\k]w-[}5K-.?AVhwrb5d^{-[e{wRUO8y(vbdm망~FVGDmϓu[/[Oj}˖Cu?Zcc&IQ?:by͖i>g6v5.[IʉR~GͷfY+*yvǝɶ ᷙfEs}dUn{lVbUV(:+sT'޳u+>@I w[YQVTz0<._e -PR{~X ߸Ŗ[fOmٲUwu7d3Ͻ-?&ƒ]S+Xzx.1Z_S[muDmϓT:NuYcUi_~qO̖6lmW⾕+wĵ>lV_}*=hcTs~U(:+oT'޳u+>@I w[YQVɧ-_UI&e)~-?V7^SY{l٪;̳c9e۩Ճ@lY0[ݞߝˉv;zRml*pg)5Q&l>.bŗYt⍷^uݲצ[lى '{w묓-[~:Fk%,|lv%[~dwAM>>⾋VRrbTw7[MZax+{M/kAp~i6[Ī>GVm̖ju.v9(:+k}8KR֋.eae) ܭ[(,*WX FUH fQrEdE@2HA2 пzWsfy>uÜy ͉lݷvpI`dQVCMo7/%[x5l+^|oou^{lٱA+*[gO }hxrbl;׾uٲM 7^SjMX[nUz;d?lԚk-;UY/rTogcUVY]wg뗥$z]R.;;4uǐ]<ɉR|Gmo˶1cFnʖ6N8 ͔8[Ě>׻kr6fK:ܜJU;3ٺo P82]!>hɭ(njm͖oslzc5Y<;[,MozUR-X-;zMvyg?σޣʉzXb/}([ҿYה_Rޝv^sY^3+bx_d)eO}=ٳkcn?ٙ/_&C]yS<~ xⱕT}MN̕uwǟ|:V{|0[Dqqߩap~)-bM5azܜU;3ٺo P82]!>hɭ(njzعvI-r~w6mZ>~"[5)K[nm+uɧfծ fO }hxrbK.Y׻krߣqq̖u.Nss(W<hNg-+>@M w&lze?Q:u?/fΚ [lm't}e7'?rcyoԗlٺpy{{U9 /\JٶR[MkbT0uq'd3̖m4~^j޼yٲSaS6[ [vYX*M{ƙ?Y\G&M5_|+3gNq=fij#xl%U_s~O;a:Wl)\rlԧ/[~5h%]+S>=&9sfݲ7е_#xl%U_sU}5hlٺb-p׿>[zam&=4[Ě>׻kzߣqq\TQ$xМx}[hW|A AMnu=DvWW7xs1}5>=R.ՅuZUkH-2cO<3N-_Gl߱Lxrit,rU=O~*[v?lݲ4Ib,1Su5h9bŒA}i]m-9睟Q<^ xⱕT}MOUgm+5s^̖=.wY?dM͞=]c063[Ě>׻}Ә׹84H[95,, 6zxlUV)lԆ17o^ݲ?lӦMJuܹsm̙ST>A^~VfW\yU4ͷޞSe7tKKgwo|H=կ}=[$#3uȡeˏS<[euR,r T/$m%n,];:U7lak_[\tO>=Syҿ׻}Ә׹8ͱ6H[95,, 6zԧ>_~U=ߋVZ)gُIXkXk-Om;x=uǞuªm߻ٶGwel_~ouLlz۷϶Ym\WϽbϑ_6RiBGx|] ri:3_mwܙSEi", NN>lّ'GmlYZ:Uv>wa;ʖk>k=v+f̘lM73Q/'Gk=vđ>lMn18ⱋVR5=1W϶Jߗ~{U&5(b*m;&VYc5 &s0fu.9wI3ٺo P82]!>hɭ(n|C'>_ζ5xMܹs}͚lgrxj]zݽ&,(;3?\{}ꪫf([z饋n%[[ol?ezqwf댷4aG?l+R +d{?z{~:'61fnMx'&>^Gh!`쿍w߾}>lQ&5/o=̙3~yvD;#{>ǟ|:['ֆG>/_\zzto.|}1O<Yv1OYJ|u4yc.[I\u\_f+K=Gu ~]̘1#W ߶vٶS,Lg|U:V̰nk\n2= c^b?cmG4z<95,, 6z~C:U[j]P,rٶw77҃3gN;R<#ٺcŗU|ʩŲζݷ}xn-n2W^wUlgMs#NσޣΉRoflݥItz+~[jٶSGg7yMdoXjmϖ>?R3"nN>5[g,ϧ$dd\wu?ݳmwwygT[Γ=/vwiRCyhs/d뎧E/9qe Ϛ|_پX?hU[<> xⱕT}MOU;_?fټyx0[oc_׋ӧg&L˶zE/eTQc5 &sߩA:k; {@;$ (~Whr!ʦ{Gvg,oe+Rqv:U4̟xߔm/lZɃmG?qXz(y=?ԙ00߫^xt&ۋm}iBfnƌP<ٳGpM7,&E{G=[w=m˖\rl^o.^3fZ_,Ŗ[f}"%n4ڻ۲uտsmv>Czi?m3&"KrὲXsēNglF] ;gqm^|9[" W^3+5=xlc+im4~ Kw'&O5kw olmFco>]K.H:V̰nk\n2]6c^bcmG4r\95,, 6ze`K,Q,|cDq7wB~Yw;:mA^~f]|ig,(%^ޙ<0=p{;M qYf_˖|-TZ.G{fJ\:~}E}`=RKu&Ot϶=RioqɧWÏIz:&#M̱+gVYe⑿?^FjUW-v}{4?}ꫯm#w9DdgX_wu|08ⱋVR5=1W]Zvcilo\<'pKџQ'J?\{}w'oUMlKeW1&eοŮ v e$dq#X o3Esw z~nAE {@;$ (~Whr!ʦ&^;ިfΜٙtgڴi[\o_Dk8ZW\yUuװ߷+fJi:3ߕo.^ IʖA^~f[oӘ'c讜`b%[҄iҊ]Ζ4g7ZMT3MQ}ciCkh?=1MKQ gg*Mpjy~ؙ kfΚU֫ 7ڨ7o^gbh*xzle\O񸞚-G@M w&lz'rj)6Z>~}w?o\\{k_5k_Ύ? M(5];?ޝvΖA^~f{bM'W(nl߻0[ S=Ԉ,Qz^C]zsn :IqI*W^9_κuߐ*z=#jkQ<zjxLc+^L_ui·4Yr=PL>Ͼ>&Zzq^Ǫ &s#5hc^bUcm~G{@;$ (~Whr!ʦ{ʖM‹9sdӋ|{ Xzgv%{]Ufm/X~o 6߼`y߮ y{{5=?_Teyl{A1s֬lUlUu.6xVλZ#rUO /[mu@?tz63[Ě>׻}jl*?7Ӡ"=ٺo P82]!>hɭ(瞗-[>䛇Zx[nO_qd6)=馛esǝI⾚,Mˮ %X"{-M@~~K~R{l~o1Y\ۙ2M=[oOyR^zUMz쉧:կ3і^zbO|rB4aI'ZO?>'پSϓE_Z*\rl-]N?l_u7g翐Ï82[G#xl%U_s5y_9&2,ә/ q_>UOZ?L7C{4~wU~-bMM zu|nAEq3dlݷvpI`dQ]~+_-;/([6GźWZc5;v>3v Dʫ:ooEg ~:)=Zozv>0 }h_ɸkHpٲutϽ~kX\-XPwmHir*~yιV[o= g׾Gm̟ՙ =8_JpaqWd۞HΓ+MZ~Xy啳1곟|qζtS}64[޲i9sLdm- Gmg&&L +voxCo5kql>{|s\t649$?я;wߙpܹ v}883[n#TWsb(ޝ:J첳;1/]Se49I`1/;c|zի^d)͛Wum=?΃'tJ϶jKi2NvuagK;״jq.[I귛o8: x:}OWF7oRw;#NqÍ7gۘJqK,mx%6ju~x|׻;K^y-eݰ6wolM6迣Hm+hNg-+>@M w$I$]<$Ic/ٺo P82]!>h#I$I8bpc$I$ {@;$ (~WH$I)c+I$I895,, 6$Ij/G@M w$I$]<$Ic/ٺo P82]!>h#I$I8bpc$I$ {@;$ (~WH$I)c+I$I895,, 6$Ij/G@M w$I$]<$Ic/ٺo P82]!>h#I$I8bpc$I$ {@;$ (~WH$I)c+I$I895,, 6$Ij/G@M w$I$]<$Ic/ٺo P82]!>h#I$I8bpc$I$ {@;$ (~WH$I)c+I$I895,, 6$Ij/G@M w$I$]<$Ic/ٺo P82]!>h#I$I8bpc$I$ {@;$ (~WH$I)c+I$I895,, 6$Ij/G@M w$I$]<$Ic/ٺo P82]!>h#I$I8bpc$I$ {@;$ (~WH$I)c+I$I895,, 6$Ij/G@M w$I$]<$Ic/ٺo P82]!>h#I$I8bpc$I$ {@;$ (~WH$I)c+I$I895,, 6$Ij/G@M w$I$]<$Ic/ٺo P82]!>h#I$I8bpc$I$ {@;$ (~WH$I)c+I$I895,, 6$Ij/G@M w$I$]<$Ic/ٺo P82]!>h#I$I8bpc$I$ {@;$ (~WH$I)c+I$I895,, 6$Ij/G@M w$I$]<$Ic/ٺo P82]A$IL x$I$I@s=[m\jXY@+H$I$I$hNg-+>@M wI$IR318ⱓ$I$͉lݷvpI`d#$I$IN$IT4'޳u&qe@)Kan hW|A8/)\jXYvpI`dPR&qe@)Kan hW|A8/)\jXYvpI`dPR&qe@)Kan hW|A8/)\jXYvpI`dPR&qe@)Kan hW|A8/)\jXYvpI`dPR&qe@)Kan hW|A8/)\jXYvpI`dPR&qe@)Kan hW|A8/)\jXYvpI`dPR&qe@)Kan hW|A8/)\jXYvpI`dPR&qe@)Kan hW|A8/)\jXYvpI`dPR&qe@)Kan hW|A8/)\jXYvpI`dPR&qe@)Kan hW|A8/)\jXYvpI`dPR&qe@)Kan hW|A)S(.IDAT8/)\jXYvpI`dPR&qe@)Kan hW|A8/)\jXYvpI`dPR&qe@)Kan hW|A8/)\jXYvpI`dPR&qe@)Kan hW|A8/)\jXYvpI`dPR&qe@)Kan hW|$I$I$I$I$I$I$I$I4ZsI`I$I$I$I$I$I$I$I$I ?W|$I$I$I$I$I$I$I$I4ZsI`I$I$I$I$I$I$I$I$I ?W|$I$I$I$I$I$I$I$I4Zs`@h &`@h &`@h &`@h &`@ݴZaWa;s4mazըIA3D"Tz?'qi:Y7{[ $I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$iA%I$I$I$I$I$I$I$I$I$IZ@I$I$I$I$I$I$I$I$I$IdPz&I?'$5,It9%|`Hω/IM G$]N|Ij*Xp?$sKRS:ǿ] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM %w) J4#'$5,ܥ0(IҌT> Pr$I3rKRS2@] $ȉ/IM p/y_<pyb8wFG޻_~# mD?$Iן_G.p/y_|~p^yb?m86W޿O~ID{?$Iן_.y~p^yb?m86~ I?'$5,\ ]oy~p^yb?m&7=@$]N|Ij*X?b'?Kpgzwpv壛y'o~_l~^}˗^~e|i{7? M;~/&U޿O~v&Wyb?mp_6$@ȿIt9%|`y^}|Cm~\o[oc_jrqGn~[T7wn4yo*_'M~;N68Mޯ~|`o}$IT>w̥>͟|g7?}63wn~cs/K^~ *_'M~;N68Mޯ~4ymI$sKRSNFn`1|׿<裛?wo3[̓>^zs[柹4!3%n4yo*_'M~;N68Mޯ~|`o}$IT>w%>7/eW͟n~c߲5YkyipÍ̶Gn3<7ݑ{ߌoI\.˵k=fzSO]G@?2J/ B8J/ B8@ w  w| Bxͧ&k0O5̸jA樣1_ߒ\~,_~GshF[} h}hWZ!͍h}hWZ!rHx CGAD;>ADFXbߚ\6gc gC@4\HF+Fsd hn4wHF+Fsd E3Pz=   2 }7⚬5J<9#G&_/fqomƷ&.ԩSQkܹ84D#ϭd hn4wHF+Fsd hn4wHF+P$<# " ",p)Z#5+yn f#_/ l` J2Φn~yV[om%4D#ϭd hn4wHF+Fsd hn4wHF+P$<# " ",p)Z#5+Yc5>D|@ulwJ%WLn\{@?2J/ B8J/ B8@ w  w| Bxͧ&k0/hf"G9̌mŗܿьD\.[ҽ{ws\}\Ϟ=z۷7/B@4\HF+Fsd hn4wHF+Fsd E3Pz=   2 }7⚬5ZüH^7[x~xݺuss,ҺgwJrwvn=С팜xEhF[} h}hWZ!͍h}hWZ!rHx CGAD;>ADFX`OqMaVZie/^v܇b5O>f<ߠO'˨_~pIfZ;sX]}ۿ4ht՗WZ!͍h}hWZ!͍h}!7g0{AA?Adn>5YkĆy^g};ΌY?j }3>\F Ǟx.fZyw]Nvy-oKhF[} h}hWZ!͍h}hWZ!rHx CGAD;>ADFX5YkԆyyoDy13V~iklW஭r wf\'6㪫vDj ~ne.$_Gs@2Z_ip47;$_Gs@2Z_ip "( =AAA>`OqMa^ǻD2Čl"]ݏsIRz3oÌ&|Y&+_}[|vx;wv_N?2J/ B8J/ B8@ w  w| Bxͧ&k0/o\>}>yMMM~2[ni;xP3-r\@o;hgeG&}O@4:\HF+Fsd hn4wHF+Fsd E3Pz=   2 }7⚬5jüOg}\~43>k}k׮'4".q ? 袋n,Y?jY}ݯn ~ne.$_Gs@2Z_ip47;$_Gs@2Z_ip "( =AAA>`OqMa^'6` 5lފ+Tjh".q K/܌}2iz۷w>?,N?2J/ B8J/ B8@ w  w| Bxͧ&k0/oSn3yYZe~fq2c*.-5\&ljzaf: ȱoGhF[} h}hWZ!͍h}hWZ!rHx CGAD;>ADFX`OqMa^Ͽzi[r%e0쨣ƌF\.[k;,ׯ_X2m YO{5Lh՗WZ!͍h}hWZ!͍h}!7g0{AA?Adn>5YkԆyyu);E3>mqb-f]Mf|5rYI@7FF9,VWLn^f ~ne.$_Gs@2Z_ip47;$_Gs@2Z_ip "( =AAA>`OqMa^G?;E lƧmwzݺussՌF\.+i=3]vfySN6$W_ݻcs2ht՗WZ!͍h}hWZ!͍h}!7g0{AA?Adn>5YkԆyy۟ ?C9ߘiuAfGiV+.6:|dM7+5eg̼+M7C@4:\HF+Fsd hn4wHF+Fsd E3Pz=   2 }7⚬5jüOsZI_`Ƨe7*5uz<_\&i?>}9"6kz 0rESiewet}Lkԩ{eN?2J/ B8J/ B8@ w  w| Bxͧ&k0/oaG}<73> 묻Ywy42i@o=2Dko35#F4DƌgƷht՗WZ!͍h}hWZ!͍h}!7g0{AA?Adn>5YkԆyy۟8ϿLjo4뉎g_|eƧ!.mi跱[nf=oW_w:t0sxw_`i s/s B8J/ B8J/Cn @aq p'(|k֨ ?-Yk~zܟ51bHoǝv2c˶4Ma,rǹ2julYk׮{g2 ~ne.$_GsP\WmƦ`V*9h}h\Qj׋{tIn!GŞ'1c@i}hPL[7mWx;v (B8~7~vDVk( =AAA>`OqMa^%\z7dƷo-fޭ4˶66| 3_O2˨/,9rQf|%hF[} h}he~j^EbN7z hn4wP(Wo*f,(vhp47;$lbId5/| ϸ! ϑ =AAA>`OqMa^%9\SSWo6Y=zjOK\.{w /lg_6D>K袋޽{~,S V_@2Z_ip47;ŕŗ]^B8+JJ# k_GsC1qo]Qj@@rzo ᐛ˪AMVkωAOQdQmZsA}ڵk>3-2{flrYM@ g9#k{fnWht՗WZ!͍@qee,z8FsR@5ڡhPL[WB8jPռk<$s?G6{AA?Adn>5YkԆyy۟ADFXpg\㐄~NȆw  w| Bxͧ&k0/oSΟl7ȣIlŖfN)ie ?a7O1?\|ɥfޤhF[} h}hW_vbN7z hn4wP(+TCZ!͍}uEA/Cnj/5Y F3qHB?'sdCGAD;>ADFX׮];3_לoeƧ-.i5yne5Gnf(}vgFm_3oR4Dϭd hn4w+/f1'ip47;(WڕF@FsbߺԀޠ!7U\#8$9# " ",p)Z6Tj=4555~Ɍ&v݌B\.j}}fH=˯` f ~ne.$_GsP\Y|595N/@Ԯ4P vhp47;$d5/| ϸ! ϑ =AAA>`OqMa^R<8siJ*f.{5c4z{ﳯYGkF~hF[} /}=;8jf,ƣ_GsP\Y|595N/@Ԯ4P vhp47;$d5/ܴ3ܔ /[oiƦkYs5IDlq p'(|k֨ ?r˙6`C35<[jYe ?zƈ~oi+V_︋n4_ouZ!͍@qee,z8FsR@5ڡhPL[WB8jPռss|-شq@= yn ! ϑ =AAA>`OqMa^'1cǙf|Kv;flVrv@ʫf=a s/sw$_GsP\Y|595N/@Ԯ4P vhp47;$d5/qjkYs5IDlq p'(|k֨ ?I|}f߽SW޸_sݻw7sx\f7r̺!j s/sG@ hn4w+/f1'ip47;(WڕF@FsbߺԀޠ!7UMθF<7\㐄~NȆw  w| Bxͧ&k0/o;h[r%͹fyo7c,zܹY_W^o s/sG@ hn4w+/f1'ip47;(WڕF@FsbߺԀޠ!7UMθF<7\㐄~NȆw  w| Bxͧ&k0/o0{{weYeV g4N?2 }4MmFsks@k_GsrE]iz hn4w(&+J HN Z!rS{Y5j^ kYs5IDlq p'(|k֨ ?I2իoxߺ:e;w\ftq̲௿\k-wɌM s/sG@ hn4w+/f1'ip47;(WڕF@FsbߺԀޠ!7UMθF<7\㐄~NȆw  w| Bxͧ&k0/o#F4~_ƌ/7‹rkf-.Y6^xeV[V[o>S3.-4DϭH |s[/(,ŜPoh\QjW^;B8~R{_^V j@zB69z@>pC9џ#z=   2 }7⚬5jüO[f#ObƗ[{u22cˬ s/sG@ hn4w+/f1'ip47;(WڕF@FsbߺԀޠ!7UMθF<7\㐄~NȆw  w| Bxͧ&k0/oVx 0c#YfY% [} >_GsP\Y|595N/@Ԯ4P vhp47;$d5/lr5,乁|$s?G6{AA?Adn>5YkԆyy۟1<‹f7|3;mfl-@}ϭH |s[/(,ŜPoh\QjW^;B8~R{_^V j@zB69z@>pC9џ#z=   2 }7⚬5jüO[͝޽9ޑÎ2?wfKE?2 }4MmFsks@k_GsrE]iz hn4w(&+J HN Z!rS{Y5j^ kYs5IDlq p'(|k֨ ?8qu_>,38p3o@}ϭH |s[/(,ŜPoh\QjW^;B8~R{_^V j@zB69z@>pC9џ#z=   2 }7⚬5jüO5^z8Dᦿo!f7ʫ̼KE?2|Kou>~nunS&'\|xl#^q\qGiw8FNv7ޗKctZ oznkp47;ŕŗ]3 |T9m0׉ܰr&?fYPz hn4wPkirx?&ݶCr;~:b7{JumϦjC/ܡ17=|J&W1Siw~=9fYN{a;I,N~?wv'^rnq;rbϹz#>\kK.;ԫsyQ:oJq>~{_^V j|/sϻkޝ1LwI'_f=JewO?o׸SON:7Jߧyy7?^<8]~4w9#'F9]0Bw7[ͲYZ׈ͽ{vWL^|b3tws࣏r~r/yǝS 'N?cqեꕿn=ӥ1t>\|ɥwf9!ύ445aҺƵ;nVwɥgM(ݷO>erE;*}}gC9џ#z=   2 }7⚬5jüO]o=s,cqkjj2ctY<3g@}ϭqyc{{-9t~VK̙%;٘xKfls.z=7R}Wr u`M_ßfҋ`OqMa^Z//b>ށd櫥\/՗4ߘcyW>+f[u[f=YjƖxn}+m.[)RϘ¿PRϥ3 =GB8⊫E+ks3O|v:DԵنq̿B/@еoг;VeFC1:zt94.vhp47;ԗ"}n=z'Qnn֬# m}FADž;e+,oun/cڜ3{ج[y,ܺ[RQ8ݗXu8wSυ.qY(~67YO.{ߚv̇ ᐛ˪AM[:ud|kߜpE5=oz~;]SS4hpE !Zj0sVbM7s7ln6k㐔o8쨣_1i?'K/ن8qCK׈^i|S/].=ڻus=f8Rkr /;om֑\5ww^zi35Ηz>7kPyΛqm1nSe_Qv]П#z=   2 }7⚬5jüO?Ǝ_cOj).4~ne.ԫ+er=tI0LSKNތ:~j{MVdw}%A@ hn4w+mˮ-bJLR 7-⎹:Nť FsBծI g[ كelC/ܡ>q[@auHZ^'nmWռQ8EnxuR?9~?L6<,uc"g^-ɏ1*u ͺ=Z!rS{Y5f{QK/3kn0f}:^Ht-^:6ۻN<ߜם2|ر')LΟ<Ŭ#OxiziniN/5uu&UM·LN?#g59ZͿܐH\+Ϯ*ͯF\q>JQFU/iVϬZݻww?Y9П#z=   2 }7⚬5jüO1wy5VXrI@V_yn)m֝t;R˯lƦe{Q2 bsT/(Z4ˮb/̭>p4^>bx^#B8 Q> P_+7pe!vhp47;Wp=z/c֗%umVIjɳތmj?'npعWog.sMtl񊰟y2OJkbzi2FzkpMeՠy~9\d50ke˭2[:9qk˸˯Vjܧq9r:sX5ǹqi9/+x-׷َ4̺*.W.71c*էO桞ύJ45q^K.miiz =sdCGAD;>ADFXX Ŧ_GsP\qhs_vTsd=/Z.ycE Z!͍պv4lYW ܺX"Zovhp47;Ur:7ueK+5րYK1Vnǜ#kndekːQJ?dzzmo~;l^K_^V jw6FwOt|>{=o7'\zss2m MG32 fj?QGc1+,ǫT=79;ʯ~I駎IV[o]szkŬ? /{ɧ6E\kܟu[lyS.Ȭ7K|nѮAIiΛJ렖xͱ5IFnQ#Ιs6+\_ Թ;vuf{1S7洿=X)k!{8n;btwԛ݁g\vz[w]R}W25ϵgmmg^rX˯YHJlP s[/(ZTDs>t]XOF[jȽN[RSorv>7p}]K1fšFsjU^1KN{4]7ANuMߋwnh]eW4˩rSyZ6?z hn4w/[4]Gs|]C_F]Py\wܹF];9qFi+7>~ݞ*}C~?q5|"56gHjk wܳt2wŷvu?g2j6pS۰fۛ1i7+~ڲIK?_-JnCN,wm:`\s|^t⿏u:}+3=C7>lwͷ>W.t{Ut{}ʭJ?wiJ.?dHik_zTFF%;o*<4FWmf]{g'~^tnr9v ]zȆw  w| Bxͧ&k0/o~aYn˭2ˇKE?2tV|SEݎCOp~̡KNΣiE Y?:.<ҭG/ӡ'lniw7٦& u*4c4=B8⊫EixŜ_mP s/qGMQ`k~#&^B8Ey[u(hwĕAtu[F@KZ!͍Wox+;}8Y9厚7se\ycf$Zq#fr۞6hpMeՠy}3(?V:wwfRof|c}&D3_[Gn+7n(59a~MaxM s_0*󯿹+Mw.ܪ6>z$q䩧ϿcPsu71(9rEfjW.7ӱi;k{i~YoKM('3׊&횺t1(]> G9̬K׭ͬ?oy{)ҡC0'+M Gvi'7||sxB5(;o*6W+nO>em?F<8toY۷wxu<ң\lq p'(|k֨ ?ioKEKs/'urI@V_P +9xgK,_ \Ktٶ{ x?鲙fJ/u'=Y@15A/@ZԮzB:ݎ9M}}l%ri9z5i ڡhP{E?s Kî9/ܮGh ˬ:99,[ C7FZIfc"g-[6"aiB8hPּ'2,93>+q~7qȑٳ{⩧<-b˖ߋ-m_F?8l!ʝwd\# 6xٟr>W/~iYO>vL~.[M[z|a3W%>sͶۚ98,[ ߀LS7>ʊ)X1c[XsMoe++jz㛴]}͵fFPyyyݿ&I|C/⿏ЬG2zgK,a.֟#z=   2 }7⚬yD`7'o:nlq P_s/s?{9w-^1%5oK/G_okAGSܘ0s.b//1(_GsP\qh5 O܌q+&zz-?`3dM1(hp47;(uWF:sEJ˵oѡz=z/SjK|k_GsCm?w\z#wco},ԵoYGdQe*R 8Y6HXOߚ~,BI:/Q6˵oa]Ǣ=p<[n5jWײ+f ᐛˢAMZl*+}A6ߜ{]-;&{Ïq<9#liiŗ\j~κϿc欄^7_z+rrϙh}lK#F|`5'd;m66$n|1]|CΖj|fmMIuq2T*.W+fFPyyyݿ&IT7Jݻ/r 7euGܑeA:Xϑ =AAA>`OqM.8y۟4zQ-7˅KE?2B:s7M}\5yk׾YW7e"%`߄pU?`nyq[~Qf oznkp47;WV$9}Yfr9,S˟­., Zh\C3sԚDF[|>f]^^;B8j( .|]L58#w`ܭOe*W&k}e# ip[#M]WkƄ;/QJ]c+Fc<kZiRQ]A!7vqG}̌O?uMMMf=oƷ$5XI_Gf /l mFmԹ/]׮Mk4ʫcǎf?"Xߧz%>CĿ`OqMIyy۟4_Sg5˅KE?2n,漍lÞnrit]g\I*%6`siB;q=mI@鹭hW\-ZMÓ,/6W'{:fY& mg\i-첉r -{W̲mqq^xa9'#C8W~ ;C/0;K9uFu䬒b-kyÏ\S.f]aIjUWy#K-2i8"m_Jsu߮16F$נ_ysyoV /޽+XC?G6{AA?Adn>5Y'ImvքDgƇKE?2݁ǘs6gn ټp3z#](5e%`۟3˧i"KE|_GsP\qh5 Oӛ7:od4ˤIߺXf?kš Fs岪]<[i3o 3ˤ&DmڡhPEO}}Lferٳ^ˮ`i爫o,iyn8]Ey#yY&MGNaٜja?xm&f9͙_^ jҞwu+ر⫯2ixfqr#Gjk+4/Hќ gco+߰o:5ɧ 7Y}ݯnIL43~&gK.d*ٜUvf|;Vs_0ˤe_J+luT-,B_qM:(FccpС}#C=_f ңy՟#z=   2 }7⚬Փ$ ?i2&!@}ϭ>L~-׾}5@K/2i*MnN6$QK޾6˦5ɽ{^2$A@ hn4w+Iszçe,cҗu|)nƺi2?P,z hn4wP.u͜57Ό1V4Pz hn4w"=af|F8nߚP抺lZ9whDy\i ݌5ˤmw7V4󊰟-c-z7d1/0Dl\z]>Ͼ0[wL"_qy70`l5N?cYO}4^ /esZ8qge 䬵{?.]:jO7mz|4|ƹ{z#mN^kgkd! /נw޴!|:6 >G_MrBAOQ4;L͖[me;N' ;+٦/J?Ҍ gOW^f?v/,gW_P>i9_#x7<ۈp%v3^,S^V]ӚǞa'A@ hn4w+Isz b ;J3!Fs岪]W\s}3q^1NKdj(vhp47;F+\zY~ʫ񭉫\VZ1s~mb/i3> Su﹤نr4󊰟E򺂶ӼipMݠ&y-׷[2iyif}ޮ2c+wL"Ϛ`ևڵkgUnʅ4wf|iɧ 7‹/62UC69k|xf4Mt|V;Y䍷6[Zn6L ynxy}uPܽ|6cx4sdCGAD;>ADFX< 17=nA/46B:m6i/3Rܣ2f 9mَY?gW@鹭hW\-ZMÓ,-tY_d66Ӳ{y#F4c˯j92}ƕf|cǎf[!`'YKC׮]K?e揭;L33{칗Y7vx3RqǤy[3 "ObƧ17R?afl=96{nVnw1555kW*d·Ï8Ҍ +hFVbV3syn޽{MǶ\mf|<7~ EΛA#Gj:Es?G6{AA?Ad.PZ+\š=͹ _+uٞ1SJ72j^* oznkp47;WV$9>[(*of<Ԃ^B8Kv=fH5X{7lK^;B8U[4D}un+ے02~3fZXn5ͶxK.ۚ<6xKfNh>'e%xE"^[BiB8nP]/r3 :ud ~f|⎉7;{f}f=bƧ[n5g_3^_իfov0kk7qf|B69k|>J3>+quflk^} 3O}4c[RN=m΍_ΛAͼUn2hO9# " ",pZA_+5id؋Zɏ\%.o-v4+Kmy?-)=˞̌ |s[/(Z O⿰q%P z= hn4wP.\P^1iK# ţFslfyjgmѣvx/njmI\ Yy53V ;lOooI;*3WįK¤_sڵ3㵥1W,I3Q4wZ!rػ()}eU@PTEWT BQ@EHPpk5ĬnqWE4rw:ygzT?NN۷뙪~Ir|Anmaa!}M5Z{Ec= f|\{En S]zYE^\V>yG9ΌTEμ·|،O)N7N; 3.nSw7v邅f?^oo֦ZEt|:7~ +UAJpdŗ_עZב =!B> !$ 7@isk<5oV<{3>M~?/N!C&aƧG4S꼎 ٦_Gk_zj76aR/ ѓgi G-36Y\ً\^.#?n8Wx^g-i{_ڤ/jw9f|5ᄏۻofl厉xlO>3/ytPxf-Z?f|"T_n șWgu|RfϮtl]Xomf|V^cQZ_Y5(T>}Ct>J^jo5Gåuԯ#z=B!d?|BHB,np^AQg@V]0cvyuƌDm?kO?`Wls[/ȯrh $1i\^=1c -zM hmvP*Ig#Fiy~fj?z hmvHV>ywjƦ;-3r=wf|Zjudq@8fluj>xmYf<&_ڤ/j?t͚53z>،篿iz6oތ1t|\{f{?~/3>.omM!/x٢fÆi[~UfN: șWB` /yajs3jGY5(T}'h欉3Ϛ^~53~B!$B`q 08n9O8܌M˯3U)יijӾ'ﴥכ`@ hmv\/Z'I̹ϸ f.oG&ip6Z;(wǚcO0c4c>y, ڡh퐬<|}>mwGѼ۟1_NЛZ3>Mmoe__g& =x3ט4>y,}⿦0iB8&}I-PԼQGӤIތb̳ͼ޼Q;&m۶3c'mzݺu3clk vz 36mqٯ/_Py·6mۚI+>x,x‰puj,54uwEflm3sy{^Ǧo_ɫf!IDATa8Mixf)W͛Y5(T&k-]=z3<=unuү#z=B!d?|BHB,np^AVy;m6ȟWQ5 {KmW""g^AŹ;ּ{C/\j+36+BYe*wyfڵ;6.~B!$B`q 0oٜޤ4cV]k6ߊ~gƧiǘ}9r[ MmFk .xĜu:7ef,EI!Jݻi˛`&}'^;B8ZZyUϿgr=?isfcO0ck˞s2c}Y۵K P%@MRz[63f˻ofl5>&`tb[Z36m~r_PyO烊s6mژy/9 Vi undW|q7_oR8~C߿[j/4@u$CG!O|BI(z .P+\j߱9OI/7cC(@:.nҤ}hmCJ mznkp6Z;U/9W?5u-f<EI!Jֻ^u+6}Y@C/!9yܯ=64͚77_r=`Mش}??n3U0^gmﱡR%ipMZ&y5ڵ7{#GkÏ>q͚53sE6oތFǤ\5A֛![`+ș|Pq-:GCբE i(JSs#נR!ΛyMzСnڷo.[ΏhHwB!P\WЇբe+sz,^gƦmMC]ބK{ػfGf|s'oG`@ hmv\/Z_H{ro(k_GkRi.^oƦik>yQOz hmvHV>}Ox{9ލcW}M[f|v'o!?0ckvE](3oזj/O/Cmҗ5I[0_Y 3wɼfl8&5aʵn]svش}ݿj\[z_PywǛ8i[yck;צ*M΍_JMu'O1c+@鹭hW^>]{`򎟵Ќ5I/@{moN،M߾WB@G!v;0x'ͽŒmLޒ?7Ӵ>yO7ckmBs_bɫfa C1w3ش])_Ek:.C:>M=wϺ، ٦_Gk_zBZs߳{ѓg&ip6Z;(ww037s4>O^5 vhp6Z;$+XiƤ\ͽ3>M=cf^b& \^sn'<ϼ][DZ?I_R $5?oj+W4kK뽱c0c#cXr徟Λ=یMk?WDEμP烊s9]tƤ*M΍_J5[ÏyS_mVls[/ȯrh}~!-9>{ ik_GkRq{:{D36M#M0Uk_GkC?<߆mLyOV}.Xm֦!,8Ƈjr83}Y۵K P%@MR;lhuכ59}0c#cXr'MpIfl6>ٯO?_Py5:7uflcPjPF֯AYy2q]fG~B!$B`q 0r9OjKVZleVCޤ4Ӳ]f>y笺ӌ ٦_Gk_zBZs:L3&ip6Z;(wz \FimC>y, ڡh퐬<|>3Ƥ\Mp~>ڔ[pϚI)l@ _<9}'g[>vmahB8&}I-PԼos=z0 jƫ/Сy_oH(sm(ZnmV*"g^AŹyF2cR4:7~ +XΛ-rmw>f_K_o6G !$ 7@iskœ<:v^x]ǧЉgDq5ӲѷUg@  hmv\/Z_H{λuڶ ik_GkRqGyӮflzy,}R%ipMZ&ykr邅f;Es3ͷl0.7ߌ4I)իVn"I:>M;׷o?3R9B*}mۮXo,J(=kP_vr>o+Za^x֯#z=B!d?|BHB,np^A֙o6iќ2y^fC1c+Q!`o72eJ?EW>3,d!@~E iqyҚwɆxH^B8Zw\T4i=[ncɫf! Fkds]oE8brS^ԟϗ[rƊؤ6yپW'/\cBsk^еjWx^gޮ-_" _ڤ/j&SעE -oڴSR~1:}MfɌ4I)=g^4ԻwO`V*"g^AŹS/ECUBYejכ?sQ׮]WƼZdq!O! EoJ{} @XKz5iԜިi')J{k֢[kҰcx۶s_܌ ٦_Gk_zBZs.e3WјixH^B8Zw/)4 3> .^o^;B8Z].6r=״Yȋq@u><)֙I~1f^ νιӯKQ5 yyxyT{0~!jIjrN8qٖ׶m;/4?^a]}رGqH`{C״3fmƧM/):9f|B-r:T ߙy-^b7 Vi undW7_|vm~5?=u$CG!O|BI(z .P+\ uͭ33> tٟwhWCES4I -[1583R,d!@~E iIg=|^>XH^B8Zw]'}Nf>o׽4Pn!^;B8Z޻nnjm,E/yMj'urfoisؤީv@6zPC~`񼼼rYT{0~!jIjr}W6y/5c{h!cR}H~/3> ;O[znjTEμP烊s@oСf.o0cR4<7~ +j 7; weƣ8ב =!B> !$ 7@iso\-}}f|[w싷!f|=\!+6(63R! !@~E iIyEfw`@ZhTL6y͚7w+$-Z})v! Fkssf|cP, YWjhw$37xfl޳l>:L3[ef_]ӵ>HO/Cmҗ5I[6 dn߮OX(-ߛqqL<ƛn6\ϘI߫=z4 EEF׌[AŽW0s˷Ơ*M!ύ_yVZ}/܌Gq֯#z=B!d?|BHB,np^Aު+ Î1(xяJ{ifn%i#Ǜk_ČT{ dh| hmv\/Z_HKbޡ0 iz- hmvP*u5w5f|̿Cj/z hmvH^>kw93e4m=y]|~agIó̶6rflB*֬YSƠ*M!ύ_y3p@)N7cQzHwB!P\WЇ4 #ƞ`עsVi'?y^f-[R.u%eϸ͚}F$3>sm蹭hW^>Ĝޮ#2sz͚7wx֌$H/@$z~c^5I!fJU|k_GkC?p(3׬EʋCg^$~>?f"='`aN]vmfNӶ=S;缝gT }RKO¡6Kj͗_ul;i;q$3/yx,ݤwC?j'׺uk}a^#>ng-^}af>e˖Ï>1R4<7_s^}Λ;Svz:zwo瘱?=u$CG!O|BI(z .P+\YcעmpW?yMF?lhc(=\y,j]~(.ɣfNuvmx!@~E iI靼pEg}M.i7?z hmvP*O1s>l3> ǝsٶB@C!~mgQgzRhÓqyM%l|Ut'qZR]9FOa'a5/R> yyxyipMX&yrι6=gka߾ҵi֌;3_Bǟ4]k+$̘y~щ'Q+f^}n6|PI,x-|EqԭR~XPj熗kPߟy+#ݻw/̯}>\3Ydq!O! EoJ{} @ާ9g8nyMkҴٶפI7熇k(oWvq vhp6Z;#g.s1kO\~f~ڟGQT߭yOqJ;w3szmwH6=kugR]p= i^f;3Ӓ?wڴ3VY˵HO/Cm5i[w~W,[^׼{hSc `0/ 0y$X&.+] 2 =\$OwdY45Q-3oWhh\ hmv\/Z_HKb΢Ӗ^o-()R_}MX Fk]wP3oQ=b.D/^۬I}z hmvHO>'_YԲut?-e3z<@9I|~!?0s&~m.?<pmipMZF%5o%FvٮNݖFߟ{=zt|oScy3߷|պ8ֹ3ǟ~n.q u>$_W6m/my: Vi yneys٢f/2ٟjᡇ5+pf2C^[oҴ;uz3G}].taf~иyhW^>Ĝ>]Ԯ6n.0*2j3@@/@${+kӾhwk^м>־g. ڡh퐞|?k0{ lּ;g՝f|]{~lᏌ7i5sW%?Duͼ}_M97aY'Ϯ{sSωmo}ulynϚU_p!(nule붅u4` EV_t=B8ZձJbR+]kVݙo6 ]6ɭnV>G٧Fk]'[i.wBƲG.̧Z >vhp6Z;+W>N?Ck7YԵlfw6ԥ\8v,wy{O8Q|GߝE6)Goٺ9K =(_V5Ssڴ3s:ժ!u~z ds==sufR_b^WG:lhpk_'OiFk jEulS>V/s.-򩾶R9]jƊ^B8ZJwuAfR~1Eb^mt[uj70`Rq, ڡh퐾<|X]!cf^ּ'(ϕ6u(}Ņk^c|] SߺdSuZteg2c\ z;_k܂_0/إGo37' w/>|mipM\&y+dfuIuLX~~o]m(u 'R~[7qkGԼ8w!7o1CA%wƚ"ؒ˗ym|_eZ_u}]%JPYey_-Z,V][o3A !$ 7@ish&^̜?Q#>v/YkSGr_Q{#exVz@Wv4̧w՛sf;z{Er哿q'w;oݽkY/ȯzj$1gMG2ݕopeqhÏ5zM hmvP*uComk3M6+qoyܼﶳ͚9ۺeUZ@C/!}y6{ifYuifR; ֽR+='ψyB|+լE3W?y}9͍6~-W=mOR~y}mpĴhiw_%K$O¡6s4gkݺ~My}B5׬5CܦM;~[f>5ܖm樯7V!Cu_y{ɧj|PI/rt3[6D_졇u{oμ Vi un$ //‹.6s(:4~e^[7\x:g}ϼ_G2{B~'7X>)?~ᅅf_{_!`;q/9|ױs7 OQCGN|] :M]G~^b3W9_mW~/ĭjզ]᯷_p..7٫p;У_Gk_Q%1gM/:LOq,ZWX`bq/=[w6Wzf^B8ZJw=o gvj>?^_a!9"o/rݥgR~3}4`3&΅d^;B8Z?8l&:uqP|?6<zۺfo}y*U8/n_/EHmb}mw0S~qCی}X%rS/]UxXp oz z R?[\3~K|ӝ {ټS.6<Ϭ^[⾿D~!jIz(&OmymB7ZϲVԓO?.*x.7mmݽ翗BVOqwvڻ%/uO?߾(Ɵ7|}C&cBs:MB*}N>yFMuLZ)k̡̽|T*F9YyY{q7zsS竉珁>G}΅-Zy=u$CG!O|BI(z .P+\6Wz.ץE֮]m_N#>nڴW_nZ/׸_m֥Yuҧ_Gk_Q$1gm&[Ybke՚_ U~6hp6Z;(feR?߆CڳyX3.΅d^;B8ZKknMZn:oew׽O‚[:6/Kz@KǍ?I;l??mfu-hօ-1fL z7\~9n-[ug.l?.QbCatlF?խ3t)ff~sS/ȯ(h._{U }Gի}n@A/@{+;8/DձKw7gͶ>ƌMb! ٣Fk]p_5])QOYΌ=u EO^?Rnu}9qI,yOnMsa ~zHwB!P\WЇ4 =(sNǥ{۞6ۍSԇ/_gմYs7%f{IYc^k+t鹩hW^IYz dK_l@>5B/@5>r{9l3sK6n8D3>d^;B8Zt}i?lnzӗdz箹۵i>n/5`Hߛ ?.F>wcrM63ۭmp٦?:Kja}7lXH`I'Ouh( %sYKz}^xl_G2{B~'7X>qsCn=GsZ[u&^̭{Ugyإy]T۵wN<-}M{Sw]&M~EnC1H!@~UӋ%9+E\9zͶ^+B8Z ٻ^tc?n~Gգnn={h^B@C! G^>[3ǁcb]?pͶ\8c31:y.?‘GS{*Nqk\h?uQuƅnS3κŃv~mIipMZ&yכ].5{h')1aőoս{w暵o3J-߻ ̭={pTAX'rGuk,?q]f[PjRFY8o/˖vlZZr3>}?~B!$B`q 0i ~Z:/|`NJ}^c7U_e=q.pW?7mx(A{ຍf>ayh̚z筻mgV*9p =f-;ſfaǸ ^7B8ZjxFa!ݏ;;̜59i5^k_GkCÓ}ݘul3m[|߫f8]st}Ґ~>]|n~]̾~ȃ9,AԨX'~pƅ6ѿ{Mu Դטze&%/ﳱ^[*y O/Cm-cǎӌ"y1g5_I&1s1Z?յli flc/SN^c}n(7`\z E}w̘g3$n6_`&?ts/bu׳ /R sYʝ7IA~¢rHMݯ-r|g3?Я#z=B!d?|BHB,np^Aиg{|7#]]vw]zvmPum xF/{/{-3O` Hfav}vݣKzQ[.O-| >tt{4P sKz޼@Xz hmve+Cᗣǝ6 0\/oW?3s54~O^q?nl?G<[݁ǝUS !$ 7@is@ZX@VuL/ B8ZJ/S;flc _B!$B`q 0d^ǴB8ZJ/ B8Z y;bީכY^'!j O CG!O|BI(z .P+\z hmvF+Fkh hmvd3ײu[s=ο~3< Py=Pz=B!d?|BHB,np^A _Gk@4_ip6Z;D_Gk$k=̵h*/{\/Cm @aq!O! EoJ{} Bc!hhW!I/7bonۙY^'_B!$B`q 0d^ǴB8ZJ/ B8Z Y}7bo'Y^'_B!$B`q 0d^ǴB8ZJ/ B8Zo[p__yy@h}¡6{0{B~'7X>ia@Y1Fkh hmvF+FkYw>&&M3~6\=_Os'#BH'>!$(a.H  ip6Z;D_Gk@4_ip6Z;Ȣu|⎟еi׿tvW>36)K6Znc^]0㫑 44¡6{0{B~'7X>ia@Y1Fkh hmvF+FkYssmw\!32p(3Č*/h{]/Cm @aq!O! EoJ{} Bc!hhW!f+v1׿i5۔y+vs>HߵB8@pwB!P\WЇ -, +:hW!hh .#IӦ5m{|L] n˯3V^'_B!$B`q 0d^ǴB8ZJ/ B8Zdըk`!+< s&Mmco#/hH{^/Cm @aq!O! EoJ{} Bc!hhW!jm0R8}nݫGqmO5'c^[_yy@C!j O CG!O|BI(z .P+\z hmvF+Fkh hmve3Wn5إ;lLMfr3g]uc]M͜Zm.y3G\>BB8@pwB!P\WЇ -, +:hW!hh N[zCaa:ӣn}u3ٍ;}@B8@pwB!P\WЇ -, +:hW!hh .&ף.暘vu}OR> Py=Pz=B!d?|BHB,np^A _Gk@4_ip6Z;D_GkXnu1)#8ѭyCIB@/Cm @aq!O! EoJ{} Bc!hhW!fꥫ\;c\vm;g՝fiBA/Cm @aq!O! EoJ{} rͦ\mu46z hmvF+Fkh hmvW; \赲v˝n>4y¡6{0{B~'7X>iZӗ R>32B8ZJ/ B8Zcӗ\h hmvF+Fkh hmvF+'#BH'>!$(a.D_Gk@4_ip6Z;D_Gk@4_ip <( =!B> !$ 7@is B8ZJ/ B8ZJ/Cm @aq!O! EoJ{} hhW!hhW!j O CG!O|BI(z .P+\F+Fkh hmvF+Fkh Py=Pz=B!d?|BHB,np^A@4_ip6Z;D_Gk@4_ip6Z;D_B!$B`q 0J/ B8ZJ/ B8@pwB!P\WЇW!hhW!h¡6{0{B~'7X>h hmvF+Fkh hmvF+'#BH'>!$(a.D_Gk@4_ip6Z;D_Gk@4_ip <( =!B> !$ 7@is B8ZJ/ B8ZJ/Cm @aq!O! EoJ{} hhW!hhW!j O CG!O|BI(z .P+\F+Fkh hmvF+Fkh Py=Pz=B!d?|BHB,np^ hm/ hm/D =!B> !$ 7@ixip6_Gkxip6_GkyHHwB!P\W@B8ZK/@B8ZK/6P41 Vu)\Hϋ/IKr~O[=oۋo/V+zIy%iX\Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR= 0KaPy%iXݥ0(IҍT,R$F^|IZ@Fw) Jt#/$- %I_e] $ȋ/IK2.@Inŗz``t$I7KR=Iy%iX$IT,D$}^|IZ|"I?/$- >$Iߟ_$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$A%I$I$I$I$I$I$I$I$I$Iz@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$A%I$I$I$I$I$I$I$I$I$Iz@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$A%I$I$I$I$I$I$I$I$I$Iz@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$A%I$I$I$I$I$I$I$I$I$Iz@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$A%I$I$I$I$I$I$I$I$I$Iz@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$A%I$I$I$I$I$I$I$I$I$Iz@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$A%I$I$I$I$I$I$I$I$I$Iz@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$A%I$I$I$I$I$I$I$I$I$Iz@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$A%I$I$I$I$I$I$I$I$I$Iz@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$A%I$I$I$I$I$I$I$I$I$Iz@I$I$I$I$I$I$I$I$I$IdP$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$A%I$I$I$I$I$I$I$I$I$Iz@I$I$I$I$I$IUqowl5tZa)f؟As**Q.**i`>1M9J1 ۃUQj,m7 W= O~/ -0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0P6q>'>@%`(@9*KDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD@ωPI,X""""""""""""""""""""""""""""""""" }N|Jb9PsT @w)lS@?8*K )Ăd] N|JbR.m '>@%`)Ydq6X,KaPI,XJYܥM$,% ,Rئ~pT @w)lS@?8*K )Ăd] N|JbR.m '>@%`)Ydq6X,KaPI,XJYܥM$,% ,Rئ~pT @w)lS@?8*K )Ăd] N|JbR.m '>@%`)Ydq6X,KaPI,XJYܥM$,% ,Rئ~pT @w)lS@?8*K )Ăd] N|JbR.m '>@%`)Ydq6X,KaPI,XJYܥM$,% ,Rئ~pT @w)lS@?8*K )Ăd] N|JbR.m '>@%`)Ydq6X,KaPI,XJYܥM$,% ,Rئ~pT @w)lS@?8*K )Ăd] N|JbR.m '>@%`)Ydq6X,KaPI,XJYܥM$,% ,Rئ~pT @w)lS@?8*K )Ăd] N|JbR.m '>@%`)Ydq6X,KaPI,XJYܥM$,% ,Rئ~pT @w)lS@?8*K )Ăd] N|JbR.m '>@%`)Ydq6X,KaPI,XJYܥM$,% ,Rئ~pT @w)lS@?8*K )Ăd] N|JbR.m '>@%`)Ydq6X,KaPI,XJYܥM$,% ,Rئ~pT @w)lS@?8*K )Ăd] N|JbR.m '>@%`)Ydq6X,KaPI,XJYܥM$,% ,Rئ~pT @w)lS@?8*K )Ăd] N|JbR.m '>@%`)Ydq6X,KaPI,XJYܥM$,%ؽ{wz7ߜ.tEiΜ9iiOOK,I˖-K7҃>^~Iettt~M7//CJKfJy{ҩ>O|"}_M6lH/B|Lhq6X,^ϋ/N;- n&@ޚ5k1Ҏ;wߝ.9p nHӟߛotX,{7wyiԩſ"k׮чܶmg>444T|"/N>hX0MsT E#38Q+sMrK_uwN_^|YlAh;MsT _tE[Ypa/~Vk4{ΑGu7ωPI,XJV?M3g,M7i֬YiΜ9iڴioL򕯤X|{94}⿍7o;~-8dMsT O{MW.-({W\nO;vǦ]vgy&u]s\Zhŋ_*n400P|ʕ+mݖlْ??twꪫG];梋.J>`xCJww}?$,%_;788+>MkFn4k֬bjnڴ)vag… ӯx~4sƦ$|Ъ>|Ădx:무e˖x[\jU1F.]Z|جX"ݻ7{WŋO<1 [5]&@9*K/c-[o[oMw ;v[ߴgg?ꫯ2gY||ߏ@k{MsT v[\}itt4Z]wݕ26mo'?9sN;w|#̙3'=&@9*K'|2~>6]w]5<@>}zfSOMg-4#C[&@9*Kt'|ߌGM3f([δi?֭[W<7|7#+@+;MsT mׯ/~׿o9d6mڔs[ʕ+gwO/KK.$^ 7ωPI,XJVwm߾=͞=sN:餴o߾x!f͚{C=os=x^c=6^ښ 6?q ZotXzl~_[;www9Shm\,XP<9/mթkצ]v 7ωPI,XJV7KiڴioO}*2aq;3FFFP&+Vsڶm[7ωPI,XJV7xsGL}c9묳o^(s饗{MsT =N(~o}[ g˖-ihh9?;w,sg{MsT =<@i޽ iYjU@ IDAT ͞=xNYfK{MsT =_|q|K_OX7o.Ό3zpѢEsr;MsT -騣*~{,2wqߐh3rx9R|' }N|JbR7Ms1itt42Z;rxp 3rO?oމ7ωPI,XJV]}sx_;rN;xmڴx؜{idd$otXnYhQw} oxx8͜9[ _~9r@^xa񜱹袋Ҟ={m]&@9*Kf,ohh}L>=ڵ+2),_{r6n/?͛7)S~iV~Ădu֭[6 /Ow}w|7tω7o^ӫoN7ωPI,XJVwlذmsnx{rV\/C?|z{[:+{>'>@%`)Yݱnݺя~/4FFF@75?/m۶?x夓NJ7tS?߹otXꪫ6gƍIeΜ9d޼yq{qW<@6mZZlY{Ӟ={#`ҋ7ωPI,XJVw\poyxr)SSKʶmҥK'x;ߟG߯otX8쳋6矏O*K,)ݻwO~4w͌3Ҋ+~wx4;MsT ߶ɔ)SҾ}%\R]9/RMپ}{wq0iI;ߣotXXpa69㥓ygiO,>`3s̴zu10!w$,%;͛WM!nݺuߕ?1^y5\/~dʔ)iɒ%K###chAMDwJFPHk`@" W3((D#\ "t %#( $K}SLOuU== !B!6=FB!B!$#B!BId`cD!B!$@ɕ~@@I;N8J=X43ꗗoF7{z˴kΔ,YڇDDb@!B!$1Avz|!B!BA&=6B! B!B! t\+>DOdJ*ֱiyǭ~yYpnݻwG^}զDĚmۚ 6# z !B!$&N!B!Bq#ȤdžB!BdcC!B!$1q+q ,wH?}l%Gunr^_^V^'DOdO})keӦMyRH1'-ZXxDOdYfֱ믿)γE ҥKMQGe_yđo@0E<\ğ]B!BH|_.}r_ :=b压/:^ϟSN9MCe׮]O>p־(Pz=`0py16?=6@!B!DϿ\J@t\ z$;w3f)bŊV$+WMCiҥe˖HAN/8c 7pc c TB!BOO D'XL1tPzyuqHa<'I-tP2dmz`cl$szlt B!B!@ɕ~@@I;On[/>n26nh˭ުí~dMŋb'=`0py16?=6@!B!DϿ\J@t\ z$˖-֭[)cҤIV9R7O O?՗i߾^ _1>n>?@20F B! t\+>DOd?4 pfǎzpZ{)M6V,XЬXB/|c 7pc c TB!BOO D'XLҸqczy7uГGuۼy^$e,Z(///^ _1>n>?@20F B! t\+>DOdru7{l^ׯ9&_^|A _1>n>?@20F B! t\+>DOdeܹRLsH}V?K7O9/cǎ9A/ ̿c|} d`.O.PA!B!$>/>/W|`1rGac/sыZZ>xOtB Y}4mT7G>Wt㏱ E*!B!'z'W,&Yˬc{Ck{"Ӌ:uXT^]7E>Wt㏱ E*!B!'z'W,&Y>}uT\޽[/J{^n6/>/W|`1rMխzYt)T^.\9s=L4IWRu꟤hѢ9H/ ̿c|} d`.O.PA!B!$>/>/W|`1rӠAH"fժUzP9rYgQeddo,X0|u>J;0 _1>n>?@20F B! t\+>DOd~3 6mEBcf"FaJ.m>MϷORR% _1>n>?@20F B! t\+>DOd{ֱΚ3fEn׮]Zj־z9́bn>?@20F B! t\+>DOdmԨQΚ^zEfH">z9"ZRlYIÇ[eĈ9A/ ̿c|} d`.O.PA!B!$>/>/W|`1rͿ/gM޽b 7w\Stik߼*T,^X/uZҤIgHBȱSO^VXA>Wt㏱ E*!B!'z'W,&YLѢE5~믿 1c SX1kfĈzM:Z_\vezIx9usc@0E<\ğ]B!BH|_.}r_ :=bƌcw믿>^uSP!k_F W6mf5\c٣ ̊+QGe퇗aÆEOz`cl$szlt B!B!@ɕ~@@IV"wm۷O/g},Xڇ9cͶmfSD kYS~}vZhܭ_>/}/e˖5[lы!8+: H"B!B=r+q k.ӴiS4n|wz07|] *ŋ_S\93~x_⧟~2׿f͘1cb=`0py16?=6@!B!DϿ\J@t\ z$+_LE ,hvj6mڤWg3Æ 3K[) 40}^<Ϥȑ#MѢEmeM=`0py16?=6@!B!DϿ\J@t\ z$+ZPRHӮ];F… MnLٲe甠ynk~iԨyg̖-[jbcSO?ZNͲe*'z`cl$szlt B!B!@ɕ~@@IVzڳgҥZJ* 6|g_~1;wgw6ׯ77G67xcLﲦAfY4Aafm/ Mѣ0aYdٴiٿ?bk׮5}8p袋Ga+,Y̛7/^"+: H"B!B=r+q &J*e&r? ={]A@1Wt㏱ E*!2o1gquΔtF=!ټui )Vt >/W|`1BVyMӦMMJn>?@20F t;Η ߷l!B.|js1XO}ڧC8@ɕ~@@IY 4ȜtI.XR%sz3qF3j(ӸqcSP!?,/htJ=!B2'M…dV{ףO D'XL۷? 2\}զynݺc5K6GqR9M&M̥^jk~mn:w^7ߘѣGΝ;֭[G __XUiР9M#DOdH鯿2}8pҥ 'l<ȹD?{o47SL1ӫџH2216? vE*97m6|yZ ;TP+WԬY˜V~ X+40SN3?ZIf;#}<1s=ԺuMJLٲeͱ0z9Isɥ^ 4K֗xS'()YV{B!$P0{5^iw1.}r_ :=b p˙< zZ_~Ѱ8LѢEJ*i\}8֨Q̜9S7c( VZe `WnOb<,֭[7?UcZ|/_~np,\/^O*)l9 1>3ydk>'Un )\,\=/^z%볐[nMb k?$}MNG#\Cҝ~/#c7~FÇ[l߾]7xg0ͽT{?+恉ȤFH,[ zdwi'7BgwuZIF^VF[m^؜n9Tmhƽ:޴H?ݟܤzc={,ȼ>ao^Fzjmrk%7wb%x^")*Dh}ŗ"-OHB@;oIb̼oڻo>/W|`18L~ZoF7OKZ?͓bǎpIM[vi b sUWG}l۶M7CHQ@~,X4k(P:5v={TeddD׋ `?ꋗaÆ=Ȏ _~:.;L7M3"eSÕEFtI7 \߾}khϞ=3ҵuĕkHO%bV^,)j&_lݺU7x2Y0Lso \zg<01"]"]27Lsύ<$9#63~H1_nlߑSw߳'2OKoSP!k38|kAgͦJժHo>&GLn><#̢ڇ%RRﳤjjV[{k>Rtp k_ C(hGWRIͭEO D'XL{93f56^ﯛ'ň#}rI1uTk$ˬvͳYfSl*U̒%Kts_)R:# 4}1th]s5V?4i$2fH,oKZ̾}"=Ȏ /Vj@@a~asl*sx2駟">,z?7N/tw`xqD}*I֭uժU+^(t=+4R+&_dc T3gOo`CPXSf˶?} :~E(FG +^ +#~~X ƣ@PrM̶?vZ˄!$&k<=Vo|$$`yskLLޥ@ɕ~@@Ity/g[N=>SO=U7O;7/n;wMrE首-ZXy93ts\w<ӨQ#j* A… GrO4MݧkERA+k(t=K4Υy`bE&=6@Y}jm?Q{{tk_Q& ƞt|dͫ)+(QҼ1q?+M%K>/{z`k0$54l|W־P?M5ERvyv+u+q ,3</k׮jΝUe˖J*~IFf/Xz1 ƚ5kGm3";.rW^RdI_J*e/^w%4v5s^A\=&//Ċ/=Ȏ /Vb@@za~aslrx28pTu]q~◼%](;W#\C7} T^*T`l٢KRO5D:ͥςa{a{?+恉.7&#<{9Mٲe#C=- ۺ}Aį(cKn/F[|ySX1b+ZkO߬yЋW_ϳKv(Hb͡^㉊\ۺt%n]B gΧEzl$<zZ`O^֭[gj|z1 j׮]nݺB\rW^棏>2{ѫ?_|a~aS-_Z]ٶm^M(߬7D+WEO=Ȏ /Vb@@`~3?˦2WKYfY/Ap־}zєNg!>}馛b)oC@̥ςa{a{<0#]¥eۭ>GKZu]o|i16lֻ1w`b˯4hзIN93͒˭;~E(=g `>)c5oɶטg1/r_ǎ7Dλ/ȣy0kN9Tyvkd$D{<ܘӼE s "?t ?PhQk}DN|s$*Rty/.DO D'XL{-[fVZ եKktWi>Ier: .ԋ!d( V۷YSti3tȗscfرRJ:梋.2^<-Zd *d?P/zLh˕W^##;'|Xgϲ]vV$-[M7|pk?rJٲe޽{)+](W#.]C7}TY#_R={^4Ν xl} 3恉DI1!ߜRDH?ZOYOgަr*sJŊͷ`'+D@e^}kDo[ۘ)SmqlؘaHȗ^oN>bMKHTj6mjkއ:Z6`u3ɧ!e+^ڦr^ȵ_xvnz6"?6wuP0z}}[#ӷޅ~@ɕ~@@Ity/N8#dǎyTZ'M)@I2`"_+S *D~F@xǭD֜xfz\ٸq9sug<K"əgi:E0WIFF9#Hʕ+"'=Ȏ /Vb@@z`~aslrx4ضm)ZɸqttI~^Ӌtw:ࡹzqLX"c7~tKTx߾}~ "pgͽrazI.PBb-&Gn6˺ :)&\t.TQnvsFMzYSNֲy#"EvF^3ޛe-p)Xot}#}O1xτIoZ'9묳xך/tVsGZӑ"s>Z!AMnGR| Kt?]J@t\ z$ @:]>}1o pBk_"'< 4s4~xkYqBp(Eŋ[ /Ν;by"}*k^!-y8 FmsX3zLn?^wﮛ#8#;'|XLm/rgTpi~wKɻv_[-M6իHItGC;?y㔪!Ȥ%216yw*_/Z/DO\,ןd̽0{{<09X#]"k7)o)'Evu,ȢLL2 ;Vִke\~ac9 ")eֲyI9_+Umx{Zj26YHF(k$y"EUs%!BC M1<̳VT t\+>DOdHo1ru 1dk_Ҷm[xBL81UX^zbE176~n C:tuRZ?{nӰaCk[^yHoQqرzcO?l/ lٲE/8cbu3w1Y6U|<\_M7ݤk׮~H7/˖-ӫI9tG\v A&}<T9E vz|rShQkB@ \,7V.LzltTOZ}i{ŕfֲAɧ>d5ku6Z7($ ]/57)rlrl kYOX6k]o/a[ү}~>K;[HF(P0k}lԩcm;kTjZBr^VI?@?o$#.}r_ :=b pދNU\'Iŕ"kҤ/~)Yٻw^E:vh틤sκiL1 F9_|u֙ŋ[ۓTZ5כvYU!]ćǤM6V =Ȏ /Vr@L-/rgTpi~tR^ΝJ*U+"IzWRҵߠࡸzqD&&oS%΍-Z;Z(t=Y0Lso \{{&odc Tr= 7dZ6ȼ+PB־dmoY)?It@^7.YV ӨQų%*T0[ﰖM|akXa5mk= &Z_I'lm?kzk-GHP`lyskL~k>'W,&YC۬qn_?ˌ3j%}~Hy h~mK/w6L2E7O3gZE\"dVԬY۷O/8b ϲ=]V-4pƍCrGSNIʗ/rUkd)216yw*ZFW򊵿 csX'c }<0#]"Uob>EtZ.QB&Ŋ)k a-$(S^nμo7 -/XZ&HhżL"*W)];}<>c9ƬeDw9Te˚[D([\FMk$jeO@ɕ~@@Ity>kw}y&NhD#.ڵ^M͛gDؽ{n h:vh̟?_7m۶EZە\yBڵԨQ/}ы `)\~}?^&LAFvO}†Ev~MU.4UT!y衇t5mɓ#_ZcUuĵkl"c7~#46lЫJ͛7rY++. xs`@O92*R1[aN8oY3`J*e훗"ED 0eKUMfzZ;jtޚ_,;Zc͌ydΧVx}xտmj׮c+ZW"CRSx k?<2 ƞGl7bO@ɕ~@@Ityy0\k5kfRJE uS89쳭DO֭[[ۖ~%CϞ=}RV-g1?~/7gz̑>~_ a~eSõoCR@f=ȤFH >WZ&Y=gnG/[_`-ׄ(Snn2:UP9ͧYmc͝X드xIVx}G̎yyŗ%*(kMbUV~P0W7|j}s+q ,^lk,[L7r K̙cFioF.0~׏3F7( ʕ+[INt@5ڶ$Q_j|zc"_^/^q1>j \,\>./ڷooCҼys4p}Crwfk7tPD '\2[T(hs:56}\TC .4 O>ϋwi "p'4>pLzltT˯3ʕ7nKf.k?f;Z%a*ʔ$ iѲ>I tDe¤7}yqVXuS|y}Zg/ ,[a3`C_kqg틗Wǿf-CHC圦Mȿ붩7>/W|`18fҤIXy/%ۖ)S8pZ%ӫ ĦML˗֯_qg޽k>?HF?-ؾ}tH_%s^1ccYҶm[b5 L.ME۷o7Ŋ"yWt@~~mnz왭mإkٹ|qcLMݧo߾yVZ^5n{=_Ypx2Y0Lso \} nݺ[}ʚxga9k_xIf;r0eMQMԬYZYVDSO5E5={63~?7yg ^k}> JɺYZLD(P09ךŋKt{B .O fwYg}*FO D'XL{ٱc)R5^s9G7ă>hm[SO='eMyWmKׯ" P+WZ/Ǐh"k^VXgE,_hGbzL6olJ.mI"EOgz>~_ 0a~ϲ?%K%K3r)Fȗu[Gm߯V(!ș>}*={"_[nj7`kk.҆Hgg0ͽ0r<0<#]"絾ϻK.LX‹/Y5/LnL&ng?BԮ}{}"E%ˬKFVx}|=HC˰# ?Lεf޷[v;]:K2ADO D'XL{ bl٢]Æ mKxD{`\Y@ڷoomW" ~(<\\9 2enUr~G?ذaCcr]wY"cb5 󋿹Y6~<\_yV_$7pn8vÇM# I&OV(눋L_dbl>WP̙3(Pj#Y˲Zd)\_}(t=Y0Lso l\~3 =ȤFH<@?^/aA9X%V)]M,]_5XS1խ>{f-(<1bŋ~X`;P0`55kֲGҰa#-!szfifVT t\+>DOdHb+ĒW_}U77iwYm5*L2v%~n4@@~vi y ϟomˮ]t@\uUֶuY/y!\=&+VU#./-u)"HR"=) ɽʕ+[HZjRQ0!1216yw*k@ѹsgz?3[D9QdR{<s`@g. ]"U폝zcxye–}{ސer0e%Aucgo:4n|>3ﭾy࣏P} 44[T(P0yךf͛[#)_ՖxOX%[aO~@ɕ~@@Ity/vR`֘I +6%-ZMMjլv-[q_X۔\j|3fի֭8p`XoTo>d3}t3bӽ{wӷoC/b<*lbfΜi{9#=zD~Ifb/u_N˗/7&L0O?ywmk|ʔ)}X}ԬY3_6lK.D7Fe$BWC']mڴ̝;׌;w}O=T… rZ0u=U,zF>>fĉ櫯26l{5k=zt䳵cРAӦM 눋O n۶-25-ўyk?4o<[0k׮5~7n\wygsu?\SI2?x,pyG/<د_ȼ'D>.φs/bd>( <8YB={"I{y`cLzltT䷦Z}RZum2a̭njMX)YM)WIٲe'~D ]T} JqV[/ *a.u)o#GKkA {wyg#׶ }39_Rެ͘3xcf/F[&kٰFR$v⛓#};"g33f2?-Ys s?//̵ڧZt\J@t\ z$ @:༗;kz$JP W_mmS"j0n'a}Ѯk׮iL?ǛkcϲB^jU?|P7܉'hD~"!Er9G+lݻ͐!CGmAGq- ے7CW˗{W^)ܐZ~ ر#RHN}MxJ%!Ngdbl>.(&OlRx0eʔCRheROj2~/9EI9s"E֬YWtaO/Wm x',sT8e^rٳg0޶\?s\^ӳB%K\ z-p<0|q@&=6@E%Z}{aEOA0e%Aus4hhˊU?[S-:\kKr>m>̩dV[/y]^&ϾLYO~-\AEߞ%suif>ymclOCOF`b}ēGy9E5j4ӄ+ Wn-H@6Lvƛ"XauՋܴy>;936%y{tmnFD 3'>/W|`183/o&=INnE JzI"?_oS^ŠGO\n"_>/JaK.$/yhtfzHf@y~ĉOrٰaL_>G\-ɣ>Bi~40qŊB׸sC~{РARJr>_q/}Κ^/%KDz$ٳgE$D<{iYۖ~Gvȗ }$3ΰE "1@vO}:U 3[ s̺uZ۔㏺i`kk>[7͑K͵a~ao[eSŇ;Mda|y]x(͛7s1 -[.;7 o=\iJQBz 6Rf}7E.?t$G}r9T!Dp:5LMݧʩmnZ7L{oj,(ۖǗ]vYGMˉ:u2VқI0~'x̹wEWf?뮻LbŬeb<ǖw3֢ : &}ᣏ2*R![}l-T),,} cQXT@^7W[r˭YS-~E^m>+(i^J;a((炗_ZIͳots_z;yM"sAzc}C^ɼִk/~^kg5֫wGyZ:ޛλނ#QE ]xE^Y Lk֟u;z9I,h^˩uZm4TK">9~}kK.jj}r+q ,^ȯ1rwq1w\k["jp߃ڵB t_Ch~FW,;i$4[o5_15)v_RbrF}O˯\$Y=SU~UYCVmHha"?9qY~UZ߹-_z9{zsIo>s[Dfٲez+#ׯ^O[yytZhQ-ɓO>_/|LL=1>j f[ sL{TХ0-Iϲ¯ KftuInt?ZԩSup~ jھ}iР.Ƚݼ:p@hMҥ7Rzʔ)z1>9O>DpDp:5cLMݧ+(J*e\QȏHz9餓r,vR-ʵM8ׇsOyy}D}>VP I);Z&Iv`֫gm?B#.EcO3T [#be\xB/R@N:~7r-ۯt#syS\"ے}ވʔ)c-eg_X$*SN˛Xs.]o+bmEO D'XL{Y{nKz['"ϯ^xږM6i$^^@Voxm۶y.)\Jǎ#,JF@y:u4nlܸQo:ƍgK^~:Оޮ)4yhWoWK/馾z)y-=Zo6VXWWO;,t=XrYgEPs(r>.[v9%6%rٱcn7zÆ zi߾^syVW"Tx8 ;'|XML~taT*(\ƆT,\?/\X1?ܣ9/B> 8\klsNsgZmbMʕ|} #ry"2^z.^_@3XȗƷl٢wG_}^$"oъ#rmۦ77xwEWƲ瞋Imw;ud- 0@o*P.>ȤFHꇗ뮻j3}/V5a,K*?#^ZlXѬxYr>{F+(ycaL:c,kx&bhߏhРa8vnBO浦FHom<_Ez{̱кum}!]ӡٲlO@yzs}ry+TZ5.E>n/~jJq+q ,^Cfzܼ,X@7ϷSN9ڎDy7^>C<߮"ׇh$_->Yv9餓6g 9ym??'![oMVj~"cUD kݒC=̚_hvEJƕ1ە_/c__H0|q#[5=zЋͬY|^r?luxmy/IVmz(Q\11b'/_~n1>j f[ sLW W>?a~T,\?/ƎkG"L]znmfC"_|y5\gC)>$ZfͬM+Wܛ *QR#7/ٜ|VxE,O._G\F&&o>7D+(u^M7ݤ>}XrwHd{oNP9͛n]*| x{'W,&Yܛ7o5n^/bmCR\-Nygvj{13iҤ5Te5_9cr܋K?!|ˬ2gkA"|qCOryۢE|YJ~UP5S<\UVEr#-j-#E*{=O9v7/JP!/kN<:4r-^Gm9r.3jԨH%=\>ەK>pa[ˤB}V5m}, [QXD@^7g[g71Z6̹NV?$RI "~X -#RM/CkeDeӬysk{9E>7lȴk>Rx^2oLdb7iO\N)^y Ē'~\tž_tk{y-}C^ɺ/wy>kUSXk+Շb~XȬyYG'͋/y=:׼ޟf5X)53͂E5~X`#ȣ#X-ݤIڬ'ѮY#޼I>}͘#I%hBʦMby&cwmvmzkr%K4^$O{S-,ݻw׋W\amӋod|X.xFb53Ӆ9k]ƆEMEWec/Afҥ>Haw #\gCѝ/AS?;QPPZL ((`")` DP@P2( * &@2 ׀J\vٙUu+lٲMSΞ=K`b̙oީB0]TgS2շƷӄ9g`z΅(6нJfw)a<DߙaD 1?VxNj*2 zqOdAB񤎼1c0t"Ǖz`={ĝ$ _|.bc++ͣ7&MX˗/)QA_}rMk_Ɓ! mjPw^RuX|P4(?ۑLʄn^}L`֘wFMGLoيŚ:c_YdN7M4y4 yiֵ^ʢB#uyPa͇ }<iYPaA7g'M1qZc\]Uµ701=чS;E+4oh,Y&S V,'&NZ_zȳ_~kf7ZxqĬ?YpJɎ;y$!0ET39r"?t 0.01u&:A5a?",m#`eHG/2;&Xb/ҥ M<ݰ[&n !]wk`>c [`Bˍy  |P]hu\ӿC08uMa~"؃w!3tWao4+B8B4%$ 3fcUZӱ~57V.&ADCxct"Ǖ=wi6 9tPc\s ?q?`hp3sT۷/MLӲ%\te`Bt{EL2ݻ7;Vؑy4Y`ұR | vЁaGsI6Y qrLeA7 &&ZT$)'O?&8V@X.ׯ{C0oܴiMUrew *d5LtoL2,Xir)R4h kѢEV:upaRM…uZs5r;a?'=RixRt׾Sli[X-QtyaF@)62n_6hdm;w* 7_yH͞=0^x!r#zܺaRuժU4 ;`s,`޽mW]6M]A0oMLHX!}H8Cs p}K0Cx/6D lZhݦ-KD0܌O ˾gmve(u2KcW쳮JO>tj/;oTF Ʃ"-C ? KD#|+ȏf7OбCY)=Ӭ|5X'NaGr2e˲ OA!1A0` Kl@{PzpGLWNCTPnJ0,]t 1f|}MDDܘlɇʁyv|w+WƝXjU7 @Lb)ҝwXaрS3K ;6MɖTf͢W^a- Ui:={pǜ8qB;iHY]yÆ 4m~`hwL0ϷΝ;[cǎ, ǵJ,,ip?Jwp2ty%4D³&O9̄`E면xW,ބta*з-l@QzQ,M:o&\_&3gΤI"ϑ0?Cs%Dq=U*'OL8kR14ioźOVh+L0tXyL={fw\dz\iVj*9͛i0`_ sC$׾mK! mjPwժṲh|TVzLS%?29 @ݤMX5kbz7ibM6:|8+-\%Ҷ;Y T =_c(an74NeR+5zu ?i4`z mVRײc3Y:'[,o+2rߤiO>ST2O&ܗu5x74j;,%771TP-MV]g]kR5V壄{ w[ߋN-[Y|=]VGY|PD% c q y d%B6 =̘1;zv\Nvdg^ ҥ^z,_ȍIV3]&Sy'8آE  4_?&qD惱Jcǎ,O%LR7=ywެ\|9Ӈ]~'|ʀׯCӧi6ٴi+o;TPpM<]̒%K"i(nݚ&IG}Q*QƋ?5mLcn:͙;G,ބtס@Թ|s!aI9iڳn,z{V6Ayv&Uy3? .ʎ^0iSծ=CZ UPǘa7a?MFe/D~>>;v,+»nAz@ʕxXޙhsJ;YﰷlB \%anݺY)B2շN%ϑ?CԠJ"m {T AÆ Y>J5IyX>J_i@ҥKG$$cn {D$qLpoK6.b\ wܾnʡI;rZ[ڗq`m)DmC *+ǎ3Ѵai5Ie.L:S ˩nӞ}Lif/}ʴ4֔tס@h V|MR>}Y'2iX+m;q*t/4J  ֿ}t9KDÆ`+HXJ{dvŢ֭ytջ/?nbv@R͚X UQM3ib͑{2->swC/`e)>4>(1AAH<A AX2!^zT!Nw.ÇgyB;wIn4jәTg< PG&ct*UX&qG}tQnԝ]tEi`v嗳:*r?Fuؑcʕ0s+|*UX^C=DC &+ ”ڵk@IC/_>jӦ Z;x6RB:M~w'u4`q͚5Yn Zޓ *Ttb :4{> itbӧӪe-a?'o&+O)ݾLwڎBN}naF1&MA#G{#^;bSo[u?n־=ƛڂs%KCN?.]xJd9s'톀ŋ84 y-Zu2wK0)EX=  ' !KY drKj_w֍]0>m\RhQfhժ_n2c VR^hq~܉^z$2}tVR69g:\ Qm{O;3姄 1bN驧`ӦM@>( E=@;XOcoӸ}oE^+ꁹExGXyJMq}^xDد}ږB6Ԡ1@EeAӰq)SP~7tqI乘!|Tǟ]Ե,֔ta0K4{YTdo<Mkמ}w4a( !{ wΝ哊`{pvM:w7fu6kbX^J6oa&b~U6Oi('-x~O?xa㏬LyKA!1A0` Kl@{E!H넹s粼{'`AI4/ۻn"a: @/v (W 3A>s,/ =J=nݺ>&T:u'`yAק)#ЫE-`j/ ˗g4{Ћ/Pr{pdZNJj*:sLdA`ac*L8QkիG0M8ϩQFY5rŽnL?U޼yO?&uOdYEɞ={ȑ#4IȤzw(DV`]Pǘ A^s/pl Kp4gXh/_>VTx?ɆN7i4hʅ/}kH؟!3H8Cʩ ؼy_+WIa޽6M,xV@fqգz'S;y+in@qJEzymPp- D]W_eA^'aeLh=IDAT[ QhP lA46hzWov\JK`ɔȔ)rjr#otш eU~?U : )[Ś:LzoD,\%KcW [?VH-V5kbۻ +1VͷlߑDwXPX?J̪]o4 .SE)i3f=Иŧ+r ɮŢ,>(1AAH<A AX2!^`rmG%'>3,h֬Y46| O髯I6,^{:F70c3f 7hvM-[|wN=O>.JN 믈Y Y& &(4<)1'Np|w,Ohȑ9vO7Ltݻۺuk3fB^{m vqJ-SLL2)ӰaCkݺu4-oM71X t?bŊcEnŽ+_D,L]@wȤzgvr"?t 0.O(cLE 62HA#G6 ŋrmFÍ2~xV+u:>3nl8D}C?p1I˖-i/I~A[9g z(6нJ,O%St 4VqƱ*UDC~Ow8пжж~ 1@]v ڵoώKiY|2%2e:vouIV_%^3&96qo|SQ>}Y h)C7 !ITԵCGX;2a;,OZ,+҈7bv% F|WbusCKş 1ś Ri=o/b7/_&X>Jte^{.J?y*T0 4N{~>i={9sKA!1A0` Kl@{i;* >'%NȘ̄][_|1ԩ O h>ڵkict 1f|pc`7]Z>T\XAtM4eUV&ގwu 0fLηr &s *Y*seyQ(P b;BIӦMvOaGWm(0-OB ޽{i3I=!,dqm*U˵(:]gϦIRrm?(US۰EY~J| wL~$AQȉ-(<1h~n_}Y[6\`A:S/_oɖN74݇z뭷XAyOܷs$9\ Qm{O Xtid@rN%8K.sp…x ,L0{2ɉqOcopަH4.2=%Q=mF]c?Zq}ZtwP[ڗq`pm)DmC *ęY|TbzLJ0Pɤ3eF@G֬Zik4!,; A05%u 4㹑>-CsXd2a8j;,?]{x/?Z/Lٲ,֮М><:|8 ݗ]|ő7-i] \GO3w;u~46zn`TGcu]k۴s[XPXZ|+W髯eA=0@),% Bb/`:A ـ_fm ʕ:u ׂiȑ#ihʼ,_M6p-a< M Dt._ zbCN=,VZ4S`XG91ݻ7h W\ACml2 FIYz5 zih\ [lIA-С MJ8m۶V\I:z!>JCFw%c'qzOR#k`+Szv+09g}&1O?V{ixR*Uɔp?K0|ҭ`U AQȉ-(<1j~l_;-K6/t0s9}4 7K/t~@}ᮓ-ǝo~*TUT%QoH6_rbcMTF 륪u T^}җKfJg> VZz-豄zLa9.AA#O|AC AVbmr]hh=zTM~h&wn"a: @/&zo9Y@֬Y3Լys)M6eu~w 4kK~Ʃҭ[7>8:t}@={I S0dy&_/_dӠ%KdLO>$R*PsNU~W s$IԩG Re=v3nE/NV&PW_ P"pΓ'+{3˲u}CȤAhѢ+f*AA~A[9-9\ Qm{O ̩ҤI"c *T1 Xq_} l=SCfۗ__$+ ? x8'=2{B7.L+cclf*=qڴqPuX GVڴy 豄zLa9.AA#O|AC A nw"ɓ'NnIi4bŊ4TKѢEYzL?hhZ&cL*n. ӝzJǎY 'c@ة8`oZ/[p[уaH`2ͧZj4,fbi *իWg/^ &&ک: *di/VVXKC'hbٳg>vT|`?vMDL/fA4|p t;,IBQȉ-(&tcLnk6Fq/+c wp|(a|_+Zp! 7ʜ9sX ?RO8ce{tܺ!{aB>, - z:ϑlxAϕETn]+"8!7l@sD agAY&$+  x8~{{B7.b\g:@4ae|h[ QhP W|{N:5aJoGLL˄nΎ^,]"}2S'Y C K>Gi{XL@=޲Z~9X`zϚ]{ELiJ7r'1-ۯZ{VT}nkA֭7)'x|>bTvY 7 /V,{K.:p0KcWG`KMhlKA!1A0` Kl@{Ѷm[֞_1Jƌ_v֍:fذa,;:t3ȟ^6&7U\@C=3IY 'ʕc@={?Z/ŋp[`qi7ѣih~w.c5kp-G;qs410`BZ](P r^l:@ .SƟU\u֬JCZ6o+8|0Mbi=` U42TL:zNЏY~'$mdRvr"?t 0<Sg◶y: [ݻwgAEΝ;GÍJ%J0.Zf7nLtܺᥗ^JC]L2\o߾4Է/}>G"=WBigSi?e$S׮]i6='煜d'G񤎟 6n\Ÿ2]=L`k_Ɓံ 5`@BA{,>(vz< /dʖ-Kõ`MTvmn3~ X3{l_ɓzZzNz+a1 dRvr"?t 0<S!?/V_V0Xb<͍wpeEr2=ێ;}CA˅ƏOC}K&/7a}d3DHz(6нrs5s̱/RNLt'H?!x8'54A)2.T=`k_Ɓံ 5V]ǎAY4>(0}v4|H:4z̻,ގНgMXJw5,&\jCv4u hb,?/|Qa^ :An .`C*TdvUfw]vc3B$ r\  $F ,d }=`@hܩS'LꪫX: sPFժUY: wncj"|SŭdGP¤L2w\V'ȉIv}0` ͛oRP^\9_vh{_[.KDg;pʶ+L|ݻwF:D@[ gϞN5Yn"0E me;J| D u:xN0O<,-{YT ڎBN}naF1cƢ{M g鶑wۗ 2a=2н߂nJÍ~6rl;L }'ioTQ[9-!}H8Cm@ i9:mLQ"s=hɄE3B VR"k'8,!x8>~{_EG x80<жж~̭t'eg7Vdǣ4sG,ގL2 @Kkaǣï74t3v,ބtס)@hŪ5ڹC4T& 4l ^Y;ϚG[Kf+i,:~4+ϯ3ߎ]bJ#n*QdĨI~&:704^jڌN[}7d+\'-[/637͚:@0A=0@),% Bb/`:A ـfm ]wu44Vbi Xn7ܹs[ S/_< uS M*n. }\2e Sub]}4sΝ;?9r$5mܸ3~%DwyDž 4=7pɓ'#hݻP#~6}tI&_aG7p_Ӭ&'d|rVR˖-i' >EBMJ^/jP.JvxNx NO>45tdR3h; 9bDNps#g鶑wۗ 2a=2sU\g}pBw&&M%}0>G"=WBigS0 _ZhAC ay<֬Y+VX|Cs>̟??2/(P+'&Ya yÑw<;6_EG x80<жжALfq(M|w9X ص#ӦLdMpcǥ6MLhz=X CPYJsIjhP>+3DYn}7x;@5K}/C7lҸ%} 1=ώ+.ʝze+I/|?)10ҵUf-+,XjޛRߛi_],>z|ժUŚ ilD' c q y d%B6 =w934O?D裏hhڬ]4gH^zPW05Tŋ|aÆPOAN%K|P.Ǵ^Jv'~_>֭ e 4;cǎP-/"jݺ5 e`jڴi 59͛7\NGYK*T'_nٳ&O}6m CZ n1bC;0l_9sF) 2cu쒍ǝɾaRXA!}=G"=WBigS0?v6TŴ`޽YTŊh8q&wĮ]/ub~w5!uhQb*/_!bE)0,^ݧ/Z?o4etoGbZFR1MEX D˂2I0ADUHQ^}{N㦟b@N -`Jc΀M@Kop ţO4Tdwfd&UpaVCN,6H1AAH<A AX2!`bnh?TP#L3AѢEYyP?_M7UTuڕzJ.]X 'wi>?4s֬YꥄÌ3X QbEVZ4,!i_4<wKw70?>jѢv":LnjC X0TNv|:Kӽ^z%V҆ h'u; 7BX)`"L4qD:vN]>j*c XСCYl<|ZBQȉ-(&tcLEW62tA'#]O:EÍ{E\]vC=DlPVhЂ h?:ÄnO>44~-R=ONfǕLz٤œO>P:p Z@ 4qѥ),Y꣄B 9Y(䎠K.r"?t0<SKO(ֽcq?_F/v>i|PiӦ4(OnBw&@mIѣ4IdqgoCZ.Լys[2~ٷs$!;s Dq=I@l,vM72M0iXjU6;tMndObt}9yc~ea0 ӵ/AHBۆTAGԾ}/A &=4ޮ2er[ w,;qJkuӷKcdtסИwƲre}22aY~P&MXך1CV/;wx;@59&h^>r Gi9#4gAwɎ' mҸ%7 7=c,KAm{r5jx~ܥ+5ײ:(zLa9.AA#O|AC A>.kWl- {Y,[oPHehxd7TiӆM7mۖիWzJݺuY ';wf@N&Z i#`H󆰋#GxLp(,/(ouƌ,_h/BI&Ev~u&4y XbvO"q NiԨ+C鯿ខtwи~;v >Gi4'}Hj1b"O'9T 7l@CG!~??/i_6 |dbq7ǀ>snlAh҄ w}zܙ>\{%}0=G"=BigS4+WK)Ν;ik2ԙTAK~7Ut;1 C;;a쭻@A)2.T=`k_Ɓჶ 5zXڶk+V+ȱ,]yiLB J,ŎJńn0`nKwzeLn:|8KcCN,?*UX[^GX@3ϚM`e]XtԱSwsUA:>>Ҹ)]N |iTvj֬m@wy֦[X<վܹ~wS{ X|D' c q y d%B6 =5m M0kw'߳g u T yLK.4ݻ7,X@C]DBS t禛nr-:AN A ,$:^T).W_}u9Pi|:uh-vwv޼yGx0ΎjӧO[իWguСCi2sID;c$~cXCN:"L+2twи J,IC=>J .#LD&Շ >BN}naF1c*t~- 5ƈ#XI@?F/v>e|ѣGbq4ST\_> v:)9yKdpȐ!\hѢ4ԷdbqoHX,q ͚5FL1<6nܘ_@ZCDꫯFboϟ_Yf$WgtЁd6ũ7hZFbذa>J?3 a:'2>\r"?t 0<S3uOh1^z%V>dOg᷶w8ˆl_]Ȣk׮4(6mbuP^k-K믿N>fK2~ٷs$!s Dq=&fo4/Jв!'&Yadycyǣc~ea0 ӵ/AHBۆTE'NQz,ߴj: .`uWZ|K2aL&[뮪U gˤk8;> /$ c tN I2>'}Hj1b"O'9TTVM4iӦ|Ȕ~m# pڗ a;0ꫯX:-[p⋬W\;A}ǀSA&XOU 4lX[9 ]9H8CJ vZҒ%Kh>cV6$+ L"x!xㇱwm 2U0ڗq`m$DmC *W^ΎGga#K'U߽[B,>Ue” 4wն;G}9.WdR>;F(p[<C !Iô}kp-O?Ӛ{E_.aQzCGoWbu`Z=AM|q5ֳo[ƎAiާ X M'/`(1d(Bƿd,faJ+WTPLv ;usΥ1AAH<A AX2!t  ]+WfӧOGv{eCXܤ7.&715T2L<Η/ {h'4hЀEɉ JspOHMhc6n<]4[N—'ND&|/Ƚ$͛8S&-"ON}?9GХ^jر&ڴib016 i'БE v*M4m4 Jbu (@CCI &ܣ߅c&D^O Qh; 9>F1c*`MzICq뭷!c ?_F/v>a|ѢE:0?ӺꪫX=vE\pl=nEv+i/t)[9 ]H8C*~Ä~r) tr&YAd ycyǓ?xo~ea0 ӵ/ARBۆTI?o0RuX&wDZ4*SLʴnO}X,6SV:46Ƽ7ȱ,ML?x*V8#4'ք tg*,ON:PX}VZJ ?k`Hˍջcdz4NTYYLVܹ#^oBGʇ;q*) Z5/){k߁{,^ ܥ+5ePZ|1zLa9.AA#O|AC Age˲09V`MD[.+R?1<?v9tP4#Hh*TqsxꩧX^2}X] sYx nݻwz(aAT6 =tyPHj1b"O'=xfzP#Ol% ~n_xCn ]L/\YѣiQXH/4lؐOiذa4<)z܊L Vit)[s=L?CJ"m {*1LΠAX^zkʇdߓ Oj;=~{2.T=`k?8}h[ QhPub5c,MuJ0>~4K2iʔLB5bNZfJŋ`R5ڰGg_,dnJwf#(M6=gphOx`B.ЭSbJ,6ƽxƛnbe+t㏛YTկҲWxWᆲPɒX)u{V>jxKue{`&t:Ȕ Ԭì<kaxWx\6oaKY] \Ù2uS=  ' !KY dr3+_Hy9KSo;v:w?~ *Ь\DBS ŋY^Jnv`uS3gXyaAM6OX=FEfȐ!(vI|'K|4;uTLN^vb,X^Dʔ)Îz'ih2[9D[;wLER|<3.J[nq9z`{Y(fP"l@Ή{Q&;i[X-QLǘॗ^bA9 ^xV~??/!}Y7.tk|@{IJY= l'ϟꨄwcǭtaWC:u#po-е[}H8CJ {OjXS{2I yǣ/c0ۀ_EG zg];89-(mAEдkϾ9+(Q:|8KIuz^mlƉ2iʔ0ͯVV7I`F֚uzt>d*T0j׾=uS0Ss:((Pe.cr玿>ƍYiM9Ci,> aXGc,oܫg+WG|J*dO[١}75WoŚRbCN ?)KiwYI5j4CL.r +OO~,~,YBXsGժUQ=  ' !KY dr3իY*]zwߡ9sЬ~e߳C{YM75^uU,?XbܽA$C͛`>]i /:qM66lؐA$'ge]a#G"100DŽg7>}uuys|͚5@>, 'OfuV`i&cذa,-IlSbEί *$M4ay(3*&:@ٳ& -rNA&ջ mG!'>CZ n1|S¢_qwUrLYޗu 9az|QjU7;ޙz@  kxAK.Idq⇾\`mذ ?po-w0 ܇+!3t09g[Rh`-J$+wcoJ)a}YT4,_46ƽ| 7^~U&UUQ ʕ+rx?Jw_7g'Nι}ƄPn!V& 2e˲2!go=ƚ֊UkX=`H(z\aSXKA_t%,AH"!z}c~fcJ*z@5wy1YDBSn"d]v,?tw\'@.\姄4ڷoW29VZE kfy(*T(k)#Q{Y B石</n_4FWS 5rNA7QR&;i[X-QLc;vڴiC]e~+esJ,A˺09عs }g4(gu`~_fuUzhl=X78q"+_ xl&XwNݷ;|fJ"m {*1L0?ٳ4uƍVJ$+/w<#xi{_EGX vg mK! mjPD=~2Lg֧}B._s*?29W*{,?J*[ciMɒX֮$_|/Ż%uI@h#e^J{d03wWbiLgZٻ`Iro%\\@f>/%dPAQ@%IQ("$K$$A qY2%kVUg9sg~ULw?}zxl&rSkq.N?YS&gU."oDI&}fu J/v귽mrʬzq1IYr)G}tus9^x!V:>sd\Vn=X,'פ;LGff|O5ev3wye3<&uxGm.>2h8Ya2>MGY_!.7eiq'~p_jX{A6|W769#,]?Ř#SfK}m<uCG|t>U?S lYe.8yWkԴ)DdM^qgioj.v4`>8&6[ϐm۾9/8u=id| 4e$j2Omc:}i)nKY&~,Iaj^/%[`w^]J7fU?Z6ft;VG\re@Mb^.lg?Y\DMmGqAº;^Y)}!.Lj3aF4Ln֯妤?0WtMjf׮ᮻgMrכ~)2slA\Ą/s0/[vJBų>'.([_SO=5N>dZf5h~:*2&{=/n8KWlfvYd}ɖU&=sY&B+LL~E~xM&! w!>P2;QW&d,SGjTutd~66kʵl7y="Ydl)_j#-/#}=no4NO϶Lz=Y"}W[m֗'r)qQ qm<2quCO|t>?g?8yW}[92iL^qg|ioj朗ǶAMi4Oc qlbɜÏQ1׹ ͎9V-#&Ai4`ʰoRܑxMֶλ|)<#ٲ˜x5U~kNy|3DnϦSO?[yo!Lj.92iZkg,\9^h5d-u7Lb˭ƀeѾݛ-۲m)ͦq_@{5" {JJMoy?[wUhT^{egHz_?z姤m_~8˄tFDwyٶy[R<q1җ-L]wgV=2OhI灥^:[qq1I~I//[Ff"_̼&̌b3ԱNUc^r%2ˤRnJ_-m8ٮ{i2t-M^ꪫeuig)Ӌ{:䓳m./N0g>mGt̺k,v!d*sq32~u\[;L\]_+f06O,Rs*\sM+8ʿHdx=Yڻc۠Eڎ&5lgԁǒ=Mǘu]հ%[G~xa5m|Ǟh)x)kbʔ9R}O6os}+LH|c͕WUӅ_MߍT}`wj *jF2^N<wMe_|g4o}[ul i'k:|g22ǟpb6xO|2[fX[y~QFg}n6}UYvl)iٗ\zY6}7^DNI[jH}b:}hՊEY4hsqGmV,Rٿu`jY obh/ɧ_?(8aj׀cd҃mi?FK^z)ޱ坸2}OY:g([?㶖I_/qeҿiΖ52 .`q-&l}y;kds8۸{2G&o.8[ozFf 7 x8233xX u,u֘KUn|A-5-?}y/Md~<6hڵly=&bFVZ)NZ#s5vi8@I]njX{4rm;ٶI98[=/k_|1Βydb:PZ1L}* ΖW&}rq g=[WDd&uOxfPjxl_ѤI>8&6yj3?J1/B񻫯Vx _"[oLju7gw#֔iGa'?ꮟ}lw#7?\r?oqns8( SRS}Uf^xY]w۽kmo{[2zoH>zRRs|3N?S׹执 /l{ʼo,s_6Xh>gy .8o"9+}q۲yrȖQ&y(3y9c0)Gl#JPّ[ˋH+.doh2dNYn-~ש-U&5IMq_@{5" {;3q-Ogl{FK/Aº;^u}fGˤ&|m\ү{Ȗ[>[7&Ĕ)Ş?áSNkC9$ZmP뒣.f׏MԩS[Sf.8۸qٲGOUz?vIMT;l=\_fwg?lY#yGwgzS>>|%[Ȥ?y䑭o"X>21_47I[q[F&}⠃j5cTw-Ȗ=2~mT2~u^[;ts3Tݧplr5l[lŊ{,6n}ku4i]=qglǶAMkL~/%3ı *'?=fny˭n54qމf}=[Ou/fLTyeAUV]uBǓ~yF̑IgO{(w:ʤK:.rʼ.{6x{k0#k-{d=[o#o*[w7|+.k>>qɜM)PX`)aW@ܬ_jU#IJ]ֵ dɣ>ZmG 鍊nWJ^z饭4s?xk|#ٿw`򓟴Um٦0p/'Z]y|\t<g4(W絵H8PZ1L}* N˖92'ݫ5Hsյ",[kiM:36ZpjiǶAMl8?8&6hJ-%}7ho5ƉKbǝXMoʖ?Zj62ؔi,Wa|q]uVe/#>-g,yAj6󖷼۲et~xߜ#e]M?T}}D6XrW/eU|=n5{ڽ{#q#ޛW.*55ej ,E0 zcuzdX`8KmvvL~:V$kUJ]vY71[hYmՊ_jX> x衇ëqޑIs9n?ln6L;V׼5zjwW~lܗ~{AożΛ?Zbփv뭷f22^H_NKM X_õq}%M^Gyd_N}z\r%l;b*}.^Yc{ӗʤx8,=íyF˂ .Xl/٤/FJǮӧ{n.-={w̴J mOJگ@vK*[r!k1cޏ~3&P}wqdfgT}:eWlq+QֱN]c<̓-?&]ץ ~K/ʹE`'V_}ֹ1_&[|S`SФQ_G/F&4ulpqծO8wA6)wv}lFKG:|u]7߄}to}=l}2󶷽xgGlM{2~v:!+V`l:SuJKjY7=c~J\D&9묳Z?S&nb 7m"MtSxˈZpPj46(uQ 7u`űd86AE2Ԡgdob_\toZdxjLRs+8#62.[ql{Am4`0oo#qUI4K_rq_t˭Ńx_]nj7{Z͓5\k ʤFTi4T5K Ҷ'5L9ē팩`Q?٘>O‹ijsn(NYY`ܦneVME{<5K^m/wMVy9ƛlbjT}I]9yD:&ƚ|/d-^Z:fUS5Ů^7|2b;̖3?y̘ܷL糪*GݩbZFzsnj}j\t/_6|l5iz0e?Zҽt~ם<9nKO:9~'c}j~9$X,`81lq kƽT׃u-wYiڴi_$U?灅UW]5?Oeu`rW5#X nFO-vdM]v!0 +]8=  -W݃$} ]qHmHzl9&UItNoHZen}x߯)i?җC%gtwbw._~1=Op衇ֽfe6&YU&uOxFOׂT{WiұmP~mGS&?ԁǒMLjJ{}Os\{|q]fjVg)Ӭe}Ajϱ\oV%5 LMM4?pwM?T}`ʦL#SWsiGNz&Y%~^܎n3=tIM: knbʔ"5{j3|!?<5Z^?eJw.X,2f_]y%ⶌ'ݧ6([hIuZV_zmkͽߡՠv,M7 SR3څZ8[ϬRsYwl^6LPGKjE>eے^ c >5ej ,E0 z#= @}q,!MlPu{{eR0_} Sxjz 7ݭFcW_s]:>˫z;nK'y)2[GRsͬ|/yƛwj\lO>qs/T|o[ɉF=niunuu4LyOoin$S֙}. g02>/Ww[nl[R(zl&$k}j~9$X,`8N[o8iߜtI,q a]C]_ +x㍭y}ku7K,DqǷ~EJbdo< ::U5$x;'Ij5}ѠW6ployVaz]zW-&=/y`1_Dpɥ^gEj/ۆ2Ω!gC{ww3[D}݋Gy$'?k϶Ӥk~:k2kUy3qU7Ѳ">OvQx22Al2,YYc'nw7mͶ-ޖFq;mvs8 Sv6`K.XyfiR=|au"WwmǏVXxG+UA:פzޖmct$<:vX>ǟpbn(6xlݝ&[Ht\e.Vcn|=yO^F[l?^=seԝN;)MI&ԔsI,Y0p|0>:N7=ܨ7we8iG Iǥju?6qZwu/mUV[m .h{%=*Ap%{lguV6g}v7;G?}x?^{{WVSN-^gQ&5͜6۬2eJ$}e 6h??/u@â.ZtA3<O&ժDqdfgq2hIMǪHYa1oVb\wktm{EI͹cm8l1cNSe^7q9)+bVؒ#%5>k~ʠ\U3s9[_J[lE|8?.zJ}ZdZwIk뮻nznnC_y啋[Xs5mݶ#*'tRAqasaХ&np@?<>bM6i]+n溧Yq-8VM? >M-R-MlP1O:_ݷԆj%,[1SC__ӕO|⓭ů/d..9gŔo̦%~}Ew_-­ݩy|jʙ/ǟpbq}d9G?>ӟn}>Ҿ˲.:z8LS65??7n5M\]_5ֱ15Kw:+ٲznξk5\{폷>s|6eʜ ,PJڰ>^wCAcOMK&ԔsI,Y0p W{fl~PTcT [.u{]v|L[Φsk_6|?l;Rַ?p6} >5ej ,E0 zC՞hy"""Ø}2^c6 윖^_˦3GlʜqML&ԔsI,Y0p W{fl~PTcT c^xbUW͎)K}s/#""2I/;6lԻɦ+wO{Haǝ雚M)PX`)a@3S&6L߳d{M/""29SX\3ƺ6,>oOM/=g|K =Pg|}jH86A0_V5ɎӋ Z>jy,]w=[ʜsUp-MN&ԔsI,Y0p W{fl~PTcT {~pٱ2%5-ƛEDD%7Q;/M_GN8l){sU6}ǡ >5ej ,E0 zC՞hy"""+_5;^,?lzA;옝Rkٴuo,L3[jJx1eCX4AܧО3>@Mb@o3>>O5cZZؠBDDTcfp/g󈈈3=T1\sefkln'5]h%q, SS h&Rdq7_fpA-R-MlP!""?ϽRw.6h㙲;O?|6H?ϮS\sl: /Ι)~ٴÔz4AܧО3>@Mb@o3>>O5cZZؠBDDDDD&Wtַ5O9swG}j~9$X,`8= T36@?Eű *DDDDDdr{+OYtSdK_&ԔsI,Y0p W{fl~PTcTɭQ1u~AM/M|M SS h&Rdq7_fpA-R-MlP!"""""#O?|+d)?1yK}j~9$X,`8= T36@?Eű *DDDDDy'7Ztde;l}q_@{5" {j4<Ռ jjqlb Ovo1ť<2bʮg/fI_&ԔsI,Y0p W{fl~PTcTHvtr[nuqAbǝX׼5u}fe͖-I|} SS h&Rdq7_fpA-R-MlP!"""""==Pۿ[v>/e˖%>M)PX`)a@3S&6g/]7k|/-[5AܧО3>@Mb@o3>>O5cZZؠBDDDDDz_SdzoP5ej ,E0 zC՞hy""""""~ƙ}ov>;ٚ7.O#jOM/=g|K =Pg|}jH86A'/?1W,s1Gv^f.]vbv/t׮ >5ej ,E0 zC՞hy""""""fKW_Psٴ2W}j~9$X,`8= T36@?Eű *DDDDDD;W}j~9$X,`8= T36@?Eű *DDDDDD;W}j~9$X,`8= T36@?Eű *DDDDDD;W}j~9$X,`8= T36@?Eű *DDDDDD;W}j~9$X,`8= T36@?Eű *DDDDDD;W}j~9$X,`8= T36@?Eű *DDDDDD;W}j~9$X,`8= T36@?Eű *DDDDDD;W}j~9$X,`8= T36@?Eű *DDDDDD;W}j~9$X,`8= T36@?Eű *DDDDDD;W}j~9$X,`8= T36@?Eű *DDDDDD;W}j~9$X,`8= T36@?Eű *DDDDDD;W}j~9$X,`8= T36@?Eű *DDDDDD;W}j~9$X,`8= T36@?5IDATEű *DDDDDD;W}j~9$X,`8= T36@?Eű *DDDDDD;W}j~9$X,`8= T36@?Eű *DDDDDD;W}j~9$X,`㞈&,4#FDDDDDa86""""""қ4AܧО3>@MbA<HoH3 qlDDDDDDfc#""""""I}j~9$X,`㞈&,4#,H/Hoq_@{5" '""""""qrqDDDDDDzrqDDDDDD4Eܯ&P&RdP})PX`)(žzSppI,Yb_ )`88$X,J/0j ,EؗBo 5" RK7 g|K@)Л3>@MbRM&RdP})PX`)(žzSppI,Yb_ )`88$X,J/0j ,EؗBo 5" RK7 g|K@)Л3>@MbRM&RdP})PX`)(žzSppI,Yb_ )`88$X,J/0j ,EؗBo 5" RK7 g|K@)Л3>@MbRM&RdP})PX`)(žzSppI,Yb_ )`88$X,J/0j ,EؗBo 5" RK7 g|K@)Л3>@MbRM&RdP})PX`)(žzSppI,Yb_ )`88$X,J/0j ,EؗBo 5" RK7 g|K@)Л3>@MbRM&RdP})PX`)(žzSppI,Yb_ )`88$X,J/0j ,EؗBo 5" RK7 g|K@)Л3>@MbRM&RdP})PX`)(žzSppI,Yb_ )`88$X,J/0j ,EؗBo 5" RK7 g|K@)Л3>@MbRM&RdP})PX`);wh@@gF0r6,˓] p$,O#w)lS .>|||||||||||||||||||$I$I$I$I$I$I$I$IWK$I$I$I$I$I$I$I$I^,K$I$I$I$I$I$I$I$Iz$,I$I$I$I$I$I$I$I$.>|$I$I$I$I$I$I$I$IWP` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` ;w,0z $` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` ڹcAֳU  0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0CMIENDB`cangjie_compiler-1.0.7/figures/Compiler_Architecture_Diagram_zh.png000066400000000000000000013364721510705540100256020ustar00rootroot00000000000000PNG  IHDR @(IDATx^ܡ 4iJdx0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0   `@@0ޝU "(EH0Ġ`HD@ `LA/$h1A\Y .(1-e.0??92}I1ey3y`X@40 ,%/yȔtIDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDt|L%""""""""""""""""""""""""""""""""ݧd,> 8`-@ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,Cn (I Y4^ ) :>@&q2dЈ{)2ː@#ʠd,C{.,X? =q/P 8`.]Z]s5 'P㎫7[>QKa7A$X\tE_ꪫ7޸z;4hݟn7A$XrLLLT}ܹղe%KToylS(\?n (I Ye}l}sjѢE_G~dҥ+[K/4> {ݧd,CV q7y_]wOgzqI'̱SOUlM.;>MtI YV/w;[3gV^zi|͟?q_X;[_W~4 KtI Yݶxj6k}הO>ZjU|ehzۮO`:@ː}w}wN;k_=euE̘1zG(T/}:>@&q2dᩧ=~M={vėY# G3 8`N|dX/Lxwt|LeȢ߹:>3cƌ'?I|P<ϼySFb }O$X,c̙3{r[ٟ/<>edX/Lxwt|Leb2_V;F6d뮋:ulg…)#|ŏ]tI YɼyzǦnZxCwA&x+Gʒ%K믿^n xϿP 8`XzڙgY|CkbŊꡇZlVcɻ㇕K/ʠd,Ct-QŊO$X,@o:@ː0Y-ޗ_> 8`& `ݧd,Cd x_~t|Leb,˯tI Y ҥK01fk=glmo6lrX FH/}:>@&q2d͝;uL65kVjժ͘1Mm٦ F[O}:>@&q2d~:{g}6UgnMwub@tI Y鍊nj}O'|2=cϩN,X0}u2!k뮻+S։ F[O}:>@&q2dq^xYg޷? .OY'N,m>:@ː5yz&a{G;UVR0B}u2!kz_|&ra0}u2!k ޳?guV|:&ޗ_> 8`SN8GuTXOW\qE=suŧ3 `j}u2!\x,'SO=yS֙05:@ːU|g=̞=;><05:@ːU]wݵN;Yf޳ɮzNJ6x{49蠃Zy˯tI YJ]|sN;-~I:@ːU.  ׿V/D,tF@&q2d'm٦>%dʕ Xap"""2ap"""k(""""iap""' &ODDD{>e2!\N=/UXa0~"""2a0~"""k(""""iap""' &ODDD{>e2!\N=/UXa0~"""2a0~"""k(""""iap""' &ODDD{>e2!\N=/UX`\sQ?ozyti:5Hz*F ~ @.3P 8`ʕpŊ՜9sȂ ◟@x0|Ҩ7<@:4q=N p~M~N? c(I YJ]H [J~04tjT:55@:\bg :>@&q2d3ΨN9 /R@'s(+xӨOө!@S 4Sr=F2ːU. 'C>\iƛ^F ~N ҸJy8Q?HK1 A$XrY0< J~04tjT:55@:\bg :>@&q2d@P5W񦗧Q?HSC4ҩ!@ίi{>e2!\ O<0|Ҩ7<@:4q=N p~M~N? c(I Y`x9s͕F`i tjkt)@t|Le*t,\bXebb"~ Sϡ k4M/O~N?Mi\OSC<_ӨO%}ʠd,CVr/@&q2d+Xx>`\sQ?ozyti:5Hz*F ~ @.3P 8`e`;ӱϯcseŲ؋5W񦗧Q?HSC4ҩ!@ίi{>e2!\3 O;58`\sQ?ozyti:5Hz*F ~ @.3P 8`e`;NO,qJ~04tjT:55@:\bg :>@&q2dv,XHs͕F`i tjkt)@t|Le*^Wjڳ[>X8=.9+xӨOө!@S 4Sr=F2ːUVկw2k[~0>|Zy睭ϥ?NO,qJ~04tjT:55@:\bg :>@&q2d+eʕ+?o=~Yh s5W񦗧Q?HSC4ҩ!@ίi{>e2!\pŊա-X >ɭm̙3nXHs͕F`i tjkt)@t|Le*׺,\|yu!:(>ɭmU-kb ]s5W񦗧Q?HSC4ҩ!@ίi{>e2!\.\lYuZjjY89 -qJ~04tjT:55@:\bg :>@&q2dk\iƛ^F ~N ҸJy8Q?HK1 A$XrMeEvۭC9$k`tS<>\iƛ^F ~N ҸJy8Q?HK1 A$Xr[o~[>Y&[f,xqs5W񦗧Q?HSC4ҩ!@ίi{>e2!\/pƌ,gKX89 -qJ~04tjT:5}?|511y[jU?4Sr=F2ːU[b` . Y89 -q'x"3kQ"<0iy[bE},Z(>>~#es֕W^Y}_<\iot=&ǀr7-^xn[lYu]wU_~y~]wݵʐig%dtjT\2{ >Z`A0@Spt=yW59q|W_ cXuVg}v]-Mίio~W{wuGf@K1 A$Xr,x㍫.,j,M8g<|s}rsϭć0}ݷu7y>t[dg@\rI5gΜ5OS|ʋ׿UdԳٳ[ץM^W/Xs~0?+&SC^^[ou-a[tiu5T'pB;vb-zSp/7"QϷ_uoۑCFӭs& wQ-d?Qɽ n֪ϬYۯԧ>Uiz/ַU}#4r9餓#8zM?f5*:O}.[5dYhQ| %}ʠd,CVnֱ0lfr-N@ x3ϟ=frpKbL=609Ӫkn5'Xm&^~!#[b>n|UlVs=VŋWw{X/w]_~⋫yU?x:/zP=P|Qm^c᪗7N}߮;mo{[uGLJW{jں7vm[5z3Yr185d*Xir1ć.4몫-j,)c.qR;O>d|zQ oxCo|#>/.iq:bg}6> F&wWj343<6h/K|QG:#}٧~M?иn5%]wݵ59#çUԟwyqn0,3P 8`ʵ`ֱbo[KMYh sF駟gC7EݶLZ{݀Vܗ#L_Y"nZԿ>,]cJ^R7o~W(`0no`y{ 7撰XX_e]N83/ȚW~_'>Q瞭&)=LΚnS_.\0>-ϣ?_| C`JƓ0s N EDΝ[-[,>XdI曷P1 N 7ߩlCȖ/ْPD40XJ%?˒-[1YL 6{큑\}99}x=f}εq_u.0|p~p7 ?@n\m۶|Ů]uKn;H0jԨQ<%_}Ȟ=W ޽{'@bCUn]oV 8P[3/uSc=>x`aM]&>Y0SB֭[yVq`l`@NsSO=%ݯXʛ7a0L8QQV QFL2;6zwWbȑR}"3pYzjC4jHdɒEΝ;74iW?^8 j ~_;Nʕ+yH%K?>O :ի_~c:`9w!{@d/_.Ņkٲe<H}1tt4i"˨m$IaСR]zy<@ ``~:9sF(P@jm4ω.h;_}Om]=of "w4=Á _: 9~Oh 8=׉y<8vz&S$[y!ZO@ OVZR\*[fb޼yځd"k֧ʾ܏050n3@4@XdE;'1ՠAqq%f~U "0Ÿs/VZe@`9sWV^͓HXd)6$eʔI… k׮dImxzx@ ɠO>ƍO?QȞ=NU-\'JM׿x/ڵbgԀxe6:~ڴi]1UPL ׯ_ϓ%2 2|Foj ~Zn |2O\Ij\,̕ ~:R\}JԩW[zse̘Q( nHл oN-@hB/\צÇnMז-[xR_;Hu6w}?=%fΜ9R\Zr%O"A>(HſV}yCffi~I{vh!-:=o{1^lj<^@ /1N8 /?#OkזE Pm0F@zN2dٳgcW\gΜQ:-l߾]{[vfVC2„ K/$u&ڴiRdI;wn.]:~^NyץUR%$tQQ1O\ J)pO\`DM+uC  w)RD*O܄8xa<@ d\3a_A 'LdEP:u'tPSůʳ1@z[:?1cto:w̓y w"^ 6𤾂2%%^o]ٳgJ͛7Kս{wdN&L  GYф@-^N`l`@NsRӧ /vڂdڮuV1bOcgm׻-^Qt6-\9,^XQ.}f0jc URE[KO?j.բQ_XDJ}  A?D|AVtӫmv4hv4!`se1qD[Zk# 94 |GRۍySd6YafCzwy[&E.^?h/ ٳ7JȜ\iߗ_~ @A%0Q%WF3Tؤb!+^ r-W-$?~/baf@zy0uTnF;< p1P?0TӐ0WTs%b>a1b`KG=zu0'87-x~# bKfI~)0 a A5S; " ;@ESNnԂ xKhA(Z|oWƳ%JHuEljߖ ^u+c4nƏ^AR9R"E~ 2/]+u0^iҥ̟Өo'&a>U(c  A M{ĵau蝋UV-̗ԨQCQIlӤI)?]ҥs *:xRD>N w|l.=[nhhk\ʕ+;bw&Sן,iP/^hgyF'|RL-ZժU+X"xh߾T_E}(mf]hٲѣ=z@w9@_F}3fHà-[F `xK0\=a0W ceve]2&HjԨQ =cRd 9sfZn͓ě˖-xy`B<^@ Ν+Ҩ 6$mhfGMAMj۶-ZSO˕ eˤR)SzŘ1c;#V\)o߮m6eʔbb̓SjƼP+qe\=a0W cfp̼~A^>ʎ… ={,\#/ۨI&$ K.kѿ=O[I-L$U<"&' gԨQ};^agYP7Ο?_QIls!)?5kƓ8ڷG^V,,YR=zg<Fo>yҰֹ݂ ;U2=|  GY+`wvXbh׮Ș1 TT 7m$ݨq$yᇥJ;")S&|]-[Iv! $Eʙ3v9-r&c7xC,_\O6/nJAyx`a A89poFX@)AmMȾ}JeCNjQvă6Kf0 6' &Hu0Nu6 mc^ĤIEq\+! *$vͳp :U S 0+m#<@0cKpQ݂{ϟ_.22l=8/I\ʕ+֭+.\cԈrz^}UG3F3,:w Ι3G|b޽AN~iH 2  h޼#L~]񼌢7vuqtGxem61qDGuNFѷ1Ɖp0c=>x`a6iw}1Y-v%EWÆ oƓ}(eR=5tEjUV<[݁To}5;AѮh5G?عs碍\nR:K/.2kUhQzEeruQn?a%(: xiGҥKKu$`S{b`S y@?Lx5? Ce˦g֬Yc*/tܹswf ?c^N$`VA4w/ʕ+ܙ3gDnɓŋ}E իWy2S`>x,\0W + uCs%P_~R=b|}t,bŊ̠cN*O?Iq5_I Ӓf͚%OyZhO٦jժ7|WzǤz¦voj ~BmmȐ!0ljhСCbњZJĥK%iDݻK"c;zVâr7/X` {%ƨD`j ~s%0WrK+~:йRaH~^n6dp <^x7;0]al a޼y" zbg&Dk6mē:(:T-QSƌ pV{Щk׮cǎ-Z bDȐNݱc/S_.6m*(Z7ML0ٳgEݺuER\rIq5$Cn- &&]EqkMn "BljljO/_2 :0L GR ( v͓'̶m۴w:^FdmfOh< {ϙ3'a;ha  GYsзZӹsg)NNuwcɒ% nF(QB3O 0EKdC 5|puV`S{b`S us_j ~7Wȓ'D袿S^x`VٳaÆRݹ/.Ǔ'Eƌrb6t;vg2hcqaٵMjV/bذabǎO 1j(.F5mڔ'ڵkbҤIFѣ>5?d0W̕Jb>A4:g͚UWm&Óiм ިŋ$@o }dgh^cy}AXMخ0ɏuC_CE^h",蛊̡H0ooY%5?y]eʔ/^I g} f04VZR~^jڴi `3@4@XdAjo& ( *-һr /&|_^?2e$6lZ@da A\N~WxЦt=M*UĄ $ljO vtx *W,R00nH}'՛:t'OѣG[jժ٩Fڷo3M͚5̙35sD_EΜ9"cR c|Q~}P -+Jn s%P1tP4gϞ͛%a5 c&? A j| ŗW;v2[8hEK7nY`ԈRL"ؼy3OXv)Ѩc$)1LSP 5x~# ,9[auJ*%-\zgOMדwI rѣꫯ방=1/w: enk.mUB(Qh׭[7xA3O0fF߾}tFKmǛ%a5 c&? A j| ŗ=b2sl Bjڴ)OThNl%,5?2 ͘1^.m۶Im4^IR mHH"*Vސ(5jԐ$UTI;/xeصkW-Nd  GYsзڧI&R\rbȐ!ў9sF;ɉ'%2~x.]:\dXF`h ݗ_U[5rH$&fAzKlS{֭Yo P 2Q6\`Ȗ-T_.= hCIem1o!(2{ijժO{n8!#|ǎ3`G˗dC-0WbJn s%P1t3 'K^رr =i$L9rHiuQ׮]˓VS0+m#<@}P|yawIEF<}3fQd]thpQ-ZTj+Vсa~iԄ xpF uS^ A?D|A9pȞ=3R̙52ϰs -mn!ܹs+@:O>Rc>;9)8Ц -[?I2uɉ`߾}t͛W={'ŮfƟtJ+Wk6a06~Kx `\0˗EݥzR=\۠@cŒ%KJe[g Ο?/ *$Y 2H>^һ],aK\zURF{5?ns0WrK+~:w}Qs 1tP)QywB AT\6_b>Vk(04nŖ5WFG5kP[F9!Ya@@ Ǡz|> pV5h$nu6+ץK1!^zi^yuURE;--ȸCR9TZ@k45̔)x7%ixg5SXzWCeruQ{'FH- &OCl#Ghxtѩvh֬֨yݼoj ~7s%jx)m6ۢ~qf2=th<1HF]vi^G 2ӧ 駟9sJRҥ޽{yW^lԂ x-'k׎'ˊ+F!ɓ'y2Sgy$ЮEj ~s%`0W8uC  ҷKQdj*4.d,_\9)Ȑ|8z"39rH]WbŤRDy-}AT ]&͛' *$+?vbNA4#oFtE*#r~gmwKi sծ][3zQa֭ZjR^DgΜF9sxr%h?k 0 ͢+HςQ>OK~i*Vhk3ƍFժU'GyD'ٺ뮻x4=x5kIו7o^o>q=Hӕ%Kѻwoi&u9^,dum. ݻK%2?~MS2?vعs)ٲebĉbȐ!ڻx6mDFDEɒ%EO*h7oQӧOIW_k-/ t֐'pϞ=RFPnݺIyz-/O/M0)3@4@XdAߚ:֬Y#e&.ZPqUmQ͛7W6L5ƍ2dO;wN 4L?7zdTy|:u*O;vejBvhР(:c=s̚h6'_nPK.^sIqEbW֮534 hpP7`/& T-N&:wDcB2+ ?T]s CI鍢qh\^=)Q۷ԔoTƍy2` u4 s%J?@}  "87O7J*Is?E֭[KqE%4nM[W .Q M6_b `4 Y+ߟ+P#l#;oڵkǓ?\jQ `hTN4I*(0)3@4@XdAߚZ+[Ior5m#aٲetfؔ6lܸQ/^\jS<ơCl,`x5x`W&6^o]O=OAm&.\iӦRZ5kƳ04aS{Mx5q Ksɓ'߼ޮQ%K ?d@sXȚ(ϗJ*27j̙<̞=[JkT…Ņ xҘE&HӦMz7x2X5?e0W0W|a_A oNR3f+20n8cM]vՌԹsxQ`mVZ@vM~sIqʕ+WBk -P#L[~IʕEվ{YժUs4ƢFI6;a1<|TQGI,`S1gh<05]VdʔI\PbƌDҵfʜ9s(L N>-ڴi#/h-TN1ʋn2`κhƁ ~6i Y\Icǎh lj7_ aS{xj ~aŊQ:tH&@J.- "oߞ&_@sd~|?/K~bZ`A ֭+_~zzo)Sݻz|Ib6ہ6-Y's̓O>)cblx2+qO+I0W/ '@}~ߔ?~_?sC/u?.kTϞ=y0{`rĜOT. c/P1 0T?37? >)MD';gќA9ʽsNUN˖-3)t#3JСCuT9#`߾}R9F+0)3@4@XdA-Z$ҥK'>M4Ҝ%*V(mƋ4=k֬R[c6}w<0xcTƍy_@9M6eY7S׮]y7vnj?q(^tR^m`k-~sYlR\t)MZFfW^yE޽;M~^vm[mFOҘls޼y4o<޴~~q݄ x}ˮ]D2evEFTre*HxUV<+P*4^/RHda5 c&? A  8eg ?j%~V߇i _߼jdN'OƍSm?YztBMԡCd|ȓ'xXظq1A6$?^.2t ?`VS0+m#<@0*UJW`\쐍\r> 6a_^pٲeiZ''9[#eݻ ~$H١7X~ThJ @k 2ytwD`S1gh<05ٳ5#pmdCeaA CŋX͜9' mJxo_:uH12AIuuSsEJ 3,X W. ^}Uޜc@R ݓ JJ"P1 pr9mȑ#Eƍw!'zŖ-[xC 6p ϡ>3gbEx`ҦM)FF}$W_$IaR}T޼yɓ'yi(U.({{nޤHc@R٦ HM6 s%s%g'4i4b8s8v':yd)?R-ġ5 D:v03,SfJ: P^']tf"0)3@4@XdA.iѣb͚8GxJ8 ц;&pT?i5Gڦ .-ɝ;/]=OK12j.N u.]$~mQre)]Xt-#^/>sEuuM\xgcճ?qݺuR]b)SLڢmۊQFiA4!z~Qo&/R5J5@b`wA %KHib6Κ5'OtٓO>)ՓQF<+q /('&vٳTQn@6a5 c&? A ;W<:0Po595]c[/3?j?O3&L oرcÓ$^հaCQ2Z d&gۺu+Rς Ə/IWs0 G >  GYsз&ٳgԩSMڵŋLxLRΜ9 / vk׮nX-\={f&OZ8cdT^x0l0jF(d`y)~-23_ʓ`hڴiHCxb~GǓŸ gZ\M[%Kbf%Ziň[F֬Y#ZhaڿNM/_Y{NٲeJ.c?[>,5k&' *$/_γmHV@4i$(?oH%^ϢSNDflde-KF+W'{< Ә8 '2L>QM65dd7|mMSud1 O땲e˦6o\KhիWynBeT"EDz`T s%s%dӑ2ekСC1g© Ŕ㲫-[̙3Keꢍ<?/CWF`$ralWGx|:w#~f+Q`x .<0(@?@j@X ,gyIu2S۶mM}%*vh 0[kРAr[0:)}  , oM}JJ%K ~׼޽[TTI3رc*ԬYSCQor%Lt#GxV+J*̙3y" &_Zk͚5cWs|:ݺu3fʰ9rhy֮b4j(^A"ѣG1Z$={hƇ tww}g0f J6}<)dP+ϟ_6h@tA 6L̝;W3p2>|f4*Eaz)](\g(2\׫lFv`^W\Ij\=a$gk_~BR FW k׮嗸SK.Iiª3y{Ld0+^`.<@?OF7;/ ծ];06)ojD5~VbاO)?RT:Y 'D6rDk5m4M`h ֭+I0N {x~# ,9[6'fΜYE1xرbΝhɎV&WhW|G"SLi3gNK-9s OP.OgH&6ZmJ:/^%JڧUi/_^GצMߖcGe˖U22AЫʛix| 跸gϞPBRLTD+ .'D2 uh?;~ʳt 223(6r z1cƈ,YH\ٴ-[Zd Ohsٿ51-&SSZ/jmo՞Q^2n8nFɈ<x<_4  GYsз:{R Qt4mۊs'N𢔠ݻt"O1F/$^_]=z rY~R<@`Ff3x`]Fى*EK/]F#FspUL)>vUR%ۿ.x| BEtСCy L@ZDNF*lɨ+ox2jժOf :tHQ~7$sH_oi֬Y<8wPT. J5jҤI<%>zh1m47j=+W}OlР$5h0rH^rcL 1Wb.̕^+I-~uC Ac+tо0Թ;r dT.r}=& /P1?tz7yW cP#Z;mfǎcc;l=RӧF{c o߮J7Le8}t1dW)S&Nhoގh  GYsз:c߾}"C RDG|I1j(rJqIu|tRDf$d7̙-&Vg`('z>$PЂ3JղeKsU~}YtЦH^wbelj<>@ ܝ3gNwN]cz iu1Y]dĉ2VZœ&VjH'-%z'JF{+#o={'M)44_v' 47Jp )_4ݻtuUR_L 1Wb-̕^+I-~uC9xXzuL?~_  CxralW6]x|:1ctz vowdg @/*.gΓ$R]QŊѣGyb˗O*go.}Tnݤ۷/+Ы5*;5ty0jҥ龇p Ǡz|> pVo^#Zj֭[~Sj ;Ƴpk..]:1bo cB^xʕ+Xd 10 t_oѣy@!uԑڤL>̓дy^fMDDX`S{j ~PyG"Ç~?뢓 rTR̿`Ju3*y*8qB݇4x GR+VXo_8r䈩i!ex`a8}s+F)̙# C{2ӦZh5kV~W^bڵj߾}!s&[={k׮rY~ n덢2116 &… tb1S~wD͓N'cQ~} ȜjժRz-T Rfԙ3gxpE1qDS:.Zxݹsgg^{5N\thѢT7hdgϞR5֭TwX:tHL2=/X5h`f&᫙P 1WbO+I0WZ c?'QFR>Az CxralW6]x|:ҥtuv^uxfEA @/*tHO=H-[b… nMƿ̳Xkh>ŲcH̚5K.50T [>  GYsзk)"GhlڴI(QB;= q8y#m׫]v71yFUR' hۣFyf"i O-62[8GyqR9KMW=Dť}}S;mj ~iӤyLHu-JN:lٲ-$dBo[*IիW-wJ i3 IJeĚ5kR[/_/ SݻK26isΉqƉ H3S:uĖ-[xvC&.Fe\2'%w=K%T/hlŊKVZRX5hJZ׳>/W`(+/̕^+I-~uC1럓ehg~jLjS@W)]t$4zh|03#сΝ;yUc  A O]KlH ˻ @/*fs'E{ΪUzzߍЁwvI;vnւ5u0j p Ǡz|> pA@ o޼0$(Ё6?0n@1uEѣRFI h Z ԰aC-2f(oΓl4m6[8G}֭"]tR9dըQ#z7|czzyqn߾}yR'_ޜc@B}BڵX].]?/K {.oSQy'8|(W^.2AD$pΜ9R\ԟ~"wܚcjժAc#F(ov"C _U{?~x…bBΝ;\{'*2nb*v/_ΓJfͤ2ujՊ_n EJ*IZנAʘ s%j\IZa$z{ A Ǭ=8Cu^',JռۄT. c/P1tεk4>d_ﶬ(V-YaZGK,II0ÂP#`2etѷdrUQlY$`ݜL~I3 ia5áh] ϛn:>}z:]dHҷ/T3gE@A?D|AaϞ=)d$_Ό3lcTXQ[`:#բ5k$fҤIR"s'|Rԩw;R,eʔIDx(~x/N5JQ3gd%i)̧s'CM&N<4,o7{jx[ciԩV{`S{1j ~귥E= c?'bHYZi߾}750 qj*vM~:)t0]e •?~dɢ] @/*fhBX~}QcH=#]c}7713 qݼN%cƌ qjذ+ʛ7T3<D /-x~# ,9[Ȼxbm!}(HfCʗ/ؼy30tF 4k PBhѶmx2 j*sωbŊ;w{Νux aS;-~/S?I_egS;cNWP6 x [ܐ QƅKRBL)Zne7,RQ1\d1wB\KA/(L$D(*[_6i33==T=3;sp׮];g r˚*aP{ٜ#O<لcԨQvBPTSB~{ZI#8Ҫѷo_gI◿E:bPa޼y?]/қ[rv[t] w)(6[j FM:hР vme{ Vts,bƌv3?6WŶ13Z|Ȉ& h[_mʔ)'u:]~G]Pa{ 6T5`T\pvՊR'p ?_?jRivHuwJi:h]';=hʟԮߡr9<3/gP 8Y.{+uFdP{l'}_&K,Ү]?_ Yb],1QLbvDqs%~_^/PEñF}1 W]uUUUT/+<}Ck֬>S:gϞzᨶGU"E{`l.+>Ma? 6doݺuvآ 5S@|<+Ig%~ƈ/}Wj̚5F8Tp(ԮEx^E<rz T3qn7jڴil4{H]w?lWCj-jw+mCeIﺣ& Ȋ+P ]g%Ey*xa9B|EW_}USE&f/*W*9/#ɩ&M;=M6:Y&&)TTpҤKzv;Ce¡o*M , }#gxZ5Po߾ζ+UR$3}3s$=Az()JR'2 {챇slA4dQQPE1qݽ zlHmchgh {MѶ&;9*6l3 G4gvء*.‘YPFdM‘cʕvwGv L{ꩧ]v9,"<=jK+ jϼvf*VˠvvyuRZv]4179!wW{{gncǎ]ueQD\|yB;ww_GtIýQFycƌ&LuګIXx_l;HUl#FLG8TP~{gq*o +Al\"Wl*:vXJM\g%<+Y b?cGdUp-u"*[tYׯ]%t_9 Mh?b4YtJ: gл{Ϯ: i6tSg[Ң8gj5‹w>>(E(/U`w}gWY# J]xWjϐ!Cmg:#<қ:u=mBڭ fb{ǜ8=]&QZg%<+Y d?cG>RP{#?kp̛7ϮR8Y/l3A@Tf/*W*9/#uW8O84R=ٳ#'Q}G),.kCWQ۶qsA<"TJ}5iN֭[VѣG[o`"rړ$F'nԣ&yPAVɓ'۴4r/\ϫG6Wg8]%rߧOl v5 D W^uGlF.]xA_@m۶ug-z;s{"0zgt '8<&i@~wv$~E(x;bΜ9v^p=p+~.4,Z'a[|y仳Am~,nVf`@ZlC;@k,nH50]v;=\In jdT AF_&$yjuBEz Ngb:38UA+VWg}m~x*D@7(X(Br/\ϫG6擊)&dd뭷vkfWu]>ݕmFvl$ 鳟iU*yn8k꽃~l F@yB;}E(v [sK:8mzޤQ7 m?rn&g2e?2hLh #,mMf8L6۷r9+ N=3zE(F= S I^TUs#_GI/&cƌd<ƞ >~*Ud}+:~FU׌۪ VrEj5bg6eQ*(\r7`t}mHςZPpT.(7Ib7w2-ZdSH'> ֩e;:BHJ[8eܬY*-6v@7Xdmk.%a~"~qPɓKne]e˖URvB^xzÍ5j1ΧY .(t衇o?win׮];o-6xc_*h)'z~l.T<ټ(Gu+<.*iEucԮá?ЮioQ\!G4/Mgu72*u0aN?˅^zo']+Z.7x몃5\~R7kJT[5{<#~,̜9o8j)El\"W\o*gTYI:DtEy(5`jzwXСs`} IQxNydaYƟ<~6$FR徫á>f_쪑."g;B^~ezwygMAh!=sB{\B{ 'xwq`L39pGl\"WL~U*0E@ 4 $ųtg%4鰟 #jPp}u8!EzIEFK8(XhEr\ϫG6Ͻ|.jhRz ePZ}ζJ zvu]Ta=%OY F=z.W_M&b.J`*L]x]QkfzF4/wqo,k[nvuNT:Y':Vgs@ZlC;@k,nH54HvZz͖.]903M Dž^hW)+Y =o]-Yfy=\[lF9b̙뗍8Lj3SOub=i/3A+-*+COv>8==lӢ`QK,[^T_ŊHZ/vֳךn:M6:.2d7~yK|ήRasx_1zgSUЖE)S߾Gc;|g%EtEύ.jժ6ѨbxI j`pp:{lcK`( +jQ"W)l~9$K84m=G휋}ǿ&v`ߢtI˅&B> j0c*7&ot=L#ZP z,wuE|,-3N6hEaE-*W*9/#Ɂ=a?p+vLHKB*ζ˅ rq饗]"GxZ5ZP|[n䁶-;wٿ_~o&RK/9 B?], VPڷo}'G%QgZ"8M 4>wߵ9=\gpq5)*d`q@rB+a?#f)'pͨ0aLn|_~vHR{I/}n6oɒ%v[;hu&lk6؞>9srcu:Euc.:U;0A횙TG߿sl6:ă>hYtv9A87O?~͎G^ ~~!{|6TRJRP4X8; B5+w ǨQ*UI/|rh\"W{|Mw#-*?om@*u:u]*_|3l := 8Lj;}̘1]-̒4{39G<1Թy}uocĈ׹K.$C#5hsαdMzʮ)i@Qͨ6z-Zn04BU>܄CEkf@Pxj/4C_w>S?]-CY րdC<+YIEύ.8p@g/f]-wGy\۶m*VT VPy\ϫG6ǴiӜÆ-[WZ9{スʮUgЄ?!V_ BmgN]B6#GUD A"+V'[ԩZCW,U}՟n#Z6tSg8j-Zv%mJVZQ,nH5Ə|N6ggymDE׮]^{n*Wájs|Ak.^7Y\d?zС{~(Wo~X¡AVx嗝mcʔ)v7o'p3u.Qѧr4[JÝW]u\;Y6rjWb ֟0a],6爇՟f>ÜP3f˕O?!3G n6m㲡ڵ\QPTsuQ.Tаo7l0o-QŻuK8-PdYP6xf|j*0E!C8 g8h<+YIEύ.gCq]/sN7n=̺S?e={vhߣ`i,HQxNyda>Zqvr>p虂-[Q_~PΝkWO [U\[Ih,!V_w\|zk邏Nhr8'6<}wv(XGQE }Ygzcv[iŅ^YƦ*.L}uMaPzMrg}ߡmUt;۱Vfbsd-٬.YlS.*ߌ->d`q@rs>+۷ &%*V'Q"n+Wvjc:,_{]n*pBo~]vV W WQu0nURĞ={sϭYTq#<28]^Qn:eQ,;]p};SWA9!΋ r^*F[*lSGq7{'74:v4nשwz,A;֮^ ^o-tU*T yh+g̙N>‘>(s f͚e/4{Go1c]-Q(5 <`ó9L_T:Pm@O/p}E(X둢+y|kƀjuQ}^*.jϟm~R_C*4cc2d 69ptꫯj5K89 J NZ ~ދ/3(޵kWgrQMԩn8N;4ZEG} TtQeܸq^n68'xlkp= nvEo,(Ҡ{AD5]>W]u]ji8q ̙c/4{CwBz]-5Q(} /3dzX<+i=3F|0}QsV@_{NȪPr%裏g6J=O`i,H^TUs#_GOzg? ӧOEu]o'M*'XK8T{ww]Ml7iz~￿]~/֊UPl쿇sαΜ&=z׷o_o6s)ihtvU;w_n;6Za?f8b id-jbWŭjW(mJVZQ,nHy_޽̆fPd͈AmLh߾yA7f[!,;t_YvR@7o7~xO{:j E]M74VTuNYguV߉:j.Yl5SQ-Ԏj؜#W]m] (YzTSOu-*jg^AAb@|\zq%駟F^c8c{|x]t\NǑ4vygn[Xt_n?ڵ/^lWevmM#&Wk{@=ϝShR[}W"mJVZQ,nH,Z/"a?7ҋrz~un=ptҥN1QuRpŊαC(%̈q]iygW+KPg,-[̻ݻ; jY*XJ+ jUTG*#`sx_ԁs̘1*٣^Զc K//f jvC8w~˪`СC햋C9baFuȨQ"JENP$,xW; G<䯹-Xvmц-5'zYYY Jc?cG>Q۸β)o:8ábP`EQ4AU),Ÿ)ًxNyda]T'|Ү^7|\NkU/8Q,)Ymh5@F 7Y$G|fΜY@8@\sMSl{6ƍgWZTgƼԬ~1i$J4jܹ}Wr@YQm@9SñfIvphT4; >ЧYG6{^N& jgP;csx_vT|N˅ z4+~l\:_QnPfTp^?n&Y :, ]i&F7owWD^wVRL,gCH`Th%k^*Rdz7ꩊ*0v˗;BB1Y:<+i=3F|0}QsU|X@z<y,xg8c6JBƎk7K`(ًxNydacYN5EfU\~V]U\p~VUrK眃zr(Xbwv6 zj{;0g9p^}4fгM뽅 M'|v6;8ZVZ9^!}KvB @ZlC;@k,nH9iF\tL4^{O8BoǷzk'Q?nj>V[9,N Ժ@ieQNN*fMK= 4Ge]?n"S]xÇ6TiTN:9ˆCi*lDudD݃'e椢*fj?RF ]PO>liYIi<+JZ9L_T\KJLcC99 sOx$){'OK`1G=lrE<"S">r/;vlf_f;Z*t?w2izwAzז4van֮]k b?#Ӫk]/F.]JP9'h۶3k3iҤ&9r_ʯ7ޫnы -5@F 7Y$G|-Z俼]-ڌ*SL8ѮKTኼ±dJUZ/ Lѣ7l0uQG9 3̟?߮u-nr]j>u]mǍm?c=Y=^Q sC_zcƌؼFڻV?OG z>ܠ ^>Qy ]C{V~J:v;ÿNS3*3_`_ȮQvy7p"Q׉i;{5λ袋ϭ\=જCi[}qqūƳJYI뱟1#jk)'XI80`]n^}Ux ,Ÿ)2AqxNydaϐ{xulR߯ vyT=I'T3&Cz/ >4y~6UW^ ~ߞo8x J]PUW^l3ߖޏJlko8:t_k<~,1bYPj(g/]h #,mͿu/5psr>ZB>vWMC7|s±N;$*n!{lAh64@;/EѦMe]ֹ'Y~lhvTĮ8g* b[)*{o]N@Q3v_pS(A jGulKN̸q.SOML5hRkv-ܠ \PMq YnԩSfg@Q!L]}vE ׿՛0a7x`P_,Ǐ֮]kw_Gf B(fri5659wUz*0vSO=G^z],$K<+i=3F|0}Q-"wy;y+#GUsRo{WJ`i,HyUs#_GGϬ=(zwt7,c;I&Y2dH䄧Q~O?Tݻ;V~K4LO毕 ^y#BePxumϋWꪫ&~; Ø={Dj8gݺu=\^H<rg:Q*t _|l͜Pr=ObZ->d`q@r*jA?,7k)YTWrƔ)SjECI&9}U**ױj9u]o}PS1p@g67SN>}P'8 .]l#IyNi GԀrΠvT}~ݨ뚨Pʬf&ӟV&t#x}lKtV${Qz_~jR͜3gXKjC 3F-wyzB3!1ceTwqGx k}ϫ\H2U`HӢg8Q&OlW)g%<+JZ9L_Tb6ln9?O*Ğ'sЄLk޽{;mnZ,Ÿ)ًxNyda},^իrgA|LC49RB}<8)QA}}TD㬳>si xFnc57jV~UWW_mW)9 .6 Upܹ>\\~ve߉km ]x~ᴩѳgO?wo]$Sq)8\*4YY4=cUjYo{Ŷ13Z|Ȉ& h[K`|Ik?xo=]Tho97-{bitڵpU`˖-9pPF\?d[o߉DiLfKs=gW^ugplя~l'NhO?_b7~~fWcP;asx_s]kKħ%Num9|8BUܮ )>cJMOl;WxV³,UjwK*7jڤʵB-rht@]cnֈ-H^TUs#_Gk.]8.v֬Yv3 q(Əo7Q5kѤjx87~"A*Jc'[nO.ӪTP}Hs GmW3Ml'i&bR13fx~=\PzUͽd*MeCj?*htV<(JꡇrKvEJ멧rG8M7TqpLgMW*zY/»[b5vqBT7|;cm s<2/wӪTP󵼣γСsлFK@,+(>h^s~~xWMr;ی =o=zf{JAu-t҆/ Hmchgh {MѶF@uFS2]{'S&Lw|Kl ZJWQwk۶zO~磌;MT N?tZN8g64`SOƍ~$xgbyy q{ BǥN u޽56Q~I7y.=zp\SkLҳE}>StLկ}wWZF&Mu9G*,(ljqCqU)^kC&YP|[B^zUYTEԍ{1EyWS@ZlC;@k,nH5 S??Gm2H=oUP%Ӣ8>%T0V@lW*j+)]wu53QM\qvU:tptϷ:QZn+Q:N5zP{Ϟ= jO0{6/}O *]6 89ZB|w.|B9:bvx hg}V`*ׯpvL[׈vsαU<?oovsn\qvCݏ ;j)8(2k6üyZMTP.KzffTIӧbhmm&Јg%<+!xVs@|09MdVha|u]N5~uMeu;bCE%j0B{9Z,,-P t}~jJgASL˅<>}s<O^>8ۉlI^zokYK8=P"Cr6LjI MB ;fJC4AP9 W߈%jb}]Dξ* 빤&?3=Ë*h[T;V<@ZlC;@k,nHZ::#4 F$N}' EN;vFZ3~gY+u oKLi3c/53%-[r|UTnOc?|Z+._?& vZՌ:N5rP,D^aP{t0{6/w 7Ul U9s.sa֬Y~gj{IB39X)~)]m ݻzoAڵ[-]?:XyXA ; -*X/*pꩧ:`CEΝkWT YH,X`WGGYu) 7e{ufIlk ѳ7s6fhbӊ_v-'Nㄾ5$/c㏷SĮ_YIcteGu?tC齔ecNy۷ǤgTFJm5K`w#>rM \l6&j<g}9p\~v>hgZBG? 7xc↞{"S[57ߴ)+ ; +_j7 sH{ Նk4&HB?qsU/~Qm۶u7* /MHmchgh {MѶVW^Nuc=Y}qo͚5Z :"Θ1_cҤI4PO>{}V]tV^m7[kצ^tQ39 Bn)q^gߣbȑvaP{tafؼ V|J;][^s5޲e.px(~-(&8躆KÀ'']dN*Pk?Q&uV~nL:̧I~ӊ%< j_:w俚~nTGnfjk-Rij<+i 9 >rɓ';wWOz_~\$)nX.tލc?]Kz]NG=S#>rޢEfr'z|-E#mUZOϷ>;#EE6mĐ+imZ1.W 9se MڨE-zC}}-†&YdDn;b{ܥBל}Ds -5@F 7Y$GZ?ONҌE3]wwVb7[n/Yw,i:tg}vӽ /~䷚SL^.b8ꪫe8UA^xQQ* j1›;wd+i&~MF`QK@`o}EzXknݺ5/;ǒ|g7|c9u7pGcǎeo>`gIK.j65ԩSGs5x:Y==,Cף$OxVQa:VXYC3+z9ZC 7 u]:[xqbQsO7ި{K8]P17k;pg9L%NNKŰaün"{mIB8Ӝs MԡuxQ7tSտAyl^OoNZ-?\LU(/r]~ꡖQMuQK/d7Swz91?vS i2ZejDŸ4q sG=?ܮ{<Hmchgh {MѶVG:v+Nl~? 93n{嗽_vw?tRmqFjB39^[.{\*ڵk=CvLzoڴiO3.Z !5`3g3z޷dz.j}q#˗JųYIcEGsϣ&kz~_M.čm'˖-9[T@ o.c G }{Q\wjc9LW+wwr {ӧWgUoޟIBz.X*n;o͚5v-!z7wrQP?>7E,Gydϊ&\P;7|Ӯpz~ggaWo(M"8zgoUrM)>l\;wnkRh #,m\>Os?񏽽;zYeǏ=#ެYq?QFF9f3xcǎg3ի׭[7Cc=޽{o45 ^r=T@4zvp~l!} :ٍ9.A&L;+(WDhUL2RMq'!߷K&bw 6 I o;l~Paĸ4[AB9#*aTp魷޲k zANŋ; 첋sU8C5A@[k,}Մ~`Zi ĉSN9/Bн~M*Fw<+JGwL6o/U믿?nj]tRMhiVB(Ec9L ؼ9Ӕ6K+Vf4CR?f3XuG߅sSyv{NN* ͛gP,j-:&еGM4[j]-w>Co]I(=<$]z~ ,R3A=̙ckw 6v@7Xdmk2zչ|)]t*2bg=L dM;TCN<ٮ[թS'd`q@r+@s-Oir # _!$G{ Ȋmchgh {MѶdkd͍<@rɑCdJ@6~M) +5@F 7Y$G =!47dў&Gz*9r5@r6v@7Xdm+@Jh˓!$G{9H!ddўbZ->d`q@r+@s-Oir kIDAT# _!$G{ Ȋmchgh {MѶdkd͍<@rɑCdJ@6~M) +5@F 7Y$G =!47dў&Gz*9r5@r6v@7Xdm+@Jh˓!$G{9H!ddўbZ->d`q@r+@s-Oir # _!$G{ Ȋmchgh {MѶdkd͍<@rɑCdJ@6~M) +5@F 7Y$G =!47dў&Gz*9r5@r6v@7Xdm+@Jh˓!$G{9H!ddўbZ->d`q@r+@s-Oir # _!$G{ Ȋmchgh {MѶdkd͍<@rɑCdJ@6~M) +5@F 7Y$G =!47dў&Gz*9r5@r6v@7Xdm+@Jh˓!$G{9H!ddўbZ->d`q@r+@s-Oir # _!$G{ Ȋmchgh {MѶdk0SQX =Xx?mn=t;SZƸ3pPK=?׎x?sO!=o`=%o;7%Y,s[sx?mn=t;SZƸ3pPK=?׎x?sO!=o`=%o;7%Y,s[sx?mn=t;SZƸ3pPK=?׎x?sO!=o`=%o;7%Y,s[sx?mn=t;SZƸ3pPK=?׎x?sO!=o`=%o;7%Y,s[sx?mn=t;SZƸ3pPK=?׎x?sO!=o`=%o;7%Y,s[sx?mn=t;SZƸ3pPK=?׎x?sO!=o`=%o;7%Y,*""""Mv&ODDD|Cم ED}_ .>@I,% 򶊈H?|'"""}.PDDDDv|CoDDDdWƸ3pPK"""" EDDd7].PDD:|?1 dR`/otw"""|Cه EDDDd77}_ .>@I,% )$ ] pPK`.m (ɂd0r6dRKanpJ`)YܥM7%Y,FRئ\|,XJ#w)lS .>@I,% )$ ] pPK`.m (ɂd0r6dRKanpJ`)YܥM7%Y,FRئ\|,XJ#w)lS .>@I,% )$ ] pPK`.m (ɂd0r6dRKanpJ`)YܥM7%Y,FRئ\|,XJ#w)lS .>@I,% )$ ] pPK`.m (ɂd0r6dRKanpJ`)YܥM7%Y,FRئ\|,XJ#w)lS .>@I,% )$ ] pPK`.m (ɂd0r6dRKanpJ`)YܥM7%Y,FRئ\|,XJ#w)lS .>@I,% )$ ] pPK`.m (ɂd0r6dRKanpJ`)YܥM7%Y,FRئ\|,XJ#w)lS .>@I,% )$ ] pPK`.m (ɂd0r6dRKanpJ`)YܥM7%Y,FRئ\|,XJ#w)lS .>@I,% )$ ] pPK`.m (ɂd0r6dRKanpJ`)YܥM7%Y,FRئ\|,XJ#w)lS .>@I,% )$ ] pPK`.m (ɂd0r6dRKanpJ`)YܥM7%Y,FRئ\|,XJ#w)lS .>@I,% )$ ] pPK`.m (ɂd0r6dRKanpJ`)YܥM7%Y,FRئ\|,XJ#w)lS .>@I,% )$ ] pPK`.m (ɂd0r6dRKanpJ`)YܥM7%Y,FRئ\|,XJ#w)lS .>@I,% )$ ] pPK`.m (ɂd0r6dRKanpJ`)YܥM7%Y,FRئ\|,XJ#w)lS .>@I,% )$ ] pPK`.m (ɂd0r6dRKanpJ`)YܥM7%Y,FRئ\|,XJ#w)lS .>@I,% )$ ] pPK`.m (ɂd0r6dRKanpJ`)YܥM7%Y,FRئ\|,XJ#w)lS .>@I,% )$ ] pPK`.m (ɂd0r6dRKanpJ`)YܥM7%Y,FRئ\|,XJ#w)lS .>@I,% )$ ] pPK`.m (ɂd0r6dRKanpJ`)YܥM7%Y,FRئ\|,XJ#w)lS .>@I,% )$ ] pPK`.m (ɂ%""""""""""""""""""""""""""""""""?$ \|,X""""""""""""""""""""""""""""""""" sJ`-(ɂ%""""""""""""""""""""""""""""""""?0 @x <`0 @x <`0 @x <`0 @x <`0 @x <`0 a΃%+ fv}U]YDdWD]U@D}1Q!!.* 1jb_%U&UII%UI%{νst~?=/} X0 `,`@ X0 `,`@ X0 `,`@ X0 `,`@ X0 `,`@ X0 `,`@ X ?#I$I$I$I$I$I$I$I$i!9$I$I$I$I$I$I$I$I  $I$I$I$I$I$I$I$I4]shHN$I$I$I$I$I$I$I$IƟ3>@Cr%I$I$I$I$I$I$I$I$M0,,r] kSdphHNLu)M!92+ץ6Lg|$ \0,,r] kSdphHNLu)M!92+ץ6Lg|$ \0,,r] kSdphHNLu)M!92+ץ6Lg|$ \0,,r] kSdphHNLu)M!92+ץ6Lg|$ \0,,r] kSdphHNLu)M!92+ץ6Lg|$ \0,,r] kSdphHNLu)M!92+ץ6Lg|$ \0,,r] kSdphHNLu)M!92+ץ6Lg|$ \0,,r] kSdphHNLu)M!92+ץ6Lg|$ \0,,r] kSdphHNLu)M!92+ץ6Lg|$ \0,,r] kSdphHNLu)M!92+ץ6Lg|$ \0,,r] kSdphHNLu)M!92+ץ6Lg|$ \0,,r] kSdphHNLu)M!92+ץ6Lg|$ \0,,r] kSdphHNLu)M!92+ץ6Lg|$ \0,,r] kSdphHNLu)M!92+ץ6Lg|$ +˒$I3 hϒ$I3 hϒ$IN9%If^9%If+ !92s$Il'DZ$Il)Dz$I$SeIOcIxXdphHNL`x,I4ۀq,I4q,I4icY$i6X$i18;0,,<'K$6}rK$&}rK$MZ@;X$IM@8$Im<Ύ5Lg|$ '$I~'qsIh9%Iƭ)ruIh9%I~`[g쾧yi.k[:̟V^?8ǹf3 ט9v'qsIfS/34N9s]fz!Mץx::T>9sKj.s`2o'qsI龧]}VWwȟQ{33U>vʱc]_}{ W_{jW/~!ˏ;smqǟ.pmp_ZgMmw}iW]ϏkozǟT \vՕߛ~5p/XP{;6m 鼻>Z>~Ղ+Z|xwF<@8q.ʹ;n5UZ{u g,Gw~*\iQ3nvubWFk㭷u뫓#O~4Oyk I OMpͭo ΃OjӯW;_cKyw}7jWćM͟>9sknx}ۋf2\2(is͕}r8g1^IWݶumeuZڕyFM3ho)ruvg86oSn:>W+Bq-bqm[Յ"g svy۴W[}kU/yjO.盬>{;z $h9ΥV/蜯?^l;w=9fr.r38;0,,<'ZN·Tvn?uOVkagoխ_I3?fVM~a◽z'S\{Yկr]k{v9jU\Ylh9ε|wnx9fП`n'qsnU{#ϾZsq/;ss[=d\]/r,Xh10;uo*̫$>?zI3w>P&'~V-XiQ|.Յ5`}-pʭ}toϫzܦ?1vhtOLt޶%6Zvmm/{{m*dW6o-QvcٱА`d99uU-nlUgyͽr,dڏ~Q3rw=K{|VQV^us6mnwWq mp̡ܟ@8qWxpל\ocuӣ-~~շ?,&0.s6wVO}r8hf1:h3n8S5)j :d\]/r,Xv{>V 1yn9Zz~W~:;viƷΤ?OیcVܦ_suϑ'۴7ySldw_fn-!>9sK3*^Kٷ>Xl;[=ǻ(dqVϣrz1+BkISuSg&3>@CreÓ|\2X{ʹ\f۫9.gUXݺE:ҿDz_毸bot9yukm8|)O|A99͛_9skv\mpan7%|sw`ܗ@8qbAQ'oF-}_mꪇT<'MNz`b,xvUG}E(de?f]Зm•w}vmM_ʻ?|ɴj;Zy{._#Oeh95E9nBm'z;3m41'qsiY]xNJQv﮻s;M_tߓ7n/\rغ՝_E~ߩU6e._<Ύ5Lg|$ '&fExkuic:|ۙ. .ٺl^Ҫ?<|af;}_fSؼ^u6mmWɵjAړw;jTk_KSG}r8̫g}p0{-חV,\9@3(3Xr?8ǹF+oyG̅|.=O>[lD=iǵk۴%fQf1q)ru^{rtqܲ L϶zX]Vo }6Uu}ԏ9㘮sx sq+o-{ob~T>yW_ogb6ƛϭ&/xaMvę7m41'qsiY-{}jž^O}qv.,Wo+oMU}JXPm._<Ύ5Lg|$ '&fߍuwع(ngiYpɮW6ݢͥU@y;K {koqO/5涳ͷ7.<ėu>| gV/}몝?n}:c_ydW\ẹϻE|ZvOq\ꇾX^sv3nzoսO}?39tB==bA-X,6Sg&3>@Crelg//}{_uEUw|\W(V ßy;Se۾7Վ{XZyUs #,|0|׃(m/קuvml_ʫy= :^:-fW_}r8/}EoV}1/Yc3sg`Яr8ǹF37y0: 0Ur~W/’!Wlӆ3yyF[S{E՟nQN|R>>aT/X|_(Em ; j6Vhym^RlӏN⾆Y]>~u*׼b.Zx ݶZ[F<@8q.M׎{Tz[js;-eF/Z^g]}3`dSg&3>@Crewzᇫ:ꨣ޺7o^kWo /~j>W_~y0>j_jkX~yN75^:9sk7GyiK1*ng,Qf0^49@x|'qsnkAWkvkyd >~{;mF=f2(F<@;XαտjSy4n_16N޹w|xV ~߲Þیc_|S{? ̵[ak5̚\KPMS-k1#|YF<@8q.-,}gi>b?kWF; ._<Ύ5Lg|$G?V\qE] qWّg]^lqY۵qRl\WV,W{}r8:率:1:T>tq1][m})Co VZT|Ssy2@*=>9sk3ϘYg [jݍ7+[&W}oF[n[go_یzdh95ھ_]5\b]O8`w}=c-8֋6Vw/{ WjdN~5\p#___o{wvՇ, 88ǹ4Uw?jM,^?Y}N/^~G9d 1V'\_nCZzyLq\se\gmί,8ٱА`d4UZ_h٭ wڮZe5+|qKvZ{[% }vg:_jܶ-Ky26nq&Ͻ͏Eq|yq\E{Z_Mw}Vi,iKԯX8ǹړyv)]V_׋sw}|ձ6yFg-kh95mv٫8>m>~+O9~U/&/xa8EzmұQ4[|θG4uq\FyAT/~jDw~,h@ |=08;0,,s7[Vnֈ'xoFw]z6m3lKo.3VOEVm 383@&ۡ_PG6y;2{1>m[mu)9BxîOnϲsܦvG9 μw@8q.e9xLRM,~r,>Kz[y5mmv(M>Z~Vϻ6VZyg{mfLޛPq0qV?_c\Oyk Ijk+7{lͪMGo~.zW>&s2 ~/nD9Mp5S SYpbD/r}NUȕ~&s_v短Ɲ/WnMq-XR_mQR {F++>9s<|ѧ}\wmB|gw?beО@*+>9s}g ڱ^oEV}ߑAQ@vEّ]vdQ₊hT*b4ƛ&1Qc$M֭,ޡ{ޙ9=S{Nw~~gyzmj\'#fݩbeGR˨1fm;[  Ӹ:n?:tIX5Ηir zEQ 'lL1g@yjz 8ȘkU0e։fT\{1S:I:?W-vN*TjxCm_Av>hHL*y\SEQT~EQ ,YT2}VR}ٳ櫎=DNK;sF1~Ll2STSN?׉ѰkGQy<<'7mըwg򍑷ߥMKW4m16 mZtbtxId¬8'bȵȵi`~MAW6>VdLF̎*lvQm~xlHƫؠhu)+EQ ,t2֜Tۓq<'ObL5@|Xć5@$畢(yyN̆uFpl9ժ[?u^6b!f)b܍c!bq cr:7E)eu<+|1%S Y+8bԐxB3Q PcAuITSvdT[[VJjg# 0#ju5HG_Uc!b-Iع ׎(yyN<#icD?".Udw\M8Lp@ x0V_t*.PcC*y\SEQT~EQ ,Y}V2oj0;VbŊ֗_~a9 >sL I[4(sy_}L+VJ*& @A @ztF8Kdm!sҲKu\,>{S?*]Zz߳*6<8E'cs,coPs ȦyQb՘N}HŘk kHd Re01ωd'Leʁ[55Tc!ܧbSYΪcl_3…u1o(2S˘$8Kkٮ}r?RE1 שlv2"H@TWcڈ~Scu.$>8E'csBĬNf^AscMdsssuThֶKlCcݾK⒑FsUmz R}8ITd~lh@ES8Ϝk((* a"V[~j 'Çǰ=uN~bHZ E#i.CQq%k#6&+ _X\)jgldc\716,Zv风~,MgWw讎-YpS^4y(3H(3Naי7ω5OUd̂8.Gl`2YyioY/fn>sҮPca2y =EHݹ{*/lP$ rjDOD0w(P^mm^m 4]p>(2Oǘ$]MlnBŚ<9:xI '[vV ;3'i#[_ mjDW=vU?^GVab5NY#o~ h`~M|*d}Won7^UTW0˿WRq6e˕v$VSH?]솼G=g&Pc T4̹(?)X,!Gw1׿ƐE@2G%8]:/۔*UZ `ȹp"o&Sb8)klrb781 ܔG +EQ .40a kmp^)2Oǘ$aፑsW@2[ b15gMQ84yXg u(La.cyT^gұL! '޴Ka G~0\kVAAISN~悊ɔ?VPc9$Yɵ]v'R4kʖ/TmVݯw͵ 1.2,9FO,\ur]֊_-v>;TÖS?J)ڈq`4]p>(2Oǘ$MA@m9ɄTL{c=SS=AE+Ua {Xg u(La.col}X237j&fֆ}iVbmtlGUgjucFZvY&m()-:R};S3 1+c)*l2S: IĻםx5vUfD 찍ʹUCw=9lhB726Qc;If"a# U['obA烢(yyN ֨"oSQ`#su>tw$^*&d0ס`~M-9ťg=-6syH7lJQy<<' 9Xka @ +EQ 733x|u,N J6xS _Xu5S5tڢ9:B3ձ; c aA7E)eu⎘+t<:f:+*Un^=q޴ *Ui埨A?>1 [ Km8i6/"!IoP`ӢSO>S8|Rx&rNR1*#ߐc~!co5!ÒO?!aN~ѥVǶ6 b=b\2h|Pe01I~1{{lZuyt.K|s>DޣnUDD& T4̹(?)X,!G2O?Tń )|a$w7xۛݜT^Z}jDCZ7oUWk(((ztsgM'߱ZvbW=]~d5հiZ_"ʵ]L*Ա;nr[V.tlqقu%Xgu7E)eumj1iK{CVrUL"Zt~ɔ ''4asRf@we>8]>1Nk޾jD꯵k7q8~ [lڌZ)bΗ԰箇1eT/~ /Qq^`AQy<<'O$}͇͌U|XzYuN;I1ƾIziO0p9EQ'>EQT@EV4ٳgI|xKD\T> 'K.8m4jW p݇T|.UY{67sN\g̥\!o>|񷪏ŻK<.'ܔGR畢(yyNEԜ?6mØ5@$ Re01 I(w]ļ/TlMgu7WE^*.[( + ^(3N!;3^Oj-v ynkuBXWWR U|;LϘ!&w# 8|RMy$p^)2Oǘ${ԭ>SRmsKUtژ5@n >. Re01IHNuF&t2Z2rJm9n3:3(La.c8g6b6aL^̄r36uyvOh  ~BRy<-,;2xIU-ڨx1.cD5b(,U+byCW'F6nRq񐵚_Ļz8Is"sh|Pe01IY~gYGT^Øp.#ߙz;&-:w'bچ1@@%&~mrq'4o~)g5EQEOQbEQA@*y_Ou=?+Ji(=B:ds15]d!N!6$76unR 0i:DCԎlSw>s\Hbp^)2Oǘ$ޤ.abԍ:I09X5@Ty( ax@O"1N{3unexU6*F֘z\ߓB6 x|:f$_lYշ_cDkϫcqL'R{n{3 &|NsVvN J}A*4]p>(2Oǘ$9~3| |1>՟0&o~-'W /A@&x?P3皢(Cħ( HX`ȢAgUmD[lE@2G%80=2tUPPe[U|0CޘܡpuntئH+*L^#P;eƃ?y]jd[AnQe01IIL6渮G5<`tɆ1ka a  _kGQy<<':#9٨3ReԱ! _ 1pFj7343Xg(La.cCrEGT?_պ>M[wvSO6gg/O 99CkĬehu.ό͇U򼙎{W9ĘtQ+aʽ>jjO~Sfg}2z/v p'Rq:u%o{k2a֪oq}:Hׄ$烢(yyN˔{;aű.@/?'v?b!cx}4$aMnQe01IIL6FL;}d˘5@8p5k|EQ dHv}C&ئ¤m!ꘐ~Y wXg sCQ\\'#ZWsyƷʪM"cA~4m;T\*\?~zM_x!s ֨8'7ylNH­LFb(!c٢JVb| T:R5kʚ=%ڭ l򪝓Zɩk Tx)2Oǘ$ My^;19)t2b֝*&]6Qm XXx5XxEQ dH-޳ʖ/TB媱ߗ06 =:Y.*.HXgxuXg87E)eu=Y̜1wW{"pxܕj\p3YЅ/;È|xq7X*UQ6K{VŤw l*U͉[ߚ|=1yyfǸt:YtV 7!v1etxE"Θ 'ތݗZm *Qn5H?a&51ok* E'csm_^t b(ϑ؟;T1@@mc/ aD>~F[mJ@@ES8Ϝk((* a"+Z`lѼys7s K7ڹ5^S/u( QV 1h sMQEQ!~SE$,XdE_4LnuV X#Gz-sRխ[Wͷ~)2T%8̇I&?@mG]Ǵ)R=67K>3-'d![nW&Uz2IFU 'ΫlS,ĭTFFWeT{l|H|Se01ω?dME8(1 5wXhXDE'csb3.Vᕃm~DlPIMhl3Xgy(La.cӵnƩy󉘀aߦ"kyCr;(@?>1crҺ瀘G<ݭČ8!=nd-_MiuC>U1zX*TR¤#EQ {}{R|1L_S1鐮`A#UۨСpu>s">4 x?P3皢(Cħ( HX`ȊL5q UM7݄M]?YaӰaClNQ/)O~@6%z @aMj<'ϯ7ߥA+kkh-?j/$Z5\6 bٰ}&ت멱ܐ͹ns${^B,uusܔ}Se01ω?dZ5Ϧ<_-W^iSQ3 J'6sl87d k`` E'csb3.vᕶ}cEl TIV vVqa:#13Xgy(La.c) &u684VݯW皈zM[n* bͰ^l{[WPu'Pc|W0cF8*VE}CCU0m^Px}z0u(x)2Oǘ2d#Dو  sXSe01ω9uM3Ul9gP7Jņ p^)2S˘ v9we{id-_h]:rkկy|86 W_$?t)*R/d) #?7ĘU'u1@r]e,V;hk(WX;NchK[2e=;e7'cS9x)ZCOn>x)2Oǘ2h̑> sXSe01ω9uW%sYZu^9e3?Re01In#+{5z~R1RL{G*ȳxFs~xCkm1sϨLXD0 ~nSmV3XxE5~evjH~}(b'ȿ=&Br,B=/D PĽ SYxM* 3TDu< x)2Oǘ㧫z5shhHrh sMQEQ!~SE$,XdE_ 7o+C QcؤbX*bo-[`HZ#A> 3Ϩx3f`sk…]T7o.EEJx?2ݔ #?5UlJkZ|Cwd,GůQbjfdE'7 n1damǵ2%񳿼)ۧ7eE'cs4 a 9X x)2OǘXgx': Kx1Dr=)6Pc!Tl`Q3?Re01In $2KFQqYG &!dWXTlU\yNfVb&K>e93&@.`5k9Rs;[Ttxxflan8q%'ym;{.A]L^#ɦMYWO|0{|Tr?܌mps=HDeG#EQ |1yͪ?'> cҁ W=հEkO{wѨe۸{nBeR_1Gh@ES8Ϝk((* a"+r38HƫXSp{n 3Xgy(La.ca1 zq޼"FC.t5 Ɍϼ k'T^i{F `Q6c|(ѷ3 Py"F{Pǀ/U|2I+%toH<7w sE'csBlP?'G.~b!OT Sm[q}*$3uh@ES8Ϝk((* a"+`xկT6K. n:oslN@2Xx?2ݔ (C̩٠:0Yv)uLBcw0ܔ'|%׷լ(AOc[.C/Nn>x)2Oǘ2h5@` x)2OǘXgx'u]lLbEL:B1@5:uFTy(La.c"S*+bLk?UcD1հPlYkɐc(*4y5sHld݇TL: 2rL.L@9tKk }3Xw< x)2Oǘ`CU6e˕W3a}宇_Vmm nscId(x`{?q[F@3x?P3皢(Cħ( HX`ȊhZe˖U  5{los lN@2Xx?2ݔF2nY}LME4AțoY+w2afvQțäu1 ;T6 #f-SbCoU"q_Y)/u(=oV7&pRA{vGj(#}x-2AdvTgT?N9bt y'-Q1 f_Zu/XT9;y6~7Yayw\^G19!6bزso՟ۇ`MU\Ymf5n4`>MS1BBCU{^R=Y~oNL3\1/r/Ib? ;rE'csBlmMSz U`4`>MU"Y9uuF1mڇesuUv}5WHc1>nkBsg/|uuXgDW2: ?nYjwUL41EJ] ?S6BWn3|ڢfFo6˖\gZQm21QMsW0P պ@'vf!l@@\#EQ |1j՟M[aCp`bh@ES8Ϝk((* a"+`6mR'?qոqck? [ǎN;ۻ*o?KQ~ N#My44fm;sww4C4no!fn>bD6ڇe[ձج8vl&,ݬ +n }Ln ѹ)/Se01ω?Y3` 4 Se01ω90GO[J'ŦnkbFaT?`gXghXg\uF4y(La.c-1:uՠy+u{k'to&/ Jf(bhPHݦl1A׉uQq%k3p\cUQ[؈5E.n:N,+WwC o$vOytaC@\#EQ |1R& h.A.:ռ}7H*9^IyҁRDh sMQEQ!~SE$,XdE_4 gUu N~aEQ) '<>yE7yG4 /TIN=՘6ޤ,Xܠ#.㸳}j5]~TݖS?T:<Ӂ^w19 k%a Pf( 댒93y(La.cִc *UQ; pjx)2OǘHj0%a a x)2OǘXg͞?m-vyJ"*Ubq{U|:$343+EQf ss$F"cϖדh2t3Xz߳6~+YΪJ1p<y6 [Vdj(9} a(Fwz4yk=1~tk8Ȭ-%4ipu+4x:Re01 @٧}9|<6nBr<^'4 0p9EQ'>EQT@EVfؿ??}eڵj|0]{j }TSq6v6LoN~i (* an?ܔG@udY;mÁOɔ/Q9EG.~FωcJ:j\klflb2ݔ'Щ . د`tᦼם(yyN!x5ٰk= Se01ω90Cلsv}Ō=|lվj^#' '3hJQ\\'I+YZMjE%\7% vT mdURMuҴu18x5? 'ܡ✄ab "kzG?36NxوG^Qa@@\#EQ dD,EUL$34f IEES8Ϝk((* a"+r3  O:]qB ӧUy`ڵkFOP0KpAnʣwd,)UL&[-t#{Mxld,48)_TQm3fZ>(zEo# 9馼dOkS5}pS^NQy<<'d @\X5C` x)2OǘXgD5Ϻ>::teq7ģAV댒c3+EQf ssh,\G]D8;6U@aǬfKgMldс'N0Ss7zɆ]ʍJkZg.`lUPˉ@kgUlXп!WHQy<<'& o\d݇TL0\hHH|)g5EQEOQb}E pÆ j oЃ>bl;S2d6(*Ma~?ܔG@Ȇ7]()q 'Eŕolko[DL1&lh.4$$>xQ3皢(Cħ( HX`ȊhO?ư9<6/-[gb@矫?~C(JS_%8 7w=Iz }((Olj,ŸT ?dӟ,">e`s;L]?'36V1~#y8 ]cWn=ܔqWP1My;EQ CLję\gűy ka p rE'csb>Ygł >5K`Re01I|}H]Tgܿ}Cց󿲶jD8r1Das1l^B .T16~)6D7oVc۔/_o149_Ha%vmhޣnUc;>s٠c8* )i"l1E e˕-28E5ET1~ӾDoAnK8;T়ם(yyN!@h٥3 ۘ5k^w191 Ryȹ+3:uF>JQ\\'\բ~hpu+Kb94L 1.s3Y27KP[ +\n|ܒ_'svS1_#yx>xI kqk=Y/|bR2^tfQ ;rE'csBllx׮<3c\6H94h4$aMx)2OǘslX\5%XdE'csb34aA+}LU}+θKΈ>8E)eu;=Rf백1CmpB:mۉ`u?\MhֶצlBU TG_P1~M@16lޮkwVY"b;,UFmu,(U:=yUbme~Ld6~0u(glf(5ݏfh sMQEQ!~SE$,XdE_4 PԤI50~xlZB}U1Bʕi\lRŚF(*'䦼dhNda%d¢O1XMyeיʪ_'q LDl- |Xl}}'Rc:I$MyZU}ȢWl ܔ}Se01ω?Y4p5kK>x)2OǘXgh¬3d5R]ߡ#DW`q 畢(3Nis@WRפ۬OڻA@Hm#q|q cvzL^Ͱ4w2x|4v}SAzt:DW//֒`g] ;rE'csBl:\`Oc,\./4^uU6j1QsY$jT'A@BMG9fn>IAA{*5jAo1Щ Ux)O9G*&)/u( %U>&3s [^)g5EQEOQb}0;'NP|<3gΨ6ǎqE@2Kx?2ݔ'?Bz5ƖdlnJω Q2˹s;Lٸ$ c5@|Xx)2OǘXgh¨3`V V\P;3:uF>JQ\\'A@wS װ?'qN2t3fmO$o@1p{1aࡷ{ܵFw~k="b"":4x:Re01 1P>X"/ &4 wm8lBN x/P3皢(Cħ( HX`Ȋh0񂫧z tpisW^旵vZ^V6-o*ƆT> G96=E]`bZus2d.;?^0 ̰ۀ첈#l #Ȧ ; +E  ʢqA DX2)+OMNS}{O雗c@]7qwfUo It-s[kͲn5G<:}S]зZ OdwYWfL5yqN7 ow)gIYc `8;P88W9gbTW?lk3MEa'8՝&3_TKKY#`9ș gyq*'󌼦MuVWZ%y 63&yF~_)(WLq,DZeK [ali,sլ#ݥy*nX/~7Om?fs߱ٺ^fl5?{;8c^Z󗸏nf=R|qǹԮc87}C#@MffL8;0'X&YY0m1w=޴eFno:35 B"Wۤ}}[pu7"fsc|qnwgsguye=m.}Kv|lV?wc?fΚݜU?7P^;P88Wwt>ldr#~yKf08syF^n{)wd58c̜e.9{ ÐyFsgqr]Xu Dvbzix5`jMej7w#_R?Wj67^zjmS/7,?ɇF[/uUmW[3zo?lɔuo7vKoҍu|tQvө@XJ-Ry{4۶NcޠnuCyqiz;lnCy/>@y8\ݩ9OCMsMrø09șL>s@y8\d<ۥ;d?X [ۧE*ްmkv.{ jfR(SqwYpb]'_Z~"usꥹFx5=no?>f*sQݜm;Vo=[O[o-7ViEh%X麹*Ս(zZpk n(~2GGW^NsxS--j|_GSec !qe5,ؿ7YO=5E%n; BY|\k}jC{ldN}Zodu|cf~v~ZzczKoMj9f^o˶k9.=z[yq]ߞ7M]{='8՝Ɲ?>؟t-e0+49 wyq*'󌼦*}jZf{xꊇJ`:vUW!dR(SqwYp=56VZꇟm-DQoN̶HM-Zq5KsxojfVZmakaklGcN\~ɺ`M\i3x0M`:q<R|qǹnz*}S`Zn.^Nus4?}tt}Y\=}" /S<Ύ5 g| Ik2,ZoL3WoKMiq %սPVp}P/L &SZH#^zCU_N]7Qoƌc~hץKV_gq]m߫zfӱޔv>h79lnCy/>@y8\i9>wd`Dc=Fs@y8\d@y8\i9Dw#O0+t;s%3P|ށqǹ<#ydK r՜e'B3x;m\0aF+=ƃϸtE{pIԛܼm,}hsWH/שvUSʸt[;m6̇_|ށqǹTK-Tu{.?ss&2X2s(Oq3gL6~m'Rיw}6DKۦ~'ZnׅNyF3P) )8ջ,|X͇m[/-M&+WK͘]Z.n3٦`:/w~WٿuAӁGMKRKZ:›}.x{.n-oJ[&]~ju7޼[35p)<8sݰ/̜e݁]ݾ,8c>&҂m-wʍ=ec !qe5,ؿgJǥnmF]veۍP_Wʇe~zkްkzfϩ:o=,wkͶVj#Om}Hi5j}x3>:˲So=w1*k]]}q;(uCyCqzlnCy/>@y8\i*s/5g= s`wzY('m7Ƈlzi|((Oq;CgDJbc}eX0,sedJ(Oq3=7}JM,rv=_5s '8Υv-7 ğZ}[} ~|H)g3>@C$kY >??۴뮻j{{ŋ/* %ս;DLv㿍Tz<_dծ |hĊ%P88 }.%i* N4I=wu;Uv5+]iYP,~1qv`884$NLx ]m0.xgV/|xo{꥗^Z6'\>lkkYN\fR?~zyn.7}fmIM 7MMjKo[jƌ֛v·_|ށqǹ^@ʺm=c‚;z/a0`.>@y8\ed1rݜgUzj6C'VՅ}5S]y@|E]ywkø^f1=2P) )8ջ[pPu-jڠsӚ^0ͻ[/uV'\J-xU[9+gwO<'{^]6Zi~ν>S,&X-zӞi(OqK[ֈ Jc]ӬښSԛθ;+9 /S<Ύ5 g| Ik=#|n~ i_|1%|3SK6Z rHդOݾ9?[ɞ?87EƟ^'o7pDi7UDT,z}J-l}pTz_ɾG[y~^z#oF︯^w͗7;{n4]|=ms]P^;P88yX s̘t5gy=/-Kn^;L>s3X2sՋ;P88Wg\giCϺZp;i;wu_ѻ󛿮?cV_g\\]h2yJ)WLq,DZekjn6"+S]0GWFQ7>OPhvu-=]J1wZ{^GZ/XtNیUvQKi(OqKƻ6M_μZoю{d4X Zfv J o~zM,8YP,~1qv`884$NLD\}cj6|?w=_̞Zz27}g\m:[cW>H鏹Yқf܍w|H_iO>ץo=ȇ9Ӈӹ|(ocRKÔ>'85藳x6vI-0ӵn-h`0;U|ށqǹ׭yF;y:U[jY}vV0b^CZ#]I9I;&=h/ R<@Xc]Wi[RY ENՖ3*-qgU|}Lx-`%qckӮZ 9dQv?֢ix?x@y8\?<#>r~&SZWuR9ǺU|U#e1}3P) )8ջ,|%,a'fv{m3N }u=9h͚=wW?LB+V'ڌKW{szk=^~';bwqR%^/>[ K/~zoة7ߗmd,x/55c`ٱА82| /TmQ&|?c/6;V=lKL ȶhr+zfv"77 }Nҷmv՛vduYWVg`u헟[gHo]plz{uZOSoZu^_]k:}9Z|9N:˫ι:c.c/:V'\v'_֛:<5ӇccfM1|(/VY-}4zayqzy7&ۮuasssgnw3}<)n}Պ38MB㫯azşu{Xy>c{lNJu_Ϗcj9Mr~%-_YDs߬GJ"uwn>_Qzhތ9/}c5_^m'{Y~hUV6bַ;/>]QG|:Gk-a\m7j2WZ@?}~{ Vn~'MCy㍱7ߪζk2yqNlsǜ޵iav9pw{[k?ۥ">z>\uz@Xo i=LK~c)hK5k3fV+6MCy/>@y8\)}H[auST9ڙt9@w2x%s->@y8\d7y;}Ԍ6{_]'ܝUV_ggH~k12(' R<@Xc]kl}4{qCϺ2ɔoXທ Wsc2zϾ'cjs8}v ox8s^Zyߛalʞ^dxL8;0'X&Y.cU+bn?&~,͉Epu6ܬj}(͎en餉|~9ۮxjr}K {jnc_wP㮼xU_Nk?q߽,}v|ܱSndDӳ}TמGbZfT3^fJi2Kz}YmUyqNia۶gjc]ޫ >x;;Ro* s33x%sg6[|ށqǹ<#o*|E>*fA*=7iQt-;HۥPD3(# R<@Xc]ׯ#M%Nqӵ^.'g1"yoZ=eMs4'VӷxTkq^m3v>j-^7er |:޿nۆc 7쾚(}h&IZ}W^n׋|((Oq{]纺 Abۓ,{,0G&99^)>@y8\eedSg\xWYn sDJ~Iղˏńvbm'gyJ)WLq,DZe?Bkﳓ"JI B~j5׮=?oeҗz޻>ff[<@y8\sߟA/-O^5 {4a4l]otk0cc !qe5zO~-_(W\qEd^zbwqm5׬8[V/ǻ,?.[cߙNKIߎvc8h=f}p { 񘤖BTW6GV\uiiꛙӛ}vh;zhMCyc//cKoWP^;P88W &9@g,9(OqKuM-ιe ci#οu ݰɟgty;i…O BY|\d3i a;Z\f՛qB-0v7Os˱YUk?n;ξ EOo^mM/pj7rl::{tlKyMo^^Cy/>@y8\e4 s`mbJ(OqKu1WZmakovbuѧnWJsmؿՌ3sqhg13P) )8ջ[+ζYpY}Vë6~mk>)=Oj]NNӳS۫w'8Υt#_v_^M_2(LzTyY㶝%8VL8;0'X&Y ~ӟ~7iܟu{]Xs=qvm>jUV>~ӟMI) ̶ig@I/K&څ}kJ{.q7-7mчyY^g7=tGFuM|қuP88W|պm1b\r[v^tɧKͶߥo1Fg0[k>O5yJ)^ec9u[.Wxk|{6}z8{<մU_θf?jю{ZZ2nӍ6n~OXZ>v/ջP884Js}{+O.{j9j|{6_~`0XpphH`d n,ԧ>U=#OTgG}z'g}я~T oj뭷G'wב<}֛?~kÛoXq]Sj@I/K$P88$Iqǹ$IҠ2űǺ$M_qk%ux'8%Ibc !qe5`Z.n; quwב߂ 9sT{u5״JL (t\8+ꬳΪVXalv;s`"$IR,^?8sIz(OqK$ Z(SqK$Ջ@y8\$)L8;0'X&Y W5cƌlN{G',-׍Ν[:vuU\rISOU?t\p]w}:=`EpIX~q$IP88$IP8X$Iqǹ$IR,^?0qv`884$NL_7LvlN|ͫ^z)v|l袋.wUUW]X]|ŵ{$Ix'8%IkZkUOMog>V_k-%Ib(OqK$Ջ@y8\$iЊ?@Xc]$^vq$Ix`ٱА82|Z'̶h{/]vl5=ٖ_~wQjmFjv_EpIX~q$IP88$IP8X$Iqǹ$IR,^?0qv`884$NL_X->j饗_8 t衇Vod]#wwqWtPQG/^=XZ? .I@y8\$^vq$IVr$IP88$IS<Ύ5 g| I{RK-UzW.v};êK/z>vo̎K~[nc.n/"$IR,^?8sIz(OqK$ Z(SqK$Ջ@y8\$)L8;0'X&Y뮫v?o>_|Qӟ7~WwG?dg~ƛP"$IR,^?8sIz(OqK$ Z(SqK$Ջ@y8\$)L8;0'X&Y?_$I@C$ '$Ix'8%Ik@C$ '$Ix'8%Ik@C$ '$Ix'8%Ik@C$ '$Ix'8%Ik@E$ R\0*'X&Y֦P82ץ6g| I.)`48T$NL(u)M"qe@)Kam  ,,Jq] kShpH`dPRXF3>@E$ R\0*'X&Y֦P82ץ6g| I.)`48T$NL(u)M"qe@)Kam  ,,Jq] kShpH`dPRXF3>@E$ R\0*'X&Y֦P82ץ6g| I.)`48T$NL(u)M"qe@)Kam  ,,Jq] kShpH`dPRXF3>@E$ R\0*'X&Y֦P82ץ6g| I.)`48T$NL(u)M"qe@)Kam  ,,Jq] kShpH`dPRXF3>@E$ œIDATR\0*'X&Y֦P82ץ6g| I.)`48T$NL(u)M"qe˒$idz$I4 Ǵ$I$ {@1-I$IR7bhpH`d@s$Iz@q,I$R@=ű,I$IP?qK$I^X;0*'X&Y|,IP?qK$IP?qK$IҨOǒ$I$u;W<֎7g| IG<KOǒ$IҨOǒ$I4*DZ$I$Iݎ "qe%I$T~8\$I@qǹ$I$ KqOqK$IiqP82x\$MxN'8%Ia+ OqK$IҰ?@qǹ$I$I xo ,,x>%IĊT~8\$I@qǹ$I$ KqOqK$IiqP82x\$MxN'8%Ia+ OqK$IҰ?@qǹ$I$I xo ,,x>%IĊT~8\$I@qǹ$I$ KqOqK$IiqP82x\$MxN'8%Ia+ OqK$IҰ?@qǹ$I$I xo ,,x>%IĊT~8\$I@qǹ$I$ KqOqK$IiqP82x\$MxN'8%Ia+ OqK$IҰ?@qǹ$I$I xo ,,x>%IĊT~8\$I@qǹ$I$ KqOqK$IiqP82x\$MxN'8%Ia+ OqK$IҰ?@qǹ$I$I xo ,,x>%IĊT~8\$I@qǹ$I$ KqOqK$IiqP82x\$MxN'8%Ia+ OqK$IҰ?@qǹ$I$I xo ,,x>%IĊT~8\$I@qǹ$I$ KqOqK$IiqP82x\$MxN'8%Ia+ OqK$IҰ?@qǹ$I$I xo ,,x>%IĊT~8\$I@qǹ$I$ KqOqK$IiqP82x\$MxN'8%Ia+ OqK$IҰ?@qǹ$I$I xo ,,x>%IĊT~8\$I@qǹ$I$ KqOqK$IiqP82x\$MxN'8%Ia+ OqK$IҰ?@qǹ$I$I xo ,,x>%IĊT~8\$I@qǹ$I$ KqOqK$IiqP82x\$MxN'8ΥQꙟ?K%Ip'8%I$iX~8\$INsMW<֎7g| IG<Nj^7~AU*z+Nޗ~m#s*P?qq.Rv~Y&[n[;-Ə m5}ũW3g'8%I-{mlJ_zqǗ:4=/CNxq_1և8'8%IRKsKXQh7HRƿI/fqP82x\RE˞)V[mbӭ/Nl^wʗν# تXmڴCϸ GRwT~8\.{lM~~8\$^8/7/o\?L8Vk9<*g.}ōϾwis}yps}yǓ "qe"Ǻq3}w\ }[+nss֞۵7d C*hc߾nP?qq.B_Ew8lӫ| 6^]\/q4w3Sle+?}(_='8%Iu).5G)9h.}2\0{}6jm~YJ?[U5 aBt>D-ipq4]O|JR.vl4/v^>9s/nxBԟ{gc.b0oŅ,V/>).\' ^׊6ټ8Kܝ6sk\"V)ۯ9 nXq$],͟NlNJ_ol~e7wԹW|h4?{Ns}ytǣ9ח]<. xo ,,x>%} C7zl^uew/gXK N\+n/{X1c͙QvIKE:v~0A-lqҰw7e vҫ4^|Zz6.{dT0/hֻλsEڴiHn41 OqKҰX҂l5_,xW>Xв=4>bwͶonՋ@Ԫ/19jnǯLXPL1mpbw~m7 x?dUs;y`_[<@qǹ$Im~Bv~nn"sv{vk9}ܾ{4\_\_1axcxhpH`d@q.-:l4w_UiѻsSYa\-hq4hZsy߲m W<>@qǹ4o7As  \O3geis~lz9?x9͟NdgۯKMHV'8%iZrٿ}u)>] e<&e;}p}na:%K,ż;ug߲<{u왟~~K~i ^1ͬuu:[ٗb'8%IRz{^>c܅N}iiCS+3?5A-/iM^AkE{e׹<ڍuח(P82x\Q=:YU^^ߵ}úoV|~+nWs]{wO]JWjq5,;b녻3gΞg%}m7h]𩧳ǭP?qq. ki6<ͥoپX5m.-K󍻾os6 yʦϘQdz;me],:n8sI8߾μlNݼ=̶/os7$gҗ oW>q ci{~Ksknͷlm;7;Wt}WzYJ7V'8%IR:sssi7%n#n{mgٶcm.l7Ӄu bq׳׹< ef4Jv`48T$NL?8^`Mݣ֓?UDx;}zݧ׶L }Fffqޝ+/F-wXX%>niq0 {ᾇe?#\!8ͥo&UFr4Wf=:Xk}jX4gM7on~sqP?qq.IzxI;lN[{y@}*>m?V=omTYzOM|(Ͻ|LuY>_r}Yz}Yxo ,,x>5{S)KogpWi=r]oݴ/zlTOqKVzSxV-vўoV9 bM~cu'ma]Мw-{=Vrc?Cv;Rş[~8\+}0-AW,:b{m&l6v?o+yEINwN^s3g.VlNE'cX/H_^o,XmM^<گ&[[0u gk|1>PJmhlVYpp88$Iil~ exe~t=3Vlm;{'|<{={x?/\3%X;0*'X&Y|/k0ʣT|4l}W|5wud#Ϲ<{|.}ێ{XYPP?qq. [G-2SodN7Ѽ}Zuf7wK?a\М7u)owlx;e@-P?qqp+YskIYK3.}sEcis͟}5۶Ӫ~]ZH1o+;Wg\e+ʟ%Q/-f[si}:4Yk͎IV w˶]x}X!Oݹ"{^ͥ/Lvo_|qU\-BqVYpp88j5gpmPOo~|̩mSx?}6{>neqsdۏUcqۺ~3U/8lu\[XrYv^nnwLX}G},- e7|;{={rhcxhpH`d@qݛG~+nKs,LD\׋-)۾Mt~U]z#ȶjgU㶃T\*qNuiuU4ku}`t}|,XOuj9v|@s[gcRuѲgkNl4wQYԫ̞{n}e=3ڽWd" B#v\PcZԣ݂vpx,8sMVsn͉{]]OC\.zC*]WۏUXcZ},۾/N_Hg.7lu5Z~ֺb6^hdJ]G2k8P/ZK&rhcxhpH`d@qݛG~,>56>9soxwY7ޗ=Jox7X\ ~#x {ꛕ7}ƌ,8XOqKRV[-[uX/=қxW~cBY~}Z5l M&Z5g>jmt4~,cnvsqn[fZs)ZHV'8պE"l>,ۭV'3(v{WNxakm_nzlN"_lmx]7+VKofϾ=:[!-QMve_wO|og>'Alme?vՊf[<@qǹV9kĽ'=>>沭vh.Gjނm[X{X}]iCWf_u}(b|9kv{7ҿ6c}}Ŗ l6=+_hkײ԰^_kF$kF3>@E$ #Ep F=jc8˳}&$=w춦nQrNj~盫&fdg}V,8XOqKPzC[/"W:Y@_zq7pϾ4ۧUqxǜ&;ghX߱|Omte=nsW>lng ܜ# bg88Wޓ?W3X3{.}a|){LՇeaܪf=; {h.n,&[ώIY'RZ|,إ|–LK#n_E~K_lяwκ({^u>S{E:]0u_8y]_аۥŕ{>[(oe^x'o^<@qǹV9kĽ']{κ.KS j 4Z-} f۷ձ,K}Vzy5W[:Vwn굵t}ݢ|O>7o;~Ym]"rvbia*糃`ZW~,bNk\_VjX/5Q "qe"oY^qĉ[OsT]zc{+eM-qf1-v춦q^d:-~8\{}b͵fg?umLlaK6ۯUq:x m@s59['=o-w5Jo~nc=&ݲ&׍ιK Xnqt SBs_o(ou{TvTK>!oat-/ݗ/pXqZݚ:?κ8{eG,,~P;۲ǟ>cFG_Xţl9\]断U]uxm^*(7vellW&ާ/(<3?mD$DzXۡf2חr}yx=+kF3>@E$ #EpiK>cl~ >d1-_p]v[#xCY7_\ 7eT88ܵOX̜5`Rzs\?vҥ]ѧd'nOi@s79[y\Js|gɭfw?3Ֆ{z7lFş_~8\E˞<\v*GX7XWq?,Z웗G.!;a,ò0nU  &ڎ{\lpb묿ѸJ*}G=alᾇeϿ@d.xL֚N}'9d9Ù^/cvvE>u3/̞Ss.+ۧU`*l[!>oGv.>>X~WL$=:DzmFx,8sMVsn͉{]ϝ/=4Oח>&[n=T8:~u逓d'VecխqSY\[\i~nTqgQƛeO}yBG4>}s,K_e/+'+kF3>@E$ #Epi;{q-{-ۧ,&_|gpoq_(A?[P*~8\kÈW_=K *NFJɞ XU6mZ؛֍mFYZ<{i0-h6&;gLqX[I,KOsd{?4ޠ粒Y[<}A- q/1{*G}Ƶ'W>wi.og"l>[UúTo?.i ]Y~%Ŗ;,Ug({mSi>Ի_dMQ}`jT qNK҇~ez?{Q<6w}fԥ^ոsQU Zb׃g;VzHܿkIka8c=.\k^|~(P?qqU՜_[`϶n}qu`Zc\.WS+!5ٿVǪ[㮪wmmbyd| Jixe3-i^zUo/[qǗSmζU/.ח{}P82x\B}6;i\ VqR;۳v]P>h8cf'6{z~ǟ=fΞ{sGYӮaYМz9gKsЩec҇mL/51=_=;sHviW0P?qq/Ӿw^dJ u'viHU}X,8|3/,f=7ؚkn|Ra)ss_3ƛ4iܫquG^pc^eO]: \rOrTN8;X&ƻJ&Zѯ2tM9nOqkjrڋ|httuig;~G]d۷ձEyBqAU.6zyU'gU]շߜkk.]bNsonmwִjŧeS̵;ic͜5oofw ًϱZ}z˽u/ xo ,,x>Q.:7}:9@gIX,8XOqKu*}pەu+?^1vE|lAW~^L>4:5ozܧ]ò9[ޠٚ[}'R7dͷnS3ۯ҇!}0,-ԍ^8Qo"mG+Ro7J,?le;jӦeܜ|s͟Lm {u:nhkg|QL u-K X}sjVU/Jvob{^b*]c5g:wcWTXc쾚[wMkf7 P?qqՏ9k|ZT,>sz3(fnSGsyx:c9~ՍEeY7?V}%?=X͵}nl9k|l^[w}Ku|ͥq^rr}r݋++kF3>@E$ #Epi~1R&7UtZ?ru).;>β^.&a`P?qq.ե֮LwҾǝxSn-^{vf,я:ei9{Noain?iװ,hΖ7(s1_=髯>}_n!?jDCekaߴ4şe~8|[rCK:fκ(L~F|wv l\{oK;>ҿzᝮt_nO/\]Af?\4>lcK47oS^]묿Qve>l}1h=6o3G}; -cOqkjsZE|8iINRZ,>޲_|/~Z˱J_6uwpnVǪ[oר^[Ky=-عQ^WsOlNgZ{nq̶u Q\rY}Kaȓcn,%L?IlXҗ+kP/׽2v`48T$NL?8^FG8>֛bYp88CofԛH雦\#}soo,ѻ.<گoZϡN_MN)p}k4gA!O?z:κ_~_Zbn*-r!Ku(<qZ:wۯx/){ ͥ)}N% NGbvsiG h7 4x Ou1~Ze=xe?Nsmg_e*qN7=J֜},Q*&@qǹVnOZ7>M>~PZtMyGv":JUb!n`ݮݪձָk.yƮ|~Ko^O=.>`w|{vhݍ6͞sY/އ~Sn_#uyWg_ߌ*ח?\#ח;oP/׽2v`48T$NL?8^Fmwq|5gbۿ- \d9-&: ~P]'8ΥA/}s婔j7>rM1vκ8{͜=7R^xv;}k4gAUǓ>qѲg&oK1kn.ί;V~ߋv7:˳8Qw~_l4oinl|(0[ugpoXGlVòSo*kOl:vk\+{\:uh/3h.[\$V|/dO3NY:psV6-{Ke/4۷z`Z`1n_~8\SklXw̞CY'nҗN_}/[rձlגngP:';^UUcխqWOQJst">^{_:Oelɴ;ht &{Ξos1}ݙ7,{lCg_Gd"ח>yr}ו "qe"4o&,}agb>\~= iGNId*~8\'~,nUZT/-*wl9k<~'[xkdKo扯gj~it2qĉĶeth.O7b)s ʜUOS`־?.YbV˞{sEyZ2}}wv;bO݆T4P?qq>u7V蠣T gg۷j>,;,؝҇Cט9+{e郝q:v˳65(b~ ^ve묿Q}z_{ ?]n)mkK⾓ kcӦNl-W_~8\SkPjXOŗz[e?g_Wj75c-chbn{fΞS+m r{yRcܿŃn8Q+wk\Z'ۯjxomߪAlY{v'Q&unv4:ie_k[>Ugc?p-עlݍ6ͶVi^꼖󨓳}оǞ=҂fZl檼2hǶ^{~ϫzHK-{mj'85iڍZ-.&+ۧmâq}c7~lÏnwY,;KVV|[:VwU~'µމu7,{~>~[>ZϰtdϷl׃ɶwټfO`BxezyU\_A\qdxcxhpH`d@q.ZV@9۲}*(\Fô]?rN SU5(IX,8XOqKTzxtm}?n/Xd"n?Vۺs?ȄZ{ ) ^R{l6/۾[/(9:gл扯g#֋cW~󅷳}{XE>w}'[ w y5'85iڍ/ϣ{ʶWK+K''Zc@)zʮ|W=7ݢx?d|ju5NkkNY= ~+ۯ7Ǟ{Q:U˞oWܞm_E}I'۲o~s\_G/_cP82x\Zze6.҇t>;vokpvSw?U<>@qǹ4(=گ̶k{g&[nת=X_d;uPqW {l,Ks3gϙ(?cmչnm־A'_V;}4{ j4U;x?g+ձ OqR~wK<BͶYx\xRfòdvef,-0S${N~>{L͝}lQ&lmթWݙ_sqA`b|ͭY7uyW7F{sjvͥ>|;{OXmڴǎlߩTiCϸ Vf?(N@qǹ֠Y0=v ǥUU6~Tk2:i_p]{"V1,-nWnXukU}6~ſ? @6wѲg}Zj.}ǝYl~;ܼ;gueDӦ^4>7~os^}WdjWoS*ۧҵ8Tex,^X;0*'X&Y|/KTC3EYBgv=l.9-&: ~x|8siPw3fd?JEқO]W҇Oc~#vzcY7_lV{œ?^=4o.OT6gA Bemw^ѧ4>Okg88s7${="Ww|ŅVNq ڇe'ZB[ƏRӲ҃uFsכ5n4ӂc15w%7gBvO }UZ#_YZ#n?vm>l(dzq;_x{BחQOn/;/xTv;_kOqkj ڜu Io4o칤>.-Ff/wRcY.' 6{]Z .>=:M^U/ԭZnoӆZ¼m=XzKܷ-{6}&R7k(nzKo);b7+K?wU}_Y{fvͥK>n:wy>^/ˮ/sX2v`48T$NL?8^FCNx6&欻A{Kw7#$Z7 ;U9 Vq u]e?髯XJoSj&[oi'ۮUMgpoXPZq"o>۳}+}v7oPlR M' +6!սOqJi!Z6mZ/SZ}_h>mjmv#۶.{ܙ󉥅}']gWs}zէv~@S(D?JDkKMt es*}[>z(^@qǹ YҰ=\ʎl^~+(LsҸ}':V˅1of۫\{9Ӝ(kw4֎Zze|ƪ xw6mxzn\J>[NZRAņ[lrLv'nvݨSqĉU#ﻹn=<ҵ}չֿe}3 &k[rC~Sm/Z2^bmda,AU-E'_p og1պ5Xk:a}v;kOqkj u* I Yrt$Z}Q`VDzyԁ'mS6oʟ%KeOҿͣ4l\=U/L㋋=x_MZDn})6ȶ3ȶK6{S-}QCN/4TA,,USi,}\_Vǒ "qe"4*wx(bN8>[=۷XD7u74{\) '8ΥA+-@蠣F~lN{𻿞Yō'K9/6Ko>']6xͥ>h*5gXݞ KU1x0ަ4,şy~8|Jgס,-l^v ~Xv" 'WON@/K?inG_V>z4զMWYZ?3 -{kcW~"A:];⛲* ~PU {ܙ}k*[ՍOm{M݊P?qq5s֩4l'dϧ캧m߫ ֘VR MV2.(nq]٩Wߕvե{([ڶu\lu54L"[lSHdԲ`P?qq. bi3;sJ~?83gd[kw?OJ#[:]0}P FsS}slZT#fs7+J|:_>v]e'ZZcL[8+-"v+?y8Ro]`on2 ӵt*>vMu'=Qg߼8beGYX0) Vſ;sH}U+uigO}v͝v>Urs}Y*KW<֎7g| IG<NjҰXaX(먱y=}-bAGg65Fj?r֩,&aU?|qҠ;(;Jx|+?/mltZonnqĉskW' d7jU~TٺW's{re_|X9'8Gu7,{҇ ~Xv" '>OOY[G~P{?mybW7ۿEiuZМ{u2g{joEv?784Շ nIq)KV|xG=&.şm~8|K>נl͵fOSϰ4H>mo-w㴰@~P;lnv7{\e}'K~[ۺ8{}ݠuc/dXlv_natw3TǫZwXWJ}͞^}j9ٶ'[Z%} C|/c1sNAY0NC'ݎ_G~8\SkSi؞OYy|NeKn}0۾bveiO^Oa~e9W\7[tyNҘO1#ۧEn\ϙ7,k,w7vK*ۧ{_MϽ3.K_"ח#חy}yǒ "qe"4=_::5fNK x/u$ZqZ{[ǽ.\ˌ 2 Ip 0d\pzə&I(aت(?[O~@8wG'$%u&L3Vy-aAZY n 6WC 1f /c$w~kLqk^"bhPԸ D׿ QvmsyKw67tF!^'B>~y5dŻNY"eݠ @("1d hor3| lԏAƅ\VSㆭ| Bfl<&)LZF׎u'.b(v!AAf-ŵ`~ŵ[)i3g?4z!N@:i:('O~1_V+=R;)SniŮ-uy Q :OEd,+t^i hVAJhm/[ ɨKKW5Mff,sr=ߊz1 JynΈ0u㧊k(Z2?E痳Pv AAA!0Ȃ xğ|Dx^1gn[CWb@LY򈸶? /3 mb^VotE(Y (}s-'suEhA&FL\-;NQfYĻE'ObH le̖@wbQhFe"biަy?'w ұHyn6sQ󗛛xY (},C cj֪MŒͲnmw.,ʢN>8(;"6 A34ݼOc1n<8%bMΊ!EWv]\& p:ni+{c9y4ҡ08 黲rzO[aBz=B>~9GƬ~Z},yYQ/Gb CcXRgHzZ%%"N{<_ ,:L(t^ik]{%skyBtQ@s>iӖ?jZ[Ak*[.+z'I5]ˇAde"Yo(m |O|XdAAP 6űpaqh߳&-of&Pkv8I_iؗ^8}'a|XAP1d9sj79nShCm%sG^6x٣D NT?:=Y{{El1-X0-V'c*eD~i׭iK}LヤlB>~yעyzm&k[taS_hlIEA}4h#i}Cۃ4AsWExy_lu."/m֙ Ip 0 //Dv$<.ə)è Eq80q?O~@V8e+O17ϾWZh{45dH0xLcю EzMCB^&/8:-~$f}\KZ 1f8 /b6IqM'Иp2s6/dDȯ@&AǼg_WL*"Ie @oXCAo&4iѶy lQ$arYtqx>VȨ$Euxy^N}*Daf-s6<)t7=lŭ]z|fϥu! 8pq PB>~9GƬAp&_.Xq%>h8&h=OtcH弥cWr-Dqwݼ i[#c[-g:xc cƤ 2֟h,uRBHdn i и+r2ra>=+ho ʇć  I|AA#< `^6+l|No۹1h sч"E}] UZXwF=_'N ,B>~9iN-e@ǠQo[y/N0<͙_S Ni=z =6TCW"svӃQ3eҢq's1*Ñ!b%-kyq>) WzYKic_(7~2gi717+ AP1YfΆݢ / Ҋo86zAx[*b5t(w!h4AHx@8AƱ<+K1IƗ5jeAnE A@:ŰFZѲ̍}G)8I Pa+dGK>'f%٥CzA AP1IEyOE{e^%7-8VI6x6{?n 4ϭbTQfG[) Hy:Ei`ѕAzsc:|>5 eSbGmGL0&-ZcXwၘU)hs&He1-dz;$-n` siBY_(ӻ8ޡwq6<=gփ"N}b1[xpiqND#E[hS-^= ʛ mt$y AP1Y6+h!O5Y+0tm8oݡ(ͲԪm}k$&޷R4a tft] /cGE!cDz'Z$Qܚn{<既n(YIABm˧Cxaϥ55[&6=>nwUIOHDo~F@|/O~1kd>wYǟY#'Ř8aזn \ ~,\1E 2 `4!skN&So8sǯ>Rpϥ̾I}n?ɞ7qÿnt(It[{1$cFa rr$Yw(m |O|XdAAP d2^b~:ыЂzί@<ƎSDti@"AǼD_s,-%C)Z,tEG^e_4ݥ=I=Hh^^!`E[gF1wS7ct30lyzER T(:.~+C73~C>~9Gc֠Z} hQQ?ŬAA_»"_ҋ 'nϻ5Gצz1{0HT3Shܬq"} SX1 [s9 75m~c Jx?A׶ҦSwInj1PG6hƷAA1r (cev˖iv#NmL"֘ uG$q /t 7pFzCBoTf,~ O~5 VB{s:Ԏב1>OtqKǮ"mص@~<~{1N)y6D~DcD0t&u}n+OfL '.lh ]`CDbTdƯhPT 旓%I_ Cok7ACxC$> Ip {.}e֤UmmN^װ7g _3(D<k.2u} AtKev{Z D B>~9q@2oFEa@' oN' njfPt&ʢ c@Zcvg.i K[:v/vЁ#XnN@A)GMXo(k4"i O#w mt$ Jx?{O7RF 6hN.ҽωx+t)X+'1Iɰ鰄 gq{yL16|1qҜ`5u 1 >h%.@H ~p~ | pCmcρ?FEcsuT9 OZqصQn~s"2YȃcWχЙWdqn3|(yvz '-Z#o!|9ŏ}uxЭp2yFzAEVh(//W' Y{(m |O|XdAAP +VxلoG '/^/fe" 2Z(XBdBEb*~rk^&-# d}Q@AP1ʧ_ Qѹ@Әnu2nkԨa.1^uscv?D\cg޵|hA9&'@b[37%O=+Ӧ4,l/lzY-Db!(`@@>~ywYnlɫ/43"kv>߅hԬ&/Vu}C<6hƷAbQ; +Sn1h׽K"&IGIRQn2=t AQ1kNQ+:!bae͌'~}]'4ݡI4ϩuޓ3h ӆb 1f/NPD \x;@>~9Gc8Z}t[ԓw'>AVi®-1$祠t A\"q΄h /?ѰIsܜOB9'3a^z$R>.Q;0|0\/g ~슷5 !ț@O~@]zg1oOZLge~bsCao5o%'/9dtn{_@b塗 i4Ę͙lVt a@2}cϳ =y+D,bY6ͤc7אQ%?SZ_v0ǯI|dQ{.}%G 6hƷALxy 0i򁈳Bsr<&i(ڲX@ Ipn|J)FX(һO3y[ޟ49H#g׷B<&МnP!0H*^L/'/W,?]F{CAP>'>APH,  (1ay=齢{av@Zӥ`Q/Eiqnн8?R_ Lĵq:= Jx?-A q1o^c7RC'~}xƤEkGL0n+߅<;hḊqwin Z}˚ lkfE}@ O~ej#OnY"Ͳi%QBߵO_(c9wf?և_fؠߠ9v΃C)XT 2WGY!#CL8+tIn'mjz;gT. V>(.?K]vmcE+qZql'pSV+ V"]!2~%uKE^Cvx x[@>~9ݘ51qd>:wJ#Db<ޏo>(ڲX@bύ%uD}FQoCz2rst!`]Q湵߬;3Id^ȯoe"&Ɍ@( G\$bfUa~9KeWAA , Gy'Avx}qZ,.zP ՝Ǹg?t*%O2aIl|z eGq x{@>~9qqOk-Nnөih qo|hZڹj|k1vL\ʌx2Q`dc6w1fdAǼg[:v'9OEteOcb9z^^ sh?eKT~WQ}h3b~1^ЙPQ>.t 4gE x[@>~9ݘ51qd>N -JoXMà;g,iĮ-~<nx Q>vHO:c}n榨5j49#n%IDMDAͶs( @t: _HrmeWAA , Gy'AvV1LejH'kXIѱwAǹߊn޳Vm`&3 C4j\b܇mۋW |iIҡQN :x{@>~9q`롛MZƘY+Ɖ%b vK >H61ă`<`0lBtLx (},ӡWQ鳆f٠6ㆅn#4 qiC17.k*y\+C#b} !oԽw|<`(a"}RZ>T QztȜ _%_ 슷5 ! ZxC>~9qB Wzx4?޸.0ټAG/Bݼ$  Jx?4zGߋ>0ܚ5I3?o(W1IcSϊr[ymHs<}\9K-_vOT_NreWAA , Gy'AvнP~H,𤨋i6/By.ۚI u5(D<-A ;c$O'9RUYﱧ5DgBLw醷1AǼ$Y7 c6jVB O)M2~_+ ?O~etBΝ95'fhQQ+41Ig?hc5GϋWmHdt $ zX:Q ?ٚ x%k;g1N-*腡 D^{7 E+i gEz"H3O)'0>n _Ekr>vP06 (}sU0Z0fwfnt=&jC^B>~y3Qʧ_鳆f٠6ㆅn#t ?`4Ni}Ĉ8#ODAs=ŵ;ㅲE :܋wCrQw5=.o:N3(n>j߻2L»".|~&V&/^/ .@_]Dz.>oD)"}!jԨa>w]? ヷAǼ5f |ckDCens+7ϓuoEkˠ iJW<ŃO6;wTMy^{01V> Vf&bԥDܽbv<`ܒ?O$'0\nqcH}k^"l}Q - `=xC>~9@*^hX,2te%<&hT-;fӡ[8 @ B>~y)QłDfٰmγ mi<_E^'<.y" !UR"{x z;47܇W.^/Xc@LmE Ip wLO]'H uQP_.L^痳o (m |O|XdAAP dIStz1^ɊI ZNt14? :f6ij)f{Z-x1$NU妹`Pq0!Jx?9 r0ۅ~!10 ,AǼg}^_ѵ|H5,&y7$[QfMcÙ߈g䉨6hv]!h۹Hܒ=gDz'h̯ߥiN.nBRt(21|mx\1<}FVmDVw^&b"aSk)j֪m;ÁoVKtV0>x[@>~9Gc֨Z}B︛n+Mt].;wE>Df-_(҇][mx7FFe:v1bfcy/F?֪D尲"&)Os /󨙋nэ'ݮ͇Lx]>$~ /&Y]F{CAP>'>APH,  (1@8m"}1d^^ѩc QZzaOq<V3I]A/&iQQ$]vqf:>hԴH{35'~]q0!?`IDATJx?@g`\.l1[qٜ- Ʉf!Jx?<& = 7ۡ1Y"Ͳa <dFIG7Qmlu[gqmEa"N] u7O䥠I}# [a)ĸKD,ψ89Q6u ZRt;H/ CF947opzmAP1aY&k”%z+gPAL vm pq;`4cn:I4vCQ_V^2S=?n]7tNF7ViuQw,X!~ /ۓ,ʮx[! ( ($YAOlмmC*gŐf@Z1u&vIQv+w6P J735Gϋ<dgMX~p]0q0!Jx?@g`\.l&0fAp:@2ul׽o5S]Ao hx (}gxqD؛eB@FFπ;1Yǯ}.~.I1Q@}ߛ0h@qw^ifQ: LYQ9ŏG0>x[@>~9Gc(Z}oPc^wv{z <Ѱk0 w_ŵ8a潒6îwaO`n$R?W$~',/YӍ'ݮѭHضrz>H0 ^EeM슷5 !~9I0]،1[2ln-D0(hQ ӊBl~ˊN FTf.oM"Fԩ+BFy:Av:Ų/ظX"0 w0O~՘5*VحlԬqEBj߳H&vm AtFM?n /$5, 2?}_ELFz ~ O]'[wV@2uQЁ<}\,A6+ho ʇć  I|AA#< 8Й#$@h/o*)1_kHQL_L®D]ĩsv࡫1oPd tA[A0{6 (}so}$fa3lE$ Jx?<kߋf٠mU%t!(DO7hҁ]V*X'b@Yo-b̍c9[z )L2{W:X)3pI6اn,ᔏK&( ɼSڠkӻ~o 'ޏy?jYW{A]K"}!`6GЮ-2$zL\OA4y {0[w/h;qƓnui5[BXMO3_uV`~ o(m |O|XdAAP iv)i4$ AP68X۵|HXye3̀;#f&1gn)nc - `=xC>~9IΤ~;| ;^F+[}(lټ[@ O~n+~OY!ͲA ĵn '4\Ju;E?"O+G>qˠ;gPF ѳE~IeoneRg8-u2场\}OQ:w1v00>x[@>~9Gc(Z}B팢$s{ZElIٵeѸk/qƉA][ ֒kx?%uDy}GN1iA7ู̳(f%ҧ/:lk/meWAA , Gy'AӨ6)p" i5B Pl{lMȼaY3&f_lԪ-)s"Na(;hݾqNW1ŐM1y1AǼ4]Bx߱B!yL{&~ L5xlx7"&.0fwBtLx (}<0v΃>(植(7n#4 %KײrCD G7h9z^;/<|Mqҽϋ<0"$3ŢV:1n9yF^E_8EqX㨈O QV*a<' Ag@0>x[@>~9GcְZ}a}+E{PqʟEz+<#L&҆][iHl}}U ay{09y[K kXQ=}"& Ɠnm]pE. e_aʮx[! ( ($YAOtߊ62ptS,i5^ S2r{h3[~/n&A5ceپDNx+hQ 7/6O~D+{dlيA$ Jx?<pMEi1Y ͲA И_zMϿ-b@y&_Zec\y+ ty:7k5K?Q9q^CvUl(5<}!z #DT]RG\CAk-8;` :cρ?IS .~.ꯘnHo]>"XsH6vm`ܾ ߅! M ;sbH ѦSwQf;#&Sl46xb{ņ3*:x3ĐBKaSxZb"-1o^V1lAP1y[0q/æ@e@C4yیő &LPu#"OEj4]y)(jԬi4?eƞK_݌w9 NbHT[p`6Qc;Wx[@>~9Gc0Z}kPqz*vX'jSSص% ][οy[K ըQCIa]&t0 i.hY!*OuQ8Hf0,2>Pv AAA!0Ȃ xğ|mۋ6VТzZ(c%e&E{ѓK7jO"zL\K1j›$3 ɜoqV5W,Ek?5cz@]Yqͤΰ C ݣ3l 4%1^xE<cρ?ES,ۃŖ VĤ׊Q`ז0LvmT ;;:d@א1LVtȓn< =x8xgxV(3e6슷5 !.s4;tt}GygLaY3oځX`Ao'ޏy?H`X c`2f+Bt37{(6*x (}pw~(?8+ofY6BPV׵E N^7h;F~+EW̶76eH_(mD|Yg_8?LYQ\71"JiS1'` :"J@:_m(.~.g~/ Jx?#1kd>rF} )hy+Lخ-a<*~v:8te_eB㗝 x2vFʇG^ e /eWAA , Gy'AzVaXy㇨ :y3[:v5*7H4~{3m$%+WP{cUQ_ m Jx?A }\J0nѾgۆ l-D$c  U>AP1ynbƧDL\&[2ե`c۹E'fY?6BP;!3(~mKis!Mu2-zW.2]RG͹w>2?޼c7Q+m;qA0q*q-?LZF\#DaHxVf%bp=ȍKno##@>{AP1qY k ji팦;( `k]Z iq\FZ'>ʣ8qE?Ɠy6W)#Of0\/By]F{CAP>'>APH,  (1doRy*(C8_hں`!>),~DYAYl&A<^ጳ  x@>~9Y6jZw {X*7,` 0fn!5k6OTAǼ `DVacu;?#g},Fh:C!Kꖊ+by {UJ:|=HWl4osȷǯSĻek@X8`D8vqA1z0r;ɄmșDVJ-w~'̧VF!c[UF3<}cρ?M]AS8C!`ז0LvmT ;'6t@u\Ve qOT˟>|x2۴i{ ք1n0\/By]F{CAP>'>APH,  (1DgFT"b+(hOUIִY7h^:xxon1Ac6.)Ou= (}s$Y$kK}Ž 跚Js+hnV9vyF][0yصUP.ݐ4U5LZg䜾:'"Wt<THf0\/By]F{CAP>'>APH,  (1cv:KQL^^O'>WA/n&1s͎j~`q]&b@m AP1d[5jwZLKyz'*BDZM|Һn* l-Di#Bm4hqk,6mcȠ3}otqE\T,ʤR9[# eA{{ORVT'nЫikwu+1n񕓑]rʒGD~4v]y6f)ǡx\^э'jHJvkCȴO;_`~Pv AAA!0Ȃ xğ| 2hW+0A#'(Ƹ$ ڿWyMUQ<}՚L:9z cf? t"%OK OKL\UmNj&A/Zi_Hcn A\6 (}sm[P m#B V>ޟDIc-D&i2ij|B>~y9?G48~"6lo9 ʢGpoͲŠ @ظaY3QEvcp+ q&ԩ+?Rxas1`EL,x~nc*Oy)NGþw`ԭ@\ dpÿ0 %uD8pW5$?,aC8E ɻ{+\:@>~9GRƬAdkP7oĮ-a<*~vnZZ uk5&r<ZYXHx2 `(wW"xӢ>_=I_`~(Pv AAA!0Ȃ xğ| ;#+A.D㧈(qt1L$|dO]N=i I'AǼ]u4)w`5Vf&`e"FGR6zEXEQ+æ13:.wm=t/"Ϥ8eؼM;Qvm6hn[3sv>{AP1IA4k]W= `k]{!ski1$:$geC"+C|+bO5ӭiPGO;_`~(Pv AAA!0Ȃ xğ|zۓӭb ic"׾7n|hjewZCUA/-y&L,IЉl|$cH#;UA }}oY0$h--Rvyؔ%vw0`Vܘ+dH拴y6O'ޏy?36Cąĵo"NG6zA>u((cAPt-*&@AWq^ }q"}1Ɠy5\uUQEEe !<']F{CAP>'>APH,  (1`s5jqQڝ0O z>,;w|Hx9e&At*Y}y0M#O^ Am AP1omFB}ADP ZRTc-MBy&٪(fBt q*?> AP1y6{!bcG=_\t"։mun#4 s﴿7*3{ͣ|q&Z$C&=QZ2H_usq ;6.|;98%bd׍N}*u0|ڽV -/]q^1x^[w/w@@= (}sY-{bs+Gص% ][+Y[K wDQF+d2cdб"}1Ɠy5^irQ/~?슷5 !8Q-9dc$J@Z$3<!Jx?Ƥ׊Tg"N]pamu8dݐͲnm`ql~]LŻN:Z{A@ϰ%uDI#h@ԭq <+aA1{NqO9vm aVA/,ϭ[fMQN++$rǂ"?ąDbpO>b#q4")krQ/~O슷5 !:@>~9GRǬŒu -Nص% ][οX:6@b]sD9.Э~lXЊx/i4ԭﱧE`~( Pv AAA!0Ȃ xğ|$:-vIц 4Q>r-q E1ucF]m(Il}Vv^L\=0I1AǼt{Өhܬ͑at:K~9~C)'66 oL[EӔLVh#/m(ok2y2h ~216&"em`gCrQ.؇I6hDNi}8-<8"Bl9yϣ5k6*\rHg6t:7~27n"b>$b_7' 5 j߳:Q>.7'J70nԍ l=$ [_+M ukcJ4O~a7f%:H'N#k Vkߋ8kK& j+%ski4/ZCNkxy^Ky2S NI`z(֝ ҧ/W*_;Bok7ACxC$> IpP<~92rZmoX1VB'߹z>h؆td27Z3@^y:'vil||qs%u<0 m+R"}Pwos%} (}s1k ^n;V !$^ikK& j+Ź4NsVq:t4/x/i3usQ+txLr_"e9<5 !AP1@B4i,Svmq¦ACĆYVn#4 `AQ6+#'b@uA~V=/bмʧ_17@yoh8}X~]s1&޷X猱߉<젴]eAא1"=Ɖ%b;1G1n~'y5'/bu u@3jw/+F67 sIaHnEPJaH@>~9ݘ5Ix1Z} mi'ceokK& j+nKzgG"^1f"}8'6@Z/([j>xLr_"e Bok7ACxC$> Ip hQȢGFZ6Mơy~XNhҪIYб´ap,/r)`&a Am AP1$ :qqⳫJۅf q?]-U۾w~/ lQRC*gǶTAǼ{hPXȴi/^bIfY;ta }GNB m>7j8". hez DUR'{428Ezӿusp{WyaM"Bnq㳗Lc;];ƞK+BVݘ zz8po5]E,x0Y$ :~2qm1O~a7fM^ VGAqbז0LvmWAlx_DԬU4㱅hٮW,H_,NIr}~"& `~0\2~_슷5 !.j7*vix7{SzzX,i7 1v$s(b3,=O= }u7vZcO~a7fM^ V!#wI{?mז0LvmWAܒf@MjԬi,qTZ"NQR<q/Z܂`rzABok7ACxC$> cο;dUdG +APDD!"($PEI\Mq QL&fI_3g~\i^KVuuU=<Ϝ{?n~~_ʡ鼟VAQ[-J1q~~΢'m+/X;4i^f葷9E^R.X|՛5{aB?JiC{Nߪ\ǹΥ+?^׌P^yMwn| k_oim8]]Z+Om6~Ae(=Q`xr:W7;G<->zCyTӮk_>v3 ;{VcJ~W6ے k{zm{Jkw}KYu;nv|ncp:%gngu ȟNa4鍷tveeO*/_xuu_ۢ?yw|/gzm6<.}%^}y \#)vz -ۙߡG>8T^_kiчgɡ?`xr:זu7[尘Wjl.:׾r_JS^_}{Suw+֞\֚6K/ ;+3v9Tmk0y'zV㷤ugZv s?}9=;k4חmf'?W+k7L#9`Epm|psqnyU{mnD;^<1ʫ޹?b8{7}FvV??wV;H.+?{w?zmk1  '꾼u\ mv]>Nj]sVPP4oM>fw֗k_/+]7#JOk\ǹεʋn?.ꔳ/@O|7ay{U|xuϮko6 ͘h_T7S++?i{ph9罴T͵*k\vQ[oy+Ue\Tsњ횴aGU>GpGf/|ͯѴ7{(oE=\wk:,d_ki~{?e`_zV=8뢾,ʎ8z{?uwo~v=;urL~S):u.Ik k<\e*3_05?nizE~-i___T`Ѵ&^X/7k\_V2^y_`ː8/kz'V=4~3;k<=-Wy!Þ4{ZK;,W]Zwu/UĊ \]m-mm$<Ǘk[r=orʛV7~[ns+]ܧVySs}y}__ϖ `HX,Gy\Va]4vm83e_g<ϯۻ'qsIڌyf5yf.R]Z{Ńת._m__Y~|g^W])\TgW'>kU ~eڬ\#k_ʛQ?I˻zsW={?,ڙ-黚}DyyC_ʡ{;%wW05_y茶={CZM;_j5;䗬B=;y뭬ɗYuۣ?}*ow}VyAZ-r;?FoXX7?[ZmNvQy1G~E}u ־ߩ?`xr:$I5K@}:XOeF/x҆z`Z]^V?׾Er}\_^˟/Б YЏ܏"o]>hҮ{]]nʓbkWm6խ4a_%vG9 }&Q^jqɛkU\=S:u.mX-歿nsK;w׾Nw^^]n96mኧ c6C(0r6.ƌWo;>@Gr2d@?r?΋7}Z y[rA~]V߭}}׿}{U}J}DÎW ~v]s`? O\f vnG-rYyw\f9-N'߯z䆽yq;psef?ٵo6S(0U9l<ײhR>^&rM~c)o9-ޚ/j_nGk_=7+}Xzk_ʺoj\ǹ%IR{Ry#3/vkK{{Uwi/}T~o%{n>ۨZ93^y_`ː8/NxkC6^/o?zGە?o;-+=9QI\:cgOwU[~zg7 dߪ"0mmɯU'z֚//ok9T]ܧrs;>V޵eK;mXZɯބ/FUTb'qsI^c=pwyknfڪݺl|O7S'ϫ9w /[F\_Ϛ `HX,Gy\w~kE ι>oOv}ڿo!z쀅s߯}֛'1$.a0ٵ|_~CK+Onm(Y>u\L}immot堳:lm9Pz\:u.IRkjQf;>r8Yar}~FWzigW{x{+=z'TE0.Ӫ/mZʵrbh㴼|q_֢ʛQOسq=VS=;E+GY_Е8T7,ʚClMҶ~{_V 2 O\$~`o=^x(knj[ێ:toUԾ˫e-_>0 v|e~~1w<~}gT^gl*/(.ͶU_j0;Գj_^9t_|uuy]@m O\+.I}:zvS6(=Q`xr:$i 2u CǏ~ku^1?sBrXja {fꔳ/|Ak]󫵏kX?>%l)NO|q6yÓ8׹4ʋSVw{X#}^9\1?}G/mjlf^WȗkÏ}4w``xr:$i?Ur6?RߧR Sۘ5{P} 1C^|ޥ_qo^t%vΕ7>~ir}&wkclǽ]˜z9Ó8׹$Ij[AmZFmFW񅿚]WoZƑLx}it$,C#.I{*0@Gr2d@?r?΋$YÓ8׹$I4w``xr:$I0@Gr2d@?r?΋$YÓ8׹$I4w``xr:$I0@Gr2d@?r?΋$YÓ8׹$I4w``xr:$I0@Gr2d@?r?΋$YÓ8׹$I4w``xr:$I0@Gr2d@?r?΋$YÓ8׹$I4w``xr:$I0@Gr2d@?r?΋$YÓ8׹$I4w``xr:$I0@Gr2d@?r?΋$YÓ8׹$I4w``xr:$I0@Gr2d@?r?΋$YÓ8׹$I4w``xr:$I0@Gr2d@?r?΋$YÓ8׹$I4w``xr:$I0@Gr2d@?r?$I OcI$ijÓX$I0<%I$Ij;+k7L#9`Kv'ױ$I4u,I$IS \ǒ$I$Б YПܓ%IҖ OcI$iJÔkY$I0<%I$Ij;+k7L#9`_/K _kI$iÖkZ$I0|%I$I0 v|e`.ϥp6L#9`s)M`HX,\ gS4:! <0 v|e`.ϥp6L#9`s)M`HX,\ gS4:! <0 v|e`.ϥp6L#9`s)M`HX,\ gS4:! <0 v|e`.ϥp6L#9`s)M`HX,\ gS4:! <0 v|e`.ϥp6L#9`s)M`HX,\ gS4:! <0 v|e`.ϥp6L#9`s)M`HX,\ gS4:! <0 v|e`.ϥp6L#9`s)M`HX,\ gS4:! <0 v|e`.ϥp6L#9`s)M`HX,\ gS4:! <0 v|e`.ϥp6L#9`s)M`HX,\ gS4:! <0 v|e`.ϥp6L#9`s)M`HX,\ gS4:! <0 v|e`.ϥp6L#9`s)M`HX,\ gS4:! <0 v|e`.ϥp6L#9`s)M`HX,\ gS4:! <0 v|e`.ϥp6L#9`s)M`HX,\ gS4:! <0 v|e`.ϥp6L#9`s)M`HX,\ gS4:! <0 v|e`.ϥp6L#9`s)M`HX,\ gS4:! <0 v|e`.ϥp6L#9`s)M`HX,\ gS4:! <0 v|e`.ϥp6L#9`s)M`HX,_͒$In`e7Б YЯܗ%I$I˿70 v|e~,I$IژX^it$,C+eI$I 6L#9`_/$I$I˿70 v|e~徜TH$I)/o`ː}9$I$S_|H$I ~it$,C+v$I$Ip= `HX,Wy@$I$r򱓏-I$IR;@9\4:! rE$IN9ASǖ$I$r?q`ː}9_"I$Ij|cK$IN9П|0 v|e~徜/r$I$S_T>v%I$IjO>y\;>@Gr2d@r_H$I)/h*;ؒ$I$S_'<.L#9`_/\$I$I4|lI$I)/zБ YЯܗE.$Iv N>$I$Ip= `HX,W"I$IR;Mc'[$Iv sit$,C+|$I$r򱓏-I$IR;@9\4:! rE$IN9ASǖ$I$r?q`ː}9_"I$Ij|cK$IN9П|0 v|e~徜/r$I$S_T>v%I$IjO>y\;>@Gr2d@r_H$I)/h*;ؒ$I$S_'<.L#9`_/\$I$I4|lI$I)/zБ YЯܗE.$Iv N>$I$Ip= `HX,W"I$IR;Mc'[$Iv sit$,C+|$I$r򱓏-I$IR;@9\4:! rE$IN9ASǖ$I$r?q`ː}9_"I$Ij|cK$IN9П|0 v|e~徜/r$I$S_T>v%I$IjO>y\;>@Gr2d@r_H$I)/h*;ؒ$I$S_'<.L#9`_/\$I$I4|lI$I)/zБ YЯܗE.$Iv N>$I$Ip= `HX,W"I$IR;Mc'[$Iv sit$,C+|$I$r򱓏-I$IR;@9\4:! rE$IN9ASǖ$I?{lE-"K~c@E8<YT9K19D@RA1#fE3 AF "H:yZsNRڡoݻѨ_.q*1X,Eߗ&DDDDDDF;zl!"""""b4w\ʀ;>@L,teQ¢ǎ[z顿2z" ]}YorADDDDDh/豣""""""F^@zo| Ą^`H}_֛\0]ߴŝsy޽bpk3q-U+1+Ynx ne_daz=vBDDDDDh/H w| ,Yz o~7H/[ Zλk&K1 R 豣""""""F^@zo| Ą^`H}_֛\0]K-yL7+1$,]c20H=&¢ǎ[z顿2z" ]}Yort-1|cDptZq ;zl!"""""b4w\ʀ;>@L,te51op۝~ƙ̳vCs׮3qgJ8FD ' KW1 R 豣"Kqw ]߷ݻqMq{|VƔA3v^@zo| Ą^`H}_֛\0]J#E{S.{ָ[LV1"b8IXr-ǸeazLE=1y+~ǝ9Z͛7w^vŗ.7mq{Q|XL׍k8A&K8z.>"b%.q*1X,Eߗ&L׸ݹӳSL|Xo1Y Lj$`ʵ1A1=vB1cp-[4s35jp}\gґsU9Øv@zo| Ą^`H}_֛\0]Ld2Sv6GJ8FD ' KW1 R 豣"&]=̜?3Dȹ*oՙv@zo| Ą^`H}_֛\0]LЍw㠛.vڹmiCt\I n 顿2z" ]}Yortk3SO?kk֬馾8ć5pN\1ncacG-D-p[ag:uc#Fr~7nF?>ֵ?섉""Z 顿2z" ]}Yort-ĥ|cDptZq ;zl!b@L,teӵ6ZIҕk9-c c ,z豅n_""U}=p Pp b.7`fRo>V1"b8IXr-ǸeazLE=1o?3Qǘ("7IQ\צ>""U}=p Pp b.7`fRo>V1"b8IXr-ǸeazLE=1we3Y5Q3Dn2鹮M}DDz 顿2z" ]}Yort-ĥ|cDptZq ;zl!b<6j?q&6 yMbT&=׵iU_ =7\TbB/Xd/M.pN\1ncacG-Dޭt5j0Oy*6?m)pQjZ+Ftw箺wͷ{ߍ7o2uY,)cqn \nN7j7ir[~Mw͵׹ƌ}½5d鲜c M8=ȣnwk]w}̱=7_֘rŤ%a͌&N#nkjT7mq~?߽257b@L,te5ĒkHb]ju&AO6{Zjzkv{O&1ѿ^aJ8|cXIMG5rw{bxnSg\rmWQ@I$8}+&6?X$_m۶u5k4*e|yL}iwE_vo:ԫWԡC&)$viL(cs\׮͛6lРaoeLZI( :C$(LcM}DD1w\ʀ;>@L,te5V$\MٰJI6oLQZ ǘ(iɮ}ZAhOM;Qô/~&6.?Ӿ?sŠ1:m ʫv 62rO))N=klU;gueߎ.'Fp;o\u>6"3Ft;q* 05m)Ws/7d谂͓lr<ĥ\.J9sk$Cmٲc.ʽw>^?2Ƣ𕙯:uv=\t)ɪ @Xc 7[Rkz(UIQ\vhcH w| ,Yz k\_<삒tiN<-\شpa5]wP*oxc&n3*+1SLݥ]j֬iVZn?M{q;!jT2~iK;̱mۚ Uxyi/jl#HI&**i3Ak$\xH㒠-h\򕿸n矟5I\>SNԯیrcQ) 6=uߙ.ɪ @Xc 7\i@L,te5P90 [h½GBc =I|i; K9'|bĭ]{q&.*%7qu5=zd&ی3C?i9#GvrͯO84O| kL\T^pn武q_[6=}|vԖʯ5oܴyG7m10y;zl!b+gRiuiV}=p Pp b.7`Ƶ8z%nnvvp&O1,J8lJK/#.wm7yZ }lb/&&j;u:,$zi;;LB8ݸ>X@ /!;m)Ih͠y⿖xk߾ڣ;wN$ I] LqجY3;M\XT.]SĎtva=vBM#)WaܲQ|ʫvoϺ7|;o]6 %- Y?(s.t94ߴUI>糹nS{sW_sqMnc3׃?P._5]7jy׷_e6};+uL~70q{G3k]w}/O|VƔCcH w| ,Yz k^4.cJ+Ivŗ!Cea4e7bF8ӵm֔Jk谻L_1k׹.cگʺuf]p^{3O:Ɋ6h0_+1=ޗw-[4?[~}Mrf<Ӎ{)wǝC2jժeUe.6}7mq?ߴY5kϘ2dnv_p{}L9A:̴G@IPNSVƉl3~,Y,qc=9} w +M?ͷfe6rvr{KݰgFg\ؽkժ)Wrnsٔ4Okwܯ̓N'OGacG-D]ynիw={2ټ+2kjmRٔ<+WWvmӗ5<e⥦|I۹KO\_ =W+VvqkfP9l'?0š$׿#Idr7Y>gbbL{5IDAT~JzI)s2^"&w(q) 4e\j%?V1쮻]G2ysk\MAʻP)o:1p Pp b.7`8ȴ4M65?d7 -a*xmw‰'f麴KJ8ƪ a6ip[rSVRM}q;zr#_+1$yQg:vtW5:7h`N8ɔ/T?wiK+sYus3uyJB1:$[1KQ]*w+V{ 䈟ϝgl }D@סS{>;v >d4OkN?3y"C]> %ѬnK+wIe|UzI ZWçs>7XJҊLr<E=1w{cVʳ6gkIp&]6%nîyUWt=4}2sw%^I(ˆU֫mWߘ\ޝ=n?_ݞ$HTW5n$ױ,PV)8B䆍3 vڵs>6"sDO= =7\TbB/Xd/M.AL\i۳g/3Q6=x& 332As>kj%VButio=~m)FI&ɗt~%IX.ث2haS-ZWff 㒥\c1umܸ[z)[=iǯ$k+9'䙺.Ϻu뺙nʄu{ﻝv)PEIѰa#S_IY1UV^OiM 'r-ŸIfuMBpr1i%ͷܚ5q^$YesƘGy8SLvkSW+豣"n1&*zP sUQ LrAfX)/9󬳳>;% .Zlڋryw\O\]3Ut݅(SNԯ,Ġ8qIl*kgvUΚn,T33^iCxsC;.@e &E@1h3qXӨ?r{> SW>fS_ٸ\hJ8FCvムl67mz -MU4FϿ*u\ܸy4Sߡ2UI2݆gFkiǺz6vo:hLO@I-SO?kޤonϗ_yՔ ?G\.)}I~Te<%$Rr=ŗ\>>1nUi2XNcg-Qrn drX=vB-Ƥ\q>CT=IԧۋRIZz:v%9=DJry'-Ԡ>ڻz碼(|7ߴAsq'nLx]v%mkUTn8*e=b(."F.q*1X,Eߗ&Lנ Um&Ťmܸ;CD5z #&!|L.V1\WNӆGI5k<%.岉*hU^u5Z|ܼu;Lk&FUI|IUMϏ?v}wQGmM'3L=&O+7q Q0>SߛoՔ) 7c=ִKMm.DD5O$A]+$.)V2섉>2wWLXi%?)/4ex ,z豅[I|-g^gn/kL?{֓Q;I~b;I{~X'NI&f͚ߙmAsG|xv{ZG "Fw.q*1X,Eߗ&Lנ z3q&]oU|!$)O pYn]ŗ_2acH󺹍rQ(W^=Ӧ\r^aGY1?,ʺY|ILn]$aS& ~~cO@OQa]=}hڴ{&:eH oQXS& ;6sLfxm1ϓ)=ou-[tK.3e+4y~&>6 }ֺdxgM,.y;zl!b>ȣk͖0JdžqwMq=Cy*w!6#O|kV9HI7g?>yͯ>s}G2jW޻vDyӱaKM s$+pࢋ/qC${inQ_3tm۶5R:.|-w'iۯNI$w㠛LU)s\C7n_E~I fZy3%+4~og}_IP:y?r^n>6oܭ߰Ѵw櫯g*2n:g;ラYqc=ts?\U[S9H w| ,Yz kB7~']VI2-]>J{eez=i6S&J2Դ)o%9.r^a;\/w Ǵ)cVOW z=)ْ'ࡇv22tlv#S$իwfþέtu`٧ELNk6nLLd`@Xc WY:> xJ0]_y!Y})ծL,]K/ܔ-_xѴ!qđ&6j;WhQ{Jb7v'xbA[olc9;\v|R$&O _}zu=L]~k׮>)AsQ$))飏?5w%IؠACSc;.TJ9/j]`sz@zo| Ą^`H}_֛\0]60;zDKVf͚=_Д c%VZNQ6l]&jeisL&0cTrQ2 #t>5(ɭt'tO8'{e%9BgsnAsYƑ$ϓ$L8&7ng,y;rhSgL|J<ݮ琡L|6q5噙>A7l`q@Xc 7\Q?Ch s.w \XeM]]¯ok1' w@L,te5hcׯ$2[;bX (=^Su5q'sn?M0&hN2Q۽GӮ_֘Ju}$PXhCI-O͛z˯A - 'b$FXzaeܮ^G\ogX>ZQ`HK.5ex ,z豅ћFR(!r<-[4u{#ߤt~ZMQ3)VU3I_Q@{BosoZbUrŗ\>>1n|=2a mmM}g|ĈZ2x>L.aQ=W^5ep\p Pp b.7`m`,twn\\&;>&!cO7l4qz}߾c9obn)|ʹ9}+&>/2xmM|t-~IL=ѝ;bpͮqƦ'db'3^iØmHbWw Ǵ:[c,g㎦3<+dE=1zH3DU#BArbTM1A:?3cJS50erߝMcMSOjsbjS01pZN7wWL梶aF䜺|!N>ôYHRC$JM<"k.q*1X,Eߗ&Lנ nN^6m6eG3gr%zN%!|3L=b ?0N<76fϓ|:%֮]ۭe/1}L|UYKfܥ۸y)ŭ>acG-Dlkq%*ߴկ_+sqO>eѣbLX|ۼu# 8SP=iGY[n/D z7m=5w\ʀ;>@L,te5hcsM72q+Vu? `:>mv=Sl޼۲_lcS&*ox]Ȕ0sȣ2C;Fa=2K"^z+11z^@zo| Ą^`H}_֛\0]60;z=})t鋸>mMlu1n޺ݵh#w&>)=ǚ,McL|\J:ݾ8A&:%Y[o3I8d0|Nmo7eRխ[Qݻ/FqKSOtl:fb<Ir~w77%9|PRλk=s_ʼn&2QL{ѝ;XD,-p Pp b.7`m`,tw~wjʋ>&}?`%&:y{1d;bIlַ_?Sx '$ܥ铘o@s5U+*фnG<ȣLlZDzN3I섉/?Xeu}ߪϡؠACG2Xzs =vBM2)g>yݺys6m6e6( r~w77 fk׮V>xrm&>$}S&*uM~ne&KG= =7\TbB/Xd/M.A t℉L|6mI8if㥗]nʋ]fbVCsL|6y{:6N~1$?#^ؽMҠD$ܼu;Lٔ$:9@UA:X-ym5?flK.&~y~`f9ob\2ѝ;zľ$Ob! -9]owewW^3LP^+^}͵&X-y"INt=/4[icq]5LT[豣"FoI`bY4bذ*It<|kf9ob\2$w&鰻>&?-ZpW\y{ÏL݅ġ2j9Γjc)V,]f﷒ش3\ڵMnݺOv ,z豅ћTR.a!J)O5#kj&l߾O[ol)},w'~Ә~j M/5q;;L?vf3\}:>Jn33u~e}nS[= =7\TbB/Xd/M.A tL|~ƙOݻl1N8j~Og7 ߹ebMzqO>ebT}J(nڲ-3m۶vk׭7m䪌%]8vܓ&X-yR~}SOZ]JcANMc/b:urOX]y'qVSS= =7\TbB/Xd/M.A t5k4IۧE_'dbYؼysS^9jMàDIw f7BwFl˶?L/4'i&(}ٮWޮqƦͰ; $1xaK&XyRlJ2RNclW&:kRS豣"FoRIV ҟO|I2 x!wϽ1cpS_f~7lnIzG,;I) t<\liuI}MbN$|vDϟW2Afr]q(4vU+r2ԁť.q*1X,Eߗ&Lנ nN^qL|׭xl1̳LlnUȣl&1ZըAeM '>q%kͤS26ldcvrNt>~&DXq% C?R4=][cl.b~Y^=o~GeQO豣"FoRIf{бqsw;}ɦI8ZQ'iӦMMĉ&liuIι=v=-qZyAf34*۴?}ǑgDF=g =7\TbB/Xd/M.A tEM|v9'k5,ck6xϽؤ+L<_f9ob\2{ix!Ҿw@6nΌ wLd5QM)u]s[@L,te5hcW|ÏL|?'q]wl1yQط_?s>k)?,MAcQ&6;zā72IzM$ݴeg^aF_U)IGcGV}-Vqs챦qM7bX6%!$?,^6mjb<:#mv ,z豅ћTR.Aqi%+qA:i% SLA{^7Sxa&X-y"et=S[ R1&o=MX=vBM*)ߠgS_|ic=M;IZ W]g4h t1;!`tׯ__[?Ƞu34[MrQ>[niUGH w| ,Yz k\W|lHYvڦO⫯aY8xt&6iG?>KaLlumb;+A 4F ߹eb+#rʩ&6IO:d'zʵg&֭0n7m1嫲o~xqǙb o4t1n9;~Zn/JO2Xs=vBM*)ߠg(SGIۨ:u[άY?w衝\6{gO}f͚&6i%YxI'l;!`&mUg3h.Fy,ķߙI߸qcǰ6j9dh&QCH w| ,Yz k\W&>)./6,c|GMyo[d}MDI8c3h7qÏB7BwFL69zwu`$S@_sxД%-Vq]4h6onrc-Z,4=%1g=KlذE=1zHʥ z959K[Awɥ)Ou}i&Y<%xyf݈$K]rM(}Ou3}:͠PFkRuD/^pᅙwTcL߳4nF\&M>:N7ڵsfkFC;.@e &E@Ƥ?c>abjr?Ʃ/N3=}[a$yQ&:4e` 4F ߹eb]F 6I(gI$c@qӖmcͦV6벞s>kr'M}bǘFO|nj~~ 5P?豣"FoRI=CDXݯ~[v)=ܴ#s$ J~5ךl07o?Sc#Ll)4nƥ^{ČAʻF:1Zp Pp b.7`Ƶ1zڵk甈.Jv=Glܸqi,]j֬i|鏧$ٹKS3Ll\׭i_,t{-t~jzSgZ՟0ųXopZ2} L9dr'ːL|1XJ}y֭[׽ۦ\z.7e8 ,z豅ћTR.A>,S_I' lÔ-Tc=G&>Ip8쮻Ml6I\T{_t& ^7RXW_w={ʚݯ$5z S;H w| ,Yz k\sQLܮ}ߠxa&:+#JҰ4Ixuכx~w&:8,SxMl\vѴ/=h:s5fnw͛77uwO b1'7s)&*;|)+&-y"]W<([%)ϙ2) !t]~W7豣"FoRI=CzG:=;u:b)P㓴e˖O'2,}i_,݉4ߤڗu_ի-%b4cw6u`G+IþAsC;.@e &E@ƤL#ы/M7>_p!\֬wL|f]vEoacG-Dޤr z(95(~e.AsF4I)cvڦO⫯apLl\BߝMcMh;wbbKɠXu3m?׺nM[LyD,\= =7\TbB/Xd/M.qm`L^Ϛ5ke?-7Gbfܦ-L|uV1[5h'׭ۗfl9b(n0uZ2qد_Ӷg؃h;WNl[L]OqO>-o-^t%&*ZժU˔:OWb6۱TD7z|!y199;zl!b&o'xO[|<\'\%&6.zӾXiuI#Eyyv_*B⯿mp]9_I!b顿2z" ]}Yortkc[Lŗvڦb}L|+ w7y25nzqvp׮3eϚmQϢ7ZdPuqs}er''`%K j 1VF1->_acG-Dޤrbͯ<{mPrN(:>)nj}sŪ&>u`5ظ ;\TM}R1h.{,6=YgcϳEnͦ"k.q*1X,Eߗ&L׸60&]_IM٨ OӾkiʄQ|L} L^u5}sf%>˯G鄉L~ 4F ߹Gb+9&>~i[k?9^yIt(QE$:6=%I$KeϿpK.3^@L,te5 I׫m׮۰q)c>alѢ۲S&p$Uw}M^v)}Yi[F)Ag4iohvi'ӦB7BwƑl{<%_kDy_zꙶ$M2uqJI.PUⰻ e;h|GM(]L[n-y[DM~/NOw2Qi6wP7|<`׮[ګS~oKdas=vBM*)ߨ!KߠAlǠ5qӾX #G2qMs"^ؽ3<#6v:v4틅;\dA#:ĖAs1fĸ='Mb0<p Pp b.7`Ƶ1zQ|/yMϑF2acO]ڵMݢ$6})V͚0Ygcꍲ~$d<#L[B7BwƑL@L,te5 I3Iz& zYSG.Z4k⸣;w6erﭷ 6{Jr@I2E3t{۷wobꫯaYL!ai* 4F ߹Wb~rM41zvz[&S7mzeܥK&q\ԪU!\ L!ʱf2I:xyL];{MBުc=i>OD ^OIdߚ2Qoe:udO02Ƃ͖Ctͦ >GacG-D$ryF  .ظun_3?~>w^fnóAr\.V1~KbݎنO>Φ z깹25h㬸.oo$++M͛7l^&1Z8??n굦\>JO!d>0?%xɧ:<”׋/Դ)9t|wT~}7i\w dޮ7SF[ d_^O6m~g)Wfڦ}}~TXvO=iSUQ ,z豅ћdR.Ϩ!7۴ơ<7I"hݾEI_й{ͷrq9/3Iu޽0uyJ{:>J%aaLN1&'`֭x( ^7su)n}ںFEqoSkM,"g.q*1X,Eߗ&L׸60&]2IcO3#r?Xe3^q76u}M\cJ }ι+Mٰn{ aFn{)[%iܜran4c=YerB7Bw%"S}`)fZli{앉9˯ L!u%i~3LIW=uR ѣK3^6媲Rɴ3L~%갻v6n6esq˶?ͷܚ9Ϻ O.`%A⮻fX!hV}~¢ǎ[I'~xaK.Q(?Ilp9(!#*$t$` A "X&@AW "DD<_=]5|tOU+ؙٳ=LW/O>v:/77{~FƖy7u֦xAA}u966g'ck>Qo;4}S9hj?m/sF]˪c[o/\woǛ|LgNܷ$ߨr"c_yv!u O.}?Xܾboۇ=YqeY3h/|%XQ^o?9a?;[zyߝ)ާq^g;ޑ_~e8AkXGYܶ~26sG}Lq{I_g'u!qexڭ Gyx\6 2nēOV=cϽP K W]u\ ⪫Ӄ=[nMAҗB.Z hϩ|zf,p̃~yɧ~c<ēƛnN'rZwAn.vǝz^o=F1,_$K7$W^] ̃cdw W[mc[dE?+[mUf:k6+kk(oC~ݥ͟t_ oxCۋǙjt([lv>3~rV~}*?_g*mc6}5nPZ[ϟm ׌mД`9HG~ryT֍S)ߏK:= ,,hW<.Nj\nM]8̓;袋Vw\pkn]=\[lj7U3>OV7<Gϣ9bۓ<.~_K-T5mHqa/b﷏jl:(sl /p3f_W6x5׾j~30Ϥs+o_i啫&C4z ͜93eN8䁆k)N3]ğduuq?=mMTʫu]7PxYgSS-+n]_8b;5g'ck>xG^V\1zѯ_1,WxZ}s]_w3}^EV.:(uZ= ,,hW<.Nj\nM]8əg=󬳧:{r"=]l7x[wWvsO<Ը!(_~9ϡi jwVIqI.\^zb{Mn9ԕ}l~ry<ē4oI6r5;?yxGxqaKZ[5[mUcO.l^ uW0]˫iV.F?O}_Z}sdב}o}۞{]lf{_q{I_k'u!qexڭ GAqܥOK,Dqa9sf-WG]驧Ic*m˯9>adM vW"~谯6fyXx:Zs5ӽ?Pl3xa:ǥEg*V_-~[lwv]Ng-;H]}/imټwqug_Ͽ"-Ŷ{m͇htş *;qߒTm jw\^{1{΃=\oGY}^1^u ugUOWYek7d衋^tIZ|ŋ[p-fwc ~?SӺsNk}lmn^Tlsl /p:Gg}T::%=oy[oӄ%Lga,c팟FpnphH\`Yd@q9^vkQ?n4޶?B[T?*SW]R FsG_.5wX`M< r7p`v/3Ea_Sf]C5rСZkSN=/=*WO>ҪVlc=lab:_lk*uuim?f̘Ql9Wlcw+=o};}4oI6r5;[<[lv:/VkxA 7|OV[=z{c[guӟᄃ_o?Q0@xY~:_7)xwqi7(/Gx!|c[h}}~7{ry}o9Ư xFlŕVJq攟Oo/e~vzO~a7)qcaϽN⾓]޿c-$S|Оxy\G|E+E.j|K.Y欰Bq۩4ǽmc~OHk/BC|q t7U o3YyަmV ZPA 7&-"s|` '0:b"~(.|[C.9m0}~9Lƛl.Wcק?ϸ|ya5`)G~-=ߋEy< sya|~Ӽyʫ){:y@ك94-owsoأ>^x?KL{+,`Pq߉kc(WSo},r׾zz!n^ {pym~Z]soIcfk_r_Ƌ/=;dD5f`y}ɦO>𣏽rwޕ.t YO{-9Hu?ӌ|wO|~𡇫Ϙ'{P @#ks@784$.,]/rF]t/z>3a_ruܹU֝;7iv.H_}o`zW\yu1Lv/R_jlX]ZkUsK#㼖C O~zc֬ŪY4.|g)C䋧}ۦ^ Wyʀj@5H87Yf<-y'T}O0hy@_3l5*Vy5\3?=qfz(G߿0NIGTbP.qy/T/:j;^ *;qߒTm jrU-{\pʫmy A{b 3f(ַSm{Z)>dY6b~V'rn;ᠻآ.Z ~m>k]p6'+?S Royw_.kY_^g%m&*z~'gU̳Ξ`ϾpжZc;UnLW^}MZz饋Doyƛn.TZ}sU6QE|-hO<<.t#>@C" "I$IR= *;qߒTm  _|9oٳg۩m[o6/ͷآtZt֬;ⶹK/Xb6s[l.⪴Ke6tuӼym lK;wټwNO=LX\jߒv%_|Оxy\G|E+E.$Iz/Tw%5AV#iǝjmE!nӟ٧}s/N ,@GxcŅ핇 r-Wo-iOtmަDlUWMzos`C&>дKo!~1uϽcQb[{´[Lhl#}Kjz= ,,hW<.Nj\$I$I_0}K:=~yլYV\1m{v|7|˭c'sZwܴꪫ[.͝^w3mV˱?.;7]]N=tؗv7zny_~vx)kq^ =̳հO~[nw9sTN~6x.|vyp5.vOW?"iM7Mj` s̸N󪡢u5+<>VELn52ˤ]Ϯd]w޷nHpnphH\`Yd@q9^"I$IA}'[$Iz/: АvrE$ITOqNܷ$I$I_'u!qex$I$ oI$I)hO<<.t#>@C" "I$IR= *;qߒ$I$S\Оxy\G|E+E.$Iz/Tw%I$I= ,,hW<.Nj\$I$I_0}K$ITOq@{9\qXYЮx\H$I)`Pq߉$I$ s@784$.,]/r$I$S\-I$IR=pnphH\`Yd@q9^"I$IA}'[$Iz/: АvrE$ITOqNܷ$I$I_'u!qex$I$ oI$I)hO<<.t#>@C" "I$IR= *;qߒ$I$S\Оxy\G|E+E.$Iz/Tw%I$I= ,,hW<.Nj\$I$I_0}K$ITOq@{9\qXYЮx\H$I)`Pq߉$I$ s@784$.,]/r$I$S\-I$IR=pnphH\`Yd@q9^"I$IA}'[$Iz/: АvrE$ITOqNܷ$I$I_'u!qex$I$ oI$I)hO<<.t#>@C" "I$IR= *;qߒ$I$S\Оxy\G|E+E.$Iz/Tw%I$I= ,,hW<.Nj\$I$I_0}K$ITOq@{9\qXYЮx\H$I)`Pq߉$I$ s@784$.,]/r$I$S\-I$IR=pnphH\`Yd@q9^"I$IA}'[$Iz/: АvrE$ITOqNܷ$I$I_'u!qex$I$ oI$I)hO<<.t#>@C" "I$IR= *;qߒ$I$S\Оxy\G|E+E.$Iz/Tw%I$I= ,,hW<.Nj\$I$I_0}K$ITOq@{9\qXYЮx\H$I)`Pq߉$I$ s@784$.,]/r$I$S\-I$IR=pnphH\`Yd@qY$I4`Pq߉*$I$I_'u!qeeI$IhA}'$I$S\Оxy\G|E+%I$I 8B$ITOq@{9\qXYЮx\$I$5LE I$IR=pnphH\`Yd@38B$ITOq@{9\qXY_L=$I$S\Оxy\G|E?S$I$&: Аg$I@{9\qXY_L]$I4hO<<.t#>@C" &?W$I$5qnphH\`YdRMА'Υ0!qe@OKa6t#>@C" 8l G|E=q. ,,z\ )XYĹfS@784$.,s)̦nphH\`YdRMА'Υ0!qe@OKa6t#>@C" 8l G|E=q. ,,z\ )XYĹfS@784$.,s)̦nphH\`YdRMА'Υ0!qe@OKa6t#>@C" 8l G|E=q. ,,z\ )XYĹfS@784$.,s)̦nphH\`YdRMА'Υ0!qe@OKa6t#>@C" 8l G|E=q. ,,z\ )XYĹfS@784$.,s)̦nphH\`YdRMА'Υ0!qe@OKa6t#>@C" 8l G|E=q. ,,z\ )XYĹfS@784$.,s)̦nphH\`YdRMА'Υ0!qe@OKa6t#>@C" 8l G|E=q. ,,z\ )XYĹfS@784$.,s)̦nphH\`YdRMА'Υ0!qe@OKa6t#>@CK$I$I$I$I$I$I$I$I(!q%I$I$I$I$I$I$I$I$MА$I$I$I$I$I$I$I$I& xshH\`I$I$I$I$I$I$I$I$I94$.$I$I$I$I$I$I$I$I^` 0F@` 0F@` 0F@` 0F@` 0F@` 0F@` 0F@` 0F@` 0F@` 0F@` 0F@νlvugJ;0`MCNЄ-$"%Z.[<\p0Q(FA(&`0H5"ZcPNB-&+kakM>7}7I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$I$I$I$I$I$I$I$I$I$I d$5>4IԨ`HgǗF ND$?;$5*/Xp"$i%Qy$Iώ/I 7Ts) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s) $iԨ`@s)1psIDAT $iԨ`@s) $iԨ` s7EFUO}泛CUsR=?`<_~rmr'Wy\\;U'&y|/6H܁(]HgǗF˥{ Ys^}G;z~ /q[=ͶSR=~O_a )ߑc.U'&y|/ɵɵ`<_~rmr'Wyk"߅$IZv|IjT^\iې>0o]>}gQ}cg} Ta^Q=Ͷ,W?kX?`<_~rmr'Wy\\;U'&y|/6H܁(]HgǗF˥{ Yŋ~+UяWݻُ9R}3gOdZvť^V}d )ߑc.U'&y|/ɵɵ`<_~rmr'Wyk"߅$IZv|IjT^\iې]w<}? g/뿩o>]]ﮞ?mkylU?]$Is0OE?6v̓<OM*_kkSO|_~f۝q瞼%/AW?_RHw$ y|/ɵɵ`<_~rmr'Wy\\;U#qwa"I֟_,u6dmW5|K/᫫w\W->~Í?ȶ<K1H1*_kkIs0OE?6v̓<OM*_kkuw(.{/UvCmnƭ'cZȃ~_3Ϭ>SN0H1*_kkW?iCM}ֲ ;wl>\/xa;_Q}Ϻg d|G'Wy\\;U'&y|/ɵɵ`<_~ 0w |&$i%Qyr^mCvq`dmUw).oU/L ?OWϱm-ɵ׽W>3.?9x`&~K_ d|G'Wy\\;U'&y|/ɵɵ`<_~ 0w |&$i%Qyr^mCvu`ھKSO=>Ç~%]gWkrOkZ;preϫ)w;ݭM'Xy7M|G'Wy\\;U'&y|/ɵɵ`<_~ 0w |&$i%{%Eѵ.%/H%KQ@$眓sň"A% 9aBE_y=ks~(SU=ջs{v4M_`۟B50oCu.pQ?^~ȓG1En-y~E,YR3,[R̭ah٪M}0DWk=|m@dx}/p_v^_ ܃dk 0s MSX'D ? 0`@?^x!?$o|+WEH٭%${>%(.1|pb?Hzz࣏o@?G 18jn5nj? _;pֳߨU%[^R vP#ݯ{,x}/p_vv[3_Kw#S}} _;? 5z5fx^-w^;^_ ܃Dv/p̎;.Ƹ#cW8uG{<rԷ_|*U,AԌԞV?RIDU V]t 8yJǟ!ys MSX'D fϙ+ɰyV_.~ 93[K/Zϟ_7a]C5WA D#\fU6"As5q'V v~PRsp,wbb w߳fw/,5/p_v[[u4g\n.jݮݜs,OjŸDpϒm8xk/$6;_6|@Mۨ.LQ'xՉy Ǖ[UAz]g;c⅞"ndi1Nyk+(xvY1x/rXxg n:e RmT[_uf߼nϕȰ%1 ??kj(YN\Ty c6ޭbQ}1G߇kzPx53HETUSc:uTC1^@Ofr|S^{RTq6:^h}<9gΪ̒E]lo ܃D@wQUɒ%ű(*A]𗞽zeC޼{H=#J*j1_x]٥o/1|]<@ ,\t]Zxڶk';:U\EYp- HoB7mzc:n1)3g3;N7 "a.C wOcDՏMkS)㟪seݠ%c;jϋC|nC^{wyiYcߣ!U5c^sȪTM-{ ZTB-^{= .a ?Qzn}z/@#^'#__f<-yj\ 5Q^{phVYqVm,^?R::N})1vRmOT0ЗVzO,ZB14t_ԉD:;_ $c87h @S={)++K,ڴm+G?R-[1_JJ:׊Wc1ԬYSwCACCCCCC+>/pOv!k1{b Lu9u>7m?- H~}&tͷU"ҥϿ^c Hs з)UF01._]%)PH[躍)91Kˉԍmb%ob|rL ǜ\f=zcBuZFFئ16qH\ hhhhhhhoxGCCC..d-Qv<ܤ7<>iiBĶ0[pi?XDkV[0ir{?¬YbL \tX'nmV#Hh9 _; kE/(FB׼61>&0hHacS}8+, Z45 _;d]?8FR|e7PďŐ3W`]p|ݐ̓f@-mrʥ6\D$plBk 5 _;yʝWV(t9ƒA|Vx:,xeT,_"k>IN/+=X=~xn FB׼^@_̺t*@ԩryp1eff96o*9B ˕b ?^45~n YK<;c3tMw7(P@]?vkN 9I`41lup޴G D#\t'}0T+^x[Vl/\DMڼW̑("k7 0~p_6|tQY;6z/f74Wo1^ to լJt.Zjא_6|ےv[ }?C귊|ӌc9)ŪЉb[Ju^(771^j\k/p_vnZ*R8O}&-s@{%ʔ۰B{c 1 _].pU]E E0N:`mԧk^C{ eﻟ#@ c b!yGf7h G={Ŝf^X͘9KlpmDp_8$~.44444447⣡ij dy~;+M4GX.}{Yw}.vp7Vvkn 9jo7y)|D?tmٺM@?G_/~WQX `vɜG"ncHIQ}'/s$AV&n#A`F)=|mځ7 ;5/nաTJ\bm c2%Jmԗa^bdUX'>O(Zjא_6|ےvSsߪ q,vwc"7QͶb\,(outKt5w>zc *,_L1 9$-Vqk5G?TUZ6J#[w@ߎ/׹#kș+ooZK1w9u5ԉe|k>]vj1Q+sKv]q ?6mۊ1xb_1 (~M#Q;O/~.#}Qؽ{q pܹs-ߵ$ϰ|*)^!2ȷIN>#n-# _UVVnH 3D.@?G*m"~W^X㟩mv(etCqReqQO  ?kޠ+D׼cu11&KF,q|JW.;gωsB) _4 Շ?s@{EM<| yk-YjW?NݱOYkEY۬U߻ŸhQ=D_·a%C$Ӗcj\kq-=|mx^5Xv: 氃- ۲w"1{|_ 9rV|K,&};qN@h u"x?_y+O׼3Vz.CO|ذ{CEà;ѧ![ lSO1+ꖲefN>*S Sb ?^45~n YK<s1իNYv}`O?#07cEe?얖y1!d CEt4XXo?YEfύ_ E77,{*&|Npfh` H Z)=|mځ7 ;5/fm_Ijoĸp ^̗ts&oyZe~_+UZs$A sN+C -g/qyZhk/p_vmR)q8Vz_ƴ #7 )8ӗ["K-}`zy zC:u0o?B$9?ɭ̀YƊ1r2Ld؁& : CnY;!lvm1.bwA\DH+dApϱ`7y#`7@F_6|tb3e,wc…-sRe,-k%bZsC1G$F~0nb5j)im 1iEhk/p_vmR)qQh_4 6XT9Ǒ nPBNVs.+Ըr^? ׆ _]LM `{3W}5 $wo D4|k1k7hg֨So1&Vu"x?_y+O׼!9 RiԗbӲgnGĸPH7ҶBpF_6|tbC|M ))9ղc…Ȭ:*[ضxя~r wY-gF6(9 M<| yk-YjWRʏ)V<(7t7۴8R# ݀@w@+%q-=|mx-Oc8(SK(bLP'y D2~ ܃CWPytCsأg/ms1f !0v^ ߬gSb{tdڵE_ 'q$TP !ys MSX'D SMtw?DXL:MltIuZFHn.2˕b wS 4I9WN1 "a.H^#~GUPq@OT9б?m#? _^ @ ސDF9/)=|mځ7 ;5/Dr51kM#GAuZv۷Buc95hJ1EĘX-?f%ZTKP&kޖ,_C5 $ ZKHIQ~ICT[Py j\k/p_v^5o1U 89j /!ao$$3|A)PHl/MY1 |}v" u"x?_y+O׼!9|o->S1 !04΋l@;'@μzNC?Ƀ  hhhhE?م%j`ߎ' 74<.?ZTRbd{Dحe׼y 1ab ~F10|H?Hs vZrM(lM+^x;Av;lG8' $[^RuZvZ9x7Qa̢b?hW))9o}潢pF_6|twbft& F&]?mVj@_`͑ĸhm&~HMDdѺ0|Q`78HM ssԇuSӮ>rnDž˭q|oͧ>.87y n4NykAW؉y!:DR\% 51B`h}Z" D},1CkAWƭ\Ԣ!=|mځ%Kj^H|ҭ`N} c̆.t+ayq,YUSvO]+Ըr^? ׆YUh q.s)9sl}FgK ǖ;/Ӭ_e[bL0ƭ%iu"x?_y+O׼}ɧjԩQvqޛQ0ƍ3cc"3ϊIU Wϋq\~jeV{hβeˉ~~Ҽy <'' sk*#ON xt2bك=r^<@ ,\t]ZxB?c%9rP^\ƀ֡;cƎ}dEsjש]K!=z O+  ~s$1_5p o\i0猏s@B!"%7P8LڼW#G7%|]16\nNJMR6*V2… 7Ď?kޠ+D׼ ka6bbLVG SjV!`5!gjZ+bl8(thIŸHPߨs1/WIkGÅZTKP&kޖ,'l-I |dG࿢ /pOv!k X /#WmE'٭e_jԨ!5,[Rykn$0@2@Z[<feQ ئ! jo؆]'7x:16*wF^+&|pF_6|tS^S%b~#v%=S~ $NciGĘHLܴGk3P߆EiPy jא_6|ےvC`n_z))j/mrUdnwLd V F5w*VmUP1(ߔ{Py j\k/p_v^kv_~C`i֟ijΣon"P}y7r51њ9D9/A^W^{6z 5/!0>.ʓ'gpgūf -ؽZ1 2TSRRRg6-Zf/:rvWUV-1jժ~OqL9VqH\ hhhhhhhoxGCCC..d-Qv1@sZ!T@7ߌ"ᔂdv >/oB>^7Ԣr^Z45 _;d]=pө/Uzj5kCgɋmCYƭ{\EA?bnq5 _;D2#KԊf 5 9Tښj~^}x~:QKP'_t uy5]c+t+NS`ʗ Z 8m abxϢW6ہ{w .}J%X[DVqH\ hhhhhhhoxGCCC..d-Qv]x](nUb_ʕ+8;*]Z=>u]w~'ǣQb%~@kԣ]b u1FTju%ypL8I~_|<@ ,\t]Zxգ7ReddUE|{vkT yb~LLN(E ~s$s`F7D{h(Tol/1~ոs_q\h?uI@-yiv7Ągč|{  bǟS^{ovk^LXԆnS:>^j4mf<@mxs5y3ıstLذ[%CmR1Vv]jQ9/A-x ׆x[Ԯⅷ-kRCCZ{(9fQq, w1asR1:*j\9/A|myk" \һiaޤόs=/"(d!E< "3C=~ ܃CWPyz]*PW 7q;P1K_ tQbn_9~}v/Uxqq,_c;z!ys MSX'D lزuJ*3}u[K'I̜5[-B$9? i8~nԊ8?خ?5~8v.gjZ-]OىW vNNhn4NykAW؉y!|k~4ǘbzmf ؠCO1!0!`ۇMVE?;w&5Ԣ!=|mځ%Km6cA1ƭ)P}I!s̸^֠٫Ĝfjc55Ըז_6|.`5Ny!0` u"@/pF]A}:uǠAb?͊-+W"Oh [<{:-xݾ&YYY_'ƀw5CACCCCCC+>/pOv!k ן*s-I EPN>#!%J| ﯃Z:78Ϸc~M-B$9? =n(.R8~%ć LPz rDNŶͪk*ݔ̏itF ߜg:b tSp~\v /;/7XÍ)=|mځ7 ;5/q7+6\?q<x ذC/1ͧR*+U4Sw]."Sjov 5Ԣ!=|mځ%K%Wmy?hF6Q^Y;Ku[.uJ{@Ga4DJbN{& j\kq-=|m/C^W^{6z 5/!PcO}䆏)ƅd_|֬]g{/.b_jժm*T(':c1L1F_\U;nG^b^fqH\ hhhhhhhoxGCCC..d-Qv<78n/CHsIحd;v9 B$9? =̀عJ8CR['IwW3rYzp⢪ݼNn4 bǟS^{ovk^ϪC充 z/XyvE ռyUÎԖ܀Zjא_6|ےvM9lu41{\РOїbn$J > ]gu555Ըז_6|7cfpWϙL4 h]:D2~ ܃CWPyz\^0vx16X?ctrUA1)EbHzn~ !Y kF1^ǟ{|t/o7ddd!>Z!ys MSX'D Dⳋ_qd~OU`A1ywE]RG lJl2d+B$9? 9pC/6fՇ?P%KmZy+'i.Qaί F_6|tӪ7+T$>| 7#b(Xb⹈F\Ԁ6܄Zjא_6|ےvMF}60=>1JB7u+pTӮVA3fk7cf<(!\N}4TT uJ$23iOqkwL.y+P'ZC^W^{6z 5/ׅ ڵ1r'о3RYWoYrʫ&cO??@PV-~@kM~r5!о7GQu;q&7E7CACCCCCC+>/pOv!kTűŋE=bPZC-x ׆x[Ԯpaφ 51^G[nFO;.;Gll{ϒmbEKg>.PZC|myk\gϙaN:`mԧk^ HWT af3 UjbQ[Ղo-ov: Z9s1^ELw_|R|QbQ-V`Aq,mwׂ?Ƀ  hhhhE?م%j`ߎ'R{>%D+ǒ6:٭@d2=c@?G@u>V6VfT=V |;yxZ~hN-vkl}{js}xJ#c lC)VgB#%ECux w@  ?ykAW؉ycE"TR^ѳTA6zOXF,?\w i7oছSùbY ýϜG Rk| ԢP&s/p_vm^] ,?u&oʽj5um3D zxݠ٫q(Џyóg*#_~]7ء-s ^Nqs/p_v~I]T}’>\ wF^?^gN/_x+O׼?ϨzD͞3WM8I[Aau/Q1N??WNV|PM1SM49pݟr1ukbLBqN֬YS?A.SF</E˖ju곋_}3ݧo['@Lb5yV1lr- +7G7CACCCCCC+>/pOv!k׮bŊ'ݺuo^ʕKMOO{Z ZkmvIu !u47[ɫ7n.ܨ6\̩ ru{dyڣn#ge+\J\?/7:/VܷMm>xp>x L -m#}vBZ_Fz1t>U̓ f?ZYfi;Sߣ., >| _;]a'P_TBEɕD ׽x zX֨kF1|LԴܪ@f1OhlT͏5okQoS×l$Ԣ _;6ԮѼ]*d^f-];:%1h7_Cނ^/8/v[U4ƾ1 ]o@?//Xz7+RLֻq!=|m]$1>#G+Ux /D/`m˼Wf͞fΚ-<=cb['}| OD_3e*33S<\F}٭@ eV7j:S ~sAl(cIJiTZzj{Zһb;Ntݢْ.V}+.B(--ߎN~]7r~88vcax50yt1M񺡋60xV ߚ#{I8_BU%Wc~?Png_6|tp1 łvI@xIjusf5~蟌P'Ď~kG,A}2/ޥ$߯7 م-XH7{N^%_|7~B t G׮BZjȐ{ef7& :kU*!RbA|.C&ME?A5:cيb{f/caw{:M|-<@ ,\t]Zx7XZ7sb 9pEW7oHs 7bn4t|kM`:f#/8RRr1ѢKk4mMl/RY)}%X`nk儂EKm,,D_3?Z,C$5# W"9;q1O޸xN ;ogŘH[׌nhHư7_6|jXNt Fa}? im6[>TK\g=+pseVl35ȢŶI̒vBA/Sk7)d(tѢ`5~Z(ך_6|ܪ]g<׈CB)E)B /%,5-wOO(XDي8 ;cH2RC25yn8T תCMq!9/p_v:M8cIB9Pow6l'`:N?yaJNP_0;`@1ְa&?^z}7x3OBΝ[TJJ?aOM2Ey"EGl Ι;oاP _Gzц]F+.] -Z?*]Zl,=== k֮4z=:;@okW|4444M_`۟B50o-?T^]5;?7m U_7D $3~sAt(0<+j %m;ӑ})DJS.f>n$/Q|8)X 2[} g,! 4C?p~ωYĘhjNmF| 8?k`Wvk`,p"ߦf.Ld+_zWnQ+Ga0"E|:/9a ΡP%b1(י_6|ܨ]-jҥvۛHO%ĿłiNdخ39)Ɛd `w>/b ?q j\p_^{IG]$u A#^ 7w Dgs_vtىuWNc 5ke+] }9-[}_,f͞#njab\(oBRcF:w]N(_B Ht21C:wsڴm+|91 04`_C[n]}pooCM|-<@ ,\t]ZxM3ɔSE_2c,79?[*W] ew)jG7*^JsZjZnp!Q p+RFbۆwIҍ25nR(J74Ӈ`dF 2u>1&k| ).7eϿ!Ɓ/p_v vha'浳d@p ߞ.zEn!:P X)0~: JW!9y _\([Q<rr5ꪶG{l \so>8DZt/p_vm]&C&Ɠn"=D직O]"=@Z Ge+>f`+/ן_6|t@IB(爅omexѪݢA ~N ܃..ܠ>;N[N1p)_O>B^잓6sߒqlݶ]{/>BwE6,Tcо3s+7|}oۮʕ+]5BƛonD?C;ts:v$ !0v#F2}k9;s TMWűkRbVM|-<@ ,\t]Zxu`y GTɒ%E_`da.㟪\5˙q羪>xMm|S4,qV(Hl3c }ЇݚK;'0njUqn.a5\=UuZvE|{%xp5X68@ 6؅$RO>3RnRNaUQn~K&BUUŤ{6!ƀs/p_v vha'2e۳*OBb[V*5j!{nskǒ1,'0!Z·m6hJ1&QVHߢ~uy"Mw{tY| t>M5okF,!٫Pn|yk-^떳TNvv_A{Ʒ]6G+SAfV~"R`^ !|jx0FV쾔7}f>c@$b j\p _{^{IG]$u A#^k/φ]u"s/pFPH81 w $FA=|?H2es|Ū+WU^]5ZXZ}@;EE9*}6lƍAzfslUQ 9WωcC?K-Z.HլYsj@0(?#u6b.C.]E?APtDPvmf)0^3ۯu{,|C<.{#7l$<}F1 ЛZ!ys MSX'D by +qS_H&Mżbda.}ks8Av򫎃ǫ/+(0aGT <r#Zv7aj@7 2NC'%]s[hA(0(uI"@ vA8jŃ*CߞF`v Æ mi1.VŷcF!| 8?߼k`Wvk^BRr~ͧjȂ joITKPP`%bV#I)J 7 kرW˜Iq|v@KomY+)U*5o @ Ѥ|4"Ԣ|}yk-+\q+ uaaKQ+5Me&Ra$:(ox_CM>1 0$Z j\p_w^{IG]$u A#^lggCDd:Y\k](]$A}V$==]]G1F?lrb? E+vɂ_~+HtYt舘όzQoՅ VWvcmEB Q鳯mǟjm*bU޴]?C`lfϙ+#<?ܹsΝo~{9 haسWoOh-T 4Pgo 'T [:~1|<c@{Z!ys MSX'D Ă޼σqٶ~1oح%!s B*Ԫ/K3 ?;ib|8FH)k#%%Џ }>zVv1B7чу2h)UmW1uLOOWN@#w4uuiw/4ЌD. s^WzOl#=玏q|.\8pyk5D׼f;C~ ݌'n#Ɔk/b\u6Y웓^n-z c?j3`!P躱|XQ!ߞx/p_vm]L_&7/>zZwc164YAb^è%d.r#8)Mظ[1C$Z j\p_w^{IG]$u A#^/lu"/pFPH85S+W>rLl=5[{NMnVHW<|E@K}˔ӿ[b`9hذlيb!0z_x)*U*dt`vv˕b6Dشmb.ÀE?A5:]bŊm?'L$8c@okW|4444M_`۟B50oo~xsoe$D $3~sAh=ϣYrqvk߬H2Q `Jt5}{/,R#A+~o0D~lf CamtS);횭9C8h@@'7+ԗjcf^  tC, gBׄ|ԤK1)EK[㲡UŘPƯB< _6|jXNtk~tkc1YӮWccWv1>A׍l~~_v(\&bjء9vpcV6vX7Qpk : 1?OPU"'rC3N\Qm:q㓁9SrZhk/p_vmkW \>!=O|\4[rmP}AI"? afVm y{؎y?*o캇ޓϫSBը qQ&;k‘[Hn`Ae e7(g{}z`(UPԉy/pFPH8Ol*WoA_ҥKW׎sRZ5oUVU#TVA1BĨ ֭uݧQF 1bJq[/7!0:_|uI-ZTv UJAw)5:lz'@=~WT޼n;Z)))n0do/Tό[jeYb惏>+D?^45~n YK<O6o* BX>?N[KB207oױ7Q.V- &F"|Ln0ҕǫ7iXAVb['?t@ uBhخ!5wor`ɋ>٩TQg%b kc>&u}'Ħqy?ok`Wvk^CIf]OcbתZ#owuUDլ4dmj"n(\oJnXCDdwϒmy2 $ ? ]u[Ɠۡg ̪TF`)浂ZyE=|mځ])H0Á:oD ![ \{j ]:*)N棿ʗj5P&~kEin x29-B]Eс>P|?P':u"`m 4srގ:y /WTFo9j֬/b|$((33SmxUdɛh\7 3c܆]W){ O@ sF# ![ƬfZ'/zx!:CACCCCCC+>/pOv!k}KF< , ‘/n-Ɍ#\`7JU*CCֳ߈qN!+5Ml@1[f{9@aj1c aL_vcAz]C2DW%NԆW :q#gΊ턫NjŜn}F_6|jXNtK=eY+g['8aա,onB-yo]GLW- {S㟉>!ة׶o0?H5jz.-=CUoJz_\Ú#jTh~]<4` o;w%UVU?oXs_zm1E}_Śu& ;S ݛ3iS]ϿG`z ߷W_-^7)cϘ)5z򜤢T m9 4ȺuI4[:^ hhhhhhh ,\t&d K%+?ZÏ>%k5`:>V"%! u1Q?,=x~rK+@n)G ;-S۾~MHV=7 ؼMb:-ڋ0T0DNAUZl'V|̊bBn@d ]-o؉qw!ӄ]W*m} sh*]L{b;tcƯGOw"5Q#_3Cus`~3Uj"Ʀ96z: 4oJ\UPQKԢC-N|xsMWJp(S\k6~r B;e+/̋H(_PHjBy>gbn;x=P5nr/>warg4kc6eǘ:ߓ`n ԧk\ |,.~O[%,}؝ٳpXÀX)6}US F\eUbEW\K eVj?(Їޗ|S7JY GH tR10kMF3V -/444444oGCCC.S]Z'Oa¢K8f7t#f.lsoB-v\c#w6[` Bl뷻,-mc^*l\wvha'ƝI1Dr7s 5pbρjapPƉSM-p A-?Ԣ_>7| tծCgc*@a|q8j/(a z3 8umz )p@'q{d?LP5nr/>wa\:!}xj/q:!h=/FP:/A}:]F?븷}_ZO?(;;'& 9v3~ǟcb?K_MO%tK_ o± l&pj곋_}%8W\%uzޱHlVtXPvkF@s_  ->45~dYXFiQ}rAhvs@Hg=oM[*Ν눉.t3]a ծ_mbaԪNvj׫߈mueSI DR191Qx#om 'X{oc cSN!(+[WTNĸ-i,c b 8|nA8բmbLջElۙ\(,EEmO6~)UcoDEZ4< . 9hZ}5_B@z0ҵ=ܭ{*3+Kڦm6aq ?^Ap#" k‡_[yqNDO .kPq?UNNP(ϿOls޽؝mEkXM%?؏gDT@Zn#ms]X8)B b,7!0a ~AAC hhhhEwj YKTyN0&Cǎb %! "˫]O;R.z3_n#&0M߶OSyBA5@X(v7*V@8Τ+v4ZedcƂ[{/bܰBQ? />wvha':ƥbLҠ]3CCmbI&&?  h8y ;7o.r:;SqH*׬J)Iќb?V%{OcK@xT5#d~&Ol&q7 ;0 "pʝj؜5޿@w_Z8NP'u"'yaJ5O#GExoRJb_â4vm@aD|_&§mPFM?zZ7D $ ZƏ*QV&O}I(QBeuϽ*A`d FXIUŊ=)nswƟyC hhhhEwj YKTyWߨ"EIlD 37shϋ⼙ވZY8rWEX-05@q⽁9)4Dr~Zࡣ)V} ed8k1q8B4?O8ɍSĸaG‚taHq"شo+i17`G iy 8|nA8բ3ۺ -wJefeshj֥&4|,SĿYk?/ԢC-N/>wn:jׁSLkyS׉`@w>AcfA;^J c*]`]j#sFA ݱǩDN= 8=B 5a:C:RGcwNu]'LZү_?2kl7,;}wU/I@ mcytJxИ|?/444444oGCCC.S]iٲU( :LTy4hqN8zҷ}/ \wn .'Fϻ} 'B]Zl'4v LM$Ʒэ0?[w#I>.,9m㢏Uc*V-[5p{  =)v_$NaB$uEu6oí˷َ_7&S$@`/_uUU\E1U5Ԓ'ĶX|^xs'D׸;Re_ Q0rƭ94eU돼%]LM:f"YuB- asᦣvZпR@w)(1V~]i_o&ۤԸ _>7|t?ppC'o|Dg2C s](]&T\LG~%KEX؝xƌuծ[A"?go*A7jվz?)Obg}NlJ@H$>uH@R MSXNMv!kaRn]qvRӑ6z]P{7| jxNtm?ktݲp8VfbX"TnĿH@Ԣ6=/>wn~׮+<+2p߾P  0ָ؇-7<&]ƅdsFD`^L:ӍY]gB~s](]A}}Ř oPEa , y|~/?-gzQ/˕+'ut b3ۤF` /444444oGCCC.S]Z KO??UZuqBxh W/IDAT1m=T EfjUH ~ 8>M,X5oQTv9sma*#|z?PcNn q~"?(5c4dvs/>wvװ]~MS7AZFϟ2vZl JW~E! ߵ|P{ ~,&@iSv}scдb\<ߪUAbt O @!05wxX9QՖ~|e2B,>s](]A}?ˋqIJԏ?*$uv\sb #ݺw}S y UhQ_1o#Ʊz6!0Ds_  ->45~d&nR۲+K2?D 7sAAmPxtn؅UBB눉ܱ8ӪEXj Y7;v!TnΧmyLlc@ZIOn~~I% ΋ryCq2gU~9Z@#^AppEn"-qK 7~N2\ղ~1MS0;^d,og.V\*LǨE! ߵ+@UDDX8&@w G5Sܠq)׊nq؏=@oHTj\HF/>wa߯d mjν6i^cv$?Ns](]A}%/߷_Qblra7Vv|׏DL_\ZgHNDT@o~7$~ZcY@z~Ʌ@Xy :^ hhhhhhh ,\t&0qj?.|DVZ-n.錿G\PPIo^hCfFdyh3B xL^n6rA9UϊnmOU22D'majkV ωZcq5૎Sj7n>_>7| jQa'5f\۴ Y+Źi|"4]`FfaU4xD$HQїп}M.Tlc7h?NԢ | ߵ+@H"5mq8&>3bAL9%E;Ǐ*7ޒL?O6ϭ.q! 8|nQט۵Wo+ҼMb}'ԉۼ`n 7O׸iF.]D^{M1.RRz +:??/omO͚7WL ~ޒ@71p8l? 0hDw} sJ*%9qqV炿郿Z7|⣡ij ݩ9l:t(N<:KAϿ}3GqѣLjɮ|?|/7>7sAA4CM4z4?.ӶӟN )'8&Bio*V-sNۧ[ i֥IB˟8klWDt ,V7)ptN{vZ˖TN?= 8|nA8բ^Nt۶p1`9 ZmZy .㚨 -b,ݢT R}uƨ[D_SE u[ߌV4HԢL\ ߵ]o^CDDp;~L$,W7J@n;T&o~ nԸ {_>7|(@ )}u.V:-1i^3ԉ׼`n 7O׸~ }_E7(K/Y*awN+.K_} *~}t45~n`e~3✑}m=qV>uNwbHD^-.W Lp,&s wS-8KZ.H(%®WQ}'u]*S\7Ky1s^AppE~b,yxvʫ]O7SVj'vR-tx}bl!PB@Ԣ ߵ+@H[&զA`A}Q._Iӫh&5.>sF]+A`to_(1z u"@3 8=B 5ئfǟU2eĘFsb.Jժ㗊?WΩ ':wۥF@ a炿郿Z7|⣡ij f.(jM4 erg,wJOZZc"=FM}cé+t: V !{GDw{_4q*U٣@:|D%ʔg'pajϊ >\ ]-5D׸v OC+資3VmLHaCU l䱗 ըDNwI;A7A^Ex< ߵ+@ȠiKED8e8& hpx@oP 9 ;0B`Z qL D9@1 8=toԩ#'e˖U'M,߷_Gz-A`b!PT լYxs%TŊUbŶLtA`dV|.>{~'>/p ,o悂rK猸[EmpX_PO&{c"m }cé+tҼ}22 /_p(ڿfQ@5pM7"EMbB.qB .5ju pB />wvװ]v|,2rf@ ']~S` GUcVPͺ1>{6&[R1ݱ#@;P&7>swjWZUM$??&hX@wP5nj/>waߵFFvsMP'u"$ sP:]ZmڼEo\ֳg/19pV"I$L,j).'ORA<'!0}C hhhhE7@2 -UF3r ohq?62p׉B ־q\ao9xR*}j5j)GȪ_Z$W=n5c-(W8h(Ǩij3bL?s`Wz ;1.}qLv *ջ ~LV0<#UE?;Ͻ-^7eddmg."0J:QC-< [j׻>1>'XuxQCZ @!0w-jW6A@hu" CW(qoUb]4.)Ƣ~Mw+$&K5֋~hѢ+!02 +>uH@R MSX\7sAAvO7vFsfV=c"ͺ}cé+t.؏Us6 0\R^FTF}@u4ZbE_FNOVj# ݼ7Ç>wvڅo]AC sU"\Y+kOM*H}(PǪu.51;?E j_>7| U[KM$ ~DXИ;qX%Un#UB8=ԅˊC$3+K5S9Xfk#C+hP&?>sF]+A`tv׌ԉgu" CW(q E3[NV\-ZD"QC`b!PT |Eh222ԁCXE 3w+?~T XWմY3WJ\*W,c"]BJu_  ->45~n`e~3dӗvwqntpO֮ת8&iX7VvBG`כo{bHT }D}(U.z6D'>~R5K*TH\*]8xD> ~xsvkUkEt5u^7k٫& >tàQLp j?M}|xs. nUo"10C7oSYae7H_*䉟mgUF-Ո?4ExXu>yo&mxH˪Hv1uv^teD;D j?M_|ys -9;Bk%MNn 1)GԉJ{_̍BtQ߇iE'ucdffO?(zs!0_*Zjvrrr'c*F-p6aEʏJw?v{_L LzGC~MUt]k~N>{~'>/p ,o悂mV3BxD[Eq\&zwbyDrM߾P]0;nTvz;G*.wpN7bdE.~F(l a 8|nA8բ^Nt{m'BJ222!3+˨e}0pci|%hdES#^Appvը=]Mc"atcĂ;ft 8eخյՇ^ tkдb_VCgxi1jWq#C~ @WÄR!3c(@ubd!# 8=t7:D~vɒ%շ}/GrY=իU"ω j3DH7iN}El}=f 6B ~N>{~'>/p ,o悂Z$ichS6=*tױE'v L bK6 @~.H%7>"aզP#-UԂ~njsoc/cWTo؊En% ?_s`Wz ;5.}NH^CDteZPHa}6‹/)V[;o#C`0;ݵF:甿郿Z7|⣡ij f.(ȩ✑:-ڋh8.USfs?тȴ- @~.H%ͺ #aU?CX?f1h%*\3\ 8|nA8բ^NtIb,RuG7] L222Eo_?\ *#7>ZT/Ԣ_>7| ]; #".&R]1J;yx- ڄZ…Ւ'6^؇uqB< @[BaZEi|1ZƢBAG^Ap07z 5n$?"͚7o*QB֕Z/^%X!06ouv3[=1MP=8>;7%~Gˎ>wKG @`t速S:^ hhhhhhh ,\t.󛹠  N']PD5Dhovv͏9`^M?R}JvZlgH@2r&3E/4?qP?gs`Wz ;5+X$LAiA[u8?nP_ԸU 88b/_!PBPZ4| ߵkI X5}E1.n%:pCGUB~Lă]>LA!qA< @H|{+oC@e-):Qԉ$~sP:]YlؗDGlC-^"#Ą />wvװ]:}^/蟮*׬#ΏRe+>yq mTQ[6aD&Ʉ$Fї V5;\'"QzZ4y ;7k|U)lDDHv8&@wb pz`P/ '2}*VBl}w%c;Ը 8|n_ND!vVA`.wZeu6:Nu"O CW(q\+eј1cE+ dffO?(#Ą P@UժU;n5j1@yX.>uH@R MSX\7sAAkySt6NOc1SB6Hl;CW^Ftc RȒ^8q S{Z7iZY'3?!~ZLs}[OR=}t Bz/,}1jc}XA_>7| jQa'u?]g?? yFx s䍏Ͻ-;ľL7 ȿ.0FBoHPzZ49 ;7kYC)xOqvc1!НXBcfAIdY+&ZDwmAxb?5ns/>waW'ΊMtoCV=Mqs!^wrIe.,3Cv6Ng5zu7!_̍BtdabXV'}roӫWo7^A P@@И|?/444444oGCCC.?\ i^7RlۛuzmxLo?7mtvsU$;G istc_ Rn+ƶ7M4SId ծg-FwҰ}W[B3xu~l\̇i+!%tܬKz%uhs/>wn~׮[oQO bB;,Qk~S).70p&8|?++|;u5ns/>wa%p{ ۼۛrrK1LHBת4mx͎!^I.Yu༱]5=g:=ԉf/F]tur)?-[ExRї8';ctt @ -JVXh Y7 ]-5DCg㙖;%^Q(#C̅ɺS xÀ^LEC-| mYtc_'_"XLt%*A.bl^|w@lU۾>!*|?u5ns/>wa6BWaY=҃LqLvG} ,Ӄn8O`x&L}3Ŋ4u{!_̍BtMF>I D_~:/ 06&clկ_ȣ{~*˔03)?~@ $ :^ hhhhhhh ,\t.@r*]L;nN7tC?v LMFN'zJ8?B^n"]`: DTtc Rgv9(HQO+];<-[ntn{ gS&Ex8-4E<̝OL.4ܪ^ׄ_>7| jxNtFm$FLA7y0e5 }(y TFfa㚙S2FϟjQH|ysMG}%1X!Fwq#=y?+pAIG%j\ 5ns/>wa&pW{i|Dm$kBv F !Fg(x'})}Bpiz()D u" CW(qٹ;'O±Dh1:' A`A:oA'X15p`ʹWEd@ 0\!}ACCCCCCKO|4444M_` Xs5D;Ӵ-Bвc GbX-07qN!c nYwk}b\Rj 79ǂT ZQ&붸^G edŏg-7rN VzM/ N46 Ÿ&,4?s`Wvsm:1 /{B}t)Ԣ{}( 2sGnkN6mR*}Z~8h8@-*M>|ysMGתdbLB1o=F: cB;Lw eʉqrM}68c!K4Ըj_>7|(OUW*:w6Nloתد ?‡_"q:?$E{^mg.m}N[%ԉf/F]tƍWJ%7Q/{>§q9A`l~F(Q|F\Μ;5n,cZrl^  :^ hhhhhhh ,\t. ۟tU<_n-ceԪpVk5j!o>[ |NKix.xӊn>_}u],tָ~!2-73LHXh?~Nys'D4Q m著gNh!cbbLV*7B ~)FrURW.sjQ hs/>wnjNCNJ1M=FNupz  02wn)Ѳ窎Gcswf]1rrK Ͽ'M4Ըj_>7|(@z(]a1DZ #mze`I D2h21n$NWhNq1ib;+Tۛ6}Ww u:Œ_y+N׸6=govvo0ߪboz%s@H5H|,@0 +  ps_  ->45~n`e~3DF!a(*!M(a}ψmbeE;~)SMbW5nh۸|1i7DTtcs R&ƴjs&Vnt#OiSEx`_>7| jxNtŚ#XFpm?3'-b?eӣܛ2oHKns䦉E_˦{=v%0R]hw-J(H!0:Ԣj_>7| tծs{Zi,\X-wJl'z =XC;Lw ~ 5fXnڨB߉m!3W킀WB: @9}Lu Q/T1C &~)Yv$WZ(1%{ Mt7nŘ>y@(N0W^Ap07z 5n,C[s=5Cb<uNno !_*?!'t炿郿Z7|⣡ij f.ࡣ*#8nq/_1%6n-0{d_z=[, bM_OUN76dz )Kwvha'%Nηb?k-*o*qv]fxk7 )ν~Nb,"NK&mxP%v^]GXǮG:ޛZ49 ;7Ӄ:(̏DBf#g$t%"Њ.; =Ǩiׂ_ E?~gvNj\ 5ns/>waظCn[jœgſ[}Pd7Bk%a E4rG =g}FHuf1M6m+Ɗĩvnw͢%ԉf/F]tƍU#]s͵OiS^WX~ 01cdɒKE^}ַM#G'$C`"233ű^<~Bll^  :^ hhhhhhh ,\t.=-C/(ZZ-?S^zj/vn-0%9%  >F~1NF+ZXAwIMUN76{] PI=vn uSD_9TtZp2jV?Xh aA9 ;Z4ް]Zv 6by?+ľL#l-n6g+)h^+s`1|ޏ PP ]ed0m11C-*M>|ysMgٿA|lwz|xg۾ž궸^ZS;u14m9y{>cEwiؾ0hx}֮8WB: @9Ƃj·'b&PZI^-=X/\T3w>ziUfVT8q}|[ӘE/P'J!_̍Bt}l&,ZD㧠 穃T^^^RhQo.|*QEjժ֭ߠ?Ed>8n&٤CuՒ˴5u%KjРAg޴3D?B"C hhhhE7@2 =n}dH"C5yTtc=GOcq :*sCG -Jc\\7M,4 ~ S-k\?ta)c҆v^ 66XllsLu2F+YZ ~5-t]-16%@B!8:~s<FZTB-| 5&1UF-o638RHcGIr&c4 պ-~8eoPJqS[^Apxh<4)i@@Grt/7XwR͹;aY[>]8OAqL\뚉LkySu:Œ_y+N׸9pFPOAW] &d0*vֽ؏);;;}]!Wν*˯$tL +^ht6xjp6D-*M>|ys-k6V·sb;7(dblҶpOF-kДM?nٿD<V]O y{L[Z.]} 57u ;0شso#,ƨݤxN@}`,b #9t~Z(*[m9y/%{:QBa߯`nJk\7V^z9A`~ԃ=ʕ+'G$ !b=Z5iT}8a- Ds?tcM<@roS^AppVzk\#>8 A¨Vtm0n.0n-ۆuOTf #1 g?@)G>~R\wU7Q47?GQJEC^AppKD7Uv10L6 }V]\8׋9†u}EJ\[\כom;sѸ%<|AC+M|nys l^wн44`"_+z9zGt~ځ3uCf u:Œ_y+N׸n|lq ׹:۩7Q{y[)cŪ\Va. ڦ΁u&A7Ϲ~7-{`8F5㇝Ӎv RI>o|:)ąe1jtIsExa_>7| bE5.ת-~"бjʝF]/wy鳈OV{P>Ft=@p\ꄮ'>weN}J^)SA1J3Ԣj_>7| Uɍ>nn5k#<^cK=—U.*R:Р]']W|UUr!?e6laKh{h'ԸRs/>waCm4/` z(!cem:iGw|V)o^`|WM7Dtd15$]k&ocyWyP'J!_̍BtV~~U~7߂:' [Gz#~hZlC1[x?CǎjcU| !nmj׮蟌˧N-Z~8^|UR1cǩ77,~brʉ1̘9SÉ/444444oGCCC.?\6B7yQPhhaX'{׫Lt~|4l8.~~h2 fc10s9҂T;xs9pu-B+uVeμ9W&a@Bs/>w֢nNM1sTGQ6"T#GXy5n.3|՚ݢEt)^3QɏÄ@bѴ7D@`tE! ["kW -VP|?Lafmک藎co7~a)cUx~bwaӦuBr|5Lrc]+T5PAӗyϽ;?.a?^,S^'tI|P'J!_̍Bt+^-ZDlCPu˯ԸTff? VK-5§KW_-FJԝR' m5?_Cc^:J6a|'εN\L :Lh:w>ϋ?@ 7tP&V%J(Ч71?b֬b{~'>/p ,o~XϭߊdS6<$7 L'oc Ʃ]֡бb^Q?̜nl Rϲ(3c~W:3?!-ZžM $,4?suC׸Nf>)-  QسWZ-q^ժk+n?ZcPbLͻI:miH?PB"9 ;D׮w{_jROUTKn}N=_Bm=ʫ]Osy{)hNqq\^Q|$DB+M|nys LX.޳NO_&Xu9fVbpGj>6g ]6Cݶ~qm|dR4ԉD3~sP:]zѲe+q& i4A&ƥ/XĢnݺĘ~O?o ǪZ?Qb={aBh%KmZ|&z-ZTl;9quϘ 018)RZ~o^sE[^=u3bƛQ:vS D?7Ǝw$É/444444oGCCC.?\͖l%@yI/n.Dr22 aWDA%ZuEbatcs,Ѡ]-G|xt ?X1)@/C6YX,4 ~ Z4VƍfՁjnPsB Gm:=ZY{8.Z.wZeF=',C`tE! [+}/bڿ.I*C "R])^ps$ԩsʕ+ M4kV]x7!^"//O=o8XQ`-"j׮癿郿Z7|⣡ij f.Q4m#γW%˔W#W;]LYd,\۹]<X̛b?@7F9ոt㇕Ӎͱ0Wl{ȺhNO(R "*~m$-b:/|xa_>7| Ԣ5n,((yʝ\ݢP[n gm߮pbU_u\,[b+^*V]O;m 38PB"9 ; k5j%-1F-۪vb}oۥJ`!c{ϳH1Q|%*v=q :7v꥖;%I'Ըj_>7|¤~=.f|JlkT>!3+Km|bqt8(`ow}w5x2hϋ6?pb1U/M+T'Ns: |_̍BtI+!O<$9Ao-5U@JΜ&?#[:wο[7oD5x& lkXoS׮S˗ |\(dn;= uk~/؉2ܢNkm5nԨFg>}kS}!9C hhhhE7@2 1t.?ho,z31,0S =|ejOĸAXU=]R߆9ㅝӍ'o|DsCr 2xr1r:AReľh߶V=q:$;-4P!Z*ޅkXh?~NysA"ʹuC׸n1ܶG۴q;*V%~Xf9iG =6g_$T?@lhODzB- ~s/>wna]~Cu6fm/E)$Ͷ3?u[\=#\V'qyyx'\ЃT( &{[\Ik!N( ՘;`Lz,~kw{q%Ը- 8|n =kE6Y̟v kX5ClFve+WaW#i{>%*{9=|7*-e=q"z`&;^%ԉf/F +U8yUn׫yX~7Ws_HyoaEdr哪Wގ *{_@STVűƂ_?DG˿XլYK;.HJHXJ,~bHtruUW}Ţ~KŸF[mWժU`F"[Ѩѩsg#ވ9ij^O:MxPVfjX[疿郿Z7|⣡ij f._YSf]j7+YZlx28Q-NGO==}ͪטEz&F/tS7K {X~Rl,nlv ɨ7m@ LY>؎qAfv:HmV;K-[g;Oa3l~8ZN twnXh?~Nys 6Xw\jLtmPQKUzmUM'#F>-wv(؂ڀRXV\Bz·[`t`aD3pCG8iq16 jQC^Ap=e}jw;TyVPVm7ǯއndDC=]C>Vo]i\ = n6x0ѬgԪڧ4fAa_*NS N? =dk;c|oKX9]<(hNqYƷ]~}yA{[ Gz_е" {f~~B+M|nys L{K0 ߨ6&W)3†I {b_;w?}Uz9L#7(iB(N0W^Ap07ٻ(K6  IQT 4"*QEug!Mc#D'u5߷ު>YYEW}_ּ7{` u2G *~ӟuwڦ=寸8 :Úxfs5꒵=g> kq䑝8i ޭ? -c,o\t%~cT>7{ UrUkewno;Bwm~;&~=쳟gmi92@|_m47rks~'?ml~'z ~2;[nv'2B!If?;$ T^\ܗ\Ӵnkܫwƛt>+OCʗ/?8K:p:k|7tF˯i'&ΟU"u\Wer_COxzV5׀rA1C,p2 qer E=6vpzI? Q~0ǝrm}9{\?d㕡!dR2:Z*)OyKk'7|o:u-ˀ5 O93w.=gڒk/ɵɵ&%`˷K~iM7k.;﷜! y:oZu|Ua}S_xۻݣߡG7} 'v}>C*_2=o U^39 Ή]Ή,g|X`m _ Ktӹ|#7;\}_n:x⫳[4/{+&àqe<,( &g?7fqj.W;o_K,ʴzrmr<7t {׼8esod- ?0YԬ_n^z:{*kX s^'N 1T 9+&sXr*o_XX woNy5\,g.gّk/ɵɵ妼<_nyoXmRy-޶|_Ua+~kC>ovfL~_w2<9S'A̗cZ٧ ˏYAawe@sH'o!7voY/6_\q{=]1 >}-;ЬcNôjfc뚔n!(5=_׾c=:k^{+.B|!?W;; _۶&/}8e\r[n|T&n~HP|,\~=|.$iK@˥}9_88Ns9>-7,ƗoP= |i:ҷw>7RN!פͥw>W[m\t>i]`k/ɵɵi`͟{֋4c>GtfYAG0#>ҬX^m[td 5Nw|1=ygnmm.(Ӗk/ɵɵu}s:[y9suCʿۯ qY|cj]v|ΫካScKf9ivAͩYNԗn]:߃_;Xθ]θ#6_ԓkkQ3˿ ^z7;:G?s}Uexֆg)u;;cw>#=r{0׿/sbs"Y>_E=Xg!}A57~ⓝ^y[};ˮǛi,\yUe I4%i u徜/b ^؜I K;gPXOJǬ^<7tF˯i'&25V/&o*^[;:7%3:?mW4sqyKxokmfik&[n]s'yiqNrNd9kk?א|!'>c΂6[/}802yuQSȺaV {yuխ?QԧԬX]_L˻Oj}m7Omq䑝k=1t~>‹^lah'K?5_g9-Î}JZKF\o4Xzrmr>gg5yv޿2 ﵟkr^y(!GMwo7a69o/xG:θ]θ#6_ԓkkK>|nIsч>kS9On-_%/xˇiʐ3/r<Q֖߿>ÛO}泝ǙE?/O>e!O~S:;-C ,o5yC;.)>w:7ۮs;7+yYQZp{z4az;|2 9֘&l|u~~u_qAs;OULûyO~M|.$iK@˥}9_88Ns9/V޵E|o-߼R=c+na >w>R>dP:oP-k- c=-ht52/of6X_dL>w12YuEa'& ^8[4i_Ouvs;oYWw@v߃8Pfhl''y*Cx:9|[ZrO)COy`|!|x<-3n3ȵ(fw_?_k6t;|/vn1nNCef-Uy|~_u# $I4Pyrr_s1N\/l^~uBUѧy|hŽ>wiZ/Ͼ Q2οwBax|W:垐:ߣ~s9׼o5Zʠnx=+oQwvzrmr`ҷŝ|?P U[2}&hNPw8'v9'5_cm R;wmݛK_sY_y\t%!V4 9UO|Yy5g?yci8ek}Sv/~y{`W\y~+Vh>1G>zc}?O|my:o;nM󶷿mw~w]w]m 76W95u≓>^7H,Sl)jo4K_<QOMn)6jf 6l.;> ~T xK>/;,ڔk/ɵɵ-8ɟ/yKƦ@QO;{20pCM^Z{޷9'7e;'? *? ʷg.gّk/ɵɵYl&ίR5)CẃGלv\OC׆r<Qֶf6 zԣj>tG?:o7v|1IU~|3/Y\{Z<䰩Мqˮl}eRolq;oy;Wo?}_{>vy{3_ԓkk{7ȝ|>c%=8 ;o;sSs%o\qL}q2#ߞqW7wr,,U'&XOg/69sG7?͹oZTgzqrƝy,Cg.G,?|u^}e9'v9'*_cm@ӟ\ӟߧq>Z7|u~P+\vo:kn65ͷuO͵xgCV> I4%i u徜/b 3_ԓkk@?yOE=6v<QV> I4%i u徜/b 3_ԓkk@?yOE=6v<QV> I4%i u徜/b 3_ԓkk@?yOE=6v<QV> I4%i u徜/b 3_ԓkk@?yOE=6v<QV> I4%i u徜/b 3_ԓkk@?yOE=6v<QV> I4%i u徜/b 3_ԓkk@?yOE=6v<QV> I4%i u徜/b 3_ԓkk@?yOE=6v<QV> I4%i u徜/b 3_ԓkk@?yOE=6v<QV> I4%i u徜/b 3_ԓkk@?yOE=6v<QV> I4%i u徜/b 3_ԓkk@?yOE=6v<QV> I4%i u徜/b 3_ԓkk@?yOE=6v<QV> I4%i u徜/b͆oy~3_ԓkk@?yOE=6v<QV> I4%i u徜/bο͙_wg_(Wy\\;U'&~|/ɵɵ<_z 0&@PHώ/I,n+|1*_ԓkk@?yOE=6v<QV> I4%i u徜/<_zrmr'Wy\\;U'&~|/6B!If?;$ T^\ܗ\<QOM*_ԓkk@?yOE=w Z\($IgǗ K7ԕr ~|/ɵɵ<_zrmr'Wy\\;UcD+ $I4Pyrr_sOE=6v<QOM*_ԓkk@?yX`L܁hs$I_*/X.PWb.U'&~|/ɵɵ<_zrmr'Wyk;|.$iK@˥}9_@?yOE=6v<QOM*_cm1qυB$~v|I`t@]/狹'Wy\\;U'&~|/ɵɵ<_z 0&@PHώ/I,n+|1*_ԓkk@?yOE=6v<QV> I4%i u徜/<_zrmr'Wy\\;U'&~|/6B!If?;$ T^\ܗ\<QOM*_ԓkk@?yOE=w Z\($IgǗ K7ԕr ~|/ɵɵ<_zrmr'Wy\\;UcD+ $I4Pyrr_sOE=6v<QOM*_ԓkk@?yX`L܁hs$I_*/X.PWb.U'&~|/ɵɵ<_zrmr'Wyk;|.$iK@˥}9_@?yOE=6v<QOM*_cm1qυB$~v|I`t@]/狹'Wy\\;U'&~|/ɵɵ<_z 0&@PHώ/I,n+|1*_ԓkk@?yOE=6v<QV> I4%i u徜/<_zrmr'Wy\\;U'&~|/6B!If?;$ T^\ܗ\<QOM*_ԓkk@?yOE=w Z\($IgǗ K7ԕr ~|/ɵɵ<_zrmr'Wy\\;UcD+ $I4Pyrr_sOE=6v<QOM*_ԓkk@?yX`L܁hs$I_*/X.PWb.U'&~|/ɵɵ<_zrmr'Wyk;|.$iK@˥}9_@?yOE=6v<QOM*_cm1qυB$~v|I`t@]/狹'Wy\\;U'&~|/ɵɵ<_z 0&@PHώ/I,n+e+_ԓktzrm<QO ӕ/ɵ#1\($IgǗ K7ԕ2ӕ/ɵ`E=6LW'Iޑ|.$iK@˥}0]y\+_ԓktzrm$HG> I4%i u tzrm<QO ӕ/ɵ`zE]>c#%$I4Pyrs) $i%ie Ka$IȎ/I,h\ %IGv|I`@+R(I8K@@Z9@IƑ_*/XʹJ44Py2VΥ0PqdǗ r.$#;$ T^ s) $i%ie Ka$IȎ/I,h\ %IGv|I`@+R(I8K@@Z9@IƑ_*/XʹJ44Py2VΥ0PqdǗ r.$#;$ T^ s) $i%ie Ka$IȎ/I,h\ %IGv|I`@+R(I8K@@Z9@IƑ_*/XʹJ44Py2VΥ0PqdǗ r.$#;$ T^ s) $i%ie Ka$IȎ/I,h\ %IGv|I`@+R(I8K@@Z9@IƑ_*/XʹJ44Py2VΥ0PqdǗ r.$#;$ T^ s) $i%ie Ka$IȎ/I,h\ %IGv|I`@+R(I8K@@Z9@IƑ_*/XʹJ44Py2VΥ0PqdǗ r.$#;$ T^ s) $i%ie Ka$IȎ/I,h\ %IGv|I`@+R(I8K@@Z9@IƑ_*/XʹJ44Py2VΥ0PqdǗ r.$#;$ T^ s) $i%ie Ka$IȎ/I,h\ %IGv|I`@+R(I8K@@Z9@IƑ_*/XʹJ44Py2VΥ0PqdǗ r.$#;$ T^ s) $i%ie Ka$IȎ/I,h\ %IGv|I`@+R(I8K@@Z9@IƑ_*/XʹJ44Py2VΥ0PqdǗ r.$#;$ T^ s) $i%ie Ka$IȎ/I,h\ %IGv|I`@+R(I8K@@Z9@IƑ_*/XʹJ44Py2VΥ0PqdǗ r.$#;$ T^ s) $i%ie 1@8IDATKa$IȎ/I,h\ %IGv|I`@+R(I8K@@Z9@IƑ_*/XʹJ44Py2VΥ0PqdǗ r.$#;$ T^ s) $i%ie Ka$IȎ/I,h\ %IGv|I`@+R(I8K@@Z9@IƑ_*/XʹJ44Py2VΥ0PqdǗ r.$#;$ T^ s) $i%ie Ka$IȎ/I,h\ %IGv|I`@+R(I8K@@Z9@IƑ_*/XʹJ44Py2VΥ0PqdǗ r.$#;$ T^ s) $i%ie Ka$IȎ/I,h\ %IGv|I;(a(zQH(ĔeP$I$I$I$I$I$I$I$ISR\|| J$I$I$I$I$I$I$I$i] pP@I$I$I$I$I$I$I$I$MKanpJ2(I$I$I$I$I$I$I$I)w)  .>@I>X%I$I$I$I$I$I$I$I4.@($I$I$I$I$I$I$I$Iܥ07%`$I$I$I$I$I$I$I$IҔ$,$I$I$I$I$I$I$I$Ir eP$I$I$I$I$I$I$I$ISR\|| J$I$I$I$I$I$I$I$i] pP@I$I$I$I$I$I$I$I$MKanpJ2(I$I$I$I$I$I$I$I)w)  .>@I>X%I$I$I$I$I$I$I$I4.@($I$I$I$I$I$I$I$Iܥ07%`$I$I$I$I$I$I$I$IҔ$,$I$I$I$I$I$I$I$Ir eP$I$I$I$I$I$I$I$ISR\|| J$I$I$I$I$I$I$I$i] pP@I$I$I$I$I$I$I$I$MKanpJ2(I$I$I$I$I$I$I$I)w)  .>@I>X%I$I$I$I$I$I$I$I4.@($I$I$I$I$I$I$I$Iܥ07%`$I$I$I$I$I$I$I$IҔ$,$I$I$I$I$I$I$I$Ir eP$I$I$I$I$I$I$I$ISR\|| J$I$I$I$I$I$I$I$i] pP@I$I$I$I$I$I$I$I$MKanpJ2(I$I$I$I$I$I$I$I)w)  .>@I>X%I$I$I$I$I$I$I$I4.@($I$I$I$I$I$I$I$Iܥ07%`$I$I$I$I$I$I$I$IҔ$,$I$I$I$I$I$I$I$Ir eP$I$I$I$I$I$I$I$ISR\|| J$I$I$I$I$I$I$I$i] pP@I$I$I$I$I$I$I$I$MKanpJ2(I$I$I$I$I$I$I$I)w)  .>@I>X%I$I$I$I$I$I$I$I4.@($I$I$I$I$I$I$I$Iܥ07%`$I$I$I$I$I$I$I$IҔ$,$I$I$I$I$I$I$I$Ir eP$I$I$I$I$I$I$I$ISR\|| J$I$I$I$I$I$I$I$i] pP@I$I$I$I$I$I$I$I$MKanpJ2(I$I$I$I$I$I$I$I)w)  .>@I>X%I$I$I$I$I$I$I$I4.@($I$I$I$I$I$I$I$Iܥ07%`$I$I$I$I$I$I$I$IҔ$,$I$I$I$I$I$I$I$Ir eP$I$I$I$I$I$I$I$ISR\|| J$I$I$I$I$I$I$I$i] pP$I$I$I$I$I$I$I$I>$,I$I$I$I$I$I$I$I$+}.>@I>X$I$I$I$I$I$I$I$IW\||$I$I$I$I$I$I$I$I%`I$I$I$I$I$I$I$I$I_s`@X``@X``@X``@X``@X``@X``@X``@X``@X``@X``@X``@X``@X``@X``@X``@X``@X``@X``@X``@X``@X``@X``@X``@X`(~$I$I$I$I$I$I$I$Iҿ%I$I$I$I$I$I$I$I$}(K$I$I$I$I$I$I$I$I xP$I$I$I$I$I$I$I$I>$,I$I$I$I$I$I$I$I$+}.>@I>X,FRئ\||@I>X,FRئ\||@I>X,FRئ\||@I>X,FRئ\||@I>X,FRئ\||@I>X,FRئ\|||I}B3* ,E.)" R\ f|K@)Kam h3>@Ebץ64"RdPRXPX`)(u)M`H,Y֦f0T$X,Jq] kS@3* ,E.)" R\ f|K@)Kam h3>@Ebץ64"RdPRXPX`)(u)M`H,Y֦f0T$X,Jq] kS@3* ,E.)" R\ f|K@)Kam h3>@Ebץ64"RdPRXPX`)(u)M`H,Y֦f0T$X,Jq] kS@3* ,E.)" R\ f|K@)Kam h3>@Ebץ64"RdPRXPX`)(u)M`H,Y֦f0T$X,Jq] kS@3* ,E.)" R\ f|K@)Kam h3>@Ebץ64"RdPRXPX`)(u)M`H,Y֦f0T$X,Jq] kS@3* ,E.)"5kV;g>ncǦ78jiUWMGNlM0aB4iR:M7ݔ^y啸>e>eʔt'C9j4r4|iwL{w򗿜@ץ64"Rd0ӷ[fX OMo/<tA N[ot'ӧ@/ԟ"Rd]uUi]vI ^+=6,M<9͜93z{g~ wOdܸqo^#>+_?3>@EbȢtmm6{}T5X#M6-͝;7J͚5+ziˎL4cAm3E3T$X,zꩴgve6J?a͕W^FWYa5\||"ٮ4lذ5 80 >:稣JNcEnls9n69"@* ,EV3\pu9#cJ]ziРAٱt>-|#ˌ?>6Č3leV[m4o޼ ">_?3>@EbȪ|0-ru[p iȐ!1uԩScn{Sm).)b-(s7."@* ,EV͝;7=ɮy|_oOC͎;kŷle>敻(sa"@* ,EVM:5ޝկ~5vYj4pK}6OllMv,E8">_?3>@EbȪ_=.馛9snKg|ͱK}CʶWd׎Mk͎?qlm/ԟ"Rd?Zw/~e1cFZs5c-曧n]2zl{EƏ{޴˦SN9%͜937U|.Pf|KUO/rN:(6ƊόPX`)giˮso|KCAe^1cF2m]l/ԟ"Rd 7ܐ]2뮻n={v+{9cbZuU>|xl /ԟ"Rd^{]2'pBlkw}:th2;vl2/bl/ԟ"Rdܹsӻ;c^muΡ̭/m̧?)>_?3>@EbȪ;#eFϟjsLveN:|N>ley'>_?3>@EbȪSN9%e=ؼ׻[([twg}Ci޼y4J|Pf|KU/cǎͮo'y7w4lذ\?+"v:gĉ魷ފ1E3T$X(4hPv} 2$͜93v|x㍱"w}_~v:gvHz /ԟ"Rdǣ>]2{glg\~9ckČ92]|wމ3E3T$Xkk[3Έ^x!;2G}tlX=\` m-(nm&E3T$Xַ]2^xalg̛7/?;";Sl%)>__?3>@EbȪw1e{ؼO0aBvNef͚we]Xcl]СCӁ4{y33E3T$X3fLvm/͙3'6S8ʼ˱yJ+eN|p<9"@* ,EV}lFٵ-+Ǧ}><yKSM6$Ww3lذtǦG}4z|">F]"Bv}ݷ{]wݕ>Ot,۝/M0!'?I͋^#>3_?3>@EbȪM74E]wشϙ6mZv^eؼǽ+LoZacNQ,Q|NPf|KUkVvmlƱisgfU+5k֬'M_~x+*>_?3>@EbȪbxm 6,6s?<y^qKСCwY`!{HDDDDDDPDDDDDDDD~X2qEDDDDD/HB.qf0T$Xz뭳k[fٱyrdT_͗b1ivʎqq93>t_CG8~""""""PDDDDI!8@3* ,EV}ٵ-}ʄ s*S,<#3L6lXv ԩSf oDDDDDD}q EDDDDDDD{;tO?\#`H,Y{d׶̽)oyvNE *3gL'tRZfecׯ_ &NFDDDDDDPDDDDDDDD'HCiZ12N f|KUGqDvm\ygTdȑi#w9;bA/v!gyjCzp5h7uHkM312N f|KUk[{^lg̟?caxNEviؼW;3skb7j1yZcvS4>/^#`H,Yque׶7ؼx)sGYgG/=9 j1yZcvS4>/^#`H,Y裏f׶̮]vYv>e>ؼO\:cX|穽Zg }?!1~48@3* ,EV}̝;7 4(EYf4cƌإO8)s 7}ƞ{Oӧ.x@S{ԃ<1~@CZch/qf0T$Xz;vlv}\~yW,j8lذ\+K?y ch>穽Zg }?!1~48@3* ,EV|-sޭޚG1c}NqMyWC =O:cPii|^8F PX`)oϮoW^9͛7/vՎ;<ʜx≱yfU͡j1yZcvS4>/^#`H,Y2gΜ+f׸mjlAve~_}ܹsӠAs+;x}@S{ԃ<1~@CZch/qf0T$X8qbv|{|0;2"gώ] 70;"FMj1yZcvS4>/^#`H,Ysue׸ȑ#ӬYb^ώ'?ؼu]+w+6ƋzګuiM@ xq4"RdӨQ\O]zGy$ 4(;2<@X{o?~|-UGydv~E]v4o޼-OyjCzp5h7uHkM312N f|KUO_ײ\f]z &d]f饗^J~x߿G1c&KՁcĦx}@S{ԃ<1~@CZch/qf0T$Xzz2,]2{gk\qv~eLm;M]v%;"kfl '@EbȪ|3ٵ믿>vYfΜYgXli޼yBJ6?>6]j6`㎱)4^|^3>Okn?gŋcd"׿W^9eF~m:餓o1vY>:FyOʎQGC =O:cPii|^8F PX`)sɮwx≱RsgXf]v]뷿m2Jz饗b:묳*3eʔ/OyjCzp5h7uHkM312N f|KUosIlIv;_bv~{:thvle |حKrl{e~[o.mQ\ 70;2ӧO]j1yZcvS4>/^#`H,Ywe]6O}*;k[\iȐ!1uΔ)Sb.ꫳuĉӼyb[o)yjCzp5h7uHkM312N f|K ӦMˮ{A.4hРX:XU{go[oUfiذaq^3>Okn?X:9t衇~~gbΝR[n%M:uS>/^#`H,Y1s̴;d?fر{K/?l1#FH>`ľe>|x~yؽGLlIΙ6mZC|^3>Okn?_>7ۧo9d~E>.]v=:}ߌBlu(S__ӟ4UdO͋'^grq4"Rd5KqӸ+7 :/M,ٳgɓ'CfJ~zemIwqGĊ>f1cƤ? =O:cPi螷z+7.Y#a~fob.o~O>:.8gΜveS' 8F PX`)7t7GI=Pbj=颋.^Cs._*ٶ6ltR /BZs5s*%8F PX`)v#<2{-,*͝~i;HwifmOt饗~8tX駟NrK: +NG ,H|^3>Okn?;ꨣEV_}g,޺4(NOWc]v 'dO,mjNaÆgP o:Udԩycı 8@3* ,EVٽ]^zӧ;9zOXoĈqt7.l)  ?}zy睳Ez뮻.V[ߦ{fԮ}k]i{j1yZcvS,~".Ϧ҅{ŋ.(ޝs.]g+J+/*r 'wy'n`έkǁ\#`H,Yn馴[f2|t9ٳgCio=;6;ƪR|FqstΜ9PE%zګuiMh3gLlIȄ bn;SʂS\voc_K~e+.Ul#HcXxƷhof5jTv~E-68'h3>@EbȢ:.䒴;,a+)nr}_O3f̈_ꮾ.dYnG}Ѹk { j1yZcvSuͬYҝwޙ>tv-;6miVKj=ztfm:4iR:M7ݔ^y啸9ɯw)S҉'9䐎jȑ#믟w1_rk?7TuQ"+bzcn;m˂*Cϙ3'v{7^|򓟌]xNSNɶ^{-n`oί̹Z<rq4"Rd0O=T׾k>O+Wz9Aeӝ~W_!yjCzp5h7uHkN[oUwR,tV,u,W\qEZwuM{xez:n7H_~y:蠃:ӝzONӧOi'ٱ9b%b w^{tl{kK/MC ɶl/KD4eGc5ϟ\#`H,Yt믿~_38>-ܲ"CZ+mfiOӗ=\\oᄏfiqCk]woXZc5Ҙ1cꫯ6`oYe]>=vm @^3>OknUW]ЀaM<9͜93m.츊ܛ;1)[Ygy&~iРAqDƍn:y;^=]O;Ws.]/|!^ĂsO1bD~wz&I y?~|l^[12N f|K@_aګuiMnfcRUXc4mڴ4wx(`i&*;Sr-g4iRbrieP8'|26_bzڹ?|e{G2K3f̈]믟cAhZ:I <\{y-&8@3* ,E} zjCzp5h7uHk_JO=T}ݳhWE~ê[MgW^ye5jTv\UgVH\sM<W,g//~16ov-%?-2v閵^;fu]76o /bl? l~鸉Fhw9;"/̙NS8^3o}7a<1~@CZ={v:c_T=txcAh̙O^zi:رc΂2nܸ8 .n _|q߿v ʨQG.Cz럶K/+G?XX`/nga)Ϋoۅɓc7|<ٱv{*[lE2oq־;ibx`v_*v/<^Te{bqe-.{&.Xg}-R,rꫯϙ\#`H,Y/ wqG&llܹz3Xzeaoc@!iM0!;eVX׿uL=SNIkve5H>hL`yݤtWvFc&Nn4*y;3ꫯmwA9s&Z~:FJvs.j)+~Ό1";2}ZYX3nsnإ̙3'>>͚5+n4p윋zꩱy%8@3* ,E} V-ܒN;tGv-mfiV˧6(?>}O'|rꪫ:7>c@?w G|=]iMƯ|ӟNZ+C6mZ>|xs`xݤtweY&g/uc>u4jԨl?Sfo}=c~={Ɏswإ[vle!3gΌ]zTq`w--vؗ4u'>윋 aEO{B<_rq4"RdWrqYƌ bM7M.?eZoqʤIq\oҍ7R3z? c=6q}wl/S|*Wk[.;|NID+j"lAl '?u:^\pAYH6}8tIi4gc-:c>דּγ믿Bcwu؛sLq.{4~]]@z衇bS,w1dr17 v-n<|4nܸlS<0wuG"#G}T=X?w\tEK?OfƧXm]wS_1cFc(uz<2K#=\QN_~ywFc@Uӎ;_v/I?o!m^z!x>eުd]vΥɓcs))g {ꋢfR~zMRl9b}K_ʎH)twSY{!f=o,^VXO<YŃC γ_}Sϋ>ެNԉǖir!_0?gΜص~߿v 3zoĮ-[Xbi,u~lsQGu\/ves9'v/mH(\of?;2{_]laϱ'?T]N;Cbb1*7+ O+Ƭ {VqM7ϕ\#`H,YܴiӲq)s'KŔ)Sc+/|!6_*؊ߪ:sP.{S}]]'/Ӕ s9#cJ]ziРAٱt>lauhov/uwG?~|l^3f,raV[w38ӳYfe:+,3^}Վ˼}]fιcJ_<[,,UUnk!'8F PX`)+_^xaض[Kń c+KG[w=6]E}տ2D1/[{2nmz!:Ch:^#7||\ve K +g { fQY5Sq 'd_dѱiqtθqbZhy7Boce99 Ty7Л}_R'W_ [.;?Mq?yȐ!1uԩSc,SnRʅ=R쿝5F`L<27|seܽ/v:b^=?qv̝|%v閉'f{,vܓO>FЮ ~ZX,RWdv?{|SC \rBe*:rJ_DR*tEtS*B7ː.J诛I9i:+ C[2?<}>Y{Z{߯}k<>D#LjD!T !$"E#BHZ˖,Y"ƍ FpB-vlgG%*.]I-[Lf#  !~As&̅p;gn=Ϙ5j6լYu K*7o~ $5yU_*<p GZA;ʂ&k4k~s{ohȐ!2y}٪Ri]Jw%9y ?8~2&ܵklr7J4YZ/&>'$dυvb1&m߾9CU?sul3c VZmFX "[XФT?yUF7pL9ojQ޽eUyFeЫW/\͚5Kf1\|n@8zHURYzLzd?FljB 8BHDHF!+?Æ Scco6I#p9;wjeriFa:a@B6moS )8= 06meY fΜ}N&MTR-Zp֭['Izjonmc@#_ƍUp?pcK1LiuVPB*Z7ddxwT]v%CVZTZUE믗YSMaBҀ`eK/Re]#{di$$@;1~d}x \a X Avd,١I+ 7rUW2#Pmz!M߾}UyPVdDKÆ Uih"Ufv*\rP0E|OGhq!ʀ3>!D4hdBI pB56F;wc+PmSmp2v1̞=[啊+$[ny+:|pw~Wg{iVH|nj,$bzM bt9p GZA;ʁ&k4k% .P}N84r~aՎ|[uV=Tj+, u$.+W5}t5̘1mJ4Y[/&>'$d7N9Y;{쮟č7ިڙ6;4>qfg?E>2ilL#!Rp',B!i|8UZٰaLx*$ǀիWˬM.ԩ/N&Ʌ !2l0!|72/V\tA;Cf+p;Uw.5Yj{@X4Tr8#JHe@Cյn5ɒn:Zjرce9CU;lz饗dRt!$MkI ]ԁ۶ms~J4Y[/&>'$d7N,r?<~WUX9jٱct8w3-ZPA:uIcu{@6m$iӦ͝;W&O$ X:CUr}u{L a}wMYBhq!ʀ3>!D4hdBI 8P믿.¼yT[l@9se*UT{>L&2n8u]I&$a0 !hwW #8!oܸQf {#8A8 5JJ"+T1ׯLNJD10yYPB dufzMdخ6/U;t"RI0`!l'3FCHג ۻ|hyf#Tu{2KONڵUیF-5믭AvZgaHC .?4Y]릙^X~@^zL9}QV… e1RM !$mI ]V>a?YDojժR{ M׋I I"Y!Ǜ#׾{8,*;8n:侱١I: 7;?rz)k/?覛n 8ʮdUF[\`Le-N84#!Rp',B!i|hlܸ#^zikqī5j8[nED>\ʶ@8-(8-MBرw}sh̙2K(,_}U}>S .P2?;.)^.]aS 9?VgbJHuLVI >͘1C&BYj6|77(MQ&v8!'/wY6yǫv{I%@z1I9!I%kvbM+:t̒8}vSm7*50MOElذAaԾ}{<̟?_(*- 4&LJOs=^pᷘdFljB 8BHDHF!˛}12Y~Oc  v@'OIa@BH!&MF~L*zhĉ2yl۪=FpFJ'd?Fm6kaH:!Z7dd͛7I#gر?O{Vkԍ{.o; !$kJ ]F%I cƌQ-$$i^u9sBH{@;1'M:U˨Yf':~kF&MOEarzɤdѪo];X4l(o1`$!'|55kLZd߈FljB 8BHDHF!˛{OQeH?~jTn]@?ԧOYL|g P՝͛7$0 !ptٳg䡲n:Zj^gϞ2y,lڴdہ,$bzM٦MW_}Uf!!!ǚcdU7v8ɶ5oO%dܸq?Fp$$!Ǜc[&M-oD#LjD!T !$"E#BHZU /P… eHWC~裏75k,.2lNO?LJ2B a;ɱw2i$1B ŵ#sz:{2 ^87mT?YH&&:B*յn5ɢѭ[7㎓I#g5\SÇ4",ZhiRMwsI3pa{ ͛noFSLŕ>@ύ77b@R =Пdu$vb2NCw{;f#MOET3x`U ,aʕOF&M ϕM6E;q+e_ rƳ(6;4>^ve)K.\qa ۴iS40`2yꩧW^2KE-#6zwdT"E4r8NBHeB"BX4!_qꩧ~ڵ2yy䑪nW^#wtM !x!<իĉeHTFdHXbI/Y&^{FG;0yYRY,uJI폣>Z*dž[`~X&u΃&L 'J7!$ K ] >Cg]vQi rJ;UVUrxj`@uw^L}NH}O;19'!p>AիWw6m$ݻ2yQФTxٕAwS?Ncu֯_/&>}~@'tLx0ྐc8%$%;s#g/cq"B*E!$-p*Ћ/(ʕ+~ŋ+1"ߜ:uz!|!Bظqz>=s2y$̞=[m3ٹ瞫6j߾HG%DڜDj׮a 9?VguBHeAwM3Y&Y?.\b1q`ƷJ*|}ƍUs2y"~ 5&.+7 U֭[A :ڷ^dd4zNLdi.]_W1zGe١I+8l! >%-{NT+R&M!D4hdBI Al<%cƌQuBL4iDN84TfΜ9C {=駟v \wuΐ!C܀&Mrm̟?ߙ2e#8s :?AvҥR~Hjk׺'*>Sν~Iv~a!⫯_LW_uqvznp'Nt?|4lP=# ܛqY7ԨQ#4z-U,"krg>=299?Vg%;ݵnZ5ɢ1p@qoz衇T;]ʤ;2Ç{!D4hdBI 'j֬P?_ cN2G.8Gm]>}dҢA`8DJaU)]hLX~3|p‡(8y)0I,X+M՗/N6mܠW$:\-Zش{:;wvopB|,^X#FP0W^Q#cP۷i~Nzui=pL6M(YQBKIJe˖c9F] NI#muChW_Yѭ*5jwߢur8#6l"qΥ^/AYdbS luJIljg}T;S&C9Da^`݁w2gTJmBw7ov75h@A _~Ѓ16@Kk+s9n`|%ܰa(.03{lY\YB`e`„ *kUV9uQ퀪UW"8lv-RjG>Cn~AYv&  U =Pi$>O-)gv{rSn]Udb|GEըQÝx YLd?BB*s:pa{/ b2',GFUFF* e@ԋg;!XV޽NV;I\  EC9=lvSܶvkի<pAWܟ4sΕF{ډC^,e]co q2iQФTxٕAwQ4iПINXv#*~gw}`~ k}'*z衪~#M/UI{.\+)[ba@MY?Y@hq!ʀ3>!D4hdBI 2eP1c Q*\  &RlN^Vk&pd :9lpq Qoڴi.U/NE)sΪA gQuC8;Qc{,r<@UN³tԩm6Wm5c=\gA;l^nMf?K2y|y.hȑ2y|B"&# xޓ?׀c m 8pu"}!$kkM ]- 67׬YS7ڞ8Rk61{j* 6GsrI^ EG4>lvSܶg憀52_J Par a.,Y?P?FL*Zj١Aˋ /2h@ntRNS#7ߌ% c8>*:uJC%믿^3WzY*b@8!;VoT~ D4r8NBHeB"BX4!_8CԘA͚5IC˺ lܷasxSr>RyT]P.]dR_؜m\8Y/X:+2/SjUՆtA*r)W@8Lzv!k.S>9/'5BȨízdR+=y> 4j(YmYo]Vƈ(TF۷w7uUe@-ZPϕ0Ns̆ dЀG5֭B"&ς͛>!*y 8#6l"6) ;l46?ҼM+Y&Y?07W^]+ fb:v)"'۶.=mܸ9UbոqgzwNw6P1~J.袒/CHך ۻ,[@OFw֮]+1g$an>5D轂!\uɪ##k({p{h(}n,੧ P$hРA;!w*_eUvbrׂ#K?F˗/Sʼn'd[>DK(h@n5Re+!;)S\ÅʹiT{ :uv*vI5I ^Z&OOD#LjD!T !$"E#BHZ813;wL^2\믿2y>:6'@/r|?,Y=L [GuT(;TQ褓NrC qUW]J쳏3g _`cU6TT_2Kc(iLQ /rrIxƍژnIf wy:x>h/1dP:ulA+eF؈FpQ,_GyD3ϔI @1L6gu[iCdi6Y*kݴkUcѪ? ۺI=۷*F{8ZmBXym,Fh",3* )m s/vZl҄?>͒CHכ + ct饗,1p@UW_-A\n\r%t8k֬4J#n<7Y`Сa駟i͟`+TOd\ iFz.G_F?L^f Mi̶_|n87Tm/% +MKz/a5I 8}tU? 'cq"B*E!$-pg}hȐ!2yI,]TիW32t5%5kTc+dr_؜ C_pӰaC\Ti8t8 G1c8sӡC7̗O_~ha8{|&G~wy3vX筷r}Yns?~z}l3sq3 N.C}ժU]wKé]wsi9M6U ĥ~ܳ]#b%/3W|Δ-H駟.w `hpEsw?DqH-SAҫW/F#<{=dVZ4h@g4qD #M8q4Lk4SYGzRN95e;oFf!1 0y؜)pglR@5vdelh4uJI[lУ>*z2{lU.۶.|ݿwyo»/kB w~i&Qkq|{YjrN}7[?}k9p`jT^)Q5Q=P0(Zϳ|Mq۞ځ0Çܷ8+t Kw:`kuFzꩧ>Edl5!x?ߴw_F=M&B~/_IDAT@0:qg%*pػ%+Wk%U IRYf 'cq"B*E!$-pL- :#e#BBz*fab;mI}cs,\cUQ&ye9R^vY#vW_}Z ~i 0 ZٜBr[nI&)8s b/4e}R8Rѣ-r2o裏dּz)^OY6̙3Gf/xy$C6@}Yf<5j8'OY_8M4QuOf >[i ƘKSaIkA1L6gu[iC0KkHZ7ddfb(x`<ms8}Dy(_oۺ|'? PP^J*^-[l۶M@ & ޻BP,14k:K(5'D U5'.P@pw|Fq@_X# ve se]ł@x{[) Ҵ&EKyo6)nغlٲ?xVvWp[n]UFPh˖-c|9h0ߏ53tATodl5!5nX ? Pܹse4+h@nkGqYa ^Ҳ>fW~}iS1ߪ‚5I W_ i@hq!ʀ3>!D4hdBI K?LC!N /Y 3UUJU\?`ضm[UV &B{ډG^,aK;^A4+h@n_pmBЛG}Tp2izOW^q}iS $ IR^oq#D4r8NBHeB"BX4!_ r܌pJYClnU&zz8dN*`Tz77K ga[@GOf l{<%XȜiӦ({*?W8MJö`FpxdWA{キcd 9,ϢӬ :dR&zm7nC^w~mxmTpi{BƯZfͦM۫s`~58*7q衇rM+VC}kҤhSN/t:=8NsLs"dtM bdp YwVn,YYcGAVƆGz׺i$$vnv(Ʀ2 p&rrJu֪|O>Yf LDq@v(>ϳ|Mq۞v /z=[ >o9;He{ډG%G_F͓ bCӬ͛7pk?A!uYIN ǐc#$=t ǒ#K㇀m?PՓ7ިe@r ]wRFAw?&L8 E=Z #˅Zj% ~#!Rp',B!iW0w^Tʆpv*?G)8D+R& r=7kW_Y|O2w2K(,\г_ # q,8m&ٜb~ˤ%gH/Ss9GQ2 z&\B6i_~KDc)>2iYfӸqc5.Fp(f|g, .KfժUI䲮(tGg dQZ7tDd lK/$mދ'+1$&:\١MY5vdilhW9ֺi$$G=T 9n##!Rp',B!iW0^u5vFa}r!?QY7[tE G Dv%\" q2,hĉ2KxUoỤv>{UF :t* SNy뭷TP׮]Apݰa,&0_K1kNpݣ/vK/418]096F}Y ײ/{)0ԤIgرΎ;ds1n`!54i'1PBcH1LiuVai'Aa\quJI zc|wm7hܹ2/pw_U.gW-Yw1Ž;l~iFmU C7͛ϔb)f ˵ݻL!IB!/wY~|{eժUYbRM69͚5SAUVuWR%KrD o5Q=P{p>)۳v|2[`>ߡt=A"޴iS<0Yi'9Y?#?F˖-Sʼn'dj~١I+&9,Xׯ #k SeA5ISW𹣏>Z&J 8|pBa xƍڑC9Dv-RPS0>#!Rp',B!iW06nh=T'oV srO>Y&N>I\8^=[bs#BPbi]6U~ ʈ#TyFvL ]ɺJ 6]w 5۷ow;0UefeASL|ԻtRA7a1dU>gBp(Ϗwwq\ P|r 6`1&pΖ 6aS{ lRœkɶJ9N!ǐc,=u9R 0@&-Nɕ@pn*wUeB~L`ԁ:(%t.bUњ5kd@eYP՝^&KGRن\Tep 2yd|g@Dc=VuM& &9қM%r()8_eҼԫWOɤ 6*m9>F?ܧW &O:$Uw/ a{!a3Qv[ou}Q뮻=ylF^{M6b5 >Rgt9?Vgu6{vh~lskfi6Y^릑_gE]$Fޥv@8eg!F]h]XMŒ#lK2ưkkl\vL !IB!/wYAfuYC̙TREam78W0ڡElҶ /E5Q=Pq{p|+d7e{j 'GJ櫯Rjd.Xdi.r՟c 6T}7n,MO]4 7e '/߭x?"ˁ$m|嗪¾8^B'.X~ F۳n:-RAȍwqZj~#MJz?Wq:ߵӎ18B!g|Bi`"8gjJ9h:sL 8Xc4ah&˃}Y406'/ywTFA~w)GUrUj-[2! qr7v{2yApr ^FߦMʆYl,dwpeC{IN&;SNw}* /o=L Sw;OFO? NQg}GA<;k,YLEkagRuApBt9?Vgu6{vh~lβ}%i$aV|Mn_AA7ŶjժZYO1<&..?~:[lqj֬N=TXdic`>6;4>^ve7:Kf>Pe{x|Rx7Trc,RI[6b^\ᐩ}3ɓUF<dFljB 8BHDHF!+8+Wt?1 $jS/c;gϞ2ys>O?$e={^m$<1i_ڐR@Y3<#G# e[ݻinW^}ӷo_U.aZm=S9%AߗIqDptܿ$3i$5FµZJf 8qaXkS5-Z'wi߾{ Ɠ|dSJb۶mɭ)8}]n _0`{,;Wp@(ADL֯UFW_}L:ŇCaH H;4?ui(Z7ddnҴiHQ.Sn*믿0{dzօjrn*k6 q3N0 kRr)r!?Zpa{4 ֈ:tP`B`sgԧO(55^45Q{ U\dGM7e7e{v?)*."U83U}PeҢ}O;1}ȱ$M2EhС2yjJ&/ t /2M:xU?r"_}fa'>7ߔY"=ZQS?#!Rp',B!iWi`CC'qW^2i\~<'o2yAp*, ©abs2*.l>2iAZnʁ$ROQ{͚52y,vi=" {* ڵL mԨ*Rr(m۶\7UFaoN;pcdgϖY7dzN;~5eر֠ԩD@P/~UFr9jժ)8fj֬2k Νު\]q2[Ekl2>6o^Zf C!0yВnf/O%i[cIƆGp\릕,_H@ !@@fɓeFrF}6ou!駟0~Ֆ 1Lp)r!$Zpa{4 ?wd}˗/w2o@ i h;lK.2iQ}MTNrz1$q xMqٞa˲>ÇqR,ӉK,… UN>d<5x Ge١I+&=lՇ\+*% BkT޲e[ibԲeK%п xwR18B!g|Bi`"89Qd9o![o3裏d1B}2iI؜Ka+zgd@2Y?mA'1ʶ }v7X,:餓d3fjѳ>+r 8[N&/3g2!8id^2(pWn߾}eRO^uUF.bo^0 /xW\6Y~;p3gZfs5 Y٬Y3UvxWU}y'E{vbrcI񃍃ϲO6lYRA=TN*MO]4 7edы_~Y&O4weUe&MRzD ൧Ĩ=ag@mtˤDhq!ʀ3>!D4hdBI J/9R&/HaYըQC x2yAlM}2iI؜ v+,߲ #L^{ReՇbX~1k׮2yAmԭ[W (hTypXrL{*-T)8R͘1C& 8N+|-噙%ϟj.?z=K:(ƤR*U:;H|`.)wlF_(tի4jذ ^id`g,šk0stCaH H;4?a].06?#nZ5gϞ^cI#l}~1|嗪.#ÈJ׺ ?2ydUQR8ښsHs!׊ ۻR56l9/ΜWiI 5q'  ^$>0o/)Վ?X& !PЀŀ>(Yi'9Yvک>9߄֥ }ZfR6;4>^veo(oX<@{eIJi&(GJիWW|2tQֈ5 ŋ;5kTuOb+:tPCA' /cq"B*E!$-p*#ߏ4_}*  6r!D|>Ȥ%cs2¼yd߿B2?pgQi6mȤEGlY^ƍw^,SLQeIկ_ Š6N!ǻjRj׮= v+g˗l%9Wu*詢8E<]v%M$6 8۷w AmxMrԩ*hܸq2y` :G!0yY݆^J .7IZG}Mrɺ$l+J9#!`VQѶm[UggCD¦R.c Oz!iy5i][s r!$8Zpa{Uj@',}C X{LβerJWr[ʴiT_ KDIvsI}a> ^vS-C( [߀:e҂d^XdmdtEgFФTxٕ&]~y2SN9E.24{\[fJxI[;?+khCΌBWm  /cq"B*E!$-p*nM#TjU_~ɭwYˤyUF_LnuUWɤ%cs2Bs9즛nRe@'|L+V' SdYG!#<խ[W&- GV|駟dC, zꩧdR6ȅ AmveҢ)8okpW;},֭5jEu*Nlj>VZ(s-0.ԠA_ղ\]wu2K$\y啪n38C&裏VeKx%0uTUN `ՆmNSp GZmEڡ k줐ıauiCP -plذA&[nEzPCR.\bL:Y{LX0kk!P9G^+_0lUQ{|'7zrJ W8KoYr5qD^pFvsϹfbM}vY3sL(H, NbHe7a{}\TՎ;dN(H,ӍK⏬W0:u҄VJt /ݤ ce ׿Ck ڟ0%?S7[c7\aʺudVx=ߧr7"s%G%߱18B!g|Bi`"8ٳ8Dbر-YD&͚5k*U_&@2?Ǭ9Fx΢q6@~>ݻ2={ʤb;q-h@oOIcl͛e򢰝 Gas:ЪUd?83U讻I @: .@%)>! Lg4M͛7WcRHW\q,*4|AUQ~Af M6Y@Y r7r,]:իW,V#CUe}2KI)><p GZmEڡ*!`I!cC#:\0 CNG2iWFT;0 =C Z*߶u!e%\pJZa֜C¡\s V`egUK/xw&k.D^7߸8tAM6ᙎ@}q7b̦OAAdeaMH@Fҟoa{l%+WTuB%-RuBAf~Xdmxy=T2 u| MO]YJ@n`Η}ѷOoڴLX[MuQ_Rk;wVpY)ʆʔ7r/01Ye-DUF̷IBhq!ʀ3>!D4hdBI JmN: _>N 8!|+|hᔷuVdlNQ^(\>ff>)nPmҥ* IDoV&/;SaӇ_t,)tjo@(/:uR2ouҴ7n(=a=̚5ꈓOrHd8nRu[2K$|wNժUU׮]+xfu  F#Gɭ=G =z*èk׮2y)><p GZmDe/FU_²C $i446?'.뚀J?p.Nhڴi2yLsOR'x̶Vr{oO Ok֬ | 5I h['@?LJdFljB 8BHDHF!+j5P:u UxU^hРA2i`Fʇ 2/Q`T%a'ˀpt9yT[nʂ.4vO( 85\eVl&O?L[lqj׮ALn70`L`&YG_ >U^Dū4j߾L)}Um0zGer+^'eHr@Pb)27GV 18Ta`dr 8愖tguQًQ뗰PCIYc' kK#<º&bq>I&Ύ;dH~E.Yg;<Ш~օjՒI#CUuCwLXlcֵ5(BJC^+_0l ue1 v@9tP7+ 5Q{ 'I9H`= vDm=`V{ډ@%Goʔ)_F89as7a]W 6;4>^veI'|TfM E؂}O8Q&O&۝?\f\|k>I@B|Vjժu XwyǺl۶Mf/ IJ@#uCݻwIS18B!g|Bi`"8Oٜq›HްaCms~׮]eRq468EATNQ06ùogrb;1hzꩲċ'˶Ah'xBqoݺb>} tbMv_r1 ꥗^.).Vp\{g{XjiQgCu.#W1p *ѸqdXduV)pHPTZ˹.o䉥cKXkk!BJG^+_0lpǦyj+Q\Y~!!@= v9-Zp 5Q{ O>rITv=`{ډA%GV{Q}3}w,+terФTxٕ lؾ뮻۴i#.Hӧ*#xōmO~S,Y h{f ÀT9ԠAgƌ2)7cq"B*E!$-p =z8t'pb!UNR}ݝ͛7 >VmV& */am{WdXyWUm'2i4J#;ey;pѣGˤyA*Cdr띪UwqL x$?ߩQjG1:=?8r.8q~Ϩw2y,9RŨ؍>^xkÐ!CT[xuxM.rhС2yh)><p үoR^\ewʽN2QZ7dT9PcAڑ4Jwׅ /LX=֜C#9V`eE(u%Ȭ%!<*7rW:o3k,w3A@p>:uۂׯUYYX%=P$>O͐\H{,K⏬|5ҥ̒&Lڛ0ФTxٕawU*8餓T[&_Te@IeΜ9͹mPg-?Ԯ][EoYkQen<23x`6lC$xbo߾V/%,p9FSc`4o<<Ⱦ#!Rp',B!iWxx}]`Lw}JEqj_|1lN,2_ˤeZӽeM O?,2 dƍUԩ,[Lf۳?~L e[e^#QsϩaN5-l&M\T)><p GZmDe/FU_²C 5vRIRֺY kפR쏍7Z(9sjCRUJwׅ-ZPuCX'r!5s! /wYQ|;|-@ {UR͚5s|Agݺu2{ ,Y<(H,#)yoMjGf-XdynӦMsHG#oФTxٕa8`g:uIˆcǎ2'*?ٻXy*aaQ ,D 0l" "*!;B@EDEFAAED@voe/'S]^wW [շ֩΍-226V0 בco˻.H|瞥6bwU㮻Z8󡙣-۠dI_e#>@Cr%`R o{ydMJG; Zxxkij`S6lRcuᇗ)bP q!M׿u鼊ŷRb>nii-"oW6"fxY?o}i}^xag=\D#~_MMXV[^[U\?vacXOY8>'  qbB|8Sgޓ~}{;W_}ucO4nj?]ڶWDjWCgRWi*_lAՕf`[mԖ(si0mIi?1K/tΛ7{ܤ=oQuY;Oh{^׼tx̤X>,56CG~NDxE|_ϛע_Q ?.770ֹ;P*=Li@\ZqVhK޴X1!Ov">ik+|&26m r (/bW`[yK"T]76"{XU=!mUON{;ޑ7xd>"I'SrW\qEnѺ)7zWj' .ț]bG7]Tqj 7,kϳ>;711?k/u4'T=pd]vɛ6G|HE݋!.ї|"8Ǽfu/0 cGnyZ>,56'CG~Nյ&omc6&_Q u+WD<}ޥ6uȚ9Q_|uhC~>-oMu^p';Kqn|Ȼ,(|9792{}m?im=E޼$Ӽ]~7USjwPU?zs_j#b뭷Λ[9E [0q"]$لZ*o:(ێ W9餓JBan8cJmE~[*mq뭷M[!+yݧsmO]+"k\[qE%DկΛThcsΧi[4'{liI ! Ӈ3ի4/6C Uڼict6ͱۦM}#abޓY?nψ袋捊vsżdDfu/C);MozS޴ uͭ!퉅!' F ZVUW]-.UD}wjӯP0U/}K;=XޥVU@4̉u_I~+%oZw/O>̴_?q?iC#b~{݆V}ME>7."^Wu$W[nɻ+N/'쬳:m]m>X9뮻nk}$QxFXħ?<}R}cKv?'Z~>O0 $ IaWܘVCi7|wߝ7M  1#zH~QG jj`Sêޟ 6 o:Vna"-ث]+]h>yEyӡT=EyVX@]7bʛMS_7:X?=r08}>XJSbSb01vƃkZ^tNs#:Um_S/`MC"sQnܪrWy0r^ġ7_T_WKnm&`|ކ|9!W_}u޽w^ge-sW|K_҇k+:o:k,~`hHN$YL Wabӹ__mׄwF%/ɛ/~jzlSjwPuVsx"xe)Sİ7IƏ乭"b1bzk_ڼPUW]~D4<@wqP6 +^Kn/v޴[0@mIy ㎥//xAe6z^z E{w?EH*_Dok<vy߈(XN<.o>u=(~zap}&uzŦT]yh矟7maV:~D6nc|NizOf!kmUE*>u7Ρ^-~/UOӼ=/ǹkɟx≼XMz}cM燿o:J O?=7<t~ߖ^{gqF|HY#А`IƯŏ_#bA_׿oWb!l9qh|g!~Nkhj`S.RE|ϛYgU:"-x]wugڋ8csWΧ(DQ?~{7Y2L |fD.lixy(H裏vVZi(WUޥ/ν6izosE / 5'>|">{>Pڷ(ʹgҹDkb2MINBXo>lI]^|vUWZx__j+3̛6&G4Ushc?gعd'z_:VXi%,:"!CÚ{^7cqh-v[C CW~Nյ,s;E\zyF|{+;bY0'Zl-@!?B[:I/8MRU;7_el׃c-u"C=wYU5& Yqg=YF<;̻{|"ⷜdXW_}u"F}S]>ҹ8nc7/y"^җ67.[guJ^H]IP-j_{RK-yG/Nr' F|K0~կj1W\._uUsZkF0?cGMKBnrʜ֚"T]7ł=yv"7bm-K 2ڞzꩼX{| \믿~D<->V[Xϥv^zͼ]S7T*Λu]}G,ro=oJZVSO=5o>Q1KQr!bW޷o~yX{KOĂ{q1"oQ7o+3}zŦT]yh! "?#oژWG4um ;םB{ڏ"+#=yO|QMK,D"{1 mnFGM }[C|E9P^17kY6 5 39sKD,2cCw&}N\Z8ׁkC~>o-ybǤ{y}`f?w^ה^sM6٤bͿgF UyhT+*8럛31rgqFcsXqƗbۍ- ?,Xr%;wuWu&^۽~9G|O^yyDraU&ЈG:>h>=z{ޯw1o>,~`hHN$YL W3[oRFĢ^ҿʍ(-o)?"~Šnn;ng?9-M'|O|"o*%/yI鼋⮽ďļ}|+(REyʛoqE<f>U߅7e>lI]^|vUgbLmElyF- y7n]'i:1ye-Q KW"D;P:"N<ļfu0/?]:~7pC޼uЇ1>M!/W F Zwqv"yS>#-5s:`\֖|B[:I/8AXܗ f/Ɓs5:RxSuq˪жW6U0&&c݇wK-TXsc 7/{<9oZTNu~D<9=`<-یe];KQD[N8t<ҿbƇ~xNKéz[nm.ҿǂX\;]wg}򦭲^{ιw=o4qcD؈|yg1YDo-ybǤֿPՏd0 -;SPsk +Mq*m~yeìn:1"V}{0yYgR9➒o971(yZ>FXDQ7߼t>9cV0-|bk Êo,ΈO<MtMS#Vr' F|K0~5#~}[o~'\w^n1w_ x"u~_{QGjDSjwPuXcV_}S!# Ds ["ܸ(6CD<ʻyBE,vءnmji8?x}n(?M1tgw^o~K'"׿?G!x≥}qc\#unx뮫 YF_捉ժ'dwwߝwZޓzX_܇ NϤ.VTT3 gqF"&QgIc/IG&}['I=?^וڎqkqM-Gq7_TO>dgUV)g_~yޥҬ2/KtovlaskcH=C_~Nյ,^{i#XS#F)5s:`\-~%oZa<~/̬_󶷽[g?Y箻z|3{}=cAFi3жW:o_YDy=׋WŒK.-x!totn<]WQbwyg/|-o<.+]vUW]5,GwG<{t."$`e>^W7pCOxMj-g,b5q9ͧF~>O0 $ IajΪZ߈xo_qy6lyDo|#)q-tnj`Sj07~?<ԧ>U:vQ ƒf778hrA[lQ:^Ygq|e)ۙgZ_7յ4D(zMBDxyV[_FĢ.D[ne"^L6(&:.m]8}]tE6Xc54X_V[m7jޓzX_܇ NϤ.VTT;Km~k1F]'e&o͚n'h2;*o :cJ&?\:"v}yY}se^x駗Ρx8xXV܀9askcH=ChF~Nյ,Wj'"ָ}Cv/rJER kD:\֦|Zڒ7-yLCi_,d0n՜rגYAhMC۾_^9Y܄X/tF<~b9H`[٪AsQ},CBm2)cs|b@s]wݵ@|x㍝vۭo xiF:{\n V][s5zj,~`hHN$YL Ws =ʫn=D5.M-lAysXJm eMU?ύQ Ə[w;R/XnȚ9848ׁkS~>omɛ</ybr_2_חb\ETjTU5q )ڈ޺tNW9NSk1#b=Hk߫< ;W|͹E5 h>n)71x>-\6ӟv[oҶ ȃ>Nă 4_ogS%^r' F|K0~5'~//&?n1M-lAysX!nLڞ|ɼK-bub+A6nlrEW}{_ޥV]veKǍSz:oI,v9=^Ӟ:RqCDO+~+⬳ʛ/`GqD"&:RᄏXMD,G?R[ED /0RxJo^:nq˸&ޓY_܇ NϤ.VTTj"qKD~ymX9(8isqoZu󞌮#ZkR o}[yֈ( g]:"oۼK-}ӎלM{jK=61T]R{0.7տ97F)&uN4N uڔi~kK޴X1 ô/&ybr_26e/{YO=yO7YdMJT\okmV9^Cʛ*^ۮZ:nU4a_6/r+;6c!a/%гҾ 8ۮz2fo\zj4˯G f!9d0)_͉x_~R(Ҷ3±EM;o [fQ=أtxb(-rxr{,4xES[w]ME8Uqi݇clOzuᢈ|5UtpXw^O0 $ IajB umyױYq>ŮEM;o+|_-97|06׏Q{>iQ`jn~xm?SEĂaj_^xX"λ-n(">kehoEDq6zy7e"-sEĿ68J͍V[_*6[o{sv>xߟwRs#ƛK.$6xbe>an3{2UjwPM뮻n":n@@_Ѹ p9v&on'i*s=KmFl&yFb.l<"-o*Yo>"~_]nV_w/mⷂVZt>E5~yp 7&Ƽq!78[CFBs{`pe)8(2*"nVwΒK.Y:VQ d&qN4 uiK~>4~%oZa<~/vwt<ΣSZy啻+_q0"|yYrbu|bEbK^ G;'j`Rv7vǪX{Y`\{xKTx`nbA"8餓:kvA"}|y]s5m٦g=>Lw(f`|^n7JuSH?l04$'X,&YK}+>я]E|zŸo5vax_nwnD[n%6x*+Xj;b=nf!H1BaSiU7q]O&f_rlvyp }Zy~7lGMoݏ*F,菅'w]w@nnF[nZLޓY_܇ NOM >]SnΪ,\;kaSskcChV~Nյ,fs3|a(XP5wB[m&iN4ׁumϳi~kK޴X1m'~?"b4jXS>ߚ/x kVڬ#Rq'M׳>t_R;F^4Q`'?In|k_9S-f`<:#vu׼ʯG f!9d0)_zK<7rm})܈'=yF5vaxsR"~]r%m]Ns# [mUi SO=[L"+GOm~xjcQ/>\sMw!G>Sbw.7sQ/ޔB>nQǓ*cal>^{->>]tѼ"b!u]Y4wYb7?c=s}]oZɍqLyG|$ut86zE|fzRh! cx]:;?`O oXw^<9 Ǣ>dp}nx[;S>JMM;&g[nR9b ??6&(׽uݱ3_D7xcw^])8MsMS?u{2&^) 7P:"9昼yk+Z/~1o>J[Y|r!Q"ƽ3<_iŵM!@Cr%`Rկ~sc}ͻ~y͍x 5van$|XOOXӟivM o7_ ] 97ba'?|cΫw1o^<&hBWc,(ct"^Om7+R}.R8+X:^0x! 2zE}";߸yf~ktoiB#nqSH"c17eޓU}_Z?܇ NOMmArŦTyg]j{,wź/|}a/m3hiceFnӼ';;+ ?̛7C-CD\b8餓J 70o>J[UAo\|wUUEw[CWB{`pe)pq#w1.~w^G>ֽ{6ZКN8{]wݵoȪ2 spw ;_lS~^eڒ7-yLc0im O_K'>Q}؂|/| KM7ݴ[Dy`K37>]*z 0V[u5_WqB>A#|OY)|{fI~>O0 $ Iaj^,T tPϭAuܚZTjo^:N+a}w.m;HAB_t̺#C~͇(@ϭs9'o^O>t"kC,D7| &nlb-JY6lw\P0?cɻ,XE>dB~8J׿u/>jޓX_܇ NOMmArŦ8O?}/~;v(4ϱG5}#'&};ޓԝ|/3|ɼycU`[[- +{W}Y}e^O~3he/^T[pskc`Ch^~Nյ,vb|޻(i =][L[[:i-& }`@Q1/y'.:묳JmUEq?xnZ0ʫ"?}Ea G~7Mnv!YnJ繐"ϙP0<9qͧ^r' F|K0~ǫ_R_GD!Xho{JW_7o\SjwP9?FzZt:tn|Ȯx-yAo G]X`ۆC8G˛.Z(?XkI'YfeJPWڝc-g8>~6*%XsUW]rw>ۮ3szgC7* I>.K^{f^1 ' >¢>dp}nnS 4/6ƕ?<-gԈ޽nRem)8 saB?i뎓da?Nn+˛6*ϡo|y5>}Y}e^8(Z7+viΟ|iKkn! SB{`pe)8klkz]wӎF)56ω:P=:Pombڒ7-yLs$ݷ<~/=Y~Kߙ ү}D;ꨣn7tS|'U7M)AjwPCP;ܝ qc?s> -8KsAR?67?.R;EM'[!" N?яJ^O[ pI]cn/})j mqέ1d~u!G~Nյ,wv K-TV_~Y 9Q\=\֦|֖ic 6ݷ<~/X~=ךmV/ҼDhs~u%r;/w}g=Yט#}+7:O>d~gmx(CgŴ_BX)R:s=7o֘|;/QxbZ,Z#<[a*svX޼Q#K/]:"`f⦚d]w\3_*o}і"x#'n(9/$^{Χ>dp}9Ҷ U/CwWwĂ|~s^p9a?Q;8^YGuL\wxOՙ#r;oq޴Qݲ +#"nD@kӖy ✿owǷEQ<'?Inz(mŘ[cH:#W F ?a"k{ ^PzQBq (/.w!W*Cǡms:ܨ{ؖ|X֖ic ,䒥F!&-}`}ӟ.}oFD믿>o>c=iDxGW>r`-_Z ,~/"6pWc=wQ0>kVEĽ"d ~[*~tEgFr' F|K0~zᇻ?&>vu[onaVZW{t:7΍7ޘK.[۸Yslݧ!݂g}viLoswN:Ρyh7(׼5xc|EX\E'O<{jW&l?蠃:m_|=to:;t^WvV\qXq#A{サX(/~ yOhkt&WU[t-7`|wGOr- +xOI+01gfms5X{}7דּ:7͝w]q4¤sg46nsLA}`m>~@Q|i6k&7jDu Ņ7&{&LߝQ(}E 6ؠc{}?7hQ/r3!v~_M'VuI{zꩼD^{m@~Mx,}BY#А`I/ɽF<CFѢ[O>dޥns̄7yK_9cA >`s\+46psZ0 暥qgNr' F|K0~4O5:}0\M20'xsvviy@s=.g\veyp饗"X% H?l04$'X,& yrC:h0n?f9r' F|K0~4O5:}<}IDAT0\M21_#А`I/ɽF<CF5}`6,I@Cr%`R'>F&`֘/~`hHN$YL @^Ӈu?`!s>O0 $ Iahkt`:3@Cr%`R'>F&`֘/~`hHN$YL @^Ӈu?`!s>O0 $ Iahkt`:3@Cr%`R'>F&`֘/~`hHN$YL @^Ӈu?`!s>O0 $ Iahkt`:3@Cr%`R'>F&`֘/~`hHN$YL @^Ӈu?`!s>O0 $ Iahkt`:3@Cr%`RK!B!Dr !B!BO!B! &B!>O0 $ I/!B!}(B!B!#L?!B!w0B!BZPH?l04$'X,&EB!B4 .B!B!`0B!br !B1KAY#А`IyB!B|0܇B!B!b~B!Bq}(B!,e#>@Cr%RMА`I(jSl04$'X, .0 $ BK6#>@Cr%RMА`I(jSl04$'X, .0 $ BK6#>@Cr%RMА`I(jSl04$'X, .0 $ BK6#>@Cr%RMА`I(jSl04$'X, .0 $ BK6#>@Cr%RMА`I(jSl04$'X, .0 $ BK6#>@Cr%RMА`I(jSl04$'X, .0 $ BK6#>@Cr%RMА`I(jSl04$'X, .0 $ BK6#>@Cr%RMА`I(jSl04$'X, .0 $ BK6#>@Cr%RMА`I(jSl04$'X, .0 $ BK6; a ׏B 3#m QX' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X,FRئ\|%`yKanp`.m X' )`I>X$I$I$I$I$I$I$I$I+\|%`I$I$I$I$I$I$I$I$Is%I$I$I$I$I$I$I$I$X$I$I$I$I$I$I$I$I ?`I>X$I$I$I$I$I$I$I$I+\|(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` @@(0 P` ;w,0z $` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @` @vX`,vH 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ 0@ g([6IENDB`cangjie_compiler-1.0.7/include/000077500000000000000000000000001510705540100164325ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/000077500000000000000000000000001510705540100200325ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/AST/000077500000000000000000000000001510705540100204615ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/AST/ASTCasting.h000066400000000000000000000144551510705540100226030ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements type casting ast related nodes. */ #ifndef CANGJIE_UTILS_AST_CASTING_H #define CANGJIE_UTILS_AST_CASTING_H #include #include "cangjie/AST/NodeX.h" #include "cangjie/AST/Types.h" #include "cangjie/Utils/CastingTemplate.h" namespace Cangjie { // Register NodeType::kind for mono typed AST. #define ASTKIND(KIND, VALUE, NODE, SIZE) \ template <> struct NodeType { \ static const AST::ASTKind kind = AST::ASTKind::KIND; \ }; #include "cangjie/AST/ASTKind.inc" #undef ASTKIND // Register NodeType::kind for mono typed Ty. DEFINE_NODE_TYPE_KIND(AST::ArrayTy, AST::TypeKind::TYPE_ARRAY); DEFINE_NODE_TYPE_KIND(AST::VArrayTy, AST::TypeKind::TYPE_VARRAY); DEFINE_NODE_TYPE_KIND(AST::PointerTy, AST::TypeKind::TYPE_POINTER); DEFINE_NODE_TYPE_KIND(AST::CStringTy, AST::TypeKind::TYPE_CSTRING); DEFINE_NODE_TYPE_KIND(AST::TupleTy, AST::TypeKind::TYPE_TUPLE); DEFINE_NODE_TYPE_KIND(AST::FuncTy, AST::TypeKind::TYPE_FUNC); DEFINE_NODE_TYPE_KIND(AST::UnionTy, AST::TypeKind::TYPE_UNION); DEFINE_NODE_TYPE_KIND(AST::IntersectionTy, AST::TypeKind::TYPE_INTERSECTION); DEFINE_NODE_TYPE_KIND(AST::InterfaceTy, AST::TypeKind::TYPE_INTERFACE); DEFINE_NODE_TYPE_KIND(AST::ClassTy, AST::TypeKind::TYPE_CLASS); DEFINE_NODE_TYPE_KIND(AST::EnumTy, AST::TypeKind::TYPE_ENUM); DEFINE_NODE_TYPE_KIND(AST::StructTy, AST::TypeKind::TYPE_STRUCT); DEFINE_NODE_TYPE_KIND(AST::TypeAliasTy, AST::TypeKind::TYPE); DEFINE_NODE_TYPE_KIND(AST::GenericsTy, AST::TypeKind::TYPE_GENERICS); template using ShouldImplSeparately = std::enable_if_t && ShouldInstantiate::value>; #ifndef UT #define STATIC_ASSERT static_assert #endif // Defined the mono type checking method for ASTNode, Ty. template struct TypeAs> { static inline bool IsInstanceOf(const AST::Node& node) { STATIC_ASSERT(NodeType::kind != AST::ASTKind::NODE); return node.astKind == NodeType::kind; } }; template struct TypeAs>> { static inline bool IsInstanceOf(const AST::Ty& ty) { return ty.kind == NodeType::kind; } }; // Customized type checking function for ASTNode. template <> struct TypeAs { static inline bool IsInstanceOf(const AST::Node& node) { return node.astKind >= AST::ASTKind::DECL && node.astKind <= AST::ASTKind::INVALID_DECL; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const AST::Node& node) { return node.astKind >= AST::ASTKind::CLASS_LIKE_DECL && node.astKind <= AST::ASTKind::STRUCT_DECL; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const AST::Node& node) { return node.astKind >= AST::ASTKind::VAR_DECL && node.astKind <= AST::ASTKind::FUNC_PARAM; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const AST::Node& node) { return node.astKind == AST::ASTKind::FUNC_PARAM || node.astKind == AST::ASTKind::MACRO_EXPAND_PARAM; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const AST::Node& node) { return TypeAs::IsInstanceOf(node) || node.astKind == AST::ASTKind::VAR_WITH_PATTERN_DECL; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const AST::Node& node) { return node.astKind >= AST::ASTKind::CLASS_LIKE_DECL && node.astKind <= AST::ASTKind::INTERFACE_DECL; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const AST::Node& node) { return node.astKind >= AST::ASTKind::PATTERN && node.astKind <= AST::ASTKind::INVALID_PATTERN; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const AST::Node& node) { return node.astKind >= AST::ASTKind::TYPE && node.astKind <= AST::ASTKind::INVALID_TYPE; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const AST::Node& node) { return node.astKind >= AST::ASTKind::EXPR && node.astKind <= AST::ASTKind::INVALID_EXPR; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const AST::Node& node) { return node.astKind >= AST::ASTKind::ASSIGN_EXPR && node.astKind <= AST::ASTKind::SUBSCRIPT_EXPR; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const AST::Node& node) { return node.astKind == AST::ASTKind::MEMBER_ACCESS || node.astKind == AST::ASTKind::REF_EXPR; } }; // Customized type checking function for Ty. template <> struct TypeAs { static inline bool IsInstanceOf(const AST::Ty& ty) { return ty.kind == AST::TypeKind::TYPE_CLASS || ty.kind == AST::TypeKind::TYPE_INTERFACE; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const AST::Ty& ty) { return ty.IsPrimitive(); } }; template <> struct TypeAs { static inline bool IsInstanceOf(const AST::Ty& ty) { if (ty.kind != AST::TypeKind::TYPE_CLASS) { return false; } return dynamic_cast(&ty) != nullptr; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const AST::Ty& ty) { if (ty.kind != AST::TypeKind::TYPE_ENUM) { return false; } return dynamic_cast(&ty) != nullptr; } }; } // namespace Cangjie #endif // CANGJIE_UTILS_AST_CASTING_Hcangjie_compiler-1.0.7/include/cangjie/AST/ASTContext.h000066400000000000000000000236351510705540100226370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the ASTContext related classes, which holds the data for typecheck and later procedures. */ #ifndef CANGJIE_AST_ASTCONTEXT_H #define CANGJIE_AST_ASTCONTEXT_H #include #include #include #include #include #include #include "cangjie/AST/Node.h" #include "cangjie/AST/ScopeManagerApi.h" #include "cangjie/AST/Searcher.h" #include "cangjie/AST/Symbol.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/AST/Cache.h" namespace Cangjie { /** Lucene like inverted index. */ struct InvertedIndex { /** Inverted index of Symbol's name. */ std::unordered_map> nameIndexes; /** Inverted index of Symbol's scope name. */ std::unordered_map> scopeNameIndexes; /** Map of scope gate. */ std::unordered_map scopeGateMap; /** Inverted index of Symbol's scope level. */ std::unordered_map> scopeLevelIndexes; /** Inverted index of Symbol's ast kind. */ std::unordered_map> astKindIndexes; /** Put @c name into a trie, easy to do prefix search. */ std::unique_ptr nameTrie = std::make_unique(); /** Put @c scopeName into a trie, easy to do prefix search. */ std::unique_ptr scopeNameTrie = std::make_unique(); /** Put @c astKind into a trie, easy to do suffix search. */ std::unique_ptr astKindTrie = std::make_unique(); /** Put the begin position of a symbol into a trie, easy to do range search. */ std::unique_ptr posBeginTrie = std::make_unique(); /** Put the begin position of a symbol into a trie, easy to do range search. */ std::unique_ptr posEndTrie = std::make_unique(); /** The minimum possible position. */ Position minPos = BEGIN_POSITION; /** The maximum possible position. */ Position maxPos = {0, PosSearchApi::MAX_LINE, PosSearchApi::MAX_COLUMN}; /** Clear all indexes. */ void Reset(); /** Build inverted index. */ void Index(AST::Symbol* symbol, bool withTrie = true); /** Delete inverted index. */ void Delete(AST::Symbol* symbol); }; // Names is constructed with 'declaration name, scopeName' using Names = std::pair; // ty var -> upperbound -> AST nodes of generic constraints using GCBlames = std::map, std::map, std::set>>>; /** AST Context for sema and codegen. */ class ASTContext { public: ASTContext(DiagnosticEngine& diag, AST::Package& pkg); ~ASTContext() = default; /** * Delete desugar expr do two things: * 1. Recursively delete inverted indexes of the sub symbols of the symbol. * 2. Reset the desugar expr. * * @see DeleteInvertedIndexes */ void DeleteDesugarExpr(OwnedPtr& desugar); void DeleteInvertedIndexes(Ptr root); void DeleteCurrentInvertedIndexes(Ptr node); // recursively clear all cache entries of sub-tree void ClearTypeCheckCache(const AST::Node& root); // only remove cache for this node void RemoveTypeCheckCache(const AST::Node& node); // set a dummy last key so that the synthesize of the node will be skipped when possible void SkipSynForCorrectTy(const AST::Node& node); // Skip Syn for all nodes with correct ty void SkipSynForCorrectTyRec(const AST::Node& root); /** * Reset a AST context. */ void Reset(); static std::string GetPackageName(Ptr node); bool HasTargetTy(Ptr node) const; void AddDeclName(const Names& names, AST::Decl& decl); void RemoveDeclByName(const Names& names, const AST::Decl& decl); std::vector> GetDeclsByName(const Names& names) const; /** * @brief Store outer variable with pattern declaration. * @details This function stores a variable declaration from the outer scope that includes a pattern. * * @param vd The variable declaration from the outer scope. * @param vpd The variable declaration with pattern to be stored. */ void StoreOuterVarWithPatternDecl(const AST::VarDecl& vd, AST::VarWithPatternDecl& vpd); /** * @brief Get the outer variable declaration abstract. * @details This function retrieves the abstract of a variable declaration from the outer scope. * * @param vd The variable declaration from the outer scope. * @return The abstract of the variable declaration. */ AST::VarDeclAbstract& GetOuterVarDeclAbstract(AST::VarDecl& vd) const; /** * @brief Insert an enum constructor. * @details This function inserts a constructor for an enum type with the specified name, argument size, and * declaration. * * @param name The name of the enum constructor. * @param argSize The size of the arguments for the enum constructor. * @param decl The declaration of the enum type. * @param enableMacroInLsp Whether enable macro in LSP. */ void InsertEnumConstructor(const std::string& name, size_t argSize, AST::Decl& decl, bool enableMacroInLsp); bool IsEnumConstructor(const std::string& name) const; /** * @brief Find an enum constructor. * @details This function finds a constructor for an enum type with the specified name and argument size. * * @param name The name of the enum constructor. * @param argSize The size of the arguments for the enum constructor. * @return A vector of pointers to the declarations of the enum constructors that match the specified name and * argument size. */ const std::vector>& FindEnumConstructor(const std::string& name, size_t argSize) const; std::set> Mem2Decls(const AST::MemSig& memSig); DiagnosticEngine& diag; Ptr const curPackage{nullptr}; const std::string fullPackageName; /** An unified table, contain all info. */ std::list> symbolTable; InvertedIndex invertedIndex; unsigned currentScopeLevel{0}; unsigned currentMaxDepth{0}; std::string currentScopeName{TOPLEVEL_SCOPE_NAME}; /** * Current symbols being checked which are stored in a stack. Push the node to the stack when entering a * `VarDecl`, `FuncDecl` or other high level check target, and pop the node after finishing the checking. */ std::stack> currentCheckingNodes{}; std::unordered_map, Ptr> targetTypeMap; /**< The node should be inferred to the corresponding type. */ std::unordered_map, Ptr> lastTargetTypeMap; /**< Target last time the expr is checked */ std::unordered_map, AST::TypeCheckCache> typeCheckCache; std::unordered_map, OwnedPtr> typeToAutoBoxedDeclMap; std::unordered_map, OwnedPtr> typeToAutoBoxedDeclBaseMap; std::unique_ptr searcher = std::make_unique(); /** A vector for checking qualified types. */ std::vector> packageDecls; /* Used to lookup possible types given a member's signature, not considering inherited members. * Use function Mem2Decls to include inheriting types. */ std::unordered_map>, AST::MemSigHash> mem2Decls; /* from a decl to its subtype decls visible in current package, as a supplement to mem2Decls */ std::unordered_map, std::set>> subtypeDeclsMap; /** track source of generic parameters' upperbounds, for diagnose **/ GCBlames gcBlames; /** Lambda nodes that are 'direct' sub-expression of some func call's arg. * Should never create new placeholder ty vars for these lambdas. * In case the func params' instantiated types can be known, then there's expected type. * In case some of the func's generic args can't be solved, placeholders for these generic args will * be propagated, and no new ones will need to be created. */ std::unordered_set> funcArgReachable; private: /** Mapping from VarDecl to the outer VarWithPatternDecl, helps for finding the initializer. */ std::unordered_map, Ptr> varDeclToVarWithPatternDeclMap; /** Mapping from name to mapping from arguments size to constructor declaration. */ std::unordered_map>>> enumConstructors; /** * declMap is a map used to look up an target declaration of a reference with known reference name and scope name. * Therefore, the key of declMap is an pair of type , whose first element is the declaration name * and the second element is the name of the scope it locates to make an declaration unique. */ std::unordered_map, std::vector>, HashPair> declMap; bool IsNodeInOriginalMacroCallNodes(AST::Decl& decl) const; }; /** * This structure is used for lsp dot completion. * Since std::variant cannot perform correctly in windows, we chose to use this custom type. */ struct Candidate { std::vector> decls; std::unordered_set> tys; bool hasDecl; explicit Candidate(const std::vector>& decls) : decls(decls), hasDecl(true) { } explicit Candidate(const std::unordered_set>& tys) : tys(tys), hasDecl(false) { } Candidate() = default; }; } // namespace Cangjie #endif // CANGJIE_AST_ASTCONTEXT_H cangjie_compiler-1.0.7/include/cangjie/AST/ASTKind.inc000066400000000000000000000164071510705540100224210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. // ASTKIND(ast kind, ast value for human reading, ast node, ast size for notify checking related code) #ifdef ASTKIND ASTKIND(ANNOTATION, "annotation", Annotation, 584) ASTKIND(MODIFIER, "modifier", Modifier, 264) ASTKIND(DECL, "*decl", Decl, 728) // begin of subtypes of Decl. ASTKIND(MAIN_DECL, "main_decl", MainDecl, 752) ASTKIND(FUNC_DECL, "func_decl", FuncDecl, 824) ASTKIND(MACRO_DECL, "macro_decl", MacroDecl, 784) ASTKIND(CLASS_LIKE_DECL, "class_like_decl", ClassLikeDecl, 824) // begin: subtypes of ClassLikeDecl and InheritableDecl. ASTKIND(CLASS_DECL, "class_decl", ClassDecl, 840) ASTKIND(INTERFACE_DECL, "interface_decl", InterfaceDecl, 832) // end: subtypes of ClassLikeDecl. ASTKIND(EXTEND_DECL, "extend_decl", ExtendDecl, 888) ASTKIND(ENUM_DECL, "enum_decl", EnumDecl, 928) ASTKIND(STRUCT_DECL, "struct_decl", StructDecl, 792) // end of subtypes of InheritableDecl. ASTKIND(TYPE_ALIAS_DECL, "type_alias_decl", TypeAliasDecl, 760) ASTKIND(PRIMARY_CTOR_DECL, "primary_ctor_decl", PrimaryCtorDecl, 744) ASTKIND(BUILTIN_DECL, "builtin_decl", BuiltInDecl, 736) ASTKIND(VAR_DECL, "var_decl", VarDecl, 808) // begin of subtypes of VarDecl and VarDeclAbstract. ASTKIND(PROP_DECL, "prop_decl", PropDecl, 896) ASTKIND(MACRO_EXPAND_PARAM, "macro_expand_param", MacroExpandParam, 1608) ASTKIND(FUNC_PARAM, "func_param", FuncParam, 872) // end of subtypes of VarDecl. ASTKIND(VAR_WITH_PATTERN_DECL, "var_with_pattern_decl", VarWithPatternDecl, 800) // end of subtypes of VarDeclAbstract. ASTKIND(GENERIC_PARAM_DECL, "generic_param_decl", GenericParamDecl, 752) ASTKIND(PACKAGE_DECL, "package_decl", PackageDecl, 736) ASTKIND(MACRO_EXPAND_DECL, "macro_expand_decl", MacroExpandDecl, 1464) ASTKIND(INVALID_DECL, "invalid_decl", InvalidDecl, 728) // end of subtypes of Decl. ASTKIND(PATTERN, "*pattern", Pattern, 264) // begin of subTypes of Pattern. ASTKIND(VAR_PATTERN, "var_pattern", VarPattern, 280) ASTKIND(CONST_PATTERN, "const_pattern", ConstPattern, 280) ASTKIND(TUPLE_PATTERN, "tuple_pattern", TuplePattern, 360) ASTKIND(ENUM_PATTERN, "enum_pattern", EnumPattern, 368) ASTKIND(VAR_OR_ENUM_PATTERN, "var_or_enum_pattern", VarOrEnumPattern, 352) ASTKIND(TYPE_PATTERN, "type_pattern", TypePattern, 328) ASTKIND(EXCEPT_TYPE_PATTERN, "except_type_pattern", ExceptTypePattern, 360) ASTKIND(COMMAND_TYPE_PATTERN, "effect_type_pattern", CommandTypePattern, 360) ASTKIND(WILDCARD_PATTERN, "wildcard_pattern", WildcardPattern, 264) ASTKIND(INVALID_PATTERN, "invalid_pattern", InvalidPattern, 264) // end of subtypes of Pattern. ASTKIND(TYPE, "*type", Type, 376) // begin of subtypes of Type. ASTKIND(REF_TYPE, "ref_type", RefType, 560) ASTKIND(QUALIFIED_TYPE, "qualified_type", QualifiedType, 568) ASTKIND(OPTION_TYPE, "option_type", OptionType, 424) ASTKIND(CONSTANT_TYPE, "constant_type", ConstantType, 408) ASTKIND(VARRAY_TYPE, "varray_type", VArrayType, 456) ASTKIND(PRIMITIVE_TYPE, "primitive_type", PrimitiveType, 416) ASTKIND(PAREN_TYPE, "paren_type", ParenType, 424) ASTKIND(FUNC_TYPE, "func_type", FuncType, 472) ASTKIND(TUPLE_TYPE, "tuple_type", TupleType, 464) ASTKIND(THIS_TYPE, "this_type", ThisType, 376) ASTKIND(INVALID_TYPE, "invalid_type", InvalidType, 376) // end of subtypes of Type. ASTKIND(EXPR, "*expr", Expr, 432) // begin of subtypes of Expr. ASTKIND(WILDCARD_EXPR, "wildcard_expr", WildcardExpr, 432) ASTKIND(CALL_EXPR, "call_expr", CallExpr, 592) ASTKIND(PAREN_EXPR, "paren_expr", ParenExpr, 480) ASTKIND(MEMBER_ACCESS, "member_access_expr", MemberAccess, 768) // begin of subtypes of NameReferenceExpr. ASTKIND(REF_EXPR, "ref_expr", RefExpr, 672) // end of subtypes of NameReferenceExpr. ASTKIND(OPTIONAL_EXPR, "optional_expr", OptionalExpr, 464) ASTKIND(OPTIONAL_CHAIN_EXPR, "optional_chain_expr", OptionalChainExpr, 432) ASTKIND(PRIMITIVE_TYPE_EXPR, "primitive_type_expr", PrimitiveTypeExpr, 432) ASTKIND(RETURN_EXPR, "return_expr", ReturnExpr, 464) ASTKIND(LIT_CONST_EXPR, "lit_const_expr", LitConstExpr, 544) ASTKIND(INTERPOLATION_EXPR, "interpolation_expr", InterpolationExpr, 496) ASTKIND(STR_INTERPOLATION_EXPR, "str_interpolation_expr", StrInterpolationExpr, 512) ASTKIND(ASSIGN_EXPR, "assign_expr", AssignExpr, 480) // begin of OverloadableExpr. ASTKIND(UNARY_EXPR, "unary_expr", UnaryExpr, 464) ASTKIND(BINARY_EXPR, "binary_expr", BinaryExpr, 464) ASTKIND(INC_OR_DEC_EXPR, "inc_or_dec_expr", IncOrDecExpr, 464) ASTKIND(SUBSCRIPT_EXPR, "subscript_expr", SubscriptExpr, 528) // end of OverloadableExpr. ASTKIND(IS_EXPR, "is_expr", IsExpr, 464) ASTKIND(AS_EXPR, "as_expr", AsExpr, 464) ASTKIND(RANGE_EXPR, "range_expr", RangeExpr, 512) ASTKIND(ARRAY_LIT, "array_lit_expr", ArrayLit, 528) ASTKIND(ARRAY_EXPR, "array_expr", ArrayExpr, 544) ASTKIND(POINTER_EXPR, "pointer_expr", PointerExpr, 448) ASTKIND(TUPLE_LIT, "tuple_lit_expr", TupleLit, 528) ASTKIND(MATCH_EXPR, "match_expr", MatchExpr, 576) ASTKIND(BLOCK, "block", Block, 512) ASTKIND(IF_EXPR, "if_expr", IfExpr, 544) ASTKIND(LET_PATTERN_DESTRUCTOR, "let_pattern_destructor", LetPatternDestructor, 512) ASTKIND(TOKEN_PART, "token_part", TokenPart, 448) ASTKIND(QUOTE_EXPR, "quote_expr", QuoteExpr, 512) ASTKIND(TRY_EXPR, "try_expr", TryExpr, 752) ASTKIND(WHILE_EXPR, "while_expr", WhileExpr, 512) ASTKIND(JUMP_EXPR, "jump_expr", JumpExpr, 448) ASTKIND(LAMBDA_EXPR, "lambda_expr", LambdaExpr, 464) ASTKIND(TRAIL_CLOSURE_EXPR, "trailing_closure_expr", TrailingClosureExpr, 496) ASTKIND(FOR_IN_EXPR, "for_in_expr", ForInExpr, 560) ASTKIND(DO_WHILE_EXPR, "do_while_expr", DoWhileExpr, 528) ASTKIND(TYPE_CONV_EXPR, "type_conv_expr", TypeConvExpr, 496) ASTKIND(THROW_EXPR, "throw_expr", ThrowExpr, 464) ASTKIND(PERFORM_EXPR, "perform_expr", PerformExpr, 464) ASTKIND(RESUME_EXPR, "resume_expr", ResumeExpr, 528) ASTKIND(SPAWN_EXPR, "spawn_expr", SpawnExpr, 512) ASTKIND(SYNCHRONIZED_EXPR, "synchronized_expr", SynchronizedExpr, 512) ASTKIND(MACRO_EXPAND_EXPR, "macro_expand_expr", MacroExpandExpr, 1312) ASTKIND(IF_AVAILABLE_EXPR, "if_available_expr", IfAvailableExpr, 448) ASTKIND(INVALID_EXPR, "invalid_expr", InvalidExpr, 464) // end of subtypes of Expr. ASTKIND(GENERIC, "generic", Generic, 400) ASTKIND(GENERIC_CONSTRAINT, "generic_constraint", GenericConstraint, 384) ASTKIND(MATCH_CASE, "match_case", MatchCase, 360) ASTKIND(MATCH_CASE_OTHER, "match_case_other", MatchCaseOther, 296) ASTKIND(FUNC_ARG, "func_arg", FuncArg, 416) ASTKIND(FUNC_PARAM_LIST, "func_param_list", FuncParamList, 344) ASTKIND(FUNC_BODY, " func_body", FuncBody, 448) ASTKIND(STRUCT_BODY, "struct_body", StructBody, 328) ASTKIND(CLASS_BODY, "class_body", ClassBody, 328) ASTKIND(INTERFACE_BODY, "interface_body", InterfaceBody, 328) ASTKIND(DUMMY_BODY, "dummy_body", DummyBody, 256) ASTKIND(IMPORT_CONTENT, "import_content", ImportContent, 600) ASTKIND(IMPORT_SPEC, "import_spec", ImportSpec, 912) ASTKIND(PACKAGE_SPEC, "package_spec", PackageSpec, 464) ASTKIND(PACKAGE, "package", Package, 392) ASTKIND(FEATURE_ID, "feature_id", FeatureId, 304) ASTKIND(FEATURES_DIRECTIVE, "features_directive", FeaturesDirective, 304) ASTKIND(FILE, "file", File, 552) ASTKIND(NODE, "node", Node, 256) #endif cangjie_compiler-1.0.7/include/cangjie/AST/ASTTypeValidator.h000066400000000000000000000014151510705540100237720ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the class which used for validating correctness of ast sema ty. */ #ifndef CANGJIE_AST_TYPE_VALIDATOR_H #define CANGJIE_AST_TYPE_VALIDATOR_H #include "cangjie/AST/Node.h" #include "cangjie/AST/PrintNode.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/AST/ASTCasting.h" namespace Cangjie::AST { void ValidateUsedNodes(DiagnosticEngine& diagEngine, Package& pkg); } // namespace Cangjie::AST #endif cangjie_compiler-1.0.7/include/cangjie/AST/AttributePack.h000066400000000000000000000551131510705540100234010ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * Define all AST Attribute and helper classes. */ #ifndef CANGJIE_AST_ATTRIBUTE_H #define CANGJIE_AST_ATTRIBUTE_H #include #include #include #include #include #include namespace Cangjie::AST { using AttrSizeType = uint64_t; /** * Attribute fields size. */ constexpr AttrSizeType ATTR_SIZE = 64; /** * Node properties: decl modifiers, invalid status, etc. */ enum class Attribute { /** * Mark whether nominal types form cycles or whether type alias decls form a cycles; * Used when checking nominal and alias types. * W: PreCheck. * R: TypeManager, TypeChecker. */ IN_REFERENCE_CYCLE, /** * Mark whether code is unreachable. * W: Collector (Sema), TypeCheckMatchExpr, Utils (Sema), LLVMCodeGen. * R: AST2CHIR, LLVMCodeGen. * CONSIDER REMOVE THE ATTRIBUTE. READ ONLY ONCE. */ UNREACHABLE, /** * Mark whether the decl will be implicit used by other package. * W: Sema. * R: ASTSerialization. */ IMPLICIT_USED, /** * Mark whether a declaration is imported from other packages. Used by type checks, * W: HLIRCodeGen, HLIRCodeGen, LLVMCodeGen, ASTSerialization, ImportManager, GenericInstantiation * R: TypeChecker, ASTSerialization, FFISerialization, ADReverse, HLIRCodeGen, HLIR2LLVM. */ IMPORTED, /** * Mark whether a declaration is global. Used by ADReverse, AST2GIR, HLIR, LLVM, * W: ADReverse, CHIRRewrite, FFISerialization, Collector, TypeChecker * R: ADReverse, AST2CHIR, HLIRCodeGen, LLVMCodeGen, TypeChecker, * CompilerInstance */ GLOBAL, /** * Mark whether a variable declaration has been initialized. * W: ASTSerialization, ImportManager, CheckInitialization (TypeChecker). * R: CheckInitialization (TypeChecker). */ INITIALIZED, /** * Mark whether an AST Node is added by the compiler. * W: Parser, PreCheck, TypeChecker, Desugar, Collector, GIR2AST, Clone, Create, MacroExpansion. * R: ASTContext, ADReverse, PrintNode, TypeChecker. */ COMPILER_ADD, /** * Mark whether an AST Node is a generic declaration. * W: Parser, GenericInstantiation. * R: AST2CHIR, Walker, HLIRCodeGen, LLVMCodeGen, Sema, TypeManager, Collector, GenericInstantiation, * TypeChecker. */ GENERIC, // OPTIMIZE: use macro methods to keep consistent. /** * Mark whether a function in interface has default implementation * Or whether a vardecl has default initializer, eg: true if var a = 1 , false is let a : Int64 * W: Parser. * R: ASTSerialization, Sema, CHIR Rewrite */ DEFAULT, /** * Mark whether a member is a static one. * W: PreCheck, TypeChecker * R: AST2CHIR, HLIRCodeGen, LLVMCodeGen, parser, Collector, PreCheck, TypeChecker. */ STATIC, /** * Mark whether a member is a public one. * W: PreCheck * R: PreCheck, TypeChecker. */ PUBLIC, /** * Mark whether a member is a private one. * W: PreCheck. * R: LLVMCodeGen, CheckInitialization (Sema), PreCheck, TypeChecker, Utils (Sema). */ PRIVATE, /** * Mark whether a member is a protected one. * W: FFISerialization, PreCheck. * R: PreCheck, TypeChecker. */ PROTECTED, /** * Mark whether a declaration is an external one. * W: ADReverse, Parser, Desugar, PreCheck, TypeChecker. * R: ASTSerialization, TypeChecker, Types.cpp, AST2CHIR, ASTSerialization, Parser, PreCheck. */ EXTERNAL, /** * Mark whether a declaration is an internal one. * W: ADReverse, Parser, Desugar, PreCheck, TypeChecker. * R: ASTSerialization, TypeChecker, Types.cpp, AST2CHIR, ASTSerialization, Parser, PreCheck. */ INTERNAL, /** * Mark whether a declaration in fact overrides the inherited one (even if the user does not annotate '@override'). * W: TypeChecker. * R: PreCheck, TypeChecker. * Problems: How does it even happen since we have PreCheck --> TypeChecker? */ OVERRIDE, /** * Mark whether a declaration in fact overrides the inherited one (even if the user does not annotate '@redef'). * W: TypeChecker. * R: PreCheck, TypeChecker. * Problems: How does it even happen since we have PreCheck --> TypeChecker? */ REDEF, /** * Mark whether a function is an abstract one. * W: Parser. * R: ABSTRACT, AST2CHIR, CodeGenHLIR, FFTSerialization, PreCheck, TypeChecker. */ ABSTRACT, /** * Mark whether a declaration is a sealed one. * W: Parser. * R: TypeChecker. */ SEALED, /** * Mark whether a declaration is in fact open (even if the user does not annotate '@open'). * W: FFISerialization, PreCheck. * R: CHIRRewrite, TypeChecker. */ OPEN, /** * Mark whether a declaration is a operator one. * W: TypeChecker. * R: CodeGenLLVM, Parser, PreCheck, TypeChecker. */ OPERATOR, /** * Mark whether a declaration is a foreign one. * W: PreCheck. * R: AST2CHIR, CodeGenHLIR, CodeGenJs, WellFormednessChecker, Parser, TypeChecker, PreCheck. */ FOREIGN, /** * Mark whether a declaration is a unsafe one. * W: Parser, Desugar. * R: Parser, CheckInitialization (Sema), Desugar, TypeCHecker. */ UNSAFE, /** * Mark whether a declaration is a mutable one. * W: PreCheck. * R: AST2CHIR, CodeGenHLIR, TypeChecker. */ MUT, /** * Check if a node has broken child node. * W: Parser. * R: Parser. */ HAS_BROKEN, /** * Mark whether a declaration is defined within a Struct. * W: Parser, PreCheck, TypeChecker, Utils (Sema). * R: CheckInitialization (Sema), CodeGenHLIR, CodeGenLLVM, CHIRRewrite. */ IN_STRUCT, /** * Mark whether a declaration is defined within an extend. * W: PreCheck, TypeChecker. * R: TypeChecker, AST2CHIR, CodeGenHLIR. */ IN_EXTEND, /** * Mark whether a declaration is defined within an enum. * W: PreCheck. * R: Sema, AST2CHIR. */ IN_ENUM, /** * Mark whether a declaration is defined within an interface or class. * W: PreCheck, TypeChecker, Utils (Sema), ASTSerialization, Parser, Collector, Desugar. * R: PreCheck, TypeChecker, CodeGenHLIR, */ IN_CLASSLIKE, /** * Mark whether a Node is within a macro body. * W: None. * R: None. * Problems: It seems not used at all. */ IN_MACRO, /** * Mark whether a constructor is a primary one. * W: Desugar.DesugarPrimaryCtorSetPrimaryFunc (Sema). * R: CheckInitialization (Sema), TypeChecker. */ PRIMARY_CONSTRUCTOR, /** * Mark whether a function is a constructor. * W: Parser, Collector, Desugar, Utils (Sema) * R: AllCodeGen, Parser, Desugar, PreCheck, TypeChecker, CheckInitialization (Sema), Expand, AST2CHIR. * Problems: The same identifier is defined in Space.h and widely used in Space.cpp */ CONSTRUCTOR, /** * Mark whether a function is an enum constructor. * W: Collector. * R: AST2CHIR, PreCheck, TypeChecker, TypeManager, Space, LLVMCodeGen. * Problems: Attributes of the same kind are used 'randomly'... No rules and patterns. */ ENUM_CONSTRUCTOR, /** * Mark whether a function is finalizer. * W: Parser. * R: Parser. */ FINALIZER, /** * Mark whether source code is cloned. NEEDED BY LSP. * W: Clone, MacroExpansion, Parser, Utils (Sema). * R: None. */ IS_CLONED_SOURCE_CODE, /** * Mark whether a variable is captured by a lambda or a function. * W: TypeChecker. * R: TypeChecker, CodeGenLLVM, CodeGenHLIR. */ IS_CAPTURE, /** * Mark whether the target of the node is defined in core. * W: Desugar * R: Desugar */ IN_CORE, /** * Mark whether a type needs to be boxed into a class type (that has uniform memory representation). * W: Desugar. * R: Desugar. */ NEED_AUTO_BOX, /** * Mark whether a node is expanded from macros. * W: MacroExpansion. * R: None. */ MACRO_EXPANDED_NODE, /** * Mark whether a function definition is a macro definition. * W: Parser. * R: TypeChecker, Parser. */ MACRO_FUNC, /** * Mark whether a function definition is a wrapper function generated from some macro definition. * W: Parer. * R: CodeGenHLIR, TypeCheckExpr. */ MACRO_INVOKE_FUNC, /** * Mark whether a function definition' body is a wrapper function's body generated from some macro definition. * W: Parser. * R: TypeCheckExpr, CodeGenLLV, CodeGenHLIR. */ MACRO_INVOKE_BODY, /** * Mark whether an expression appears on the left-hand side of an assign expression. * W: Collector. * R: TypeCHeckExpr, TypeCheckExtend. */ LEFT_VALUE, // Native node kind, for @C etc. /** * Mark whether a node is c call. * W: Parser. * R: Parser, Sema, HLIRCodeGen. */ C, /** * Mark whether a function or variable is imported with definition body. * W: Modules. * R: CHIR */ SRC_IMPORTED, /** * Mark whether a node is foreign call. * W: Parser, AST2CHIR. * R: Parser, ImportManager, Sema, AST2CHIR, HLIRCodeGen. */ JAVA_APP, JAVA_EXT, /** * Mark whether a node is std call. * W: Parser. * R: HLIRCodeGen. */ STD_CALL, /** * Mark whether a node cannot mangle. * The attribute will be set in such cases: * - All kinds of CFunc, including foreign func, @C func, and CFunc lambda. * - Base autoboxed class declarations. * - Wrapper functions created when desugaring macro. * - Non-const and non-global VarDecl(Only temp var of StrInterpolationExpr desugar set for now). * W: Parser, Sema. * R: Sema, Mangler. */ NO_MANGLE, /** * Mark whether a node has been through initialization check to avoid infinite loop. * W: Sema. * R: Sema. */ INITIALIZATION_CHECKED, /** * Mark a broken node and no further checking is necessary. * W: Parser, Sema. * R: Parser, Sema. */ IS_BROKEN, /** * Mark whether this decl is an instantiated generic decl. * W: GenericInstantiator. * R: Sema, ImportManager, HLIRCodeGen, LLVMCodeGen. */ GENERIC_INSTANTIATED, /** * Mark whether param or arg has initial value. * W: Sema. * R: Sema, AST2CHIR, HLIRCodeGen, LLVMCodeGen. */ HAS_INITIAL, /** * Mark if foreign variable is volatile in cjo. * W: AUTOSDK. * R: Sema. */ IS_VOLATILE, /** * Mark whether the node use overflow strategy. * W: Parser, Sema. * R: Sema, HLIRCodeGen. */ NUMERIC_OVERFLOW, /** * Mark whether this function will be replaced with intrinsic. * W: Parser, Sema. * R: Sema, AST2CHIR, HLIRCodeGen, LLVMCodeGen. */ INTRINSIC, /** * Mark whether the node is generated by external tool. * W: cjogen, Parser. * R: Modules, Sema, ImportManager, HLIRCodeGen, Macro. */ TOOL_ADD, /** * Mark whether the node is visited by type check. * W: Sema, ImportManager (clear the attribute when export&import AST). * R: Sema. */ IS_CHECK_VISITED, /** * Mark whether 1. the node is a package and is going to be incremental compiled. * 2. the node is a decl which is incrementally cached and do not need typechecking and IR generation. * W: IncrementalCompilerInstance, ASTSerialization * R: GenericInstantiation. */ INCRE_COMPILE, /** * Mark whether the node is a call expr which is desugared call with side effect, and has 'mapExpr' in children. * W: Sema * R: AST2CHIR */ SIDE_EFFECT, /** * Mark whether the node is an additional node implicitly added by the compiler. * W: ImportManager, Desugar * R: ImportManager, CodeGen-Debug, CHIR. */ IMPLICIT_ADD, /** * Mark whether the func is desugared from main decl. * W: Desugar * R: Sema, CodeGen */ MAIN_ENTRY, /** * Mark those declarations or expressions which were generated for mocking purposes. * Such AST nodes aren't handled for further mocking preparations like replacing calls with accessors. * W: MockSupportManager, MockManager * R: MockSupportManager, MockManager */ GENERATED_TO_MOCK, /** * Mark whether the expression is the outermost binary expression. * For example, in `let _ = 1 + 2 * 3`, the addition is the outermost expression. * W: Sema * R: Sema */ IS_OUTERMOST, /** * Mark whether the decl is modified by @Annotation, or an array literal is a collection of annotations. * W: Sema * R: Sema */ IS_ANNOTATION, /** * Mark whether the decl needs reflect info. * W: Sema * R: Codegen */ NO_REFLECT_INFO, /** * Mark whether this class or package supports mocking. * If special compilation mode is used to compile some package, * then this flag should be set to each declaration of that package. * W: ASTLoader, MockSupportManager * R: ASTWriter, MockSupportManager, TestManager */ MOCK_SUPPORTED, /** * Mark whether this class was changed to open only for mocking purposes. * W: MockSupportManager * R: MockSupportManager */ OPEN_TO_MOCK, /** * Mark whether this function or property in extend body implement abstract function in inherited interfaces. * W: StructInheritanceChecker * R: AnalyzeFunctionLinkage */ INTERFACE_IMPL, /** * Mark whether the declaration is being compiled for using in tests. It enables extended exporting abilities. * W: DesugarAfterInstantiation * R: ImportManager */ FOR_TEST, /** * Mark whether this declaration contains createMock/createSpy calls inside. * W: TestManager * R: PartialInstantiation, TestManager */ CONTAINS_MOCK_CREATION_CALL, // For compatibility, new enumerated values need add last. /** * Mark whether it is "common" declaration. * W: Parser * R: ASTSerialization, SEMA, AST2CHIR */ COMMON, /** * Mark whether the decaration defined in common part of CP package. */ FROM_COMMON_PART, /** * Inform if `platform enum` matched with non-exhaustive `common enum` */ COMMON_NON_EXHAUSTIVE, /** * Mark whether it is "platform" declaration. * W: ASTSerialization * R: ASTChecker, AST2CHIR */ PLATFORM, /** * Mark node that is common decl with default implementation. * The decl is including common func/var/prop * W: Parser * R: SEMA, AST2CHIR */ COMMON_WITH_DEFAULT, /** * Mark whether a class is java mirror (binding for a java class). * W: Parser. * R: Sema. */ JAVA_MIRROR, /** * Mark whether a class is a successor of java mirror (direct or indirect child of java mirror class). * W: Parser, Sema. * R: Sema. */ JAVA_MIRROR_SUBTYPE, /** * Mark whether a class is an Objective-C mirror (binding for a Objective-C class). * W: Parser. * R: Sema. */ OBJ_C_MIRROR, /** * Mark whether a class is a successor of an Objective-C mirror (direct or indirect child of an Objective-C class). * W: Parser. * R: Sema. */ OBJ_C_MIRROR_SUBTYPE, /** * Mark whether a pure cangjie decl is mapped to use by java side. * W: Parser * R: Sema. */ JAVA_CJ_MAPPING, /** * Mark whether a node is a desugared mirror field decl. * Usually the node is a prop decl. * W: Parser. * R: Sema. */ DESUGARED_MIRROR_FIELD, AST_ATTR_END, }; static const std::unordered_map ATTR2STR{ {AST::Attribute::IN_REFERENCE_CYCLE, "IN_REFERENCE_CYCLE"}, {AST::Attribute::UNREACHABLE, "UNREACHABLE"}, {AST::Attribute::IMPLICIT_USED, "IMPLICIT_USED"}, {AST::Attribute::IMPORTED, "IMPORTED"}, {AST::Attribute::GLOBAL, "GLOBAL"}, {AST::Attribute::INITIALIZED, "INITIALIZED"}, {AST::Attribute::COMPILER_ADD, "COMPILER_ADD"}, {AST::Attribute::GENERIC, "GENERIC"}, {AST::Attribute::DEFAULT, "DEFAULT"}, {AST::Attribute::STATIC, "STATIC"}, {AST::Attribute::PUBLIC, "PUBLIC"}, {AST::Attribute::PRIVATE, "PRIVATE"}, {AST::Attribute::PROTECTED, "PROTECTED"}, {AST::Attribute::EXTERNAL, "EXTERNAL"}, {AST::Attribute::INTERNAL, "INTERNAL"}, {AST::Attribute::OVERRIDE, "OVERRIDE"}, {AST::Attribute::REDEF, "REDEF"}, {AST::Attribute::ABSTRACT, "ABSTRACT"}, {AST::Attribute::SEALED, "SEALED"}, {AST::Attribute::OPEN, "OPEN"}, {AST::Attribute::OPERATOR, "OPERATOR"}, {AST::Attribute::FOREIGN, "FOREIGN"}, {AST::Attribute::UNSAFE, "UNSAFE"}, {AST::Attribute::MUT, "MUT"}, {AST::Attribute::HAS_BROKEN, "HAS_BROKEN"}, {AST::Attribute::IN_STRUCT, "IN_STRUCT"}, {AST::Attribute::IN_EXTEND, "IN_EXTEND"}, {AST::Attribute::IN_ENUM, "IN_ENUM"}, {AST::Attribute::IN_CLASSLIKE, "IN_CLASSLIKE"}, {AST::Attribute::IN_MACRO, "IN_MACRO"}, {AST::Attribute::PRIMARY_CONSTRUCTOR, "PRIMARY_CONSTRUCTOR"}, {AST::Attribute::CONSTRUCTOR, "CONSTRUCTOR"}, {AST::Attribute::ENUM_CONSTRUCTOR, "ENUM_CONSTRUCTOR"}, {AST::Attribute::FINALIZER, "FINALIZER"}, {AST::Attribute::IS_CLONED_SOURCE_CODE, "IS_CLONED_SOURCE_CODE"}, {AST::Attribute::IS_CAPTURE, "IS_CAPTURE"}, {AST::Attribute::IN_CORE, "IN_CORE"}, {AST::Attribute::NEED_AUTO_BOX, "NEED_AUTO_BOX"}, {AST::Attribute::MACRO_EXPANDED_NODE, "MACRO_EXPANDED_NODE"}, {AST::Attribute::MACRO_FUNC, "MACRO_FUNC"}, {AST::Attribute::MACRO_INVOKE_FUNC, "MACRO_INVOKE_FUNC"}, {AST::Attribute::MACRO_INVOKE_BODY, "MACRO_INVOKE_BODY"}, {AST::Attribute::LEFT_VALUE, "LEFT_VALUE"}, {AST::Attribute::C, "C"}, {AST::Attribute::SRC_IMPORTED, "SRC_IMPORTED"}, {AST::Attribute::JAVA_APP, "JAVA_APP"}, {AST::Attribute::JAVA_EXT, "JAVA_EXT"}, {AST::Attribute::STD_CALL, "STD_CALL"}, {AST::Attribute::NO_MANGLE, "NO_MANGLE"}, {AST::Attribute::INITIALIZATION_CHECKED, "INITIALIZATION_CHECKED"}, {AST::Attribute::IS_BROKEN, "IS_BROKEN"}, {AST::Attribute::GENERIC_INSTANTIATED, "GENERIC_INSTANTIATED"}, {AST::Attribute::HAS_INITIAL, "HAS_INITIAL"}, {AST::Attribute::IS_VOLATILE, "IS_VOLATILE"}, {AST::Attribute::NUMERIC_OVERFLOW, "NUMERIC_OVERFLOW"}, {AST::Attribute::INTRINSIC, "INTRINSIC"}, {AST::Attribute::TOOL_ADD, "TOOL_ADD"}, {AST::Attribute::IS_CHECK_VISITED, "IS_CHECK_VISITED"}, {AST::Attribute::INCRE_COMPILE, "INCRE_COMPILE"}, {AST::Attribute::SIDE_EFFECT, "SIDE_EFFECT"}, {AST::Attribute::IMPLICIT_ADD, "IMPLICIT_ADD"}, {AST::Attribute::MAIN_ENTRY, "MAIN_ENTRY"}, {AST::Attribute::GENERATED_TO_MOCK, "GENERATED_TO_MOCK"}, {AST::Attribute::IS_OUTERMOST, "IS_OUTERMOST"}, {AST::Attribute::IS_ANNOTATION, "IS_ANNOTATION"}, {AST::Attribute::NO_REFLECT_INFO, "NO_REFLECT_INFO"}, {AST::Attribute::MOCK_SUPPORTED, "MOCK_SUPPORTED"}, {AST::Attribute::OPEN_TO_MOCK, "OPEN_TO_MOCK"}, {AST::Attribute::INTERFACE_IMPL, "INTERFACE_IMPL"}, {AST::Attribute::FOR_TEST, "FOR_TEST"}, {AST::Attribute::CONTAINS_MOCK_CREATION_CALL, "CONTAINS_MOCK_CREATION_CALL"}, {AST::Attribute::COMMON, "COMMON"}, {AST::Attribute::FROM_COMMON_PART, "FROM_COMMON_PART"}, {AST::Attribute::COMMON_NON_EXHAUSTIVE, "COMMON_NON_EXHAUSTIVE"}, {AST::Attribute::PLATFORM, "PLATFORM"}, {AST::Attribute::COMMON_WITH_DEFAULT, "COMMON_WITH_DEFAULT"}, {AST::Attribute::JAVA_MIRROR, "JAVA_MIRROR"}, {AST::Attribute::JAVA_MIRROR_SUBTYPE, "JAVA_MIRROR_SUBTYPE"}, {AST::Attribute::OBJ_C_MIRROR, "OBJ_C_MIRROR"}, {AST::Attribute::OBJ_C_MIRROR_SUBTYPE, "OBJ_C_MIRROR_SUBTYPE"}, {AST::Attribute::JAVA_CJ_MAPPING, "JAVA_CJ_MAPPING"}, {AST::Attribute::DESUGARED_MIRROR_FIELD, "DESUGARED_MIRROR_FIELD"}, {AST::Attribute::AST_ATTR_END, "AST_ATTR_END"}, }; class AttributePack { public: AttributePack() : attributes(attrSetSize) { } explicit AttributePack(const std::vector>& bitSets) : attributes(bitSets) { attributes.resize(attrSetSize); // Guarantees the container's size is always enough. } ~AttributePack() = default; /** * Set the attributes in @p attrs to @c true. */ void SetAttr(Attribute attr, bool enable) { size_t idx = static_cast(attr) / ATTR_SIZE; (void)attributes[idx].set(static_cast(attr) % ATTR_SIZE, enable); } /** * Check whether the @p attr is @c true. */ bool TestAttr(Attribute attr) const { size_t idx = static_cast(attr) / ATTR_SIZE; return idx < attributes.size() && attributes[idx].test(static_cast(attr) % ATTR_SIZE); } std::vector> GetRawAttrs() const { return attributes; } std::vector GetAllIdxOfAttr() const { std::vector enableAttrIdxs; for (auto attr : attributes) { for (AttrSizeType i = 0; i < ATTR_SIZE; ++i) { if (!attr[i]) { continue; } enableAttrIdxs.emplace_back(i); } } return enableAttrIdxs; } std::string ToString() const { if (ATTR2STR.size() != static_cast(AST::Attribute::AST_ATTR_END) + 1) { return "ATTR2STR has invalid mapping(" + std::to_string(ATTR2STR.size()) + "!=" + std::to_string(static_cast(AST::Attribute::AST_ATTR_END) + 1) + ")"; } std::vector enableAttrIdxs = GetAllIdxOfAttr(); std::stringstream ret; ret << "["; for (auto i : enableAttrIdxs) { ret << ATTR2STR.at(static_cast(i)); if (i != enableAttrIdxs.back()) { ret << ", "; } } ret << "]"; return ret.str(); } private: static constexpr size_t attrSetSize = static_cast(Attribute::AST_ATTR_END) / ATTR_SIZE + 1; std::vector> attributes; }; } // namespace Cangjie::AST #endif cangjie_compiler-1.0.7/include/cangjie/AST/Cache.h000066400000000000000000000046621510705540100216450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares cache for type check. */ #ifndef CANGJIE_AST_CACHE_H #define CANGJIE_AST_CACHE_H #include "cangjie/AST/Types.h" #include "cangjie/Basic/DiagnosticEngine.h" namespace Cangjie::AST { using TargetCache = std::pair, Ptr>; struct CacheEntry { bool successful = false; Ptr result = nullptr; DiagnosticCache diags; TargetCache targets; }; struct CacheKey { Ptr target; bool isDesugared; DiagnosticCache::DiagCacheKey diagKey; bool operator==(const CacheKey& b) const; }; struct CacheKeyHash { size_t operator()(const CacheKey& key) const { auto v = std::hash>()(key.target); v = hash_combine(v, key.isDesugared); v = hash_combine(v, key.diagKey); return v; } }; /* type check cache for one AST node */ struct TypeCheckCache { std::unordered_map synCache; std::unordered_map chkCache; std::optional lastKey{}; }; // member signature information available by just syntax check struct MemSig { std::string id; bool isVarOrProp; size_t arity = 0; // arity in case of member function, otherwise 0; variadic arg not considered size_t genArity = 0; // number of possible explicit generic args in case of member function, otherwise 0 // note that all generic func can possibly have 0 explicit gen args bool operator==(const MemSig& b) const; }; struct MemSigHash { size_t operator()(const MemSig& sig) const { auto v = std::hash()(sig.id); v = hash_combine(v, sig.isVarOrProp); v = hash_combine(v, sig.arity); v = hash_combine(v, sig.genArity); return v; } }; // Collect and restore necessary target decls in the sub-tree. // Most targets are needed only after post-check, when they are filled by the normal procedure. // Currently, this cache is only for checking enum constructor without type args. TargetCache CollectTargets(const AST::Node& node); void RestoreTargets(AST::Node& node, const TargetCache& targets); } // namespace Cangjie #endif cangjie_compiler-1.0.7/include/cangjie/AST/Clone.h000066400000000000000000000264501510705540100217010ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares AST clone apis. Moreover, a callback function is provided as * a parameter @p visitor which supports doing something during clone period. */ #ifndef CANGJIE_AST_CLONE_H #define CANGJIE_AST_CLONE_H #include #include #include "cangjie/AST/NodeX.h" namespace Cangjie::AST { using VisitFunc = std::function; void DefaultVisitFunc(const Node& source, const Node& target); void SetIsClonedSourceCode(const Node& source, Node& target); void CopyBasicInfo(Ptr source, Ptr target); MacroInvocation CloneMacroInvocation(const MacroInvocation& me); OwnedPtr CloneGeneric(const Generic& generic, const VisitFunc& visitor = DefaultVisitFunc); class ASTCloner { public: template static std::vector> CloneVector(const std::vector>& nodes) { std::vector> resNodes; for (auto& it : nodes) { (void)resNodes.emplace_back(Clone(it.get())); } return resNodes; } template static OwnedPtr Clone(Ptr node, const VisitFunc& visitFunc = DefaultVisitFunc) { OwnedPtr clonedNode = ASTCloner().CloneWithRearrange(node, visitFunc); return OwnedPtr(static_cast(clonedNode.release())); } private: /** Map between 'pointer to source node pointer' to 'pointer to cloned node pointer'. */ std::unordered_map*, Ptr*> targetAddr2targetAddr; /** Map bewteen 'source node pointer' to 'cloned node pointer'. */ std::unordered_map, Ptr> source2cloned; template void TargetAddrMapInsert(Ptr& from, Ptr& target) { if (from == nullptr) { return; } targetAddr2targetAddr[reinterpret_cast*>(&from)] = reinterpret_cast*>(&target); } OwnedPtr CloneWithRearrange(Ptr node, const VisitFunc& visitor = DefaultVisitFunc); /** Get a cloned unique_ptr of a given node. */ template static OwnedPtr CloneNode(Ptr node, const VisitFunc& visitor = DefaultVisitFunc); static OwnedPtr CloneType(Ptr type, const VisitFunc& visitor = DefaultVisitFunc); template static OwnedPtr CloneExpr(Ptr expr, const VisitFunc& visitor = DefaultVisitFunc); static OwnedPtr CloneDecl(Ptr decl, const VisitFunc& visitor = DefaultVisitFunc); static OwnedPtr ClonePattern(Ptr pattern, const VisitFunc& visitor = DefaultVisitFunc); static OwnedPtr CloneQualifiedType(const QualifiedType& node, const VisitFunc& visitor); static OwnedPtr CloneParenType(const ParenType& node, const VisitFunc& visitor); static OwnedPtr CloneOptionType(const OptionType& node, const VisitFunc& visitor); static OwnedPtr CloneFuncType(const FuncType& node, const VisitFunc& visitor); static OwnedPtr CloneTupleType(const TupleType& node, const VisitFunc& visitor); static OwnedPtr CloneConstantType(const ConstantType& node, const VisitFunc& visitor); static OwnedPtr CloneVArrayType(const VArrayType& node, const VisitFunc& visitor); static OwnedPtr CloneRefType(const RefType& type, const VisitFunc& visitor); static OwnedPtr CloneMacroExpandExpr(const MacroExpandExpr& mee, const VisitFunc& visitor); static OwnedPtr CloneTokenPart(const TokenPart& tp, const VisitFunc& visitor); static OwnedPtr CloneQuoteExpr(const QuoteExpr& qe, const VisitFunc& visitor); static OwnedPtr CloneIfExpr(const IfExpr& ie, const VisitFunc& visitor); static OwnedPtr CloneTryExpr(const TryExpr& te, const VisitFunc& visitor); static OwnedPtr CloneThrowExpr(const ThrowExpr& te, const VisitFunc& visitor); static OwnedPtr ClonePerformExpr(const PerformExpr& pe, const VisitFunc& visitor); static OwnedPtr CloneResumeExpr(const ResumeExpr& re, const VisitFunc& visitor); static OwnedPtr CloneReturnExpr(const ReturnExpr& re, const VisitFunc& visitor); static OwnedPtr CloneWhileExpr(const WhileExpr& we, const VisitFunc& visitor); static OwnedPtr CloneDoWhileExpr(const DoWhileExpr& dwe, const VisitFunc& visitor); static OwnedPtr CloneAssignExpr(const AssignExpr& ae, const VisitFunc& visitor); static OwnedPtr CloneIncOrDecExpr(const IncOrDecExpr& ide, const VisitFunc& visitor); static OwnedPtr CloneUnaryExpr(const UnaryExpr& ue, const VisitFunc& visitor); static OwnedPtr CloneBinaryExpr(const BinaryExpr& be, const VisitFunc& visitor); static OwnedPtr CloneRangeExpr(const RangeExpr& re, const VisitFunc& visitor); static OwnedPtr CloneSubscriptExpr(const SubscriptExpr& se, const VisitFunc& visitor); static OwnedPtr CloneMemberAccess(const MemberAccess& ma, const VisitFunc& visitor); static OwnedPtr CloneCallExpr(const CallExpr& ce, const VisitFunc& visitor); static OwnedPtr CloneParenExpr(const ParenExpr& pe, const VisitFunc& visitor); static OwnedPtr CloneLambdaExpr(const LambdaExpr& le, const VisitFunc& visitor); static OwnedPtr CloneLitConstExpr(const LitConstExpr& lce, const VisitFunc& visitor); static OwnedPtr CloneArrayLit(const ArrayLit& al, const VisitFunc& visitor); static OwnedPtr CloneArrayExpr(const ArrayExpr& ae, const VisitFunc& visitor); static OwnedPtr ClonePointerExpr(const PointerExpr& ptre, const VisitFunc& visitor); static OwnedPtr CloneTupleLit(const TupleLit& tl, const VisitFunc& visitor); static OwnedPtr CloneRefExpr(const RefExpr& re, const VisitFunc& visitor); static OwnedPtr CloneForInExpr(const ForInExpr& fie, const VisitFunc& visitor); static OwnedPtr CloneMatchExpr(const MatchExpr& me, const VisitFunc& visitor); static OwnedPtr CloneJumpExpr(const JumpExpr& je); static OwnedPtr CloneTypeConvExpr(const TypeConvExpr& tce, const VisitFunc& visitor); static OwnedPtr CloneSpawnExpr(const SpawnExpr& se, const VisitFunc& visitor); static OwnedPtr CloneSynchronizedExpr(const SynchronizedExpr& se, const VisitFunc& visitor); static OwnedPtr CloneInvalidExpr(const InvalidExpr& ie); static OwnedPtr CloneInterpolationExpr(const InterpolationExpr& ie, const VisitFunc& visitor); static OwnedPtr CloneStrInterpolationExpr( const StrInterpolationExpr& sie, const VisitFunc& visitor); static OwnedPtr CloneTrailingClosureExpr( const TrailingClosureExpr& tc, const VisitFunc& visitor); static OwnedPtr CloneIsExpr(const IsExpr& ie, const VisitFunc& visitor); static OwnedPtr CloneAsExpr(const AsExpr& ae, const VisitFunc& visitor); static OwnedPtr CloneOptionalExpr(const OptionalExpr& oe, const VisitFunc& visitor); static OwnedPtr CloneOptionalChainExpr(const OptionalChainExpr& oce, const VisitFunc& visitor); static OwnedPtr CloneLetPatternDestructor( const LetPatternDestructor& ldp, const VisitFunc& visitor); static OwnedPtr CloneIfAvailableExpr(const IfAvailableExpr& e, const VisitFunc& visitor); static OwnedPtr CloneConstPattern(const ConstPattern& cp, const VisitFunc& visitor); static OwnedPtr CloneVarPattern(const VarPattern& vp, const VisitFunc& visitor); static OwnedPtr CloneTuplePattern(const TuplePattern& tp, const VisitFunc& visitor); static OwnedPtr CloneTypePattern(const TypePattern& tp, const VisitFunc& visitor); static OwnedPtr CloneEnumPattern(const EnumPattern& ep, const VisitFunc& visitor); static OwnedPtr CloneExceptTypePattern(const ExceptTypePattern& etp, const VisitFunc& visitor); static OwnedPtr CloneCommandTypePattern( const CommandTypePattern& ctp, const VisitFunc& visitor); static OwnedPtr CloneVarOrEnumPattern(const VarOrEnumPattern& vep, const VisitFunc& visitor); static OwnedPtr CloneBlock(const Block& block, const VisitFunc& visitor = DefaultVisitFunc); static OwnedPtr CloneClassBody(const ClassBody& cb, const VisitFunc& visitor); static OwnedPtr CloneStructBody(const StructBody& sb, const VisitFunc& visitor); static OwnedPtr CloneInterfaceBody(const InterfaceBody& ib, const VisitFunc& visitor); static OwnedPtr CloneGenericConstraint( const GenericConstraint& gc, const VisitFunc& visitor = DefaultVisitFunc); static OwnedPtr CloneFuncBody(const FuncBody& fb, const VisitFunc& visitor = DefaultVisitFunc); static OwnedPtr CloneFuncParam(const FuncParam& fp, const VisitFunc& visitor = DefaultVisitFunc); static OwnedPtr CloneFuncParamList( const FuncParamList& fpl, const VisitFunc& visitor = DefaultVisitFunc); static OwnedPtr CloneFuncArg(const FuncArg& fa, const VisitFunc& visitor = DefaultVisitFunc); static OwnedPtr CloneAnnotation( const Annotation& annotation, const VisitFunc& visitor = DefaultVisitFunc); static OwnedPtr CloneImportSpec(const ImportSpec& is, const VisitFunc& visitor = DefaultVisitFunc); static OwnedPtr CloneMatchCase(const MatchCase& mc, const VisitFunc& visitor = DefaultVisitFunc); static OwnedPtr CloneMatchCaseOther( const MatchCaseOther& mco, const VisitFunc& visitor = DefaultVisitFunc); static OwnedPtr CloneGenericParamDecl(const GenericParamDecl& gpd); static OwnedPtr CloneVarWithPatternDecl(const VarWithPatternDecl& vwpd, const VisitFunc& visitor); static OwnedPtr CloneVarDecl(const VarDecl& vd, const VisitFunc& visitor); static OwnedPtr CloneFuncDecl(const FuncDecl& fd, const VisitFunc& visitor); static OwnedPtr ClonePrimaryCtorDecl(const PrimaryCtorDecl& pcd, const VisitFunc& visitor); static OwnedPtr ClonePropDecl(const PropDecl& pd, const VisitFunc& visitor); static OwnedPtr CloneExtendDecl(const ExtendDecl& ed, const VisitFunc& visitor); static OwnedPtr CloneMacroExpandDecl(const MacroExpandDecl& med); static OwnedPtr CloneStructDecl(const StructDecl& sd, const VisitFunc& visitor); static OwnedPtr CloneClassDecl(const ClassDecl& cd, const VisitFunc& visitor); static OwnedPtr CloneInterfaceDecl(const InterfaceDecl& id, const VisitFunc& visitor); static OwnedPtr CloneEnumDecl(const EnumDecl& ed, const VisitFunc& visitor); static OwnedPtr CloneTypeAliasDecl(const TypeAliasDecl& tad, const VisitFunc& visitor); }; } // namespace Cangjie::AST #endif // CANGJIE_AST_CLONE_H cangjie_compiler-1.0.7/include/cangjie/AST/Comment.h000066400000000000000000000051151510705540100222360ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares cache for node. */ #ifndef CANGJIE_AST_COMMENT_H #define CANGJIE_AST_COMMENT_H #include #include "cangjie/Lex/Token.h" namespace Cangjie::AST { enum class CommentStyle : uint8_t { LEAD_LINE, TRAIL_CODE, OTHER, }; enum class CommentKind : uint8_t { LINE, BLOCK, DOCUMENT, // block comment started with "/**" e.g. /**xxxx*/. exclude: start with "/***", empty comment "/**/". }; struct Comment { CommentStyle style; CommentKind kind; Token info; std::string ToString() const; }; /// e.g. /// // line 1 /// // line 2 /// main() { /*block 1*/ // line 3 /// // line 4 /// // line 6 /// return 0 /// } // group 1: line 1, line 2, group 2: block 1, line 3, group 3: line 4, line6 struct CommentGroup { std::vector cms; bool IsEmpty() const { return cms.empty(); } std::string ToString() const; }; /// /// Comments are classified into leadingComments, innerComments and trailingComments based on the location relationship /// among nodes and comments, For details, see the description in AttachComment.cpp. /// e.g. /// /** c0 lead classDecl of class A */ /// class A { // c1 lead var decl of a /// // c2 lead varDecl of a /// var a = 1 // c3 trail varDecl of a /// // c4 trail varDecl of a /// } // c5 trail classDecl of A /// // c6 lead funcDecl of foo /// func foo(/* c7 inner funcParamList of foo */) /// { /// } /// // c8 trail funcDecl of foo /// /// main() { /// 0 /// } /// struct CommentGroups { std::vector leadingComments; std::vector innerComments; std::vector trailingComments; bool IsEmpty() const { return leadingComments.empty() && innerComments.empty() && trailingComments.empty(); } std::string ToString() const; }; /** * all comment groups in the token stream and location-related information */ struct CommentGroupsLocInfo { std::vector commentGroups; // key: groupIndex value: preTokenIndex in tokenStream(ignore nl, semi, comment) std::unordered_map cgPreInfo; // key: groupIndex value: followTokenIndex in tokenStream(ignore nl, comment, end) std::unordered_map cgFollowInfo; const std::vector& tkStream; }; } #endif // CANGJIE_AST_COMMENT_Hcangjie_compiler-1.0.7/include/cangjie/AST/Create.h000066400000000000000000000126121510705540100220370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares AST create apis. */ #ifndef CANGJIE_AST_CREATE_H #define CANGJIE_AST_CREATE_H #include #include #include "cangjie/AST/Clone.h" #include "cangjie/AST/Node.h" namespace Cangjie::AST { void CopyNodeScopeInfo(Ptr ret, Ptr e); void CopyFileID(Ptr ret, Ptr e); void CopyNodeWithFileID(Ptr ret, Ptr e); /** Create an expression node of type Unit. */ OwnedPtr CreateUnitExpr(Ptr ty = nullptr); OwnedPtr CreateBoolLit(bool isTrue); OwnedPtr CreateForInExpr( OwnedPtr&& pattern, OwnedPtr&& inExpression, OwnedPtr&& body); OwnedPtr CreateBreakExpr(Expr& refLoop); OwnedPtr CreateCallExpr(OwnedPtr funcExpr, std::vector> args, Ptr resolvedFunc = nullptr, Ptr ty = nullptr, CallKind callTy = CallKind::CALL_INVALID); OwnedPtr CreateFuncArgForOptional(const FuncParam& param); OwnedPtr CreateFuncParamForOptional(const FuncParam& param); /** Create FuncParamList node. */ OwnedPtr CreateFuncParamList( std::vector> params, const VisitFunc& visitor = DefaultVisitFunc); /** Create Type in core node. */ OwnedPtr CreateRefTypeInCore(const std::string& name); OwnedPtr CreateTmpVarDecl(Ptr type = nullptr, Ptr initializer = nullptr); OwnedPtr CreateVarPattern(const std::string& varName, Ptr ty = nullptr); /** Create RefExpr node. */ OwnedPtr CreateRefExpr( const SrcIdentifier& id, Ptr ty = nullptr, const Position& pos = {0, 0, 0}, std::vector> args = {}); /** * Create a compiler-generated RefExpr node. This node may not have proper location info. * Note that this funnction do not add COMPILER_ADD attribute to the result expr. */ OwnedPtr CreateRefExpr(const std::string& name, const Position& pos = {0, 0, 0}); OwnedPtr CreateRefExprInCore(const std::string& name); OwnedPtr CreateRefExprInAST(const std::string& name); OwnedPtr CreateRefExpr(Decl& vd); /// \p pos copy begin and end from node \ref pos OwnedPtr CreateRefExpr(Decl& vd, const Node& pos); /** Create RefType node */ OwnedPtr CreateRefType(const std::string& refName, std::vector> args = {}); OwnedPtr CreateRefType(InheritableDecl& typeDecl); /** Create MemberAccess node with given target sema. */ OwnedPtr CreateMemberAccess(OwnedPtr expr, Decl& field); OwnedPtr CreateMemberAccess(OwnedPtr expr, const std::string& field); /** Create CType Generic Constraint */ OwnedPtr CreateConstraintForFFI(const std::string& upperBound); OwnedPtr CreateMatchCase(OwnedPtr pattern, OwnedPtr expr); OwnedPtr CreateMatchExpr(OwnedPtr selector, std::vector> matchCases, Ptr ty, Expr::SugarKind sugarKind = Expr::SugarKind::NO_SUGAR); OwnedPtr CreateLitConstExpr( LitConstKind kind, const std::string& val, Ptr ty, bool needToMakeRef = false ); OwnedPtr CreateTupleLit(std::vector> elements, Ptr ty); OwnedPtr CreateArrayLit(std::vector> elements, Ptr ty); OwnedPtr CreateTupleAccess(OwnedPtr expr, size_t index); OwnedPtr CreateUnaryExpr(OwnedPtr expr, TokenKind op); OwnedPtr CreateBinaryExpr( OwnedPtr leftExpr, OwnedPtr rightExpr, TokenKind op); OwnedPtr CreateReturnExpr(OwnedPtr expr, Ptr refFuncBody = nullptr); OwnedPtr CreateLambdaExpr(OwnedPtr funcBody); OwnedPtr CreateAssignExpr( OwnedPtr leftValue, OwnedPtr rightExpr, Ptr ty = nullptr); OwnedPtr CreateFuncArg(OwnedPtr expr, const std::string& argName = "", Ptr ty = nullptr); OwnedPtr CreateFuncDecl( const std::string& funcName, OwnedPtr body = nullptr, Ptr ty = nullptr); OwnedPtr CreateFuncBody(std::vector> paramLists, OwnedPtr retType, OwnedPtr body, Ptr ty = nullptr); OwnedPtr CreateFuncParam(const std::string& paramName, OwnedPtr paramType = nullptr, OwnedPtr paramValue = nullptr, Ptr ty = nullptr); OwnedPtr CreateFuncParamList(std::vector> params, Ptr ty = nullptr); OwnedPtr CreateBlock(std::vector> nodes, Ptr ty = nullptr); OwnedPtr CreateIfExpr(OwnedPtr condExpr, OwnedPtr body, OwnedPtr elseBody = nullptr, Ptr semaType = nullptr); OwnedPtr CreateVarDecl( const std::string& varName, OwnedPtr initializer = nullptr, Ptr type = nullptr); OwnedPtr CreateThrowExpr(Decl& var); OwnedPtr CreatePerformExpr(Decl& var); OwnedPtr CreateResumeExpr(Decl& var); OwnedPtr CreateTypePattern( OwnedPtr&& pattern, OwnedPtr&& type, Expr& selector ); } // namespace Cangjie::AST #endif // CANGJIE_AST_CREATE_H cangjie_compiler-1.0.7/include/cangjie/AST/Identifier.h000066400000000000000000000124321510705540100227160ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_AST_IDENTIFIER #define CANGJIE_AST_IDENTIFIER #include #include "cangjie/Basic/Position.h" #include "cangjie/Utils/CheckUtils.h" namespace Cangjie { /** * Data class that stores the string value after NFC transformation, start location and length of a source code * identifier. Note that the length of source code identifier may not equal to the length of \ref value because * of NFC transformation. */ struct Identifier { Identifier() : v{} { } Identifier(std::string s, const Position& begin, const Position& end); /** * helper function, to be used as the string value of the identifier */ operator const std::string&() const { return v; } ///@{ /** * Assignment from string, string_view, or const char[N]. These assignment functions assert that \p identifier * is in NFC form. */ Identifier& operator=(std::string_view identifier); template Identifier& operator=(const char (&identifier)[N]) { return (*this = std::string_view(identifier)); } Identifier& operator=(const std::string& identifier); Identifier& operator=(std::string&& identifier); Identifier& operator=(const Identifier& other) = default; Identifier& operator=(Identifier&& other) = default; Identifier(const Identifier& other) = default; Identifier(Identifier&& other) = default; ///@} const std::string& Val() const { return v; } bool Valid() const; bool Empty() const { return v.empty(); } /**< clear identifier string value and length */ void Clear() { v = {}; beginPos = INVALID_POSITION; endPos = INVALID_POSITION; } inline friend bool operator==(const std::string& l, const Identifier& r); bool operator==(std::string_view other) const { return v == other; } bool operator!=(std::string_view other) const { return !(*this == other); } bool operator==(const Identifier& other) const { return v == other.v; } bool operator!=(const Identifier& other) const { return !(*this == other); } inline friend std::string operator+(const std::string& prefix, const Identifier& r); std::string operator+(const std::string& postfix) const { return v + postfix; } Identifier& operator+=(const std::string& other) { std::string v1{std::move(v) + other}; SetValue(std::move(v1)); return *this; } const Position& Begin() const { return beginPos; } const Position& End() const { return endPos; } void SetPos(const Position& begin, const Position& end) { beginPos = begin; endPos = end; } void SetFileID(unsigned fileID) { beginPos.fileID = endPos.fileID = fileID; } size_t Length() const { CJC_ASSERT(beginPos.line == endPos.line); return static_cast(static_cast(endPos.column - beginPos.column)); } /**< check position is zero */ bool ZeroPos() const { return beginPos.IsZero(); } friend std::ostream& operator<<(std::ostream& out, const Identifier& identifier); private: std::string v; /**< string value of the identifier */ Position beginPos{INVALID_POSITION}; /**< start location */ Position endPos{INVALID_POSITION}; void SetValue(std::string&& s); // private setter for debug issue }; inline bool operator==(const std::string& l, const Identifier& r) { return l == r.v; } inline std::string operator+(const std::string& prefix, const Identifier& r) { return prefix + r.v; } /** * A source identifier differs from its base class \ref Identifier in that it can be a rawIdentifier (i.e. surrounded * by a pair of backquotes). */ struct SrcIdentifier : public Identifier { SrcIdentifier() : Identifier{}, raw{false} { } explicit SrcIdentifier(std::string s) : Identifier{std::move(s), INVALID_POSITION, INVALID_POSITION}, raw{false} { } SrcIdentifier(std::string s, const Position& begin, const Position& end, bool isRaw) : Identifier{std::move(s), begin, end}, raw{isRaw} { } bool IsRaw() const { return raw; } void SetRaw(bool v) { raw = v; } std::string GetRawText() const { return raw ? "`" + Val() + "`" : Val(); } Position GetRawPos() const { return raw ? Begin() - 1 : Begin(); } Position GetRawEndPos() const { return raw ? End() + 1 : End(); } ///@{ /** * Assignment from raw string. This function asserts \p v conforms to NFC and the identifier is not raw. */ using Identifier::operator=; SrcIdentifier& operator=(const SrcIdentifier& other) = default; SrcIdentifier& operator=(SrcIdentifier&& other) = default; SrcIdentifier(const SrcIdentifier& other) = default; SrcIdentifier(SrcIdentifier&& other) = default; ///@} private: bool raw; /**< is raw identifier */ }; } #endif cangjie_compiler-1.0.7/include/cangjie/AST/IntLiteral.h000066400000000000000000000100621510705540100227000ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares int literal apis. */ #ifndef CANGJIE_INTLITERAL_H #define CANGJIE_INTLITERAL_H #include #include "cangjie/AST/Types.h" namespace Cangjie::AST { class IntLiteral { public: IntLiteral() = default; IntLiteral(const std::string& stringVal, const TypeKind kind) : type(kind) { InitIntLiteral(stringVal, kind); } IntLiteral(const uint64_t val, const TypeKind kind, bool overflow, bool max = false) : int64Val(static_cast(val)), uint64Val(val), outOfRange(overflow), outOfMax(max), type(kind) { // Calc wrapping value and saturating value. CalcWrappingAndSaturatingVal(); } IntLiteral(const int64_t val, const TypeKind kind, bool overflow, bool max = false) : int64Val(val), uint64Val(static_cast(val)), outOfRange(overflow), outOfMax(max), type(kind) { sign = (IsUnsigned() || int64Val >= 0) ? 1 : -1; CalcWrappingAndSaturatingVal(); } ~IntLiteral() = default; void Assign(const IntLiteral& other) { sign = other.sign; int64Val = other.int64Val; uint64Val = other.uint64Val; outOfRange = other.outOfRange; type = other.type; } int Sign() const { return sign; } void SetSign(int input) { sign = input; } int64_t Int64() const { return int64Val; } void SetInt64(int64_t input) { int64Val = input; uint64Val = static_cast(input); } uint64_t Uint64() const { return uint64Val; } void SetUint64(uint64_t input) { uint64Val = input; int64Val = static_cast(input); } void CalcWrappingAndSaturatingVal(); void SetWrappingValue(); void SetSaturatingValue(); std::string GetValue() const; bool IsOutOfRange() const { return outOfRange; } bool IsNegativeNum() const { return sign == -1 && uint64Val != 0; } void InitIntLiteral(const std::string& stringVal, const TypeKind kind); bool GreaterThanOrEqualBitLen(const TypeKind kind) const; IntLiteral operator-() const; IntLiteral operator~() const; IntLiteral operator+(const IntLiteral& rhs) const; IntLiteral operator-(const IntLiteral& rhs) const; IntLiteral operator*(const IntLiteral& rhs) const; IntLiteral operator/(const IntLiteral& rhs) const; IntLiteral operator%(const IntLiteral& rhs) const; IntLiteral operator>>(const IntLiteral& rhs) const; IntLiteral operator<<(const IntLiteral& rhs) const; IntLiteral operator&(const IntLiteral& rhs) const; IntLiteral operator^(const IntLiteral& rhs) const; IntLiteral operator|(const IntLiteral& rhs) const; IntLiteral PowerOf(const IntLiteral& exponent) const; static int EscapeCharacterToInt(char c); void SetOutOfRange(Ptr ty); private: bool CheckOverflow(); bool IsUnsigned() const { return type >= TypeKind::TYPE_UINT8 && type <= TypeKind::TYPE_UINT64; } uint64_t QuickPow(const uint64_t inBase, const uint64_t inExp, const uint64_t maxVal, bool& overflow) const; uint64_t GetAbsValue() const; int sign{1}; int64_t int64Val{0}; uint64_t uint64Val{0}; int64_t sint64Val{0}; // saturating int value if outOfRange is true uint64_t suint64Val{0}; // saturating uint value if outOfRange is true int64_t wint64Val{0}; // wrapping int value if outOfRange is true uint64_t wuint64Val{0}; // wrapping uint value if outOfRange is true bool outOfRange{false}; // outOfMax is useful only if outOfRange is true bool outOfMax{false}; // outOfMax=true means Maximum overflow, otherwise Minimum overflow TypeKind type{TypeKind::TYPE_IDEAL_INT}; }; } // namespace Cangjie::AST #endif // CANGJIE_INTLITERAL_H cangjie_compiler-1.0.7/include/cangjie/AST/Match.h000066400000000000000000000031031510705540100216630ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares AST match apis, and some useful macros. */ #ifndef CANGJIE_AST_MATCH_H #define CANGJIE_AST_MATCH_H #include "cangjie/AST/NodeX.h" #include "cangjie/AST/ASTCasting.h" namespace Cangjie::AST { /** * ASTKind to Node type mapping. */ template struct NodeKind {}; #define ASTKIND(KIND, VALUE, NODE, SIZE) \ template <> struct NodeKind { \ using Type = AST::NODE; \ }; #include "cangjie/AST/ASTKind.inc" #undef ASTKIND /** * Convert Node to certain ASTKind, use static_cast as possible. * @param node Node to be convert. * @return The AST Node of kind @p Kind. */ template auto As(Ptr node) { return DynamicCast::Type*>(node.get()); } /** * Convert Node to certain ASTKind, use static_cast. * @param node Node to be convert. * @return The AST Node of kind @p Kind. */ template inline auto StaticAs(NodeT node) { return StaticCast::Type*>(node); } } // namespace Cangjie::AST #endif // CANGJIE_AST_MATCH_H cangjie_compiler-1.0.7/include/cangjie/AST/Node.h000066400000000000000000003155071510705540100215320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * AST nodes mainly includes three kinds of nodes: Decl, Expr and Type. And we keep as much source code information as * possible, so it's easy to convert AST back to source code. */ #ifndef CANGJIE_AST_NODE_H #define CANGJIE_AST_NODE_H #include #include #include #include #include #include #include #include #include #include #include "cangjie/AST/AttributePack.h" #include "cangjie/AST/Comment.h" #include "cangjie/AST/Identifier.h" #include "cangjie/AST/IntLiteral.h" #include "cangjie/AST/Types.h" #include "cangjie/Basic/Linkage.h" #include "cangjie/Basic/Position.h" #include "cangjie/Lex/Token.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/ConstantsUtils.h" namespace Cangjie { enum class ScopeKind : uint8_t; enum class ExprKind : uint8_t; namespace AST { struct Symbol; struct Expr; struct FuncArg; struct Decl; struct FuncDecl; struct StructDecl; struct CallExpr; struct ArrayLit; struct RefExpr; struct NameReferenceExpr; struct Pattern; struct Package; struct File; struct LitConstExpr; struct Block; struct PropDecl; struct MacroInvocation; struct LambdaExpr; enum class BuiltInType : uint8_t; using TyVar = GenericsTy; using TyVarEnv = std::map, std::set>>; /** * ASTKind of the Node. */ enum class ASTKind : uint8_t { #define ASTKIND(KIND, VALUE, NODE, SIZE) KIND, #include "cangjie/AST/ASTKind.inc" #undef ASTKIND }; /** * Mapping of ASTKind to string. */ const static std::unordered_map ASTKIND_TO_STR = { #define ASTKIND(KIND, VALUE, NODE, SIZE) {ASTKind::KIND, VALUE}, #include "cangjie/AST/ASTKind.inc" #undef ASTKIND }; /** * ForInKind of the ForInExpr. */ enum class ForInKind : uint8_t { FORIN_INVALID, FORIN_RANGE, FORIN_STRING, FORIN_ITER }; // Used to convert unique_ptr vector to pointer vector. template std::vector> ConverVector(const std::vector>& source) { std::vector> result; auto convertPtr = [&result](const OwnedPtr& ptr) { result.push_back(ptr.get()); }; std::for_each(source.begin(), source.end(), convertPtr); return result; } /** * Base struct for all nodes of the abstract syntax tree. All nodes contain position information marking the beginning * of the corresponding source text segment. */ struct Node { Position begin; /**< The begin Position of the Node. Generated by parser */ Position end; /**< The end Position of the Node. Generated by parser */ CommentGroups comments; /**< Comments attached to the node. Generated by parser */ /** * The semantic type of the Node. * W: ASTContext, Clone, Create, Expand, CodeGenHLIR (PrepareImportedAST), ASTSerialization (Load), * FFTSerialization, Sema. * R: Clone, Create, Expand, PrintNode, CodeGenCHIR, CodeGenHLIR, CodeGenLLVM, ASTSerialization * (Save) FFTSerialization, Sema, Mangle, Utils/Utils. * Problems: CodeGenHLIR etc should not write this field. * */ Ptr ty{Ty::GetInitialTy()}; /** * The Symbol of the Node. * W: Collector. * R: ASTContext, Assumption, CheckInitialization, Search, TypeChecker (few). * Problems: Why do we even need Symbol? */ Symbol* symbol{nullptr}; /** * Managed by ScopeManager. * W: Create, Collector, Desugar. * R: PreCheck, ScopeManager, AST2CHIR, Assumption, TypeChecker, CheckInitialization (Sema), CodeGenHLIR, * CheckTypeCompatible, Collector, Desugar, PrintNode. */ std::string scopeName{""}; /** * Id used for export and import, should be unique value for each decl in single package. * NOTE: For cjo's compatibility of different version, the exportId must be decl's signature. * W: Utils/Mangle.cpp, ASTSerialization * R: ASTSerialization */ std::string exportId{""}; /** Current file node. * W: Desugar, Create, CompilerStrategy, ASTSerialization, MacroExpansion, Parse, Collector, TypeChecker. * R: Desugar, PreCheck, Create, CodeGenHLIR, CompilerStrategy, ImportManager, Collector, TypeChecker. * Problems: Why so many modules write this field? * Generated by parser */ Ptr curFile{nullptr}; /** * Current macro call node. * W: MacroExpansion. * R: TypeChecker, Collector. */ Ptr curMacroCall{nullptr}; /** * Scope level, import scope is 0, each file has separate import scope. Package's toplevel scope is 1. * W: Collector, Create, Desugar. * R: TypeChecker, Create, ScopeManager, AST2CHIR, PreCheck, Sema. */ uint32_t scopeLevel{0}; /** * A flag set by Walker, indicating this node is visited by walker. * W: Walker. * R: Walker. */ mutable unsigned visitedByWalkerID{0}; /** * A flag set by Walker, indicating this node is in macrocall for lsp. * W: Sema. * R: Searcher, DiagnosticEngine. */ bool isInMacroCall{false}; /** * W: On construction. * R: AST2CHIR, All CodeGen, Macro, Serialization, ImportManager, Parse, Sema * Generated by parser * NOTE: This must be const, cannot be modified outside constructor. */ const ASTKind astKind; Node() : astKind(ASTKind::NODE) { } virtual ~Node(); virtual std::string ToString() const { return ""; } /** * Get the @p attr. */ const AttributePack GetAttrs() const { return attributes; } void CopyAttrs(const AttributePack& attrs) { attributes = attrs; } void CloneAttrs(const Node& other) { attributes = other.attributes; } /** * Set the attributes in @p attrs to @c true. */ template void EnableAttr(Args... attrs) noexcept { (attributes.SetAttr(attrs, true), ...); } /** * Set the attributes in @p attrs to @c false. */ template void DisableAttr(Args... attrs) noexcept { (attributes.SetAttr(attrs, false), ...); } /** * Check whether the @p attr is @c true. */ bool TestAttr(Attribute attr) const noexcept { return attributes.TestAttr(attr); } /* Check whether all given attributes are enabled. */ template bool TestAttr(Attribute attr, Args&&... args) const noexcept { return TestAttr(attr) && TestAttr(std::forward(args)...); } /* Check whether any of the given attributes is enabled. */ template bool TestAnyAttr(Attribute attr, Args&&... args) const noexcept { bool ret = TestAttr(attr); if constexpr (sizeof...(args) != 0) { ret = ret || TestAnyAttr(std::forward(args)...); } return ret; } /** * Remove some compiler added information. */ virtual void Clear() noexcept { DisableAttr(Attribute::INITIALIZED); DisableAttr(Attribute::INITIALIZATION_CHECKED); DisableAttr(Attribute::IS_CHECK_VISITED); ty = Ty::GetInitialTy(); } bool IsStructOrClassDecl() const noexcept { return astKind == ASTKind::CLASS_DECL || astKind == ASTKind::STRUCT_DECL; } /** * Whether a declaration is a struct declaration. */ bool IsNominalDecl() const noexcept { return astKind == ASTKind::CLASS_DECL || astKind == ASTKind::INTERFACE_DECL || astKind == ASTKind::STRUCT_DECL || astKind == ASTKind::EXTEND_DECL || astKind == ASTKind::ENUM_DECL; } /** * Whether a node is ClassBody, InterfaceBody, StructBody or DummyBody for 'extend' and 'enum'. */ bool IsNominalDeclBody() const noexcept { return astKind == ASTKind::CLASS_BODY || astKind == ASTKind::INTERFACE_BODY || astKind == ASTKind::STRUCT_BODY || astKind == ASTKind::DUMMY_BODY; } /** * Whether a declaration is ClassDecl or InterfaceDecl. */ bool IsClassLikeDecl() const noexcept { return astKind == ASTKind::CLASS_DECL || astKind == ASTKind::INTERFACE_DECL; } /** * Whether a node is ClassBody or InterfaceBody. */ bool IsClassLikeDeclBody() const noexcept { return astKind == ASTKind::CLASS_BODY || astKind == ASTKind::INTERFACE_BODY; } /** * Whether the Node is FuncDecl or LambdaExpr. */ bool IsFuncLike() const noexcept { return astKind == ASTKind::FUNC_DECL || astKind == ASTKind::LAMBDA_EXPR || astKind == ASTKind::PRIMARY_CTOR_DECL || astKind == ASTKind::MACRO_DECL; } /** * Whether the Node is FuncDecl or LambdaExpr. */ bool IsFunc() const noexcept { return astKind == ASTKind::FUNC_DECL; } /** * Whether a node is function or PropDecl. */ bool IsFuncOrProp() const noexcept { return astKind == ASTKind::FUNC_DECL || astKind == ASTKind::PROP_DECL; } /** * Whether a node is Decl. */ bool IsDecl() const noexcept { return astKind >= ASTKind::DECL && astKind <= ASTKind::INVALID_DECL; } /** * Whether a node is Expr. */ bool IsExpr() const noexcept { return astKind >= ASTKind::EXPR && astKind <= ASTKind::INVALID_EXPR; } /** * Whether the Node is Invalid Expr or Invalid Decl. */ bool IsInvalid() const noexcept { return astKind == ASTKind::INVALID_DECL || astKind == ASTKind::INVALID_EXPR || astKind == ASTKind::INVALID_TYPE; } /** * Whether the Node is a loop expr. */ bool IsLoopExpr() const noexcept { return astKind == ASTKind::FOR_IN_EXPR || astKind == ASTKind::WHILE_EXPR || astKind == ASTKind::DO_WHILE_EXPR; } /** * Whether a declaration is static or global. * @return true if a declaration is either static or global. */ bool IsStaticOrGlobal() const noexcept { return this->TestAttr(Attribute::GLOBAL) || this->TestAttr(Attribute::STATIC); } bool IsTypeDecl() const noexcept { return astKind == ASTKind::CLASS_DECL || astKind == ASTKind::INTERFACE_DECL || astKind == ASTKind::STRUCT_DECL || astKind == ASTKind::ENUM_DECL || astKind == ASTKind::TYPE_ALIAS_DECL || astKind == ASTKind::GENERIC_PARAM_DECL || astKind == ASTKind::BUILTIN_DECL; } /** * Whether the Node is Macro Expand Node. */ bool IsMacroCallNode() const noexcept { return astKind == ASTKind::MACRO_EXPAND_DECL || astKind == ASTKind::MACRO_EXPAND_EXPR || astKind == ASTKind::MACRO_EXPAND_PARAM; } /** * Whether the node has open semantics. * That is, it is a open class or interface, or an open/abstract func. * The behaviour is UNDEFINED if the node is invalid. */ virtual bool IsOpen() const noexcept { return false; } /** * Should diagnostic engine emit a diagnose message for this node. * Common usage: Diagnose(node.ShouldDiagnose(), ...) * @param allowCompilerAdd if true, do not check whether this is node is added by the compiler * @return false if this->TestAttr(Attribute::COMPILER_ADD) or at least one child's ty is invalid, true otherwise */ bool ShouldDiagnose(bool allowCompilerAdd = false) const; /** * Check @p other node is in same package. */ bool IsSamePackage(const Node& other) const; /** * Get node's targets decl. */ std::vector> GetTargets() const; /** * Get node's target decl. */ Ptr GetTarget() const; /** * Set node's target decl. */ void SetTarget(Ptr target); /** * Get a MacroInvocation ptr. * @return MacroInvocation ptr if a node is MacroExpandExpr or MacroExpandDecl, * nullptr otherwise. */ Ptr GetConstInvocation() const; /** * Get a MacroInvocation ptr. * @return MacroInvocation ptr if a node is MacroExpandExpr or MacroExpandDecl, * nullptr otherwise. */ Ptr GetInvocation(); /** * Get the begin Position of the Node. * @return begin Position in macrocall file if the Node is expanded from macrocall, * begin position in curfile otherwise. */ Position GetBegin() const; /** * Get the end Position of the Node. * @return end Position in macrocall file if the Node is expanded from macrocall, * end position in curfile otherwise. */ Position GetEnd() const; /** * Get the sourcePos of macrocall by originPos in curfile. * @return the sourcePos in macrocall file if the Node is expanded from macrocall, * originPos in curfile otherwise. */ Position GetMacroCallPos(Position originPos, bool isLowerBound = false) const; /** * Get the new Position of macrocall in curfile by originPos before the macro is expanded, for lsp. * @return new Position of macrocall in curfile if the Node is MacroExpandExpr/MacroExpandDecl or in macrocall, * INVALID_POSITION otherwise. */ Position GetMacroCallNewPos(const Position& originPos); /** * For debug, * get the original Position of the node if it is from MacroCall in curfile, * curPos otherwise. */ Position GetDebugPos(const Position& curPos) const; const std::string& GetFullPackageName() const; protected: explicit Node(ASTKind kind) : astKind(kind) { } private: AttributePack attributes; }; /** * normal string, JString, multiple lines, or multiple lines of string. */ enum class StringKind { NORMAL, JSTRING, MULTILINE, MULTILINE_RAW }; /** * Expression has suffix '?' or not. */ enum class SuffixKind { NONE, QUEST }; /** * Access control modifiers */ struct Modifier : Node { TokenKind modifier; /**< Modifier token. */ bool isExplicit = false; Modifier(TokenKind kind, const Position& pos) : Node(ASTKind::MODIFIER), modifier(kind) { begin = pos; end = {pos.fileID, pos.line, pos.column + Len(kind)}; isExplicit = true; } bool operator<(const Modifier& rhs) const { return (modifier < rhs.modifier); } std::string ToString() const override; }; enum class AnnotationKind { JAVA, CALLING_CONV, C, JAVA_MIRROR, JAVA_IMPL, OBJ_C_MIRROR, OBJ_C_IMPL, FOREIGN_NAME, ATTRIBUTE, /** * NOTE: 'OVERFLOW' is a macro in early versions of glibc (VERSION < 2.27). * To be compatiable with old versions of glibc we should avoid using 'OVERFLOW' as an identifier. * For details, please check 'https://man7.org/linux/man-pages/man3/matherr.3.html'. */ NUMERIC_OVERFLOW, INTRINSIC, WHEN, FASTNATIVE, ANNOTATION, // Indicate @Annotation. CUSTOM, // Indicate a Custom Annotation, such as @JsonName. CONSTSAFE, DEPRECATED, FROZEN, ENSURE_PREPARED_TO_MOCK, UNKNOWN }; using AnnotationTargetT = uint16_t; /** * Target of `@Annotation`. */ enum class AnnotationTarget : AnnotationTargetT { TYPE = 0, PARAMETER, INIT, MEMBER_PROPERTY, MEMBER_FUNCTION, MEMBER_VARIABLE, ENUM_CONSTRUCTOR, GLOBAL_FUNCTION, GLOBAL_VARIABLE, EXTEND, }; /** * Built-in or custom Annotation. */ struct Annotation : public Node { bool isCompileTimeVisible{}; /**< True when @! Annotation. */ AnnotationKind kind; /**< Generated by parser */ Identifier identifier; /**< Generated by parser */ Position lsquarePos; std::vector> args; /**< Arguments in order. Generated by parser */ Position rsquarePos; OverflowStrategy overflowStrategy{OverflowStrategy::NA}; /**< Generated by parser */ Identifier adAnnotation; /**< Generated by parser, Optional, consider delete */ std::vector attrs; /**< attributes for @Attribute, Generated by parser. */ std::vector attrCommas; /**< commas between attributes, Generated by parser. */ // For j-foreign inter operation. std::string definedPackage; /**< Generated by parser, Optional */ // For When Annotations. OwnedPtr condExpr; /**< Generated by parser, Optional */ // For @Annotation. uint16_t target = 0; // For imported j-foreign annotations. // It is true only if the annotation is defined with `@Retention(RetentionPolicy.RUNTIME)`. bool runtimeVisible = false; // For Custom Annotation. OwnedPtr baseExpr; /**< Generated by parser after macro */ Annotation(const std::string& name, AnnotationKind annoKind, const Position& pos) : Node(ASTKind::ANNOTATION), kind(annoKind), identifier{name, INVALID_POSITION, INVALID_POSITION} { begin = pos; } Annotation() : Node(ASTKind::ANNOTATION) { } bool TestTarget(AnnotationTarget t) const noexcept { CJC_ASSERT(static_cast(t) < static_cast(std::numeric_limits>::digits)); return static_cast(target & (static_cast(1) << static_cast(t))); } void EnableTarget(AnnotationTarget t) noexcept { CJC_ASSERT(static_cast(t) < static_cast(std::numeric_limits>::digits)); target |= static_cast( static_cast(1) << static_cast(t)); } void EnableAllTargets() noexcept { target = static_cast(~0u); } bool operator<(const Annotation& rhs) const { return (kind < rhs.kind); } }; /** * Reference represent a map of identifier and target, which is determined by sema. */ struct Reference { /**< Reference's name. (When it's text is raw identifier the raw mark "``" will be removed. * e.g., name of "`type_1`" is "type_1".) Generated by Parser. */ SrcIdentifier identifier; Ptr target{nullptr}; /**< Target of the Reference. */ std::vector> targets; /**< Overloaded Functions or super class/interfaces. */ Reference() = default; explicit Reference(const SrcIdentifier& id) : identifier{id} { } explicit Reference(SrcIdentifier&& id) : identifier{std::move(id)} { } explicit Reference(const std::string& name) : identifier{name, INVALID_POSITION, INVALID_POSITION, false} { } }; /** * Base type node. */ struct Type : Node { ~Type() override = default; Position commaPos; /**< Generated by parser, Optional */ Position bitAndPos; /**< Generated by parser, Optional */ // The following three properties are used for type parameters, // e.g in a tuple type "(p1:Int64, `p2`:Int64)" // "p1" and "p2" is typeParameterName // "`p2`" typeParameterName is "p2" generated by raw identifier // colonPos is the position of `:` // typePos is the position of `Int64` Position colonPos; /**< Generated by parser, Optional */ Position typePos; /**< Generated by parser, Optional */ /**< TypeParameter's name. (When it's raw identifier the raw mark "``" will be removed. * e.g., name of "`type_1`" is "type_1".) Generated by parser. */ std::string typeParameterName; /**< Generated by parser, Optional */ /**< If typeParameter's name text is raw identifer, it's name text is composed of an ordinary identifier or * a keyword wrapped by a pair of backquotes. e.g.,"`type_1`". Generated by parser. */ bool typeParameterNameIsRawId{false}; Type() : Node(ASTKind::TYPE) { } void Clear() noexcept override { Node::Clear(); } virtual std::vector> GetTypeArgs() const { return {}; } std::string GetTypeParameterNameRawText() const { return typeParameterNameIsRawId ? ("`" + typeParameterName + "`") : typeParameterName; } protected: Type(ASTKind kind) : Node(kind) { } }; /** * Bad type node emitted on parse errors. */ struct InvalidType : Type { explicit InvalidType(const Position& pos) : Type(ASTKind::INVALID_TYPE) { EnableAttr(Attribute::IS_BROKEN); begin = pos; end = pos; } }; /** * Represents a reference type. */ struct RefType : Type { Reference ref; /**< Reference of the type. */ Position leftAnglePos; /**< Position of '<'. */ std::vector> typeArguments; /**< Instantiation type. */ Position rightAnglePos; /**< Position of '>'. */ RefType() : Type(ASTKind::REF_TYPE) { } void Clear() noexcept override { Type::Clear(); ref.target = nullptr; ref.targets.clear(); } std::string ToString() const override; std::vector> GetTypeArgs() const override { return ConverVector(typeArguments); } /** * Check if type target is generic ThisType. */ bool IsGenericThisType() const; }; /** * Represents This type. */ struct ThisType : Type { explicit ThisType(const Position& pos) : Type(ASTKind::THIS_TYPE) { begin = pos; end = begin; end.column += Len(TokenKind::THISTYPE); } }; /** * Represents primitive type, including Rune, bool, Int32, etc. */ struct PrimitiveType : Type { std::string str; /**< Primitive type string represents. */ TypeKind kind{TypeKind::TYPE_INVALID}; /**< TYPE_INT8, TYPE_INT16, etc. */ PrimitiveType() : Type(ASTKind::PRIMITIVE_TYPE) { } std::string ToString() const override { return str; } }; /** * Represents a parenthesized type which is used for type conversion. */ struct ParenType : Type { Position leftParenPos; /**< Position of '('. */ OwnedPtr type; /**< Type between '(' and ')'. */ Position rightParenPos; /**< Position of ')'. */ ParenType() : Type(ASTKind::PAREN_TYPE) { } }; /** * Represents a Nested Type with scope like obja.objb.c, @p baseType is obja.objb, @p field is c. */ struct QualifiedType : Type { OwnedPtr baseType; /**< Split from last '.', take the left parts. Generated by parser*/ Position dotPos; /**< Position of the '.'. Generated by parser*/ /**< Split from last '.', take the right parts. When it's text raw identifier the raw mark "``" will be removed. * e.g., name of "`type_1`" is "type_1".) Generated by parser */ SrcIdentifier field; Position leftAnglePos; /**< Position of '<' Generated by parser,Optional*/ std::vector> typeArguments; /**< Instantiation type. Generated by parser,Optional*/ Position rightAnglePos; /**< Position of '>' Generated by parser,Optional*/ Ptr target{nullptr}; /**< Target of the QualifiedType. */ QualifiedType() : Type(ASTKind::QUALIFIED_TYPE) { } void Clear() noexcept override { Type::Clear(); target = nullptr; } std::vector> GetTypeArgs() const override { return ConverVector(typeArguments); } /** * Get the field Position of the QualifiedType. * @return field Position in macrocall file if the QualifiedType is expanded from macrocall, * field position in curfile otherwise. */ Position GetFieldPos() const; }; /** * A OptionType node represents the syntactic sugar of an option type, example: `?T` represents Option. */ struct OptionType : Type { OwnedPtr componentType; /**< The component type of the option type. Generated by parser*/ unsigned int questNum{0}; /**< Number of the quest. Generated by parser*/ std::vector questVector; /**< Positions of '?'. Generated by parser*/ OwnedPtr desugarType; /**< Desugar type of '?T'. */ OptionType() : Type(ASTKind::OPTION_TYPE) { } }; /** * A ConstantType node represents the constant value start with '$' in generic parameter list. * Currently, this type is used only in VArrayType. */ struct ConstantType : public Type { OwnedPtr constantExpr; /**< Expression after the '$'. */ Position dollarPos; /**< Positions of '$'. */ ConstantType() : Type(ASTKind::CONSTANT_TYPE) { } }; /** * Represents an VArray Type. 'begin' is also the position of 'VArray'. */ struct VArrayType : public Type { Position varrayPos; /**< Positions of 'VArray'. */ Position leftAnglePos; /**< Positions of '<'. */ OwnedPtr typeArgument; /**< The component type of the array. */ OwnedPtr constantType; /**< ConstantType */ Position rightAnglePos; /**< Positions of '>'. */ VArrayType() : Type(ASTKind::VARRAY_TYPE) { } }; /** * A FuncType node represents a function type, example: `(Int32, Int32)->Int32`, @p paramType is (Int32, Int32), @p * retType is Int32. */ struct FuncType : Type { Position leftParenPos; /**< Position of '('. */ std::vector> paramTypes; /**< Splitted by comma, type(s) of parameter(s) */ Position rightParenPos; /**< Position of ')'. */ Position arrowPos; /**< Position of '->'. */ OwnedPtr retType; /**< Split from last "->", take the right parts. */ FuncType() : Type(ASTKind::FUNC_TYPE) { } bool isC{false}; }; /** * A TupleType node represents a tuple type defined by type*type*type. */ struct TupleType : Type { Position leftParenPos; /**< Position of '('. Generated by parser */ Position rightParenPos; /**< Position of ')'. Generated by parser*/ std::vector> fieldTypes; /**< Type of each filed in tuple. Generated by parser*/ std::vector commaPosVector; /**< Positions of ','. Generated by parser*/ TupleType() : Type(ASTKind::TUPLE_TYPE) { } }; /** * The inheritance node has been visited or under visiting. */ enum class InheritanceVisitStatus { UNVISITED, /**< Not visit yet. */ VISITING, /**< Visiting now. */ VISITED, /**< Has been visited. */ }; /** * Represents Generic Constraint. */ struct GenericConstraint : public Node { Position wherePos; /**< The position of where. */ OwnedPtr type; /**< Type variable. */ Position operatorPos; /**< The position of upper bound operator. */ std::vector bitAndPos; /**< The position of '&'. */ std::vector> upperBounds; /**< Upper bounds, class, interface */ Position commaPos; /**< The position of where. */ GenericConstraint() : Node(ASTKind::GENERIC_CONSTRAINT) { } }; /** * Represents Generic. */ struct Generic : public Node { Position leftAnglePos; /**< The position of left angle bracket. */ std::vector> typeParameters; /**< Generic type parameters. */ Position rightAnglePos; /**< The position of right angle bracket. */ std::vector> genericConstraints; /**< Constraints of type parameters. */ TyVarEnv assumptionCollection; Generic() : Node(ASTKind::GENERIC) { } }; struct DeclHash { size_t instVar{0}; /**< Hash of type declaration's data layout (instantce member variables). */ size_t virt{0}; /**< Hash of member's which allowing dynamic dispatch (virtual functions and properties). */ size_t sig{0}; /**< the API of a decl (e.g. name of named parameter). Need recompile user*/ size_t srcUse{0}; /**< the ABI (e.g. foreign, @Annotation) and source usage of a decl (e.g. public). */ size_t bodyHash{0}; /**< Change of this part does not propagate to users. */ int gvid{0}; /** GlobalVarOrder. no file here as it is stored in Decl*/ }; /** * Base decl node. */ struct Decl : Node { std::set modifiers; /**< Modifier set of the Decl. Generated by parser */ std::vector> annotations; /**< Annotation set of the Decl. Generated by parser */ OwnedPtr annotationsArray; /**< Custom annotations of the Decl. Generated by Sema. */ /**< Decl's name. (When it's text is raw identifier, the raw mark "``" will be removed. * e.g., name of "`type_1`" is "type_1".) Generated by parser. */ SrcIdentifier identifier; OwnedPtr generic; /**< Generated by parser , Optional */ std::string identifierForLsp; /**< Decl's raw name, identifier is changed such as PropDecl. */ Position keywordPos; /**< Position of the keyword, like var, func, etc. Generated by parser */ std::string rawMangleName; // mangle name generated by parser, used by incremental compile // hash values, used by incremental compile DeclHash hash{}; bool toBeCompiled{false}; /** The name after mangling. * W: AST2CHIR, CodeGenHLIR, CodeGenJS, CodeGenLLVM, ASTSerialization, Utils.Mangle, * Parser.CheckRenameAnnotationKind * R: AST2CHIR, CodeGenHLIR, CodeGenJS, CodeGenLLVM, ASTSerialization, Utils.Mangle */ std::string mangledName; std::string moduleName; /**< The root package name of current package. */ std::string fullPackageName; /**< The full package name of current package. */ Ptr outerDecl{nullptr}; /**< Pointer to current outer declaration. Generated by parser */ Ptr genericDecl{nullptr}; /**< Pointer to generic declaration if it is an instantiated one. */ Linkage linkage{Linkage::EXTERNAL}; /**< The linkage of a decl. Default: external */ bool doNotExport{false}; /**< If the flag is true, the Decl will not be exported. */ Ptr platformImplementation; mutable std::vector> dependencies; /**< Variables that the current one depends on */ Decl() : Node(ASTKind::DECL) { } ~Decl() override = default; InheritanceVisitStatus checkFlag{InheritanceVisitStatus::UNVISITED}; unsigned captureIndex = 0; /**< The index in the function context class. */ /** * Whether a declaration is a member declaration. */ bool IsMemberDecl() const noexcept { return outerDecl && outerDecl->IsNominalDecl(); } /** * Check whether contains the @p anno. */ bool HasAnno(AnnotationKind anno) const { for (auto& it : annotations) { if (it->kind == anno) return true; } return false; } void Clear() noexcept override { checkFlag = InheritanceVisitStatus::UNVISITED; Node::Clear(); } virtual std::vector>& GetMemberDecls() { static std::vector> empty; return empty; }; const std::vector>& GetMemberDecls() const { return const_cast(this)->GetMemberDecls(); }; std::vector> GetMemberDeclPtrs() const; bool IsBuiltIn() const; /** * Get a generic decl's generic declaration. */ Ptr GetGeneric() const; /** * Get the identifier Position of the Decl. * @return identifier Position in macrocall file if the Decl is expanded from macrocall, * identifier position in curfile otherwise. */ Position GetIdentifierPos() const; /** * Check if a declaration can be exported. * @return true if declaration can be exported. */ virtual bool IsExportedDecl() const; /** * Check if a declaration is marked with 'const'. * @return true if the declaration is marked with 'const'. */ bool IsConst() const; /** * Get the desugar decl of this decl * @return the ptr of desugar decl if it exist, nullptr otherwise. */ Ptr GetDesugarDecl() const; bool IsCommonOrPlatform() const; bool IsCommonMatchedWithPlatform() const; protected: Decl(ASTKind kind) : Node(kind) { } }; struct InheritableDecl : public Decl { Position upperBoundPos; /**< Position of <:. */ std::vector> inheritedTypes; /**< Super class or super interfaces. */ std::set> GetSuperInterfaceTys() const; std::vector> GetStableSuperInterfaceTys() const; // guarantees sub-types always exist before super-types std::vector> GetAllSuperDecls(); protected: InheritableDecl(ASTKind kind) : Decl(kind) { } }; enum class BuiltInType : uint8_t { ARRAY, POINTER, CSTRING, CFUNC, VARRAY, // Will have other builtin types. }; /** * Built-in decl is for built-in types, such as Array, CString. */ struct BuiltInDecl : public Decl { BuiltInType type; BuiltInDecl(BuiltInType type) : Decl(ASTKind::BUILTIN_DECL), type(type) { } bool IsType(BuiltInType ty) const { return ty == type; } }; /** * A GenericParamDecl node represents a generic type decl in generic function, class, interface. */ struct GenericParamDecl : public Decl { Position commaPos; /**< The comma position of generic parameters. Generated by parser, Optional */ GenericParamDecl() : Decl(ASTKind::GENERIC_PARAM_DECL) { } }; struct VarDeclAbstract : Decl { OwnedPtr type; /**< Generated by parser */ Position colonPos; /**< Position of ':'. Generated by parser */ OwnedPtr initializer; /**< Initialize Expr. Generated by parser*/ Position assignPos; /**< Position of '='. */ bool isVar{false}; /**< To distinguish @c Let or @c Var. */ bool isConst{false}; /**< A flag to mark whether the decl is const. Generated by parser */ protected: VarDeclAbstract(ASTKind astKind) : Decl(astKind) { } }; /** * A VarDecl node represents a *local* variable declaration. */ struct VarDecl : VarDeclAbstract { Ptr parentPattern{nullptr}; /**< Save the target pattern if the varDecl is in pattern. */ bool isIdentifierCompilerAdd{false}; /**< Is the identifier is add by compiler. */ bool isResourceVar{false}; bool isMemberParam{false}; /**< identify member variable parameter. Generated by Parser. LSP will use. */ VarDecl() : VarDeclAbstract(ASTKind::VAR_DECL) { } std::string ToString() const override; protected: VarDecl(ASTKind kind) : VarDeclAbstract(kind) { } }; struct VarWithPatternDecl : VarDeclAbstract { OwnedPtr irrefutablePattern; /**< Irrefutable pattern. */ VarWithPatternDecl() : VarDeclAbstract(ASTKind::VAR_WITH_PATTERN_DECL) { } }; /** * A parameter to a method or lambda. For most semantic / codegen purposes, a parameter is equivalent to a variable, but * its assignment is different from a normal initializer. */ struct FuncParam : VarDecl { Position notMarkPos; /**< Position of '!'. Generated by Parser */ OwnedPtr assignment; /**< Init the param. Generated by Parser */ Position commaPos; /**< Position of ','. Generated by Parser */ OwnedPtr desugarDecl; /**< Desugar default param function. */ bool isNamedParam{false}; /**< identify named parameter. Generated by Parser */ bool hasLetOrVar{false}; /**< identify 'let' or 'var' in parameter list. Generated by Parser */ FuncParam() : VarDecl(ASTKind::FUNC_PARAM) { } void Clear() noexcept override { Decl::Clear(); DisableAttr(Attribute::HAS_INITIAL); } protected: explicit FuncParam(ASTKind astKind) : VarDecl(astKind) { } }; /** * Empty DummyBody scope for LSP split decl scopes. */ struct DummyBody : Node { DummyBody() : Node(ASTKind::DUMMY_BODY) { } }; /** * An EnumDecl node represents an enum declaration. */ struct EnumDecl : InheritableDecl { /** * EnumDecl will be parsed according to the following rules: * 1. If there are NO associated values, then constructors in enumBody will be parsed to cases of VarLikeDecl * 2. If there are associated values, then constructors in enumBody will be parsed to cases of FuncDecl * 3. Members are functions defined in the enum */ Position leftCurlPos; /**< The position of '{'. */ OwnedPtr bodyScope; /**< split body scope for LSP. */ std::vector> constructors; /**< VarDecl or FuncDecl. */ std::vector bitOrPosVector; /**< Positions of '|'. */ std::vector> members; /**< Functions in the enum. */ Position rightCurlPos; /**< The position of '}'. */ bool hasArguments{false}; /**< Has arguments of the constructor, like Identifier1(TypeList1). */ bool hasEllipsis{false}; /**< Has ellipsis case, i.e. is non-exhaustive enum. */ Position ellipsisPos; /**< Position of ellipsis, if one exists. */ EnumDecl() : InheritableDecl(ASTKind::ENUM_DECL) { } std::vector>& GetMemberDecls() override { return members; } }; /** * A TypeAliasDecl node represents a type declaration. */ struct TypeAliasDecl : Decl { Position assignPos; /**< Position of '='. Generated by Parser. */ OwnedPtr type; /**< Type of TypeAliasDecl. Generated by Parser. */ TypeAliasDecl() : Decl(ASTKind::TYPE_ALIAS_DECL) { } }; /** * A FuncParamList presents a list of params which are in '()'. */ struct FuncParamList : Node { Position leftParenPos; /**< Position of '('. Generated by parser */ std::vector> params{}; /**< Function parameters. Generated by parser */ Position rightParenPos; /**< Position of ')'. Generated by parser */ size_t variadicArgIndex{0}; /**< Index of '...'. */ bool hasVariableLenArg{false}; /**< Whether have variable length argument. */ FuncParamList() : Node(ASTKind::FUNC_PARAM_LIST) { } }; /** * Reperesent the Kind of the VARIABLE CAPTURE. */ enum class CaptureKind : uint8_t { NO_CAPTURE, CAPTURE_VAR, TRANSITIVE_CAPTURE }; /** * A FuncBody node represents a function body which contains @p paramLists, @p retType and @p body. */ struct FuncBody : Node { std::vector> paramLists; /**< List of FuncParamList. Generated by parser */ Position doubleArrowPos; /**< Position of '=>'. Generated by parser, Optional */ Position colonPos; /**< Position of ':'. Generated by parser, Optional */ OwnedPtr retType; /**< Return type (node) of the function. ret->retType = ParseType(); Generated by parser, Optional */ OwnedPtr body; /**< Body of the function. Generated by parser */ OwnedPtr generic; /**< Generated by parser, Optional */ Ptr funcDecl{nullptr}; /**< Associated function declaration, should be nil for lambda. */ std::unordered_set> capturedVars; /**< Mutable variables captured by the function. */ Ptr outerFunc{nullptr}; /**< The pointer to the outer function. */ Ptr parentClassLike{nullptr}; /**< Parent Class or Interface. Generated by parser, Optional */ Ptr parentStruct{nullptr}; /**< Parent StructDecl. Generated by parser, Optional */ Ptr parentEnum{nullptr}; /**< Parent EnumDecl. Generated by parser, Optional */ CaptureKind captureKind{CaptureKind::NO_CAPTURE}; /**< Capture Variable Kind. */ FuncBody() : Node(ASTKind::FUNC_BODY) { } void Clear() noexcept override { Node::Clear(); capturedVars.clear(); } }; /** * Invoke super class's init functions. */ enum class ConstructorCall { NONE, /**< No super or other init function being called. */ OTHER_INIT, /**< Called other init function */ SUPER /**< Called super function */ }; /** * A FuncDecl node represents a function declaration. */ struct FuncDecl : Decl { Position leftParenPos; /**< Position of '('. Generated by parser*/ Position rightParenPos; /**< Position of ')'. Generated by parser*/ OwnedPtr funcBody; /**< Function body, contains param type and return type. Generated by parser*/ Ptr ownerFunc{nullptr}; /**< For default Parameter, no need to be serialized.*/ Ptr propDecl{nullptr}; OverflowStrategy overflowStrategy{OverflowStrategy::NA}; ConstructorCall constructorCall{ConstructorCall::NONE}; /**< Super or other init function called in constructor. */ TokenKind op{TokenKind::ILLEGAL}; /**< For operator overload. Generated by parser*/ size_t variadicArgIndex{0}; /**< Index of '...'. */ bool hasVariableLenArg{false}; /**< Whether have variable length argument. */ bool isConst{false}; /**< A flag to mark whether the decl is const. Generated by parser */ bool isSetter{false}; /**< Whether is setter in PropDecl. Generated by parser */ bool isGetter{false}; /**< Whether is getter in PropDecl. Generated by parser */ bool isInline{false}; /**< mark function is an inline function*/ bool isFastNative{false}; /**< mark function whether has annotation FastNative */ bool isReExportedOrRenamed{false}; /**< mark function whether is renamed or re-exported. */ bool isFrozen{false}; /**< mark function whether has annotation Frozen. */ FuncDecl() : Decl(ASTKind::FUNC_DECL) { } ~FuncDecl() override = default; /** * Whether a funcDecl is finalizer. */ bool IsFinalizer() const { return identifier == "~init"; } bool IsExportedDecl() const override; bool IsOpen() const noexcept override; }; /** * A MacroDecl node represents a macro declaration. */ struct MacroDecl : Decl { Position leftParenPos; /**< Position of '('. */ Position rightParenPos; /**< Position of ')'. */ OwnedPtr funcBody; /**< Macro body, contains param type and return type. Generated by Parser.*/ OwnedPtr desugarDecl; ~MacroDecl() override = default; MacroDecl() : Decl(ASTKind::MACRO_DECL) { } }; /** * A MainDecl node represents Program Entry Point Definition. */ struct MainDecl : Decl { OwnedPtr funcBody; /**< Function body, contains param type and return type. Generated by Parser. */ OwnedPtr desugarDecl; /**< Desugar Main Decl to function. */ OverflowStrategy overflowStrategy{OverflowStrategy::NA}; /**< Generated by Parser. */ bool isFastNative{false}; /**< mark decl whether has annotation fastNative, Generated by Parser */ ~MainDecl() override = default; MainDecl() : Decl(ASTKind::MAIN_DECL) { } }; struct PrimaryCtorDecl : Decl { OwnedPtr funcBody; /**< Function body, contains param type and return type. Generated by Parser. */ OverflowStrategy overflowStrategy{OverflowStrategy::NA}; /**< Overflow Strategy of Primary Constructor. */ bool hasVariableLenArg{false}; /**< Whether have variable length argument. */ bool isFastNative{false}; /**< mark function whether has annotation fastNative */ bool isConst{false}; /**< A flag to mark whether the decl is const. Generated by parser */ ~PrimaryCtorDecl() override = default; PrimaryCtorDecl() : Decl(ASTKind::PRIMARY_CTOR_DECL) { // Valid 'PrimaryCtorDecl' must be desugared to 'FuncDecl', 'PrimaryCtorDecl' always not export. doNotExport = true; } }; /** * A PropDecl node represents a property declaration. */ struct PropDecl : VarDecl { Position leftCurlPos; /**< The postion of '{'. */ std::vector> getters; std::vector> setters; Position rightCurlPos; /**< The postion of '}'. */ ~PropDecl() override = default; PropDecl() : VarDecl(ASTKind::PROP_DECL) { } bool IsExportedDecl() const override; bool IsOpen() const noexcept override; }; /** * StructBody definition. */ struct StructBody : Node { Position leftCurlPos; /**< Position of '{'. */ std::vector> decls; /**< Declarations in the body. */ Position rightCurlPos; /**< Position of '}'. */ StructBody() : Node(ASTKind::STRUCT_BODY) { } }; /** * A StructDecl node represents a struct declaration. */ struct StructDecl : InheritableDecl { OwnedPtr body; /**< StructBody. */ bool HasConstOrFrozenInit() { if (!hasConstOrFrozenInit.has_value()) { const auto& decls = body->decls; bool has = std::find_if(decls.cbegin(), decls.cend(), [](const OwnedPtr& decl) { return decl->TestAttr(Attribute::CONSTRUCTOR) && (decl->IsConst() || decl->HasAnno(AnnotationKind::FROZEN)); }) != decls.cend(); hasConstOrFrozenInit = {has}; } return *hasConstOrFrozenInit; } StructDecl() : InheritableDecl(ASTKind::STRUCT_DECL) { } std::vector>& GetMemberDecls() override { return body ? body->decls : Decl::GetMemberDecls(); } private: std::optional hasConstOrFrozenInit{std::nullopt}; }; /** * InterfaceBody definition. */ struct InterfaceBody : Node { Position leftCurlPos; /**< Position of '{'. Generated by Parser. */ std::vector> decls; /**< Declarations in the body. Generated by Parser. */ Position rightCurlPos; /**< Position of '}'. Generated by Parser. */ InterfaceBody() : Node(ASTKind::INTERFACE_BODY) { } }; /** * Common parts of ClassDecl and InterfaceDecl. */ struct ClassLikeDecl : InheritableDecl { std::set> subDecls; /**< A <: B ==> B.subDecls = {A}. */ protected: ClassLikeDecl(ASTKind kind) : InheritableDecl(kind) { } }; /** * An InterfaceDecl node represents an interface declaration. */ struct InterfaceDecl : ClassLikeDecl { OwnedPtr body; /**< InterfaceBody. Generated by Parser. */ InterfaceDecl() : ClassLikeDecl(ASTKind::INTERFACE_DECL) { } std::vector>& GetMemberDecls() override { return body ? body->decls : Decl::GetMemberDecls(); } bool IsOpen() const noexcept override { return true; } }; /** * ClassBody definition. */ struct ClassBody : Node { Position leftCurlPos; /**< Position of '{'. Generated by parser */ std::vector> decls; /**< Decls in the body. Generated by parser */ Position rightCurlPos; /**< Position of '}'. Generated by parser */ ClassBody() : Node(ASTKind::CLASS_BODY) { } }; /** * A CLassDecl node represents a class declaration. */ struct ClassDecl : ClassLikeDecl { OwnedPtr body; /**< ClassBody. */ bool HasConstOrFrozenInit() { if (!hasConstOrFrozenInit.has_value()) { const auto& decls = body->decls; bool has = std::find_if(decls.cbegin(), decls.cend(), [](const OwnedPtr& decl) { return decl->TestAttr(Attribute::CONSTRUCTOR) && (decl->IsConst() || decl->HasAnno(AnnotationKind::FROZEN)); }) != decls.cend(); hasConstOrFrozenInit = {has}; } return *hasConstOrFrozenInit; } ClassDecl() : ClassLikeDecl(ASTKind::CLASS_DECL) { } Ptr GetSuperClassDecl() const { Ptr ret = nullptr; for (auto& it : inheritedTypes) { if (it->ty && it->ty->kind == TypeKind::TYPE_CLASS) { ret = static_cast(it->ty.get())->decl; break; } } return ret; } std::vector>& GetMemberDecls() override { return body ? body->decls : Decl::GetMemberDecls(); } bool IsOpen() const noexcept override { return TestAnyAttr(Attribute::OPEN, Attribute::ABSTRACT); } private: std::optional hasConstOrFrozenInit{std::nullopt}; }; /** * An ExtendDecl node represents a extend declaration. */ struct ExtendDecl : InheritableDecl { OwnedPtr extendedType; /**< The type being extended. */ Position wherePos; /**< Position of 'where'. */ OwnedPtr bodyScope; /**< split body scope for LSP. */ Position leftCurlPos; /**< The position of '{'. */ std::vector> members; /**< Declarations of the extend decl. */ Position rightCurlPos; /**< The position of '}'. */ ExtendDecl() : InheritableDecl(ASTKind::EXTEND_DECL) { } std::vector>& GetMemberDecls() override { return members; } bool IsExportedDecl() const override; }; /** * Placeholder for invalid declaration. */ struct InvalidDecl : Decl { explicit InvalidDecl(const Position& pos) : Decl(ASTKind::INVALID_DECL) { EnableAttr(Attribute::IS_BROKEN); begin = pos; end = pos; doNotExport = true; } }; /** * Base class of patterns. */ struct Pattern : Node { Ptr ctxExpr{nullptr}; /**< Expr to match pattern. */ protected: Pattern(ASTKind kind) : Node(kind) { } }; /** * An InvalidPattern node represents the invalid pattern. */ struct InvalidPattern : Pattern { InvalidPattern() : Pattern(ASTKind::INVALID_PATTERN) { EnableAttr(Attribute::IS_BROKEN); } explicit InvalidPattern(const Position& pos) : Pattern(ASTKind::INVALID_PATTERN) { EnableAttr(Attribute::IS_BROKEN); begin = pos; end = pos; } }; /** * A ConstPattern node represents the constant pattern. */ struct ConstPattern : Pattern { OwnedPtr literal; /**< Literal constant. Generated by parser */ OwnedPtr operatorCallExpr; /**< resolved "==" overloaded function call */ ConstPattern() : Pattern(ASTKind::CONST_PATTERN) { } }; /** * A WildcardPattern node represents the wildcard pattern. */ struct WildcardPattern : Pattern { WildcardPattern() : Pattern(ASTKind::WILDCARD_PATTERN) { } explicit WildcardPattern(const Position& begin) : Pattern(ASTKind::WILDCARD_PATTERN) { this->begin = begin; this->end = begin + static_cast(Len(TokenKind::WILDCARD)); } }; /** * A VarPattern node represents the variable pattern. */ struct VarPattern : Pattern { OwnedPtr varDecl; /**< Matched variable. Generated by parser */ OwnedPtr desugarExpr; /**< DesugarExpr for varDecl in TypePattern. */ VarPattern() : Pattern(ASTKind::VAR_PATTERN) { } explicit VarPattern(SrcIdentifier ident, const Position& nodePos = {}) : Pattern(ASTKind::VAR_PATTERN) { varDecl = MakeOwned(); varDecl->begin = nodePos; varDecl->identifier = std::move(ident); varDecl->end = nodePos; varDecl->end = varDecl->identifier.End(); varDecl->isVar = false; begin = nodePos; end = varDecl->end; varDecl->parentPattern = this; } }; /** * A TuplePattern node represents the tuple pattern. */ struct TuplePattern : Pattern { Position leftBracePos; std::vector> patterns; /**< Each pattern in the tuple pattern. */ std::vector commaPosVector; Position rightBracePos; TuplePattern() : Pattern(ASTKind::TUPLE_PATTERN) { } }; /** * A TypePattern node represents the type pattern. */ struct TypePattern : Pattern { OwnedPtr pattern; /**< WildPattern or VarPattern. Generated by Parser */ Position colonPos; /**< Position of ':'. Generated by Parser */ OwnedPtr type; /**< Type of the pattern. Generated by Parser */ OwnedPtr desugarExpr; /**< Desugar of upcast type pattern. */ OwnedPtr desugarVarPattern; /**< Desugar of downcast type pattern. */ bool needRuntimeTypeCheck{true}; /**< Flag that notice if should do runtime type check. */ bool matchBeforeRuntime{true}; /**< Flag that notice if type pattern is matched on compile time. */ TypePattern() : Pattern(ASTKind::TYPE_PATTERN) { } }; /** * A EnumPattern node represents the enum pattern. */ struct EnumPattern : Pattern { OwnedPtr constructor; /**< Constructor of the enum. Generated by Parser*/ std::vector> patterns; /**< Each pattern in the enum parameters. Generated by Parser*/ Position leftParenPos; /**< Position of '('. Generated by Parser*/ std::vector commaPosVector; Position rightParenPos; /**< Position of ')'. Generated by Parser*/ EnumPattern() : Pattern(ASTKind::ENUM_PATTERN) { } std::string GetIdentifier() const; }; /** * A VarOrEnumPattern node represents a VarPattern or an EnumPattern. */ struct VarOrEnumPattern : public Pattern { /**< Identifier of the variable or the enum constructor. * (When it's text is raw identifier, the raw mark "``" will be removed. * e.g., name of "`type_1`" is "type_1".) Generated by Parser. */ SrcIdentifier identifier; OwnedPtr pattern; /**< VarPattern or EnumPattern. */ VarOrEnumPattern(const SrcIdentifier& id) : Pattern(ASTKind::VAR_OR_ENUM_PATTERN), identifier{id} { } }; /** * A ExceptTypePattern node represents the exception type pattern. */ struct ExceptTypePattern : Pattern { OwnedPtr pattern; /**< VarPattern. Generated by Parser*/ Position patternPos; /**< Position of VarPattern. Generated by Parser*/ Position colonPos; /**< Position of ':'. Generated by Parser*/ std::vector> types; /**< Types of exceptions. Generated by Parser*/ std::vector bitOrPosVector; /**< Positions of '|'. Generated by Parser*/ ExceptTypePattern() : Pattern(ASTKind::EXCEPT_TYPE_PATTERN) { } }; /** * A CommandTypePattern node represents the effect type pattern. */ struct CommandTypePattern : Pattern { OwnedPtr pattern; /**< VarPattern. Generated by Parser*/ Position patternPos; /**< Position of VarPattern. Generated by Parser*/ Position colonPos; /**< Position of ':'. Generated by Parser*/ std::vector> types; /**< Types of Effects. Generated by Parser*/ std::vector bitOrPosVector; /**< Positions of '|'. Generated by Parser*/ CommandTypePattern() : Pattern(ASTKind::COMMAND_TYPE_PATTERN) { } }; /** * Base expression node. */ struct Expr : Node { OwnedPtr desugarExpr; Ptr sourceExpr{nullptr}; /**< the source Expr if this node is a desugarExpr. */ Ptr mapExpr{nullptr}; /**< recorded mapped Expr if this node is a cloned expr with side effect. */ Position semiPos; /**< Position of ';'. Generated by parser, Optional */ /** * Struct constNumValue store the primitive types for an 'expr', we will extend some primitive types * like int and float to bigint and bigfloat, so struct which is convenient to store a member with * a non-trivial default constructor is used instead of union */ enum class FlowStatus { NORMAL, OVER, UNDER }; struct { IntLiteral asInt; struct { long double value{0}; /**< Parsed from literal by `stold`, storaged with high-precision. */ FlowStatus flowStatus{FlowStatus::NORMAL}; /**< Indicate the value flow status */ } asFloat; /**< The original high-precision float number. */ bool asBoolean{true}; } constNumValue; /** Used to mark sugar kind which is desugared before typecheck. * Also used to mark desugared call expr like `Token(xxx)` */ enum class SugarKind { QUEST, IF_LET, // if-let Expression WHILE_LET, // while-let Expression FOR_IN_EXPR, // for-in Expression IS, // is Expression AS, // as Expression TOKEN_CALL, // call expr like: Token(xxx) NO_SUGAR // Not a sugar at all. }; /** * Mark the current node for desugar, indicates that the node or subnode contains the quest suffix(?). */ OverflowStrategy overflowStrategy{ OverflowStrategy::NA}; /**< For overflow strategy. Generated by parser, Optional */ bool isConst{false}; bool isBaseFunc{false}; bool isInFlowExpr{false}; bool hasSemi{false}; /**< Mark the expr has ';'. Generated by parser, Optional*/ bool hasQuestSuffix{false}; Expr() : Node(ASTKind::EXPR) { } ~Expr() override = default; virtual std::vector> GetTypeArgs() const { return {}; } bool IsReferenceExpr() const noexcept { return astKind == ASTKind::REF_EXPR || astKind == ASTKind::MEMBER_ACCESS; } protected: Expr(ASTKind kind) : Node(kind) { } }; /** * Represent wildcard left value expr. * e.g. _ in `_ = 1` */ struct WildcardExpr : Expr { WildcardExpr() : Expr(ASTKind::WILDCARD_EXPR) { } explicit WildcardExpr(const Position& begin) : Expr(ASTKind::WILDCARD_EXPR) { this->begin = begin; this->end = begin + static_cast(Len(TokenKind::WILDCARD)); } }; /** * A Block Node represents block. */ struct Block : Expr { Position leftCurlPos; /**< Position of '{'. Generated by Parser */ std::vector> body; /**< Block body. Generated by Parser */ Position rightCurlPos; /**< Position of '}'. Generated by Parser */ Position unsafePos; /**< Position of 'unsafe'. Generated by Parser, Optional */ Block() : Expr(ASTKind::BLOCK) { } Ptr GetLastExprOrDecl() const; }; /** * An IfExpr node represents an if expression or an if-let expression */ struct IfExpr : Expr { Position ifPos; /**< Position of 'if'. */ Position leftParenPos; /**< Position of '('. */ OwnedPtr condExpr; /**< Condition Expr. Generated by Parser*/ Position rightParenPos; /**< Position of ')'. */ OwnedPtr thenBody; /**< Then body. Generated by Parser*/ Position elsePos; /**< Position of 'else'. */ OwnedPtr elseBody; /**< Else body. Generated by Parser*/ bool isElseIf{false}; /**< Whether has else if block. Generated by Parser*/ bool hasElse{false}; /**< Whether has else block. Generated by Parser*/ SugarKind sugarKind = SugarKind::NO_SUGAR; IfExpr() : Expr(ASTKind::IF_EXPR) { } }; /** * A LetPatternDestructor only appears in if expr or while expr condition. * such as: if(let Some(v) <- x) {...} */ struct LetPatternDestructor : Expr { std::vector> patterns; /**< Patterns to be destructed. Generated by Parser*/ std::vector orPos; /**< '|' that separates the patterns.*/ Position backarrowPos; /**< Position of '<-'. */ OwnedPtr initializer; /**< Initialize Expr. Generated by Parser*/ LetPatternDestructor() : Expr{ASTKind::LET_PATTERN_DESTRUCTOR} { } }; /** * A TokenPart node represents Consecutive tokens separated by interpolation in quote expression. */ struct TokenPart : Expr { std::vector tokens; TokenPart() : Expr(ASTKind::TOKEN_PART) { } TokenPart(std::vector& tokens) : Expr(ASTKind::TOKEN_PART), tokens(tokens) { if (!tokens.empty()) { begin = tokens[0].Begin(); end = tokens.back().End(); } } }; /** * A QuoteExpr node represents quote expression. */ struct QuoteExpr : Expr { Position quotePos; /**< Position of 'quote'. */ Position leftParenPos; /**< Position of '('. */ Position rightParenPos; /**< Position of ')'. */ /** * Arguments of the quote expression, include DollarExpr and TokenPart. */ std::vector> exprs; QuoteExpr() : Expr(ASTKind::QUOTE_EXPR) { } }; /** * One match case. */ struct MatchCase : Node { std::vector> patterns; /**< A collection of patterns connected by `|`. */ std::vector bitOrPosVector; OwnedPtr patternGuard; /**< Pattern match some condition. */ Position wherePos; /**< Position of 'where', only matters when patternGuard is not null. */ Position arrowPos; /**< Position of '=>'. */ OwnedPtr exprOrDecls; /**< Actions when matching. */ MatchCase() : Node(ASTKind::MATCH_CASE) { } void SetCtxExprForPatterns(Ptr ctxExpr) { for (auto& pattern : patterns) { if (pattern) { pattern->ctxExpr = ctxExpr; } } } }; /** * Simple match case. */ struct MatchCaseOther : Node { OwnedPtr matchExpr; /**< Simple match expr. Generated by Parser. */ Position arrowPos; /**< Position of '=>'. */ OwnedPtr exprOrDecls; /**< Actions when matching. Generated by Parser.*/ MatchCaseOther() : Node(ASTKind::MATCH_CASE_OTHER) { } }; /** * A MatchExpr node represents match-case expression. */ struct MatchExpr : Expr { SugarKind sugarKind = SugarKind::NO_SUGAR; bool matchMode{false}; /**< Normal pattern match:true, Implement if-else if: false. Generated by Parser.*/ Position leftParenPos; /**< Position of '('. */ OwnedPtr selector; /**< The Expr to be match. */ Position rightParenPos; /**< Position of ')'. */ Position leftCurlPos; /**< Position of '{'. */ std::vector> matchCases; /**< Match cases. */ std::vector> matchCaseOthers; /**< No selector mode. */ Position rightCurlPos; /**< Position of '}'. */ MatchExpr() : Expr(ASTKind::MATCH_EXPR) { } }; struct Handler { Position pos; OwnedPtr commandPattern; OwnedPtr block; OwnedPtr desugaredLambda; Ty* commandResultTy; }; /** * A TryExpr node represents Try expression. */ struct TryExpr : Expr { Position tryPos; /**< Position of 'try'. */ Position lParen; /**< Resource left paren position. */ std::vector> resourceSpec; /**< Init several resources. */ std::vector resourceSpecCommaPos; /**< Resource comma positions. */ Position rParen; /**< Resource right paren position. */ OwnedPtr tryBlock; /**< Try block. */ std::vector catchPosVector; /**< Positions of 'catch'. */ std::vector catchLParenPosVector; /**< Positions of '(' in catch blocks. */ std::vector catchRParenPosVector; /**< Positions of ')' in catch blocks. */ std::vector> catchBlocks; /**< Several catch blocks. */ std::vector> catchPatterns; /**< '_' or ExceptTypePattern. */ Position finallyPos; /**< Position of 'finally'. */ OwnedPtr finallyBlock; /**< Finally block. */ std::vector handlers; /**< Handler cases in try/handle */ bool isIllegalResourceSpec{false}; /**< Nested try expressions inside resource specifications are not allowed. */ bool isDesugaredFromTryWithResources{ false}; /**< Desugared from try-with-resources expressions. Generated by Parser.*/ bool isDesugaredFromSyncBlock{false}; /**< Desugared from synchronized expressions. For better error reporting. */ OwnedPtr tryLambda; /**< Try block as a lambda. Used for desugaring try/handle */ OwnedPtr finallyLambda; /**< Finally block as a lambda. Used for desugaring try/handle */ TryExpr() : Expr(ASTKind::TRY_EXPR) { } }; /** * A ThrowExpr node represents throw expression. */ struct ThrowExpr : Expr { Position throwPos; /**< Position of 'throw'. */ OwnedPtr expr; /**< Throw expr. */ ThrowExpr() : Expr(ASTKind::THROW_EXPR) { } }; /** * A PerformExpr node represents perform expression. */ struct PerformExpr : Expr { Position performPos; /**< Position of 'perform'. */ OwnedPtr expr; /**< Perform expr. */ PerformExpr() : Expr(ASTKind::PERFORM_EXPR) { } }; /** * A ResumeExpr node represents resume expression. */ struct ResumeExpr : Expr { Position resumePos; /**< Position of 'resume'. */ Position withPos; /**< Position of 'with' (optional). */ OwnedPtr withExpr; /**< With expr (optional). */ Position throwingPos; /**< Position of 'throwing' (optional). */ OwnedPtr throwingExpr; /**< Throwing expr (optional). */ std::optional> enclosing; /**< Pointer to the (immediate) enclosing handler, if any. */ ResumeExpr() : Expr(ASTKind::RESUME_EXPR) { } }; /** * A ReturnExpr node represents return expression. */ struct ReturnExpr : Expr { Position returnPos; /**< Position of 'return'. */ OwnedPtr expr; /**< Return expr. Generated by Parser.*/ Ptr refFuncBody{nullptr}; /**< In which function body. */ ReturnExpr() : Expr(ASTKind::RETURN_EXPR) { } }; /** * A JumpExpr node represents continue or break expression. */ struct JumpExpr : Expr { Ptr refLoop{nullptr}; /**< In which loop expr. */ bool isBreak{false}; /**< Break: true, Continue: false. Generated by Parser.*/ JumpExpr() : Expr(ASTKind::JUMP_EXPR) { } }; /** * A ForInExpr node represents for-in expression. */ struct ForInExpr : Expr { Position leftParenPos; /**< Position of '('. */ OwnedPtr pattern; /**< Same Pattern in the pattern match. */ Ptr patternInDesugarExpr{nullptr}; Position inPos; /**< Position of 'in'. */ OwnedPtr inExpression; /**< In some expr. Generated by Parser.*/ Position rightParenPos; /**< Position of ')'. */ Position wherePos; /**< Position of 'if'. */ OwnedPtr patternGuard; /**< Same PatternGuard in the pattern match. Generated by Parser.*/ OwnedPtr body; /**< Body of the ForInExpr. Generated by Parser.*/ ForInKind forInKind; ForInExpr() : Expr(ASTKind::FOR_IN_EXPR), forInKind(ForInKind::FORIN_INVALID) { } /// Test whether the range expression of this forin is closed range, and the step is either missing, or literal one. /// Some CHIR optimization depends on this. #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool IsClosedRangeOne() const; #endif }; /** * A WhileExpr node represents while expression. */ struct WhileExpr : Expr { Position whilePos; /**< Position of 'while'. */ Position leftParenPos; /**< Positin of '('. */ OwnedPtr condExpr; /**< While condition. */ Position rightParenPos; /**< Position of ')'. */ OwnedPtr body; /**< Body of the WhileExpr. */ SugarKind sugarKind = SugarKind::NO_SUGAR; WhileExpr() : Expr(ASTKind::WHILE_EXPR) { } }; /** * A DoWhileExpr node represents do-while expression. */ struct DoWhileExpr : Expr { Position doPos; /**< Position of 'do'. */ OwnedPtr body; /**< Body of the DoWhileExpr. */ Position whilePos; /**< Position of 'while'. */ Position leftParenPos; /**< Position of '('. */ OwnedPtr condExpr; /**< Do while condition. */ Position rightParenPos; /**< Position of ')'. */ DoWhileExpr() : Expr(ASTKind::DO_WHILE_EXPR) { } }; /** * An OverloadableExpr represents an expr whose operator can be overloaded. * OverloadableExpr: AssignExpr, UnaryExpr, BinaryExpr, ArrayAccess. */ struct OverloadableExpr : Expr { TokenKind op{TokenKind::ILLEGAL}; /**< Operator kind. */ protected: OverloadableExpr(ASTKind kind) : Expr(kind) { } }; /** * An AssignExpr Node represents assignment expression. * example: * a = b * ^^^^^ */ struct AssignExpr : OverloadableExpr { OwnedPtr leftValue; /**< Left hand side expr. */ Position assignPos{}; /**< Position of the assign operator. */ OwnedPtr rightExpr; /**< Right hand side expr. */ bool isCompound = false; AssignExpr() : OverloadableExpr(ASTKind::ASSIGN_EXPR) { } void Clear() noexcept override; }; /** * An IncOrDecExpr node represents inc or dec expression. * e.g. * a++ or a-- * ^^^ ^^^ */ struct IncOrDecExpr : OverloadableExpr { Position operatorPos; /**< Position of '++' or '--'. */ OwnedPtr expr; IncOrDecExpr() : OverloadableExpr(ASTKind::INC_OR_DEC_EXPR) { } }; /** * UnaryExpr node represents unary operation expression. * e.g. * !a or -a * ^^ ^^ */ struct UnaryExpr : OverloadableExpr { OwnedPtr expr; /**< The unary expr. */ Position operatorPos; /**< Position of '- !'. */ UnaryExpr() : OverloadableExpr(ASTKind::UNARY_EXPR) { } void Clear() noexcept override; }; /** * BinaryExpr node represents binary operation expression. * e.g. * a + b or a % b * ^^^^^ ^^^^^ */ struct BinaryExpr : OverloadableExpr { OwnedPtr leftExpr; /**< Left expr. */ OwnedPtr rightExpr; /**< Right expr. */ Position operatorPos; /**< Position of '&&', '||', etc. */ BinaryExpr() : OverloadableExpr(ASTKind::BINARY_EXPR) { } void Clear() noexcept override; }; /** * IsExpr node represents `is` operation expression. */ struct IsExpr : Expr { OwnedPtr leftExpr; /**< Left expr. Generated by Parser.*/ OwnedPtr isType; /**< Right type. Generated by Parser.*/ Position isPos; /**< Position of 'is'. */ IsExpr() : Expr(ASTKind::IS_EXPR) { } }; /** * AsExpr node represents `as` operation expression. */ struct AsExpr : Expr { OwnedPtr leftExpr; /**< Left expr. */ OwnedPtr asType; /**< Right type. */ Position asPos; /**< Position of 'as'. */ AsExpr() : Expr(ASTKind::AS_EXPR) { } }; /** * RangeExpr node represents range expression like 'a...b:c'. */ struct RangeExpr : Expr { OwnedPtr startExpr; /**< No start will be @c nullptr. Generated by parser */ Position rangePos; /**< Position of '...'.Generated by parser */ OwnedPtr stopExpr; /**< No stop will be @c nullptr. Generated by parser */ Position colonPos; /**< Position of ':'. Generated by parser */ OwnedPtr stepExpr; /**< No step will be @c nullptr. Generated by parser */ Ptr decl = nullptr; /**< StructDecl for Range literals. */ bool isClosed{true}; /**< A flag to mark this is closed range or a half-open range. */ RangeExpr() : Expr(ASTKind::RANGE_EXPR) { } }; /** * SubscriptExpr node represents an index access expression. * e.g. * a[x] or b[1][2] * ^^^^ ^^^^^^^ */ struct SubscriptExpr : OverloadableExpr { OwnedPtr baseExpr; /**< Left parts split by last '['. Generated by Parser.*/ Position leftParenPos; /**< Position of '['. */ std::vector> indexExprs; /**< Exprs between '[' and ']'. Generated by Parser.*/ std::vector commaPos; Position rightParenPos; /**< Position of ']'. */ /** * Whether the baseExpr is of type tuple. * Currently, after desugaring, a SubscriptExpr can only be an access to a tuple or a varray. */ bool isTupleAccess{false}; bool IsVArrayAccess() const { return baseExpr && baseExpr->ty->kind == TypeKind::TYPE_VARRAY; } SubscriptExpr() : OverloadableExpr(ASTKind::SUBSCRIPT_EXPR) { } void Clear() noexcept override; }; /** * InterpolationExpr node represents Interpolation expression in String like "${b}". */ struct InterpolationExpr : Expr { std::string rawString; /**< The Interpolation string "${a}". Generated by Parser.*/ Position dollarPos; /**< Position of '$'. */ OwnedPtr block; /**< { a + b }, Generated by Parser.*/ InterpolationExpr() : Expr(ASTKind::INTERPOLATION_EXPR) { } }; /** * StrInterpolationExpr node represents String Interpolation expression like "a ${b} c". */ struct StrInterpolationExpr : Expr { std::string rawString; /**< The value of string Interpolation "a ${b} c". Generated by Parser.*/ std::vector strParts; /**< StringPart in string Interpolation. Generated by Parser.*/ /** * strPartExprs separated by interpolation, include string LitConstExpr and InterpolationExpr. Generated by Parser. */ std::vector> strPartExprs; StrInterpolationExpr() : Expr(ASTKind::STR_INTERPOLATION_EXPR) { } }; /** * LitConstKind contains all literal kind. * This enum has a correspondence with class TokenKindHelper in std.ast/TokenKind.cj * They need to be changed respectively at the same time. */ enum class LitConstKind : uint8_t { INTEGER, /**< Int8, Int16, Int32, Int64, UInt8, etc. */ RUNE_BYTE, /**< UInt8 type such as b'a', b'\n' literals */ FLOAT, /**< float16, float32, float64. */ RUNE, /**< Rune. */ STRING, /**< String. */ JSTRING, /**< JString. */ BOOL, /**< bool. */ UNIT, /**< Unit. */ NONE /**< None literal const. */ }; /** * There should not be a need to treat literals of different primitives types as different C++ classes instead, just * fill the type in Expr during parsing, which is the only field that needs any testing */ struct LitConstExpr : Expr { OwnedPtr ref; /**< RefType for String literals. */ std::string rawString; /**< The literal raw string for fmt. Generated by Parser.*/ OwnedPtr siExpr; /**< string interpolation expression. Generated by Parser.*/ std::string stringValue; /**< The literal string representation. Generated by Parser.*/ std::vector codepoint; /**< Code point of the character in the string. Generated by Parser.*/ LitConstKind kind{LitConstKind::NONE}; /**< Literal const kind. Generated by Parser.*/ StringKind stringKind{StringKind::NORMAL}; /**< Kind of String. Generated by Parser.*/ bool isSingleQuote{false}; // Quotations of string-related literals can be single or double. unsigned int delimiterNum{1}; /**< Delimiter '#' number for raw string. Generated by Parser.*/ LitConstExpr() : Expr(ASTKind::LIT_CONST_EXPR) { } LitConstExpr(LitConstKind kind, std::string str) : Expr(ASTKind::LIT_CONST_EXPR), stringValue(std::move(str)), kind(kind) { } std::string LitKindToString() const { switch (kind) { case LitConstKind::INTEGER: return "Integer"; case LitConstKind::RUNE_BYTE: return "RuneByte"; case LitConstKind::FLOAT: return "Float"; case LitConstKind::RUNE: return "Rune"; case LitConstKind::STRING: return "String"; case LitConstKind::JSTRING: return "JString"; case LitConstKind::BOOL: return "Bool"; case LitConstKind::UNIT: return "Unit"; case LitConstKind::NONE: return "None"; } } std::string ToString() const override; // Get number literal type kind from suffix, if no suffix TYPE_IDEAL will be returned TypeKind GetNumLitTypeKind(); }; /** * A MacroInvocation represents Macro Invocation Information. */ struct MacroInvocation { // Read only. Determined in Parsing. std::string fullName; /**< Full name of the Macro Call: p1.Moo. Generated by parser */ std::vector fullNameDotPos; /**< Dot positions in full name. Generated by parser */ std::string identifier; /**< Macro name: Moo. Generated by parser */ Position identifierPos; /**< Position of the MacroCall's identifier. Generated by parser */ Position leftSquarePos; /**< Position of '['. Generated by parser, Optional */ Position rightSquarePos; /**< Position of ']'. Generated by parser, Optional */ Position leftParenPos; /**< Position of '('. Generated by parser, Optional */ Position rightParenPos; /**< Position of ')'. Generated by parser, Optional */ Position atPos; /**< The position of '@'. Generated by parser. */ Position macroNamePos; /**< The real position of Macro name: Moo. Generated by parser. */ std::vector attrs; /**< Attributes to Macro. */ bool HasAttr() { return hasAttr; } std::vector args; /**< Arguments to the Macro. */ OwnedPtr decl; /**< ASTKind of the Decl as Macro's args. */ Ptr parent{nullptr}; /**< Parent node. */ Ptr target{nullptr}; /**< Target node. */ // For primary ctor decl. Need to reconsider this. std::string outerDeclIdent; /**< MacroExpandDecl's parent decl identifier. */ // Arguments include macroExpression and TokenPart. std::vector> nodes; std::vector macroAtPosition; /**< all macros' begin position. */ std::vector newTokens; /**< Tokens after macro evaluation. */ std::string newTokensStr; /**< Tokens str after macro evaluation. */ std::variant scope; bool hasParenthesis{false}; bool hasAttr{false}; bool isFuncParam{false}; bool isCustom{false}; /**< Indicates Custom Annotation. */ bool isCompileTimeVisible{}; /**< True when @! Annotation. */ // Mapping between new position column and origin source position in macrocall for debug. std::map> macroDebugMap; // Mapping between new position column and origin source position before macro expansion. std::map> new2originPosMap; // Mapping between new position column and new source position after macro expansion. std::map> new2macroCallPosMap; // Mapping between hash of origin position and new position after macro expansion. std::map> origin2newPosMap; // Mapping between hash of origin position and begin position of origin token before macro expansion. std::map> originPosMap; bool isCurFile{false}; bool isForLSP{false}; bool hasShownCode{false}; /**< Has shown the code that generated by macro once in diagnostics. */ Position mcBegin; /**< Begin position of new source after macro expansion. */ Position mcEnd; /**< End position of new source after macro expansion. */ /// True if this is an @IfAvailable node. For compatibility, @IfAvailable node is tucked into a MacroExpandExpr /// and translated to IfExpr later. bool IsIfAvailable() const; }; /** * A MacroExpandExpr node represents where Macro need to be expanded. * When and only when representing a @IfAvailable(namedParam: arg, lambda1, lambda2), methods GetNamedArg and GetLambda * are available to get the sub-exprs. They are put into invocation.nodes just for backward compatibility of ast nodes. */ struct MacroExpandExpr : Expr { MacroInvocation invocation; /**< MacroExpand Invoking Information. */ Identifier identifier; /**< Macro name: Moo */ std::vector> annotations; /**< Annotation set of the Decl. */ std::set modifiers; /**< Modifier set of the Decl. */ MacroExpandExpr() : Expr(ASTKind::MACRO_EXPAND_EXPR) { } Ptr GetNamedArg() const; Ptr GetLambda(size_t i) const; /// Get the inner exprs. Used in desugar. std::tuple, OwnedPtr, OwnedPtr> Decompose(); }; /** * A MacroExpandDecl node represents where Macro need to be expanded. */ struct MacroExpandDecl : Decl { MacroInvocation invocation; /**< MacroExpandDecl Invoking Information. */ MacroExpandDecl() : Decl(ASTKind::MACRO_EXPAND_DECL) { } }; /** * A MacroExpandParam node represents where Macro need to be expanded in the params list. */ struct MacroExpandParam : FuncParam { MacroInvocation invocation; MacroExpandParam() : FuncParam(ASTKind::MACRO_EXPAND_PARAM) { invocation.isFuncParam = true; } }; /** * Function arguments in CallExpr. */ struct FuncArg : Node { SrcIdentifier name; /**< Argument name. Generated by parser, Optional */ Position colonPos; /**< Position of ':'. Generated by parser, Optional */ OwnedPtr expr; /**< The expr of the argument. Generated by parser */ Position commaPos; /**< Position of ','. Generated by parser, Optional */ bool withInout{false}; /**< Indicate if modified with 'inout'. Generated by parser, Optional */ Position inoutPos; /**< Position of 'inout'. Generated by parser, Optional */ FuncArg() : Node(ASTKind::FUNC_ARG) { } void Clear() noexcept override { Node::Clear(); if (expr != nullptr) { expr->Clear(); } DisableAttr(Attribute::HAS_INITIAL); } std::string ToString() const override; }; /** * Which function call. */ enum class CallKind { CALL_INVALID = 0, /**< Invalid call. */ CALL_DECLARED_FUNCTION, /**< Normal function call. */ CALL_OBJECT_CREATION, /**< Create an object. */ CALL_FUNCTION_PTR, /**< Function point invoke. */ CALL_STRUCT_CREATION, /**< Create a struct. */ CALL_SUPER_FUNCTION, /**< Is super() function call. */ CALL_VARIADIC_FUNCTION, /**< Variadic function call. */ CALL_ANNOTATION, /**< Custom annotation call. */ // Todo: remove CALL_BUILTIN_FUNCTION, /**< Is buildin api call. */ CALL_INTRINSIC_FUNCTION /**< Is intrinsic function call. */ }; /** * CallExpr node represents function-call expression. */ struct CallExpr : Expr { OwnedPtr baseFunc; /**< The expr, may be RefExpr or MemberAccess. Generated by Parser. */ Position leftParenPos; /**< Position of '('. */ std::vector> args; /**< Arguments to the function. */ Position rightParenPos; /**< Position of ')'. */ Ptr resolvedFunction{nullptr}; /**< Resolved called function. */ CallKind callKind{CallKind::CALL_INVALID}; /**< Kind of the call. */ std::optional>> desugarArgs; /**< It points to args and defaultArgs, which are used to do the real parameter sorting. When it is none, the callExpr does not go into args reordering. */ std::vector> defaultArgs; /**< The defaultArgs used to store supplements for default args*/ bool needCheckToTokens{ false}; /**< A flag to mark whether the call is needed to check implementing interface ToTokens . */ SugarKind sugarKind = SugarKind::NO_SUGAR; CallExpr() : Expr(ASTKind::CALL_EXPR) { } void Clear() noexcept override; std::string ToString() const override; }; /** * NameReferenceExpr is used as an attribute holder abstraction for RefExpr and MemberAccess. */ struct NameReferenceExpr : Expr { Position leftAnglePos; /**< The position of left angle bracket '<'. */ std::vector> typeArguments; /**< Instantiation type. */ Position rightAnglePos; /**< The position of right angle bracket '>'. */ /** The type arguments type if call generic function without explicit type arguments. */ std::vector> instTys; /** The func call or pattern whose baseFunc is this ref */ Ptr callOrPattern{nullptr}; Ptr matchedParentTy{nullptr}; /**< The determined parent ty of current reference's context structure type. */ /** For LSP. Point to typeAliasDecl if the current reference is aliased name. Can be ignored in clone. */ Ptr aliasTarget{nullptr}; bool isAlone{true}; /**< Whether the current expr is separately written. Generated by Parser. */ /** Whether the type arguments are added by compiler, instead of from src code. * Only happens if this reference points to a type alias. */ bool compilerAddedTyArgs{false}; std::vector> GetTypeArgs() const override { return ConverVector(typeArguments); } size_t OuterArgSize() const; protected: NameReferenceExpr(ASTKind astKind) : Expr(astKind) { } NameReferenceExpr(const NameReferenceExpr&) = delete; NameReferenceExpr& operator=(const NameReferenceExpr&) = delete; }; /** * MemberAccess node represents object member access expression like obj.a.b. */ struct MemberAccess : NameReferenceExpr { OwnedPtr baseExpr; /**< Left parts split by last '.'. */ Position dotPos; /**< Position of the right most '.'. */ /**< Right parts split by last '.'. (When it's text is raw identifier, the raw mark "``" will be removed. * e.g., name of "`type_1`" is "type_1".) */ SrcIdentifier field; std::vector> targets; /**< If the accessed symbol is a function name, this field stores overloaded function definitions. */ Ptr target{nullptr}; /**< If the accessed symbol is not a function name, this field stores the target. */ std::unordered_map, std::unordered_set>> foundUpperBoundMap; /**< The targets in all upperBounds. */ bool isExposedAccess{false}; /**< Whether the current expr is exposed to upper bound. */ bool isPattern{false}; /**< Whether the current expr is inside a pattern. Generated by Parser.*/ MemberAccess() : NameReferenceExpr(ASTKind::MEMBER_ACCESS) { } void Clear() noexcept override { Expr::Clear(); baseExpr->Clear(); foundUpperBoundMap.clear(); target = nullptr; targets.clear(); instTys.clear(); isExposedAccess = false; matchedParentTy = nullptr; desugarExpr = OwnedPtr(); } std::string ToString() const override; /** * Get the field Position of the MemberAccess. * @return field Position in macrocall file if the MemberAccess is expanded from macrocall, * field position in curfile otherwise. */ Position GetFieldPos() const; }; /** * A ParenExpr node represents a parenthesized expression which the expression between "(" and ")". * ``` * a = (1+2)*3; * ``` */ struct ParenExpr : Expr { Position leftParenPos; /**< Position of '('. */ OwnedPtr expr; /**< Expr between '(' and ')'. */ Position rightParenPos; /**< Position of ')'. */ ParenExpr() : Expr(ASTKind::PAREN_EXPR) { } void Clear() noexcept override; }; /** * A lambdaExpr node represents a lambda expression with '=>'. */ struct LambdaExpr : Expr { LambdaExpr() : Expr(ASTKind::LAMBDA_EXPR) { } OwnedPtr funcBody; /**< Funcbody of lambda. */ std::string mangledName; /**< The name after mangling. */ explicit LambdaExpr(OwnedPtr body) : Expr(ASTKind::LAMBDA_EXPR), funcBody(std::move(body)) { } }; struct TrailingClosureExpr : Expr { Position leftLambda; /**< Left Position of Lambda. */ OwnedPtr expr; /**< refExpr or callExpr. */ OwnedPtr lambda; /**< tail closure package. */ Position rightLambda; /**< Right Position of Lambda. */ TrailingClosureExpr() : Expr(ASTKind::TRAIL_CLOSURE_EXPR) { } }; struct OptionalExpr : Expr { OwnedPtr baseExpr; Position questPos; /**< The position of '?'. */ explicit OptionalExpr() : Expr(ASTKind::OPTIONAL_EXPR) { } }; struct OptionalChainExpr : Expr { OwnedPtr expr; explicit OptionalChainExpr() : Expr(ASTKind::OPTIONAL_CHAIN_EXPR) { } }; /** * An ArrayLit node represents a List literal with expressions between comma. * ``` * [1, 2, 3, 4, 5] * ``` */ struct ArrayLit : Expr { Position leftSquarePos; /**< Position of '['. */ std::vector> children; /**< Children in the list. */ std::vector commaPosVector; /**< Positions of commas. */ Position rightSquarePos; /**< Position of ']'. */ Ptr initFunc{nullptr}; ArrayLit() : Expr(ASTKind::ARRAY_LIT) { } std::string ToString() const override; }; /** * An ArrayExpr node represents a 'RawArray' or a 'VArray' constructing expressions. * For RawArray it can be: * ``` * RawArray() * RawArray(n, 9) * RawArray(5, (i) => {i * 2}) * RawArray(Collection/List) * ``` * For VArray it can be: * ``` * VArray(repeat: 1) * VArray({i => i}) * ``` */ struct ArrayExpr : public Expr { OwnedPtr type; /**< Array type. Generated by Sema */ Position leftParenPos; /**< Position of '('. */ std::vector> args; /**< Generated by Sema */ std::vector commaPosVector; /**< Generated by Sema */ Position rightParenPos; /**< Position of ')'. Generated by Sema */ Ptr initFunc{nullptr}; bool isValueArray{false}; /* Is a VArray expr or not. */ ArrayExpr() : Expr(ASTKind::ARRAY_EXPR) { } std::string ToString() const override; }; /** * Represents an Pointer Constructor. * Currently, only two kinds: init(),init(origin: CPointer). */ struct PointerExpr : Expr { OwnedPtr type; /**< Pointer type. */ OwnedPtr arg; /**< Arguments to the constructor, one or none. */ PointerExpr() : Expr(ASTKind::POINTER_EXPR) { } std::string ToString() const override; }; /** * A TupleLit node represents a tuple literal with expressions between comma. * ``` * (1, 2, 3, 4) * ``` */ struct TupleLit : Expr { Position leftParenPos; /**< Position of '('. */ std::vector> children; /**< Children of tuple. */ std::vector commaPosVector; /**< Positions of commas. */ Position rightParenPos; /**< Position of ')'. */ TupleLit() : Expr(ASTKind::TUPLE_LIT) { } }; /** * A TypeConvExpr node represents numeric type convert expression. */ struct TypeConvExpr : Expr { OwnedPtr type; /**< Must be numeric or function type. */ Position leftParenPos; /**< Position of '('. */ OwnedPtr expr; /**< The expr to be convert. */ Position rightParenPos; /**< Position of ')'. */ TypeConvExpr() : Expr(ASTKind::TYPE_CONV_EXPR) { } }; /** * RefExpr represents an expression of reference. */ struct RefExpr : NameReferenceExpr { Reference ref; /**< Reference of the expr. */ bool isThis{false}; bool isSuper{false}; bool isQuoteDollar{false}; RefExpr() : NameReferenceExpr(ASTKind::REF_EXPR) { } void Clear() noexcept override { Expr::Clear(); ref.target = nullptr; ref.targets.clear(); instTys.clear(); matchedParentTy = nullptr; } std::string ToString() const override; /** * Get the identifier Position of the RefExpr. * @return identifier Position in macrocall file if the RefExpr is expanded from macrocall, * identifier position in curfile otherwise. */ Position GetIdentifierPos() const; }; /** * A PrimitiveTypeExpr is a placeholder Expr for primitive type static function call. * e.g. Int64.foo() is a MemberAccess, whose baseExpr is a PrimitiveTypeExpr, where its TypeKind = * TypeKind::TYPE_INT64. */ struct PrimitiveTypeExpr : Expr { TypeKind typeKind; explicit PrimitiveTypeExpr(TypeKind typeKind) : Expr(ASTKind::PRIMITIVE_TYPE_EXPR), typeKind(typeKind) { } }; /** * Placeholder for invalid expression. */ struct InvalidExpr : Expr { std::string value; InvalidExpr() : Expr(ASTKind::INVALID_EXPR) { EnableAttr(Attribute::IS_BROKEN); } explicit InvalidExpr(const Position& pos) : Expr(ASTKind::INVALID_EXPR) { EnableAttr(Attribute::IS_BROKEN); begin = pos; end = pos; } }; /** * FeatureId * : Identifier (DOT Identifier)* * ; */ struct FeatureId : Node { std::vector identifiers; /**< identifiers with positions */ std::vector dotPoses; /**< position of dots */ FeatureId() : Node(ASTKind::FEATURE_ID) { } std::string ToString() const override; }; /** * A FeaturesDirective represents a feature set in file header. * featuresDirective * : FEATURES NL* FeatureId * (COMMA NL* FeatureId)* * end+; */ struct FeaturesDirective : Node { std::vector content; /**< content of featuresDirective*/ std::vector commaPoses; /**< comma poses */ FeaturesDirective() : Node(ASTKind::FEATURES_DIRECTIVE) { } }; enum class ImportKind { IMPORT_SINGLE, IMPORT_ALIAS, IMPORT_ALL, IMPORT_MULTI, }; /** * single import: a.b.c or a::b.c * multi import: a.b.{c, d} or a::b.{c, d} or a::{c, d} * alias import: a.b as ab or a::b as orgaB * import all: a.b.* or a::b.*. a::* is invalid. */ struct ImportContent : Node { ImportKind kind{ImportKind::IMPORT_SINGLE}; bool hasDoubleColon{}; /**< Has '::', i.e. orgnization name */ /// Prefer to use GetPrefixPath() and GetImportedPackageName(). std::vector prefixPaths; /**< Prefix paths in packageName. */ std::vector prefixPoses; /**< Prefix positions in packageName. */ std::vector prefixDotPoses; /**< Dot positions in packageName. */ // Used for single-import, alias-import, all-import Identifier identifier; /**< Imported identifier. */ // Used for alias-import Position asPos; /**< Position of 'as'. */ Identifier aliasName; /**< Alias identifier. */ // Used for multi-import Position leftCurlPos; /**< Position of '{'. */ std::vector items; /**< Multi-imported items. */ std::vector commaPoses; /**< Positions of each ','. */ Position rightCurlPos; /**< Position of '}'. */ bool isDecl{false}; /**< Identifier is a declaration, set by ImportManager. */ ImportContent() : Node(ASTKind::IMPORT_CONTENT) { } std::string ToString() const override; std::string GetPrefixPath() const; std::string GetImportedPackageName() const; }; /** * An ImportSpec represents an import sentence in file header. */ struct ImportSpec : Node { OwnedPtr modifier; Position importPos; /**< Position of 'import'. */ ImportContent content; std::vector> annotations; /**< Annotation set of the ImportSpec. */ ImportSpec() : Node(ASTKind::IMPORT_SPEC) { } bool IsReExport() const { return modifier && modifier->modifier != TokenKind::PRIVATE; } bool IsImportAll() const { return content.kind == ImportKind::IMPORT_ALL; } bool IsImportSingle() const { return content.kind == ImportKind::IMPORT_SINGLE; } bool IsImportAlias() const { return content.kind == ImportKind::IMPORT_ALIAS; } bool IsImportMulti() const { return content.kind == ImportKind::IMPORT_MULTI; } bool IsPrivateImport() const { return !modifier || modifier->modifier == TokenKind::PRIVATE; } }; /** * A PackageSpec node represents the package of the current file. */ struct PackageSpec : Node { OwnedPtr modifier; Position macroPos; /**< Position of 'macro'. */ Position packagePos; /**< Position of 'package'. */ bool hasDoubleColon{}; /**< Has '::', i.e. orgnization name */ // For the following three vectors, a valid `PackageSpec` should have the same size. /// Prefer to use GetPackageName(). std::vector prefixPaths; /**< Prefix paths in packageName. */ std::vector prefixPoses; /**< Prefix positions in packageName. */ std::vector prefixDotPoses; /**< Dot positions in packageName. */ Identifier packageName; /**< Package name. */ bool hasMacro{false}; bool hasPlatform{false}; bool hasCommon{false}; PackageSpec() : Node(ASTKind::PACKAGE_SPEC) { } std::string GetPackageName() const; }; /** * A File node represents one source file. */ struct File : Node { std::string fileName; /**< File name. Generated by parser */ std::string filePath; /**< File path. Generated by parser */ uint64_t fileHash{0}; /**< Hash of the file. Generated by parser */ OwnedPtr feature; /**< Features that belong to file. Generated by parser, Optional */ OwnedPtr package; /**< The package the file belongs to. Generated by parser, Optional */ std::vector> imports; /**< Import information. Generated by parser */ std::vector> decls; /**< All toplevel decls. Generated by parser */ std::vector> trashBin; Ptr curPackage{nullptr}; /**< Current package node. */ size_t indexOfPackage; bool hasMacro{false}; /**< Whether has definition or call of macro. Generated by parser */ std::string macroCallFilePath; /**< MacroCall File path after macro expansion. */ Position macroCallPosBase{0, 1, 1}; /**< MacroCall pos base for macro expansion. */ std::vector> originalMacroCallNodes; /**< Owner of MacroCall ASTs after macro expansion. */ std::vector> exportedInternalDecls; /**< Ptr to the exported internal declarations. */ bool isCommon{false}; bool isPlatform{false}; File() : Node(ASTKind::FILE) { } bool operator==(const File& r) const { return fileName == r.fileName && filePath == r.filePath && fileHash == r.fileHash; } bool operator!=(const File& r) const { return !(*this == r); } }; enum class AccessLevel { PRIVATE, INTERNAL, PROTECTED, PUBLIC, }; /** * A Package Node represents a package contains multi files and imports. */ struct Package : Node { std::string fullPackageName{"default"}; /**< Package name, default name is 'default'. */ std::vector> files; /**< All src files in package. */ std::vector> genericInstantiatedDecls; /**< The instantiations of the generic decls. */ /** Imported non-generic functions or global const variables with source code. */ std::vector> srcImportedNonGenericDecls; // Following three members are used after sema. std::vector> inlineFuncDecls; /**< Imported inline functions. */ AccessLevel accessible{AccessLevel::PUBLIC}; bool hasSourceImportedDecl{false}; bool isMacroPackage{false}; bool noSubPkg{false}; bool needExported{true}; /**< Parent path of package path is "src", there is no need to export this package. */ Package() : Node(ASTKind::PACKAGE) { } Package(const std::string& pkgName) : Node(ASTKind::PACKAGE), fullPackageName(pkgName) { } bool IsEmpty() const { for (auto& file : files) { if (file->package || !file->decls.empty()) { return false; } // Filter compiler added importSpec of core in stdlib. for (auto& import : file->imports) { if (!import->TestAttr(Attribute::COMPILER_ADD)) { return false; } } } return true; } bool HasFeature() const { for (auto& file : files) { if (file->feature != nullptr && file->feature->content.size() > 0) { return true; } } return false; } }; /** * A PackageDecl node represents imported package. */ struct PackageDecl : Decl { Ptr const srcPackage; /**< Imported source package node. */ PackageDecl(Package& pkg) : Decl(ASTKind::PACKAGE_DECL), srcPackage(&pkg) { identifier = pkg.fullPackageName; fullPackageName = pkg.fullPackageName; if (pkg.TestAttr(Attribute::IMPORTED)) { EnableAttr(Attribute::IMPORTED); } } }; /** * A spawn expression node. */ struct SpawnExpr : Expr { Position spawnPos; /**< The position of 'spawn'. Generated by parser */ OwnedPtr futureObj; /**< The future object that each spawn should created. */ OwnedPtr task; /**< The task that should executed. Generated by parser */ OwnedPtr arg; /**< The thread context argument. Generated by parser, Optional */ Position leftParenPos; /**< Position of '('. Optional */ Position rightParenPos; /**< Position of ')'. Optional */ SpawnExpr() : Expr(ASTKind::SPAWN_EXPR) { } }; /** * A spawn expression node. */ struct SynchronizedExpr : Expr { Position syncPos; /**< The position of 'synchronized'. */ Position leftParenPos; OwnedPtr mutex; /**< The mutex that the synchronized block referred. */ Position rightParenPos; OwnedPtr body; /**< The block inside the Synchronized. */ SynchronizedExpr() : Expr(ASTKind::SYNCHRONIZED_EXPR) { } }; inline bool CompNodeByPos(Ptr n1, Ptr n2) { if (!n1 || !n2) { return n1 < n2; } // 'n1' is less than 'n2' if 'n1' and 'n2' are not null and n1's position is less than n2's position. // Compare pointers if the node positions are same. // NOTE: comparison of Postion will ignore 'fileID', so compare fileID separately. return n1->begin.fileID < n2->begin.fileID || (n1->begin.fileID == n2->begin.fileID && n1->begin < n2->begin) || (n1->begin.fileID == n2->begin.fileID && n1->begin == n2->begin && n1 < n2); } struct CmpNodeByPos { bool operator()(Ptr n1, Ptr n2) const { return CompNodeByPos(n1, n2); } }; using OrderedDeclSet = std::set, CmpNodeByPos>; } // namespace AST } // namespace Cangjie #endif // CANGJIE_AST_NODE_H cangjie_compiler-1.0.7/include/cangjie/AST/NodeX.h000066400000000000000000000025311510705540100216500ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * AST nodes mainly includes eXtra AST nodes. These nodes are not available to Parser and libast, but visible to * semantic checkings. In other words, these nodes are pure semantic nodes. */ #ifndef CANGJIE_AST_NODEX_H #define CANGJIE_AST_NODEX_H #include "cangjie/AST/Node.h" namespace Cangjie::AST { /// @IfAvailable(name: arg, lambda1, lambda2) after macro expansion (before it is a MacroExpandExpr). struct IfAvailableExpr : public Expr { IfAvailableExpr(OwnedPtr namedArg, OwnedPtr lambdaArg1, OwnedPtr lambdaArg2) : Expr{ASTKind::IF_AVAILABLE_EXPR}, arg{std::move(namedArg)}, lambda1{std::move(lambdaArg1)}, lambda2{std::move(lambdaArg2)} { } Ptr GetArg() const { return arg.get(); } Ptr GetLambda1() const { return lambda1.get(); } Ptr GetLambda2() const { return lambda2.get(); } private: OwnedPtr arg; OwnedPtr lambda1; OwnedPtr lambda2; }; } // namespace Cangjie::AST #endif cangjie_compiler-1.0.7/include/cangjie/AST/PrintNode.h000066400000000000000000000011631510705540100225350ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares AST and related data print apis. */ #ifndef CANGJIE_AST_PRINTNODE_H #define CANGJIE_AST_PRINTNODE_H #include #include "cangjie/AST/Node.h" namespace Cangjie { void PrintNode(Ptr node, unsigned indent = 0, const std::string addition = ""); } // namespace Cangjie #endif // CANGJIE_AST_PRINTNODE_H cangjie_compiler-1.0.7/include/cangjie/AST/Query.h000066400000000000000000000041361510705540100217430ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Query and related classes. */ #ifndef CANGJIE_AST_QUERY_H #define CANGJIE_AST_QUERY_H #include #include #include #include "cangjie/Basic/Position.h" namespace Cangjie { /** * Operations on the Query node. */ enum class Operator { AND, /**< Condition1 && Condition2. */ OR, /**< Condition1 || Condition2. */ NOT, /**< Condition1 ! Condition2. */ }; /** * The type of the Query node. */ enum class QueryType { OP, STRING, POS, NONE }; /** * Search term match kind. */ enum class MatchKind { PRECISE, /**< Query string "name: foo". */ PREFIX, /**< Query string "name: foo*". */ SUFFIX /**< Query string "name: *foo". */ }; /** * Query is a query tree, Leaf Node represent the query, none leaf node is query condition. */ struct Query { Query(std::string key, std::string value) : key(std::move(key)), value(std::move(value)) { } Query(std::string key, std::string value, MatchKind matchKind) : key(std::move(key)), value(std::move(value)), matchKind(matchKind) { } explicit Query(Operator op) : op(op) { type = QueryType::OP; } Query() = default; std::string key; /**< Leaf node's key. */ std::string value; /**< Leaf node's value. */ std::unordered_set fileHashes; /**< For filter certain files. */ Position pos; /**< Save the Position value. */ std::string sign{"="}; /**< Only position filed support '=', '<', '<='. */ QueryType type{QueryType::NONE}; std::unique_ptr left; std::unique_ptr right; Operator op{Operator::AND}; MatchKind matchKind{MatchKind::PRECISE}; void PrettyPrint(std::string& result) const; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/include/cangjie/AST/RecoverDesugar.h000066400000000000000000000015511510705540100235540ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This is a file containing all functions that can recover ast node from desugar state. */ #ifndef CANGJIE_RECOVERDESUGAR_H #define CANGJIE_RECOVERDESUGAR_H #include "cangjie/AST/Node.h" namespace Cangjie::AST { void RecoverToBinaryExpr(BinaryExpr& be); void RecoverToUnaryExpr(UnaryExpr& ue); void RecoverToSubscriptExpr(SubscriptExpr& se); void RecoverToAssignExpr(AssignExpr& ae); void RecoverCallFromArrayExpr(CallExpr& ce); void RecoverJArrayCtorCall(CallExpr& ce); void RecoverToCallExpr(CallExpr& ce); void RecoverFromVariadicCallExpr(CallExpr& ce); } #endif // CANGJIE_RECOVERDESUGAR_H cangjie_compiler-1.0.7/include/cangjie/AST/ReferenceType.h000066400000000000000000000022741510705540100233770ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the method of determining the reference type. */ #ifndef CANGJIE_AST_REFERENCETYPE_H #define CANGJIE_AST_REFERENCETYPE_H #include "cangjie/AST/Types.h" namespace Cangjie { namespace AST { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND inline bool IsReferenceType(const AST::Ty& ty) { // With CJNATIVE-BE, we translate // 1) Classes, // 2) Option, // 3) Arrays // as reference types. bool isRefOption = false; if (ty.IsCoreOptionType()) { auto& enumTy = static_cast(ty); auto elemTy = enumTy.typeArgs[0]; bool elemTyIsOption = elemTy->IsCoreOptionType(); // notice that Option> is not reference type. isRefOption = IsReferenceType(*elemTy) && !elemTyIsOption; } return ty.IsClassLike() || ty.IsArray() || isRefOption; } #endif } // namespace AST } // namespace Cangjie #endif // CANGJIE_AST_REFERENCETYPE_H cangjie_compiler-1.0.7/include/cangjie/AST/ScopeManagerApi.h000066400000000000000000000064111510705540100236320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the ScopeManagerApi. */ #ifndef CANGJIE_AST_SCOPEMANAGERAPI_H #define CANGJIE_AST_SCOPEMANAGERAPI_H #include #include "cangjie/AST/Symbol.h" namespace Cangjie { // Toplevel scope name "a". const std::string TOPLEVEL_SCOPE_NAME = "a"; class ASTContext; /** * Static methods and fields of ScopeManager. */ struct ScopeManagerApi { /** * Remove the child scope name info. * * @return A scope name without tailing '_*', given a0a_a, will get a0a. */ static std::string GetScopeNameWithoutTail(const std::string& scopeName) { auto found = scopeName.find_last_of(childScopeNameSplit); if (found != std::string::npos) { return scopeName.substr(0, found); } else { return scopeName; } } /** * Get parent scope name. * * @param scopeName The scope name. * * @return Parent scope name, parent scope name of @c aa0ab_a is @c aa, * if already reach the toplevel, will get empty string. */ static std::string GetParentScopeName(const std::string& scopeName) { if (scopeName == "A") { return ""; } // If already reach toplevel. if (scopeName == TOPLEVEL_SCOPE_NAME) { return "A"; } auto found = scopeName.find_last_of(scopeNameSplit); if (found == std::string::npos) { return TOPLEVEL_SCOPE_NAME; } return scopeName.substr(0, found); } /** * Get child scope name. * * @return Child scope name of scope gate, example: aa0ab_a -> aa0ab0a. */ static std::string GetChildScopeName(const std::string& scopeName) { std::string result = scopeName; auto found = result.find_last_of(childScopeNameSplit); if (found == std::string::npos) { return result; } result.replace(result.find(childScopeNameSplit), 1, std::string(1, scopeNameSplit)); return result; } /** * Get scope gate of @p scopeName. * * @return scope gate. */ static AST::Symbol* GetScopeGate(const ASTContext& ctx, const std::string& scopeName); /** * Get scope gate name of @p scopeName. * * @return scope gate name, scope gate name of a0b is a_b, a0b_a is a_b. */ static std::string GetScopeGateName(const std::string& scopeName); /** * Each level of scope name are encoding will 52 chars, split by '0'. * * ``` * func foo * { * // Scope name here is a0a. * func goo * { * // Scope name here is a0a0a. * } * } * ``` */ constexpr static char scopeNameSplit = '0'; /** * Scope gate use this split to contain child scope info. * * ``` * func foo // scope name of node FuncDecl @c foo is a_a * { * // scope name here is a0a * } * ``` */ constexpr static char childScopeNameSplit = '_'; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/include/cangjie/AST/Searcher.h000066400000000000000000000242241510705540100223720ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Searcher related classes. */ #ifndef CANGJIE_AST_SEACHER_H #define CANGJIE_AST_SEACHER_H #include #include #include #include "cangjie/AST/Query.h" #include "cangjie/AST/Symbol.h" namespace Cangjie { class ASTContext; /** * The Node of @p Trie tree, which contains the information of the next node and the value, prefix, suffix of current * node. If current node is the end of a path which represents a string, the @p rawValue contains the value of the * string. */ class TrieNode { public: using Next = std::map>; Next next; /**< Next trie node map. */ char c; /**< Trie node's value. */ std::string value; /**< Store string value at the leaf node. */ std::string prefix; /**< Store prefix string of current node. */ std::set suffixes; /**< Store suffix strings of current node. */ std::set ids; /**< Symbol ID set. */ uint32_t depth = 0; /**< Depth of the node in the tree. */ TrieNode* parent{nullptr}; /**< Pointer to the parent node. */ explicit TrieNode(char c) : c(c) { } }; /** * @p Trie is a class which contains a tree whose single path represents a string. Trie tree is used to do prefix and * suffix string pattern match. */ class Trie { public: std::unique_ptr root; Trie() { root = std::make_unique('R'); } explicit Trie(const std::string& value) { root = std::make_unique('R'); Insert(value); } void Reset() { root = std::make_unique('R'); } void Reset(const std::string& value) { root = std::make_unique('R'); Insert(value); } /** * Insert a string, the prefix string and the suffix strings of current * node are stored in @c prefix and @c suffixes. * The value of the given string is stored in @c value at the leaf node. * @param value String value to be inserted. */ void Insert(const std::string& value); /** * Insert a string, the prefix string and the suffix strings of current * node are stored in @c prefix and @c suffixes. * The value of the given string is stored in @c value at the leaf node. * @param value String value to be inserted. * @param id Symbol id. */ void Insert(const std::string& value, AST::Symbol& id); /** * Delete the node that has been inserted by insert method above. */ void Delete(const std::string& value, AST::Symbol& id); /** * Get the root node to begin traverse for prefix search. * @param prefix Prefix search string. * @return The last node of the @p prefix string, when input @a0b, will point to the node of b. */ TrieNode* GetPrefixRootNode(const std::string& prefix) const; /** * Traverse from the prefix root node to the bottom, get value from the leaf and add to match results. * @param prefix Prefix search string. */ std::vector PrefixMatch(const std::string& prefix) const; /** * Traverse from the root node to the bottom, when suffix value matched, concatenate the prefix and suffix, add to * match results. * @param suffix Suffix search string. */ std::vector SuffixMatch(const std::string& suffix) const; }; /** * Simplified range query realization. */ class PosSearchApi { public: /** * Cast position to a string monotonously. */ static std::string PosToStr(const Position& pos); /** * Find the common root TrieNode of the given position in PosTrie. */ static TrieNode* FindCommonRootInPosTrie(const Trie& posTrie, const Position& pos); /** * Find the common root TrieNode of the given position string in PosTrie. */ static TrieNode* FindCommonRootInPosTrie(const Trie& posTrie, const std::string& pos); /** * Get IDs of symbols which is within a position range. */ static std::set GetIDsWithinPosXByRange(const Trie& posTrie, const Position& startPos, const Position& endPos, bool isLeftClose = true, bool isRightClose = false); /** * Get IDs of symbols which is greater than the @p pos. * @param pos The position to compare. * @param isClose Default is true, [pos, ...). * @return Symbol IDs which are greater than @p pos. */ static std::set GetIDsGreaterThanPos(const Trie& posTrie, const Position& pos, bool isClose = true); /** * Get IDs of symbols which is less than the @p pos. * @param pos The position to compare. * @param isClose Default is true, (..., pos]. * @return Symbol IDs which are less than @p pos. */ static std::set GetIDsLessThanPos(const Trie& posTrie, const Position& pos, bool isClose = true); static void GetIDsLessThanPosOfDepth( TrieNode& n, const std::string& posStr, std::set& ids, bool& isClose); const static int MAX_DIGITS_FILE = 4; /**< Max num of files to compile once is 9999. */ const static int MAX_DIGITS_LINE = 5; /**< Max num of lines of a file is 99999. */ const static int MAX_DIGITS_COLUMN = 5; /**< Max column width is 99999. */ constexpr static int MAX_DIGITS_POSITION = MAX_DIGITS_FILE + MAX_DIGITS_LINE + MAX_DIGITS_COLUMN; constexpr static int MAX_LINE = static_cast(10e5); const static int MAX_COLUMN = static_cast(10e3); }; using Order = std::function; /** * Sort functions. */ struct Sort { /* * Sort by position ascending order. */ static Order posAsc; /* * Sort by position descending order. */ static Order posDesc; }; /** * AST Searcher will find the AST node by Query. */ class Searcher { public: /** * Search symbol table in @p ctx, according to the @p query. */ std::vector Search(const ASTContext& ctx, const std::string& query, const Order& order = Sort::posDesc, const std::unordered_set& fileHashes = {}); std::vector Search(const ASTContext& ctx, const Query* query, const Order& order = Sort::posDesc); void InvalidateCache() { cache.clear(); } /** * Clear all caches except @p query. */ void InvalidateCacheBut(const std::string& query); std::vector GetScopeNamesByPrefix(const ASTContext& ctx, const std::string& prefix) const; void SetCache(std::unordered_map>& newCache) { cache = newCache; } std::unordered_map> GetCache() { return cache; } private: unsigned long StrToUint(const ASTContext& ctx, const std::string& queryVal) const; // Get the IDS of symbols in symbol tables whose position equal to the given @p pos. std::set GetIDsByPosEQ( const ASTContext& ctx, const Position& pos, bool isLeftClose = true, bool isRightClose = true) const; // Get the IDS of symbols in symbol tables whose position is less than the given @p pos. If @contain is true, also // return the symbols contain the @p pos. std::set GetIDsByPosLT(const ASTContext& ctx, const Position& pos, bool contain = false) const; // Get the IDS of symbols in symbol tables whose position is greater than the given @p pos. If @contain is true, // also return the symbols contain the @p pos. std::set GetIDsByPosGT(const ASTContext& ctx, const Position& pos, bool contain = false) const; std::set GetIDsByName(const ASTContext& ctx, const std::string& name) const; std::set GetIDsByNamePrefix(const ASTContext& ctx, const std::string& prefix) const; std::set GetIDsByNameSuffix(const ASTContext& ctx, const std::string& suffix) const; std::set GetIDsByScopeName(const ASTContext& ctx, const std::string& scopeName) const; std::set GetIDsByScopeLevel(const ASTContext& ctx, uint32_t scopeLevel) const; std::set GetIDsByASTKind(const ASTContext& ctx, const std::string& astKind) const; std::vector GetAstKindsBySuffix(const ASTContext& ctx, const std::string& suffix) const; std::set PerformSearch(const ASTContext& ctx, const Query& query); std::set GetIDs(const ASTContext& ctx, const Query& query) const; std::set GetIDsByPos(const ASTContext& ctx, const Query& query) const; std::set GetIDsByName(const ASTContext& ctx, const Query& query) const; std::set GetIDsByScopeLevel(const ASTContext& ctx, const Query& query) const; std::set GetIDsByScopeName(const ASTContext& ctx, const Query& query) const; std::set GetIDsByASTKind(const ASTContext& ctx, const Query& query) const; static std::set Intersection(const std::set& set1, const std::set& set2); static std::set Union(const std::set& set1, const std::set& set2); std::set Difference(const std::set& set1, const std::set& set2) const; bool InFiles(const std::unordered_set& files, const AST::Symbol& id) const; std::pair, bool> FindInSearchCache( const Query& query, const std::string& normalizedQuery); std::vector FilterAndSortSearchResult( const std::set& ids, const Query& query, const Order& order) const; std::unordered_map> cache; friend class PosSearchApi; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/include/cangjie/AST/Symbol.h000066400000000000000000000052471510705540100221070ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares Symbol related classes. */ #ifndef CANGJIE_AST_SYMBOL_H #define CANGJIE_AST_SYMBOL_H #include #include #include "cangjie/AST/Node.h" namespace Cangjie { class Collector; struct HashID { uint64_t hash64 = 0; uint32_t fieldID = 0; friend bool operator==(const HashID& lhs, const HashID& rhs) { return std::tie(lhs.hash64, lhs.fieldID) == std::tie(rhs.hash64, rhs.fieldID); } friend bool operator<(const HashID& lhs, const HashID& rhs) { return std::tie(lhs.hash64, lhs.fieldID) < std::tie(rhs.hash64, rhs.fieldID); } }; namespace AST { static std::unordered_map ASTKIND_TO_STRING_MAP{ #define ASTKIND(KIND, VALUE, NODE, SIZE) {ASTKind::KIND, VALUE}, #include "cangjie/AST/ASTKind.inc" #undef ASTKIND }; class SymbolApi { public: static HashID NextHashID(uint64_t fileHash) { ids++; HashID hashIDs{ .hash64 = fileHash, .fieldID = ids }; return hashIDs; } static void ResetID() { ids = -1u; } private: inline static std::atomic_uint32_t ids = -1u; }; struct Symbol { const std::string name; /**< Symbol name. */ Symbol* const id{nullptr}; /**< Symbol id. */ Ptr const node{nullptr}; /**< AST node. */ const HashID hashID{0, 0}; /**< Combine file content hash id and symbol id. */ const uint32_t scopeLevel{0}; /**< Scope level, toplevel scope is 0. */ const std::string scopeName; /**< Managed by ScopeManager. */ const ASTKind astKind{ASTKind::NODE}; /**< AST kind, for quick filter. */ Ptr target{nullptr}; /**< Target for all ref symbol. */ bool invertedIndexBeenDeleted{false}; /**< Mark whether inverted index has been deleted. */ void UnbindTarget() { target = nullptr; } private: // Only allow 'Cangjie::Collector' to create symbol. friend class Cangjie::Collector; Symbol(uint64_t fileHash, std::string name, Node& src, uint32_t scopeLevel, std::string scopeName) : name(std::move(name)), id(this), node(&src), hashID(SymbolApi::NextHashID(fileHash)), scopeLevel(scopeLevel), scopeName(std::move(scopeName)), astKind(src.astKind) { } Symbol(const Symbol& sym) = delete; }; } // namespace AST } // namespace Cangjie #endif // CANGJIE_AST_SYMBOL_H cangjie_compiler-1.0.7/include/cangjie/AST/TypeKind.inc000066400000000000000000000060431510705540100227060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * Only list user writable types in this file. * The ORDER of tokens below is IMPORTANT. Once modified, the following must be modified as well. * 'enum TypeKind:int16' in ModuleFormat.fbs * 'external class TokenKindHelper' in TokenKind.cj std.ast * 'Patterns: Array<(TokenKind*UInt16*String*String)>' in TokenKind.cj * 'operator func ==(right: TokenKind)' in TokenKind.cj * 'external enum TokenKind' in TokenKind.cj */ #ifdef TYPE_KIND TYPE_KIND(TYPE_INVALID, InvalidTy, "Invalid") /**< Invalid type. */ TYPE_KIND(TYPE_UNIT, PrimitiveTy, "Unit") /**< unit. */ TYPE_KIND(TYPE_INT8, PrimitiveTy, "Int8") /**< Int8. */ TYPE_KIND(TYPE_INT16, PrimitiveTy, "Int16") /**< Int16. */ TYPE_KIND(TYPE_INT32, PrimitiveTy, "Int32") /**< Int32. */ TYPE_KIND(TYPE_INT64, PrimitiveTy, "Int64") /**< Int64. */ TYPE_KIND(TYPE_INT_NATIVE, PrimitiveTy, "IntNative") /**< IntNative. */ TYPE_KIND(TYPE_IDEAL_INT, PrimitiveTy, "Int") /**< Ideal int for default integer literal. */ TYPE_KIND(TYPE_UINT8, PrimitiveTy, "UInt8") /**< UInt8. */ TYPE_KIND(TYPE_UINT16, PrimitiveTy, "UInt16") /**< UInt16. */ TYPE_KIND(TYPE_UINT32, PrimitiveTy, "UInt32") /**< UInt32. */ TYPE_KIND(TYPE_UINT64, PrimitiveTy, "UInt64") /**< UInt64. */ TYPE_KIND(TYPE_UINT_NATIVE, PrimitiveTy, "UIntNative") /**< UIntNative. */ TYPE_KIND(TYPE_FLOAT16, PrimitiveTy, "Float16") /**< Float16. */ TYPE_KIND(TYPE_FLOAT32, PrimitiveTy, "Float32") /**< Float32. */ TYPE_KIND(TYPE_FLOAT64, PrimitiveTy, "Float64") /**< Float64. */ TYPE_KIND(TYPE_IDEAL_FLOAT, PrimitiveTy, "Float") /**< Ideal float for default float literal. */ TYPE_KIND(TYPE_RUNE, PrimitiveTy, "Rune") /**< Rune. */ TYPE_KIND(TYPE_NOTHING, NothingTy, "Nothing") /**< The Nothing type. */ TYPE_KIND(TYPE_BOOLEAN, PrimitiveTy, "Bool") /**< Bool. */ TYPE_KIND(TYPE_TUPLE, TupleTy, "Tuple") /**< Tuple. */ TYPE_KIND(TYPE_ENUM, EnumTy, "Enum") /**< Enum. */ TYPE_KIND(TYPE_FUNC, FuncTy, "Function") /**< Function type) like (float32) -> Int32 */ TYPE_KIND(TYPE_STRUCT, StructTy, "Struct") /**< Struct. */ TYPE_KIND(TYPE_ARRAY, ArrayTy, "RawArray") /**< Array. */ TYPE_KIND(TYPE_VARRAY, VArrayTy, "VArray") /**< VArray. */ TYPE_KIND(TYPE_POINTER, PointerTy, "CPointer") /**< Pointer. */ TYPE_KIND(TYPE_CSTRING, CStringTy, "CString") /**< CString. */ TYPE_KIND(TYPE_CLASS, ClassTy, "Class") /**< Class. */ TYPE_KIND(TYPE_INTERFACE, InterfaceTy, "Interface") /**< Interface. */ TYPE_KIND(TYPE, TypeAliasTy, "TypeAlias") /**< Type alias ty. */ TYPE_KIND(TYPE_GENERICS, GenericsTy, "Generic") /**< Generic type. */ #endif cangjie_compiler-1.0.7/include/cangjie/AST/Types.h000066400000000000000000001021021510705540100217320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Type related classes. */ #ifndef CANGJIE_AST_TYPES_H #define CANGJIE_AST_TYPES_H #include #include #include #include #include #include #include #include #include #include #include #include "cangjie/Lex/Token.h" #include "cangjie/Utils/SafePointer.h" namespace Cangjie::AST { struct Decl; struct GenericParamDecl; struct ClassDecl; struct ClassLikeDecl; struct StructDecl; struct InterfaceDecl; struct EnumDecl; struct TypeAliasDecl; struct GenericsTy; enum class TypeKind { /** * User writable types are defined in TypeKind.inc. */ #define TYPE_KIND(KIND, TYPE, NAME) KIND, #include "cangjie/AST/TypeKind.inc" #undef TYPE_KIND /** NOTE: Following type kinds are only used during type checking, and will not appeared on user's code. */ TYPE_ANY, /**< Temporary Any type, will be replaced when start type checking. */ TYPE_INTERSECTION, /**< The intersection type. */ TYPE_UNION, /**< The union type. */ TYPE_QUEST, /**< The quest type. If type is not annotated) mark quest first. */ TYPE_INITIAL, /**< Initial type for any 'Ptr' 's initialization. */ }; inline const std::map COMPOUND_ASSIGN_EXPR_MAP = {{TokenKind::ADD_ASSIGN, TokenKind::ADD}, {TokenKind::SUB_ASSIGN, TokenKind::SUB}, {TokenKind::MUL_ASSIGN, TokenKind::MUL}, {TokenKind::EXP_ASSIGN, TokenKind::EXP}, {TokenKind::DIV_ASSIGN, TokenKind::DIV}, {TokenKind::MOD_ASSIGN, TokenKind::MOD}, {TokenKind::AND_ASSIGN, TokenKind::AND}, {TokenKind::OR_ASSIGN, TokenKind::OR}, {TokenKind::BITAND_ASSIGN, TokenKind::BITAND}, {TokenKind::BITOR_ASSIGN, TokenKind::BITOR}, {TokenKind::BITXOR_ASSIGN, TokenKind::BITXOR}, {TokenKind::LSHIFT_ASSIGN, TokenKind::LSHIFT}, {TokenKind::RSHIFT_ASSIGN, TokenKind::RSHIFT}}; /** * Base type. */ struct Ty { /** Represent the Sema type kind. * W: no. * R: ImportManager, Sema, GenericInstantiator, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ const TypeKind kind; /** Name of the user-defined type constructors. Primitive types do not have type constructors. * W: ImportManager. * R: Driver, ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string name; /** Type parameters for generic types or other element types. * W: Sema. * R: ImportManager, Sema, GenericInstantiator, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::vector> typeArgs{}; /** Destructor. * U: no. */ virtual ~Ty() = default; /** Return whether a ty is integer. * U: ImportManager, Sema, CHIR, HLIRCodeGen. */ bool IsInteger() const; bool IsIntegerSubType() const; /** Return whether a ty is signed integer. * U: Sema, AST2CHIR, CHIR, HLIRCodeGen. */ bool IsSignedInteger() const; /** Return whether a ty is unsigned integer. * U: Sema, AST2CHIR, CHIR, HLIRCodeGen. */ bool IsUnsignedInteger() const; /** Return whether a ty is float. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen. */ bool IsFloating() const; bool IsFloatingSubType() const; /** Return whether a ty is bool. * U: Sema, HLIRCodeGen. */ bool IsBoolean() const; bool IsBooleanSubType() const; /** Return whether a ty is char. * U: Sema. */ bool IsRune() const; /** Return whether a ty is ideal. * U: Sema. */ bool IsIdeal() const; /** Return whether a ty is invalid. * U: Sema, AST2CHIR, HLIRCodeGen. */ bool IsInvalid() const; /** Return whether a ty is unit. * U: Sema, HLIRCodeGen, LLVMCodeGen. */ bool IsUnit() const; /** Return whether a ty is unit or nothing. * U: Sema, HLIRCodeGen, LLVMCodeGen. */ bool IsUnitOrNothing() const; /** Return whether a ty is quest. * U: Sema. */ bool IsQuest() const; /** Return whether a ty is primitive. * U: ImportManager, Sema, GenericInstantiator, HLIRCodeGen, LLVMCodeGen. */ bool IsPrimitive() const; bool IsPrimitiveSubType() const; /** Return whether a ty can be extend. * U: Sema. */ bool IsExtendable() const; /** Return whether a ty is numeric. * U: Sema. */ bool IsNumeric() const; /** Return whether a ty is native. * U: Sema. */ bool IsNative() const; /** Return whether a ty is builtin. * U: Sema, AST2CHIR. */ bool IsBuiltin() const; /** Return whether a ty is immutable. * U: Sema. */ bool IsImmutableType() const; /** Return whether a ty is generic. * U: Sema, GenericInstantiator, AST2CHIR, HLIRCodeGen. */ bool IsGeneric() const; /** Return whether a ty is a placeholder type variable. * U: Sema. */ bool IsPlaceholder() const; /** Return whether a ty is struct. * U: Sema, AST2CHIR, HLIRCodeGen. */ bool IsStruct() const; /** Return whether a ty is enum. * U: Sema, GenericInstantiator, LLVMCodeGen. */ bool IsEnum() const; /** Return whether a ty is option. * U: Sema, CodeGen */ bool IsCoreOptionType() const; /** Return whether a ty is class. * U: Sema. */ bool IsClass() const; /** Return whether a ty is interface. * U: Sema. */ bool IsInterface() const; /** Return whether a ty is intersection. * U: Sema. */ bool IsIntersection() const; /** Return whether a ty is union. * U: Sema. */ bool IsUnion() const; /** Return whether a ty is nominal. * U: Sema. */ bool IsNominal() const; /** Return whether a ty is built-in RawArray. * U: Sema, GenericInstantiator, HLIRCodeGen, LLVMCodeGen, Utils. */ bool IsArray() const; /** Return whether a ty is struct Array that defined in core package. * U: Sema, GenericInstantiator, HLIRCodeGen, LLVMCodeGen, Utils. */ bool IsStructArray() const; /** Return whether a ty is CPointer. * U: Sema, HLIRCodeGen, LLVMCodeGen, CHIR. */ bool IsPointer() const; /** Return whether a ty is CFunc. * U: Sema, HLIRCodeGen, LLVMCodeGen. */ virtual bool IsCFunc() const { return false; }; /** Return whether a ty is C char *. * U: Sema, CodeGen, AST, CHIR */ bool IsCString() const; /** Return whether a ty is erase generic. * U: Sema. */ bool IsEraseGeneric() const; /** Return whether a ty is class like. * U: Sema, AST2CHIR, HLIRCodeGen, LLVMCodeGen. */ bool IsClassLike() const; /** Return whether a ty is func. * U: Sema, AST2CHIR, HLIRCodeGen, LLVMCodeGen. */ bool IsFunc() const; /** Return whether a ty is tuple. * U: Sema, LLVMCodeGen. */ bool IsTuple() const; /** Return whether a ty is closure. * U: AST2CHIR, CodeGen */ bool IsClosureType() const; /** Return whether a ty is object. * U: Sema. */ bool IsObject() const; /** Return whether a ty is any. * U: Sema. */ bool IsAny() const; /** Return whether a ty is CType interface. * U: Sema. */ bool IsCType() const; /** Return whether a ty is nothing. * U: Sema. */ bool IsNothing() const; /** Return whether a ty is string. * U: Sema, HLIRCodeGen. */ bool IsString() const; /** Return whether a ty is range. * U: Sema. */ bool IsRange() const; /** Return whether a ty contains given @p ty. * U: Sema. */ bool Contains(Ptr ty) const; /** Return whether a ty has InvalidTy. * U: Sema, GenericInstantiator. */ bool HasInvalidTy() const; /** Return whether a ty has IdealTy. * U: Sema, GenericInstantiator. */ bool HasIdealTy() const; /** Return whether a ty has QuestTy. * U: Sema. */ bool HasQuestTy() const; /** Return whether a ty has Generic. * U: Sema, GenericInstantiator, AST2CHIR. */ bool HasGeneric() const; /** Return whether a ty has IntersectionTy. * U: Sema. */ bool HasIntersectionTy() const; /** Return whether a ty has placeholder ty var. * U: Sema. */ bool HasPlaceholder() const; static Ptr GetPrimitiveUpperBound(Ptr ty); /** Return whether two tys has the same typeArgs number. * U: Sema. */ static bool IsTyArgsSizeEqual(const Ty& ty1, const Ty& ty2); /** A correct type does not contain InvalidTy or nullptr. * U: Sema, GenericInstantiator, AST2CHIR. */ static bool IsTyCorrect(Ptr ty) noexcept; /** Return whether some tys are correct. * U: Sema. */ static bool AreTysCorrect(const std::vector>& tys) { return std::all_of(tys.begin(), tys.end(), [](auto& ty) { return Ty::IsTyCorrect(ty); }); } static bool AreTysCorrect(const std::set>& tys) { return std::all_of(tys.begin(), tys.end(), [](auto& ty) { return Ty::IsTyCorrect(ty); }); } /** APIs to check given @p ty 's category. */ static bool IsMetCType(const AST::Ty& ty); // Check if ty is CType which is based Pointer, include CString, CPointer and CFunc. static bool IsCTypeBasePointer(const AST::Ty& ty) { return ty.IsPointer() || ty.IsCString() || ty.IsCFunc(); } static bool IsCTypeConstraint(const AST::Ty& ty); static bool IsPrimitiveCType(const AST::Ty& ty); static bool IsCStructType(const AST::Ty& ty); /** Return whether some tys has Generic. * U: GenericInstantiator. */ static bool ExistGeneric(const std::vector>& tySet); /** Return the unique name of a ty if not null, otherwise return "Invalid". * U: Sema. */ static std::string ToString(Ptr ty); /** * Connect all ty names in stable order with the given delimiter. */ template static std::string GetTypesToStableStr(const Container& tys, const std::string& delimiter); template static std::string GetTypesToStr(const Container& tys, const std::string& delimiter) { std::string str; for (auto it = tys.begin(); it != tys.end(); it++) { if (it == tys.begin()) { str += ToString(*it); } else { str += delimiter + ToString(*it); } } return str; } /** * Get ty's corresponding declaration. The method will return instantiated decl if it exists. */ template static Ptr GetDeclOfTy(Ptr ty); /** * Get ty's corresponding declaration, which always be generic decl if it has generic. */ template static Ptr GetDeclPtrOfTy(Ptr ty); /** * Get instantiated ty's corresponding generic ty. */ static Ptr GetGenericTyOfInsTy(const AST::Ty& ty); static Ptr GetInitialTy(); template static bool NominalTyEqualTo(const TypeDeclT& base, const Ty& other); /** Return whether this ty is an initial ty value. */ static bool IsInitialTy(Ptr ty); /** Return generic typeArgs (among the candidates) of a ty. * U: Sema. */ std::set> GetGenericTyArgs(); std::set> GetGenericTyArgs(const std::set>& candidates); /** Return whether the typeArgs of a ty is only one. * U: Sema. */ bool IsTyArgsSingleton() const; /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ virtual std::string String() const = 0; static std::string KindName(TypeKind k); /** Print typeArgs of a ty. * U: Sema. */ std::string PrintTypeArgs() const; /** * Hash current ty. * For nominal type, hash with typeArgs' address and associated decl's address. * For other types, hash with typeArgs' address, members and typeKind. * For basic implementation, hash with current ty's address. */ virtual size_t Hash() const; virtual bool operator==(const Ty& other) const { return this == &other; } protected: /** Constructor. * U: Sema. */ explicit Ty(TypeKind k) : kind(k) { } bool invalid{false}; // Type is invalid if any of element is invalid. bool generic{false}; // Type is generic if any of element is generic. }; /** * Initial type. */ struct InitialTy : Ty { InitialTy() noexcept : Ty(TypeKind::TYPE_INITIAL) { invalid = true; // Initial ty is used instead of nullptr, so also marked as invalid. } /** * Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override { return Ty::String(); } }; /** * Invalid type. */ struct InvalidTy : Ty { /** Constructor. * U: no. */ InvalidTy() noexcept : Ty(TypeKind::TYPE_INVALID) { invalid = true; } /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override { return Ty::String(); } }; /** * If return type is uncertain, annotate a ? ty here. */ struct QuestTy : Ty { /** Constructor. * U: Sema. */ QuestTy() noexcept : Ty(TypeKind::TYPE_QUEST) { } /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. (But actually only can be called by Sema) */ std::string String() const override { return Ty::String(); } }; /** * Primitive type. Contains integer, float, bool, char, unit and nothing. */ struct PrimitiveTy : Ty { // To decide actual type of IntNative. static constexpr uint64_t GetArchBitness() { // 1 byte = 8 bit return 8 * sizeof(void*); } /** To decide actual type of IntNative. * W: no. * R: Sema, CHIR, HLIRCodeGen, LLVMCodeGen. */ uint64_t bitness = GetArchBitness(); /** Constructor. * U: Sema. */ explicit PrimitiveTy(TypeKind k) noexcept : Ty(k) { } /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override { return Ty::String(); } }; /** * Nothing type. Inherited from the 'PrimitiveTy' to accommodate all usages. */ struct NothingTy : PrimitiveTy { /** Constructor. * U: TypeManager. */ explicit NothingTy() noexcept : PrimitiveTy(TypeKind::TYPE_NOTHING) { } }; /** * Top type. */ struct AnyTy : Ty { /** Constructor. * U: no. */ explicit AnyTy() noexcept : Ty(TypeKind::TYPE_ANY) { } /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override { return "Any"; } }; /** * Array type. */ struct ArrayTy : Ty { /** Array dimensions. * W: no. * R: ImportManager, Sema, GenericInstantiator, CHIR, HLIRCodeGen, LLVMCodeGen. */ unsigned int dims{0}; /** Constructor. * U: Sema. */ ArrayTy(Ptr elemTy, unsigned int dims) : Ty(TypeKind::TYPE_ARRAY), dims(dims) { name = "RawArray"; typeArgs.emplace_back(elemTy); invalid = !elemTy || elemTy->HasInvalidTy(); generic = elemTy && elemTy->HasGeneric(); } /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override; size_t Hash() const override; bool operator==(const Ty& other) const override; }; /** * VArray type. */ struct VArrayTy : public Ty { /** VArray size. * W: Sema. * R: ImportManager, Sema, Chir, LLVMCodeGen, HLIRCodeGen. */ int64_t size{0}; /** Constructor. * U: Sema. */ VArrayTy(Ptr elemTy, int64_t size) : Ty(TypeKind::TYPE_VARRAY), size(size) { name = "VArray"; typeArgs.emplace_back(elemTy); invalid = !elemTy || elemTy->HasInvalidTy(); generic = elemTy && elemTy->HasGeneric(); } /** Return the unique name of a ty. * U: Sema. */ std::string String() const override { std::string ge = "<" + Ty::ToString(typeArgs[0]) + ", $" + std::to_string(size) + ">"; return name + ge; } size_t Hash() const override; bool operator==(const Ty& other) const override; }; /** * Pointer type. */ struct PointerTy : Ty { /** Constructor. * U: Sema. */ PointerTy(Ptr elemTy) : Ty(TypeKind::TYPE_POINTER) { name = "CPointer"; typeArgs.emplace_back(elemTy); invalid = !elemTy || elemTy->HasInvalidTy(); generic = elemTy && elemTy->HasGeneric(); } /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override; size_t Hash() const override; bool operator==(const Ty& other) const override { return kind == other.kind && typeArgs == other.typeArgs; } }; struct CStringTy : Ty { /** Constructor. * U: Sema, Mangle */ CStringTy() noexcept : Ty(TypeKind::TYPE_CSTRING) { name = "CString"; } /** Return the unique name of a ty. * U: Sema, Mangle */ std::string String() const override { return "CString"; } }; /** * Tuple type. */ struct TupleTy : Ty { /** * Mark whether a function is a ClosureTy. This will only be used on CHIR node's ty, never appeared on AST node. */ bool isClosureTy{false}; /** Constructor. * U: Sema. */ explicit TupleTy(std::vector> elemTys, bool isClosureTy = false) : Ty(TypeKind::TYPE_TUPLE), isClosureTy(isClosureTy) { name = "Tuple"; typeArgs = elemTys; invalid = !Ty::AreTysCorrect(typeArgs); generic = Ty::ExistGeneric(typeArgs); } /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override; size_t Hash() const override; bool operator==(const Ty& other) const override; }; /** * Function type. */ struct FuncTy : Ty { /** * Function param types. * W: Sema. * R: ImportManager, Sema, GenericInstantiator, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ const std::vector> paramTys{}; /** * Function return type. * W: no. * R: ImportManager, Sema, GenericInstantiator, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ Ptr const retTy{nullptr}; /** * Mark whether a function is C function. * W: no. * R: ImportManager, Sema, CHIR, HLIRCodeGen. */ const bool isC{false}; /** * Mark whether a function is a ClosureTy. This will only be used on CHIR node's ty, never appeared on AST node. */ const bool isClosureTy{false}; /** * Mark whether a C function has variable-length argument. * W: Sema. * R: Sema, CHIR, HLIRCodeGen, LLVMCodeGen, Modules. */ const bool hasVariableLenArg{false}; /** * Mark whether this type is only an upper-bound that helps type inference, and no expression's * type will ever be up-cast to it. Which means implicit boxing for variance should be allowed. */ const bool noCast{false}; /** Constructor. * U: Sema, CHIR, Modules. */ struct Config { const bool isC{false}; const bool isClosureTy{false}; const bool hasVariableLenArg{false}; const bool noCast{false}; }; FuncTy(std::vector> paramVector, Ptr rType, const Config cfg = {false, false, false, false}) : Ty(TypeKind::TYPE_FUNC), paramTys(std::move(paramVector)), retTy(rType), isC(cfg.isC), isClosureTy(cfg.isClosureTy), hasVariableLenArg(cfg.hasVariableLenArg), noCast(cfg.noCast) { typeArgs = paramTys; // Currently, only CFunc has variable length parameters. invalid = !Ty::AreTysCorrect(typeArgs) || !Ty::IsTyCorrect(rType) || (!isC && hasVariableLenArg); generic = Ty::ExistGeneric(typeArgs) || (rType && rType->HasGeneric()); typeArgs.emplace_back(retTy); } /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override; /** * Mark whether a function is C function. * W: no. * R: ImportManager, Sema, CHIR, HLIRCodeGen. */ bool IsCFunc() const override { return isC; } size_t Hash() const override; bool operator==(const Ty& other) const override; }; /** * Union type. * Note: not used as normal 'Node' 's type, so does not need to set 'invalid' and 'generic' value. */ struct UnionTy : Ty { /** * Union types member. * W: no. * R: Sema, CHIR. */ std::set> tys; /** Constructor. * U: Sema. */ UnionTy(std::set> tys) : Ty(TypeKind::TYPE_UNION) { this->tys = std::move(tys); } /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override; UnionTy(const UnionTy&) = delete; UnionTy& operator=(const UnionTy&) = delete; size_t Hash() const override; bool operator==(const Ty& other) const override; }; /** * Intersection type. * Note: not used as normal 'Node' 's type, so does not need to set 'invalid' and 'generic' value. */ struct IntersectionTy : Ty { /** * Intersection types member. * W: no. * R: Sema, CHIR. */ std::set> tys; /** Constructor. * U: Sema. */ IntersectionTy(std::set> tys) : Ty(TypeKind::TYPE_INTERSECTION) { this->tys = std::move(tys); } /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override; IntersectionTy(const IntersectionTy&) = delete; IntersectionTy& operator=(const IntersectionTy&) = delete; size_t Hash() const override; bool operator==(const Ty& other) const override; }; struct InterfaceTy; /** * Base type for class and interface. * Note: middle inherited type, does not need to set 'invalid' and 'generic' value. */ struct ClassLikeTy : Ty { /** * Class like decl pointer. * W: ImportManager, Sema, GenericInstantiator. * R: ImportManager, Sema, AST2CHIR, HLIRCodeGen, LLVMCodeGen. */ Ptr commonDecl{nullptr}; /** * Direct subtypes declared by class, interface or extend. * For example, suppose that we have `interface I`, `class A <: I`, `interface J <: I`, and `extend Int64 <: I`, * then the `directSubtypes` of `I` is `{A, J, Int64}`. * This filed is used for the exhaustive checking of sealed classes/interfaces. * W: Sema * R: Sema */ std::unordered_set> directSubtypes; /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override { return Ty::String(); } /** Return all the super interface types. * U: Sema, GenericInstantiator, CHIR. */ virtual std::set> GetSuperInterfaceTys() const = 0; protected: /** Constructor. * U: no. */ ClassLikeTy(TypeKind k, ClassLikeDecl& cld) : Ty(k), commonDecl(&cld) { } }; /** * Interface type. */ struct InterfaceTy : ClassLikeTy { /** * Generic interface decl pointer. This ptr is used to distinguish tys with same name and same typeArguments. It * always points to generic decl in generic ty, which is used to obtain the same InterfaceTy. * W: ImportManager. * R: Sema. */ Ptr const declPtr{nullptr}; /** * Interface decl pointer. * W: ImportManager, Sema, GenericInstantiator. * R: Sema, GenericInstantiator, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen, Utils. */ Ptr decl{nullptr}; /** Constructor. * U: Sema. */ InterfaceTy(const std::string& name, InterfaceDecl& id, const std::vector>& typeArgs); /** Return all the super interface types. * U: Sema, GenericInstantiator, CHIR. */ std::set> GetSuperInterfaceTys() const override; /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override; /** * Generic interface type pointer. * W: ImportManager, Sema. * R: Sema. */ Ptr GetGenericTy() const; size_t Hash() const override; bool operator==(const Ty& other) const override; }; /** * Class type. */ struct ClassTy : ClassLikeTy { /** * Generic class decl pointer. This ptr is used to distinguish tys with same name and same typeArguments. It * always points to generic decl in generic ty, which is used to obtain the same ClassTy. * W: ImportManager. * R: Sema. */ Ptr const declPtr{nullptr}; /** * Class decl pointer. * W: ImportManager, Sema, GenericInstantiator. * R: Sema, GenericInstantiator, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen, Utils. */ Ptr decl{nullptr}; /** Return super class type. * U: Sema, GenericInstantiator, AST2CHIR, CHIR, HLIRCodeGen. */ Ptr GetSuperClassTy() const; /** Constructor. * U: Sema. */ ClassTy(const std::string& name, ClassDecl& cd, const std::vector>& typeArgs); /** Return all the super interface types. * U: Sema, GenericInstantiator, CHIR. */ std::set> GetSuperInterfaceTys() const override; /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override; /** * Generic class type pointer. * W: ImportManager, Sema. * R: Sema. */ Ptr GetGenericTy() const; virtual size_t Hash() const override; virtual bool operator==(const Ty& other) const override; }; struct ClassThisTy : ClassTy { /** Constructor. * U: Sema. */ ClassThisTy(std::string name, ClassDecl& cd, std::vector> typeArgs) : ClassTy(name, cd, typeArgs) { } /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override { return "This"; } size_t Hash() const override; bool operator==(const Ty& other) const override; }; struct TypeAliasTy : Ty { /** * TypeAlias decl pointer. * W: no. * R: ImportManager, Sema. */ Ptr declPtr{nullptr}; /** Constructor. * U: Sema. */ TypeAliasTy(const std::string& name, TypeAliasDecl& tad, const std::vector>& typeArgs) : Ty(TypeKind::TYPE), declPtr(&tad) { this->name = name; this->typeArgs = typeArgs; this->invalid = !Ty::AreTysCorrect(this->typeArgs); this->generic = Ty::ExistGeneric(this->typeArgs); } /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override; TypeAliasTy(const TypeAliasTy&) = delete; TypeAliasTy& operator=(const TypeAliasTy&) = delete; size_t Hash() const override; bool operator==(const Ty& other) const override; }; /** * Generic type. */ struct GenericsTy : public Ty { /** * Generic param decl pointer. * W: no. * R: ImportManager, Sema, GenericInstantiator, HLIRCodeGen, LLVMCodeGen. */ Ptr const decl{nullptr}; /** Constructor. * U: Sema. */ GenericsTy(const std::string& name, GenericParamDecl& gpd); /** * Mark whether decl is erased. * W: Sema, AST2CHIR. * R: Sema. */ const bool isEraseMode; /** * The upperBounds of generic ty. After collecting assumptions, it will contain all direct * upper bounds and non-GenericsTy transitive upper bounds. * W: Sema. * R: Sema, AST2CHIR. */ std::set> upperBounds; /** * A flag to mark whether the upperBound is legal after constraint sanity check. * W: Sema. * R: Sema. */ bool isUpperBoundLegal{true}; /** * The lower bound of generic ty. * W: Sema. * R: Sema. */ Ptr lowerBound{nullptr}; /** * A flag to mark all type checking should be ignored for this ty var. * W: Sema. * R: Sema. */ bool isAliasParam{false}; /** * If false, the type var is introduced by a surrounding definition, and does not need to be solved. * If true, the type var is a placeholder type variable that needs to be solved, created either * by a generic function/type's instantiation, or by a function/lambda's parameter/return type * that is not annotated. * W: Sema. * R: Sema. */ bool isPlaceholder{false}; /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override; size_t Hash() const override; bool operator==(const Ty& other) const override; }; /** * Enum type. */ struct EnumTy : Ty { /** * Generic enum decl pointer. This ptr is used to distinguish tys with same name and same typeArguments. It * always points to generic decl in generic ty, which is used to obtain the same EnumTy. * W: ImportManager. * R: Sema, GenericInstantiator, HLIRCodeGen. */ Ptr const declPtr{nullptr}; /** * Enum decl pointer. * W: ImportManager, Sema, GenericInstantiator. * R: Sema, GenericInstantiator, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ Ptr decl{nullptr}; /** * don't know. * W: no. * R: no. */ std::unordered_map fieldMap; /** Constructor. * U: Sema. */ EnumTy(const std::string& name, EnumDecl& ed, const std::vector>& typeArgs); /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override; /** Return all the super interface types. * U: Sema, GenericInstantiator. */ std::set> GetSuperInterfaceTys() const; /** * Generic enum type pointer. * W: ImportManager, Sema. * R: Sema. */ Ptr GetGenericTy() const; /** * Whether a correspond RefEnumTy exists or not. * W: Sema * R: CodeGen */ bool hasCorrespondRefEnumTy = false; virtual size_t Hash() const override; virtual bool operator==(const Ty& other) const override; bool IsNonExhaustive() const; }; struct RefEnumTy : EnumTy { RefEnumTy(const std::string& name, EnumDecl& ed, const std::vector>& typeArgs) : EnumTy(name, ed, typeArgs) { this->hasCorrespondRefEnumTy = true; } /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override; size_t Hash() const override; bool operator==(const Ty& other) const override; }; /** * Struct type. */ struct StructTy : Ty { /** * Generic 'struct' decl pointer. This ptr is used to distinguish tys with same name and same typeArguments. It * always points to generic decl in generic ty, which is used to obtain the same StructTy. * W: ImportManager. * R: Sema. */ Ptr const declPtr{nullptr}; /** * Struct decl pointer. * W: ImportManager, Sema, GenericInstantiator. * R: Sema, GenericInstantiator, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ Ptr decl{nullptr}; /** Constructor. * U: Sema. */ StructTy(const std::string& name, StructDecl& sd, const std::vector>& typeArgs); /** Return the unique name of a ty. * U: ImportManager, Sema, AST2CHIR, CHIR, HLIRCodeGen, LLVMCodeGen. */ std::string String() const override; /** Return all the super interface types. * U: Sema, GenericInstantiator. */ std::set> GetSuperInterfaceTys() const; /** * Generic struct type pointer. * W: ImportManager, Sema. * R: Sema. */ Ptr GetGenericTy() const; size_t Hash() const override; bool operator==(const Ty& other) const override; }; bool CompTyByNames(Ptr ty1, Ptr ty2); struct CmpTyByName { bool operator()(Ptr ty1, Ptr ty2) const { return CompTyByNames(ty1, ty2); } }; } // namespace Cangjie::AST #endif // CANGJIE_AST_TYPES_H cangjie_compiler-1.0.7/include/cangjie/AST/Utils.h000066400000000000000000000227141510705540100217400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the AST utils interface. */ #ifndef CANGJIE_AST_UTILS_H #define CANGJIE_AST_UTILS_H #include #include "cangjie/Utils/ConstantsUtils.h" #include "cangjie/AST/Node.h" #include "cangjie/Utils/CastingTemplate.h" namespace Cangjie::AST { /** * Add Attribute and curfile in macro expanded node. */ void AddMacroAttr(AST::Node& node); /** * Recursively set 'curFile' to @p file for @p root. */ void AddCurFile(AST::Node& root, Ptr file = nullptr); /** * Whether the macrocall node is actually a pure annotation. */ inline bool IsPureAnnotation(const AST::MacroInvocation& invocation) { return invocation.isCustom && invocation.isCurFile; } std::vector> SortModifierByPos(const std::set& modifiers); /** * Whether the given node is a class or struct constructor. */ inline bool IsInstanceConstructor(const AST::Node& node) { return node.TestAttr(AST::Attribute::CONSTRUCTOR) && !node.TestAttr(AST::Attribute::STATIC); } /** * Whether the given decl is the static initializer. */ inline bool IsStaticInitializer(const AST::Decl& decl) { return decl.TestAttr(AST::Attribute::STATIC, AST::Attribute::CONSTRUCTOR); } /** * Whether the given decl is a class, struct or enum constructor. */ inline bool IsClassOrEnumConstructor(const AST::Decl& decl) { return IsInstanceConstructor(decl) || decl.TestAttr(AST::Attribute::ENUM_CONSTRUCTOR); } inline bool IsGlobalOrMember(const AST::Node& node) { return node.TestAnyAttr(AST::Attribute::GLOBAL, AST::Attribute::IN_CLASSLIKE, AST::Attribute::IN_ENUM, AST::Attribute::IN_STRUCT, AST::Attribute::IN_EXTEND); } inline bool IsInstanceMember(const AST::Decl& decl) { return decl.outerDecl && decl.outerDecl->IsNominalDecl() && !decl.TestAttr(AST::Attribute::STATIC); } inline bool IsGlobalOrStaticVar(const Decl& decl) { return decl.astKind == ASTKind::VAR_DECL && decl.TestAnyAttr(Attribute::STATIC, Attribute::GLOBAL); } inline bool IsInheritableClass(const Decl& decl) { return decl.astKind == ASTKind::CLASS_DECL && decl.TestAnyAttr(Attribute::OPEN, Attribute::ABSTRACT); } bool IsMemberParam(const Decl& decl); bool IsSingleRuneStringLiteral(const Expr& expr); bool IsSingleByteStringLiteral(const Expr& expr); Ptr GetSizeDecl(const AST::Ty& ty); std::optional HasJavaAttr(const AST::Node& node) noexcept; /** * Initialize @p lce 's constValue with its string value. */ void InitializeLitConstValue(AST::LitConstExpr& lce); struct FloatTypeInfo { uint64_t inf; std::string min; std::string max; }; FloatTypeInfo GetFloatTypeInfoByKind(AST::TypeKind kind); void SetOuterFunctionDecl(AST::Decl& decl); bool IsInDeclWithAttribute(const AST::Decl& decl, AST::Attribute attr); /** * Iterate all toplevel decls in given 'pkg', and perform the function 'process' on each of toplevel decl. */ inline void IterateToplevelDecls(const Package& pkg, const std::function&)>& process) { for (auto& file : pkg.files) { (void)std::for_each(file->decls.begin(), file->decls.end(), process); (void)std::for_each(file->exportedInternalDecls.begin(), file->exportedInternalDecls.end(), process); } } /** * Iterate all exportable function from toplevel and member decls. */ void IterateAllExportableDecls(const AST::Package& pkg, const std::function action); std::vector> FlattenVarWithPatternDecl(const AST::VarWithPatternDecl& vwpDecl); std::string GetAnnotatedDeclKindString(const Decl& decl); inline bool InsideAtJavaDecl(const AST::Decl& decl) { if ((decl.astKind == AST::ASTKind::FUNC_DECL || decl.astKind == AST::ASTKind::VAR_DECL) && decl.TestAttr(AST::Attribute::IN_CLASSLIKE)) { return HasJavaAttr(decl).has_value(); } if (decl.astKind == AST::ASTKind::FUNC_PARAM && decl.outerDecl) { return InsideAtJavaDecl(*decl.outerDecl); } return false; } bool IsPackageMemberAccess(const AST::MemberAccess& ma); bool IsThisOrSuper(const AST::Expr& expr); AST::AccessLevel GetAccessLevel(const AST::Node& node); AST::Attribute GetAttrByAccessLevel(AccessLevel level); std::string GetAccessLevelStr(const AST::Node& node, const std::string& surround = ""); std::string GetAccessLevelStr(const AST::Package& pkg); inline bool IsCompatibleAccessLevel(AST::AccessLevel srcLevel, AST::AccessLevel refLevel) { return srcLevel <= refLevel; } std::string GetImportedItemFullName(const AST::ImportContent& content, const std::string& commonPrefix = ""); void ExtractArgumentsOfDeprecatedAnno( const Ptr annotation, std::string& message, std::string& since, bool& strict ); /// Check whether this condition or condition subtree is a condition, i.e. has a let pattern subtree. bool IsCondition(const Expr& e); bool DoesNotHaveEnumSubpattern(const LetPatternDestructor& let); bool IsValidCFuncConstructorCall(const CallExpr& ce); inline bool IsNestedFunc(const AST::FuncDecl& fd) { return !fd.TestAttr(AST::Attribute::GLOBAL) && (!fd.outerDecl || fd.outerDecl->IsFunc()); } inline bool IsDefaultImplementation(const AST::Decl& decl) { return decl.TestAttr(AST::Attribute::DEFAULT) && decl.outerDecl && decl.outerDecl->astKind == AST::ASTKind::INTERFACE_DECL; } // Check if the function can be source-exported without regard to its modifier. inline bool CanBeSrcExported(const AST::FuncDecl& fd) { if (fd.isInline) { return true; } const bool isGenericFunction = (fd.funcBody && fd.funcBody->generic) || fd.TestAttr(Attribute::GENERIC); if ((isGenericFunction || fd.IsExportedDecl() || fd.linkage != Linkage::INTERNAL) && (fd.isConst || fd.isFrozen)) { return true; } auto decl = fd.ownerFunc ? fd.ownerFunc.get() : &fd; return !IsInDeclWithAttribute(*decl, Attribute::GENERIC_INSTANTIATED) && IsDefaultImplementation(*decl); } inline bool IsInstMemberVarInGenericDecl(const AST::VarDecl& vd) { return vd.astKind == ASTKind::VAR_DECL && !vd.TestAttr(AST::Attribute::STATIC) && IsInDeclWithAttribute(vd, AST::Attribute::GENERIC); } inline bool IsCommonWithoutDefault(const Decl& decl) { return decl.TestAttr(AST::Attribute::COMMON) && !decl.TestAttr(AST::Attribute::COMMON_WITH_DEFAULT); } bool IsVirtualMember(const AST::Decl& decl); inline bool IsStaticVar(const AST::Decl& decl) { return decl.astKind == AST::ASTKind::VAR_DECL && decl.TestAttr(AST::Attribute::STATIC); } /** * If the member variable has initializer and there is const init in its parent declaration, * it should be source exported. */ inline bool IsMemberVarShouldBeSrcExported(const AST::VarDecl& vd) { // Non-const static member variables are not exported because the static init function cannot be invoked by users. // Therefore source code export is not required. if (!vd.outerDecl || !vd.initializer || vd.TestAttr(AST::Attribute::STATIC)) { return false; } auto& od = *vd.outerDecl; return (od.astKind == AST::ASTKind::STRUCT_DECL && StaticCast(od).HasConstOrFrozenInit()) || (od.astKind == AST::ASTKind::CLASS_DECL && StaticCast(od).HasConstOrFrozenInit()); } struct VarDeclWithPosition { Ptr decl; std::size_t fieldPosition; }; /** * Constructs list of class/struct instance variables with correct initialization order. * The initialization order of instance variables in same file is preserved. * The initializations of common/specific instance variables may be interleaved * @param parentDecl Declaration of class/struct * @returns List of declarations with their original declaration's index in class/struct */ std::vector GetVarsInitializationOrderWithPositions(const Decl& parentDecl); void InsertPropGetterSignature(PropDecl& prop, Attribute attrToBeSet); void InsertPropSetterSignature(PropDecl& prop, Attribute attrToBeSet); void InsertMirrorVarProp(ClassDecl& decl, Attribute attrToBeSet); } // namespace Cangjie::AST namespace Cangjie::Interop::Java { using namespace Cangjie::AST; bool IsImpl(const Decl& decl); bool IsJObject(const Decl& decl); /** * For stages where packageName is not set yet */ bool IsJObject(const Decl& decl, const std::string& packageName); bool IsMirror(const Decl& decl); bool IsCJMapping(const Decl& decl); /** * public func $getJavaRef(): Java_CFFI_JavaEntity { * return Java_CFFI_JavaEntity() * } */ void InsertJavaRefGetterStubWithBody(ClassDecl& decl); bool IsDeclAppropriateForSyntheticClassGeneration(const Decl& decl); std::string GetSyntheticNameFromClassLike(const ClassLikeDecl& cld); /** * Generates and inserts the synthetic class declaration. * The synthetic class implements the given interface or abstract class and has the following structure: * * Example of generated synthetic * ``` * // CL is interface or abstract class. If CL is interface then JObject will be added as super class * class CL_impl <: CL { * init(ref: Java_CFFI_JavaEntity) { // inherited from JObject * $javaref = ref * } * * public func $getJavaRef() { // inherited from JObject * return $javaref * } * } * ``` */ void InsertSyntheticClassDecl(ClassLikeDecl& decl, File& file); } #endif cangjie_compiler-1.0.7/include/cangjie/AST/Walker.h000066400000000000000000000067261510705540100220720ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Walker related classes. */ #ifndef CANGJIE_AST_WALKER_H #define CANGJIE_AST_WALKER_H #include #include #include #include #include "cangjie/AST/Node.h" namespace Cangjie::AST { /** * Enum class for visit action in the Walker. * * The values have been specially designed to make `STOP_NOW` has the highest priority when several values are combined * with `OR` operator. * * @see operator|(VisitAction, VisitAction) */ enum class VisitAction : uint8_t { WALK_CHILDREN, /**< Continue to walk into child items. */ SKIP_CHILDREN, /**< Continue walking, but don't enter child items. */ STOP_NOW, /**< Stop walking immediately. */ KEEP_DECISION /**< Only clean up states. Keep action as it is. */ }; /** * The main class used for walking the Rune AST. */ template class WalkerT { /** * A typealias for the visit function callback. It accepts a pointer to the Node being visited as its only * parameter and returns the VisitAction after walking into it. */ using VisitFunc = std::function)>; public: /** * The constructor to create an AST walker. * @param node The AST node being visited. * @param VisitPre The function executed before walking into its children. * @param VisitPost The function executed after walking into its children. */ explicit WalkerT(Ptr node, VisitFunc VisitPre = nullptr, VisitFunc VisitPost = nullptr) : node(node), VisitPre(std::move(VisitPre)), VisitPost(std::move(VisitPost)) { ID = GetNextWalkerID(); } /** * The constructor to create an AST walker. * @param node The AST node being visited. * @param id Given walker id for current walker. * @param VisitPre The function executed before walking into its children. * @param VisitPost The function executed after walking into its children. */ WalkerT(Ptr node, unsigned id, VisitFunc VisitPre = nullptr, VisitFunc VisitPost = nullptr) : node(node), VisitPre(std::move(VisitPre)), VisitPost(std::move(VisitPost)), ID(id) { } /** * The function starts an AST walking. */ void Walk() const { Walk(node); }; static unsigned GetNextWalkerID(); private: /** * The AST node as walking entry. */ Ptr node; /** * The function executed before walking into its children. */ VisitFunc VisitPre; /** * The function executed after walking into its children. */ VisitFunc VisitPost; /** * Walker ID. */ unsigned ID{0}; /** * Next Walker ID. */ static std::atomic_uint nextWalkerID; /** * The function used internally for walking a certain AST node. * @param curNode The node to be walked. * @return VisitAction decision after walking into it. */ VisitAction Walk(Ptr curNode) const; template friend class WalkerT; }; using Walker = WalkerT; using ConstWalker = WalkerT; extern template class WalkerT; extern template class WalkerT; } // namespace Cangjie::AST #endif // CANGJIE_AST_WALKER_H cangjie_compiler-1.0.7/include/cangjie/Basic/000077500000000000000000000000001510705540100210535ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/Basic/Color.h000066400000000000000000000045241510705540100223070ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares Color related apis. */ #ifndef CANGJIE_BASIC_COLOR_H #define CANGJIE_BASIC_COLOR_H #include #include #include #include #include namespace Cangjie { struct ColorSingleton { public: static ColorSingleton& getInstance() { static ColorSingleton singleton; return singleton; } std::string ANSI_COLOR_RESET; std::string ANSI_COLOR_BRIGHT; std::string ANSI_COLOR_BLACK; std::string ANSI_COLOR_RED; std::string ANSI_COLOR_GREEN; std::string ANSI_COLOR_YELLOW; std::string ANSI_COLOR_BLUE; std::string ANSI_COLOR_MAGENTA; std::string ANSI_COLOR_CYAN; std::string ANSI_COLOR_WHITE; std::string ANSI_COLOR_WHITE_BACKGROUND_BLACK_FOREGROUND; private: #ifdef _WIN32 unsigned long initialStdoutMode; unsigned long initialStderrMode; #endif ColorSingleton(); ~ColorSingleton(); }; inline const std::string ANSI_NO_COLOR; inline const std::string ANSI_COLOR_RESET = ColorSingleton::getInstance().ANSI_COLOR_RESET; inline const std::string ANSI_COLOR_BRIGHT = ColorSingleton::getInstance().ANSI_COLOR_BRIGHT; inline const std::string ANSI_COLOR_BLACK = ColorSingleton::getInstance().ANSI_COLOR_BLACK; inline const std::string ANSI_COLOR_RED = ColorSingleton::getInstance().ANSI_COLOR_RED; inline const std::string ANSI_COLOR_GREEN = ColorSingleton::getInstance().ANSI_COLOR_GREEN; inline const std::string ANSI_COLOR_YELLOW = ColorSingleton::getInstance().ANSI_COLOR_YELLOW; inline const std::string ANSI_COLOR_BLUE = ColorSingleton::getInstance().ANSI_COLOR_BLUE; inline const std::string ANSI_COLOR_MAGENTA = ColorSingleton::getInstance().ANSI_COLOR_MAGENTA; inline const std::string ANSI_COLOR_CYAN = ColorSingleton::getInstance().ANSI_COLOR_CYAN; inline const std::string ANSI_COLOR_WHITE = ColorSingleton::getInstance().ANSI_COLOR_WHITE; inline const std::string ANSI_COLOR_WHITE_BACKGROUND_BLACK_FOREGROUND = \ ColorSingleton::getInstance().ANSI_COLOR_WHITE_BACKGROUND_BLACK_FOREGROUND; } // namespace Cangjie #endif // CANGJIE_BASIC_COLOR_H cangjie_compiler-1.0.7/include/cangjie/Basic/DiagRefactor/000077500000000000000000000000001510705540100234055ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/Basic/DiagRefactor/DiagnosticAll.def000066400000000000000000000016611510705540100266060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/Basic/DiagRefactor/DiagnosticChir.def" #include "cangjie/Basic/DiagRefactor/DiagnosticConditionalCompilation.def" #include "cangjie/Basic/DiagRefactor/DiagnosticDriver.def" #include "cangjie/Basic/DiagRefactor/DiagnosticFrontend.def" #include "cangjie/Basic/DiagRefactor/DiagnosticIncrementalCompilation.def" #include "cangjie/Basic/DiagRefactor/DiagnosticLexer.def" #include "cangjie/Basic/DiagRefactor/DiagnosticModule.def" #include "cangjie/Basic/DiagRefactor/DiagnosticPackage.def" #include "cangjie/Basic/DiagRefactor/DiagnosticParser.def" #include "cangjie/Basic/DiagRefactor/DiagnosticParserQuery.def" #include "cangjie/Basic/DiagRefactor/DiagnosticSema.def" cangjie_compiler-1.0.7/include/cangjie/Basic/DiagRefactor/DiagnosticChir.def000066400000000000000000000071011510705540100267560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. ERROR(chir_diag_begin, "chir_diag_begin") // variable initialisation check ERROR(chir_used_before_initialization, "variable '%s' is used before initialization") ERROR(chir_illegal_usage_of_member, "'%s' is not allowed to be accessed before all member variables are initialized") ERROR(chir_cannot_assign_initialized_let_variable, "cannot assign to value which is an initialized 'let' constant") ERROR(chir_class_uninitialized_field, "not all the member variables are initialized in this constructor") ERROR(chir_illegal_usage_of_super_member, "super member '%s' is not allowed to be used before calling super()") // constant safety check ERROR(chir_arithmetic_operator_overflow, "arithmetic operation '%s' overflow", "operation '%s' would overflow") ERROR(chir_idx_out_of_bounds, "array index is out of bounds", "%s") ERROR(chir_divisor_is_zero, "%s by zero", "attempt to %s by zero") ERROR(chir_shift_length_overflow, "shift operation overflow", "attempt to shift %s bits on %s type") ERROR(chir_typecast_overflow, "integer type conversion overflow", "type conversion from %s to %s would overflow") ERROR(chir_step_non_zero_range, "step cannot be zero in range expression") // dce check WARNING(chir_dce_unreachable_statement, UNUSED, "unreachable statement", "unreachable statement", {"any code following this expression is unreachable"}) WARNING(chir_dce_unreachable_function, UNUSED, "unreachable function ", "function with parameter of nothing type will never be executed", ) WARNING(chir_dce_unreachable_return, UNUSED, "unreachable return", "unreachable return") WARNING(chir_dce_unreachable_if, UNUSED, "unreachable '%s' expression", "unreachable '%s' expression", {"any code following this expression is unreachable"}) WARNING(chir_dce_unreachable, UNUSED, "unreachable '%s'", "unreachable '%s'", {"any code following this expression is unreachable"}) WARNING(chir_dce_unreachable_expression_hint, UNUSED, "unreachable expression", "unreachable expression", {"any code following this expression is unreachable"}) WARNING(chir_dce_unreachable_block_in_expression, UNUSED, "unreachable block in '%s' expression", "unreachable block in '%s' expression") WARNING(chir_dce_unreachable_block, UNUSED, "unreachable block", "unreachable block") WARNING(chir_dce_unused_expression, UNUSED, "unused expression", "unused expression") WARNING(chir_dce_unused_function, UNUSED, "unused function:'%s'", "unused function") WARNING(chir_dce_unused_function_main, UNUSED, "unused function:'main'", "unused function") // Also included in `UNUSED_MAIN` group WARNING(chir_dce_unreachable_expression, UNUSED, "unreachable expression", "unreachable expression") WARNING(chir_dce_unused_variable, UNUSED, "unused variable:'%s'", "unused variable") WARNING(chir_dce_unused_operator, UNUSED, "unused result of the operator:'%s'", "unused result of the operator") WARNING(chir_unreachable_pattern, UNUSED, "unreachable pattern") // const eval check ERROR(chir_eval_support, "const evaluation has been disabled by compiler option `--no-interp-const-eval`") ERROR(chir_annotation_not_applicable, "'@%s' not applicable to %s") // CJMP ERROR(frontend_can_not_handle_to_many_chir, "Can't handle more than one CHIR file") // Native FFI ERROR(chir_native_ffi_java_illegal_type_cast, "Illegal type cast from Java type '%s' to non Java type '%s'") ERROR(chir_diag_end, "chir_diag_end") cangjie_compiler-1.0.7/include/cangjie/Basic/DiagRefactor/DiagnosticConditionalCompilation.def000066400000000000000000000026261510705540100325420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. ERROR(conditional_compilation_diag_begin, "") ERROR(conditional_compilation_not_support_op, "conditional compilation '%s' not support this op '%s'") ERROR(conditional_compilation_not_support_this_condition, "conditional compilation have not supported this condition: '%s'") ERROR(conditional_compilation_not_support_builtin_value, "builtin condition '%s' do not support '%s', supported listed: '%s'") ERROR(conditional_compilation_not_support_cjc_version_format, "cjc version's format should be 'xx.xx.xx'") ERROR(conditional_compilation_invalid_condition_expr, "conditional compilation not support this expression") ERROR(conditional_compilation_not_have_condition_expr, "conditional compilation should have condition expression") ERROR(conditional_compilation_invalid_condition_value, "conditional compilation's condition value should be string literal without interpolation") ERROR(conditional_compilation_just_support_block, "conditional compilation macro '@If' and '@Else' only support used in block") ERROR(conditional_compilation_unexpected_after_macro, "unexpected conditional compilation directive") ERROR(conditional_compilation_diag_end, "") cangjie_compiler-1.0.7/include/cangjie/Basic/DiagRefactor/DiagnosticDriver.def000066400000000000000000000136601510705540100273330ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. ERROR(driver_diag_begin, "") // Common error ERROR(no_such_file_or_directory, "No such file or directory: '%s'.") ERROR(no_such_directory, "No such directory: '%s'.") ERROR(not_a_directory, "Not a directory: '%s'.") ERROR(not_a_file, "Not a file: '%s'.") ERROR(permission_denied, "Permission denied: '%s'.") ERROR(invalid_path, "Invalid path: '%s'.") ERROR(no_such_file_or_directory_of_macro_obj, "No such file or directory: '%s', please check if input file exists, separate paths by space if there are multiple input files") ERROR(input_file_overwritten_by_generated_output, "The input file '%s' would be overwritten by the generated output.") // Ignore argument warnings WARNING(driver_warning_no_such_file, DRIVER_ARG, "No such file: '%s'") WARNING(driver_warning_no_such_file_or_directory, DRIVER_ARG, "No such file or directory: '%s'.%s") WARNING(driver_warning_no_such_directory, DRIVER_ARG, "No such directory: '%s'.%s") WARNING(driver_warning_not_a_directory, DRIVER_ARG, "Not a directory: '%s'.%s") WARNING(driver_warning_not_a_file, DRIVER_ARG, "Not a file: '%s'.%s") WARNING(driver_warning_permission_denied, DRIVER_ARG, "Permission denied: '%s'.%s") WARNING(driver_warning_invalid_path, DRIVER_ARG, "Invalid path: '%s'.%s") WARNING(driver_warning_not_pdba_file, DRIVER_ARG, "Not a .pdba file: '%s' ('%s').%s") WARNING(driver_warning_not_cjo_file, DRIVER_ARG, "Not a .cjo file: '%s' ('%s').%s") WARNING(driver_warning_not_cj_file, DRIVER_ARG, "Not a .cj file: '%s' ('%s').%s") WARNING(driver_warning_not_chir_file, DRIVER_ARG, "Not a .chir file: '%s' ('%s').%s") WARNING(driver_warning_not_bc_file, DRIVER_ARG, "Not a .bc file: '%s' ('%s').%s") WARNING(driver_warning_not_archive_file, DRIVER_ARG, "Not a .a file: '%s' ('%s').%s") WARNING(driver_warning_not_object_file, DRIVER_ARG, "Not a .o file: '%s' ('%s').%s") WARNING(driver_warning_argument_unused, DRIVER_ARG, "the arg '%s' may be unused") WARNING(driver_warning_path_close_to_length_limit, DRIVER_ARG, "the path length for %s is close to the system limit (%s characters). The compilation has a possibility to fail.") // warning of --static-std is not supported for ohos WARNING(driver_static_std_for_ohos, DRIVER_ARG, "Statically link packages of the std module is not supported for OHOS") // driver & option errors ERROR(driver_require_package_directory, "expected one package path to build") ERROR(driver_require_package_directory_scan_dependency, "expected one package path to scan dependency") ERROR(driver_require_one_package_directory, "expect exact one package path to build") ERROR(driver_require_one_package_directory_scan_dependency, "expect exact one .cjo file to scan dependency") ERROR(driver_source_cjo_empty, "expected one .cjo file to scan dependency") ERROR(driver_not_accept_cjo_inputs_when, "not accept .cjo inputs when %s is specified") ERROR(driver_source_file_empty, "expected at least one source code file when compiling source code") ERROR(driver_invalid_source_file, "source file '%s' doesn't exist") ERROR(driver_invalid_file_or_directory, "invalid file or directory '%s'") ERROR(driver_invalid_binary_file, "invalid binary file '%s'") ERROR(driver_invalid_unsupport_extension, "unsupported output extension '.%s', extension '.%s' is required") ERROR(driver_invalid_require_extension, "output to '%s' is not allowed, extension '.%s' is required") ERROR(driver_path_exceeds_length_limit, "the path length for %s exceeds the system limit (%s characters)") ERROR(driver_unsupported_target_cpu, "cpu type '%s' is not supported for the current target") ERROR(driver_require_chir_directory, "expected one chir path to build") // condition compilation arg error and warnings ERROR(driver_cfg_value_err, "user defined condition should be like: k1=v1,k2=v2 or ") ERROR(driver_cfg_toml_content_err, "user defined condition in cfg.toml should be like: k1 = \"v1\", and each key-value pair occupies a separate line.") ERROR(dirver_cfg_invaild_identifier, "user defined condition variable should be a vaild identifier") ERROR(dirver_cfg_same_with_builtin, "user defined condition's key can not be the same with builtin condition") ERROR(dirver_cfg_key_repeat, "user defined condition's key can not repeat") ERROR(dirver_cfg_not_a_dir, "'%s' is not a vaild directory path, please check it or pass the key-value pair like: k1=v1,k2=v2.") ERROR(driver_cfg_file_read_failed, "read '%s' failed, due to '%s'") WARNING(driver_cfg_path_ignored, DRIVER_ARG, "conditional compilation has been set by key-value mode, cfg.toml will be ignored") ERROR(driver_invalid_compile_as_exe, "--compile-as-exe only takes effect when lto mode is enabled") ERROR(driver_invalid_compile_as_exe_platform, "Windows, Mac, IOS does not support --compile-as-exe") WARNING(driver_useless_option, DEPRECATED, "option '%s' is deprecated and will be removed in the future") // module arg warning WARNING(driver_unsupport_compile_package_with_source_file, UNSUPPORT_COMPILE_SOURCE, "when having --package flag, not support compiling packages with source file '%s'") WARNING(driver_unsupport_compile_source_file_with_path, UNSUPPORT_COMPILE_SOURCE, "when compiling source file, not support compiling it with directory '%s'") // warning of deprecated option which will be removed in future WARNING(driver_deprecated_option, DEPRECATED, "option '%s' is deprecated and will be removed in future%s.") // pgo errors ERROR(driver_pgo_both_gen_and_use, "using both '--pgo-instr-gen' and '--pgo-instr-use' is not allowed.") ERROR(driver_pgo_invalid_profile_extension, "Not a .profdata file: '%s'") // Plugin ERROR(not_a_valid_plugin, "'%s' is not a valid compiler plugin to be loaded or executed") ERROR(plugin_throws_exception, "exception occurs while executing compiler plugin(s)") ERROR(driver_diag_end, "") cangjie_compiler-1.0.7/include/cangjie/Basic/DiagRefactor/DiagnosticFrontend.def000066400000000000000000000011101510705540100276420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. ERROR(frontend_diag_begin, "") ERROR(frontend_failed_to_detect_cangjie_home, "failed to detect cangjie home, reason: %s") ERROR(frontend_failed_to_detect_cangjie_modules, "failed to detect cangjie modules, reason: %s") ERROR(frontend_invalid_output_path, "can not generate file to path: '%s'") ERROR(frontend_diag_end, "") cangjie_compiler-1.0.7/include/cangjie/Basic/DiagRefactor/DiagnosticIncrementalCompilation.def000066400000000000000000000006361510705540100325370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. ERROR(incremental_compilation_diag_begin, "") ERROR(cache_search_error, "compilation cache '%s' is missing") ERROR(incremental_compilation_diag_end, "") cangjie_compiler-1.0.7/include/cangjie/Basic/DiagRefactor/DiagnosticLexer.def000066400000000000000000000120621510705540100271520ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. ERROR(lex_diag_begin, "lex_diag_begin") ERROR(lex_unknown_start_of_token, "unknown start of token: %s", "unknown start of token: %s") ERROR(lex_unexpected_digit, "unexpected digit '%s' in %s", "unexpected digit", { "because %s" }) ERROR(lex_cannot_start_with_digit, "cannot start a(n) %s literal with a '%s' digit", "unexpected digit") ERROR(lex_expected_digit, "expected %s digit, found '%s'", "expected %s digit") ERROR(lex_unexpected_decimal_point, "unexpected decimal point '.' in %s base number", "unexpected decimal point", { "because of this %s prefix" }) ERROR(lex_unexpected_exponent_part, "unexpected exponent part '%s__' in %s", "unexpected exponent part", { "because %s" }) ERROR(lex_expected_exponent_part, "expected exponent part in hexadecimal float number '%s'", "expected exponent part", { "because it is hexadecimal float number" }) ERROR(lex_expected_identifier_after_dollar, "expected identifier after '$', found %s", "expected identifier after '$'") ERROR(lex_unrecognized_symbol, "unrecognized symbol '%s'", "unrecognized symbol") ERROR(lex_expected_identifier, "expected identifier, found '%s'", "expected identifier") ERROR(lex_expected_back_quote, "expected '`', found '%s'", "expected '`'", { "to match this opening backquote" }) ERROR(lex_unterminated_single_line_string, "unterminated single-line string", "unterminated single-line string", { "because this interpolation is not terminated with a '}'", }) ERROR(lex_unterminated_multi_line_string, "unterminated multi-line string", "unterminated multi-line string", { "because this interpolation is not terminated with a '}'", }) ERROR(lex_unterminated_interpolation, "unterminated string interpolation", "unterminated string interpolation", { "because it is in single-line string" }) ERROR(lex_multiline_string_start_from_newline, "multi-line string must start with newline character", "expected to start with newline character", { "because it is in multi-line string" }) ERROR(lex_unterminated_raw_string, "unterminated raw string", "unterminated raw string", { "because it interpolation is not terminated with a '}'", }) ERROR(lex_expected_quote_in_raw_string, "expected '#' or '\"' in raw string, found '%s'", "expected '#' or '\"'", { "because it is raw string prefix" }) ERROR(lex_unrecognized_escape, "unrecognized escape '%s' in %s literal", "unrecognized escape", { "because it is in %s literal" }) ERROR(lex_unterminated_block_comment, "unterminated block comment", "unterminated block comment") ERROR(lex_expected_left_bracket, "expected '{' in unicode escape, found '%s'", "expected '{'", { "because it is in this Unicode escape" }) ERROR(lex_expected_right_bracket, "expected '}', found '%s'", "expected '}'", { "to match this opening '{'", }) ERROR(lex_expected_right_bracket_or_hexadecimal, "expected '}' or hexadecimal digit in unicode escape, found '%s'", "expected '}' or hexadecimal digit", { "because it is in this Unicode escape"}) ERROR(lex_expected_character, "expected a character in rune literal, found '%s'", "expected a character", { "because it is in rune literal" }) ERROR(lex_expected_letter_after_underscore, "this cannot be an identifier", "expected a Unicode XID_Continue after underscore") ERROR(lex_illegal_integer_suffix, "illegal integer suffix '%s'", "expected valid integer type suffix", { "invalid type suffix" }) ERROR(lex_illegal_float_suffix, "illegal float number suffix '%s'", "expected valid float number type suffix", { "invalid type suffix" }) ERROR(lex_illegal_non_decimal_float, "float suffix can only be in decimal", "expected valid digits", { "invalid type suffix" }) ERROR(lex_expected_character_in_char_literal, "expected one character in rune literal", "expected one character in rune literal") ERROR(lex_unterminated_char_literal, "unterminated rune literal", "unterminated rune literal") ERROR(lex_characters_overflow, "rune literal may only contain one character", "may only contain one character") ERROR(lex_unknown_suffix, "unknown suffix '%s' for number literal", "unknown suffix '%s'") ERROR(lex_illegal_UTF8_encoding_byte, "illegal byte '%s' in UTF-8 encoding", "illegal byte in UTF-8 encoding") ERROR(lex_illegal_unicode, "illegal character:%s", "illegal character:%s") WARNING(lex_unsecure_unicode, PARSER, "unsecure character:%s", "unsecure character:%s") ERROR(lex_illegal_uni_character_literal, "illegal unicode scalar value '\\u{%s}'", "unicode scalar value must be in range '\\u{0000}' to '\\u{D7FF}' or '\\u{E000}' to '\\u{10FFFF}'") ERROR(lex_too_many_digits, "%s contains too many digits", "too many digits for \\u", { "at most 2 digits in an escaped byte character" }) ERROR(lex_unrecognized_char_in_binary_string, "unrecognized character '%s' in %s", "unrecognized character here", { "in %s" }) ERROR(lex_diag_end, "lex_diag_end")cangjie_compiler-1.0.7/include/cangjie/Basic/DiagRefactor/DiagnosticModule.def000066400000000000000000000026131510705540100273210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. ERROR(module_diag_begin, "") // file system error ERROR(module_read_file_to_buffer_failed, "read file '%s' failed, reason: '%s'") ERROR(module_common_part_path_is_required, "specify common part path of when compiling platform part of package.") ERROR(module_read_file_conflicted, "the file '%s' is a duplicate source file") WARNING(module_version_not_identical, PACKAGE_IMPORT, "ast file for '%s' created by cjc which version is '%s' and current cjc version is '%s', it may cause crash because lack information of declaration") ERROR(module_open_bcFile_failed, "can't open output file '%s': '%s'") ERROR(module_loaded_ast_failed, "validation of %s file '%s' failed, please confirm it was created by compiler whose version is '%s'.") ERROR(module_same_name_with_indirect_dependent_pkg, "failed to load dependent package '%s' for package '%s', which have same package name with source package") // Used by lsp ERROR(module_unsupport_circular_dependencies, "packages %s are in circular dependencies.") ERROR(module_common_cjo_wrong_package, "common part is for another package '%s', expected the same as for current package '%s'.") ERROR(module_diag_end, "") cangjie_compiler-1.0.7/include/cangjie/Basic/DiagRefactor/DiagnosticPackage.def000066400000000000000000000051721510705540100274320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. ERROR(import_package_diag_begin, "") ERROR(package_unsupport_save, "unsupported %s '%s' when saving AST") ERROR(package_unsupported_load, "unsupported %s when loading AST") ERROR(package_invalid_cjo_dependency, "version of package '%s' and its dependent package '%s' are incompatible") ERROR(package_decl_not_find_in_package, "'%s' is not accessible in package '%s'") ERROR(package_search_error, "can not find package '%s'") ERROR(package_import_itself_illegal, "package '%s' should not import itself") ERROR(package_missed_cjo_main_pkg_part_for_test_pkg, "package '%s' is being compiled with --test-only option, but no dependency for production part of this package provided") ERROR(package_multiple_package_declarations, "found more than one package declaration for the package") ERROR(package_name_not_identical_lsp, "package name supposed to be '%s'") ERROR(package_name_inconsistent_with_macro, "package name with macro should be consistent in the same package.") WARNING(package_shadowed_import, PACKAGE_IMPORT, "imported decl '%s' is shadowed, it will be ignored by compiler") WARNING(package_conflict_import, PACKAGE_IMPORT, "imported decl '%s' is conflicted with other import") ERROR(package_cannot_export_macro_package, "it is not allowed to re-export a macro package in a package.") ERROR(package_import_inconsistent, "The imported cjo file failed verification, the package name is %s which is not '%s'") ERROR(package_mocking_support_inconsistency, "dependent package '%s' is compiled with mocking support, the current package must be compiled with mocking support too (pass '%s' compilation option)") ERROR(package_accessibility, "package '%s' is '%s' which cannot be imported by %s package '%s'") ERROR(package_re_export_package_name, "imported package name '%s' cannot be modified by '%s'") ERROR(package_root_package_should_be_public, "root package can only modified by 'public'") ERROR(packages_visibility_inconsistent, "package must have one visibility level (%s != %s)") ERROR(packages_macro_inconsistent, "package must be either macro or not") WARNING(feature_already_seen_name, PACKAGE_IMPORT, "feature is already declared") ERROR(feature_null_declaration, "features declaration must be included in every file within the source set where it is used") ERROR(feature_different_consistency, "feature names must be consistent across all files within a source set") ERROR(import_package_diag_end, "") cangjie_compiler-1.0.7/include/cangjie/Basic/DiagRefactor/DiagnosticParser.def000066400000000000000000000630111510705540100273270ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. ERROR(parse_diag_begin, "") // This is universal diag. ERROR(parse_expected_name, "expected %s %s, found %s", "expected %s here", { "%s" }) ERROR(parse_unexpected_newline_between_at_and_mc, "unexpected '' between '@' and the macro invocation '%s'", "unexpected '' here") ERROR(parse_expect_escape_dollar_token, "expected identifier or '(' after '$'") ERROR(parse_varray_type_parameter, "expected type parameters after 'VArray' keyword") ERROR(parse_varray_type_args_mismatch, "expected %s between '<' and '>' of 'VArray' type") ERROR(parse_expect_integer_literal_varray, "expected an integer literal than or equal to 0 after '$' to specificate the size of 'VArray' type") ERROR(parse_varray_with_paren, "expected '(' or '{' after 'VArray' for 'VArray' constructor") ERROR(parse_expected_import, "expected 'import' after module name, found %s", "expected 'import' here", { "after module name" }) ERROR(parse_expected_module_name, "expected module name after keyword 'from', found %s", "expected module name here", { "after keyword 'from'" }) ERROR(parse_expected_right_delimiter, "unclosed delimiter: '%s'", "expected '%s' here", { "to match this opening '%s'", }) ERROR(parse_invalid_return_type, "there should be no return type in a %s") ERROR(parse_unmatched_right_delimiter, "unmatched delimiter: '%s'", "unmatched delimiter") ERROR(parse_expected_literal, "expected literal after '-', found %s", "expected literal here", { "after this" }) ERROR(parse_expected_pattern, "expected pattern, found %s", "expected pattern here") ERROR(parse_expected_backarrow_in_let_cond, "expected '<-' in %s expression, found %s", "expected '<-' here") ERROR(parse_expected_left_paren_after, "expected '(' after '%s', found %s", "expected '(' here", { "after this" }) ERROR(parse_expected_left_angle_after, "expected '<' after '%s', found %s", "expected '<' here", { "after this" }) ERROR(parse_expected_expr_or_decl_in, "expected expression or declaration, found %s", "expected expression or declaration here") ERROR(parse_expected_catch_or_finally_in_try, "expected 'catch' or 'finally' after try block, found %s", "expected 'catch' or 'finally' here", {"after try block"}) ERROR(parse_expected_catch_or_handle_or_finally_in_try, "expected 'catch', 'handle' or 'finally' after try block, found %s", "expected 'catch', 'handle' or 'finally' here", {"after try block"}) ERROR(parse_expected_colon_in_catch_pattern, "expected ':' in exception type pattern, found %s", "expected ':' here") ERROR(parse_expected_colon_in_effect_pattern, "expected ':' in effect type pattern, found %s", "expected ':' here") ERROR(parse_expected_wildcard_or_exception_pattern, "expected wildcard or exception type pattern, found %s", "expected wildcard or exception type pattern here") ERROR(parse_expected_wildcard_or_effect_pattern, "expected wildcard or effect type pattern, found %s", "expected wildcard or effect type pattern here") ERROR(parse_expected_double_arrow_in_case, "expected '=>' in case, found %s", "expected '=>' here") ERROR(parse_selector_or_match_expression_body, "expected '(' or '{' after 'match', found %s", "expected '(' or '{' here") ERROR(parse_expected_left_brace, "expected '{', found %s", "expected '{' here") ERROR(parse_expected_left_paren, "expected '(', found %s", "expected '(' here") ERROR(parse_expected_case, "expected 'case' in match, found %s", "expected 'case' here") ERROR(parse_unexpected_line_break, "expected '(' after 'quote', found line break", "unexpected line break here") ERROR(parse_expected_paren_or_brace_after_try, "expected '(' or '{' after 'try', found %s", "expected '(' or '{' here", {"after this"}) ERROR(parse_expected_assignment, "expected '=', found %s", "expected '=' here") ERROR(parse_expected_in_forin_expression, "expected 'in' in for-in expression, found %s", "expected 'in' here") ERROR(parse_expected_while_in_do_while, "expected 'while' in do-while expression, found %s", "expected 'while' here") ERROR(parse_expected_double_arrow_in_lambda, "expected '=>' in lambda expression, found %s", "expected '=>' here") ERROR(parse_expected_ccd_in_lambda, "expected one of ',', ':' or '=>', found %s", "expected one of ',', ':' or '=>' here") ERROR(parse_expected_character, "expected %s, found %s", "expected %s here") ERROR(parse_expected_character_after, "expected %s after '%s'") ERROR(parse_importing_by_package_name_is_not_supported, "expected '.'", "expected '.' here") ERROR(parse_expected_decl, "expected declaration, found %s", "expected declaration here") ERROR(parse_expected_one_of_identifier_or_pattern, "expected identifier or pattern after '%s', found %s", "expected identifier or pattern here") ERROR(parse_expected_get_or_set_in_prop, "expected 'get' or 'set' in prop body, found %s", "expected 'get' or 'set' here") ERROR(parse_expected_where_brace, "expected '{' or 'where', found %s", "expected '{' or 'where' here") ERROR(parse_expected_lt_brace, "expected '{' or '<', found %s", "expected '{' or '<' here") ERROR(parse_expected_lt_paren, "expected '(' or '<', found %s", "expected '(' or '<' here") ERROR(parse_expected_identifier_lp, "expected ')' or identifier, found %s", "expected ')' or identifier here") ERROR(parse_expected_dot_lparen, "expected ',' or ')', found %s", "expected ',' or ')' here") ERROR(parse_expected_arrow_in_func_type, "expected '->' in function type") ERROR(parse_unexpected_colon_in_range, "unexpected ':' in index access", "unexpected ':' here") ERROR(parse_expected_lsquare_after, "expected '[' after '%s', found %s", "expected '[' here") ERROR(parse_expected_type_argument, "expected type argument") ERROR(parse_expected_parameter_rp, "expected one parameter name or ')', found %s", "expected one parameter name or ')' here") ERROR(parse_expected_no_newline_after, "expected no new-line character after %s", "expected no new-line character here", {"after %s"}) ERROR(parse_expected_if_let_andand, "expected '&&', '||', or ')', got 'where'", "did you mean to write '&&'") ERROR(parse_duplicated_item, "duplicated %s '%s'%s", "duplicated %s", {"previous one is here"}) WARNING(parse_nl_warning, PARSER, "possibly confusing line terminator","", {"possibly confusing line terminator between '%s' and '%s'"}) // Declaration related. ERROR(parse_illegal_function_name, "'main' declaration doesn't need 'func' keyword", "", {"help: try to remove 'func' keyword"}) ERROR(parse_expected_macro_decl_define_in_macro_package, "macro declaration must be defined in macro package", "expected to be defined in macro package") ERROR(parse_expected_public_before_macro_decl, "macro declaration must be modified with 'public'", "expected 'public' before here") ERROR(parse_macro_unexpected_empty_parameter, "unexpected empty parameters in macro declaration", "expected paratmeters here") ERROR(parse_macro_expected_right_parameter_nums, "too many parameters in macro declaration", "too many parameters here") ERROR(parse_macro_illegal_param_type, "macro declaration's parameter type must be 'Tokens'", "expected 'Tokens' here") ERROR(parse_macro_illegal_ret_type, "macro declaration's return type must be 'Tokens'", "expected 'Tokens' here, got '%s'") ERROR(parse_macro_illegal_named_param, "cannot use named parameter in macro declaration", "unexpected '!' here", { "expected '%s : Tokens' here" }) ERROR(parse_macro_define_conflicted_with_builtin, "macro declaration name '%s' is conflicted with builtin %s identifier", "unexpected macro identifier here") ERROR(parse_macro_call_illegal_with_builtin, "unexpected '[' for builtin macro '%s'", "expected '(' here") ERROR(parse_unexpected_declaration_in_scope, "unexpected %s in %s", "unexpected %s", { "in %s" }) ERROR(parse_const_expected_initializer, "const variable declaration must be initialized", "expected a initializer here") ERROR(parse_unexpected_const_modifier_on_variable, "unexpected modifier 'const' on var or let variable", "unexpected modifier") ERROR(parse_var_must_be_initialized, "variable in top-level scope must be initialized ", "expected '=' here") ERROR(parse_expected_one_of_type_or_initializer, "variable declaration '%s' needs either type or initializer", "expected ':' or '=' after variable name") ERROR(parse_expected_type_or_init_in_pattern, "variable declaration in pattern needs either type or initializer", "expected ':' or '=' after pattern") ERROR(parse_named_parameter_after_unnamed, "unnamed parameters must come before named parameters", "unexpected unnamed parameter here", { "because it must come before this named parameter" }) ERROR(parse_member_parameter_after_regular, "regular parameters must come before member variable parameters", "unexpected parameter here", { "because it must come before this member variable parameter" }) ERROR(parse_decl_cannot_inherit_their_self, "declaration '%s' cannot inherit itself", "illegal super declaration here", { "because '%s' cannot inherit itself" }) ERROR(parse_intrinsic_function_must_be_toplevel, "intrinsic function must be toplevel scope", "intrinsic function must be toplevel scope") ERROR(parse_intrinsic_function_cannot_have_body, "intrinsic function cannot have body", "intrinsic function cannot have body") ERROR(parse_abstract_func_must_have_return_type, "abstract function must have return type", "abstract function must have return type") ERROR(parse_duplicated_get_or_set, "duplicated '%s' in prop", "duplicated '%s'", {"previous one"}) ERROR(parse_unknown_enum_constructor, "unknown enum constructor", "unknown enum constructor") ERROR(parse_getter_setter_cannot_be_generic, "'%s' cannot be generic", "unexpected generic here", {"in '%s'"}) ERROR(parse_unexpected_where, "unexpected 'where' in non-generic declaration", "unexpected 'where' here", {"because this declaration is non-generic"}) ERROR(parse_setter_must_contain_one_parameter, "setter must contain 1 parameter", "expected 1 parameter inside") ERROR(parse_setter_can_only_accept_one_parameter, "setter can only accept 1 parameter", "can only accept 1 parameter") ERROR(parse_duplicated_intrinsic_function, "duplicated intrinsic function '%s'", "duplicated intrinsic function", { "the previous one is here" }) ERROR(parse_missing_body, "body of %s is missing", "missing %s body here") ERROR(parse_invalid_super_declaration, "cannot inherit from type: '%s'", "this type cannot be inherited") ERROR(parse_static_init_can_not_accept_any_parameter, "static initializer cannot have any parameter") ERROR(parse_finalizer_can_not_accept_any_parameter, "finalizer cannot have any parameter") // Expression related. ERROR(parse_invalid_quote_dollar_expr, "invalid expression after the operator '$'") ERROR(parse_unexpected_lambda_expr_in_toplevel, "unexpected lambda expression in top-level scope", "unexpected lambda expression in top-level scope") ERROR(parse_trailing_closure_only_follow_name, "trailing closure can only be used on function calls with function or variable names") ERROR(parse_invalid_left_hand_expr, "invalid left-hand expression of assignment '%s'", "", { "cannot assign to this expression" }) ERROR(parse_chained_none_associative, "%s operators cannot be chained", "") ERROR(parse_duplicated_step_op, "duplicated step operator ':' on range expression", "redundant operator", { "previous one", "on range expression" }) ERROR(parse_invalid_step_op, "invalid step operator ':' on %s expression", "invalid operator", { "on %s expression" }) ERROR(parse_expected_expression, "expected expression after %s, found %s", "expected expression here") ERROR(parse_invalid_incre_expr, "cannot %s a un-assignable expression", "", { "cannot assign to this expression", }) ERROR(parse_unrecognized_token_after_macro_node, "unrecognized operator %s after declaration", "unrecognized operator", { "after declaration" }) ERROR(parse_expected_operator_or_end, "expected operator or end of expression, found %s", "expected operator or end of expression") ERROR(parse_cannot_have_assi_in_init, "cannot have assignment expression in initializer", "cannot have assignment expression here", {}) ERROR(parse_case_body_cannot_be_empty, "match case cannot be empty", "match case cannot be empty") ERROR(parse_redefined_resource_name, "resource name '%s' is already defined", "redefinition of resource name", { "previous one" }) ERROR(parse_newline_not_allowed_between_spawn_and_argument, "unexpected newlines between 'spawn' and the argument followed it") ERROR(parse_expected_no_arguments_in_spawn, "expected no %s in lambda expression of spawn", "cannot contain %s", { "in spawn" }) ERROR(parse_invalid_overloaded_operator, "cannot overload operator %s ", "cannot overload this operator") ERROR(parse_empty_string_interpolation, "string interpolation cannot be empty", "empty string interpolation") ERROR(parse_invalid_unicode_scalar, "code point '%s' is too large", "unrecognized code point") ERROR(parse_wildcard_can_not_be_used_as_member_name, "wildcard cannot be used as member name") ERROR(parse_unexpected_expected_found, "unexpected %s", "expected %s, found %s") ERROR(parse_cannot_operator_a_tuple, "cannot '%s' a tuple") // Type related. ERROR(parse_expected_parentheses, "type before arrow of function type should be surrounded by parentheses") ERROR(parse_this_type_not_allow, "'This' type is not allowed") ERROR(parse_unexpected_tuple_decl_type, "Legacy tuple type syntax no longer allowed after version 0.28.4", "use ',' instead") ERROR(parse_expected_type, "expected type name after %s, found %s", "expected type name here", { "after %s" }) ERROR(parse_newline_not_allowed_between_quest_and_type, "unexpected newlines between '?' and the type followed it") ERROR(parse_redundant_arrow_after_func_type, "redundant '->' after function type") ERROR(parse_all_parameters_must_be_named, "in a parameter type list, either all parameters must be named, or none of them; mixed is not allowed") ERROR(parse_only_tuple_and_func_type_allow_type_parameter_name, "unexpected %s") // Pattern related. ERROR(parse_illegal_declaration_pattern, "%s patterns cannot be used in class or struct body", "", {"in %s body"}) ERROR(parse_illegal_or_pattern, "'|' is not allowed here", "expected ',' or ')', found '|'") ERROR(parse_tuple_pattern_expected_more_field, "1-element tuple pattern is not allowed", "1-element tuple pattern is not allowed") ERROR(parse_type_pattern_in_let_cond, "type pattern is not allowed in %s expression", "type pattern is not allowed here") // Import package related. ERROR(parse_expected_macro_decl_in_macro_package, "cannot use 'public' on %s declarations in a macro package", "", {"macro package defined here"}) ERROR(parse_package_as_all, "The alias name should contain '.*' suffix after import-all", "expected '%s*'", { "after import-all" }) ERROR(parse_package_name_length_overflow, "length of package name '%s' overflow", "length overflow", {}) ERROR(parse_package_name_has_backtick, "cannot using '`' in package name") // Macro related. ERROR(parse_illegal_macro_expand_input_args, "unexpected '[' after '\\' for macro argument") ERROR(parse_illegal_macro_expand_attr_args, "unexpected '(' after '\\' for macro attribute argument") ERROR(parse_illegal_macro_expand_input_args_without_paren, "unexpected parameters for macro invocation here", "expected declaration like: function, enum, class, interface, variable, property, extend ...") ERROR(parse_illegal_macro_expand_input_without_paren_in_paramlist, "unexpected parameters for macro invocation", "expected '(' here") ERROR(parse_ifavailable_arg_no_name, "@IfAvailable expect an argument name") ERROR(parse_ifavailable_not_lambda, "@IfAvailable expect a literal lambda here") // Annotation related. ERROR(parse_unexpected_anno_on, "unexpected annotation '%s' on %s", "unexpected annotation here", { "on %s" }) ERROR(parse_unexpected_overflow_annotation, "unexpected overflow annotation before '%s'", "unexpected overflow annotation", {"before this"}) ERROR(parse_unrecognized_expression_in_when, "unrecognized expression '%s' in annotation '@When'", "unrecognized expression here") ERROR(parse_unrecognized_attr_in_anno, "unrecognized attribute '%s' in annotation '@%s'", "unexpected attribute here") WARNING(parse_empty_attribute, PARSER, "empty attribute of annotation '@%s'", "empty attribute here") WARNING(parse_duplicated_attr_value, PARSER, "duplicated attribute value: '%s'", "duplicated value here", { "the previous one is here" }) WARNING(parse_unsafe_will_be_ignored, PARSER, "'unsafe' modifier will be ignored in backend '%s'", "will be ignored in backend '%s'") ERROR(parse_duplicated_annotation, "duplicated annotation: '%s'", "duplicated annotation here", { "the previous one is here" }) ERROR(parse_conflict_annotation, "'%s' and '%s' annotations conflict on %s", "unexpected annotation", { "because it is conflicted with this" }) // common/platform ERROR(parse_common_and_platform_in_the_same_file, "'common' and 'platform' declarations can not be in the same file") ERROR(parse_common_function_must_have_return_type, "'common' function return type must be specified") ERROR(parse_platform_function_must_have_return_type, "'platform' function return type must be specified") ERROR(parse_platform_function_parameter_cannot_have_default_value, "'platform' %s parameter can not have default value") ERROR(parse_platform_member_must_have_implementation, "the member %s must have body in 'platform' %s") ERROR(parse_expected_type_with_cjmp_var, "'%s' %s type must be specified") ERROR(parse_cjmp_outdecl_miss_match, "%s is %s, but %s is not %s") ERROR(parse_cjmp_static_init, "static init can not be '%s'") ERROR(parse_common_in_non_common_file, "common declaration must be defined in common package part") ERROR(parse_platform_in_non_platform_file, "platform declaration must be defined in platform package part") ERROR(parse_cjmp_generic_decl, "generic declaration can not be '%s'") ERROR(parse_cjmp_pattern_decl, "%s pattern can not be '%s'") ERROR(parse_explicitly_abstract_only_for_cjmp_abstract_class, "only common/platform or Native FFI mirror abstract classes can have explicitly abstract %s") // Modifier related. ERROR(parse_illegal_modifier_in_scope, "unexpected modifier '%s' on %s%s", "unexpected modifier", { "on %s", "in %s" }) ERROR(parse_conflict_modifier, "'%s' and '%s' modifiers conflict on %s", "unexpected modifier", { "because it is conflicted with this", "on %s" }) ERROR(parse_expected_no_modifier, "expected no modifier before %s, found '%s'", "expected no modifier here", { "before %s" }) ERROR(parse_duplicate_modifier, "duplicated modifier: '%s'", "duplicated modifier", {"previous one is here"}) ERROR(parse_duplicate_type_parameter_name, "duplicated type parameter name: '%s'", "duplicated type parameter name", {"previous one is here"}) ERROR(parse_unexpected_type_in, "unexpected type in '%s'", "unexpected type here", { "in %s" }) WARNING(parse_redundant_modifier, PARSER, "redundant modifier: '%s'", "%s implies '%s'") ERROR(parse_variable_length_parameter_can_not_be_first, "variable length parameter can not be the first parameter") ERROR(parse_variable_length_parameter_must_in_the_end, "variable length parameter must in the end of the parameter list") ERROR(parse_variable_length_parameter_only_in_the_foreign_function, "variable length parameter can only show in the foreign function") ERROR(parse_foreign_func_should_not_be_generic, "foreign function should not be generic function") ERROR(parse_foreign_func_must_declare_return_type, "foreign function must declare its return type") ERROR(parse_foreign_function_with_body, "foreign function can not have body") ERROR(parse_expected_static_for_const_member_var, "expected static before const member variable", "const member variable must be modified by static") // @Deprecated annotation ERROR(parse_deprecated_wrong_argument, "argument '%s' of @Deprecated should be %s") ERROR(parse_deprecated_argument_duplication, "argument '%s' of @Deprecated can not be duplicated") ERROR(parse_deprecated_arguments_must_be_lit_const_expr, "argument of @Deprecated is not string literal or boolean value. Variables not allowed") ERROR(parse_deprecated_empty_string_argument, "argument '%s' of @Deprecated must not be empty string") ERROR(parse_deprecated_unknown_argument, "unknown argument '%s' in @Deprecated") ERROR(parse_deprecated_invalid_target, "%s can not be target of @Deprecated") ERROR(parse_annotation_max_one_argument, "%s requires zero or one%s argument", "expected %s here") ERROR(parse_annotation_one_argument, "%s requires exactly one%s argument", "expected %s here") // interop ERROR(parse_foreign_name_on_ffi_decl_member, "@ForeignName could only be used on FFI declaration member") // java mirror ERROR(parse_java_mirror_function_cannot_have_body, "java-mirrored function '%s' cannot have body", "java-mirrored function cannot have body") ERROR(parse_java_mirror_function_must_have_return_type, "java-mirrored function must have return type", "java-mirrored function must have return type") ERROR(parse_java_mirror_prop_cannot_have_setter, "java-mirrored property cannot have setter", "java-mirrored property cannot have setter") ERROR(parse_java_mirror_prop_cannot_have_getter, "java-mirrored property cannot have getter", "java-mirrored property cannot have getter") WARNING(parse_java_mirror_prop_is_deprecated, PARSER, "java-mirrored property is deprecated, use field instead") ERROR(parse_java_mirror_decl_cannot_have_primary_ctor, "java-mirrored declaration cannot have primary constructor", "java-mirrored declaration cannot have primary constructor") ERROR(parse_java_mirror_constructor_cannot_have_body, "java-mirrored constructor cannot have body", "java-mirrored constructor cannot have body") ERROR(parse_java_mirror_cannot_have_private_member, "java-mirrored declaration cannot have private member") ERROR(parse_java_mirror_cannot_have_static_init, "java-mirrored declaration cannot have static initializer") ERROR(parse_java_mirror_cannot_have_finalizer, "java-mirrored declaration cannot have finalizer") ERROR(parse_java_mirror_cannot_have_const_member, "java-mirrored declaration cannot have const member") ERROR(parse_java_mirror_cannot_be_sealed, "@JavaMirror declaration cannot be sealed") ERROR(parse_java_impl_cannot_be_generic, "@JavaImpl declaration cannot be generic") ERROR(parse_java_impl_cannot_be_abstract, "@JavaImpl declaration cannot be abstract") ERROR(parse_java_impl_cannot_be_sealed, "@JavaImpl declaration cannot be sealed") ERROR(parse_java_impl_cannot_have_static_init, "@JavaImpl declaration cannot have static initializer") ERROR(parse_java_mirror_cannot_have_open_prop, "java-mirrored declaration cannot have open property") // java impl ERROR(parse_java_impl_cannot_be_open, "@JavaImpl class cannot be open") ERROR(parse_java_impl_cannot_be_interface, "interface cannot be @JavaImpl") // Objective-C mirror ERROR(parse_objc_mirror_cannot_have_primary_ctor, "@ObjCMirror declaration cannot have primary constructor", "@ObjCMirror declaration cannot have primary constructor") ERROR(parse_objc_mirror_ctor_cannot_have_body, "@ObjCMirror declaration constructor cannot have body", "@ObjCMirror declaration constructor cannot have body") ERROR(parse_objc_mirror_method_cannot_have_body, "@ObjCMirror declaration method '%s' cannot have body", "@ObjCMirror declaration method cannot have body") ERROR(parse_objc_mirror_method_must_have_return_type, "@ObjCMirror declaration method must have return type", "@ObjCMirror declaration method must have return type") ERROR(parse_objc_mirror_cannot_be_sealed, "@ObjCMirror declaration cannot be sealed") ERROR(parse_objc_mirror_cannot_have_private_member, "@ObjCMirror declaration cannot have private member") ERROR(parse_objc_mirror_cannot_have_static_init, "@ObjCMirror declaration cannot have static initializer") ERROR(parse_objc_mirror_cannot_have_finalizer, "@ObjCMirror declaration cannot have finalizer") ERROR(parse_objc_mirror_cannot_have_const_member, "@ObjCMirror declaration cannot have const member") ERROR(parse_objc_impl_cannot_be_generic, "@ObjCImpl declaration cannot be generic") ERROR(parse_objc_impl_cannot_be_abstract, "@ObjCImpl declaration cannot be abstract") ERROR(parse_objc_impl_cannot_be_sealed, "@ObjCImpl declaration cannot be sealed") ERROR(parse_objc_impl_cannot_have_static_init, "@ObjCImpl declaration cannot have static initializer") ERROR(parse_objc_impl_cannot_be_open, "@ObjCImpl class cannot be open") ERROR(parse_objc_impl_cannot_be_interface, "interface cannot be @ObjCImpl") ERROR(parse_objc_mirror_field_cannot_have_initializer, "@ObjCMirror declaration field cannot have initializer") ERROR(parse_objc_mirror_field_cannot_be_static, "@ObjCMirror declaration field cannot be 'static'") ERROR(parse_objc_mirror_prop_cannot_have_getter, "@ObjCMirror declaration property '%s' cannot have getter", "@ObjCMirror declaration property cannot have getter") ERROR(parse_objc_mirror_prop_cannot_have_setter, "@ObjCMirror declaration property '%s' cannot have setter", "@ObjCMirror declaration property cannot have setter") ERROR(parse_objc_mirror_member_must_have_foreign_name, "@ObjCMirror declaration member must have @ForeignName annotation", "@ObjCMirror declaration member must have @ForeignName annotation") ERROR(parse_objc_impl_member_must_have_foreign_name, "@ObjCImpl declaration member must have @ForeignName annotation", "@ObjCImpl declaration member must have @ForeignName annotation") ERROR(parse_objc_interop_not_supported, "Objective-C interoperability feature '%s' is not yet supported") ERROR(parse_diag_error, "%s", "%s") WARNING(parse_diag_warning, PARSER, "%s", "%s") // Don't add any diag after this kind. ERROR(parse_diag_end, "") cangjie_compiler-1.0.7/include/cangjie/Basic/DiagRefactor/DiagnosticParserQuery.def000066400000000000000000000033501510705540100303550ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. ERROR(parse_query_diag_begin, "") // Query parser diagnostic info. ERROR(parse_query_expected_position_compare_operator, "expected '= or < or <='") ERROR(parse_query_expected_hash_id_compare_operator, "expected '='") ERROR(parse_query_invalid_query_value, "should be identifer, positive integer, 'foo*' or '*foo'") ERROR(parse_query_expected_query_symbol, "expected identifier or '( # _'") ERROR(parse_query_expected_logic_symbol, "expected '&&' or '||', but got '%s' here") ERROR(parse_query_position_illegal_file_id, "the first element of position tuple should be file id") ERROR(parse_query_position_illegal_line_num, "the second element of position tuple should be line number") ERROR(parse_query_position_illegal_column_num, "the third element of position tuple should be column number") ERROR(parse_query_position_comma_required, "comma required here to seperate position tuple") ERROR(parse_query_hashid_illegal_file_hash, "the first element of hash id should be file hash") ERROR(parse_query_hashid_illegal_fieldid, "the second element of hash id should be field id") // Searcher diagnostic info. ERROR(searcher_past_the_end_of_array, "Searcher error: id number '%s' past the end of array and it would be ignored.") ERROR(searcher_empty_number, "Searcher error: number empty.") ERROR(searcher_invalid_number, "Searcher error: number '%s' must be positive integer.") ERROR(searcher_invalid_scope_name, "Searcher error: scope name doesn't support suffix search *'%s'.") ERROR(parse_query_diag_end, "")cangjie_compiler-1.0.7/include/cangjie/Basic/DiagRefactor/DiagnosticSema.def000066400000000000000000000671141510705540100267700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. ERROR(sema_diag_begin, "") // General ERROR(sema_invalid_node_after_check, "semantic error") ERROR(sema_unable_to_infer_decl, "unable to infer declaration type, please add type annotation") ERROR(sema_mismatched_types, "mismatched types", "expected '%s', found '%s'") ERROR(sema_mismatched_types_multiple_assign, "mismatched types", "the expression has type '%s'") ERROR(sema_mismatched_types_because, "mismatched types", "expected '%s', found '%s'", {"expected '%s' because of %s"}) ERROR(sema_ambiguous_use, "ambiguous use of '%s'") ERROR(sema_undeclared_identifier, "undeclared identifier '%s'") ERROR(sema_undefined_variable, "variable '%s' is used before being defined") ERROR(sema_redefinition, "redefinition of declaration '%s'") ERROR(sema_conflict_with_sub_package, "top-level declaration '%s' is conflicted with possible sub-package '%s'") ERROR(sema_core_object_not_found_when_no_prelude, "class 'Object' of package 'std/core' is not found, cannot use '--no-prelude' option") ERROR(sema_accessibility_with_main_hint, "'%s' declaration uses %s types", "%s '%s' contains %s type") ERROR(sema_accessibility, "'%s' declaration uses %s types") ERROR(sema_param_miss_match, "mismatched number of parameters", "expected '%s', found '%s'") // Function ERROR(sema_unable_to_infer_return_type, "unable to infer return type, please add type annotation") ERROR(sema_unable_to_infer_generic_func, "unable to infer generic argument of this function") ERROR(sema_invalid_called_object, "called object is not a function or constructor") ERROR(sema_invalid_return, "'return' must be used inside a function body") ERROR(sema_invalid_return_in_static_init, "'return' cannot be used inside the static initializer") ERROR(sema_wrong_number_of_arguments, "%s for parameter list '%s' in call", "expected %s, found %s") ERROR(sema_unordered_arguments, "positional argument cannot appear after named argument") ERROR(sema_param_named_mismatched, "parameter name mismatched") ERROR(sema_need_named_argument, "missing argument prefix %s for named parameter") ERROR(sema_invalid_subscript_assign_parameter, "overloaded operator '[]' can only have one named parameter 'value'") ERROR(sema_invalid_subscript_assign_parameter_num, "overloaded operator '[]' should have at least one positional parameter for index") ERROR(sema_invalid_subscript_assign_return, "the return type of subscript assignment must be 'Unit'") ERROR(sema_overload_conflicts, "%s '%s' has overload conflicts") ERROR(sema_static_function_overload_conflicts, "overloaded functions '%s' cannot mix static and non-static") ERROR(sema_use_mutable_func_alone, "mutable function '%s' cannot be used alone as reference") ERROR(sema_unsafe_func_can_only_be_called, "the unsafe function can only be called rather than as name reference") ERROR(sema_ambiguous_match_primitive_extend, "ambiguous match for function call '%s' of these extended type: %s") ERROR(sema_immutable_access_mutable_func, "cannot use mutable function on immutable value", "is immutable") ERROR(sema_recursive_constructor_call, "recursive constructor calling detected") ERROR(sema_cannot_have_default_param, "optional parameter cannot be used in %s function") ERROR(sema_trailing_lambda_cannot_used_for_non_function, "trailing lambda cannot be used for %s", "declaration type of parameter: '%s'") // Expression ERROR(sema_unable_to_infer_expr, "unable to infer the type of this expression, please add type annotation") ERROR(sema_exceed_num_value_range, "the number '%s' exceeds the value range of type '%s'") ERROR(sema_exceed_float_literal_range, "the number '%s' exceeds the value range of floating-point literal") WARNING(sema_float_literal_too_large, UNGROUPED, "magnitude of floating-point literal too large for type '%s', maximum is %s") WARNING(sema_float_literal_too_small, UNGROUPED, "magnitude of floating-point literal too small for type '%s', minimum is %s") ERROR(sema_invalid_unary_expr, "invalid unary operator '%s' on type '%s'") ERROR(sema_invalid_unary_expr_with_target, "invalid unary operator '%s' on type '%s' with return type '%s'") ERROR(sema_invalid_binary_expr, "invalid binary operator '%s' on type '%s' and '%s'") ERROR(sema_invalid_subscript_expr, "invalid subscript operator [] on type '%s' with index %s") ERROR(sema_cannot_assign_to_subscript, "cannot assign to this subscript expression") ERROR(sema_not_member_of, "'%s' is not a member of %s '%s'") ERROR(sema_member_not_imported, "'%s' is not imported") ERROR(sema_cannot_assign_to_immutable, "cannot assign to immutable value") ERROR(sema_unqualified_left_value_assigned, "'%s' can not be assigned") ERROR(sema_not_found_from_generic_upper_bounds, "'%s' is not found for generic type '%s' in its upper bounds") ERROR(sema_different_or_pattern, "patterns connected by '|' should be of the same kind", "%s") ERROR(sema_var_in_or_pattern, "cannot introduce variables in patterns connected by '|'") ERROR(sema_var_in_or_condition, "cannot introduce variables in conditions connected by '||'") ERROR(sema_nonexhuastive_patterns, "non-exhaustive patterns", "the selector is of type '%s'") WARNING(sema_unreachable_pattern, UNUSED, "unreachable pattern") ERROR(sema_lambdaExpr_must_have_type_annotation, "parameters of this lambda expression must have type annotations") ERROR(sema_use_func_capture_var_alone, "%s capturing mutable variables needs to be called directly") ERROR(sema_enum_constructor_with_param_must_have_args, "enum constructor '%s' must be used with arguments") ERROR(sema_optional_chain_non_optional, "cannot use optional chaining", "cannot use optional chaining on non-optional value of type '%s'") ERROR(sema_capture_before_initialization, "cannot capture variable '%s' before initialization") ERROR(sema_interpolation_in_const_pattern, "cannot use string interpolation in constant pattern") ERROR(sema_cannot_ref_to_pkg_name, "package name cannot be referred independently") ERROR(sema_use_expr_without_import, "import '%s' to use the '%s' expression") // Generic ERROR(sema_generic_func_without_type_arg, "type arguments needed for the generic function%s", "cannot infer type arguments for the generic function") ERROR(sema_generic_type_inconsistent, "generic types substitutions are inconsistent for '%s'") ERROR(sema_generic_argument_no_match, "type argument's number does not match type parameter's number") ERROR(sema_generic_constraint_not_looser, "the constraint of type parameter is not looser than parent's constraint") ERROR( sema_generic_instantiation_causes_ambiguous_functions, "generic instantiation '%s' causes ambiguous function '%s'") // where sanity check. ERROR(sema_multiple_class_upperbounds, "generic parameter '%s' cannot have two or more class upper bounds '%s' without subtype relation") ERROR(sema_generic_param_exist_in_class_irrelevant_upperbound_recursively, "generic parameter '%s' cannot be used in class irrelevant upper bounds '%s'") ERROR(sema_generic_param_directly_recursive, "generic parameter '%s' is bounded directly recursively with '%s' which is forbidden") ERROR(sema_upper_bound_must_be_class_or_interface, "the upper bound '%s' of generic parameter '%s' must be class or interface") // Inheritance check ERROR(sema_inherit_member_kind_inconsistent, "%s member '%s' cannot have the same name with %s member in %s") ERROR(sema_inherit_super_member_kind_inconsistent, "inherited members '%s' have inconsistent decl types") ERROR(sema_inherit_member_type_inconsistent, "%s of the inherited %s members '%s' are not identical and not in subtype relation") ERROR(sema_inherit_abstract_class_static_unimplement_func, "abstract class '%s' cannot contain unimplemented static %s '%s'") ERROR(sema_cannot_override, "cannot override %s '%s'") ERROR(sema_invalid_member_visibility_in_class, "the visibility of an '%s' %s must be 'public' or 'protected'") ERROR(sema_weak_visibility, "a deriving member must be at least as visible as its base member", "%s") ERROR(sema_cannot_inherit_sealed, "cannot %s %s 'sealed' %s '%s'") ERROR(sema_inherit_thread_context_invalid, "user defined decl '%s' not support to inherit, implement or extend 'ThreadContext'") ERROR(sema_inherit_thread_context_not_open, "'%s' cannot be modified with 'open' when inherit, implement or extend 'ThreadContext'") ERROR(sema_inherit_not_return_this, "an open function that returns 'This' must keep the return type 'This' when overridden") ERROR(sema_return_type_incompatible, "return type of '%s' is not identical or not a subtype of the overridden/redefined/implement function") // Spawn ERROR(sema_spawn_arg_invalid, "invalid argument of spawn expr, user-defined `ThreadContext` types are prohibited now") WARNING(sema_spawn_arg_no_effect, UNGROUPED, "argument of spawn expr does not take effect at current backend") // Interface ERROR(sema_interface_call_with_unimplemented_call, "static invocation contains unimplemented static %s '%s'") // class / interface / struct ERROR(sema_type_uninitialized_static_field, "the static member variable '%s' is not initialized") ERROR(sema_instance_func_cannot_be_used_in_finalizer, "instance %s cannot be used in the finalizer") ERROR(sema_no_non_param_constructor_in_super_class, "there is no non-parameter constructor in super class, please invoke super call explicitly") ERROR(sema_non_abstract_class_cannot_be_sealed, "non-abstract class cannot be modified by 'sealed'") ERROR(sema_static_variable_use_generic_parameter, "static member cannot depend on generic parameter '%s'") ERROR(sema_cstruct_cannot_impl_interfaces, "struct with @C cannot implement interfaces") ERROR(sema_class_need_abstract_modifier_or_func_need_impl, "class '%s' missing abstract modifier, otherwise abstract function or property should be implemented") ERROR(sema_need_member_implementation, "implementation of function or property is needed in '%s'") ERROR(sema_export_same_private_decl, "currently, it is not possible to export two private declarations with the same name") // Extend ERROR(sema_extend_function_cannot_overridden, "cannot override %s '%s' in extend of supertype") ERROR(sema_extend_member_cannot_shadow, "extend member '%s' is not allowed to shadow members of '%s'") ERROR(sema_illegal_extended_type, "extending type '%s' is not allowed") ERROR(sema_extend_generic_must_be_used, "type parameter%s must be used in extended type") ERROR(sema_extend_duplicate_interface, "interface '%s' has been implemented by '%s', please remove it") ERROR(sema_extend_not_interface, "expected an interface, found non-interface type", "expected an interface here") ERROR(sema_extend_illegal_member, "illegal extend member, only functions, props, associated types are allowed") ERROR(sema_extend_use_super, "'super' is not allowed inside an extend declaration") ERROR(sema_type_cannot_extend_imported_interface, "%s type '%s' cannot extend imported interface") ERROR(sema_c_type_cannot_extend_interface, "c type '%s' cannot support interface extend") ERROR(sema_immutable_type_extend_assignment_index_operator, "it's illegal to extend index assignment operator '[](index, value)' for immutable type") ERROR(sema_immutable_type_illegal_property, "there cannot have mutable property in immutable type") ERROR(sema_interface_is_not_extendable, "interface '%s' is not able to be extended") ERROR(sema_invalid_mut_modifier_extend_of_struct, "'mut' modifier is illegal in extend body of '%s'") ERROR(sema_extend_check_sequence_cannot_decide, "unable to decide which extension happens first") ERROR(sema_export_extend_depend_non_export_extend, "exported extension cannot indirectly export the functions '%s' of the non-exported extension") // Property ERROR(sema_property_must_have_accessors, "property must have accessors") ERROR(sema_immutable_property_with_setter, "immutable property cannot have setter") ERROR(sema_property_have_same_declaration_in_inherit_mut, "property '%s' should have 'mut' modifier") ERROR(sema_property_have_same_declaration_in_inherit_immut, "property '%s' should be immutable") ERROR(sema_property_must_implement_both, "property must implement both getter/setter of interface property '%s'") // Const evaluation ERROR(sema_expect_const, "expected 'const' %s", "%s") ERROR(sema_cannot_define_var_in_const_funciton, "cannot define 'var' variable in 'const' function") ERROR(sema_no_const_init, "cannot define 'const' member function without 'const' constructor") ERROR(sema_class_const_init_with_var, "cannot define 'const' constructor with 'var' members in class") // Annotation ERROR(sema_annotation_no_const_init, "class with '@Annotation' should have 'const' constructor") ERROR(sema_annotation_arg_target, "'@Annotation' can only have one named argument 'target'") ERROR(sema_annotation_arg_target_array_lit, "the argument of '@Annotation' should be array literal") WARNING(sema_annotation_non_public, UNGROUPED, "'@Annotation' modifying non-'public' class is invisible at runtime") ERROR(sema_annotation_custom_place, "cannot use custom annotation") // inout ERROR(sema_inout_modify_cstring_or_zerosized, "the expression qualified by 'inout' cannot be of %s") ERROR(sema_inout_modify_non_ctype, "the type of experssion qualified by 'inout' must meet 'CType' constraint") ERROR(sema_inout_must_be_var_variable, "'inout' can only qualify variable defined with 'var'", "%s") ERROR(sema_inout_modify_heap_variable, "the variable qualified by 'inout' cannot be directly or indirectly derived from an instance of a 'class'") ERROR(sema_inout_can_only_used_in_cfunc_calling, "'inout' can only be used in a 'CFunc' calling") ERROR(sema_inout_mismatch, "mismatch 'inout' of function argument with type '%s'") // Java interoperation ERROR(sema_annotation_error_arg_num, "'%s' should have %s arg") ERROR(sema_annotation_error_arg_range, "'%s' only supports %s as arg") ERROR(sema_annotation_error_object, "'%s' can only modify %s") ERROR(sema_java_incorrect_use_between_types, "type annotated with '@Java[\"ext\"]' can only be used within the declaration which has '@Java[\"ext\"]' " "annotation") ERROR(sema_java_non_jtype, "%s type in %s '%s' with '@Java' must meet JType constraint") ERROR(sema_java_invalid_unit, "%s type in %s '%s' with '@Java' can not be 'Unit'") ERROR(sema_java_app_inherit_ext, "only types annotated with '@Java[\"ext\"] can %s from a type annotated with '@Java[\"ext\"]'") ERROR(sema_java_unsupported_decl, "%s is not supported in %s '%s' annotated with '@Java'") ERROR(sema_missing_java_interop_annotation, "%s '%s' should have '@Java' annotation") ERROR(sema_generic_static_access, "cannot access static member with generic parameter in '@Java' types") ERROR(sema_primitive_type_as_generics_arg, "only reference types are available for '@Java' generics") ERROR(sema_meet_constraint_indirectly, "types that meet constraints by 'extend' cannot be used in '@Java' generics") ERROR( sema_static_member_in_interface_must_has_body, "static functions in '@Java'-annotated interfaces must have a body") ERROR(sema_extend_a_java_type, "types annotated with '@Java' cannot be extended") ERROR(sema_generic_upper_bounds_must_be_java_in_java, "generic type's upper bound in types annotated with '@Java' should be annotated with '@Java' too") ERROR(sema_define_java_annotation, "types annotated with '@Java' cannot be annotated with '@Annotation' together") ERROR( sema_invalid_use_of_java_annotation, "imported Java annotations can only be used with types annotated with '@Java'") ERROR( sema_invalid_use_of_annotation_jffi, "only imported Java annotations can be used with types annotated with '@Java'") ERROR(sema_annotation_not_applicable_jffi, "'@%s' not applicable to %s") ERROR(sema_cannot_use_annotation_jffi, "cannot use annotation here") ERROR(sema_shadow_cannot_in_type_args, "'%s' is not allowd to be used here as type argument, because it shadows field '%s' with its super type '%s'") ERROR(sema_unsupported_type_argument_in_java_interop, "type argument in java interoperation should meet 'JType' constraint") ERROR(sema_cjmapping_struct_generic_not_supported, "cjmapping struct generic %s is not supported") ERROR(sema_cjmapping_struct_inheritance_interface_not_supported, "cjmapping struct inheritance interface is not supported") ERROR(sema_cjmapping_decl_not_supported, "cjmapping decl type is not supported for %s") ERROR(sema_cjmapping_method_arg_not_supported, "argument type of cjmapping member function is not supported") ERROR(sema_cjmapping_method_ret_unsupported, "return type '%s' of function inside %s CJMapping is not supported") // VArray ERROR(sema_varray_size_match, "mismatch 'VArray' type's size", "expected size is %s, found %s") ERROR(sema_varray_args_number_mismatch, "'VArray' constructor accepts only one argument") ERROR(sema_varray_subscript_num, "'VArray' accepts exactly one subscript index with type of 'Int64'") ERROR(sema_varray_in_cfunc, "return type of CFunc cannot be 'VArray' type") ERROR(sema_varray_arg_type_with_reftype, "'%s' directly or indirectly contains an unsupported type", "contain unsupported instance member variable with type '%s'") // CFFI ERROR(sema_invalid_cfunc_return_type, "return type of CFunc must be instantiated with CType") // Unit test ERROR(sema_mock_disabled, "mocking features are disabled, you can enable them by passing %s compilation option explicitly, or using default " "mode") ERROR(sema_mock_not_in_test_mode, "mocking features can be used only in the test mode, please pass %s compilation option to compile the package in " "the test mode") ERROR(sema_mock_unsupported_type, "only mocking of classes or interfaces is supported") ERROR(sema_mock_wrong_static_decl, "static/top-level declaration to mock shouldn't be private, local, constant or constructor") ERROR(sema_mock_doesnt_support_mocking, "'%s' doesn't support mocking, please be sure that its package '%s' is mock-compatible (was compiled with %s " "compilation option)") ERROR(sema_mock_frozen_required, "generic wrapper function '%s' for createMock/createSpy calls should be marked with @Frozen annotation") // effects ERROR(sema_command_handle_type_error, "the command handle type must implement 'effect.Command'") ERROR(sema_resumption_handle_type_error, "the type of the resumption must extend 'effect.Resumption'") ERROR(sema_resumption_incorrect_return_type, "the return type of the resumption ('%s') does not match the type of the try block ('%s')") ERROR(sema_command_resumption_mismatch, "the parameter type of the resumption ('%s') does not match the result type of the command ('%s')") ERROR(sema_implicit_resume_outside_handler, "'resume' outside of an immediate handler must have a resumption argument") ERROR(sema_resume_no_with, "a resumption of non-Unit type '%s' must have a 'with' or 'throwing' clause") ERROR(sema_resume_wrong_resumption_type, "resumptions must be of type 'core.Resumption', but actual type is '%s'") ERROR(sema_mismatching_handle_block, "The type of this handle block is '%s', which mismatches the smallest common supertype '%s' of previous branches.") ERROR(sema_return_in_try_handle_block, "Return statements are not allowed within try/handle blocks") ERROR(sema_command_incompatible_type, "type '%s' does not implement compatible instantiations of 'Command'") ERROR(sema_resume_throwing_mismatch_type, "the type of the `resume throwing` must be a subtype of core.Exception or core.Error") WARNING(sema_useless_command_type, UNUSED, "useless command type") // @Deprecated - call-site errors: ERROR(sema_deprecated_error, "%s '%s' is deprecated%s%s", "deprecated") WARNING(sema_deprecated_warning, DEPRECATED, "%s '%s' is deprecated%s%s", "deprecated") // @Deprecated - declaration-side errors: ERROR(sema_deprecation_weakening, "strictness of @Deprecated can not be weaken on inheritors") ERROR(sema_deprecation_override_error, "overridden %s '%s' should be marked with @Deprecated") WARNING(sema_deprecation_override_warning, DEPRECATED, "overridden %s '%s' should be marked with @Deprecated") ERROR(sema_deprecation_redef_error, "redefined %s '%s' should be marked with @Deprecated") WARNING(sema_deprecation_redef_warning, DEPRECATED, "redefined %s '%s' should be marked with @Deprecated") // common/platform matching ERROR(sema_common_open_class_no_init, "please implement the constructor explicitly for common open class '%s'") ERROR(sema_multiple_common_implementations, "'common' %s has several platform implementations") ERROR(sema_common_direct_extension_has_duplicate_private_members, "declaration 'common' extend '%s' has a conflicting private %s '%s'") ERROR(sema_not_matched, "'%s' %s can not find '%s' match") ERROR(sema_platform_var_not_match_let, "'platform' '%s' can not match 'common' '%s'") ERROR(sema_platform_init_common_primary_constructor, "'platform' init can not be used to implement primary 'common' constructor") ERROR(platform_has_different_kind, "'platform' decl kind(%s) is not equal to 'common'(%s)") ERROR(sema_platform_primary_unmatched_var_decl, "parameter in 'platform' primary constructor must also be a member variable declaration " "if it's a member variable declaration in 'common' primary constructor") ERROR(common_non_exaustive_platfrom_exaustive_mismatch, "exhaustive 'common' %s cannot be matched with non-exhaustive 'platform' %s") ERROR(sema_platform_has_different_type, "'platform' %s type is not equal to 'common' type") ERROR(sema_platform_member_must_have_implementation, "the member %s must have body in 'platform' %s") ERROR(sema_platform_has_different_modifier, "'platform' %s modifier is not match 'common' modifier") ERROR(sema_platform_has_different_annotation, "'platform' %s annotation is not match 'common' annotation") ERROR(sema_platform_has_different_parameter, "'platform' function parameter is not match 'common' parameter") ERROR(sema_platform_has_different_super_type, "'platform' %s super types is not match 'common' super types") ERROR(sema_platform_has_duplicate_extensions, "declaration 'platform' extend '%s' has a conflicting extension") ERROR(sema_common_package_has_main, "main function cannot be used in common package part") ERROR(sema_common_static_let_cant_be_initialized_in_static_init, "'common' static let '%s' can not be initialized in static init") ERROR(sema_cjmp_abstract_class_member_has_no_explicit_modifier, "'%s' abstract class %s must have explicit '%s' or 'abstract' modifier") ERROR(sema_explicitly_abstract_can_not_have_body, "abstract %s can not have body") ERROR(sema_explicitly_abstract_only_for_cjmp_abstract_class, "only common/platform class can have explicitly abstract %s") ERROR(sema_open_abstract_platform_can_not_replace_open_common, "open common %s can not be overridden with abstract platform %s") // java mirror ERROR(sema_java_mirror_ctor_arg_must_be_java_mirror, "argument type of java-mirrored constructor must be of @JavaMirror type") ERROR(sema_java_mirror_method_arg_must_be_java_mirror, "argument type of java-mirrored function must be of @JavaMirror type") ERROR(sema_java_mirror_method_ret_unsupported, "return type '%s' of function inside %s class is not supported") ERROR(sema_java_mirror_prop_must_be_java_mirror, "property of java-mirrored declaration must be of @JavaMirror type") ERROR(sema_java_mirror_subtype_must_be_annotated, "super declaration '%s' is inheritable only for declaration annotated with @JavaMirror or @JavaImpl") ERROR(sema_java_mirror_cannot_inherit_pure_cangjie_type, "@JavaMirror-annotated declaration cannot inherit pure cangjie type") ERROR(sema_java_impl_cannot_inherit_pure_cangjie_type, "@JavaImpl-annotated declaration cannot inherit pure cangjie type") ERROR(sema_java_mirror_subtype_anno_must_inherit_mirror, "@JavaImpl-annotated declaration must inherit @JavaMirror-annotated declaration") ERROR(sema_java_mirror_cannot_be_extended_with_interface, "@JavaMirror class cannot be extended with interface") ERROR(sema_java_impl_cannot_be_extended_with_interface, "@JavaImpl class cannot be extended with interface") ERROR(sema_java_impl_redefinition, "redefinition of java declaration '%s'") ERROR(sema_java_mirror_interoplib_must_be_imported, "interoplib.interop must be imported to use java interoperability") ERROR(sema_java_interop_not_supported, "Java interoperability feature '%s' is not yet supported") WARNING(sema_java_interoplib_version_too_old, UNGROUPED, "java interoplib.interop library's version is too old. Compiler was built expecting versoin '%s'. " "Compatibility problems could happen. Use it at your own risk") WARNING(sema_java_interoplib_version_mismatch, UNGROUPED, "java interoplib.interop library's version is '%s', but compiler was built expecting version '%s'. " "Compatibility problems could happen. Use it at your own risk") // Objective-C mirror ERROR(sema_objc_mirror_ctor_param_must_be_objc_compatible, "param type of Objective-C mirror constructor must be Objective-C compatible") ERROR(sema_objc_mirror_method_param_must_be_objc_compatible, "param type of Objective-C mirror method must be Objective-C compatible") ERROR(sema_objc_mirror_method_ret_must_be_objc_compatible, "return type of Objective-C mirror method must be Objective-C compatible") ERROR(sema_objc_mirror_prop_must_be_objc_compatible, "Objective-C mirror property type must be Objective-C compatible") ERROR(sema_objc_mirror_field_must_be_objc_compatible, "Objective-C mirror field type must be Objective-C compatible") ERROR(sema_objc_mirror_decl_cannot_inherit, "Objective-C mirror cannot inherit other supertypes") ERROR(sema_objc_mirror_subtype_cannot_multiple_inherit, "Objective-C mirror subtype cannot inherit multiple types (only 1 interface or 1 class is allowed)") ERROR(sema_objc_mirror_subtype_must_be_annotated, "Objective-C mirror subtype must be annotated with @ObjCMirror or @ObjCImpl") ERROR(sema_objc_mirror_subtype_must_inherit_mirror, "Objective-C mirror subtype must inherit Objective-C mirror") ERROR(sema_objc_mirror_must_inherit_mirror, "@ObjCMirror declaration cannot inherit not @ObjCMirror declarations") ERROR(sema_objc_mirror_interoplib_must_be_imported, "interoplib.objc must be imported to use Objective-C interoperability") ERROR(sema_objc_interop_not_supported, "Objective-C interoperability feature '%s' is not yet supported") ERROR(sema_objc_pointer_argument_must_be_objc_compatible, "ObjCPointer can only be used with Objective-C compatible types") ERROR(sema_foreign_name_appeared_in_child, "@ForeignName could not appear on overridden declaration") ERROR(sema_foreign_name_conflicting_annotation, "Declaration '%s' has a conflicting @ForeignName annotation") ERROR(sema_foreign_name_conflicting_derived_annotation, "Declaration '%s' has a conflicting derived @ForeignName '%s'") // @IfAvailable ERROR(sema_ifavailable_arg_no_name, "the first argument of @IfAvailable expression must have a name") ERROR(sema_ifavailable_arg_not_literal, "the first argument of @IfAvailable expression must be a literal expression") ERROR(sema_ifavailable_unknow_arg_name, "unknown parameter name '%s'") // @APILevel ERROR(sema_apilevel_multi_anno, "annotate more than one '@!APILevel'") WARNING(sema_apilevel_missing_arg, APILEVEL_CHECK, "annotation missing named argument '%s' or unable to read as numerical value") ERROR(sema_only_literal_support, "only %s literal values are supported for now") ERROR(sema_apilevel_ref_higher, "cannot reference '%s'(level: %s) which higher than level of the current scope(level: %s)") WARNING(sema_apilevel_syscap_warning, APILEVEL_CHECK, "inappropriate syscap '%s'") ERROR(sema_apilevel_syscap_error, "inappropriate syscap '%s'") ERROR(sema_apilevel_multi_diff_syscap, "declaration mark with different syscap") ERROR(sema_ifavailable_level_limit, "`@IfAvaliable` feature is not avaliable in device where the APILevel is less than 19 due to missing capatability " "in ROM") WARNING(sema_unused_import, UNUSED, "unused import '%s'", "unused import") ERROR(sema_diag_end, "") cangjie_compiler-1.0.7/include/cangjie/Basic/DiagRefactor/DiagnosticWarnGroupKind.def000066400000000000000000000012251510705540100306240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. WARN_GROUP("unused", UNUSED) WARN_GROUP("unused-main", UNUSED_MAIN) WARN_GROUP("driver-arg", DRIVER_ARG) WARN_GROUP("deprecated", DEPRECATED) WARN_GROUP("unsupport-compile-source", UNSUPPORT_COMPILE_SOURCE) WARN_GROUP("package-import", PACKAGE_IMPORT) WARN_GROUP("parser", PARSER) WARN_GROUP("semantics", SEMANTICS) WARN_GROUP("interpreter", INTERPRETER) WARN_GROUP("apilevel-check", APILEVEL_CHECK) cangjie_compiler-1.0.7/include/cangjie/Basic/DiagnosticCHIR.def000066400000000000000000000027161510705540100242730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. ERROR(chir_diag_begin, "") ERROR(chir_var_might_circular_dependency, "%s might have circular dependency") ERROR(chir_file_might_circular_dependency, "%s might have file circular dependency") ERROR(chir_used_before_initialization, "variable '%s' is used before initialization") // sancov pass ERROR(chir_sancov_illegal_usage_of_pc_table, "use '--sanitizer-coverage-pc-table, [inline-bool-flag|inline-8bit-counters|trace-pc-guard]' instead") ERROR(chir_sancov_illegal_usage_of_level, "'--sanitizer-coverage-level' is illegal here") // interpreter ERROR(interp_cannot_interp_node, "['%s'] failed to interpret node '%s'") ERROR(interp_malloc_failed, "failed allocating memory") ERROR(interp_unsupported, "['%s'] unsupported '%s'") ERROR(interp_unsupported_type, "['%s'] unsupported type '%s'") ERROR(interp_cannot_convert_array2ffi, "cannot convert array to FFI value") ERROR(interp_cannot_load_incremental_bchir, "could not load previous incremental BCHIR") // const eval ERROR(const_eval_exception, "an exception was thrown while evaluating constant") ERROR(const_eval_load_dep, "failed to load const eval dependency '%s'") ERROR(const_eval_unsupported, "tried to run non-const code in const eval") ERROR(chir_diag_end, "")cangjie_compiler-1.0.7/include/cangjie/Basic/DiagnosticEmitter.h000066400000000000000000000016011510705540100246400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Diagnostic Emitter class, which emits single diagnostic to console. */ #ifndef CANGJIE_DIAGNOSTICEMITTER_H #define CANGJIE_DIAGNOSTICEMITTER_H #include "cangjie/Basic/DiagnosticEngine.h" namespace Cangjie { class DiagnosticEmitter { public: DiagnosticEmitter( Diagnostic& d, bool nc, bool enableRangeCheckICE, std::basic_ostream& o, SourceManager& sourceManager); ~DiagnosticEmitter(); // return false if some errors occurred. bool Emit() const; private: class DiagnosticEmitterImpl* impl; }; } // namespace Cangjie #endif // CANGJIE_DIAGNOSTICEMITTER_H cangjie_compiler-1.0.7/include/cangjie/Basic/DiagnosticEngine.h000066400000000000000000001100661510705540100244420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the DiagnosticEngine related classes, which provides diagnostic capabilities. */ #ifndef CANGJIE_BASIC_DIAGNOSTICENGINE_H #define CANGJIE_BASIC_DIAGNOSTICENGINE_H #include #include "cangjie/AST/Node.h" #include "cangjie/Basic/Position.h" #include "cangjie/Basic/SourceManager.h" #include "cangjie/Option/Option.h" namespace Cangjie { const int DEFAULT_DIAG_NUM = 8; class DiagnosticEngine; using ArgType = std::variant; // It is true if all args type are std::string, otherwise false. template constexpr bool IsAllString = std::conjunction_v...>; const DiagColor MAIN_HINT_COLOR = DiagColor::RED; const DiagColor OTHER_HINT_COLOR = DiagColor::CYAN; const DiagColor NOTE_COLOR = DiagColor::BLUE; const DiagColor HELP_COLOR = DiagColor::GREEN; const DiagColor NO_COLOR = DiagColor::NO_COLOR; const std::string MACROCALL_CODE = "the code after the macro is expanded as follows"; /** * DiagArgument is a struct packing the argument of Diagnose function. */ struct DiagArgument { public: ArgType arg; DiagArgument() = default; DiagArgument(int args) : arg(args) { } DiagArgument(const std::string& args) : arg(args) { } DiagArgument(const char* args) : arg(args) { } DiagArgument(int64_t args) : arg(static_cast(args)) { } DiagArgument(size_t args) : arg(static_cast(args)) { } DiagArgument(char args) : arg(args) { } DiagArgument(Position pos) : arg(pos) { } ~DiagArgument() = default; }; /* * DiagKind is the specific diagnostic kind. */ enum class DiagKind { #define NOTE(Kind, Info) Kind, #define ERROR(Kind, Info) Kind, #define WARNING(Kind, Group, Info) Kind, #include "cangjie/Basic/DiagnosticsAll.def" #undef ERROR #undef WARNING #undef NOTE }; constexpr const char* const DIAG_KIND_STR[]{ #define NOTE(Kind, Info) #Kind, #define ERROR(Kind, Info) #Kind, #define WARNING(Kind, Group, Info) #Kind, #include "cangjie/Basic/DiagnosticsAll.def" #undef ERROR #undef WARNING #undef NOTE }; constexpr size_t DIAG_KIND_STR_SIZE = sizeof(DIAG_KIND_STR) / sizeof(*DIAG_KIND_STR); enum class DiagSeverity : uint8_t { DS_ERROR, DS_WARNING, DS_NOTE, DS_HINT }; enum class DiagCategory : uint8_t { LEX = 0, PARSE, PARSE_QUERY, // Parse query only used for lsp. CONDITIONAL_COMPILATION, IMPORT_PACKAGE, MODULE, MACRO_EXPAND, SEMA, CHIR, OTHER }; constexpr const DiagSeverity DiagSeveritys[]{ #define ERROR(Kind, Info) DiagSeverity::DS_ERROR, #define NOTE(Kind, Info) DiagSeverity::DS_NOTE, #define WARNING(Kind, Group, Info) DiagSeverity::DS_WARNING, #include "cangjie/Basic/DiagnosticsAll.def" #undef ERROR #undef WARNING #undef NOTE }; constexpr const char* const DiagMessages[] = { #define ERROR(Kind, Info) Info, #define WARNING(Kind, Group, Info) Info, #define NOTE(Kind, Info) Info, #include "cangjie/Basic/DiagnosticsAll.def" #undef ERROR #undef WARNING #undef NOTE }; constexpr const WarnGroup warnGroups[] = { #define ERROR(Kind, ...) WarnGroup::NONE, #define WARNING(Kind, Group, ...) WarnGroup::Group, #define NOTE(Kind, Info) WarnGroup::NONE, #include "cangjie/Basic/DiagnosticsAll.def" #undef WARNING #undef ERROR #undef NOTE }; /** * This is class to keep diagnostic data for single error. * Format of one diagnostic like: * ``` * error: `error message' * ==> file.cj:line:column * | source code 1 * | ^^^^^^^^^^^^^ 'mainHint' * | source code 2 source code 3 * | ~~~~~~ `otherHint 1` ~~~~~~ 'otherHint 2` * ``` * note: the 'error message' and 'mainHint' share same source code position. the 'otherHints' need register new position * if it has. * */ struct ErrorData { std::string message; std::string mainHint; std::vector otherHints{}; ErrorData(std::string message1 = "", std::string mainHint1 = "", std::vector otherHints1 = {}) : message(std::move(message1)), mainHint(std::move(mainHint1)), otherHints(std::move(otherHints1)) { } }; /** * This is new diagKind for refactoring, will replace previous DiagKind if all diag have been modified. */ const ErrorData errorData[] = { #define ERROR(Kind, ...) {__VA_ARGS__}, #define WARNING(Kind, Group, ...) {__VA_ARGS__}, #include "cangjie/Basic/DiagRefactor/DiagnosticAll.def" #undef WARNING #undef ERROR }; const std::map SEVE_TO_COLOR{ {DiagSeverity::DS_ERROR, DiagColor::RED}, {DiagSeverity::DS_WARNING, DiagColor::YELLOW}, {DiagSeverity::DS_NOTE, DiagColor::RED}}; const DiagKind DEFAULT_KIND = DiagKind::sema_diag_begin; /** * This is new diagKind for refactoring, will replace previous DiagKind if all diag have been modified. */ constexpr const DiagSeverity rDiagSeveritys[] = { #define ERROR(Kind, ...) DiagSeverity::DS_ERROR, #define WARNING(Kind, ...) DiagSeverity::DS_WARNING, #include "cangjie/Basic/DiagRefactor/DiagnosticAll.def" #undef WARNING #undef ERROR }; constexpr const WarnGroup rWarnGroups[] = { #define ERROR(Kind, ...) WarnGroup::NONE, #define WARNING(Kind, Group, ...) WarnGroup::Group, #include "cangjie/Basic/DiagRefactor/DiagnosticAll.def" #undef WARNING #undef ERROR }; constexpr const char* warnGroupDescrs[] = { #define WARN_GROUP(DESCR, KIND) DESCR, #include "cangjie/Basic/DiagRefactor/DiagnosticWarnGroupKind.def" #undef WARN_GROUP }; constexpr size_t WARN_GROUP_DESCRS_SIZE = sizeof(warnGroupDescrs) / sizeof(*warnGroupDescrs); /** * This is new diagKind for refactoring, will replace previous DiagKind if all diag have been modified. */ enum class DiagKindRefactor : unsigned { #define ERROR(Kind, ...) Kind, #define WARNING(Kind, ...) Kind, #include "cangjie/Basic/DiagRefactor/DiagnosticAll.def" #undef WARNING #undef ERROR }; constexpr const char* const RE_DIAG_KIND_STR[]{ #define ERROR(Kind, ...) #Kind, #define WARNING(Kind, ...) #Kind, #include "cangjie/Basic/DiagRefactor/DiagnosticAll.def" #undef WARNING #undef ERROR }; constexpr size_t RE_DIAG_KIND_STR_SIZE = sizeof(RE_DIAG_KIND_STR) / sizeof(*RE_DIAG_KIND_STR); struct Range { Position begin; Position end; bool operator==(const Range& right) const { return begin == right.begin && end == right.end; } size_t Hash() const { return (static_cast(begin.fileID)) ^ (static_cast(begin.line) << 8u) ^ (static_cast(begin.column) << 16u) ^ (static_cast(begin.fileID) << 24u) ^ (static_cast(begin.line) << 32u) ^ (static_cast(begin.column) << 40u); } bool IsDefault() const { return begin == DEFAULT_POSITION && end == DEFAULT_POSITION; } bool HasZero() const { return begin.IsZero() || end.IsZero(); } friend Range MakeRange(const Position& begin, Position end); friend Range MakeRange(const Position& identifierPos, const std::string& identifier); private: // The constructor of range is private, use MakeRange to make a Range. Range(Position b, Position e) : begin(b), end(e) { } }; Range MakeRange(const Position& begin, Position end); Range MakeRange(const Position& identifierPos, const std::string& identifier); Range MakeRange(const Identifier& id); struct IntegratedString { Range range = MakeRange(DEFAULT_POSITION, DEFAULT_POSITION); std::string str; DiagColor color{DiagColor::RESET}; IntegratedString() = default; IntegratedString(Range r, std::string s, DiagColor c) : range(r), str(std::move(s)), color(c) { } inline bool IsDefault() const { return range.begin == DEFAULT_POSITION && range.end == DEFAULT_POSITION; } }; struct Substitution { Range range = MakeRange(DEFAULT_POSITION, DEFAULT_POSITION); std::string str; Substitution(Range& r, std::string& s) : range(r), str(s) { } }; struct DiagHelp { std::vector substitutions; std::string helpMes; DiagHelp() = default; DiagHelp(std::string s) : helpMes(std::move(s)) { } void AddSubstitution(Position p, std::string s) { auto range = MakeRange(p, p + 1); substitutions.emplace_back(range, s); } void AddSubstitution(Range range, std::string s) { substitutions.emplace_back(range, s); } void AddSubstitution(const Token& t, std::string s) { auto range = MakeRange(t.Begin(), t.End()); substitutions.emplace_back(range, s); } void AddSubstitution(AST::Node& node, std::string s) { auto range = MakeRange(node.begin, node.end); substitutions.emplace_back(range, s); } bool IsShowSource() const { return !substitutions.empty(); } bool IsDefault() const { return helpMes.empty() && substitutions.empty(); } }; /** * SubDiagnostic attach to diagnostic, like note attach to error or warning. */ struct SubDiagnostic { std::string subDiagMessage; IntegratedString mainHint; std::vector otherHints; DiagHelp help; SubDiagnostic() = delete; explicit SubDiagnostic(std::string s) : subDiagMessage(std::move(s)) { } SubDiagnostic(const Range& range, const std::string& s) : subDiagMessage(s) { mainHint = IntegratedString{range, "", NOTE_COLOR}; } void AddMainHint(const Range& range, const std::string& str) { mainHint = IntegratedString{range, str, NOTE_COLOR}; } void AddHelp(DiagHelp& h) { help = h; } bool IsShowSource() const { return !(mainHint.IsDefault() && otherHints.empty()); } private: void AddMainHint(const Position& pos, const std::string& str) { Range range = MakeRange(pos, pos + 1); AddMainHint(range, str); } void AddMainHint(const Token& tok, const std::string& str) { Range range = MakeRange(tok.Begin(), tok.End()); AddMainHint(range, str); } void AddMainHint(const AST::Node& node, const std::string& str) { Range range = MakeRange(node.begin, node.end); AddMainHint(range, str); } }; /** * Diagnostic contains all diagnostic information. * regular variable name: diagnostic */ class Diagnostic { public: template Diagnostic(const Position s, const Position e, const DiagKind kind, const Args... args) : start(s), end(e), kind(kind), args{args...} { diagSeverity = DiagSeveritys[static_cast(kind)]; diagCategory = GetDiagnoseCategory(kind); warnGroup = warnGroups[static_cast(kind)]; // Refactor kind is set by default. rKind = DiagKindRefactor::parse_diag_begin; }; // This is new diagKind for refactoring, will replace previous DiagKind if all diag have been modified. template Diagnostic(bool refactor, const Range range, const DiagKindRefactor kind, const Args... args) : isRefactor(refactor), rKind(kind) { static_assert(IsAllString, "the diagnostic only support string type argument"); std::vector arguments{args...}; auto errData = errorData[static_cast(kind)]; diagSeverity = rDiagSeveritys[static_cast(kind)]; warnGroup = rWarnGroups[static_cast(kind)]; errorMessage = InsertArguments(errData.message, arguments); if (SEVE_TO_COLOR.find(diagSeverity) != SEVE_TO_COLOR.end()) { mainHint = IntegratedString(range, errData.mainHint, SEVE_TO_COLOR.at(diagSeverity)); } else { mainHint = IntegratedString(range, errData.mainHint, DiagColor::RED); } diagCategory = GetDiagnoseCategory(kind); } Diagnostic() { diagSeverity = DiagSeveritys[static_cast(kind)]; diagCategory = GetDiagnoseCategory(kind); warnGroup = warnGroups[static_cast(kind)]; }; Position start; /**< Diagnostic start position */ Position end; /**< Diagnostic end position */ DiagKind kind{DEFAULT_KIND}; /**< Diagnostic kind */ bool printSourceCode{true}; /**< Whether to print the related source code. */ /// Whether this Diagnostic is created from \ref DiagnoseRefactor bool isRefactor{false}; bool isConvertedToRefactor{false}; /// refactor kind DiagKindRefactor rKind{DiagKindRefactor::lex_diag_begin}; std::string errorMessage; /// 3 | func foo1() : (Float32,String) {} /// | ~~~~~~~~~~~~~~~~ ^^ expected 'Tuple', found 'Unit' /// | | /// | expected 'Tuple' because of return type /// In such a diagnostic, the red message with ^ is called \ref mainHint, and the blue message with ~ is /// called \ref otherHints IntegratedString mainHint; std::vector otherHints; // Stands for refactor notes to differ with notes, need modify after refactor. std::vector subDiags; std::vector helps; std::vector args; DiagSeverity diagSeverity; std::string diagMessage; DiagCategory diagCategory; WarnGroup warnGroup; /// A note often points to another code segment that together with the main diagnostic position to cause the issue. /// For example, such message is a note \ref notes: /// note: the overriden function /// ==> xxx.cj:6:22: /// 6 | public open func foo(): This { /// | ^^^ std::vector notes; Ptr curMacroCall{nullptr}; bool isInMacroCall{false}; // This is API supported to lsp. Will delete after refactoring. bool IsValid() const; // Check if the diagnostic is valid. Position GetBegin(); // Get begin position of the diagnostic. Position GetEnd(); // Get end position of the diagnostic. std::string GetErrorMessage(); // Get error message. DiagCategory GetDiagCategory() const; // Get category. int GetDiagKind() const; void HandleBadOtherHints(); static DiagCategory GetDiagnoseCategory(DiagKind diagKind); static DiagCategory GetDiagnoseCategory(DiagKindRefactor diagKind); static std::string InsertArguments(std::string& rawString, std::vector& arguments); }; enum class DiagHandlerKind : uint8_t { HANDLER, COMPILER_HANDLER, LSP_HANDLER, }; /** * DiagnosticHandler is a abstract base class.It is responsible for handling diagnostic. */ class DiagnosticHandler { public: /** * This is a Diagnostic handling function, it receives @p myDiag from DiagnosticEngine. * @param myDiag The Diagnostic to be handled. */ virtual void HandleDiagnose(Diagnostic& myDiag) { (void)myDiag; } // This two structs are used to filter old style error messages. struct OldHashFunc { size_t operator()(const std::pair& pair) const { static const std::hash hashString{}; size_t stringHash = hashString(pair.second); const Position& pos = pair.first; return stringHash ^ (static_cast(pos.fileID) << 8u) ^ (static_cast(pos.line) << 16u) ^ (static_cast(pos.column) << 24u); } }; struct OldEqualFunc { bool operator()(const std::pair& a, const std::pair& b) const { const Position& p1 = a.first; const Position& p2 = b.first; return p1.fileID == p2.fileID && p1.line == p2.line && p1.column == p2.column && a.second == b.second; } }; virtual void Clear() const { prevDiags.clear(); } void SetPrevDiag(Position pos, std::string str); bool HasPrevDiag(Position pos, std::string str); DiagHandlerKind GetKind() const noexcept { return kind; } friend class DiagnosticEngine; DiagnosticHandler(DiagnosticEngine& d, DiagHandlerKind k) : diag(d), kind(k) { } virtual ~DiagnosticHandler() = default; protected: std::mutex mtx; DiagnosticEngine& diag; const DiagHandlerKind kind{DiagHandlerKind::HANDLER}; mutable std::unordered_set, OldHashFunc, OldEqualFunc> prevDiags; }; /** * CompilerDiagObserver is the default DiagObserver of the compiler, and the diagnostic message will output to stdout or * stderr. */ class CompilerDiagnosticHandler : public DiagnosticHandler { public: explicit CompilerDiagnosticHandler(DiagnosticEngine& diag, bool noC = false, bool json = false) : DiagnosticHandler(diag, DiagHandlerKind::COMPILER_HANDLER), noColor(noC), jsonFormat(json) { } void EmitCategoryDiagnostics(DiagCategory cate); void EmitDiagnoseGroup(); void EmitDiagnosesInJson() noexcept; std::vector GetCategoryDiagnostic( DiagCategory cate) const { auto set = diagnostics[cate]; return std::vector{set.begin(), set.end()}; } struct hashFunc { // Only take the position and severity into account, if it is too restricted. size_t operator()(const Diagnostic& diag) const { return (diag.mainHint.range.Hash() >> 1) ^ (diag.diagSeverity == DiagSeverity::DS_ERROR); } }; struct equalFunc { bool operator()(const Diagnostic& a, const Diagnostic& b) const { return a.mainHint.range == b.mainHint.range && (a.diagSeverity == b.diagSeverity); } }; void Clear() const override { prevDiags.clear(); diagnostics.clear(); } /** * The compiler real behavior after @p diag emitting. Like: error message print to stderr. */ bool SaveCategoryDiagnostic(const Diagnostic& d) { bool success = false; mtx.lock(); if (diagnostics[d.diagCategory].find(d) == diagnostics[d.diagCategory].end()) { diagnostics[d.diagCategory].insert(d); success = true; } mtx.unlock(); return success; } void EmitDiagnose(Diagnostic d); /** * Save all diagnostic to a structure. For deduplication or some tools may need read from it. */ bool SaveDiagnostics(const Diagnostic& d); /** * The current strategy is that diagnostic will not be reported if preceding stage have some errors, but it will * have some improper circumstance if the error has no relationship with previous stage error. * Consider delete this strategy in the future. */ bool CanBeEmitted(const DiagCategory& d); void HandleDiagnose(Diagnostic& d) override; bool IsJsonFormat() const noexcept { return jsonFormat; } void SetOutToStringStream() { outToStringStream = true; } void SetOutToErrStream() { outToStringStream = false; } std::string GetOutString() { return strStream.str(); } void CacheTheCountInJsonFormat(); ~CompilerDiagnosticHandler() override = default; private: bool noColor{false}; bool jsonFormat{false}; mutable std::map> diagnostics; std::list diagsJsonBuff; std::string diagNumJsonBuff; bool outToStringStream{false}; std::ostringstream strStream; /** * Get all diagnostics of the diag category, and they are sorted by range(Sorted by begin position in ascending * order. If the begins are the same, sorted by end position in ascending order.) */ std::vector GetCategoryDiagnosticsSortedByRange(DiagCategory cate) const; }; /** * DiagnosticBuilder is a helper class which can add extra information (like highlight or fix) after Diagnose() and * invoke DiagnosticEngine to notify diagnostic when it deconstructs. */ class DiagnosticBuilder { public: DiagnosticBuilder(DiagnosticEngine& diag, Diagnostic diagnostic); Diagnostic diagnostic; DiagnosticEngine& diag; DiagnosticBuilder(const DiagnosticBuilder& p) = delete; DiagnosticBuilder& operator=(const DiagnosticBuilder& p) = delete; template void AddNote(const Position& pos, DiagKind kind, Args... args) { auto end = pos == DEFAULT_POSITION ? pos : pos + 1; Diagnostic myDiag(pos, end, kind, args...); diagnostic.notes.push_back(myDiag); } template void AddNote(const AST::Node& node, const Position& pos, DiagKind kind, Args... args) { auto begin = node.GetMacroCallPos(pos); auto end = begin == DEFAULT_POSITION ? begin : begin + 1; Diagnostic myDiag(begin, end, kind, args...); diagnostic.notes.push_back(myDiag); } template void AddNote(const AST::Node& node, DiagKind kind, Args... args) { AddNote(node.GetBegin(), kind, args...); } template void AddHint(const Position& pos, Args... args) { static_assert(IsAllString, "args of AddHint in diagnostic builder should all be string."); std::vector arguments{args...}; auto finalPos = pos; if (diagnostic.curMacroCall) { finalPos = diagnostic.curMacroCall->GetMacroCallPos(pos, true); } Range range = MakeRange(finalPos, finalPos + 1); AddHint(range, arguments); } template void AddHint(const Range& range, Args... args) { static_assert(IsAllString, "args of AddHint in diagnostic builder should all be string."); std::vector arguments{args...}; if (diagnostic.curMacroCall) { return AddHint(MakeRange(diagnostic.curMacroCall->GetMacroCallPos(range.begin), diagnostic.curMacroCall->GetMacroCallPos(range.end, true)), arguments); } AddHint(range, arguments); } template void AddHint(const Token& tok, Args... args) { static_assert(IsAllString, "args of AddHint in diagnostic builder should all be string."); std::vector arguments{args...}; Range range = MakeRange(tok.Begin(), tok.End()); AddHint(range, arguments); } template void AddHint(const AST::Node& node, Args... args) { static_assert(IsAllString, "args of AddHint in diagnostic builder should all be string."); std::vector arguments{args...}; AddHint(MakeRange(node.GetBegin(), node.GetEnd()), arguments); } /** * AddHint will insert the mark and hint message both into error. Like: * ''' * ... * `source code` * ~~~~ `hint message` * ... * ''' * */ void AddHint(const Range& range, std::vector& arguments); template void AddMainHintArguments(Args... args) { static_assert(IsAllString, "args of AddHint in diagnostic builder should all be string."); std::vector arguments{args...}; auto insertedStr = Diagnostic::InsertArguments(diagnostic.mainHint.str, arguments); diagnostic.mainHint.str = insertedStr; } void AddNote(const SubDiagnostic& sub); void AddNote(const Range& range, const std::string& note); void AddNote(const AST::Node& node, const std::string& note); void AddNote(const AST::Node& node, const Range& range, const std::string& note); void AddNote(const std::string& note); void AddNote(const Position& pos, const std::string& note); void AddHelp(const DiagHelp& help); ~DiagnosticBuilder(); }; /** * DiagnosticCache is a helper class that caches the stored diags in DiagnosticEngine and restores them later. */ class DiagnosticCache { public: using DiagCacheKey = int32_t; DiagnosticCache() {} /* Remember the diags already in the diags before type check and exclude them later */ void ToExclude(const DiagnosticEngine& diagBefore); void BackUp(const DiagnosticEngine& diagAfter); void Restore(DiagnosticEngine& dst); static DiagCacheKey ExtractKey(const DiagnosticEngine& diag); bool NoError() const; std::vector cachedDiags; }; enum class DiagEngineErrorCode : uint8_t { NO_ERRORS, DIAG_RANGE_ERROR, UNKNOWN }; /** * DiagnosticEngine is main diagnostic processing center. It is responsible for handle diagnostics and emit * diagnostic information. * regular variable name: diag */ class DiagnosticEngine { friend class DiagSuppressor; friend class DiagnosticBuilder; friend class DiagnosticCache; public: bool ignoreScopeCheck{false}; /**< If true, scope related error would be ignored. */ // For each compilation, we only print errors of the first stage that produced errors. // The following variable stores which stage it is, or nullopt if no error is produced. DiagnosticEngine(const DiagnosticEngine& p) = delete; DiagnosticEngine& operator=(const DiagnosticEngine& p) = delete; DiagnosticEngine(); ~DiagnosticEngine() noexcept; ///@{ /// The two api below are used by CJLint and lsp. void SetIsEmitter(bool emitter); void SetDisableWarning(bool dis); ///@} ///@{ /// Getters & setters. bool HasSourceManager(); bool GetIsEmitter() const; void SetIsDumpErrCnt(bool dump); bool GetIsDumpErrCnt() const; void SetSourceManager(SourceManager* sm); SourceManager& GetSourceManager() noexcept; std::lock_guard LockFirstErrorCategory(); const std::optional& FirstErrorCategory() const; int32_t GetDisableDiagDeep() const; const std::vector& GetStoredDiags() const; void SetStoredDiags(std::vector&& value); bool GetEnableDiagnose() const; void EnableDiagnose(const std::vector& diags); std::vector ConsumeStoredDiags(); bool DiagFilter(Diagnostic& diagnostic) noexcept; void AddMacroCallNote(Diagnostic& diagnostic, const AST::Node& node, const Position& pos); // ability of transaction void Prepare(); void Commit(); void ClearTransaction(); // use DiagEngineErrorCode rather than internal error message (for libast) void EnableCheckRangeErrorCodeRatherICE(); void DisableCheckRangeErrorCodeRatherICE(); bool IsCheckRangeErrorCodeRatherICE() const; void SetDiagEngineErrorCode(DiagEngineErrorCode errorCode); ///@{ /** * Diagnose API. Note that new code should use DiagnoseRefactor for more user-friendly message. * @param start The Diagnostic start position. * @param end The Diagnostic end position. * @param kind The Diagnostic kind. * @param args The Diagnostic format arguments. * @return DiagnosticBuilder A temporary local instance which contains Diagnostic. */ template DiagnosticBuilder Diagnose(const Position start, const Position end, DiagKind kind, Args... args) { if (HardDisable()) { return DiagnosticBuilder(*this, Diagnostic{}); } Diagnostic diagnostic(start, end, kind, args...); return DiagnosticBuilder(*this, diagnostic); } /** * Diagnose API. * @param pos The Diagnostic start position. * @param kind The Diagnostic kind. * @param args The Diagnostic format arguments. * @return DiagnosticBuilder A temporary local instance which contains Diagnostic. */ template DiagnosticBuilder Diagnose(const Position pos, DiagKind kind, Args... args) { return Diagnose(pos, pos + 1, kind, args...); } template DiagnosticBuilder Diagnose(const AST::Node& node, const Position pos, DiagKind kind, Args... args) { if (node.isInMacroCall) { auto diagnostic = Diagnostic{}; diagnostic.isInMacroCall = true; return DiagnosticBuilder(*this, diagnostic); } if (node.TestAttr(AST::Attribute::MACRO_EXPANDED_NODE) && node.curMacroCall) { auto begin = node.GetMacroCallPos(pos); Diagnostic diagnostic(begin, begin + 1, kind, args...); AddMacroCallNote(diagnostic, node, pos); return DiagnosticBuilder(*this, diagnostic); } return Diagnose(pos, kind, args...); } template DiagnosticBuilder Diagnose(const AST::Node& node, DiagKind kind, Args... args) { if (node.isInMacroCall) { auto diagnostic = Diagnostic{}; diagnostic.isInMacroCall = true; return DiagnosticBuilder(*this, diagnostic); } // Refactor the Diagnose of the node after the macro expansion. if (node.TestAttr(AST::Attribute::MACRO_EXPANDED_NODE) && node.curMacroCall) { Diagnostic diagnostic(node.GetBegin(), node.GetEnd(), kind, args...); AddMacroCallNote(diagnostic, node, node.begin); return DiagnosticBuilder(*this, diagnostic); } return Diagnose(node.GetBegin(), kind, args...); } DiagnosticBuilder Diagnose(const Diagnostic& diagnostic) { return {*this, diagnostic}; } template DiagnosticBuilder Diagnose(DiagKind kind, Args... args) { if (HardDisable()) { return DiagnosticBuilder(*this, Diagnostic{}); } Diagnostic diagnostic(DEFAULT_POSITION, DEFAULT_POSITION, kind, args...); return DiagnosticBuilder(*this, diagnostic); } ///@} ///@{ /// DiagnoseRefactor issues a diagnose with more user-friendly message than \ref Diagnose. New code should /// always use DiagnoseRefactor. template DiagnosticBuilder DiagnoseRefactor(DiagKindRefactor kind, const Position pos, Args... args) { static_assert(IsAllString, "the diagnose only support string type argument"); // The span of 'range' is left off and right on, like: [begin, end). Range range = MakeRange(pos, pos + 1); Diagnostic diagnostic(true, range, kind, args...); return DiagnosticBuilder(*this, diagnostic); } template DiagnosticBuilder DiagnoseRefactor(DiagKindRefactor kind, const Range range, Args... args) { static_assert(IsAllString, "the diagnose only support string type argument"); CheckRange(Diagnostic::GetDiagnoseCategory(kind), range); Diagnostic diagnostic(true, range, kind, args...); return DiagnosticBuilder(*this, diagnostic); } template DiagnosticBuilder DiagnoseRefactor(DiagKindRefactor kind, const Token& token, Args... args) { static_assert(IsAllString, "the diagnose only support string type argument"); auto range = MakeRange(token.Begin(), token.End()); Diagnostic diagnostic(true, range, kind, args...); return DiagnosticBuilder(*this, diagnostic); } template DiagnosticBuilder DiagnoseRefactor(DiagKindRefactor kind, const AST::Node& node, Args... args) { static_assert(IsAllString, "the diagnose only support string type argument"); auto range = MakeRange(node.GetBegin(), node.GetEnd()); Diagnostic diagnostic(true, range, kind, args...); diagnostic.isInMacroCall = node.isInMacroCall; AddMacroCallNote(diagnostic, node, node.begin); return DiagnosticBuilder(*this, diagnostic); } template DiagnosticBuilder DiagnoseRefactor(DiagKindRefactor kind, const AST::Node& node, const Position pos, Args... args) { static_assert(IsAllString, "the diagnose only support string type argument"); // The span of 'range' is left off and right on, like: [begin, end). auto begin = node.GetMacroCallPos(pos, true); Range range = MakeRange(begin, begin + 1); Diagnostic diagnostic(true, range, kind, args...); diagnostic.isInMacroCall = node.isInMacroCall; AddMacroCallNote(diagnostic, node, pos); return DiagnosticBuilder(*this, diagnostic); } template DiagnosticBuilder DiagnoseRefactor(DiagKindRefactor kind, const AST::Node& node, const Range range, Args... args) { static_assert(IsAllString, "the diagnose only support string type argument"); CheckRange(Diagnostic::GetDiagnoseCategory(kind), range); auto newRange = MakeRealRange(node, range.begin, range.end); Diagnostic diagnostic(true, newRange, kind, args...); diagnostic.isInMacroCall = node.isInMacroCall; AddMacroCallNote(diagnostic, node, range.begin); return DiagnosticBuilder(*this, diagnostic); } template DiagnosticBuilder DiagnoseRefactor(DiagKindRefactor kind, const AST::Node& node, const Token& token, Args... args) { static_assert(IsAllString, "the diagnose only support string type argument"); auto range = MakeRealRange(node, token.Begin(), token.End(), token.kind == TokenKind::END); Diagnostic diagnostic(true, range, kind, args...); diagnostic.isInMacroCall = node.isInMacroCall; AddMacroCallNote(diagnostic, node, token.Begin()); diagnostic.curMacroCall = node.curMacroCall; return DiagnosticBuilder(*this, diagnostic); } ///@} /** * Convert unformat diagnostic message to real diagnostic message. */ void ConvertArgsToDiagMessage(Diagnostic& diagnostic) noexcept; /** * Register diagnostic observer to diagnostic engine. */ void RegisterHandler(std::unique_ptr&& h); void RegisterHandler(DiagFormat format); void IncreaseErrorCount(DiagCategory category); void IncreaseWarningCount(DiagCategory category); void IncreaseErrorCount(); uint64_t GetWarningCount(); uint64_t GetErrorCount(); void IncreaseWarningPrintCount(); unsigned int GetWarningPrintCount() const; void IncreaseErrorPrintCount(); unsigned int GetErrorPrintCount() const; std::optional GetMaxNumOfDiags() const; bool IsSupressedUnusedMain(const Diagnostic& diagnostic) noexcept; void HandleDiagnostic(Diagnostic& diagnostic) noexcept; void EmitCategoryDiagnostics(DiagCategory cate); DiagEngineErrorCode GetCategoryDiagnosticsString(DiagCategory cate, std::string& diagOut); void EmitCategoryGroup(); void SetErrorCountLimit(std::optional errorCountLimit); std::vector GetCategoryDiagnostic(DiagCategory cate); void ClearError(); void Reset(); /** * Set the status of diagnostic engine. * @param enable */ void SetDiagnoseStatus(bool enable); /** * Get the status of diagnostic engine. */ bool GetDiagnoseStatus() const; /** * Report the number of errors and warnings. */ void ReportErrorAndWarningCount(); void DisableScopeCheck() { ignoreScopeCheck = true; } class StashDisableDiagnoseStatus { public: StashDisableDiagnoseStatus(DiagnosticEngine* e, bool hasTargetType); ~StashDisableDiagnoseStatus() noexcept; private: DiagnosticEngine* engine; bool enableDiagnose{true}; int32_t disableDiagDeep = 0; std::vector storedDiags; bool hasTargetType{true}; }; std::unique_ptr AutoStashDisableDiagnoseStatus(bool hasTargetType = false) { return std::make_unique(this, hasTargetType); } private: class DiagnosticEngineImpl* impl; bool HardDisable() const; void CheckRange(DiagCategory cate, const Range& range); Range MakeRealRange( const AST::Node& node, const Position begin, const Position end, bool begLowBound = false) const; std::vector DisableDiagnose(); void EnableDiagnose(); }; } // namespace Cangjie #endif // CANGJIE_BASIC_DIAGNOSTICENGINE_H cangjie_compiler-1.0.7/include/cangjie/Basic/DiagnosticJsonFormatter.h000066400000000000000000000050571510705540100260350ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Diagnostic Json Formatter class, which format diagnostic info to json string. */ #ifndef CANGJIE_DIAGNOSTICJSONFORMATTER_H #define CANGJIE_DIAGNOSTICJSONFORMATTER_H #include #include #include #include "cangjie/Basic/DiagnosticEngine.h" namespace Cangjie { /// Print diagnostic as json. Used when --diagnostic-format=json enabled. class DiagnosticJsonFormatter { public: explicit DiagnosticJsonFormatter(DiagnosticEngine& diag) : diag(diag) { } static std::string AssembleDiagnosticJsonString( const std::list& diagsJsonBuff, const std::string& numJsonBuff); std::string FormatDiagnosticToJson(const Diagnostic& d, size_t deep = DIAG_JSON_DEEP); std::string FormatDiagnosticCountToJsonString(); DiagnosticJsonFormatter(const DiagnosticJsonFormatter&) = delete; DiagnosticJsonFormatter& operator=(const DiagnosticJsonFormatter&) = delete; DiagnosticJsonFormatter(const DiagnosticJsonFormatter&&) = delete; DiagnosticJsonFormatter& operator=(const DiagnosticJsonFormatter&&) = delete; private: static constexpr size_t DIAG_JSON_DEEP = 3; DiagnosticEngine& diag; inline static std::string Whitespace(size_t num) { return std::string(num, ' '); } static std::string AssembleDiagnosticsJsonString(const std::list& diagsJsonBuff); std::string FormatDiagnosticMainContentJson(size_t deep, const Diagnostic& d); std::string FormatDiagnosticNotesJson(size_t deep, const std::vector& subDiags); std::string FormatDiagnosticHelpsJson(size_t deep, const std::vector& helps); std::string FormatDiagnosticSubDiagJson(size_t deep, const SubDiagnostic& subDiag); std::string FormatDiagnosticDiagHelpJson(size_t deep, const DiagHelp& help, bool newLine = true); std::string FormatHintJson(size_t deep, const IntegratedString& hint, bool newLine = true); std::string FormatSubstitutionJson(size_t deep, const Substitution& substitution); std::string FormatHintsJson(size_t deep, const std::vector& hints); std::string FormatRangeJson(size_t deep, const Range& range); std::string FormatPostionJson(size_t deep, const Position& pos); }; } // namespace Cangjie #endif // CANGJIE_DIAGNOSTICJSONFORMATTER_Hcangjie_compiler-1.0.7/include/cangjie/Basic/DiagnosticMacroExpand.def000066400000000000000000000075271510705540100257540ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. ERROR(macro_expand_diag_begin, "") ERROR(macro_undeclared_identifier, "undeclared identifier '%s'") ERROR(macro_expect_attributed_macro, "expect an attributed macro '%s', but find a plain one") ERROR(macro_evaluate_failed, "macro evaluation has failed for macro call '%s'") ERROR(macro_expect_macro_definition, "expect macro defintion '%s', but found another Declareation") ERROR(macro_undefined_pkg_name, "undefined macro package '%s', expect a right package name for macro") ERROR(macro_expect_plain_macro, "expect a plain macro '%s', but find an attributed one") ERROR(macro_expand_invalid_attr_tokens, " illegal attribute tokens in macro call, which is '%s'") ERROR(macro_expand_invalid_input_tokens, " illegal input tokens in macro call") ERROR(macro_expand_invalid_escape, " illegal escape in macro call") ERROR(macro_unexpect_def_and_call_in_same_pkg, "macro's call and definition can not in one package") ERROR(macro_init_interpter_faild, "initialization of interpreter for macro expansion failed") ERROR(macro_expand_invalid_node_replace, "invalid node expanded in macro call's father node") ERROR(macro_ambiguous_match, "ambiguous match for macro call %s") ERROR(macro_using_error, "macro using error, '%s'") ERROR(macro_expand_failed, "macro expansion failed for macro call '%s'") ERROR(not_find_macro_library, "not find macro definition library path") ERROR(can_not_open_macro_library, "Cannot dlopen from the dynamic library '%s' for macro") ERROR(macro_cannot_find_method, "Cannot find method from dynamic libs for macro call '%s'") ERROR(macro_expect_decl_not_enum_constructor, "expected declaration, but found enum constructor %s") ERROR(macro_expect_declaration, "expected declaration, but found other node") ERROR(macro_expect_enum_constructor, "expected an enum constructor") ERROR(macro_expect_one_enum_constructor, "expected one enum constructor, but found more than one node") ERROR(macro_expect_one_expr_or_pattern, "expected one expr or pattern, but found more than one node") ERROR(macro_expect_one_expr, "expected one expr, but found exprs or other node") ERROR(macro_unexpect_no_expr, "expected one expr, but not found expr") ERROR(macro_call_map_to_empty_value_token, "Cannot handle result of macro call: token '%s' is empty") ERROR(macro_call_map_info_failed, "Cannot map information for macro call due to token '%s' mismatch with '%s'") ERROR(macro_call_save_file_failed, "Failed to save results of macro call to file") ERROR(macro_expand_code_should_not_have_macrocall, "Code generated by macro '%s' should not have macro call.") ERROR(macro_expand_cannot_find_dependency, "cannot find BCHIR file for macro package dependency %s.") ERROR(macro_expand_exception_occurred, "exception occurred during macro expansion.") ERROR(macro_expand_failed_init_top_level, "Interpreter failed when initializing top-level.") ERROR(macro_named_parameter_after_unnamed, "unnamed parameters must come before named parameters.") ERROR(macro_unexpected_empty_parameter, "unexpected empty tokens after the macro expansion for the func parameters.") ERROR(macro_assert_parent_context_failed, "The macro call '%s' should with the surround code contains a call '%s'.") ERROR(macro_build_in_unexpect_params, "The build-in macro '%s' expected an empty parameter, but found non-empty one.") ERROR(macro_build_in_unexpect_params_attrs, "The build-in macro '%s' is a plain macro, but find an attributed one.") ERROR(macro_is_deprecated_error, "macro '%s' is deprecated%s%s") WARNING(macro_is_deprecated_warning, DEPRECATED, "macro '%s' is deprecated%s%s") ERROR(macro_expand_atexcl, "macro expansion cannot be prefixed with '@!'") ERROR(macro_expand_diag_end, "") cangjie_compiler-1.0.7/include/cangjie/Basic/DiagnosticSema.def000066400000000000000000000547411510705540100244400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. ERROR(sema_diag_begin, "") ERROR(sema_diag_report_error_message, "%s") NOTE(sema_diag_report_note_message, "%s") ERROR(sema_incompatible_expo_target_type, "the type of an exponentiation expression is either 'Int64' or 'Float64', which conflicts the type '%s' required " "by the context") ERROR(sema_ambiguous_expo_right_operand_type, "both 'Int64' and 'Float64' is compatible with the right operand's type of this exponentiation (assignment) " "expression; please give an explicit one") ERROR(sema_not_a_type, "'%s' is not a type") ERROR(sema_incompatible_func_body_and_return_type, "the return type of this function cannot be calculated from the function body and all the return expressions") ERROR(sema_type_must_toplevel, "%s type must be toplevel") ERROR(sema_undeclared_type_name, "undeclared type name '%s'") ERROR(sema_undeclared_identifier, "undeclared identifier '%s'") ERROR(sema_redefinition, "redefinition of declaration '%s'") ERROR(sema_expand_macro_redefinition, "redefinition of macro '%s'") NOTE(sema_previous_decl, "'%s' is previously declared here") ERROR(sema_used_before_initialization, "variable '%s' is used before initialization") ERROR(sema_global_var_used_before_initialization, "global/static variable '%s' is used before initialization during initializing '%s'") ERROR(sema_duplicated_item_in_enum, "'%s' is already exist in enum '%s'") ERROR(sema_invalid_constructor_in_enum, "invalid constructor in enum declaration") ERROR(sema_multiple_constructor_in_enum, "find multiple constructor '%s' of enum declaration") ERROR(sema_enum_constructor_type_not_match, "no matching enum constructor '%s' for given arguments") ERROR(sema_invalid_enum_member_access, "base of member access can not be enum variable") ERROR(sema_invalid_type_param_of_enum_member_access, "type arguments cannot appear after '%s' when enum type '%s' is given") ERROR(sema_invalid_loop_control, "'break' or 'continue' must be used inside a loop") ERROR(sema_cannot_currying, "%s cannot have more than one parameter list") ERROR(sema_value_type_recursive, "value type recursive detected: '%s'") ERROR(sema_inheritance_cycle, "inheritance cycle detected: '%s'") ERROR(sema_typealias_cycle, "type cycle detected: '%s'") ERROR(sema_typealias_external_refer_internal, "'%s' type '%s' refers to '%s' type '%s'") ERROR(sema_invalid_coalescing, "type of left operand does not support coalescing operation. coalescing is only valid for 'Option'") ERROR(sema_div_zero, "division by 0") ERROR(sema_mod_zero, "mod by 0") ERROR(sema_arithmetical_op_overflow, "overflow in '%s' calculation") ERROR(sema_shift_count_overflow, "shift count overflow") ERROR(sema_negative_shift_count, "shift count is negative") ERROR(sema_func_capture_var_cannot_assign, "%s captured a mutable variable %s, %s cannot be assigned to a variable") ERROR(sema_func_capture_var_cannot_return, "%s captured a mutable variable %s, %s cannot be used as a return value") ERROR(sema_func_capture_var_cannot_param, "%s captured a mutable variable %s, %s cannot be used as a param") ERROR(sema_func_capture_var_cannot_expr, "%s captured a mutable variable %s, %s cannot be used as a expression") ERROR(sema_invalid_unary_expr, "invalid unary operator '%s' on type '%s'") NOTE(sema_invalid_unary_expr_note, "you may want to implement operator func %s() for type '%s'") ERROR(sema_fail_flow_expr_operand_has_named_param, "flow operand cannot contain named parameter") ERROR(sema_operator_overload_invalid_num_parameter, "invalid number of parameters for operator '%s'") ERROR(sema_operator_overload_built_in_unary_operator, "operator func %s() of type %s is a built-in function and cannot be overridden") ERROR(sema_operator_overload_built_in_binary_operator, "operator func %s(%s) of type %s is a built-in function and cannot be overridden") ERROR(sema_operator_overload_can_not_has_default_param, "optional parameter can not be used in operator overload function") ERROR(sema_empty_arrayLit_type_undefined, "array literal type cannot be inferred") ERROR(sema_inconsistency_elemType, "inconsistent element type for %s literal") ERROR(sema_tuple_pattern_not_match, "%s isn't a tuple to match tuple pattern") ERROR(sema_unsupport_operator, "not supported operator: '%s'") ERROR(sema_array_size_type_error, "array size must be of type Int64") ERROR(sema_array_expression_param_type_error, "array init expression parameter's type must be type Int64") ERROR(sema_array_expression_type_error, "array init expression must has type (Int64)->T") ERROR(sema_array_single_element_type_error, "array init with single element must be subtype of 'List' or 'Collection'") ERROR(sema_array_element_type_error, "array initialize element type error") ERROR(sema_array_too_much_argument, "too much arguments given for array constructor, only accept 0~2 arguments") ERROR(sema_array_first_arg_cannot_be_named, "array's first argument cannot be named argument") ERROR(sema_array_second_arg_cannot_be_named, "array's second argument cannot be named argument when type is (Int64)->T") ERROR(sema_array_second_wrong_named_arg, "array's second argument must have named prefix 'repeat:' when type is T") ERROR(sema_pointer_too_much_argument, "too much arguments given for CPointer constructor, only accept 0~1 arguments") ERROR(sema_cfunc_too_many_arguments, "too many arguments given to CFunc constructor, only accept 1 argument") ERROR( sema_pointer_single_element_type_error, "the single argument of CPointer constructor must be 'CPointer' or 'CFunc'") ERROR(sema_pointer_unknow_generic_type, "'CPointer' generic type cannot be inferred") ERROR(sema_builtin_invalid_index, "%s index must be an integer literal") ERROR(sema_builtin_index_in_bound, "%s index must be in bounds") ERROR(sema_tuple_element_cmp_not_bool, "the '%s' operation between type '%s' and type '%s' is not evaluated to a Bool") ERROR(sema_tuple_cmp_not_supported, "operator '%s' between tuple type '%s' and '%s' is not supported") ERROR(sema_step_non_zero_range, "step cannot be zero in range expression") ERROR(sema_inconsistency_range_elemType, "start and stop must be of the same type in range expression") ERROR(sema_range_step_not_int64, "step must be Int64 in range expression") ERROR(sema_no_match_function_declaration_for_call, "no matching function declaration for function call '%s'") ERROR(sema_no_match_function_declaration_for_ref, "no matching function declaration for function reference '%s'") ERROR(sema_no_match_constructor, "no matching constructor for call '%s'") ERROR(sema_ambiguous_arg_type, "ambiguous arguments type in call expression") ERROR(sema_ambiguous_match, "ambiguous match for function call '%s'") ERROR(sema_ambiguous_constructor_match, "ambiguous match for constructor call '%s'") ERROR(sema_ambiguous_func_ref, "ambiguous match for reference '%s'") ERROR(sema_mismatched_type_for_pattern_in_vardecl, "the pattern in this variable declaration can not match its type") ERROR(sema_parameters_and_arguments_mismatch, "parameters and arguments mismatch") ERROR(sema_cstruct_cannot_autobox, "struct with @C cannot implicitly used as '%s'") ERROR(sema_unit_cannot_as_cfunc_arg, "Unit cannot be used as argument type of CFunc") ERROR(sema_overload_conflicts, "%s '%s' has overload conflicts") ERROR(sema_no_match_operator_function_call, "no matching function for operator '()' function call") ERROR(sema_pattern_can_not_be_assigned, "the pattern isn't irrefutable pattern and it can not be initialized") ERROR(sema_unknown_named_argument, "unknown named argument prefix '%s:'") ERROR(sema_multiple_named_argument, "named argument prefix '%s:' cannot appeared more than once in call") ERROR(sema_invalid_named_arguments, "invalid named arguments prefix '%s:', target is not a named parameter") ERROR(sema_unsupport_named_argument, "named argument cannot be used in variable function call") ERROR(sema_pattern_literal_expected, "only const literal is allowed in const pattern") ERROR(sema_pattern_not_match, "%s pattern is not matched") ERROR(sema_not_overload_in_match, "no overloaded '==' function in match case pattern") ERROR(sema_type_incompatible, "type incompatible in this %s") ERROR(sema_subscript_set_not_supported, "type %s does not have operator func [](index, value) for index type %s") ERROR(sema_subscript_get_set_not_supported, "type %s does not have both operator func [](index) and operator func [](index, value) for index type %s") ERROR(sema_tuple_pattern_with_correct_size_expected, "tuple pattern with correct size expected") ERROR(sema_enum_pattern_param_size_error, "enum pattern's parameters size is wrong") ERROR(sema_match_case_must_have_default, "at least one default case, such as wildcard pattern, variable pattern or '[...]' for sequence pattern in match " "case.") ERROR(sema_match_case_has_no_type, "this match case has no type") ERROR(sema_package_internal_decl_obtain_illegal, "%s '%s' in package '%s' cannot be obtained") ERROR(sema_package_name_conflict, "package name '%s' is conflicted with other imported package name, please use 'as' to eliminate conflict.") ERROR(sema_invalid_access_control, "can not access field '%s'") ERROR(sema_invalid_access_function, "can not access function '%s'") ERROR(sema_invalid_file_hash, "There is invalid file hash in access control check") ERROR(sema_func_no_override_or_redefine_modifier, "do not need '%s' modifier for %s '%s'") ERROR(sema_unexpected_param_for_entry, "'main' cannot be defined with parameter whose type is not 'Array'") ERROR(sema_unexpected_return_type_for_entry, "return type of 'main' is not 'Integer' or 'Unit'") ERROR(sema_redefinition_entry, "multiple 'main's are found in source files") ERROR(sema_missing_entry, "'main' is missing") ERROR(sema_numeric_convert_must_be_numeric, "the expression for numeric type conversion must have a numeric type") ERROR(sema_cfunc_ctor_must_be_cpointer, "argument type of 'CFunc' constructor must be of type 'CPointer'") ERROR(sema_ref_not_be_type, "expected member name or constructor call after '%s' type name") ERROR( sema_expr_in_forin_must_has_iterator, "the type %s of expression in for-in expression does not implement Iterator") ERROR(sema_forin_pattern_must_be_irrefutable, "the pattern in for-in expression must be irrefutable") ERROR(sema_wrong_forin_guard, "pattern guard should be Boolean type") ERROR(sema_generics_type_variable_not_defined, "generics type variable '%s' has not defined") ERROR(sema_generic_type_argument_not_match_constraint, "generics type arguments do not match the constraint of '%s'") NOTE(sema_which_constraint_not_match, "'%s' is not a subtype of %s") ERROR(sema_generic_type_without_type_argument, "generic type should be used with type argument") ERROR(sema_non_generic_function_with_type_argument, "non-generic function should not be used with type argument") ERROR(sema_throw_expr_with_wrong_type, "the object thrown must derive from `core.Exception`") ERROR(sema_except_catch_type_error, "the exception catch type must be class and extends from core.Exception or core.Error") ERROR(sema_no_core_object, "`core` package should be imported") ERROR(sema_invalid_intrinsic_decl, "intrinsic function '%s' cannot be declared in '%s' package") ERROR(sema_generic_infinite_instantiation, "generic infinite instantiation") ERROR(sema_forbid_generic_nonstatic_method, "non-static generic member function '%s' is not supported") ERROR(sema_forbid_generic_constructor, "generic constructor '%s' is not supported") ERROR(sema_forbid_generic_finalizer, "generic finalizer '%s' is not supported") ERROR(sema_import_not_in_current_module, "this package does not belong to the current module, please write its module name explicitly.") ERROR(sema_flow_expressions_use_this_or_super, "'%s' is not allowed to be used in flow expressions") ERROR(sema_symbol_not_collected, "reference node named '%s' is not collected in symbol table.") ERROR(sema_cannot_convert_literal, "cannot convert %s literal to type '%s'") ERROR(sema_cannot_have_parameter, "%s cannot have parameter") // C FFI Sema Prepare Check. ERROR(sema_only_cfunc_can_use_annotation, "only CFunc can use '%s'") ERROR(sema_annotation_error_arg_num, "'%s' should have %s arg") ERROR(sema_annotation_calling_conv_not_support, "'@CallingConv' have not support '%s' yet") ERROR(sema_annotation_invalid_args_type, "'%s' arg should be right type") ERROR(sema_unexpected_wrapper, "unexpected %s") ERROR(sema_native_var_error, "variable can not be modified with 'foreign' and implicit @C.") // C FFI Sema Check. ERROR(sema_illegal_scope_use_of_annotation, "'%s' can only be used in top-level scope") ERROR(sema_illegal_use_of_annotation, "%s cannot be modified with '%s'") ERROR(sema_cffi_cannot_have_type_param, "%s cannot have type parameters") ERROR(sema_unsafe_function_invoke_failed, "unsafe function or native function should be invoked in unsafe context.") ERROR(sema_func_capture_var_not_ctype, "captured variable mustn't be struct with @C") ERROR(sema_cfunc_cannot_capture_var, "cannot capture variable %s in CFunc lambda expression") ERROR(sema_cfunc_cannot_capture_this, "'%s' is not allowed to be captured in CFunc lambda expression") ERROR(sema_illegal_ctype_generic_argument, "generic argument mustn't be struct with @C") ERROR(sema_illegal_cpointer_generic_type, "generic type of CPointer must be instantiated with CType") ERROR(sema_invalid_cfunc_arg_type, "arguments type of CFunc must be instantiated with CType") ERROR(sema_cfunc_type, "cfunc type must be a function type") ERROR(sema_invalid_tuple_field_ctype, "tuple member mustn't be struct with @C") ERROR(sema_enum_pattern_func_cty_error, "member func '%s' is forbidden in enum '%s' with @C") ERROR(sema_enum_pattern_func_param_cty_error, "member func '%s' of enum '%s' mustn't has struct with @C") ERROR(sema_illegal_ctype_member, "non-static member variable '%s' of %s '%s' cannot be struct with @C") ERROR(sema_illegal_member_of_cstruct, "member variable '%s' of struct '%s' with @C must be instantiated with CType") ERROR(sema_cfunc_cannot_have_unit_args, "CFunc cannot have arguments of type Unit") ERROR(sema_cstruct_cannot_have_unit_fields, "member variables cannot be type Unit in struct with @C") ERROR(sema_cfunc_cannot_have_named_args, "CFunc cannot have named arguments") ERROR(sema_cfunc_var_cannot_have_var_param, "CFunc with variable-length parameters cannot be assigned to variables") // class / interface / struct ERROR(sema_inheritance_non_ref_type, "inheritance is not a ref type: '%s'") ERROR(sema_class_uninitialized_field, "the uninitialized member variable '%s' is not initialized in the constructor of class or struct") ERROR(sema_this_or_super_not_allowed_to_initialize_non_static_member, "'%s' is not allowed to initialize non-static member") ERROR(sema_this_or_super_not_allowed_to_initialize_static_member, "'%s' is not allowed to initialize static member") ERROR(sema_illegal_usage_of_super_member, "super member '%s' is not allowed to be used before calling super()") ERROR(sema_illegal_usage_of_member, "'%s' is not allowed to be accessed before all member variables are initialized") ERROR(sema_invalid_assignment_to_this_expr, "cannot assign a value to 'this'") ERROR(sema_invalid_override_member_in_class, "cannot override non-abstract %s '%s' with abstract %s") ERROR(sema_invalid_override_or_redefine_member_in_interface, "cannot override implemented interface %s '%s' with abstract %s") ERROR(sema_illegal_this_in_interface, "'this' can not be used in interface") ERROR(sema_use_super_in_interface, "'super' cannot be used in interface") ERROR(sema_super_use_error_inside_non_class, "'super' can only be used in class") ERROR(sema_assignment_of_member_variable_cannot_use_this_or_super, "'%s' is not allowed to be used in %s") ERROR(sema_illegal_super_alone, "invalid super expression, it can only be used on the left-hand side of a dot") ERROR(sema_abstract_class_can_not_be_instantiated, "abstract class '%s' can not be instantiated") ERROR(sema_interface_can_not_be_instantiated, "interface '%s' can not be instantiated") ERROR(sema_non_inheritable_super_class, "super class '%s' is not inheritable") ERROR(sema_superclass_must_be_placed_at_first, "super class '%s' must be placed at the beginning of supertype list") ERROR(sema_illegal_multi_inheritance, "only one super class may appear in supertype list of class '%s'") ERROR(sema_invalid_this_call_outside_ctor, "invalid calling '%s' outside the constructor") ERROR(sema_privated_abstract_func_in_class, "private abstract %s is forbidden in abstract class '%s'") ERROR(sema_invalid_string_implementation, "the type '%s' should implement interface 'ToString' in the 'core' package") ERROR(sema_invalid_tokens_implementation, "the type '%s' should implement interface 'ToTokens' in the 'ast' package") ERROR(sema_multiple_primary_constructors, "%s '%s' cannot have more than one primary constructor") ERROR(sema_interface_is_not_inheritable, "'%s' interface is not able to be inherited") ERROR(sema_interface_is_not_implementable, "'%s' interface is not able to be implemented explicitly") ERROR(sema_inherit_duplicate_interface, "%s '%s' inherits or implements duplicate interface '%s'") ERROR(sema_return_unit, "return expressions in a constructor must be either 'return' or 'return ()'") ERROR(sema_illegal_place_of_calling_this_or_super, "call to '%s' must be first expression in constructor of %s '%s'") ERROR(sema_illegal_place_of_calling_this_primary_constructor, "invalid calling 'this' in primary constructor ") ERROR(sema_this_super_use_error_outside_class, "'%s' cannot be used outside class or struct or interface") ERROR(sema_missing_func_body, "%s '%s' can not be abstract") ERROR(sema_interface_member_must_be_implemented, "interface %s '%s' must be implemented in '%s'") ERROR(sema_interface_member_must_be_implemented_in_struct, "interface %s '%s' must be implemented in '%s'") ERROR(sema_member_variable_can_not_shadow, "the variable '%s' must not shadow a member variable of the supertype") ERROR(sema_missing_overridden_func, "'override' %s '%s' does not have an overridden %s in its supertype") ERROR(sema_missing_redefined_func, "'redef' %s '%s' does not have a redefined 'static' %s in its supertype") ERROR(sema_static_and_non_static_member_cannot_have_same_name, "%s member '%s' cannot have the same name with %s member in %s") ERROR(sema_generic_member_type_argument_different, " type argument of function '%s' is different in parent class or interfaces") ERROR(sema_c_type_cannot_implement_interface, "c type '%s' cannot implement interface") ERROR(sema_illegal_access_non_static_member, "'%s' is non-static member, cannot access by type name") ERROR(sema_illegal_access_interface_field, "field '%s' cannot be accessed without interface name '%s'") ERROR(sema_illegal_access_inner_classlike, "inner %s '%s' cannot be accessed without name '%s'") ERROR(sema_cannot_modify_var, "instance member variable '%s' cannot be modified in immutable function") ERROR(sema_illegal_capture_this, "'this' is not allowed to be captured in constructor of %s") ERROR(sema_object_cannot_access_static_member, "object cannot access static member '%s'") ERROR(sema_abstract_method_cannot_be_accessed_directly, "abstract method '%s' cannot be accessed directly") ERROR(sema_return_type_invariance, "return type of '%s' can only be class/interface type which implements or inherits the interface type '%s'") ERROR(sema_static_members_cannot_call_members, "non-static variable '%s' cannot be referenced from a static context") ERROR(sema_class_inherit_non_class_nor_interface, "class '%s' can only inherit a class or implement interfaces") ERROR(sema_type_implement_non_interface, "%s '%s' can only implement interface") ERROR(sema_interface_inherit_non_interface, "interface '%s' can only inherit interface") ERROR(sema_generic_in_operator_overload, "generic is not allowed in operator overload function") ERROR(sema_illegal_this_outside_struct_constructor, "'this' is only allowed to be used inside constructor or function for struct") ERROR(sema_static_function_cannot_access_non_static_member, "'%s' is non-static member, cannot be accessed by static function '%s'") ERROR(sema_static_variable_cannot_access_non_static_member, "'%s' is non-static member, cannot be accessed by static variable") ERROR(sema_static_lambdaExpr_cannot_access_non_static, "invalid use of non-static member '%s' in static lambda expression") ERROR(sema_redef_modify_static_func, "'redef' cannot be used to modify an instance '%s'") ERROR(sema_illegal_member_used_in_open_constructor, "instance member %s '%s' cannot be accessed in the constructor of open class '%s'") ERROR(sema_finalizer_forbidden_in_class, "finalizer is forbidden in class '%s' that is %s") // expose ERROR(sema_generic_no_member_match_in_upper_bounds, "no member match for generic member access when searching in upper bounds") ERROR(sema_generic_ambiguous_method_match_in_upper_bounds, "ambiguous method '%s' match for generic member access function call when searching in upper bounds") ERROR(sema_generic_no_method_match_in_upper_bounds, "no method '%s' match for generic member access function call when searching in upper bounds") ERROR(sema_cannot_instantiated_by_incomplete_type, "can not instantiate '%s' by %s for it has unimplemented static member") ERROR(sema_invalid_field_expose_access, "'%s' is not a static member of %s '%s'") // mut ERROR(sema_capture_this_or_instance_field_in_func, "'%s' cannot be captured in the %s") ERROR(sema_use_this_as_an_expression_in_func, "'this' cannot be used as an expression in the %s") ERROR(sema_incompatible_mut_modifier_between_struct_and_interface, "'mut' modifier of '%s' is incompatible with that in interface '%s'") ERROR(sema_immutable_function_cannot_access_mutable_function, "immutable function '%s' cannot access mutable function '%s'") // This ERROR(sema_invalid_position_of_this_type, "'This' type is not allowed here") // Property ERROR(sema_property_override_implement_type_diff, "The type of the override/implement property must be the same") // WARNING(typealias_unused_type_parameters, UNUSED, "type arg(s) %s are not used") WARNING( sema_capture_has_shadow_variable, SEMANTICS, "the variable '%s' actually captures this decl %p, can not captures the decl %p") WARNING(sema_useless_exception_type, UNUSED, "useless exception type") WARNING(sema_ignore_open, UNUSED, "the current member should not have 'open' modifier because it is in a non-inheritable class") NOTE(sema_found_candidate_decl, "found candidate") NOTE(sema_found_possible_candidate_decl, "found possible candidate") // Do not add diagnostic information below this information. ERROR(sema_diag_end, "") cangjie_compiler-1.0.7/include/cangjie/Basic/DiagnosticsAll.def000066400000000000000000000006201510705540100244310ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/Basic/DiagnosticCHIR.def" #include "cangjie/Basic/DiagnosticMacroExpand.def" #include "cangjie/Basic/DiagnosticSema.def" cangjie_compiler-1.0.7/include/cangjie/Basic/Display.h000066400000000000000000000064201510705540100226330ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file converts utf-8 string to the displayed column width of unicode as follows: * * - The null character (U+0000) has a column width of 0. * * - Other C0/C1 control characters and DEL will lead to a return value of -1. * * - Non-spacing and enclosing combining characters (general category code Mn or Me in the Unicode database) have a * column width of 0. * * - SOFT HYPHEN (U+00AD) has a column width of 1. * * - Other format characters (general category code Cf in the Unicode database) and ZERO WIDTH SPACE (U+200B) * have a column width of 0. * * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) have a column width of 0. * * - Spacing characters in the East Asian Wide (W) or East Asian Full-width (F) category as defined in Unicode * Technical Report #11 have a column width of 2. * * - All remaining characters (including all printable ISO 8859-1 and WGL4 characters, Unicode control characters, * etc.) have a column width of 1. */ #ifndef CANGJIE_BASIC_DISPLAY_H #define CANGJIE_BASIC_DISPLAY_H #include #include #include namespace Cangjie { static const size_t NORMAL_CODEPOINT_LEN = 4; static const size_t HORIZONTAL_TAB_LEN = 4; static const uint8_t ASCII_BASE = 127; /// Characters that need escaped when print to console. static std::unordered_map escapePrintMap = { {'\b', "\\b"}, {'\t', "\\t"}, {'\n', "\\n"}, {'\v', "\\v"}, {'\f', "\\f"}, {'\r', "\\r"} }; /// Convert arithmetic value to hex string with length. All letters returned are in uppercase. template std::string ToHexString(T w, size_t len = sizeof(T) >> 1) { static_assert(std::is_arithmetic>::value, "only support converting arithmetic value to hex"); static const std::string digits("0123456789ABCDEF"); std::string ret(len, '0'); for (size_t i = 0, j = (len - 1) * NORMAL_CODEPOINT_LEN; i < len; ++i, j -= NORMAL_CODEPOINT_LEN) { ret[i] = digits[(w >> j) & 0x0f]; } return ret; } inline std::string ToBinaryString(uint8_t num) { const static int bStringLen = 8; return "0b" + std::bitset(num).to_string(); } std::basic_string UTF8ToChar32(const std::string& str); std::string Char32ToUTF8(const char32_t& str); std::string Char32ToUTF8(const std::basic_string& str); /// Returns a string of spaces, with length at least enough to fill content[0..column-1] using unicode DisplayWidth. std::string GetSpaceBeforeTarget(const std::string& content, int column); /// Convert the input Unicode scalar point \ref ch into a string to be printed in diagnostic message. std::string ConvertChar(const int32_t& ch); std::string ConvertUnicode(const int32_t& str); ///@{ /// Get unicode display width, that is how many spaces it takes to render them in console. /// Used by fmt. size_t DisplayWidth(const std::basic_string& pwcs); size_t DisplayWidth(const std::string& str) noexcept; ///@} } #endif // CANGJIE_BASIC_DISPLAY_H cangjie_compiler-1.0.7/include/cangjie/Basic/DisplayColumn.def000066400000000000000000000063041510705540100243210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. BIND(0x0300, 0x036F) BIND(0x0483, 0x0486) BIND(0x0488, 0x0489) BIND(0x0591, 0x05BD) BIND(0x05BF, 0x05BF) BIND(0x05C1, 0x05C2) BIND(0x05C4, 0x05C5) BIND(0x05C7, 0x05C7) BIND(0x0600, 0x0603) BIND(0x0610, 0x0615) BIND(0x064B, 0x065E) BIND(0x0670, 0x0670) BIND(0x06D6, 0x06E4) BIND(0x06E7, 0x06E8) BIND(0x06EA, 0x06ED) BIND(0x070F, 0x070F) BIND(0x0711, 0x0711) BIND(0x0730, 0x074A) BIND(0x07A6, 0x07B0) BIND(0x07EB, 0x07F3) BIND(0x0901, 0x0902) BIND(0x093C, 0x093C) BIND(0x0941, 0x0948) BIND(0x094D, 0x094D) BIND(0x0951, 0x0954) BIND(0x0962, 0x0963) BIND(0x0981, 0x0981) BIND(0x09BC, 0x09BC) BIND(0x09C1, 0x09C4) BIND(0x09CD, 0x09CD) BIND(0x09E2, 0x09E3) BIND(0x0A01, 0x0A02) BIND(0x0A3C, 0x0A3C) BIND(0x0A41, 0x0A42) BIND(0x0A47, 0x0A48) BIND(0x0A4B, 0x0A4D) BIND(0x0A70, 0x0A71) BIND(0x0A81, 0x0A82) BIND(0x0ABC, 0x0ABC) BIND(0x0AC1, 0x0AC5) BIND(0x0AC7, 0x0AC8) BIND(0x0ACD, 0x0ACD) BIND(0x0AE2, 0x0AE3) BIND(0x0B01, 0x0B01) BIND(0x0B3C, 0x0B3C) BIND(0x0B3F, 0x0B3F) BIND(0x0B41, 0x0B43) BIND(0x0B4D, 0x0B4D) BIND(0x0B56, 0x0B56) BIND(0x0B82, 0x0B82) BIND(0x0BC0, 0x0BC0) BIND(0x0BCD, 0x0BCD) BIND(0x0C3E, 0x0C40) BIND(0x0C46, 0x0C48) BIND(0x0C4A, 0x0C4D) BIND(0x0C55, 0x0C56) BIND(0x0CBC, 0x0CBC) BIND(0x0CBF, 0x0CBF) BIND(0x0CC6, 0x0CC6) BIND(0x0CCC, 0x0CCD) BIND(0x0CE2, 0x0CE3) BIND(0x0D41, 0x0D43) BIND(0x0D4D, 0x0D4D) BIND(0x0DCA, 0x0DCA) BIND(0x0DD2, 0x0DD4) BIND(0x0DD6, 0x0DD6) BIND(0x0E31, 0x0E31) BIND(0x0E34, 0x0E3A) BIND(0x0E47, 0x0E4E) BIND(0x0EB1, 0x0EB1) BIND(0x0EB4, 0x0EB9) BIND(0x0EBB, 0x0EBC) BIND(0x0EC8, 0x0ECD) BIND(0x0F18, 0x0F19) BIND(0x0F35, 0x0F35) BIND(0x0F37, 0x0F37) BIND(0x0F39, 0x0F39) BIND(0x0F71, 0x0F7E) BIND(0x0F80, 0x0F84) BIND(0x0F86, 0x0F87) BIND(0x0F90, 0x0F97) BIND(0x0F99, 0x0FBC) BIND(0x0FC6, 0x0FC6) BIND(0x102D, 0x1030) BIND(0x1032, 0x1032) BIND(0x1036, 0x1037) BIND(0x1039, 0x1039) BIND(0x1058, 0x1059) BIND(0x1160, 0x11FF) BIND(0x135F, 0x135F) BIND(0x1712, 0x1714) BIND(0x1732, 0x1734) BIND(0x1752, 0x1753) BIND(0x1772, 0x1773) BIND(0x17B4, 0x17B5) BIND(0x17B7, 0x17BD) BIND(0x17C6, 0x17C6) BIND(0x17C9, 0x17D3) BIND(0x17DD, 0x17DD) BIND(0x180B, 0x180D) BIND(0x18A9, 0x18A9) BIND(0x1920, 0x1922) BIND(0x1927, 0x1928) BIND(0x1932, 0x1932) BIND(0x1939, 0x193B) BIND(0x1A17, 0x1A18) BIND(0x1B00, 0x1B03) BIND(0x1B34, 0x1B34) BIND(0x1B36, 0x1B3A) BIND(0x1B3C, 0x1B3C) BIND(0x1B42, 0x1B42) BIND(0x1B6B, 0x1B73) BIND(0x1DC0, 0x1DCA) BIND(0x1DFE, 0x1DFF) BIND(0x200B, 0x200F) BIND(0x202A, 0x202E) BIND(0x2060, 0x2063) BIND(0x206A, 0x206F) BIND(0x20D0, 0x20EF) BIND(0x302A, 0x302F) BIND(0x3099, 0x309A) BIND(0xA806, 0xA806) BIND(0xA80B, 0xA80B) BIND(0xA825, 0xA826) BIND(0xFB1E, 0xFB1E) BIND(0xFE00, 0xFE0F) BIND(0xFE20, 0xFE23) BIND(0xFEFF, 0xFEFF) BIND(0xFFF9, 0xFFFB) BIND(0x10A01, 0x10A03) BIND(0x10A05, 0x10A06) BIND(0x10A0C, 0x10A0F) BIND(0x10A38, 0x10A3A) BIND(0x10A3F, 0x10A3F) BIND(0x1D167, 0x1D169) BIND(0x1D173, 0x1D182) BIND(0x1D185, 0x1D18B) BIND(0x1D1AA, 0x1D1AD) BIND(0x1D242, 0x1D244) BIND(0xE0001, 0xE0001) BIND(0xE0020, 0xE007F) BIND(0xE0100, 0xE01EF)cangjie_compiler-1.0.7/include/cangjie/Basic/Linkage.h000066400000000000000000000015101510705540100225730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares some common utility functions. */ #ifndef CANGJIE_BASIC_LINKAGE_H #define CANGJIE_BASIC_LINKAGE_H #include namespace Cangjie { /** * Represent the linkage of the Decl. */ enum class Linkage : uint8_t { WEAK_ODR, /**< weak_odr linkage. */ EXTERNAL, /**< External linkage. */ INTERNAL, /**< Internal linkage. */ LINKONCE_ODR, /**< linkonce_odr linkage. */ EXTERNAL_WEAK, /**< external_weak linkage */ }; } // namespace Cangjie #endif // CANGJIE_BASIC_LINKAGE_H cangjie_compiler-1.0.7/include/cangjie/Basic/Match.h000066400000000000000000000116301510705540100222610ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file provide pattern match for AST Node. */ #ifndef CANGJIE_BASIC_MATCH_H #define CANGJIE_BASIC_MATCH_H #include namespace Cangjie::Meta { // The generic Signature falls back on the Signature of the call operator if present. template struct Signature : Signature::operator())> { }; // Pointer to member function fall back on the plain function Signatures. template struct Signature : Signature { }; // Pointer to member function (const) fall back on the plain function Signatures. template struct Signature : Signature { }; // Get Ith element of packed parameters. template using IthElem = std::tuple_element_t>; // Actual implementation just for pure function Signature types. template struct Signature { using Type = Ret(Args...); template using Argument = IthElem; using RetType = Ret; static constexpr size_t numArgs = sizeof...(Args); }; // Return type of the function. template using RetT = typename Signature>::RetType; // Check all the types whether same. template using AreT = std::conjunction...>; // Matcher functor do the pattern match. template class Matcher { public: Matcher(Node& node) : node(node) {} ~Matcher(){}; template auto operator()(Lambdas&&... lambdas) -> decltype(auto) { constexpr size_t numLambdas = sizeof...(Lambdas); // Check num of arguments. constexpr bool validNumArgs = (... && ((Signature::numArgs == 0) or (Signature::numArgs == 1))); static_assert(validNumArgs, "Can only match on lambdas with one argument(zero argument only in " "the default case)."); constexpr int numArgs = (... + (Signature::numArgs)); static_assert( numArgs == numLambdas or numArgs == numLambdas - 1, "There can be only one default case in the match."); // Check return types match. using FirstRetType = RetT>; static_assert(AreT...>::value, "All functions must have same return type."); return Invoke(lambdas...); } private: // Invoke the function with 0 argument. template auto Invoke0(const Lambda& lambda) -> std::enable_if_t::numArgs == 0, RetT> { return lambda(); } // Invoke the function with 1 argument. template auto Invoke0(Lambda& lambda) -> std::enable_if_t::numArgs == 1, RetT> { using TargetT = std::remove_reference_t::template Argument<0>>; // Add const if Type has const. if (auto* p = dynamic_cast(&node)) { return std::invoke(lambda, *p); } } // Invoke the last lambda. template auto Invoke(Lambda& lambda) -> RetT { return Invoke0(lambda); } // Invoke all the lambdas. template auto Invoke(const Lambda first, const Lambdas&... rest) -> RetT { // Check the default case should be the last one. constexpr int numFirstArgs = Signature::numArgs; constexpr size_t numRest = sizeof...(Lambdas); static_assert( numFirstArgs == 1 or (numFirstArgs == 0 and numRest == 0), "The default case should at the end of match."); using TargetT = std::remove_reference_t::template Argument<0>>; // Add const if Type has const. if (auto* p = dynamic_cast(&node)) { return std::invoke(first, *p); } else { return Invoke(rest...); } } Node& node; }; /** * The match is a dsl for AST Node pattern match. There are some constraint of the usage: * 1. All return types need to be same. * 2. The match case must have one argument. * 3. The defualt case(0 argument) must at the last of the match, it is optional. * @param node The Node reference. * ``` * match(*n)( * [](Expr& e){}, * [](Decl& d){}, * ... * [](){} * ); * ``` */ template auto match(Node& node) -> decltype(auto) { return Matcher(node); } }; // namespace Cangjie::Meta #endif // CANGJIE_BASIC_MATCH_H cangjie_compiler-1.0.7/include/cangjie/Basic/Position.h000066400000000000000000000057751510705540100230460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Position, which represents the position in a source file. */ #ifndef CANGJIE_BASIC_POSITION_H #define CANGJIE_BASIC_POSITION_H #include namespace Cangjie { enum class PositionStatus { KEEP, /**< Mark the position is valid and should be kept. */ IGNORE, /**< Mark the position should be ignored when emitting debug info. */ }; /** * A position in a source file. Line and column start at 1 (byte count for column). */ struct Position { Position(unsigned int fileID, int line, int column) noexcept : fileID(fileID), line(line), column(column) { } Position(int line, int column) noexcept : line(line), column(column) { } Position() = default; Position(unsigned int fileID, int line, int column, bool curfile) noexcept : fileID(fileID), line(line), column(column), isCurFile(curfile) { } unsigned int fileID = 0; int line = 0; int column = 0; bool isCurFile{false}; bool operator==(const Position& rhs) const; bool operator!=(const Position& rhs) const; bool operator<(const Position& rhs) const; bool operator<=(const Position& rhs) const; bool operator>(const Position& rhs) const; bool operator>=(const Position& rhs) const; Position operator+(const Position& rhs) const; Position& operator+=(const Position& rhs); Position operator-(const Position& rhs) const; Position& operator-=(const Position& rhs); Position operator+(const size_t w) const; Position operator-(const size_t w) const; std::string ToString() const; /** * Whether line and column are both zero. */ bool IsZero() const; void Mark(PositionStatus newStatus); PositionStatus GetStatus() const; friend std::ostream& operator<<(std::ostream& out, const Position& pos) { out << pos.ToString(); return out; } inline uint64_t Hash64() const { return (static_cast(fileID) << 32u) ^ (static_cast(line) << 16u) ^ (static_cast(column)); } // Hash without fileID for Macro. inline uint32_t Hash32() const { return (static_cast(line) << 16u) ^ (static_cast(column)); } // Get the pair from the hash value that created by Position.Hash32(). static std::pair RestorePosFromHash(uint32_t hash) { return std::pair(static_cast(hash >> 16u), static_cast(hash & 0xFFFF)); } private: PositionStatus status{PositionStatus::KEEP}; }; const Position INVALID_POSITION = Position{0, 0, 0}; const Position BEGIN_POSITION = Position{0, 1, 1}; const Position DEFAULT_POSITION = Position{0, -1, -1}; } // namespace Cangjie #endif // CANGJIE_BASIC_POSITION_H cangjie_compiler-1.0.7/include/cangjie/Basic/Print.h000066400000000000000000000136751510705540100223340ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares Print related apis. */ #ifndef CANGJIE_BASIC_PRINT_H #define CANGJIE_BASIC_PRINT_H #include #include #include #include #include #include #include "cangjie/Basic/Position.h" #include "cangjie/Basic/Utils.h" #include "cangjie/Basic/Color.h" #include "cangjie/Utils/ICEUtil.h" namespace Cangjie { // No color means nothing, reset means reset state. enum class DiagColor : uint8_t { NO_COLOR, RESET, BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, REVERSE, }; ///@{ /// These functions are used in driver/backend tools error. They do not have code position info and therefore do /// not fit in DiagnosticEngine. These print functions can handle colored printing on Linux. /// Errors are printed red, warnings yellow, and info green. inline void Print() {} template inline void Print(Args&&... args) { ((std::cout << args << ' '), ...); } inline void Println() { std::cout << std::endl; } template inline void Println(Arg&& arg) { std::cout << std::forward(arg) << std::endl; } template inline void Println(Arg&& arg, Args&&... args) { std::cout << std::forward(arg); ((std::cout << ' ' << std::forward(args)), ...); std::cout << std::endl; } inline void PrintNoSplit() {} template inline void PrintNoSplit(Args&&... args) { (std::cout << ... << args); } inline void PrintIndentOnly(unsigned indent, unsigned numSpaces = 2) { std::cout << std::string(indent * numSpaces, ' '); } template inline void PrintIndent(unsigned indent, const Args... args) { PrintIndentOnly(indent); Println(args...); } template inline void PrintCommandDesc(const Opt& option, const Desc& desc, int optionWidth) { std::cout << " " << std::left << std::setfill(' ') << std::setw(optionWidth) << option << desc << std::endl; } const std::string RED_ERROR_MARK = ANSI_COLOR_RED + "error" + ANSI_COLOR_RESET + ": "; const std::string YELLOW_WARNING_MARK = ANSI_COLOR_YELLOW + "warning" + ANSI_COLOR_RESET + ": "; const std::string GREEN_INFO_MARK = ANSI_COLOR_GREEN + "info" + ANSI_COLOR_RESET + ": "; const std::string GREEN_DEBUG_MARK = ANSI_COLOR_GREEN + "debug" + ANSI_COLOR_RESET + ": "; static const std::unordered_map ColorPrintMap = { {DiagColor::NO_COLOR, ANSI_NO_COLOR}, {DiagColor::RESET, ANSI_COLOR_RESET}, {DiagColor::BLACK, ANSI_COLOR_BLACK}, {DiagColor::RED, ANSI_COLOR_RED}, {DiagColor::GREEN, ANSI_COLOR_GREEN}, {DiagColor::YELLOW, ANSI_COLOR_YELLOW}, {DiagColor::BLUE, ANSI_COLOR_BLUE}, {DiagColor::MAGENTA, ANSI_COLOR_MAGENTA}, {DiagColor::CYAN, ANSI_COLOR_CYAN}, {DiagColor::WHITE, ANSI_COLOR_WHITE}, {DiagColor::REVERSE, ANSI_COLOR_WHITE_BACKGROUND_BLACK_FOREGROUND}, }; inline void ErrorWithColor(const DiagColor& color, const std::string& content, bool isBright = false) { if (isBright) { std::cerr << ANSI_COLOR_BRIGHT; } std::cerr << ColorPrintMap.at(color); std::cerr << content; if (color != DiagColor::NO_COLOR) { std::cerr << ANSI_COLOR_RESET; } } // no format Error print with new line template inline void Errorln(Args&&... args) noexcept { std::cerr << RED_ERROR_MARK; ((std::cerr << args), ...); std::cerr << std::endl; } /** * Write given args to std error */ template inline void WriteError(Args&&... args) { ((std::cerr << args), ...); } // no format Error print without new line template inline void Error(Args&&... args) { std::cerr << RED_ERROR_MARK; ((std::cerr << args), ...); } // format Error print with new line inline void Errorf(const char* fmt, ...) { std::cerr << RED_ERROR_MARK; va_list myargs; va_start(myargs, fmt); (void)vfprintf(stderr, fmt, myargs); va_end(myargs); std::cerr << std::flush; } template inline void Warning(Args&&... args) { std::cerr << YELLOW_WARNING_MARK; ((std::cerr << args), ...); } template inline void Warningln(Args&&... args) { std::cerr << YELLOW_WARNING_MARK; ((std::cerr << args), ...); std::cerr << std::endl; } inline void Warningf(const char* fmt, ...) { std::cerr << YELLOW_WARNING_MARK; va_list myargs; va_start(myargs, fmt); (void)vfprintf(stderr, fmt, myargs); va_end(myargs); } template inline void Info(Args&&... args) { std::cout << GREEN_INFO_MARK; ((std::cout << args), ...); } template inline void Infoln(Args&&... args) { std::cout << GREEN_INFO_MARK; ((std::cout << args), ...); std::cout << std::endl; } inline void Infof(const char* fmt, ...) { PrintNoSplit(GREEN_INFO_MARK); va_list myargs; va_start(myargs, fmt); vprintf(fmt, myargs); va_end(myargs); } inline void Printf(const char* fmt, ...) { va_list myargs; va_start(myargs, fmt); vprintf(fmt, myargs); va_end(myargs); } template inline void Debug([[maybe_unused]] Args&&... args) { #ifndef NDEBUG PrintNoSplit(GREEN_DEBUG_MARK); Print(args...); #endif } template inline void Debugln([[maybe_unused]] Args&&... args) { #ifndef NDEBUG PrintNoSplit(GREEN_DEBUG_MARK); Println(args...); #endif } inline void Debugf([[maybe_unused]] const char* fmt, ...) { #ifndef NDEBUG PrintNoSplit(GREEN_DEBUG_MARK); va_list myargs; va_start(myargs, fmt); vprintf(fmt, myargs); va_end(myargs); #endif } ///@} } // namespace Cangjie #endif // CANGJIE_BASIC_PRINT_H cangjie_compiler-1.0.7/include/cangjie/Basic/SourceManager.h000066400000000000000000000130051510705540100237560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the SourceManager related classes, which manages the source files. */ #ifndef CANGJIE_BASIC_SOURCEMANAGER_H #define CANGJIE_BASIC_SOURCEMANAGER_H #include #include #include #include #include #include "cangjie/Basic/Position.h" #include "cangjie/Basic/Utils.h" #include "cangjie/Lex/Token.h" namespace Cangjie { /** * Source has all information of source code. */ struct Source { unsigned int fileID = 0; std::string path; std::string buffer; uint64_t fileHash; // To differ imported source. /* * lineOffsets records the offset of the start position of each line in the source code relative to pInputStart. * every two adjacent elements in this vector can be seen as a left closed right open interval pair, * one pair corresponds to one line * * e.g. * `lineOffsets == {0, 5}` means the source code have two lines, * and the first line has five chars (including line terminator), the pair can be abbreviated as [0, 5), * 0 is the offset of start of the first line relative to pInputStart, * 5 is the offset of start of the second line relative to pInputStart. * */ std::vector lineOffsets{0}; /**< First offset of each line. */ std::optional packageName = std::nullopt; size_t PosToOffset(const Position& pos) const; Source(unsigned int fileID, std::string path, std::string buffer, uint64_t fileHash = 0, const std::optional& packageName = std::nullopt); Position GetEndPos() const { auto sourceSplited = Utils::SplitLines(buffer); if (sourceSplited.empty()) { return Position{fileID, 1, 1}; } auto sourceLine = static_cast(sourceSplited.size()); auto column = static_cast(sourceSplited.back().size()) + 1; return Position{fileID, sourceLine, column}; } std::unordered_map offsetCommentsMap; /**< Offset->Comments map. */ }; /** * SourceManager manage all source files. */ class SourceManager { private: std::unordered_map filePathToFileIDMap; std::vector sources{{0, "", ""}}; public: SourceManager() = default; SourceManager(const SourceManager&) = delete; SourceManager& operator=(SourceManager&) = delete; /** * Get the source by file id. * @param id File id. */ Source& GetSource(const unsigned int id) { if (id >= sources.size()) { return sources[0]; } else { return sources[id]; } } const std::vector& GetSources() const { return sources; } /** * Get the file id by file path. * @param path file path. */ int GetFileID(const std::string& path) { auto exist = filePathToFileIDMap.find(path); if (exist != filePathToFileIDMap.end()) { return exist->second; } else { return -1; } } /** * Get the number of files. */ unsigned int GetNumberOfFiles() { return static_cast(sources.size()); } bool HasSource() { // The source manager will create 1 source by default. return GetNumberOfFiles() != 1; } bool IsSourceFileExist(const unsigned int id); int GetLineEnd(const Position& pos); void SaveSourceFile(unsigned int fileID, std::string normalizedPath, std::string buffer, uint64_t fileHash, std::optional packageName = std::nullopt); /** * This add fake files info to `filePathToFileIDMap`, * to keep the file id stable and consistent with previous compilations phase. */ void ReserveCommonPartSources(std::vector files); /** * Add a source to SourceManager. * @param path File path. * @param buffer Source code. */ unsigned int AddSource(const std::string& path, const std::string& buffer, std::optional packageName = std::nullopt); /** * Add source to SourceManager. Package name default to null. */ unsigned int AppendSource(const std::string& path, const std::string& buffer); /// Overwrite commentsMap with \ref commentsMap void AddComments(const TokenVecMap& commentsMap); void Clear() { sources.clear(); filePathToFileIDMap.clear(); sources.emplace_back(Source{0, "", ""}); } /** * Get content between given position. * @param begin: begin position. * @param end: end position. * @param importGenericContent generic instantiation content. */ std::string GetContentBetween( const Position& begin, const Position& end, const std::string& importGenericContent = "") const; /** * Get content between given position. * @param fileID target file. * @param begin: begin position. * @param end: end position. * @param importGenericContent generic instantiation content. */ std::string GetContentBetween(unsigned int fileID, const Position& begin, const Position& end, const std::string& importGenericContent = "") const; static const std::string testPkgSuffix; }; } // namespace Cangjie #endif // CANGJIE_BASIC_SOURCEMANAGER_H cangjie_compiler-1.0.7/include/cangjie/Basic/StringConvertor.h000066400000000000000000000053661510705540100244060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the StringConvertor, which can convert string to codepoint vector and do string * normalization. */ #ifndef CANGJIE_BASIC_STRINGCONVERTOR_H #define CANGJIE_BASIC_STRINGCONVERTOR_H #include #include #include #include namespace Cangjie { class StringConvertor { public: /// Normalize function is used to deal with escape characters and convert raw string into string which can be /// processed in codegen directly. static std::string Normalize(const std::string& str, bool isRawString = false); /// convert string into raw string which contains escape characters. static std::string Recover(const std::string& str); /** * Convert utf-8 string to codepoint vector. * * UTF-8 Binary | codepoint Binary * 0xxxxxxx | 00000000 000000000 00000000 0xxxxxxx * 110xxxxx 10xxxxxx | 00000000 000000000 00000xxx xxxxxxxx * 1110xxxx 10xxxxxx 10xxxxxx | 00000000 000000000 xxxxxxxx xxxxxxxx * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | 00000000 0000xxxxx xxxxxxxx xxxxxxxx */ static std::vector UTF8ToCodepoint(const std::string& str); static std::string CodepointToUTF8(const uint32_t pointValue); /// Read a hex number from \ref it and convert it to a unicode point, forward \ref it past the end of the read /// hex value, and returns a string that represents the unicode point /// The behaviour is erroneous if the input number is not a unicode point static std::string GetUnicodeAndToUTF8(std::string::const_iterator& it, const std::string::const_iterator& strEnd); /// Convert escape sequences to literal from. /// e.g., "\b" is converted to "\\b", so a literal "\b" is in the result static std::string EscapeToJsonString(const std::string& str); #ifdef _WIN32 enum StrEncoding { UTF8, GBK, UNKNOWN }; static StrEncoding GetStringEncoding(const std::string& data); static std::optional GBKToUTF8(const std::string& gbkStr); static std::optional UTF8ToGBK(const std::string& utf8Str); static std::optional NormalizeStringToUTF8(const std::string& data); static std::optional NormalizeStringToGBK(const std::string& data); static std::optional StringToWString(const std::string& data); #endif }; } // namespace Cangjie #endif // CANGJIE_BASIC_STRINGCONVERTOR_H cangjie_compiler-1.0.7/include/cangjie/Basic/UGTypeKind.h000066400000000000000000000021511510705540100232060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_UGTYPEKIND_H #define CANGJIE_UGTYPEKIND_H #include enum UGTypeKind { UG_CLASS = -128, UG_INTERFACE = -127, UG_RAWARRAY = -126, UG_FUNC = -125, UG_COMMON_ENUM = -124, UG_WEAKREF = -123, UG_INTEROP_FOREIGN_PROXY = -122, UG_INTEROP_EXPORTED_REF = -121, UG_GENERIC_CUSTOM = -2, UG_GENERIC = -1, UG_NOTHING = 0, UG_UNIT = 1, UG_BOOLEAN = 2, UG_RUNE = 3, UG_UINT8 = 4, UG_UINT16 = 5, UG_UINT32 = 6, UG_UINT64 = 7, UG_UINT_NATIVE = 8, UG_INT8 = 9, UG_INT16 = 10, UG_INT32 = 11, UG_INT64 = 12, UG_INT_NATIVE = 13, UG_FLOAT16 = 14, UG_FLOAT32 = 15, UG_FLOAT64 = 16, UG_CSTRING = 17, UG_CPOINTER = 18, UG_CFUNC = 19, UG_VARRAY = 20, UG_TUPLE = 21, UG_STRUCT = 22, UG_ENUM = 23, }; #endif // CANGJIE_UGTYPEKIND_H cangjie_compiler-1.0.7/include/cangjie/Basic/Utils.h000066400000000000000000000035611510705540100223310ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares some common utility functions. */ #ifndef CANGJIE_BASIC_UTILS_H #define CANGJIE_BASIC_UTILS_H #include #include namespace Cangjie::Utils { const uint8_t LINUX_LINE_TERMINATOR_LENGTH = 1; const uint8_t WINDOWS_LINE_TERMINATOR_LENGTH = 2; /// Get hash value by std::hash. uint64_t GetHash(const std::string& content); /// Split a string by '\r\n' and '\n' to form a vector of strings. std::vector SplitLines(const std::string& str); /// Split a string by customised \ref delimiter. Typically used in command line arguments parsing. std::vector SplitString(const std::string& str, const std::string& delimiter); /// \param splitDc whether to split by '::' as well. std::vector SplitQualifiedName(const std::string& qualifiedName, bool splitDc = false); /// Join strings \ref strs with \ref delimiter. std::string JoinStrings(const std::vector& strs, const std::string& delimiter); /** * check whether character(s) start from pStr is `LineTerminator` * if it is a Windows LineTerminator '\r\n', return 2 * if it is a Linux LineTerminator '\n', return 1 * otherwise, 0 is returned. */ uint8_t GetLineTerminatorLength(const char* pStr, const char* pEnd); #ifdef _WIN32 struct WindowsOsVersion { unsigned long dwMajorVersion; unsigned long dwMinorVersion; unsigned long dwBuildNumber; unsigned long dwPlatformId; }; WindowsOsVersion GetOSVersion(); #endif inline std::string GetLineTerminator() { #ifdef _WIN32 return "\r\n"; #else return "\n"; #endif } } // namespace Cangjie::Utils #endifcangjie_compiler-1.0.7/include/cangjie/Basic/Version.h000066400000000000000000000012251510705540100226510ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the cangjie compiler's version. */ #ifndef CANGJIE_DRIVER_VERSION_H #define CANGJIE_DRIVER_VERSION_H #include namespace Cangjie { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND extern const std::string CANGJIE_VERSION; extern const std::string CANGJIE_COMPILER_VERSION; #endif void PrintVersion(); } // namespace Cangjie #endif // CANGJIE_DRIVER_VERSION_H cangjie_compiler-1.0.7/include/cangjie/CHIR/000077500000000000000000000000001510705540100205575ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/CHIR/AST2CHIR/000077500000000000000000000000001510705540100217365ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/CHIR/AST2CHIR/AST2CHIR.h000066400000000000000000000415501510705540100232730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares Translate AST to CHIR.0 */ #ifndef CANGJIE_CHIR_AST2CHIR_H #define CANGJIE_CHIR_AST2CHIR_H #include #include #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/PrintNode.h" #include "cangjie/AST/Walker.h" #include "cangjie/AST/Utils.h" #include "cangjie/CHIR/ImplicitImportedFuncMgr.h" #include "cangjie/CHIR/AST2CHIR/AST2CHIRNodeMap.h" #include "cangjie/CHIR/AST2CHIR/GlobalVarInitializer.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Type/CHIRType.h" #include "cangjie/CHIR/Value.h" #include "cangjie/IncrementalCompilation/CompilationCache.h" #include "cangjie/Mangle/CHIRMangler.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Sema/GenericInstantiationManager.h" #include "cangjie/Utils/ProfileRecorder.h" #include "cangjie/AST/Types.h" #include "cangjie/Sema/TypeManager.h" namespace Cangjie::CHIR { class AST2CHIR { public: class AST2CHIRBuilder { public: const GlobalOptions* opts; const GenericInstantiationManager* gim; ImportManager* import; SourceManager* sourceManager; TypeManager* types; DiagAdapter* diag; IncreKind kind; CompilationCache* cachedInfo; CHIRBuilder* builder; CHIRType* chirType; std::string outputPath; bool isComputingAnnos{}; // is in computing annotations mode // Note: this variable is used only in sancov. /** * @brief Build AST2CHIR instance. */ AST2CHIR Build() { CJC_NULLPTR_CHECK(opts); CJC_NULLPTR_CHECK(import); CJC_NULLPTR_CHECK(types); CJC_NULLPTR_CHECK(diag); return AST2CHIR(*this); } /** * @brief set global options. * * @param globalOptions global options to set. */ AST2CHIRBuilder* SetGlobalOptions(const GlobalOptions& globalOptions) { opts = &globalOptions; return this; } /** * @brief set incremetal kind. * * @param increKind incremetal kind to set. */ AST2CHIRBuilder* SetIncreKind(const IncreKind& increKind) { kind = increKind; return this; } AST2CHIRBuilder* SetCachedInfo(CompilationCache& newinfo) { cachedInfo = &newinfo; return this; } /** * @brief set generic instantiate manager. * * @param genericInstantiateManager generic instantiate manager to set. */ AST2CHIRBuilder* SetGenericInstantiationManager(const GenericInstantiationManager* genericInstantiateManager) { gim = genericInstantiateManager; return this; } /** * @brief set import manager. * * @param manager import manager to set. */ AST2CHIRBuilder* SetImportManager(ImportManager& manager) { import = &manager; return this; } /** * @brief set source manager. * * @param manager source manager to set. */ AST2CHIRBuilder* SetSourceManager(SourceManager& manager) { sourceManager = &manager; return this; } AST2CHIRBuilder* SetDiag(DiagAdapter& ciDiag) { diag = &ciDiag; return this; } /** * @brief set typeManager context. * * @param typeManager typeManager context to set. */ AST2CHIRBuilder* SetTypeManager(TypeManager& typeManager) { types = &typeManager; return this; } /** * @brief set CHIR type manager. * * @param type CHIR type manager. */ AST2CHIRBuilder* SetCHIRType(CHIRType& type) { chirType = &type; return this; } /** * @brief set CHIR Builder. * * @param builder CHIR Builder. */ AST2CHIRBuilder* SetCHIRBuilder(CHIRBuilder& bd) { builder = &bd; return this; } /** * @brief set CHIR output path. * * @param path output path. */ AST2CHIRBuilder* SetOutputPath(const std::string& path) { outputPath = path; return this; } AST2CHIRBuilder* SetComputeAnnotations(bool value) { isComputingAnnos = value; return this; } }; ~AST2CHIR() { } /** * @brief translate AST package to CHIR package * * @param node AST package * @return bool return false if IR is illegal */ bool ToCHIRPackage(AST::Package& node); CHIR::Package* GetPackage() const { return package; } std::unordered_set&& GetSrcCodeImportedFuncs() { return std::move(srcCodeImportedFuncs); } std::unordered_set&& GetSrcCodeImportedVars() { return std::move(srcCodeImportedVars); } std::unordered_map&& GetImplicitFuncs() { return std::move(implicitFuncs); } std::vector&& GetInitFuncsForConstVar() { for (auto f : initFuncsForAnnoFactory) { initFuncsForConstVar.push_back(f); } return std::move(initFuncsForConstVar); } std::unordered_map&& GetMaybeUnreachableBlocks() { return std::move(maybeUnreachable); } std::vector>&& GetAnnoFactoryFuncs() { return std::move(annoFactoryFuncs); } void SetAnnoOnlyDecls(std::vector&& annoOnly) { annoOnlyDecls = std::move(annoOnly); } private: explicit AST2CHIR(AST2CHIRBuilder& builderToCHIR) : opts(*builderToCHIR.opts), gim(builderToCHIR.gim), importManager(*builderToCHIR.import), sourceManager(*builderToCHIR.sourceManager), types(*builderToCHIR.types), diag(*builderToCHIR.diag), cachedInfo(*builderToCHIR.cachedInfo), kind(builderToCHIR.kind), builder(*builderToCHIR.builder), chirType(*builderToCHIR.chirType), outputPath(builderToCHIR.outputPath), isComputingAnnos{builderToCHIR.isComputingAnnos} { } void AST2CHIRCheck(); void CollectImplicitFuncs(); void AddToImplicitFuncs(AST::FuncDecl& funcDecl, std::vector& registeredImplicitFuncs, std::unordered_set>& implicitlyImportedDecls) const; void CollectImportedDecls(const AST::Package& node); void CollectDeclsInCurPkg(AST::Package& node); void CollectImportedGenericInstantiatedDecl( const AST::Package& node, std::unordered_set& mangledNameSet); void CollectImportedDeclUsedInCurPkg(AST::Decl& decl); void CollectImportedPropDecl(AST::PropDecl& propDecl); void CollectImportedGenericDecl(AST::Decl& decl); void CollectDecls(AST::Decl& decl, bool instantiated); void CollectDeclsFromEnumDecl(AST::EnumDecl& enumDecl); void CollectDeclsFromExtendDecl(AST::ExtendDecl& extendDecl); void CollectDeclsFromClassLikeDecl(AST::ClassLikeDecl& classLikeDecl); void CollectDeclsFromStructDecl(const AST::StructDecl& structDecl); void CollectMemberDecl(AST::Decl& decl); void CollectFuncDecl(AST::FuncDecl& funcDecl); void CollectImportedFuncDeclAndDesugarParams(AST::FuncDecl& funcDecl); void CollectImportedGlobalOrStaticVarDecl(AST::VarDecl& varDecl); void CollectDeclToList(AST::Decl& decl, std::vector>& astNodes); void CollectFuncDeclToList(AST::FuncDecl& func, std::vector>& list); void CollectDesugarDecl(AST::Decl& decl); void CollectInstantiatedDecls(const AST::Decl& decl); void CollectVarandVarwithpatternDecl(AST::Decl& decl); /** * @brief create all top-level func decl's shell and var decls, cache them to global symbol table. */ void CacheTopLevelDeclToGlobalSymbolTable(); void CreateAnnoOnlyDeclSig(const AST::Decl& decl); void CreatePseudoDefForAnnoOnlyDecl(const AST::Decl& decl); void CacheCustomTypeDefToGlobalSymbolTable(); void CreateCustomTypeDef(const AST::Decl& decl, bool isImported); void TranslateAllCustomTypeTy(); void TranslateNominalDecls(const AST::Package& pkg); void SetFuncAttributeAndLinkageType(const AST::FuncDecl& astFunc, FuncBase& chirFunc); void FlatternPattern(const AST::Pattern& pattern, bool isLocalConst); void CreateAndCacheGlobalVar(const AST::VarDecl& decl, bool isLocalConst); void CreateGlobalVarSignature(const std::vector>& decls, bool isLocalConst = false); void CreateFuncSignatureAndSetGlobalCache(const AST::FuncDecl& funcDecl); void CreateImportedFuncSignatureAndSetGlobalCache(const AST::FuncDecl& funcDecl); void CreateImportedValueSignatureAndSetGlobalCache(const AST::VarDecl& varDecl); void CreatePseudoImportedFuncSignatureAndSetGlobalCache(const AST::FuncDecl& funcDecl); void TranslateAllDecls(const AST::Package& pkg, const InitOrder& initOrder); void TranslateTopLevelDeclsInParallel(); void TranslateInParallel(const std::vector>& decls); /** @brief Returns true if the AST2Chir process is unsuccessfull **/ bool HasFailed() const; // Register all source file names void RegisterAllSources(); void SetInitFuncForStaticVar(); void TranslateInitOfGlobalVars(const AST::Package& pkg, const InitOrder& initOrder); void CollectTopLevelDecls(AST::Package& pkg); void CacheSomeDeclsToGlobalSymbolTable(); void CollectStaticInitFuncInfo(); void CollectFuncsAndVars(); void CollectLocalConstFuncAndVars(const AST::Package& pkg); std::pair SortGlobalVarDecl(const AST::Package& pkg); void SetGenericDecls() const; void SetExtendInfo(); void UpdateExtendParent(); Translator CreateTranslator(); /* Micro refactoring for CJMP. */ void TranslateFuncParams(const AST::FuncDecl& funcDecl, Func& func) const; void TranslateVecDecl(const std::vector>& decls, Translator& trans) const; /* Add methods for CJMP. */ // Try to deserialize common part for CJMP. bool TryToDeserializeCHIR(); // Check whether the decl is deserialized for CJMP. bool MaybeDeserialized(const AST::Decl& decl) const; // Try to get deserialized node from package including CustomTypeDef (excluding Extend), Func, GlobalVar, // ImportedValue. template T* TryGetDeserialized(const AST::Decl& decl) { if (!MaybeDeserialized(decl)) { return nullptr; } std::string key = GLOBAL_VALUE_PREFIX + decl.mangledName; if (decl.TestAttr(AST::Attribute::FOREIGN) && decl.IsFunc()) { key = decl.rawMangleName; } T* res = nullptr; if constexpr (std::is_base_of_v) { auto it = deserializedDefs.find(key); res = it == deserializedDefs.end() ? nullptr : static_cast(it->second); } else { // get value auto it = deserializedVals.find(key); res = it == deserializedVals.end() ? nullptr : dynamic_cast(it->second); } if (res && !decl.TestAttr(AST::Attribute::PLATFORM)) { deserializedDecls.insert(&decl); } return res; } // build symbol table for deserialized decls from common part. void BuildDeserializedTable(); // Reset platform func for CJMP. void ResetPlatformFunc(const AST::FuncDecl& funcDecl, Func& func); // Check whether the decl need be translated for CJMP. inline bool NeedTranslate(const AST::Decl& decl) const { return deserializedDecls.find(&decl) == deserializedDecls.end(); } const GlobalOptions& opts; const GenericInstantiationManager* gim; ImportManager& importManager; SourceManager& sourceManager; TypeManager& types; DiagAdapter& diag; CompilationCache& cachedInfo; IncreKind kind; VirtualWrapperDepMap curVirtFuncWrapDep; VirtualWrapperDepMap delVirtFuncWrapForIncr; // cache global func decl, global var decl AST2CHIRNodeMap globalCache; CHIRBuilder& builder; CHIRType& chirType; std::vector> allTopLevelNodes; /** @brief all files that after sorting in this package */ std::vector pkgFiles; std::unordered_map implicitFuncs; std::vector initFuncsForConstVar; std::vector initFuncsForAnnoFactory; std::unordered_map maybeUnreachable; std::string outputPath; bool isComputingAnnos{}; CHIR::Package* package{nullptr}; bool failure{false}; std::set dependencyPkg; // ======================== Imported Pkg Top-Level Decl Part ======================== // // See REG_IMPLICIT_IMPORTED_NON_GENERIC_FUNC for more details. std::unordered_set> implicitDecls{}; // including imported global var and static var. std::vector> importedGlobalAndStaticVars{}; // including imported global func and member func. std::vector> importedGlobalAndMemberFuncs{}; // including imported nominal decl: ClassDecl、InterfaceDecl、StructDecl、EnumDecl. std::vector> importedNominalDecls{}; // ============= instantiated in current pkg, who's generic definition is also in imported pkg============= // std::vector> importedGenericInstantiatedNominalDecls{}; // ======================== Current Pkg Top-Level Decl Part ======================== // // including global VarDecl、global VarWithPatternDecl、static member VarDecl. std::vector> globalAndStaticVars{}; /// during computeAnnotations, translate only the annoFactoryFuncs of these decls. /// These are decls that are not preserved when selecting const subpkg but have @!CustomAnnotations's. /// \See ComputeAnnotations.cpp std::vector annoOnlyDecls{}; // including global FuncDecl、instantiated global FuncDecl、 MemberDecl(instance/static/open/abstract/instantiated). std::vector> globalAndMemberFuncs{}; // including ClassDecl、InterfaceDecl、StructDecl、ExtendDecl、EnumDecl. std::vector> nominalDecls{}; // Definition in other language, thus no body std::vector> foreignFuncs{}; // ======================== Generic Top-Level Decl Part ======================== // // Collect all top level Geneirc nominalDecl: ClassLikeDecl、EnumDecl、StructDecl std::vector> genericNominalDecls{}; // ======================== For Global Variable Initialization ======================== // // The static vars which are initialized in `static init func`. std::unordered_set> varsInitedByStaticInitFunc; // The info about `static init func`, static vars initialized in it and the parent custom type decl StaticInitInfoMap staticInitFuncInfoMap; // All funcs, global vars and static vars which need to analyze dependencies for variable initialization ElementList> funcsAndVars; // All local const vars which need to be lifted as global vars ElementList> localConstVars; ElementList> localConstFuncs; // anno factory funcs std::vector> annoFactoryFuncs; // File and its containing vars map. Note that the static vars initialized in `static init func` // are not included here std::unordered_map, std::vector>> fileAndVarMap; std::unordered_set> usedSrcImportedNonGenericDecls; /* Add fields for CJMP. */ bool outputCHIR{false}; // Output type is CHIR bool mergingPlatform{false}; // Merging platform part over already compiled chir std::unordered_set> deserializedDecls; // decls which don't need to be retranslated. // Deserialized node cache table, key is identifier. std::unordered_map deserializedDefs; std::unordered_map deserializedVals; std::unordered_set srcCodeImportedFuncs; std::unordered_set srcCodeImportedVars; bool creatingLocalConstVarSignature{false}; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/AST2CHIR/AST2CHIRChecker.h000066400000000000000000000014261510705540100245560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_AST2CHIR_CHECKER_H #define CANGJIE_CHIR_AST2CHIR_CHECKER_H #include "cangjie/AST/Node.h" #include "cangjie/CHIR/AST2CHIR/AST2CHIRNodeMap.h" #include "cangjie/CHIR/Type/CHIRType.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { bool AST2CHIRCheckCustomTypeDef( const AST::Node& astNode, const CustomTypeDef& chirNode, const AST2CHIRNodeMap& globalCache); bool AST2CHIRCheckValue(const AST::Node& astNode, const Value& chirNode); } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/AST2CHIR/AST2CHIRNodeMap.h000066400000000000000000000031531510705540100245340ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the symbol table of CHIR. */ #ifndef CANGJIE_CHIR_SYMBOLTABLE_H #define CANGJIE_CHIR_SYMBOLTABLE_H #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/CHIR/Value.h" #include "cangjie/Mangle/CHIRMangler.h" #include "cangjie/Sema/TypeManager.h" namespace Cangjie::CHIR { template class AST2CHIRNodeMap { public: AST2CHIRNodeMap() { } ~AST2CHIRNodeMap() = default; bool Has(const Cangjie::AST::Node& node) const { return cache.find(&node) != cache.end(); } void Set(const Cangjie::AST::Node& node, T& chirNode) { CJC_ASSERT(cache.emplace(&node, &chirNode).second); } T* Get(const Cangjie::AST::Node& node) const { auto chirNode = cache.at(&node); CJC_NULLPTR_CHECK(chirNode); return chirNode; } T* TryGet(const Cangjie::AST::Node& node) const { auto it = cache.find(&node); if (it != cache.end()) { return it->second; } return nullptr; } const std::unordered_map& GetALL() const { return cache; } void Erase(const Cangjie::AST::Node& node) { (void)cache.erase(&node); } private: std::unordered_map cache; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/AST2CHIR/CollectLocalConstDecl/000077500000000000000000000000001510705540100260755ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/CHIR/AST2CHIR/CollectLocalConstDecl/CollectLocalConstDecl.h000066400000000000000000000017541510705540100324140ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_COLLECT_LOCAL_CONST_DECL_H #define CANGJIE_CHIR_COLLECT_LOCAL_CONST_DECL_H #include #include "cangjie/AST/Node.h" namespace Cangjie::CHIR { class CollectLocalConstDecl { public: /** * @brief collect local const decls * * @param decls AST decls * @param rootIsGlobalDecl 1st param is global or local decl */ void Collect(const std::vector>& decls, bool rootIsGlobalDecl); const std::vector& GetLocalConstVarDecls() const; const std::vector& GetLocalConstFuncDecls() const; private: std::vector localConstVarDecls; std::vector localConstFuncDecls; }; } #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/AST2CHIR/GlobalDeclAnalysis.h000066400000000000000000000206361510705540100256120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_GLOBALDECLANALYSIS_H #define CANGJIE_CHIR_GLOBALDECLANALYSIS_H #include #include #include #include "cangjie/AST/Node.h" #include "cangjie/AST/Walker.h" #include "cangjie/Sema/GenericInstantiationManager.h" namespace Cangjie::CHIR { class GlobalDeclAnalysis { public: GlobalDeclAnalysis(DiagAdapter& ciDiag, const GenericInstantiationManager* gim, IncreKind kind, const ElementList>& funcsAndVars, const ElementList>& localConstVars, const StaticInitInfoMap& staticInitFuncInfoMap, bool outputChir = false, bool mergingPlatform = false) : diag(&ciDiag), gim(gim), kind(kind), funcsAndVars(funcsAndVars), localConstVars(localConstVars), staticInitFuncInfoMap(staticInitFuncInfoMap), outputChir(outputChir), mergingPlatform(mergingPlatform) { } /** * @brief analysis circular dependency in global var decls * * @param nodesWithDeps AST decls * @param fileAndVarMap key is file, value is decls in this file * @param cachedInfo incremental compilation cached info * @return InitOrder ordered var decls */ InitOrder Run(const ElementList>& nodesWithDeps, const std::unordered_map, std::vector>>& fileAndVarMap, CompilationCache& cachedInfo); private: bool SkipDependencyTo(const AST::Decl& decl); void CheckUseBeforeInit(const std::vector>& vars); void AnalysisFileDependencyImpl(const Ptr& rootDecl, const Ptr& curDecl, std::unordered_set>& visited); void AnalysisFileDependency(); void AnalysisGlocalVarsAndLocalConstVarsDependencyImpl(const Ptr& rootDecl, const Ptr& curDecl, std::unordered_set>& visited); void AnalysisGlobalVarsAndLocalConstVarsDependency(const InitOrder& initOrder); void AnalysisDependency(const ElementList>& nodesWithDeps); void AdditionalAnalysisDepOfNonStaticCtor(const AST::FuncDecl& func, std::vector>& dependencies, std::vector>& localConstVarDeps); void AdditionalAnalysisDepOfStaticInit( const Ptr& staticInit, std::vector>& dependencies) const; void WalkAndCollectDep(const AST::Node& curNode, std::vector>& dependencies, std::vector>& localConstVarDeps); void AnalysisDepOf(const AST::Decl& rootDecl, std::vector>& dependencies, std::vector>& localConstVarDeps); void ParseVarWithPatternDecl(const AST::VarWithPatternDecl& root); void ParsePattern(const AST::VarWithPatternDecl& rootDecl, AST::Pattern& pattern); void AddDependencyImpl(const AST::Decl& depDecl, std::vector>& dependencies) const; void AddDependency(const AST::Decl& depDecl, std::vector>& dependencies); AST::VisitAction VisitExprAction(const AST::Expr& expr, std::vector>& dependencies, std::vector>& localConstVarDeps); AST::VisitAction CollectDepInCallExpr( const AST::CallExpr& callExpr, std::vector>& dependencies); AST::VisitAction CollectDepInLitConstExpr( const AST::LitConstExpr& litConstExpr, std::vector>& dependencies); InitOrder MergeLocalConstVarInitOrder( const InitOrder& initOrder, const std::vector> sortedVars) const; std::vector> SortGlobalVarDep(const ElementList>& nodesWithDeps, const std::unordered_map, std::vector>>& fileAndVarMap, CompilationCache& cachedInfo); InitOrder SortLocalConstVarDep(const InitOrder& initOrder); DiagAdapter* diag; const GenericInstantiationManager* gim; IncreKind kind; // All funcs and variables which will form the dependency graph by their dependencies ElementList> funcsAndVars; // All local const variables which will be lifted to global const ElementList>& localConstVars; // The info of what static init func initialize what static variables const StaticInitInfoMap& staticInitFuncInfoMap; // A map to track what `VarDecl`s are hold by `VarWithPatternDecl` std::unordered_map, Ptr> varPtternDeclMap; // A map to track the dependencies between the funcs and global/static variables DeclDepMap funcsAndVarsDepMap; // A map to track the dependencies from the funcs and global/static variables to local const vars std::unordered_map, std::vector>> localConstVarsDepMap; // A map to track the dependencies between the files FileDepMap fileDependencyMap; // A map to track the dependencies between global/static variables in same file, this will be used to check // `use before initialization` error std::unordered_map, std::unordered_set>> var2varDepsInFile; // A map to track the dependencies between the global/static variables and local const variables, this wil be used // to determine a final init order of all variables DeclDepMap globalVarsAndLocalConstVarsDepMap; bool outputChir = false; bool mergingPlatform = false; }; template class DepsUnit { public: T element; std::vector depsUnits; std::vector userUnits; std::vector circularDepElements; int disc = -1; int low = -1; bool stackMember = false; explicit DepsUnit(T ele) { this->element = ele; } void AddDeps(DepsUnit& unit) { auto pos = std::find(depsUnits.begin(), depsUnits.end(), &unit); if (pos == depsUnits.end()) { depsUnits.emplace_back(&unit); } } void AddUsers(DepsUnit& unit) { auto pos = std::find(userUnits.begin(), userUnits.end(), &unit); if (pos == userUnits.end()) { (void)userUnits.emplace_back(&unit); } } }; // use class to provide sort ability template class TarjanSort { public: void InitDependency(const std::vector& rawElements, const std::unordered_map>& dependencyMap); void DoTarjanSort(); void TarjanSortImpl(DepsUnit& unit, int& index, std::stack*>& st); void UpdateSccUnit(std::unordered_map>& dependencyMap); void UpdateSccUnitDeps(DepsUnit& unit, std::unordered_map>& dependencyMap); std::vector GetOrderElement(); virtual void DetectCircularDep() = 0; virtual std::string GetEleStr(T) const = 0; virtual ~TarjanSort(); std::vector*> newSccUnits; std::vector>> allNewSccUnits; std::map*> node2SccUnitMap; std::vector*> diagUnits; std::vector*> units; std::vector>> allUnits; std::unordered_set*> visitedUnits; std::map*> node2UnitMap; }; class VarCirDepsChecker : public TarjanSort> { public: explicit VarCirDepsChecker(DiagAdapter& ciDiag); void DetectCircularDep() override; std::string GetEleStr(Ptr decl) const override { return decl->identifier; } bool IsVar(const AST::Decl& decl) const; DiagAdapter* diag; std::vector diagLog; }; class FileCirDepsChecker : public TarjanSort> { public: FileCirDepsChecker(DiagAdapter& ciDiag, const std::vector& vardiagLog); void InitFileDep(const FileDepMap& depMap); void DetectCircularDep() override; std::string GetEleStr(Ptr node) const override { return node->fileName; } DiagAdapter* diag; std::vector diagLog; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/AST2CHIR/GlobalVarInitializer.h000066400000000000000000000106411510705540100261660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_GLOBALVARINITIALIZER_H #define CANGJIE_CHIR_GLOBALVARINITIALIZER_H #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" namespace Cangjie::CHIR { // ordered file ordered var decl using OrderedDecl = std::pair>, std::vector>>; class GlobalVarInitializer { public: explicit GlobalVarInitializer(Translator& trans, const ImportManager& importManager, std::vector& initFuncsForConstVar, bool enableIncre) : builder(trans.builder), globalSymbolTable(trans.globalSymbolTable), opts(trans.opts), importManager(importManager), initFuncsForConstVar(initFuncsForConstVar), trans(trans), enableIncre(enableIncre) { } /** * @brief generate global var init function * * @param pkg AST package * @param initOrder ordered global var decls */ void Run(const AST::Package& pkg, const InitOrder& initOrder); private: void CreatePackageInit(const AST::Package& curPackage, const InitOrder& initOrder); void CreatePackageLiteralInit(const AST::Package& curPackage, const InitOrder& initOrder); inline std::pair PreparePackageInit(const AST::Package& curPackage); inline std::pair PreparePackageLiteralInit(const AST::Package& curPackage); void InsertInitializerIntoPackageInitializer(FuncBase& init, Func& packageInit); FuncBase* TranslateSingleInitializer(const AST::VarDecl& decl); bool IsIncrementalNoChange(const AST::VarDecl& decl) const; Func* TranslateInitializerToFunction(const AST::VarDecl& decl); ImportedFunc* TranslateIncrementalNoChangeVar(const AST::VarDecl& decl); Ptr GetGlobalVariable(const AST::VarDecl& decl); template Ptr CreateGVInitFunc(const T& node, Args&& ... args) const; void RemoveInitializerForVarDecl(const AST::VarDecl& varDecl, Func& fileInit) const; void RemoveCommonInitializersReplacedWithPlatform( Func& fileInit, const std::vector>& decls) const; Ptr TryGetFileInitialializer(const AST::File& file, const std::string& suffix = ""); Ptr TranslateFileInitializer(const AST::File& file, const std::vector>& decls); Ptr TranslateFileLiteralInitializer(const AST::File& file, const std::vector>& decls); Func* TranslateVarWithPatternInitializer(const AST::VarWithPatternDecl& decl); Func* TranslateWildcardPatternInitializer(const AST::VarWithPatternDecl& decl); Func* TranslateTupleOrEnumPatternInitializer(const AST::VarWithPatternDecl& decl); void FillGVInitFuncWithApplyAndExit(const std::vector>& varInitFuncs); void AddImportedPackageInit(const AST::Package& curPackage, const std::string& suffix = ""); Ptr GetImportsInitFunc(const AST::Package& curPackage, const std::string& suffix = ""); Ptr CreateImportsInitFunc(const AST::Package& curPackage, const std::string& suffix = ""); void UpdateImportsInit(const AST::Package& curPackage, Func& importsInitFunc, const std::string& suffix = ""); void AddGenericInstantiatedInit(); Ptr GeneratePackageInitBase(const AST::Package& curPackage, const std::string& suffix = ""); void InsertAnnotationVarInitInto(Func& packageInit); bool NeedVarLiteralInitFunc(const AST::Decl& decl); FuncBase* TranslateVarInit(const AST::Decl& var); // Add methods for CJMP template T* TryGetDeserialized(const std::string& mangledName) { // merging platform if (opts.inputChirFiles.size() == 1) { return TryGetFromCache(GLOBAL_VALUE_PREFIX + mangledName, trans.deserializedVals); } return nullptr; } private: CHIRBuilder& builder; AST2CHIRNodeMap& globalSymbolTable; const GlobalOptions& opts; const ImportManager& importManager; std::vector& initFuncsForConstVar; Translator& trans; bool enableIncre; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/AST2CHIR/TranslateASTNode/000077500000000000000000000000001510705540100250515ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/CHIR/AST2CHIR/TranslateASTNode/ExceptionTypeMapping.h000066400000000000000000000036221510705540100313410ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares normal type and exception type maping struct template. */ #ifndef CANGJIE_CHIR_EXCEPTION_TYPEMAPPING_H #define CANGJIE_CHIR_EXCEPTION_TYPEMAPPING_H #include "cangjie/CHIR/Expression/Terminator.h" namespace Cangjie::CHIR { template struct CHIRNodeMap { }; // Defined CHIR type mapping register macro. #define DEFINE_CHIR_TYPE_MAPPING(TYPE) \ template <> struct CHIRNodeMap { \ using Normal = TYPE; \ using Exception = TYPE##WithException; \ } DEFINE_CHIR_TYPE_MAPPING(CHIR::Apply); DEFINE_CHIR_TYPE_MAPPING(CHIR::Invoke); DEFINE_CHIR_TYPE_MAPPING(CHIR::InvokeStatic); DEFINE_CHIR_TYPE_MAPPING(CHIR::TypeCast); DEFINE_CHIR_TYPE_MAPPING(CHIR::Allocate); DEFINE_CHIR_TYPE_MAPPING(CHIR::Spawn); DEFINE_CHIR_TYPE_MAPPING(CHIR::Intrinsic); DEFINE_CHIR_TYPE_MAPPING(CHIR::RawArrayAllocate); template <> struct CHIRNodeMap { using Normal = CHIR::UnaryExpression; using Exception = CHIR::IntOpWithException; }; template <> struct CHIRNodeMap { using Normal = CHIR::BinaryExpression; using Exception = CHIR::IntOpWithException; }; template using CHIRNodeNormalT = typename CHIRNodeMap::Normal; template using CHIRNodeExceptionT = typename CHIRNodeMap::Exception; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h000066400000000000000000001605561510705540100273700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares Translator */ #ifndef CANGJIE_CHIR_TRANSLATOR_H #define CANGJIE_CHIR_TRANSLATOR_H #include "cangjie/AST/NodeX.h" #include "cangjie/CHIR/AST2CHIR/AST2CHIRNodeMap.h" #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/ExceptionTypeMapping.h" #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/CHIRType.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Value.h" #include "cangjie/Option/Option.h" #include "cangjie/Sema/GenericInstantiationManager.h" #include "cangjie/Utils/SafePointer.h" namespace Cangjie::CHIR { class Translator { public: Translator(CHIRBuilder& builder, CHIRType& chirTy, const Cangjie::GlobalOptions& opts, const GenericInstantiationManager* gim, AST2CHIRNodeMap& globalSymbolTable, const ElementList>& localConstVars, const ElementList>& localConstFuncs, const IncreKind& kind, const std::unordered_map& deserializedVals, std::vector>& annoFactories, std::unordered_map& maybeUnreachable, bool computeAnnotations, std::vector& initFuncForAnnoFactory, const Cangjie::TypeManager& typeManager) : builder(builder), chirTy(chirTy), globalSymbolTable(globalSymbolTable), localConstVars(localConstVars), localConstFuncs(localConstFuncs), opts(opts), gim(gim), increKind(kind), mergingPlatform(opts.inputChirFiles.size() == 1), deserializedVals(deserializedVals), annoFactoryFuncs(annoFactories), maybeUnreachable(maybeUnreachable), isComputingAnnos{computeAnnotations}, initFuncsForAnnoFactory{initFuncForAnnoFactory}, typeManager{typeManager} { } /** * @brief Translates an AST node into a Value. * * @param node The AST node to be translated. * @param trans The translator instance used for translation. * @return A pointer to the translated Value. */ static Ptr TranslateASTNode(const AST::Node& node, Translator& trans) { auto base = &node; auto backBlock = trans.currentBlock; auto nodePtr = GetDesugaredExpr(node); if (auto expr = DynamicCast(nodePtr); expr && expr->mapExpr != nullptr) { base = expr->mapExpr; } if (auto res = trans.exprValueTable.TryGet(*base)) { return res; } Ptr res = nullptr; switch (nodePtr->astKind) { #define ASTKIND(KIND, VALUE, NODE, SIZE) \ case AST::ASTKind::KIND: { \ res = trans.Visit(*AST::StaticAs(nodePtr)); \ break; \ } #include "cangjie/AST/ASTKind.inc" #undef ASTKIND default: res = trans.Visit(*nodePtr); } /* There are two cases need add `Goto` when translate AST::Block case1: if the Block node is a desugar node, then add `Goto`. example code: | | desugar block `print("${a.a.b}\n")` => desugar to `print({var tmp1 = Stringbuilder(); tmp1.append({a.a.b})})` | | desugar block case2: unsafe block. example code: var a = unsafe{} */ if (auto subBlock = DynamicCast(nodePtr)) { if (nodePtr != &node || nodePtr->TestAttr(AST::Attribute::UNSAFE)) { trans.CreateAndAppendTerminator(trans.GetBlockByAST(*subBlock), backBlock); } } return res; } struct InstCalleeInfo { Type* instParentCustomTy{nullptr}; Type* thisType{nullptr}; std::vector instParamTys; Type* instRetTy{nullptr}; std::vector instantiatedTypeArgs; bool isVirtualFuncCall{false}; }; struct InstInvokeCalleeInfo { std::string srcCodeIdentifier; FuncType* instFuncType{nullptr}; FuncType* originalFuncType{nullptr}; // not ()->Unit, include this type std::vector instantiatedTypeArgs; std::vector genericTypeParams; Type* thisType{nullptr}; }; // === static helper functions == /** * @brief Retrieves the constructor ID of an enum. * * @param target The enum declaration. * @return The constructor ID of the enum. */ static uint64_t GetEnumCtorId(const AST::Decl& target); /** * @brief Retrieves the pattern ID of an enum pattern. * * @param enumPattern The enum pattern. * @return The pattern ID of the enum. */ static uint64_t GetEnumPatternID(const AST::EnumPattern& enumPattern); /** * @brief Translates a type from AST to CHIR. * * @param ty The type in AST format. * @return The translated type. */ Ptr TranslateType(AST::Ty& ty); /** * @brief Retrieves the debug location information of a value. * * @param value The value to get the debug location for. * @return The debug location information. */ DebugLocation GetValueDebugLocationInfo(const Value& value) const; /** * @brief Translates a code location from Cangjie positions to a debug location. * * @param begin The beginning position of the code. * @param end The ending position of the code. * @return The translated debug location. */ DebugLocation TranslateLocation(const Cangjie::Position& begin, const Cangjie::Position& end) const; /** * @brief Translates a file location to a debug location. * * @param fileID The ID of the file. * @return The translated file location. */ DebugLocation TranslateFileLocation(unsigned fileID) const; /** * @brief Translates a node location to a debug location. * * @param node The AST node. * @return The translated node location. */ DebugLocation TranslateLocation(const AST::Node& node) const; /** * @brief Retrieves the operator location for a given expression. * * @tparam T The type of the expression. * @param expr The expression to get the operator location for. * @return The operator location. */ template DebugLocation GetOperatorLoc(const T& expr) { const std::string& opStr = TOKENS[static_cast(expr.op)]; Cangjie::Position begin; if constexpr (std::is_same_v) { begin = expr.assignPos.IsZero() ? expr.begin : expr.assignPos; } else { begin = expr.operatorPos.IsZero() ? expr.begin : expr.operatorPos; } auto end = begin + Cangjie::Position(0, 0, static_cast(opStr.size())); return TranslateLocation(begin, end); } /** * @brief Sets the generic function map. * * @param funcMap A map from generic function declarations to their specialized instances. */ void SetGenericFuncMap(const std::unordered_map>& funcMap) { genericFuncMap = funcMap; } // ===--------------------------------------------------------------------===// // Expression API // ===--------------------------------------------------------------------===// /** * @brief Creates and appends an expression of type TExpr. * * @tparam TExpr The type of the expression to create. * @param resultTy The result type of the expression. * @param args The arguments for the expression constructor. * @return A pointer to the created expression. */ template TExpr* CreateAndAppendExpression(Type* resultTy, Args&&... args) { return Cangjie::CHIR::CreateAndAppendExpression(builder, resultTy, args...); } /** * @brief Creates and appends an expression with a specified debug location. * * @tparam TExpr The type of the expression to create. * @param loc The debug location for the expression. * @param resultTy The result type of the expression. * @param args The arguments for the expression constructor. * @return A pointer to the created expression. */ template TExpr* CreateAndAppendExpression(const DebugLocation& loc, Type* resultTy, Args&&... args) { return Cangjie::CHIR::CreateAndAppendExpression(builder, loc, resultTy, args...); } /** * @brief Creates and appends an expression with specified debug locations for warnings and expression. * * @tparam TExpr The type of the expression to create. * @param locForWarning The debug location for potential warnings. * @param loc The debug location for the expression. * @param resultTy The result type of the expression. * @param args The arguments for the expression constructor. * @return A pointer to the created expression. */ template TExpr* CreateAndAppendExpression( const DebugLocation& locForWarning, const DebugLocation& loc, Type* resultTy, Args&&... args) { return Cangjie::CHIR::CreateAndAppendExpression(builder, locForWarning, loc, resultTy, args...); } /** * @brief Creates and appends a constant expression. * * @tparam TLitVal The type of the literal value. * @param resultTy The result type of the expression. * @param parentBlock The block to which the expression will be appended. * @param args The arguments for the expression constructor. * @return A pointer to the created constant expression. */ template Constant* CreateAndAppendConstantExpression(Type* resultTy, Block& parentBlock, Args&&... args) { Constant* expr = builder.CreateConstantExpression(resultTy, &parentBlock, args...); parentBlock.AppendExpression(expr); return expr; } /** * @brief Creates and appends a constant expression with a specified debug location. * * @tparam TLitVal The type of the literal value. * @param loc The debug location for the expression. * @param resultTy The result type of the expression. * @param parentBlock The block to which the expression will be appended. * @param args The arguments for the expression constructor. * @return A pointer to the created constant expression. */ template Constant* CreateAndAppendConstantExpression( const DebugLocation& loc, Type* resultTy, Block& parentBlock, Args&&... args) { auto expr = builder.CreateConstantExpression(loc, resultTy, &parentBlock, args...); parentBlock.AppendExpression(expr); return expr; } /** * @brief Creates and appends a terminator. * * @tparam TExpr The type of the terminator to create. * @param args The arguments for the terminator constructor. * @return A pointer to the created terminator. */ template TExpr* CreateAndAppendTerminator(Args&&... args) { return Cangjie::CHIR::CreateAndAppendTerminator(builder, std::forward(args)...); } /** * @brief Creates and appends a terminator with a specified debug location. * * @tparam TExpr The type of the terminator to create. * @param loc The debug location for the terminator. * @param args The arguments for the terminator constructor. * @return A pointer to the created terminator. */ template TExpr* CreateAndAppendTerminator(const DebugLocation& loc, Args&&... args) { return Cangjie::CHIR::CreateAndAppendTerminator(builder, loc, std::forward(args)...); } /** * @brief Retrieves the current block. * * @return A pointer to the current block. */ Ptr GetCurrentBlock() const; /** * @brief Sets the current block. * * @param block The block to set as the current block. */ void SetCurrentBlock(Block& block); /** * @brief Creates an empty global variable initialization function. * * @param mangledName The mangled name of the function. * @param identifier The identifier of the function. * @param rawMangledName The raw mangled name of the function. * @param pkgName The package name. * @param linkage The linkage type of the function. * @param loc The debug location for the function. * @param isConst The global var is const or not. * @return A pointer to the created function. */ Ptr CreateEmptyGVInitFunc(const std::string& mangledName, const std::string& identifier, const std::string& rawMangledName, const std::string& pkgName, const Linkage& linkage, const DebugLocation& loc, bool isConst); /** * @brief Returns a dereferenced value if necessary. * * @param val The value to dereference. * @param loc The debug location for the operation. * @return A pointer to the dereferenced value. */ Ptr GetDerefedValue(Ptr val, const DebugLocation& loc = INVALID_LOCATION); /** * @brief Flattens a variable declaration with a pattern. * * @param pattern The pattern used in the declaration. * @param target The target value to flatten. * @param isLocalPattern Indicates if the pattern is local. */ void FlattenVarWithPatternDecl(const AST::Pattern& pattern, const Ptr& target, bool isLocalPattern); /** * @brief Translates a literal constant expression to a CHIR constant. * * @param expr The literal constant expression. * @param realTy The real type of the constant. * @param block The block to which the constant will be added. * @return A pointer to the translated constant. */ Ptr TranslateLitConstant(const AST::LitConstExpr& expr, AST::Ty& realTy, Ptr block); /** * @brief Translates a literal constant expression to a CHIR literal value. * * @param expr The literal constant expression. * @param realTy The real type of the constant. * @return A pointer to the translated literal value. */ Ptr TranslateLitConstant(const AST::LitConstExpr& expr, AST::Ty& realTy); /** * @brief Creates annotation factory functions for a declaration. * * @param decl The declaration to create annotation factory functions for. * @param parent The custom type definition. * @return The annotation information. */ AnnoInfo CreateAnnoFactoryFuncSig(const AST::Decl& decl, CustomTypeDef* parent); /** * @brief Translates the body of an annotation factory function. * * @param decl The declaration. * @param func The function to translate the body for. */ void TranslateAnnoFactoryFuncBody(const AST::Decl& decl, Func& func); void TranslateAnnotationsArrayBody(const AST::Decl& decl, Func& func); std::vector TranslateAnnotationsArraySig(const AST::ArrayLit& annos, const Func& func); GlobalVar* TranslateCustomAnnoInstanceSig(const AST::Expr& expr, const Func& func, size_t i); /** * @brief Creates annotation information for a function parameter. * * @param astParam The AST function parameter. * @param chirParam The CHIR parameter. * @param parent The custom type definition. */ void CreateParamAnnotationInfo(const AST::FuncParam& astParam, Parameter& chirParam, CustomTypeDef& parent); /** * @brief Creates annotation information for a declaration. * * @tparam TValue The type of the value to create annotation info for. * @param decl The declaration. * @param value The value to create annotation info for. * @param parent The custom type definition. Can be null. */ template void CreateAnnotationInfo(const AST::Decl& decl, TValue& value, CustomTypeDef* parent) { value.SetAnnoInfo(CreateAnnoFactoryFuncSig(decl, parent)); } /** * @brief Creates annotation factory functions for a function declaration. * * @param funcDecl The function declaration. * @param parent The custom type definition. Can be null. */ void CreateAnnoFactoryFuncsForFuncDecl(const AST::FuncDecl& funcDecl, CustomTypeDef* parent); /** * @brief Retrieves a wrapper function from a member access. * * @param thisType The type of 'this'. * @param funcName The name of the function. * @param instFuncType The instance function type. * @param isStatic Indicates if the function is static. * @param funcInstTypeArgs The function instance type arguments. * @return A pointer to the wrapper function. */ Value* GetWrapperFuncFromMemberAccess(Type& thisType, const std::string funcName, FuncType& instFuncType, bool isStatic, std::vector& funcInstTypeArgs); void SetCompileTimeValue(bool val) { isCompileTimeValue = val; } /** * @brief Retrieves the desugared expression from a node. * * @param node The node to desugar. * @return A pointer to the desugared expression. */ static const Ptr GetDesugaredExpr(const AST::Node& node) { auto expr = DynamicCast(&node); auto nodePtr = &node; while (expr != nullptr && expr->desugarExpr != nullptr) { nodePtr = expr->desugarExpr.get(); expr = DynamicCast(nodePtr); } return nodePtr; } Translator Copy() const; /// Set the top level decl this Translator object is visiting. These apis are used to make sure local const funcs /// are translated iff they are being visited as top level decls. void SetTopLevel(const AST::Decl& decl); bool IsTopLevel(const AST::Decl& decl) const; /// common translation functions /// Translate AST expression \p node as the argument of another CHIR expression Value* TranslateExprArg(const AST::Node& node); /// T* can be implicitly converted to bool, delete this overload to avoid Type* converted to bool template Value* TranslateExprArg(const AST::Node& node, T*) = delete; /// Translate AST expression \p node as the argument of another CHIR expression, and convert it to target CHIR /// type \p targetTy. Value* TranslateExprArg(const AST::Node& node, Type& targetTy, bool deref = true); /// Translate AST expression \p node as the return value of another CHIR expression, and store it to the target /// location \p debugLoc if \ref loc is not null and the result value is not of Unit or Nothing type. /// \param debugLoc If present, use it as the debug location of the generated typecast/store, if any. If absent, use /// the location of \param location as the debug location. /// This function is typically used in /// translate a subblock of another expression as the return value /// if (cond) { bl1 } else { bl2 } /// auto condValue = TranslateExprArg(cond, *GetBoolTy()); /// auto tb = CreateBlock(), fb = CreateBlock(); /// auto loc = ifExpr->GetType()->IsUnitOrNothing() ? null : Allocate(RefType{ifExpr->GetType()}); /// (switch to tb) TranslateSubExprAsValue(bl1, loc); /// (switch to fb) TranslateSubExprAsValue(bl2, loc); void TranslateSubExprToLoc(const AST::Node& node, Value* location); void TranslateSubExprToLoc(const AST::Node& node, Value* location, const DebugLocation& debugLoc); /// Translate a node and leave the value of it as the return value of another expression. This is typically used in /// translation of the last expression of an AST block. Ptr TranslateSubExprAsValue(const AST::Node& node); /// Translate AST expression \p node here (in \ref currentBlock), and discard the return value if any. This function /// is typically used when /// 1. translating the sub expressions excluding the last (because the last is used as the /// return value of the block) of a AST block. /// 2. translating the initialiser of a discarded pattern void TranslateSubExprToDiscarded(const AST::Node& node); void CollectValueAnnotation(const AST::Decl& decl); void CollectTypeAnnotation(const AST::InheritableDecl& decl, const CustomTypeDef& cl); private: friend class GlobalVarInitializer; // Used for add compileTimeVal tag for compile-time evaluation. bool isCompileTimeValue = false; CHIRBuilder& builder; CHIRType& chirTy; AST2CHIRNodeMap localValSymbolTable; AST2CHIRNodeMap& globalSymbolTable; const ElementList>& localConstVars; const ElementList>& localConstFuncs; const AST::Decl* topLevelDecl{nullptr}; /** Used for store side effect expressions and other expression which does not have associated valid ast. */ AST2CHIRNodeMap exprValueTable; // Since property's getter and setter will share same annotation function, we need to cache the function name. std::unordered_map, std::string> annotationFuncMap; static std::unordered_map> jAnnoFuncMap; std::vector> blockGroupStack; Ptr currentBlock; Ptr delayExitSignal; // this map is used for check if jump expr is inside try catch scope. std::unordered_map, Ptr> forInExprAST2DebugLocMap; // this map is used for set return value for forIn expr, gc-pool issue:Cangjie-manifest/issues/2442 std::unordered_map, Ptr> forInExprReturnMap; size_t lambdaWrapperIndex{0}; const Cangjie::GlobalOptions& opts; const GenericInstantiationManager* gim; // Map for blocks inside looping control flow, it has conditionBlock, falseBlock. AST2CHIRNodeMap, Ptr>> terminatorSymbolTable; // generic func, instantiated func std::unordered_map> genericFuncMap; enum class ControlType : uint8_t { BREAK = 0, CONTINUE, RETURN, NORMAL, MAX_TYPES, }; /** * Store the tryExprs outside current expression (Only for expressions inside tryBody). * Used for collecting all reachable try-catch exceptions for catch block. */ std::vector> tryBodyContext; std::stack> tryCatchContext; struct FinallyControlVal { Ptr finallyBlock; /** * Has fixed size of DEFAULT_CASE, and will indexed by ControlType before 'DEFAULT_CASE' * the pair of blocks is {the block before control flow, control flow's target block}. */ std::vector, Ptr>>> controlFlowBlocks; FinallyControlVal(Ptr finally) : finallyBlock(finally), controlFlowBlocks(static_cast(ControlType::MAX_TYPES)) { } }; /** * Used for store finally entry block of current try expression. * Try-finally will inside control flow will be generated like: * while () { * controlVal: UInt8 * try { * break ==> disconnect blocks, * store [the block where break happens, the block generated break] at the index of 'break' * xxxx * continue ==> disconnect blocks, * store [the block where continue happens, the block generated continue] at the index of 'continue' * ... * return ==> disconnect blocks, * store [the block where return happens, the block generated return] at the index of 'return' * } finally.normal { finally body; goto endBlock} * } finally.throw { finally body; raise exception } * } finally.break { finally body; goto stored break block } -> may generate multiple times * } finally.continue { finally body; goto stored break block } -> may generate multiple times * } finally.return { finally body; goto return } -> may generate multiple times * } */ std::stack finallyContext; // Record nesting scope info, it is used for 1. debug info; 2. compare scope of try-finally and control flow expr. std::vector scopeInfo{0}; const IncreKind& increKind; const bool mergingPlatform; // add by cjmp const std::unordered_map& deserializedVals; // add by cjmp std::vector>& annoFactoryFuncs; std::unordered_map& maybeUnreachable; bool isComputingAnnos{}; std::vector& initFuncsForAnnoFactory; const Cangjie::TypeManager& typeManager; class ScopeContext { public: explicit ScopeContext(Translator& trans) : scopeInfo(trans.scopeInfo) { scopeInfo.emplace_back(-1); } ~ScopeContext() { scopeInfo.pop_back(); } void ScopePlus() { ++scopeInfo.back(); } private: std::vector& scopeInfo; }; private: void SetSymbolTable(const AST::Node& node, Value& val, bool isLocal = true); Ptr GetSymbolTable(const AST::Node& node) const; Ptr GetNominalSymbolTable(const AST::Node& node); Ptr TypeCastOrBoxIfNeeded(Value& val, Type& expectedTy, const DebugLocation& loc, bool needCheck = true); Ptr GetBlockByAST(const AST::Block& block) { auto val = GetSymbolTable(block); CJC_ASSERT(val->IsBlock()); return StaticCast(val.get()); } Block* CreateBlock() { return builder.CreateBlock(blockGroupStack.back()); } void SetFuncBlockGroup(BlockGroup& group); bool OverloadableExprMayThrowException(const AST::OverloadableExpr& expr, const Type& leftValTy) const; void SetRawMangledNameForIncrementalCompile(const AST::FuncDecl& astFunc, Func& chirFunc) const; /** @brief Wrapped expression creation to handle both normal context and try-catch context.*/ template Expression* TryCreate(Block* parent, Args&&... args) { if (tryCatchContext.empty()) { auto expr = CreateAndAppendExpression>(std::forward(args)..., parent); return expr; } return TryCreateExceptionTerminator>(*parent, std::forward(args)...); } /** * @brief Wrapped expression creation to handle both normal context and try-catch context. * For IntOp, typecast with overflow strategy. */ template Expression* TryCreateWithOV(Block* parent, bool mayThrowE, OverflowStrategy ofs, Args&&... args) { if (tryCatchContext.empty() || !mayThrowE) { return CreateAndAppendExpression>(std::forward(args)..., ofs, parent); } return TryCreateExceptionTerminator>(*parent, std::forward(args)...); } template TEx* TryCreateExceptionTerminator(Block& parent, Args&&... args) { auto errBB = tryCatchContext.top(); auto sucBB = CreateBlock(); if (delayExitSignal && !blockGroupStack.empty() && blockGroupStack.back() != errBB->GetParentBlockGroup()) { // cannot jump to another bg because of for-in bg // use forin delay signal to store exception state auto exceptBB = CreateBlock(); exceptBB->SetExceptions({}); auto ret = CreateAndAppendExpression(std::forward(args)..., sucBB, exceptBB, &parent); currentBlock = exceptBB; UpdateDelayExitSignal(CalculateDelayExitLevelForThrow()); CreateAndAppendTerminator(exceptBB); currentBlock = sucBB; return ret; } auto ret = CreateAndAppendExpression(std::forward(args)..., sucBB, errBB, &parent); currentBlock = sucBB; return ret; } std::vector GetFuncInstArgs(const AST::CallExpr& expr); Expression* GenerateFuncCall(Value& callee, const FuncType* instantiedFuncTy, const std::vector calleeInstTypeArgs, Type* thisTy, const std::vector& args, DebugLocation loc); Expression* GenerateDynmaicDispatchFuncCall(const InstInvokeCalleeInfo& funcInfo, const std::vector& args, Value* thisObj = nullptr, Value* thisRTTI = nullptr, DebugLocation loc = INVALID_LOCATION); CHIR::Type* GetExactParentType(Type& fuzzyParentType, const AST::FuncDecl& resolvedFunction, FuncType& funcType, std::vector& funcInstTypeArgs, bool checkAbstractMethod, bool checkResult = true); // translate var decl Ptr TranslateLeftValueOfVarDecl(const AST::VarDecl& decl, bool rValueIsEmpty, bool isLocalVar); void StoreRValueToLValue(const AST::VarDecl& decl, Value& rval, Ptr& lval); void HandleVarWithVarPattern(const AST::VarPattern& pattern, const Ptr& initNode, bool isLocalPattern); void HandleVarWithTupleAndEnumPattern(const AST::Pattern& pattern, const std::vector>& subPatterns, const Ptr& initNode, bool isLocalPattern); // translate classLike decl void TranslateClassLikeDecl(ClassDef& classDef, const AST::ClassLikeDecl& decl); /* ** Create a Function type for virtual function. */ Ptr CreateVirtualFuncType(const AST::FuncDecl& decl); void AddMemberVarDecl(CustomTypeDef& def, const AST::VarDecl& decl); inline bool IsOpenPlatformReplaceAbstractCommon(ClassDef& classDef, const AST::FuncDecl& decl) const; inline void RemoveAbstractMethod(ClassDef& classDef, const AST::FuncDecl& decl) const; void TranslateClassLikeMemberFuncDecl(ClassDef& classDef, const AST::FuncDecl& decl); void AddMemberPropDecl(CustomTypeDef& def, const AST::PropDecl& decl); void TranslateAbstractMethod(ClassDef& classDef, const AST::FuncDecl& decl, bool hasBody); /* Add methods for CJMP. */ // Micro refactoring for CJMP. void SetClassSuperClass(ClassDef& classDef, const AST::ClassLikeDecl& decl); void SetClassImplementedInterface(ClassDef& classDef, const AST::ClassLikeDecl& decl); // Translate member var init func for common/platform decls. // Return empty `xxx$varInit` func for member var of common/platform decl, otherwise return nullptr. Func* ClearOrCreateVarInitFunc(const AST::Decl& decl); // Translate `xxx$varInit` func for member var of common/platform decl, otherwise return nullptr. Func* TranslateVarInit(const AST::VarDecl& varDecl); // Translate `A$varInit` func for member vars of common/platform decl, otherwise return nullptr. Func* TranslateVarsInit(const AST::Decl& decl); // Add `apply` `xxx$varInit` func of all fields into `A$varInit` func. void TranslateVariablesInit(const AST::Decl& parent, CHIR::Parameter& thisVar); // Add inlined apply for xxx$varInit func. Ptr TranslateConstructorFuncInline(const AST::Decl& parent, const AST::FuncBody& funcBody); // Check whether the member of decl should be translated. bool ShouldTranslateMember(const AST::Decl& decl, const AST::Decl& member) const; Ptr Visit(const AST::ArrayExpr& array); Ptr Visit(const AST::ArrayLit& array); Ptr Visit(const AST::AssignExpr& assign); Ptr Visit(const AST::BinaryExpr& binaryExpr); Ptr Visit(const AST::Block& b); Ptr Visit(const AST::CallExpr& callExpr); Ptr Visit(const AST::ClassDecl& decl); Ptr Visit(const AST::DoWhileExpr& doWhileExpr); Ptr Visit(const AST::EnumDecl& decl); Ptr Visit(const AST::ExtendDecl& decl); Ptr Visit(const AST::FuncArg& arg); Ptr Visit(const AST::FuncBody& funcBody); Ptr Visit(const AST::FuncDecl& func); Ptr Visit(const AST::IfExpr& ifExpr); Ptr Visit(const AST::InterfaceDecl& decl); Ptr Visit(const AST::JumpExpr& jumpExpr); Ptr Visit(const AST::LambdaExpr& lambdaExpr); Ptr Visit(const AST::ForInExpr& forInExpr); Ptr Visit(const AST::LitConstExpr& expr); Ptr Visit(const AST::MatchExpr& matchExpr); Ptr Visit(const AST::MemberAccess& member); Ptr Visit(const AST::ParenExpr& expr); Ptr Visit(const AST::PointerExpr& expr); Ptr Visit(const AST::RefExpr& refExpr); Ptr Visit(const AST::ReturnExpr& expr); Ptr Visit(const AST::SpawnExpr& spawnExpr); Ptr Visit(const AST::StructDecl& decl); Ptr Visit(const AST::SubscriptExpr& subscriptExpr); Ptr Visit(const AST::ThrowExpr& throwExpr); Ptr Visit(const AST::TryExpr& tryExpr); Ptr Visit(const AST::TupleLit& tuple); Ptr Visit(const AST::TypeConvExpr& typeConvExpr); Ptr Visit(const AST::UnaryExpr& unaryExpr); Ptr Visit(const AST::VarDecl& decl); Ptr Visit(const AST::VarWithPatternDecl& patternDecl); Ptr Visit(const AST::WhileExpr& whileExpr); Ptr Visit(const AST::Node& node) const { if (isComputingAnnos && Is(node)) { return nullptr; } CJC_ASSERT(false && "Should not reach here!"); return nullptr; } // ===--------------------------------------------------------------------===// // Helper data structure for left-value translation // ===--------------------------------------------------------------------===// struct LeftValueInfo { Value* base; std::vector path; LeftValueInfo(Value* base_, const std::vector& path_) : base(base_), path(path_) { } }; // ===--------------------------------------------------------------------===// // Helper function for RefExpr translation // ===--------------------------------------------------------------------===// LeftValueInfo TranslateThisOrSuperRefAsLeftValue(const AST::RefExpr& refExpr); LeftValueInfo TranslateClassMemberVarRefAsLeftValue(const AST::RefExpr& refExpr) const; LeftValueInfo TranslateStructMemberVarRefAsLeftValue(const AST::RefExpr& refExpr) const; LeftValueInfo TranslateEnumMemberVarRef(const AST::RefExpr& refExpr); LeftValueInfo TranslateVarRefAsLeftValue(const AST::RefExpr& refExpr); Value* TranslateThisOrSuperRef(const AST::RefExpr& refExpr); Value* TranslateVarRef(const AST::RefExpr& refExpr); Value* TranslateGlobalOrLocalFuncRef(const AST::RefExpr& refExpr, Value& originalFunc); Value* TranslateMemberFuncRef(const AST::RefExpr& refExpr); Value* TranslateFuncRef(const AST::RefExpr& refExpr); // ===--------------------------------------------------------------------===// // Helper function for AssignExpr translation // ===--------------------------------------------------------------------===// Value* TranslateVArrayAssign(const AST::AssignExpr& assign); Value* TranslateLeftValueInfo(const LeftValueInfo& lhs, const DebugLocation& loc); Value* TranslateCompoundAssign(const AST::AssignExpr& assign); Value* TranslateTrivialAssign(const AST::AssignExpr& assign); Func* GetCurrentFunc() const { CJC_ASSERT(!blockGroupStack.empty()); return blockGroupStack.front()->GetOwnerFunc(); } struct BindingConfig { bool hasInitial{false}; bool createDebug{true}; bool setSymbol{true}; }; // Translate function. void BindingFuncParam(const AST::FuncParamList& paramList, const BlockGroup& funcBody, const BindingConfig& cfg = {false, true, true}); Ptr TranslateFuncBody(const AST::FuncBody& funcBody); Ptr TranslateInstanceMemberFunc(const AST::Decl& parent, const AST::FuncBody& funcBody); Ptr TranslateConstructorFunc(const AST::Decl& parent, const AST::FuncBody& funcBody); Ptr TranslateNestedFunc(const AST::FuncDecl& func); Translator SetupContextForLambda(const AST::Block& body); /** NOTE: This method must be called with new translator. */ Ptr TranslateLambdaBody(Ptr lambda, const AST::FuncBody& funcBody, const BindingConfig& config); friend class TranslateCondCtrlExpr; friend class TranslateWhileExpr; // ========Methods used for translating ForInExpr========= Ptr GetOuterMostExpr(); void InitializeDelayExitSignal(const DebugLocation& loc); Ptr InitializeCondVar(const DebugLocation& loc); ForIn* InitForInExprSkeleton(const AST::ForInExpr& forInExpr, Ptr& inductiveVar, Ptr& condVar); Ptr GenerateForInRetValLocation(const DebugLocation& loc); void UpdateDelayExitSignalInForInEnd(const ForIn& forIn); void GenerateSignalCheckForThrow(); int64_t CalculateDelayExitLevelForBreak(); int64_t CalculateDelayExitLevelForContinue(); int64_t CalculateDelayExitLevelForReturn(); int64_t CalculateDelayExitLevelForThrow(); void UpdateDelayExitSignal(int64_t level); Ptr GetOuterBlockGroupReturnValLocation(); void TranslateForInCondControlFlow(Ptr& condVar); void TranslateForInBodyBlockGroup(const AST::ForInExpr& forInExpr); friend class TranslateForInExpr; // ========Methods used for translating ForInExpr with Range kind======== Ptr TranslateForInRange(const AST::ForInExpr& forInExpr); void TranslateForInRangeLatchBlockGroup(const AST::Node& node); std::tuple DelayExitIsNot0(const DebugLocation& loc); // ========Methods used for translating ForInExpr with String kind======= Ptr TranslateForInString(const AST::ForInExpr& forInExpr); void TranslateForInStringLatchBlockGroup(Ptr& inductiveVar); // ========Methods used for translating ForInExpr with Iter kind========== Ptr TranslateForInIter(const AST::ForInExpr& forInExpr); /// Make a Option::None value of option type \param optionType. Ptr MakeNone(Type& optionType, const DebugLocation& loc); Ptr TranslateForInIterCondition(Ptr& iterNextLocation, Ptr& astTy); void TranslateForInIterPattern(const AST::ForInExpr& forInExpr, Ptr& iterNextLocation); void TranslateForInIterLatchBlockGroup(const AST::MatchExpr& matchExpr, Ptr& iterNextLocation); // ========End methods used for translating ForInExpr========= // Short circuit helper function. Ptr TransShortCircuitAnd( const Ptr leftValue, const AST::Expr& rightExpr, const DebugLocation& loc, bool isImplicitAdd = false); Ptr TransShortCircuitOr( const Ptr leftValue, const AST::Expr& rightExpr, const DebugLocation& loc, bool isImplicitAdd = false); Ptr ProcessBinaryExpr(const AST::BinaryExpr& binaryExpr); // ====================call expr=============== // ==================== global func or instance member func call=============== Ptr TranslateIntrinsicCall(const AST::CallExpr& expr); Ptr TranslateCStringCtorCall(const AST::CallExpr& expr); Ptr TranslateForeignFuncCall(const AST::CallExpr& expr); Value* GenerateLoadIfNeccessary(Value& arg, bool isThis, bool isMut, bool isInOut, const DebugLocation& loc); bool IsOverflowOpCall(const AST::FuncDecl& func); Value* TranslateNonStaticMemberFuncCall(const AST::CallExpr& expr); Value* TranslateStaticMemberFuncCall(const AST::CallExpr& expr); Value* CreateGetRTTIWrapper(Value* value, Block* bl, const DebugLocation& loc); Value* TranslateMemberFuncCall(const AST::CallExpr& expr); Value* TranslateTrivialFuncCall(const AST::CallExpr& expr); Ptr TranslateNothingTypeCall(const AST::CallExpr& expr); Ptr TranslateGlobalOrInstanceMemberFuncCall(const AST::CallExpr& expr); Ptr ProcessCallExpr(const AST::CallExpr& expr); Ptr GetMapExpr(AST::Node& node) const; void ProcessMapExpr(AST::Node& originExpr, bool isSubScript = false); void PrintDevirtualizationMessage(const AST::CallExpr& expr, const std::string& nodeType); Ptr GetTypeOfInvokeStatic(const AST::Decl& funcDecl); Ptr GetMemberFuncCallerInstType(const AST::CallExpr& expr, bool needExactTy = true); std::pair, Type*> GetMemberFuncParamAndRetInstTypes(const AST::CallExpr& expr); // ==================== lambda func call=============== Ptr TranslateFuncTypeValueCall(const AST::CallExpr& expr); // ==================== constructor call=============== Ptr TranslateCFuncConstructorCall(const AST::CallExpr& expr); Ptr TranslateEnumCtorCall(const AST::CallExpr& expr); /// Translate common parts of struct/class ctor call of left value and right value versions. /// \returns translate this arg. std::pair TranslateStructOrClassCtorCallCommon(const AST::CallExpr& expr); Value* TranslateStructOrClassCtorCall(const AST::CallExpr& expr); // ==================== func args=============== static bool IsOptimizableTy(Ptr ty); static bool IsOptimizableEnumTy(Ptr ty); static uint64_t GetJumpablePatternVal(const AST::Pattern& pattern); bool CanOptimizeMatchToSwitch(const AST::MatchExpr& matchExpr); std::vector TranslateASTTypes(const std::vector>& genericInfos); bool HasNothingTypeArg(std::vector& args) const; // ============= memberaccess expr ==================== Ptr TranslateStaticTargetOrPackageMemberAccess(const AST::MemberAccess& member); Ptr TranslateInstanceMemberMemberAccess(const AST::MemberAccess& member); Ptr TranslateEnumMemberAccess(const AST::MemberAccess& member); Ptr TranslateVarMemberAccess(const AST::MemberAccess& member); Ptr TranslateFuncMemberAccess(const AST::MemberAccess& member); Value* WrapMemberMethodByLambda(const AST::FuncDecl& funcDecl, const InstCalleeInfo& instFuncType, Value* thisObj); bool IsVirtualFuncCall( const ClassDef& obj, const AST::FuncDecl& funcDecl, bool isSuperCall); InvokeCallContext GenerateInvokeCallContext(const InstCalleeInfo& instFuncType, Value& caller, const AST::FuncDecl& callee, const std::vector& args); InstCalleeInfo GetInstCalleeInfoFromRefExpr(const AST::RefExpr& expr); InstCalleeInfo GetInstCalleeInfoFromMemberAccess(const AST::MemberAccess& expr); Ptr GetBaseFromMemberAccess(const AST::Expr& base); Ptr TransformThisType(Value& rawThis, Type& expectedTy, Lambda& curLambda); // ============= match expr ==================== /** Entrance of translating match with selector. */ void TranslateMatchWithSelector(const AST::MatchExpr& matchExpr, Ptr retVal); /** Entrance of translating match as optimized switch table. */ void TranslateMatchAsTable(const AST::MatchExpr& matchExpr, Ptr retVal); /** Entrance of translating match without selector. */ void TranslateConditionMatches(const AST::MatchExpr& matchExpr, Ptr retVal); bool CanOptimizeToSwitch(const AST::LetPatternDestructor& let) const; Block* TranslateMatchCaseBody( const AST::Block& caseBody, Ptr retVal, Ptr endBlock); /** Entrance of translating multiple outermost or-pattern case. */ // return value -> {false block, true block} std::pair, Ptr> TranslateOrPattern( const std::vector>& patterns, Ptr selectorVal, const DebugLocation& originLoc); std::pair, Ptr> TranslateConstantMultiOr(const std::vector values, Ptr value); Ptr GetEnumIDValue(Ptr ty, Ptr selectorVal); /* Translate or patterns except constant and enum patterns. */ std::pair, Ptr> TranslateComplicatedOrPattern( const std::vector>& patterns, const Ptr selectorVal, const DebugLocation& originLoc); /** Entrance of translating single outermost nesting case pattern. */ // return value -> {false block, true block} std::pair, Ptr> TranslateNestingCasePattern(const AST::Pattern& pattern, const Ptr selectorVal, const DebugLocation& originLoc, const SourceExpr& sourceExpr = SourceExpr::MATCH_EXPR); void TranslatePatternGuard(const AST::MatchCase& matchCase, Ptr falseBlock, Ptr& trueBlock); // ========= helper functions for translating each of nesting patterns ========== // NOTE: 'blocks' is pair of {final matched block, next condition block} // 'enum pattern''s element value should be generated in 'next condition block'. // 'var pattern''s typecast should be generated in 'final matched block'. Ptr DispatchingPattern(std::queue, Ptr>>& queue, const std::pair, Ptr>& blocks, const DebugLocation& originLoc); void CollectingSubPatterns(const Ptr& patternTy, const std::vector>& patterns, const Ptr value, std::queue, Ptr>>& queue, unsigned offset = 0); void HandleVarPattern(const AST::VarPattern& varPattern, const Ptr value, const Ptr& trueBlock); Ptr CastEnumValueToConstructorTupleType(Ptr enumValue, const AST::EnumPattern& enumPattern); Ptr HandleEnumPattern(const AST::EnumPattern& enumPattern, Ptr value, std::queue, Ptr>>& queue, const Ptr& trueBlock, const DebugLocation& originLoc); Type* GetSelectorType(const AST::EnumTy& ty) const; Ptr HandleTypePattern(const AST::TypePattern& typePattern, Ptr value, std::queue, Ptr>>& queue); Ptr HandleConstPattern( const AST::ConstPattern& constPattern, Ptr value, const DebugLocation& originLoc); // ========= helper functions for translating match as table ========== void TranslateTrivialMatchAsTable( const AST::MatchExpr& match, Ptr selectorVal, Ptr retVal, size_t countOfPatterns); // Record relation of enum pattern and its matching case body's id. struct SecondSwitchInfo { const AST::EnumPattern& ep; size_t caseId = 0; }; // NOTE: using map for fixed order of IR generation. struct EnumMatchInfo { // enum pattern index of the case direct pattern -> map std::map>> indexToBodies; // enum pattern index of the case direct pattern -> the blocks to generate body for each type of enum std::map firstSwitchBlocks; // enum pattern index of the case direct pattern -> wildcard cases's info std::map> innerDefaultInfos; }; void TranslateEnumPatternMatchAsTable(const AST::MatchExpr& match, Ptr enumVal, Ptr retVal); void CollectEnumPatternInfo(const AST::Pattern& pattern, EnumMatchInfo& info, size_t caseId); std::unordered_map>> TranslateSecondLevelAsTable( const EnumMatchInfo& info, Ptr enumVal, Ptr firstDefaultBlock); /** * Translate sub patterns for each case inside second level switch table. * @param endBlock [in]: last block when all sub-patterns are not matched. * @param tableBlock [in]: the block where to start generating remain sub-patterns. * @param enumVal [in]: match selector. * @param infos [in]: enum pattern info of current second level swith. * @param blockBranchInfos [out]: map to store the true blocks of current match case id. * eg: enum E { R |G(Int64) |B(Rune, Object) } * match(sel) { * case R | G(1) => 0 * case B('c', v: ToString) => v; 2 * case B('c', v: Object) => v; 3 * case B('a', v) => 3 * case _ => 4 * } * For match case above, the first level switch generate 'multibranch sel.enumId, R.bb, B.bb, _.bb' * The second level for 'B' generate "multibranch id, 'c'.bb, 'a'.bb" * The @p tableBlock is "'a'.bb", then @p enumVal is the value of 'sel', * @p infos contains case info of 'B('c', v: ToString)' and 'B('c', v: Object)', * @p blockBranchInfos will has the keys '1' for 'B('c', v: ToString)', '2' for 'B('c', v: Object)'. */ void TranslateSecondLevelTable(Ptr endBlock, const Ptr tableBlock, const Ptr enumVal, const std::vector& infos, std::unordered_map>>& blockBranchInfos); template void CreateWrappedStore(const DebugLocation& loc, Ptr value, Ptr location, Args&&... args) { auto resultType = location->GetType(); CJC_ASSERT(resultType->IsRef()); resultType = StaticCast(resultType)->GetBaseType(); CreateAndAppendExpression(loc, builder.GetUnitTy(), TypeCastOrBoxIfNeeded(*value, *resultType, loc), location, std::forward(args)...); } template void CreateWrappedStore(const Ptr& value, const Ptr& location, Args&&... args) { auto resultType = location->GetType(); CJC_ASSERT(resultType->IsRef()); resultType = StaticCast(resultType)->GetBaseType(); CreateAndAppendExpression( builder.GetUnitTy(), TypeCastOrBoxIfNeeded(*value, *resultType, value->GetDebugLocation()), location, std::forward(args)...); } template void CreateWrappedBranch(const SourceExpr& sourceExpr, Args&&... args) { auto expr = CreateAndAppendTerminator(std::forward(args)...); expr->SetSourceExpr(sourceExpr); } Ptr CreateGetElementRefWithPath(const DebugLocation& loc, Ptr lhsBase, const std::vector& path, Ptr block, const CustomType& customType); /// Create a typecast or some equivalent expressions that represent a typecast. TypeCast* CreateWrappedTypeCast(Type* ty, Value* operand, Block* parent) { return CreateWrappedTypeCast(INVALID_LOCATION, ty, operand, parent); } TypeCast* CreateWrappedTypeCast(const DebugLocation& loc, Type* ty, Value* operand, Block* parent) { return CreateAndAppendExpression(loc, ty, operand, parent); } void HandleInitializedArgVal(const AST::CallExpr& ce, std::vector& args); void TranslateThisObjectForNonStaticMemberFuncCall( const AST::CallExpr& expr, std::vector& args, bool needsMutableThis); void TranslateTrivialArgsWithSugar( const AST::CallExpr& expr, std::vector& args, const std::vector& expectedArgTys); Value* TranslateTrivialArgWithNoSugar(const AST::FuncArg& arg, Type* expectedArgTy, const DebugLocation& loc); void TranslateTrivialArgsWithNoSugar( const AST::CallExpr& expr, std::vector& args, const std::vector& expectedArgTys); void TranslateTrivialArgs( const AST::CallExpr& expr, std::vector& args, const std::vector& expectedArgTys); Ptr GetCurrentThisObject(const AST::FuncDecl& resolved); Value* GetCurrentThisObjectByMemberAccess(const AST::MemberAccess& memAccess, const AST::FuncDecl& resolved, const DebugLocation& loc); // Translate SubscriptExpr Ptr TranslateTupleAccess(const AST::SubscriptExpr& subscriptExpr); Ptr TranslateVArrayAccess(const AST::SubscriptExpr& subscriptExpr); LeftValueInfo TranslateRefExprAsLeftValue(const AST::RefExpr& refExpr); LeftValueInfo TranslateMemberAccessAsLeftValue(const AST::MemberAccess& member); LeftValueInfo TranslateStructOrClassCtorCallAsLeftValue(const AST::CallExpr& expr); LeftValueInfo TranslateCallExprAsLeftValue(const AST::CallExpr& expr); // Translate the element ref of a compound assignment where the assignee is a member access. void TranslateCompoundAssignmentElementRef(const AST::MemberAccess& ma); LeftValueInfo TranslateExprAsLeftValue(const AST::Expr& expr); Value* GenerateLeftValue(const LeftValueInfo& leftValInfo, const DebugLocation& loc); // check annotation target friend class AnnotationTranslator; Ptr GetImplicitThisParam() const; // ArrayExpr // => VArrayBuilder ( size, item, null ) Ptr InitVArrayByItem(const AST::ArrayExpr& vArray); // => VArrayBuilder ( size, null, initFunc ) Ptr InitVArrayByLambda(const AST::ArrayExpr& vArray); Ptr InitArrayByCollection(const AST::ArrayExpr& array); Ptr InitArrayByItem(const AST::ArrayExpr& array); Ptr InitArrayByLambda(const AST::ArrayExpr& array); // ArrayLit - StructArray Ptr TranslateStructArray(const AST::ArrayLit& array); // ArrayLit - VArray Ptr TranslateVArray(const AST::ArrayLit& array); // TryExpr std::vector GetExceptionsForTry(const AST::TryExpr& tryExpr); Ptr TranslateCatchBlocks(const AST::TryExpr& tryExpr, Ptr retVal, Ptr endBlock); void CreateFinallyExpr(bool hasFinally, Ptr endBlock); Ptr TranslateFinally(const AST::Block& finally, Ptr exceptionBlock); void TranslateFinallyNormalFlows(const AST::Block& finally, const FinallyControlVal& finallyControlInfo); void TranslateFinallyRethrowFlows(const AST::Block& finally); std::pair, Ptr> TranslateExceptionPattern(const AST::Pattern& pattern, Ptr eVal); GenericType* TranslateCompleteGenericType(AST::GenericsTy& ty); // intrinsic special Type* HandleSpecialIntrinsic(IntrinsicKind intrinsicKind, std::vector& args, Type* retTy); void AddMemberMethodToCustomTypeDef(const AST::FuncDecl& decl, CustomTypeDef& def); }; static const std::unordered_map op2ExprKind = { {Cangjie::TokenKind::ADD, ExprKind::ADD}, {Cangjie::TokenKind::SUB, ExprKind::SUB}, {Cangjie::TokenKind::MUL, ExprKind::MUL}, {Cangjie::TokenKind::DIV, ExprKind::DIV}, {Cangjie::TokenKind::MOD, ExprKind::MOD}, {Cangjie::TokenKind::EXP, ExprKind::EXP}, {Cangjie::TokenKind::BITAND, ExprKind::BITAND}, {Cangjie::TokenKind::BITOR, ExprKind::BITOR}, {Cangjie::TokenKind::BITXOR, ExprKind::BITXOR}, {Cangjie::TokenKind::LSHIFT, ExprKind::LSHIFT}, {Cangjie::TokenKind::RSHIFT, ExprKind::RSHIFT}, {Cangjie::TokenKind::LT, ExprKind::LT}, {Cangjie::TokenKind::GT, ExprKind::GT}, {Cangjie::TokenKind::LE, ExprKind::LE}, {Cangjie::TokenKind::GE, ExprKind::GE}, {Cangjie::TokenKind::NOTEQ, ExprKind::NOTEQUAL}, {Cangjie::TokenKind::EQUAL, ExprKind::EQUAL}, }; const static std::unordered_map> packageMap = { {CORE_PACKAGE_NAME, coreIntrinsicMap}, {SYNC_PACKAGE_NAME, cjnativeSyncIntrinsicMap}, {OVERFLOW_PACKAGE_NAME, overflowIntrinsicMap}, {RUNTIME_PACKAGE_NAME, runtimeIntrinsicMap}, {REFLECT_PACKAGE_NAME, reflectIntrinsicMap}, {MATH_PACKAGE_NAME, mathIntrinsicMap}, {INTEROP_PACKAGE_NAME, interOpIntrinsicMap} }; // Below are instrinsics without a source-level declaration, their delcaration should be dinamically generated const static std::unordered_map headlessIntrinsics = { {GET_TYPE_FOR_TYPE_PARAMETER_NAME, IntrinsicKind::GET_TYPE_FOR_TYPE_PARAMETER}, {IS_SUBTYPE_TYPES_NAME, IntrinsicKind::IS_SUBTYPE_TYPES}, }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/AST2CHIR/Utils.h000066400000000000000000000165701510705540100232200ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_AST2CHIR_UTILS_H #define CANGJIE_CHIR_AST2CHIR_UTILS_H #include "cangjie/AST/Node.h" #include "cangjie/Basic/Position.h" #include "cangjie/CHIR/CHIRContext.h" #include "cangjie/CHIR/Type/CHIRType.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/Option/Option.h" namespace Cangjie { namespace CHIR { /** * @brief Translates the function's generic upper bounds. * * @param chirTy The CHIR type to be translated. * @param func The function declaration containing generic information. */ void TranslateFunctionGenericUpperBounds(CHIRType& chirTy, const AST::FuncDecl& func); /** * @brief Adjusts the function type based on the function declaration. * * @param funcType The function type to be adjusted. * @param funcDecl The function declaration used for adjustment. * @param builder The CHIR builder used for type construction. * @param chirType The CHIR type associated with the function. * @return Adjusted function type. */ FuncType* AdjustFuncType(FuncType& funcType, const AST::FuncDecl& funcDecl, CHIRBuilder& builder, CHIRType& chirType); /** * @brief Retrieves the debug location of a variable. * * @param cctx The CHIR context. * @param decl The declaration of the variable. * @return Debug location of the variable. */ DebugLocation GetVarLoc(const CHIRContext& cctx, const AST::Decl& decl); /** * @brief Translates a code location without considering the scope. * * @param context The CHIR context. * @param beginPos The beginning position of the code. * @param endPos The ending position of the code. * @return Translated debug location. */ DebugLocation TranslateLocationWithoutScope( const CHIRContext& context, const Cangjie::Position& beginPos, const Cangjie::Position& endPos); /** * @brief Retrieves the generic parameter types. * * @param decl The declaration containing generic parameters. * @param chirType The CHIR type associated with generics. * @return Vector of generic types. */ std::vector GetGenericParamType(const AST::Decl& decl, CHIRType& chirType); /** * @brief Retrieves the name of the defined package. * * @param funcDecl The function declaration used to find the package name. * @return Name of the defined package. */ std::string GetNameOfDefinedPackage(const AST::FuncDecl& funcDecl); /** * @brief Builds attribute information from an attribute pack. * * @param attr The attribute pack. * @return Attribute information. */ AttributeInfo BuildAttr(const AST::AttributePack& attr); /** * @brief Builds attribute information for a variable declaration. * * @param decl The variable declaration. * @return Attribute information for the variable. */ AttributeInfo BuildVarDeclAttr(const AST::VarDecl& decl); /** * @brief Checks if a function is a mutable struct function. * * @param function The function declaration. * @return True if the function is a mutable struct function, false otherwise. */ bool IsStructMutFunction(const AST::FuncDecl& function); /** * @brief Checks if a global declaration is imported from source code. * * @param decl The declaration. * @param opts The global options. * @return True if the declaration is an imported global declaration, false otherwise. */ bool IsSrcCodeImportedGlobalDecl(const AST::Decl& decl, const GlobalOptions& opts); /** * @brief Checks if a symbol is an imported declaration. * * @param decl The declaration. * @param opts The global options. * @return True if the declaration is an imported symbol, false otherwise. */ bool IsSymbolImportedDecl(const AST::Decl& decl, const GlobalOptions& opts); /** * @brief Checks if a function is local. * * @param func The function declaration. * @return True if the function is local, false otherwise. */ bool IsLocalFunc(const AST::FuncDecl& func); /** * @brief Retrieves the outer declaration containing the given declaration. * * @param decl The declaration. * @return Pointer to the outer declaration. */ AST::Decl* GetOuterDecl(const AST::Decl& decl); /** * @brief Checks if an operator is an overflow operator. * * @param name The name of the operator. * @param type The function type. * @return True if the operator is an overflow operator, false otherwise. */ bool IsOverflowOperator(const std::string& name, const FuncType& type); /** * @brief Retrieves the overflow strategy prefix. * * @param ovf The overflow strategy. * @return Overflow strategy prefix. */ std::string OverflowStrategyPrefix(OverflowStrategy ovf); /** * @brief Checks if an operator is an overflow operator. * * @param name The name of the operator. * @return True if the operator is an overflow operator, false otherwise. */ bool IsOverflowOperator(const std::string& name); /** * @brief Checks if a type can be an integer type. * * @param type The type to check. * @return True if the type can be an integer type, false otherwise. */ bool CanBeIntegerType(const Type& type); /** * @brief Adjusts the type of a variable initialization. * * This function takes in a function type, an outer declaration, a CHIR builder, and a CHIR type, * and returns a pointer to a FuncType. It is used to adjust the type of a variable initialization * based on the given parameters. * * @param funcType The function type. * @param outerDecl The outer declaration. * @param builder The CHIR builder. * @param chirType The CHIR type. * @return A pointer to a FuncType. */ FuncType* AdjustVarInitType( const FuncType& funcType, const AST::Decl& outerDecl, CHIRBuilder& builder, CHIRType& chirType); /** * @brief Try to get a pointer to an object of type T from the cache. * * @param key The key associated with the object to retrieve. * @param cache The cache containing key-value pairs where values are pointers to U. * @return A pointer to the object of type T if found; otherwise, nullptr. */ template inline typename std::enable_if, T*>::type TryGetFromCache(const std::string& key, const std::unordered_map& cache) { auto it = cache.find(key); return it == cache.end() ? nullptr : dynamic_cast(it->second); } void SetCompileTimeValueFlagRecursivly(Func& initFunc); /** * @brief Retrieves the instantiated member type by given type and name. * * @param rootType root type * @param names Member var name. * @param builder The CHIR builder used for building the type. * @return The instantiated member type. */ Type* GetInstMemberTypeByName( const CustomType& rootType, const std::vector& names, CHIRBuilder& builder); /** * @brief Retrieves the instantiated member type by given type and name, checking for read-only. * * @param rootType root type * @param path Member var name. * @param builder The CHIR builder used for building the type. * @return A pair containing the type and a boolean flag indicating read-only status. */ std::pair GetInstMemberTypeByNameCheckingReadOnly( const CustomType& rootType, const std::vector& names, CHIRBuilder& builder); std::pair GetInstMemberTypeByNameCheckingReadOnly( const GenericType& rootType, const std::vector& names, CHIRBuilder& builder); } // namespace CHIR } // namespace Cangjie #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/000077500000000000000000000000001510705540100223425ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/Analysis.h000066400000000000000000000120231510705540100242740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_ANALYSIS_H #define CANGJIE_CHIR_ANALYSIS_ANALYSIS_H #include "cangjie/CHIR/Expression/Terminator.h" #include namespace Cangjie::CHIR { /** * @brief mark status whether value is reachable. */ enum class ReachableKind : uint8_t { UNREACHABLE, REACHABLE }; /** * @brief abstract conception of domain for CHIR IR analysis. * @tparam Domain the specific domain that the analysis pass focus on. */ template class AbstractDomain { public: /** * @brief destructor of abstract domain. */ virtual ~AbstractDomain() { } /** * @brief merge two abstract domain. * @param rhs other abstract domain. * @return whether changed after join function. */ virtual bool Join(const Domain& rhs) { (void)rhs; return false; } /** * @brief whether abstract domain is bottom. * @return results of whether abstract domain is bottom. */ virtual bool IsBottom() const { return kind == ReachableKind::UNREACHABLE; } /** * @brief create string from this abstract domain. * @return created string. */ virtual std::string ToString() const { return ""; } protected: explicit AbstractDomain() : kind(ReachableKind::UNREACHABLE) { } ReachableKind kind; }; /** * @brief abstract analysis pass for CHIR IR analysis. * @tparam Domain the specific domain that the analysis pass focus on. */ template , Domain>>> class Analysis { public: /** * @brief destructor of abstract analysis pass. */ virtual ~Analysis() { } /** * @brief return bottom of domain for analysis. * @return bottom state of analysis, */ virtual Domain Bottom() = 0; /** * @brief abstract function to initialize function entry state. * @param state entry state */ virtual void InitializeFuncEntryState(Domain& state) { (void)state; } /** * @brief abstract function to update state of current lambda. * @param lambda lambda to update state. */ virtual void UpdateCurrentLambda(const Lambda* lambda) { this->currentLambda = lambda; } /** * @brief abstract function to initialize lambda entry state. * @param state entry state */ virtual void InitializeLambdaEntryState(Domain& state) { (void)state; } /// @brief If it's apply to a lambda, we need to clear the state of vars captured by the lambda. virtual void HandleVarStateCapturedByLambda(Domain& state, const Lambda* lambda) { (void)state; (void)lambda; } /// abstract function to update state of lambda expression virtual void PreHandleLambdaExpression(Domain& state, const Lambda* lambda) { (void)state; (void)lambda; } /// abstract function to update state of all expressions. virtual void PropagateExpressionEffect(Domain& state, const Expression* expression) { (void)state; (void)expression; } /// abstract function to update state of all terminators. virtual std::optional PropagateTerminatorEffect(Domain& state, const Terminator* terminator) { (void)state; (void)terminator; return std::nullopt; } /// abstract function static bool Filter(const Func& method) { (void)method; return true; } /// abstract function to check in queue times for one certain block virtual bool CheckInQueueTimes(const Block* block, Domain& curState) { (void)block; (void)curState; return false; } /// get analysis name static std::string GetAnalysisName() { return name; } /// get block limit number to check whether a function should be analysed static std::optional GetBlockLimit() { return blockLimit; } /// manually set state to stable void SetToStable() { isStable = true; } protected: explicit Analysis(const Func* func, bool isDebug = false) : func(func), isDebug(isDebug) { } /// The function that will be analysed. const Func* func; /// Will print the debug message if it's true. bool isDebug; /// The current function that being analysed. It can be a nested function. std::optional currentLambda{std::nullopt}; /// If the result of this analysis is stable. bool isStable{false}; /// The name of this data-flow analysis. static const std::string name; /// Limit on the number of blocks of a function that can be analysed. static const std::optional blockLimit; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/AnalysisWrapper.h000066400000000000000000000145401510705540100256430ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_ANALYSISWRAPPER_H #define CANGJIE_CHIR_ANALYSIS_ANALYSISWRAPPER_H #include "cangjie/CHIR/Analysis/Engine.h" #include "cangjie/CHIR/Package.h" #include "cangjie/Utils/TaskQueue.h" #include namespace Cangjie::CHIR { /// template false type of value analysis. template struct IsValueAnalysis : std::false_type {}; /// template true type of value analysis. template struct IsValueAnalysis> : std::true_type {}; /** * @brief wrapper class of analysis pass, using to do parallel or check works. * @tparam TAnalysis analysis to wrapper. * @tparam TDomain domain of analysis. */ template , TDomain>>, typename = std::enable_if_t, TAnalysis>>> class AnalysisWrapper { public: /** * @brief abstract class for CHIR analysis wrapper. * @param builder CHIR builder for generating IR. */ explicit AnalysisWrapper(CHIRBuilder& builder) : builder(builder) { } /** * @brief main method to analysis from wrapper class. * @tparam Args the args type of analysis. * @param package package to do optimization. * @param isDebug flag whether print debug log. * @param threadNum thread num to do analysis * @param args args of analysis */ template void RunOnPackage(const Package* package, bool isDebug, size_t threadNum, Args&&... args) { if (threadNum == 1) { RunOnPackageInSerial(package, isDebug, std::forward(args)...); } else { RunOnPackageInParallel(package, isDebug, threadNum, std::forward(args)...); } } /** * @brief main method to analysis from wrapper class per function. * @tparam Args the args type of analysis. * @param func function CHIR IR to do optimization. * @param isDebug flag whether print debug log. * @param args args of analysis * @return result of analysis per function */ template std::unique_ptr> RunOnFunc(const Func* func, bool isDebug, Args&&... args) { auto analysis = std::make_unique(func, builder, isDebug, std::forward(args)...); auto engine = Engine(func, std::move(analysis)); return engine.IterateToFixpoint(); } /** * @brief return result of analysis for certain function * @param func function to return analysis result * @return analysis result */ Results* CheckFuncResult(const Func* func) { if (auto it = resultsMap.find(func); it != resultsMap.end()) { return it->second.get(); } else { return nullptr; } } /** * @brief clear analysis result */ void InvalidateAllAnalysisResults() { resultsMap.clear(); } /** * @brief clear analysis result of certain function * @param func function to clear analysis result * @return whether clear is happened */ bool InvalidateAnalysisResult(const Func* func) { if (auto it = resultsMap.find(func); it != resultsMap.end()) { resultsMap.erase(it); return true; } else { return false; } } private: template void RunOnPackageInSerial(const Package* package, bool isDebug, Args&&... args) { if constexpr (IsValueAnalysis::value) { SetUpGlobalVarState(*package, isDebug, std::forward(args)...); } for (auto func : package->GetGlobalFuncs()) { if (ShouldBeAnalysed(*func)) { if (auto res = RunOnFunc(func, isDebug, std::forward(args)...)) { resultsMap.emplace(func, std::move(res)); } } } } template void RunOnPackageInParallel(const Package* package, bool isDebug, size_t threadNum, Args&&... args) { if constexpr (IsValueAnalysis::value) { SetUpGlobalVarState(*package, isDebug, std::forward(args)...); } Utils::TaskQueue taskQueue(threadNum); using ResTy = std::unique_ptr>; std::vector> results; for (auto func : package->GetGlobalFuncs()) { if (ShouldBeAnalysed(*func)) { results.emplace_back(taskQueue.AddTask( [func, isDebug, &args..., this]() { return RunOnFunc(func, isDebug, std::forward(args)...); }, // Roughly use the number of Blocks as the cost of task weight func->GetBody()->GetBlocks().size())); } } taskQueue.RunAndWaitForAllTasksCompleted(); for (auto& result : results) { if (auto res = result.get()) { resultsMap.emplace(res->func, std::move(res)); } } } bool ShouldBeAnalysed(const Func& func) { if constexpr (IsValueAnalysis::value) { if (resultsMap.find(&func) != resultsMap.end()) { return false; } } return TAnalysis::Filter(func); } template void SetUpGlobalVarState(const Package& package, bool isDebug, Args&&... args) { TAnalysis::InitialiseLetGVState(package, builder); for (auto gv : package.GetGlobalVars()) { if (auto init = gv->GetInitFunc(); gv->TestAttr(Attribute::READONLY) && init && resultsMap.find(init) == resultsMap.end()) { // Multiple global vars may be initialised in the same function. // e.g. let (x, y) = (1, 2) resultsMap.emplace(init, RunOnFunc(init, isDebug, std::forward(args)...)); } } } std::unordered_map>> resultsMap; CHIRBuilder& builder; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/Arithmetic.h000066400000000000000000000160311510705540100246050ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_ARITHMETIC_H #define CANGJIE_CHIR_ANALYSIS_ARITHMETIC_H #include #include #include #include #include namespace Cangjie::CHIR { /// Sign extend \p val to a new width \p width int64_t SignExtend64(uint64_t val, unsigned srcWidth); /// Sign extend \p val to a new width \p width template int64_t SignExtend64(T val) { static_assert(std::is_unsigned_v); return SignExtend64(val, sizeof(T) * CHAR_BIT); } /// value 64 constexpr unsigned B64 = 64u; /// whether a value is power of 2. template bool IsPowerOf2(T val) { static_assert(std::is_unsigned_v); return val && !(val & (val - 1)); } /// Count leading zeroes template size_t Clz(T val); /// Count leading ones template size_t Clo(T val); /// Count trailing zeroes template size_t Ctz(T val); /// Count trailing ones template size_t Cto(T val); /// Count the number of set bits template unsigned Popcnt(T val); #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wsign-conversion" #endif namespace { /// count leading zero of a value template struct LeadingZeroesCounter { static_assert(std::is_unsigned_v); #if __cplusplus >= 201907L static inline size_t Count(T val) { return static_cast(std::countl_zero(val)); } #else static size_t Count(T val) { size_t zeroBits = 0; // bisection method for (T shift = std::numeric_limits::digits >> 1; shift; shift >>= 1) { T tmp = val >> shift; if (tmp) { val = tmp; } else { zeroBits |= shift; } } return zeroBits; } #endif }; /// count leading zero of a value implement #if __cplusplus < 201907L && (__GNUC__ >= 4 || defined(_MSC_VER)) // template specialization for 32-bit integers when gcc and msvc builtin available template struct LeadingZeroesCounter { static size_t Count(T val) { #if __has_builtin(__builtin_clz) || defined(__GNUC__) return __builtin_clz(val); #elif define(_MSC_VER) unsigned long index; _BitScanReverse(&index, val); return index ^ (sizeof(T) * CHAR_BIT - 1u); #endif } }; #endif /// count leading zero of a value implement #if __cplusplus < 201907L && (!defined(_MSC_VER) || defined(_M_X64)) // template specialization for 64-bit integers when gcc and msvc builtin available template struct LeadingZeroesCounter { static size_t Count(T val) { #if __has_builtin(__builtin_clzll) return __builtin_clzll(val); #elif define(_MSC_VER) unsigned long index; _BitScanReverse64(&index, val); return index ^ (sizeof(T) * CHAR_BIT - 1u); #endif } }; #endif } // namespace template size_t Clz(T val) { static_assert(std::is_integral_v && !std::is_signed_v); if (!val) { return std::numeric_limits::digits; } return LeadingZeroesCounter::Count(val); } template size_t Clo(T val) { return Clz(~val); } namespace { /// count trailing zero of a value template struct TrailingZeroesCounter { #if __cplusplus >= 201907L static inline size_t Count(T val) { return std::countr_zero(val); } #else static size_t Count(T val) { if (val & 1) { return 0; } // bisection method size_t zeroBits = 0; T shift = std::numeric_limits::digits >> 1; T mask = std::numeric_limits::max() >> shift; while (shift) { if ((val & mask) == 0) { val >>= shift; zeroBits |= shift; } shift >>= 1; mask >>= shift; } return zeroBits; } #endif }; /// count trailing zero of a value implement #if __cplusplus < 201907L && (__GNUC__ >= 4 || defined(_MSC_VER)) // template specialization for 32-bit integers when gcc and msvc builtin available template struct TrailingZeroesCounter { static size_t Count(T val) { #if __has_builtin(__builtin_ctz) || defined(__GNUC__) return __builtin_ctz(val); #elif define(_MSC_VER) unsigned long index; _BitScanForward(&index, val); return index; #endif } }; #endif /// count trailing zero of a value implement #if __cplusplus < 201907L && (!defined(_MSC_VER) || defined(_M_X64)) // template specialization for 64-bit integers when gcc and msvc builtin available template struct TrailingZeroesCounter { static size_t Count(T val) { #if __has_builtin(__builtin_ctzll) return __builtin_ctzll(val); #elif define(_MSC_VER) unsigned long index; _BitScanForward64(&index, val); return index; #endif } }; #endif } // namespace template size_t Ctz(T val) { static_assert(std::is_integral_v && !std::is_signed_v); if (!val) { return std::numeric_limits::digits; } return TrailingZeroesCounter::Count(val); } template size_t Cto(T val) { return Ctz(~val); } namespace { /// template specialization for 64-bit integers template struct PopulationCounter { static unsigned Count(T val) { static_assert(sizeOfT <= 4); #if __cplusplus >= 201907L return static_cast(std::popcount(val)); #elif __GNUC__ >= 4 return __builtin_popcount(val); #elif defined(_MSC_VER) return __popcnt(val); #else uint32_t v = val; v = v - ((v >> 1) & 0x55555555); v = (v & 0x33333333) + ((v >> 2) & 0x33333333); return (((v + (v >> 4)) & 0x0f0f0f0f) * 0x1010101) >> 24; #endif } }; /// template specialization for 64-bit integers template struct PopulationCounter { static unsigned Count(T val) { #if __cplusplus >= 201907L return static_cast(std::popcount(val)); #elif __GNUC__ >= 4 return __builtin_popcountll(val); #elif defined(_MSC_VER) return __popcnt64(val); #else uint64_t v = val; v = v - ((v >> 1) & 0x5555555555555555ULL); v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL); v = (v + (v >> 4)) & 0x0f0f0f0f0f0f0f0fULL; return static_cast(static_cast(v * 0x0101010101010101ULL) >> 56); #endif } }; } // namespace template unsigned Popcnt(T val) { static_assert(std::is_integral_v && !std::is_signed_v); return PopulationCounter::Count(val); } } // namespace Cangjie::CHIR #if defined(__clang__) #pragma clang diagnostic pop #endif #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/BoolDomain.h000066400000000000000000000051331510705540100245400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_BOOL_DOMAIN_H #define CANGJIE_CHIR_ANALYSIS_BOOL_DOMAIN_H #include "cangjie/CHIR/Expression/Terminator.h" namespace Cangjie::CHIR { using PtrSymbol = Ptr; /// Represents all possible values of a CHIRNode that has Ty bool. class BoolDomain { public: /// deleted constructor, use BoolDomain::FromBool instead. BoolDomain(bool) = delete; BoolDomain(const BoolDomain& other); BoolDomain(BoolDomain&& other); BoolDomain& operator=(const BoolDomain& other); BoolDomain& operator=(BoolDomain&& other); ~BoolDomain(); /// Shared instances that represents all the possible values of BoolDomain. static BoolDomain True(); static BoolDomain False(); static BoolDomain Top(); static BoolDomain Bottom(); bool IsTrue() const; bool IsFalse() const; /// every bool is possible. bool IsTop() const; /// every bool is not possible or init state. bool IsBottom() const; /// non top bool IsNonTrivial() const; /// whether state is determined. bool IsSingleValue() const; /// get determined state. bool GetSingleValue() const; /// Construct from bool value static BoolDomain FromBool(bool v); /// operator of bool friend BoolDomain operator&(const BoolDomain& a, const BoolDomain& b); friend BoolDomain operator|(const BoolDomain& a, const BoolDomain& b); friend BoolDomain LogicalAnd(const BoolDomain& a, const BoolDomain& b); friend BoolDomain LogicalOr(const BoolDomain& a, const BoolDomain& b); friend BoolDomain operator!(const BoolDomain& v); friend std::ostream& operator<<(std::ostream& out, const BoolDomain& v); /// union of two states static BoolDomain Union(const BoolDomain& a, const BoolDomain& b); /// whether two states are same bool IsSame(const BoolDomain& domain) const; private: unsigned v; // Construct from integer value \p v. This constructor is private; use True/False/Top/Bottom instead. explicit BoolDomain(unsigned v); }; // operator== on BoolDomain is deleted because there is no definite meaning of equality on BoolDomain, be it the // identity of a boolean domain or the identity of boolean logical value. Use IsTrue/IsTop/... to check the value // of a BoolDomain bool operator==(const BoolDomain& a, const BoolDomain& b) = delete; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/CallGraphAnalysis.h000066400000000000000000000143061510705540100260600ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_CALLGRAPH_ANALYSIS_H #define CANGJIE_CHIR_CALLGRAPH_ANALYSIS_H #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Transformation/Devirtualization.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { class CallGraph { public: class Node; class Edge; /// A node in the call graph for a package. /// Typically represents a function in the call graph. There are also special /// "null" nodes used to represent theoretical entries in the call graph. class Node { public: /// Creates a node for the specified function. explicit Node(Func* func) : func(func) { } Node(const Node&) = delete; Node& operator=(const Node&) = delete; using Iterator = std::vector::iterator; using ConstIterator = std::vector::const_iterator; /// iterator operation of call graph. Iterator Begin(); Iterator End(); ConstIterator Begin() const; ConstIterator End() const; bool Empty() const; /// Returns the function that this call graph node represents. Func* GetFunction() const; /// Adds a edge to current node. void AddCalledEdge(const Edge& edge); /// Delete a edge from current node. void DeleteCalledEdge(const Edge& edge); private: friend class CallGraph; Func* func; /// The edge called by the function of current node. std::vector calledEdges; }; class Edge { public: /// The kind of call in call graph /// Edge kind of apply expression is DIRECT. /// Edge kind of invoke expression or the abstract call from entryNode is VIRTUAL. enum Kind : bool { VIRTUAL = false, DIRECT = true }; Edge() = default; explicit Edge(Node* n, Kind k); inline Node* GetNode() const; inline Kind GetKind() const; bool operator==(const Edge& other) const; private: friend class Node; std::pair edgeValue; }; explicit CallGraph(const Package* package, DevirtualizationInfo& devirtFuncInfo); /// This will insert a new call graph node for /// Func if one does not already exist. Node* GetOrCreateNode(const Func& func); /// Populate call graph node based on the calls inside the associated function's block group. void PopulateCallGraphNode(Node& node, BlockGroup& funcBlockGroup); /// Add a function to the call graph, and link the node to all of the /// functions that it calls. void AddToCallGraph(const Func& func, bool isCalledByEntryNode); /// Returns the Node which is used to represent /// undetermined calls into the callgraph. Node* GetEntryNode() const { return entryNode.get(); } /// Get all the possible callee func of invoke. std::unordered_set GetAllPossibleCalleeOfInvoke( const std::pair>& method) const; private: DevirtualizationInfo& devirtFuncInfo; using FunctionMapTy = std::map>; /// A map from Func* to Node*. FunctionMapTy functionMap; /// This node has edges to all external functions and those internal /// functions that have their address taken. std::unique_ptr entryNode; /// This node has edges to it from all functions making indirect calls /// or calling an external function. std::unique_ptr exitNode; void AddVirtualEdgeToNode(Node& node, const Expression& expression); void AddDirectEdgeToNode(Node& node, const Expression& expression); }; class CallGraphAnalysis { public: explicit CallGraphAnalysis(const Package* package, DevirtualizationInfo& devirtFuncInfo) : package(package), devirtFuncInfo(devirtFuncInfo) { } /// Call Graph Analysis for specific Package. void DoCallGraphAnalysis(bool isDebug); /// The Function list of post-order sequence of SCCs. /// This list is formed the first time we walk the graph. std::vector postOrderSCCFunctionlist; private: const Package* package; DevirtualizationInfo& devirtFuncInfo; /// Build the SCCs for Call Graph. void BuildSCC(const CallGraph& callGraph); /// Print the Call Graph for debug. void PrintCallGraph(const CallGraph& callGraph) const; /// Element of VisitStack during DFS. struct StackElement { CallGraph::Node* node; // The current node pointer. CallGraph::Node::Iterator nextChild; // The next child node, modified inplace during DFS. unsigned minVisited; // Minmum uplink value of all children of Node. StackElement(CallGraph::Node* node, const CallGraph::Node::Iterator& child, unsigned min) : node(node), nextChild(child), minVisited(min) { } bool operator==(const StackElement& other) const { return node == other.node && nextChild == other.nextChild && minVisited == other.minVisited; } }; /// The visit counters used to detect when a complete SCC is on the stack. /// visitNum is the global counter. /// /// nodeVisitNumbers are per-node visit numbers, also used as DFS flags. unsigned visitNum = 0; std::map nodeVisitNumbers; /// Stack holding nodes of the SCC. std::vector sccNodeStack; /// The current SCC std::vector currentSCC; /// DFS stack, Used to maintain the ordering. The top contains the current node, /// the next child to visit, and the mininum unplink value of all child std::vector visitStack; /// A single "visit" within the non-recursive DFS traversal. void DFSVisitOne(CallGraph::Node& node); /// The stack-based DFS traversal; defined below. void DFSVisitChildren(); /// Compute the next SCC using the DFS traversal. void GetNextSCC(); }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/ConstAnalysis.h000066400000000000000000001373321510705540100253160ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_CONST_ANALYSIS_H #define CANGJIE_CHIR_ANALYSIS_CONST_ANALYSIS_H #include "cangjie/CHIR/Analysis/ValueAnalysis.h" #include "cangjie/CHIR/DiagAdapter.h" #include "cangjie/CHIR/OverflowChecking.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { struct ConstValue { enum class ConstKind : uint8_t { UINT, INT, FLOAT, RUNE, BOOL, STRING }; ConstValue() = delete; ConstValue(ConstKind kind); virtual ~ConstValue(); /// join two state of const value, return nullopt if no change happened. virtual std::optional> Join(const ConstValue& rhs) const = 0; /// output to string of const value. virtual std::string ToString() const = 0; /// clone a const value. virtual std::unique_ptr Clone() const = 0; /// get const value kind, such as bool, int, float. ConstKind GetConstKind() const; protected: /// value kind for const value, every subtype has a type kind. ConstKind kind; }; struct ConstBoolVal final : ConstValue { ConstBoolVal(bool val) : ConstValue(ConstKind::BOOL), val(val) { } ~ConstBoolVal() final = default; /// join two state of const value, return nullopt if no change happened. std::optional> Join(const ConstValue& rhs) const override; std::string ToString() const override; std::unique_ptr Clone() const override; bool GetVal() const; private: bool val; }; struct ConstRuneVal final : ConstValue { ConstRuneVal(char32_t val) : ConstValue(ConstKind::RUNE), val(val) { } ~ConstRuneVal() final = default; /// join two state of const value, return nullopt if no change happened. std::optional> Join(const ConstValue& rhs) const override; std::string ToString() const override; std::unique_ptr Clone() const override; char32_t GetVal() const; private: char32_t val; }; struct ConstStrVal final : ConstValue { ConstStrVal(std::string val) : ConstValue(ConstKind::STRING), val(val) { } ~ConstStrVal() final = default; /// join two state of const value, return nullopt if no change happened. std::optional> Join(const ConstValue& rhs) const override; std::string ToString() const override; std::unique_ptr Clone() const override; std::string GetVal() const; private: std::string val; }; struct ConstUIntVal final : ConstValue { ConstUIntVal(uint64_t val) : ConstValue(ConstKind::UINT), val(val) { } ~ConstUIntVal() final = default; /// join two state of const value, return nullopt if no change happened. std::optional> Join(const ConstValue& rhs) const override; std::string ToString() const override; std::unique_ptr Clone() const override; uint64_t GetVal() const; private: uint64_t val; }; struct ConstIntVal final : ConstValue { ConstIntVal(int64_t val) : ConstValue(ConstKind::INT), val(val) { } ~ConstIntVal() final = default; /// join two state of const value, return nullopt if no change happened. std::optional> Join(const ConstValue& rhs) const override; std::string ToString() const override; std::unique_ptr Clone() const override; int64_t GetVal() const; private: int64_t val; }; struct ConstFloatVal final : ConstValue { ConstFloatVal(double val) : ConstValue(ConstKind::FLOAT), val(val) { } ~ConstFloatVal() final = default; /// join two state of const value, return nullopt if no change happened. std::optional> Join(const ConstValue& rhs) const override; std::string ToString() const override; std::unique_ptr Clone() const override; double GetVal() const; private: double val; }; /** * @brief the abstract value domain of const value */ using ConstValueDomain = ValueDomain; /** * @brief the state of const value domain */ using ConstDomain = State; /** * @brief partially specialized analysis import value. */ template <> const std::string Analysis::name; template <> const std::optional Analysis::blockLimit; template <> ConstDomain::ChildrenMap ValueAnalysis::globalChildrenMap; template <> ConstDomain::AllocatedRefMap ValueAnalysis::globalAllocatedRefMap; template <> ConstDomain::AllocatedObjMap ValueAnalysis::globalAllocatedObjMap; template <> std::vector> ValueAnalysis::globalRefPool; template <> std::vector> ValueAnalysis::globalAbsObjPool; template <> ConstDomain ValueAnalysis::globalState; /** * @brief check whether global var need const analysis. * @param gv global var to check. * @return flag global var need analyse */ template <> bool IsTrackedGV>(const GlobalVar& gv); /** * @brief literal value analysis function * @param literalValue input literal value to analyse * @return const value literalValue is. */ template <> ValueDomain HandleNonNullLiteralValue>(const LiteralValue* literalValue); /** * @brief constant value analysis for CHIR IR. */ class ConstAnalysis final : public ValueAnalysis { public: ConstAnalysis() = delete; /** * @brief const analysis constructor * @param func function to analyse * @param builder CHIR builder for generating IR. * @param isDebug flag whether print debug log. * @param diag reporter to report warning or error. */ ConstAnalysis(const Func* func, CHIRBuilder& builder, bool isDebug, DiagAdapter* diag); ~ConstAnalysis() final; private: void PrintDebugMessage(const Expression* expr, const ConstValue* absVal) const; void MarkExpressionAsMustNotOverflow(Expression& expr) const; // ======== Transfer functions for normal expressions based on ExprMajorKind ======== // void HandleNormalExpressionEffect(ConstDomain& state, const Expression* expression) override; enum class ExceptionKind : uint8_t { SUCCESS, FAIL, NA }; void HandleUnaryExpr(ConstDomain& state, const UnaryExpression* unaryExpr, ExceptionKind& exceptionKind); void HandleBinaryExpr(ConstDomain& state, const BinaryExpression* binaryExpr, ExceptionKind& exceptionKind); void HandleOthersExpr(ConstDomain& state, const Expression* expression, ExceptionKind& exceptionKind); // ======================= Transfer functions for terminators ======================= // std::optional HandleTerminatorEffect(ConstDomain& state, const Terminator* terminator) override; std::optional HandleBranchTerminator(const ConstDomain& state, const Branch* branch) const; std::optional HandleMultiBranchTerminator(const ConstDomain& state, const MultiBranch* multi) const; ExceptionKind HandleIntOpWithExcepTerminator(ConstDomain& state, const IntOpWithException* intOp); // ============= Helper functions for Unary/BinaryExpression ============= // template ExceptionKind HandleNegOpOfInt(ConstDomain& state, const TUnary* expr, const ConstValue* constVal) { auto dest = expr->GetResult(); auto os = expr->GetOverflowStrategy(); const T* absVal = static_cast(constVal); using ValType = decltype(absVal->GetVal()); ValType res = 0; ValType val = absVal->GetVal(); bool isOverflow = false; if constexpr (std::is_same_v) { isOverflow = OverflowChecker::IsIntOverflow(dest->GetType()->GetTypeKind(), ExprKind::NEG, 0, val, os, &res); } else if constexpr (std::is_same_v) { isOverflow = OverflowChecker::IsUIntOverflow(dest->GetType()->GetTypeKind(), ExprKind::NEG, 0, val, os, &res); } else { CJC_ABORT(); } if (isOverflow && os == OverflowStrategy::THROWING) { if (this->isStable) { auto builder = diag->DiagnoseRefactor( DiagKindRefactor::chir_arithmetic_operator_overflow, ToRange(expr->GetDebugLocation()), "-"); std::string hint = "-" + dest->GetType()->ToString() + "(" + absVal->ToString() + ")"; builder.AddMainHintArguments(hint); builder.AddNote(GenerateTypeRangePrompt(expr->GetResult()->GetType())); } state.SetToBound(dest, /* isTop = */ true); return ExceptionKind::FAIL; } else { state.Update(dest, std::make_unique(res)); return ExceptionKind::SUCCESS; } } // (a+b), (a-b), (a*b), (a/b), (a%b) template ExceptionKind HandleArithmeticOp(ConstDomain& state, const TBinary* binary, ExprKind kind) { auto lhs = binary->GetLHSOperand(); auto rhs = binary->GetRHSOperand(); CJC_ASSERT(lhs->GetType() == rhs->GetType()); const ConstValue* lhsAbsVal = state.CheckAbstractValue(lhs); const ConstValue* rhsAbsVal = state.CheckAbstractValue(rhs); if (!lhsAbsVal && !rhsAbsVal) { state.SetToBound(binary->GetResult(), /* isTop = */ true); return ExceptionKind::NA; } if (lhs->GetType()->IsInteger()) { auto intTy = StaticCast(lhs->GetType()); if (intTy->IsSigned()) { return HandleArithmeticOpOfInt(state, binary, kind, lhsAbsVal, rhsAbsVal); } else { return HandleArithmeticOpOfInt(state, binary, kind, lhsAbsVal, rhsAbsVal); } } else if constexpr (std::is_same_v) { HandleArithmeticOpOfFloat(state, binary, lhsAbsVal, rhsAbsVal); } return ExceptionKind::NA; } /** * This function handles constant folding on the arithmetic operations whose operands * are integers, which include: * a) BinaryExpression: ADD, SUB, MUL, DIV, MOD * * It will try to calculate the result of the arithmetic operations base on the known * constant information. It will first check if this arithmetic operation is trivial * (e.g. `a * 0` is a trivial arithmetic operation as its result is always zero). * Then, if we have the constant information of all the operands, we will calculate the * result. If an overflow has occurred, an error may be raised depends on the overflow * strategy. If we don't have the enough constant information, the result will be set * to a Top. * * ** note **: * 1. We don't handle EXP binary operations in this funciton. The reason is the type of * its parameter is different. The types of all the other binary expressions' parameters * are the same. See @fn HandleExpOp for details. */ template ExceptionKind HandleArithmeticOpOfInt( ConstDomain& state, const TBinary* expr, ExprKind kind, const ConstValue* lhs, const ConstValue* rhs) { auto dest = expr->GetResult(); auto os = expr->GetOverflowStrategy(); const T* left = static_cast(lhs); const T* right = static_cast(rhs); auto isTrivial = HandleTrivialArithmeticOp(state, expr, kind, left, right); if (isTrivial != ExceptionKind::NA) { return isTrivial; } if (expr->GetLHSOperand() == expr->GetRHSOperand() && kind == ExprKind::SUB) { // `a - a` => 0 state.Update(dest, std::make_unique(0)); return ExceptionKind::SUCCESS; } if (!left || !right) { state.SetToBound(expr->GetResult(), /* isTop = */ true); return ExceptionKind::NA; } using ValType = decltype(left->GetVal()); ValType res = 0; ValType x = left->GetVal(); ValType y = right->GetVal(); bool isOverflow = false; if constexpr (std::is_same_v) { isOverflow = OverflowChecker::IsIntOverflow(dest->GetType()->GetTypeKind(), kind, x, y, os, &res); } else if constexpr (std::is_same_v) { isOverflow = OverflowChecker::IsUIntOverflow(dest->GetType()->GetTypeKind(), kind, x, y, os, &res); } else { CJC_ABORT(); } if (isOverflow && os == OverflowStrategy::THROWING) { RaiseArithmeticOverflowError(expr, kind, left, right); state.SetToBound(dest, /* isTop = */ true); return ExceptionKind::FAIL; } else { state.Update(dest, std::make_unique(res)); return ExceptionKind::SUCCESS; } } /** * This function handles constant folding on the trivial arithmetic operation, which means the result * of it should be a constant. This function also detects the DIV_BY_ZERO error. * This function will return a true if it's a trivial arithmetic operation; otherwise it will return a * false. * * note: `a + 0` is *not* a trivial arithmetic operation, as its result `a` is not a constant; * `a * 0` is a trivial arithmetic operation, as its result is always zero. * * Here is a list of the trivial arithmetic operation this function handles. * a) ADD: none. * b) SUB: none. (We handle the `a - a` in @fn HandleArithmeticOpOfInt.) * c) MUL: `a * 0 = 0` and `0 * a = 0` * d) DIV: `a \ 0` and `0 \ a = 0` * e) MOD: `a % 0`, `0 % a = 0` and `a % 1 == 0` * f) EXP: We don't handle this kind of operations in this funciton. See @fn HandleExpOp. */ template ExceptionKind HandleTrivialArithmeticOp( ConstDomain& state, const TBinary* expr, ExprKind kind, const T* left, const T* right) { auto dest = expr->GetResult(); if (right) { if (right->GetVal() == 0) { if (kind == ExprKind::DIV || kind == ExprKind::MOD) { // `a / 0` or `a % 0` => error RaiseDivByZeroError(expr, kind); state.SetToBound(dest, /* isTop = */ true); return ExceptionKind::FAIL; } else if (kind == ExprKind::MUL) { // `a * 0` => 0 state.Update(dest, std::make_unique(0)); return ExceptionKind::SUCCESS; } } if (right->GetVal() == 1 && kind == ExprKind::MOD) { // `a % 1` => 0 state.Update(dest, std::make_unique(0)); return ExceptionKind::SUCCESS; } } if (left && left->GetVal() == 0) { if (kind == ExprKind::MUL || kind == ExprKind::DIV || kind == ExprKind::MOD) { // `0 * a`, `0 \ a` or `0 % a` state.Update(dest, std::make_unique(0)); return ExceptionKind::SUCCESS; } } return ExceptionKind::NA; } // a**b template ExceptionKind HandleExpOp(ConstDomain& state, const TBinary* binary) { auto dest = binary->GetResult(); if (!dest->GetType()->IsInteger()) { state.SetToBound(dest, /* isTop = */ true); return ExceptionKind::NA; } auto lhs = binary->GetLHSOperand(); auto rhs = binary->GetRHSOperand(); const ConstIntVal* lhsAbsVal = static_cast(state.CheckAbstractValue(lhs)); const ConstUIntVal* rhsAbsVal = static_cast(state.CheckAbstractValue(rhs)); if (rhsAbsVal) { // `a ** 0 = 1` has a higher priority than `0 ** a = 0` as `0 ** 0 = 1` if (rhsAbsVal->GetVal() == 0) { // `a ** 0 = 1` state.Update(dest, std::make_unique(1)); return ExceptionKind::SUCCESS; } } if (lhsAbsVal) { auto val = lhsAbsVal->GetVal(); if (val == 0 || val == 1) { // `0 ** a = 0` and `1 ** a = 1` state.Update(dest, std::make_unique(val)); return ExceptionKind::SUCCESS; } } if (!lhsAbsVal || !rhsAbsVal) { state.SetToBound(dest, /* isTop = */ true); return ExceptionKind::NA; } auto os = binary->GetOverflowStrategy(); int64_t res = 0; bool isOverflow = OverflowChecker::IsExpOverflow(lhsAbsVal->GetVal(), rhsAbsVal->GetVal(), os, &res); if (isOverflow && os == OverflowStrategy::THROWING) { RaiseArithmeticOverflowError(binary, ExprKind::EXP, lhsAbsVal, rhsAbsVal); state.SetToBound(dest, /* isTop = */ true); return ExceptionKind::FAIL; } else { state.Update(dest, std::make_unique(res)); return ExceptionKind::SUCCESS; } } /** * This function handles constant folding on the arithmetic operations whose operands * are floats, which include: * a) BinaryExpression: ADD, SUB, MUL, DIV * * It will try to calculate the result of the arithmetic operations base on the known * constant information. If we have the constant information of all the operands, we * will calculate the result. Otherwise, or if the result is an NaN or an Inf, the * state of the result will be set to Top. * * ** note **: * 1. We don't handle EXP binary operations whose operands are floats. See @fn HandleExpOp. */ void HandleArithmeticOpOfFloat( ConstDomain& state, const BinaryExpression* binaryExpr, const ConstValue* lhs, const ConstValue* rhs) const; template ExceptionKind HandleBitwiseOp(ConstDomain& state, const TBinary* binaryExpr, ExprKind kind) { auto lhs = binaryExpr->GetLHSOperand(); auto rhs = binaryExpr->GetRHSOperand(); auto isLSigned = StaticCast(lhs->GetType())->IsSigned(); auto isRSigned = StaticCast(rhs->GetType())->IsSigned(); const ConstValue* lhsAbsVal = state.CheckAbstractValue(lhs); const ConstValue* rhsAbsVal = state.CheckAbstractValue(rhs); if (isLSigned && isRSigned) { return HandleBitwiseOpOfType(state, binaryExpr, kind, lhsAbsVal, rhsAbsVal); } else if (isLSigned) { return HandleBitwiseOpOfType(state, binaryExpr, kind, lhsAbsVal, rhsAbsVal); } else if (isRSigned) { return HandleBitwiseOpOfType(state, binaryExpr, kind, lhsAbsVal, rhsAbsVal); } else { return HandleBitwiseOpOfType(state, binaryExpr, kind, lhsAbsVal, rhsAbsVal); } } /** * This function handles constant folding on the bitwise operations, which include: * a) BinaryExpression: LSHIFT, RSHIFT, BITAND, BITXOR, BITOR * * It will try to calculate the result of the bitwise operations base on the known * constant information. First, we will do some checking. For bitwise left/right * shift operation, an error will be raised in the following two cases: * a) if the right operand is known to be negative, an error will be raise. * b) if the value of the right operand is GE than the length of the left operand, * an error. * Then we will update the state the result of the operations based on our known * constant information. * * ** note **: * 1. Both parameter @p lhs and @p rhs are required to be non-null values. */ template ExceptionKind HandleBitwiseOpOfType( ConstDomain& state, const TBinary* binaryExpr, ExprKind kind, const ConstValue* lhs, const ConstValue* rhs) { const L* left = static_cast(lhs); const R* right = static_cast(rhs); auto dest = binaryExpr->GetResult(); bool isShiftOp = kind == ExprKind::LSHIFT || kind == ExprKind::RSHIFT; if (!right) { state.SetToBound(dest, /* isTop = */ true); return ExceptionKind::NA; } auto rightVal = right->GetVal(); if constexpr (std::is_same_v) { if (isShiftOp && rightVal < 0) { RaiseNegativeShiftError(binaryExpr, rightVal); state.SetToBound(dest, /* isTop = */ true); return ExceptionKind::FAIL; } } auto lhsOperandType = binaryExpr->GetLHSOperand()->GetType(); if (isShiftOp) { auto lhsValBit = StaticCast(lhsOperandType)->GetBitness(); if (static_cast(rightVal) >= lhsValBit) { RaiseOvershiftError(binaryExpr, rightVal, lhsValBit); state.SetToBound(dest, /* isTop = */ true); return ExceptionKind::FAIL; } } if (!left) { state.SetToBound(dest, /* isTop = */ true); return ExceptionKind::NA; } using LValType = decltype(left->GetVal()); using RValType = decltype(right->GetVal()); #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wsign-conversion" #endif const static std::unordered_map> ops = { {ExprKind::LSHIFT, [](LValType x, RValType y) { return x << y; }}, {ExprKind::RSHIFT, [](LValType x, RValType y) { return x >> y; }}, {ExprKind::BITAND, [](LValType x, RValType y) { return x & y; }}, {ExprKind::BITXOR, [](LValType x, RValType y) { return x ^ y; }}, {ExprKind::BITOR, [](LValType x, RValType y) { return x | y; }}}; #if defined(__clang__) #pragma clang diagnostic pop #endif auto op = ops.find(kind); CJC_ASSERT(op != ops.end()); LValType res = op->second(left->GetVal(), rightVal); res = CutOffHighBits(res, lhsOperandType->GetTypeKind()); state.Update(dest, std::make_unique(res)); return ExceptionKind::SUCCESS; } void HandleRelationalOp(ConstDomain& state, const BinaryExpression* binaryExpr); /** * This function handles constant folding on the relational operations, which include: * a) BinaryExpression: LT, GT, LE, GE, EQUAL, NOTEQUAL * * It will try to calculate the result of the relational operations base on the known * constant information. * * ** note **: * 1. Both parameter @p lhs and @p rhs are required to be non-null values. */ template void HandleRelationalOpOfType( ConstDomain& state, const BinaryExpression* binaryExpr, const ConstValue* lhs, const ConstValue* rhs) { const T* left = static_cast(lhs); const T* right = static_cast(rhs); using ValType = decltype(left->GetVal()); const static std::unordered_map> ops = { {ExprKind::LT, std::less{}}, {ExprKind::LE, std::less_equal{}}, {ExprKind::GT, std::greater{}}, {ExprKind::GE, std::greater_equal{}}, {ExprKind::EQUAL, std::equal_to{}}, {ExprKind::NOTEQUAL, std::not_equal_to{}}, }; auto op = ops.find(binaryExpr->GetExprKind()); CJC_ASSERT(op != ops.end()); bool res = op->second(left->GetVal(), right->GetVal()); state.Update(binaryExpr->GetResult(), std::make_unique(res)); } void HandleLogicalOp(ConstDomain& state, const BinaryExpression* binaryExpr) const; // =============== Error reporting functions for DIV_BY_ZERO or OVERFLOW erros =============== // template void RaiseDivByZeroError(const TBinary* binary, ExprKind kind) { if (this->isStable) { auto& loc = binary->GetDebugLocation(); auto prompt = kind == ExprKind::DIV ? "divide" : "modulo"; auto builder = diag->DiagnoseRefactor(DiagKindRefactor::chir_divisor_is_zero, ToRange(loc), prompt); builder.AddMainHintArguments(prompt); } } template void RaiseArithmeticOverflowError(const TBinary* expr, ExprKind kind, const T* leftVal, const U* rightVal) { if (this->isStable) { auto& loc = expr->GetDebugLocation(); auto ty = expr->GetResult()->GetType(); const static std::unordered_map ops = { {ExprKind::ADD, "+"}, {ExprKind::SUB, "-"}, {ExprKind::MUL, "*"}, {ExprKind::DIV, "/"}, {ExprKind::MOD, "%"}, {ExprKind::EXP, "**"}, }; auto token = ops.find(kind); CJC_ASSERT(token != ops.end()); auto builder = diag->DiagnoseRefactor( DiagKindRefactor::chir_arithmetic_operator_overflow, ToRange(loc), token->second); std::string hint = ty->ToString() + "(" + leftVal->ToString() + ") " + token->second + " " + expr->GetRHSOperand()->GetType()->ToString() + "(" + rightVal->ToString() + ")"; builder.AddMainHintArguments(hint); builder.AddNote(GenerateTypeRangePrompt(expr->GetResult()->GetType())); } } template void RaiseNegativeShiftError(const TBinary* expr, T rightVal) { if (this->isStable) { auto& loc = expr->GetDebugLocation(); auto builder = diag->DiagnoseRefactor(DiagKindRefactor::chir_shift_length_overflow, ToRange(loc)); builder.AddMainHintArguments(std::to_string(rightVal), expr->GetLHSOperand()->GetType()->ToString()); builder.AddNote("right operand can not be negative"); } } template void RaiseOvershiftError(const TBinary* expr, T rightVal, uint64_t leftValBit) { if (this->isStable) { auto& loc = expr->GetDebugLocation(); auto lhsTyString = expr->GetLHSOperand()->GetType()->ToString(); auto builder = diag->DiagnoseRefactor(DiagKindRefactor::chir_shift_length_overflow, ToRange(loc)); builder.AddMainHintArguments(std::to_string(rightVal), lhsTyString); builder.AddNote("the type of left operand is " + lhsTyString + ", the most bits that expected to shift are " + std::to_string(leftValBit - 1)); } } std::string GenerateTypeRangePrompt(Type* type) { const static std::unordered_map> typeToRange = { {Type::TypeKind::TYPE_INT8, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_INT16, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_INT32, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_INT64, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_INT_NATIVE, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_UINT8, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_UINT16, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_UINT32, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_UINT64, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_UINT_NATIVE, {std::numeric_limits::min(), std::numeric_limits::max()}}}; auto [min, max] = typeToRange.at(type->GetTypeKind()); return "range of " + type->ToString() + " is " + std::to_string(min) + " ~ " + std::to_string(max); } // =============== Transfer functions for TypeCast expression =============== // template ExceptionKind HandleTypeCast(ConstDomain& state, const TTypeCast* cast) { auto dest = cast->GetResult(); auto srcTy = cast->GetSourceTy(); if (srcTy->IsInteger()) { if (auto srcAbsVal = state.CheckAbstractValue(cast->GetSourceValue()); srcAbsVal) { return HandleTypecastOfInt(state, cast, srcAbsVal); } } if (srcTy->IsRef() || srcTy->IsClass()) { state.Propagate(cast->GetSourceValue(), dest); return ExceptionKind::NA; } state.SetToTopOrTopRef(dest, /* isRef = */ dest->GetType()->IsRef()); return ExceptionKind::NA; } template ExceptionKind HandleTypecastOfInt(ConstDomain& state, const TTypeCast* cast, const ConstValue* srcAbsVal) { switch (cast->GetSourceTy()->GetTypeKind()) { case Type::TypeKind::TYPE_INT8: { auto constVal = StaticCast(srcAbsVal)->GetVal(); return HandleTypecastOfIntDispatcher(state, cast, static_cast(constVal)); } case Type::TypeKind::TYPE_INT16: { auto constVal = StaticCast(srcAbsVal)->GetVal(); return HandleTypecastOfIntDispatcher(state, cast, static_cast(constVal)); } case Type::TypeKind::TYPE_INT32: { auto constVal = StaticCast(srcAbsVal)->GetVal(); return HandleTypecastOfIntDispatcher(state, cast, static_cast(constVal)); } case Type::TypeKind::TYPE_INT64: { auto constVal = StaticCast(srcAbsVal)->GetVal(); return HandleTypecastOfIntDispatcher(state, cast, static_cast(constVal)); } case Type::TypeKind::TYPE_INT_NATIVE: { auto constVal = StaticCast(srcAbsVal)->GetVal(); return HandleTypecastOfIntDispatcher(state, cast, static_cast(constVal)); } case Type::TypeKind::TYPE_UINT8: { auto constVal = StaticCast(srcAbsVal)->GetVal(); return HandleTypecastOfIntDispatcher(state, cast, static_cast(constVal)); } case Type::TypeKind::TYPE_UINT16: { auto constVal = StaticCast(srcAbsVal)->GetVal(); return HandleTypecastOfIntDispatcher(state, cast, static_cast(constVal)); } case Type::TypeKind::TYPE_UINT32: { auto constVal = StaticCast(srcAbsVal)->GetVal(); return HandleTypecastOfIntDispatcher(state, cast, static_cast(constVal)); } case Type::TypeKind::TYPE_UINT64: { auto constVal = StaticCast(srcAbsVal)->GetVal(); return HandleTypecastOfIntDispatcher(state, cast, static_cast(constVal)); } case Type::TypeKind::TYPE_UINT_NATIVE: { auto constVal = StaticCast(srcAbsVal)->GetVal(); return HandleTypecastOfIntDispatcher(state, cast, static_cast(constVal)); } default: // Currently, we haven't suppport constant folding on a cast operation from an // integer to a float. state.SetToBound(cast->GetResult(), /* isTop = */ true); return ExceptionKind::NA; } } template ExceptionKind HandleTypecastOfIntDispatcher(ConstDomain& state, const TTypeCast* cast, SrcTy val) { auto targetTyKind = cast->GetTargetTy()->GetTypeKind(); switch (targetTyKind) { case Type::TypeKind::TYPE_INT8: return CastOrRaiseExceptionForInt(state, cast, val); case Type::TypeKind::TYPE_INT16: return CastOrRaiseExceptionForInt(state, cast, val); case Type::TypeKind::TYPE_INT32: return CastOrRaiseExceptionForInt(state, cast, val); case Type::TypeKind::TYPE_INT64: return CastOrRaiseExceptionForInt(state, cast, val); case Type::TypeKind::TYPE_INT_NATIVE: return CastOrRaiseExceptionForInt(state, cast, val); case Type::TypeKind::TYPE_UINT8: return CastOrRaiseExceptionForInt(state, cast, val); case Type::TypeKind::TYPE_UINT16: return CastOrRaiseExceptionForInt(state, cast, val); case Type::TypeKind::TYPE_UINT32: return CastOrRaiseExceptionForInt(state, cast, val); case Type::TypeKind::TYPE_UINT64: return CastOrRaiseExceptionForInt(state, cast, val); case Type::TypeKind::TYPE_UINT_NATIVE: return CastOrRaiseExceptionForInt(state, cast, val); default: state.SetToBound(cast->GetResult(), /* isTop = */ true); return ExceptionKind::NA; } } template ExceptionKind CastOrRaiseExceptionForInt(ConstDomain& state, const TTypeCast* cast, SrcTy val) { auto os = cast->GetOverflowStrategy(); TargetTy res = 0; bool isOverflow = OverflowChecker::IsTypecastOverflowForInt(val, &res, os); if (isOverflow && os == OverflowStrategy::THROWING) { RaiseTypeCastOverflowError(cast, val); state.SetToBound(cast->GetResult(), /* isTop = */ true); return ExceptionKind::FAIL; } else { using ConstVal = std::conditional_t, ConstIntVal, ConstUIntVal>; state.Update(cast->GetResult(), std::make_unique(res)); return ExceptionKind::SUCCESS; } } template void RaiseTypeCastOverflowError(const TTypeCast* cast, T srcVal) { if (this->isStable) { auto& loc = cast->GetDebugLocation(); auto builder = diag->DiagnoseRefactor(DiagKindRefactor::chir_typecast_overflow, ToRange(loc)); std::string srcValStr = cast->GetSourceTy()->ToString() + "(" + std::to_string(srcVal) + ")"; builder.AddMainHintArguments(srcValStr, cast->GetTargetTy()->ToString()); builder.AddNote(GenerateTypeRangePrompt(cast->GetTargetTy())); } } // =============== Helper functions for Array/VArrayOutOfBounds Check =============== // const FuncInfo boxInitFunc = FuncInfo("init", "$BOX_RNat5Array", {ANY_TYPE, ANY_TYPE}, NOT_CARE, NOT_CARE); const FuncInfo arrayInitFunc = FuncInfo("init", "Array", {NOT_CARE}, NOT_CARE, "std.core"); const FuncInfo arraySliceFunc = FuncInfo("slice", "Array", {ANY_TYPE, ANY_TYPE, ANY_TYPE}, ANY_TYPE, "std.core"); const FuncInfo arrayBracketsFunc = FuncInfo("[]", "Array", {NOT_CARE}, ANY_TYPE, "std.core"); const FuncInfo arrayGetFunc = FuncInfo("get", "Array", {ANY_TYPE, ANY_TYPE}, ANY_TYPE, "std.core"); const FuncInfo arraySetFunc = FuncInfo("set", "Array", {ANY_TYPE, ANY_TYPE, ANY_TYPE}, ANY_TYPE, "std.core"); const FuncInfo arraySizeGet = FuncInfo("$sizeget", "Array", {ANY_TYPE}, ANY_TYPE, "std.core"); const FuncInfo rangeInitFunc = FuncInfo("init", "Range", {NOT_CARE}, NOT_CARE, "std.core"); static constexpr size_t thisArgIndex = 0; static constexpr size_t lenFieldIndex = 2; void HandleApplyExpr(ConstDomain& state, const Apply* apply, Value* refObj) override; std::optional HandleApplyWithExceptionTerminator( ConstDomain& state, const ApplyWithException* apply, Value* refObj) override; template ExceptionKind HandleApply(ConstDomain& state, const TApply* apply, Value* /* refObj */) { auto calleeFunc = DynamicCast(apply->GetCallee()); if (!calleeFunc) { return ExceptionKind::NA; } if (IsExpectedFunction(*calleeFunc, boxInitFunc)) { HandleBoxedArrayInit(state, apply); } else if (IsExpectedFunction(*calleeFunc, arrayInitFunc)) { HandleArrayInit(state, apply); } else if (IsExpectedFunction(*calleeFunc, arraySliceFunc)) { HandleArraySlice(state, apply); } else if (IsExpectedFunction(*calleeFunc, arraySizeGet)) { HandleArraySizeGet(state, apply); } else if (IsExpectedFunction(*calleeFunc, arrayBracketsFunc) || IsExpectedFunction(*calleeFunc, arrayGetFunc) || IsExpectedFunction(*calleeFunc, arraySetFunc)) { return HandleArrayAccess(state, apply); } else if (IsExpectedFunction(*calleeFunc, rangeInitFunc)) { return HandleRangeInit(state, apply); } return ExceptionKind::NA; } template void HandleBoxedArrayInit(ConstDomain& state, const TApply* apply) { /** * func init(this: Class-_CN7default27$BOX_RNat5ArrayIlEE&, array: Struct-_CNat5ArrayIlE) */ auto args = apply->GetArgs(); CJC_ASSERT(args.size() == 2U); constexpr size_t boxedValueIndex = 0; auto toBeInitedArray = state.GetChild(args[thisArgIndex], boxedValueIndex); CJC_ASSERT(toBeInitedArray); constexpr size_t initArrayArgIndex = 1; auto initArg = args[initArrayArgIndex]; state.Propagate(initArg, toBeInitedArray); } /** * public struct Array { * let rawptr: RawArray * let start: Int64 * let len: Int64 * ... * } * The index of the field `len` is 2. */ template void HandleArrayInit(ConstDomain& state, const TApply* apply) { auto args = apply->GetArgs(); CJC_ASSERT(args.size() > 0); auto lenChild = state.GetChild(args[thisArgIndex], lenFieldIndex); /** * struct Array { * init() { ... } * init(size: Int64, item!: T) { ... } * init(elements: Collection) { ... } * init(size: Int64, initElement: (Int64) -> T) { ... } * init(data: RawArray, start: Int64, len: Int64) { ... } * } * note: Each function has an implicit `this` parameter. */ constexpr size_t initWithOneParameter = 1; constexpr size_t initWithTwoParameter = 2; constexpr size_t initWithThreeParameter = 3; constexpr size_t initWithFourParamter = 4; switch (args.size()) { case initWithOneParameter: { // init() { ... } return state.Update(lenChild, std::make_unique(0)); } case initWithTwoParameter: { // init(elements: Collection) { ... } constexpr size_t collectionParamterIndex = 1; constexpr size_t boxedValIndex = 0; // class $Box_Array { let $value : Array } if (auto boxedVal = state.GetChild(args[collectionParamterIndex], boxedValIndex); boxedVal) { if (auto collectionLen = state.GetChild(boxedVal, lenFieldIndex); collectionLen) { state.Propagate(collectionLen, lenChild); } } return; } case initWithThreeParameter: { // init(size: Int64, item!: T) { ... } // init(size: Int64, initElement: (Int64) -> T) { ... } constexpr size_t sizeParamterIndex = 1; return state.Propagate(args[sizeParamterIndex], lenChild); } case initWithFourParamter: { // init(data: RawArray, start: Int64, len: Int64) { ... } constexpr size_t lenParamterIndex = 3; return state.Propagate(args[lenParamterIndex], lenChild); } default: InternalError("Unsupported array init func"); return; } } template void HandleArraySlice(ConstDomain& state, const TApply* apply) { /** * func slice(start: Int64, len: Int64): Array */ auto args = apply->GetArgs(); CJC_ASSERT(args.size() == 3U); auto lenChild = state.GetChild(apply->GetResult(), lenFieldIndex); CJC_ASSERT(lenChild); constexpr size_t lenParameterIndex = 2; state.Propagate(args[lenParameterIndex], lenChild); } template void HandleArraySizeGet(ConstDomain& state, const TApply* apply) { /** * $sizeget: (Class-$BOX_RNat5ArrayIlE) -> Int64 */ auto args = apply->GetArgs(); CJC_ASSERT(args.size() == 1); if (auto lenChild = state.GetChild(args[thisArgIndex], lenFieldIndex); lenChild) { state.Propagate(lenChild, apply->GetResult()); } } template ExceptionKind HandleArrayAccess(ConstDomain& state, const TApply* apply) { /** * This function handles the following four approaches to accessing an array. * struct Array { * operator func [](index: Int64): T { ... } * operator func [](index: Int64, value!: T): Unit { ... } * func get(index: Int64): Option { ... } * func set(index: Int64, element: T): Unit { ... } * } */ auto args = apply->GetArgs(); CJC_ASSERT(args.size() >= 2U); auto lenNode = state.GetChild(args[0U], 2U); if (!lenNode) { return ExceptionKind::NA; } auto lenVal = state.CheckAbstractValue(lenNode); if (!lenVal) { return ExceptionKind::NA; } CJC_ASSERT(lenVal->GetConstKind() == ConstValue::ConstKind::INT); constexpr size_t indexParameterIndex = 1; auto indexVal = state.CheckAbstractValue(args[indexParameterIndex]); if (!indexVal || indexVal->GetConstKind() != ConstValue::ConstKind::INT) { return ExceptionKind::NA; } auto len = StaticCast(lenVal)->GetVal(); auto index = StaticCast(indexVal)->GetVal(); return RaiseOutOfBoundError(apply, static_cast(len), index); } template ExceptionKind HandleRangeInit(ConstDomain& state, const TApply* apply) { /** * This function handles the following four approaches to accessing an array. * struct Range { * init(start: T, end: T, step: Int64, hasStart: Bool, hasEnd: Bool, isClosed: Bool) { ... } * } */ auto args = apply->GetArgs(); CJC_ASSERT(args.size() == 7U); constexpr size_t stepParameterIndex = 3; auto stepNode = args[stepParameterIndex]; auto stepVal = state.CheckAbstractValue(stepNode); if (!stepVal) { return ExceptionKind::NA; } CJC_ASSERT(stepVal->GetConstKind() == ConstValue::ConstKind::INT); auto step = StaticCast(stepVal)->GetVal(); if (step != 0) { return ExceptionKind::SUCCESS; } if (this->isStable) { auto builder = diag->DiagnoseRefactor(DiagKindRefactor::chir_step_non_zero_range, ToRange(apply->GetDebugLocation())); return ExceptionKind::FAIL; } return ExceptionKind::NA; } template ConstAnalysis::ExceptionKind HandleIntrinsic(ConstDomain& state, const TIntrinsic* intrinsic) { auto dest = intrinsic->GetResult(); state.SetToTopOrTopRef(dest, /* isRef = */ dest->GetType()->IsRef()); if (intrinsic->GetIntrinsicKind() == CHIR::IntrinsicKind::VARRAY_GET) { return HandleVArrayGet(state, intrinsic); } if (intrinsic->GetIntrinsicKind() == CHIR::IntrinsicKind::VARRAY_SET) { return HandleVArraySet(state, intrinsic); } return ExceptionKind::NA; } template ExceptionKind HandleVArrayGet(const ConstDomain& state, const TIntrinsic* intrinsic) { // Intrinsic/varrayGet(arr, indexes...) constexpr size_t varrayOperandIndex = 0; if (intrinsic->GetNumOfOperands() != 2U) { return ExceptionKind::NA; } auto arrNode = intrinsic->GetOperand(varrayOperandIndex); constexpr size_t indexOperandIndex = 1; auto indexNode = intrinsic->GetOperand(indexOperandIndex); auto indexVal = state.CheckAbstractValue(indexNode); if (!indexVal) { return ExceptionKind::NA; } CJC_ASSERT(indexVal->GetConstKind() == ConstValue::ConstKind::INT); auto index = StaticCast(indexVal)->GetVal(); CJC_ASSERT(arrNode->GetType()->IsVArray()); auto len = StaticCast(arrNode->GetType())->GetSize(); return RaiseOutOfBoundError(intrinsic, len, index); } template ExceptionKind HandleVArraySet(const ConstDomain& state, const TIntrinsic* intrinsic) { // Intrinsic/varraySet(arr, value, index) constexpr size_t varrayOperandIndex = 0; CJC_ASSERT(intrinsic->GetNumOfOperands() == 3U); auto arrRefNode = intrinsic->GetOperand(varrayOperandIndex); constexpr size_t indexOperandIndex = 2; auto indexNode = intrinsic->GetOperand(indexOperandIndex); auto indexVal = state.CheckAbstractValue(indexNode); if (!indexVal) { return ExceptionKind::NA; } CJC_ASSERT(indexVal->GetConstKind() == ConstValue::ConstKind::INT); auto index = StaticCast(indexVal)->GetVal(); CJC_ASSERT(arrRefNode->GetType()->IsRef()); auto arrType = StaticCast(arrRefNode->GetType())->GetBaseType(); CJC_ASSERT(arrType->IsVArray()); auto len = StaticCast(arrType)->GetSize(); auto res = RaiseOutOfBoundError(intrinsic, len, index); if (res == ExceptionKind::SUCCESS) { const_cast(intrinsic)->template Set(false); } return res; } template ExceptionKind RaiseOutOfBoundError(const TExpr* expr, size_t len, int64_t index) { if (index < 0) { if (this->isStable) { auto builder = diag->DiagnoseRefactor(DiagKindRefactor::chir_idx_out_of_bounds, ToRange(expr->GetDebugLocation())); builder.AddMainHintArguments("array index can not be negative"); } return ExceptionKind::FAIL; } else if (static_cast(index) >= len) { if (this->isStable) { auto builder = diag->DiagnoseRefactor(DiagKindRefactor::chir_idx_out_of_bounds, ToRange(expr->GetDebugLocation())); auto hint = "array index " + std::to_string(index) + " is past the end of array (which contains " + std::to_string(len) + " elements)"; builder.AddMainHintArguments(hint); } return ExceptionKind::FAIL; } return ExceptionKind::SUCCESS; } DiagAdapter* diag; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/ConstMemberVarCollector.h000066400000000000000000000036631510705540100272610ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file includes de-virtualization information collector for const member. */ #ifndef CANGJIE_CHIR_ANALYSIS_CONST_MEMBER_VAR_COLLECTOR_H #define CANGJIE_CHIR_ANALYSIS_CONST_MEMBER_VAR_COLLECTOR_H #include #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Expression/Expression.h" namespace Cangjie::CHIR { class ConstMemberVarCollector { public: using ConstMemberMapType = std::unordered_map>; explicit ConstMemberVarCollector(const Package* package, ConstMemberMapType& constMemberMap) : package(package), constMemberMap(constMemberMap) { } /// mark member info containing its original type and its derived class struct MemberInfo { MemberInfo() = default; explicit MemberInfo(Type* orig) : oriType(orig) { } Type* oriType = nullptr; Type* derivedType = nullptr; }; /// collect memher which can be using for devirtualization pass. void CollectConstMemberVarType(); /// judge if a member is declared as base type, and only init as one devrived type. void JudgeIfOnlyDerivedType(const CustomTypeDef& def, std::unordered_map& index2Type); /// handle StoreElementRef expression in CHIR IR. void HandleStoreElementRef( const StoreElementRef* stf, const Value* firstParam, std::unordered_map& index2Type) const; /// get source for a location. static const Value* GetSourceTargetRecursively(const Value* value); private: const Package* package = nullptr; ConstMemberMapType& constMemberMap; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/ConstantRange.h000066400000000000000000000367461510705540100253010ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_CONSTANTRANGE_H #define CANGJIE_CHIR_ANALYSIS_CONSTANTRANGE_H #include #include "cangjie/CHIR/Analysis/SInt.h" namespace Cangjie::CHIR { // If represented precisely, the result of some range operations may consist // of multiple disjoint ranges. As only a single range may be returned, any // range covering these disjoint ranges constitutes a valid result, but some // may be more useful than others depending on context. The preferred range // type specifies whether a range that is non-wrapping in the unsigned or // signed domain, or has the smallest size, is preferred. If a signedness is // preferred but all ranges are non-wrapping or all wrapping, then the // smallest set size is preferred. If there are multiple smallest sets, any // one of them may be returned. enum PreferredRangeType { Smallest, Unsigned, Signed }; /** * @brief return preferred type whether use signed or unsigned. * @param useUnsigned flag whether is unsigned. * @return preferred range type which indicate unsigned or unsigned. */ PreferredRangeType PreferFromBool(bool useUnsigned); /// relation operations enum class RelationalOperation : uint8_t { LT, LE, EQ, GT, GE, NE }; /** * @brief A constant range represents a set of integer values between an interval. * It consists of two values of type SInt, representing the lower and upper * bounds of the interval. It is to be noted that the bounds may **wrap around** * the end of the numeric range. * * e.g. for a set {0,1,2,3}, * * [0, 0) = {} = Empty set * [0, 1) = {0} * [0, 2) = {0, 1} * [0, 3) = {0, 1, 2} * * [1, 1) = illegal * [1, 2) = {1} * [1, 3) = {1, 2} * [1, 0) = {1, 2, 3} * * [2, 2) = illegal * [2, 3) = {2} * [2, 0) = {2, 3} * [2, 1) = {2, 3, 0} * * [3, 3) = {3, 0, 1, 2} = Full set * [3, 0) = {3} * [3, 1) = {3, 0} * [3, 2) = {3, 0, 1} * * Note that ConstantRange can be used to represent either signed or unsigned ranges. */ class ConstantRange { public: /// Initialise a full or empty (unsigned)set for the sepcified int width. explicit ConstantRange(IntWidth width, bool full); /// Initialize a range to hold the single specified value. ConstantRange(const SInt& v); /** * @brief Initialise a range of values explicitly. * This will assert out if (lower==upper) && (lower!=min or max value for its type). * It will also assert out if the two SInt's are not the same width. */ ConstantRange(const SInt& l, const SInt& u); /** * @brief Take a binary relationship between two SInt, and a specific value of type SInt, * create a constraint (e.g. SInt is (I16, 3), relationship is NE, the constraint will be >= 3). * Return a set of all values in the full set that satisfy the cosntraint. * * e.g. For a set {0,1,2,3}, * From(EQ, 1) = {1} * From(NE, 1) = {2, 3, 0} * From(GE, 1) = {1, 2, 3} * From(GT, 1) = {2, 3} * From(LE, 1) = {0, 1} * From(LT, 1) = {0} */ static ConstantRange From(RelationalOperation rel, const SInt& v, bool isSigned); /// return empty range static ConstantRange Empty(IntWidth intWidth); /// return full range from minimum to maximum static ConstantRange Full(IntWidth intWidth); /// Create non-empty constant range with the given bounds. If lower and /// upper are the same, a full range is returned. static ConstantRange NonEmpty(const SInt& l, const SInt& r); /// Return the const lower value of this range const SInt& Lower() const&; /// Return the lower value of this range SInt Lower() &&; // Return the upper value of this range const SInt& Upper() const&; /// get upper bound of sInt SInt Upper() &&; /// get width of sInt IntWidth Width() const; /// check whether range is full set bool IsFullSet() const; /// check whether range is empty set bool IsEmptySet() const; /// check whether range is not empty set bool IsNotEmptySet() const; /** * @brief Return true if this set is not a trivial set, which means it contains * no value range information i.e. it's not a full set. * The reason is doing value range analysis on a variable that can be any * value is meaningless. */ bool IsNonTrivial() const; /** * @brief Return true if this set wraps around the unsigned domain. e.g. [3, 1) * Special cases: * Empty set: Not wrapped. * Full set: Not wrapped. * [X, 0) == [X, max]: Not wrapped. (e.g. [2, 0) = {2, 3} for the set {0,1,2,3}) */ bool IsWrappedSet() const; /** * @brief Return true if the exclusive upper bound wraps around the unsigned domain. * Special cases: * Empty set: Not wrapped. * Full set: Not wrapped. * [X, 0): Wrapped. */ bool IsUpperWrapped() const; /** * @brief Return true if this set wraps around the signed domain. * Special cases: * Empty set: Not wrapped. {-1, 0, 1} * Full set: Not wrapped. * [X, signedMin) == [X, signedMax]: Not wrapped. (e.g. [-1, -2) = {-1, 0 ,1, 2} for a set {-2,..,2}) */ bool IsSignWrappedSet() const; /** * @brief Return true if the exclusive upper bound wraps around the signed domain. * Special cases: * Empty set: Not wrapped. * Full set: Not wrapped. * [X, signedMin): Wrapped. */ bool IsUpperSignWrapped() const; /** * @brief Split the range into two if it is a wrapped range with given signess \p asUnsigned. * The behaviour is undefined if \p this is not wrapped. */ std::pair SplitWrapping(bool asUnsigned) const; /// Return true if the specified value is in the set. bool Contains(const SInt& v) const; /// Assume that there is only one value in this range, and return this value. const SInt& GetSingleElement() const; /// Return true if this set contains exactly one member. bool IsSingleElement() const; /// Compare set size of this range with the range rhs. bool IsSizeStrictlySmallerThan(const ConstantRange& rhs) const; /// Return the largest unsigned value contained in the ConstantRange. SInt UMaxValue() const; /// Return the smallest unsigned value contained in the ConstantRange. SInt UMinValue() const; /// Return the largest signed value contained in the ConstantRange. SInt SMaxValue() const; /// Return the smallest signed value contained in the ConstantRange. SInt SMinValue() const; /// Return real max value. SInt MaxValue(bool isUnsigned) const; /// Return real min value. SInt MinValue(bool isUnsigned) const; /// Return true if this range is equal to another range. bool operator==(const ConstantRange& rhs) const; /// Return true if this range is not equal to another range. bool operator!=(const ConstantRange& rhs) const; /// Subtract the specified constant from the endpoints of this constant range. /// e.g. [5, 8) subtract 3 = [2, 5) ConstantRange Subtract(const SInt& v) const; /// Subtract the specified range from this range. /// e.g. [5, 8) diff [6, 9) = [5, 6) ConstantRange Difference(const ConstantRange& rhs) const; /// Return the range that results from the intersection of this range with /// another range. If the intersection is disjoint, such that two results /// are possible, the preferred range is determined by the PreferredRangeType. ConstantRange IntersectWith(const ConstantRange& rhs, PreferredRangeType type = Smallest) const; /// Return the range that results from the union of this range with another /// range. The resultant range is guaranteed to include the elements of both /// sets, but may contain more. For example, [3, 9) union [12, 15) is [3, 15), /// which includes 9, 10, and 11, which were not included in either set before. ConstantRange UnionWith(const ConstantRange& rhs, PreferredRangeType type = Smallest) const; /// Return a new range in the specified bit width, which must be strictly /// larger than the current type. The returned range will correspond to /// the possible range of values if the source range had been zero extended /// to the specified bit width. ConstantRange ZeroExtend(IntWidth width) const; /// Return a new range in the specified bit width, which must be strictly /// larger than the current type. The returned range will correspond to /// the possible range of values if the source range had been sign extended /// to the specified bit width. ConstantRange SignExtend(IntWidth width) const; /// Return a new range in the specified bit width, which must be /// strictly smaller than the current type. The returned range will /// correspond to the possible range of values if the source range had /// been truncated to the specified bit width. /// e.g. width 16 -> 8 /// original lower: 0000 0110 0100 1111 upper: 0011 1000 0011 1001 /// | | | | /// --------- --------- /// In this example, the result range collects the values that can be /// represented by the last 8-bit positions. And the result will be a /// full set with 8-bit width. ConstantRange Truncate(IntWidth dstWidth) const; /** * @brief print sint fomat */ struct Formatter : SIntFormatterBase { const ConstantRange& range; static constexpr char DIVIDOR = '|'; friend std::ostream& operator<<(std::ostream& out, const Formatter& fmt); }; /// const range beauty print format. Formatter ToString(bool asUnsigned = true, Radix radix = Radix::R10) const; #ifndef NDEBUG void Dump(bool asUnsigned) __attribute__((used)); #endif /// Return a new range representing the possible values resulting /// from an addition of a value in this range and a value in rhs. ConstantRange Add(const ConstantRange& rhs) const; /// Return a new range representing the possible values resulting /// from a subtraction of a value in this range and a value in rhs. ConstantRange Sub(const ConstantRange& rhs) const; /// Return a new range representing the possible values resulting /// from a multiplication of a value in this range and a value in rhs, /// treating both this and rhs as unsigned ranges. ConstantRange UMul(const ConstantRange& rhs) const; /// Return a new range representing the possible values resulting /// from a multiplication of a value in this range and a value in rhs, /// treating both this and rhs as signed ranges. ConstantRange SMul(const ConstantRange& rhs) const; /// Return a new range representing the possible values resulting from an /// unsigned remainder operation of a value in this range and a value in rhs. ConstantRange UDiv(const ConstantRange& rhs) const; /// Return a new range representing the possible values resulting from an /// unsigned division of a value in this range and a value in rhs. ConstantRange SDiv(const ConstantRange& rhs) const; /// Return a new range representing the possible values resulting from /// an unsigned remainder operation of a value in this range and a /// value in rhs. ConstantRange URem(const ConstantRange& rhs) const; /// Return a new range representing the possible values resulting /// from a signed remainder operation of a value in this range and a /// value in rhs. ConstantRange SRem(const ConstantRange& rhs) const; /// Perform an unsigned saturating addition of two constant ranges. ConstantRange UAddSat(const ConstantRange& rhs) const; /// Perform a signed saturating addition of two constant ranges. ConstantRange SAddSat(const ConstantRange& rhs) const; /// Perform an unsigned saturating subtraction of two constant ranges. ConstantRange USubSat(const ConstantRange& rhs) const; /// Perform a signed saturating subtraction of two constant ranges. ConstantRange SSubSat(const ConstantRange& rhs) const; /// Perform an unsigned saturating multiplication of two constant ranges. ConstantRange UMulSat(const ConstantRange& rhs) const; /// Perform a signed saturating multiplication of two constant ranges. ConstantRange SMulSat(const ConstantRange& rhs) const; /// Perform a signed saturating division of two constant ranges. ConstantRange SDivSat(const ConstantRange& rhs) const; /// Return a new range that is the complement set of the current set. ConstantRange Inverse() const; /// Calculate absolute value range. If the original range contains signed /// min, then the resulting range will contain signed min if and only if /// intMinIsPoison is false. ConstantRange Abs(bool intMinIsPoison = false) const; /// Negate all the values in this range and return the new range. /// e.g. [2, 5) = {2, 3, 4} => {-4, -3, -2} = [-4, -1) /// note: If the original range contains signed min, then the result /// will also contain signed min. ConstantRange Negate() const; private: SInt lower, upper; /// Create an empty set with the same width. ConstantRange Empty() const; /// Create a full set with the same width. ConstantRange Full() const; /// Return the range that results from the intersection of this range with another range /// assuming both ranges are unwrapped. If the intersection is disjoint, such that two /// results are possible, the preferred range is determined by the PreferredRangeType. ConstantRange IntersectBothWrapped(const ConstantRange& rhs, PreferredRangeType type) const; /// Return the range that results from the intersection of this range with another range /// assuming this range is wrapped and the another is unwrapped. If the intersection is disjoint, /// such that two results are possible, the preferred range is determined by the PreferredRangeType. ConstantRange IntersectWrappedWithUnwrapped(const ConstantRange& rhs, PreferredRangeType type) const; /// Return the range that results from the intersection of this range with another range /// assuming both ranges are unwrapped. ConstantRange IntersectBothUnwrapped(const ConstantRange& rhs) const; /// Return the range that results from the union of this range with another range /// assuming both ranges are wrapped. ConstantRange UnionBothWrapped(const ConstantRange& rhs) const; /// Return the range that results from the union of this range with another range /// assuming this range is wrapped and the another is unwrapped. If the union is disjoint, /// such that two results are possible, the preferred range is determined by the PreferredRangeType. ConstantRange UnionWrappedWithUnwrapped(const ConstantRange& rhs, PreferredRangeType type) const; /// Return the range that results from the union of this range with another range /// assuming both ranges are unwrapped. If the union is disjoint, such that two /// results are possible, the preferred range is determined by the PreferredRangeType. ConstantRange UnionBothUnwrapped(const ConstantRange& rhs, PreferredRangeType type) const; /// Div implement for both sat div and normal div ConstantRange SDivImpl(const ConstantRange& rhs, const std::function& div) const; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/DevirtualizationInfo.h000066400000000000000000000056451510705540100266760ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file includes de-virtualization type analysis. */ #ifndef CANGJIE_CHIR_ANALYSIS_DEVIRTUALIZATION_INFO_H #define CANGJIE_CHIR_ANALYSIS_DEVIRTUALIZATION_INFO_H #include #include "cangjie/CHIR/Analysis/ConstMemberVarCollector.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" #include "cangjie/Option/Option.h" namespace Cangjie::CHIR { /** * @brief type kind for devirtualization pass. */ enum class DevirtualTyKind : uint8_t { SUBTYPE_OF, // Means a type who is the sub-class or sub-interface of another type. EXACTLY, // Means a type exactly. }; /** * @brief collect info for devirtualization pass, such as return map, subtype map. */ class DevirtualizationInfo { public: DevirtualizationInfo() = delete; /// constructor of info collector for devirtualization pass. explicit DevirtualizationInfo(const Package* package, const GlobalOptions& opts) : package(package), opts(opts) { } /** * @brief main method to collect devirtualization info. */ void CollectInfo(); /** * @brief re-collect ret map after other optimization pass. */ void FreshRetMap(); /** * @brief check custom type is internal. * @param def custom type to check. * @return flag whether is internal custom type. */ bool CheckCustomTypeInternal(const CustomTypeDef& def) const; /** * @brief collect const members to devirt. */ void CollectConstMemberVarType(); /** * @brief subType map inheritance info */ struct InheritanceInfo { ClassType* parentInstType; Type* subInstType; }; /** * @brief subtype map from class definition to inheritance info list. */ using SubTypeMap = std::unordered_map>; /// return subtype map. const SubTypeMap& GetSubtypeMap() const; /// return const member type map. const ConstMemberVarCollector::ConstMemberMapType& GetConstMemberMap() const; /// return real runtime return type map. const std::unordered_map& GetReturnTypeMap() const; /// map from type to its custom type definition. std::unordered_map> defsMap; private: void CollectReturnTypeMap(Func& func); SubTypeMap subtypeMap; std::unordered_map realRuntimeRetTyMap; ConstMemberVarCollector::ConstMemberMapType constMemberTypeMap; const Package* package; const GlobalOptions opts; }; } // namespace Cangjie::CHIR #endif // CANGJIE_DEVIRTUALIZATION_INFO_COLLECT_H cangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/Engine.h000066400000000000000000000247621510705540100237330ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_ENGINE_H #define CANGJIE_CHIR_ANALYSIS_ENGINE_H #include "cangjie/CHIR/Analysis/Results.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Value.h" #include #include namespace Cangjie::CHIR { template struct LambdaState { /** * @brief lambda expression to store state. */ const Lambda* lambda; /** * @brief entry states of lambda expression. */ std::unique_ptr> entrySets; LambdaState() = delete; /** * @brief lambda state constructor. * @param lambda lambda to store state. * @param entrySets entry states of lambda. */ LambdaState(const Lambda* lambda, std::unique_ptr> entrySets) : lambda(lambda), entrySets(std::move(entrySets)) { } /** * @brief move constructor of lambda state. * @param rhs state move from. */ LambdaState(LambdaState&& rhs) { this->lambda = rhs.lambda; this->entrySets = std::move(rhs.entrySets); } /** * @brief move operator. * @param rhs state move from. * @return result of move operator. */ LambdaState& operator=(LambdaState&& rhs) { this->lambda = rhs.lambda; this->entrySets = std::move(rhs.entrySets); return *this; } }; template , Domain>::value>> class Engine { friend class Results; public: Engine() = delete; /** * @brief constructor of analysis framework. * @param func the function to analyse. * @param analysis_ analysis pass. */ Engine(const Func* func, std::unique_ptr> analysis_) : func(func), analysis(std::move(analysis_)), entrySets(std::make_unique>()) { } /** * @brief do one iterator to analyse. * @return analysis results. */ std::unique_ptr> IterateToFixpoint() { if (!func->GetBody() || DoesExceedBlockLimit()) { // the perpose of checking entry block is to skip invalid IR after function inline in CHIR // delele it if IR is valid after fix error in function inline return nullptr; } if (func->TestAttr(Attribute::SKIP_ANALYSIS)) { return nullptr; } for (auto bb : func->GetBody()->GetBlocks()) { entrySets->emplace(bb, analysis->Bottom()); } auto& entrybbState = entrySets->at(func->GetEntryBlock()); analysis->InitializeFuncEntryState(entrybbState); #ifdef AnalysisDevDebug std::cout << "==================" << func->GetSrcCodeIdentifier() << ": " << analysis->GetAnalysisName() << "==================" << std::endl; std::cout << "=======after Initialize()=======" << std::endl; for (auto bb : func->GetBody()->GetBlocks()) { std::cout << bb->GetIdentifier() << ": " << entrySets->at(bb).ToString() << std::endl; } #endif IterateSingleUnitToFixpoint(func->GetEntryBlock(), entrySets.get()); #ifdef AnalysisDevDebug std::cout << "=======after IterateToFixpoint()=======" << std::endl; for (auto bb : func->GetBody()->GetBlocks()) { std::cout << bb->GetIdentifier() << ": " << entrySets->at(bb).ToString() << std::endl; } std::cout << std::endl; #endif uint64_t worklistIdx = 0; while (worklistIdx < lambdaWorklist.size()) { auto& lambdaUnit = lambdaWorklist[worklistIdx]; auto& lambda = lambdaUnit.lambda; analysis->UpdateCurrentLambda(lambda); auto lambdaEntrySets = lambdaUnit.entrySets.get(); auto& envState = lambdaEnvState.at(lambda); analysis->HandleVarStateCapturedByLambda(envState, lambda); auto [it, _] = lambdaEntrySets->emplace(lambda->GetEntryBlock(), std::move(envState)); analysis->InitializeLambdaEntryState(it->second); for (auto bb : lambda->GetBody()->GetBlocks()) { lambdaEntrySets->try_emplace(bb, analysis->Bottom()); } #ifdef AnalysisDevDebug std::cout << "==================" << lambda->GetSrcCodeIdentifier() << ": " << analysis->GetAnalysisName() << "==================" << std::endl; std::cout << "=======after Initialize()=======" << std::endl; for (auto bb : lambda->GetBody()->GetBlocks()) { std::cout << bb->GetIdentifier() << ": " << lambdaEntrySets->at(bb).ToString() << std::endl; } #endif IterateSingleUnitToFixpoint(lambda->GetEntryBlock(), lambdaEntrySets); ++worklistIdx; #ifdef AnalysisDevDebug std::cout << "=======after IterateToFixpoint()=======" << std::endl; for (auto bb : lambda->GetBody()->GetBlocks()) { std::cout << bb->GetIdentifier() << ": " << lambdaEntrySets->at(bb).ToString() << std::endl; } std::cout << std::endl; #endif } analysis->SetToStable(); return std::make_unique>( func, std::move(analysis), std::move(entrySets), std::move(lambdaWorklist)); } /** * @brief do one iterator to analyse func body. * @param entryBlock number of entry blocks. * @param entryStates states of entry blocks. */ void IterateSingleUnitToFixpoint(Block* entryBlock, std::unordered_map* entryStates) { std::deque worklist = TopologicalSort(entryBlock); std::unordered_set worklistSet(worklist.begin(), worklist.end()); #ifdef AnalysisDevDebug std::vector temp(worklist.begin(), worklist.end()); std::cout << "=======initialized worklist=======" << std::endl; for (auto bb : temp) { std::cout << bb->GetIdentifier() << " "; } std::cout << std::endl; std::cout << "=======iterating=======" << std::endl; #endif auto state = analysis->Bottom(); while (!worklist.empty()) { auto bb = worklist.front(); worklist.pop_front(); worklistSet.erase(bb); #ifdef AnalysisDevDebug std::cout << "processing " << bb->GetIdentifier() << std::endl; #endif state = entryStates->at(bb); // should not have `&` if (state.IsBottom()) { continue; } #ifdef AnalysisDevDebug std::cout << "entry: " << state.ToString() << std::endl; #endif auto exprs = bb->GetExpressions(); if (exprs.empty()) { continue; } auto terminator = StaticCast(exprs.back()); exprs.pop_back(); for (auto exp : exprs) { if (exp->GetExprKind() == ExprKind::LAMBDA) { auto lambda = StaticCast(exp); analysis->PreHandleLambdaExpression(state, lambda); if (auto it = lambdaEnvState.find(lambda); it != lambdaEnvState.end()) { it->second = state; // should be a copy assignment } else { lambdaEnvState.emplace(lambda, state); } if (lambdaWorklistSet.find(lambda) == lambdaWorklistSet.end()) { lambdaWorklistSet.emplace(lambda); lambdaWorklist.emplace_back(lambda, std::make_unique>()); } } else { // If it's apply to a lambda, we need to clear the state of vars captured by the lambda. if (auto lambda = IsApplyToLambda(exp); lambda) { analysis->HandleVarStateCapturedByLambda(state, lambda); } analysis->PropagateExpressionEffect(state, exp); } #ifdef AnalysisDevDebug std::cout << exp->GetResult()->ToString() << std::endl; std::cout << state.ToString() << std::endl; #endif } if (auto lambda = IsApplyToLambda(terminator); lambda) { analysis->HandleVarStateCapturedByLambda(state, lambda); } std::optional targetSucc = analysis->PropagateTerminatorEffect(state, terminator); #ifdef AnalysisDevDebug std::cout << "exit: " << state.ToString() << std::endl; #endif if (analysis->CheckInQueueTimes(bb, state)) { // if inqueue over certain times, quit analysis and set top to state. // then spread top state to its successors targetSucc = std::nullopt; } auto succs = targetSucc.has_value() ? std::vector{targetSucc.value()} : bb->GetSuccessors(); for (auto succ : succs) { #ifdef AnalysisDevDebug std::cout << succ->GetIdentifier() << " is joining with " << bb->GetIdentifier() << std::endl; std::cout << succ->GetIdentifier() << ":\n" << entryStates->at(succ).ToString() << std::endl; std::cout << bb->GetIdentifier() << ":\n" << state.ToString() << std::endl; #endif auto hasChanged = entryStates->at(succ).Join(state); if (hasChanged && worklistSet.find(succ) == worklistSet.end()) { worklist.push_back(succ); worklistSet.insert(succ); } } } } private: /** * @brief check whether exceed block limit to ensure whether do opt in this function. * @return flag whether exceed block limit. */ bool DoesExceedBlockLimit() { if (!analysis->GetBlockLimit().has_value()) { return false; } return func->GetBody()->GetBlocks().size() > analysis->GetBlockLimit().value(); } const Func* func; std::unique_ptr> analysis; std::unique_ptr> entrySets; std::vector> lambdaWorklist{}; std::unordered_set lambdaWorklistSet{}; std::unordered_map lambdaEnvState{}; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/FlatSet.h000066400000000000000000000063611510705540100240630ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_FLAT_SET_H #define CANGJIE_CHIR_ANALYSIS_FLAT_SET_H #include "cangjie/CHIR/Analysis/Analysis.h" namespace Cangjie::CHIR { /** * @brief abstract domain for pass reach definition analysis * @tparam T abstract value */ template class FlatSet : AbstractDomain> { public: /** * @brief domain kind, Bottom means non-initialization state or non-reachable state, top means all possible state, * Elem means normal state. */ enum class FlatSetKind : uint8_t { Bottom, Elem, Top }; /** * @brief constructor of Flat Set domain. * @param isTop flag whether create a top domain, otherwise a bottom one. */ explicit FlatSet(bool isTop) : flatSetKind(isTop ? FlatSetKind::Top : FlatSetKind::Bottom) { } /** * @brief constructor of Flat Set domain. * @param elem normal value state. */ explicit FlatSet(T elem) : flatSetKind(FlatSetKind::Elem), elem(elem) { } /** * @brief destructor. */ virtual ~FlatSet() { } /** * @brief join domains of flat set. * @param rhs other domain to join with. * @return whether changed after join. */ bool Join(const FlatSet& rhs) override { if (flatSetKind == FlatSetKind::Top || rhs.flatSetKind == FlatSetKind::Bottom) { return false; } if (rhs.flatSetKind == FlatSetKind::Top) { flatSetKind = FlatSetKind::Top; return true; } if (flatSetKind == FlatSetKind::Bottom) { *this = rhs; return true; } if (this->elem == rhs.elem) { return false; } else { flatSetKind = FlatSetKind::Top; return true; } } /// judge whether abstract domain is bottom. bool IsBottom() const override { return flatSetKind == FlatSetKind::Bottom; } /// judge whether abstract domain is top. bool IsTop() const { return flatSetKind == FlatSetKind::Top; } /// output string of flat set abstract domain. std::string ToString() const override { if (flatSetKind == FlatSetKind::Top) { return "top"; } else if (flatSetKind == FlatSetKind::Bottom) { return "bottom"; } else { return elem->ToString(); } } /// set to top or bottom void SetToBound(bool isTop) { flatSetKind = isTop ? FlatSetKind::Top : FlatSetKind::Bottom; } /// get elem value std::optional GetElem() const { if (flatSetKind == FlatSetKind::Elem) { return elem; } else { return std::nullopt; } } // update elem value to domain void UpdateElem(T ele) { flatSetKind = FlatSetKind::Elem; elem = ele; } protected: explicit FlatSet() : AbstractDomain>() { } FlatSetKind flatSetKind; T elem; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/GenKillAnalysis.h000066400000000000000000000121421510705540100255440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_GENKILL_ANALYSIS_H #define CANGJIE_CHIR_ANALYSIS_GENKILL_ANALYSIS_H #include #include "cangjie/CHIR/Analysis/Analysis.h" namespace Cangjie::CHIR { /** * @brief A GenKill Analysis is an analysis that track the states of facts of a certain kind. It focuses if a fact * is true, or not. `Gen` a fact means a fact becomes true, and `Kill` a fact means a fact becomes false. * In a GenKill Analysis, with the execution of different expressions, a fact mey be `Gen`-ed or `Kill`-ed. * * For example, if we are doing a maybe-uninit analysis(we focus on the variables that maybe unintialised), * when we meet an Allocate expression, we could know that this Allocation creates an uninitialised variable, * and this is a `Gen` point; when we meet a Store expression which puts a value to the place created by * the Allocate expression before, we could know that this variable has been initialised, and this is a `Kill` * point. * * A GenKill Analysis must be a must analysis or a maybe analysis. A `must` analysis determines whether a * fact is true on *all* paths through the program. And a `maybe` analysis determines wheter a fact is true * on *some* path throught the program. Thus, a `must` analysis and a `maybe` analysis behave differently * on the `Join` operation. */ enum class AnalysisKind { MUST, MAYBE }; /** * @brief generate and kill domain * @tparam Domain abastract value for generate and kill domain */ template class GenKillDomain : public AbstractDomain { public: GenKillDomain() = delete; /** * @brief gen kill domain constructor * @param domainSize domain size */ GenKillDomain(size_t domainSize) : AbstractDomain() { states = std::vector(domainSize); } ~GenKillDomain() override { } /** * @brief join domain with other domain. * @param rhs other domain to join with. * @return whether changed after join. */ bool Join(const Domain& rhs) override { this->kind = ReachableKind::REACHABLE; auto size = states.size(); bool changed = false; auto& rhsStates = rhs.states; for (size_t i = 0; i < size; ++i) { bool oldState = states[i]; states[i] = (mustOrMaybe == AnalysisKind::MUST) ? (oldState & rhsStates[i]) : (oldState | rhsStates[i]); changed |= oldState ^ states[i]; } return changed; } /// output string of generate and kill abstract domain. std::string ToString() const override { if (this->kind == ReachableKind::UNREACHABLE) { return "Unreachable"; } else { std::stringstream ss; ss << "Reachable("; for (auto b : states) { ss << b << " "; } ss << ")"; return ss.str(); } } /// set state of input index to generate. inline void Gen(const size_t index) { states[index] = true; } /// set state of input index to kill. inline void Kill(const size_t index) { states[index] = false; } /// set all states to generate. void GenAll() { auto size = states.size(); states.assign(size, true); } /// set all states to kill. void KillAll() { auto size = states.size(); states.assign(size, false); } /// propagate state from index from to index to. inline void PropagateFrom(const size_t from, const size_t to) { states[to] = states[from]; } /// check whether state of input index is generate. inline bool IsTrueAt(const size_t index) const { return states[index]; } /** * @brief The kind of a GenKillAnalysis. A GenKillAnalysis must be a must analysis or a maybe analysis. * A `must` analysis determines whether a fact is true on *all* paths through the program. * A `maybe` analysis determines wheter a fact is true on *some* path throught the program. */ static const AnalysisKind mustOrMaybe; protected: /// The current abstract state. std::vector states; }; /** * @brief analysis pass of generate and kill domain. * @tparam Domain abstract domain. */ template , Domain>>> class GenKillAnalysis : public Analysis { public: GenKillAnalysis() = delete; /// constructor of generate and kill analysis. explicit GenKillAnalysis(const Func* func) : Analysis(func) { } /// destructor of generate and kill analysis. ~GenKillAnalysis() override { } /// get domain size from analysis. size_t GetDomainSize() { return domainSize; } protected: /** The size of the GenKill domain. */ size_t domainSize; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/GetOrThrowResultAnalysis.h000066400000000000000000000064111510705540100274640ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_GETORTHROW_RESULT_ANALYSIS_H #define CANGJIE_CHIR_ANALYSIS_GETORTHROW_RESULT_ANALYSIS_H #include "cangjie/CHIR/Analysis/Analysis.h" #include "cangjie/CHIR/Analysis/FlatSet.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { /** * @brief domain definition of get or throw optimization pass. */ class GetOrThrowResultAnalysis; class GetOrThrowResultDomain final : public AbstractDomain { public: friend class GetOrThrowResultAnalysis; GetOrThrowResultDomain() = delete; /** * @brief constructor for domain definition of get or throw optimization pass. * @param ArgIdxMap index map, value of domain. */ GetOrThrowResultDomain(std::unordered_map* ArgIdxMap); ~GetOrThrowResultDomain() { } /// join two domains bool Join(const GetOrThrowResultDomain& rhs) override; /// output string of get or throw domain. std::string ToString() const override; /** * @brief check location's domain result. * @param location location value to check its domain. * @return return apply if location has its domain. */ const Apply* CheckGetOrThrowResult(const Value* location) const; private: /// results of get or throw analysis std::vector> GetOrThrowResults; /// from location to index std::unordered_map* ArgIdxMap; }; template <> const std::string Analysis::name; template <> const std::optional Analysis::blockLimit; class GetOrThrowResultAnalysis final : public Analysis { public: GetOrThrowResultAnalysis() = delete; /** * @brief constructor of get or throw analysis. * @param func function to analyse. * @param isDebug flag whether print debug log. */ GetOrThrowResultAnalysis(const Func* func, bool isDebug); ~GetOrThrowResultAnalysis() { } /// return Bottom of GetOrThrowResultDomain GetOrThrowResultDomain Bottom() override; /** * @brief use input state to initialize entry state of functions. * @param state input entry state of analysing function. */ void InitializeFuncEntryState(GetOrThrowResultDomain& state) override; /** * @brief propagate state to next expression. * @param state current state of this function. * @param expression next expression to analyse. */ void PropagateExpressionEffect(GetOrThrowResultDomain& state, const Expression* expression) override; /** * @brief propagate state to next terminator. * @param state current state of this function. * @param terminator next terminator to analyse. * @return blocks return after analysis. */ std::optional PropagateTerminatorEffect( GetOrThrowResultDomain& state, const Terminator* terminator) override; private: /// from location to index std::unordered_map ArgIdxMap; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/MaybeInitAnalysis.h000066400000000000000000000104451510705540100261040ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_MAYBE_INIT_ANALYSIS_H #define CANGJIE_CHIR_ANALYSIS_MAYBE_INIT_ANALYSIS_H #include "cangjie/CHIR/Analysis/MaybeUninitAnalysis.h" namespace Cangjie::CHIR { class MaybeInitAnalysis; /** * @brief maybe init domain, indicate a state whether values have been init. */ class MaybeInitDomain : public GenKillDomain { friend class MaybeInitAnalysis; public: MaybeInitDomain() = delete; /** * @brief constructor of maybe init domain. * @param domainSize domain size of one function. * @param ctorInitInfo extra info for init function check. * @param allocateIdxMap allocate map to analysis in this pass. */ MaybeInitDomain(size_t domainSize, const ConstructorInitInfo* ctorInitInfo, std::unordered_map* allocateIdxMap); /// constructor of maybe init domain. ~MaybeInitDomain() override { } /** * @brief check whether location is maybe inited. * @param location location to check status. * @return status if location is maybe inited. */ std::optional IsMaybeInitedAllocation(const Value* location) const; /** * @brief extra info to indicate if status of value in init function or its super class. */ enum class InitedMemberKind { SUPER_MEMBER, LOCAL_MEMBER, NA }; /** * @brief check if member var is init in init function. * @param memberIndex index of member var. * @return info to indicate if member var is in super class or this class. */ InitedMemberKind IsMaybeInitedMember(size_t memberIndex) const; private: /// init local member to inited. void SetAllLocalMemberInited(); /// extra info for init function check. const ConstructorInitInfo* ctorInitInfo; /// allocate map from location to index. std::unordered_map* allocateIdxMap; }; /** * @brief partially specialized member to MaybeInitDomain of analysis */ template <> const std::string Analysis::name; template <> const std::optional Analysis::blockLimit; template <> const AnalysisKind GenKillDomain::mustOrMaybe; /** * @brief may be init analysis, analyse a status which values whether have been init. */ class MaybeInitAnalysis final : public GenKillAnalysis { public: MaybeInitAnalysis() = delete; /** * @brief may be init analysis constructor. * @param func function to analyse. * @param ctorInitInfo extra info for init function check. */ MaybeInitAnalysis(const Func* func, const ConstructorInitInfo* ctorInitInfo); /// may be init analysis destructor. ~MaybeInitAnalysis() final { } /// return Bottom of MaybeInitDomain MaybeInitDomain Bottom() override; /** * @brief use input state to initialize entry state of functions. * @param state input entry state of analysing function. */ void InitializeFuncEntryState(MaybeInitDomain& state) override; /** * @brief propagate state to next expression. * @param state current state of this function. * @param expression next expression to analyse. */ void PropagateExpressionEffect(MaybeInitDomain& state, const Expression* expression) override; /** * @brief propagate state to next terminator. * @param state current state of this function. * @param expression next terminator to analyse. * @return blocks return after analysis. */ std::optional PropagateTerminatorEffect(MaybeInitDomain& state, const Terminator* expression) override; private: void HandleAllocateExpr(MaybeInitDomain& state, const Allocate* allocate); void HandleStoreExpr(MaybeInitDomain& state, const Store* store); void HandleStoreElemRefExpr(MaybeInitDomain& state, const StoreElementRef* store) const; void HandleApplyExpr(MaybeInitDomain& state, const Apply* apply) const; const ConstructorInitInfo* ctorInitInfo; std::unordered_map allocateIdxMap{}; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/MaybeUninitAnalysis.h000066400000000000000000000134161510705540100264500ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_MAYBE_UNINIT_ANALYSIS_H #define CANGJIE_CHIR_ANALYSIS_MAYBE_UNINIT_ANALYSIS_H #include "cangjie/CHIR/Analysis/GenKillAnalysis.h" #include "cangjie/CHIR/Type/CustomTypeDef.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { /** * @brief info for analysing init functions. */ struct ConstructorInitInfo { CustomTypeDef* thisCustomDef; ClassDef* superClassDef; /// If the function is the constructor of a class, the numbers of members in its super class. size_t superMemberNums = 0; /// all members = superMemberNums + localMemberNums. size_t localMemberNums = 0; }; class MaybeUninitAnalysis; /** * @brief maybe not init domain, indicate a status which values whether have not been init. */ class MaybeUninitDomain : public GenKillDomain { friend class MaybeUninitAnalysis; public: MaybeUninitDomain() = delete; /** * @brief constructor of maybe not init domain. * @param domainSize domain size of one function. * @param ctorInitInfo extra info for init function check. * @param allocateIdxMap allocate map to analysis in this pass. */ MaybeUninitDomain(size_t domainSize, const ConstructorInitInfo* ctorInitInfo, std::unordered_map* allocateIdxMap); /// constructor of maybe not init domain. ~MaybeUninitDomain() override { } /** * @brief join two domain to one. * @param rhs other domain to join. * @return return true is join state is changed. */ bool Join(const MaybeUninitDomain& rhs) override; /** * @brief check whether location is maybe not inited. * @param location location to check status. * @return status if location is maybe not inited. */ std::optional IsMaybeUninitedAllocation(const Value* location) const; /** * @brief get position of certain location. * @param location location to get position. * @return position. */ const std::set& GetMaybeInitedPos(const Value* location) const; /** * @brief extra info to indicate if status of value in init function or its super class. */ enum class UninitedMemberKind { SUPER_MEMBER, LOCAL_MEMBER, NA }; /** * @brief check if member var is not init in init function. * @param memberIndex index of member var. * @return info to indicate if member var is in super class or this class. */ UninitedMemberKind IsMaybeUninitedMember(size_t memberIndex) const; /// get position of certain member index. const std::set& GetMaybeInitedPos(size_t memberIndex) const; /// return all uninited local members including super class. std::vector GetMaybeUninitedLocalMembers() const; private: /// init local member to uninited. void SetAllLocalMemberInited(); /// extra info for init function check. const ConstructorInitInfo* ctorInitInfo; /// allocate map from location to index. std::unordered_map* allocateIdxMap; /// maybe not init position array. std::vector> maybeInitedPos; }; /** * @brief partially specialized member to MaybeUninitDomain of analysis */ template <> const std::string Analysis::name; template <> const std::optional Analysis::blockLimit; template <> const AnalysisKind GenKillDomain::mustOrMaybe; /** * @brief maybe init analysis, analyse a status which values whether have not been init. */ class MaybeUninitAnalysis final : public GenKillAnalysis { public: MaybeUninitAnalysis() = delete; /** * @brief maybe not init analysis constructor. * @param func function to analyse. * @param ctorInitInfo extra info for init function check. */ MaybeUninitAnalysis(const Func* func, const ConstructorInitInfo* ctorInitInfo); /// maybe not init analysis destructor. ~MaybeUninitAnalysis() final { } /// return Bottom of MaybeUninitDomain MaybeUninitDomain Bottom() override; /** * @brief use input state to initialize entry state of functions. * @param state input entry state of analysing function. */ void InitializeFuncEntryState(MaybeUninitDomain& state) override; /** * @brief propagate state to next expression. * @param state current state of this function. * @param expression next expression to analyse. */ void PropagateExpressionEffect(MaybeUninitDomain& state, const Expression* expression) override; /** * @brief propagate state to next terminator. * @param state current state of this function. * @param expression next terminator to analyse. * @return blocks return after analysis. */ std::optional PropagateTerminatorEffect(MaybeUninitDomain& state, const Terminator* expression) override; private: void HandleAllocateExpr(MaybeUninitDomain& state, const Allocate* allocate); void HandleStoreExpr(MaybeUninitDomain& state, const Store* store); void HandleStoreElemRefExpr(MaybeUninitDomain& state, const StoreElementRef* store) const; void HandleApplyExpr(MaybeUninitDomain& state, const Apply* apply) const; const ConstructorInitInfo* ctorInitInfo; std::unordered_map allocateIdxMap{}; }; /** * init allocate map for VIC analysis from all expressions */ void SaveAllocateMap( const BlockGroup& body, size_t& allocateIdx, std::unordered_map& allocateIdxMap); } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/ReachingDefinitionAnalysis.h000066400000000000000000000136631510705540100277610ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_REACHING_DEFINITION_ANALYSIS_H #define CANGJIE_CHIR_ANALYSIS_REACHING_DEFINITION_ANALYSIS_H #include "cangjie/CHIR/Analysis/Analysis.h" #include "cangjie/CHIR/Analysis/FlatSet.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { class ReachingDefinitionAnalysis; /** * @brief reaching definition domain, indicate a state whether values is unreachable. */ class ReachingDefinitionDomain final : public AbstractDomain { friend class ReachingDefinitionAnalysis; public: ReachingDefinitionDomain() = delete; /** * @brief constructor of ReachingDefinitionDomain * @param allocateIdxMap allocate map to analysis in this pass. */ ReachingDefinitionDomain(std::unordered_map* allocateIdxMap); /// constructor of ReachingDefinitionDomain. ~ReachingDefinitionDomain() final { } /** * @brief join two domain to one. * @param rhs other domain to join. * @return return true is join state is changed. */ bool Join(const ReachingDefinitionDomain& rhs) override; /// return formatted info string. std::string ToString() const override; /** * @brief check store location is reachable * @param location location to check * @return store expression if location is reached, otherwise nullptr */ const Store* CheckReachingDef(const Value* location) const; /** * @brief check load location is reachable * @param location location to check * @return load expression if location is reached, otherwise nullptr */ const Load* CheckReachingLoadDef(const Value* location) const; private: /// reaching status of store expressions. std::vector> reachingDefs; /// reaching status of load expressions. std::vector> reachingLoadDefs; /// allocate index map of one function. std::unordered_map* allocateIdxMap; }; /** * @brief partially specialized member to ReachingDefinitionDomain of analysis */ template <> const std::string Analysis::name; template <> const std::optional Analysis::blockLimit; /** * @brief reaching definition analysis, analyse allocate is reachable. */ class ReachingDefinitionAnalysis final : public Analysis { public: ReachingDefinitionAnalysis() = delete; /** * @brief constructor for reaching definition analysis. * @param func function to analyse. */ explicit ReachingDefinitionAnalysis(const Func* func); /// reaching definition analysis destructor. ~ReachingDefinitionAnalysis() final { } /// return Bottom of ReachingDefinitionDomain ReachingDefinitionDomain Bottom() override; /** * @brief use input state to initialize entry state of functions. * @param state input entry state of analysing function. */ void InitializeFuncEntryState(ReachingDefinitionDomain& state) override; /// special state work for lambda expression. void HandleVarStateCapturedByLambda(ReachingDefinitionDomain& state, const Lambda* lambda) override; /** * @brief propagate state to next expression. * @param state current state of this function. * @param expression next expression to analyse. */ void PropagateExpressionEffect(ReachingDefinitionDomain& state, const Expression* expression) override; /** * @brief propagate state to next terminator. * @param state current state of this function. * @param terminator next terminator to analyse. * @return blocks return after analysis. */ std::optional PropagateTerminatorEffect( ReachingDefinitionDomain& state, const Terminator* terminator) override; private: void HandleStoreExpr(ReachingDefinitionDomain& state, const Store* store); void HanldeStoreElemRefExpr(ReachingDefinitionDomain& state, const StoreElementRef& storeElemRef); void HandleGetElemRefExpr(ReachingDefinitionDomain& state, const GetElementRef* getElemRef); void HandleLoadExpr(ReachingDefinitionDomain& state, const Load* load); template void HandleApplyExpr(ReachingDefinitionDomain& state, const TApply* apply) { // If it's a apply to a mut function of a tracked struct. if (auto callee = apply->GetCallee(); callee->TestAttr(Attribute::MUT)) { auto structArg = apply->GetArgs()[0]; if (!StaticCast(structArg->GetType())->GetBaseType()->IsStruct()) { return; } if (auto it = allocateIdxMap.find(structArg); it != allocateIdxMap.end()) { state.reachingDefs[it->second].SetToBound(/* isTop = */ true); state.reachingLoadDefs[it->second].SetToBound(/* isTop = */ true); } } } template void HandleIntrinsicExpr(ReachingDefinitionDomain& state, const TIntrinsic* intrinsic) { // If the tracked object is the parameter of the inout intrinsic if (intrinsic->GetIntrinsicKind() == CHIR::IntrinsicKind::INOUT_PARAM) { CJC_ASSERT(intrinsic->GetNumOfOperands() == 1); auto operand = intrinsic->GetOperand(0); if (auto it = allocateIdxMap.find(operand); it != allocateIdxMap.end()) { state.reachingDefs[it->second].SetToBound(/* isTop = */ true); state.reachingLoadDefs[it->second].SetToBound(/* isTop = */ true); } } } /// allocate index map of one function. std::unordered_map allocateIdxMap; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/Results.h000066400000000000000000000146651510705540100241700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_RESULTS_H #define CANGJIE_CHIR_ANALYSIS_RESULTS_H #include "cangjie/CHIR/Analysis/Analysis.h" #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Value.h" #include namespace Cangjie::CHIR { template struct LambdaState; /** * @brief abstract results after analysis * @tparam Domain abstract domain which results is. */ template , Domain>::value>> class Results { public: Results() = delete; /** * @brief constructor of abstract results for analysis * @param func function to analyse. * @param analysis analysis pass. * @param entrySets entry state from block to domain. * @param lambdaResults lambda results if func has lambda. */ Results(const Func* func, std::unique_ptr> analysis, std::unique_ptr> entrySets, std::vector> lambdaResults) : func(func), analysis(std::move(analysis)), entrySets(std::move(entrySets)), lambdaResults(std::move(lambdaResults)) { for (auto& lambdaRes : this->lambdaResults) { lambdaResultsMap.emplace(lambdaRes.lambda, lambdaRes.entrySets.get()); } } /** * @brief main method to generate results * @param actionBeforeVisitExpr lambda function before visit. * @param actionAfterVisitExpr lambda function after visit. * @param actionOnTerminator lambda function on terminator. */ void VisitWith(std::function actionBeforeVisitExpr, std::function actionAfterVisitExpr, std::function)> actionOnTerminator) { for (auto bb : func->GetBody()->GetBlocks()) { VisitBlockWith(actionBeforeVisitExpr, actionAfterVisitExpr, actionOnTerminator, *bb, entrySets.get()); } for (auto& lambdaUnit : lambdaResults) { if (!lambdaUnit.lambda->GetBody()) { // This lambda maybe in a dead block and thus it has been deleted. continue; } for (auto bb : lambdaUnit.lambda->GetBody()->GetBlocks()) { VisitBlockWith( actionBeforeVisitExpr, actionAfterVisitExpr, actionOnTerminator, *bb, lambdaUnit.entrySets.get()); } } } const Func* func; private: // Add only cangjie native backend for cjmp #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::vector GetBlocksToAnalyse(const Expression& expr) const { if (auto initVarFunc = TryGetInstanceVarInitFromApply(expr)) { return initVarFunc->GetBody()->GetBlocks(); } return {}; } void VisitBlockNonTerminatorExpressionsWith( std::function actionBeforeVisitExpr, std::function actionAfterVisitExpr, const Block& block, Domain& state) { auto exprs = block.GetNonTerminatorExpressions(); for (size_t i = 0; i < exprs.size(); ++i) { auto& expr = exprs[i]; actionBeforeVisitExpr(state, expr, i); SimulatingProcessingSingleExpression(state, expr); auto blocks = GetBlocksToAnalyse(*expr); for (auto innerBlock : blocks) { VisitBlockNonTerminatorExpressionsWith(actionBeforeVisitExpr, actionAfterVisitExpr, *innerBlock, state); } actionAfterVisitExpr(state, expr, i); } } #endif void VisitBlockWith(std::function actionBeforeVisitExpr, std::function actionAfterVisitExpr, std::function)> actionOnTerminator, Block& block, std::unordered_map* entryStates = nullptr) { if (!entryStates) { if (entrySets->find(&block) != entrySets->end()) { entryStates = entrySets.get(); } else { auto parentLambda = StaticCast(block.GetParentBlockGroup()->GetOwnerExpression()); entryStates = lambdaResultsMap.at(parentLambda); } } auto it = entryStates->find(&block); if (it == entryStates->end()) { return; } auto state = it->second; // should be a copy if (state.IsBottom()) { return; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND VisitBlockNonTerminatorExpressionsWith(actionBeforeVisitExpr, actionAfterVisitExpr, block, state); #endif auto terminator = block.GetTerminator(); if (auto lambda = IsApplyToLambda(terminator); lambda) { // If it's apply to a lambda, we need to clear the state of vars captured by the lambda. analysis->HandleVarStateCapturedByLambda(state, lambda); } auto targetSucc = analysis->PropagateTerminatorEffect(state, terminator); actionOnTerminator(state, terminator, targetSucc); } void SimulatingProcessingSingleExpression(Domain& state, Expression* expr) { if (expr->GetExprKind() == ExprKind::LAMBDA) { return analysis->PreHandleLambdaExpression(state, StaticCast(expr)); } if (auto lambda = IsApplyToLambda(expr); lambda) { // If it's apply to a lambda, we need to clear the state of vars captured by the lambda. analysis->HandleVarStateCapturedByLambda(state, lambda); } analysis->PropagateExpressionEffect(state, expr); } /// The analysis we are doing. std::unique_ptr> analysis; /// This unordered_map collects the abstract state solution at the entry point of each block. std::unique_ptr> entrySets; std::unordered_map*> lambdaResultsMap; std::vector> lambdaResults; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/SInt.h000066400000000000000000000361141510705540100233750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_SINT_H #define CANGJIE_CHIR_ANALYSIS_SINT_H #include #include #include #include "cangjie/CHIR/Type/Type.h" namespace Cangjie::CHIR { /** * @brief int width enum */ enum IntWidth : unsigned { I8 = 8, I16 = 16, I32 = 32, I64 = 64 }; /// get width from CHIR type. IntWidth ToWidth(const Type& ty); /// Build an IntWidth from bitness \p v constexpr IntWidth FromUnsigned(unsigned v) { switch (v) { case 8u: return IntWidth::I8; case 16u: return IntWidth::I16; case 32u: return IntWidth::I32; case 64u: return IntWidth::I64; default: CJC_ABORT(); return IntWidth::I64; } } /// get width of template type t. template constexpr IntWidth FromUnsigned() { return FromUnsigned(sizeof(T) * CHAR_BIT); } /// radix enum replacing number. enum Radix : unsigned { R2 = 2, R10 = 10, R16 = 16 }; /** * @brief base formatter struct, to print SInt with selected signess and radix. */ struct SIntFormatterBase { /// whether is unsigned. bool asUnsigned; /// number radix to print. Radix radix; /// beauty print delimiter. static constexpr char DIVIDOR = '|'; /// set radix base. decltype(std::setbase(0)) GetBaseManip() const; /// stream output with beauty print. friend std::ostream& operator<<(std::ostream& out, const SIntFormatterBase& range); }; /** * @brief SInt number to unify unsigned and signed number and its operator. */ struct SInt final { /// 64 word width standard. using WordType = uint64_t; /// minimum unit with 8 width size. static constexpr unsigned WORD_SIZE = 8; /// count of bits that is used to store SInt. static constexpr unsigned BITS_PER_WORD = 64; /// maximum number uint64. static constexpr WordType WORD_TYPE_MAX = ~0ull; /// Region: constructors & getters SInt(IntWidth width, WordType val); /// copy constructor with width and value. SInt(const SInt& other); /// move constructor with width and value. SInt(SInt&& other); /// implicit constructors from unsigned values. SInt(unsigned val); /// implicit constructors from unsigned long values. SInt(unsigned long val); /// implicit constructors from unsigned long long values. SInt(unsigned long long val); /// Prevent unexpected sign extension SInt(int) = delete; SInt(long) = delete; SInt(long long) = delete; /// Construct an SInt with given width from string, which represents the SInt by radix. /// Should any error occurs during the construction (i.e. width not enough, incorrect character in radix), that /// error is NOT handled. SInt(IntWidth width, const std::string& str, Radix radix); /// return unsigned value WordType UVal() const; /// return signed value int64_t SVal() const; /// output width of SInt IntWidth Width() const; /// Region: get static constants static SInt Zero(IntWidth w); /// Gets the maximum unsigned value of SInt of width \p width static SInt UMaxValue(IntWidth w); /// Gets the maximum signed value of SInt of width \p width static SInt SMaxValue(IntWidth w); /// Gets the minimum unsigned value of SInt of width \p width static SInt UMinValue(IntWidth w); /// Gets the minimum signed value of SInt of width \p width static SInt SMinValue(IntWidth w); /// Get all zero SInt of input width static SInt AllOnes(IntWidth w); /// Get a bit mask integer with exactly \p no bit set static SInt BitMask(IntWidth w, unsigned no); /// Get a bit mask integer with bits from \p loBit to \p hiBit set. static SInt BitMask(IntWidth w, unsigned loBit, unsigned hiBit); /// Get a bit mask integer with wrapping. /// If \p loBit > \p hiBit, the behaviour is same as the non-wrapped version; /// If \p loBit <= \p hiBit, bits from \p hiBit to width - 1 and bits from 0 to \p loBit are all set. /// Specifically if \p loBit == \p hiBit, the result has all bits within \p width set. static SInt WrappedBitMask(IntWidth w, unsigned loBit, unsigned hiBit); /// Set an SInt of width \p width, with exactly high \p highBits set static SInt GetHighBitsSet(IntWidth w, unsigned highBits); /// Set an SInt of width \p width, with exactly low \p lowBits set static SInt GetLowBitsSet(IntWidth w, unsigned lowBits); /// Set the selected bit \p pos to one with width length. static SInt GetOneBitSet(IntWidth w, unsigned pos); /// Region: value tests /// Determine if this SInt is negative bool IsNeg() const; bool IsNonNeg() const; bool IsPositive() const; /// Determine if this SInt is non negative bool IsSignBitSet() const; /// Determine if this SInt is negative bool IsSignBitClear() const; /// Determine if this SInt has only the specified bit set. bool IsOneBitSet(unsigned no) const; /// Determine if all bits are set bool IsAllOnes() const; /// Is certain number. bool IsZero() const; bool IsOne() const; bool IsUMaxValue() const; bool IsSMaxValue() const; bool IsUMinValue() const; bool IsSMinValue() const; // /Check if this SInt has an \p n-bits unsigned value bool IsUIntN(unsigned n) const; /// Check if this SInt has an \p n-bits signed value bool IsSIntN(unsigned n) const; /// Check if value is power of 2. bool IsPowerOf2() const; /// Check if value is negated power of 2. bool IsNegatedPowerOf2() const; /// Check if this SInt is returned by SignMask bool IsSignMask() const; /// change Sint to bool. bool ToBool() const; /// Returns the value of this SInt saturate to \p maxv uint64_t GetULimitedValue(uint64_t maxv = UINT64_MAX) const; /// Check if this SInt consists of a repeated bit pattern /// e.g. 0x01010101 is a splat of 8. /// \p splatSizeInBits must divide \f width without remainder bool IsSplat(unsigned splatSizeInBits) const; /// Check if val is mask with input bits, bits < BITS_PER_WORD bool IsMask(unsigned bits) const; /// Check if this SInt contains a non-empty sequence of ones without remainder bool IsShiftedMask() const; /// Check if this SInt contains a non-empty sequence of ones without remainder. If true, \p index specify the /// index of the lowest set bit and \p len specify the length of the mask. Otherwise, both parameters are unchanged bool IsShiftedMask(unsigned& index, unsigned& len) const; /// Returns a SInt with the same width as this, and with low bits zero-masked and high bits right shift to the /// least significant bits SInt HighBits(unsigned num) const; /// Returns a SInt with the same width as this, and with high bits zero-masked SInt LowBits(unsigned num) const; /// Check if the two SInt has the same value after ZExt one of them if needed. static bool IsSameValue(const SInt& a, const SInt& b); /** * @brief beauty printer for SInt */ struct Formatter final : public SIntFormatterBase { /// SInt value to print const SInt& value; /// output stream function friend std::ostream& operator<<(std::ostream& out, const Formatter& fmt); }; /// format SInt to beauty format. Formatter ToString(bool asUnsigned, Radix radix = Radix::R10) const; /// Region: unary operators SInt operator++(int); SInt& operator++(); SInt operator--(int); SInt& operator--(); bool operator!() const; /// assignment operator SInt& operator=(const SInt& other); SInt& operator=(SInt&& other); SInt& operator=(uint64_t v); // compound assignment operators SInt& operator&=(const SInt& other); SInt& operator&=(uint64_t v); SInt& operator|=(const SInt& other); SInt& operator|=(uint64_t v); SInt& operator^=(const SInt& other); SInt& operator^=(uint64_t v); SInt& operator*=(const SInt& other); SInt& operator*=(uint64_t v); SInt operator*(const SInt& other) const; SInt& operator+=(const SInt& other); SInt& operator+=(uint64_t v); SInt& operator-=(const SInt& other); SInt& operator-=(uint64_t v); SInt& operator<<=(unsigned count); SInt& operator<<=(const SInt& count); SInt operator<<(const SInt& count) const; SInt operator<<(unsigned count) const; /// Arithmetic right shift, that is, the sign bit is preserved when the left operand is a negative number SInt AShr(unsigned count) const; void AShrInPlace(unsigned count); /// Logical right shift, that is, the sign bit is lost when the left operand is a negative number SInt LShr(unsigned count) const; void LShrInPlace(unsigned count); SInt Ashr(const SInt& count) const; SInt LShr(const SInt& count) const; void LShrInPlace(const SInt& count); SInt Shl(unsigned count) const; SInt Shl(const SInt& count) const; /// Concatenate the bits from \p v onto the bottom of this /// RR: what does this mean? SInt Concat(const SInt& v) const; /// Divide and remainder operations. Note that division and remainder never overflow. SInt UDiv(const SInt& rhs) const; SInt UDiv(uint64_t rhs) const; SInt SDiv(const SInt& rhs) const; SInt SDiv(int64_t rhs) const; SInt URem(const SInt& rhs) const; SInt URem(uint64_t rhs) const; SInt SRem(const SInt& rhs) const; SInt SRem(int64_t rhs) const; /// Arithmetic operations with an additional flag \p overflow that indicates this operation has overflow or not SInt SAddOvf(const SInt& rhs, bool& overflow) const; SInt UAddOvf(const SInt& rhs, bool& overflow) const; SInt SSubOvf(const SInt& rhs, bool& overflow) const; SInt USubOvf(const SInt& rhs, bool& overflow) const; SInt SMulOvf(const SInt& rhs, bool& overflow) const; SInt UMulOvf(const SInt& rhs, bool& overflow) const; SInt SShlOvf(const SInt& count, bool& overflow) const; SInt UShlOvf(const SInt& count, bool& overflow) const; /// Arithmetic operations with saturates the result SInt SAddSat(const SInt& rhs) const; SInt UAddSat(const SInt& rhs) const; SInt SSubSat(const SInt& rhs) const; SInt USubSat(const SInt& rhs) const; SInt SMulSat(const SInt& rhs) const; SInt UMulSat(const SInt& rhs) const; SInt SDivSat(const SInt& rhs) const; SInt SShlSat(const SInt& rhs) const; SInt UShlSat(const SInt& rhs) const; /// at operator to get certain bit. bool At(unsigned bit) const; bool operator[](unsigned bit) const; // Region: Comparison operators bool operator==(const SInt& other) const; bool operator==(uint64_t rhs) const; bool operator!=(const SInt& rhs) const; bool operator!=(uint64_t rhs) const; bool Ult(const SInt& rhs) const; bool Ult(uint64_t rhs) const; bool Slt(const SInt& rhs) const; bool Slt(int64_t rhs) const; bool Ule(const SInt& rhs) const; bool Ule(uint64_t rhs) const; bool Sle(const SInt& rhs) const; bool Sle(int64_t rhs) const; bool Ugt(const SInt& rhs) const; bool Ugt(uint64_t rhs) const; bool Sgt(const SInt& rhs) const; bool Sgt(int64_t rhs) const; bool Uge(const SInt& rhs) const; bool Uge(uint64_t rhs) const; bool Sge(const SInt& rhs) const; bool Sge(int64_t rhs) const; static SInt UMin(const SInt& lhs, const SInt& rhs); static SInt SMin(const SInt& lhs, const SInt& rhs); static SInt UMax(const SInt& lhs, const SInt& rhs); static SInt SMax(const SInt& lhs, const SInt& rhs); /// Check if any bit is set to true in both operands bool Intersects(const SInt& other) const; /// Check if all bits set in this SInt are also set in the other bool IsSubsetOf(const SInt& other) const; /// Truncate to SInt width \p width SInt Trunc(IntWidth w) const; /// Truncate this SInt as unsigned to another unisgned with a new width. If the truncation cannot be lossless, /// return max value. SInt TruncUSat(IntWidth w) const; /// Truncate this SInt as signed to another signed with a new width. If the truncation cannot be lossless, /// return the saturated value. SInt TruncSSat(IntWidth w) const; /// Extend this SInt as signed to a new width. SInt SExt(IntWidth w) const; /// Extend this SInt as unsigned to a new width. SInt ZExt(IntWidth w) const; /// Region: manipulating functions /// Set all bits to one void SetAllBits(); /// Set the selected bit \p pos to one void SetBit(unsigned pos); /// Set from \p lo to \p hi bits to one void SetBits(unsigned lo, unsigned hi); /// set low bits to one. void SetLowBits(unsigned lo); /// set high bits to one. void SetHighBits(unsigned hi); /// clear position to zero. void ClearBit(unsigned pos); /// flip all bits. void FlipAllBits(); /// flip position bit. void FlipBit(unsigned pos); /// Negate this value in place void Neg(); /// indicate active bits. unsigned ActiveBits() const; /// get significant bits. unsigned SignificantBits() const; // get zero extend value. uint64_t GetZExtValue() const; // get signed zero extend value. int64_t GetSExtValue() const; /// Count leading zeroes unsigned Clz() const; /// Count leading ones unsigned Clo() const; /// Get the number of leading bits of this SInt that are equal to its sign bit unsigned GetNumSignBits() const; /// Count trailing zeroes unsigned Ctz() const; /// Count trailing ones unsigned Cto() const; /// Count population unsigned Popcnt() const; /// set bits and return new value. static SInt GetBitsSetFrom(IntWidth w, unsigned lo); private: IntWidth width; uint64_t val; static uint64_t MaskBit(unsigned pos); SInt& ClearUnusedBits(); void FromString(const std::string& str, Radix radix); int UCmp(const SInt& rhs) const; int SCmp(const SInt& rhs) const; }; /// SInt operators. bool operator==(uint64_t v1, const SInt& v2); bool operator!=(uint64_t v1, const SInt& v2); SInt operator-(SInt v); SInt operator+(SInt a, const SInt& b); SInt operator+(const SInt& a, SInt&& b); SInt operator+(SInt a, uint64_t rhs); SInt operator+(uint64_t lhs, SInt b); SInt operator&(SInt a, const SInt& b); SInt operator&(const SInt& a, SInt&& b); SInt operator&(SInt a, uint64_t rhs); SInt operator&(uint64_t lhs, SInt b); SInt operator|(SInt a, const SInt& b); SInt operator|(const SInt& a, SInt&& b); SInt operator|(SInt a, uint64_t rhs); SInt operator|(uint64_t lhs, SInt b); SInt operator^(SInt a, const SInt& b); SInt operator^(const SInt& a, SInt&& b); SInt operator^(SInt a, uint64_t rhs); SInt operator^(uint64_t lhs, SInt b); SInt operator-(SInt a, const SInt& b); SInt operator-(const SInt& a, SInt&& b); SInt operator-(SInt a, uint64_t rhs); SInt operator-(uint64_t lhs, SInt b); SInt operator*(SInt a, uint64_t rhs); SInt operator*(uint64_t lhs, SInt b); } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/SIntDomain.h000066400000000000000000000232111510705540100245170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_SINT_DOMAIN_H #define CANGJIE_CHIR_ANALYSIS_SINT_DOMAIN_H #include "cangjie/CHIR/Analysis/BoolDomain.h" #include "cangjie/CHIR/Analysis/ConstantRange.h" #include "cangjie/CHIR/Analysis/SInt.h" #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/CHIRCasting.h" namespace Cangjie::CHIR { /** * @brief mem template define, using for pointer of SIntDomain */ template using Mem = T; /** * @brief domain structure of SInt, using as value to const analysis. */ class SIntDomain { public: /// Construct an SInt domain with \p numeric as numeric range /// \p isUnsigned indicates whether the numeric range is a unsigned range or a signed range /// symbolic ranges are always stored as signed ranges SIntDomain(const ConstantRange& numeric, bool isUnsigned); SIntDomain(const SIntDomain& other) = default; SIntDomain(SIntDomain&& other) = default; // no copy assignment #ifdef __APPLE__ // The code doesn't compile on MacOS without operator=. SIntDomain& operator=(const SIntDomain& other) = default; #else SIntDomain& operator=(const SIntDomain& other) = delete; #endif SIntDomain& operator=(SIntDomain&& other) = default; ~SIntDomain() = default; /** * @brief from Symbol to constant range. */ using SymbolicBoundsMap = std::map; /// Construct an SIntDomain with \p numeric as numeric range and \p symbolics as symbolic range /// This is usually used only for internal constructs. /// Note that each numeric bound of symbolic ranges must be non trivial. SIntDomain(const ConstantRange& numeric, SymbolicBoundsMap&& symbolics, bool isUnsigned); /// Constructs an SIntDomain with \p numeric as numeric range and one pair of symbolic range represented /// by {\p symbol, \p symbolicBound}. SIntDomain(const ConstantRange& numeric, PtrSymbol symbol, const ConstantRange& symbolicBound); /// check domain is top bool IsTop() const; /// check domain is non top bool IsNonTrivial() const; /// check domain is bottom bool IsBottom() const; /** * @brief beauty print formatter for SIntDomain symbol. */ struct SymbolicFormatter final : public ConstantRange::Formatter { /// symbol to print. PtrSymbol symbol; /// output ostream function. friend std::ostream& operator<<(std::ostream& out, const SymbolicFormatter& fmt); }; /** * @brief beauty print formatter for SIntDomain. */ struct Formatter final { /// SIntDomain to print. const SIntDomain& d; /// whether SIntDomain is unsigned. const bool asUnsigned; /// output radix for domain. const Radix radix; /// constructor for beauty print formatter of SIntDomain template Formatter(const SIntDomain& value, bool asUnsigned, Radix radix) : d{value}, asUnsigned{asUnsigned}, radix{radix} { } /// get domain from formatter. const SIntDomain* operator->() const; }; /// output function of beauty print formatter for SIntDomain. friend std::ostream& operator<<(std::ostream& out, const Formatter& fmt); /// beauty printer for SIntDomain with specific unsigned flag and radix. Formatter ToString(bool asUnsigned, Radix radix = Radix::R10) const; /// default beauty printer for SIntDomain. Formatter ToString() const; /// output function for SIntDomain. friend std::ostream& operator<<(std::ostream& out, const SIntDomain& d); /** * @brief static constructor from CHIR literal value. * @param literal CHIR literal value. * @return pointer of SIntDomain. */ static Mem From(const LiteralValue& literal); /** * @brief static constructor from SInt value and relation, for example < 10. * @param rel compare operator. * @param value SInt number. * @param isUnsigned whether range is unsigned. * @return pointer of SIntDomain. */ static Mem FromNumeric(RelationalOperation rel, const SInt& value, bool isUnsigned); /** * @brief static constructor from symbol and relation. * @param rel compare operator. * @param symbol symbol to indicate range. * @param width SInt width. * @param isUnsigned whether range is unsigned. * @return pointer of SIntDomain. */ static Mem FromSymbolic(RelationalOperation rel, PtrSymbol symbol, IntWidth width, bool isUnsigned); /** * @brief static constructor of top domain * @param width SInt width * @param isUnsigned whether range is unsigned. * @return pointer of SIntDomain. */ static Mem Top(IntWidth width, bool isUnsigned); /** * @brief static constructor of bottom domain * @param width SInt width * @param isUnsigned whether range is unsigned. * @return pointer of SIntDomain. */ static Mem Bottom(IntWidth width, bool isUnsigned); /// Get constant range from domain. const ConstantRange& NumericBound() const&; /// Get constant range from domain. ConstantRange NumericBound() &&; /// Get domain width. IntWidth Width() const; /// whether domain is unsigned. bool IsUnsigned() const; /// Symbolic bounds map iterate helper struct SymbolicBoundsMapIterator { /// iterator constructor for symbolic bounds. SymbolicBoundsMapIterator(const SymbolicBoundsMap& map); /// get begin iterator. std::map::const_iterator Begin() const; /// get end iterator. std::map::const_iterator End() const; /// check whether map is empty. bool Empty() const; private: /// symbolic Bounds map from SIntDomain. const SymbolicBoundsMap& map; }; /// get symbolic bounds from SIntDomain. SymbolicBoundsMapIterator SymbolicBounds() const; /// Returns a pointer to the ConstantRange if this domain has a bound against \p symbol, or null pointer otherwise const ConstantRange* FindSymbolicBound(PtrSymbol symbol) const; /// check if constant range in domain is single value. [a, a+1) bool IsSingleValue() const; /// get width from CHIR type static IntWidth ToWidth(const Ptr& type); /// get intersect domain from two domains. static Mem Intersects(const Mem& lhs, const Mem& rhs); /// get union domain from two domains. static Mem Unions(const Mem& lhs, const Mem& rhs); /// check whether domain is same as input domain. bool IsSame(const SIntDomain& domain) const; private: ConstantRange numeric; SymbolicBoundsMap symbolics; bool unsignedFlag; }; /** * @brief get opposite relation operator from input relation operation. * @param a input relation operation. * @return opposite relation operation. */ RelationalOperation SymbolicNeg(RelationalOperation a); /** * @brief arithmetic operation info collector. */ struct CHIRArithmeticBinopArgs { /// binary operation left side domain const SIntDomain& ld; /// binary operation right side domain const SIntDomain& rd; /// Resolved lhs & rhs symbols (i.e. resolved by calling ValueRangeCache::Projection) PtrSymbol l, r; /// binary operation op kind. ExprKind op; /// overflow strategy. Cangjie::OverflowStrategy ov; /// flag if arithmetic is unsigned operation. bool uns; CHIRArithmeticBinopArgs(const SIntDomain& ld, const SIntDomain& rd, PtrSymbol l, PtrSymbol r, ExprKind op, Cangjie::OverflowStrategy ov, bool isUnsigned) : ld{ld}, rd{rd}, l{std::move(l)}, r{std::move(r)}, op{op}, ov{ov}, uns{isUnsigned} { } }; /// compute arithmetic binary op with two SIntDomain inputs, one SIntDomain output SIntDomain ComputeArithmeticBinop(CHIRArithmeticBinopArgs&& args); /** * @brief relation operation info collector, such as <. */ struct CHIRRelIntBinopArgs { /// binary operation left side domain const Mem& ld; /// binary operation right side domain const Mem& rd; /// Resolved lhs & rhs symbols (i.e. resolved by calling ValueRangeCache::Projection) PtrSymbol l, r; /// binary operation op kind. ExprKind op; /// flag if arithmetic is unsigned operation. bool uns; CHIRRelIntBinopArgs( const Mem& ld, const Mem& rd, PtrSymbol l, PtrSymbol r, ExprKind op, bool isUnsigned) : ld{ld}, rd{rd}, l{std::move(l)}, r{std::move(r)}, op{op}, uns{isUnsigned} { } }; /// compute relation binary op with two SIntDomain inputs, BoolDomain output BoolDomain ComputeRelIntBinop(CHIRRelIntBinopArgs&& args); /// compute equality binary op with two BoolDomain inputs, BoolDomain output BoolDomain ComputeEqualityBoolBinop(const BoolDomain& ld, const BoolDomain& rd, ExprKind op); /// constant range converter from unsigned to signed or signed to unsigned. ConstantRange NumericConversion( const ConstantRange& src, IntWidth dstSize, bool srcUnsigned, bool dstUnsigned, Cangjie::OverflowStrategy ov); /// compute new constant bounds from type cast operations. ConstantRange ComputeTypeCastNumericBound( const SIntDomain& v, IntWidth dstSize, bool dstUnsigned, Cangjie::OverflowStrategy ov); } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/TypeAnalysis.h000066400000000000000000000126171510705540100251470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_TYPE_ANALYSIS_H #define CANGJIE_CHIR_ANALYSIS_TYPE_ANALYSIS_H #include "cangjie/CHIR/Analysis/DevirtualizationInfo.h" #include "cangjie/CHIR/Analysis/ValueAnalysis.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { struct TypeValue { TypeValue() = delete; /** * @brief constructor from devirtual type kind and CHIR Type * @param kind devirtual type kind, is exactly this type or have derived type. * @param baseLineType CHIR Type of value */ TypeValue(DevirtualTyKind kind, Type* baseLineType); /// constructor of type value. virtual ~TypeValue() { } /** * @brief join with other type value. * @param rhs type value to join with. * @return type value if value is changed otherwise nullopt. */ std::optional> Join(const TypeValue& rhs) const; /// print type value to string. std::string ToString() const; /// clone a type value. std::unique_ptr Clone() const; /// get devirtual type kind, is exactly this type or have derived type. DevirtualTyKind GetTypeKind() const; /// get CHIR type. Type* GetSpecificType() const; /// get CHIR builder to create CHIR IR. static void SetCHIRBuilder(CHIRBuilder* chirBuilder); protected: DevirtualTyKind kind; Type* baseLineType; private: static CHIRBuilder* builder; std::string GetKindString(DevirtualTyKind clsTyKind) const; }; /** * @brief the abstract value domain of type value */ using TypeValueDomain = ValueDomain; /** * @brief the state of type value domain */ using TypeDomain = State; /** * @brief partially specialized analysis import value. */ template <> const std::string Analysis::name; template <> const std::optional Analysis::blockLimit; template <> TypeDomain::ChildrenMap ValueAnalysis::globalChildrenMap; template <> TypeDomain::AllocatedRefMap ValueAnalysis::globalAllocatedRefMap; template <> TypeDomain::AllocatedObjMap ValueAnalysis::globalAllocatedObjMap; template <> std::vector> ValueAnalysis::globalRefPool; template <> std::vector> ValueAnalysis::globalAbsObjPool; template <> TypeDomain ValueAnalysis::globalState; /** * @brief check whether global var need type analysis. * @param gv global var to check. * @return flag global var need analyse */ template <> bool IsTrackedGV>(const GlobalVar& gv); /** * @brief constant type analysis for CHIR IR to devirt invoke call. */ class TypeAnalysis final : public ValueAnalysis { public: TypeAnalysis() = delete; /** * @brief constructor for type analysis. * @param func function to analyse * @param builder CHIR builder for generating IR. * @param isDebug flag whether print debug log. * @param realRetTyMap extra info to detect real type from apply expression. */ TypeAnalysis(const Func* func, CHIRBuilder& builder, bool isDebug, const DevirtualizationInfo& devirtInfo); ~TypeAnalysis() final { } /** * @brief check CHIR function whether has invoke expression. * @param body function body to detect * @return flag if has invoke expression. */ static bool CheckFuncHasInvoke(const BlockGroup& body); /** * @brief call CheckFuncHasInvoke to detect CHIR function whether has invoke expression. * @param method function to detect. * @return flag if has invoke expression. */ static bool Filter(const Func& method); /// print type value with its expression. void PrintDebugMessage(const Expression* expr, const TypeValue* absVal) const; private: void HandleFuncParam(TypeDomain& state, Parameter* param, Value* refObj) override; void HandleAllocateExpr(TypeDomain& state, const Allocate* expression, Value* newObj) override; void HandleNormalExpressionEffect(TypeDomain& state, const Expression* expression) override; void HandleApplyExpr(TypeDomain& state, const Apply* apply, Value* refObj) override; void HandleInvokeExpr(TypeDomain& state, const Invoke* invoke, Value* refObj) override; template void HandleTypeCastExpr(TypeDomain& state, const TTypeCast* typecast) const; void HandleBoxExpr(TypeDomain& state, const Box* boxExpr) const; void HandleDefaultExpr(TypeDomain& state, const Expression* expr) const; Value* PreHandleDefaultExpr(TypeDomain& state, const Expression* expr) const; void UpdateDefaultValue(TypeDomain& state, Value* value, Value* refObj, Type* relType = nullptr) const; void PreHandleGetElementRefExpr(TypeDomain& state, const GetElementRef* getElemRef) override; void PreHandleFieldExpr(TypeDomain& state, const Field* field) override; std::optional HandleTerminatorEffect(TypeDomain& state, const Terminator* terminator) override; const std::unordered_map& realRetTyMap; const ConstMemberVarCollector::ConstMemberMapType& constMemberTypeMap; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/Utils.h000066400000000000000000000172711510705540100236230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_UTILS_H #define CANGJIE_CHIR_ANALYSIS_UTILS_H #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/CHIR/Analysis/Analysis.h" #include "cangjie/CHIR/DebugLocation.h" #include "cangjie/CHIR/Type/Type.h" #include #include namespace Cangjie::CHIR { /** * @brief join template functions. * @tparam K map key, normally CHIR value. * @tparam V map value, normally domain. * @param lhs left side to join. * @param rhs right side to join. * @param action join function of different domian. * @return flag if changed after join. */ template bool MapJoinTemplate( std::unordered_map& lhs, const std::unordered_map& rhs, std::function action) { bool changed = false; for (auto& [k2, v2] : rhs) { if (auto it = lhs.find(k2); it != lhs.end()) { auto& v1 = it->second; changed |= action(k2, v1, v2); } else { lhs.emplace(k2, v2); changed = true; } } return changed; } /** * @brief map join interface * @tparam T map key, normally CHIR value. * @tparam Domain map value, normally domain. * @param lhs left side to join. * @param rhs right side to join. * @return flag if changed after join. */ template , Domain>>> bool MapJoin(std::unordered_map& lhs, const std::unordered_map& rhs) { const auto action = [](T, Domain& v1, const Domain& v2) -> bool { return v1.Join(v2); }; return MapJoinTemplate(lhs, rhs, action); } /** * @brief join a vector domain * @tparam Domain domain to join * @param lhs left side to join. * @param rhs right side to join. * @return flag if changed after join. */ template , Domain>>> bool VectorJoin(std::vector& lhs, const std::vector& rhs) { CJC_ASSERT(lhs.size() == rhs.size()); bool changed = false; for (size_t i = 0; i < lhs.size(); ++i) { changed |= lhs[i].Join(rhs[i]); } return changed; } /// get ref name from index std::string GetRefName(size_t index); /// get object name from index std::string GetObjName(size_t index); /// get child object name from index and parent name. std::string GetObjChildName(std::string parentName, size_t fieldIdx); /** * @brief from cangjie position to range. * @param loc location to change. * @return cangjie range change to. */ template Cangjie::Range ToRange(const T& loc) { auto begin = loc.GetBeginPos(); auto end = loc.GetEndPos(); Cangjie::Position beginPos = Cangjie::Position( loc.GetFileID(), static_cast(begin.line), static_cast(begin.column)); Cangjie::Position endPos = Cangjie::Position( loc.GetFileID(), static_cast(end.line), static_cast(end.column)); return MakeRange(beginPos, endPos); } /** * @brief from cangjie position to range with non zero. * @param loc location to change. * @return cangjie range change to. */ template std::pair ToRangeIfNotZero(const T& loc) { auto begin = loc.GetBeginPos(); auto end = loc.GetEndPos(); Cangjie::Position beginPos(loc.GetFileID(), static_cast(begin.line), static_cast(begin.column)); Cangjie::Position endPos(loc.GetFileID(), static_cast(end.line), static_cast(end.column)); if (!beginPos.IsZero() && !endPos.IsZero()) { return std::make_pair(true, MakeRange(beginPos, endPos)); } return std::make_pair(false, MakeRange(beginPos, endPos)); } /** * @brief from CHIR location to cangjie position. * @param loc CHIR location input. * @return cangjie position change to. */ Cangjie::Position ToPosition(const DebugLocation& loc); /** * @brief from CHIR location to format print. * @param loc CHIR location. * @param isPrintFileName flag whether print file name. * @return print result of location. */ std::string ToPosInfo(const DebugLocation& loc, bool isPrintFileName = false); /** * @brief cut from long type value to small type with high bits cut off. * @tparam T type to cut. * @param srcNum number input to cut. * @param destTypeKind destination type. * @return value after cut. */ template T CutOffHighBits(T srcNum, Type::TypeKind destTypeKind) { switch (destTypeKind) { case Type::TypeKind::TYPE_UINT8: return static_cast(static_cast(srcNum)); case Type::TypeKind::TYPE_UINT16: return static_cast(static_cast(srcNum)); case Type::TypeKind::TYPE_UINT32: return static_cast(static_cast(srcNum)); case Type::TypeKind::TYPE_UINT64: return static_cast(static_cast(srcNum)); case Type::TypeKind::TYPE_UINT_NATIVE: return static_cast(static_cast(srcNum)); case Type::TypeKind::TYPE_INT8: return static_cast(static_cast(srcNum)); case Type::TypeKind::TYPE_INT16: return static_cast(static_cast(srcNum)); case Type::TypeKind::TYPE_INT32: return static_cast(static_cast(srcNum)); case Type::TypeKind::TYPE_INT64: return static_cast(static_cast(srcNum)); case Type::TypeKind::TYPE_INT_NATIVE: return static_cast(static_cast(srcNum)); case Type::TypeKind::TYPE_FLOAT32: return static_cast(static_cast(srcNum)); case Type::TypeKind::TYPE_FLOAT64: return static_cast(static_cast(srcNum)); default: CJC_ABORT(); return static_cast(0); } } /** * @brief check if member var is initialized. * @param func function to check, * @param store store expression. * @return path of member var. */ std::optional IsInitialisingMemberVar(const Func& func, const StoreElementRef& store); /** * @brief This function will check if an expression is an Apply expression, and if it's callee * is a result of a Lambda expression. If it's, it will return the lambda; otherwise a nullptr. */ const Lambda* IsApplyToLambda(const Expression* expr); /// This function will check if the function is getOrThrow function. bool IsGetOrThrowFunction(const Expression& expr); /** * @brief find least common supper class of two input classes. * @param ty1 first class. * @param ty2 second class. * @param builder CHIR builder for collecting info in CHIR. * @return least common supper class. */ ClassType* LeastCommonSuperClass(ClassType* ty1, ClassType* ty2, CHIRBuilder* builder); /// check if type is enum with struct body. bool IsStructEnum(const Ptr& type); /// check if type is enum without struct body. bool IsRefEnum(const Ptr& type); /// check if expression is arithmetic of unsigned value. bool IsUnsignedArithmetic(const BinaryExpression& expr); /// get CHIR func value from apply. Func* TryGetInstanceVarInitFromApply(const Expression& expr); /** * @brief Get mutable variables that captured by lambda recursively. * if there is lambda define or lambda calling, we will step in and collect child lambda's captured mutable variables * @param lambda the root lambda. * @return captured mutable variables. */ std::unordered_set GetLambdaCapturedVarsRecursively(const Lambda& lambda); } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/ValueAnalysis.h000066400000000000000000001772311510705540100253060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_VALUE_ANALYSIS_H #define CANGJIE_CHIR_ANALYSIS_VALUE_ANALYSIS_H #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/Analysis/ValueDomain.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" #include #include #include #include namespace Cangjie::CHIR { template class ValueAnalysis; /** * @brief abstract state to store CHIR value state, mainly store value and reference state. * @tparam ValueDomain abstract domain to store status of CHIR value. */ template , ValueDomain>>> class State final : public AbstractDomain> { friend class ValueAnalysis; public: /// state map type from CHIR value to valueDomain using ProgramState = std::unordered_map; /// reference map from CHIR ref to object or other ref. using RefMap = std::unordered_map>; /// children map from parent value to its child object. using ChildrenMap = std::unordered_map>; /// map from CHIR allocate expression to ref using AllocatedRefMap = std::unordered_map; /// map from CHIR allocate expression to object using AllocatedObjMap = std::unordered_map; /** * @brief State constructor from relation map of value in function. * @param childrenMap CHIR child map. * @param allocatedRefMap allocate reference map. * @param allocatedTwoLevelRefMap allocate reference map for two level reference. * @param allocatedObjMap allocate object map. * @param refPool all references in function. * @param absObjPool all objects in function. */ explicit State(ChildrenMap* childrenMap, AllocatedRefMap* allocatedRefMap, AllocatedRefMap* allocatedTwoLevelRefMap, AllocatedObjMap* allocatedObjMap, std::vector>* refPool, std::vector>* absObjPool) : childrenMap(childrenMap), allocatedRefMap(allocatedRefMap), allocatedTwoLevelRefMap(allocatedTwoLevelRefMap), allocatedObjMap(allocatedObjMap), refPool(refPool), absObjPool(absObjPool) { } /** * @brief copy constructor * @param rhs other state to copy. */ State(const State& rhs) { this->kind = rhs.kind; this->programState = rhs.programState; this->refMap = rhs.refMap; this->childrenMap = rhs.childrenMap; this->allocatedRefMap = rhs.allocatedRefMap; this->allocatedTwoLevelRefMap = rhs.allocatedTwoLevelRefMap; this->allocatedObjMap = rhs.allocatedObjMap; this->refPool = rhs.refPool; this->absObjPool = rhs.absObjPool; } /** * @brief copy operator * @param rhs other state to copy. * @return copied state. */ State& operator=(const State& rhs) { this->kind = rhs.kind; this->programState = rhs.programState; this->refMap = rhs.refMap; // We don't re-assign `childrenMap`, `allocatedRefMap`, `allocatedObjMap`, `refPool` and `absObjPool`. // The reason is, these three fields are identical across all the states in an analysis // to a function. return *this; } ~State() { } /** * @brief join two state, mainly happen in merge two states of different blocks. * @param rhs other state to join. * @return true if state changed. */ bool Join(const State& rhs) override { if (rhs.kind == ReachableKind::UNREACHABLE) { return false; } else if (this->kind == ReachableKind::UNREACHABLE) { *this = rhs; return true; } else { auto changed = MapJoin(programState, rhs.programState); // We can consider only do the Join operations on living Refs. changed |= RefMapJoin(rhs); return changed; } } /** * @brief output string with beauty format. * @return output string. */ std::string ToString() const override { if (this->kind == ReachableKind::UNREACHABLE) { return "Unreachable"; } else { std::stringstream ss; ss << "programState: { "; for (auto& [k, v] : std::as_const(programState)) { ss << k->GetIdentifier() << " -> " << v.ToString() << ", "; } ss << "}\n"; ss << "refMap: { "; for (auto& [k, v] : std::as_const(refMap)) { ss << k->GetUniqueID() << " -> "; if (auto ref = std::get_if(&v)) { ss << (*ref)->GetUniqueID(); } else { ss << std::get(v)->GetIdentifier(); } ss << ", "; } ss << "}"; return ss.str(); } } /** * @brief Update the abstract state of value @p dest. * @param dest value to update * @param absVal domain to update to value. */ template >> void Update(Value* dest, Arg&& absVal) { if (auto it = programState.find(dest); it != programState.end()) { it->second = std::forward(absVal); } else { programState.emplace(dest, std::forward(absVal)); } } /** * @brief Set the abstract state of value @p dest to the bound value of the abstract domain. * @param dest value to set to bound. * @param isTop set to top if true else set to bottom. */ void SetToBound(Value* dest, bool isTop) { if (auto it = programState.find(dest); it != programState.end()) { it->second.SetSelfToBound(isTop); } else { programState.emplace(dest, ValueDomain(isTop)); } } /** * @brief do InitToTopOrTopRef if dest is in state map. * @param dest value to init. * @param isRef set to top ref if true else set to top value. */ void TrySetToTopOrTopRef(Value* dest, bool isRef) { if (auto it = programState.find(dest); it != programState.end()) { return; } InitToTopOrTopRef(dest, isRef); } /** * @brief Initialise or update the state of @p dest to a Top or a TopRef depends on @p isRef. * @param dest value to set to bound. * @param isRef set to top ref if true else set to top value. */ void SetToTopOrTopRef(Value* dest, bool isRef) { if (auto it = programState.find(dest); it != programState.end()) { if (isRef) { it->second = Ref::GetTopRefInstance(); } else { it->second = ValueDomain(/* isTop = */ true); } } else { InitToTopOrTopRef(dest, isRef); } } /// Assuming the state of value @p dest is not a Ref, return a const pointer to the /// abstract value of value @p dest. auto CheckAbstractValue(Value* obj) const { auto it = programState.find(obj); return it != programState.end() ? it->second.CheckAbsVal() : nullptr; } /** * @brief get abstract domain from ref or object * @param obj ref or object to get its abstract domain. * @return domain relation to object or ref. */ const ValueDomain* GetAbstractDomain(Value* obj) const { auto absObj = obj; if (obj->GetType()->IsRef()) { absObj = CheckAbstractObjectRefBy(obj); } return CheckAbstractValueWithTopBottom(absObj); } /** * @brief checkout state from object, return null if no state found. * @param obj object to get domain. * @return return null if no state found else its domain. */ const ValueDomain* CheckAbstractValueWithTopBottom(Value* obj) const { auto it = programState.find(obj); return it != programState.end() ? &it->second : nullptr; } /** * @brief Propagate the state of value @p src to @p dest and handle their children as appropriate. * @param src source value state pass from * @param dest destination state pass to * @param state state input to update, null if update state self. */ void Propagate(Value* src, Value* dest, State* state = nullptr) { auto targetState = !state ? this : state; auto targetProgramState = &targetState->programState; PropagateWithoutChildren(src, dest, targetProgramState); auto destIt = targetState->childrenMap->find(dest); auto srcIt = childrenMap->find(src); if (destIt != targetState->childrenMap->end()) { auto& destChildren = destIt->second; if (srcIt != childrenMap->end()) { // a) Both dest value and src value have children information. Therefore, we just need // to propagate the state of children in order. It should be noted that, for certain // types such as Enum, we are only concerned with that state of **some** of the members // (e.g. for enum, we only record the state of the constructor index). Thus, the size of // dest children may be less than the size of src children. auto& srcChildren = srcIt->second; CJC_ASSERT(srcChildren.size() >= destChildren.size()); for (size_t i = 0; i < destChildren.size(); ++i) { PropagateWithoutChildren(srcChildren[i], destChildren[i], targetProgramState); } } else { // b) The dest value has children, but the src value does not have children information. // This often happens when we use a untracked structure(e.g. return value of a function, // or nested member of custom definition) to assign a tracked structure. // e.g. var sa = SA(); sa = ca.sa // In this case, we want to change any known member information(if its value kind is VAL) // to an unknown status(Top). targetState->SetNonTopChildrenStateToTop(destChildren); } } else if (srcIt != childrenMap->end()) { // c) The dest value does not have children, but the src value does. // What we do is creating the children for dest and propagating the state of children in order. auto& srcChildren = srcIt->second; const auto setChildState = [this, &srcChildren, targetProgramState](AbstractObject* child, size_t index) { targetProgramState->emplace(child, programState.at(srcChildren[index])); }; targetState->CreateChildren(dest, srcChildren.size(), setChildState); } // d) Neither dest value and src value have children. // Do nothing. } /** * @brief @p refVal should be a first-class reference or a second-class reference (e.g. Class-CA& or * Class-CA&&). */ AbstractObject* CheckAbstractObjectRefBy(Value* refVal) const { auto it = programState.find(refVal); if (it != programState.end()) { CJC_ASSERT(it->second.GetKind() == ValueDomain::ValueKind::REF); auto refIt = refMap.find(it->second.GetRef()); if (refIt != refMap.end()) { if (std::holds_alternative(refIt->second)) { return std::get(refIt->second); } else { auto oRefIt = refMap.find(std::get(refIt->second)); if (oRefIt != refMap.end()) { CJC_ASSERT(std::holds_alternative(oRefIt->second)); return std::get(oRefIt->second); } } } } return nullptr; } /// Get the children of @p obj. If it does not have any child, an empty vector will be returned. std::vector GetChildren(Value* obj) { if (auto it = childrenMap->find(obj); it != childrenMap->end()) { return it->second; } if (auto it = programState.find(obj); it != programState.end()) { if (it->second.GetKind() == ValueDomain::ValueKind::REF) { auto ref = it->second.GetRef(); if (auto refIt = refMap.find(ref); refIt != refMap.end()) { CJC_ASSERT(std::holds_alternative(refIt->second)); return GetChildren(std::get(refIt->second)); } } } return {}; } /** * @brief get child from object with index input. * @param obj parent object. * @param index child object index. * @return child object */ AbstractObject* GetChild(Value* obj, size_t index) { auto children = GetChildren(obj); return index < children.size() ? children[index] : nullptr; } /** * @brief create ref to CHIR reference type value and set to top. * @param dest CHIR reference type value. * @param expr mostly allocate expression. * @return abstract object created. */ AbstractObject* GetReferencedObjAndSetToTop(Value* dest, const Expression* expr = nullptr) { CJC_ASSERT(!dest->GetType() || dest->GetType()->IsRef() || dest->GetType()->IsGeneric()); auto ref = CreateNewRef(expr); Update(dest, ref); auto obj = CreateNewObject(GetObjName(absObjPool->size()), expr); SetToBound(obj, /* isTop = */ true); refMap.emplace(ref, obj); return obj; } AbstractObject* GetTwoLevelRefAndSetToTop(Value* dest, const Expression* expr) { CJC_ASSERT(!dest->GetType() || dest->GetType()->IsRef()); auto firstRef = StaticCast(dest->GetType()); CJC_ASSERT(firstRef->IsRef()); auto refOuter = CreateNewRef(expr); Update(dest, refOuter); auto refInner = CreateNewRef(expr, true); refMap.emplace(refOuter, refInner); auto obj = CreateNewObject(GetObjName(absObjPool->size()), expr); refMap.emplace(refInner, obj); SetToBound(obj, true); return obj; } /// check value is bottom bool CheckValueIsBottom(const Ptr& value) const { auto it = programState.find(value); return it != programState.end() && it->second.GetKind() == ValueDomain::ValueKind::BOTTOM; } /// check value is top bool CheckValueIsTop(const Ptr& value) const { auto it = programState.find(value); return it != programState.end() && it->second.GetKind() == ValueDomain::ValueKind::TOP; } bool CheckValueIsObject(const Ptr& value) const { auto it = programState.find(value); return it != programState.end() && it->second.GetKind() == ValueDomain::ValueKind::VAL; } bool CheckValueIsRef(const Ptr& value) const { auto it = programState.find(value); return it != programState.end() && it->second.GetKind() == ValueDomain::ValueKind::REF; } /// clear all states. void ClearState() { for (auto& it : programState) { if (it.second.GetKind() == ValueDomain::ValueKind::VAL) { // set to top it.second = true; } } } private: Ref* CreateNewRef(const Expression* expr = nullptr, bool createTwoLevelRef = false) { auto& allocateMap = createTwoLevelRef ? allocatedTwoLevelRefMap : allocatedRefMap; Ref* ref = nullptr; bool isStaticRef = &ValueAnalysis::globalState == this; if (expr) { if (auto it = allocateMap->find(expr); it != allocateMap->end()) { ref = it->second; } else { ref = refPool->emplace_back(std::make_unique(GetRefName(refPool->size()), isStaticRef)).get(); allocateMap->emplace(expr, ref); } } else { ref = refPool->emplace_back(std::make_unique(GetRefName(refPool->size()), isStaticRef)).get(); } return ref; } AbstractObject* CreateNewObject(std::string objectName, const Expression* expr = nullptr) { objectName = &ValueAnalysis::globalState == this ? "s" + objectName : objectName; AbstractObject* obj = nullptr; if (expr) { if (auto it = allocatedObjMap->find(expr); it != allocatedObjMap->end()) { obj = it->second; } else { obj = absObjPool->emplace_back(std::make_unique(objectName)).get(); allocatedObjMap->emplace(expr, obj); } } else { obj = absObjPool->emplace_back(std::make_unique(objectName)).get(); } return obj; } /** * Initialise the state of @p dest to a Top or a TopRef depends on @p isRef. */ void InitToTopOrTopRef(Value* dest, bool isRef) { CJC_ASSERT(programState.find(dest) == programState.end()); if (isRef) { programState.emplace(dest, Ref::GetTopRefInstance()); } else { programState.emplace(dest, ValueDomain(/* isTop = */ true)); } } /** * Propagate the state of value @p src to @p dest. Comparing to the function @fn Propagate, * this function won't propagate the state of these values' children. */ void PropagateWithoutChildren(Value* src, Value* dest, ProgramState* state = nullptr) { state = !state ? &this->programState : state; if (!src->IsParameter() && !src->IsLocalVar()) { CJC_ASSERT(src->IsFunc()); return (void)state->try_emplace(dest, /* isTop = */ true); } if (auto it = state->find(dest); it != state->end()) { it->second = programState.at(src); } else { state->emplace(dest, programState.at(src)); } } /** * Create the children of @p obj based on the @p childrenNum. The parameter @p setChildState * is used to set the state of each child. */ void CreateChildren(Value* obj, size_t childrenNum, std::function setChildState) { auto objName = obj->GetIdentifier(); std::vector children; for (size_t i = 0; i < childrenNum; ++i) { auto child = CreateNewObject(GetObjChildName(objName, i)); setChildState(child, i); children.emplace_back(child); } if (!children.empty()) { childrenMap->emplace(obj, std::move(children)); } } void SetSelfAndChildrenStateToTop(Value* val) { Value* obj = nullptr; if (val->GetType()->IsRef()) { obj = CheckAbstractObjectRefBy(val); } else { obj = val; } if (obj) { SetToBound(obj, /* isTop = */ true); SetNonTopChildrenStateToTop(GetChildren(obj)); } } void SetNonTopChildrenStateToTop(const std::vector& children) { for (auto child : children) { auto childIt = programState.find(child); CJC_ASSERT(childIt != programState.end()); if (childIt->second.GetKind() == ValueDomain::ValueKind::VAL) { childIt->second = /* isTop = */ true; } } } void StoreGVChildrenState(Value* src, AbstractObject* dest) { auto& gs = ValueAnalysis::globalState; auto srcChildrenIt = childrenMap->find(src); if (srcChildrenIt == childrenMap->end()) { return; } auto& srcChildren = srcChildrenIt->second; auto destChildren = gs.GetChildren(dest); CJC_ASSERT(srcChildren.size() == destChildren.size()); for (size_t i = 0; i < srcChildren.size(); ++i) { auto srcStateIt = programState.find(srcChildren[i]); CJC_ASSERT(srcStateIt != programState.end()); auto& objState = srcStateIt->second; if (objState.GetKind() == ValueDomain::ValueKind::VAL) { gs.Update(destChildren[i], objState); } else if (objState.GetKind() == ValueDomain::ValueKind::REF) { if (auto srcObj = CheckAbstractObjectRefBy(srcChildren[i]); srcObj) { // The state of a local child may be a TopRef. auto targetObj = gs.GetReferencedObjAndSetToTop(destChildren[i]); PropagateWithoutChildren(srcObj, targetObj, &gs.programState); } } } } void LoadGVChildrenState(AbstractObject* src, LocalVar* dest) { auto objChildrenIt = ValueAnalysis::globalState.childrenMap->find(src); if (objChildrenIt == ValueAnalysis::globalState.childrenMap->end()) { return; } auto& objChildren = objChildrenIt->second; const auto setChildState = [this, &objChildren](AbstractObject* child, size_t index) { auto& srcState = ValueAnalysis::globalState.programState.at(objChildren[index]); programState.emplace(child, srcState); if (srcState.GetKind() != ValueDomain::ValueKind::REF) { return; } if (auto ref = srcState.GetRef(); !ref->IsTopRefInstance() && refMap.find(ref) == refMap.end()) { auto& obj = ValueAnalysis::globalState.refMap.at(ref); refMap.try_emplace(ref, obj); CJC_ASSERT(std::holds_alternative(obj)); programState.insert( *ValueAnalysis::globalState.programState.find(std::get(obj))); } }; if (auto it = childrenMap->find(dest); it != childrenMap->end()) { auto& destChildren = it->second; CJC_ASSERT(objChildren.size() == destChildren.size()); for (size_t i = 0; i < destChildren.size(); ++i) { setChildState(destChildren[i], i); } } else { CreateChildren(dest, objChildren.size(), setChildState); } } bool RefMapJoin(const State& rhs) { const auto action = [this, &rhs](const Ref* key, std::variant& v1, const std::variant& v2) -> bool { (void)key; if (auto ref1 = std::get_if(&v1); ref1) { if ((*ref1)->IsTopRefInstance()) { return false; } CJC_ASSERT(std::holds_alternative(v2)); auto ref2 = std::get(v2); if (ref2->IsTopRefInstance()) { v1 = Ref::GetTopRefInstance(); return true; } if (*ref1 != ref2) { if ((*ref1)->IsEquivalent(ref2)) { return false; } else if (ref2->CanRepresent(*ref1)) { v1 = ref2; return true; } else if ((*ref1)->CanRepresent(ref2)) { return false; } else { v1 = MergeRef(*ref1, ref2, rhs.programState, rhs.refMap); return true; } } } else { CJC_ASSERT(std::holds_alternative(v1)); CJC_ASSERT(std::holds_alternative(v2)); CJC_ASSERT(std::get(v1) == std::get(v2)); } return false; }; return MapJoinTemplate>(refMap, rhs.refMap, action); } Ref* MergeRef(Ref* lhs, Ref* rhs, const ProgramState& rhsProgramState, const RefMap& rhsRefMap) { const auto getAbsObject = [](const RefMap& refMap1, Ref* ref) -> AbstractObject* { auto it = refMap1.find(ref); if (auto val = std::get_if(&it->second); val) { return *val; } else { return nullptr; } }; auto newRef = CreateNewRef(); newRef->AddRoots(lhs, rhs); auto lhsObj = getAbsObject(refMap, lhs); auto rhsObj = getAbsObject(rhsRefMap, rhs); CJC_ASSERT(lhsObj && rhsObj); auto newObj = CreateNewObject(GetObjName(absObjPool->size())); ValueDomain newAbsVal = programState.at(lhsObj); // should be a clone newAbsVal.Join(rhsProgramState.at(rhsObj)); programState.emplace(newObj, std::move(newAbsVal)); refMap.emplace(newRef, newObj); return newRef; } private: ProgramState programState; RefMap refMap; ChildrenMap* childrenMap; AllocatedRefMap* allocatedRefMap; // only for inner ref of && AllocatedRefMap* allocatedTwoLevelRefMap; AllocatedObjMap* allocatedObjMap; std::vector>* refPool; std::vector>* absObjPool; }; /** * @brief check if global var need analyse. * @tparam TDomain specific value domain. * @return true means need analyse. */ template inline bool IsTrackedGV(const GlobalVar& /* gv */) { return true; } /** * @brief handle literal value of CHIR IR. * @tparam TDomain specific value domain. * @return domain related to literal value. */ template inline TDomain HandleNonNullLiteralValue(const LiteralValue* /* literal */) { return TDomain(/* isTop = */ true); } /** * @brief abstract value analysis of ValueDomain. * @tparam ValueDomain specific ValueDomain */ template class ValueAnalysis : public Analysis> { public: using isValueAnalysis = void; /// delete default construtor. ValueAnalysis() = delete; /** * @brief constructor for value analysis * @param func function to analyse * @param builder CHIR builder for generating IR. * @param isDebug flag whether print debug log. */ ValueAnalysis(const Func* func, CHIRBuilder& builder, bool isDebug = false) : Analysis>(func, isDebug), builder(builder) { } virtual ~ValueAnalysis() override { } /** * @brief init all global variables. * @param package package to analyse. * @param builder CHIR builder for generating IR. */ static void InitialiseLetGVState(const Package& package, CHIRBuilder& builder) { globalState.kind = ReachableKind::REACHABLE; for (auto gv : package.GetGlobalVars()) { if (!gv->TestAttr(Attribute::READONLY) || !gv->GetInitFunc() || !IsTrackedGV(*gv)) { continue; } auto realTy = StaticCast(gv->GetType())->GetBaseType(); if (realTy->IsRef()) { auto ref = globalState.CreateNewRef(nullptr); globalState.programState.emplace(gv, ref); auto oRef = globalState.CreateNewRef(nullptr); globalState.refMap.emplace(ref, oRef); auto obj = globalState.CreateNewObject(GetObjName(globalAllocatedObjMap.size()), nullptr); globalState.refMap.emplace(oRef, obj); globalState.programState.emplace(obj, true); } else { auto obj = globalState.GetReferencedObjAndSetToTop(gv, nullptr); SetObjChildrenStateToTop(globalState, obj, realTy, builder); } } } /** * @brief get bottom of state * @return return bottom state. */ State Bottom() final { return State( &childrenMap, &allocatedTwoLevelRefMap, &allocatedRefMap, &allocatedObjMap, &refPool, &absObjPool); } /** * @brief init function entry state. * @param state state to store function entry state. */ void InitializeFuncEntryState(State& state) override { state.kind = ReachableKind::REACHABLE; for (auto param : this->func->GetParams()) { if (param->GetType()->IsRef() || param->GetType()->IsGeneric()) { auto refObj = state.GetReferencedObjAndSetToTop(param, nullptr); HandleFuncParam(state, param, refObj); } else { state.programState.emplace(param, /* isTop = */ true); } } } /** * @brief init lambda entry state. * @param state state to store lambda entry state. */ void InitializeLambdaEntryState(State& state) override { CJC_ASSERT(this->currentLambda.has_value()); for (auto param : this->currentLambda.value()->GetParams()) { if (param->GetType()->IsRef() || param->GetType()->IsGeneric()) { auto refObj = state.GetReferencedObjAndSetToTop(param, nullptr); HandleFuncParam(state, param, refObj); } else { state.programState.emplace(param, /* isTop = */ true); } } } /** * @brief init state of lambda capture vars. * @param state state to store lambda capture vars. * @param lambda analysed lambda. */ void HandleVarStateCapturedByLambda(State& state, const Lambda* lambda) override { for (auto var : GetLambdaCapturedVarsRecursively(*lambda)) { state.SetSelfAndChildrenStateToTop(var); } } /** * @brief init state of lambda capture vars. * @param state state to store lambda capture vars. * @param lambda analysed lambda. */ void PreHandleLambdaExpression(State& state, const Lambda* lambda) override { state.SetToBound(lambda->GetResult(), /* isTop = */ true); } /** * @brief propagate state in normal expression. * @param state state to store all domain. * @param expression normal expression to analyse domain. */ void PropagateExpressionEffect(State& state, const Expression* expression) final { switch (expression->GetExprMajorKind()) { case ExprMajorKind::MEMORY_EXPR: PreHandleMemoryExpr(state, expression); break; case ExprMajorKind::UNARY_EXPR: case ExprMajorKind::BINARY_EXPR: { break; } case ExprMajorKind::OTHERS: { PreHandleOthersExpr(state, expression); break; } case ExprMajorKind::STRUCTURED_CTRL_FLOW_EXPR: default: InternalError("Unsupported major expr kind"); break; } HandleNormalExpressionEffect(state, expression); } /** * @brief propagate state in terminators. * @param state state to store all domain. * @param terminator normal terminators to analyse domain. * @return blocks may goto after analysing. */ std::optional PropagateTerminatorEffect(State& state, const Terminator* terminator) override { switch (terminator->GetExprKind()) { case ExprKind::APPLY_WITH_EXCEPTION: { return PreHandleApplyExpr(state, StaticCast(terminator)); } case ExprKind::INVOKE_WITH_EXCEPTION: { return PreHandleInvokeExpr(state, StaticCast(terminator)); } case ExprKind::ALLOCATE_WITH_EXCEPTION: { auto allocate = StaticCast(terminator); auto refObj = PreHandleAllocateExpr(state, allocate); return HandleAllocateWithExceptionTerminator(state, allocate, refObj); } case ExprKind::RAW_ARRAY_ALLOCATE_WITH_EXCEPTION: { return PreHandleRawArrayAllocate(state, StaticCast(terminator)); } case ExprKind::INTRINSIC_WITH_EXCEPTION: { auto intrinsic = StaticCast(terminator); if (intrinsic->GetIntrinsicKind() == CHIR::IntrinsicKind::INOUT_PARAM) { return PreHandleInoutIntrinsic(state, intrinsic); } else { break; } } default: break; } return HandleTerminatorEffect(state, terminator); } /// state of global variables. static State globalState; /// children map of global variables. static typename State::ChildrenMap globalChildrenMap; /// allocate ref map of global variables. static typename State::AllocatedRefMap globalAllocatedRefMap; /// allocate object map of global variables. static typename State::AllocatedObjMap globalAllocatedObjMap; /// all global reference static std::vector> globalRefPool; /// all global object static std::vector> globalAbsObjPool; protected: virtual void PreHandleGetElementRefExpr(State& state, const GetElementRef* getElemRef) { auto dest = getElemRef->GetResult(); auto destIt = state.programState.find(dest); if (destIt == state.programState.end()) { auto destRef = state.CreateNewRef(getElemRef); state.programState.emplace(dest, destRef); state.refMap.emplace(destRef, FindTargetElement(state, getElemRef)); } else { CJC_ASSERT(destIt->second.GetKind() == ValueDomain::ValueKind::REF); auto destRef = destIt->second.GetRef(); auto destRefIt = state.refMap.find(destRef); CJC_ASSERT(destRefIt != state.refMap.end()); destRefIt->second = FindTargetElement(state, getElemRef); } } virtual void PreHandleFieldExpr(State& state, const Field* field) { auto dest = field->GetResult(); auto indexes = field->GetPath(); if (indexes.size() > 1) { if (auto it = state.programState.find(dest); it == state.programState.end()) { state.InitToTopOrTopRef(dest, dest->GetType()->IsRef()); } return; } if (auto it = childrenMap.find(field->GetBase()); it != childrenMap.end()) { auto& children = it->second; if (indexes[0] < children.size()) { return state.PropagateWithoutChildren(children[indexes[0]], field->GetResult()); } } state.SetToTopOrTopRef(dest, dest->GetType()->IsRef()); } private: void PreHandleMemoryExpr(State& state, const Expression* expression) { switch (expression->GetExprKind()) { case ExprKind::ALLOCATE: { auto allocate = StaticCast(expression); auto refObj = PreHandleAllocateExpr(state, allocate); HandleAllocateExpr(state, allocate, refObj); break; } case ExprKind::LOAD: { PreHandleLoadExpr(state, StaticCast(expression)); break; } case ExprKind::STORE: { PreHandleStoreExpr(state, StaticCast(expression)); break; } case ExprKind::GET_ELEMENT_REF: { PreHandleGetElementRefExpr(state, StaticCast(expression)); break; } case ExprKind::STORE_ELEMENT_REF: { PreHandleStoreElementRefExpr(state, StaticCast(expression)); break; } default: { InternalError("Unexpected memory expr"); break; } } } bool PreHandleOthersExpr(State& state, const Expression* expression) { switch (expression->GetExprKind()) { case ExprKind::CONSTANT: { PreHandleConstantExpr(state, StaticCast(expression)); return true; } case ExprKind::DEBUGEXPR: { return true; } case ExprKind::TUPLE: { PreHandleTupleExpr(state, StaticCast(expression)); return true; } case ExprKind::FIELD: { PreHandleFieldExpr(state, StaticCast(expression)); return true; } case ExprKind::APPLY: { PreHandleApplyExpr(state, StaticCast(expression)); return true; } case ExprKind::INVOKE: { PreHandleInvokeExpr(state, StaticCast(expression)); return true; } case ExprKind::TYPECAST: { auto cast = StaticCast(expression); if (!cast->Get()) { return PreHandleNonCheckedTypeCast(state, cast); } return false; } case ExprKind::RAW_ARRAY_ALLOCATE: { PreHandleRawArrayAllocate(state, StaticCast(expression)); return true; } case ExprKind::INTRINSIC: { auto intrinsic = StaticCast(expression); if (intrinsic->GetIntrinsicKind() == CHIR::IntrinsicKind::INOUT_PARAM) { PreHandleInoutIntrinsic(state, intrinsic); return true; } else { return false; } } default: { return false; } } } void PreHandleConstantExpr(State& state, const Constant* constant) { auto dest = constant->GetResult(); if (state.programState.find(dest) != state.programState.end()) { return; } if (constant->IsConstantNull()) { if (dest->GetType()->IsRef()) { state.programState.emplace(dest, Ref::GetTopRefInstance()); } else { state.programState.emplace(dest, /* isTop = */ true); } } else { state.programState.emplace(dest, HandleNonNullLiteralValue(constant->GetValue())); } } template AbstractObject* FindTargetElement(State& state, const TElemRef* elemRef) { auto loc = elemRef->GetLocation(); if (loc->IsGlobal() || loc->TestAttr(Attribute::STATIC)) { return AbstractObject::GetTopObjInstance(); } auto& paths = elemRef->GetPath(); if (paths.size() > 1) { return AbstractObject::GetTopObjInstance(); } auto locIt = state.programState.find(loc); if (locIt == state.programState.end()) { if (this->isDebug) { std::cout << "Value Analysis: use-before-initialization detected"; } return AbstractObject::GetTopObjInstance(); } auto& locVal = locIt->second; CJC_ASSERT(locVal.GetKind() == ValueDomain::ValueKind::REF); auto locRef = locVal.GetRef(); if (locRef->IsTopRefInstance()) { return AbstractObject::GetTopObjInstance(); } auto refIt = state.refMap.find(locRef); CJC_ASSERT(refIt != state.refMap.end()); CJC_ASSERT(std::holds_alternative(refIt->second)); auto rootObj = std::get(refIt->second); auto childrenIt = state.childrenMap->find(rootObj); if (childrenIt == state.childrenMap->end() || paths[0] >= childrenIt->second.size()) { return AbstractObject::GetTopObjInstance(); } return childrenIt->second[paths[0]]; } void PreHandleStoreExpr(State& state, const Store* store) { /* * We are storing to a Ref. And there are two cases (can be splitted into four). * The first case (a&b) is that we are trying to put a new Ref to the location of the Store * expression, which means the location of the Store (a Ref) stores a Ref or a Bottom. * Thus, we just need to replace the old Ref or the old Bottom. * * The second case (c&d) is that the Ref is referencing an AbstractObject, whicn means we * need to update the abstract value of this AbstractObject in `programState`. If this * AbstractObject is a compound data structure, we also need to update the abstract value * of its children. * * a) class CA { var x = 1 }; ... var ca = CA() ... * IR: %0 : Class-CA& = Allocate(Class-CA) * %1 : Unit = Apply(init, %0) * %ca : Class-CA&& = Allocate(Class-CA&) // currently, %ca |-> Ref0. * %3 : Unit = Store(%0 : Class-CA&, %ca : Class-CA&&) * programState: { %0 |-> Ref0, %ca |-> Ref1, Obj0 |-> Top, Obj0.x |-> 1 } * refMap: { Ref1 |-> Ref2, Ref0 |-> Obj0 } * * b) class CA { var x = 1 }; ... var ca = CA(); ca = CA() ... * IR: %0 : Class-CA& = Allocate(Class-CA) * %1 : Unit = Apply(init, %0) * %3 : Unit = Store(%0, %ca : Class-CA&&) // currently, %ca |-> Ref1, Ref1 |-> Ref0. * programState: { %0 |-> Ref2, Obj1 |-> Top, Obj1.x |-> 1, %ca |-> Ref1, Obj0 |-> Top, Obj0.x |-> 1 } * refMap: { Ref2 |-> Obj1, Ref1 |-> Ref2, Ref0 |-> Obj0 } * * c) class CA { var x = 1 }; ... var ca = CA(); ca.x = 2 ... * IR: %0 : Int64 = ConstantInt(2) * %1 : Class-CA& = Load(%ca : Class-CA&&) * %2 : Int64& = GetElementRef(%1, 0) * %3 : Unit = Store(%0, %2) * programState: { %2 |-> Ref2, %1 |-> Ref0, %0 |-> 2, %ca |-> Ref1, Obj0 |-> Top, Obj0.x |-> 2 } * refMap: { Ref2 |-> Obj0.x, Ref1 |-> Ref0, Ref0 |-> Obj0 } * * d) struct SA { var y = 2 }; ... var sa = SA(); sa = SA() ... * IR: %0 : SA& = Allocate(SA) * %1 : Unit = Apply(init, %0 : SA&) * %2 : SA = Load(%0 : SA&) * %3 : Unit = Store(%2 : SA, %sa : SA&) * programState: { %2 |-> Top, %2.y |-> 2, %0 |-> Ref1, Obj1 |-> Top, Obj1.y |-> 2, * %sa |-> Ref0, Obj0 |-> Top, Obj0.y |-> 2 } * refMap: { Ref1 |-> Obj1, Ref0 |-> Obj0 } */ auto value = store->GetValue(); if (value->IsGlobal() || value->TestAttr(Attribute::STATIC)) { return; } auto location = store->GetLocation(); if (location->IsGlobal() || location->TestAttr(Attribute::STATIC)) { return HandleStoreToGlobal(state, location, value); } auto valIt = state.programState.find(value); CJC_ASSERT(valIt != state.programState.end()); auto locIt = state.programState.find(location); CJC_ASSERT(locIt != state.programState.end()); auto& locVal = locIt->second; CJC_ASSERT(locVal.GetKind() == ValueDomain::ValueKind::REF); auto locRef = locVal.GetRef(); if (locRef->IsTopRefInstance()) { return; } if (auto it = state.refMap.find(locRef); it != state.refMap.end()) { auto& locRefVal = it->second; if (std::holds_alternative(locRefVal)) { if (auto dest = std::get(locRefVal); !dest->IsTopObjInstance()) { // c, d) CJC_ASSERT(location->IsLocalVar()); if (StaticCast(location)->GetExpr()->GetExprKind() == ExprKind::GET_ELEMENT_REF) { state.PropagateWithoutChildren(value, dest); } else { state.Propagate(value, dest); } } } else { // b auto& toBeStored = valIt->second; CJC_ASSERT(toBeStored.GetKind() == ValueDomain::ValueKind::REF); it->second = const_cast(toBeStored.GetRef()); } } else { // a) auto& toBeStored = valIt->second; CJC_ASSERT(toBeStored.GetKind() == ValueDomain::ValueKind::REF); state.refMap.emplace(locRef, const_cast(toBeStored.GetRef())); } } void HandleStoreToGlobal(State& state, Value* location, Value* value) { if (this->isStable || !location->IsGlobalVarInCurPackage()) { return; } auto gv = VirtualCast(location); if (!gv->TestAttr(Attribute::READONLY) || globalState.programState.find(gv) == globalState.programState.end()) { return; } auto targetObj = globalState.CheckAbstractObjectRefBy(gv); CJC_NULLPTR_CHECK(targetObj); if (gv->GetType()->GetRefDims() == 1) { state.PropagateWithoutChildren(value, targetObj, &globalState.programState); state.StoreGVChildrenState(value, targetObj); } else { // The global var must be a class / raw array, and we don't record children of them. CJC_ASSERT(globalState.GetChildren(targetObj).empty()); if (auto srcObj = state.CheckAbstractObjectRefBy(value); srcObj) { state.PropagateWithoutChildren(srcObj, targetObj, &globalState.programState); } else { globalState.Update(targetObj, /* isTop = */ true); } } } void PreHandleLoadExpr(State& state, const Load* load) { /* * We are loading from a Ref. Similar to the store expression, there are also two cases here. * The first case (a) is loading from a two-level Ref. Thus, what we get is just another * one-level Ref. * * The second case (b,c) is loading from an one-level Ref, which means it stores a value * instead of a Ref. Therefore, we will copy the abstract value of the abstract object * referenced by the Ref. If this AbstractObject is a compound data structure, we also need * to copy the abstract value of its children. * * a,b) class CA { var x = 1 }; ... var ca = CA(); ca.x ... * IR: %0 : Class-CA& = Load(%ca : Class-CA&&) the first Load * %1 : Int64& = GetElementRef(%0 : Class-CA&, 0) * %2 : Int64 = Load(%1 : Int64&) the second Load * programState: { %2 |-> 1, %1 |-> Ref2, %0 |-> Ref0, %ca |-> Ref1, Obj0 |-> Top, Obj0.x |-> 1 } * refMap: { Ref2 |-> Obj0.x, Ref1 |-> Ref0, Ref0 |-> Obj0 } * * c) struct SA { var y = 2 }; ... var sa = SA(); sa = SA() ... * IR: %0 : SA& = Allocate(SA) * %1 : Unit = Apply(init, %0 : SA&) * %2 : SA = Load(%0 : SA&) * ... * programState: { %2 |-> Top, %2.y |-> 2, %0 |-> Ref1, Obj1 |-> Top, Obj1.y |-> 2, * %sa |-> Ref0, Obj0 |-> Top, Obj0.y |-> 2 } * refMap: { Ref1 |-> Obj1, Ref0 |-> Obj0 } */ auto loc = load->GetLocation(); if (loc->IsGlobal() || loc->TestAttr(Attribute::STATIC)) { return HandleLoadFromGlobal(state, load); } auto dest = load->GetResult(); auto locIt = state.programState.find(loc); CJC_ASSERT(locIt != state.programState.end()); auto& locVal = locIt->second; CJC_ASSERT(locVal.GetKind() == ValueDomain::ValueKind::REF); if (locVal.GetRef()->IsTopRefInstance()) { return state.SetToTopOrTopRef(dest, dest->GetType()->IsRef()); } auto refIt = state.refMap.find(locVal.GetRef()); if (refIt == state.refMap.end()) { if (this->isDebug) { std::cout << "Value Analysis: use-before-initialization detected"; } return; } auto& refVal = refIt->second; if (std::holds_alternative(refVal)) { // a) CJC_ASSERT(dest->GetType()->IsRef()); return state.Update(dest, std::get(refVal)); } else { auto obj = std::get(refVal); if (obj->IsTopObjInstance()) { // %0 = GetElementRef(...) %0 -> Refx, Refx -> TopObj // %1 = Load(%0) %1 -> Top/TopRef depends on the type return state.SetToTopOrTopRef(dest, dest->GetType()->IsRef()); } else { // b,c) return state.Propagate(std::get(refVal), dest); } } } void HandleLoadFromGlobal(State& state, const Load* load) { auto dest = load->GetResult(); if (state.programState.find(dest) != state.programState.end()) { return; } auto loc = load->GetLocation(); if (!loc->IsGlobalVarInCurPackage()) { return state.InitToTopOrTopRef(dest, dest->GetType()->IsRef()); } auto globalVar = VirtualCast(loc); if (!globalVar->TestAttr(Attribute::READONLY)) { return state.InitToTopOrTopRef(dest, dest->GetType()->IsRef()); } auto initializer = globalVar->GetInitializer(); if (initializer) { CJC_ASSERT(!initializer->IsNullLiteral()); state.programState.emplace(dest, HandleNonNullLiteralValue(initializer)); } else { auto gvIt = globalState.programState.find(globalVar); if (gvIt == globalState.programState.end()) { return state.InitToTopOrTopRef(dest, dest->GetType()->IsRef()); } auto gRefIt = globalState.refMap.find(gvIt->second.GetRef()); CJC_ASSERT(gRefIt != globalState.refMap.end()); if (std::holds_alternative(gRefIt->second)) { // The global var must be a class / raw array, and we don't record children of them. // gv_a -> sRef1, sRef1 -> sRef2, sRef2 -> Obj0, Obj0 -> Class-CA // %0 = Load(gv_a), %0 -> sRef2, sRef2 -> Obj0 auto oRef = std::get(gRefIt->second); state.programState.emplace(dest, oRef); auto oRefIt = globalState.refMap.find(oRef); CJC_ASSERT(oRefIt != globalState.refMap.end()); auto obj = std::get(oRefIt->second); state.refMap.emplace(oRef, obj); auto objIt = globalState.programState.find(obj); CJC_ASSERT(objIt != globalState.programState.end()); state.programState.emplace(obj, objIt->second); } else { auto obj = std::get(gRefIt->second); globalState.PropagateWithoutChildren(obj, dest, &state.programState); state.LoadGVChildrenState(obj, dest); } } } void PreHandleStoreElementRefExpr(State& state, const StoreElementRef* storeElemRef) { auto value = storeElemRef->GetValue(); if (value->IsGlobal() || value->TestAttr(Attribute::STATIC)) { return; } auto targetObj = FindTargetElement(state, storeElemRef); if (targetObj->IsTopObjInstance()) { return; } state.PropagateWithoutChildren(value, targetObj); } template Value* PreHandleAllocateExpr(State& state, const TAllocate* allocate) { // In our framework, we associate a *single* memory location with every static allocation site. // That is, when we meet an Allocation expression again, we won't come up with a new Ref, instead // we will keep the same old Ref. // The reason that we don't generate a new fresh Ref is, the iteration won't be converge. If the // program has a loop, the analysis will keep going around the loop, generating new memory locations, // getting back to the top of the loop with a new input value. Thus, we'll need to rerun the analysis // for the loop again, and this will never stop. // e.g. // approach (a): Generate a new Ref everytime // while() { round1 round2 round3 // var a = CA() a |-> Ref1 a |-> Ref2 a |-> Ref3 ...BOOM! // } // approach (b): Generate a single Ref everytime // while() { round1 round2 // var a = CA() a |-> Ref1 a |-> Ref ...stable :-D // } auto dest = allocate->GetResult(); auto allocatedTy = allocate->GetType(); if (allocatedTy->IsRef()) { // %0 : Class-CA&& = Allocate(Class-CA&) // programState: { %0 |-> Ref1 } state.programState.emplace(dest, state.CreateNewRef(allocate)); return nullptr; } else { // struct SA { var x = 1; var y = CA() } // %0 : Struct-SA& = Allocate(Struct-SA) // programState: { %0 |-> Ref0, Obj0 |-> Top, Obj0.0 -> Top, Obj0.1 -> TopRef }, // where Obj0.0 represents member `x`, Obj0.1 represents member `y` // refMap: { Ref0 |-> Obj0 } auto obj = state.GetReferencedObjAndSetToTop(dest, allocate); SetObjChildrenStateToTop(state, obj, allocatedTy, builder); return obj; } } template std::optional PreHandleApplyExpr(State& state, const TApply* apply) { // check if this apply is a call to a mut func of a struct if (auto callee = apply->GetCallee(); callee->TestAttr(Attribute::MUT)) { auto structArg = apply->GetArgs()[0]; auto structTy = StaticCast(structArg->GetType())->GetBaseType(); if (structTy->IsStruct()) { if (auto obj = state.CheckAbstractObjectRefBy(structArg); obj) { if (childrenMap.find(obj) != childrenMap.end()) { SetObjChildrenStateToTop(state, obj, structTy, builder); } } } else { CJC_ASSERT(structTy->IsClass()); // actually interface in Cangjie } } auto refObj = PreHandleFuncCall(state, apply); if constexpr (std::is_same_v) { HandleApplyExpr(state, apply, refObj); return std::nullopt; } else { return HandleApplyWithExceptionTerminator(state, apply, refObj); } } template std::optional PreHandleInvokeExpr(State& state, const TInvoke* invoke) { auto refObj = PreHandleFuncCall(state, invoke); if constexpr (std::is_same_v) { HandleInvokeExpr(state, invoke, refObj); return std::nullopt; } else { return HandleInvokeWithExceptionTerminator(state, invoke, refObj); } } template Value* PreHandleFuncCall(State& state, const T* apply) { auto dest = apply->GetResult(); auto ty = dest->GetType(); if (ty->IsRef() || ty->IsGeneric()) { return state.GetReferencedObjAndSetToTop(dest, apply); } else { state.SetToBound(dest, /* isTop = */ true); SetObjChildrenStateToTop(state, dest, ty, builder); return nullptr; } } static void SetObjChildrenStateToTop(State& state, Value* root, Type* rootTy, CHIRBuilder& builder) { std::vector childrenTypes; if (rootTy->GetTypeKind() == Type::TypeKind::TYPE_STRUCT) { auto structTy = StaticCast(rootTy); auto structDef = structTy->GetStructDef(); if (structTy->IsStructArray()) { childrenTypes = {true, false, false}; } else if (!structDef->IsCStruct()) { for (auto ty : structTy->GetInstantiatedMemberTys(builder)) { childrenTypes.emplace_back(ty->IsRef()); } } } else if (rootTy->GetTypeKind() == Type::TypeKind::TYPE_TUPLE) { auto tupleTy = StaticCast(rootTy); for (auto ty : tupleTy->GetElementTypes()) { childrenTypes.emplace_back(ty->IsRef()); } } else if (rootTy->GetTypeKind() == Type::TypeKind::TYPE_ENUM) { // We only focus the state of index of an enum. And its type is not a ref type. childrenTypes.emplace_back(false); } else if (rootTy->GetTypeKind() == Type::TypeKind::TYPE_CLASS) { auto classTy = StaticCast(rootTy); auto classDef = classTy->GetClassDef(); if (classDef->GetSrcCodeIdentifier().find("$BOX_RNat5Array") == 0) { childrenTypes.emplace_back(classDef->GetInstanceVar(0).type->IsRef()); } } if (auto it = state.childrenMap->find(root); it != state.childrenMap->end()) { auto& children = it->second; CJC_ASSERT(children.size() == childrenTypes.size()); for (size_t i = 0; i < children.size(); ++i) { state.SetToTopOrTopRef(children[i], childrenTypes[i]); } } else { const auto setChildState = [&state, &childrenTypes](AbstractObject* child, size_t index) { state.SetToTopOrTopRef(child, childrenTypes[index]); }; state.CreateChildren(root, childrenTypes.size(), setChildState); } } void PreHandleTupleExpr(State& state, const Tuple* tuple) { state.SetToBound(tuple->GetResult(), /* isTop = */ true); auto operands = tuple->GetOperands(); auto operandNum = tuple->GetResult()->GetType()->IsEnum() ? 1 : operands.size(); if (auto it = childrenMap.find(tuple->GetResult()); it != childrenMap.end()) { auto& children = it->second; CJC_ASSERT(children.size() == operandNum); for (size_t i = 0; i < operandNum; ++i) { state.PropagateWithoutChildren(operands[i], children[i]); } } else { const auto setChildState = [&state, &operands](AbstractObject* child, size_t index) { state.PropagateWithoutChildren(operands[index], child); }; state.CreateChildren(tuple->GetResult(), operandNum, setChildState); } } bool PreHandleNonCheckedTypeCast(State& state, const TypeCast* cast) { auto dest = cast->GetResult(); if (dest->GetUsers().size() == 1U && dest->GetUsers()[0]->GetExprKind() == ExprKind::MULTIBRANCH) { // var x = 34; match (x) { ... } // There will be a 'TypeCast(%x, UInt64)' but its checkTypeCast is false. return false; } auto ty = dest->GetType(); if (ty->IsRef()) { const auto getClassDef = [](const Type& ty) { if (!ty.IsRef()) { return static_cast(nullptr); } auto baseTy = StaticCast(ty).GetBaseType(); if (!baseTy->IsClass()) { return static_cast(nullptr); } return StaticCast(baseTy)->GetClassDef(); }; auto targetClassDef = getClassDef(*ty); bool isCollection = targetClassDef && targetClassDef->GetSrcCodeIdentifier() == "Collection" && targetClassDef->GetPackageName() == Cangjie::CORE_PACKAGE_NAME; auto sourceClassDef = getClassDef(*cast->GetSourceTy()); bool isBoxArray = sourceClassDef && sourceClassDef->GetSrcCodeIdentifier().find(Cangjie::BOX_DECL_PREFIX) == 0 && sourceClassDef->GetAllInstanceVarNum() == 1 && sourceClassDef->GetInstanceVar(0).type->IsStructArray(); if (isBoxArray && isCollection) { return false; } state.GetReferencedObjAndSetToTop(dest, cast); } else { state.SetToBound(dest, /* isTop = */ true); SetObjChildrenStateToTop(state, dest, ty, builder); } return true; } template std::optional PreHandleRawArrayAllocate(State& state, const TRawArrayAllocate* allocate) { state.GetReferencedObjAndSetToTop(allocate->GetResult(), allocate); return std::nullopt; } template std::optional PreHandleInoutIntrinsic(State& state, const TIntrinsic* intrinsic) { auto param = intrinsic->GetOperand(0); if (!param->IsLocalVar()) { return std::nullopt; } state.SetSelfAndChildrenStateToTop(param); return std::nullopt; } // ============ functions that need to be implemented by a concrete analysis ============ // virtual void HandleFuncParam(State& state, Parameter* param, Value* refObj) { (void)state; (void)param; (void)refObj; } virtual void HandleAllocateExpr(State& state, const Allocate* expression, Value* refObj) { (void)state; (void)expression; (void)refObj; } virtual std::optional HandleAllocateWithExceptionTerminator( State& state, const AllocateWithException* allocate, Value* refObj) { (void)state; (void)allocate; (void)refObj; return std::nullopt; } virtual void HandleApplyExpr(State& state, const Apply* apply, Value* refObj) { (void)state; (void)apply; (void)refObj; } virtual std::optional HandleApplyWithExceptionTerminator( State& state, const ApplyWithException* apply, Value* refObj) { (void)state; (void)apply; (void)refObj; return std::nullopt; } virtual void HandleInvokeExpr(State& state, const Invoke* invoke, Value* refObj) { (void)state; (void)invoke; (void)refObj; } virtual std::optional HandleInvokeWithExceptionTerminator( State& state, const InvokeWithException* invoke, Value* refObj) { (void)state; (void)invoke; (void)refObj; return std::nullopt; } virtual void HandleNormalExpressionEffect(State& state, const Expression* expression) { (void)state; (void)expression; } virtual std::optional HandleTerminatorEffect(State& state, const Terminator* terminator) { (void)state; (void)terminator; return std::nullopt; } typename State::ChildrenMap childrenMap; typename State::AllocatedRefMap allocatedRefMap; typename State::AllocatedRefMap allocatedTwoLevelRefMap; typename State::AllocatedObjMap allocatedObjMap; std::vector> refPool; std::vector> absObjPool; protected: CHIRBuilder& builder; }; // namespace Cangjie::CHIR } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/ValueDomain.h000066400000000000000000000237451510705540100247320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_VALUE_DOMAIN_H #define CANGJIE_CHIR_ANALYSIS_VALUE_DOMAIN_H #include "cangjie/CHIR/Analysis/Analysis.h" #include "cangjie/CHIR/Value.h" #include #include #include #include #include #include namespace Cangjie::CHIR { /** * @brief template equal functor to compare domain */ template struct HasEqual { template static auto hasEqual(int) -> decltype(std::declval().Join(std::declval()), std::true_type()); template static auto hasToString(int) -> decltype(std::declval().ToString(), std::true_type()); template static auto hasClone(int) -> decltype(std::declval().Clone(), std::true_type()); template static std::false_type hasEqual(...); template static std::false_type hasToString(...); template static std::false_type hasClone(...); static constexpr bool value = decltype(hasEqual(0))::value && decltype(hasToString(0))::value && decltype(hasClone(0))::value; }; /** * @brief abstract object to store domain of CHIR value. */ class AbstractObject : public Value { public: /// create an abstract object from identifier created by analysis pass. explicit AbstractObject(std::string identifier); using Value::ToString; std::string ToString() const override; /// get static top object. static AbstractObject* GetTopObjInstance(); /// if this object is top object. bool IsTopObjInstance() const; }; class Ref { public: /// reference object of CHIR value. explicit Ref(std::string uniqueID, bool isStatic); /// get unique id created by analysis pass. std::string GetUniqueID() const; /// add input ref to this roots. void AddRoots(Ref* r1, Ref* r2); /// check if is same as input ref. bool IsEquivalent(Ref* r); /// check ref can be represented, write cache if meet new one. bool CanRepresent(Ref* r); /// get info directly from cache, otherwise return nullopt. std::optional CheckCache(Ref* r); /// write cache to ref with input. void WriteCache(Ref* r, bool res); /// get top ref. static Ref* GetTopRefInstance(); /// check if ref is top. bool IsTopRefInstance() const; private: bool isStatic; std::string uniqueID; std::unordered_set roots; std::unordered_map cache; std::mutex cacheMtx; }; /** * @brief Abstract domain for CHIR value. * @tparam AbstractValue value type to analyse in specific domain. */ template ::value>> class ValueDomain : public AbstractDomain> { public: /** * @brief The kind of a abstract value. A abstract value can be a Ref, a Val, Top or Bottom. * * Top * | \ \ \ * ... Ref(a) Ref(b) ... Val(1) Val(2) ... * \ \ \ | / / / * Bottom * * From the Hasse diagram, we can see that the abstract domain is not a standard domain. * This is, there is no an explicit partial-order relationship between a Ref and Top. * The reason is, if the abstract value of a value is a Ref. Then ... * */ enum class ValueKind { BOTTOM, REF, VAL, TOP }; /** * @brief delete default constructor. */ ValueDomain() = delete; /** * @brief create a bound domain. * @param isTop create top is true else bottom. */ explicit ValueDomain(bool isTop) : kind(isTop ? ValueKind::TOP : ValueKind::BOTTOM) { } /** * @brief create a ref domain. * @param ref ref to create domain. */ explicit ValueDomain(Ref* ref) : kind(ValueKind::REF), ref(ref) { } /** * @brief create an object domain. * @param absVal object to create domain. */ explicit ValueDomain(std::unique_ptr absVal) : kind(ValueKind::VAL), absVal(std::move(absVal)) { } /// copy constructor. ValueDomain(const ValueDomain& rhs) { this->kind = rhs.kind; this->ref = rhs.ref; if (rhs.absVal) { this->absVal = rhs.absVal->Clone(); } else { this->absVal.reset(); } } /// copy operator. ValueDomain& operator=(const ValueDomain& rhs) { this->kind = rhs.kind; this->ref = rhs.ref; if (rhs.absVal) { this->absVal = rhs.absVal->Clone(); } else { this->absVal.reset(); } return *this; } /// move constructor. ValueDomain(ValueDomain&& rhs) { this->kind = rhs.kind; this->ref = rhs.ref; this->absVal = std::move(rhs.absVal); } /// move operator. ValueDomain& operator=(ValueDomain&& rhs) { this->kind = rhs.kind; this->ref = rhs.ref; this->absVal = std::move(rhs.absVal); return *this; } /// create a bound domain from operator. ValueDomain& operator=(bool isTop) { this->kind = isTop ? ValueKind::TOP : ValueKind::BOTTOM; this->ref = nullptr; this->absVal.reset(); return *this; } /// create domain from a ref. ValueDomain& operator=(Ref* r) { CJC_ASSERT(kind != ValueKind::VAL); this->kind = ValueKind::REF; this->ref = r; return *this; } /// create domain from a value. ValueDomain& operator=(std::unique_ptr vl) { CJC_ASSERT(this->kind != ValueKind::REF); this->kind = ValueKind::VAL; this->absVal = std::move(vl); return *this; } /// destructor. virtual ~ValueDomain() { } /// join two different domain, return std::nullopt if no change happened. bool Join(const ValueDomain& rhs) override { /** * 1. (Top, _) => no change * 2. a) (Ref, Bottom) => no change * a) (Ref, Ref) => must be equal, thus no change * b) (Ref, Val) => impossible * c) (Ref, Top) => Top * 3. a) (Val, Bottom) => no change * b) (Val, Ref) => impossible * c) (Val(a), Val(b)) => Top if a != b; otherwise no change * d) (Val, Top) => Top * 4. a) (Bottom, Bottom) => no change * b) (Bottom, v) => v */ if (this->kind == ValueKind::TOP || rhs.kind == ValueKind::BOTTOM) { return false; } if (rhs.kind == ValueKind::TOP) { SetSelfToBound(/* isTop = */ true); return true; } if (this->kind == ValueKind::BOTTOM) { *this = rhs; return true; } if (this->kind == ValueKind::REF && rhs.kind == ValueKind::REF) { CJC_ASSERT(this->ref && rhs.ref); if (this->ref->IsTopRefInstance()) { return false; } else if (rhs.ref->IsTopRefInstance()) { SetSelfToTopRef(); return true; } else if (this->ref == rhs.ref) { return false; } else { SetSelfToTopRef(); return true; } } if (this->kind == ValueKind::VAL && rhs.kind == ValueKind::VAL) { CJC_ASSERT(this->absVal && rhs.absVal); auto res = this->absVal->Join(*(rhs.absVal)); if (res.has_value()) { if (res.value().get() == nullptr) { SetSelfToBound(/* isTop = */ true); } else { this->absVal = std::move(res.value()); } return true; } else { return false; } } // (Val, Ref) or (Ref, Val) CJC_ABORT(); return false; } /// check if domain is bottom. bool IsBottom() const override { return kind == ValueKind::BOTTOM; } /// check if domain is top. bool IsTop() const { return kind == ValueKind::TOP; } std::string ToString() const override { switch (kind) { case ValueKind::BOTTOM: return "Bottom"; case ValueKind::REF: return this->ref->GetUniqueID(); case ValueKind::VAL: return absVal->ToString(); case ValueKind::TOP: return "TOP"; } } /// get value kind. ValueKind GetKind() const { return kind; } /// get ref if kind is ref. const Ref* GetRef() const { CJC_ASSERT(kind == ValueKind::REF); return ref; } /// set ref if kind is ref. void SetRef(Ref* r) { CJC_ASSERT(kind != ValueKind::VAL); if (kind != ValueKind::REF) { kind = ValueKind::REF; } this->ref = r; } /// set to bound, top is true else bottom. void SetSelfToBound(bool isTop) { this->kind = isTop ? ValueKind::TOP : ValueKind::BOTTOM; ref = nullptr; absVal.reset(); } /// set to top ref if kind is ref. void SetSelfToTopRef() { CJC_ASSERT(kind == ValueKind::REF); ref = Ref::GetTopRefInstance(); } /// get object if kind is object. const AbstractValue* CheckAbsVal() const { CJC_ASSERT(kind != ValueKind::REF); return absVal ? absVal.get() : nullptr; } private: ValueKind kind; Ref* ref; std::unique_ptr absVal; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Analysis/ValueRangeAnalysis.h000066400000000000000000000174501510705540100262570ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANALYSIS_VALUE_RANGE_ANALYSIS_H #define CANGJIE_CHIR_ANALYSIS_VALUE_RANGE_ANALYSIS_H #include #include "cangjie/CHIR/Analysis/BoolDomain.h" #include "cangjie/CHIR/Analysis/SIntDomain.h" #include "cangjie/CHIR/Analysis/ValueAnalysis.h" #include "cangjie/CHIR/DiagAdapter.h" #include "cangjie/CHIR/Utils.h" namespace Cangjie::CHIR { class ValueRange { public: enum class RangeKind : uint8_t { BOOL, SINT }; ValueRange() = delete; explicit ValueRange(RangeKind kind); virtual ~ValueRange(); /// join two range, return nullopt if no change happened. virtual std::optional> Join(const ValueRange& rhs) const = 0; virtual std::string ToString() const = 0; virtual std::unique_ptr Clone() const = 0; /// get range kind, now suppert BOOL or SINT. RangeKind GetRangeKind() const; protected: RangeKind kind; }; class BoolRange : public ValueRange { public: explicit BoolRange(BoolDomain domain); ~BoolRange() override = default; /// join two range, return nullopt if no change happened. std::optional> Join(const ValueRange& rhs) const override; std::string ToString() const override; std::unique_ptr Clone() const override; /// get range kind, get BOOL for this range type. const BoolDomain& GetVal() const; private: BoolDomain domain; }; class SIntRange : public ValueRange { public: explicit SIntRange(SIntDomain domain); ~SIntRange() override = default; /// join two range, return nullopt if no change happened. std::optional> Join(const ValueRange& rhs) const override; std::string ToString() const override; std::unique_ptr Clone() const override; /// get range kind, get BOOL for this range type. const SIntDomain& GetVal() const; private: SIntDomain domain; }; /** * @brief the abstract value domain of range value */ using RangeValueDomain = ValueDomain; /** * @brief the state of range value domain */ using RangeDomain = State; /** * @brief partially specialized analysis import value. */ template <> const std::string Analysis::name; template <> const std::optional Analysis::blockLimit; template <> RangeDomain::ChildrenMap ValueAnalysis::globalChildrenMap; template <> RangeDomain::AllocatedRefMap ValueAnalysis::globalAllocatedRefMap; template <> RangeDomain::AllocatedObjMap ValueAnalysis::globalAllocatedObjMap; template <> std::vector> ValueAnalysis::globalRefPool; template <> std::vector> ValueAnalysis::globalAbsObjPool; template <> RangeDomain ValueAnalysis::globalState; /** * @brief heck whether global var need range analysis. * @param gv global var to check. * @return flag global var need analyse */ template <> bool IsTrackedGV(const GlobalVar& gv); /** * @brief literal value analysis function * @param literal input literal value to analyse * @return range value literalValue is. */ template <> RangeValueDomain HandleNonNullLiteralValue(const LiteralValue* literal); /** * @brief range analysis for CHIR IR. */ class RangeAnalysis final : public ValueAnalysis { public: RangeAnalysis() = delete; /** * @brief range analysis constructor. * @param func function to analyse * @param builder CHIR builder for generating IR. * @param isDebug flag whether print debug log. * @param diag reporter to report warning or error. */ RangeAnalysis(const Func* func, CHIRBuilder& builder, bool isDebug, const Ptr& diag); ~RangeAnalysis() override; /** * @brief get bool domain of CHIR value from state. * @param state state to get domain. * @param value CHIR value to get domain. * @return domain found in state. */ static BoolDomain GetBoolDomainFromState(const RangeDomain& state, const Ptr& value); /** * @brief get SInt domain of CHIR value from state. * @param state state to get domain. * @param value CHIR value to get domain. * @return domain found in state. */ static const SIntDomain& GetSIntDomainFromState(const RangeDomain& state, const Ptr& value); /** * @brief check this block analyse times, quit analysing in this block if inqueue more than a number. * @param block block to check inqueue times. * @param curState state to check. * @return true if InQueue time exceed the maximum else false. */ bool CheckInQueueTimes(const Block* block, RangeDomain& curState) override; private: template || std::is_same_v>> void PrintDebugMessage(const Ptr& expr, const Domain& domain) const { std::stringstream ss; ss << "[RangeAnalysis] The value of " + ExprKindMgr::Instance()->GetKindName(static_cast(expr->GetExprKind())) + ToPosInfo(expr->GetDebugLocation()) + " has been set to " << domain << "\n"; std::cout << ss.str(); } void PrintBranchOptMessage(const Ptr& expr, bool isTrueBlockRemained) const; // ======== Transfer functions for normal expressions based on ExprMajorKind ======== // void HandleNormalExpressionEffect(RangeDomain& state, const Expression* expression) override; void HandleUnaryExpr(RangeDomain& state, const UnaryExpression* unaryExpr) const; void HandleBinaryExpr(RangeDomain& state, const BinaryExpression* binaryExpr); void HandleOthersExpr(RangeDomain& state, const Expression* expression); // ======================= Transfer functions for terminators ======================= // std::optional HandleTerminatorEffect(RangeDomain& state, const Terminator* terminator) override; std::optional HandleBranchTerminator(const RangeDomain& state, const Branch* branch) const; std::optional HandleMultiBranchTerminator(const RangeDomain& state, const MultiBranch* multi) const; enum class ExceptionKind : uint8_t { SUCCESS, FAIL, NA }; // =============== Transfer functions for TypeCast expression =============== // SIntDomain ComputeTypeCast(RangeDomain& state, PtrSymbol oldSymbol, const SIntDomain& v, IntWidth dstSize, bool dstUnsigned, OverflowStrategy ov) const; template ExceptionKind HandleTypeCast(RangeDomain& state, const TTypeCast* cast) { auto from = cast->GetSourceTy(); auto to = cast->GetTargetTy(); if (!from->IsInteger() || !to->IsInteger()) { state.SetToTopOrTopRef(cast->GetResult(), cast->GetResult()->GetType()->IsRef()); return ExceptionKind::NA; } auto value = cast->GetSourceValue(); const auto& sourceDomain = GetSIntDomainFromState(state, value); auto res = ComputeTypeCast( state, value, sourceDomain, ToWidth(*to), to->IsUnsignedInteger(), cast->GetOverflowStrategy()); state.Update(cast->GetResult(), std::make_unique(res)); return ExceptionKind::NA; } BoolDomain GenerateBoolRangeFromBinaryOp(RangeDomain& state, const Ptr& binaryExpr) const; DiagAdapter* diag; std::unordered_map inqueueTimes; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/AnnoInfo.h000066400000000000000000000053671510705540100224520ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANNOINFO_H #define CANGJIE_CHIR_ANNOINFO_H #include #include "cangjie/Utils/SafePointer.h" namespace Cangjie::CHIR { // Need refactor: temporary solution of custom defined annotation /** * generate an instance of user defined annotation class, * e.g * * @annotation * public class JsonName { * public const JsonName(public let name: String) {} * } * * class Worker { * Worker( * @JsonName["worker_name"] * public let name: String, * @JsonName["worker_age"] * public let age: Int64 * ){} * } * * here class Worker has two member vars: name and age, and both use user-defined annotation `JsonName`, * so each of two member var has `AnnoInfo` to indicate a compiler-added function that is used to * create instance of `class JsonName`, the `mangledName` in `AnnoInfo` record the mangledName of the * compiler-added function. * * respectively, the two compiler-added funcs are $Anno_CN7default6Worker4nameE and $Anno_CN7default6Worker3ageE. * * the former will create `JsonName` and pass "worker_name" as argument to `JsonName`'s constructor, * similarly, the latter will pass "worker_age" to `JsonName`'s constructor. * * finally, the mangledName is used to generate metadata in CodeGen. * */ struct AnnoInfo { // If it's not cangjie custom annotation, mangledName should be "none". Required for generating metadata. std::string mangledName{"none"}; // mangledName of annotation generated func // `AnnoPair` is a structure used to save the information of the annotation whose parameter values are // literal constants: // - `annoClassName` is the name of that annotation class // - `paramValues` is used to save each parameter value as a string // note: annotation that has no parameters is also included struct AnnoPair { std::string annoClassName; std::vector paramValues; AnnoPair(const std::string& annoClassName, const std::vector& paramValues) : annoClassName(annoClassName), paramValues(paramValues) { } }; // `annoPairs` is used to collect all annotations whose parameter values are literal constants. // Annotations without parameters are also included in the collection. std::vector annoPairs; bool IsAvailable() const { return mangledName != "none"; } }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Annotation.h000066400000000000000000000263411510705540100230500ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANNOTATION_H #define CANGJIE_CHIR_ANNOTATION_H #include "cangjie/Basic/Linkage.h" #include "cangjie/CHIR/DebugLocation.h" #include "cangjie/Utils/ConstantsUtils.h" #include #include #include #include #include #include #include namespace Cangjie::CHIR { class FuncBase; struct Annotation { Annotation() = default; Annotation(const Annotation&) = default; Annotation& operator=(const Annotation&) = default; virtual ~Annotation() = default; virtual std::unique_ptr Clone() = 0; virtual std::string ToString() = 0; }; /** * R: CodeGen * W: ConstAnalysis */ struct NeedCheckArrayBound : public Annotation { public: explicit NeedCheckArrayBound() = default; explicit NeedCheckArrayBound(bool input) : need(input) { } static bool Extract(const NeedCheckArrayBound* input) { return input->need; } std::unique_ptr Clone() override { return std::make_unique(need); } std::string ToString() override { std::string needStr = need ? "true" : "false"; return "checkArrayBound: " + needStr; } private: bool need = true; }; /** * W: AST2CHIR, Transformation/Devirtualization * R: IRChecker */ struct NeedCheckCast : public Annotation { public: explicit NeedCheckCast() = default; explicit NeedCheckCast(bool checked) : need(checked) { } static bool Extract(const NeedCheckCast* needChecked) { return needChecked->need; } std::unique_ptr Clone() override { return std::make_unique(need); } std::string ToString() override { std::string needStr = need ? "true" : "false"; return "checkTypeCast: " + needStr; } private: bool need = true; }; struct DebugLocationInfoForWarning : public Annotation { public: explicit DebugLocationInfoForWarning() = default; explicit DebugLocationInfoForWarning(const DebugLocation& locationInfo) : location(locationInfo) { } static const DebugLocation Extract(const DebugLocationInfoForWarning* input) { return input->location; } std::unique_ptr Clone() override { return std::make_unique(location); } std::string ToString() override { return "warning " + location.ToString(); } private: DebugLocation location; }; struct LinkTypeInfo : public Annotation { public: explicit LinkTypeInfo() : linkType(Cangjie::Linkage::EXTERNAL){}; explicit LinkTypeInfo(const Cangjie::Linkage& linkType) : linkType(linkType){}; static Cangjie::Linkage Extract(const LinkTypeInfo* li) { return li->linkType; } std::unique_ptr Clone() override { return std::make_unique(linkType); } std::string ToString() override { std::map linkToString = {{Cangjie::Linkage::EXTERNAL, "external"}, {Cangjie::Linkage::WEAK_ODR, "weak_odr"}, {Cangjie::Linkage::INTERNAL, "internal"}, {Cangjie::Linkage::LINKONCE_ODR, "linkonce_odr"}}; return "linkType: " + linkToString[linkType]; } private: Cangjie::Linkage linkType; }; struct WrappedRawMethod : public Annotation { public: explicit WrappedRawMethod() = default; explicit WrappedRawMethod(FuncBase* method) : rawMethod(method) { } static FuncBase* Extract(const WrappedRawMethod* input) { return input->rawMethod; } std::unique_ptr Clone() override { return std::make_unique(rawMethod); } std::string ToString() override; private: FuncBase* rawMethod{nullptr}; }; /** * the marks used by some different pass in chir. * * NOTE: currently the types of nodes using these skip kind will not be duplicated, * so we only need to store one skipping kind. */ enum class SkipKind : uint8_t { NO_SKIP, SKIP_DCE_WARNING, SKIP_FORIN_EXIT, SKIP_VIC, }; struct SkipCheck : public Annotation { public: explicit SkipCheck() = default; explicit SkipCheck(SkipKind kind) : kind(kind) { } static SkipKind Extract(const SkipCheck* input) { return input->kind; } std::unique_ptr Clone() override { return std::make_unique(kind); } std::string ToString() override; private: SkipKind kind{SkipKind::NO_SKIP}; }; struct NeverOverflowInfo : public Annotation { public: explicit NeverOverflowInfo() = default; explicit NeverOverflowInfo(bool b) : neverOverflowInfo(b){}; static bool Extract(const NeverOverflowInfo* info) { return info->neverOverflowInfo; } std::unique_ptr Clone() override { return std::make_unique(neverOverflowInfo); } std::string ToString() override { std::string str = neverOverflowInfo ? "true" : "false"; return "NeverOverflowInfo: " + str; } private: bool neverOverflowInfo{false}; }; struct IsAutoEnvClass : public Annotation { public: explicit IsAutoEnvClass() = default; explicit IsAutoEnvClass(bool b) : isAutoEnv(b){}; static bool Extract(const IsAutoEnvClass* info) { return info->isAutoEnv; } std::unique_ptr Clone() override { return std::make_unique(isAutoEnv); } std::string ToString() override { std::string str = isAutoEnv ? "true" : "false"; return "IsAutoEnvClass: " + str; } private: bool isAutoEnv{false}; }; struct IsCapturedClassInCC : public Annotation { public: explicit IsCapturedClassInCC() = default; explicit IsCapturedClassInCC(bool b) : flag(b){}; static bool Extract(const IsCapturedClassInCC* info) { return info->flag; } std::unique_ptr Clone() override { return std::make_unique(flag); } std::string ToString() override { std::string str = flag ? "true" : "false"; return "IsCapturedClassInCC: " + str; } private: bool flag{false}; }; struct EnumCaseIndex : public Annotation { public: explicit EnumCaseIndex() = default; explicit EnumCaseIndex(std::optional b) : index(b){}; static std::optional Extract(const EnumCaseIndex* info) { return info->index; } std::unique_ptr Clone() override { return std::make_unique(index); } std::string ToString() override { if (index.has_value()) { return "EnumCaseIndex: " + std::to_string(index.value()); } return ""; } private: std::optional index{std::nullopt}; }; struct VirMethodOffset : public Annotation { public: explicit VirMethodOffset() = default; explicit VirMethodOffset(std::optional v) : offset(v){}; static std::optional Extract(const VirMethodOffset* info) { return info->offset; } std::unique_ptr Clone() override { return std::make_unique(offset); } std::string ToString() override { if (offset.has_value()) { return "VirMethodOffset: " + std::to_string(offset.value()); } return ""; } private: std::optional offset{std::nullopt}; }; // This class is used to manage CHIR Annotation's. class AnnotationMap final { public: // Set annotation T for this node, updating its value if it already exists template void Set(Args&&... args) { auto index = std::type_index(typeid(T)); annotations[index] = std::make_unique(std::forward(args)...); } // Get the value of the annotation T associated to this node template typename std::invoke_result_t Get() const { auto index = std::type_index(typeid(T)); auto location = annotations.find(index); // Check if we have a recorded annotation. Otherwise we return a default // empty value for this kind of annotation. if (location != annotations.end()) { return T::Extract(static_cast(location->second.get())); } else { auto emptyT = T(); return T::Extract(&emptyT); } } template void Remove() { auto index = std::type_index(typeid(T)); annotations.erase(index); } /// Returns a reference to the annotation. Adds a new one if none exists. This API is used to change the associated /// annotation value. template T& GetAnno() { if (auto location = annotations.find(std::type_index(typeid(T))); location != annotations.end()) { return static_cast(*location->second); } else { auto inserted = annotations.emplace(std::type_index(typeid(T)), std::make_unique()); return static_cast(*inserted.first->second); } } inline const DebugLocation& GetDebugLocation() const { return loc; } inline void SetDebugLocation(const DebugLocation& newLoc) { loc = newLoc; } inline void SetDebugLocation(DebugLocation&& newLoc) { loc = std::move(newLoc); } std::string ToString() const; AnnotationMap() = default; AnnotationMap(const AnnotationMap& other) { for (auto& anno : other.annotations) { annotations[anno.first] = anno.second->Clone(); } loc = other.loc; } AnnotationMap(AnnotationMap&& other) = default; AnnotationMap& operator=(const AnnotationMap& other) { if (this == &other) { return *this; } annotations.clear(); for (auto& anno : other.annotations) { annotations[anno.first] = anno.second->Clone(); } loc = other.loc; return *this; } AnnotationMap& operator=(AnnotationMap&& other) { swap(annotations, other.annotations); loc = other.loc; return *this; } const std::unordered_map>& GetAnnos() const { return annotations; } private: std::unordered_map> annotations; // DebugLocation is a specialised field for better performance, since most expression/value/decl/type has one. DebugLocation loc{}; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/AttributeInfo.h000066400000000000000000000136541510705540100235200ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ATTRIBUTEINFO_H #define CANGJIE_CHIR_ATTRIBUTEINFO_H #include #include namespace Cangjie::CHIR { enum class Attribute { // tokens attribute STATIC, ///< Mark whether a member is a static one. PUBLIC, ///< Mark whether a member is a public one. PRIVATE, ///< Mark whether a member is a private one. PROTECTED, ///< Mark whether a member is a protected one. ABSTRACT, ///< Mark whether a function is an abstract one. VIRTUAL, ///< Mark whether a declaration is in fact open (even if the user does not use `open` keyword). OVERRIDE, ///< Mark whether a declaration in fact overrides the inherited one ///(even if the user does not use `override` keyword). REDEF, ///< Mark whether a declaration in fact overrides the inherited one (even if the user does not use ///< `redef` keyword). SEALED, ///< Mark whether a declaration is a sealed one. FOREIGN, ///< Mark whether a declaration is a foreign one. MUT, ///< Mark whether a declaration is a mutable one. FINAL, /**< Mark a Func override a parent class's func, and this func self does not have VIRTUAL Attribute. */ OPERATOR, ///< Mark whether a declaration is a operator one. READONLY, ///< 'let x = xxx', 'x' enable READONLY attribute CONST, ///< correspond `const` keyword in Cangjie source code. IMPORTED, ///< Mark whether variable、func、enum、struct、class is imported from other package. GENERIC_INSTANTIATED, ///< Mark whether a `GlobalVar/Func/Type` is instantiated. NO_DEBUG_INFO, ///< Mark a `Value` doesn't contain debug info, like line/column number. GENERIC, ///< Mark a declaration is generic INTERNAL, ///< GlobalVar/Func/Enum/Class/Struct/Interface is visible in current and sub package. COMPILER_ADD, ///< Mark a `Value` is added by compiler, like "copied default func from interface". // compiler attribute NO_REFLECT_INFO, ///< Mark a `Value` is't used by `reflect` feature. NO_INLINE, ///< Mark a Func can't be inlined. NON_RECOMPILE, ///< only used in `ImportedValue` in incremental compilation, indicate this ImportedValue is ///< converted from a decl in current package that is not recompiled. UNREACHABLE, ///< Mark a Block is unreachable. NO_SIDE_EFFECT, ///< Mark a Func does't have side effect. COMMON, ///< Mark whether it's common declaration. PLATFORM, ///< Mark whether it's platform declaration. SKIP_ANALYSIS, ///< Mark node that is not used for analysis. ///< e.g. Node can be skiped if it has no body when creating 'common part' DESERIALIZED, ///< Node deserialized from .chir file INITIALIZER, ///< Mark nodes that related to initialization process. ///< Marked functions are package initializer, file initializers, variable initializer or so. ///< On the block is used to search for it among other blocks of the function. UNSAFE, ///< Mark whether a function that was marked as `unsafe` // Native FFI attributes JAVA_MIRROR, ///< Mark whether it's @JavaMirror declaration (binding for a java class). JAVA_IMPL, ///< Mark whether it's @JavaImpl declaration. ATTR_END }; const std::unordered_map ATTR_TO_STRING{{Attribute::STATIC, "static"}, {Attribute::PUBLIC, "public"}, {Attribute::PRIVATE, "private"}, {Attribute::PROTECTED, "protected"}, {Attribute::ABSTRACT, "abstract"}, {Attribute::VIRTUAL, "virtual"}, {Attribute::OVERRIDE, "override"}, {Attribute::REDEF, "redef"}, {Attribute::SEALED, "sealed"}, {Attribute::FOREIGN, "foreign"}, {Attribute::MUT, "mut"}, {Attribute::OPERATOR, "operator"}, {Attribute::CONST, "compileTimeVal"}, {Attribute::READONLY, "readOnly"}, {Attribute::IMPORTED, "imported"}, {Attribute::NON_RECOMPILE, "nonRecompile"}, {Attribute::GENERIC_INSTANTIATED, "generic_instantiated"}, {Attribute::INTERNAL, "internal"}, {Attribute::COMPILER_ADD, "compilerAdd"}, {Attribute::GENERIC, "generic"}, {Attribute::NO_REFLECT_INFO, "noReflectInfo"}, {Attribute::NO_INLINE, "noInline"}, {Attribute::NO_DEBUG_INFO, "noDebugInfo"}, {Attribute::UNREACHABLE, "unreachable"}, {Attribute::NO_SIDE_EFFECT, "noSideEffect"}, {Attribute::FINAL, "final"}, {Attribute::COMMON, "common"}, {Attribute::PLATFORM, "platform"}, {Attribute::SKIP_ANALYSIS, "skip_analysis"}, {Attribute::DESERIALIZED, "deserialized"}, {Attribute::INITIALIZER, "initializer"}, {Attribute::UNSAFE, "unsafe"}, {Attribute::JAVA_MIRROR, "javaMirror"}, {Attribute::JAVA_IMPL, "javaImpl"}}; constexpr uint64_t ATTR_SIZE = 64; class AttributeInfo { public: explicit AttributeInfo() : attributesInfo(0) { } explicit AttributeInfo(const std::bitset& attrs) : attributesInfo(attrs) { } ~AttributeInfo() = default; void SetAttr(Attribute attr, bool enable) { (void)attributesInfo.set(static_cast(attr), enable); } bool TestAttr(Attribute attr) const { return attributesInfo.test(static_cast(attr)); } std::bitset GetRawAttrs() const { return attributesInfo; } void AppendAttrs(const AttributeInfo& info) { attributesInfo |= info.GetRawAttrs(); } void Dump() const; std::string ToString() const; private: std::bitset attributesInfo; // attribute bitset }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Base.h000066400000000000000000000033241510705540100216040ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_BASE_H #define CANGJIE_CHIR_BASE_H #include #include #include "cangjie/CHIR/Annotation.h" namespace Cangjie::CHIR { class Base { public: template void Set(Args&&... args) { anno.Set(std::forward(args)...); } template void Remove() { anno.Remove(); } // Get the value of the annotation T associated to this node template decltype(std::declval().Get()) Get() const { return anno.Get(); } template T& GetAnno() { return anno.GetAnno(); } virtual const DebugLocation& GetDebugLocation() const { return anno.GetDebugLocation(); } inline void SetDebugLocation(const DebugLocation& loc) { anno.SetDebugLocation(loc); } inline void SetDebugLocation(DebugLocation&& loc) { anno.SetDebugLocation(std::move(loc)); } void CopyAnnotationMapFrom(const Base& other) { anno = other.anno; } std::string ToStringAnnotationMap() const { return anno.ToString(); } const AnnotationMap& GetAnno() const { return anno; } AnnotationMap MoveAnnotation() { return std::move(anno); } void SetAnnotation(AnnotationMap&& ot) { anno = std::move(ot); } virtual ~Base() = default; private: AnnotationMap anno; }; } #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/CHIR.h000066400000000000000000000201401510705540100214520ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the entry of CHIR. */ #ifndef CANGJIE_CHIR_CHIR_H #define CANGJIE_CHIR_CHIR_H #include "cangjie/CHIR/AST2CHIR/AST2CHIR.h" #include "cangjie/CHIR/Analysis/ValueRangeAnalysis.h" #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/DiagAdapter.h" namespace Cangjie::CHIR { class ToCHIR { public: ToCHIR(CompilerInstance& ci, AST::Package& pkg, AnalysisWrapper& constAnalysisWrapper, CHIRBuilder& builder) : ci(ci), opts(ci.invocation.globalOptions), typeManager(ci.typeManager), sourceManager(ci.GetSourceManager()), importManager(ci.importManager), gim(ci.gim), diagEngine(ci.diag), cangjieHome(ci.cangjieHome), pkg(pkg), outputPath(ci.invocation.globalOptions.output), kind(ci.kind), cachedInfo(ci.cachedInfo), releaseCHIRMemory(ci.releaseCHIRMemory), needToOptString(ci.needToOptString), needToOptGenericDecl(ci.needToOptGenericDecl), builder(builder), constAnalysisWrapper(constAnalysisWrapper), diag(diagEngine) { } ~ToCHIR() = default; bool Run(); /// Compute Annotation values, and save the results for AOP checkings. /// Only AST2CHIR and necessary CHIR opt's are executed. bool ComputeAnnotations(std::vector&& annoOnly); CHIR::Package* GetPackage() const { return chirPkg; } OptEffectStrMap GetOptEffectMap() const { return strEffectMap; } VirtualWrapperDepMap GetCurVirtualFuncWrapperDepForIncr() { return curVirtFuncWrapDep; } VirtualWrapperDepMap GetDeleteVirtualFuncWrapperForIncr() { return delVirtFuncWrapForIncr; } std::set GetCCOutFuncsRawMangle() { return ccOutFuncsRawMangle; } VarInitDepMap GetVarInitDepMap() const; std::vector> ConstructSubBuilders(size_t threadNum, size_t funcNum) { std::vector>> results; Utils::TaskQueue builderTaskQueue(threadNum); for (size_t i = 0; i < funcNum; i++) { results.emplace_back(builderTaskQueue.AddTask>( [this, i]() { return std::make_unique(builder.GetChirContext(), i); })); } builderTaskQueue.RunAndWaitForAllTasksCompleted(); std::vector> builderList; for (auto& result : results) { auto res = result.get(); builderList.emplace_back(std::move(res)); } return builderList; } std::unordered_map GetImplicitFuncs() const { return implicitFuncs; } std::vector GetConstVarInitFuncs() const { return initFuncsForConstVar; } const std::vector>& GetAnnoFactoryFuncs() const { return annoFactoryFuncs; } const AST2CHIRNodeMap& GetGlobalNominalCache() const { return globalNominalCache; } enum Phase : uint8_t { RAW, // after translation, OPT, // after compiler optimization, PLUGIN, // after perform pulgin ANALYSIS_FOR_CJLINT, // after analysis for cjlint PHASE_MIN = RAW, PHASE_MAX = ANALYSIS_FOR_CJLINT, }; private: /// \param annoOnly pass the decls of which only annoFactoryFuncs are to be translated, during /// computing annotations stage. Empty in normal AST2CHIR translation. bool TranslateToCHIR(std::vector&& annoOnly); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool PerformPlugin(CHIR::Package& package); #endif void DumpCHIRDebug(const std::string& suffix, bool checkFlag = true); void DoClosureConversion(); void ReportUnusedCode(); void Devirtualization(DevirtualizationInfo& devirtInfo); void UnreachableBlockElimination(); void UnreachableBlockReporter(); void NothingTypeExprElimination(); void UselessExprElimination(); void UnreachableBranchReporter(); void UselessFuncElimination(); void RedundantLoadElimination(); void UselessAllocateElimination(); void RunGetRefToArrayElemOpt(); void RedundantGetOrThrowElimination(); void FlatForInExpr(); void RunUnreachableMarkBlockRemoval(); void RunMarkClassHasInited(); void RunMergingBlocks(const std::string& firstName, const std::string& secondName); bool RunVarInitChecking(); bool RunConstantPropagationAndSafetyCheck(); bool RunConstantPropagation(); void RunRangePropagation(); bool RunNativeFFIChecks(); void RunArrayListConstStartOpt(); void RunFunctionInline(DevirtualizationInfo& devirtInfo); void RunArrayLambdaOpt(); void RunRedundantFutureOpt(); void RunNoSideEffectMarkerOpt(); void RunSanitizerCoverage(); bool RunOptimizationPassAndRulesChecking(); void MarkNoSideEffect(); void RunUnitUnify(); DevirtualizationInfo CollectDevirtualizationInfo(); bool RunConstantEvaluation(); bool RunIRChecker(const Phase& phase); void UpdatePosOfMacroExpandNode(); void RecordCodeInfoAtTheBegin(); void RecordCodeInfoAtTheEnd(); void RecordCHIRExprNum(const std::string& suffix); bool RunAnalysisForCJLint(); void RunConstantAnalysis(); // run semantic checks that have to be performed on CHIR bool RunAnnotationChecks(); void EraseDebugExpr(); void CFFIFuncWrapper(); void RemoveUnusedImports(bool removeSrcCodeImported); void ReplaceSrcCodeImportedValueWithSymbol(); void CreateBoxTypeForRecursionValueType(); void CreateVTableAndUpdateFuncCall(); void UpdateMemberVarPath(); template std::pair DoCFFIFuncWrapper(T& curFunc, bool isForeign, bool isExternal = true); template bool IsAllApply(const T* curFunc); CompilerInstance& ci; const GlobalOptions& opts; TypeManager* typeManager; SourceManager& sourceManager; ImportManager& importManager; const GenericInstantiationManager* gim; DiagnosticEngine& diagEngine; const std::string& cangjieHome; AST::Package& pkg; std::string outputPath; IncreKind kind; CompilationCache& cachedInfo; uint64_t ccEnvCounter = 0; CHIR::Package* chirPkg{nullptr}; bool releaseCHIRMemory = true; // This flag is served for const propagation. The cangjie kernel const propagation doesn't need to optimize // string, but the cjlint need to do it. This flag is for differentiating this behavior. bool needToOptString = false; bool needToOptGenericDecl = false; CHIRBuilder& builder; uint64_t debugFileIndex{0}; AnalysisWrapper& constAnalysisWrapper; OptEffectCHIRMap effectMap; OptEffectStrMap strEffectMap; VirtualWrapperDepMap curVirtFuncWrapDep; VirtualWrapperDepMap delVirtFuncWrapForIncr; // Raw mangled name of top or mem funcs had closure convert. If there is // any change in incremental compilation, rollback is required. std::set ccOutFuncsRawMangle; class DiagAdapter diag; std::unordered_set srcCodeImportedFuncs; std::unordered_set srcCodeImportedVars; std::unordered_set uselessClasses; std::unordered_set uselessLambda; std::unordered_map implicitFuncs; std::vector initFuncsForConstVar; std::unordered_map maybeUnreachable; /// Whether this CHIR convertor is translating Annotations bool isComputingAnnos{false}; std::vector> annoFactoryFuncs; AST2CHIRNodeMap globalNominalCache; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/CHIRBuilder.h000066400000000000000000000344751510705540100230010ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the CHIRBuilder class in CHIR. */ #ifndef CANGJIE_CHIR_CHIRBUILDER_H #define CANGJIE_CHIR_CHIRBUILDER_H #include "cangjie/CHIR/CHIRContext.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" #include "cangjie/CHIR/ConstantUtils.h" namespace Cangjie::CHIR { class CHIRBuilder { friend class CHIRContext; CHIRBuilder& operator=(const CHIRBuilder&) = delete; CHIRBuilder() = delete; public: explicit CHIRBuilder(CHIRContext& context, size_t threadIdx = 0); CHIRBuilder(const CHIRBuilder& builder) : context(builder.context), markAsCompileTimeValue(builder.markAsCompileTimeValue), threadIdx(builder.threadIdx) { } ~CHIRBuilder(); // ===--------------------------------------------------------------------===// // Type API // ===--------------------------------------------------------------------===// template TType* GetType(Args&&... args) { return context.GetType(args...); } StructType* GetStructType( const std::string& package, const std::string& name, const std::vector& genericType = {}) const { return context.GetStructType(package, name, genericType); } StructType* GetStringTy() { return context.GetStringTy(); } VArrayType* GetZeroSizedTy() { return GetType(GetInt8Ty(), 0U); } NothingType* GetNothingType() const { return context.GetNothingType(); } UnitType* GetUnitTy() const { return context.GetUnitTy(); } BooleanType* GetBoolTy() const { return context.GetBoolTy(); } RuneType* GetRuneTy() const { return context.GetRuneTy(); } IntType* GetInt8Ty() const { return context.GetInt8Ty(); } IntType* GetInt16Ty() const { return context.GetInt16Ty(); } IntType* GetInt32Ty() const { return context.GetInt32Ty(); } IntType* GetInt64Ty() const { return context.GetInt64Ty(); } IntType* GetIntNativeTy() const { return context.GetIntNativeTy(); } IntType* GetUInt8Ty() const { return context.GetUInt8Ty(); } IntType* GetUInt16Ty() const { return context.GetUInt16Ty(); } IntType* GetUInt32Ty() const { return context.GetUInt32Ty(); } IntType* GetUInt64Ty() const { return context.GetUInt64Ty(); } IntType* GetUIntNativeTy() const { return context.GetUIntNativeTy(); } FloatType* GetFloat16Ty() const { return context.GetFloat16Ty(); } FloatType* GetFloat32Ty() const { return context.GetFloat32Ty(); } FloatType* GetFloat64Ty() const { return context.GetFloat64Ty(); } CStringType* GetCStringTy() const { return context.GetCStringTy(); } VoidType* GetVoidTy() const { return context.GetVoidTy(); } // Need refactor: object may be a new type and not inherited from Class void SetObjectTy(ClassType* ty) { context.SetObjectTy(ty); } ClassType* GetObjectTy() const { return context.GetObjectTy(); } void SetAnyTy(ClassType* ty) { context.SetAnyTy(ty); } ClassType* GetAnyTy() const { return context.GetAnyTy(); } // ===--------------------------------------------------------------------===// // BlockGroup API // ===--------------------------------------------------------------------===// BlockGroup* CreateBlockGroup(Func& func); // ===--------------------------------------------------------------------===// // Basic Block API // ===--------------------------------------------------------------------===// Block* CreateBlock(BlockGroup* parentGroup); std::pair SplitBlock(const Expression& separator); // ===--------------------------------------------------------------------===// // Value API // ===--------------------------------------------------------------------===// // Note: we should be able to automatically infer the `TArgVal` here template TLitVal* CreateLiteralValue(Args&&... args) { TLitVal* litVal = new TLitVal(std::forward(args)...); this->allocatedValues.push_back(litVal); return litVal; } Parameter* CreateParameter(Type* ty, const DebugLocation& loc, Func& parentFunc); Parameter* CreateParameter(Type* ty, const DebugLocation& loc, Lambda& parentLambda); GlobalVar* CreateGlobalVar(const DebugLocation& loc, RefType* ty, const std::string& mangledName, const std::string& srcCodeIdentifier, const std::string& rawMangledName, const std::string& packageName); // ===--------------------------------------------------------------------===// // Expression API // ===--------------------------------------------------------------------===// /** @brief Return a Expression.*/ template TExpr* CreateExpression(Type* resultTy, Args&&... args) { TExpr* expr = new TExpr(std::forward(args)...); this->allocatedExprs.push_back(expr); CJC_NULLPTR_CHECK(expr->GetTopLevelFunc()); std::string idStr = "%" + std::to_string(expr->GetTopLevelFunc()->GenerateLocalId()); LocalVar* res = new LocalVar(resultTy, idStr, expr); this->allocatedValues.push_back(res); return expr; } template TExpr* CreateExpression(const DebugLocation& loc, Type* resultTy, Args&&... args) { auto expr = CreateExpression(resultTy, args...); expr->SetDebugLocation(loc); return expr; } template TExpr* CreateExpression( const DebugLocation& locForWarning, const DebugLocation& loc, Type* resultTy, Args&&... args) { auto expr = CreateExpression(resultTy, args...); expr->SetDebugLocation(loc); expr->template Set(locForWarning); return expr; } /** @brief Return a Terminator.*/ template TExpr* CreateTerminator(Args&&... args) { static_assert(std::is_base_of_v); TExpr* expr = new TExpr(std::forward(args)...); this->allocatedExprs.push_back(expr); return expr; } template TExpr* CreateTerminator(const DebugLocation& loc, Args&&... args) { auto expr = CreateTerminator(std::forward(args)...); expr->SetDebugLocation(loc); return expr; } template TExpr* CreateTerminator(DebugLocation& loc, Args&&... args) { return CreateTerminator(std::as_const(loc), std::forward(args)...); } template TExpr* CreateTerminator(DebugLocation&& loc, Args&&... args) { auto expr = CreateTerminator(std::forward(args)...); expr->SetDebugLocation(std::move(loc)); return expr; } template Constant* CreateConstantExpression(Type* resultTy, Block* parentBlock, Args&&... args) { TLitVal* litVal = CreateLiteralValue(resultTy, std::forward(args)...); Constant* expr = new Constant(litVal, parentBlock); this->allocatedExprs.push_back(expr); CJC_NULLPTR_CHECK(parentBlock->GetTopLevelFunc()); std::string idStr = "%" + std::to_string(parentBlock->GetTopLevelFunc()->GenerateLocalId()); LocalVar* res = new LocalVar(resultTy, idStr, expr); this->allocatedValues.push_back(res); return expr; } template Constant* CreateConstantExpression(const DebugLocation& loc, Type* resultTy, Block* parentBlock, Args&&... args) { auto expr = CreateConstantExpression(resultTy, parentBlock, args...); expr->SetDebugLocation(loc); return expr; } Func* CreateFunc(const DebugLocation& loc, FuncType* funcTy, const std::string& mangledName, const std::string& srcCodeIdentifier, const std::string& rawMangledName, const std::string& packageName, const std::vector& genericTypeParams = {}); // ===--------------------------------------------------------------------===// // StructDef API // ===--------------------------------------------------------------------===// StructDef* CreateStruct(const DebugLocation& loc, const std::string& srcCodeIdentifier, const std::string& mangledName, const std::string& pkgName, bool isImported); // ===--------------------------------------------------------------------===// // ClassDef API // ===--------------------------------------------------------------------===// ClassDef* CreateClass(const DebugLocation& loc, const std::string& srcCodeIdentifier, const std::string& mangledName, const std::string& pkgName, bool isClass, bool isImported); // ===--------------------------------------------------------------------===// // EnumDef API // ===--------------------------------------------------------------------===// EnumDef* CreateEnum(const DebugLocation& loc, const std::string& srcCodeIdentifier, const std::string& mangledName, const std::string& pkgName, bool isImported, bool isNonExhaustive); // ===--------------------------------------------------------------------===// // ExtendDef API // ===--------------------------------------------------------------------===// ExtendDef* CreateExtend(const DebugLocation& loc, const std::string& mangledName, const std::string& pkgName, bool isImported, const std::vector genericParams = {}); // ===--------------------------------------------------------------------===// // Package API // ===--------------------------------------------------------------------===// Package* CreatePackage(const std::string& name); Package* GetCurPackage() const; template T* CreateImportedVarOrFunc(Type* ty, const std::string& mangledName, const std::string& srcCodeIdentifier, const std::string& rawMangledName, const std::string& srcPackageName, const std::vector& genericTypeParams = {}, bool addToIR = true) { T* importDecl = nullptr; if constexpr (std::is_same_v) { importDecl = new ImportedFunc(ty, GLOBAL_VALUE_PREFIX + mangledName, srcCodeIdentifier, rawMangledName, srcPackageName, genericTypeParams); } else { importDecl = new ImportedVar(ty, GLOBAL_VALUE_PREFIX + mangledName, srcCodeIdentifier, rawMangledName, srcPackageName); } CJC_NULLPTR_CHECK(importDecl); importDecl->EnableAttr(Attribute::IMPORTED); this->allocatedValues.push_back(importDecl); if (context.GetCurPackage() != nullptr && addToIR) { context.GetCurPackage()->AddImportedVarAndFunc(importDecl); } return importDecl; } void SetCompileTimeValueMark(bool val) { markAsCompileTimeValue = val; } bool GetCompileTimeValueMark() const { return markAsCompileTimeValue; } CHIRContext& GetChirContext() { return context; } size_t GetAllNodesNum() const { return context.GetAllNodesNum(); } size_t GetTypesNum() const { return context.dynamicAllocatedTys.size(); } void MergeAllocatedInstance() { context.GetAllocatedExprs().insert( context.GetAllocatedExprs().end(), allocatedExprs.begin(), allocatedExprs.end()); context.GetAllocatedValues().insert( context.GetAllocatedValues().end(), allocatedValues.begin(), allocatedValues.end()); context.GetAllocatedBlockGroups().insert(context.GetAllocatedBlockGroups().end(), allocatedBlockGroups.begin(), allocatedBlockGroups.end()); context.GetAllocatedBlocks().insert( context.GetAllocatedBlocks().end(), allocatedBlocks.begin(), allocatedBlocks.end()); context.GetAllocatedStructs().insert( context.GetAllocatedStructs().end(), allocatedStructs.begin(), allocatedStructs.end()); context.GetAllocatedClasses().insert( context.GetAllocatedClasses().end(), allocatedClasses.begin(), allocatedClasses.end()); context.GetAllocatedEnums().insert( context.GetAllocatedEnums().end(), allocatedEnums.begin(), allocatedEnums.end()); context.GetAllocatedExtends().insert( context.GetAllocatedExtends().end(), allocatedExtends.begin(), allocatedExtends.end()); allocatedExprs.clear(); allocatedValues.clear(); allocatedBlockGroups.clear(); allocatedBlocks.clear(); allocatedStructs.clear(); allocatedClasses.clear(); allocatedEnums.clear(); allocatedExtends.clear(); } std::unordered_set GetAllCustomTypes() const; std::unordered_set GetAllGenericTypes() const; void EnableIRCheckerAfterPlugin(); void DisableIRCheckerAfterPlugin(); bool IsEnableIRCheckerAfterPlugin() const; private: CHIRContext& context; // A flag indicate if the created CHIR value/expression should be marked as compile time value for const evaluation bool markAsCompileTimeValue = false; bool enableIRCheckerAfterPlugin = true; size_t threadIdx; std::vector allocatedExprs; std::vector allocatedValues; std::vector allocatedBlockGroups; std::vector allocatedBlocks; std::vector allocatedStructs; std::vector allocatedClasses; std::vector allocatedEnums; std::vector allocatedExtends; }; } // namespace Cangjie::CHIR #endif // CANGJIE_CHIR_CHIRBUILDER_H cangjie_compiler-1.0.7/include/cangjie/CHIR/CHIRCasting.h000066400000000000000000000350551510705540100227760ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements type casting for CHIR related nodes. */ #ifndef CANGJIE_UTILS_CHIR_CASTING_H #define CANGJIE_UTILS_CHIR_CASTING_H #include #include "cangjie/CHIR/Value.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/CustomTypeDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/ExtendDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/Utils/CastingTemplate.h" namespace Cangjie { // Type.h DEFINE_NODE_TYPE_KIND(CHIR::RuneType, CHIR::Type::TypeKind::TYPE_RUNE); DEFINE_NODE_TYPE_KIND(CHIR::BooleanType, CHIR::Type::TypeKind::TYPE_BOOLEAN); DEFINE_NODE_TYPE_KIND(CHIR::UnitType, CHIR::Type::TypeKind::TYPE_UNIT); DEFINE_NODE_TYPE_KIND(CHIR::NothingType, CHIR::Type::TypeKind::TYPE_NOTHING); DEFINE_NODE_TYPE_KIND(CHIR::VoidType, CHIR::Type::TypeKind::TYPE_VOID); DEFINE_NODE_TYPE_KIND(CHIR::TupleType, CHIR::Type::TypeKind::TYPE_TUPLE); DEFINE_NODE_TYPE_KIND(CHIR::StructType, CHIR::Type::TypeKind::TYPE_STRUCT); DEFINE_NODE_TYPE_KIND(CHIR::EnumType, CHIR::Type::TypeKind::TYPE_ENUM); DEFINE_NODE_TYPE_KIND(CHIR::FuncType, CHIR::Type::TypeKind::TYPE_FUNC); DEFINE_NODE_TYPE_KIND(CHIR::ClassType, CHIR::Type::TypeKind::TYPE_CLASS); DEFINE_NODE_TYPE_KIND(CHIR::RawArrayType, CHIR::Type::TypeKind::TYPE_RAWARRAY); DEFINE_NODE_TYPE_KIND(CHIR::VArrayType, CHIR::Type::TypeKind::TYPE_VARRAY); DEFINE_NODE_TYPE_KIND(CHIR::CPointerType, CHIR::Type::TypeKind::TYPE_CPOINTER); DEFINE_NODE_TYPE_KIND(CHIR::CStringType, CHIR::Type::TypeKind::TYPE_CSTRING); DEFINE_NODE_TYPE_KIND(CHIR::GenericType, CHIR::Type::TypeKind::TYPE_GENERIC); DEFINE_NODE_TYPE_KIND(CHIR::RefType, CHIR::Type::TypeKind::TYPE_REFTYPE); DEFINE_NODE_TYPE_KIND(CHIR::BoxType, CHIR::Type::TypeKind::TYPE_BOXTYPE); DEFINE_NODE_TYPE_KIND(CHIR::ThisType, CHIR::Type::TypeKind::TYPE_THIS); // Expression.h DEFINE_NODE_TYPE_KIND(CHIR::Constant, CHIR::ExprKind::CONSTANT); DEFINE_NODE_TYPE_KIND(CHIR::Allocate, CHIR::ExprKind::ALLOCATE); DEFINE_NODE_TYPE_KIND(CHIR::Load, CHIR::ExprKind::LOAD); DEFINE_NODE_TYPE_KIND(CHIR::Store, CHIR::ExprKind::STORE); DEFINE_NODE_TYPE_KIND(CHIR::GetElementRef, CHIR::ExprKind::GET_ELEMENT_REF); DEFINE_NODE_TYPE_KIND(CHIR::GetElementByName, CHIR::ExprKind::GET_ELEMENT_BY_NAME); DEFINE_NODE_TYPE_KIND(CHIR::StoreElementRef, CHIR::ExprKind::STORE_ELEMENT_REF); DEFINE_NODE_TYPE_KIND(CHIR::StoreElementByName, CHIR::ExprKind::STORE_ELEMENT_BY_NAME); DEFINE_NODE_TYPE_KIND(CHIR::Apply, CHIR::ExprKind::APPLY); DEFINE_NODE_TYPE_KIND(CHIR::Invoke, CHIR::ExprKind::INVOKE); DEFINE_NODE_TYPE_KIND(CHIR::InvokeStatic, CHIR::ExprKind::INVOKESTATIC); DEFINE_NODE_TYPE_KIND(CHIR::TypeCast, CHIR::ExprKind::TYPECAST); DEFINE_NODE_TYPE_KIND(CHIR::InstanceOf, CHIR::ExprKind::INSTANCEOF); DEFINE_NODE_TYPE_KIND(CHIR::GoTo, CHIR::ExprKind::GOTO); DEFINE_NODE_TYPE_KIND(CHIR::Branch, CHIR::ExprKind::BRANCH); DEFINE_NODE_TYPE_KIND(CHIR::MultiBranch, CHIR::ExprKind::MULTIBRANCH); DEFINE_NODE_TYPE_KIND(CHIR::Exit, CHIR::ExprKind::EXIT); DEFINE_NODE_TYPE_KIND(CHIR::RaiseException, CHIR::ExprKind::RAISE_EXCEPTION); DEFINE_NODE_TYPE_KIND(CHIR::ApplyWithException, CHIR::ExprKind::APPLY_WITH_EXCEPTION); DEFINE_NODE_TYPE_KIND(CHIR::InvokeWithException, CHIR::ExprKind::INVOKE_WITH_EXCEPTION); DEFINE_NODE_TYPE_KIND(CHIR::InvokeStaticWithException, CHIR::ExprKind::INVOKESTATIC_WITH_EXCEPTION); DEFINE_NODE_TYPE_KIND(CHIR::IntOpWithException, CHIR::ExprKind::INT_OP_WITH_EXCEPTION); DEFINE_NODE_TYPE_KIND(CHIR::TypeCastWithException, CHIR::ExprKind::TYPECAST_WITH_EXCEPTION); DEFINE_NODE_TYPE_KIND(CHIR::IntrinsicWithException, CHIR::ExprKind::INTRINSIC_WITH_EXCEPTION); DEFINE_NODE_TYPE_KIND(CHIR::AllocateWithException, CHIR::ExprKind::ALLOCATE_WITH_EXCEPTION); DEFINE_NODE_TYPE_KIND(CHIR::RawArrayAllocateWithException, CHIR::ExprKind::RAW_ARRAY_ALLOCATE_WITH_EXCEPTION); DEFINE_NODE_TYPE_KIND(CHIR::SpawnWithException, CHIR::ExprKind::SPAWN_WITH_EXCEPTION); DEFINE_NODE_TYPE_KIND(CHIR::Tuple, CHIR::ExprKind::TUPLE); DEFINE_NODE_TYPE_KIND(CHIR::Field, CHIR::ExprKind::FIELD); DEFINE_NODE_TYPE_KIND(CHIR::FieldByName, CHIR::ExprKind::FIELD_BY_NAME); DEFINE_NODE_TYPE_KIND(CHIR::RawArrayAllocate, CHIR::ExprKind::RAW_ARRAY_ALLOCATE); DEFINE_NODE_TYPE_KIND(CHIR::RawArrayLiteralInit, CHIR::ExprKind::RAW_ARRAY_LITERAL_INIT); DEFINE_NODE_TYPE_KIND(CHIR::RawArrayInitByValue, CHIR::ExprKind::RAW_ARRAY_INIT_BY_VALUE); DEFINE_NODE_TYPE_KIND(CHIR::VArray, CHIR::ExprKind::VARRAY); DEFINE_NODE_TYPE_KIND(CHIR::VArrayBuilder, CHIR::ExprKind::VARRAY_BUILDER); DEFINE_NODE_TYPE_KIND(CHIR::GetException, CHIR::ExprKind::GET_EXCEPTION); DEFINE_NODE_TYPE_KIND(CHIR::Intrinsic, CHIR::ExprKind::INTRINSIC); DEFINE_NODE_TYPE_KIND(CHIR::If, CHIR::ExprKind::IF); DEFINE_NODE_TYPE_KIND(CHIR::Loop, CHIR::ExprKind::LOOP); DEFINE_NODE_TYPE_KIND(CHIR::ForInRange, CHIR::ExprKind::FORIN_RANGE); DEFINE_NODE_TYPE_KIND(CHIR::ForInIter, CHIR::ExprKind::FORIN_ITER); DEFINE_NODE_TYPE_KIND(CHIR::ForInClosedRange, CHIR::ExprKind::FORIN_CLOSED_RANGE); DEFINE_NODE_TYPE_KIND(CHIR::Lambda, CHIR::ExprKind::LAMBDA); DEFINE_NODE_TYPE_KIND(CHIR::Debug, CHIR::ExprKind::DEBUGEXPR); DEFINE_NODE_TYPE_KIND(CHIR::Spawn, CHIR::ExprKind::SPAWN); DEFINE_NODE_TYPE_KIND(CHIR::GetInstantiateValue, CHIR::ExprKind::GET_INSTANTIATE_VALUE); DEFINE_NODE_TYPE_KIND(CHIR::Box, CHIR::ExprKind::BOX); DEFINE_NODE_TYPE_KIND(CHIR::UnBox, CHIR::ExprKind::UNBOX); DEFINE_NODE_TYPE_KIND(CHIR::GetRTTI, CHIR::ExprKind::GET_RTTI); DEFINE_NODE_TYPE_KIND(CHIR::GetRTTIStatic, CHIR::ExprKind::GET_RTTI_STATIC); // CustomTypeDef.h, 包含class enum struct三种 DEFINE_NODE_TYPE_KIND(CHIR::ClassDef, CHIR::CustomDefKind::TYPE_CLASS); DEFINE_NODE_TYPE_KIND(CHIR::EnumDef, CHIR::CustomDefKind::TYPE_ENUM); DEFINE_NODE_TYPE_KIND(CHIR::StructDef, CHIR::CustomDefKind::TYPE_STRUCT); DEFINE_NODE_TYPE_KIND(CHIR::ExtendDef, CHIR::CustomDefKind::TYPE_EXTEND); // Defined the mono type checking method for CHIRNode. template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsFunc(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsBlock(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsBlockGroup(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsFuncWithBody(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsGlobalVarInCurPackage(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsImportedFunc(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsImportedVar(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsLiteral(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsLiteral() && static_cast(value).IsBoolLiteral(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsLiteral() && static_cast(value).IsRuneLiteral(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsLiteral() && static_cast(value).IsStringLiteral(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsLiteral() && static_cast(value).IsIntLiteral(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsLiteral() && static_cast(value).IsFloatLiteral(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsLiteral() && static_cast(value).IsUnitLiteral(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsLiteral() && static_cast(value).IsNullLiteral(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsLocalVar(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsParameter(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsGlobalVar(); } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Value& value) { return value.IsImportedSymbol(); } }; template struct TypeAs>> { static inline bool IsInstanceOf(const CHIR::Type& node) { return node.GetTypeKind() == NodeType::kind; } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Type& node) { return node.IsBuiltinType(); } }; template <> struct TypeAs { static inline bool IsInstanceOf(const CHIR::Type& node) { return node.GetTypeKind() == CHIR::Type::TypeKind::TYPE_CLASS || node.GetTypeKind() == CHIR::Type::TypeKind::TYPE_STRUCT || node.GetTypeKind() == CHIR::Type::TypeKind::TYPE_ENUM; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const CHIR::Type& node) { return node.GetTypeKind() >= CHIR::Type::TypeKind::TYPE_INT8 && node.GetTypeKind() <= CHIR::Type::TypeKind::TYPE_FLOAT64; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const CHIR::Type& node) { return node.GetTypeKind() >= CHIR::Type::TypeKind::TYPE_FLOAT16 && node.GetTypeKind() <= CHIR::Type::TypeKind::TYPE_FLOAT64; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const CHIR::Type& node) { return node.GetTypeKind() >= CHIR::Type::TypeKind::TYPE_INT8 && node.GetTypeKind() <= CHIR::Type::TypeKind::TYPE_UINT_NATIVE; } }; template using ExprImplSeparately = std::enable_if_t && ShouldInstantiate::value>; template struct TypeAs> { static inline bool IsInstanceOf(const CHIR::Expression& node) { return node.GetExprKind() == NodeType::kind; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const CHIR::Expression& node) { return node.GetExprKind() == CHIR::ExprKind::APPLY || node.GetExprKind() == CHIR::ExprKind::INVOKE || node.GetExprKind() == CHIR::ExprKind::INVOKESTATIC; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const CHIR::Expression& node) { return node.GetExprKind() == CHIR::ExprKind::INVOKE || node.GetExprKind() == CHIR::ExprKind::INVOKESTATIC; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const CHIR::Expression& node) { return node.GetExprKind() == CHIR::ExprKind::APPLY_WITH_EXCEPTION || node.GetExprKind() == CHIR::ExprKind::INVOKE_WITH_EXCEPTION || node.GetExprKind() == CHIR::ExprKind::INVOKESTATIC_WITH_EXCEPTION; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const CHIR::Expression& node) { return node.GetExprKind() == CHIR::ExprKind::INVOKE_WITH_EXCEPTION || node.GetExprKind() == CHIR::ExprKind::INVOKESTATIC_WITH_EXCEPTION; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const CHIR::Expression& node) { return node.GetExprKind() == CHIR::ExprKind::APPLY_WITH_EXCEPTION || node.GetExprKind() == CHIR::ExprKind::INVOKE_WITH_EXCEPTION || node.GetExprKind() == CHIR::ExprKind::INVOKESTATIC_WITH_EXCEPTION || node.GetExprKind() == CHIR::ExprKind::INT_OP_WITH_EXCEPTION || node.GetExprKind() == CHIR::ExprKind::TYPECAST_WITH_EXCEPTION || node.GetExprKind() == CHIR::ExprKind::INTRINSIC_WITH_EXCEPTION || node.GetExprKind() == CHIR::ExprKind::ALLOCATE_WITH_EXCEPTION || node.GetExprKind() == CHIR::ExprKind::RAW_ARRAY_ALLOCATE_WITH_EXCEPTION || node.GetExprKind() == CHIR::ExprKind::SPAWN_WITH_EXCEPTION; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const CHIR::Expression& node) { auto kind = node.GetExprKind(); return kind >= CHIR::ExprKind::NEG && kind <= CHIR::ExprKind::BITNOT; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const CHIR::Expression& node) { auto kind = node.GetExprKind(); return kind >= CHIR::ExprKind::ADD && kind <= CHIR::ExprKind::OR; } }; template <> struct TypeAs { static inline bool IsInstanceOf(const CHIR::Expression& node) { auto kind = node.GetExprKind(); return kind <= CHIR::ExprKind::RAW_ARRAY_ALLOCATE_WITH_EXCEPTION && kind >= CHIR::ExprKind::GOTO; } }; template <> struct TypeAs { static bool IsInstanceOf(const CHIR::Expression& node) { auto kind = node.GetExprKind(); return kind >= CHIR::ExprKind::FORIN_RANGE && kind <= CHIR::ExprKind::FORIN_CLOSED_RANGE; } }; template struct TypeAs>> { static inline bool IsInstanceOf(const CHIR::CustomTypeDef& node) { return node.GetCustomKind() == NodeType::kind; } }; } #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/CHIRChecker.h000066400000000000000000000424261510705540100227520ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_CHIR_CHECKER_H #define CANGJIE_CHIR_CHIR_CHECKER_H #include #include "cangjie/CHIR/CHIR.h" #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/Expression/ExpressionWrapper.h" #include "cangjie/CHIR/Package.h" #include "cangjie/Option/Option.h" namespace Cangjie::CHIR { class CHIRChecker { public: enum class Rule { EMPTY_BLOCK, // there must be expressions in block, one block can't be empty CHECK_FUNC_BODY, // check all expressions in func body, including their types GET_INSTANTIATE_VALUE_SHOULD_GONE, // `GetInstantiateValue` shouldn't be in IR CHIR_GET_RTTI_STATIC_TYPE // type in GetRTTIStatic should be This or Generic type }; CHIRChecker(const Package& package, const Cangjie::GlobalOptions& opts, CHIRBuilder& builder); bool CheckPackage(const std::unordered_set& r); private: struct VirMethodFullContext { std::string srcCodeIdentifier; FuncType* originalFuncType{nullptr}; std::vector genericTypeParams; size_t offset{0}; Type* thisType{nullptr}; ClassType* srcParentType{nullptr}; }; template void ParallelCheck(F check, const std::vector& items) { size_t threadNum = opts.GetJobs(); Cangjie::Utils::TaskQueue taskQueue(threadNum); // Place for holding the results. std::vector> results; for (auto item : items) { results.emplace_back( taskQueue.AddTask([check, this, item]() { (this->*check)(*item); })); } taskQueue.RunAndWaitForAllTasksCompleted(); } /** * @brief `srcType` can be set to `dstType` * 1. store `srcType` to `dstType` * 2. func call from arg type(`srcType`) to param type(`dstType`) * 3. get element ref from declared type(`srcType`) to user point type(`dstType`) * 4. store element ref from user point type(`srcType`) to declared type(`dstType`) */ bool TypeIsExpected(const Type& srcType, const Type& dstType); bool InstTypeArgsSatisfyGenericConstraints( const std::vector& instTypeArgs, const std::vector& genericTypeArgs); bool InstTypeCanSetToGenericRelatedType(Type& instType, const Type& genericRelatedType); // ===--------------------------------------------------------------------===// // API for report // ===--------------------------------------------------------------------===// void Warningln(const std::string& info); void Errorln(const std::string& info); void WarningInFunc(const Value& func, const std::string& info); void WarningInExpr(const Value& func, const Expression& expr, const std::string& info); void ErrorInFunc(const Value& func, const std::string& info); void ErrorInLambdaOrFunc(const FuncBase& func, const Lambda* lambda, const std::string& info); void ErrorInExpr(const Value& func, const Expression& expr, const std::string& info); void TypeCheckError( const Expression& expr, const Value& value, const std::string& expectedType, const Func& topLevelFunc); void ErrorInGlobalVar(const GlobalVarBase& var, const std::string& info); // ===--------------------------------------------------------------------===// // Check Values // ===--------------------------------------------------------------------===// /** * @brief global identifier must be unique */ void CheckGlobalIdentifier(const Value& value); void CheckTopLevelFunc( const Func* calculatedFunc, const Func& realFunc, const std::string& valueName, const std::string& valueId); void CheckFunc(const Func& func); void CheckGlobalVar(const GlobalVarBase& var); void CheckImportedVarAndFuncs(const ImportedValue& value); void CheckGlobalVarType(const GlobalVarBase& var); /** * @brief type in `params` must equal to type in `funcType` */ void CheckFuncParams( const std::vector& params, const FuncType& funcType, const std::string& funcIdentifier); bool CheckFuncType(const Type* type, const Lambda* lambda, const FuncBase& topLevelFunc); bool CheckParamTypes(const std::vector& paramTypes, const Lambda* lambda, const FuncBase& topLevelFunc); bool CheckCFuncType(const FuncType& funcType, const Lambda* lambda, const FuncBase& topLevelFunc); void CheckFuncRetValue( const LocalVar* retVal, const Type& retType, const Lambda* lambda, const Func& topLevelFunc); void CheckBlockGroup(const BlockGroup& blockGroup, const Func& topLevelFunc); void CheckBlock(const Block& block, const Func& topLevelFunc); /** * @brief the successor of current block's predecessor must be current block */ void CheckPredecessors(const Block& block, const Func& topLevelFunc); void CheckLocalId(BlockGroup& blockGroup, const Func& topLevelFunc); void CheckUnreachableOpAndGenericTyInFuncBody(const BlockGroup& blockGroup); void CheckUnreachableOpAndGenericTyInBG(const BlockGroup& blockGroup, std::vector& reachableValues, std::vector& reachableGenericTypes); void CheckUnreachableOpAndGenericTyInBlock(const Block& block, std::vector& reachableValues, std::vector& reachableGenericTypes, std::unordered_set& visitedBlocks); void CheckUnreachableOpAndGenericTyInExpr(const Expression& expr, std::vector& reachableValues, std::vector& reachableGenericTypes); void CheckUnreachableOperandInExpr(const Expression& expr, std::vector& reachableValues); void CheckUnreachableGenericTypeInExpr(const Expression& expr, std::vector& reachableGenericTypes); bool CheckFuncBase(const FuncBase& func); bool CheckOriginalLambdaInfo(const FuncBase& func); bool CheckLiftedLambdaType(const FuncBase& func); // ===--------------------------------------------------------------------===// // Check CustomTypeDef // ===--------------------------------------------------------------------===// void CheckStructDef(const StructDef& def); bool CheckParentCustomTypeDef(const FuncBase& func, const CustomTypeDef& def); // ===--------------------------------------------------------------------===// // Check Terminator // ===--------------------------------------------------------------------===// void CheckTerminator(const Expression& expr, const Func& topLevelFunc); void CheckGoTo(const GoTo& expr, const Func& topLevelFunc); void CheckExit(const Exit& expr, const Func& topLevelFunc); void CheckRaiseException(const RaiseException& expr, const Func& topLevelFunc); void CheckBranch(const Branch& expr, const Func& topLevelFunc); void CheckMultiBranch(const MultiBranch& expr, const Func& topLevelFunc); void CheckApplyWithException(const ApplyWithException& expr, const Func& topLevelFunc); void CheckApplyBase(const ApplyBase& expr, const Func& topLevelFunc); void CheckInvokeWithException(const InvokeWithException& expr, const Func& topLevelFunc); void CheckInvokeBase(const InvokeBase& expr, const Func& topLevelFunc); void CheckInvokeStaticWithException(const InvokeStaticWithException& expr, const Func& topLevelFunc); void CheckInvokeStaticBase(const InvokeStaticBase& expr, const Func& topLevelFunc); void CheckIntOpWithException(const IntOpWithException& expr, const Func& topLevelFunc); void CheckUnaryExprBase(const UnaryExprBase& expr, const Func& topLevelFunc); void CheckBinaryExprBase(const BinaryExprBase& expr, const Func& topLevelFunc); void CheckSpawnWithException(const SpawnWithException& expr, const Func& topLevelFunc); void CheckSpawnBase(const SpawnBase& expr, const Func& topLevelFunc); void CheckTypeCastWithException(const TypeCastWithException& expr, const Func& topLevelFunc); void CheckIntrinsicWithException(const IntrinsicWithException& expr, const Func& topLevelFunc); void CheckIntrinsicBase(const IntrinsicBase& expr, const Func& topLevelFunc); void CheckAllocateWithException(const AllocateWithException& expr, const Func& topLevelFunc); void CheckAllocateBase(const AllocateBase& expr, const Func& topLevelFunc); void CheckRawArrayAllocateWithException(const RawArrayAllocateWithException& expr, const Func& topLevelFunc); void CheckRawArrayAllocateBase(const RawArrayAllocateBase& expr, const Func& topLevelFunc); // ===--------------------------------------------------------------------===// // Check Unary Expression // ===--------------------------------------------------------------------===// void CheckUnaryExpression(const UnaryExpression& expr, const Func& topLevelFunc); // ===--------------------------------------------------------------------===// // Check Binary Expression // ===--------------------------------------------------------------------===// void CheckBinaryExpression(const BinaryExpression& expr, const Func& topLevelFunc); void CheckCalculExpression(const BinaryExprBase& expr, const Func& topLevelFunc); void CheckExponentiationExpression(const BinaryExprBase& expr, const Func& topLevelFunc); void CheckBitExpression(const BinaryExprBase& expr, const Func& topLevelFunc); void CheckCompareExpression(const BinaryExprBase& expr, const Func& topLevelFunc); void CheckLogicExpression(const BinaryExprBase& expr, const Func& topLevelFunc); // ===--------------------------------------------------------------------===// // Check Memory Expression // ===--------------------------------------------------------------------===// void CheckMemoryExpression(const Expression& expr, const Func& topLevelFunc); void CheckAllocate(const Allocate& expr, const Func& topLevelFunc); void CheckLoad(const Load& expr, const Func& topLevelFunc); void CheckStore(const Store& expr, const Func& topLevelFunc); void CheckGetElementRef(const GetElementRef& expr, const Func& topLevelFunc); void CheckGetElementByName(const GetElementByName& expr, const Func& topLevelFunc); void CheckStoreElementRef(const StoreElementRef& expr, const Func& topLevelFunc); void CheckStoreElementByName(const StoreElementByName& expr, const Func& topLevelFunc); // ===--------------------------------------------------------------------===// // Check Control Flow Expression // ===--------------------------------------------------------------------===// void CheckControlFlowExpression(const Expression& expr, const Func& topLevelFunc); void CheckLambda(const Lambda& expr, const Func& topLevelFunc); // ===--------------------------------------------------------------------===// // Check Other Expression // ===--------------------------------------------------------------------===// void CheckOtherExpression(const Expression& expr, const Func& topLevelFunc); void CheckConstant(const Constant& expr, const Func& topLevelFunc); void CheckDebug(const Debug& expr, const Func& topLevelFunc); void CheckTuple(const Tuple& expr, const Func& topLevelFunc); void CheckEnumTuple(const Tuple& expr, const Func& topLevelFunc); void CheckStructTuple(const Tuple& expr, const Func& topLevelFunc); void CheckNormalTuple(const Tuple& expr, const Func& topLevelFunc); void CheckField(const Field& expr, const Func& topLevelFunc); void CheckFieldByName(const FieldByName& expr, const Func& topLevelFunc); void CheckApply(const Apply& expr, const Func& topLevelFunc); bool CheckCallee(const Value& callee, const Expression& expr, const Func& topLevelFunc); void CheckApplyFuncArgs(const std::vector& args, const std::vector& instParamTypes, bool varArgs, const Expression& expr, const Func& topLevelFunc); FuncType* CalculateInstFuncType( FuncType& originalFuncType, const std::vector& instantiatedTypeArgs, const std::vector& genericTypeParams, Type* instOuterType); bool CheckInstantiatedTypeArgs(const std::vector& instantiatedTypeArgs, const std::vector& genericTypeParams, const Expression& expr, const Func& topLevelFunc); bool CheckApplyThisType( const Value& callee, const Type* thisType, const Expression& expr, const Func& topLevelFunc); void CheckApplyFuncRetValue(const Type& instRetType, const Expression& expr, const Func& topLevelFunc); void CheckInvoke(const Invoke& expr, const Func& topLevelFunc); bool CheckInvokeThisType( Type& objType, const Type* thisType, const Expression& expr, const Func& topLevelFunc); bool CheckVirtualMethod(const VirMethodFullContext& methodCtx, const Expression& expr, const Func& topLevelFunc); std::vector CheckVTableExist( const Type& thisType, const ClassType& srcParentType, const Expression& expr, const Func& topLevelFunc); std::vector CheckVTableExist(const BuiltinType& thisType, const ClassType& srcParentType); std::vector CheckVTableExist(const CustomType& thisType, const ClassType& srcParentType); std::vector CheckVTableExist(const ClassType& srcParentType, const Func& topLevelFunc); std::vector CheckVTableExist(const GenericType& thisType, const ClassType& srcParentType); bool CheckVirtualMethodFuncType(const FuncType& declaredType, const FuncType& callSiteType, const std::string& errMsgBase, const Func& topLevelFunc); void CheckInvokeFuncArgs(const std::vector& args, const std::vector& originalParamTypes, const Expression& expr, const Func& topLevelFunc); void CheckInvokeStatic(const InvokeStatic& expr, const Func& topLevelFunc); void CheckInstanceOf(const InstanceOf& expr, const Func& topLevelFunc); void CheckTypeCast(const TypeCast& expr, const Func& topLevelFunc); void CheckGetException(const GetException& expr, const Func& topLevelFunc); void CheckSpawn(const Spawn& expr, const Func& topLevelFunc); void CheckRawArrayAllocate(const RawArrayAllocate& expr, const Func& topLevelFunc); void CheckRawArrayLiteralInit(const RawArrayLiteralInit& expr, const Func& topLevelFunc); void CheckRawArrayInitByValue(const RawArrayInitByValue& expr, const Func& topLevelFunc); void CheckVArray(const VArray& expr, const Func& topLevelFunc); void CheckVArrayBuilder(const VArrayBuilder& expr, const Func& topLevelFunc); void CheckIntrinsic(const Intrinsic& expr, const Func& topLevelFunc); void CheckBox(const Box& expr, const Func& topLevelFunc); void CheckUnBox(const UnBox& expr, const Func& topLevelFunc); void CheckTransformToGeneric(const TransformToGeneric& expr, const Func& topLevelFunc); void CheckTransformToConcrete(const TransformToConcrete& expr, const Func& topLevelFunc); void CheckGetInstantiateValue(const GetInstantiateValue& expr, const Func& topLevelFunc); void CheckUnBoxToRef(const UnBoxToRef& expr, const Func& topLevelFunc); void CheckGetRTTI(const GetRTTI& expr, const Func& topLevelFunc); void CheckGetRTTIStatic(const GetRTTIStatic& expr, const Func& topLevelFunc); bool CheckThisTypeIsEqualOrSubTypeOfFuncParentType( Type& thisType, const FuncBase& func, const Expression& expr, const Func& topLevelFunc); // ===--------------------------------------------------------------------===// // Check Expressions // ===--------------------------------------------------------------------===// void CheckExpression(const Expression& expr, const Func& topLevelFunc); /** * @brief terminator can't jump to another block group */ void CheckTerminatorJump(const Terminator& terminator, const Func& topLevelFunc); // ===--------------------------------------------------------------------===// // Utils // ===--------------------------------------------------------------------===// void OverflowStrategyMustBeValid( const OverflowStrategy& ofs, const Expression& expr, const Func& topLevelFunc); bool CheckTypeIsValid( const Type& type, const std::string& typeName, const Expression& expr, const Func& topLevelFunc); bool OperandNumIsEqual(size_t expectedNum, const Expression& expr, const Func& topLevelFunc); bool OperandNumIsEqual(const std::vector& expectedNum, const Expression& expr, const Func& topLevelFunc); bool SuccessorNumIsEqual(size_t expectedNum, const Terminator& expr, const Func& topLevelFunc); bool OperandNumAtLeast(size_t expectedNum, const Expression& expr, const Func& topLevelFunc); bool SuccessorNumAtLeast(size_t expectedNum, const Terminator& expr, const Func& topLevelFunc); void ShouldNotHaveResult(const Terminator& expr, const Func& topLevelFunc); bool CheckHaveResult(const Expression& expr, const Func& topLevelFunc); private: const Package& package; const Cangjie::GlobalOptions& opts; CHIRBuilder& builder; std::unordered_set identifiers; std::set duplicatedGlobalIds; std::mutex checkIdentMutex; std::ostream& errorMessage = std::cerr; std::atomic checkResult{true}; std::unordered_set optionalRules; }; } #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/CHIRContext.h000066400000000000000000000177741510705540100230420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the CHIRContext class in CHIR. */ #ifndef CANGJIE_CHIR_CHIRCONTEXT_H #define CANGJIE_CHIR_CHIRCONTEXT_H #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" #include #include #include #include namespace Cangjie::CHIR { class Type; class Expression; class Package; class BlockGroup; class Block; class ExtendDef; struct TypePtrHash { size_t operator()(const Type* ptr) const; }; struct TypePtrEqual { bool operator()(const Type* ptr1, const Type* ptr2) const; }; class CHIRContext { CHIRContext(const CHIRContext&) = delete; CHIRContext& operator=(const CHIRContext&) = delete; public: std::unordered_set constAllocatedTys; std::unordered_set dynamicAllocatedTys; explicit CHIRContext(std::unordered_map* fnMap = nullptr, size_t threadsNum = 1); ~CHIRContext(); Package* GetCurPackage() const; void SetCurPackage(Package* pkg); // Register Source Name void RegisterSourceFileName(unsigned fileId, const std::string& fileName) const; const std::string& GetSourceFileName(unsigned fileId) const; const std::unordered_map* GetFileNameMap() const; /** @brief Move the dynamicAllocatedTys in CHIRContext into constAllocatedTys of CHIRContext in this.*/ void MergeTypes(); void SetFileNameMap(std::unordered_map* fnMap) { fileNameMap = fnMap; } void SetThreadNum(size_t num) { threadsNum = num; } // ===--------------------------------------------------------------------===// // Type API // ===--------------------------------------------------------------------===// /** @brief Return a type.*/ template TType* GetType(Args&&... args) { TType checkTy(std::forward(args)...); auto constIt = this->constAllocatedTys.find(&checkTy); if (constIt == this->constAllocatedTys.end()) { std::unique_lock lock(CHIRContext::dynamicAllocatedTysMtx); auto dynamicIt = this->dynamicAllocatedTys.find(&checkTy); if (dynamicIt == this->dynamicAllocatedTys.end()) { auto ty = new TType(std::forward(args)...); this->dynamicAllocatedTys.insert(ty); return ty; } else { return static_cast(*dynamicIt); } } else { return static_cast(*constIt); } } StructType* GetStructType( const std::string& package, const std::string& name, const std::vector& genericType = {}) const; StructType* GetStringTy() const; NothingType* GetNothingType() const { return nothingTy; } UnitType* GetUnitTy() const { return unitTy; } BooleanType* GetBoolTy() const { return boolTy; } RuneType* GetRuneTy() const { return runeTy; } IntType* GetInt8Ty() const { return int8Ty; } IntType* GetInt16Ty() const { return int16Ty; } IntType* GetInt32Ty() const { return int32Ty; } IntType* GetInt64Ty() const { return int64Ty; } IntType* GetIntNativeTy() const { return intNativeTy; } IntType* GetUInt8Ty() const { return uint8Ty; } IntType* GetUInt16Ty() const { return uint16Ty; } IntType* GetUInt32Ty() const { return uint32Ty; } IntType* GetUInt64Ty() const { return uint64Ty; } IntType* GetUIntNativeTy() const { return uintNativeTy; } FloatType* GetFloat16Ty() const { return float16Ty; } FloatType* GetFloat32Ty() const { return float32Ty; } FloatType* GetFloat64Ty() const { return float64Ty; } CStringType* GetCStringTy() const { return cstringTy; } VoidType* GetVoidTy() const { return voidTy; } // Need refactor: object may be a new type and not inherited from Class void SetObjectTy(ClassType* ty) { objectTy = ty; } ClassType* GetObjectTy() const { CJC_ASSERT(objectTy != nullptr); return objectTy; } void SetAnyTy(ClassType* ty) { anyTy = ty; } ClassType* GetAnyTy() const { CJC_ASSERT(anyTy != nullptr); return anyTy; } // get enum selector type based on type kind Type* ToSelectorType(Type::TypeKind kind) const; size_t GetAllNodesNum() const { size_t num = 0; for (auto& ele : allocatedExprs) { (void)ele; num++; } for (auto& ele : allocatedValues) { (void)ele; num++; } for (auto& ele : allocatedBlockGroups) { (void)ele; num++; } for (auto& ele : allocatedBlocks) { (void)ele; num++; } for (auto& ele : allocatedStructs) { (void)ele; num++; } for (auto& ele : allocatedClasses) { (void)ele; num++; } for (auto& ele : allocatedEnums) { (void)ele; num++; } return num; } size_t GetTypesNum() const { return dynamicAllocatedTys.size(); } std::vector& GetAllocatedExprs() { return allocatedExprs; } std::vector& GetAllocatedValues() { return allocatedValues; } std::vector& GetAllocatedBlockGroups() { return allocatedBlockGroups; } std::vector& GetAllocatedBlocks() { return allocatedBlocks; } std::vector& GetAllocatedStructs() { return allocatedStructs; } std::vector& GetAllocatedClasses() { return allocatedClasses; } std::vector& GetAllocatedEnums() { return allocatedEnums; } std::vector& GetAllocatedExtends() { return allocatedExtends; } void DeleteAllocatedInstance(std::vector& idxs); void DeleteAllocatedTys(); private: /* * @brief Cached Pointer of allocated instance in CHIR. */ Package* curPackage; /* The file name string pool for debug location: fileID map to source path */ std::unordered_map* fileNameMap; /* * @brief Cached Pointer of allocated instance in CHIR. */ std::vector allocatedExprs; std::vector allocatedValues; std::vector allocatedBlockGroups; std::vector allocatedBlocks; std::vector allocatedStructs; std::vector allocatedClasses; std::vector allocatedEnums; std::vector allocatedExtends; static std::mutex allocatedListMtx; UnitType* unitTy{nullptr}; BooleanType* boolTy{nullptr}; RuneType* runeTy{nullptr}; NothingType* nothingTy{nullptr}; IntType *int8Ty{nullptr}, *int16Ty{nullptr}, *int32Ty{nullptr}, *int64Ty{nullptr}, *intNativeTy{nullptr}; IntType *uint8Ty{nullptr}, *uint16Ty{nullptr}, *uint32Ty{nullptr}, *uint64Ty{nullptr}, *uintNativeTy{nullptr}; FloatType *float16Ty{nullptr}, *float32Ty{nullptr}, *float64Ty{nullptr}; CStringType* cstringTy{nullptr}; ClassType* objectTy{nullptr}; ClassType* anyTy{nullptr}; VoidType* voidTy{nullptr}; static std::mutex dynamicAllocatedTysMtx; size_t threadsNum; }; } // namespace Cangjie::CHIR #endif // CANGJIE_CHIR_CHIRCONTEXT_Hcangjie_compiler-1.0.7/include/cangjie/CHIR/CHIRPrinter.h000066400000000000000000000023761510705540100230310ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the CHIRPrinter class in CHIR. */ #ifndef CANGJIE_CHIR_CHIRPRINTER_H #define CANGJIE_CHIR_CHIRPRINTER_H #include "cangjie/CHIR/CHIR.h" #include namespace Cangjie::CHIR { class Type; class Expression; class Func; class If; class Loop; class ForIn; class Value; class Block; class BlockGroup; class Package; class CHIRPrinter { public: static void PrintCFG(const Func& func, const std::string& path); static void PrintPackage(const Package& package, const std::string& fullPath); static void PrintCHIRSerializeInfo(ToCHIR::Phase phase, const std::string& path); public: CHIRPrinter(std::ostream& os) : os(os) { } ~CHIRPrinter() = default; /* * @brief Returns the output stream of the printer. * */ std::ostream& GetStream() const { return os; } private: /** @brief The output stream for the printer.*/ std::ostream& os; }; } // namespace Cangjie::CHIR #endif // CANGJIE_CHIR_CHIRPRINTER_Hcangjie_compiler-1.0.7/include/cangjie/CHIR/Checker/000077500000000000000000000000001510705540100221235ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/CHIR/Checker/ComputeAnnotations.h000066400000000000000000000056631510705540100261400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. // This files declares the entry of ComputeAnnotationsBeforeCheck stage. #ifndef CANGJIE_CHIR_CHECKER_COMPUTEANNOTATIONS_H #define CANGJIE_CHIR_CHECKER_COMPUTEANNOTATIONS_H #include "cangjie/AST/Node.h" #include "cangjie/CHIR/CHIR.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { using namespace AST; using Number = unsigned long long; /// Value representation of Annotation after consteval. /// All integers/bool/char are represented internally as \ref Number, any object-like is stored in /// AnnoInstanceClassInst object. Two shared_ptr point to the same object when they point to the /// same CHIR object. Unit is never stored. /// Enum with args are stored as the index of constructor followed by constructor args together as a vector. Enum /// withoug args are stored as \ref Number, a simple index of constructor. struct AnnoInstanceValue : public std::variant, std::monostate> { Number Value() const; long long SignedValue() const; unsigned Rune() const; bool Bool() const; const std::string& String() const; double Float() const { return std::get(*this); } std::shared_ptr Object() const; operator bool() const noexcept; std::string ToString() const noexcept; }; /// object representation in AnnoInstanceValue struct AnnoInstanceClassInst { const InheritableDecl* cl; std::vector> a; const AnnoInstanceValue* GetField(const std::string& s) const { for (auto& p : a) { if (!p.first.empty() && p.first == s) { return &p.second; } } return nullptr; } const AnnoInstanceValue& GetField(size_t i) const { return a[i].second; } std::string ToString() const; }; using AnnoInstance = std::shared_ptr; using AnnoMap = std::unordered_map>; /// Temporary results of computing annotations. /// Fields \ref ctx and \ref builder are used for holding the memory used by other fields. /// \ref map A map from AST decl to the vector of Annotation Instance of it. struct ConstEvalResult { CHIRContext ctx; CHIRBuilder builder; CHIR::Package* pkg; AnnoMap map; ConstEvalResult(std::unordered_map& fileNameMap, size_t jobs) : ctx{}, builder{ctx, jobs}, pkg{}, map{} { ctx.SetFileNameMap(&fileNameMap); ctx.SetThreadNum(jobs); } void Dump() const; }; OwnedPtr ComputeAnnotations(AST::Package& pkg, CompilerInstance& ci); } #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Checker/ConstSafetyCheck.h000066400000000000000000000017641510705540100255040ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_CHECKER_CONST_SAFETY_CHECK_H #define CANGJIE_CHIR_CHECKER_CONST_SAFETY_CHECK_H #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/CHIR/Analysis/AnalysisWrapper.h" #include "cangjie/CHIR/Analysis/ConstAnalysis.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Package.h" namespace Cangjie::CHIR { class ConstSafetyCheck { public: using ConstAnalysisWrapper = AnalysisWrapper; explicit ConstSafetyCheck(ConstAnalysisWrapper* constAnalysisWrapper); void RunOnPackage(const Package& package, size_t threadNum) const; void RunOnFunc(const Func* func) const; private: ConstAnalysisWrapper* analysisWrapper; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Checker/UnreachableBranchCheck.h000066400000000000000000000024241510705540100265630ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_CHECKER_UNREACHABLE_BRANCH_CHECK_H #define CANGJIE_CHIR_CHECKER_UNREACHABLE_BRANCH_CHECK_H #include "cangjie/CHIR/Analysis/AnalysisWrapper.h" #include "cangjie/CHIR/Analysis/ConstAnalysis.h" #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/DiagAdapter.h" #include "cangjie/CHIR/Package.h" #include "cangjie/Utils/TaskQueue.h" namespace Cangjie::CHIR { class UnreachableBranchCheck { public: using ConstAnalysisWrapper = AnalysisWrapper; explicit UnreachableBranchCheck( ConstAnalysisWrapper* constAnalysisWrapper, DiagAdapter& diag, const std::string& packageName); void RunOnPackage(const Package& package, size_t threadNum); void RunOnFunc(const Ptr func); private: void PrintWarning(const Terminator& node, Block& block, std::set& hasProcessed, bool isRecursive = false); DiagAdapter& diag; ConstAnalysisWrapper* analysisWrapper; const std::string& currentPackageName; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Checker/VarInitCheck.h000066400000000000000000000055131510705540100246120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_CHECKER_VAR_INIT_CHECK_H #define CANGJIE_CHIR_CHECKER_VAR_INIT_CHECK_H #include "cangjie/CHIR/Analysis/MaybeInitAnalysis.h" #include "cangjie/CHIR/Analysis/MaybeUninitAnalysis.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/DiagAdapter.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Package.h" namespace Cangjie::CHIR { class VarInitCheck { public: explicit VarInitCheck(DiagAdapter* diag); void RunOnPackage(const Package* package, size_t threadNum); void RunOnFunc(const Func* func); private: // ================================================================= // void UseBeforeInitCheck(const Func* func, const ConstructorInitInfo* ctorInitInfo, const std::vector& members); bool CheckLoadToUninitedAllocation(const MaybeUninitDomain& state, const Load& load) const; bool CheckGetElementRefToUninitedAllocation( const MaybeUninitDomain& state, const GetElementRef& getElementRef) const; void CheckLoadToUninitedCustomDefMember(const MaybeUninitDomain& state, const Func* func, const Load* load, const std::vector& members) const; void CheckStoreToUninitedCustomDefMember(const MaybeUninitDomain& state, const Func* func, const StoreElementRef* store, const std::vector& members) const; void AddMaybeInitedPosNote( DiagnosticBuilder& builder, const std::string& identifier, const std::set& maybeInitedPos) const; void CheckUninitedDefMember(const MaybeUninitDomain& state, const Expression* expr, const std::vector& members, size_t index, bool onlyCheckSuper = false) const; void RaiseUninitedDefMemberError(const MaybeUninitDomain& state, const Func* func, const std::vector& members, const std::vector& uninitedMemberIdx) const; template void CheckMemberFuncCall(const MaybeUninitDomain& state, const Func& initFunc, const TApply& apply) const; void RaiseIllegalMemberFunCallError(const Expression* apply, const Func* memberFunc) const; // ================================================================= // void ReassignInitedLetVarCheck(const Func* func, const ConstructorInitInfo* ctorInitInfo, const std::vector& members) const; void CheckStoreToInitedCustomDefMember(const MaybeInitDomain& state, const Func* func, const StoreElementRef* store, const std::vector& members) const; private: DiagAdapter* diag; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/ConstantUtils.h000066400000000000000000000014401510705540100235410ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares some utility constants. */ #ifndef CANGJIE_CHIR_CONSTANTUTILS_H #define CANGJIE_CHIR_CONSTANTUTILS_H #include namespace Cangjie::CHIR { inline const std::string FUNC_MANGLE_NAME_MALLOC_CSTRING = "_CNat4LibC13mallocCStringHRNat6StringE"; inline const std::string FUNC_MANGLE_NAME_CSTRING_SIZE = "_CNatXk4sizeHv"; inline const std::string GLOBAL_VALUE_PREFIX = "@"; // identifier prefix constexpr size_t CLASS_REF_DIM{2}; } // namespace Cangjie::CHIR #endif // CANGJIE_CHIR_CONSTANTUTILS_H cangjie_compiler-1.0.7/include/cangjie/CHIR/DebugLocation.h000066400000000000000000000057021510705540100234530ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the DebugLocation in CHIR. */ #ifndef CANGJIE_CHIR_DEBUGLOCATION_H #define CANGJIE_CHIR_DEBUGLOCATION_H #include #include namespace Cangjie::CHIR { const std::string INVALID_NAME = ""; // Invalid file name struct Position { unsigned line{0}; unsigned column{0}; bool IsLegal() const { return line != 0 && column != 0; } bool IsZero() const { return line == 0 && column == 0; } }; /** * @brief A Debug location in source code. * */ class DebugLocation { public: DebugLocation(const std::string& absPath, unsigned fileID, const Position& beginPos, const Position& endPos, const std::vector& scopeInfo = {0}) : absPath(&absPath), fileID(fileID), beginPos(beginPos), endPos(endPos), scopeInfo(scopeInfo) { } DebugLocation() : absPath(&INVALID_NAME), fileID(0), beginPos({0, 0}), endPos({0, 0}) { } ~DebugLocation() = default; // ===--------------------------------------------------------------------===// // Position // ===--------------------------------------------------------------------===// Position GetBeginPos() const; void SetBeginPos(const Position& pos); Position GetEndPos() const; void SetEndPos(const Position& pos); bool IsInvalidPos() const; bool IsInvalidMacroPos() const; // ===--------------------------------------------------------------------===// // Scope Info // ===--------------------------------------------------------------------===// std::vector GetScopeInfo() const; void SetScopeInfo(const std::vector& scope); // ===--------------------------------------------------------------------===// // File Info // ===--------------------------------------------------------------------===// unsigned GetFileID() const; const std::string& GetAbsPath() const; std::string GetFileName() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// bool operator==(const DebugLocation& other) const; std::string ToString() const; void Dump() const; private: const std::string* absPath; /* the absolute path of file */ unsigned fileID; /* the file id */ Position beginPos; /* the begin position in file, start from 1, 1 */ Position endPos; /* the end position in file, start from 1, 1 */ std::vector scopeInfo; /* scope info, like 0-0-0 0-1 */ }; const DebugLocation INVALID_LOCATION = DebugLocation(); } // namespace Cangjie::CHIR #endif // CANGJIE_CHIR_DEBUGLOCATION_H cangjie_compiler-1.0.7/include/cangjie/CHIR/DiagAdapter.h000066400000000000000000000076561510705540100231130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_DIAG_WRAPPER_H #define CANGJIE_CHIR_DIAG_WRAPPER_H #include #include "cangjie/AST/Node.h" #include "cangjie/Basic/DiagnosticEngine.h" namespace Cangjie::CHIR { class DiagAdapter { public: DiagAdapter(DiagnosticEngine& diag) : diag(diag){}; template DiagnosticBuilder DiagnoseRefactor(DiagKindRefactor kind, const Range& range, Args&&... args) { auto key = static_cast(range.begin.Hash64()); auto it = posRange2MacroCallMap.lower_bound(key); if (it == posRange2MacroCallMap.end() || !it->second) { // means the range's begin is not from macro expanded node return diag.DiagnoseRefactor(kind, range, std::forward(args)...); } AST::Node node; node.EnableAttr(AST::Attribute::MACRO_EXPANDED_NODE); node.curMacroCall = it->second; return diag.DiagnoseRefactor(kind, node, range, std::forward(args)...); } template DiagnosticBuilder DiagnoseRefactor(DiagKindRefactor kind, const Cangjie::Position& pos, Args&&... args) { auto key = static_cast(pos.Hash64()); auto it = posRange2MacroCallMap.lower_bound(key); CJC_ASSERT(it == posRange2MacroCallMap.end() || !it->second); // means the range's begin is not from macro expanded node return diag.DiagnoseRefactor(kind, pos, std::forward(args)...); } template DiagnosticBuilder Diagnose(DiagKind kind, Args&&... args) { return diag.Diagnose(kind, std::forward(args)...); } uint64_t GetErrorCount() { return diag.GetErrorCount(); } SourceManager& GetSourceManager() noexcept { return diag.GetSourceManager(); } /** * the purpose of this map is to record PositionRange-to-MacroCall relation * for any position from `Macro Expanded Node`, we can get original `Macro Call`. * * let's give an example of a macro call * * * @Test * func foo() { * ... * let a = 1 * } * * let b = 2 * * * Because `a` is in MacroCall but `b` is not, position of `a` may change after `Macro Expand`, * the position relationship before and after `Macro Expand` of `a` is maintained in `Macro Call Node`, * it's important to get correct MacroCall Node from any random postion, * here we use this map to record PositionRange-to-MacroCall mapping. * * in detail of mapping, as for this example, will insert two key-value to this map, * the first is `@`'s position and the pointer that point to `Macro Call Node` * the second is `}`'s position and nullptr * * Let's say we need to get correct position of two VarDecls: a and b * as for a: * to find the correct `Macro Call Node`, we can use `std::map::lower_bound` to find `@`, * whose position is `not greater` than a, that is `less or equal` than a. * * the combination of `std::greater` and `std::map::lower_bound` * is actually just a way to achieve `not greater`, * * you may ask why not use `std::map::upper_bound` to find `@`, because then if we use `@` as an input to find * the corresponding MacroCallNode, it will failed. * * as for b: * we found `}`'s position and nullptr by the same way, but `}`'s postion corresponds to `nullptr` as we * set before, so the `b` is not in a `Macro Call Node`, the postion of b is already correct. * */ std::map, std::greater<>> posRange2MacroCallMap; DiagnosticEngine& diag; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/ExprKind.inc000066400000000000000000000104521510705540100230000ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. // Tips: // 1. EXPRKIND(expression kind, exression name for human reading, Expression class) // 2. Remember to modify CHIRCasting.h and PackageFormat.fbs after adding a new expression #if defined(MAJORKIND) && defined(EXPRKIND) MAJORKIND(TERMINATOR, EXPRKIND(GOTO, "GoTo", GoTo) EXPRKIND(BRANCH, "Branch", Branch) EXPRKIND(MULTIBRANCH, "MultiBranch", MultiBranch) EXPRKIND(EXIT, "Exit", Exit) EXPRKIND(APPLY_WITH_EXCEPTION, "ApplyWithException", ApplyWithException) EXPRKIND(INVOKE_WITH_EXCEPTION, "InvokeWithException", InvokeWithException) EXPRKIND(INVOKESTATIC_WITH_EXCEPTION, "InvokeStaticWithException", InvokeStaticWithException) EXPRKIND(RAISE_EXCEPTION, "RaiseException", RaiseException) EXPRKIND(INT_OP_WITH_EXCEPTION, "IntOpWithException", IntOpWithException) EXPRKIND(SPAWN_WITH_EXCEPTION, "SpawnWithException", SpawnWithException) EXPRKIND(TYPECAST_WITH_EXCEPTION, "TypeCastWithException", TypeCastWithException) EXPRKIND(INTRINSIC_WITH_EXCEPTION, "IntrinsicWithException", IntrinsicWithException) EXPRKIND(ALLOCATE_WITH_EXCEPTION, "AllocateWithException", AllocateWithException) EXPRKIND(RAW_ARRAY_ALLOCATE_WITH_EXCEPTION, "RawArrayAllocateWithException", RawArrayAllocateWithException) ) MAJORKIND(UNARY_EXPR, EXPRKIND(NEG, "Neg", UnaryExpression) EXPRKIND(NOT, "Not", UnaryExpression) EXPRKIND(BITNOT, "BitNot", UnaryExpression) ) MAJORKIND(BINARY_EXPR, EXPRKIND(ADD, "Add", BinaryExpression) EXPRKIND(SUB, "Sub", BinaryExpression) EXPRKIND(MUL, "Mul", BinaryExpression) EXPRKIND(DIV, "Div", BinaryExpression) EXPRKIND(MOD, "Mod", BinaryExpression) EXPRKIND(EXP, "Exp", BinaryExpression) EXPRKIND(LSHIFT, "LShift", BinaryExpression) EXPRKIND(RSHIFT, "RShift", BinaryExpression) EXPRKIND(BITAND, "BitAnd", BinaryExpression) EXPRKIND(BITOR, "BitOr", BinaryExpression) EXPRKIND(BITXOR, "BitXor", BinaryExpression) EXPRKIND(LT, "LT", BinaryExpression) EXPRKIND(GT, "GT", BinaryExpression) EXPRKIND(LE, "LE", BinaryExpression) EXPRKIND(GE, "GE", BinaryExpression) EXPRKIND(EQUAL, "Equal", BinaryExpression) EXPRKIND(NOTEQUAL, "NotEqual", BinaryExpression) EXPRKIND(AND, "And", BinaryExpression) EXPRKIND(OR, "Or", BinaryExpression) ) MAJORKIND(MEMORY_EXPR, EXPRKIND(ALLOCATE, "Allocate", Allocate) EXPRKIND(LOAD, "Load", Load) EXPRKIND(STORE, "Store", Store) EXPRKIND(GET_ELEMENT_REF, "GetElementRef", GetElementRef) EXPRKIND(GET_ELEMENT_BY_NAME, "GetElementByName", GetElementByName) EXPRKIND(STORE_ELEMENT_REF, "StoreElementRef", StoreElementRef) EXPRKIND(STORE_ELEMENT_BY_NAME, "StoreElementByName", StoreElementByName) ) MAJORKIND(STRUCTURED_CTRL_FLOW_EXPR, EXPRKIND(IF, "If", If) EXPRKIND(LOOP, "Loop", Loop) EXPRKIND(FORIN_RANGE, "ForInRange", ForInRange) EXPRKIND(FORIN_ITER, "ForInIter", ForInIter) EXPRKIND(FORIN_CLOSED_RANGE, "ForInClosedRange", ForInClosedRange) EXPRKIND(LAMBDA, "Lambda", Lambda) ) MAJORKIND(OTHERS, EXPRKIND(CONSTANT, "Constant", Constant) EXPRKIND(DEBUGEXPR, "Debug", Debug) EXPRKIND(TUPLE, "Tuple", Tuple) EXPRKIND(FIELD, "Field", Field) EXPRKIND(FIELD_BY_NAME, "FieldByName", FieldByName) EXPRKIND(APPLY, "Apply", Apply) EXPRKIND(INVOKE, "Invoke", Invoke) EXPRKIND(INVOKESTATIC, "InvokeStatic", InvokeStatic) EXPRKIND(INSTANCEOF, "InstanceOf", InstanceOf) EXPRKIND(TYPECAST, "TypeCast", TypeCast) EXPRKIND(GET_EXCEPTION, "GetException", GetException) EXPRKIND(RAW_ARRAY_ALLOCATE, "RawArrayAllocate", RawArrayAllocate) EXPRKIND(RAW_ARRAY_LITERAL_INIT, "RawArrayLiteralInit", RawArrayLiteralInit) EXPRKIND(RAW_ARRAY_INIT_BY_VALUE, "RawArrayInitByValue", RawArrayInitByValue) EXPRKIND(VARRAY, "VArray", VArray) EXPRKIND(VARRAY_BUILDER, "VArrayBuilder", VArrayBuilder) EXPRKIND(INTRINSIC, "Intrinsic", Intrinsic) EXPRKIND(SPAWN, "Spawn", Spawn) EXPRKIND(GET_INSTANTIATE_VALUE, "GetInstantiateValue", GetInstantiateValue) EXPRKIND(BOX, "Box", Box) EXPRKIND(UNBOX, "UnBox", Unbox) EXPRKIND(TRANSFORM_TO_GENERIC, "TransformToGeneric", TransformToGeneric) EXPRKIND(TRANSFORM_TO_CONCRETE, "TransformToConcrete", TransformToConcrete) EXPRKIND(UNBOX_TO_REF, "UnBoxToRef", UnBoxToRef) EXPRKIND(GET_RTTI, "GetRTTI", GetRTTI) EXPRKIND(GET_RTTI_STATIC, "GetRTTIStatic", GetRTTIStatic) ) #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Expression/000077500000000000000000000000001510705540100227165ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/CHIR/Expression/Expression.h000066400000000000000000002202321510705540100252270ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares expressions of CHIR, but not including terminators */ #ifndef CANGJIE_CHIR_EXPRESSION_H #define CANGJIE_CHIR_EXPRESSION_H #include "cangjie/CHIR/IntrinsicKind.h" #include "cangjie/CHIR/LiteralValue.h" #include "cangjie/Utils/SafePointer.h" #include #include #include #include #include #include #include namespace Cangjie::CHIR { class CHIRBuilder; class ExprTypeConverter; /** * @brief Expression major kind. */ enum class ExprMajorKind : uint8_t { #define EXPRKIND(...) #define MAJORKIND(KIND, ...) KIND, #include "cangjie/CHIR/ExprKind.inc" #undef EXPRKIND #undef MAJORKIND }; /** * @brief Expression minor kind. */ enum class ExprKind : uint8_t { INVALID = 0, #define EXPRKIND(KIND, ...) KIND, #define MAJORKIND(_1, ...) __VA_ARGS__ #include "cangjie/CHIR/ExprKind.inc" #undef EXPRKIND #undef MAJORKIND MAX_EXPR_KINDS }; class ExprKindMgr { public: static const ExprKindMgr* Instance() { static ExprKindMgr ins; return &ins; } ExprMajorKind GetMajorKind(ExprKind exprKind) const { return expr2MajorExprMap.at(exprKind); } std::string GetKindName(size_t exprKind) const { return exprKindNames[exprKind]; } private: void InitMap(ExprMajorKind majorKind, ...) { va_list vaList; va_start(vaList, majorKind); ExprKind arg = va_arg(vaList, ExprKind); while (static_cast(arg) != 0) { expr2MajorExprMap[arg] = majorKind; arg = va_arg(vaList, ExprKind); } va_end(vaList); } ExprKindMgr() { #define EXPRKIND(KIND, ...) ExprKind::KIND, #define EXPRKINDS(...) __VA_ARGS__ #define MAJORKIND(KIND, ...) InitMap(ExprMajorKind::KIND, EXPRKINDS(__VA_ARGS__) ExprKind::INVALID); #include "cangjie/CHIR/ExprKind.inc" #undef EXPRKIND #undef EXPRKINDS #undef MAJORKIND } const char* exprKindNames[static_cast(Cangjie::CHIR::ExprKind::MAX_EXPR_KINDS)] = { "INVALID", #define EXPRKIND(KIND, NAME, ...) NAME, #define MAJORKIND(_1, ...) __VA_ARGS__ #include "cangjie/CHIR/ExprKind.inc" #undef EXPRKIND #undef MAJORKIND }; std::unordered_map expr2MajorExprMap; }; /** * @brief class `Expression` is a base class for all expression in CHIR */ class Expression : public Base { friend class CHIRContext; friend class CHIRBuilder; friend class Value; friend class LocalVar; friend class Block; friend class BlockGroup; friend class ExprTypeConverter; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// ExprKind GetExprKind() const; std::string GetExprKindName() const; ExprMajorKind GetExprMajorKind() const; bool IsApply() const; bool IsApplyWithException() const; bool IsBinaryExpr() const; bool IsConstant() const; bool IsConstantNull() const; bool IsConstantBool() const; bool IsConstantInt() const; bool IsConstantFloat() const; bool IsConstantString() const; bool IsDebug() const; bool IsDynamicDispatch() const; bool IsField() const; bool IsFuncCall() const; bool IsIntOpWithException() const; bool IsInvoke() const; bool IsInvokeStaticBase() const; bool IsLambda() const; bool IsLoad() const; bool IsTerminator() const; bool IsTypeCast() const; bool IsUnaryExpr() const; /** * @brief Retrieves all block groups. * * @return A vector of pointers to all block groups. */ const std::vector& GetBlockGroups() const; /** * @brief Retrieves the result of the expression. * * @return The result of the expression. */ LocalVar* GetResult() const; /** * @brief Retrieves the result type of the expression, * if this expression is a terminator and not have result, then return nullptr. * * @return The result type or nullptr. */ Type* GetResultType() const; // ===--------------------------------------------------------------------===// // Parent // ===--------------------------------------------------------------------===// /** * @brief Retrieves the parent block of the expression. * * @return The parent block of the expression. */ Block* GetParentBlock() const; /** * @brief Retrieves the parent block group of the expression. * * @return The parent block group of the expression. */ BlockGroup* GetParentBlockGroup() const; /** * @brief Retrieves the top-level function which this expression belongs to. * * @return The top-level func. */ Func* GetTopLevelFunc() const; // ===--------------------------------------------------------------------===// // Operand // ===--------------------------------------------------------------------===// /** * @brief Retrieves the number of operands. * * @return The number of operands. */ virtual size_t GetNumOfOperands() const; /** * @brief Retrieves an operand by index. * * @param idx The index of the operand to retrieve. * @return The operand at the specified index. */ virtual Value* GetOperand(size_t idx) const; /** * @brief Retrieves all operands. * * @return A vector of pointers to all operands. */ virtual std::vector GetOperands() const; /** * @brief Replace old operand in specified position. * * @param idx The sepcified position. * @param newOperand The destination value. */ virtual void ReplaceOperand(size_t idx, Value* newOperand); /** * @brief Replace old operand with the new one, if there are many old operands in same expression, * then replace them all * * @param oldOperand The source value. * @param newOperand The destination value. */ virtual void ReplaceOperand(Value* oldOperand, Value* newOperand); // ===--------------------------------------------------------------------===// // Modify Self // ===--------------------------------------------------------------------===// /** * @brief Remove this expression from its parent block, and insert it into after the @expr expression. * * @param expr The destination position which is after this reference expression. */ void MoveAfter(Expression* expr); /** * @brief Remove this expression from its parent block, and insert it into before the `expr` expression. * * @param expr The destination position which is before this reference expression. */ void MoveBefore(Expression* expr); /** * @brief Move this expression from current parent block to the end of another one. * * @param block the new block which the expression belongs to */ void MoveTo(Block& block); /** * @brief Break all connection with its parent block and operands * * that means you can not get its parent block and operands by this expression any more, * and you can not get this expression by its parent block and its operands, too. * But we don't free this expression's memory. */ virtual void RemoveSelfFromBlock(); /** * @brief Replaces the current expression with a new expression. * * @param newExpr The new expression to replace the current one. */ virtual void ReplaceWith(Expression& newExpr); // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// /** * @brief Converts the expression to a string representation. * * @param indent The number of spaces to indent the string. Default is 0. * @return The string representation of the expression. */ virtual std::string ToString(size_t indent = 0) const; /** * @brief Converts the expression to a string representation and print out. */ void Dump() const; virtual Expression* Clone(CHIRBuilder& builder, Block& parent) const = 0; protected: explicit Expression( ExprKind kind, const std::vector& operands, const std::vector& blockGroups, Block* parent); explicit Expression(ExprKind kind, const std::vector& operands, Block* parent) : Expression(kind, operands, {}, parent) { } explicit Expression(const Expression& other, Block* parent) : Expression(other.kind, other.operands, other.blockGroups, parent) { } virtual ~Expression() = default; Expression& operator=(const Expression&) = delete; void AppendOperand(Value& op); void SetParent(Block* newParent); void EraseOperands(); std::string CommentToString() const; std::string AddExtraComment(const std::string& comment) const; ExprKind kind; // Expression kind. std::vector operands; // The operands. std::vector blockGroups; // The regions of special expression, such as Func. Block* parent; // The owner basicblock of this expression. LocalVar* result = nullptr; // The result. }; /** * @brief Unary expression, including Neg, Not and BitNot. * * Cangjie Code: * var x = 1 * var y = -x // kind is Neg */ class UnaryExpression : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// using Expression::GetOperand; Value* GetOperand() const; OverflowStrategy GetOverflowStrategy() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; protected: ~UnaryExpression() override = default; private: explicit UnaryExpression(ExprKind kind, Value* operand, Cangjie::OverflowStrategy ofs, Block* parent) : Expression(kind, {operand}, {}, parent), overflowStrategy(ofs) { } UnaryExpression* Clone(CHIRBuilder& builder, Block& parent) const override; Cangjie::OverflowStrategy overflowStrategy; }; /** * @brief Binary expression, including: * 1. mathematical operator, such as `add`, `sub`, `mul`, `div`, `mod` and `exp` * 2. bit operator, such as `<<`, `>>`, `&`, `|` and `^` * 3. comparison operator, such as `<`, `>`, `<=`, `>=`, `==` and `!=` * 4. logical operator, such as `&&` and `||` * * Cangjie Code: * var x = 1 + 2 // kind is Add, `1` is left-hand side op, `2` is right-hand side op */ class BinaryExpression : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves the left-hand side operand. * * @return The left-hand side operand. */ Value* GetLHSOperand() const; /** * @brief Retrieves the right-hand side operand. * * @return The right-hand side operand. */ Value* GetRHSOperand() const; OverflowStrategy GetOverflowStrategy() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; protected: ~BinaryExpression() override = default; private: explicit BinaryExpression(ExprKind kind, Value* lhs, Value* rhs, OverflowStrategy ofs, Block* parent); explicit BinaryExpression(ExprKind kind, Value* lhs, Value* rhs, Block* parent); BinaryExpression* Clone(CHIRBuilder& builder, Block& parent) const override; Cangjie::OverflowStrategy overflowStrategy{Cangjie::OverflowStrategy::NA}; }; /** * @brief Constant expression in CHIR. * There are Bool, Rune, String, Int, Float, Unit literal in Cangjie * * Cangjie Code: * var x = 1 // `x` is Int literal */ class Constant : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// LiteralValue* GetValue() const; // ===--------------------------------------------------------------------===// // All kinds of literal // ===--------------------------------------------------------------------===// bool GetBoolLitVal() const; double GetFloatLitVal() const; char32_t GetRuneLitVal() const; int64_t GetSignedIntLitVal() const; std::string GetStringLitVal() const; uint64_t GetUnsignedIntLitVal() const; bool IsBoolLit() const; bool IsFloatLit() const; bool IsIntLit() const; bool IsJString() const; bool IsNullLit() const; bool IsRuneLit() const; bool IsSignedIntLit() const; bool IsUnSignedIntLit() const; bool IsStringLit() const; bool IsUnitLit() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; protected: ~Constant() override = default; private: explicit Constant(LiteralValue* val, Block* parent) : Expression(ExprKind::CONSTANT, {val}, {}, parent) { } Constant* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief Allocate memory whit specific type * * Cangjie Code: * var x = 1 // allocate 8 Bytes memory */ class Allocate : public Expression { friend class CHIRContext; friend class CHIRBuilder; friend class ExprTypeConverter; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief which type this `Allocate` allocates */ Type* GetType() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; protected: ~Allocate() override = default; private: explicit Allocate(Type* ty, Block* parent) : Expression(ExprKind::ALLOCATE, {}, {}, parent), ty(ty) { } Allocate* Clone(CHIRBuilder& builder, Block& parent) const override; Type* ty; // The type to be allocated. }; /** * @brief Load a value from reference * * Cangjie Code: * var x = 1 * println(x) // Load `x` first */ class Load : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief which reference to be load * * @return a value with reference type */ Value* GetLocation() const; protected: ~Load() override = default; private: explicit Load(Value* location, Block* parent) : Expression(ExprKind::LOAD, {location}, {}, parent) { } Load* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief Store a value to a reference. * * Cangjie Code: * var x = 1 // store `1` to `x`, `1` is the source value, `x` is the location */ class Store : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Get source value */ Value* GetValue() const; /** * @brief Get destination value */ Value* GetLocation() const; protected: ~Store() override = default; private: explicit Store(Value* val, Value* location, Block* parent) : Expression(ExprKind::STORE, {val, location}, {}, parent) { } Store* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief Compute the reference to the corresponding child element data * from the data specified by `Location` base on `Path`. * * Cangjie Code: * struct S { var a = 1 } * var x = S().a // `S()` is the location, the path is { 0 } * * we only get the reference, not take up the memory, unless this reference is loaded */ class GetElementRef : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief which reference to be compute * * @return a value with reference type */ Value* GetLocation() const; const std::vector& GetPath() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; protected: ~GetElementRef() override = default; private: explicit GetElementRef(Value* location, const std::vector& path, Block* parent) : Expression(ExprKind::GET_ELEMENT_REF, {location}, {}, parent), path(path) { } GetElementRef* Clone(CHIRBuilder& builder, Block& parent) const override; std::vector path; }; /** * @brief Compute the reference to the corresponding child element data * from the data specified by `Location` base on `Name`. * * Cangjie Code: * struct S { var a = 1 } * var x = S().a // `S()` is the location, the Name is { "a" } * * we only get the reference, not take up the memory, unless this reference is loaded */ class GetElementByName : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief which reference to be compute * * @return a value with reference type */ Value* GetLocation() const; const std::vector& GetNames() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; protected: ~GetElementByName() override = default; private: explicit GetElementByName(Value* location, const std::vector& names, Block* parent) : Expression(ExprKind::GET_ELEMENT_BY_NAME, {location}, {}, parent), names(names) { } GetElementByName* Clone(CHIRBuilder& builder, Block& parent) const override; std::vector names; }; /** * @brief Store a value to the corresponding child element * * Cangjie Code: * struct S { var a = 1 } * var x = S() * x.a = 2 // `2` is the source value, `x` is the location, the path is { 0 } */ class StoreElementRef : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves the source value. * * @return The value. */ Value* GetValue() const; /** * @brief Retrieves the location. * * @return The location. */ Value* GetLocation() const; /** * @brief Retrieves the path. * * @return A vector containing the path. */ const std::vector& GetPath() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; protected: ~StoreElementRef() override = default; private: explicit StoreElementRef(Value* value, Value* location, const std::vector& path, Block* parent) : Expression(ExprKind::STORE_ELEMENT_REF, {value, location}, {}, parent), path(path) { } StoreElementRef* Clone(CHIRBuilder& builder, Block& parent) const override; std::vector path; }; /** * @brief Store a value to the corresponding child element * * Cangjie Code: * struct S { var a = 1 } * var x = S() * x.a = 2 // `2` is the source value, `x` is the location, the name is { "a" } */ class StoreElementByName : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves the source value. * * @return The value. */ Value* GetValue() const; /** * @brief Retrieves the location. * * @return The location. */ Value* GetLocation() const; const std::vector& GetNames() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; protected: ~StoreElementByName() override = default; private: explicit StoreElementByName(Value* value, Value* location, const std::vector& names, Block* parent) : Expression(ExprKind::STORE_ELEMENT_BY_NAME, {value, location}, {}, parent), names(names) { } StoreElementByName* Clone(CHIRBuilder& builder, Block& parent) const override; std::vector names; }; /** * @brief Context for a function calling */ struct FuncCallContext { std::vector args; // function arguments std::vector instTypeArgs; // See `instantiatedTypeArgs` in class `FuncCall` Type* thisType{nullptr}; // See `thisType` in class `FuncCall` }; /** * @brief Context for a virtual function */ struct VirMethodContext { std::string srcCodeIdentifier; // function name FuncType* originalFuncType{nullptr}; // method signature type, from virtual method type in parent CustomTypeDef std::vector genericTypeParams; }; /** * @brief Context for a virtual function calling */ struct InvokeCallContext { Value* caller{nullptr}; // the object in Invoke, or the rtti in InvokeStatic FuncCallContext funcCallCtx; VirMethodContext virMethodCtx; }; /** * @brief An expression for function call, including Apply, Invoke and InvokeStatic */ class FuncCall : public Expression { friend class ExprTypeConverter; friend class CHIRSerializer; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves a list of the application argument nodes. * * @return A vector of pointers to the application argument nodes. */ virtual std::vector GetArgs() const = 0; /** * @brief Retrieves the type of 'this'. * * @return The type of 'this'. */ Type* GetThisType() const; /** * @brief Sets the type of 'this'. * * @param type The type of 'this'. */ void SetThisType(Type* type); /** * @brief Retrieves the instantiated argument types. * * @return A vector of pointers to the instantiated argument types. */ const std::vector& GetInstantiatedTypeArgs() const; protected: explicit FuncCall(ExprKind kind, const FuncCallContext& funcCallCtx, Block* parent); /** * @brief Record instantiated type args. * Cangjie Code: * func foo() {} * foo() // {Bool, Int32} is current Apply's instantiated type args */ std::vector instantiatedTypeArgs; /** * @brief Record object type if callee is member method. * 1. foo(), global func, `thisType` is nullptr * 2. obj.foo(), instance member method, `thisType` is obj's type * 3. A.foo(), static member method, `thisType` is A * 4. method(), a member method with implicit `this`, `thisType` is parent custom type * * As for global func and instance member method, we can compute `thisType` from callee's args, * but for static member method, we can't. */ Type* thisType{nullptr}; }; /** * @brief A non-virtual function call * * Cangjie Code: * func foo() {} * foo() // `foo` is global function, not virtual function */ class Apply : public FuncCall { friend class ExprTypeConverter; friend class CHIRSerializer; friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves the callee of the application. * * @return The callee of the application. */ Value* GetCallee() const; /** * @brief Retrieves a list of the application argument nodes. * * @return A vector of pointers to the application argument nodes. */ std::vector GetArgs() const override; /** * @brief Checks if the call is a super call. * * @return True if the call is a super call, false otherwise. */ bool IsSuperCall() const; /** * @brief Mark this Apply is `super()`. */ void SetSuperCall(); // ===--------------------------------------------------------------------===// // Instantiated Types // ===--------------------------------------------------------------------===// /** * @brief Retrieves the instantiated parent custom type of the callee. * * @return The instantiated parent custom type of the callee. */ Type* GetInstParentCustomTyOfCallee(CHIRBuilder& builder) const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; protected: ~Apply() override = default; private: explicit Apply(Value* callee, const FuncCallContext& callContext, Block* parent); Apply* Clone(CHIRBuilder& builder, Block& parent) const override; /** * @brief Mark this Apply is `super()`. */ bool isSuperCall{false}; }; /** * @brief An expression for virtual function call, including Invoke and InvokeStatic */ class DynamicDispatch : public FuncCall { friend class ExprTypeConverter; friend class PrivateTypeConverterNoInvokeOriginal; friend class CHIRSerializer; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves the method name of this Invoke operation. * * @return The method name of this Invoke operation. */ const std::string& GetMethodName() const; /** * @brief Retrieves the method type of this Invoke operation. * * @return The method type of this Invoke operation. */ FuncType* GetMethodType() const; const std::vector& GetGenericTypeParams() const; /** * @brief Retrieves the method's offset in vtable. * * @return The offset, greater than or equal to zero. */ size_t GetVirtualMethodOffset(CHIRBuilder* builder = nullptr) const; // ===--------------------------------------------------------------------===// // Instantiated Types // ===--------------------------------------------------------------------===// /** * @brief Retrieves base class's instantiated type, base class is the one in which method first appears. * * @return Base class's instantiated type. */ ClassType* GetInstSrcParentCustomTypeOfMethod(CHIRBuilder& builder) const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// /* * @brief Retrieves virtual method's attribute. * * @return Virtual method's attribute. */ AttributeInfo GetVirtualMethodAttr(CHIRBuilder& builder) const; protected: explicit DynamicDispatch(ExprKind kind, const InvokeCallContext& callContext, Block* parent); VirMethodContext virMethodCtx; private: std::vector GetVirtualMethodInfo(CHIRBuilder& builder) const; }; /** * @brief An expression for virtual instance member method call * * Cangjie Code: * interface I { func foo() {} } * class A <: I { public func foo() {} } * var x: I = A() * x.foo() // `foo` is a virtual instance member method, which means `foo` can be overrided by class A */ class Invoke : public DynamicDispatch { friend class ExprTypeConverter; friend class PrivateTypeConverterNoInvokeOriginal; friend class CHIRSerializer; friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Replace old operand in specified position. * * @param idx The sepcified position. * @param newOperand The destination value. */ void ReplaceOperand(size_t idx, Value* newOperand) final; /** * @brief Replace old operand with the new one, if there are many old operands in same expression, * then replace them all * * @param oldOperand The source value. * @param newOperand The destination value. */ void ReplaceOperand(Value* oldOperand, Value* newOperand) final; /** * @brief Retrieves the object of this Invoke operation. * Object's type doesn't always equal to ThisType, maybe we use an object from sub type to call * a virtual func from super type, then in order to func param type matched, * this object need to be casted to super type, but ThisType is still the sub type. * * @return The object of this Invoke operation. */ Value* GetObject() const; /** * @brief Retrieves the call arguments of this Invoke operation. * * @return A vector of pointers to the call arguments. */ std::vector GetArgs() const override; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; protected: ~Invoke() override = default; private: explicit Invoke(const InvokeCallContext& callContext, Block* parent); Invoke* Clone(CHIRBuilder& builder, Block& parent) const override; void UpdateThisType(); }; /** * @brief Get a value's runtime type info, operand's type is class& or This& * * Cangjie Code: * interface I { * func foo() { * goo() // `goo` may be overrided by sub class, we need to know `this`'s runtime type info * // so operand is `this`, added in func foo's param list by CHIR * } * static func goo() {} * } */ class GetRTTI : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// Value* GetOperand() const; using Expression::GetOperand; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: GetRTTI(Value* val, Block* parent); ~GetRTTI() override = default; GetRTTI* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief Get a type's runtime type info * * Cangjie Code: * interface I { static func goo() {} } * class A <: I { static func goo() {} } * func foo() where T <: I { * T.goo() // Get `T`'s runtime type info * } */ class GetRTTIStatic : public Expression { friend class ExprTypeConverter; friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// Type* GetRTTIType() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: GetRTTIStatic(Type* type, Block* parent); ~GetRTTIStatic() override = default; GetRTTIStatic* Clone(CHIRBuilder& builder, Block& parent) const override; Type* ty; }; /** * @brief If a static member method calling can trigger dynamic dispatch, we will use `InvokeStatic` to describe * * Cangjie Code: * interface I { static func goo() {} } * class A <: I { static func goo() {} } * func foo() where T <: I { * T.goo() // we don't know which `goo` is called in runtime, I.goo or A.goo * } */ class InvokeStatic : public DynamicDispatch { friend class ExprTypeConverter; friend class PrivateTypeConverterNoInvokeOriginal; friend class CHIRSerializer; friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves the call arguments of this Invoke operation. * * @return A vector of pointers to the call arguments. */ std::vector GetArgs() const override; /** * @brief Retrieves the RTTI value. * * @return The RTTI value. */ Value* GetRTTIValue() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; protected: ~InvokeStatic() override = default; private: explicit InvokeStatic(const InvokeCallContext& callContext, Block* parent); InvokeStatic* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief Cast a src type to target type. * * Cangjie Code: * var x: Int32 = 1 * var y: Int64 = Int64(x) // x is Int32, cast to Int64 */ class TypeCast : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** @brief Get the source value of this operation */ Value* GetSourceValue() const; /** @brief Get the source type of this operation */ Type* GetSourceTy() const; /** @brief Get the target type of this operation */ Type* GetTargetTy() const; /** @brief Get the overflow strategy of this cast operation */ OverflowStrategy GetOverflowStrategy() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; protected: ~TypeCast() override = default; private: explicit TypeCast(Value* operand, Block* parent); explicit TypeCast(Value* operand, Cangjie::OverflowStrategy overflow, Block* parent); TypeCast* Clone(CHIRBuilder& builder, Block& parent) const override; Cangjie::OverflowStrategy overflowStrategy{Cangjie::OverflowStrategy::NA}; }; /** * @brief Judge whether an object is specific type, we will get a result in runtime. * * Cangjie Code: * interface I {} * class A <: I {} * var x: I = A() * println(x is A) // judge whether `x`'s type is class `A` in runtime */ class InstanceOf : public Expression { friend class ExprTypeConverter; friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** @brief Get the object to test against */ Value* GetObject() const; /** @brief Get the tested type */ Type* GetType() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit InstanceOf(Value* operand, Type* ty, Block* parent); ~InstanceOf() override = default; InstanceOf* Clone(CHIRBuilder& builder, Block& parent) const override; Type* ty; }; /** * @brief Cast a value type to reference type. * value type includes: * 1. Int, UInt, Float, Rune, Bool, Unit, Nothing * 2. Enum, Struct * 3. Tuple, VArray, Func, CPointer, CString * reference type includes: * 1. Class&, Array& * 2. BoxType& * 3. This& */ class Box : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** @brief Get the source value of this operation */ Value* GetSourceValue() const; /** @brief Get the source type of this operation */ Type* GetSourceTy() const; /** @brief Get the target type of this operation */ Type* GetTargetTy() const; private: explicit Box(Value* operand, Block* parent); ~Box() override = default; Box* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief Cast a reference type to value type. * value type includes: * 1. Int, UInt, Float, Rune, Bool, Unit, Nothing * 2. Enum, Struct * 3. Tuple, VArray, Func, CPointer, CString * reference type includes: * 1. Class&, Array& * 2. BoxType& * 3. This& */ class UnBox : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** @brief Get the source value of this operation */ Value* GetSourceValue() const; /** @brief Get the source type of this operation */ Type* GetSourceTy() const; /** @brief Get the target type of this operation */ Type* GetTargetTy() const; private: explicit UnBox(Value* operand, Block* parent); ~UnBox() override = default; UnBox* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief Cast a concrete type to a generic related type. * 1. Bool -> T * 2. Class A& -> Class A& */ class TransformToGeneric : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** @brief Get the source value of this operation */ Value* GetSourceValue() const; /** @brief Get the source type of this operation */ Type* GetSourceTy() const; /** @brief Get the target type of this operation */ Type* GetTargetTy() const; private: explicit TransformToGeneric(Value* operand, Block* parent); ~TransformToGeneric() override = default; TransformToGeneric* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief Cast a generic related type to a concrete type. * 1. T -> Bool * 2. Class A& -> Class A& */ class TransformToConcrete : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** @brief Get the source value of this operation */ Value* GetSourceValue() const; /** @brief Get the source type of this operation */ Type* GetSourceTy() const; /** @brief Get the target type of this operation */ Type* GetTargetTy() const; private: explicit TransformToConcrete(Value* operand, Block* parent); ~TransformToConcrete() override = default; TransformToConcrete* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief Cast a reference type to a value ref type. * 1. Class I& -> Struct S& */ class UnBoxToRef : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** @brief Get the source value of this operation */ Value* GetSourceValue() const; /** @brief Get the source type of this operation */ Type* GetSourceTy() const; /** @brief Get the target type of this operation */ Type* GetTargetTy() const; private: explicit UnBoxToRef(Value* operand, Block* parent); ~UnBoxToRef() override = default; UnBoxToRef* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief Build up some values to a tuple. * * Cangjie Code: * var x = (1, 2, 3) // generate a tuple to describe */ class Tuple : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves all element values in tuple. */ std::vector GetElementValues() const; /** * @brief Retrieves all element types in tuple. */ std::vector GetElementTypes() const; private: explicit Tuple(const std::vector& values, Block* parent); ~Tuple() override = default; Tuple* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief Compute child element value from specific `Base` and `Indices`. * `Field` is different from `GetElementRef`, `Base` in `Field` is value type, * but `Location` in `GetElementRef` is reference type * * Cangjie Code: * var x = (1, 2, 3) * println(x[0]) // use `Field` to get first element value, `Base` is `x`, `Indices` is {0} */ class Field : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// Value* GetBase() const; std::vector GetPath() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit Field(Value* val, const std::vector& path, Block* parent); ~Field() override = default; Field* Clone(CHIRBuilder& builder, Block& parent) const override; std::vector path; }; /** * @brief Compute child element value from specific `Base` and `Name`. * `FieldByName` is different from `GetElementByName`, `Base` in `FieldByName` is value type, * but `Location` in `GetElementRef` is reference type * * Cangjie Code: * struct S { * let a = 1 * } * let x = S() * println(x.a) // use `FieldByName` to get first element value, `Base` is `x`, `Name` is {"a"} */ class FieldByName : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// Value* GetBase() const; const std::vector& GetNames() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit FieldByName(Value* val, const std::vector& names, Block* parent); ~FieldByName() override = default; FieldByName* Clone(CHIRBuilder& builder, Block& parent) const override; std::vector names; }; // ===--------------------------------------------------------------------===// // RawArray is an internal type used in struct Array of std.core // Its implementation of constructor and memory allocating is from CodeGen // So CHIR need to use specific expressions to describe // ===--------------------------------------------------------------------===// /** * @brief Allocate a memory to store array element value, * this expression is an internal implementation to generate Array * * Cangjie Code: * var x: Array = [1, 2, 3] // use `RawArrayAllocate` to get a memory to store `1, 2, 3` * CHIR: * %0: Int64 = Constant(3) * %1: RawArray& = RawArrayAllocate(Int32, %0) * %2: Int32 = Constant(1) * %3: Int32 = Constant(2) * %4: Int32 = Constant(3) * %5: Unit = RawArrayLiteralInit(%1, %2, %3, %4) */ class RawArrayAllocate : public Expression { friend class ExprTypeConverter; friend class TypeConverterForCC; friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves element quantity in Array. */ Value* GetSize() const; /** * @brief Retrieves element type in Array. */ Type* GetElementType() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit RawArrayAllocate(Type* eleTy, Value* size, Block* parent); ~RawArrayAllocate() override = default; RawArrayAllocate* Clone(CHIRBuilder& builder, Block& parent) const override; Type* elementType; // The element type. }; /** * @brief When Array is generated by `[]`, we will use `RawArrayLiteralInit` to initialize Array. * * Cangjie Code: * var x: Array = [1, 2, 3] // use `RawArrayLiteralInit` to initialize `[1, 2, 3]` * CHIR: * %0: Int64 = Constant(3) * %1: RawArray& = RawArrayAllocate(Int32, %0) * %2: Int32 = Constant(1) * %3: Int32 = Constant(2) * %4: Int32 = Constant(3) * %5: Unit = RawArrayLiteralInit(%1, %2, %3, %4) */ class RawArrayLiteralInit : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves address which allocated by `RawArrayAllocate`. */ Value* GetRawArray() const; /** * @brief Retrieves element quantity in Array. */ size_t GetSize() const; /** * @brief Retrieves all elements in Array. */ std::vector GetElements() const; private: explicit RawArrayLiteralInit(const Ptr raw, std::vector elements, Block* parent); ~RawArrayLiteralInit() override = default; RawArrayLiteralInit* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief In struct Array's member method `public init(size: Int64, repeat!: T)`, * we use `RawArray(size, repeat: repeat)` to initialize RawArray. * * Cangjie Code: * public init(size: Int64, repeat!: T) { * // we use `RawArrayInitByValue` to describe this initialization * // `size` stands for element quantity, `repeat` stands for element init value * this.rawptr = RawArray(size, repeat: repeat) * ... * } */ class RawArrayInitByValue : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves address which allocated by `RawArrayAllocate`. */ Value* GetRawArray() const; /** * @brief Retrieves element quantity in Array. */ Value* GetSize() const; /** * @brief Retrieves element init value in Array. */ Value* GetInitValue() const; private: explicit RawArrayInitByValue(Value* raw, Value* size, Value* initVal, Block* parent); ~RawArrayInitByValue() override = default; RawArrayInitByValue* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief Generate a VArray object by `[]`. * * Cangjie Code: * var x: VArray = [0, 1] */ class VArray : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves element quantity in VArray. */ int64_t GetSize() const; private: explicit VArray(std::vector elements, Block* parent); ~VArray() override = default; VArray* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief Generate a VArray object by item or lambda. * * Cangjie Code: * // size is 2, item is 3, initFunc is null * var x = VArray(repeat: 3) * * // size is 2, item is null, initFunc is {i => i} * var y = VArray({i => i}) */ class VArrayBuilder : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves element quantity in VArray. */ Value* GetSize() const; /** * @brief Retrieves element init value in VArray. */ Value* GetItem() const; /** * @brief Retrieves element init function in VArray. */ Value* GetInitFunc() const; private: explicit VArrayBuilder(Value* size, Value* item, Value* initFunc, Block* parent); enum class ElementIdx { SIZE_IDX = 0, ITEM_IDX = 1, INIT_FUNC_IDX = 2 }; ~VArrayBuilder() override = default; VArrayBuilder* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief Get exception object in runtime, this object is passed to intrinsic `beginCatch`. * * Cangjie Code: * try { * throw Exception() * } catch(_) {} // we will capture exception in catch condition */ class GetException : public Expression { friend class CHIRContext; friend class CHIRBuilder; private: explicit GetException(Block* parent); ~GetException() override = default; GetException* Clone(CHIRBuilder& builder, Block& parent) const override; }; struct IntrisicCallContext { IntrinsicKind kind{IntrinsicKind::NOT_INTRINSIC}; std::vector args; std::vector instTypeArgs; }; /** * @brief Generate intrinsic function calling. * All intrinsic kinds are listd in cangjie/CHIR/IntrinsicKind.h */ class Intrinsic : public Expression { friend class ExprTypeConverter; friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves the intrinsic kind. */ CHIR::IntrinsicKind GetIntrinsicKind() const; /** * @brief Retrieves the generic type information. * * @return A vector of pointers to the generic types. */ const std::vector& GetInstantiatedTypeArgs() const; /** * @brief Retrieves the arguments of the intrinsic operation. * * @return A vector of pointers to the arguments. */ const std::vector& GetArgs() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit Intrinsic(const IntrisicCallContext& callContext, Block* parent); ~Intrinsic() override = default; Intrinsic* Clone(CHIRBuilder& builder, Block& parent) const override; private: CHIR::IntrinsicKind intrinsicKind; std::vector instantiatedTypeArgs; }; /** * @brief A high-level description for `if` in Cangjie. * `If` expression will be desugared to `Branch` in the end. */ class If : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** @brief Get the condition of this If Expression */ Value* GetCondition() const; /** @brief Get the true branch of this If Expression */ BlockGroup* GetTrueBranch() const; /** @brief Get the false branch of this If Expression */ BlockGroup* GetFalseBranch() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit If(Value* cond, BlockGroup* thenBody, BlockGroup* elseBody, Block* parent); ~If() override = default; If* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief A high-level description for `while` and `do-while` in Cangjie. * `Loop` expression will be desugared to `Branch` + `GoTo` in the end. */ class Loop : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** @brief Get the loop body of this Loop Expression */ BlockGroup* GetLoopBody() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit Loop(BlockGroup* loopBody, Block* parent); ~Loop() override = default; Loop* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief A high-level description for `for-in` in Cangjie. * `ForIn` expression will be desugared to `Branch` + `GoTo` in the end. */ class ForIn : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** @brief Get the induction variable of this ForIn Expression */ Value* GetInductionVar() const; /** @brief Get the loop condition variable of this ForIn Expression */ Value* GetLoopCondVar() const; /** @brief Get the body block group of this ForIn Expression */ BlockGroup* GetBody() const; /** @brief Get the latch block group of this ForIn Expression */ BlockGroup* GetLatch() const; /** @brief Get the conditional block group of this ForIn Expression */ BlockGroup* GetCond() const; /** @brief Init the body, latch, and conditional block groups of this ForIn Expression */ void InitBlockGroups(BlockGroup& body, BlockGroup& latch, BlockGroup& cond); // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; /// Describes the execution order of sub block groups in a forin expression struct BGExecutionOrder { BGExecutionOrder(std::initializer_list bgs) { CJC_ASSERT(bgs.size() == BG_NUMBER); std::copy(bgs.begin(), bgs.end(), s); } BlockGroup* const * begin() const { return &s[0]; } BlockGroup* const * end() const { return s + BG_NUMBER; } private: constexpr static int BG_NUMBER{3}; BlockGroup* s[BG_NUMBER]; }; /// Get the execution order of a forin expression. The first block is the jump target from outer expressions. virtual BGExecutionOrder GetExecutionOrder() const = 0; protected: explicit ForIn(ExprKind kind, Value* inductionVar, Value* loopCondVar, Block* parent); ~ForIn() override = default; template T* CloneBase(CHIRBuilder& builder, Block& parent) const; }; /** * @brief An expression to describe `for-in`. * * Cangjie Code: * for (x in 0..10) {} // this pattern is translated to `ForInRange` */ class ForInRange : public ForIn { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// BGExecutionOrder GetExecutionOrder() const override; private: explicit ForInRange(Value* inductionVar, Value* loopCondVar, Block* parent) : ForIn(ExprKind::FORIN_RANGE, inductionVar, loopCondVar, parent) {} ~ForInRange() override = default; ForInRange* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief An expression to describe `for-in`. * * Cangjie Code: * let arr = [1, 2, 3] * for (x in arr) {} // this pattern is translated to `ForInIter`, * // `arr`'s `next()` will be called for visiting its element */ class ForInIter : public ForIn { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// BGExecutionOrder GetExecutionOrder() const override; private: explicit ForInIter(Value* inductionVar, Value* loopCondVar, Block* parent); ~ForInIter() override = default; ForInIter* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief An expression to describe `for-in`. * * Cangjie Code: * for (x in 0..=10) {} // this pattern is translated to `ForInClosedRange` */ class ForInClosedRange : public ForIn { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// BGExecutionOrder GetExecutionOrder() const override; private: explicit ForInClosedRange(Value* inductionVar, Value* loopCondVar, Block* parent); ~ForInClosedRange() override = default; ForInClosedRange* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief An expression to describe lambda and local function in Cangjie. * * Cangjie Code: * func foo() { * var x = { i => 1 } // this lambda is translated to `Lambda` * func goo() {} // this local function is translated to `Lambda` * } */ class Lambda : public Expression { friend class ExprTypeConverter; friend class BlockGroup; friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// bool IsCompileTimeValue() const; void SetCompileTimeValue(); /** * @brief Checks if the lambda expression is local. * * @return True if the lambda expression is local, false otherwise. */ bool IsLocalFunc() const; /** * @brief Retrieves the function type of the lambda expression. * * @return The function type of the lambda expression. */ FuncType* GetFuncType() const; /** * @brief Retrieves the identifier of the lambda expression. * * @return The identifier of the lambda expression. */ std::string GetIdentifier() const; /** * @brief Retrieves the identifier of the lambda expression without the prefix '@'. * * @return The identifier of the lambda expression without the prefix '@'. */ std::string GetIdentifierWithoutPrefix() const; /** * @brief Retrieves the source code identifier of the lambda expression. * * @return The source code identifier of the lambda expression. */ std::string GetSrcCodeIdentifier() const; /** * @brief Sets the host function for parameter default values. * * @param hostFunc The host function to set. */ void SetParamDftValHostFunc(Lambda& hostFunc); /** * @brief Retrieves the host function for parameter default values. * * @return The host function for parameter default values. */ Lambda* GetParamDftValHostFunc() const; // ===--------------------------------------------------------------------===// // Lambda Body // ===--------------------------------------------------------------------===// /** * @brief Retrieves the body of the lambda expression. * * @return The body of the lambda expression. */ BlockGroup* GetBody() const; /** * @brief Initializes the body of the lambda expression. * * @param newBody The new body to initialize. */ void InitBody(BlockGroup& newBody); /** * @brief Retrieves the entry block of the lambda expression. * * @return The entry block of the lambda expression. */ Block* GetEntryBlock() const; /** * @brief Adds a parameter to the lambda expression. * * @param arg The parameter to add. */ void AddParam(Parameter& arg); /** * @brief Retrieves the number of parameters in the lambda expression. * * @return The number of parameters in the lambda expression. */ size_t GetNumOfParams() const; /** * @brief Retrieves a parameter at a specific index. * * @param index The index of the parameter. * @return The parameter at the specified index. */ Parameter* GetParam(size_t index) const; /** * @brief Retrieves all parameters of the lambda expression. * * @return A vector of pointers to the parameters. */ const std::vector& GetParams() const; /** * @brief Retrieves the generic type parameters of the lambda expression. * * @return A vector of pointers to the generic type parameters. */ const std::vector& GetGenericTypeParams() const; /** * @brief Sets the return value of the lambda expression. * * @param ret The return value to set. */ void SetReturnValue(LocalVar& ret); /** * @brief Retrieves the return value of the lambda expression. * * @return A pointer to the return value. */ LocalVar* GetReturnValue() const; /** * @brief Retrieves the return type of the lambda expression. * * @return The return type of the lambda expression. */ Type* GetReturnType() const; // ===--------------------------------------------------------------------===// // Modify Self // ===--------------------------------------------------------------------===// /** * @brief Removes the lambda expression from its block. */ void RemoveSelfFromBlock() override; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// /** * @brief Retrieves captured variables of the lambda expression. * These variables include mutable and immutable variables that declared in local scope, * so they must be local variables or parameters, not be global variables. * Of course, if there is another lambda calling, we will think that lambda is captured in current lambda * * @return A vector of pointers to the captured variables. */ std::vector GetCapturedVariables() const; std::string ToString(size_t indent = 0) const override; private: explicit Lambda(FuncType* ty, Block* parent, bool isLocalFunc = false, const std::string& identifier = "", const std::string& srcCodeIdentifier = "", const std::vector& genericTypeParams = {}); ~Lambda() override { funcTy = nullptr; }; /** * @brief Removes the body of the lambda expression. */ void RemoveBody(); Lambda* Clone(CHIRBuilder& builder, Block& parent) const override; private: std::string identifier; // the mangledName of nested function or lambda. std::string srcCodeIdentifier; // the name of nested function or lambda. FuncBody body; FuncType* funcTy; bool isLocalFunc{false}; std::vector genericTypeParams; Lambda* paramDftValHostFunc = nullptr; bool isCompileTimeValue = false; }; /** * @brief Generate debug information for cjdb. */ class Debug : public Expression { friend class CHIRSerializer; friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief obtains the identifier of the corresponding Cangjie source code. */ std::string GetSrcCodeIdentifier() const; Value* GetValue() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit Debug(Value* local, std::string srcCodeIdentifier, Block* parent); ~Debug() override = default; Debug* Clone(CHIRBuilder& builder, Block& parent) const override; std::string srcCodeIdentifier; }; /** * @brief An expression to describe `spawn` in Cangjie. * * Cangjie Code: * let x = spawn(arg) { * return 1 * } * * Will be desugared by CHIR: * %0: Class-Future& = Allocate(Class-Future) * %1: ()->Int64 = Lambda[mangled name]()=> { * return 1 * } * %2: Void = Apply(Future.init, %0, %1) * %3: ArgType = xxx * %4: Class-Future& = Spawn(%0, %3) * Then, %0 is Future object, %3 is spawn argument, spawn only has one argument or not * * After redundant Future removing optimization, operands in `Spawn` will be changed: * %1: ()->Int64 = Lambda[mangled name]()=> { * return 1 * } * %3: ArgType = xxx * %4: Class-Future& = Spawn(%1, %3) * Then, %1 is closure */ class Spawn : public Expression { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Get the spawn argument. * * @return nullptr if no argument. */ Value* GetSpawnArg() const; bool IsExecuteClosure() const; void SetExecuteClosure(FuncBase& func); // ===--------------------------------------------------------------------===// // Before Optimization // ===--------------------------------------------------------------------===// /** @brief Get the Future object.*/ Value* GetFuture() const; // ===--------------------------------------------------------------------===// // After Optimization // ===--------------------------------------------------------------------===// /** @brief Get the closure object.*/ Value* GetClosure() const; /** * @brief Get the member method `executeClosure` in class Future. */ FuncBase* GetExecuteClosure() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit Spawn(Value* val, Block* parent); explicit Spawn(Value* val, Value* arg, Block* parent); ~Spawn() override = default; Spawn* Clone(CHIRBuilder& builder, Block& parent) const override; private: /** * @brief After optimization, backend will use `executeClosure` to create new thread, not `Future` object. * `executeClosure` is member method in class `Future` which is declared in std.core */ FuncBase* executeClosure{nullptr}; }; /** * @brief Store instantiated types. * * Cangjie Code: * func foo() {} * let x = foo // we need to store `Bool` for `x`, otherwise, * // we won't know which instantiated type args are used in next line * x() */ class GetInstantiateValue : public Expression { friend class ExprTypeConverter; friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// std::vector GetInstantiateTypes() const; Value* GetGenericResult() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit GetInstantiateValue(Value* val, std::vector insTypes, Block* parent); ~GetInstantiateValue() override = default; GetInstantiateValue* Clone(CHIRBuilder& builder, Block& parent) const override; std::vector instantiateTys; }; } // namespace Cangjie::CHIR #endif // CANGJIE_CHIR_EXPRESSION_H cangjie_compiler-1.0.7/include/cangjie/CHIR/Expression/ExpressionWrapper.h000066400000000000000000000123521510705540100265720ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * Many Expressions have their xxxWithException version, we wrap them with a new class instead of using a base class */ #ifndef CANGJIE_CHIR_EXPRESSION_WRAPPER_H #define CANGJIE_CHIR_EXPRESSION_WRAPPER_H #include "cangjie/CHIR/Expression/Terminator.h" namespace Cangjie { namespace CHIR { class ExpressionBase { public: const Expression* GetRawExpr() const; LocalVar* GetResult() const; std::vector GetOperands() const; protected: explicit ExpressionBase(const Expression* e); private: const Expression* expr; }; class FuncCallBase : public ExpressionBase { public: explicit FuncCallBase(const Expression* e); explicit FuncCallBase(const FuncCall* expr); explicit FuncCallBase(const FuncCallWithException* exprE); std::vector GetArgs() const; Type* GetThisType() const; std::vector GetInstantiatedTypeArgs() const; private: const FuncCall* expr; const FuncCallWithException* exprE; }; class ApplyBase : public FuncCallBase { public: explicit ApplyBase(const Expression* e); explicit ApplyBase(const Apply* expr); explicit ApplyBase(const ApplyWithException* exprE); Value* GetCallee() const; Type* GetInstParentCustomTyOfCallee(CHIRBuilder& builder) const; private: const Apply* expr; const ApplyWithException* exprE; }; class DynamicDispatchBase : public FuncCallBase { public: explicit DynamicDispatchBase(const Expression* e); explicit DynamicDispatchBase(const DynamicDispatch* expr); explicit DynamicDispatchBase(const DynamicDispatchWithException* exprE); std::vector GetGenericTypeParams() const; std::string GetMethodName() const; FuncType* GetMethodType() const; size_t GetVirtualMethodOffset() const; ClassType* GetInstSrcParentCustomTypeOfMethod(CHIRBuilder& builder) const; private: const DynamicDispatch* expr; const DynamicDispatchWithException* exprE; }; class InvokeBase : public DynamicDispatchBase { public: explicit InvokeBase(const Expression* e); explicit InvokeBase(const Invoke* expr); explicit InvokeBase(const InvokeWithException* exprE); Value* GetObject() const; private: const Invoke* expr; const InvokeWithException* exprE; }; class InvokeStaticBase : public DynamicDispatchBase { public: explicit InvokeStaticBase(const Expression* e); explicit InvokeStaticBase(const InvokeStatic* expr); explicit InvokeStaticBase(const InvokeStaticWithException* exprE); Value* GetRTTIValue() const; private: const InvokeStatic* expr; const InvokeStaticWithException* exprE; }; class UnaryExprBase : public ExpressionBase { public: explicit UnaryExprBase(const Expression* e); explicit UnaryExprBase(const UnaryExpression* expr); explicit UnaryExprBase(const IntOpWithException* exprE); ExprKind GetOpKind() const; std::string GetExprKindName() const; Value* GetOperand() const; OverflowStrategy GetOverflowStrategy() const; private: const UnaryExpression* expr; const IntOpWithException* exprE; }; class BinaryExprBase : public ExpressionBase { public: explicit BinaryExprBase(const Expression* e); explicit BinaryExprBase(const BinaryExpression* expr); explicit BinaryExprBase(const IntOpWithException* exprE); ExprKind GetOpKind() const; std::string GetExprKindName() const; Value* GetLHSOperand() const; Value* GetRHSOperand() const; OverflowStrategy GetOverflowStrategy() const; private: const BinaryExpression* expr; const IntOpWithException* exprE; }; class SpawnBase : public ExpressionBase { public: explicit SpawnBase(const Expression* e); explicit SpawnBase(const Spawn* expr); explicit SpawnBase(const SpawnWithException* exprE); Value* GetObject() const; bool IsExecuteClosure() const; private: const Spawn* expr; const SpawnWithException* exprE; }; class IntrinsicBase : public ExpressionBase { public: explicit IntrinsicBase(const Expression* e); explicit IntrinsicBase(const Intrinsic* expr); explicit IntrinsicBase(const IntrinsicWithException* exprE); IntrinsicKind GetIntrinsicKind() const; std::vector GetInstantiatedTypeArgs() const; private: const Intrinsic* expr; const IntrinsicWithException* exprE; }; class AllocateBase : public ExpressionBase { public: explicit AllocateBase(const Expression* e); explicit AllocateBase(const Allocate* expr); explicit AllocateBase(const AllocateWithException* exprE); Type* GetType() const; private: const Allocate* expr; const AllocateWithException* exprE; }; class RawArrayAllocateBase : public ExpressionBase { public: explicit RawArrayAllocateBase(const Expression* e); explicit RawArrayAllocateBase(const RawArrayAllocate* expr); explicit RawArrayAllocateBase(const RawArrayAllocateWithException* exprE); Type* GetElementType() const; Value* GetSize() const; private: const RawArrayAllocate* expr; const RawArrayAllocateWithException* exprE; }; } } #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Expression/Terminator.h000066400000000000000000000652351510705540100252260ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares all terminators of CHIR, including Terminator itself and its sub class */ #ifndef CANGJIE_CHIR_TERMINATOR_H #define CANGJIE_CHIR_TERMINATOR_H #include "cangjie/CHIR/Expression/Expression.h" namespace Cangjie::CHIR { class CHIRBuilder; class ExprTypeConverter; enum class SourceExpr : uint8_t { IF_EXPR, WHILE_EXPR, DO_WHILE_EXPR, MATCH_EXPR, IF_LET_OR_WHILE_LET, QUEST, BINARY, FOR_IN_EXPR, OTHER }; /** * @brief Terminator class in CHIR. * Terminator is also an expression, but it can only be at the end of one block. */ class Terminator : public Expression { friend class Block; public: // ===--------------------------------------------------------------------===// // Operand // ===--------------------------------------------------------------------===// std::vector GetOperands() const override; Value* GetOperand(size_t idx) const override; size_t GetNumOfOperands() const override; // ===--------------------------------------------------------------------===// // Successor // ===--------------------------------------------------------------------===// size_t GetNumOfSuccessor() const; Block* GetSuccessor(size_t index) const; /** * @brief Replace `oldSuccessor` with `newSuccessor` * * @param oldSuccessor: one of the current successors * @param newSuccessor: new successor */ void ReplaceSuccessor(Block& oldSuccessor, Block& newSuccessor); /** * @brief Replaced the successor of this terminator * * @param index: the index-th successor need to be replaced * @param newSuccessor: new successor */ void ReplaceSuccessor(size_t index, Block& newSuccessor); const std::vector GetSuccessors() const; // ===--------------------------------------------------------------------===// // Modify Self // ===--------------------------------------------------------------------===// void ReplaceWith(Expression& newTerminator) override; /** * @brief Break all connection with its parent and operands * that means you can not get its parent and operands any more, and you can not get this terminator by its parent * and its operands, too. But we don't free this terminator's memory. */ void RemoveSelfFromBlock() override; protected: explicit Terminator( ExprKind kind, const std::vector& operands, const std::vector& successors, Block* parent); ~Terminator() override = default; void AppendSuccessor(Block& block); /** * @brief Get first successor's index in operands. * * in another sense, return the num of operand(does not include successors). * * NOTE: because returned value is constant, * call this function only after Terminator's full initialization(operand and successor are properly set). */ size_t GetFirstSuccessorIndex() const; private: void LetSuccessorsRemoveCurBlock() const; }; /** * @brief Jump from current block to another one */ class GoTo : public Terminator { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// Block* GetDestination() const; private: explicit GoTo(Block* destBlock, Block* parent); ~GoTo() override = default; GoTo* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief `if`, `for-in`, `while`, `do-while` and `match` can be translated to `Branch` */ class Branch : public Terminator { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** @brief Get the condition of this Branch Expression */ Value* GetCondition() const; /** @brief Get the true block of this Branch Expression */ Block* GetTrueBlock() const; /** @brief Get the false block of this Branch Expression */ Block* GetFalseBlock() const; /** @brief Set the source expr, mark where this branch is from */ void SetSourceExpr(SourceExpr srcExpr); /** @brief Get the source expr */ SourceExpr GetSourceExpr() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit Branch(Value* cond, Block* trueBlock, Block* falseBlock, Block* parent); ~Branch() override = default; SourceExpr sourceExpr{SourceExpr::OTHER}; Branch* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief `match` can be translated to `MultiBranch` in O2 */ class MultiBranch : public Terminator { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** @brief Get the condition of this MultiBranch Expression */ Value* GetCondition() const; /** @brief Get the case values of this MultiBranch Expression */ const std::vector& GetCaseVals() const; /** @brief Get the case value by index of this MultiBranch Expression */ uint64_t GetCaseValByIndex(size_t index) const; /** @brief Get the case block by index of this MultiBranch Expression */ Block* GetCaseBlockByIndex(size_t index) const; /** @brief Get the default block of this MultiBranch Expression */ Block* GetDefaultBlock() const; std::vector GetNormalBlocks() const; private: explicit MultiBranch(Value* cond, Block* defaultBlock, const std::vector& vals, const std::vector& succs, Block* parent); ~MultiBranch() override = default; std::string ToString(size_t indent = 0) const override; MultiBranch* Clone(CHIRBuilder& builder, Block& parent) const override; /** * @brief The specific case values used to match. * Note that default Block does not have the case val. */ std::vector caseVals; }; /** * @brief Exit current function. */ class Exit : public Terminator { friend class CHIRContext; friend class CHIRBuilder; private: explicit Exit(Block* parent); ~Exit() override = default; Exit* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief Throw an exception. */ class RaiseException : public Terminator { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** @brief Get the exception value of this RaiseException Expression */ Value* GetExceptionValue() const; /** * @brief Get the exception block of this RaiseException Expression. * * Return exception block if exist, * nullptr, otherwise. */ Block* GetExceptionBlock() const; private: explicit RaiseException(Value* value, Block* parent); explicit RaiseException(Value* value, Block* successor, Block* parent); ~RaiseException() override = default; RaiseException* Clone(CHIRBuilder& builder, Block& parent) const override; }; class ExpressionWithException : public Terminator { public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves the success block. * * @return The success block. */ Block* GetSuccessBlock() const; /** * @brief Retrieves the error block. * * @return The error block. */ Block* GetErrorBlock() const; protected: explicit ExpressionWithException(ExprKind kind, Block* parent); explicit ExpressionWithException( ExprKind kind, const std::vector& operands, const std::vector& successors, Block* parent); }; /** * @brief An expression for function call, including * ApplyWithException, InvokeWithException and InvokeStaticWithException */ class FuncCallWithException : public ExpressionWithException { friend class ExprTypeConverter; friend class CHIRSerializer; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves a list of the application argument nodes. * * @return A vector of pointers to the application argument nodes. */ virtual std::vector GetArgs() const = 0; /** * @brief Retrieves the type of 'this'. * * @return The type of 'this'. */ Type* GetThisType() const; /** * @brief Sets the type of 'this'. * * @param type The type of 'this'. */ void SetThisType(Type* type); /** * @brief Retrieves the instantiated argument types. * * @return A vector of pointers to the instantiated argument types. */ const std::vector& GetInstantiatedTypeArgs() const; protected: explicit FuncCallWithException(ExprKind kind, const FuncCallContext& funcCallCtx, Block* parent); /** * @brief Record instantiated type args. * Cangjie Code: * func foo() {} * foo() // {Bool, Int32} is current Apply's instantiated type args */ std::vector instantiatedTypeArgs; /** * @brief Record object type if callee is member method. * 1. foo(), global func, `thisType` is nullptr * 2. obj.foo(), instance member method, `thisType` is obj's type * 3. A.foo(), static member method, `thisType` is A * 4. method(), a member method with implicit `this`, `thisType` is parent custom type * * As for global func and instance member method, we can compute `thisType` from callee's args, * but for static member method, we can't. */ Type* thisType{nullptr}; }; /** * @brief `Apply` expression wroten in `try` block */ class ApplyWithException : public FuncCallWithException { friend class ExprTypeConverter; friend class TypeConverterForCC; friend class CHIRSerializer; friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves the callee of this ApplyWithException operation. * * @return The callee of this ApplyWithException operation. */ Value* GetCallee() const; /** * @brief Retrieves a list of the ApplyWithException operation argument nodes. * * @return A vector of pointers to the argument nodes. */ std::vector GetArgs() const override; // ===--------------------------------------------------------------------===// // Instantiated Types // ===--------------------------------------------------------------------===// /** * @brief Retrieves the instantiated parent custom type of the callee. * * @return The instantiated parent custom type of the callee. */ Type* GetInstParentCustomTyOfCallee(CHIRBuilder& builder) const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit ApplyWithException( Value* callee, const FuncCallContext& callContext, Block* sucBlock, Block* errBlock, Block* parent); ~ApplyWithException() override = default; ApplyWithException* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief An expression for virtual function call, including Invoke and InvokeStatic */ class DynamicDispatchWithException : public FuncCallWithException { friend class ExprTypeConverter; friend class PrivateTypeConverterNoInvokeOriginal; friend class CHIRSerializer; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves the method name of this Invoke operation. * * @return The method name of this Invoke operation. */ const std::string& GetMethodName() const; /** * @brief Retrieves the method type of this Invoke operation. * * @return The method type of this Invoke operation. */ FuncType* GetMethodType() const; const std::vector& GetGenericTypeParams() const; /** * @brief Retrieves the method's offset in vtable. * * @return The offset, greater than or equal to zero. */ size_t GetVirtualMethodOffset(CHIRBuilder* builder = nullptr) const; // ===--------------------------------------------------------------------===// // Instantiated Types // ===--------------------------------------------------------------------===// /** * @brief Retrieves base class's instantiated type, base class is the one in which method first appears. * * @return Base class's instantiated type. */ ClassType* GetInstSrcParentCustomTypeOfMethod(CHIRBuilder& builder) const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// /* * @brief Retrieves virtual method's attribute. * * @return Virtual method's attribute. */ AttributeInfo GetVirtualMethodAttr(CHIRBuilder& builder) const; protected: explicit DynamicDispatchWithException( ExprKind kind, const InvokeCallContext& callContext, Block* sucBlock, Block* errBlock, Block* parent); VirMethodContext virMethodCtx; private: std::vector GetVirtualMethodInfo(CHIRBuilder& builder) const; }; /** * @brief `Invoke` expression wroten in `try` block */ class InvokeWithException : public DynamicDispatchWithException { friend class ExprTypeConverter; friend class TypeConverterForCC; friend class PrivateTypeConverterNoInvokeOriginal; friend class CHIRSerializer; friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves the object of this InvokeWithException operation. * * @return The object of this InvokeWithException operation. */ Value* GetObject() const; /** * @brief Retrieves the call arguments of this InvokeWithException operation. * * @return A vector of pointers to the call arguments. */ std::vector GetArgs() const override; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit InvokeWithException( const InvokeCallContext& callContext, Block* sucBlock, Block* errBlock, Block* parent); ~InvokeWithException() override = default; InvokeWithException* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief `InvokeStatic` expression wroten in `try` block */ class InvokeStaticWithException : public DynamicDispatchWithException { friend class ExprTypeConverter; friend class TypeConverterForCC; friend class PrivateTypeConverterNoInvokeOriginal; friend class CHIRSerializer; friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves the RTTI value. * * @return The RTTI value. */ Value* GetRTTIValue() const; /** * @brief Retrieves the call arguments of this InvokeStaticWithException operation. * * @return A vector of pointers to the call arguments. */ std::vector GetArgs() const override; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit InvokeStaticWithException( const InvokeCallContext& callContext, Block* sucBlock, Block* errBlock, Block* parent); ~InvokeStaticWithException() override = default; InvokeStaticWithException* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief `Unary` or `Binary` expression wroten in `try` block */ class IntOpWithException : public ExpressionWithException { friend class CHIRDeserializer; friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves the operation kind. * * @return The operation kind. */ ExprKind GetOpKind() const; /** * @brief Retrieves the operation kind name. * * @return The operation kind name. */ std::string GetOpKindName() const; /** * @brief Retrieves the left-hand side operand. * * @return The left-hand side operand. */ Value* GetLHSOperand() const; /** * @brief Retrieves the right-hand side operand. * * @return The right-hand side operand. */ Value* GetRHSOperand() const; /** * @brief Retrieves the overflow strategy. * * @return The overflow strategy. */ OverflowStrategy GetOverflowStrategy() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit IntOpWithException(ExprKind unaryKind, Value* operand, Block* normal, Block* exception, Block* parent); explicit IntOpWithException( ExprKind binaryKind, Value* lhs, Value* rhs, Block* normal, Block* exception, Block* parent); ~IntOpWithException() override = default; IntOpWithException* Clone(CHIRBuilder& builder, Block& parent) const override; ExprKind opKind; // Operator Kind Cangjie::OverflowStrategy overflowStrategy{Cangjie::OverflowStrategy::NA}; }; /** * @brief `TypeCast` expression wroten in `try` block */ class TypeCastWithException : public ExpressionWithException { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves the overflow strategy. * * @return The overflow strategy. */ OverflowStrategy GetOverflowStrategy() const; /** * @brief Retrieves the source value of this cast operation. * * @return The source value of this cast operation. */ Value* GetSourceValue() const; /** * @brief Retrieves the source type of this cast operation. * * @return The source type of this cast operation. */ Type* GetSourceTy() const; /** * @brief Retrieves the target type of this cast operation. * * @return The target type of this cast operation. */ Type* GetTargetTy() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit TypeCastWithException(Value* operand, Block* normal, Block* exception, Block* parent); ~TypeCastWithException() override = default; TypeCastWithException* Clone(CHIRBuilder& builder, Block& parent) const override; }; /** * @brief `Intrinsic` expression wroten in `try` block */ class IntrinsicWithException : public ExpressionWithException { friend class ExprTypeConverter; friend class TypeConverterForCC; friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Retrieves the intrinsic kind. * * @return The intrinsic kind. */ CHIR::IntrinsicKind GetIntrinsicKind() const; /** * @brief Retrieves the generic type information. * * @return A vector of pointers to the generic types. */ const std::vector& GetInstantiatedTypeArgs() const; /** * @brief Retrieves the arguments of the intrinsic operation. * * @return A vector of pointers to the arguments. */ const std::vector GetArgs() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit IntrinsicWithException( const IntrisicCallContext& callContext, Block* normal, Block* exception, Block* parent); ~IntrinsicWithException() override = default; IntrinsicWithException* Clone(CHIRBuilder& builder, Block& parent) const override; CHIR::IntrinsicKind intrinsicKind; std::vector instantiatedTypeArgs; }; /** * @brief `Allocate` expression wroten in `try` block */ class AllocateWithException : public ExpressionWithException { friend class ExprTypeConverter; friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// Type* GetType() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit AllocateWithException(Type* ty, Block* normal, Block* exception, Block* parent); ~AllocateWithException() override = default; AllocateWithException* Clone(CHIRBuilder& builder, Block& parent) const override; /** @brief The type to be allocated. */ Type* ty; }; /** * @brief `RawArrayAllocate` expression wroten in `try` block */ class RawArrayAllocateWithException : public ExpressionWithException { friend class ExprTypeConverter; friend class TypeConverterForCC; friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// Value* GetSize() const; Type* GetElementType() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit RawArrayAllocateWithException(Type* eleTy, Value* size, Block* normal, Block* exception, Block* parent); ~RawArrayAllocateWithException() override = default; RawArrayAllocateWithException* Clone(CHIRBuilder& builder, Block& parent) const override; Type* elementType; // The element type. }; /** * @brief `Spawn` expression wroten in `try` block */ class SpawnWithException : public ExpressionWithException { friend class CHIRContext; friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// /** * @brief Get the spawn argument. * * @return nullptr if no argument. */ Value* GetSpawnArg() const; bool IsExecuteClosure() const; void SetExecuteClosure(FuncBase& func); // ===--------------------------------------------------------------------===// // Before Optimization // ===--------------------------------------------------------------------===// /** @brief Get the future argument for execute.*/ Value* GetFuture() const; // ===--------------------------------------------------------------------===// // After Optimization // ===--------------------------------------------------------------------===// /** @brief Get the closure argument for execute closure.*/ Value* GetClosure() const; /** * @brief Get the FuncBase* of execute closure. * * @return nullptr if not exist. */ FuncBase* GetExecuteClosure() const; // ===--------------------------------------------------------------------===// // Others // ===--------------------------------------------------------------------===// std::string ToString(size_t indent = 0) const override; private: explicit SpawnWithException( Value* val, Value* arg, Block* normal, Block* exception, Block* parent); explicit SpawnWithException( Value* val, Block* normal, Block* exception, Block* parent); ~SpawnWithException() override = default; SpawnWithException* Clone(CHIRBuilder& builder, Block& parent) const override; /** * @brief After optimization, backend will use `executeClosure` to create new thread, not `Future` object. * `executeClosure` is member method in class `Future` which is declared in std.core */ FuncBase* executeClosure{nullptr}; }; } // namespace Cangjie::CHIR #endif // CANGJIE_CHIR_EXPRESSION_H cangjie_compiler-1.0.7/include/cangjie/CHIR/GenerateVTable/000077500000000000000000000000001510705540100234075ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/CHIR/GenerateVTable/GenerateVTable.h000066400000000000000000000037271510705540100264210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file generate VTable in CHIR and update ir */ #ifndef CANGJIE_CHIR_GENERATE_VTABLE_H #define CANGJIE_CHIR_GENERATE_VTABLE_H #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/IncrementalCompilation/IncrementalScopeAnalysis.h" #include "cangjie/Option/Option.h" namespace Cangjie { namespace CHIR { class GenerateVTable { public: GenerateVTable(Package& pkg, CHIRBuilder& b, const GlobalOptions& opts); /** * @brief Create VTable. */ void CreateVTable(); /** * @brief Update VTable for operator func. */ void UpdateOperatorVirFunc(); /** * @brief Create wrapper func for virtual method. * * @param kind The result of incremental compilation. * @param increCachedInfo Cache info of incremental compilation. * @param curVirtFuncWrapDep Dependency info, used by incremental compilation. * @param delVirtFuncWrapForIncr Dependency info, used by incremental compilation. */ void CreateVirtualFuncWrapper(const IncreKind& kind, const CompilationCache& increCachedInfo, VirtualWrapperDepMap& curVirtFuncWrapDep, VirtualWrapperDepMap& delVirtFuncWrapForIncr); /** * @brief Create wrapper func for mut method. */ void CreateMutFuncWrapper(); /** * @brief Update Invoke and InvokeStatic after computing virtual func offset. */ void UpdateFuncCall(); private: FuncBase* GetMutFuncWrapper(const Type& thisType, const std::vector& args, const std::vector& instTypeArgs, Type& retType, const FuncBase& callee); Package& package; CHIRBuilder& builder; const GlobalOptions& opts; std::unordered_map mutFuncWrappers; }; } } #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/GenerateVTable/UpdateOperatorVTable.h000066400000000000000000000043471510705540100276240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_UPDATE_OPERATOR_VTABLE_H #define CANGJIE_CHIR_UPDATE_OPERATOR_VTABLE_H #include #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/UserDefinedType.h" #include "cangjie/Utils/ConstantsUtils.h" namespace Cangjie::CHIR { /// Integer operators that can be affected by overflow strategy, given a specific set of argument types, are called /// overflow operator. After collecting extend and vtable info, split overflow operator in vtable into three /// versions if that operator func (of an interface) can be extended by integer types. class UpdateOperatorVTable { public: UpdateOperatorVTable(const Package& package, CHIRBuilder& builder); /** * @brief update vtable */ void Update(); private: using OverflowOpIndex = size_t; struct RewriteVtableInfo { std::set ov; }; void CollectOverflowOperators(); void CollectOverflowOperatorsOnInterface(ClassDef& def); void AddRewriteInfo(ClassDef& def, size_t index); void RewriteVtable(); void RewriteOneVtableEntry(ClassType& infType, CustomTypeDef& user, const VirtualFuncInfo& funcInfo, size_t index); Func* GenerateBuiltinOverflowOperatorFunc( const std::string& name, OverflowStrategy ovf, const ExtendDef& user, bool isBinary); void RewriteVtableEntryRec(const ClassDef& inf, CustomTypeDef& user, const RewriteVtableInfo& info); void CollectVTableUsers(); private: const Package& package; CHIRBuilder& builder; // order ClassDef* by mangled name to keep binary equality struct RewriteInfoOrdering { bool operator()(ClassDef* one, ClassDef* another) const; }; std::map interRewriteInfo; std::unordered_map cache; // parent vtable, sub vtables std::unordered_map> vtableUsers; }; } #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/GenerateVTable/VTableGenerator.h000066400000000000000000000045211510705540100266060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_VTABLE_CREATOR_H #define CANGJIE_CHIR_VTABLE_CREATOR_H #include #include "cangjie/CHIR/UserDefinedType.h" #include "cangjie/CHIR/Type/CustomTypeDef.h" namespace Cangjie::CHIR { class VTableGenerator { public: explicit VTableGenerator(CHIRBuilder& builder); /** * @brief generate vtable for CustomTypeDef * * @param customTypeDef generate and set this def's vtable */ void GenerateVTable(CustomTypeDef& customTypeDef); private: void MergeVtable(ClassType& instParentTy, VTableType& vtable); void CollectCurDefMethodsMayBeInVtable(const CustomTypeDef& def, std::vector& publicFuncs); std::vector GetAllMethods(const CustomTypeDef& def); std::vector GetAllMethods(const Type& ty); VirtualFuncInfo CreateVirtualFuncInfo(const AbstractMethodInfo& method, Type& originalParentType, const std::unordered_map& replaceTable); VirtualFuncInfo CreateVirtualFuncInfo( FuncBase& method, Type& originalParentType, const std::unordered_map& replaceTable); bool UpdateVtable(VirtualFuncInfo& curFuncInfo, VTableType& vtable); bool IsSigTypeMatched(const VirtualFuncInfo& curFuncInfo, const VirtualFuncInfo& funcInfoInVtable); bool VirtualFuncShouldAddToVTableInItsOwnParent(ClassType& ownParent, ClassType& alreadyIn); void UpdateAbstractMethodInVtable(VTableType& vtable); void UpdateAbstractMethodWithImplementedMethod( VTableType& vtable, const ClassType& curParentTy, VirtualFuncInfo& abstractFuncInfo); std::vector CollectAllPublicAndProtectedMethods(const CustomTypeDef& curDef); std::unordered_map GetInstMapFromDefIncludeParents( const CustomTypeDef& def, const Type& curType); std::vector CollectMethodsIncludeParentsMayBeInVtable(const CustomTypeDef& curDef); void CollectMethodsFromAncestorInterfaceMayBeInVTable( const CustomTypeDef& curDef, std::vector& methods); private: CHIRBuilder& builder; }; } #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/GenerateVTable/WrapMutFunc.h000066400000000000000000000021371510705540100257760ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file generate mut func wrapper */ #ifndef CANGJIE_CHIR_WRAP_MUT_FUNC_H #define CANGJIE_CHIR_WRAP_MUT_FUNC_H #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/Type/ExtendDef.h" namespace Cangjie { namespace CHIR { class WrapMutFunc { public: WrapMutFunc(CHIRBuilder& b); /** * @brief Create wrapper func for mut method. * * @param customTypeDef Visit all mut methods in this CustomTypeDef. */ void Run(CustomTypeDef& customTypeDef); /** * @brief Return cache info, map. */ std::unordered_map&& GetWrappers(); private: void CreateMutFuncWrapper(FuncBase* rawFunc, CustomTypeDef& curDef, ClassType& srcClassTy); CHIRBuilder& builder; std::unordered_map wrapperFuncs; }; } } #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/GenerateVTable/WrapVirtualFunc.h000066400000000000000000000047211510705540100266600ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_WRAP_VIRTUAL_FUNC_H #define CANGJIE_CHIR_WRAP_VIRTUAL_FUNC_H #include #include "cangjie/IncrementalCompilation/CompilationCache.h" #include "cangjie/IncrementalCompilation/IncrementalScopeAnalysis.h" #include "cangjie/CHIR/UserDefinedType.h" #include "cangjie/CHIR/Type/CustomTypeDef.h" namespace Cangjie::CHIR { class WrapVirtualFunc { public: WrapVirtualFunc(CHIRBuilder& builder, const CompilationCache& increCachedInfo, const IncreKind incrementalKind, const bool targetIsWin); /** * @brief wrap virtual function * * @param customTypeDef wrap virtual function in this def's vtable */ void CheckAndWrap(CustomTypeDef& customTypeDef); VirtualWrapperDepMap&& GetCurVirtFuncWrapDep(); VirtualWrapperDepMap&& GetDelVirtFuncWrapForIncr(); private: struct WrapperFuncGenericTable { std::vector funcGenericTypeParams; std::unordered_map replaceTable; std::unordered_map inverseReplaceTable; }; FuncBase* CreateVirtualWrapperIfNeeded(const VirtualFuncInfo& funcInfo, const VirtualFuncInfo& parentFuncInfo, Type& selfTy, CustomTypeDef& customTypeDef, const ClassType& parentTy); void CreateVirtualWrapperFunc(Func& func, FuncType& wrapperTy, const VirtualFuncInfo& funcInfo, Type& selfTy, WrapVirtualFunc::WrapperFuncGenericTable& genericTable); WrapperFuncGenericTable GetReplaceTableForVirtualFunc( const ClassType& parentTy, const std::string& funcIdentifier, const VirtualFuncInfo& parentFuncInfo); FuncType* RemoveThisArg(FuncType* funcTy); void HandleVirtualFuncWrapperForIncrCompilation(const FuncBase* wrapper, const FuncBase& curFunc); FuncType* GetWrapperFuncType(FuncType& parentFuncTyWithoutThisArg, Type& selfTy, const std::unordered_map& replaceTable, bool isStatic); private: CHIRBuilder& builder; const CompilationCache& increCachedInfo; const IncreKind incrementalKind; const bool targetIsWin; std::unordered_map wrapperCache; VirtualWrapperDepMap curVirtFuncWrapDep; VirtualWrapperDepMap delVirtFuncWrapForIncr; }; } #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/GeneratedFromForIn.h000066400000000000000000000020301510705540100244030ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_GENERATEDFROMFORIN_H #define CANGJIE_CHIR_GENERATEDFROMFORIN_H #include "cangjie/CHIR/Annotation.h" namespace Cangjie::CHIR { /// This type is used to track CHIR node generated from for-in expr translation, so const propagation may optimise on /// them even in O0. struct GeneratedFromForIn final : public Annotation { GeneratedFromForIn() : value{false} {} explicit GeneratedFromForIn(bool) : value{true} {} std::unique_ptr Clone() override { return std::make_unique(); } std::string ToString() override { return "// generated-from-forin "; } static bool Extract(const GeneratedFromForIn* label) { return label->value; } private: bool value; }; } #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/IRChecker.h000066400000000000000000000014131510705540100225260ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /* type check block group block check block terminal check */ #ifndef CANGJIE_CHIR_IRCHECKER_H #define CANGJIE_CHIR_IRCHECKER_H #include #include "cangjie/CHIR/CHIR.h" #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/Option/Option.h" namespace Cangjie::CHIR { bool IRCheck(const class Package& root, const Cangjie::GlobalOptions& opts, CHIRBuilder& builder, const ToCHIR::Phase& phase, std::ostream& out = std::cerr); } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/ImplicitImportedFuncMgr.h000066400000000000000000000056661510705540100255050ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_IMPLICIT_IMPORTED_FUNC_MGR_H #define CANGJIE_CHIR_IMPLICIT_IMPORTED_FUNC_MGR_H #include "cangjie/AST/Node.h" namespace Cangjie::CHIR { /** * The information structure of the imported functions that are implicitly called only in CodeGen. */ struct ImplicitImportedFunc { AST::ASTKind parentKind; std::string identifier{}; std::string parentName{}; }; class ImplicitImportedFuncMgr { public: enum class FuncKind : uint8_t { GENERIC, NONE_GENERIC }; static ImplicitImportedFuncMgr& Instance() noexcept; void RegImplicitImportedFunc(const ImplicitImportedFunc& func, FuncKind funcKind) noexcept; std::vector GetImplicitImportedFuncs(FuncKind funcKind); ImplicitImportedFuncMgr(ImplicitImportedFuncMgr&&) = delete; ImplicitImportedFuncMgr(const ImplicitImportedFuncMgr&) = delete; ImplicitImportedFuncMgr& operator=(ImplicitImportedFuncMgr&&) = delete; ImplicitImportedFuncMgr& operator=(const ImplicitImportedFuncMgr&) = delete; private: ImplicitImportedFuncMgr() noexcept = default; ~ImplicitImportedFuncMgr() = default; /** * This vector is used to store imported generic function information. * These imported generic functions, which are called implicitly in CodeGen, are from the "std.core" package. * Their generic instances may be in other import packages. */ std::vector implicitImportedGenericFuncs{}; /** * This vector is used to store imported non-generic function information. * These imported functions, which are called implicitly in CodeGen, are from the "std.core" package. * If the function has no parent class, * its parent class name is an empty string and its parentKind is "INVALID_DECL". */ std::vector implicitImportedNonGenericFuncs{}; }; class ImplicitImportedFuncRegister { public: ImplicitImportedFuncRegister(const ImplicitImportedFunc& func, ImplicitImportedFuncMgr::FuncKind kind) noexcept { ImplicitImportedFuncMgr::Instance().RegImplicitImportedFunc(func, kind); } ~ImplicitImportedFuncRegister() = default; }; #define REG_IMPLICIT_IMPORTED_NON_GENERIC_FUNC(outDeclKind, identifier, ...) \ static ImplicitImportedFuncRegister g_reg_##identifier##__VA_ARGS__( \ {outDeclKind, #identifier, #__VA_ARGS__}, ImplicitImportedFuncMgr::FuncKind::NONE_GENERIC) #define REG_IMPLICIT_IMPORTED_GENERIC_FUNC(outDeclKind, identifier, ...) \ static ImplicitImportedFuncRegister g_reg_##identifier##__VA_ARGS__( \ {outDeclKind, #identifier, #__VA_ARGS__}, ImplicitImportedFuncMgr::FuncKind::GENERIC) } // namespace Cangjie::CHIR #endif // CANGJIE_CHIR_IMPLICIT_IMPORTED_FUNC_MGR_H cangjie_compiler-1.0.7/include/cangjie/CHIR/Interpreter/000077500000000000000000000000001510705540100230625ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/CHIR/Interpreter/BCHIR.h000066400000000000000000000352031510705540100240650ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the translation of CHIR to BCHIR. */ #ifndef CANGJIE_CHIR_INTERRETER_BCHIR_H #define CANGJIE_CHIR_INTERRETER_BCHIR_H #include #include #include #include "cangjie/CHIR/Interpreter/OpCodes.h" #include "cangjie/CHIR/Interpreter/InterpreterValue.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR::Interpreter { class Bchir { friend class BCHIRLinker; public: Bchir() { defaultFuncPtrs.resize(static_cast(DefaultFunctionKind::INVALID)); } Bchir Clone() const; /** @brief the type of each cell in the bytecode. */ using ByteCodeContent = uint32_t; using ByteCodeIndex = uint32_t; static const ByteCodeContent BYTECODE_CONTENT_MAX = UINT32_MAX; static const ByteCodeIndex BYTECODE_INDEX_MAX = UINT32_MAX; static const int byteCodeContentWidth = sizeof(ByteCodeContent) * CHAR_BIT; /** @brief the type of a variable identifier in BCHIR. Should be the same as ByteCodeContent for * simplification purposes. */ using VarIdx = ByteCodeContent; /** @brief constant for Ops */ static const ByteCodeContent FLAG_ONE = 1; static const ByteCodeContent FLAG_TWO = 2; static const ByteCodeContent FLAG_THREE = 3; static const ByteCodeContent FLAG_FOUR = 4; static const ByteCodeContent FLAG_FIVE = 5; static const ByteCodeContent FLAG_SIX = 6; static const ByteCodeContent DUMMY = 0; struct CodePosition { // position in fileNames vector size_t fileID; unsigned line; unsigned column; }; struct Definition { public: /** @brief pushes opcode into the bytecode. */ void Push(OpCode opcode); /** @brief pushes value into the bytecode. */ void Push(Bchir::ByteCodeContent value); /** @brief push a 8 bytes value */ void Push8bytes(uint64_t value); /** @brief sets value at index in the bytecode. */ void Set(ByteCodeIndex index, Bchir::ByteCodeContent value); /** @brief sets opcode at index in the bytecode. */ void SetOp(ByteCodeIndex index, OpCode opcode); /** @brief get value at index in bytecode */ inline ByteCodeContent Get(ByteCodeIndex index) const { CJC_ASSERT(index < bytecode.size()); return bytecode[static_cast(index)]; } /** @brief get 8-bytes value at index in bytecode */ inline uint64_t Get8bytes(ByteCodeIndex index) const { // needs two cells to represent 8 bytes CJC_ASSERT(index + 1 < bytecode.size()); return bytecode[static_cast(index)] + (static_cast(bytecode[static_cast(index) + 1]) << byteCodeContentWidth); } /** @brief get size of bytecode */ size_t Size() const; /** @brief next available index (same as Size, but returns ByteCodeIndex). */ ByteCodeIndex NextIndex() const; /** @brief resizes the bytecode array */ void Resize(size_t newSize); /** @brief get a reference to the bytecode array */ const std::vector& GetByteCode() const; /** @brief set total number of local vars (including arguments) */ void SetNumLVars(ByteCodeContent num); /** @brief get total number of local vars */ ByteCodeContent GetNumLVars() const; /** @brief set number of arguments */ void SetNumArgs(ByteCodeContent num); // Annotations /** @brief associate `nameIdx` mangled name to `idx` in mangledNames */ void AddMangledNameAnnotation(ByteCodeIndex idx, const std::string& mangledName); /** @brief associate `pos` to `idx` in codePositions */ void AddCodePositionAnnotation(ByteCodeIndex idx, const CodePosition& pos); /** @brief get mangled name of `idx` */ const std::string& GetMangledNameAnnotation(ByteCodeIndex idx) const; /** @brief get code position of `idx` */ const CodePosition& GetCodePositionAnnotation(ByteCodeIndex idx) const; /** @brief get all the mangled names */ const std::unordered_map& GetMangledNamesAnnotations() const; /** @brief get all the code positions */ const std::unordered_map& GetCodePositionsAnnotations() const; private: /** @brief number of parameters */ ByteCodeContent numArgs{0}; /** @brief number of local vars (including parameters) */ ByteCodeContent numLVars{0}; /** @brief bytecode of this definition */ std::vector bytecode; /** @brief Annotation: bytecode index -> mangled name */ std::unordered_map mangledNamesAnnotations; /** @brief Annotation: bytecode index -> source code position */ std::unordered_map codePositionsAnnotations; }; // Default functions from core static const std::string throwArithmeticException; static const std::string throwOverflowException; static const std::string throwIndexOutOfBoundsException; static const std::string throwNegativeArraySizeException; static const std::string callToString; static const std::string throwArithmeticExceptionMsg; static const std::string throwOutOfMemoryError; static const std::string checkIsError; static const std::string throwError; static const std::string callPrintStackTrace; static const std::string callPrintStackTraceError; /** @brief default functions declared by the user or standard library that the interpreter * needs to identify */ enum class DefaultFunctionKind { THROW_ARITHMETIC_EXCEPTION, THROW_OVERFLOW_EXCEPTION, THROW_INDEX_OUT_OF_BOUNDS_EXCEPTION, THROW_NEGATIVA_ARRAY_SIZE_EXCEPTION, CALL_TO_STRING, THROW_ARITHMETIC_EXCEPTION_MSG, THROW_OUT_OF_MEMORY_ERROR, CHECK_IS_ERROR, THROW_ERROR, CALL_PRINT_STACK_TRACE, CALL_PRINT_STACK_TRACE_ERROR, MAIN, INVALID }; static const std::vector defaultFunctionsManledNames; /** @brief Get function pointer */ ByteCodeIndex GetDefaultFunctionPointer(DefaultFunctionKind f) const; /** @brief Return the vector of pointers to the default functions */ const std::vector& GetDefaultFuncPtrs() const; /** @brief Generate `defaultFuncPtrs` from `defaultFuncMangledNames` and `mangle2ptr` */ void LinkDefaultFunctions(const std::unordered_map& mangle2ptr); /** @brief get main function mangled name */ const std::string& GetMainMangledName() const; /** @brief Set main function mangled name */ void SetMainMangledName(const std::string& mangledName); /** @brief returns the number of arguments the main function expects, or 0 if package does not contain a main * function */ size_t GetMainExpectedArgs() const; /** @brief sets the number of arguments the main function expects */ void SetMainExpectedArgs(size_t v); /** @brief sets this package as bchir for core package */ void SetAsCore(); /** @brief true if this bchir for core package */ bool IsCore() const; // For serialization // OPTIMIZE: this is quite expensive to serialize all mangled names again, // we can do better by resolving those that can be resolved first // method name -> mangled name using SVTable = std::unordered_map; struct SClassInfo { // only direct superclasses std::vector superClasses; SVTable vtable; std::string finalizer; }; // mangled name -> class using SClassTable = std::unordered_map; // For execution // method id -> func body index using VTable = std::unordered_map; struct ClassInfo { // Transitive closure of superclasses, required for instanceof std::set superClasses; VTable vtable; ByteCodeIndex finalizerIdx = 0; // func body index // Required to go from `ClassId` to CHIR `Class*` during constant evaluation. std::string mangledName; }; // OPTIMIZE: we can do better to make this as a vector // class id -> class info using ClassTable = std::unordered_map; /** @brief get linkedByteCode */ const Definition& GetLinkedByteCode() const; /** @brief get value at index from linkedByteCode */ inline ByteCodeContent Get(ByteCodeIndex index) const { return linkedByteCode.Get(index); } /** @brief get 8-bytes value at index from linkedByteCode */ inline uint64_t Get8bytes(ByteCodeIndex index) const { return linkedByteCode.Get8bytes(index); } /** @brief sets value at index in the linkedByteCode. */ void Set(ByteCodeIndex index, Bchir::ByteCodeContent value); /** @brief sets opcode at index in the linkedByteCode. */ void SetOp(ByteCodeIndex index, OpCode opcode); /** @brief resize linked bytecode array */ void Resize(size_t newSize); /** @brief add a string to the string section */ size_t AddString(std::string str); /** @brief get string at index from the string section */ const std::string& GetString(size_t index) const; /** @brief get all strings */ const std::vector& GetStrings() const; IVal* StoreStringArray(IArray&& array); /** @brief get types section */ const std::vector& GetTypes() const; /** @brief Adds a new type to the types section and returns its index. */ size_t AddType(Cangjie::CHIR::Type& ty); /** @brief Retrieves a type from the types section * Index must be smaller than type vector */ const Cangjie::CHIR::Type* GetTypeAt(size_t idx) const; /** @brief add a function definition */ void AddFunction(std::string mangledName, Definition&& def); /** @brief get all functions */ const std::map& GetFunctions() const; /** @brief add global variable */ void AddGlobalVar(std::string mangledName, Definition&& def); /** @brief get all global variables */ const std::map& GetGlobalVars() const; /** @brief Add a new file name to the file names section. */ size_t AddFileName(const std::string& name); /** @brief Get the file name at index `idx` from the file names section. */ const std::string& GetFileName(size_t idx) const; /** @brief Get the file names section. */ const std::vector& GetFileNames() const; /** @brief Set the file names section. */ void SetFileNames(std::vector&& names); size_t GetNumGlobalVars() const; void SetNumGlobalVars(size_t num); void SetGlobalInitFunc(const std::string& name); const std::string& GetGlobalInitFunc() const; void SetGlobalInitLiteralFunc(const std::string& name); const std::string& GetGlobalInitLiteralFunc() const; void AddSClass(const std::string& mangledName, SClassInfo&& classInfo); const SClassInfo* GetSClass(const std::string& mangledName) const; SClassInfo* GetSClass(const std::string& mangledName); const SClassTable& GetSClassTable() const; void AddClass(ByteCodeContent id, ClassInfo&& classInfo); const ClassInfo& GetClass(ByteCodeContent id) const; bool ClassExists(ByteCodeContent id) const; void SetVtableEntry(ByteCodeContent classId, ByteCodeContent mId, ByteCodeIndex idx); /** @brief set the class finalizer */ void SetClassFinalizer(ByteCodeContent classId, ByteCodeIndex idx); ByteCodeIndex GetClassFinalizer(ByteCodeContent classId); const ClassTable& GetClassTable() const; /** @brief Instantiate important types and functions from core. To be used only when we don't want to link core. */ void InstantiateDefaultCoreClassesAndFunctions(); std::string packageName; void RemoveFunction(const std::string& name); void RemoveGlobalVar(const std::string& name); void RemoveClass(const std::string& name); /** @brief remove the function/variable/class with the provided mangled name */ void RemoveDefinition(const std::string& name); std::vector initFuncsForConsts; private: // before linking (needs serializing) // ordered map so that serialization order is deterministic /** @brief mangled name -> global variables definition (initializer if any) * the initializer can only be Constant or a Function */ std::map globalVars; /** @brief mangled name -> function definition */ std::map functions; // mangled name of the global init function of this package std::string globalInitFunc; // mangled name of the global init Literal function of this package std::string globalInitLiteralFunc; /** @brief class table for serialization */ SClassTable sClassTable; /** @brief all the mangled names */ std::vector mangledNames; // both before and after (needs serializing) /** @brief pointers to CHIR context types */ std::vector types; /** @brief section for string literals */ std::vector strings; /** @brief IArrays for const strings. Only for linked BCHIR. */ std::vector> stringArrays; /** @brief all the file names */ std::vector fileNames; /** @brief main function mangled name */ std::string mainMangledName; // after linking (no need to serialize) /** @brief bytecode after linking */ Definition linkedByteCode; /** @brief class table after linking */ ClassTable classTable; /** @brief all special function pointers * * Assumption: ByteCodeIndex == 0 means that the function does not exist in BCHIR */ std::vector defaultFuncPtrs; /** @brief the number of arguments the function main expects if this package has a main function */ size_t expectedNumberOfArgumentsByMain = 0; /** @brief total num of global vars */ size_t numGlobalVars = 0; /** @brief true if this is core package */ bool isCore{false}; static const std::string DEFAULT_MANGLED_NAME; static const CodePosition DEFAULT_POSITION; }; } // namespace Cangjie::CHIR::Interpreter #endif // CANGJIE_CHIR_INTERRETER_BCHIR_H cangjie_compiler-1.0.7/include/cangjie/CHIR/Interpreter/BCHIRInterpreter.h000066400000000000000000000327371510705540100263220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the interpreter for BCHIR. */ #ifndef CANGJIE_CHIR_INTERRETER_BCHIRINTERPRETER_H #define CANGJIE_CHIR_INTERRETER_BCHIRINTERPRETER_H // we need to undefine X86_64 and AARCH64 because of include/cangjie/Option/Option.h #if defined(X86_64) #undef X86_64 #endif #if defined(AARCH64) #undef AARCH64 #endif #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/CHIR/Interpreter/BCHIR.h" #include "cangjie/CHIR/Interpreter/BCHIRPrinter.h" #include "cangjie/CHIR/Interpreter/BCHIRResult.h" #include "cangjie/CHIR/Interpreter/InterpreterArena.h" #include "cangjie/CHIR/Interpreter/InterpreterEnv.h" #include "cangjie/CHIR/Interpreter/InterpreterStack.h" #include "cangjie/CHIR/Interpreter/InterpreterValueUtils.h" #include "cangjie/CHIR/OverflowChecking.h" #include #include #include #ifdef _WIN32 #include // we need to undefine THIS, INTERFACE, and FASTCALL which are defined by MinGW #if defined(THIS) #undef THIS #endif #if defined(INTERFACE) #undef INTERFACE #endif #if defined(interface) #undef interface #endif #if defined(CONST) #undef CONST #endif #if defined(GetObject) #undef GetObject #endif #if defined(FASTCALL) #undef FASTCALL #endif #elif defined(__linux__) || defined(__APPLE__) #include #endif namespace Cangjie::CHIR::Interpreter { class BCHIRInterpreter { public: BCHIRInterpreter(Bchir& bchir, DiagnosticEngine& diag, const std::unordered_map& dyHandles, Bchir::ByteCodeIndex playgroundIdxBase, Bchir::ByteCodeIndex externalPlaygroundIdx, bool isConstEval = false) : bchir(bchir), env(bchir.GetNumGlobalVars()), dyHandles(dyHandles), playgroundIdxBase(playgroundIdxBase), externalPlaygroundIdx(externalPlaygroundIdx), isConstEval(isConstEval), diag(diag) { } /** @brief runt the interpreter */ IResult Run(size_t baseIdx, bool expectsReturn = true); /** @brief access the Bchir being evaluated */ const Bchir& GetBchir() const; /** @brief Moves a value to the arena, and returns the pointer */ IPointer ToArena(IVal&& value); /** @brief get the value of a global variable */ const IVal& PeekValueOfGlobal(Bchir::VarIdx id) const; /** @brief set the global vars in the environment according to `gVarInitVals` */ void SetGlobalVars(std::unordered_map&& gVarInitIVals); /** @brief returns the result of the previous run, or INotRun if interpreter never ran */ const IResult& GetLastResult() const; /** @brief the max size of the internal playground, the part of the bytecode * where this interpreter instance can generate code. */ static const size_t INTERNAL_PLAYGROUND_SIZE = 20; /** @brief the max size of the external playground, the part of the bytecode * where users of this instance can generate code. */ static const size_t EXTERNAL_PLAYGROUND_SIZE = 20; #ifndef NDEBUG /** @brief Debug utility. Return a string with the code position for a bytecode operation index. */ std::string DebugGetPosition(Bchir::ByteCodeIndex index); /** @brief Debug utility. Return a string with the mangled for a bytecode operation index. */ std::string DebugGetMangledName(Bchir::ByteCodeIndex index) const; /** @brief Check if writing debug data to a file is enabled. If so, create output file for PrintDebugInfo. */ void PrepareRuntimeDebug(const GlobalOptions& options); /** @brief Debug utility. Write information about current pc to file (if enabled). */ void PrintDebugInfo(Bchir::ByteCodeIndex currentPc); #endif // simply storing the main argument strings here // for CJ_GetMainArgs to query std::vector mainArgs; private: /** @brief the bytecode to be interpreted */ Bchir& bchir; /** @brief interpreter stack */ InterpreterStack interpStack; /* The way the environment works is as follows (note that this approach only works for closure converted OP code): 1. when *entering a function* we should StartStackFrame (store current base pointer and set it to the top of the local environment stack). 2. when *exiting a function* we should RestoreStackFrame (restore the base pointer to the previous one). 3. when entering thunks that are not function's thunks, we just (possibly) emplace new variables in the local environment stack (see SetLocal implementation for details). 4. when exiting thunks that are not function's thunks we simply do nothing. */ /** @brief environment for local and global variables */ Env env; /* represents the heap */ Arena arena; /** @brief available dynamic libs to load syscall functions */ const std::unordered_map& dyHandles; /** @brief true if an unrecoverable error occurred in the interpreter */ bool interpreterError = false; /** @brief program counter */ Bchir::ByteCodeIndex pc = 0; /** @brief base index, that is, the index where the interpretation starts */ Bchir::ByteCodeIndex baseIndex = 0; /** @brief the index of the playground where this interpreter can generate bytecode. The size of the payground is given by INTERNAL_PLAYGROUND_SIZE. */ Bchir::ByteCodeIndex playgroundIdxBase; /** @brief the value of playgroundIdx is always set to playgroundIdxBase when Run is executed and is updated * accordingly when bytecode is generated by the runtime. */ Bchir::ByteCodeIndex playgroundIdx; /** @brief index of the external playground. * * External entities can write to this playground and the interpreter should never change it. Normally external * entities will write bytecode to be evaluated by the interpreter. E.g. * * APPLY :: SOME_IDX :: NUMBER_OF_ARGS * * and then call Run(externalPlayground). */ Bchir::ByteCodeIndex externalPlaygroundIdx; bool raiseExnToTopLevel = false; std::optional exception{}; // Is the interpreter being used for constant evaluation? bool isConstEval = false; /** @brief interpreter last result */ IResult result{INotRun{}}; #ifndef NDEBUG bool printRuntimeDebugInfo{false}; std::fstream debugFile; #endif void Interpret(); void InterpretString(); template void InterpretApply(); template void InterpretInvoke(); void InterpretDeref(); void InterpretSyscall(); template void InterpretTypeCast(); /** @brief Returns true if exception is raised. */ bool InterpretIntrinsic0(); /** @brief Returns true if exception is raised. */ bool InterpretIntrinsic1(); void InterpretArrayBuilder(); void InterpretRawArrayInitByValue(); template void InterpretAllocateRawArray(); void InterpretSwitch(); void InterpretStoreInRef(); void InterpretGetRef(); void InterpretFieldTpl(); void InterpretReturn(); IVal* AllocateValue(IVal&& value); // Invoke support Bchir::ByteCodeIndex FindMethod(Bchir::ByteCodeContent classId, Bchir::ByteCodeContent nameId); // Switch support template void InterpretSwitchWithType(); // Instanceof support bool IsSubclass(Bchir::ByteCodeContent lhs, Bchir::ByteCodeContent rhs); // Raise exceptions // be careful when using these functions, they should return to // the main loop immediate void RaiseArithmeticException(Bchir::ByteCodeIndex sourcePc); void RaiseOverflowException(Bchir::ByteCodeIndex sourcePc); void RaiseIndexOutOfBoundsException(Bchir::ByteCodeIndex sourcePc); void RaiseNegativeArraySizeException(Bchir::ByteCodeIndex sourcePc); void RaiseArithmeticExceptionMsg(Bchir::ByteCodeIndex sourcePc, const std::string& str); void RaiseOutOfMemoryError(Bchir::ByteCodeIndex sourcePc); void RaiseError(Bchir::ByteCodeIndex, const std::string&); /* Binary operations */ /** @brief Perform binary operation */ template void BinOp(); /** @brief Binary operation for TypeKind and Overflow strategy. Returns true if excetion is raised. */ template bool BinOpTyKindAndOverflowStrat(CHIR::Type::TypeKind kind, Cangjie::OverflowStrategy strat); /** @brief Perform binary operation with a fixed boolean type */ template void BinOpFixedBool(); /** @brief Perform binary operation auxiliar for integral types. Returns true if excetion is raised. */ template bool BinOpInt(Cangjie::OverflowStrategy strat); /** @brief Binary exponential. Returns true if excetion is raised. */ bool BinExpOpInt(Cangjie::OverflowStrategy strat); /** @brief Perform regular arithmetic operations: ADD, SUB, MUL, DIV, MOD, BITAND, BITOR, BITXOR, LT, LE, GT, GE, * EQ, NEQ. Returns true if excetion is raised. */ template bool BinRegOpInt(Cangjie::OverflowStrategy strat); /** @brief Perform shift operation. Returns true if excetion is raised. */ template bool BinShiftOpIntCase(); /** @brief Perform shift operation. Returns true if excetion is raised. */ template bool BinShiftOpInt(); /** @brief Adds res to the argument stack or raises exception. Returns true if excetion is raised. */ template bool PushIfNotOverflow(bool overflow, S res, Cangjie::OverflowStrategy strat); /** @brief Perform binary operation auxiliar for floating point types. Returns true if excetion is raised. */ template bool BinOpFloat(); /** @brief Perform binary operation for bools. Returns true if excetion is raised. */ template bool BinOpBool(); /** @brief Perform binary operation for Rune. Returns true if excetion is raised. */ template bool BinOpRune(); /** @brief Perform binary operation for Unit. Returns true if excetion is raised. */ template bool BinOpUnit(); /** @brief Perform binary compare operation. Returns true if excetion is raised. */ template bool BinOpCompare(T x, T y); /* FFI utilities */ /** @brief Calculates the size required to store the C type corresponding to `ty`. */ unsigned long SizeOfFFIType(const CHIR::Type& ty); /** @brief Calculates the alignment of the C type corresponding to `ty`. */ unsigned long AlignOfFFIType(const CHIR::Type& ty); /** @brief returns true on success, false otherwise */ /* we just pass the resultSize to make the code quality check happy */ bool ExecuteSyscall(void (*func)(void), size_t numberOfArgs, void* execResult); void PerformSyscall( const std::string& name, Bchir::ByteCodeIndex opIdx, void (*func)(void), size_t args, const CHIR::Type& resTy); /* Intrinsic functions */ template void InterpretIntrinsic(); /** @brief Returns true if exception is raised. */ void InterpretVArrayGet(); /** @brief Returns true if exception is raised. */ bool InterpretArrayGetIntrinsic(Bchir::ByteCodeIndex idx, bool indexCheck); bool InterpretArrayGet(Bchir::ByteCodeIndex idx, bool indexCheck, IPointer& arrayPtr, int64_t argIndex); void InterpretRawArrayLiteralInit(); void InterpretRefEq(); void InterpretCJCodeCanUseSIMD(); void ReportConstEvalException(Bchir::ByteCodeIndex opIdx, std::string exceptionName); /* TypeCast */ template bool CastOrRaiseExceptionForInt(SourceTyRaw v, OverflowStrategy strategy, Bchir::ByteCodeIndex opIdx); template bool CastOrRaiseExceptionForFloat(SourceTyRaw floatVal, Bchir::ByteCodeIndex opIdx); template bool InterpretTypeCastForInt( T val, CHIR::Type::TypeKind targetKind, OverflowStrategy strategy, Bchir::ByteCodeIndex opIdx); template bool InterpretTypeCastForFloat(T floatVal, CHIR::Type::TypeKind targetKind, Bchir::ByteCodeIndex opIdx); /* ERROR handling */ /** @brief terminate interpretation due to unexpected error. * * @param opIdx for the operation that caused the failure. We use it to extract the position. */ DiagnosticEngine& diag; template void FailWith(Bchir::ByteCodeIndex opIdx, std::string excErrorMsg, DiagKind kind, Args... args) { auto& sm = diag.GetSourceManager(); auto pos = bchir.GetLinkedByteCode().GetCodePositionAnnotation(opIdx); // convert file name (bchir) ID to (source manager) ID auto fileName = bchir.GetFileName(pos.fileID); auto fileId = sm.GetFileID(fileName); if (fileId == -1) { fileId = static_cast(sm.AddSource(fileName, "")); } Cangjie::Position cjPos{ static_cast(fileId), static_cast(pos.line), static_cast(pos.column)}; if (cjPos.IsZero()) { diag.Diagnose(kind, args...); } else { diag.Diagnose(cjPos, kind, args...); } RaiseError(opIdx, excErrorMsg); } }; } // namespace Cangjie::CHIR::Interpreter #endif // CANGJIE_CHIR_INTERRETER_BCHIRINTERPRETER_H cangjie_compiler-1.0.7/include/cangjie/CHIR/Interpreter/BCHIRLinker.h000066400000000000000000000117201510705540100252300ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the linker for BCHIR. */ #ifndef CANGJIE_CHIR_INTERRETER_BCHIRLINKER_H #define CANGJIE_CHIR_INTERRETER_BCHIRLINKER_H #include "cangjie/CHIR/Interpreter/BCHIR.h" #include "cangjie/CHIR/Interpreter/InterpreterValueUtils.h" #include "cangjie/Option/Option.h" namespace Cangjie::CHIR::Interpreter { class BCHIRLinker { public: BCHIRLinker(Bchir& topBchir) : topBchir(topBchir), topDef(topBchir.linkedByteCode) { } /** @brief Link the provided packages in topBchir. * * @returns the value of the global vars that need to be initialized manually. ATM the return map is empty * if `ForConstEval == false`. */ template std::unordered_map Run(std::vector& packages, const GlobalOptions& options); int GetGVARId(const std::string& name) const; private: Bchir& topBchir; Bchir::Definition& topDef; /** @brief memoization table for the index in bchir.fileNames */ std::unordered_map fileName2IndexMemoization; /** @brief memoization table for the index in bchir.types */ std::unordered_map type2IndexMemoization; /** @brief memoization table for the index in bchir.strings */ std::unordered_map strings2IndexMemoization; /** @brief location in the bytecode of function's mangled names */ std::unordered_map mName2FuncBodyIdx; /** @brief functions that have not yet been encoded, and set of locations that need to be updated when they are * encoded */ std::unordered_map> mName2FuncBodyIdxPlaceHolder; Bchir::ByteCodeContent gvarId{0}; std::unordered_map mName2GvarId; Bchir::ByteCodeContent classId{0}; std::unordered_map mName2ClassId; Bchir::ByteCodeContent methodId{0}; std::unordered_map mName2MethodId; /** Index of a dummy function of the form FRAME :: 0 :: ABORT */ Bchir::ByteCodeIndex dummyAbortFuncIdx{Bchir::BYTECODE_CONTENT_MAX}; void LinkClasses(const Bchir& bchir); void LinkClass(const Bchir& bchir, const std::string& mangledName); void LinkAndInitGlobalVars( const Bchir& bchir, std::unordered_map& gvarId2InitIVal, bool isLast); /** @brief Generates a dummy function that simply aborts interpretation. */ void GenerateDummyAbortFunction(); void LinkFunctions(const std::vector& packages); void GenerateCallsToConstInitFunctions(const std::vector& constInitFuncs); /** @brief Traverse currentDef and append it to topBCHIR */ void TraverseAndLink(const Bchir& bchir, const Bchir::Definition& currentDef, const std::vector& fileMap, const std::vector& typeMap, const std::vector& stringMap); void AddPosition(const std::unordered_map& positions, const std::vector& fileMap, Bchir::ByteCodeIndex curr); void AddMangledName( const std::unordered_map& mangledNames, Bchir::ByteCodeIndex curr); /** @brief returns a map from that maps unliked files indexes to linked files indexes */ std::vector UnlinkedFiles2LinkedFiles(const std::vector& fileNames); /** @brief returns a map from that maps unliked types indexes to linked types indexes */ std::vector UnlinkedTypes2LinkedTypes(const std::vector& types); /** @brief returns a map from that maps unliked strings indexes to strings types indexes */ std::vector UnlinkedStrings2LinkedStrings(const std::vector& strings); Bchir::ByteCodeIndex FreshGVarId(); /** @brief get the class id of class `classMangledName` */ Bchir::ByteCodeContent GetClassId(const std::string& classMangledName); Bchir::ByteCodeContent GetMethodId(const std::string& methodName); /** @brief add `idx` to the set of locations that need to be set once function with mangled name `nName` is encoded */ void AddToMName2FuncBodyIdxPlaceHolder(const std::string& mName, Bchir::ByteCodeIndex idx); /** @brief resolve all the indexes from `mName2FuncBodyIdxPlaceHolder` */ void ResolveMName2FuncBodyIdxPlaceHolder(const std::string& mName, Bchir::ByteCodeIndex idx); }; } // namespace Cangjie::CHIR::Interpreter #endif // CANGJIE_CHIR_INTERRETER_BCHIRLINKER_H cangjie_compiler-1.0.7/include/cangjie/CHIR/Interpreter/BCHIRPrinter.h000066400000000000000000000062251510705540100254330ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares a BCHIR printer. */ #ifndef CANGJIE_CHIR_INTERRETER_BCHIRPRINTER_H #define CANGJIE_CHIR_INTERRETER_BCHIRPRINTER_H #include "cangjie/CHIR/Interpreter/BCHIR.h" #include "cangjie/CHIR/Interpreter/OpCodes.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/Option/Option.h" #include #include #include #include namespace Cangjie::CHIR::Interpreter { class BCHIRPrinter { public: BCHIRPrinter(std::ostream& os, const Bchir& bchir) : os(os), bchir(bchir) { } // This function is not used by the interpreter directly, but it's // necessary when debugging the interpreter runtime void Print(std::string header = ""); // This prints out all relevant information that will be serialized. void PrintAll(std::string header = ""); // Open (and create) a file for writing debug BCHIR information to for the // given package and compilation stage. static std::fstream GetBCHIROutputFile( const GlobalOptions& options, const std::string& fullPackageName, const std::string& stageName); private: std::ostream& os; const Bchir& bchir; class DefinitionPrinter { public: DefinitionPrinter(const Bchir& bchir, const Bchir::Definition& def, std::ostream& os) : bchir(bchir), os(os), bytecode(def.GetByteCode()), def(def) { } void Print(); private: const std::string LEFT{""}; const std::string RIGHT{""}; const std::string ARGSEP{", "}; const std::string OPSEP{"\n"}; void PrintOP(); void PrintOPFloat(); void PrintOPFloat8bytes(); void PrintOPRune(); void PrintOPSwitch(); void PrintOPBinRshift(OpCode opCode); void PrintOPTypeCast(); void PrintPath(); void PrintOPIntrinsic(OpCode opCode); // PrintOpCode advances index void PrintOpCode(); void PrintAtIndex(); void PrintAtIndex8bytes(); // This function is not used by the interpreter directly, but it's // necessary when debugging the interpreter runtime void PrintTy(); void Print(size_t argIndex, const std::string& str); private: const Bchir& bchir; std::ostream& os; const std::vector& bytecode; const Bchir::Definition& def; size_t index{0}; }; // These methods are intended to print out BCHIR data // according to how it is serialized such that // we can debug BEP issues better later on. // This will need updates when new things are introduced to BCHIR. void PrintSClassTable(); void PrintStrings(); void PrintTypes(); void PrintSourceFiles(); static const std::unordered_map OVERFLOW_STRAT2STRING; }; } // namespace Cangjie::CHIR::Interpreter #endif // CANGJIE_CHIR_INTERRETER_BCHIRPRINTER_H cangjie_compiler-1.0.7/include/cangjie/CHIR/Interpreter/BCHIRResult.h000066400000000000000000000014661510705540100252700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares interpreter values. */ #ifndef CANGJIE_CHIR_INTERRETER_BCHIRRESULT_H #define CANGJIE_CHIR_INTERRETER_BCHIRRESULT_H #include "cangjie/CHIR/Interpreter/InterpreterValueUtils.h" #include namespace Cangjie::CHIR::Interpreter { // possible outcomes of interpretation struct INotRun { }; struct IException { IVal ptr; }; struct ISuccess { IVal val; }; using IResult = std::variant; } // namespace Cangjie::CHIR::Interpreter #endif // CANGJIE_CHIR_INTERRETER_BCHIRRESULT_Hcangjie_compiler-1.0.7/include/cangjie/CHIR/Interpreter/CHIR2BCHIR.h000066400000000000000000000236411510705540100246200ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the translation of CHIR to BCHIR. */ #ifndef CANGJIE_CHIR_INTERRETER_CHIR2BCHIR_H #define CANGJIE_CHIR_INTERRETER_CHIR2BCHIR_H #include #include #include "cangjie/Basic/SourceManager.h" #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Interpreter/BCHIR.h" #include "cangjie/CHIR/Interpreter/BCHIRPrinter.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Value.h" #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Option/Option.h" namespace Cangjie::CHIR::Interpreter { class CHIR2BCHIR { public: /** @brief Compile CHIR `chirPkg` into BCHIR `destBchir`. Set ForConstEval to true if BCHIR is intended * to evaluate constants. */ template static void CompileToBCHIR(const Package& chirPkg, Bchir& destBchir, const std::vector& initFuncsForConstVar, SourceManager& sm, const GlobalOptions& options, bool printBchir = false, bool incremental = false) { CHIR2BCHIR chir2bchir(destBchir, sm, incremental); chir2bchir.TranslatePackage(chirPkg, initFuncsForConstVar); if (printBchir) { auto stageName = ForConstEval ? "ce-chir2bchir" : "chir2bchir"; auto bchirFile = CHIR::Interpreter::BCHIRPrinter::GetBCHIROutputFile(options, chirPkg.GetName(), stageName); auto bchirPrinter = CHIR::Interpreter::BCHIRPrinter(bchirFile, destBchir); bchirPrinter.PrintAll("BCHIR after CHIR2BCHIR for " + chirPkg.GetName()); } } private: Bchir& bchir; /** @brief source manager so that we can translate SOURCE_FILE intrinsic function */ SourceManager& sourceManager; /** @brief CHIR const local variable ID */ Bchir::ByteCodeContent constLocalVarId = 0; /** @brief Constant local variables ids. */ std::unordered_map const2CLVarId; /** @brief Whether this is incremental, with the previous BCHIR having been restored */ bool isIncremental; CHIR2BCHIR(Bchir& bchir, SourceManager& sm, bool isIncremental) : bchir(bchir), sourceManager(sm), isIncremental(isIncremental) { } /** @brief memoization table for the index in bchir.fileNames */ std::unordered_map fileNameToIndexMemoization; /** @brief memoization table for the index in bchir.constStrings */ std::unordered_map constStringsMemoization; /** @brief memoization table for the index in bchir.types */ std::unordered_map typesMemoization; /** @brief translate a CHIR package into BCHIR */ template void TranslatePackage( const Package& chirPkg, const std::vector& initFuncsForConstVar); template void TranslateClassesLike(const Package& chirPkg); template void TranslateClasses(const Package& chirPkg); template void TranslateStucts(const Package& chirPkg); template void TranslateEnums(const Package& chirPkg); template void TranslateExtends(const Package& chirPkg); template void TranslateGlobalVars(const Package& chirPkg); template void TranslateFunctions(const Package& chirPkg); /** @brief Adds a new type to the types section of bchir and returns its index. Uses typesMemoization. */ Bchir::ByteCodeContent GetTypeIdx(CHIR::Type& chirType); void SetDefaultFunction(const Package& chirPkg, const Func& f); /* These functions should possibly be marked as intrinsic in CHIR, but since they aren't at the moment we intercept them in CHIR2BCHIR and translate them to intrinsic functions in BCHIR. */ static const std::unordered_map syscall2IntrinsicKind; /** @brief Add a new string to the string section of bchir using stringsMem for memoization. */ Bchir::ByteCodeContent GetStringIdx(std::string str); /** @brief Translate a CHIR location into a BCHIR location */ Bchir::CodePosition CHIRPos2BCHIRPos(const DebugLocation& loc); struct Context { Bchir::Definition def; /** @brief CHIR local variable ID */ Bchir::ByteCodeContent localVarId = 0; /** @brief Local variables ids. */ std::unordered_map val2lvarId; /** @brief Index of the blocks that have been encoded so far. */ std::unordered_map bb2Index; /** @brief Index of the place holders of the blocks that haven't been encoded. */ std::unordered_map> bb2IndexPlaceHolder; }; template Context TranslateGlobalVar(const GlobalVar& gv); void TranslateFuncDef(Context& ctx, const Func& func); void TranslateValue(Context& ctx, const Value& value); void TranslateLiteralValue(Context& ctx, const LiteralValue& value); void TranslateBlock(Context& ctx, const Block& bb); void TranslateExpression(Context& ctx, const Expression& expr); void TranslateIntValue(Context& ctx, const IntLiteral& value); void TranslateFloatValue(Context& ctx, const FloatLiteral& value); void TranslateTerminatorExpression(Context& ctx, const Expression& expr); void TranslateUnaryExpression(Context& ctx, const Expression& expr); void TranslateBinaryExpression(Context& ctx, const Expression& expr); void TranslateMemoryExpression(Context& ctx, const Expression& expr); void TranslateOthersExpression(Context& ctx, const Expression& expr); void TranslateField(Context& ctx, const Expression& expr); void TranslateInvoke(Context& ctx, const Expression& expr); void TranslateTypecast(Context& ctx, const Expression& expr); template void TranslateIntrinsicExpression(Context& ctx, const T& intrinsic); void TranslateCApplyExpression(Context& ctx, const Apply& apply, const Cangjie::CHIR::FuncType& funcTy); void TranslateApplyExpression(Context& ctx, const Apply& apply); void TranslateApplyWithExceptionExpression(Context& ctx, const ApplyWithException& apply); void TranslateMultiBranch(Context& ctx, const MultiBranch& branch); void TranslateAllocate(Context& ctx, const Expression& expr); void TranslateIntOpWithException(Context& ctx, const IntOpWithException& expr); void TranslateBox(Context& ctx, const Box& expr); void TranslateInstanceOf(Context& ctx, const InstanceOf& expr); /** @brief Returns the index for block `bb`. If the block hasn't been encoded yet * it returns a dummy index and adds `indexPlaceHolder` to `bb2IndexPlaceHolder`. */ Bchir::ByteCodeContent BlockIndex(Context& ctx, const Block& bb, Bchir::ByteCodeIndex indexPlaceHolder); /** @brief Resolves all the placeholders for block `bb` with `idx`. */ void ResolveBB2IndexPlaceHolder(Context& ctx, const Block& bb, Bchir::ByteCodeIndex idx); /** @brief Returns the id of a local variable. */ Bchir::ByteCodeContent LVarId(Context& ctx, const Value& value); /** @brief generate bytecode for setting the result variable and jump indexes for * x_WITH_EXCEPTION operations */ void TranslateTryTerminatorJumps(Context& ctx, const Terminator& expr); inline void PushArgs(Context&) const { } #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wsign-conversion" #endif template inline void PushArgs(Context& ctx, Bchir::ByteCodeContent v, Args&&... args) { ctx.def.Push(v); PushArgs(ctx, args...); } /** @brief Copy default annotations from CHIRNode into the OpAnnotationMap */ template void AddAnnotations(Context& ctx, const Expression& expr, Bchir::ByteCodeIndex opCodeIndex); /** @brief Copy default annotations from CHIRNode into the OpAnnotationMap */ template void AddAnnotations(Context& ctx, const Value& value, Bchir::ByteCodeContent opCodeIndex); #ifndef NDEBUG // in debug mode store as much information as possible to make the debugging process easier template #else template #endif void PushOpCodeWithAnnotations(Context& ctx, OpCode opCode, const Expression& expr, Args&&... args) { auto opIdx = ctx.def.NextIndex(); ctx.def.Push(opCode); if constexpr (StoreMangledName || StoreCodePos) { AddAnnotations(ctx, expr, opIdx); } PushArgs(ctx, args...); } #ifndef NDEBUG // in debug mode store as much information as possible to make the debugging process easier template #else template #endif void PushOpCodeWithAnnotations(Context& ctx, OpCode opCode, const Value& value, const Args&&... args) { auto opIdx = ctx.def.NextIndex(); ctx.def.Push(opCode); if constexpr (StoreMangledName || StoreCodePos) { AddAnnotations(ctx, value, opIdx); } PushArgs(ctx, args...); } #if defined(__clang__) #pragma clang diagnostic pop #endif bool IsConstClass(const CustomTypeDef& def) const; }; } // namespace Cangjie::CHIR::Interpreter #endif // CANGJIE_CHIR_INTERRETER_CHIR2BCHIR_H cangjie_compiler-1.0.7/include/cangjie/CHIR/Interpreter/ConstEval.h000066400000000000000000000103161510705540100251320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the constant evaluation pass using the interpreter */ #ifndef CANGJIE_CHIR_INTERP_CONST_EVAL_H #define CANGJIE_CHIR_INTERP_CONST_EVAL_H #include #include #include #include #include #include #include #include #include #include #include namespace Cangjie::CHIR::Interpreter { class IVal2CHIR { public: IVal2CHIR(CHIRBuilder& chirBuilder, const Bchir& bchir, const Package& package) : chirBuilder(chirBuilder), bchir(bchir), package(package) { } IVal2CHIR(const IVal2CHIR&) = delete; IVal2CHIR& operator=(const IVal2CHIR&) = delete; // Returns a Constant that represents the same value as `val`. // Returns nullptr if `val` does not have an equivalent constant. Constant* TryConvertToConstant(Type& ty, const IVal& val, Block& parent); // Converts `val` to chir that produces an equivalent value. // Returns the value for the expression that holds the full value. // Returns nullptr if the value cannot be converted because the type is // unsupported or if it references a function that has not been imported. Value* ConvertToChir(Type& ty, const IVal& val, std::function& insertExpr, Block& parent); private: Value* ConvertStringToChir( Type& ty, const ITuple& val, const std::function& insertExpr, Block& parent); Value* ConvertTupleToChir(Type& ty, const ITuple& val, std::function& insertExpr, Block& parent); Value* ConvertEnumToChir( EnumType& ty, const IVal& val, std::function& insertExpr, Block& parent); Value* ConvertRefToChir(RefType& ty, const IVal& val, std::function& insertExpr, Block& parent); Value* ConvertFuncToChir(const FuncType& ty, const IFunc& val); Value* ConvertArrayToChir( VArrayType& ty, const IArray& val, std::function& insertExpr, Block& parent); ClassType* FindClassType(const std::string& mangledName); CHIRBuilder& chirBuilder; const Bchir& bchir; const Package& package; }; class ConstEvalPass { public: explicit ConstEvalPass(CompilerInstance& ci, CHIRBuilder& builder, SourceManager& sourceManager, const Cangjie::GlobalOptions& opts, DiagnosticEngine& diag) : ci(ci), builder(builder), sourceManager(sourceManager), opts(opts), diag(diag) { } ConstEvalPass(const ConstEvalPass&) = delete; ConstEvalPass& operator=(const ConstEvalPass&) = delete; // Evaluates constants (variables declared with `const`) and simplifies // their intializers. void RunOnPackage(Package& package, const std::vector& initFuncsForConstVar, std::vector& bchirPackages); private: void RunInterpreter(Package& package, std::vector& bchirPackages, const std::vector& initFuncsForConstVar, std::function onSuccess); void ReplaceGlobalConstantInitializers(Package& package, BCHIRInterpreter& interpreter, BCHIRLinker& linker); void RemoveConstructorCalls(const Value& value); std::optional CreateNewInitializer( Func& oldInitializer, const BCHIRInterpreter& interpreter, const BCHIRLinker& linker, const Package& package); void PrintDebugMessage( const DebugLocation& loc, const Func& oldInit, const std::optional& newInit) const; CompilerInstance& ci; CHIRBuilder& builder; SourceManager& sourceManager; const Cangjie::GlobalOptions& opts; DiagnosticEngine& diag; }; } // namespace Cangjie::CHIR::Interpreter #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Interpreter/InterpreterArena.h000066400000000000000000000044601510705540100265110ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares an arena for allocating interpreter values. */ #ifndef CANGJIE_CHIR_INTERRETER_INTERPREVERARENA_H #define CANGJIE_CHIR_INTERRETER_INTERPREVERARENA_H #include "cangjie/CHIR/Interpreter/InterpreterValueUtils.h" namespace Cangjie::CHIR::Interpreter { class Arena { public: /* list of objects that needs to run finalizer on them */ std::vector finalizingObjects; Arena() { buckets.reserve(BUCKETS); buckets.emplace_back(std::make_unique>()); buckets.back()->reserve(BUCKET_SIZE); finalizingObjects.reserve(BUCKET_SIZE); } IVal* Allocate(IVal&& value) { if (buckets.back()->size() == BUCKET_SIZE) { buckets.emplace_back(std::make_unique>()); buckets.back()->reserve(BUCKET_SIZE); } auto& lastBucket = buckets.back(); lastBucket->emplace_back(std::move(value)); auto ptr = &lastBucket->back(); return ptr; } void PrintStats() { std::cout << "Number of buckets: " << buckets.size() << std::endl; } int64_t GetAllocatedSize() { CJC_ASSERT(buckets.size() >= 1); size_t r = ((buckets.size() - 1) * BUCKET_SIZE + buckets.back()->size()) * sizeof(IVal); return static_cast(r); } private: static const size_t BUCKETS = 2048; static const size_t BUCKET_SIZE = 2048; // Why unique_ptr? Because in C++ vector reallocation may either copy or move its contents. // It sohuld move if possible -- and it should be possible in this case. // HOWEVER, unique_ptr will FORCE the move. IE -- if something goes wrong and moving is not // possible for some reason, this will NOT COMPILE. // Therefore, I think to start of it's worth keeping the indirection on, and remove it only // after profiling. T0D0!! using Bucket = std::unique_ptr>; std::vector buckets; }; } // namespace Cangjie::CHIR::Interpreter #endif // CANGJIE_CHIR_INTERRETER_INTERPREVERARENA_Hcangjie_compiler-1.0.7/include/cangjie/CHIR/Interpreter/InterpreterEnv.h000066400000000000000000000054311510705540100262120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares an environment for the BCHIR interpreter. */ #ifndef CANGJIE_CHIR_INTERRETER_INTERPRETERENV_H #define CANGJIE_CHIR_INTERRETER_INTERPRETERENV_H #include "cangjie/CHIR/Interpreter/BCHIR.h" #include "cangjie/CHIR/Interpreter/InterpreterValueUtils.h" namespace Cangjie::CHIR::Interpreter { struct Env { const static size_t LOCAL_ENV_DEFAULT_SIZE = 1024; Env(size_t sizeGlobalEnv) : numberOfGlobals(sizeGlobalEnv) { local.reserve(LOCAL_ENV_DEFAULT_SIZE); global.resize(numberOfGlobals, IInvalid()); } void SetLocal(Bchir::VarIdx var, IVal&& node) { local[bp + var] = std::move(node); } void AllocateLocalVarsForFrame(size_t number) { // this should always be performed when entering the frame CJC_ASSERT(local.size() == bp); local.resize(local.size() + number); } void SetGlobal(Bchir::VarIdx var, IVal&& node) { CJC_ASSERT(var < global.size()); global[var] = std::move(node); } const IVal& GetLocal(Bchir::VarIdx var) { CJC_ASSERT(bp + var < local.size()); CJC_ASSERT(!std::holds_alternative(local[bp + var])); return local[bp + var]; } IVal& GetGlobal(Bchir::VarIdx var) { CJC_ASSERT(var < global.size()); // Global vars are initialized with IInvalid. global[var] can be IInvalid the first time we read it. return global[var]; } const IVal& PeekGlobal(Bchir::VarIdx var) const { CJC_ASSERT(var < global.size()); // Global vars are initialized with IInvalid. global[var] can be IInvalid the first time we read it. return global[var]; } void StartStackFrame() { bp = local.size(); } /** @brief set base pointer to newBP and clean environment stack after bp. */ void RestoreStackFrameTo(size_t newBP) { // we assume that the newBP is the preceeding stack frame of bp local.erase(local.begin() + static_cast::difference_type>(bp), local.end()); bp = newBP; } size_t GetBP() const { return bp; } private: size_t numberOfGlobals; /** @brief environment for global variables */ std::vector global; /** @brief environment for local variables */ std::vector local; /** @brief the base pointer in the local environment (an index of `local`) */ size_t bp{0}; }; } // namespace Cangjie::CHIR::Interpreter #endif // CANGJIE_CHIR_INTERRETER_INTERPRETERENV_Hcangjie_compiler-1.0.7/include/cangjie/CHIR/Interpreter/InterpreterStack.h000066400000000000000000000253371510705540100265360ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares a stack for the bytecode interpreter. */ #ifndef CANGJIE_CHIR_INTERRETER_INTERPRETERSTACK_H #define CANGJIE_CHIR_INTERRETER_INTERPRETERSTACK_H #include "cangjie/CHIR/Interpreter/BCHIR.h" #include "cangjie/CHIR/Interpreter/InterpreterValueUtils.h" namespace Cangjie::CHIR::Interpreter { struct ControlState { OpCode opCode; size_t argStackPtr; Bchir::ByteCodeIndex byteCodePtr; size_t envBP; }; /** @brief explicit stacks for the interpreter */ struct InterpreterStack { const static size_t ARG_STACK_SIZE = 16384; const static size_t OP_STACK_SIZE = 1024; InterpreterStack() { argStack.reserve(ARG_STACK_SIZE); controlStack.reserve(OP_STACK_SIZE); } ~InterpreterStack() { while (ArgsSize() > 0) { ArgsPopBack(); } } InterpreterStack(InterpreterStack&) = delete; InterpreterStack& operator=(const InterpreterStack& other) = delete; /** * @brief Consume an IValStack and transform it into an IVal **/ IVal ToIVal(IValStack&& n) const { return std::visit( [&](auto&& arg) -> IVal { using T = std::decay_t; if constexpr (std::is_same_v) { ITuple res; std::swap(res.content, *arg.contentPtr); delete arg.contentPtr; return res; } else if constexpr (std::is_same_v) { IArray res; std::swap(res.content, *arg.contentPtr); delete arg.contentPtr; return res; } else if constexpr (std::is_same_v) { IObject res; std::swap(res.content, *arg.contentPtr); res.classId = arg.classId; delete arg.contentPtr; return res; } else { return arg; } }, std::move(n)); } /** * @brief Consume an IVal and transform it into an IValStack */ IValStack FromIVal(IVal&& n) const { return std::visit( [&](auto&& arg) -> IValStack { using T = std::decay_t; if constexpr (std::is_same_v) { std::vector* ptr = new std::vector(); std::swap(*ptr, arg.content); ITuplePtr res{ptr}; return res; } else if constexpr (std::is_same_v) { std::vector* ptr = new std::vector(); std::swap(*ptr, arg.content); IArrayPtr res{ptr}; return res; } else if constexpr (std::is_same_v) { std::vector* ptr = new std::vector(); std::swap(*ptr, arg.content); IObjectPtr res{arg.classId, ptr}; return res; } else { return arg; } }, std::move(n)); } /** * @brief Pop an element of type T from the stack * * Fails if the top of the stack is not of type T */ template T ArgsPop() { CJC_ASSERT(!argStack.empty()); using S = std::decay_t; auto arg = std::move(argStack.back()); argStack.pop_back(); if constexpr (std::is_same_v) { ITuple res; std::swap(res.content, *std::get(arg).contentPtr); delete std::get(arg).contentPtr; return res; } else if constexpr (std::is_same_v) { IArray res; std::swap(res.content, *std::get(arg).contentPtr); delete std::get(arg).contentPtr; return res; } else if constexpr (std::is_same_v) { IObject res; std::swap(res.content, *std::get(arg).contentPtr); res.classId = std::get(arg).classId; delete std::get(arg).contentPtr; return res; } else { return std::get(arg); } } /** * @brief Pop an IVal from the stack * * Avoid using this method unless you will use the result, * and you can't know the type of the top element */ inline IVal ArgsPopIVal() { CJC_ASSERT(!argStack.empty()); auto n = std::move(argStack.back()); argStack.pop_back(); return ToIVal(std::move(n)); } /** * @brief Pop and destroy the top of the stack * * This is faster that using ArgsPopIVal */ void ArgsPopBack() { CJC_ASSERT(!argStack.empty()); std::visit( [&](auto& arg) { using T = std::decay_t; if constexpr (std::is_same_v) { delete arg.contentPtr; } else if constexpr (std::is_same_v) { delete arg.contentPtr; } else if constexpr (std::is_same_v) { delete arg.contentPtr; } }, argStack.back()); argStack.pop_back(); } /** * @brief Pop size elements from the stack and place them on elems * * elems will be cleared before using it. */ void ArgsPop(size_t size, std::vector& elems) { // OPTIMIZE CJC_ASSERT(size <= argStack.size()); elems.clear(); elems.reserve(size); for (size_t i = 0; i < size; ++i) { (void)elems.emplace_back(ArgsPopIVal()); } std::reverse(elems.begin(), elems.end()); } /** * @brief Get the top element as an IVal * * This method is slow, try to avoid it */ IVal ArgsTopIVal() const { CJC_ASSERT(!argStack.empty()); return ArgsGet(1, 0); } /** * @brief remove n elements from the stack */ void ArgsRemove(size_t n) { CJC_ASSERT(n <= argStack.size()); while (n--) { ArgsPopBack(); } } /** * @brief remove all but n element from the stack */ void ArgsRemoveAfter(size_t n) { CJC_ASSERT(n <= argStack.size()); auto toRemove = argStack.size() - n; ArgsRemove(toRemove); } /** * @brief Get the index element from offsetFromEnd element from the top * * Try to avoid this method, since it copies the IVal */ IVal ArgsGet(size_t offsetFromEnd, size_t index) const { auto idx = (argStack.size() - offsetFromEnd) + index; CJC_ASSERT(idx < argStack.size()); return ArgsGet(idx); } /** * @brief Get the idx element from the bottom of the stack * * Try to avoid this method, since it copies the IVal */ IVal ArgsGet(size_t idx) const { IValStack val = argStack[idx]; return std::visit( [&](auto&& arg) -> IVal { using T = std::decay_t; if constexpr (std::is_same_v) { ITuple res{*arg.contentPtr}; return res; } else if constexpr (std::is_same_v) { IArray res{*arg.contentPtr}; return res; } else if constexpr (std::is_same_v) { IObject res{arg.classId, *arg.contentPtr}; return res; } else { return arg; } }, std::move(val)); } size_t ArgsSize() const { return argStack.size(); } /** * @brief Push a value of type T into the stack */ template void ArgsPush(T&& node) { using S = std::decay_t; static_assert(!std::is_same_v, "ArgsPush can't be used with IVal, only the internal values of IVal"); if constexpr (std::is_same_v) { ITuplePtr t{new std::vector}; std::swap(*t.contentPtr, node.content); (void)argStack.emplace_back(t); } else if constexpr (std::is_same_v) { IArrayPtr t{new std::vector}; std::swap(*t.contentPtr, node.content); (void)argStack.emplace_back(t); } else if constexpr (std::is_same_v) { IObjectPtr t{node.classId, new std::vector}; std::swap(*t.contentPtr, node.content); (void)argStack.emplace_back(t); } else { (void)argStack.emplace_back(std::forward(node)); } } /** * @brief Push an IVal into the stack * * Try to avoid this method since it's slow */ void ArgsPushIVal(IVal&& node) { (void)argStack.emplace_back(FromIVal(std::move(node))); } /** * @brief Push an IVal, but only copy it when needed */ void ArgsPushIValRef(const IVal& node) { std::visit( [this](const auto& arg) { using T = std::decay_t; T v = arg; ArgsPush(std::move(v)); }, node); } void ArgsSwapFromEnd(size_t i, size_t j, size_t offsetFromEnd) { std::swap(argStack[(argStack.size() - offsetFromEnd) + i], argStack[(argStack.size() - offsetFromEnd) + j]); } const ControlState& CtrlTop() const { return controlStack.back(); } void CtrlPush(ControlState&& op) { controlStack.emplace_back(std::move(op)); } ControlState CtrlPop() { auto ctrl = std::move(controlStack.back()); controlStack.pop_back(); return ctrl; } void CtrlDrop() { controlStack.pop_back(); } bool CtrlIsEmpty() const { return controlStack.empty(); } const std::vector& GetCtrlStack() const { return controlStack; } size_t CtrlSize() const { return controlStack.size(); } private: /** @brief stack for arguments */ std::vector argStack; /** @brief stack for control flow */ std::vector controlStack; }; } // namespace Cangjie::CHIR::Interpreter #endif // CANGJIE_CHIR_INTERRETER_INTERPRETERSTACK_Hcangjie_compiler-1.0.7/include/cangjie/CHIR/Interpreter/InterpreterValue.h000066400000000000000000000100401510705540100265260ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares interpreter values. */ #ifndef CANGJIE_CHIR_INTERRETER_INTERPREVERVALUE_H #define CANGJIE_CHIR_INTERRETER_INTERPREVERVALUE_H #include #include #include namespace Cangjie::CHIR::Interpreter { struct IInvalid; struct IUInt8; struct IUInt16; struct IUInt32; struct IUInt64; struct IUIntNat; struct IInt8; struct IInt16; struct IInt32; struct IInt64; struct IIntNat; struct IFloat16; struct IFloat32; struct IFloat64; struct IRune; struct IBool; struct IUnit; struct INullptr; struct IPointer; struct ITuple; struct IArray; struct IObject; // These types correspond to the ones above, but where memory management is // implemented by the user, they make the IValStack variant behave nicely // (ie have trivial destructor and constructors) struct ITuplePtr; struct IArrayPtr; struct IObjectPtr; struct IFunc; using IVal = std::variant< // Primitives IInvalid, // 0 IUInt8, // 1 IUInt16, // 2 IUInt32, // 3 IUInt64, // 4 IUIntNat, // 5 IInt8, // 6 IInt16, // 7 IInt32, // 8 IInt64, // 9 IIntNat, // 10 IFloat16, // 11 IFloat32, // 12 IFloat64, // 13 IRune, // 14 IBool, // 15 IUnit, // 16 INullptr, // 17 // Pointers (the values of atoms that point to values, see later about heap allocations) IPointer, // 18 // Aggregates ITuple, // 19 IArray, // 20 IObject, // 21 // Opaque things (functions, names) IFunc // 22 >; using IValStack = std::variant< // Primitives IInvalid, // 0 IUInt8, // 1 IUInt16, // 2 IUInt32, // 3 IUInt64, // 4 IUIntNat, // 5 IInt8, // 6 IInt16, // 7 IInt32, // 8 IInt64, // 9 IIntNat, // 10 IFloat16, // 11 IFloat32, // 12 IFloat64, // 13 IRune, // 14 IBool, // 15 IUnit, // 16 INullptr, // 17 // Pointers (the values of atoms that point to values, see later about heap allocations) IPointer, // 18 // Aggregates ITuplePtr, // 19 IArrayPtr, // 20 IObjectPtr, // 21 // Opaque things (functions, names) IFunc // 22 >; // Note: in the following structs we are not making the content field const, otherwise // operator= becomes deleted. This is being used for instance in InterpreterStack::ArgsSet. // In the future we can possibly eliminate this API method if there is a performance gain. struct IInvalid { }; struct IUInt8 { std::uint8_t content; }; struct IUInt16 { std::uint16_t content; }; struct IUInt32 { std::uint32_t content; }; struct IUInt64 { std::uint64_t content; }; struct IUIntNat { std::size_t content; }; struct IInt8 { std::int8_t content; }; struct IInt16 { std::int16_t content; }; struct IInt32 { std::int32_t content; }; struct IInt64 { std::int64_t content; }; struct IIntNat { #if (defined(__x86_64__) || defined(__aarch64__)) std::int64_t content; #else std::int32_t content; #endif }; struct IFloat16 { float content; }; struct IFloat32 { float content; }; struct IFloat64 { double content; }; struct IRune { char32_t content; }; struct IBool { bool content; }; struct IUnit { }; struct INullptr { }; struct IPointer { IVal* content; }; struct ITuple { std::vector content; }; struct IArray { std::vector content; }; struct IObject { std::uint32_t classId; std::vector content; }; struct ITuplePtr { std::vector* contentPtr; }; struct IArrayPtr { std::vector* contentPtr; }; struct IObjectPtr { std::uint32_t classId; std::vector* contentPtr; }; struct IFunc { std::size_t content; // program pointer to the function declaration }; } // namespace Cangjie::CHIR::Interpreter #endif // CANGJIE_CHIR_INTERRETER_INTERPREVERVALUE_Hcangjie_compiler-1.0.7/include/cangjie/CHIR/Interpreter/InterpreterValueUtils.h000066400000000000000000000215331510705540100275600ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares interpreter values. */ #ifndef CANGJIE_CHIR_INTERRETER_INTERPREVERVALUEUTILS_H #define CANGJIE_CHIR_INTERRETER_INTERPREVERVALUEUTILS_H #include "cangjie/CHIR/Interpreter/InterpreterValue.h" #include "cangjie/CHIR/Interpreter/BCHIR.h" namespace Cangjie::CHIR::Interpreter { struct IValUtils { template inline static constexpr T& Get(IVal& v) { return std::get(v); } template inline static constexpr T&& Get(IVal&& v) { return std::get(std::move(v)); } template inline static constexpr const T& Get(const IVal& v) { return std::get(v); } template inline static constexpr std::add_pointer_t GetIf(IVal* pv) noexcept { return std::get_if(pv); } template inline static constexpr std::add_pointer_t GetIf(const IVal* pv) noexcept { return std::get_if(pv); } /** @brief Print Interpreter values */ static void Printer(const IVal& v, std::ostream& os = std::cout); /** @brief Transform value to a string * * This function is not used by the interpreter directly, but it's * necessary when debugging the interpreter runtime */ static std::string ToString(const IVal& v); /** @brief Create an interpreter primitive value with content given in the argument */ template static T PrimitiveValue(K value) { if constexpr (std::is_same::value) { return PrimitiveInt8(value); } else if constexpr (std::is_same::value) { return PrimitiveInt16(value); } else if constexpr (std::is_same::value) { return PrimitiveInt32(value); } else if constexpr (std::is_same::value) { return PrimitiveInt64(value); } else if constexpr (std::is_same::value) { return PrimitiveIntNat(value); } else if constexpr (std::is_same::value) { return PrimitiveUInt8(value); } else if constexpr (std::is_same::value) { return PrimitiveUInt16(value); } else if constexpr (std::is_same::value) { return PrimitiveUInt32(value); } else if constexpr (std::is_same::value) { return PrimitiveUInt64(value); } else if constexpr (std::is_same::value) { return PrimitiveUIntNat(value); } else if constexpr (std::is_same::value) { return PrimitiveFloat16(value); } else if constexpr (std::is_same::value) { return PrimitiveFloat32(value); } else if constexpr (std::is_same::value) { return PrimitiveFloat64(value); } else if constexpr (std::is_same::value) { return PrimitiveBool(value); } else if constexpr (std::is_same::value) { return PrimitiveRune(value); } else { CJC_ABORT(); } } /** @brief Create an interpreter primitive values of some Type with the content given in the argument */ template static IVal PrimitiveOfType(CHIR::Type& ty, K value) { switch (ty.GetTypeKind()) { case CHIR::Type::TypeKind::TYPE_UINT8: return PrimitiveValue(value); case CHIR::Type::TypeKind::TYPE_UINT16: return PrimitiveValue(value); case CHIR::Type::TypeKind::TYPE_UINT32: return PrimitiveValue(value); case CHIR::Type::TypeKind::TYPE_ENUM: case CHIR::Type::TypeKind::TYPE_UINT64: return PrimitiveValue(value); case CHIR::Type::TypeKind::TYPE_UINT_NATIVE: return PrimitiveValue(value); case CHIR::Type::TypeKind::TYPE_INT8: return PrimitiveValue(value); case CHIR::Type::TypeKind::TYPE_INT16: return PrimitiveValue(value); case CHIR::Type::TypeKind::TYPE_INT32: return PrimitiveValue(value); case CHIR::Type::TypeKind::TYPE_INT64: return PrimitiveValue(value); case CHIR::Type::TypeKind::TYPE_INT_NATIVE: #if (defined(__x86_64__) || defined(__aarch64__)) return PrimitiveValue(value); #else return PrimitiveValue(value); #endif case CHIR::Type::TypeKind::TYPE_FLOAT16: return PrimitiveValue(value); case CHIR::Type::TypeKind::TYPE_FLOAT32: return PrimitiveValue(value); case CHIR::Type::TypeKind::TYPE_FLOAT64: return PrimitiveValue(value); default: { CJC_ABORT(); } } return INullptr(); } static ITuple CreateCPointer(size_t ptr) { return ITuple{{IValUtils::PrimitiveValue(ptr)}}; } /** * @brief Creates an array representation of a string, as used on Cangjie's core */ static IArray StringToArray(const std::string& str) { auto array = IArray(); array.content.reserve(str.size() + 1); array.content.emplace_back(IValUtils::PrimitiveValue(static_cast(str.size()))); for (auto& c : str) { array.content.emplace_back(IValUtils::PrimitiveValue(static_cast(c))); } return array; } /** @brief returns the number of bits of an IVal */ template static size_t SizeOf() { if constexpr (std::is_same::value || std::is_same::value) { return sizeof(uint8_t) * CHAR_BIT; } else if constexpr (std::is_same::value || std::is_same::value) { return sizeof(uint16_t) * CHAR_BIT; } else if constexpr (std::is_same::value || std::is_same::value) { return sizeof(uint32_t) * CHAR_BIT; } else if constexpr (std::is_same::value || std::is_same::value) { return sizeof(uint64_t) * CHAR_BIT; } else if constexpr (std::is_same::value || std::is_same::value) { return sizeof(size_t) * CHAR_BIT; } else { CJC_ABORT(); } } private: /** @brief Auxiliary printing function */ static void PrintVector(const std::vector& vec, std::ostream& os); /** @brief Auxiliary printing function */ static void PrintNonNumeric(const IVal& v, std::ostream& os); inline static IInt8 PrimitiveInt8(int8_t value) { return IInt8{value}; } inline static IInt16 PrimitiveInt16(int16_t value) { return IInt16{value}; } inline static IInt32 PrimitiveInt32(int32_t value) { return IInt32{value}; } inline static IInt64 PrimitiveInt64(int64_t value) { return IInt64{value}; } #if (defined(__x86_64__) || defined(__aarch64__)) inline static IIntNat PrimitiveIntNat(int64_t value) #else inline static IIntNat PrimitiveIntNat(int32_t value) #endif { return IIntNat{value}; } inline static IUInt8 PrimitiveUInt8(uint8_t value) { return IUInt8{value}; } inline static IUInt16 PrimitiveUInt16(uint16_t value) { return IUInt16{value}; } inline static IUInt32 PrimitiveUInt32(uint32_t value) { return IUInt32{value}; } inline static IUInt64 PrimitiveUInt64(uint64_t value) { return IUInt64{value}; } inline static IUIntNat PrimitiveUIntNat(size_t value) { return IUIntNat{value}; } inline static IFloat16 PrimitiveFloat16(float value) { return IFloat16{value}; } inline static IFloat32 PrimitiveFloat32(float value) { return IFloat32{value}; } inline static IFloat64 PrimitiveFloat64(double value) { return IFloat64{value}; } inline static IBool PrimitiveBool(bool value) { return IBool{value}; } inline static IRune PrimitiveRune(char32_t value) { return IRune{value}; } }; } // namespace Cangjie::CHIR::Interpreter #endif // CANGJIE_CHIR_INTERRETER_INTERPREVERVALUE_UTILS_Hcangjie_compiler-1.0.7/include/cangjie/CHIR/Interpreter/OpCodes.h000066400000000000000000000032331510705540100245700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the bytecode operation codes for the BCHIR interpreter. */ #ifndef CANGJIE_CHIR_INTERPRETER_OPCODES_H #define CANGJIE_CHIR_INTERPRETER_OPCODES_H #include "cangjie/Utils/Utils.h" namespace Cangjie::CHIR::Interpreter { enum class OpCode { #define OPCODE(ID, VALUE, SIZE, HAS_EXC_HANDLER) ID, #include "cangjie/CHIR/Interpreter/OpCodes.inc" #undef OPCODE }; const std::string OpCodeLabel[static_cast(OpCode::INVALID) + 1]{ #define OPCODE(ID, VALUE, SIZE, HAS_EXC_HANDLER) (VALUE), #include "cangjie/CHIR/Interpreter/OpCodes.inc" #undef OPCODE }; const uint32_t OpCodeArgSize[static_cast(OpCode::INVALID) + 1]{ #define OPCODE(ID, VALUE, SIZE, HAS_EXC_HANDLER) (SIZE), #include "cangjie/CHIR/Interpreter/OpCodes.inc" #undef OPCODE }; constexpr bool OpHandlesException[static_cast(OpCode::INVALID) + 1]{ #define OPCODE(ID, VALUE, SIZE, HAS_EXC_HANDLER) (HAS_EXC_HANDLER), #include "cangjie/CHIR/Interpreter/OpCodes.inc" #undef OPCODE }; uint32_t inline GetOpCodeArgSize(OpCode opCode) { return OpCodeArgSize[static_cast(opCode)]; } std::string inline GetOpCodeLabel(OpCode opCode) { return OpCodeLabel[static_cast(opCode)]; } constexpr bool inline OpHasExceptionHandler(OpCode opCode) { return OpHandlesException[static_cast(opCode)]; } }; // namespace Cangjie::CHIR::Interpreter #endif // CANGJIE_CHIR_INTERPRETER_OPCODES_H cangjie_compiler-1.0.7/include/cangjie/CHIR/Interpreter/OpCodes.inc000066400000000000000000000157221510705540100251200ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file lists bytecode interpreter operation codes. */ // OPCODE(op code id, op code value for human reading, number of additional bytecode arguments, handles exception) #ifdef OPCODE // Enviroment accessors OPCODE(GVAR, "GVAR", 1, false) // global variable OPCODE(LVAR, "LVAR", 1, false) // local variable // Enviroment Setters OPCODE(GVAR_SET, "GVAR_SET", 1, false) // global variable OPCODE(LVAR_SET, "LVAR_SET", 1, false) // local variable // Memory related OPCODE(ALLOCATE_CLASS, "ALLOCATE_CLASS", 2, false) // id, fields OPCODE(ALLOCATE_CLASS_EXC, "ALLOCATE_CLASS_EXC", 3, true) // id, fields, jump index for exception OPCODE(ALLOCATE_STRUCT, "ALLOCATE_STRUCT", 1, false) // number of fields OPCODE(ALLOCATE_STRUCT_EXC, "ALLOCATE_STRUCT_EXC", 2, true) // fields, jump index for exception OPCODE(ALLOCATE_RAW_ARRAY, "ALLOCATE_RAW_ARRAY", 0, false) OPCODE(ALLOCATE_RAW_ARRAY_EXC, "ALLOCATE_RAW_ARRAY_EXC", 1, true) OPCODE(ALLOCATE_RAW_ARRAY_LITERAL, "ALLOCATE_RAW_ARRAY_LITERAL", 1, false) // array size OPCODE(ALLOCATE_RAW_ARRAY_LITERAL_EXC, "ALLOCATE_RAW_ARRAY_LITERAL_EXC", 2, true) // array size OPCODE(RAW_ARRAY_LITERAL_INIT, "RAW_ARRAY_LITERAL_INIT", 1, false) // array size OPCODE(RAW_ARRAY_INIT_BY_VALUE, "RAW_ARRAY_INIT_BY_VALUE", 0, false) OPCODE(ALLOCATE, "ALLOCATE", 0, false) OPCODE(ALLOCATE_EXC, "ALLOCATE_EXC", 1, true) // jump index for exception OPCODE(FRAME, "FRAME", 1, false) // Constructors OPCODE(UINT8, "UINT8", 1, false) OPCODE(UINT16, "UINT16", 1, false) OPCODE(UINT32, "UINT32", 1, false) OPCODE(UINT64, "UINT64", 2, false) // need 2 cells to store uint64_t value OPCODE(UINTNAT, "UINTNAT", 2, false) // need 2 cells to store uint native value in a 64-bit machine OPCODE(INT8, "INT8", 1, false) OPCODE(INT16, "INT16", 1, false) OPCODE(INT32, "INT32", 1, false) OPCODE(INT64, "INT64", 2, false) // need 2 cells to store int64_t value OPCODE(INTNAT, "INTNAT", 2, false) // need 2 cells to store int native value in a 64-bit machine OPCODE(FLOAT16, "FLOAT16", 1, false) OPCODE(FLOAT32, "FLOAT32", 1, false) OPCODE(FLOAT64, "FLOAT64", 2, false) // need 2 cells to store double value OPCODE(RUNE, "RUNE", 1, false) OPCODE(BOOL, "BOOL", 1, false) OPCODE(UNIT, "UNIT", 0, false) OPCODE(NULLPTR, "NULLPTR", 0, false) OPCODE(STRING, "STRING", 1, false) OPCODE(TUPLE, "TUPLE", 1, false) OPCODE(ARRAY, "ARRAY", 1, false) OPCODE(VARRAY, "VARRAY", 1, false) // size OPCODE(VARRAY_BY_VALUE, "VARRAY_BY_VALUE", 0, false) OPCODE(VARRAY_GET, "VARRAY_GET", 1, false) // path size OPCODE(FUNC, "FUNC", 1, false) // Terminators OPCODE(RETURN, "RETURN", 0, false) // return to call site OPCODE(JUMP, "JUMP", 1, false) // Jump to subsequent index OPCODE(BRANCH, "BRANCH", 2, false) // Branch // Interpreter specific OPCODE(EXIT, "EXIT", 0, false) OPCODE(STORE, "STORE", 0, false) // same as ASG but does not produce result OPCODE(DROP, "DROP", 0, false) // Drop operation from argument stack OPCODE(GETREF, "GETREF", 1, false) // path size + variable sized path OPCODE(STOREINREF, "STOREINREF", 1, false) // path size + variable sized path // Active Operations // Unary operations OPCODE(UN_NEG, "UN_NEG", 2, false) // type kind, overflow strategy OPCODE(UN_NEG_EXC, "UN_NEG_EXC", 3, true) // type kind, overflow strategy, jump index for exception OPCODE(UN_NOT, "UN_NOT", 2, false) OPCODE(UN_BITNOT, "UN_BITNOT", 2, false) OPCODE(UN_INC, "UN_INC", 2, false) OPCODE(UN_DEC, "UN_DEC", 2, false) // Binary operations OPCODE(BIN_ADD, "BIN_ADD", 2, false) // type kind, overflow strategy OPCODE(BIN_SUB, "BIN_SUB", 2, false) OPCODE(BIN_MUL, "BIN_MUL", 2, false) OPCODE(BIN_DIV, "BIN_DIV", 2, false) OPCODE(BIN_MOD, "BIN_MOD", 2, false) OPCODE(BIN_EXP, "BIN_EXP", 2, false) OPCODE(BIN_ADD_EXC, "BIN_ADD_EXC", 3, true) // type kind, overflow strategy, jump index for exception OPCODE(BIN_SUB_EXC, "BIN_SUB_EXC", 3, true) OPCODE(BIN_MUL_EXC, "BIN_MUL_EXC", 3, true) OPCODE(BIN_DIV_EXC, "BIN_DIV_EXC", 3, true) OPCODE(BIN_MOD_EXC, "BIN_MOD_EXC", 3, true) OPCODE(BIN_EXP_EXC, "BIN_EXP_EXC", 3, true) OPCODE(BIN_LT, "BIN_LT", 2, false) OPCODE(BIN_GT, "BIN_GT", 2, false) OPCODE(BIN_LE, "BIN_LE", 2, false) OPCODE(BIN_GE, "BIN_GE", 2, false) OPCODE(BIN_NOTEQ, "BIN_NOTEQ", 2, false) OPCODE(BIN_EQUAL, "BIN_EQUAL", 2, false) OPCODE(BIN_BITAND, "BIN_BITAND", 2, false) OPCODE(BIN_BITOR, "BIN_BITOR", 2, false) OPCODE(BIN_BITXOR, "BIN_BITXOR", 2, false) OPCODE(BIN_LSHIFT, "BIN_LSHIFT", 3, false) // type kind, overflow strategy, right operand type kind OPCODE(BIN_RSHIFT, "BIN_RSHIFT", 3, false) OPCODE(BIN_LSHIFT_EXC, "BIN_LSHIFT_EXC", 4, true) // type kind, overflow strategy, right operand type kind, exception OPCODE(BIN_RSHIFT_EXC, "BIN_RSHIFT_EXC", 4, true) // All other operations OPCODE(FIELD, "FIELD", 1, false) OPCODE(FIELD_TPL, "FIELD_TPL", 1, false) // number of indexes n + n indexes OPCODE(INVOKE, "INVOKE", 2, false) // number of arguments and method name OPCODE(INVOKE_EXC, "INVOKE_EXC", 3, true) // number of arguments, method name, jump index for exception OPCODE(TYPECAST, "TYPECAST", 3, false) // source type kind, target type kind, overflow stategy OPCODE(TYPECAST_EXC, "TYPECAST_EXC", 4, true) // src + target type kind, overflow strat, jump index for exception OPCODE(INSTANCEOF, "INSTANCEOF", 1, false) OPCODE(APPLY, "APPLY", 1, false) OPCODE(APPLY_EXC, "APPLY_EXC", 2, true) // number of arguments and and jump index for exception OPCODE(CAPPLY, "CAPPLY", 1, false) // same as APPLY but used for CFunc OPCODE(ASG, "ASG", 0, false) OPCODE(DEREF, "DEREF", 0, false) OPCODE(RAISE, "RAISE", 0, false) OPCODE(RAISE_EXC, "RAISE_EXC", 1, true) // taraget block OPCODE(GET_EXCEPTION, "GET_EXCEPTION", 0, true) OPCODE(SYSCALL, "SYSCALL", 2, false) OPCODE(INTRINSIC0, "INTRINSIC0", 1, false) // intrinsic ID OPCODE(INTRINSIC1, "INTRINSIC1", 2, false) // intrinsic ID, extra info 1 OPCODE(INTRINSIC2, "INTRINSIC2", 3, false) // intrinsic ID, extra info 1, extra info 2 // static_cast(INTRINSICx_EXC) should be precisely static_cast(INTRINSICx) + 3 OPCODE(INTRINSIC0_EXC, "INTRINSIC0_EXC", 2, true) // intrinsic ID, jump index for exception OPCODE(INTRINSIC1_EXC, "INTRINSIC1_EXC", 3, true) // intrinsic ID, extra info 1, jump index for exception OPCODE(INTRINSIC2_EXC, "INTRINSIC2_EXC", 4, true) // intrinsic ID, extra info 1, extra info 2, jump index for exception OPCODE(SWITCH, "SWITCH", 2, false) // value type, number of cases OPCODE(SPAWN, "SPAWN", 0, false) OPCODE(SPAWN_EXC, "SPAWN_EXC", 1, true) // jump index for exception // box/unbox and generics OPCODE(BOX, "BOX", 1, false) // class ID OPCODE(UNBOX, "UNBOX", 0, false) OPCODE(UNBOX_REF, "UNBOX_REF", 0, false) OPCODE(NOT_SUPPORTED, "NOT_SUPPORTED", 0, false) OPCODE(ABORT, "ABORT", 0, false) // abort interpretation, can exist only in const eval BCHIR but shouldn't be reached OPCODE(INVALID, "INVALID", 0, false) #endif // OPCODE cangjie_compiler-1.0.7/include/cangjie/CHIR/Interpreter/Utils.h000066400000000000000000000031501510705540100243320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares some utility functions for interpreter module. */ #ifndef CANGJIE_CHIR_INTERRETER_UTILS_H #define CANGJIE_CHIR_INTERRETER_UTILS_H #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Interpreter/BCHIR.h" #include "cangjie/CHIR/Interpreter/InterpreterValue.h" #include "cangjie/CHIR/Interpreter/OpCodes.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/Type.h" namespace Cangjie::CHIR::Interpreter { OpCode PrimitiveTypeKind2OpCode(Type::TypeKind kind); OpCode UnExprKind2OpCode(Cangjie::CHIR::ExprKind exprKind); OpCode BinExprKind2OpCode(Cangjie::CHIR::ExprKind exprKind); OpCode BinExprKindWitException2OpCode(Cangjie::CHIR::ExprKind exprKind); IVal ByteCodeToIval(const Bchir::Definition& def, const Bchir& bchir, Bchir& topBchir); template std::string MangleMethodName(const std::string& methodName, const FuncType& funcTy) { // T0D0: instead we can change SVTable so that the key is pair std::string res = methodName + "("; size_t start = 0; if constexpr (OmitFirstArg) { start = 1; } auto paramTys = funcTy.GetParamTypes(); for (size_t i = start; i < paramTys.size(); i++) { res += paramTys[i]->ToString() + " "; } res += ")"; return res; } } #endif // CANGJIE_CHIR_INTERRETER_BCHIR_H cangjie_compiler-1.0.7/include/cangjie/CHIR/IntrinsicKind.h000066400000000000000000000736231510705540100235130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the CHIR IntrinsicKind. */ #ifndef CANGJIE_CHIR_INTRINSICKIND_H #define CANGJIE_CHIR_INTRINSICKIND_H #include "cangjie/Utils/ConstantsUtils.h" #include #include namespace Cangjie::CHIR { /** * @brief Comparing the func name and package name is not a general way to distinguish * between different intrinsics. It requires there are no duplicate function name in the * same package. * * We should move to a more general method, such as adding a @collect["some name"] * annotation in CJ so that parser will collect the corresponding Decl into a map. * This is also useful in CHIR where we need to find specific declarations by name. * (suggested by David) * */ // CORE static const std::string SIZE_OF_NAME = "sizeOfType"; static const std::string ALIGN_OF_NAME = "alignOfType"; static const std::string ARRAY_ACQUIRE_RAW_DATA_NAME = "acquireRawData"; static const std::string ARRAY_RELEASE_RAW_DATA_NAME = "releaseRawData"; static const std::string ARRAY_BUILT_IN_COPY_TO_NAME = "intrinsicBuiltInCopyTo"; static const std::string ARRAY_GET_NAME = "arrayGet"; static const std::string ARRAY_SET_NAME = "arraySet"; static const std::string ARRAY_GET_UNCHECKED_NAME = "arrayGetUnchecked"; static const std::string ARRAY_GET_REF_UNCHECKED_NAME = "arrayGetRefUnchecked"; // cjnative only static const std::string ARRAY_SET_UNCHECKED_NAME = "arraySetUnchecked"; static const std::string ARRAY_SIZE_NAME = "arraySize"; static const std::string ARRAY_CLONE_NAME = "arrayClone"; static const std::string ARRAY_SLICE_INIT_NAME = "arraySliceInit"; static const std::string ARRAY_SLICE_NAME = "arraySlice"; static const std::string ARRAY_SLICE_RAWARRAY_NAME = "rawArrayOfArraySlice"; static const std::string ARRAY_SLICE_START_NAME = "startOfArraySlice"; static const std::string ARRAY_SLICE_SIZE_NAME = "sizeOfArraySlice"; static const std::string ARRAY_SLICE_GET_ELEMENT_NAME = "arraySliceGet"; static const std::string ARRAY_SLICE_SET_ELEMENT_NAME = "arraySliceSet"; static const std::string ARRAY_SLICE_GET_ELEMENT_UNCHECKED_NAME = "arraySliceGetUnchecked"; static const std::string ARRAY_SLICE_SET_ELEMENT_UNCHECKED_NAME = "arraySliceSetUnchecked"; // vector intrinsic for array operation static const std::string VECTOR_COMPARE_32_NAME = "vectorCompare32"; // cjnative only static const std::string VECTOR_INDEX_BYTE_32_NAME = "vectorIndexByte32"; // cjnative only // Foreign functions translated to intrinsic functions in the interpreter // These functions are not not marked as intrinsic in Cangjie but they are translated to intrinsics in BCHIR const std::string CJ_CORE_CAN_USE_SIMD_NAME = "CJ_CORE_CanUseSIMD"; // cjnative only static const std::string CJ_TLS_DYN_SET_SESSION_CALLBACK_NAME = "CJ_TLS_DYN_SetSessionCallback"; static const std::string CJ_TLS_DYN_SSL_INIT_NAME = "CJ_TLS_DYN_SslInit"; static const std::string FILL_IN_STACK_TRACE_NAME = "fillInStackTrace"; static const std::string DECODE_STACK_TRACE_NAME = "decodeStackTrace"; static const std::string CPOINTER_GET_POINTER_ADDRESS_NAME = "getPointerAddress"; static const std::string CPOINTER_READ_NAME = "readPointer"; static const std::string CPOINTER_WRITE_NAME = "writePointer"; static const std::string CPOINTER_ADD_NAME = "addPointer"; static const std::string CSTRING_CONVERT_CSTR_TO_PTR_NAME = "convertCStr2Ptr"; static const std::string BIT_CAST_NAME = "bitCast"; /* * Used for the mock framework purpuses, to match concrete types of type parameters to the defined stubs * The intrinsic doesn't have a declaration and for its call, * it's intended to be generated dynamically in the compiler front-end */ static const std::string GET_TYPE_FOR_TYPE_PARAMETER_NAME = GET_TYPE_FOR_TYPE_PARAMETER_FUNC_NAME; static const std::string IS_SUBTYPE_TYPES_NAME = IS_SUBTYPE_TYPES_FUNC_NAME; // Package runtime static const std::string INVOKE_GC_NAME = "invokeGC"; static const std::string SET_GC_THRESHOLD_NAME = "setCjGCThreshold"; static const std::string GET_MAX_HEAP_SIZE_NAME = "getMaxCjHeapSize"; static const std::string GET_ALLOCATE_HEAP_SIZE_NAME = "getAllocatedCjHeapSize"; static const std::string DUMP_CJ_HEAP_DATA_NAME = "dumpCjHeapData"; static const std::string GET_GC_COUNT_NAME = "getCjGCCount"; static const std::string GET_GC_TIME_US_NAME = "getCjGCTime"; static const std::string GET_GC_FREED_SIZE_NAME = "getCjGCFreedSize"; static const std::string START_CJ_CPU_PROFILING_NAME = "startCjCPUProfiling"; static const std::string STOP_CJ_CPU_PROFILING_NAME = "stopCjCPUProfiling"; static const std::string GET_REAL_HEAP_SIZE_NAME = "getRealHeapSize"; static const std::string GET_THREAD_NUMBER_NAME = "getThreadNumber"; static const std::string GET_BLOCKING_THREAD_NUMBER_NAME = "getBlockingThreadNumber"; static const std::string GET_NATIVE_THREAD_NUMBER_NAME = "getNativeThreadNumber"; static const std::string FUTURE_INIT_NAME = "futureInit"; static const std::string OBJECT_REFEQ_NAME = "intrinsicRefEq"; static const std::string RAW_ARRAY_REFEQ_NAME = "intrinsicRefEqRawArray"; // cjnative only static const std::string OBJECT_ZERO_VALUE_NAME = "zeroValue"; static const std::string IS_THREAD_OBJECT_INITED_NAME = "isThreadObjectInited"; static const std::string GET_THREAD_OBJECT_NAME = "getThreadObject"; static const std::string SET_THREAD_OBJECT_NAME = "setThreadObject"; static const std::string FUTURE_IS_COMPLETE_NAME = "futureIsComplete"; // cjnative only static const std::string FUTURE_WAIT_NAME = "futureWait"; // cjnative only static const std::string FUTURE_NOTIFYALL_NAME = "futureNotifyAll"; // cjnative only // ============================ cjnative only start ============================== // REFLECTION #define REFLECTION_KIND_TO_RUNTIME_FUNCTION(REFLECTION_KIND, CJ_FUNCTION, RUNTIME_FUNCTION) \ static const std::string REFLECTION_KIND##_NAME = #CJ_FUNCTION; #include "cangjie/CHIR/LLVMReflectionIntrinsics.def" #undef REFLECTION_KIND_TO_RUNTIME_FUNCTION // ============================ cjnative only end ============================== // these intrinsics will be removed in later version static const std::string OVERFLOW_CHECKED_ADD_NAME = "checkedAdd"; static const std::string OVERFLOW_CHECKED_SUB_NAME = "checkedSub"; static const std::string OVERFLOW_CHECKED_MUL_NAME = "checkedMul"; static const std::string OVERFLOW_CHECKED_DIV_NAME = "checkedDiv"; static const std::string OVERFLOW_CHECKED_MOD_NAME = "checkedMod"; static const std::string OVERFLOW_CHECKED_POW_NAME = "checkedPow"; static const std::string OVERFLOW_CHECKED_INC_NAME = "checkedInc"; static const std::string OVERFLOW_CHECKED_DEC_NAME = "checkedDec"; static const std::string OVERFLOW_CHECKED_NEG_NAME = "checkedNeg"; static const std::string OVERFLOW_THROWING_ADD_NAME = "throwingAdd"; static const std::string OVERFLOW_THROWING_SUB_NAME = "throwingSub"; static const std::string OVERFLOW_THROWING_MUL_NAME = "throwingMul"; static const std::string OVERFLOW_THROWING_DIV_NAME = "throwingDiv"; static const std::string OVERFLOW_THROWING_MOD_NAME = "throwingMod"; static const std::string OVERFLOW_THROWING_POW_NAME = "throwingPow"; static const std::string OVERFLOW_THROWING_INC_NAME = "throwingInc"; static const std::string OVERFLOW_THROWING_DEC_NAME = "throwingDec"; static const std::string OVERFLOW_THROWING_NEG_NAME = "throwingNeg"; static const std::string OVERFLOW_SATURATING_ADD_NAME = "saturatingAdd"; static const std::string OVERFLOW_SATURATING_SUB_NAME = "saturatingSub"; static const std::string OVERFLOW_SATURATING_MUL_NAME = "saturatingMul"; static const std::string OVERFLOW_SATURATING_DIV_NAME = "saturatingDiv"; static const std::string OVERFLOW_SATURATING_MOD_NAME = "saturatingMod"; static const std::string OVERFLOW_SATURATING_POW_NAME = "saturatingPow"; static const std::string OVERFLOW_SATURATING_INC_NAME = "saturatingInc"; static const std::string OVERFLOW_SATURATING_DEC_NAME = "saturatingDec"; static const std::string OVERFLOW_SATURATING_NEG_NAME = "saturatingNeg"; static const std::string OVERFLOW_WRAPPING_ADD_NAME = "wrappingAdd"; static const std::string OVERFLOW_WRAPPING_SUB_NAME = "wrappingSub"; static const std::string OVERFLOW_WRAPPING_MUL_NAME = "wrappingMul"; static const std::string OVERFLOW_WRAPPING_DIV_NAME = "wrappingDiv"; static const std::string OVERFLOW_WRAPPING_MOD_NAME = "wrappingMod"; static const std::string OVERFLOW_WRAPPING_POW_NAME = "wrappingPow"; static const std::string OVERFLOW_WRAPPING_INC_NAME = "wrappingInc"; static const std::string OVERFLOW_WRAPPING_DEC_NAME = "wrappingDec"; static const std::string OVERFLOW_WRAPPING_NEG_NAME = "wrappingNeg"; static const std::string SLEEP_NAME = "intrinsicSleep"; static const std::string SOURCE_FILE_NAME = "sourceFile"; static const std::string SOURCE_LINE_NAME = "sourceLine"; static const std::string IDENTITY_HASHCODE_NAME = "identityHashCodeForRefType"; static const std::string IDENTITY_HASHCODE_FOR_ARRAY_NAME = "identityHashCodeForArrayType"; static const std::string VARRAY_SET_NAME = "varraySet"; static const std::string VARRAY_GET_NAME = "varrayGet"; // ============================ cjnative only start ============================== // SYNC static const std::string ATOMIC_LOAD_NAME = "load"; static const std::string ATOMIC_STORE_NAME = "store"; static const std::string ATOMIC_SWAP_NAME = "swap"; static const std::string ATOMIC_COMPARE_AND_SWAP_NAME = "compareAndSwap"; static const std::string ATOMIC_FETCH_ADD_NAME = "fetchAdd"; static const std::string ATOMIC_FETCH_SUB_NAME = "fetchSub"; static const std::string ATOMIC_FETCH_AND_NAME = "fetchAnd"; static const std::string ATOMIC_FETCH_OR_NAME = "fetchOr"; static const std::string ATOMIC_FETCH_XOR_NAME = "fetchXor"; static const std::string MUTEX_INIT_NAME = "mutexInit"; static const std::string MUTEX_LOCK_NAME = "mutexLock"; static const std::string MUTEX_TRY_LOCK_NAME = "mutexTryLock"; static const std::string MUTEX_CHECK_STATUS_NAME = "mutexCheckStatus"; static const std::string MUTEX_UNLOCK_NAME = "mutexUnlock"; static const std::string WAITQUEUE_INIT_NAME = "waitQueueInit"; static const std::string MONITOR_INIT_NAME = "monitorInit"; static const std::string MOITIOR_WAIT_NAME = "monitorWait"; static const std::string MOITIOR_NOTIFY_NAME = "monitorNotify"; static const std::string MOITIOR_NOTIFY_ALL_NAME = "monitorNotifyAll"; static const std::string MULTICONDITION_WAIT_NAME = "multiConditionMonitorWait"; static const std::string MULTICONDITION_NOTIFY_NAME = "multiConditionMonitorNotify"; static const std::string MULTICONDITION_NOTIFY_ALL_NAME = "multiConditionMonitorNotifyAll"; // ============================ cjnative only end ============================== // Math static const std::string ABS_NAME = "intrinsicAbs"; static const std::string FABS_NAME = "intrinsicFabs"; static const std::string FLOOR_NAME = "intrinsicFloor"; static const std::string CEIL_NAME = "intrinsicCeil"; static const std::string TRUC_NAME = "intrinsicTrunc"; static const std::string SIN_NAME = "intrinsicSin"; static const std::string COS_NAME = "intrinsicCos"; static const std::string EXP_NAME = "intrinsicExp"; static const std::string EXP2_NAME = "intrinsicExp2"; static const std::string LOG_NAME = "intrinsicLog"; static const std::string LOG2_NAME = "intrinsicLog2"; static const std::string LOG10_NAME = "intrinsicLog10"; static const std::string SQRT_NAME = "intrinsicSqrt"; static const std::string ROUND_NAME = "intrinsicRound"; static const std::string POW_NAME = "intrinsicPow"; static const std::string POWI_NAME = "intrinsicPowi"; /* C FFI functions These C functions are used in the standard library but never defined. We consider them intrinsic, similar to FFI_CJ_AST_*, so that we can link them statically in the interpreter. */ static const std::string STRLEN_NAME = "strlen"; static const std::string MEMCPY_S_NAME = "memcpy_s"; static const std::string MEMSET_S_NAME = "memset_s"; static const std::string FREE_NAME = "free"; static const std::string MALLOC_NAME = "malloc"; static const std::string STRCMP_NAME = "strcmp"; static const std::string MEMCMP_NAME = "memcmp"; static const std::string STRNCMP_NAME = "strncmp"; static const std::string STRCASECMP_NAME = "strcasecmp"; static const std::string UNSAFE_BEGIN = "_unsafe_begin"; static const std::string UNSAFE_END = "_unsafe_end"; static const std::string POINTER_INIT = "_Pointer_Init_"; static const std::string CSTR_INIT = "_CString_Init_"; static const std::string CJ_AST_LEX = "CJ_AST_Lex"; static const std::string CJ_ASTPARSEEXPR = "CJ_AST_ParseExpr"; static const std::string CJ_ASTPARSEDECL = "CJ_AST_ParseDecl"; static const std::string CJ_ASTPARSE_PROPMEMBERDECL = "CJ_AST_ParsePropMemberDecl"; static const std::string CJ_ASTPARSE_PRICONSTRUCTOR = "CJ_AST_ParsePrimaryConstructor"; static const std::string CJ_ASTPARSEPATTERN = "CJ_AST_ParsePattern"; static const std::string CJ_ASTPARSETYPE = "CJ_AST_ParseType"; static const std::string CJ_ASTPARSETOPLEVEL = "CJ_AST_ParseTopLevel"; static const std::string CJ_PARENT_CONTEXT = "CJ_CheckParentContext"; static const std::string CJ_MACRO_ITEM_INFO = "CJ_SetItemInfo"; static const std::string CJ_GET_CHILD_MESSAGES = "CJ_GetChildMessages"; static const std::string CJ_CHECK_ADD_SPACE = "CJ_CheckAddSpace"; static const std::string CJ_AST_DIAGREPORT = "CJ_AST_DiagReport"; static const std::string BLACK_BOX_NAME = "blackBox"; // ============================ cjnative only start ============================== static const std::string CROSS_ACCESS_BARRIER_NAME = "CrossAccessBarrier"; static const std::string CREATE_EXPORT_HANDLE_NAME = "CreateExportHandle"; static const std::string GET_EXPORTED_REF_NAME = "GetExportedRef"; static const std::string REMOVE_EXPORTED_REF_NAME = "RemoveExportedRef"; // ============================ cjnative only end ============================== /** * @brief In the future we should generate intrinsic/xxx node in CHIR * to represent intrinsic functions. That way we don't need intrinsic * to have function body as well. We can keep using this enum tho. * */ enum IntrinsicKind : uint16_t { NOT_INTRINSIC, NOT_IMPLEMENTED, // For hoisting, but we should later split arraybuilder // into allocation and initialisation ARRAY_INIT, // CORE SIZE_OF, ALIGN_OF, ARRAY_ACQUIRE_RAW_DATA, ARRAY_RELEASE_RAW_DATA, ARRAY_BUILT_IN_COPY_TO, ARRAY_GET, ARRAY_SET, ARRAY_GET_UNCHECKED, ARRAY_GET_REF_UNCHECKED, ARRAY_SET_UNCHECKED, ARRAY_SIZE, ARRAY_CLONE, ARRAY_SLICE_INIT, ARRAY_SLICE, ARRAY_SLICE_RAWARRAY, ARRAY_SLICE_START, ARRAY_SLICE_SIZE, ARRAY_SLICE_GET_ELEMENT, ARRAY_SLICE_GET_ELEMENT_UNCHECKED, ARRAY_SLICE_SET_ELEMENT, ARRAY_SLICE_SET_ELEMENT_UNCHECKED, FILL_IN_STACK_TRACE, DECODE_STACK_TRACE, CHR, ORD, CPOINTER_GET_POINTER_ADDRESS, CPOINTER_INIT0, // CPointer constructor with no arguments CPOINTER_INIT1, // CPointer constructor with one argument CPOINTER_READ, CPOINTER_WRITE, CPOINTER_ADD, CSTRING_INIT, CSTRING_CONVERT_CSTR_TO_PTR, INOUT_PARAM, REGISTER_WATCHED_OBJECT, OBJECT_REFEQ, RAW_ARRAY_REFEQ, // cjnative only OBJECT_ZERO_VALUE, INVOKE_GC, SET_GC_THRESHOLD, DUMP_CJ_HEAP_DATA, GET_GC_COUNT, GET_GC_TIME_US, GET_GC_FREED_SIZE, START_CJ_CPU_PROFILING, STOP_CJ_CPU_PROFILING, BLACK_BOX, GET_MAX_HEAP_SIZE, GET_ALLOCATE_HEAP_SIZE, GET_REAL_HEAP_SIZE, GET_THREAD_NUMBER, GET_BLOCKING_THREAD_NUMBER, GET_NATIVE_THREAD_NUMBER, VARRAY_SET, VARRAY_GET, // About Future FUTURE_INIT, FUTURE_IS_COMPLETE, // cjnative only FUTURE_WAIT, // cjnative only FUTURE_NOTIFYALL, // cjnative only IS_THREAD_OBJECT_INITED, GET_THREAD_OBJECT, SET_THREAD_OBJECT, OVERFLOW_CHECKED_ADD, OVERFLOW_CHECKED_SUB, OVERFLOW_CHECKED_MUL, OVERFLOW_CHECKED_DIV, OVERFLOW_CHECKED_MOD, OVERFLOW_CHECKED_POW, OVERFLOW_CHECKED_INC, OVERFLOW_CHECKED_DEC, OVERFLOW_CHECKED_NEG, OVERFLOW_THROWING_ADD, OVERFLOW_THROWING_SUB, OVERFLOW_THROWING_MUL, OVERFLOW_THROWING_DIV, OVERFLOW_THROWING_MOD, OVERFLOW_THROWING_POW, OVERFLOW_THROWING_INC, OVERFLOW_THROWING_DEC, OVERFLOW_THROWING_NEG, OVERFLOW_SATURATING_ADD, OVERFLOW_SATURATING_SUB, OVERFLOW_SATURATING_MUL, OVERFLOW_SATURATING_DIV, OVERFLOW_SATURATING_MOD, OVERFLOW_SATURATING_POW, OVERFLOW_SATURATING_INC, OVERFLOW_SATURATING_DEC, OVERFLOW_SATURATING_NEG, OVERFLOW_WRAPPING_ADD, OVERFLOW_WRAPPING_SUB, OVERFLOW_WRAPPING_MUL, OVERFLOW_WRAPPING_DIV, OVERFLOW_WRAPPING_MOD, OVERFLOW_WRAPPING_POW, OVERFLOW_WRAPPING_INC, OVERFLOW_WRAPPING_DEC, OVERFLOW_WRAPPING_NEG, // llvm vector instructions for string optimization VECTOR_COMPARE_32, // cjnative only VECTOR_INDEX_BYTE_32, // cjnative only // Foreign functions translated to intrinsic functions in the interpreter, // These functions are not not marked as intrinsic in Cangjie but they are translated to intrinsics in BCHIR CJ_CORE_CAN_USE_SIMD, // cjnative only CJ_TLS_DYN_SET_SESSION_CALLBACK, CJ_TLS_DYN_SSL_INIT, // ============================ cjnative only start ================= REFLECTION_INTRINSIC_START_FLAG, #define REFLECTION_KIND_TO_RUNTIME_FUNCTION(REFLECTION_KIND, CJ_FUNCTION, RUNTIME_FUNCTION) REFLECTION_KIND, #include "cangjie/CHIR/LLVMReflectionIntrinsics.def" #undef REFLECTION_KIND_TO_RUNTIME_FUNCTION REFLECTION_INTRINSIC_END_FLAG, // ============================ cjnative only end ================= SLEEP, SOURCE_FILE, SOURCE_LINE, IDENTITY_HASHCODE, IDENTITY_HASHCODE_FOR_ARRAY, // ============================ cjnative only start ================= // SYNC ATOMIC_LOAD, ATOMIC_STORE, ATOMIC_SWAP, ATOMIC_COMPARE_AND_SWAP, ATOMIC_FETCH_ADD, ATOMIC_FETCH_SUB, ATOMIC_FETCH_AND, ATOMIC_FETCH_OR, ATOMIC_FETCH_XOR, MUTEX_INIT, CJ_MUTEX_LOCK, MUTEX_TRY_LOCK, MUTEX_CHECK_STATUS, MUTEX_UNLOCK, WAITQUEUE_INIT, MONITOR_INIT, MOITIOR_WAIT, MOITIOR_NOTIFY, MOITIOR_NOTIFY_ALL, MULTICONDITION_WAIT, MULTICONDITION_NOTIFY, MULTICONDITION_NOTIFY_ALL, CROSS_ACCESS_BARRIER, CREATE_EXPORT_HANDLE, GET_EXPORTED_REF, REMOVE_EXPORTED_REF, // ============================ cjnative only end ================= // Syscall // AST lib FFI FFI_CJ_AST_LEX, FFI_CJ_AST_PARSEEXPR, FFI_CJ_AST_PARSEDECL, FFI_CJ_AST_PARSE_PROPMEMBERDECL, FFI_CJ_AST_PARSE_PRICONSTRUCTOR, FFI_CJ_AST_PARSE_PATTERN, FFI_CJ_AST_PARSE_TYPE, FFI_CJ_AST_PARSETOPLEVEL, FFI_CJ_AST_DIAGREPORT, // Macro With Context FFI FFI_CJ_PARENT_CONTEXT, FFI_CJ_MACRO_ITEM_INFO, FFI_CJ_GET_CHILD_MESSAGES, FFI_CJ_CHECK_ADD_SPACE, // CodeGen CG_UNSAFE_BEGIN, CG_UNSAFE_END, // C FFI funcs STRLEN, MEMCPY_S, MEMSET_S, FREE, MALLOC, STRCMP, MEMCMP, STRNCMP, STRCASECMP, // The interpreter is using these for cjnative backend as well ATOMIC_INT8_LOAD, ATOMIC_INT8_STORE, ATOMIC_INT8_SWAP, ATOMIC_INT8_CAS, ATOMIC_INT8_FETCH_ADD, ATOMIC_INT8_FETCH_SUB, ATOMIC_INT8_FETCH_AND, ATOMIC_INT8_FETCH_OR, ATOMIC_INT8_FETCH_XOR, ATOMIC_INT16_LOAD, ATOMIC_INT16_STORE, ATOMIC_INT16_SWAP, ATOMIC_INT16_CAS, ATOMIC_INT16_FETCH_ADD, ATOMIC_INT16_FETCH_SUB, ATOMIC_INT16_FETCH_AND, ATOMIC_INT16_FETCH_OR, ATOMIC_INT16_FETCH_XOR, ATOMIC_INT32_LOAD, ATOMIC_INT32_STORE, ATOMIC_INT32_SWAP, ATOMIC_INT32_CAS, ATOMIC_INT32_FETCH_ADD, ATOMIC_INT32_FETCH_SUB, ATOMIC_INT32_FETCH_AND, ATOMIC_INT32_FETCH_OR, ATOMIC_INT32_FETCH_XOR, ATOMIC_INT64_LOAD, ATOMIC_INT64_STORE, ATOMIC_INT64_SWAP, ATOMIC_INT64_CAS, ATOMIC_INT64_FETCH_ADD, ATOMIC_INT64_FETCH_SUB, ATOMIC_INT64_FETCH_AND, ATOMIC_INT64_FETCH_OR, ATOMIC_INT64_FETCH_XOR, ATOMIC_UINT8_LOAD, ATOMIC_UINT8_STORE, ATOMIC_UINT8_SWAP, ATOMIC_UINT8_CAS, ATOMIC_UINT8_FETCH_ADD, ATOMIC_UINT8_FETCH_SUB, ATOMIC_UINT8_FETCH_AND, ATOMIC_UINT8_FETCH_OR, ATOMIC_UINT8_FETCH_XOR, ATOMIC_UINT16_LOAD, ATOMIC_UINT16_STORE, ATOMIC_UINT16_SWAP, ATOMIC_UINT16_CAS, ATOMIC_UINT16_FETCH_ADD, ATOMIC_UINT16_FETCH_SUB, ATOMIC_UINT16_FETCH_AND, ATOMIC_UINT16_FETCH_OR, ATOMIC_UINT16_FETCH_XOR, ATOMIC_UINT32_LOAD, ATOMIC_UINT32_STORE, ATOMIC_UINT32_SWAP, ATOMIC_UINT32_CAS, ATOMIC_UINT32_FETCH_ADD, ATOMIC_UINT32_FETCH_SUB, ATOMIC_UINT32_FETCH_AND, ATOMIC_UINT32_FETCH_OR, ATOMIC_UINT32_FETCH_XOR, ATOMIC_UINT64_LOAD, ATOMIC_UINT64_STORE, ATOMIC_UINT64_SWAP, ATOMIC_UINT64_CAS, ATOMIC_UINT64_FETCH_ADD, ATOMIC_UINT64_FETCH_SUB, ATOMIC_UINT64_FETCH_AND, ATOMIC_UINT64_FETCH_OR, ATOMIC_UINT64_FETCH_XOR, ATOMIC_BOOL_LOAD, ATOMIC_BOOL_STORE, ATOMIC_BOOL_SWAP, ATOMIC_BOOL_CAS, ATOMIC_REFERENCEBASE_LOAD, ATOMIC_REFERENCEBASE_STORE, ATOMIC_REFERENCEBASE_SWAP, ATOMIC_REFERENCEBASE_CAS, ATOMIC_OPTIONREFERENCE_LOAD, ATOMIC_OPTIONREFERENCE_STORE, ATOMIC_OPTIONREFERENCE_SWAP, ATOMIC_OPTIONREFERENCE_CAS, // CHIR 2: Exception intrinsic BEGIN_CATCH, // CHIR 2: Math intrinsic ABS, FABS, FLOOR, CEIL, TRUNC, SIN, COS, EXP, EXP2, LOG, LOG2, LOG10, SQRT, ROUND, POW, POWI, BIT_CAST, // preinitialize intrinsic PREINITIALIZE, // Box cast intrinsic OBJECT_AS, IS_NULL, GET_TYPE_FOR_TYPE_PARAMETER, IS_SUBTYPE_TYPES, }; static const std::unordered_map coreIntrinsicMap = { {SIZE_OF_NAME, SIZE_OF}, {ALIGN_OF_NAME, ALIGN_OF}, {ARRAY_ACQUIRE_RAW_DATA_NAME, ARRAY_ACQUIRE_RAW_DATA}, {ARRAY_RELEASE_RAW_DATA_NAME, ARRAY_RELEASE_RAW_DATA}, {ARRAY_BUILT_IN_COPY_TO_NAME, ARRAY_BUILT_IN_COPY_TO}, {ARRAY_GET_NAME, ARRAY_GET}, {ARRAY_SET_NAME, ARRAY_SET}, {ARRAY_GET_UNCHECKED_NAME, ARRAY_GET_UNCHECKED}, {ARRAY_GET_REF_UNCHECKED_NAME, ARRAY_GET_REF_UNCHECKED}, {ARRAY_SET_UNCHECKED_NAME, ARRAY_SET_UNCHECKED}, {ARRAY_SIZE_NAME, ARRAY_SIZE}, {ARRAY_CLONE_NAME, ARRAY_CLONE}, {ARRAY_SLICE_INIT_NAME, ARRAY_SLICE_INIT}, {ARRAY_SLICE_NAME, ARRAY_SLICE}, {ARRAY_SLICE_RAWARRAY_NAME, ARRAY_SLICE_RAWARRAY}, {ARRAY_SLICE_START_NAME, ARRAY_SLICE_START}, {ARRAY_SLICE_SIZE_NAME, ARRAY_SLICE_SIZE}, {ARRAY_SLICE_GET_ELEMENT_NAME, ARRAY_SLICE_GET_ELEMENT}, {ARRAY_SLICE_SET_ELEMENT_NAME, ARRAY_SLICE_SET_ELEMENT}, {ARRAY_SLICE_GET_ELEMENT_UNCHECKED_NAME, ARRAY_SLICE_GET_ELEMENT_UNCHECKED}, {ARRAY_SLICE_SET_ELEMENT_UNCHECKED_NAME, ARRAY_SLICE_SET_ELEMENT_UNCHECKED}, {VECTOR_COMPARE_32_NAME, VECTOR_COMPARE_32}, {VECTOR_INDEX_BYTE_32_NAME, VECTOR_INDEX_BYTE_32}, {FILL_IN_STACK_TRACE_NAME, FILL_IN_STACK_TRACE}, {DECODE_STACK_TRACE_NAME, DECODE_STACK_TRACE}, {CPOINTER_GET_POINTER_ADDRESS_NAME, CPOINTER_GET_POINTER_ADDRESS}, {CPOINTER_READ_NAME, CPOINTER_READ}, {CPOINTER_WRITE_NAME, CPOINTER_WRITE}, {CPOINTER_ADD_NAME, CPOINTER_ADD}, {CSTRING_CONVERT_CSTR_TO_PTR_NAME, CSTRING_CONVERT_CSTR_TO_PTR}, {BIT_CAST_NAME, BIT_CAST}, {FUTURE_INIT_NAME, FUTURE_INIT}, {IS_THREAD_OBJECT_INITED_NAME, IS_THREAD_OBJECT_INITED}, {GET_THREAD_OBJECT_NAME, GET_THREAD_OBJECT}, {SET_THREAD_OBJECT_NAME, SET_THREAD_OBJECT}, {FUTURE_IS_COMPLETE_NAME, FUTURE_IS_COMPLETE}, {FUTURE_WAIT_NAME, FUTURE_WAIT}, {FUTURE_NOTIFYALL_NAME, FUTURE_NOTIFYALL}, {OBJECT_REFEQ_NAME, OBJECT_REFEQ}, {RAW_ARRAY_REFEQ_NAME, RAW_ARRAY_REFEQ}, {OBJECT_ZERO_VALUE_NAME, OBJECT_ZERO_VALUE}, {SOURCE_FILE_NAME, SOURCE_FILE}, {SOURCE_LINE_NAME, SOURCE_LINE}, {IDENTITY_HASHCODE_NAME, IDENTITY_HASHCODE}, {IDENTITY_HASHCODE_FOR_ARRAY_NAME, IDENTITY_HASHCODE_FOR_ARRAY}, {STRLEN_NAME, STRLEN}, {MEMCPY_S_NAME, MEMCPY_S}, {MEMSET_S_NAME, MEMSET_S}, {FREE_NAME, FREE}, {MALLOC_NAME, MALLOC}, {STRCMP_NAME, STRCMP}, {MEMCMP_NAME, MEMCMP}, {STRNCMP_NAME, STRNCMP}, {STRCASECMP_NAME, STRCASECMP}, // atomic for Thread Class {ATOMIC_LOAD_NAME, ATOMIC_LOAD}, {ATOMIC_STORE_NAME, ATOMIC_STORE}, {ATOMIC_FETCH_ADD_NAME, ATOMIC_FETCH_ADD}, {ATOMIC_COMPARE_AND_SWAP_NAME, ATOMIC_COMPARE_AND_SWAP}, {SLEEP_NAME, SLEEP}, {GET_TYPE_FOR_TYPE_PARAMETER_NAME, GET_TYPE_FOR_TYPE_PARAMETER}, {IS_SUBTYPE_TYPES_NAME, IS_SUBTYPE_TYPES}, }; static const std::unordered_map overflowIntrinsicMap = { {OVERFLOW_CHECKED_ADD_NAME, OVERFLOW_CHECKED_ADD}, {OVERFLOW_CHECKED_SUB_NAME, OVERFLOW_CHECKED_SUB}, {OVERFLOW_CHECKED_MUL_NAME, OVERFLOW_CHECKED_MUL}, {OVERFLOW_CHECKED_DIV_NAME, OVERFLOW_CHECKED_DIV}, {OVERFLOW_CHECKED_MOD_NAME, OVERFLOW_CHECKED_MOD}, {OVERFLOW_CHECKED_POW_NAME, OVERFLOW_CHECKED_POW}, {OVERFLOW_CHECKED_INC_NAME, OVERFLOW_CHECKED_INC}, {OVERFLOW_CHECKED_DEC_NAME, OVERFLOW_CHECKED_DEC}, {OVERFLOW_CHECKED_NEG_NAME, OVERFLOW_CHECKED_NEG}, {OVERFLOW_THROWING_ADD_NAME, OVERFLOW_THROWING_ADD}, {OVERFLOW_THROWING_SUB_NAME, OVERFLOW_THROWING_SUB}, {OVERFLOW_THROWING_MUL_NAME, OVERFLOW_THROWING_MUL}, {OVERFLOW_THROWING_DIV_NAME, OVERFLOW_THROWING_DIV}, {OVERFLOW_THROWING_MOD_NAME, OVERFLOW_THROWING_MOD}, {OVERFLOW_THROWING_POW_NAME, OVERFLOW_THROWING_POW}, {OVERFLOW_THROWING_INC_NAME, OVERFLOW_THROWING_INC}, {OVERFLOW_THROWING_DEC_NAME, OVERFLOW_THROWING_DEC}, {OVERFLOW_THROWING_NEG_NAME, OVERFLOW_THROWING_NEG}, {OVERFLOW_SATURATING_ADD_NAME, OVERFLOW_SATURATING_ADD}, {OVERFLOW_SATURATING_SUB_NAME, OVERFLOW_SATURATING_SUB}, {OVERFLOW_SATURATING_MUL_NAME, OVERFLOW_SATURATING_MUL}, {OVERFLOW_SATURATING_DIV_NAME, OVERFLOW_SATURATING_DIV}, {OVERFLOW_SATURATING_MOD_NAME, OVERFLOW_SATURATING_MOD}, {OVERFLOW_SATURATING_POW_NAME, OVERFLOW_SATURATING_POW}, {OVERFLOW_SATURATING_INC_NAME, OVERFLOW_SATURATING_INC}, {OVERFLOW_SATURATING_DEC_NAME, OVERFLOW_SATURATING_DEC}, {OVERFLOW_SATURATING_NEG_NAME, OVERFLOW_SATURATING_NEG}, {OVERFLOW_WRAPPING_ADD_NAME, OVERFLOW_WRAPPING_ADD}, {OVERFLOW_WRAPPING_SUB_NAME, OVERFLOW_WRAPPING_SUB}, {OVERFLOW_WRAPPING_MUL_NAME, OVERFLOW_WRAPPING_MUL}, {OVERFLOW_WRAPPING_DIV_NAME, OVERFLOW_WRAPPING_DIV}, {OVERFLOW_WRAPPING_MOD_NAME, OVERFLOW_WRAPPING_MOD}, {OVERFLOW_WRAPPING_POW_NAME, OVERFLOW_WRAPPING_POW}, {OVERFLOW_WRAPPING_INC_NAME, OVERFLOW_WRAPPING_INC}, {OVERFLOW_WRAPPING_DEC_NAME, OVERFLOW_WRAPPING_DEC}, {OVERFLOW_WRAPPING_NEG_NAME, OVERFLOW_WRAPPING_NEG}, }; static const std::unordered_map reflectIntrinsicMap = { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #define REFLECTION_KIND_TO_RUNTIME_FUNCTION(REFLECTION_KIND, CJ_FUNCTION, RUNTIME_FUNCTION) \ {REFLECTION_KIND##_NAME, REFLECTION_KIND}, #include "cangjie/CHIR/LLVMReflectionIntrinsics.def" #undef REFLECTION_KIND_TO_RUNTIME_FUNCTION #endif }; static const std::unordered_map interOpIntrinsicMap = { {CROSS_ACCESS_BARRIER_NAME, CROSS_ACCESS_BARRIER}, {CREATE_EXPORT_HANDLE_NAME, CREATE_EXPORT_HANDLE}, {GET_EXPORTED_REF_NAME, GET_EXPORTED_REF}, {REMOVE_EXPORTED_REF_NAME, REMOVE_EXPORTED_REF} }; static const std::unordered_map cjnativeSyncIntrinsicMap = { {ATOMIC_LOAD_NAME, ATOMIC_LOAD}, {ATOMIC_STORE_NAME, ATOMIC_STORE}, {ATOMIC_SWAP_NAME, ATOMIC_SWAP}, {ATOMIC_COMPARE_AND_SWAP_NAME, ATOMIC_COMPARE_AND_SWAP}, {ATOMIC_FETCH_ADD_NAME, ATOMIC_FETCH_ADD}, {ATOMIC_FETCH_SUB_NAME, ATOMIC_FETCH_SUB}, {ATOMIC_FETCH_AND_NAME, ATOMIC_FETCH_AND}, {ATOMIC_FETCH_OR_NAME, ATOMIC_FETCH_OR}, {ATOMIC_FETCH_XOR_NAME, ATOMIC_FETCH_XOR}, {MUTEX_INIT_NAME, MUTEX_INIT}, {MUTEX_LOCK_NAME, CJ_MUTEX_LOCK}, {MUTEX_TRY_LOCK_NAME, MUTEX_TRY_LOCK}, {MUTEX_CHECK_STATUS_NAME, MUTEX_CHECK_STATUS}, {MUTEX_UNLOCK_NAME, MUTEX_UNLOCK}, {WAITQUEUE_INIT_NAME, WAITQUEUE_INIT}, {MONITOR_INIT_NAME, MONITOR_INIT}, {MOITIOR_WAIT_NAME, MOITIOR_WAIT}, {MOITIOR_NOTIFY_NAME, MOITIOR_NOTIFY}, {MOITIOR_NOTIFY_ALL_NAME, MOITIOR_NOTIFY_ALL}, {MULTICONDITION_WAIT_NAME, MULTICONDITION_WAIT}, {MULTICONDITION_NOTIFY_NAME, MULTICONDITION_NOTIFY}, {MULTICONDITION_NOTIFY_ALL_NAME, MULTICONDITION_NOTIFY_ALL}, }; static const std::unordered_map runtimeIntrinsicMap = { {INVOKE_GC_NAME, INVOKE_GC}, {SET_GC_THRESHOLD_NAME, SET_GC_THRESHOLD}, {DUMP_CJ_HEAP_DATA_NAME, DUMP_CJ_HEAP_DATA}, {GET_GC_COUNT_NAME, GET_GC_COUNT}, {GET_GC_TIME_US_NAME, GET_GC_TIME_US}, {GET_GC_FREED_SIZE_NAME, GET_GC_FREED_SIZE}, {START_CJ_CPU_PROFILING_NAME, START_CJ_CPU_PROFILING}, {STOP_CJ_CPU_PROFILING_NAME, STOP_CJ_CPU_PROFILING}, {BLACK_BOX_NAME, BLACK_BOX}, {GET_MAX_HEAP_SIZE_NAME, GET_MAX_HEAP_SIZE}, {GET_ALLOCATE_HEAP_SIZE_NAME, GET_ALLOCATE_HEAP_SIZE}, {GET_REAL_HEAP_SIZE_NAME, GET_REAL_HEAP_SIZE}, {GET_THREAD_NUMBER_NAME, GET_THREAD_NUMBER}, {GET_BLOCKING_THREAD_NUMBER_NAME, GET_BLOCKING_THREAD_NUMBER}, {GET_NATIVE_THREAD_NUMBER_NAME, GET_NATIVE_THREAD_NUMBER}, {CROSS_ACCESS_BARRIER_NAME, CROSS_ACCESS_BARRIER}, {CREATE_EXPORT_HANDLE_NAME, CREATE_EXPORT_HANDLE}, {GET_EXPORTED_REF_NAME, GET_EXPORTED_REF}, {REMOVE_EXPORTED_REF_NAME, REMOVE_EXPORTED_REF}, }; static const std::unordered_map mathIntrinsicMap = { {ABS_NAME, ABS}, {FABS_NAME, FABS}, {FLOOR_NAME, FLOOR}, {CEIL_NAME, CEIL}, {TRUC_NAME, TRUNC}, {SIN_NAME, SIN}, {COS_NAME, COS}, {EXP_NAME, EXP}, {EXP2_NAME, EXP2}, {LOG_NAME, LOG}, {LOG2_NAME, LOG2}, {LOG10_NAME, LOG10}, {SQRT_NAME, SQRT}, {ROUND_NAME, ROUND}, {POW_NAME, POW}, {POWI_NAME, POWI}, }; } // namespace Cangjie::CHIR #endif // Cangjie::CANGJIE_CHIR_INTRINSICKIND_H cangjie_compiler-1.0.7/include/cangjie/CHIR/LLVMReflectionIntrinsics.def000066400000000000000000000177511510705540100261050ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. REFLECTION_KIND_TO_RUNTIME_FUNCTION(IS_INTERFACE, isInterface, CJ_MCC_IsInterface) REFLECTION_KIND_TO_RUNTIME_FUNCTION(IS_CLASS, isClass, CJ_MCC_IsClass) REFLECTION_KIND_TO_RUNTIME_FUNCTION(IS_PRIMITIVE, isPrimitive, CJ_MCC_IsPrimitive) REFLECTION_KIND_TO_RUNTIME_FUNCTION(IS_STRUCT, isStruct, CJ_MCC_IsStruct) REFLECTION_KIND_TO_RUNTIME_FUNCTION(IS_GENERIC, isGeneric, CJ_MCC_IsGeneric) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_OR_CREATE_TYPEINFO_FOR_REFLECT, getOrCreateTypeInfoForReflect, CJ_MCC_GetOrCreateTypeInfoForReflect) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_TYPETEMPLATE, getTypeTemplate, CJ_MCC_GetTypeTemplate) REFLECTION_KIND_TO_RUNTIME_FUNCTION(CHECK_METHOD_ACTUAL_ARGS, checkMethodActualArgs, CJ_MCC_CheckMethodActualArgs) REFLECTION_KIND_TO_RUNTIME_FUNCTION(METHOD_ENTRYPOINT_IS_NULL, methodEntryPointIsNull, CJ_MCC_MethodEntryPointIsNull) REFLECTION_KIND_TO_RUNTIME_FUNCTION(IS_RELECT_UNSUPPORTED_TYPE, isReflectUnsupportedType, CJ_MCC_IsReflectUnsupportedType) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_TYPE_FOR_ANY, getTypeForAny, CJ_MCC_GetTypeForAny) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_TYPE_BY_MANGLED_NAME, getTypeByMangledName, CJ_MCC_GetTypeByMangledName) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_TYPE_NAME, getTypeName, CJ_MCC_GetTypeName) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_TYPE_BY_QUALIFIED_NAME, getTypeByQualifiedName, CJ_MCC_getTypeByQualifiedName) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_TYPE_QUALIFIED_NAME_LENGTH, getTypeQualifiedNameLength, CJ_MCC_GetQualifiedNameLength) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_TYPE_QUALIFIED_NAME, getTypeQualifiedName, CJ_MCC_GetQualifiedName) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_NUM_OF_INTERFACE, getNumOfInterface, CJ_MCC_GetNumOfInterface) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_INTERFACE, getInterface, CJ_MCC_GetInterface) REFLECTION_KIND_TO_RUNTIME_FUNCTION(IS_SUBTYPE, isSubType, CJ_MCC_IsSubType) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_TYPE_INFO_MODIFIER, getTypeInfoModifier, CJ_MCC_GetTypeInfoModifier) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_TYPE_INFO_ANNOTATIONS, getTypeInfoAnnotations, CJ_MCC_GetTypeInfoAnnotations) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_OBJ_CLASS, getObjClass, CJ_MCC_GetObjClass) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_SUPER_TYPE_INFO, getSuperTypeInfo, CJ_MCC_GetSuperTypeInfo) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_NUM_OF_INSTANCE_METHOD_INFOS, getNumOfInstanceMethodInfos, CJ_MCC_GetNumOfInstanceMethodInfos) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_INSTANCE_METHOD_INFO, getInstanceMethodInfo, CJ_MCC_GetInstanceMethodInfo) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_NUM_OF_STATIC_METHOD_INFOS, getNumOfStaticMethodInfos, CJ_MCC_GetNumOfStaticMethodInfos) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_STATIC_METHOD_INFO, getStaticMethodInfo, CJ_MCC_GetStaticMethodInfo) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_METHOD_NAME, getMethodName, CJ_MCC_GetMethodName) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_METHOD_RETURN_TYPE, getMethodReturnType, CJ_MCC_GetMethodReturnType) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_METHOD_MODIFIER, getMethodModifier, CJ_MCC_GetMethodModifier) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_METHOD_ANNOTATIONS, getMethodAnnotations, CJ_MCC_GetMethodAnnotations) REFLECTION_KIND_TO_RUNTIME_FUNCTION(APPLY_CJ_METHOD, applyCJInstanceMethod, CJ_MCC_ApplyCJInstanceMethod) REFLECTION_KIND_TO_RUNTIME_FUNCTION(APPLY_CJ_STATIC_METHOD, applyCJStaticMethod, CJ_MCC_ApplyCJStaticMethod) REFLECTION_KIND_TO_RUNTIME_FUNCTION(APPLY_CJ_GENERIC_METHOD, applyCJGenericInstanceMethod, CJ_MCC_ApplyCJGenericInstanceMethod) REFLECTION_KIND_TO_RUNTIME_FUNCTION(APPLY_CJ_GENERIC_STATIC_METHOD, applyCJGenericStaticMethod, CJ_MCC_ApplyCJGenericStaticMethod) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_NUM_OF_ACTUAL_PARAMETERS, getNumOfActualParameters, CJ_MCC_GetNumOfActualParameters) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_NUM_OF_GENERIC_PARAMETERS, getNumOfGenericParameters, CJ_MCC_GetNumOfGenericParameters) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_ACTUAL_PARAMETER_INFO, getActualParameterInfo, CJ_MCC_GetActualParameterInfo) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_GENERIC_PARAMETER_INFO, getGenericParameterInfo, CJ_MCC_GetGenericParameterInfo) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_NUM_OF_INSTANCE_FIELD_INFOS, getNumOfInstanceFieldInfos, CJ_MCC_GetNumOfInstanceFieldInfos) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_INSTANCE_FIELD_INFO, getInstanceFieldInfo, CJ_MCC_GetInstanceFieldInfo) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_NUM_OF_STATIC_FIELD_INFOS, getNumOfStaticFieldInfos, CJ_MCC_GetNumOfStaticFieldInfos) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_STATIC_FIELD_INFO, getStaticFieldInfo, CJ_MCC_GetStaticFieldInfo) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_STATIC_FIELD_NAME, getStaticFieldName, CJ_MCC_GetStaticFieldName) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_STATIC_FIELD_TYPE, getStaticFieldType, CJ_MCC_GetStaticFieldType) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_STATIC_FIELD_ANNOTATIONS, getStaticFieldAnnotations, CJ_MCC_GetStaticFieldAnnotations) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_FIELD_NAME, getInstanceFieldName, CJ_MCC_GetInstanceFieldName) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_FIELD_TYPE, getInstanceFieldType, CJ_MCC_GetInstanceFieldType) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_FIELD_ANNOTATIONS, getInstanceFieldAnnotations, CJ_MCC_GetInstanceFieldAnnotations) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_FIELD_MODIFIER, getInstanceFieldModifier, CJ_MCC_GetInstanceFieldModifier) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_STATIC_FIELD_MODIFIER, getStaticFieldModifier, CJ_MCC_GetStaticFieldModifier) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_FIELD_VALUE, getInstanceFieldValue, CJ_MCC_GetInstanceFieldValue) REFLECTION_KIND_TO_RUNTIME_FUNCTION(SET_FIELD_VALUE, setInstanceFieldValue, CJ_MCC_SetInstanceFieldValue) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_STATIC_FIELD_VALUE, getStaticFieldValue, CJ_MCC_GetStaticFieldValue) REFLECTION_KIND_TO_RUNTIME_FUNCTION(SET_STATIC_FIELD_VALUE, setStaticFieldValue, CJ_MCC_SetStaticFieldValue) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_FIELD_DECLARING_TYPE, getFieldDeclaringType, CJ_MCC_GetFieldDeclaringType) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_PARAMETER_INDEX, getParameterIndex, CJ_MCC_GetParameterIndex) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_PARAMETER_NAME, getParameterName, CJ_MCC_GetParameterName) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_PARAMETER_TYPE, getParameterType, CJ_MCC_GetParameterType) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_PARAMETER_ANNOTATIONS, getParameterAnnotations, CJ_MCC_GetParameterAnnotations) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_RELATED_PACKAGE_INF, getRelatedPackageInfo, CJ_MCC_GetRelatedPackageInfo) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_PACKAGE_NAME, getPackageName, CJ_MCC_GetPackageName) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_PACKAGE_NUM_OF_TYPE_INFOS, getPackageNumOfTypeInfos, CJ_MCC_GetPackageNumOfTypeInfos) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_PACKAGE_TYPE_INFO, getPackageTypeInfo, CJ_MCC_GetPackageTypeInfo) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_PACKAGE_NUM_OF_GLOBAL_METHODS, getPackageNumOfGlobalMethodInfos, CJ_MCC_GetPackageNumOfGlobalMethodInfos) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_PACKAGE_GLOBAL_METHOD_INFO, getPackageGlobalMethodInfo, CJ_MCC_GetPackageGlobalMethodInfo) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_PACKAGE_NUM_OF_GLOBAL_FIELD_INFOS, getPackageNumOfGlobalFieldInfos, CJ_MCC_GetPackageNumOfGlobalFieldInfos) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_PACKAGE_GLOBAL_FIELD_INFO, getPackageGlobalFieldInfo, CJ_MCC_GetPackageGlobalFieldInfo) REFLECTION_KIND_TO_RUNTIME_FUNCTION(LOAD_PACKAGE, loadPackage, CJ_MCC_LoadPackage) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_PACKAGE_BY_QUALIFIEDNAME, getPackageByQualifiedName, CJ_MCC_GetPackageByQualifiedName) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_PACKAGE_VERSION, getPackageVersion, CJ_MCC_GetPackageVersion) REFLECTION_KIND_TO_RUNTIME_FUNCTION(GET_SUB_PACKAGES, getSubPackages, CJ_MCC_GetSubPackages)cangjie_compiler-1.0.7/include/cangjie/CHIR/LiteralValue.h000066400000000000000000000106411510705540100233230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_LITERAL_VALUE_H #define CANGJIE_CHIR_LITERAL_VALUE_H #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { enum ConstantValueKind : uint8_t { KIND_BOOL, KIND_RUNE, KIND_INT, KIND_FLOAT, KIND_STRING, KIND_UNIT, KIND_NULL, // Specially, Func is also a kind of constant value in CHIR thus can be bound to a variable by `Constant` expression KIND_FUNC }; /* * @brief Base class for literals in CHIR. * */ class LiteralValue : public Value { friend class CHIRSerializer; public: bool IsBoolLiteral() const; bool IsFloatLiteral() const; bool IsIntLiteral() const; bool IsNullLiteral() const; bool IsRuneLiteral() const; bool IsStringLiteral() const; bool IsUnitLiteral() const; protected: explicit LiteralValue(Type* ty, ConstantValueKind literalKind); ~LiteralValue() = default; private: ConstantValueKind GetConstantValueKind() const; ConstantValueKind literalKind; }; /* * @brief Bool Literal in CHIR. * * Define bool literal value. */ class BoolLiteral : public LiteralValue { friend class CHIRBuilder; public: bool GetVal() const; std::string ToString() const override; private: BoolLiteral(Type* ty, bool val); ~BoolLiteral() = default; bool val; /* The value of contant boolean */ }; /* * @brief Rune Literal in CHIR. * * Define char literal value (with char32_t unicode character). */ class RuneLiteral : public LiteralValue { friend class CHIRBuilder; public: char32_t GetVal() const; std::string ToString() const override; private: explicit RuneLiteral(Type* ty, char32_t val); ~RuneLiteral() override = default; char32_t val; /* The value of constant character */ }; /* * @brief String Literal in CHIR. * * Define string literal value. */ class StringLiteral : public LiteralValue { friend class CHIRBuilder; public: std::string GetVal() const&; std::string GetVal() &&; std::string ToString() const override; private: explicit StringLiteral(Type* ty, std::string val); ~StringLiteral() override = default; std::string val; }; /* * @brief IntLiteral in CHIR. * * Define signed or unsigned integer literal value. * The integer literal value can be Int8, Int16, Int32, Int64, IntNative, UInt8, UInt16, * UInt32, UInt64, UIntNative type. */ class IntLiteral : public LiteralValue { friend class CHIRBuilder; public: int64_t GetSignedVal() const; uint64_t GetUnsignedVal() const; bool IsSigned() const; std::string ToString() const override; private: explicit IntLiteral(Type* ty, uint64_t val); ~IntLiteral() override = default; /** @brief The value of this constant. * * It is stored in 64 bits unsigned integer, but the actual kind is determined by * the type of this literal */ uint64_t val; }; /* * @brief FloatLiteral in CHIR. * * Define IEEE64 standard float value. * The float literal value can be Float16, Float32, Float64 type. */ class FloatLiteral : public LiteralValue { friend class CHIRBuilder; public: double GetVal() const; std::string ToString() const override; private: explicit FloatLiteral(Type* ty, double val); ~FloatLiteral() override = default; /** @brief The value of this constant. * * It is stored in 64 bits float, but the actual kind is determined by * the type of this literal */ double val; }; /* * @brief UnitLiteral in CHIR. * * Define unit literal value. */ class UnitLiteral : public LiteralValue { friend class CHIRBuilder; public: std::string ToString() const override; private: explicit UnitLiteral(Type* ty); ~UnitLiteral() override = default; }; /* * @brief NullLiteral in CHIR. * */ class NullLiteral : public LiteralValue { friend class CHIRBuilder; public: std::string ToString() const override; private: explicit NullLiteral(Type* ty); ~NullLiteral() override = default; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/OverflowChecking.h000066400000000000000000000225161510705540100241750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_OVERFLOWCHECKING_H #define CANGJIE_CHIR_OVERFLOWCHECKING_H // DO NOT remove this include. Required for builds for Windows and newer clang #include #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/Type.h" namespace Cangjie::CHIR { class OverflowChecker { public: /** * @brief Check if overflow after operation of x and y, Note: x and y themselves do not exceed the type range. * * @param typeKind The type kind of the operation. * @param exprKind The expression kind of the operation. * @param x The first operand. * @param y The second operand. * @param strategy The overflow strategy to be used. * @param res The result of the operation. * @return True if an overflow occurs, false otherwise. */ static bool IsIntOverflow(const Type::TypeKind& typeKind, const ExprKind& exprKind, int64_t x, int64_t y, const OverflowStrategy& strategy, int64_t* res); /** * @brief Check if overflow after operation of x and y, Note: x and y themselves do not exceed the type range. * * @param typeKind The type kind of the operation. * @param exprKind The expression kind of the operation. * @param x The first operand. * @param y The second operand. * @param strategy The overflow strategy to be used. * @param res The result of the operation. * @return True if an overflow occurs, false otherwise. */ static bool IsUIntOverflow(const Type::TypeKind& typeKind, const ExprKind& exprKind, uint64_t x, uint64_t y, const OverflowStrategy& strategy, uint64_t* res); /** * @brief Given two int/uint x and y, check if the operation is overflow * and stores the result in res according to the overflow strategy. * * @tparam T The data type of the operands. * @param x The first operand. * @param y The second operand. * @param kind The expression kind of the operation. * @param strategy The overflow strategy to be used. * @param res The result of the operation. * @return True if an overflow occurs, false otherwise. */ template static bool IsOverflow(T x, T y, ExprKind kind, const OverflowStrategy& strategy, T* res); /** * @brief Checks for overflow during an exponential operation. * * @param x The base of the exponentiation. * @param y The exponent. * @param strategy The overflow strategy to be used. * @param res The result of the operation. * @return True if an overflow occurs, false otherwise. */ static bool IsExpOverflow(int64_t x, uint64_t y, OverflowStrategy strategy, int64_t* res); /** * @brief Checks for overflow when typecasting an integer to another type. * * @tparam T The source data type. * @tparam K The target data type. * @param x The value to be typecast. * @param res The result of the typecast. * @param strategy The overflow strategy to be used. * @return True if an overflow occurs, false otherwise. */ template static bool IsTypecastOverflowForInt(T x, K* res, OverflowStrategy strategy) { CJC_NULLPTR_CHECK(res); CJC_ASSERT(!(std::is_same::value) && !(std::is_same::value)); bool isOverflow = false; if (std::is_signed::value && !std::is_signed::value && x < 0) { isOverflow = true; } else if (!std::is_signed::value && std::is_signed::value) { isOverflow = x > std::numeric_limits::max(); } else { using LargerType = typename std::conditional::type; constexpr auto tmax = static_cast(std::numeric_limits::max()); constexpr auto kmax = static_cast(std::numeric_limits::max()); if constexpr (tmax > kmax) { isOverflow = x > static_cast(std::numeric_limits::max()) || x < static_cast(std::numeric_limits::min()); } else { isOverflow = static_cast(x) > std::numeric_limits::max() || static_cast(x) < std::numeric_limits::min(); } } if (isOverflow && strategy == OverflowStrategy::SATURATING) { if (x < 0) { *res = std::numeric_limits::min(); } else { *res = std::numeric_limits::max(); } } else { *res = (K)x; } return isOverflow; } /** * @brief Checks for overflow after an addition operation. * * @tparam T The data type of the operands. * @param x The first operand. * @param y The second operand. * @param strategy The overflow strategy to be used. * @param res The result of the addition. * @return True if an overflow occurs, false otherwise. */ template static bool IsOverflowAfterAdd(T x, T y, OverflowStrategy strategy, T* res) { CJC_NULLPTR_CHECK(res); bool isOverflow = __builtin_add_overflow(x, y, res); if (isOverflow && strategy == OverflowStrategy::SATURATING) { if (*res > x) { *res = std::numeric_limits::min(); } else { *res = std::numeric_limits::max(); } } return isOverflow; } /** * @brief Checks for overflow after a subtraction operation. * * @tparam T The data type of the operands. * @param x The first operand. * @param y The second operand. * @param strategy The overflow strategy to be used. * @param res The result of the subtraction. * @return True if an overflow occurs, false otherwise. */ template static bool IsOverflowAfterSub(T x, T y, OverflowStrategy strategy, T* res) { CJC_NULLPTR_CHECK(res); bool isOverflow = __builtin_sub_overflow(x, y, res); if (isOverflow && strategy == OverflowStrategy::SATURATING) { if (*res > x) { *res = std::numeric_limits::min(); } else { *res = std::numeric_limits::max(); } } return isOverflow; } /** * @brief Checks for overflow after a multiplication operation. * * @tparam T The data type of the operands. * @param x The first operand. * @param y The second operand. * @param strategy The overflow strategy to be used. * @param res The result of the multiplication. * @return True if an overflow occurs, false otherwise. */ template static bool IsOverflowAfterMul(T x, T y, OverflowStrategy strategy, T* res) { CJC_NULLPTR_CHECK(res); bool isOverflow = __builtin_mul_overflow(x, y, res); if (isOverflow && strategy == OverflowStrategy::SATURATING) { if (x < 0 && y < 0) { *res = std::numeric_limits::max(); } else if (x >= 0 && y >= 0) { *res = std::numeric_limits::max(); } else { *res = std::numeric_limits::min(); } } return isOverflow; } /** * @brief Checks for overflow after a division operation. * * @tparam T The data type of the operands. * @param x The first operand. * @param y The second operand. * @param strategy The overflow strategy to be used. * @param res The result of the division. * @return True if an overflow occurs, false otherwise. */ template static bool IsOverflowAfterDiv(T x, T y, OverflowStrategy strategy, T* res) { CJC_NULLPTR_CHECK(res); bool isOverflow = false; if (std::is_signed::value) { isOverflow = x == std::numeric_limits::min() && y == -1; if (isOverflow && strategy == OverflowStrategy::WRAPPING) { *res = std::numeric_limits::min(); return isOverflow; } else if (isOverflow && strategy == OverflowStrategy::SATURATING) { *res = std::numeric_limits::max(); return isOverflow; } else if (isOverflow) { return isOverflow; } } CJC_ASSERT(y != 0); *res = x / y; return isOverflow; } /** * @brief Checks for overflow after a modulus operation. * * @tparam T The data type of the operands. * @param x The first operand. * @param y The second operand. * @param res The result of the modulus operation. * @return True if an overflow occurs, false otherwise. */ template static bool IsOverflowAfterMod(T x, T y, T* res) { CJC_NULLPTR_CHECK(res); if (std::is_signed::value) { if (x == std::numeric_limits::min() && y == -1) { *res = 0; // CJ decided that this does not cause overflow return true; } } CJC_ASSERT(y != 0); *res = x % y; return false; } }; } // namespace Cangjie::CHIR #endif // CANGJIE_CHIR_OVERFLOWCHECKING_H cangjie_compiler-1.0.7/include/cangjie/CHIR/Package.h000066400000000000000000000135371510705540100222740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_PACKAGE_H #define CANGJIE_CHIR_PACKAGE_H #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/ExtendDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include #include namespace Cangjie::CHIR { class Package { explicit Package(const std::string& name); ~Package() = default; friend class CHIRContext; friend class CHIRBuilder; public: enum class AccessLevel : uint8_t { INVALID = 0, INTERNAL, PROTECTED, PUBLIC }; // ===--------------------------------------------------------------------===// // Base Infomation // ===--------------------------------------------------------------------===// void Dump() const; std::string ToString() const; std::string GetName() const; AccessLevel GetPackageAccessLevel() const; void SetPackageAccessLevel(const AccessLevel& level); // ===--------------------------------------------------------------------===// // Global Var API // ===--------------------------------------------------------------------===// void AddGlobalVar(GlobalVar* item); std::vector GetGlobalVars() const; void SetGlobalVars(std::vector&& vars); // ===--------------------------------------------------------------------===// // Global Function API // ===--------------------------------------------------------------------===// void AddGlobalFunc(Func* item); std::vector GetGlobalFuncs() const; void SetGlobalFuncs(const std::vector& funcs); Func* GetPackageInitFunc() const; void SetPackageInitFunc(Func* func); void SetPackageLiteralInitFunc(Func* func); Func* GetPackageLiteralInitFunc() const; // ===--------------------------------------------------------------------===// // Imported Var and Function API // ===--------------------------------------------------------------------===// void AddImportedVarAndFunc(ImportedValue* item); std::vector GetImportedVarAndFuncs() const; void SetImportedVarAndFuncs(std::vector&& items); // ===--------------------------------------------------------------------===// // StructDef API // ===--------------------------------------------------------------------===// void AddStruct(StructDef* item); std::vector GetStructs() const; void SetStructs(std::vector&& s); void AddImportedStruct(StructDef* item); std::vector GetImportedStructs() const; void SetImportedStructs(std::vector&& s); std::vector GetAllStructDef() const; // ===--------------------------------------------------------------------===// // ClassDef API // ===--------------------------------------------------------------------===// void AddClass(ClassDef* item); std::vector GetClasses() const; void SetClasses(std::vector&& items); void AddImportedClass(ClassDef* item); std::vector GetImportedClasses() const; void SetImportedClasses(std::vector&& s); std::vector GetAllClassDef() const; // ===--------------------------------------------------------------------===// // EnumDef API // ===--------------------------------------------------------------------===// void AddEnum(EnumDef* item); std::vector GetEnums() const; void SetEnums(std::vector&& s); void AddImportedEnum(EnumDef* item); std::vector GetImportedEnums() const; void SetImportedEnums(std::vector&& s); std::vector GetAllEnumDef() const; // ===--------------------------------------------------------------------===// // ExtendDef API // ===--------------------------------------------------------------------===// void AddExtend(ExtendDef* item); std::vector GetExtends() const; void SetExtends(std::vector&& items); void AddImportedExtend(ExtendDef* item); std::vector GetImportedExtends() const; void SetImportedExtends(std::vector&& items); std::vector GetAllExtendDef() const; // ===--------------------------------------------------------------------===// // Others API // ===--------------------------------------------------------------------===// std::vector GetAllCustomTypeDef() const; std::vector GetCurPkgCustomTypeDef() const; std::vector GetAllImportedCustomTypeDef() const; private: std::string name; // full package name, like "std.core" AccessLevel pkgAccessLevel{AccessLevel::INVALID}; // public/internal/protected, get from AST // imported decls std::vector importedVarAndFuncs; std::vector importedStructs; std::vector importedClasses; std::vector importedEnums; std::vector importedExtends; // decls in current package std::vector globalVars; std::vector globalFuncs; std::vector structs; std::vector classes; std::vector enums; std::vector extends; Func* packageInitFunc = nullptr; Func* packageLiteralInitFunc = nullptr; // global literals init function in one package }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Serializer/000077500000000000000000000000001510705540100226705ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/CHIR/Serializer/CHIRDeserializer.h000066400000000000000000000014271510705540100261350ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_SERIALIZER_DESERIALIZER_H #define CANGJIE_CHIR_SERIALIZER_DESERIALIZER_H #include "cangjie/CHIR/CHIR.h" #include "cangjie/CHIR/CHIRBuilder.h" #include namespace Cangjie::CHIR { class CHIRDeserializer { class CHIRDeserializerImpl; public: static bool Deserialize(const std::string& fileName, Cangjie::CHIR::CHIRBuilder& chirBuilder, ToCHIR::Phase& phase, bool compilePlatform = false); private: explicit CHIRDeserializer() { } }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Serializer/CHIRSerializer.h000066400000000000000000000012421510705540100256170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_SERIALIZER_CHIRSERIALIZER_H #define CANGJIE_CHIR_SERIALIZER_CHIRSERIALIZER_H #include "cangjie/CHIR/CHIR.h" #include "cangjie/CHIR/Package.h" #include namespace Cangjie::CHIR { class CHIRSerializer { class CHIRSerializerImpl; public: static void Serialize(const Package& package, const std::string filename, ToCHIR::Phase phase); }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/StringWrapper.h000066400000000000000000000036031510705540100235410ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_STRING_WRAPPER_H #define CANGJIE_CHIR_STRING_WRAPPER_H #include namespace Cangjie::CHIR { /** * @brief Extend api for std::string */ class StringWrapper { public: explicit StringWrapper(const std::string& initVal = ""); /** * @brief Return current object as a string. * * @return The content of current object. */ const std::string& Str() const; /** * @brief Append new content to the old one. * * @param newValue The new content. */ void Append(const std::string& newValue); /** * @brief Append new content to the old one. * * @param newValue The new content. * @param delimiter The delimiter. */ void Append(const std::string& newValue, const std::string& delimiter); /** * @brief Remove the last N characters. * * @param n The number of characters. */ void RemoveLastNChars(const size_t n); /** * @brief If current object has content, then append delimiter to the content, otherwise, not append. * * @param delimiter The delimiter. * @return an object which has already appended delimiter. */ StringWrapper& AddDelimiterOrNot(const std::string& delimiter); /** * @brief If the `newValue` is empty, then clear current object's content, * if not, append the `newValue` to current object's content. * * @param newValue The new content. * @return an object which has already appended or cleared. */ StringWrapper& AppendOrClear(const std::string& newValue); private: std::string value; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/ToStringUtils.h000066400000000000000000000365551510705540100235400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TOSTRING_UTILS_H #define CANGJIE_CHIR_TOSTRING_UTILS_H #include "cangjie/CHIR/IntrinsicKind.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/StringWrapper.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" #include "cangjie/Utils/SafePointer.h" #include #include #include #include namespace Cangjie::CHIR { /** * Map of IntrinsicKind to string value. */ const std::unordered_map INTRINSIC_KIND_TO_STRING_MAP{ {CHIR::NOT_INTRINSIC, "notIntrinsic"}, {CHIR::NOT_IMPLEMENTED, "notImplemented"}, // For hoisting, but we should later split arraybuilder // into allocation and initialisation {CHIR::ARRAY_INIT, "arrayInit"}, // CORE {CHIR::SIZE_OF, CHIR::SIZE_OF_NAME}, {CHIR::ALIGN_OF, CHIR::ALIGN_OF_NAME}, {CHIR::GET_TYPE_FOR_TYPE_PARAMETER, CHIR::GET_TYPE_FOR_TYPE_PARAMETER_NAME}, {CHIR::IS_SUBTYPE_TYPES, CHIR::IS_SUBTYPE_TYPES_NAME}, {CHIR::ARRAY_ACQUIRE_RAW_DATA, CHIR::ARRAY_ACQUIRE_RAW_DATA_NAME}, {CHIR::ARRAY_RELEASE_RAW_DATA, CHIR::ARRAY_RELEASE_RAW_DATA_NAME}, {CHIR::ARRAY_BUILT_IN_COPY_TO, CHIR::ARRAY_BUILT_IN_COPY_TO_NAME}, {CHIR::ARRAY_GET, CHIR::ARRAY_GET_NAME}, {CHIR::ARRAY_SET, CHIR::ARRAY_SET_NAME}, {CHIR::ARRAY_GET_UNCHECKED, CHIR::ARRAY_GET_UNCHECKED_NAME}, #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND {CHIR::ARRAY_GET_REF_UNCHECKED, CHIR::ARRAY_GET_REF_UNCHECKED_NAME}, #endif {CHIR::ARRAY_SET_UNCHECKED, CHIR::ARRAY_SET_UNCHECKED_NAME}, {CHIR::ARRAY_SIZE, CHIR::ARRAY_SIZE_NAME}, {CHIR::ARRAY_CLONE, CHIR::ARRAY_CLONE_NAME}, {CHIR::ARRAY_SLICE_INIT, CHIR::ARRAY_SLICE_INIT_NAME}, {CHIR::ARRAY_SLICE, CHIR::ARRAY_SLICE_NAME}, {CHIR::ARRAY_SLICE_RAWARRAY, CHIR::ARRAY_SLICE_RAWARRAY_NAME}, {CHIR::ARRAY_SLICE_START, CHIR::ARRAY_SLICE_START_NAME}, {CHIR::ARRAY_SLICE_SIZE, CHIR::ARRAY_SLICE_SIZE_NAME}, {CHIR::ARRAY_SLICE_GET_ELEMENT, CHIR::ARRAY_SLICE_GET_ELEMENT_NAME}, {CHIR::ARRAY_SLICE_SET_ELEMENT, CHIR::ARRAY_SLICE_SET_ELEMENT_NAME}, {CHIR::ARRAY_SLICE_GET_ELEMENT_UNCHECKED, CHIR::ARRAY_SLICE_GET_ELEMENT_UNCHECKED_NAME}, {CHIR::ARRAY_SLICE_SET_ELEMENT_UNCHECKED, CHIR::ARRAY_SLICE_SET_ELEMENT_UNCHECKED_NAME}, {CHIR::FILL_IN_STACK_TRACE, CHIR::FILL_IN_STACK_TRACE_NAME}, {CHIR::DECODE_STACK_TRACE, CHIR::DECODE_STACK_TRACE_NAME}, {CHIR::CHR, "chr"}, {CHIR::ORD, "ord"}, {CHIR::CPOINTER_GET_POINTER_ADDRESS, CHIR::CPOINTER_GET_POINTER_ADDRESS_NAME}, {CHIR::CPOINTER_INIT0, "pointerInit0"}, // CPointer constructor with no parameters {CHIR::CPOINTER_INIT1, "pointerInit1"}, // CPointer constructor with one parameter {CHIR::CPOINTER_READ, CHIR::CPOINTER_READ_NAME}, {CHIR::CPOINTER_WRITE, CHIR::CPOINTER_WRITE_NAME}, {CHIR::CPOINTER_ADD, CHIR::CPOINTER_ADD_NAME}, {CHIR::CSTRING_INIT, "_CString_init_"}, {CHIR::CSTRING_CONVERT_CSTR_TO_PTR, CHIR::CSTRING_CONVERT_CSTR_TO_PTR_NAME}, {CHIR::BIT_CAST, CHIR::BIT_CAST_NAME}, {CHIR::INOUT_PARAM, "_inout_"}, {CHIR::REGISTER_WATCHED_OBJECT, "registerWatchedObject"}, {CHIR::OBJECT_REFEQ, CHIR::OBJECT_REFEQ_NAME}, #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND {CHIR::RAW_ARRAY_REFEQ, CHIR::RAW_ARRAY_REFEQ_NAME}, #endif {CHIR::OBJECT_ZERO_VALUE, CHIR::OBJECT_ZERO_VALUE_NAME}, {CHIR::INVOKE_GC, CHIR::INVOKE_GC_NAME}, {CHIR::SET_GC_THRESHOLD, CHIR::SET_GC_THRESHOLD_NAME}, {CHIR::GET_MAX_HEAP_SIZE, CHIR::GET_MAX_HEAP_SIZE_NAME}, {CHIR::GET_ALLOCATE_HEAP_SIZE, CHIR::GET_ALLOCATE_HEAP_SIZE_NAME}, {CHIR::DUMP_CJ_HEAP_DATA, CHIR::DUMP_CJ_HEAP_DATA_NAME}, {CHIR::GET_GC_COUNT, CHIR::GET_GC_COUNT_NAME}, {CHIR::GET_GC_TIME_US, CHIR::GET_GC_TIME_US_NAME}, {CHIR::GET_GC_FREED_SIZE, CHIR::GET_GC_FREED_SIZE_NAME}, {CHIR::START_CJ_CPU_PROFILING, CHIR::START_CJ_CPU_PROFILING_NAME}, {CHIR::STOP_CJ_CPU_PROFILING, CHIR::STOP_CJ_CPU_PROFILING_NAME}, {CHIR::GET_REAL_HEAP_SIZE, CHIR::GET_REAL_HEAP_SIZE_NAME}, {CHIR::GET_THREAD_NUMBER, CHIR::GET_THREAD_NUMBER_NAME}, {CHIR::GET_BLOCKING_THREAD_NUMBER, CHIR::GET_BLOCKING_THREAD_NUMBER_NAME}, {CHIR::GET_NATIVE_THREAD_NUMBER, CHIR::GET_NATIVE_THREAD_NUMBER_NAME}, {CHIR::VARRAY_SET, CHIR::VARRAY_SET_NAME}, {CHIR::VARRAY_GET, CHIR::VARRAY_GET_NAME}, // About Future {CHIR::FUTURE_INIT, CHIR::FUTURE_INIT_NAME}, #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND {CHIR::FUTURE_IS_COMPLETE, CHIR::FUTURE_IS_COMPLETE_NAME}, {CHIR::FUTURE_WAIT, CHIR::FUTURE_WAIT_NAME}, {CHIR::FUTURE_NOTIFYALL, CHIR::FUTURE_NOTIFYALL_NAME}, #endif {CHIR::IS_THREAD_OBJECT_INITED, CHIR::IS_THREAD_OBJECT_INITED_NAME}, {CHIR::GET_THREAD_OBJECT, CHIR::GET_THREAD_OBJECT_NAME}, {CHIR::SET_THREAD_OBJECT, CHIR::SET_THREAD_OBJECT_NAME}, {CHIR::OVERFLOW_CHECKED_ADD, CHIR::OVERFLOW_CHECKED_ADD_NAME}, {CHIR::OVERFLOW_CHECKED_SUB, CHIR::OVERFLOW_CHECKED_SUB_NAME}, {CHIR::OVERFLOW_CHECKED_MUL, CHIR::OVERFLOW_CHECKED_MUL_NAME}, {CHIR::OVERFLOW_CHECKED_DIV, CHIR::OVERFLOW_CHECKED_DIV_NAME}, {CHIR::OVERFLOW_CHECKED_MOD, CHIR::OVERFLOW_CHECKED_MOD_NAME}, {CHIR::OVERFLOW_CHECKED_POW, CHIR::OVERFLOW_CHECKED_POW_NAME}, {CHIR::OVERFLOW_CHECKED_INC, CHIR::OVERFLOW_CHECKED_INC_NAME}, {CHIR::OVERFLOW_CHECKED_DEC, CHIR::OVERFLOW_CHECKED_DEC_NAME}, {CHIR::OVERFLOW_CHECKED_NEG, CHIR::OVERFLOW_CHECKED_NEG_NAME}, {CHIR::OVERFLOW_THROWING_ADD, CHIR::OVERFLOW_THROWING_ADD_NAME}, {CHIR::OVERFLOW_THROWING_SUB, CHIR::OVERFLOW_THROWING_SUB_NAME}, {CHIR::OVERFLOW_THROWING_MUL, CHIR::OVERFLOW_THROWING_MUL_NAME}, {CHIR::OVERFLOW_THROWING_DIV, CHIR::OVERFLOW_THROWING_DIV_NAME}, {CHIR::OVERFLOW_THROWING_MOD, CHIR::OVERFLOW_THROWING_MOD_NAME}, {CHIR::OVERFLOW_THROWING_POW, CHIR::OVERFLOW_THROWING_POW_NAME}, {CHIR::OVERFLOW_THROWING_INC, CHIR::OVERFLOW_THROWING_INC_NAME}, {CHIR::OVERFLOW_THROWING_DEC, CHIR::OVERFLOW_THROWING_DEC_NAME}, {CHIR::OVERFLOW_THROWING_NEG, CHIR::OVERFLOW_THROWING_NEG_NAME}, {CHIR::OVERFLOW_SATURATING_ADD, CHIR::OVERFLOW_SATURATING_ADD_NAME}, {CHIR::OVERFLOW_SATURATING_SUB, CHIR::OVERFLOW_SATURATING_SUB_NAME}, {CHIR::OVERFLOW_SATURATING_MUL, CHIR::OVERFLOW_SATURATING_MUL_NAME}, {CHIR::OVERFLOW_SATURATING_DIV, CHIR::OVERFLOW_SATURATING_DIV_NAME}, {CHIR::OVERFLOW_SATURATING_MOD, CHIR::OVERFLOW_SATURATING_MOD_NAME}, {CHIR::OVERFLOW_SATURATING_POW, CHIR::OVERFLOW_SATURATING_POW_NAME}, {CHIR::OVERFLOW_SATURATING_INC, CHIR::OVERFLOW_SATURATING_INC_NAME}, {CHIR::OVERFLOW_SATURATING_DEC, CHIR::OVERFLOW_SATURATING_DEC_NAME}, {CHIR::OVERFLOW_SATURATING_NEG, CHIR::OVERFLOW_SATURATING_NEG_NAME}, {CHIR::OVERFLOW_WRAPPING_ADD, CHIR::OVERFLOW_WRAPPING_ADD_NAME}, {CHIR::OVERFLOW_WRAPPING_SUB, CHIR::OVERFLOW_WRAPPING_SUB_NAME}, {CHIR::OVERFLOW_WRAPPING_MUL, CHIR::OVERFLOW_WRAPPING_MUL_NAME}, {CHIR::OVERFLOW_WRAPPING_DIV, CHIR::OVERFLOW_WRAPPING_DIV_NAME}, {CHIR::OVERFLOW_WRAPPING_MOD, CHIR::OVERFLOW_WRAPPING_MOD_NAME}, {CHIR::OVERFLOW_WRAPPING_POW, CHIR::OVERFLOW_WRAPPING_POW_NAME}, {CHIR::OVERFLOW_WRAPPING_INC, CHIR::OVERFLOW_WRAPPING_INC_NAME}, {CHIR::OVERFLOW_WRAPPING_DEC, CHIR::OVERFLOW_WRAPPING_DEC_NAME}, {CHIR::OVERFLOW_WRAPPING_NEG, CHIR::OVERFLOW_WRAPPING_NEG_NAME}, {CHIR::REFLECTION_INTRINSIC_START_FLAG, "reflectionIntrinsicStart"}, // REFLECTION #define REFLECTION_KIND_TO_RUNTIME_FUNCTION(REFLECTION_KIND, CJ_FUNCTION, RUNTIME_FUNCTION) \ {CHIR::REFLECTION_KIND, #CJ_FUNCTION}, #include "cangjie/CHIR/LLVMReflectionIntrinsics.def" #undef REFLECTION_KIND_TO_RUNTIME_FUNCTION {CHIR::REFLECTION_INTRINSIC_END_FLAG, "reflectionIntrinsicEnd"}, {CHIR::SLEEP, CHIR::SLEEP_NAME}, {CHIR::SOURCE_FILE, CHIR::SOURCE_FILE_NAME}, {CHIR::SOURCE_LINE, CHIR::SOURCE_LINE_NAME}, {CHIR::IDENTITY_HASHCODE, CHIR::IDENTITY_HASHCODE_NAME}, {CHIR::IDENTITY_HASHCODE_FOR_ARRAY, CHIR::IDENTITY_HASHCODE_FOR_ARRAY_NAME}, #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // SYNC {CHIR::ATOMIC_LOAD, CHIR::ATOMIC_LOAD_NAME}, {CHIR::ATOMIC_STORE, CHIR::ATOMIC_STORE_NAME}, {CHIR::ATOMIC_SWAP, CHIR::ATOMIC_SWAP_NAME}, {CHIR::ATOMIC_COMPARE_AND_SWAP, CHIR::ATOMIC_COMPARE_AND_SWAP_NAME}, {CHIR::ATOMIC_FETCH_ADD, CHIR::ATOMIC_FETCH_ADD_NAME}, {CHIR::ATOMIC_FETCH_SUB, CHIR::ATOMIC_FETCH_SUB_NAME}, {CHIR::ATOMIC_FETCH_AND, CHIR::ATOMIC_FETCH_AND_NAME}, {CHIR::ATOMIC_FETCH_OR, CHIR::ATOMIC_FETCH_OR_NAME}, {CHIR::ATOMIC_FETCH_XOR, CHIR::ATOMIC_FETCH_XOR_NAME}, {CHIR::MUTEX_INIT, CHIR::MUTEX_INIT_NAME}, {CHIR::CJ_MUTEX_LOCK, CHIR::MUTEX_LOCK_NAME}, {CHIR::MUTEX_TRY_LOCK, CHIR::MUTEX_TRY_LOCK_NAME}, {CHIR::MUTEX_CHECK_STATUS, CHIR::MUTEX_CHECK_STATUS_NAME}, {CHIR::MUTEX_UNLOCK, CHIR::MUTEX_UNLOCK_NAME}, {CHIR::WAITQUEUE_INIT, CHIR::WAITQUEUE_INIT_NAME}, {CHIR::MONITOR_INIT, CHIR::MONITOR_INIT_NAME}, {CHIR::MOITIOR_WAIT, CHIR::MOITIOR_WAIT_NAME}, {CHIR::MOITIOR_NOTIFY, CHIR::MOITIOR_NOTIFY_NAME}, {CHIR::MOITIOR_NOTIFY_ALL, CHIR::MOITIOR_NOTIFY_ALL_NAME}, {CHIR::MULTICONDITION_WAIT, CHIR::MULTICONDITION_WAIT_NAME}, {CHIR::MULTICONDITION_NOTIFY, CHIR::MULTICONDITION_NOTIFY_NAME}, {CHIR::MULTICONDITION_NOTIFY_ALL, CHIR::MULTICONDITION_NOTIFY_ALL_NAME}, {CHIR::VECTOR_COMPARE_32, CHIR::VECTOR_COMPARE_32_NAME}, {CHIR::VECTOR_INDEX_BYTE_32, CHIR::VECTOR_INDEX_BYTE_32_NAME}, {CHIR::CJ_CORE_CAN_USE_SIMD, CHIR::CJ_CORE_CAN_USE_SIMD_NAME}, {CHIR::CROSS_ACCESS_BARRIER, CHIR::CROSS_ACCESS_BARRIER_NAME}, {CHIR::CREATE_EXPORT_HANDLE, CHIR::CREATE_EXPORT_HANDLE_NAME}, {CHIR::GET_EXPORTED_REF, CHIR::GET_EXPORTED_REF_NAME}, {CHIR::REMOVE_EXPORTED_REF, CHIR::REMOVE_EXPORTED_REF_NAME}, #endif {CHIR::CJ_TLS_DYN_SET_SESSION_CALLBACK, CHIR::CJ_TLS_DYN_SET_SESSION_CALLBACK_NAME}, {CHIR::CJ_TLS_DYN_SSL_INIT, CHIR::CJ_TLS_DYN_SSL_INIT_NAME}, // CodeGen {CHIR::CG_UNSAFE_BEGIN, "cgUnsafeBegin"}, {CHIR::CG_UNSAFE_END, "cgUnsafeEnd"}, // CHIR 2: Exception intrinsic {CHIR::BEGIN_CATCH, "beginCatch"}, // CHIR 2: Math intrinsic {CHIR::ABS, "abs"}, {CHIR::FABS, "fabs"}, {CHIR::FLOOR, "floor"}, {CHIR::CEIL, "ceil"}, {CHIR::TRUNC, "trunc"}, {CHIR::SIN, "sin"}, {CHIR::COS, "cos"}, {CHIR::EXP, "exp"}, {CHIR::EXP2, "exp2"}, {CHIR::LOG, "log"}, {CHIR::LOG2, "log2"}, {CHIR::LOG10, "log10"}, {CHIR::SQRT, "sqrt"}, {CHIR::ROUND, "round"}, {CHIR::POW, "pow"}, {CHIR::POWI, "powi"}, // preinitialize intrinsic {CHIR::PREINITIALIZE, "preinitialize"}, {CHIR::IS_THREAD_OBJECT_INITED, IS_THREAD_OBJECT_INITED_NAME}, // Box cast intrinsic {CHIR::OBJECT_AS, "object.as"}, {CHIR::IS_NULL, "isNull"}, {CHIR::BLACK_BOX, "blackBox"} }; const std::unordered_map PACKAGE_ACCESS_LEVEL_TO_STRING_MAP = { {CHIR::Package::AccessLevel::INTERNAL, "internal"}, {CHIR::Package::AccessLevel::PROTECTED, "protected"}, {CHIR::Package::AccessLevel::PUBLIC, "public"}, }; class BlockGroup; class Block; class Func; class Lambda; class ImportedValue; class ClassType; /** * @brief Prints indentation to the specified stream. * * @param stream The output stream to print to. * @param indent The number of indentation levels. */ void PrintIndent(std::ostream& stream, size_t indent = 1); /** * @brief Generates a string representation of generic constraints. * * @param genericTypeParams The list of generic type parameters. * @return A string representing the generic constraints. */ std::string GetGenericConstaintsStr(const std::vector& genericTypeParams); /** * @brief Generates a string representation of a block group. * * @param blockGroup The block group to represent. * @param indent The number of indentation levels. * @return A string representing the block group. */ std::string GetBlockGroupStr(const BlockGroup& blockGroup, size_t indent = 0); /** * @brief Generates a string representation of a block. * * @param block The block to represent. * @param indent The number of indentation levels. * @return A string representing the block. */ std::string GetBlockStr(const Block& block, size_t indent = 0); /** * @brief Generates a string representation of a function. * * @param func The function to represent. * @param indent The number of indentation levels. * @return A string representing the function. */ std::string GetFuncStr(const Func& func, size_t indent = 0); std::string FuncSymbolStr(const Func& func); /** * @brief Generates a string representation of a lambda expression. * * @param lambda The lambda expression to represent. * @param indent The number of indentation levels. * @return A string representing the lambda expression. */ std::string GetLambdaStr(const Lambda& lambda, size_t indent = 0); /** * @brief Generates a string representation of an imported value. * * @param value The imported value to represent. * @return A string representing the imported value. */ std::string GetImportedValueStr(const ImportedValue& value); /** * @brief Generates a string representation of an imported function. * * @param value The imported function to represent. * @return A string representing the imported function. */ std::string GetImportedFuncStr(const ImportedFunc& value); /** * @brief Generates a string representation of exceptions. * * @param exceptions The list of exception classes. * @return A string representing the exceptions. */ std::string GetExceptionsStr(const std::vector& exceptions); /** * @brief Generates a string representation of generic type parameters. * * @param genericTypeParams The list of generic type parameters. * @return A string representing the generic type parameters. */ std::string GetGenericTypeParamsStr(const std::vector& genericTypeParams); /** * @brief Generates a string representation of generic type parameter constraints. * * @param genericTypeParams The list of generic type parameters. * @return A string representing the generic type parameter constraints. */ std::string GetGenericTypeParamsConstraintsStr(const std::vector& genericTypeParams); /** * @brief Adds a comma or not to the string stream based on a condition. * * @param ss The string stream to modify. */ void AddCommaOrNot(std::stringstream& ss); /** * @brief Converts a package access level to a string. * * @param level The package access level to convert. * @return A string representing the package access level. */ std::string PackageAccessLevelToString(const Package::AccessLevel& level); /** * @brief Converts a custom type kind to a string. * * @param def The custom type definition to convert. * @return A string representing the custom type kind. */ std::string CustomTypeKindToString(const CustomTypeDef& def); /** * @brief Converts a boolean value to a string. * * @param flag The boolean value to convert. * @return A string representing the boolean value. */ std::string BoolToString(bool flag); StringWrapper ThisTypeToString(const Type* thisType); std::string InstTypeArgsToString(const std::vector& instTypeArgs); std::string ExprOperandsToString(const std::vector& args); std::string ExprWithExceptionOperandsToString(const std::vector& args, const std::vector& successors); std::string OverflowToString(Cangjie::OverflowStrategy ofStrategy); } // namespace Cangjie::CHIR #endif // CANGJIE_CHIR_TOSTRING_UTILS_H cangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/000077500000000000000000000000001510705540100235655ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/ArrayLambdaOpt.h000066400000000000000000000031311510705540100265760ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_ARRAY_LAMBDA_OPT_H #define CANGJIE_CHIR_TRANSFORMATION_ARRAY_LAMBDA_OPT_H #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { /** * CHIR Opt Pass: optimize CHIR IR from lambda function init to value init. */ class ArrayLambdaOpt { public: /** * @brief constructor of array lambda optimization pass. * @param builder CHIR builder for generating IR. */ explicit ArrayLambdaOpt(CHIRBuilder& builder); /** * @brief run array lambda optimization on a certain package CHIR IR. * @param package package to do optimization. * @param isDebug flag whether print debug log. */ void RunOnPackage(const Ptr& package, bool isDebug); private: void RunOnFunc(const Ptr& func, bool isDebug); Ptr CheckCanRewriteLambda(const Ptr& expr) const; Ptr CheckIfLambdaReturnConst(const Lambda& lambda) const; void RewriteArrayInitFunc(Apply& apply, const Ptr& constant); Ptr CheckCanRewriteZeroValue(const Ptr& expr) const; void RewriteZeroValue(const Ptr& init, const Ptr& zeroVal) const; CHIRBuilder& builder; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/ArrayListConstStartOpt.h000066400000000000000000000035761510705540100303730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_ARRAYLIST_CONST_START_OPT_H #define CANGJIE_CHIR_TRANSFORMATION_ARRAYLIST_CONST_START_OPT_H #include "cangjie/CHIR/Transformation/FunctionInline.h" namespace Cangjie::CHIR { /** * CHIR Opt Pass: optimization for cangjie array list loop and start point. * 1. inline special func with array loop function. * 2. replace start point call with const zero. */ class ArrayListConstStartOpt { public: /** * @brief constructor of optimization for cangjie array list loop and start point. * @param builder CHIR builder for generating IR. * @param opts global options of cangjie inputs. * @param pass inline opt pass. */ explicit ArrayListConstStartOpt(CHIRBuilder& builder, const GlobalOptions& opts, FunctionInline& pass) : builder(builder), opts(opts), pass(pass) { } /** * @brief run array list start optimization on a certain package CHIR IR. * @param package package to do optimization. */ void RunOnPackage(const Ptr& package); /** * @brief Get effect map after this pass. * @return effect map affected by this pass. */ const OptEffectCHIRMap& GetEffectMap() const; private: bool CheckNeedRewrite(const Apply& apply) const; bool IsStartAddIndexExpression(const Field& field, bool isIteratorFunc) const; void RewriteStartWithConstZero(Expression& oldExpr) const; CHIRBuilder& builder; const std::string optPassName{"ArrayListConstStartOpt Inline"}; const GlobalOptions& opts; FunctionInline& pass; OptEffectCHIRMap effectMap; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/BlockGroupCopyHelper.h000066400000000000000000000057131510705540100300060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file instantiate functions for CHIR pass Function Inline and Devirtualization */ #ifndef CANGJIE_CHIR_TRANSFORMATION_BLOCK_GROUP_COPY_HELPER_H #define CANGJIE_CHIR_TRANSFORMATION_BLOCK_GROUP_COPY_HELPER_H #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/Expression/Terminator.h" namespace Cangjie::CHIR { /** * helper class for copying blockgroup, do instantiation if instMap is set from apply * using in two pass now: * 1. devirtalizaion * 2. function inline */ class BlockGroupCopyHelper { public: /** * @brief constructor of block group copy helper class * @param builder CHIR builder for generating IR */ explicit BlockGroupCopyHelper(CHIRBuilder& builder) : builder(builder) { } /** * @brief main entry for copying block group, return result block group and its return value. * @param other block group copy from * @param parentFunc parent func where copy block in * @return */ std::pair CloneBlockGroup(const BlockGroup& other, Func& parentFunc); /** * @brief get instantiation map from apply call, using for function instantiation. * @param apply input apply expression to get instantiation map */ void GetInstMapFromApply(const Apply& apply); /** * @brief replace value with extra value map, such as parameter value map from old block group. * @param block block to replace value * @param valueMap extra value map to replace value */ void SubstituteValue(Ptr block, std::unordered_map& valueMap); private: void InstBlockGroup(Ptr group); static void CollectValueMap(const Lambda& oldLambda, const Lambda& newLambda, std::unordered_map& valueMap, std::unordered_set& newDebugs); static void CollectValueMap(const Block& oldBlk, const Block& newBlk, std::unordered_map& valueMap, std::unordered_set& newDebugs); static void CollectValueMap(const BlockGroup& oldBG, const BlockGroup& newBG, std::unordered_map& valueMap, std::unordered_set& newDebugs); static void ReplaceExprOperands(const Block& block, const std::unordered_map& valueMap); static void ReplaceExprOperands(const BlockGroup& bg, const std::unordered_map& valueMap); // map for block group instantiation std::unordered_map instMap; // mark this type if need replace this type Type* thisType = nullptr; CHIRBuilder& builder; }; void FixCastProblemAfterInst(Ptr group, CHIRBuilder& builder); } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/BoxRecursionValueType.h000066400000000000000000000022301510705540100302140ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_BOX_RECURSION_VALUE_TYPE_H #define CANGJIE_CHIR_TRANSFORMATION_BOX_RECURSION_VALUE_TYPE_H #include "cangjie/CHIR/CHIRBuilder.h" namespace Cangjie::CHIR { /** * CHIR Normal Pass: add box and unbox between of several certain expressions, such as GetElementRef, tuple. */ class BoxRecursionValueType { public: /** * @brief constructor for pass to add box and unbox expressions. * @param pkg input package. * @param builder CHIR builder for generating IR. */ BoxRecursionValueType(Package& pkg, CHIRBuilder& builder); /** * @brief main process to add box and unbox expressions. */ void CreateBoxTypeForRecursionValueType(); private: void CreateBoxTypeForRecursionEnum(); void CreateBoxTypeForRecursionStruct(); void InsertBoxAndUnboxExprForRecursionValueType(); Package& pkg; CHIRBuilder& builder; }; } #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/ClosureConversion.h000066400000000000000000000170001510705540100274160ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_CLOSURE_CONVERSION_H #define CANGJIE_CHIR_TRANSFORMATION_CLOSURE_CONVERSION_H #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/Package.h" #include "cangjie/Option/Option.h" namespace Cangjie::CHIR { /** * CHIR Normal Pass: mainly change lambda to normal global func and deal with related issues. */ class ClosureConversion { public: /** * @brief constructor to do closure conversion. * @param package input package to do closure conversion. * @param builder CHIR builder for generating IR. * @param opts global options from Cangjie inputs. * @param srcCodeImportedFuncMap */ ClosureConversion(Package& package, CHIRBuilder& builder, const GlobalOptions& opts, const std::unordered_set& srcCodeImportedFuncs); /** * @brief Main process to do closure conversion. */ void Convert(); /** * @brief Get mangle name from functions generated from closure conversion. * @return mangle names generated from closure conversion. */ std::set GetCCOutFuncsRawMangle() const; /** * @brief Get useless class def after closure conversion, will delete in remove useless pass. * @return useless class def after closure conversion. */ std::unordered_set GetUselessClassDef() const; /** * @brief Get useless lambda after closure conversion, will delete in remove useless pass. * @return useless lambda after closure conversion. */ std::unordered_set GetUselessLambda() const; private: enum class PrintType { NESTED_FUNC, TOP_LEVEL_FUNC, BOX_MUT_VAR }; void ConvertGlobalFunctions(); std::vector CollectNestedFunctions(); void InlineLambda(const std::vector& funcs); void ConvertNestedFunctions(const std::vector& funcs); void ConvertImportedFunctions(); Ptr CreateAutoEnvImplObject( Block& parent, ClassType& autoEnvImplType, const std::vector& envs, Expression& user, Value& srcFunc); void LiftType(); void LiftGenericTypes(); Type* ConvertTypeToClosureType(Type& type); Type* ConvertCompositionalType(const Type& type); FuncType* ConvertFuncArgsAndRetType(const FuncType& oldFuncTy); std::vector BoxAllMutableVars(const std::vector& rawEnvs); Value* BoxMutableVar(LocalVar& env); ClassDef* CreateBoxClassDef(Type& type); LocalVar* CreateBoxClassObj(const LocalVar& env, const ClassDef& classDef); void ReplaceEnvWithBoxObjMemberVar(LocalVar& env, LocalVar& boxObj, LocalVar& lValue); void LiftNestedFunctionWithCFuncType(Lambda& nestedFunc); std::pair SetBoxClassAsMutableVar(LocalVar& rValue); void RecordDuplicateLambdaName(const Lambda& func); std::string GenerateGlobalFuncIdentifier(const Lambda& lambda); ClassType* GenerateInstantiatedClassType(ClassType& autoEnvImplType, const Expression& user, Value& srcFunc); std::vector ConvertArgsType(const std::vector& types); Type* ConvertTupleType(const TupleType& type); Type* ConvertFuncType(const FuncType& type); Type* ConvertEnumType(const EnumType& type); Type* ConvertStructType(const StructType& type); Type* ConvertClassType(const ClassType& type); Type* ConvertRawArrayType(const RawArrayType& type); Type* ConvertVArrayType(const VArrayType& type); Type* ConvertCPointerType(const CPointerType& type); Type* ConvertRefType(const RefType& type); Type* ConvertBoxType(const BoxType& type); Package& package; CHIRBuilder& builder; ClassDef& objClass; const GlobalOptions& opts; const std::unordered_set& srcCodeImportedFuncs; // key: any type, value: closure type std::unordered_map typeConvertMap; std::unordered_map boxClassMap; std::unordered_map convertedCache; std::unordered_map duplicateLambdaName; ClassDef* GetOrCreateGenericAutoEnvBaseDef(size_t paramNum); ClassDef* GetOrCreateAutoEnvBaseDef(const FuncType& funcType); ClassDef* CreateAutoEnvImplDef(const std::string& className, const std::vector& genericTypes, const Value& srcFunc, ClassDef& superClassDef, std::unordered_map& originalTypeToNewType); ClassDef* GetOrCreateAutoEnvImplDef(FuncBase& func, ClassDef& superClassDef); ClassDef* GetOrCreateAutoEnvImplDef(Lambda& func, ClassDef& superClassDef, const std::vector& boxedEnvs); void CreateInstOverrideMethodInAutoEnvImplDef(ClassDef& autoEnvImplDef, FuncBase& srcFunc, const std::unordered_map& originalTypeToNewType); void CreateGenericOverrideMethodInAutoEnvImplDef(ClassDef& autoEnvImplDef, FuncBase& srcFunc, const std::unordered_map& originalTypeToNewType); void CreateMemberVarInAutoEnvImplDef(ClassDef& parentClass, const std::vector& boxedEnvs, const std::unordered_map& originalTypeToNewType); void ReplaceUserPoint(FuncBase& srcFunc, Expression& user, ClassDef& autoEnvImplDef); void ReplaceUserPoint( Lambda& srcFunc, Expression& user, const std::vector& envs, ClassDef& autoEnvImplDef); void ConvertExpressions(); void ConvertApplyWithExceptionToInvokeWithException(const std::vector& applyExprs); void ConvertApplyToInvoke(const std::vector& applyExprs); void CreateVTableForAutoEnvDef(); bool LambdaCanBeInlined(const Expression& user, const FuncBase& lambda); void DoFunctionInlineForLambda(); ClassDef* GetOrCreateInstAutoEnvBaseDef(const FuncType& funcType, ClassDef& superClass); Func* LiftLambdaToGlobalFunc( ClassDef& autoEnvImplDef, Lambda& nestedFunc, const std::vector& genericTypeParams, const std::unordered_map& instMap, const std::vector& capturedValues); void ModifyTypeMismatchInExpr(); void WrapApplyRetVal(Apply& apply); void WrapApplyWithExceptionRetVal(ApplyWithException& apply); void WrapInvokeRetVal(Expression& e); void WrapInvokeWithExceptionRetVal(Expression& e); void WrapGetElementRefRetVal(GetElementRef& getEleRef); void WrapFieldRetVal(Field& field); void WrapTypeCastSrcVal(TypeCast& typecast); ClassDef* GetOrCreateAutoEnvWrapper(ClassType& instAutoEnvBaseType); ClassDef* CreateAutoEnvWrapper(const std::string& className, ClassType& superClassType); void CreateMemberVarInAutoEnvWrapper(ClassDef& autoEnvWrapperDef); Func* CreateGenericMethodInAutoEnvWrapper(ClassDef& autoEnvWrapperDef); void CreateInstMethodInAutoEnvWrapper(ClassDef& autoEnvWrapperDef, Func& genericFunc); std::unordered_map genericAutoEnvBaseDefs; std::unordered_map instAutoEnvBaseDefs; std::unordered_map instAutoEnvWrapperDefs; std::unordered_map autoEnvImplDefs; std::unordered_set needConvertedGenericTys; std::set ccOutFuncsRawMangle; std::unordered_set uselessClasses; std::unordered_set uselessLambda; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/ConstPropagation.h000066400000000000000000000133351510705540100272350ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_CONST_PROPAGATION_H #define CANGJIE_CHIR_TRANSFORMATION_CONST_PROPAGATION_H #include "cangjie/CHIR/Analysis/AnalysisWrapper.h" #include "cangjie/CHIR/Analysis/ConstAnalysis.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Transformation/DeadCodeElimination.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { /** * CHIR Opt Pass: do optimization with analysis results of const value analysis. */ class ConstPropagation { public: /** * @brief const analysis wrapper to call const analysis. */ using ConstAnalysisWrapper = AnalysisWrapper; /** * @brief constructor to do const propagation. * @param builder CHIR builder for generating IR. * @param constAnalysisWrapper const analysis wrapper which produce analysis results. * @param options global options from Cangjie inputs. */ explicit ConstPropagation(CHIRBuilder& builder, ConstAnalysisWrapper* constAnalysisWrapper, const GlobalOptions& options); /** * @brief Main process to do const propagation. * @param package package to do optimization. * @param isDebug flag whether print debug log. * @param isCJLint flag whether CJLint is enabled. */ void RunOnPackage(const Ptr& package, bool isDebug, bool isCJLint); /** * @brief Main process to do const propagation per func. * @param func func to do optimization. * @param isDebug flag whether print debug log. * @param isCJLint flag whether CJLint is enabled. */ void RunOnFunc(const Ptr& func, bool isDebug, bool isCJLint = false); /** * @brief Get effect map after this pass. * @return effect map affected by this pass. */ const OptEffectCHIRMap& GetEffectMap() const; /** * @brief Get all funcs need to remove unreachable blocks. * @return functions */ const std::vector& GetFuncsNeedRemoveBlocks() const; private: struct RewriteInfo { Expression* oldExpr; size_t index; // the index of the oldExpr in its parent block LiteralValue* literalVal; RewriteInfo(Expression* oldExpr, size_t index, LiteralValue* literalVal) : oldExpr(oldExpr), index(index), literalVal(literalVal) { } }; // ==================== Rewrite Non-terminator Expressions ==================== // /** * This function will generate a literal value based on the constant information * from @p constVal. The type of the literal value is @p type. */ Ptr GenerateConstExpr( const Ptr& type, const Ptr& constVal, bool isCJLint = false); /** * This function will rewrite an expression based on the @p rewriteInfo, which stores * the exrpession to be rewrited, the index of this expression and the new expression. */ void RewriteToConstExpr(const RewriteInfo& rewriteInfo, bool isDebug) const; /** * This function will check if a unary expression can be simplified according to the rules * of arithmetic when there is *no constant information* about the operand of the expression. * If it can be simplified, the usages of the result of this unary expression will be replaced * by its operand. * * Here is a list of the operation this function handles. * a) NOT: `!(!b) => b` * b) BITNOT: `!(!x) => x` * * note: `-(-a) != a` as there might be an overflow while calculating `(-a)`. */ void TrySimplifyingUnaryExpr(const Ptr& unary, bool isDebug) const; /** * This function will check if a binary expression can be simplified according to the rules * of arithmetic when there is *no constant information* about the operand of the expression. * If it can be simplified, the usages of the result of this binary expression will be replaced * by its operand. * * Here is a list of the operation this function handles. * a) ADD: `0 + a => a`, 'a + 0 => a' * b) SUB: `a - 0 => a` * c) MUL: `1 * a => a`, `a * 1 => a` * d) DIV: `a / 1 => a` * e) EXP: `a ** 1 => a` * f) LSHIFT: `a << 0 => a` * g) RSHIFT: `a >> 0 => a` * h) BITAND: `a & a => a` * j) BITOR: `a | a => a` * * note: We don't rewrite `0 - a` to `-a` as CodeGen will rewrite `-a` to `0 - a`. */ template void TrySimplifyingBinaryExpr(const ConstDomain& state, const Ptr& binary, bool isDebug); /** * This function will replaced all use of the result of the expression @p expr with the value * @p newVal. A debug message will also be printed if @p isDebug is true. */ void ReplaceUsageOfExprResult(const Ptr& expr, const Ptr& newVal, bool isDebug) const; // ==================== Rewrite Terminator Expressions ==================== // void RewriteTerminator(Terminator* oldTerminator, LiteralValue* newValue, Block* newTarget, bool isDebug) const; // ==================== Rewrite Terminator Expressions ==================== // void RecordEffectMap(const Expression* expr, const Func* func) const; private: CHIRBuilder& builder; ConstAnalysisWrapper* analysisWrapper; const GlobalOptions& opts; static OptEffectCHIRMap effectMap; std::vector funcsNeedRemoveBlocks; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/DeadCodeElimination.h000066400000000000000000000147061510705540100275670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_DEAD_CODE_ELIMINATION_H #define CANGJIE_CHIR_TRANSFORMATION_DEAD_CODE_ELIMINATION_H #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/DiagAdapter.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Package.h" #include "cangjie/Utils/TaskQueue.h" namespace Cangjie::CHIR { /** * CHIR Opt Pass: summary of useless code elimination pass. */ class DeadCodeElimination { public: /** * @brief constructor for dead code elimination pass. * @param builder CHIR builder for generating IR. * @param diag cangjie error or warning reporter. * @param packageName this package name. */ explicit DeadCodeElimination(CHIRBuilder& builder, DiagAdapter& diag, const std::string& packageName); /** * @brief process to do useless function elimination. * @param package package to do dead code elimination. * @param opts global options from Cangjie inputs. */ void UselessFuncElimination(Package& package, const GlobalOptions& opts); /** * @brief process to do useless expr elimination. * @param package package to do dead code elimination. * @param isDebug flag whether print debug log. */ void UselessExprElimination(const Package& package, bool isDebug) const; /** * @brief process to delete nothing type. * @param package package to do dead code elimination. * @param isDebug flag whether print debug log. */ void NothingTypeExprElimination(const Package& package, bool isDebug); /** * @brief process to do unreachable block elimination. * @param package package to do dead code elimination. * @param isDebug flag whether print debug log. */ void UnreachableBlockElimination(const Package& package, bool isDebug) const; /** * @brief process to do unreachable block elimination. * @param funcs functions to do dead code elimination. * @param isDebug flag whether print debug log. */ void UnreachableBlockElimination(const std::vector& funcs, bool isDebug) const; /** * @brief process to report unreachable block warning. * @param package package to report warning. * @param threadsNum threads num join to do this pass. * @param maybeUnreachableBlocks may be unreachable blocks to report. */ void UnreachableBlockWarningReporter(const Package& package, size_t threadsNum, const std::unordered_map& maybeUnreachableBlocks); /** * @brief process to remove blocks which is marked unreachable. * @param package package to clear block marked unreachable. */ void ClearUnreachableMarkBlock(const Package& package) const; /** * @brief process to report unused block warning. * @param package package to report warning. * @param opts global options from Cangjie inputs. */ void ReportUnusedCode(const Package& package, const GlobalOptions& opts); private: CHIRBuilder& builder; DiagAdapter& diag; const std::string& currentPackageName; const std::string GLOBAL_INIT_MANGLED_NAME = "_global_init"; const std::string STD_CORE_FUTURE_MANGLED_NAME = "_CNat6Future"; const std::string STD_CORE_EXECUTE_CLOSURE_MANGLED_NAME = "executeClosure"; // =============== Functions for Useless Variable Check =============== // void UselessVariableCheckForFunc(const BlockGroup& funcBody, bool isDebug); bool CheckOneUsers(const std::vector& users) const; bool CheckTwoUsers(const std::vector& users) const; void UselessExprEliminationForFunc(const Func& func, bool isDebug) const; // =============== Functions for Nothing type Check =============== // void NothingTypeExprEliminationForFunc(BlockGroup& funcBody, bool isDebug); static bool CheckAllUsersIsNotUse(const Value& value, const std::vector& users); // =============== Functions for Useless Func Elimination =============== // bool CheckUselessFunc(const Func& func, const GlobalOptions& opts); // =============== Functions for Unreachable Block Elimination =============== // bool CheckUselessBlock(const Block& block) const; void BreakBranchConnection(const Block& block) const; void ClearUnreachableMarkBlockForFunc(const BlockGroup& body) const; void UnreachableBlockEliminationForFunc(const BlockGroup& body, bool isDebug) const; // =============== Functions for Useless IR Elimination =============== // bool CheckUselessExpr(const Expression& expr, bool isReportWarning = false) const; // =============== Functions for Debug Message Dump =============== // Ptr GetUnreachableExpression(const CHIR::Block& block, bool& isNormal) const; void PrintUnreachableBlockWarning( const CHIR::Block& block, const CHIR::Terminator& terminator, bool& isPrinted); // =============== Functions for dce reporter =============== // void TryReportUnusedOnExpr(Expression& expr, const GlobalOptions& opts, bool blockUsed); void ReportUnusedFunc(const Func& func, const GlobalOptions& opts); void ReportUnusedGlobalVar(const GlobalVar& globalVar); void DiagUnusedVariable(const Debug& expr); void ReportUnusedLocalVariable(const Expression& expr, bool isDebug); void ReportUnusedExpression(Expression& expr); template void DiagUnusedCode( const std::pair& nodeRange, DiagKindRefactor diagKind, Args&& ... args); void DiagUnusedVariableForParam(const Debug& expr); void DiagUnusedVariableForLocalVar(const Debug& expr, bool isDebug); void DiagUnusedLambdaVariable(const Debug& expr); std::string GetLiteralFromExprKind(const ExprKind& kind) const; // ============== Functions for clean code in parallel ===========// void ReportUnusedCodeInFunc(const BlockGroup& body, const GlobalOptions& opts); void UnreachableBlockWarningReporterInSerial( const Package& package, const std::unordered_map& maybeUnreachableBlocks); void UnreachableBlockWarningReporterInParallel(const Package& package, size_t threadsNum, const std::unordered_map& maybeUnreachableBlocks); }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/Devirtualization.h000066400000000000000000000102201510705540100272660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_DEVIRTUALIZATION_H #define CANGJIE_CHIR_TRANSFORMATION_DEVIRTUALIZATION_H #include "cangjie/CHIR/Analysis/AnalysisWrapper.h" #include "cangjie/CHIR/Analysis/DevirtualizationInfo.h" #include "cangjie/CHIR/Analysis/TypeAnalysis.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Value.h" #include namespace Cangjie::CHIR { class Devirtualization { public: /** * @brief wrapper for type analysis. */ using TypeAnalysisWrapper = AnalysisWrapper; /** * @brief rewrite info if a invoke can be de-virtualize. */ struct RewriteInfo { Invoke* invoke; FuncBase* realCallee; Type* thisType; std::vector typeArgs; Apply* newApply = nullptr; }; Devirtualization() = delete; /** * @brief constructor for Devirtualization pass. * @param typeAnalysisWrapper * @param devirtFuncInfo */ explicit Devirtualization(TypeAnalysisWrapper* typeAnalysisWrapper, DevirtualizationInfo& devirtFuncInfo); /** * @brief main optimization pass entry. * @param funcs funcs to devirtualization. * @param builder CHIR builder for generating IR. * @param isDebug flag whether print debug log. */ void RunOnFuncs(const std::vector& funcs, CHIRBuilder& builder, bool isDebug); /** * @brief get functions containing invoke expression. * @param package user package to optimization. * @return return functions containing invoke expression. */ static std::vector CollectContainInvokeExprFuncs(const Ptr& package); /// get optimized functions which are marked frozen. const std::vector& GetFrozenInstFuns() const; /// after first devirt pass, do second devirtualization for frozen func. /// this function mainly get results from second type analysis. void AppendFrozenFuncState(const Func* func, std::unique_ptr> analysisRes); /// function signature to determine a certain function. struct FuncSig { std::string name; std::vector types; std::vector typeArgs; }; private: void RunOnFunc(const Func* func, CHIRBuilder& builder); std::pair FindRealCallee( CHIRBuilder& builder, const TypeValue* typeState, const FuncSig& method) const; bool IsValidSubType(CHIRBuilder& builder, const Type* expected, Type* specific, std::unordered_map& replaceTable) const; bool IsInstantiationOf(CHIRBuilder& builder, const GenericType* generic, const Type* instantiated) const; void InstantiateFuncIfPossible(CHIRBuilder& builder, std::vector& rewriteInfoList); void CollectCandidates( CHIRBuilder& builder, ClassType* specific, std::pair& res, const FuncSig& method) const; FuncBase* GetCandidateFromSpecificType( CHIRBuilder& builder, ClassType& specific, const FuncSig& method) const; static void RewriteToApply(CHIRBuilder& builder, std::vector& rewriteInfos, bool isDebug); static bool RewriteToBuiltinOp(CHIRBuilder& builder, const RewriteInfo& info, bool isDebug); /** * check func whether has invoke expression, implement func for CollectContainInvokeExprFuncs */ static bool CheckFuncHasInvoke(const BlockGroup& bg); TypeAnalysisWrapper* analysisWrapper; DevirtualizationInfo& devirtFuncInfo; std::vector rewriteInfos{}; // frozen inst functions after devirt, these func need a devirt optimization too after first devirt opt std::vector frozenInstFuns; // extra type state from outside std::unordered_map>> frozenStates; std::unordered_map frozenInstFuncMap; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/FlatForInExpr.h000066400000000000000000000057711510705540100264330ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_FLAT_FORIN_EXPR_H #define CANGJIE_CHIR_TRANSFORMATION_FLAT_FORIN_EXPR_H #include "cangjie/CHIR/CHIRContext.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Package.h" namespace Cangjie::CHIR { /** * CHIR normaol Pass: mainly flat ForIn Expression, generate standard CHIR IR to replace ForIn Expr. */ class FlatForInExpr { public: /** * @brief constructor to flat ForIn Expression. * @param builder CHIR builder for generating IR. */ explicit FlatForInExpr(CHIRBuilder& builder); /** * @brief Main process to flat ForIn Expression. * @param package package to do optimization. */ void RunOnPackage(const Package& package); private: CHIRBuilder& builder; void RunOnFunc(Func& func); void RunOnBlockGroup(BlockGroup& blockGroup); using ExprIt = std::vector::iterator; // for-in-iter before translation // %0 = Apply(iterator, for-value) // %1 = Allocate(Int64) // delay exit signal // %2 = Allocate(Enum-Option<...>) // iterator var // %3 = Allocate(Bool) // cond var // Store(Constant(0), %1) // Store(Tuple(Constant(1)), %2) // init value is None, unused // Store(Constant(true), %3) // init value is true, unused // %4 = For(%2, %3) { // #2: body // do body things... // #3: latch // %5 = Apply(next, %0) // Store(%5, %2) // #4: cond // %6 = TypeCast(Enum-Option<...>, %5) // %7 = Field(%6, 0) // %8 = Not(%7) // Store(%8, %3) // } // GoTo(delay-exit-true-block) // after translation // for-in-iter after translation // %0 = Apply(iterator, for-value) // %1 = Allocate(Int64) // delay exit signal // %2 = Allocate(Enum-Option<...>) // iterator var // %3 = Allocate(Bool) // cond var // Store(Constant(0), %1) // Store(Tuple(Constant(1)), %2) // init value is None, unused // Store(Constant(true), %3) // init value is true, unused // GoTo(#3) // #3: latch // %5 = Apply(next, %0) // Store(%5, %2) // GoTo(#4) // #4: cond // %6 = TypeCast(Enum-Option<...>, %5) // %7 = Field(%6, 0) // %8 = Not(%7) // Store(%8, %3) // GoTo(#5) // new block, named jump block // #2: body // do body things... // GoTo(#3) // #5: jump block // %9 = Load(%3) // Branch(%9, #2, delay-exit-true-block) /// \param it the iterator to forIn /// \param end the iterator of the end of the block containing the forIn expression void FlatternForInExpr(ExprIt it, ExprIt end); void FlatternForInClosedRange(ExprIt it, ExprIt end); }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/FunctionInline.h000066400000000000000000000046741510705540100266750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares FunctionInline class for CHIR */ #ifndef CANGJIE_CHIR_TRANSFORMATION_FUNCTION_INLINE_H #define CANGJIE_CHIR_TRANSFORMATION_FUNCTION_INLINE_H #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/Option/Option.h" namespace Cangjie::CHIR { /** * CHIR Opt Pass: do function inline for CHIR IR. */ class FunctionInline { public: /** * @brief constructor for function inline pass * @param builder CHIR builder for generating IR. * @param optLevel optimization level from Cangjie inputs. * @param debug flag whether print debug log. */ FunctionInline(CHIRBuilder& builder, const GlobalOptions::OptimizationLevel& optLevel, bool debug) : builder(builder), optLevel(optLevel), debug(debug) { } /** * @brief Main process to do function inline. * @param func func to do function inline. */ void Run(Func& func); /** * @brief Get effect map after this pass. * @return effect map affected by this pass. */ const OptEffectCHIRMap& GetEffectMap() const; /** * @brief Main process to do function inline per apply. * @param apply apply expression to do function inline. * @param name pass name to pring log. */ void DoFunctionInline(const Apply& apply, const std::string& name); private: bool CheckCanRewrite(const Apply& apply); void RecordEffectMap(const Apply& apply); void ReplaceFuncResult(LocalVar* resNew, LocalVar* resOld); std::pair CloneBlockGroupForInline( const BlockGroup& other, Func& parentFunc, const Apply& apply); void SetGroupDebugLocation(BlockGroup& group, const DebugLocation& loc); void InlineImpl(BlockGroup& bg); CHIRBuilder& builder; const GlobalOptions::OptimizationLevel& optLevel; bool debug{false}; Func* globalFunc{nullptr}; std::unordered_map inlinedCountMap; std::unordered_map funcSizeMap; const std::string optName{"Function Inline"}; OptEffectCHIRMap effectMap; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/GetRefToArrayElem.h000066400000000000000000000017101510705540100272160ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_GET_REF_TO_ARRAY_ELEM_H #define CANGJIE_CHIR_TRANSFORMATION_GET_REF_TO_ARRAY_ELEM_H #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { /** * CHIR Opt Pass: ARRAY_GET_UNCHECKED intrinsic optimization. */ class GetRefToArrayElem { public: /** * @brief Main process to ARRAY_GET_UNCHECKED intrinsic optimization. * @param package package to do optimization. * @param builder CHIR builder for generating IR. */ static void RunOnPackage(const Package& package, CHIRBuilder& builder); private: static void RunOnFunc(const Func& func, CHIRBuilder& builder); }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/LambdaInline.h000066400000000000000000000030541510705540100262570ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_LAMBDA_INLINE_H #define CANGJIE_CHIR_TRANSFORMATION_LAMBDA_INLINE_H #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/Transformation/FunctionInline.h" #include "cangjie/Option/Option.h" namespace Cangjie::CHIR { /** * @brief inline lambda expression if meet condition as blow: * 1. only have one consumer as a callee to apply expression. * 2. only have one consumer as a parameter to apply expression, which will not escape in new function. */ class LambdaInline { public: /** * @brief lambda inline constructor. * @param builder chir builder to create IR. * @param opts options to indicate whether to do optimization. */ LambdaInline(CHIRBuilder& builder, const GlobalOptions& opts); /** * @brief interface to do lambda inline. * @param funcs all lambda functions in the package. */ void InlineLambda(const std::vector& funcs); private: /// run on single lambda void RunOnLambda(Lambda& lambda); /// judge whether you can do optimization ob a lambda if it is passed to a new easy function. bool IsLambdaPassToEasyFunc(const Lambda& lambda) const; const GlobalOptions& opts; /// function inline pass FunctionInline inlinePass; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/MarkClassHasInited.h000066400000000000000000000016331510705540100274120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_MARK_CLASS_HASINITED_H #define CANGJIE_CHIR_TRANSFORMATION_MARK_CLASS_HASINITED_H #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/Package.h" namespace Cangjie::CHIR { /** * CHIR normal Pass: add has invited flag to class which has finalizer, in case of finalize before init. */ class MarkClassHasInited { public: /** * @brief Main process to add has invited flag to class. * @param package package to do optimization. * @param builder CHIR builder for generating IR. */ static void RunOnPackage(const Package& package, CHIRBuilder& builder); }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/MergeBlocks.h000066400000000000000000000025231510705540100261350ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_MERGE_BLOCKS_H #define CANGJIE_CHIR_TRANSFORMATION_MERGE_BLOCKS_H #include "cangjie/Option/Option.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { /** * CHIR Opt Pass: do block merge for CHIR IR. */ class MergeBlocks { public: /** * @brief constructor for mergin blocks. */ explicit MergeBlocks() = default; /** * @brief Main process to do block merge. * @param package package to do optimization. * @param builder CHIR builder for generating IR. * @param opts global options from Cangjie inputs. */ static void RunOnPackage(const Package& package, CHIRBuilder& builder, const GlobalOptions& opts); /** * @brief Main process to do block merge per func. * @param body func body to merge blocks * @param builder CHIR builder for generating IR. * @param opts global options from Cangjie inputs. */ static void RunOnFunc(const BlockGroup& body, CHIRBuilder& builder, const GlobalOptions& opts); }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/NoSideEffectMarker.h000066400000000000000000000015411510705540100273770ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_NO_SIDE_EFFECT_MARKER_H #define CANGJIE_CHIR_TRANSFORMATION_NO_SIDE_EFFECT_MARKER_H #include "cangjie/CHIR/Expression/Expression.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { class NoSideEffectMarker { public: static void RunOnPackage(const Ptr& package, bool isDebug); static void RunOnFunc(const Ptr& value, bool isDebug); private: static bool CheckPackage(const std::string& packageName); static bool CheckNoSideEffectList(const Func& func); }; } // namespace CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/RangePropagation.h000066400000000000000000000076461510705540100272130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_RANGE_PROPAGATION_H #define CANGJIE_CHIR_TRANSFORMATION_RANGE_PROPAGATION_H #include "cangjie/CHIR/Analysis/AnalysisWrapper.h" #include "cangjie/CHIR/Analysis/ValueRangeAnalysis.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Transformation/DeadCodeElimination.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { /** * CHIR Opt Pass: do optimization with analysis results of range analysis. */ class RangePropagation { public: /** * @brief range analysis wrapper to call range analysis. */ using RangeAnalysisWrapper = AnalysisWrapper; /** * @brief constructor to do range propagation. * @param builder CHIR builder for generating IR. * @param rangeAnalysisWrapper range analysis wrapper which produce analysis results. * @param diag reporter to print warning * @param enIncre flag whether is incremental compile. */ explicit RangePropagation( CHIRBuilder& builder, RangeAnalysisWrapper* rangeAnalysisWrapper, DiagAdapter* diag, bool enIncre); /** * @brief Main process to do range propagation. * @param package package to do optimization. * @param isDebug flag whether print debug log. */ void RunOnPackage(const Ptr& package, bool isDebug); /** * @brief Main process to do const propagation per func. * @param func func to do optimization. * @param isDebug flag whether print debug log. */ void RunOnFunc(const Ptr& func, bool isDebug); /** * @brief Get effect map after this pass. * @return effect map affected by this pass. */ const OptEffectCHIRMap& GetEffectMap() const; /** * @brief Get all funcs need to remove unreachable blocks. * @return functions */ const std::vector& GetFuncsNeedRemoveBlocks() const; private: struct RewriteInfo { Expression* oldExpr; size_t index; // the index of the oldExpr in its parent block LiteralValue* literalVal; RewriteInfo(Expression* oldExpr, size_t index, LiteralValue* literalVal) : oldExpr(oldExpr), index(index), literalVal(literalVal) { } }; // ==================== Rewrite Non-terminator Expressions ==================== // /** * This function will generate a literal value based on the range information * from @p constVal. The type of the literal value is @p type. */ Ptr GenerateConstExpr(const Ptr& type, const Ptr& rangeVal); /** * This function will rewrite an expression based on the @p rewriteInfo, which stores * the exrpession to be rewrited, the index of this expression and the new expression. */ void RewriteToConstExpr(const RewriteInfo& rewriteInfo, bool isDebug) const; // ==================== Rewrite Terminator Expressions ==================== // /** * This function will rewrite a Branch terminator or a MultiBranch terminator to a GoTo * terminator. The new successor will be @p targetSucc. */ void RewriteBranchTerminator(const Ptr& branch, const Ptr& targetSucc, bool isDebug); void RecordEffectMap(const Expression* expr, const Func* func) const; void CheckVarrayIndex(const Ptr& intrin, const RangeDomain& state) const; CHIRBuilder& builder; RangeAnalysisWrapper* analysisWrapper; DiagAdapter* diag; bool enIncre; static OptEffectCHIRMap effectMap; std::vector funcsNeedRemoveBlocks; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/RedundantFutureRemoval.h000066400000000000000000000022601510705540100304030ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_REDUNDANT_FUTURE_REMOVAL_H #define CANGJIE_CHIR_TRANSFORMATION_REDUNDANT_FUTURE_REMOVAL_H #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { /** * CHIR Opt Pass: replace future object with closure call in spawn expression. */ class RedundantFutureRemoval { public: RedundantFutureRemoval(const Package& pkg, bool isDebug); /** * @brief Main process to do future remove in spawn expression. */ void RunOnPackage(); private: void RunOnFunc(const Func& func); FuncBase* GetExecureClosureFunc() const; std::pair CheckSpawnWithFuture(Expression& expr) const; void RewriteSpawnWithOutFuture(Spawn& spawnExpr, LocalVar& futureValue, Apply& apply); const Package& package; bool isDebug{false}; FuncBase* executeClosure{nullptr}; }; } #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/RedundantGetOrThrowElimination.h000066400000000000000000000021361510705540100320420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_REDUNDANT_GETORTHROW_ELIMINATION_H #define CANGJIE_CHIR_TRANSFORMATION_REDUNDANT_GETORTHROW_ELIMINATION_H #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { /** * CHIR Opt Pass: replace GetOrThrow function with value directly. */ class RedundantGetOrThrowElimination { public: /** * @brief constructor for GetOrThrow function elimination. */ explicit RedundantGetOrThrowElimination(); /** * @brief Main process to do GetOrThrow elimination. * @param package package to do optimization. * @param isDebug flag whether print debug log. */ void RunOnPackage(const Ptr& package, bool isDebug) const; private: void RunOnFunc(const Ptr& func, bool isDebug) const; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/RedundantLoadElimination.h000066400000000000000000000021231510705540100306510ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_REDUNDANT_LOAD_ELIMINATION_H #define CANGJIE_CHIR_TRANSFORMATION_REDUNDANT_LOAD_ELIMINATION_H #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { /** * CHIR Opt Pass: replace redundant load expr with exact value itself. */ class RedundantLoadElimination { public: /** * @brief constructor for redundant load expression elimination. */ explicit RedundantLoadElimination(); /** * @brief Main process to do redundant load elimination. * @param package package to do optimization. * @param isDebug flag whether print debug log. */ void RunOnPackage(const Ptr& package, bool isDebug) const; private: void RunOnFunc(const Ptr& func, bool isDebug) const; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/SanitizerCoverage.h000066400000000000000000000117331510705540100273670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_SANITIZER_COVERAGE_H #define CANGJIE_CHIR_TRANSFORMATION_SANITIZER_COVERAGE_H #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/DiagAdapter.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Value.h" #include "cangjie/Option/Option.h" namespace Cangjie::CHIR { /** * CHIR Normal Pass: add sanitizer coverage code to certain code, such as compare, switch, etc. */ class SanitizerCoverage { public: /** * @brief constructor to insert sanitizer coverage code. * @param option global options of cangjie inputs. * @param builder CHIR builder for generating IR. */ explicit SanitizerCoverage(const GlobalOptions& option, CHIRBuilder& builder); /** * @brief Main process to insert sanitizer coverage code. * @param package package to do optimization. * @param diag reporter to print error or warning info. * @param isDebug flag whether print debug log. * @return flag whether error happens in package. */ bool RunOnPackage(const Ptr& package, DiagAdapter& diag, bool isDebug); private: void RunOnFunc(const Ptr& func, bool isDebug); bool CheckSancovOption(DiagAdapter& diag) const; // entry for different sanitizer coverage option void InjectTraceForCmp(BinaryExpression& binary, bool isDebug); void InjectTraceForSwitch(MultiBranch& mb, bool isDebug); void InsertCoverageAheadBlock(Block& block, bool isDebug); void InjectTraceMemCmp(Expression& expr, bool isDebug); // for switch trace RawArrayAllocate* CreateArrayForSwitchCaseList(MultiBranch& multiBranch); Intrinsic* CreateRawDataAcquire(const Expression& dataList, Type& elementType) const; // for memory compare IR generator std::vector GenerateCStringMemCmp(const std::string& fuzzName, Value& oper1, Value& oper2, Apply& apply); std::vector GenerateStringMemCmp(const std::string& fuzzName, Value& oper1, Value& oper2, Apply& apply); std::vector GenerateArrayCmp(const std::string& fuzzName, Value& oper1, Value& oper2, Apply& apply); std::pair CastArrayListToArray(Value& oper1, Value& oper2, Apply& apply); Expression* CreateOneCPointFromList(Value& array, Apply& apply, Type& elementType, Type& startType); std::pair> GetMemFuncSymbols(Value& oper1, Value& oper2, Apply& apply); Expression* CreateMemCmpFunc(const std::string& intrinsicName, Type& paramsType, const std::vector& params, const DebugLocation& loc, Block* parent); // create coverage call for different option std::vector GenerateCoverageCallByOption(const DebugLocation& loc, bool isDebug, Block* parent); std::vector GeneratePCGuardExpr(const DebugLocation& loc, bool isDebug, Block* parent); std::vector GenerateInline8bitExpr(const DebugLocation& loc, bool isDebug, Block* parent); std::vector GenerateInlineBoolExpr(const DebugLocation& loc, bool isDebug, Block* parent); std::vector GenerateStackDepthExpr(const DebugLocation& loc, bool isDebug, Block* parent); // create imported function or global var GlobalVar* GenerateGlobalVar(const std::string& globalVarName, const DebugLocation& loc, Type& globalType); GlobalVar* GetGlobalVar(const std::string& globalVarName); ImportedValue* GenerateForeignFunc( const std::string& globalFuncName, const DebugLocation& loc, Type& funcType, const std::string& packName = ""); ImportedValue* GetImportedFunc(const std::string& mangledName); Func* CreateInitFunc(const std::string& name, FuncType& funcType, const DebugLocation& loc); // create init func void GenerateInitFunc(const Func& globalInitFunc, bool isDebug); void CreateTopLevelInitFunc(const std::vector& initFuncs, const Func& globalInitFunc); Func* CreateArrayInitFunc(const std::string& initItemName, Type& initType); Func* CreatePCTableInitFunc(); Intrinsic* CreateRawDataAcquire(Type& type, const std::vector& list, Value& size, Block& block); void InitFuncBag(const Package& package); // option imported from system const GlobalOptions::SanitizerCoverageOptions& sanCovOption; // a list for pc table array std::vector> pcArray; // function Bag imported from package for fuzz std::unordered_map funcBag; // global var Bag imported needed by fuzz std::unordered_map globalVarBag; // bb counter overall package int64_t bbCounter{0}; std::string packageName; CHIRBuilder& builder; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/StdNoSideEffectwhiteList.inc000066400000000000000000000011251510705540100311250ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file lists no side effect std function for further optimize. * */ "bg5roundFd$$d", "at6StringS2==F6String$$b", "at7Extend!l<:8HashableX8hashCodeF$$l", "at7Extend!d<:8HashableX8hashCodeF$$l", "at6StringS7compareF6String$$8Ordering", "at7Extend!8Ordering<:10Comparable<8Ordering>X7compareF8Ordering$$8Ordering" cangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/UnitUnify.h000066400000000000000000000033531510705540100256740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_UNIT_UNIFY_H #define CANGJIE_CHIR_TRANSFORMATION_UNIT_UNIFY_H #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { /** * @brief unify all used units to one in a function. * eliminate side effects for unit variables * * before pass: * %0: Unit = Apply(@_CN7default3fooEv) * %1: Void = Apply(@_CN7default3foo2Ev, %0) * %2: Void = Apply(@_CN7default3foo3Ev, %0) * after pass: * %3: Unit = Constant(unit) * %0: Unit = Apply(@_CN7default3fooEv) * %1: Void = Apply(@_CN7default3foo2Ev, %3) // change used unit to const value %3 * %2: Void = Apply(@_CN7default3foo3Ev, %3) // change used unit to const value %3 */ class UnitUnify { public: /** * @brief constructor to unify all used units to one in a function. * @param builder CHIR builder for generating IR. */ explicit UnitUnify(CHIRBuilder& builder); /** * @brief Main process to unify all used units to one in a function. * @param package package to do optimization. * @param isDebug flag whether print debug log. */ void RunOnPackage(const Ptr& package, bool isDebug); private: void RunOnFunc(const Ptr& func, bool isDebug); void LoadOrCreateUnit(Ptr& constant, const Ptr& group); CHIRBuilder& builder; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Transformation/UselessAllocateElimination.h000066400000000000000000000016741510705540100312270ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_TRANSFORMATION_USELESS_ALLOCATE_ELIMINATION_H #define CANGJIE_CHIR_TRANSFORMATION_USELESS_ALLOCATE_ELIMINATION_H #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { /** * CHIR Opt Pass: delete all useless allocation in CHIR IR. */ class UselessAllocateElimination { public: /** * @brief Main process to do useless allocate elimination. * @param package package to do optimization. * @param isDebug flag whether print debug log. */ static void RunOnPackage(const Package& package, bool isDebug); private: static void RunOnFunc(const Func& func, bool isDebug); }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Type/000077500000000000000000000000001510705540100215005ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/CHIR/Type/CHIRType.h000066400000000000000000000070161510705540100232440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_CHIRTYPE_H #define CANGJIE_CHIR_CHIRTYPE_H #include "cangjie/AST/Types.h" #include "cangjie/CHIR/AST2CHIR/AST2CHIRNodeMap.h" #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/Type/Type.h" #include namespace Cangjie::CHIR { class CHIRTypeCache { public: std::unordered_map& typeMap; // AST::Type -> CHIR::Type // cache custom type(interface decl, class decl, struct decl, enum decl), except for extend decl AST2CHIRNodeMap globalNominalCache; explicit CHIRTypeCache(std::unordered_map& typeMap) : typeMap(typeMap) { } explicit CHIRTypeCache( std::unordered_map& typeMap, const AST2CHIRNodeMap& globalNominalCache) : typeMap(typeMap), globalNominalCache(globalNominalCache) { } }; // typeTranslate class CHIRType { public: explicit CHIRType(CHIRBuilder& builder, CHIRTypeCache& typeCache) : builder(builder), chirTypeCache(typeCache) { } ~CHIRType() = default; /** * @brief Translates an AST type to a CHIR type. * * @param ty The AST type to be translated. * @return The translated CHIR type. */ Type* TranslateType(AST::Ty& ty); /** * @brief Fills the generic argument types. * * @param ty The AST generics type to be processed. */ void FillGenericArgType(AST::GenericsTy& ty); /* Notice that chirTypeCache.globalNominalCache is non-thread-safe, so SetGlobalNominalCache only be invoked * serially. Concurrent execution of SetGlobalNominalCache is not advisable. */ void SetGlobalNominalCache(const AST::Decl& decl, CustomTypeDef& def) { chirTypeCache.globalNominalCache.Set(decl, def); } Ptr GetGlobalNominalCache(const AST::Decl& decl) const { return chirTypeCache.globalNominalCache.Get(decl); } Ptr TryGetGlobalNominalCache(const AST::Decl& decl) const { return chirTypeCache.globalNominalCache.TryGet(decl); } bool Has(const AST::Decl& decl) const { return chirTypeCache.globalNominalCache.Has(decl); } const std::unordered_map& GetAllTypeDef() const { return chirTypeCache.globalNominalCache.GetALL(); } std::unordered_map& GetTypeMap() const { return chirTypeCache.typeMap; } const AST2CHIRNodeMap& GetGlobalNominalCache() const { return chirTypeCache.globalNominalCache; } private: Type* TranslateTupleType(AST::TupleTy& tupleTy); Type* TranslateFuncType(const AST::FuncTy& fnTy); Type* TranslateStructType(AST::StructTy& structTy); Type* TranslateClassType(AST::ClassTy& classTy); Type* TranslateInterfaceType(AST::InterfaceTy& interfaceTy); Type* TranslateEnumType(AST::EnumTy& enumTy); Type* TranslateArrayType(AST::ArrayTy& arrayTy); Type* TranslateVArrayType(AST::VArrayTy& varrayTy); Type* TranslateCPointerType(AST::PointerTy& pointerTy); CHIRBuilder& builder; CHIRTypeCache& chirTypeCache; // mutex for translateType. TranslateType is recursive, so we use the recursive mutex. static std::recursive_mutex chirTypeMtx; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Type/ClassDef.h000066400000000000000000000054171510705540100233440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_CLASS_H #define CANGJIE_CHIR_CLASS_H #include "cangjie/CHIR/Type/CustomTypeDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" #include #include #include #include namespace Cangjie::CHIR { class ClassDef : public CustomTypeDef { friend class CustomDefTypeConverter; friend class CHIRDeserializer; friend class CHIRSerializer; public: // ===--------------------------------------------------------------------===// // Base Infomation // ===--------------------------------------------------------------------===// ClassType* GetType() const override; void SetType(CustomType& ty) override; bool IsAbstract() const; bool IsClass() const; bool IsInterface() const; std::string ToString() const override; /** * @brief Whether this class is user defined annotation. * * @return return true for classes that are marked with the @Annotation annotation. */ bool IsAnnotation() const; void SetAnnotation(bool value); // ===--------------------------------------------------------------------===// // Super Parent // ===--------------------------------------------------------------------===// ClassType* GetSuperClassTy() const; ClassDef* GetSuperClassDef() const; bool HasSuperClass() const; void SetSuperClassTy(ClassType& ty); // ===--------------------------------------------------------------------===// // Member Function // ===--------------------------------------------------------------------===// void AddAbstractMethod(AbstractMethodInfo methodInfo); std::vector GetAbstractMethods() const; void SetAbstractMethods(const std::vector& methods); FuncBase* GetFinalizer() const; protected: void PrintComment(std::stringstream& ss) const override; private: explicit ClassDef(std::string srcCodeIdentifier, std::string identifier, std::string pkgName, bool isClass); ~ClassDef() override = default; friend class CHIRContext; friend class CHIRBuilder; void PrintAbstractMethod(std::stringstream& ss) const; bool isClass = false; // class or interface bool isAnnotation = false; // whether the class is modified by @Annotation ClassType* superClassTy = nullptr; std::vector abstractMethods; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Type/CustomTypeDef.h000066400000000000000000000270241510705540100244110ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CUSTOMTYPE_H #define CANGJIE_CUSTOMTYPE_H #include "cangjie/CHIR/Value.h" #include #include #include #include #include #include #include namespace Cangjie::CHIR { class ClassDef; class ExtendDef; struct MemberVarInfo { std::string name; std::string rawMangledName; Type* type = nullptr; AttributeInfo attributeInfo; DebugLocation loc; AnnoInfo annoInfo; FuncBase* initializerFunc = nullptr; /**< Func with initializer evaluation if any */ const CustomTypeDef* outerDef = nullptr; bool TestAttr(Attribute attr) const { return attributeInfo.TestAttr(attr); } bool IsImmutable() const { return attributeInfo.TestAttr((Attribute::READONLY)) || attributeInfo.TestAttr(Attribute::CONST); } }; enum CustomDefKind : uint8_t { TYPE_STRUCT, TYPE_ENUM, TYPE_CLASS, // include class, interface TYPE_EXTEND }; class CustomTypeDef : public Base { friend class GlobalVarBase; friend class FuncBase; friend class CustomType; friend class CustomDefTypeConverter; friend class CHIRDeserializer; public: // ===--------------------------------------------------------------------===// // Base Infomation // ===--------------------------------------------------------------------===// CustomDefKind GetCustomKind() const; std::string GetIdentifier() const; std::string GetIdentifierWithoutPrefix() const; std::string GetSrcCodeIdentifier() const; std::string GetPackageName() const; virtual Type* GetType() const; virtual void SetType(CustomType& ty) = 0; virtual std::vector GetGenericTypeParams() const; bool IsGenericDef() const; void Dump() const; virtual std::string ToString() const; bool IsClass() const; /** @brief is class or interface */ bool IsClassLike() const; bool IsEnum() const; bool IsExtend() const; bool IsInterface() const; bool IsStruct() const; // ===--------------------------------------------------------------------===// // Attribute // ===--------------------------------------------------------------------===// AttributeInfo GetAttributeInfo() const; void AppendAttributeInfo(const AttributeInfo& info); void EnableAttr(Attribute attr); bool TestAttr(Attribute attr) const; void DisableAttr(Attribute attr); // ===--------------------------------------------------------------------===// // Super Parent // ===--------------------------------------------------------------------===// void AddImplementedInterfaceTy(ClassType& interfaceTy); std::vector GetImplementedInterfaceDefs() const; std::vector GetImplementedInterfaceTys() const; size_t GetImplementedInterfacesNum() const; /** * @brief super types in current def, including class and interface, excluding super types' super types * e.g. class A <: C & I1 {}; class C <: I2 {} * then super types in class A are C and I1, not including I2 * * @return super types, but we don't guarantee the order */ std::vector GetSuperTypesInCurDef() const; /** @brief visit super def and super def's all extend defs which meet the condition, * and get their super classes or interfaces recursively, not including current def's extend defs * * interface I1 {} * interface I2 <: I1 {} * class C1 <: I2 * * interface I3 {} * interface I4 <: I3 {} * extend C1 <: I4 * * interface I5 {} * class C2 <: C1 & I5 * * interface I6 {} * extend C2 <: I6 {} * * so the result of `C2.GetSuperTypesRecusively()` is {I1 ~ I5, C1}, not including I6 * * @return super types, but we don't guarantee the order */ std::vector GetSuperTypesRecusively(CHIRBuilder& builder) const; // ===--------------------------------------------------------------------===// // Member Function // ===--------------------------------------------------------------------===// void AddMethod(class FuncBase* method); std::vector GetMethods() const; // you need to update vtable by yourself, after setting methods void SetMethods(const std::vector& items); /** * @brief Retrieves the expected function based on the given name and type. * * @param funcName The name of the function to be retrieved. * @param funcType The type of the function to be retrieved. * @param isStatic Indicates whether the function is static. * @param replaceTable A map for replacing generic types with specific types. * @param funcInstTypeArgs A vector to store the function instance type arguments. * @param builder The CHIR builder used for building the function. * @param checkAbstractMethod Indicates whether to check for an abstract method. * @return A pair containing the expected function and a boolean flag. */ std::pair GetExpectedFunc(const std::string& funcName, FuncType& funcType, bool isStatic, std::unordered_map replaceTable, std::vector& funcInstTypeArgs, CHIRBuilder& builder, bool checkAbstractMethod) const; // ===--------------------------------------------------------------------===// // Member Var // ===--------------------------------------------------------------------===// void AddStaticMemberVar(class GlobalVarBase* variable); std::vector GetStaticMemberVars() const; void SetStaticMemberVars(const std::vector& vars); /** * @brief Add member into non-static members. * * @param variable instance member var */ void AddInstanceVar(MemberVarInfo variable); /** * @brief get instance member var quantity * * @return count from super class's instance member var */ size_t GetAllInstanceVarNum() const; /** * @brief get instance member var * * @param index count from super class's instance member var * @return instance member var */ MemberVarInfo GetInstanceVar(size_t index) const; /** * @brief get all instance member vars * * @return including super class's and current def's instance member vars */ std::vector GetAllInstanceVars() const; /** * @brief get instance member var quantity * * @return only count current def's instance member var, not including super class's */ size_t GetDirectInstanceVarNum() const; MemberVarInfo GetDirectInstanceVar(size_t index) const; std::vector GetDirectInstanceVars() const; void SetDirectInstanceVars(const std::vector& vars); FuncBase* GetVarInitializationFunc() const; void SetVarInitializationFunc(FuncBase* func); // ===--------------------------------------------------------------------===// // Annotation // ===--------------------------------------------------------------------===// AnnoInfo GetAnnoInfo() const; void SetAnnoInfo(const AnnoInfo& info); // ===--------------------------------------------------------------------===// // Vtable // ===--------------------------------------------------------------------===// /** * @brief add virtual function to vtable * * @param srcClassTy a class which function belongs to * @param info virtual function info */ void AddVtableItem(ClassType& srcClassTy, VirtualFuncInfo&& info); const VTableType& GetVTable() const; void SetVTable(const VTableType& table); /** * @brief update virtual function in vtable * * @param srcClassTy a class which function belongs to * @param index virtual function's index * @param newFunc new virtual function pointer * @param newParentTy new virtual function's parent class, `nullptr` means not changed * @param newName new src code name, empty means not changed */ void UpdateVtableItem(ClassType& srcClassTy, size_t index, FuncBase* newFunc, Type* newParentTy = nullptr, const std::string newName = ""); /** * @brief get virtual function's index in vtable * * @param funcCallType function name and type * @param isStatic function is static or not * @param replaceTable an auxiliary map * @param builder CHIR builder */ std::vector GetFuncIndexInVTable( const FuncCallType& funcCallType, bool isStatic, std::unordered_map& replaceTable, CHIRBuilder& builder) const; // ===--------------------------------------------------------------------===// // Extra Information // ===--------------------------------------------------------------------===// const std::vector& GetExtends() const; void AddExtend(ExtendDef& extend); /** * @brief if current def is instantiated decl, we store its generic decl * * @return its generic decl */ CustomTypeDef* GetGenericDecl() const; void SetGenericDecl(CustomTypeDef& decl); bool CanBeInherited() const; protected: explicit CustomTypeDef( std::string srcCodeIdentifier, const std::string& identifier, const std::string& pkgName, CustomDefKind kind) : kind(kind), srcCodeIdentifier(srcCodeIdentifier), identifier(identifier), packageName(pkgName) { } virtual ~CustomTypeDef() = default; virtual void PrintAttrAndTitle(std::stringstream& ss) const; std::string GenericInsArgsToString(const CustomType& ty) const; std::string GenericDefArgsToString() const; void PrintParent(std::stringstream& ss) const; virtual void PrintComment(std::stringstream& ss) const; void PrintLocalVar(std::stringstream& ss) const; void PrintStaticVar(std::stringstream& ss) const; void PrintMethod(std::stringstream& ss) const; void PrintVTable(std::stringstream& ss) const; protected: CustomDefKind kind : 8; std::string srcCodeIdentifier; /**< used for srcCodeIdentifier */ std::string identifier; /**< used for identifier */ std::string packageName; /**< package where this type defined by user */ CustomType* type = nullptr; /**< Type */ /** original generic decl which current instantiated decl derives from */ CustomTypeDef* genericDecl = nullptr; std::vector methods; /**< non-abstract member methods */ std::vector implementedInterfaceTys; /**< implemented interfaces */ std::vector instanceVars; /**< local member variables */ std::vector staticVars; /**< static member variables */ AttributeInfo attributeInfo; /**< attribute */ AnnoInfo annoInfo; /**< struct/class/enum annoInfo */ VTableType vtable; std::vector extends; FuncBase* varInitializationFunc = nullptr; /**< Func for initializing instance variables with initializers */ }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Type/EnumDef.h000066400000000000000000000043041510705540100231750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ENUM_H #define CANGJIE_CHIR_ENUM_H #include "cangjie/CHIR/Type/CustomTypeDef.h" #include "cangjie/CHIR/Type/Type.h" #include #include namespace Cangjie::CHIR { struct EnumCtorInfo { std::string name; std::string mangledName; FuncType* funcType; /**< (AssociatedType_1, ..., AssociatedType_N) -> EnumType */ }; class EnumDef : public CustomTypeDef { friend class CustomDefTypeConverter; public: // ===--------------------------------------------------------------------===// // Base Infomation // ===--------------------------------------------------------------------===// EnumType* GetType() const override; void SetType(CustomType& ty) override; /** * @brief an enum def like: enum XXX { A | B | ... }, is named NOT exhaustive * * @return true if enum is exhaustive */ bool IsExhaustive() const; std::string ToString() const override; void AddCtor(EnumCtorInfo ctor); EnumCtorInfo GetCtor(size_t index) const; std::vector GetCtors() const; void SetCtors(const std::vector& items); /** * @brief check if all constructors is trivial * * @return true if all constructors do not have parameters */ bool IsAllCtorsTrivial() const; protected: void PrintAttrAndTitle(std::stringstream& ss) const override; private: explicit EnumDef(std::string srcCodeIdentifier, std::string identifier, std::string pkgName, bool isNonExhaustive) : CustomTypeDef(srcCodeIdentifier, identifier, pkgName, CustomDefKind::TYPE_ENUM), nonExhaustive{isNonExhaustive} { } ~EnumDef() override = default; void PrintConstructor(std::stringstream& ss) const; friend class CHIRContext; friend class CHIRBuilder; std::vector ctors; bool nonExhaustive; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Type/ExtendDef.h000066400000000000000000000031721510705540100235220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_EXTENDDEF_H #define CANGJIE_EXTENDDEF_H #include "cangjie/CHIR/Type/CustomTypeDef.h" #include "cangjie/CHIR/Type/Type.h" namespace Cangjie::CHIR { class ExtendDef : public CustomTypeDef { friend class CHIRContext; friend class CHIRBuilder; friend class CustomDefTypeConverter; public: // ===--------------------------------------------------------------------===// // Base Infomation // ===--------------------------------------------------------------------===// Type* GetType() const override; void SetType(CustomType& ty) override; Type* GetExtendedType() const; void SetExtendedType(Type& ty); CustomTypeDef* GetExtendedCustomTypeDef() const; virtual std::vector GetGenericTypeParams() const override; // ===--------------------------------------------------------------------===// // Super Parent // ===--------------------------------------------------------------------===// void RemoveParent(ClassType& parent); protected: void PrintAttrAndTitle(std::stringstream& ss) const override; void PrintComment(std::stringstream& ss) const override; private: explicit ExtendDef( const std::string& identifier, const std::string& pkgName, std::vector genericParams = {}); Type* extendedType{nullptr}; std::vector genericParams; }; } #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Type/PrivateTypeConverter.h000066400000000000000000000306111510705540100260160ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_PRIVATE_TYPE_CONVERTER_H #define CANGJIE_CHIR_PRIVATE_TYPE_CONVERTER_H #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/CustomTypeDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/ExtendDef.h" #include "cangjie/CHIR/Type/StructDef.h" namespace Cangjie::CHIR { template class ExprTypeFunctor; template class ValueTypeFunctor; template class CustomDefTypeFunctor; #define VISIT_IMPL_DEFAULT(OP_TYPE) \ { \ return Visit##OP_TYPE##DefaultImpl(o, std::forward(args)...); \ } #define VISIT_IMPL_DISPATCH_EXPR(KIND, OP) \ { \ KIND, \ [](TSelf* self, Expression& o, Args...args) { \ return self->VisitSubExpression(*StaticCast(&o), std::forward(args)...); \ } \ } #define VISIT_IMPL_DISPATCH_VALUE(KIND, OP) \ { \ KIND, \ [](TSelf* self, Value& o, Args...args) { \ return self->VisitSubValue(*VirtualCast(&o), std::forward(args)...); \ } \ } #define VISIT_IMPL_DISPATCH_DEF(KIND, OP) \ { \ KIND, \ [](TSelf* self, CustomTypeDef& o, Args...args) { \ return self->VisitSubDef(*StaticCast(&o), std::forward(args)...); \ } \ } template class CustomDefTypeFunctor { public: using TSelf = CustomDefTypeFunctor; using FType = std::function; using Dispatcher = std::unordered_map; virtual ~CustomDefTypeFunctor() { } virtual R VisitDef(CustomTypeDef& o, Args... args) { static Dispatcher dispatcher = InitCustomTypeDefTable(); auto func = dispatcher.find(o.GetCustomKind()); CJC_ASSERT(func != dispatcher.end()); return func->second(this, o, std::forward...); } protected: virtual R VisitDefDefaultImpl([[maybe_unused]] CustomTypeDef& o, [[maybe_unused]] Args... args) = 0; virtual R VisitSubDef(StructDef& o, Args... args) = 0; virtual R VisitSubDef(EnumDef& o, Args... args) = 0; virtual R VisitSubDef(ClassDef& o, Args... args) = 0; virtual R VisitSubDef(ExtendDef& o, Args... args) = 0; private: static Dispatcher InitCustomTypeDefTable() { Dispatcher dispatcher = { VISIT_IMPL_DISPATCH_DEF(CustomDefKind::TYPE_STRUCT, StructDef), VISIT_IMPL_DISPATCH_DEF(CustomDefKind::TYPE_ENUM, EnumDef), VISIT_IMPL_DISPATCH_DEF(CustomDefKind::TYPE_CLASS, ClassDef), VISIT_IMPL_DISPATCH_DEF(CustomDefKind::TYPE_EXTEND, ExtendDef), }; return dispatcher; } }; template class ExprTypeFunctor { public: using TSelf = ExprTypeFunctor; using FType = std::function; using Dispatcher = std::unordered_map; virtual ~ExprTypeFunctor() { } virtual R VisitExpr(Expression& o, Args... args) { static Dispatcher dispatcher = InitExprVTable(); auto func = dispatcher.find(o.GetExprKind()); if (func != dispatcher.end()) { return func->second(this, o, std::forward...); } return VisitExprDefaultImpl(o, std::forward...); } protected: virtual R VisitExprDefaultImpl([[maybe_unused]] Expression& o, [[maybe_unused]] Args... args) = 0; virtual R VisitSubExpression(Allocate& o, Args... args) = 0; virtual R VisitSubExpression(AllocateWithException& o, Args... args) = 0; virtual R VisitSubExpression(InstanceOf& o, Args... args) = 0; virtual R VisitSubExpression(RawArrayAllocate& o, Args... args) = 0; virtual R VisitSubExpression(RawArrayAllocateWithException& o, Args... args) = 0; virtual R VisitSubExpression(Apply& o, Args... args) = 0; virtual R VisitSubExpression(ApplyWithException& o, Args... args) = 0; virtual R VisitSubExpression(Invoke& o, Args... args) = 0; virtual R VisitSubExpression(InvokeWithException& o, Args... args) = 0; virtual R VisitSubExpression(InvokeStatic& o, Args... args) = 0; virtual R VisitSubExpression(InvokeStaticWithException& o, Args... args) = 0; virtual R VisitSubExpression(Constant& o, Args... args) = 0; virtual R VisitSubExpression(Intrinsic& o, Args... args) = 0; virtual R VisitSubExpression(IntrinsicWithException& o, Args... args) = 0; virtual R VisitSubExpression(GetInstantiateValue& o, Args... args) = 0; virtual R VisitSubExpression(Lambda& o, Args... args) = 0; virtual R VisitSubExpression(GetRTTIStatic& o, Args... args) = 0; private: static Dispatcher InitExprVTable() { Dispatcher dispatcher = { VISIT_IMPL_DISPATCH_EXPR(ExprKind::ALLOCATE, Allocate), VISIT_IMPL_DISPATCH_EXPR(ExprKind::ALLOCATE_WITH_EXCEPTION, AllocateWithException), VISIT_IMPL_DISPATCH_EXPR(ExprKind::INSTANCEOF, InstanceOf), VISIT_IMPL_DISPATCH_EXPR(ExprKind::RAW_ARRAY_ALLOCATE, RawArrayAllocate), VISIT_IMPL_DISPATCH_EXPR(ExprKind::RAW_ARRAY_ALLOCATE_WITH_EXCEPTION, RawArrayAllocateWithException), VISIT_IMPL_DISPATCH_EXPR(ExprKind::APPLY, Apply), VISIT_IMPL_DISPATCH_EXPR(ExprKind::APPLY_WITH_EXCEPTION, ApplyWithException), VISIT_IMPL_DISPATCH_EXPR(ExprKind::INVOKE, Invoke), VISIT_IMPL_DISPATCH_EXPR(ExprKind::INVOKE_WITH_EXCEPTION, InvokeWithException), VISIT_IMPL_DISPATCH_EXPR(ExprKind::INVOKESTATIC, InvokeStatic), VISIT_IMPL_DISPATCH_EXPR(ExprKind::INVOKESTATIC_WITH_EXCEPTION, InvokeStaticWithException), VISIT_IMPL_DISPATCH_EXPR(ExprKind::CONSTANT, Constant), VISIT_IMPL_DISPATCH_EXPR(ExprKind::INTRINSIC, Intrinsic), VISIT_IMPL_DISPATCH_EXPR(ExprKind::INTRINSIC_WITH_EXCEPTION, IntrinsicWithException), VISIT_IMPL_DISPATCH_EXPR(ExprKind::GET_INSTANTIATE_VALUE, GetInstantiateValue), VISIT_IMPL_DISPATCH_EXPR(ExprKind::LAMBDA, Lambda), VISIT_IMPL_DISPATCH_EXPR(ExprKind::GET_RTTI_STATIC, GetRTTIStatic), }; return dispatcher; } }; template class ValueTypeFunctor { public: using TSelf = ValueTypeFunctor; using FType = std::function; using Dispatcher = std::unordered_map; virtual ~ValueTypeFunctor() { } virtual R VisitValue(Value& o, Args... args) { static Dispatcher dispatcher = InitValueVTable(); if (auto func = dispatcher.find(o.GetValueKind()); func != dispatcher.end()) { return func->second(this, o, std::forward...); } return VisitValueDefaultImpl(o, std::forward...); } protected: virtual R VisitValueDefaultImpl([[maybe_unused]] Value& o, [[maybe_unused]] Args... args) { CJC_ABORT(); } virtual R VisitSubValue(Func& o, Args... args) VISIT_IMPL_DEFAULT(Value); virtual R VisitSubValue(ImportedFunc& o, Args... args) VISIT_IMPL_DEFAULT(Value); private: static Dispatcher InitValueVTable() { Dispatcher dispatcher = { VISIT_IMPL_DISPATCH_VALUE(Value::ValueKind::KIND_FUNC, Func), VISIT_IMPL_DISPATCH_VALUE(Value::ValueKind::KIND_IMP_FUNC, ImportedFunc), }; return dispatcher; } }; #undef VISIT_IMPL_DEFAULT #undef VISIT_IMPL_DISPATCH_EXPR #undef VISIT_IMPL_DISPATCH_VALUE class TypeConverter { public: TypeConverter(const ConvertTypeFunc& converter, CHIRBuilder& builder) : converter(converter), builder(builder) { } virtual ~TypeConverter() = default; protected: virtual Type* ConvertType(Type& type); FuncType* ConvertFuncParamsAndRetType(const FuncType& input); ConvertTypeFunc converter; CHIRBuilder& builder; }; class ExprTypeConverter : public virtual TypeConverter, public ExprTypeFunctor { public: ExprTypeConverter(const ConvertTypeFunc& converter, CHIRBuilder& builder) : TypeConverter(converter, builder) { } virtual void VisitValue(Value&) { } protected: void VisitExprDefaultImpl(Expression& o) override; void VisitSubExpression(Allocate& o) override; void VisitSubExpression(AllocateWithException& o) override; void VisitSubExpression(InstanceOf& o) override; void VisitSubExpression(RawArrayAllocate& o) override; void VisitSubExpression(RawArrayAllocateWithException& o) override; void VisitSubExpression(Apply& o) override; void VisitSubExpression(ApplyWithException& o) override; void VisitSubExpression(Invoke& o) override; void VisitSubExpression(InvokeWithException& o) override; void VisitSubExpression(InvokeStatic& o) override; void VisitSubExpression(InvokeStaticWithException& o) override; void VisitSubExpression(Constant& o) override; void VisitSubExpression(Intrinsic& o) override; void VisitSubExpression(IntrinsicWithException& o) override; void VisitSubExpression(GetInstantiateValue& o) override; void VisitSubExpression(Lambda& o) override; void VisitSubExpression(GetRTTIStatic& o) override; }; class ValueTypeConverter : public virtual TypeConverter, public ValueTypeFunctor { public: ValueTypeConverter(const ConvertTypeFunc& converter, CHIRBuilder& builder) : TypeConverter(converter, builder) { } void VisitSubValue(Func& o) override; void VisitSubValue(ImportedFunc& o) override; void VisitValueDefaultImpl(Value& o) override; }; class CustomDefTypeConverter : public virtual TypeConverter, public CustomDefTypeFunctor { public: CustomDefTypeConverter(const ConvertTypeFunc& converter, CHIRBuilder& builder) : TypeConverter(converter, builder) { } protected: void VisitDefDefaultImpl(CustomTypeDef& o) final; void VisitSubDef(StructDef& o) final; void VisitSubDef(EnumDef& o) final; void VisitSubDef(ClassDef& o) final; void VisitSubDef(ExtendDef& o) final; }; class PrivateTypeConverter : public ExprTypeConverter, public ValueTypeConverter { public: PrivateTypeConverter(const ConvertTypeFunc& converter, CHIRBuilder& builder) : TypeConverter(converter, builder), ExprTypeConverter(converter, builder), ValueTypeConverter(converter, builder) { } using ExprTypeConverter::VisitExpr; using ValueTypeConverter::VisitValue; void VisitValue(Value& o) override { return ValueTypeConverter::VisitValue(o); } }; class PrivateTypeConverterNoInvokeOriginal : public PrivateTypeConverter { public: PrivateTypeConverterNoInvokeOriginal(const ConvertTypeFunc& converter, CHIRBuilder& builder) : TypeConverter(converter, builder), PrivateTypeConverter(converter, builder) { } private: void VisitSubExpression(Invoke& o) override; void VisitSubExpression(InvokeWithException& o) override; void VisitSubExpression(InvokeStatic& o) override; void VisitSubExpression(InvokeStaticWithException& o) override; }; class TypeConverterForCC : public ExprTypeConverter, public ValueTypeConverter, public CustomDefTypeConverter { public: TypeConverterForCC( const ConvertTypeFunc& normalConverter, const ConvertTypeFunc& funcConverter, CHIRBuilder& builder) : TypeConverter(normalConverter, builder), ExprTypeConverter(normalConverter, builder), ValueTypeConverter(normalConverter, builder), CustomDefTypeConverter(normalConverter, builder), funcConverter(funcConverter) { } using ExprTypeConverter::VisitExpr; using ValueTypeConverter::VisitValue; void VisitValue(Value& o) override { return ValueTypeConverter::VisitValue(o); } void VisitSubValue(Func& o) override; void VisitValueDefaultImpl(Value& o) override; protected: void VisitSubExpression(RawArrayAllocate& o) override; void VisitSubExpression(RawArrayAllocateWithException& o) override; Type* ConvertType(Type& type) override; private: ConvertTypeFunc funcConverter; }; } // namespace Cangjie::CHIR #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Type/StructDef.h000066400000000000000000000025451510705540100235620ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_STRUCT_H #define CANGJIE_CHIR_STRUCT_H #include "cangjie/CHIR/Type/CustomTypeDef.h" #include #include namespace Cangjie::CHIR { class StructDef : public CustomTypeDef { public: // ===--------------------------------------------------------------------===// // Base Infomation // ===--------------------------------------------------------------------===// StructType* GetType() const override; void SetType(CustomType& ty) override; /** * @brief return true if this struct annotated with @C */ bool IsCStruct() const; void SetCStruct(bool value); protected: void PrintComment(std::stringstream& ss) const override; private: explicit StructDef(std::string srcCodeIdentifier, std::string identifier, std::string pkgName) : CustomTypeDef(srcCodeIdentifier, identifier, pkgName, CustomDefKind::TYPE_STRUCT) {} ~StructDef() override = default; friend class CHIRContext; friend class CHIRBuilder; bool isC = false; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Type/Type.h000066400000000000000000001000241510705540100225670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Type class in CHIR. */ #ifndef CANGJIE_CHIR_TYPE_H #define CANGJIE_CHIR_TYPE_H #include "cangjie/CHIR/AttributeInfo.h" #include "cangjie/CHIR/UserDefinedType.h" #include "cangjie/Utils/CheckUtils.h" #include #include #include #include #include #include #include #include #include namespace Cangjie::CHIR { class CHIRContext; class CustomTypeDef; class ClassDef; class EnumDef; class ExtendDef; struct EnumCtorInfo; class StructDef; class FuncBase; class ClassType; class CHIRBuilder; class GenericType; struct AbstractMethodInfo; struct AbstractMethodParam; /** HashCombine is a function used to create hash with fewer collisions. */ template inline void HashCombine(size_t& hashVal, const T& val) { hashVal ^= std::hash()(val) + 0x9e3779b9 + (hashVal << 6) + (hashVal >> 2); // 6, 2 are specific constant in the hash algorithm. } // The base case. template inline void HashValue(std::size_t& hashVal, const T& val) { HashCombine(hashVal, val); } // Iterate for any args. template inline void HashValue(std::size_t& hashVal, const T& val, const Types&... others) { HashCombine(hashVal, val); HashValue(hashVal, others...); } template inline size_t HashValue(const Types&... values) { std::size_t hashVal = 0; HashValue(hashVal, values...); return hashVal; } /* * @brief Definitions of all of the base types for the Type system. * * The instances of the Type class are immutable: once they are created,they are never changed. * */ class Type { public: enum TypeKind : uint8_t { TYPE_INVALID = 0, // integer TYPE_INT8, TYPE_INT16, TYPE_INT32, TYPE_INT64, TYPE_INT_NATIVE, // unsigned integer TYPE_UINT8, TYPE_UINT16, TYPE_UINT32, TYPE_UINT64, TYPE_UINT_NATIVE, // float TYPE_FLOAT16, TYPE_FLOAT32, TYPE_FLOAT64, // other primitive type TYPE_RUNE, TYPE_BOOLEAN, TYPE_UNIT, TYPE_NOTHING, // Void type TYPE_VOID, // composite type TYPE_TUPLE, TYPE_STRUCT, TYPE_ENUM, TYPE_FUNC, TYPE_CLASS, // Built-in array related type TYPE_RAWARRAY, TYPE_VARRAY, // Built-in CFFI related type TYPE_CPOINTER, TYPE_CSTRING, // Generic type TYPE_GENERIC, // Referece to an value with abritray type TYPE_REFTYPE, // Built-in box type TYPE_BOXTYPE, TYPE_THIS, MAX_TYPE_KIND, }; protected: explicit Type(TypeKind kind); public: virtual ~Type() = default; virtual std::string ToString() const; void Dump() const; /** @brief Return the type kind. */ inline TypeKind GetTypeKind() const { return kind; } bool SatisfyCType() const; bool IsSameTypeKind(const Type& type) const { return this->kind == type.kind; } /** @brief Check whether the type is Interger. */ bool IsInteger() const { return kind >= TYPE_INT8 && kind <= TYPE_UINT_NATIVE; } bool IsNumeric() const { return kind >= TYPE_INT8 && kind <= TYPE_FLOAT64; } bool IsUnsignedInteger() const { return kind >= TYPE_UINT8 && kind <= TYPE_UINT_NATIVE; } bool IsInvalid() const { return kind == TYPE_INVALID; } /** @brief Check whether the type is Float. */ bool IsFloat() const { return kind >= TYPE_FLOAT16 && kind <= TYPE_FLOAT64; } bool IsRune() const { return kind == TYPE_RUNE; } bool IsBoolean() const { return kind == TYPE_BOOLEAN; } bool IsUnit() const { return kind == TYPE_UNIT; } bool IsNothing() const { return kind == TYPE_NOTHING; } bool IsPrimitive() const { return kind >= TYPE_INT8 && kind <= TYPE_NOTHING; } bool IsTuple() const { return kind == TYPE_TUPLE; } /** * @brief if this is a class type for representing the context of a closure, return true. */ bool IsAutoEnv() const; /** * @brief if this is a abstract class type for representing the context of a closure, return true. */ bool IsAutoEnvBase() const; bool IsAutoEnvInstBase() const; bool IsAutoEnvGenericBase() const; bool IsFunc() const { return kind == TYPE_FUNC; } bool IsStruct() const { return kind == TYPE_STRUCT; } bool IsClass() const { return kind == TYPE_CLASS; } bool IsRawArray() const { return kind == TYPE_RAWARRAY; } bool IsVArray() const { return kind == TYPE_VARRAY; } bool IsEnum() const { return kind == TYPE_ENUM; } bool IsCPointer() const { return kind == TYPE_CPOINTER; } bool IsCString() const { return kind == TYPE_CSTRING; } bool IsString() const; bool IsClassRef(bool nullable = false) const; bool IsGeneric() const { return kind == TYPE_GENERIC; } bool IsVoid() const { return kind == TYPE_VOID; } bool IsRef() const { return kind == TYPE_REFTYPE; } bool IsNominal() const { return kind == TYPE_CLASS || kind == TYPE_STRUCT || kind == TYPE_ENUM; } bool IsClassOrArray() const { return kind == TYPE_CLASS || kind == TYPE_RAWARRAY; } bool IsBox() const { return kind == TYPE_BOXTYPE; } bool IsThis() const { return kind == TYPE_THIS; } bool IsConstant() const; bool IsAny() const; bool IsCJFunc() const; bool IsCFunc() const; bool IsCType() const; bool IsStructArray() const; bool IsCustomType() const { return kind == TYPE_CLASS || kind == TYPE_STRUCT || kind == TYPE_ENUM; } bool IsClassOrStruct() const { return kind == TYPE_CLASS || kind == TYPE_STRUCT; } bool IsBuiltinType() const; bool IsValueType() const { return IsPrimitive() || IsEnum() || IsTuple() || IsStruct() || IsVArray() || IsCPointer() || IsCString() || IsFunc(); } bool IsValueOrGenericType() const { return IsValueType() || IsGeneric(); } bool IsReferenceType() const { return IsClassOrArray() || IsBox() || IsThis(); } Type* StripAllRefs() const; bool IsReferenceTypeWithRefDims(size_t dims) const; bool IsValueOrGenericTypeWithRefDims(size_t dims) const; bool IsGenericRelated() const { if (IsGeneric()) { return true; } for (auto arg : GetTypeArgs()) { if (arg->IsGenericRelated()) { return true; } } return false; } /** @brief Get hash value. */ virtual size_t Hash() const; /** @brief Check whether two Type instances are equal. */ virtual bool operator==(const Type& other) const; /** @brief Get the nested levels of nested reference type. */ uint8_t GetRefDims() const { return refDims; } std::vector GetTypeArgs() const { return argTys; } /** * @brief if `this` is generic related, and we need to know which type every generic type is instantiated to * @param targetTy: an instantiated type whose type structure is same with `this` * @return if `this` is not generic related, an empty map will return * if type structure between `this` and `targetTy` is different, `false` will return */ std::pair> CalculateGenericTyMapping( const Type& targetTy) const; /** @brief visit type, its type args, its type args' type args recursively * @param visitor: a lambda, use current type or type args as param * if lambda returns false, then type args won't be visited * if lambda returns true, then visit all type args */ void VisitTypeRecursively(const std::function& visitor) const; bool IsEqualOrSubTypeOf(const Type& parentType, CHIRBuilder& builder, std::set>* visited = nullptr) const; bool IsEqualOrInstantiatedTypeOf(const Type& genericRelatedType, CHIRBuilder& builder, std::set>* visited = nullptr) const; virtual std::vector GetDeclareAndExtendMethods(CHIRBuilder& builder) const; virtual const std::vector& GetExtends(CHIRBuilder* builder = nullptr) const; virtual std::vector GetSuperTypesRecusively(CHIRBuilder& builder, std::set>* visited = nullptr); /** * @brief Checks if current type satisfies generic constraints. * * @param type The genric type with constraints. * @param builder The CHIR builder used for building the type. * @param instMap Replace generic type to concrete type * @return True if the generic constraints are satisfied, false otherwise. */ bool SatisfyGenericConstraints(const GenericType& type, CHIRBuilder& builder, const std::unordered_map& instMap, std::set>* visited = nullptr) const; protected: /** * funcType: argTys = { paramTypes, returnType } * refType: argTys = { baseType } * tupleType: argTys = { fieldTypes } * rawArray: argTys = { elementType } * struct/class/enum: argTys = { generic instantiated arg types} * closureType: argTys = {funcType, envType } * others argTys is empty */ std::vector argTys; TypeKind kind : 8; // The current base type of this type. uint8_t refDims{0}; // The nested reference levels, default 0 indicate non-reference type. }; class BuiltinType : public Type { friend class CHIRSerializer; public: explicit BuiltinType(TypeKind kind) : Type{kind} {} ~BuiltinType() override = default; /** @brief visit all extend defs which meet the condition, and get their super interfaces recursively * * interface I1 {} * interface I2 <: I1 {} * extend CPointer <: I2 {} // meet the condition * * interface I3 {} * interface I4 <: I3 {} * extend CPointer <: I4 {} // NOT meet the condition * * interface I5 {} * extend Bool <: I5 {} * interface I6 {} * extend CPointer <: I6 where T <: I5 {} // meet the condition * * interface I7 {} * extend CPointer <: I7 where T <: I1 {} // NOT meet the condition * * so the result of `CPointer.GetSuperTypesRecusively()` is {I1, I2, I6} */ std::vector GetSuperTypesRecusively(CHIRBuilder& builder, std::set>* visited = nullptr) override; const std::vector& GetExtends(CHIRBuilder* builder = nullptr) const override; void AddExtend(ExtendDef& extend); std::vector GetExtendMethods() const; std::vector GetDeclareAndExtendMethods(CHIRBuilder& builder) const override; protected: std::vector extends; }; class NumericType : public BuiltinType { const std::map INTEGER_TO_BIT_LEN{ {TypeKind::TYPE_INT8, 8}, {TypeKind::TYPE_INT16, 16}, {TypeKind::TYPE_INT32, 32}, {TypeKind::TYPE_INT64, 64}, {TypeKind::TYPE_UINT8, 8}, {TypeKind::TYPE_UINT16, 16}, {TypeKind::TYPE_UINT32, 32}, {TypeKind::TYPE_UINT64, 64}, #if (defined(__x86_64__) || defined(__aarch64__)) {TypeKind::TYPE_UINT_NATIVE, 64}, {TypeKind::TYPE_INT_NATIVE, 64}, #else {TypeKind::TYPE_UINT_NATIVE, 32}, {TypeKind::TYPE_INT_NATIVE, 32}, #endif {TypeKind::TYPE_FLOAT16, 16}, {TypeKind::TYPE_FLOAT32, 32}, {TypeKind::TYPE_FLOAT64, 64}, }; protected: explicit NumericType(TypeKind kind) : BuiltinType(kind) { } ~NumericType() override = default; public: /** * @brief Get bit length of current numeric type. */ uint64_t GetBitness() const { return INTEGER_TO_BIT_LEN.at(kind); } }; class FloatType : public NumericType { private: explicit FloatType(TypeKind kind); ~FloatType() override = default; friend class CHIRContext; }; class IntType : public NumericType { public: bool IsSigned() const { return kind >= TYPE_INT8 && kind <= TYPE_INT_NATIVE; } bool IsIntNative() const { return kind == TYPE_INT_NATIVE; } bool IsUIntNative() const { return kind == TYPE_UINT_NATIVE; } private: explicit IntType(TypeKind kind); ~IntType() override = default; friend class CHIRContext; }; class FuncType : public Type { public: Type* GetReturnType() const { return argTys.back(); } std::vector GetParamTypes() const { return std::vector(argTys.begin(), argTys.end() - 1); } size_t GetNumOfParams() const { CJC_ASSERT(!argTys.empty()); return argTys.size() - 1; } Type* GetParamType(size_t index) const { CJC_ASSERT(index + 1 < argTys.size()); return argTys[index]; } /** * @brief Mark whether this FuncType has `VarLenParam` * * Note: Only foreign functions can have variable-length arguments, * which means the func must be a CFunc. * * the Cangjie self's (without foreign) `VarLenParam` is just a desugar, which is desugared to * Struct-Array and not reflected on CHIR. * */ inline bool HasVarArg() const { return hasVarArg; } std::string ToString() const override; size_t Hash() const override; bool operator==(const Type& other) const override; private: explicit FuncType( const std::vector& paramTys, Type* retTy, bool hasVarLenParam = false, bool isCFunc = false) : Type(TypeKind::TYPE_FUNC), hasVarArg(hasVarLenParam), isCFunc(isCFunc) { this->argTys = paramTys; this->argTys.emplace_back(retTy); } ~FuncType() override = default; friend class CHIRContext; friend class Type; /** * Whether params in this FuncType has `VarLenParam`, the func must be CFunc. */ bool hasVarArg{false}; bool isCFunc{false}; }; class CustomType : public Type { friend class CHIRSerializer; public: CustomTypeDef* GetCustomTypeDef() const; std::vector GetGenericArgs() const; /** @brief visit CustomTypeDef and all extend defs which meet the condition, * and get their super classes or interfaces recursively * * interface I1 {} * interface I2 <: I1 {} * class C1 <: I2 * * interface I3 {} * interface I4 <: I3 {} * extend C1 <: I4 * * interface I5 {} * class C2 <: C1 & I5 * * interface I6 {} * extend C2 <: I6 {} * * so the result of `C2.GetSuperTypesRecusively()` is {I1 ~ I6, C1} */ std::vector GetSuperTypesRecusively(CHIRBuilder& builder, std::set>* visited = nullptr) override; /** * @brief Retrieves the instance map for generic types. * * @param instMap The map to store the instance types. * @param builder The CHIR builder used for building the types. */ void GetInstMap(std::unordered_map& instMap, CHIRBuilder& builder) const; /** * @brief Retrieves the instantiated member type by a given path. * * @param path The path to the member type. * @param builder The CHIR builder used for building the type. * @return The instantiated member type. */ Type* GetInstMemberTypeByPath(const std::vector& path, CHIRBuilder& builder) const { return GetInstMemberTypeByPathCheckingReadOnly(path, builder).first; } /** * @brief Retrieves the instantiated method types. * * @param builder The CHIR builder used for building the types. * @return A vector of instantiated method types. */ std::vector GetInstMethodTypes(CHIRBuilder& builder) const; /** * @brief Retrieves the instantiated member type by a given path, checking for read-only. * * @param path The path to the member type. * @param builder The CHIR builder used for building the type. * @return A pair containing the type and a boolean flag indicating read-only status. */ std::pair GetInstMemberTypeByPathCheckingReadOnly( const std::vector& path, CHIRBuilder& builder) const; /** * @brief Retrieves all instance member types. * * @param builder The CHIR builder used for building the types. * @return A vector of instance member types. */ std::vector GetInstantiatedMemberTys(CHIRBuilder& builder); /** * @brief Retrieves the implemented interface types. * * @param builder The CHIR builder used for building the types. * @return A vector of implemented interface types. */ std::vector GetImplementedInterfaceTys(CHIRBuilder* builder, std::set>* visited = nullptr); /** * @brief Retrieves the implemented interface types without extension. * * @param builder The CHIR builder used for building the types. * @return A vector of implemented interface types without extension. */ std::vector GetImplementedInterfaceTysWithoutExtend(CHIRBuilder& builder); /** * @brief Retrieves the exact parent type for a given function. * * @param funcName The name of the function. * @param funcType The type of the function. * @param isStatic Indicates whether the function is static. * @param funcInstTypeArgs A vector to store the function instance type arguments. * @param builder The CHIR builder used for building the type. * @param checkAbstractMethod Indicates whether to check for an abstract method. * @return The exact parent type. */ Type* GetExactParentType(const std::string& funcName, FuncType& funcType, bool isStatic, std::vector& funcInstTypeArgs, CHIR::CHIRBuilder& builder, bool checkAbstractMethod); /** * @brief Retrieves the expected function for a given name and type. * * @param funcName The name of the function. * @param funcType The type of the function. * @param isStatic Indicates whether the function is static. * @param funcInstTypeArgs A vector to store the function instance type arguments. * @param builder The CHIR builder used for building the function. * @param checkAbstractMethod Indicates whether to check for an abstract method. * @return A pair containing the expected function and a boolean flag. */ std::pair GetExpectedFunc(const std::string& funcName, FuncType& funcType, bool isStatic, std::vector& funcInstTypeArgs, CHIR::CHIRBuilder& builder, bool checkAbstractMethod); /** * @brief Retrieves the index of a function in the virtual table. * * @param funcCallType Function name and type. * @param isStatic Indicates whether the function is static. * @param builder The CHIR builder used for building the function. * @return The virtual table search result. */ std::vector GetFuncIndexInVTable( const FuncCallType& funcCallType, bool isStatic, CHIR::CHIRBuilder& builder) const; /** * @brief Retrieves the declared and extended methods. * * @param builder The CHIR builder used for building the methods. * @return A vector of declared and extended methods. */ std::vector GetDeclareAndExtendMethods(CHIRBuilder& builder) const override; virtual void ResetAllInstantiatedType() { implementedInterfaceTys.clear(); instantiatedMemberTys.clear(); hasSetSuperInterface = false; hasSetInstMemberTy = false; } const std::vector& GetExtends(CHIRBuilder* builder = nullptr) const override; size_t Hash() const override; bool operator==(const Type& other) const override; protected: explicit CustomType(TypeKind kind, CustomTypeDef* def, const std::vector& typeArgs); ~CustomType() override = default; friend class CHIRContext; protected: CustomTypeDef* def{nullptr}; // Custom Type Define. std::vector implementedInterfaceTys; std::vector instantiatedMemberTys; private: std::vector CalculateImplementedInterfaceTys(CHIRBuilder& builder, std::set>* visited = nullptr); std::vector CalculateCurDefInstantiatedMemberTys(CHIRBuilder& builder); std::vector CalculateExtendImplementedInterfaceTys(CHIRBuilder& builder, std::set>* visited = nullptr) const; private: bool hasSetSuperInterface{false}; bool hasSetInstMemberTy{false}; std::recursive_mutex setSuperInterfaceMtx; std::mutex setInstMemberTyMtx; }; class ClassType : public CustomType { public: ClassDef* GetClassDef() const; ClassType* GetSuperClassTy(CHIRBuilder* builder); std::string ToString() const override; void ResetAllInstantiatedType() override { CustomType::ResetAllInstantiatedType(); superClassTy = nullptr; hasSetSuperClass = false; } std::vector GetInstAbstractMethodTypes(CHIRBuilder& builder) const; private: explicit ClassType(ClassDef* classDef, const std::vector& genericArgs = {}); ~ClassType() override = default; friend class CHIRContext; ClassType* CalculateSuperClassTy(CHIRBuilder& builder); private: ClassType* superClassTy{nullptr}; bool hasSetSuperClass{false}; std::mutex setSuperClassMtx; }; class StructType : public CustomType { public: StructDef* GetStructDef() const; std::string ToString() const override; private: explicit StructType(StructDef* structDef, const std::vector& genericArgs = {}); ~StructType() override = default; friend class CHIRContext; }; class EnumType : public CustomType { public: EnumDef* GetEnumDef() const; /** * @brief Is Option enum from std/core */ bool IsOption() const; std::vector GetConstructorInfos(CHIRBuilder& builder) const; std::string ToString() const override; /** * @brief whether this EnumType is boxed, boxed Enum Type is used to break the ring of types. * * e.g. * * struct S { * let x:Option = None * } * * the type of S's member x above is boxed EnumType, otherwise as struct and enum are both value type, * S will contain Option, Option contains S, S contains Option again, * and infinite recursion of inclusion continues. */ bool IsBoxed(CHIRBuilder& builder); private: explicit EnumType(EnumDef* enumDef, const std::vector& genericArgs = {}); ~EnumType() override = default; friend class CHIRContext; bool isBoxed = false; // Annotation: indicate EnumType is boxed. bool CheckIsBoxed(const EnumType& original, Type& curType, CHIRBuilder& builder, bool doCheck, std::unordered_set& visited); }; class TupleType : public Type { public: std::vector GetElementTypes() const { return argTys; } Type* GetElementType(size_t index) const { return argTys[index]; } std::string ToString() const override; private: explicit TupleType(const std::vector& argTys) : Type(TypeKind::TYPE_TUPLE) { this->argTys = argTys; } ~TupleType() override = default; friend class CHIRContext; }; class RefType : public Type { public: std::string ToString() const override; Type* GetBaseType() const { return argTys[0]; } /** * @brief recursively get base type */ Type* GetRootBaseType() const { Type* root = argTys[0]; if (argTys[0]->GetTypeKind() == Type::TypeKind::TYPE_REFTYPE) { root = static_cast(argTys[0])->GetRootBaseType(); } return root; } private: explicit RefType(Type* baseType) : Type(TypeKind::TYPE_REFTYPE) { this->argTys.emplace_back(baseType); this->refDims = static_cast(baseType->GetRefDims() + static_cast(1)); } ~RefType() override = default; friend class CHIRContext; }; class BoxType : public Type { public: std::string ToString() const override; Type* GetBaseType() const { return argTys[0]; } private: explicit BoxType(Type* baseType) : Type(TypeKind::TYPE_BOXTYPE) { this->argTys.emplace_back(baseType); } ~BoxType() override = default; friend class CHIRContext; }; class ThisType : public Type { public: std::string ToString() const override; private: explicit ThisType() : Type(TypeKind::TYPE_THIS) { } ~ThisType() override = default; friend class CHIRContext; }; class RawArrayType : public BuiltinType { public: std::string ToString() const override; size_t Hash() const override; bool operator==(const Type& other) const override; unsigned int GetDims() const { return dims; } Type* GetElementType() const { return argTys[0]; } private: explicit RawArrayType(Type* elemTy, unsigned int dims) : BuiltinType(TypeKind::TYPE_RAWARRAY), dims(dims) { argTys.emplace_back(elemTy); } ~RawArrayType() override = default; friend class CHIRContext; unsigned int dims; // array dimensions. }; class VArrayType : public BuiltinType { public: std::string ToString() const override; size_t Hash() const override; bool operator==(const Type& other) const override; unsigned int GetSize() const { return static_cast(size); } Type* GetElementType() const { return argTys[0]; } private: explicit VArrayType(Type* elemTy, int64_t size) : BuiltinType(TypeKind::TYPE_VARRAY), size(size) { argTys.emplace_back(elemTy); } ~VArrayType() override = default; friend class CHIRContext; int64_t size; // varray size. }; class RuneType : public BuiltinType { private: explicit RuneType() : BuiltinType(TypeKind::TYPE_RUNE) { } ~RuneType() override = default; friend class CHIRContext; }; class BooleanType : public BuiltinType { private: explicit BooleanType() : BuiltinType(TypeKind::TYPE_BOOLEAN) { } ~BooleanType() override = default; friend class CHIRContext; }; class UnitType : public BuiltinType { private: explicit UnitType() : BuiltinType(TypeKind::TYPE_UNIT) { } ~UnitType() override = default; friend class CHIRContext; }; class NothingType : public BuiltinType { private: explicit NothingType() : BuiltinType(TypeKind::TYPE_NOTHING) { } ~NothingType() override = default; friend class CHIRContext; }; class CStringType : public BuiltinType { private: explicit CStringType() : BuiltinType(TypeKind::TYPE_CSTRING) { } ~CStringType() override = default; friend class CHIRContext; }; struct CPointerType : BuiltinType { public: std::string ToString() const override; Type* GetElementType() const { return argTys[0]; } const std::vector& GetExtends(CHIRBuilder* builder = nullptr) const override; private: explicit CPointerType(Type* elemTy) : BuiltinType(TypeKind::TYPE_CPOINTER) { this->argTys.emplace_back(elemTy); } friend class CHIRContext; }; class GenericType : public Type { public: const std::vector& GetUpperBounds() const { return upperBounds; } void SetUpperBounds(const std::vector& args); std::string GetSrcCodeIdentifier() const { return srcCodeIdentifier; } /** * @brief Retrieves the instantiated member type by a given path. * * @param path The path to the member type. * @param builder The CHIR builder used for building the type. * @return The instantiated member type. */ Type* GetInstMemberTypeByPath(const std::vector& path, CHIRBuilder& builder) const { return GetInstMemberTypeByPathCheckingReadOnly(path, builder).first; } /** * @brief Retrieves the instantiated member type by a given path, checking for read-only. * * @param path The path to the member type. * @param builder The CHIR builder used for building the type. * @return A pair containing the type and a boolean flag indicating read-only status. */ std::pair GetInstMemberTypeByPathCheckingReadOnly( const std::vector& path, CHIRBuilder& builder) const; /** * @brief Retrieves the instance map for generic types. * * @param instMap The map to store the instance types. * @param builder The CHIR builder used for building the types. */ void GetInstMap(std::unordered_map& instMap, CHIRBuilder& builder) const; size_t Hash() const override; bool operator==(const Type& other) const override; std::string ToString() const override; std::string GetIdentifier() const { return identifier; } bool orphanFlag = false; // mark unimplemented free T bool skipCheck = false; private: explicit GenericType(const std::string& identifier, const std::string& srcName) : Type(TypeKind::TYPE_GENERIC), identifier(identifier), srcCodeIdentifier(srcName) { } ~GenericType() override = default; friend class CHIRContext; // Note, this identifier is different from the identifier in CJ source code, // cause it should be unique to help distinguish different generics in CHIR std::string identifier; std::string srcCodeIdentifier; std::vector upperBounds; }; class VoidType : public BuiltinType { private: explicit VoidType() : BuiltinType(TypeKind::TYPE_VOID) { } ~VoidType() override = default; friend class CHIRContext; }; const static std::unordered_map TYPEKIND_TO_STRING{ {Type::TypeKind::TYPE_INVALID, "Invalid"}, {Type::TypeKind::TYPE_INT8, "Int8"}, {Type::TypeKind::TYPE_INT16, "Int16"}, {Type::TypeKind::TYPE_INT32, "Int32"}, {Type::TypeKind::TYPE_INT64, "Int64"}, {Type::TypeKind::TYPE_INT_NATIVE, "IntNative"}, {Type::TypeKind::TYPE_UINT8, "UInt8"}, {Type::TypeKind::TYPE_UINT16, "UInt16"}, {Type::TypeKind::TYPE_UINT32, "UInt32"}, {Type::TypeKind::TYPE_UINT64, "UInt64"}, {Type::TypeKind::TYPE_UINT_NATIVE, "UIntNative"}, {Type::TypeKind::TYPE_FLOAT16, "Float16"}, {Type::TypeKind::TYPE_FLOAT32, "Float32"}, {Type::TypeKind::TYPE_FLOAT64, "Float64"}, {Type::TypeKind::TYPE_RUNE, "Rune"}, {Type::TypeKind::TYPE_BOOLEAN, "Bool"}, {Type::TypeKind::TYPE_UNIT, "Unit"}, {Type::TypeKind::TYPE_NOTHING, "Nothing"}, {Type::TypeKind::TYPE_TUPLE, "Tuple"}, {Type::TypeKind::TYPE_BOXTYPE, "BoxType"}, {Type::TypeKind::TYPE_STRUCT, "Struct"}, {Type::TypeKind::TYPE_ENUM, "Enum"}, {Type::TypeKind::TYPE_CLASS, "Class"}, {Type::TypeKind::TYPE_FUNC, "Func"}, {Type::TypeKind::TYPE_RAWARRAY, "RawArray"}, {Type::TypeKind::TYPE_VARRAY, "VArray"}, {Type::TypeKind::TYPE_CPOINTER, "CPointer"}, {Type::TypeKind::TYPE_CSTRING, "CString"}, {Type::TypeKind::TYPE_GENERIC, "GenericType"}, {Type::TypeKind::TYPE_VOID, "Void"}, {Type::TypeKind::TYPE_REFTYPE, "RefType"}}; Type* GetFieldOfType(Type& baseTy, uint64_t index, CHIRBuilder& builder); } // namespace Cangjie::CHIR #endif // CANGJIE_CHIR_TYPE_H cangjie_compiler-1.0.7/include/cangjie/CHIR/UserDefinedType.h000066400000000000000000000055761510705540100240040ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_USER_DEFINED_TYPE_H #define CANGJIE_CHIR_USER_DEFINED_TYPE_H #include "cangjie/AST/Node.h" #include "cangjie/CHIR/AttributeInfo.h" #include #include #include namespace Cangjie::CHIR { class FuncType; class Type; class GenericType; class FuncBase; class AttributeInfo; class ClassType; class Translator; class Value; class CustomTypeDef; class CHIRTypeCompare { public: bool operator()(const Type* key1, const Type* key2) const; }; struct VirtualFuncTypeInfo { FuncType* sigType{nullptr}; // instantiated type, (param types)->Unit, param types exclude `this` type FuncType* originalType{nullptr}; // virtual func's original func type from parent def, (param types)->retType, // param types include `this` type Type* parentType{nullptr}; // CustomType or extended type(may be primitive type) Type* returnType{nullptr}; // instantiated type std::vector methodGenericTypeParams; // store `T` of `func foo()` }; // for vtable struct VirtualFuncInfo { std::string srcCodeIdentifier; FuncBase* instance{nullptr}; AttributeInfo attr; VirtualFuncTypeInfo typeInfo; // store virtual func's type info, // if virtual func need to be wrappered, don't update type info }; using VTableType = std::map, CHIRTypeCompare>; using TranslateASTNodeFunc = std::function; struct FuncSigInfo { std::string funcName; // src code name FuncType* funcType{nullptr}; // declared type, including `this` type and return type // there may be generic type in it std::vector genericTypeParams; }; struct FuncCallType { std::string funcName; // src code name FuncType* funcType{nullptr}; // inst type, including `this` type and return type std::vector genericTypeArgs; }; struct VTableSearchRes { ClassType* instSrcParentType{nullptr}; // instantiated by instantiate func type ClassType* halfInstSrcParentType{nullptr}; // instantiated by current def FuncType* originalFuncType{nullptr}; // a generic func type, from current def, not parent def FuncBase* instance{nullptr}; CustomTypeDef* originalDef{nullptr}; // this virtual func belongs to a vtable, // and this vtable belongs to a CustomTypeDef std::vector genericTypeParams; AttributeInfo attr; size_t offset{0}; }; using ConvertTypeFunc = std::function; } #endifcangjie_compiler-1.0.7/include/cangjie/CHIR/Utils.h000066400000000000000000000615731510705540100220440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares utils API for CHIR */ #ifndef CANGJIE_CHIR_UTILS_H #define CANGJIE_CHIR_UTILS_H #include #include #include "cangjie/AST/ASTCasting.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/CHIR/Visitor/Visitor.h" #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/CHIRContext.h" #include "cangjie/CHIR/DiagAdapter.h" #include "cangjie/CHIR/Type/CHIRType.h" #include "cangjie/CHIR/Value.h" #include "cangjie/Utils/CastingTemplate.h" namespace Cangjie::CHIR { const int LENGTH_OF_IF = 2; using OptEffectCHIRMap = std::unordered_map, std::unordered_set>>; // ==================================== for global var init ====================================== template struct ElementList { std::vector stableOrderValue; std::unordered_set randomOrderValue; void AddElement(const T& element) { if (randomOrderValue.find(element) != randomOrderValue.end()) { return; } randomOrderValue.emplace(element); stableOrderValue.emplace_back(element); } void AddElements(const std::vector& elements) { for (auto& element : elements) { AddElement(element); } } bool HasElement(const T& element) const { return randomOrderValue.find(element) != randomOrderValue.end(); } }; struct StaticInitInfo { Ptr staticInitFunc; Ptr staticInitVar; std::vector> vars; StaticInitInfo(const Ptr& staticInitFunc, const Ptr& staticInitVar, const std::vector>& vars) : staticInitFunc(staticInitFunc), staticInitVar(staticInitVar), vars(vars) { } }; using StaticInitInfoMap = std::unordered_map, StaticInitInfo>; using InitOrder = std::vector, std::vector>>>; template struct DepMap { std::unordered_map> randomOrderValue; std::vector>> stableOrderValue; void InitDeps(const T& root) { randomOrderValue.emplace(root, std::vector{}); stableOrderValue.emplace_back(std::make_pair(root, std::vector{})); } void AddDep(const T& root, const T& dep) { auto& deps = randomOrderValue[root]; if (std::find(deps.begin(), deps.end(), dep) != deps.end()) { return; } randomOrderValue[root].emplace_back(dep); for (auto& element : stableOrderValue) { if (element.first == root) { element.second.emplace_back(dep); } } } void AddDeps(const T& root, const std::vector& deps) { CJC_ASSERT(randomOrderValue.find(root) == randomOrderValue.end()); randomOrderValue.emplace(root, deps); stableOrderValue.emplace_back(std::make_pair(root, deps)); } std::vector GetDep(const T& root) { return randomOrderValue[root]; } }; using FileDepMap = DepMap>; using DeclDepMap = DepMap>; using VarDepMap = std::unordered_map, std::vector>>; inline const std::string GV_INIT_WILDCARD_PATTERN = "wildcard_pattern_"; inline const std::string GV_PKG_INIT_ONCE_FLAG = "$has_applied_pkg_init_func"; // ==================================== for function matching ==================================== inline const std::string ANY_TYPE = "ANY_TYPE"; inline const std::string NOT_CARE = "NOT_CARE"; struct FuncInfo { const std::string funcName; const std::string parentTy; const std::vector params; const std::string returnTy; const std::string pkgName; FuncInfo(const std::string& name, const std::string& parent, const std::vector& params, const std::string& returnTy, const std::string& pkgName) noexcept : funcName(name), parentTy(parent), params(params), returnTy(returnTy), pkgName(pkgName) { } bool operator==(const FuncInfo& rhs) const { return (funcName == rhs.funcName && parentTy == rhs.parentTy && params == rhs.params && returnTy == rhs.returnTy && pkgName == rhs.pkgName); } }; /** * @brief Checks if a function matches the expected function information. * * @param func The function to check. * @param funcInfo The expected function information. * @return True if the function matches the expected information, false otherwise. */ bool IsExpectedFunction(const FuncBase& func, const FuncInfo& funcInfo); /** * @brief Prints optimization information for an expression. * * @param e The expression to print information for. * @param debug A flag indicating whether to print debug information. * @param optName The name of the optimization. */ void PrintOptInfo(const Expression& e, bool debug, const std::string& optName); // ==================================== for AST2CHIR.0 ==================================== /** * @brief Determines the kind of function from an AST function declaration. * * @param func The AST function declaration. * @return The kind of function. */ FuncKind GetFuncKindFromAST(const AST::FuncDecl& func); /** * @brief Checks if a function is a virtual function. * * @param funcDecl The function declaration to check. * @return True if the function is virtual, false otherwise. */ bool IsVirtualFunction(const FuncBase& funcDecl); /** * @brief Checks if a function is a static method in an interface. * * @param func The function to check. * @return True if the function is a static method in an interface, false otherwise. */ bool IsInterfaceStaticMethod(const Func& func); /** * @brief Checks if a function is semantically abstract and an instance. * * @param func The AST function declaration to check. * @return True if the function is semantically abstract and an instance, false otherwise. */ inline bool IsSemanticAbstractInstance(const AST::FuncDecl& func) { return !func.TestAttr(AST::Attribute::STATIC) && (func.TestAttr(AST::Attribute::ABSTRACT) || (func.outerDecl && func.outerDecl->astKind == AST::ASTKind::INTERFACE_DECL)); } /** * @brief Performs a topological sort on a set of blocks starting from the entry block. * * @param entrybb The entry block to start the sort. * @return A deque of blocks in topological order. */ std::deque TopologicalSort(Block* entrybb); /** * @brief Adds expressions to the global initialization function. * * @param initFunc The global initialization function. * @param insertExpr The expressions to add to the initialization function. */ void AddExpressionsToGlobalInitFunc(const Func& initFunc, const std::vector& insertExpr); /** * @brief Retrieves static member variables from a declaration. * * @param decl The declaration to retrieve static member variables from. * @return A vector of pointers to static member variables. */ std::vector> GetStaticMemberVars(const AST::InheritableDecl& decl); /** * @brief Retrieves non-static member variables from a declaration. * * @param decl The declaration to retrieve non-static member variables from. * @return A vector of pointers to non-static member variables. */ std::vector> GetNonStaticMemberVars(const AST::InheritableDecl& decl); /** * @brief Retrieves non-static superclass member variables from a class-like declaration. * * @param classLikeDecl The class-like declaration to retrieve non-static superclass member variables from. * @return A vector of pointers to non-static superclass member variables. */ std::vector> GetNonStaticSuperMemberVars(const AST::ClassLikeDecl& classLikeDecl); /** * @brief Checks if an enum declaration is an enum option. * * @param enumDecl The enum declaration to check. * @return True if the enum declaration is an enum option, false otherwise. */ inline bool IsEnumOption(const AST::EnumDecl& enumDecl) { return enumDecl.identifier == STD_LIB_OPTION && enumDecl.fullPackageName == CORE_PACKAGE_NAME; } /** * @brief Constructs the output path based on the output directory and package name. * * @param output The output directory or file path. * @param fullPkgName The full package name. * @param suffix The suffix to append to the output path. * @return The constructed output path. */ inline std::string GetOutputPath( const std::string& output, const std::string& fullPkgName, const std::string suffix = "_trans/") { if (Cangjie::FileUtil::IsDir(output)) { return Cangjie::FileUtil::JoinPath(output, fullPkgName) + suffix; } else { return Cangjie::FileUtil::GetFileBase(output) + suffix; } } /** * @brief Retrieves the debug position for a given expression. * * @tparam T The type of the expression. * @param expr The expression to retrieve the debug position from. * @return A pair containing a boolean and a range representing the debug position. */ template std::pair GetDebugPos(const T& expr) { auto warningPos = expr.template Get(); auto warningRange = ToRangeIfNotZero(warningPos); if (warningRange.first) { return warningRange; } auto& pos = expr.GetDebugLocation(); auto range = ToRangeIfNotZero(pos); return range; } /** * @brief Checks if a position is in a different package. * * @param pos The position to check. * @param currentPackage The current package name. * @param diag The diagnostic adapter. * @return True if the position is in a different package, false otherwise. */ bool IsCrossPackage(const Cangjie::Position& pos, const std::string& currentPackage, DiagAdapter& diag); /** * @brief Sets a value to skip print warnings. * * @param value The value to set the skip print warning flag for. */ inline void SetSkipPrintWarning(Ptr value) { value->Set(SkipKind::SKIP_DCE_WARNING); } inline void MergeEffectMap(const OptEffectCHIRMap& from, OptEffectCHIRMap& to) { for (auto fromIt : from) { auto toIt = to.find(fromIt.first); if (toIt == to.end()) { to.emplace(fromIt); } else { toIt->second.merge(fromIt.second); } } } /** * @brief Checks if a type has a "nothing" type. * * @param type The type to check. * @return True if the type has a "nothing" type, false otherwise. */ bool HasNothingType(Type& type); /** * @brief Replaces uses of a function with a wrapper function. * * @param curFunc The current function. * @param apply The apply to replace. * @param wrapperFunc The wrapper function. * @param isForeign A flag indicating if the replacement is foreign. */ void ReplaceUsesWithWrapper(Value& curFunc, const Apply* apply, Value& wrapperFunc, bool isForeign = false); /** * @brief Checks if a block group is nested within another block group. * * @param blockGroup The block group to check. * @param scope The scope block group. * @return True if the block group is nested within the scope, false otherwise. */ bool IsNestedBlockOf(const BlockGroup* blockGroup, const BlockGroup* scope); /** * @brief Creates a new type with the given arguments. * * @param oldType The old type to base the new type on. * @param newArgs The new type arguments. * @param builder The CHIR builder. * @return The new type with the given arguments. */ Type* CreateNewTypeWithArgs(Type& oldType, const std::vector& newArgs, CHIRBuilder& builder); /** * @brief Replaces raw generic argument types with new types. * * @param type The type to replace. * @param replaceTable The mapping of generic types to new types. * @param builder The CHIR builder. * @return The type with replaced raw generic argument types. */ Type* ReplaceRawGenericArgType( Type& type, const std::unordered_map& replaceTable, CHIRBuilder& builder); /** * @brief Replaces a type with a concrete type. * * @param type The type to replace. * @param concreteType The concrete type to replace with. * @param builder The CHIR builder. * @return The type replaced with the concrete type. */ Type* ReplaceThisTypeToConcreteType(Type& type, Type& concreteType, CHIRBuilder& builder); /** * @brief Converts a real function type to a virtual function type. * * @param type The real function type to convert. * @param builder The CHIR builder. * @return The virtual function type. */ FuncType* ConvertRealFuncTypeToVirtualFuncType(const FuncType& type, CHIRBuilder& builder); /** * @brief Performs a type cast or boxes a value if needed. * * @param val The value to cast or box. * @param expectedTy The expected type. * @param builder The CHIR builder. * @param parentBlock The parent block. * @param loc The debug location. * @param needCheck A flag indicating if a check is needed. * @return The value after type casting or boxing if needed. */ Ptr TypeCastOrBoxIfNeeded( Value& val, Type& expectedTy, CHIRBuilder& builder, Block& parentBlock, const DebugLocation& loc, bool needCheck = true); /** * @brief Creates and appends an expression to the builder. * * @tparam TExpr The type of the expression. * @tparam Args The argument types for the expression constructor. * @param builder The CHIR builder. * @param args The arguments for the expression constructor. * @return The created expression. */ template TExpr* CreateAndAppendExpression(CHIRBuilder& builder, Args&&... args) { auto expr = builder.CreateExpression(args...); expr->GetParentBlock()->AppendExpression(expr); return expr; } /** * @brief Creates and appends a terminator to the builder. * * @tparam TExpr The type of the terminator. * @tparam Args The argument types for the terminator constructor. * @param builder The CHIR builder. * @param args The arguments for the terminator constructor. * @return The created terminator. */ template TExpr* CreateAndAppendTerminator(CHIRBuilder& builder, Args&&... args) { auto expr = builder.CreateTerminator(args...); expr->GetParentBlock()->AppendExpression(expr); return expr; } /** * @brief Checks if a function is a static initializer. * * @param func The function to check. * @return True if the function is a static initializer, false otherwise. */ bool IsStaticInit(const AST::FuncDecl& func); bool IsStaticInit(const FuncBase& func); /** * @brief Checks if an expression is a super or this call. * * @param expr The expression to check. * @return True if the expression is a super or this call, false otherwise. */ bool IsSuperOrThisCall(const AST::CallExpr& expr); /** * @brief Retrieves the instance map from the current definition to the current type. * * @param curType The current type definition. * @return The instance map from the current definition to the current type. */ std::unordered_map GetInstMapFromCurDefToCurType(const CustomType& curType); std::unordered_map GetInstMapFromCurDefAndExDefToCurType(const CustomType& curType); /** * @brief Retrieves the instance map from a custom type definition and its parent. * * @param def The custom type definition. * @param instMap The instance map to populate. * @param builder The CHIR builder. */ void GetInstMapFromCustomDefAndParent( const CustomTypeDef& def, std::unordered_map& instMap, CHIRBuilder& builder); /** * @brief Retrieves all instantiated parent types for a given class type. * * @param cur The current class type. * @param builder The CHIR builder. * @param parents The vector to store the parent types. */ void GetAllInstantiatedParentType(ClassType& cur, CHIRBuilder& builder, std::vector& parents, std::set>* visited = nullptr); /** * @brief Creates a box type reference for a given base type. * * @param baseTy The base type to create a box type reference for. * @param builder The CHIR builder. * @return The created box type reference. */ Type* CreateBoxTypeRef(Type& baseTy, CHIRBuilder& builder); class GenericTypeConvertor { public: GenericTypeConvertor(const std::unordered_map& instMap, CHIRBuilder& builder) : instMap(instMap), builder(builder) {} Type* ConvertToInstantiatedType(Type& type); private: const std::unordered_map& instMap; CHIRBuilder& builder; }; /** * @brief Converts function parameters and return type using a convertor function. * * @param input The input function type to convert. * @param convertor The function to convert types. * @param builder The CHIR builder. * @return The converted function type. */ FuncType* ConvertFuncParamsAndRetType(FuncType& input, ConvertTypeFunc& convertor, CHIRBuilder& builder); /** * @brief Retrieves the types declared in an inner definition. * * @param innerDef The inner definition to retrieve types from. * @return A vector of types declared in the inner definition. */ std::vector GetOutDefDeclaredTypes(const Value& innerDef); /** * @brief Checks if a parent definition is from an extend. * * @param cur The current custom type definition. * @param parent The parent class definition. * @return True if the parent definition is from an extend, false otherwise. */ bool ParentDefIsFromExtend(const CustomTypeDef& cur, const ClassDef& parent); /** * @brief Visits function blocks in topological sort order. * * @param funcBody The function body to visit blocks for. * @param preVisit The function to call before visiting an expression. */ void VisitFuncBlocksInTopoSort(const BlockGroup& funcBody, std::function preVisit); /** * @brief Retrieves the function type from an auto environment base definition. * * @param autoEnvBaseDef The auto environment base definition. * @return A pair containing the name and function type. */ std::pair GetFuncTypeFromAutoEnvBaseDef(const ClassDef& autoEnvBaseDef); /** * @brief Retrieves the function type without the 'this' pointer from an auto environment base type. * * @param autoEnvBaseType The auto environment base type. * @return A pair containing a vector of parameter types and the return type. */ std::pair, Type*> GetFuncTypeWithoutThisPtrFromAutoEnvBaseType(const ClassType& autoEnvBaseType); /** * @brief Retrieves the method index in an auto environment object. * * @param methodName The name of the method to find the index for. * @return The index of the method in the auto environment object. */ size_t GetMethodIdxInAutoEnvObject(const std::string& methodName); /** * @brief Retrieves the selector type from an enum declaration. * * @param decl The enum declaration to retrieve the selector type from. * @return The selector type. */ Type::TypeKind GetSelectorType(const AST::EnumDecl& decl); /** * @brief Retrieves the selector type from an enum definition. * * @param def The enum definition to retrieve the selector type from. * @return The selector type. */ Type::TypeKind GetSelectorType(const EnumDef& def); /** * @brief Checks if a type can be used as a valid enum selector type. * * @param type The type to check. * @return True if the type can be used as a valid enum selector type, false otherwise. */ bool IsEnumSelectorType(const Type& type); /** * @brief Retrieves the extended interface definitions from a custom type definition. * * @param def The custom type definition to retrieve extended interface definitions from. * @return A vector of pointers to the extended interface definitions. */ std::vector GetExtendedInterfaceDefs(const CustomTypeDef& def); /** * @brief Checks if a custom type definition matches the expected package and source code name. * * @param def The custom type definition to check. * @param packageName The expected package name. * @param defSrcCodeName The expected source code name. * @return True if the custom type definition matches the expected package and source code name, false otherwise. */ bool CheckCustomTypeDefIsExpected( const CustomTypeDef& def, const std::string& packageName, const std::string& defSrcCodeName); /** * @brief Checks if a custom type definition is the core 'Any' type. * * @param def The custom type definition to check. * @return True if the custom type definition is the core 'Any' type, false otherwise. */ bool IsCoreAny(const CustomTypeDef& def); /** * @brief Checks if a custom type definition is the core 'Object' type. * * @param def The custom type definition to check. * @return True if the custom type definition is the core 'Object' type, false otherwise. */ bool IsCoreObject(const CustomTypeDef& def); /** * @brief Checks if a custom type definition is the core 'Option' type. * * @param def The custom type definition to check. * @return True if the custom type definition is the core 'Option' type, false otherwise. */ bool IsCoreOption(const CustomTypeDef& def); /** * @brief Checks if a custom type definition is the core 'Future' type. * * @param def The custom type definition to check. * @return True if the custom type definition is the core 'Future' type, false otherwise. */ bool IsCoreFuture(const CustomTypeDef& def); /** * @brief Checks if a class definition is a closure conversion environment class. * * @param def The class definition to check. * @return True if the class definition is a closure conversion environment class, false otherwise. */ bool IsClosureConversionEnvClass(const ClassDef& def); /** * @brief Checks if a class definition is a captured class. * * @param def The class definition to check. * @return True if the class definition is a captured class, false otherwise. */ bool IsCapturedClass(const ClassDef& def); /** * @brief Retrieves the parent function of a given value. * * @param value The value to retrieve the parent function from. * @return The parent function of the given value. */ Func* GetTopLevelFunc(const Value& value); /** * @brief Retrieves the visible generic types for a given value. * class A { * func foo() { * func goo1() { * func goo2() { * value --> visiable generic types' order is {T1, T2, T3, T4, T5, T6, T7, T8} * } * } * } * } * * @param value The value to retrieve visible generic types for. * @return A vector of pointers to the visible generic types. */ std::vector GetVisiableGenericTypes(const Value& value); /** * @brief Retrieves the function parameters from a function body. * * @param funcBody The function body to retrieve parameters from. * @return A vector of pointers to the function parameters. */ const std::vector& GetFuncParams(const BlockGroup& funcBody); /** * @brief Retrieves the return value from a function body. * * @param funcBody The function body to retrieve the return value from. * @return The return value of the function body. */ LocalVar* GetReturnValue(const BlockGroup& funcBody); /** * @brief Retrieves the function type from a function body. * * @param funcBody The function body to retrieve the function type from. * @return The function type of the function body. */ FuncType* GetFuncType(const BlockGroup& funcBody); /** * @brief Checks if a value is a struct or extension method. * * @param value The value to check. * @return True if the value is a struct or extension method, false otherwise. */ bool IsStructOrExtendMethod(const Value& value); /** * @brief Checks if a value is a constructor. * * @param value The value to check. * @return True if the value is a constructor, false otherwise. */ bool IsConstructor(const Value& value); /** * @brief get original value before cast. * @param expr get original value if expr is cast. * @return value before cast. */ Value* GetCastOriginalTarget(const Expression& expr); bool IsInstanceVarInit(const Value& value); std::vector GetSuperTypesRecusively(Type& subType, CHIRBuilder& builder); Type* GetInstParentCustomTypeForApplyCallee(const Apply& expr, CHIRBuilder& builder); Type* GetInstParentCustomTypeForAweCallee(const ApplyWithException& expr, CHIRBuilder& builder); std::vector GetFuncIndexInVTable( Type& root, const FuncCallType& funcCallType, bool isStatic, CHIRBuilder& builder); bool ParamTypeIsEquivalent(const Type& paramType, const Type& argType); /** * @brief the input type may not have vtable, but the return type must have vtable. * because CPointer's vtable is store in CPointer * * @param type given type. * @return type with vtable. */ BuiltinType* GetBuiltinTypeWithVTable(BuiltinType& type, CHIRBuilder& builder); } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Value.h000066400000000000000000000745411510705540100220170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_VALUE_H #define CANGJIE_CHIR_VALUE_H #include "cangjie/CHIR/AnnoInfo.h" #include "cangjie/CHIR/AttributeInfo.h" #include "cangjie/CHIR/Base.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/UserDefinedType.h" #include "cangjie/Utils/SafePointer.h" #include #include #include #include #include #include #include #include #include #include #include namespace Cangjie::CHIR { class Allocate; class Expression; class Constant; class Func; class FuncBody; class BlockGroup; class Terminator; class CustomTypeDef; class FuncType; class Debug; class Lambda; class CHIRBuilder; class ClassDef; class LiteralValue; class Value : public Base { friend class CHIRContext; friend class CHIRBuilder; friend class Expression; friend class Terminator; friend class ValueTypeConverter; template friend class ValueTypeFunctor; friend class TypeConverterForCC; friend class CustomDefTypeConverter; friend class CHIRDeserializer; friend class CHIRSerializer; public: enum ValueKind : uint8_t { KIND_LITERAL, KIND_GLOBALVAR, KIND_PARAMETER, KIND_IMP_FUNC, KIND_IMP_VAR, KIND_LOCALVAR, KIND_FUNC, KIND_BLOCK, KIND_BLOCK_GROUP }; // ===--------------------------------------------------------------------===// // Base Infomation // ===--------------------------------------------------------------------===// bool IsBlock() const; bool IsBlockGroup() const; // including: // 1. imported function // 2. function in current package bool IsFunc() const; // including: // 1. func declared in current package // 2. func declared in imported pacakge but instantiated in current package // 3. func declared in imported pacakge with `const` // 4. func declared in imported pacakge with @Frozen and compiled with O2 bool IsFuncWithBody() const; // including: // 1. func and global var in current package // 2. imported func and imported global var bool IsGlobal() const; // including: // 1. imported global var // 2. global var in current package bool IsGlobalVar() const; bool IsGlobalVarInCurPackage() const; bool IsImportedFunc() const; // including: // 1. imported function // 2. imported global var bool IsImportedSymbol() const; bool IsImportedVar() const; bool IsLiteral() const; bool IsLocalVar() const; // including: // 1. parameter in function // 2. parameter in lambda bool IsParameter() const; Type* GetType() const; virtual std::string GetSrcCodeIdentifier() const; const std::string& GetIdentifier() const; std::string GetIdentifierWithoutPrefix() const; std::vector GetUsers() const; // we replace `this` with `newValue` in `scope`, when `scope` is nullptr, we replace nodes in package scope void ReplaceWith(Value& newValue, const BlockGroup* scope = nullptr); virtual std::string ToString() const = 0; void Dump() const; bool IsCompileTimeValue() const; // ===--------------------------------------------------------------------===// // Attribute // ===--------------------------------------------------------------------===// AttributeInfo GetAttributeInfo() const; void AppendAttributeInfo(const AttributeInfo& info); void DisableAttr(Attribute attr); void EnableAttr(Attribute attr); bool TestAttr(Attribute attr) const; // ===--------------------------------------------------------------------===// // Annotation // ===--------------------------------------------------------------------===// const AnnoInfo& GetAnnoInfo() const; void SetAnnoInfo(AnnoInfo&& info); protected: explicit Value(Type* ty, std::string identifier, ValueKind kind); virtual ~Value() = default; Value(const Value&) = delete; Value& operator=(const Value&) = delete; // ===--------------------------------------------------------------------===// // User // ===--------------------------------------------------------------------===// void AddUserOnly(Expression* expr); void ClearUsersOnly(); void RemoveUserOnly(Expression* expr); protected: Type* ty; // variable type std::string identifier; // variable identifier AttributeInfo attributes; // variable attribute std::vector users; // variable users std::mutex userMutex; // mutex for AddUserOnly and RemoveUserOnly AnnoInfo annoInfo; // annoInfo, used in struct/class/enum member func private: ValueKind GetValueKind() const; ValueKind kind; // value kind }; class Parameter : public Value { friend class CHIRBuilder; friend class FuncBody; friend class Expression; friend class Lambda; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// std::string GetSrcCodeIdentifier() const override; std::string ToString() const override; // ===--------------------------------------------------------------------===// // Parent // ===--------------------------------------------------------------------===// Func* GetOwnerFunc() const; void SetOwnerFunc(Func* owner); Lambda* GetOwnerLambda() const; void SetOwnerLambda(Lambda* newParent); Func* GetTopLevelFunc() const; // ===--------------------------------------------------------------------===// // Debug Expression // ===--------------------------------------------------------------------===// // if parameter do not have debug, return nullptr Debug* GetDebugExpr() const; private: Func* ownerFunc = nullptr; Lambda* ownerLambda = nullptr; private: explicit Parameter(Type* ty, const std::string& indexStr, Func* ownerFunc); explicit Parameter(Type* ty, const std::string& indexStr, Lambda& ownerLambda); ~Parameter() override = default; }; class LocalVar : public Value { friend class CHIRBuilder; friend class Expression; friend class Func; friend class Lambda; friend class CHIRDeserializer; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// Expression* GetExpr() const; std::string GetSrcCodeIdentifier() const override; std::string ToString() const override; const DebugLocation& GetDebugLocation() const override; bool IsRetValue() const; // ===--------------------------------------------------------------------===// // Parent // ===--------------------------------------------------------------------===// BlockGroup* GetOwnerBlockGroup() const; Func* GetTopLevelFunc() const; // ===--------------------------------------------------------------------===// // Debug Expression // ===--------------------------------------------------------------------===// // if localVar do not have debug, return nullptr Debug* GetDebugExpr() const; private: explicit LocalVar(Type* ty, std::string indexStr, Expression* expr); ~LocalVar() override = default; void SetRetValue(); private: Expression* expr; // The owner of this result value. bool isRetValue = false; // If func return value or not }; /** * this class represent imported/non-imported global var and static member var in Cangjie's source code. */ class GlobalVarBase : public virtual Value { friend class CHIRBuilder; friend class CustomTypeDef; friend class ImportedVar; friend class GlobalVar; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// std::string GetSrcCodeIdentifier() const override; const std::string& GetPackageName() const; const std::string& GetRawMangledName() const; // ===--------------------------------------------------------------------===// // Parent // ===--------------------------------------------------------------------===// /** * @brief only static member var has declaredParent, others return nullptr */ CustomTypeDef* GetParentCustomTypeDef() const; void DestroySelf(); protected: std::string packageName; // package where this globalVar defined by user std::string srcCodeIdentifier; // the name of global variable std::string rawMangledName; // rawMangledName, generated by Parser, used by Incremental Compile CustomTypeDef* declaredParent = nullptr; // e.g. class A { static var x = 1 } // `A` is declaredParent of `x` private: explicit GlobalVarBase(std::string srcCodeIdentifier, std::string rawMangledName, std::string packageName); ~GlobalVarBase() override = default; }; class GlobalVar : public GlobalVarBase { friend class CHIRBuilder; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// LiteralValue* GetInitializer() const; void SetInitializer(LiteralValue& literalValue); Func* GetInitFunc() const; void SetInitFunc(Func& func); std::string ToString() const override; bool IsLocalConst() const; protected: LiteralValue* initializer = nullptr; // the initializer must be a LiteralValue if exist Func* initFunc{}; // init function of this globalVar, null if initializer is not null private: explicit GlobalVar(Type* ty, std::string identifier, std::string srcCodeIdentifier, std::string rawMangledName, std::string packageName); ~GlobalVar() override = default; }; enum FuncKind : uint8_t { DEFAULT, // Default funcKind GETTER, SETTER, LAMBDA, // cced lambda CLASS_CONSTRUCTOR, PRIMAL_CLASS_CONSTRUCTOR, STRUCT_CONSTRUCTOR, PRIMAL_STRUCT_CONSTRUCTOR, GLOBALVAR_INIT, FINALIZER, MAIN_ENTRY, ANNOFACTORY_FUNC, MACRO_FUNC, DEFAULT_PARAMETER_FUNC, INSTANCEVAR_INIT, /**< These functions are related to initialization of class/struct's instance variables */ FUNCKIND_END }; const std::unordered_map FUNCKIND_TO_STRING{{FuncKind::DEFAULT, "default"}, {FuncKind::GETTER, "getter"}, {FuncKind::SETTER, "setter"}, {FuncKind::LAMBDA, "lambda"}, {FuncKind::CLASS_CONSTRUCTOR, "classConstructor"}, {FuncKind::PRIMAL_CLASS_CONSTRUCTOR, "classPrimalConstructor"}, {FuncKind::STRUCT_CONSTRUCTOR, "structConstructor"}, {FuncKind::PRIMAL_STRUCT_CONSTRUCTOR, "structPrimalConstructor"}, {FuncKind::GLOBALVAR_INIT, "globalVarInit"}, {FuncKind::FINALIZER, "finalizer"}, {FuncKind::MAIN_ENTRY, "mainEntry"}, {FuncKind::ANNOFACTORY_FUNC, "annoFactory"}, {FuncKind::MACRO_FUNC, "macro"}, {FuncKind::DEFAULT_PARAMETER_FUNC, "defaultParameter"}, {FuncKind::INSTANCEVAR_INIT, "memberVariablesInit"}}; struct AbstractMethodParam { std::string paramName; Type* type = nullptr; AnnoInfo annoInfo; std::string ToString(); }; struct AbstractMethodInfo { std::string methodName; // abstract method name Type* methodTy = nullptr; // abstract method type std::vector paramInfos; // abstract method parameters AttributeInfo attributeInfo; // abstract method attribute AnnoInfo annoInfo; // abstract method annoInfo std::vector methodGenericTypeParams; // store `T` of `func foo()` bool hasBody; // abstract method in interface may have func body ClassDef* parent = nullptr; AbstractMethodInfo(const std::string& methodName, const std::string& mangledName, Type* methodTy, const std::vector& paramInfos, const AttributeInfo& attrInfo, const AnnoInfo& annoInfo, const std::vector& methodGenericTypeParams, bool hasBody, ClassDef* parent) : methodName(methodName), methodTy(methodTy), paramInfos(paramInfos), attributeInfo(attrInfo), annoInfo(annoInfo), methodGenericTypeParams(methodGenericTypeParams), hasBody(hasBody), parent(parent), mangledName(mangledName) { } bool TestAttr(Attribute attr) const { return attributeInfo.TestAttr(attr); } // This function will append ".0" to the original mangledName from AST to avoid backend issues. std::string GetMangledName() const { return mangledName + ".0"; } std::string GetASTMangledName() const { return mangledName; } private: std::string mangledName; // abstract method mangled name }; class ImportedValue : public virtual Value { friend class CHIRBuilder; friend class ImportedVar; friend class ImportedFunc; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// std::string ToString() const override; virtual const std::string& GetSourcePackageName() const = 0; // ===--------------------------------------------------------------------===// // Modify Self // ===--------------------------------------------------------------------===// virtual void DestroySelf(); private: explicit ImportedValue(); ~ImportedValue() override = default; }; class Block : public Value { friend class CHIRContext; friend class CHIRBuilder; friend class Expression; friend class Terminator; friend class BlockGroup; friend class CHIRDeserializer; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// std::string ToString() const override; std::vector GetSuccessors() const; std::vector GetPredecessors() const; std::vector GetExceptions() const; void SetExceptions(const std::vector& ep); bool IsLandingPadBlock() const; bool IsEntry() const; // ===--------------------------------------------------------------------===// // Expressions // ===--------------------------------------------------------------------===// void AppendExpressions(const std::vector& expressions); void AppendExpression(Expression* expression); Expression* GetExpressionByIdx(size_t idx) const; std::vector GetExpressions() const; size_t GetExpressionsNum() const; std::vector GetNonTerminatorExpressions() const; Terminator* GetTerminator() const; void InsertExprIntoHead(Expression& expr); // ===--------------------------------------------------------------------===// // Parent // ===--------------------------------------------------------------------===// BlockGroup* GetParentBlockGroup() const; void SetParentBlockGroup(BlockGroup* parent); Func* GetTopLevelFunc() const; // ===--------------------------------------------------------------------===// // Modify Self // ===--------------------------------------------------------------------===// void MoveTo(BlockGroup& newBlockGroup); void RemoveSelfFromBlockGroup(); Block* Clone(CHIRBuilder& builder, BlockGroup& newGroup) const; private: explicit Block(std::string identifier, BlockGroup* parentGroup); ~Block() override = default; Block(const Block&) = delete; Block& operator=(const Block&) = delete; void RemoveExprOnly(Expression& expr); void AddPredecessor(Block* block); void AppendNonTerminatorExpression(Expression* expression); void AppendTerminator(Terminator* term); void RemovePredecessor(Block& block); void AppendExprOnly(Expression& expr); void ClearExprsOnly(); void AppendPredecessorOnly(Block& block); void RemovePredecessorOnly(Block& block); void ClearPredecessorsOnly(); private: BlockGroup* parentGroup; // block parent block group std::vector exprs; // block expressions std::vector predecessors; // predecessors /** * @brief the exceptions info * nullopt for general block * empty vector for landingpad block catch all exceptions * non-empty vector for landingpad block catch specific exceptions */ std::optional> exceptions{std::nullopt}; }; /** * Consist of a group of Blocks, included by Func、Lambda、For、Loop、If */ class BlockGroup : public Value { friend class CHIRContext; friend class CHIRBuilder; friend class Block; friend class Lambda; friend class ForIn; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// size_t GetExpressionsNum() const; std::string ToString() const override; // ===--------------------------------------------------------------------===// // Block // ===--------------------------------------------------------------------===// void AddBlock(Block* block); void AddBlocks(const std::vector& newBlocks); Block* GetBlockByIdx(size_t idx) const; std::vector GetBlocks() const; Block* GetEntryBlock() const; void SetEntryBlock(Block* block); // ===--------------------------------------------------------------------===// // Parent // ===--------------------------------------------------------------------===// /** * @brief Get the func where this blockGroup located * * if this blockGroup belongs to If/Loop/ForIn/Lambda, * the method will also continue looking up until ownerFunc is not empty. */ Func* GetTopLevelFunc() const; Func* GetOwnerFunc() const; void SetOwnerFunc(Func* func); Expression* GetOwnerExpression() const; BlockGroup* Clone(CHIRBuilder& builder, Func& newFunc) const; BlockGroup* Clone(CHIRBuilder& builder, Lambda& newLambda) const; private: explicit BlockGroup(std::string identifier); ~BlockGroup() override = default; void ClearBlockGroup(); void ClearBlocksOnly(); void SetOwnedFuncOnly(Func* newFunc); void SetOwnerExpression(Expression& expr); void RemoveBlock(Block& block); void CloneBlocks(CHIRBuilder& builder, BlockGroup& parent) const; private: Block* entryBlock = nullptr; // block group entryBlock std::vector blocks; Func* ownerFunc = nullptr; /**< can only be Lambda/ForIn/If/Loop */ Expression* ownerExpression = nullptr; }; /** * @brief FuncBody class is an interlayer. * * FuncBody is used to keep parameters and body of function and lambda */ class FuncBody { friend class Func; friend class Lambda; private: BlockGroup* GetBody() const; void RemoveBody(); void RemoveParams(); void AddParam(Parameter& param); Parameter* GetParam(size_t index) const; const std::vector& GetParams() const; LocalVar* GetReturnValue() const; void SetReturnValue(LocalVar& ret); explicit FuncBody(); private: BlockGroup* body = nullptr; std::vector parameters; LocalVar* retValue = nullptr; }; class FuncBase : public virtual Value { friend class TypeConverterForCC; friend class CustomTypeDef; friend class ImportedFunc; friend class Func; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// bool IsConstructor() const; bool IsFinalizer() const; bool IsGVInit() const; bool IsLambda() const; bool IsPrimalConstructor() const; bool IsInstanceVarInit() const; bool IsStaticInit() const; FuncKind GetFuncKind() const; void SetFuncKind(FuncKind kind); const std::string& GetPackageName() const; std::string GetSrcCodeIdentifier() const override; const std::string& GetRawMangledName() const; void SetRawMangledName(const std::string& name); FuncBase* GetGenericDecl() const; void SetGenericDecl(FuncBase& decl); bool IsFastNative() const; void SetFastNative(bool fastNative); bool IsCFFIWrapper() const; void SetCFFIWrapper(bool isWrapper); FuncBase* GetParamDftValHostFunc() const; void SetParamDftValHostFunc(FuncBase& hostFunc); // ===--------------------------------------------------------------------===// // Signature Infomation // ===--------------------------------------------------------------------===// void SetOriginalLambdaInfo(const FuncSigInfo& info); FuncType* GetOriginalLambdaType() const; std::vector GetOriginalGenericTypeParams() const; size_t GetNumOfParams() const; FuncType* GetFuncType() const; Type* GetReturnType() const; const std::vector& GetGenericTypeParams() const; // ===--------------------------------------------------------------------===// // Parent // ===--------------------------------------------------------------------===// CustomTypeDef* GetParentCustomTypeDef() const; Type* GetParentCustomTypeOrExtendedType() const; CustomTypeDef* GetOuterDeclaredOrExtendedDef() const; /** * @brief a function in class, struct, enum or extend */ bool IsMemberFunc() const; /** * @brief a method declared in class, including in extend class */ bool IsClassMethod() const; /** * @brief a method declared in struct, including in extend struct */ bool IsStructMethod() const; /** * @brief a method declared in enum, including in extend enum */ bool IsEnumMethod() const; /** * @brief a method defined within `Extend` scope */ bool IsInExtend() const; // ===--------------------------------------------------------------------===// // Attribute // ===--------------------------------------------------------------------===// /** * @brief there are two cases: * 1. func is generic, that means `TestAttr(Attribute::GENERIC)` is true * 2. func has parent CustomTypeDef, and its parent is generic */ bool IsInGenericContext() const; bool IsCFunc() const; bool IsVirtualFunc() const; // ===--------------------------------------------------------------------===// // Modify Self // ===--------------------------------------------------------------------===// virtual void DestroySelf(); protected: std::string srcCodeIdentifier; // origin name std::string rawMangledName; std::string packageName; CustomTypeDef* declaredParent{nullptr}; // e.g. class A { func foo() {} } // `class A` is declaredParent of `foo` // e.g. extend A { func goo() {} } // `extend A` is declaredParent of `goo` FuncBase* genericDecl = nullptr; // original generic decl from which current instantiated decl derives FuncKind funcKind = FuncKind::DEFAULT; bool isFastNative{}; // is this func is annotated with @FastNative bool isCFFIWrapper = false; // when generated by CFFI wrapper. /** 1. when lambda is lifted to global func, its generic type params and func type may be changed, * but cjdb need original type to show * e.g. func foo() { * func goo() {} // this lambda will be lifted to global func * } * after being lifted, global func `goo` is as follow: * func goo(env: class&) {} */ FuncSigInfo originalLambdaInfo; std::vector genericTypeParams; /** func foo(a !: Bool = true) {} * param `a` is desugared by sema to global function named `a.0` * we name `foo` is host function of `a.0`, so `paramDftValHostFunc` of `a.0` is `foo` */ FuncBase* paramDftValHostFunc = nullptr; private: FuncBase(const std::string& srcCodeIdentifier, const std::string& rawMangledName, const std::string& packageName, const std::vector& genericTypeParams); }; /* * @brief Func class in CHIR. * * Func is an expression with a block group. */ class Func : public FuncBase { friend class BlockGroup; friend class CHIRBuilder; friend class ValueTypeConverter; friend class CHIRSerializer; friend class CHIRDeserializer; public: // ===--------------------------------------------------------------------===// // Base Information // ===--------------------------------------------------------------------===// std::string ToString() const override; uint64_t GenerateBlockId(); uint64_t GenerateBlockGroupId(); uint64_t GenerateLocalId(); void InheritIDFromFunc(const Func& func); const DebugLocation& GetPropLocation() const; void SetPropLocation(const DebugLocation& loc); // ===--------------------------------------------------------------------===// // Func Body // ===--------------------------------------------------------------------===// BlockGroup* GetBody() const; void InitBody(BlockGroup& newBody); void ReplaceBody(BlockGroup& newBody); Block* GetEntryBlock() const; void AddParam(Parameter& param); Parameter* GetParam(size_t index) const; const std::vector& GetParams() const; /** * @brief get a `LocalVar` represent the returned value of this Func. */ LocalVar* GetReturnValue() const; bool HasReturnValue() const; void SetReturnValue(LocalVar& ret); size_t GetExpressionsNum() const; // ===--------------------------------------------------------------------===// // Modify Self // ===--------------------------------------------------------------------===// void DestroySelf() override; // ===--------------------------------------------------------------------===// // Incremental Compile // ===--------------------------------------------------------------------===// /** * Gets the RawMangledName of parent if parent is ExtendDecl. Returns null otherwise. This function is only used in * collection of CHIR optimisation in incremental compilation and should not be used elsewhere. */ const std::string& GetParentRawMangledName() const; /** * Sets the RawMangledName if parent is ExtendDecl. This function is only used in collection of CHIR optimisation * in incremental compilation and should not be used elsewhere. */ void SetParentRawMangledName(const std::string& name); private: void DestroyFuncBody(); void RemoveBody(); void RemoveParams(); void SetLocalId(uint64_t id); void SetBlockId(uint64_t id); void SetBlockGroupId(uint64_t id); private: explicit Func(Type* ty, const std::string& identifier, const std::string& srcCodeIdentifier, const std::string& rawMangledName, const std::string& packageName, const std::vector& genericTypeParams = {}); ~Func() override = default; FuncBody body; std::string parentName; // a hack, if parent is extend decl, we store its raw mangled name, only extend decl DebugLocation propLoc; // when this is setter or getter, it has prop location, needed by cjlint uint64_t localId = 0; // the local Id in expression uint64_t blockId = 0; // the id in block uint64_t blockGroupId = 0; // the id in block group }; class ImportedFunc : public ImportedValue, public FuncBase { friend class CHIRBuilder; friend class ValueTypeConverter; public: const std::vector& GetParamInfo() const; void SetParamInfo(std::vector&& params); const std::string& GetSourcePackageName() const override; ~ImportedFunc() override = default; std::string ToString() const override; private: ImportedFunc(Type* ty, const std::string& identifier, const std::string& srcCodeIdentifier, const std::string& rawMangledName, const std::string& packageName, const std::vector& genericTypeParams = {}); private: // In incremental compilation, non-recompiled funcs will be represented as an `importedValue`, // in this case, we need to record more information // Note: some of this is overlapped with other meta-datas, and we should have this not only for // incremental compilation, but all cases std::vector paramInfo; // abstract method parameters }; class ImportedVar : public ImportedValue, public GlobalVarBase { friend class CHIRBuilder; public: const std::string& GetSourcePackageName() const override; private: explicit ImportedVar(Type* ty, std::string identifier, std::string srcCodeIdentifier, std::string rawMangledName, std::string packageName); ~ImportedVar() override = default; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/include/cangjie/CHIR/Visitor/000077500000000000000000000000001510705540100222165ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/CHIR/Visitor/SimpleIterator.h000066400000000000000000000026401510705540100253340ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Simple Iterator in CHIR. */ #ifndef CANGJIE_CHIR_SIMPLEITERATOR_H #define CANGJIE_CHIR_SIMPLEITERATOR_H #include namespace Cangjie::CHIR { class Expression; class Block; class BlockGroup; /* * @brief Simple Iterator for CHIR node. * */ class SimpleIterator { public: /** * @brief Iterates over an expression and returns a vector of block groups. * * @param expr The expression to iterate over. * @return A vector of block groups. */ static std::vector Iterate(const Expression& expr); /** * @brief Iterates over a block group and returns a vector of blocks. * * @param blockGroup The block group to iterate over. * @return A vector of blocks. */ static std::vector Iterate(const BlockGroup& blockGroup); /** * @brief Iterates over a block and returns a vector of expressions. * * @param block The block to iterate over. * @return A vector of expressions. */ static std::vector Iterate(const Block& block); }; } // namespace Cangjie::CHIR #endif // CANGJIE_CHIR_SIMPLEITERATOR_Hcangjie_compiler-1.0.7/include/cangjie/CHIR/Visitor/Visitor.h000066400000000000000000000351671510705540100240420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Visitor Class in CHIR. */ #ifndef CANGJIE_CHIR_VISITOR_H #define CANGJIE_CHIR_VISITOR_H #include "cangjie/CHIR/Value.h" #include "cangjie/CHIR/Visitor/SimpleIterator.h" #include namespace Cangjie::CHIR { /* * @brief VisitResult of the walk * */ enum class VisitResult : uint8_t { CONTINUE, /**< Continue to walk into child items. */ SKIP, /**< Continue walking, but don't enter child items. */ STOP /**< Stop walking immediately. */ }; using VisitExprActionFunc = std::function; using VisitBlockActionFunc = std::function; using VisitBlockGroupActionFunc = std::function; /* * @brief Walk all blocksGroups nested under the given expression. * * The order in which blocksGroups, blocks and expressions are visited is determined by 'Iterator', * default using 'SimpleIterator'. * */ template VisitResult Walk(const Expression& expr, VisitBlockGroupActionFunc pre, VisitBlockGroupActionFunc post) { for (auto& blockGroup : Iterator::Iterate(expr)) { auto status = pre(*blockGroup); if (status == VisitResult::SKIP) { continue; } if (status == VisitResult::STOP) { return VisitResult::STOP; } for (auto& block : Iterator::Iterate(*blockGroup)) { for (auto& innerExpr : Iterator::Iterate(*block)) { if (Walk(*innerExpr, pre, post) == VisitResult::STOP) { return VisitResult::STOP; } } } if (post(*blockGroup) == VisitResult::STOP) { return VisitResult::STOP; } } return VisitResult::CONTINUE; } /* * @brief Walk all blocks nested under the given expression. * * The order in which blocksGroups, blocks and expressions are visited is determined by 'Iterator', * default using 'SimpleIterator'. * */ template VisitResult Walk(const Expression& expr, VisitBlockActionFunc pre, VisitBlockActionFunc post) { for (auto& blockGroup : Iterator::Iterate(expr)) { for (auto& block : Iterator::Iterate(*blockGroup)) { auto status = pre(*block); if (status == VisitResult::SKIP) { continue; } if (status == VisitResult::STOP) { return VisitResult::STOP; } for (auto& innerExpr : Iterator::Iterate(*block)) { if (Walk(*innerExpr, pre, post) == VisitResult::STOP) { return VisitResult::STOP; } } if (post(*block) == VisitResult::STOP) { return VisitResult::STOP; } } } return VisitResult::CONTINUE; } /* * @brief Walk all expressions nested under (and including) the given expression. * * The order in which blocksGroups, blocks and expressions are visited is determined by 'Iterator', * default using 'SimpleIterator'. * */ template VisitResult Walk(Expression& expr, VisitExprActionFunc pre, VisitExprActionFunc post) { auto status = pre(expr); if (status == VisitResult::SKIP) { return VisitResult::SKIP; } if (status == VisitResult::STOP) { return VisitResult::STOP; } for (auto& blockGroup : Iterator::Iterate(expr)) { for (auto& block : Iterator::Iterate(*blockGroup)) { for (auto& innerExpr : Iterator::Iterate(*block)) { if (Walk(*innerExpr, pre, post) == VisitResult::STOP) { return VisitResult::STOP; } } } } return post(expr); } /// checking whether type T is one of any of the given /// types in the variadic list. template using IsAnyOf = std::disjunction...>; /// Helper templates to deduce the argument and return type of an ActionFunc. template Arg ArgTy(Ret (*)(Arg)); template Ret RetTy(Ret (*)(Arg)); template Arg ArgTy(Ret (F::*)(Arg)); template Ret RetTy(Ret (F::*)(Arg)); template Arg ArgTy(Ret (F::*)(Arg) const); template Ret RetTy(Ret (F::*)(Arg) const); template decltype(ArgTy(&F::operator())) ArgTy(F); template decltype(RetTy(&F::operator())) RetTy(F); /// Type definition of the argument and return of the given callable 'T'. template using ArgTOf = decltype(ArgTy(std::declval())); template using RetTOf = decltype(RetTy(std::declval())); /* * @brief Visitor for CHIR Node * * Visit all of the blocksGroups, blocks or expressions nested under (and including) * the given block group, basicblock or expression. */ class Visitor { public: /* * @brief Visit all of the blocksGroups, blocks or expressions nested under (and including) * the given expression. * * The order in which blocksGroups, blocks and expressions are visited is determined by 'Iterator', * default using 'SimpleIterator'. * * Example: * Visitor::Visit(expr, [](Expression& e) { ... }, [](Expression& e) { ... }); * Visitor::Visit(expr, [](Block& block) { ... }, [](Block& block) { ... }); * Visitor::Visit(expr, [](BlockGroup& t) { ... }, [](BlockGroup& t) { ... }); * */ template , Expression&, Block&, BlockGroup&>::value && std::is_same, ArgTOf>::value && std::is_same, VisitResult>::value && std::is_same, RetTOf>::value>> static void Visit(Expression& root, PreActionFuncTy pre, PostActionFuncTy post) { (void)Walk(root, pre, post); } /* * @brief Visit all of the blocksGroups, blocks or expressions nested under (and including) * the given expression. * * The order in which blocksGroups, blocks and expressions are visited is determined by 'Iterator', * default using 'SimpleIterator'. * * Example: * Visitor::Visit(expr, [](Expression& e) { ... }); * Visitor::Visit(expr, [](Block& block) { ... }); * Visitor::Visit(expr, [](BlockGroup& t) { ... }); * */ template , typename = std::enable_if_t::value && std::is_same, VisitResult>::value>> static void Visit(Expression& root, PreActionFuncTy pre) { auto post = [](ArgT) { return VisitResult::CONTINUE; }; (void)Walk(root, pre, post); } /* * @brief Visit all of the Derived Expression nested under (and including) * the given expression. * * The order in which blocksGroups, blocks and expressions are visited is determined by 'Iterator', * default using 'SimpleIterator'. * * Example: * Visitor::Visit(expr, [](BinaryExpr& be) { ... }); * Visitor::Visit(expr, [](Branch& b) { ... }); * */ template , typename = std::enable_if_t, VisitResult>::value>, typename = std::enable_if_t::value && std::is_convertible::value>> static void Visit(Expression& root, PreActionFuncTy pre) { auto wrapperPre = [&pre](Expression& child) { if (auto argTy = dynamic_cast*>(&child); argTy) { return pre(*argTy); } return VisitResult::CONTINUE; }; auto post = [](Expression&) { return VisitResult::CONTINUE; }; (void)Walk(root, wrapperPre, post); } /* * @brief Visit all of the Derived Expression nested under (and including) * the given expression. * * The order in which blocksGroups, blocks and expressions are visited is determined by 'Iterator', * default using 'SimpleIterator'. * * Example: * Visitor::Visit(expr, [](BinaryExpr& be) { ... }, [](BinaryExpr& be) { ... }); * Visitor::Visit(expr, [](Branch& b) { ... }, [](Branch& b) { ... }); * */ template , typename = std::enable_if_t, VisitResult>::value && std::is_same, VisitResult>::value && !std::is_same::value && std::is_convertible::value && std::is_same, ArgT>::value>> static void Visit(Expression& root, PreActionFuncTy pre, PostActionFuncTy post) { auto wrapperPre = [&pre](Expression& child) { if (auto argTy = dynamic_cast*>(&child); argTy) { return pre(*argTy); } return VisitResult::CONTINUE; }; auto wrapperPost = [&post](Expression& child) { if (auto argTy = dynamic_cast*>(&child); argTy) { return post(*argTy); } return VisitResult::CONTINUE; }; (void)Walk(root, wrapperPre, wrapperPost); } /* * @brief Visit all of the blocksGroups, blocks, expressions or derived expression nested under (and including) * the given block or block group. * * The order in which blocksGroups, blocks and expressions are visited is determined by 'Iterator', * default using 'SimpleIterator'. * * Example: * Visitor::Visit(block, [](Expression& e) { ... }); * Visitor::Visit(blockGroup, [](Block& block) { ... }); * Visitor::Visit(blockGroup, [](BlockGroup& t) { ... }); * Visitor::Visit(blockGroup, [](Branch& b) { ... }); * */ template , typename = std::enable_if_t::value && std::is_same, VisitResult>::value>> static void Visit(RootTy& root, PreActionFuncTy pre) { if constexpr (std::is_same::value) { auto status = pre(static_cast(root)); if (status == VisitResult::SKIP || status == VisitResult::STOP) { return; } } for (auto& child : Iterator::Iterate(root)) { Visit(*child, pre); } } /* * @brief Visit all of the blocksGroups, blocks, expressions or derived expression nested under (and including) * the given block or block group. * * The order in which blocksGroups, blocks and expressions are visited is determined by 'Iterator', * default using 'SimpleIterator'. * * Example: * Visitor::Visit(block, [](Expression& e) { ... }, [](Expression& e) { ... }); * Visitor::Visit(blockGroup, [](Block& block) { ... }, [](Block& block) { ... }); * Visitor::Visit(blockGroup, [](BlockGroup& t) { ... }, [](BlockGroup& t) { ... }); * Visitor::Visit(blockGroup, [](Branch& b) { ... }, [](Branch& b) { ... }); * */ template , typename = std::enable_if_t::value && std::is_same, VisitResult>::value && std::is_same>::value && std::is_same, RetTOf>::value>> static void Visit(RootTy& root, PreActionFuncTy pre, PostActionFuncTy post) { if constexpr (std::is_same::value) { auto status = pre(static_cast(root)); if (status == VisitResult::SKIP || status == VisitResult::STOP) { return; } } for (auto& child : Iterator::Iterate(root)) { Visit(*child, pre, post); } if constexpr (std::is_same::value) { post(static_cast(root)); } } /* * @brief Visit all of blocksGroups, blocks and expressions under (and including) * the given func. * * Example: * Visitor::Visit(func, [](BinaryExpr& be) { ... }); * Visitor::Visit(func, [](Branch& b) { ... }); * */ template , typename = std::enable_if_t, VisitResult>::value>, typename = std::enable_if_t::value>> static void Visit(const Func& root, PreActionFuncTy pre) { Visit(*root.GetBody(), pre); } /* * @brief Visit all of blocksGroups, blocks and expressions under (and including) * the given func. * * Example: * Visitor::Visit(func, [](BinaryExpr& be) { ... }, [](BinaryExpr& be) { ... }); * Visitor::Visit(func, [](Branch& b) { ... }, [](Branch& b) { ... }); * */ template , typename = std::enable_if_t, VisitResult>::value && std::is_same, VisitResult>::value && !std::is_same::value && std::is_same, ArgT>::value>> static void Visit(const Func& root, PreActionFuncTy pre, PostActionFuncTy post) { Visit(*root.GetBody(), pre, post); } }; } // namespace Cangjie::CHIR #endif // CANGJIE_CHIR_VISITOR_Hcangjie_compiler-1.0.7/include/cangjie/CodeGen/000077500000000000000000000000001510705540100213365ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/CodeGen/EmitPackageIR.h000066400000000000000000000042521510705540100241170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_EMITPACKAGEIR_H #define CANGJIE_EMITPACKAGEIR_H #include "llvm/IR/Module.h" #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/Package.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/FrontendTool/IncrementalCompilerInstance.h" #include "cangjie/Option/Option.h" namespace Cangjie::CodeGen { /** * @brief This function generates the package modules. * Note that after using llvm::Module, call the ClearPackageModules to clear the memory. * * @param chirBuilder A CHIRBuilder of CHIR. * @param chirData CHIRData of a complete package. * @param options GlobalOptions to compile a package. * @param compilerInstance DefaultCompilerInstance. * @param enableIncrement A falg, indicating whether incremental compilation is enabled. * @return A vector of std::unique_ptr. If --aggressive-parallel-compile is enabled, * multiple llvm::Modules are returned. Otherwise, only one llvm::Module is returned. */ #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::vector> GenPackageModules(CHIR::CHIRBuilder& chirBuilder, const CHIRData& chirData, const GlobalOptions& options, DefaultCompilerInstance& compilerInstance, bool enableIncrement); #endif /** * @brief Save the LLVM module to the specified Bitcode file path * * @param module A llvm::Module to be cached. * @param bcFilePath CHIRData of a complete package * @return If the saving is successful, true is returned. Otherwise, false is returned. */ bool SavePackageModule(const llvm::Module& module, const std::string& bcFilePath); /** * @brief Clear and release all modules. It ensures that all resources are properly released and cleaned up. * * @param packageModules A vector of unique pointers to LLVM modules to be cleared. */ void ClearPackageModules(std::vector>& packageModules); } // namespace Cangjie::CodeGen #endif // CANGJIE_EMITPACKAGEIR_H cangjie_compiler-1.0.7/include/cangjie/ConditionalCompilation/000077500000000000000000000000001510705540100244745ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/ConditionalCompilation/ConditionalCompilation.h000066400000000000000000000022171510705540100313110ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the ConditionalCompilation related classes, which provides conditional compilation capabilities. */ #ifndef CANGJIE_CONDITIONAL_COMPILATION_H #define CANGJIE_CONDITIONAL_COMPILATION_H #include "cangjie/AST/Node.h" #include "cangjie/Frontend/CompilerInstance.h" namespace Cangjie { namespace AST { class ConditionalCompilation { public: friend class CompilerInstance; explicit ConditionalCompilation(CompilerInstance* ci); ~ConditionalCompilation(); /// entrance of conditional compilation stage void HandleConditionalCompilation(const Package& root) const; /// file entrance of conditional compilation stage. Used by \ref HandleConditionalCompilation and macro expansion void HandleFileConditionalCompilation(File& file) const; private: class ConditionalCompilationImpl* impl; }; } // namespace AST } // namespace Cangjie #endif cangjie_compiler-1.0.7/include/cangjie/Driver/000077500000000000000000000000001510705540100212655ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/Driver/Backend/000077500000000000000000000000001510705540100226145ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/Driver/Backend/Backend.h000066400000000000000000000045551510705540100243250ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Backend related classes, which provides versatile compile utility. */ #ifndef CANGJIE_DRIVER_BACKEND_H #define CANGJIE_DRIVER_BACKEND_H #include "cangjie/Driver/DriverOptions.h" #include "cangjie/Driver/Toolchains/ToolChain.h" #include "cangjie/Option/Option.h" namespace Cangjie { class Tool; class Job; /** * Backend is an interface. An implementation of Backend generates one or more * tools. A tool contains a tool name (e.g. llc, javac), some arguments (e.g. -O2, -static, -L.), and * some necessary environment settings for generating an actual executable command. A tool represents * a well-structured command. Backend itself does not execute any commands. */ class Backend { public: /** * @brief The constructor of class Backend. * * @param job The compilation job. * @param driverOptions The data structure is obtained through parsing the compilation options. * @param driver It is the object that triggers the compiler's compilation process. * @return Backend The instance of Backend. */ explicit Backend(Job& job, const DriverOptions& driverOptions, const Driver& driver) : driver(driver), driverOptions(driverOptions), ownerJob(job) {} /** * @brief The destructor of class Backend. */ virtual ~Backend() = default; /** * @brief Generate toolchain, assembly tools. * * @return bool Return true If generate success. */ bool Generate(); const std::vector& GetBackendCmds() { return backendCmds; } protected: const Driver& driver; const DriverOptions& driverOptions; std::unique_ptr TC; Job& ownerJob; std::vector backendCmds; virtual bool GenerateToolChain() = 0; virtual bool ProcessGeneration() = 0; /** * Check whether tools exist (and get their paths if required) * If some dependencies are not available, this function should return false so the generation won't proceed. */ virtual bool PrepareDependencyPath() = 0; }; } // namespace Cangjie #endif // CANGJIE_DRIVER_BACKEND_H cangjie_compiler-1.0.7/include/cangjie/Driver/Backend/CJNATIVEBackend.h000066400000000000000000000071251510705540100254450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the cjnative backend class. */ #ifndef CANGJIE_DRIVER_BACKEND_CJNATIVE_H #define CANGJIE_DRIVER_BACKEND_CJNATIVE_H #include "cangjie/Driver/Backend/Backend.h" #include "cangjie/Driver/DriverOptions.h" #include "cangjie/Option/Option.h" namespace Cangjie { /** * CJNATIVEBackend generates three different kind of tools representing three different compiling stages. * The first stage is 'opt', which does some preprocessing on bitcode and outputs intermediate bitcode files. * The second stage is 'llc', which compiles bitcode into object files. * The final stage is 'ld', which links object files together and outputs an executable or a dynamic library. * For each input bitcode file, an 'opt' and a 'llc' tool will be generated. * Only one 'ld' linker will be used to create an executable file from all the object files (including those * given as input). */ class CJNATIVEBackend : public Backend { public: /** * @brief The constructor of class CJNATIVEBackend. * * @param job The compilation job. * @param driverOptions The data structure is obtained through parsing the compilation options. * @param driver It is the object that triggers the compiler's compilation process. * @return CJNATIVEBackend The instance of CJNATIVEBackend. */ CJNATIVEBackend(Job& job, const DriverOptions& driverOptions, const Driver& driver) : Backend(job, driverOptions, driver){}; /** * @brief The destructor of class CJNATIVEBackend. */ ~CJNATIVEBackend() = default; protected: bool GenerateToolChain() override; /** * @brief Scan where 'opt', 'llc' and 'ld' are and get their paths. */ bool PrepareDependencyPath() override; bool ProcessGeneration() override; private: static const std::string tempFolder; std::string optPath; std::string llcPath; /** * @brief Create an instance of a cjnative base tool. */ std::unique_ptr GenerateCJNativeBaseTool(const std::string& toolPath); std::vector GetFrontendOutputs(); void GenerateCacheCopyTool(const std::vector& files, const std::string& extension); /** * @brief This function generates 'opt' tools. For each .bc input file, an 'opt' tool is generated. * The output is a list of filenames, which are output files of 'opt' tools. * We name it PreprocessTool here because the cjnative backend not only does optimize (what 'opt' stands for), * but also does some important bitcode transformation (e.g. intrinsic functions) that 'llc' requires. */ std::vector GeneratePreprocessTools(const std::vector& bitCodeFiles); void PreprocessOfNewPassManager(Tool& tool); bool ProcessGenerationOfNormalCompile(const std::vector& bitCodeFiles); bool ProcessGenerationOfIncrementalNoChangeCompile(const std::vector& bitCodeFiles); /** * @brief This function generates 'llc' tools. For each .opt.bc input file (which is output file from 'opt' tools) * a 'llc' tool is generated. The return is a list of files generated by 'llc' tools. */ std::vector GenerateCompileTool( const std::vector& bitCodeFiles, bool emitAssembly = false); }; } // namespace Cangjie #endif // CANGJIE_DRIVER_BACKEND_CJNATIVE_H cangjie_compiler-1.0.7/include/cangjie/Driver/Driver.h000066400000000000000000000037741510705540100227040ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Driver, who runs the compiler. */ #ifndef CANGJIE_DRIVER_DRIVER_H #define CANGJIE_DRIVER_DRIVER_H #include #include #include #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Driver/DriverOptions.h" namespace Cangjie { class Driver { public: /** * @brief The constructor of class Driver. * * @param args The arguments vector. * @param diag The main diagnostic processing center. * @param exeName The name of exe. * @return Driver The instance of Driver. */ Driver(const std::vector& args, DiagnosticEngine& diag, const std::string& exeName = "cjc"); /** * @brief Parse arguments and setup options specified by user in the command. * * @return bool Return true If success. */ bool ParseArgs(); /** * @brief Read necessary paths from environment variables and store them in GlobalOptions. * * @param environmentVars The environment variables. */ void EnvironmentSetup(const std::unordered_map& environmentVars); /** * @brief The main function of the compilation process. * * @return bool Return true If success. */ bool ExecuteCompilation() const; /** * @brief Generate backend and linking commands. * * @return bool Return true If success. */ bool InvokeCompileToolchain() const; std::vector args; DiagnosticEngine& diag; std::unique_ptr optionTable; std::unique_ptr argList; std::unique_ptr driverOptions; std::string executableName; std::string cangjieHome; }; } // namespace Cangjie #endif // CANGJIE_DRIVER_DRIVER_H cangjie_compiler-1.0.7/include/cangjie/Driver/DriverOptions.h000066400000000000000000000110731510705540100242470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the DriverOptions, which provides options for Driver. */ #ifndef CANGJIE_DRIVER_DRIVEROPTIONS_H #define CANGJIE_DRIVER_DRIVEROPTIONS_H #include #include "cangjie/Option/Option.h" namespace Cangjie { class DriverOptions : public GlobalOptions { public: /** * @brief The constructor of class DriverOptions. * * @return DriverOptions The instance of DriverOptions. */ DriverOptions() = default; /** * @brief The destructor of class DriverOptions. */ ~DriverOptions() override = default; std::string optArg; std::string llcArg; std::optional targetCPU = std::nullopt; bool linkStatic = false; // Strip symbol table for DSO and executable or not. // Controlled by --strip-all/-s. bool stripSymbolTable = false; // User-provided a custom argument to linker. // Values is passed by --link-option. std::vector linkOption; // User-provided custom arguments to linker. // Values is passed by --link-options. std::vector linkOptions; // User-provided paths for linkers to search for libraries, // passed by --library-path/-L. std::vector librarySearchPaths; // User-provided library names for linkers, // passed by --library/-l. std::vector libraries; // User-provided paths for the driver to search for binaries and object files, // passed by --toolchain/-B. std::vector toolChainPaths; // Sysroot is the root directory under which toolchain binaries, libraries and header files can be found. // The sysroot is "/" by default. #ifdef _WIN32 std::string sysroot = "C:/windows"; #else std::string sysroot = "/"; #endif bool customizedSysroot = false; bool useRuntimeRpath = false; // Whether cjc write rpath with sanitizer version cangjie runtime path to binary bool sanitizerEnableRpath = false; bool incrementalCompileNoChange = false; // ---------- CODE OBFUSCATION OPTIONS ---------- bool enableObfAll = false; /** * @brief Check whether it is obfuscation enabled. * * @return bool Return true If it is obfuscation enabled. */ bool IsObfuscationEnabled() const override { return enableStringObfuscation || enableConstObfuscation || enableLayoutObfuscation || enableCFflattenObfuscation || enableCFBogusObfuscation; } std::optional enableObfExportSyms; std::optional enableObfLineNumber; std::optional enableObfSourcePath; std::optional enableLayoutObfuscation; std::optional enableConstObfuscation; std::optional enableStringObfuscation; std::optional enableCFflattenObfuscation; std::optional enableCFBogusObfuscation; std::optional layoutObfSymPrefix; std::optional layoutObfInputSymMappingFiles = std::nullopt; std::optional layoutObfOutputSymMappingFile = std::nullopt; std::optional layoutObfUserMappingFile = std::nullopt; std::optional obfuscationConfigFile = std::nullopt; static const int OBFUCATION_LEVEL_MIN = 1; static const int OBFUCATION_LEVEL_MAX = 9; /** * Valid range of obfuscation level is [1,9] (see OBFUCATION_LEVEL_MIN and OBFUCATION_LEVEL_MAX). * Higher obfuscation level represents that more obfuscation work will be done on the code, which * makes the product more difficult to be understood by reverse enginering. Higher obfuscation level * also causes larger code size and more runtime overhead. 5 is a balanced choice, for both source * code safety and cost. */ int obfuscationLevel = 5; std::optional obfuscationSeed = 0; /** * @brief Reprocess obfuse option. * * @return bool Return true. */ bool ReprocessObfuseOption() override; protected: virtual std::optional ParseOption(OptionArgInstance& arg) override; virtual bool PerformPostActions() override; private: bool CheckObfuscationOptions() const; bool CheckTargetCPUOption(); bool SetupSysroot(); bool CheckRuntimeRPath() const; bool CheckSanitizerRPath() const; bool CheckStaticOption(); }; } // namespace Cangjie #endif // CANGJIE_DRIVER_DRIVEROPTIONS_H cangjie_compiler-1.0.7/include/cangjie/Driver/Stdlib.inc000066400000000000000000000047401510705540100232060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file lists paths to std module bitcode files, which is included by headers. */ #ifdef STDLIB STDLIB(CORE, "std", "core") STDLIB(BINARY, "std", "binary") STDLIB(IO, "std", "io") STDLIB(MATH, "std", "math") STDLIB(OVERFLOW, "std", "overflow") STDLIB(RUNTIME, "std", "runtime") STDLIB(CONVERT, "std", "convert") STDLIB(RANDOM, "std", "random") STDLIB(COLLECTION, "std", "collection") STDLIB(UNICODE, "std", "unicode") STDLIB(SORT, "std", "sort") STDLIB(ARGOPT, "std", "argopt") #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND STDLIB(AST, "std", "ast") STDLIB(INTEROP, "std", "interop") #endif STDLIB(TIME, "std", "time") STDLIB(SYNC, "std", "sync") STDLIB(COLLECTION.CONCURRENT, "std", "collection.concurrent") STDLIB(NET, "std", "net") STDLIB(REGEX, "std", "regex") STDLIB(UNITTEST.COMMON, "std", "unittest.common") STDLIB(UNITTEST.PROP_TEST, "std", "unittest.prop_test") STDLIB(UNITTEST.DIFF, "std", "unittest.diff") STDLIB(MATH.NUMERIC, "std", "math.numeric") STDLIB(FS, "std", "fs") STDLIB(UNITTEST.MOCK.INTERNAL, "std", "unittest.mock.internal") STDLIB(UNITTEST.MOCK, "std", "unittest.mock") STDLIB(REFLECT, "std", "reflect") STDLIB(REF, "std", "ref") STDLIB(STD.CRYPTO, "std", "crypto") STDLIB(CRYPTO.DIGEST, "std", "crypto.digest") STDLIB(CRYPTO.CIPHER, "std", "crypto.cipher") STDLIB(CONSOLE, "std", "console") STDLIB(DATABASE, "std", "database") STDLIB(DATABASE.SQL, "std", "database.sql") STDLIB(POSIX, "std", "posix") STDLIB(PROCESS, "std", "process") STDLIB(ENV, "std", "env") STDLIB(OBJECTPOOL, "std", "objectpool") STDLIB(UNITTEST, "std", "unittest") #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND STDLIB(DERIVING.API, "std", "deriving.api") STDLIB(DERIVING.RESOLVE, "std", "deriving.resolve") STDLIB(DERIVING.IMPL, "std", "deriving.impl") STDLIB(DERIVING.BUILTINS, "std", "deriving.builtins") STDLIB(DERIVING, "std", "deriving") STDLIB(UNITTEST.TESTMACRO, "std", "unittest.testmacro") STDLIB(UNITTEST.MOCK.MOCKMACRO, "std", "unittest.mock.mockmacro") #endif // CANGJIE_CODEGEN_CJNATIVE_BACKEND STDLIB_TOPPKG(STD, "std") #ifndef BACKEND_BC STDLIB_ROOTPKG(STD, "std") STDLIB(FFI.PYTHON, "std", "ffi.python") // This will cause `cjstack` cannot be linked, because missing symbol `LLVMFuzzerRunDriver`. STDLIB(FUZZ, "fuzz", "fuzz") #endif #endif cangjie_compiler-1.0.7/include/cangjie/Driver/StdlibMap.h000066400000000000000000000017461510705540100233250ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the map of standard library names. */ #ifndef CANGJIE_DIRVIER_STDLIBMAP_H #define CANGJIE_DIRVIER_STDLIBMAP_H #include #include namespace Cangjie { const std::string GET_COMMAND_LINE_ARGS = "getCommandLineArgs"; const std::string MODULE_SPLIT = "/"; const std::unordered_map STANDARD_LIBS = { #define STDLIB(NAME, MODULE, SUB_PACKAGE) {MODULE "." SUB_PACKAGE, SUB_PACKAGE}, #define STDLIB_ROOTPKG(NAME, ROOT_PACKAGE) {ROOT_PACKAGE, ROOT_PACKAGE}, #define STDLIB_TOPPKG(NAME, TOP_PACKAGE) {TOP_PACKAGE, TOP_PACKAGE}, #include "cangjie/Driver/Stdlib.inc" #undef STDLIB_TOPPKG #undef STDLIB_ROOTPKG #undef STDLIB }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/include/cangjie/Driver/TempFileInfo.h000066400000000000000000000035511510705540100237630ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the TempFileInfo and TempFileKind. */ #ifndef CANGJIE_DRIVER_TEMP_FILE_INFO_H #define CANGJIE_DRIVER_TEMP_FILE_INFO_H #include namespace Cangjie { // A struct for passing output file info between Driver and Frontend. struct TempFileInfo { std::string fileName; // Record file name without suffix std::string filePath; // Record the absolute file path. std::string rawPath{""}; // Record the original path of the file. bool isFrontendOutput{false}; // Record file is output by the frontend bool isForeignInput{false}; // Record whether it is a pre-compiled file (.bc/.o) provided by users. }; enum class TempFileKind { O_CJO, // output .cjo file O_FULL_BCHIR, // output .full.bchir file O_BCHIR, // output .bchir file T_BC, // temp .bc(bitcode) file O_BC, // output .bc(bitcode) file O_EXE, // output executable file O_DYLIB, // output dynamic library file O_STATICLIB, // output static library file O_MACRO, // output dynamic library file for macro O_CHIR, // output CHIR serialization file #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND T_OPT_BC, // temp .opt.bc(optimized bitcode) file O_OPT_BC, // output .opt.bc(optimized bitcode) file T_ASM, // temp .s(assembled) file #endif T_OBJ, // temp .o(binary object) file T_EXE_MAC, // temp executable file for macro strip T_DYLIB_MAC, // temp dynamic library file for macro strip }; } // namespace Cangjie #endif // CANGJIE_DRIVER_TEMP_FILE_INFO_Hcangjie_compiler-1.0.7/include/cangjie/Driver/TempFileManager.h000066400000000000000000000066601510705540100244460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the TempFileManager class. */ #ifndef CANGJIE_DRIVER_TEMP_FILES_UTIL_H #define CANGJIE_DRIVER_TEMP_FILES_UTIL_H #include #include #include #include #include "cangjie/Driver/TempFileInfo.h" #include "cangjie/Option/Option.h" namespace Cangjie { class TempFileManager { public: /** * @brief Disable the copy constructor of class TempFileManager. */ TempFileManager(TempFileManager const&) = delete; /** * @brief Disable the copy assignment operator of class TempFileManager. */ TempFileManager& operator=(TempFileManager const&) = delete; /** * @brief Obtains the globally unique TempFileManager instance. * * @return TempFileManager The globally unique TempFileManager instance. */ static TempFileManager& Instance() { static TempFileManager manager{}; return manager; } /** * @brief Initialize the constructed TempFileManager. * * @param options GlobalOptions Instance. * @param isFrontend It is cjc-frontend or cjc being executed. * @return bool Return true if Initialization succeeded. */ bool Init(const GlobalOptions& options, bool isFrontend); /** * @brief Create a new TempFileInfo whose type is kind. * * @param info Old TempFileInfo, It may only have the fileName field. * @param kind Type of TempFileInfo to be created. * @return TempFileInfo The new TempFileInfo. */ TempFileInfo CreateNewFileInfo(const TempFileInfo& info, TempFileKind kind); /** * @brief Get the path of the temporary folder. * It may be a generated temporary directory or a user specified location. * * @return std::string The path of the temporary folder. */ std::string GetTempFolder(); /** * @brief Delete all temporary files. * * @param isSignalSafe Whether temporary files are safe. */ void DeleteTempFiles(bool isSignalSafe = false); /** * @brief Check whether temporary files are deleted. * * @return bool Return true If temporary files are deleted. */ bool IsDeleted() const; private: bool isCjcFrontend{false}; GlobalOptions opts{}; std::string tempDir{}; std::string outputDir{}; std::string outputName{}; std::vector deletedFiles{}; std::atomic isDeleted{0}; // 0: not deleted, 1: deleting, 2: deleted TempFileManager(){}; bool InitOutPutDir(); bool InitTempDir(); TempFileInfo CreateIntermediateFileInfo(const TempFileInfo& info, TempFileKind kind); TempFileInfo CreateTempBcFileInfo(const TempFileInfo& info, TempFileKind kind); TempFileInfo CreateOutputFileInfo(const TempFileInfo& info, TempFileKind kind); std::unordered_map> fileSuffixMap; TempFileInfo CreateTempFileInfo(const TempFileInfo& info, TempFileKind kind); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND TempFileInfo CreateLinuxLLVMOptOutputBcFileInfo(const TempFileInfo& info, TempFileKind kind); #endif std::string GetDylibSuffix() const; }; } // namespace Cangjie #endif // CANGJIE_DRIVER_TEMP_FILES_UTIL_H cangjie_compiler-1.0.7/include/cangjie/Driver/Tool.h000066400000000000000000000164611510705540100223630ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares Tool class and its functions. */ #ifndef CANGJIE_DRIVER_TOOL_H #define CANGJIE_DRIVER_TOOL_H #include #include #include #include #include #include "cangjie/Utils/FileUtil.h" #include "cangjie/Driver/ToolFuture.h" namespace Cangjie { enum class ToolType { UNKNOWN, FRONTEND, BACKEND, OTHER, INTERNAL_IMPLEMENTED }; class Tool { public: /** * @brief The constructor of class Tool. * * @param name Tool name. * @param type Tool type. * @param environmentVars Environment for tool execution. * @return Tool The instance of Tool. */ Tool(const std::string& name, ToolType type, const std::unordered_map& environmentVars) : type(type), name(name), environmentVars(environmentVars) { } /** * @brief The destructor of class Tool. */ ~Tool() = default; const ToolType type{ToolType::UNKNOWN}; /**< Tool type. */ const std::string name; /**< Tool name, search from path of operation system. */ const std::unordered_map environmentVars; /**< Environment for tool execution. */ /** * @brief Get arguments. * * @return std::vector& A list of appended arguments. */ const std::vector& GetArgs() const { return args; } /** * @brief Get full arguments. * * @return std::vector A list of full arguments. */ const std::vector GetFullArgs() const { std::vector fullArgs{}; fullArgs.emplace_back(name); fullArgs.insert(fullArgs.end(), args.begin(), args.end()); return fullArgs; } /** * @brief Append an argument to the argument list. * * @param arg The argument to append. */ void AppendArg(const std::string& arg) { this->args.emplace_back(arg); } /** * @brief Append an argument to the argument list if the condition is true. * * @param condition If condition is true, argument is appended, otherwise nothing happens. * @param argument The argument to append. */ template void AppendArgIf(bool condition, String&& argument) { if (condition) { this->args.emplace_back(std::forward(argument)); } } /** * @brief Append arguments by using AppendArg(arg). * * @param arg The first argument to append. * @param margs The remaining arguments to append. */ template void AppendArg(const std::string& arg, Args... margs) { AppendArg(arg); AppendArg(margs...); } /** * @brief Append arguments to the argument list if the condition is true. * * @param condition If condition is true, arguments are appended, otherwise nothing happens. * @param argument The first argument to append. * @param arguments The remaining arguments to append. */ template void AppendArgIf(bool condition, String&& argument, Args&&... arguments) { if (condition) { AppendArg(std::forward(argument)); AppendArg(std::forward(arguments)...); } } /** * @brief Append a list of arguments by using AppendArg(arg). * * @param margs The args argument list. */ void AppendArg(const std::vector& margs) { for (const std::string& arg : margs) { AppendArg(arg); } } /** * @brief Break the input into parts and append the results as arguments for the tool. * The input argument will be split into mutiple arguments with spaces. This method * could be used to append a sub-command (a list of arguments joined by spaces) to * the tool. * * @param arg The string of arguments to append. */ void AppendMultiArgs(const std::string& argStr); /** * @brief Append arguments by using AppendMultiArgs(arg). * * @param arg The first string of arguments to append. * @param args The remaining string of arguments to append. */ template void AppendMultiArgs(const std::string& arg, Args... margs) { AppendMultiArgs(arg); AppendMultiArgs(margs...); } /** * @brief Append a list of arguments by using AppendMultiArgs(arg). * * @param args The list of string of arguments to append. */ void AppendMultiArgs(const std::vector& margs) { for (const std::string& arg : margs) { AppendMultiArgs(arg); } } /** * @brief Set LD_LIBRARY_PATH. * * @param newLdLibraryPath The new LD_LIBRARY_PATH to set. */ void SetLdLibraryPath(const std::string& newLdLibraryPath) { this->ldLibraryPath = newLdLibraryPath; } /** * @brief Get LD_LIBRARY_PATH. */ std::string GetLdLibraryPath() const { return ldLibraryPath; } /** * @brief Execute the run method. * * @return std::unique_ptr The pointer of ToolFuture. */ std::unique_ptr Run() const; /** * @brief Get name. * * @return std::string The name. */ std::string GetName() const { return name; } /** * @brief Get command string. * * @return std::string The command string. */ const std::string GetCommandString() const { return GenerateCommand(); } /** * @brief The execute method. * * @return std::unique_ptr The pointer of ToolFuture. */ std::unique_ptr Execute(bool verbose) const; private: std::vector args{}; /**< Arguments for this tool. */ std::string ldLibraryPath{""}; /**< LD_LIBRARY_PATH */ std::string GenerateCommand() const; #ifndef _WIN32 std::list BuildEnvironmentVector() const; #endif bool InternalImplementedCommandExec() const; }; enum class ToolID { #define TOOL(ID, TYPE, NAME) ID, #include "cangjie/Driver/Tools.inc" #undef TOOL }; struct ToolInfo { const std::string name; /**< The external binary tools name. */ /** * @brief The constructor of class ToolInfo. * * @param name The external binary tools name. * @return ToolInfo The thread future. */ explicit ToolInfo(std::string name) : name(name) { } }; /** * @brief Make single tool batch. * * @param tools The pointer of Tool. * @return ToolBatch A list of tool pointer. */ using ToolBatch = std::vector>; inline ToolBatch MakeSingleToolBatch(std::unique_ptr tools) { ToolBatch result{}; result.emplace_back(std::move(tools)); return result; } const std::unordered_map g_toolList = { #define TOOL(ID, TYPE, NAME) {ToolID::ID, ToolInfo(NAME)}, #include "cangjie/Driver/Tools.inc" #undef TOOL }; } // namespace Cangjie #endif // CANGJIE_DRIVER_TOOL_H cangjie_compiler-1.0.7/include/cangjie/Driver/ToolFuture.h000066400000000000000000000052431510705540100235520ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares ToolFuture class and its subclasses. */ #ifndef CANGJIE_DRIVER_TOOLFUTURE_H #define CANGJIE_DRIVER_TOOLFUTURE_H #include #include #ifdef _WIN32 #include #include #undef CONST #undef interface #else #include #endif #include "cangjie/Utils/Utils.h" using namespace Cangjie; class ToolFuture { public: enum class State { SUCCESS, RUNNING, FAILED }; /** * @brief Get status of the asynchronous operation indicated by 'ToolFuture'. * * @return State The thread state. */ virtual State GetState() = 0; /** * @brief The destructor of class ToolFuture. */ virtual ~ToolFuture() {}; }; class ThreadFuture : public ToolFuture { public: /** * @brief The constructor of class ThreadFuture. * * @param input The result of asynchronous operation. * @return ThreadFuture The thread future. */ explicit ThreadFuture(std::future&& input) : future(std::move(input)) {} /** * @brief Get status of the asynchronous operation indicated by 'ThreadFuture'. * * @return State The thread state. */ State GetState() override; private: std::optional result = std::nullopt; std::future future; }; #ifdef _WIN32 class WindowsProcessFuture : public ToolFuture { public: /** * @brief The constructor of class WindowsProcessFuture. * * @param pi The process information. * @return WindowsProcessFuture The windows process future. */ explicit WindowsProcessFuture(PROCESS_INFORMATION pi): pi(pi) {} /** * @brief Get status of the asynchronous operation indicated by 'WindowsProcessFuture'. * * @return State The thread state. */ State GetState() override; private: PROCESS_INFORMATION pi; }; #else class LinuxProcessFuture : public ToolFuture { public: /** * @brief The constructor of class LinuxProcessFuture. * * @param pi The process id. * @return LinuxProcessFuture The linux process future. */ explicit LinuxProcessFuture(pid_t pid) : pid(pid) {} /** * @brief Get status of the asynchronous operation indicated by 'LinuxProcessFuture'. * * @return State The status of asynchronous operation. */ State GetState() override; private: pid_t pid; }; #endif #endif // CANGJIE_DRIVER_TOOLFUTURE_Hcangjie_compiler-1.0.7/include/cangjie/Driver/ToolOptions.h000066400000000000000000000145601510705540100237350ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares Tool Options functions. */ #ifndef CANGJIE_DRIVER_TOOL_OPTIONS_H #define CANGJIE_DRIVER_TOOL_OPTIONS_H #include #include #include #include "cangjie/Driver/DriverOptions.h" namespace Cangjie { namespace ToolOptions { using SetFuncType = const std::function; using ToolOptionType = std::function; namespace OPT { /** * @brief Set the new password manager opt options. * * @param setOptionHandler The function to remove the initial hyphen in the options. * @param driverOptions The data structure is obtained through parsing the compilation options. */ void SetNewPassManagerOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions); /** * @brief Set code obfuscation opt options. * * @param setOptionHandler The function to set composite option. * @param driverOptions The data structure is obtained through parsing the compilation options. */ void SetCodeObfuscationOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions); /** * @brief Set LTO opt options. * * @param setOptionHandler The function to set LTO lld options. * @param driverOptions The data structure is obtained through parsing the compilation options. */ void SetLTOOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions); /** * @brief Set optimization level opt options. * * @param setOptionHandler The function to set optimization level. * @param driverOptions The data structure is obtained through parsing the compilation options. */ void SetOptimizationLevelOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions); /** * @brief set opt options. * * @param setOptionHandler The function to set opt options. * @param driverOptions The data structure is obtained through parsing the compilation options. */ void SetOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions); /** * @brief Set verify opt options. * * @param setOptionHandler The function to set 'only-verify-out' option. * @param driverOptions The data structure is obtained through parsing the compilation options. */ void SetVerifyOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions); /** * @brief Set triple opt options. * * @param setOptionHandler The function to set 'mtriple' option. * @param driverOptions The data structure is obtained through parsing the compilation options. */ void SetTripleOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions); /** * @brief Set transparent opt options. * * @param setOptionHandler The function to set transparent options. * @param driverOptions The data structure is obtained through parsing the compilation options. */ void SetTransparentOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions); /** * @brief Set Pgo opt options. * * @param setOptionHandler The function to set Pgo options. * @param driverOptions The data structure is obtained through parsing the compilation options. */ void SetPgoOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions); } // namespace OPT namespace LLC { /** * @brief Set optimization level llc options. * * @param setOptionHandler The function to llc optimization level options. * @param driverOptions The data structure is obtained through parsing the compilation options. */ void SetOptimizationLevelOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions); /** * @brief Set llc options. * * @param setOptionHandler The function to set llc options. * @param driverOptions The data structure is obtained through parsing the compilation options. */ void SetOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions); /** * @brief Set triple llc options. * * @param setOptionHandler The function to set 'mtriple' option. * @param driverOptions The data structure is obtained through parsing the compilation options. */ void SetTripleOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions); /** * @brief Set transparent llc options. * * @param setOptionHandler The function to set args may contain multiple opt arguments separated by spaces. * @param driverOptions The data structure is obtained through parsing the compilation options. */ void SetTransparentOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions); } // namespace LLC namespace LLD { /** * @brief Set LTO optimization level lld options. * * @param setOptionHandler The function to set LTO lld options. * @param driverOptions The data structure is obtained through parsing the compilation options. */ void SetLTOOptimizationLevelOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions); /** * @brief Set LTO lld options. * * @param setOptionHandler The function to LTO lld options. * @param driverOptions The data structure is obtained through parsing the compilation options. */ void SetLTOOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions); /** * @brief Set Pgo lld options. * * @param setOptionHandler The function to. * @param driverOptions The data structure is obtained through parsing the compilation options. */ void SetPgoOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions); } // namespace LLD /** * @brief Call function object to set options. * * @param setOptionHandler The function to set options. * @param driverOptions The data structure is obtained through parsing the compilation options. * @param typeFunc a function object. */ void SetOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions, const ToolOptionType typeFunc); /** * @brief Call function objects to set options. * * @param setOptionHandler The function to set options. * @param driverOptions The data structure is obtained through parsing the compilation options. * @param typeFuncs a function object vector. */ void SetOptions( SetFuncType setOptionHandler, const DriverOptions& driverOptions, const std::vector& typeFuncs); } // namespace ToolOptions } // namespace Cangjie #endif // CANGJIE_DRIVER_TOOL_OPTIONS_H cangjie_compiler-1.0.7/include/cangjie/Driver/Toolchains/000077500000000000000000000000001510705540100233705ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/Driver/Toolchains/GCCPathScanner.h000066400000000000000000000066251510705540100262750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares GCCPath and GCCPathScanner. * Our toolchain uses some files under gcc library path to generate executable files. * We need to pass gcc library path to 'ld' command for linking these necessary .o files. * GCCPath stores gcc library path and gcc installation path. * GCCPathScanner automatically scans file system for an available gcc installation and return its path. * How GCCPathScanner scans depends on the triple target, i.e. cpu architecture, vendor and os. */ #ifndef CANGJIE_DRIVER_GCCPATHSCANNER_H #define CANGJIE_DRIVER_GCCPATHSCANNER_H #include #include #include #include #include "cangjie/Option/Option.h" namespace Cangjie { // Version: major.minor.build struct GCCVersion { public: uint8_t major; uint8_t minor; uint8_t build; /** * @brief Compare GCC versions. * * @return bool Return true If GCC version number is larger. */ bool operator<(const GCCVersion& gccVersion) const { if (major < gccVersion.major) { return true; } else if (major > gccVersion.major) { return false; } if (minor < gccVersion.minor) { return true; } else if (minor > gccVersion.minor) { return false; } return build < gccVersion.build; } }; struct GCCPath { public: std::string libPath; GCCVersion gccVersion; }; /** * GCCPathScanner searches some default paths in system for an available gcc installation. */ class GCCPathScanner { public: /** * @brief The constructor of class GCCPathScanner. * * @param tripleTarget The target format information. * @param files The crt files that need for linking. * @param searchPaths The default paths. * @return GCCPathScanner The instance of GCCPathScanner. */ GCCPathScanner(const Triple::Info& tripleTarget, const std::vector& files, const std::vector& searchPaths) : tripleTarget(tripleTarget), forFiles(files), searchPaths(searchPaths){}; /** * @brief The destructor of class GCCPathScanner. */ ~GCCPathScanner() = default; /** * @brief Scan some possible directories where gcc may be installed. * * @return std::optional The possible directories where gcc may be installed. */ std::optional Scan(); /** * @brief To convert the string to GCCVersion. * * @param versionStr The version string. * @return std::optional The optional GCC version. */ static std::optional StrToGCCVersion(const std::string& versionStr); private: const Triple::Info tripleTarget; const std::vector forFiles; std::vector searchPaths; static std::vector GetAllMatchingSubDirectories( const Triple::Info& target, const std::vector& directories); bool AllForFilesExist(const std::string& path); static bool IsCompatiableTripleName(const Triple::Info& info, const std::string& name); }; } // namespace Cangjie #endif // CANGJIE_DRIVER_GCCPATHSCANNER_H cangjie_compiler-1.0.7/include/cangjie/Driver/Toolchains/ToolChain.h000066400000000000000000000204631510705540100254260ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the ToolChain class. */ #ifndef CANGJIE_DRIVER_TOOLCHAIN_H #define CANGJIE_DRIVER_TOOLCHAIN_H #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include #endif #include "cangjie/Driver/Driver.h" #include "cangjie/Driver/DriverOptions.h" #include "cangjie/Driver/Tool.h" #include "cangjie/Option/Option.h" namespace Cangjie { class ToolChain { public: #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND /** * @brief The constructor of class ToolChain. * * @param driver It is the object that triggers the compiler's compilation process. * @param driverOptions The command line arguments input by the user. * @param backendCmds The generated backend commands. * @return ToolChain The instance of ToolChain. */ ToolChain(const Cangjie::Driver& driver, const DriverOptions& driverOptions, std::vector& backendCmds) : driver(driver), driverOptions(driverOptions), backendCmds(backendCmds) {}; #endif /** * @brief The destructor of class ToolChain. */ virtual ~ToolChain() {}; /** * @brief Initialize library paths. */ virtual void InitializeLibraryPaths() {}; /** * @brief Prepare dependency path. */ virtual bool PrepareDependencyPath() = 0; /** * @brief Process generation. * * @param input The generated object files. * @return bool Return true If success. */ virtual bool ProcessGeneration(std::vector& input) = 0; protected: const Cangjie::Driver& driver; const DriverOptions& driverOptions; std::vector& backendCmds; virtual std::string GetSharedLibraryExtension() const { return ".so"; } void CheckAndAddPathTo(std::vector& results, const std::string& path, std::string base = "") const { auto fullPath = FileUtil::JoinPath(base, path); if (FileUtil::FileExist(fullPath)) { results.emplace_back(fullPath); } } // @brief C Runtime library paths are system paths & some default paths under which we search for crt1.o, crti.o, // etc. Some default paths may not exist in some system, we add paths that exist only. void AddCRuntimeLibraryPath(const std::string& path) { CheckAndAddPathTo(cRuntimeLibraryPaths, path); } const std::vector& GetCRuntimeLibraryPath() const { return cRuntimeLibraryPaths; } // Library paths are from LIBRARY_PATH variable. A user may give a directory // that doesn't exist at all! Comparing to AddCRuntimeLibraryPath(...), we treat user given paths // as existing paths instead of filtering them silently. The user could find such problems easier // if all user-given paths are shown in the final command. void AddLibraryPath(const std::string& path) { if (!path.empty()) { libraryPaths.emplace_back(path); } } void AddLibraryPaths(const std::vector& paths) { libraryPaths.insert(libraryPaths.end(), paths.begin(), paths.end()); } std::vector GetLibraryPaths() { return libraryPaths; } std::string GetDynamicLinkerPath(const Triple::Info& tripleInfo) const { switch (tripleInfo.os) { case Triple::OSType::LINUX: if (tripleInfo.env == Triple::Environment::OHOS && tripleInfo.arch == Triple::ArchType::AARCH64) { return "/lib/ld-musl-aarch64.so.1"; } if (tripleInfo.env == Triple::Environment::OHOS && tripleInfo.arch == Triple::ArchType::ARM32) { return "/lib/ld-musl-arm.so.1"; } if (tripleInfo.env == Triple::Environment::OHOS && tripleInfo.arch == Triple::ArchType::X86_64) { return "/lib/ld-musl-x86_64.so.1"; } if (tripleInfo.env == Triple::Environment::ANDROID) { return "/system/bin/linker64"; } if (tripleInfo.arch == Triple::ArchType::X86_64) { return "/lib64/ld-linux-x86-64.so.2"; } if (tripleInfo.arch == Triple::ArchType::AARCH64) { return "/lib/ld-linux-aarch64.so.1"; } break; default: break; } return ""; } // Generate the link options of built-in libraries. void GenerateLinkOptionsOfBuiltinLibs(Tool& tool) const; // Generate the static link options of built-in libraries except 'std-ast'. // The 'std-ast' library is dynamically linked by default. virtual void GenerateLinkOptionsOfBuiltinLibsForStaticLink(Tool& tool) const; // Generate the dynamic link options of built-in libraries. virtual void GenerateLinkOptionsOfBuiltinLibsForDyLink(Tool& tool) const; // Traverse built-in libraries. void ForEachBuiltinDependencies(const std::unordered_set& builtinDependencies, const std::function& lambda) const { std::for_each(builtinDependencies.begin(), builtinDependencies.end(), lambda); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND const std::unordered_map> ALWAYS_DYNAMIC_LINK_STD_LIBRARIES = { {"libcangjie-std-ast.a", {"libcangjie-std-core.a", "libcangjie-std-collection.a", "libcangjie-std-sort.a", "libcangjie-std-math.a"}}}; #endif // Check other dependencies of 'staticLib' and emplace them in 'otherLibs'. void CheckOtherDependeniesOfStaticLib( const std::string& libName, std::set& dynamicLibraries, std::set& otherLibs) const; void AppendObjectsFromCompiled(Tool& tool, const std::vector& objFiles, std::vector>& inputOrderTuples); void AppendObjectsFromInput(std::vector>& inputOrderTuples); void AppendLibrariesFromInput(std::vector>& inputOrderTuples); void AppendLinkOptionFromInput(std::vector>& inputOrderTuples); void AppendLinkOptionsFromInput(std::vector>& inputOrderTuples); void SortInputlibraryFileAndAppend(Tool& tool, const std::vector& objFiles); TempFileInfo GetOutputFileInfo(const std::vector& objFiles) const; // Get the right cruntime lib folder name by giving arch. std::string GetArchFolderName(const Triple::ArchType& arch) const; // Only available for ELF or MachO targets. void GenerateRuntimePath(Tool& tool); // Make some guesses about library paths based on target and sysroot. virtual std::vector ComputeLibPaths() const; // Make some guesses about tool binary paths based on target and sysroot. virtual std::vector ComputeBinPaths() const; std::string FindToolPath(const std::string toolName, const std::vector& paths) const { return FileUtil::FindProgramByName(toolName, paths); }; template std::string FindToolPath( const std::string toolName, const std::vector& paths, Args&&... morePaths) const { std::string toolPath = FindToolPath(toolName, paths); if (!toolPath.empty()) { return toolPath; } return FindToolPath(toolName, morePaths...); }; std::string FindCangjieLLVMToolPath(const std::string& toolName) const; std::string FindUserToolPath(const std::string& toolName) const; virtual std::string GetClangRTProfileLibraryName() const { return "libclang_rt-profile.a"; } private: // cRuntimeLibraryPaths is used to search for C runtime object files, such as crt1.o, crti.o, crtn.o. std::vector cRuntimeLibraryPaths; // libraryPaths are obtained from LIBRARY_PATH env, which will be used to search for library files (.so/.a). std::vector libraryPaths; }; } // namespace Cangjie #endif // CANGJIE_DRIVER_TOOLCHAIN_H cangjie_compiler-1.0.7/include/cangjie/Driver/Tools.inc000066400000000000000000000024671510705540100230710ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file lists external binary tools to be used, which is included by headers. */ #ifdef TOOL #ifdef _WIN32 TOOL(CJC, FRONTEND, "cjc.exe") TOOL(CLANG, BACKEND, "clang.exe") TOOL(OPT, BACKEND, "opt.exe") TOOL(LLC, BACKEND, "llc.exe") TOOL(LLD, OTHER, "ld.lld.exe") TOOL(LD64_LLD, OTHER, "ld64.lld.exe") TOOL(LD, BACKEND, "ld.exe") TOOL(AR, OTHER, "ar.exe") TOOL(LLVM_AR, OTHER, "llvm-ar.exe") TOOL(OBJCOPY, OTHER, "objcopy.exe") TOOL(LLVM_OBJCOPY, OTHER, "llvm-objcopy.exe") TOOL(CMD, OTHER, "cmd.exe") TOOL(DSYMUTIL, OTHER, "dsymutil.exe") TOOL(CODESIGN, OTHER, "codesign.exe") TOOL(STRIP, BACKEND, "strip.exe") #else TOOL(CJC, FRONTEND, "cjc") TOOL(CLANG, BACKEND, "clang") TOOL(OPT, BACKEND, "opt") TOOL(LLC, BACKEND, "llc") TOOL(LD, BACKEND, "ld") TOOL(LLD, OTHER, "ld.lld") TOOL(LD64_LLD, OTHER, "ld64.lld") TOOL(AR, OTHER, "ar") TOOL(LLVM_AR, OTHER, "llvm-ar") TOOL(OBJCOPY, OTHER, "objcopy") TOOL(LLVM_OBJCOPY, OTHER, "llvm-objcopy") TOOL(DSYMUTIL, OTHER, "dsymutil") TOOL(CODESIGN, OTHER, "codesign") TOOL(STRIP, BACKEND, "strip") #endif #endif cangjie_compiler-1.0.7/include/cangjie/Driver/Utils.h000066400000000000000000000035531510705540100225440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares some utility functions. */ #ifndef CANGJIE_DRIVER_UTILS_H #define CANGJIE_DRIVER_UTILS_H #include #include #include namespace Cangjie { /** * @brief Get the input string quoted with single quotes. * Note: Single quotes in the input are transform to '\'' instead of \'. * * @param str The input string. * @return std::string The single quoted string. */ std::string GetSingleQuoted(const std::string& str); /** * @brief Get the input string quoted for passing as a command line argument. * - In the case of Linux, the argument is quoted with single quotes. Nested single quotes are * transformed to '\''. * - In the case of Windows, the argument is quoted with double quotes. Nested double quotes and * backslashes are escaped by \. * * @param arg The input string. * @return std::string The quoted argument. */ std::string GetCommandLineArgumentQuoted(const std::string& arg); /** * @brief Prepend to paths. * * @param prefix The path prefix to be added. * @param paths The path vector. * @param quoted Determine whether the path string add single quotes. * @return std::vector The vector of paths with a prefix added. */ std::vector PrependToPaths( const std::string& prefix, const std::vector& paths, bool quoted = false); /** * @brief Get darwin SDK version. * * @param sdkPath The sdk path. * @return std::optional The sdk version info. */ std::optional GetDarwinSDKVersion(const std::string& sdkPath); } // namespace Cangjie #endif // CANGJIE_DRIVER_UTILS_H cangjie_compiler-1.0.7/include/cangjie/Frontend/000077500000000000000000000000001510705540100216115ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/Frontend/CompileStrategy.h000077500000000000000000000047021510705540100251030ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the CompileStrategy related classes, which provide real compile ablility. */ #ifndef CANGJIE_FRONTEND_COMPILESTRATEGY_H #define CANGJIE_FRONTEND_COMPILESTRATEGY_H #include #include #include #include namespace Cangjie { class CompilerInstance; class TypeChecker; /** * Strategy type. */ enum class StrategyType { DEFAULT, /**< Default compile strategy. */ FULL_COMPILE, /**< Full compile strategy. */ INCREMENTAL_COMPILE, /**< Increamental compile strategy. */ }; /** * Compile Strategy use different compilation unit to compile. */ class CompileStrategy { public: explicit CompileStrategy(CompilerInstance* ci) : ci(ci) { } virtual ~CompileStrategy() { } virtual bool Parse() = 0; bool ConditionCompile() const; void DesugarAfterSema() const; bool ImportPackages() const; bool MacroExpand() const; virtual bool Sema() = 0; bool OverflowStrategy() const; StrategyType type{StrategyType::DEFAULT}; protected: /** * Desugar Syntactic sugar. */ void PerformDesugar() const; /** * Do TypeCheck and Generic Instantiation. */ void TypeCheck() const; CompilerInstance* ci = nullptr; // A collection of file ids, used to determine whether the id is conflicted. std::unordered_set fileIds; private: /** * @brief Do merge custom annotations from '.cj.d' file to ast tree. * @details If the cjd file is in the same directory as the cjo file and the file name is the same as it, * this function will do parse and macroexpand for it. * @note This function will modify the ast tree in compiler instance. */ void ParseAndMergeCjds() const; }; /** * Full Compile Strategy will compile the whole module. */ class FullCompileStrategy : public CompileStrategy { public: explicit FullCompileStrategy(CompilerInstance* ci); ~FullCompileStrategy(); bool Parse() override; bool Sema() override; private: friend class FullCompileStrategyImpl; class FullCompileStrategyImpl* impl; }; } // namespace Cangjie #endif // CANGJIE_FRONTEND_COMPILESTRATEGY_H cangjie_compiler-1.0.7/include/cangjie/Frontend/CompilerInstance.h000066400000000000000000000374041510705540100252310ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the CompilerInstance, which holds all compile context. */ #ifndef CANGJIE_FRONTEND_COMPILERINSTANCE_H #define CANGJIE_FRONTEND_COMPILERINSTANCE_H #include #include #include #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Node.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/CHIR/Analysis/AnalysisWrapper.h" #include "cangjie/CHIR/Analysis/ConstAnalysis.h" #include "cangjie/CHIR/Analysis/TypeAnalysis.h" #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/Frontend/CompileStrategy.h" #include "cangjie/Frontend/CompilerInvocation.h" #include "cangjie/IncrementalCompilation/IncrementalScopeAnalysis.h" #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "cangjie/MetaTransformation/MetaTransform.h" #endif #include "cangjie/Modules/ImportManager.h" namespace Cangjie { using HANDLE = void*; class GenericInstantiationManager; class MacroExpansion; class TypeChecker; class PackageManager; class BaseMangler; class TestManager; /** * Compile util to some stage. */ enum class CompileStage { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND LOAD_PLUGINS, /**< Load compiler plugins. */ #endif PARSE, /**< Parse the source code. */ CONDITION_COMPILE, /**< Conditional compile. */ IMPORT_PACKAGE, /**< Import packages. */ MACRO_EXPAND, /**< Expand macros. */ AST_DIFF, /**< Diff the AST to get incremental compilation scope. */ SEMA, /**< TypeCheck. */ DESUGAR_AFTER_SEMA, GENERIC_INSTANTIATION, /**< GenericInstantiation. */ OVERFLOW_STRATEGY, /**< Overflow strategy. */ MANGLING, /**< Mangling all decls. */ CHIR, /**< CHIR. */ CODEGEN, /**< Generate target code. */ SAVE_RESULTS, /**< Save AST and CHIR results to files. */ COMPILE_STAGE_NUMBER /**< The number of compile stage */ }; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND class CHIRData { public: void InitData(std::unordered_map* fileNameMap, size_t threadNum); CHIR::CHIRContext& GetCHIRContext(); void AppendNewPackage(CHIR::Package* package); std::vector GetAllCHIRPackages() const; CHIR::Package* GetCurrentCHIRPackage() const; void SetImplicitFuncs(const std::unordered_map& funcs); std::unordered_map GetImplicitFuncs() const; void SetConstVarInitFuncs(const std::vector& funcs); std::vector GetConstVarInitFuncs() const; CHIR::AnalysisWrapper& GetConstAnalysisResultRef(); const CHIR::AnalysisWrapper& GetConstAnalysisResult() const; private: CHIR::CHIRContext cctx; std::vector chirPkgs; // used by codegen std::unordered_map implicitFuncs; // used by interpreter std::vector initFuncsForConstVar; // only for AnalysisWrapper CHIR::CHIRBuilder builder{cctx, 0}; // provide the capability and results of constant analysis, used by cjlint CHIR::AnalysisWrapper constAnalysisWrapper{builder}; }; #endif /** * The base compiler instance, holds the data which has full compile lifetime. */ class CompilerInstance { friend class CompileStrategy; friend class MacroExpansion; friend class MacroEvaluation; friend class TypeChecker; friend class GenericInstantiationManager; friend class FullCompileStrategy; friend class IncrementalCompileStrategy; public: CompilerInstance(CompilerInvocation& invocation, DiagnosticEngine& diag); virtual ~CompilerInstance(); /** * Set different CompileStrategy to do the real compile jobs. */ void SetCompileStrategy(CompileStrategy* newCompileStrategy) { delete this->compileStrategy; this->compileStrategy = newCompileStrategy; } /** * Initialize the compilation flow by * 1) check pre-conditions * 2) set the action functions in compilation flow */ virtual bool InitCompilerInstance(); /** * Perform compile to some @p stage. */ virtual bool Compile(CompileStage stage = CompileStage::CHIR); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool PerformPluginLoad(); #endif /** * Perform parse. */ virtual bool PerformParse(); virtual Ptr GetFileByPath(const std::string& filePath) { (void)filePath; return nullptr; } /** * Perform conditonal compilation. */ virtual bool PerformConditionCompile(); /** * Perform import package. */ virtual bool PerformImportPackage(); /** * Perform macro expand. */ virtual bool PerformMacroExpand(); /** * Perform AST diff to get incremental compilation scope. */ virtual bool PerformIncrementalScopeAnalysis(); bool CalculateASTCache(); /** * Perform typecheck and other semantic check jobs. */ virtual bool PerformSema(); /** * Perform desugar after sema. */ virtual bool PerformDesugarAfterSema(); /** * Perform generic instantiation. */ virtual bool PerformGenericInstantiation(); /** * Perform overflow strategy. */ virtual bool PerformOverflowStrategy(); /** * Perform Mangle. */ virtual bool PerformMangling(); bool GenerateCHIRForPkg(AST::Package& pkg); virtual bool PerformCHIRCompilation(); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::unordered_map& GetFileNameMap() { return fileNameMap; } #endif /** * Dump the parsed tokens for debugging. */ virtual bool DumpTokens(); /** * Dump the symbols for debugging. */ virtual void DumpSymbols(); /** * Dump the expanded macro for debugging. */ virtual void DumpMacro(); /** * Codegen to target code. */ virtual bool PerformCodeGen() { return true; } /** * Export AST and generate cjo, bchir. */ virtual bool PerformCjoAndBchirSaving() { return true; } /** * Get raw ptrs of all Packages after Compile. * * @return empty vector If Parse failed. */ std::vector> GetPackages() { return pkgs; } /** * Get raw ptrs of source Packages after Compile. * * @return empty vector If Parse failed. */ std::vector> GetSourcePackages() const; /** * Get Source Manager. */ SourceManager& GetSourceManager(); /** * Get ASTContext by Package. * * @param pkg Package pointer. * @return ASTContext of the @p pkg, if not found, return nullptr. */ ASTContext* GetASTContextByPackage(Ptr pkg) const; /** * Get dependent package of current package. * Information is written in json format. */ const std::vector& GetDepPkgInfo() { return depPackageInfo; } /** * Import all packages from other libs. */ bool ImportPackages(); /** * Get ExtendDecl Set from primitive type or Decl. * @param type can be primitive type or Decl. * @return set of Ptr, if not found, return empty set. */ std::set> GetExtendDecls( const std::variant, Ptr>& type) const; /** * Obtains the extended members visible to the current file through the type declaration. * @param type can be primitive type or Decl. * @return vector of Ptr, if not found, return empty set. */ std::vector> GetAllVisibleExtendMembers( const std::variant, Ptr>& type, const AST::File& curFile) const; /** * Get the candidate decls or types of given @p expr in given @p scopeName from sema cache @p ctx. * If the @p hasLocal is true, the target will be found from local scope firstly. * NOTE: used by lsp. * @param ctx cached sema context. * @param scopeName the scopeName of current position. * @param expr the expression waiting to found candidate decls or types. * @param hasLocalDecl whether the given expression is existed in the given scope. * @return found candidate decls or types. */ Candidate GetGivenReferenceTarget( ASTContext& ctx, const std::string& scopeName, AST::Expr& expr, bool hasLocalDecl) const; bool UpdateAndWriteCachedInfoToDisk(); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND /** * Record the handle for compiler plugin */ void AddPluginHandle(HANDLE handle) { (void)pluginHandles.emplace_back(handle); } /** * Unload the handles for all compiler plugins */ bool UnloadPluginHandle() { metaTransformPluginBuilder = {}; // plugins should be unloaded after metaTransformPluginBuilder deconstruction. for (auto& handle : pluginHandles) { if (InvokeRuntime::CloseSymbolTable(handle) != 0) { Errorln("close plugin dynamic library failed."); return false; } } pluginHandles.clear(); return true; } #endif /** * Infomation written to cached file and needed by incremental compiling */ CompilationCache cachedInfo; /** * Infomation generated in CHIR stage */ struct CHIRInfo { OptEffectStrMap optEffectMap; std::unordered_set> ccTys; VirtualWrapperDepMap curVirtFuncWrapDep; VirtualWrapperDepMap delVirtFuncWrapForIncr; std::set ccOutFuncsRawMangle; VarInitDepMap varInitDepMap; }; CHIRInfo chirInfo; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND MetaTransformPluginBuilder metaTransformPluginBuilder; #endif /** * CompilerInvocation, storing anything external the Instance needs. */ CompilerInvocation& invocation; /** * The compiler home path, can be set or detect from executable path. */ std::string cangjieHome; /** * The standard modules path, with os/target/backend information. */ std::string cangjieModules; /** * Input source file paths. */ std::vector srcFilePaths; /** * Input source file directories. */ std::unordered_set srcDirs; /** * Collect all diagnostic information during compilation, then print to stderr. */ DiagnosticEngine& diag; /** * Determine the compile order. */ PackageManager* packageManager = nullptr; /** * TypeManager hold all types. */ TypeManager* typeManager = nullptr; /** * ImportManager hold all import related information for TypeCheck. */ ImportManager importManager; /** * TestManager provides test specific functionality which should be reflected in the compiler logic. */ TestManager* testManager = nullptr; /** * GenericInstantiationManager hold generic infos. */ GenericInstantiationManager* gim = nullptr; // Compile one Package from source files. bool compileOnePackageFromSrcFiles = false; // Read source code from cache. bool loadSrcFilesFromCache = false; // the source code cache map use for LSP. Key is path, Value is source code. std::unordered_map bufferCache; // tokensEvalInMacro for DumpMacro, error report. std::vector tokensEvalInMacro; /** * The current processing package, used in modular compilation. */ Ptr currentPkg{nullptr}; /** * @brief Get the CHIRContext */ CHIR::CHIRContext& GetCHIRContext(); /** * @brief Get astPkg2chirPkgMap */ const std::unordered_map, Ptr>& GetASTPkg2CHIRPkgMap() { return astPkg2chirPkgMap; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // used only by cjlint const CHIR::AnalysisWrapper& GetConstAnalysisWrapper() const; std::vector GetAllCHIRPackages() const; #else // used only by cjlint const CHIR::AnalysisWrapper& GetConstAnalysisWrapper() const { return constAnalysisWrapper; } std::vector GetAllCHIRPackages() const { std::vector res; for (auto& it : astPkg2chirPkgMap) { res.emplace_back(it.second); } return res; } #endif /** * @brief mangler */ std::unique_ptr mangler; /** * Map between the mangle name (before sema) with the Decl. */ std::map> mangledName2DeclMap; // Every compiler instance has a source manager. SourceManager sm; /// Incremental compilation state. IncreKind kind{IncreKind::INVALID}; ///@{ /// Overriden by CJLint bool releaseCHIRMemory = true; bool needToOptString = false; bool needToOptGenericDecl = false; bool isCJLint = false; ///@} /************** Used by Incremental Compilation Start **************/ RawMangled2DeclMap rawMangleName2DeclMap; ASTCache astCacheInfo; std::unordered_set> declsWithDuplicatedRawMangleName; std::unordered_map, int>>> directExtends; FileMap fileMap; std::vector order; /// Used by CJLint void SetBuildTrie(bool flag) { buildTrie = flag; } /************** Used by Incremental Compilation End **************/ bool DeserializeCHIR(); std::vector> srcPkgs; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND CHIRData chirData; // we need to remove this later std::unordered_map, Ptr> astPkg2chirPkgMap; #endif protected: /** * Perform functions map according to different CompileStage. */ std::unordered_map> performMap; CompileStrategy* compileStrategy = nullptr; TypeChecker* typeChecker = nullptr; // Modularize compilation. bool ModularizeCompilation(); // Build Trie tree or not; bool buildTrie = true; std::unordered_map fileNameMap; // Merge source packages and imported packages, also init ASTContext here. void MergePackages(); // Allowing only add source package once. Used for LSPCompilerInstance. void AddSourceToMember(); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void ManglingHelpFunction(const BaseMangler& baseMangler); #endif void CacheCompileArgs(); void CacheSemaUsage(SemanticInfo&& info); void UpdateMangleNameForCachedInfo(); private: // Guess the CANGJIE_HOME. bool DetectCangjieHome(); // Guess the modules file path. bool DetectCangjieModules(); // Merged source packages and imported packages. std::vector> pkgs; // Package to ASTContext map. std::unordered_map, std::unique_ptr> pkgCtxMap; std::vector depPackageInfo; virtual void UpdateCachedInfo(); bool WriteCachedInfo(); bool ShouldWriteCacheFile() const; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::vector pluginHandles; #endif }; } // namespace Cangjie #endif // CANGJIE_FRONTEND_COMPILERINSTANCE_H cangjie_compiler-1.0.7/include/cangjie/Frontend/CompilerInvocation.h000066400000000000000000000027621510705540100255750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the CompilerInvocation, which parses the arguments. */ #ifndef CANGJIE_FRONTEND_COMPILERINVOCATION_H #define CANGJIE_FRONTEND_COMPILERINVOCATION_H #include #include #include #include "cangjie/Frontend/FrontendOptions.h" #include "cangjie/Option/Option.h" #include "cangjie/Option/OptionTable.h" #include "cangjie/Macro/InvokeUtil.h" namespace Cangjie { /** * All configuration for the compiler, options of all stages of translation. */ class CompilerInvocation { public: CompilerInvocation() { optionTable = CreateOptionTable(true); argList = std::make_unique(); } ~CompilerInvocation() = default; /** * Parse raw string arguments for compiler invocation. * @param args String representation of arguments. */ bool ParseArgs(const std::vector& args); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::string GetRuntimeLibPath(const std::string& relativePath = "../runtime/lib"); #endif GlobalOptions globalOptions; FrontendOptions frontendOptions; std::unique_ptr optionTable = nullptr; std::unique_ptr argList = nullptr; }; } #endif // CANGJIE_FRONTEND_COMPILERINVOCATION_H cangjie_compiler-1.0.7/include/cangjie/Frontend/FrontendObserver.h000066400000000000000000000026061510705540100252550ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_FRONTEND_FRONTENDOBSERVER_H #define CANGJIE_FRONTEND_FRONTENDOBSERVER_H #include #include #include "cangjie/Frontend/CompilerInstance.h" namespace Cangjie { /// A simple observer of frontend actions. class FrontendObserver { public: FrontendObserver() = default; virtual ~FrontendObserver() = default; /// An event, triggerd when the frontend has parsed AST. virtual void ParsedAST(CompilerInstance& instance) = 0; }; /// List of FrontendObserver. class MultiFrontendObserver : public FrontendObserver { public: /// Make a clone of \ref observer and register it. void Add(FrontendObserver* observer) { observers.push_back(std::unique_ptr(observer)); } /// An event, triggerd when the frontend has parsed AST. void ParsedAST(CompilerInstance& instance) override { std::for_each(observers.begin(), observers.end(), [&instance](const auto& observer) { observer->ParsedAST(instance); }); }; private: std::vector> observers; }; } // namespace Cangjie #endif // CANGJIE_FRONTEND_FRONTENDOBSERVER_H cangjie_compiler-1.0.7/include/cangjie/Frontend/FrontendOptions.h000066400000000000000000000032201510705540100251120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the FrontendOptions, which parses frontend arguments. */ #ifndef CANGJIE_FRONTEND_FRONTENDOPTIONS_H #define CANGJIE_FRONTEND_FRONTENDOPTIONS_H #include #include "cangjie/Option/Option.h" namespace Cangjie { class FrontendOptions : public GlobalOptions { public: virtual ~FrontendOptions() { } enum class DumpAction { NO_ACTION, /**< No specific action. */ DUMP_TOKENS, /**< Dump tokens. */ DUMP_PARSE, /**< Dump ast. */ DUMP_AST, /**< Dump ast after semantic check. */ DUMP_SYMBOLS, /**< Dump symbols after semantic check. */ DUMP_IR, /**< Dump readable ir. */ DUMP_BC, /**< Dump machine-readable bitcode */ TYPE_CHECK, /**< Parse ast and do typecheck. */ DUMP_MACRO, /**< Dump evaluated tokens in macro expansion. */ DUMP_DEP_PKG, /**< Dump dependent packages of current package. */ DESERIALIZE_CHIR, /**< Deserialize Chir. */ }; FrontendOptions() : dumpAction(DumpAction::NO_ACTION) {} /** * Indicates the dump action the user requested that the frontend execute. */ DumpAction dumpAction = DumpAction::NO_ACTION; protected: virtual std::optional ParseOption(OptionArgInstance& arg) override; }; } // namespace Cangjie #endif // CANGJIE_FRONTEND_FRONTENDOPTIONS_H cangjie_compiler-1.0.7/include/cangjie/FrontendTool/000077500000000000000000000000001510705540100224475ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/FrontendTool/CjdCompilerInstance.h000066400000000000000000000034541510705540100265060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the CjdCompilerInstance, which performs the default compile flow. */ #ifndef CANGJIE_FRONTEND_CJDCOMPILERINSTANCE_H #define CANGJIE_FRONTEND_CJDCOMPILERINSTANCE_H #include "cangjie/FrontendTool/DefaultCompilerInstance.h" namespace Cangjie { /// Compiler instance that compiles .cj.d file. This ci skips all stages after sema, except that it still produces a /// cjo file in CjoAndBchirSaving. class CjdCompilerInstance : public DefaultCompilerInstance { public: CjdCompilerInstance(CompilerInvocation& invocation, DiagnosticEngine& diag) : DefaultCompilerInstance(invocation, diag) { buildTrie = false; } ~CjdCompilerInstance() override = default; ///@{ /// After sema, skip all stages until cjo saving bool PerformDesugarAfterSema() override { return true; } bool PerformGenericInstantiation() override { return true; } bool PerformOverflowStrategy() override { return true; } // use DefaultCompilerInstance::PerformMangling bool PerformCHIRCompilation() override { return true; } bool PerformCodeGen() override { return true; } bool PerformCjoAndBchirSaving() override { Utils::ProfileRecorder recorder("Main Stage", "Save cjo"); bool ret = true; for (auto& srcPkg : GetSourcePackages()) { ret = ret && SaveCjo(*srcPkg); } return ret; } ///@} }; } // namespace Cangjie #endif // CANGJIE_FRONTEND_CJDCOMPILERINSTANCE_H cangjie_compiler-1.0.7/include/cangjie/FrontendTool/DefaultCompilerInstance.h000066400000000000000000000032631510705540100273700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the DefaultCompilerInstance, which performs the default compile flow. */ #ifndef CANGJIE_FRONTEND_DEFAULTCOMPILERINSTANCE_H #define CANGJIE_FRONTEND_DEFAULTCOMPILERINSTANCE_H #include "cangjie/Frontend/CompilerInstance.h" namespace Cangjie { namespace CodeGen { class CGModule; } class DefaultCompilerInstance : public CompilerInstance { public: DefaultCompilerInstance(CompilerInvocation& invocation, DiagnosticEngine& diag); ~DefaultCompilerInstance() override; bool PerformParse() override; bool PerformImportPackage() override; bool PerformConditionCompile() override; bool PerformMacroExpand() override; bool PerformSema() override; bool PerformOverflowStrategy() override; bool PerformDesugarAfterSema() override; bool PerformGenericInstantiation() override; bool PerformCHIRCompilation() override; bool PerformCodeGen() override; bool PerformCjoAndBchirSaving() override; bool PerformMangling() override; void DumpDepPackage(); void DumpIR() const; void DumpBC() const; protected: bool SaveCjoAndBchir(AST::Package& pkg) const; bool SaveCjo(const AST::Package& pkg) const; void RearrangeImportedPackageDependence() const; bool CodegenOnePackage(AST::Package& pkg, bool enableIncrement) const; private: class DefaultCIImpl* impl; }; } // namespace Cangjie #endif // CANGJIE_FRONTEND_DEFAULTCOMPILERINSTANCE_H cangjie_compiler-1.0.7/include/cangjie/FrontendTool/FrontendTool.h000066400000000000000000000017551510705540100252450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares frontend execute apis. */ #ifndef CANGJIE_FRONTENDTOOL_FRONTENDTOOL_H #define CANGJIE_FRONTENDTOOL_FRONTENDTOOL_H #include #include #include "cangjie/Frontend/FrontendObserver.h" namespace Cangjie { class Driver; /** * Execute frontend. */ int ExecuteFrontend(const std::string& exePath, const std::vector& args, const std::unordered_map& environmentVars); /** * Execute frontend by compiler invocation. */ bool ExecuteFrontendByDriver(DefaultCompilerInstance& instance, const Driver& driver); bool NeedCreateIncrementalCompilerInstance(const GlobalOptions& opts); } // namespace Cangjie #endif // CANGJIE_FRONTENDTOOL_FRONTENDTOOL_H cangjie_compiler-1.0.7/include/cangjie/FrontendTool/IncrementalCompilerInstance.h000066400000000000000000000045101510705540100302410ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the IncrementalCompilerInstance, which performs the incremental compile flow. */ #ifndef CANGJIE_FRONTEND_INCREMENTALCOMPILERINSTANCE_H #define CANGJIE_FRONTEND_INCREMENTALCOMPILERINSTANCE_H #include "cangjie/FrontendTool/DefaultCompilerInstance.h" #include "cangjie/IncrementalCompilation/CachedMangleMap.h" #include "cangjie/IncrementalCompilation/IncrementalScopeAnalysis.h" #include "cangjie/IncrementalCompilation/IncrementalCompilationLogger.h" #include "cangjie/Parse/ASTHasher.h" namespace Cangjie { class IncrementalCompilerInstance : public DefaultCompilerInstance { public: IncrementalCompilerInstance(CompilerInvocation& invocation, DiagnosticEngine& diag) : DefaultCompilerInstance(invocation, diag) { } ~IncrementalCompilerInstance() override; bool InitCompilerInstance() override; bool PerformIncrementalScopeAnalysis() override; bool PerformSema() override; bool PerformOverflowStrategy() override; bool PerformDesugarAfterSema() override; bool PerformGenericInstantiation() override; bool PerformMangling() override; bool PerformCHIRCompilation() override; bool PerformCodeGen() override; virtual bool PerformCjoAndBchirSaving() override; CachedMangleMap cacheMangles; // Store the struct types who's layout has been changed and needs to be regenerated its IR. // NOTE: since class layout is generated in separate step, we don not need to collect them. // And enum types have fixed layout for non-class or same as class type generation, it also can be ignored. std::unordered_set> changedTypes; private: std::unordered_set> declsToBeReCompiled; // stored mangled name before sema, generated by ASTDiff std::list rawIncrRemovedDecls; void SaveCompilationResult(Cangjie::IncreResult&& result); void LoadCachedCodegenResult() const; void UpdateCachedInfo() override; void UpdateCHIROptEffectMap(); }; } // namespace Cangjie #endif // CANGJIE_FRONTEND_INCREMENTALCOMPILERINSTANCE_H cangjie_compiler-1.0.7/include/cangjie/IncrementalCompilation/000077500000000000000000000000001510705540100244725ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/IncrementalCompilation/ASTCacheCalculator.h000066400000000000000000000033121510705540100302270ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_AST_CACHE_CALCULATOR_H #define CANGJIE_AST_CACHE_CALCULATOR_H #include "cangjie/AST/Node.h" #include "cangjie/IncrementalCompilation/CompilationCache.h" namespace Cangjie::IncrementalCompilation { /// a class that computes the information necessary to be kept for further incremental compilation, and also /// data that may later be used to analyse changed decls since last compilation. class ASTCacheCalculator { public: ASTCacheCalculator(const AST::Package& p, const std::pair& srcInfo); ~ASTCacheCalculator(); void Walk() const; RawMangled2DeclMap mangled2Decl{}; // RawMangledName -> Ptr map ASTCache ret{}; std::unordered_set> duplicatedMangleNames{}; // decls with duplicate RawMangledName // store direct extends temporarily: direct extends with the same extended type and constraints are the same, // collect them while traversing the ast, compute their cache after extracting all direct extends with the // same RawMangledName std::unordered_map, int>>> directExtends; std::vector order; // the order of global decls by which decls are written to the cache. // order of members need not record as they are recorded in the MemberCache struct FileMap fileMap; private: class ASTCacheCalculatorImpl* impl; }; } // namespace Cangjie::IncrementalCompilation #endif cangjie_compiler-1.0.7/include/cangjie/IncrementalCompilation/CachedMangleMap.h000066400000000000000000000055141510705540100276010ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_INCREMENTAL_COMPILATION_CACHED_MANGLE_MAP #define CANGJIE_INCREMENTAL_COMPILATION_CACHED_MANGLE_MAP #include #include #include "cangjie/AST/Node.h" #include "cangjie/IncrementalCompilation/IncrementalCompilationLogger.h" /// Describes which decls are new or removed compared to last compilation. The decls are recorded by their mangled /// names. struct CachedMangleMap { // Stored mangledName of decls which need be removed from IR. // NOTE: mangled names should be CodeGen recognizable. std::unordered_set incrRemovedDecls; // imported inline decls, to be set external and non-dso_local std::unordered_set importedInlineDecls; std::unordered_set newExternalDecls; void EmplaceImportedInlineDeclPtr(const Cangjie::AST::Decl &decl) { importedInlineDeclsPtr.emplace(&decl); }; void Clear() { incrRemovedDecls.clear(); importedInlineDecls.clear(); newExternalDecls.clear(); importedInlineDeclsPtr.clear(); } void UpdateImportedInlineDeclsMangle() { importedInlineDecls.clear(); for (const auto& p : importedInlineDeclsPtr) { CJC_NULLPTR_CHECK(p); if (p) { importedInlineDecls.emplace(p->mangledName); } } } void Dump() const { auto& logger = IncrementalCompilationLogger::GetInstance(); if (!logger.IsEnable()) { return; } if (incrRemovedDecls.empty() && importedInlineDecls.empty() && newExternalDecls.empty()) { logger.LogLn("[CachedMangleMap] empty"); return; } logger.LogLn("[CachedMangleMap] START"); if (!incrRemovedDecls.empty()) { logger.LogLn("[incrRemovedDecls]:"); for (auto incrRemovedDecl : incrRemovedDecls) { logger.LogLn(incrRemovedDecl); } } if (!importedInlineDecls.empty()) { logger.LogLn("[importedInlineDecls]:"); for (auto importedInlineDecl : importedInlineDecls) { logger.LogLn(importedInlineDecl); } } if (!newExternalDecls.empty()) { logger.LogLn("[newExternalDecls]:"); for (auto newExternalDecl : newExternalDecls) { logger.LogLn(newExternalDecl); } } logger.LogLn("[CachedMangleMap] END"); } private: std::unordered_set importedInlineDeclsPtr; }; #endif // CANGJIE_INCREMENTAL_COMPILATION_CACHED_MANGLE_MAPcangjie_compiler-1.0.7/include/cangjie/IncrementalCompilation/CompilationCache.h000066400000000000000000000151311510705540100300460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_COMPILATION_CACHE_H #define CANGJIE_COMPILATION_CACHE_H #include #include #include #include #include #include "cangjie/AST/Node.h" namespace Cangjie { using RawMangledName = std::string; using RawMangled2DeclMap = std::unordered_map>; // A map to record the CHIR optimizations effects on incremental compilation, where the key // is the raw mangle name of the source decl, and the value is the set of the raw mangle name // of the polluted decls using OptEffectStrMap = std::unordered_map>; // no need to have the type defined here using OptEffectNodeMap = std::unordered_map, std::unordered_set>>; using SrcImportedDepMap = std::unordered_map, std::set>>; // record the raw mangle to it's virual func wrapper mangled name map using VirtualWrapperDepMap = std::unordered_map; // record the raw mangle to it's var init func mangled name map using VarInitDepMap = std::unordered_map; /********************* Compilation Cache In Parser *******************/ /// Describes the file and decl index of a decl. Used to analyse if the relative order among top leve or member decls /// are changed since last compilation. struct GlobalVarIndex { std::string file; int id; bool operator<(const GlobalVarIndex& other) const { return file == other.file && id < other.id; } }; [[maybe_unused]] std::ostream& operator<<(std::ostream& out, const GlobalVarIndex& id); /// Base class necessary to describe all info of a decl struct DeclCacheBase { size_t sigHash{0}; // the API of a decl (e.g. name of named parameter) size_t srcUse{0}; // the ABI (e.g. foreign, @Annotation) and source usage of a decl (e.g. public) size_t bodyHash{0}; // the body hash of a decl that has no impact on API (e.g. @Overflow, line no) // for type decl, bodyHash records accessibility and constraints uint8_t astKind{0}; // is global var or varwithpattern (excluding var in varwithpattern), or is static var, used in gvid // property can have member function decl bool isGV{false}; GlobalVarIndex gvid{"", 0}; std::vector members; std::string cgMangle; // mangle names for codegen }; struct MemberDeclCache : DeclCacheBase { RawMangledName rawMangle; }; struct TopLevelDeclCache : DeclCacheBase { std::vector extends; size_t instVarHash{0}; size_t virtHash{0}; // order of virtual member decls }; using ASTCache = std::unordered_map; /*********************************************************************/ /********************* Compilation Cache In Sema *********************/ /* * Definitions: * name usages: * api usage (any usage of a type decl): * rawMangledName: A in let a: A * qualifier: pkg1.pkg2.pkg3 in let b: pkg1.pkg2.pkg3.A * name usage: * unqualified usage (not right to any . operator): * f in f() * a in return a * qualified usage (right to . operator): * decl member accessing usage (left is an type object or typeName): * a in obj.a * package qualified usage (left is a package name): * p1.p2 in p1.p2.f * f in p2.f */ // Approximately records the usage of a name of a class/package decl struct NameUsage { std::set parentDecls; // RawMangledName of used parent type decl std::set packageQualifiers; // Eg: for 'p1.p2.A', identifier is 'A', qualifier is 'p1.p2'. // only top level non-type decls may have this kind of usage bool hasUnqualifiedUsage{false}; bool hasUnqualifiedUsageOfImported{false}; }; struct UseInfo { std::set usedDecls; std::map usedNames; }; struct SemaUsage { UseInfo apiUsages; UseInfo bodyUsages; std::set boxedTypes; }; struct SemaRelation { std::set inherits; std::set extends; std::set extendedInterfaces; }; struct SemanticInfo { // Record what decls and expressions are used in toplevel/member's. Do not care about removed decl's internal usage. // map 'toplevel/member-> usage'. std::unordered_map, SemaUsage> usages; // Record 'type->(inherits, extends, extend interfaces). std::unordered_map relations; // Record for 'builtin type -> (extends, extend interfaces)'. std::unordered_map builtInTypeRelations; // Record for user defined decl -> compiler_add_decl_mangle. std::unordered_map> compilerAddedUsages; }; /*********************************************************************/ // decls groupby file orderby gvid using FileMap = std::unordered_map>; // the gvid is written in the Decl using CachedFileMap = std::unordered_map>; // the gvid value is not needed as they are sorted /// All cache info of an instance of incremental compilation. Some are to be stored for further compilations, /// and some are used for further analysis of this compilation. struct CompilationCache { uint64_t specs = 0; uint64_t lambdaCounter = 0; uint64_t stringLiteralCounter = 0; uint64_t envClassCounter = 0; std::vector compileArgs; std::vector, std::vector>>> varAndFuncDep; OptEffectStrMap chirOptInfo; VirtualWrapperDepMap virtualFuncDep; VarInitDepMap varInitDepMap; std::set ccOutFuncs; // raw mangled name of gloable or member funcs had closure convert in chir SemanticInfo semaInfo; ASTCache curPkgASTCache; CachedFileMap fileMap; ASTCache importedASTCache; std::vector bitcodeFilesName; }; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool WriteCache(const struct ::Cangjie::AST::Package& pkg, CompilationCache&& cachedInfo, std::vector&& order, const std::string& path); #endif } // namespace Cangjie #endif // CANGJIE_COMPILATION_CACHE_H cangjie_compiler-1.0.7/include/cangjie/IncrementalCompilation/IncrementalCompilationLogger.h000066400000000000000000000061771510705540100324560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_INCREMENTAL_COMPILATION_LOGER_H #define CANGJIE_INCREMENTAL_COMPILATION_LOGER_H #include #include #include #include #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/ConstantsUtils.h" /// Log states of incremental compilation into log file class IncrementalCompilationLogger { public: static IncrementalCompilationLogger& GetInstance() { static IncrementalCompilationLogger logger = IncrementalCompilationLogger(); return logger; } void SetDebugPrint(bool flag) { debugPrint = flag; } void InitLogFile(const std::string& logFilePath) { if (logFilePath.empty()) { return; } if (!Cangjie::FileUtil::HasExtension(logFilePath, "log")) { return; } auto realDirPath = Cangjie::FileUtil::GetAbsPath(Cangjie::FileUtil::GetDirPath(logFilePath)); if (!realDirPath.has_value()) { return; } auto fileNameWithExt = Cangjie::FileUtil::GetFileName(logFilePath); auto ret = Cangjie::FileUtil::JoinPath(realDirPath.value(), fileNameWithExt); fileStream.open(ret, std::ofstream::out); if (fileStream.is_open()) { writeKind = WriteKind::FILE; saveLogFile = true; } } void LogLn(const std::string& input) { if (debugPrint) { std::cout << input << std::endl; } if (writeKind == WriteKind::FILE) { fileStream << input << std::endl; } else { strStream << input << std::endl; } } void Log(const std::string& input) { if (debugPrint) { std::cout << input; } if (writeKind == WriteKind::FILE) { fileStream << input; } else { strStream << input; } } bool IsEnable() const { return debugPrint || saveLogFile; } enum class WriteKind : uint8_t { BUFF, FILE }; void SetWriteKind(WriteKind kind) { writeKind = kind; } void WriteBuffToFile() { if (writeKind == WriteKind::FILE) { if (fileStream.is_open()) { fileStream << strStream.str(); writeKind = WriteKind::FILE; } } } private: IncrementalCompilationLogger() {} ~IncrementalCompilationLogger() noexcept { #ifndef CANGJIE_ENABLE_GCOV try { #endif if (fileStream.is_open()) { fileStream.close(); } #ifndef CANGJIE_ENABLE_GCOV } catch (const std::exception& e) { // do nothing for noexcept } catch (...) { // do nothing for noexcept } #endif } bool debugPrint{false}; bool saveLogFile{false}; std::ostringstream strStream; std::ofstream fileStream; WriteKind writeKind{WriteKind::BUFF}; }; #endifcangjie_compiler-1.0.7/include/cangjie/IncrementalCompilation/IncrementalScopeAnalysis.h000066400000000000000000000032431510705540100316040ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_INCRE_SCOPE_ANALYSIS_H #define CANGJIE_INCRE_SCOPE_ANALYSIS_H #include #include #include #include #include #include "cangjie/AST/Node.h" #include "cangjie/IncrementalCompilation/CompilationCache.h" #include "cangjie/Modules/ImportManager.h" namespace Cangjie { enum class IncreKind { NO_CHANGE, INCR, ROLLBACK, EMPTY_PKG, INVALID }; struct IncreResult { IncreKind kind; std::unordered_set> declsToRecompile; std::list deleted; std::list deletedMangleNames; // deleted mangle names for codegen CompilationCache cacheInfo; RawMangled2DeclMap mangle2decl; std::list reBoxedTypes; void Dump() const; }; // A helper struct to organize the args of incremental scope analysis entry function struct IncrementalScopeAnalysisArgs { const RawMangled2DeclMap rawMangleName2DeclMap; ASTCache&& astCacheInfo; const AST::Package& srcPackage; const GlobalOptions& op; const ImportManager& importer; const CompilationCache& cachedInfo; const FileMap& fileMap; std::unordered_map, int>>>&& directExtends; }; // Entry function of incremental scope analysis IncreResult IncrementalScopeAnalysis(IncrementalScopeAnalysisArgs&& args); } // namespace Cangjie #endif // CANGJIE_INCRE_SCOPE_ANALYSIS_H cangjie_compiler-1.0.7/include/cangjie/IncrementalCompilation/Utils.h000066400000000000000000000062351510705540100257510ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CACHE_DATA_UTILS_H #define CANGJIE_CACHE_DATA_UTILS_H #include "cangjie/AST/Node.h" #include "cangjie/AST/Walker.h" #include "cangjie/IncrementalCompilation/IncrementalScopeAnalysis.h" #include "cangjie/Parse/ASTHasher.h" #include "cangjie/Utils/SipHash.h" namespace Cangjie::IncrementalCompilation { void PrintDecl(std::ostream& out, const AST::Decl& decl); bool IsVirtual(const AST::Decl& decl); inline bool IsImported(const AST::Decl& decl) { return decl.TestAttr(AST::Attribute::IMPORTED); } inline bool IsInstance(const AST::Decl& decl) { return !decl.TestAttr(AST::Attribute::STATIC) && !decl.TestAttr(AST::Attribute::CONSTRUCTOR); } bool IsTyped(const AST::Decl& decl); inline bool IsUntyped(const AST::Decl& decl) { return !IsTyped(decl); } inline bool IsEnumConstructor(const AST::Decl& decl) { return decl.TestAttr(AST::Attribute::ENUM_CONSTRUCTOR); } std::vector> GetMembers(const AST::Decl& decl); // returns true if this decl is possibly affected by decl order, either by use-after-check or by side effects bool IsOOEAffectedDecl(const AST::Decl& decl); // convert unsorted container to sorted, used commonly in incremental compilation to maintain or discard the order template ().cbegin())> std::vector> ToSorted(const Cont& cont, Cmp&& cmp) { std::vector> ans(cont.size()); size_t i{0}; for (auto& elem: cont) { ans[i++] = &elem; } std::stable_sort(ans.begin(), ans.end(), [&cmp](auto a, auto b) { return cmp(*a, *b); }); return ans; } template ().cbegin())> std::vector> ToSorted(const Cont& cont) { std::vector> ans(cont.size()); size_t i{0}; for (auto& elem: cont) { ans[i++] = &elem; } std::stable_sort(ans.begin(), ans.end(), [](auto a, auto b) { return *a < *b; }); return ans; } // no default cmp function for container of pointers, because numeric values of pointers are never consistent // always pass a compare function template ().cbegin())> std::vector> ToSortedPointers(const Cont& cont, Cmp&& cmp) { std::vector> ans(cont.size()); size_t i{0}; for (auto elem: cont) { ans[i++] = elem; } std::stable_sort(ans.begin(), ans.end(), [&cmp](auto a, auto b) { return cmp(a, b); }); return ans; } // ordered set is not sorted, return a reference to itself template const auto& ToSorted(const std::set& cont) { return cont; } template const auto& ToSorted(const std::map& cont) { return cont; } std::string TrimPackagePath(const std::string& path); std::string GetTrimmedPath(AST::File* file); } // namespace Cangjie::IncrementalCompilation #endif cangjie_compiler-1.0.7/include/cangjie/InvokeCJ/000077500000000000000000000000001510705540100215025ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/InvokeCJ/invoke_api.h000066400000000000000000000177351510705540100240140ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file This file declares the APIs for C Invoke CJ. * @author Zapanov Rinchin */ #ifndef CJ_INVOKE_API_H #define CJ_INVOKE_API_H #include "stdint.h" /** * Type that represents error codes. The programmer can quickly check the * return value of the API call to determine if a result in expected value range. */ typedef int32_t CJErrorCode; /** * Runtime identifier. */ typedef int32_t CangjieRT; /** * Type that represents a @c CJ function. */ typedef void* CJFunction; /** * Type that represents a asynchronously invoked function. */ typedef int64_t CJThreadHandle; const CJErrorCode CJ_OK = 0; const CJErrorCode CJ_FAILED_WITH_EXCEPTION = 1; const CJErrorCode CJ_HANDLE_ALREADY_RELEASED = 2; const CJErrorCode CJ_TASK_IS_RUNNING = 3; const CJErrorCode CJ_ILLEGAL_VALUE = -1; const CJErrorCode CJ_NO_MEM = -2; const CJErrorCode CJ_INTERNAL_ERROR = -3; /** * This function should be called once before any other interaction with CJ. * If some customs signal handlers were set in C code (for example via * [sigaction](https://man7.org/linux/man-pages/man2/sigaction.2.html)) * it will be saved during call of this function, and new, RT-specific handlers will be used. * * @param rt pointer to the location where the resulting `CangjieRT` will be * placed, which is `INVALID_RT` on failure. * @param params an array of string arguments to initialize CJ Runtime. Should be zero-terminated. * * @return CJ_OK on success; otherwise, returns a suitable error code on failure. */ CJErrorCode initCangjieRuntime(CangjieRT* rt, char* params[]); /** * Function that should be called when no more interaction with Cangjie code is needed. * All resources consumed by runtime are freed after that. If custom signal handlers * were saved during call of `initCangjieRuntime`, it will be restored. * * @param rt the Cangjie RT that will be destroyed. * * @return CJ_OK on success; otherwise, returns a suitable error code on failure. */ CJErrorCode destroyCangjieRuntime(CangjieRT rt); /** * Finds public Cangjie `@C` function with the given name in the specified package. * Signature should not be specified here as `@C` functions can't be overloaded in Cangjie. * * @param functionLoc pointer to the location where the resulting `CJFunction` will be placed, * which is `INVALID_CJ_FUNCTION` on failure. * @param rt the Cangjie RT instance. * @param packageName a package name. * @param funcName a function name. * * @return CJ_OK on success; otherwise, returns a suitable error code on failure. */ CJErrorCode findCangjieFunction(CJFunction* functionLoc, CangjieRT rt, char* packageName, char* funcName); /** * Synchronously invoke specified function. * * @param retLoc - pointer to the location where to put result. * @param exceptionDescriptionLoc pointer to the location where resulting exception description * will be stored.If pointer is `NULL` the description will not be prepared. * @param rt `CangjieRT` instance that was created with `initCangjieRuntime` function. * @param func Cangjie function found by `findCangjieFunction`. * @param varargs also, this function takes varargs, so, all needed by Cangjie function arguments could be passed there. * * @return On normal executions the value returned could be one of the following: * * ```c * CJ_OK, * CJ_FAILED_WITH_EXCEPTION, * ``` * * If invocation of Cangjie function was successful, the status is set to `OK`, result is stored * in the location pointed by `retLoc`, and `exception_description` is `NULL`. * * However, if there was an **exception**: the status is set to `FAILED_WITH_EXCEPTION` and * nothing is written into `retLoc`. If `exceptionDescriptionLoc` is not `NULL` the C string * with description of exception is allocated: it contains name of exception, line number and * stack trace. The address of this string is written into `exceptionDescriptionLoc`. * * Otherwise, return a suitable error code on unexpected failure. * * @note this is an expensive operation and should be used with care. * * @note the C thread that calls Cangjie function doesn't become a fiber, it is still a native * thread. It means, that it will not be preempted during the execution of Cangjie code and this * thread will not start execution other tasks from fibers queue. That's why such synchronous * calls of Cangjie code should be done with caution. */ CJErrorCode invokeCJFunction(void* retLoc, char** exceptionDescriptionLoc, CangjieRT rt, CJFunction func, ...); /** * Asynchronously invoke specified function. Received handle could be used for result acquisition. * This call is not blocking, it immediately returns when task is scheduled. * * @param handleLoc pointer to the location where to put resulting handle. * @param rt `CangjieRT` instance that was created with `initCangjieRuntime` function. * @param func Cangjie function found by `findCangjieFunction`. * @param varargs Also, this function takes varargs, so, all needed by Cangjie function arguments could be passed there. * * @return `CJ_OK` on success; otherwise, returns a suitable error code on failure. */ CJErrorCode runCJFunctionAsync(CJThreadHandle* handleLoc, CangjieRT rt, CJFunction func, ...); /** * Get the result of asynchrounsly invoked function. * Call of such function is blocking, if execution is still in progress, the caller will wait till it ends; * This function is very similar to `invokeCJFunction` function from synchronous part. * * @param retLoc address of the location, where to store the result, or `NULL` in case of `void` functions; * @param exceptionDescriptionLoc pointer to the location where resulting exception description will be stored. * If pointer is `NULL` the description will not be prepared. * @param rt `CangjieRT` instance that was created with `initCangjieRuntime` function. * @param handle the handle of the asynchronous invoked function. * * @return On normal executions the value returned could be one of the following: * * ```c * CJ_OK, * CJ_FAILED_WITH_EXCEPTION, * CJ_HANDLE_ALREADY_RELEASED, * ``` * * If invocation of Cangjie function was successful, the status is set to `OK`, result is stored in * the location pointed by `retLoc`, and `exception_description` is `NULL`. * * However, if there was an **exception**: the status is set to `FAILED_WITH_EXCEPTION` and nothing * is written into `retLoc`. If `exceptionDescriptionLoc` is not `NULL` the C string with * description of exception is allocated: it contains name of exception, line number and stack * trace. The address of this string is written into `exceptionDescriptionLoc`. * * If accessed handle was already released to the moment when `getTaskResult` is called, * `CJ_HANDLE_ALREADY_RELEASED` is returned. * * Otherwise, return a suitable error code on unexpected failure. */ CJErrorCode getTaskResult(void* retLoc, char** exceptionDescriptionLoc, CangjieRT rt, CJThreadHandle handle); /** * Releases `handle` to reclaim memory that used to store the task result. If task is still running * `releaseCJThreadHandle` will instantly return, without reclaiming any underlying memory. * * @param rt `CangjieRT` instance that was created with `initCangjieRuntime` function. * @param handle the instance of `CJThreadHandle` that should be released. * * @return `CJ_OK` on success; `CJ_TASK_IS_RUNNING` if the task is still running; otherwise, * returns a suitable error code on failure. */ CJErrorCode releaseCJThreadHandle(CangjieRT rt, CJThreadHandle handle); #endif // CJ_INVOKE_API_H cangjie_compiler-1.0.7/include/cangjie/Lex/000077500000000000000000000000001510705540100205625ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/Lex/AnnotationToken.h000066400000000000000000000014701510705540100240500ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Annotation Token related classes, which is set of lexcial annotation tokens of Cangjie. */ #ifndef CANGJIE_LEX_ANNOTATION_TOKEN_H #define CANGJIE_LEX_ANNOTATION_TOKEN_H #include #include #include "cangjie/Basic/Position.h" namespace Cangjie { inline const char* ANNOTATION_TOKENS[] = { #define ANNOTATION_TOKEN(ID, VALUE, LITERAL, INVALID_PRECEDENCE) LITERAL, #include "cangjie/Lex/AnnotationTokens.inc" #undef ANNOTATION_TOKEN }; } // namespace Cangjie #endif // CANGJIE_LEX_ANNOTATION_TOKEN_H cangjie_compiler-1.0.7/include/cangjie/Lex/AnnotationTokens.inc000066400000000000000000000014511510705540100245540ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. // ANNOTATION_TOKEN(token id, token value for human reading, token literal, token priority) #ifdef ANNOTATION_TOKEN ANNOTATION_TOKEN(EXCEPT, "except", "except", 0) ANNOTATION_TOKEN(INCLUDE, "include", "include", 0) ANNOTATION_TOKEN(PRIMAL, "primal", "primal", 0) ANNOTATION_TOKEN(STAGE, "stage", "stage", 0) ANNOTATION_TOKEN(MODE, "mode", "mode", 0) ANNOTATION_TOKEN(FORWARD, "forward", "forward", 0) ANNOTATION_TOKEN(BACKWARD, "backward", "backward", 0) ANNOTATION_TOKEN(DUAL, "dual", "dual", 0) ANNOTATION_TOKEN(ILLEGAL, "illegal", "", 0) #endif cangjie_compiler-1.0.7/include/cangjie/Lex/Lexer.h000066400000000000000000000136211510705540100220150ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Lexer, which performs lexical analysis of the source code. */ #ifndef CANGJIE_LEX_LEXER_H #define CANGJIE_LEX_LEXER_H #include #include #include #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Lex/Token.h" namespace Cangjie { const uint8_t BYTE_X_FLAG = 0b10000000; // 0x80 const uint8_t BYTE_2_FLAG = 0b11000000; // 0xc0 const uint8_t BYTE_3_FLAG = 0b11100000; // 0xe0 const uint8_t BYTE_4_FLAG = 0b11110000; // 0xf0 const uint8_t BYTE_5_FLAG = 0b11111000; // 0xf8 const uint8_t BYTE_6_FLAG = 0b11111100; // 0xfc const uint8_t LOW_6_BIT_MASK = 0b00111111; const uint8_t LOW_5_BIT_MASK = 0b00011111; const uint8_t LOW_4_BIT_MASK = 0b00001111; const uint8_t LOW_3_BIT_MASK = 0b00000111; const uint8_t LOW_2_BIT_MASK = 0b00000011; const uint8_t LOW_1_BIT_MASK = 0b00000001; const uint32_t UTF8_1_MAX = 0x7F; const uint32_t UTF8_2_MAX = 0x07FF; const uint32_t UTF8_3_MAX = 0xFFFF; const uint32_t UTF8_4_MAX = 0x10FFFF; const uint32_t UTF8_5_MAX = 0x3FFFFFF; const uint32_t UTF8_6_MAX = 0x7FFFFFFF; const uint8_t LEFT_SHIFT_30 = 30; const uint8_t LEFT_SHIFT_24 = 24; const uint8_t LEFT_SHIFT_18 = 18; const uint8_t LEFT_SHIFT_12 = 12; const uint8_t LEFT_SHIFT_6 = 6; const int32_t BYTE_4_BASE = 65536; const int32_t BYTE_3_BASE = 2048; const uint8_t BYTE_2_BASE = 128; const uint8_t BYTE_1_STEP = 1; const uint8_t BYTE_2_STEP = 2; const uint8_t BYTE_3_STEP = 3; const uint8_t BYTE_4_STEP = 4; const uint8_t BYTE_5_STEP = 5; const uint8_t BYTE_6_STEP = 6; const int32_t ERROR_UTF8 = -2; // The illegal ascii char stands for error unicode. const uint8_t BYTE_3_INDEX = 2; const uint8_t BYTE_4_INDEX = 3; const uint8_t BYTE_5_INDEX = 4; const uint8_t BYTE_6_INDEX = 5; const uint8_t UNICODE_MAX_NUM = 8; const uint8_t BIN_BASE = 2; const uint8_t OCT_BASE = 8; const uint8_t DEC_BASE = 10; const uint8_t HEX_BASE = 16; /// Get a list of contextual keyword. const std::vector& GetContextualKeyword(); bool IsContextualKeyword(std::string_view s); class Lexer { public: /// Create Lexer. /// \param cts whether to enableCollectTokenStream Lexer(unsigned int fileID, const std::string& input, DiagnosticEngine& diag, SourceManager& sm, bool cts = false); /// \param pos the location of the first character of input. This is typically called in parsing interpolation /// string. Lexer(unsigned int fileID, const std::string& input, DiagnosticEngine& diag, SourceManager& sm, const Position& pos); /// \param splitAmbi /// This is called by libast Lexer(const std::string& input, DiagnosticEngine& diag, SourceManager& sm, bool cts = false, bool splitAmbi = true); Lexer(const std::string& input, DiagnosticEngine& diag, SourceManager& sm, const Position& pos, bool cts = false); Lexer(const std::string& input, DiagnosticEngine& diag, Source& s, const Position& pos, bool cts = false); /// Create Lexer with \ref inputTokens from successful macro func call. Lexer(const std::vector& inputTokens, DiagnosticEngine& diag, SourceManager& sm, bool cts = false); ~Lexer(); // FrontendTool/SourceManager/Parse/Macro/stdlib/unittests. /// read and return next token Token Next(); // Parse/unittests. /// Read and return the next \ref num tokens. If there are less than \ref num tokens left, all are returned. /// otherwise, comments are omitted. const std::list& LookAhead(size_t num); /// Returns true if the next token is any of the TokenKind's described by range \ref begin and \ref end. /// \param skipNewline whether to ignore NL bool Seeing(const std::vector::const_iterator& begin, const std::vector::const_iterator& end, bool skipNewline = false); /// Returns true if the next token is any of \ref kinds. /// \param skipNewline whether to ignore NL bool Seeing(const std::vector& kinds, bool skipNewline = false); // Parse. // Parse/unittests. /// Read and return the next \ref num tokens. If there are less than \ref num tokens left, all are returned. /// otherwise, comments and NL's are omitted. std::list LookAheadSkipNL(size_t num); /// Return all comments collected, and clear the comment cache std::vector GetComments(); const std::vector& GetTokenStream() const; /// Cache the current lookahead and read pointer. void SetResetPoint(); /// Restore to the previous state when \ref SetResetPoint is called. void Reset(); /// get the first position of all the tokens of this Lexer. const Position& GetPosBase() const; unsigned GetFileID() const; void ClearStringParts(const Token& t); /** * @brief Enable lexing of EH keywords * * @param enabled Whether to enable or disable EH tokens lexing */ void SetEHEnabled(bool enabled) const; // Parse/Macro. const std::vector& GetStrParts(const Token& t); /// Read all tokens until the END token, excluded. After this, the Lexer cannot yield any more token. std::vector GetTokens(); std::vector GetCollectTokens() const; bool StartCollectTokens(); void StopCollectTokens(bool bStart); std::size_t GetCurrentToken() const; private: class LexerImpl* impl; }; /// Escape a Cangjie multiline interpolation string by prefixing each double quote with a backslash. Returns a valid /// cangjie source that can be lexed into a multiline interpolation string. /// \param value string literal value of a cangjie multinline interpolation string. std::string ProcessQuotaMarks(const std::string& value, bool processSingle = false); }; // namespace Cangjie #endif // CANGJIE_LEX_LEXER_H cangjie_compiler-1.0.7/include/cangjie/Lex/Token.h000066400000000000000000000130211510705540100220100ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Token related classes, which is set of lexcial tokens of Cangjie. */ #ifndef CANGJIE_LEX_TOKEN_H #define CANGJIE_LEX_TOKEN_H #include #include #include #include #include #include "cangjie/Basic/Position.h" #include "cangjie/Utils/CheckUtils.h" namespace Cangjie { constexpr uint8_t NUM_TOKENS = 200; enum class TokenKind : unsigned char { #define TOKEN(ID, VALUE, LITERAL, PRECEDENCE) ID, #include "cangjie/Lex/Tokens.inc" #undef TOKEN }; inline const char* TOKEN_KIND_VALUES[] = { #define TOKEN(ID, VALUE, LITERAL, PRECEDENCE) VALUE, #include "cangjie/Lex/Tokens.inc" #undef TOKEN }; inline const char* TOKENS[] = { #define TOKEN(ID, VALUE, LITERAL, PRECEDENCE) LITERAL, #include "cangjie/Lex/Tokens.inc" #undef TOKEN }; /** * @brief Check if a given token is experimental * * @param token The `const char*` representation of the token to check * @return bool Whether `token` is experimental */ inline bool IsExperimental(const char* token) { #define TOKEN(ID, VALUE, LITERAL, PRECEDENCE) #define EXPERIMENTAL_TOKEN(ID, VALUE, LITERAL, PRECEDENCE) \ if (strcmp(token, LITERAL) == 0) { \ return true; \ } #include "cangjie/Lex/Tokens.inc" #undef TOKEN #undef EXPERIMENTAL_TOKEN return false; } inline const uint8_t TOKEN_TO_OPERATOR_PRECEDENCE[NUM_TOKENS] = { #define TOKEN(ID, VALUE, LITERAL, PRECEDENCE) PRECEDENCE, #include "cangjie/Lex/Tokens.inc" #undef TOKEN }; /** * Get the length of some TokenKind. */ inline int Len(TokenKind tokenKind) { return static_cast(strlen(TOKENS[static_cast(tokenKind)])); } /** * A larger value ("tighter precedence") means an operator is grouped more closely with * its operands; for example `a + b * c` means `a + (b * c)` because multiplication * has precedence 15, which is larger/tighter than addition's 14. * * The `INVALID_PRECEDENCE` constant is a special case; 0 means "this is not an * operator whose precedence you asked about" -- it does not mean "loosest possible * precedence". Tokens such as `break` and `;` have this precedence. */ static const uint8_t INVALID_PRECEDENCE = 0; struct Token { TokenKind kind; // read-only accessor unsigned int delimiterNum = 1; // Delimiter '#' number for raw string. bool isSingleQuote{false}; // Quotations of string-related literals can be single or double. bool commentForMacroDebug{false}; // added in compiler macro process const Position& Begin() const { return begin; } const Position& End() const; explicit Token(TokenKind kind) : kind(kind) {} Token(TokenKind kind, std::string value) : kind(kind) { SetValue(std::move(value)); } /// \param be begin position /// \param en end position /// \param value value of Token. for identifiers, this value is after canonical recompose Token(TokenKind kind, std::string value, const Position& be, const Position& en, bool cmtForMacDebug = false) : kind(kind), commentForMacroDebug(cmtForMacDebug) { SetValuePos(std::move(value), be, en); } Token(const Token& other) = default; Token(Token&& other) = default; Token& operator=(const Token& other) = default; Token& operator=(Token&& other) = default; bool operator<(const Token& ct) const { return Begin() < ct.Begin(); } bool operator==(const Token& ct) const { return Begin() == ct.Begin(); } bool IsBlockComment() { return kind == TokenKind::COMMENT && v.rfind("/*", 0) != std::string::npos; } const std::string& Value() const { return v; } /// Sets the string value of the token. /// WARNING: Typically the begin and end position need to be set with the string value simultaneously. Only call /// this when you shall set the position somewhere later or the position does not matter. void SetValue(std::string s) { v = std::move(s); } void SetValuePos(std::string s, const Position& be, const Position& en) { SetValue(std::move(s)); begin = be; end = en; } size_t Length() const { CJC_ASSERT(begin.fileID == end.fileID); CJC_ASSERT(begin.line == end.line); return static_cast(static_cast(end.column - begin.column)); } void SetCurFile(bool isCurFile) { begin.isCurFile = end.isCurFile = isCurFile; } bool operator==(std::string_view other) const { return v == other; } bool operator!=(std::string_view other) const { return !(*this == other); } private: std::string v; Position begin{INVALID_POSITION}; Position end{INVALID_POSITION}; }; /** * Split string literal into string content and string expression. */ struct StringPart { enum StrKind { STR, EXPR } strKind; std::string value; Position begin; Position end; StringPart(StrKind strKind, const std::string& value, const Position& begin, const Position& end) { this->strKind = strKind; this->value = value; this->begin = begin; this->end = end; } }; using TokenVecMap = std::unordered_map>; const std::vector& GetEscapeTokenKinds(); } // namespace Cangjie #endif // CANGJIE_LEX_TOKEN_H cangjie_compiler-1.0.7/include/cangjie/Lex/Tokens.inc000066400000000000000000000151221510705540100225210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. // TOKEN(token id, token value for human reading, token literal, token priority) // Note that stdlib/ast/AnyKind.cj should be modified at the same time that this file has been changed. #ifdef TOKEN #ifndef EXPERIMENTAL_TOKEN #define EXPERIMENTAL_TOKEN TOKEN #define EXPERIMENTAL_TOKEN_DEFAULT #endif TOKEN(DOT, "dot", ".", 0) TOKEN(COMMA, "comma", ",", 0) TOKEN(LPAREN, "l_paren", "(", 0) TOKEN(RPAREN, "r_paren", ")", 0) TOKEN(LSQUARE, "l_square", "[", 0) TOKEN(RSQUARE, "r_square", "]", 0) TOKEN(LCURL, "l_curl", "{", 0) TOKEN(RCURL, "r_curl", "}", 0) TOKEN(EXP, "exp", "**", 16) TOKEN(MUL, "mul", "*", 15) TOKEN(MOD, "mod", "%", 15) TOKEN(DIV, "div", "/", 15) TOKEN(ADD, "add", "+", 14) TOKEN(SUB, "sub", "-", 14) TOKEN(INCR, "incr", "++", 0) TOKEN(DECR, "decr", "--", 0) TOKEN(AND, "and", "&&", 5) TOKEN(OR, "or", "||", 3) TOKEN(COALESCING, "coalescing", "??", 2) TOKEN(PIPELINE, "pipeline", "|>", 1) TOKEN(COMPOSITION, "composition", "~>", 1) TOKEN(NOT, "not", "!", 0) TOKEN(BITAND, "bit_and", "&", 8) TOKEN(BITOR, "bit_or", "|", 6) TOKEN(BITXOR, "bit_xor", "^", 7) TOKEN(BITNOT, "bit_not", "~", 0) TOKEN(LSHIFT, "lshift", "<<", 13) TOKEN(RSHIFT, "rshift", ">>", 13) TOKEN(COLON, "colon", ":", 0) TOKEN(SEMI, "semi", ";", 0) TOKEN(ASSIGN, "assign", "=", 0) TOKEN(ADD_ASSIGN, "add_assign", "+=", 0) TOKEN(SUB_ASSIGN, "sub_assign", "-=", 0) TOKEN(MUL_ASSIGN, "mul_assign", "*=", 0) TOKEN(EXP_ASSIGN, "exp_assign", "**=", 0) TOKEN(DIV_ASSIGN, "div_assign", "/=", 0) TOKEN(MOD_ASSIGN, "mod_assign", "%=", 0) TOKEN(AND_ASSIGN, "and_assign", "&&=", 0) TOKEN(OR_ASSIGN, "or_assign", "||=", 0) TOKEN(BITAND_ASSIGN, "bit_and_assign", "&=", 0) TOKEN(BITOR_ASSIGN, "bit_or_assign", "|=", 0) TOKEN(BITXOR_ASSIGN, "bit_xor_assign", "^=", 0) TOKEN(LSHIFT_ASSIGN, "lshift_assign", "<<=", 0) TOKEN(RSHIFT_ASSIGN, "rshift_assign", ">>=", 0) TOKEN(ARROW, "arrow", "->", 0) TOKEN(BACKARROW, "backarrow", "<-", 0) TOKEN(DOUBLE_ARROW, "double_arrow", "=>", 0) TOKEN(RANGEOP, "range_op", "..", 11) TOKEN(CLOSEDRANGEOP, "closed_range_op", "..=", 11) TOKEN(ELLIPSIS, "ellipsis", "...", 0) TOKEN(HASH, "hash", "#", 0) TOKEN(AT, "at", "@", 0) TOKEN(QUEST, "quest", "?", 1) TOKEN(LT, "less", "<", 10) TOKEN(GT, "greater", ">", 10) TOKEN(LE, "less_equal", "<=", 10) TOKEN(GE, "greater_equal", ">=", 10) TOKEN(IS, "is", "is", 10) TOKEN(AS, "as", "as", 10) TOKEN(NOTEQ, "not_equal", "!=", 9) TOKEN(EQUAL, "equal", "==", 9) TOKEN(WILDCARD, "wildcard", "_", 0) TOKEN(INT8, "Int8", "Int8", 0) TOKEN(INT16, "Int16", "Int16", 0) TOKEN(INT32, "Int32", "Int32", 0) TOKEN(INT64, "Int64", "Int64", 0) TOKEN(INTNATIVE, "IntNative", "IntNative", 0) TOKEN(UINT8, "UInt8", "UInt8", 0) TOKEN(UINT16, "UInt16", "UInt16", 0) TOKEN(UINT32, "UInt32", "UInt32", 0) TOKEN(UINT64, "UInt64", "UInt64", 0) TOKEN(UINTNATIVE, "UIntNative", "UIntNative", 0) TOKEN(FLOAT16, "Float16", "Float16", 0) TOKEN(FLOAT32, "Float32", "Float32", 0) TOKEN(FLOAT64, "Float64", "Float64", 0) TOKEN(RUNE, "Rune", "Rune", 0) TOKEN(BOOLEAN, "Bool", "Bool", 0) TOKEN(NOTHING, "Nothing", "Nothing", 0) TOKEN(UNIT, "Unit", "Unit", 0) TOKEN(STRUCT, "struct", "struct", 0) TOKEN(ENUM, "enum", "enum", 0) TOKEN(VARRAY, "VArray", "VArray", 0) TOKEN(THISTYPE, "This", "This", 0) TOKEN(PACKAGE, "package", "package", 0) TOKEN(IMPORT, "import", "import", 0) TOKEN(CLASS, "class", "class", 0) TOKEN(INTERFACE, "interface", "interface", 0) TOKEN(FUNC, "func", "func", 0) TOKEN(MACRO, "macro", "macro", 0) TOKEN(QUOTE, "quote", "quote", 0) TOKEN(DOLLAR, "dollar", "$", 0) TOKEN(LET, "let", "let", 0) TOKEN(VAR, "var", "var", 0) TOKEN(CONST, "const", "const", 0) TOKEN(TYPE, "type", "type", 0) TOKEN(INIT, "init", "init", 0) TOKEN(THIS, "this", "this", 0) TOKEN(SUPER, "super", "super", 0) TOKEN(IF, "if", "if", 0) TOKEN(ELSE, "else", "else", 0) TOKEN(CASE, "case", "case", 0) TOKEN(TRY, "try", "try", 0) TOKEN(CATCH, "catch", "catch", 0) TOKEN(FINALLY, "finally", "finally", 0) TOKEN(FOR, "for", "for", 0) TOKEN(DO, "do", "do", 0) TOKEN(WHILE, "while", "while", 0) TOKEN(THROW, "throw", "throw", 0) TOKEN(RETURN, "return", "return", 0) TOKEN(CONTINUE, "continue", "continue", 0) TOKEN(BREAK, "break", "break", 0) TOKEN(IN, "in", "in", 0) TOKEN(NOT_IN, "not_in", "!in", 0) TOKEN(MATCH, "match", "match", 0) TOKEN(WHERE, "where", "where", 0) TOKEN(EXTEND, "extend", "extend", 0) TOKEN(WITH, "with", "with", 0) TOKEN(PROP, "prop", "prop", 0) TOKEN(STATIC, "static", "static", 0) TOKEN(PUBLIC, "public", "public", 0) TOKEN(PRIVATE, "private", "private", 0) TOKEN(INTERNAL, "internal", "internal", 0) TOKEN(PROTECTED, "protected", "protected", 0) TOKEN(OVERRIDE, "override", "override", 0) TOKEN(REDEF, "redef", "redef", 0) TOKEN(ABSTRACT, "abstract", "abstract", 0) TOKEN(SEALED, "sealed", "sealed", 0) TOKEN(OPEN, "open", "open", 0) TOKEN(FOREIGN, "foreign", "foreign", 0) TOKEN(INOUT, "inout", "inout", 0) TOKEN(MUT, "mut", "mut", 0) TOKEN(UNSAFE, "unsafe", "unsafe", 0) TOKEN(OPERATOR, "operator", "operator", 0) TOKEN(SPAWN, "spawn", "spawn", 0) TOKEN(SYNCHRONIZED, "synchronized", "synchronized", 0) TOKEN(UPPERBOUND, "upperbound", "<:", 0) TOKEN(MAIN, "main", "main", 0) TOKEN(IDENTIFIER, "identifier", "", 0) TOKEN(PACKAGE_IDENTIFIER, "package_identifier", "", 0) TOKEN(INTEGER_LITERAL, "integer_literal", "", 0) TOKEN(RUNE_BYTE_LITERAL, "rune_byte_literal", "", 0) TOKEN(FLOAT_LITERAL, "float_literal", "", 0) TOKEN(COMMENT, "comment", "", 0) TOKEN(NL, "newline", "", 0) TOKEN(END, "end", "", 0) TOKEN(SENTINEL, "sentinel", "", 0) TOKEN(RUNE_LITERAL, "char_literal", "", 0) TOKEN(STRING_LITERAL, "string_literal", "", 0) TOKEN(JSTRING_LITERAL, "jstring_literal", "", 0) TOKEN(MULTILINE_STRING, "multiline_string", "", 0) TOKEN(MULTILINE_RAW_STRING, "multiline_raw_string", "", 0) TOKEN(BOOL_LITERAL, "bool_literal", "", 0) TOKEN(UNIT_LITERAL, "unit_literal", "", 0) // Will not be produced from Lexer. TOKEN(DOLLAR_IDENTIFIER, "dollar_identifier", "", 0) TOKEN(ANNOTATION, "annotation", "", 0) TOKEN(AT_EXCL, "at_exclamation", "@!", 0) TOKEN(COMMON, "common", "common", 0) TOKEN(PLATFORM, "platform", "platform", 0) EXPERIMENTAL_TOKEN(PERFORM, "perform", "perform", 0) EXPERIMENTAL_TOKEN(RESUME, "resume", "resume", 0) EXPERIMENTAL_TOKEN(THROWING, "throwing", "throwing", 0) EXPERIMENTAL_TOKEN(HANDLE, "handle", "handle", 0) TOKEN(ILLEGAL, "illegal", "", 0) TOKEN(DOUBLE_COLON, "double_colon", "::", 0) TOKEN(FEATURES, "features", "features", 0) #ifdef EXPERIMENTAL_TOKEN_DEFAULT #undef EXPERIMENTAL_TOKEN #undef EXPERIMENTAL_TOKEN_DEFAULT #endif #endif cangjie_compiler-1.0.7/include/cangjie/Macro/000077500000000000000000000000001510705540100210735ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/Macro/InvokeConfig.h000066400000000000000000000107131510705540100236270ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file configures parameters for C to call cangjie function. */ #ifndef CANGJIE_INVOKECONFIG_H #define CANGJIE_INVOKECONFIG_H #include "cangjie/Lex/Token.h" namespace Cangjie { namespace InvokeRuntime { enum RTLogLevel { RTLOG_VERBOSE, RTLOG_DEBUGY, RTLOG_INFO, RTLOG_WARNING, RTLOG_ERROR, RTLOG_FATAL_WITHOUT_ABORT, RTLOG_FATAL, RTLOG_OFF }; struct HeapParam { public: HeapParam(const size_t& rSize, const size_t& hSize, const double& lThreshold, const double& hUtilization, const double& hGrowth, const double& aRate, const size_t& aWaitTime) : regionSize(rSize), heapSize(hSize), exemptionThreshold(lThreshold), heapUtilization(hUtilization), heapGrowth(hGrowth), allocationRate(aRate), allocationWaitTime(aWaitTime) { } ~HeapParam() = default; private: [[maybe_unused]] size_t regionSize; // The initial size of heap (must be in range (0, heapSize]). [[maybe_unused]] size_t heapSize; // The maximum size of heap (must be > 0). [[maybe_unused]] double exemptionThreshold; // The maximum size of heap need to free (must be > 0). [[maybe_unused]] double heapUtilization; [[maybe_unused]] double heapGrowth; [[maybe_unused]] double allocationRate; // The utilization of of heap must be in range (0.0, 1.0). [[maybe_unused]] size_t allocationWaitTime; }; struct GCParam { public: GCParam(const size_t& hThreshold, const double& gRatic, const uint64_t& gcInter, const uint64_t& backInterval, const int32_t& gThreads) : gcThreshold(hThreshold), garbageThreshold(gRatic), gcInterval(gcInter), backupGCInterval(backInterval), gcThreads(gThreads) { } private: [[maybe_unused]] size_t gcThreshold; [[maybe_unused]] double garbageThreshold; [[maybe_unused]] uint64_t gcInterval; [[maybe_unused]] uint64_t backupGCInterval; [[maybe_unused]] int32_t gcThreads; }; struct LogParam { public: explicit LogParam(RTLogLevel rLevel) : logLevel(rLevel) { } ~LogParam() = default; private: [[maybe_unused]] enum RTLogLevel logLevel; }; struct ConcurrencyParam { public: ConcurrencyParam(const size_t& tStackSize, const size_t& cStackSize, const uint32_t& pNum) : thStackSize(tStackSize), coStackSize(cStackSize), processorNum(pNum) { } ~ConcurrencyParam() = default; private: [[maybe_unused]] size_t thStackSize; // The thread stack size (must be > 0). [[maybe_unused]] size_t coStackSize; // The task stack size (must be in range (0, 2GB]). [[maybe_unused]] uint32_t processorNum; // The number of processors (must be > 0). }; struct ConfigParam { public: ConfigParam(const HeapParam& hParam, const GCParam& gParam, const LogParam& lParam, const ConcurrencyParam& cParam) : heapParam(hParam), gcParam(gParam), logParam(lParam), coroutineParam(cParam) { } ~ConfigParam() = default; private: [[maybe_unused]] struct HeapParam heapParam; [[maybe_unused]] struct GCParam gcParam; [[maybe_unused]] struct LogParam logParam; [[maybe_unused]] struct ConcurrencyParam coroutineParam; }; const int G_COROUTINE_NAME_SIZE = 32; struct CoroutineAttr { char name[G_COROUTINE_NAME_SIZE]; size_t stackSize; // The stack size (must be in range (0, 2GB]). }; } // namespace Invoke } // namespace Cangjie #endif // CANGJIE_INVOKECONFIG_Hcangjie_compiler-1.0.7/include/cangjie/Macro/InvokeUtil.h000066400000000000000000000151131510705540100233360ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares basic invoke related apis. */ #ifndef CANGJIE_UTILS_INVOKE_H #define CANGJIE_UTILS_INVOKE_H #include #include #include #include #include #include #include #include #if defined(__linux__) || defined(__APPLE__) #include #endif namespace Cangjie { namespace InvokeRuntime { // Declare 32-byte alignment to ensure c++ generate same function definition as cangjie IR in both x86 and arm64. struct UnsafePtrType { uint8_t* ptr; } __attribute__((aligned(32))); const size_t REGION_SIZE = 64; const size_t HEAP_SIZE = 1024 * 1024; const double EXEMPTION_THRESHOLD = 0.8; const double HEAP_UTILIZATION = 0.6; const double HEAP_GROWTH = 0.15; const double ALLOCATION_RATE = 10240; const size_t ALLOCATION_WAIT_TIME = 1000; const size_t GC_THRESHOLD = 20; const double GARBAGE_THRESHOLD = 0.5; const uint64_t GC_INTERVAL = 150; const uint64_t BACKUP_GC_INTERNAL = 240; const uint32_t GC_THREADS = 8; const size_t STACK_SIZE = 64 * 1024; const size_t CO_STACK_SIZE = 4 * 1024; const uint32_t PROCESSOR_NUM = 24; const size_t HEAP_INITIAL_SIZE = 64 * 1024; using CommonFuncPtrT = uint8_t* (*)(void*, const int64_t, void*); using AttrFuncPtrT = uint8_t* (*)(void*, const int64_t, void*, const int64_t, void*); using HANDLE = void*; using InitGlobalFuncPtr = void* (*)(void*); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND using RuntimeInitArg = std::unordered_map; #endif #ifdef _WIN32 /** * @brief Open dynamic lib, save it to singleton variable. * * @param libPath Dynamic library path. * @return HANDLE Handle for the dynamic library. */ HANDLE OpenSymbolTable(const std::string& libPath); #elif defined(__linux__) || defined(__APPLE__) /** * @brief Open dynamic lib, save it to singleton variable. * * @param libPath Dynamic library path. * @param dlopenMode The MODE argument to `dlopen' * @return */ HANDLE OpenSymbolTable(const std::string& libPath, int dlopenMode = RTLD_LAZY | RTLD_GLOBAL); #endif /** * @brief Get method's address from handle of dynamic library. * * @param HANDLE Handle for the dynamic library. * @param name Name of method. * @return HANDLE Symbol's address for the method. */ HANDLE GetMethod(HANDLE handle, const char* name); /** * @brief Prepare cj runtime, call runtime init. * * @param HANDLE Handle for the dynamic library. * @param initArgs The arguments for initialize runtime. * @return bool Return true if the initialization is successful; otherwise, false. */ bool PrepareRuntime(const HANDLE handle, InvokeRuntime::RuntimeInitArg& initArgs); /** * @brief Finish cj runtime, call runtime finish. * * @param HANDLE Handle for the dynamic library. */ void FinishRuntime(const HANDLE handle); /** * @brief call methods by name and invoke it from dynamic library. * * @param HANDLE Handle for the dynamic library. * @param method name of method. * @param envs environment variable. * @return int64_t Return 0 if call runtime successed; otherwise, failed. */ int64_t CallRuntime(const HANDLE handle, const std::string& method, std::unordered_map& envs); /** * @brief Close dynamic lib. * * @param HANDLE Handle for the dynamic library. * @return int Return 0 if close successfully; otherwise, failed. */ int CloseSymbolTable(HANDLE handle); /** * @brief get libs that has been open. * * @return std::vector opened llibrary Handle. */ std::vector GetOpenedLibHandles(); /** * @brief set libs that has been open. * * @param HANDLE Handle for the dynamic library. */ void SetOpenedLibHandles(HANDLE handle); /** * @brief clear libs vector that has been open. */ void ClearOpenedLibHandles(); } // namespace Invoke class RuntimeInit { public: static RuntimeInit& GetInstance(); bool InitRuntime(const std::string& runtimeLibPath, InvokeRuntime::RuntimeInitArg initArgs = {}); void CloseRuntime(); ~RuntimeInit() = default; InvokeRuntime::HANDLE GetHandle() { return handle; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void* runtimeMethodFunc{nullptr}; void* runtimeReleaseFunc{nullptr}; #endif private: /** * Close macro dynamic library. */ void CloseMacroDynamicLibrary(); bool InitRuntimeMethod(); bool initRuntime{false}; InvokeRuntime::HANDLE handle{nullptr}; std::mutex mutex; }; class MacroProcMsger { public: static MacroProcMsger& GetInstance(); ~MacroProcMsger() = default; std::atomic_bool macroSrvRun{false}; std::atomic_bool pipeError{false}; std::mutex mutex; #ifdef _WIN32 typedef void *HANDLE; HANDLE hParentRead{}; HANDLE hParentWrite{}; // server process info HANDLE hChildRead{}; HANDLE hChildWrite{}; HANDLE hProcess{}; HANDLE hThread{}; #else int pipefdP2C[2]{-1, -1}; // 0 for srv read from cli, 1 for cli write msg to srv int pipefdC2P[2]{-1, -1}; // 0 for cli read from srv, 1 for srv write msg to cli #endif // client void CloseMacroSrv(); bool SendMsgToSrv(const std::vector& msg); bool ReadMsgFromSrv(std::vector& msg); bool ReadAllMsgFromSrv(std::list>& msgVec); void CloseClientResource(); // server bool SendMsgToClient(const std::vector& msg); bool ReadMsgFromClient(std::vector& msg); #ifdef _WIN32 void SetSrvPipeHandle(HANDLE hRead, HANDLE hWrite); #else void SetSrvPipeHandle(int hRead, int hWrite); #endif private: MacroProcMsger(){}; bool WriteToSrvPipe(const uint8_t* buf, size_t size) const; bool ReadFromSrvPipe(uint8_t* buf, size_t size) const; bool WriteToClientPipe(const uint8_t* buf, size_t size) const; bool ReadFromClientPipe(uint8_t* buf, size_t size) const; const size_t msgSliceLen = 4096; // Pipe cache is limited, too long messages cannot be written. }; } // namespace Cangjie #endif // CANGJIE_UTILS_INVOKE_H cangjie_compiler-1.0.7/include/cangjie/Macro/MacroCall.h000066400000000000000000000203551510705540100231060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /* * @file * * This file implements the class MacroCall. */ #ifndef CANGJIE_MACROCALL_H #define CANGJIE_MACROCALL_H #include #include #include "cangjie/AST/Node.h" #include "cangjie/Frontend/CompilerInstance.h" namespace Cangjie { static const std::string SOURCE_PACKAGE = "sourcePackage"; static const std::string SOURCE_FILE = "sourceFile"; static const std::string SOURCE_LINE = "sourceLine"; static const std::vector BUILD_IN_MACROS = {SOURCE_PACKAGE, SOURCE_FILE, SOURCE_LINE}; #ifdef _WIN32 const std::string LIB_SUFFIX = ".dll"; #elif defined(__APPLE__) const std::string LIB_SUFFIX = ".dylib"; #else const std::string LIB_SUFFIX = ".so"; #endif // Short name for vector smart pointer. template using PtrVector = std::vector>; using DeclUPtrVector = PtrVector; using TokenVector = std::vector; /** * A Vector Target represents Macro replace location. */ template struct VectorTarget { std::vector* pointer; size_t loc; // Target location. }; // Macro expansion replace node. using PtrType = std::variant*, // For those parent is an expr, like initializer in vardecl. OwnedPtr*, // Match expr in match case other. VectorTarget>, // For those parent is vector, like body in block. VectorTarget>, // Children in array lit and tuple lit. VectorTarget>, // Decls in class body, struct body. VectorTarget>>; // FuncParam in func paramlist. enum class MacroKind { EXPR_KIND = 0, // @M(1+2) DECL_KIND = 1, // @M class A{} PARAM_KIND = 2, // func f (@M(a)) UNINITIALIZED = 3, }; enum class MacroEvalStatus : uint8_t { INIT = 0, // Not ready to evaluate macrocall, child macrocalls should be evaluated first. READY = 1, // Ready to evaluate macrocall. EVAL = 2, // Evaluate macrocall. SUCCESS = 3, // Evaluate macrocall successful. FAIL = 4, // Evaluate macrocall failed. REEVAL = 5, // ReEvaluate macroCall, becasue there are still macrocalls left After macro evaluated. FINISH = 6, // No need to reEvaluate, because there are no macrocalls left After macro evaluated. ANNOTATION = 7, // Need change macrocall to decl with annotation. REEVALFAILED = 8, }; enum class ItemKind { STRING = 1, INT = 2, BOOL = 3, TKS = 4, }; struct ItemInfo { std::string key; ItemKind kind; std::string sValue; int64_t iValue; bool bValue; std::vector tValue; }; struct ChildMessage { std::string childName; std::vector items; }; class MacroCall { public: explicit MacroCall(Ptr node); ~MacroCall() {} std::vector> GetAnnotations() const; /** * @brief Find valid macro function. * * @param instance Compiler instance for create diagnosis informations. * @return bool Returns true if the initialization is successful; otherwise, false. */ bool ResolveMacroCall(CompilerInstance* instance); /** * @brief Find macro defined method from dynamic library. * * @param instance Compiler instance for create diagnosis informations. * @return bool Return true if find the method successful; otherwise, false. */ bool FindMacroDefMethod(CompilerInstance* instance); inline Ptr GetInvocation() const { return invocation; } inline std::set GetModifiers() const { return modifiers ? *modifiers : std::set{}; } inline Ptr GetNode() { return node; } inline Position GetBeginPos() const { return begin; } inline Position GetEndPos() const { return end; } inline std::string GetFullName() { return invocation->fullName; } inline std::string GetIdentifier() { return invocation->identifier; } inline Ptr GetDefinition() { return definition; } inline bool HasAttribute() { return invocation->HasAttr(); } inline std::string GetMacroInfo() { return "@" + GetFullName() + " in " + node->curFile->fileName + ":" + std::to_string(begin.line) + ":" + std::to_string(begin.column); } /** * @brief An inner macro call finds itself nested inside a particular outer macro call. * * @param parentStr The Outter macro name. * @param report Whether report error. * @return bool Returns true if the inner macro call is nested in the given outer macro call. */ bool CheckParentContext(const char* parentStr, bool report); /** * @brief Report diagnostic. * * @param level Error level. * @param range Positon range for error code. * @param message Diagnostic message. * @param hint Diagnostic hint. */ void DiagReport(const int level, const Range range, const char *message, const char* hint) const; /** * @brief An inner macro can also communicate with an outer macro. * When the inner macro executes, it calls the library function `setItem` * * @param key The key of macro context message. * @param value The value of macro context message. * @param type The of macro context message. */ void SetItemMacroContext(char* key, void* value, uint8_t type); /** * @brief When the outer macro executes, it calls `getChildMessages`, getting messages. * * @param childrenStr The inner macro name that send messages. */ void*** GetChildMessagesFromMacroContext(const char* childrenStr); PtrType replaceLoc; /**< Macro replace target Location */ std::vector children; MacroEvalStatus status = MacroEvalStatus::INIT; bool isOuterMost{false}; size_t threadId = 0; // Begin: For macrocall in string interpolation. bool isForInterpolation{false}; TokenKind strKind = TokenKind::STRING_LITERAL; std::string newStr; // End: For macrocall in string interpolation. // For runtime invoke. void* invokeFunc{nullptr}; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void* coroutineHandle{nullptr}; #endif bool isDataReady{false}; bool useParentPos{false}; /* The following functions and variables are used for macro with context */ MacroCall* parentMacroCall {nullptr}; /** Record the current macrocall parent node. */ CompilerInstance* ci{nullptr}; /** For call diag. */ std::vector recordMacroInfo{}; std::vector> macroInfoVec{}; bool hasSend{false}; std::string methodName; // macrodef method std::string packageName; // macrodef package std::string libPath; // macrodef lib path std::vector parentNames; // MacroContext: assertParentContext. std::vector items; // MacroContext: setItem. std::vector childMessages; // MacroContext: getChildMessages. std::vector assertParents; // MacroContext: assertParentContext failed parentName. private: MacroKind kind; Ptr invocation{nullptr}; Ptr node{nullptr}; Ptr definition{nullptr}; Position begin; Position end; std::set* modifiers{nullptr}; bool GetAllDeclsForMacroName(const std::string ¯oName, std::vector>& decls); bool GetValidFuncDecl(std::vector>& decls); bool BindDefinition(const std::string ¯oName); inline void BindDefinition(const Ptr fd) { definition = fd; } bool BindInvokeFunc(); /* * Recursive function to set recordMacroInfo info. */ void TraverseMacroNode( MacroCall* macroNode, const std::string& childName, std::vector>& macroInfos); bool TraverseParentMacroNode(MacroCall* mcNode, const std::string& parentName); }; } #endifcangjie_compiler-1.0.7/include/cangjie/Macro/MacroCommon.h000066400000000000000000000061671510705540100234700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the common macro methods and alias. */ #ifndef CANGJIE_MACROCOMMON_H #define CANGJIE_MACROCOMMON_H #include #include #include #include #include #include "cangjie/Basic/Print.h" #include "cangjie/Macro/MacroCall.h" namespace Cangjie { /** * Collect macro infos per package. */ struct MacroCollector { // macro definitions. std::vector> macroDefFuncs; // macro invocations. std::vector macCalls; // current pacakge who uses macros. Ptr curPkg; // imported macro pacakges. std::unordered_set> importedMacroPkgs; // Clear previous info when we expand macros in a different package. void clear() { macroDefFuncs.clear(); macCalls.clear(); importedMacroPkgs.clear(); } }; std::vector GetTokensFromString( const std::string& source, DiagnosticEngine& diag, const Position pos = INVALID_POSITION); bool IsCurFile(const SourceManager& sm, const Token& tk, unsigned int fileID = 0); // Convert Tokens into string representation in source code. std::string LineToString(TokenVector& line); bool MacroExpandFailed(const std::vector& retTokens); bool CheckAddSpace(Token curToken, Token nextToken); /** * Helper class to convert Tokens to string. */ class MacroFormatter { public: MacroFormatter( const TokenVector ts, std::vector eposVec, int offset) : input(ts), escapePosVec(eposVec), offset(offset) { } MacroFormatter(const TokenVector ts) : input(ts) { PushIntoLines(); } const std::string Produce(bool hasComment = true); private: std::vector input; std::vector lines; std::vector escapePosVec; std::string retStr; int offset; void PushIntoLines(); void LinesToString(bool hasComment); bool SeeCurlyBracket(const TokenVector& lineOfTk, const TokenKind& tk) const; }; inline size_t GetTokenLength(size_t originalSize, TokenKind kind, unsigned delimiterNum) { constexpr auto doubleQuotesSize = 2; constexpr auto multiQuotesSize = 6; switch (kind) { // both windows and linux trait NL as 1 size case TokenKind::NL: return 1; case TokenKind::STRING_LITERAL: return originalSize + doubleQuotesSize; case TokenKind::RUNE_LITERAL: case TokenKind::JSTRING_LITERAL: return originalSize + doubleQuotesSize + 1; case TokenKind::MULTILINE_STRING: return originalSize + multiQuotesSize; // For ##"abc"##, the offset of the length and value is 3 * 2. case TokenKind::MULTILINE_RAW_STRING: return originalSize + ((delimiterNum + 1) << 1); default: return originalSize; } } } // namespace Cangjie #endif cangjie_compiler-1.0.7/include/cangjie/Macro/MacroEvalMsgSerializer.h000066400000000000000000000055711510705540100256260ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /* * @file * * This file declares the Macro evaluation messages serializer for MacroEvaluation. */ #ifndef CANGJIE_MACROEVALMSGSLZER_H #define CANGJIE_MACROEVALMSGSLZER_H #include #include #include #include #include "cangjie/Macro/MacroCommon.h" #include "flatbuffers/MacroMsgFormat_generated.h" namespace Cangjie { class MacroEvalMsgSerializer { public: void SerializeDeflibMsg(const std::unordered_set& macrolibs, std::vector& bufferData); void SerializeMacroCallMsg(const Cangjie::MacroCall& macCall, std::vector& bufferData); bool SerializeMacroCallResultMsg(const MacroCall& macCall, std::vector& bufferData); void SerializeMultiCallsMsg(const std::list& macCalls, std::vector& bufferData); void SerializeExitMsg(std::vector& bufferData, bool flag = true); static MacroMsgFormat::MsgContent GetMacroMsgContenType(const std::vector& bufferData); static void DeSerializeDeflibMsg(std::list& macroLibs, const std::vector& bufferData); static void DeSerializeRangeFromCall(Position& begin, Position& end, const MacroMsgFormat::MacroCall& callFmt); static void DeSerializeIdInfoFromCall(std::string& id, Position& pos, const MacroMsgFormat::MacroCall& callFmt); static void DeSerializeArgsFromCall(std::vector& args, const MacroMsgFormat::MacroCall& callFmt); static void DeSerializeAttrsFromCall(std::vector& attrs, const MacroMsgFormat::MacroCall& callFmt); static void DeSerializeParentNamesFromCall( std::vector& parentNames, const MacroMsgFormat::MacroCall& callFmt); static void DeSerializeChildMsgesFromCall( std::vector& childMsges, const MacroMsgFormat::MacroCall& callFmt); static void DeSerializeIdInfoFromResult(std::string& id, Position& pos, const std::vector& bufferData); static void DeSerializeTksFromResult(std::vector& tks, const std::vector& bufferData); static MacroEvalStatus DeSerializeStatusFromResult(const std::vector& bufferData); static void DeSerializeItemsFromResult(std::vector& items, const std::vector& bufferData); static void DeSerializeAssertParentsFromResult( std::vector& assertParents, const std::vector& bufferData); static void DeSerializeDiagsFromResult(std::vector& diags, const std::vector& bufferData); MacroEvalMsgSerializer() noexcept {}; private: flatbuffers::FlatBufferBuilder builder{1024}; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/include/cangjie/Macro/MacroEvaluation.h000066400000000000000000000160441510705540100243420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the MacroEvaluation class. */ #ifndef CANGJIE_MACROEVALUATION_H #define CANGJIE_MACROEVALUATION_H #include "cangjie/AST/Node.h" #ifdef _WIN32 #include // we need to undefine THIS, INTERFACE, and FASTCALL which are defined by MinGW #if defined(THIS) #undef THIS #endif #if defined(INTERFACE) #undef INTERFACE #endif #if defined(interface) #undef interface #endif #if defined(CONST) #undef CONST #endif #if defined(GetObject) #undef GetObject #endif #if defined(FASTCALL) #undef FASTCALL #endif #elif defined(__linux__) || defined(__APPLE__) #include #endif #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Macro/MacroCommon.h" #include "cangjie/Macro/MacroEvalMsgSerializer.h" namespace Cangjie { class MacroEvaluation { public: MacroEvaluation(CompilerInstance* cIns, MacroCollector* mc, bool useChildProcess) : ci(cIns), macroCollector(mc), useChildProcess(useChildProcess) { InitThreadNum(); if (useChildProcess) { CreateMacroSrvProcess(); } } ~MacroEvaluation() { } const std::vector GetVecOfGeneratedCodes() { return vecOfGeneratedCodes; } /** * @brief Evaluate macro in runtime, used by compiled-macro evaluation. */ void Evaluate(); /** * @brief Help evaluate macro, need used by interpreted evaluation directly. */ void EvalMacros(); /** * @brief Convert tokens to string for .macrocall file. * * @param tokens processed tokens after macro expand. * @param offset Indentation in file. * @return std::string results after converting. */ std::string ConvertTokensToString(const TokenVector& tokens, int offset = 1); // Begin: for process isolation in lsp. void CreateMacroSrvProcess(); void ExecuteEvalSrvTask(); // End: for process isolation in lsp. private: CompilerInstance* ci{nullptr}; MacroCollector* macroCollector{nullptr}; // Generated Tokens in string format for pretty print. std::vector vecOfGeneratedCodes; std::vector escapePosVec = {}; // All escape token's pos. std::list pMacroCalls; // For multi-thread to evaluate macrocalls. std::vector> childMacCalls; // For save child macrocalls. size_t threadNum = 0; // Max num of threads. std::vector isThreadUseds; bool enableParallelMacro{false}; std::unordered_map usedMacroPkgs; // for compiled macro bool useChildProcess{false}; // Begin: for process isolation in lsp. static MacroEvalMsgSerializer msgSlzer; std::list> macDecls; std::list> macCalls; // client process bool SendMacroDefTask(const std::unordered_set& macroLibs) const; void SendExitStgTask() const; void WaitMacroDefResult() const; bool SendMacroCallTask(MacroCall& call) const; void WaitMacroCallEvalResult(MacroCall& call) const; bool SendMacroCallsTask(std::list& calls) const; bool WaitMacroCallsEvalResult(std::list& calls) const; void DeserializeMacroCallsResult( std::list& calls, const std::list>& msgList) const; // srv process std::unique_ptr CreateMacroExpand(const MacroMsgFormat::MacroCall& callFmt) const; void DeSerializeMacroCall(const MacroMsgFormat::MacroCall& callFmt); bool SerializeAndNotifyResult(MacroCall& macCall) const; bool EvalMacroCallsAndWaitResult(); #if defined(__linux__) || defined(__APPLE__) void RunMacroSrv(); void ExecMacroSrv(pid_t pid) const; #endif bool FindDef(const std::vector& msg) const; // find macro libs and opene lib handles in macro srv bool EvalMacroCall(std::vector& msg); // EvalMacroCall in macro srv void ResetForNextEval(); // End: for process isolation in isolation lsp. void InitThreadNum() { // Maxnum of threads: half of hardware_concurrency. threadNum = std::thread::hardware_concurrency() / 2; if (ci->invocation.globalOptions.enableParallelMacro && threadNum > 1) { isThreadUseds.resize(threadNum, false); enableParallelMacro = true; } } /** * Obtain macro dynamic library from --macro-lib. */ std::unordered_set GetMacroDefDynamicFiles(); /** * Save used macros for unused import. */ void SaveUsedMacros(MacroCall& macCall); /** * Save used macro packages for init global variable. */ void SaveUsedMacroPkgs(const std::string packageName); /** * Init global variable before parallel compiled macro. */ void InitGlobalVariable(); /** * Eval single macro with runtime. */ void EvaluateWithRuntime(MacroCall& macCall); /** * Release threadHandle when used parallel mode */ void ReleaseThreadHandle(MacroCall& macCall); /** * Check attribute for macrocall. */ bool CheckAttrTokens(std::vector& attrTokens, MacroCall& macCall); void ProcessTokensInQuoteExpr( TokenVector& input, size_t& startIndex, size_t& curIndex, MacroCall& macCall, bool retEval = false); bool HasMacroCallInStrInterpolation(const std::string& str, MacroCall& macCall); bool HasMacroCallInStrInterpolation( TokenVector& input, size_t startIndex, size_t curIndex, MacroCall& parentMacCall); void CreateChildMacroCall( TokenVector& inputTokens, size_t& startIndex, size_t& curIndex, MacroCall& macCall, bool reEval = false); void CheckDeprecatedMacrosUsage(MacroCall& macCall) const; bool NeedCreateMacroCallTree(MacroCall& macCall, bool reEval); void CreateMacroCallTree(MacroCall& macCall, bool reEval = false); void CreateMacroCallsTree(bool reEval = false); void EvalOneMacroCall(MacroCall& macCall); void EvalMacroCallsOnSingleThread(); void EvalMacroCallsOnMultiThread(); bool CreateThreadToEvalMacroCall(MacroCall& macCall); bool WaitForOneMacroCallEvalFinish(std::list& evalMacCalls); void EvalMacroCalls(); /** * ReEvaluate the macrocalls if there are new macrocalls after macro expansion. */ void ReEvalAfterEvalMacroCalls(); /** * Record the inner built-in macro. The expanded position info will use the outer macro's position info. */ void RefreshBuildInMacroPostionInfo(); /** * Release the memory allocated when using macro with context. */ void FreeMacroInfoVecForMacroCall(MacroCall& mc) const; void ProcessNewTokens(MacroCall& macCall); void CollectMacroLibs(); }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/include/cangjie/Macro/MacroExpansion.h000066400000000000000000000052751510705540100242030ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the MacroExpander related classes, which provides macro expand capabilities. */ #ifndef CANGJIE_MACROEXPAND_H #define CANGJIE_MACROEXPAND_H #include #include #include "cangjie/AST/Node.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Macro/InvokeUtil.h" #include "cangjie/Macro/MacroCommon.h" namespace Cangjie { class MacroExpansion { public: MacroExpansion(CompilerInstance* ci) : ci(ci) { } void Execute(AST::Package& package); void Execute(std::vector>& packages); // String format of macro generated Tokens, for pretty print. std::vector tokensEvalInMacro; private: Ptr curPackage{nullptr}; CompilerInstance* ci{nullptr}; MacroCollector macroCollector; /** * Collect macro placeholder nodes in a package. */ void CollectMacros(AST::Package& package); /** * Evaluate macro. */ void EvaluateMacros(); /** * Process macro information after macro expansion. */ void ProcessMacros(AST::Package& package); /** * Replace AST after macro expansion. */ void ReplaceAST(AST::Package& package); /** * Replace AST helper. */ void ReplaceEachMacro(MacroCall& macCall); /** * Check attribute if replaced node is enum case member. */ void CheckReplacedEnumCaseMember(MacroCall& macroNode, PtrVector& newNodes) const; /** * Check node Consistency: if all nodes are T. */ template void CheckNodesConsistency( PtrVector& nodes, PtrVector& newNodes, VectorTarget>& target) const; void ReplaceDecls( MacroCall& macroNode, PtrVector& newNodes, VectorTarget>& target) const; void ReplaceParams(MacroCall& macroNode, PtrVector& newNodes, VectorTarget>& target) const; /** * Check FuncParamList legality. */ void CheckReplacedFuncParamList( const MacroCall& macroNode, const VectorTarget>& target) const; /** * Replace each macro node to target position. */ void ReplaceEachMacroHelper(MacroCall& macroNode, PtrVector& newNodes) const; /** * Replace File Node. */ void ReplaceEachFileNode(const AST::File& file); }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/include/cangjie/Macro/NodeSerialization.h000066400000000000000000000354331510705540100246770ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /* * @file * * This file declares the NodeWriter, which serializes AST for LibAST. */ #ifndef CANGJIE_MODULES_NODESERIALIZATION_H #define CANGJIE_MODULES_NODESERIALIZATION_H #include #include #include #include "flatbuffers/NodeFormat_generated.h" #include "cangjie/AST/Node.h" #include "cangjie/Basic/DiagnosticEngine.h" using AstNode = Ptr; using AstExpr = Ptr; using AstCallExpr = Ptr; using AstLambdaExpr = Ptr; using AstType = Ptr; using AstRefType = Ptr; using AstRefExpr = Ptr; using AstPrimitiveType = Ptr; using AstAnnotation = Ptr; using AstModifier = Ptr; using AstDecl = Ptr; using AstVarDecl = Ptr; using AstMainDecl = Ptr; using AstFuncDecl = Ptr; using AstMacroDecl = Ptr; using AstMacroExpandDecl = Ptr; using AstFuncArg = Ptr; using AstFuncBody = Ptr; using AstFuncParam = Ptr; using AstMacroExpandParam = Ptr; using AstBlock = Ptr; using AstStructBody = Ptr; using AstInterfaceBody = Ptr; using AstClassBody = Ptr; using AstGeneric = Ptr; using AstGenericParamDecl = Ptr; using AstGenericConstraint = Ptr; using AstPattern = Ptr; using AstEnumPattern = Ptr; using AstMatchCase = Ptr; using AstMatchCaseOther = Ptr; using AstFile = Ptr; using AstFeaturesDirective = Ptr; using AstPackageSpec = Ptr; using AstImportSpec = Ptr; using NodeFormatDecl = flatbuffers::Offset; using NodeFormatExpr = flatbuffers::Offset; using NodeFormatType = flatbuffers::Offset; using NodeFormatPattern = flatbuffers::Offset; namespace NodeSerialization { const size_t INITIAL_FILE_SIZE = 65536; using namespace Cangjie; class NodeWriter { public: NodeWriter(Ptr nodePtr) : nodePtr(nodePtr), builder(INITIAL_FILE_SIZE) { } ~NodeWriter() { } uint8_t* ExportNode(); // uint8_t* -> unsafePtr in CangJie private: std::vector bufferData; Ptr nodePtr = nullptr; // nodePtr is the AST node to be serialized flatbuffers::Offset emptyDeclBase = flatbuffers::Offset(); flatbuffers::Offset emptyNodeBase = flatbuffers::Offset(); flatbuffers::Offset emptyTypeBase = flatbuffers::Offset(); template flatbuffers::Offset>> FlatVectorCreateHelper( const std::vector>& input, flatbuffers::Offset (NodeWriter::*funcPtr)(V)); NodeFormat::Position FlatPosCreateHelper(const Position& pos) const; flatbuffers::Offset> CreatePositionVector( const std::vector& positions); flatbuffers::FlatBufferBuilder builder; // FlatBufferBuilder which contains the buffer it grows. flatbuffers::Offset MacroInvocationCreateHelper( const AST::MacroInvocation& macroInvocation); std::vector> TokensVectorCreateHelper( std::vector tokenVector); flatbuffers::Offset SerializeNodeBase(AstNode node); flatbuffers::Offset SerializePattern(AstPattern pattern); flatbuffers::Offset SerializeConstPattern(AstPattern pattern); flatbuffers::Offset SerializeWildcardPattern(AstPattern pattern); flatbuffers::Offset SerializeVarPattern(AstPattern pattern); flatbuffers::Offset SerializeExceptTypePattern(AstPattern pattern); flatbuffers::Offset SerializeCommandTypePattern(AstPattern pattern); flatbuffers::Offset SerializeResumptionTypePattern(AstPattern pattern); flatbuffers::Offset SerializeTypePattern(AstPattern pattern); flatbuffers::Offset SerializeEnumPattern(const AST::Pattern* pattern); flatbuffers::Offset SerializeMultiEnumPattern(AstPattern pattern); flatbuffers::Offset SerializeVarOrEnumPattern(AstPattern pattern); flatbuffers::Offset SerializeTuplePattern(AstPattern pattern); flatbuffers::Offset SerializeEnumPattern(const AST::EnumPattern* enumPattern); flatbuffers::Offset SerializeMatchCase(AstMatchCase matchcase); flatbuffers::Offset SerializeMatchCaseOther(AstMatchCaseOther matchcaseother); flatbuffers::Offset SerializeExpr(AstExpr expr); flatbuffers::Offset SerializeWildcardExpr(AstExpr expr); flatbuffers::Offset SerializeBinaryExpr(AstExpr expr); flatbuffers::Offset SerializeIsExpr(AstExpr expr); flatbuffers::Offset SerializeAsExpr(AstExpr expr); flatbuffers::Offset SerializeLitConstExpr(AstExpr expr); flatbuffers::Offset SerializeUnaryExpr(AstExpr expr); flatbuffers::Offset SerializeParenExpr(AstExpr expr); flatbuffers::Offset SerializeCallExpr(const AST::Expr* expr); flatbuffers::Offset SerializeRefExpr(const AST::Expr* expr); flatbuffers::Offset SerializeReturnExpr(AstExpr expr); flatbuffers::Offset SerializeAssignExpr(AstExpr expr); flatbuffers::Offset SerializeMemberAccess(AstExpr expr); flatbuffers::Offset SerializeLambdaExpr(const AST::Expr* expr); flatbuffers::Offset SerializeTrailingClosureExpr(AstExpr expr); flatbuffers::Offset SerializeTypeConvExpr(AstExpr expr); flatbuffers::Offset SerializeTryExpr(AstExpr expr); flatbuffers::Offset SerializeMatchExpr(AstExpr expr); flatbuffers::Offset SerializeTokenPart(AstExpr expr); flatbuffers::Offset SerializeQuoteExpr(AstExpr expr); flatbuffers::Offset SerializeThrowExpr(AstExpr expr); flatbuffers::Offset SerializePerformExpr(AstExpr expr); flatbuffers::Offset SerializeResumeExpr(AstExpr expr); flatbuffers::Offset SerializeForInExpr(AstExpr expr); flatbuffers::Offset SerializeIfExpr(AstExpr expr); flatbuffers::Offset SerializeLetPatternDestructor(AstExpr expr); flatbuffers::Offset SerializeBlockExpr(AstExpr expr); flatbuffers::Offset SerializeWhileExpr(AstExpr expr); flatbuffers::Offset SerializeDoWhileExpr(AstExpr expr); flatbuffers::Offset SerializeJumpExpr(AstExpr expr); flatbuffers::Offset SerializeIncOrDecExpr(AstExpr expr); flatbuffers::Offset SerializePrimitiveTypeExpr(AstExpr expr); flatbuffers::Offset SerializeSpawnExpr(AstExpr expr); flatbuffers::Offset SerializeSynchronizedExpr(AstExpr expr); flatbuffers::Offset SerializeArrayLit(AstExpr expr); flatbuffers::Offset SerializeTupleLit(AstExpr expr); flatbuffers::Offset SerializeSubscriptExpr(AstExpr expr); flatbuffers::Offset SerializeRangeExpr(AstExpr expr); flatbuffers::Offset SerializeCallExpr(const AST::CallExpr* callExpr); flatbuffers::Offset SerializeLambdaExpr(const AST::LambdaExpr* lambdaExpr); flatbuffers::Offset SerializeBlock(AstBlock block); flatbuffers::Offset SerializeFuncArg(AstFuncArg funcArg); flatbuffers::Offset SerializeRefExpr(const AST::RefExpr* refExpr); flatbuffers::Offset SerializeOptionalExpr(AstExpr expr); flatbuffers::Offset SerializeOptionalChainExpr(AstExpr expr); flatbuffers::Offset SerializeMacroExpandExpr(AstExpr expr); flatbuffers::Offset SerializeArrayExpr(AstExpr expr); flatbuffers::Offset SerializeTypeBase(AstType type); flatbuffers::Offset SerializeType(AstType type); flatbuffers::Offset SerializeRefType(const AST::Type* type); flatbuffers::Offset SerializePrimitiveType(const AST::Type* type); flatbuffers::Offset SerializeArrayType(AstType type); flatbuffers::Offset SerializeFuncType(AstType type); flatbuffers::Offset SerializeThisType(AstType type); flatbuffers::Offset SerializeParenType(AstType type); flatbuffers::Offset SerializeQualifiedType(AstType type); flatbuffers::Offset SerializeOptionType(AstType type); flatbuffers::Offset SerializeTupleType(AstType type); flatbuffers::Offset SerializeRefType(const AST::RefType* refType); flatbuffers::Offset SerializePrimitiveType(const AST::PrimitiveType* primitiveType); flatbuffers::Offset SerializeVArrayType(AstType type); flatbuffers::Offset SerializeConstantType(AstType type); flatbuffers::Offset SerializeAnnotation(AstAnnotation annotation); flatbuffers::Offset SerializeModifier(AstModifier modifier); flatbuffers::Offset SerializeDeclBase(AstDecl decl); flatbuffers::Offset SerializeDecl(AstDecl decl); flatbuffers::Offset SerializeVarWithPatternDecl(AstDecl decl); flatbuffers::Offset SerializeVarDecl(const AST::Decl* decl); flatbuffers::Offset SerializeMainDecl(const AST::Decl* decl); flatbuffers::Offset SerializeFuncDecl(const AST::Decl* decl); flatbuffers::Offset SerializeMacroDecl(const AST::Decl* decl); flatbuffers::Offset SerializeMacroExpandDecl(const AST::Decl* decl); flatbuffers::Offset SerializeStructDecl(AstDecl decl); flatbuffers::Offset SerializePropDecl(AstDecl decl); flatbuffers::Offset SerializeTypeAliasDecl(AstDecl decl); flatbuffers::Offset SerializeExtendDecl(AstDecl decl); flatbuffers::Offset SerializeClassDecl(AstDecl decl); flatbuffers::Offset SerializeInterfaceDecl(AstDecl decl); flatbuffers::Offset SerializeEnumDecl(AstDecl decl); flatbuffers::Offset SerializePrimaryCtorDecl(AstDecl decl); flatbuffers::Offset SerializeMacroExpandParamOfDecl(const AST::Decl* decl); flatbuffers::Offset SerializeVarDecl(const AST::VarDecl* varDecl); flatbuffers::Offset SerializeMainDecl(const AST::MainDecl* mainDecl); flatbuffers::Offset SerializeFuncDecl(const AST::FuncDecl* funcDecl); flatbuffers::Offset SerializeMacroDecl(const AST::MacroDecl* macroDecl); flatbuffers::Offset SerializeMacroExpandDecl( const AST::MacroExpandDecl* macroExpandDecl); flatbuffers::Offset SerializeDeclOfFuncParam(const AST::Decl* decl); flatbuffers::Offset SerializeFuncBody(AstFuncBody funcBody); flatbuffers::Offset SerializeFuncParam(AstFuncParam funcParam); flatbuffers::Offset SerializeMacroExpandParam(AstMacroExpandParam mep); flatbuffers::Offset SerializeStructBody(AstStructBody structBody); flatbuffers::Offset SerializeInterfaceBody(AstInterfaceBody interfaceBody); flatbuffers::Offset SerializeClassBody(AstClassBody classBody); flatbuffers::Offset SerializeGeneric(AstGeneric generic); flatbuffers::Offset SerializeGenericParamDecl(AstGenericParamDecl genericParamDecl); flatbuffers::Offset SerializeGenericConstraint( AstGenericConstraint genericConstraint); flatbuffers::Offset SerializeFile(AstFile file); flatbuffers::Offset SerializeImportSpec(AstImportSpec importSpec); flatbuffers::Offset SerializeImportContent(const AST::ImportContent& content); flatbuffers::Offset SerializePackageSpec(AstPackageSpec packageSpec); flatbuffers::Offset SerializeFeaturesDirective(AstFeaturesDirective featureDirective); flatbuffers::Offset SerializeFeatureId(const AST::FeatureId& featureId); }; template flatbuffers::Offset>> NodeWriter::FlatVectorCreateHelper( const std::vector>& input, flatbuffers::Offset (NodeWriter::*funcPtr)(V)) { std::vector> vecK; for (auto& ele : input) { auto fbK = std::invoke(funcPtr, this, ele.get()); vecK.push_back(fbK); } return builder.CreateVector(vecK); } } // namespace NodeSerialization #endif // CANGJIE_MODULES_NODESERIALIZATION_H cangjie_compiler-1.0.7/include/cangjie/Macro/TestEntryConstructor.h000066400000000000000000000047101510705540100254550ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Test Entry Constructor related classes, which provide to construct test entry ast. */ #ifndef CANGJIE_FRONTEND_TESTCONSTRUCTION_H #define CANGJIE_FRONTEND_TESTCONSTRUCTION_H #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Node.h" namespace Cangjie { template inline std::unique_ptr MakeUniquePtr() { auto ptr = std::make_unique(); ptr->EnableAttr(AST::Attribute::COMPILER_ADD); return ptr; } template inline OwnedPtr MakeOwnedNode() { auto ptr = MakeOwned(); ptr->EnableAttr(AST::Attribute::COMPILER_ADD); return ptr; } class TestPackage { public: explicit TestPackage(const std::string& packageName) : packageName(packageName) {}; const std::string& packageName; std::vector> testRegisterFunctions; }; class TestModule { public: explicit TestModule(const std::string& moduleName) : moduleName(moduleName) {}; const std::string& moduleName; std::vector> testPackages; }; class TestEntryConstructor { public: explicit TestEntryConstructor(DiagnosticEngine& diag) : diag(diag) {}; void CheckTestSuite(const std::vector>& packages); static void ConstructTestSuite(const std::string& moduleName, std::vector>& srcPkgs, const std::vector> importedPkgs, bool compileTestsOnly); static bool IsTestRegistrationFunction(const Ptr funcDecl); DiagnosticEngine& diag; private: void CheckTestSuiteConstraints(AST::Node& root, const std::vector>& funcs); void CheckClassWithMacro(AST::MacroExpandDecl& med); void CheckFunctionWithAtTest( AST::MacroExpandDecl& med, const std::vector>& funcs, const std::string& macroName); static void ConstructTestImports(AST::Package& pkg, TestModule& module); static void ConstructTestEntry(AST::Package& pkg, TestModule& module); static Ptr FindMainPartPkgForTestPkg( const Ptr testPackage, std::vector> importedPkgs); }; } // namespace Cangjie #endif // CANGJIE_FRONTEND_TESTCONSTRUCTION_H cangjie_compiler-1.0.7/include/cangjie/Macro/TokenSerialization.h000066400000000000000000000015261510705540100250660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /* * @file * * This file declares the Cangjie Token Serialization. */ #ifndef CANGJIE_MODULES_TOKENSERIALIZATION_H #define CANGJIE_MODULES_TOKENSERIALIZATION_H #include #include #include #include "cangjie/Lex/Token.h" namespace TokenSerialization { using namespace Cangjie; std::vector GetTokensBytes(const std::vector& tokens); std::vector GetTokensFromBytes(const uint8_t* pBuffer); uint8_t* GetTokensBytesWithHead(const std::vector& tokens); } // namespace TokenSerialization #endif // CANGJIE_MODULES_TOKENSERIALIZATION_H cangjie_compiler-1.0.7/include/cangjie/Mangle/000077500000000000000000000000001510705540100212355ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/Mangle/ASTMangler.h000066400000000000000000000056201510705540100233460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_MANGLE_ASTMANGLER_H #define CANGJIE_MANGLE_ASTMANGLER_H #include "cangjie/Mangle/BaseMangler.h" namespace Cangjie { inline const std::string MANGLE_PAREN_PREFIX = "P"; inline const std::string MANGLE_OPTION_PREFIX = "O"; inline const std::string MANGLE_AST_CFUNC_PREFIX = "CF"; inline const std::string MANGLE_FUNC_PREFIX = "F"; inline const std::string MANGLE_QUALIFIED_PREFIX = "Q"; inline const std::string MANGLE_INITIAL_PREFIX = "INITIAL"; inline const std::string MANGLE_EXIG_PREFIX = "Extend!"; inline const std::string MANGLE_THIS_PREFIX = "H"; inline const std::string MANGLE_PUBLIC_PREFIX = "P"; inline const std::string MANGLE_PROTECTED_PREFIX = "R"; inline const std::string MANGLE_PRIVATE_PREFIX = "I"; inline const std::string MANGLE_DIDOLLAR_PREFIX = "$$"; inline const std::string MANGLE_VARIABLE_PREFIX = "V"; inline const std::string MANGLE_LET_PREFIX = "L"; inline const std::string MANGLE_INOUT_PREFIX = "$IO"; inline const std::string MANGLE_GEXTEND_PREFIX = "W"; /** * @brief Name mangle class * Class ASTMangler is designed to manage mangling rules during Parser. * */ class ASTMangler { public: /** * @brief The constructor of class ASTMangler. * * @param fullPackageName The full package name. * @return ASTMangler The instance of ASTMangler. */ explicit ASTMangler(const std::string& fullPackageName); /** * @brief The destructor of class ASTMangler. */ ~ASTMangler(); /** * @brief Helper function for mangling decl name. * * @param decl The decl to be mangled. * @return std::string The mangled signature. */ std::string Mangle(const AST::Decl& decl) const; /** * @brief Helper function for mangling primitive types name. * * @param type The primitive types do not have Decl*, the first overload is for source package, and * the second overload imported packages. * @return std::string The mangled signature. */ static std::string ManglePrimitiveType(const AST::Type& type); /** * @brief Helper function for mangling builtin types name. * * @param type The builtin types to be mangled. * @return std::string The mangled signature. */ static std::string MangleBuiltinType(const std::string& type); /** * @brief Get the extended type name. * * @param name The extended mangled name to be truncated. * @return std::optional The extended type name. */ static std::optional TruncateExtendMangledName(const std::string& name); private: struct ASTManglerImpl* pimpl; }; } // namespace Cangjie #endif // CANGJIE_MANGLE_ASTMANGLER_H cangjie_compiler-1.0.7/include/cangjie/Mangle/BaseMangler.h000066400000000000000000000557011510705540100235760ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file is name mangling utils. */ #ifndef CANGJIE_MANGLE_BASEMANGLER_H #define CANGJIE_MANGLE_BASEMANGLER_H #include #include #include #include #include #include "cangjie/AST/Node.h" #include "cangjie/Driver/StdlibMap.h" #include "cangjie/Mangle/MangleUtils.h" #include "cangjie/Utils/ConstantsUtils.h" #include "cangjie/Utils/FileUtil.h" namespace Cangjie { class ManglerContext { public: /** * @brief Obtains global wildcard variable index under its scope. * * @param file Indicates AST::File.fileHash. * @param target Indicates the index needs to be obtained which is AST::VarWithPatternDecl. * @return std::optional The index. */ std::optional GetIndexOfGlobalWildcardVar( const Ptr file, const Ptr target) const; /** * @brief Obtains local wildcard variable index under its scope. * * @param node Indicates "global AST::VarDeclAbstract"/AST::FuncDecl/AST::PrimaryCtorDecl/AST::LambdaExpr node ptr. * @param target Indicates the index needs to be obtained which is AST::VarWithPatternDecl. * @return std::optional The index. */ std::optional GetIndexOfLocalWildcardVar( const Ptr node, const Ptr target) const; /** * @brief Obtains global variable, local variable of function or lambda index under its scope. * * @param node Indicates "global AST::VarDeclAbstract"/AST::FuncDecl/AST::PrimaryCtorDecl/AST::LambdaExpr node ptr. * @param target Indicates the index needs to be obtained which is AST::VarDeclAbstract. * @return std::optional The index. */ std::optional GetIndexOfVar( const Ptr node, const Ptr target) const; /** * @brief Obtains global lambda, local lambda of function or lambda index under its scope. * * @param node Indicates "global AST::VarDeclAbstract"/AST::FuncDecl/AST::PrimaryCtorDecl/AST::LambdaExpr node ptr. * @param target Indicates the index needs to be obtained which is AST::LambdaExpr. * @return std::optional The index. */ std::optional GetIndexOfLambda( const Ptr node, const Ptr target) const; /** * @brief Obtains global extend index under its scope. * * @param file Indicates AST::File.fileHash. * @param target Indicates the index needs to be obtained which is AST::ExtendDecl. * @return std::optional The index. */ std::optional GetIndexOfExtend( const Ptr file, const Ptr target) const; /** * @brief Obtain global wildcard variable map which is used to calculate index. * * @param file Indicates AST::File.fileHash. * @param vpd Indicates the index needs to be calculated which is AST::VarWithPatternDecl. */ void SaveGlobalWildcardVar(const Ptr file, const Ptr vpd); /** * @brief Obtain local wildcard variable map which is used to calculate index. * * @param node Indicates "global AST::VarDeclAbstract"/AST::FuncDecl/AST::PrimaryCtorDecl/AST::LambdaExpr node ptr. */ void SaveLocalWildcardVar2Decl(const Ptr node); /** * @brief Obtain map contains global variable, local variable of function or lambda. * * @param node Indicates "global AST::VarDeclAbstract"/AST::FuncDecl/AST::PrimaryCtorDecl/AST::LambdaExpr node ptr. */ void SaveVar2CurDecl(const Ptr node); /** * @brief Obtain map contains global lambda, local lambda of function or lambda. * * @param node Indicates "global AST::VarDeclAbstract"/AST::FuncDecl/AST::PrimaryCtorDecl/AST::LambdaExpr node ptr. */ void SaveLambda2CurDecl(const Ptr node); /** * @brief Obtain global extend decl map. * * @param file Indicates AST::File.fileHash. * @param node Indicates "global AST::ExtendDecl". */ void SaveExtend2CurFile(const Ptr file, const Ptr node); /** * @brief Check if all the elements are wildcard. * * @param root Indicates pointer of AST::Pattern */ static bool CheckAllElementsWildcard(const Ptr& root); /** * @brief Obtains name reduce "$test". * * @param pkgName Indicates unit test package name. * @return std::string The package name. */ static std::string ReduceUnitTestPackageName(std::string pkgName) { const std::string suffix = "$test"; size_t lenOfSuffix = suffix.length(); size_t lenOfPkgName = pkgName.length(); if (lenOfPkgName >= lenOfSuffix && pkgName.substr(lenOfPkgName - lenOfSuffix) == suffix) { return pkgName.substr(0, lenOfPkgName - lenOfSuffix); } return pkgName; } // Key is file hash, value is global VarWithPatternDecl. std::map, std::vector>> file2GlobalWildcardVar; // Key is AST::File/AST::FuncDecl/AST::LambdaExpr node ptr, value is vector of VarWithPatternDecl. std::map, std::vector>> node2LocalWildcardVar; // Key is AST::File/AST::FuncDecl/AST::LambdaExpr node ptr, value is vector of lambda expr. std::map, std::vector>> node2Lambda; // Key is AST::File/AST::FuncDecl/AST::LambdaExpr node ptr, value is map of local var identifier to var decls. std::map, std::map>>> node2LocalVar; std::map, std::map>>> file2ExtendDecl; std::vector fileIndexes; }; // Class BaseMangler is designed for doing name mangling and containing all // information that mangling need. class BaseMangler { public: std::string moduleDelimiter = MANGLE_DOLLAR_PREFIX; size_t lambdaCounter = 0; // Mangler is used to generate mangledName and exportId, in most scenarios, there is no difference. // But in Java FFI scenario, the exportId of declarations with generics is different with the mangledName, // so we add the member as a flag to indicate which use the mangler is using for. bool exportIdMode = false; std::unordered_map> manglerCtxTable; /** * @brief The constructor of class BaseMangler. */ BaseMangler(){}; /** * @brief The constructor of class BaseMangler. * * @param delimiter The delimiter of module. * @return BaseMangler The instance of BaseMangler. */ BaseMangler(const std::string& delimiter) : moduleDelimiter(delimiter){}; /** * @brief The destructor of class BaseMangler. */ virtual ~BaseMangler() = default; /** * @brief Helper function for mangling decl name. * * @param decl The decl to be mangled. * @return std::string The mangled signature. */ std::string Mangle(const AST::Decl& decl) const; /** * @brief Helper function for mangling decl name with prefix path. * * @param decl The decl to be mangled. * @param prefix Path of the decl. * @return std::string The mangled signature. */ std::string Mangle(const AST::Decl& decl, const std::vector>& prefix) const; /** * @brief Helper function for mangling function decl name. * * @param funcDecl The function decl to be mangled. * @param prefix Path of the function decl. * @param genericsTypeStack Generic vector of function mangled name. * @param declare Indicates genericsTypeStack vector is get or push. * @return std::string The mangled signature. */ std::string MangleFunctionDecl(const AST::FuncDecl& funcDecl, const std::vector>& prefix, std::vector& genericsTypeStack, bool declare) const; /** * @brief Helper function for mangling decl name. * * @param decl The decl to be mangled. * @param prefix Path of the decl. * @param genericsTypeStack Generic vector of decl mangled name. * @param declare Indicates genericsTypeStack vector is get or push. * @param withSuffix Indicates whether it is default param function decl. * @return std::string The mangled signature. */ std::string MangleDecl(const AST::Decl& decl, const std::vector>& prefix, std::vector& genericsTypeStack, bool declare, bool withSuffix = true) const; /** * @brief Helper function for mangling path of the ast node. * * @param node The node needs to get the prefix path. * @param prefix Path of the node. * @param genericsTypeStack Generic vector of ast node mangled name. * @param declare Indicates genericsTypeStack vector is get or push. * @return std::string The mangled signature. */ std::string ManglePrefix(const AST::Node& node, const std::vector>& prefix, std::vector& genericsTypeStack, bool declare) const; /** * @brief Obtain package mangled name. * * @param decl The decl needs to get package mangled name. * @return std::string The mangled signature. */ std::string ManglePackage(const AST::Decl& decl) const; /** * @brief Obtain lambda mangled name. * * @param lambda The lambda expr. * @param prefix Path of the node. * @return std::string The mangled signature. */ std::string MangleLambda(const AST::LambdaExpr& lambda, const std::vector>& prefix) const; /** * @brief Find outer container of lambda. * * @param iter Iterator of the prefix which is second param. * @param prefix Path of the node. * @return Ptr The outer container of lambda. */ Ptr FindOuterNodeOfLambda( const std::vector>::const_iterator& iter, const std::vector>& prefix) const; /** * @brief Check whether the decl is lacal variable decl. * * @param decl The decl to be judged. * @return bool If yes, true is returned. Otherwise, false is returned. */ bool IsLocalVariable(const AST::Decl& decl) const; /** * @brief Helper function for mangling type name. * * @param ty The type to be mangled. * @return std::string The mangled signature. */ std::string MangleType(const AST::Ty& ty) const; /** * @brief Helper function for mangling type name. * * @param ty The type to be mangled. * @param genericsTypeStack Generic vector of type mangled name. * @param declare Indicates genericsTypeStack vector is get or push. * @param isCollectGTy Indicates whether it is needed to collect generic type. * @return std::string The mangled signature. */ std::string MangleType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare = false, bool isCollectGTy = true) const; /** * @brief Obtain mangling type prefix string. * * @param ty The type to be mangled. * @return std::string The mangled prefix string. */ std::string GetPrefixOfType(const AST::Ty& ty) const; /** * @brief Generate a name based on the `decl` and `typeArgs` of a `ty` node, * as a part of the AST type's `mangledName` or a part of the CodeGen type's name. * * @param ty The struct, interface or class type to be mangled. * @param genericsTypeStack Generic vector of type mangled name. * @param declare Indicates genericsTypeStack vector is get or push. * @param isCollectGTy Indicates whether it is needed to collect generic type. * @return std::string The mangled type string. */ std::string MangleUserDefinedType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare, bool isCollectGTy = true) const; /** * @brief Collect variable or lambda to calculate index. * * @param ctx Save collected variable or lambda. * @param pkg The AST::Package node being visited. */ void CollectVarOrLambda(ManglerContext& ctx, AST::Package& pkg) const; /** * @brief Export id for AST::Package. * * @param pkg The AST::Package node being visited. */ void MangleExportId(AST::Package& pkg); /** * @brief Export id for AST::decl. * * @param decl The AST::Decl node to be exported. * @return std::string The export id. */ std::string MangleExportId(AST::Decl& decl) const; /** * @brief Obtain mangled any type name. * * @return std::string The mangled any type name. */ std::string GetAnyMangledTypeName() const { return "CNat3AnyE"; } /** * @brief Obtain mangled index of wildcard. * * @param vwpDecl Indicates The AST::VarWithPatternDecl. * @param prefix Path of the AST::VarWithPatternDecl. * @return std::optional The index. */ std::optional GetIndexOfWildcard( const AST::VarWithPatternDecl& vwpDecl, const std::vector>& prefix) const; /** * @brief Obtain ManglerContext of package. * * @param pkg Indicates pointer of AST::Package. * @return std::unique_ptr The value of manglerCtxTable map which key is package. */ std::unique_ptr PrepareContextForPackage(const Ptr pkg); /** * @brief Convert file name to hash value and then to base 62 number. * * @param input Indicates file name string. * @return std::string The string of base 62 number. */ static std::string HashToBase62(const std::string& input); /** * @brief Determine whether str is hashable. * * @param str Indicates file name which is hashable. * @return bool If yes, true is returned. Otherwise, false is returned. */ static bool IsHashable(const std::string& str); /** * @brief Determine whether fileName ends with ".cj", if yes, strip it. * * @param fileName Indicates file name which will strip extension. * @return std::string FileName without extension. */ static std::string FileNameWithoutExtension(const std::string& fileName); protected: std::string MangleExtendEntity(const AST::ExtendDecl& extendDecl, std::vector& genericsTypeStack, bool declare) const; std::string MangleFuncParams(const AST::FuncDecl& funcDecl, std::vector& genericsTypeStack, bool declare, bool isCollectGTy = true) const; std::string MangleGenericArguments(const AST::Decl& decl, std::vector& genericsTypeStack, bool declare) const; std::string MangleGenericArgumentsHelper(const AST::Decl& decl, std::vector& genericsTypeStack, bool declare, bool isCollectGTy = true) const; std::string MangleFullPackageName(const std::string& packageName) const; std::string MangleFullPackageName(const AST::Decl& decl) const; /** * @brief Mangle `main` func and `test.entry` func. */ virtual std::optional MangleEntryFunction(const AST::FuncDecl& funcDecl) const; std::string MangleEnumType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare, bool isCollectGTy = true) const; std::string MangleRawArrayType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare, bool isCollectGTy = true) const; std::string MangleVArrayType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare, bool isCollectGTy = true) const; std::string MangleTupleType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare, bool isCollectGTy = true) const; std::string MangleFuncType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare, bool isCollectGTy = true) const; std::string MangleVarDecl(const AST::Decl& decl, const::std::vector>& prefix) const; std::string MangleVarWithPatternDecl(const AST::VarWithPatternDecl& vwpDecl, const::std::vector>& prefix) const; // Collect number of pure wildcard top-level declaration of each file mutable std::unordered_map wildcardMap{}; mutable std::mutex manglerUpdateMutex; private: // Get the direct parent decl rather than the intermediate decl like `extendDecl`. Ptr GetOuterDecl(const AST::Decl& decl) const; /** * @brief Iterate parent declarations of the decl and itself to find a generic package name, * where the generic declaration is declared. */ std::string ManglePackageNameForGeneric(const AST::Decl& decl) const; std::string MangleCPointerType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare, bool isCollectGTy = true) const; std::string MangleGenericType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare = false) const; std::string MangleGenericType(const AST::Ty& ty) const; std::string MangleCStringType() const { return "k"; } void MangleExportIdForGenericParamDecl(const AST::Decl& decl) const; }; namespace MangleUtils { /** * @brief Mangle name. * * @param str The identifier. * @return std::string The mangled name which is . */ inline std::string MangleName(const std::string& str) { return std::to_string(str.length()) + str; } inline const std::unordered_map PRIMITIVE_TYPE_MANGLE = { {AST::TypeKind::TYPE_NOTHING, "n"}, {AST::TypeKind::TYPE_UNIT, "u"}, {AST::TypeKind::TYPE_RUNE, "c"}, {AST::TypeKind::TYPE_BOOLEAN, "b"}, {AST::TypeKind::TYPE_FLOAT16, "Dh"}, {AST::TypeKind::TYPE_FLOAT32, "f"}, {AST::TypeKind::TYPE_FLOAT64, "d"}, {AST::TypeKind::TYPE_INT8, "a"}, {AST::TypeKind::TYPE_INT16, "s"}, {AST::TypeKind::TYPE_INT32, "i"}, {AST::TypeKind::TYPE_INT64, "l"}, {AST::TypeKind::TYPE_INT_NATIVE, "q"}, {AST::TypeKind::TYPE_UINT8, "h"}, {AST::TypeKind::TYPE_UINT16, "t"}, {AST::TypeKind::TYPE_UINT32, "j"}, {AST::TypeKind::TYPE_UINT64, "m"}, {AST::TypeKind::TYPE_UINT_NATIVE, "r"}, }; inline const std::unordered_map STD_PKG_MANGLE = { #include "cangjie/Mangle/StdPkg.inc" }; inline const std::unordered_map OPERATOR_TYPE_MANGLE = { {"[]", "ix"}, {"!", "nt"}, {"-", "ng"}, {"**", "pw"}, {"*", "ml"}, {"/", "dv"}, {"%", "rm"}, {"+", "pl"}, {"<<", "ls"}, {">>", "rs"}, {"<", "lt"}, {">", "gt"}, {"<=", "le"}, {">=", "ge"}, {"==", "eq"}, {"!=", "ne"}, {"&", "an"}, {"^", "eo"}, {"|", "or"}, {"()", "cl"}, }; /** * @brief Replace "from" in the string with "to". * * @param str The original string. * @param from The substring to be replaced. * @param to The replacement substring. * @return std::string The new string after replacement. */ inline std::string ReplaceSubstring(std::string str, const std::string& from, const std::string& to) { auto found = str.find(from); while (found != std::string::npos) { str.replace(found, from.length(), to); found = str.find(from); } return str; } /** * @brief Check if a declaration is an auto-boxed base declaration. * * @param decl The declaration to check. * @return bool True if the declaration is an auto-boxed base declaration, false otherwise. */ inline bool IsAutoBoxedBaseDecl(const AST::Decl& decl) { return decl.TestAttr(AST::Attribute::NO_MANGLE) && decl.astKind == AST::ASTKind::CLASS_DECL; } /** * @brief Generate a mangled name for an enumeration element. * * @param enumDeclMangledName The mangled name of the enumeration. * @param decl The declaration of the enumeration element. * @return std::string The mangled name of the enumeration element. */ inline std::string MangleEnumElement(const std::string& enumDeclMangledName, const AST::Decl& decl) { auto elementClassName = enumDeclMangledName + MANGLE_DOLLAR_PREFIX + decl.identifier; if (decl.ty && decl.ty->kind == AST::TypeKind::TYPE_FUNC) { auto funcTy = static_cast(decl.ty.get()); // Do temp mangle of enum element to ensure not duplicate. elementClassName += MANGLE_DOLLAR_PREFIX + std::to_string(funcTy->paramTys.size()); } return elementClassName; } /** * @brief Get the mangled name of a compiler-added class. * * @param className The name of the compiler-added class. * @return std::string The mangled name of the compiler-added class. */ inline std::string GetMangledNameOfCompilerAddedClass(const std::string& className) { // As for the class added by FE, it’s not in any package, so // in it’s mangled name, the would be empty // (i.e., length is zero). return MANGLE_NESTED_PREFIX + MangleName(className) + MANGLE_SUFFIX; } /** * @brief Compute the mangled name of a declaration with a custom identifier. * * @param decl A reference to the AST::Decl. * @param customIdentifier A string representing the custom identifier. * @return std::string The mangled name of the declaration with the custom identifier. */ inline std::string ComputeMangledNameWithCustomIdentifier(AST::Decl& decl, const std::string& customIdentifier) { BaseMangler mangler; auto declId = decl.identifier; auto declMangledName = decl.mangledName; decl.identifier = customIdentifier; decl.mangledName = ""; auto mangledNameWithCustomId = mangler.Mangle(decl); decl.identifier = declId; decl.mangledName = declMangledName; return mangledNameWithCustomId; } /** * @brief Get the optimized std package name. * * @param pkgName The original std package name. * @return std::string The optimized std package name. */ inline std::string GetOptPkgName(const std::string& pkgName) { if (MangleUtils::STD_PKG_MANGLE.find(pkgName) != MangleUtils::STD_PKG_MANGLE.end()) { return MangleUtils::STD_PKG_MANGLE.at(pkgName); } return MangleUtils::MangleName(pkgName); } /** * @brief Mangle file name part for global private declaration. * * @param decl A reference to the AST::Decl. * @return std::string The mangled file name modified by private. */ std::string MangleFilePrivate(const AST::Decl& decl); /** * @brief Convert a decimal string to a mangled number string. * * @param decimal The input decimal string. * @return std::string The mangled number string. */ std::string DecimalToManglingNumber(const std::string& decimal); } // namespace MangleUtils } // namespace Cangjie #endif // CANGJIE_MANGLE_BASEMANGLER_H cangjie_compiler-1.0.7/include/cangjie/Mangle/CHIRMangler.h000066400000000000000000000036171510705540100234500ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the CHIRMangle. */ #ifndef CANGJIE_MANGLE_CHIRMANGLER_H #define CANGJIE_MANGLE_CHIRMANGLER_H #include "cangjie/Mangle/BaseMangler.h" namespace Cangjie::CHIR { /** * Name mangle class * Class CHIRMangler is designed to manage mangling rules during CHIR compilation. * */ class CHIRMangler : public BaseMangler { public: /** * @brief The constructor of class CHIRMangler. * * @param compileTest The variable to enable compile test. * @return CHIRMangler The instance of CHIRMangler. */ CHIRMangler(bool compileTest) : BaseMangler(), enableCompileTest(compileTest){}; /** * @brief The constructor of class CHIRMangler. * * @param delimiter The delimiter of module. * @param compileTest The variable to enable compile test. * @return CHIRMangler The instance of CHIRMangler. */ CHIRMangler(const std::string& delimiter, bool compileTest) : BaseMangler(delimiter), enableCompileTest(compileTest){}; /** * @brief The destructor of class CHIRMangler. */ virtual ~CHIRMangler() = default; protected: /** * @brief Mangle the signature of CFunc. * eg: the signature of CFunc<(Int64, Bool)->Float64> is "dlb". * * @param cFuncTy Indicates it is the sema type. * @return std::string The mangled signature. */ std::string MangleCFuncSignature(const AST::FuncTy& cFuncTy) const; private: std::optional MangleEntryFunction(const Cangjie::AST::FuncDecl& funcDecl) const override; bool enableCompileTest; }; } // namespace Cangjie::CHIR #endif // CANGJIE_MANGLE_CHIRMANGLER_H cangjie_compiler-1.0.7/include/cangjie/Mangle/CHIRManglingUtils.h000066400000000000000000000150531510705540100246350ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file provides some utils for CHIR type mangling. */ #ifndef CANGJIE_MANGLE_CHIRMANGLINGUTILS_H #define CANGJIE_MANGLE_CHIRMANGLINGUTILS_H #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIRMangling { inline const std::string MANGLE_VIRTUAL_PREFIX = "_CV"; inline const std::string MANGLE_MUTABLE_PREFIX = "_CM"; inline const std::string MANGLE_FUNC_PREFIX = "_CC"; inline const std::string MANGLE_EXTEND_PREFIX = "$X"; inline const std::string MANGLE_INSTANTIATE_PREFIX = "_CI"; inline const std::string MANGLE_LAMBDA_PREFIX = "_CL"; inline const std::string MANGLE_OPERATOR_PREFIX = "_CO"; inline const std::string MANGLE_ANNOTATION_LAMBDA_PREFIX = "_CA"; inline const std::string MANGLE_CLOSURE_GENERIC_PREFIX = "$Cg"; inline const std::string MANGLE_CLOSURE_INSTANTIATE_PREFIX = "$Ci"; inline const std::string MANGLE_CLOSURE_FUNC_PREFIX = "$Cf"; inline const std::string MANGLE_CLOSURE_LAMBDA_PREFIX = "$Cl"; inline const std::string MANGLE_CLOSURE_WRAPPER_PREFIX = "$Cw"; inline const std::string MANGLE_ABSTRACT_INST_PREFIX = "$i"; inline const std::string MANGLE_ABSTRACT_GENERIC_PREFIX = "$vg"; inline const std::string MANGLE_ABSTRACT_INSTANTIATED_PREFIX = "$vi"; inline const std::string MANGLE_GENERIC_PREFIX = "$g"; /** * @brief Generate mangled name for virtual function. * * @param rawFunc The wrapper function. * @param customTypeDef The parent deference Type. * @param parentTy The parent class type. * @param isVirtual Indicates whether it is virtual. * @return std::string The mangled virtual function signature. */ std::string GenerateVirtualFuncMangleName( const Cangjie::CHIR::FuncBase* rawFunc, const Cangjie::CHIR::CustomTypeDef& customTypeDef, const Cangjie::CHIR::ClassType* parentTy, bool isVirtual); /** * @brief Generate mangled name for instantiate function. * * @param baseName The origin identifier. * @param instTysInFunc The type args. * @return std::string The mangled instantiate function signature. */ std::string GenerateInstantiateFuncMangleName(const std::string& baseName, const std::vector& instTysInFunc); /** * @brief Generate mangled name for lambda function. * * @param baseFunc The parent function. * @param counter The lambda wrapper index. * @return std::string The mangled lambda signature. */ std::string GenerateLambdaFuncMangleName(const Cangjie::CHIR::Func& baseFunc, size_t counter); /** * @brief Overflow strategy to mangled name. * * @param ovf The overflow strategy. * @return std::string The mangled string for overflow strategy. */ std::string OverflowStrategyToString(OverflowStrategy ovf); /** * @brief Generate mangled name for overflow operator function. * * @param name The operator function name. * @param ovf The overflow strategy. * @param isBinary Indicates whether it is binary. * @return std::string The mangled overflow operator function signature. */ std::string GenerateOverflowOperatorFuncMangleName(const std::string& name, OverflowStrategy ovf, bool isBinary, const Cangjie::CHIR::BuiltinType& type); /** * @brief Generate mangled name for annotation function. * * @param name The origin annotation function signature. * @return std::string The mangled annotation function signature. */ std::string GenerateAnnotationFuncMangleName(const std::string& name); namespace ClosureConversion { /** * @brief Generate mangled name for generic base class. * * @param paramNum The function param type size. * @return std::string The mangled signature. */ std::string GenerateGenericBaseClassMangleName(size_t paramNum); /** * @brief Generate mangled name for instantiated base class. * * @param funcType The closure conversion function type. * @return std::string The mangled signature. */ std::string GenerateInstantiatedBaseClassMangleName(const Cangjie::CHIR::FuncType& funcType); /** * @brief Generate mangled name for global implement class. * * @param func The function in closure conversion. * @return std::string The mangled signature. */ std::string GenerateGlobalImplClassMangleName(const Cangjie::CHIR::FuncBase& func); /** * @brief Generate mangled name for lambda implement class. * * @param func The lambda in closure conversion. * @param count The duplicate lambda count. * @return std::string The mangled signature. */ std::string GenerateLambdaImplClassMangleName(const Cangjie::CHIR::Lambda& func, size_t count); /** * @brief Generate mangled name for wrapper class. * * @param def The instantiate auto environment base type. * @return std::string The mangled signature. */ std::string GenerateWrapperClassMangleName(const Cangjie::CHIR::ClassDef &def); /** * @brief Generate mangled name for generic abstract function. * * @param def The auto environment base deference. * @return std::string The mangled signature. */ std::string GenerateGenericAbstractFuncMangleName(const Cangjie::CHIR::ClassDef &def); /** * @brief Generate mangled name for instantiated abstract function. * * @param def The auto environment base deference. * @return std::string The mangled signature. */ std::string GenerateInstantiatedAbstractFuncMangleName(const Cangjie::CHIR::ClassDef &def); /** * @brief Generate mangled name for generic override function. * * @param func The source function. * @return std::string The mangled signature. */ std::string GenerateGenericOverrideFuncMangleName(const Cangjie::CHIR::FuncBase &func); /** * @brief Generate mangled name for instantiate override function. * * @param func The source function. * @return std::string The mangled signature. */ std::string GenerateInstOverrideFuncMangleName(const Cangjie::CHIR::FuncBase &func); /** * @brief Generate mangled name for wrapper class generic override function. * * @param def The auto environment wrapper deference. * @return std::string The mangled signature. */ std::string GenerateWrapperClassGenericOverrideFuncMangleName(const Cangjie::CHIR::ClassDef &def); /** * @brief Generate mangled name for wrapper class instantiate override function. * * @param def The auto environment wrapper deference. * @return std::string The mangled signature. */ std::string GenerateWrapperClassInstOverrideFuncMangleName(const Cangjie::CHIR::ClassDef &def); } // namespace Cangjie::CHIRMangling::ClosureConversion } // namespace Cangjie::CHIRMangling #endif // CANGJIE_MANGLE_CHIRMANGLINGUTILS_H cangjie_compiler-1.0.7/include/cangjie/Mangle/CHIRTypeManglingUtils.h000066400000000000000000000065411510705540100255010ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file provides some utils for CHIR type mangling. */ #ifndef CANGJIE_MANGLE_CHIRTYPEMANGLINGUTILS_H #define CANGJIE_MANGLE_CHIRTYPEMANGLINGUTILS_H #include "cangjie/CHIR/Type/Type.h" #include "cangjie/Mangle/MangleUtils.h" namespace Cangjie::CHIR { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND inline const std::string MANGLE_GENERIC_PREFIX = "G"; inline const std::string MANGLE_ARRAY_SLICE_PREFIX = "S_"; inline const std::string MANGLE_CLOSURE_STR = "Closure"; inline const std::string MANGLE_UNIT_STR = "Unit"; inline const std::string MANGLE_CSTRING_STR = "CString"; inline const std::string MANGLE_RAWARR_STR = "RawArray"; inline const std::string MANGLE_VARR_STR = "VArray"; inline const std::string MANGLE_CPTR_STR = "CPointer"; inline const std::string MANGLE_TUPLE_STR = "Tuple"; inline const std::string MANGLE_BOX_STR = "Box"; #endif /** * @brief Get identifier without prefix '_C'. * * @param identifier The substring identifier. * @return std::string The mangled name without prefix '_C'. */ inline std::string StripCangjiePrefix(std::string identifier) { if (!identifier.empty()) { return identifier.substr(MANGLE_PREFIX_LEN); } return identifier; } /** * @brief Helper function for mangling chir type name. * * @param t The chir type. * @return std::string The mangled chir type name. */ std::string MangleType(const Cangjie::CHIR::Type& t); /** * @brief Helper function for mangling chir type name. * * @param t The chir type to be mangled. * @param genericsTypeStack The generic type vector. * @return std::string The mangled chir type name. */ std::string MangleType(const Cangjie::CHIR::Type& t, const std::vector& genericsTypeStack); /** * @brief Generate mangled name for primitive type. * * @param kind The chir type kind. * @return std::string The mangled chir primitive type name. */ std::string ManglePrimitive(const Cangjie::CHIR::Type::TypeKind& kind); /** * @brief Generate mangled name for type args qualified. * * @param typeArgs The type args vector to be printed. * @param forNameFieldOfTi For name field of introType. * @return std::string The mangled chir type args qualified name. */ std::string GetTypeArgsQualifiedName(const std::vector& typeArgs, bool forNameFieldOfTi = false); /** * @brief Generate mangled name for type qualified. * * @param t The chir type to be mangled. * @param forNameFieldOfTi For name field of introType. * @return std::string The mangled chir type qualified name. */ std::string GetTypeQualifiedName(const CHIR::Type& t, bool forNameFieldOfTi = false); /** * @brief Get identifier for custom type. * * @param type The chir custom type. * @return std::string The mangled chir custom type name. */ std::string GetCustomTypeIdentifier(const CHIR::CustomType& type); /** * @brief Get identifier for custom type deference. * * @param def The chir custom type deference. * @return std::string The mangled chir custom type deference name. */ std::string GetCustomTypeDefIdentifier(const CHIR::CustomTypeDef& def); } // namespace Cangjie::CHIR #endif // CANGJIE_MANGLE_CHIRTYPEMANGLINGUTILS_H cangjie_compiler-1.0.7/include/cangjie/Mangle/MangleUtils.h000066400000000000000000000063731510705540100236430ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Mangle Utils. */ #ifndef CANGJIE_MANGLE_UTILS_H #define CANGJIE_MANGLE_UTILS_H #include #include namespace Cangjie { inline const std::string MANGLE_CANGJIE_PREFIX = "_C"; inline const std::string MANGLE_NESTED_PREFIX = "N"; inline const std::string MANGLE_SUFFIX = "E"; inline const std::string MANGLE_GLOBAL_VARIABLE_INIT_PREFIX = "GV"; inline const std::string MANGLE_GLOBAL_PACKAGE_INIT_PREFIX = "GP"; inline const std::string MANGLE_GLOBAL_FILE_INIT_PREFIX = "GF"; inline const std::string MANGLE_FUNC_PARA_INIT_PREFIX = "PI"; inline const std::string SPECIAL_NAME_FOR_INIT_FUNCTION = "ii"; inline const std::string SPECIAL_NAME_FOR_INIT_RESET_FUNCTION = "ir"; inline const std::string SPECIAL_NAME_FOR_INIT_LITERAL_FUNCTION = "il"; inline const std::string SPECIAL_NAME_FOR_IMPORTS_INIT_FUNCTION = "fi"; inline const std::string SPECIAL_NAME_FOR_LITERAL_IMPORTS_INIT_FUNCTION = "fl"; inline const std::string SPECIAL_NAME_FOR_INIT_FLAG_RESET_FUNCTION = "if"; inline const std::string SPECIAL_NAME_FOR_GET = "get"; inline const std::string SPECIAL_NAME_FOR_SET = "set"; inline const std::string MANGLE_CFUNC_PREFIX = "FC"; inline const std::string MANGLE_GENERAL_FUNC_PREFIX = "F0"; inline const std::string MANGLE_FILE_ID_PREFIX = "U"; inline const std::string MANGLE_TYPE_STRUCT_PREFIX = "R"; inline const std::string MANGLE_TYPE_CLASS_PREFIX = "C"; inline const std::string MANGLE_TYPE_ENUM_PREFIX = "N"; inline const std::string MANGLE_TYPE_ARRAY_PREFIX = "A"; inline const std::string MANGLE_FUNC_PARAM_TYPE_PREFIX = "H"; inline const std::string MANGLE_VOID_TY_SUFFIX = "v"; inline const std::string MANGLE_COUNT_PREFIX= "K"; inline const std::string MANGLE_LAMBDA_PREFIX = "L"; inline const std::string MANGLE_EXTEND_PREFIX = "X"; inline const std::string USER_MAIN_MANGLED_NAME = "user.main"; inline const std::string MANGLE_GENERIC_PREFIX = "I"; inline const std::string MANGLE_ANONYMOUS_VARIABLE_PREFIX = "0"; inline const std::string MANGLE_COMPRESSED_PREFIX = "Y"; inline const std::string MANGLE_POINTER_PREFIX = "P"; inline const std::string MANGLE_VARRAY_PREFIX = "V"; inline const std::string MANGLE_TUPLE_PREFIX = "T"; inline const std::string MANGLE_GENERIC_TYPE_PREFIX = "G"; inline const std::string MANGLE_AT_PREFIX = "@"; inline const std::unordered_set PRIMITIVE_PREFIX_SET = { 'n', 'u', 'c', 'b', 'f', 'd', 'a', 's', 'i', 'l', 'q', 'h', 't', 'j', 'm', 'r', 'D', 'v' }; inline const std::string MANGLE_DOLLAR_PREFIX = "$"; inline const std::string MANGLE_WILDCARD_PREFIX = "_"; inline const std::string MANGLE_DOT_PREFIX = "."; inline const std::string MANGLE_LT_PREFIX = "<"; inline const std::string MANGLE_GT_PREFIX = ">"; inline const std::string MANGLE_LT_COLON_PREFIX = "<:"; inline const size_t MANGLE_PREFIX_LEN = 2; inline const size_t MANGLE_CHAR_LEN = 1; inline const size_t MANGLE_SPECIAL_NAME_LEN = 2; inline const size_t MANGLE_PROP_LEN = 3; inline const size_t FILE_HASH_LEN = 13; } // namespace Cangjie #endif // CANGJIE_MANGLE_UTILS_H cangjie_compiler-1.0.7/include/cangjie/Mangle/StdPkg.inc000066400000000000000000000044341510705540100231310ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file lists mangling name map of std package. */ #ifndef STD_PKG #define STD_PKG { {"std.ad", "aa"}, {"std.sync", "ab"}, {"std.collection", "ac"}, {"std.deriving", "ad"}, {"std.database", "ae"}, {"std.net", "af"}, {"std.chirad", "ag"}, {"std.io", "ah"}, {"std.convert", "ai"}, {"std.ffi", "aj"}, {"std.argopt", "ak"}, {"std.objectpool", "al"}, {"std.reflect", "am"}, {"std.fs", "an"}, {"std.runtime", "ao"}, {"std.overflow", "ap"}, {"std.unittest", "aq"}, {"std.binary", "ar"}, {"std.unicode", "as"}, {"std.core", "at"}, {"std.console", "au"}, {"std.random", "av"}, {"std.sort", "aw"}, {"std.ast", "ax"}, {"std.env", "ay"}, {"std.process", "ba"}, {"std.time", "bb"}, {"std.regex", "bc"}, {"std.posix", "bd"}, {"std.crypto", "be"}, {"std.ref", "bf"}, {"std.math", "bg"}, {"std.collection.concurrent", "bh"}, {"std.collection.concurrent.native", "bi"}, {"std.deriving.impl", "bj"}, {"std.deriving.builtins", "bk"}, {"std.deriving.api", "bl"}, {"std.deriving.resolve", "bm"}, {"std.database.sql", "bn"}, {"std.net.native", "bo"}, {"std.convert.native", "bq"}, {"std.objectpool.native", "bs"}, {"std.fs.native", "bt"}, {"std.runtime.native", "bu"}, {"std.unittest.mock", "bv"}, {"std.unittest.testmacro", "bw"}, {"std.unittest.prop_test", "bx"}, {"std.unittest.common", "by"}, {"std.unittest.native", "ca"}, {"std.unittest.diff", "cb"}, {"std.unittest.mock.mockmacro", "cc"}, {"std.unittest.mock.internal", "cd"}, {"std.core.native", "ce"}, {"std.random.native", "cf"}, {"std.ast.native", "cg"}, {"std.env.native", "ch"}, {"std.process.native", "ci"}, {"std.time.native", "cj"}, {"std.regex.native", "ck"}, {"std.posix.native", "cl"}, {"std.crypto.digest", "cm"}, {"std.crypto.cipher", "cn"}, {"std.math.numeric", "co"}, {"std.math.native", "cp"}, {"std.math.numeric.native", "cq"}, } #endifcangjie_compiler-1.0.7/include/cangjie/MetaTransformation/000077500000000000000000000000001510705540100236475ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/MetaTransformation/MetaTransform.h000066400000000000000000000114661510705540100266120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the MetaTransform related classes. */ #ifndef CANGJIE_METATRANSFORMPLUGINBUILDER_H #define CANGJIE_METATRANSFORMPLUGINBUILDER_H #include #include #include namespace Cangjie { namespace CHIR { class CHIRBuilder; class Func; class Package; } // namespace CHIR enum class MetaTransformKind { UNKNOWN, FOR_CHIR_FUNC, FOR_CHIR_PACKAGE, FOR_CHIR, }; struct MetaTransformConcept { virtual ~MetaTransformConcept() = default; bool IsForCHIR() const { return kind > MetaTransformKind::UNKNOWN && kind < MetaTransformKind::FOR_CHIR; } bool IsForFunc() const { return kind == MetaTransformKind::FOR_CHIR_FUNC; } bool IsForPackage() const { return kind == MetaTransformKind::FOR_CHIR_PACKAGE; } protected: MetaTransformKind kind = MetaTransformKind::UNKNOWN; }; /** * An abstract concept for MetaTransform * @tparam DeclT (any limitations?) */ template struct MetaTransform : public MetaTransformConcept { public: virtual void Run(DeclT&) = 0; MetaTransform() : MetaTransformConcept() { if constexpr (std::is_same_v) { kind = MetaTransformKind::FOR_CHIR_FUNC; } else if constexpr (std::is_same_v) { kind = MetaTransformKind::FOR_CHIR_PACKAGE; } else { kind = MetaTransformKind::UNKNOWN; } } virtual ~MetaTransform() = default; }; struct MetaKind { struct CHIR; }; /** * Manages a sequence plugins over a particular metadata. * @tparam MetaKind */ template class MetaTransformPluginManager { public: explicit MetaTransformPluginManager() = default; MetaTransformPluginManager(MetaTransformPluginManager&& metaTransformPluginManager) : mtConcepts(std::move(metaTransformPluginManager.mtConcepts)) { } MetaTransformPluginManager& operator=(MetaTransformPluginManager&& rhs) { mtConcepts = std::move(rhs.mtConcepts); return *this; } ~MetaTransformPluginManager() = default; template void AddMetaTransform(std::unique_ptr mt) { mtConcepts.emplace_back(std::move(mt)); } void ForEachMetaTransformConcept(std::function action) { for (auto& mtc : mtConcepts) { action(*mtc); } } private: std::vector> mtConcepts; }; using CHIRPluginManager = MetaTransformPluginManager; extern template class MetaTransformPluginManager; class MetaTransformPluginBuilder { public: void RegisterCHIRPluginCallback(std::function callback) { chirPluginCallbacks.emplace_back(callback); } CHIRPluginManager BuildCHIRPluginManager(CHIR::CHIRBuilder& builder); private: std::vector> chirPluginCallbacks; }; /** * Information of a MetaTransform. */ struct MetaTransformPluginInfo { const char* cjcVersion; void (*registerTo)(MetaTransformPluginBuilder&); /* some other members: such as name, orders, etc. */ }; #define CHIR_PLUGIN(plugin_name) \ namespace Cangjie { \ extern const std::string CANGJIE_VERSION; \ } \ extern "C" MetaTransformPluginInfo getMetaTransformPluginInfo() \ { \ return {Cangjie::CANGJIE_VERSION.c_str(), [](MetaTransformPluginBuilder& mtBuilder) { \ mtBuilder.RegisterCHIRPluginCallback([](CHIRPluginManager& mtm, CHIR::CHIRBuilder& builder) { \ mtm.AddMetaTransform(std::make_unique(builder)); \ }); \ }}; \ } } // namespace Cangjie #endif cangjie_compiler-1.0.7/include/cangjie/Modules/000077500000000000000000000000001510705540100214425ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/Modules/ASTSerialization.h000066400000000000000000000073541510705540100250110ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the AST Serialization related classes, which provides AST serialization capabilities. */ #ifndef CANGJIE_MODULES_ASTSERIALIZATION_H #define CANGJIE_MODULES_ASTSERIALIZATION_H #include #include #include #include #include #include "flatbuffers/ModuleFormat_generated.h" #include "cangjie/AST/Node.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Modules/ASTSerializationTypeDef.h" #include "cangjie/Sema/TypeManager.h" namespace Cangjie { constexpr FormattedIndex INVALID_FORMAT_INDEX = 0; constexpr size_t INITIAL_FILE_SIZE = 65536; constexpr int FB_MAX_DEPTH = 128; constexpr int FB_MAX_TABLES = 2000000; class CjoManager; // There are two kinds of decls to be saved based on where the decl is from, we // use fullId which is defined ad [pkgId, declId] to index decl: // 1. decl declared in current package, so index it with [currentPkgId, declId] // 2. decl is imported from other packages, so index it with [PkgId, declId] // In addition, some decl is definitely declared in current package, so we only // use declId instead of fullId to store it. class ASTWriter { public: ASTWriter(DiagnosticEngine& diag, const std::string& packageDepInfo, const ExportConfig& exportCfg, const CjoManager& cjoManager); ~ASTWriter(); // Add for cjmp void SetSerializingCommon(); /** Export external decls of a package AST to a buffer. */ void ExportAST(const AST::PackageDecl& package) const; /** Pre-save serialized generic decls, inlinable function, default functions, constant vardecls. */ void PreSaveFullExportDecls(AST::Package& package) const; void AST2FB(std::vector& data, const AST::PackageDecl& package) const; // Save semaTypes, return TyIndex and construct savedTypeMap. FormattedIndex SaveType(Ptr pType) const; void SetIsChirNow(bool isChirNow = false); private: class ASTWriterImpl; std::unique_ptr pImpl; }; // Decls are indexed by fullId. So there are two methods to get decl: // 1. if pkgId=currentPkgId, load decl based on declId. // 2. others, find decl from cjoManager according to fullId. class ASTLoader { public: ASTLoader(std::vector&& data, const std::string& fullPackageName, TypeManager& typeManager, const CjoManager& cjoManager, const GlobalOptions& opts); // Not use default destructor because 'ASTLoaderImpl' is defined as forward decl in header. ~ASTLoader(); OwnedPtr LoadPackageDependencies() const; void LoadPackageDecls() const; std::unordered_set LoadCachedTypeForPackage( const AST::Package& sourcePackage, const std::map>& mangledName2DeclMap); std::string LoadPackageDepInfo() const; void LoadRefs() const; std::string GetImportedPackageName() const; void SetImportSourceCode(bool enable) const; const std::vector GetDependentPackageNames() const; // Add for cjmp void PreloadCommonPartOfPackage(AST::Package& pkg) const; std::string PreReadAndSetPackageName(); std::vector ReadFileNames() const; Ptr LoadType(FormattedIndex type) const; // A flag to avoid conflicts when we are reusing the AST serialiser from CHIR void SetIsChirNow(bool isChirNow = false); private: class ASTLoaderImpl; OwnedPtr pImpl; }; } // namespace Cangjie #endif // CANGJIE_MODULES_ASTSERIALIZATION_H cangjie_compiler-1.0.7/include/cangjie/Modules/ASTSerializationTypeDef.h000066400000000000000000000024261510705540100262650ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_MODULES_ASTSERIALIZATIONTYPEDEF_H #define CANGJIE_MODULES_ASTSERIALIZATIONTYPEDEF_H #include #include "flatbuffers/CachedASTFormat_generated.h" #include "flatbuffers/ModuleFormat_generated.h" namespace Cangjie { using FormattedIndex = uint32_t; using TStringOffset = flatbuffers::Offset; using TDeclDepOffset = flatbuffers::Offset; using TEffectMapOffset = flatbuffers::Offset; using uoffset_t = flatbuffers::uoffset_t; struct ExportConfig { // Whether save initializer of var decl and func body of function. bool exportContent{false}; // Whether write cacahed cjo astData which used for load cached type info in incremental compilation. bool exportForIncr{false}; bool exportForTest{false}; // Whether save source files with their absolute paths. // Used when compiled with the `--coverage` option. bool needAbsPath{false}; bool compileCjd{false}; }; } // namespace Cangjie #endifcangjie_compiler-1.0.7/include/cangjie/Modules/CjoManager.h000066400000000000000000000114761510705540100236320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the cjo file related classes. */ #ifndef CANGJIE_MODULES_CJO_MANAGER_H #define CANGJIE_MODULES_CJO_MANAGER_H #include "cangjie/AST/Node.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Sema/TypeManager.h" namespace Cangjie { class ASTLoader; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND const uint8_t CJO_MAJOR_VERSION = 0; const uint8_t CJO_MINOR_VERSION = 1; const uint8_t CJO_PATCH_VERSION = 0; #endif class CjoManager { public: struct Config { DiagnosticEngine& diag; TypeManager& typeManager; const GlobalOptions& globalOptions; bool& importSrcCode; }; explicit CjoManager(const Config& config); ~CjoManager(); void AddSourcePackage(AST::Package& pkg) const; void AddImportedPackageFromASTNode(OwnedPtr&& pkg) const; bool LoadPackageHeader(const std::string& fullPackageName, const std::string& cjoPath) const; void LoadAllDeclsAndRefs() const; bool NeedCollectDependency(std::string curName, bool isCurMacro, std::string depName) const; /** * Loads the declaration of each package in packages on demand. * If @p fromLsp is false, only the dependent packages of each package in @p packages are loaded. * Otherwise, the packages in @p packages are also loaded. */ void LoadPackageDeclsOnDemand(const std::vector>& packages, bool fromLsp = false) const; /** * Collect visible package of current 'fullPackageName' * @param importedPackage the package which imports 'fullPackageName'. Empty for source package. */ void AddPackageDeclMap(const std::string& fullPackageName, const std::string& importedPackage = ""); /** For loading cached types during incremental compilation. */ std::unordered_set LoadCachedPackage(const AST::Package& pkg, const std::string& cjoPath, const std::map>& mangledName2DeclMap) const; /** For --scan-dependency of cjo. */ std::string GetPackageDepInfo(const std::string& cjoPath) const; Ptr GetPackageDecl(const std::string& fullPackageName) const; std::optional> PreReadCommonPartCjoFiles(); Ptr GetCommonPartCjo(std::string expectedName) const; Ptr GetPackage(const std::string& fullPackageName) const; std::vector> GetAllPackageDecls(bool includeMacroPkg = false) const; void RemovePackage(const std::string& fullPkgName, const Ptr package) const; const std::map& GetPackageMembers( const std::string& fullPackageName) const; const AST::OrderedDeclSet& GetPackageMembersByName( const std::string& fullPackageName, const std::string& name) const; Ptr GetImplicitPackageMembersByName(const std::string& fullPackageName, const std::string& name) const; std::optional GetPackageCjoPath(std::string fullPackageName) const; /** return {fullPackageName, cjoPath} */ std::pair GetPackageCjo(const AST::ImportSpec& importSpec) const; std::vector GetFullPackageNames(const AST::ImportSpec& import) const; // for single import "import a.b.c", the possible imported cjo's are a.b and a.b.c // for other import specs, the possible name is unique. std::vector GetPossibleCjoNames(const AST::ImportSpec& import) const; std::string GetPackageNameByImport(const AST::ImportSpec& importSpec) const; bool IsImportPackage(const AST::ImportSpec& importSpec) const; bool IsOnlyUsedByMacro(const std::string& fullPackageName) const; void SetOnlyUsedByMacro(const std::string& fullPackageName, bool onlyUsedByMacro) const; bool IsMacroRelatedPackageName(const std::string& fullPackageName) const; void UpdateSearchPath(const std::string& cangjieModules) const; const std::vector& GetSearchPath() const; bool GetCanInline() const; /** * For LSP, set cached cjo data @p cjoData and optional corresponding sha256 digest @p encrypt * for @param fullPackageName . */ void SetPackageCjoCache(const std::string& fullPackageName, const std::vector& cjoData) const; void ClearCjoCache() const; void DeleteASTLoaders() const noexcept; void ClearVisitedPkgs() const; DiagnosticEngine& GetDiag() const; Ptr>> GetExportIdDeclMap(const std::string& fullPackageName) const; private: class CjoManagerImpl* impl; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/include/cangjie/Modules/ImportManager.h000066400000000000000000000442521510705540100243670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the ImportManager related classes, which provides import capabilities. */ #ifndef CANGJIE_MODULES_IMPORTMANAGER_H #define CANGJIE_MODULES_IMPORTMANAGER_H #include #include #include #include #include #include "cangjie/AST/ASTCasting.h" #include "cangjie/AST/Node.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/CHIR/Interpreter/BCHIR.h" #include "cangjie/Modules/CjoManager.h" #include "cangjie/Utils/CheckUtils.h" #include "CjoManager.h" namespace Cangjie { class DefaultCompilerInstance; class TypeManager; class ASTLoader; class ASTWriter; class CjoManager; struct ExternalLibCfg { std::string name; std::string path; std::unordered_set exports; }; enum class DepType { DIRECT, INDIRECT, BOTH }; /** * ImportManager is used to manage all imported packages. It can do these things: * 1. Load a saved package to a @see PackageDecl node using deserialization method. * 2. Export external decls in current package AST using serialization method. * 3. Manage all imported packages. * 4. Merge ASTs from source files into one Package AST and generate file-realImports Map based on source file asts. * 5. Provide methods for decl lookup in sema. */ class ImportManager { public: explicit ImportManager(DiagnosticEngine& d, TypeManager& typeManager, const GlobalOptions& opts) : diag(d), opts(opts), typeManager(typeManager) { cjoManager = MakeOwned(CjoManager::Config{diag, typeManager, opts, importSrcCode}); CJC_NULLPTR_CHECK(cjoManager); dependencyGraph = MakeOwned(*cjoManager); CJC_NULLPTR_CHECK(dependencyGraph); } ~ImportManager(); /** * Export external decls of a Package AST to a buffer. */ void ExportAST( bool saveFileWithAbsPath, std::vector& astData, const AST::Package& pkg, const std::function additionalSerializations = [](ASTWriter&) {}); /** * Export all decls signature of a Package AST to a buffer. */ std::vector ExportASTSignature(const AST::Package& pkg); /** * Pre-save full exporting decls after sema's desugar before generic instantiation. * NOTE: avoid export boxed decl creation. */ void ExportDeclsWithContent(bool saveFileWithAbsPath, AST::Package& package); /** * Get all imported package decls. * @param includedMacroPkg indicates whether macro packages should be collected. */ std::vector> GetAllImportedPackages(bool includeMacroPkg = false) const; /** * Get all imported packageDecls of current node, * LSP will call it to get all imported packages in @param fullPackageName. */ const std::vector>& GetCurImportedPackages(const std::string& fullPackageName) const; /** This should not be used for source package. */ const AST::OrderedDeclSet& GetPackageMembersByName(const AST::Package& package, const std::string& name) const; /** Get accessible 'targetPackage' decls from 'srcPackage'. NOTE: used by LSP */ AST::OrderedDeclSet GetPackageMembers( const std::string& srcFullPackageName, const std::string& targetFullPackageName) const; std::vector>>> GetImportedDecls(const AST::File& file) const; /** * Get all accessible decls by @param file and @param name, and return decls. */ std::vector> GetImportedDeclsByName(const AST::File& file, const std::string& name) const; /** * Get possible packageDecl and status of whether a conflict occurs by a node and qualified name. */ std::pair, bool> GetImportedPackageDecl( Ptr node, const std::string& packageName) const; /** * Get all direct and indirect imported packageDecls of a package whose fullPackageName is @param fullPackageName */ const std::vector>& GetAllDependentPackageDecls( const std::string& fullPackageName, bool includeMacroPkg = false) const; /** * Get direct and indirect imported package names by @param fullPkgName. * NOTE: exclude packages which is only used by macro expansion. */ const std::set& GetAllDependentPackageNames(const std::string& fullPackageName) const; /** * Get information of dependent packages of @param packages in json format. */ std::vector GeneratePkgDepInfo(const std::vector>& packages) const; std::vector GeneratePkgDepInfoByCjo(const std::string& cjoPath); /** * Get direct imported package names by @param package. * NOTE: Can be used after parser step. used by LSP. */ std::set CollectDirectDepPkg(const AST::Package& package) const; /** * Build map index for importmanager. */ bool BuildIndex(const std::string& cangjieModules, const GlobalOptions& globalOptions, std::vector>& packages); /** * Load package from package name and cjo path. * Note: The function is used by lsp. */ Ptr LoadPackageFromCjo(const std::string& fullPkgName, const std::string& cjoPath) const; Ptr GetImportedDecl(const std::string& fullPackageName, const std::string& name) const; template T* GetImportedDecl(const std::string& fullPackageName, const std::string& name) const { Ptr decl = GetImportedDecl(fullPackageName, name); return DynamicCast(decl); } Ptr GetAstDecl(const std::string& name) const { return GetImportedDecl(AST_PACKAGE_NAME, name); } template T* GetAstDecl(const std::string& name) const { auto decl = GetAstDecl(name); return DynamicCast(decl); } Ptr GetCoreDecl(const std::string& name) const { return GetImportedDecl(CORE_PACKAGE_NAME, name); } template T* GetCoreDecl(const std::string& name) const { Ptr decl = GetCoreDecl(name); return DynamicCast(decl); } Ptr GetSyncDecl(const std::string& name) const { return GetImportedDecl(SYNC_PACKAGE_NAME, name); } /** * Resolve imports for 'curPackage' */ void ResolveImports(const AST::Package& pkg); std::unordered_set GetUsedSTDLibFiles(DepType type) { std::unordered_set result; for (auto& item : stdDepsMap) { if (item.second == type || item.second == DepType::BOTH) { (void)result.emplace(item.first); } } return result; } bool IsMacroRelatedPackageName(const std::string& fullPackageName) const; /** * Use for macro debug: Update File Node Import info. */ void UpdateFileNodeImportInfo(AST::Package& package, const AST::File& file, OwnedPtr& newFile); std::vector GetImportedStdMacroPackages() const { static const std::vector STD_MACRO_PACKAGES{ "std.unittest.testmacro", "std.unittest.mock.mockmacro", "std.deriving"}; std::vector importedMacroPackages; for (auto& packageName : STD_MACRO_PACKAGES) { if (auto pd = GetPackageDecl(packageName)) { importedMacroPackages.push_back(packageName); } } return importedMacroPackages; } /** * Get the direct imported macro package name for macro expand * ====================== * // useMacro.cj * import macro_def_pkg1.* * import macro_def_pkg2.* * import other_pkg.* * @M1 class A{} * ====================== * We need to get macro_def_pkg1, macro_def_pkg2 for macro expansion. */ std::unordered_set> GetImportedPkgsForMacro() { return directMacroDeps; } /** Find cjo and load cached type for given source package, return codegen mangledName of remove decls. */ std::unordered_set LoadCachedTypeForPackage( const AST::Package& pkg, const std::map>& mangledName2DeclMap); /** * For LSP, set cached cjo data @p cjoData and optional corresponding sha256 digest @p encrypt * for @param fullPackageName . */ void SetPackageCjoCache(const std::string& fullPackageName, const std::vector& cjoData) const; /** * For LSP, clear all cached cjo data. */ void ClearPackageCjoCache() const; /** @brief Set the BCHIR data for package. Used by LSP */ void SetPackageBchirCache(const std::string& fullPackageName, const std::vector& bchirData); /** @brief Returns a pointer for the cached BCHIR data for fullPackageName if it exists or nullptr otherwise. */ const std::vector* GetBchirCache(const std::string& fullPackageName); Ptr GetCjoManager() const { return cjoManager.get(); } /** @brief Clear the cache for BCHIR packages. */ void ClearPackageBCHIRCache() { bchirFileCacheMap.clear(); } /** Set BCHIRCache. only for lsp. */ void SetUseBCHIRCache(bool shouldImport) { useBCHIRCache = shouldImport; } /** Get whether the source code will be reparsed during importation. */ bool IsUseBCHIRCache() const { return useBCHIRCache; } /** * For LSP, set whether need to import source code in astLoader. * LSP will set the status to 'false'. */ void SetSourceCodeImportStatus(bool shouldImport) { importSrcCode = shouldImport; } /** Get whether the source code will be reparsed during importation. */ bool IsSourceCodeImported() { return importSrcCode; } /** * Find packageDecl from the map which contains both source and imported packages. * @param fullPackageName [in]: full package name 'module/package' * @return if found, return the packageDecl, otherwise return nullptr. */ Ptr GetPackageDecl(const std::string& fullPackageName) const; Ptr GetPackage(const std::string& fullPackageName) const; void SetImportedPackageFromASTNode(std::vector>& pkgs); using DeclImportsMap = std::unordered_map, std::vector>>; const DeclImportsMap& GetImportsOfDecl(const std::string& fullPackageName) const { static const DeclImportsMap EMPTY{}; auto found = declsImportedByNodeMap.find(fullPackageName); return found == declsImportedByNodeMap.end() ? EMPTY : found->second; } static std::string GetMainPartPkgNameForTestPkg(const std::string& testPkgName); static bool IsTestPackage(const std::string& pkgName); bool IsExtendAccessible( const AST::File& file, const AST::ExtendDecl& ed, const Ptr builder = nullptr) const; // handles valid enum, struct, class, interface ty only, return true in all other cases bool IsTyAccessible(const AST::File& file, const AST::Ty& ty) const; bool IsExtendMemberImported(const Ptr extend, const AST::File& file, const AST::Decl& member, const Ptr builder) const; bool IsExtendMemberAccessible(const AST::File& file, const AST::Decl& member, AST::Ty& baseTy, const Ptr builder = nullptr) const; void AddUsedMacroDecls(const Ptr file, Ptr decl); std::map, AST::CmpNodeByPos>>& GetUsedMacroDecls(const AST::File& file); public: /** * Whether import prelude libraries by default, default value is false, and * it will be set by CompilerInstance during compilation. */ bool implicitPrelude{false}; std::unordered_map importedBchirs; const std::vector& GetSearchPath() const { return cjoManager->GetSearchPath(); } void DeleteASTWriters() noexcept; void DeleteASTLoaders() noexcept; std::unordered_map GetDepPkgCjdPaths() { return cjdFilePaths; } DiagnosticEngine& GetDiagnosticEngine() const { return diag; } private: DiagnosticEngine& diag; const GlobalOptions& opts; bool useBCHIRCache{false}; std::unordered_map> bchirFileCacheMap; /* * Store all accessible decls for each file in source package. * fileHash -> {declName, declSet} */ std::map> fileImportedDeclsMap; /* * Store all used macro decls for each file in source package. * indexOfPackage -> {packageName, declSet} */ std::map, AST::CmpNodeByPos>>> fileUsedMacroDeclsMap; /* * Store all accessible decls for in source package. {declName, declSet}. */ std::map importedDeclsMap; std::unordered_map, ASTWriter*> astWriters; /** Store the cjo file path of STD packages on used. */ std::unordered_map stdDepsMap; /** Direct imported macro packages. */ std::unordered_set> directMacroDeps; /** Map of fullPackageName -> imported decl to the 'ImportSpec' which imports the decl. */ std::unordered_map declsImportedByNodeMap; /** NOTE: Collect imported alias decl and alias-import only. */ std::unordered_map, std::set> declToTypeAlias; /** * @brief Try deserializing BCHIR. */ bool deserializeBCHIR{false}; /** * When it is true, import generic, inline and default implementation's source code. */ bool importSrcCode{true}; private: /** * Used in `ResolveImportedPackageForFile` to handle package which has been parsed. * @return `false` if error occurred in recursive call of `ResolveImportedPackageForFile`, * return `true` for normal case. */ bool HandleParsedPackage( const AST::Package& package, const std::string& filePath, bool isUsedAsCommon, bool isRecursive); void SaveDepPkgCjdPath(const std::string& fullPackageName, const std::string& cjoPath); /** * Resolve all packages imported. */ bool ResolveImportedPackages(const std::vector>& packages); bool ResolveImportedPackageHeaders(const AST::Package& package, bool isRecursive = false); /** * Resolve all packages imported for file. */ bool ResolveImportedPackageForFile(AST::File& file, bool isRecursive = false); void HandleSTDPackage(const std::string& fullPackageName, const std::string& cjoPath, bool isRecursive = false); void AddImportedDeclsForSourcePackage(const AST::Package& pkg); /** * Get information of dependency packages of @param pkg in json format. */ std::string GeneratePkgDepInfo(const AST::Package& pkg, bool exportCJO = true) const; bool CheckCjoPathLegality(const OwnedPtr& import, const std::string& cjoPath, const std::string& fullPackageName, bool isRecursive, bool isMainPartPkgForTestPkg); void UpdateMacroPackageUsage(const AST::Package& pkg); void CheckImports(const AST::Package& pkg); void CheckRedefinition(const AST::Package& pkg); bool IsTypeAccessible(const AST::File& file, const AST::Type& type) const; bool IsExtendAllUpperBoundsImported( const AST::ExtendDecl& ed, const AST::File& file, const Ptr builder) const; bool IsDeclAccessible(const AST::File& file, AST::Decl& decl) const; const Ptr FindImplmentInterface( const AST::File& file, const AST::Decl& member, const Ptr& id) const; friend class PackageManager; class DependencyGraph { public: explicit DependencyGraph(CjoManager& cjoManager) : cjoManager(cjoManager) { } const std::vector>& GetDirectDependencyPackageDecls(const std::string& fullPackageName); const std::vector>& GetAllDependencyPackageDecls( const std::string& fullPackageName, bool includeMacroPkg); const std::set& GetAllDependencyPackageNames( const std::string& fullPackageName, bool includeMacroPkg); void AddDependenciesForPackage(AST::Package& pkg); private: const std::map, AST::CmpNodeByPos>>& GetEdges( const std::string& fullPackageName) const; void AddDependenciesForImport(AST::Package& pkg, const AST::ImportSpec& import); void AddDependency(const std::string& u, const std::string& v, const AST::ImportSpec& import); CjoManager& cjoManager; std::map, AST::CmpNodeByPos>>> dependencyMap; std::unordered_map> pkgReExportMap; //! Caches for the query APIs. These caches are build on demand. std::unordered_map>> cacheDirectDependencyPackageDecls; std::unordered_map, std::vector>, HashPair> cacheDependencyPackageDecls; std::unordered_map> cacheDependencyPackageNames; }; //! Key data structures used by `ImportManager`: OwnedPtr cjoManager; OwnedPtr dependencyGraph; Ptr curPackage; TypeManager& typeManager; // Key is cjd fullPackageName, and value is .cj.d file path. std::unordered_map cjdFilePaths; }; } // namespace Cangjie #endif // CANGJIE_MODULES_IMPORTMANAGER_H cangjie_compiler-1.0.7/include/cangjie/Modules/ModulesUtils.h000066400000000000000000000102631510705540100242460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Utility functions for Modules. */ #ifndef CANGJIE_MODULES_UTILS_H #define CANGJIE_MODULES_UTILS_H #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/Utils.h" #include "cangjie/Modules/ImportManager.h" namespace Cangjie::Modules { template bool IsAllFuncDecl(T& results) { for (auto it : results) { if (it && it->astKind != AST::ASTKind::FUNC_DECL && it->astKind != AST::ASTKind::MACRO_DECL) { return false; } } return true; } enum class PackageRelation { NONE, CHILD, SAME_MODULE, SAME_PACKAGE }; /** * Get what relation 'targetFullPackageName' is to 'srcFullPackageName'. * Eg: 1. srcFullPackageName: a, targetFullPackageName: b, return NONE. * 2. srcFullPackageName: a, targetFullPackageName: a.b, return CHILD. * 3. srcFullPackageName: a.b, targetFullPackageName: a.c, return SAME_MODULE. * 4. srcFullPackageName: a.b, targetFullPackageName: a.b, return SAME_PACKAGE. * Note: If one package is a testcase, we'll remove its '$test' suffix first. */ PackageRelation GetPackageRelation(const std::string& srcFullPkgName, const std::string& targetFullPkgName); /** * Return true if 'srcFullPackageName' is direct parent package of 'targetFullPackageName'. * Eg: 1. srcFullPackageName: a, targetFullPackageName: a.b, return true. * 2. srcFullPackageName: a, targetFullPackageName: a.b.c, return false. */ inline bool IsSuperPackage(const std::string& srcFullPackageName, const std::string& targetFullPackageName) { auto pureSrcFullPackageName = ImportManager::IsTestPackage(srcFullPackageName) ? ImportManager::GetMainPartPkgNameForTestPkg(srcFullPackageName) : srcFullPackageName; auto pureTargetFullPackageName = ImportManager::IsTestPackage(targetFullPackageName) ? ImportManager::GetMainPartPkgNameForTestPkg(targetFullPackageName) : targetFullPackageName; if (pureTargetFullPackageName.rfind(pureSrcFullPackageName, 0) == 0) { // don't split org name, make it part of root package name. auto srcNames = Utils::SplitQualifiedName(pureSrcFullPackageName); auto targetNames = Utils::SplitQualifiedName(pureTargetFullPackageName); return srcNames.size() + 1 == targetNames.size(); } return false; } inline std::string RelationToString(PackageRelation relation) { return relation == PackageRelation::NONE ? "irrelevant" : relation == PackageRelation::CHILD ? "child" : relation == PackageRelation::SAME_MODULE ? "same module" : "same package"; } inline bool IsVisible(const AST::Node& node, PackageRelation relation) { return node.TestAnyAttr(AST::Attribute::PUBLIC) || relation == PackageRelation::SAME_PACKAGE || (node.TestAttr(AST::Attribute::PROTECTED) && relation != PackageRelation::NONE) || (node.TestAttr(AST::Attribute::INTERNAL) && (relation == PackageRelation::CHILD || relation == PackageRelation::SAME_PACKAGE)); } inline void AddImportedDeclToMap( const AST::OrderedDeclSet& decls, AST::OrderedDeclSet& targetSet, AST::AccessLevel importLevel) { for (auto decl : decls) { if (IsCompatibleAccessLevel(importLevel, GetAccessLevel(*decl))) { targetSet.emplace(decl); } } } inline AST::OrderedDeclSet GetVisibleDeclToMap( const AST::OrderedDeclSet& decls, AST::AccessLevel importLevel, PackageRelation relation) { AST::OrderedDeclSet ret; std::copy_if(decls.cbegin(), decls.cend(), std::inserter(ret, ret.end()), [importLevel, relation](auto decl) { return IsCompatibleAccessLevel(importLevel, GetAccessLevel(*decl)) && IsVisible(*decl, relation); }); return ret; } const std::string NO_CJO_HELP_INFO = "check if the .cjo file of the package exists in CANGJIE_PATH or CANGJIE_HOME, or use " "'--import-path' to specify the .cjo file path"; } // namespace Cangjie::Modules #endif cangjie_compiler-1.0.7/include/cangjie/Modules/PackageManager.h000066400000000000000000000060141510705540100244420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares PackageManager related classes, which resolves dependencies between packages. */ #ifndef CANGJIE_FRONTENDTOOL_PACKAGEMANAGER_H #define CANGJIE_FRONTENDTOOL_PACKAGEMANAGER_H #include #include #include #include #include "cangjie/Modules/ImportManager.h" namespace Cangjie { class PackageManager; /** * PackageInfo stores information about the current package, including to packageName, package dir path, source files * and dependencies. */ struct PackageInfo { std::string fullPackageName; /** * The dependencies of current package. */ std::unordered_set deps; explicit PackageInfo(const std::string& fullPackageName) : fullPackageName(fullPackageName) { } }; /** * PackageManager is used to do module compilation by resolving package dependencies and generating build commands. */ class PackageManager { public: explicit PackageManager(ImportManager& importManager) : importManager(importManager) { } /** * Resolve package dependencies using topological sort method. It should be noted that circular dependencies are * allowed. * @param pkgs packages whose dependencies need to be resolved. * @param withCodeGen it is true when call this function in CodeGen staged. */ bool ResolveDependence(std::vector>& pkgs); /** * Return buildOrders for read private member. */ const std::vector>& GetBuildOrders() const { return orderedPackageInfos; } /** * If a package relies on packages which are from other SCC (Strongly Connected Component), it should be recorded. */ std::unordered_map> packageToOtherSccMap; std::unordered_map> packageInfoMap; private: ImportManager& importManager; std::vector> orderedPackageInfos; struct TarjanContext { std::unordered_map indices; /**< The discovered order of vertices in a DFS. */ std::unordered_map lowlinks; /**< The smallest index reachable from the vertex. */ std::unordered_map onStack; /**< Indicate whether the vertex is on stack. */ }; void TarjanForSCC(TarjanContext& ctx, std::stack& st, size_t& index, PackageInfo* u); /** * Check and collect dependencies for each package by parsing source files. */ void CollectDeps(std::vector>& pkgs); void CollectDepsInFile(AST::File& file, const std::unique_ptr& pkgInfo, const AST::Package& pkg); }; } // namespace Cangjie #endif // CANGJIE_FRONTENDTOOL_PACKAGEMANAGER_H cangjie_compiler-1.0.7/include/cangjie/Option/000077500000000000000000000000001510705540100213025ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/Option/Option.h000066400000000000000000001054571510705540100227370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Option related classes, which provides options for compiler. */ #ifndef CANGJIE_OPTION_OPTION_H #define CANGJIE_OPTION_OPTION_H #include #include #include #include #include #include #include #include #include "cangjie/Driver/TempFileInfo.h" #include "cangjie/Option/OptionTable.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/Utils.h" #include "cangjie/Utils/Semaphore.h" namespace Cangjie { bool SetupConditionalCompilationCfgFromFile(const std::string& filePath, std::unordered_map& passedWhenKeyValue, DiagnosticEngine& diag); enum class DiagKindRefactor : unsigned; /** * A help structure for storing paths. GlobalOptions checks whether a path is a directory * or a file, we may use the structure to represent directory path and file path. * As a result, we don't have to call IsDir() everywhere. */ struct Path { enum class Type { FILE, DIRECTORY }; Type type; std::string path; }; enum class WarnGroup : uint8_t { #define WARN_GROUP(DESCR, KIND) KIND, #include "cangjie/Basic/DiagRefactor/DiagnosticWarnGroupKind.def" #undef WARN_GROUP UNGROUPED, NONE, }; /* * The options that control diagnostic output strategy. * json: output to console with json format. * noColor: output to console without color. * default: output to console with color. * */ enum class DiagFormat : uint8_t { JSON, NO_COLOR, DEFAULT }; /* * The options that control mock support strategy. * on: compile with mocking support and allow using mocking features in the test mode. * off: compile in the regular mode, forbid using mocking features. * runtimeError: compile in the regular mode, allow using mocking features in the test mode, * but throw an exception at runtime when trying to create mock. * default: compile with no passed explicit mock option * */ enum class MockMode : uint8_t { ON, OFF, RUNTIME_ERROR, DEFAULT }; class Option { public: /** * @brief The destructor of class Option. */ virtual ~Option() { argList = nullptr; } /** * @brief Parse from arguments. * * @param argList The argument list. * @return bool Return true if pass success. */ virtual bool ParseFromArgs(ArgList& argList) = 0; ArgList* argList = nullptr; }; /** * For target format information. */ namespace Triple { enum class BackendType : uint8_t { CJNATIVE = 0, UNKNOWN, }; enum class ArchType : uint8_t { X86_64 = 0, AARCH64, ARM32, ARM64, UNKNOWN, }; enum class OSType : uint8_t { WINDOWS, LINUX, DARWIN, // MacOS IOS, // iOS UNKNOWN, }; enum class Vendor : uint8_t { PC, APPLE, UNKNOWN, }; enum class Environment : uint8_t { OHOS, GNU, ANDROID, SIMULATOR, NOT_AVAILABLE, }; struct Info { ArchType arch; Vendor vendor; OSType os; Environment env; std::string apiLevel{}; /** * @brief Convert triple information to format triple string. * * @return std::string The triple name. */ std::string ToTripleString() const; /** * @brief Convert triple information to full triple string. * * @return std::string The triple name. */ std::string ToFullTripleString() const; /** * @brief Determine if current triple information is equal to other. * * @return bool Return true If It is equal. */ inline bool operator==(const Info& other) const { return arch == other.arch && vendor == other.vendor && os == other.os && env == other.env; } /** * @brief Determine if current triple information is not equal to other. * * @return bool Return true If It is not equal. */ inline bool operator!=(const Info& other) const { return !(*this == other); } /** * @brief Get OS family. * * @return OSType The OS family. */ inline OSType GetOSFamily() const { return os; } /** * @brief Determine if current is mingw. * * @return bool Return true If It is mingw. */ inline bool IsMinGW() const { return os == OSType::WINDOWS && env == Environment::GNU; } /** * @brief Determine if current is macos. * * @return bool Return true If It is macos. */ inline bool IsMacOS() const { return vendor == Vendor::APPLE && (os == OSType::DARWIN || os == OSType::IOS); } /** * @brief Get Arch type. * * @return OSType The Arch type. */ inline ArchType GetArchType() const { return arch; } /** * @brief Get effective triple string. * * @return std::string The triple name. */ std::string GetEffectiveTripleString() const; /** * @brief Convert Arch type to string. * * @return std::string The Arch type name. */ std::string ArchToString() const; /** * @brief Convert OS to string. * * @return std::string The OS name. */ std::string OSToString() const; /** * @brief Convert vendor to string. * * @return std::string The vendor name. */ std::string VendorToString() const; /** * @brief Convert environment to string. * * @return std::string The environment name. */ std::string EnvironmentToString() const; }; /** * @brief Convert backend type to string. * * @param backend The different backend. * @return std::string The backend name. */ std::string BackendToString(const BackendType& backend); /** * @brief Is valid triple name. * * @param info The triple info. * @param name The triple name. * @return bool Return true If It is valid triple name. */ bool IsPossibleMatchingTripleName(const Info& info, const std::string& name); } // namespace Triple class GlobalOptions : public Option { public: struct Environment { std::unordered_map allVariables; std::optional cangjieHome = std::nullopt; std::vector cangjiePaths; std::vector libraryPaths; std::vector paths; std::optional macOSSDKRoot = std::nullopt; }; /** * @brief The destructor of class GlobalOptions. */ virtual ~GlobalOptions() { } /** * @brief Parse from arguments. * * @param argList The argument list. * @return bool Return true if pass success. */ bool ParseFromArgs(ArgList& argList) override; /** * @brief Generate frontend options. * * @return std::vector The frontend options vector. */ std::vector GenerateFrontendOptions() const; bool frontendMode = false; Environment environment; Triple::Info host = { #ifdef __aarch64__ Triple::ArchType::AARCH64, #elif __x86_64__ Triple::ArchType::X86_64, #else Triple::ArchType::UNKNOWN, #endif #ifdef __APPLE__ Triple::Vendor::APPLE, #else Triple::Vendor::UNKNOWN, #endif #ifdef _WIN32 Triple::OSType::WINDOWS, #elif defined(__APPLE__) Triple::OSType::DARWIN, #else Triple::OSType::LINUX, #endif #ifdef __APPLE__ Triple::Environment::NOT_AVAILABLE, #else Triple::Environment::GNU, #endif }; Triple::Info target = host; std::unordered_map passedWhenKeyValue; std::vector passedWhenCfgPaths; /** * @brief Get the relative file path related to cache. * * @param filePath The full path of the file. * @return std::string Representing the relative file path with respect to the cache directory. */ std::string GetCacheRelativeFilePath(const std::string& filePath) const; /** * @brief Generate the cached path name. * * @param fullPackageName The full package name. * @param extension The file extension. * @return std::string Representing the generated cached path name. */ std::string GenerateCachedPathName(const std::string& fullPackageName, const std::string& extension) const; /** * @brief Generate the cached path name for code generation. * * @param subModuleName The sub-module name. * @param extension The file extension. * @return std::string Representing the generated cached path name. */ std::string GenerateCachedPathNameForCodeGen(const std::string& subModuleName, const std::string& extension) const; /** * @brief Generates the names of the cached directory and file based on the full package name. * * @param fullPackageName The full package name. * @return std::pair The first element is the directory name and the second element is * the file name. */ std::pair GenerateNamesOfCachedDirAndFile(const std::string& fullPackageName) const; /** * @brief Gets the hashed filename for the given object file name. * * @param objFileName The object file name. * @return std::string The new filename based on the hash value of the object file name. */ std::string GetHashedObjFileName(const std::string& objFileName) const; /** * @brief Updates the cached directory name to reflect the full package name. * * @param fullPackageName The full package name. */ void UpdateCachedDirName(const std::string& fullPackageName); std::string cangjieHome; std::string output = "./"; std::optional outputDir = std::nullopt; std::optional outputJavaGenDir = std::nullopt; std::vector importPaths; /**< .cjo search paths */ #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::vector pluginPaths; /**< meta-transform plugins */ #endif std::optional commonPartCjo = std::nullopt; /**< .cjo path for common part of package */ // enable incremental compilation bool enIncrementalCompilation = false; bool printIncrementalInfo = false; std::string compilationCachedPath; // cached path generated with full package name std::string compilationCachedDir; std::string compilationCachedFileName; std::string executablePath; /**< Absolute path of `cjc` executable program. */ std::string moduleSrcPath; /**< module src path to be compiled. */ std::string moduleName; /**< module name for codegen. */ bool compilePackage = false; bool compileMacroPackage = false; /**< compile macro package flag. */ bool enableOutputType = false; /**< use output type option flag. */ bool saveTemps = false; /**< keep compilation intermediate results or not */ std::string tempFolderPath = "."; /**< a place for saving compilation intermediate result */ std::vector packagePaths; /**< package paths to be compiled. */ std::vector srcFiles; /**< source code files. */ std::vector inputObjs; /**< .o files to link. */ std::string inputCjoFile; /**< .cjo files to scan */ std::vector inputChirFiles; /**< .chir files to complete compilation */ std::vector inputPdbaFiles; /**< cbc import libraries, which used to import cbclib */ // Built-in package dependencies which analysed by Frontend and used by Driver. // Built-in packages are official cangjie libraries bundled with cjc compiler release, // Libraries such as std module, encoding module, compress module, are built-in packages. // Frontend knows which built-in packages the current package depends. Frontend stores // dependent built-in packages into 'directBuiltinDependencies' or 'indirectBuiltinDependencies', // then Driver could get these info and link dependent packages at linkage time. std::unordered_set directBuiltinDependencies; /**< direct dependencies. */ std::unordered_set indirectBuiltinDependencies; /**< indirect dependencies. */ std::vector frontendOutputFiles; /**< .bc files to compile. */ std::vector removedPathPrefix; /**< The removed path prefix of debug info. */ std::vector bcInputFiles; // Mark the input files and their input order. std::vector> inputFileOrder; // Mark the input library(-l) files and their input order. std::vector> inputLibraryOrder; // Mark the input link options and their input order. std::vector> inputLinkOptionsOrder; // Mark the input link option and their input order. std::vector> inputLinkOptionOrder; /** * @brief Replaces an old input filename with a new one in the list. * * @param oldName The old filename to be replaced. * @param newName The new filename to replace the old one. */ void ReplaceInputFileName(const std::string& oldName, const std::string& newName) { for (size_t i = 0; i < inputFileOrder.size(); i++) { std::tuple oldTuple = inputFileOrder[i]; if (std::get<0>(oldTuple) == oldName) { inputFileOrder[i] = std::make_tuple(newName, std::get<1>(oldTuple)); } } } Triple::BackendType backend = Triple::BackendType::CJNATIVE; std::vector macroLib; /* used in LSP and CJLINT */ bool enableVerbose = false; /**< When enable verbose, compiler will dump every command raw string. */ bool showUsage = false; /**< Show help when user want to, or error occurs when parsing arguments. */ bool scanDepPkg = false; /**< Get the package(s) which the current package depends on. */ bool noSubPkg = false; /**< Whether the package has sub-packages. */ bool displayLineInfo = true; /**< Whether display line information. */ bool enableCompileDebug = false; /**< Whether enable compile debug. */ bool enableAddCommentToAst = false; /**< Whether enable add comment to ast. */ bool enableMacroInLSP = false; /**< Whether enable macro in LSP. */ bool enableMacroDebug = false; /**< Whether enable debug macro. */ bool enableParallelMacro = false; /**< Whether enable parallel macro expansion. */ bool enableCompileTest = false; /**< Whether enable compile test. */ bool compileTestsOnly = false; /** Compile *_test.cj files only */ bool exportForTest = false; /** Export some additional kinds of declaration specifically for using them in tests */ bool enableEH = false; /** Whether support for effect handlers is enabled */ MockMode mock = MockMode::DEFAULT; /**< Whether enable mocking. */ DiagFormat diagFormat = DiagFormat::DEFAULT; /** Whether output diagnostic with color*/ bool parseTest = false; /**< If we set flag '-test', this will be true. */ bool implicitPrelude = true; /**< Whether import prelude libraries by default. */ bool enableInteropCJMapping = false; /**< Whether enable cj data structure mapping for interop */ bool enableTimer = false; /**< Whether enable timer report. */ bool enableMemoryCollect = false; /**< Whether enable memory usage report. */ std::optional errorCountLimit = 8; /**< limits the amount of errors compiler prints */ #ifdef CANGJIE_CHIR_WFC_OFF bool chirWFC = false; #else bool chirWFC = true; /**< Whether enable well-formedness check on CHIR. */ #endif bool chirEA = false; /**< Whether enable escape analysis on CHIR. */ bool chirLICM = false; /**< Whether enable LICM on CHIR (this depends on escape analysis) */ // CHIR closure-conversion bool chirCC = false; enum class OptimizationLevel : uint8_t { O0, O1, O2, O3, Os, Oz }; OptimizationLevel optimizationLevel = OptimizationLevel::O0; /**< Backend optimization level in CJNative. */ std::string optPassOptions = ""; /**< customized opt pass options from user.*/ enum class OutputMode : uint8_t { EXECUTABLE, STATIC_LIB, SHARED_LIB, CHIR }; OutputMode outputMode = OutputMode::EXECUTABLE; bool enableFuncSections = false; bool enableDataSections = false; bool enableGcSections = false; bool enableHotReload = false; bool disableReflection = false; bool enablePgoInstrGen = false; bool enablePgoInstrUse = false; std::string pgoProfileFile = ""; bool discardEhFrame = false; // Control link mode of std module. // The 'linkStaticStd' is 'true' when cjc uses '--static-std' link option. // The 'linkStaticStd' is 'false' when cjc uses '--dy-std' link option. // The 'linkStaticStd' is 'std::nullopt' when cjc neither uses '--static-std' nor '--dy-std' link option. std::optional linkStaticStd = std::nullopt; // LTO optimization options enum class LTOMode : uint8_t { FULL_LTO, THIN_LTO, NO_LTO }; LTOMode ltoMod = LTOMode::NO_LTO; bool enableCompileAsExe = false; /** * @brief Checks whether LTO is enabled. * * @return bool Returns true if LTO is enabled, otherwise returns false. */ bool IsLTOEnabled() const { return ltoMod != LTOMode::NO_LTO; } bool IsCompileAsExeEnabled() const { return enableCompileAsExe; } /** * @brief Checks whether LTO is full enabled. * * @return bool Returns true if LTO is full enabled, otherwise returns false. */ bool IsFullLTOEnabled() const { return ltoMod == LTOMode::FULL_LTO; } bool disableChirOpt = false; /**< Disable Chir Optimization When use -O2 and --disable-chir-opt*/ bool disableBackendOpt = false; /**< Disable Backend Optimization When use -O2 and --disable-backend-opt*/ struct SanitizerCoverageOptions { enum class Type { SCK_UNKNOW = -1, SCK_NONE = 0, SCK_FUNCTION, SCK_BB, } coverageType = Type::SCK_UNKNOW; bool traceCmp = false; bool traceMemCmp = false; bool tracePCGuard = false; bool inline8bitCounters = false; bool inlineBoolFlag = false; bool pcTable = false; bool stackDepth = false; /** * @brief Default constructor of SanitizerCoverageOptions. * * @return The SanitizerCoverageOptions object. */ SanitizerCoverageOptions() = default; /** * @brief Sets the sanitizer coverage level. * * @param level The coverage level. */ void SetSancovLevel(Type level) { coverageType = level; } /** * @brief Checks if the sanitizer coverage feature is enabled. * * @return bool Returns true if the sanitizer coverage feature is enabled, otherwise returns false. */ bool IsSancovEnabled() const { return traceCmp || traceMemCmp || tracePCGuard || inline8bitCounters || inlineBoolFlag || pcTable || stackDepth; } /** * @brief Serializes the SanitizerCoverageOptions object into string. * * @return std::string A serialized string. */ std::string ToSerializedString() const; }; SanitizerCoverageOptions sancovOption; // stacktrace format options: default/simple/all enum class StackTraceFormat : uint8_t { DEFAULT, SIMPLE, ALL }; StackTraceFormat stackTraceFmt = StackTraceFormat::DEFAULT; enum class OptimizationFlag : uint8_t { SWITCH_OPT, CONST_PROPAGATION, LETC_FOLDING, REF_FOLDING, REF_TO_LETC, REDUNDANT_RETURNS, REDUNDANT_FUTURE, FUNC_INLINING, DEVIRTUALIZATION, REMOVE_REDUNDANT_ASG, ARRAY_LAMBDA_OPT, SROA_OPT, LOOP_INVARIANT_CODE_MOTION, VALUE_RANGE_ANALYSIS, REDUNDANT_LOAD, }; std::set selectedCHIROpts; bool interpreter = false; /**< Run interpreter */ enum class PrintBCHIROption : uint8_t { DESERIALIED, LINKED, CHIR2BCHIR, CE_CHIR2BCHIR, INTERPRETER, CE_LINKED, ALL }; std::vector printBCHIR = {false, false, false, false, false, false, false}; /** * @brief Print the PrintBCHIROption object. * * @return bool Returns true if print success, otherwise returns false. */ bool PrintBchir(PrintBCHIROption stage) const { return printBCHIR[static_cast(PrintBCHIROption::ALL)] || printBCHIR[static_cast(stage)]; } std::vector interpreterSearchPaths; /**< Interpreter will search in these paths for dependencies that cannot deserialize. */ std::vector interpLoadLib; std::vector interpreterArgs; bool interpreterPrintResult = false; bool interpFullBchir = false; bool interpMainNoLinkage = false; bool interpCHIR = false; bool constEvalDebug = false; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool computeAnnotationsDebug{false}; // --debug-annotations #endif bool disableCodeGen = false; bool disableDeserializer = false; bool chirDebugOptimizer = false; enum class CHIRMode : uint8_t { NA, STANDARD, WITH_ID, ALL }; bool codegenDebugMode = false; bool chirDumpDebugMode = false; bool chirDeserialize = false; std::string chirDeserializePath; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool disableInstantiation = true; #endif bool disableSemaVic = false; bool enableChirRGetOrThrowE = false; bool disableChirUselessImportElimination = false; bool enableOpaque = false; CHIRMode chirRenderMode = CHIRMode::NA; bool chirHLIR = false; bool chirLLVM = false; bool strictNumberMode = false; /**< Whether apply strict number mode. */ bool fastMathMode = false; /**< Whether apply fast math mode. */ bool printVersionOnly = false; /**< Whether print version only. */ bool enableCoverage = false; /** < Whether instrument code to generate coverage info. */ /// Whether to compile .cj.d files. Note that when true, .cj files are not processed. bool compileCjd{false}; enum class SanitizerType : uint8_t { NONE, ADDRESS, THREAD, HWADDRESS }; SanitizerType sanitizerType = SanitizerType::NONE; bool experimentalMode = false; std::optional jobs; /* parallel compile jobs. */ std::optional aggressiveParallelCompile = std::nullopt; bool aggressiveParallelCompileWithoutArg = false; std::vector bitcodeFilesName; /** < the name of packageMoudle.bc. */ std::vector symbolsNeedLocalized; /** < Symbols that need to be localized in the compiled binary. */ /** * @brief Determine if the output mode is executable. * * @return bool Returns true if the output mode is executable, otherwise returns false. */ bool CompileExecutable() const { return (outputMode == GlobalOptions::OutputMode::EXECUTABLE); } /** * @brief Get options backend type. * * @return Options::Backend The backend type. */ Options::Backend GetOptionsBackend() { switch (backend) { case Triple::BackendType::CJNATIVE: return Options::Backend::CJNATIVE; default: break; } return Options::Backend::UNKNOWN; } OverflowStrategy overflowStrategy = OverflowStrategy::THROWING; /** * @brief Get Cangjie library path name of host. * * @return std::string Cangjie library path name is a name consists of os, arch and * backend, such as linux_aarch64_cjnative. */ std::string GetCangjieLibHostPathName() const; /** * @brief Get Cangjie library path of target. * * @return std::string Cangjie library path name. */ std::string GetCangjieLibTargetPathName() const; /** * @brief Get serialized stack trace format string. * * @return std::string The stack trace format string. */ std::string GetStackTraceFormat() const { return "--" + StackTraceFormatToSerializedString(); }; /** * @brief Determine if it is cross compiling. * * @return bool Return true If it is cross compiling. */ inline bool IsCrossCompiling() const { return host != target; } /** * @brief Determine if chir optimization level is over O2. * * @return bool Return true If chir optimization level is over O2. */ bool IsCHIROptimizationLevelOverO2() const { return (!disableChirOpt) && optimizationLevel >= GlobalOptions::OptimizationLevel::O2; } /** * @brief Determine if chir optimization exists. * * @return bool Return true If chir optimization exists. */ bool IsOptimizationExisted(OptimizationFlag flag) const { if (disableChirOpt) { return false; } return selectedCHIROpts.find(flag) != selectedCHIROpts.end(); } /** * @brief Set optimization level. * * @return bool Returns true If it set success. */ bool SetOptimizationLevel(OptimizationLevel level); /** * @brief Checks if constant expression evaluation is enabled. * * @return bool Returns true if ir is enabled; otherwise returns false. */ bool IsConstEvalEnabled() const { // No constant evaluation when: // - Running with `--chir-interpreter` - everything gets interpreted anyway. return !interpreter && !interpCHIR; } /** * @brief Checks if the Sancov option is enabled. * * @return bool Returns true if the Sancov option is enabled, otherwise returns false. */ bool IsSancovOptionEnabled() const { return sancovOption.inline8bitCounters || sancovOption.inlineBoolFlag || sancovOption.tracePCGuard || sancovOption.pcTable || sancovOption.stackDepth || sancovOption.traceCmp || sancovOption.traceMemCmp; } /** * @brief Checks if the sanitizer is enabled. * * @return bool Returns true if the sanitizer is enabled; otherwise returns false. */ inline bool EnableSanitizer() const { return sanitizerType != SanitizerType::NONE; } /** * @brief Checks if the asan is enabled. * * @return bool Returns true if the asan is enabled; otherwise returns false. */ inline bool EnableAsan() const { return sanitizerType == SanitizerType::ADDRESS; } /** * @brief Checks if the tsan is enabled. * * @return bool Returns true if the tsan is enabled; otherwise returns false. */ inline bool EnableTsan() const { return sanitizerType == SanitizerType::THREAD; } /** * @brief Checks if the HwAsan is enabled. * * @return bool Returns true if the HwAsan is enabled; otherwise returns false. */ inline bool EnableHwAsan() const { return sanitizerType == SanitizerType::HWADDRESS; } /** * @brief Converts the sanitizer type to a short string representation. * * @return std::string The short string representation of the sanitizer type. */ std::string SanitizerTypeToShortString() const; /** * @brief Checks if the obfuscation is enabled. * * @return bool Returns true if the obfuscation is enabled; otherwise returns false. */ virtual bool IsObfuscationEnabled() const { return false; } /** * @brief Reprocesses the obfuscation option. * * @return bool Returns true if the reprocessing was successful; otherwise returns false. */ virtual bool ReprocessObfuseOption() { return true; } /** * @brief Parses an integer value from an option argument instance. * * @param arg The optional argument instance. * @param from The minimum allowed value. Default is INT_MIN. * @param to The maximum allowed value. Default is INT_MAX. * @return std::optional The optional integer value. */ static std::optional ParseIntOptionValue(const OptionArgInstance& arg, int from = INT_MIN, int to = INT_MAX); /** * @brief Gets the extension of a shared library file. * * @param osType The operating system type. * @return std::string The extension of the shared library file. */ static std::string GetSharedLibraryExtension(Triple::OSType osType); /** * @brief Reads paths from environment variables. * * @param environmentVars An environment variable map. */ virtual void ReadPathsFromEnvironmentVars(const std::unordered_map& environmentVars); /** * @brief Checks if a directory path exists. * * @param path The optional absolute directory path. * @return std::string The optional absolute directory path. */ std::optional CheckDirectoryPath(const std::string& path) const; /** * @brief Checks if a directory path with file mode exists. * * @param path The optional absolute directory path. * @param mode The file mode. * @return std::string The optional absolute directory path. */ std::optional ValidateDirectoryPath(const std::string& path, FileUtil::FileMode mode) const; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND /** * @brief Checks if the input file path is valid. * * @param path The input file path. * @return std::optional The optional absolute file path. */ std::optional CheckInputFilePath(const std::string& path) const; #endif /** * @brief Validates the input file path. * * @param path The input file path. * @param notFoundError A reference to the diagnostic kind used to generate the error message. * @return std::optional The optional valid file path. */ std::optional ValidateInputFilePath( const std::string& path, const DiagKindRefactor notFoundError) const; /** * @brief Sets the frontend mode. */ inline void SetFrontendMode() { frontendMode = true; } /** * @brief Sets the path for the compilation cache. */ void SetCompilationCachedPath(); /** * @brief Converts the object to a serialized form. * * @return std::vector The serialized data vector. */ std::vector ToSerialized() const; /** * @brief Gets the number of jobs currently running in parallel. * * @return The number of jobs currently running in parallel. */ size_t GetJobs() const { return jobs.value_or(std::thread::hardware_concurrency()); } enum class CandidateEmitCHIRPhase : uint8_t { NA, RAW, OPT }; /**< Candidate phases of chir serialization file that can be emitted. */ CandidateEmitCHIRPhase emitCHIRPhase{ CandidateEmitCHIRPhase::NA}; /**< Emit chir serialization file of the specified phase. */ /** * @brief Checks if CHIR emission is enabled. * * @return True if CHIR emission is enabled, false otherwise. */ bool IsEmitCHIREnable() const { return emitCHIRPhase != CandidateEmitCHIRPhase::NA; } protected: virtual std::optional ParseOption(OptionArgInstance& arg); virtual bool PerformPostActions(); private: bool TryParsePreOption(OptionArgInstance& arg, ArgList& argList, bool& skipParsing); bool TryParseOption(OptionArgInstance& arg, ArgList& argList); void OccurrenceCheck(const OptionArgInstance& arg, ArgList& argList) const; void DeprecatedOptionCheck(const OptionArgInstance& arg) const; bool SetupConditionalCompilationCfg(); void SetupChirOptions(); bool ReprocessOutputs(); bool CheckOutputPathLength() const; bool ReprocessInputs(); bool ReprocessCoverageOptions(); bool ReprocessReflectionOption(); bool CheckScanDependencyOptions() const; bool CheckSanitizerOptions() const; bool CheckLtoOptions() const; bool CheckCompileAsExeOptions() const; bool CheckPgoOptions() const; bool CheckCompileMacro() const; void RefactJobs(); void RefactAggressiveParallelCompileOption(); void DisableStaticStdForOhos(); bool ProcessInputs(const std::vector& inputs); bool HandleArchiveExtension(DiagnosticEngine& diag, const std::string& value); bool HandleCJOExtension(DiagnosticEngine& diag, const std::string& value); bool HandleCJExtension(DiagnosticEngine& diag, const std::string& value); bool HandleCHIRExtension(DiagnosticEngine& diag, const std::string& value); bool HandleCJDExtension(DiagnosticEngine& diag, const std::string& value); bool HandleBCExtension(DiagnosticEngine& diag, const std::string& value); bool HandleNoExtension(DiagnosticEngine& diag, const std::string& value); void ErrorExperimentalOption(const std::string& argName) { Errorf("%s is an experimental feature. \"%s\" option is required to enable experimental features.\n", argName.c_str(), "--experimental"); } std::string PassedWhenKeyValueToSerializedString() const; std::string BackendTypeToSerializedString() const; std::string OptimizationLevelToSerializedString() const; std::string StackTraceFormatToSerializedString() const; std::string OutputModeToSerializedString() const; std::string SelectedCHIROptsToSerializedString() const; std::string OverflowStrategyToSerializedString() const; std::string SanitizerTypeToSerializedString() const; void CollectOrderedInputFiles(ArgInstance& arg, uint64_t idx); }; extern const std::unordered_map OPTIMIZATION_LEVEL_TO_BACKEND_OPTION; bool IsUnsafeBackend(Triple::BackendType backendType); class WarningOptionMgr { public: static void UpdateFlags(const std::vector newFlags) { GetInstance()->suppressedFlags = newFlags; } static void UpdateFlag(size_t index, bool newVal) { auto& flags = GetInstance()->suppressedFlags; CJC_ASSERT(index < flags.size()); flags[index] = newVal; } bool IsSuppressed(size_t index) { CJC_ASSERT(index < suppressedFlags.size()); return suppressedFlags[index]; } static WarningOptionMgr* GetInstance() { static WarningOptionMgr ins{}; return &ins; } private: WarningOptionMgr() = default; WarningOptionMgr(const WarningOptionMgr& wom) = delete; WarningOptionMgr& operator=(const WarningOptionMgr& wom) = delete; ~WarningOptionMgr() = default; private: std::vector suppressedFlags = std::vector(static_cast(WarnGroup::NONE), false); }; } // namespace Cangjie #endif // CANGJIE_OPTION_OPTION_H cangjie_compiler-1.0.7/include/cangjie/Option/OptionTable.h000066400000000000000000000257521510705540100237060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the OptionTable related classes. */ #ifndef CANGJIE_OPTION_OPTIONTABLE_H #define CANGJIE_OPTION_OPTIONTABLE_H #include #include #include #include "cangjie/Utils/Utils.h" namespace Cangjie { class Arg; class OptionArgInstance; class ArgList; namespace Options { enum class Kind : uint8_t { UNKNOWN = 0, #define KIND(kind) kind, #include "cangjie/Option/Options.inc" #undef KIND }; enum class Backend : uint8_t { UNKNOWN = 0, #define BACKEND_DEF(backend) backend, #include "cangjie/Option/Options.inc" #undef BACKEND_DEF }; enum class Group : uint8_t { UNKNOWN = 0, #define GROUP_DEF(group) group, #include "cangjie/Option/Options.inc" #undef GROUP_DEF }; enum class ID : uint16_t { #define OPTION(NAME, ID, KIND, BACKENDS, GROUPS, ALIAS, FLAGS, HELPTEXT, OCCURRENCE) ID, #include "cangjie/Option/Options.inc" #undef OPTION }; enum class Occurrence : uint16_t { UNKNOWN = 0, #define OCCURRENCE(occurrence) occurrence, #include "cangjie/Option/Options.inc" #undef OCCURRENCE }; enum class Visibility : uint8_t { VISIBLE, INVISIBLE }; enum class OptionType { GENERAL, EXPERIMENTAL }; struct OptionValue { const std::string value; const std::string help; std::vector backends{0}; std::vector groups{0}; }; } // namespace options /** * Option table to handle raw options information, parse arguments. */ class OptionTable { public: /** * This struct is used to store all information in the definition of the OPTION, whose format is like: * OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, HELPTEXT). */ struct OptionInfo { const std::string name; /**< NAME */ uint16_t id{0}; /**< ID */ Options::Kind kind{0}; /**< KIND */ std::vector backends{0}; /**< Backends the option supports. */ std::vector groups{0}; /**< Groups the option belongs to */ const char* alias{nullptr}; /**< ALIAS */ std::vector values; /**< FLAGS, pre-defined flags list */ Options::Occurrence occurrence; /**< OCCURRENCE, occurrence type, warn multiple time usage or not */ const std::string help; /**< HELPTEXT */ Options::Visibility visible{0}; /**< VISIBILITY */ Options::OptionType optionType = Options::OptionType::GENERAL; Options::ID GetID() const { return static_cast(id); } std::string GetAlias() const { if (alias == nullptr) { return ""; } return std::string(alias); } Options::Kind GetKind() const { return kind; } std::vector GetGroups() const { return groups; } bool BelongsGroup(Options::Group group) const { return std::count(groups.begin(), groups.end(), group) != 0; } bool BelongsToAnyOfGroup(const std::set& targets) const { for (Options::Group target : targets) { if (std::count(groups.begin(), groups.end(), target) != 0) { return true; } } return false; } bool BelongsToBackend(Options::Backend target) const { return std::any_of(backends.begin(), backends.end(), [target](Options::Backend backend) { return backend == Options::Backend::ALL || backend == target; }); } std::string GetName() const { return name; } std::vector GetOptionValues() const { std::vector optionValues{}; auto& opvalues = values; for (Options::OptionValue optionValue : opvalues) { optionValues.emplace_back(optionValue); } return optionValues; } Options::Occurrence GetOccurrenceType() const { return occurrence; } }; /** * @brief The constructor of class OptionTable. * * @param infos The option information vector. * @param frontendMode The true value is frontend mode. * @return OptionTable The instance of OptionTable. */ explicit OptionTable(const std::vector& infos, bool frontendMode) : optionInfos(infos), frontendMode(frontendMode) { enabledGroups.emplace(frontendMode ? Options::Group::FRONTEND : Options::Group::DRIVER); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND enabledBackends.emplace(Options::Backend::CJNATIVE); #endif } /** * @brief The destructor of class OptionTable. */ ~OptionTable() { } /** * @brief Helper function for determining if the objects appear in targets. * * @param targets The target format information. * @param objects The object vector. * @param universal The object kind. * @return bool if yes, true is returned. Otherwise, false is returned. */ template static bool BelongsTo(const std::set& targets, const std::vector& objects, const T universal) { for (const auto& object : objects) { if (object == universal) { return true; } if (std::count(targets.begin(), targets.end(), object) != 0) { return true; } } return false; } /** * @brief Parse all arguments from raw arguments string. Including parsing files and separated options. */ bool ParseArgs(const std::vector& argsStrs, ArgList& argList); /** * @brief Print help text. */ void Usage(Options::Backend backend, std::set groups, bool showExperimental = false); enum class ArgType { FullyParsed, PartiallyParsed, }; /** * @brief Option information table. */ const std::vector optionInfos; private: const static int OPTION_WIDTH = 28; /**< The option total width. */ const bool frontendMode = false; std::set enabledBackends {}; std::set enabledGroups { Options::Group::GLOBAL }; /** * @brief A help function for parsing joined arguments. * The first element of return is argument name and the second element of return is argument value. * Joined argument '-joined-arg=value' has argument name '-joined-arg' and argument value 'value'. * For other type of arguments, argument name is itself, argument value is nullopt. */ std::pair> ParseArgumentName(const std::string& argStr) const; /** * @brief A help method for ParseArgs. This argument parser only process an argument string without any * spaces in it. As a result, ParseOneArg accepts Flag kind and Joined kind Arguments, for example: * -xxx and -yyy=111. * In the case of Separated kind arguments, the argument is parsed partically. We have to get the next * argument to finish parsing a separated argument option. */ std::optional ParseOptionArg(const std::string& argStr); /** * @brief A help method that sets value for an Arg object. * It returns nullopt if given value is invalid for the Arg. */ std::optional SetArgValue( OptionArgInstance& arg, const std::string& value, bool isSeparatedOptionWithoutSpace) const; /** * @brief Consume one or more arguments to get a complete Arg object and insert into given ArgList. */ bool ParseShortTermArgs(const std::vector& argsStrs, size_t& idx, ArgList& argList); bool ParseSeparatedArgValue(OptionArgInstance& arg, const std::vector& argsStrs, size_t& idx) const; bool ShouldArgumentBeRecognized(const std::string& argName, bool hasValue, const OptionTable::OptionInfo& i); void PrintInfo(const OptionInfo& info, Options::Backend backend, bool showExperimental) const; }; enum class ArgInstanceType { Input, Option }; class ArgInstance { public: const ArgInstanceType argInstanceType; /** * The string what user actually input for this option. */ std::string str; virtual ~ArgInstance() = default; protected: ArgInstance(ArgInstanceType argInstanceType) : argInstanceType(argInstanceType) { } }; class InputArgInstance : public ArgInstance { public: /** * The value of input */ const std::string value; InputArgInstance(const std::string& value) : ArgInstance(ArgInstanceType::Input), value(value) { str = value; } ~InputArgInstance() = default; }; class OptionArgInstance : public ArgInstance { public: /** * The metadata of the argument, i.e. which option the argument belongs to. */ const OptionTable::OptionInfo& info; /** * The name of argument. */ std::string name; OptionTable::ArgType argType = OptionTable::ArgType::PartiallyParsed; bool hasJoinedValue = false; /** * The argument of option. For options with no arguments, the value is empty. */ std::string value; OptionArgInstance(const OptionTable::OptionInfo& info, const std::string& name, const std::string& value) : ArgInstance(ArgInstanceType::Option), info(info), name(name), value(value) { } OptionArgInstance(const OptionTable::OptionInfo& info, const std::string& name) : OptionArgInstance(info, name, "") { } ~OptionArgInstance() = default; }; class ArgList { public: std::vector> args; ArgList() { args = std::vector>(); } ~ArgList() = default; void MarkSpecified(const OptionArgInstance& arg) { isSpecified.emplace(arg.info.GetID()); } bool IsSpecified(Options::ID id) const { return isSpecified.find(id) != isSpecified.end(); } void AddInput(const std::string& input) { inputs.emplace_back(input); } std::vector GetInputs() const { return inputs; } void MarkWarned(Options::ID id) { warnedList.emplace(id); } bool IsWarned(Options::ID id) const { return warnedList.find(id) != warnedList.end(); } private: std::unordered_set isSpecified; std::unordered_set warnedList; std::vector inputs; }; std::unique_ptr CreateOptionTable(bool frontendMode = false); } // namespace Cangjie #endif // CANGJIE_OPTION_OPTIONTABLE_H cangjie_compiler-1.0.7/include/cangjie/Option/Options.inc000066400000000000000000001400221510705540100234270ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file lists the configuration for all cjc options, which is included by headers. */ // ------------------------------ A guide to Options.inc ------------------------------ // This file lists all compile options `cjc` supports. Each option listed in the file // has a number of properties. Command line argument parser will check properties and // use different parsing rules on options for different properties. // // Definition of a compile option looks as follows: // OPTION(NAME, ID, KIND, BACKENDS, GROUPS, ALIAS, VALUES, OCCURRENCE, HELPTEXT) // - NAME // is a string type option name, e.g. "--module-name" or "--output" // - ID // is used to identify the option in cpp code, it should be unique in the list // - KIND // is the type of option. For example, "-g" is an option which requires no argument, // "-o" is an option which requires one argument (the output name). See KIND for // details. // - BACKENDS // is a brace enclosed list of BACKEND(...), the options will only be visible and // available in a version of `cjc` that supports given BACKEND(...). See BACKEND_DEF // for supported BACKEND(...). Note that, "," is used as paramter separator in macro // expansion. To be able to separate elements in a list, we defined a special macro, // COMMA (see below), to separate elements in lists. COMMA will be translated to "," // after macro expansion. // - GROUPS // is a brace enclosed list of GROUP(...), different version of `cjc` supports // different group of options, see GROUP_DEF for details. // - ALIAS // is a string type option name, which is an alias of NAME. If ALIAS is defined, // then both NAME and ALIAS could be recognized as option ID. In case that ALIAS is // recognized, for SEPARATED kind options, the argument could follows the ALIAS name // without space in command line. It could be `nullptr` if the option has no alias // name. // - VALUES // is a list of values that the option supports. In case that a SEPARATED kind // option only supports limited number of values, we could use VALUES to defined a // list of supported values. The command line argument parser could give a pretty // print on supported values, and refined parsing control (for different backends or // groups) could be applied on different values. See PREDEFVALUE for details. // - OCCURRENCE // affects the behaviour of parser when user specified the option multiple times in a // command. For example, in case of "-o", which is marked as `SINGLE_OCCURRENCE`, if // user inputs `cjc main.cj -o main -o a.out`, a warning will be printed since `-o main` // is overwritten by `-o a.out`. For FLAG kind options, `MULTIPLE_OCCURRENCE` should be // used since it does not change the semantic of command (in most times). /** * Kinds of option. */ #ifdef KIND // A one-way switch without arguments, e.g. "-g", "-c". KIND(FLAG) KIND(FLAG_WITH_ARG) // An option that takes an argument separated by space, e.g. "--output ./main". If a // SEPARATED kind option is specified by NAME, the argument could be separated by space // or "="; if the options is specified by ALIAS, the argument could be separated by space // or nothing (in other words, the space could be omitted). KIND(SEPARATED) // An option that takes an argument without separator, e.g. "-O". "-O " will not be // recognized. KIND(CONTINOUS) #endif /** * Groups of option. * Groups affect the output of show usage and how parser recognizes options. */ #ifdef GROUP_DEF // Global options, both `cjc` and `cjc-frontend` recognizes. GROUP_DEF(GLOBAL) // Driver options, only `cjc` recognizes. GROUP_DEF(DRIVER) // Frontend options, only `cjc-frontend` recognizes. GROUP_DEF(FRONTEND) // Stable options, or non-experimental options. If an option does not belongs to GROUP(STABLE), // then it must be used with "--experimental" option. GROUP_DEF(STABLE) // Options that will be visible in community distribution (external release). Options that belong // to GROUP(VISIBLE) must be listed on `cjc` user manual. GROUP_DEF(VISIBLE) #endif /** * Backends options support. * An option could belongs to mutiple BACKEND groups. */ #ifdef BACKEND_DEF // For general options that support all backends. BACKEND_DEF(ALL) // The option will be visible and available in `cjc` that supports CJNATIVE BE. BACKEND_DEF(CJNATIVE) #endif /** * Occurrence type of an option in a command. */ #ifdef OCCURRENCE // A warning will be printed if the option occurs multiple times in a command. OCCURRENCE(SINGLE_OCCURRENCE) // No occurrence checking for the option while parsing. OCCURRENCE(MULTIPLE_OCCURRENCE) #endif #ifdef PREDEFVALUE // Note: Use COMMA instead of "," to separate elements in a list #define COMMA , PREDEFVALUE(backend_value, { VALUE("llvm", "compile with cjnative backend (default)", { BACKEND(ALL) }, { GROUP(STABLE) })}); PREDEFVALUE(warn_group, { VALUE("all", "", { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("unused", "", { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("unused-main", "", { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("driver-arg", "", { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("deprecated", "", { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("unsupport-compile-source", "", { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("package-import", "", { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("parser", "", { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("semantics", "", { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("interpreter", "", { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("apilevel-check", "", { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) })}); PREDEFVALUE(diagnostic_value, { VALUE("json", "", { BACKEND(ALL) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("noColor", "", { BACKEND(ALL) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("default", "", { BACKEND(ALL) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) }); PREDEFVALUE(emit_chir_phase, { VALUE("raw", "emit CHIR before compiler optimization", {BACKEND(ALL)}, {GROUP(STABLE) COMMA GROUP(VISIBLE)}) COMMA VALUE("opt", "emit CHIR after compiler optimization (default)", {BACKEND(ALL)}, {GROUP(STABLE) COMMA GROUP(VISIBLE)})}); PREDEFVALUE(chir_mode, { VALUE("standard", "", { BACKEND(ALL) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("withID", "", { BACKEND(ALL) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("all", "", { BACKEND(ALL) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) }); PREDEFVALUE(simple_on_off_mode, { VALUE("on", "", { BACKEND(ALL) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("off", "", { BACKEND(ALL) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) }); PREDEFVALUE(integer_overflow_mode, { VALUE("throwing", "", { BACKEND(ALL) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("wrapping", "", { BACKEND(ALL) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("saturating", "", { BACKEND(ALL) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) }); PREDEFVALUE(sanitizer_type, { VALUE("address", "enable address sanitizer", { BACKEND(CJNATIVE) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("thread", "enable thread sanitizer", { BACKEND(CJNATIVE) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("hwaddress", "enable hardware-assisted address sanitizer", { BACKEND(CJNATIVE) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) }); PREDEFVALUE(stack_trace_format, { VALUE("default", "", { BACKEND(CJNATIVE) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("simple", "", { BACKEND(CJNATIVE) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("all", "", { BACKEND(CJNATIVE) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) }); PREDEFVALUE(output_type_list, { // for CJNATIVE backend VALUE("exe", "emit executable (default)", { BACKEND(CJNATIVE) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("staticlib", "emit static library", { BACKEND(CJNATIVE) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("dylib", "emit dynamic library", { BACKEND(CJNATIVE) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("chir", "emit partially compiled package", { BACKEND(CJNATIVE) }, { GROUP(VISIBLE) }) COMMA #if !defined(CANGJIE_VISIBLE_OPTIONS_ONLY) VALUE("hotreload", "emit dynamic library with hot reload supports", { BACKEND(CJNATIVE) }, { GROUP(VISIBLE) }) #endif }); PREDEFVALUE(lto_mode, { VALUE("full", "", { BACKEND(CJNATIVE) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("thin", "", { BACKEND(CJNATIVE) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) }); PREDEFVALUE(bchir_print_mode, { VALUE("deserialized", "after deserialisation", { BACKEND(CJNATIVE) }, { GROUP(STABLE) }) COMMA VALUE("linked", "after linkage", { BACKEND(CJNATIVE) }, { GROUP(STABLE) }) COMMA VALUE("chir2bchir", "after chir2bchir", { BACKEND(CJNATIVE) }, { GROUP(STABLE) }) COMMA VALUE("interpreter", "interpretation trace", { BACKEND(CJNATIVE) }, { GROUP(STABLE) }) COMMA VALUE("ce-linked", "const eval after linkage", { BACKEND(CJNATIVE) }, { GROUP(STABLE) }) COMMA VALUE("ce-chir2bchir", "const eval after chir2bchir", { BACKEND(CJNATIVE) }, { GROUP(STABLE) }) COMMA VALUE("all", "all of the above", { BACKEND(CJNATIVE) }, { GROUP(STABLE) }) }); PREDEFVALUE(mock_mode, { VALUE("on", "", { BACKEND(ALL) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("off", "", { BACKEND(ALL) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) COMMA VALUE("runtime-error", "", { BACKEND(ALL) }, { GROUP(STABLE) COMMA GROUP(VISIBLE) }) }); #undef COMMA #endif /** * If you are trying to add a new `cjc` option, here is what you need to do. * 1. Add an option definition below. Here is some notes. * OPTION(NAME, ID, KIND, BACKENDS, GROUPS, ALIAS, VALUES, OCCURRENCE, HELPTEXT) * - NAME is your option name, for example "--my-option". NOTE: use DOUBLE HYPHEN (--) * instead of single hyphen (-) for the option prefix if option name has more than * one character. * - ID could be any unique name that could represent your option. * - If your option takes no arguments, then use FLAG for KIND. Use SEPARATED for * KIND otherwise. * - If your option is for all backends, then BACKENDS should be `{ BACKEND(ALL) }`, * otherwise replace ALL with the backend name your option supports. If your option * supports two or more backends, such as CJNATIVE and OTHER, then you could write * `{ BACKEND(CJNATIVE) COMMA BACKEND(OTHER) }`. * - If your option is a temporary option or is not ready to be released, they you * could give your option group `{ GROUP(GLOBAL) }`. If your option is tested and * is listed on `cjc` user manual, they you may mark your option as `GROUP(VISIBLE)`. * - Does your option have a short name? Set ALIAS to your option's one character * short name if it has one, set ALIAS `nullptr` otherwise. * - If your option has SEPARATED kind but only recognizes values from a list of * candidates, then you should consider defining candidates in PREDEFVALUE. `{}` for * VALUES otherwise. * - If you are adding a FLAG kind option, then OCCURRENCE should be MULTIPLE_OCCURRENCE. * If you are adding a SEPARATED kind option, and if the option could not be specified * multiple times in a command (if later one overwrites formers), they OCCURRENCE * should be SINGLE_OCCURRENCE. MULTIPLE_OCCURRENCE otherwise. * - HELPTEXT is a string that describes your option. It will be displayed in `cjc -h`. * 2. Add corresponding data member in Option.h. * 3. Add option trigger logic in Option.cpp. * See g_actions in Option.cpp. * For details, see "A guide to Options.inc" (scolls to top). */ #ifdef OPTION #define COMMA , ////////////////////////////// // Frontend Options: Frontend Group options only work when "-frontend" is enabled. OPTION("--dump-tokens", DUMP_TOKENS, FLAG, { BACKEND(ALL) }, { GROUP(FRONTEND) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Print all of the tokens in one compile unit") OPTION("--dump-parse", DUMP_PARSE, FLAG, { BACKEND(ALL) }, { GROUP(FRONTEND) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Print AST after parser") OPTION("--dump-ast", DUMP_AST, FLAG, { BACKEND(ALL) }, { GROUP(FRONTEND) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Print AST after semantic analysis") OPTION("--dump-symbols", DUMP_SYMBOLS, FLAG, { BACKEND(ALL) }, { GROUP(FRONTEND) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Print symbols after semantic analysis") OPTION("--dump-ir", DUMP_IR, FLAG, { BACKEND(ALL) }, { GROUP(FRONTEND) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Print human readable IR") OPTION("--dump-bc", DUMP_BC, FLAG, { BACKEND(ALL) }, { GROUP(FRONTEND) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Dump machine-readable LLVM bitcode") OPTION("--typecheck", TYPE_CHECK, FLAG, { BACKEND(ALL) }, { GROUP(FRONTEND) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Parse and check type of input file(s)") OPTION("--dump-macro", DUMP_MACRO, FLAG, { BACKEND(ALL) }, { GROUP(FRONTEND) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Dump tokens after macro expansion") OPTION("-d", COMPILE_CJD, FLAG, { BACKEND(ALL) }, { GROUP(FRONTEND) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Compile declaration file(s) (.cj.d)") OPTION("--diagnostic-format", DIAG_FORMAT, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, nullptr, diagnostic_value, SINGLE_OCCURRENCE, "Diagnostic format. Candidate modes: ") OPTION("--backend", BACKEND_MODE, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, backend_value, SINGLE_OCCURRENCE, "Specify backend toolchain") OPTION("--scan-dependency", DUMP_DEPENDENT_PACKAGE, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Get the package(s) which the current package depends on") OPTION("--no-sub-pkg", NO_SUB_PACKAGE, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "The package doesn't have sub-packages") OPTION("--cfg", CONDITIONAL_COMPILATION_CONFIG, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "User defined condition to compile") OPTION("--debug-macro", COMPILE_DEBUG_MACRO, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable debug macro") OPTION("--parallel-macro-expansion", PARALLEL_MACRO_EXPANSION, FLAG, {BACKEND(ALL)}, {GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable parallel macro expansion") OPTION("-g", COMPILE_DEBUG, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable compile debug version target") OPTION("--trimpath", TRIMPATH, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "Remove a specified path prefix in debuginfo") OPTION("--strip-all", STRIP_ALL, FLAG, {BACKEND(CJNATIVE) }, {GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, "-s", {}, MULTIPLE_OCCURRENCE, "Strip the symbol table from executable and dynamic library") OPTION("--no-stacktrace-info", NO_STACKTRACE_INFO, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Do not display the line number and file name information") OPTION("--test", COMPILE_TEST, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable compile test") OPTION("--test-only", COMPILE_TESTS_ONLY, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Compile test files only (`_test.cj`), production parts should be added as a dependency separately") OPTION("--export-for-test", EXPORT_FOR_TESTS, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Export some additional kinds of declaration specifically for using them in tests") OPTION("--mock", MOCK, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, mock_mode, SINGLE_OCCURRENCE, "Specify whether mock features are enabled, or disabled, " "or a runtime exception is thrown when trying to use mock features") OPTION("--output", OUTPUT_FILE, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, "-o", {}, SINGLE_OCCURRENCE, "Specify product name or output directory when compiling a package") OPTION("--output-dir", OUTPUT_DIR, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Specify output directory (it affects '--output' option)") OPTION("--output-javagen-dir", OUTPUT_JAVA_GEN_DIR, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Specify output directory for javagen") OPTION("--no-prelude", NO_PRELUDE, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Not import cangjie std.core package by default, only applicable when compiling std.core") OPTION("--enable-interop-cjmapping", ENABLE_INTEROP_CJMAPPING, FLAG, {BACKEND(CJNATIVE)}, { GROUP(GLOBAL) COMMA GROUP(VISIBLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "enable cj data structure mapping for interop") OPTION("--static", STATIC, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Statically link cangjie library") OPTION("--static-std", STATIC_STD, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Statically link packages of the std module") OPTION("--dy-std", DY_STD, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Dynamically link packages of the std module") OPTION("--static-libs", STATIC_LIBS, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Statically link packages of other modules except std") OPTION("--dy-libs", DY_LIBS, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Dynamically link packages of other modules except std") OPTION("--lto", LTO, SEPARATED, {BACKEND(CJNATIVE)}, {GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, nullptr, lto_mode, SINGLE_OCCURRENCE, "Enable LTO to either 'full' or 'thin' (Not available for Windows target)") OPTION("--compile-as-exe", COMPILE_AS_EXE, FLAG, {BACKEND(CJNATIVE)}, {GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "Hide symbols from bitcode files in LTO mode, and only the package init symbols remain visible") OPTION("--profile-compile-time", PROFILE_COMPILE_TIME, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(VISIBLE) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Print time spent of all phases in the compilation") OPTION("--profile-compile-memory", PROFILE_COMPILE_MEMORY, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(VISIBLE) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Print memory usage of all phases in the compilation") #ifndef DISABLE_EFFECT_HANDLERS OPTION("--enable-eh", ENABLE_EFFECTS, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enables experimental support for effect handlers") #endif OPTION("--dump-chir-debug", DUMP_CHIR_DEBUG, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Dump CHIR readable txt file when compiling for debug") #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND OPTION("--debug-annotations", DUMP_ANNOTATIONS_DEBUG, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Debug computing annotations stage") #endif OPTION("--deserialize-chir-and-dump", DESERIALIZE_CHIR_AND_DUMP, SEPARATED, { BACKEND(ALL) }, { GROUP(FRONTEND) COMMA GROUP(STABLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Deserialize CHIR and dump CHIL to file") OPTION("--emit-chir", EMIT_CHIR, FLAG_WITH_ARG, {BACKEND(ALL)}, {GROUP(GLOBAL) COMMA GROUP(VISIBLE) COMMA GROUP(STABLE)}, nullptr, emit_chir_phase, SINGLE_OCCURRENCE, "emit CHIR serialization file of the specified phase. Candidate phases:") OPTION("--chir-wfc", CHIR_WFC, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, simple_on_off_mode, SINGLE_OCCURRENCE, "Well-formedness check for CHIR. Candidate modes: ") OPTION("--disable-inst", DISABLE_INSTANTIATION, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable instantiation") OPTION("--enable-chir-redundant-getorthrow-elimination", ENABLE_CHIR_REDUNDANT_GETORTHROW_ELIMINATION, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable CHIR redundant GetOrThrow elimination.") OPTION("--disable-chir-useless-import-elimination", DISABLE_CHIR_USELESS_IMPORT_ELIMINATION, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, "--disable-chir-UIE", {}, MULTIPLE_OCCURRENCE, "Disable CHIR useless import elimination") OPTION("--chir-ea", CHIR_EA, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, simple_on_off_mode, SINGLE_OCCURRENCE, "escape analysis for CHIR. Candidate modes: ") OPTION("--fchir-constant-propagation", CONST_PROPAGATION, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable constant propagation optimizaion in CHIR") OPTION("--fno-chir-constant-propagation", NO_CONST_PROPAGATION, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable constant propagation optimizaion in CHIR") OPTION("--fchir-function-inlining", FUNC_INLINING, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable function inlining optimizaion in CHIR") OPTION("--fno-chir-function-inlining", NO_FUNC_INLINING, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable function inlining optimizaion in CHIR") OPTION("--fchir-devirtualization", DEVIRTUALIZATION, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable devirtualization optimizaion in CHIR") OPTION("--fno-chir-devirtualization", NO_DEVIRTUALIZATION, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable devirtualization optimizaion in CHIR") OPTION("--fchir-redundant-return-removal", REDUANDANT_RETURN_REMOVAL, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable redundant return removal in CHIR") OPTION("--fno-chir-redundant-return-removal", NO_REDUANDANT_RETURN_REMOVAL, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable redundant return removal in CHIR") OPTION("--fchir-redundant-future-removal", REDUNDANT_FUTURE_REMOVAL, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable redundant future removal in CHIR") OPTION("--fno-chir-redundant-future-removal", NO_REDUNDANT_FUTURE_REMOVAL, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable redundant future removal in CHIR") OPTION("--fchir-redundant-assign-removal", REDUANDANT_ASSIGN_REMOVAL, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable redundant assign removal in CHIR") OPTION("--fno-chir-redundant-assign-removal", NO_REDUANDANT_ASSIGN_REMOVAL, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable redundant assign removal in CHIR") OPTION("--fchir-loop-invariant-code-motion", LOOP_INVARIANT_CODE_MOTION, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable loop invariant code motion in CHIR") OPTION("--fno-chir-loop-invariant-code-motion", NO_LOOP_INVARIANT_CODE_MOTION, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable loop invariant code motion in CHIR") OPTION("--fchir-sroa", SROA_OPT, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable scalar replacement of aggregates in CHIR") OPTION("--fno-chir-sroa", NO_SROA_OPT, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable scalar replacement of aggregates in CHIR") OPTION("--fchir-switch-opt", SWITCH_OPT, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable switch opt optimizaion in CHIR") OPTION("--fchir-letc-folding", LETC_FOLDING, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable letc folding optimizaion in CHIR") OPTION("--fchir-ref-folding", REF_FOLDING, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable ref folding optimizaion in CHIR") OPTION("--fchir-array-lambda", ARRAY_LAMBDA_OPT, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable array lambda optimizaion in CHIR") OPTION("--sanitizer-coverage-inline-8bit-counters", SANCOV_INLINE_8BIT, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable sanitizer-coverage-inline-8bit-counters in CHIR") OPTION("--sanitizer-coverage-inline-bool-flag", SANCOV_INLINE_BOOL_FLAG, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable sanitizer-coverage-inline-bool-flag in CHIR") OPTION("--sanitizer-coverage-trace-pc-guard", SANCOV_TRACE_PC_GUARD, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable sanitizer-coverage-trace-pc-guard in CHIR") OPTION("--sanitizer-coverage-pc-table", SANCOV_PC_TABLE, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable sanitizer-coverage-pc-table in CHIR") OPTION("--sanitizer-coverage-stack-depth", SANCOV_STACK_DEPTH, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable sanitizer-coverage-stack-depth in CHIR") OPTION("--sanitizer-coverage-trace-compares", SANCOV_TRACE_COMPARES, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable sanitizer-coverage-trace-compares in CHIR") OPTION("--sanitizer-coverage-trace-memcmp", SANCOV_TRACE_MEM_COMPARES, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable sanitizer-coverage-trace-memcmp in CHIR") OPTION("--sanitizer-coverage-level=0", SANCOV_LEVEL_0, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "sancov level 0") OPTION("--sanitizer-coverage-level=1", SANCOV_LEVEL_1, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "sancov level 1") OPTION("--sanitizer-coverage-level=2", SANCOV_LEVEL_2, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "sancov level 2") OPTION("--sanitizer-coverage-level", SANCOV_LEVEL_CUSTOM, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "Set sanitizer-coverage level") OPTION("--disable-sema-vic", DISABLE_SEMA_VIC, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable variable initialization check in SEMA") OPTION("--disable-chir-opt", DISABLE_CHIR_OPT, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable optimizaion in CHIR") OPTION("--disable-backend-opt", DISABLE_BACKEND_OPT, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable optimizaion in Backend") OPTION("--chir-opt-debug", CHIR_OPT_DEBUG, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Debug optimizer") OPTION("--render-chir", RENDER_CHIR, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, chir_mode, SINGLE_OCCURRENCE, "Render CHIR as graph. Candidate modes: ") OPTION("--int-overflow", INT_OVERFLOW_MODE, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, integer_overflow_mode, SINGLE_OCCURRENCE, "Specify default integer overflow strategy: ") OPTION("--fast-math", FAST_MATH_MODE, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable fast-math mode") OPTION("--opt-options", OPT_OPTIONS, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Options directly passed to opt, put the value into \"\" when there is space in it") OPTION("--llc-options", LLC_OPTIONS, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Options directly passed to llc, put the value into \"\" when there is space in it") OPTION("--link-option", LINK_OPTION, SEPARATED, { BACKEND(ALL) }, { GROUP(DRIVER) COMMA GROUP(STABLE) #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND COMMA GROUP(VISIBLE) #endif }, nullptr, {}, MULTIPLE_OCCURRENCE, "An option directly passed to linker") OPTION("--link-options", LINK_OPTIONS, SEPARATED, { BACKEND(ALL) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Options directly passed to linker") OPTION("--library-path", LIBRARY_PATH, SEPARATED, { BACKEND(ALL) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, "-L", {}, MULTIPLE_OCCURRENCE, "Add directory to library search path") OPTION("--library", LIBRARY, SEPARATED, { BACKEND(ALL) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, "-l", {}, MULTIPLE_OCCURRENCE, "Link library") OPTION("--set-runtime-rpath", USE_RUNTIME_RPATH, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Add cangjie runtime directory to rpath") // "-B" is a gcc/clang convention for the option which passes the path of binaries and object files. OPTION("--toolchain", TOOLCHAIN, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, "-B", {}, MULTIPLE_OCCURRENCE, "Use toolchain binaries and object files at the given directory") OPTION("--target", TARGET, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Generate code for the given target platform") OPTION("--target-cpu", TARGET_CPU, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(VISIBLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Generate instructions for the given target processor") OPTION("--sysroot", SYSROOT, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Set the system root directory under which bin, lib and include can be found") OPTION("--output-type", OUTPUT_TYPE, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, output_type_list, SINGLE_OCCURRENCE, "Specify output file type") OPTION("-O0", OPTIMIZATION_0, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Optimization level 0 (default)") OPTION("-O1", OPTIMIZATION_1, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(VISIBLE) COMMA GROUP(STABLE) }, "-O", {}, MULTIPLE_OCCURRENCE, "Optimization level 1") OPTION("-O2", OPTIMIZATION_2, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Optimization level 2") OPTION("-O3", OPTIMIZATION_3, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Optimization level 3") OPTION("-Os", OPTIMIZATION_S, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Optimization level s, like -O2 with extra optimizations for size") OPTION("-Oz", OPTIMIZATION_Z, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Optimization level z, like -Os but reduces code size further") // NOTE: -O must not be moved to the front of -O1(-O) or -O will be treated as an invald option. OPTION("-O", OPTIMIZATION_CUSTOM, CONTINOUS, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Set Optimization level") OPTION("--opt-pass-options", OPT_PASS_OPTIONS, SEPARATED, { BACKEND(CJNATIVE)}, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Set opt pass options") OPTION("--module-name", MODULE_NAME, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Tell compiler name of the module") OPTION("--package", PACKAGE_COMPILE, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, "-p", {}, MULTIPLE_OCCURRENCE, "Specify package directory to be compiled") OPTION("--import-path", IMPORT_PATH, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Add .cjo search path") OPTION("--plugin", PLUGIN_PATH, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Specify the dynamic library path of compiler plugin") OPTION("--common-part-cjo", COMMON_PART_PATH, FLAG_WITH_ARG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(VISIBLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Add .cjo path for common part of package") OPTION("--incremental-compile", INCRE_COMPILE, FLAG, {BACKEND(CJNATIVE)}, {GROUP(GLOBAL) COMMA GROUP(VISIBLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable incremental compilation.") OPTION("--incremental-debug", INCRE_DEBUG, FLAG, {BACKEND(CJNATIVE)}, {GROUP(GLOBAL)}, nullptr, {}, MULTIPLE_OCCURRENCE, "Print debug message during incremental compilation.") OPTION("--save-temps", SAVE_TEMPS, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Save intermediate compilation results. : path to save temp files.") OPTION("--warn-off", WARN_OFF, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, "-Woff", warn_group, MULTIPLE_OCCURRENCE, "Suppress a specific group of warning") OPTION("--warn-on", WARN_ON, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, "-Won", warn_group, MULTIPLE_OCCURRENCE, "Report a specific group of warning") OPTION("--error-count-limit", ERROR_COUNT_LIMIT, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Emit specified of errors only. Available options: all, (8 by default)") OPTION("--verbose", VERBOSE, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, "-V", {}, MULTIPLE_OCCURRENCE, "Enable verbose") OPTION("--version", VERSION, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, "-v", {}, MULTIPLE_OCCURRENCE, "Print compiler version information ") OPTION("--help", HELP, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, "-h", {}, MULTIPLE_OCCURRENCE, "Show usage") OPTION("--compile-macro", COMPILE_MACRO, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Options to compile the macro define package") OPTION("--coverage", ENABLE_COVERAGE, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable coverage") OPTION("--experimental", EXPERIMENTAL, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable experimental options") // ---------- PARALLEL COMPILE OPTIONS ---------- OPTION("--debug-codegen", DEBUG_CODEGEN, FLAG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Debug codeGen mode (execute the codeGen sub-tasks in series and dump debug-ir to file)") OPTION("--jobs", JOBS, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, "-j", {}, SINGLE_OCCURRENCE, "Number of tasks to run at once") OPTION("--aggressive-parallel-compile", AGGRESSIVE_PARALLEL_COMPILE, FLAG_WITH_ARG, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, "--apc", {}, SINGLE_OCCURRENCE, "Enable agrressive parallel compile and specify the number of tasks to run at once") // ---------- SANITIZER OPTIONS ---------- OPTION("--sanitize", SANITIZE, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, sanitizer_type, SINGLE_OCCURRENCE, "Enable sanitizer: ") OPTION("--sanitize-set-rpath", SANITIZE_SET_RPATH, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Add -rpath with cangjie runtime directory to linker flags for sanitizer-enabled product") // ---------- CODE OBFUSCATION OPTIONS ---------- OPTION("--fobf-string", OBF_STRING, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable string literal obfuscation") OPTION("--fno-obf-string", NO_OBF_STRING, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable string literal obfuscation") OPTION("--fobf-const", OBF_CONST, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable constant literal obfuscation") OPTION("--fno-obf-const", NO_OBF_CONST, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable constant literal obfuscation") OPTION("--fobf-layout", OBF_LAYOUT, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable code layout obfuscation") OPTION("--fno-obf-layout", NO_OBF_LAYOUT, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable code layout obfuscation") OPTION("--fobf-cf-flatten", OBF_CF_FLATTEN, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable control flow flatten obfuscation") OPTION("--fno-obf-cf-flatten", NO_OBF_CF_FLATTEN, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable control flow flatten obfuscation") OPTION("--fobf-cf-bogus", OBF_CF_BOGUS, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable control flow bogus obfuscation") OPTION("--fno-obf-cf-bogus", NO_OBF_CF_BOGUS, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable control flow bogus obfuscation") OPTION("--fobf-all", OBF_ALL, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable all obfuscations") OPTION("--fobf-export-symbols", OBF_EXPORT_SYMS, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Obfuscate export symbols when layout obfuscation is enabled") OPTION("--fno-obf-export-symbols", NO_OBF_EXPORT_SYMS, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Don't obfuscate export symbols when layout obfuscation is enabled") OPTION("--obf-sym-input-mapping", OBF_SYM_INPUT_MAPPING, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Specify the input files of symbol mapping for layout obfuscation") OPTION("--obf-sym-output-mapping", OBF_SYM_OUTPUT_MAPPING, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Specify the output file of symbol mapping for layout obfuscation") OPTION("--obf-apply-mapping-file", OBF_MAP_FILE, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Supply user-defined symbol mapping file for layout obfuscation") OPTION("--obf-sym-prefix", OBF_SYM_PREFIX, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Specify the prefix of obfuscated symbols for layout obfuscation") OPTION("--fobf-source-path", OBF_SOURCE_PATH, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Obfuscate source path of symbols when layout obfuscation is enabled") OPTION("--fno-obf-source-path", NO_OBF_SOURCE_PATH, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Don't obfuscate source path of symbols when layout obfuscation is enabled") OPTION("--fobf-line-number", OBF_LINE_NUMBER, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Obfuscate line number of symbols when layout obfuscation is enabled") OPTION("--fno-obf-line-number", NO_OBF_LINE_NUMBER, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Don't obfuscate line number of symbols when layout obfuscation is enabled") OPTION("--obf-config", OBF_CONFIG, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Specify obfuscation configure file") OPTION("--obf-level", OBF_LEVEL, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Specify obfuscation level. Available value: 1 to 9 (5 by default)") OPTION("--obf-seed", OBF_SEED, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Specify random seed for obfuscation algorithm. Available value: ") OPTION("--interp-const-eval-debug", INTERP_CONST_EVAL_DEBUG, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "Enable debug logging in const evaluation") OPTION("--print-bchir", PRINT_BCHIR, SEPARATED, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) }, nullptr, bchir_print_mode, MULTIPLE_OCCURRENCE, "Print BCHIR") OPTION("--disable-codegen", DISABLE_CODEGEN, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE)}, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable code generation") OPTION("--disable-reflection", DISABLE_REFLECTION, FLAG, { BACKEND(ALL) }, { GROUP(GLOBAL) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Disable reflection") #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND OPTION("--stack-trace-format", STACK_TRACE_FORMAT, SEPARATED, {BACKEND(CJNATIVE)}, {GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE)}, nullptr, stack_trace_format, SINGLE_OCCURRENCE, "Specify stack trace format") OPTION("--ffunction-sections", FUNC_SECTIONS, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "enable function-sections") OPTION("--fno-function-sections", NO_FUNC_SECTIONS, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "disable function-sections") OPTION("--fdata-sections", DATA_SECTIONS, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "enable data-sections") OPTION("--fno-data-sections", NO_DATA_SECTIONS, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "disable data-sections") OPTION("--gc-sections", GC_SECTIONS, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "enable gc-sections") OPTION("--no-gc-sections", NO_GC_SECTIONS, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "disable gc-sections") OPTION("--pgo-instr-gen", PGO_INSTR_GEN, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "PGO instrumentation") OPTION("--pgo-instr-use", PGO_INSTR_USE, SEPARATED, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, SINGLE_OCCURRENCE, "Read PGO instrumentation profile") OPTION("--discard-eh-frame", DISCARD_EH_FRAME, FLAG, { BACKEND(CJNATIVE) }, { GROUP(DRIVER) COMMA GROUP(STABLE) COMMA GROUP(VISIBLE) }, nullptr, {}, MULTIPLE_OCCURRENCE, "Discard the eh_frame section") #endif // NOTE: when adding a new option, use DOUBLE HYPHEN (--) instead of single hyphen (-) for the option, // unless it is a single character option or it is a single character abbreviation. #undef COMMA #endif // OPTION cangjie_compiler-1.0.7/include/cangjie/Parse/000077500000000000000000000000001510705540100211045ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/Parse/ASTChecker.h000066400000000000000000000167461510705540100232070ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * */ #ifndef CANGJIE_ASTCHECKER_H #define CANGJIE_ASTCHECKER_H #include #include "cangjie/AST/Node.h" #include "cangjie/Lex/Lexer.h" #define AST_NULLPTR_CHECK(node, f) \ do { \ if ((f) == nullptr) { \ CollectInfo(node, #f); \ } \ } while (0) #define ATTR_NULLPTR_CHECK(node, f) \ do { \ if ((f) == nullptr) { \ CollectInfo(node, #f); \ } \ } while (0) #define ZERO_POSITION_CHECK(node, pos) \ do { \ if ((pos).IsZero()) { \ /** CollectInfo(node, #pos); */ \ } \ } while (0) #define VEC_AST_NULLPTR_CHECK(node, vec) \ do { \ for (auto& f : (vec)) { \ if (f == nullptr) { \ CollectInfo(node, #vec); \ } \ } \ } while (0) #define VEC_ZERO_POS_CHECK(node, vec) \ do { \ for (auto& pos : (vec)) { \ if (pos.IsZero()) { \ /** CollectInfo(node, #vec); */ \ } \ } \ } while (0) #define VEC_EMPTY_STRING_CHECK(node, vec) \ do { \ for (auto& f : (vec)) { \ if (f.empty()) { \ /** CollectInfo(node, #vec); */ \ } \ } \ } while (0) #define EMPTY_STRING_CHECK(node, str) \ do { \ if ((str).empty()) { \ /** CollectInfo(node, #str); */ \ } \ } while (0) #define EMPTY_IDENTIFIER_CHECK(node, id) \ do { \ if ((id).Empty() || (id).ZeroPos()) { \ /** CollectInfo(node, #id); */ \ } \ } while (0) #define EMPTY_VEC_CHECK(node, vec) \ do { \ if ((vec).empty()) { \ /** CollectInfo(node, #vec); */ \ } \ } while (0) namespace Cangjie::AST { class ASTChecker { public: void CheckAST(Node& node); void CheckAST(const std::vector>& pkgs); void CheckBeginEnd(Ptr node); void CheckBeginEnd(const std::vector>& pkgs); private: std::unordered_map)>> checkFuncMap{ #define ASTKIND(KIND, VALUE, NODE, SIZE) {ASTKind::KIND, &ASTChecker::Check##NODE}, #include "cangjie/AST/ASTKind.inc" #undef ASTKIND }; // Function declarations of Checking several kinds of Node #define ASTKIND(KIND, VALUE, NODE, SIZE) void Check##NODE(Ptr node); #include "cangjie/AST/ASTKind.inc" #undef ASTKIND std::set checkInfoSet; void CollectInfo(Ptr node, const std::string& subInfo); void CheckInheritableDecl(Ptr node); }; } // namespace Cangjie::AST #endif // CANGJIE_ASTCHECKER_H cangjie_compiler-1.0.7/include/cangjie/Parse/ASTHasher.h000066400000000000000000000033651510705540100230460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * */ #ifndef CANGJIE_ASTHASHER_H #define CANGJIE_ASTHASHER_H #include #include "cangjie/AST/Node.h" #include "cangjie/Lex/Lexer.h" namespace Cangjie::AST { class ASTHasher { public: // pass global options to ASTHasher before creating any instance of ASTHasher static void Init(const GlobalOptions& op); using hash_type = size_t; static hash_type HashWithPos(Ptr node); static hash_type HashNoPos(Ptr node); static hash_type HashDeclBody(Ptr decl); static hash_type HashDeclSignature(Ptr decl); // hash package specs and import specs of package static hash_type HashSpecs(const Package& pk); static inline size_t CombineHash(const size_t acc, const size_t value) { // 6, 2 are specific constants in the hash algorithm. return acc ^ (value + 0x9e3779b9 + (acc << 6) + (acc >> 2)); } // incr 2.0 static hash_type SigHash(const AST::Decl& decl); static hash_type SrcUseHash(const AST::Decl& decl); // hashAnnos true to consider/ignore the annotations in the hash computation static hash_type BodyHash(const AST::Decl& decl, const std::pair& srcInfo, bool hashAnnos = true); static hash_type ImportedDeclBodyHash(const AST::Decl& decl); static hash_type VirtualHash(const Decl& decl); static hash_type HashMemberAPIs(std::vector>&& memberAPIs); }; } // namespace Cangjie::AST #endif // CANGJIE_ASTHASHER_H cangjie_compiler-1.0.7/include/cangjie/Parse/ParseModifiersRules.h000066400000000000000000000015521510705540100252070ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares provide API to get the checking rules of modifiers. */ #ifndef CANGJIE_PARSE_PARSE_MODIFIERS_RULES_H #define CANGJIE_PARSE_PARSE_MODIFIERS_RULES_H #include "Parser.h" namespace Cangjie { using ModifierRules = std::unordered_map>>; const ModifierRules& GetModifierRulesByDefKind(DefKind defKind); const ModifierRules& GetModifierWarningRulesByDefKind(DefKind defKind); std::optional GetAttributeByModifier(TokenKind tokenKind); } // namespace Cangjie #endif // CANGJIE_PARSE_PARSEMODIFIERS_H cangjie_compiler-1.0.7/include/cangjie/Parse/Parser.h000066400000000000000000000211101510705540100225040ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Parser related classes, which parses the source code to AST. */ #ifndef CANGJIE_PARSE_PARSER_H #define CANGJIE_PARSE_PARSER_H #include "cangjie/AST/NodeX.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Lex/Lexer.h" #include "cangjie/Option/Option.h" namespace Cangjie { enum class ScopeKind : uint8_t { TOPLEVEL = 0, CLASS_BODY, INTERFACE_BODY, STRUCT_BODY, EXTEND_BODY, FUNC_BODY, MACRO_BODY, PRIMARY_CONSTRUCTOR_BODY_FOR_CLASS, PRIMARY_CONSTRUCTOR_BODY_FOR_STRUCT, PROP_MEMBER_SETTER_BODY, PROP_MEMBER_GETTER_BODY, ENUM_BODY, ENUM_CONSTRUCTOR, PRIMARY_CONSTRUCTOR_FUNC_PARAM, MAIN_BODY, UNKNOWN_SCOPE }; // ExprKind means the expected expr in parseExpr. // e.g. VAR_INIT stands for all those expr who can be var initializer. enum class ExprKind : uint8_t; std::string ConvertToken(const Token& t); /// Record the correspondence between AnnotationKinds and identifiers. const std::unordered_map NAME_TO_ANNO_KIND = { {"JavaMirror", AST::AnnotationKind::JAVA_MIRROR}, {"JavaImpl", AST::AnnotationKind::JAVA_IMPL}, {"ObjCMirror", AST::AnnotationKind::OBJ_C_MIRROR}, {"ObjCImpl", AST::AnnotationKind::OBJ_C_IMPL}, {"ForeignName", AST::AnnotationKind::FOREIGN_NAME}, {"CallingConv", AST::AnnotationKind::CALLING_CONV}, {"C", AST::AnnotationKind::C}, {"Attribute", AST::AnnotationKind::ATTRIBUTE}, {"Intrinsic", AST::AnnotationKind::INTRINSIC}, {"OverflowThrowing", AST::AnnotationKind::NUMERIC_OVERFLOW}, {"OverflowWrapping", AST::AnnotationKind::NUMERIC_OVERFLOW}, {"OverflowSaturating", AST::AnnotationKind::NUMERIC_OVERFLOW}, {"When", AST::AnnotationKind::WHEN}, {"FastNative", AST::AnnotationKind::FASTNATIVE}, {"Annotation", AST::AnnotationKind::ANNOTATION}, {"ConstSafe", AST::AnnotationKind::CONSTSAFE}, {"Deprecated", AST::AnnotationKind::DEPRECATED}, {"Frozen", AST::AnnotationKind::FROZEN}, {"EnsurePreparedToMock", AST::AnnotationKind::ENSURE_PREPARED_TO_MOCK}}; bool IsBuiltinAnnotation(const std::string& moduleName, const std::string& identifier); inline bool IsIdentifierOrContextualKeyword(const TokenKind& kind) { return kind == TokenKind::IDENTIFIER || Utils::In(GetContextualKeyword(), [&kind](const TokenKind& tokenKind) { return kind == tokenKind; }); } template using PtrVector = std::vector>; /// The main class used for parsing. class Parser { public: /// Create Parser with \ref fileID and string \ref input. /// \param parseDeclFile true if this parser is used to parse .cj.d file. When parsing these files, do not report /// an error if the func is missing its body. Parser(unsigned int fileID, const std::string& input, DiagnosticEngine& diag, SourceManager& sm, bool attachComment = false, bool parseDeclFile = false); /// Create Parser with string \ref input and the position of the first token. Only used in macro reparse. Parser(const std::string& input, DiagnosticEngine& diag, SourceManager& sm, const Position& pos = {0, 1, 1}, bool attachComment = false, bool parseDeclFile = false); /// Create Parser with tokens \ref inputTokens returned by macro func. Only used in macro reparse. Parser(const std::vector& inputTokens, DiagnosticEngine& diag, SourceManager& sm, bool attachComment = false, bool parseDeclFile = false); ~Parser(); size_t GetLineNum() const; // Frontend/Macro/Sema. OwnedPtr ParseTopLevel(); // Macro/Sema/stdlib. OwnedPtr ParseDecl(ScopeKind scopeKind); /// Parse expression entrance. No context info provided. This function is public only for testing. OwnedPtr ParseExpr(); /// ParseExpr entrance from libast. The scope of this expr is unknown as it lacks a parsing context. OwnedPtr ParseExprLibast(); // unittests. OwnedPtr ParseType(); OwnedPtr ParsePattern(); /// Parse annotation arguments from unprocessed tokens, and put them into \ref anno /// Used by fmt. void ParseAnnotationArguments(AST::Annotation& anno) const; OwnedPtr ParseCustomAnnotation() const; // Macro. std::vector> ParseNodes(std::variant scope, AST::Node& currentMacroCall, const std::set& modifiers = {}, std::vector> annos = {}); Parser& EnableCustomAnno(); std::size_t GetProcessedTokens() const; std::string GetPrimaryDeclIdentRawValue() const; Parser& SetEHEnabled(bool enabled); bool IsEHEnabled() const; Parser& SetModuleName(const std::string& name); Parser& SetPrimaryDecl(const std::string& decl); Parser& SetForImport(bool isForImport); Parser& SetCurFile(Ptr curFile); void SetCompileOptions(const GlobalOptions& opts); DiagnosticEngine& GetDiagnosticEngine() const; TokenVecMap GetCommentsMap() const; /// Issue a diagnostic. If the parsing context is in a macro call, consider it. template DiagnosticBuilder ParseDiagnoseRefactor(DiagKindRefactor kind, const Position pos, Args&&... args) { auto n = MakeOwned(); n->begin = pos; n->end = pos + 1; if (CurMacroCall()) { n->EnableAttr(AST::Attribute::MACRO_EXPANDED_NODE); n->curMacroCall = CurMacroCall(); } return GetDiagnosticEngine().DiagnoseRefactor(kind, *n, pos, std::forward(args)...); } template DiagnosticBuilder ParseDiagnoseRefactor(DiagKindRefactor kind, const Range range, Args&&... args) { auto n = MakeOwned(); n->begin = range.begin; n->end = range.end; if (CurMacroCall()) { n->EnableAttr(AST::Attribute::MACRO_EXPANDED_NODE); n->curMacroCall = CurMacroCall(); } return GetDiagnosticEngine().DiagnoseRefactor(kind, *n, range, std::forward(args)...); } template DiagnosticBuilder ParseDiagnoseRefactor(DiagKindRefactor kind, const Token& token, Args&&... args) { auto n = MakeOwned(); n->begin = token.Begin(); n->end = token.End(); if (CurMacroCall()) { n->EnableAttr(AST::Attribute::MACRO_EXPANDED_NODE); n->curMacroCall = CurMacroCall(); } return GetDiagnosticEngine().DiagnoseRefactor(kind, *n, token, std::forward(args)...); } template DiagnosticBuilder ParseDiagnoseRefactor(DiagKindRefactor kind, const AST::Node& node, Args&&... args) { auto n = MakeOwned(); n->begin = node.begin; n->end = node.end; if (CurMacroCall()) { n->EnableAttr(AST::Attribute::MACRO_EXPANDED_NODE); n->curMacroCall = CurMacroCall(); } return GetDiagnosticEngine().DiagnoseRefactor(kind, *n, std::forward(args)...); } /// Skip next token if token kind satisfied bool Skip(TokenKind kind); protected: /// used by QueryParser /// Peek next token, save it to lookAhead const Token& Peek(); /// Consume next token, put a sentinel symbol on it void Next(); /// Whether the next token is the given token kind. bool Seeing(TokenKind kind); bool Seeing(TokenKind rangeLeft, TokenKind rangeRight); bool SeeingAny(const std::vector& kinds); /// Whether the following tokens are the given token vector. 'skipNL=true' means NL token should be skipped when /// scan following tokens. bool Seeing(const std::vector& kinds, bool skipNewline = true); /// See several tokens without space between them, skip all the tokens except the last token. bool SeeingCombinator(const std::vector& kinds); /// Whether the next token is of the given token type and follows the given combined token bool SeeingTokenAndCombinator(TokenKind kind, const std::vector& cmb); /// Skip tokens described by \ref kinds if possible. void SkipCombinator(const std::vector& kinds); const Token& LookAhead() const; const Token& LastToken() const; Ptr CurMacroCall() const; private: class ParserImpl* impl; }; } // namespace Cangjie #endif // CANGJIE_PARSE_PARSER_H cangjie_compiler-1.0.7/include/cangjie/Sema/000077500000000000000000000000001510705540100207175ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/Sema/CommonTypeAlias.h000066400000000000000000000135051510705540100241400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares some type alias used by the semantic check modules. */ #ifndef CANGJIE_SEMA_COMMONTYPEALIAS_H #define CANGJIE_SEMA_COMMONTYPEALIAS_H #include "cangjie/AST/Node.h" #include "cangjie/AST/Types.h" #include "cangjie/Utils/PartiallyPersistent.h" namespace Cangjie { enum class BlameStyle { ARGUMENT, RETURN, CONSTRAINT }; struct Blame { Ptr src; Ptr lb; Ptr ub; BlameStyle style{BlameStyle::ARGUMENT}; bool operator<(const Blame& rhs) const { if (!rhs.src) { return false; } if (!src) { return true; } return src->begin < rhs.src->begin || (src->begin == rhs.src->begin && src->end < rhs.src->end); } }; using AST::TyVar; using TyVars = std::set>; using TyVarUB = std::map, std::set>>; using LowerBounds = PSet>; using UpperBounds = PSet>; template inline TyVars StaticToTyVars(C tys) { TyVars ret; for (auto ty : tys) { ret.emplace(static_cast(ty.get())); } return ret; } struct TyVarBounds { LowerBounds lbs{}; UpperBounds ubs{}; // must pick from one of these types, will kick out elements as more info becomes available // when initialized: will have a single element Any UpperBounds sum{}; // may greedily decide a ty var's solution, should have only 0 or 1 element : the solution // PSet here only to reuse its backtracking PSet> eq{}; // currently don't track source for non-local ty var solving failure, // therefore don't need backtrackable data structure for blames std::map, std::set> lb2Blames; std::map, std::set> ub2Blames; std::string ToString() const; }; using Constraint = std::map, TyVarBounds>; using Constraints = std::vector; // Substitution from type variables to types. using TypeSubst = std::map, Ptr>; using TypeSubsts = std::set; // Substitution from type variables to their multiple instantiated types. // Eg: Given interface I3 and class C <: I3 & I3, then [Ti |-> [T, V]] using MultiTypeSubst = std::map, std::set>>; std::string ToStringC(const Constraint& c); std::string ToStringS(const TypeSubst& m); std::string ToStringMS(const MultiTypeSubst& m); /* * Maps possibly needed to instantiate any type. * * Theoretically, there should be the following 3 levels of mapping: * u2i map: map from universal ty var to instance ty var, should be applied first * promote maps: map from ty var of supertype to types consisting of ty var of subtype, * should be applied in order, after applying u2i map * inst map: map from instance ty var to type argument, should be applied last * * An example: * open class A { func foo(x: T, y: S) {} } * open class B <: A> {} * class C <: B> {} * let v = C().foo(Some([1]), 2). * * For the instance of `foo` in the last line, the u2i map is: * [T |-> T', R |-> R', U |-> U', S |-> S'] * where T', R', U', S' are instance ty vars, which are placeholders for real type args. * * The promote maps are: * [[T' |-> Option], * [R' |-> Array]] * * The inst map is: * [U' |-> Int] * * foo's declared type is: * (T, S)->Unit * after applying u2i map: * (T', S')->Unit * after applying promote maps one by one * (Option, S')->Unit * (Option>, S')->Unit * after applying inst map: * (Option>, S')->Unit * * Now only S' is left to be solved, the solution of which is obviously Int. * Then we add this solution to inst map: * [U' |-> Int, S' |-> Int] * * So foo's instantiated type is: * (Option>, Int)->Unit * * promote maps and inst map can be merged into one if needed, * since there can't be loop in these substitutions. * But u2i map shouldn't be merged with the rest, * since its domain can overlap with the range of other substituions. * e.g. * func f():Unit { * f() // here u2i map:[T |-> T'], inst map: [T' |-> T] * } * * In this struct, `inst` represents promote maps and inst map merged. */ struct SubstPack { TypeSubst u2i; MultiTypeSubst inst; }; std::string ToStringP(const SubstPack& m); struct StableTyCmp { bool operator ()(const Ptr ty1, const Ptr ty2) const { return AST::CompTyByNames(ty1, ty2); } }; using StableTys = std::set, StableTyCmp>; using StableTyVars = std::set, StableTyCmp>; enum class SolvingErrStyle { DEFAULT, NO_CONSTRAINT, CONFLICTING_CONSTRAINTS, ARG_MISMATCH, RET_MISMATCH }; struct SolvingErrInfo { SolvingErrStyle style = SolvingErrStyle::DEFAULT; Ptr tyVar; std::vector> lbs; std::vector> ubs; // in case of conflicting constraints, first blames for lbs, then blames for ubs std::vector> blames; }; using ErrOrSubst = std::variant; // tyvar -> lb X ub X sum X eq using CstVersionID = std::map, std::tuple>; template<> void PData::Commit(Constraint& data); template<> void PData::Reset(Constraint& data); template<> CstVersionID PData::Stash(Constraint& data); template<> void PData::Apply(Constraint& data, CstVersionID& version); template<> void PData::ResetSoft(Constraint& data); } // namespace Cangjie #endif // CANGJIE_SEMA_COMMONTYPEALIAS_H cangjie_compiler-1.0.7/include/cangjie/Sema/Desugar.h000066400000000000000000000010721510705540100224620ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Desugar function. */ #ifndef CANGJIE_SEMA_DESUGAR_H #define CANGJIE_SEMA_DESUGAR_H #include "cangjie/AST/Node.h" namespace Cangjie { using namespace AST; void PerformDesugarBeforeTypeCheck(Node& root, bool desugarMacrocall = false); } // namespace Cangjie #endif cangjie_compiler-1.0.7/include/cangjie/Sema/GenericInstantiationManager.h000066400000000000000000000046471510705540100265170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * GenericInstantiationManager is the global manager to maintain the generic information. */ #ifndef CANGJIE_SEMA_GENERIC_INSTANTIATION_MANAGER_H #define CANGJIE_SEMA_GENERIC_INSTANTIATION_MANAGER_H #include "cangjie/AST/Node.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Sema/CommonTypeAlias.h" namespace Cangjie { /** GenericInfo contains the information of the generic decl used to identify a generic decl when instantiated. */ struct GenericInfo { Ptr decl; /**< The raw generic decl. */ TypeSubst gTyToTyMap; /**< The map between the generic ty to instantiated ty. */ GenericInfo(const Ptr decl, const TypeSubst& map) : decl(decl), gTyToTyMap(map) { } }; /** * The class of generic instantiation manager is a global manager that * maintains cache of generic and instantiated decls' information. */ class GenericInstantiationManager { public: explicit GenericInstantiationManager(CompilerInstance& ci); ~GenericInstantiationManager(); /** Generic instantiation package entrance. */ void GenericInstantiatePackage(AST::Package& pkg) const; /** * Get the instantiated decl corresponding to the genericInfo: * @param genericInfo [in] generic decl instantiation parameters. * @param pkg [in] current processing package. MUST given, if call this api outside genericInstantiation step. */ Ptr GetInstantiatedDeclWithGenericInfo(const GenericInfo& genericInfo, AST::Package& pkg) const; /** Get set of instantiated decl of given generic decl */ std::unordered_set> GetInstantiatedDecls(const AST::Decl& genericDecl) const; /** Prepare for generic instantiation processing: * 1. clear all cache generated before. * 2. pre-build context cache. */ void ResetGenericInstantiationStage() const; std::unordered_map, std::unordered_set>> GetAllGenericToInsDecls() const; friend class MockUtils; private: class InstantiatedExtendRecorder; class GenericInstantiationManagerImpl; std::unique_ptr impl; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/include/cangjie/Sema/IncrementalUtils.h000066400000000000000000000026471510705540100243630ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the helper functions used for incremental semantic checking. */ #ifndef CANGJIE_SEMA_INCREMENTAL_UTILS_H #define CANGJIE_SEMA_INCREMENTAL_UTILS_H #include #include "cangjie/AST/Node.h" #include "cangjie/IncrementalCompilation/CompilationCache.h" namespace Cangjie::Sema { void MarkIncrementalCheckForCtor(const std::unordered_set>& declsToBeReCompiled); std::unordered_set> CollectChangedStructTypes( const AST::Package& pkg, const std::unordered_set>& declsToBeReCompiled); void HandleCtorForIncr(const AST::Package& pkg, std::map>& mangledName2DeclMap, SemanticInfo& usageCache); std::string GetTypeRawMangleName(const AST::Ty& ty); void CollectCompilerAddedDeclUsage(const AST::Package& pkg, SemanticInfo& usageCache); void CollectRemovedMangles(const std::string& removed, SemanticInfo& semaInfo, std::unordered_set& removedMangles); void CollectRemovedManglesForReCompile( const AST::Decl& changed, SemanticInfo& semaInfo, std::unordered_set& removedMangles); } // namespace Cangjie::Sema #endif cangjie_compiler-1.0.7/include/cangjie/Sema/TestManager.h000066400000000000000000000071651510705540100233130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the TypeManager related classes, which manages all types. */ #ifndef CANGJIE_SEMA_TEST_MANAGER_H #define CANGJIE_SEMA_TEST_MANAGER_H #include "cangjie/AST/Walker.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Option/Option.h" #include "cangjie/Sema/GenericInstantiationManager.h" #include "cangjie/Sema/TypeManager.h" namespace Cangjie { class MockManager; class MockSupportManager; class MockUtils; enum class MockKind : uint8_t { PLAIN_MOCK, SPY, NOT_MOCK }; class TestManager { public: explicit TestManager( ImportManager& im, TypeManager& tm, DiagnosticEngine& diag, const GlobalOptions& compilationOptions ); void PreparePackageForTestIfNeeded(AST::Package& pkg); void MarkDeclsForTestIfNeeded(std::vector> pkgs) const; static bool IsDeclOpenToMock(const AST::Decl& decl); static bool IsDeclGeneratedForTest(const AST::Decl& decl); static bool IsMockAccessor(const AST::Decl& decl); void Init(GenericInstantiationManager* instantiationManager); ~TestManager(); private: ImportManager& importManager; TypeManager& typeManager; DiagnosticEngine& diag; const bool testEnabled; MockMode mockMode; const bool mockCompatibleIfNeeded; const bool mockCompatible; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND const bool exportForTest; #endif OwnedPtr mockManager {nullptr}; OwnedPtr mockSupportManager {nullptr}; Ptr mockUtils {nullptr}; Ptr GenerateMockClassIfNeededAndGet(const AST::CallExpr& callExpr, AST::Package& pkg); void GenerateAccessors(AST::Package& pkg); void ReplaceCallsWithAccessors(AST::Package& pkg); void ReplaceCallsToForeignFunctions(AST::Package& pkg); void HandleMockCalls(AST::Package& pkg); Ptr GetInstantiatedDeclInCurrentPackage(const Ptr classLikeToMockTy); void CheckIfNoMockSupportDependencies(const AST::Package& curPkg); bool IsThereMockUsage(AST::Package& pkg) const; static bool ArePackagesMockSupportConsistent( const AST::Package& currentPackage, const AST::Package& importedPackage); AST::VisitAction HandleCreateMockCall(AST::CallExpr& callExpr, AST::Package& pkg); void WrapWithRequireMockObjectIfNeeded(Ptr expr, Ptr target); AST::VisitAction HandleMockAnnotatedLambda(const AST::LambdaExpr& lambda); void ReportDoesntSupportMocking(const AST::Expr& reportOn, const std::string& name, const std::string& package); void ReportUnsupportedType(const AST::Expr& reportOn); void ReportNotInTestMode(const AST::Expr& reportOn); void ReportMockDisabled(const AST::Expr& reportOn); void ReportWrongStaticDecl(const AST::Expr& reportOn); void PrepareDecls(AST::Package& pkg); void PrepareToSpy(AST::Package& pkg); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void ReportFrozenRequired(const AST::FuncDecl& reportOn); void MarkMockCreationContainingGenericFuncs(AST::Package& pkg) const; bool ShouldBeMarkedAsContainingMockCreationCall( const AST::CallExpr& callExpr, const Ptr enclosingFunc) const; void HandleDeclsToExportForTest(std::vector> pkgs) const; #endif }; } // namespace Cangjie #endif // CANGJIE_SEMA_TYPE_MANAGER_H cangjie_compiler-1.0.7/include/cangjie/Sema/TypeChecker.h000066400000000000000000000050761510705540100233060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the TypeChecker related classes, which provides typecheck capabilities. */ #ifndef CANGJIE_SEMA_TYPECHECKER_H #define CANGJIE_SEMA_TYPECHECKER_H #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Node.h" #include "cangjie/Frontend/CompilerInstance.h" namespace Cangjie { class InstCtxScope; class TypeChecker { public: explicit TypeChecker(CompilerInstance* ci); ~TypeChecker(); /** * Using control statement "for" to finish packages' typecheck. It invokes two functions as followed. * @see PrepareTypeCheck * @see TypeCheck */ void TypeCheckForPackages(const std::vector>& pkgs) const; void SetOverflowStrategy(const std::vector>& pkgs) const; /** * Perform autobox and recursive type resolving of enum. */ void PerformDesugarAfterInstantiation(ASTContext& ctx, AST::Package& pkg) const; // Desugar after sema. void PerformDesugarAfterSema(const std::vector>& pkgs) const; /** * Synthesize the given @p expr in given @p scopeName and return the found candidate decls or types. * If the @p hasLocal is true, the target will be found from local scope firstly. * @param ctx cached sema context. * @param scopeName the scopeName of current position. * @param expr the expression waiting to found candidate decls or types. * @param hasLocalDecl whether the given expression is existed in the given scope. * @return found candidate decls or types. */ Candidate SynReferenceSeparately( ASTContext& ctx, const std::string& scopeName, AST::Expr& expr, bool hasLocalDecl) const; /** * Remove members from @p targets that do not satisfy extensions of generic instantiated types. * @param baseTy specialized extended type. * @param targets all candidate members. */ void RemoveTargetNotMeetExtendConstraint(const Ptr baseTy, std::vector>& targets); private: friend class InstCtxScope; /** * The class used to synthesize fuzzy results for LSP usage. */ class Synthesizer; /** The class for checking enum sugar. */ class EnumSugarChecker; class TypeCheckerImpl; std::unique_ptr impl; }; // class TypeChecker } // namespace Cangjie #endif cangjie_compiler-1.0.7/include/cangjie/Sema/TypeManager.h000066400000000000000000000652621510705540100233170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the TypeManager related classes, which manages all types. */ #ifndef CANGJIE_SEMA_TYPE_MANAGER_H #define CANGJIE_SEMA_TYPE_MANAGER_H #include #include #include #include "cangjie/AST/Node.h" #include "cangjie/AST/Types.h" #include "cangjie/Sema/CommonTypeAlias.h" #include "cangjie/Utils/Utils.h" namespace Cangjie { enum class TypeCompatibility { INCOMPATIBLE, SUBTYPE, IDENTICAL }; const std::string FUTURE_TYPE_NAME = "Future"; const size_t INT32_SIZE = 4; #define TYPE_PRIMITIVE_MIN AST::TypeKind::TYPE_UNIT #define TYPE_PRIMITIVE_MAX AST::TypeKind::TYPE_BOOLEAN class TyVarScope; class InstCtxScope; class TypeManager { public: TypeManager(); ~TypeManager(); // Primitive types. static Ptr GetPrimitiveTy(AST::TypeKind kind); static Ptr GetInvalidTy() { return &theInvalidTy; } static Ptr GetNothingTy() { return GetPrimitiveTy(AST::TypeKind::TYPE_NOTHING); } static Ptr GetQuestTy() { return &theQuestTy; } static Ptr GetCStringTy() { return &theCStringTy; } /** * Check if there are generic types in ty. */ static Ptr GetNonNullTy(Ptr ty); static bool IsCoreFutureType(const AST::Ty& ty); /** APIs to generate new type or get existed type from cache. */ Ptr GetGenericsTy(AST::GenericParamDecl& gpd); Ptr GetEnumTy(AST::EnumDecl& ed, const std::vector>& typeArgs = {}); Ptr GetRefEnumTy(AST::EnumDecl& ed, const std::vector>& typeArgs); Ptr GetStructTy(AST::StructDecl& sd, const std::vector>& typeArgs); Ptr GetTupleTy(const std::vector>& typeArgs, bool isClosureTy = false); Ptr GetFunctionTy(const std::vector>& paramTys, Ptr retTy, AST::FuncTy::Config cfg = {false, false, false, false}); Ptr GetArrayTy(Ptr elemTy, unsigned int dims); Ptr GetVArrayTy(AST::Ty& elemTy, int64_t size); Ptr GetPointerTy(Ptr elemTy); Ptr GetArrayTy(); Ptr GetClassTy(AST::ClassDecl& cd, const std::vector>& typeArgs); Ptr GetClassThisTy(AST::ClassDecl& cd, const std::vector>& typeArgs); Ptr GetInterfaceTy(AST::InterfaceDecl& id, const std::vector>& typeArgs); Ptr GetTypeAliasTy(AST::TypeAliasDecl& tad, const std::vector>& typeArgs); Ptr GetIntersectionTy(const std::set>& tys); Ptr GetUnionTy(const std::set>& tys); Ptr GetAnyTy() { return anyTy; } Ptr GetCTypeTy() const { // `ctypeTy` is nullptr only when the core package of the standard library does not exist. // In this case, `anyTy` is used as a placeholder. return ctypeTy ? ctypeTy : anyTy; } void SetSemaAnyTy(Ptr semAnyTy) { anyTy = semAnyTy; } void SetSemaCTypeTy(Ptr semCTypeTy) { ctypeTy = semCTypeTy; } /** * Instantiate the @p ty of a node, change the key in @p typeMapping, to the value in @p typeMapping. * If a substituted result type is appeared in @p ctxVars * then this type is considered as fully qualified and cannot be substituted to other type. * @return the instantiated type. */ std::set> GetInstantiatedTys(Ptr ty, const MultiTypeSubst& mts); Ptr GetBestInstantiatedTy(Ptr ty, const MultiTypeSubst& mts); Ptr GetInstantiatedTy(Ptr ty, const TypeSubst& typeMapping); std::set> ApplyTypeSubstForTys(const TypeSubst& subst, const std::set>& tys); Ptr ApplySubstPack(const Ptr declaredTy, const SubstPack& maps, bool ignoreUnsolved = false); std::set> ApplySubstPackNonUniq( const Ptr declaredTy, const SubstPack& maps, bool ignoreUnsolved = false); /** instantiate using the current InstCtxScope */ Ptr InstOf(Ptr ty); /** recover instance ty vars back to universal ty vars for err reporting */ Ptr RecoverUnivTyVar(Ptr ty); SubstPack GetInstMapping(); void PackMapping(SubstPack& maps, const MultiTypeSubst m); void PackMapping(SubstPack& maps, const TypeSubst m); void PackMapping(SubstPack& maps, AST::GenericsTy& tv, AST::Ty& instTy); /* * Flatten the two-step substitution of SubstPack into one-step. * Mainly for places where updating to SubstPack is too much work. * An example: * input: u2i = [T |-> T'], inst = [T' |-> {Int64 | String}] * output: [T |-> {Int64 | String}] */ MultiTypeSubst ZipSubstPack(const SubstPack& mapping); void MakeInstTyVar(SubstPack& maps, AST::GenericsTy& uTv); void MakeInstTyVar(SubstPack& maps, const AST::Decl& d); std::vector> GetTypeArgs(const AST::Ty& ty); Ptr GetBlockRealTy(const AST::Block& block) const; Ptr GetRealExtendedTy(AST::Ty& child, const AST::Ty& interfaceTy); /** * Get all super type of @p ty. */ std::unordered_set> GetAllSuperTys( AST::Ty& ty, const TypeSubst& typeMapping = {}, bool withExtended = true); /** * Use this instead of GetAllSuperTys whenever possible. */ bool HasSuperTy(AST::Ty& ty, AST::Ty& superTy, const TypeSubst& typeMapping, bool withExtended = true); std::unordered_set> GetAllCommonSuperTys(const std::unordered_set>& tys); TypeSubst GetSubstituteMapping(const AST::Ty& nominalTy, const TypeSubst& typeMapping); std::vector> GetAllSuperInterfaceTysBFS(const AST::InheritableDecl& decl); /** APIs to check type relations. */ bool IsSubtype(Ptr leaf, Ptr root, bool implicitBoxed = true, bool allowOptionBox = true); bool IsFuncSubtype(const AST::Ty& leaf, const AST::Ty& root); bool IsFuncParametersSubtype(const AST::FuncTy& leaf, const AST::FuncTy& root); bool IsTupleSubtype(const AST::Ty& leaf, const AST::Ty& root); bool IsTyEqual(Ptr subTy, Ptr baseTy); bool IsPlaceholderEqual(AST::Ty& leaf, AST::Ty& root); bool IsLitBoxableType(Ptr leaf, Ptr root); bool HasExtensionRelation(AST::Ty& childTy, AST::Ty& interfaceTy); /** * Check if two function types have the same parameter type. This is used for checking function overloading and * function overriding. */ bool IsFuncParameterTypesIdentical(const AST::FuncTy& t1, const AST::FuncTy& t2); /** * Check if two set of param types are same. * Firstly, substituting @p paramTys1 with the @p typeMapping and then checking with @p paramTys2 . */ bool IsFuncParameterTypesIdentical(const std::vector>& paramTys1, const std::vector>& paramTys2, const TypeSubst& typeMapping = {}); TypeCompatibility CheckTypeCompatibility( Ptr lvalue, Ptr rvalue, bool implicitBoxed = true, bool isGeneric = false); bool CheckGenericDeclInstantiation(Ptr d, const std::vector>& typeArgs); bool CheckExtendWithConstraint(const AST::Ty& ty, Ptr extend); /** * Use the base ty of memberAccess to get the map of all the generic ty to instant ty, use to handle generic param * pass. */ void GenerateStructDeclGenericMapping(MultiTypeSubst& m, const AST::InheritableDecl& decl, const AST::Ty& targetTy); void GenerateTypeMappingForUpperBounds(MultiTypeSubst& m, const AST::MemberAccess& ma, AST::Decl& target); void GenerateTypeMappingForUpperBounds(SubstPack& m, const AST::MemberAccess& ma, AST::Decl& target); void GenerateGenericMapping(MultiTypeSubst& m, AST::Ty& baseType); void GenerateGenericMapping(SubstPack& m, AST::Ty& baseType); TypeSubst GenerateGenericMappingFromGeneric(const AST::Decl& parentDecl, const AST::Decl& childDecl) const; MultiTypeSubst GenerateStructDeclTypeMapping(const AST::Decl& decl); void ReplaceIdealTy(Ptr* ty); void RestoreJavaGenericsTy(AST::Decl& decl) const; /** * Get all extend interface types. */ std::unordered_set> GetAllExtendInterfaceTy(AST::Ty& ty); bool HasExtendedInterfaceTy(AST::Ty& ty, AST::Ty& superTy, const TypeSubst& typeMapping); /** Get the ty used for getting related extends. */ Ptr GetTyForExtendMap(AST::Ty& ty); /** Get builtin ty extends. For array ty & cpointer ty, will only return instantiated extends. */ std::set> GetBuiltinTyExtends(AST::Ty& ty); std::optional GetOverrideCache( const AST::FuncDecl* src, const AST::FuncDecl* target, AST::Ty* baseTy, AST::Ty* expectInstParent); void AddOverrideCache( const AST::FuncDecl& src, const AST::FuncDecl& target, AST::Ty* baseTy, AST::Ty* expectInstParent, bool val); /** Get extends for given generic/instantiated @p decl */ std::set> GetDeclExtends(const AST::InheritableDecl& decl); /** Get origin extends for given builtin ty. For array ty & cpointer ty, will only return generic extends.*/ std::set> GetAllExtendsByTy(AST::Ty& ty); std::unordered_set> GetAllExtendedDecls(); std::unordered_set> GetAllExtendedBuiltIn(); void UpdateBuiltInTyExtendDecl(AST::Ty& builtinTy, AST::ExtendDecl& ed); void RecordUsedExtend(AST::Ty& child, AST::Ty& interfaceTy); void RecordUsedGenericExtend(AST::Ty& boxedTy, Ptr extend = nullptr); void RemoveExtendFromMap(AST::ExtendDecl& ed); std::unordered_set> GetAllBoxedTys() const { return boxedTys; } void Clear(); void ClearMapCache() { builtinTyToExtendMap.clear(); instantiateBuiltInTyToExtendMap.clear(); declToExtendMap.clear(); declInstantiationStatus.clear(); tyExtendInterfaceTyMap.clear(); tyToSuperTysMap.clear(); overrideOrShadowCache.clear(); ClearRecordUsedExtends(); } std::unordered_set> GetBoxUsedExtends() const { return boxUsedExtends; } std::unordered_set> GetBoxedNonGenericDecls() const { return boxedNonGenericDecls; } std::unordered_set> GetTyUsedExtends(Ptr ty) const { auto found = tyUsedExtends.find(ty); return found != tyUsedExtends.end() ? found->second : std::unordered_set>{}; } void ClearRecordUsedExtends() { boxUsedExtends.clear(); tyUsedExtends.clear(); checkedTyExtendRelation.clear(); boxedTys.clear(); } /** * whether the classLike decl has func decl override the base funcDecl. * @param baseDecl the classLike decl * @param funcDecl the base funcDecl */ Ptr GetOverrideDeclInClassLike( AST::Decl& baseDecl, const AST::FuncDecl& funcDecl, bool withAbstractOverrides = false); void UpdateTopOverriddenFuncDeclCache(const AST::Decl* src, const AST::Decl* target); Ptr GetTopOverriddenFuncDecl(const AST::FuncDecl* funcDecl) const; /** * whether the decl is override the funcDecl. */ bool IsFuncDeclSubType(const AST::FuncDecl& decl, const AST::FuncDecl& funcDecl); bool IsFuncDeclEqualType(const AST::FuncDecl& decl, const AST::FuncDecl& funcDecl); /** * Determine whether the interfaces implemented by two extension declarations have inheritance relationships. * @param r, l the two extension declarations being compared * @return two bool values, the first indicating whether a relationship exists. * the second indicates that r is the parent interface of l if the former is true. */ std::pair IsExtendInheritRelation(const AST::ExtendDecl& r, const AST::ExtendDecl& l); bool PairIsOverrideOrImpl(const AST::Decl& child, const AST::Decl& parent, const Ptr baseTy = nullptr, const Ptr parentTy = nullptr); // Try to constrain tv by tyCtor as an upperbound. // tyCtor's type args must be GenericsTy. // The tyCtor's type arguments in tv's upperbound will be replaced // by newly allocated placeholder type vars, // which will eventually be solved together with tv. // Returns the new upperbound. // Will return nullptr if the new constaint can't possibly be satisfied. // // e.g. ConstrainByCtor(T, U->R) will: // 1. allocate new ty var U' and R' // 2. add U'->R' to T's upperbound // 3. return U'->R' Ptr ConstrainByCtor(AST::GenericsTy& tv, AST::Ty& tyCtor); // Similar to ConstrainByCtor, but the upperbound will be added to sum instead of ubs. // Also, in case some of the constructors are generic, the ty args between upperbounds // can be shared, in order to sync the constraints (in a best-effort manner). Ptr AddSumByCtor(AST::GenericsTy& tv, AST::Ty& tyCtor, std::vector>& tyArgs); bool OfSameCtor(Ptr ty, Ptr tyCtor); // currently only for collecting map from member to decls Ptr GetDummyBuiltInDecl(Ptr ty); /** Get extend decls that extend @p baseTy with given @p interfaceTy */ std::optional> GetExtendDeclByInterface(AST::Ty& baseTy, AST::Ty& interfaceTy); /** * Get extend decls that extend @p baseTy with given @p member, it can be default implementation in interface or * extension's member. */ Ptr GetExtendDeclByMember(const AST::Decl& member, AST::Ty& baseTy); /** * Allocate/release a placeholder TyVar with a dummy declaration. Used as placeholder for instantiation type, * or placeholder for temporarily unknown type, such as unannotated lambda param type and recursive function's * return type. * isPlaceholder field will be set to true. * Release should be managed only by TyVarScope. */ Ptr AllocTyVar( const std::string& srcId = "Ti", bool needSolving = false, Ptr derivedFrom = nullptr); size_t ScopeDepthOfTyVar(const AST::GenericsTy& tyVar); bool TyVarHasNoSum(TyVar& tv) const; const std::set>& GetUnsolvedTyVars(); void MarkAsUnsolvedTyVar(AST::GenericsTy& tv); std::set> GetInnermostUnsolvedTyVars(); Ptr TryGreedySubst(Ptr ty); // constraints for placeholder type vars Constraint constraints; private: friend class TyVarScope; friend class InstCtxScope; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND friend class TyGeneralizer; #endif void ReleaseTyVar(Ptr genTy); public: /** The extend context maps. */ std::unordered_map, std::set>> builtinTyToExtendMap; std::unordered_map, std::set>> declToExtendMap; private: inline static AST::InvalidTy theInvalidTy = AST::InvalidTy(); inline static AST::AnyTy theAnyTy = AST::AnyTy(); inline static AST::QuestTy theQuestTy = AST::QuestTy(); inline static AST::CStringTy theCStringTy = AST::CStringTy(); inline static std::vector primitiveTys = []() { std::vector tys; for (auto i = static_cast(TYPE_PRIMITIVE_MIN); i <= static_cast(TYPE_PRIMITIVE_MAX); i++) { if (i == static_cast(AST::TypeKind::TYPE_NOTHING)) { tys.emplace_back(AST::NothingTy()); } else { tys.emplace_back(AST::PrimitiveTy(static_cast(i))); } } return tys; }(); struct TypePointer { explicit TypePointer(Ptr ptr) : ptr(ptr) { } ~TypePointer() = default; Ptr operator->() const { return ptr; } Ptr Get() const { return ptr; } bool HasValue() const { return ptr != nullptr; } bool operator==(const TypePointer& other) const { if (!this->HasValue() && !other.HasValue()) { return true; } return this->HasValue() && other.HasValue() && *this->Get() == *other.Get(); } private: Ptr const ptr; }; struct TypeHash { size_t operator()(const TypePointer& p) const { return p.HasValue() ? p->Hash() : 0; } }; // These unordered sets are hash tables to save types. std::unordered_set allocatedTys; std::unordered_set, Ptr>, HashPair> checkedTyExtendRelation; std::unordered_set> boxedTys; std::unordered_set> boxUsedExtends; std::unordered_set> boxedNonGenericDecls; /** Used generic extends for each instantiated type. */ std::unordered_map, std::unordered_set>> tyUsedExtends; std::unordered_map, std::set>> instantiateBuiltInTyToExtendMap; /** TypeManager caches. */ std::unordered_map, std::unordered_set>> tyExtendInterfaceTyMap; struct TypeInfo { Ptr ty; TypeSubst mapping; bool withExtended; }; struct TypeInfoHash { size_t operator()(const TypeInfo& info) const { size_t ret = 0; ret = hash_combine>(ret, info.ty); for (auto n : info.mapping) { ret = hash_combine>(ret, n.first); ret = hash_combine>(ret, n.second); } ret = hash_combine(ret, info.withExtended); return ret; } }; struct TypeInfoEqual { bool operator()(const TypeInfo& lhs, const TypeInfo& rhs) const { if (lhs.ty != rhs.ty || lhs.withExtended != rhs.withExtended || lhs.mapping.size() != rhs.mapping.size()) { return false; } for (auto it1 : lhs.mapping) { auto it2 = rhs.mapping.find(it1.first); if (it2 == rhs.mapping.end() || it1.second != it2->second) { return false; } } return true; } }; std::unordered_map>, TypeInfoHash, TypeInfoEqual> tyToSuperTysMap; /** Store checked typeArgs instantiation result for generic decls. */ std::unordered_map, std::map>, bool>> declInstantiationStatus; Ptr anyTy = &theAnyTy; Ptr ctypeTy = nullptr; struct SubtypeCacheKey { Ptr leaf{nullptr}; Ptr root{nullptr}; bool implicitBoxed{false}; bool allowOptionBox{false}; SubtypeCacheKey(Ptr leaf, Ptr root, bool implicitBoxed = true, bool allowOptionBox = true) : leaf(leaf), root(root), implicitBoxed(implicitBoxed), allowOptionBox(allowOptionBox) { } }; struct SubtypeCacheKeyHash { size_t operator()(const SubtypeCacheKey& key) const { size_t ret = 0; ret = hash_combine(ret, key.leaf); ret = hash_combine(ret, key.root); ret = hash_combine(ret, key.implicitBoxed); ret = hash_combine(ret, key.allowOptionBox); return ret; } }; struct SubtypeCacheKeyEqual { bool operator()(const SubtypeCacheKey& lhs, const SubtypeCacheKey& rhs) const { return std::tie(lhs.leaf, lhs.root, lhs.implicitBoxed, lhs.allowOptionBox) == std::tie(rhs.leaf, rhs.root, rhs.implicitBoxed, rhs.allowOptionBox); } }; std::unordered_map subtypeCache; struct OverrideOrShadowKey { const AST::FuncDecl* src{nullptr}; const AST::FuncDecl* target{nullptr}; const AST::Ty* baseTy{nullptr}; const AST::Ty* expectInstParent{nullptr}; OverrideOrShadowKey(const AST::FuncDecl* s, const AST::FuncDecl* t, const AST::Ty* b, const AST::Ty* e) : src(s), target(t), baseTy(b), expectInstParent(e) { } }; struct OverrideOrShadowEqual { bool operator()(const OverrideOrShadowKey& lhs, const OverrideOrShadowKey& rhs) const { return std::tie(lhs.src, lhs.target, lhs.baseTy, lhs.expectInstParent) == std::tie(rhs.src, rhs.target, rhs.baseTy, rhs.expectInstParent); } }; struct OverrideOrShadowHash { size_t operator()(const OverrideOrShadowKey& key) const { size_t ret = 0; ret = hash_combine(ret, key.src); ret = hash_combine(ret, key.target); ret = hash_combine(ret, key.baseTy); ret = hash_combine(ret, key.expectInstParent); return ret; } }; /** Stores the overwrite or shadow judgment result determined based on BaseTy, src funcDecl, and target funcDecl. */ std::unordered_map overrideOrShadowCache; std::unordered_map, std::vector>> overrideCache; // a counter for naming tyvars unsigned long long nextUniqId{0}; // all tyvar resources std::unordered_set tyVarPool; // dummy decls to be associated with tyvar std::vector> dummyGenDecls; std::map, OwnedPtr> dummyBuiltInDecls; // the level each tyvar is introduced, used when unifying 2 tyvars; // should be [high level |-> low level], NOT the opposite std::map, size_t> tyVarScopeDepth; std::set> unsolvedTyVars; // only internal states. remembering the scopes of tyvar introduction and mapping context std::vector> tyVarScopes; std::vector> instCtxScopes; Ptr topScope; // wrap all uses of placeholder ty vars in SubstPack, just in case private: template TypeT* GetTypeTy(Args&&... args); bool IsClassTyEqual(AST::Ty& leaf, AST::Ty& root); Ptr GetExtendInterfaceSuperTy(AST::ClassTy& classTy, const AST::Ty& interfaceTy); /** * The class is used to store typeMapping and ctxVars as context condition of the recursive instantiation. */ class TyInstantiator { private: friend class TypeManager; TyInstantiator(TypeManager& tyMgr, const TypeSubst& mapping) : tyMgr(tyMgr), typeMapping(mapping) { } ~TyInstantiator() = default; inline Ptr Instantiate(Ptr ty) { return AST::Ty::IsTyCorrect(ty) ? Instantiate(*ty) : ty; } Ptr Instantiate(AST::Ty& ty); Ptr GetInstantiatedStructTy(AST::StructTy& structTy); Ptr GetInstantiatedClassTy(AST::ClassTy& classTy); Ptr GetInstantiatedInterfaceTy(AST::InterfaceTy& interfaceTy); Ptr GetInstantiatedEnumTy(AST::EnumTy& enumTy); Ptr GetInstantiatedArrayTy(AST::ArrayTy& arrayTy); Ptr GetInstantiatedPointerTy(AST::PointerTy& cptrTy); // Get instantiated ty of set type 'IntersectionTy' and 'UnionTy'. template Ptr GetInstantiatedSetTy(SetTy& ty); Ptr GetInstantiatedGenericTy(AST::GenericsTy& ty); TypeManager& tyMgr; const TypeSubst& typeMapping; }; void GetNominalSuperTy( const AST::Ty& nominalTy, const TypeSubst& typeMapping, std::unordered_set>& tyList); bool HasNominalSuperTy(AST::Ty& nominalTy, AST::Ty& superTy, const TypeSubst& typeMapping); bool IsPlaceholderSubtype(AST::Ty& leaf, AST::Ty& root); bool IsGenericSubtype(AST::Ty& leaf, AST::Ty& root, bool implicitBoxed, bool allowOptionBox); bool IsClassLikeSubtype(AST::Ty& leaf, AST::Ty& root, bool implicitBoxed, bool allowOptionBox); bool IsStructOrEnumSubtype(AST::Ty& leaf, AST::Ty& root, bool implicitBoxed, bool allowOptionBox); bool IsArraySubtype(const AST::Ty& leaf, const AST::Ty& root); bool IsVArraySubtype(const AST::Ty& leaf, const AST::Ty& root); bool IsPointerSubtype(const AST::Ty& leaf, const AST::Ty& root); bool IsPrimitiveSubtype(const AST::Ty& leaf, AST::Ty& root); bool IsTyExtendInterface(const AST::Ty& classTy, const AST::Ty& interfaceTy); bool CheckGenericType(Ptr lvalue, Ptr rvalue, bool implicitBoxed = true); void GenerateExtendGenericMappingVisit( MultiTypeSubst& typeMapping, AST::Ty& baseType, std::unordered_set>& visited); void GenerateStructDeclGenericMappingVisit(MultiTypeSubst& m, const AST::InheritableDecl& decl, const AST::Ty& targetTy, std::unordered_set>& visited); void GenerateGenericMappingVisit(MultiTypeSubst& m, AST::Ty& baseType, std::unordered_set>& visited); /* Alternative version that generate SubstPack. * Should migrate to this version everywhere in the future. */ void GenerateGenericMappingVisit( SubstPack& m, AST::Ty& baseType, std::unordered_set>& visited, bool contextual); void GenerateExtendGenericMappingVisit( SubstPack& typeMapping, AST::Ty& baseType, std::unordered_set>& visited, bool contextual); void GenerateStructDeclGenericMappingVisit(SubstPack& m, const AST::InheritableDecl& decl, const AST::Ty& targetTy, std::unordered_set>& visited, bool contextual); std::unordered_set> GetAllExtendInterfaceTyHelper( const std::set>& extends, const std::vector>& typeArgs); bool HasExtendInterfaceTyHelper(AST::Ty& superTy, const std::set>& extends, const std::vector>& typeArgs); }; } // namespace Cangjie #endif // CANGJIE_SEMA_TYPE_MANAGER_H cangjie_compiler-1.0.7/include/cangjie/Utils/000077500000000000000000000000001510705540100211325ustar00rootroot00000000000000cangjie_compiler-1.0.7/include/cangjie/Utils/Casting.h000066400000000000000000000007551510705540100227020ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements contains all supported casting headers. */ #ifndef CANGJIE_UTILS_CASTING_H #define CANGJIE_UTILS_CASTING_H #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/AST/ASTCasting.h" #endif cangjie_compiler-1.0.7/include/cangjie/Utils/CastingTemplate.h000066400000000000000000000215161510705540100243740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file defines template for type casting. */ #ifndef CANGJIE_UTILS_CASTING_TEMPLATE_H #define CANGJIE_UTILS_CASTING_TEMPLATE_H #include #include "cangjie/Utils/SafePointer.h" #include "cangjie/Utils/CheckUtils.h" /** * Support customized casting template for 'AST::Node', 'AST::Ty', 'CHIRNode' types. * Mainly supply three template functions: * 1. 'Is(instance*)' or 'Is(instance*)' or 'Is(instance&)' * return true if the given pointer is instance of the given type 'To'. * 2. 'StaticCast(instance*)' or 'StaticCast(instance)' or 'StaticCast(instance)' * return the casted value of the given pointer or reference (it will keep 'const' qualifier). * 3. 'DynamicCast(instance*)' or 'DynamicCast(instance*)' * return the runtime-casted pointer result of the given pointer (it will keep 'const' qualifier). */ namespace Cangjie { // Do not add function in this template. // NOTE: newly added type without customization will fail compiling. template struct TypeAs { }; template struct NodeType { }; // Defined NodeType::kind register macro. #define DEFINE_NODE_TYPE_KIND(TYPE, KIND) \ template <> struct NodeType { \ static const auto kind = KIND; \ } // Define type filter for intermidiate AST types. template struct ShouldInstantiate : public std::true_type { }; template struct ShouldInstantiate : public std::conditional_t, std::false_type, ShouldInstantiate> { }; template inline bool constexpr IsTypeOf(From node) { using ToType = std::remove_pointer_t; using FromType = std::remove_pointer_t; if constexpr (std::is_base_of_v) { return node != nullptr; } else { static_assert(std::is_base_of_v, "casting irrelevant types"); bool isSubType = node != nullptr && TypeAs>::IsInstanceOf(*node); #if defined(CMAKE_ENABLE_ASSERT) || !defined(NDEBUG) CJC_ASSERT(isSubType == (dynamic_cast(node) != nullptr) && "Error casting"); #endif return isSubType; } } template inline std::enable_if_t && !std::is_pointer_v, bool> Is(const From& node) { return IsTypeOf(&node); } template inline std::enable_if_t && std::is_pointer_v, bool> Is(From node) { return IsTypeOf(node); } template inline std::enable_if_t, bool> Is(From node) { static_assert(std::is_pointer_v); return IsTypeOf(node); } template inline bool Is(Ptr node) { return Is(node.get()); } template bool Is(const OwnedPtr& node) { return Is(node.get()); } template struct CastTo { using ToType = std::conditional_t, std::remove_pointer_t, std::remove_reference_t>; using type = std::conditional_t>, std::conditional_t, const ToType*, const ToType&>, std::conditional_t, ToType*, ToType&>>; }; template using CastToT = typename CastTo::type; namespace Details { template constexpr bool IsVirtualBaseImpl(...) { return true; } template ()))>* = nullptr> constexpr bool IsVirtualBaseImpl(int) { return false; } } template struct IsVirtualBaseOf: public std::integral_constant && Details::IsVirtualBaseImpl(0) && !Details::IsVirtualBaseImpl(0)> {}; template constexpr bool IsVirtualBaseOfV = IsVirtualBaseOf::value; // 'To' can be normal type or pointer type. // 'From' type must be pointer type. template inline CastToT DynamicCast(From node) { static_assert(std::is_pointer_v && !std::is_reference_v); if (!IsTypeOf(node)) { return nullptr; } if constexpr (IsVirtualBaseOfV>, std::remove_pointer_t>) { return dynamic_cast>(node); } else { return static_cast>(node); } } // 'To' can be normal type or pointer type. // 'From' type must be pointer type. template inline CastToT DynamicCast(Ptr node) { static_assert(!std::is_reference_v); if (!IsTypeOf(node.get())) { return nullptr; } if constexpr (IsVirtualBaseOfV>, std::remove_pointer_t>) { return dynamic_cast>(node.get()); } else { return static_cast>(node.get()); } } ///@{ /** * Cast from virtual base class to derived class. \ref VirtualCast asserts the class must succeed. Otherwise, use * \ref DynamicCast */ template CastToT VirtualCast(From* node) { static_assert(IsVirtualBaseOfV>, std::remove_pointer_t>); auto r = dynamic_cast>(node); CJC_ASSERT(!node || r); return r; } template CastToT VirtualCast(Ptr node) { static_assert(IsVirtualBaseOfV>, std::remove_pointer_t>); auto r = dynamic_cast>(node.get()); CJC_ASSERT(!node || r); return r; } template CastToT VirtualCast(From& node) { static_assert(IsVirtualBaseOfV>); return dynamic_cast>(node); } ///@} // 'From' can only be pointer type with or without 'const'. // 'To' can be normal type or pointer type. template inline std::enable_if_t, CastToT> StaticCast(From node) { static_assert(std::is_pointer_v && !std::is_reference_v); #if defined(CMAKE_ENABLE_ASSERT) || !defined(NDEBUG) auto ptr = dynamic_cast>(node); CJC_ASSERT(ptr != nullptr && "Error casting"); #endif static_assert(!IsVirtualBaseOfV>, std::remove_pointer_t>, "static_cast cannot cast via virtual base, use dynamic_cast instead."); return static_cast>(node); } // 'From' can only be normal type with or without 'const' (reference is defined in parameter). // 'To' can be normal type or reference type. template inline std::enable_if_t, CastToT> StaticCast(From& node) { static_assert(!std::is_pointer_v && !std::is_pointer_v); #if (defined(CMAKE_ENABLE_ASSERT) || !defined(NDEBUG)) && !defined(__APPLE__) auto ptr = dynamic_cast>(&node); CJC_ASSERT(ptr != nullptr && "Error casting"); #endif static_assert(!IsVirtualBaseOfV, std::remove_reference_t>, "static_cast cannot cast via virtual base, use dynamic_cast instead."); return static_cast>(node); } template inline CastToT StaticCast(Ptr node) { static_assert(!std::is_reference_v); #if (defined(CMAKE_ENABLE_ASSERT) || !defined(NDEBUG)) && !defined(__APPLE__) auto ptr = dynamic_cast>(node.get()); CJC_ASSERT(ptr != nullptr && "Error casting"); #endif static_assert(!IsVirtualBaseOfV>, std::remove_pointer_t>, "static_cast cannot cast via virtual base, use dynamic_cast instead."); return static_cast>(node.get()); } template inline To RawStaticCast(From src) { return static_cast(src); } template inline To RawStaticCast(Ptr src) { return static_cast(src.get()); } } // namespace Cangjie #endif // CANGJIE_UTILS_CASTING_TEMPLATE_Hcangjie_compiler-1.0.7/include/cangjie/Utils/CheckUtils.h000066400000000000000000000025001510705540100233360ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares some condition check helper macros. */ #ifndef CANGJIE_UTILS_CHECKUTILS_H #define CANGJIE_UTILS_CHECKUTILS_H #include #include #ifdef CMAKE_ENABLE_ASSERT #define CJC_ASSERT(f) \ { \ if (!(f)) { \ abort(); \ } \ } #define CJC_ABORT() abort() #else #ifdef NDEBUG #define CJC_ASSERT(f) static_cast(f) #define CJC_ABORT() #else #define CJC_ASSERT(f) assert(f) #define CJC_ABORT() abort() #endif #endif #define CJC_NULLPTR_CHECK(p) CJC_ASSERT((p) != nullptr) #endif cangjie_compiler-1.0.7/include/cangjie/Utils/ConstantsUtils.h000066400000000000000000000157421510705540100243110ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares some utility constants. */ #ifndef CANGJIE_CONSTANTSUTILS_H #define CANGJIE_CONSTANTSUTILS_H #include #include #include namespace Cangjie { enum class OverflowStrategy : uint8_t { NA, CHECKED, WRAPPING, THROWING, SATURATING, OVERFLOW_STRATEGY_END }; inline const std::string MAIN_INVOKE = "$mainInvoke"; inline const std::string ENV_NAME = "$env"; inline const std::string TEST_ENTRY_NAME = "$test.entry"; inline const std::string BOX_DECL_PREFIX = "$BOX_"; inline const std::string STATIC_INIT_VAR = "$init"; inline const std::string STATIC_INIT_FUNC = "static.init"; constexpr std::string_view TO_ANY{"$toAny"}; inline const std::string INOUT_GHOST_SYSCALL = "_inout_"; inline const std::string ITER_COMPILER = "iter-compiler"; inline const std::string V_COMPILER = "v-compiler"; inline const std::string INVALID_IDENTIFIER = ""; inline const std::string RESOURCE_NAME = "v-freshExc"; inline const std::string CANGJIE_HOME = "CANGJIE_HOME"; inline const std::string SOURCEFILE = "sourceFile"; inline const std::string SOURCELINE = "sourceLine"; inline const std::string SANCOV_VARIABLE_FLAG = "$sancov$"; // File extension. inline const std::string SERIALIZED_FILE_EXTENSION = ".cjo"; inline const std::string CJ_D_FILE_EXTENSION = ".cj.d"; inline const std::string FULL_BCHIR_SERIALIZED_FILE_EXTENSION = ".full.bchir"; inline const std::string BCHIR_SERIALIZED_FILE_EXTENSION = ".bchir"; inline const std::string CACHED_AST_EXTENSION = ".cachedast"; inline const std::string CACHED_IMPORTED_CJO_EXTENSION = ".iast"; inline const std::string CACHED_CHIR_OPT_EXTENSION = ".cachedchiropt"; inline const std::string CACHED_GLOBAL_DECL_DEP_EXTENSION = ".cachedgdecldep"; inline const std::string CACHED_MODIFIED_AST_EXTENSION = ".cachedModified"; inline const std::string CACHED_LOG_EXTENSION = ".log"; inline const std::string CHIR_SERIALIZATION_FILE_EXTENSION = ".chir"; inline const std::string CHIR_READABLE_FILE_EXTENSION = ".chirtxt"; // Built-in type name. inline const std::string CLASS_EXCEPTION = "Exception"; inline const std::string CLASS_ERROR = "Error"; inline const std::string CLASS_COMMAND = "Command"; inline const std::string CLASS_HANDLER_FRAME = "HandlerFrame"; inline const std::string CLASS_IMMEDIATE_FRAME = "ImmediateFrame"; inline const std::string CLASS_DOUBLE_RESUME_EXCEPTION = "DoubleResumeException"; inline const std::string CLASS_FRAME_EXCEPTION_WRAPPER = "ImmediateFrameExceptionWrapper"; inline const std::string CLASS_FRAME_ERROR_WRAPPER = "ImmediateFrameErrorWrapper"; inline const std::string CLASS_EARLY_RETURN = "ImmediateEarlyReturn"; inline const std::string OPTION_NAME = "Option"; inline const std::string OPTION_VALUE_CTOR = "Some"; inline const std::string OPTION_NONE_CTOR = "None"; inline const std::string ANY_NAME = "Any"; inline const std::string OBJECT_NAME = "Object"; inline const std::string JOBJECT_NAME = "JObject"; inline const std::string CTYPE_NAME = "CType"; inline const std::string RAW_ARRAY_NAME = "RawArray"; constexpr std::string_view CPOINTER_NAME = "CPointer"; constexpr std::string_view CSTRING_NAME = "CString"; constexpr std::string_view CFUNC_NAME = "CFunc"; inline const std::string VARRAY_NAME = "VArray"; inline const std::string TOSTRING_NAME = "ToString"; inline const std::string BOX_NAME = "Box"; const std::string JTYPE_NAME = "JType"; const std::string JARRAY_NAME = "JArray"; // Macro with context. inline const int MACRO_DEF_NUM = 2; inline const std::string MC_EXCEPTION = "MacroContextException"; inline const int MACRO_COMMON_ARGS = 1; inline const int MACRO_ATTR_ARGS = 2; inline const std::string MACRO_OBJECT_NAME = "MACRO_OBJECT"; constexpr std::string_view IF_AVAILABLE = "IfAvailable"; // Standard library package name // Please do not change the const char[] type to std::string type because the initialization order of // std::string and std::map types across translation units is undefined,especially on the Windows platform. inline const std::string DEFAULT_PACKAGE_NAME = "default"; inline constexpr const char CORE_PACKAGE_NAME[] = "std.core"; inline constexpr const char SYNC_PACKAGE_NAME[] = "std.sync"; inline constexpr const char MATH_PACKAGE_NAME[] = "std.math"; inline constexpr const char OVERFLOW_PACKAGE_NAME[] = "std.overflow"; inline constexpr const char RUNTIME_PACKAGE_NAME[] = "std.runtime"; inline constexpr const char UNICODE_PACKAGE_NAME[] = "std.unicode"; inline constexpr const char UNITTEST_MOCK_PACKAGE_NAME[] = "std.unittest.mock"; inline constexpr const char UNITTEST_MOCK_INTERNAL_PACKAGE_NAME[] = "std.unittest.mock.internal"; inline constexpr const char AST_PACKAGE_NAME[] = "std.ast"; inline constexpr const char INTEROP_PACKAGE_NAME[] = "std.interop"; inline constexpr const char NET_PACKAGE_NAME[] = "std.net"; inline constexpr const char REFLECT_PACKAGE_NAME[] = "std.reflect"; inline constexpr const char REF_PACKAGE_NAME[] = "std.ref"; inline constexpr const char EFFECT_INTERNALS_PACKAGE_NAME[] = "stdx.effect"; inline constexpr const char EFFECT_PACKAGE_NAME[] = "stdx.effect"; // Standard library class name inline const std::string STD_LIB_ARRAY = "Array"; inline const std::string STD_LIB_FUTURE = "Future"; inline const std::string STD_LIB_MONITOR = "Monitor"; inline const std::string STD_LIB_MUTEX = "Mutex"; inline const std::string STD_LIB_OPTION = "Option"; inline const std::string STD_LIB_STRING = "String"; inline const std::string STD_LIB_WAIT_QUEUE = "WaitQueue"; inline const std::string STD_LIB_WEAK_REF = "WeakRef"; inline const std::string INTEROP_LIB_EXPORTED_REF = "ExportedRef"; inline const std::string INTEROP_LIB_FOREIGN_PROXY = "ForeignProxy"; // Global init function name for CHIR, CodeGen. inline const std::string ANNOTATION_VAR_POSTFIX{"@Annotation@"}; // Closure Conversion inline const std::string CC_DEF_PREFIX = "$C"; inline const std::string GENERIC_VIRTUAL_FUNC = "$GenericVirtualFunc"; inline const std::string INST_VIRTUAL_FUNC = "$InstVirtualFunc"; // CFFI inline const std::string CFFI_FUNC_SUFFIX = "$real"; namespace Interop::Java { inline const std::string INTEROP_JOBJECT_NAME = "JObject"; inline const std::string INTEROP_JSTRING_NAME = "JString"; inline const std::string INTEROP_JARRAY_NAME = "JArray"; inline const std::string INTEROP_JAVA_LANG_PACKAGE = "java.lang"; inline const std::string JAVA_REF_FIELD_NAME = "$javaref"; constexpr auto JAVA_REF_GETTER_FUNC_NAME = "$getJavaRef"; constexpr auto INTEROPLIB_CFFI_JAVA_ENTITY = "Java_CFFI_JavaEntity"; } // namespace Cangjie::Interop::Java // Headless instrinsics inline const std::string GET_TYPE_FOR_TYPE_PARAMETER_FUNC_NAME = "getTypeForTypeParameter"; inline const std::string IS_SUBTYPE_TYPES_FUNC_NAME = "isSubtypeTypes"; constexpr std::string_view ORG_NAME_SEPARATOR{"@"}; } // namespace Cangjie #endif // CANGJIE_CONSTANTSUTILS_H cangjie_compiler-1.0.7/include/cangjie/Utils/FileUtil.h000066400000000000000000000337411510705540100230300ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares file util related apis. */ #ifndef CANGJIE_UTILS_FILEUTIL_H #define CANGJIE_UTILS_FILEUTIL_H #include #include #include #include #include #ifdef _MSC_VER // Workaround for "combaseapi.h(229): // error C2187: syntax error: 'identifier' was unexpected here" when using /permissive- struct IUnknown; #include #define R_OK 4 #define W_OK 2 #define X_OK 0 #define F_OK 0 #define access _access #else #include #endif #include "cangjie/Basic/Print.h" namespace Cangjie { class DiagnosticEngine; const size_t FILE_NAME_MAX_LENGTH = 255; const size_t FILE_LEN_LIMIT = 4ULL * 1024 * 1024 * 1024; // 4 GB #ifdef _WIN32 const static std::string INJECTION_STRING = "^\";|&"; // On Windows, both dos and unix dir separators ('\\' and '/') are supported. // To search for dir separators from a string, only methods similar to `find_last_of` and `find_first_of` can be used. // `find` cannot be used. const std::string DIR_SEPARATOR = "\\/"; const std::string CURRENT_PATH = ".\\"; const std::string UPPERLEVEL_PATH = "..\\"; const size_t FILE_PATH_MAX_LENGTH = 260; const size_t DIR_PATH_MAX_LENGTH = 248; #else const static std::string INJECTION_STRING = "|;&$><\\!\n"; // Check ` otherwhere. const std::string DIR_SEPARATOR = "/"; const std::string CURRENT_PATH = "./"; const std::string UPPERLEVEL_PATH = "../"; const size_t FILE_PATH_MAX_LENGTH = 4096; const size_t DIR_PATH_MAX_LENGTH = 4096; #endif /** * Monad by overloading | as pipe operator. * @param F this function return option or value. * @param opt option. * @return the same as F. */ template auto operator|(const std::optional& opt, F function) -> decltype(function(opt.value())) { if (opt) { return function(*opt); } else { Errorln("fail to invoke system api"); return {}; } } namespace FileUtil { /** * Check whether there is commandline injection in command. * @param cmd commandline string * @return true if found injection, else return false. */ bool CheckCommandLineInjection(const std::string& cmd); /** * Escape Backtick by insert a backslash before the backtick * @param s commandline string. * @return string with escaped backtick. */ std::string TransferEscapeBacktick(const std::string& s); /** * Get file name from filePath. * @param filePath string like "path/file.extension". * @return file.extension. */ std::string GetFileName(const std::string& filePath); /** * Get file name from filePath. * @param filePath string like "path1/path2" or ".". * @return path2 or real dir name. */ std::string GetDirName(const std::string& filePath); /** * Get dir name from filePath. * @param filePath string like "path/file.extension". * @return dir path. */ std::string GetDirPath(const std::string& filePath); /** * Get extension from filePath. * @param filePath string like "path/file.extension". * @return extension. */ std::string GetFileExtension(const std::string& filePath); /** * Get the position of the dot character used as file extension separator in a file path. * @param filePath string like "path/file.extension". * @return the position of '.' between 'file' and 'extension'. */ std::optional GetFileExtensionSeparatorPos(const std::string& filePath); /** * Get name from filePath. * @param filePath string like "path/file.extension". * @return file. */ std::string GetFileNameWithoutExtension(const std::string& filePath); /** * Judge whether file has some extension. * @param filePath string like "path/file.extension". * @param extension string like "extension". * @return true if match. */ bool HasExtension(const std::string& filePath, const std::string& extension); bool HasCJDExtension(std::string_view path); /** * Get file base from filePath. * @param filePath string like "path/file.extension". * @return path/file. */ std::string GetFileBase(const std::string& filePath); /** * Get the input string quoted. * Double quotes in the input are escaped by backslash. * @param str. * @return quoted string. */ std::string GetQuoted(const std::string& str); /** * Judge whether file is directory, using system api. * @param filePath filePath string can be relative path or absolute path. * @return true if filePath is directory. */ bool IsDir(const std::string& filePath); /** * Check whether a path is an absolute path. * @param filePath * @return true if given path is an absoluate path. */ bool IsAbsolutePath(const std::string& filePath); /** * Take a path to a file or an empty directory and try to remove the item. * @param itemPath path to a file or an empty directory * @return true if specified item is removed successfully */ bool Remove(const std::string& itemPath); #ifdef _WIN32 void GetAllDirsUnderCurrentPath(const std::string& path, std::vector& allFiles); #endif /** * Convert from windows '\' to unix '/', or reverse. * The conversion is performed only on Windows. Otherwise, the original string is returned. * If @p input contains '\0', the characters before it will be returned as the normalized path. * * @param input string to be converted * @param toUnix indicate transition from windows '\' to unix '/', or reverse. * * @return string after conversion on Windows, or the original string otherwise. */ std::string Normalize(const std::string& input, const bool toUnix = false); /** * Read .ast file to buffer. * @param[in] filePath String like "path/file.ast". * @param[in] buffer Save .cjo file. * @param[out] failedReason Why read file to buffer failed. * @return whether func invoked successfully. */ bool ReadBinaryFileToBuffer(const std::string& filePath, std::vector& buffer, std::string& failedReason); /** * Get the size of the File. * @param Path to the file. * @return file sizse. */ size_t GetFileSize(const std::string& filePath); /** * Read file and return its content. * @param[in] filePath String like "path/file.extension". * @param[out] failedReason Why read file to buffer failed. * @return content */ std::optional ReadFileContent(const std::string& filePath, std::string& failedReason); /** * Write data into file. * @param filePath string like "path/to/file". * @param data string to be written into file. * @return whether func invoked successfully. */ bool WriteToFile(const std::string& filePath, const std::string& data); /** * Write buffer into .cjo file. * @param filePath string like "path/file.cjo". * @param buffer save .ast file. * @return whether func invoked successfully. */ bool WriteBufferToASTFile(const std::string& filePath, const std::vector& buffer); /** * Get relative path from basePath and normalize it with UNIX flavor delimiter. * @param basePath absolute path to be calculated from, like "path1/path3/[file.extension]". * Folder path must end with '/'. * @param path absolute path like "path1/path2/file.extension". * @return calculated relative path like "../path2/file.extension". */ std::optional GetRelativePath(const std::string& basePath, const std::string& path); /** * Append extension after file. * @param file string like "path/file". * @param backup used when file is empty. * @param extension string for appending. * @return string like "path/file.extension". */ std::string AppendExtension(const std::string& file, const std::string& backup, const std::string& extension); /** * Get absolute path from relative path. * @param path relative path. * @return absolute path. */ std::optional GetAbsPath(const std::string& path); /** * Return whether the absolute path of the given path exceeds the system upper limit. * @param path relative or absolute path. * @return True if the absolute path exceeds the limit. */ bool IsAbsolutePathAboveLengthLimit(const std::string& path); /** * Recursively get all directories under current path. * @param path root directory path. * @return all directories under current path. */ std::vector GetAllDirsUnderCurrentPath(const std::string& path); /** * Non-recursively get all files under current path. * @param path root directory path. * @param extension file extension for collection. * @param notSkipTest whether skip source file whose name is ending with '_test', some like 'xxx_test.cj'. * @return all files under current path. */ std::vector GetAllFilesUnderCurrentPath( const std::string& path, const std::string& extension, bool shouldSkipTestFiles = true, bool shouldSkipRegularFiles = false); /** * Join path and file, strict or no strict path is OK. * File separators on different platforms are considered. * @param base path at first. * @param append file at last. * @return path/file. */ std::string JoinPath(const std::string& base, const std::string& append); /** * Get the shorter hash value of long hash value. * @param fullHash int value which record the * @return Shortened hash. */ std::string GetShortHash(uint64_t fullHash); /** * Split string by given delimiter. * @param str string by split. * @param del delimiter. * @return strings get from str. */ std::vector SplitStr(const std::string& str, const char del); /** * Used for ending monad. * @param str str can be any string. * @return the same as str. */ std::string IdenticalFunc(const std::string& str); /** * Get package name through file system mapping relationships. */ std::string GetPkgNameFromRelativePath(const std::string& path); /** * Check whether a string is an identifier. * Identifier rule as below (not including keywords, but in some case contextual keywords are special strings that can * be used as an Identifier). * Ident : XID_Start XID_Continue* | '_' XID_Continue+ * ; * RawIdent * : '`' Ident '`' * ; * Identifier * : Ident * | RawIdent * ; * @param str str can be any string. * @param useContextualKeyWords whether contextual keywords can be used. * @return whether str is an identifier */ bool IsIdentifier(const std::string& str, bool useContextualKeyWords = true); std::string NormalizePath(const std::string& path); /** * Convert package name 'std.pkg' to a form of 'cangjie--' */ std::string ConvertPackageNameToLibCangjieBaseFormat(const std::string& fullPackageName); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND /** * Convert filename '.' to a form of 'cangjie-' */ std::string ConvertFilenameToLibCangjieBaseFormat(const std::string& objectFile); /** * Convert filename '.' to a form of 'libcangjie-<.extension>' */ std::string ConvertFilenameToLibCangjieFormat(const std::string& objectFile, const std::string& extension); #else /** * Convert filename '.' to a form of 'cangjie-' */ std::string ConvertFilenameToLibCangjieBaseFormat( const std::string& objectFile, const std::string& sanitizerSuffix = ""); /** * Convert filename '.' to a form of 'libcangjie-<.extension>' */ std::string ConvertFilenameToLibCangjieFormat( const std::string& objectFile, const std::string& extension, const std::string& sanitizerSuffix = ""); #endif std::string ConvertFilenameToLtoLibCangjieFormat(const std::string& objectFile); enum FileMode { FM_EXIST = F_OK, FM_EXE = X_OK, FM_WRITE = W_OK, FM_READ = R_OK }; enum class AccessResultType { OK, NOT_EXIST, NO_PERMISSION, FAILED_WITH_UNKNOWN_REASON }; AccessResultType AccessWithResult(const std::string& path, FileMode mode); bool Access(const std::string& path, FileMode mode); /** * Check if @p path exists. * NOTE: @p caseSensitive is meaningful on windows, otherwise it will be ignored. */ bool FileExist(const std::string& path, bool caseSensitive = false); /** * Search module by name and path for searching. * @param fileName file name * @param searchPaths paths for searching * @param caseSensitive indicates whether the search process is case sensitive, is meaningful only on windows * @return */ std::optional FindFileByName( const std::string& fileName, const std::vector& searchPaths, bool caseSensitive = false); /** * Find Program by an executable name, like: ./cjc, cjc, and return the full path with the executable name. * Optionally can pass a list of paths, to find the full path. */ std::string FindProgramByName(const std::string& name, const std::vector& paths); /** * Search specific serialization file by packageName from searchPaths. * @param fullPackageName full package name * @param suffix file suffix of the serialization file * @param searchPaths paths for searching * @return */ std::string FindSerializationFile( const std::string& fullPackageName, const std::string& suffix, const std::vector& searchPaths); /** * Judge a directory exist or not, if not exit create it. * @param directoryPath string like "/path/directory/". * @return 0 : exist or create successful, -1 : create failed. */ int32_t CreateDirs(const std::string& directoryPath); struct Directory { std::string path; std::string name; }; std::vector GetDirectories(const std::string& path); std::vector SplitEnvironmentPaths(const std::string& pathsString); bool CanWrite(const std::string& path); /** * Remove prefixes from an absolute path. */ std::string RemovePathPrefix(const std::string& absolutePath, const std::vector& removedPathPrefix); #ifdef _WIN32 // make a file or directory hidden void HideFile(const std::string& path); #endif bool IsSlash(char c); /// Get file name for a package when used as default temp/output file. /// e.g. for package a::b.c, returns a#b.c std::string ToCjoFileName(std::string_view fullPackageName); std::string ToPackageName(std::string_view cjoName); } // namespace FileUtil } // namespace Cangjie #endif // CANGJIE_UTILS_FILEUTIL_H cangjie_compiler-1.0.7/include/cangjie/Utils/FloatFormat.h000066400000000000000000000010721510705540100235210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_UTILS_FLOATFORMAT_H #define CANGJIE_UTILS_FLOATFORMAT_H #include #include namespace Cangjie::FloatFormat { uint16_t Float32ToFloat16(float value); bool IsUnderFlowFloat(const std::string& literal); } // namespace Cangjie::FloatFormat #endif // CANGJIE_UTILS_FLOATFORMAT_Hcangjie_compiler-1.0.7/include/cangjie/Utils/ICEUtil.h000066400000000000000000000052511510705540100225440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares ICE related variables and functions. */ #ifndef CANGJIE_UTILS_ICE_H #define CANGJIE_UTILS_ICE_H #include "cangjie/Basic/Color.h" #include "cangjie/Utils/CheckUtils.h" #include #include namespace Cangjie { enum class CompileStage; namespace ICE { const int EXIT_CODE = 2; // Internal compiler error const std::string MSG_PART_ONE = ANSI_COLOR_RED + "Internal Compiler Error: " + ANSI_COLOR_RESET; const std::string MSG_PART_TWO = "\nPlease report this to Cangjie team and include the project. Error Code: "; constexpr int64_t FRONTEND_TP = -1; constexpr int64_t UNITTEST_TP = -2; constexpr int64_t LSP_TP = -3; void PrintVersionFromError(); void RemoveTempFile(); int64_t GetTriggerPoint(); class TriggerPointSetter { public: TriggerPointSetter(CompileStage cs) { SetICETriggerPoint(cs); } TriggerPointSetter(int64_t tp) { SetICETriggerPoint(tp); } ~TriggerPointSetter() { SetICETriggerPoint(); } friend int64_t GetTriggerPoint(); static int64_t interpreterTP; // Module code of the interpreter stage, which is equal to frontend code plus 1. static int64_t writeCahedTP; // Module code of the write cahed stage, which is equal to frontend code plus 2. private: // Save global ICE trigger point static int64_t triggerPoint; static void SetICETriggerPoint(CompileStage cs); static void SetICETriggerPoint(int64_t tp = ICE::FRONTEND_TP); }; bool CanWriteOnceICEMessage(); } // namespace ICE template inline void InternalError(Args&&... args) { if (ICE::CanWriteOnceICEMessage()) { ICE::PrintVersionFromError(); std::cerr << ICE::MSG_PART_ONE; ((std::cerr << args), ...); int64_t tp = ICE::GetTriggerPoint(); std::cerr << ICE::MSG_PART_TWO << std::to_string(tp) << std::endl; // When ut and lsp cases are compiled, do not exit after ICE, // because some ut cases are designed to go to the wrong branch. if (tp == ICE::LSP_TP || tp == ICE::UNITTEST_TP) { return; } ICE::RemoveTempFile(); #ifdef NDEBUG _exit(ICE::EXIT_CODE); #else CJC_ASSERT(false); #endif } } template inline void InternalError(bool pred, Args&&... args) { if (!pred) { InternalError(std::forward(args)...); } } } // namespace Cangjie #endif // CANGJIE_UTILS_ICE_H cangjie_compiler-1.0.7/include/cangjie/Utils/ParallelUtil.h000066400000000000000000000110241510705540100236730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares some utility functions for Parallel Compile. */ #ifndef CANGJIE_UTILS_PARALLELUTIL_H #define CANGJIE_UTILS_PARALLELUTIL_H #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/UserDefinedType.h" #include "cangjie/Utils/TaskQueue.h" #include #include using namespace Cangjie::CHIR; namespace Cangjie::Utils { class ParallelUtil { public: explicit ParallelUtil(CHIR::CHIRBuilder& builder, size_t threadsNum) : threadsNum(threadsNum), builder(builder) { } void RunAST2CHIRInParallel(const std::vector>& decls, const CHIRType& chirType, const Cangjie::GlobalOptions& opts, const GenericInstantiationManager* gim, AST2CHIRNodeMap& globalCache, const ElementList>& localConstVars, const ElementList>& localConstFuncs, IncreKind& kind, const std::unordered_map& deserializedVals, const TranslateASTNodeFunc& funcForTranlateASTNode, std::unordered_map& maybeUnreachable, bool computeAnnotations, std::vector& initFuncsForAnnoFactory, const Cangjie::TypeManager& typeManager, std::vector>& annoFactoryFuncs) { size_t funcNum = decls.size(); std::vector> builderList = ConstructSubBuilders(funcNum); Utils::TaskQueue taskQueue(threadsNum); std::vector> trans; std::vector> chirTypes; CHIR::CHIRTypeCache chirTypeCache(chirType.GetTypeMap(), chirType.GetGlobalNominalCache()); std::vector> maybeUnreachableBlocks; maybeUnreachableBlocks.resize(funcNum); for (size_t idx = 0; idx < funcNum; ++idx) { auto decl = decls.at(idx); auto subChirType = std::make_unique(*builderList[idx], chirTypeCache); auto tran = std::make_unique( *builderList[idx], *subChirType, opts, gim, globalCache, localConstVars, localConstFuncs, kind, deserializedVals, annoFactoryFuncs, maybeUnreachableBlocks[idx], computeAnnotations, initFuncsForAnnoFactory, typeManager); tran->SetTopLevel(*decl); taskQueue.AddTask( [translator = tran.get(), decl, &funcForTranlateASTNode]() { return funcForTranlateASTNode(*decl, *translator); }); if (decl->TestAttr(AST::Attribute::GLOBAL) && !Is(decl)) { tran->CollectValueAnnotation(*decl); } trans.emplace_back(std::move(tran)); chirTypes.emplace_back(std::move(subChirType)); } taskQueue.RunAndWaitForAllTasksCompleted(); for (auto& subBd : builderList) { (*subBd).MergeAllocatedInstance(); } builder.GetChirContext().MergeTypes(); for (auto& it : maybeUnreachableBlocks) { maybeUnreachable.merge(it); } // If the function RunAST2CHIRInParallel is invoked again, we need revert chirTypePool to the inital state. } private: size_t threadsNum; CHIR::CHIRBuilder& builder; std::vector> ConstructSubBuilders(const size_t funcNum) { std::vector>> results; Utils::TaskQueue contextTaskQueue(threadsNum); for (size_t i = 0; i < funcNum; i++) { results.emplace_back(contextTaskQueue.AddTask>([this, i]() { auto subBuilder = std::make_unique(this->builder.GetChirContext(), i); return subBuilder; })); } contextTaskQueue.RunAndWaitForAllTasksCompleted(); std::vector> builderList; for (auto& result : results) { auto res = result.get(); builderList.emplace_back(std::move(res)); } return builderList; } }; } // namespace Cangjie::Utils #endif cangjie_compiler-1.0.7/include/cangjie/Utils/PartiallyPersistent.h000066400000000000000000000175201510705540100253320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares partially persistent data structure */ #ifndef CANGJIE_UTILS_PARTIALLYPERSISTENT_H #define CANGJIE_UTILS_PARTIALLYPERSISTENT_H #include #include #include "cangjie/Utils/CheckUtils.h" namespace Cangjie { using VersionID = size_t; const VersionID DUMMY_VERSION_ID = 0; // Uniformed interface to manipulate compound pseudo persistent data. // Should be specialized for each specific type. // Template parameters are on each function to allow inference. class PData { public: // add a new checkpoint // note that an empty container always start with a base checkpoint template static void Commit(T& data) { data.commit(); } // go back to last checkpoint, discard all changes later template static void Reset(T& data) { data.reset(); } // go back to last checkpoint, changes later saved and can be re-applied with the returned ID template static V Stash(T& data) { return data.stash(); } /* apply the changes saved before, specified by the given ID Note that the ID is associated with the current last checkpoint. Stash and apply must happen under the same last checkpoint. Otherwise the behavior is undefined. */ template static void Apply(T& data, V& version) { data.apply(version); } /* don't change anything, but remove the last checkpoint, effectively making changes since second last checkpoint uncommitted e.g.: 1, 2 !! 3, 4 !! 5, 6 ResetSoft --> 1, 2 !! 3, 4, 5, 6 where `!!` is checkpoint in case no second last checkpoint, will only clear all stashed versions */ template static void ResetSoft(T& data) { data.resetSoft(); } /* automatically call Commit(data) when created, and ResetSoft(data) when exiting scope */ template struct CommitScope { explicit CommitScope(T& data): data(data) { Commit(data); } ~CommitScope() { ResetSoft(data); } CommitScope() = delete; CommitScope(const CommitScope& other) = delete; T& data; }; }; // partially persistent set // can backtrack to history version and switch between saved versions // but only current version editable template class PSet { public: PSet() { commit(); } PSet(const PSet& other):data(other.data), log(other.log), stashes(other.stashes) { } explicit PSet(const std::set& other):data(other) { commit(); } explicit PSet(const std::initializer_list& init):data(std::set(init)) { commit(); } const std::set& raw() { return data; } PSet& operator=(const PSet& other) { if (this == &other) { return *this; } clear(); data = other.data; log = other.log; stashes = other.stashes; return *this; } PSet& operator=(const std::set& other) { if (&other == &data) { return *this; } clear(); data = other; return *this; } // implicit cast for ease of use // must be const since there can't be unlogged changes operator const std::set&() { return data; } void clear() { data.clear(); log.clear(); stashes.clear(); commit(); } std::pair::iterator, bool> insert(const T& value) { checkIn(value); return data.insert(value); } size_t erase(const T& value) { checkOut(value); return data.erase(value); } typename std::set::iterator erase(const typename std::set::iterator& pos) { checkOut(*pos); return data.erase(pos); } bool contains(const T& value) const { return data.count(value) > 0; } size_t count(const T& value) { return data.count(value); } void merge(std::set& src) { for (auto& e : src) { checkIn(e); } data.merge(src); } void merge(PSet& src) { for (auto& e : src.data) { checkIn(e); } data.merge(src.data); src.clear(); } bool empty() const noexcept { return data.empty(); } size_t size() const { return data.size(); } typename std::set::iterator begin() { return data.begin(); } typename std::set::iterator end() { return data.end(); } typename std::set::const_iterator begin() const { return data.begin(); } typename std::set::const_iterator end() const { return data.end(); } typename std::set::iterator cbegin() const noexcept { return data.cbegin(); } typename std::set::iterator cend() const noexcept { return data.cend(); } typename std::set::iterator rbegin() { return data.rbegin(); } typename std::set::iterator rend() { return data.rend(); } typename std::set::const_iterator rbegin() const { return data.rbegin(); } typename std::set::const_iterator rend() const { return data.rend(); } typename std::set::iterator find(const T& value) { return data.find(value); } typename std::set::const_iterator find(const T& value) const { return data.find(value); } void commit() { log.emplace_back(std::vector()); stashes.emplace_back(std::vector>()); } void reset() { CJC_ASSERT(!log.empty()); for (auto l = log.back().rbegin(); l != log.back().rend(); l++) { resetOne(*l); } log.back().clear(); } VersionID stash() { CJC_ASSERT(!stashes.empty()); stashes.back().push_back(log.back()); reset(); return stashes.back().size(); } void apply(VersionID ver) { if (ver == 0) { return; } auto verRaw = ver - 1; CJC_ASSERT(stashes.back().size() > verRaw); for (auto& l : stashes.back()[verRaw]) { applyOne(l); } } void resetSoft() { if (log.size() > 1) { stashes.pop_back(); auto& newLast = log[log.size() - 2]; newLast.insert(newLast.end(), log.back().begin(), log.back().end()); log.pop_back(); } else { stashes.back().clear(); } } bool diff() { return log.back().empty(); } private: struct Log { bool isInsert; // otherwise erase T data; }; std::set data; std::vector> log; std::vector>> stashes; void resetOne(const Log& l) { if (l.isInsert) { data.erase(l.data); } else { data.insert(l.data); } } void applyOne(const Log& l) { if (l.isInsert) { data.insert(l.data); } else { data.erase(l.data); } } void checkIn(const T& value) { if (!contains(value)) { log.back().push_back({true, value}); } } void checkOut(const T& value) { if (contains(value)) { log.back().push_back({false, value}); } } }; } // namespace #endif cangjie_compiler-1.0.7/include/cangjie/Utils/ProfileRecorder.h000066400000000000000000000036121510705540100243730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file defines the ProfileRecorder class for performance analysis. */ #ifndef CANGJIE_UTILS_PROFILE_RECORDER_H #define CANGJIE_UTILS_PROFILE_RECORDER_H #include #include namespace Cangjie::Utils { class ProfileRecorder { public: enum class Type { INVALID_TYPE = 0x00, TIMER = 0x01, MEMORY = 0x02, ALL = 0x03 // TIMER | MEMORY }; ProfileRecorder( const std::string& title, const std::string& subtitle, const std::string& desc = ""); ~ProfileRecorder(); static void SetPackageName(const std::string& name); static void SetOutputDir(const std::string& path); static void Enable(bool en, const Type& type = Type::ALL); static void Start( const std::string& title, const std::string& subtitle, const std::string& desc = ""); static void Stop( const std::string& title, const std::string& subtitle, const std::string& desc = ""); /** * @brief Record some code info. Avoid introducing other module-specific content by custom function. * @param item Indicates the name of a single information item. * @param getData Indicates the closure function for obtaining the value of the item. */ static void RecordCodeInfo(const std::string& item, const std::function& getData); static void RecordCodeInfo(const std::string& item, int64_t value); static std::string GetResult(const Type& type = Type::ALL); private: std::string title_; std::string subtitle_; std::string desc_; }; } // namespace Cangjie::Utils #endif // CANGJIE_UTILS_PROFILE_RECORDER_H cangjie_compiler-1.0.7/include/cangjie/Utils/SafePointer.h000066400000000000000000000205411510705540100235240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares wrappers for unique_ptr and raw pointers * that throws exception on accessing nullptr. */ #ifndef CANGJIE_UTILS_SAFEPOINTER_H #define CANGJIE_UTILS_SAFEPOINTER_H #include #ifndef CANGJIE_ENABLE_GCOV static_assert(true); #include #endif #include #include #include "CheckUtils.h" /* * A non-crash counterpart for unique_ptr and raw pointer. * Usage is almost identical to unique_ptr and raw pointer. * * It is user's duty to guarantee that null pointers are not dereferenced * and that no pointer is accessed after the owner's lifespan ends. * * However, a dereference to null pointer * will raise an exception instead of triggering a system-level fault signal. * Dereference to a pointer that is already released still crashes. */ #ifndef CANGJIE_ENABLE_GCOV class NullPointerException : public std::exception { public: NullPointerException(); int64_t GetTriggerPoint() const noexcept { return triggerPoint; } const char* what() const noexcept override { return static_cast("Dereferencing a null pointer"); } private: int64_t triggerPoint = 0; }; #endif template class Ptr; template class OwnedPtr { public: OwnedPtr() : origin(nullptr) { } OwnedPtr(std::nullptr_t) : origin(nullptr) { } /* to infer type argument when not up-casting */ OwnedPtr(T* raw) : origin(raw) { } template >> OwnedPtr(U* raw) : origin(raw) { } template >> OwnedPtr(OwnedPtr&& src) : origin(src.origin) { src.origin = nullptr; } OwnedPtr(const OwnedPtr&) = delete; OwnedPtr& operator=(const OwnedPtr&) = delete; void operator=(std::nullptr_t) { reset(nullptr); } ~OwnedPtr() { delete origin; } Ptr get() const { return Ptr(*this); } void reset(std::nullptr_t = nullptr) { auto old = origin; origin = nullptr; if (old) { delete old; } } template >> void reset(U* raw) { auto old = origin; origin = raw; if (old) { delete old; } } template >> void swap(OwnedPtr& src) noexcept { std::swap(origin, src.origin); } T* release() { auto old = origin; origin = nullptr; return old; } template >> void operator=(OwnedPtr&& src) { auto old = origin; origin = src.origin; src.origin = nullptr; if (old) { delete old; } } T* operator->() const { #ifdef CANGJIE_ENABLE_GCOV return origin; #else if (origin != nullptr) { return origin; } else { #ifndef CANGJIE_ENABLE_GCOV throw NullPointerException(); #else #ifdef CJC_ABORT CJC_ABORT(); #else return nullptr; #endif #endif } #endif } T& operator*() const { return *operator->(); } operator bool() const { return origin != nullptr; } template bool operator<(const OwnedPtr& src) const { return origin < src.origin; } bool operator!=(std::nullptr_t) const { return origin != nullptr; } bool operator==(std::nullptr_t) const { return origin == nullptr; } template bool operator!=(const OwnedPtr& src) const { return origin != src.origin; } template bool operator==(const OwnedPtr& src) const { return origin == src.origin; } template friend OwnedPtr StaticPointerCast(OwnedPtr&& o); friend void Swap(OwnedPtr& one, OwnedPtr& other) { std::swap(one.origin, other.origin); } private: template friend class OwnedPtr; template friend class Ptr; friend std::hash>; T* origin = nullptr; }; template OwnedPtr StaticPointerCast(OwnedPtr&& o) { return {static_cast(o.release())}; } template class Ptr { public: Ptr() { } Ptr(std::nullptr_t) : ptr(nullptr) { } /* to infer type argument when not up-casting */ Ptr(T* raw) : ptr(raw) { } template >> Ptr(U* src) : ptr(src) { } template >> Ptr(const OwnedPtr& src) : ptr(src.origin) { } template >> Ptr(const Ptr& src) : ptr(src.ptr) { } template >> Ptr(const Ptr&& src) : ptr(src.ptr) { } template >> Ptr& operator=(const OwnedPtr& src) { this->ptr = src.origin; return *this; } template >> Ptr& operator=(const Ptr& src) { this->ptr = src.ptr; return *this; } template >> Ptr& operator=(const Ptr&& src) { this->ptr = src.ptr; return *this; } T* operator->() const { #ifdef CANGJIE_ENABLE_GCOV return ptr; #else if (ptr != nullptr) { return ptr; } else { #ifndef CANGJIE_ENABLE_GCOV throw NullPointerException(); #else #ifdef CJC_ABORT CJC_ABORT(); #else return nullptr; #endif #endif } #endif } T& operator*() const { return *operator->(); } T* get() const { return ptr; } template Ptr StaticCast() { return Ptr(static_cast(ptr)); } operator bool() const { return ptr != nullptr; } operator T*() const { return ptr; } template bool operator<(const Ptr& src) const { return ptr < src.ptr; } bool operator!=(std::nullptr_t) const { return ptr != nullptr; } bool operator==(std::nullptr_t) const { return ptr == nullptr; } template bool operator!=(const Ptr& src) const { return ptr != src.ptr; } template bool operator==(const Ptr& src) const { return ptr == src.ptr; } private: template friend class Ptr; friend std::hash>; T* ptr = nullptr; }; /* Do not support raw array */ template std::enable_if_t::value, OwnedPtr> MakeOwned(Args&&... args) { return OwnedPtr(new T(std::forward(args)...)); } template struct std::hash> { std::size_t operator()(const OwnedPtr& ptr) const noexcept { return static_cast(reinterpret_cast(ptr.origin)); } }; template struct std::hash> { std::size_t operator()(const Ptr& ptr) const noexcept { return static_cast(reinterpret_cast(ptr.ptr)); } }; template struct std::hash> { std::size_t operator()(const OwnedPtr& ptr) const noexcept { return static_cast(reinterpret_cast(ptr.origin)); } }; template struct std::hash> { std::size_t operator()(const Ptr& ptr) const noexcept { return static_cast(reinterpret_cast(ptr.ptr)); } }; template struct std::remove_pointer> { using type = T; }; #endif cangjie_compiler-1.0.7/include/cangjie/Utils/Semaphore.h000066400000000000000000000021101510705540100232200ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares Semaphore class. */ #ifndef CANGJIE_UTILS_SEMAPHORE_H #define CANGJIE_UTILS_SEMAPHORE_H #include #include #include namespace Cangjie::Utils { class Semaphore { public: static Semaphore& Get(); void Release(); void Acquire(); void SetCount(std::size_t newCount); std::size_t GetCount(); private: Semaphore(); ~Semaphore() = default; Semaphore(Semaphore&&) = delete; // Move construct Semaphore(const Semaphore&) = delete; // Copy construct Semaphore& operator=(const Semaphore&) = delete; // Copy assign Semaphore& operator=(Semaphore&&) = delete; // Move assign std::mutex mtx; std::condition_variable cv; std::size_t count; }; } // namespace Cangjie::Utils #endifcangjie_compiler-1.0.7/include/cangjie/Utils/Signal.h000066400000000000000000000043421510705540100225230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares crash signal handler related functions. */ #ifndef CANGJIE_UTILS_SIGNAL_H #define CANGJIE_UTILS_SIGNAL_H #if (defined RELEASE) #include "cangjie/Utils/ICEUtil.h" #include "cangjie/Utils/FileUtil.h" #ifdef __unix__ #include #include #elif __APPLE__ #include #elif _WIN32 #include #include #endif namespace Cangjie { #if defined(__unix__) || defined(__APPLE__) const std::string SIGNAL_MSG_PART_ONE = "Interrupt signal ("; /* Create alternate signal stack. */ void CreateAltSignalStack(); #elif _WIN32 const std::string SIGNAL_MSG_PART_ONE = "Windows unexpected exception code ("; void RegisterCrashExceptionHandler(); #endif const std::string SIGNAL_MSG_PART_TWO = ") received."; /* Register signal handler for crash signals. */ void RegisterCrashSignalHandler(); #ifdef CANGJIE_BUILD_TESTS #define SIGNAL_TEST namespace SignalTest { using SignalTestCallbackFuncType = void (*)(void); enum TriggerPointer { NON_POINTER, // The test callback function is not executed. MAIN_POINTER, // Execute the test callback function inserted in the main func. DRIVER_POINTER, // Execute the test callback function inserted in the Driver module. PARSER_POINTER, // Execute the test callback function inserted in the Parser module. SEMA_POINTER, // Execute the test callback function inserted in the Sema module. CHIR_POINTER, // Execute the test callback function inserted in the CHIR module. CODEGEN_POINTER // Execute the test callback function inserted in the CodeGen module. }; void SetSignalTestCallbackFunc(SignalTestCallbackFuncType fp, TriggerPointer pointerType, int errorCodeOffset); void ExecuteSignalTestCallbackFunc(TriggerPointer executionPoint); } // namespace SignalTest #endif } // namespace Cangjie #endif namespace Cangjie { /* Register signal handler for Crtl C signal. */ void RegisterCrtlCSignalHandler(); } // namespace Cangjie #endif // CANGJIE_UTILS_SIGNAL_H cangjie_compiler-1.0.7/include/cangjie/Utils/SipHash.h000066400000000000000000000110151510705540100226400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_UTILS_SIPHASH_H #define CANGJIE_UTILS_SIPHASH_H #include "cangjie/Utils/CheckUtils.h" #include #include namespace Cangjie::Utils { class SipHash { public: template static uint64_t GetHashValue(const std::bitset& rawData) { const uint64_t data = rawData.to_ullong(); const uint8_t* bytes = reinterpret_cast(&data); const size_t size = sizeof(uint64_t); return SipHash_2_4(bytes, size); } template static uint64_t GetHashValue(T data) { static_assert(std::is_arithmetic_v); const uint8_t* bytes = reinterpret_cast(&data); const size_t size = sizeof(T); return SipHash_2_4(bytes, size); } static uint64_t GetHashValue(std::string data) { const uint8_t* bytes = reinterpret_cast(data.data()); const size_t size = data.size(); return SipHash_2_4(bytes, size); } static uint64_t GetHashValue(const char* data) { return GetHashValue(std::string{data}); } private: static const uint64_t k0_ = 0xdeadbeef; static const uint64_t k1_ = 0x12345678; static size_t GetValueLeft(const uint8_t* dataPointer, size_t sizeLeft) { uint64_t valueLeft{0}; static_assert(CHAR_BIT <= 8); switch (sizeLeft) { case 7: valueLeft |= static_cast(dataPointer[6]) << 48; [[fallthrough]]; case 6: valueLeft |= static_cast(dataPointer[5]) << 40; [[fallthrough]]; case 5: valueLeft |= static_cast(dataPointer[4]) << 32; [[fallthrough]]; case 4: valueLeft |= static_cast(dataPointer[3]) << 24; [[fallthrough]]; case 3: valueLeft |= static_cast(dataPointer[2]) << 16; [[fallthrough]]; case 2: valueLeft |= static_cast(dataPointer[1]) << 8; [[fallthrough]]; case 1: valueLeft |= static_cast(dataPointer[0]); [[fallthrough]]; default: break; } return valueLeft; } static uint64_t SipHash_2_4(const uint8_t* data, const size_t size) { uint64_t v0 = k0_ ^ 0x736f6d6570736575ull; uint64_t v1 = k1_ ^ 0x646f72616e646f6dull; uint64_t v2 = k0_ ^ 0x6c7967656e657261ull; uint64_t v3 = k1_ ^ 0x7465646279746573ull; uint64_t sizep{0}; const uint64_t* dataPointer{reinterpret_cast(data)}; for (; sizep + CHAR_BIT <= size; sizep += CHAR_BIT) { uint64_t m{*dataPointer++}; v3 ^= m; SipRounds(v0, v1, v2, v3); SipRounds(v0, v1, v2, v3); v0 ^= m; } if (size - sizep != 0) { auto valueLeft = GetValueLeft(reinterpret_cast(dataPointer), size - sizep); v3 ^= valueLeft; SipRounds(v0, v1, v2, v3); SipRounds(v0, v1, v2, v3); v0 ^= valueLeft; } v2 ^= 0xff; SipRounds(v0, v1, v2, v3); SipRounds(v0, v1, v2, v3); SipRounds(v0, v1, v2, v3); SipRounds(v0, v1, v2, v3); return v0 ^ v1 ^ v2 ^ v3; } static void SipRounds(uint64_t& v0, uint64_t& v1, uint64_t& v2, uint64_t& v3) { v0 += v1; v1 = (v1 << 13) | (v1 >> (64 - 13)); // `64` means v1 is 64 bits, `13` comes from siphash algorithm v1 ^= v0; v0 = (v0 << 32) | (v0 >> (64 - 32)); // `64` means v0 is 64 bits, `32` comes from siphash algorithm v2 += v3; v3 = (v3 << 16) | (v3 >> (64 - 16)); // `64` means v3 is 64 bits, `16` comes from siphash algorithm v3 ^= v2; v0 += v3; v3 = (v3 << 21) | (v3 >> (64 - 21)); // `64` means v3 is 64 bits, `21` comes from siphash algorithm v3 ^= v0; v2 += v1; v1 = (v1 << 17) | (v1 >> (64 - 17)); // `64` means v1 is 64 bits, `17` comes from siphash algorithm v1 ^= v2; v2 = (v2 << 32) | (v2 >> (64 - 32)); // `64` means v2 is 64 bits, `32` comes from siphash algorithm } }; } #endifcangjie_compiler-1.0.7/include/cangjie/Utils/StdUtils.h000066400000000000000000000016341510705540100230620ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_UTILS_STDUTILS_H #define CANGJIE_UTILS_STDUTILS_H #include #include namespace Cangjie { constexpr int STOINT_BASE{10}; std::optional Stoi(const std::string& s, int base = STOINT_BASE); std::optional Stol(const std::string& s, int base = STOINT_BASE); std::optional Stoul(const std::string& s, int base = STOINT_BASE); std::optional Stoll(const std::string& s, int base = STOINT_BASE); std::optional Stoull(const std::string& s, int base = STOINT_BASE); std::optional Stod(const std::string& s); std::optional Stold(const std::string& s); } #endif cangjie_compiler-1.0.7/include/cangjie/Utils/TaskQueue.h000066400000000000000000000106531510705540100232170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares some utility functions for Concurrent Task Queue. */ #ifndef CANGJIE_UTILS_TASKQUEUE_H #define CANGJIE_UTILS_TASKQUEUE_H #include #include #include #include #include #include "cangjie/Utils/CheckUtils.h" namespace Cangjie::Utils { template using TaskResult = std::future; struct Task { public: template explicit Task(F&& fn, uint64_t priority) : fn(std::forward(fn)), priority(priority) { } void operator()() const { fn(); } bool operator<(const Task& other) const { return priority < other.priority; } private: std::function fn; uint64_t priority; /**< A larger value indicates a higher priority. **/ }; /** * A heuristic parallel task queue. Each task needs to be created with a * specified weight. Each available thread selects the task at the head * of the queue from the task queue to execute. This means tasks with * higher weights can always be executed with higher priority. * * Note that **adding tasks** and **executing tasks** are phased and it * is not allowed to add tasks again after TaskQueue starts to execute. * Therefore, make sure all tasks have been added before TaskQueue is * executed. Otherwise, unintended behavior may occur. * * @tparam TRes The type of task result */ class TaskQueue { public: /** * When the value of `threadsNum` is 0, the value of `threadsNum` is * changed to 1 to prevent queue tasks from having no executors. */ explicit TaskQueue(size_t threadsNum) : threadsNum(threadsNum > 0 ? threadsNum : 1) { } ~TaskQueue() { threads.clear(); } /** * Add a task into the queue. This is not a concurrency-safe method. * @tparam TRes The result type of the task. * @param fn Function to be executed by the task. * @param priority Priority of the task. * @return A place where stores the result of the task. */ template TaskResult AddTask(const std::function& fn, uint64_t priority = 0U) { CJC_ASSERT(!isStarted && "Do not add new tasks while executing."); auto task = std::make_shared>(fn); TaskResult res = task->get_future(); tasks.emplace([task]() mutable { (*task)(); }, priority); return res; } /** * Create threads and start executing tasks in the queue asynchronously * in the background. */ void RunInBackground() { if (tasks.empty()) { return; } CreateThreads(); } /** * Waiting for all threads to complete the tasks. */ void WaitForAllTasksCompleted() { for (auto& thread : threads) { if (thread.joinable()) { thread.join(); } } threads.clear(); } /** * Create threads to start asynchronously executing tasks in the queue and * waiting for all threads to complete the tasks. * Note: it will block the main thread. */ void RunAndWaitForAllTasksCompleted() { if (tasks.empty()) { return; } CreateThreads(); WaitForAllTasksCompleted(); } private: void CreateThreads() { isStarted = true; auto fixedThreadsNum = std::min(tasks.size(), threadsNum); for (size_t threadIdx = 0; threadIdx < fixedThreadsNum; ++threadIdx) { (void)threads.emplace_back([this] { DoTask(); }); } } void DoTask() { // Once the thread is idle, it selects the task at the head of the queue to execute. while (true) { std::unique_lock lock(mutex); // No remaining tasks. Thread exits polling. if (tasks.empty()) { return; } Task task = tasks.top(); tasks.pop(); lock.unlock(); task(); } } std::priority_queue tasks; std::mutex mutex; size_t threadsNum; std::vector threads; bool isStarted = false; }; } // namespace Cangjie::Utils #endif cangjie_compiler-1.0.7/include/cangjie/Utils/Unicode.h000066400000000000000000000167231510705540100227020ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_UTILS_UNICODE #define CANGJIE_UTILS_UNICODE #include #include #include #include #include #include namespace Cangjie::Unicode { using UTF8 = unsigned char; using UTF16 = unsigned short; using UTF32 = unsigned; /** * UTF conversion result type */ enum class ConversionResult { OK, SOURCE_EXHAUSTED, SOURCE_ILLEGAL, TARGET_EXHAUSTED }; /** * Helper class, reference to array. */ template struct ArrayRef { ArrayRef(const ArrayRef& other) = default; ArrayRef(ArrayRef&& other) = default; ArrayRef& operator=(const ArrayRef& other) = default; ArrayRef& operator=(ArrayRef&& other) = default; template constexpr ArrayRef(T (&arr)[Length]) : beginv{arr}, sz{Length} { } constexpr ArrayRef(const T* v, int s): beginv{v}, sz{s} {} ArrayRef(const std::vector& v) : beginv{v.data()}, sz{static_cast(v.size())} {} int Size() const { return sz; } bool Empty() const { return sz == 0; } const T* begin() const { return beginv; } const T* end() const { return beginv + sz; } private: const T* beginv; int sz; }; struct StringRef : public ArrayRef { StringRef(const StringRef& other) = default; StringRef(StringRef&& other) = default; StringRef& operator=(const StringRef& other) = default; StringRef& operator=(StringRef&& other) = default; template constexpr StringRef(const char (&arr)[Length]) : ArrayRef{reinterpret_cast(arr), static_cast(strlen(arr))} { } constexpr StringRef(const UTF8* v, int s) : ArrayRef{v, s} {} explicit StringRef(const std::vector& v) : ArrayRef{v} {} StringRef(const std::string& s) : ArrayRef{reinterpret_cast(s.data()), static_cast(s.size())} {} StringRef(std::string_view s) : ArrayRef{reinterpret_cast(s.data()), static_cast(s.size())} {} std::vector ToUTF32() const; }; /** * Converts a UTF8 sequence to UTF32. */ ConversionResult ConvertUTF8toUTF32( const UTF8** sourceStart, const UTF8* sourceEnd, UTF32** targetStart, UTF32* targetEnd); constexpr int UNI_MAX_UTF8_BYTES_PER_CODE_POINT{4}; /** * Read one Unicode character from \p sourceStart. This function asserts a Unicode codepoint is successfully read. * \p sourceStart is advanced to tell the caller how many chars are consumed. */ UTF32 ReadOneUnicodeChar(const UTF8** sourceStart, const UTF8* sourceEnd); /** * Convert an Unicode code point to UTF8 sequence. * \param Source a Unicode code point. * \param [in,out] ResultPtr pointer to the output buffer, needs to be at least \c UNI_MAX_UTF8_BYTES_PER_CODE_POINT * bytes. On success \c ResultPtr is updated one past end of the converted sequence. * \returns true on success. */ bool ConvertCodepointToUTF8(UTF32 codepoint, char*& resultPtr); ///@{ /** * Converts a stream of raw bytes assumed to be UTF32 into a UTF8 std::string. * * \param [in] SrcBytes A buffer of what is assumed to be UTF-32 encoded text. * \param [out] Out Converted UTF-8 is stored here on success. * \returns true on success */ bool ConvertUTF32ToUTF8String(ArrayRef srcBytes, std::string& res); bool ConvertUTF32ToUTF8String(ArrayRef src, std::string& res); ///@} bool IsASCII(UTF32 v); /** * \returns true if character \p c falls in ascii and can be used as the first character of an identifier */ bool IsASCIIIdStart(UTF32 c); /** * \returns true if character \p c falls in ascii and can be used as a following character of an identifier */ bool IsASCIIIdContinue(UTF32 c); /** * \returns true if character \p c falls in Unicode XID_Start. This includes ASCII letters and excludes underscore. */ bool IsXIDStart(UTF32 c); /** * \returns true if character \p c falls in Unicode XID_Start. This includes ASCII letters and underscore. */ bool IsCJXIDStart(UTF32 c); /** * \returns true if character \p c falls in Unicode XID_Continue. This includes ASCII letters, digits, and underscore. */ bool IsXIDContinue(UTF32 c); /** * Result enum for normalisation form C quick check */ enum class NfcQcResult { YES, NO, MAYBE, }; /** * Get canonical combining class value of codepoint \p c. The value can be used either as an enum or an integer. See * Unicode 15.0.0 3.11 D104-D106. */ uint_fast8_t GetCanonicalCombiningClass(UTF32 c); /** * Quick check whether a string conforms to NFC. * \returns MAYBE indicates a normalisation has to be done to further know if \p s is in normalised from. */ NfcQcResult NfcQuickCheck(const std::string& s); /** * Convert \p s into NFD. */ std::string CanonicalDecompose(const std::string& s); ///@{ /* * Convert \p s into NFC. */ std::string CanonicalRecompose(const std::string& s); std::string CanonicalRecompose(const UTF8* begin, const UTF8* end); ///@} /// Get the Display width of character /// \param isCJK: indicates whether the codepoint is in CJK context, that is, ambiguous width characters are treated as /// double width; otherwise, they're treated as single width. int LookupWidth(UTF32 cp, bool isCJK); /** Check whether a string conforms to NFC. */ bool IsNFC(const std::string& s); /** * Converts a unicode string to Normalisation Form C. * \param [in,out] s the string to convert. If \p s is already in NFC form, no conversion is performed (which is faster * than \p CanonicalRecompose). */ void NFC(std::string& s); /** * Decompose Hanguls. * Exposed only for testing. */ void DecomposeHangul(UTF32 c, std::function&& emitChar); std::optional ComposeHangul(UTF32 a, UTF32 b); /// Returns Unicode tr11 based width of \p codepoint, or 0 if \p codepoint is a control character. /// If \p isCJK is true, ambiguous width characters are treated as double width; otherwise they are treated as single /// width. int SingleCharWidth(UTF32 codepoint, bool isCJK = false); /// Returns displayd width in columns of string \p s, based on Unicode tr11. /// Ambiguous width chars are treates as double width if \p isCJK is true; otherwise they are treated as single width. int StrWidth(StringRef s, bool isCJK = false); ///@{ /// Returns display width of string \p s when used in cjc diagnostic emitter. That is, the length of 0x09 is 4; /// otherwise the result equals to that of \ref StrWidth or \ref SingleCharWidth. int DisplayWidth(StringRef s, bool isCJK = false); int DisplayWidth(UTF32 cp, bool isCJK = false); ///@} struct UnicodeCharRange { UTF32 lower; UTF32 upper; friend inline bool operator<(UTF32 value, const UnicodeCharRange& range) { return value < range.lower; } friend inline bool operator<(const UnicodeCharRange& range, UTF32 value) { return range.upper < value; } }; /** * Helper class that checks whether a give unicode codepoint is in the specific range. */ struct UnicodeCharSet { const UnicodeCharRange* ranges; int size; template explicit constexpr UnicodeCharSet(const UnicodeCharRange (&r)[Size]) noexcept : ranges{r}, size{Size} { } bool Contains(UTF32 c) const; }; /** * Returns whether this unicode is legal, including Unicode Cs, that is, [0, 0xd7ff] U [0xe000, 0x10ffff]. */ bool IsLegalUnicode(UTF32 c); } // namespace Cangjie::Unicode #endif cangjie_compiler-1.0.7/include/cangjie/Utils/Utils.h000066400000000000000000000236671510705540100224210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares some utility functions. */ #ifndef CANGJIE_UTILS_UTILS_H #define CANGJIE_UTILS_UTILS_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cangjie/Utils/SafePointer.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/ConstantsUtils.h" #include "cangjie/Utils/SipHash.h" #include "cangjie/Utils/PartiallyPersistent.h" namespace Cangjie { namespace AST { // Forward types to avoid include recursively. struct Decl; struct ClassLikeDecl; struct InheritableDecl; struct FuncDecl; struct Node; struct File; struct Ty; } // namespace AST /** * Create an unique_ptr and check if succeed. */ template std::unique_ptr CreateUniquePtr(Args&&... args) { auto ptr = std::make_unique(std::forward(args)...); CJC_NULLPTR_CHECK(ptr); return ptr; } struct HashPair { template std::size_t operator()(const std::pair& pair) const { auto h1 = std::hash{}(pair.first); auto h2 = std::hash{}(pair.second); return h1 ^ h2; } }; /** hash_combine is a function used to create hash with fewer collisions. */ template inline size_t hash_combine(size_t& seed, const T& v) { std::hash hasher; seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); // 6,2 are specific constant in the hash algorithm. return seed; } namespace Utils { inline std::string FillZero(int num, int len) { std::ostringstream out; out << std::setfill('0') << std::setw(len) << num; return out.str(); } /* Get Mangled name for wrapper function of macro. */ std::string GetMacroFuncName(const std::string& fullPackageName, bool isAttr, const std::string& ident); /** * Trim from left. */ static inline void ltrim(std::string& str) { str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](const auto& it) { return !std::isspace(it); })); } /** * Trim from right. */ static inline void rtrim(std::string& str) { str.erase( std::find_if(str.rbegin(), str.rend(), [](const auto& it) { return !std::isspace(it); }).base(), str.end()); } /** * Trim from both sides. */ inline void TrimString(std::string& str) { ltrim(str); rtrim(str); } inline auto TrimStringVector(std::vector& container) { std::for_each(std::begin(container), std::end(container), [](auto& it) { TrimString(it); }); } template std::vector SetToVec(const T2& s) { std::vector res(s.cbegin(), s.cend()); return res; } template std::set VecToSet(const std::vector& v) { std::set res; for (auto& e : v) { res.insert(e); } return res; } template std::set VecToSortedSet(const std::vector& v, const C& cmp) { return std::set(v.begin(), v.end(), cmp); } template static inline void EraseIf(T& container, Pred pred) { auto iter = container.begin(); while (iter != container.end()) { if (pred(*iter)) { iter = container.erase(iter); } else { iter++; } } } template static inline std::unordered_set GetKeys(const std::unordered_map& container) { std::unordered_set keys; std::for_each(container.cbegin(), container.cend(), [&keys](auto& it) { keys.insert(it.first); }); return keys; } template std::set GetKeys(const std::map& container) { std::set keys; std::for_each(container.cbegin(), container.cend(), [&keys](auto& it) { keys.insert(it.first); }); return keys; } template bool InKeys(const K& key, const M& collection) { static_assert(std::is_same(), ""); return collection.find(key) != collection.cend(); } /** * Element is in container or not. */ template static inline bool In(const T element, const std::initializer_list& elements) { std::vector vec(elements); auto it = std::find(vec.cbegin(), vec.cend(), element); return it != vec.cend(); } template static inline bool In(const T element, const std::vector& container) { auto it = std::find(container.cbegin(), container.cend(), element); return it != container.cend(); } template static inline bool In(const T element, const std::set& set) { return set.find(element) != set.cend(); } template static inline bool In(const T element, const PSet& set) { return set.find(element) != set.cend(); } template static inline bool In(const T element, const std::unordered_set& set) { return set.find(element) != set.cend(); } template static inline std::enable_if_t, U> && std::is_pointer_v, bool> In( const T element, const std::set>& set) { return set.find(Ptr(element)) != set.cend(); } template static inline std::enable_if_t, U> && std::is_pointer_v, bool> In( const T element, const std::unordered_set>& set) { return set.find(Ptr(element)) != set.cend(); } template static inline bool In(const T& container, Pred pred) { auto it = std::find_if(container.begin(), container.end(), pred); return it != container.end(); } template static inline bool NotIn(T element, const std::vector& container) { auto it = std::find(container.begin(), container.end(), element); return it == container.end(); } template static inline bool NotIn(const T& container, Pred pred) { auto it = std::find_if(container.begin(), container.end(), pred); return it == container.end(); } template inline bool All(const T& container, Pred pred) { return std::all_of(container.cbegin(), container.cend(), pred); } template static inline bool AnyOf(Args... args) { return (... || args); } template static inline bool AllOf(Args... args) { return (... && args); } template inline void RemoveFromVec(std::vector& vec, const T& item) { vec.erase(std::remove(vec.begin(), vec.end(), item), vec.end()); } inline std::string StrToLower(std::string str) { (void)transform(str.begin(), str.end(), str.begin(), ::tolower); return str; } OverflowStrategy StringToOverflowStrategy(const std::string& name); bool ValidOverflowStrategy(const std::string& name); std::string OverflowStrategyName(const OverflowStrategy& overflowStrategy); std::string GenerateRandomHexString(); /** * Try to parse a string into an integer. * Return std::nullopt if input string is not a valid integer or it exceeds the range integer can represent. */ std::optional TryParseInt(const std::string& str); std::vector StringifyArgumentVector(int argc, const char** argv); std::unordered_map StringifyEnvironmentPointer(const char** envp); std::string GetRootPackageName(const std::string& fullPackageName); #ifdef _WIN32 std::optional GetApplicationPath(); #else std::optional GetApplicationPath( const std::string& argv0, const std::unordered_map& environmentVars); #endif // integer to char sequence compresser struct Out64 { explicit Out64(uint_fast64_t v1 = 0, bool start = true, bool finish = true) : v{v1}, delimAtStart{start}, delimAtFinish{finish} {} Out64& operator<<(uint_fast64_t v1) { v |= v1; return *this; } friend std::ostream& operator<<(std::ostream& out, const Out64& w) { constexpr uint_fast64_t byteLen{6}; static constexpr unsigned char chars[1 << byteLen] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '+', '-' }; // putchar internally cast the int argument to unsigned char right before printing, let's do the same here constexpr unsigned char delim = '&'; if (w.v) { if (w.delimAtStart) { out << delim; } constexpr int cacheSize = static_cast(sizeof(v) * CHAR_BIT / byteLen + 1); unsigned char cache[cacheSize]; auto v1{w.v}; int bit{0}; // compress a 2^6 value into one printable character while (v1) { cache[bit++] = chars[v1 % (1 << byteLen)]; v1 >>= byteLen; } while (--bit >= 0) { CJC_ASSERT(bit < cacheSize); out << cache[bit]; } if (w.delimAtFinish) { out << delim; } } return out; } private: uint_fast64_t v; bool delimAtStart, delimAtFinish; }; inline std::string HashString64(const std::string& str) { Out64 printer{0, false, false}; printer << static_cast(std::hash{}(str)); std::stringstream ss; ss << printer; return std::move(*ss.rdbuf()).str(); } } // namespace Utils } // namespace Cangjie #endif // CANGJIE_UTILS_UTILS_H cangjie_compiler-1.0.7/integration_build/000077500000000000000000000000001510705540100205115ustar00rootroot00000000000000cangjie_compiler-1.0.7/integration_build/scripts/000077500000000000000000000000001510705540100222005ustar00rootroot00000000000000cangjie_compiler-1.0.7/integration_build/scripts/cjnative/000077500000000000000000000000001510705540100240035ustar00rootroot00000000000000cangjie_compiler-1.0.7/integration_build/scripts/cjnative/build_native.sh000066400000000000000000000041461510705540100270110ustar00rootroot00000000000000#!/bin/bash # Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set -e; # 编译Cangjie编译器 + cjdb cd ${WORKSPACE}/cangjie_compiler; python3 build.py clean; python3 build.py build -t ${build_type} --build-cjdb ${AddOptsBuildpy}; python3 build.py install; source output/envsetup.sh; # 验证安装 cjc -v; # 编译运行时 cd ${WORKSPACE}/cangjie_runtime/runtime; python3 build.py clean; python3 build.py build -t ${build_type} -v ${cangjie_version}; python3 build.py install; cp -rf ${WORKSPACE}/cangjie_runtime/runtime/output/common/${kernel}_${build_type}_${cmake_arch}/{lib,runtime} ${WORKSPACE}/cangjie_compiler/output; # 编译标准库 cd ${WORKSPACE}/cangjie_runtime/std; python3 build.py clean; python3 build.py build -t ${build_type} \ --target-lib=${WORKSPACE}/cangjie_runtime/runtime/output \ --target-lib=$OPENSSL_PATH; python3 build.py install; cp -rf ${WORKSPACE}/cangjie_runtime/std/output/* ${WORKSPACE}/cangjie_compiler/output/; # 编译STDX扩展库 cd ${WORKSPACE}/cangjie_stdx; python3 build.py clean; python3 build.py build -t ${build_type} \ --include=${WORKSPACE}/cangjie_compiler/include \ --target-lib=$OPENSSL_PATH; python3 build.py install; export CANGJIE_STDX_PATH=${WORKSPACE}/cangjie_stdx/target/${kernel}_${cmake_arch}_cjnative/static/stdx; # 编译cjpm cd ${WORKSPACE}/cangjie_tools/cjpm/build; python3 build.py clean; python3 build.py build -t ${build_type} --set-rpath $RPATH; python3 build.py install; # 编译cjfmt cd ${WORKSPACE}/cangjie_tools/cjfmt; cd build; python3 build.py clean; python3 build.py build -t ${build_type}; python3 build.py install; # 编译hle cd ${WORKSPACE}/cangjie_tools/hyperlangExtension/build; python3 build.py clean; python3 build.py build -t ${build_type}; python3 build.py install; # 编译lsp cd ${WORKSPACE}/cangjie_tools/cangjie-language-server/build; python3 build.py clean; python3 build.py build -t ${build_type} -j 16; python3 build.py install;cangjie_compiler-1.0.7/integration_build/scripts/cjnative/build_windows-x86_64.sh000066400000000000000000000126511510705540100301510ustar00rootroot00000000000000#!/bin/bash # Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set -e; # 编译Cangjie编译器 for native cd ${WORKSPACE}/cangjie_compiler; python3 build.py clean; python3 build.py build -t ${build_type} ${AddOptsBuildpy}; # 编译Cangjie编译器 + cjdb for windows export CMAKE_PREFIX_PATH=${MINGW_PATH}/x86_64-w64-mingw32; python3 build.py build -t ${build_type} \ --product cjc \ --no-tests \ --target windows-x86_64 \ --target-sysroot ${MINGW_PATH}/ \ --target-toolchain ${MINGW_PATH}/bin \ --build-cjdb; python3 build.py build -t ${build_type} \ --product libs \ --target windows-x86_64 \ --target-sysroot ${MINGW_PATH}/ \ --target-toolchain ${MINGW_PATH}/bin; python3 build.py install --host windows-x86_64; python3 build.py install; cp -rf output-x86_64-w64-mingw32/* output; source output/envsetup.sh; # 验证安装 cjc -v; # 编译运行时 cd ${WORKSPACE}/cangjie_runtime/runtime; python3 build.py clean; python3 build.py build -t ${build_type} \ --target windows-x86_64 \ --target-toolchain ${MINGW_PATH}/bin \ -v ${cangjie_version}; python3 build.py install; cp -rf ${WORKSPACE}/cangjie_runtime/runtime/output/common/windows_${build_type}_x86_64/{lib,runtime} ${WORKSPACE}/cangjie_compiler/output; cp -rf ${WORKSPACE}/cangjie_runtime/runtime/output/common/windows_${build_type}_x86_64/{lib,runtime} ${WORKSPACE}/cangjie_compiler/output-x86_64-w64-mingw32; # 编译标准库 cd ${WORKSPACE}/cangjie_runtime/std; python3 build.py clean; python3 build.py build -t ${build_type} \ --target windows-x86_64 \ --target-lib=${WORKSPACE}/cangjie_runtime/runtime/output \ --target-lib=${MINGW_PATH}/x86_64-w64-mingw32/lib \ --target-sysroot ${MINGW_PATH}/ \ --target-toolchain ${MINGW_PATH}/bin; python3 build.py install; cp -rf ${WORKSPACE}/cangjie_runtime/std/output/* ${WORKSPACE}/cangjie_compiler/output/; cp -rf ${WORKSPACE}/cangjie_runtime/std/output/* ${WORKSPACE}/cangjie_compiler/output-x86_64-w64-mingw32/; # 编译STDX扩展库 cd ${WORKSPACE}/cangjie_stdx; python3 build.py clean; python3 build.py build -t ${build_type} \ --include=$WORKSPACE/cangjie_compiler/include \ --target-lib=${MINGW_PATH}/x86_64-w64-mingw32/lib \ --target windows-x86_64 \ --target-sysroot ${MINGW_PATH}/ \ --target-toolchain ${MINGW_PATH}/bin; python3 build.py install; export CANGJIE_STDX_PATH=${WORKSPACE}/cangjie_stdx/target/windows_x86_64_cjnative/static/stdx; # 编译cjpm cd ${WORKSPACE}/cangjie_tools/cjpm/build; python3 build.py clean; python3 build.py build -t ${build_type} --target windows-x86_64; python3 build.py install; # 编译cjfmt cd ${WORKSPACE}/cangjie_tools/cjfmt/build; python3 build.py clean; python3 build.py build -t ${build_type} --target windows-x86_64; python3 build.py install; # 编译hle cd ${WORKSPACE}/cangjie_tools/hyperlangExtension/build; python3 build.py clean; python3 build.py build -t ${build_type} --target windows-x86_64; python3 build.py install; # 编译lsp cd ${WORKSPACE}/cangjie_tools/cangjie-language-server/build; python3 build.py clean; python3 build.py build -t ${build_type} --target windows-x86_64 -j 16; python3 build.py install; # 清空历史构建 mkdir -p $WORKSPACE/software; rm -rf $WORKSPACE/software/*; # 打包Cangjie Frontend cd $WORKSPACE/software; mkdir -p cangjie/lib/windows_x86_64_cjnative; cp $WORKSPACE/cangjie_compiler/LICENSE cangjie; cp $WORKSPACE/cangjie_compiler/Open_Source_Software_Notice.docx cangjie; chmod -R 750 cangjie; mv $WORKSPACE/cangjie_compiler/output-x86_64-w64-mingw32/lib/windows_x86_64_cjnative/libcangjie-ast-support.a cangjie/lib/windows_x86_64_cjnative; find cangjie -print0 | xargs -0r touch -t "$BEP_BUILD_TIME"; find cangjie -print0 | LC_ALL=C sort -z | xargs -0 zip -o -X $WORKSPACE/software/cangjie-frontend-windows-x64-${cangjie_version}.zip; # 打包Cangjie SDK rm -rf cangjie && cp -R $WORKSPACE/cangjie_compiler/output-x86_64-w64-mingw32 cangjie; cp $WORKSPACE/cangjie_tools/cjpm/dist/cjpm.exe cangjie/tools/bin; mkdir -p cangjie/tools/config; cp $WORKSPACE/cangjie_tools/cjfmt/build/build/bin/cjfmt.exe cangjie/tools/bin; cp $WORKSPACE/cangjie_tools/cjfmt/config/*.toml cangjie/tools/config; cp $WORKSPACE/cangjie_tools/hyperlangExtension/target/bin/main.exe cangjie/tools/bin/hle.exe; cp -r $WORKSPACE/cangjie_tools/hyperlangExtension/src/dtsparser cangjie/tools; rm -rf cangjie/tools/dtsparser/*.cj; cp $WORKSPACE/cangjie_tools/cangjie-language-server/output/bin/LSPServer.exe cangjie/tools/bin; cp $WORKSPACE/cangjie_compiler/LICENSE cangjie; cp $WORKSPACE/cangjie_compiler/Open_Source_Software_Notice.docx cangjie; chmod -R 750 cangjie; find cangjie -print0 | xargs -0r touch -t "$BEP_BUILD_TIME"; find cangjie -print0 | LC_ALL=C sort -z | xargs -0 zip -o -X $WORKSPACE/software/cangjie-sdk-windows-x64-${cangjie_version}.zip; # 打包Cangjie STDX cp -R $WORKSPACE/cangjie_stdx/target/windows_x86_64_cjnative ./; cp $WORKSPACE/cangjie_stdx/LICENSE windows_x86_64_cjnative; cp $WORKSPACE/cangjie_stdx/Open_Source_Software_Notice.docx windows_x86_64_cjnative; chmod -R 750 windows_x86_64_cjnative; find windows_x86_64_cjnative -print0 | xargs -0r touch -t "$BEP_BUILD_TIME"; find windows_x86_64_cjnative -print0 | LC_ALL=C sort -z | xargs -0 zip -o -X $WORKSPACE/software/cangjie-stdx-windows-x64-${cangjie_version}.${stdx_version}.zip; chmod 550 *.zip; ls -lh $WORKSPACE/softwarecangjie_compiler-1.0.7/integration_build/scripts/cjnative/build_windows_libs.sh000066400000000000000000000027401510705540100302240ustar00rootroot00000000000000#!/bin/bash # Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set -e; # 编译windows frontend export CMAKE_PREFIX_PATH=${MINGW_PATH}/x86_64-w64-mingw32; cd ${WORKSPACE}/cangjie_compiler; python3 build.py build -t ${build_type} \ --product libs \ --target windows-x86_64 \ --target-sysroot ${MINGW_PATH}/ \ --target-toolchain ${MINGW_PATH}/bin; python3 build.py install; source output/envsetup.sh; # 验证cjc可用 cjc -v; # 编译windows 运行时 cd ${WORKSPACE}/cangjie_runtime/runtime; python3 build.py clean; python3 build.py build -t ${build_type} \ --target windows-x86_64 \ --target-toolchain ${MINGW_PATH}/bin \ -v ${cangjie_version}; python3 build.py install; cp -rf ${WORKSPACE}/cangjie_runtime/runtime/output/common/windows_${build_type}_x86_64/{lib,runtime} ${WORKSPACE}/cangjie_compiler/output; # 编译windows 标准库 cd ${WORKSPACE}/cangjie_runtime/std; python3 build.py clean; python3 build.py build -t ${build_type} \ --target windows-x86_64 \ --target-lib=${WORKSPACE}/cangjie_runtime/runtime/output \ --target-lib=${MINGW_PATH}/x86_64-w64-mingw32/lib \ --target-sysroot ${MINGW_PATH}/ \ --target-toolchain ${MINGW_PATH}/bin; python3 build.py install; cp -rf ${WORKSPACE}/cangjie_runtime/std/output/* ${WORKSPACE}/cangjie_compiler/output/;cangjie_compiler-1.0.7/integration_build/scripts/cjnative/package_native.sh000066400000000000000000000051371510705540100273060ustar00rootroot00000000000000#!/bin/bash # Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set -e; # 清空历史构建 mkdir -p $WORKSPACE/software; rm -rf $WORKSPACE/software/*; # 打包Cangjie Frontend cd $WORKSPACE/software; mkdir -p cangjie/lib/${kernel}_${cmake_arch}_cjnative; cp $WORKSPACE/cangjie_compiler/LICENSE cangjie; cp $WORKSPACE/cangjie_compiler/Open_Source_Software_Notice.docx cangjie; chmod -R 750 cangjie; mv $WORKSPACE/cangjie_compiler/output/lib/${kernel}_${cmake_arch}_cjnative/libcangjie-ast-support.a cangjie/lib/${kernel}_${cmake_arch}_cjnative; $tar \ --sort=name --mtime="@${SOURCE_DATE_EPOCH}" \ --owner=0 \ --group=0 \ --numeric-owner \ --pax-option=exthdr.name=$d/PaxHeaders/%f,delete=ctime \ -cf \ - cangjie | gzip -n > cangjie-frontend-${os}-${arch_name}-${cangjie_version}.tar.gz; # 打包Cangjie SDK rm -rf cangjie && cp -R $WORKSPACE/cangjie_compiler/output cangjie; cp $WORKSPACE/cangjie_tools/cjpm/dist/cjpm cangjie/tools/bin/cjpm; mkdir -p cangjie/tools/config; cp $WORKSPACE/cangjie_tools/cjfmt/build/build/bin/cjfmt cangjie/tools/bin; cp $WORKSPACE/cangjie_tools/cjfmt/config/*.toml cangjie/tools/config; cp $WORKSPACE/cangjie_tools/hyperlangExtension/target/bin/main cangjie/tools/bin/hle; cp -r $WORKSPACE/cangjie_tools/hyperlangExtension/src/dtsparser cangjie/tools; rm -rf cangjie/tools/dtsparser/*.cj; cp $WORKSPACE/cangjie_tools/cangjie-language-server/output/bin/LSPServer cangjie/tools/bin; cp $WORKSPACE/cangjie_compiler/LICENSE cangjie; cp $WORKSPACE/cangjie_compiler/Open_Source_Software_Notice.docx cangjie; chmod -R 750 cangjie; $tar \ --sort=name --mtime="@${SOURCE_DATE_EPOCH}" \ --owner=0 \ --group=0 \ --numeric-owner \ --pax-option=exthdr.name=$d/PaxHeaders/%f,delete=ctime \ -cf \ - cangjie | gzip -n > cangjie-sdk-${os}-${arch_name}-${cangjie_version}.tar.gz; # 打包Cangjie STDX cp -R $WORKSPACE/cangjie_stdx/target/${kernel}_${cmake_arch}_cjnative ./; cp $WORKSPACE/cangjie_stdx/LICENSE ${kernel}_${cmake_arch}_cjnative; cp $WORKSPACE/cangjie_stdx/Open_Source_Software_Notice.docx ${kernel}_${cmake_arch}_cjnative; chmod -R 750 ${kernel}_${cmake_arch}_cjnative; find ${kernel}_${cmake_arch}_cjnative -print0 | xargs -0r touch -t "$BEP_BUILD_TIME"; find ${kernel}_${cmake_arch}_cjnative -print0 | LC_ALL=C sort -z | xargs -0 zip -o -X $WORKSPACE/software/cangjie-stdx-${os}-${arch_name}-${cangjie_version}.${stdx_version}.zip; chmod 550 *.tar.gz *.zip; ls -lh $WORKSPACE/softwarecangjie_compiler-1.0.7/integration_build/scripts/ohcangjie/000077500000000000000000000000001510705540100241275ustar00rootroot00000000000000cangjie_compiler-1.0.7/integration_build/scripts/ohcangjie/build_flatc.sh000066400000000000000000000006511510705540100267350ustar00rootroot00000000000000#!/bin/bash # Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set -e; mkdir build && cd build; cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release ..; make flatc -j; mkdir -p $1; cp ./flatc ./$1; zip -r $1.zip ./$1; cangjie_compiler-1.0.7/integration_build/scripts/ohcangjie/build_ohos_aarch64-hwasan.sh000066400000000000000000000027771510705540100314160ustar00rootroot00000000000000#!/bin/bash # Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set -e; cd ${WORKSPACE}/cangjie_compiler; python3 build.py build -t release \ --product libs \ --hwasan \ --target ohos-aarch64 \ --target-toolchain ${OHOS_ROOT}/prebuilts/clang/ohos/${kernel}-${cmake_arch}/llvm/bin \ --target-sysroot ${OHOS_ROOT}/out/sdk/obj/third_party/musl/sysroot; python3 build.py install; source output/envsetup.sh; cd ${WORKSPACE}/cangjie_runtime/runtime; python3 build.py clean; python3 build.py build -t release \ -hwasan \ --target ohos-aarch64 \ --target-toolchain ${OHOS_ROOT} \ -v ${cangjie_version}; python3 build.py install; cp -rf ${WORKSPACE}/cangjie_runtime/runtime/output/common/linux_ohos_release_aarch64/{lib,runtime} ${WORKSPACE}/cangjie_compiler/output; cd ${WORKSPACE}/cangjie_runtime/std; python3 build.py clean; python3 build.py build -t release \ --hwasan \ --target ohos-aarch64 \ --target-lib=${WORKSPACE}/cangjie_runtime/runtime/output \ --target-lib=${WORKSPACE}/cangjie_runtime/runtime/output/common \ --target-toolchain ${OHOS_ROOT}/prebuilts/clang/ohos/${kernel}-${cmake_arch}/llvm/bin \ --target-sysroot ${OHOS_ROOT}/out/sdk/obj/third_party/musl/sysroot; python3 build.py install; cp -rf ${WORKSPACE}/cangjie_runtime/std/output/* ${WORKSPACE}/cangjie_compiler/output/;cangjie_compiler-1.0.7/integration_build/scripts/ohcangjie/build_ohos_aarch64.sh000066400000000000000000000027271510705540100301320ustar00rootroot00000000000000#!/bin/bash # Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set -e; cd ${WORKSPACE}/cangjie_compiler; python3 build.py build -t release \ --product libs \ --target ohos-aarch64 \ --target-toolchain ${OHOS_ROOT}/prebuilts/clang/ohos/${kernel}-${cmake_arch}/llvm/bin \ --target-sysroot ${OHOS_ROOT}/out/sdk/obj/third_party/musl/sysroot; python3 build.py install; source output/envsetup.sh; cd ${WORKSPACE}/cangjie_runtime/runtime; python3 build.py clean; python3 build.py build -t release \ --target ohos-aarch64 \ --target-toolchain ${OHOS_ROOT} \ -v ${cangjie_version}; python3 build.py install; cp -rf ${WORKSPACE}/cangjie_runtime/runtime/output/common/linux_ohos_release_aarch64/{lib,runtime} ${WORKSPACE}/cangjie_compiler/output; cd ${WORKSPACE}/cangjie_runtime/std; python3 build.py clean; python3 build.py build -t release \ --target ohos-aarch64 \ --target-lib=${WORKSPACE}/cangjie_runtime/runtime/output \ --target-lib=${WORKSPACE}/cangjie_runtime/runtime/output/common \ --target-toolchain ${OHOS_ROOT}/prebuilts/clang/ohos/${kernel}-${cmake_arch}/llvm/bin \ --target-sysroot ${OHOS_ROOT}/out/sdk/obj/third_party/musl/sysroot; python3 build.py install; cp -rf ${WORKSPACE}/cangjie_runtime/std/output/* ${WORKSPACE}/cangjie_compiler/output/;cangjie_compiler-1.0.7/integration_build/scripts/ohcangjie/build_ohos_stdx.sh000066400000000000000000000040541510705540100276570ustar00rootroot00000000000000#!/bin/bash # Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set -e; cd ${WORKSPACE}/cangjie_stdx; unset LD_LIBRARY_PATH; export CANGJIE_HOME=${WORKSPACE}/cangjie_compiler/output; export PATH=${CANGJIE_HOME}/bin:${CANGJIE_HOME}/tools/bin:$PATH:${HOME}/.cjpm/bin; export LD_LIBRARY_PATH=${CANGJIE_HOME}/runtime/lib/linux_$(arch)_cjnative:${CANGJIE_HOME}/tools/lib python3 build.py clean; python3 build.py build -t release \ --include=${WORKSPACE}/cangjie_compiler/include \ --target-lib=$OPENSSL_PATH \ --target ohos-aarch64 \ --target-sysroot ${OHOS_ROOT}/out/sdk/obj/third_party/musl/sysroot \ --target-toolchain ${OHOS_ROOT}/prebuilts/clang/ohos/linux-x86_64/llvm/bin; python3 build.py install; # 打包Cangjie STDX cp -R $WORKSPACE/cangjie_stdx/target/linux_ohos_aarch64_cjnative ./; cp $WORKSPACE/cangjie_stdx/LICENSE linux_ohos_aarch64_cjnative; cp $WORKSPACE/cangjie_stdx/Open_Source_Software_Notice.docx linux_ohos_aarch64_cjnative; chmod -R 750 linux_ohos_aarch64_cjnative; zip -qr $WORKSPACE/software/cangjie-stdx-ohos-aarch64-${cangjie_version}.${stdx_version}.zip linux_ohos_aarch64_cjnative; python3 build.py clean; python3 build.py build -t release \ --include=${WORKSPACE}/cangjie_compiler/include \ --target-lib=$OPENSSL_PATH \ --target ohos-x86_64 \ --target-sysroot ${OHOS_ROOT}/out/sdk/obj/third_party/musl/sysroot \ --target-toolchain ${OHOS_ROOT}/prebuilts/clang/ohos/linux-x86_64/llvm/bin; python3 build.py install; # # 打包Cangjie STDX cp -R $WORKSPACE/cangjie_stdx/target/linux_ohos_x86_64_cjnative ./; cp $WORKSPACE/cangjie_stdx/LICENSE linux_ohos_x86_64_cjnative; cp $WORKSPACE/cangjie_stdx/Open_Source_Software_Notice.docx linux_ohos_x86_64_cjnative; chmod -R 750 linux_ohos_x86_64_cjnative; zip -qr $WORKSPACE/software/cangjie-stdx-ohos-x86_64-${cangjie_version}.${stdx_version}.zip linux_ohos_x86_64_cjnative;cangjie_compiler-1.0.7/integration_build/scripts/ohcangjie/build_ohos_x86_64.sh000066400000000000000000000027551510705540100276410ustar00rootroot00000000000000#!/bin/bash # Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set -e; cd ${WORKSPACE}/cangjie_compiler; python3 build.py install; python3 build.py build -t release \ --product libs \ --target ohos-x86_64 \ --target-toolchain ${OHOS_ROOT}/prebuilts/clang/ohos/${kernel}-${cmake_arch}/llvm/bin \ --target-sysroot ${OHOS_ROOT}/out/sdk/obj/third_party/musl/sysroot; python3 build.py install; source output/envsetup.sh; cd ${WORKSPACE}/cangjie_runtime/runtime; python3 build.py clean; python3 build.py build -t release \ --target ohos-x86_64 \ --target-toolchain ${OHOS_ROOT} \ -v ${cangjie_version}; python3 build.py install; cp -rf ${WORKSPACE}/cangjie_runtime/runtime/output/common/linux_ohos_release_x86_64/{lib,runtime} ${WORKSPACE}/cangjie_compiler/output; cd ${WORKSPACE}/cangjie_runtime/std; python3 build.py clean; python3 build.py build -t release \ --target ohos-x86_64 \ --target-lib=${WORKSPACE}/cangjie_runtime/runtime/output \ --target-lib=${WORKSPACE}/cangjie_runtime/runtime/output/common \ --target-toolchain ${OHOS_ROOT}/prebuilts/clang/ohos/${kernel}-${cmake_arch}/llvm/bin \ --target-sysroot ${OHOS_ROOT}/out/sdk/obj/third_party/musl/sysroot; python3 build.py install; cp -rf ${WORKSPACE}/cangjie_runtime/std/output/* ${WORKSPACE}/cangjie_compiler/output/;cangjie_compiler-1.0.7/schema/000077500000000000000000000000001510705540100162475ustar00rootroot00000000000000cangjie_compiler-1.0.7/schema/BCHIRFormat.fbs000066400000000000000000000043501510705540100207450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. include "ModuleFormat.fbs"; namespace BCHIRFormat; file_identifier "BIR2"; struct CodePosition { file: uint32; line: uint32; // line col: uint32; // column } struct PosAnnotation { index: uint32; codePosition: CodePosition; } // Just used in debug mode. struct ChirIDAnnotation { index: uint32; // the original CHIR node ID chidNodeId: uint64; } table TType { //All the different types are flattened kind:uint8; // TypeKind static casted to an unsigned int // TFunc retType:uint32; hasVarLenParam:bool; // Func type. isCFunc:bool; // Func type. isBoxed:bool; // Enum type. // TypeArguments, also for funcTy's paramTypes typeArgs:[uint32]; // TypeArguments, also for structdef Ty's paramTypes typeLocalMemberArgs:[uint32]; dims: uint64 = 0; // RawArray } table MangledNamePair { index: uint32; // index to the vector bytecode in Definition mangled: uint32; // index to the vector mangledNames in Model } table CodePositionPair { index: uint32; // index to the vector bytecode in Definition pos: CodePosition; } struct VMethod { method : uint32; // index to the vector mangledNames in Model function : uint32; // index to the vector mangledNames in Model } table ClassElem { mangledName : uint32; // index to the vector mangledNames in Model superClasses : [uint32]; // indexes to the vector mangledNames in Model vtable : [VMethod]; finalizer: uint32 = 0; } table Definition { mangledIndex: uint32; numArgs: uint32; numLVars: uint32; bytecodes: [uint32]; mangledNamesMap: [MangledNamePair]; codePositions: [CodePositionPair]; } table Model { // WORK IN PROGRESS packageName: string; // package name allTypes: [TType]; // all saved types bchirTypes: [uint32]; // the bchir package types, pointers to allTypes mangledNames: [string]; globalVars: [Definition]; functions: [Definition]; classTable: [ClassElem]; globalInitFunc: string; fileNames: [string]; strings: [string]; } root_type Model;cangjie_compiler-1.0.7/schema/CMakeLists.txt000066400000000000000000000031261510705540100210110ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # Generate cangjie code for libast using flatc with cangjie backend. set(FLATC_EXECUTABLE "${CMAKE_BINARY_DIR}/bin/flatc") set(FLATBUFFERS_SOURCE ModuleFormat.fbs CachedASTFormat.fbs NodeFormat.fbs) list(APPEND FLATBUFFERS_SOURCE BCHIRFormat.fbs PackageFormat.fbs) list(APPEND FLATBUFFERS_SOURCE MacroMsgFormat.fbs) set(FLATC_OUTPUTS) foreach(FILE ${FLATBUFFERS_SOURCE}) get_filename_component(FILENAME ${FILE} NAME_WE) set(FLATC_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/flatbuffers/${FILENAME}_generated.h") add_custom_command( OUTPUT ${FLATC_OUTPUT} COMMAND ${FLATC_EXECUTABLE} ARGS --no-warnings -c -o "${CMAKE_CURRENT_BINARY_DIR}/flatbuffers" ${FILE} DEPENDS ${FILE} flatbuffers # flatc defined in flatbuffers project: add_executable(flatc..) WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) list(APPEND FLATC_OUTPUTS ${FLATC_OUTPUT}) endforeach() set(FLATBUFFERS_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_BINARY_DIR}/include PARENT_SCOPE) add_custom_target(CangjieFlatbuffersHeaders DEPENDS ${FLATC_OUTPUTS} ${FLATC_LIBAST_OUTPUTS}) cangjie_compiler-1.0.7/schema/CachedASTFormat.fbs000066400000000000000000000076461510705540100216500ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. // This file discribes the format of a cangjie package used in incremental compilation. namespace CachedASTFormat; // Module file_identifier "CAST"; // CHIR Opt infos table DeclDep{ decl:string; dependency:[string]; } // describes the change of srcDecl is to be perceived by effectedDecls table EffectMap{ srcDecl:string; effectedDecls:[string]; } table VirtualDep{ raw: string; // 'rawMangledName' of decl wrapper: string; // 'mangledName' of virtual func wrapper for CodeGen } // Semantic usage infos table Usage { definition: string; // 'rawMangledName' of context decl, only be toplevel or member decl. apiUsage: UseInfo; // Use info for API part. bodyUsage: UseInfo; // Use info for implementation part. boxedTypes: [string]; // 'rawMangledName' of the types which have been boxed in current 'definition'. } table UseInfo { usedDecls: [string]; // 'rawMangledName' of decls that directly used decl usedNames: [NameInfo]; // identifiers that used as type annotation or decl name } table NameInfo { name: string; // Used identifier. conditions: [bool]; // Conditions: 1. whether the name has been used as unqualified. // 2. whether the name was resolved as unqualified imported decl // etc parentDecls: [string]; // 'rawMangledName' or builtin type's name of 'name''s qualified parent decl. qualifiers: [string]; // qualifier prefix to access package's decl name. } table Relation { definition: string; // 'rawMangledname' of the declaration or name of extended built-in type. inherited: [string]; // 'rawMangledname' of the inherited types. extends: [string]; // 'rawMangledname' of the extened decls. extendInterfaces: [string]; // 'rawMangledname' of the extend interfaces. } table CompilerAddedUsage { definition: string; // 'rawMangledname' of the declaration related: [string]; // 'mangledName' of compiler added decls for CodeGen } table SemanticInfo { usages: [Usage]; relations: [Relation]; builtInTypeRelations: [Relation]; compilerAddedUsages: [CompilerAddedUsage]; } // incr ast begins here table GlobalVarIndex { file: string; id: int; } table MemberDecl { mangle:string; // raw mangled name sig:uint64; // sig hash srcUse:uint64; // srcuse hash body:uint64; // body hash type:uint8; isGV:bool; // is global or static var gvid:GlobalVarIndex; members:[MemberDecl]; // member decls of member decl, e.g. property can have getters & setters cgMangle:string; // mangled name for CodeGen } table TopDecl { mangle:string; // raw mangled name sig:uint64; // signature hash srcUse:uint64; // source usage hash body:uint64; // body hash type:uint8; // astKind isGV:bool; // is global var instVar:uint64; // instance variable hash virt:uint64; // virtual function hash gvid:GlobalVarIndex; // see struct GVID members:[MemberDecl]; extends:[string]; // mangled name of extended decls cgMangle:string; // mangled name for CodeGen } table HashedPackage{ version:string; // cjc version packageName:string; // package name. specs:uint64; // package spec reference compileOptionArgs:[string]; topDecls:[TopDecl]; // src decls importedDecls:[TopDecl]; // imported decls varAndFunc:[DeclDep]; // dependencies among global vars and funcs chirOptInfo:[EffectMap]; virtualDep:[VirtualDep]; varInitDep:[VirtualDep]; ccOutFuncs:[string]; // raw mangled name of top or mem funcs had closure convert lambdaCounter:uint64; envClassCounter:uint64; strLitCounter:uint64; semanticInfo:SemanticInfo; bitcodeFilesName:[string]; } root_type HashedPackage; cangjie_compiler-1.0.7/schema/MacroMsgFormat.fbs000066400000000000000000000032521510705540100216260ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. namespace MacroMsgFormat; struct Position { file_id: uint32; line: int32; column: int32; } table Token { kind: uint8; value: string; begin: Position; end: Position; delimiterNum : uint32 = 1; } table IdInfo { name : string; pos : Position; } struct IntValue { val : int64; } struct BoolValue { val : bool; } table TokensValue { val: [Token]; } union OptionValue { sValue : string, iValue : IntValue, bValue : BoolValue, tValue : TokensValue } table ItemInfo { key : string; value : OptionValue; } table ChildMsg { childName : string; items : [ItemInfo]; } table DefLib { paths : [string]; } table Diagnostic { diagSeverity : int32; begin : Position; end : Position; errorMessage : string; mainHint: string; } table MacroCall { id : IdInfo; hasAttrs : bool; args : [Token]; attrs : [Token]; parentNames : [string]; childMsges : [ChildMsg]; methodName : string; packageName : string; libPath : string; begin : Position; end : Position; } table MacroResult { id : IdInfo; status : uint8; tks : [Token]; items : [ItemInfo]; assertParents : [string]; diags : [Diagnostic]; } table MultiMacroCalls { calls : [MacroCall]; } struct ExitTask { flag : bool; } union MsgContent { defLib : DefLib, multiCalls : MultiMacroCalls, macroResult : MacroResult, exitTask : ExitTask } table MacroMsg { content : MsgContent; } root_type MacroMsg;cangjie_compiler-1.0.7/schema/ModuleFormat.fbs000066400000000000000000000313251510705540100213450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. // This is actually a package format namespace PackageFormat; // Module file_identifier "CJOF"; // We expect the loader to redo the symbol table // but not type inference / check within a loaded module // Using 'uint32' as type of 'SemaTy/Decl/Expr' 's table index. // 0 is invalid index, valid index start from 1, which is the offset of table plus 1. // Because of the size of AST node and 'Ty', length of uint32 is enough that oom will occurred before out-of-range. enum DeclKind : uint16 { InvalidDecl, ClassDecl, InterfaceDecl, FuncDecl, PropDecl, VarDecl, VarWithPatternDecl, FuncParam, StructDecl, EnumDecl, ExtendDecl, TypeAliasDecl, GenericParamDecl, BuiltInDecl, } // Position member's max length same as 'struct Position''s definition. struct Position { file:uint32; pkgId:uint32; // Package's index that file beglongs to. 0 indicates current package line:int32; column:int32; ignore:bool; } table FullId { pkgId:int32; // package that the decl belongs to decl:string; // exportId of refed node in other package index:uint32; // index of referenced decl in same package } table Constraint { begin:Position; end:Position; type:uint32; // type of corresponding typeParameter uppers:[uint32]; // UpperBounds } table Generic{ typeParameters:[uint32]; // GenericParamDecls constraints:[Constraint]; } table FuncParamList { params:[uint32]; desugars:[uint32]; } table FuncBody { paramLists:[FuncParamList]; retType:uint32; body:uint32; // index of body expr always:bool; captureKind:uint8; } struct DeclHash { instVar:uint64; virt:uint64; sig:uint64; srcUse:uint64; bodyHash:uint64; } union ConstValue { Int8Value, UInt8Value, Int16Value, UInt16Value, Int32Value, UInt32Value, Int64Value, UInt64Value, Float32Value, Float64Value, ArrayValue, StringValue:string, CompositeValue:CompositeValueIndex, } struct Int8Value { val:int8; } struct UInt8Value { val:uint8; } struct Int16Value { val:int16; } // alse represents Float16 struct UInt16Value { val:uint16; } struct Int32Value { val:int32; } struct UInt32Value { val:uint32; } struct Int64Value { val:int64; } struct UInt64Value { val:uint64; } struct Float32Value { val:float32; } struct Float64Value { val:float64; } table ArrayValue { val:[ConstValue]; } struct CompositeValueIndex { idx:uint32; } table MemberValue { field:string; type:uint32; value:ConstValue; } table CompositeValue { // represents class, struct, tuple, enum type:uint32; fields:[MemberValue]; } table AutoDiffInfo { isDiff:bool; isAdj:bool; primal:string; excepts:[string]; includes:[string]; stage:uint64; } table ClassInfo { inheritedTypes:[uint32]; body:[uint32]; adInfo:AutoDiffInfo; isAnno:bool = false; // indicates if current class is custom annotation annoTargets:uint8; // available if current class is custom annotation runtimeVisible:bool; annoTargets2:uint8; } table InterfaceInfo { inheritedTypes:[uint32]; body:[uint32]; } table StructInfo { inheritedTypes:[uint32]; body:[uint32]; adInfo:AutoDiffInfo; } table EnumInfo { inheritedTypes:[uint32]; body:[uint32]; adInfo:AutoDiffInfo; hasArguments:bool; nonExhaustive:bool; ellipsisPos:Position; } table ExtendInfo { inheritedTypes:[uint32]; body:[uint32]; } table VarInfo { isVar:bool; isConst:bool; isMemberParam:bool; initializer:uint32; // Index of expression value:ConstValue; // Has value if it is constant } table VarWithPatternInfo { isVar:bool; isConst:bool; irrefutablePattern:Pattern; // Only existed in expression child node. initializer:uint32; // Index of expression } enum OverflowPolicy : uint8 { NA, Checked, Wrapping, Throwing, Saturating, } enum OperatorKind : uint8 { NA, // Overloadable Index, // [] Call, // () Not, // ! Power, // ** Multiply, // * Divide, // / Remainder, // % Add, // + Subtract, // - BitLeftShift, // << BitRightShift, // >> LT, // < LE, // <= GT, // > GE, // >= Equal, // == NotEqual, // != BitAnd, // & BitXor, // ^ BitOr, // | // Others PostInc, // ++ PostDec, // -- Is, // is As, // as LogicAnd, // && LogicOr, // || Coalescing, // ?? Pipeline, // |> Composition, // ~> Assign, // = PowerAssign, // **= MultiplyAssign, // *= DivideAssign, // /= RemainderAssign, // %= AddAssign, // += SubtractAssign, // -= LeftShiftAssign, // <<= RightShiftAssign, // >>= BitAndAssign, // &= BitXorAssign, // ^= BitOrAssign, // |= LogicAndAssign, // &&= LogicOrAssign, // ||= } table FuncInfo { funcBody:FuncBody; overflowPolicy:OverflowPolicy; op:OperatorKind = NA; adInfo:AutoDiffInfo; isConst:bool; isInline:bool; isFastNative:bool; } table ParamInfo { isNamedParam:bool; isMemberParam:bool; defaultVal:uint32; // Index of expression } table PropInfo { isConst:bool; isMutable:bool; setters:[uint32]; getters:[uint32]; } enum BuiltInType : uint8 { Array, VArray, CPointer, CString, CFunc, } table BuiltInInfo { builtInType:BuiltInType; } table AliasInfo { aliasedTy:uint32; // Index of semantic type } union DeclInfo { ClassInfo, InterfaceInfo, StructInfo, EnumInfo, ExtendInfo, PropInfo, VarInfo, VarWithPatternInfo, ParamInfo, FuncInfo, BuiltInInfo, AliasInfo, } table Decl { kind:DeclKind = InvalidDecl; isTopLevel:bool = false; // whether the decl is toplevel fullPkgName:string; genericDecl:FullId; generic:Generic; begin:Position; end:Position; identifier:string; identifierPos:Position; attributes:[uint64]; annotations:[Anno]; // Semantic & CodeGen info type: uint32; mangledName: string; // mangledName for CodeGen exportId: string; // exportId for import // info used in ast diff mangledBeforeSema:string; hash:DeclHash; // Specific information for each specific kind of declaration info:DeclInfo; // record dependencies result of common part for CJMP dependencies:[FullId]; } enum AnnoKind : uint16 { Deprecated, TestRegistration, Frozen, Custom, JavaMirror, JavaImpl, ObjCMirror, ObjCImpl, ForeignName } // Current annotation representation is a balance between needs of @Deprecated // and potential generalization for other annotations table Anno { // Short name "Anno" because of the clash with "Annotation" from "PackageFormat.fbs" kind:AnnoKind; identifier:string; args:[AnnoArg]; } table AnnoArg { name:string; expr:uint32; // index of Expr (LitConstExpr) } enum ExprKind : uint16 { InvalidExpr, WildcardExpr, PrimitiveTypeExpr, ReturnExpr, JumpExpr, MemberAccess, RefExpr, CallExpr, UnaryExpr, IncOrDecExpr, LitConstExpr, BinaryExpr, SubscriptExpr, AssignExpr, ArrayExpr, PointerExpr, TypeConvExpr, ThrowExpr, SpawnExpr, ArrayLit, TupleLit, MatchExpr, LetPatternDestructor, IfExpr, TryExpr, WhileExpr, DoWhileExpr, LambdaExpr, Block, MatchCase, MatchCaseOther, AdjointExpr, GradExpr, ValWithGradExpr, VJPExpr, ForInExpr, IfAvailableExpr, PerformExpr, ResumeExpr, } enum CallKind : uint8 { NA, CallDeclaredFunction, CallObjectCreation, CallStructCreation, CallSuperFunction, CallVariadicFunction, CallFunctionPtr, CallAnnotation, CallBuiltinFunction, CallIntrinsicFunction, } enum LitConstKind : uint8 { Integer, RuneByte, Float, Rune, String, JString, Bool, Unit, } enum StringKind : uint8 { Normal, JString, MultiLine, MultiLineRaw, } enum ForInKind : uint8 { NA, Range, String, Iterator, } union ExprInfo { CallInfo, UnaryInfo, BinaryInfo, IncOrDecInfo, LitConstInfo, ReferenceInfo, LambdaInfo, AssignInfo, ArrayInfo, JumpInfo, FuncArgInfo, SubscriptInfo, MatchInfo, BlockInfo, TryInfo, LetPatternDestructorInfo, ForInInfo, MatchCaseInfo, SpawnInfo, } table CallInfo { hasSideEffect:bool; callKind:CallKind = NA; } table UnaryInfo { op:OperatorKind; } table BinaryInfo { op:OperatorKind; } table IncOrDecInfo { op:OperatorKind; } table LitConstInfo { strValue:string; constKind:LitConstKind; strKind:StringKind; } table ReferenceInfo { // For RefExpr/MemberAccess reference:string; // also for LitConstExpr string value target:FullId; instTys:[uint32]; // Instantiated type index vectors. matchedParentTy:uint32; // For instantiation re-arrange. } table LambdaInfo { funcBody:FuncBody (required); // LambdaExpr supportMock:bool; } table AssignInfo { isCompound:bool; op:OperatorKind; } table ArrayInfo { initFunc:FullId; isValueArray:bool; } table JumpInfo { isBreak:bool; } table FuncArgInfo { withInout:bool; isDefaultVal:bool; } table SubscriptInfo { isTupleAccess:bool; } table MatchInfo { matchMode:bool; } table BlockInfo { isExpr:[bool]; } table TryInfo { resources:[FullId]; // for try-with-resource patterns:[Pattern]; } table LetPatternDestructorInfo { patterns:[Pattern]; } table ForInInfo { pattern:Pattern; forInKind:ForInKind = NA; } table MatchCaseInfo { patterns:[Pattern]; } table SpawnInfo { future:FullId; } table Expr { kind:ExprKind = InvalidExpr; begin:Position; end:Position; mapExpr:uint32; // index of expr to deal with side effect. operands:[uint32]; // index of sub expressions type:uint32; overflowPolicy:OverflowPolicy; info:ExprInfo; } enum PatternKind:int8 { InvalidPattern, ConstPattern, WildcardPattern, VarPattern, TuplePattern, TypePattern, EnumPattern, ExceptTypePattern, CommandTypePattern, } table Pattern { kind:PatternKind = InvalidPattern; begin:Position; end:Position; patterns:[Pattern]; types:[uint32]; // SemaTy indexes. exprs:[uint32]; values:[ConstValue]; matchBeforeRuntime:bool; needRuntimeTypeCheck:bool; } // For compatibility any new type kind MUST only be added at last. enum TypeKind : uint16 { Invalid, // invalid // primitive type Unit, // unit // integer Int8, Int16, Int32, Int64, IntNative, // unsigned integer UInt8, UInt16, UInt32, UInt64, UIntNative, // float Float16, Float32, Float64, Rune, // char Nothing, Bool, // bool // composite type Tuple, // Tuple Enum, // enum Func, // function Struct, // struct // reference type Array, // Array VArray, // VArray CPointer, CString, Class, Interface, Type, // typealias Generic, // genericTy } union SemaTyInfo { FuncTyInfo, CompositeTyInfo, GenericTyInfo, ArrayTyInfo, } table FuncTyInfo { retType:uint32; // Func return type isC:bool; // CFunc or @C func type. hasVariableLenArg:bool; } table CompositeTyInfo { declPtr:FullId; isThisTy:bool=false; } table GenericTyInfo { declPtr:FullId; upperBounds:[uint32]; } table ArrayTyInfo { dimsOrSize:int64; } table SemaTy { kind:TypeKind=Unit; // TypeArguments, also for funcTy's paramTypes typeArgs:[uint32]; info:SemaTyInfo; } enum AccessModifier : uint8 { Private, Internal, Protected, Public, } table ImportSpec { begin:Position; end:Position; prefixPaths:[string]; identifier:string; asIdentifier:string; reExport:AccessModifier = Private; isDecl:bool=false; hasDoubleColon:bool; } table Imports { importSpecs:[ImportSpec]; } enum PackageKind : uint8 { Normal, // normal package. Macro, // macro package. Foreign, // foreign language package. Mock, // package with mocking support. } enum AccessLevel : uint8 { Public, Protected, Internal, } struct CjoVersion { major_num:uint8; minor_num:uint8; patch_num:uint8; } table FileInfo { fileID: uint32; begin: Position; end: Position; } // all SemaTys, decls are saved here, and indexed by other nodes. table Package { version:string; // cjc version. cjoVersion:CjoVersion; // cjo format version. fullPkgName:string; // full package name. pkgDepInfo:string; // package dependency info. imports:[string]; // all imported packages. allFiles:[string]; // all files in package. allFileImports:[Imports]; // all file imports in package. allTypes:[SemaTy]; // all saved SemaTys. allDecls:[Decl]; // all saved decls. allExprs:[Expr]; // all saved expressions. allValues:[CompositeValue];// all saved constant composite values. kind:PackageKind = Normal; // package kind. access:AccessLevel = Public; moduleName:string; // append some info of File for CJMP allFileInfo: [FileInfo]; } root_type Package; cangjie_compiler-1.0.7/schema/NodeFormat.fbs000066400000000000000000000440431510705540100210060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. namespace NodeFormat; struct Position { file_id: uint32; line: int32; column: int32; } union AnyExpr { BINARY_EXPR: BinaryExpr, UNARY_EXPR: UnaryExpr, PAREN_EXPR: ParenExpr, LIT_CONST_EXPR: LitConstExpr, CALL_EXPR: CallExpr, REF_EXPR: RefExpr, RETURN_EXPR: ReturnExpr, ASSIGN_EXPR: AssignExpr, MEMBER_ACCESS: MemberAccess, IF_EXPR: IfExpr, BLOCK: Block, LAMBDA_EXPR: LambdaExpr, ARRAY_LIT: ArrayLit, TUPLE_LIT: TupleLit, SUBSCRIPT_EXPR: SubscriptExpr, RANGE_EXPR: RangeExpr, FOR_IN_EXPR: ForInExpr, IS_EXPR: IsExpr, AS_EXPR: AsExpr, TRAILING_CLOSURE_EXPR: TrailingClosureExpr, TYPE_CONV_EXPR: TypeConvExpr, THROW_EXPR: ThrowExpr, TRY_EXPR: TryExpr, PRIMITIVE_TYPE_EXPR: PrimitiveTypeExpr, WHILE_EXPR: WhileExpr, DO_WHILE_EXPR: DoWhileExpr, JUMP_EXPR: JumpExpr, INC_OR_DEC_EXPR: IncOrDecExpr, SPAWN_EXPR: SpawnExpr, SYNCHRONIZED_EXPR: SynchronizedExpr, LET_PATTERN_DESTRUCTOR: LetPatternDestructor, TOKEN_PART: TokenPart, QUOTE_EXPR: QuoteExpr, MATCH_EXPR: MatchExpr, OPTIONAL_EXPR: OptionalExpr, OPTIONAL_CHAIN_EXPR: OptionalChainExpr, MACRO_EXPAND_EXPR: MacroExpandExpr, WILDCARD_EXPR: WildcardExpr, ARRAY_EXPR: ArrayExpr, PERFORM_EXPR: PerformExpr, RESUME_EXPR: ResumeExpr, } table MatchCase { base: NodeBase; patterns: [Pattern]; patternguard: Expr; if_pos: Position; arrow_pos: Position; expr_or_decls: Block; bit_or_pos_vec: [Position]; } table MatchCaseOther { base: NodeBase; match_expr: Expr; arrow_pos: Position; expr_or_decls: Block; } union AnyPattern { CONST_PATTERN: ConstPattern, WILDCARD_PATTERN: WildcardPattern, VAR_PATTERN: VarPattern, EXCEPT_TYPE_PATTERN: ExceptTypePattern, TYPE_PATTERN: TypePattern, ENUM_PATTERN: EnumPattern, VAR_OR_ENUM_PATTERN: VarOrEnumPattern, TUPLE_PATTERN: TuplePattern, COMMAND_TYPE_PATTERN: CommandTypePattern, } table Pattern { base: NodeBase; pattern: AnyPattern; } table ConstPattern { base: NodeBase; literal: Expr; operator_call_expr: CallExpr; } table WildcardPattern { base: NodeBase; } table VarPattern { base: NodeBase; var_decl: VarDecl; } table ExceptTypePattern { base: NodeBase; pattern: Pattern; pattern_pos: Position; colon_pos: Position; types: [Type]; bit_or_pos_vec: [Position]; } table CommandTypePattern { base: NodeBase; pattern: Pattern; pattern_pos: Position; colon_pos: Position; types: [Type]; bit_or_pos_vec: [Position]; } table TypePattern { base: NodeBase; pattern: Pattern; colon_pos: Position; type: Type; } table EnumPattern { base: NodeBase; ref: Expr; left_paren_pos: Position; patterns: [Pattern]; right_paren_pos: Position; comma_pos_vec: [Position]; } table VarOrEnumPattern { base: NodeBase; identifier: string; pattern: Pattern; } table TuplePattern { base: NodeBase; left_paren_pos: Position; patterns: [Pattern]; right_paren_pos: Position; comma_pos_vec: [Position]; } table BinaryExpr { base: NodeBase; left_expr: Expr; right_expr: Expr; operator_kind: uint16; operator_pos: Position; } table IsExpr { base: NodeBase; expr: Expr; is_type: Type; is_pos: Position; } table AsExpr { base: NodeBase; expr: Expr; as_type: Type; as_pos: Position; } table UnaryExpr { base: NodeBase; expr: Expr; operator_kind: uint16; operator_pos: Position; } table WildcardExpr { base: NodeBase; } table ArrayExpr { base: NodeBase; type: Type; leftParenPos: Position; args : [FuncArg]; rightParenPos: Position; isValueArray: bool; } table ParenExpr { base: NodeBase; left_paren_pos: Position; expr: Expr; right_paren_pos: Position; } table LitConstExpr { base: NodeBase; literal: string; literal_const_kind: uint16; delimiter_num: uint16; string_kind: uint16; is_single_quote: bool; } table FuncArg { // not in FuncParams base: NodeBase; name: string; name_pos: Position; colon_pos: Position; expr: Expr; comma_pos: Position; withInout: bool; } table CallExpr { base: NodeBase; base_func: Expr; // FIXME: RefExpr or MemberAccess. left_paren_pos: Position; args: [FuncArg]; right_paren_pos: Position; } table RefExpr { base: NodeBase; ref: Reference; left_angle_pos: Position; type_arguments: [Type]; right_angle_pos: Position; is_this: bool; is_super: bool; is_quote_dollar: bool; } table ReturnExpr { base: NodeBase; return_pos: Position; expr: Expr; } table AssignExpr { base: NodeBase; left_value: Expr; assign_op: uint16; assign_pos: Position; right_expr: Expr; } table MemberAccess { base: NodeBase; base_expr: Expr; dot_pos: Position; field: string; field_pos: Position; left_angle_pos: Position; type_arguments: [Type]; right_angle_pos: Position; } table IfExpr { base: NodeBase; if_pos: Position; cond_expr: Expr; body: Block; has_else: bool; else_pos: Position; else_body: Expr; isElseIf: bool; left_paren_pos: Position; right_paren_pos: Position; } table LetPatternDestructor { base: NodeBase; patterns: [Pattern]; bit_or_pos_vec: [Position]; backarrow_pos: Position; initializer: Expr; } table LambdaExpr { base: NodeBase; body: FuncBody; mockSupported: bool; } table ArrayLit { // {1,2,3,4,5} base: NodeBase; left_curl_pos: Position; children: [Expr]; comma_pos_vec: [Position]; right_curl_pos: Position; } table TupleLit { // (1,2,3,4,5) base: NodeBase; left_paren_pos: Position; children: [Expr]; comma_pos_vec: [Position]; right_paren_pos: Position; } table SubscriptExpr { // array[3][4] base: NodeBase; base_expr: Expr; left_square_pos: Position; index_exprs: [Expr]; right_square_pos: Position; is_tuple_access: bool; } table MatchExpr { base: NodeBase; match_mode: bool; left_paren_pos: Position; selector: Expr; right_paren_pos: Position; left_curl_pos: Position; match_cases: [MatchCase]; match_case_others: [MatchCaseOther]; right_curl_pos: Position; } table RangeExpr { // let range1 = 0..10:2 base: NodeBase; start_expr: Expr; range_pos: Position; stop_expr: Expr; colon_pos: Position; step_expr: Expr; is_closed: bool; } table ForInExpr { base: NodeBase; left_paren_pos: Position; pattern: Pattern; in_pos: Position; in_expr: Expr; right_paren_pos: Position; if_pos: Position; pattern_guard: Expr; body: Block; } table WhileExpr { base: NodeBase; while_pos: Position; left_paren_pos: Position; cond_expr: Expr; right_paren_pos: Position; body: Block; } table SpawnExpr { base: NodeBase; spawn_pos: Position; task_expr: Expr; has_arg: bool; spawn_arg_expr: Expr; left_paren_pos: Position; right_paren_pos: Position; } table SynchronizedExpr { base: NodeBase; sync_pos: Position; left_paren_pos: Position; mutex_expr: Expr; right_paren_pos: Position; body: Block; } table TrailingClosureExpr { base: NodeBase; left_lambda: Position; expr: Expr; lambda: LambdaExpr; right_lambda: Position; } table TypeConvExpr { base: NodeBase; type: Type; left_paren_pos: Position; expr: Expr; right_paren_pos: Position; } table ThrowExpr { base: NodeBase; expr: Expr; } table PerformExpr { base: NodeBase; expr: Expr; } table ResumeExpr { base: NodeBase; with_pos: Position; with_expr: Expr; throwing_pos: Position; throwing_expr: Expr; } table Handler { handler_pos: Position; command_pattern: Pattern; handle_block: Block; } table TryExpr { base: NodeBase; resource_spec: [VarDecl]; is_resource_spec: bool; try_block: Block; catch_blocks: [Block]; catch_patterns: [Pattern]; // Pattern finally_pos: Position; finally_block: Block; resource_spec_lparen_pos: Position; resource_spec_rparen_pos: Position; resource_spec_comma_pos_vec: [Position]; catch_pos_vec: [Position]; catch_left_paren_pos_vec: [Position]; catch_right_paren_pos_vec: [Position]; handlers: [Handler]; } table DoWhileExpr { base: NodeBase; do_pos: Position; body: Block; while_pos: Position; left_paren_pos: Position; cond_expr: Expr; right_paren_pos: Position; } table IncOrDecExpr { base: NodeBase; operator_kind: uint16; operator_pos: Position; expr: Expr; } table OptionalExpr { base: NodeBase; baseExpr: Expr; quest_pos: Position; } table OptionalChainExpr { base: NodeBase; expr: Expr; } table Token { kind: uint16; value: string; pos: Position; } table TokenPart { tokens: [Token]; } table QuoteExpr { base: NodeBase; left_paren_pos: Position; exprs: [Expr]; right_paren_pos: Position; } table Reference { // This is not a node. identifier: string; identifier_pos: Position; } table FuncParamList { base: NodeBase; left_paren_pos: Position; params: [FuncParam]; right_paren_pos: Position; } union MacroParam { MACRO_EXPAND_PARAM: MacroExpandParam, } table FuncParam { // (a: Int32, b! : Int32) nodeBase: NodeBase; base: VarDecl; colon_pos: Position; assignment: Expr; comma_pos: Position; is_named_param: bool; is_member_param: bool; not_mark_pos: Position; has_let_or_var: bool; macroParam: MacroParam; } table FuncBody { base: NodeBase; // paramList not support Curry Func param_list: FuncParamList; // FIXME arrow_pos: Position; // '=>' colon_pos: Position; // ':' ret_type: Type; has_body: bool; body: Block; // maybe a better name generic: Generic; } table JumpExpr { base: NodeBase; is_break: bool; } table Expr { base: NodeBase; expr: AnyExpr; } table PrimitiveTypeExpr { base: NodeBase; kind: uint16; } union AnyType { REF_TYPE: RefType, PRIMITIVE_TYPE: PrimitiveType, FUNC_TYPE: FuncType, THIS_TYPE: ThisType, PAREN_TYPE: ParenType, QUALIFIED_TYPE: QualifiedType, OPTION_TYPE: OptionType, TUPLE_TYPE: TupleType, VARRAY_TYPE: VArrayType, CONSTANT_TYPE: ConstantType } table TypeBase { base: NodeBase; comma_pos: Position; type_parameter_name: string; colon_pos: Position; type_pos: Position; bit_and_pos: Position; } table RefType { // T or T base: TypeBase; ref: Reference; left_angle_pos: Position; type_arguments: [Type]; right_angle_pos: Position; } table PrimitiveType { base: TypeBase; type_str: string; kind: uint16; } table FuncType { base: TypeBase; left_paren_pos: Position; param_types: [Type]; right_paren_pos: Position; arrow_pos: Position; ret_type: Type; isC: bool; } table ThisType { base: TypeBase; } table ParenType { base: TypeBase; left_paren_pos: Position; type: Type; right_paren_pos: Position; } table QualifiedType { base: TypeBase; base_type: Type; dot_pos: Position; field: string; field_pos: Position; left_angle_pos: Position; type_arguments: [Type]; right_angle_pos: Position; } table OptionType { base: TypeBase; component_type: Type; quest_num: int; quest_vector: [Position]; } table TupleType { base: TypeBase; field_types: [Type]; left_paren_pos: Position; right_paren_pos: Position; comma_pos_vector: [Position]; } table VArrayType { base: TypeBase; varrayPos: Position; leftAnglePos: Position; typeArgument: Type; constantType: Type; rightAnglePos: Position; } table ConstantType { base: TypeBase; constantExpr: Expr; dollarPos: Position; } table Type { base: TypeBase; type: AnyType; } table VarDecl { base: DeclBase; type: Type; colon_pos: Position; initializer: Expr; // 2 + 3 assign_pos: Position; // Pos of "=" is_var: bool; // var or let isEnumConstruct: bool; emptyKeyword: bool; } table VarWithPatternDecl { base: DeclBase; type: Type; colon_pos: Position; initializer: Expr; pattern: Pattern; assign_pos: Position; is_var: bool; } table FuncDecl { base: DeclBase; left_paren_pos: Position; right_paren_pos: Position; func_body: FuncBody; is_setter: bool; is_getter: bool; op_kind: int32; // operator kind for operator function isEnumConstruct: bool; } table MainDecl { base: DeclBase; func_body: FuncBody; } table MacroDecl { base: DeclBase; leftParenPos: Position; rightParenPos: Position; func_body: FuncBody; } table StructBody { base: NodeBase; left_curl_pos: Position; decls: [Decl]; right_curl_pos: Position; } table StructDecl { base: DeclBase; body: StructBody; upper_bound_pos: Position; // pos of <: super_types: [Type]; generic: Generic; } table ClassBody { base: NodeBase; left_curl_pos: Position; decls: [Decl]; right_curl_pos: Position; } table ClassDecl { base: DeclBase; upper_bound_pos: Position; // position of <: super_types: [Type]; // [B,C] in A <: B,C body: ClassBody; sub_decls: [Decl]; // represent set } table InterfaceBody { base: NodeBase; left_curl_pos: Position; decls: [Decl]; right_curl_pos: Position; } table InterfaceDecl { base: DeclBase; upper_bound_pos: Position; // position of <: super_types: [Type]; body: InterfaceBody; sub_decls: [Decl]; // represent set } table EnumDecl { base: DeclBase; has_arguments: bool; left_curl_pos: Position; constructors: [Decl]; bit_or_pos_vec: [Position]; members: [Decl]; right_curl_pos: Position; upper_bound_pos: Position; super_interface_types: [Type]; hasEllipsis: bool; ellipsis_pos: Position; } table GenericParamDecl { base: DeclBase; comma_pos: Position; } table PrimaryCtorDecl { base: DeclBase; func_body: FuncBody; } table PropDecl { base: VarDecl; colon_pos: Position; left_curl_pos: Position; getters: [FuncDecl]; setters: [FuncDecl]; right_curl_pos: Position; } table GenericConstraint { base: NodeBase; where_pos: Position; type: RefType; operator_pos: Position; upper_bound: [Type]; bit_and_pos_vec: [Position]; comma_pos: Position; } table Generic { left_angle_pos: Position; type_parameters: [GenericParamDecl]; right_angle_pos: Position; generic_constraints: [GenericConstraint]; content: string; } table Annotation { base: NodeBase; kind: uint16; identPos: Position; identifier: string; args: [FuncArg]; // For special annos overflow_strategy: string; attrs: [string]; condExpr: Expr; left_square_pos: Position; right_square_pos: Position; is_compile_time_visible: bool; } table Modifier { base: NodeBase; kind: uint16; is_explicit: bool; } table DeclBase { base: NodeBase; identifier: string; identifier_pos: Position; keyword_pos: Position; annotations: [Annotation]; modifiers: [Modifier]; // Set of Modifier generic: Generic; isConst: bool; } table TypeAliasDecl { base: DeclBase; assign_pos: Position; type: Type; generic: Generic; } table ExtendDecl { base: DeclBase; extended_type: Type; upper_bound_pos: Position; interfaces: [Type]; where_pos: Position; left_curl_pos: Position; members: [Decl]; right_curl_pos: Position; } union AnyDecl { FUNC_DECL: FuncDecl, MAIN_DECL: MainDecl, VAR_DECL: VarDecl, VAR_WITH_PATTERN_DECL: VarWithPatternDecl, STRUCT_DECL: StructDecl, CLASS_DECL: ClassDecl, INTERFACE_DECL: InterfaceDecl, GENERIC_PARAM_DECL: GenericParamDecl, PRIMARY_CTOR_DECL: PrimaryCtorDecl, PROP_DECL: PropDecl, ENUM_DECL: EnumDecl, TYPE_ALIAS_DECL: TypeAliasDecl, EXTEND_DECL: ExtendDecl, MACRO_DECL: MacroDecl, MACRO_EXPAND_DECL: MacroExpandDecl, FUNC_PARAM: FuncParam, MACRO_EXPAND_PARAM: MacroExpandParam } table Decl { base: DeclBase; decl: AnyDecl; } union AnyNode { EXPR: Expr, DECL: Decl, TYPE: Type, PATTERN: Pattern, FILE: File, PACKAGE_SPEC: PackageSpec, IMPORT_SPEC: ImportSpec, ANNOTATION: Annotation, FEATURES_DIRECTIVE: FeaturesDirective } table Block { base: NodeBase; left_curl_pos: Position; // '{' body: [Node]; // write unions into vector right_curl_pos: Position; // '}' is_unsafe: bool; unsafe_pos: Position; // 'unsafe' } table MacroInvocation { fullName: string; identifier: string; identifierPos: Position; leftSquarePos: Position; rightSquarePos: Position; leftParenPos: Position; rightParenPos: Position; atPos: Position; attrs: [Token]; args: [Token]; decl: Decl; hasParenthesis: bool; is_compile_time_visible: bool; } table MacroExpandExpr { base: NodeBase; invocation: MacroInvocation; identifier: string; identifierPos: Position; annotations: [Annotation]; modifiers: [Modifier]; // Set of Modifier } table MacroExpandDecl { base: DeclBase; invocation: MacroInvocation; } table MacroExpandParam { base: FuncParam; invocation: MacroInvocation; } table File { base: NodeBase; fileName: string; filePath: string; package: PackageSpec; imports: [ImportSpec]; decls: [Decl]; feature: FeaturesDirective; } table FeatureId { base: NodeBase; identifiers: [string]; identPoses: [Position]; dotPoses: [Position]; } table FeaturesDirective { base: NodeBase; content: [FeatureId]; commaPoses: [Position]; } enum AccessibleKind:uint8 { ACCESSIBLE_PUBLIC, ACCESSIBLE_PROTECTED, ACCESSIBLE_INTERNAL, } table PackageSpec { base: NodeBase; macroPos:Position; packagePos:Position; prefixPaths:[string]; prefixPoses:[Position]; prefixDotPoses:[Position]; packageName:string; packageNamePos:Position; accessible:AccessibleKind=ACCESSIBLE_PUBLIC; hasMacro:bool; } enum ImportKind:int8 { IMPORT_SINGLE, IMPORT_ALIAS, IMPORT_ALL, IMPORT_MULTI, } table ImportContent { base:NodeBase; kind:ImportKind; prefixPaths:[string]; prefixPoses:[Position]; prefixDotPoses:[Position]; identifier:string; identifierPos:Position; asPos:Position; asIdentifier:string; asIdentifierPos:Position; leftCurlPos:Position; items:[ImportContent]; commaPoses:[Position]; rightCurlPos:Position; } enum ReExportKind:int8{ REEXPORT_PRIVATE, REEXPORT_INTERNAL, REEXPORT_PROTECTED, REEXPORT_PUBLIC, } table ImportSpec { base: NodeBase; reExport:ReExportKind=REEXPORT_PRIVATE; importPos:Position; content:ImportContent; } table NodeBase { begin: Position; end: Position; ast_kind: string; } table Node { base: NodeBase; root: AnyNode; } root_type Node; cangjie_compiler-1.0.7/schema/PackageFormat.fbs000066400000000000000000000763311510705540100214610ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. namespace PackageFormat; file_identifier "CHIR"; // Using 'uint32' as type of 'Type/Value/Expression/CustomTypeDef' 's table index. // 0 is invalid index or means void, valid index start from 1, which is the offset of table plus 1. // Using 'uint64' as a bitset for attributes: // 0 STATIC, Mark whether a member is a static one. // 1 PUBLIC, Mark whether a member is a public one. // 2 PRIVATE, Mark whether a member is a private one. // 3 PROTECTED, Mark whether a member is a protected one. // 4 ABSTRACT, Mark whether a function is an abstract one. // 5 VIRTUAL, Mark whether a declaration is in fact open (even if the user does not use `open` keyword). // 6 OVERRIDE, Mark whether a declaration in fact overrides the inherited one (even if the user does not use `override` keyword). // 7 REDEF, Mark whether a declaration in fact overrides the inherited one (even if the user does not use `redef` keyword). // 8 SEALED, Mark whether a declaration is a sealed one. // 9 FOREIGN, Mark whether a declaration is a foreign one. // 10 MUT, Mark whether a declaration is a mutable one. // 11 FINAL, Mark a Func override a parent class's func, and this func self does not have VIRTUAL Attribute. // 12 OPERATOR, Mark whether a declaration is a operator one. // 13 JAVA, Mark whether a node is foreign call. // 14 READONLY, 'let x = xxx', 'x' enable READONLY attribute // 15 CONST, correspond `const` keyword in Cangjie source code. // 16 IMPORTED, Mark whether variable、func、enum、struct、class is imported from other package. // 17 GENERIC_INSTANTIATED, Mark whether a `GlobalVar/Func/Type` is instantiated. // 18 NO_DEBUG_INFO, Mark a `Value` doesn't contain debug info, like line/column number. // 19 GENERIC, Mark a declaration is generic // 20 INTERNAL, GlobalVar/Func/Enum/Class/Struct/Interface is visible in current and sub package. // 21 COMPILER_ADD, Mark a `Value` is added by compiler, like "copied default func from interface". // compiler attribute // 22 NO_REFLECT_INFO, Mark a `Value` is't used by `reflect` feature. // 23 NO_INLINE, Mark a Func can't be inlined. // 24 NON_RECOMPILE, only used in `ImportedValue` in incremental compilation, indicate this ImportedValue is converted from a decl in current package that is not recompiled. // 25 UNREACHABLE, Mark a Block is unreachable. // 26 NO_SIDE_EFFECT, Mark a Func does't have side effect. enum CHIRTypeKind: uint8 { INVALID, // integer INT8, INT16, INT32, INT64, INT_NATIVE, // unsigned integer UINT8, UINT16, UINT32, UINT64, UINT_NATIVE, // float FLOAT16, FLOAT32, FLOAT64, // other primitive type RUNE, BOOLEAN, UNIT, NOTHING, // Void type VOID, // composite type TUPLE, STRUCT, ENUM, FUNC, CLASS, // Built-in array related type RAWARRAY, VARRAY, // Built-in CFFI related type C_POINTER, C_STRING, // Generic type GENERIC, // Referece to an value with abritray type REFTYPE, // Built-in box type BOXTYPE, THIS, } enum SourceExpr: uint8 { IF_EXPR, WHILE_EXPR, DO_WHILE_EXPR, MATCH_EXPR, IF_LET_OR_WHILE_LET, QUEST, BINARY, FOR_IN_EXPR, OTHER, } table Type { kind: CHIRTypeKind; typeID: uint32; // current id in types argTys: [uint32]; // use types id refDims: uint8 = 0; } table RuneType { base: Type; extends: [uint32]; // use defs id } table BooleanType { base: Type; extends: [uint32]; // use defs id } table UnitType { base: Type; extends: [uint32]; // use defs id } table NothingType { base: Type; extends: [uint32]; // use defs id } table NumericType { base: Type; extends: [uint32]; // use defs id } table IntType { base: NumericType; } table FloatType { base: NumericType; } table TupleType { base: Type; } table RawArrayType { base: Type; dims: uint32; } table VArrayType { base: Type; size: int64; } table FuncType { base: Type; isCFuncType: bool = false; hasVarArg: bool = false; } table CustomType { base: Type; implementedInterfaceTys: [uint32]; // use types id instantiatedMemberTys: [uint32]; // use types id customTypeDef: uint32; // use defs id hasSetSuperInterface: bool = false; hasSetInstMemberTy: bool = false; } table EnumType { base: CustomType; isBoxed: bool = false; } table StructType { base: CustomType; } table ClassType { base: CustomType; superClassTy: uint32; // use type id hasSetSuperClass: bool = false; } table CStringType { base: Type; extends: [uint32]; // use defs id } table CPointerType { base: Type; extends: [uint32]; // use defs id } table GenericType { base: Type; orphanFlag: bool = false; skipCheck: bool = false; identifier: string(shared); srcCodeIdentifier: string(shared); upperBounds: [uint32]; // use types id } table RefType { base: Type; } table BoxType { base: Type; } table VoidType { base: Type; } table ThisType { base: Type; } enum Linkage: int16 { WEAK_ODR, EXTERNAL, INTERNAL, LINKONCE_ODR, EXTERNAL_WEAK, } table Pos { line: uint32; column: uint32; } table DebugLocation { filePath: string(shared); fileId: uint32; beginPos: Pos; endPos: Pos; scope: [int32]; } table LinkTypeInfo { linkage: Linkage; } enum SkipKind : uint8 { NO_SKIP, SKIP_DCE_WARNING, SKIP_FORIN_EXIT, SKIP_VIC, } table SkipCheck { skipKind: SkipKind; } table NeedCheckArrayBound { need: bool = true; } table NeedCheckCast { need: bool = true; } enum OverflowStrategy: uint8 { NA, CHECKED, WRAPPING, THROWING, SATURATING, } table NeverOverflowInfo { neverOverflow: bool = false; } table GeneratedFromForIn { value: bool = false; } table IsAutoEnvClass { value: bool = false; } table IsCapturedClassInCC { value: bool = false; } table EnumCaseIndex { index: int64 = -1; } table VirMethodOffset { offset: int64 = -1; } table WrappedRawMethod { rawMethod: uint32; // use values id } table VariantTypeCastWrapper { lambdaCache: [uint32]; // use exprs id } union Annotation { needCheckArrayBound: NeedCheckArrayBound, needCheckCast: NeedCheckCast, debugLocationInfo: DebugLocation, debugLocationInfoForWarning: DebugLocation, generatedFromForIn: GeneratedFromForIn, isAutoEnvClass: IsAutoEnvClass, isCapturedClassInCC: IsCapturedClassInCC, linkTypeInfo: LinkTypeInfo, skipCheck: SkipCheck, neverOverflowInfo: NeverOverflowInfo, enumCaseIndex: EnumCaseIndex, virMethodOffset: VirMethodOffset, wrappedRawMethod: WrappedRawMethod, variantTypeCastWrapper : VariantTypeCastWrapper } table Base { annos: [Annotation]; } enum ValueKind: uint8 { LITERAL, GLOBALVAR, PARAMETER, IMPORTED_FUNC, IMPORTED_VAR, LOCALVAR, FUNC, BLOCK, BLOCK_GROUP, } table Value { base: Base; type: uint32; // use types id identifier: string(shared); kind: ValueKind; valueID: uint32; // current id in values attributes: uint64; annoInfo: AnnoInfo; } enum ConstantValueKind: uint8 { BOOL, RUNE, INT, FLOAT, STRING, UNIT, NULL, FUNC, } table LiteralValue { base: Value; literalKind: ConstantValueKind; } table BoolLiteral { base: LiteralValue; val: bool; } table StringLiteral { base: LiteralValue; val: string(shared); } table RuneLiteral { base: LiteralValue; val: uint32; // coding of `rune` } table IntLiteral { base: LiteralValue; val: uint64; } table FloatLiteral { base: LiteralValue; val: double = 1.0; // use a non-0.0 as the default value to avoid sign problem } table UnitLiteral { base: LiteralValue; } table NullLiteral { base: LiteralValue; } table Parameter { base: Value; ownedFunc: uint32; // use values id ownedLambda: uint32; // use exprs id } table LocalVar { base: Value; associatedExpr: uint32; // use exprs id isRetVal: bool = false; } table GlobalVar { base: Value; rawMangledName: string(shared); srcCodeIdentifier: string(shared); packageName: string(shared); defaultInitVal: uint32; // use values id associatedInitFunc: uint32; // use values id declaredParent: uint32; // use defs id } enum FuncKind: uint8 { DEFAULT, GETTER, SETTER, LAMBDA, CLASS_CONSTRUCTOR, PRIMAL_CLASS_CONSTRUCTOR, STRUCT_CONSTRUCTOR, PRIMAL_STRUCT_CONSTRUCTOR, GLOBALVAR_INIT, FINALIZER, MAIN_ENTRY, ANNOFACTORY_FUNC, MACRO_FUNC, DEFAULT_PARAMETER_FUNC, INSTANCEVAR_INIT } table ImportedValue { base: Value; } table ImportedFunc { base: ImportedValue; // func base srcCodeIdentifier: string(shared); rawMangledName: string(shared); packageName: string(shared); declaredParent: uint32; // use defs id genericDecl: uint32; // use values id funcKind: FuncKind; isFastNative: bool; isCFFIWrapper: bool; // originalLambdaInfo : FuncSigInfo // 1. when lambda is lifted to global func, its generic type params and func type may be changed, // but cjdb need original type to show // e.g. func foo() { // func goo() {} // this lambda will be lifted to global func // } // after being lifted, global func `goo` is as follow: // func goo(env: class&) {} originalLambdaFuncType: uint32; // use types id. declared type, including `this` type and return type, there may be generic type in it. originalLambdaGenericTypeParams: [uint32]; // use types id genericTypeParams: [uint32]; // use types id paramDftValHostFunc: uint32; // use values id paramInfo: [AbstractMethodParam]; } table ImportedVar { base: ImportedValue; packageName: string(shared); srcCodeIdentifier: string(shared); rawMangledName: string(shared); } table Block { base: Value; parentGroup: uint32; // use values id exprs: [uint32]; // use exprs predecessors: [uint32]; // use values id isLandingPadBlock: bool; exceptionCatchList: [uint32]; // use types id } table BlockGroup { base: Value; entryBlock: uint32; // use values id blocks: [uint32]; // use values id ownedFunc: uint32; // use values id ownedExpression: uint32; // use exprs id } table Func { base: Value; // func base srcCodeIdentifier: string(shared); rawMangledName: string(shared); packageName: string(shared); declaredParent: uint32; // use defs id genericDecl : uint32; // use values id funcKind: FuncKind; isFastNative: bool; isCFFIWrapper: bool; // originalLambdaInfo : FuncSigInfo // 1. when lambda is lifted to global func, its generic type params and func type may be changed, // but cjdb need original type to show // e.g. func foo() { // func goo() {} // this lambda will be lifted to global func // } // after being lifted, global func `goo` is as follow: // func goo(env: class&) {} originalLambdaFuncType: uint32; // use types id. declared type, including `this` type and return type, there may be generic type in it. originalLambdaGenericTypeParams: [uint32]; // use types id genericTypeParams: [uint32]; // use types id paramDftValHostFunc: uint32; // use values id // FuncBody body: uint32; // use values id params: [uint32]; // use values id retVal: uint32; // use values id parentName: string(shared); propLoc: DebugLocation; localId: uint64; blockId: uint64; blockGroupId: uint64; } enum CustomDefKind: uint8 { STRUCT, ENUM, CLASS, EXTEND, } table AnnoInfo { mangledName: string(shared); } table MemberVarInfo { name: string(shared); rawMangledName: string(shared); type: uint32; // use types id attributes: uint64; loc: DebugLocation; annoInfo : AnnoInfo; initializerFunc: uint32; outerDef: uint32; // use defs id } table VirtualFuncTypeInfo { sigType : uint32; // use types id originalType : uint32; // use types id parentType : uint32; // use types id returnType : uint32; // use types id methodGenericTypeParams : [uint32]; // use types id } table VirtualFuncInfo { srcCodeIdentifier : string(shared); instance : uint32; // used value Id attributes: uint64; typeInfo : VirtualFuncTypeInfo; } table VTableElement { ty : uint32; // use types id info : [VirtualFuncInfo]; } table CustomTypeDef { base: Base; kind: CustomDefKind; customTypeDefID: uint32; // current id in defs srcCodeIdentifier: string(shared); identifier: string(shared); packageName: string(shared); type: uint32; // use types id genericDecl: uint32; // use defs id methods: [uint32]; // use values id implementedInterfaces: [uint32]; // use types id instanceMemberVars: [MemberVarInfo]; staticMemberVars: [uint32]; // use values id attributes: uint64; annoInfo: AnnoInfo; vtable: [VTableElement]; extends: [uint32]; // use defs id varInitializationFunc: uint32; } table EnumCtorInfo { identifier: string(shared); mangledName: string(shared); funcType: uint32; // use types id } table EnumDef { base: CustomTypeDef; ctors: [EnumCtorInfo]; nonExhaustive: bool; } table StructDef { base: CustomTypeDef; isCStruct: bool = false; } table ExtendDef { base: CustomTypeDef; extendedType: uint32; // use types id genericParams: [uint32]; // use types id } enum ClassDefKind: int16 { CLASS, INTERFACE } table AbstractMethodParam { paramName: string(shared); paramType: uint32; // use types id annoInfo : AnnoInfo; } table AbstractMethodInfo { methodName: string(shared); mangledName: string(shared); methodType: uint32; // use types id paramsInfo: [AbstractMethodParam]; attributes: uint64; annoInfo : AnnoInfo; methodGenericTypeParams : [uint32]; // use types id hasBody : bool; parent: uint32; // class def, use def id } table ClassDef { base: CustomTypeDef; kind: ClassDefKind; isAnnotation: bool = false; superClass: uint32; // use types id abstractMethods: [AbstractMethodInfo]; } enum CHIRExprKind: uint8 { INVALID, // Terminator GOTO, BRANCH, MULTIBRANCH, EXIT, APPLY_WITH_EXCEPTION, INVOKE_WITH_EXCEPTION, INVOKESTATIC_WITH_EXCEPTION, RAISE_EXCEPTION, INT_OP_WITH_EXCEPTION, SPAWN_WITH_EXCEPTION, TYPECAST_WITH_EXCEPTION, INTRINSIC_WITH_EXCEPTION, ALLOCATE_WITH_EXCEPTION, RAW_ARRAY_ALLOCATE_WITH_EXCEPTION, // Unary NEG, NOT, BITNOT, // Binary ADD, SUB, MUL, DIV, MOD, EXP, LSHIFT, RSHIFT, BITAND, BITOR, BITXOR, LT, GT, LE, GE, EQUAL, NOTEQUAL, AND, OR, // Memory ALLOCATE, LOAD, STORE, GET_ELEMENT_REF, GET_ELEMENT_BY_NAME, STORE_ELEMENT_REF, STORE_ELEMENT_BY_NAME, // Complext IF, LOOP, FORIN_RANGE, FORIN_ITER, FORIN_CLOSED_RANGE, LAMBDA, // Others CONSTANT, DEBUGEXPR, TUPLE, FIELD, FIELD_BY_NAME, APPLY, INVOKE, INVOKE_STATIC, INSTANCEOF, TYPECAST, GET_EXCEPTION, RAW_ARRAY_ALLOCATE, RAW_ARRAY_LITERAL_INIT, RAW_ARRAY_INIT_BY_VALUE, VARRAY, VARRAY_BUILDER, INTRINSIC, SPAWN, GET_INSTANTIATE_VALUE, BOX, UNBOX, TRANSFORM_TO_GENERIC, TRANSFORM_TO_CONCRETE, UNBOX_TO_REF, GET_RTTI, GET_RTTI_STATIC, } enum IntrinsicKind: uint16 { NOT_INTRINSIC, NOT_IMPLEMENTED, // For hoisting, but we should later split arraybuilder // into allocation and initialisation ARRAY_INIT, // CORE SIZE_OF, ALIGN_OF, ARRAY_ACQUIRE_RAW_DATA, ARRAY_RELEASE_RAW_DATA, ARRAY_BUILT_IN_COPY_TO, ARRAY_GET, ARRAY_SET, ARRAY_GET_UNCHECKED, ARRAY_GET_REF_UNCHECKED, ARRAY_SET_UNCHECKED, ARRAY_SIZE, ARRAY_CLONE, ARRAY_SLICE_INIT, ARRAY_SLICE, ARRAY_SLICE_RAWARRAY, ARRAY_SLICE_START, ARRAY_SLICE_SIZE, ARRAY_SLICE_GET_ELEMENT, ARRAY_SLICE_GET_ELEMENT_UNCHECKED, ARRAY_SLICE_SET_ELEMENT, ARRAY_SLICE_SET_ELEMENT_UNCHECKED, FILL_IN_STACK_TRACE, DECODE_STACK_TRACE, CHR, ORD, CPOINTER_GET_POINTER_ADDRESS, CPOINTER_INIT0, // CPointer constructor with no arguments CPOINTER_INIT1, // CPointer constructor with one argument CPOINTER_READ, CPOINTER_WRITE, CPOINTER_ADD, CSTRING_INIT, CSTRING_CONVERT_CSTR_TO_PTR, INOUT_PARAM, REGISTER_WATCHED_OBJECT, OBJECT_REFEQ, RAW_ARRAY_REFEQ, // cjnative only OBJECT_ZERO_VALUE, INVOKE_GC, SET_GC_THRESHOLD, DUMP_CJ_HEAP_DATA, GET_GC_COUNT, GET_GC_TIME_US, GET_GC_FREED_SIZE, START_CJ_CPU_PROFILING, STOP_CJ_CPU_PROFILING, BLACK_BOX, GET_MAX_HEAP_SIZE, GET_ALLOCATE_HEAP_SIZE, GET_REAL_HEAP_SIZE, GET_THREAD_NUMBER, GET_BLOCKING_THREAD_NUMBER, GET_NATIVE_THREAD_NUMBER, VARRAY_SET, VARRAY_GET, // About Future FUTURE_INIT, FUTURE_IS_COMPLETE, // cjnative only FUTURE_WAIT, // cjnative only FUTURE_NOTIFYALL, // cjnative only IS_THREAD_OBJECT_INITED, GET_THREAD_OBJECT, SET_THREAD_OBJECT, OVERFLOW_CHECKED_ADD, OVERFLOW_CHECKED_SUB, OVERFLOW_CHECKED_MUL, OVERFLOW_CHECKED_DIV, OVERFLOW_CHECKED_MOD, OVERFLOW_CHECKED_POW, OVERFLOW_CHECKED_INC, OVERFLOW_CHECKED_DEC, OVERFLOW_CHECKED_NEG, OVERFLOW_THROWING_ADD, OVERFLOW_THROWING_SUB, OVERFLOW_THROWING_MUL, OVERFLOW_THROWING_DIV, OVERFLOW_THROWING_MOD, OVERFLOW_THROWING_POW, OVERFLOW_THROWING_INC, OVERFLOW_THROWING_DEC, OVERFLOW_THROWING_NEG, OVERFLOW_SATURATING_ADD, OVERFLOW_SATURATING_SUB, OVERFLOW_SATURATING_MUL, OVERFLOW_SATURATING_DIV, OVERFLOW_SATURATING_MOD, OVERFLOW_SATURATING_POW, OVERFLOW_SATURATING_INC, OVERFLOW_SATURATING_DEC, OVERFLOW_SATURATING_NEG, OVERFLOW_WRAPPING_ADD, OVERFLOW_WRAPPING_SUB, OVERFLOW_WRAPPING_MUL, OVERFLOW_WRAPPING_DIV, OVERFLOW_WRAPPING_MOD, OVERFLOW_WRAPPING_POW, OVERFLOW_WRAPPING_INC, OVERFLOW_WRAPPING_DEC, OVERFLOW_WRAPPING_NEG, // llvm vector instructions for string optimization VECTOR_COMPARE_32, // cjnative only VECTOR_INDEX_BYTE_32, // cjnative only // Foreign functions translated to intrinsic functions in the interpreter, // These functions are not not marked as intrinsic in Cangjie but they are translated to intrinsics in BCHIR CJ_CORE_CAN_USE_SIMD, // cjnative only CJ_TLS_DYN_SET_SESSION_CALLBACK, CJ_TLS_DYN_SSL_INIT, // ============================ cjnative only start ================= REFLECTION_INTRINSIC_START_FLAG, IS_INTERFACE, IS_CLASS, IS_PRIMITIVE, IS_STRUCT, IS_GENERIC, GET_OR_CREATE_TYPEINFO_FOR_REFLECT, GET_TYPETEMPLATE, CHECK_METHOD_ACTUAL_ARGS, METHOD_ENTRYPOINT_IS_NULL, IS_RELECT_UNSUPPORTED_TYPE, GET_TYPE_FOR_ANY, GET_TYPE_BY_MANGLED_NAME, GET_TYPE_NAME, GET_TYPE_BY_QUALIFIED_NAME, GET_TYPE_QUALIFIED_NAME_LENGTH, GET_TYPE_QUALIFIED_NAME, GET_NUM_OF_INTERFACE, GET_INTERFACE, IS_SUBTYPE, GET_TYPE_INFO_MODIFIER, GET_TYPE_INFO_ANNOTATIONS, GET_OBJ_CLASS, GET_SUPER_TYPE_INFO, GET_NUM_OF_INSTANCE_METHOD_INFOS, GET_INSTANCE_METHOD_INFO, GET_NUM_OF_STATIC_METHOD_INFOS, GET_STATIC_METHOD_INFO, GET_METHOD_NAME, GET_METHOD_RETURN_TYPE, GET_METHOD_MODIFIER, GET_METHOD_ANNOTATIONS, APPLY_CJ_METHOD, APPLY_CJ_STATIC_METHOD, APPLY_CJ_GENERIC_METHOD, APPLY_CJ_GENERIC_STATIC_METHOD, GET_NUM_OF_ACTUAL_PARAMETERS, GET_NUM_OF_GENERIC_PARAMETERS, GET_ACTUAL_PARAMETER_INFO, GET_GENERIC_PARAMETER_INFO, GET_NUM_OF_INSTANCE_FIELD_INFOS, GET_INSTANCE_FIELD_INFO, GET_NUM_OF_STATIC_FIELD_INFOS, GET_STATIC_FIELD_INFO, GET_STATIC_FIELD_NAME, GET_STATIC_FIELD_TYPE, GET_STATIC_FIELD_ANNOTATIONS, GET_FIELD_NAME, GET_FIELD_TYPE, GET_FIELD_ANNOTATIONS, GET_FIELD_MODIFIER, GET_STATIC_FIELD_MODIFIER, GET_FIELD_VALUE, SET_FIELD_VALUE, GET_STATIC_FIELD_VALUE, SET_STATIC_FIELD_VALUE, GET_FIELD_DECLARING_TYPE, GET_PARAMETER_INDEX, GET_PARAMETER_NAME, GET_PARAMETER_TYPE, GET_PARAMETER_ANNOTATIONS, GET_RELATED_PACKAGE_INF, GET_PACKAGE_NAME, GET_PACKAGE_NUM_OF_TYPE_INFOS, GET_PACKAGE_TYPE_INFO, GET_PACKAGE_NUM_OF_GLOBAL_METHODS, GET_PACKAGE_GLOBAL_METHOD_INFO, GET_PACKAGE_NUM_OF_GLOBAL_FIELD_INFOS, GET_PACKAGE_GLOBAL_FIELD_INFO, LOAD_PACKAGE, GET_PACKAGE_BY_QUALIFIEDNAME, GET_PACKAGE_VERSION, GET_SUB_PACKAGES, REFLECTION_INTRINSIC_END_FLAG, // ============================ cjnative only end ================= SLEEP, SOURCE_FILE, SOURCE_LINE, IDENTITY_HASHCODE, IDENTITY_HASHCODE_FOR_ARRAY, // ============================ cjnative only start ================= // SYNC ATOMIC_LOAD, ATOMIC_STORE, ATOMIC_SWAP, ATOMIC_COMPARE_AND_SWAP, ATOMIC_FETCH_ADD, ATOMIC_FETCH_SUB, ATOMIC_FETCH_AND, ATOMIC_FETCH_OR, ATOMIC_FETCH_XOR, MUTEX_INIT, CJ_MUTEX_LOCK, MUTEX_TRY_LOCK, MUTEX_CHECK_STATUS, MUTEX_UNLOCK, WAITQUEUE_INIT, MONITOR_INIT, MOITIOR_WAIT, MOITIOR_NOTIFY, MOITIOR_NOTIFY_ALL, MULTICONDITION_WAIT, MULTICONDITION_NOTIFY, MULTICONDITION_NOTIFY_ALL, CROSS_ACCESS_BARRIER, CREATE_EXPORT_HANDLE, GET_EXPORTED_REF, REMOVE_EXPORTED_REF, // ============================ cjnative only end ================= // Syscall // AST lib FFI FFI_CJ_AST_LEX, FFI_CJ_AST_PARSEEXPR, FFI_CJ_AST_PARSEDECL, FFI_CJ_AST_PARSE_PROPMEMBERDECL, FFI_CJ_AST_PARSE_PRICONSTRUCTOR, FFI_CJ_AST_PARSE_PATTERN, FFI_CJ_AST_PARSE_TYPE, FFI_CJ_AST_PARSETOPLEVEL, FFI_CJ_AST_DIAGREPORT, // Macro With Context FFI FFI_CJ_PARENT_CONTEXT, FFI_CJ_MACRO_ITEM_INFO, FFI_CJ_GET_CHILD_MESSAGES, FFI_CJ_CHECK_ADD_SPACE, // CodeGen CG_UNSAFE_BEGIN, CG_UNSAFE_END, // C FFI funcs STRLEN, MEMCPY_S, MEMSET_S, FREE, MALLOC, STRCMP, MEMCMP, STRNCMP, STRCASECMP, // The interpreter is using these for cjnative backend as well ATOMIC_INT8_LOAD, ATOMIC_INT8_STORE, ATOMIC_INT8_SWAP, ATOMIC_INT8_CAS, ATOMIC_INT8_FETCH_ADD, ATOMIC_INT8_FETCH_SUB, ATOMIC_INT8_FETCH_AND, ATOMIC_INT8_FETCH_OR, ATOMIC_INT8_FETCH_XOR, ATOMIC_INT16_LOAD, ATOMIC_INT16_STORE, ATOMIC_INT16_SWAP, ATOMIC_INT16_CAS, ATOMIC_INT16_FETCH_ADD, ATOMIC_INT16_FETCH_SUB, ATOMIC_INT16_FETCH_AND, ATOMIC_INT16_FETCH_OR, ATOMIC_INT16_FETCH_XOR, ATOMIC_INT32_LOAD, ATOMIC_INT32_STORE, ATOMIC_INT32_SWAP, ATOMIC_INT32_CAS, ATOMIC_INT32_FETCH_ADD, ATOMIC_INT32_FETCH_SUB, ATOMIC_INT32_FETCH_AND, ATOMIC_INT32_FETCH_OR, ATOMIC_INT32_FETCH_XOR, ATOMIC_INT64_LOAD, ATOMIC_INT64_STORE, ATOMIC_INT64_SWAP, ATOMIC_INT64_CAS, ATOMIC_INT64_FETCH_ADD, ATOMIC_INT64_FETCH_SUB, ATOMIC_INT64_FETCH_AND, ATOMIC_INT64_FETCH_OR, ATOMIC_INT64_FETCH_XOR, ATOMIC_UINT8_LOAD, ATOMIC_UINT8_STORE, ATOMIC_UINT8_SWAP, ATOMIC_UINT8_CAS, ATOMIC_UINT8_FETCH_ADD, ATOMIC_UINT8_FETCH_SUB, ATOMIC_UINT8_FETCH_AND, ATOMIC_UINT8_FETCH_OR, ATOMIC_UINT8_FETCH_XOR, ATOMIC_UINT16_LOAD, ATOMIC_UINT16_STORE, ATOMIC_UINT16_SWAP, ATOMIC_UINT16_CAS, ATOMIC_UINT16_FETCH_ADD, ATOMIC_UINT16_FETCH_SUB, ATOMIC_UINT16_FETCH_AND, ATOMIC_UINT16_FETCH_OR, ATOMIC_UINT16_FETCH_XOR, ATOMIC_UINT32_LOAD, ATOMIC_UINT32_STORE, ATOMIC_UINT32_SWAP, ATOMIC_UINT32_CAS, ATOMIC_UINT32_FETCH_ADD, ATOMIC_UINT32_FETCH_SUB, ATOMIC_UINT32_FETCH_AND, ATOMIC_UINT32_FETCH_OR, ATOMIC_UINT32_FETCH_XOR, ATOMIC_UINT64_LOAD, ATOMIC_UINT64_STORE, ATOMIC_UINT64_SWAP, ATOMIC_UINT64_CAS, ATOMIC_UINT64_FETCH_ADD, ATOMIC_UINT64_FETCH_SUB, ATOMIC_UINT64_FETCH_AND, ATOMIC_UINT64_FETCH_OR, ATOMIC_UINT64_FETCH_XOR, ATOMIC_BOOL_LOAD, ATOMIC_BOOL_STORE, ATOMIC_BOOL_SWAP, ATOMIC_BOOL_CAS, ATOMIC_REFERENCEBASE_LOAD, ATOMIC_REFERENCEBASE_STORE, ATOMIC_REFERENCEBASE_SWAP, ATOMIC_REFERENCEBASE_CAS, ATOMIC_OPTIONREFERENCE_LOAD, ATOMIC_OPTIONREFERENCE_STORE, ATOMIC_OPTIONREFERENCE_SWAP, ATOMIC_OPTIONREFERENCE_CAS, // Exception intrinsic BEGIN_CATCH, // Math intrinsic ABS, FABS, FLOOR, CEIL, TRUNC, SIN, COS, EXP, EXP2, LOG, LOG2, LOG10, SQRT, ROUND, POW, POWI, BIT_CAST, // preinitialize intrinsic PREINITIALIZE, // Box cast intrinsic OBJECT_AS, IS_NULL, GET_TYPE_FOR_TYPE_PARAMETER, IS_SUBTYPE_TYPES, } table Expression { base: Base; kind: CHIRExprKind; expressionID: uint32; // current id in exprs operands: [uint32]; // use values id blockGroups: [uint32]; // use values id parentBlock: uint32; // use values id resultLocalVar: uint32; // use values id resultTy: uint32; // use types id } table UnaryExpression { base: Expression; overflowStrategy: OverflowStrategy; } table BinaryExpression { base: Expression; overflowStrategy: OverflowStrategy; } table Constant { base: Expression; } table Allocate { base: Expression; targetType: uint32; // use types id } table Load { base: Expression; } table Store { base: Expression; } table GetElementRef { base: Expression; path: [uint64]; } table GetElementByName { base: Expression; names: [string]; } table StoreElementRef { base: Expression; path: [uint64]; } table StoreElementByName { base: Expression; names: [string]; } table VirMethodContext { srcCodeIdentifier: string(shared); originalFuncType: uint32; // use types id genericTypeParams: [uint32]; // use types id } table Apply { base: Expression; isSuperCall: bool = false; // FuncCall instantiatedTypeArgs: [uint32]; // use types id thisType: uint32; // use types id } table Invoke { base: Expression; // FuncCall instantiatedTypeArgs: [uint32]; // use types id thisType: uint32; // use types id // DynamicDispatch virMethodCtx: VirMethodContext; } table TypeCast { base: Expression; overflowStrategy: OverflowStrategy; } table InstanceOf { base: Expression; targetType: uint32; // use types id, the class type of this Instanceof operation } table Box { base: Expression; } table UnBox { base: Expression; } table Terminator { base: Expression; successors: [uint32]; // use values id } table GoTo { base: Terminator; } table Branch { base: Terminator; sourceExpr: SourceExpr; } table MultiBranch { base: Terminator; caseVals: [uint64]; } table Exit { base: Terminator; } table RaiseException { base: Terminator; } table ApplyWithException { base: Terminator; thisType: uint32; // use types id instantiatedTypeArgs: [uint32]; // use types id } table InvokeWithException { base: Terminator; // FuncCallWithException instantiatedTypeArgs: [uint32]; // use types id thisType: uint32; // use types id // DynamicDispatchWithException virMethodCtx: VirMethodContext; } table IntOpWithException { base: Terminator; opKind: CHIRExprKind; overflowStrategy: OverflowStrategy; } table TypeCastWithException { base: Terminator; } table IntrinsicWithException { base: Terminator; intrinsicKind: IntrinsicKind; instantiatedTypeArgs: [uint32]; // use types id } table AllocateWithException { base: Terminator; targetType: uint32; // use types id } table RawArrayAllocateWithException { base: Terminator; elementType: uint32; // use types id } table RawArrayLiteralAllocateWithException { base: Terminator; elementType: uint32; // use types id } table SpawnWithException { base: Terminator; executeClosure: uint32; // use values id } table Tuple { base: Expression; } table Field { base: Expression; path: [uint64]; } table FieldByName { base: Expression; names: [string]; } table RawArrayAllocate { base: Expression; elementType: uint32; // use types id } table RawArrayLiteralAllocate { base: Expression; elementType: uint32; // use types id } table RawArrayLiteralInit { base: Expression; } table RawArrayInitByValue { base: Expression; } table VArray { base: Expression; } table VArrayBd { base: Expression; } table GetException { base: Expression; } table Intrinsic { base: Expression; intrinsicKind: IntrinsicKind; instantiatedTypeArgs: [uint32]; // use types id } table If { base: Expression; } table Loop { base: Expression; } table ForInRange { base: Expression; } table ForInIter { base: Expression; } table ForInClosedRange { base: Expression; } table Debug { base: Expression; srcCodeIdentifier: string(shared); } table Spawn { base: Expression; executeClosure: uint32; // use values id } table Lambda { base: Expression; funcTy: uint32; // use types id isLocalFunc: bool = false; identifier: string(shared); srcCodeIdentifier: string(shared); params: [uint32]; // use values id genericTypeParams: [uint32]; // use types id body: uint32; // use values id retVal: uint32; // use values id isCompileTimeValue: bool = false; } table InvokeStatic { base: Expression; // FuncCall instantiatedTypeArgs: [uint32]; // use types id thisType: uint32; // use types id // DynamicDispatch virMethodCtx: VirMethodContext; } table GetInstantiateValue { base: Expression; instantiateTys: [uint32]; // use types id } table InvokeStaticWithException { base: Terminator; // FuncCallWithException instantiatedTypeArgs: [uint32]; // use types id thisType: uint32; // use types id // DynamicDispatchWithException virMethodCtx: VirMethodContext; } table TransformToGeneric { base: Expression; } table TransformToConcrete { base: Expression; } table UnBoxToRef { base: Expression; } table GetRTTI { base: Expression; } table GetRTTIStatic { base: Expression; rttiType: uint32; // use type id } union TypeElem { RuneType, BooleanType, UnitType, NothingType, IntType, FloatType, TupleType, RawArrayType, VArrayType, FuncType, CustomType, EnumType, StructType, ClassType, CStringType, CPointerType, GenericType, RefType, BoxType, VoidType, ThisType, } union CustomTypeDefElem { EnumDef, StructDef, ClassDef, ExtendDef, } union ExpressionElem { UnaryExpression, BinaryExpression, Constant, Allocate, Load, Store, GetElementRef, GetElementByName, StoreElementRef, StoreElementByName, Apply, Invoke, TypeCast, InstanceOf, Box, UnBox, GoTo, Branch, MultiBranch, Exit, RaiseException, ApplyWithException, InvokeWithException, IntOpWithException, TypeCastWithException, IntrinsicWithException, AllocateWithException, RawArrayAllocateWithException, SpawnWithException, Tuple, Field, FieldByName, RawArrayAllocate, RawArrayLiteralInit, RawArrayInitByValue, VArray, VArrayBd, GetException, Intrinsic, If, Loop, ForInRange, ForInIter, ForInClosedRange, Debug, Spawn, Lambda, InvokeStatic, InvokeStaticWithException, GetInstantiateValue, TransformToConcrete, TransformToGeneric, UnBoxToRef, GetRTTI, GetRTTIStatic, } union ValueElem { BoolLiteral, RuneLiteral, StringLiteral, IntLiteral, FloatLiteral, UnitLiteral, NullLiteral, Parameter, LocalVar, GlobalVar, Func, ImportedVar, ImportedFunc, Block, BlockGroup, } enum PackageAccessLevel: uint8 { INVALID, INTERNAL, PROTECTED, PUBLIC, } enum Phase: uint8 { RAW, // before compiler optimization, after translation from ast OPT, // after compiler optimization PLUGIN, // after perform pulgin ANALYSIS_FOR_CJLINT, // after analysis for cjlint } table CHIRPackage { name: string(shared); path: string(shared); pkgAccessLevel: PackageAccessLevel; types: [TypeElem]; values: [ValueElem]; exprs: [ExpressionElem]; defs: [CustomTypeDefElem]; packageInitFunc: uint32; // use values id phase: Phase; packageLiteralInitFunc: uint32; // use valueID (Func) maxImportedValueId: uint32; maxImportedStructId: uint32; maxImportedClassId: uint32; maxImportedEnumId: uint32; maxImportedExtendId: uint32; } root_type CHIRPackage; cangjie_compiler-1.0.7/schema/StyleGuide.md000066400000000000000000000016501510705540100206510ustar00rootroot00000000000000# Style guide Identifiers in a schema are meant to translate to many different programming languages, so using the style of your "main" language is generally a bad idea. For this reason, below is a suggested style guide to adhere to, to keep schemas consistent for interoperation regardless of the target language. Where possible, the code generators for specific languages will generate identifiers that adhere to the language style, based on the schema identifiers. - Table, struct, enum and rpc names (types): UpperCamelCase. - Table and struct field names: snake_case. This is translated to lowerCamelCase automatically for some languages, e.g. Java. - Enum values: UpperCamelCase. - namespaces: UpperCamelCase. Formatting (this is less important, but still worth adhering to): - Opening brace: on the same line as the start of the declaration. - Spacing: Indent by 2 spaces. None around `:` for types, on both sides for `=`. cangjie_compiler-1.0.7/src/000077500000000000000000000000001510705540100155765ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/AST/000077500000000000000000000000001510705540100162255ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/AST/ASTContext.cpp000066400000000000000000000230701510705540100207270ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the ASTContext related classes. */ #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Walker.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace AST; ASTContext::ASTContext(DiagnosticEngine& diag, AST::Package& pkg) : diag(diag), curPackage(&pkg), fullPackageName(pkg.fullPackageName) { Reset(); } void ASTContext::DeleteDesugarExpr(OwnedPtr& desugar) { DeleteInvertedIndexes(desugar.get()); desugar = OwnedPtr(); } void ASTContext::DeleteInvertedIndexes(Ptr root) { if (!root) { return; } auto deleteInvertedIndexes = [this](Ptr node) { if (node && node->symbol) { invertedIndex.Delete(node->symbol); } return VisitAction::WALK_CHILDREN; }; Walker(root, deleteInvertedIndexes).Walk(); searcher->InvalidateCache(); } void ASTContext::DeleteCurrentInvertedIndexes(Ptr node) { if (node && node->symbol) { invertedIndex.Delete(node->symbol); } } void ASTContext::Reset() { currentScopeLevel = 0; currentScopeName = TOPLEVEL_SCOPE_NAME; currentCheckingNodes = {}; SymbolApi::ResetID(); symbolTable.clear(); invertedIndex.Reset(); } std::string ASTContext::GetPackageName(Ptr node) { std::string name; if (node == nullptr) { return name; } auto visitor = [&name](Ptr node) { switch (node->astKind) { case AST::ASTKind::REF_EXPR: { auto re = RawStaticCast(node); name = re->ref.identifier + name; return VisitAction::STOP_NOW; } case AST::ASTKind::MEMBER_ACCESS: { auto ma = RawStaticCast(node); name = "." + ma->field + name; return VisitAction::WALK_CHILDREN; } case AST::ASTKind::REF_TYPE: { auto rt = RawStaticCast(node); name = rt->ref.identifier + name; return VisitAction::STOP_NOW; } case AST::ASTKind::QUALIFIED_TYPE: { auto qt = RawStaticCast(node); name = "." + qt->field + name; return VisitAction::WALK_CHILDREN; } default: break; } return VisitAction::STOP_NOW; }; Walker(node, visitor).Walk(); return name; } void ASTContext::StoreOuterVarWithPatternDecl(const AST::VarDecl& vd, AST::VarWithPatternDecl& vpd) { varDeclToVarWithPatternDeclMap[&vd] = &vpd; } AST::VarDeclAbstract& ASTContext::GetOuterVarDeclAbstract(AST::VarDecl& vd) const { auto iter = varDeclToVarWithPatternDeclMap.find(&vd); if (iter != varDeclToVarWithPatternDeclMap.cend()) { CJC_NULLPTR_CHECK(iter->second); return *iter->second; } return vd; } void ASTContext::InsertEnumConstructor(const std::string& name, size_t argSize, AST::Decl& decl, bool enableMacroInLsp) { // Both the EnumConstructor nodes before and after expansion are added to the symbol table in LSP, need to skip // nodes before macro expansion. if (enableMacroInLsp && IsNodeInOriginalMacroCallNodes(decl)) { return; } auto iter = enumConstructors.find(name); if (iter != enumConstructors.cend()) { auto& argSizeToDecls = iter->second; auto argSizeToDeclsIter = argSizeToDecls.find(argSize); if (argSizeToDeclsIter != argSizeToDecls.cend()) { argSizeToDeclsIter->second.emplace_back(&decl); } else { argSizeToDecls.emplace(std::make_pair(argSize, std::vector>{&decl})); } } else { enumConstructors.emplace(std::make_pair( name, std::unordered_map>>{{argSize, std::vector>{&decl}}})); } } bool ASTContext::IsNodeInOriginalMacroCallNodes(AST::Decl& decl) const { if (!decl.curFile) { return false; } for (auto& originalMacroCallNode : decl.curFile->originalMacroCallNodes) { if (originalMacroCallNode.get()->astKind != ASTKind::MACRO_EXPAND_DECL) { return false; } auto med = StaticAs(originalMacroCallNode.get()); if (!med->invocation.decl.get() || med->invocation.decl->astKind != AST::ASTKind::ENUM_DECL) { return false; } auto enumNode = StaticAs(med->invocation.decl.get()); for (auto& constructor : enumNode->constructors) { if (constructor.get() == &decl) { return true; } } } return false; } bool ASTContext::IsEnumConstructor(const std::string& name) const { return Utils::InKeys(name, enumConstructors); } const std::vector>& ASTContext::FindEnumConstructor(const std::string& name, size_t argSize) const { const static std::vector> EMPTY_DECLS = {}; auto iter = enumConstructors.find(name); if (iter != enumConstructors.cend()) { auto& argSizeToDecls = iter->second; auto argSizeToDeclsIter = argSizeToDecls.find(argSize); if (argSizeToDeclsIter != argSizeToDecls.cend()) { return argSizeToDeclsIter->second; } } return EMPTY_DECLS; } void ASTContext::ClearTypeCheckCache(const AST::Node& root) { AST::ConstWalker cleaner(&root, [this](Ptr node) { typeCheckCache.erase(node); return VisitAction::WALK_CHILDREN; }); cleaner.Walk(); } void ASTContext::RemoveTypeCheckCache(const AST::Node& node) { typeCheckCache.erase(&node); } void ASTContext::SkipSynForCorrectTyRec(const AST::Node& root) { AST::ConstWalker enabler(&root, [this](Ptr node) { if (Ty::IsTyCorrect(node->ty)) { SkipSynForCorrectTy(*node); } return VisitAction::WALK_CHILDREN; }); enabler.Walk(); } void ASTContext::SkipSynForCorrectTy(const AST::Node& node) { auto key = CacheKey{.target = node.ty, .isDesugared = false, .diagKey = 0}; if (auto expr = DynamicCast(&node); expr && expr->desugarExpr) { key.isDesugared = true; } typeCheckCache[&node].lastKey = key; } void ASTContext::RemoveDeclByName(const Names& names, const Decl& decl) { auto found = declMap.find(names); if (found != declMap.end()) { Utils::EraseIf(found->second, [&decl](auto it) { return it == &decl; }); } } bool ASTContext::HasTargetTy(Ptr node) const { auto find = targetTypeMap.find(node); return find != targetTypeMap.end() && find->second != nullptr; } void ASTContext::AddDeclName(const Names& names, AST::Decl& decl) { declMap[names].emplace_back(&decl); } std::vector> ASTContext::GetDeclsByName(const Names& names) const { auto found = declMap.find(names); return found != declMap.end() ? found->second : std::vector>{}; } void InvertedIndex::Reset() { nameIndexes.clear(); scopeNameIndexes.clear(); scopeGateMap.clear(); scopeLevelIndexes.clear(); astKindIndexes.clear(); nameTrie->Reset(); scopeNameTrie->Reset(TOPLEVEL_SCOPE_NAME); astKindTrie->Reset(); for (auto& kindString : AST::ASTKIND_TO_STRING_MAP) { astKindTrie->Insert(kindString.second); } posBeginTrie->Reset(); posEndTrie->Reset(); } void InvertedIndex::Index(AST::Symbol* symbol, bool withTrie) { if (symbol == nullptr) { return; } std::string scopeName = ScopeManagerApi::GetScopeNameWithoutTail(symbol->scopeName); nameIndexes[symbol->name].insert(symbol->id); scopeNameIndexes[scopeName].insert(symbol->id); if (symbol->scopeName.find(ScopeManagerApi::childScopeNameSplit) != std::string::npos) { scopeGateMap[symbol->scopeName] = symbol; } scopeLevelIndexes[symbol->scopeLevel].insert(symbol->id); astKindIndexes[AST::ASTKIND_TO_STRING_MAP[symbol->astKind]].insert(symbol->id); if (withTrie) { nameTrie->Insert(symbol->name, *symbol->id); posBeginTrie->Insert(PosSearchApi::PosToStr(symbol->node->begin), *symbol->id); posEndTrie->Insert(PosSearchApi::PosToStr(symbol->node->end), *symbol->id); } } void InvertedIndex::Delete(AST::Symbol* symbol) { if (symbol == nullptr) { return; } if (symbol->invertedIndexBeenDeleted) { return; } std::string scopeName = ScopeManagerApi::GetScopeNameWithoutTail(symbol->scopeName); nameIndexes[symbol->name].erase(symbol); scopeNameIndexes[scopeName].erase(symbol); if (symbol->scopeName.find(ScopeManagerApi::childScopeNameSplit) != std::string::npos) { scopeGateMap.erase(symbol->scopeName); } scopeLevelIndexes[symbol->scopeLevel].erase(symbol); astKindIndexes[AST::ASTKIND_TO_STRING_MAP[symbol->astKind]].erase(symbol); nameTrie->Delete(symbol->name, *symbol); posBeginTrie->Delete(PosSearchApi::PosToStr(symbol->node->begin), *symbol); posEndTrie->Delete(PosSearchApi::PosToStr(symbol->node->end), *symbol); symbol->invertedIndexBeenDeleted = true; } std::set> ASTContext::Mem2Decls(const AST::MemSig& memSig) { auto decls = mem2Decls[memSig]; for (auto d : mem2Decls[memSig]) { decls.insert(subtypeDeclsMap[d].begin(), subtypeDeclsMap[d].end()); } return decls; } cangjie_compiler-1.0.7/src/AST/ASTTypeValidator.cpp000066400000000000000000000150101510705540100220650ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements validating the used nodes. */ #include #include "cangjie/AST/ASTTypeValidator.h" namespace Cangjie::AST { class ASTTypeValidator { public: ~ASTTypeValidator() = default; explicit ASTTypeValidator(DiagnosticEngine& diag) : id(Walker::GetNextWalkerID()), diag(diag) { (void)validRanges.emplace(RawStaticCast(nullptr), MakeRange(DEFAULT_POSITION, DEFAULT_POSITION)); } VisitAction PreVisitor(const Node& node) { auto range = MakeRange(node.begin, node.end); if (!range.HasZero()) { (void)validRanges.emplace(&node, std::move(range)); } auto [action, needCheck] = GetNodeValidatingStrategy(node); if (auto expr = DynamicCast(&node); expr && expr->desugarExpr) { auto preVisit = [this](auto node) { return PreVisitor(*node); }; auto postVisit = [this](auto node) { return PostVisitor(*node); }; Walker(expr->desugarExpr.get(), id, preVisit, postVisit).Walk(); action = VisitAction::SKIP_CHILDREN; } if (action == VisitAction::SKIP_CHILDREN) { needCheck = false; } (void)checkStatus.emplace(&node, needCheck); // Dot not check the children of type node (may be typealias substitution result). return Is(node) ? VisitAction::SKIP_CHILDREN : action; } VisitAction PostVisitor(const Node& node) { if (!checkStatus[&node]) { (void)checkStatus.erase(&node); return VisitAction::WALK_CHILDREN; } if (node.TestAttr(Attribute::FROM_COMMON_PART)) { return VisitAction::SKIP_CHILDREN; } if (node.TestAttr(Attribute::COMMON) && node.TestAttr(Attribute::IMPORTED)) { return VisitAction::SKIP_CHILDREN; } // Target can be ignored that: // 1. the target is type node's target. // 2. the target has ignored astKinds. auto curTarget = node.GetTarget(); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool valid = !curTarget || Is(node) || Utils::In(curTarget->astKind, ignoreKinds) || Ty::IsTyCorrect(curTarget->ty); CJC_ASSERT(valid); valid = valid && Ty::IsTyCorrect(node.ty) && !node.ty->HasIdealTy() && !node.ty->HasQuestTy(); #endif CJC_ASSERT(valid); if (auto fb = DynamicCast(&node); fb) { Ptr target = nullptr; if (fb->parentClassLike) { target = fb->parentClassLike; } else if (fb->parentStruct) { target = fb->parentStruct; } else if (fb->parentEnum) { target = fb->parentEnum; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND valid = valid && (!target || Ty::IsTyCorrect(target->ty)); #endif CJC_ASSERT(valid); } auto [preNode, range] = validRanges.top(); // Since 'validRanges' has default value, it must not be empty. if (!valid) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_invalid_node_after_check, node, range); builder.AddNote("please report this to Cangjie team and include the project"); return VisitAction::STOP_NOW; } if (&node == preNode) { validRanges.pop(); } return VisitAction::WALK_CHILDREN; }; unsigned id; private: // Get node visiting strategy for validation step. static std::pair GetNodeValidatingStrategy(const Node& node) { auto action = VisitAction::WALK_CHILDREN; bool needCheck = true; // Ignore for node which: // 1. the node is added as initial arg. // 2. the node is generic. // 3. the node is marked as 'INCRE_COMPILE' which type is loaded from cache. // 4. the node's kind is existed in ignore map. if (node.TestAttr(Attribute::GENERIC) || Utils::In(node.astKind, ignoreKinds) || (node.astKind != ASTKind::PACKAGE && node.TestAttr(Attribute::INCRE_COMPILE)) || (node.astKind == ASTKind::FUNC_ARG && node.TestAttr(Attribute::HAS_INITIAL))) { action = VisitAction::SKIP_CHILDREN; } else if (Utils::In(node.astKind, noneTyKinds)) { // Do not check nodes that should not have sema ty. needCheck = false; } else if (Is(node) && node.ty->IsGeneric()) { // For type node, if type itself is generic T, ignored. action = VisitAction::SKIP_CHILDREN; } else if (auto target = node.GetTarget(); target && target->astKind == ASTKind::PACKAGE_DECL) { // Ignore children when target is package decl. action = VisitAction::SKIP_CHILDREN; } return {action, needCheck}; } DiagnosticEngine& diag; std::stack, Range>> validRanges; std::unordered_map, bool> checkStatus; // Node kinds who's type and children can be ignored. inline const static std::unordered_set ignoreKinds { ASTKind::GENERIC_PARAM_DECL, ASTKind::GENERIC_CONSTRAINT, ASTKind::PRIMARY_CTOR_DECL, ASTKind::TYPE_ALIAS_DECL, ASTKind::BUILTIN_DECL, ASTKind::MODIFIER, ASTKind::ANNOTATION, ASTKind::PACKAGE_SPEC, ASTKind::IMPORT_SPEC, ASTKind::WILDCARD_PATTERN, ASTKind::GENERIC, ASTKind::MACRO_EXPAND_DECL, ASTKind::MACRO_EXPAND_EXPR, ASTKind::MACRO_EXPAND_PARAM}; // Node kinds who's sema type can be ignored. inline const static std::unordered_set noneTyKinds{ ASTKind::PACKAGE, ASTKind::FILE, ASTKind::PRIMARY_CTOR_DECL, ASTKind::MACRO_DECL, ASTKind::MAIN_DECL, ASTKind::CLASS_BODY, ASTKind::INTERFACE_BODY, ASTKind::STRUCT_BODY, ASTKind::DUMMY_BODY, ASTKind::RETURN_EXPR, ASTKind::LET_PATTERN_DESTRUCTOR, ASTKind::FUNC_PARAM_LIST, }; }; void ValidateUsedNodes(DiagnosticEngine& diagEngine, Package& pkg) { ASTTypeValidator validator(diagEngine); auto preVisit = [&validator](auto node) { return validator.PreVisitor(*node); }; auto postVisit = [&validator](auto node) { return validator.PostVisitor(*node); }; Walker(&pkg, validator.id, preVisit, postVisit).Walk(); } } // namespace Cangjie::AST cangjie_compiler-1.0.7/src/AST/CMakeLists.txt000066400000000000000000000015061510705540100207670ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set(LIBAST_FFI_AST Node.cpp RecoverDesugar.cpp Types.cpp IntLiteral.cpp Walker.cpp Clone.cpp Create.cpp Utils.cpp Identifier.cpp Comment.cpp) set(AST_EXTRA_SRC Cache.cpp PrintNode.cpp Query.cpp Searcher.cpp ASTContext.cpp ASTTypeValidator.cpp ScopeManagerApi.cpp) add_library(CangjieASTCommon OBJECT ${LIBAST_FFI_AST}) add_library(CangjieASTExtra OBJECT ${AST_EXTRA_SRC}) target_compile_options(CangjieASTCommon PRIVATE ${CJC_EXTRA_WARNINGS}) target_compile_options(CangjieASTExtra PRIVATE ${CJC_EXTRA_WARNINGS}) cangjie_compiler-1.0.7/src/AST/Cache.cpp000066400000000000000000000033361510705540100177410ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements type check cache utils. */ #include "cangjie/AST/Cache.h" #include "cangjie/Utils/Casting.h" using namespace Cangjie; using namespace AST; namespace Cangjie::AST { TargetCache CollectTargets(const Node& node) { if (auto farg = DynamicCast(&node)) { return CollectTargets(*farg->expr); } if (auto target1 = node.GetTarget()) { if (auto ma = DynamicCast(&node); ma && ma->baseExpr && ma->baseExpr->IsReferenceExpr()) { auto target2 = ma->baseExpr->GetTarget(); return std::make_pair(target1, target2); } else { return std::make_pair(target1, nullptr); } } return std::make_pair(nullptr, nullptr); } void RestoreTargets(Node& node, const TargetCache& targets) { if (auto farg = DynamicCast(&node)) { RestoreTargets(*farg->expr, targets); } node.SetTarget(targets.first); if (auto ma = DynamicCast(&node); ma && ma->baseExpr && ma->baseExpr->IsReferenceExpr()) { ma->baseExpr->SetTarget(targets.second); } } bool CacheKey::operator==(const CacheKey& b) const { return target == b.target && isDesugared == b.isDesugared && diagKey == b.diagKey; } bool MemSig::operator==(const MemSig& b) const { return id == b.id && isVarOrProp == b.isVarOrProp && arity == b.arity && genArity == b.genArity; } } // namespace Cangjie::AST cangjie_compiler-1.0.7/src/AST/Clone.cpp000066400000000000000000002042321510705540100177740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements AST clone apis. */ #include "cangjie/AST/Clone.h" #include #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/NodeX.h" #include "cangjie/Basic/Match.h" #include "cangjie/Basic/Print.h" namespace Cangjie::AST { using namespace Meta; void DefaultVisitFunc(const Node& /* source */, const Node& /* target */) { } void SetIsClonedSourceCode(const Node& /* source */, Node& target) { target.EnableAttr(Attribute::IS_CLONED_SOURCE_CODE); } void CopyBasicInfo(Ptr source, Ptr target) { if (source && target) { target->begin = source->begin; target->end = source->end; target->curMacroCall = source->curMacroCall; target->isInMacroCall = source->isInMacroCall; target->comments = source->comments; CopyNodeScopeInfo(target, source); } } OwnedPtr CloneGeneric(const Generic& generic, const VisitFunc& visitor) { auto ret = MakeOwned(); for (auto& it : generic.typeParameters) { auto gpd = MakeOwned(); auto d = ASTCloner::Clone(it.get(), visitor); gpd.reset(As(d.release())); ret->typeParameters.push_back(std::move(gpd)); } for (auto& it : generic.genericConstraints) { ret->genericConstraints.push_back(ASTCloner::Clone(it.get(), visitor)); } ret->leftAnglePos = generic.leftAnglePos; ret->rightAnglePos = generic.rightAnglePos; return ret; } MacroInvocation CloneMacroInvocation(const MacroInvocation& me) { MacroInvocation mi; mi.fullName = me.fullName; mi.fullNameDotPos = me.fullNameDotPos; mi.identifier = me.identifier; mi.identifierPos = me.identifierPos; mi.leftSquarePos = me.leftSquarePos; mi.attrs = me.attrs; mi.rightSquarePos = me.rightSquarePos; mi.leftParenPos = me.leftParenPos; mi.args = me.args; mi.rightParenPos = me.rightParenPos; mi.newTokens = me.newTokens; mi.newTokensStr = me.newTokensStr; mi.parent = me.parent; mi.scope = me.scope; return mi; } namespace { // This function should be called in each node which inherit Node directly. void CopyNodeField(Ptr ret, const Node& e) { ret->begin = e.begin; ret->end = e.end; ret->ty = e.ty; ret->curMacroCall = e.curMacroCall; ret->isInMacroCall = e.isInMacroCall; ret->comments = e.comments; CopyNodeScopeInfo(ret, &e); ret->CopyAttrs(e.GetAttrs()); } } // namespace // Clone nodes which inherit Node indirectly, should only be call by clone function which inherit Node directly. OwnedPtr ASTCloner::CloneGenericParamDecl(const GenericParamDecl& gpd) { auto ret = MakeOwned(); ret->outerDecl = gpd.outerDecl; ret->commaPos = gpd.commaPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneFuncParam(const FuncParam& fp, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->colonPos = fp.colonPos; ret->assignment = CloneExpr(fp.assignment.get(), visitor); if (fp.desugarDecl) { auto decl = CloneDecl(fp.desugarDecl.get(), visitor); ret->desugarDecl.reset(RawStaticCast(decl.release())); } ret->isNamedParam = fp.isNamedParam; ret->isMemberParam = fp.isMemberParam; ret->commaPos = fp.commaPos; ret->notMarkPos = fp.notMarkPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneVarWithPatternDecl(const VarWithPatternDecl& vwpd, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->type = CloneType(vwpd.type.get(), visitor); ret->assignPos = vwpd.assignPos; ret->colonPos = vwpd.colonPos; ret->isVar = vwpd.isVar; ret->isConst = vwpd.isConst; ret->irrefutablePattern = ClonePattern(vwpd.irrefutablePattern.get(), visitor); ret->initializer = CloneExpr(vwpd.initializer.get(), visitor); return ret; } OwnedPtr ASTCloner::CloneVarDecl(const VarDecl& vd, const VisitFunc& visitor) { auto ret = match(vd)([&visitor](const FuncParam& e) { return CloneFuncParam(e, visitor); }, []() { return MakeOwned(); }); // Clone field in VarDecl. ret->type = CloneType(vd.type.get(), visitor); ret->colonPos = vd.colonPos; ret->initializer = CloneExpr(vd.initializer.get(), visitor); ret->assignPos = vd.assignPos; ret->isVar = vd.isVar; ret->isConst = vd.isConst; ret->isResourceVar = vd.isResourceVar; ret->isIdentifierCompilerAdd = vd.isIdentifierCompilerAdd; ret->parentPattern = vd.parentPattern; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneFuncDecl(const FuncDecl& fd, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->leftParenPos = fd.leftParenPos; ret->rightParenPos = fd.rightParenPos; CJC_NULLPTR_CHECK(fd.funcBody); ret->funcBody = CloneNode(fd.funcBody.get(), visitor); ret->funcBody->funcDecl = ret.get(); // Reset funcDecl of funcBody to new function. ret->propDecl = fd.propDecl; ret->constructorCall = fd.constructorCall; ret->op = fd.op; ret->ownerFunc = fd.ownerFunc; ret->overflowStrategy = fd.overflowStrategy; ret->isFastNative = fd.isFastNative; ret->isGetter = fd.isGetter; ret->isSetter = fd.isSetter; ret->isInline = fd.isInline; ret->isConst = fd.isConst; ret->isFrozen = fd.isFrozen; ret->variadicArgIndex = fd.variadicArgIndex; ret->hasVariableLenArg = fd.hasVariableLenArg; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::ClonePrimaryCtorDecl(const PrimaryCtorDecl& pcd, const VisitFunc& visitor) { auto ret = MakeOwned(); CJC_NULLPTR_CHECK(pcd.funcBody); ret->funcBody = CloneNode(pcd.funcBody.get(), visitor); ret->overflowStrategy = pcd.overflowStrategy; ret->isFastNative = pcd.isFastNative; ret->isConst = pcd.isConst; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::ClonePropDecl(const PropDecl& pd, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->type = CloneType(pd.type.get(), visitor); ret->colonPos = pd.colonPos; ret->leftCurlPos = pd.leftCurlPos; ret->rightCurlPos = pd.rightCurlPos; ret->isVar = pd.isVar; ret->isConst = pd.isConst; for (auto& func : pd.setters) { auto decl = CloneDecl(func.get(), visitor); auto fd = MakeOwned(); fd.reset(As(decl.release())); fd->EnableAttr(Attribute::COMPILER_ADD); ret->setters.push_back(std::move(fd)); } for (auto& func : pd.getters) { auto decl = CloneDecl(func.get(), visitor); auto fd = MakeOwned(); fd.reset(As(decl.release())); fd->EnableAttr(Attribute::COMPILER_ADD); ret->getters.push_back(std::move(fd)); } ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneRefType(const RefType& type, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), type); ret->ref = type.ref; ret->leftAnglePos = type.leftAnglePos; ret->typeArguments = std::vector>(); for (auto& it : type.typeArguments) { ret->typeArguments.push_back(CloneType(it.get(), visitor)); } ret->rightAnglePos = type.rightAnglePos; ret->commaPos = type.commaPos; ret->bitAndPos = type.bitAndPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneExtendDecl(const ExtendDecl& ed, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->extendedType = CloneType(ed.extendedType.get(), visitor); for (auto& interface : ed.inheritedTypes) { ret->inheritedTypes.push_back(CloneType(interface.get(), visitor)); } for (auto& func : ed.members) { ret->members.push_back(CloneDecl(func.get(), visitor)); } if (ed.generic) { ret->generic = CloneGeneric(*ed.generic, visitor); } if (ed.bodyScope) { ret->bodyScope = MakeOwned(); CopyNodeScopeInfo(ret->bodyScope.get(), ed.bodyScope.get()); } ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneMacroExpandDecl(const MacroExpandDecl& med) { auto ret = MakeOwned(); ret->invocation = CloneMacroInvocation(med.invocation); ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneStructDecl(const StructDecl& sd, const VisitFunc& visitor) { auto ret = MakeOwned(); for (auto& it : sd.inheritedTypes) { ret->inheritedTypes.push_back(CloneType(it.get(), visitor)); } ret->body = CloneNode(sd.body.get(), visitor); if (sd.generic) { ret->generic = CloneGeneric(*sd.generic, visitor); } ret->upperBoundPos = sd.upperBoundPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneClassDecl(const ClassDecl& cd, const VisitFunc& visitor) { auto ret = MakeOwned(); for (auto& it : cd.inheritedTypes) { ret->inheritedTypes.push_back(CloneType(it.get(), visitor)); } ret->body = CloneNode(cd.body.get(), visitor); if (cd.generic) { ret->generic = CloneGeneric(*cd.generic, visitor); } ret->upperBoundPos = cd.upperBoundPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneInterfaceDecl(const InterfaceDecl& id, const VisitFunc& visitor) { auto ret = MakeOwned(); for (auto& it : id.inheritedTypes) { ret->inheritedTypes.push_back(CloneType(it.get(), visitor)); } ret->body = CloneNode(id.body.get(), visitor); if (id.generic) { ret->generic = CloneGeneric(*id.generic, visitor); } ret->upperBoundPos = id.upperBoundPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneEnumDecl(const EnumDecl& ed, const VisitFunc& visitor) { auto ret = MakeOwned(); for (auto& it : ed.inheritedTypes) { ret->inheritedTypes.push_back(CloneType(it.get(), visitor)); } for (auto& it : ed.constructors) { ret->constructors.push_back(CloneDecl(it.get(), visitor)); } for (auto& func : ed.members) { ret->members.push_back(CloneDecl(func.get(), visitor)); } if (ed.generic) { ret->generic = CloneGeneric(*ed.generic, visitor); } ret->hasArguments = ed.hasArguments; if (ed.bodyScope) { ret->bodyScope = MakeOwned(); CopyNodeScopeInfo(ret->bodyScope.get(), ed.bodyScope.get()); } ret->leftCurlPos = ed.leftCurlPos; ret->bitOrPosVector = ed.bitOrPosVector; ret->rightCurlPos = ed.rightCurlPos; ret->upperBoundPos = ed.upperBoundPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneTypeAliasDecl(const TypeAliasDecl& tad, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->assignPos = tad.assignPos; ret->type = CloneType(tad.type.get(), visitor); if (tad.generic != nullptr) { ret->generic = CloneGeneric(*tad.generic, visitor); } ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneQualifiedType(const QualifiedType& node, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->baseType = CloneType(node.baseType.get(), visitor); ret->field = node.field; ret->target = node.target; for (auto& it : node.typeArguments) { ret->typeArguments.push_back(CloneType(it.get(), visitor)); } return ret; } OwnedPtr ASTCloner::CloneParenType(const ParenType& node, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->type = CloneType(node.type.get(), visitor); ret->leftParenPos = node.leftParenPos; ret->rightParenPos = node.rightParenPos; return ret; } OwnedPtr ASTCloner::CloneOptionType(const OptionType& node, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->componentType = CloneType(node.componentType.get(), visitor); ret->questNum = node.questNum; ret->questVector = node.questVector; if (node.desugarType != nullptr) { ret->desugarType = Clone(node.desugarType.get(), visitor); } return ret; } OwnedPtr ASTCloner::CloneFuncType(const FuncType& node, const VisitFunc& visitor) { auto ret = MakeOwned(); for (auto& paramType : node.paramTypes) { ret->paramTypes.emplace_back(CloneType(paramType.get(), visitor)); } ret->retType = CloneType(node.retType.get(), visitor); ret->isC = node.isC; ret->leftParenPos = node.leftParenPos; ret->rightParenPos = node.rightParenPos; ret->arrowPos = node.arrowPos; return ret; } OwnedPtr ASTCloner::CloneTupleType(const TupleType& node, const VisitFunc& visitor) { auto ret = MakeOwned(); for (auto& it : node.fieldTypes) { ret->fieldTypes.push_back(CloneType(it.get(), visitor)); } ret->leftParenPos = node.leftParenPos; ret->rightParenPos = node.rightParenPos; ret->commaPosVector = node.commaPosVector; return ret; } OwnedPtr ASTCloner::CloneConstantType(const ConstantType& node, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->constantExpr = CloneExpr(node.constantExpr.get(), visitor); ret->dollarPos = node.dollarPos; return ret; } OwnedPtr ASTCloner::CloneVArrayType(const VArrayType& node, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->leftAnglePos = node.leftAnglePos; ret->typeArgument = CloneType(node.typeArgument.get(), visitor); ret->constantType = CloneType(node.constantType.get(), visitor); ret->rightAnglePos = node.rightAnglePos; return ret; } // Clone nodes which inherit Node directly. OwnedPtr ASTCloner::CloneType(Ptr type, const VisitFunc& visitor) { if (!type) { return OwnedPtr(); } auto ret = match(*type)([&visitor](const RefType& e) { return OwnedPtr(CloneRefType(e, visitor)); }, [](const PrimitiveType& e) { return OwnedPtr(MakeOwned(e)); }, [&visitor](const ParenType& e) { return OwnedPtr(CloneParenType(e, visitor)); }, [&visitor](const QualifiedType& e) { return OwnedPtr(CloneQualifiedType(e, visitor)); }, [&visitor](const OptionType& e) { return OwnedPtr(CloneOptionType(e, visitor)); }, [&visitor](const FuncType& e) { return OwnedPtr(CloneFuncType(e, visitor)); }, [&visitor](const TupleType& e) { return OwnedPtr(CloneTupleType(e, visitor)); }, [&visitor](const ConstantType& e) { return OwnedPtr(CloneConstantType(e, visitor)); }, [&visitor](const VArrayType& e) { return OwnedPtr(CloneVArrayType(e, visitor)); }, [](const ThisType& e) { return OwnedPtr(MakeOwned(e)); }, [](const InvalidType& e) { return OwnedPtr(MakeOwned(e)); }, [](const Type& e) { return OwnedPtr(MakeOwned(e)); }, []() { // Invalid case. return MakeOwned(); }); CJC_ASSERT(ret && ret->astKind == type->astKind); ret->commaPos = type->commaPos; ret->bitAndPos = type->bitAndPos; ret->typeParameterName = type->typeParameterName; ret->typeParameterNameIsRawId = type->typeParameterNameIsRawId; ret->typePos = type->typePos; ret->colonPos = type->colonPos; CopyNodeField(ret.get(), *type); visitor(*type, *ret); ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneMacroExpandExpr(const MacroExpandExpr& mee, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->identifier = mee.identifier; expr->invocation = CloneMacroInvocation(mee.invocation); for (auto& anno : mee.annotations) { expr->annotations.emplace_back(CloneNode(anno.get(), visitor)); } expr->modifiers.insert(mee.modifiers.begin(), mee.modifiers.end()); return expr; } OwnedPtr ASTCloner::CloneTokenPart(const TokenPart& tp, const VisitFunc& /* visitor */) { auto expr = MakeOwned(); expr->tokens.assign(tp.tokens.begin(), tp.tokens.end()); return expr; } OwnedPtr ASTCloner::CloneQuoteExpr(const QuoteExpr& qe, const VisitFunc& visitor) { auto expr = MakeOwned(); for (auto& i : qe.exprs) { expr->exprs.push_back(CloneExpr(i.get(), visitor)); } expr->quotePos = qe.quotePos; expr->leftParenPos = qe.leftParenPos; expr->rightParenPos = qe.rightParenPos; return expr; } OwnedPtr ASTCloner::CloneIfExpr(const IfExpr& ie, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->ifPos = ie.ifPos; expr->leftParenPos = ie.leftParenPos; expr->condExpr = CloneExpr(ie.condExpr.get(), visitor); expr->rightParenPos = ie.rightParenPos; auto n = CloneExpr(ie.thenBody.get(), visitor); expr->thenBody.reset(As(n.release())); expr->hasElse = ie.hasElse; expr->isElseIf = ie.isElseIf; expr->elsePos = ie.elsePos; expr->elseBody = CloneExpr(ie.elseBody.get(), visitor); return expr; } OwnedPtr ASTCloner::CloneTryExpr(const TryExpr& te, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->tryPos = te.tryPos; expr->lParen = te.lParen; for (auto& resource : te.resourceSpec) { expr->resourceSpec.push_back( OwnedPtr(As(CloneDecl(resource.get(), visitor).release()))); } expr->isDesugaredFromSyncBlock = te.isDesugaredFromSyncBlock; expr->isDesugaredFromTryWithResources = te.isDesugaredFromTryWithResources; expr->isIllegalResourceSpec = te.isIllegalResourceSpec; expr->tryBlock = CloneExpr(te.tryBlock.get(), visitor); for (size_t i = 0; i < te.catchPosVector.size(); i++) { expr->catchPosVector.push_back(te.catchPosVector[i]); } CJC_ASSERT(te.catchBlocks.size() == te.catchPatterns.size()); for (size_t i = 0; i < te.catchBlocks.size(); i++) { expr->catchBlocks.push_back(CloneExpr(te.catchBlocks[i].get(), visitor)); expr->catchPatterns.push_back(ClonePattern(te.catchPatterns[i].get(), visitor)); } for (const auto& handler : te.handlers) { auto cloned = Handler(); cloned.pos = handler.pos; cloned.commandPattern = ClonePattern(handler.commandPattern.get(), visitor); cloned.block = CloneExpr(handler.block.get(), visitor); if (handler.desugaredLambda) { cloned.desugaredLambda = CloneLambdaExpr(*handler.desugaredLambda, visitor); CopyNodeField(cloned.desugaredLambda, *handler.desugaredLambda); } expr->handlers.emplace_back(std::move(cloned)); } expr->finallyPos = te.finallyPos; expr->finallyBlock = CloneExpr(te.finallyBlock.get(), visitor); expr->resourceSpecCommaPos = te.resourceSpecCommaPos; expr->rParen = te.rParen; expr->catchLParenPosVector = te.catchLParenPosVector; expr->catchRParenPosVector = te.catchRParenPosVector; if (te.tryLambda) { expr->tryLambda = CloneExpr(te.tryLambda.get(), visitor); CopyNodeField(expr->tryLambda, *te.tryLambda); } if (te.finallyLambda) { expr->finallyLambda = CloneExpr(te.finallyLambda.get(), visitor); CopyNodeField(expr->finallyLambda, *te.finallyLambda); } return expr; } OwnedPtr ASTCloner::CloneThrowExpr(const ThrowExpr& te, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->throwPos = te.throwPos; expr->expr = CloneExpr(te.expr.get(), visitor); return expr; } OwnedPtr ASTCloner::ClonePerformExpr(const PerformExpr& pe, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->performPos = pe.performPos; expr->expr = CloneExpr(pe.expr.get(), visitor); return expr; } OwnedPtr ASTCloner::CloneResumeExpr(const ResumeExpr& re, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->resumePos = re.resumePos; expr->withPos = re.withPos; expr->withExpr = CloneExpr(re.withExpr.get(), visitor); expr->throwingPos = re.throwingPos; expr->throwingExpr = CloneExpr(re.throwingExpr.get(), visitor); return expr; } OwnedPtr ASTCloner::CloneReturnExpr(const ReturnExpr& re, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->returnPos = re.returnPos; expr->expr = CloneExpr(re.expr.get(), visitor); expr->refFuncBody = re.refFuncBody; return expr; } OwnedPtr ASTCloner::CloneWhileExpr(const WhileExpr& we, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->whilePos = we.whilePos; expr->leftParenPos = we.leftParenPos; expr->condExpr = CloneExpr(we.condExpr.get(), visitor); expr->body = CloneExpr(we.body.get(), visitor); expr->rightParenPos = we.rightParenPos; return expr; } OwnedPtr ASTCloner::CloneDoWhileExpr(const DoWhileExpr& dwe, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->doPos = dwe.doPos; expr->body = CloneExpr(dwe.body.get(), visitor); expr->whilePos = dwe.whilePos; expr->leftParenPos = dwe.leftParenPos; expr->condExpr = CloneExpr(dwe.condExpr.get(), visitor); expr->rightParenPos = dwe.rightParenPos; return expr; } OwnedPtr ASTCloner::CloneAssignExpr(const AssignExpr& ae, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->leftValue = CloneExpr(ae.leftValue.get(), visitor); expr->op = ae.op; expr->rightExpr = CloneExpr(ae.rightExpr.get(), visitor); expr->isCompound = ae.isCompound; expr->assignPos = ae.assignPos; return expr; } OwnedPtr ASTCloner::CloneIncOrDecExpr(const IncOrDecExpr& ide, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->op = ide.op; expr->operatorPos = ide.operatorPos; expr->expr = CloneExpr(ide.expr.get(), visitor); return expr; } OwnedPtr ASTCloner::CloneUnaryExpr(const UnaryExpr& ue, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->op = ue.op; expr->expr = CloneExpr(ue.expr.get(), visitor); expr->operatorPos = ue.operatorPos; return expr; } OwnedPtr ASTCloner::CloneBinaryExpr(const BinaryExpr& be, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->op = be.op; expr->leftExpr = CloneExpr(be.leftExpr.get(), visitor); expr->rightExpr = CloneExpr(be.rightExpr.get(), visitor); expr->operatorPos = be.operatorPos; return expr; } OwnedPtr ASTCloner::CloneRangeExpr(const RangeExpr& re, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->startExpr = CloneExpr(re.startExpr.get(), visitor); expr->rangePos = re.rangePos; expr->stopExpr = CloneExpr(re.stopExpr.get(), visitor); expr->colonPos = re.colonPos; expr->stepExpr = CloneExpr(re.stepExpr.get(), visitor); expr->isClosed = re.isClosed; expr->decl = re.decl; return expr; } OwnedPtr ASTCloner::CloneSubscriptExpr(const SubscriptExpr& se, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->baseExpr = CloneExpr(se.baseExpr.get(), visitor); expr->leftParenPos = se.leftParenPos; for (auto& it : se.indexExprs) { expr->indexExprs.push_back(CloneExpr(it.get(), visitor)); } expr->commaPos = se.commaPos; expr->rightParenPos = se.rightParenPos; expr->isTupleAccess = se.isTupleAccess; return expr; } OwnedPtr ASTCloner::CloneMemberAccess(const MemberAccess& ma, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->baseExpr = CloneExpr(ma.baseExpr.get(), visitor); expr->dotPos = ma.dotPos; expr->field = ma.field; expr->target = ma.target; expr->targets = ma.targets; expr->isPattern = ma.isPattern; expr->isAlone = ma.isAlone; expr->instTys = ma.instTys; expr->matchedParentTy = ma.matchedParentTy; expr->callOrPattern = ma.callOrPattern; expr->foundUpperBoundMap = ma.foundUpperBoundMap; expr->isExposedAccess = ma.isExposedAccess; for (auto& it : ma.typeArguments) { expr->typeArguments.push_back(CloneType(it.get(), visitor)); } return expr; } OwnedPtr ASTCloner::CloneCallExpr(const CallExpr& ce, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->baseFunc = CloneExpr(ce.baseFunc.get(), visitor); expr->leftParenPos = ce.leftParenPos; expr->needCheckToTokens = ce.needCheckToTokens; std::unordered_map, Ptr> cloneTable; // For replace desugarArgs for (auto& it : ce.args) { expr->args.push_back(CloneNode(it.get(), visitor)); cloneTable[it.get()] = expr->args.back().get(); } if (ce.desugarArgs.has_value()) { expr->defaultArgs = std::vector>(); for (auto& it : ce.defaultArgs) { expr->defaultArgs.push_back(CloneNode(it.get(), visitor)); cloneTable[it.get()] = expr->defaultArgs.back().get(); } expr->desugarArgs = std::vector>(); for (auto& it : ce.desugarArgs.value()) { expr->desugarArgs.value().push_back(cloneTable[it]); } } expr->rightParenPos = ce.rightParenPos; expr->resolvedFunction = ce.resolvedFunction; expr->callKind = ce.callKind; expr->sugarKind = ce.sugarKind; if (auto ma = DynamicCast(expr->baseFunc.get()); ma) { ma->callOrPattern = expr.get(); } return expr; } OwnedPtr ASTCloner::CloneParenExpr(const ParenExpr& pe, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->leftParenPos = pe.leftParenPos; expr->expr = CloneExpr(pe.expr.get(), visitor); expr->rightParenPos = pe.rightParenPos; return expr; } OwnedPtr ASTCloner::CloneLambdaExpr(const LambdaExpr& le, const VisitFunc& visitor) { auto expr = MakeOwned(CloneNode(le.funcBody.get(), visitor)); if (le.TestAttr(Attribute::MOCK_SUPPORTED)) { expr->EnableAttr(Attribute::MOCK_SUPPORTED); } return expr; } OwnedPtr ASTCloner::CloneLitConstExpr(const LitConstExpr& lce, const VisitFunc& visitor) { auto expr = MakeOwned(lce.kind, lce.stringValue); expr->codepoint = lce.codepoint; expr->delimiterNum = lce.delimiterNum; expr->stringKind = lce.stringKind; expr->rawString = lce.rawString; if (lce.ref) { expr->ref = CloneRefType(*lce.ref, visitor); visitor(*lce.ref, *expr->ref); } if (lce.siExpr) { expr->siExpr = CloneExpr(lce.siExpr.get(), visitor); } return expr; } OwnedPtr ASTCloner::CloneInterpolationExpr( const InterpolationExpr& ie, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->rawString = ie.rawString; expr->dollarPos = ie.dollarPos; expr->block = CloneExpr(ie.block.get(), visitor); return expr; } OwnedPtr ASTCloner::CloneStrInterpolationExpr( const StrInterpolationExpr& sie, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->rawString = sie.rawString; expr->strParts = sie.strParts; for (auto& it : sie.strPartExprs) { expr->strPartExprs.push_back(CloneExpr(it.get(), visitor)); } return expr; } OwnedPtr ASTCloner::CloneArrayLit(const ArrayLit& al, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->leftSquarePos = al.leftSquarePos; for (auto& it : al.children) { expr->children.push_back(CloneExpr(it.get(), visitor)); } expr->commaPosVector = al.commaPosVector; expr->rightSquarePos = al.rightSquarePos; expr->initFunc = al.initFunc; return expr; } OwnedPtr ASTCloner::CloneArrayExpr(const ArrayExpr& ae, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->type = CloneType(ae.type.get(), visitor); expr->leftParenPos = ae.leftParenPos; expr->args.resize(ae.args.size()); for (size_t i = 0; i < ae.args.size(); ++i) { expr->args[i] = CloneNode(ae.args[i].get(), visitor); } expr->commaPosVector = ae.commaPosVector; expr->rightParenPos = ae.rightParenPos; expr->initFunc = ae.initFunc; expr->isValueArray = ae.isValueArray; return expr; } OwnedPtr ASTCloner::ClonePointerExpr(const PointerExpr& ptre, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->type = CloneType(ptre.type.get(), visitor); expr->arg = CloneNode(ptre.arg.get(), visitor); return expr; } OwnedPtr ASTCloner::CloneTupleLit(const TupleLit& tl, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->leftParenPos = tl.leftParenPos; for (auto& it : tl.children) { expr->children.push_back(CloneExpr(it.get(), visitor)); } expr->commaPosVector = tl.commaPosVector; expr->rightParenPos = tl.rightParenPos; return expr; } OwnedPtr ASTCloner::CloneRefExpr(const RefExpr& re, const VisitFunc& visitor) { auto expr = CreateRefExpr(re.ref.identifier); expr->leftAnglePos = re.leftAnglePos; expr->ref.target = re.ref.target; expr->ref.targets = re.ref.targets; for (auto& it : re.typeArguments) { expr->typeArguments.push_back(CloneType(it.get(), visitor)); } expr->rightAnglePos = re.rightAnglePos; expr->isSuper = re.isSuper; expr->isThis = re.isThis; expr->isAlone = re.isAlone; expr->instTys = re.instTys; expr->matchedParentTy = re.matchedParentTy; expr->callOrPattern = re.callOrPattern; return expr; } OwnedPtr ASTCloner::CloneForInExpr(const ForInExpr& fie, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->leftParenPos = fie.leftParenPos; expr->rightParenPos = fie.rightParenPos; expr->pattern = ClonePattern(fie.pattern.get(), visitor); expr->inPos = fie.inPos; expr->inExpression = CloneExpr(fie.inExpression.get(), visitor); expr->wherePos = fie.wherePos; expr->patternGuard = CloneExpr(fie.patternGuard.get(), visitor); expr->patternInDesugarExpr = fie.patternInDesugarExpr; expr->body = CloneExpr(fie.body.get(), visitor); expr->forInKind = fie.forInKind; return expr; } OwnedPtr ASTCloner::CloneMatchExpr(const MatchExpr& me, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->matchMode = me.matchMode; expr->sugarKind = me.sugarKind; expr->selector = CloneExpr(me.selector.get(), visitor); for (auto& i : me.matchCases) { expr->matchCases.push_back(CloneNode(i.get(), visitor)); } for (auto& i : me.matchCaseOthers) { expr->matchCaseOthers.push_back(CloneNode(i.get(), visitor)); } expr->leftParenPos = me.leftParenPos; expr->rightParenPos = me.rightParenPos; expr->leftCurlPos = me.leftCurlPos; expr->rightCurlPos = me.rightCurlPos; return expr; } OwnedPtr ASTCloner::CloneJumpExpr(const JumpExpr& je) { auto expr = MakeOwned(); expr->isBreak = je.isBreak; expr->refLoop = je.refLoop; return expr; } OwnedPtr ASTCloner::CloneTypeConvExpr(const TypeConvExpr& tce, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->type = OwnedPtr(CloneType(tce.type.get(), visitor).release()); expr->expr = CloneExpr(tce.expr.get(), visitor); expr->leftParenPos = tce.leftParenPos; expr->rightParenPos = tce.rightParenPos; return expr; } OwnedPtr ASTCloner::CloneSpawnExpr(const SpawnExpr& se, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->task = CloneExpr(se.task.get(), visitor); expr->arg = CloneExpr(se.arg.get(), visitor); if (se.futureObj) { auto obj = CloneDecl(se.futureObj.get(), visitor); expr->futureObj = OwnedPtr(As(obj.release())); } expr->spawnPos = se.spawnPos; expr->leftParenPos = se.leftParenPos; expr->rightParenPos = se.rightParenPos; return expr; } OwnedPtr ASTCloner::CloneSynchronizedExpr(const SynchronizedExpr& se, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->mutex = CloneExpr(se.mutex.get(), visitor); expr->body = CloneExpr(se.body.get(), visitor); expr->syncPos = se.syncPos; expr->leftParenPos = se.leftParenPos; expr->rightParenPos = se.rightParenPos; return expr; } OwnedPtr ASTCloner::CloneInvalidExpr(const InvalidExpr& ie) { auto expr = MakeOwned(ie.begin); expr->value = ie.value; return expr; } OwnedPtr ASTCloner::CloneTrailingClosureExpr( const TrailingClosureExpr& tc, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->leftLambda = tc.leftLambda; expr->rightLambda = tc.rightLambda; if (tc.expr) { expr->expr = CloneExpr(tc.expr.get(), visitor); } if (tc.lambda) { expr->lambda = CloneExpr(tc.lambda.get(), visitor); } return expr; } OwnedPtr ASTCloner::CloneIsExpr(const IsExpr& ie, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->leftExpr = CloneExpr(ie.leftExpr.get(), visitor); expr->isType = CloneType(ie.isType.get(), visitor); expr->isPos = ie.isPos; return expr; } OwnedPtr ASTCloner::CloneAsExpr(const AsExpr& ae, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->leftExpr = CloneExpr(ae.leftExpr.get(), visitor); expr->asType = CloneType(ae.asType.get(), visitor); expr->asPos = ae.asPos; return expr; } OwnedPtr ASTCloner::CloneOptionalExpr(const OptionalExpr& oe, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->baseExpr = CloneExpr(oe.baseExpr.get(), visitor); expr->questPos = oe.questPos; return expr; } OwnedPtr ASTCloner::CloneOptionalChainExpr(const OptionalChainExpr& oce, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->expr = CloneExpr(oce.expr.get(), visitor); return expr; } OwnedPtr ASTCloner::CloneLetPatternDestructor( const LetPatternDestructor& ldp, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->backarrowPos = ldp.backarrowPos; for (auto& p : ldp.patterns) { expr->patterns.push_back(ClonePattern(p.get(), visitor)); } expr->orPos = ldp.orPos; expr->initializer = CloneExpr(ldp.initializer.get(), visitor); return expr; } OwnedPtr ASTCloner::CloneIfAvailableExpr(const IfAvailableExpr& e, const VisitFunc& visitor) { auto arg = CloneFuncArg(*e.GetArg(), visitor); auto lambda1 = CloneLambdaExpr(*e.GetLambda1(), visitor); auto lambda2 = CloneLambdaExpr(*e.GetLambda2(), visitor); auto expr = MakeOwned(std::move(arg), std::move(lambda1), std::move(lambda2)); return expr; } /** * NOTE: To guarantee the members of Expr is copied, the sub method should not be called outside 'CloneExpr'. */ template OwnedPtr ASTCloner::CloneExpr(Ptr expr, const VisitFunc& visitor) { if (!expr) { return OwnedPtr(); } auto clonedExpr = match(*expr)( // PrimitiveExpr, AdjointExpr are ignored. [&visitor](const IfExpr& ie) { return OwnedPtr(CloneIfExpr(ie, visitor)); }, [](const PrimitiveTypeExpr& pte) { return OwnedPtr(MakeOwned(pte.typeKind)); }, [&visitor](const MacroExpandExpr& mee) { return OwnedPtr(CloneMacroExpandExpr(mee, visitor)); }, [&visitor](const TokenPart& tp) { return OwnedPtr(CloneTokenPart(tp, visitor)); }, [&visitor](const QuoteExpr& qe) { return OwnedPtr(CloneQuoteExpr(qe, visitor)); }, [&visitor](const TryExpr& te) { return OwnedPtr(CloneTryExpr(te, visitor)); }, [&visitor](const ThrowExpr& te) { return OwnedPtr(CloneThrowExpr(te, visitor)); }, [&visitor](const PerformExpr& te) { return OwnedPtr(ClonePerformExpr(te, visitor)); }, [&visitor](const ResumeExpr& re) { return OwnedPtr(CloneResumeExpr(re, visitor)); }, [&visitor](const ReturnExpr& re) { return OwnedPtr(CloneReturnExpr(re, visitor)); }, [&visitor](const WhileExpr& we) { return OwnedPtr(CloneWhileExpr(we, visitor)); }, [&visitor](const DoWhileExpr& dwe) { return OwnedPtr(CloneDoWhileExpr(dwe, visitor)); }, [&visitor](const AssignExpr& ae) { return OwnedPtr(CloneAssignExpr(ae, visitor)); }, [&visitor](const IncOrDecExpr& ide) { return OwnedPtr(CloneIncOrDecExpr(ide, visitor)); }, [&visitor](const UnaryExpr& ue) { return OwnedPtr(CloneUnaryExpr(ue, visitor)); }, [&visitor](const BinaryExpr& be) { return OwnedPtr(CloneBinaryExpr(be, visitor)); }, [&visitor](const RangeExpr& re) { return OwnedPtr(CloneRangeExpr(re, visitor)); }, [&visitor](const SubscriptExpr& se) { return OwnedPtr(CloneSubscriptExpr(se, visitor)); }, [&visitor](const MemberAccess& ma) { return OwnedPtr(CloneMemberAccess(ma, visitor)); }, [&visitor](const CallExpr& ce) { return OwnedPtr(CloneCallExpr(ce, visitor)); }, [&visitor](const ParenExpr& pe) { return OwnedPtr(CloneParenExpr(pe, visitor)); }, [&visitor](const LambdaExpr& le) { return OwnedPtr(CloneLambdaExpr(le, visitor)); }, [&visitor](const LitConstExpr& lce) { return OwnedPtr(CloneLitConstExpr(lce, visitor)); }, [&visitor](const ArrayLit& al) { return OwnedPtr(CloneArrayLit(al, visitor)); }, [&visitor](const ArrayExpr& asl) { return OwnedPtr(CloneArrayExpr(asl, visitor)); }, [&visitor](const PointerExpr& ptre) { return OwnedPtr(ClonePointerExpr(ptre, visitor)); }, [&visitor](const TupleLit& tl) { return OwnedPtr(CloneTupleLit(tl, visitor)); }, [&visitor](const RefExpr& re) { return OwnedPtr(CloneRefExpr(re, visitor)); }, [&visitor](const ForInExpr& fie) { return OwnedPtr(CloneForInExpr(fie, visitor)); }, [&visitor](const MatchExpr& me) { return OwnedPtr(CloneMatchExpr(me, visitor)); }, [](const JumpExpr& je) { return OwnedPtr(CloneJumpExpr(je)); }, [&visitor](const TypeConvExpr& e) { return OwnedPtr(CloneTypeConvExpr(e, visitor)); }, [&visitor](const SpawnExpr& se) { return OwnedPtr(CloneSpawnExpr(se, visitor)); }, [&visitor](const SynchronizedExpr& se) { return OwnedPtr(CloneSynchronizedExpr(se, visitor)); }, [](const InvalidExpr& ie) { return OwnedPtr(CloneInvalidExpr(ie)); }, [&visitor](const Block& b) { return OwnedPtr(CloneBlock(b, visitor)); }, [&visitor](const InterpolationExpr& ie) { return OwnedPtr(CloneInterpolationExpr(ie, visitor)); }, [&visitor](const StrInterpolationExpr& sie) { return OwnedPtr(CloneStrInterpolationExpr(sie, visitor)); }, [&visitor](const TrailingClosureExpr& tc) { return OwnedPtr(CloneTrailingClosureExpr(tc, visitor)); }, [&visitor](const IsExpr& ie) { return OwnedPtr(CloneIsExpr(ie, visitor)); }, [&visitor](const AsExpr& ae) { return OwnedPtr(CloneAsExpr(ae, visitor)); }, [&visitor](const OptionalExpr& oe) { return OwnedPtr(CloneOptionalExpr(oe, visitor)); }, [&visitor](const OptionalChainExpr& oe) { return OwnedPtr(CloneOptionalChainExpr(oe, visitor)); }, [](const WildcardExpr& /* we */) { return OwnedPtr(MakeOwned()); }, [&visitor](const LetPatternDestructor& ld) { return OwnedPtr(CloneLetPatternDestructor(ld, visitor)); }, [&visitor](const IfAvailableExpr& ie) { return OwnedPtr(CloneIfAvailableExpr(ie, visitor)); }, [&expr]() { // Invalid and ignored cases. auto invalidExpr = MakeOwned(expr->begin); return OwnedPtr(invalidExpr.release()); }); CJC_ASSERT(clonedExpr); if (clonedExpr->astKind != ASTKind::INVALID_EXPR) { CopyNodeField(clonedExpr.get(), *expr); // Clone field in Expr. clonedExpr->isConst = expr->isConst; clonedExpr->isBaseFunc = expr->isBaseFunc; clonedExpr->isInFlowExpr = expr->isInFlowExpr; clonedExpr->constNumValue = expr->constNumValue; clonedExpr->hasSemi = expr->hasSemi; clonedExpr->mapExpr = (expr->mapExpr == expr) ? clonedExpr.get() : expr->mapExpr; clonedExpr->sourceExpr = expr->sourceExpr; clonedExpr->overflowStrategy = expr->overflowStrategy; if (expr->desugarExpr && !clonedExpr->desugarExpr) { clonedExpr->desugarExpr = CloneExpr(expr->desugarExpr.get(), visitor); } clonedExpr->EnableAttr(Attribute::COMPILER_ADD); visitor(*expr, *clonedExpr); } auto result = DynamicCast(clonedExpr.release()); CJC_NULLPTR_CHECK(result); return OwnedPtr(result); } /** * NOTE: To guarantee the members of Decl is copied, the sub method should not be called outside 'CloneDecl'. */ OwnedPtr ASTCloner::CloneDecl(Ptr decl, const VisitFunc& visitor) { if (!decl) { return OwnedPtr(); } auto ret = match(*decl)([](const GenericParamDecl& e) { return CloneGenericParamDecl(e); }, [&visitor](const PropDecl& e) { return ClonePropDecl(e, visitor); }, [&visitor](const VarDecl& e) { return CloneVarDecl(e, visitor); }, [&visitor](const FuncDecl& e) { return CloneFuncDecl(e, visitor); }, [&visitor](const StructDecl& e) { return CloneStructDecl(e, visitor); }, [&visitor](const ClassDecl& e) { return CloneClassDecl(e, visitor); }, [&visitor](const InterfaceDecl& e) { return CloneInterfaceDecl(e, visitor); }, [&visitor](const EnumDecl& e) { return CloneEnumDecl(e, visitor); }, [&visitor](const PrimaryCtorDecl& e) { return ClonePrimaryCtorDecl(e, visitor); }, [&visitor](const ExtendDecl& e) { return CloneExtendDecl(e, visitor); }, [](const MacroExpandDecl& e) { return CloneMacroExpandDecl(e); }, [&visitor](const VarWithPatternDecl& e) { return CloneVarWithPatternDecl(e, visitor); }, [&visitor](const TypeAliasDecl& e) { return CloneTypeAliasDecl(e, visitor); }, // MainDecl and MacroDecl are ignored. Since they will be desugared before semantic typecheck. [](const MacroDecl& e) { return OwnedPtr(MakeOwned(e.begin).release()); }, [](const MainDecl& e) { return OwnedPtr(MakeOwned(e.begin).release()); }, [](const InvalidDecl& e) { return OwnedPtr(MakeOwned(e.begin).release()); }, [&decl]() { // Invalid and ignored cases. auto invalidDecl = MakeOwned(decl->begin); return OwnedPtr(invalidDecl.release()); }); CJC_ASSERT(ret); CopyNodeField(ret.get(), *decl); // Clone field in Decl. ret->modifiers.insert(decl->modifiers.begin(), decl->modifiers.end()); ret->identifier = decl->identifier; ret->identifierForLsp = decl->identifierForLsp; ret->keywordPos = decl->keywordPos; ret->moduleName = decl->moduleName; ret->fullPackageName = decl->fullPackageName; ret->outerDecl = decl->outerDecl; ret->platformImplementation = decl->platformImplementation; ret->checkFlag = decl->checkFlag; ret->captureIndex = decl->captureIndex; ret->linkage = decl->linkage; for (auto& anno : decl->annotations) { ret->annotations.emplace_back(CloneNode(anno.get(), visitor)); } ret->annotationsArray = CloneNode(decl->annotationsArray.get(), visitor); visitor(*decl, *ret); return ret; } OwnedPtr ASTCloner::CloneConstPattern(const ConstPattern& cp, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->literal = CloneExpr(cp.literal.get(), visitor); ret->operatorCallExpr = CloneExpr(cp.operatorCallExpr.get(), visitor); return ret; } OwnedPtr ASTCloner::CloneVarPattern(const VarPattern& vp, const VisitFunc& visitor) { OwnedPtr ret; if (vp.varDecl) { ret = MakeOwned(vp.varDecl->identifier, vp.begin); auto d = CloneDecl(vp.varDecl.get(), visitor); ret->varDecl.reset(As(d.release())); } else { ret = MakeOwned(); } ret->desugarExpr = CloneExpr(vp.desugarExpr.get(), visitor); return ret; } OwnedPtr ASTCloner::CloneTuplePattern(const TuplePattern& tp, const VisitFunc& visitor) { auto ret = MakeOwned(); for (auto& i : tp.patterns) { ret->patterns.push_back(ClonePattern(i.get(), visitor)); } ret->leftBracePos = tp.leftBracePos; ret->rightBracePos = tp.rightBracePos; ret->commaPosVector = tp.commaPosVector; return ret; } OwnedPtr ASTCloner::CloneTypePattern(const TypePattern& tp, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->pattern = ClonePattern(tp.pattern.get(), visitor); ret->type = CloneType(tp.type.get(), visitor); ret->colonPos = tp.colonPos; if (tp.desugarExpr != nullptr) { ret->desugarExpr = CloneExpr(tp.desugarExpr.get(), visitor); } if (tp.desugarVarPattern != nullptr) { ret->desugarVarPattern = ASTCloner::Clone(tp.desugarVarPattern.get(), visitor); } ret->needRuntimeTypeCheck = tp.needRuntimeTypeCheck; ret->matchBeforeRuntime = tp.matchBeforeRuntime; return ret; } OwnedPtr ASTCloner::CloneEnumPattern(const EnumPattern& ep, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->constructor = CloneExpr(ep.constructor.get(), visitor); for (auto& p : ep.patterns) { ret->patterns.emplace_back(ClonePattern(p.get(), visitor)); } ret->leftParenPos = ep.leftParenPos; ret->rightParenPos = ep.rightParenPos; ret->commaPosVector = ep.commaPosVector; return ret; } OwnedPtr ASTCloner::CloneExceptTypePattern( const ExceptTypePattern& etp, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->pattern = ClonePattern(etp.pattern.get(), visitor); for (auto& i : etp.types) { ret->types.push_back(CloneType(i.get(), visitor)); } ret->patternPos = etp.patternPos; ret->colonPos = etp.colonPos; ret->bitOrPosVector = etp.bitOrPosVector; return ret; } OwnedPtr ASTCloner::CloneCommandTypePattern( const CommandTypePattern& ctp, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->pattern = ClonePattern(ctp.pattern.get(), visitor); for (auto& i : ctp.types) { ret->types.push_back(CloneType(i.get(), visitor)); } ret->patternPos = ctp.patternPos; ret->colonPos = ctp.colonPos; ret->bitOrPosVector = ctp.bitOrPosVector; return ret; } OwnedPtr ASTCloner::CloneVarOrEnumPattern( const VarOrEnumPattern& vep, const VisitFunc& visitor) { auto ret = MakeOwned(vep.identifier); if (vep.pattern) { ret->pattern = ClonePattern(vep.pattern.get(), visitor); } return ret; } /** * NOTE: To guarantee the members of Pattern is copied, the sub method should not be called outside 'ClonePattern'. */ OwnedPtr ASTCloner::ClonePattern(Ptr pattern, const VisitFunc& visitor) { if (!pattern) { return OwnedPtr(); } auto ret = match(*pattern)( [&visitor](const ConstPattern& e) { return OwnedPtr(CloneConstPattern(e, visitor)); }, [](const WildcardPattern& e) { return OwnedPtr(MakeOwned(e)); }, [&visitor](const VarPattern& e) { return OwnedPtr(CloneVarPattern(e, visitor)); }, [&visitor](const TuplePattern& e) { return OwnedPtr(CloneTuplePattern(e, visitor)); }, [&visitor](const TypePattern& e) { return OwnedPtr(CloneTypePattern(e, visitor)); }, [&visitor](const EnumPattern& e) { return OwnedPtr(CloneEnumPattern(e, visitor)); }, [&visitor](const ExceptTypePattern& e) { return OwnedPtr(CloneExceptTypePattern(e, visitor)); }, [&visitor](const CommandTypePattern& e) { return OwnedPtr(CloneCommandTypePattern(e, visitor)); }, [&visitor](const VarOrEnumPattern& e) { return OwnedPtr(CloneVarOrEnumPattern(e, visitor)); }, [](const InvalidPattern& e) { return OwnedPtr(MakeOwned(e)); }, []() { return OwnedPtr(MakeOwned()); }); CJC_ASSERT(ret && ret->astKind == pattern->astKind); CopyNodeField(ret.get(), *pattern); // Clone field in Pattern. ret->ctxExpr = pattern->ctxExpr; ret->EnableAttr(Attribute::COMPILER_ADD); visitor(*pattern, *ret); return ret; } OwnedPtr ASTCloner::CloneBlock(const Block& block, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), block); // Clone field in Block. ret->unsafePos = block.unsafePos; ret->leftCurlPos = block.leftCurlPos; for (auto& it : block.body) { ret->body.push_back(CloneNode(it.get(), visitor)); } ret->rightCurlPos = block.rightCurlPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneClassBody(const ClassBody& cb, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), cb); // Clone field in ClassBody. ret->leftCurlPos = cb.leftCurlPos; for (auto& it : cb.decls) { ret->decls.push_back(CloneDecl(it.get(), visitor)); } ret->rightCurlPos = cb.rightCurlPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneStructBody(const StructBody& sb, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), sb); ret->leftCurlPos = sb.leftCurlPos; for (auto& it : sb.decls) { ret->decls.push_back(CloneDecl(it.get(), visitor)); } ret->rightCurlPos = sb.rightCurlPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneInterfaceBody(const InterfaceBody& ib, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), ib); // Clone field in InterfaceBody. ret->leftCurlPos = ib.leftCurlPos; for (auto& it : ib.decls) { ret->decls.push_back(CloneDecl(it.get(), visitor)); } ret->rightCurlPos = ib.rightCurlPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneGenericConstraint( const GenericConstraint& gc, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), gc); // Clone field in GenericConstraint. ret->type.reset(As(CloneType(gc.type.get(), visitor).release())); for (auto& upperBound : gc.upperBounds) { ret->upperBounds.push_back(CloneType(upperBound.get(), visitor)); } ret->wherePos = gc.wherePos; ret->operatorPos = gc.operatorPos; ret->bitAndPos = gc.bitAndPos; ret->commaPos = gc.commaPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneFuncBody(const FuncBody& fb, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), fb); // Clone field in FuncBody. for (auto& it : fb.paramLists) { ret->paramLists.push_back(CloneNode(it.get(), visitor)); } ret->doubleArrowPos = fb.doubleArrowPos; ret->colonPos = fb.colonPos; ret->retType = CloneType(fb.retType.get(), visitor); ret->body = CloneExpr(fb.body.get(), visitor); if (fb.generic) { ret->generic = CloneGeneric(*fb.generic, visitor); } for (auto& it : fb.capturedVars) { ret->capturedVars.insert(it); } ret->captureKind = fb.captureKind; ret->outerFunc = fb.outerFunc; ret->parentClassLike = fb.parentClassLike; ret->parentStruct = fb.parentStruct; ret->parentEnum = fb.parentEnum; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneFuncParamList(const FuncParamList& fpl, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), fpl); // Clone field in FuncParamList. ret->leftParenPos = fpl.leftParenPos; for (auto& it : fpl.params) { auto funcParam = MakeOwned(); auto d = CloneDecl(it.get(), visitor); funcParam.reset(As(d.release())); ret->params.push_back(std::move(funcParam)); } ret->variadicArgIndex = fpl.variadicArgIndex; ret->hasVariableLenArg = fpl.hasVariableLenArg; ret->rightParenPos = fpl.rightParenPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneFuncArg(const FuncArg& fa, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), fa); // Clone field in FuncArg. ret->name = fa.name; ret->withInout = fa.withInout; ret->colonPos = fa.colonPos; ret->expr = CloneExpr(fa.expr.get(), visitor); ret->commaPos = fa.commaPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr ASTCloner::CloneAnnotation(const Annotation& annotation, const VisitFunc& visitor) { auto ret = MakeOwned(annotation.identifier, annotation.kind, annotation.begin); CopyNodeField(ret.get(), annotation); ret->kind = annotation.kind; ret->definedPackage = annotation.definedPackage; ret->identifier = annotation.identifier; ret->attrs = annotation.attrs; ret->attrCommas = annotation.attrCommas; ret->adAnnotation = annotation.adAnnotation; ret->rsquarePos = annotation.rsquarePos; ret->lsquarePos = annotation.lsquarePos; for (auto& arg : annotation.args) { ret->args.emplace_back(CloneNode(arg.get(), visitor)); } ret->condExpr = CloneExpr(annotation.condExpr.get()); ret->baseExpr = CloneExpr(annotation.baseExpr.get()); return ret; } OwnedPtr ASTCloner::CloneImportSpec(const ImportSpec& is, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), is); if (is.modifier) { // Clone Modifier ret->modifier = MakeOwned(is.modifier->modifier, is.modifier->begin); CopyNodeField(ret->modifier.get(), *is.modifier); ret->modifier->isExplicit = is.modifier->isExplicit; } std::function cloneContent = [&cloneContent](const ImportContent& src, ImportContent& dst) { CopyNodeField(&dst, src); dst.kind = src.kind; dst.prefixPaths = src.prefixPaths; dst.prefixPoses = src.prefixPoses; dst.prefixDotPoses = src.prefixDotPoses; dst.identifier = src.identifier; dst.asPos = src.asPos; dst.aliasName = src.aliasName; dst.leftCurlPos = src.leftCurlPos; if (!src.items.empty()) { dst.items.resize(src.items.size()); for (size_t i = 0; i < src.items.size(); ++i) { cloneContent(src.items[i], dst.items[i]); } } dst.commaPoses = src.commaPoses; dst.rightCurlPos = src.rightCurlPos; }; cloneContent(is.content, ret->content); for (auto& it : is.annotations) { ret->annotations.emplace_back(CloneNode(it.get(), visitor)); } return ret; } OwnedPtr ASTCloner::CloneMatchCase(const MatchCase& mc, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), mc); // Clone field in MatchCase. for (auto& pattern : mc.patterns) { ret->patterns.emplace_back(ClonePattern(pattern.get(), visitor)); } ret->patternGuard = CloneExpr(mc.patternGuard.get(), visitor); ret->exprOrDecls = CloneExpr(mc.exprOrDecls.get(), visitor); ret->wherePos = mc.wherePos; ret->arrowPos = mc.arrowPos; ret->EnableAttr(Attribute::COMPILER_ADD); ret->bitOrPosVector = mc.bitOrPosVector; return ret; } OwnedPtr ASTCloner::CloneMatchCaseOther(const MatchCaseOther& mco, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), mco); // Clone field in MatchCaseOther. ret->matchExpr = CloneExpr(mco.matchExpr.get(), visitor); ret->exprOrDecls = CloneExpr(mco.exprOrDecls.get(), visitor); ret->arrowPos = mco.arrowPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } // Clone Node. template OwnedPtr ASTCloner::CloneNode(Ptr node, const VisitFunc& visitor) { if (!node) { return OwnedPtr(); } auto clonedNode = match(*node)( // NOTE: Package, File, PackageSpec, ImportSpec, Modifier are ignored. // EnumBody struct is empty, no need to be cloned [&visitor](Type& type) { return OwnedPtr(CloneType(Ptr(&type), visitor)); }, [&visitor](Expr& expr) { return OwnedPtr(CloneExpr(Ptr(&expr), visitor)); }, [&visitor](Decl& decl) { return OwnedPtr(CloneDecl(Ptr(&decl), visitor)); }, [&visitor](Pattern& pattern) { return OwnedPtr(ClonePattern(Ptr(&pattern), visitor)); }, [&visitor](const ClassBody& cb) { return OwnedPtr(CloneClassBody(cb, visitor)); }, [&visitor](const StructBody& rb) { return OwnedPtr(CloneStructBody(rb, visitor)); }, [&visitor](const InterfaceBody& ib) { return OwnedPtr(CloneInterfaceBody(ib, visitor)); }, [&visitor](const GenericConstraint& gc) { return OwnedPtr(CloneGenericConstraint(gc, visitor)); }, [&visitor](const FuncBody& fb) { return OwnedPtr(CloneFuncBody(fb, visitor)); }, [&visitor](const FuncParamList& fpl) { return OwnedPtr(CloneFuncParamList(fpl, visitor)); }, [&visitor](const FuncArg& fa) { return OwnedPtr(CloneFuncArg(fa, visitor)); }, [&visitor](const MatchCase& mc) { return OwnedPtr(CloneMatchCase(mc, visitor)); }, [&visitor](const MatchCaseOther& mco) { return OwnedPtr(CloneMatchCaseOther(mco, visitor)); }, [&visitor](const Annotation& ann) { return OwnedPtr(CloneAnnotation(ann, visitor)); }, [](const DummyBody&) { return OwnedPtr(OwnedPtr()); }, [&visitor](const ImportSpec& is) { return OwnedPtr(CloneImportSpec(is, visitor)); }, []() { // Invalid cases. return OwnedPtr(MakeOwned()); }); CJC_ASSERT(clonedNode); visitor(*node, *clonedNode); clonedNode->EnableAttr(Attribute::COMPILER_ADD); auto result = DynamicCast(clonedNode.release()); return OwnedPtr(result); } OwnedPtr ASTCloner::CloneWithRearrange(Ptr node, const VisitFunc& visitor) { VisitFunc collectMap = [this, visitor](Node& from, Node& target) { // Collect decl to decl map. if (auto decl = DynamicCast(&from); decl) { source2cloned[decl] = ⌖ auto& targetDecl = static_cast(target); TargetAddrMapInsert(decl->outerDecl, targetDecl.outerDecl); TargetAddrMapInsert(decl->genericDecl, targetDecl.genericDecl); // unorder_set> users field in Decl is ignored. } if (auto expr = DynamicCast(&from); expr) { source2cloned[expr] = ⌖ auto& targetExpr = static_cast(target); TargetAddrMapInsert(expr->sourceExpr, targetExpr.sourceExpr); TargetAddrMapInsert(expr->mapExpr, targetExpr.mapExpr); } if (auto funcBody = DynamicCast(&from); funcBody) { source2cloned[funcBody] = ⌖ auto& targetFuncBody = static_cast(target); TargetAddrMapInsert(funcBody->funcDecl, targetFuncBody.funcDecl); TargetAddrMapInsert(funcBody->outerFunc, targetFuncBody.outerFunc); TargetAddrMapInsert(funcBody->parentClassLike, targetFuncBody.parentClassLike); TargetAddrMapInsert(funcBody->parentStruct, targetFuncBody.parentStruct); TargetAddrMapInsert(funcBody->parentEnum, targetFuncBody.parentEnum); } if (auto pattern = DynamicCast(&from); pattern) { source2cloned[pattern] = ⌖ auto& targetPattern = static_cast(target); TargetAddrMapInsert(pattern->ctxExpr, targetPattern.ctxExpr); } // Package and File are ignored since usually we do not need to clone them. // Collect target variable address to target variable address map. // For sub-class of Expr, there is only 1 layer under it, we just enumerate them. // For sub-class of Decl, there are 2 layers under it. FuncParam extends Vardecl // which extends Decl. // Macro related ASTs also are ignored switch (from.astKind) { // sub-class of Expr struct case ASTKind::REF_EXPR: { auto& reFrom = static_cast(from); auto& reTarget = static_cast(target); TargetAddrMapInsert(reFrom.ref.target, reTarget.ref.target); TargetAddrMapInsert(reFrom.callOrPattern, reTarget.callOrPattern); break; } case ASTKind::ARRAY_EXPR: { auto& aeFrom = static_cast(from); auto& aeTarget = static_cast(target); TargetAddrMapInsert(aeFrom.initFunc, aeTarget.initFunc); break; } case ASTKind::ARRAY_LIT: { auto& aeFrom = static_cast(from); auto& aeTarget = static_cast(target); TargetAddrMapInsert(aeFrom.initFunc, aeTarget.initFunc); break; } case ASTKind::MEMBER_ACCESS: { auto& maFrom = static_cast(from); if (auto base = DynamicCast(maFrom.baseExpr.get()); !base || !base->isThis) { break; // Do not rearrange target if current is not accessing 'this'. } auto& maTarget = static_cast(target); TargetAddrMapInsert(maFrom.target, maTarget.target); TargetAddrMapInsert(maFrom.callOrPattern, maTarget.callOrPattern); for (size_t i = 0; i < maFrom.targets.size(); i++) { TargetAddrMapInsert(maFrom.targets[i], maTarget.targets[i]); } // NOTE: foundUpperBoundMap is ignored break; } case ASTKind::FOR_IN_EXPR: { auto& fiFrom = static_cast(from); auto& fiTarget = static_cast(target); TargetAddrMapInsert(fiFrom.patternInDesugarExpr, fiTarget.patternInDesugarExpr); break; } case ASTKind::CALL_EXPR: { auto& ceFrom = static_cast(from); if (auto ma = DynamicCast(ceFrom.baseFunc.get())) { if (auto base = DynamicCast(ma->baseExpr.get()); !base || !base->isThis) { // Do not rearrange target if current is not call of refExpr or memberAccess of 'this'. break; } } auto& ceTarget = static_cast(target); TargetAddrMapInsert(ceFrom.resolvedFunction, ceTarget.resolvedFunction); break; } case ASTKind::RETURN_EXPR: { auto& rtFrom = static_cast(from); auto& rtTarget = static_cast(target); TargetAddrMapInsert(rtFrom.refFuncBody, rtTarget.refFuncBody); break; } case ASTKind::JUMP_EXPR: { auto& jeFrom = static_cast(from); auto& jeTarget = static_cast(target); TargetAddrMapInsert(jeFrom.refLoop, jeTarget.refLoop); break; } case ASTKind::REF_TYPE: { auto& reFrom = static_cast(from); auto& reTarget = static_cast(target); TargetAddrMapInsert(reFrom.ref.target, reTarget.ref.target); break; } case ASTKind::QUALIFIED_TYPE: { auto& qtFrom = static_cast(from); auto& qtTarget = static_cast(target); TargetAddrMapInsert(qtFrom.target, qtTarget.target); break; } // case ASTKind::FUNC_BODY handled in if case case ASTKind::FUNC_DECL: { auto& fdFrom = static_cast(from); auto& fdTarget = static_cast(target); TargetAddrMapInsert(fdFrom.ownerFunc, fdTarget.ownerFunc); TargetAddrMapInsert(fdFrom.propDecl, fdTarget.propDecl); break; } case ASTKind::VAR_DECL: { auto& vdFrom = static_cast(from); auto& vdTarget = static_cast(target); TargetAddrMapInsert(vdFrom.parentPattern, vdTarget.parentPattern); break; } default: break; } visitor(from, target); }; OwnedPtr targetNode = CloneNode(node, collectMap); // Rearrange pointer to node pointer's target from source node pointer to cloned node pointer. for (auto& [s, t] : targetAddr2targetAddr) { if (source2cloned.find(*s) != source2cloned.end()) { *t = source2cloned[*s]; } } return targetNode; } } // namespace Cangjie::AST cangjie_compiler-1.0.7/src/AST/Comment.cpp000066400000000000000000000054571510705540100203460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Comment related structs. */ #include "cangjie/AST/Comment.h" #include "cangjie/Basic/StringConvertor.h" namespace Cangjie { std::string AST::CommentGroups::ToString() const { if (IsEmpty()) { return "{}"; } std::string str{"{"}; bool needComma = false; if (!leadingComments.empty()) { str += "\"leadingComments\":["; for (auto cg : leadingComments) { if (needComma) { str += ", "; } str += cg.ToString(); needComma = true; } str += "]"; } if (!innerComments.empty()) { if (needComma) { str += ", "; } needComma = false; str += "\"innerComments\":["; for (auto cg : innerComments) { if (needComma) { str += ","; } str += cg.ToString(); needComma = true; } str += "]"; needComma = true; } if (!trailingComments.empty()) { if (needComma) { str += ", "; } needComma = false; str += "\"trailingComments\":["; for (auto cg : trailingComments) { if (needComma) { str += ", "; } str += cg.ToString(); needComma = true; } str += "]"; needComma = true; } str += "}"; return str; } std::string AST::CommentGroup::ToString() const { if (IsEmpty()) { return "{\"cms\":[]}"; } std::string str{"{\"cms\":["}; bool needComma = false; for (auto c : cms) { if (needComma) { str += ", "; } str += c.ToString(); needComma = true; } str += "]}"; return str; } std::string AST::Comment::ToString() const { std::string str; str += "{\"style\":"; switch (style) { case CommentStyle::LEAD_LINE: str += "\"leadLine\""; break; case CommentStyle::TRAIL_CODE: str += "\"trailCode\""; break; case CommentStyle::OTHER: str += "\"other\""; break; } str += ", \"kind\":"; switch (kind) { case CommentKind::LINE: str += "\"line\""; break; case CommentKind::BLOCK: str += "\"block\""; break; case CommentKind::DOCUMENT: str += "\"doc\""; break; } str += ", \"info\":\"" + StringConvertor::EscapeToJsonString(info.Value()) + "\"}"; return str; } }cangjie_compiler-1.0.7/src/AST/Create.cpp000066400000000000000000000550311510705540100201400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements AST create apis. */ #include "cangjie/AST/Create.h" #include #include #include #include #include "cangjie/AST/Clone.h" #include "cangjie/AST/Match.h" #include "cangjie/Basic/Match.h" #include "cangjie/Basic/Position.h" #include "cangjie/Basic/Print.h" #include "cangjie/Basic/StringConvertor.h" namespace Cangjie::AST { using namespace Meta; void CopyNodeScopeInfo(Ptr ret, Ptr e) { if (!ret || !e) { return; } ret->curFile = e->curFile; ret->scopeName = e->scopeName; ret->scopeLevel = e->scopeLevel; } void CopyFileID(Ptr ret, Ptr e) { if (!ret || !e) { return; } ret->begin.fileID = e->begin.fileID; ret->end.fileID = e->end.fileID; } void CopyNodeWithFileID(Ptr ret, Ptr e) { if (!ret || !e) { return; } CopyNodeScopeInfo(ret, e); CopyFileID(ret, e); } OwnedPtr CreateUnitExpr(Ptr ty) { OwnedPtr ret = MakeOwned(); ret->kind = LitConstKind::UNIT; ret->stringValue = "()"; if (Ty::IsTyCorrect(ty)) { ret->ty = ty; } ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr CreateUnaryExpr(OwnedPtr expr, TokenKind op) { OwnedPtr ret = MakeOwned(); CJC_NULLPTR_CHECK(expr); CJC_ASSERT(op == TokenKind::ADD || op == TokenKind::SUB || op == TokenKind::NOT); ret->expr = std::move(expr); ret->op = op; ret->ty = ret->expr->ty; CopyNodeScopeInfo(ret.get(), ret->expr.get()); ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr CreateBinaryExpr( OwnedPtr leftExpr, OwnedPtr rightExpr, TokenKind op) { OwnedPtr ret = MakeOwned(); CJC_NULLPTR_CHECK(leftExpr); CJC_NULLPTR_CHECK(rightExpr); CJC_ASSERT((op >= TokenKind::EXP && op <= TokenKind::SUB) || (op >= TokenKind::AND && op <= TokenKind::COMPOSITION) || (op >= TokenKind::BITAND && op <= TokenKind::BITXOR) || (op >= TokenKind::LSHIFT && op <= TokenKind::RSHIFT) || (op >= TokenKind::LT && op <= TokenKind::EQUAL)); ret->leftExpr = std::move(leftExpr); ret->rightExpr = std::move(rightExpr); ret->ty = ret->leftExpr->ty; ret->op = op; CopyNodeScopeInfo(ret.get(), ret->leftExpr.get()); ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr CreateBreakExpr(Expr& refLoop) { OwnedPtr ret = MakeOwned(); ret->isBreak = true; ret->refLoop = &refLoop; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr CreateBoolLit(bool isTrue) { OwnedPtr ret = MakeOwned(); ret->kind = LitConstKind::BOOL; ret->stringValue = isTrue ? "true" : "false"; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr CreateReturnExpr(OwnedPtr expr, Ptr refFuncBody) { auto ret = MakeOwned(); CJC_NULLPTR_CHECK(expr); ret->expr = std::move(expr); CopyNodeScopeInfo(ret.get(), ret->expr.get()); ret->EnableAttr(Attribute::COMPILER_ADD); if (refFuncBody) { ret->refFuncBody = refFuncBody; } return ret; } OwnedPtr CreateForInExpr( OwnedPtr&& pattern, OwnedPtr&& inExpression, OwnedPtr&& body) { auto ret = MakeOwned(); ret->pattern = std::move(pattern); ret->inExpression = std::move(inExpression); ret->body = std::move(body); ret->EnableAttr(Attribute::COMPILER_ADD); CJC_NULLPTR_CHECK(ret->pattern); CJC_NULLPTR_CHECK(ret->inExpression); CJC_NULLPTR_CHECK(ret->body); return ret; } OwnedPtr CreateAssignExpr(OwnedPtr leftValue, OwnedPtr rightExpr, Ptr ty) { auto ret = MakeOwned(); CJC_NULLPTR_CHECK(leftValue); CJC_NULLPTR_CHECK(rightExpr); ret->leftValue = std::move(leftValue); ret->rightExpr = std::move(rightExpr); ret->op = TokenKind::ASSIGN; ret->EnableAttr(Attribute::COMPILER_ADD); CopyNodeScopeInfo(ret.get(), ret->leftValue.get()); if (Ty::IsTyCorrect(ty)) { ret->ty = ty; } return ret; } OwnedPtr CreateFuncArg(OwnedPtr expr, const std::string& argName, Ptr ty) { auto ret = MakeOwned(); CJC_NULLPTR_CHECK(expr); CopyBasicInfo(expr.get(), ret.get()); if (expr->astKind == ASTKind::BLOCK) { // NOTE: Block should be attached to 'desugarExpr'. // Create a dummy desugar block expr, in order to create a CHIR `Goto` node. auto re = CreateRefExpr("$dummy"); re->ty = expr->ty; re->desugarExpr = std::move(expr); ret->expr = std::move(re); } else { ret->expr = std::move(expr); } ret->name = argName; ret->EnableAttr(Attribute::COMPILER_ADD); ret->ty = Ty::IsTyCorrect(ty) ? ty : ret->expr->ty; return ret; } OwnedPtr CreateRefTypeInCore(const std::string& name) { OwnedPtr ret = MakeOwned(); ret->ref.identifier = name; ret->EnableAttr(Attribute::IN_CORE); return ret; } OwnedPtr CreateFuncArgForOptional(const FuncParam& param) { auto arg = MakeOwned(); arg->name = param.identifier; arg->begin = param.begin; arg->expr = ASTCloner::Clone(param.assignment.get()); arg->EnableAttr(Attribute::COMPILER_ADD); arg->ty = param.ty; arg->end = param.end; CopyNodeScopeInfo(arg.get(), ¶m); return arg; } OwnedPtr CreateFuncParamForOptional(const FuncParam& param) { auto pa = MakeOwned(); pa->identifier = param.identifier; pa->begin = param.begin; pa->type = ASTCloner::Clone(param.type.get()); pa->ty = param.ty; pa->curFile = param.curFile; pa->end = param.end; pa->EnableAttr(Attribute::COMPILER_ADD); CopyNodeScopeInfo(pa.get(), ¶m); return pa; } OwnedPtr CreateFuncDecl(const std::string& funcName, OwnedPtr body, Ptr ty) { auto ret = MakeOwned(); ret->identifier = funcName; ret->funcBody = std::move(body); if (Ty::IsTyCorrect(ty)) { ret->ty = ty; } else if (ret->funcBody) { ret->funcBody->funcDecl = ret.get(); CopyNodeScopeInfo(ret.get(), ret->funcBody.get()); ret->ty = ret->funcBody->ty; } ret->toBeCompiled = true; // For incremental compilation. ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr CreateFuncBody(std::vector> paramLists, OwnedPtr retType, OwnedPtr body, Ptr ty) { auto ret = MakeOwned(); ret->paramLists = std::move(paramLists); ret->retType = std::move(retType); ret->body = std::move(body); ret->EnableAttr(Attribute::COMPILER_ADD); if (ret->body) { CopyNodeScopeInfo(ret.get(), ret->body.get()); } if (Ty::IsTyCorrect(ty)) { ret->ty = ty; } return ret; } OwnedPtr CreateFuncParam( const std::string& paramName, OwnedPtr paramType, OwnedPtr paramValue, Ptr ty) { auto ret = MakeOwned(); ret->identifier = paramName; ret->type = std::move(paramType); ret->assignment = std::move(paramValue); ret->EnableAttr(Attribute::COMPILER_ADD); if (ret->assignment) { CopyNodeScopeInfo(ret.get(), ret->assignment.get()); } if (Ty::IsTyCorrect(ty)) { ret->ty = ty; } else if (ret->type) { ret->ty = ret->type->ty; } return ret; } OwnedPtr CreateFuncParamList(std::vector> params, const VisitFunc& visitor) { auto ret = MakeOwned(); params.erase(std::remove_if(params.begin(), params.end(), [](auto& e) { return !e; }), params.end()); for (auto& it : params) { auto funcParam = MakeOwned(); auto d = ASTCloner::Clone(it, visitor); funcParam.reset(As(d.release())); CopyNodeScopeInfo(ret.get(), funcParam.get()); ret->params.push_back(std::move(funcParam)); } ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr CreateBlock(std::vector> nodes, Ptr ty) { auto ret = MakeOwned(); nodes.erase(std::remove_if(nodes.begin(), nodes.end(), [](auto& e) { return e == nullptr; }), nodes.end()); for (auto& it : nodes) { CJC_ASSERT(it->IsDecl() || it->IsExpr()); if (it->IsDecl()) { ret->body.push_back(std::move(it)); } else if (it->IsExpr()) { ret->body.push_back(std::move(it)); } } ret->EnableAttr(Attribute::COMPILER_ADD); if (Ty::IsTyCorrect(ty)) { ret->ty = ty; } return ret; } OwnedPtr CreateVarDecl(const std::string& varName, OwnedPtr initializer, Ptr type) { auto ret = MakeOwned(); ret->identifier = varName; ret->type = ASTCloner::Clone(type); ret->initializer = std::move(initializer); ret->EnableAttr(Attribute::COMPILER_ADD, Attribute::IMPLICIT_ADD); if (ret->initializer) { ret->ty = ret->initializer->ty; CopyNodeScopeInfo(ret.get(), ret->initializer.get()); } ret->toBeCompiled = true; // For incremental compilation. return ret; } OwnedPtr CreateTmpVarDecl(Ptr type, Ptr initializer) { static std::atomic_uint i = 1; auto tmpVarName = "$tmp" + std::to_string(i); ++i; return CreateVarDecl(tmpVarName, ASTCloner::Clone(initializer), type); } OwnedPtr CreateVarPattern(const std::string& varName, Ptr ty) { auto v = MakeOwned(); v->varDecl = MakeOwned(); v->varDecl->identifier = varName; v->varDecl->parentPattern = v.get(); v->varDecl->EnableAttr(Attribute::COMPILER_ADD, Attribute::IMPLICIT_ADD); if (Ty::IsTyCorrect(ty)) { v->ty = ty; v->varDecl->ty = ty; } v->EnableAttr(Attribute::COMPILER_ADD); return v; } OwnedPtr CreateRefExpr(const SrcIdentifier& id, Ptr ty, const Position& pos, std::vector> args) { auto ret = MakeOwned(); ret->ref.identifier = id; ret->begin = pos; ret->end = pos; if (!id.IsRaw()) { ret->isThis = ret->ref.identifier == "this"; ret->isSuper = ret->ref.identifier == "super"; } for (auto type : args) { CJC_NULLPTR_CHECK(type); ret->typeArguments.emplace_back(ASTCloner::Clone(type)); } if (Ty::IsTyCorrect(ty)) { ret->ty = ty; } ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr CreateRefExpr(const std::string& name, const Position& pos) { return CreateRefExpr({name, pos, pos, false}, nullptr, pos); } OwnedPtr CreateRefExprInCore(const std::string& name) { OwnedPtr ret = CreateRefExpr(name); ret->EnableAttr(Attribute::IN_CORE); return ret; } OwnedPtr CreateRefExprInAST(const std::string& name) { OwnedPtr ret = CreateRefExpr(name); ret->EnableAttr(Attribute::IN_MACRO); return ret; } OwnedPtr CreateRefExpr(Decl& vd) { auto ret = MakeOwned(); ret->ref.identifier = vd.identifier; ret->ref.target = &vd; ret->ty = vd.ty; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr CreateRefExpr(Decl& vd, const Node& pos) { auto ret = MakeOwned(); ret->ref.identifier = {vd.identifier.Val(), pos.GetBegin(), pos.GetEnd()}; ret->ref.target = &vd; ret->ty = vd.ty; ret->begin = pos.GetBegin(); ret->end = pos.GetEnd(); ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr CreateRefType(const std::string& refName, std::vector> args) { auto ret = MakeOwned(); ret->ref.identifier = refName; for (auto type : args) { ret->typeArguments.emplace_back(ASTCloner::Clone(type)); } ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr CreateRefType(InheritableDecl& typeDecl) { auto ret = MakeOwned(); ret->ref.identifier = typeDecl.identifier; ret->ref.target = &typeDecl; ret->ty = typeDecl.ty; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr CreateArrayLit(std::vector> elements, Ptr ty) { auto ret = MakeOwned(); auto arrayType = DynamicCast(ty); CJC_ASSERT(arrayType); ret->ty = ty; ret->children = std::move(elements); return ret; } OwnedPtr CreateMemberAccess(OwnedPtr expr, const std::string& field) { auto memberAccess = MakeOwned(); memberAccess->field = field; memberAccess->EnableAttr(Attribute::COMPILER_ADD); if (expr->astKind == ASTKind::BLOCK) { // NOTE: Block should be attached to 'desugarExpr'. // Create a dummy desugar block expr, in order to create a CHIR `Goto` node. auto re = CreateRefExpr("$dummy"); re->ty = expr->ty; re->desugarExpr = std::move(expr); memberAccess->baseExpr = std::move(re); } else { memberAccess->baseExpr = std::move(expr); } CopyBasicInfo(memberAccess->baseExpr.get(), memberAccess.get()); if (!memberAccess->baseExpr || !Ty::IsTyCorrect(memberAccess->baseExpr->ty)) { return memberAccess; } // Only if there is a unique implementation name in the declaration can be handled correctly. auto foundMember = [&memberAccess, &field](const InheritableDecl& decl) { for (auto fieldDecl : decl.GetMemberDeclPtrs()) { if (fieldDecl->identifier == field) { memberAccess->ty = fieldDecl->ty; memberAccess->target = fieldDecl; return true; } } return false; }; if (auto classType = DynamicCast(memberAccess->baseExpr->ty); classType) { auto currentClass = classType->decl; CJC_NULLPTR_CHECK(currentClass); while (currentClass != nullptr && Ty::IsInitialTy(memberAccess->ty)) { if (foundMember(*currentClass)) { break; } currentClass = currentClass->GetSuperClassDecl(); } } else if (memberAccess->baseExpr->ty->IsNominal()) { auto decl = Ty::GetDeclOfTy(memberAccess->baseExpr->ty); CJC_NULLPTR_CHECK(decl); (void)foundMember(*decl); } else if (memberAccess->baseExpr->ty->IsBuiltin()) { // extended methods } else { CJC_ABORT(); } return memberAccess; } OwnedPtr CreateMemberAccess(OwnedPtr expr, Decl& field) { auto ret = MakeOwned(); if (expr->astKind == ASTKind::BLOCK) { // NOTE: Block should be attached to 'desugarExpr'. // Create a dummy desugar block expr, in order to create a CHIR `Goto` node. auto re = CreateRefExpr("$dummy"); re->ty = expr->ty; re->desugarExpr = std::move(expr); ret->baseExpr = std::move(re); } else { ret->baseExpr = std::move(expr); } ret->field = field.identifier.Val(); ret->field.SetRaw(field.identifier.IsRaw()); ret->target = &field; ret->ty = field.ty; ret->isExposedAccess = field.ty && field.ty->IsGeneric(); ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr CreateConstraintForFFI(const std::string& upperBound) { auto gc = MakeOwned(); OwnedPtr t = MakeOwned(); t->ref.identifier = "T"; gc->type = std::move(t); gc->begin = DEFAULT_POSITION; gc->end = DEFAULT_POSITION; OwnedPtr ctype = MakeOwned(); ctype->ref.identifier = upperBound; gc->upperBounds.emplace_back(std::move(ctype)); return gc; } OwnedPtr CreateMatchCase(OwnedPtr pattern, OwnedPtr expr) { auto ty = expr->ty; auto matchCase = MakeOwned(); matchCase->EnableAttr(Attribute::COMPILER_ADD); matchCase->patterns.emplace_back(std::move(pattern)); matchCase->ty = ty; auto caseBlock = MakeOwned(); caseBlock->EnableAttr(Attribute::COMPILER_ADD); caseBlock->body.emplace_back(std::move(expr)); caseBlock->ty = ty; matchCase->exprOrDecls = std::move(caseBlock); return matchCase; } OwnedPtr CreateMatchExpr(OwnedPtr selector, std::vector> matchCases, Ptr ty, Expr::SugarKind sugarKind) { auto matchExpr = MakeOwned(); matchExpr->EnableAttr(Attribute::COMPILER_ADD); matchExpr->matchMode = true; matchExpr->selector = std::move(selector); matchExpr->matchCases = std::move(matchCases); matchExpr->sugarKind = sugarKind; matchExpr->ty = ty; return matchExpr; } OwnedPtr CreateLitConstExpr(LitConstKind kind, const std::string& val, Ptr ty, bool needToMakeRef) { auto ret = MakeOwned(); ret->kind = kind; ret->stringValue = val; if (kind == LitConstKind::RUNE) { ret->codepoint = StringConvertor::UTF8ToCodepoint(val); } ret->ty = ty; if (!Ty::IsTyCorrect(ty)) { return ret; } if (ty->IsFloating()) { ret->constNumValue.asFloat.value = std::stold(val); } else if (ty->IsInteger()) { if (ty->IsUnsignedInteger()) { ret->constNumValue.asInt.SetUint64(std::stoull(val)); } else { ret->constNumValue.asInt.SetInt64(std::stoll(val)); } } else if (ty->IsBoolean()) { if (val == "true") { ret->constNumValue.asBoolean = true; } else if (val == "false") { ret->constNumValue.asBoolean = false; } else { CJC_ABORT(); } } else if (ty->IsString()) { ret->stringKind = StringKind::NORMAL; } else if (ty->kind != TypeKind::TYPE_RUNE && !ty->IsUnit()) { CJC_ABORT(); } if (needToMakeRef) { if (auto decl = Ty::GetDeclOfTy(ty); decl) { ret->ref = CreateRefType(*decl); } } return ret; } OwnedPtr CreateTupleLit(std::vector> elements, Ptr ty) { auto ret = MakeOwned(); auto tupleType = DynamicCast(ty); CJC_ASSERT(tupleType != nullptr); ret->ty = ty; ret->children = std::move(elements); return ret; } OwnedPtr CreateCallExpr(OwnedPtr funcExpr, std::vector> args, Ptr resolvedFunc, Ptr ty, CallKind callTy) { auto ret = MakeOwned(); CJC_NULLPTR_CHECK(funcExpr); if (Is(funcExpr.get())) { auto ma = StaticAs(funcExpr.get()); ma->callOrPattern = ret.get(); } if (Is(funcExpr.get())) { auto re = StaticAs(funcExpr.get()); re->callOrPattern = ret.get(); } ret->baseFunc = std::move(funcExpr); ret->args = std::move(args); ret->callKind = callTy; if (Ty::IsTyCorrect(ty)) { ret->ty = ty; } CopyBasicInfo(ret->baseFunc.get(), ret.get()); if (resolvedFunc != nullptr) { ret->resolvedFunction = resolvedFunc; if (resolvedFunc->TestAttr(Attribute::CONSTRUCTOR)) { if (Is(ty)) { ret->callKind = CallKind::CALL_STRUCT_CREATION; } else if (Is(ty)) { ret->callKind = CallKind::CALL_OBJECT_CREATION; } } if (ret->callKind == CallKind::CALL_INVALID) { ret->callKind = CallKind::CALL_DECLARED_FUNCTION; } } ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr CreateLambdaExpr(OwnedPtr funcBody) { auto ret = MakeOwned(std::move(funcBody)); ret->ty = ret->funcBody->ty; return ret; } OwnedPtr CreateFuncParamList(std::vector> params, Ptr ty) { auto ret = MakeOwned(); ret->params = std::move(params); if (Ty::IsTyCorrect(ty)) { ret->ty = ty; } return ret; } OwnedPtr CreateIfExpr( OwnedPtr condExpr, OwnedPtr body, OwnedPtr elseBody, Ptr semaType) { auto ret = MakeOwned(); ret->condExpr = std::move(condExpr); ret->thenBody = std::move(body); ret->elseBody = std::move(elseBody); if (Ty::IsTyCorrect(semaType)) { ret->ty = semaType; } else if (ret->thenBody && ret->elseBody && ret->thenBody->ty == ret->elseBody->ty) { ret->ty = ret->thenBody->ty; } ret->hasElse = ret->elseBody != nullptr; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr CreateTupleAccess(OwnedPtr expr, size_t index) { auto elem = MakeOwned(); if (auto type = DynamicCast(expr->ty)) { elem->isTupleAccess = true; elem->ty = type->typeArgs[index]; elem->baseExpr = std::move(expr); elem->indexExprs.emplace_back(MakeOwned(LitConstKind::INTEGER, std::to_string(index))); elem->indexExprs[0]->constNumValue.asInt.SetUint64(index); return elem; } CJC_ABORT(); return nullptr; } OwnedPtr CreateThrowExpr(Decl& var) { auto refExpr = MakeOwned(); refExpr->ref.identifier = var.identifier; refExpr->ref.target = &var; auto ret = MakeOwned(); ret->expr = std::move(refExpr); return ret; } OwnedPtr CreatePerformExpr(Decl& var) { auto refExpr = MakeOwned(); refExpr->ref.identifier = var.identifier; refExpr->ref.target = &var; auto ret = MakeOwned(); ret->expr = std::move(refExpr); return ret; } OwnedPtr CreateTypePattern( OwnedPtr && pattern, OwnedPtr && type, Expr& selector ) { Ptr ty = type->ty; CJC_NULLPTR_CHECK(ty); CJC_NULLPTR_CHECK(selector.ty); auto typePattern = MakeOwned(); typePattern->EnableAttr(AST::Attribute::COMPILER_ADD); typePattern->ctxExpr = &selector; typePattern->pattern = std::move(pattern); typePattern->type = std::move(type); typePattern->pattern->ty = ty; typePattern->ty = ty; typePattern->curFile = selector.curFile; return typePattern; } } // namespace Cangjie::AST cangjie_compiler-1.0.7/src/AST/Identifier.cpp000066400000000000000000000024011510705540100210100ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/AST/Identifier.h" #include #include "cangjie/Utils/ConstantsUtils.h" namespace Cangjie { Identifier::Identifier(std::string s, const Position& begin, const Position& end) { SetValue(std::move(s)); SetPos(begin, end); } Identifier& Identifier::operator=(std::string_view identifier) { std::string s{identifier}; SetValue(std::move(s)); return *this; } Identifier& Identifier::operator=(const std::string& identifier) { std::string s{identifier}; SetValue(std::move(s)); return *this; } Identifier& Identifier::operator=(std::string&& identifier) { SetValue(std::move(identifier)); return *this; } bool Identifier::Valid() const { return v != INVALID_IDENTIFIER; } std::ostream& operator<<(std::ostream& out, const Identifier& identifier) { return out << identifier.Val() << ", begin = " << identifier.Begin() << ", end = " << identifier.End(); } void Identifier::SetValue(std::string&& s) { std::swap(v, s); } } // namespace Cangjie cangjie_compiler-1.0.7/src/AST/IntLiteral.cpp000066400000000000000000000427621510705540100210130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements int literal apis. */ #include "cangjie/AST/IntLiteral.h" #include #include #include #include #include "cangjie/AST/ASTCasting.h" #include "cangjie/Utils/StdUtils.h" using namespace Cangjie; using namespace AST; namespace { const std::map INTEGER_TO_MAX_VALUE{ {TypeKind::TYPE_INT8, std::numeric_limits::max()}, {TypeKind::TYPE_INT16, std::numeric_limits::max()}, {TypeKind::TYPE_INT32, std::numeric_limits::max()}, {TypeKind::TYPE_INT64, std::numeric_limits::max()}, {TypeKind::TYPE_UINT8, std::numeric_limits::max()}, {TypeKind::TYPE_UINT16, std::numeric_limits::max()}, {TypeKind::TYPE_UINT32, std::numeric_limits::max()}, {TypeKind::TYPE_UINT64, std::numeric_limits::max()}, {TypeKind::TYPE_IDEAL_INT, std::numeric_limits::max()}, }; const std::map INTEGER_TO_MIN_VALUE{ {TypeKind::TYPE_INT8, std::numeric_limits::min()}, {TypeKind::TYPE_INT16, std::numeric_limits::min()}, {TypeKind::TYPE_INT32, std::numeric_limits::min()}, {TypeKind::TYPE_INT64, std::numeric_limits::min()}, {TypeKind::TYPE_UINT8, std::numeric_limits::min()}, {TypeKind::TYPE_UINT16, std::numeric_limits::min()}, {TypeKind::TYPE_UINT32, std::numeric_limits::min()}, {TypeKind::TYPE_UINT64, std::numeric_limits::min()}, {TypeKind::TYPE_IDEAL_INT, std::numeric_limits::min()}, }; const std::map INTEGER_TO_BIT_LEN{ {TypeKind::TYPE_INT8, 8}, {TypeKind::TYPE_INT16, 16}, {TypeKind::TYPE_INT32, 32}, {TypeKind::TYPE_INT64, 64}, {TypeKind::TYPE_UINT8, 8}, {TypeKind::TYPE_UINT16, 16}, {TypeKind::TYPE_UINT32, 32}, {TypeKind::TYPE_UINT64, 64}, {TypeKind::TYPE_IDEAL_INT, 64}, }; } // namespace static const size_t I64_WIDTH = 64; static const size_t UI64_WIDTH = 64; bool IntLiteral::GreaterThanOrEqualBitLen(const TypeKind kind) const { auto iter = INTEGER_TO_BIT_LEN.find(kind); if (iter == INTEGER_TO_BIT_LEN.end()) { return false; } return uint64Val >= iter->second; } int IntLiteral::EscapeCharacterToInt(char c) { switch (c) { case 't': return static_cast('\t'); case 'b': return static_cast('\b'); case 'r': return static_cast('\r'); case 'n': return static_cast('\n'); case '\'': return static_cast('\''); case '"': return static_cast('\"'); case '\\': return static_cast('\\'); case 'f': return static_cast('\f'); case 'v': return static_cast('\v'); case '0': return static_cast('\0'); default: return -1; } } // return -1 if encounter errors // parse byte such as b'x', b'\n', b'\u{ff}' static int ParseByteIntLitString(const std::string& val) { size_t startQuote = val.find_first_of('\'', 0); size_t endQuote = val.find_last_of('\'', val.size() - 1); // 2 is minimum value. It must start with 'b' and '\'' since this is a lexer rule. if (endQuote - startQuote < 2) { return -1; } std::string lit = val.substr(startQuote + 1, (endQuote - startQuote) - 1); constexpr size_t esCharLen = 2; // 2 is the length of escape characters. if (lit.size() == 1) { return static_cast(lit[0]); } else if (lit.size() == esCharLen && lit[0] == '\\') { return IntLiteral::EscapeCharacterToInt(lit[1]); } else if (lit.size() > esCharLen && lit[1] == 'u') { size_t lCurlPos = val.find_first_of('{', 0); size_t rCurlPos = val.find_first_of('}', 0); if (lCurlPos == std::string::npos || rCurlPos == std::string::npos || rCurlPos - lCurlPos <= 1) { return -1; } std::string digits = val.substr(lCurlPos + 1, (rCurlPos - lCurlPos) - 1); const int hexBase = 16; return Stoi(digits, hexBase).value_or(-1); } return -1; } static std::pair GetBaseAndPureStringValue(const std::string& stringVal) { const int baseLen = 2; const int binBase = 2; const int octBase = 8; const int hexBase = 16; int base = 10; bool isNegNum = false; std::string prefix; std::string stringValue = stringVal; if (stringValue.at(0) == '-') { stringValue.erase(0, 1); isNegNum = true; } if (stringValue.size() > baseLen) { prefix = stringValue.substr(0, baseLen); std::transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower); } // Erase underline. like 1_2_3 ==> 123 stringValue.erase(std::remove(stringValue.begin(), stringValue.end(), '_'), stringValue.end()); // Get base. if (prefix == "0b") { stringValue = stringValue.erase(0, baseLen); base = binBase; } else if (prefix == "0o") { stringValue = stringValue.erase(0, baseLen); base = octBase; } else if (prefix == "0x") { stringValue = stringValue.erase(0, baseLen); base = hexBase; } if (isNegNum) { stringValue = "-" + stringValue; } return std::make_pair(base, stringValue); } void IntLiteral::InitIntLiteral(const std::string& stringVal, const TypeKind kind) { if (stringVal.empty()) { return; } type = kind; if (stringVal.at(0) == '-') { sign = -1; } auto [base, stringValue] = GetBaseAndPureStringValue(stringVal); bool outOfUintRange = false; bool outOfIntRange = false; if (stringVal[0] == 'b') { int64Val = ParseByteIntLitString(stringVal); if (int64Val == -1) { int64Val = 0; outOfIntRange = true; outOfUintRange = true; } uint64Val = static_cast(int64Val); } else { if (auto ul = Stoull(stringValue, base)) { uint64Val = *ul; } else { uint64Val = 0; outOfUintRange = true; } if (auto il = Stoll(stringValue, base)) { int64Val = *il; } else { int64Val = 0; outOfIntRange = true; } } outOfRange = sign == -1 ? outOfIntRange : outOfUintRange; } static TypeKind GetRealTypeKindOfNative(Ptr ty) { CJC_ASSERT(ty); TypeKind k = ty->kind; if (k == TypeKind::TYPE_INT_NATIVE) { k = StaticCast(ty)->bitness == I64_WIDTH ? TypeKind::TYPE_INT64 : TypeKind::TYPE_INT32; } else if (k == TypeKind::TYPE_UINT_NATIVE) { k = StaticCast(ty)->bitness == UI64_WIDTH ? TypeKind::TYPE_UINT64 : TypeKind::TYPE_UINT32; } return k; } void IntLiteral::SetOutOfRange(Ptr ty) { type = GetRealTypeKindOfNative(ty); outOfRange = outOfRange || CheckOverflow(); // check overflow if convert not out of range } bool IntLiteral::CheckOverflow() { if (INTEGER_TO_MAX_VALUE.find(type) == INTEGER_TO_MAX_VALUE.end()) { return false; } if (sign == 1) { return uint64Val > INTEGER_TO_MAX_VALUE.at(type); } else { return int64Val < INTEGER_TO_MIN_VALUE.at(type); } } namespace { uint64_t CalcWrappingValue(uint64_t value, TypeKind type) { if (type == TypeKind::TYPE_UINT8) { return static_cast(static_cast(value)); } if (type == TypeKind::TYPE_UINT16) { return static_cast(static_cast(value)); } if (type == TypeKind::TYPE_UINT32) { return static_cast(static_cast(value)); } return value; } int64_t CalcWrappingValue(int64_t value, TypeKind type) { if (type == TypeKind::TYPE_INT8) { return static_cast(static_cast(value)); } if (type == TypeKind::TYPE_INT16) { return static_cast(static_cast(value)); } if (type == TypeKind::TYPE_INT32) { return static_cast(static_cast(value)); } return value; } } // namespace void IntLiteral::CalcWrappingAndSaturatingVal() { if (!outOfRange) { return; } if (INTEGER_TO_MAX_VALUE.find(type) == INTEGER_TO_MAX_VALUE.end()) { return; } if (IsUnsigned()) { wuint64Val = CalcWrappingValue(uint64Val, type); if (outOfMax) { suint64Val = INTEGER_TO_MAX_VALUE.at(type); } else { suint64Val = static_cast(INTEGER_TO_MIN_VALUE.at(type)); } } else { wint64Val = CalcWrappingValue(int64Val, type); if (outOfMax) { sint64Val = static_cast(INTEGER_TO_MAX_VALUE.at(type)); } else { sint64Val = INTEGER_TO_MIN_VALUE.at(type); } } } void IntLiteral::SetWrappingValue() { if (!outOfRange) { return; } if (IsUnsigned()) { SetUint64(wuint64Val); return; } SetInt64(wint64Val); } void IntLiteral::SetSaturatingValue() { if (!outOfRange) { return; } if (IsUnsigned()) { SetUint64(suint64Val); return; } SetInt64(sint64Val); } std::string IntLiteral::GetValue() const { if (IsUnsigned()) { return std::to_string(uint64Val); } return std::to_string(int64Val); } IntLiteral IntLiteral::operator-() const { int64_t min = INTEGER_TO_MIN_VALUE.at(type); bool overflow = IsUnsigned() || (sign < 0 && int64Val == min); return IntLiteral(-int64Val, type, overflow); } IntLiteral IntLiteral::operator~() const { // Cast to signed type to keep sign value. return IntLiteral(static_cast(~uint64Val), type, false); } IntLiteral IntLiteral::operator+(const IntLiteral& rhs) const { if (sign + rhs.sign == 0) { return IntLiteral(int64Val + rhs.int64Val, type, false); } if (sign > 0) { bool overflow = (INTEGER_TO_MAX_VALUE.at(type) - uint64Val) < rhs.uint64Val; return IntLiteral(uint64Val + rhs.uint64Val, type, overflow, overflow); } else { bool overflow = (INTEGER_TO_MIN_VALUE.at(type) - int64Val) > rhs.int64Val; return IntLiteral(int64Val + rhs.int64Val, type, overflow); } } IntLiteral IntLiteral::operator-(const IntLiteral& rhs) const { // For a - b, where a,b >= 0 or a,b < 0. if (sign + rhs.sign != 0) { if (sign == 1 && IsUnsigned()) { // allowed overflow calculation return IntLiteral(uint64Val - rhs.uint64Val, type, uint64Val < rhs.uint64Val); } return IntLiteral(int64Val - rhs.int64Val, type, false); } // For a - b, where a >= 0, b < 0 or a < 0, b >= 0. must be signed type. if (sign > 0) { uint64_t rhsPos = static_cast(-rhs.int64Val); // `uint64Val` is guaranteed to be smaller than or equal to `MAX`, but `rhsPos` is not. // We cannot subtract `MAX` by `rhsPos`, because the subtraction may overflow. bool overflow = (INTEGER_TO_MAX_VALUE.at(type) - uint64Val) < rhsPos; return IntLiteral(int64Val - rhs.int64Val, type, overflow, overflow); } else { bool overflow = (int64Val - INTEGER_TO_MIN_VALUE.at(type)) < rhs.int64Val; return IntLiteral(int64Val - rhs.int64Val, type, overflow); } } IntLiteral IntLiteral::operator*(const IntLiteral& rhs) const { if ((int64Val == 0 && uint64Val == 0) || (rhs.int64Val == 0 && rhs.uint64Val == 0)) { return IntLiteral(int64_t(0), type, false); } // Integer division will around to lower number except sign if (sign + rhs.sign == 0) { int64_t minVal = INTEGER_TO_MIN_VALUE.at(type); // if one of operand is -1, multiplication will not overflow bool inverse = (int64Val == -1) || (rhs.int64Val == -1); if (inverse) { return IntLiteral(int64Val * rhs.int64Val, type, false); } else if (outOfRange || rhs.outOfRange) { return IntLiteral(int64Val * rhs.int64Val, type, true); } // Eg a * -b > min or -a * b > min => overflow when min / -b < a or min / -a < b. if (rhs.int64Val == 0 || int64Val == 0) { return IntLiteral(int64_t(0), type, false); } bool overflow = (sign > 0) ? (minVal / rhs.int64Val < int64Val) : (minVal / int64Val < rhs.int64Val); return IntLiteral(int64Val * rhs.int64Val, type, overflow); } uint64_t maxVal = INTEGER_TO_MAX_VALUE.at(type); if (sign > 0) { // Eg a * b < max => overflow when max / b < a. bool overflow = maxVal / uint64Val < rhs.uint64Val; return IntLiteral(uint64Val * rhs.uint64Val, type, overflow, overflow); } // Eg a * -b < max => overflow when max / |-a| < |-b|. auto absValue = GetAbsValue(); if (absValue == 0) { return IntLiteral(int64_t(0), type, false); } bool overflow = maxVal / absValue < rhs.GetAbsValue(); return IntLiteral(int64Val * rhs.int64Val, type, overflow, overflow); } IntLiteral IntLiteral::operator/(const IntLiteral& rhs) const { if (sign + rhs.sign > 0) { return IntLiteral(uint64Val / rhs.uint64Val, type, false); } int64_t minVal = INTEGER_TO_MIN_VALUE.at(type); bool overflow = (sign + rhs.sign) < 0 && int64Val == minVal && rhs.int64Val == -1; if (overflow && int64Val == std::numeric_limits::min()) { return IntLiteral(static_cast(-static_cast(int64Val)), type, overflow, overflow); } if (rhs.int64Val != 0) { return IntLiteral(int64Val / rhs.int64Val, type, overflow, overflow); } if (rhs.outOfRange) { return IntLiteral(int64Val, type, false); } auto rhsAbsValue = rhs.GetAbsValue(); if (rhsAbsValue != 0) { int64_t res = static_cast(GetAbsValue() / rhsAbsValue) * sign * rhs.sign; return IntLiteral(res, type, overflow, overflow); } return IntLiteral(int64Val, type, false); } IntLiteral IntLiteral::operator%(const IntLiteral& rhs) const { if (sign + rhs.sign > 0) { return IntLiteral(uint64Val % rhs.uint64Val, type, false); } if (rhs.int64Val != 0) { return IntLiteral(int64Val % rhs.int64Val, type, false); } if (rhs.outOfRange) { return IntLiteral(int64Val, type, false); } auto rhsAbsValue = rhs.GetAbsValue(); if (rhsAbsValue != 0) { // In c++ calculation, sign of mod is decided by left value. return IntLiteral(static_cast(GetAbsValue() % rhsAbsValue) * sign, type, false); } return IntLiteral(int64Val, type, false); } IntLiteral IntLiteral::operator>>(const IntLiteral& rhs) const { // Cast to signed type to keep sign value. return IntLiteral(static_cast(uint64Val >> rhs.uint64Val), type, false); } IntLiteral IntLiteral::operator<<(const IntLiteral& rhs) const { // Cast to signed type to keep sign value. return IntLiteral(static_cast(uint64Val << rhs.uint64Val), type, false); } IntLiteral IntLiteral::operator&(const IntLiteral& rhs) const { // Cast to signed type to keep sign value. return IntLiteral(static_cast(uint64Val & rhs.uint64Val), type, false); } IntLiteral IntLiteral::operator^(const IntLiteral& rhs) const { // Cast to signed type to keep sign value. return IntLiteral(static_cast(uint64Val ^ rhs.uint64Val), type, false); } IntLiteral IntLiteral::operator|(const IntLiteral& rhs) const { // Cast to signed type to keep sign value. return IntLiteral(static_cast(uint64Val | rhs.uint64Val), type, false); } uint64_t IntLiteral::QuickPow(const uint64_t inBase, const uint64_t inExp, const uint64_t maxVal, bool& overflow) const { uint64_t res = 1; uint64_t base = inBase; uint64_t exp = inExp; bool ov = false; while (exp != 0 && !overflow) { if ((exp & 1) != 0) { if (ov || (base != 0 && (maxVal / base < res))) { overflow = true; } res *= base; } if (!ov && (base != 0 && (maxVal / base < base))) { ov = true; } base *= base; exp >>= 1; } return res; } IntLiteral IntLiteral::PowerOf(const IntLiteral& exponent) const { bool overflow = false; if (exponent.sign < 0) { // power of minus number will not overflow double result = pow(static_cast(int64Val), static_cast(exponent.int64Val)); return IntLiteral(static_cast(result), type, overflow); } // overflow condition: a ** b > max, -a ** b > max where b is even, |-a ** b| > |min| where b is odd bool inverse = sign < 0 && (exponent.uint64Val & 1) == 1; uint64_t maxVal = inverse ? static_cast(-INTEGER_TO_MIN_VALUE.at(type)) : INTEGER_TO_MAX_VALUE.at(type); uint64_t base = sign > 0 ? uint64Val : static_cast(-int64Val); uint64_t result = QuickPow(base, exponent.uint64Val, maxVal, overflow); if (inverse) { return IntLiteral(-static_cast(result), type, overflow); } return IntLiteral(result, type, overflow, overflow); } uint64_t IntLiteral::GetAbsValue() const { /* The min value of int64_t cast to uint64_t is exactly its absolute value. */ if (int64Val == std::numeric_limits::min()) { return static_cast(int64Val); } return sign == 1 ? uint64Val : static_cast(-int64Val); } cangjie_compiler-1.0.7/src/AST/Node.cpp000066400000000000000000001225511510705540100176240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the ToString method for nodes. */ #include "cangjie/AST/Node.h" #include #include #include #include #include #include #include #include #include #include "cangjie/AST/Match.h" #include "cangjie/AST/RecoverDesugar.h" #include "cangjie/AST/Symbol.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/Position.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/StdUtils.h" namespace Cangjie { using namespace AST; namespace { const std::string EMPTY_PACKAGE_NAME = ""; struct Span { std::string str; int numNewLine{0}; int width{0}; }; Span NextSpan(const std::string& str, const Position& begin, const Position& end, int offset = 0) { Span span; span.str = str; span.numNewLine = end.line - begin.line; span.width = (span.numNewLine > 0 ? end.column - 1 : end.column - begin.column) + offset; return span; } std::ostream& operator<<(std::ostream& out, Span const& span) { int numNewLine = span.numNewLine; while (numNewLine > 0) { out << '\n'; numNewLine--; } out << std::right << std::setw(span.width) << span.str; return out; } /** * @brief Get the end position of a token in *.macrocall file by the same token's position in curfile. * @param key: the Hash value of line and column about token's end position in curfile. * @param first: the Hash value of line and column about token's begin position in curfile. * @param second: the begin position of the same token in *.macrocall file. * @return the end position in *.macrocall file. */ Position GetMacroCallEndPos(bool isCurFile, uint32_t key, uint32_t first, const Position second) { auto end = second; if (!isCurFile) { // key and first are only column. auto columnOffset = (key > first) ? (key - first) : 0; return end + columnOffset; } // key and first are the Hash value of line and column that created by Position.Hash32(). auto keyPos = Position::RestorePosFromHash(key); auto firstPos = Position::RestorePosFromHash(first); auto lineOffset = (keyPos.first > firstPos.first) ? (keyPos.first - firstPos.first) : 0; if (lineOffset > 0) { end.line += lineOffset; end.column = keyPos.second; return end; } auto columnOffset = (key > first) ? (key - first) : 0; return end + columnOffset; } /** * @brief Get the end position of a token for LSP using new2originPosMap. * @param newPos: the Hash value of line and column about token's end position in curfile. * @param first: the Hash value of line and column about token's begin position in curfile. * @return the end position in *.macrocall file. */ Position GetMacroCallEndPosforLsp(uint32_t newPos, uint32_t first, const Position second) { auto end = second; // newPos and first are the Hash value of line and column that created by Position.Hash32(). auto keyPos = Position::RestorePosFromHash(newPos); auto firstPos = Position::RestorePosFromHash(first); auto lineOffset = (keyPos.first > firstPos.first) ? (keyPos.first - firstPos.first) : 0; if (lineOffset > 0) { end.line += lineOffset; end.column = keyPos.second; return end; } auto columnOffset = (newPos > first) ? (newPos - first) : 0; return end + columnOffset; } /** * @brief Get the sourcePos of macrocall by the curfile's pos. For cjc, we * get sourcePos only in *.macrocall file . * @return the sourcePos in curfile if the source can be founded in curfile, * sourcePos in macrocall file otherwise. */ Position GetMacroSourcePos(const MacroInvocation& invocation, const Position& pos, bool isLowerBound = false) { auto key = invocation.isCurFile ? pos.Hash32() : static_cast(pos.column); if (isLowerBound) { // Get end position. if (invocation.isForLSP) { // For lsp. auto posIt = invocation.new2originPosMap.find(pos.Hash32()); if (posIt != invocation.new2originPosMap.end()) { ++posIt; } if (posIt == invocation.new2originPosMap.end()) { posIt = invocation.new2originPosMap.lower_bound(pos.Hash32()); } if (posIt == invocation.new2originPosMap.end()) { return pos; } auto sourcePos = GetMacroCallEndPosforLsp(pos.Hash32(), posIt->first, posIt->second); if (sourcePos.isCurFile) { return sourcePos; } } auto posIt = invocation.new2macroCallPosMap.lower_bound(key); if (posIt != invocation.new2macroCallPosMap.end()) { return GetMacroCallEndPos(invocation.isCurFile, key, posIt->first, posIt->second); } return pos; } // Get begin/identifier/field position. if (invocation.isForLSP) { // For lsp. if (invocation.new2originPosMap.find(pos.Hash32()) == invocation.new2originPosMap.end()) { return pos; } auto sourcePos = invocation.new2originPosMap.at(pos.Hash32()); if (sourcePos.isCurFile) { return sourcePos; } } if (invocation.new2macroCallPosMap.find(key) != invocation.new2macroCallPosMap.end()) { return invocation.new2macroCallPosMap.at(key); } return pos; } } // namespace std::string Modifier::ToString() const { if (isExplicit) { return TOKENS[static_cast(modifier)]; } else { return ""; } } std::string VarDecl::ToString() const { std::stringstream ss; Position curSpanBegin = begin; int i = 0; for (auto modifier : modifiers) { if (i > 0) { ss << NextSpan(modifier.ToString(), curSpanBegin, modifier.end); } else { ss << modifier.ToString(); } curSpanBegin = modifier.end; i++; } // The length of "var" and "let" are both 3. ss << NextSpan(isVar ? "var" : "let", curSpanBegin, keywordPos, 3); curSpanBegin = keywordPos + Position{0, 0, 3}; ss << NextSpan(identifier, curSpanBegin, identifier.Begin(), static_cast(identifier.Length())); curSpanBegin = identifier.End(); if (type) { ss << NextSpan(":", curSpanBegin, colonPos, 1); curSpanBegin = colonPos + Position{0, 0, 1}; ss << NextSpan(type->ToString(), curSpanBegin, type->end); curSpanBegin = type->end; } if (initializer) { ss << NextSpan("=", curSpanBegin, assignPos, 1); curSpanBegin = assignPos + Position{0, 0, 1}; ss << NextSpan(initializer->ToString(), curSpanBegin, initializer->end); } return ss.str(); } std::string EnumPattern::GetIdentifier() const { if (!constructor) { return ""; } switch (constructor->astKind) { case ASTKind::REF_EXPR: { return static_cast(*constructor).ref.identifier; } case ASTKind::MEMBER_ACCESS: { return static_cast(*constructor).field; } default: { CJC_ABORT(); return ""; } } } std::string CallExpr::ToString() const { std::stringstream ss; Position curSpanBegin = baseFunc->begin; ss << NextSpan(baseFunc->ToString(), curSpanBegin, baseFunc->end); curSpanBegin = baseFunc->end; ss << NextSpan("(", curSpanBegin, leftParenPos, 1); curSpanBegin = leftParenPos + Position{0, 0, 1}; for (auto& arg : args) { ss << NextSpan(arg->ToString(), curSpanBegin, arg->end); curSpanBegin = arg->end; if (arg->commaPos != INVALID_POSITION) { ss << NextSpan(",", curSpanBegin, arg->commaPos, 1); curSpanBegin = arg->commaPos + Position{0, 0, 1}; } } ss << NextSpan(")", curSpanBegin, rightParenPos, 1); return ss.str(); } void CallExpr::Clear() noexcept { RecoverToCallExpr(*this); Expr::Clear(); baseFunc->Clear(); callKind = CallKind::CALL_INVALID; resolvedFunction = nullptr; } std::string FuncArg::ToString() const { std::stringstream ss; Position curSpanBegin = begin; if (!name.Empty()) { curSpanBegin = name.Begin(); ss << NextSpan(name, curSpanBegin, name.Begin(), static_cast(name.Length())); curSpanBegin += Position{0, 0, static_cast(name.Length())}; ss << NextSpan(":", curSpanBegin, colonPos, 1); curSpanBegin += Position{0, 0, 1}; } else { curSpanBegin = expr->begin; } if (withInout) { ss << "inout "; } ss << NextSpan(expr->ToString(), curSpanBegin, expr->end); return ss.str(); } std::string MemberAccess::ToString() const { std::stringstream ss; Position curSpanBegin = baseExpr->begin; ss << NextSpan(baseExpr->ToString(), curSpanBegin, baseExpr->end); curSpanBegin = baseExpr->end; ss << NextSpan(".", curSpanBegin, dotPos, 1); curSpanBegin = dotPos + Position{0, 0, 1}; ss << NextSpan(field, curSpanBegin, field.Begin(), static_cast(field.Length())); return ss.str(); } std::string LitConstExpr::ToString() const { if (kind == LitConstKind::STRING) { return "\"" + stringValue + "\""; } else { return stringValue; } } TypeKind LitConstExpr::GetNumLitTypeKind() { int suffixWidth = 0; if (kind == LitConstKind::RUNE_BYTE) { return TypeKind::TYPE_UINT8; } if (kind == LitConstKind::INTEGER) { auto suffixStart = std::find_if(stringValue.begin(), stringValue.end(), [](char c) { return c == 'i' || c == 'u'; }); std::string suffix = std::string(suffixStart, stringValue.end()); if (suffix.empty()) { return TypeKind::TYPE_IDEAL_INT; } else { if (auto suffixWid = Stoi(std::string(suffix.begin() + 1, suffix.end()))) { suffixWidth = *suffixWid; } else { return TypeKind::TYPE_INVALID; } // The following will calculate logarithm 8, 16, 32, 64 base 2 and will get // 3,4,5,6, take 3(int) or 4(float) as base and plus powerBase2 - 3 + INT8 or UINT8 or FLOAT16 int powerBase2 = __builtin_ctz(static_cast(suffixWidth)); char signedness = suffix[0]; // The following code violates P.08-CPP(V5.0), however, I think it is OK since the number of width must // be the several possibilities. If not the case, lexer would report an error in the early stage. Can // only be i, u, f three cases if (signedness == 'i' || signedness == 'u') { int leastPowerBase2 = 3; // int starts from 8=2^3 bits width TypeKind tk = signedness == 'i' ? TypeKind::TYPE_INT8 : TypeKind::TYPE_UINT8; return static_cast(powerBase2 - leastPowerBase2 + static_cast(tk)); } else { return TypeKind::TYPE_INVALID; } } } else if (kind == LitConstKind::FLOAT) { // Check whether it is a hexadecimal floating pointing number. If it is then // 'f' is allowed as digits and there will not be any suffix. // We should skip the negative sign if it exists. bool isNegative = !stringValue.empty() && stringValue.front() == '-'; std::string prefix = stringValue.substr(isNegative ? 1 : 0, std::min(stringValue.size(), 2UL)); if (prefix == "0x" || prefix == "0X") { return TypeKind::TYPE_IDEAL_FLOAT; } auto suffixStart = std::find_if(stringValue.begin(), stringValue.end(), [](char c) { return c == 'f'; }); std::string suffix = std::string(suffixStart, stringValue.end()); if (suffix.empty()) { return TypeKind::TYPE_IDEAL_FLOAT; } else { if (auto suffixWid = Stoi(std::string(suffix.begin() + 1, suffix.end()))) { suffixWidth = *suffixWid; } else { return TypeKind::TYPE_INVALID; } int powerBase2 = __builtin_ctz(static_cast(suffixWidth)); int leastPowerBase2 = 4; return static_cast(powerBase2 - leastPowerBase2 + static_cast(TypeKind::TYPE_FLOAT16)); } } return TypeKind::TYPE_INVALID; } std::string RefType::ToString() const { std::stringstream ss; ss << ref.identifier.Val(); Position curSpanBegin = ref.identifier.End(); if (!typeArguments.empty()) { ss << NextSpan("<", curSpanBegin, leftAnglePos, 1); curSpanBegin = leftAnglePos + Position{0, 0, 1}; for (auto& typeArgument : typeArguments) { ss << NextSpan(typeArgument->ToString(), curSpanBegin, typeArgument->end); curSpanBegin = typeArgument->end; } ss << NextSpan(">", curSpanBegin, rightAnglePos, 1); } return ss.str(); } std::string RefExpr::ToString() const { return ref.identifier; } std::string ArrayLit::ToString() const { std::stringstream ss; ss << "["; Position curSpanBegin = begin + Position{0, 0, 1}; size_t i = 0; for (auto& child : children) { ss << NextSpan(child->ToString(), curSpanBegin, child->end); curSpanBegin = child->end; if (i < commaPosVector.size()) { ss << NextSpan(",", curSpanBegin, commaPosVector[i], 1); curSpanBegin = commaPosVector[i] + Position{0, 0, 1}; } i++; } ss << NextSpan("]", curSpanBegin, rightSquarePos, 1); return ss.str(); } std::string ArrayExpr::ToString() const { std::stringstream ss; Position curSpanBegin = begin; ss << NextSpan(type->ToString(), curSpanBegin, type->end); curSpanBegin = type->end; ss << NextSpan("(", curSpanBegin, leftParenPos, 1); curSpanBegin = leftParenPos + Position{0, 0, 1}; for (size_t i = 0; i < args.size(); ++i) { ss << NextSpan(args[i]->ToString(), curSpanBegin, args[i]->end); curSpanBegin = args[i]->end; if (i < commaPosVector.size()) { ss << NextSpan(",", curSpanBegin, commaPosVector[i], 1); curSpanBegin = commaPosVector[i] + Position{0, 0, 1}; } } ss << NextSpan(")", curSpanBegin, rightParenPos, 1); return ss.str(); } std::string PointerExpr::ToString() const { std::stringstream ss; std::string expr = "CPointer<"; Position curSpanBegin = begin; Position curSpanEnd = curSpanBegin + Position{0, 0, static_cast(expr.size())}; ss << NextSpan(expr, curSpanBegin, curSpanEnd); curSpanBegin = curSpanEnd; if (ty && !ty->typeArgs.empty()) { expr = Ty::ToString(ty->typeArgs[0]); curSpanEnd = curSpanBegin + Position{0, 0, static_cast(expr.size())}; ss << NextSpan(expr, curSpanBegin, curSpanEnd); } // 2 is string length of ">(". curSpanEnd = curSpanBegin + Position{0, 0, 2}; ss << NextSpan(">(", curSpanBegin, curSpanEnd); curSpanBegin = curSpanEnd; if (arg) { expr = arg->ToString(); curSpanEnd = curSpanBegin + Position{0, 0, static_cast(expr.size())}; ss << NextSpan(expr, curSpanBegin, curSpanEnd); } ss << NextSpan(")", curSpanBegin, curSpanBegin + Position{0, 0, 1}); return ss.str(); } Ptr Block::GetLastExprOrDecl() const { if (body.empty()) { return nullptr; } return body.back().get(); } Node::~Node() { Node::Clear(); if (symbol && symbol->node == this) { symbol->invertedIndexBeenDeleted = true; } } bool Node::ShouldDiagnose(bool allowCompilerAdd) const { if (!allowCompilerAdd) { if (TestAttr(Attribute::COMPILER_ADD) && !TestAttr(Attribute::IS_CLONED_SOURCE_CODE)) { return false; } } else if (TestAttr(Attribute::COMPILER_ADD) && begin.IsZero()) { // Should not diagnose when the position is empty even the 'allowCompilerAdd' flag is true. return false; } // Ignore macro added nodes. if (TestAttr(Attribute::MACRO_INVOKE_FUNC) || TestAttr(Attribute::MACRO_INVOKE_BODY)) { return false; } if (auto e = DynamicCast(this); e) { if (e->sourceExpr) { return false; } } return true; } bool Node::IsSamePackage(const Node& other) const { Ptr otherFile = other.curFile; if (!curFile || !otherFile) { return true; } Ptr curPackage = curFile->curPackage; Ptr otherPackage = otherFile->curPackage; if (!curPackage || !otherPackage) { return true; } return curPackage == otherPackage; } void SubscriptExpr::Clear() noexcept { RecoverToSubscriptExpr(*this); baseExpr->Clear(); for (auto& indexExpr : indexExprs) { indexExpr->Clear(); } commaPos.clear(); Expr::Clear(); } void AssignExpr::Clear() noexcept { RecoverToAssignExpr(*this); Expr::Clear(); } void UnaryExpr::Clear() noexcept { RecoverToUnaryExpr(*this); Expr::Clear(); expr->Clear(); } void BinaryExpr::Clear() noexcept { RecoverToBinaryExpr(*this); Expr::Clear(); leftExpr->Clear(); rightExpr->Clear(); } void ParenExpr::Clear() noexcept { Expr::Clear(); expr->Clear(); } bool RefType::IsGenericThisType() const { if (auto cd = DynamicCast(ref.target); cd && cd->generic && !cd->generic->typeParameters.empty() && ref.identifier == "This") { return true; } return false; } std::set> InheritableDecl::GetSuperInterfaceTys() const { std::set> ret; for (auto& types : inheritedTypes) { if (types && types->ty && types->ty->kind == TypeKind::TYPE_INTERFACE) { ret.insert(RawStaticCast(types->ty)); } } return ret; } std::vector> InheritableDecl::GetStableSuperInterfaceTys() const { auto cmp = [](const Ptr ty1, const Ptr ty2) { return CompTyByNames(ty1, ty2); }; std::set, decltype(cmp)> ret(cmp); for (auto& types : inheritedTypes) { if (types && types->ty && types->ty->kind == TypeKind::TYPE_INTERFACE) { ret.emplace(RawStaticCast(types->ty)); } } return std::vector>(ret.begin(), ret.end()); } std::vector> InheritableDecl::GetAllSuperDecls() { std::set> visited; // to avoid multiple paths or cycle std::vector> ret; // to guarantee order std::queue> workList; workList.push(this); if (auto cd = DynamicCast(this)) { visited.emplace(cd); ret.emplace_back(cd); } while (!workList.empty()) { auto curDecl = workList.front(); workList.pop(); for (auto& it : curDecl->inheritedTypes) { if (auto clsTy = DynamicCast(it->ty); clsTy && visited.count(clsTy->declPtr) == 0) { workList.push(clsTy->declPtr); visited.emplace(clsTy->declPtr); ret.emplace_back(clsTy->declPtr); } else if (auto interfaceTy = DynamicCast(it->ty); interfaceTy && visited.count(interfaceTy->declPtr) == 0) { workList.push(interfaceTy->declPtr); visited.emplace(interfaceTy->declPtr); ret.emplace_back(interfaceTy->declPtr); } } } return ret; } std::vector> Decl::GetMemberDeclPtrs() const { std::vector> results; if (auto cd = DynamicCast(this); cd) { for (auto& decl : cd->body->decls) { results.push_back(decl.get()); } } else if (auto id = DynamicCast(this); id) { for (auto& decl : id->body->decls) { results.push_back(decl.get()); } } else if (auto sd = DynamicCast(this); sd) { for (auto& decl : sd->body->decls) { results.push_back(decl.get()); } } else if (auto ed = DynamicCast(this); ed) { for (auto& constructor : ed->constructors) { results.emplace_back(constructor.get()); } for (auto& decl : ed->members) { results.push_back(decl.get()); } } else if (auto exd = DynamicCast(this); exd) { for (auto& decl : exd->members) { results.push_back(decl.get()); } } return results; } void Node::SetTarget(Ptr target) { switch (astKind) { case ASTKind::REF_TYPE: { RawStaticCast(this)->ref.target = target; break; } case ASTKind::REF_EXPR: { RawStaticCast(this)->ref.target = target; break; } case ASTKind::QUALIFIED_TYPE: { RawStaticCast(this)->target = target; break; } case ASTKind::MEMBER_ACCESS: { RawStaticCast(this)->target = target; break; } case ASTKind::MACRO_EXPAND_DECL: { RawStaticCast(this)->invocation.target = target; break; } case ASTKind::MACRO_EXPAND_EXPR: { RawStaticCast(this)->invocation.target = target; break; } case ASTKind::MACRO_EXPAND_PARAM: { RawStaticCast(this)->invocation.target = target; break; } default: return; } } Ptr Node::GetTarget() const { switch (astKind) { case ASTKind::REF_TYPE: { return RawStaticCast(this)->ref.target; } case ASTKind::REF_EXPR: { return RawStaticCast(this)->ref.target; } case ASTKind::QUALIFIED_TYPE: { return RawStaticCast(this)->target; } case ASTKind::MEMBER_ACCESS: { return RawStaticCast(this)->target; } case ASTKind::MACRO_EXPAND_DECL: { return RawStaticCast(this)->invocation.target; } case ASTKind::MACRO_EXPAND_EXPR: { return RawStaticCast(this)->invocation.target; } case ASTKind::MACRO_EXPAND_PARAM: { return RawStaticCast(this)->invocation.target; } default: return nullptr; } } std::vector> Node::GetTargets() const { switch (astKind) { case ASTKind::REF_TYPE: { return RawStaticCast(this)->ref.targets; } case ASTKind::REF_EXPR: { return RawStaticCast(this)->ref.targets; } case ASTKind::MEMBER_ACCESS: { auto targetDecls = RawStaticCast(this)->targets; std::vector> decls(targetDecls.begin(), targetDecls.end()); return decls; } default: return {}; } } /** * Get a MacroInvocation ptr. * @return MacroInvocation ptr if a node is MacroExpandExpr or MacroExpandDecl, * nullptr otherwise. */ Ptr Node::GetConstInvocation() const { if (this->astKind == ASTKind::MACRO_EXPAND_EXPR) { auto mc = RawStaticCast(this); return &(mc->invocation); } if (this->astKind == ASTKind::MACRO_EXPAND_DECL) { auto mc = RawStaticCast(this); return &(mc->invocation); } if (this->astKind == ASTKind::MACRO_EXPAND_PARAM) { auto mc = RawStaticCast(this); return &(mc->invocation); } return nullptr; } bool MacroInvocation::IsIfAvailable() const { return fullName == IF_AVAILABLE; } Ptr MacroExpandExpr::GetNamedArg() const { if (invocation.IsIfAvailable() && !invocation.nodes.empty()) { return StaticCast(invocation.nodes[0].get()); } return {}; } Ptr MacroExpandExpr::GetLambda(size_t i) const { if (invocation.IsIfAvailable() && invocation.nodes.size() > i) { return StaticCast(invocation.nodes[i + 1].get()); } return {}; } std::tuple, OwnedPtr, OwnedPtr> MacroExpandExpr::Decompose() { return {OwnedPtr(StaticCast(std::move(invocation.nodes[0].release()))), OwnedPtr(StaticCast(std::move(invocation.nodes[1].release()))), OwnedPtr(StaticCast(std::move(invocation.nodes[2].release())))}; } /** * Get a MacroInvocation ptr. * @return MacroInvocation ptr if a node is MacroExpandExpr or MacroExpandDecl, * nullptr otherwise. */ Ptr Node::GetInvocation() { if (this->astKind == ASTKind::MACRO_EXPAND_EXPR) { auto mc = RawStaticCast(this); return &(mc->invocation); } if (this->astKind == ASTKind::MACRO_EXPAND_DECL) { auto mc = RawStaticCast(this); return &(mc->invocation); } if (this->astKind == ASTKind::MACRO_EXPAND_PARAM) { auto mc = RawStaticCast(this); return &(mc->invocation); } return nullptr; } /** * Get the new Position of macrocall in curfile by originPos before the macro is expanded, for lsp. * @return new Position of macrocall in curfile if the Node is MacroExpandExpr/MacroExpandDecl or in macrocall, * INVALID_POSITION otherwise. */ Position Node::GetMacroCallNewPos(const Position& originPos) { Ptr pInvocation = nullptr; if (this->isInMacroCall && this->curMacroCall) { Ptr tempNode = this->curMacroCall; // Get outermost macrocall. while (tempNode->curMacroCall) { tempNode = tempNode->curMacroCall; } pInvocation = tempNode->GetInvocation(); } if (this->IsMacroCallNode()) { Ptr tempNode = this; // Get outermost macrocall. while (tempNode->curMacroCall) { tempNode = tempNode->curMacroCall; } pInvocation = tempNode->GetInvocation(); } if (!pInvocation || pInvocation->originPosMap.empty() || pInvocation->origin2newPosMap.empty()) { return INVALID_POSITION; } auto key = static_cast(originPos.Hash32()); if (pInvocation->originPosMap.lower_bound(key) == pInvocation->originPosMap.end()) { return INVALID_POSITION; } auto newkey = pInvocation->originPosMap.lower_bound(key)->second.Hash64(); if (pInvocation->origin2newPosMap.find(newkey) != pInvocation->origin2newPosMap.cend()) { return pInvocation->origin2newPosMap.at(newkey); } return INVALID_POSITION; } /** * Get the sourcePos of macrocall by originPos in curfile. * @return the sourcePos in macrocall file if the Node is expanded from macrocall, * originPos in curfile otherwise. */ Position Node::GetMacroCallPos(Position originPos, bool isLowerBound) const { if (this->TestAttr(Attribute::MACRO_EXPANDED_NODE) && this->curMacroCall) { auto pInvocation = this->curMacroCall->GetConstInvocation(); if (pInvocation && !IsPureAnnotation(*pInvocation)) { return GetMacroSourcePos(*pInvocation, originPos, isLowerBound); } } auto pInvocation = this->GetConstInvocation(); if (pInvocation) { if (this->begin.fileID != originPos.fileID) { // The original position and macrocall are not in the same file. return originPos; } // The original position and macrocall should be in the same file. return GetMacroSourcePos(*pInvocation, originPos, isLowerBound); } return originPos; } /** * Get the begin Position of the Node. * @return begin Position in macrocall file if the Node is expanded from macrocall, * begin position in curfile otherwise. */ Position Node::GetBegin() const { return this->GetMacroCallPos(this->begin); } /** * Get the end Position of the Node. * @return end Position in macrocall file if the Node is expanded from macrocall, * end position in curfile otherwise. */ Position Node::GetEnd() const { auto beginPos = this->GetMacroCallPos(this->begin); auto endPos = this->GetMacroCallPos(this->end, true); // The fileID of position may be different, may come from the macro definition or from the macrocall. if (beginPos.fileID != endPos.fileID) { endPos = beginPos + 1; } return endPos; } size_t NameReferenceExpr::OuterArgSize() const { if (auto ce = DynamicCast(callOrPattern)) { return ce->args.size(); } else if (auto pat = DynamicCast(callOrPattern)) { return pat->patterns.size(); } return 0; } /** * Get the field Position of the MemberAccess. * @return field Position in macrocall file if the MemberAccess is expanded from macrocall, * field position in curfile otherwise. */ Position MemberAccess::GetFieldPos() const { return this->GetMacroCallPos(this->field.Begin()); } /** * Get the identifier Position of the RefExpr. * @return identifier Position in macrocall file if the RefExpr is expanded from macrocall, * identifier position in curfile otherwise. */ Position RefExpr::GetIdentifierPos() const { return this->GetMacroCallPos(this->ref.identifier.Begin()); } /** * Get the field Position of the QualifiedType. * @return field Position in macrocall file if the QualifiedType is expanded from macrocall, * field position in curfile otherwise. */ Position QualifiedType::GetFieldPos() const { return this->GetMacroCallPos(this->field.Begin()); } bool Decl::IsBuiltIn() const { return astKind == ASTKind::BUILTIN_DECL; } /** * Get the identifier Position of the Decl. * @return identifier Position in macrocall file if the Decl is expanded from macrocall, * identifier position in curfile otherwise. */ Position Decl::GetIdentifierPos() const { return this->GetMacroCallPos(this->identifier.Begin()); } Ptr Decl::GetGeneric() const { if (auto fd = DynamicCast(this); fd && fd->funcBody) { if (fd->funcBody->generic) { return fd->funcBody->generic.get(); } else if (fd->funcBody->parentEnum != nullptr && fd->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { return fd->funcBody->parentEnum->generic.get(); } return nullptr; } if (auto vd = DynamicCast(this); vd && vd->outerDecl && vd->outerDecl->astKind == ASTKind::ENUM_DECL) { return StaticAs(vd->outerDecl)->generic.get(); } return generic.get(); } bool Decl::IsExportedDecl() const { if (TestAnyAttr(Attribute::PUBLIC, Attribute::PROTECTED)) { return true; } if (TestAttr(Attribute::INTERNAL)) { if (curFile && curFile->curPackage) { return !curFile->curPackage->noSubPkg; } else { return true; } } if (TestAttr(Attribute::PRIVATE)) { return false; } // When the decl is `extend A`, B may be decl without modifiers such as GenericParamDecl, BuiltinDecl. // In this case, they must be exported for extend's extendType checking. return true; } bool Decl::IsConst() const { if (auto vd = DynamicCast(this); vd) { return vd->isConst; } else if (auto fd = DynamicCast(this); fd) { return fd->isConst; } else if (auto pcd = DynamicCast(this); pcd) { return pcd->isConst; } return false; } Ptr Decl::GetDesugarDecl() const { if (auto macroDecl = DynamicCast(this); macroDecl) { return macroDecl->desugarDecl.get(); } else if (auto mainDecl = DynamicCast(this); mainDecl) { return mainDecl->desugarDecl.get(); } else if (auto funcParam = DynamicCast(this); funcParam) { return funcParam->desugarDecl.get(); } return nullptr; } bool Decl::IsCommonOrPlatform() const { return TestAttr(AST::Attribute::COMMON) || TestAttr(AST::Attribute::PLATFORM); } bool Decl::IsCommonMatchedWithPlatform() const { return TestAttr(AST::Attribute::COMMON) && platformImplementation; } /** * For debug, get the original Position of the node if it is from MacroCall in curfile, curPos otherwise. */ Position Node::GetDebugPos(const Position& curPos) const { auto pInvocation = this->GetConstInvocation(); if (!pInvocation) { // If the node is not macrocall node, then check whether it is expanded from macrocall node or not. if (curPos == INVALID_POSITION || !this->TestAttr(Attribute::MACRO_EXPANDED_NODE) || !this->curMacroCall) { return curPos; } // Current node is expanded from macrocall node. pInvocation = this->curMacroCall->GetConstInvocation(); if (!pInvocation || pInvocation->macroDebugMap.empty()) { return curPos; } } auto key = static_cast(curPos.column); if (pInvocation->macroDebugMap.lower_bound(key) == pInvocation->macroDebugMap.end()) { return curPos; } return pInvocation->macroDebugMap.lower_bound(key)->second; } const std::string& Node::GetFullPackageName() const { if (auto decl = DynamicCast(this); decl && !decl->fullPackageName.empty()) { return decl->fullPackageName; } if (curFile && curFile->curPackage) { return curFile->curPackage->fullPackageName; } return EMPTY_PACKAGE_NAME; } std::string PackageSpec::GetPackageName() const { std::stringstream ss; for (size_t i{0}; i < prefixPaths.size(); ++i) { ss << prefixPaths[i]; if (i == 0 && hasDoubleColon) { ss << TOKENS[static_cast(TokenKind::DOUBLE_COLON)]; } else { ss << TOKENS[static_cast(TokenKind::DOT)]; } } ss << packageName.Val(); return ss.str(); } std::string ImportContent::GetPrefixPath() const { std::stringstream ss; for (size_t i{0}; i < prefixPaths.size(); ++i) { ss << prefixPaths[i]; if (i == prefixPaths.size() - 1) { break; } if (i == 0 && hasDoubleColon) { // valid import do not end with :: ss << TOKENS[static_cast(TokenKind::DOUBLE_COLON)]; } else { ss << TOKENS[static_cast(TokenKind::DOT)]; } } return ss.str(); } std::string ImportContent::GetImportedPackageName() const { std::stringstream ss; for (size_t i{0}; i < prefixPaths.size(); ++i) { ss << prefixPaths[i]; // do not add . if this is the last of import xxx.*, because * is not part of package name if (kind == ImportKind::IMPORT_ALL && i + 1 == prefixPaths.size()) { continue; } if (i == 0 && hasDoubleColon) { ss << TOKENS[static_cast(TokenKind::DOUBLE_COLON)]; } else { ss << TOKENS[static_cast(TokenKind::DOT)]; } } if (kind != ImportKind::IMPORT_ALL) { ss << identifier.Val(); } return ss.str(); } std::string ImportContent::ToString() const { std::function toString = [](auto& ss, auto& content) { for (const auto& prefix : content.prefixPaths) { ss << prefix << "."; } if (content.kind != ImportKind::IMPORT_MULTI) { ss << content.identifier.Val(); if (content.kind == ImportKind::IMPORT_ALIAS) { ss << " as " << content.aliasName.Val(); } } return ss.str(); }; std::stringstream ss; toString(ss, *this); if (kind != ImportKind::IMPORT_MULTI) { return ss.str(); } ss << "{" << std::endl; for (const auto& item : items) { ss << " "; toString(ss, item); ss << std::endl; } ss << "}"; return ss.str(); } std::string FeatureId::ToString() const { std::stringstream ss; size_t idx = 0; for (const auto& ident : this->identifiers) { ss << ident.Val(); if (idx < dotPoses.size()) { ss << "."; idx++; } } return ss.str(); } bool ExtendDecl::IsExportedDecl() const { // ExtendedType Check (Direct and Interface Extensions): If B in extend A isn't exported, the extendDecl should // not be exported. For imported decl, extendedType may be nullptr and not ready. if (extendedType != nullptr) { for (auto& it : extendedType->GetTypeArgs()) { if (it && it->GetTarget() && !it->GetTarget()->IsExportedDecl()) { return false; } } } auto extendedDecl = Ty::GetDeclPtrOfTy(ty); bool isInSamePkg = extendedDecl && extendedDecl->fullPackageName == fullPackageName; auto isUpperBoundExport = [this]() { bool isUpperboundAllExported = true; for (auto& tp : generic->genericConstraints) { CJC_NULLPTR_CHECK(tp); for (auto& up : tp->upperBounds) { CJC_NULLPTR_CHECK(up); if (up->GetTarget() == nullptr) { continue; } isUpperboundAllExported = up->GetTarget()->IsExportedDecl() && isUpperboundAllExported; } } return isUpperboundAllExported; }; // Direct Extensions Check: if (inheritedTypes.empty()) { // Rule 1: In `package std.core`, direct extensions of types visible outside the package are exported. if (fullPackageName == "std.core") { return true; } if (isInSamePkg) { // Rule 2: When direct extensions are defined in the same `package` as the extended type, whether the // extension is exported is determined by the lowest access level of the type used in the extended type and // the generic constraints (if any). if (!generic) { return extendedDecl->IsExportedDecl(); } return extendedDecl->IsExportedDecl() && isUpperBoundExport(); } else { // Rule 3: When the direct extension is in a different `package` from the declaration of the type being // extended, the extension is never exported and can only be used in the current `package`. return false; } } // Interface Extensions Check: if (isInSamePkg) { // Rule 1: When the interface extension and the extended type are in the same `package`, the extension is // exported together with the extended type and is not affected by the access level of the interface type. return extendedDecl->IsExportedDecl(); } // Rule 2: When an interface extension is in a different `package` from the type being extended, whether the // interface extension is exported is determined by the smallest access level of the type used in the // interface type and the generic constraints (if any). bool isInterfaceAllExported = false; for (auto& inhertType : inheritedTypes) { if (inhertType->GetTarget() == nullptr) { continue; } if (inhertType->GetTarget()->IsExportedDecl()) { isInterfaceAllExported = true; break; } } if (!generic) { return isInterfaceAllExported; } return isInterfaceAllExported && isUpperBoundExport(); } bool PropDecl::IsExportedDecl() const { if (!outerDecl || outerDecl->astKind != ASTKind::EXTEND_DECL) { return Decl::IsExportedDecl(); } auto extend = StaticCast(outerDecl); auto extendedDecl = Ty::GetDeclPtrOfTy(extend->ty); // If extend and extended decleration in same package, all member of extend will be exported. if (!extendedDecl || extendedDecl->fullPackageName == extend->fullPackageName) { return Decl::IsExportedDecl(); } // If extend is direct extension, all member will be not exported. if (extend->inheritedTypes.empty()) { return false; } return Decl::IsExportedDecl() && TestAttr(Attribute::INTERFACE_IMPL); } bool FuncDecl::IsExportedDecl() const { if (!outerDecl || outerDecl->astKind != ASTKind::EXTEND_DECL) { return Decl::IsExportedDecl(); } auto extend = StaticCast(outerDecl); auto extendedDecl = Ty::GetDeclPtrOfTy(extend->ty); // If extend and extended decleration in same package, all member of extend will be exported. if (!extendedDecl || extendedDecl->fullPackageName == extend->fullPackageName) { return Decl::IsExportedDecl(); } // If extend is direct extension, all member will be not exported. if (extend->inheritedTypes.empty()) { return false; } return Decl::IsExportedDecl() && TestAttr(Attribute::INTERFACE_IMPL); } bool FuncDecl::IsOpen() const noexcept { if (!outerDecl || !outerDecl->IsOpen() || TestAttr(Attribute::STATIC)) { return false; } if (TestAnyAttr(Attribute::OPEN, Attribute::ABSTRACT)) { return true; } return !TestAttr(AST::Attribute::IMPORTED) && !funcBody->body; } bool PropDecl::IsOpen() const noexcept { if (!outerDecl || !outerDecl->IsOpen() || TestAttr(Attribute::STATIC)) { return false; } if (TestAnyAttr(Attribute::OPEN, Attribute::ABSTRACT)) { return true; } return !TestAttr(AST::Attribute::IMPORTED) && getters.empty() && setters.empty(); } } // namespace Cangjie cangjie_compiler-1.0.7/src/AST/PrintNode.cpp000066400000000000000000001640461510705540100206460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements AST and related data print apis. */ #include "cangjie/AST/PrintNode.h" #if !defined(NDEBUG) || defined(CMAKE_ENABLE_ASSERT) #include #include #include "cangjie/AST/ASTCasting.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/Match.h" #include "cangjie/Basic/Print.h" #include "cangjie/Basic/StringConvertor.h" #include "cangjie/Utils/Utils.h" #endif namespace Cangjie { using namespace AST; // Enable printNode for debug and CI version. #if !defined(NDEBUG) || defined(CMAKE_ENABLE_ASSERT) using namespace Utils; using namespace Meta; namespace { const unsigned ONE_INDENT = 1u; const unsigned TWO_INDENT = 2u; const unsigned THREE_INDENT = 3u; #define UNKNOWN_TY (ANSI_COLOR_RED + "unknown" + ANSI_COLOR_RESET) void PrintModifiers(const Decl& decl, unsigned indent) { std::string str = "Modifiers:"; for (auto& i : decl.modifiers) { str += " " + std::string(TOKENS[static_cast(i.modifier)]); } if (!decl.modifiers.empty()) { PrintIndent(indent + ONE_INDENT, str); } } void PrintTarget(unsigned indent, const Decl& target, std::string addition = "target") { if (target.mangledName.empty()) { PrintIndent(indent, addition + ": ptr:", &target); } else { PrintIndent(indent, addition + ": mangledName:", "\"" + target.exportId + "\""); } } void PrintBasic(unsigned indent, const Node& node) { // Basic info: std::string filePath = node.curFile ? node.curFile->filePath : "not in file"; PrintIndent(indent, "curFile:", filePath); PrintIndent(indent, "position:", node.begin.ToString(), node.end.ToString()); PrintIndent(indent, "scopeName:", "\"" + node.scopeName + "\""); PrintIndent(indent, "ty:", node.ty->String()); auto fullPkgName = node.GetFullPackageName(); if (!fullPkgName.empty()) { PrintIndent(indent, "fullPackageName:", fullPkgName); } else { PrintIndent(indent, "fullPackageName is empty"); } PrintIndent(indent, "attributes:", node.GetAttrs().ToString()); if (!node.exportId.empty()) { PrintIndent(indent, "exportId:", "\"" + node.exportId + "\""); } if (auto d = DynamicCast(&node)) { PrintIndent(indent, "linkage:", static_cast(d->linkage), ", isConst:", static_cast(d->IsConst())); if (!d->mangledName.empty()) { PrintIndent(indent, "mangledName:", "\"" + d->mangledName + "\""); } if (d->annotationsArray) { PrintNode(d->annotationsArray.get(), indent, "annotationsArray"); } if (d->outerDecl) { PrintTarget(indent, *d->outerDecl, "outerDecl"); } } if (!node.comments.IsEmpty()) { PrintIndent(indent, "comments: " + node.comments.ToString()); } } void PrintIndentTokens(unsigned indent, const std::vector& args) { std::stringstream ss; PrintIndent(indent, "pos: ", args.front().Begin().ToString(), args.back().End().ToString()); for (auto& it : args) { if (it.kind == TokenKind::NL) { ss << ";"; } else { ss << it.Value(); } } PrintIndent(indent, "\"" + ss.str() + "\""); } void PrintMacroInvocation(unsigned indent, const MacroInvocation& invocation) { if (invocation.attrs.empty()) { PrintIndent(indent + ONE_INDENT, "// no attributes"); } else { PrintIndent(indent + ONE_INDENT, "attrs {"); PrintIndentTokens(indent + TWO_INDENT, invocation.attrs); PrintIndent(indent + ONE_INDENT, "}"); } if (invocation.args.empty()) { PrintIndent(indent + ONE_INDENT, "// no arguments"); } else { PrintIndent(indent + ONE_INDENT, "args {"); PrintIndentTokens(indent + TWO_INDENT, invocation.args); PrintIndent(indent + ONE_INDENT, "}"); } if (invocation.decl) { PrintNode(invocation.decl, indent + ONE_INDENT, "decl"); } } void PrintPackage(unsigned indent, const Package& package) { PrintIndent(indent, "Package:", package.fullPackageName, "{"); PrintIndent(indent + ONE_INDENT, "noSubPkg:", package.noSubPkg); for (auto& it : package.files) { PrintNode(it.get(), indent + ONE_INDENT); } PrintIndent(indent + ONE_INDENT, "genericInstantiatedDecls {"); for (auto& it : package.genericInstantiatedDecls) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "}"); PrintIndent(indent + ONE_INDENT, "srcImportedNonGenericDecls {"); for (auto& it : package.srcImportedNonGenericDecls) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "}"); PrintIndent(indent + ONE_INDENT, "inlineFuncDecls {"); for (auto& it : package.inlineFuncDecls) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "}"); PrintIndent(indent, "}"); } void PrintFile(unsigned indent, const File& file) { PrintIndent(indent, "File:", file.fileName, "{"); PrintNode(file.feature.get(), indent + ONE_INDENT, "features"); PrintNode(file.package.get(), indent + ONE_INDENT, "package"); for (auto& it : file.imports) { PrintNode(it.get(), indent + ONE_INDENT); } for (auto& it : file.decls) { if (it->identifier == MAIN_INVOKE) { continue; } PrintNode(it.get(), indent + ONE_INDENT); } for (auto& it : file.exportedInternalDecls) { PrintNode(it.get(), indent + ONE_INDENT, "exportedInternalDecls"); } PrintIndent(indent + ONE_INDENT, "originalMacroCallNodes ["); for (auto& macroNode : file.originalMacroCallNodes) { PrintNode(macroNode.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); PrintIndent(indent, "}"); } void PrintMacroExpandParam(unsigned indent, const AST::MacroExpandParam& macroExpand) { PrintIndent(indent, "MacroExpandParam:", macroExpand.invocation.fullName, "{"); PrintMacroInvocation(indent, macroExpand.invocation); PrintIndent(indent, "}"); } void PrintGenericParamDecl(unsigned indent, const AST::GenericParamDecl& gpd) { PrintIndent(indent, "GenericParamDecl:", gpd.identifier.Val(), "{"); PrintBasic(indent + ONE_INDENT, gpd); PrintIndent(indent, "}"); } void PrintAnnotation(unsigned indent, const Annotation& anno) { PrintIndent(indent, "Annotation:", anno.identifier, "{"); PrintBasic(indent + ONE_INDENT, anno); PrintIndent(indent + ONE_INDENT, "args ["); for (auto& arg : anno.args) { PrintNode(arg.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); PrintIndent(indent + ONE_INDENT, "adAnnotation:", anno.adAnnotation); PrintNode(anno.condExpr.get(), indent + ONE_INDENT, "condExpr"); PrintIndent(indent + ONE_INDENT, "target:", anno.target); PrintNode(anno.baseExpr.get(), indent + ONE_INDENT, "baseExpr"); PrintIndent(indent, "}"); } void PrintAnnotations(const Decl& decl, unsigned indent) { PrintIndent(indent, "annotations ["); for (auto& anno : decl.annotations) { PrintAnnotation(indent + ONE_INDENT, *anno.get()); } PrintIndent(indent, "]"); } void PrintFuncParam(unsigned indent, const FuncParam& param) { PrintIndent(indent, "FuncParam:", param.identifier.Val(), "{"); PrintBasic(indent + ONE_INDENT, param); PrintAnnotations(param, indent + ONE_INDENT); PrintNode(param.type.get(), indent + ONE_INDENT, "type"); PrintNode(param.assignment.get(), indent + ONE_INDENT, "assignment"); if (param.desugarDecl) { PrintNode(param.desugarDecl.get(), indent + ONE_INDENT, "desugarDecl"); } PrintIndent(indent, "}"); } void PrintFuncParamList(unsigned indent, const FuncParamList& paramList) { PrintIndent(indent, "FuncParamList {"); if (paramList.params.empty()) { PrintIndent(indent + ONE_INDENT, "// no Params"); } else { for (auto& it : paramList.params) { PrintNode(it.get(), indent + ONE_INDENT); } } PrintIndent(indent, "}"); } void PrintMacroDecl(unsigned indent, const MacroDecl& macroDecl) { PrintIndent(indent, "MacroDecl:", macroDecl.identifier.Val(), "{"); PrintBasic(indent + ONE_INDENT, macroDecl); PrintModifiers(macroDecl, indent); if (macroDecl.desugarDecl) { PrintIndent(indent + ONE_INDENT, "// desugar decl"); PrintNode(macroDecl.desugarDecl.get(), indent + ONE_INDENT); } else { PrintIndent(indent + ONE_INDENT, "// macro body"); PrintNode(macroDecl.funcBody.get(), indent + ONE_INDENT); } PrintIndent(indent, "}"); } void PrintFuncDecl(unsigned indent, const FuncDecl& funcDecl) { PrintIndent(indent, "FuncDecl:", funcDecl.identifier.Val(), funcDecl.identifier.Begin(), funcDecl.identifier.End(), "{"); PrintBasic(indent + ONE_INDENT, funcDecl); if (funcDecl.TestAttr(Attribute::CONSTRUCTOR) && funcDecl.constructorCall == ConstructorCall::SUPER) { if (auto ce = DynamicCast(funcDecl.funcBody->body->body.begin()->get()); ce) { if (ce->TestAttr(Attribute::COMPILER_ADD)) { PrintIndent(indent + ONE_INDENT, "super: implicitSuper"); } else { PrintIndent(indent + ONE_INDENT, "super: explicitSuper"); } } } PrintAnnotations(funcDecl, indent + ONE_INDENT); PrintModifiers(funcDecl, indent); PrintNode(funcDecl.funcBody.get(), indent + ONE_INDENT); PrintIndent(indent, "}"); } void PrintPrimaryCtorDecl(unsigned indent, const PrimaryCtorDecl& primaryCtor) { PrintIndent(indent, "PrimaryCtorDecl:", primaryCtor.identifier.Val(), "{"); PrintBasic(indent + ONE_INDENT, primaryCtor); PrintAnnotations(primaryCtor, indent + ONE_INDENT); PrintModifiers(primaryCtor, indent); PrintNode(primaryCtor.funcBody.get(), indent + ONE_INDENT); PrintIndent(indent, "}"); } void PrintMainDecl(unsigned indent, const MainDecl& mainDecl) { PrintIndent(indent, "MainDecl:", mainDecl.identifier.Val(), "{"); PrintBasic(indent + ONE_INDENT, mainDecl); if (mainDecl.desugarDecl) { PrintNode(mainDecl.desugarDecl.get(), indent + ONE_INDENT, "desugarDecl"); } else { PrintNode(mainDecl.funcBody.get(), indent + ONE_INDENT, "funcBody"); } PrintIndent(indent, "}"); } void PrintGeneric(unsigned indent, const Generic& generic) { PrintIndent(indent, "typeParameters {"); for (auto& it : generic.typeParameters) { PrintNode(it.get(), indent + ONE_INDENT); } PrintIndent(indent, "}"); PrintIndent(indent, "genericConstraints {"); for (auto& it : generic.genericConstraints) { PrintNode(it.get(), indent + ONE_INDENT); } PrintIndent(indent, "}"); } void PrintFuncBody(unsigned indent, const FuncBody& body) { PrintIndent(indent, "FuncBody {"); PrintBasic(indent + ONE_INDENT, body); if (body.generic) { PrintGeneric(indent + ONE_INDENT, *body.generic); } for (auto& it : body.paramLists) { PrintNode(it.get(), indent + ONE_INDENT); } if (!body.retType) { PrintIndent(indent + ONE_INDENT, "// no return type"); } else { PrintIndent(indent + ONE_INDENT, "// return type"); PrintNode(body.retType.get(), indent + ONE_INDENT); } PrintNode(body.body.get(), indent + ONE_INDENT, "body"); PrintIndent(indent, "}"); } void PrintPropDecl(unsigned indent, const PropDecl& propDecl) { PrintIndent(indent, "PropDecl:", propDecl.identifier.Val(), "{"); PrintBasic(indent + ONE_INDENT, propDecl); PrintModifiers(propDecl, indent); PrintNode(propDecl.type.get(), indent + ONE_INDENT, "type"); for (auto& propMemberDecl : propDecl.setters) { PrintNode(propMemberDecl.get(), indent + ONE_INDENT); } for (auto& propMemberDecl : propDecl.getters) { PrintNode(propMemberDecl.get(), indent + ONE_INDENT); } PrintIndent(indent, "}"); } void PrintMacroExpandDecl(unsigned indent, const AST::MacroExpandDecl& macroExpand) { PrintIndent(indent, "MacroExpand:", macroExpand.invocation.fullName, "{"); PrintBasic(indent + ONE_INDENT, macroExpand); PrintMacroInvocation(indent, macroExpand.invocation); PrintIndent(indent, "}"); } void PrintVarWithPatternDecl(unsigned indent, const VarWithPatternDecl& varWithPatternDecl) { PrintIndent(indent, "VarWithPatternDecl {"); PrintBasic(indent + ONE_INDENT, varWithPatternDecl); if (varWithPatternDecl.isConst) { PrintIndent(indent + ONE_INDENT, "Const"); } else { PrintIndent(indent + ONE_INDENT, varWithPatternDecl.isVar ? "Var" : "Let"); } PrintNode(varWithPatternDecl.irrefutablePattern.get(), indent + ONE_INDENT); PrintNode(varWithPatternDecl.type.get(), indent + ONE_INDENT, "type"); PrintNode(varWithPatternDecl.initializer.get(), indent + ONE_INDENT, "initializer"); PrintIndent(indent, "}"); } void PrintVarDecl(unsigned indent, const VarDecl& varDecl) { std::string key = varDecl.isConst ? "const " : varDecl.isVar ? "var " : "let "; PrintIndent(indent, "VarDecl:", key + varDecl.identifier.Val(), "{"); PrintBasic(indent + ONE_INDENT, varDecl); PrintAnnotations(varDecl, indent + ONE_INDENT); PrintModifiers(varDecl, indent); PrintNode(varDecl.type.get(), indent + ONE_INDENT, "type"); PrintNode(varDecl.initializer.get(), indent + ONE_INDENT, "initializer"); PrintIndent(indent, "}"); } void PrintBasicpeAliasDecl(unsigned indent, const TypeAliasDecl& alias) { PrintIndent(indent, "TypeAliasDecl", alias.identifier.Val(), "{"); PrintBasic(indent + ONE_INDENT, alias); if (alias.generic) { PrintGeneric(indent + ONE_INDENT, *alias.generic); } PrintNode(alias.type.get(), indent + ONE_INDENT, "type"); PrintIndent(indent, "}"); } void PrintClassDecl(unsigned indent, const ClassDecl& classDecl) { if (auto attr = HasJavaAttr(classDecl); attr) { std::string argStr = attr.value() == Attribute::JAVA_APP ? "app" : "ext"; PrintIndent(indent, "@Java[\"" + argStr + "\"]"); } PrintIndent(indent, "ClassDecl:", classDecl.identifier.Val(), "{"); PrintBasic(indent + ONE_INDENT, classDecl); PrintAnnotations(classDecl, indent + ONE_INDENT); PrintModifiers(classDecl, indent); if (classDecl.generic) { PrintGeneric(indent + ONE_INDENT, *classDecl.generic); } if (!classDecl.inheritedTypes.empty()) { PrintIndent(indent + ONE_INDENT, "inheritedTypes ["); for (auto& it : classDecl.inheritedTypes) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); } PrintNode(classDecl.body.get(), indent + ONE_INDENT); PrintIndent(indent, "}"); } void PrintInterfaceDecl(unsigned indent, const InterfaceDecl& interfaceDecl) { if (auto attr = HasJavaAttr(interfaceDecl); attr) { PrintIndent(indent, "@Java[\"", attr.value() == Attribute::JAVA_APP ? "app" : "ext", "\"]"); } PrintIndent(indent, "InterfaceDecl:", interfaceDecl.identifier.Val(), "{"); PrintBasic(indent + ONE_INDENT, interfaceDecl); PrintModifiers(interfaceDecl, indent); if (interfaceDecl.generic) { PrintGeneric(indent + ONE_INDENT, *interfaceDecl.generic); } if (!interfaceDecl.inheritedTypes.empty()) { PrintIndent(indent + ONE_INDENT, "inheritedTypes: ["); for (auto& it : interfaceDecl.inheritedTypes) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); } PrintNode(interfaceDecl.body.get(), indent + ONE_INDENT); PrintIndent(indent, "}"); } void PrintEnumDecl(unsigned indent, const EnumDecl& enumDecl) { PrintIndent(indent, "EnumDecl:", enumDecl.identifier.Val(), "{"); PrintBasic(indent + ONE_INDENT, enumDecl); PrintModifiers(enumDecl, indent); if (enumDecl.generic) { PrintGeneric(indent + ONE_INDENT, *enumDecl.generic); } if (!enumDecl.inheritedTypes.empty()) { PrintIndent(indent + ONE_INDENT, "inheritedTypes ["); for (auto& it : enumDecl.inheritedTypes) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); } PrintIndent(indent + ONE_INDENT, "constructors ["); for (auto& it : enumDecl.constructors) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); PrintIndent(indent + ONE_INDENT, "members ["); for (auto& it : enumDecl.members) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); PrintIndent(indent, "}"); } void PrintStructDecl(unsigned indent, const StructDecl& decl) { if (decl.TestAttr(Attribute::C)) { PrintIndent(indent, "@C"); } PrintIndent(indent, "StructDecl:", decl.identifier.Val(), "{"); PrintBasic(indent + ONE_INDENT, decl); PrintAnnotations(decl, indent + ONE_INDENT); PrintModifiers(decl, indent); if (decl.generic) { PrintGeneric(indent + ONE_INDENT, *decl.generic); } PrintIndent(indent + ONE_INDENT, "StructBody {"); PrintNode(decl.body.get(), indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, "}"); PrintIndent(indent, "}"); } void PrintExtendDecl(unsigned indent, const ExtendDecl& ed) { PrintIndent(indent, "ExtendDecl {"); PrintBasic(indent + ONE_INDENT, ed); PrintNode(ed.extendedType.get(), indent + ONE_INDENT, "extendedType"); PrintIndent(indent + ONE_INDENT, "inheritedTypes ["); for (auto& interface : ed.inheritedTypes) { PrintNode(interface.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); if (ed.generic) { PrintGeneric(indent + ONE_INDENT, *ed.generic); } PrintIndent(indent + ONE_INDENT, "extendMembers ["); for (auto& it : ed.members) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); PrintIndent(indent, "}"); } void PrintIfExpr(unsigned indent, const IfExpr& expr) { PrintIndent(indent, "IfExpr {"); PrintBasic(indent + ONE_INDENT, expr); PrintNode(expr.condExpr.get(), indent + ONE_INDENT, "condExpr"); PrintNode(expr.thenBody.get(), indent + ONE_INDENT, "thenBody"); if (expr.hasElse) { PrintNode(expr.elseBody.get(), indent + ONE_INDENT, "elseBody"); } PrintIndent(indent, "}"); } void PrintMatchCase(unsigned indent, const MatchCase& mc) { PrintIndent(indent, "MatchCase: {"); PrintBasic(indent + ONE_INDENT, mc); PrintIndent(indent + ONE_INDENT, "patterns {"); for (auto& it : mc.patterns) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "}"); PrintNode(mc.patternGuard.get(), indent + ONE_INDENT, "patternGuard"); PrintIndent(indent + ONE_INDENT, "exprOrDecls ["); for (auto& it : mc.exprOrDecls->body) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); PrintIndent(indent, "}"); } void PrintMatchExpr(unsigned indent, const MatchExpr& expr) { PrintIndent(indent, "MatchExpr:", "{"); PrintBasic(indent + ONE_INDENT, expr); if (expr.matchMode) { PrintIndent(indent + ONE_INDENT, "selector: {"); PrintNode(expr.selector.get(), indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, "}"); PrintIndent(indent + ONE_INDENT, "matchCases: ["); for (auto& matchCase : expr.matchCases) { PrintMatchCase(indent + TWO_INDENT, *StaticCast(matchCase.get())); } PrintIndent(indent + ONE_INDENT, "]"); } else { for (auto& matchCaseOther : expr.matchCaseOthers) { PrintIndent(indent + ONE_INDENT, "matchCases: {"); PrintNode(matchCaseOther->matchExpr.get(), indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, "}"); PrintIndent(indent + ONE_INDENT, "exprOrDecls: ["); for (auto& it : matchCaseOther->exprOrDecls->body) { PrintNode(it.get(), indent + ONE_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); } } PrintIndent(indent, "}"); } void PrintTryExpr(unsigned indent, const TryExpr& expr) { PrintIndent(indent, "TryExpr", "{"); PrintBasic(indent + ONE_INDENT, expr); if (!expr.resourceSpec.empty()) { PrintIndent(indent + ONE_INDENT, "ResourceSpec", "{"); for (auto& i : expr.resourceSpec) { PrintNode(i.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "}"); } PrintIndent(indent + ONE_INDENT, "TryBlock", "{"); PrintNode(expr.tryBlock.get(), indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, "}"); for (unsigned int i = 0; i < expr.catchBlocks.size(); i++) { PrintIndent(indent + ONE_INDENT, "Catch", "{"); PrintIndent(indent + TWO_INDENT, "CatchPattern", "{"); PrintNode(expr.catchPatterns[i].get(), indent + THREE_INDENT); PrintIndent(indent + TWO_INDENT, "}"); PrintIndent(indent + TWO_INDENT, "CatchBlock", "{"); PrintNode(expr.catchBlocks[i].get(), indent + THREE_INDENT); PrintIndent(indent + TWO_INDENT, "}"); PrintIndent(indent + ONE_INDENT, "}"); } for (const auto& handler : expr.handlers) { PrintIndent(indent + ONE_INDENT, "Handle", "{"); PrintIndent(indent + TWO_INDENT, "HandlePattern", "{"); PrintIndent(indent + THREE_INDENT, "CommandTypePattern:"); PrintNode(handler.commandPattern.get(), indent + THREE_INDENT); PrintIndent(indent + TWO_INDENT, "}"); PrintIndent(indent + TWO_INDENT, "HandleBlock", "{"); PrintNode(handler.block.get(), indent + THREE_INDENT); PrintIndent(indent + TWO_INDENT, "}"); PrintIndent(indent + TWO_INDENT, "DesugaredLambda", "{"); PrintNode(handler.desugaredLambda.get(), indent + THREE_INDENT); PrintIndent(indent + TWO_INDENT, "}"); PrintIndent(indent + ONE_INDENT, "}"); } PrintIndent(indent + ONE_INDENT, "FinallyBlock", "{"); PrintNode(expr.finallyBlock.get(), indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, "}"); if (!expr.handlers.empty()) { PrintIndent(indent + ONE_INDENT, "TryLambda", "{"); PrintNode(expr.tryLambda.get(), indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, "}"); PrintIndent(indent + ONE_INDENT, "FinallyLambda", "{"); PrintNode(expr.finallyLambda.get(), indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, "}"); } PrintIndent(indent, "}"); } void PrintThrowExpr(unsigned indent, const ThrowExpr& expr) { PrintIndent(indent, "ThrowExpr", "{"); PrintBasic(indent + ONE_INDENT, expr); PrintNode(expr.expr.get(), indent + ONE_INDENT, "expr"); PrintIndent(indent, "}"); } void PrintPerformExpr(unsigned indent, const PerformExpr& expr) { PrintIndent(indent, "PerformExpr", "{"); PrintBasic(indent + ONE_INDENT, expr); PrintNode(expr.expr.get(), indent + ONE_INDENT, "PerformExpr"); PrintIndent(indent, "}"); } void PrintResumeExpr(unsigned indent, const ResumeExpr& expr) { PrintIndent(indent, "ResumeExpr", "{"); PrintBasic(indent + ONE_INDENT, expr); PrintNode(expr.withExpr.get(), indent + ONE_INDENT, "WithExpr"); PrintNode(expr.throwingExpr.get(), indent + ONE_INDENT, "ThrowingExpr"); PrintIndent(indent, "}"); } void PrintReturnExpr(unsigned indent, const ReturnExpr& expr) { PrintIndent(indent, "ReturnExpr", "{"); PrintBasic(indent + ONE_INDENT, expr); PrintNode(expr.expr.get(), indent + ONE_INDENT, "expr"); PrintIndent(indent, "}"); } void PrintJumpExpr(unsigned indent, const JumpExpr& expr) { std::string str1 = expr.isBreak ? "Break" : "Continue"; PrintIndent(indent, "JumpExpr:", str1); PrintBasic(indent + ONE_INDENT, expr); } void PrintForInExpr(unsigned indent, const ForInExpr& expr) { PrintIndent(indent, "ForInExpr {"); PrintBasic(indent + ONE_INDENT, expr); PrintIndent(indent + ONE_INDENT, "pattern", ":"); PrintNode(expr.pattern.get(), indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, "inExpression", ":"); PrintNode(expr.inExpression.get(), indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, "patternGuard", ":"); PrintNode(expr.patternGuard.get(), indent + TWO_INDENT); PrintNode(expr.body.get(), indent + ONE_INDENT, "body"); PrintIndent(indent, "}"); } void PrintWhileExpr(unsigned indent, const WhileExpr& expr) { PrintIndent(indent, "WhileExpr {"); PrintBasic(indent + ONE_INDENT, expr); PrintIndent(indent + ONE_INDENT, "condExpr", "{"); PrintNode(expr.condExpr.get(), indent + TWO_INDENT, "condition expr"); PrintIndent(indent + ONE_INDENT, "}"); PrintNode(expr.body.get(), indent + ONE_INDENT, "While-Body"); PrintIndent(indent, "}"); } void PrintDoWhileExpr(unsigned indent, const DoWhileExpr& expr) { PrintIndent(indent, "DoWhileExpr {"); PrintBasic(indent + ONE_INDENT, expr); PrintNode(expr.body.get(), indent + ONE_INDENT, "DoWhile-Body"); PrintIndent(indent + ONE_INDENT, "condExpr", "{"); PrintNode(expr.condExpr.get(), indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, "}"); PrintIndent(indent, "}"); } void PrintAssignExpr(unsigned indent, const AssignExpr& expr) { const std::unordered_map op2str = { {TokenKind::ASSIGN, "'='"}, {TokenKind::ADD_ASSIGN, "'+='"}, {TokenKind::SUB_ASSIGN, "'-='"}, {TokenKind::MUL_ASSIGN, "'*='"}, {TokenKind::EXP_ASSIGN, "'**='"}, {TokenKind::DIV_ASSIGN, "'/='"}, {TokenKind::MOD_ASSIGN, "'%%='"}, {TokenKind::AND_ASSIGN, "'&&='"}, {TokenKind::OR_ASSIGN, "'||='"}, {TokenKind::BITAND_ASSIGN, "'&='"}, {TokenKind::BITOR_ASSIGN, "'|='"}, {TokenKind::BITXOR_ASSIGN, "'^='"}, {TokenKind::LSHIFT_ASSIGN, "'<<='"}, {TokenKind::RSHIFT_ASSIGN, "'>>='"}, }; PrintIndent(indent, "AssignExpr", "{"); PrintBasic(indent + ONE_INDENT, expr); std::string str1; auto result = op2str.find(expr.op); if (result != op2str.end()) { str1 = result->second; } PrintIndent(indent + ONE_INDENT, str1); PrintNode(expr.leftValue.get(), indent + ONE_INDENT, "leftValue"); PrintNode(expr.rightExpr.get(), indent + ONE_INDENT, "rightExpr"); if (expr.overflowStrategy != OverflowStrategy::NA) { PrintIndent(indent + ONE_INDENT, "overflowStrategy:", OverflowStrategyName(expr.overflowStrategy)); } PrintIndent(indent, "}"); } void PrintIncOrDecExpr(unsigned indent, const IncOrDecExpr& expr) { PrintIndent(indent, "IncOrDecExpr", "{"); PrintBasic(indent + ONE_INDENT, expr); std::string str1 = (expr.op == TokenKind::INCR) ? "++" : "--"; PrintIndent(indent + ONE_INDENT, str1); PrintNode(expr.expr.get(), indent + ONE_INDENT); if (expr.overflowStrategy != OverflowStrategy::NA) { PrintIndent(indent + ONE_INDENT, "overflowStrategy:", OverflowStrategyName(expr.overflowStrategy)); } PrintIndent(indent, "}"); } void PrintUnaryExpr(unsigned indent, const UnaryExpr& unaryExpr) { PrintIndent(indent, "UnaryExpr", "{", TOKENS[static_cast(unaryExpr.op)]); PrintBasic(indent + ONE_INDENT, unaryExpr); PrintNode(unaryExpr.expr.get(), indent + ONE_INDENT); if (unaryExpr.overflowStrategy != OverflowStrategy::NA) { PrintIndent(indent + ONE_INDENT, "overflowStrategy:", OverflowStrategyName(unaryExpr.overflowStrategy)); } PrintIndent(indent, "}"); } void PrintBinaryExpr(unsigned indent, const BinaryExpr& binaryExpr) { PrintIndent(indent, "BinaryExpr ", TOKENS[static_cast(binaryExpr.op)], "{"); PrintBasic(indent + ONE_INDENT, binaryExpr); PrintNode(binaryExpr.leftExpr.get(), indent + ONE_INDENT, "leftExpr:"); PrintNode(binaryExpr.rightExpr.get(), indent + ONE_INDENT, "rightExpr:"); if (binaryExpr.overflowStrategy != OverflowStrategy::NA) { PrintIndent(indent + ONE_INDENT, "overflowStrategy:", OverflowStrategyName(binaryExpr.overflowStrategy)); } PrintIndent(indent, "}"); } void PrintRangeExpr(unsigned indent, const RangeExpr& rangeExpr) { PrintIndent(indent, "RangeExpr {"); PrintBasic(indent + ONE_INDENT, rangeExpr); PrintIndent(indent + ONE_INDENT, "Start:"); PrintNode(rangeExpr.startExpr.get(), indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, "Stop:"); PrintNode(rangeExpr.stopExpr.get(), indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, "Step:"); PrintNode(rangeExpr.stepExpr.get(), indent + TWO_INDENT); PrintIndent(indent, "}"); } void PrintSubscriptExpr(unsigned indent, const SubscriptExpr& expr) { PrintIndent(indent, "SubscriptExpr {"); PrintBasic(indent + ONE_INDENT, expr); PrintNode(expr.baseExpr.get(), indent + ONE_INDENT, "baseExpr"); PrintIndent(indent + ONE_INDENT, "indexExprs ["); for (auto& it : expr.indexExprs) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); PrintIndent(indent, "}"); } void PrintMemberAccess(unsigned indent, const MemberAccess& expr) { PrintIndent(indent, "MemberAccess", "{"); PrintBasic(indent + ONE_INDENT, expr); PrintNode(expr.baseExpr.get(), indent + ONE_INDENT, "baseExpr"); PrintIndent(indent + ONE_INDENT, "field:", expr.field.Val()); if (expr.typeArguments.empty()) { PrintIndent(indent + ONE_INDENT, "// no type arguments"); } else { PrintIndent(indent + ONE_INDENT, "typeArguments ["); for (auto& it : expr.typeArguments) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); } if (expr.target) { PrintTarget(indent + ONE_INDENT, *expr.target); } PrintIndent(indent + ONE_INDENT, "targets ["); for (auto t : expr.targets) { PrintTarget(indent + TWO_INDENT, *t); } PrintIndent(indent + ONE_INDENT, "]"); PrintIndent(indent, "}"); } void PrintOptionalExpr(unsigned indent, const OptionalExpr& optionalExpr) { PrintIndent(indent, "OptionalExpr", "{"); PrintBasic(indent + ONE_INDENT, optionalExpr); PrintIndent(indent + ONE_INDENT, "BaseExpr:"); PrintNode(optionalExpr.baseExpr.get(), indent + ONE_INDENT); PrintIndent(indent, "}"); } void PrintOptionalChainExpr(unsigned indent, const OptionalChainExpr& optionalChainExpr) { PrintIndent(indent, "OptionalChainExpr", "{"); PrintBasic(indent + ONE_INDENT, optionalChainExpr); PrintIndent(indent + ONE_INDENT, "Expr:"); PrintNode(optionalChainExpr.expr.get(), indent + ONE_INDENT); PrintIndent(indent, "}"); } void PrintLetPatternDestructor(unsigned indent, const LetPatternDestructor& expr) { PrintIndent(indent, "LetPattern", "{"); PrintBasic(indent + ONE_INDENT, expr); PrintIndent(indent + ONE_INDENT, "patterns ["); for (auto& p : expr.patterns) { PrintIndent(indent + TWO_INDENT, "|", "{"); PrintNode(p.get(), indent + THREE_INDENT); PrintIndent(indent + TWO_INDENT, "}"); } PrintIndent(indent + ONE_INDENT, "]"); PrintIndent(indent + ONE_INDENT, "Initializer:", "{"); PrintNode(expr.initializer.get(), indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, "}"); PrintIndent(indent, "}"); } void PrintFuncArg(unsigned indent, const FuncArg& expr) { PrintIndent(indent, "FuncArg", "{"); PrintBasic(indent + ONE_INDENT, expr); if (!expr.name.Empty()) { PrintIndent(indent + ONE_INDENT, "ArgName:", expr.name.Val()); } if (expr.withInout) { PrintIndent(indent + ONE_INDENT, "inout"); } PrintNode(expr.expr.get(), indent + ONE_INDENT); PrintIndent(indent, "}"); } void PrintCallExpr(unsigned indent, const CallExpr& expr) { PrintIndent(indent, "CallExpr {"); PrintBasic(indent + ONE_INDENT, expr); PrintIndent(indent + ONE_INDENT, "BaseFunc {"); PrintNode(expr.baseFunc.get(), indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, "}"); if (expr.args.empty()) { PrintIndent(indent + ONE_INDENT, "// no arguments"); } else { PrintIndent(indent + ONE_INDENT, "arguments ["); for (auto& it : expr.args) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); } if (expr.resolvedFunction) { PrintTarget(indent + ONE_INDENT, *expr.resolvedFunction, "resolvedFunction"); } PrintIndent(indent, "}"); } void PrintParenExpr(unsigned indent, const ParenExpr& parenExpr) { PrintIndent(indent, "ParenExpr {"); PrintBasic(indent + ONE_INDENT, parenExpr); PrintNode(parenExpr.expr.get(), indent + ONE_INDENT); PrintIndent(indent, "}"); } void PrintLambdaExpr(unsigned indent, const LambdaExpr& expr) { PrintIndent(indent, "LambdaExpr {"); PrintBasic(indent + ONE_INDENT, expr); PrintIndent(indent + ONE_INDENT, "mangledName:", expr.mangledName); PrintNode(expr.funcBody.get(), indent + ONE_INDENT, "LambdaExpr-Body"); PrintIndent(indent, "}"); } void PrintLitConstExpr(unsigned indent, const LitConstExpr& expr) { PrintIndent( indent, "LitConstExpr:", expr.LitKindToString(), "\"" + StringConvertor::Recover(expr.stringValue) + "\"", "{"); PrintBasic(indent + ONE_INDENT, expr); if (expr.siExpr) { PrintNode(expr.siExpr.get(), indent + ONE_INDENT, "siExpr"); } PrintIndent(indent, "}"); } void PrintInterpolationExpr(unsigned indent, const InterpolationExpr& ie) { PrintIndent(indent, "InterpolationExpr:", "\"" + ie.rawString + "\"", "{"); PrintBasic(indent + ONE_INDENT, ie); PrintNode(ie.block.get(), indent + ONE_INDENT, "block"); PrintIndent(indent, "}"); } void PrintStrInterpolationExpr(unsigned indent, const StrInterpolationExpr& expr) { PrintIndent(indent, "StrInterpolationExpr:", "\"" + expr.rawString + "\"", "{"); PrintBasic(indent + ONE_INDENT, expr); PrintIndent(indent + ONE_INDENT, "strPartExprs ["); for (auto& sp : expr.strPartExprs) { PrintNode(sp.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); PrintIndent(indent, "}"); } void PrintArrayLit(unsigned indent, const ArrayLit& expr) { PrintIndent(indent, "ArrayLiteral", "{"); PrintBasic(indent + ONE_INDENT, expr); if (expr.children.empty()) { PrintIndent(indent + ONE_INDENT, "// no members"); } PrintIndent(indent + ONE_INDENT, "children ["); for (auto& it : expr.children) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); PrintIndent(indent, "}"); } void PrintArrayExpr(unsigned indent, const ArrayExpr& expr) { auto typeName = expr.isValueArray ? "VArrayExpr" : "ArrayExpr"; PrintIndent(indent, typeName, "{"); PrintBasic(indent + ONE_INDENT, expr); PrintIndent(indent + ONE_INDENT, "args ["); for (size_t i = 0; i < expr.args.size(); ++i) { PrintNode(expr.args[i].get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); PrintIndent(indent, "}"); } void PrintTupleLit(unsigned indent, const TupleLit& expr) { PrintIndent(indent, "TupleLiteral", "{"); PrintBasic(indent + ONE_INDENT, expr); for (auto& it : expr.children) { PrintNode(it.get(), indent + ONE_INDENT); } PrintIndent(indent, "}"); } void PrintBasicpeConvExpr(unsigned indent, const TypeConvExpr& expr) { PrintIndent(indent, "TypeConvExpr", "{"); PrintBasic(indent + ONE_INDENT, expr); PrintIndent(indent + ONE_INDENT, "// Convert Type:"); PrintNode(expr.type.get(), indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, "// Expr:"); PrintNode(expr.expr.get(), indent + TWO_INDENT); PrintIndent(indent, "}"); } void PrintRefExpr(unsigned indent, const RefExpr& refExpr) { PrintIndent(indent, "RefExpr:", refExpr.ref.identifier.Val(), "{"); PrintBasic(indent + ONE_INDENT, refExpr); if (refExpr.typeArguments.empty()) { PrintIndent(indent + ONE_INDENT, "// no type arguments"); } else { PrintIndent(indent + ONE_INDENT, "typeArguments ["); for (auto& it : refExpr.typeArguments) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); } if (refExpr.ref.target) { PrintTarget(indent + ONE_INDENT, *refExpr.ref.target); } PrintIndent(indent + ONE_INDENT, "targets ["); for (auto t : refExpr.ref.targets) { PrintTarget(indent + TWO_INDENT, *t); } PrintIndent(indent + ONE_INDENT, "]"); PrintIndent(indent, "}"); } void PrintSpawnExpr(unsigned indent, const SpawnExpr& expr) { PrintIndent(indent, "SpawnExpr {"); PrintBasic(indent + ONE_INDENT, expr); if (expr.futureObj) { PrintNode(expr.futureObj.get(), indent + ONE_INDENT); } if (expr.arg) { PrintNode(expr.arg.get(), indent + ONE_INDENT); } if (expr.task) { PrintNode(expr.task.get(), indent + ONE_INDENT); } PrintIndent(indent, "}"); } void PrintSynchronizedExpr(unsigned indent, const SynchronizedExpr& expr) { PrintIndent(indent, "SynchronizedExpr {"); PrintBasic(indent + ONE_INDENT, expr); PrintNode(expr.mutex.get(), indent + TWO_INDENT, "mutex"); PrintNode(expr.body.get(), indent + TWO_INDENT, "body"); PrintIndent(indent, "}"); } void PrintIfAvailableExpr(unsigned indent, const IfAvailableExpr& expr) { PrintIndent(indent, "IfAvailableExpr {"); PrintBasic(indent + ONE_INDENT, expr); PrintNode(expr.GetArg(), indent + TWO_INDENT, "arg"); PrintNode(expr.GetLambda1(), indent + TWO_INDENT, "lambda1"); PrintNode(expr.GetLambda2(), indent + TWO_INDENT, "lamdba2"); PrintIndent(indent, "}"); } void PrintBlock(unsigned indent, const Block& block) { PrintIndent(indent, "Block: {"); PrintBasic(indent + ONE_INDENT, block); if (block.body.empty()) { PrintIndent(indent + ONE_INDENT, "// no block"); } else { for (auto& it : block.body) { PrintNode(it.get(), indent + ONE_INDENT); } } PrintIndent(indent, "}"); } void PrintInterfaceBody(unsigned indent, const InterfaceBody& body) { PrintIndent(indent, "InterfaceBody {"); for (auto& it : body.decls) { PrintNode(it.get(), indent + ONE_INDENT); } PrintIndent(indent, "}"); } void PrintClassBody(unsigned indent, const ClassBody& body) { PrintIndent(indent, "ClassBody {"); for (auto& it : body.decls) { PrintNode(it.get(), indent + ONE_INDENT); } PrintIndent(indent, "}"); } void PrintStructBody(unsigned indent, const StructBody& body) { for (auto& it : body.decls) { PrintNode(it.get(), indent); } } void PrintIsExpr(unsigned indent, const IsExpr& isExpr) { PrintIndent(indent, "IsExpr : ", "{"); PrintBasic(indent + ONE_INDENT, isExpr); PrintNode(isExpr.leftExpr.get(), indent + ONE_INDENT, "leftExpr"); PrintNode(isExpr.isType.get(), indent + ONE_INDENT, "isType"); PrintIndent(indent, "}"); } void PrintAsExpr(unsigned indent, const AsExpr& asExpr) { PrintIndent(indent, "AsExpr: {"); PrintBasic(indent + ONE_INDENT, asExpr); PrintNode(asExpr.leftExpr.get(), indent + ONE_INDENT, "leftExpr"); PrintNode(asExpr.asType.get(), indent + ONE_INDENT, "asType"); PrintIndent(indent, "}"); } void PrintTokenPart(unsigned indent, const AST::TokenPart& tokenPart) { PrintIndentTokens(indent + ONE_INDENT, tokenPart.tokens); } void PrintQuoteExpr(unsigned indent, const AST::QuoteExpr& qe) { PrintIndent(indent, "QuoteExpr {"); for (auto& expr : qe.exprs) { if (expr->astKind == ASTKind::TOKEN_PART) { PrintIndent(indent + ONE_INDENT, "TokenPart", "("); } else { PrintIndent(indent + ONE_INDENT, "DollarExpr", "("); } PrintNode(expr.get(), indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, ")"); } PrintIndent(indent, "}"); } void PrintMacroExpandExpr(unsigned indent, const AST::MacroExpandExpr& expr) { PrintIndent(indent, "MacroExpand:", expr.invocation.fullName, "{"); PrintBasic(indent + ONE_INDENT, expr); PrintMacroInvocation(indent, expr.invocation); PrintIndent(indent, "}"); } void PrintRefType(unsigned indent, const RefType& refType) { PrintIndent(indent, "RefType:", refType.ref.identifier.Val(), "{"); PrintBasic(indent + ONE_INDENT, refType); if (refType.typeArguments.empty()) { PrintIndent(indent + ONE_INDENT, "// no type arguments"); } else { PrintIndent(indent + ONE_INDENT, "typeArguments ["); for (auto& it : refType.typeArguments) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); } if (refType.ref.target) { PrintTarget(indent + ONE_INDENT, *refType.ref.target); } PrintIndent(indent + ONE_INDENT, "targets ["); for (auto t : refType.ref.targets) { PrintTarget(indent + TWO_INDENT, *t); } PrintIndent(indent + ONE_INDENT, "]"); PrintIndent(indent, "}"); } void PrintParenType(unsigned indent, const ParenType& type) { PrintIndent(indent, "ParenType {"); PrintBasic(indent + ONE_INDENT, type); PrintNode(type.type.get(), indent + ONE_INDENT); PrintIndent(indent, "}"); } void PrintFuncType(unsigned indent, const FuncType& type) { PrintIndent(indent, "FuncType {"); PrintBasic(indent + ONE_INDENT, type); PrintIndent(indent + ONE_INDENT, "paramTypes ["); for (auto& paramType : type.paramTypes) { PrintNode(paramType.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); PrintNode(type.retType.get(), indent + ONE_INDENT, "retType"); PrintIndent(indent + ONE_INDENT, "IsCFuncType:", type.isC); PrintIndent(indent, "}"); } void PrintTupleType(unsigned indent, const TupleType& type) { PrintIndent(indent, "TupleType {"); PrintBasic(indent + ONE_INDENT, type); PrintIndent(indent + ONE_INDENT, "fieldTypes ["); for (auto& it : type.fieldTypes) { PrintNode(it.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); PrintIndent(indent, "}"); } void PrintThisType(unsigned indent, const ThisType& type) { PrintIndent(indent, "ThisType {"); PrintBasic(indent + ONE_INDENT, type); PrintIndent(indent, "}"); } void PrintPrimitiveType(unsigned indent, const PrimitiveType& type) { PrintIndent(indent, "PrimitiveType:", type.str, "{"); PrintBasic(indent + ONE_INDENT, type); PrintIndent(indent, "}"); } void PrintOptionType(unsigned indent, const OptionType& type) { PrintIndent(indent, "OptionType {"); PrintBasic(indent + ONE_INDENT, type); PrintIndent(indent + ONE_INDENT, "BaseType:"); PrintNode(type.componentType.get(), indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, "QuestNums:", type.questNum); PrintIndent(indent, "}"); } void PrintVArrayType(unsigned indent, const VArrayType& type) { PrintIndent(indent, "VArrayType {"); PrintBasic(indent + ONE_INDENT, type); PrintIndent(indent + ONE_INDENT, "typeArguments:"); PrintNode(type.typeArgument.get(), indent + TWO_INDENT); PrintIndent(indent, "}"); } void PrintQualifiedType(unsigned indent, const QualifiedType& type) { PrintIndent(indent, "QualifiedType {"); PrintBasic(indent + ONE_INDENT, type); PrintNode(type.baseType.get(), indent + ONE_INDENT, "baseType"); PrintIndent(indent + ONE_INDENT, "field:", type.field.Val()); if (type.target) { PrintTarget(indent + ONE_INDENT, *type.target); } PrintIndent(indent, "}"); } void PrintGenericConstraint(unsigned indent, const GenericConstraint& generic) { PrintIndent(indent, "GenericConstraint {"); PrintNode(generic.type.get(), indent + ONE_INDENT); PrintIndent(indent + ONE_INDENT, "upperBounds {"); for (auto& upperBound : generic.upperBounds) { PrintNode(upperBound.get(), indent + TWO_INDENT); } PrintIndent(indent + ONE_INDENT, "}"); PrintIndent(indent, "}"); } void PrintConstPattern(unsigned indent, const ConstPattern& constPattern) { PrintIndent(indent, "ConstPattern {"); PrintNode(constPattern.literal.get(), indent + ONE_INDENT); PrintIndent(indent, "}"); } void PrintTuplePattern(unsigned indent, const TuplePattern& tuplePattern) { PrintIndent(indent, "TuplePattern {"); PrintIndent(indent + ONE_INDENT, "patterns ["); for (auto& pattern : tuplePattern.patterns) { PrintNode(pattern.get(), indent + ONE_INDENT); } PrintIndent(indent + ONE_INDENT, "]"); PrintIndent(indent, "}"); } void PrintBasicpePattern(unsigned indent, const TypePattern& typePattern) { PrintIndent(indent, "TypePattern {"); PrintBasic(indent + ONE_INDENT, typePattern); PrintNode(typePattern.pattern.get(), indent + ONE_INDENT, "pattern"); PrintNode(typePattern.type.get(), indent + ONE_INDENT, "type"); if (typePattern.desugarVarPattern.get()) { PrintNode(typePattern.desugarVarPattern.get(), indent + ONE_INDENT, "// desugarVarPattern"); } PrintIndent(indent, "}"); } void PrintVarPattern(unsigned indent, const VarPattern& varPattern) { PrintIndent(indent, "VarPattern:", varPattern.varDecl->identifier.Val(), "{"); PrintBasic(indent + ONE_INDENT, varPattern); PrintNode(varPattern.varDecl, indent + ONE_INDENT, "varDecl"); PrintIndent(indent, "}"); } void PrintEnumPattern(unsigned indent, const EnumPattern& enumPattern) { PrintIndent(indent, "EnumPattern {"); PrintNode(enumPattern.constructor.get(), indent + ONE_INDENT); for (auto& pattern : enumPattern.patterns) { PrintNode(pattern.get(), indent + ONE_INDENT); } PrintIndent(indent, "}"); } void PrintExceptTypePattern(unsigned indent, const ExceptTypePattern& exceptTypePattern) { PrintIndent(indent, "ExceptTypePattern {"); PrintNode(exceptTypePattern.pattern.get(), indent + ONE_INDENT); for (auto& type : exceptTypePattern.types) { PrintNode(type.get(), indent + ONE_INDENT); } PrintIndent(indent, "}"); } void PrintVarOrEnumPattern(unsigned indent, const VarOrEnumPattern& ve) { PrintIndent(indent, "VarOrEnumPattern {"); PrintIndent(indent + ONE_INDENT, ve.identifier); if (ve.pattern) { PrintIndent(indent + ONE_INDENT, "Subpatterns {"); PrintNode(ve.pattern, indent + TWO_INDENT); PrintIndent(indent + ONE_INDENT, "}"); } PrintIndent(indent, "}"); } void PrintCommandTypePattern(unsigned indent, const CommandTypePattern& commandTypePattern) { PrintNode(commandTypePattern.pattern.get(), indent + ONE_INDENT); for (auto& type : commandTypePattern.types) { PrintNode(type.get(), indent + ONE_INDENT); } } void PrintFeaturesDirective(unsigned indent, const FeaturesDirective& featuresDirective) { PrintIndent(indent, "FeaturesDirective:", "features", "{"); PrintIndent(indent + ONE_INDENT, "ids: ", "["); if (!featuresDirective.content.empty()) { std::stringstream ss; for (size_t i = 0; i < featuresDirective.content.size(); i++) { ss << featuresDirective.content[i].ToString(); if (i < featuresDirective.content.size() - 1) { ss << ", "; } } PrintIndent(indent + TWO_INDENT, ss.str()); } else { PrintIndent(indent + TWO_INDENT, "// no feature arguments"); } PrintIndent(indent + ONE_INDENT, "]"); PrintIndent(indent + ONE_INDENT, "position:", featuresDirective.begin.ToString(), featuresDirective.end.ToString()); PrintIndent(indent, "}"); } void PrintPackageSpec(unsigned indent, const PackageSpec& package) { PrintIndent(indent, "PackageSpec:", package.packageName.Val(), "{"); PrintIndent(indent + ONE_INDENT, package.GetPackageName()); if (!package.prefixPaths.empty()) { auto prefixPath = JoinStrings(package.prefixPaths, "."); PrintIndent(indent + ONE_INDENT, "prefixPath:", std::move(prefixPath)); } if (package.modifier) { PrintIndent(indent + ONE_INDENT, "modifier:", TOKENS[static_cast(package.modifier->modifier)]); } PrintIndent(indent, "}"); } void PrintImportSpec(unsigned indent, const ImportSpec& import) { if (import.content.kind != ImportKind::IMPORT_MULTI) { PrintIndent(indent, "ImportSpec:", import.content.identifier.Val(), "{"); if (import.content.kind == ImportKind::IMPORT_ALIAS) { PrintIndent(indent + ONE_INDENT, "aliasName:", import.content.aliasName.Val()); } if (!import.content.prefixPaths.empty()) { PrintIndent(indent + ONE_INDENT, "prefixPaths:", import.content.GetPrefixPath()); } if (import.modifier) { PrintIndent(indent + ONE_INDENT, "modifier:", TOKENS[static_cast(import.modifier->modifier)]); } PrintIndent(indent + ONE_INDENT, "isDecl:", import.content.isDecl); PrintIndent(indent, "}"); } else { std::string commonPrefix = import.content.GetPrefixPath(); for (const auto& item : import.content.items) { PrintIndent(indent, "ImportSpec:", item.identifier.Val(), "{"); if (item.kind == ImportKind::IMPORT_ALIAS) { PrintIndent(indent + ONE_INDENT, "aliasName:", item.aliasName.Val()); } PrintIndent(indent + ONE_INDENT, "prefixPaths:", item.GetPrefixPath()); if (import.modifier) { PrintIndent(indent + ONE_INDENT, "modifier:", TOKENS[static_cast(import.modifier->modifier)]); } PrintIndent(indent, "}"); } } } } // namespace #endif void PrintNode([[maybe_unused]] Ptr node, [[maybe_unused]] unsigned indent, [[maybe_unused]] const std::string addition) { #if !defined(NDEBUG) || defined(CMAKE_ENABLE_ASSERT) // Enable printNode for debug and CI version. if (!addition.empty()) { PrintIndent(indent, "//", node ? addition : addition + " nullptr"); } if (!node) { return; } if (node->IsExpr()) { auto expr = RawStaticCast(node); if (expr->desugarExpr) { return PrintNode(expr->desugarExpr.get(), indent, addition); } } match (*node)([&indent](const Package& package) { PrintPackage(indent, package); }, [&indent](const File& file) { PrintFile(indent, file); }, // ----------- Decls -------------------- [&indent](const GenericParamDecl& gpd) { PrintGenericParamDecl(indent, gpd); }, [&indent](const FuncParam& param) { PrintFuncParam(indent, param); }, [&indent](const MacroExpandParam& macroExpand) { PrintMacroExpandParam(indent, macroExpand); }, [&indent](const FuncParamList& paramList) { PrintFuncParamList(indent, paramList); }, [&indent](const MainDecl& mainDecl) { PrintMainDecl(indent, mainDecl); }, [&indent](const FuncDecl& funcDecl) { PrintFuncDecl(indent, funcDecl); }, [&indent](const MacroDecl& macroDecl) { PrintMacroDecl(indent, macroDecl); }, [&indent](const FuncBody& body) { PrintFuncBody(indent, body); }, [&indent](const PropDecl& propDecl) { PrintPropDecl(indent, propDecl); }, [&indent](const MacroExpandDecl& macroExpand) { PrintMacroExpandDecl(indent, macroExpand); }, [&indent](const VarWithPatternDecl& vwpd) { PrintVarWithPatternDecl(indent, vwpd); }, [&indent](const VarDecl& varDecl) { PrintVarDecl(indent, varDecl); }, [&indent](const TypeAliasDecl& alias) { PrintBasicpeAliasDecl(indent, alias); }, [&indent](const ClassDecl& classDecl) { PrintClassDecl(indent, classDecl); }, [&indent](const InterfaceDecl& interfaceDecl) { PrintInterfaceDecl(indent, interfaceDecl); }, [&indent](const EnumDecl& enumDecl) { PrintEnumDecl(indent, enumDecl); }, [&indent](const StructDecl& decl) { PrintStructDecl(indent, decl); }, [&indent](const ExtendDecl& ed) { PrintExtendDecl(indent, ed); }, [&indent](const PrimaryCtorDecl& pc) { PrintPrimaryCtorDecl(indent, pc); }, // -----------Exprs---------------------- [&indent](const IfExpr& expr) { PrintIfExpr(indent, expr); }, [&indent](const MatchExpr& expr) { PrintMatchExpr(indent, expr); }, [&indent](const TryExpr& expr) { PrintTryExpr(indent, expr); }, [&indent](const ThrowExpr& expr) { PrintThrowExpr(indent, expr); }, [&indent](const PerformExpr& expr) { PrintPerformExpr(indent, expr); }, [&indent](const ResumeExpr& expr) { PrintResumeExpr(indent, expr); }, [&indent](const ReturnExpr& expr) { PrintReturnExpr(indent, expr); }, [&indent](const JumpExpr& expr) { PrintJumpExpr(indent, expr); }, [&indent](const ForInExpr& expr) { PrintForInExpr(indent, expr); }, [&indent](const WhileExpr& expr) { PrintWhileExpr(indent, expr); }, [&indent](const DoWhileExpr& expr) { PrintDoWhileExpr(indent, expr); }, [&indent](const AssignExpr& expr) { PrintAssignExpr(indent, expr); }, [&indent](const IncOrDecExpr& expr) { PrintIncOrDecExpr(indent, expr); }, [&indent](const UnaryExpr& unaryExpr) { PrintUnaryExpr(indent, unaryExpr); }, [&indent](const BinaryExpr& binaryExpr) { PrintBinaryExpr(indent, binaryExpr); }, [&indent](const RangeExpr& rangeExpr) { PrintRangeExpr(indent, rangeExpr); }, [&indent](const SubscriptExpr& expr) { PrintSubscriptExpr(indent, expr); }, [&indent](const MemberAccess& expr) { PrintMemberAccess(indent, expr); }, [&indent](const FuncArg& expr) { PrintFuncArg(indent, expr); }, [&indent](const CallExpr& expr) { PrintCallExpr(indent, expr); }, [&indent](const ParenExpr& parenExpr) { PrintParenExpr(indent, parenExpr); }, [&indent](const LambdaExpr& expr) { PrintLambdaExpr(indent, expr); }, [&indent](const LitConstExpr& expr) { PrintLitConstExpr(indent, expr); }, [&indent](const InterpolationExpr& expr) { PrintInterpolationExpr(indent, expr); }, [&indent](const StrInterpolationExpr& expr) { PrintStrInterpolationExpr(indent, expr); }, [&indent](const ArrayLit& expr) { PrintArrayLit(indent, expr); }, [&indent](const ArrayExpr& expr) { PrintArrayExpr(indent, expr); }, [&indent](const TupleLit& expr) { PrintTupleLit(indent, expr); }, [&indent](const TypeConvExpr& expr) { PrintBasicpeConvExpr(indent, expr); }, [&indent](const RefExpr& refExpr) { PrintRefExpr(indent, refExpr); }, [&indent](const OptionalExpr& expr) { PrintOptionalExpr(indent, expr); }, [&indent](const OptionalChainExpr& expr) { PrintOptionalChainExpr(indent, expr); }, [&indent](const LetPatternDestructor& expr) { PrintLetPatternDestructor(indent, expr); }, [&indent](const PrimitiveTypeExpr& pte) { PrintIndent(indent, "PrimitiveTypeExpr: " + pte.ty->String()); }, [&indent](const SpawnExpr& expr) { PrintSpawnExpr(indent, expr); }, [&indent](const SynchronizedExpr& expr) { PrintSynchronizedExpr(indent, expr); }, [&indent](const InvalidExpr& /* expr */) { PrintIndent(indent, "InvalidExpr: Need to be fixed!"); }, [&indent](const Block& block) { PrintBlock(indent, block); }, [&indent](const InterfaceBody& body) { PrintInterfaceBody(indent, body); }, [&indent](const ClassBody& body) { PrintClassBody(indent, body); }, [&indent](const StructBody& body) { PrintStructBody(indent, body); }, [&indent](const IsExpr& isExpr) { PrintIsExpr(indent, isExpr); }, [&indent](const AsExpr& asExpr) { PrintAsExpr(indent, asExpr); }, [&indent](const TokenPart& tokenPart) { PrintTokenPart(indent, tokenPart); }, [&indent](const QuoteExpr& quoteExpr) { PrintQuoteExpr(indent, quoteExpr); }, [&indent](const MacroExpandExpr& macroExpr) { PrintMacroExpandExpr(indent, macroExpr); }, [&indent](const WildcardExpr& /* WildcardExpr */) { PrintIndent(indent, "WildcardExpr:", "_"); }, [&indent](const IfAvailableExpr& expr) { PrintIfAvailableExpr(indent, expr); }, // ----------- Type -------------------- [&indent](const RefType& refType) { PrintRefType(indent, refType); }, [&indent](const ParenType& type) { PrintParenType(indent, type); }, [&indent](const FuncType& type) { PrintFuncType(indent, type); }, [&indent](const TupleType& type) { PrintTupleType(indent, type); }, [&indent](const ThisType& type) { PrintThisType(indent, type); }, [&indent](const PrimitiveType& type) { PrintPrimitiveType(indent, type); }, [&indent](const OptionType& type) { PrintOptionType(indent, type); }, [&indent](const VArrayType& type) { PrintVArrayType(indent, type); }, [&indent](const QualifiedType& type) { PrintQualifiedType(indent, type); }, [&indent](const GenericConstraint& generic) { PrintGenericConstraint(indent, generic); }, [&indent](const InvalidType&) { PrintIndent(indent, "InvalidType: Need to be fixed!"); }, // -----------pattern---------------------- [&indent](const ConstPattern& constPattern) { PrintConstPattern(indent, constPattern); }, [&indent](const WildcardPattern& /* WildcardPattern */) { PrintIndent(indent, "WildcardPattern:", "_"); }, [&indent](const VarPattern& varPattern) { PrintVarPattern(indent, varPattern); }, [&indent](const TuplePattern& tuplePattern) { PrintTuplePattern(indent, tuplePattern); }, [&indent](const TypePattern& typePattern) { PrintBasicpePattern(indent, typePattern); }, [&indent](const EnumPattern& enumPattern) { PrintEnumPattern(indent, enumPattern); }, [&indent](const ExceptTypePattern& exceptTypePattern) { PrintExceptTypePattern(indent, exceptTypePattern); }, [&indent](const CommandTypePattern& cmdTypePattern) { PrintCommandTypePattern(indent, cmdTypePattern); }, [indent](const VarOrEnumPattern& ve) { PrintVarOrEnumPattern(indent, ve); }, // ----------- package---------------------- [&indent](const FeaturesDirective& feature) { PrintFeaturesDirective(indent, feature); }, [&indent](const PackageSpec& package) { PrintPackageSpec(indent, package); }, [&indent](const ImportSpec& import) { if (import.IsImportMulti()) { return; } PrintImportSpec(indent, import); }, // -----------no match---------------------- [&indent, &node]() { PrintIndent(indent, "UnknowNode:", ASTKIND_TO_STR.at(node->astKind), "{"); PrintBasic(indent + ONE_INDENT, *node); PrintIndent(indent, "}"); }); #endif } void PrintNode(Ptr node) { PrintNode(node, 0, ""); } } // namespace Cangjie cangjie_compiler-1.0.7/src/AST/Query.cpp000066400000000000000000000211701510705540100200370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Query and related classes. */ #include "cangjie/AST/Query.h" #include #include #include "QueryParser.h" #include "cangjie/Utils/StdUtils.h" using namespace Cangjie; namespace { std::string OpToStr(Operator op) { switch (op) { case Operator::AND: return "&&"; case Operator::NOT: return "!"; case Operator::OR: return "||"; default: return std::to_string(static_cast(op)); } } } // namespace std::unique_ptr QueryParser::Parse() { return ParseBooleanClause(); } std::unique_ptr QueryParser::ParseBooleanClause() { std::unique_ptr left; if (Skip(TokenKind::LPAREN)) { left = ParseParenClause(); } else if (Seeing(TokenKind::IDENTIFIER) || Seeing(TokenKind::WILDCARD)) { left = ParseTerm(); } else { GetDiagnosticEngine().DiagnoseRefactor( DiagKindRefactor::parse_query_expected_query_symbol, LookAhead().Begin()); return nullptr; } if (!left) { return nullptr; } bool unexpectedEnd = !SeeingAny({TokenKind::AND, TokenKind::OR, TokenKind::NOT, TokenKind::END}) && !(Seeing(TokenKind::RPAREN) && parsingParenClause); if (unexpectedEnd) { Next(); GetDiagnosticEngine().DiagnoseRefactor( DiagKindRefactor::parse_query_expected_logic_symbol, LookAhead().Begin(), LookAhead().Value()); return nullptr; } while (SeeingAny({TokenKind::AND, TokenKind::OR, TokenKind::NOT})) { auto op = std::make_unique(); op->type = QueryType::OP; // Determine the op. if (Skip(TokenKind::AND)) { op->op = Operator::AND; } else if (Skip(TokenKind::OR)) { op->op = Operator::OR; } else if (Skip(TokenKind::NOT)) { op->op = Operator::NOT; } std::unique_ptr right; if (Skip(TokenKind::LPAREN)) { right = ParseParenClause(); } else if (Seeing(TokenKind::IDENTIFIER) || Seeing(TokenKind::WILDCARD)) { right = ParseTerm(); } else { GetDiagnosticEngine().DiagnoseRefactor( DiagKindRefactor::parse_query_expected_query_symbol, LookAhead().Begin()); return nullptr; } if (!right) { return nullptr; } op->right = std::move(right); op->left = std::move(left); left = std::move(op); } return left; } std::unique_ptr QueryParser::ParseParenClause() { parsingParenClause = true; std::unique_ptr curNode = ParseBooleanClause(); parsingParenClause = false; if (!Skip(TokenKind::RPAREN)) { GetDiagnosticEngine().DiagnoseRefactor( DiagKindRefactor::parse_expected_character, LookAhead().Begin(), ")", LookAhead().Value()); return nullptr; } return curNode; } std::optional QueryParser::ParseComparator() { if (Skip(TokenKind::ASSIGN)) { return "="; } else if (Skip(TokenKind::LT)) { return "<"; } else if (Skip(TokenKind::LE)) { return "<="; } else if (SeeingCombinator({TokenKind::GT, TokenKind::ASSIGN})) { SkipCombinator({TokenKind::GT, TokenKind::ASSIGN}); return ">="; } else if (Skip(TokenKind::GT)) { return ">"; } else { return std::nullopt; } } std::unique_ptr QueryParser::ParsePositionTerm() { auto getNumber = [](const std::string& str) -> int { return Stoi(str).value_or(0); }; auto pos = std::make_unique(); pos->key = TOKEN_KIND_VALUES[static_cast(TokenKind::WILDCARD)]; pos->type = QueryType::POS; auto sign = ParseComparator(); if (!sign.has_value()) { GetDiagnosticEngine().DiagnoseRefactor( DiagKindRefactor::parse_query_expected_position_compare_operator, LookAhead().Begin()); return nullptr; } pos->sign = *sign; if (!Skip(TokenKind::LPAREN)) { GetDiagnosticEngine().DiagnoseRefactor( DiagKindRefactor::parse_expected_character, LookAhead().Begin(), "(", LookAhead().Value()); return nullptr; } if (!Skip(TokenKind::INTEGER_LITERAL)) { GetDiagnosticEngine().DiagnoseRefactor( DiagKindRefactor::parse_query_position_illegal_file_id, LookAhead().Begin()); return nullptr; } pos->pos.fileID = static_cast(getNumber(LookAhead().Value())); if (!Skip(TokenKind::COMMA)) { GetDiagnosticEngine().DiagnoseRefactor( DiagKindRefactor::parse_query_position_comma_required, LookAhead().Begin()); return nullptr; } if (!Skip(TokenKind::INTEGER_LITERAL)) { GetDiagnosticEngine().DiagnoseRefactor( DiagKindRefactor::parse_query_position_illegal_line_num, LookAhead().Begin()); return nullptr; } pos->pos.line = getNumber(LookAhead().Value()); if (!Skip(TokenKind::COMMA)) { GetDiagnosticEngine().DiagnoseRefactor( DiagKindRefactor::parse_query_position_comma_required, LookAhead().Begin()); return nullptr; } if (!Skip(TokenKind::INTEGER_LITERAL)) { GetDiagnosticEngine().DiagnoseRefactor( DiagKindRefactor::parse_query_position_illegal_column_num, LookAhead().Begin()); return nullptr; } pos->pos.column = getNumber(LookAhead().Value()); if (!Skip(TokenKind::RPAREN)) { GetDiagnosticEngine().DiagnoseRefactor( DiagKindRefactor::parse_expected_character, LookAhead().Begin(), ")", LookAhead().Value()); return nullptr; } return pos; } std::unique_ptr QueryParser::ParseNormalTerm() { auto term = std::make_unique(); if (Skip(TokenKind::IDENTIFIER)) { term->key = LookAhead().Value(); } if (!Skip(TokenKind::COLON)) { GetDiagnosticEngine().DiagnoseRefactor( DiagKindRefactor::parse_expected_character, LookAhead().Begin(), ":", LookAhead().Value()); return nullptr; } term->sign = "="; // Suffix query, `*decl`. if (Skip(TokenKind::MUL)) { term->type = QueryType::STRING; std::string prefix; if (Skip(TokenKind::WILDCARD)) { prefix = "_"; } if (Seeing(TokenKind::INT8, TokenKind::RUNE_LITERAL)) { term->value = prefix + LookAhead().Value(); term->matchKind = MatchKind::SUFFIX; Next(); } else { GetDiagnosticEngine().DiagnoseRefactor( DiagKindRefactor::parse_query_invalid_query_value, LookAhead().Begin()); return nullptr; } } else if (Seeing(TokenKind::INT8, TokenKind::RUNE_LITERAL) && !Seeing(TokenKind::DOLLAR)) { term->value = LookAhead().Value(); term->type = QueryType::STRING; Next(); // Prefix query, `foo*`. if (Skip(TokenKind::MUL)) { term->matchKind = MatchKind::PREFIX; } } else { if (Seeing(TokenKind::DOLLAR_IDENTIFIER) || Seeing(TokenKind::DOLLAR)) { GetDiagnosticEngine().DiagnoseRefactor(DiagKindRefactor::lex_unrecognized_symbol, LookAhead(), "$"); } GetDiagnosticEngine().DiagnoseRefactor(DiagKindRefactor::parse_query_invalid_query_value, LookAhead().Begin()); return nullptr; } return term; } std::unique_ptr QueryParser::ParseTerm() { if (Seeing(TokenKind::IDENTIFIER)) { return ParseNormalTerm(); } if (Skip(TokenKind::WILDCARD)) { return ParsePositionTerm(); } GetDiagnosticEngine().DiagnoseRefactor(DiagKindRefactor::parse_query_expected_query_symbol, LookAhead().Begin()); return nullptr; } void Query::PrettyPrint(std::string& result) const { if (type == QueryType::OP) { result.append("("); left->PrettyPrint(result); result.append(OpToStr(op)); right->PrettyPrint(result); result.append(")"); } else if (type == QueryType::POS) { result.append(key).append(sign).append("("); result.append(pos.ToString()); result.append(")"); } else { if (matchKind == MatchKind::PREFIX) { result.append(key).append(sign).append(value + "*"); } else if (matchKind == MatchKind::SUFFIX) { result.append(key).append(sign).append("*" + value); } else { result.append(key).append(sign).append(value); } } } cangjie_compiler-1.0.7/src/AST/QueryParser.h000066400000000000000000000031051510705540100206570ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the QueryParser. */ #ifndef CANGJIE_AST_QUERY_PARSER_H #define CANGJIE_AST_QUERY_PARSER_H #include #include #include "cangjie/AST/Query.h" #include "cangjie/Parse/Parser.h" namespace Cangjie { /** * @brief The main class used for parsing query statements. */ class QueryParser : public Parser { public: template explicit QueryParser(Args&&... args) : Parser(std::forward(args)...) { } /** * @brief The main parse entry. */ std::unique_ptr Parse(); private: /** * @brief Parse boolean clause with parens, like (a:b || c:d). */ std::unique_ptr ParseParenClause(); /** * @brief Parse boolean clause, like a:b && c:d. */ std::unique_ptr ParseBooleanClause(); /** * @brief Parse normal term, like a:b. */ std::unique_ptr ParseNormalTerm(); /** * @brief Parse position term, like _ < (1,2,3). */ std::unique_ptr ParsePositionTerm(); /** * @brief Parse comparator sign, '=', '>', '>=', '<', '<='. */ std::optional ParseComparator(); /** * @brief Parse term entry. */ std::unique_ptr ParseTerm(); bool parsingParenClause{false}; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/AST/RecoverDesugar.cpp000066400000000000000000000225631510705540100216610ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This function includes all functions recovering node from desugar state. */ #include "cangjie/AST/RecoverDesugar.h" #include "cangjie/AST/Match.h" namespace Cangjie::AST { namespace { void UnsetCallExprOfNode(NameReferenceExpr& nre) { nre.isAlone = true; nre.callOrPattern = nullptr; } OwnedPtr TryRecoverFunctionCallExpr(OwnedPtr base) { if (base->astKind == ASTKind::MEMBER_ACCESS) { auto ma = RawStaticCast(base.get()); if (ma->field == "()") { return std::move(ma->baseExpr); } } return base; } void RecoverToPipelineExpr(BinaryExpr& be) { CJC_NULLPTR_CHECK(be.desugarExpr); // Recover to PipelineExpr. auto& callExpr = StaticCast(*be.desugarExpr); be.rightExpr = TryRecoverFunctionCallExpr(std::move(callExpr.baseFunc)); if (auto nre = DynamicCast(be.rightExpr.get())) { UnsetCallExprOfNode(*nre); } CJC_ASSERT(callExpr.args.size() == 1); be.leftExpr = std::move(callExpr.args[0]->expr); be.desugarExpr = OwnedPtr(); } void RecoverToCompositionExpr(BinaryExpr& be) { CJC_NULLPTR_CHECK(be.desugarExpr); // Recover to CompositionExpr. auto& callExpr = StaticCast(*be.desugarExpr); CJC_ASSERT(callExpr.args.size() == 2); // there are 2 arguments CJC_NULLPTR_CHECK(callExpr.args[0]); be.leftExpr = TryRecoverFunctionCallExpr(std::move(callExpr.args[0]->expr)); if (auto nre = DynamicCast(be.leftExpr.get())) { UnsetCallExprOfNode(*nre); } CJC_NULLPTR_CHECK(callExpr.args[1]); be.rightExpr = TryRecoverFunctionCallExpr(std::move(callExpr.args[1]->expr)); if (auto nre = DynamicCast(be.rightExpr.get())) { UnsetCallExprOfNode(*nre); } be.desugarExpr = OwnedPtr(); } } // namespace void RecoverToSubscriptExpr(SubscriptExpr& se) { if (se.desugarExpr == nullptr) { return; } // Recover to SubscriptExpr. if (auto callExpr = DynamicCast(se.desugarExpr.get()); callExpr) { RecoverToCallExpr(*callExpr); auto ma = StaticCast(callExpr->baseFunc.get()); se.baseExpr = std::move(ma->baseExpr); CJC_ASSERT(se.indexExprs.size() == callExpr->args.size()); for (size_t i = 0; i < callExpr->args.size(); ++i) { se.indexExprs[i] = std::move(callExpr->args[i]->expr); } se.desugarExpr = OwnedPtr(); if (auto nre = DynamicCast(se.baseExpr.get())) { UnsetCallExprOfNode(*nre); } } } void RecoverToUnaryExpr(UnaryExpr& ue) { if (ue.desugarExpr == nullptr) { return; } // Recover to UnaryExpr. auto& callExpr = StaticCast(*ue.desugarExpr); RecoverToCallExpr(callExpr); auto ma = StaticCast(callExpr.baseFunc.get()); ue.expr = std::move(ma->baseExpr); if (auto nre = DynamicCast(ue.expr.get())) { UnsetCallExprOfNode(*nre); } ue.desugarExpr = OwnedPtr(); } void RecoverToBinaryExpr(BinaryExpr& be) { if (be.desugarExpr == nullptr) { return; } // Case for tuple equal desugar. if (be.desugarExpr->astKind != AST::ASTKind::CALL_EXPR) { be.desugarExpr = OwnedPtr(); return; } RecoverToCallExpr(StaticCast(*be.desugarExpr)); if (be.op == TokenKind::PIPELINE) { RecoverToPipelineExpr(be); return; } else if (be.op == TokenKind::COMPOSITION) { RecoverToCompositionExpr(be); return; } // Recover to BinaryExpr. auto callExpr = StaticCast(be.desugarExpr.get()); auto ma = StaticCast(callExpr->baseFunc.get()); be.leftExpr = std::move(ma->baseExpr); be.rightExpr = std::move(callExpr->args[0]->expr); if (auto nre = DynamicCast(be.leftExpr.get())) { UnsetCallExprOfNode(*nre); } be.curFile->trashBin.emplace_back(std::move(be.desugarExpr)); be.desugarExpr = OwnedPtr(); } void RecoverToAssignExpr(AssignExpr& ae) { if (ae.desugarExpr == nullptr) { return; } if (ae.desugarExpr->astKind == AST::ASTKind::CALL_EXPR && ae.leftValue->astKind == AST::ASTKind::SUBSCRIPT_EXPR) { // Recover to AssignExpr. auto& callExpr = StaticCast(*ae.desugarExpr); RecoverToCallExpr(callExpr); auto ma = StaticCast(callExpr.baseFunc.get()); auto se = StaticCast(ae.leftValue.get()); se->baseExpr = std::move(ma->baseExpr); CJC_ASSERT(!callExpr.args.empty()); for (size_t i = 0; i < callExpr.args.size() - 1; ++i) { // The last arg is right expr. se->indexExprs[i] = std::move(callExpr.args[i]->expr); se->indexExprs[i]->mapExpr = nullptr; } ae.leftValue->curFile = ae.curFile; if (ae.isCompound) { // Desugar of compound assignExpr will create binaryExpr as the last funcArg of the call. auto be = StaticCast(callExpr.args.back()->expr.get()); ae.rightExpr = std::move(be->rightExpr); } else { ae.rightExpr = std::move(callExpr.args.back()->expr); } if (se->baseExpr) { se->baseExpr->mapExpr = nullptr; if (auto nre = DynamicCast(se->baseExpr.get())) { UnsetCallExprOfNode(*nre); } } ae.desugarExpr = OwnedPtr(); } else if (ae.desugarExpr->astKind == AST::ASTKind::ASSIGN_EXPR) { // Recover to AssignExpr. auto assignExpr = StaticCast(ae.desugarExpr.get()); auto& callExpr = StaticCast(*assignExpr->rightExpr); RecoverToCallExpr(callExpr); ae.leftValue = std::move(assignExpr->leftValue); ae.rightExpr = std::move(callExpr.args[0]->expr); ae.desugarExpr = OwnedPtr(); ae.leftValue->mapExpr = nullptr; } } void RecoverCallFromArrayExpr(CallExpr& ce) { if (!ce.desugarExpr || ce.desugarExpr->astKind != ASTKind::ARRAY_EXPR) { return; } auto ae = StaticAs(ce.desugarExpr.get()); for (auto& it : ae->args) { (void)ce.args.emplace_back(std::move(it)); } ce.desugarExpr = OwnedPtr(); } void RecoverToCallExpr(CallExpr& ce) { if (ce.desugarExpr == nullptr) { return; } // We should recover the `CallExpr` recursively, since `desugarExpr` can be nested, // e.g., function calling operator with variadic arguments. if (ce.desugarExpr->desugarExpr) { // The `desugarExpr` of a `CallExpr` must be `CallExpr`. CallExpr& desugarCallExpr = StaticCast(*ce.desugarExpr); RecoverToCallExpr(desugarCallExpr); ce.callKind = desugarCallExpr.callKind; } // Recover to CallExpr. if (ce.callKind == CallKind::CALL_VARIADIC_FUNCTION) { RecoverFromVariadicCallExpr(ce); } else if (auto callExpr = DynamicCast(ce.desugarExpr.get()); callExpr) { if (auto ma = DynamicCast(callExpr->baseFunc.get()); ma) { if (ma->field == "()") { ce.baseFunc = std::move(ma->baseExpr); ce.args = std::move(callExpr->args); ce.curFile->trashBin.emplace_back(std::move(ce.desugarExpr)); } } } else if (Is(ce.desugarExpr.get())) { RecoverCallFromArrayExpr(ce); } else if (auto pointerExpr = DynamicCast(ce.desugarExpr.get()); pointerExpr) { if (pointerExpr->arg) { (void)ce.args.emplace_back(std::move(pointerExpr->arg)); } ce.desugarExpr = OwnedPtr(); } } void RecoverFromVariadicCallExpr(CallExpr& ce) { if (!ce.desugarExpr || ce.desugarExpr->astKind != ASTKind::CALL_EXPR) { return; } auto callExpr = StaticAs(ce.desugarExpr.get()); ce.callKind = callExpr->callKind; ce.baseFunc = std::move(callExpr->baseFunc); CJC_ASSERT(!callExpr->args.empty()); // At least one `ArrayLit`. size_t idx = 0; size_t arrayLitIdx = 0; size_t arraySize = 0; for (; idx < callExpr->args.size(); ++idx) { if (idx + 1 < callExpr->args.size() && callExpr->args[idx + 1]->name.Empty()) { // Fixed positional arguments. ce.args[idx] = std::move(callExpr->args[idx]); continue; } arrayLitIdx = idx; auto arrayLit = StaticAs(callExpr->args[arrayLitIdx]->expr.get()); arraySize = arrayLit->children.size(); // Variadic arguments. for (size_t i = 0; i < arraySize; ++i) { ce.args[arrayLitIdx + i]->expr = std::move(arrayLit->children[i]); } break; } idx++; // Named arguments. for (; idx < callExpr->args.size(); ++idx) { CJC_ASSERT(idx + arraySize > 0); auto originIdx = (idx + arraySize) - 1; CJC_ASSERT(originIdx < ce.args.size()); ce.args[originIdx] = std::move(callExpr->args[idx]); } ce.desugarExpr = OwnedPtr(); } } // namespace Cangjie::AST cangjie_compiler-1.0.7/src/AST/ScopeManagerApi.cpp000066400000000000000000000024141510705540100217300ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements ScopeManager apis. */ #include "cangjie/AST/ScopeManagerApi.h" #include "cangjie/AST/ASTContext.h" using namespace Cangjie; using namespace AST; Symbol* ScopeManagerApi::GetScopeGate(const ASTContext& ctx, const std::string& scopeName) { auto it = ctx.invertedIndex.scopeGateMap.find(scopeName); if (it != ctx.invertedIndex.scopeGateMap.end()) { return it->second; } return nullptr; } std::string ScopeManagerApi::GetScopeGateName(const std::string& scopeName) { std::string currentScope; auto found = scopeName.find_last_of(childScopeNameSplit); if (found != std::string::npos) { currentScope = scopeName.substr(0, found); } else { currentScope = scopeName; } found = currentScope.find_last_of(scopeNameSplit); if (found != std::string::npos) { currentScope.replace(found, 1, 1, childScopeNameSplit); } else { // Toplevel don't have root name. return ""; } return currentScope; } cangjie_compiler-1.0.7/src/AST/Searcher.cpp000066400000000000000000000517671510705540100205050ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Searcher related classes. */ #include "cangjie/AST/Searcher.h" #include #include #include #include #include "QueryParser.h" #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/ScopeManagerApi.h" #include "cangjie/Basic/Print.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/StdUtils.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace AST; using namespace Utils; using namespace FileUtil; namespace { std::string NormalizeQuery(const Query& query, const Order& order) { std::string normalizedQuery; query.PrettyPrint(normalizedQuery); std::for_each(query.fileHashes.cbegin(), query.fileHashes.cend(), [&normalizedQuery](auto fileHash) { normalizedQuery.append("|fileHash:").append(FileUtil::GetShortHash(fileHash)); }); if (&order == &Sort::posAsc) { normalizedQuery.append("|sort:asc"); } else if (&order == &Sort::posDesc) { normalizedQuery.append("|sort:desc"); } return normalizedQuery; } } // namespace void Trie::Insert(const std::string& value) { TrieNode* n = root.get(); for (unsigned i = 0; i < value.size(); ++i) { if (n->next.find(value[i]) == n->next.end()) { n->next.emplace(value[i], std::make_unique(value[i])); } n = n->next[value[i]].get(); n->prefix = value.substr(0, i + 1); n->suffixes.insert(value.substr(i + 1)); } n->value = value; } void Trie::Insert(const std::string& value, Symbol& id) { TrieNode* n = root.get(); for (unsigned i = 0; i < value.size(); ++i) { if (n->next.find(value[i]) == n->next.end()) { n->next.emplace(value[i], std::make_unique(value[i])); } auto next = n->next[value[i]].get(); next->depth = n->depth + 1; next->parent = n; next->ids.insert(&id); n = next; n->prefix = value.substr(0, i + 1); n->suffixes.insert(value.substr(i + 1)); } n->value = value; } void Trie::Delete(const std::string& value, Symbol& id) { TrieNode* n = root.get(); for (char i : value) { if (n->next.empty()) { return; } auto next = n->next[i].get(); next->ids.erase(&id); n = next; } } TrieNode* Trie::GetPrefixRootNode(const std::string& prefix) const { TrieNode* n = root.get(); for (auto c : prefix) { TrieNode::Next::const_iterator found = n->next.find(c); if (found == n->next.end()) { return nullptr; } n = found->second.get(); } return n; } std::vector Trie::PrefixMatch(const std::string& prefix) const { std::vector matches; auto prefixRoot = GetPrefixRootNode(prefix); std::function walk = [&matches, &walk](TrieNode* n) { if (!n) { return; } if (!n->value.empty()) { matches.push_back(n->value); } for (auto& i : std::as_const(n->next)) { walk(i.second.get()); } }; walk(prefixRoot); return matches; } std::vector Trie::SuffixMatch(const std::string& suffix) const { std::vector matches; std::function walk = [&suffix, &matches, &walk](TrieNode* n) { if (!n) { return; } if (n->suffixes.find(suffix) != n->suffixes.cend()) { matches.push_back(n->prefix + suffix); } for (auto& i : std::as_const(n->next)) { walk(i.second.get()); } }; walk(root.get()); return matches; } std::string PosSearchApi::PosToStr(const Position& pos) { std::string ret = FillZero(static_cast(pos.fileID), MAX_DIGITS_FILE); ret += FillZero(pos.line, MAX_DIGITS_LINE); ret += FillZero(pos.column, MAX_DIGITS_COLUMN); return ret; } TrieNode* PosSearchApi::FindCommonRootInPosTrie(const Trie& posTrie, const Position& pos) { std::string posStr = PosToStr(pos); TrieNode* n = posTrie.root.get(); for (auto c : posStr) { TrieNode::Next::const_iterator found = n->next.find(c); if (found == n->next.cend()) { return n; } n = found->second.get(); } return n; } TrieNode* PosSearchApi::FindCommonRootInPosTrie(const Trie& posTrie, const std::string& pos) { TrieNode* n = posTrie.root.get(); for (auto c : pos) { TrieNode::Next::const_iterator found = n->next.find(c); if (found == n->next.cend()) { return n; } n = found->second.get(); } return n; } std::set PosSearchApi::GetIDsGreaterThanPos(const Trie& posTrie, const Position& pos, bool isClose) { std::string posStr = PosToStr(pos); TrieNode* n = FindCommonRootInPosTrie(posTrie, posStr); TrieNode::Next::const_iterator startIter; std::set ids; while (n->depth > MAX_DIGITS_FILE) { if (n->depth == MAX_DIGITS_POSITION) { n = n->parent; continue; } if (isClose) { startIter = n->next.lower_bound(posStr[n->depth]); isClose = false; } else { startIter = n->next.upper_bound(posStr[n->depth]); } for (auto it = startIter; it != n->next.end(); ++it) { ids = Searcher::Union(ids, it->second->ids); } n = n->parent; } return ids; } void PosSearchApi::GetIDsLessThanPosOfDepth( TrieNode& n, const std::string& posStr, std::set& ids, bool& isClose) { auto endIter = n.next.lower_bound(posStr[n.depth]); if (endIter != n.next.cend()) { if (isClose) { if (endIter->first == posStr[n.depth]) { ++endIter; } isClose = false; } } for (auto it = n.next.cbegin(); it != endIter; ++it) { ids = Searcher::Union(ids, it->second->ids); } } std::set PosSearchApi::GetIDsLessThanPos(const Trie& posTrie, const Position& pos, bool isClose) { std::string posStr = PosToStr(pos); TrieNode* n = FindCommonRootInPosTrie(posTrie, posStr); std::set ids; while (n->depth > MAX_DIGITS_FILE) { if (n->depth == MAX_DIGITS_POSITION) { n = n->parent; continue; } GetIDsLessThanPosOfDepth(*n, posStr, ids, isClose); n = n->parent; } return ids; } std::set PosSearchApi::GetIDsWithinPosXByRange( const Trie& posTrie, const Position& startPos, const Position& endPos, bool isLeftClose, bool isRightClose) { std::set ids; if (endPos <= startPos) { return ids; } ids = GetIDsGreaterThanPos(posTrie, startPos, isLeftClose); ids = Searcher::Intersection(ids, GetIDsLessThanPos(posTrie, endPos, isRightClose)); return ids; } // Used for sort function, caller guarantees symbol inputs are not nullptr. Order Sort::posAsc = [](const Symbol* a, const Symbol* b) noexcept { CJC_ASSERT(a && b); if (!a->node || !b->node) { return a->node < b->node; } if (a->node->begin.fileID < b->node->begin.fileID) { return true; } else if (a->node->begin.fileID == b->node->begin.fileID && a->node->begin < b->node->begin) { return true; } else if (a->node->begin.fileID == b->node->begin.fileID && a->node->begin == b->node->begin) { // If the begin positions of different nodes are the same, the one with the bigger end position should be at the // front. return a->node->end == b->node->end ? a->astKind < b->astKind : b->node->end < a->node->end; } else { return false; } }; Order Sort::posDesc = [](const Symbol* a, const Symbol* b) noexcept { return Sort::posAsc(b, a); // Descending is reversed usage of ascending. }; std::pair, bool> Searcher::FindInSearchCache( const Query& query, const std::string& normalizedQuery) { std::vector results; bool needFilter = !query.fileHashes.empty(); auto it = cache.find(normalizedQuery); if (it == cache.end()) { // Cached filtered results. return {results, false}; } if (!needFilter) { return {it->second, true}; } for (auto sym : it->second) { if (InFiles(query.fileHashes, *sym->id)) { results.push_back(sym); } } return {results, true}; } std::vector Searcher::FilterAndSortSearchResult( const std::set& ids, const Query& query, const Order& order) const { std::vector results; bool needFilter = !query.fileHashes.empty(); for (Symbol* id : ids) { if (needFilter && !InFiles(query.fileHashes, *id)) { continue; } results.push_back(id); } sort(results.begin(), results.end(), order); return results; } std::vector Searcher::Search(const ASTContext& ctx, const Query* query, const Order& order) { if (!query) { return {}; } auto normalizedQuery = NormalizeQuery(*query, order); auto ret = FindInSearchCache(*query, normalizedQuery); if (ret.second) { return ret.first; } std::set ids = PerformSearch(ctx, *query); auto results = FilterAndSortSearchResult(ids, *query, order); cache.insert_or_assign(normalizedQuery, results); return results; } std::vector Searcher::Search( const ASTContext& ctx, const std::string& query, const Order& order, const std::unordered_set& fileHashes) { QueryParser qp(query, ctx.diag, ctx.diag.GetSourceManager()); std::unique_ptr q = qp.Parse(); if (!q) { return {}; } if (!fileHashes.empty()) { q->fileHashes = fileHashes; } auto symbols = Search(ctx, q.get(), order); return symbols; } std::set Searcher::GetIDsByName(const ASTContext& ctx, const std::string& name) const { std::set ids; auto it = ctx.invertedIndex.nameIndexes.find(name); if (it != ctx.invertedIndex.nameIndexes.end()) { ids.insert(it->second.begin(), it->second.end()); } return ids; } std::set Searcher::GetIDsByNamePrefix(const ASTContext& ctx, const std::string& prefix) const { std::set ids; std::vector names = ctx.invertedIndex.nameTrie->PrefixMatch(prefix); for (std::string& name : names) { ids = Union(ids, GetIDsByName(ctx, name)); } return ids; } std::set Searcher::GetIDsByNameSuffix(const ASTContext& ctx, const std::string& suffix) const { std::set ids; std::vector names = ctx.invertedIndex.nameTrie->SuffixMatch(suffix); for (std::string& name : names) { ids = Union(ids, GetIDsByName(ctx, name)); } return ids; } std::set Searcher::GetIDsByScopeName(const ASTContext& ctx, const std::string& scopeName) const { std::set ids; auto it = ctx.invertedIndex.scopeNameIndexes.find(scopeName); if (it != ctx.invertedIndex.scopeNameIndexes.end()) { ids.insert(it->second.begin(), it->second.end()); } return ids; } void Searcher::InvalidateCacheBut(const std::string& query) { for (auto it = cache.begin(); it != cache.end();) { if (it->first.find(query) == std::string::npos) { it = cache.erase(it); } else { ++it; } } } std::vector Searcher::GetScopeNamesByPrefix(const ASTContext& ctx, const std::string& prefix) const { return ctx.invertedIndex.scopeNameTrie->PrefixMatch(prefix); } std::set Searcher::GetIDsByScopeLevel(const ASTContext& ctx, uint32_t scopeLevel) const { std::set ids; auto it = ctx.invertedIndex.scopeLevelIndexes.find(scopeLevel); if (it != ctx.invertedIndex.scopeLevelIndexes.end()) { ids.insert(it->second.begin(), it->second.end()); } return ids; } std::set Searcher::GetIDsByASTKind(const ASTContext& ctx, const std::string& astKind) const { std::set ids; auto it = ctx.invertedIndex.astKindIndexes.find(astKind); if (it != ctx.invertedIndex.astKindIndexes.end()) { ids.insert(it->second.begin(), it->second.end()); } return ids; } std::vector Searcher::GetAstKindsBySuffix(const ASTContext& ctx, const std::string& suffix) const { return ctx.invertedIndex.astKindTrie->SuffixMatch(suffix); } std::set Searcher::GetIDsByPosEQ( const ASTContext& ctx, const Position& pos, bool isLeftClose, bool isRightClose) const { // Equivalent to finding symbol n whose position meets n->node->begin <= pos && pos < n->node->end. std::set ids; if (ctx.invertedIndex.posBeginTrie != nullptr) { ids = PosSearchApi::GetIDsLessThanPos(*ctx.invertedIndex.posBeginTrie, pos, isLeftClose); } std::set ids1; if (ctx.invertedIndex.posEndTrie != nullptr) { ids1 = PosSearchApi::GetIDsGreaterThanPos(*ctx.invertedIndex.posEndTrie, pos, isRightClose); } if (!ids1.empty()) { return Intersection(ids, ids1); } // We use scope_level to filter the node contain the pos. if (ids.empty()) { return {}; } std::set result; int scopeLevel = static_cast((*ids.cbegin())->scopeLevel); for (auto id : ids) { if (scopeLevel >= 0) { result.insert(id); scopeLevel--; } else { break; } } return result; } std::set Searcher::GetIDsByPosLT(const ASTContext& ctx, const Position& pos, bool contain) const { // Equivalent to finding symbol n whose position meets n->node->begin <= pos && pos < n->node->end. std::set ids; if (ctx.invertedIndex.posEndTrie != nullptr) { ids = PosSearchApi::GetIDsLessThanPos(*ctx.invertedIndex.posEndTrie, pos, false); } if (contain) { ids = Union(ids, GetIDsByPosEQ(ctx, pos)); } return ids; } std::set Searcher::GetIDsByPosGT(const ASTContext& ctx, const Position& pos, bool contain) const { // Equivalent to finding symbol n whose position meets n->node->begin > pos && pos <= n->node->end. std::set ids; if (ctx.invertedIndex.posBeginTrie != nullptr) { ids = PosSearchApi::GetIDsGreaterThanPos(*ctx.invertedIndex.posBeginTrie, pos, false); } if (contain) { ids = Union(ids, GetIDsByPosEQ(ctx, pos)); } return ids; } std::set Searcher::PerformSearch(const ASTContext& ctx, const Query& query) { std::set ids; if (query.type == QueryType::OP) { if (!query.left || !query.right) { return {}; } if (query.op == Operator::AND) { ids = PerformSearch(ctx, *query.left); ids = Intersection(ids, PerformSearch(ctx, *query.right)); } else if (query.op == Operator::OR) { ids = PerformSearch(ctx, *query.left); ids = Union(ids, PerformSearch(ctx, *query.right)); } else if (query.op == Operator::NOT) { ids = PerformSearch(ctx, *query.left); ids = Difference(ids, PerformSearch(ctx, *query.right)); } } else { ids = GetIDs(ctx, query); } return ids; } std::set Searcher::GetIDsByPos(const ASTContext& ctx, const Query& query) const { std::set ids; if (query.type != QueryType::POS) { return ids; } if (query.sign == "=") { ids = GetIDsByPosEQ(ctx, query.pos); } else if (query.sign == "<") { ids = GetIDsByPosLT(ctx, query.pos); } else if (query.sign == "<=") { ids = GetIDsByPosLT(ctx, query.pos, true); } else if (query.sign == ">") { ids = GetIDsByPosGT(ctx, query.pos); } else if (query.sign == ">=") { ids = GetIDsByPosGT(ctx, query.pos, true); } return ids; } std::set Searcher::GetIDsByName(const ASTContext& ctx, const Query& query) const { std::set ids; if (query.key != "name") { return ids; } if (query.matchKind == MatchKind::PRECISE) { ids = GetIDsByName(ctx, query.value); } else if (query.matchKind == MatchKind::PREFIX) { ids = GetIDsByNamePrefix(ctx, query.value); } else { ids = GetIDsByNameSuffix(ctx, query.value); } return ids; } std::set Searcher::GetIDsByScopeLevel(const ASTContext& ctx, const Query& query) const { std::set ids; constexpr int decimalBase = 10; if (query.key != "scope_level") { return ids; } if (query.sign == "=") { unsigned long level = StrToUint(ctx, query.value); if (level < UINT32_MAX) { ids = GetIDsByScopeLevel(ctx, static_cast(level)); } } else if (query.sign == "<") { for (uint32_t scopelevel = 0; scopelevel < std::strtoul(query.value.c_str(), nullptr, decimalBase); ++scopelevel) { ids = Union(ids, GetIDsByScopeLevel(ctx, scopelevel)); } } else if (query.sign == "<=") { for (uint32_t scopelevel = 0; scopelevel <= std::strtoul(query.value.c_str(), nullptr, decimalBase); ++scopelevel) { ids = Union(ids, GetIDsByScopeLevel(ctx, scopelevel)); } } return ids; } std::set Searcher::GetIDsByScopeName(const ASTContext& ctx, const Query& query) const { std::set ids; if (query.key != "scope_name") { return ids; } if (query.matchKind == MatchKind::PRECISE) { ids = GetIDsByScopeName(ctx, query.value); } else if (query.matchKind == MatchKind::PREFIX) { auto scopeNames = GetScopeNamesByPrefix(ctx, query.value); for (auto& n : scopeNames) { ids = Union(ids, GetIDsByScopeName(ctx, n)); } } else { ctx.diag.DiagnoseRefactor(DiagKindRefactor::searcher_invalid_scope_name, DEFAULT_POSITION, query.value); } return ids; } std::set Searcher::GetIDsByASTKind(const ASTContext& ctx, const Query& query) const { std::set ids; if (query.key != "ast_kind") { return ids; } if (query.matchKind == MatchKind::PRECISE) { ids = GetIDsByASTKind(ctx, query.value); } else if (query.matchKind == MatchKind::SUFFIX) { auto astKinds = GetAstKindsBySuffix(ctx, query.value); for (auto& n : astKinds) { ids = Union(ids, GetIDsByASTKind(ctx, n)); } } return ids; } std::set Searcher::GetIDs(const ASTContext& ctx, const Query& query) const { std::set ids; if (query.type == QueryType::POS) { ids = GetIDsByPos(ctx, query); } else { std::string key = query.key; if (key == "name") { ids = GetIDsByName(ctx, query); } else if (key == "scope_level") { ids = GetIDsByScopeLevel(ctx, query); } else if (key == "scope_name") { ids = GetIDsByScopeName(ctx, query); } else if (key == "ast_kind") { ids = GetIDsByASTKind(ctx, query); } else { Errorf("Unknow query term: %s!\n", key.c_str()); } } return ids; } unsigned long Searcher::StrToUint(const ASTContext& ctx, const std::string& queryVal) const { if (queryVal.empty()) { ctx.diag.DiagnoseRefactor(DiagKindRefactor::searcher_empty_number, DEFAULT_POSITION); return UINT32_MAX; } unsigned long uintValue = UINT32_MAX; bool validDigits = std::all_of(queryVal.cbegin(), queryVal.cend(), [](auto c) { return std::isdigit(c); }); if (validDigits) { if (auto v = Stoul(queryVal)) { uintValue = *v; } else { validDigits = false; } } if (!validDigits) { ctx.diag.DiagnoseRefactor(DiagKindRefactor::searcher_invalid_number, DEFAULT_POSITION, queryVal); return UINT32_MAX; } if (uintValue >= ctx.symbolTable.size() && !ctx.symbolTable.empty()) { ctx.diag.DiagnoseRefactor(DiagKindRefactor::searcher_past_the_end_of_array, DEFAULT_POSITION, queryVal); return UINT32_MAX; } return uintValue; } std::set Searcher::Intersection(const std::set& set1, const std::set& set2) { std::set ret; std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), std::inserter(ret, ret.begin())); return ret; } std::set Searcher::Union(const std::set& set1, const std::set& set2) { std::set ret; std::set_union(set1.begin(), set1.end(), set2.begin(), set2.end(), std::inserter(ret, ret.begin())); return ret; } std::set Searcher::Difference(const std::set& set1, const std::set& set2) const { std::set ret; std::set_difference(set1.begin(), set1.end(), set2.begin(), set2.end(), std::inserter(ret, ret.begin())); return ret; } bool Searcher::InFiles(const std::unordered_set& files, const Symbol& id) const { return files.find(id.hashID.hash64) != files.end(); } cangjie_compiler-1.0.7/src/AST/Types.cpp000066400000000000000000000704001510705540100200360ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Type related classes. */ #include "cangjie/AST/Types.h" #include #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/Utils/Utils.h" namespace Cangjie::AST { namespace { const std::unordered_map TYPEKIND_TO_STRING_MAP{ #define TYPE_KIND(KIND, TYPE, NAME) {TypeKind::KIND, NAME}, #include "cangjie/AST/TypeKind.inc" #undef TYPE_KIND }; template T1 GetGenericTy(T2 decl) { return decl && decl->generic ? static_cast(decl->ty.get()) : nullptr; } template size_t HashNominalTy(const TypeDeclT& ty) { size_t ret = 0; ret = hash_combine(ret, ty.name); for (auto typeArg : ty.typeArgs) { ret = hash_combine>(ret, typeArg); } ret = hash_combine>(ret, ty.declPtr); return ret; } bool IsJavaGeneric(const GenericParamDecl& gpd) { if (gpd.outerDecl && HasJavaAttr(*gpd.outerDecl)) { return true; } if (auto funcDecl = DynamicCast(gpd.outerDecl); funcDecl && funcDecl->funcBody && funcDecl->funcBody->parentClassLike && HasJavaAttr(*funcDecl->funcBody->parentClassLike)) { return true; } return false; } } // namespace size_t Ty::Hash() const { size_t ret = 0; return hash_combine>(ret, this); } size_t ArrayTy::Hash() const { size_t ret = 0; ret = hash_combine>(ret, typeArgs.empty() ? nullptr : typeArgs[0]); ret = hash_combine(ret, dims); ret = hash_combine(ret, TypeKind::TYPE_ARRAY); return ret; } size_t VArrayTy::Hash() const { size_t ret = 0; ret = hash_combine>(ret, typeArgs.empty() ? nullptr : typeArgs[0]); ret = hash_combine(ret, size); ret = hash_combine(ret, TypeKind::TYPE_VARRAY); return ret; } size_t PointerTy::Hash() const { size_t ret = 0; ret = hash_combine>(ret, typeArgs.empty() ? nullptr : typeArgs[0]); ret = hash_combine(ret, TypeKind::TYPE_POINTER); return ret; } size_t TupleTy::Hash() const { size_t ret = 0; ret = hash_combine(ret, isClosureTy); for (auto typeArg : typeArgs) { ret = hash_combine>(ret, typeArg); } ret = hash_combine(ret, TypeKind::TYPE_TUPLE); return ret; } size_t FuncTy::Hash() const { size_t ret = 0; ret = hash_combine>(ret, retTy); for (auto paramTy : paramTys) { ret = hash_combine>(ret, paramTy); } ret = hash_combine(ret, isClosureTy); ret = hash_combine(ret, isC); ret = hash_combine(ret, hasVariableLenArg); ret = hash_combine(ret, noCast); ret = hash_combine(ret, TypeKind::TYPE_FUNC); return ret; } size_t UnionTy::Hash() const { size_t ret = 0; for (auto ty : tys) { ret = hash_combine>(ret, ty); } ret = hash_combine(ret, TypeKind::TYPE_UNION); return ret; } size_t IntersectionTy::Hash() const { size_t ret = 0; for (auto ty : tys) { ret = hash_combine>(ret, ty); } ret = hash_combine(ret, TypeKind::TYPE_INTERSECTION); return ret; } size_t GenericsTy::Hash() const { size_t ret = 0; ret = hash_combine>(ret, decl); return ret; } std::string Ty::String() const { return KindName(kind); } std::string Ty::KindName(TypeKind k) { auto iter = TYPEKIND_TO_STRING_MAP.find(k); return iter == TYPEKIND_TO_STRING_MAP.end() ? "UnknownType" : iter->second; } InterfaceTy::InterfaceTy(const std::string& name, InterfaceDecl& id, const std::vector>& typeArgs) : ClassLikeTy(TypeKind::TYPE_INTERFACE, id), declPtr(&id), decl(&id) { this->name = name; this->typeArgs = typeArgs; this->invalid = !Ty::AreTysCorrect(Utils::VecToSet(this->typeArgs)); this->generic = Ty::ExistGeneric(this->typeArgs); } ClassTy::ClassTy(const std::string& name, ClassDecl& cd, const std::vector>& typeArgs) : ClassLikeTy(TypeKind::TYPE_CLASS, cd), declPtr(&cd), decl(&cd) { this->name = name; this->typeArgs = typeArgs; this->invalid = !Ty::AreTysCorrect(Utils::VecToSet(this->typeArgs)); this->generic = Ty::ExistGeneric(this->typeArgs); } EnumTy::EnumTy(const std::string& name, EnumDecl& ed, const std::vector>& typeArgs) : Ty(TypeKind::TYPE_ENUM), declPtr(&ed), decl(&ed) { this->name = name; this->typeArgs = typeArgs; this->invalid = !Ty::AreTysCorrect(Utils::VecToSet(this->typeArgs)); this->generic = Ty::ExistGeneric(this->typeArgs); } StructTy::StructTy(const std::string& name, StructDecl& sd, const std::vector>& typeArgs) : Ty(TypeKind::TYPE_STRUCT), declPtr(&sd), decl(&sd) { this->name = name; this->typeArgs = typeArgs; this->invalid = !Ty::AreTysCorrect(Utils::VecToSet(this->typeArgs)); this->generic = Ty::ExistGeneric(this->typeArgs); } Ptr ClassTy::GetGenericTy() const { return ::Cangjie::AST::GetGenericTy(declPtr); } Ptr InterfaceTy::GetGenericTy() const { return ::Cangjie::AST::GetGenericTy(declPtr); } Ptr EnumTy::GetGenericTy() const { return ::Cangjie::AST::GetGenericTy(declPtr); } Ptr StructTy::GetGenericTy() const { return ::Cangjie::AST::GetGenericTy(declPtr); } size_t ClassTy::Hash() const { size_t hash = HashNominalTy(*this); return hash_combine(hash, false); } bool ClassTy::operator==(const Ty& other) const { // Since ClassThisTy is subtype of ClassTy, ensure the other is not ClassThisTy. return NominalTyEqualTo(*this, other) && !Is(other); } size_t ClassThisTy::Hash() const { size_t hash = HashNominalTy(*this); return hash_combine(hash, true); } size_t InterfaceTy::Hash() const { return HashNominalTy(*this); } size_t StructTy::Hash() const { return HashNominalTy(*this); } size_t EnumTy::Hash() const { size_t hash = HashNominalTy(*this); return hash_combine(hash, false); } bool EnumTy::operator==(const Ty& other) const { // Since RefEnumTy is subtype of EnumTy, ensure the other is not RefEnumTy. return NominalTyEqualTo(*this, other) && !Is(other); } bool EnumTy::IsNonExhaustive() const { return decl && decl->hasEllipsis; } size_t RefEnumTy::Hash() const { size_t hash = HashNominalTy(*this); return hash_combine(hash, true); } size_t TypeAliasTy::Hash() const { return HashNominalTy(*this); } bool Ty::IsInteger() const { return IsSignedInteger() || IsUnsignedInteger() || kind == TypeKind::TYPE_IDEAL_INT; } bool Ty::IsIntegerSubType() const { if (IsInteger()) { return true; } if (!IsGeneric()) { return false; } auto genericTy = RawStaticCast(this); return genericTy->lowerBound && genericTy->lowerBound->IsInteger(); } bool Ty::IsSignedInteger() const { return kind >= TypeKind::TYPE_INT8 && kind <= TypeKind::TYPE_INT_NATIVE; } bool Ty::IsUnsignedInteger() const { return kind >= TypeKind::TYPE_UINT8 && kind <= TypeKind::TYPE_UINT_NATIVE; } bool Ty::IsFloating() const { return (kind >= TypeKind::TYPE_FLOAT16 && kind <= TypeKind::TYPE_FLOAT64) || kind == TypeKind::TYPE_IDEAL_FLOAT; } bool Ty::IsFloatingSubType() const { if (IsFloating()) { return true; } if (!IsGeneric()) { return false; } auto genericTy = RawStaticCast(this); return genericTy->lowerBound && genericTy->lowerBound->IsFloating(); } bool Ty::IsBoolean() const { return kind == TypeKind::TYPE_BOOLEAN; } bool Ty::IsBooleanSubType() const { if (IsBoolean()) { return true; } if (!IsGeneric()) { return false; } auto genericTy = RawStaticCast(this); return genericTy->lowerBound && genericTy->lowerBound->IsBoolean(); } bool Ty::IsRune() const { return kind == TypeKind::TYPE_RUNE; } bool Ty::IsIdeal() const { return kind == TypeKind::TYPE_IDEAL_INT || kind == TypeKind::TYPE_IDEAL_FLOAT; } bool Ty::IsInvalid() const { return kind == TypeKind::TYPE_INVALID || kind == TypeKind::TYPE_INITIAL; } bool Ty::IsUnit() const { return kind == TypeKind::TYPE_UNIT; } bool Ty::IsUnitOrNothing() const { return kind == TypeKind::TYPE_UNIT || kind == TypeKind::TYPE_NOTHING; } bool Ty::IsQuest() const { return kind == TypeKind::TYPE_QUEST; } bool Ty::IsPrimitive() const { return kind >= TypeKind::TYPE_UNIT && kind <= TypeKind::TYPE_BOOLEAN; } bool Ty::IsPrimitiveSubType() const { if (IsPrimitive()) { return true; } if (!IsGeneric()) { return false; } auto genericTy = RawStaticCast(this); return genericTy->lowerBound && genericTy->lowerBound->IsPrimitive(); } bool Ty::IsGeneric() const { return kind == TypeKind::TYPE_GENERICS; } bool Ty::IsPlaceholder() const { if (kind == TypeKind::TYPE_GENERICS) { return RawStaticCast(this)->isPlaceholder; } return false; } bool Ty::IsArray() const { return kind == TypeKind::TYPE_ARRAY; } bool Ty::IsStructArray() const { if (kind == TypeKind::TYPE_STRUCT) { auto ty = RawStaticCast(this); if (ty->declPtr && ty->declPtr->fullPackageName == CORE_PACKAGE_NAME && ty->declPtr->identifier == "Array") { return true; } } return false; } bool Ty::IsPointer() const { return kind == TypeKind::TYPE_POINTER; } bool Ty::IsCString() const { return kind == TypeKind::TYPE_CSTRING; } bool Ty::IsStruct() const { return kind == TypeKind::TYPE_STRUCT; } bool Ty::IsEraseGeneric() const { if (IsGeneric()) { auto ty = RawStaticCast(this); return ty->isEraseMode; } return false; } bool Ty::IsClassLike() const { return kind == TypeKind::TYPE_CLASS || kind == TypeKind::TYPE_INTERFACE; } bool Ty::IsClass() const { return kind == TypeKind::TYPE_CLASS; } bool Ty::IsInterface() const { return kind == TypeKind::TYPE_INTERFACE; } bool Ty::IsEnum() const { return kind == TypeKind::TYPE_ENUM; } bool Ty::IsCoreOptionType() const { // Core's enum Option check, support for auto package Option. if (!this->IsEnum()) { return false; } auto& enumTy = *RawStaticCast(this); return enumTy.declPtr && enumTy.declPtr->fullPackageName == CORE_PACKAGE_NAME && enumTy.declPtr->identifier == STD_LIB_OPTION && enumTy.typeArgs.size() == 1; } bool Ty::IsFunc() const { return kind == TypeKind::TYPE_FUNC; } bool Ty::IsTuple() const { return kind == TypeKind::TYPE_TUPLE; } bool Ty::IsClosureType() const { if (kind != TypeKind::TYPE_TUPLE) { return false; } return RawStaticCast(this)->isClosureTy; } bool Ty::IsIntersection() const { return kind == TypeKind::TYPE_INTERSECTION; } bool Ty::IsUnion() const { return kind == TypeKind::TYPE_UNION; } bool Ty::IsNominal() const { return kind == TypeKind::TYPE_CLASS || kind == TypeKind::TYPE_INTERFACE || kind == TypeKind::TYPE_STRUCT || kind == TypeKind::TYPE_ENUM; } bool Ty::IsObject() const { if (kind == TypeKind::TYPE_CLASS) { auto ty = RawStaticCast(this); if (ty->declPtr->fullPackageName == CORE_PACKAGE_NAME && ty->declPtr->identifier == "Object") { return true; } } return false; } bool Ty::IsAny() const { if (kind == TypeKind::TYPE_INTERFACE) { auto ty = RawStaticCast(this); if (ty->declPtr->fullPackageName == CORE_PACKAGE_NAME && ty->declPtr->identifier == "Any") { return true; } } return kind == TypeKind::TYPE_ANY; } bool Ty::IsCType() const { if (kind == TypeKind::TYPE_INTERFACE) { auto ty = RawStaticCast(this); if (ty->declPtr->fullPackageName == CORE_PACKAGE_NAME && ty->declPtr->identifier == CTYPE_NAME) { return true; } } return false; } bool Ty::IsNothing() const { return kind == TypeKind::TYPE_NOTHING; } bool Ty::IsString() const { if (IsStruct()) { auto ty = RawStaticCast(this); if (ty->declPtr->fullPackageName == CORE_PACKAGE_NAME && ty->name == "String") { return true; } } return false; } bool Ty::IsRange() const { if (IsStruct()) { auto ty = RawStaticCast(this); if (ty->declPtr->fullPackageName == CORE_PACKAGE_NAME && ty->name == "Range" && ty->typeArgs.size() == 1) { return true; } } return false; } bool Ty::Contains(Ptr ty) const { if (this == ty) { return true; } if (auto ity = DynamicCast(this); ity) { return Utils::In(ity->tys, [ty](auto it) { return it && it->Contains(ty); }); } else if (auto uty = DynamicCast(this); uty) { return Utils::In(uty->tys, [ty](auto it) { return it && it->Contains(ty); }); } else { return Utils::In(typeArgs, [ty](auto it) { return it && it->Contains(ty); }); } } bool Ty::HasIdealTy() const { if (IsIdeal()) { return true; } for (auto it : this->typeArgs) { if (it && it->HasIdealTy()) { return true; } } return false; } bool Ty::HasQuestTy() const { if (IsQuest()) { return true; } for (auto it : this->typeArgs) { if (it && it->HasQuestTy()) { return true; } } return false; } // 'invalid' and 'generic' attribute is widely used during sema process, // so deciding their value in constructor, not in checking runtime. bool Ty::HasInvalidTy() const { return invalid; // 'invalid' attribute is checked and set in constructor. } bool Ty::HasGeneric() const { return generic; // 'generic' attribute is checked and set in constructor. } bool Ty::HasIntersectionTy() const { if (IsIntersection()) { return true; } for (auto typeArg : typeArgs) { if (typeArg && typeArg->HasIntersectionTy()) { return true; } } return false; } bool Ty::HasPlaceholder() const { if (auto genTy = DynamicCast(this)) { return genTy->isPlaceholder; } for (auto typeArg : typeArgs) { if (typeArg && typeArg->HasPlaceholder()) { return true; } } return false; } bool Ty::IsExtendable() const { if (kind == TypeKind::TYPE) { auto typeAliasDecl = RawStaticCast(this)->declPtr; return typeAliasDecl && typeAliasDecl->type && typeAliasDecl->type->ty && !typeAliasDecl->TestAttr(Attribute::IN_REFERENCE_CYCLE) && typeAliasDecl->type->ty->IsExtendable(); } return kind == TypeKind::TYPE_CLASS || kind == TypeKind::TYPE_ENUM || kind == TypeKind::TYPE_STRUCT || IsArray() || IsPointer() || IsPrimitive() || IsCString(); } bool Ty::IsNumeric() const { return kind >= TypeKind::TYPE_INT8 && kind <= TypeKind::TYPE_IDEAL_FLOAT; } bool Ty::IsNative() const { return kind == TypeKind::TYPE_INT_NATIVE || kind == TypeKind::TYPE_UINT_NATIVE; } bool Ty::IsBuiltin() const { return (kind >= TypeKind ::TYPE_UNIT && kind <= TypeKind::TYPE_BOOLEAN) || kind == TypeKind::TYPE_ARRAY || kind == TypeKind::TYPE_POINTER || kind == TypeKind::TYPE_CSTRING || kind == TypeKind::TYPE_VARRAY; } bool Ty::IsImmutableType() const { return (kind >= TypeKind::TYPE_UNIT && kind <= TypeKind::TYPE_FUNC) || IsString() || IsRange(); } std::string ArrayTy::String() const { std::string str = ToString(typeArgs[0]); std::string prefix; std::string suffix; for (int i = 0; i < static_cast(dims); i++) { prefix += "Array<"; suffix += ">"; } return prefix + str + suffix; } Ptr ClassTy::GetSuperClassTy() const { if (!decl) { return nullptr; } for (auto& types : decl->inheritedTypes) { if (types && types->ty && types->ty->kind == TypeKind::TYPE_CLASS) { return RawStaticCast(types->ty); } } return nullptr; } std::set> ClassTy::GetSuperInterfaceTys() const { if (!decl) { return {}; } return decl->GetSuperInterfaceTys(); } std::set> StructTy::GetSuperInterfaceTys() const { if (!decl) { return {}; } return decl->GetSuperInterfaceTys(); } std::set> InterfaceTy::GetSuperInterfaceTys() const { if (!decl) { return {}; } return decl->GetSuperInterfaceTys(); } std::set> EnumTy::GetSuperInterfaceTys() const { if (!decl) { return {}; } return decl->GetSuperInterfaceTys(); } std::string PointerTy::String() const { return "CPointer<" + ToString(typeArgs[0]) + ">"; } std::string TupleTy::String() const { std::string str{""}; if (isClosureTy) { str += "Closure<"; } else { str += "Tuple<"; } for (auto& typeArg : typeArgs) { if (&typeArg == &typeArgs.back()) { str += ToString(typeArg); } else { str += ToString(typeArg) + ", "; } } return str + ">"; } std::string FuncTy::String() const { std::string str{"("}; for (auto& paramTy : paramTys) { if (¶mTy == ¶mTys.back()) { str += ToString(paramTy); } else { str += ToString(paramTy) + ", "; } } str = str + ") -> " + ToString(retTy); return isC ? "CFunc<" + str + ">" : str; } template std::string Ty::GetTypesToStableStr(const std::unordered_set>& tys, const std::string& delimiter); template std::string Ty::GetTypesToStableStr(const std::set>& tys, const std::string& delimiter); template std::string Ty::GetTypesToStableStr(const Container& tys, const std::string& delimiter) { std::set, CmpTyByName> sortedTys; sortedTys.insert(tys.begin(), tys.end()); return GetTypesToStr(sortedTys, delimiter); } std::string IntersectionTy::String() const { return GetTypesToStableStr(tys, " & "); } std::string UnionTy::String() const { return GetTypesToStableStr(tys, " | "); } GenericsTy::GenericsTy(const std::string& name, GenericParamDecl& gpd) : Ty(TypeKind::TYPE_GENERICS), decl(&gpd), isEraseMode(IsJavaGeneric(gpd)) { this->name = name; this->generic = true; } std::string GenericsTy::String() const { return "Generics-" + name; } std::string EnumTy::String() const { return "Enum-" + name + PrintTypeArgs(); } std::string RefEnumTy::String() const { return "Enum-" + name + PrintTypeArgs() + "(asRef)"; } std::string ClassTy::String() const { return "Class-" + name + PrintTypeArgs(); } std::string InterfaceTy::String() const { return "Interface-" + name + PrintTypeArgs(); } std::string StructTy::String() const { return "Struct-" + name + PrintTypeArgs(); } std::string TypeAliasTy::String() const { return "TypeAlias-" + name + PrintTypeArgs(); } std::string Ty::PrintTypeArgs() const { if (typeArgs.size() == 0) { return ""; } return "<" + GetTypesToStr(typeArgs, ", ") + ">"; } Ptr Ty::GetPrimitiveUpperBound(Ptr ty) { if (auto genTy = DynamicCast(ty)) { if (!genTy->isPlaceholder && genTy->lowerBound) { return RawStaticCast(ty)->lowerBound; } } return ty; } bool Ty::IsTyCorrect(Ptr ty) noexcept { return ty != nullptr && !ty->invalid; } bool Ty::IsPrimitiveCType(const Ty& ty) { // Primitive CType. static std::unordered_set primitiveCType = {TypeKind::TYPE_UNIT, TypeKind::TYPE_BOOLEAN, TypeKind::TYPE_INT8, TypeKind::TYPE_UINT8, TypeKind::TYPE_INT16, TypeKind::TYPE_UINT16, TypeKind::TYPE_INT32, TypeKind::TYPE_UINT32, TypeKind::TYPE_INT64, TypeKind::TYPE_UINT64, TypeKind::TYPE_INT_NATIVE, TypeKind::TYPE_UINT_NATIVE, TypeKind::TYPE_FLOAT32, TypeKind::TYPE_FLOAT64}; return primitiveCType.find(ty.kind) != primitiveCType.end(); } bool Ty::IsCStructType(const Ty& ty) { if (ty.kind != TypeKind::TYPE_STRUCT) { return false; } auto rty = RawStaticCast(&ty); return rty->declPtr && rty->declPtr->TestAttr(Attribute::C); } // Check if ty is CType. bool Ty::IsMetCType(const Ty& ty) { if (Is(ty)) { CJC_ASSERT(!ty.typeArgs.empty() && Ty::IsTyCorrect(ty.typeArgs[0])); return IsMetCType(*ty.typeArgs[0]); } bool isCType = IsPrimitiveCType(ty) || ty.IsPointer() || ty.IsCString() || ty.IsCFunc() || IsCStructType(ty); // TYPE_FLOAT16 is used by AI and cannot be directly deleted. return isCType || ty.kind == TypeKind::TYPE_QUEST; } bool Ty::IsCTypeConstraint(const Ty& ty) { if (IsMetCType(ty) && !IsPrimitiveCType(ty)) { return ty.kind == TypeKind::TYPE_STRUCT && RawStaticCast(&ty)->name != "String"; } return false; } bool Ty::ExistGeneric(const std::vector>& tySet) { return std::any_of(tySet.begin(), tySet.end(), [](auto& ty) { return ty && ty->HasGeneric(); }); } bool Ty::IsTyArgsSizeEqual(const Ty& ty1, const Ty& ty2) { return ty1.typeArgs.size() == ty2.typeArgs.size(); } bool Ty::IsTyArgsSingleton() const { return this->typeArgs.size() == 1; } std::set> Ty::GetGenericTyArgs() { if (IsGeneric()) { return {this}; } std::set> tys; for (auto& it : typeArgs) { if (it && it->HasGeneric()) { tys.merge(it->GetGenericTyArgs()); } } return tys; } std::set> Ty::GetGenericTyArgs(const std::set>& candidates) { if (IsGeneric() && candidates.count(RawStaticCast(this)) > 0) { return {RawStaticCast(this)}; } std::set> tys; for (auto& it : typeArgs) { if (it && it->HasGeneric()) { tys.merge(it->GetGenericTyArgs(candidates)); } } return tys; } std::string Ty::ToString(Ptr ty) { return ty ? ty->String() : "Invalid"; } /** Get ty's corresponding declaration, will be instantiated decl if exist. */ template Ptr Ty::GetDeclOfTy(Ptr ty); template Ptr Ty::GetDeclOfTy(Ptr ty); template Ptr Ty::GetDeclOfTy(Ptr ty) { if (!ty) { return nullptr; } Ptr decl = nullptr; switch (ty->kind) { case TypeKind::TYPE_CLASS: decl = RawStaticCast(ty)->decl; break; case TypeKind::TYPE_INTERFACE: decl = RawStaticCast(ty)->decl; break; case TypeKind::TYPE_STRUCT: decl = RawStaticCast(ty)->decl; break; case TypeKind::TYPE_ENUM: decl = RawStaticCast(ty)->decl; break; case TypeKind::TYPE: decl = RawStaticCast(ty)->declPtr; break; default:; } return DynamicCast(decl); } /** Get ty's corresponding declaration, always be generic decl if has generic. */ template Ptr Ty::GetDeclPtrOfTy(Ptr ty); template Ptr Ty::GetDeclPtrOfTy(Ptr ty); template Ptr Ty::GetDeclPtrOfTy(Ptr ty) { if (!ty) { return nullptr; } Ptr decl = nullptr; switch (ty->kind) { case TypeKind::TYPE_CLASS: decl = RawStaticCast(ty)->declPtr; break; case TypeKind::TYPE_INTERFACE: decl = RawStaticCast(ty)->declPtr; break; case TypeKind::TYPE_STRUCT: decl = RawStaticCast(ty)->declPtr; break; case TypeKind::TYPE_ENUM: decl = RawStaticCast(ty)->declPtr; break; case TypeKind::TYPE: decl = RawStaticCast(ty)->declPtr; break; default:; } // Map common type to platform decl if exist. if (decl && decl->platformImplementation) { decl = decl->platformImplementation; } return DynamicCast(decl); } /** Get instantiated ty's corresponding generic ty. */ Ptr Ty::GetGenericTyOfInsTy(const Ty& ty) { if (!Ty::IsTyCorrect(&ty)) { return nullptr; } switch (ty.kind) { case TypeKind::TYPE_CLASS: return static_cast(ty).GetGenericTy(); case TypeKind::TYPE_INTERFACE: return static_cast(ty).GetGenericTy(); case TypeKind::TYPE_STRUCT: return static_cast(ty).GetGenericTy(); case TypeKind::TYPE_ENUM: return static_cast(ty).GetGenericTy(); default: return nullptr; } } bool Ty::IsInitialTy(Ptr ty) { return !ty || ty->kind == TypeKind::TYPE_INITIAL; } Ptr Ty::GetInitialTy() { static InitialTy initialTy = InitialTy(); return &initialTy; } bool CompTyByNames(Ptr ty1, Ptr ty2) { if (!ty1 || !ty2) { return ty1 < ty2; } // 'ty1' is less than 'ty2' if 'ty1' and 'ty2' are both not null and ty1's name is less then ty2's name. // If the tys' names are same, and any of them do not have pointed declaration, // compare their address to ensure all different tys are counted, otherwise compare their declarations. std::string str1 = ty1->String(); std::string str2 = ty2->String(); auto decl1 = Ty::GetDeclPtrOfTy(ty1); auto decl2 = Ty::GetDeclPtrOfTy(ty2); return str1 < str2 || (str1 == str2 && ((!decl1 || !decl2) ? ty1 < ty2 : CompNodeByPos(decl1, decl2))); } template bool Ty::NominalTyEqualTo(const TypeDeclT& base, const Ty& other) { auto q = dynamic_cast(&other); return q && base.typeArgs.size() == q->typeArgs.size() && memcmp(base.typeArgs.data(), q->typeArgs.data(), base.typeArgs.size() * sizeof(intptr_t)) == 0 && base.name == q->name && base.declPtr == q->declPtr; } bool StructTy::operator==(const Ty& other) const { return NominalTyEqualTo(*this, other); } bool InterfaceTy::operator==(const Ty& other) const { return NominalTyEqualTo(*this, other); } bool ClassThisTy::operator==(const Ty& other) const { return NominalTyEqualTo(*this, other); } bool TypeAliasTy::operator==(const Ty& other) const { return NominalTyEqualTo(*this, other); } bool RefEnumTy::operator==(const Ty& other) const { return NominalTyEqualTo(*this, other); } bool ArrayTy::operator==(const Ty& other) const { auto q = dynamic_cast(&other); return q && typeArgs == q->typeArgs && dims == q->dims; } bool VArrayTy::operator==(const Ty& other) const { auto q = dynamic_cast(&other); return q && typeArgs == q->typeArgs && size == q->size; } bool TupleTy::operator==(const Ty& other) const { auto q = dynamic_cast(&other); return q && typeArgs.size() == q->typeArgs.size() && isClosureTy == q->isClosureTy && memcmp(typeArgs.data(), q->typeArgs.data(), typeArgs.size() * sizeof(intptr_t)) == 0; } bool FuncTy::operator==(const Ty& other) const { auto q = dynamic_cast(&other); return q && isC == q->isC && noCast == q->noCast && hasVariableLenArg == q->hasVariableLenArg && isClosureTy == q->isClosureTy && retTy == q->retTy && paramTys.size() == q->paramTys.size() && memcmp(paramTys.data(), q->paramTys.data(), paramTys.size() * sizeof(intptr_t)) == 0; } bool UnionTy::operator==(const Ty& other) const { auto q = dynamic_cast(&other); return q && tys == q->tys; } bool IntersectionTy::operator==(const Ty& other) const { auto q = dynamic_cast(&other); return q && tys == q->tys; } bool GenericsTy::operator==(const Ty& other) const { auto q = dynamic_cast(&other); return q && decl == q->decl; } } // namespace Cangjie::AST cangjie_compiler-1.0.7/src/AST/Utils.cpp000066400000000000000000000742571510705540100200500ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the AST utils interface. */ #include "cangjie/AST/Utils.h" #include #include #include #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/Utils.h" #include "cangjie/Utils/ConstantsUtils.h" #include "cangjie/Utils/FloatFormat.h" #include "cangjie/Utils/StdUtils.h" #include "cangjie/Utils/Utils.h" namespace Cangjie::AST { void AddCurFile(Node& root, Ptr file) { Ptr curFile = file; unsigned walkerID = Walker::GetNextWalkerID(); /** * A set that stores all visited nodes to avoid multiple visits to the same node. */ std::function)> setCurFile = [&curFile, &setCurFile, walkerID](Ptr node) { switch (node->astKind) { case ASTKind::PACKAGE: { auto package = RawStaticCast(node); for (auto& it : package->files) { Walker(it.get(), walkerID, setCurFile).Walk(); } return VisitAction::STOP_NOW; } case ASTKind::FILE: { curFile = RawStaticCast(node); curFile->curFile = curFile; for (auto& import : curFile->imports) { import->curFile = curFile; } return VisitAction::WALK_CHILDREN; } default: if (node->curFile == nullptr) { node->curFile = curFile; } // For diag after macro expansion. if (node->TestAttr(Attribute::MACRO_EXPANDED_NODE) && node->curMacroCall) { AddMacroAttr(*node); return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; } }; Walker(&root, walkerID, setCurFile).Walk(); } /** * Add Attribute and curfile in macro expanded node. */ void AddMacroAttr(AST::Node& node) { if (!node.TestAttr(Attribute::MACRO_EXPANDED_NODE) || !node.curMacroCall) { return; } auto macroCall = node.curMacroCall; auto pInvocation = macroCall->GetInvocation(); if (!pInvocation) { return; } auto curFile = node.curFile; if (IsPureAnnotation(*pInvocation)) { Walker walker(&node, [curFile](auto curNode) -> VisitAction { curNode->curFile = curFile; return VisitAction::WALK_CHILDREN; }); walker.Walk(); return; } Walker walker(&node, [macroCall, curFile](Ptr curNode) -> VisitAction { curNode->EnableAttr(Attribute::MACRO_EXPANDED_NODE); curNode->curMacroCall = macroCall; curNode->curFile = curFile; return VisitAction::WALK_CHILDREN; }); walker.Walk(); } std::vector> SortModifierByPos(const std::set& modifiers) { std::vector> modifiersVec{}; (void)std::for_each(modifiers.begin(), modifiers.end(), [&](auto& mod) { modifiersVec.push_back(&mod); }); std::sort( modifiersVec.begin(), modifiersVec.end(), [](auto& mod1, auto& mod2) { return mod1->begin < mod2->begin; }); return modifiersVec; } bool IsMemberParam(const Decl& decl) { return decl.astKind == AST::ASTKind::FUNC_PARAM && StaticCast(decl).isMemberParam; } bool IsSingleRuneStringLiteral(const Expr& expr) { if (auto lit = DynamicCast(&expr); lit && lit->kind == LitConstKind::STRING && lit->codepoint.size() == 1) { return true; } return false; } bool IsSingleByteStringLiteral(const Expr& expr) { if (IsSingleRuneStringLiteral(expr) && StaticCast(expr).codepoint.front() <= std::numeric_limits::max()) { return true; } return false; } Ptr GetSizeDecl(const AST::Ty& ty) { if (!ty.IsClassLike()) { return nullptr; } auto decl = Ty::GetDeclOfTy(&ty); CJC_ASSERT(decl); for (auto& member : decl->GetMemberDecls()) { if (auto pd = DynamicCast(member.get()); pd && !pd->getters.empty() && member->identifier == "size") { return pd->getters[0].get(); } } return nullptr; } std::optional HasJavaAttr(const AST::Node& node) noexcept { if (node.TestAttr(Attribute::JAVA_APP)) { return {Attribute::JAVA_APP}; } if (node.TestAttr(Attribute::JAVA_EXT)) { return {Attribute::JAVA_EXT}; } return std::nullopt; } namespace { Ptr GetPrimitiveUpperBoundTy(Ty& ty) { if (ty.IsPrimitive()) { return RawStaticCast(&ty); } if (!ty.IsGeneric()) { return nullptr; } auto& genericTy = static_cast(ty); if (genericTy.lowerBound != nullptr && genericTy.lowerBound->IsPrimitive()) { return RawStaticCast(genericTy.lowerBound); } return nullptr; } const std::map, TypeKind> NATIVE_TYPEKIND_MAP{ {{32, TypeKind::TYPE_INT_NATIVE}, TypeKind::TYPE_INT32}, {{64, TypeKind::TYPE_INT_NATIVE}, TypeKind::TYPE_INT64}, {{32, TypeKind::TYPE_UINT_NATIVE}, TypeKind::TYPE_UINT32}, {{64, TypeKind::TYPE_UINT_NATIVE}, TypeKind::TYPE_UINT64}, }; } // namespace std::mutex g_floatTypeInfoMtx; FloatTypeInfo GetFloatTypeInfoByKind(AST::TypeKind kind) { const static std::unordered_map floatTypeInfoMap = { {AST::TypeKind::TYPE_FLOAT16, {0xF800, "5.960464477539063E-8", "6.5504E4"}}, {AST::TypeKind::TYPE_FLOAT32, {0xFF000000, "1.40129846E-45", "3.40282347E38"}}, {AST::TypeKind::TYPE_FLOAT64, {0xFFE0000000000000, "4.9406564584124654E-324", "1.7976931348623157E308"}}, {AST::TypeKind::TYPE_IDEAL_FLOAT, {0xFFE0000000000000, "4.9406564584124654E-324", "1.7976931348623157E308"}}}; std::lock_guard guard(g_floatTypeInfoMtx); auto info = floatTypeInfoMap.find(kind); CJC_ASSERT(info != floatTypeInfoMap.end()); return info->second; } void InitializeLitConstValue(LitConstExpr& lce) { if (!Ty::IsTyCorrect(lce.ty)) { return; } // LitConstExpr is always a const expression. lce.isConst = true; // We don't need to handle the exception throwing from string-to-number api here, // because it's already been done in Parser phase. auto primitiveTy = GetPrimitiveUpperBoundTy(*lce.ty); if (primitiveTy == nullptr) { return; } if (primitiveTy->IsInteger()) { auto kind = primitiveTy->IsNative() ? NATIVE_TYPEKIND_MAP.at({primitiveTy->bitness, primitiveTy->kind}) : primitiveTy->kind; auto stringVal = IsSingleByteStringLiteral(lce) ? std::to_string(lce.codepoint.front()) : lce.stringValue; lce.constNumValue.asInt.InitIntLiteral(stringVal, kind); } else if (primitiveTy->IsFloating()) { std::string stringValue = lce.stringValue; stringValue.erase(std::remove(stringValue.begin(), stringValue.end(), '_'), stringValue.end()); if (auto val = Stold(stringValue)) { lce.constNumValue.asFloat.value = *val; } else { if (Cangjie::FloatFormat::IsUnderFlowFloat(stringValue)) { lce.constNumValue.asFloat.flowStatus = Expr::FlowStatus::UNDER; lce.constNumValue.asFloat.value = 0; } else { lce.constNumValue.asFloat.flowStatus = Expr::FlowStatus::OVER; uint64_t value = GetFloatTypeInfoByKind(AST::TypeKind::TYPE_FLOAT64).inf >> 1; lce.constNumValue.asFloat.value = reinterpret_cast(value); } } } else if (primitiveTy->IsBoolean()) { lce.constNumValue.asBoolean = lce.stringValue == "true"; } } void SetOuterFunctionDecl(AST::Decl& decl) { Ptr root = nullptr; if (auto fd = DynamicCast(&decl)) { root = fd->funcBody.get(); } else if (auto vd = DynamicCast(&decl); vd && (vd->TestAttr(Attribute::GLOBAL) || (vd->outerDecl && vd->outerDecl->IsNominalDecl()))) { // As for decls in lambda expr, their outerDecl is lambda's left decl, may be a VarDecl(only global var or // member var). Because lambda is expr in AST, not a decl, so we can't set lambda as outerDecl. root = vd->initializer.get(); } if (root == nullptr) { return; } auto visitor = [&decl](Ptr node) -> VisitAction { // `var d = { i => let temp = 1 }`, outerDecls of lambda param `i` and local var decl `temp` are both `d` if (auto fp = DynamicCast(node); fp) { fp->outerDecl = &decl; return VisitAction::SKIP_CHILDREN; } else if (auto funcDecl = DynamicCast(node); funcDecl) { funcDecl->outerDecl = &decl; return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; Walker walker(root, visitor); walker.Walk(); } bool IsInDeclWithAttribute(const Decl& decl, AST::Attribute attr) { auto current = &decl; while (current != nullptr) { if (current->TestAttr(attr)) { return true; } if (auto fd = DynamicCast(current); fd && fd->ownerFunc && fd->ownerFunc->TestAttr(attr)) { return true; // Default param decl of a generic function is also treated as in generic. } current = current->outerDecl; } return false; } std::vector> FlattenVarWithPatternDecl(const AST::VarWithPatternDecl& vwpDecl) { std::vector> result; std::deque> patterns; patterns.emplace_back(vwpDecl.irrefutablePattern.get()); while (!patterns.empty()) { Ptr pattern = patterns.front(); patterns.pop_front(); switch (pattern->astKind) { case AST::ASTKind::WILDCARD_PATTERN: case AST::ASTKind::VAR_PATTERN: result.emplace_back(pattern); break; case AST::ASTKind::TUPLE_PATTERN: { auto tuplePattern = StaticCast(pattern); for (size_t i = 0; i < tuplePattern->patterns.size(); i++) { patterns.emplace_back(tuplePattern->patterns[i].get()); } break; } case AST::ASTKind::ENUM_PATTERN: { auto enumPattern = StaticCast(pattern); for (size_t i = 0; i < enumPattern->patterns.size(); i++) { patterns.emplace_back(enumPattern->patterns[i].get()); } break; } default: break; } } return result; } std::string GetAnnotatedDeclKindString(const Decl& decl) { switch (decl.astKind) { case ASTKind::CLASS_DECL: case ASTKind::INTERFACE_DECL: case ASTKind::STRUCT_DECL: case ASTKind::ENUM_DECL: return "type"; case ASTKind::FUNC_DECL: if (decl.TestAttr(Attribute::CONSTRUCTOR)) { return "init"; } CJC_ASSERT(decl.TestAnyAttr(Attribute::IN_CLASSLIKE, Attribute::IN_STRUCT, Attribute::IN_ENUM)); return "member function"; case ASTKind::PROP_DECL: return "member property"; case ASTKind::VAR_DECL: CJC_ASSERT(decl.TestAnyAttr(Attribute::IN_CLASSLIKE, Attribute::IN_STRUCT, Attribute::IN_ENUM)); return "member variable"; case ASTKind::FUNC_PARAM: return "parameter"; default: CJC_ABORT(); return ""; } } /** * Iterate all variables and functions in @p id. */ static void IterateAllMembersInTypeDecl(const InheritableDecl& id, const std::function action) { for (auto& decl : id.GetMemberDeclPtrs()) { if (decl->astKind == ASTKind::FUNC_DECL && !decl->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { action(*decl); } else if (decl->astKind == ASTKind::PROP_DECL) { auto propDecl = StaticAs(decl.get()); for (auto& funcDecl : propDecl->getters) { action(*funcDecl.get()); } for (auto& funcDecl : propDecl->setters) { action(*funcDecl.get()); } } else if (decl->astKind == ASTKind::VAR_DECL) { action(*decl); } } } void IterateAllExportableDecls(const Package& pkg, const std::function action) { for (auto& file : pkg.files) { for (auto& it : file->decls) { action(*it); if (auto id = DynamicCast(it.get())) { IterateAllMembersInTypeDecl(*id, action); } } } } bool IsPackageMemberAccess(const AST::MemberAccess& ma) { if (ma.baseExpr == nullptr) { return false; } Ptr target = ma.baseExpr->GetTarget(); if (target == nullptr) { return false; } if (target->astKind == ASTKind::PACKAGE_DECL) { return true; } return false; } bool IsThisOrSuper(const AST::Expr& expr) { if (auto re = DynamicCast(&expr)) { return re->isThis || re->isSuper; } return false; } std::string GetImportedItemFullName(const ImportContent& content, const std::string& commonPrefix) { std::stringstream ss; if (!commonPrefix.empty()) { ss << commonPrefix << "."; } ss << content.GetPrefixPath(); if (content.kind == ImportKind::IMPORT_ALIAS || content.kind == ImportKind::IMPORT_SINGLE) { ss << "." << content.identifier.Val(); } return ss.str(); } bool IsCondition(const Expr& e) { if (Is(e)) { return true; } if (auto p = DynamicCast(&e)) { return IsCondition(*p->expr); } if (auto bin = DynamicCast(&e); bin && (bin->op == TokenKind::AND || bin->op == TokenKind::OR)) { return IsCondition(*bin->leftExpr) || IsCondition(*bin->rightExpr); } return false; } bool DoesNotHaveEnumSubpattern(const LetPatternDestructor& let) { for (auto& p : let.patterns) { if (auto enumPattern = DynamicCast(&*p); enumPattern && !enumPattern->patterns.empty()) { return false; } } return true; } #define ATTR_ACCESS_MAP \ ATTR_WITH_LEVEL(Attribute::PRIVATE, AccessLevel::PRIVATE) \ ATTR_WITH_LEVEL(Attribute::INTERNAL, AccessLevel::INTERNAL) \ ATTR_WITH_LEVEL(Attribute::PROTECTED, AccessLevel::PROTECTED) \ ATTR_WITH_LEVEL(Attribute::PUBLIC, AccessLevel::PUBLIC) AccessLevel GetAccessLevel(const Node& node) { static const std::array, 4> ATTRIBUTE_ACCESS_MAP = { #define ATTR_WITH_LEVEL(ATTR, LEVEL) std::make_pair(ATTR, LEVEL), ATTR_ACCESS_MAP #undef ATTR_WITH_LEVEL }; auto level = node.astKind == ASTKind::IMPORT_SPEC ? AccessLevel::PRIVATE : AccessLevel::INTERNAL; for (const auto& e : ATTRIBUTE_ACCESS_MAP) { if (node.TestAttr(e.first)) { level = e.second; break; } } // When decl has parent type decl, real access level will not larger than parent decl's access level. if (auto decl = DynamicCast(&node); decl && decl->outerDecl && decl->outerDecl->IsNominalDecl()) { for (const auto& e : ATTRIBUTE_ACCESS_MAP) { if (decl->outerDecl->TestAttr(e.first) && e.second < level) { return e.second; } } } return level; } Attribute GetAttrByAccessLevel(AccessLevel level) { static const std::unordered_map ACCESS_TO_ATTR_MAP = { #define ATTR_WITH_LEVEL(ATTR, LEVEL) {LEVEL, ATTR}, ATTR_ACCESS_MAP #undef ATTR_WITH_LEVEL }; return ACCESS_TO_ATTR_MAP.at(level); } std::string GetAccessLevelStr(const AST::Node& node, const std::string& surround) { static const std::array, 4> ATTRIBUTE_ACCESS_MAP = { std::make_pair(Attribute::PRIVATE, "private"), std::make_pair(Attribute::INTERNAL, "internal"), std::make_pair(Attribute::PROTECTED, "protected"), std::make_pair(Attribute::PUBLIC, "public"), }; for (const auto& e : ATTRIBUTE_ACCESS_MAP) { if (node.TestAttr(e.first)) { return surround.empty() ? e.second : surround + e.second + surround; } } return surround.empty() ? "private" : surround + "private" + surround; } std::string GetAccessLevelStr(const AST::Package& pkg) { switch (pkg.accessible) { case AccessLevel::INTERNAL: return "internal"; case AccessLevel::PROTECTED: return "protected"; default: return "public"; } } inline bool NeedPoint(const std::string& str) { return str.size() > 0 && (isalpha(str.back()) || isdigit(str.back())); } void ExtractArgumentsOfDeprecatedAnno( const Ptr annotation, std::string& message, std::string& since, bool& strict ) { for (auto& arg : annotation->args) { if (auto lce = DynamicCast(arg->expr.get()); lce) { if (arg->name == "" || arg->name == "message") { message = " " + lce->stringValue; } else if (arg->name == "since") { since = " since " + lce->stringValue; } else if (arg->name == "strict") { strict = lce->ToString() == "true"; } } } if (NeedPoint(message)) { message += '.'; } if (NeedPoint(since)) { since += '.'; } } bool IsValidCFuncConstructorCall(const CallExpr& ce) { // ce.ty is correct only when the whole CFunc constructor call is correct if (Ty::IsTyCorrect(ce.ty) && ce.baseFunc && Is(ce.baseFunc)) { // if this is a builtin CFunc constructor call, do not check the arguments if (auto callee = DynamicCast(StaticCast(ce.baseFunc.get())->ref.target); callee && callee->type == BuiltInType::CFUNC) { return true; } } return false; } bool IsVirtualMember(const Decl& decl) { // rule 1: top-level function is not virtual function if (!decl.outerDecl) { return false; } /** * rule 2: function in non-open and non-abstract class, is not virtual function * class A { * // The following functions are not semantic virtual functions since the class is not `open`, * // they will never be overrode even if they have `open` flag. * public open func foo() {} * open func goo() {} * } */ auto isInNonOpenClass = decl.outerDecl->astKind == AST::ASTKind::CLASS_DECL && !decl.outerDecl->TestAnyAttr(AST::Attribute::ABSTRACT, AST::Attribute::OPEN); if (isInNonOpenClass) { return false; } // rule 3: non-default static function, constructor and distructor are not virtual function // The default function needs to be dispatched to the subtype. if (auto fd = DynamicCast(&decl); (decl.TestAttr(AST::Attribute::STATIC) && !decl.TestAttr(AST::Attribute::DEFAULT)) || decl.TestAttr(AST::Attribute::CONSTRUCTOR) || (fd && fd->IsFinalizer())) { return false; } // rule 4: generic instantiated function, is not virtual function if (decl.TestAttr(AST::Attribute::GENERIC_INSTANTIATED)) { return false; } // rule 5: function defined within an extend is not virtual function if (decl.outerDecl->astKind == AST::ASTKind::EXTEND_DECL) { return false; } // rule 6: only public or protected function has the opportunity to be overrode if (decl.outerDecl->astKind == AST::ASTKind::CLASS_DECL && !decl.TestAttr(AST::Attribute::PUBLIC) && !decl.TestAttr(AST::Attribute::PROTECTED)) { return false; } // rule 7: if the base function has open modifier, or is abstract, // or is defined within interface, it must be virtual function return decl.TestAnyAttr(AST::Attribute::OPEN, AST::Attribute::ABSTRACT) || decl.outerDecl->astKind == AST::ASTKind::INTERFACE_DECL; } std::vector GetVarsInitializationOrderWithPositions(const Decl& parentDecl) { std::vector commonDecls; std::vector platformDecls; std::size_t idx = 0; for (auto& decl : parentDecl.GetMemberDecls()) { if (decl->astKind != ASTKind::VAR_DECL || decl->IsStaticOrGlobal()) { continue; } Ptr varDecl = StaticCast(decl.get()); if (varDecl->TestAttr(AST::Attribute::FROM_COMMON_PART)) { commonDecls.push_back({varDecl, idx}); } else { platformDecls.push_back({varDecl, idx}); } idx++; } std::sort(platformDecls.begin(), platformDecls.end(), [](const auto& lhs, const auto& rhs) { return lhs.decl->begin < rhs.decl->begin; }); std::vector resultDecls; auto platformDeclsIt = platformDecls.begin(); std::unordered_set> wasPlatformVars; for (auto& [commonDecl, commonDeclOffset] : commonDecls) { std::unordered_set> platformVarsDeps; for (auto& dep : commonDecl->dependencies) { if (!dep->TestAttr(Attribute::PLATFORM)) { continue; } auto [_, inserted] = wasPlatformVars.emplace(dep); if (inserted) { platformVarsDeps.emplace(dep); } } while (!platformVarsDeps.empty()) { CJC_ASSERT(platformDeclsIt != platformDecls.end()); auto it = platformVarsDeps.find(platformDeclsIt->decl); if (it != platformVarsDeps.end()) { platformVarsDeps.erase(it); } resultDecls.emplace_back(*platformDeclsIt); platformDeclsIt++; } resultDecls.push_back({commonDecl, commonDeclOffset}); } for (; platformDeclsIt != platformDecls.end(); platformDeclsIt++) { resultDecls.emplace_back(*platformDeclsIt); } return resultDecls; } void InsertPropGetterSignature(PropDecl& prop, Attribute attrToBeSet) { auto getter = MakeOwned(); getter->identifier = "get"; getter->outerDecl = prop.outerDecl; getter->propDecl = ∝ getter->isGetter = true; getter->CloneAttrs(prop); getter->DisableAttr(Attribute::MUT); getter->EnableAttr(Attribute::COMPILER_ADD, attrToBeSet); if (prop.outerDecl->astKind == ASTKind::INTERFACE_DECL) { getter->EnableAttr(Attribute::PUBLIC); } OwnedPtr getterBody = MakeOwned(); getterBody->EnableAttr(Attribute::COMPILER_ADD); getterBody->paramLists.push_back(MakeOwned()); getterBody->paramLists.begin()->get()->EnableAttr(Attribute::COMPILER_ADD); getter->funcBody = std::move(getterBody); getter->funcBody->funcDecl = getter.get(); prop.getters.emplace_back(std::move(getter)); } void InsertPropSetterSignature(PropDecl& prop, Attribute attrToBeSet) { auto setter = MakeOwned(); setter->identifier = "set"; setter->outerDecl = prop.outerDecl; setter->propDecl = ∝ setter->isSetter = true; setter->CloneAttrs(prop); setter->DisableAttr(Attribute::MUT); setter->EnableAttr(Attribute::COMPILER_ADD, attrToBeSet); if (prop.outerDecl->astKind == ASTKind::INTERFACE_DECL) { setter->EnableAttr(Attribute::PUBLIC); } OwnedPtr setterBody = MakeOwned(); setterBody->EnableAttr(Attribute::COMPILER_ADD); setterBody->paramLists.push_back(MakeOwned()); auto setterParam = MakeOwned(); setterParam->EnableAttr(Attribute::COMPILER_ADD); setterParam->identifier = "set"; setterBody->paramLists.begin()->get()->params.push_back(std::move(setterParam)); setterBody->paramLists.begin()->get()->EnableAttr(Attribute::COMPILER_ADD); setter->funcBody = std::move(setterBody); setter->funcBody->funcDecl = setter.get(); prop.setters.emplace_back(std::move(setter)); } void InsertPropConvertedByField(ClassDecl& decl, VarDecl& varDecl, Attribute attrToBeSet) { auto propDecl = MakeOwned(); propDecl->begin = varDecl.begin; propDecl->end = varDecl.end; propDecl->keywordPos = varDecl.keywordPos; propDecl->identifier = varDecl.identifier; propDecl->colonPos = varDecl.colonPos; propDecl->type = std::move(varDecl.type); propDecl->ty = varDecl.ty; propDecl->CloneAttrs(varDecl); propDecl->EnableAttr(Attribute::DESUGARED_MIRROR_FIELD); propDecl->modifiers.insert(varDecl.modifiers.begin(), varDecl.modifiers.end()); propDecl->isVar = varDecl.isVar; for (auto& anno : varDecl.annotations) { propDecl->annotations.emplace_back(ASTCloner::Clone(anno.get())); } if (varDecl.isVar) { propDecl->EnableAttr(Attribute::MUT); Modifier mut = Modifier(TokenKind::MUT, varDecl.begin); mut.curFile = varDecl.curFile; propDecl->modifiers.insert(std::move(mut)); } propDecl->outerDecl = varDecl.outerDecl; InsertPropGetterSignature(*propDecl.get(), attrToBeSet); if (varDecl.isVar) { InsertPropSetterSignature(*propDecl.get(), attrToBeSet); } decl.body->decls.emplace_back(std::move(propDecl)); } void InsertMirrorVarProp(ClassDecl& decl, Attribute attrToBeSet) { auto& members = decl.GetMemberDecls(); // Collect the original field std::vector oldVars; for (auto& member : members) { if (member->astKind == ASTKind::VAR_DECL) { oldVars.emplace_back(StaticAs(member.get())); } } // Generate and insert the new prop for (auto var : oldVars) { InsertPropConvertedByField(decl, *var, attrToBeSet); } // Delete the original field members.erase(std::remove_if(members.begin(), members.end(), [](auto& node) { return node.get()->astKind == ASTKind::VAR_DECL; }), members.end()); } } // namespace Cangjie::AST namespace { using namespace Cangjie::AST; void SetPositionAndCurFileByProvidedNode(Node& consumer, Node& provider) { consumer.curFile = provider.curFile; consumer.begin = provider.begin; consumer.end = provider.end; } } namespace Cangjie::Interop::Java { bool IsImpl(const Decl& decl) { return !decl.TestAttr(Attribute::JAVA_MIRROR) && decl.TestAttr(Attribute::JAVA_MIRROR_SUBTYPE); } bool IsJObject(const Decl& decl) { return IsJObject(decl, decl.fullPackageName); } bool IsJObject(const Decl& decl, const std::string& packageName) { return IsMirror(decl) && decl.identifier.Val() == INTEROP_JOBJECT_NAME && packageName == INTEROP_JAVA_LANG_PACKAGE; } bool IsMirror(const Decl& decl) { return decl.TestAttr(Attribute::JAVA_MIRROR); } bool IsCJMapping(const Decl& decl) { return decl.TestAttr(Attribute::JAVA_CJ_MAPPING); } /** * public func $getJavaRef(): Java_CFFI_JavaEntity { * return Java_CFFI_JavaEntity() * } */ void InsertJavaRefGetterStubWithBody(ClassDecl& decl) { std::vector> callParams; std::vector> paramLists; auto pl = CreateFuncParamList(std::move(callParams)); pl->begin = decl.begin; pl->end = decl.end; paramLists.push_back(std::move(pl)); auto constructor = CreateCallExpr(CreateRefExpr(INTEROPLIB_CFFI_JAVA_ENTITY), {}); auto ret = CreateReturnExpr(std::move(constructor)); std::vector> nodes; nodes.emplace_back(std::move(ret)); auto funcBody = CreateFuncBody( std::move(paramLists), CreateRefType(INTEROPLIB_CFFI_JAVA_ENTITY), CreateBlock(std::move(nodes))); auto fd = CreateFuncDecl(JAVA_REF_GETTER_FUNC_NAME, std::move(funcBody), nullptr); fd->EnableAttr(Attribute::PUBLIC, Attribute::IN_CLASSLIKE); fd->fullPackageName = decl.fullPackageName; fd->funcBody->funcDecl = fd.get(); fd->funcBody->parentClassLike = &decl; fd->outerDecl = &decl; decl.body->decls.emplace_back(std::move(fd)); } bool IsDeclAppropriateForSyntheticClassGeneration(const Decl& decl) { return decl.TestAttr(Attribute::JAVA_MIRROR) && (decl.astKind == ASTKind::INTERFACE_DECL || (decl.astKind == ASTKind::CLASS_DECL && decl.TestAttr(Attribute::ABSTRACT))); } std::string GetSyntheticNameFromClassLike(const ClassLikeDecl& cld) { return cld.identifier.Val() + "$impl"; } // abstract on parser stage, on sema stage abstractness will be removed void InsertSyntheticClassDecl(ClassLikeDecl& decl, File& file) { auto synthetic = MakeOwned(); if (decl.TestAttr(Attribute::PUBLIC)) { synthetic->EnableAttr(Attribute::PUBLIC); } else if (decl.TestAttr(Attribute::INTERNAL)) { synthetic->EnableAttr(Attribute::INTERNAL); } else if (decl.TestAttr(Attribute::PROTECTED)) { synthetic->EnableAttr(Attribute::PROTECTED); } else if (decl.TestAttr(Attribute::PRIVATE)) { synthetic->EnableAttr(Attribute::PRIVATE); } synthetic->EnableAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE, Attribute::COMPILER_ADD, Attribute::ABSTRACT); synthetic->identifier = GetSyntheticNameFromClassLike(decl); synthetic->identifier.SetPos(decl.identifier.Begin(), decl.identifier.End()); if (decl.astKind == ASTKind::INTERFACE_DECL) { // add JObject as supertype auto jobject = CreateRefType(INTEROP_JOBJECT_NAME); SetPositionAndCurFileByProvidedNode(*jobject, decl); synthetic->inheritedTypes.emplace_back(std::move(jobject)); } synthetic->inheritedTypes.emplace_back(CreateRefType(decl)); synthetic->fullPackageName = decl.fullPackageName; SetPositionAndCurFileByProvidedNode(*synthetic, decl); synthetic->body = MakeOwned(); SetPositionAndCurFileByProvidedNode(*synthetic->body, *synthetic); synthetic->moduleName = ::Cangjie::Utils::GetRootPackageName(decl.fullPackageName); file.decls.emplace_back(std::move(synthetic)); } } // namespace Cangjie::Interop::Java cangjie_compiler-1.0.7/src/AST/Walker.cpp000066400000000000000000001461601510705540100201660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Walker related classes. */ #include "cangjie/AST/Walker.h" #include #include "cangjie/AST/Match.h" #include "cangjie/Basic/Match.h" using namespace Cangjie; using namespace Cangjie::AST; using namespace Meta; namespace Cangjie::AST { template std::atomic_uint WalkerT::nextWalkerID = 1; template unsigned WalkerT::GetNextWalkerID() { // All instantiate of WalkerT must share same counter. if (WalkerT::nextWalkerID == 0) { WalkerT::nextWalkerID++; } return WalkerT::nextWalkerID++; } template class WalkerT; template class WalkerT; } // namespace Cangjie::AST template VisitAction Walker::Walk(Ptr curNode) const; template VisitAction ConstWalker::Walk(Ptr curNode) const; template VisitAction WalkerT::Walk(Ptr curNode) const { if (!curNode) { return VisitAction::WALK_CHILDREN; } if (curNode->astKind != ASTKind::MODIFIER && curNode->visitedByWalkerID == ID) { // If already visited. // Modifiers are usually stored in a std::set. return VisitAction::WALK_CHILDREN; } curNode->visitedByWalkerID = ID; VisitAction action = VisitAction::WALK_CHILDREN; if (VisitPre) { // If VisitPost function is given, call it first. action = VisitPre(curNode); } if (action == VisitAction::STOP_NOW) { return action; } if (action == VisitAction::WALK_CHILDREN) { if (Is(curNode)) { auto expr = StaticAs(curNode); if (Walk(expr->desugarExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } else if (Is(curNode)) { auto decl = StaticCast(curNode); for (auto& it : decl->annotations) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(decl->annotationsArray.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } switch (curNode->astKind) { case ASTKind::PACKAGE: { auto package = StaticAs(curNode); // In mock process, genericInstantiatedDecls may change during iteration, so don't using iterator for (size_t i = 0; i < package->genericInstantiatedDecls.size(); ++i) { if (package->genericInstantiatedDecls[i]->TestAttr(Attribute::FROM_COMMON_PART)) { continue; } if (Walk(package->genericInstantiatedDecls[i].get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } for (auto& it : package->files) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } // Source imported decls also should be walked. for (auto& srcFunc : package->srcImportedNonGenericDecls) { if (Walk(srcFunc) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::FILE: { auto file = StaticAs(curNode); if (Walk(file->package.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& it : file->imports) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } for (auto& decl : file->exportedInternalDecls) { if (Walk(decl.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } for (auto& it : file->decls) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::PRIMARY_CTOR_DECL: { auto pcd = StaticAs(curNode); for (auto modifier : pcd->modifiers) { if (Walk(&modifier) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(pcd->funcBody.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::MACRO_DECL: { auto md = StaticAs(curNode); for (auto modifier : md->modifiers) { if (Walk(&modifier) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (!md->desugarDecl) { if (Walk(md->funcBody.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } else { if (Walk(md->desugarDecl.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::MAIN_DECL: { auto md = StaticAs(curNode); if (md->desugarDecl) { if (Walk(md->desugarDecl.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } else { if (Walk(md->funcBody.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::FUNC_DECL: { auto fd = StaticAs(curNode); for (auto modifier : fd->modifiers) { if (Walk(&modifier) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(fd->funcBody.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::FUNC_BODY: { auto fb = StaticAs(curNode); if (Walk(fb->generic.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& paramList : fb->paramLists) { if (Walk(paramList.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(fb->retType.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(fb->body.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::FUNC_PARAM_LIST: { auto fpl = StaticAs(curNode); for (auto& param : fpl->params) { if (Walk(param.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::FUNC_PARAM: { auto fp = StaticAs(curNode); if (Walk(fp->type.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(fp->assignment.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(fp->desugarDecl.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::MACRO_EXPAND_PARAM: { auto mep = StaticAs(curNode); if (Walk(mep->invocation.decl.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::PROP_DECL: { auto pd = StaticAs(curNode); for (auto modifier : pd->modifiers) { if (Walk(&modifier) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(pd->type.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& it : pd->getters) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } for (auto& it : pd->setters) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::VAR_WITH_PATTERN_DECL: { auto vpd = StaticAs(curNode); if (Walk(vpd->irrefutablePattern.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(vpd->type.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(vpd->initializer.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::VAR_DECL: { auto vd = StaticAs(curNode); for (auto modifier : vd->modifiers) { if (Walk(&modifier) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(vd->type.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(vd->initializer.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::TYPE_ALIAS_DECL: { auto ta = StaticAs(curNode); if (Walk(ta->generic.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(ta->type.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::CLASS_DECL: { auto cd = StaticAs(curNode); for (auto modifier : cd->modifiers) { if (Walk(&modifier) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(cd->generic.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& refType : cd->inheritedTypes) { if (Walk(refType.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(cd->body.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::INTERFACE_DECL: { auto id = StaticAs(curNode); for (auto modifier : id->modifiers) { if (Walk(&modifier) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(id->generic.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& it : id->inheritedTypes) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(id->body.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::ENUM_DECL: { auto ed = StaticAs(curNode); if (Walk(ed->generic.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& it : ed->inheritedTypes) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } for (auto& it : ed->constructors) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } for (auto& it : ed->members) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::STRUCT_DECL: { auto sd = StaticAs(curNode); for (auto modifier : sd->modifiers) { if (Walk(&modifier) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(sd->generic.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& it : sd->inheritedTypes) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(sd->body.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::STRUCT_BODY: { auto rb = StaticAs(curNode); for (auto& it : rb->decls) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::EXTEND_DECL: { auto ed = StaticAs(curNode); if (Walk(ed->extendedType.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& it : ed->inheritedTypes) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(ed->generic.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& it : ed->members) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::CLASS_BODY: { auto cb = StaticAs(curNode); for (auto& it : cb->decls) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::INTERFACE_BODY: { auto ib = StaticAs(curNode); for (auto& it : ib->decls) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::MACRO_EXPAND_DECL: { auto med = StaticAs(curNode); if (Walk(med->invocation.decl.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::MACRO_EXPAND_EXPR: { auto mee = StaticAs(curNode); if (Walk(mee->invocation.decl.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::IF_EXPR: { auto ie = StaticAs(curNode); if (Walk(ie->condExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(ie->thenBody.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (ie->hasElse) { if (Walk(ie->elseBody.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::LET_PATTERN_DESTRUCTOR: { auto lpd = StaticAs(curNode); for (auto& p : lpd->patterns) { if (Walk(p.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(lpd->initializer.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::MATCH_CASE: { auto mc = StaticAs(curNode); for (auto& it : mc->patterns) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(mc->patternGuard.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(mc->exprOrDecls.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::MATCH_CASE_OTHER: { auto mco = StaticAs(curNode); if (Walk(mco->matchExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(mco->exprOrDecls.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::MATCH_EXPR: { auto me = StaticAs(curNode); if (me->matchMode) { if (Walk(me->selector.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& it : me->matchCases) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } } else { for (auto& it : me->matchCaseOthers) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::TRY_EXPR: { auto te = StaticAs(curNode); for (auto& it : te->resourceSpec) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(te->tryBlock.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (uint32_t cnt = 0; cnt < te->catchPatterns.size(); ++cnt) { if (Walk(te->catchPatterns[cnt].get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } for (uint32_t cnt = 0; cnt < te->catchBlocks.size(); ++cnt) { if (Walk(te->catchBlocks[cnt].get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } // Once the try-handle block has been desugared, we do not want to visit // the handle blocks again, since they have been turned into lambdas but // they still contain old AST nodes. for (const auto& handler : te->handlers) { if (te->desugarExpr) { break; } if (Walk(handler.commandPattern.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(handler.block.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (handler.desugaredLambda && Walk(handler.desugaredLambda.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(te->finallyBlock.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(te->tryLambda.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(te->finallyLambda.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::THROW_EXPR: { auto te = StaticAs(curNode); if (Walk(te->expr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::PERFORM_EXPR: { auto pe = StaticAs(curNode); if (Walk(pe->expr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::RESUME_EXPR: { auto re = StaticAs(curNode); if (Walk(re->withExpr.get()) == VisitAction::STOP_NOW || Walk(re->throwingExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::RETURN_EXPR: { auto re = StaticAs(curNode); if (!re->desugarExpr) { if (Walk(re->expr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::FOR_IN_EXPR: { auto fie = StaticAs(curNode); if (Walk(fie->pattern.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(fie->inExpression.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(fie->patternGuard.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(fie->body.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::WHILE_EXPR: { auto we = StaticAs(curNode); if (Walk(we->condExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(we->body.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::DO_WHILE_EXPR: { auto dwe = StaticAs(curNode); if (Walk(dwe->body.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(dwe->condExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::ASSIGN_EXPR: { auto ae = StaticAs(curNode); if (Walk(ae->leftValue.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(ae->rightExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::INC_OR_DEC_EXPR: { auto expr = StaticAs(curNode); if (Walk(expr->expr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::UNARY_EXPR: { auto ue = StaticAs(curNode); if (Walk(ue->expr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::BINARY_EXPR: { auto be = StaticAs(curNode); if (Walk(be->leftExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(be->rightExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::RANGE_EXPR: { auto re = StaticAs(curNode); if (Walk(re->startExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(re->stopExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(re->stepExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::SUBSCRIPT_EXPR: { auto se = StaticAs(curNode); if (Walk(se->baseExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& it : se->indexExprs) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::MEMBER_ACCESS: { auto ma = StaticAs(curNode); if (Walk(ma->baseExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& it : ma->typeArguments) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::FUNC_ARG: { auto fa = StaticAs(curNode); if (Walk(fa->expr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::CALL_EXPR: { auto ce = StaticAs(curNode); if (Walk(ce->baseFunc.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (ce->desugarArgs.has_value()) { for (auto& it : ce->desugarArgs.value()) { if (Walk(it) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } } else { // 'desugarArgs' contains 'ce->args'. for (auto& it : ce->args) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::PAREN_EXPR: { auto pe = StaticAs(curNode); if (Walk(pe->expr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::LAMBDA_EXPR: { auto le = StaticAs(curNode); if (Walk(le->funcBody.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::TRAIL_CLOSURE_EXPR: { auto tce = StaticAs(curNode); if (Walk(tce->expr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(tce->lambda.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::LIT_CONST_EXPR: { auto lce = StaticAs(curNode); if (Walk(lce->ref.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (!lce->desugarExpr && Walk(lce->siExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::STR_INTERPOLATION_EXPR: { auto sie = StaticAs(curNode); for (auto& it : sie->strPartExprs) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::INTERPOLATION_EXPR: { auto ie = StaticAs(curNode); if (Walk(ie->block.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::ARRAY_LIT: { auto al = StaticAs(curNode); for (auto& it : al->children) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::ARRAY_EXPR: { auto asl = StaticAs(curNode); if (Walk(asl->type.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& it : asl->args) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::POINTER_EXPR: { auto ptrExpr = StaticAs(curNode); if (Walk(ptrExpr->type.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(ptrExpr->arg.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::TUPLE_LIT: { auto tl = StaticAs(curNode); for (auto& it : tl->children) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::TYPE_CONV_EXPR: { auto expr = StaticAs(curNode); if (Walk(expr->type.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(expr->expr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::REF_EXPR: { auto re = StaticAs(curNode); for (auto& it : re->typeArguments) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::IF_AVAILABLE_EXPR: { auto ie = StaticCast(curNode); if (Walk(ie->GetArg()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(ie->GetLambda1()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(ie->GetLambda2()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::BLOCK: { auto block = StaticAs(curNode); for (auto& it : block->body) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::REF_TYPE: { auto rt = StaticAs(curNode); for (auto& typeArg : rt->typeArguments) { if (Walk(typeArg.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::QUALIFIED_TYPE: { auto qt = StaticAs(curNode); if (Walk(qt->baseType.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& ta : qt->typeArguments) { if (Walk(ta.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::OPTION_TYPE: { auto ot = StaticAs(curNode); if (Walk(ot->desugarType.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(ot->componentType.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::CONSTANT_TYPE: { auto ct = StaticAs(curNode); if (Walk(ct->constantExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::VARRAY_TYPE: { auto vt = StaticAs(curNode); if (Walk(vt->typeArgument.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(vt->constantType.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::PAREN_TYPE: { auto pt = StaticAs(curNode); if (Walk(pt->type.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::FUNC_TYPE: { auto ft = StaticAs(curNode); for (auto& paramType : ft->paramTypes) { if (Walk(paramType.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } if (Walk(ft->retType.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::TUPLE_TYPE: { auto tt = StaticAs(curNode); for (auto& it : tt->fieldTypes) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::GENERIC_CONSTRAINT: { auto gc = StaticAs(curNode); if (Walk(gc->type.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& upperBound : gc->upperBounds) { if (Walk(upperBound.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::CONST_PATTERN: { auto cp = StaticAs(curNode); if (Walk(cp->literal.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(cp->operatorCallExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::VAR_PATTERN: { auto vp = StaticAs(curNode); if (Walk(vp->varDecl.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::TUPLE_PATTERN: { auto tp = StaticAs(curNode); for (auto& pattern : tp->patterns) { if (Walk(pattern.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::TYPE_PATTERN: { auto tp = StaticAs(curNode); if (Walk(tp->pattern.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(tp->type.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::ENUM_PATTERN: { auto ep = StaticAs(curNode); if (Walk(ep->constructor.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& pattern : ep->patterns) { if (Walk(pattern.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::VAR_OR_ENUM_PATTERN: { auto vep = StaticAs(curNode); if (Walk(vep->pattern.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::EXCEPT_TYPE_PATTERN: { auto& exceptPattern = *StaticCast(curNode); if (Walk(exceptPattern.pattern.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& i : exceptPattern.types) { if (Walk(i.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::COMMAND_TYPE_PATTERN: { auto& commandPattern = *StaticCast(curNode); if (Walk(commandPattern.pattern.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& i : commandPattern.types) { if (Walk(i.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::ANNOTATION: { auto anno = StaticAs(curNode); if (Walk(anno->baseExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } for (auto& it : anno->args) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::SPAWN_EXPR: { auto se = StaticAs(curNode); if (se->arg && Walk(se->arg.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(se->task.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (se->futureObj) { if (Walk(se->futureObj.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::SYNCHRONIZED_EXPR: { auto se = StaticAs(curNode); // Notes: Seems that other part still needs information of se->mutex after desugar, // which seems weird. If simply break when se->desugar is not null, there are test // cases which fail. if (Walk(se->mutex.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } // If se is not desugared yet, we should be able to collect se->body. if (!se->desugarExpr && Walk(se->body.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::QUOTE_EXPR: { auto qe = StaticAs(curNode); for (auto& it : qe->exprs) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::IS_EXPR: { auto ie = StaticAs(curNode); if (Walk(ie->leftExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(ie->isType.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::AS_EXPR: { auto ae = StaticAs(curNode); if (Walk(ae->leftExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } if (Walk(ae->asType.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::BUILTIN_DECL: { auto bid = StaticAs(curNode); if (Walk(bid->generic.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::GENERIC: { auto generic = StaticAs(curNode); for (auto& it : generic->typeParameters) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } for (auto& it : generic->genericConstraints) { if (Walk(it.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::OPTIONAL_CHAIN_EXPR: { auto oce = StaticAs(curNode); // Only walk child when the optional chain is not desugared. if (!oce->desugarExpr && Walk(oce->expr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } case ASTKind::OPTIONAL_EXPR: { auto oe = StaticAs(curNode); if (Walk(oe->baseExpr.get()) == VisitAction::STOP_NOW) { return VisitAction::STOP_NOW; } action = VisitAction::WALK_CHILDREN; break; } default: { action = VisitAction::WALK_CHILDREN; } } } // If VisitPost function is given, it will be called after children being visited. // The final action is a combine of visitPre, Walk(children) and visitPost. if (VisitPost) { auto optionalAction = VisitPost(curNode); if (optionalAction != VisitAction::KEEP_DECISION) { action = optionalAction; } } CJC_ASSERT(action != VisitAction::KEEP_DECISION); return action; } cangjie_compiler-1.0.7/src/Basic/000077500000000000000000000000001510705540100166175ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Basic/CMakeLists.txt000066400000000000000000000013171510705540100213610ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set(BASIC_SRC StringConvertor.cpp DiagnosticEngine.cpp DiagnosticEngineImpl.cpp Position.cpp SourceManager.cpp Display.cpp DiagnosticEmitter.cpp DiagnosticJsonFormatter.cpp Utils.cpp Print.cpp) set(BASIC_SRC ${BASIC_SRC} Version.cpp) add_library(CangjieBasic OBJECT ${BASIC_SRC}) target_compile_options(CangjieBasic PRIVATE -Wno-deprecated-declarations) target_compile_options(CangjieBasic PRIVATE ${CJC_EXTRA_WARNINGS}) cangjie_compiler-1.0.7/src/Basic/DiagnosticEmitter.cpp000066400000000000000000001124071510705540100227460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the DiagnosticEmitter related classes. */ #include "DiagnosticEmitterImpl.h" #include #include #include "cangjie/Basic/Display.h" #include "cangjie/Basic/Print.h" #include "cangjie/Utils/Unicode.h" namespace Cangjie { constexpr char BITOR = '|'; constexpr char BITXOR = '^'; constexpr char TILDE = '~'; constexpr char SPACE = ' '; constexpr char COLON = ':'; constexpr char MAIN_HINT_SYMBOL = BITXOR; constexpr char OTHER_HINT_SYMBOL = TILDE; constexpr char MULTI_LINE_KEY_SYMBOL = BITOR; constexpr char MULTI_LINE_SYMBOL = '_'; constexpr char WITHOUT_SOURCE_SYMBOL = '#'; inline std::string GetNewLine() { return std::string{"\n"}; } inline std::string GetLineSymbol() { return std::string{"==>"}; } inline std::string CharacterOfNum(size_t n, char c) { return std::string(n, c); } inline auto g_spaceOfNum = std::bind(CharacterOfNum, std::placeholders::_1, ' '); inline std::string GetColoredString(DiagColor color, const std::string& str, bool isBright = false) { CJC_ASSERT(ColorPrintMap.find(color) != ColorPrintMap.end() && "no print color found"); std::string res = ColorPrintMap.at(color); if (isBright) { res += ANSI_COLOR_BRIGHT; } res += str; if (color != DiagColor::NO_COLOR) { res += ColorPrintMap.at(DiagColor::RESET); } return res; } void DiagnosticEmitterImpl::CollectInformation(std::vector& vec, IntegratedString& str, bool isMain) { CollectedInfo info; info.hint = str.str; info.color = str.color; info.isMain = isMain; CJC_ASSERT(str.range.end >= str.range.begin); if (str.range.begin.line == str.range.end.line) { info.range = str.range; } else { // If it is multi-line, it will store respectively based on start position and end position. auto startRange = MakeRange(str.range.begin, str.range.begin + 1); auto startInfo = CollectedInfo{startRange, info.isMain, true, false, "", str.color}; vec.push_back(startInfo); multiLineRecordMap.emplace(std::pair{startInfo, multiLineHangingPtrVec.size()}); CJC_ASSERT(str.range.end.column > 1 && "end of range must be bigger than 1"); auto s = str.range.end.column > 1 ? str.range.end - Position{0, 0, 1} : Position{str.range.end.fileID, str.range.end.line, 1}; auto e = str.range.end.column > 1 ? str.range.end : Position{str.range.end.fileID, str.range.end.line, 1 + 1}; info.range = MakeRange(s, e); info.isMultiLine = true; info.isEnd = true; info.hint = str.str; multiLineRecordMap.emplace(std::pair{info, multiLineHangingPtrVec.size()}); multiLineHangingPtrVec.emplace_back(-1, -1); } vec.push_back(info); } void DiagnosticEmitterImpl::SortAndCheck(std::vector& errorInfo) const { std::sort(errorInfo.begin(), errorInfo.end(), [this](auto& a, auto& b) -> bool { if (enableRangeCheckICE) { CJC_ASSERT(a.range.begin != b.range.begin); } return a.range.begin < b.range.begin; }); } std::string DiagnosticEmitterImpl::GetSourceCode(std::vector& errorInfo) const { CJC_ASSERT(!errorInfo.empty() && "error info cannot be empty"); if (errorInfo.empty()) { return std::string{}; } auto start = errorInfo.front().range.begin; auto end = errorInfo.back().range.end; if (start.fileID == end.fileID) { return sm.GetContentBetween( start.fileID, Position(start.line, 1), Position(end.line, std::numeric_limits::max())); } // Get the code after macro expansion. auto fileID = start.fileID; std::string source; for (auto& info : errorInfo) { auto range = info.range; if (range.begin.fileID < fileID) { if (!source.empty()) { source += "\n"; } continue; } source += sm.GetContentBetween(range.begin.fileID, Position(range.begin.line, 1), Position(range.end.line, std::numeric_limits::max())); } return source; } size_t DiagnosticEmitterImpl::GetDisplayedWidthFromSource(const std::string& source, const Range& range) const { const size_t defaultWidth = 1; CJC_ASSERT(!source.empty() && "source cannot be empty"); if (source.empty()) { return defaultWidth; // 1 is default; } auto start = range.begin.column; auto end = range.end.column; auto len = source.size(); start = start > static_cast(len) ? static_cast(len) : start; end = end > static_cast(len + 1) ? static_cast(len + 1) : end; CJC_ASSERT(start >= 1 && "wrong start position"); CJC_ASSERT(end >= start); std::string substr = source.substr(static_cast(start - 1), static_cast(end - start)); auto disWidth = static_cast(static_cast(Unicode::DisplayWidth(substr))); // If display width equals to 0, it only contains control character. disWidth = disWidth > 0 ? disWidth : defaultWidth; return disWidth; } void DiagnosticEmitterImpl::InsertSymbolInFirstLine(CombinedLine& combinedLine, size_t loc, const CollectedInfo& info, const std::string& sourceLine) const { char sym = info.isMain ? MAIN_HINT_SYMBOL : OTHER_HINT_SYMBOL; auto len = GetDisplayedWidthFromSource(sourceLine, info.range); auto repStr = CharacterOfNum(len, sym); combinedLine.meta.replace(loc, len, repStr); combinedLine.colors.emplace_back(loc, loc + len, info.color); } void DiagnosticEmitterImpl::InsertSymbolNotFirstLine( CombinedLine& combinedLine, size_t loc, const CollectedInfo& info) const { size_t len = 1; auto repStr = CharacterOfNum(len, BITOR); combinedLine.meta.replace(loc, len, repStr); combinedLine.colors.emplace_back(loc, loc + len, info.color); } void DiagnosticEmitterImpl::InsertSymbolToUpperLine(SourceCombinedVec& insertedStr, size_t loc, const CollectedInfo& info, const std::string& sourceLine) const { for (size_t i = 0; i < insertedStr.size(); i++) { CJC_ASSERT(insertedStr[i].meta.size() >= loc); if (i == 0) { InsertSymbolInFirstLine(insertedStr[0], loc, info, sourceLine); } else { InsertSymbolNotFirstLine(insertedStr[i], loc, info); } } } static CollectedInfoMap StoreInfoToMap(std::vector& errorInfos) { CollectedInfoMap lineToInfoMap; std::for_each(errorInfos.begin(), errorInfos.end(), [&](auto& info) { lineToInfoMap[static_cast(info.range.begin.line)].push_back(info); }); return lineToInfoMap; } static size_t GetMaxLineColumn(std::vector& errorInfos) { size_t maxLineColumn = 0; std::for_each(errorInfos.begin(), errorInfos.end(), [&](auto& info) { CJC_ASSERT(info.range.end.line >= 0); if (static_cast(info.range.end.line) > maxLineColumn) { maxLineColumn = static_cast(info.range.end.line); } }); return std::to_string(maxLineColumn).length(); } void HandleSpecialCharacters(std::string& str) { // For more special characters. str = std::regex_replace(str, std::regex("\t"), g_spaceOfNum(HORIZONTAL_TAB_LEN)); } static const size_t COMPRESS_LEN = 2; void DiagnosticEmitterImpl::CompressLineCode(CollectedInfoMap& infoMap, SourceCombinedVec& bindLineCodes) const { CJC_ASSERT(!infoMap.empty() && "info map is empty"); if (infoMap.empty()) { return; } auto base = infoMap.cbegin()->first; unsigned int second = 0; unsigned lineNum = (infoMap.rbegin()->first - infoMap.begin()->first) + 1; if (lineNum > bindLineCodes.size()) { return; } std::for_each(infoMap.rbegin(), infoMap.rend(), [&](auto& m) { if (second == 0) { second = m.first; return; } if (second - m.first > COMPRESS_LEN) { auto targetIter = bindLineCodes.begin() + m.first - base + COMPRESS_LEN; targetIter->line = 0; targetIter->meta = "..."; bindLineCodes.erase(targetIter + 1, bindLineCodes.begin() + second - base); } second = m.first; }); } CombinedLine DiagnosticEmitterImpl::CombineErrorPrintSingleLineHelper( const std::string& sourceLine, const CollectedInfo& info, bool isFirstLine) const { CJC_ASSERT(!info.IsDefault()); // Every source line have a newline character in the end, need add it to calculate the length. auto str = sourceLine + GetNewLine(); auto space = GetSpaceBeforeTarget(str, info.range.begin.column); std::string line; auto charSize = GetDisplayedWidthFromSource(str, info.range); line += space; auto symbol = info.isMain ? MAIN_HINT_SYMBOL : OTHER_HINT_SYMBOL; auto begin = line.size(); if (isFirstLine) { line += CharacterOfNum(charSize, symbol) + g_spaceOfNum(1); } line += info.hint; return {line, 0, false, {{begin, line.size(), info.color}}}; } void DiagnosticEmitterImpl::CombineErrorPrintSingleLine(SourceCombinedVec& insertedStr, const CollectedInfo& info, const std::string& sourceLine) { auto isFirstLine = insertedStr.empty(); if (isFirstLine) { insertedStr.push_back(CombineErrorPrintSingleLineHelper(sourceLine, info, isFirstLine)); } else if (info.hint.empty()) { // If hint message is empty, only need mark the source code. // Every source line have a newline character in the end, need add it to calculate the length. auto str = sourceLine + GetNewLine(); auto spaceSize = GetSpaceBeforeTarget(str, info.range.begin.column).size(); CJC_ASSERT(!insertedStr.empty() && "source combined vec cannot be empty"); if (insertedStr.empty()) { return; } InsertSymbolInFirstLine(insertedStr[0], spaceSize, info, str); } else { auto tl = CombineErrorPrintSingleLineHelper(sourceLine, info, isFirstLine); auto spaceSize = tl.meta.find_first_not_of(SPACE); auto blackLine = g_spaceOfNum(spaceSize + 1); auto str = sourceLine + GetNewLine(); insertedStr.push_back({blackLine, 0, {}}); InsertSymbolToUpperLine(insertedStr, spaceSize, info, str); insertedStr.push_back(tl); } } void DiagnosticEmitterImpl::AnalyseMultiLineHanging(const CollectedInfo& info, size_t combinedVecSize) { CJC_ASSERT(info.isMultiLine); auto loc = multiLineRecordMap[info]; CJC_ASSERT(loc < multiLineHangingPtrVec.size() && "multiLineHangingPtrVec is less then loc"); if (loc >= multiLineHangingPtrVec.size()) { return; } if (!info.isEnd) { auto tup = std::tuple{combinedVecSize, 0, info.color}; if (multiLineHangingVec.empty()) { multiLineHangingVec.emplace_back(std::vector>{tup}); multiLineHangingPtrVec[loc].first = 0; multiLineHangingPtrVec[loc].second = 0; } else { // Hanging column. auto c = std::find_if(multiLineHangingVec.rbegin(), multiLineHangingVec.rend(), [](auto& vec) { return std::get<1>(vec.back()) == 0; }); if (c == multiLineHangingVec.rbegin()) { multiLineHangingVec.emplace_back(std::vector>{tup}); multiLineHangingPtrVec[loc].first = 0; multiLineHangingPtrVec[loc].second = multiLineHangingVec.size() - 1; } else { auto offset = c == multiLineHangingVec.crend() ? 0 : 1; auto index = c.base() - multiLineHangingVec.begin() + offset; CJC_ASSERT(index >= 0); size_t uIndex = static_cast(index); CJC_ASSERT(uIndex < multiLineHangingVec.size() && "index overflow in vector"); multiLineHangingVec[uIndex].push_back(tup); multiLineHangingPtrVec[loc].first = multiLineHangingVec[uIndex].size() - 1; multiLineHangingPtrVec[loc].second = uIndex; } } } else { auto [line, column] = multiLineHangingPtrVec[loc]; CJC_ASSERT(column < multiLineHangingVec.size() && "column is bigger than size of multiLineHangingVec"); if (column >= multiLineHangingVec.size()) { return; } CJC_ASSERT(line < multiLineHangingVec[column].size() && "line is bigger than size of multiLineHangingVec"); if (line >= multiLineHangingVec[column].size()) { return; } auto [begin, end, color] = multiLineHangingVec[column][line]; multiLineHangingVec[column][line] = std::make_tuple(begin, combinedVecSize + 1, color); } } CombinedLine DiagnosticEmitterImpl::CombineErrorPrintMultiLineHelper( const std::string& sourceLine, const CollectedInfo& info, bool isFirstLine, size_t combinedVecSize) { if (info.IsDefault()) { rangeCheckError = true; if (enableRangeCheckICE) { InternalError("default range. there is something wrong before"); } return {info.hint, 0, false, {{}}}; } AnalyseMultiLineHanging(info, combinedVecSize); // Every source line have a newline character in the end, need add it to calculate the length. auto str = sourceLine + GetNewLine(); auto space = GetSpaceBeforeTarget(str, info.range.begin.column); std::string line; line += CharacterOfNum(space.size(), MULTI_LINE_SYMBOL); auto symbol = info.isMain ? MAIN_HINT_SYMBOL : OTHER_HINT_SYMBOL; if (isFirstLine) { line += symbol; if (info.isEnd && !info.hint.empty()) { line += g_spaceOfNum(1) + info.hint; } } else { line += MULTI_LINE_KEY_SYMBOL; } return {line, 0, false, {{0, line.size(), info.color}}}; } void DiagnosticEmitterImpl::CombineErrorPrintMultiLine(SourceCombinedVec& insertedStr, const CollectedInfo& info, const std::string& sourceLine, size_t combinedVecSize) { auto isFirstLine = insertedStr.empty(); if (isFirstLine) { insertedStr.push_back(CombineErrorPrintMultiLineHelper(sourceLine, info, isFirstLine, combinedVecSize)); } else { auto tl = CombineErrorPrintMultiLineHelper(sourceLine, info, isFirstLine, combinedVecSize + insertedStr.size()); auto len = tl.meta.size(); auto str = sourceLine + GetNewLine(); InsertSymbolToUpperLine(insertedStr, len - 1, info, str); insertedStr.emplace_back(tl); if (!info.hint.empty()) { insertedStr.push_back({g_spaceOfNum(len - 1), 0, false, {{}}}); } } } bool DiagnosticEmitterImpl::CombineErrorPrint(CollectedInfoMap& infoMap, SourceCombinedVec& combinedVec) { if (combinedVec.begin()->hasSourceFile) { CompressLineCode(infoMap, combinedVec); } for (auto& infos : std::as_const(infoMap)) { size_t i; for (i = 0; i < combinedVec.size(); i++) { if (combinedVec[i].line == infos.first) { break; } } CJC_ASSERT(i < combinedVec.size()); if (i >= combinedVec.size()) { return false; } auto sourceLine = combinedVec[i].meta; SourceCombinedVec insertedStr; std::for_each(infos.second.rbegin(), infos.second.rend(), [&, this](auto& info) { if (info.isMultiLine) { // CombinedVecSize start from 1. CombineErrorPrintMultiLine(insertedStr, info, sourceLine, i + 1); } else { CombineErrorPrintSingleLine(insertedStr, info, sourceLine); } }); combinedVec.insert(combinedVec.begin() + static_cast(i) + 1, insertedStr.begin(), insertedStr.end()); } return true; } /* ascii control code +-------+-------+----------------------------+------------------+---------------------+-------------------------------+ | Dec | Hex | Unicode Control Pictures | Caret notation | C escape sequence | Name | +=======+=======+============================+==================+=====================+===============================+ | 0 | 00 | NUL | ^@ | | Null | | 1 | 01 | SOH | ^A | | Start of Heading | | 2 | 02 | STX | ^B | | Start of Text | | 3 | 03 | ETX | ^C | | End of Text | | 4 | 04 | EOT | ^D | | End of Transmission | | 5 | 05 | ENQ | ^E | | Enquiry | | 6 | 06 | ACK | ^F | | Acknowledgement | | 7 | 07 | BEL | ^G | \a | Bell | | 8 | 08 | BS | ^H | \b | Backspace | | 9 | 09 | HT | ^I | \t | Horizontal Tab | | 10 | 0A | LF | ^J | \n | Line Feed | | 11 | 0B | VT | ^K | \v | Vertical Tab | | 12 | 0C | FF | ^L | \f | Form Feed | | 13 | 0D | CR | ^M | \r | Carriage Return | | 14 | 0E | SO | ^N | | Shift Out | | 15 | 0F | SI | ^O | | Shift In | | 16 | 10 | DLE | ^P | | Data Link Escape | | 17 | 11 | DC1 | ^Q | | Device Control 1 (often XON) | | 18 | 12 | DC2 | ^R | | Device Control 2 | | 19 | 13 | DC3 | ^S | | Device Control 3 (often XOFF) | | 20 | 14 | DC4 | ^T | | Device Control 4 | | 21 | 15 | NAK | ^U | | Negative Acknowledgement | | 22 | 16 | SYN | ^V | | Synchronous Idle | | 24 | 18 | CAN | ^X | | Cancel | | 25 | 19 | EM | ^Y | | End of Medium | | 26 | 1A | SUB | ^Z | | Substitute | | 27 | 1B | ESC | ^[ | \e | Escape | | 28 | 1C | FS | ^\ | | File Separator | | 29 | 1D | GS | ^] | | Group Separator | | 30 | 1E | RS | ^^ | | Record Separator | | 31 | 1F | US | ^_ | | Unit Separator | | 127 | 7F | DEL | ^? | | Delete | +-------+-------+----------------------------+------------------+---------------------+-------------------------------+ */ void DiagnosticEmitterImpl::HandleUnprintableChar(SourceCombinedVec& combinedVec) const { const int lengthOfDelay = 7; for (auto& combinedLine : combinedVec) { std::string& sourceCode = combinedLine.meta; std::string result; auto& colors = combinedLine.colors; char forwardChar; char backwardChar; size_t index; for (size_t i = 0; i < sourceCode.size(); i++) { // forward forwardChar = sourceCode[i]; #if defined __GNUC__ && not defined __clang__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wtype-limits" #endif if ((0 <= forwardChar && forwardChar <= 0x8) || (0xb <= forwardChar && forwardChar <= 0x1f) || forwardChar == 0x7f) { result += "\\u{" + ToHexString(forwardChar, NORMAL_CODEPOINT_LEN) + "}"; } else { result += forwardChar; } // backward index = sourceCode.size() - (i + 1); backwardChar = sourceCode[index]; if ((0 <= backwardChar && backwardChar <= 0x8) || (0xb <= backwardChar && backwardChar <= 0x1f) || backwardChar == 0x7f) { #if defined __GNUC__ && not defined __clang__ #pragma GCC diagnostic pop #endif std::for_each(colors.begin(), colors.end(), [=](auto& tup) { if (std::get<0>(tup) > index) { std::get<0>(tup) += lengthOfDelay; } if (std::get<1>(tup) > index) { std::get<1>(tup) += lengthOfDelay; } }); colors.emplace_back(index, index + lengthOfDelay + 1, DiagColor::REVERSE); } } combinedLine.meta = result; } } void DiagnosticEmitterImpl::EmitErrorMessage(DiagColor color, const std::string& err, const std::string& mes) { color = noColor ? NO_COLOR : color; CJC_ASSERT(ColorPrintMap.find(color) != ColorPrintMap.end() && "no print color found"); if (ColorPrintMap.find(color) == ColorPrintMap.end()) { return; } out << ColorPrintMap.at(color); out << err; if (color != DiagColor::NO_COLOR) { out << ANSI_COLOR_RESET; } out << ":" << g_spaceOfNum(1) << mes << std::endl; } void DiagnosticEmitterImpl::EmitErrorLocation(const Position& pos) { CJC_ASSERT(!pos.IsZero()); if (!sm.IsSourceFileExist(pos.fileID)) { return; } auto prefix = CharacterOfNum(maxLineNum, SPACE); auto color = noColor ? NO_COLOR : OTHER_HINT_COLOR; prefix += GetColoredString(color, GetLineSymbol()); prefix += g_spaceOfNum(1); auto source = sm.GetSource(pos.fileID); std::string path; if (source.packageName.has_value()) { path = "(package " + source.packageName.value() + ")" + FileUtil::GetFileName(source.path); } else { path = source.path; } out << prefix << path << ":" << pos.line << ":" << pos.column << ":" << std::endl; } void DiagnosticEmitterImpl::ConvertHangingContentsHelper( HangingStr& hanging, size_t i, size_t begin, size_t end, const DiagColor& color) const { auto k = i + 1; while (k < hanging.size()) { CJC_ASSERT(begin < hanging[k].size() && "begin is bigger than hanging size"); if (begin >= hanging[k].size()) { break; } if (hanging[k][begin] == g_spaceOfNum(1)) { hanging[k][begin] = GetColoredString(color, std::string{MULTI_LINE_SYMBOL}); } CJC_ASSERT(end - 1 < hanging[k].size() && "end is bigger than hanging size"); if (end - 1 >= hanging[k].size()) { break; } if (hanging[k][end - 1] == g_spaceOfNum(1)) { hanging[k][end - 1] = GetColoredString(color, std::string{MULTI_LINE_SYMBOL}); } k++; } k = begin + 1; while (k < end) { CJC_ASSERT(k < hanging[i].size() && "begin is bigger than hanging size"); if (k >= hanging[i].size()) { break; } if (hanging[i][k] == g_spaceOfNum(1)) { hanging[i][k] = GetColoredString(color, std::string{MULTI_LINE_KEY_SYMBOL}); } k++; } } HangingStr DiagnosticEmitterImpl::ConvertHangingContents(size_t line) { if (multiLineHangingVec.empty()) { return HangingStr{}; } HangingStr hanging(multiLineHangingVec.size() + 1, std::vector(line, g_spaceOfNum(1))); for (size_t i = 0; i < multiLineHangingVec.size(); i++) { for (size_t j = 0; j < multiLineHangingVec[i].size(); j++) { auto [begin, end, color] = multiLineHangingVec[i][j]; color = noColor ? DiagColor::NO_COLOR : color; CJC_ASSERT(begin != end - 1); ConvertHangingContentsHelper(hanging, i, begin, end, color); } } multiLineHangingVec.clear(); multiLineHangingPtrVec.clear(); multiLineRecordMap.clear(); return hanging; } void DiagnosticEmitterImpl::EmitSourceCode(SourceCombinedVec& combinedVec) { auto hanging = ConvertHangingContents(combinedVec.size()); // Add a empty line. auto line = g_spaceOfNum(maxLineNum + 1); DiagColor color = noColor ? NO_COLOR : OTHER_HINT_COLOR; line += GetColoredString(color, std::string{BITOR} + g_spaceOfNum(1), false); out << line << std::endl; // Source code. for (size_t i = 0; i < combinedVec.size(); i++) { auto [sourceLine, lineHere, hasSourceFile, _] = combinedVec[i]; HandleSpecialCharacters(sourceLine); std::string str; if (lineHere != 0 && hasSourceFile) { color = noColor ? NO_COLOR : OTHER_HINT_COLOR; str += GetColoredString(color, std::to_string(lineHere)); str += CharacterOfNum(maxLineNum - std::to_string(lineHere).length() + 1, SPACE); } else { str += CharacterOfNum(maxLineNum + 1, SPACE); } color = noColor ? NO_COLOR : OTHER_HINT_COLOR; str += GetColoredString(color, std::string{BITOR} + g_spaceOfNum(1), false); if (!hanging.empty()) { for (auto& vec : hanging) { str += vec[i]; } } str += sourceLine; out << str << std::endl; } // Add a empty line. out << line << std::endl; } void DiagnosticEmitterImpl::EmitSingleNoteWithSource(SubDiagnostic& note) { EmitErrorMessage(note.mainHint.color, "note", note.subDiagMessage); std::vector errorInfo; CollectInformation(errorInfo, note.mainHint, true); std::for_each(note.otherHints.begin(), note.otherHints.end(), [&, this](auto& str) { CollectInformation(errorInfo, str, false); }); SortAndCheck(errorInfo); maxLineNum = GetMaxLineColumn(errorInfo); if (note.mainHint.range.begin != DEFAULT_POSITION) { EmitErrorLocation(note.mainHint.range.begin); } ConstructAndEmitSourceCode(errorInfo); std::vector hs{note.help}; if (!GetSourceCode(errorInfo).empty() && !note.help.IsDefault()) { EmitHelp(hs); } } SubstitutionMap DiagnosticEmitterImpl::HelpSubstitutionToMap(DiagHelp& help) const { // Consider multiple-line? std::sort(help.substitutions.begin(), help.substitutions.end(), [](auto& a, auto& b) { return a.range.begin < b.range.begin; }); SubstitutionMap subMap; std::for_each(help.substitutions.begin(), help.substitutions.end(), [&](auto& info) { subMap[static_cast(info.range.begin.line)].push_back(info); }); return subMap; } void DiagnosticEmitterImpl::HelpSubstituteConvertHelper(SubstitutionMap& subMap, std::string& rawStr, unsigned int line, std::vector& infos) const { for (auto& sub : subMap[line]) { CollectedInfo info; auto range = sub.range; CJC_ASSERT(range.end.line == range.begin.line); auto size = range.end.column - range.begin.column; CJC_ASSERT(size >= 0); CJC_ASSERT(range.begin.column > 0); CJC_ASSERT(static_cast(range.begin.column - 1) < rawStr.size() && "column is bigger than size of rawStr"); rawStr.replace(static_cast(range.begin.column - 1), static_cast(size), sub.str); info.range = MakeRange(range.begin, range.begin + Position{0, 0, static_cast(sub.str.size())}); info.color = HELP_COLOR; infos.push_back(info); } } std::vector DiagnosticEmitterImpl::HelpSubstituteConvert( DiagHelp& help, SourceCombinedVec& combinedVec) const { auto subMap = HelpSubstitutionToMap(help); std::vector infos; for (auto& [rawStr, line, hasSourcefile, _] : combinedVec) { if (subMap.count(line) > 0) { // Synchronize location when multi-substitution on single line. HelpSubstituteConvertHelper(subMap, rawStr, line, infos); } } return infos; } SourceCombinedVec DiagnosticEmitterImpl::GetHelpSubstituteSource(DiagHelp& help) { CJC_ASSERT(!help.substitutions.empty()); // Consider multi-line source. auto start = help.substitutions[0].range.begin; auto source = sm.GetContentBetween( start.fileID, Position(start.line, 1), Position(start.line, std::numeric_limits::max())); if (source.empty()) { return {}; } // If last character is newline, delete it. auto windowsTerminatorSize = std::string("\r\n").size(); if (source.size() >= windowsTerminatorSize && source[source.size() - windowsTerminatorSize] == '\r' && source.back() == '\n') { source = source.substr(0, source.size() - windowsTerminatorSize); } else if (source.back() == '\n' || source.back() == '\r') { source = source.substr(0, source.size() - 1); } auto hasSourceFile = sm.IsSourceFileExist(start.fileID); return SourceCombinedVec{{source, static_cast(start.line), hasSourceFile, {{}}}}; } void DiagnosticEmitterImpl::EmitSingleHelpWithSource(DiagHelp& help) { DiagColor color = noColor ? NO_COLOR : HELP_COLOR; std::string message = GetColoredString(color, "help"); message += CharacterOfNum(1, COLON); message += g_spaceOfNum(1); message += help.helpMes; out << message + std::string{COLON} << std::endl; auto source = GetHelpSubstituteSource(help); if (source.empty()) { return; } // For macro replaced tokens, all positions are wrong. if (static_cast(source.back().meta.size()) < help.substitutions.back().range.begin.column) { return; } auto collectedInfos = HelpSubstituteConvert(help, source); auto infoMap = StoreInfoToMap(collectedInfos); if (!CombineErrorPrint(infoMap, source)) { return; } HandleUnprintableChar(source); if (!noColor) { ColorizeCombinedVec(source); } EmitSourceCode(source); } void DiagnosticEmitterImpl::EmitSingleMessageWithoutSource(const std::string& str, std::string host) { std::string combined = g_spaceOfNum(maxLineNum + 1); auto color = noColor ? NO_COLOR : OTHER_HINT_COLOR; combined += GetColoredString(color, std::string{WITHOUT_SOURCE_SYMBOL}); combined += g_spaceOfNum(1); color = noColor ? NO_COLOR : DiagColor::RESET; combined += GetColoredString(color, std::move(host), false); combined += CharacterOfNum(1, COLON); combined += g_spaceOfNum(1); combined += str; out << combined << std::endl; } void DiagnosticEmitterImpl::EmitNote() { if (diag.subDiags.empty()) { return; } // Resort all notes, the note not printed source code is being front of those printed source. std::partition(diag.subDiags.begin(), diag.subDiags.end(), [](auto& subDiag) { return !subDiag.IsShowSource(); }); std::for_each(diag.subDiags.begin(), diag.subDiags.end(), [this](auto& subDiag) { if (diag.curMacroCall && subDiag.subDiagMessage == MACROCALL_CODE) { auto pInvocation = diag.curMacroCall->GetInvocation(); if (!pInvocation || pInvocation->hasShownCode) { return; } pInvocation->hasShownCode = true; } subDiag.IsShowSource() ? EmitSingleNoteWithSource(subDiag) : EmitSingleMessageWithoutSource(subDiag.subDiagMessage, "note"); }); } void DiagnosticEmitterImpl::EmitHelp(std::vector& helps) { if (helps.empty()) { return; } // Resort all helps, the note not printed source code is being front of those printed source. std::partition(helps.begin(), helps.end(), [](auto& help) { return !help.IsShowSource(); }); std::for_each(helps.begin(), helps.end(), [this](auto& help) { help.IsShowSource() ? EmitSingleHelpWithSource(help) : EmitSingleMessageWithoutSource(help.helpMes, "help"); }); } void DiagnosticEmitterImpl::ColorizeCombinedVec(SourceCombinedVec& combinedVec) const { // Get second element of tuple. And iterates negative two element when colorizes. static const size_t two = 2; for (auto& combinedLine : combinedVec) { // Sort all. Make sure bigger ranges are before included one. std::sort(combinedLine.colors.begin(), combinedLine.colors.end(), [](auto& c1, auto& c2) { if (std::get<0>(c1) == std::get<0>(c2)) { CJC_ASSERT(std::get<1>(c1) != std::get<1>(c2)); return std::get<1>(c1) > std::get<1>(c2); } return std::get<0>(c1) < std::get<0>(c2); }); // Make colorized list. std::vector vec{combinedLine.meta.size(), DiagColor::RESET}; std::for_each(combinedLine.colors.begin(), combinedLine.colors.end(), [&](auto& c) { for (auto i = std::get<0>(c); i < std::get<1>(c); i++) { vec[i] = std::get(c); } }); // Colorize. if (vec.size() >= two) { for (size_t i = vec.size() - 1; i > 0; i--) { if (vec[i - 1] != vec[i]) { combinedLine.meta.insert(i, ColorPrintMap.at(vec[i])); } if (i == 1 && vec[i - 1] != DiagColor::RESET) { combinedLine.meta.insert(0, ColorPrintMap.at(vec[0])); } } } else if (vec.size() == 1 && vec[0] != DiagColor::RESET) { combinedLine.meta.insert(0, ColorPrintMap.at(vec[0])); } combinedLine.meta.insert(combinedLine.meta.length(), ColorPrintMap.at(DiagColor::RESET)); } } void DiagnosticEmitterImpl::ConstructAndEmitSourceCode(std::vector& errorInfo) { CJC_ASSERT(!errorInfo.empty()); auto hasSourceFile = sm.IsSourceFileExist(errorInfo.begin()->range.begin.fileID); auto infoMap = StoreInfoToMap(errorInfo); auto lineCodes = Utils::SplitLines(GetSourceCode(errorInfo)); // The libast using parser don't have the source line. if (lineCodes.empty()) { return; } // The last column is newline, delete it. if (lineCodes.back().empty()) { lineCodes.pop_back(); } CJC_ASSERT(!infoMap.empty() && "info map is empty"); if (infoMap.empty()) { return; } auto base = infoMap.begin()->first; SourceCombinedVec combinedVec; std::for_each(lineCodes.begin(), lineCodes.end(), [&](auto& line) { combinedVec.push_back({line, base++, hasSourceFile, {{}}}); }); if (!CombineErrorPrint(infoMap, combinedVec)) { return; } HandleUnprintableChar(combinedVec); if (!noColor) { ColorizeCombinedVec(combinedVec); } EmitSourceCode(combinedVec); } namespace { bool IsTextOnlyWarning(const WarnGroup& warnGroup) { return (warnGroup == WarnGroup::DRIVER_ARG || warnGroup == WarnGroup::UNSUPPORT_COMPILE_SOURCE); } } bool DiagnosticEmitterImpl::Emit() { const std::map seveToStr = { {DiagSeverity::DS_ERROR, "error"}, {DiagSeverity::DS_WARNING, "warning"}, {DiagSeverity::DS_NOTE, "note"}, }; if (seveToStr.find(diag.diagSeverity) != seveToStr.end()) { EmitErrorMessage(diag.mainHint.color, std::string(seveToStr.at(diag.diagSeverity)), diag.errorMessage); } else { CJC_ABORT(); } // Since some warnings are text-only, which means they do not have invalid position, so emit notes here. if (IsTextOnlyWarning(diag.warnGroup)) { EmitNote(); } if (diag.mainHint.range.begin == DEFAULT_POSITION) { return !rangeCheckError; } std::vector errorInfo; CollectInformation(errorInfo, diag.mainHint, true); std::for_each(diag.otherHints.begin(), diag.otherHints.end(), [&, this](auto& str) { CollectInformation(errorInfo, str, false); }); CJC_ASSERT(!errorInfo.empty()); SortAndCheck(errorInfo); maxLineNum = GetMaxLineColumn(errorInfo); EmitErrorLocation(diag.mainHint.range.begin); ConstructAndEmitSourceCode(errorInfo); EmitNote(); EmitHelp(diag.helps); out << std::endl; // Print empty line after a error. return !rangeCheckError; } DiagnosticEmitter::DiagnosticEmitter( Diagnostic& d, bool nc, bool enableRangeCheckICE, std::basic_ostream& o, SourceManager& sourceManager) : impl{new DiagnosticEmitterImpl{d, nc, enableRangeCheckICE, o, sourceManager}} { } DiagnosticEmitter::~DiagnosticEmitter() { delete impl; } bool DiagnosticEmitter::Emit() const { return impl->Emit(); } } cangjie_compiler-1.0.7/src/Basic/DiagnosticEmitterImpl.h000066400000000000000000000140321510705540100232300ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares class DiagnosticEmitterImpl, which is an implementation class of DiagnosticEmitter. */ #ifndef CANGJIE_BASIC_DIAGNOSTICEMITTERIMPL_H #define CANGJIE_BASIC_DIAGNOSTICEMITTERIMPL_H #include "cangjie/Basic/DiagnosticEmitter.h" #include "cangjie/Basic/DiagnosticEngine.h" namespace Cangjie { using SubstitutionMap = std::map>; using HangingStr = std::vector>; void HandleSpecialCharacters(std::string& str); // This struct is to record information of one printed line. struct CombinedLine { std::string meta; // Line materials, without any color. unsigned int line; // Source line number. It is 0 if meta is added. bool hasSourceFile{false}; std::vector> colors; // Range and color style to colorize meta. }; using SourceCombinedVec = std::vector; /** * This is class collecting information to analyse added line and hanging line. */ struct CollectedInfo { Range range = MakeRange(DEFAULT_POSITION, DEFAULT_POSITION); // If this information is main hint. bool isMain{false}; // If this information is navigating multiple line. if is multiple line, the range will be divided to two range, The // begin is one and the end is another one to keep all range in CollectedInfo is in same line. bool isMultiLine{false}; // If this information is end of multiple line. bool isEnd{false}; std::string hint; DiagColor color{DiagColor::RESET}; bool IsDefault() const { return range.begin == DEFAULT_POSITION; } }; using CollectedInfoMap = std::map>; struct MultiLineHashFunc { size_t operator()(const CollectedInfo& info) const { const auto rangeHashLen = 2; return (info.range.Hash() << rangeHashLen) ^ static_cast(info.isMain << 1) ^ (info.isMultiLine); } }; struct MultiLineEqualFunc { size_t operator()(const CollectedInfo& l, const CollectedInfo& r) const { return l.range == r.range && l.isMain == r.isMain && l.isMultiLine == r.isMultiLine && l.isEnd == r.isEnd; } }; class DiagnosticEmitterImpl final { public: DiagnosticEmitterImpl( Diagnostic& d, bool nc, bool enableRangeCheckICE, std::basic_ostream& o, SourceManager& sourceManager) : diag(d), noColor(nc), enableRangeCheckICE(enableRangeCheckICE), out(o), sm(sourceManager) { } bool Emit(); private: Diagnostic& diag; bool noColor{false}; bool enableRangeCheckICE{true}; bool rangeCheckError{false}; mutable size_t maxLineNum{0}; std::basic_ostream& out{std::cerr}; SourceManager& sm; std::unordered_map multiLineRecordMap; std::vector> multiLineHangingPtrVec{}; std::vector>> multiLineHangingVec{}; void CollectInformation(std::vector& vec, IntegratedString& str, bool isMain); void SortAndCheck(std::vector& errorInfo) const; size_t GetDisplayedWidthFromSource(const std::string& source, const Range& range) const; void InsertSymbolInFirstLine( CombinedLine& combinedLine, size_t loc, const CollectedInfo& info, const std::string& sourceLine) const; void InsertSymbolNotFirstLine(CombinedLine& combinedLine, size_t loc, const CollectedInfo& info) const; void InsertSymbolToUpperLine( SourceCombinedVec& insertedStr, size_t loc, const CollectedInfo& info, const std::string& sourceLine) const; CombinedLine CombineErrorPrintSingleLineHelper( const std::string& sourceLine, const CollectedInfo& info, bool isFirstLine = false) const; void CombineErrorPrintSingleLine( SourceCombinedVec& insertedStr, const CollectedInfo& info, const std::string& sourceLine); HangingStr ConvertHangingContents(size_t line); void ConvertHangingContentsHelper( HangingStr& hanging, size_t i, size_t begin, size_t end, const DiagColor& color) const; void AnalyseMultiLineHanging(const CollectedInfo& info, size_t combinedVecSize); CombinedLine CombineErrorPrintMultiLineHelper( const std::string& sourceLine, const CollectedInfo& info, bool isFirstLine, size_t combinedVecSize); void CombineErrorPrintMultiLine(SourceCombinedVec& insertedStr, const CollectedInfo& info, const std::string& sourceLine, size_t combinedVecSize); void ColorizeCombinedVec(SourceCombinedVec& combinedVec) const; std::string GetSourceCode(std::vector& errorInfo) const; bool CombineErrorPrint(CollectedInfoMap& infoMap, SourceCombinedVec& combinedVec); void HandleUnprintableChar(SourceCombinedVec& combinedVec) const; void CompressLineCode(CollectedInfoMap& infoMap, SourceCombinedVec& bindLineCodes) const; void EmitErrorMessage(DiagColor color, const std::string& err, const std::string& mes); void EmitErrorLocation(const Position& pos); void ConstructAndEmitSourceCode(std::vector& errorInfo); void EmitSourceCode(SourceCombinedVec& combinedVec); void EmitNote(); void EmitSingleNoteWithSource(SubDiagnostic& note); void EmitSingleMessageWithoutSource(const std::string& str, std::string host); SourceCombinedVec GetHelpSubstituteSource(DiagHelp& help); SubstitutionMap HelpSubstitutionToMap(DiagHelp& help) const; void HelpSubstituteConvertHelper( SubstitutionMap& subMap, std::string& rawStr, unsigned int line, std::vector& infos) const; std::vector HelpSubstituteConvert(DiagHelp& help, SourceCombinedVec& combinedVec) const; void EmitSingleHelpWithSource(DiagHelp& help); void EmitHelp(std::vector& helps); }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Basic/DiagnosticEngine.cpp000066400000000000000000000760661510705540100225540ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the DiagnosticEngine related classes. */ #include "DiagnosticEngineImpl.h" #include #include #include #include #include #include "cangjie/AST/Utils.h" #include "cangjie/Basic/DiagnosticEmitter.h" #include "cangjie/Basic/DiagnosticJsonFormatter.h" #include "cangjie/Basic/Display.h" #include "cangjie/Basic/Print.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/Unicode.h" #ifdef _WIN32 #include #endif namespace Cangjie { Range MakeRange(const Position& begin, Position end) { // The fileID of position may be different, may come from the macro definition or from the macrocall. if (begin.fileID != end.fileID) { end = begin + 1; } return Range(begin, end); } Range MakeRange(const Position& identifierPos, const std::string& identifier) { for (auto ch : identifier) { CJC_ASSERT(Unicode::IsASCII(static_cast(ch))); } return MakeRange(identifierPos, identifierPos + identifier.size()); } Range MakeRange(const Identifier& id) { return MakeRange(id.Begin(), id.End()); } DiagCategory Diagnostic::GetDiagnoseCategory(DiagKind diagKind) { DiagCategory dc = DiagCategory::OTHER; #define GET_CATE(kind, KIND) \ do { \ if (diagKind > DiagKind::kind##_diag_begin && diagKind <= DiagKind::kind##_diag_end) { \ dc = DiagCategory::KIND; \ } \ } while (0) GET_CATE(macro_expand, MACRO_EXPAND); GET_CATE(sema, SEMA); #undef GET_CATE return dc; } struct DiagRange { DiagKindRefactor start; DiagKindRefactor end; DiagCategory category; }; DiagCategory Diagnostic::GetDiagnoseCategory(DiagKindRefactor diagKind) { static const std::array RANGES = {{ {DiagKindRefactor::lex_diag_begin, DiagKindRefactor::lex_diag_end, DiagCategory::LEX}, {DiagKindRefactor::parse_diag_begin, DiagKindRefactor::parse_diag_end, DiagCategory::PARSE}, {DiagKindRefactor::sema_diag_begin, DiagKindRefactor::sema_diag_end, DiagCategory::SEMA}, {DiagKindRefactor::chir_diag_begin, DiagKindRefactor::chir_diag_end, DiagCategory::CHIR}, {DiagKindRefactor::import_package_diag_begin, DiagKindRefactor::import_package_diag_end, DiagCategory::IMPORT_PACKAGE}, {DiagKindRefactor::module_diag_begin, DiagKindRefactor::module_diag_end, DiagCategory::MODULE}, {DiagKindRefactor::driver_diag_begin, DiagKindRefactor::driver_diag_end, DiagCategory::OTHER}, {DiagKindRefactor::incremental_compilation_diag_begin, DiagKindRefactor::incremental_compilation_diag_end, DiagCategory::OTHER}, {DiagKindRefactor::parse_query_diag_begin, DiagKindRefactor::parse_query_diag_end, DiagCategory::PARSE_QUERY}, {DiagKindRefactor::frontend_diag_begin, DiagKindRefactor::frontend_diag_end, DiagCategory::OTHER}, {DiagKindRefactor::conditional_compilation_diag_begin, DiagKindRefactor::conditional_compilation_diag_end, DiagCategory::CONDITIONAL_COMPILATION}, }}; for (const auto& range : RANGES) { if (diagKind > range.start && diagKind < range.end) { return range.category; } } CJC_ABORT(); return DiagCategory::OTHER; } bool Diagnostic::IsValid() const { // only focus `mainHint`'s range, which is used by lsp return !mainHint.range.HasZero() && std::none_of(subDiags.begin(), subDiags.end(), [](auto& subDiag) { return subDiag.mainHint.range.HasZero(); }); } Position Diagnostic::GetBegin() { if (!start.IsZero()) { return start; } return mainHint.range.begin; } Position Diagnostic::GetEnd() { if (!end.IsZero()) { return end; } return mainHint.range.end; } std::string Diagnostic::GetErrorMessage() { if (!diagMessage.empty()) { return diagMessage; } return errorMessage; } DiagCategory Diagnostic::GetDiagCategory() const { return diagCategory; } int Diagnostic::GetDiagKind() const { if (!start.IsZero()) { return static_cast(kind); } // Sema diag end is last position in normal diag kind. // In order to make diagKind is unique, we only need to add rKind on it. return static_cast(DiagKind::sema_diag_end) + static_cast(rKind); } std::string Diagnostic::InsertArguments(std::string& rawString, std::vector& arguments) { if (rawString == "") { return rawString; } auto formatPos = rawString.find("%s"); size_t index = 0; while (formatPos != std::string::npos) { CJC_ASSERT(index < arguments.size()); (void)rawString.replace(formatPos, std::string("%s").size(), arguments[index]); formatPos += arguments[index].size(); index++; formatPos = rawString.find("%s", formatPos); } CJC_ASSERT(index == arguments.size()); return rawString; } void Diagnostic::HandleBadOtherHints() { if (otherHints.empty()) { return; } for (auto it = otherHints.begin(); it != otherHints.end();) { if (it->range.begin.fileID == mainHint.range.begin.fileID) { ++it; } else { subDiags.emplace_back(it->range, it->str); it = otherHints.erase(it); } } } DiagnosticBuilder::DiagnosticBuilder(DiagnosticEngine& diag, Diagnostic diagnostic) : diagnostic(std::move(diagnostic)), diag(diag) { } DiagnosticBuilder::~DiagnosticBuilder() { #ifndef CANGJIE_ENABLE_GCOV try { #endif if (!diag.GetEnableDiagnose()) { auto storedDiags = diag.ConsumeStoredDiags(); storedDiags.emplace_back(diagnostic); diag.SetStoredDiags(std::move(storedDiags)); return; } if (!diagnostic.isRefactor) { diag.ConvertArgsToDiagMessage(diagnostic); if (diag.DiagFilter(diagnostic)) { return; } if (diagnostic.kind == DEFAULT_KIND) { return; } } // No error will be reported during semantic analysis of code in macroCall. if (diagnostic.isInMacroCall) { return; } diag.HandleDiagnostic(diagnostic); #ifndef CANGJIE_ENABLE_GCOV } catch (...) { CJC_ABORT(); } #endif } void DiagnosticBuilder::AddHint(const Range& range, std::vector& arguments) { diag.CheckRange(diagnostic.GetDiagCategory(), range); auto errData = errorData[static_cast(diagnostic.rKind)]; if (diagnostic.otherHints.size() >= errData.otherHints.size()) { CJC_ASSERT(arguments.size() <= 1); auto str = arguments.empty() ? "" : arguments.front(); diagnostic.otherHints.emplace_back(range, str, OTHER_HINT_COLOR); return; } auto insertedStr = Diagnostic::InsertArguments(errData.otherHints.at(diagnostic.otherHints.size()), arguments); auto styledString = IntegratedString(range, insertedStr, OTHER_HINT_COLOR); diagnostic.otherHints.push_back(styledString); } void DiagnosticBuilder::AddNote(const SubDiagnostic& sub) { diag.CheckRange(diagnostic.GetDiagCategory(), sub.mainHint.range); for (auto& hint : sub.otherHints) { diag.CheckRange(diagnostic.GetDiagCategory(), hint.range); } for (auto& substitution : sub.help.substitutions) { diag.CheckRange(diagnostic.GetDiagCategory(), substitution.range); } diagnostic.subDiags.push_back(sub); } void DiagnosticBuilder::AddNote(const Range& range, const std::string& note) { diag.CheckRange(diagnostic.GetDiagCategory(), range); SubDiagnostic subDiag(range, note); AddNote(subDiag); } void DiagnosticBuilder::AddNote(const Position& pos, const std::string& note) { auto end = pos == DEFAULT_POSITION ? pos : pos + 1; AddNote(MakeRange(pos, end), note); } void DiagnosticBuilder::AddNote(const AST::Node& node, const std::string& note) { auto range = MakeRange(node.begin, node.end); diag.CheckRange(diagnostic.GetDiagCategory(), range); SubDiagnostic subDiag(range, note); AddNote(subDiag); } void DiagnosticBuilder::AddNote(const AST::Node& node, const Range& range, const std::string& note) { diag.CheckRange(diagnostic.GetDiagCategory(), range); auto newRange = MakeRange(node.GetMacroCallPos(range.begin), node.GetMacroCallPos(range.end, true)); SubDiagnostic subDiag(newRange, note); AddNote(subDiag); } void DiagnosticBuilder::AddNote(const std::string& note) { SubDiagnostic subDiag(note); AddNote(subDiag); } void DiagnosticBuilder::AddHelp(const DiagHelp& help) { for (auto& sub : help.substitutions) { diag.CheckRange(diagnostic.GetDiagCategory(), sub.range); } diagnostic.helps.push_back(help); } void DiagnosticEngineImpl::RegisterHandler(std::unique_ptr&& h) { handler = std::move(h); } void DiagnosticEngineImpl::IncreaseErrorCount(DiagCategory category) { firstErrorCategoryMtx.lock(); if (!firstErrorCategory.has_value() || category < firstErrorCategory.value()) { firstErrorCategory = category; } firstErrorCategoryMtx.unlock(); mux.lock(); countByCategory[category].first = countByCategory[category].first + 1; mux.unlock(); IncreaseErrorCount(); } void DiagnosticEngineImpl::IncreaseWarningCount(DiagCategory category) { mux.lock(); countByCategory[category].second = countByCategory[category].second + 1; warningCount++; mux.unlock(); } void DiagnosticEngineImpl::IncreaseErrorCount() { mux.lock(); errorCount++; mux.unlock(); } uint64_t DiagnosticEngineImpl::GetWarningCount() { [[maybe_unused]] std::lock_guard guard(firstErrorCategoryMtx); if (firstErrorCategory.has_value()) { uint64_t cnt = 0; // For warning, the count is total from start to first error category. for (auto i = 0; i <= static_cast(firstErrorCategory.value()); i++) { cnt += countByCategory[static_cast(i)].second; } return cnt; } return warningCount; } uint64_t DiagnosticEngineImpl::GetErrorCount() { [[maybe_unused]] std::lock_guard guard(firstErrorCategoryMtx); if (firstErrorCategory.has_value()) { // For warning, the count is number of first error category. if (auto iter = countByCategory.find(firstErrorCategory.value()); iter != countByCategory.end()) { return iter->second.first; } } return 0; } void DiagnosticEngine::RegisterHandler(DiagFormat format) { #if defined __GNUC__ && not defined __clang__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-default" #endif switch (format) { case DiagFormat::JSON: { auto h = std::make_unique(*this, true, true); RegisterHandler(std::move(h)); break; } case DiagFormat::NO_COLOR: { auto h = std::make_unique(*this, true); RegisterHandler(std::move(h)); break; } case DiagFormat::DEFAULT: { auto h = std::make_unique(*this); RegisterHandler(std::move(h)); break; } } #if defined __GNUC__ && not defined __clang__ #pragma GCC diagnostic pop #endif } bool DiagnosticEngineImpl::IsSupressedUnusedMain(const Diagnostic& diagnostic) noexcept { return warningOption->IsSuppressed(static_cast(WarnGroup::UNUSED_MAIN)) && diagnostic.rKind == DiagKindRefactor::chir_dce_unused_function_main; } void DiagnosticEngineImpl::HandleDiagnostic(Diagnostic& diagnostic) noexcept { if (!enableDiagnose) { return; } if (diagnostic.diagSeverity == DiagSeverity::DS_WARNING) { CJC_ASSERT(diagnostic.warnGroup != WarnGroup::NONE); if (disableWarning || warningOption->IsSuppressed(static_cast(diagnostic.warnGroup)) || IsSupressedUnusedMain(diagnostic)) { return; } else if (diagnostic.warnGroup != WarnGroup::UNGROUPED) { std::string warnGroupName = warnGroupDescrs[static_cast(diagnostic.warnGroup)]; auto msg = "this warning can be suppressed by setting the compiler option `-Woff " + warnGroupName + "`"; auto note = SubDiagnostic(msg); diagnostic.subDiags.push_back(note); } } CJC_ASSERT(handler); transactionMutex.lock(); if (isInTransaction[std::this_thread::get_id()]) { // if diagnosticEngine is in a transaction, // whole diagnostics in this transaction will be stored in transactionMap temporarily until `Commit` is called transactionMap[std::this_thread::get_id()].emplace_back(diagnostic); transactionMutex.unlock(); } else { transactionMutex.unlock(); handler->HandleDiagnose(diagnostic); } } void DiagnosticEngineImpl::Prepare() { transactionMutex.lock(); CJC_ASSERT(!isInTransaction[std::this_thread::get_id()]); isInTransaction[std::this_thread::get_id()] = true; transactionMap[std::this_thread::get_id()].clear(); transactionMutex.unlock(); } void DiagnosticEngineImpl::Commit() { transactionMutex.lock(); CJC_ASSERT(isInTransaction[std::this_thread::get_id()]); auto& cachedDiagnostic = transactionMap[std::this_thread::get_id()]; for (auto& diagnostic : cachedDiagnostic) { handler->HandleDiagnose(diagnostic); } transactionMap.erase(std::this_thread::get_id()); isInTransaction.erase(std::this_thread::get_id()); transactionMutex.unlock(); } void DiagnosticEngineImpl::ClearTransaction() { transactionMutex.lock(); CJC_ASSERT(isInTransaction[std::this_thread::get_id()]); transactionMap.erase(std::this_thread::get_id()); isInTransaction.erase(std::this_thread::get_id()); transactionMutex.unlock(); } std::string DiagnosticEngineImpl::GetArgStr( char formatChar, std::vector& formatArgs, unsigned long index, Diagnostic& diagnostic) { switch (formatChar) { case 'd': if (auto val = std::get_if(&formatArgs[index].arg)) { return std::to_string(*val); } else { Errorln("The num ", index, "format parameter does not match"); } break; case 's': if (auto val = std::get_if(&formatArgs[index].arg)) { if (val->empty()) { diagnostic.kind = DEFAULT_KIND; } return *val; } else { Errorln("The num ", index, "format parameter does not match"); } break; case 'c': if (auto val = std::get_if(&formatArgs[index].arg); val) { return std::string(1, *val); } else { Errorln("The num ", index, "format parameter does not match"); } break; case 'p': if (auto val = std::get_if(&formatArgs[index].arg); val) { return GetSourceManager().GetSource((*val).fileID).path + ":" + std::to_string((*val).line) + ":" + std::to_string((*val).column); } else { Errorln("The num ", index, "format parameter does not match"); } break; default: Errorln("%", formatChar, " is illegal format"); break; } return {}; } void DiagnosticEngineImpl::ConvertArgsToDiagMessage(Diagnostic& diagnostic) noexcept { std::string formatStr = DiagMessages[static_cast(diagnostic.kind)]; // C string format length, like length of '%f', '%d'. const static size_t formatLens = 2; uint8_t index = 0; auto formatPos = formatStr.find('%'); auto& formatArgs = diagnostic.args; while (formatPos != std::string::npos) { if (index >= formatArgs.size()) { break; } if (formatPos + 1 >= formatStr.size()) { return; } std::string argStr = GetArgStr(formatStr[formatPos + 1], formatArgs, index, diagnostic); if (!argStr.empty()) { (void)formatStr.replace(formatPos, formatLens, argStr); formatPos += argStr.size(); } index++; formatPos = formatStr.find('%', formatPos); } diagnostic.diagMessage = formatStr; for (auto& note : diagnostic.notes) { ConvertArgsToDiagMessage(note); } } void DiagnosticEngineImpl::CheckRange(DiagCategory cate, const Range& range) { // if Parse/Lex Stage has errors, even though the `range` in follow-up phases `HasZero`. // the `InternalError` is skipped. auto checkZero = [this, &range]() { // check DEFAULT_POSITION for libast before emit message if (checkRangeErrorCodeRatherICE) { if (range.begin.IsZero() || range.end.IsZero()) { diagEngineErrorCode = DiagEngineErrorCode::DIAG_RANGE_ERROR; return; } } if (range.begin.IsZero()) { InternalError("begin of range is zero"); } if (range.end.IsZero()) { InternalError("end of range is zero"); } }; if (cate == DiagCategory::LEX || cate == DiagCategory::PARSE) { // Parse/Lex 's error always be checked. (in a way of InternalError) checkZero(); return; } std::lock_guard guard(mux); if ((countByCategory.count(DiagCategory::LEX) == 0 || countByCategory[DiagCategory::LEX].first == 0) && (countByCategory.count(DiagCategory::PARSE) == 0 || countByCategory[DiagCategory::PARSE].first == 0)) { // only Parse/Lex doesn't have errors, check the range in follow-up phases' diagnose. checkZero(); return; } } void DiagnosticEngineImpl::Reset() { mux.lock(); errorCount = 0; warningCount = 0; countByCategory.clear(); mux.unlock(); firstErrorCategoryMtx.lock(); firstErrorCategory = std::nullopt; firstErrorCategoryMtx.unlock(); handler->Clear(); } bool DiagnosticEngineImpl::DiagFilter(Diagnostic& diagnostic) noexcept { for (auto& filter : diagFilters) { if (filter(diagnostic)) { return true; } } return false; } DiagnosticEngine::DiagnosticEngine() : impl{new DiagnosticEngineImpl{}} { auto h = std::make_unique(*this); RegisterHandler(std::move(h)); } DiagnosticEngine::~DiagnosticEngine() noexcept { delete impl; } std::vector DiagnosticEngineImpl::DisableDiagnose() { disableDiagDeep = disableDiagDeep + 1; if (disableDiagDeep > 0) { if (enableDiagnose) { enableDiagnose = false; } } return ConsumeStoredDiags(); } void DiagnosticEngineImpl::EnableDiagnose() { if (disableDiagDeep > 0) { disableDiagDeep = disableDiagDeep - 1; } if (disableDiagDeep == 0) { if (!enableDiagnose) { enableDiagnose = true; storedDiags.clear(); } } } void DiagnosticEngineImpl::EnableDiagnose(const std::vector& diags) { EnableDiagnose(); storedDiags = diags; } std::vector DiagnosticEngineImpl::ConsumeStoredDiags() { std::vector stored = storedDiags; storedDiags.clear(); return stored; } Range DiagnosticEngineImpl::MakeRealRange( const AST::Node& node, const Position begin, const Position end, bool begLowBound) const { auto newBegin = node.GetMacroCallPos(begin, begLowBound); auto newEnd = node.GetMacroCallPos(end, true); return MakeRange(newBegin, newEnd); } void DiagnosticEngineImpl::SetSourceManager(SourceManager* sm) { CJC_NULLPTR_CHECK(sm); sourceManager = sm; } SourceManager& DiagnosticEngineImpl::GetSourceManager() noexcept { CJC_NULLPTR_CHECK(sourceManager); return *sourceManager; } void DiagnosticEngineImpl::ReportErrorAndWarningCount() { if (!GetIsDumpErrCnt()) { return; } if (handler->GetKind() == DiagHandlerKind::COMPILER_HANDLER) { auto hk = static_cast(handler.get()); if (hk->IsJsonFormat()) { hk->CacheTheCountInJsonFormat(); return; } } auto errorCnt = GetErrorCount(); auto errorPrintCnt = GetErrorPrintCount(); if (errorCnt > 0) { WriteError(errorCnt, " error", ((errorCnt > 1) ? "s" : ""), " generated, "); WriteError(errorPrintCnt, " error", ((errorPrintCnt > 1) ? "s" : ""), " printed.\n"); } auto warningCnt = GetWarningCount(); auto warningPrintCnt = GetWarningPrintCount(); if (warningCnt > 0) { WriteError(warningCnt, " warning", ((warningCnt > 1) ? "s" : ""), " generated, "); WriteError(warningPrintCnt, " warning", ((warningPrintCnt > 1) ? "s" : ""), " printed.\n"); } } void DiagnosticEngineImpl::AddMacroCallNote(Diagnostic& diagnostic, const AST::Node& node, const Position& pos) { if (!node.TestAttr(AST::Attribute::MACRO_EXPANDED_NODE) || !node.curMacroCall) { return; } diagnostic.curMacroCall = node.curMacroCall; // Refactor the Diagnose of the node after the macro expansion. auto pInvocation = node.curMacroCall->GetInvocation(); if (!pInvocation || IsPureAnnotation(*pInvocation)) { return; } // For cjc, display a hint message on the source code if the corresponding source code exists. if (!pInvocation->isForLSP) { Position originPos; auto key = pInvocation->isCurFile ? pos.Hash32() : static_cast(pos.column); if (pInvocation->isCurFile && pInvocation->originPosMap.find(key) != pInvocation->originPosMap.end()) { originPos = pInvocation->originPosMap.at(key); } else if (pInvocation->new2originPosMap.find(pos.Hash32()) != pInvocation->new2originPosMap.end()) { originPos = pInvocation->new2originPosMap.at(pos.Hash32()); if (!originPos.isCurFile) { originPos = INVALID_POSITION; } } if (originPos != INVALID_POSITION && originPos != diagnostic.start && originPos != diagnostic.mainHint.range.begin) { if (diagnostic.errorMessage.empty()) { ConvertArgsToDiagMessage(diagnostic); auto range = MakeRange(diagnostic.start, diagnostic.end); (void)diagnostic.subDiags.emplace_back(range, "which is expanded as follows"); diagnostic.start = originPos; diagnostic.end = originPos + 1; } else { (void)diagnostic.subDiags.emplace_back(diagnostic.mainHint.range, "which is expanded as follows"); } diagnostic.mainHint.range = MakeRange(originPos, originPos + 1); } } auto mcBegin = node.curMacroCall->begin; auto idPosEnd = pInvocation->identifierPos + pInvocation->identifier.size(); // For lsp, the error range includes only identifier. // Otherwise, the error range includes the entire macrocall node. auto mcEnd = pInvocation->isForLSP ? idPosEnd : node.curMacroCall->end; std::string sevInfo = (diagnostic.diagSeverity == DiagSeverity::DS_ERROR) ? "the error" : "the warning"; (void)diagnostic.subDiags.emplace_back(MakeRange(mcBegin, mcEnd), sevInfo + " occurs after the macro is expanded"); if (!pInvocation->isForLSP) { auto codeRange = MakeRange(pInvocation->mcBegin, pInvocation->mcEnd); (void)diagnostic.subDiags.emplace_back(codeRange, MACROCALL_CODE); } } void DiagnosticHandler::SetPrevDiag(Position pos, std::string str) { mtx.lock(); prevDiags.emplace(pos, str); mtx.unlock(); } bool DiagnosticHandler::HasPrevDiag(Position pos, std::string str) { auto pair = std::make_pair(pos, str); mtx.lock(); bool res = prevDiags.find(pair) != prevDiags.end(); mtx.unlock(); return res; } static void ConvertOldDiagToNew(Diagnostic& d) { d.isConvertedToRefactor = d.isRefactor = true; d.errorMessage = d.diagMessage; CJC_ASSERT(SEVE_TO_COLOR.find(d.diagSeverity) != SEVE_TO_COLOR.end()); auto first = d.start; auto second = (d.end == DEFAULT_POSITION || d.end > d.start) ? d.end : d.start + 1; d.mainHint = IntegratedString{MakeRange(first, second), "", SEVE_TO_COLOR.at(d.diagSeverity)}; if (!d.notes.empty()) { for (auto& n : d.notes) { first = n.start; second = (n.end == DEFAULT_POSITION || n.end > n.start) ? n.end : n.start + 1; d.subDiags.emplace_back(MakeRange(first, second), n.diagMessage); } } d.start = INVALID_POSITION; d.end = INVALID_POSITION; d.diagMessage.clear(); d.notes.clear(); d.rKind = DiagKindRefactor::lex_diag_begin; } bool CompilerDiagnosticHandler::SaveDiagnostics(const Diagnostic& d) { static const std::vector CATEGORY_BE_SAVEED = { DiagCategory::LEX, DiagCategory::PARSE, DiagCategory::CONDITIONAL_COMPILATION, DiagCategory::IMPORT_PACKAGE, DiagCategory::MODULE, DiagCategory::MACRO_EXPAND, DiagCategory::SEMA, DiagCategory::CHIR, DiagCategory::OTHER, }; return Utils::In(d.diagCategory, CATEGORY_BE_SAVEED) ? SaveCategoryDiagnostic(d) : true; } bool CompilerDiagnosticHandler::CanBeEmitted(const DiagCategory& d) { // Only emit first category currently, consider remove this limitation. auto lock = diag.LockFirstErrorCategory(); bool result = !diag.FirstErrorCategory() || d <= *diag.FirstErrorCategory(); return result; } void CompilerDiagnosticHandler::HandleDiagnose(Diagnostic& d) { // This is for unifying old and new diagnostic messages. if (!d.isRefactor) { ConvertOldDiagToNew(d); } if (HasPrevDiag(d.mainHint.range.begin, d.errorMessage)) { return; } SetPrevDiag(d.mainHint.range.begin, d.errorMessage); // Diagnostic engine can accept diag without position, like driver diagnostics. if (!d.mainHint.range.IsDefault()) { if (!jsonFormat) { d.HandleBadOtherHints(); } if (!SaveDiagnostics(d)) { return; } } if (!CanBeEmitted(d.diagCategory)) { return; } if (d.diagSeverity == DiagSeverity::DS_ERROR) { diag.IncreaseErrorCount(d.diagCategory); } if (d.diagSeverity == DiagSeverity::DS_WARNING) { diag.IncreaseWarningCount(d.diagCategory); } // The lex, chir and parse is doing in parallel. So there is data race if we emit it immediately. // For lex, chir and parse diagnostic category: collect and emit at the same time. // other category: emit immediately. if (d.diagCategory == DiagCategory::LEX || d.diagCategory == DiagCategory::PARSE || d.diagCategory == DiagCategory::CHIR) { return; } EmitDiagnose(d); } void CompilerDiagnosticHandler::EmitDiagnose(Diagnostic d) { if (!diag.GetIsEmitter()) { return; } // Check whether we have printed enough errors, i.e. amount of errors exceeded error count limit or not. auto maybeNumber = diag.GetMaxNumOfDiags(); if (maybeNumber.has_value() && diag.GetErrorPrintCount() >= maybeNumber.value()) { return; } bool noRangeCheckError = true; if (jsonFormat) { DiagnosticJsonFormatter formatter(diag); diagsJsonBuff.push_back(formatter.FormatDiagnosticToJson(d)); } else if (diag.HasSourceManager()) { DiagnosticEmitter tmp = DiagnosticEmitter(d, noColor, !diag.IsCheckRangeErrorCodeRatherICE(), outToStringStream ? strStream : std::cerr, diag.GetSourceManager()); noRangeCheckError = tmp.Emit(); } else { SourceManager sm; DiagnosticEmitter tmp = DiagnosticEmitter( d, noColor, !diag.IsCheckRangeErrorCodeRatherICE(), outToStringStream ? strStream : std::cerr, sm); noRangeCheckError = tmp.Emit(); } if (!noRangeCheckError) { diag.SetDiagEngineErrorCode(DiagEngineErrorCode::DIAG_RANGE_ERROR); } if (d.diagSeverity == DiagSeverity::DS_ERROR) { diag.IncreaseErrorPrintCount(); } else { diag.IncreaseWarningPrintCount(); } } void CompilerDiagnosticHandler::CacheTheCountInJsonFormat() { DiagnosticJsonFormatter formatter(diag); diagNumJsonBuff = formatter.FormatDiagnosticCountToJsonString(); } void CompilerDiagnosticHandler::EmitDiagnoseGroup() { EmitCategoryDiagnostics(DiagCategory::LEX); // the DiagCategory::PARSE is printed only if no DiagCategory::LEX is printed if (diag.GetErrorPrintCount() == 0) { EmitCategoryDiagnostics(DiagCategory::PARSE); } } void CompilerDiagnosticHandler::EmitDiagnosesInJson() noexcept { if (!diag.GetIsEmitter()) { return; } std::cerr << DiagnosticJsonFormatter::AssembleDiagnosticJsonString(diagsJsonBuff, diagNumJsonBuff); } std::vector CompilerDiagnosticHandler::GetCategoryDiagnosticsSortedByRange(DiagCategory cate) const { auto targets = GetCategoryDiagnostic(cate); std::sort(targets.begin(), targets.end(), [](auto& a, auto& b) -> bool { auto rangeA = a.mainHint.range; auto rangeB = b.mainHint.range; if (rangeA.begin < rangeB.begin) { return true; } if (rangeA.begin == rangeB.begin && rangeA.end < rangeB.end) { return true; } return false; }); return targets; } void CompilerDiagnosticHandler::EmitCategoryDiagnostics(DiagCategory cate) { if (diagnostics.count(cate) == 0) { return; } if (!CanBeEmitted(cate)) { return; } auto targets = GetCategoryDiagnosticsSortedByRange(cate); for (auto& d : targets) { EmitDiagnose(d); } diagnostics.erase(cate); } bool DiagnosticCache::NoError() const { for (auto& diag : cachedDiags) { if (diag.diagSeverity == DiagSeverity::DS_ERROR) { return false; } } return true; } DiagnosticCache::DiagCacheKey DiagnosticCache::ExtractKey(const DiagnosticEngine& diag) { if (diag.GetDisableDiagDeep() == 0) { return 0; } else { return 1; } } void DiagnosticCache::ToExclude(const DiagnosticEngine& diagBefore) { cachedDiags = diagBefore.GetStoredDiags(); } void DiagnosticCache::BackUp(const DiagnosticEngine& diagAfter) { auto& storedDiags = diagAfter.GetStoredDiags(); if (storedDiags.size() > cachedDiags.size()) { cachedDiags = std::vector( storedDiags.begin() + static_cast(cachedDiags.size()), storedDiags.end()); } else { cachedDiags.clear(); } } void DiagnosticCache::Restore(DiagnosticEngine& dst) { auto old = dst.ConsumeStoredDiags(); old.insert(old.end(), cachedDiags.begin(), cachedDiags.end()); dst.SetStoredDiags(std::move(old)); } }; // namespace Cangjie cangjie_compiler-1.0.7/src/Basic/DiagnosticEngineImpl.cpp000066400000000000000000000163171510705540100233670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares class DiagnosticEngineImpl, which is an implementation class of DiagnosticEngine. */ #include "DiagnosticEngineImpl.h" namespace Cangjie { DiagnosticEngine::StashDisableDiagnoseStatus::StashDisableDiagnoseStatus(DiagnosticEngine* e, bool hasTargetType) : engine(e), enableDiagnose(e->impl->enableDiagnose), disableDiagDeep(e->impl->disableDiagDeep), storedDiags(e->impl->storedDiags), hasTargetType(hasTargetType) { if (!hasTargetType) { e->impl->enableDiagnose = true; e->impl->disableDiagDeep = 0; e->impl->storedDiags.clear(); } } DiagnosticEngine::StashDisableDiagnoseStatus::~StashDisableDiagnoseStatus() noexcept { if (hasTargetType) { engine->impl->enableDiagnose = true; engine->impl->disableDiagDeep = 0; engine->impl->storedDiags.erase( std::remove_if(engine->impl->storedDiags.begin(), engine->impl->storedDiags.end(), [](const Diagnostic& diag) { return diag.diagSeverity == DiagSeverity::DS_ERROR; }), engine->impl->storedDiags.end()); for (auto& diag : engine->impl->storedDiags) { engine->Diagnose(diag); } } std::swap(engine->impl->enableDiagnose, enableDiagnose); std::swap(engine->impl->disableDiagDeep, disableDiagDeep); std::swap(engine->impl->storedDiags, storedDiags); } bool DiagnosticEngine::HasSourceManager() { return impl->HasSourceManager(); } void DiagnosticEngine::SetIsEmitter(bool emitter) { return impl->SetIsEmitter(emitter); } void DiagnosticEngine::SetDisableWarning(bool dis) { return impl->SetDisableWarning(dis); } bool DiagnosticEngine::GetIsEmitter() const { return impl->GetIsEmitter(); } void DiagnosticEngine::SetIsDumpErrCnt(bool dump) { return impl->SetIsDumpErrCnt(dump); } bool DiagnosticEngine::GetIsDumpErrCnt() const { return impl->GetIsDumpErrCnt(); } void DiagnosticEngine::SetSourceManager(SourceManager* sm) { return impl->SetSourceManager(sm); } SourceManager& DiagnosticEngine::GetSourceManager() noexcept { return impl->GetSourceManager(); } void DiagnosticEngine::AddMacroCallNote(Diagnostic& diagnostic, const AST::Node& node, const Position& pos) { return impl->AddMacroCallNote(diagnostic, node, pos); } void DiagnosticEngine::Prepare() { impl->Prepare(); } void DiagnosticEngine::Commit() { impl->Commit(); } void DiagnosticEngine::ClearTransaction() { impl->ClearTransaction(); } void DiagnosticEngine::EnableCheckRangeErrorCodeRatherICE() { impl->EnableCheckRangeErrorCodeRatherICE(); } void DiagnosticEngine::DisableCheckRangeErrorCodeRatherICE() { impl->DisableCheckRangeErrorCodeRatherICE(); } bool DiagnosticEngine::IsCheckRangeErrorCodeRatherICE() const { return impl->IsCheckRangeErrorCodeRatherICE(); } void DiagnosticEngine::SetDiagEngineErrorCode(DiagEngineErrorCode errorCode) { impl->SetDiagEngineErrorCode(errorCode); } std::lock_guard DiagnosticEngine::LockFirstErrorCategory() { return impl->LockFirstErrorCategory(); } const std::optional& DiagnosticEngine::FirstErrorCategory() const { return impl->FirstErrorCategory(); } int32_t DiagnosticEngine::GetDisableDiagDeep() const { return impl->GetDisableDiagDeep(); } const std::vector& DiagnosticEngine::GetStoredDiags() const { return impl->GetStoredDiags(); } void DiagnosticEngine::SetStoredDiags(std::vector&& value) { return impl->SetStoredDiags(std::move(value)); } bool DiagnosticEngine::GetEnableDiagnose() const { return impl->GetEnableDiagnose(); } bool DiagnosticEngine::DiagFilter(Diagnostic& diagnostic) noexcept { return impl->DiagFilter(diagnostic); } void DiagnosticEngine::ConvertArgsToDiagMessage(Diagnostic& diagnostic) noexcept { impl->ConvertArgsToDiagMessage(diagnostic); } void DiagnosticEngine::RegisterHandler(std::unique_ptr&& h) { impl->RegisterHandler(std::move(h)); } void DiagnosticEngine::IncreaseErrorCount(DiagCategory category) { impl->IncreaseErrorCount(category); } void DiagnosticEngine::IncreaseWarningCount(DiagCategory category) { impl->IncreaseWarningCount(category); } void DiagnosticEngine::IncreaseErrorCount() { impl->IncreaseErrorCount(); } uint64_t DiagnosticEngine::GetWarningCount() { return impl->GetWarningCount(); } uint64_t DiagnosticEngine::GetErrorCount() { return impl->GetErrorCount(); } void DiagnosticEngine::IncreaseWarningPrintCount() { impl->IncreaseWarningPrintCount(); } unsigned int DiagnosticEngine::GetWarningPrintCount() const { return impl->GetWarningPrintCount(); } void DiagnosticEngine::IncreaseErrorPrintCount() { impl->IncreaseErrorPrintCount(); } unsigned int DiagnosticEngine::GetErrorPrintCount() const { return impl->GetErrorPrintCount(); } std::optional DiagnosticEngine::GetMaxNumOfDiags() const { return impl->GetMaxNumOfDiags(); } bool DiagnosticEngine::IsSupressedUnusedMain(const Diagnostic& diagnostic) noexcept { return impl->IsSupressedUnusedMain(diagnostic); } void DiagnosticEngine::HandleDiagnostic(Diagnostic& diagnostic) noexcept { impl->HandleDiagnostic(diagnostic); } void DiagnosticEngine::EmitCategoryDiagnostics(DiagCategory cate) { impl->EmitCategoryDiagnostics(cate); } DiagEngineErrorCode DiagnosticEngine::GetCategoryDiagnosticsString(DiagCategory cate, std::string& diagOut) { return impl->GetCategoryDiagnosticsString(cate, diagOut); } void DiagnosticEngine::EmitCategoryGroup() { impl->EmitCategoryGroup(); } void DiagnosticEngine::SetErrorCountLimit(std::optional errorCountLimit) { impl->SetErrorCountLimit(errorCountLimit); } std::vector DiagnosticEngine::GetCategoryDiagnostic(DiagCategory cate) { return impl->GetCategoryDiagnostic(cate); } void DiagnosticEngine::Reset() { impl->Reset(); } void DiagnosticEngine::ClearError() { impl->ClearError(); } void DiagnosticEngine::SetDiagnoseStatus(bool enable) { impl->SetDiagnoseStatus(enable); } bool DiagnosticEngine::GetDiagnoseStatus() const { return impl->GetDiagnoseStatus(); } void DiagnosticEngine::ReportErrorAndWarningCount() { impl->ReportErrorAndWarningCount(); } std::vector DiagnosticEngine::DisableDiagnose() { return impl->DisableDiagnose(); } void DiagnosticEngine::EnableDiagnose() { impl->EnableDiagnose(); } void DiagnosticEngine::EnableDiagnose(const std::vector& diags) { impl->EnableDiagnose(diags); } std::vector DiagnosticEngine::ConsumeStoredDiags() { return impl->ConsumeStoredDiags(); } bool DiagnosticEngine::HardDisable() const { return impl->HardDisable(); } void DiagnosticEngine::CheckRange(DiagCategory cate, const Range& range) { return impl->CheckRange(cate, range); } Range DiagnosticEngine::MakeRealRange( const AST::Node& node, const Position begin, const Position end, bool begLowBound) const { return impl->MakeRealRange(node, begin, end, begLowBound); } } // namespace Cangjie cangjie_compiler-1.0.7/src/Basic/DiagnosticEngineImpl.h000066400000000000000000000214111510705540100230230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares class DiagnosticEngineImpl, which is an implementation class of DiagnosticEngine. */ #ifndef CANGJIE_BASIC_DIAGNOSTICENGINEIMPL_H #define CANGJIE_BASIC_DIAGNOSTICENGINEIMPL_H #include "cangjie/Basic/DiagnosticEngine.h" namespace Cangjie { class DiagnosticEngineImpl { public: ~DiagnosticEngineImpl() noexcept { if (handler->GetKind() == DiagHandlerKind::COMPILER_HANDLER) { auto hk = static_cast(handler.get()); if (hk->IsJsonFormat()) { hk->EmitDiagnosesInJson(); } } } bool HasSourceManager() const { return sourceManager != nullptr; } void SetIsEmitter(bool emitter) { isEmitter = emitter; } void SetDisableWarning(bool dis) { disableWarning = dis; } bool GetIsEmitter() const { return isEmitter; } void SetIsDumpErrCnt(bool dump) { isDumpErrCnt = dump; } bool GetIsDumpErrCnt() const { return isDumpErrCnt; } void SetSourceManager(SourceManager* sm); SourceManager& GetSourceManager() noexcept; std::lock_guard LockFirstErrorCategory() { return std::lock_guard{firstErrorCategoryMtx}; } const std::optional& FirstErrorCategory() const { return firstErrorCategory; } int32_t GetDisableDiagDeep() const { return disableDiagDeep; } const std::vector& GetStoredDiags() const& { return storedDiags; } std::vector GetStoredDiags() && { return std::move(storedDiags); } void SetStoredDiags(std::vector&& value) { storedDiags = std::move(value); } bool GetEnableDiagnose() const { return enableDiagnose; } void AddMacroCallNote(Diagnostic& diagnostic, const AST::Node& node, const Position& pos); // ability of transaction void Prepare(); void Commit(); void ClearTransaction(); // use DiagEngineErrorCode rather than internal error message (for libast) void EnableCheckRangeErrorCodeRatherICE() { checkRangeErrorCodeRatherICE = true; }; void DisableCheckRangeErrorCodeRatherICE() { checkRangeErrorCodeRatherICE = false; }; bool IsCheckRangeErrorCodeRatherICE() const { return checkRangeErrorCodeRatherICE; }; void SetDiagEngineErrorCode(DiagEngineErrorCode errorCode) { diagEngineErrorCode = errorCode; }; /** * Convert unformat diagnostic message to real diagnostic message. */ void ConvertArgsToDiagMessage(Diagnostic& diagnostic) noexcept; /** * Register diagnostic observer to diagnostic engine. */ void RegisterHandler(std::unique_ptr&& h); void IncreaseErrorCount(DiagCategory category); void IncreaseWarningCount(DiagCategory category); void IncreaseErrorCount(); uint64_t GetWarningCount(); uint64_t GetErrorCount(); void IncreaseWarningPrintCount() { warningPrintCount++; } unsigned int GetWarningPrintCount() const { return warningPrintCount; } void IncreaseErrorPrintCount() { errorPrintCount++; } unsigned int GetErrorPrintCount() const { return errorPrintCount; } std::optional GetMaxNumOfDiags() const { return maxNumOfDiags; } bool IsSupressedUnusedMain(const Diagnostic& diagnostic) noexcept; void HandleDiagnostic(Diagnostic& diagnostic) noexcept; void EmitCategoryDiagnostics(DiagCategory cate) { if (handler->GetKind() == DiagHandlerKind::COMPILER_HANDLER) { auto hk = static_cast(handler.get()); hk->EmitCategoryDiagnostics(cate); } } DiagEngineErrorCode GetCategoryDiagnosticsString(DiagCategory cate, std::string& diagOut) { if (handler->GetKind() == DiagHandlerKind::COMPILER_HANDLER) { if (diagEngineErrorCode != DiagEngineErrorCode::NO_ERRORS && checkRangeErrorCodeRatherICE) { // Emit may cause unpredictable errors if diag engine error has been found before return diagEngineErrorCode; } auto hk = static_cast(handler.get()); hk->SetOutToStringStream(); hk->EmitCategoryDiagnostics(cate); hk->SetOutToErrStream(); diagOut = hk->GetOutString(); } return diagEngineErrorCode; } void EmitCategoryGroup() { if (handler->GetKind() == DiagHandlerKind::COMPILER_HANDLER) { auto hk = static_cast(handler.get()); hk->EmitDiagnoseGroup(); } } void SetErrorCountLimit(std::optional errorCountLimit) { maxNumOfDiags = std::move(errorCountLimit); } std::vector GetCategoryDiagnostic(DiagCategory cate) { if (handler->GetKind() == DiagHandlerKind::COMPILER_HANDLER) { auto hk = static_cast(handler.get()); return hk->GetCategoryDiagnostic(cate); } return {}; } void ClearError() { errorCount = 0; } void Reset(); /** * Set the status of diagnostic engine. * @param enable */ void SetDiagnoseStatus(bool enable) { enableDiagnose = enable; hardDisable = !enable; } /** * Get the status of diagnostic engine. */ bool GetDiagnoseStatus() const { return enableDiagnose; } /** * Report the number of errors and warnings. */ void ReportErrorAndWarningCount(); /** * Do diagnostic filter, if the diagnostic should be filter,return true. */ bool DiagFilter(Diagnostic& diagnostic) noexcept; /** * Disable diagnose, and take out current stored diagnoses. * return current stored diagnoses in diagnose engine. */ std::vector DisableDiagnose(); void EnableDiagnose(); /** * Set stored diagnoses to given contents, can be used to suppress cached diagnoses. * @param diags given diagnoses to be restored in diagnose engine */ void EnableDiagnose(const std::vector& diags); std::vector ConsumeStoredDiags(); bool HardDisable() const { return hardDisable; } /** * Make real Range if node is expanded from macrocall, otherwise Make Range from the begin and the end. */ Range MakeRealRange( const AST::Node& node, const Position begin, const Position end, bool begLowBound = false) const; void CheckRange(DiagCategory cate, const Range& range); private: mutable unsigned int errorCount = 0; mutable unsigned int warningCount = 0; mutable unsigned int errorPrintCount = 0; mutable unsigned int warningPrintCount = 0; // Key is category, value is error and warning count. mutable std::unordered_map> countByCategory; std::mutex mux; std::mutex transactionMutex; // for Prepare/Commit/ClearTransaction int32_t disableDiagDeep = 0; bool enableDiagnose{true}; bool disableWarning{false}; /* Does disable all warning. */ bool hardDisable{false}; /**< Mark whether disabled by SetDiagnoseStatus. */ std::unique_ptr handler; std::optional maxNumOfDiags = DEFAULT_DIAG_NUM; std::vector> diagFilters; std::vector storedDiags; // IsEmitter is used to some tools like CJLint which don't want to output error to terminal. bool isEmitter{true}; bool isDumpErrCnt{true}; SourceManager* sourceManager{nullptr}; // transaction of Diagnose, e.g Parse Type in ParseRefExpr std::unordered_map> transactionMap; std::unordered_map isInTransaction; WarningOptionMgr* const warningOption = WarningOptionMgr::GetInstance(); std::string GetArgStr( char formatChar, std::vector& formatArgs, unsigned long index, Diagnostic& diagnostic); bool checkRangeErrorCodeRatherICE{false}; DiagEngineErrorCode diagEngineErrorCode{DiagEngineErrorCode::NO_ERRORS}; std::mutex firstErrorCategoryMtx; std::optional firstErrorCategory = std::nullopt; friend class DiagnosticEngine::StashDisableDiagnoseStatus; }; } #endif cangjie_compiler-1.0.7/src/Basic/DiagnosticJsonFormatter.cpp000066400000000000000000000312531510705540100241310ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the DiagnosticEmitter related classes. */ #include "cangjie/Basic/DiagnosticJsonFormatter.h" #include "cangjie/Basic/StringConvertor.h" namespace Cangjie { namespace { static const std::unordered_map CATE_TO_STR{ {DiagCategory::LEX, "lex"}, {DiagCategory::PARSE, "parse"}, {DiagCategory::PARSE_QUERY, "parse_query"}, {DiagCategory::CONDITIONAL_COMPILATION, "conditional_compilation"}, {DiagCategory::IMPORT_PACKAGE, "import_package"}, {DiagCategory::MODULE, "module"}, {DiagCategory::MACRO_EXPAND, "macro_expand"}, {DiagCategory::SEMA, "sema"}, {DiagCategory::CHIR, "chir"}, {DiagCategory::OTHER, "other"}, }; constexpr std::size_t INDENT_WIDTH = 4; } // namespace std::string DiagnosticJsonFormatter::FormatPostionJson(size_t deep, const Position& pos) { std::string diagsJsonStr = Whitespace(deep * INDENT_WIDTH) + "\"File\": \""; std::string fileStr = std::to_string(pos.fileID); if (diag.HasSourceManager()) { if (diag.GetSourceManager().IsSourceFileExist(pos.fileID)) { auto source = diag.GetSourceManager().GetSource(pos.fileID); if (source.packageName.has_value()) { fileStr = "(package " + source.packageName.value() + ")" + FileUtil::GetFileName(source.path); } else { fileStr = source.path; } } } diagsJsonStr += StringConvertor::EscapeToJsonString(fileStr) + "\",\n"; diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"Line\": " + std::to_string(pos.line) + ",\n"; diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"Column\": " + std::to_string(pos.column); return diagsJsonStr; } std::string DiagnosticJsonFormatter::FormatRangeJson(size_t deep, const Range& range) { std::string diagsJsonStr = Whitespace(deep * INDENT_WIDTH) + "\"Range\": {\n"; ++deep; // begin diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"Begin\": {\n"; diagsJsonStr += FormatPostionJson(deep + 1, range.begin) + "\n"; diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "},\n"; // end diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"End\": {\n"; diagsJsonStr += FormatPostionJson(deep + 1, range.end) + "\n"; diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "}\n"; diagsJsonStr += Whitespace((deep - 1) * INDENT_WIDTH) + "}"; return diagsJsonStr; } std::string DiagnosticJsonFormatter::FormatHintJson(size_t deep, const IntegratedString& hint, bool newLine) { std::string diagsJsonStr = Whitespace(deep * INDENT_WIDTH) + "{\n"; if (!newLine) { diagsJsonStr = "{\n"; } ++deep; diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"Content\": \"" + StringConvertor::EscapeToJsonString(hint.str) + "\",\n"; diagsJsonStr += FormatRangeJson(deep, hint.range) + "\n"; --deep; diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "}"; return diagsJsonStr; } std::string DiagnosticJsonFormatter::FormatHintsJson(size_t deep, const std::vector& hints) { if (hints.empty()) { return {"[]"}; } std::string diagsJsonStr{"[\n"}; for (size_t i = 0; i < hints.size(); ++i) { diagsJsonStr += FormatHintJson(deep + 1, hints[i]); diagsJsonStr += (i == hints.size() - 1) ? "\n" : ",\n"; } diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "]"; return diagsJsonStr; } std::string DiagnosticJsonFormatter::FormatSubstitutionJson(size_t deep, const Substitution& substitution) { std::string diagsJsonStr = Whitespace(deep * INDENT_WIDTH) + "{\n"; ++deep; diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"Content\": \"" + StringConvertor::EscapeToJsonString(substitution.str) + "\",\n"; diagsJsonStr += FormatRangeJson(deep, substitution.range) + "\n"; --deep; diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "}"; return diagsJsonStr; } std::string DiagnosticJsonFormatter::FormatDiagnosticMainContentJson(size_t deep, const Diagnostic& d) { // Messsage std::string diagsJsonStr = Whitespace(deep * INDENT_WIDTH) + "\"Message\": \"" + StringConvertor::EscapeToJsonString(d.errorMessage) + "\",\n"; // Location diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"Location\": {\n"; diagsJsonStr += FormatPostionJson(deep + 1, d.mainHint.range.begin) + "\n"; diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "},\n"; // main hint if (d.mainHint.IsDefault()) { diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"MainHint\": null,\n"; } else { diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"MainHint\": "; diagsJsonStr += FormatHintJson(deep, d.mainHint, false) + ",\n"; } // other hints diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"OtherHints\": " + FormatHintsJson(deep, d.otherHints); return diagsJsonStr; } std::string DiagnosticJsonFormatter::FormatDiagnosticDiagHelpJson(size_t deep, const DiagHelp& help, bool newLine) { std::string diagsJsonStr = Whitespace(deep * INDENT_WIDTH) + "{\n"; if (!newLine) { diagsJsonStr = "{\n"; } ++deep; // Messsage diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"Message\": \"" + StringConvertor::EscapeToJsonString(help.helpMes) + "\",\n"; // Substitutions diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"Substitutions\": ["; if (help.substitutions.empty()) { diagsJsonStr += "]"; } else { diagsJsonStr += "\n"; ++deep; for (size_t i = 0; i < help.substitutions.size(); ++i) { diagsJsonStr += FormatSubstitutionJson(deep, help.substitutions[i]); diagsJsonStr += (i == help.substitutions.size() - 1) ? "\n" : ",\n"; } --deep; diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "]"; } --deep; diagsJsonStr += "\n" + Whitespace(deep * INDENT_WIDTH) + "}"; return diagsJsonStr; } std::string DiagnosticJsonFormatter::FormatDiagnosticSubDiagJson(size_t deep, const SubDiagnostic& subDiag) { std::string diagsJsonStr = Whitespace(deep * INDENT_WIDTH) + "{\n"; ++deep; // Messsage diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"Message\": \"" + StringConvertor::EscapeToJsonString(subDiag.subDiagMessage) + "\",\n"; // MainHint if (subDiag.mainHint.IsDefault()) { diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"MainHint\": null,\n"; } else { diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"MainHint\": "; diagsJsonStr += FormatHintJson(deep, subDiag.mainHint, false) + ",\n"; } // other hints diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"OtherHints\": " + FormatHintsJson(deep, subDiag.otherHints); // help diagsJsonStr += ",\n"; if (subDiag.help.IsDefault()) { diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"Help\": null"; } else { diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"Help\": "; diagsJsonStr += FormatDiagnosticDiagHelpJson(deep, subDiag.help, false); } --deep; diagsJsonStr += "\n" + Whitespace(deep * INDENT_WIDTH) + "}"; return diagsJsonStr; } std::string DiagnosticJsonFormatter::FormatDiagnosticNotesJson(size_t deep, const std::vector& subDiags) { std::string diagsJsonStr = Whitespace(deep * INDENT_WIDTH) + "\"Notes\": ["; if (subDiags.empty()) { diagsJsonStr += "]"; return diagsJsonStr; } diagsJsonStr += "\n"; for (size_t i = 0; i < subDiags.size(); ++i) { diagsJsonStr += FormatDiagnosticSubDiagJson(deep + 1, subDiags[i]); diagsJsonStr += (i == subDiags.size() - 1) ? "\n" : ",\n"; } diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "]"; return diagsJsonStr; } std::string DiagnosticJsonFormatter::FormatDiagnosticHelpsJson(size_t deep, const std::vector& helps) { std::string diagsJsonStr = Whitespace(deep * INDENT_WIDTH) + "\"Helps\": ["; if (helps.empty()) { diagsJsonStr += "]"; return diagsJsonStr; } diagsJsonStr += "\n"; ++deep; for (size_t i = 0; i < helps.size(); ++i) { diagsJsonStr += FormatDiagnosticDiagHelpJson(deep, helps[i]); diagsJsonStr += (i == helps.size() - 1) ? "\n" : ",\n"; } diagsJsonStr += Whitespace((deep - 1) * INDENT_WIDTH) + "]"; return diagsJsonStr; } std::string DiagnosticJsonFormatter::FormatDiagnosticToJson(const Diagnostic& d, size_t deep) { std::string diagsJsonStr = Whitespace(deep * INDENT_WIDTH) + "{\n"; ++deep; diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"DiagKind\": \""; if (d.isRefactor && !d.isConvertedToRefactor) { if (static_cast(d.rKind) < RE_DIAG_KIND_STR_SIZE) { diagsJsonStr += StringConvertor::EscapeToJsonString(RE_DIAG_KIND_STR[static_cast(d.rKind)]); } } else { if ((static_cast(d.kind) < DIAG_KIND_STR_SIZE)) { diagsJsonStr += StringConvertor::EscapeToJsonString(DIAG_KIND_STR[static_cast(d.kind)]); } } diagsJsonStr += "\",\n" + Whitespace(deep * INDENT_WIDTH) + "\"Severity\": \""; CJC_ASSERT(d.diagSeverity == DiagSeverity::DS_ERROR || d.diagSeverity == DiagSeverity::DS_WARNING); if (d.diagSeverity == DiagSeverity::DS_ERROR) { diagsJsonStr += "error"; } else { diagsJsonStr += "warning"; } diagsJsonStr += "\",\n" + Whitespace(deep * INDENT_WIDTH) + "\"WarnGroup\": \""; if (d.warnGroup == WarnGroup::NONE) { diagsJsonStr += "none"; } else if (d.warnGroup == WarnGroup::UNGROUPED) { diagsJsonStr += "ungrouped"; } else { CJC_ASSERT(static_cast(d.warnGroup) < WARN_GROUP_DESCRS_SIZE); diagsJsonStr += StringConvertor::EscapeToJsonString(warnGroupDescrs[static_cast(d.warnGroup)]); } diagsJsonStr += "\",\n" + Whitespace(deep * INDENT_WIDTH) + "\"DiagCategory\": \""; CJC_ASSERT(CATE_TO_STR.find(d.diagCategory) != CATE_TO_STR.end()); if (CATE_TO_STR.find(d.diagCategory) != CATE_TO_STR.end()) { diagsJsonStr += StringConvertor::EscapeToJsonString(CATE_TO_STR.at(d.diagCategory)) + "\""; } else { diagsJsonStr += "other\""; } // Message, Location, MainHint, OtherHints diagsJsonStr += ",\n"; diagsJsonStr += FormatDiagnosticMainContentJson(deep, d); // Notes diagsJsonStr += ",\n"; diagsJsonStr += FormatDiagnosticNotesJson(deep, d.subDiags); // Helps diagsJsonStr += ",\n"; diagsJsonStr += FormatDiagnosticHelpsJson(deep, d.helps); // end --deep; diagsJsonStr += "\n" + Whitespace(deep * INDENT_WIDTH) + "}"; return diagsJsonStr; } std::string DiagnosticJsonFormatter::AssembleDiagnosticsJsonString(const std::list& diagsJsonBuff) { if (diagsJsonBuff.empty()) { return "\"Diags\":[]"; } std::string diagsJsonStr(Whitespace(INDENT_WIDTH)); diagsJsonStr += "\"Diags\":["; for (auto it = diagsJsonBuff.begin(); it != diagsJsonBuff.end();) { if (diagsJsonStr.back() == '[') { diagsJsonStr += "\n"; } diagsJsonStr += *it; ++it; diagsJsonStr += (it == diagsJsonBuff.end()) ? "\n" : ",\n"; } diagsJsonStr += Whitespace(INDENT_WIDTH) + "]"; return diagsJsonStr; } std::string DiagnosticJsonFormatter::FormatDiagnosticCountToJsonString() { std::string diagsJsonStr(Whitespace(INDENT_WIDTH)); diagsJsonStr += "\"Num\":{\n"; size_t deep{2}; diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"Errors\": " + std::to_string(diag.GetErrorCount()) + ",\n"; diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"PrintedErrors\": " + std::to_string(diag.GetErrorPrintCount()) + ",\n"; diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"Warnings\": " + std::to_string(diag.GetWarningCount()) + ",\n"; diagsJsonStr += Whitespace(deep * INDENT_WIDTH) + "\"PrintedWarnings\": " + std::to_string(diag.GetWarningPrintCount()) + "\n"; diagsJsonStr += Whitespace(INDENT_WIDTH) + "}"; return diagsJsonStr; } std::string DiagnosticJsonFormatter::AssembleDiagnosticJsonString( const std::list& diagsJsonBuff, const std::string& numJsonBuff) { if (diagsJsonBuff.empty()) { return {}; } std::string diagsJsonStr{"{\n"}; diagsJsonStr += AssembleDiagnosticsJsonString(diagsJsonBuff); if (numJsonBuff.empty()) { diagsJsonStr += "\n}\n"; } else { diagsJsonStr += ",\n" + numJsonBuff + "\n}\n"; } return diagsJsonStr; } } // namespace Cangjiecangjie_compiler-1.0.7/src/Basic/Display.cpp000066400000000000000000000111721510705540100207320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/Basic/Display.h" #include #include #include "cangjie/Basic/Print.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/Unicode.h" namespace Cangjie { static const int UNICODE_OUTPUT_WIDTH = 8; static const std::vector> combining = { #define BIND(left, right) {left, right}, #include "cangjie/Basic/DisplayColumn.def" #undef BIND }; static int WideCharWidth(char32_t ucs) { if (ucs == 0x0a) { // newline character:'\n' return 1; } if (ucs == 0x09) { // It is horizontal tab. return HORIZONTAL_TAB_LEN; } // Other control characters. if ((0 <= ucs && ucs <= 0x8) || (0xb <= ucs && ucs <= 0x1f) || ucs == 0x7f) { return UNICODE_OUTPUT_WIDTH; } if (ucs > 0x7f && ucs < 0xa0) { return 0; } auto pred = [](auto& combine, auto& u) { return u.first > combine.second; }; // Binary search in table of non-spacing characters. if (std::binary_search(combining.begin(), combining.end(), std::make_pair(ucs, ucs), pred)) { return 0; } // If we arrive here, ucs is not a combining or C0/C1 control character. return 1 + static_cast((ucs >= 0x1100 && (ucs <= 0x115f || ucs == 0x2329 || ucs == 0x232a || (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) || (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ (ucs >= 0xffe0 && ucs <= 0xffe6) || (ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd)))); } std::basic_string UTF8ToChar32(const std::string& str) { std::wstring_convert, char32_t> conv; return conv.from_bytes(str); } std::string Char32ToUTF8(const char32_t& str) { auto s = std::basic_string{static_cast(str)}; return Char32ToUTF8(s); } std::string Char32ToUTF8(const std::basic_string& str) { std::wstring_convert, char32_t> conv; std::string res; #ifndef CANGJIE_ENABLE_GCOV try { #endif res = conv.to_bytes(str); #ifndef CANGJIE_ENABLE_GCOV } catch (const std::range_error& e) { res = ""; } #endif return res; } std::string ConvertUnicode(const int32_t& str) { return "\\u{" + ToHexString(str, NORMAL_CODEPOINT_LEN) + "}"; } std::string ConvertChar(const int32_t& ch) { if (ch < 0) { return "terminated"; } if (static_cast(ch) > ASCII_BASE) { return Char32ToUTF8(static_cast(ch)); } auto c = static_cast(ch); static std::unordered_map escapeMap = { {'\t', "tab"}, {'\n', "new line"}, }; if (escapeMap.count(c) > 0) { return std::string{escapeMap[c]} + " character"; } // Control character. if (c < 0x20 || c == 0x7f) { return "\\u{" + ToHexString(ch, NORMAL_CODEPOINT_LEN) + "}"; } return Char32ToUTF8(static_cast(ch)); } size_t DisplayWidth(const std::basic_string& pwcs) { int width = 0; for (auto s : pwcs) { width += WideCharWidth(s); } width = width < 0 ? -width : width; // If it only contains escape. return static_cast(width); } // If str contains illegal utf-8 string, UTF8ToChar32 will throw a range_error. size_t DisplayWidth(const std::string& str) noexcept { size_t len; #ifndef CANGJIE_ENABLE_GCOV try { #endif len = DisplayWidth(UTF8ToChar32(str)); #ifndef CANGJIE_ENABLE_GCOV } catch (const std::range_error&) { len = str.size(); } #endif return len; } std::string GetSpaceBeforeTarget(const std::string& content, int column) { CJC_ASSERT(!content.empty() && "source content is empty"); if (content.empty()) { return {}; } CJC_ASSERT(column > 0); column = static_cast(std::min(static_cast(column), content.size() + 1)); auto length = Unicode::DisplayWidth(content.substr(0, static_cast(column - 1))); return std::string(static_cast(static_cast(length)), ' '); } } cangjie_compiler-1.0.7/src/Basic/Position.cpp000066400000000000000000000047411510705540100211350ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Position. */ #include "cangjie/Basic/Position.h" #include #include using namespace Cangjie; using Cangjie::Position; bool Position::operator==(const Position& rhs) const { return std::tie(line, column) == std::tie(rhs.line, rhs.column); } bool Position::operator!=(const Position& rhs) const { return !(*this == rhs); } bool Position::operator<(const Position& rhs) const { return std::tie(line, column) < std::tie(rhs.line, rhs.column); } bool Position::operator<=(const Position& rhs) const { return std::tie(line, column) <= std::tie(rhs.line, rhs.column); } bool Position::operator>(const Position& rhs) const { return !(*this <= rhs); } bool Position::operator>=(const Position& rhs) const { return !(*this < rhs); } Position Position::operator+(const Position& rhs) const { Position ret; ret.fileID = fileID + rhs.fileID; ret.line = line + rhs.line; ret.column = column + rhs.column; ret.isCurFile = isCurFile; return ret; } Position& Position::operator+=(const Position& rhs) { fileID += rhs.fileID; line += rhs.line; column += rhs.column; return *this; } Position Position::operator-(const Position& rhs) const { Position ret; ret.fileID = fileID - rhs.fileID; ret.line = line - rhs.line; ret.column = column - rhs.column; return ret; } Position& Position::operator-=(const Position& rhs) { fileID -= rhs.fileID; line -= rhs.line; column -= rhs.column; return *this; } std::string Position::ToString() const { std::stringstream ss; ss << "(" << fileID << ", " << line << ", " << column << ")"; return ss.str(); } Position Position::operator+(const size_t w) const { auto ret = Position{fileID, line, column + static_cast(w)}; ret.isCurFile = isCurFile; return ret; } Position Position::operator-(const size_t w) const { auto ret = Position{fileID, line, column - static_cast(w)}; ret.isCurFile = isCurFile; return ret; } bool Position::IsZero() const { return line == 0 && column == 0; } void Position::Mark(PositionStatus newStatus) { status = newStatus; } PositionStatus Position::GetStatus() const { return status; } cangjie_compiler-1.0.7/src/Basic/Print.cpp000066400000000000000000000052011510705540100204150ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/Basic/Print.h" #ifdef _WIN32 #include #endif namespace Cangjie { #ifdef _WIN32 // Enables OS earlier than Windos10 1511 to compile normally #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 #endif const int WINDOWS_10_VERSION_1511_BUILD_NUMBER = 10586; const int WINDOWS_10 = 10; ColorSingleton::ColorSingleton() { DWORD stdoutMode; DWORD stderrMode; GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &stdoutMode); GetConsoleMode(GetStdHandle(STD_ERROR_HANDLE), &stderrMode); // store initial console mode initialStdoutMode = stdoutMode; initialStderrMode = stderrMode; // get current Windows os version auto osVersion = Utils::GetOSVersion(); if (osVersion.dwMajorVersion >= WINDOWS_10 && osVersion.dwBuildNumber >= WINDOWS_10_VERSION_1511_BUILD_NUMBER) { // current Windows os version is or newer than Windows10 (version 1511) stdoutMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; stderrMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), stdoutMode); SetConsoleMode(GetStdHandle(STD_ERROR_HANDLE), stderrMode); ANSI_COLOR_RESET = "\x1b[0m"; ANSI_COLOR_BRIGHT = "\x1b[1m"; ANSI_COLOR_BLACK = "\x1b[30m"; ANSI_COLOR_RED = "\x1b[31m"; ANSI_COLOR_GREEN = "\x1b[32m"; ANSI_COLOR_YELLOW = "\x1b[33m"; ANSI_COLOR_BLUE = "\x1b[34m"; ANSI_COLOR_MAGENTA = "\x1b[35m"; ANSI_COLOR_CYAN = "\x1b[36m"; ANSI_COLOR_WHITE = "\x1b[37m"; ANSI_COLOR_WHITE_BACKGROUND_BLACK_FOREGROUND = "\x1b[30;47m"; } }; ColorSingleton::~ColorSingleton() { // restore to the initial console mode SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), initialStdoutMode); SetConsoleMode(GetStdHandle(STD_ERROR_HANDLE), initialStderrMode); } #else ColorSingleton::ColorSingleton() { ANSI_COLOR_RESET = "\x1b[0m"; ANSI_COLOR_BRIGHT = "\x1b[1m"; ANSI_COLOR_BLACK = "\x1b[30m"; ANSI_COLOR_RED = "\x1b[31m"; ANSI_COLOR_GREEN = "\x1b[32m"; ANSI_COLOR_YELLOW = "\x1b[33m"; ANSI_COLOR_BLUE = "\x1b[34m"; ANSI_COLOR_MAGENTA = "\x1b[35m"; ANSI_COLOR_CYAN = "\x1b[36m"; ANSI_COLOR_WHITE = "\x1b[37m"; ANSI_COLOR_WHITE_BACKGROUND_BLACK_FOREGROUND = "\x1b[30;47m"; }; ColorSingleton::~ColorSingleton(){}; #endif } // namespace Cangjiecangjie_compiler-1.0.7/src/Basic/SourceManager.cpp000066400000000000000000000154351510705540100220660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the SourceManager related classes. */ #include "cangjie/Basic/SourceManager.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/FileUtil.h" using namespace Cangjie; size_t Source::PosToOffset(const Position& pos) const { if (pos.line > static_cast(lineOffsets.size())) { return buffer.length(); } if (pos.line < 1 || pos.column < 1) { return 0; } size_t index = static_cast(pos.line) - 1; auto pStart = buffer.data() + lineOffsets[index]; auto pEnd = buffer.data() + buffer.length(); size_t columnOffset = 0; while (Utils::GetLineTerminatorLength(pStart + columnOffset, pEnd) == 0 && columnOffset < (static_cast(pos.column) - 1) && pStart + columnOffset < pEnd) { columnOffset++; } if (columnOffset < (static_cast(pos.column) - 1) && pStart + columnOffset < pEnd) { // there's line terminator before `pos.column` columnOffset += Utils::GetLineTerminatorLength(pStart + columnOffset, pEnd); } return std::min(lineOffsets[index] + columnOffset, buffer.length()); } Source::Source(unsigned int fileID, std::string path, std::string buffer, uint64_t fileHash, const std::optional& packageName) : fileID(fileID), path(std::move(path)), buffer(std::move(buffer)), fileHash(fileHash), packageName(packageName) { if (this->buffer.empty()) { return; } // build lineOffsets auto pStart = this->buffer.data(); size_t length = this->buffer.length(); auto pEnd = this->buffer.data() + length; for (auto ptr = pStart; ptr < pEnd;) { while (Utils::GetLineTerminatorLength(ptr, pEnd) == 0 && ptr < pEnd) { ptr++; } if (ptr >= pEnd) { break; } ptr += Utils::GetLineTerminatorLength(ptr, pEnd); lineOffsets.emplace_back(ptr - pStart); } } void SourceManager::SaveSourceFile( unsigned int fileID, std::string normalizedPath, std::string buffer, uint64_t fileHash, std::optional packageName) { sources.emplace_back(fileID, normalizedPath, buffer, fileHash, packageName); filePathToFileIDMap.emplace(normalizedPath, fileID); } void SourceManager::ReserveCommonPartSources(std::vector files) { for (size_t i = 0; i < files.size(); i++) { auto file = files.at(i); uint64_t fileHash = 0; SaveSourceFile(static_cast(i + 1), files.at(i), "", fileHash); filePathToFileIDMap.emplace(file, i + 1); } } unsigned int SourceManager::AddSource( const std::string& path, const std::string& buffer, std::optional packageName) { // path canonicalize std::string normalizePath = FileUtil::Normalize(path); // Change fileHash from content hash to path hash. uint64_t fileHash = Utils::GetHash(normalizePath); auto existed = filePathToFileIDMap.find(normalizePath); if (existed != filePathToFileIDMap.end()) { sources[static_cast(existed->second)] = Source{static_cast(existed->second), normalizePath, buffer, fileHash, packageName}; return static_cast(existed->second); } else { auto fileID = static_cast(sources.size()); SaveSourceFile(fileID, normalizePath, buffer, fileHash, packageName); return fileID; } } unsigned int SourceManager::AppendSource(const std::string& path, const std::string& buffer) { // path canonicalize std::string normalizePath = FileUtil::Normalize(path); uint64_t fileHash = Utils::GetHash(normalizePath); auto existed = filePathToFileIDMap.find(normalizePath); if (existed != filePathToFileIDMap.end()) { auto newBuffer = sources[static_cast(existed->second)].buffer + buffer; sources[static_cast(existed->second)] = Source{static_cast(existed->second), normalizePath, newBuffer, fileHash}; return static_cast(existed->second); } else { auto fileID = static_cast(sources.size()); sources.emplace_back(fileID, normalizePath, buffer, fileHash); filePathToFileIDMap.emplace(normalizePath, fileID); return fileID; } } bool SourceManager::IsSourceFileExist(const unsigned int id) { // Check whether the *.macrocall exists or not. if (id < sources.size()) { auto path = sources[id].path; if (!path.empty() && FileUtil::GetFileExtension(path) != "cj") { return FileUtil::FileExist(path); } } return true; } int SourceManager::GetLineEnd(const Position& pos) { if (pos.fileID >= sources.size()) { return 0; } auto buffer = sources[pos.fileID].buffer; auto sourceSplited = Utils::SplitLines(buffer); if (pos.line > static_cast(sourceSplited.size())) { return 0; } CJC_ASSERT(pos.line > 0); if (pos.line <= 0) { return 0; } return static_cast(sourceSplited[static_cast(pos.line - 1)].size()); } std::string SourceManager::GetContentBetween( const Position& begin, const Position& end, const std::string& importGenericContent) const { return GetContentBetween(begin.fileID, begin, end, importGenericContent); } std::string SourceManager::GetContentBetween( unsigned int fileID, const Position& begin, const Position& end, const std::string& importGenericContent) const { if (fileID == 0 || begin <= INVALID_POSITION || end <= INVALID_POSITION || end < begin) { return ""; } CJC_ASSERT(INVALID_POSITION < begin && begin <= end); auto& sourceWithFileID = fileID >= sources.size() ? sources[0] : sources[fileID]; const auto& source = sourceWithFileID.buffer.empty() && !importGenericContent.empty() ? Source(sourceWithFileID.fileID, sourceWithFileID.path, importGenericContent) : sourceWithFileID; const auto& buffer = source.buffer; if (buffer.empty()) { return ""; } auto startOffset = source.PosToOffset(begin); auto endOffset = source.PosToOffset(end); return buffer.substr(startOffset, endOffset - startOffset); } void SourceManager::AddComments(const TokenVecMap& commentsMap) { for (const auto& it : commentsMap) { CJC_ASSERT(it.first < sources.size()); auto& source = sources[it.first]; for (auto tok : it.second) { (void)source.offsetCommentsMap.insert_or_assign(source.PosToOffset(tok.Begin()), tok); } } } namespace Cangjie { const std::string SourceManager::testPkgSuffix = "$test"; } cangjie_compiler-1.0.7/src/Basic/StringConvertor.cpp000066400000000000000000000372761510705540100225120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the StringConvertor. */ #include "cangjie/Basic/StringConvertor.h" #include "cangjie/Lex/Lexer.h" #ifdef _WIN32 #include #include #endif using namespace Cangjie; namespace { #ifdef _WIN32 std::optional CodePageToUTF16(unsigned codepage, const std::string& original) { std::wstring utf16; if (!original.empty()) { int len = MultiByteToWideChar( codepage, MB_ERR_INVALID_CHARS, const_cast(original.data()), original.size(), nullptr, 0); if (len <= 0) { return {}; } utf16.reserve(len + 1); utf16.resize(len); len = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, const_cast(original.data()), original.size(), const_cast(utf16.data()), utf16.size()); if (len <= 0) { return {}; } } return utf16; } std::optional UTF16ToCodePage(unsigned codepage, std::wstring& utf16) { std::string converted; if (!utf16.empty()) { int len = WideCharToMultiByte( codepage, 0, const_cast(utf16.data()), utf16.size(), nullptr, 0, nullptr, nullptr); if (len <= 0) { return {}; } converted.reserve(len + 1); converted.resize(len); len = WideCharToMultiByte(codepage, 0, const_cast(utf16.data()), utf16.size(), const_cast(converted.data()), converted.size(), nullptr, nullptr); if (len <= 0) { return {}; } } return converted; } /* * Returns the number of bits before the first 0 bit in the first eight bits of the first byte. * The number is also the number of bytes used by the character. * 110X_XXXX 10XX_XXXX * 1110_XXXX 10XX_XXXX 10XX_XXXX * 1111_0XXX 10XX_XXXX 10XX_XXXX 10XX_XXXX * 1111_10XX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX * 1111_110X 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX */ size_t PreNum(unsigned char byte) { unsigned char mask = 0x80; size_t num = 0; for (int i = 0; i < 8; i++) { // A byte has 8 bits. if ((byte & mask) == mask) { mask = mask >> 1; num++; } else { break; } } return num; } bool IsGBK(const std::string& data) { size_t i = 0; while (i < data.size()) { unsigned char dataI = static_cast(data[i]); if (dataI <= 0x7f) { // The code must be less than or equal to 127. The code contains only one byte and is compatible with ASCII. i++; continue; } // If the value is greater than 127, double-byte encoding is used. if (i + 1 >= data.size()) { // Not enough bytes return false; } unsigned char dataII = static_cast(data[i + 1]); if (dataI >= 0x81 && dataI <= 0xfe && dataII >= 0x40 && dataII <= 0xfe && dataII != 0xf7) { i += 2; // A character contains 2 bytes. continue; } else { return false; } } return true; } bool IsUTF8(const std::string& data) { size_t num = 0; size_t i = 0; const size_t MIN_PRENUM = 3; while (i < data.size()) { if ((data[i] & 0x80) == 0x00) { // 0XXX_XXXX for ASCII i++; continue; } size_t num = PreNum(data[i]); if (num < MIN_PRENUM) { // In UTF-8 encoding, multiple bytes must contain at least 2 bytes, // the min number of bytes used by the character is 3. // In other cases, the utf-8 is not used. return false; } i++; for (size_t j = 1; j < num; j++) { // Check whether the following num - 1 byte is 10 open. if (i >= data.size()) { // Not enough bytes return false; } if ((data[i] & 0xc0) != 0x80) { return false; } i++; } } return true; } #endif } // namespace std::string StringConvertor::GetUnicodeAndToUTF8( std::string::const_iterator& it, const std::string::const_iterator& strEnd) { static std::unordered_map hexadecimalMap = {{'0', 0}, {'1', 1}, {'2', 2}, {'3', 3}, {'4', 4}, {'5', 5}, {'6', 6}, {'7', 7}, {'8', 8}, {'9', 9}, {'A', 10}, {'B', 11}, {'C', 12}, {'D', 13}, {'E', 14}, {'F', 15}, {'a', 10}, {'b', 11}, {'c', 12}, {'d', 13}, {'e', 14}, {'f', 15}}; auto temp = it; uint32_t unicodePoint = 0; while (temp != strEnd && isxdigit(*temp)) { // hexadecimal to decimal need multiplying 16 unicodePoint = (unicodePoint * 16) + hexadecimalMap[*temp]; ++temp; } it = temp; return CodepointToUTF8(unicodePoint); } /** * Normalize function is used to deal with escape characters and convert raw string into string which can be processed * in codegen directly. */ std::string StringConvertor::Normalize(const std::string& str, bool isRawString) { std::unordered_map escapeList{{'0', '\0'}, {'\\', '\\'}, {'b', '\b'}, {'f', '\f'}, {'n', '\n'}, {'r', '\r'}, {'t', '\t'}, {'v', '\v'}, {'\'', '\''}, {'\"', '\"'}, {'\?', '\?'}}; std::string str1{}; str1.reserve(str.size()); for (auto it = str.begin(); it != str.cend(); ++it) { if (*it == '\\' && !isRawString) { // it + 1 mast 'u' and it + 2 mast '{' if ((it + 1) != str.cend() && (it + 2) != str.cend() && *(it + 1) == 'u' && *(it + 2) == '{') { // "\u{" need add 3 it += 3; str1 += GetUnicodeAndToUTF8(it, str.cend()); } else if (escapeList.find(*(it + 1)) != escapeList.end()) { ++it; str1 += escapeList.find(*(it))->second; } if (it == str.cend()) { break; } } else if (*it == '\r') { // normalize \r\n to \n in Multiline String if ((it + 1) != str.cend() && *(it + 1) == '\n') { ++it; str1 += *it; } else { str1 += "\n"; } } else { str1 += *it; } } return str1; } std::string StringConvertor::CodepointToUTF8(const uint32_t pointValue) { std::string result; // max size is 6 result.reserve(6); if (pointValue <= UTF8_1_MAX) { result += static_cast(static_cast(pointValue)); } else if (pointValue <= UTF8_2_MAX) { result += static_cast(static_cast(((pointValue >> LEFT_SHIFT_6) & LOW_5_BIT_MASK) | BYTE_2_FLAG)); result += static_cast(static_cast((pointValue & LOW_6_BIT_MASK) | BYTE_X_FLAG)); } else if (pointValue <= UTF8_3_MAX) { result += static_cast(static_cast(((pointValue >> LEFT_SHIFT_12) & LOW_4_BIT_MASK) | BYTE_3_FLAG)); result += static_cast(static_cast(((pointValue >> LEFT_SHIFT_6) & LOW_6_BIT_MASK) | BYTE_X_FLAG)); result += static_cast(static_cast((pointValue & LOW_6_BIT_MASK) | BYTE_X_FLAG)); } else if (pointValue <= UTF8_4_MAX) { result += static_cast(static_cast(((pointValue >> LEFT_SHIFT_18) & LOW_3_BIT_MASK) | BYTE_4_FLAG)); result += static_cast(static_cast(((pointValue >> LEFT_SHIFT_12) & LOW_6_BIT_MASK) | BYTE_X_FLAG)); result += static_cast(static_cast(((pointValue >> LEFT_SHIFT_6) & LOW_6_BIT_MASK) | BYTE_X_FLAG)); result += static_cast(static_cast((pointValue & LOW_6_BIT_MASK) | BYTE_X_FLAG)); } else if (pointValue <= UTF8_5_MAX) { result += static_cast(static_cast(((pointValue >> LEFT_SHIFT_24) & LOW_2_BIT_MASK) | BYTE_5_FLAG)); result += static_cast(static_cast(((pointValue >> LEFT_SHIFT_18) & LOW_6_BIT_MASK) | BYTE_X_FLAG)); result += static_cast(static_cast(((pointValue >> LEFT_SHIFT_12) & LOW_6_BIT_MASK) | BYTE_X_FLAG)); result += static_cast(static_cast(((pointValue >> LEFT_SHIFT_6) & LOW_6_BIT_MASK) | BYTE_X_FLAG)); result += static_cast(static_cast((pointValue & LOW_6_BIT_MASK) | BYTE_X_FLAG)); } else if (pointValue <= UTF8_6_MAX) { result += static_cast(static_cast(((pointValue >> LEFT_SHIFT_30) & LOW_1_BIT_MASK) | BYTE_6_FLAG)); result += static_cast(static_cast(((pointValue >> LEFT_SHIFT_24) & LOW_6_BIT_MASK) | BYTE_X_FLAG)); result += static_cast(static_cast(((pointValue >> LEFT_SHIFT_18) & LOW_6_BIT_MASK) | BYTE_X_FLAG)); result += static_cast(static_cast(((pointValue >> LEFT_SHIFT_12) & LOW_6_BIT_MASK) | BYTE_X_FLAG)); result += static_cast(static_cast(((pointValue >> LEFT_SHIFT_6) & LOW_6_BIT_MASK) | BYTE_X_FLAG)); result += static_cast(static_cast((pointValue & LOW_6_BIT_MASK) | BYTE_X_FLAG)); } return result; } namespace { std::string EscapeString(const std::string& str, const std::unordered_map& escapeList) { std::string newStr{}; newStr.reserve(str.size() + 1); auto got = escapeList.end(); for (char it : str) { got = escapeList.find(it); if (got != escapeList.end()) { newStr += '\\'; newStr += got->second; } else { newStr += it; } } return newStr; } } // namespace end std::string StringConvertor::Recover(const std::string& str) { static const std::unordered_map ESCAPE_LIST{{'\b', 'b'}, {'\f', 'f'}, {'\n', 'n'}, {'\r', 'r'}, {'\t', 't'}, {'\v', 'v'}, {'\\', '\\'}, {'\"', '"'}, {'\'', '\''}, {'\0', '0'}}; return EscapeString(str, ESCAPE_LIST); } std::string StringConvertor::EscapeToJsonString(const std::string& str) { static const std::unordered_map ESCAPE_LIST{ {'\b', 'b'}, {'\f', 'f'}, {'\n', 'n'}, {'\r', 'r'}, {'\t', 't'}, {'\\', '\\'}, {'\"', '"'}}; return EscapeString(str, ESCAPE_LIST); } std::vector StringConvertor::UTF8ToCodepoint(const std::string& str) { std::vector ret; size_t idx = 0; while (idx < str.size()) { uint32_t ch = static_cast(static_cast(str[idx])); if (ch >= BYTE_2_FLAG) { uint32_t byte1 = ch; uint32_t byte2 = static_cast(static_cast(str[idx + 1])); if (ch >= BYTE_6_FLAG) { uint32_t byte3 = static_cast(static_cast(str[idx + BYTE_3_INDEX])); uint32_t byte4 = static_cast(static_cast(str[idx + BYTE_4_INDEX])); uint32_t byte5 = static_cast(static_cast(str[idx + BYTE_5_INDEX])); uint32_t byte6 = static_cast(static_cast(str[idx + BYTE_6_INDEX])); ch = uint32_t(((byte1 & LOW_1_BIT_MASK) << LEFT_SHIFT_30) | ((byte2 & LOW_6_BIT_MASK) << LEFT_SHIFT_24) | ((byte3 & LOW_6_BIT_MASK) << LEFT_SHIFT_18) | ((byte4 & LOW_6_BIT_MASK) << LEFT_SHIFT_12) | ((byte5 & LOW_6_BIT_MASK) << LEFT_SHIFT_6) | (byte6 & LOW_6_BIT_MASK)); idx += BYTE_6_STEP; } else if (ch >= BYTE_5_FLAG) { uint32_t byte3 = static_cast(static_cast(str[idx + BYTE_3_INDEX])); uint32_t byte4 = static_cast(static_cast(str[idx + BYTE_4_INDEX])); uint32_t byte5 = static_cast(static_cast(str[idx + BYTE_5_INDEX])); ch = uint32_t(((byte1 & LOW_2_BIT_MASK) << LEFT_SHIFT_24) | ((byte2 & LOW_6_BIT_MASK) << LEFT_SHIFT_18) | ((byte3 & LOW_6_BIT_MASK) << LEFT_SHIFT_12) | ((byte4 & LOW_6_BIT_MASK) << LEFT_SHIFT_6) | (byte5 & LOW_6_BIT_MASK)); idx += BYTE_5_STEP; } else if (ch >= BYTE_4_FLAG) { uint32_t byte3 = static_cast(static_cast(str[idx + BYTE_3_INDEX])); uint32_t byte4 = static_cast(static_cast(str[idx + BYTE_4_INDEX])); ch = uint32_t(((byte1 & LOW_3_BIT_MASK) << LEFT_SHIFT_18) | ((byte2 & LOW_6_BIT_MASK) << LEFT_SHIFT_12) | ((byte3 & LOW_6_BIT_MASK) << LEFT_SHIFT_6) | (byte4 & LOW_6_BIT_MASK)); idx += BYTE_4_STEP; } else if (ch >= BYTE_3_FLAG) { uint32_t byte3 = static_cast(static_cast(str[idx + BYTE_3_INDEX])); ch = uint32_t(((byte1 & LOW_4_BIT_MASK) << LEFT_SHIFT_12) | ((byte2 & LOW_6_BIT_MASK) << LEFT_SHIFT_6) | (byte3 & LOW_6_BIT_MASK)); idx += BYTE_3_STEP; } else { ch = uint32_t(((byte1 & LOW_5_BIT_MASK) << LEFT_SHIFT_6) | (byte2 & LOW_6_BIT_MASK)); idx += BYTE_2_STEP; } } else { idx += BYTE_1_STEP; } ret.push_back(ch); } return ret; } #ifdef _WIN32 StringConvertor::StrEncoding StringConvertor::GetStringEncoding(const std::string& data) { // IsGBK() is implemented by whether the double bytes fall within the encoding range of gbk, // while each byte in the UTF-8 encoding format falls within the encoding range of gbk. // Therefore, it is meaningful to call IsUTF8() first to determine whether the encoding is not UTF-8, and then to // call IsGBK(). if (IsUTF8(data)) { return StrEncoding::UTF8; } else if (IsGBK(data)) { return StrEncoding::GBK; } else { return StrEncoding::UNKNOWN; } } std::optional StringConvertor::GBKToUTF8(const std::string& gbkStr) { std::optional utf16 = CodePageToUTF16(CP_ACP, gbkStr); if (!utf16.has_value()) { return {}; } std::optional utf8 = UTF16ToCodePage(CP_UTF8, utf16.value()); return utf8; } std::optional StringConvertor::UTF8ToGBK(const std::string& utf8Str) { std::optional utf16 = CodePageToUTF16(CP_UTF8, utf8Str); if (!utf16.has_value()) { return {}; } // Code Page 936 is the identifier for the GBK encoding in the Windows operating system. const static unsigned gbkCode = 936; std::optional gbk = UTF16ToCodePage(gbkCode, utf16.value()); return gbk; } std::optional StringConvertor::NormalizeStringToUTF8(const std::string& data) { StrEncoding encoding = GetStringEncoding(data); if (encoding == UTF8) { return data; } else if (encoding == GBK) { return GBKToUTF8(data); } else { return {}; } } std::optional StringConvertor::NormalizeStringToGBK(const std::string& data) { StrEncoding encoding = GetStringEncoding(data); if (encoding == UTF8) { return UTF8ToGBK(data); } else if (encoding == GBK) { return data; } else { return {}; } } std::optional StringConvertor::StringToWString(const std::string& data) { StrEncoding encoding = GetStringEncoding(data); if (encoding == UTF8) { return CodePageToUTF16(CP_UTF8, data); } else if (encoding == GBK) { return CodePageToUTF16(CP_ACP, data); } else { return {}; } } #endif cangjie_compiler-1.0.7/src/Basic/Utils.cpp000066400000000000000000000111441510705540100204240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements some utility functions. */ #include "cangjie/Basic/Utils.h" #include "cangjie/Lex/Token.h" #ifdef _WIN32 #include #endif #include "cangjie/Utils/CheckUtils.h" using namespace Cangjie; namespace Cangjie { uint64_t Utils::GetHash(const std::string& content) { std::size_t ret = std::hash{}(content); return static_cast(ret); } std::vector Utils::SplitLines(const std::string& str) { std::vector res; size_t length = str.size(); if (length == 0) { return res; } size_t lineTerminatorPos; size_t windowsTerminatorLength = std::string("\r\n").size(); for (size_t curIndex = 0, newLineStartPos = 0; curIndex < length;) { while (curIndex < length && !(str[curIndex] == '\r' && curIndex + 1 < length && str[curIndex + 1] == '\n') && str[curIndex] != '\n') { curIndex++; } lineTerminatorPos = curIndex; if (curIndex < length) { if (str[curIndex] == '\r' && curIndex + 1 < length && str[curIndex + 1] == '\n') { curIndex += windowsTerminatorLength; } else { curIndex++; } } res.emplace_back(str.substr(newLineStartPos, lineTerminatorPos - newLineStartPos)); newLineStartPos = curIndex; } if (str[length - 1] == '\n') { res.emplace_back(""); } return res; } std::vector Utils::SplitString(const std::string& str, const std::string& delimiter) { std::vector res; if (!str.empty()) { size_t pos = 0; size_t foundPos = str.find(delimiter); while (foundPos != std::string::npos) { res.emplace_back(str.substr(pos, foundPos - pos)); pos = foundPos + 1; foundPos = str.find(delimiter, pos); } if (pos < str.size()) { res.emplace_back(str.substr(pos)); } else if (pos == str.size()) { res.emplace_back(""); } } return res; } std::vector Utils::SplitQualifiedName(const std::string& qualifiedName, bool splitDc) { std::vector names; std::string::size_type idx = 0; std::string_view dc = TOKENS[static_cast(TokenKind::DOUBLE_COLON)]; if (auto next = qualifiedName.find(dc); splitDc && next != std::string::npos) { names.push_back(qualifiedName.substr(0, next)); idx = next + dc.size(); } while (idx < qualifiedName.size()) { if (qualifiedName[idx] == '.') { ++idx; } else if (qualifiedName[idx] == '`') { auto next = qualifiedName.find('`', idx + 1); names.emplace_back(qualifiedName.substr(idx + 1, next - (idx + 1))); idx = next == std::string::npos ? next : (next + 1); } else { auto next = qualifiedName.find('.', idx + 1); names.emplace_back(qualifiedName.substr(idx, next - idx)); idx = next; } } CJC_ASSERT(!names.empty()); return names; } std::string Utils::JoinStrings(const std::vector& strs, const std::string& delimiter) { std::string s; for (unsigned int i = 0; i < strs.size(); ++i) { s += strs[i]; if (i != strs.size() - 1) { s += delimiter; } } return s; } uint8_t Utils::GetLineTerminatorLength(const char* pStr, const char* pEnd) { if (pStr == nullptr || pEnd == nullptr) { return 0; } if (*pStr == '\n') { return LINUX_LINE_TERMINATOR_LENGTH; } else if (*pStr == '\r' && pStr + 1 <= pEnd && *(pStr + 1) == '\n') { return WINDOWS_LINE_TERMINATOR_LENGTH; } else { return 0; } } #ifdef _WIN32 typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); Utils::WindowsOsVersion Utils::GetOSVersion() { HMODULE hMod = GetModuleHandleA("ntdll.dll"); RTL_OSVERSIONINFOW osVersionInfo = {0}; if (hMod) { auto RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(hMod, "RtlGetVersion"); if (RtlGetVersion != nullptr) { osVersionInfo.dwOSVersionInfoSize = sizeof(osVersionInfo); RtlGetVersion(&osVersionInfo); } } return {osVersionInfo.dwMajorVersion, osVersionInfo.dwMinorVersion, osVersionInfo.dwBuildNumber, osVersionInfo.dwPlatformId}; } #endif } // namespace Cangjie cangjie_compiler-1.0.7/src/Basic/Version.cpp000066400000000000000000000015021510705540100207460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements compiler's version apis. */ #include "cangjie/Basic/Print.h" #include "cangjie/Basic/Version.h" namespace Cangjie { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND const std::string CANGJIE_VERSION = CJ_SDK_VERSION; #ifndef VERSION_TAIL const std::string CANGJIE_COMPILER_VERSION = "Cangjie Compiler: " + CANGJIE_VERSION; #else const std::string CANGJIE_COMPILER_VERSION = "Cangjie Compiler: " + CANGJIE_VERSION + VERSION_TAIL; #endif #endif void PrintVersion() { Cangjie::Println(CANGJIE_COMPILER_VERSION); } } // namespace Cangjie cangjie_compiler-1.0.7/src/CHIR/000077500000000000000000000000001510705540100163235ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/000077500000000000000000000000001510705540100175025ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/AST2CHIR.cpp000066400000000000000000000531521510705540100213730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/AST2CHIR.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/Match.h" #include "cangjie/CHIR/AST2CHIR/AST2CHIRChecker.h" #include "cangjie/CHIR/AST2CHIR/CollectLocalConstDecl/CollectLocalConstDecl.h" #include "cangjie/CHIR/AST2CHIR/GlobalDeclAnalysis.h" #include "cangjie/CHIR/AST2CHIR/GlobalVarInitializer.h" #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/CHIR/Visitor/Visitor.h" #include "cangjie/CHIR/Serializer/CHIRDeserializer.h" #include "cangjie/Utils/ConstantsUtils.h" #include "cangjie/Utils/ParallelUtil.h" namespace Cangjie::CHIR { bool AST2CHIR::HasFailed() const { return this->failure; } void AST2CHIR::RegisterAllSources() { auto& sources = sourceManager.GetSources(); for (auto& source : sources) { auto filePath = source.path; auto absPath = FileUtil::GetAbsPath(source.path); if (absPath.has_value()) { filePath = absPath.value(); } else { filePath = FileUtil::Normalize(filePath); } builder.GetChirContext().RegisterSourceFileName(source.fileID, filePath); } } void AST2CHIR::CollectStaticInitFuncInfo() { auto collectStaticInitFuncInfo = [this](const std::vector>& funcs) { for (auto& func : funcs) { if (!IsStaticInitializer(*func)) { continue; } // Get all the static member variables which are inited by this `static.init` func std::vector> staticVars; Ptr staticInitVar = nullptr; auto parentDecl = StaticCast(func->outerDecl); for (auto memberVar : GetStaticMemberVars(*parentDecl)) { // Skip the generated class_static_init variable if (memberVar->identifier == STATIC_INIT_VAR) { staticInitVar = memberVar; continue; } // Skip the variable which has default init value if (memberVar->initializer != nullptr) { continue; } staticVars.emplace_back(memberVar); varsInitedByStaticInitFunc.emplace(memberVar); } CJC_NULLPTR_CHECK(func->outerDecl); staticInitFuncInfoMap.emplace( func->outerDecl, StaticInitInfo(StaticCast(func), staticInitVar, staticVars)); } }; collectStaticInitFuncInfo(globalAndMemberFuncs); } void AST2CHIR::CollectFuncsAndVars() { auto collectVars = [this](const std::vector>& candidateVars) { for (auto& var : candidateVars) { // Skip the static variables which are inited by the `static init func` if (var->TestAttr(AST::Attribute::STATIC) && var->identifier != STATIC_INIT_VAR) { bool fromCommon = var->TestAttr(AST::Attribute::FROM_COMMON_PART); bool initedInStaticInit = varsInitedByStaticInitFunc.find(var) != varsInitedByStaticInitFunc.end(); if (!fromCommon && initedInStaticInit) { continue; } } funcsAndVars.AddElement(var); fileAndVarMap[var->curFile].emplace_back(var); } }; collectVars(globalAndStaticVars); auto collectFuncs = [this](const std::vector>& candidateFuncs) { for (auto& func : candidateFuncs) { if (func->TestAttr(AST::Attribute::MAIN_ENTRY) || func->identifier == MAIN_INVOKE) { continue; } funcsAndVars.AddElement(func); } }; collectFuncs(globalAndMemberFuncs); } void AST2CHIR::CollectLocalConstFuncAndVars(const AST::Package& pkg) { CollectLocalConstDecl collector; collector.Collect(globalAndStaticVars, true); collector.Collect(globalAndMemberFuncs, true); /* some const decl in member var can't be collected by visiting `init` func class CA { var b = { => const a = 1 } } member var `b` has default value, its default value doesn't show in CA.init func */ std::vector> instanceMemberVars; for (auto& decl : nominalDecls) { for (auto member : decl->GetMemberDeclPtrs()) { if (member->astKind == AST::ASTKind::VAR_DECL && !member->TestAttr(AST::Attribute::STATIC)) { instanceMemberVars.emplace_back(member); } } } collector.Collect(instanceMemberVars, true); /* some local func decl can't be collected by visiting its outer decl func foo() { const func goo() {} goo() } we can only collect goo's generic declare by visiting func foo, goo's instantiated declare can't be seen goo's instantiated declare can only be seen in `pkg.genericInstantiatedDecls` */ std::vector> instLocalconstFuncs; for (auto& decl : pkg.genericInstantiatedDecls) { // we only collect local const func decl if (decl->astKind == AST::ASTKind::FUNC_DECL && decl->IsConst() && IsLocalFunc(*StaticCast(decl.get()))) { instLocalconstFuncs.emplace_back(StaticCast(decl.get())); } } collector.Collect(instLocalconstFuncs, false); for (const auto decl : collector.GetLocalConstFuncDecls()) { localConstFuncs.AddElement(decl); } for (const auto decl : collector.GetLocalConstVarDecls()) { localConstVars.AddElement(decl); } } std::pair AST2CHIR::SortGlobalVarDecl(const AST::Package& pkg) { Utils::ProfileRecorder recorder("AST to CHIR Translation", "SortGlobalVarDecl"); CollectStaticInitFuncInfo(); CollectFuncsAndVars(); CollectLocalConstFuncAndVars(pkg); ElementList> nodesWithDeps; for (auto element : funcsAndVars.stableOrderValue) { // We will skip those non-recompiled decls in incremental compilation cause they will have no body to be // analyze, but the desugar generated `$init` should be kept specially cause it has body if (kind == IncreKind::INCR && !element->toBeCompiled && element->identifier != STATIC_INIT_VAR) { continue; } nodesWithDeps.AddElement(element); } auto analysis = GlobalDeclAnalysis( diag, gim, kind, funcsAndVars, localConstVars, staticInitFuncInfoMap, outputCHIR, mergingPlatform); auto initOrder = analysis.Run(nodesWithDeps, fileAndVarMap, cachedInfo); // Speically, now we want to flattern the local const VarWithPattern decl which will be useful in translation later. // Also we need to set these local const vars with `Recompile` flag for the sake of incremental compilation. ElementList> flatternedLocalConstVars; for (auto& element : localConstVars.stableOrderValue) { if (element->astKind == AST::ASTKind::VAR_DECL) { (const_cast(element.get()))->toBeCompiled = true; flatternedLocalConstVars.AddElement(element); } else if (auto varWithPattern = DynamicCast(element)) { (const_cast(varWithPattern))->toBeCompiled = true; auto allPatterns = FlattenVarWithPatternDecl(*varWithPattern); for (auto pattern : allPatterns) { if (pattern->astKind != AST::ASTKind::VAR_PATTERN) { continue; } auto varPattern = StaticCast(pattern); varPattern->varDecl->toBeCompiled = true; flatternedLocalConstVars.AddElement(varPattern->varDecl); } } } localConstVars = std::move(flatternedLocalConstVars); // If we find circular dependency, return false bool result = diag.GetErrorCount() == 0; return std::make_pair(initOrder, result); } void AST2CHIR::CreateGlobalVarSignature(const std::vector>& decls, bool isLocalConst) { for (auto& decl : decls) { CJC_ASSERT(decl->astKind == AST::ASTKind::VAR_DECL || decl->astKind == AST::ASTKind::VAR_WITH_PATTERN_DECL); if (decl->astKind == AST::ASTKind::VAR_DECL) { CreateAndCacheGlobalVar(*StaticCast(decl), isLocalConst); } else if (decl->astKind == AST::ASTKind::VAR_WITH_PATTERN_DECL) { FlatternPattern(*(StaticCast(decl)->irrefutablePattern), isLocalConst); } } } void AST2CHIR::FlatternPattern(const AST::Pattern& pattern, bool isLocalConst) { switch (pattern.astKind) { case AST::ASTKind::VAR_PATTERN: { auto varPattern = StaticCast(&pattern); auto varDecl = varPattern->varDecl.get(); CreateAndCacheGlobalVar(*varDecl, isLocalConst); break; } case AST::ASTKind::TUPLE_PATTERN: { auto tuplePattern = StaticCast(&pattern); for (auto& subPattern : tuplePattern->patterns) { FlatternPattern(*subPattern, isLocalConst); } break; } case AST::ASTKind::ENUM_PATTERN: { auto enumPattern = StaticCast(&pattern); for (auto& subPattern : enumPattern->patterns) { FlatternPattern(*subPattern, isLocalConst); } break; } case AST::ASTKind::WILDCARD_PATTERN: { break; } default: { Errorln("decl with unsupported pattern"); CJC_ABORT(); } } } void AST2CHIR::SetInitFuncForStaticVar() { for (auto& skipedStaticVar : varsInitedByStaticInitFunc) { // If common var with platform one, need to skip auto skipedStaticVarVal = globalCache.TryGet(*skipedStaticVar); if (skipedStaticVarVal == nullptr || skipedStaticVar->platformImplementation) { continue; } CJC_ASSERT(skipedStaticVar->astKind == AST::ASTKind::VAR_DECL); // Also set the init func here if (auto skipedStaticVarInCHIR = DynamicCast(skipedStaticVarVal)) { auto staticInitFuncInAST = staticInitFuncInfoMap.at(skipedStaticVar->outerDecl).staticInitFunc; auto staticInitFuncInCHIR = DynamicCast(globalCache.Get(*staticInitFuncInAST)); CJC_NULLPTR_CHECK(staticInitFuncInCHIR); skipedStaticVarInCHIR->SetInitFunc(*staticInitFuncInCHIR); } } } Translator AST2CHIR::CreateTranslator() { return Translator{builder, chirType, opts, gim, globalCache, localConstVars, localConstFuncs, kind, deserializedVals, annoFactoryFuncs, maybeUnreachable, isComputingAnnos, initFuncsForAnnoFactory, types}; } void AST2CHIR::TranslateInitOfGlobalVars(const AST::Package& pkg, const InitOrder& initOrder) { Utils::ProfileRecorder recorder("TranslateAllDecls", "TranslateInitOfGlobalVars"); auto trans = CreateTranslator(); GlobalVarInitializer initializer(trans, importManager, initFuncsForConstVar, kind == IncreKind::INCR); initializer.Run(pkg, initOrder); SetInitFuncForStaticVar(); } void AST2CHIR::CollectTopLevelDecls(AST::Package& pkg) { Utils::ProfileRecorder recorder("AST to CHIR Translation", "CollectTopLevelDecls"); // For some special functions, they are explicitly imported even they are NOT explicitly used. // Collect implicit imported/used decl, the decl list is made by // `REG_IMPLICIT_IMPORTED_NON_GENERIC_FUNC` and `REG_IMPLICIT_IMPORTED_GENERIC_FUNC`,there are two cases: // 1. if `pkg` is not std.core, we collect implicit imported decls, especially those generic decl in std.core, but // instantiated decls in other imported package. // 2. if `pkg` is std.core (aka we are compiling std.core), we collect implicit used decls by down stream package. CollectImplicitFuncs(); // Collect Imported Pkg Top-Level Decl Part CollectImportedDecls(pkg); // Collect Current Pkg Top-Level Decl & Generic Top-Level Decl Part CollectDeclsInCurPkg(pkg); } void AST2CHIR::CacheSomeDeclsToGlobalSymbolTable() { Utils::ProfileRecorder recorder("AST to CHIR Translation", "CacheSomeDeclsToGlobalSymbolTable"); // translate all custom type decl, and cache them to global symbol table // only create a shell of `CustomTypeDef`, including its ptr and attribute, not fill its method, vtable and so on CacheCustomTypeDefToGlobalSymbolTable(); // translate all custom type's type, and set type to `CustomTypeDef` TranslateAllCustomTypeTy(); // create all top-level func decl's shell and var decls, cache them to global symbol table. CacheTopLevelDeclToGlobalSymbolTable(); SetGenericDecls(); } void AST2CHIR::SetGenericDecls() const { if (!gim) { return; } auto genericToInsMap = gim->GetAllGenericToInsDecls(); for (auto& mapIt : genericToInsMap) { if (mapIt.first->astKind == AST::ASTKind::EXTEND_DECL) { continue; } if (mapIt.first->IsNominalDecl()) { auto chirGeneric = chirType.TryGetGlobalNominalCache(*mapIt.first); CJC_NULLPTR_CHECK(chirGeneric); for (auto insDecl : mapIt.second) { auto chirIns = chirType.TryGetGlobalNominalCache(*insDecl); // instantiated decl may be imported but not used in current package, so it's not translated by CHIR if (chirIns == nullptr) { continue; } chirIns->SetGenericDecl(*chirGeneric); } } else if (mapIt.first->astKind == AST::ASTKind::FUNC_DECL) { auto chirGeneric = globalCache.TryGet(*mapIt.first); // intrinsic if (chirGeneric == nullptr) { continue; } for (auto insDecl : mapIt.second) { auto chirIns = globalCache.TryGet(*insDecl); // shouldn't be nullptr if (chirIns == nullptr) { continue; } VirtualCast(chirIns)->SetGenericDecl(*VirtualCast(chirGeneric)); } } } } void AST2CHIR::TranslateAllDecls(const AST::Package& pkg, const InitOrder& initOrder) { Utils::ProfileRecorder recorder("AST to CHIR Translation", "TranslateAllDecls"); // step 1: translate nominal decls first TranslateNominalDecls(pkg); // step 2: translate initialization of global var TranslateInitOfGlobalVars(pkg, initOrder); // do this after init of gv to ensure gvinit of CustomAnnotations go after real gv's // this is always safe because such gvinit's are generated by CHIR and cannot be referenced in source for (auto decl : annoOnlyDecls) { CreateAnnoOnlyDeclSig(*decl); } // step 3: translate body of all funcs, the process can be run in parallel according to the compilation option Utils::ProfileRecorder::Start("TranslateAllDecls", "TranslateOtherTopLevelDecls"); if (opts.GetJobs() > 1) { TranslateTopLevelDeclsInParallel(); } else { for (auto decl : globalAndMemberFuncs) { if (!NeedTranslate(*decl)) { continue; } auto trans = CreateTranslator(); trans.SetTopLevel(*decl); Translator::TranslateASTNode(*decl, trans); if (decl->TestAttr(AST::Attribute::GLOBAL)) { trans.CollectValueAnnotation(*decl); } } for (auto decl : std::as_const(localConstFuncs.stableOrderValue)) { auto trans = CreateTranslator(); trans.SetTopLevel(*decl); Translator::TranslateASTNode(*decl, trans); } } // the parallel helper does not accept this type, use serialised translation for (auto decl : annoFactoryFuncs) { auto trans = CreateTranslator(); trans.SetTopLevel(*decl.first); trans.TranslateAnnoFactoryFuncBody(*decl.first, *decl.second); } Utils::ProfileRecorder::Stop("TranslateAllDecls", "TranslateOtherTopLevelDecls"); // step 4: set `CompileTimeValue` for lambda Utils::ProfileRecorder::Start("TranslateAllDecls", "SetCompileTimeValueFlag"); for (auto func : package->GetGlobalFuncs()) { if (func->TestAttr(Attribute::CONST)) { SetCompileTimeValueFlagRecursivly(*func); } } Utils::ProfileRecorder::Stop("TranslateAllDecls", "SetCompileTimeValueFlag"); } void AST2CHIR::TranslateInParallel(const std::vector>& decls) { Utils::ParallelUtil allDeclsParallel(builder, opts.GetJobs()); allDeclsParallel.RunAST2CHIRInParallel(decls, chirType, opts, gim, globalCache, localConstVars, localConstFuncs, kind, deserializedVals, Translator::TranslateASTNode, maybeUnreachable, isComputingAnnos, initFuncsForAnnoFactory, types, annoFactoryFuncs); } void AST2CHIR::TranslateTopLevelDeclsInParallel() { // collect all top-level funcs into a vector. std::vector> allDecls; auto needTrans = [this](Ptr decl) { return NeedTranslate(*decl); }; // Filter out decls that do not require translation. std::copy_if(globalAndMemberFuncs.begin(), globalAndMemberFuncs.end(), std::back_inserter(allDecls), needTrans); TranslateInParallel(allDecls); std::vector> localFuncsGeneric; // generic decls std::vector> localFuncsOther; // instantiated or non-generic decls for (auto func : std::as_const(localConstFuncs.stableOrderValue)) { if (func->TestAttr(AST::Attribute::GENERIC)) { localFuncsGeneric.push_back(func); } else { localFuncsOther.push_back(func); } } TranslateInParallel(localFuncsGeneric); TranslateInParallel(localFuncsOther); } void AST2CHIR::AST2CHIRCheck() { if (!opts.chirWFC) { return; } Utils::ProfileRecorder recorder("AST to CHIR Translation", "AST2CHIRCheck"); // check customDefTypes auto customDefMap = chirType.GetAllTypeDef(); for (auto& it : customDefMap) { const AST::Node& astNode = *it.first; const CHIR::CustomTypeDef& chirNode = *it.second; if (astNode.TestAttr(AST::Attribute::GENERIC, AST::Attribute::IMPORTED)) { continue; } if (astNode.TestAttr(AST::Attribute::IMPORTED) && chirNode.TestAttr(Attribute::DESERIALIZED)) { continue; } if (chirNode.TestAttr(Attribute::SKIP_ANALYSIS)) { continue; } auto ret = AST2CHIRCheckCustomTypeDef(astNode, chirNode, globalCache); if (!ret) { this->failure = true; it.second->Dump(); } } // check globalFunc and globalVar for (auto& it : globalCache.GetALL()) { if (it.second->TestAttr(Attribute::SKIP_ANALYSIS)) { continue; } auto ret = AST2CHIRCheckValue(*it.first, *it.second); if (!ret) { this->failure = true; it.second->Dump(); } } } /// Return true if chir was deserialized bool AST2CHIR::TryToDeserializeCHIR() { auto& chirFiles = opts.inputChirFiles; ToCHIR::Phase phase; if (chirFiles.size() == 1) { CHIRDeserializer::Deserialize(chirFiles.at(0), builder, phase, true); mergingPlatform = true; return true; } else if (chirFiles.size() != 0) { // synthetic limitation, however need to distinguish different chir sources diag.DiagnoseRefactor(DiagKindRefactor::frontend_can_not_handle_to_many_chir, DEFAULT_POSITION); } return false; } static Package::AccessLevel BuildPackageAccessLevel(const AST::AccessLevel& level) { static std::unordered_map accessLevelMap = { {AST::AccessLevel::INTERNAL, Package::AccessLevel::INTERNAL}, {AST::AccessLevel::PROTECTED, Package::AccessLevel::PROTECTED}, {AST::AccessLevel::PUBLIC, Package::AccessLevel::PUBLIC}, }; auto it = accessLevelMap.find(level); CJC_ASSERT(it != accessLevelMap.end()); return it->second; } bool AST2CHIR::ToCHIRPackage(AST::Package& node) { // It can be not null in case of part of the package was deserialized from .chir if (TryToDeserializeCHIR()) { package = builder.GetCurPackage(); BuildDeserializedTable(); } else { package = builder.CreatePackage(node.fullPackageName); outputCHIR = opts.outputMode == GlobalOptions::OutputMode::CHIR; } // Translating common alongside with merging platform is not supported CJC_ASSERT(!(outputCHIR && mergingPlatform)); package->SetPackageAccessLevel(BuildPackageAccessLevel(node.accessible)); RegisterAllSources(); CJC_NULLPTR_CHECK(package); dependencyPkg = importManager.GetAllDependentPackageNames(node.fullPackageName); // step 1: collect all top-level decls CollectTopLevelDecls(node); // step 2: sort and do circular dependency checking auto [initOrder, result] = SortGlobalVarDecl(node); if (!result) { return false; } // step 3: create signature of nominal decls and top-level func decls CacheSomeDeclsToGlobalSymbolTable(); // step 4: translate all colleted decls TranslateAllDecls(node, initOrder); // step 5: ast2chir check AST2CHIRCheck(); return !HasFailed(); } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/AST2CHIRChecker.cpp000066400000000000000000000545551510705540100226700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/AST2CHIRChecker.h" #include "cangjie/Utils/ProfileRecorder.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Value.h" using namespace Cangjie; using namespace Cangjie::CHIR; namespace { void Errorln(const std::string& info) { std::cerr << "ast2chir checker error: " << info << std::endl; } bool CheckPrimitiveType(const Cangjie::AST::Ty& astTy, const Type& chirTy) { const std::map chir2astTy = { {Type::TypeKind::TYPE_INT8, Cangjie::AST::TypeKind::TYPE_INT8}, {Type::TypeKind::TYPE_INT16, Cangjie::AST::TypeKind::TYPE_INT16}, {Type::TypeKind::TYPE_INT32, Cangjie::AST::TypeKind::TYPE_INT32}, {Type::TypeKind::TYPE_INT64, Cangjie::AST::TypeKind::TYPE_INT64}, {Type::TypeKind::TYPE_INT_NATIVE, Cangjie::AST::TypeKind::TYPE_INT_NATIVE}, {Type::TypeKind::TYPE_UINT8, Cangjie::AST::TypeKind::TYPE_UINT8}, {Type::TypeKind::TYPE_UINT16, Cangjie::AST::TypeKind::TYPE_UINT16}, {Type::TypeKind::TYPE_UINT32, Cangjie::AST::TypeKind::TYPE_UINT32}, {Type::TypeKind::TYPE_UINT64, Cangjie::AST::TypeKind::TYPE_UINT64}, {Type::TypeKind::TYPE_UINT_NATIVE, Cangjie::AST::TypeKind::TYPE_UINT_NATIVE}, {Type::TypeKind::TYPE_FLOAT16, Cangjie::AST::TypeKind::TYPE_FLOAT16}, {Type::TypeKind::TYPE_FLOAT32, Cangjie::AST::TypeKind::TYPE_FLOAT32}, {Type::TypeKind::TYPE_FLOAT64, Cangjie::AST::TypeKind::TYPE_FLOAT64}, {Type::TypeKind::TYPE_RUNE, Cangjie::AST::TypeKind::TYPE_RUNE}, {Type::TypeKind::TYPE_BOOLEAN, Cangjie::AST::TypeKind::TYPE_BOOLEAN}, {Type::TypeKind::TYPE_UNIT, Cangjie::AST::TypeKind::TYPE_UNIT}, {Type::TypeKind::TYPE_NOTHING, Cangjie::AST::TypeKind::TYPE_NOTHING}}; if (chir2astTy.count(chirTy.GetTypeKind()) == 0) { Cangjie::InternalError("unsupported type kind"); } return chir2astTy.at(chirTy.GetTypeKind()) == astTy.kind; } bool CheckType(const Cangjie::AST::Ty& astTy, const Type& chirTy); bool CheckTypeArgs(const Cangjie::AST::Ty& astTy, const Type& chirTy) { auto astTyArgs = astTy.typeArgs; auto chirTyArgs = chirTy.GetTypeArgs(); if (astTyArgs.size() != chirTyArgs.size()) { return false; } for (size_t i = 0; i < astTyArgs.size(); ++i) { if (!CheckType(*astTyArgs[i], *chirTyArgs[i])) { return false; } } return true; } bool CheckTupleType(const Cangjie::AST::Ty& astTy, const Type& chirTy) { auto astTyArgs = astTy.typeArgs; auto chirTyArgs = chirTy.GetTypeArgs(); if (astTyArgs.size() != chirTyArgs.size()) { return false; } for (size_t loop = 0; loop < astTyArgs.size(); loop++) { if (!CheckType(*astTyArgs[loop], *chirTyArgs[loop])) { return false; } } return true; } bool CheckFuncType(const Cangjie::AST::Ty& astTy, const Type& chirTy) { auto astTyArgs = astTy.typeArgs; auto chirTyArgs = chirTy.GetTypeArgs(); if (astTyArgs.size() != chirTyArgs.size()) { return false; } for (size_t loop = 0; loop < astTyArgs.size(); loop++) { if (!CheckType(*astTyArgs[loop], *chirTyArgs[loop])) { return false; } } return true; } bool CheckMethodType(const Cangjie::AST::Ty& astTy, const Type& chirTy) { auto astTyArgs = astTy.typeArgs; auto chirTyArgs = chirTy.GetTypeArgs(); if (astTyArgs.size() + 1 != chirTyArgs.size()) { return false; } for (size_t loop = 0; loop < astTyArgs.size(); loop++) { if (!CheckType(*astTyArgs[loop], *chirTyArgs[loop + 1])) { return false; } } return true; } bool CheckStructType(const Cangjie::AST::Ty& astTy, const Type& chirTy) { if (!astTy.IsStruct()) { return false; } auto astStruct = Cangjie::StaticCast(astTy).declPtr; auto chirStruct = Cangjie::StaticCast(chirTy).GetStructDef(); if (astStruct->mangledName != chirStruct->GetIdentifierWithoutPrefix()) { return false; } return CheckTypeArgs(astTy, chirTy); } bool CheckClassType(const Cangjie::AST::Ty& astTy, const Type& chirTy) { if (!astTy.IsClass()) { return false; } auto astClass = Cangjie::StaticCast(astTy).declPtr; auto chirClass = Cangjie::StaticCast(chirTy).GetClassDef(); if (astClass->mangledName != chirClass->GetIdentifierWithoutPrefix()) { return false; } return CheckTypeArgs(astTy, chirTy); } bool CheckInterfaceType(const Cangjie::AST::Ty& astTy, const Type& chirTy) { if (!astTy.IsInterface()) { return false; } auto astClass = Cangjie::StaticCast(astTy).declPtr; auto chirClass = Cangjie::StaticCast(chirTy).GetClassDef(); if (astClass->mangledName != chirClass->GetIdentifierWithoutPrefix()) { return false; } return CheckTypeArgs(astTy, chirTy); } bool CheckEnumType(const Cangjie::AST::Ty& astTy, const Type& chirTy) { if (!astTy.IsEnum()) { return false; } auto astEnum = Cangjie::StaticCast(astTy).declPtr; auto chirEnum = Cangjie::StaticCast(chirTy).GetEnumDef(); if (astEnum->mangledName != chirEnum->GetIdentifierWithoutPrefix()) { return false; } return CheckTypeArgs(astTy, chirTy); } bool CheckRawArrayType(const Cangjie::AST::Ty& astTy, const Type& chirTy) { if (!astTy.IsArray()) { return false; } auto astArray = Cangjie::StaticCast(astTy); auto& chirArray = Cangjie::StaticCast(chirTy); return astArray.dims == chirArray.GetDims() && CheckType(*astTy.typeArgs[0], *chirTy.GetTypeArgs()[0]); } bool CheckVArrayType(const Cangjie::AST::Ty& astTy, const Type& chirTy) { if (astTy.kind != Cangjie::AST::TypeKind::TYPE_VARRAY) { return false; } auto astArray = Cangjie::StaticCast(astTy); auto& chirArray = Cangjie::StaticCast(chirTy); return astArray.size == chirArray.GetSize() && CheckType(*astTy.typeArgs[0], *chirTy.GetTypeArgs()[0]); } bool CheckCPointerType(const Cangjie::AST::Ty& astTy, const Type& chirTy) { if (!astTy.IsPointer()) { return false; } return CheckType(*astTy.typeArgs[0], *chirTy.GetTypeArgs()[0]); } bool CheckType(const Cangjie::AST::Ty& astTy, const Type& chirTy) { if (chirTy.IsPrimitive()) { return CheckPrimitiveType(astTy, chirTy); } else if (chirTy.IsTuple()) { return CheckTupleType(astTy, chirTy); } else if (chirTy.IsFunc()) { return CheckFuncType(astTy, chirTy); } else if (chirTy.IsStruct()) { return CheckStructType(astTy, chirTy); } else if (chirTy.IsClass()) { if (StaticCast(chirTy).GetClassDef()->IsClass()) { return CheckClassType(astTy, chirTy); } else { return CheckInterfaceType(astTy, chirTy); } } else if (chirTy.IsEnum()) { return CheckEnumType(astTy, chirTy); } else if (chirTy.IsRawArray()) { return CheckRawArrayType(astTy, chirTy); } else if (chirTy.IsVArray()) { return CheckVArrayType(astTy, chirTy); } else if (chirTy.IsCPointer()) { return CheckCPointerType(astTy, chirTy); } else if (chirTy.IsCString()) { return astTy.IsCString(); } else if (chirTy.IsRef()) { return CheckType(astTy, *chirTy.GetTypeArgs()[0]); } return true; } bool CheckClass(const Cangjie::AST::ClassDecl& decl, const ClassDef& classDef) { if (!classDef.IsClass()) { Errorln(classDef.GetIdentifier() + " is expected to be a classDef."); return false; } AST::ClassTy* astSupClsTy = nullptr; for (auto& super : decl.inheritedTypes) { if (super->ty->kind == AST::TypeKind::TYPE_CLASS) { astSupClsTy = StaticCast(super->ty); } } auto chirSupClsTy = classDef.GetSuperClassTy(); // check super class type if (astSupClsTy != nullptr && chirSupClsTy != nullptr) { if (!CheckType(*astSupClsTy, *chirSupClsTy)) { Errorln(classDef.GetIdentifier() + " set wrong super class."); return false; } } else if (astSupClsTy != nullptr) { Errorln(classDef.GetIdentifier() + " not set super class."); return false; } else if (chirSupClsTy != nullptr) { Errorln(classDef.GetIdentifier() + " set redundant super class."); return false; } return true; } bool CheckInterface(const ClassDef& chirNode) { if (!chirNode.IsInterface()) { Errorln(chirNode.GetIdentifier() + " is expected to be a interfaceDef."); return false; } return true; } const CustomTypeDef* GetParentCustomTypeDef(const Value& value) { if (auto func = DynamicCast(&value)) { return func->GetParentCustomTypeDef(); } else if (auto var = DynamicCast(&value)) { return var->GetParentCustomTypeDef(); } return nullptr; } bool CheckInheritDeclGlobalMember( const Cangjie::AST::Decl& decl, const CustomTypeDef& chirNode, const AST2CHIRNodeMap& globalCache) { auto chirCache = globalCache.TryGet(decl); if (chirCache == nullptr && decl.TestAttr(Cangjie::AST::Attribute::COMMON)) { return true; } if (chirCache == nullptr) { Errorln("not find " + decl.mangledName + " translator in " + chirNode.GetIdentifier() + "."); return false; } // skip checking temporarily if (decl.IsConst() && decl.TestAttr(AST::Attribute::IMPORTED)) { return true; } // check chir node have a right declaredParent auto funcDecl = DynamicCast(&decl); if (funcDecl == nullptr || !IsStaticInit(*funcDecl)) { if (auto def = GetParentCustomTypeDef(*chirCache); def != &chirNode) { Errorln("not find " + chirCache->GetIdentifier() + " in " + chirNode.GetIdentifier() + "."); return false; } } // member func if (decl.astKind == Cangjie::AST::ASTKind::FUNC_DECL) { if (!decl.TestAttr(Cangjie::AST::Attribute::STATIC) && !CheckMethodType(*decl.ty, *chirCache->GetType())) { Errorln(chirCache->GetIdentifier() + " is expected to be promoted " + Cangjie::AST::Ty::ToString(decl.ty) + "."); return false; } if (decl.TestAttr(Cangjie::AST::Attribute::STATIC) && !CheckFuncType(*decl.ty, *chirCache->GetType())) { Errorln(chirCache->GetIdentifier() + " is expected to be promoted " + Cangjie::AST::Ty::ToString(decl.ty) + "."); return false; } } return true; } bool CheckAbstractMethod(const Cangjie::AST::Decl& decl, const CustomTypeDef& chirNode) { if (chirNode.GetCustomKind() != CustomDefKind::TYPE_CLASS) { return true; } if (decl.astKind == Cangjie::AST::ASTKind::PROP_DECL) { auto ret = true; auto& propDecl = Cangjie::StaticCast(decl); for (auto& itp : propDecl.getters) { ret = CheckAbstractMethod(*itp, chirNode) && ret; } for (auto& itp : propDecl.setters) { ret = CheckAbstractMethod(*itp, chirNode) && ret; } return ret; } auto& classDef = Cangjie::StaticCast(chirNode); for (auto& it : classDef.GetAbstractMethods()) { if (it.GetMangledName() != decl.mangledName + ".0") { continue; } auto res = true; if (it.TestAttr(Attribute::STATIC)) { res = CheckType(*decl.ty, *it.methodTy); } else { auto astTyArgs = decl.ty->typeArgs; auto chirTyArgs = it.methodTy->GetTypeArgs(); if (astTyArgs.size() + 1 != chirTyArgs.size()) { res = false; } else { for (size_t i = 0; i < astTyArgs.size(); ++i) { res = CheckType(*astTyArgs[i], *chirTyArgs[i + 1]) && res; } } } if (!res) { Errorln(it.GetMangledName() + " is expected to be " + Cangjie::AST::Ty::ToString(decl.ty) + " in " + chirNode.GetIdentifier() + "."); return false; } return true; } if (decl.platformImplementation && decl.platformImplementation->TestAttr(AST::Attribute::OPEN)) { // ABSTRACT COMMON was replaced with OPEN PLATFORM return true; } Errorln("not find abstract method " + decl.mangledName + " in " + chirNode.GetIdentifier() + "."); return false; } bool CheckLocalVar(const Cangjie::AST::Decl& decl, const CustomTypeDef& chirNode) { auto localVars = chirNode.GetAllInstanceVars(); if (chirNode.GetCustomKind() == CustomDefKind::TYPE_CLASS) { auto& classDef = static_cast(chirNode); localVars = classDef.GetDirectInstanceVars(); } for (auto& it : localVars) { if (it.name != decl.identifier.Val()) { continue; } if (!DynamicCast(decl.ty) && !CheckType(*decl.ty, *it.type)) { Errorln(it.name + " is expected to be " + Cangjie::AST::Ty::ToString(decl.ty) + " in " + chirNode.GetIdentifier() + "."); return false; } return true; } Errorln("not find local var " + decl.mangledName + " in " + chirNode.GetIdentifier() + "."); return false; } bool CheckInheritDeclMembers( const Cangjie::AST::InheritableDecl& decl, const CustomTypeDef& chirNode, const AST2CHIRNodeMap& globalCache) { auto ret = true; for (auto& it : decl.GetMemberDecls()) { // All of call to JArray constructors will be desugared, so we can skip the useless constructor member directly. if (it->TestAttr(Cangjie::AST::Attribute::GENERIC)) { continue; } // decl in Interface is abstract method. if (decl.astKind == Cangjie::AST::ASTKind::INTERFACE_DECL) { if (it->astKind != AST::ASTKind::VAR_DECL) { ret = CheckAbstractMethod(*it, chirNode) && ret; } continue; } // abstract method not have a real node if (it->TestAttr(Cangjie::AST::Attribute::ABSTRACT)) { ret = CheckAbstractMethod(*it, chirNode) && ret; continue; } if (decl.astKind == Cangjie::AST::ASTKind::INTERFACE_DECL && !it->TestAttr(Cangjie::AST::Attribute::STATIC) && (it->astKind == Cangjie::AST::ASTKind::FUNC_DECL || it->astKind == Cangjie::AST::ASTKind::PROP_DECL)) { ret = CheckAbstractMethod(*it, chirNode) && ret; continue; } // local member var not have a real node if (it->astKind == Cangjie::AST::ASTKind::VAR_DECL && !it->TestAttr(Cangjie::AST::Attribute::STATIC)) { ret = CheckLocalVar(*it, chirNode) && ret; continue; } // primary ctor not have a cache if (it->astKind == Cangjie::AST::ASTKind::PRIMARY_CTOR_DECL) { continue; } if (it->astKind == Cangjie::AST::ASTKind::PROP_DECL) { auto& propDecl = Cangjie::StaticCast(*it); for (auto& itp : propDecl.getters) { ret = CheckInheritDeclGlobalMember(*itp, chirNode, globalCache) && ret; } for (auto& itp : propDecl.setters) { ret = CheckInheritDeclGlobalMember(*itp, chirNode, globalCache) && ret; } continue; } // other all need a real node, contain method and static var. ret = CheckInheritDeclGlobalMember(*it, chirNode, globalCache) && ret; } return ret; } bool CheckClassLike( const Cangjie::AST::ClassLikeDecl& decl, const CustomTypeDef& chirNode, const AST2CHIRNodeMap& globalCache) { if (chirNode.GetCustomKind() != CustomDefKind::TYPE_CLASS) { Errorln(chirNode.GetIdentifier() + " is expected to be a classDef/interfaceDef."); return false; } auto ret = true; auto& classDef = static_cast(chirNode); // check super interface same auto astSupInter = decl.GetSuperInterfaceTys(); auto chirSupClsInter = classDef.GetImplementedInterfaceDefs(); if (astSupInter.size() != chirSupClsInter.size()) { Errorln(chirNode.GetIdentifier() + " set wrong super interfaces."); ret = false; } if (decl.astKind == Cangjie::AST::ASTKind::CLASS_DECL) { ret = CheckClass(static_cast(decl), classDef) && ret; } else { ret = CheckInterface(classDef) && ret; } return CheckInheritDeclMembers(decl, chirNode, globalCache) && ret; } bool CheckEnum( const Cangjie::AST::EnumDecl& decl, const CustomTypeDef& chirNode, const AST2CHIRNodeMap& globalCache) { if (chirNode.GetCustomKind() != CustomDefKind::TYPE_ENUM) { Errorln(chirNode.GetIdentifier() + " is expected to be a enumDef."); return false; } return CheckInheritDeclMembers(decl, chirNode, globalCache); } bool CheckStruct( const Cangjie::AST::StructDecl& decl, const CustomTypeDef& chirNode, const AST2CHIRNodeMap& globalCache) { if (chirNode.GetCustomKind() != CustomDefKind::TYPE_STRUCT) { Errorln(chirNode.GetIdentifier() + " is expected to be a structDef."); return false; } return CheckInheritDeclMembers(decl, chirNode, globalCache); } bool CheckFunc(const Cangjie::AST::FuncDecl& decl, const Value& chirNode) { if (!Is(chirNode)) { Errorln(chirNode.GetIdentifier() + " is expected to be a func."); return false; } // not check member method, it will check in customDef if (decl.outerDecl != nullptr) { return true; } auto astTy = decl.ty; auto chirTy = chirNode.GetType(); if (!CheckType(*astTy, *chirTy)) { Errorln(chirNode.GetIdentifier() + " is expected to be " + Cangjie::AST::Ty::ToString(astTy) + "."); return false; } return true; } bool CheckVar(const Cangjie::AST::VarDecl& decl, const Value& chirNode) { if (!Is(chirNode)) { Errorln(chirNode.GetIdentifier() + " is expected to be a globalVar."); return false; } auto astTy = decl.ty; auto chirTy = chirNode.GetType(); if (!CheckType(*astTy, *chirTy)) { Errorln(chirNode.GetIdentifier() + " is expected to be " + Cangjie::AST::Ty::ToString(astTy) + "."); return false; } return true; } /// Search for vtable method duplication. // O(n^2) is okay because of average table size inline FuncBase* CheckVtableCopy(const std::vector& tableMethods) { auto size = tableMethods.size(); for (size_t i = 0; i < size; i++) { auto instance1 = tableMethods[i].instance; for (size_t j = i + 1; j < size; j++) { auto instance2 = tableMethods[j].instance; if (instance1 == instance2 && tableMethods[i].srcCodeIdentifier == tableMethods[j].srcCodeIdentifier) { return instance1; } } } return nullptr; } bool CheckVtableHasNoDuplicates(const CustomTypeDef& customTypeDef) { for (auto& [srcTy, tableMethods] : customTypeDef.GetVTable()) { const auto duplicate = CheckVtableCopy(tableMethods); if (duplicate) { Errorln(customTypeDef.GetIdentifier() + " has method duplication in vtable: '" + duplicate->GetSrcCodeIdentifier() + "'."); return false; } } return true; } } // namespace namespace Cangjie::CHIR { bool AST2CHIRCheckCustomTypeDef( const AST::Node& astNode, const CustomTypeDef& chirNode, const AST2CHIRNodeMap& globalCache) { if (!astNode.IsNominalDecl()) { InternalError("unsupported decl"); return false; } auto ret = true; auto& decl = static_cast(astNode); if (decl.identifier != chirNode.GetSrcCodeIdentifier()) { Errorln(chirNode.GetIdentifier() + "'srcIdentifier is expected to be " + decl.identifier + "."); ret = false; } if (decl.mangledName != chirNode.GetIdentifierWithoutPrefix()) { Errorln(chirNode.GetIdentifier() + "'identifier is expected to be " + decl.mangledName + "."); ret = false; } // NOTE: measurements are accumulated Utils::ProfileRecorder::Start("AST to CHIR Translation", "Checking VTable duplication"); CheckVtableHasNoDuplicates(chirNode); Utils::ProfileRecorder::Stop("AST to CHIR Translation", "Checking VTable duplication"); if (astNode.astKind == AST::ASTKind::CLASS_DECL || astNode.astKind == AST::ASTKind::INTERFACE_DECL) { return CheckClassLike(static_cast(decl), chirNode, globalCache) && ret; } else if (astNode.astKind == AST::ASTKind::ENUM_DECL) { return CheckEnum(static_cast(decl), chirNode, globalCache) && ret; } else if (astNode.astKind == AST::ASTKind::STRUCT_DECL) { return CheckStruct(static_cast(decl), chirNode, globalCache) && ret; } return ret; } bool AST2CHIRCheckValue(const AST::Node& astNode, const Value& chirNode) { if (!astNode.IsDecl()) { return true; } auto& decl = static_cast(astNode); if (astNode.astKind == AST::ASTKind::VAR_DECL) { return CheckVar(static_cast(decl), chirNode); } else if (astNode.astKind == AST::ASTKind::FUNC_DECL) { return CheckFunc(static_cast(decl), chirNode); } return true; } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/ASTPackage2CHIR.cpp000066400000000000000000001666001510705540100226520ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/AST/Utils.h" #include "cangjie/CHIR/AST2CHIR/AST2CHIR.h" #include #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/ConstantUtils.h" #include "cangjie/CHIR/Type/ExtendDef.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/Mangle/CHIRManglingUtils.h" #include "cangjie/Utils/CastingTemplate.h" #include "cangjie/Utils/CheckUtils.h" namespace Cangjie::CHIR { namespace { REG_IMPLICIT_IMPORTED_NON_GENERIC_FUNC(AST::ASTKind::CLASS_DECL, init, SpawnException); REG_IMPLICIT_IMPORTED_NON_GENERIC_FUNC(AST::ASTKind::CLASS_DECL, init, Exception); REG_IMPLICIT_IMPORTED_NON_GENERIC_FUNC(AST::ASTKind::CLASS_DECL, init, IndexOutOfBoundsException); REG_IMPLICIT_IMPORTED_NON_GENERIC_FUNC(AST::ASTKind::CLASS_DECL, init, NegativeArraySizeException); REG_IMPLICIT_IMPORTED_NON_GENERIC_FUNC(AST::ASTKind::CLASS_DECL, init, OutOfMemoryError); REG_IMPLICIT_IMPORTED_NON_GENERIC_FUNC(AST::ASTKind::CLASS_DECL, printStackTrace, Exception); REG_IMPLICIT_IMPORTED_NON_GENERIC_FUNC(AST::ASTKind::CLASS_DECL, printStackTrace, Error); REG_IMPLICIT_IMPORTED_NON_GENERIC_FUNC(AST::ASTKind::INVALID_DECL, getCommandLineArgs); REG_IMPLICIT_IMPORTED_NON_GENERIC_FUNC(AST::ASTKind::INVALID_DECL, createArithmeticExceptionMsg); REG_IMPLICIT_IMPORTED_NON_GENERIC_FUNC(AST::ASTKind::INVALID_DECL, createOverflowExceptionMsg); REG_IMPLICIT_IMPORTED_NON_GENERIC_FUNC(AST::ASTKind::INVALID_DECL, CJ_CORE_ExecAtexitCallbacks); REG_IMPLICIT_IMPORTED_NON_GENERIC_FUNC(AST::ASTKind::INVALID_DECL, handleException); REG_IMPLICIT_IMPORTED_NON_GENERIC_FUNC(AST::ASTKind::INVALID_DECL, eprintln); REG_IMPLICIT_IMPORTED_NON_GENERIC_FUNC(AST::ASTKind::CLASS_DECL, setRuntimeCJThreadHandle, Thread); REG_IMPLICIT_IMPORTED_GENERIC_FUNC(AST::ASTKind::CLASS_DECL, execute, Future); REG_IMPLICIT_IMPORTED_GENERIC_FUNC(AST::ASTKind::CLASS_DECL, executeClosure, Future); REG_IMPLICIT_IMPORTED_GENERIC_FUNC(AST::ASTKind::CLASS_DECL, get, Future); Linkage GetFuncLinkage(const GlobalOptions& opts, const AST::FuncDecl& func) { if (func.TestAttr(AST::Attribute::IMPORTED)) { return func.linkage; } if (func.linkage == Linkage::EXTERNAL || func.linkage == Linkage::WEAK_ODR) { if (func.TestAttr(AST::Attribute::IMPORTED)) { return Linkage::INTERNAL; } else if (opts.target.IsMinGW()) { return Linkage::EXTERNAL; } } return func.linkage; } bool IsLocalConstFuncInStaticMember(const AST::FuncDecl& astFunc) { if (!astFunc.isConst) { return false; } return IsLocalFunc(astFunc) && GetOuterDecl(astFunc)->TestAttr(AST::Attribute::STATIC); } void SetGlobalVarLinkageType(Value& var, const AST::VarDecl& decl, bool isLiftedLocalConst, bool isWin) { // local const var is lifted to global const var by CHIR, its linkage must be internal if (isLiftedLocalConst) { var.Set(Linkage::INTERNAL); return; } if (decl.isConst && !isWin) { var.Set(Linkage::WEAK_ODR); return; } var.Set(decl.linkage); } } // namespace void AST2CHIR::AddToImplicitFuncs(AST::FuncDecl& funcDecl, std::vector& registeredImplicitFuncs, std::unordered_set>& implicitlyImportedDecls) const { std::unordered_set usedFuncInSanCov{FUNC_MANGLE_NAME_MALLOC_CSTRING, FUNC_MANGLE_NAME_CSTRING_SIZE}; if (opts.sancovOption.IsSancovEnabled() && usedFuncInSanCov.find(funcDecl.mangledName) != usedFuncInSanCov.end()) { implicitlyImportedDecls.emplace(&funcDecl); return; } auto checkFuncInfo = [](const AST::FuncDecl& funcDecl, const ImplicitImportedFunc& funcInfo) { const auto parentDecl = funcDecl.outerDecl; return funcDecl.identifier == funcInfo.identifier && (funcInfo.parentName == "" ? !parentDecl : (parentDecl && parentDecl->identifier == funcInfo.parentName && parentDecl->astKind == funcInfo.parentKind)); }; // collect implicit funcDecl. auto it = std::find_if(registeredImplicitFuncs.begin(), registeredImplicitFuncs.end(), [&checkFuncInfo, &funcDecl](auto& funcInfo) { return checkFuncInfo(funcDecl, funcInfo); }); if (it != registeredImplicitFuncs.end()) { implicitlyImportedDecls.emplace(&funcDecl); } } void AST2CHIR::CollectImplicitFuncs() { std::vector registeredImplicitFuncs{}; auto collectImplicitDecls = [®isteredImplicitFuncs, this](AST::Node* node) -> AST::VisitAction { if (node->astKind == AST::ASTKind::FUNC_DECL) { auto funcDecl = StaticCast(node); AddToImplicitFuncs(*funcDecl, registeredImplicitFuncs, implicitDecls); return AST::VisitAction::SKIP_CHILDREN; } return AST::VisitAction::WALK_CHILDREN; }; auto importedPkgs = importManager.GetAllImportedPackages(); AST::PackageDecl* stdCorePkg = importManager.GetPackageDecl(CORE_PACKAGE_NAME); CJC_NULLPTR_CHECK(stdCorePkg); // Collect implicitly imported/used generic funcDecl. // These generic functions that are called implicitly only in CodeGen are from the "std.core" package. // But their generic instances may be in other import packages,so all import packages need to be traversed. registeredImplicitFuncs = ImplicitImportedFuncMgr::Instance().GetImplicitImportedFuncs( ImplicitImportedFuncMgr::FuncKind::GENERIC); for (auto& importedPkg : importedPkgs) { for (auto& instantiatedDecl : importedPkg->srcPackage->genericInstantiatedDecls) { AST::Walker(instantiatedDecl.get(), collectImplicitDecls).Walk(); } } // Collect implicitly imported/used non-generic funcDecl. // These functions that are called implicitly only in CodeGen are from the "std.core" package. registeredImplicitFuncs = ImplicitImportedFuncMgr::Instance().GetImplicitImportedFuncs( ImplicitImportedFuncMgr::FuncKind::NONE_GENERIC); AST::IterateToplevelDecls(*stdCorePkg->srcPackage, [&collectImplicitDecls](const OwnedPtr& decl) { AST::Walker(decl.get(), collectImplicitDecls).Walk(); }); for (auto& implicitDecl : implicitDecls) { CJC_ASSERT(implicitDecl->IsFunc()); } } void AST2CHIR::CollectDeclToList(AST::Decl& decl, std::vector>& astNodes) { allTopLevelNodes.emplace_back(&decl); astNodes.emplace_back(&decl); } void AST2CHIR::CollectDeclsFromEnumDecl(AST::EnumDecl& enumDecl) { for (auto& member : enumDecl.members) { // enumDecl's members include: member function, override function, prop function, associated type CJC_ASSERT(Is(member.get()) || Is(member.get())); CollectMemberDecl(*member); } } void AST2CHIR::CollectDeclsFromExtendDecl(AST::ExtendDecl& extendDecl) { // include member function, static member function, prop func, static prop func. for (auto& member : extendDecl.members) { CollectMemberDecl(*member); } } void AST2CHIR::CollectDeclsFromClassLikeDecl(AST::ClassLikeDecl& classLikeDecl) { auto& bodyDecls = classLikeDecl.GetMemberDecls(); for (auto& member : bodyDecls) { // Skip the non-static member variable declaration if (member->astKind == AST::ASTKind::VAR_DECL && !member.get()->TestAttr(AST::Attribute::STATIC)) { continue; } CollectMemberDecl(*member); } } void AST2CHIR::CollectInstantiatedDecls(const AST::Decl& decl) { /** * This function should only collect instantiated decls whose generic decl is in current package. * if the generic decl is in upstream package, but instantiated decls is in current package, * which should be collected by `CollectImportedGenericInstantiatedDecl` */ CJC_ASSERT(decl.TestAttr(AST::Attribute::GENERIC)); if (decl.curFile->curPackage->fullPackageName != GetPackage()->GetName()) { return; } if (!gim) { return; } auto instantiatedDecls = gim->GetInstantiatedDecls(decl); // For bep, decl should be sorted by mangledName and position to make sequence stable. auto compare = [](const AST::Decl* d1, const AST::Decl* d2) { CJC_NULLPTR_CHECK(d1); CJC_NULLPTR_CHECK(d2); CJC_ASSERT(d1->mangledName != d2->mangledName); return d1->mangledName < d2->mangledName; }; std::set sortedInstantiatedDecls(compare); for (auto& tempDecl : instantiatedDecls) { sortedInstantiatedDecls.insert(tempDecl); } for (auto& instance : sortedInstantiatedDecls) { CollectDecls(*instance, false); } } void AST2CHIR::CollectFuncDecl(AST::FuncDecl& funcDecl) { // Do not collect intrinsic functions, in order to reduce the size of package. if (funcDecl.TestAttr(AST::Attribute::INTRINSIC)) { return; } if (funcDecl.TestAttr(AST::Attribute::FOREIGN)) { CollectDeclToList(funcDecl, foreignFuncs); return; } if (funcDecl.TestAttr(AST::Attribute::GENERIC)) { CollectInstantiatedDecls(funcDecl); // Collect the function CollectDeclToList(funcDecl, globalAndMemberFuncs); // We need to collect the func itself and the default param func if there is any for (auto& param : funcDecl.funcBody->paramLists[0]->params) { if (param->desugarDecl) { CollectDeclToList(*param->desugarDecl, globalAndMemberFuncs); } } } else if (funcDecl.outerDecl && funcDecl.outerDecl->ty->HasGeneric()) { // When the func is not generic decl, but if the outerDecl of the funcDecl is generic, we still need to // collect this funcDecl. CollectDeclToList(funcDecl, globalAndMemberFuncs); for (auto& param : funcDecl.funcBody->paramLists[0]->params) { /* class A { var a:T init(b:T) { a = b } func foo(c!:T = a, b!:Int64 = 2) { return 0 } } */ if (param->desugarDecl) { CollectDeclToList(*param->desugarDecl, globalAndMemberFuncs); } } } else { // `instantiated` decl denote that: generic definition in up-stream pkg, but instantiated in current pkg, these // decls's IMPORTED Attribute is false,So we can't distinguish it from the current package decl. CollectDeclToList(funcDecl, globalAndMemberFuncs); for (auto& param : funcDecl.funcBody->paramLists[0]->params) { if (param->desugarDecl) { CollectDeclToList(*param->desugarDecl, globalAndMemberFuncs); } } } } void AST2CHIR::CollectMemberDecl(AST::Decl& decl) { if (auto funcDecl = DynamicCast(&decl); funcDecl) { CollectFuncDecl(*funcDecl); } else if (auto propDecl = DynamicCast(&decl); propDecl) { // Collect all the `getter` and `setter` functions for (auto& it : propDecl->getters) { CollectFuncDecl(*it); } for (auto& it : propDecl->setters) { CollectFuncDecl(*it); } } else if (auto varDecl = DynamicCast(&decl); varDecl) { CJC_ASSERT(varDecl->TestAttr(AST::Attribute::STATIC)); // VarWithPatternDecl can't be a static member decl, so we only deal with VarDecl. if (varDecl->outerDecl && varDecl->outerDecl->TestAttr(AST::Attribute::GENERIC)) { /* We should collect geneirc static member VarDecl. Cangjie code: interface I { static func get() : T } class A <: I { public static func get() : A { A() } } class B where T <: I { static let v : T = T.get() } */ CollectDeclToList(*varDecl, globalAndStaticVars); } else { CollectDeclToList(*varDecl, globalAndStaticVars); } } } void AST2CHIR::CollectDeclsFromStructDecl(const AST::StructDecl& structDecl) { for (auto& member : structDecl.body->decls) { // Skip the non-static member variable declaration if (member->astKind == AST::ASTKind::VAR_DECL && !member.get()->TestAttr(AST::Attribute::STATIC)) { continue; } CollectMemberDecl(*member); } } void AST2CHIR::CollectDesugarDecl(AST::Decl& decl) { if (decl.astKind == AST::ASTKind::MAIN_DECL) { auto mainDecl = StaticCast(&decl); if (mainDecl->desugarDecl) { // The desugarDecl has the attribute:MAIN_ENTRY CollectFuncDecl(*mainDecl->desugarDecl); } return; } if (decl.astKind == AST::ASTKind::MACRO_DECL) { auto macroDecl = StaticCast(&decl); if (macroDecl->desugarDecl) { // The desugarDecl has the attribute:MACRO_FUNC CollectFuncDecl(*macroDecl->desugarDecl); } } return; } void AST2CHIR::CollectVarandVarwithpatternDecl(AST::Decl& decl) { // global VarDecl and VarWithPatternDecl can't be generic decl. CollectDeclToList(decl, globalAndStaticVars); } void AST2CHIR::CollectDecls(AST::Decl& decl, bool instantiated) { switch (decl.astKind) { case AST::ASTKind::EXTEND_DECL: { auto extendDecl = StaticCast(&decl); if (extendDecl->TestAttr(AST::Attribute::GENERIC)) { CollectInstantiatedDecls(*extendDecl); CollectDeclsFromExtendDecl(*extendDecl); CollectDeclToList(*extendDecl, genericNominalDecls); } else { CollectDeclsFromExtendDecl(*StaticCast(&decl)); if (instantiated) { CollectDeclToList(*extendDecl, importedGenericInstantiatedNominalDecls); } else { CollectDeclToList(*extendDecl, nominalDecls); } } break; } case AST::ASTKind::STRUCT_DECL: { auto structDecl = StaticCast(&decl); if (structDecl->TestAttr(AST::Attribute::GENERIC)) { CollectInstantiatedDecls(*structDecl); CollectDeclsFromStructDecl(*structDecl); CollectDeclToList(*structDecl, genericNominalDecls); } else { CollectDeclsFromStructDecl(*structDecl); if (instantiated) { CollectDeclToList(*structDecl, importedGenericInstantiatedNominalDecls); } else { CollectDeclToList(*structDecl, nominalDecls); } } break; } case AST::ASTKind::INTERFACE_DECL: case AST::ASTKind::CLASS_DECL: { auto classLikeDecl = StaticCast(&decl); if (classLikeDecl->TestAttr(AST::Attribute::GENERIC)) { CollectInstantiatedDecls(*classLikeDecl); CollectDeclsFromClassLikeDecl(*classLikeDecl); CollectDeclToList(*classLikeDecl, genericNominalDecls); } else { CollectDeclsFromClassLikeDecl(*classLikeDecl); if (instantiated) { CollectDeclToList(*classLikeDecl, importedGenericInstantiatedNominalDecls); } else { CollectDeclToList(*classLikeDecl, nominalDecls); } } break; } case AST::ASTKind::ENUM_DECL: { auto enumDecl = StaticCast(&decl); if (enumDecl->TestAttr(AST::Attribute::GENERIC)) { CollectInstantiatedDecls(*enumDecl); CollectDeclsFromEnumDecl(*enumDecl); CollectDeclToList(*enumDecl, genericNominalDecls); } else { CollectDeclsFromEnumDecl(*enumDecl); if (instantiated) { CollectDeclToList(*enumDecl, importedGenericInstantiatedNominalDecls); } else { CollectDeclToList(*enumDecl, nominalDecls); } } break; } case AST::ASTKind::FUNC_DECL: { CollectFuncDecl(StaticCast(decl)); break; } case AST::ASTKind::MAIN_DECL: case AST::ASTKind::MACRO_DECL: { CollectDesugarDecl(decl); break; } case AST::ASTKind::VAR_DECL: case AST::ASTKind::VAR_WITH_PATTERN_DECL: { CollectVarandVarwithpatternDecl(decl); break; } default: { // Ignore other decls. } } } void AST2CHIR::CollectImportedGenericInstantiatedDecl( const AST::Package& node, std::unordered_set& mangledNameSet) { for (auto& decl : node.genericInstantiatedDecls) { auto genericDecl = decl->genericDecl; // We collect imported Instantiated decls in the current step, and Instantiated decls in current package will be // collected by `CollectDeclsInCurPkg`. // we can't check the `IMPORTED` attribute of `genericDecl` directly, // cause if we have code: `classC.foo`,the instantiated version of classC will change the // fullPackageName, so should not use genericDecl->fullPackageName. if (genericDecl->curFile->curPackage->fullPackageName == node.fullPackageName) { continue; } // We should not collect inner function. if (auto funcDecl = DynamicCast(genericDecl); funcDecl) { if (auto outerDecl = DynamicCast(funcDecl->outerDecl); outerDecl) { continue; } } // not collect different decl with same mangled name if (decl->astKind != AST::ASTKind::EXTEND_DECL) { auto res = mangledNameSet.emplace(decl->mangledName).second; if (!res) { continue; } } CollectDecls(*decl, true); } } void AST2CHIR::CollectImportedFuncDeclAndDesugarParams(AST::FuncDecl& funcDecl) { if (IsSrcCodeImportedGlobalDecl(funcDecl, opts)) { CollectFuncDeclToList(funcDecl, globalAndMemberFuncs); } else { CollectFuncDeclToList(funcDecl, importedGlobalAndMemberFuncs); } } void AST2CHIR::CollectImportedGlobalOrStaticVarDecl(AST::VarDecl& varDecl) { if (IsSrcCodeImportedGlobalDecl(varDecl, opts)) { CollectDeclToList(varDecl, globalAndStaticVars); } else { CollectDeclToList(varDecl, importedGlobalAndStaticVars); } } void AST2CHIR::CollectImportedGenericDecl(AST::Decl& decl) { // 1. imported generic func decl // 2. imported generic nominal decl // 2.1 member var decl of imported generic nominal decl // 2.2 member func decl of imported generic nominal decl if (decl.astKind == AST::ASTKind::FUNC_DECL) { CollectImportedFuncDeclAndDesugarParams(StaticCast(decl)); } else if (decl.IsNominalDecl()) { CollectDeclToList(decl, importedNominalDecls); for (auto& d : decl.GetMemberDecls()) { CollectImportedGenericDecl(*d); } } else if (decl.astKind == AST::ASTKind::PROP_DECL) { CollectImportedPropDecl(StaticCast(decl)); } else if (decl.astKind == AST::ASTKind::VAR_DECL && decl.TestAttr(AST::Attribute::STATIC)) { CollectImportedGlobalOrStaticVarDecl(StaticCast(decl)); } } void AST2CHIR::CollectFuncDeclToList(AST::FuncDecl& func, std::vector>& list) { CollectDeclToList(func, list); for (auto& param : func.funcBody->paramLists[0]->params) { if (param->desugarDecl) { CollectDeclToList(*param->desugarDecl, list); } } } void AST2CHIR::CollectImportedPropDecl(AST::PropDecl& propDecl) { for (auto& it : propDecl.getters) { CollectImportedFuncDeclAndDesugarParams(StaticCast(*it)); } for (auto& it : propDecl.setters) { CollectImportedFuncDeclAndDesugarParams(StaticCast(*it)); } } void AST2CHIR::CollectImportedDeclUsedInCurPkg(AST::Decl& decl) { // don't need to collect intrinsic func decl, because in CHIR, if we visit an intrinsic call expr, we will create // an `intrinsic` expression of CHIR, and pass in intrinsic kind, then codegen will know which function need to be // called. we don't need to translate intrinsic func decl to any CHIR node if (decl.TestAttr(AST::Attribute::INTRINSIC)) { return; } // 1. imported generic decl // 2. source imported var decl and func decl(func decl must be inlinable) // 3. imported nominal decl // 3.1 member var decl of imported nominal decl // 3.2 member func decl of imported nominal decl // 4. imported func decl // 5. imported prop decl // 6. imported var decl and var with pattern decl if (decl.TestAttr(AST::Attribute::GENERIC) || (decl.outerDecl != nullptr && decl.outerDecl->TestAttr(AST::Attribute::GENERIC))) { CollectImportedGenericDecl(decl); } else if (decl.IsNominalDecl()) { CollectDeclToList(decl, importedNominalDecls); for (auto& d : decl.GetMemberDecls()) { CollectImportedDeclUsedInCurPkg(*d); } } else if (decl.IsFunc()) { CollectImportedFuncDeclAndDesugarParams(StaticCast(decl)); } else if (decl.astKind == AST::ASTKind::PROP_DECL) { CollectImportedPropDecl(StaticCast(decl)); } else if (decl.astKind == AST::ASTKind::VAR_DECL && decl.TestAnyAttr(AST::Attribute::GLOBAL, AST::Attribute::STATIC)) { CollectImportedGlobalOrStaticVarDecl(StaticCast(decl)); } } void AST2CHIR::CollectImportedDecls(const AST::Package& node) { usedSrcImportedNonGenericDecls = std::unordered_set>{ node.srcImportedNonGenericDecls.begin(), node.srcImportedNonGenericDecls.end()}; std::unordered_set mangledNameSet; // 1. imported generic instantiated declarations, for which we should collect their instantiated versions CollectImportedGenericInstantiatedDecl(node, mangledNameSet); // 2. all imported decls, only including used decls in current package for (auto& importPkg : importManager.GetAllImportedPackages()) { CJC_NULLPTR_CHECK(importPkg->srcPackage.get()); // Exclude current package. if (importPkg->srcPackage == &node) { continue; } // used in current package for (auto& file : importPkg->srcPackage->files) { for (auto& decl : file->decls) { CollectImportedDeclUsedInCurPkg(*decl); } for (auto& decl : file->exportedInternalDecls) { CollectImportedDeclUsedInCurPkg(*decl); } } } } void AST2CHIR::CollectDeclsInCurPkg(AST::Package& node) { for (auto& file : node.files) { for (auto& decl : file->decls) { CollectDecls(*decl, false); } } } void AST2CHIR::SetFuncAttributeAndLinkageType(const AST::FuncDecl& astFunc, FuncBase& chirFunc) { // 1. ----------------------- Attribute ----------------------- chirFunc.AppendAttributeInfo(BuildAttr(astFunc.GetAttrs())); if (astFunc.isConst) { chirFunc.EnableAttr(Attribute::CONST); } // in SEMA, if a local const func is declared in static member method, it will be set STATIC // STATIC can be set for local func in SEMA, but not in CHIR, especially for const local func, // it can be lifted to global func, we need to disable STATIC, otherwise, a wrong Func will be generated in CHIR if (IsLocalConstFuncInStaticMember(astFunc)) { chirFunc.DisableAttr(Attribute::STATIC); } // in CHIR, we treat `static.init()` as global function, not member function, // because its outerDecl is something like `class A`, if it's member function, // initializer will be translated as follows: // Func gv$_init() { // Apply(static.init)(A, [], Unit) // `T` is not declared in this scope // } // we still store its outerDecl in Sema, because it's used in `GlobalVarSort` if (IsStaticInit(astFunc)) { chirFunc.DisableAttr(Attribute::STATIC); chirFunc.DisableAttr(Attribute::PRIVATE); chirFunc.EnableAttr(Attribute::INTERNAL); } if (kind == IncreKind::INCR && !astFunc.toBeCompiled) { chirFunc.EnableAttr(Attribute::NON_RECOMPILE); } if (astFunc.TestAttr(AST::Attribute::IN_EXTEND) || astFunc.TestAttr(AST::Attribute::UNSAFE) || astFunc.TestAttr(AST::Attribute::GENERIC_INSTANTIATED) || !astFunc.TestAttr(AST::Attribute::PUBLIC)) { chirFunc.EnableAttr(Attribute::NO_REFLECT_INFO); } // 1. function `main`, generated by user // 2. function `$mainInvoke`(mangled name is `user.main`), generated by compiler // 3. function which needed by runtime(in map `SPECIAL_FUNC_NAMES`, mangled name is begin with `rt$`) if (astFunc.TestAttr(AST::Attribute::MAIN_ENTRY) || astFunc.mangledName == USER_MAIN_MANGLED_NAME || astFunc.mangledName.find("rt$") == 0) { chirFunc.EnableAttr(Attribute::NO_INLINE); } // 2. ----------------------- LinkageType ----------------------- chirFunc.Set(GetFuncLinkage(opts, astFunc)); // 3. ----------------------- Others ----------------------- chirFunc.SetFuncKind(GetFuncKindFromAST(astFunc)); if (chirFunc.GetFuncKind() == FuncKind::DEFAULT_PARAMETER_FUNC) { chirFunc.SetParamDftValHostFunc(*VirtualCast(globalCache.Get(*astFunc.ownerFunc))); } chirFunc.SetFastNative(astFunc.isFastNative); if (astFunc.TestAttr(AST::Attribute::UNSAFE)) { chirFunc.EnableAttr(Attribute::UNSAFE); } } void AST2CHIR::CreateFuncSignatureAndSetGlobalCache(const AST::FuncDecl& funcDecl) { if (funcDecl.TestAttr(AST::Attribute::GENERIC)) { TranslateFunctionGenericUpperBounds(chirType, funcDecl); } if (kind == IncreKind::INCR && !funcDecl.toBeCompiled && !IsSrcCodeImportedGlobalDecl(funcDecl, opts)) { CreatePseudoImportedFuncSignatureAndSetGlobalCache(funcDecl); return; } // when the callee of callExpr is abstract func, will create a `Invoke` node, so we don't need to put abstract // func into the `globalCache`, if (funcDecl.TestAttr(AST::Attribute::ABSTRACT)) { return; } // Try get deserialized func. Func* fn = TryGetDeserialized(funcDecl); bool isPlatform = funcDecl.TestAttr(AST::Attribute::PLATFORM); if (fn) { if (isPlatform) { ResetPlatformFunc(funcDecl, *fn); } globalCache.Set(funcDecl, *fn); if (implicitDecls.count(&funcDecl) != 0) { implicitFuncs.emplace(fn->GetIdentifierWithoutPrefix(), fn); } if (IsSrcCodeImportedGlobalDecl(funcDecl, opts)) { srcCodeImportedFuncs.emplace(fn); } return; } auto fnTy = chirType.TranslateType(*funcDecl.ty); fnTy = AdjustFuncType(*StaticCast(fnTy), funcDecl, builder, chirType); // Create BlockGroup with argument FuncType* funcTy = StaticCast(fnTy); const auto& loc = DebugLocation( TranslateLocationWithoutScope(builder.GetChirContext(), funcDecl.begin, funcDecl.end)); auto genericParamTy = GetGenericParamType(funcDecl, chirType); // Global or member function. Must not be nested func. auto pkgName = GetNameOfDefinedPackage(funcDecl); auto mangledName = funcDecl.mangledName; // there is a strange func decl in macro related package, such as `std.unittest.testmacro.a` // func name is macroCall_[a/c]_{name}_{packageName}, it's compiler added and not expected to mangle // but its mangled name and src name must be different, or we will get duplicated mangled name after cffi wrapper // we need to fix this hack way if (funcDecl.TestAttr(AST::Attribute::NO_MANGLE) && funcTy->IsCFunc()) { mangledName += CFFI_FUNC_SUFFIX; } auto srcCodeName = funcDecl.identifier; auto rawMangledName = funcDecl.rawMangleName; fn = builder.CreateFunc(loc, funcTy, mangledName, srcCodeName, rawMangledName, pkgName, genericParamTy); // This is the logic that applied when compiling common part of package // Ideally such logic should be be visually discernible if (funcDecl.TestAttr(AST::Attribute::COMMON)) { fn->EnableAttr(Attribute::COMMON); if (outputCHIR && !funcDecl.TestAttr(AST::Attribute::COMMON_WITH_DEFAULT)) { fn->EnableAttr(Attribute::SKIP_ANALYSIS); } } BlockGroup* body = builder.CreateBlockGroup(*fn); fn->InitBody(*body); CJC_ASSERT(fn); SetFuncAttributeAndLinkageType(funcDecl, *fn); // for cjmp, want to serializer whole decl if (IsSrcCodeImportedGlobalDecl(funcDecl, opts) && opts.outputMode != GlobalOptions::OutputMode::CHIR) { srcCodeImportedFuncs.emplace(fn); } TranslateFuncParams(funcDecl, *fn); if (implicitDecls.count(&funcDecl) != 0) { implicitFuncs.emplace(fn->GetIdentifierWithoutPrefix(), fn); } globalCache.Set(funcDecl, *fn); // collect annotation info, and create anno factory func // do this here rather than in Func translation, because this function is run serialised but TranslateFuncDecl // is done in parallel if (funcDecl.TestAttr(AST::Attribute::GLOBAL) && !funcDecl.TestAttr(AST::Attribute::GENERIC_INSTANTIATED)) { auto tr = CreateTranslator(); tr.CreateAnnoFactoryFuncsForFuncDecl(funcDecl, nullptr); tr.CollectValueAnnotation(funcDecl); } } void AST2CHIR::CreatePseudoImportedFuncSignatureAndSetGlobalCache(const AST::FuncDecl& funcDecl) { // when the callee of callExpr is abstract func, will create a `Invoke` node, so we don't need to put abstract // func into the `globalCache`, if (funcDecl.TestAttr(AST::Attribute::ABSTRACT)) { return; } auto fnTy = chirType.TranslateType(*funcDecl.ty); fnTy = AdjustFuncType(*StaticCast(fnTy), funcDecl, builder, chirType); FuncType* funcTy = StaticCast(fnTy); auto genericParamTy = GetGenericParamType(funcDecl, chirType); // Global or member function. Must not be nested func. auto fn = builder.CreateImportedVarOrFunc(funcTy, funcDecl.mangledName, funcDecl.identifier, funcDecl.rawMangleName, funcDecl.fullPackageName, genericParamTy); CJC_ASSERT(fn); SetFuncAttributeAndLinkageType(funcDecl, *fn); CJC_ASSERT(funcDecl.funcBody->paramLists.size() == 1); auto& funcParams = funcDecl.funcBody->paramLists[0]->params; std::vector paramsInfo; size_t idx = 0; auto paramTys = StaticCast(funcDecl.ty)->paramTys; // NOTE: 'AnnoInfo' will be added during translating customDef. std::for_each(funcParams.begin(), funcParams.end(), [this, ¶msInfo, &idx, paramTys](const OwnedPtr& param) { paramsInfo.emplace_back( AbstractMethodParam{param->identifier, chirType.TranslateType(*paramTys[idx]), AnnoInfo()}); ++idx; }); if (IsInstanceMember(funcDecl)) { paramsInfo.insert(paramsInfo.begin(), {"this", funcTy->GetParamTypes()[0], {}}); } fn->SetParamInfo(std::move(paramsInfo)); if (implicitDecls.count(&funcDecl) != 0) { implicitFuncs.emplace(fn->GetIdentifierWithoutPrefix(), fn); } globalCache.Set(funcDecl, *fn); } void AST2CHIR::CreateImportedFuncSignatureAndSetGlobalCache(const AST::FuncDecl& funcDecl) { ImportedFunc* fn = TryGetDeserialized(funcDecl); if (fn) { globalCache.Set(funcDecl, *fn); if (implicitDecls.count(&funcDecl) != 0) { implicitFuncs.emplace(fn->GetIdentifierWithoutPrefix(), fn); } return; } bool isGeneric = funcDecl.TestAttr(AST::Attribute::GENERIC); if (isGeneric) { TranslateFunctionGenericUpperBounds(chirType, funcDecl); } auto fnTy = chirType.TranslateType(*funcDecl.ty); fnTy = AdjustFuncType(*StaticCast(fnTy), funcDecl, builder, chirType); auto genericParamTy = GetGenericParamType(funcDecl, chirType); fn = builder.CreateImportedVarOrFunc(fnTy, funcDecl.mangledName, funcDecl.identifier, funcDecl.rawMangleName, funcDecl.fullPackageName, genericParamTy); CJC_NULLPTR_CHECK(fn); auto loc = TranslateLocationWithoutScope(builder.GetChirContext(), funcDecl.begin, funcDecl.end); fn->SetDebugLocation(loc); SetFuncAttributeAndLinkageType(funcDecl, *fn); if (implicitDecls.count(&funcDecl) != 0) { implicitFuncs.emplace(fn->GetIdentifierWithoutPrefix(), fn); } // set param infos of imported func auto paramTys = StaticCast(funcDecl.ty)->paramTys; const auto& funcParams = funcDecl.funcBody->paramLists[0]->params; std::vector paramsInfo; size_t idx = 0; std::for_each(funcParams.begin(), funcParams.end(), [this, ¶msInfo, &idx, paramTys](const OwnedPtr& param) { paramsInfo.emplace_back(AbstractMethodParam{param->identifier, chirType.TranslateType(*paramTys[idx]), {}}); ++idx; }); FuncType* funcTy = StaticCast(fnTy); if (IsInstanceMember(funcDecl)) { paramsInfo.insert(paramsInfo.begin(), {"this", funcTy->GetParamTypes()[0], {}}); } fn->SetParamInfo(std::move(paramsInfo)); globalCache.Set(funcDecl, *fn); } void AST2CHIR::CreateImportedValueSignatureAndSetGlobalCache(const AST::VarDecl& varDecl) { ImportedVar* var = TryGetDeserialized(varDecl); if (var) { globalCache.Set(varDecl, *var); return; } auto varType = chirType.TranslateType(*varDecl.ty); auto refTy = builder.GetType(varType); var = builder.CreateImportedVarOrFunc(refTy, varDecl.mangledName, varDecl.identifier, varDecl.rawMangleName, varDecl.fullPackageName); CJC_NULLPTR_CHECK(var); var->AppendAttributeInfo(BuildAttr(varDecl.GetAttrs())); if (varDecl.IsConst()) { var->EnableAttr(Attribute::CONST); } globalCache.Set(varDecl, *var); } void AST2CHIR::CreateAndCacheGlobalVar(const AST::VarDecl& decl, bool isLocalConst) { if (auto gv = TryGetDeserialized(decl); gv) { globalCache.Set(decl, *gv); // for cjmp, want to serializer whole decl, it is a temp solution if (IsSrcCodeImportedGlobalDecl(decl, opts)) { srcCodeImportedVars.emplace(VirtualCast(gv)); } return; } auto loc = TranslateLocationWithoutScope(builder.GetChirContext(), decl.begin, decl.end); auto mangledName = decl.mangledName; auto srcCodeName = decl.identifier; auto rawMangledName = decl.rawMangleName; auto packageName = decl.fullPackageName; auto ty = builder.GetType(chirType.TranslateType(*decl.ty)); auto warnPos = GetVarLoc(builder.GetChirContext(), decl); Value* gv = nullptr; if (kind == IncreKind::INCR && !decl.toBeCompiled && !IsSrcCodeImportedGlobalDecl(decl, opts)) { gv = builder.CreateImportedVarOrFunc(ty, mangledName, srcCodeName, rawMangledName, packageName); } else { gv = builder.CreateGlobalVar(loc, ty, mangledName, srcCodeName, rawMangledName, packageName); if (isLocalConst) { // use COMPILER_ADD to mark this global const var as lifted gv->EnableAttr(Attribute::COMPILER_ADD); } } if (kind == IncreKind::INCR && !decl.toBeCompiled) { gv->EnableAttr(Attribute::NON_RECOMPILE); } gv->Set(warnPos); gv->AppendAttributeInfo(BuildVarDeclAttr(decl)); SetGlobalVarLinkageType(*gv, decl, creatingLocalConstVarSignature, opts.target.os == Triple::OSType::WINDOWS); if (decl.IsConst()) { gv->EnableAttr(Attribute::CONST); } if (IsSrcCodeImportedGlobalDecl(decl, opts) && opts.outputMode != GlobalOptions::OutputMode::CHIR) { srcCodeImportedVars.emplace(VirtualCast(gv)); } if (decl.TestAttr(AST::Attribute::COMMON)) { gv->EnableAttr(Attribute::COMMON); } globalCache.Set(decl, *gv); } void AST2CHIR::CacheTopLevelDeclToGlobalSymbolTable() { // create imported func decls and var decls. for (auto& decl : foreignFuncs) { CJC_ASSERT(decl->astKind == AST::ASTKind::FUNC_DECL); CreateImportedFuncSignatureAndSetGlobalCache(*StaticCast(decl)); } for (auto& decl : importedGlobalAndMemberFuncs) { CJC_ASSERT(decl->astKind == AST::ASTKind::FUNC_DECL); CreateImportedFuncSignatureAndSetGlobalCache(*StaticCast(decl)); } for (auto& decl : importedGlobalAndStaticVars) { CJC_ASSERT(decl->astKind == AST::ASTKind::VAR_DECL); CreateImportedValueSignatureAndSetGlobalCache(*StaticCast(decl)); } // create current packages's func decls and var decls. for (auto& decl : globalAndMemberFuncs) { CJC_ASSERT(decl->astKind == AST::ASTKind::FUNC_DECL); CreateFuncSignatureAndSetGlobalCache(*StaticCast(decl)); } CreateGlobalVarSignature(globalAndStaticVars); creatingLocalConstVarSignature = true; // collect Annotation of global vars auto tr = CreateTranslator(); for (auto var : globalAndStaticVars) { if (!var->TestAttr(AST::Attribute::STATIC)) { auto fn = tr.CreateAnnoFactoryFuncSig(*var, nullptr); if (fn.mangledName != "none" && Is(var)) { globalCache.Get(*var)->SetAnnoInfo(std::move(fn)); } tr.CollectValueAnnotation(*var); } } CreateGlobalVarSignature(localConstVars.stableOrderValue, true); creatingLocalConstVarSignature = false; for (auto decl : std::as_const(localConstFuncs.stableOrderValue)) { CreateFuncSignatureAndSetGlobalCache(*decl); } } void AST2CHIR::CreatePseudoDefForAnnoOnlyDecl(const AST::Decl& decl) { if (chirType.Has(decl) || globalCache.TryGet(decl)) { return; } auto tr = CreateTranslator(); CustomTypeDef* sym{}; if (auto cl = DynamicCast(&decl)) { sym = builder.CreateClass(tr.TranslateLocation(decl), decl.identifier.Val(), decl.mangledName, decl.fullPackageName, Is(cl), false); } if (auto cl = DynamicCast(&decl)) { sym = builder.CreateStruct( tr.TranslateLocation(decl), decl.identifier.Val(), decl.mangledName, decl.fullPackageName, false); } if (auto cl = DynamicCast(&decl)) { sym = builder.CreateEnum(tr.TranslateLocation(decl), decl.identifier.Val(), decl.mangledName, decl.fullPackageName, false, cl->hasEllipsis); } if (auto cl = DynamicCast(&decl)) { sym = builder.CreateExtend(tr.TranslateLocation(decl), decl.mangledName, decl.fullPackageName, false); } if (sym) { sym->EnableAttr(Attribute::SKIP_ANALYSIS); chirType.SetGlobalNominalCache(decl, *sym); return; } Value* val; if (auto func = DynamicCast(&decl)) { auto fn = builder.CreateFunc(INVALID_LOCATION, builder.GetType( std::vector{func->funcBody->paramLists[0]->params.size(), builder.GetInt64Ty()}, builder.GetUnitTy()), decl.mangledName, decl.mangledName, decl.rawMangleName, decl.fullPackageName); for (auto& pr : func->funcBody->paramLists[0]->params) { builder.CreateParameter(builder.GetInt64Ty(), tr.TranslateLocation(*pr), *fn); } val = fn; } if (auto var = DynamicCast(&decl)) { val = builder.CreateGlobalVar(tr.TranslateLocation(decl), builder.GetType(builder.GetInt64Ty()), decl.mangledName, decl.identifier, decl.rawMangleName, decl.fullPackageName); } // such function does not have body (because it is a pseudo func, just a hook for annoFactoryFunc). // skip all CHIR pass on it. val->EnableAttr(Attribute::SKIP_ANALYSIS); globalCache.Set(decl, *val); } void AST2CHIR::CreateAnnoOnlyDeclSig(const AST::Decl& decl) { // pseudo def of inheritable decls are create earlier in CacheCustomTypeDefToGlobalSymbolTable; do not create twice if (auto type = DynamicCast(&decl)) { for (auto& member : type->GetMemberDecls()) { CreateAnnoOnlyDeclSig(*member); } } else { CreatePseudoDefForAnnoOnlyDecl(decl); } auto tr = CreateTranslator(); if (auto funcDecl = DynamicCast(&decl)) { tr.CreateAnnoFactoryFuncsForFuncDecl(*funcDecl, nullptr); } else { auto fn = tr.CreateAnnoFactoryFuncSig(decl, nullptr); if (fn.mangledName != "none") { if (auto var = DynamicCast(&decl)) { globalCache.Get(*var)->SetAnnoInfo(std::move(fn)); } } } } static void SetCustomTypeDefAttr(CustomTypeDef& def, const AST::Decl& decl) { def.AppendAttributeInfo(BuildAttr(decl.GetAttrs())); if (auto classDef = DynamicCast(&def)) { if (decl.TestAttr(AST::Attribute::IS_ANNOTATION)) { classDef->SetAnnotation(true); } } else if (auto structDef = DynamicCast(&def)) { if (decl.TestAttr(AST::Attribute::C)) { structDef->SetCStruct(true); } } if (Interop::Java::IsImpl(decl)) { def.EnableAttr(Attribute::JAVA_IMPL); } else if (Interop::Java::IsMirror(decl)) { def.EnableAttr(Attribute::JAVA_MIRROR); } } void AST2CHIR::CreateCustomTypeDef(const AST::Decl& decl, bool isImported) { CJC_ASSERT(decl.IsNominalDecl()); CustomTypeDef* customTypeDef = nullptr; auto loc = TranslateLocationWithoutScope(builder.GetChirContext(), decl.begin, decl.end); auto& identifier = decl.identifier.Val(); auto& mangledName = decl.mangledName; auto& pkgName = decl.genericDecl ? decl.genericDecl->fullPackageName : decl.fullPackageName; AST::Decl* uniqueDecl = nullptr; switch (decl.astKind) { case AST::ASTKind::CLASS_DECL: case AST::ASTKind::INTERFACE_DECL: customTypeDef = TryGetDeserialized(decl); if (customTypeDef == nullptr) { customTypeDef = builder.CreateClass( loc, identifier, mangledName, pkgName, decl.astKind == AST::ASTKind::CLASS_DECL, isImported); } uniqueDecl = StaticCast(decl.ty)->commonDecl; break; case AST::ASTKind::STRUCT_DECL: customTypeDef = TryGetDeserialized(decl); if (customTypeDef == nullptr) { customTypeDef = builder.CreateStruct(loc, identifier, mangledName, pkgName, isImported); } uniqueDecl = StaticCast(decl.ty)->decl; break; case AST::ASTKind::ENUM_DECL: customTypeDef = TryGetDeserialized(decl); if (customTypeDef == nullptr) { customTypeDef = builder.CreateEnum( loc, identifier, mangledName, pkgName, isImported, StaticCast(decl).hasEllipsis); } uniqueDecl = StaticCast(decl.ty)->decl; break; case AST::ASTKind::EXTEND_DECL: { customTypeDef = TryGetDeserialized(decl); if (customTypeDef == nullptr) { auto gts = GetGenericParamType(decl, chirType); customTypeDef = builder.CreateExtend(loc, mangledName, pkgName, isImported, gts); } break; } default: break; } CJC_NULLPTR_CHECK(customTypeDef); // For incremental compilation, compiler added instantiated decl may need to be ignored. if (kind == Cangjie::IncreKind::INCR && decl.TestAttr(Cangjie::AST::Attribute::GENERIC_INSTANTIATED) && !decl.toBeCompiled) { customTypeDef->EnableAttr(Attribute::NON_RECOMPILE); } if (!customTypeDef->TestAttr(Attribute::DESERIALIZED)) { SetCustomTypeDefAttr(*customTypeDef, decl); } chirType.SetGlobalNominalCache(decl, *customTypeDef); if (uniqueDecl != nullptr && uniqueDecl != &decl) { chirType.SetGlobalNominalCache(*uniqueDecl, *customTypeDef); } if (decl.TestAttr(AST::Attribute::COMMON) && !decl.TestAttr(AST::Attribute::FROM_COMMON_PART)) { customTypeDef->EnableAttr(Attribute::COMMON); } if (decl.TestAttr(AST::Attribute::PLATFORM)) { customTypeDef->EnableAttr(Attribute::PLATFORM); customTypeDef->DisableAttr(Attribute::COMMON); } } void AST2CHIR::CacheCustomTypeDefToGlobalSymbolTable() { for (auto decl : nominalDecls) { CreateCustomTypeDef(*decl, false); } for (auto decl : genericNominalDecls) { CreateCustomTypeDef(*decl, false); } for (auto decl : importedNominalDecls) { CreateCustomTypeDef(*decl, true); } for (auto decl : importedGenericInstantiatedNominalDecls) { CreateCustomTypeDef(*decl, false); } for (auto decl : annoOnlyDecls) { if (Is(decl)) { CreatePseudoDefForAnnoOnlyDecl(*decl); } } } void AST2CHIR::TranslateAllCustomTypeTy() { auto translateNow = [](const AST::Decl& decl) { if (decl.astKind == AST::ASTKind::CLASS_DECL || decl.astKind == AST::ASTKind::INTERFACE_DECL || decl.astKind == AST::ASTKind::STRUCT_DECL || decl.astKind == AST::ASTKind::ENUM_DECL) { return true; } return false; }; std::unordered_set> translatedGenericDecls; for (auto decl : importedNominalDecls) { if (!translateNow(*decl)) { continue; } auto type = chirType.TranslateType(*(decl->ty)); if (decl->TestAttr(AST::Attribute::GENERIC)) { translatedGenericDecls.insert(decl); } if (decl->identifier == OBJECT_NAME && decl->fullPackageName == CORE_PACKAGE_NAME) { auto clsTy = StaticCast(StaticCast(type)->GetBaseType()); builder.SetObjectTy(clsTy); } if (decl->identifier == ANY_NAME && decl->fullPackageName == CORE_PACKAGE_NAME) { auto clsTy = StaticCast(StaticCast(type)->GetBaseType()); builder.SetAnyTy(clsTy); } } for (auto decl : genericNominalDecls) { if (translateNow(*decl)) { chirType.TranslateType(*(decl->ty)); translatedGenericDecls.emplace(decl); } } for (auto decl : nominalDecls) { if (!translateNow(*decl)) { continue; } auto type = chirType.TranslateType(*(decl->ty)); if (decl->identifier == OBJECT_NAME && decl->fullPackageName == CORE_PACKAGE_NAME) { auto clsTy = StaticCast(StaticCast(type)->GetBaseType()); builder.SetObjectTy(clsTy); } if (decl->identifier == ANY_NAME && decl->fullPackageName == CORE_PACKAGE_NAME) { auto clsTy = StaticCast(StaticCast(type)->GetBaseType()); builder.SetAnyTy(clsTy); } } // Translate upper bounds after normal decls have been translated. for (auto decl : translatedGenericDecls) { for (auto ty : decl->ty->typeArgs) { chirType.FillGenericArgType(*RawStaticCast(ty)); } } for (auto decl : importedGenericInstantiatedNominalDecls) { if (translateNow(*decl)) { chirType.TranslateType(*(decl->ty)); } } } static bool ParentIsOtherExtendGrandParent(const ClassType& parent, ExtendDef& curDef, CHIRBuilder& builder) { auto extendDefs = curDef.GetExtendedType()->GetExtends(&builder); Utils::RemoveFromVec(extendDefs, &curDef); std::unordered_set allGrandParents; for (auto def : extendDefs) { for (auto extendParent : def->GetImplementedInterfaceTys()) { auto grandParents = extendParent->GetSuperTypesRecusively(builder); allGrandParents.insert(grandParents.begin(), grandParents.end()); } } if (allGrandParents.find(&parent) != allGrandParents.end()) { return true; } auto curParentTypeArgs = parent.GetGenericArgs(); auto curParentDef = parent.GetClassDef(); for (auto grandParent : allGrandParents) { if (grandParent->GetClassDef() != curParentDef) { continue; } auto grandParentTypeArgs = grandParent->GetGenericArgs(); CJC_ASSERT(curParentTypeArgs.size() == grandParentTypeArgs.size()); for (size_t i = 0; i < curParentTypeArgs.size(); ++i) { auto typeArg1 = curParentTypeArgs[i]; auto typeArg2 = grandParentTypeArgs[i]; if (typeArg1 != typeArg2 && !typeArg1->IsGeneric() && !typeArg2->IsGeneric()) { return false; } } // I and I is type matched return true; } return false; } void AST2CHIR::UpdateExtendParent() { /* we will remove some parent from extend def. e.g. interface I1 { func foo() {println(1)} } interface I2 <: I1 { func foo() {println(2)} } class A {} extend A <: I1 { vtable { I1 -> I1::foo } } extend A <: I2 { vtable { I1 -> I2::foo } } what about result of `A().foo()` ? in this case, it's hard to say which `foo` will be called, I1::foo or I2::foo ? we need to let cangjie users know that it must be I2::foo, not I1::foo, so vtable in `extend A <: I1` must be removed, but it's not enough codegen will generate an empty extension def of `A_ed_I1` according to `extend A <: I1`, and a non-empty extension def of `A_ed_I1` according to `extend A <: I2` codegen need to know inheritance relationship by extension def, so even though there isn't vtable in `extend A <: I1` in CHIR stage, an empty extension def still be generated in codegen stage SO, we have to remove `I1` from `extend A <: I1` */ Utils::ProfileRecorder recorder("TranslateNominalDecls", "UpdateExtendParent"); for (auto customDef : package->GetAllCustomTypeDef()) { if (!customDef->IsExtend()) { continue; } auto extendDef = StaticCast(customDef); for (auto parent : extendDef->GetImplementedInterfaceTys()) { if (ParentIsOtherExtendGrandParent(*parent, *extendDef, builder)) { extendDef->RemoveParent(*parent); } } } } void AST2CHIR::SetExtendInfo() { Utils::ProfileRecorder recorder("TranslateNominalDecls", "SetExtendInfo"); for (auto customDef : package->GetAllCustomTypeDef()) { if (customDef->TestAttr(Attribute::GENERIC_INSTANTIATED)) { continue; } auto extendDef = DynamicCast(customDef); if (extendDef == nullptr) { continue; } if (auto builtinType = DynamicCast(extendDef->GetExtendedType())) { GetBuiltinTypeWithVTable(*builtinType, builder)->AddExtend(*extendDef); } else { auto customType = StaticCast(extendDef->GetExtendedType()); auto customTypeDef = customType->GetCustomTypeDef(); CJC_NULLPTR_CHECK(customTypeDef); CJC_ASSERT(customTypeDef->GetCustomKind() != CustomDefKind::TYPE_EXTEND); customTypeDef->AddExtend(*extendDef); } } } namespace { /** * Removes redundant deserialized extends, preserving only the most specific platform implementation. * Example: Compiling linux-x86 target: * // common.cj (common definitions) * common extend Int64 {} * common extend Int64 {} * * // linux.cj (platform-specific) * platform extend Int64 {} * * // linux-x86.cj (architecture-specific) * platform extend Int64 {} * * Produces after deserialization: * 4 extends (2 common, 1 platform from linux.cj, 1 platform from linux-x86.cj) * * This function removes the 3 deserialized extends, keeping only linux-x86.cj's version. * */ void RemoveUnusedCJMPExtends(CHIR::Package& chirPkg, bool compilePlatform) { if (!compilePlatform) { return; } auto extends = chirPkg.GetExtends(); auto it = std::remove_if(extends.begin(), extends.end(), [](const ExtendDef* ed) { CJC_NULLPTR_CHECK(ed); return ed->TestAttr(CHIR::Attribute::DESERIALIZED) && (ed->TestAttr(CHIR::Attribute::COMMON) || ed->TestAttr(CHIR::Attribute::PLATFORM)); }); extends.erase(it, extends.end()); chirPkg.SetExtends(std::move(extends)); } } // namespace void AST2CHIR::TranslateNominalDecls(const AST::Package& pkg) { Utils::ProfileRecorder recorder("TranslateAllDecls", "TranslateNominalDecls"); Utils::ProfileRecorder::Start("TranslateNominalDecls", "SetGenericFuncMap"); auto trans = CreateTranslator(); /** for AST, we can't get instantiated func decl in Nominal Decl * class A { * func foo(a: T) {} * } * if `foo` is instantiated with `Bool`, we can't get `foo(a: Bool)` from `ClassDecl A` * `foo(a: Bool)` is only stored in `pkg.genericInstantiatedDecls` * AST need to guarantee that there is only one member func in `class A` which is matched with src code */ std::unordered_map> genericFuncMap; for (auto& decl : pkg.genericInstantiatedDecls) { // only care about member func decl if (decl->astKind != AST::ASTKind::FUNC_DECL || decl->outerDecl == nullptr) { continue; } auto funcDecl = StaticCast(decl.get()); auto genericFunc = StaticCast(funcDecl->genericDecl); genericFuncMap[genericFunc].emplace_back(funcDecl); CJC_ASSERT(funcDecl->funcBody && !funcDecl->funcBody->paramLists.empty()); CJC_ASSERT(genericFunc->funcBody && !genericFunc->funcBody->paramLists.empty()); auto& funcParams = funcDecl->funcBody->paramLists[0]->params; auto& genericParams = genericFunc->funcBody->paramLists[0]->params; CJC_ASSERT(funcParams.size() == genericParams.size()); for (size_t i = 0; i < funcParams.size(); ++i) { if (!funcParams[i]->desugarDecl || funcParams[i]->desugarDecl->astKind != AST::ASTKind::FUNC_DECL) { continue; } genericFuncMap[StaticCast(genericParams[i]->desugarDecl.get())].emplace_back( StaticCast(funcParams[i]->desugarDecl.get())); } } trans.SetGenericFuncMap(genericFuncMap); Utils::ProfileRecorder::Stop("TranslateNominalDecls", "SetGenericFuncMap"); Utils::ProfileRecorder::Start("TranslateNominalDecls", "TranslateDecls"); // Translate all nominal decls. TranslateVecDecl(importedNominalDecls, trans); TranslateVecDecl(importedGenericInstantiatedNominalDecls, trans); TranslateVecDecl(nominalDecls, trans); TranslateVecDecl(genericNominalDecls, trans); // Update some info for nominal decls. Utils::ProfileRecorder::Stop("TranslateNominalDecls", "TranslateDecls"); RemoveUnusedCJMPExtends(*package, opts.inputChirFiles.size() == 1); SetExtendInfo(); UpdateExtendParent(); } void AST2CHIR::TranslateFuncParams(const AST::FuncDecl& funcDecl, Func& func) const { std::vector paramLoc; if (IsInstanceMember(funcDecl)) { paramLoc.emplace_back(INVALID_LOCATION); } for (auto& astParam : funcDecl.funcBody->paramLists[0]->params) { paramLoc.emplace_back(TranslateLocationWithoutScope(builder.GetChirContext(), astParam->begin, astParam->end)); } auto fnTy = func.GetFuncType(); CJC_NULLPTR_CHECK(fnTy); auto paramTypes = fnTy->GetParamTypes(); CJC_ASSERT(paramTypes.size() == paramLoc.size()); for (size_t i = 0; i < paramTypes.size(); ++i) { builder.CreateParameter(paramTypes[i], paramLoc[i], func); } } void AST2CHIR::TranslateVecDecl(const std::vector>& decls, Translator& trans) const { for (auto decl : decls) { if (!NeedTranslate(*decl)) { continue; } Translator::TranslateASTNode(*decl, trans); } } // Add methods for CJMP // Check whether the decl is deserialized for CJMP. bool AST2CHIR::MaybeDeserialized(const AST::Decl& decl) const { // When the platform is compiled and decl is from common part or generic instantiated or imported or platform decl. if (mergingPlatform && decl.TestAnyAttr(AST::Attribute::PLATFORM, AST::Attribute::FROM_COMMON_PART, AST::Attribute::GENERIC_INSTANTIATED, AST::Attribute::IMPORTED)) { return true; } if (decl.outerDecl && decl.outerDecl->TestAttr(AST::Attribute::GENERIC_INSTANTIATED)) { return true; } return false; } namespace { template typename std::enable_if && (... && std::is_same_v, Args>), void>::type BuildDeserializedVec(std::unordered_map& table, const std::vector& first, const Args&... args) { std::string id; for (auto v : first) { id = v->GetIdentifier(); // For foreign func we use rawMangledName as key if (v->TestAttr(Attribute::FOREIGN)) { // using rawmangledName if constexpr (std::is_base_of_v) { id = v->GetRawMangledName(); } else if constexpr (std::is_same_v) { if (auto fn = dynamic_cast(v)) { id = fn->GetRawMangledName(); } } } table.emplace(id, v); } if constexpr (sizeof...(args) > 0) { BuildDeserializedVec(table, args...); } } } void AST2CHIR::BuildDeserializedTable() { CJC_NULLPTR_CHECK(package); // build for CustomTypeDef std::vector defs; auto customDefs = package->GetAllCustomTypeDef(); auto importedCustomDefs = package->GetAllImportedCustomTypeDef(); BuildDeserializedVec(deserializedDefs, customDefs, importedCustomDefs); // build for Value BuildDeserializedVec(deserializedVals, package->GetImportedVarAndFuncs()); BuildDeserializedVec(deserializedVals, package->GetGlobalVars()); BuildDeserializedVec(deserializedVals, package->GetGlobalFuncs()); BuildDeserializedVec(deserializedVals, std::vector{package->GetPackageInitFunc(), package->GetPackageLiteralInitFunc()}); } // Reset platform func for CJMP. void AST2CHIR::ResetPlatformFunc(const AST::FuncDecl& funcDecl, Func& func) { // Reset body auto body = builder.CreateBlockGroup(func); func.ReplaceBody(*body); TranslateFuncParams(funcDecl, func); // Reset location const auto& loc = DebugLocation(TranslateLocationWithoutScope(builder.GetChirContext(), funcDecl.begin, funcDecl.end)); func.SetDebugLocation(loc); // Reset attrs: to do incremental change. SetFuncAttributeAndLinkageType(funcDecl, func); func.EnableAttr(Attribute::PLATFORM); func.DisableAttr(Attribute::COMMON); func.DisableAttr(Attribute::SKIP_ANALYSIS); } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/CMakeLists.txt000066400000000000000000000006521510705540100222450ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB AST2CHIR_SRC *.cpp) add_subdirectory(TranslateASTNode) add_subdirectory(CollectLocalConstDecl) set(CHIR_BASE_SRC ${CHIR_BASE_SRC} ${AST2CHIR_SRC} PARENT_SCOPE) cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/CollectLocalConstDecl/000077500000000000000000000000001510705540100236415ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/CollectLocalConstDecl/CMakeLists.txt000066400000000000000000000005741510705540100264070ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB COLLECT_LOCAL_CONST_DECL_SRC *.cpp) set(AST2CHIR_SRC ${AST2CHIR_SRC} ${COLLECT_LOCAL_CONST_DECL_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/CollectLocalConstDecl/CollectLocalConstDecl.cpp000066400000000000000000000160131510705540100305050ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/CollectLocalConstDecl/CollectLocalConstDecl.h" #include "cangjie/AST/Walker.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/Utils/CastingTemplate.h" using namespace Cangjie; using namespace CHIR; namespace { bool ShouldSkipSelf(const AST::FuncDecl& func, const std::vector& localConstFuncs, const int64_t& funcStack) { /** don't visit param default func of global func, because: * 1. if visited, its `funcStack` is 1, it will be collected to `localConstFuncs` by mistake * 2. it must be in `globalAndMemberFuncs`, and will be visited later * * func foo(a !: Bool = true) {} * while visiting `a`, foo's param, its `desugerDecl` will be also visited, so `funcStack` count wrong, * we should avoid this case */ if (func.ownerFunc != nullptr && funcStack == 1) { return true; } /** if its generic decl isn't lifted, itself shouldn't be lifted, either * func foo(a: T1) { * func goo() { * let x = a * } * goo() * } * func `goo` is instantiated, but its instantiated decl isn't in func foo's body, * so we can't skip visiting goo_Bool by skipping visiting foo's body, but goo's generic decl is in foo's body * so it works by checking its generic decl whether be lifted * in fact, it's expected that goo's instantiated decl can be in foo's body */ if (func.TestAttr(AST::Attribute::GENERIC_INSTANTIATED)) { auto gDecl = StaticCast(func.genericDecl); if (std::find(localConstFuncs.begin(), localConstFuncs.end(), gDecl) == localConstFuncs.end()) { return true; } } return false; } bool ShouldSkipChildren(const AST::FuncDecl& func) { /** don't visit member function, because `this` can be used in local const var or func * class CA { * const init() { const p = this } * } */ if (func.outerDecl != nullptr && Is(func.outerDecl)) { return true; } /** don't visit generic function, because generic type can be used in local func * func foo(a: T) { * const func goo() { * let x = a * } * } */ if (func.TestAttr(AST::Attribute::GENERIC)) { return true; } /** the same reason with generic function * class CA { * var a: T * func foo() { * const func goo() { * let x = a * } * } * } */ if (func.outerDecl != nullptr && func.outerDecl->TestAttr(AST::Attribute::GENERIC)) { return true; } /** don't visit local generic instantiated function, because local var's mangled name may be duplicated * func foo1() { * const func goo() { * const a = 1 * } * goo() * } * func foo2() { * const func goo() { * const a = 1 * } * goo() * } * in AST, foo1::goo and foo2::goo will be instantiated and stored in package.genericInstantiatedDecls, * not in foo1 or foo2's func body, that means foo1::goo and foo2::goo are regarded as global function, * so mangled names of local var `a` in them are same. This is a design bug in AST, and expected to be fixed */ if (func.TestAttr(AST::Attribute::GENERIC_INSTANTIATED) && IsLocalFunc(func)) { return true; } return false; } } void CollectLocalConstDecl::Collect(const std::vector>& decls, bool rootIsGlobalDecl) { /** collect local const func and var decls, except for some special decls * these special decls are listed in `ShouldSkipSelf` and `ShouldSkipChildren` */ int64_t funcStack = 0; auto preVisit = [this, &funcStack](Ptr n) -> AST::VisitAction { if (auto func = DynamicCast(n); func && ShouldSkipSelf(*func, localConstFuncDecls, funcStack)) { funcStack++; return AST::VisitAction::SKIP_CHILDREN; } if (funcStack != 0) { if (auto var = DynamicCast(n); var && var->IsConst()) { localConstVarDecls.emplace_back(var); } else if (auto func = DynamicCast(n); func && func->IsConst()) { localConstFuncDecls.emplace_back(func); CJC_ASSERT(func->funcBody); /** usually, param's desugar decl can be collected by visiting func body or `globalAndMemberFuncs`, * but for instantiated func decl, it doesn't work * func foo() { * func goo(a !: Bool = true) {} * goo() * } * for goo_Int32, a instantiated func decl, it's not in foo's body, we can only collect it by * visiting AST package's `genericInstantiatedDecls`, but `a_Int32.0`, a desugar instantiated decl * of goo_Int32's param, it's not in `genericInstantiatedDecls`, so we have to visit * `param->desugarDecl` to collect this kind of function * ps: decl in `genericInstantiatedDecls` is unique_ptr, and `param->desugarDecl` is also unique_ptr * so `param->desugarDecl` can't be moved to `genericInstantiatedDecls` */ for (auto& param : std::as_const(func->funcBody->paramLists[0]->params)) { if (param->desugarDecl) { localConstFuncDecls.emplace_back(param->desugarDecl.get()); } } } } if (Is(n) || Is(n) || Is(n)) { funcStack++; } if (auto func = DynamicCast(n); func && ShouldSkipChildren(*func)) { return AST::VisitAction::SKIP_CHILDREN; } return AST::VisitAction::WALK_CHILDREN; }; auto postVisit = [&funcStack](Ptr n) -> AST::VisitAction { if (Is(n) || Is(n) || Is(n)) { funcStack--; CJC_ASSERT(funcStack >= 0); } return AST::VisitAction::WALK_CHILDREN; }; for (auto& it : decls) { funcStack = rootIsGlobalDecl ? 0 : 1; AST::ConstWalker walker{it.get(), preVisit, postVisit}; walker.Walk(); } } const std::vector& CollectLocalConstDecl::GetLocalConstVarDecls() const { return localConstVarDecls; } const std::vector& CollectLocalConstDecl::GetLocalConstFuncDecls() const { return localConstFuncDecls; }cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/GlobalDeclAnalysis.cpp000066400000000000000000001101301510705540100236760ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/GlobalDeclAnalysis.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/Match.h" #include "cangjie/CHIR/Utils.h" #include #define DEBUG0 false using namespace Cangjie; using namespace Cangjie::CHIR; AST::VisitAction GlobalDeclAnalysis::CollectDepInCallExpr( const AST::CallExpr& callExpr, std::vector>& dependencies) { if (callExpr.resolvedFunction) { AddDependency(*callExpr.resolvedFunction, dependencies); } if (!callExpr.desugarArgs.has_value()) { return AST::VisitAction::WALK_CHILDREN; } // For those call of a function which has default value in some params, there will be implicitly // dependency to the parap default value function after desugar // // For example, the CJ code below // func foo(x: Int64 = 10) {} // let res = foo() // will be desugared into // func foo(x: Int64) {} // func foo_param_x_default_value() { 10 } // let res = foo(foo_param_x_default_value()) // thus, now `res` will also depends on `foo_param_x_default_value` auto& argExprs = callExpr.desugarArgs.value(); for (size_t i = 0; i < argExprs.size(); ++i) { if (argExprs[i]->TestAttr(AST::Attribute::HAS_INITIAL)) { CJC_NULLPTR_CHECK(callExpr.resolvedFunction); CJC_NULLPTR_CHECK(callExpr.resolvedFunction->funcBody.get()); CJC_NULLPTR_CHECK(callExpr.resolvedFunction->funcBody->paramLists[0]); CJC_NULLPTR_CHECK(callExpr.resolvedFunction->funcBody->paramLists[0]->params[i]); auto arg = callExpr.resolvedFunction->funcBody->paramLists[0]->params[i]->desugarDecl.get(); AddDependency(*arg, dependencies); } } return AST::VisitAction::WALK_CHILDREN; } AST::VisitAction GlobalDeclAnalysis::CollectDepInLitConstExpr( const AST::LitConstExpr& litConstExpr, std::vector>& dependencies) { if (litConstExpr.kind == AST::LitConstKind::JSTRING || !litConstExpr.ref) { return AST::VisitAction::WALK_CHILDREN; } auto structDecl = StaticCast(litConstExpr.ref->ref.target); if (!litConstExpr.stringValue.empty()) { return AST::VisitAction::WALK_CHILDREN; } // In later compilation process, we always translate an empty string literal into a reference of // `empty` variable defined in std.core library, so there will be implicitly dependency to the // `empty` variable then for (auto& decl : structDecl->body->decls) { if (decl->astKind == AST::ASTKind::VAR_DECL && decl->identifier == "empty" && decl->TestAttr(AST::Attribute::STATIC)) { AddDependency(*decl, dependencies); } } return AST::VisitAction::WALK_CHILDREN; } AST::VisitAction GlobalDeclAnalysis::VisitExprAction(const AST::Expr& expr, std::vector>& dependencies, std::vector>& localConstVarDeps) { if (auto ma = DynamicCast(&expr); ma) { if (!ma->target) { return AST::VisitAction::WALK_CHILDREN; } if (ma->target->astKind == AST::ASTKind::FUNC_DECL) { AddDependency(*ma->target, dependencies); } if (ma->target->astKind == AST::ASTKind::VAR_DECL && ma->target->TestAttr(AST::Attribute::STATIC)) { AddDependency(*ma->target, dependencies); } if (AST::IsPackageMemberAccess(*ma) && ma->target->astKind == AST::ASTKind::VAR_DECL) { AddDependency(*ma->target, dependencies); } return AST::VisitAction::WALK_CHILDREN; } else if (auto re = DynamicCast(&expr); re) { bool hasRefTarget = re->ref.target && (re->ref.target->astKind == AST::ASTKind::FUNC_DECL || re->ref.target->astKind == AST::ASTKind::VAR_DECL); if (hasRefTarget && (re->ref.target->TestAttr(AST::Attribute::GLOBAL) || re->ref.target->TestAttr(AST::Attribute::STATIC))) { AddDependency(*re->ref.target, dependencies); } else if (hasRefTarget && re->ref.target->IsConst()) { AddDependency(*re->ref.target, localConstVarDeps); } return AST::VisitAction::SKIP_CHILDREN; } else if (auto arrExpr = DynamicCast(&expr); arrExpr) { if (arrExpr->initFunc) { AddDependency(*arrExpr->initFunc, dependencies); } return AST::VisitAction::WALK_CHILDREN; } else if (auto arrLit = DynamicCast(&expr); arrLit) { if (arrLit->initFunc) { AddDependency(*arrLit->initFunc, dependencies); } return AST::VisitAction::WALK_CHILDREN; } else if (auto callExpr = DynamicCast(&expr); callExpr) { return CollectDepInCallExpr(*callExpr, dependencies); } else if (auto litConstExpr = DynamicCast(&expr); litConstExpr) { return CollectDepInLitConstExpr(*litConstExpr, dependencies); } return AST::VisitAction::WALK_CHILDREN; } void GlobalDeclAnalysis::WalkAndCollectDep(const AST::Node& curNode, std::vector>& dependencies, std::vector>& localConstVarDeps) { std::function)> visitor = [this, &visitor, &dependencies, &localConstVarDeps]( const Ptr n) { return Meta::match(*n)( [this, &visitor, &dependencies, &localConstVarDeps](AST::Expr& expr) { AST::Expr* exprPtr = &expr; if (exprPtr->desugarExpr != nullptr) { while (exprPtr->desugarExpr != nullptr) { exprPtr = exprPtr->desugarExpr.get(); } AST::Walker walker(exprPtr, visitor); walker.Walk(); return AST::VisitAction::SKIP_CHILDREN; } return VisitExprAction(expr, dependencies, localConstVarDeps); }, [&visitor](const AST::FuncParam& param) { if (param.assignment) { // If this is func param which has default value, we should always // skip walk the `assignment` part cause it has been desugared into // the `desugarDecl` part AST::Walker walker(param.desugarDecl, visitor); walker.Walk(); return AST::VisitAction::SKIP_CHILDREN; } return AST::VisitAction::WALK_CHILDREN; }, [](const AST::FuncArg& arg) { if (arg.TestAttr(AST::Attribute::HAS_INITIAL)) { // If this is func arg will be desugared into a call to the default value // func, we should also skip since we have handled them in the `callExpr` return AST::VisitAction::SKIP_CHILDREN; } return AST::VisitAction::WALK_CHILDREN; }, [this, &visitor](const AST::FuncDecl& func) { if (gim && func.TestAttr(AST::Attribute::GENERIC)) { auto instantiatedDecls = gim->GetInstantiatedDecls(func); for (auto& instance : instantiatedDecls) { AST::Walker walker(instance, visitor); walker.Walk(); } } return AST::VisitAction::WALK_CHILDREN; }, []() { return AST::VisitAction::WALK_CHILDREN; }); }; AST::Walker walker(const_cast(&curNode), visitor); walker.Walk(); } void GlobalDeclAnalysis::AnalysisDepOf(const AST::Decl& rootDecl, std::vector>& dependencies, std::vector>& localConstVarDeps) { WalkAndCollectDep(rootDecl, dependencies, localConstVarDeps); // Speical Case 1): if current node is a static init function, then we need to filter outer the usage of the // initialized static member variables // Special Case 2): if current node is a constructor, then we also need to consider the implicit dependency to // the default value of the member variables defined in the struct/class if (auto func = DynamicCast(&rootDecl); func && func->TestAttr(AST::Attribute::CONSTRUCTOR)) { if (func->TestAttr(AST::Attribute::STATIC)) { AdditionalAnalysisDepOfStaticInit(func, dependencies); } else { AdditionalAnalysisDepOfNonStaticCtor(*func, dependencies, localConstVarDeps); } } } bool GlobalDeclAnalysis::SkipDependencyTo(const AST::Decl& decl) { // 1) the target decls are only declarations which don't have body if (decl.TestAttr(AST::Attribute::FOREIGN) || decl.TestAttr(AST::Attribute::INTRINSIC) || decl.TestAttr(AST::Attribute::ENUM_CONSTRUCTOR)) { return true; } // 2) the target decls are imported which also don't have body. Note that we need to exclude the source imported // stuff if (decl.TestAttr(AST::Attribute::IMPORTED)) { if (!decl.TestAttr(AST::Attribute::SRC_IMPORTED)) { return true; } } // 3) now we do a search to check if the target decl is in our candidate list bool res = !funcsAndVars.HasElement(&decl) && !localConstVars.HasElement(&decl); return res; } void GlobalDeclAnalysis::AddDependencyImpl( const AST::Decl& depDecl, std::vector>& dependencies) const { if (std::find(dependencies.begin(), dependencies.end(), &depDecl) != dependencies.end()) { return; } dependencies.emplace_back(&depDecl); } void GlobalDeclAnalysis::AddDependency(const AST::Decl& depDecl, std::vector>& dependencies) { // If the target decl is a static variable which is initialized inside a `static init func`, // then we need to transfer the dependency to the `static init func` if (auto varDecl = DynamicCast(&depDecl)) { if (varDecl->TestAttr(AST::Attribute::STATIC) && varDecl->initializer == nullptr) { auto pos = staticInitFuncInfoMap.find(varDecl->outerDecl); if (pos != staticInitFuncInfoMap.end()) { AddDependencyImpl(*pos->second.staticInitVar, dependencies); return; } } } // If the variable declaration is from `VarPatternDecl`, we need to add the // dependency to the `VarPatternDecl` instead if (auto varDecl = DynamicCast(&depDecl); varDecl && varDecl->parentPattern) { auto pos = varPtternDeclMap.find(varDecl); if (pos == varPtternDeclMap.end()) { return; } AddDependencyImpl(*pos->second, dependencies); return; } if (SkipDependencyTo(depDecl)) { return; } AddDependencyImpl(depDecl, dependencies); } void GlobalDeclAnalysis::ParsePattern(const AST::VarWithPatternDecl& rootDecl, AST::Pattern& pattern) { if (auto tuplePattern = DynamicCast(&pattern); tuplePattern) { for (auto& tPattern : tuplePattern->patterns) { ParsePattern(rootDecl, *tPattern.get()); } } else if (auto enumPattern = DynamicCast(&pattern); enumPattern) { for (auto& ePattern : enumPattern->patterns) { ParsePattern(rootDecl, *ePattern.get()); } } else if (auto varPattern = DynamicCast(&pattern); varPattern) { varPtternDeclMap.emplace(varPattern->varDecl.get(), &rootDecl); } else if (auto wildPattern = DynamicCast(&pattern); wildPattern) { // Do nothing here } else { CJC_ABORT(); } } void GlobalDeclAnalysis::ParseVarWithPatternDecl(const AST::VarWithPatternDecl& root) { ParsePattern(root, *root.irrefutablePattern.get()); } void GlobalDeclAnalysis::AdditionalAnalysisDepOfNonStaticCtor(const AST::FuncDecl& func, std::vector>& dependencies, std::vector>& localConstVarDeps) { CJC_ASSERT(func.funcBody->parentClassLike != nullptr || func.funcBody->parentStruct != nullptr); const AST::InheritableDecl* parentDecl = nullptr; if (func.funcBody->parentClassLike) { parentDecl = func.funcBody->parentClassLike; } else { parentDecl = func.funcBody->parentStruct; } auto memberVars = GetNonStaticMemberVars(*parentDecl); for (auto memberVar : memberVars) { if (memberVar->initializer != nullptr) { WalkAndCollectDep(*memberVar, dependencies, localConstVarDeps); } } } /// Static member variable is a dependency if it's initialized in another file. /// This is required because of all direct initializers must be applied before static init, /// so files with direct initializers are dependencies for file with static init. static bool DoesStaticInitDependOnStaticVar(const Ptr& staticInit, Ptr &staticVar) { std::optional staticVarInitializerFileId = std::nullopt; if (staticVar->platformImplementation) { auto platformStaticVar = DynamicCast(staticVar->platformImplementation); CJC_NULLPTR_CHECK(platformStaticVar); if (platformStaticVar->initializer) { staticVarInitializerFileId = platformStaticVar->initializer->begin.fileID; } } else { if (staticVar->initializer) { staticVarInitializerFileId = staticVar->initializer->begin.fileID; } } auto staticInitFileId = staticInit->begin.fileID; return staticInitFileId != staticVarInitializerFileId; } void GlobalDeclAnalysis::AdditionalAnalysisDepOfStaticInit( const Ptr& staticInit, std::vector>& dependencies) const { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND CJC_ASSERT(IsStaticInit(*staticInit)); #endif // Get all the static member variables which are inited by this `static.init` func // and filter out the dependency to these static member variables auto parentDecl = DynamicCast(staticInit->outerDecl); CJC_NULLPTR_CHECK(parentDecl); auto staticVars = GetStaticMemberVars(*parentDecl); std::vector> filtedDependencies; for (auto& dep : dependencies) { if (std::find(staticVars.begin(), staticVars.end(), dep) != staticVars.end()) { continue; } filtedDependencies.emplace_back(dep); } for (auto& staticVar : staticVars) { if (DoesStaticInitDependOnStaticVar(staticInit, staticVar)) { filtedDependencies.emplace_back(staticVar); } } dependencies = filtedDependencies; } static void ReplaceCommonDependenciesWithPlatform(std::vector>& dependencies) { for (size_t i = 0; i < dependencies.size(); i++) { if (dependencies[i]->platformImplementation) { dependencies[i] = dependencies[i]->platformImplementation; } } } static void SaveCJMPDependencies(Ptr decl, std::vector>& dependencies) { for (auto dependency : dependencies) { decl->dependencies.emplace_back(dependency); } } static void RestoreCJMPDependencies(Ptr decl, std::vector>& dependencies) { for (auto dependency : decl->dependencies) { if (dependency->platformImplementation) { dependencies.emplace_back(dependency); dependencies.emplace_back(dependency->platformImplementation); } else { dependencies.emplace_back(dependency); } } } void GlobalDeclAnalysis::AnalysisDependency(const ElementList>& nodesWithDeps) { // We have to handle the `VarPatternDecl` specially, cause it may holds // multiple variable declarations, all the dependencies to these variables // should be transformed to be dependencies to the `VarPatternDecl` itself for (auto node : nodesWithDeps.stableOrderValue) { if (auto tuple = DynamicCast(node); tuple) { ParseVarWithPatternDecl(*tuple); } } for (auto node : nodesWithDeps.stableOrderValue) { // Note: what are we doing here? if (auto vd = DynamicCast(node); vd && vd->TestAttr(AST::Attribute::STATIC) && !vd->TestAttr(AST::Attribute::DEFAULT)) { // The dependencies of static variables which initialised in the static initialiser will be // collected when traversing the static initialiser. continue; } std::vector> dependencies; std::vector> localConstVarDeps; AnalysisDepOf(*node, dependencies, localConstVarDeps); if (outputChir) { SaveCJMPDependencies(node, dependencies); } if (mergingPlatform) { ReplaceCommonDependenciesWithPlatform(dependencies); RestoreCJMPDependencies(node, dependencies); } (void)std::remove_if(dependencies.begin(), dependencies.end(), [&node](const Ptr& element) { return element == node; }); funcsAndVarsDepMap.AddDeps(node, dependencies); localConstVarsDepMap.emplace(node, localConstVarDeps); } } void GlobalDeclAnalysis::AnalysisFileDependencyImpl(const Ptr& rootDecl, const Ptr& curDecl, std::unordered_set>& visited) { auto pos = funcsAndVarsDepMap.randomOrderValue.find(curDecl); if (pos == funcsAndVarsDepMap.randomOrderValue.end()) { CJC_ASSERT(curDecl->TestAttr(AST::Attribute::SRC_IMPORTED)); return; } const auto& directDepsOfCurDecl = pos->second; for (auto& dep : directDepsOfCurDecl) { if (visited.find(dep) != visited.end()) { continue; } visited.emplace(dep); // Generate file dependency if the `dep` is a variable, otherwise, we need to further traverse the decl // dependency to search if ((dep->astKind == AST::ASTKind::VAR_DECL || dep->astKind == AST::ASTKind::VAR_WITH_PATTERN_DECL)) { if (rootDecl->curFile == dep->curFile) { var2varDepsInFile[rootDecl].emplace(dep); } else { fileDependencyMap.AddDep(rootDecl->curFile, dep->curFile); } } else { AnalysisFileDependencyImpl(rootDecl, dep, visited); } } } void GlobalDeclAnalysis::AnalysisFileDependency() { for (auto& element : funcsAndVarsDepMap.stableOrderValue) { auto srcDecl = element.first; if (srcDecl->astKind != AST::ASTKind::VAR_DECL && srcDecl->astKind != AST::ASTKind::VAR_WITH_PATTERN_DECL) { continue; } std::unordered_set> visited; AnalysisFileDependencyImpl(srcDecl, srcDecl, visited); } } void GlobalDeclAnalysis::CheckUseBeforeInit(const std::vector>& vars) { std::unordered_set> alreadyInited; for (auto& var : vars) { alreadyInited.emplace(var); if (var->identifier == STATIC_INIT_VAR) { continue; } // Should not depends on any other vars initialized later auto pos = var2varDepsInFile.find(var); if (pos != var2varDepsInFile.end()) { auto& deps = pos->second; for (auto& dep : deps) { if (alreadyInited.find(dep) == alreadyInited.end()) { diag->Diagnose(DiagKind::chir_used_before_initialization, dep->identifier.Val()); } } } } } std::vector> GlobalDeclAnalysis::SortGlobalVarDep( const ElementList>& nodesWithDeps, const std::unordered_map, std::vector>>& fileAndVarMap, CompilationCache& cachedInfo) { // 1) Analysis the decl dependencies // Note: shall we do parallel analysis here? AnalysisDependency(nodesWithDeps); // 2) Cache and update the decl dependencies result for incremental compilation for (auto& element : cachedInfo.varAndFuncDep) { auto pos = funcsAndVarsDepMap.randomOrderValue.find(element.first); if (pos == funcsAndVarsDepMap.randomOrderValue.end()) { funcsAndVarsDepMap.AddDeps(element.first, element.second); } } cachedInfo.varAndFuncDep = funcsAndVarsDepMap.stableOrderValue; // 3) Map the decl dependencies into file dependencies // Init file dependency map, note that we will have files from upstream package included here for (auto& decl : funcsAndVars.stableOrderValue) { if (fileDependencyMap.randomOrderValue.find(decl->curFile) == fileDependencyMap.randomOrderValue.end()) { fileDependencyMap.InitDeps(decl->curFile); } } AnalysisFileDependency(); // 4) find whether files circular dependency exist and get sorted files in current package. std::vector diagLog; FileCirDepsChecker fileChecker(*diag, diagLog); fileChecker.InitFileDep(fileDependencyMap); fileChecker.DoTarjanSort(); fileChecker.UpdateSccUnit(fileDependencyMap.randomOrderValue); auto sortedFiles = fileChecker.GetOrderElement(); // Check if there is use before init issue which might not been recognized by sema in incremental compilation // There is use-before issue in std/tls/net lib if (kind == IncreKind::INCR) { for (auto element : fileAndVarMap) { CheckUseBeforeInit(element.second); } } return sortedFiles; } void GlobalDeclAnalysis::AnalysisGlocalVarsAndLocalConstVarsDependencyImpl(const Ptr& rootDecl, const Ptr& curDecl, std::unordered_set>& visited) { auto pos = funcsAndVarsDepMap.randomOrderValue.find(curDecl); CJC_ASSERT(pos != funcsAndVarsDepMap.randomOrderValue.end()); const auto& directDepsOfCurDecl = pos->second; for (auto& dep : directDepsOfCurDecl) { if (visited.find(dep) != visited.end()) { continue; } visited.emplace(dep); // Generate file dependency if the `dep` is a variable, otherwise, we need to further traverse the decl // dependency to search if ((dep->astKind == AST::ASTKind::VAR_DECL || dep->astKind == AST::ASTKind::VAR_WITH_PATTERN_DECL)) { globalVarsAndLocalConstVarsDepMap.AddDep(rootDecl, dep); } else { AnalysisGlocalVarsAndLocalConstVarsDependencyImpl(rootDecl, dep, visited); } } } void GlobalDeclAnalysis::AnalysisGlobalVarsAndLocalConstVarsDependency(const InitOrder& initOrder) { // Init the dependency map with the global vars dependencies, we will add a fake dependency // by the init order of the global vars Ptr previous = nullptr; for (auto& element : initOrder) { for (auto& gv : element.second) { if (previous != nullptr) { globalVarsAndLocalConstVarsDepMap.AddDep(gv, previous); } previous = gv; } } // Then we add the local vars dependecies into the map for (auto& localConstVar : localConstVars.stableOrderValue) { std::unordered_set> visited; AnalysisGlocalVarsAndLocalConstVarsDependencyImpl(localConstVar, localConstVar, visited); } } InitOrder GlobalDeclAnalysis::MergeLocalConstVarInitOrder( const InitOrder& initOrder, const std::vector> sortedVars) const { InitOrder finalInitOrder; size_t j = 0; for (auto& pair : initOrder) { auto& file = pair.first; auto& orignalVars = pair.second; if (orignalVars.empty()) { finalInitOrder.emplace_back(std::make_pair(file, std::vector>{})); continue; } size_t i = 0; std::vector> varsInCurFile; while (j < sortedVars.size()) { if (orignalVars[i] == sortedVars[j]) { ++i; } varsInCurFile.emplace_back(sortedVars[j]); ++j; if (i == orignalVars.size()) { break; } } while (i < orignalVars.size()) { varsInCurFile.emplace_back(orignalVars[i]); ++i; } finalInitOrder.emplace_back(std::make_pair(file, varsInCurFile)); } if (j < sortedVars.size()) { auto file = sortedVars[j]->curFile; if (finalInitOrder.empty()) { std::vector> varsInCurFile; while (j < sortedVars.size()) { varsInCurFile.emplace_back(sortedVars[j]); ++j; } finalInitOrder.emplace_back(std::make_pair(file, varsInCurFile)); } else { auto& varsInCurFile = finalInitOrder.back().second; while (j < sortedVars.size()) { varsInCurFile.emplace_back(sortedVars[j]); ++j; } } } return finalInitOrder; } InitOrder GlobalDeclAnalysis::SortLocalConstVarDep(const InitOrder& initOrder) { std::vector> globalVarsAndLocalConstVars; for (auto element : funcsAndVars.stableOrderValue) { if (element->astKind == AST::ASTKind::VAR_DECL || element->astKind == AST::ASTKind::VAR_WITH_PATTERN_DECL) { globalVarsAndLocalConstVars.emplace_back(element); } } if (globalVarsAndLocalConstVars.empty()) { return initOrder; } // 1) Analysis the local const decl dependencies AnalysisDependency(localConstVars); // 3) Map the decl dependencies into var-to-var dependencies for (auto element : localConstVarsDepMap) { auto& deps = funcsAndVarsDepMap.randomOrderValue[element.first]; deps.insert(deps.end(), element.second.begin(), element.second.end()); for (auto& pair : funcsAndVarsDepMap.stableOrderValue) { if (pair.first == element.first) { pair.second.insert(pair.second.end(), element.second.begin(), element.second.end()); } } } AnalysisGlobalVarsAndLocalConstVarsDependency(initOrder); // 4) sort all variables VarCirDepsChecker varDepsChecker(*diag); varDepsChecker.InitDependency(globalVarsAndLocalConstVars, globalVarsAndLocalConstVarsDepMap.randomOrderValue); varDepsChecker.DoTarjanSort(); varDepsChecker.UpdateSccUnit(globalVarsAndLocalConstVarsDepMap.randomOrderValue); auto sortedVars = varDepsChecker.GetOrderElement(); if (diag->GetErrorCount() != 0) { return initOrder; } auto finalInitOrder = MergeLocalConstVarInitOrder(initOrder, sortedVars); return finalInitOrder; } InitOrder GlobalDeclAnalysis::Run(const ElementList>& nodesWithDeps, const std::unordered_map, std::vector>>& fileAndVarMap, CompilationCache& cachedInfo) { auto orderedFiles = SortGlobalVarDep(nodesWithDeps, fileAndVarMap, cachedInfo); // No need to carry on if we find a circular dependency in files if (diag->GetErrorCount() != 0) { return InitOrder{}; } InitOrder initOrder; for (auto file : orderedFiles) { auto pos = fileAndVarMap.find(file); if (pos != fileAndVarMap.end()) { auto vars = pos->second; initOrder.emplace_back(file, vars); } else { initOrder.emplace_back(file, std::vector>{}); } } funcsAndVars.AddElements(localConstVars.stableOrderValue); auto finalInitOrder = SortLocalConstVarDep(initOrder); return finalInitOrder; } template TarjanSort::~TarjanSort() { } // using Tarjan's algorithm to group the SCC unit template void TarjanSort::DoTarjanSort() { std::stack*> st; int index = 1; for (auto unit : units) { if (visitedUnits.find(unit) == visitedUnits.end()) { TarjanSortImpl(*unit, index, st); } } } template void TarjanSort::InitDependency( const std::vector& rawElements, const std::unordered_map>& dependencyMap) { for (auto& element : rawElements) { auto unit = std::make_unique>(element); units.emplace_back(unit.get()); node2UnitMap.emplace(element, unit.get()); allUnits.emplace_back(std::move(unit)); } for (auto& element : rawElements) { if (dependencyMap.count(element) == 0) { continue; } auto varUnit = node2UnitMap[element]; for (auto dep : dependencyMap.at(element)) { auto tempUnit = node2UnitMap[dep]; varUnit->depsUnits.emplace_back(tempUnit); (void)tempUnit->userUnits.emplace_back(varUnit); } } } template std::vector TarjanSort::GetOrderElement() { std::vector sortedNodes; std::stack*> freeNodes; // 1) find nodes which have no in-dependency and others depend on it. for (auto unit : newSccUnits) { if (unit->depsUnits.empty()) { freeNodes.push(unit); } } // 2) sort the free nodes first and update the dependency map while (!freeNodes.empty()) { auto curFreeUnit = freeNodes.top(); freeNodes.pop(); for (auto ele : curFreeUnit->circularDepElements) { sortedNodes.emplace_back(ele); } for (auto& unit : curFreeUnit->userUnits) { // Delete curFreeUnit and delete it in depsUnits. auto& deps = unit->depsUnits; auto posA = std::find(deps.begin(), deps.end(), curFreeUnit); if (posA == deps.end()) { continue; } deps.erase(posA); if (deps.empty()) { freeNodes.push(unit); } } } visitedUnits.clear(); node2UnitMap.clear(); node2SccUnitMap.clear(); newSccUnits.clear(); return sortedNodes; } template void TarjanSort::UpdateSccUnitDeps(DepsUnit& unit, std::unordered_map>& dependencyMap) { auto& cirElements = unit.circularDepElements; for (auto element : cirElements) { auto deps = dependencyMap[element]; for (auto& dep : deps) { if (std::find(cirElements.begin(), cirElements.end(), dep) == cirElements.end()) { unit.AddDeps(*node2SccUnitMap[dep]); node2SccUnitMap[dep]->AddUsers(unit); } } } } template void TarjanSort::UpdateSccUnit(std::unordered_map>& dependencyMap) { // update CircularDepElements for sccUnits for (auto unit : newSccUnits) { UpdateSccUnitDeps(*unit, dependencyMap); } #if DEBUG0 if (!newSccUnits.empty()) { std::cout << "============== SccUnits Result===============" << std::endl; for (size_t i = 0; i < newSccUnits.size(); i++) { auto elements = newSccUnits[i]->circularDepElements; std::string curSccStr; for (auto iter = elements.begin(); iter != elements.end(); iter++) { curSccStr = iter == elements.begin() ? GetEleStr(*iter) : curSccStr + " and " + GetEleStr(*iter); } std::cout << "SccUnit " << i << " has members : [" << curSccStr << "] " << std::endl; std::string depSccUnit; for (auto depScc : newSccUnits[i]->depsUnits) { auto iter = std::find(newSccUnits.begin(), newSccUnits.end(), depScc); int index = std::distance(newSccUnits.begin(), iter); depSccUnit = " SccUnit " + std::to_string(index) + depSccUnit; } std::cout << "depends on " << depSccUnit << std::endl; } std::cout << "=========================================" << std::endl; } #endif } template void TarjanSort::TarjanSortImpl(DepsUnit& unit, int& index, std::stack*>& st) { unit.disc = index; unit.low = index; ++index; st.push(&unit); unit.stackMember = true; visitedUnits.insert(&unit); for (auto dep : unit.depsUnits) { if (dep->disc == -1) { // For the dep nodes which are not visited yet, traverse them recursively // and update the `low` value based on the traverse result TarjanSortImpl(*dep, index, st); unit.low = std::min(unit.low, dep->low); } else { // For the dep nodes which are already visited and also in the stack if (dep->stackMember) { unit.low = std::min(unit.low, dep->disc); } } } // If the `disc` and `low` value is equal, which means it is the root of the SCC, // then we can pop out the stack the get the whole set of the SCC. if (unit.disc == unit.low) { diagUnits.emplace_back(&unit); auto newsccUnit = std::make_unique>(unit); newsccUnit->depsUnits.clear(); node2SccUnitMap[unit.element] = newsccUnit.get(); newsccUnit->circularDepElements.emplace_back(unit.element); while (st.top() != &unit) { auto topUnit = st.top(); topUnit->stackMember = false; diagUnits.emplace_back(topUnit); node2SccUnitMap[topUnit->element] = newsccUnit.get(); newsccUnit->circularDepElements.emplace_back(topUnit->element); st.pop(); } auto topUnit = st.top(); topUnit->stackMember = false; st.pop(); DetectCircularDep(); newSccUnits.emplace_back(newsccUnit.get()); allNewSccUnits.emplace_back(std::move(newsccUnit)); } diagUnits.clear(); } VarCirDepsChecker::VarCirDepsChecker(DiagAdapter& ciDiag) : diag(&ciDiag) { } bool VarCirDepsChecker::IsVar(const AST::Decl& decl) const { if (decl.astKind == AST::ASTKind::VAR_DECL || decl.astKind == AST::ASTKind::VAR_WITH_PATTERN_DECL) { return true; } return false; } void VarCirDepsChecker::DetectCircularDep() { if (diagUnits.size() <= 1) { return; } bool hasVar = false; for (auto iter = diagUnits.begin(); iter != diagUnits.end(); ++iter) { if (IsVar(*(*iter)->element)) { hasVar = true; break; } } // Beware: If one or more var in circular dependency, we need to diag if (!hasVar) { return; } std::string diagStr; for (auto iter = diagUnits.begin(); iter != diagUnits.end(); ++iter) { auto decl = StaticCast((*iter)->element); std::string varName = decl->identifier; auto line = ":" + std::to_string((*iter)->element->begin.line); auto column = ":" + std::to_string((*iter)->element->begin.column); std::string temp = varName + " in " + (*iter)->element->curFile->fileName + line + column; if (iter == diagUnits.begin()) { diagStr = temp; } else { diagStr = diagStr + " and " + temp; } } diag->Diagnose(DiagKind::chir_var_might_circular_dependency, diagStr); } FileCirDepsChecker::FileCirDepsChecker(DiagAdapter& ciDiag, const std::vector& vardiagLog) : diag(&ciDiag), diagLog(vardiagLog) { } void FileCirDepsChecker::InitFileDep(const FileDepMap& depMap) { std::vector> files; for (auto& dep : depMap.stableOrderValue) { files.emplace_back(dep.first); } InitDependency(files, depMap.randomOrderValue); } void FileCirDepsChecker::DetectCircularDep() { if (diagUnits.size() <= 1) { return; } std::string diagStr; for (auto iter = diagUnits.begin(); iter != diagUnits.end(); ++iter) { std::string fileName = (*iter)->element->fileName; if (iter == diagUnits.begin()) { diagStr = fileName; } else { diagStr = diagStr + " and " + fileName; } } diag->Diagnose(DiagKind::chir_file_might_circular_dependency, diagStr); #if DEBUG0 for (auto strlog : diagLog) { std::cout << strlog << std::endl; } #endif } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/GlobalVarInitializer.cpp000066400000000000000000001071761510705540100242770ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/GlobalVarInitializer.h" #include "cangjie/AST/Node.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/CHIR/Annotation.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Utils.h" using namespace Cangjie; using namespace CHIR; namespace { inline std::string GetPackageInitFuncName(const std::string& pkgName, const std::string& suffix = "") { auto specialName = SPECIAL_NAME_FOR_INIT_FUNCTION; if (suffix == "_literal") { specialName = SPECIAL_NAME_FOR_INIT_LITERAL_FUNCTION; } else if (suffix == "_importsInit") { specialName = SPECIAL_NAME_FOR_IMPORTS_INIT_FUNCTION; } else if (suffix == "_literal_importsInit") { specialName = SPECIAL_NAME_FOR_LITERAL_IMPORTS_INIT_FUNCTION; #ifdef NDEBUG } else if (suffix != "") { CJC_ABORT(); #endif } return MANGLE_CANGJIE_PREFIX + MANGLE_GLOBAL_PACKAGE_INIT_PREFIX + MangleUtils::GetOptPkgName(pkgName) + specialName + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX; } inline std::string GetFileInitFuncName( const std::string& pkgName, const std::string& fileName, const std::string_view suffix) { auto specialName = suffix.empty() ? SPECIAL_NAME_FOR_INIT_FUNCTION : SPECIAL_NAME_FOR_INIT_LITERAL_FUNCTION; return MANGLE_CANGJIE_PREFIX + MANGLE_GLOBAL_FILE_INIT_PREFIX + MangleUtils::GetOptPkgName(pkgName) + MANGLE_FILE_ID_PREFIX + (BaseMangler::IsHashable(fileName) ? BaseMangler::HashToBase62(fileName) : (BaseMangler::FileNameWithoutExtension(fileName) + "$")) + specialName + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX; } inline std::string GetGVInitFuncName(const std::string& gvName, const std::string& pkgName) { return MANGLE_CANGJIE_PREFIX + MANGLE_GLOBAL_VARIABLE_INIT_PREFIX + MangleUtils::GetOptPkgName(pkgName) + MangleUtils::MangleName(gvName) + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX; } inline std::string GetVarInitName(const AST::VarDecl& var) { return MANGLE_CANGJIE_PREFIX + MANGLE_GLOBAL_VARIABLE_INIT_PREFIX + var.mangledName.substr((MANGLE_CANGJIE_PREFIX + MANGLE_NESTED_PREFIX).size(), var.mangledName.size() - (MANGLE_CANGJIE_PREFIX + MANGLE_NESTED_PREFIX + MANGLE_SUFFIX).size()) + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX; } void FlattenPatternName(const AST::Pattern& pattern, std::string& name) { switch (pattern.astKind) { case AST::ASTKind::VAR_PATTERN: name += ":" + StaticCast(&pattern)->varDecl->identifier; break; case AST::ASTKind::TUPLE_PATTERN: { auto tuplePattern = StaticCast(&pattern); for (auto& p : tuplePattern->patterns) { FlattenPatternName(*p, name); } break; } case AST::ASTKind::ENUM_PATTERN: { auto enumPattern = StaticCast(&pattern); for (auto& p : enumPattern->patterns) { FlattenPatternName(*p, name); } break; } case AST::ASTKind::WILDCARD_PATTERN: name += ":_"; break; default: break; } } inline std::string GenerateWildcardVarIdent(const AST::Pattern& pattern) { return GV_INIT_WILDCARD_PATTERN + std::to_string(pattern.begin.line) + "_" + std::to_string(pattern.begin.column); } std::string GetGVInitNameForPattern(const AST::VarWithPatternDecl& decl) { if (decl.irrefutablePattern->astKind == AST::ASTKind::TUPLE_PATTERN) { std::string nameSuffix; FlattenPatternName(*decl.irrefutablePattern, nameSuffix); return GetGVInitFuncName("tuple_pattern", decl.fullPackageName) + nameSuffix; } else if (decl.irrefutablePattern->astKind == AST::ASTKind::ENUM_PATTERN) { std::string nameSuffix; FlattenPatternName(*decl.irrefutablePattern, nameSuffix); return GetGVInitFuncName("enum_pattern", decl.fullPackageName) + nameSuffix; } else if (decl.irrefutablePattern->astKind == AST::ASTKind::WILDCARD_PATTERN) { return GetGVInitFuncName(GenerateWildcardVarIdent(*decl.irrefutablePattern), decl.fullPackageName); } else { CJC_ABORT(); return ""; } } template struct GVInit; // the primary template is never used and therefore never defined template <> struct GVInit { explicit GVInit(const AST::Package& pkg, const Translator&, const std::string& suffix = "") : srcCodeIdentifier(GetPackageInitFuncName(pkg.fullPackageName, suffix)), mangledName(srcCodeIdentifier), rawMangledName(""), packageName(pkg.fullPackageName) { } public: // non-const, to be moved from std::string srcCodeIdentifier; std::string mangledName; std::string rawMangledName; std::string packageName; static constexpr Linkage LINKAGE = Linkage::EXTERNAL; DebugLocation loc = INVALID_LOCATION; bool isConst{false}; }; template <> struct GVInit { explicit GVInit(const AST::File& file, const Translator& trans, const std::string& suffix = "") : packageName(file.curPackage->fullPackageName), srcCodeIdentifier(GetFileInitFuncName(packageName, file.fileName, suffix)), mangledName(srcCodeIdentifier), rawMangledName(""), loc(trans.TranslateFileLocation(file.begin.fileID)) { } public: std::string packageName; std::string srcCodeIdentifier; std::string mangledName; std::string rawMangledName; static constexpr Linkage LINKAGE = Linkage::INTERNAL; DebugLocation loc; bool isConst{false}; }; template <> struct GVInit { explicit GVInit(const AST::VarDecl& var, const Translator& trans, const std::string& suffix = "") : srcCodeIdentifier("gv$_" + var.identifier + suffix), mangledName(GetVarInitName(var)), rawMangledName(var.rawMangleName), packageName(var.fullPackageName), loc(trans.TranslateLocation(var)), isConst(var.IsConst()) { } public: std::string srcCodeIdentifier; std::string mangledName; std::string rawMangledName; std::string packageName; static constexpr Linkage LINKAGE = Linkage::INTERNAL; DebugLocation loc; bool isConst; }; template <> struct GVInit { explicit GVInit(const AST::VarWithPatternDecl& decl, const Translator& trans) : srcCodeIdentifier(GetGVInitNameForPattern(decl)), mangledName(MANGLE_CANGJIE_PREFIX + MANGLE_GLOBAL_VARIABLE_INIT_PREFIX + decl.mangledName.substr((MANGLE_CANGJIE_PREFIX + MANGLE_NESTED_PREFIX).size(), decl.mangledName.size() - (MANGLE_CANGJIE_PREFIX + MANGLE_NESTED_PREFIX + MANGLE_SUFFIX).size()) + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX), rawMangledName(decl.rawMangleName), packageName(decl.fullPackageName), loc(trans.TranslateLocation(decl)), isConst(decl.IsConst()) { } public: std::string srcCodeIdentifier; std::string mangledName; std::string rawMangledName; std::string packageName; static constexpr Linkage LINKAGE = Linkage::INTERNAL; DebugLocation loc; bool isConst; }; static bool IsSimpleLiteralValue(const AST::Expr& node) { auto realNode = Translator::GetDesugaredExpr(node); if (realNode->astKind != AST::ASTKind::LIT_CONST_EXPR) { return false; } switch (realNode->ty->kind) { case AST::TypeKind::TYPE_FLOAT16: case AST::TypeKind::TYPE_FLOAT64: case AST::TypeKind::TYPE_IDEAL_FLOAT: case AST::TypeKind::TYPE_FLOAT32: case AST::TypeKind::TYPE_UINT8: case AST::TypeKind::TYPE_UINT16: case AST::TypeKind::TYPE_UINT32: case AST::TypeKind::TYPE_UINT64: case AST::TypeKind::TYPE_UINT_NATIVE: case AST::TypeKind::TYPE_INT8: case AST::TypeKind::TYPE_INT16: case AST::TypeKind::TYPE_INT32: case AST::TypeKind::TYPE_INT64: case AST::TypeKind::TYPE_INT_NATIVE: case AST::TypeKind::TYPE_IDEAL_INT: case AST::TypeKind::TYPE_RUNE: case AST::TypeKind::TYPE_BOOLEAN: return true; case AST::TypeKind::TYPE_STRUCT: return node.ty->IsString(); default: return false; } } static Ptr GetBlockWithInitializers(const BlockGroup& blockGroup) { for (auto block : blockGroup.GetBlocks()) { if (block->TestAttr(Attribute::INITIALIZER)) { return block; } } CJC_ABORT(); // there is no block related to initalization process return nullptr; } bool NeedInitGlobalVarByInitFunc(const AST::VarDecl& decl) { // If decl without initializer (like common let x: Int64). if (!decl.initializer) { return false; } // common/specific cannot be initialized with literal value, // it can change to non literal initializer in the future. // So, initializer function need to be generated for such variables. if (decl.IsCommonOrPlatform()) { return true; } return !(IsSimpleLiteralValue(*decl.initializer) && decl.ty == decl.initializer->ty); } inline bool CanInitBeGenerated(const AST::VarDeclAbstract& varDecl) { return varDecl.initializer; } /// Looking for a Block with Attribute::INITIALIZER, remove its Exit node. inline Ptr DropExitNodeOfInitializer(const Func& packageInit) { CJC_ASSERT(packageInit.TestAttr(Attribute::INITIALIZER)); auto packageInitBody = packageInit.GetBody(); auto blockWithInitializers = GetBlockWithInitializers(*packageInitBody); // drop Exit node auto exit = blockWithInitializers->GetTerminator(); exit->RemoveSelfFromBlock(); return blockWithInitializers; } } // namespace Ptr GlobalVarInitializer::GetGlobalVariable(const AST::VarDecl& decl) { return globalSymbolTable.Get(decl); } template Ptr GlobalVarInitializer::CreateGVInitFunc(const T& node, Args&&... args) const { auto context = GVInit(node, trans, std::forward(args)...); bool isConst{false}; // No debug location generated for const var initializer func // because these exprs will not be executed at runtime and falsely contribute to coverage if constexpr (std::is_same_v) { isConst = node.IsConst(); } return trans.CreateEmptyGVInitFunc(std::move(context.mangledName), std::move(context.srcCodeIdentifier), std::move(context.rawMangledName), std::move(context.packageName), GVInit::LINKAGE, isConst ? INVALID_LOCATION : std::move(context.loc), context.isConst); } Func* GlobalVarInitializer::TranslateInitializerToFunction(const AST::VarDecl& decl) { auto variable = GetGlobalVariable(decl); auto func = CreateGVInitFunc(decl); if (auto globalVar = DynamicCast(variable)) { globalVar->SetInitFunc(*func); } // No debug location generated for const var initializer auto initNode = trans.TranslateExprArg( *decl.initializer, *StaticCast(variable->GetType())->GetBaseType()); if (decl.IsConst()) { for (auto expr : trans.currentBlock->GetExpressions()) { expr->SetDebugLocation(INVALID_LOCATION); } } auto loc = decl.IsConst() ? INVALID_LOCATION : trans.TranslateLocation(decl); CJC_ASSERT(variable->GetType()->IsRef()); auto expectedTy = StaticCast(variable->GetType())->GetBaseType(); if (initNode->GetType() != expectedTy) { initNode = TypeCastOrBoxIfNeeded(*initNode, *expectedTy, builder, *trans.GetCurrentBlock(), loc, true); } trans.CreateAndAppendExpression(loc, builder.GetUnitTy(), initNode, variable, trans.GetCurrentBlock()); auto curBlock = trans.GetCurrentBlock(); if (curBlock->GetTerminator() == nullptr) { trans.CreateAndAppendTerminator(curBlock); } return func; } bool GlobalVarInitializer::IsIncrementalNoChange(const AST::VarDecl& decl) const { return enableIncre && !decl.toBeCompiled; } ImportedFunc* GlobalVarInitializer::TranslateIncrementalNoChangeVar(const AST::VarDecl& decl) { GVInit context{decl, trans}; auto ty = builder.GetType(std::vector{}, builder.GetVoidTy()); auto func = builder.CreateImportedVarOrFunc(ty, std::move(context.mangledName), std::move(context.srcCodeIdentifier), std::move(context.rawMangledName), context.packageName); func->SetFuncKind(FuncKind::GLOBALVAR_INIT); func->Set(Cangjie::Linkage::INTERNAL); if (decl.isConst) { func->EnableAttr(Attribute::CONST); } return func; } FuncBase* GlobalVarInitializer::TranslateSingleInitializer(const AST::VarDecl& decl) { if (!CanInitBeGenerated(decl)) { return nullptr; } if (decl.platformImplementation) { return nullptr; } if (auto func = TryGetDeserialized(GetVarInitName(decl)); func) { if (!decl.TestAttr(AST::Attribute::PLATFORM)) { return func; } } // The variable with literal init value is hanlded in elsewhere if (!NeedInitGlobalVarByInitFunc(decl)) { return nullptr; } if (IsIncrementalNoChange(decl) && !IsSrcCodeImportedGlobalDecl(decl, opts)) { if (decl.identifier == STATIC_INIT_VAR) { // for static variables to be inited in static ctors, the special variable "$init" falls in this branch // (as it has a default value "false") return TranslateInitializerToFunction(decl); } else { // For a non-recompile variable which is not inited in `static.init` func, after the variable itself is // translated into a pseudo import. As for the init func, we should also create a pseudo import for it // and make sure the package init func will call this imported init return TranslateIncrementalNoChangeVar(decl); } } else { return TranslateInitializerToFunction(decl); } } void GlobalVarInitializer::FillGVInitFuncWithApplyAndExit(const std::vector>& varInitFuncs) { auto curBlock = trans.GetCurrentBlock(); for (auto& func : varInitFuncs) { trans.GenerateFuncCall(*func, StaticCast(func->GetType()), std::vector{}, nullptr, std::vector{}, INVALID_LOCATION); } trans.CreateAndAppendTerminator(curBlock); } Func* GlobalVarInitializer::TranslateTupleOrEnumPatternInitializer(const AST::VarWithPatternDecl& decl) { auto func = CreateGVInitFunc(decl); if (decl.IsConst()) { func->SetDebugLocation(INVALID_LOCATION); } auto initNode = Translator::TranslateASTNode(*decl.initializer, trans); trans.FlattenVarWithPatternDecl(*decl.irrefutablePattern, initNode, false); if (decl.IsConst()) { for (auto bl : func->GetBody()->GetBlocks()) { for (auto expr : bl->GetExpressions()) { expr->SetDebugLocation(INVALID_LOCATION); } } } auto curBlock = trans.GetCurrentBlock(); if (curBlock->GetTerminator() == nullptr) { trans.CreateAndAppendTerminator(curBlock); } return func; } Func* GlobalVarInitializer::TranslateWildcardPatternInitializer(const AST::VarWithPatternDecl& decl) { auto func = CreateGVInitFunc(decl); Translator::TranslateASTNode(*decl.initializer, trans); auto curBlock = trans.GetCurrentBlock(); if (curBlock->GetTerminator() == nullptr) { trans.CreateAndAppendTerminator(curBlock); } return func; } Func* GlobalVarInitializer::TranslateVarWithPatternInitializer(const AST::VarWithPatternDecl& decl) { switch (decl.irrefutablePattern->astKind) { case AST::ASTKind::TUPLE_PATTERN: case AST::ASTKind::ENUM_PATTERN: return TranslateTupleOrEnumPatternInitializer(decl); case AST::ASTKind::WILDCARD_PATTERN: { return TranslateWildcardPatternInitializer(decl); } default: CJC_ABORT(); return nullptr; } } FuncBase* GlobalVarInitializer::TranslateVarInit(const AST::Decl& var) { if (auto vd = DynamicCast(&var)) { return TranslateSingleInitializer(*vd); } else if (auto vwpd = DynamicCast(&var)) { return TranslateVarWithPatternInitializer(*vwpd); } else { CJC_ABORT(); return nullptr; } } Ptr GlobalVarInitializer::TryGetFileInitialializer(const AST::File& file, const std::string& suffix) { auto initializerName = GetFileInitFuncName(file.curPackage->fullPackageName, file.fileName, suffix); return TryGetDeserialized(initializerName); } void GlobalVarInitializer::RemoveInitializerForVarDecl(const AST::VarDecl& varDecl, Func& fileInit) const { auto declInitName = "@" + GetVarInitName(varDecl); auto blockWithInitializers = fileInit.GetBody()->GetBlocks().back(); Expression* init = nullptr; for (auto expr : blockWithInitializers->GetExpressions()) { auto kind = expr->GetExprKind(); if (kind != ExprKind::APPLY) { continue; } auto apply = StaticCast(expr); auto initFunc = apply->GetCallee(); CJC_ASSERT(initFunc->TestAttr(Attribute::INITIALIZER)); auto applyCalleeName = initFunc->GetIdentifier(); if (applyCalleeName == declInitName) { init = expr; break; } } // if it is in this initializer if (init) { init->RemoveSelfFromBlock(); } } void GlobalVarInitializer::RemoveCommonInitializersReplacedWithPlatform( Func& fileInit, const std::vector>& decls) const { for (auto decl : decls) { if (decl->IsCommonMatchedWithPlatform()) { CJC_ASSERT(decl->astKind == AST::ASTKind::VAR_DECL); const AST::VarDecl& varDecl = StaticCast(*decl); RemoveInitializerForVarDecl(varDecl, fileInit); } } } Ptr GlobalVarInitializer::TranslateFileInitializer( const AST::File& file, const std::vector>& decls) { auto fileInit = TryGetFileInitialializer(file); if (fileInit) { CJC_ASSERT(fileInit->TestAttr(Attribute::INITIALIZER)); RemoveCommonInitializersReplacedWithPlatform(*fileInit, decls); return fileInit; } std::vector> varInitFuncs; for (auto decl : decls) { if (auto initFunc = TranslateVarInit(*decl)) { if (decl->IsConst()) { initFuncsForConstVar.emplace_back(initFunc); // In incremental compilation scenarios, only changes need to be re-evaluated. if (!enableIncre || decl->toBeCompiled) { SetCompileTimeValueFlagRecursivly(*StaticCast(initFunc)); } } varInitFuncs.push_back(initFunc); } } auto func = CreateGVInitFunc(file); FillGVInitFuncWithApplyAndExit(varInitFuncs); if (varInitFuncs.empty()) { func->DisableAttr(Attribute::NO_INLINE); } return func; } bool GlobalVarInitializer::NeedVarLiteralInitFunc(const AST::Decl& decl) { auto vd = DynamicCast(&decl); if (vd == nullptr || !vd->initializer || NeedInitGlobalVarByInitFunc(*vd)) { return false; } // common var with platform one should not be retranslated. if (decl.TestAttr(AST::Attribute::COMMON) && decl.platformImplementation) { return false; } // `decl` may be a wildcard, like: `var _ = 1`, doesn't need to be translated; // incremental no-change var does not have initialiser; they are copied from cached bc in codegen if (IsIncrementalNoChange(*vd)) { return false; } CJC_ASSERT(vd->initializer->astKind == AST::ASTKind::LIT_CONST_EXPR); auto litExpr = StaticCast(vd->initializer.get()); auto globalVar = DynamicCast(GetGlobalVariable(*vd)); CJC_ASSERT(globalVar); globalVar->SetInitializer(*trans.TranslateLitConstant(*litExpr, *litExpr->ty)); // mutable var decl need to be initialized in `file_literal`, codegen will call `file_literal` in // macro expand situation, immutable var decl doesn't need to if (!vd->isVar) { return false; } return true; } Ptr GlobalVarInitializer::TranslateFileLiteralInitializer( const AST::File& file, const std::vector>& decls) { std::list varsToGenInit{}; for (auto decl : decls) { if (NeedVarLiteralInitFunc(*decl)) { varsToGenInit.push_back(StaticCast(decl)); } } // do not generate if no literal need initialisation if (varsToGenInit.empty()) { return nullptr; } // NOTE: this function is only called by CodeGen and only used for macro expand situation. // And only mutable primitive values need to be re-initialized. // Literal reset functions do not need 'NO_INLINE' attr. auto func = CreateGVInitFunc(file, "_literal"); func->DisableAttr(Attribute::NO_INLINE); func->EnableAttr(Attribute::INITIALIZER); func->SetFuncKind(FuncKind::DEFAULT); func->SetDebugLocation(INVALID_LOCATION); auto currentBlock = trans.GetCurrentBlock(); for (auto vd : varsToGenInit) { auto globalVar = VirtualCast(GetGlobalVariable(*vd)); auto initNode = trans.TranslateExprArg(*vd->initializer); // this is in gv init for literal, we can't set breakpoint with cjdb, so we can't set DebugLocationInfo // for any expression initNode->SetDebugLocation(INVALID_LOCATION); auto expectTy = StaticCast(globalVar->GetType())->GetBaseType(); if (expectTy != initNode->GetType()) { initNode = TypeCastOrBoxIfNeeded(*initNode, *expectTy, builder, *trans.GetCurrentBlock(), INVALID_LOCATION, true); } trans.CreateAndAppendExpression(builder.GetUnitTy(), initNode, globalVar, currentBlock); } trans.CreateAndAppendTerminator(currentBlock); return func; } void GlobalVarInitializer::AddImportedPackageInit(const AST::Package& curPackage, const std::string& suffix) { auto voidTy = builder.GetVoidTy(); auto initFuncTy = builder.GetType(std::vector{}, voidTy); for (auto& dep : importManager.GetCurImportedPackages(curPackage.fullPackageName)) { const std::string& pkgName = dep->srcPackage->fullPackageName; bool doNotCallInit = dep->srcPackage->isMacroPackage; if (doNotCallInit) { continue; } auto context = GVInit(*dep->srcPackage, trans, suffix); // Try get deserialized one. ImportedFunc* initFunc = TryGetDeserialized(context.mangledName); // Already be translated when compiling common part. if (initFunc) { continue; } auto attrs = AttributeInfo(); attrs.SetAttr(Attribute::INITIALIZER, true); initFunc = builder.CreateImportedVarOrFunc( initFuncTy, context.mangledName, context.srcCodeIdentifier, context.rawMangledName, pkgName); initFunc->AppendAttributeInfo(attrs); initFunc->EnableAttr(Attribute::PUBLIC); trans.GenerateFuncCall(*initFunc, StaticCast(initFunc->GetType()), std::vector{}, nullptr, std::vector{}, INVALID_LOCATION); } } // [CJMP]: define inlining policy here and in other places after disabling CHIR transformations of common part Ptr GlobalVarInitializer::CreateImportsInitFunc(const AST::Package& curPackage, const std::string& suffix) { auto importsInitFunc = CreateGVInitFunc(curPackage, suffix + "_importsInit"); importsInitFunc->Set(Linkage::INTERNAL); auto curBlockGroup = importsInitFunc->GetBody(); auto curBlock = curBlockGroup->GetEntryBlock(); curBlock->EnableAttr(Attribute::INITIALIZER); trans.CreateAndAppendTerminator(curBlock); return importsInitFunc; } Ptr GlobalVarInitializer::GetImportsInitFunc(const AST::Package& curPackage, const std::string& suffix) { auto initializerName = GetPackageInitFuncName(curPackage.fullPackageName, suffix + "_importsInit"); auto importsInitFunc = TryGetDeserialized(initializerName); CJC_ASSERT(importsInitFunc); return importsInitFunc; } namespace { bool DoNotGenerateInitForImport(AST::PackageDecl& dep) { bool doNotCallInit = dep.srcPackage->isMacroPackage; #ifdef CANGJIE_CODEGEN_CJVM_BACKEND const std::string& pkgName = dep.srcPackage->fullPackageName; doNotCallInit = doNotCallInit || Utils::IsJava8Module(pkgName) || dep.srcPackage->TestAttr(AST::Attribute::TOOL_ADD); #endif return doNotCallInit; } } // namespace void GlobalVarInitializer::UpdateImportsInit( const AST::Package& curPackage, Func& importsInitFunc, const std::string& suffix) { Block* curBlock = DropExitNodeOfInitializer(importsInitFunc); trans.SetCurrentBlock(*curBlock); for (auto& dep : importManager.GetCurImportedPackages(curPackage.fullPackageName)) { if (DoNotGenerateInitForImport(*dep)) { continue; } auto context = GVInit(*dep->srcPackage, trans, suffix); if (auto initFunc = TryGetDeserialized(context.mangledName); initFunc) { continue; } // Creating import initializer auto voidTy = builder.GetVoidTy(); auto initFuncTy = builder.GetType(std::vector{}, voidTy); auto attrs = AttributeInfo(); attrs.SetAttr(Attribute::INITIALIZER, true); auto importInit = builder.CreateImportedVarOrFunc(initFuncTy, context.mangledName, context.srcCodeIdentifier, context.rawMangledName, dep->srcPackage->fullPackageName); importInit->AppendAttributeInfo(attrs); importInit->EnableAttr(Attribute::PUBLIC); InsertInitializerIntoPackageInitializer(*importInit, importsInitFunc); } trans.CreateAndAppendTerminator(curBlock); } void GlobalVarInitializer::AddGenericInstantiatedInit() { auto callContext = IntrisicCallContext{.kind = IntrinsicKind::PREINITIALIZE}; trans.CreateAndAppendExpression(builder.GetUnitTy(), callContext, trans.GetCurrentBlock()); } Ptr GlobalVarInitializer::GeneratePackageInitBase(const AST::Package& curPackage, const std::string& suffix) { /* var initFlag: Bool = false func pkg_init_suffix() { if (initFlag) { return } initFlag = true apply all imported package init_suffix funcs apply all current package file init_suffix funcs } */ auto func = CreateGVInitFunc(curPackage, suffix); // 1. Create global variable as guard condition. auto boolTy = builder.GetBoolTy(); auto initFlagName = suffix.empty() ? GV_PKG_INIT_ONCE_FLAG : "has_invoked_pkg_init_literal"; GlobalVar* initFlag = TryGetDeserialized(initFlagName); if (!initFlag) { initFlag = builder.CreateGlobalVar( INVALID_LOCATION, builder.GetType(boolTy), initFlagName, initFlagName, "", func->GetPackageName()); initFlag->SetInitializer(*builder.CreateLiteralValue(boolTy, false)); initFlag->EnableAttr(Attribute::NO_REFLECT_INFO); initFlag->EnableAttr(Attribute::COMPILER_ADD); initFlag->EnableAttr(Attribute::INITIALIZER); initFlag->EnableAttr(Attribute::NO_DEBUG_INFO); initFlag->Set(Linkage::INTERNAL); } // 2. Add `if (initFlag) { return }` auto curBlockGroup = func->GetBody(); auto curBlock = curBlockGroup->GetEntryBlock(); auto flagRef = trans.CreateAndAppendExpression(boolTy, initFlag, curBlock)->GetResult(); auto returnBlock = builder.CreateBlock(curBlockGroup); auto applyInitFuncBlock = builder.CreateBlock(curBlockGroup); trans.CreateAndAppendTerminator(flagRef, returnBlock, applyInitFuncBlock, curBlock); // if has initialized, return trans.CreateAndAppendTerminator(returnBlock); // 3. set `initFlag` true trans.SetCurrentBlock(*applyInitFuncBlock); auto unitTy = builder.GetUnitTy(); auto trueLit = trans.CreateAndAppendConstantExpression(boolTy, *applyInitFuncBlock, true)->GetResult(); trans.CreateAndAppendExpression(unitTy, trueLit, initFlag, applyInitFuncBlock); return func; } static Ptr FindApplyIn(const Block& block, FuncBase& applyCallee) { auto expressions = block.GetExpressions(); for (auto expression : expressions) { if (auto apply = DynamicCast(expression)) { if (apply->GetCallee() == &applyCallee) { return apply; } } } CJC_ABORT(); return nullptr; } void GlobalVarInitializer::InsertInitializerIntoPackageInitializer(FuncBase& init, Func& packageInit) { auto packageInitBody = packageInit.GetBody(); auto blockWithInitializers = GetBlockWithInitializers(*packageInitBody); if (init.TestAttr(Attribute::DESERIALIZED) && packageInit.TestAttr(Attribute::DESERIALIZED)) { // It was inserted at previous compilation phase ==> auto initCallExpr = FindApplyIn(*blockWithInitializers, init); auto lastExpr = blockWithInitializers->GetExpressions().back(); if (initCallExpr != lastExpr) { // ==> need to push to the end initCallExpr->MoveAfter(lastExpr); } return; } trans.SetCurrentBlock(*blockWithInitializers); trans.GenerateFuncCall(init, StaticCast(init.GetType()), std::vector{}, nullptr, std::vector{}, INVALID_LOCATION); } inline std::pair GlobalVarInitializer::PreparePackageInit(const AST::Package& curPackage) { // create/use deserialized base of package initializer function. Func* packageInit = builder.GetCurPackage()->GetPackageInitFunc(); Block* curBlock; if (packageInit) { curBlock = DropExitNodeOfInitializer(*packageInit); auto importsInitFunc = GetImportsInitFunc(curPackage); UpdateImportsInit(curPackage, *importsInitFunc); trans.SetCurrentBlock(*curBlock); } else { packageInit = GeneratePackageInitBase(curPackage).get(); builder.GetCurPackage()->SetPackageInitFunc(packageInit); curBlock = trans.GetCurrentBlock(); // Mark the block to find it later for modification on the next compilations. curBlock->EnableAttr(Attribute::INITIALIZER); // add apply of imported package init function. auto importsInitFunc = CreateImportsInitFunc(curPackage); InsertInitializerIntoPackageInitializer(*importsInitFunc, *packageInit); UpdateImportsInit(curPackage, *importsInitFunc); trans.SetCurrentBlock(*curBlock); // add apply of static generic instantiated init AddGenericInstantiatedInit(); } return std::make_pair(packageInit, curBlock); } /// Generate package initializer - the function that calls initializers of files /// in order that was defined and verified in previous stages. /// Package initalizer also call initializer of dependent package. void GlobalVarInitializer::CreatePackageInit(const AST::Package& curPackage, const InitOrder& initOrder) { auto [packageInit, curBlock] = PreparePackageInit(curPackage); for (auto& fileAndVars : initOrder) { // maybe there isn't global var decl in one file, in this case, we don't generate file init func if (!fileAndVars.second.empty() || enableIncre) { if (!fileAndVars.second.empty() && IsSymbolImportedDecl(*fileAndVars.second[0], opts)) { continue; } // only generate file init for this package, not imported files if (auto fileInit = TranslateFileInitializer(*fileAndVars.first, fileAndVars.second)) { InsertInitializerIntoPackageInitializer(*fileInit, *packageInit); } } } InsertAnnotationVarInitInto(*packageInit); trans.CreateAndAppendTerminator(curBlock); } inline std::pair GlobalVarInitializer::PreparePackageLiteralInit(const AST::Package& curPackage) { // create/use deserialized base of package initializer function. Func* packageLiteralInit = builder.GetCurPackage()->GetPackageLiteralInitFunc(); Block* curBlock; if (packageLiteralInit) { curBlock = DropExitNodeOfInitializer(*packageLiteralInit); auto importsInitFunc = GetImportsInitFunc(curPackage, "_literal"); UpdateImportsInit(curPackage, *importsInitFunc, "_literal"); trans.SetCurrentBlock(*curBlock); } else { // create base of package init literal function. packageLiteralInit = GeneratePackageInitBase(curPackage, "_literal"); builder.GetCurPackage()->SetPackageLiteralInitFunc(packageLiteralInit); curBlock = trans.GetCurrentBlock(); curBlock->EnableAttr(Attribute::INITIALIZER); auto importsInitFunc = CreateImportsInitFunc(curPackage, "_literal"); InsertInitializerIntoPackageInitializer(*importsInitFunc, *packageLiteralInit); UpdateImportsInit(curPackage, *importsInitFunc, "_literal"); trans.SetCurrentBlock(*curBlock); } return std::make_pair(packageLiteralInit, curBlock); } /// Generate package literal values initializer void GlobalVarInitializer::CreatePackageLiteralInit(const AST::Package& curPackage, const InitOrder& initOrder) { // NOTE: this function is only called by CodeGen and only used for macro expand situation. auto [packageLiteralInit, curBlock] = PreparePackageLiteralInit(curPackage); for (auto& fileAndVars : initOrder) { // maybe there isn't global var decl in one file, in this case, we don't generate file init func if (!fileAndVars.second.empty()) { if (fileAndVars.second[0]->TestAttr(AST::Attribute::IMPORTED)) { for (auto var : fileAndVars.second) { if (var->IsCommonOrPlatform()) { // They are always initialized via init functions in corresponding file init, // because e.g. it can be literal in common and not literal in platform, // and therefore they are not inlined in importing package. continue; } if (NeedVarLiteralInitFunc(*var)) { auto vd = StaticCast(var); auto globalVar = VirtualCast(GetGlobalVariable(*vd)); auto initNode = Translator::TranslateASTNode(*vd->initializer, trans); initNode->SetDebugLocation(INVALID_LOCATION); trans.CreateAndAppendExpression(builder.GetUnitTy(), initNode, globalVar, curBlock); } } continue; } if (auto fileLiteralInit = TranslateFileLiteralInitializer(*fileAndVars.first, fileAndVars.second)) { InsertInitializerIntoPackageInitializer(*fileLiteralInit, *packageLiteralInit); } } } trans.CreateAndAppendTerminator(curBlock); } void GlobalVarInitializer::Run(const AST::Package& pkg, const InitOrder& initOrder) { CreatePackageInit(pkg, initOrder); CreatePackageLiteralInit(pkg, initOrder); } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/MarkClassHasInited.cpp000066400000000000000000000137671510705540100236750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/MarkClassHasInited.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/ClassDef.h" using namespace Cangjie::CHIR; namespace { const std::string HAS_INITED_VAR_NAME = "$hasInited"; void AddHasInitedFlagToImportedClass(const Package& package, CHIRBuilder& builder) { for (auto classDef : package.GetImportedClasses()) { if (!classDef->GetFinalizer()) { continue; } auto attributeInfo = AttributeInfo(); attributeInfo.SetAttr(Attribute::NO_REFLECT_INFO, true); attributeInfo.SetAttr(Attribute::COMPILER_ADD, true); attributeInfo.SetAttr(Attribute::PRIVATE, true); classDef->AddInstanceVar(MemberVarInfo{HAS_INITED_VAR_NAME, "", builder.GetBoolTy(), attributeInfo}); } } void InitHasInitedFlagToFalse(Ptr initFunc, CHIRBuilder& builder, std::vector path) { auto boolTy = builder.GetBoolTy(); auto entry = initFunc->GetEntryBlock(); auto falseVal = builder.CreateConstantExpression(boolTy, entry, false); auto thisArg = initFunc->GetParam(0); CJC_NULLPTR_CHECK(thisArg); auto storeRef = builder.CreateExpression(builder.GetUnitTy(), falseVal->GetResult(), thisArg, path, entry); entry->InsertExprIntoHead(*storeRef); entry->InsertExprIntoHead(*falseVal); } void ReAssignHasInitedToTrue(Ptr initFunc, CHIRBuilder& builder, std::vector path) { auto boolTy = builder.GetBoolTy(); auto thisArg = initFunc->GetParam(0); for (auto block : initFunc->GetBody()->GetBlocks()) { auto terminator = block->GetTerminator(); if (!terminator || terminator->GetExprKind() != ExprKind::EXIT) { continue; } auto parent = terminator->GetParentBlock(); auto terminatorAnnos = terminator->MoveAnnotation(); terminator->RemoveSelfFromBlock(); auto trueVal = builder.CreateConstantExpression(boolTy, parent, true); auto storeRef = builder.CreateExpression(builder.GetUnitTy(), trueVal->GetResult(), thisArg, path, parent); auto exit = builder.CreateTerminator(parent); exit->SetAnnotation(std::move(terminatorAnnos)); parent->AppendExpressions({trueVal, storeRef, exit}); } } void AddGuardToFinalizer(Ptr classDef, CHIRBuilder& builder, std::vector path) { auto finalizer = Cangjie::DynamicCast(classDef->GetFinalizer()); if (!finalizer) { // While doing incremental compilation, the finalizer may be an ImportedFunc. return; } auto block = builder.CreateBlock(finalizer->GetBody()); auto thisArg = finalizer->GetParam(0); CJC_NULLPTR_CHECK(thisArg); auto boolTy = builder.GetBoolTy(); auto ref = builder.CreateExpression(builder.GetType(boolTy), thisArg, path, block); auto load = builder.CreateExpression(boolTy, ref->GetResult(), block); auto entry = finalizer->GetEntryBlock(); auto exit = builder.CreateBlock(finalizer->GetBody()); exit->AppendExpression(builder.CreateTerminator(exit)); auto cond = builder.CreateTerminator(load->GetResult(), entry, exit, block); block->AppendExpressions({ref, load, cond}); finalizer->GetBody()->SetEntryBlock(block); } } // namespace void MarkClassHasInited::RunOnPackage(const Package& package, CHIRBuilder& builder) { /** * To prevent any use-before-intialisation behaviour, we add a member variable * `hasInited` to indicate if this class has been initialised. The finalizer of * the class won't execute if the flag is false. * * class CA { class CA { * var x: Int64 var x: Int64 * init() { var hasInited: Bool * throw Exception() ==> init() { * } hasInited = false * ~init() { throw Exception() * println(x) // illegal hasInited = true * } } * } ~init() { * if (hasInited) { * println(x) // won't be executed * } * } */ // Add member variable `hasInited: bool` to all imported classes that have finalizer. // As any CHIR-added member won't be exported, we cannot see that the imported class has // this member variable. We need to add it by ourself. AddHasInitedFlagToImportedClass(package, builder); for (auto classDef : package.GetClasses()) { if (!classDef->GetFinalizer()) { continue; } auto attributeInfo = AttributeInfo(); attributeInfo.SetAttr(Attribute::NO_REFLECT_INFO, true); attributeInfo.SetAttr(Attribute::COMPILER_ADD, true); attributeInfo.SetAttr(Attribute::PRIVATE, true); classDef->AddInstanceVar(MemberVarInfo{HAS_INITED_VAR_NAME, "", builder.GetBoolTy(), attributeInfo}); auto index = std::vector{classDef->GetAllInstanceVarNum() - 1}; for (auto& funcBase : classDef->GetMethods()) { if (auto func = DynamicCast(funcBase); func && func->IsConstructor()) { InitHasInitedFlagToFalse(func, builder, index); ReAssignHasInitedToTrue(func, builder, index); } } AddGuardToFinalizer(classDef, builder, index); } }cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/000077500000000000000000000000001510705540100226155ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/CMakeLists.txt000066400000000000000000000005541510705540100253610ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB TRANSLATEASTNODE_SRC *.cpp) set(AST2CHIR_SRC ${AST2CHIR_SRC} ${TRANSLATEASTNODE_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateAnnotation.cpp000066400000000000000000000275521510705540100273240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/Expression/Terminator.h" #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "AnnoFactoryInfo.h" #include "cangjie/Mangle/CHIRManglingUtils.h" #endif using namespace Cangjie::CHIR; using namespace Cangjie; static const std::string& GetIdentifierToPrint(const AST::Decl& decl) { if (auto prop = DynamicCast(&decl)) { if (!prop->getters.empty()) { return prop->getters[0]->identifier; } } return decl.identifier; } using namespace AST; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // Annotation mangling rules here. // 1) Annotation Factory func: _CAF + package + orignalDecl + "Hv" // 2) each annotation var: _CAO + package + originalDecl + 64bit var index + "E" // 3) each annotation var init: _CAO + package + originalDecl + 64bit var index + "iiHv" GlobalVar* Translator::TranslateCustomAnnoInstanceSig(const Expr& expr, const Func& func, size_t i) { auto name = func.GetIdentifierWithoutPrefix(); CJC_ASSERT(name.size() > 3UL); // change _CAF prefix to _CAO name[3UL] = 'O'; name += MANGLE_COUNT_PREFIX; name += MangleUtils::DecimalToManglingNumber(std::to_string(i)); auto varName = name + MANGLE_SUFFIX; auto gv = builder.CreateGlobalVar(INVALID_LOCATION, builder.GetType(TranslateType(*expr.ty)), varName, varName, "", func.GetPackageName()); gv->EnableAttr(Attribute::COMPILER_ADD); gv->EnableAttr(Attribute::CONST); gv->Set(Linkage::INTERNAL); auto initName = std::move(name) + "iiHv"; auto ty = builder.GetType(std::vector{}, builder.GetVoidTy()); auto init = builder.CreateFunc(INVALID_LOCATION, ty, initName, initName, "", func.GetPackageName()); init->SetFuncKind(FuncKind::GLOBALVAR_INIT); init->Set(Linkage::INTERNAL); init->EnableAttr(Attribute::CONST); init->EnableAttr(Attribute::COMPILER_ADD); init->EnableAttr(Attribute::INITIALIZER); init->EnableAttr(Attribute::NO_INLINE); init->EnableAttr(Attribute::NO_REFLECT_INFO); initFuncsForAnnoFactory.push_back(init); auto bg = builder.CreateBlockGroup(*init); init->InitBody(*bg); auto bl = builder.CreateBlock(bg); bg->SetEntryBlock(bl); gv->SetInitFunc(*init); return gv; } std::vector Translator::TranslateAnnotationsArraySig(const ArrayLit& annos, const Func& func) { std::vector res; for (size_t i{0}; i < annos.children.size(); ++i) { if (isComputingAnnos && annos.children[i]->TestAttr(AST::Attribute::NO_REFLECT_INFO)) { continue; } res.push_back(TranslateCustomAnnoInstanceSig(*annos.children[i], func, i)); } return res; } void Translator::TranslateAnnotationsArrayBody(const Decl& decl, Func& func) { auto& annoInsts = func.Get(); auto annoArrSize = CreateAndAppendConstantExpression(builder.GetInt64Ty(), *currentBlock, annoInsts.size()) ->GetResult(); auto objectTy = builder.GetType(builder.GetObjectTy()); auto rawArrayTy = builder.GetType(objectTy, 1); auto rawArray = CreateAndAppendExpression( builder.GetType(rawArrayTy), objectTy, annoArrSize, currentBlock); auto arrayGeneric = builder.GetStructType("std.core", "Array"); auto genericType = StaticCast(arrayGeneric->GetGenericArgs()[0]); std::unordered_map table{{genericType, objectTy}}; auto arrayType = ReplaceRawGenericArgType(*arrayGeneric, table, builder); auto array = CreateAndAppendExpression(builder.GetType(arrayType), arrayType, currentBlock); auto arrayMethods = arrayGeneric->GetStructDef()->GetMethods(); auto arrayInit = std::find_if(arrayMethods.begin(), arrayMethods.end(), [](auto method) { return method->IsConstructor() && method->GetNumOfParams() == 4UL; }); CJC_ASSERT(arrayInit != arrayMethods.end()); auto zero = CreateAndAppendConstantExpression(builder.GetInt64Ty(), *currentBlock, 0); // call array init with RawArrayAllocate auto callContext = FuncCallContext { .args = std::vector{array->GetResult(), rawArray->GetResult(), zero->GetResult(), annoArrSize}, .thisType = builder.GetType(arrayType) }; auto retType = ReplaceRawGenericArgType(*(*arrayInit)->GetFuncType()->GetReturnType(), table, builder); CreateAndAppendExpression(retType, *arrayInit, callContext, currentBlock); // load all Annotation Instances from lifted gv and store them into the return value of annoFactoryFunc for (size_t i{0}; i < annoInsts.size(); ++i) { auto gv = annoInsts[i]; auto load = CreateAndAppendExpression(StaticCast(gv->GetType())->GetBaseType(), gv, currentBlock); auto typecast = CreateAndAppendExpression(objectTy, load->GetResult(), currentBlock); CreateAndAppendExpression( builder.GetUnitTy(), typecast->GetResult(), rawArray->GetResult(), std::vector{i}, currentBlock); } func.SetReturnValue(*array->GetResult()); CreateAndAppendTerminator(currentBlock); // translate Annotation instance init funcs // no need to cache currentBlock here, because the current function has been completed size_t annoChildrenId{0}; for (size_t i{0}; i < annoInsts.size(); ++i) { while (isComputingAnnos && decl.annotationsArray->children[annoChildrenId]->TestAttr(AST::Attribute::NO_REFLECT_INFO)) { ++annoChildrenId; } auto gv = annoInsts[i]; auto gvInit = gv->GetInitFunc(); blockGroupStack.push_back(gvInit->GetBody()); currentBlock = gvInit->GetEntryBlock(); TranslateSubExprToLoc(*decl.annotationsArray->children[annoChildrenId++], gv); CreateAndAppendTerminator(currentBlock); blockGroupStack.pop_back(); } #ifndef NDEBUG while (isComputingAnnos && annoChildrenId < decl.annotationsArray->children.size() && decl.annotationsArray->children[annoChildrenId]->TestAttr(AST::Attribute::NO_REFLECT_INFO)) { ++annoChildrenId; } CJC_ASSERT(decl.annotationsArray->children.size() == annoChildrenId); #endif // call the Annotation instance func in the package init func, for the sake of consteval auto pkgInit = builder.GetChirContext().GetCurPackage()->GetPackageInitFunc(); blockGroupStack.push_back(pkgInit->GetBody()); currentBlock = pkgInit->GetEntryBlock()->GetSuccessors()[1]; currentBlock->GetTerminator()->RemoveSelfFromBlock(); for (size_t i{0}; i < annoInsts.size(); ++i) { CreateAndAppendExpression( builder.GetVoidTy(), annoInsts[i]->GetInitFunc(), FuncCallContext{}, currentBlock); } CreateAndAppendTerminator(currentBlock); blockGroupStack.pop_back(); } #endif void Translator::TranslateAnnoFactoryFuncBody([[maybe_unused]] const AST::Decl& decl, Func& func) { auto body = builder.CreateBlockGroup(func); blockGroupStack.emplace_back(body); func.InitBody(*body); func.EnableAttr(Attribute::COMPILER_ADD); // create body auto bodyBlock = CreateBlock(); body->SetEntryBlock(bodyBlock); currentBlock = bodyBlock; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND TranslateAnnotationsArrayBody(decl, func); #endif blockGroupStack.pop_back(); } AnnoInfo Translator::CreateAnnoFactoryFuncSig(const AST::Decl& decl, CustomTypeDef* parent) { auto annosArray = decl.annotationsArray.get(); if (decl.TestAttr(AST::Attribute::IMPORTED) || !annosArray || annosArray->children.empty()) { return {"none"}; } auto found = annotationFuncMap.find(&decl); if (found != annotationFuncMap.end()) { return {found->second}; // Property's getters and setters share the same annotation function. } Type* returnTy = nullptr; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND returnTy = TranslateType(*annosArray->ty); #endif auto funcType = builder.GetType(std::vector{}, returnTy); const auto& loc = TranslateLocation(*decl.annotationsArray->children[0]); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::string mangledName = CHIRMangling::GenerateAnnotationFuncMangleName(decl.mangledName); #endif if (opts.chirDebugOptimizer) { std::string ms = "The annotation factory function of " + GetIdentifierToPrint(decl) + " in the line " + std::to_string(decl.begin.line) + " is " + mangledName + '\n'; std::cout << ms; } auto func = builder.CreateFunc(loc, funcType, mangledName, mangledName, "", decl.fullPackageName); func->SetFuncKind(FuncKind::ANNOFACTORY_FUNC); func->EnableAttr(Attribute::CONST); annoFactoryFuncs.emplace_back(&decl, func); annotationFuncMap.emplace(&decl, mangledName); if (parent) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND parent->AddMethod(func); #endif func->EnableAttr(Attribute::STATIC); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto gvs = TranslateAnnotationsArraySig(*annosArray, *func); func->Set(std::move(gvs)); // Collect annotations whose parameter values are literal constants. std::vector annoPairs; for (auto& elem : annosArray->children) { auto callExpr = StaticCast(elem.get().get()); auto& callee = callExpr->resolvedFunction->funcBody; auto annoClassDecl = callee->parentClassLike; CJC_ASSERT(annoClassDecl); auto annoClassDeclName = annoClassDecl->identifier.GetRawText(); std::vector paramValues; for (auto& arg : callExpr->args) { if (auto& argVal = arg->expr; argVal->astKind == AST::ASTKind::LIT_CONST_EXPR) { auto lit = StaticCast(argVal.get().get()); paramValues.emplace_back(lit->rawString); } else { return {mangledName, {}}; } } annoPairs.emplace_back(annoClassDeclName, paramValues); } return {mangledName, annoPairs}; #endif } std::unordered_map> Translator::jAnnoFuncMap; void Translator::CreateParamAnnotationInfo(const AST::FuncParam& astParam, Parameter& chirParam, CustomTypeDef& parent) { chirParam.SetAnnoInfo(CreateAnnoFactoryFuncSig(astParam, &parent)); } void Translator::CreateAnnoFactoryFuncsForFuncDecl(const AST::FuncDecl& funcDecl, CustomTypeDef* parent) { auto& params = funcDecl.funcBody->paramLists[0]->params; auto funcValue = GetSymbolTable(funcDecl); const AST::Decl& annotatedDecl = funcDecl.propDecl ? *funcDecl.propDecl : StaticCast(funcDecl); if (auto func = DynamicCast(funcValue)) { CreateAnnotationInfo(annotatedDecl, *func, parent); size_t offset = params.size() == func->GetNumOfParams() ? 0 : 1; for (size_t i = 0; i < params.size(); ++i) { CreateParamAnnotationInfo(*params[i], *func->GetParam(i + offset), *parent); } } else if (!funcDecl.TestAttr(AST::Attribute::IMPORTED) && funcValue->TestAttr(Attribute::NON_RECOMPILE)) { // Update annotation info for incremental created 'ImportedValue'; auto importedFunc = DynamicCast(funcValue); CJC_NULLPTR_CHECK(importedFunc); CreateAnnotationInfo(annotatedDecl, *importedFunc, parent); auto paramInfo = importedFunc->GetParamInfo(); for (size_t i = 0; i < params.size(); ++i) { paramInfo[i].annoInfo = CreateAnnoFactoryFuncSig(*params[i], parent); } importedFunc->SetParamInfo(std::move(paramInfo)); } } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateArrayExpr.cpp000066400000000000000000000324451510705540100271240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/Type.h" using namespace Cangjie::CHIR; using namespace Cangjie; using namespace Cangjie::AST; constexpr static int ARGS_NUM_TWO = 2; Ptr Translator::Visit(const AST::ArrayExpr& array) { if (array.isValueArray) { CJC_ASSERT(array.args.size() == 1); CJC_ASSERT(!array.ty->typeArgs.empty()); if (array.args[0]->ty->IsFunc()) { // Case A: "VArray({i => i})" return InitVArrayByLambda(array); } else { // Case B: "VArray(repeat: 0)" return InitVArrayByItem(array); } } CJC_ASSERT(array.ty->IsArray()); // Case A: "RawArray()" which initialize an empty array. if (array.args.empty()) { auto loc = TranslateLocation(array); auto arrayTy = chirTy.TranslateType(*array.ty); CJC_ASSERT(arrayTy->IsRef()); auto eleTy = StaticCast(StaticCast(arrayTy)->GetBaseType())->GetElementType(); auto sizeVal = CreateAndAppendConstantExpression( builder.GetInt64Ty(), *currentBlock, 0UL)->GetResult(); auto rawArrayRef = TryCreate(currentBlock, loc, arrayTy, eleTy, sizeVal)->GetResult(); CreateAndAppendExpression( builder.GetUnitTy(), rawArrayRef, std::vector{}, currentBlock); return rawArrayRef; } // Case B: "RawArray(collection)" which initialize an sized array with the `collection` content. if (array.args.size() == 1) { return InitArrayByCollection(array); } // Case C: "var x = RawArray(size, val)" which initialize an sized array where the element is with `val`. CJC_ASSERT(array.args.size() == ARGS_NUM_TWO); if (array.initFunc == nullptr) { return InitArrayByItem(array); } // Case D: "var x = RawArray(size, initFunc)" which initialize an sized array by specified init func. return InitArrayByLambda(array); } Expression* Translator::GenerateFuncCall(Value& callee, const FuncType* instantiedFuncTy, const std::vector calleeInstTypeArgs, Type* thisTy, const std::vector& args, DebugLocation loc) { auto instantiatedParamTys = instantiedFuncTy->GetParamTypes(); auto instantiatedRetTy = instantiedFuncTy->GetReturnType(); // Step 1: for the func args, cast it to the corresponding func param type if necessary std::vector castedArgs; // Except for the func with variant param length, args number should equal to params number CJC_ASSERT(args.size() == instantiatedParamTys.size() || instantiedFuncTy->HasVarArg()); size_t i = 0; for (; i < instantiatedParamTys.size(); ++i) { auto castedArg = TypeCastOrBoxIfNeeded(*args[i], *instantiatedParamTys[i], INVALID_LOCATION); castedArgs.emplace_back(castedArg); } for (; i < args.size(); ++i) { castedArgs.emplace_back(args[i]); } // Step 2: create the func call (might be a `Apply` or `ApplyWithException`) and set // its instantiated type info // we should make the instantiated type info as a forced input of the constructor of `Apply` return TryCreate(currentBlock, loc, instantiatedRetTy, &callee, FuncCallContext{ .args = castedArgs, .instTypeArgs = calleeInstTypeArgs, .thisType = thisTy}); } Ptr Translator::InitArrayByLambda(const AST::ArrayExpr& array) { CJC_ASSERT(array.args.size() == ARGS_NUM_TWO && array.initFunc != nullptr); auto loc = TranslateLocation(array); auto arrayTy = chirTy.TranslateType(*array.ty); CJC_ASSERT(arrayTy->IsRef()); auto eleTy = StaticCast(StaticCast(arrayTy)->GetBaseType())->GetElementType(); auto sizeVal = TranslateExprArg(*array.args[0]); auto initFn = GetSymbolTable(*array.initFunc); auto rawArrayExpr = CreateAndAppendExpression(loc, arrayTy, eleTy, sizeVal, currentBlock); auto rawArrayRef = rawArrayExpr->GetResult(); std::vector instantiatedTypeArgs; // if array init func is generic decl, then we will create `Apply` expr like: `Apply(init, args)` // if array init func is instantiated decl, then we will create `Apply` expr like: `Apply(init, args)` if (array.initFunc->TestAttr(AST::Attribute::GENERIC)) { for (auto ty : array.ty->typeArgs) { instantiatedTypeArgs.emplace_back(chirTy.TranslateType(*ty)); } } auto userInitFn = TranslateExprArg(*array.args[1]); // what are the initFn here all normal constructor or the arrayInitByFunc/arrayInitByCollection // check the thisType and instParentCustomDefTy std::vector instParamTys; instParamTys.emplace_back(rawArrayRef->GetType()); instParamTys.emplace_back(userInitFn->GetType()); auto instantiedFuncTy = builder.GetType(instParamTys, arrayTy); GenerateFuncCall(*initFn, instantiedFuncTy, instantiatedTypeArgs, nullptr, std::vector{rawArrayRef, userInitFn}, loc); return rawArrayRef; } Ptr Translator::InitArrayByItem(const AST::ArrayExpr& array) { CJC_ASSERT(array.args.size() == ARGS_NUM_TWO && array.initFunc == nullptr); auto loc = TranslateLocation(array); auto arrayTy = chirTy.TranslateType(*array.ty); CJC_ASSERT(arrayTy->IsRef()); auto eleTy = StaticCast(StaticCast(arrayTy)->GetBaseType())->GetElementType(); auto sizeVal = TranslateExprArg(*array.args[0]); auto initVal = TranslateExprArg(*array.args[1]); auto rawArrayRef = CreateAndAppendExpression(loc, arrayTy, eleTy, sizeVal, currentBlock)->GetResult(); CreateAndAppendExpression( loc, builder.GetUnitTy(), rawArrayRef, sizeVal, initVal, currentBlock); return rawArrayRef; } CHIR::Type* Translator::GetExactParentType( Type& fuzzyParentType, const AST::FuncDecl& resolvedFunction, FuncType& funcType, std::vector& funcInstTypeArgs, bool checkAbstractMethod, bool checkResult) { if (fuzzyParentType.IsNothing()) { return &fuzzyParentType; } auto outerDecl = resolvedFunction.outerDecl; CJC_NULLPTR_CHECK(outerDecl); if (outerDecl->TestAttr(AST::Attribute::GENERIC_INSTANTIATED)) { Type* parentTy = nullptr; if (outerDecl->astKind == AST::ASTKind::EXTEND_DECL) { parentTy = TranslateType(*StaticCast(outerDecl)->extendedType->ty); } else { parentTy = TranslateType(*outerDecl->ty); } return parentTy->StripAllRefs(); } auto funcName = resolvedFunction.identifier.Val(); auto isStatic = resolvedFunction.TestAttr(AST::Attribute::STATIC); CHIR::Type* result = nullptr; if (auto genericTy = DynamicCast(&fuzzyParentType)) { auto& upperBounds = genericTy->GetUpperBounds(); CJC_ASSERT(!upperBounds.empty()); for (auto upperBound : upperBounds) { ClassType* upperClassType = StaticCast(StaticCast(upperBound)->GetBaseType()); result = GetExactParentType( *upperClassType, resolvedFunction, funcType, funcInstTypeArgs, checkAbstractMethod, false); if (result != nullptr) { break; } } } else if (auto classTy = DynamicCast(&fuzzyParentType)) { result = classTy->GetExactParentType(funcName, funcType, isStatic, funcInstTypeArgs, builder, checkAbstractMethod); } else { std::unordered_map replaceTable; auto classInstArgs = fuzzyParentType.GetTypeArgs(); auto extendDefs = fuzzyParentType.GetExtends(&builder); CJC_ASSERT(!extendDefs.empty()); // extend def for (auto ex : extendDefs) { auto classGenericArgs = ex->GetExtendedType()->GetTypeArgs(); CJC_ASSERT(classInstArgs.size() == classGenericArgs.size()); for (size_t i = 0; i < classInstArgs.size(); ++i) { if (auto genericTy1 = DynamicCast(classGenericArgs[i])) { replaceTable.emplace(genericTy1, classInstArgs[i]); } } auto [func, done] = ex->GetExpectedFunc( funcName, funcType, isStatic, replaceTable, funcInstTypeArgs, builder, checkAbstractMethod); // when it's done, don't need to check `func` is nullptr, it can't be if (done && func->Get() == nullptr) { return ReplaceRawGenericArgType(*ex->GetExtendedType(), replaceTable, builder); } } // extend def's super interface for (auto ex : extendDefs) { for (auto ty : ex->GetImplementedInterfaceTys()) { result = ty->GetExactParentType( funcName, funcType, isStatic, funcInstTypeArgs, builder, checkAbstractMethod); if (result != nullptr) { return result; } } } } CJC_ASSERT(!checkResult || result != nullptr); return result; } Ptr Translator::InitArrayByCollection(const AST::ArrayExpr& array) { auto loc = TranslateLocation(array); auto arrayTy = chirTy.TranslateType(*array.ty); CJC_ASSERT(arrayTy->IsRef()); auto eleTy = StaticCast(StaticCast(arrayTy)->GetBaseType())->GetElementType(); auto collection = TranslateExprArg(*array.args[0]); auto sizeTy = builder.GetInt64Ty(); auto sizeGetInstFuncTy = builder.GetType(std::vector({collection->GetType()}), sizeTy); Type* originalObjType = StaticCast(collection->GetType()->StripAllRefs())->GetCustomTypeDef()->GetType(); originalObjType = builder.GetType(originalObjType); auto sizeGetOriginalFuncTy = builder.GetType(std::vector({originalObjType}), sizeTy); auto collectionDerefType = StaticCast(collection->GetType())->GetBaseType(); auto funcName = "$sizeget"; InstInvokeCalleeInfo funcInfo{funcName, sizeGetInstFuncTy, sizeGetOriginalFuncTy, std::vector{}, std::vector{}, collectionDerefType}; Value* sizeVal = GenerateDynmaicDispatchFuncCall(funcInfo, std::vector{}, collection)->GetResult(); // Create the array `RawArrayAllocate(eleTy, collection.size)` auto rawArrayRef = CreateAndAppendExpression(loc, arrayTy, eleTy, sizeVal, currentBlock)->GetResult(); // Call the `Core::arrayInitByCollection` to set the array element value CJC_NULLPTR_CHECK(array.initFunc); auto initFn = GetSymbolTable(*array.initFunc); // what are the initFn here all normal constructor or the arrayInitByFunc/arrayInitByCollection // check the thisType and instParentCustomDefTy std::vector instParamTys; instParamTys.emplace_back(rawArrayRef->GetType()); instParamTys.emplace_back(collection->GetType()); auto instantiedFuncTy = builder.GetType(instParamTys, arrayTy); // if array init func is generic decl, then we will create `Apply` expr like: `Apply(init, args)` // if array init func is instantiated decl, then we will create `Apply` expr like: `Apply(init, args)` auto instTys = array.initFunc->TestAttr(AST::Attribute::GENERIC) ? std::vector{eleTy} : std::vector{}; GenerateFuncCall(*initFn, instantiedFuncTy, instTys, nullptr, std::vector{rawArrayRef, collection}, loc); return rawArrayRef; } Ptr Translator::InitVArrayByItem(const AST::ArrayExpr& vArray) { auto loc = TranslateLocation(vArray); auto vArrayTy = StaticCast(chirTy.TranslateType(*vArray.ty)); auto eleTy = vArrayTy->GetElementType(); auto sizeVal = CreateAndAppendConstantExpression(builder.GetInt64Ty(), *currentBlock, vArrayTy->GetSize()) ->GetResult(); auto valArg = vArray.args[0].get(); auto val = TranslateExprArg(*valArg); // todo: optimize if val is constant auto fnTy = builder.GetType(std::vector({builder.GetInt64Ty()}), eleTy); auto nullFn = CreateAndAppendConstantExpression(fnTy, *currentBlock)->GetResult(); return CreateAndAppendExpression(loc, vArrayTy, sizeVal, val, nullFn, currentBlock)->GetResult(); } Ptr Translator::InitVArrayByLambda(const AST::ArrayExpr& vArray) { auto loc = TranslateLocation(vArray); auto vArrayTy = StaticCast(chirTy.TranslateType(*vArray.ty)); auto eleTy = vArrayTy->GetElementType(); auto sizeVal = CreateAndAppendConstantExpression(builder.GetInt64Ty(), *currentBlock, vArrayTy->GetSize()) ->GetResult(); auto initFn = TranslateExprArg(*vArray.args[0]); auto nullItem = CreateAndAppendConstantExpression(eleTy, *currentBlock)->GetResult(); return CreateAndAppendExpression(loc, vArrayTy, sizeVal, nullItem, initFn, currentBlock) ->GetResult(); } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateArrayLit.cpp000066400000000000000000000121431510705540100267270ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/Expression/Terminator.h" using namespace Cangjie::CHIR; using namespace Cangjie; namespace { bool IsArrayEleTypePrimitive(const CHIR::Type& type) { if (type.IsPrimitive() || type.IsVArray()) { return true; } if (type.IsStructArray()) { return IsArrayEleTypePrimitive(*StaticCast(&type)->GetGenericArgs()[0]); } if (type.IsTuple()) { auto eleTys = StaticCast(&type)->GetElementTypes(); CJC_ASSERT(eleTys.size() > 0); return std::all_of( eleTys.begin(), eleTys.end(), [](const Ptr& type) { return IsArrayEleTypePrimitive(*type); }); } return false; } } // namespace Ptr Translator::TranslateStructArray(const AST::ArrayLit& array) { auto loc = TranslateLocation(array); std::vector elements; auto arrayTy = StaticCast(chirTy.TranslateType(*array.ty)); CJC_ASSERT(arrayTy->IsStructArray()); auto eleTy = arrayTy->GetGenericArgs()[0]; auto elementSize = CreateAndAppendConstantExpression(builder.GetInt64Ty(), *currentBlock, array.children.size()) ->GetResult(); auto rawArrayType = builder.GetType(builder.GetType(eleTy, 1u)); auto rawArrayRef = TryCreate(currentBlock, loc, rawArrayType, eleTy, elementSize)->GetResult(); // in cjdb, if the arrayLit is nested,e.g. [[1,2]] // the outer RawArrayAllocate must be generated earlier than inner RawArrayAllocate, // then the location of outer's RawArrayAllocate will come before inner's. #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (IsArrayEleTypePrimitive(*eleTy)) { for (auto& child : array.children) { auto element = TranslateExprArg(*child, *eleTy); if (child->TestAttr(AST::Attribute::NO_REFLECT_INFO)) { element->EnableAttr(Attribute::NO_REFLECT_INFO); } elements.push_back(element); } CreateAndAppendExpression(builder.GetUnitTy(), rawArrayRef, elements, currentBlock); } else { // as for cjnative, if element's type is non-primitive type, // too many variables (aka `ele` below) before `RawArrayLiteralInit` may cause // `active variable analysis` stroke in backend (behind codegen), so here we use `GetElementRef + Store` // instead of `RawArrayLiteralInit`, thus the variable (`ele`) is used (by `Store`) as soon as // it is created and is not used after. for (size_t i = 0; i < array.children.size(); ++i) { auto& child = array.children[i]; auto ele = TranslateExprArg(*child, *eleTy, false); if (child->TestAttr(AST::Attribute::NO_REFLECT_INFO)) { ele->EnableAttr(Attribute::NO_REFLECT_INFO); } CreateAndAppendExpression( builder.GetUnitTy(), ele, rawArrayRef, std::vector({static_cast(i)}), currentBlock); } } #endif auto initFn = GetSymbolTable(*array.initFunc); auto result = CreateAndAppendExpression(builder.GetType(arrayTy), arrayTy, currentBlock)->GetResult(); auto intExpr = CreateAndAppendConstantExpression(builder.GetInt64Ty(), *currentBlock, 0UL); std::vector args = {result, rawArrayRef, intExpr->GetResult(), elementSize}; // what are the initFn here all normal constructor or the arrayInitByFunc/arrayInitByCollection // check the thisType and instParentCustomDefTy std::vector instParamTys; for (auto arg : args) { instParamTys.emplace_back(arg->GetType()); } auto instantiedFuncTy = builder.GetType(instParamTys, builder.GetVoidTy()); GenerateFuncCall(*initFn, instantiedFuncTy, {}, result->GetType(), args, loc); result = CreateAndAppendExpression(arrayTy, result, currentBlock)->GetResult(); return result; } Ptr Translator::TranslateVArray(const AST::ArrayLit& array) { auto loc = TranslateLocation(array); std::vector elements; auto arrayTy = chirTy.TranslateType(*array.ty); CJC_ASSERT(arrayTy->IsVArray()); auto eleTy = StaticCast(arrayTy)->GetElementType(); for (auto& child : array.children) { elements.push_back(TranslateExprArg(*child, *eleTy)); } return CreateAndAppendExpression(loc, arrayTy, elements, currentBlock)->GetResult(); } Ptr Translator::Visit(const AST::ArrayLit& array) { auto ty = array.ty; // VArray if (ty->kind == AST::TypeKind::TYPE_VARRAY) { return TranslateVArray(array); } // Array if (ty->IsStructArray()) { return TranslateStructArray(array); } InternalError("Certainly won't come here in translating arrayLit."); return nullptr; } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateAssignExpr.cpp000066400000000000000000000306671510705540100272760ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/CHIR/LiteralValue.h" using namespace Cangjie::CHIR; using namespace Cangjie; using namespace Cangjie::AST; bool Translator::OverloadableExprMayThrowException(const AST::OverloadableExpr& expr, const Type& leftValTy) const { bool mayOverflow = leftValTy.IsInteger() && expr.overflowStrategy == OverflowStrategy::THROWING; bool mayDivZero = (expr.op == TokenKind::DIV || expr.op == TokenKind::DIV_ASSIGN || expr.op == TokenKind::MOD || expr.op == TokenKind::MOD_ASSIGN) && leftValTy.IsInteger(); bool mayOverShift = expr.op == TokenKind::LSHIFT || expr.op == TokenKind::LSHIFT_ASSIGN || expr.op == TokenKind::RSHIFT || expr.op == TokenKind::RSHIFT_ASSIGN; bool noException = expr.op == TokenKind::BITAND || expr.op == TokenKind::BITAND_ASSIGN || expr.op == TokenKind::BITOR || expr.op == TokenKind::BITOR_ASSIGN || expr.op == TokenKind::BITXOR || expr.op == TokenKind::BITXOR_ASSIGN || expr.op == TokenKind::NOT; return (mayOverflow || mayDivZero || mayOverShift) && !noException; } Value* Translator::TranslateVArrayAssign(const AssignExpr& assign) { // note: // Suppose `arr` is a two-dimensional varaay, according to the specification, // `arr[m][n] = v` is illegal even if `m` & `n` are valid. // The reason is, `[]` should be regarded as a function call, and the return // value of a function is immutable. Thus, any assignment operation to an // element in a multi-dimensional varray is illegal. // If this restriction is relaxed, the `varraySet` operation will recursively // traverse the SubscriptExpr in the same way as the `varrayGet` operation. CJC_ASSERT(assign.leftValue->astKind == ASTKind::SUBSCRIPT_EXPR); auto se = StaticAs(assign.leftValue.get()); CJC_ASSERT(se->IsVArrayAccess()); const auto& loc = TranslateLocation(assign); auto lhsType = chirTy.TranslateType(*se->ty); CJC_ASSERT(se->indexExprs.size() == 1); auto indexLoc = TranslateLocation(*se->indexExprs[0]); if (COMPOUND_ASSIGN_EXPR_MAP.find(assign.op) == COMPOUND_ASSIGN_EXPR_MAP.end()) { // trivial assign, e.g a[0] = b auto rhs = TranslateExprArg(*assign.rightExpr); auto rightValLoc = TranslateLocation(*assign.rightExpr); rhs = GetDerefedValue(rhs, rightValLoc); auto lhsLeftValueInfo = TranslateExprAsLeftValue(*se->baseExpr.get()); auto lhsBase = lhsLeftValueInfo.base; // polish this if (!lhsLeftValueInfo.path.empty()) { auto lhsCustomType = StaticCast(lhsBase->GetType()->StripAllRefs()); lhsBase = CreateGetElementRefWithPath(loc, lhsBase, lhsLeftValueInfo.path, currentBlock, *lhsCustomType); } CJC_ASSERT(lhsBase->GetType()->IsRef()); auto index = TranslateExprArg(*se->indexExprs[0]); auto callContext = IntrisicCallContext { .kind = IntrinsicKind::VARRAY_SET, .args = std::vector({lhsBase, TypeCastOrBoxIfNeeded(*rhs, *lhsType, loc), index}) }; CreateAndAppendExpression(loc, builder.GetUnitTy(), callContext, currentBlock); return nullptr; } auto lhsLeftValueInfo = TranslateExprAsLeftValue(*se->baseExpr.get()); auto lhsBase = lhsLeftValueInfo.base; // polish this if (!lhsLeftValueInfo.path.empty()) { auto lhsCustomType = StaticCast(lhsBase->GetType()->StripAllRefs()); lhsBase = CreateGetElementRefWithPath(loc, lhsBase, lhsLeftValueInfo.path, currentBlock, *lhsCustomType); } CJC_ASSERT(lhsBase && lhsBase->GetType()->IsRef()); auto loadLHSValue = CreateAndAppendExpression( loc, StaticCast(lhsBase->GetType())->GetBaseType(), lhsBase, currentBlock); auto index = TranslateExprArg(*se->indexExprs[0]); auto baseLoc = TranslateLocation(*se->baseExpr); auto arrGetContext = IntrisicCallContext { .kind = IntrinsicKind::VARRAY_GET, .args = std::vector({loadLHSValue->GetResult(), index}) }; auto lhs = CreateAndAppendExpression(lhsType, arrGetContext, currentBlock)->GetResult(); if (assign.op == TokenKind::AND_ASSIGN) { auto res = TransShortCircuitAnd(lhs, *assign.rightExpr, loc); auto arrSetContext = IntrisicCallContext { .kind = IntrinsicKind::VARRAY_SET, .args = std::vector({lhsBase, TypeCastOrBoxIfNeeded(*res, *lhsType, loc), index}) }; CreateAndAppendExpression(loc, builder.GetUnitTy(), arrSetContext, currentBlock); } else if (assign.op == TokenKind::OR_ASSIGN) { auto res = TransShortCircuitOr(lhs, *assign.rightExpr, loc); auto arrSetContext = IntrisicCallContext { .kind = IntrinsicKind::VARRAY_SET, .args = std::vector({lhsBase, TypeCastOrBoxIfNeeded(*res, *lhsType, loc), index}) }; CreateAndAppendExpression(loc, builder.GetUnitTy(), arrSetContext, currentBlock); } else { // normal compound assign, e.g a[0] += b auto rhsValue = TranslateExprArg(*assign.rightExpr); bool mayHaveException = OverloadableExprMayThrowException(assign, *lhs->GetType()); auto binaryOpResult = TryCreateWithOV(currentBlock, mayHaveException, assign.overflowStrategy, loc, lhs->GetType(), op2ExprKind.at(COMPOUND_ASSIGN_EXPR_MAP.at(assign.op)), lhs, rhsValue) ->GetResult(); auto arrSetContext = IntrisicCallContext { .kind = IntrinsicKind::VARRAY_SET, .args = std::vector({lhsBase, TypeCastOrBoxIfNeeded(*binaryOpResult, *lhsType, loc), index}) }; CreateAndAppendExpression(loc, builder.GetUnitTy(), arrSetContext, currentBlock); } return nullptr; } Value* Translator::TranslateCompoundAssign(const AssignExpr& assign) { CJC_ASSERT(COMPOUND_ASSIGN_EXPR_MAP.find(assign.op) != COMPOUND_ASSIGN_EXPR_MAP.end()); auto loc = TranslateLocation(assign); auto leftValLoc = TranslateLocation(*assign.leftValue); auto rightValLoc = TranslateLocation(*assign.rightExpr); // The style of compound assign will be like: // X += Y // where "X" and "Y" can be some compilcated exprs // Translate the LHS part // 1) Get the LHS part "X" as left-value auto lhsLeftValueInfo = TranslateExprAsLeftValue(*assign.leftValue); auto lhsLeftValuePath = lhsLeftValueInfo.path; auto lhsLeftValueBase = lhsLeftValueInfo.base; auto lhsLeftValueBaseRefTy = lhsLeftValueBase->GetType(); CJC_ASSERT(lhsLeftValueBaseRefTy->IsRef()); auto lhsLeftValueBaseTy = StaticCast(lhsLeftValueBaseRefTy)->GetBaseType(); // 2) Get the LHS part "X" as right-value Value* lhsRightValue = nullptr; Type* storeTargetTy = nullptr; if (!lhsLeftValuePath.empty()) { // If the path is not empty, it means the "X" is some member access chain like: // A.b.c // where "A" can be some compilcated exprs, "b" and "c" must be member variables. // Specially, "A" can also be a implicit/explicit `this` or `super` param CJC_ASSERT(lhsLeftValueBaseRefTy->IsReferenceTypeWithRefDims(1) || lhsLeftValueBaseRefTy->IsValueOrGenericTypeWithRefDims(1)); auto lhsLeftValueBaseCustomType = StaticCast(lhsLeftValueBaseTy); // Therefore we need to get the element value from the base auto getMemberRef = CreateGetElementRefWithPath( leftValLoc, lhsLeftValueBase, lhsLeftValuePath, currentBlock, *lhsLeftValueBaseCustomType); auto memberType = StaticCast(getMemberRef->GetType())->GetBaseType(); storeTargetTy = memberType; auto loadMember = CreateAndAppendExpression(leftValLoc, memberType, getMemberRef, currentBlock); lhsRightValue = loadMember->GetResult(); } else { // If the path is empty, it means the "X" is some simple expression like reference to other // variables or function call, therefore we just need to get the right value from the left value by loading CJC_ASSERT(!lhsLeftValueBaseTy->IsRef()); storeTargetTy = lhsLeftValueBaseTy; auto loadMember = CreateAndAppendExpression(loc, lhsLeftValueBaseTy, lhsLeftValueBase, currentBlock); lhsRightValue = loadMember->GetResult(); } // 3) Get the compound value "X + Y" bool mayHaveException = OverloadableExprMayThrowException(assign, *lhsRightValue->GetType()); Value* compoundValue = nullptr; if (assign.op == TokenKind::AND_ASSIGN) { compoundValue = TransShortCircuitAnd(lhsRightValue, *assign.rightExpr, loc); } else if (assign.op == TokenKind::OR_ASSIGN) { compoundValue = TransShortCircuitOr(lhsRightValue, *assign.rightExpr, loc); } else { // Translate the RHS part "Y" Value* rhs = TranslateExprArg(*assign.rightExpr); auto binOp = TryCreateWithOV(currentBlock, mayHaveException, assign.overflowStrategy, loc, lhsRightValue->GetType(), op2ExprKind.at(COMPOUND_ASSIGN_EXPR_MAP.at(assign.op)), lhsRightValue, rhs); compoundValue = binOp->GetResult(); } compoundValue = TypeCastOrBoxIfNeeded(*compoundValue, *storeTargetTy, loc); // 4) Implement the store if (!lhsLeftValuePath.empty()) { CreateAndAppendExpression( loc, builder.GetUnitTy(), compoundValue, lhsLeftValueBase, lhsLeftValuePath, currentBlock); } else { CreateAndAppendExpression(loc, builder.GetUnitTy(), compoundValue, lhsLeftValueBase, currentBlock); } return nullptr; } Value* Translator::TranslateTrivialAssign(const AST::AssignExpr& assign) { const auto& loc = TranslateLocation(assign); auto leftValLoc = TranslateLocation(*assign.leftValue); auto rightValLoc = TranslateLocation(*assign.rightExpr); Value* rhs; std::vector path; Value* lhs; if (assign.leftValue->mapExpr) { // compound assignment, left first, right second auto linfo = TranslateExprAsLeftValue(*assign.leftValue); lhs = linfo.base; path = std::move(linfo.path); if (!path.empty()) { auto lhsCustomType = StaticCast(lhs->GetType()->StripAllRefs()); auto getMemberRef = CreateGetElementRefWithPath(loc, lhs, path, currentBlock, *lhsCustomType); exprValueTable.Set(*assign.leftValue, *getMemberRef); } rhs = TranslateExprArg(*assign.rightExpr); } else { // normal assignment, right first, left second rhs = TranslateExprArg(*assign.rightExpr); auto linfo = TranslateExprAsLeftValue(*assign.leftValue); lhs = linfo.base; path = std::move(linfo.path); } if (!lhs) { return nullptr; } CJC_ASSERT(lhs && lhs->GetType()->IsRef()); if (!path.empty()) { auto lhsCustomType = StaticCast(lhs->GetType()->StripAllRefs()); auto memberType = GetInstMemberTypeByName(*lhsCustomType, path, builder); rhs = TypeCastOrBoxIfNeeded(*rhs, *memberType, loc); CreateAndAppendExpression(loc, builder.GetUnitTy(), rhs, lhs, path, currentBlock); } else { rhs = TypeCastOrBoxIfNeeded(*rhs, *StaticCast(lhs->GetType())->GetBaseType(), loc); CreateAndAppendExpression(loc, builder.GetUnitTy(), rhs, lhs, currentBlock); } return nullptr; } Ptr Translator::Visit(const AST::AssignExpr& assign) { // Case 1: assign to a wildcard expr if (assign.leftValue->astKind == AST::ASTKind::WILDCARD_EXPR) { TranslateSubExprToDiscarded(*assign.rightExpr); return nullptr; } // Case 2: assign to a subscript expr which must be a VArray access if (auto se = DynamicCast(assign.leftValue.get()); se && !se->isTupleAccess) { return TranslateVArrayAssign(assign); } // Case 3: compound assign if (COMPOUND_ASSIGN_EXPR_MAP.find(assign.op) != COMPOUND_ASSIGN_EXPR_MAP.end()) { return TranslateCompoundAssign(assign); } // Case 4: trivial assign return TranslateTrivialAssign(assign); } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateBinaryExpr.cpp000066400000000000000000000166521510705540100272740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" using namespace Cangjie::CHIR; using namespace Cangjie; Ptr Translator::Visit(const AST::BinaryExpr& binaryExpr) { if (!binaryExpr.TestAttr(AST::Attribute::SIDE_EFFECT)) { return ProcessBinaryExpr(binaryExpr); } /* The following cangjie code may have side effect, and sema will set attribute SIDE_EFFECT on binaryExpr. test(1) == (true, true, true)) desugar to: test(1)[0] == true && test(1)[1] == true && test(1)[2] == true | |=========================> has the Attribute: SIDE_EFFECT | | ============================================================> the callExpr has mapExpr. */ if (auto rightBinaryExpr = DynamicCast(binaryExpr.rightExpr.get()); rightBinaryExpr) { AST::SubscriptExpr* subscriptExpr = nullptr; if (rightBinaryExpr->leftExpr && rightBinaryExpr->leftExpr->astKind == AST::ASTKind::SUBSCRIPT_EXPR) { subscriptExpr = StaticCast(rightBinaryExpr->leftExpr.get()); } else if (rightBinaryExpr->rightExpr && rightBinaryExpr->rightExpr->astKind == AST::ASTKind::SUBSCRIPT_EXPR) { subscriptExpr = StaticCast(rightBinaryExpr->rightExpr.get()); } if (subscriptExpr) { if (auto mapExpr = GetMapExpr(*subscriptExpr->baseExpr)) { if (!exprValueTable.Has(*mapExpr)) { auto chirNode = TranslateExprArg(*subscriptExpr->baseExpr); exprValueTable.Set(*mapExpr, *chirNode); } } } } return ProcessBinaryExpr(binaryExpr); } Ptr Translator::ProcessBinaryExpr(const AST::BinaryExpr& binaryExpr) { // BinaryExpression init func is (ExprKind, Value*, Value*, OverflowStrategy, Block*) static const std::unordered_map OP2_EXPR_KIND = { {Cangjie::TokenKind::ADD, ExprKind::ADD}, {Cangjie::TokenKind::SUB, ExprKind::SUB}, {Cangjie::TokenKind::MUL, ExprKind::MUL}, {Cangjie::TokenKind::DIV, ExprKind::DIV}, {Cangjie::TokenKind::MOD, ExprKind::MOD}, {Cangjie::TokenKind::EXP, ExprKind::EXP}, {Cangjie::TokenKind::AND, ExprKind::AND}, {Cangjie::TokenKind::OR, ExprKind::OR}, {Cangjie::TokenKind::BITAND, ExprKind::BITAND}, {Cangjie::TokenKind::BITOR, ExprKind::BITOR}, {Cangjie::TokenKind::BITXOR, ExprKind::BITXOR}, {Cangjie::TokenKind::LSHIFT, ExprKind::LSHIFT}, {Cangjie::TokenKind::RSHIFT, ExprKind::RSHIFT}, {Cangjie::TokenKind::LT, ExprKind::LT}, {Cangjie::TokenKind::GT, ExprKind::GT}, {Cangjie::TokenKind::LE, ExprKind::LE}, {Cangjie::TokenKind::GE, ExprKind::GE}, {Cangjie::TokenKind::NOTEQ, ExprKind::NOTEQUAL}, {Cangjie::TokenKind::EQUAL, ExprKind::EQUAL}, }; const auto chirType = TranslateType(*binaryExpr.ty); const auto& loc = TranslateLocation(binaryExpr); auto it = OP2_EXPR_KIND.find(binaryExpr.op); CJC_ASSERT(it != OP2_EXPR_KIND.end()); ExprKind kd = it->second; auto lhs = TranslateExprArg(*binaryExpr.leftExpr); CJC_NULLPTR_CHECK(lhs); if (kd == ExprKind::AND) { return TransShortCircuitAnd(lhs, *binaryExpr.rightExpr, loc, binaryExpr.TestAttr(AST::Attribute::COMPILER_ADD)); } if (kd == ExprKind::OR) { return TransShortCircuitOr(lhs, *binaryExpr.rightExpr, loc, binaryExpr.TestAttr(AST::Attribute::COMPILER_ADD)); } auto rightExpr = TranslateExprArg(*binaryExpr.rightExpr); bool mayHaveException = OverloadableExprMayThrowException(binaryExpr, *chirType); if (binaryExpr.leftExpr->ty->IsNothing()) { const auto& rightExprLoc = TranslateLocation(*binaryExpr.rightExpr); return TryCreateWithOV(currentBlock, mayHaveException, binaryExpr.overflowStrategy, rightExprLoc, loc, chirType, kd, lhs, rightExpr) ->GetResult(); } else { const auto& operatorLoc = GetOperatorLoc(binaryExpr); return TryCreateWithOV( currentBlock, mayHaveException, binaryExpr.overflowStrategy, operatorLoc, loc, chirType, kd, lhs, rightExpr) ->GetResult(); } } Ptr Translator::TransShortCircuitAnd( const Ptr leftValue, const AST::Expr& rightExpr, const DebugLocation& loc, bool isImplicitAdd) { auto expr = CreateAndAppendExpression( loc, builder.GetType(builder.GetBoolTy()), builder.GetBoolTy(), currentBlock); Ptr alloca = expr->GetResult(); Ptr thenBlock = CreateBlock(); Ptr elseBlock = CreateBlock(); Ptr endBlock = CreateBlock(); CJC_ASSERT( leftValue->GetType()->IsBoolean() || leftValue->GetType()->IsNothing() || leftValue->GetType()->IsGeneric()); const auto& rightLoc = TranslateLocation(rightExpr); CreateWrappedBranch(SourceExpr::BINARY, loc, leftValue, thenBlock, elseBlock, currentBlock); // Create Then Block: currentBlock = thenBlock; if (!isImplicitAdd) { currentBlock->SetDebugLocation(rightLoc); } TranslateSubExprToLoc(rightExpr, alloca, rightLoc); CreateAndAppendTerminator(loc, endBlock, currentBlock); // Create Else Block: auto boolLiteral = CreateAndAppendConstantExpression(loc, builder.GetBoolTy(), *elseBlock, false)->GetResult(); CreateWrappedStore(loc, boolLiteral, alloca, elseBlock); CreateAndAppendTerminator(loc, endBlock, elseBlock); // Update 'currentBlock' at last. currentBlock = endBlock; return CreateAndAppendExpression(loc, builder.GetBoolTy(), alloca, endBlock)->GetResult(); } Ptr Translator::TransShortCircuitOr( const Ptr leftValue, const AST::Expr& rightExpr, const DebugLocation& loc, bool isImplicitAdd) { Ptr alloca = CreateAndAppendExpression( loc, builder.GetType(builder.GetBoolTy()), builder.GetBoolTy(), currentBlock) ->GetResult(); Ptr thenBlock = CreateBlock(); Ptr elseBlock = CreateBlock(); Ptr endBlock = CreateBlock(); CJC_ASSERT( leftValue->GetType()->IsBoolean() || leftValue->GetType()->IsNothing() || leftValue->GetType()->IsGeneric()); const auto& rightLoc = TranslateLocation(rightExpr); CreateWrappedBranch(SourceExpr::BINARY, loc, leftValue, thenBlock, elseBlock, currentBlock); // Create Then Block: auto boolLiteral = CreateAndAppendConstantExpression(builder.GetBoolTy(), *thenBlock, true)->GetResult(); CreateWrappedStore(rightLoc, boolLiteral, alloca, thenBlock); CreateAndAppendTerminator(rightLoc, endBlock, thenBlock); // Create Else Block: currentBlock = elseBlock; if (!isImplicitAdd) { currentBlock->SetDebugLocation(rightLoc); } TranslateSubExprToLoc(rightExpr, alloca, rightLoc); CreateAndAppendTerminator(rightLoc, endBlock, currentBlock); // Update 'currentBlock' at last. this->currentBlock = endBlock; return CreateAndAppendExpression(loc, builder.GetBoolTy(), alloca, endBlock)->GetResult(); } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateBlock.cpp000066400000000000000000000055561510705540100262440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" using namespace Cangjie::CHIR; using namespace Cangjie; static void CollectInstantiatedFuncNodes( std::vector>& nodes, const AST::FuncDecl& genericFunc, const GenericInstantiationManager& gim) { auto decls = gim.GetInstantiatedDecls(genericFunc); if (decls.empty() && genericFunc.genericDecl) { // 'genericFunc' may be partially instantiated decl which cannot be found in map. decls = gim.GetInstantiatedDecls(*genericFunc.genericDecl); } for (auto instantiatedDecl : decls) { (void)nodes.emplace_back(instantiatedDecl); } } static bool IsUnnecessarySuperCall(const AST::Node& node) { auto ce = DynamicCast(&node); if (!ce || !ce->baseFunc) { return false; } if (auto re = DynamicCast(ce->baseFunc.get()); re && re->isSuper) { // Super call of 'Object' can be ignored. return ce->ty->IsObject(); } return false; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND static std::vector> CollectBlockBodyNodes( const AST::Block& block, const GenericInstantiationManager* gim) { std::vector> nodes; for (const auto& body : block.body) { // If we find a generic local function here, then we must // retrieve all the instantiated functions for the translation. auto funcDecl = DynamicCast(body.get()); if (funcDecl != nullptr && funcDecl->TestAttr(AST::Attribute::GENERIC)) { nodes.emplace_back(funcDecl); if (gim) { CollectInstantiatedFuncNodes(nodes, *funcDecl, *gim); } } else { if (IsUnnecessarySuperCall(*body)) { continue; } nodes.emplace_back(body.get()); } } return nodes; } #endif Ptr Translator::Visit(const AST::Block& b) { CJC_ASSERT(!blockGroupStack.empty()); auto block = CreateBlock(); // Current block may be changed during translation, // store AST block 'b' related CHIR block for caller to generate goto or branch. SetSymbolTable(b, *block); currentBlock = block; // Since cangjie's block has value, return the value of last block node. #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::vector> nodes = CollectBlockBodyNodes(b, gim); #endif for (size_t i = 0; i < nodes.size(); ++i) { if (i == nodes.size() - 1) { return TranslateSubExprAsValue(*nodes[i]); } else { TranslateSubExprToDiscarded(*nodes[i]); } } return nullptr; }cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateCallExpr.cpp000066400000000000000000001765071510705540100267310ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/CHIR/ConstantUtils.h" #include "cangjie/CHIR/IntrinsicKind.h" using namespace Cangjie::CHIR; using namespace Cangjie; std::vector Translator::TranslateASTTypes(const std::vector>& genericInfos) { std::vector ts; for (auto& genericInfo : genericInfos) { ts.emplace_back(TranslateType(*genericInfo)); } return ts; } Ptr Translator::GetMapExpr(AST::Node& node) const { if (auto expr = DynamicCast(&node); expr) { while (expr != nullptr && expr->desugarExpr != nullptr) { expr = expr->desugarExpr.get(); } if (expr != nullptr && expr->mapExpr != nullptr) { auto base = expr->mapExpr; return base; } } return nullptr; } // Init not called by 'this' or 'super' static bool IsCallRegularInit(const AST::CallExpr& expr) { if (expr.resolvedFunction && IsInstanceConstructor(*expr.resolvedFunction)) { bool callOtherInit = expr.baseFunc->astKind == AST::ASTKind::REF_EXPR && (StaticCast(expr.baseFunc.get())->isThis || StaticCast(expr.baseFunc.get())->isSuper); return !callOtherInit; } return false; } std::vector Translator::GetFuncInstArgs(const AST::CallExpr& expr) { CJC_ASSERT(expr.resolvedFunction != nullptr); std::vector funcInstTypeArgs; if (auto nre = DynamicCast(expr.baseFunc.get())) { // Skip the constructor since the instantiation type args there is for the parent custom type not for the // function call,e.g. let x = CA() if (!expr.resolvedFunction->TestAttr(AST::Attribute::CONSTRUCTOR)) { for (auto& instTy : nre->instTys) { funcInstTypeArgs.emplace_back(TranslateType(*instTy)); } } } return funcInstTypeArgs; } static bool IsPropertySetterCall(const AST::CallExpr& expr) { auto func = expr.resolvedFunction; bool res = func->isSetter; return res; } Expression* Translator::GenerateDynmaicDispatchFuncCall(const InstInvokeCalleeInfo& funcInfo, const std::vector& args, Value* thisObj, Value* thisRTTI, DebugLocation loc) { auto instantiatedParamTys = funcInfo.instFuncType->GetParamTypes(); // Step 1: for the func args, cast it to the corresponding func param type if necessary std::vector castedArgs; Value* castedThisObj = thisObj; if (thisObj != nullptr) { CJC_ASSERT(args.size() == instantiatedParamTys.size() - 1); // do we really need this cast castedThisObj = TypeCastOrBoxIfNeeded(*thisObj, *instantiatedParamTys[0], loc); for (size_t i = 0; i < args.size(); ++i) { auto castedArg = TypeCastOrBoxIfNeeded(*args[i], *instantiatedParamTys[i + 1], loc); castedArgs.emplace_back(castedArg); } } else { CJC_ASSERT(args.size() == instantiatedParamTys.size()); for (size_t i = 0; i < args.size(); ++i) { auto castedArg = TypeCastOrBoxIfNeeded(*args[i], *instantiatedParamTys[i], loc); castedArgs.emplace_back(castedArg); } } // Step 2: create the func call (might be a `Invoke` or `InvokeWithException`) and set // its instantiated type info auto invokeInfo = InvokeCallContext { .caller = (thisObj == nullptr) ? thisRTTI : castedThisObj, .funcCallCtx = FuncCallContext { .args = castedArgs, .instTypeArgs = funcInfo.instantiatedTypeArgs, .thisType = funcInfo.thisType }, .virMethodCtx = VirMethodContext { .srcCodeIdentifier = funcInfo.srcCodeIdentifier, .originalFuncType = funcInfo.originalFuncType, .genericTypeParams = funcInfo.genericTypeParams } }; if (thisObj != nullptr) { return TryCreate(currentBlock, loc, funcInfo.instFuncType->GetReturnType(), invokeInfo); } else { return TryCreate(currentBlock, loc, funcInfo.instFuncType->GetReturnType(), invokeInfo); } } Ptr Translator::GetCurrentThisObject(const AST::FuncDecl& resolved) { auto curFunc = GetCurrentFunc(); CJC_NULLPTR_CHECK(curFunc); auto thisVar = curFunc->GetParam(0); bool isThisRef = curFunc->IsConstructor() || curFunc->TestAttr(Attribute::MUT) || curFunc->GetFuncKind() == FuncKind::SETTER; bool needThisRef = resolved.TestAttr(AST::Attribute::MUT) || resolved.TestAttr(AST::Attribute::CONSTRUCTOR) || resolved.isSetter; if (IsStructOrExtendMethod(*curFunc) && isThisRef && !needThisRef) { auto objType = thisVar->GetType(); CJC_ASSERT(objType->IsRef() && !StaticCast(objType)->GetBaseType()->IsRef()); auto objBaseType = StaticCast(objType)->GetBaseType(); CJC_ASSERT(objBaseType->IsStruct()); return CreateAndAppendExpression(objBaseType, thisVar, currentBlock)->GetResult(); } else { return thisVar; } } Value* Translator::GetCurrentThisObjectByMemberAccess(const AST::MemberAccess& memAccess, const AST::FuncDecl& resolved, const DebugLocation& loc) { // this or super call:this.f(), super.f() if (AST::IsThisOrSuper(*memAccess.baseExpr)) { // MemberAccess must not be constructor call. return GetCurrentThisObject(resolved); } // member access except this or super call auto curObj = TranslateExprArg(*memAccess.baseExpr); CJC_NULLPTR_CHECK(curObj); if (memAccess.baseExpr->ty->IsClassLike()) { // class A {func foo(){return 0} // var a = A() // a.f() // a is A&& need add `Load` // let b = A() // b.f() // b is A& don't need add `Load` auto objType = curObj->GetType(); if (objType->IsRef()) { // objType is A&& or A& auto objBaseType = StaticCast(objType)->GetBaseType(); if (objBaseType->IsRef()) { // for example: objBaseType is A& auto derefedObj = CreateAndAppendExpression( TranslateLocation(*memAccess.baseExpr), objBaseType, curObj, currentBlock) ->GetResult(); derefedObj->Set(SkipKind::SKIP_DCE_WARNING); return derefedObj; } } return curObj; } else if (!memAccess.baseExpr->ty->IsStruct() || !resolved.TestAttr(AST::Attribute::MUT)) { // Non-struct and non-classlike type must perform deref. // If `this` obj's type is struct, and resolved function is not `mut`, need add `Load`. // struct A {func foo(){return 0} // var a = A() // a.foo() // a is A& need add `Load` // let b = A() // b.foo() // b is A don't need add `Load` auto objType = curObj->GetType(); if (objType->IsRef()) { // objType is A& auto objBaseType = StaticCast(objType)->GetBaseType(); CJC_ASSERT(!objBaseType->IsRef()); // for example: objBaseType is A curObj = CreateAndAppendExpression(loc, objBaseType, curObj, currentBlock)->GetResult(); } } return curObj; } // this API should be moved to some other place Translator::LeftValueInfo Translator::TranslateExprAsLeftValue(const AST::Expr& expr) { auto base = &expr; auto backBlock = currentBlock; auto desugared = GetDesugaredExpr(expr); if (auto dexpr = DynamicCast(desugared); dexpr && dexpr->mapExpr != nullptr) { base = dexpr->mapExpr; } if (auto res = exprValueTable.TryGet(*base)) { return LeftValueInfo(res, {}); } if (desugared != &expr) { LeftValueInfo res = LeftValueInfo(nullptr, {}); if (auto dexpr = DynamicCast(desugared)) { res = TranslateExprAsLeftValue(*StaticCast(dexpr)); } else { res = LeftValueInfo(TranslateASTNode(*desugared, *this), {}); } /* There are two cases need add `Goto` when translate AST::Block case1: if the Block node is a desugar node, then add `Goto`. example code: | | desugar block `print("${a.a.b}\n")` => desugar to `print({var tmp1 = Stringbuilder(); tmp1.append({a.a.b})})` | | desugar block case2: unsafe block. example code: var a = unsafe{} */ if (auto subBlock = DynamicCast(desugared)) { if (desugared != &expr || desugared->TestAttr(AST::Attribute::UNSAFE)) { CreateAndAppendTerminator(GetBlockByAST(*subBlock), backBlock); } } return res; } if (expr.astKind == AST::ASTKind::REF_EXPR) { return TranslateRefExprAsLeftValue(*StaticCast(&expr)); } else if (expr.astKind == AST::ASTKind::MEMBER_ACCESS) { return TranslateMemberAccessAsLeftValue(*StaticCast(&expr)); } else if (expr.astKind == AST::ASTKind::PAREN_EXPR) { auto parenExpr = StaticCast(&expr); return TranslateExprAsLeftValue(*parenExpr->expr); } else if (expr.astKind == AST::ASTKind::CALL_EXPR) { return TranslateCallExprAsLeftValue(*StaticCast(&expr)); } else { return LeftValueInfo(TranslateASTNode(expr, *this), {}); } } Value* Translator::GenerateLeftValue(const Translator::LeftValueInfo& leftValInfo, const DebugLocation& loc) { Value* result = leftValInfo.base; if (!leftValInfo.path.empty()) { auto baseCustomType = StaticCast(result->GetType()->StripAllRefs()); auto memberType = GetInstMemberTypeByName(*baseCustomType, leftValInfo.path, builder); if (result->GetType()->IsRef()) { auto memberRefType = builder.GetType(memberType); auto getMemberRef = CreateAndAppendExpression(loc, memberRefType, result, leftValInfo.path, currentBlock); result = getMemberRef->GetResult(); } else { auto getMember = CreateAndAppendExpression(loc, memberType, result, leftValInfo.path, currentBlock); result = getMember->GetResult(); } } return result; } void Translator::TranslateThisObjectForNonStaticMemberFuncCall( const AST::CallExpr& expr, std::vector& args, bool needsMutableThis) { Ptr resolved = expr.resolvedFunction; CJC_ASSERT(resolved && IsInstanceMember(*resolved)); CJC_NULLPTR_CHECK(resolved->outerDecl); // polish here // When current is calling a constructor and is not called with 'this' or 'super', // it should not using 'this' existed in context. CJC_ASSERT(!IsCallRegularInit(expr)); Value* thisObj = nullptr; auto loc = TranslateLocation(expr); if (auto memAccess = DynamicCast(expr.baseFunc.get())) { if (AST::IsThisOrSuper(*memAccess->baseExpr)) { // Case A: the member access is in form like "this.f()" or "super.f()", then we just get the "this" param // from current func thisObj = GetCurrentThisObject(*resolved); } else { // Case B: otherwise, we will generate the base part of the member access and get the "this" auto thisObjValueInfo = TranslateExprAsLeftValue(*memAccess->baseExpr); thisObj = thisObjValueInfo.base; // polish this if (!thisObjValueInfo.path.empty()) { auto lhsCustomType = StaticCast(thisObj->GetType()->StripAllRefs()); if (thisObj->GetType()->IsRef()) { thisObj = CreateGetElementRefWithPath(loc, thisObj, thisObjValueInfo.path, currentBlock, *lhsCustomType); } else { auto memberType = GetInstMemberTypeByName(*lhsCustomType, thisObjValueInfo.path, builder); auto getMember = CreateAndAppendExpression( loc, memberType, thisObj, thisObjValueInfo.path, currentBlock); thisObj = getMember->GetResult(); } } } // this case only happends when extending Unit or Nothing type if (thisObj == nullptr) { if (memAccess->baseExpr->ty->IsUnit()) { thisObj = CreateAndAppendConstantExpression(builder.GetUnitTy(), *currentBlock) ->GetResult(); } else if (memAccess->baseExpr->ty->IsNothing()) { thisObj = CreateAndAppendConstantExpression(builder.GetNothingType(), *currentBlock) ->GetResult(); } else { CJC_ABORT(); } } } else { thisObj = GetCurrentThisObject(*resolved); } auto thisObjTy = thisObj->GetType(); CJC_ASSERT(thisObjTy->IsReferenceTypeWithRefDims(CLASS_REF_DIM) || thisObjTy->IsReferenceTypeWithRefDims(1) || thisObjTy->IsValueOrGenericTypeWithRefDims(1) || thisObjTy->IsValueOrGenericTypeWithRefDims(0)); if (!needsMutableThis) { if (thisObjTy->IsReferenceTypeWithRefDims(CLASS_REF_DIM) || thisObjTy->IsValueOrGenericTypeWithRefDims(1)) { auto pureThisObjTy = thisObjTy->StripAllRefs(); auto targetTy = pureThisObjTy; if (pureThisObjTy->IsReferenceType()) { targetTy = builder.GetType(pureThisObjTy); } thisObj = CreateAndAppendExpression(loc, targetTy, thisObj, currentBlock)->GetResult(); } } else { CJC_ASSERT(!thisObjTy->IsValueOrGenericTypeWithRefDims(0)); if (thisObjTy->IsReferenceTypeWithRefDims(CLASS_REF_DIM)) { auto pureThisObjTy = thisObjTy->StripAllRefs(); auto targetTy = builder.GetType(pureThisObjTy); thisObj = CreateAndAppendExpression(loc, targetTy, thisObj, currentBlock)->GetResult(); } } args.insert(args.begin(), thisObj); } void Translator::TranslateTrivialArgsWithSugar( const AST::CallExpr& expr, std::vector& args, const std::vector& expectedArgTys) { Ptr resolved = expr.resolvedFunction; CJC_ASSERT(resolved->funcBody && !resolved->funcBody->paramLists.empty()); CJC_ASSERT(resolved->funcBody->paramLists[0]->params.size() == expr.desugarArgs.value().size() || resolved->hasVariableLenArg); auto& argExprs = expr.desugarArgs.value(); auto& params = resolved->funcBody->paramLists[0]->params; const auto& loc = TranslateLocation(expr); for (size_t i = 0; i < argExprs.size(); i++) { if (argExprs[i]->TestAttr(AST::Attribute::HAS_INITIAL)) { // In this case, the corresponding func param has default value which has been desugared into // a default-value-func, thus the func arg expr here will becomes a call to the default-value-func // which use all the previous args as input. For example: // Original Code: // func foo(x: Int64, y!: Int64 = x + 1) {...} // let res = foo(1) // // Desugared Result: // func foo(x: Int64, y: Int64) {...} // func foo_y_defalut_value(x: Int64) { x + 1 } // let res = foo(1, foo_y_defalut_value(1)) // 1) get the default-value-func CJC_NULLPTR_CHECK(params[i]->desugarDecl.get()); auto defaultValueFunc = GetSymbolTable(*params[i]->desugarDecl); // 2) collect the previous args as the input of the call to default-value-func std::vector defaultValueFuncArgs; for (size_t j = 0; j < args.size(); ++j) { defaultValueFuncArgs.emplace_back(args[j]); } // 3) calculte the instantiated type of the default-value-func auto instDefaultValueFuncRetTy = TranslateType(*argExprs[i]->ty); std::vector instDefaultValueFuncParamInstTys; for (size_t k = 0; k < defaultValueFuncArgs.size(); ++k) { instDefaultValueFuncParamInstTys.emplace_back(defaultValueFuncArgs[k]->GetType()); } auto instDefaultValueFuncTy = builder.GetType(instDefaultValueFuncParamInstTys, instDefaultValueFuncRetTy); auto thisInstType = GetMemberFuncCallerInstType(expr); /** * class A { * func foo(x!: Int64 = 1) {} * } * if `foo` is instantiated by `Bool`, then new instantiated func `foo_Bool` is global func, * not a member func, so instantiated func `x.0` is also a global func. */ if (auto funcBase = DynamicCast(defaultValueFunc); funcBase && funcBase->GetParentCustomTypeDef() == nullptr) { thisInstType = nullptr; } std::vector instArgs; // e.g. class A { init(a!: Int64 = 1) }; var x = A() // `A()` is callExpr for class constructor, it use `instTys` to store Int32, but desugar func is // `a.0(): Int64 {...}` without generic param. So this apply should be `A(a.0())`. if (expr.resolvedFunction == nullptr || !IsClassOrEnumConstructor(*expr.resolvedFunction)) { auto instParamsInOwnerFunc = StaticCast(expr.baseFunc.get())->instTys; for (auto ty : instParamsInOwnerFunc) { instArgs.emplace_back(TranslateType(*ty)); } } // check the this type and instParentCustomType value here auto defaultValueCall = GenerateFuncCall(*defaultValueFunc, instDefaultValueFuncTy, std::move(instArgs), thisInstType, defaultValueFuncArgs, loc); auto ret = defaultValueCall->GetResult(); Value* castedRet = ret; // remove this condition later if (!expectedArgTys.empty()) { castedRet = GenerateLoadIfNeccessary(*ret, false, false, false, loc); CJC_ASSERT(expectedArgTys.size() > i); castedRet = TypeCastOrBoxIfNeeded(*castedRet, *expectedArgTys[i], loc); } args.emplace_back(castedRet); } else { Type* expectedArgTy = nullptr; if (!expectedArgTys.empty()) { if (i < expectedArgTys.size()) { expectedArgTy = expectedArgTys[i]; } else { CJC_ASSERT(resolved->ty->IsCFunc()); } } Value* argVal = TranslateTrivialArgWithNoSugar(*argExprs[i], expectedArgTy, loc); args.emplace_back(argVal); } } } Value* Translator::TranslateTrivialArgWithNoSugar( const AST::FuncArg& arg, Type* expectedArgTy, const DebugLocation& loc) { Value* argVal = nullptr; if (arg.withInout) { auto argLeftValInfo = TranslateExprAsLeftValue(*arg.expr); argVal = GenerateLeftValue(argLeftValInfo, loc); auto ty = TranslateType(*arg.ty); auto callContext = IntrisicCallContext { .kind = IntrinsicKind::INOUT_PARAM, .args = std::vector{argVal} }; argVal = CreateAndAppendExpression(loc, ty, callContext, currentBlock)->GetResult(); } else { argVal = TranslateExprArg(*arg.expr); // This load should be able to remove since we are always generate right value from // `TranslateASTNode` API argVal = GenerateLoadIfNeccessary(*argVal, false, false, false, loc); if (expectedArgTy) { argVal = TypeCastOrBoxIfNeeded(*argVal, *expectedArgTy, loc); } } CJC_NULLPTR_CHECK(argVal); return argVal; } void Translator::TranslateTrivialArgsWithNoSugar( const AST::CallExpr& expr, std::vector& args, const std::vector& expectedArgTys) { auto loc = TranslateLocation(expr); bool needCastToExpectedTy = !expectedArgTys.empty(); size_t i = 0; for (auto& arg : expr.args) { Type* expectedArgTy = nullptr; if (needCastToExpectedTy) { if (i < expectedArgTys.size()) { expectedArgTy = expectedArgTys[i]; } else { CJC_ASSERT(expr.resolvedFunction && expr.resolvedFunction->ty->IsCFunc()); } } Value* argVal = TranslateTrivialArgWithNoSugar(*arg, expectedArgTy, loc); args.emplace_back(argVal); ++i; } } void Translator::TranslateTrivialArgs( const AST::CallExpr& expr, std::vector& args, const std::vector& expectedArgTys) { if (expr.desugarArgs.has_value()) { TranslateTrivialArgsWithSugar(expr, args, expectedArgTys); } else { TranslateTrivialArgsWithNoSugar(expr, args, expectedArgTys); } } Type* Translator::HandleSpecialIntrinsic( IntrinsicKind intrinsicKind, std::vector& args, Type* retTy) { if (intrinsicKind == BLACK_BOX) { /// black hole args change to reference. for (auto& arg : args) { auto type = arg->GetType(); if (type->IsRef()) { continue; } Ptr newArg = arg; if (arg->IsLocalVar()) { auto localVar = StaticCast(arg); auto expr = localVar->GetExpr(); if (expr->IsLoad()) { newArg = StaticCast(expr)->GetLocation(); retTy = newArg->GetType(); } else { auto loc = arg->GetDebugLocation(); retTy = builder.GetType(type); newArg = CreateAndAppendExpression(loc, retTy, type, currentBlock)->GetResult(); (void)CreateAndAppendExpression( loc, builder.GetUnitTy(), arg, newArg, currentBlock)->GetResult(); } } arg = newArg; } } return retTy; } Ptr Translator::TranslateIntrinsicCall(const AST::CallExpr& expr) { // Conditions to check if this is a call to intrinsic if (expr.callKind != AST::CallKind::CALL_INTRINSIC_FUNCTION) { return nullptr; } auto target = expr.baseFunc->GetTarget(); CJC_NULLPTR_CHECK(target); std::string identfifier = target->identifier; // Translate code position info const auto& loc = TranslateLocation(expr); auto ty = chirTy.TranslateType(*expr.ty); // Get the intrinsic kind std::string packageName{}; if (target->genericDecl) { packageName = target->genericDecl->fullPackageName; } else if (target->outerDecl && target->outerDecl->genericDecl) { packageName = target->outerDecl->genericDecl->fullPackageName; } else { packageName = target->fullPackageName; } CHIR::IntrinsicKind intrinsicKind{NOT_INTRINSIC}; if (auto it = packageMap.find(packageName); it != packageMap.end()) { CJC_ASSERT(it->second.find(identfifier) != it->second.end()); intrinsicKind = it->second.at(identfifier); } else if (auto it1 = headlessIntrinsics.find(identfifier); it1 != headlessIntrinsics.end()) { intrinsicKind = it1->second; } // Translate arguments std::vector args; TranslateTrivialArgs(expr, args, std::vector{}); auto retTy = HandleSpecialIntrinsic(intrinsicKind, args, ty); auto ne = StaticCast(expr.baseFunc.get()); // wrap this into the `GenerateFuncCall` API auto callContext = IntrisicCallContext { .kind = intrinsicKind, .args = args, .instTypeArgs = TranslateASTTypes(ne->instTys) }; auto intriVar = TryCreate(currentBlock, loc, retTy, callContext)->GetResult(); // what is this for if (expr.ty->IsUnit()) { // Codegen will not generate valid 'unit' value for intrinsic call. return CreateAndAppendConstantExpression(builder.GetUnitTy(), *currentBlock)->GetResult(); } if (retTy != ty && intrinsicKind == BLACK_BOX) { return CreateAndAppendExpression(ty, intriVar, currentBlock)->GetResult(); } return intriVar; } Ptr Translator::TranslateForeignFuncCall(const AST::CallExpr& expr) { // Conditions to check if this is a call to foreign func if (expr.resolvedFunction == nullptr) { return nullptr; } if (!expr.resolvedFunction->TestAttr(AST::Attribute::FOREIGN)) { return nullptr; } auto resolvedFunction = expr.resolvedFunction; // Translate code position info const auto& loc = TranslateLocation(expr); const auto& warningLoc = TranslateLocation(*expr.baseFunc); // polish this API auto [paramInstTys, retInstTy] = GetMemberFuncParamAndRetInstTypes(expr); bool hasVarArg = StaticCast(expr.resolvedFunction->ty)->hasVariableLenArg; bool isCFunc = StaticCast(expr.resolvedFunction->ty)->isC; auto instTargetFuncTy = builder.GetType(paramInstTys, retInstTy, hasVarArg, isCFunc); // Translate arguments std::vector args; TranslateTrivialArgs(expr, args, paramInstTys); auto callee = GetSymbolTable(*resolvedFunction); CJC_ASSERT(callee != nullptr && "TranslateApply: not supported callee now!"); auto funcCall = GenerateFuncCall(*callee, instTargetFuncTy, {}, nullptr, args, loc); if (HasNothingTypeArg(args)) { funcCall->Set(warningLoc); } auto targetCallResTy = TranslateType(*expr.ty); auto castedCallRes = TypeCastOrBoxIfNeeded(*funcCall->GetResult(), *targetCallResTy, loc); return castedCallRes; } Ptr Translator::TranslateCStringCtorCall(const AST::CallExpr& expr) { if (auto target = DynamicCast(expr.baseFunc->GetTarget()); target && target->type == AST::BuiltInType::CSTRING) { auto ty = TranslateType(*expr.ty); const auto& loc = TranslateLocation(expr); CJC_ASSERT(expr.args.size() == 1); auto argVal = TranslateExprArg(*expr.args[0]); auto callContext = IntrisicCallContext { .kind = IntrinsicKind::CSTRING_INIT, .args = std::vector{argVal} }; return CreateAndAppendExpression(loc, ty, callContext, currentBlock)->GetResult(); } return nullptr; } Ptr Translator::TranslateEnumCtorCall(const AST::CallExpr& expr) { // Conditions to check if this is a call to construct an enum value if (expr.resolvedFunction == nullptr) { return nullptr; } if (!expr.resolvedFunction->TestAttr(AST::Attribute::ENUM_CONSTRUCTOR)) { return nullptr; } auto resolvedFunction = expr.resolvedFunction; // Translate code position info const auto& loc = TranslateLocation(expr); // Get the enum case ID auto enumDecl = resolvedFunction->funcBody->parentEnum; CJC_NULLPTR_CHECK(enumDecl); auto& constrs = enumDecl->constructors; auto fieldIt = std::find_if(constrs.begin(), constrs.end(), [&resolvedFunction](auto const& decl) -> bool { return resolvedFunction == decl.get(); }); CJC_ASSERT(fieldIt != constrs.end()); auto enumId = static_cast(std::distance(constrs.begin(), fieldIt)); // Calculate instantiated callee func type // polish this API auto [paramInstTys, retInstTy] = GetMemberFuncParamAndRetInstTypes(expr); // Translate arguments std::vector args; TranslateTrivialArgs(expr, args, paramInstTys); auto ty = chirTy.TranslateType(*expr.ty); auto selectorTy = GetSelectorType(*StaticCast(expr.ty)); CJC_ASSERT(ty->IsEnum()); auto constExpr = (selectorTy->IsBoolean() ? CreateAndAppendConstantExpression( loc, selectorTy, *currentBlock, static_cast(enumId)) : CreateAndAppendConstantExpression(loc, selectorTy, *currentBlock, enumId)); args.insert(args.begin(), constExpr->GetResult()); return CreateAndAppendExpression(TranslateLocation(expr), ty, args, currentBlock)->GetResult(); } // merge this function with the right value version Translator::LeftValueInfo Translator::TranslateStructOrClassCtorCallAsLeftValue(const AST::CallExpr& expr) { // Conditions to check if this is a call to member func (constructor is not counted here) if (expr.resolvedFunction == nullptr) { return LeftValueInfo(nullptr, {}); } if (!expr.resolvedFunction->TestAttr(AST::Attribute::CONSTRUCTOR)) { return LeftValueInfo(nullptr, {}); } if (expr.resolvedFunction->outerDecl == nullptr) { return LeftValueInfo(nullptr, {}); } if (!(expr.resolvedFunction->outerDecl->astKind == AST::ASTKind::CLASS_DECL || expr.resolvedFunction->outerDecl->astKind == AST::ASTKind::STRUCT_DECL)) { return LeftValueInfo(nullptr, {}); } // Specially, static init is not handled here if (expr.resolvedFunction->TestAttr(AST::Attribute::STATIC)) { return LeftValueInfo(nullptr, {}); } // Translate code position info const auto& loc = TranslateLocation(expr); const auto& warningLoc = TranslateLocation(*expr.baseFunc); // Calculate instantiated callee func type auto thisTy = chirTy.TranslateType(*expr.ty); if (expr.ty->IsClass() || expr.ty->IsArray()) { thisTy = StaticCast(thisTy)->GetBaseType(); } auto [paramInstTys, retInstTy] = GetMemberFuncParamAndRetInstTypes(expr); auto paramInstTysWithoutThis = paramInstTys; paramInstTys.insert(paramInstTys.begin(), builder.GetType(thisTy)); auto instTargetFuncTy = builder.GetType(paramInstTys, builder.GetVoidTy()); // Translate arguments std::vector args; TranslateTrivialArgs(expr, args, paramInstTysWithoutThis); Value* thisArg = nullptr; if (IsSuperOrThisCall(expr)) { // For super constructor call site, the `this` arg of current constructor should be passed into super // constructor auto curFunc = GetCurrentFunc(); CJC_NULLPTR_CHECK(curFunc); thisArg = curFunc->GetParam(0); } else { // For trivial constructor call site, the object allocation is lifted out and then pass into constructor as // `this` arg auto allocateThis = TryCreate(currentBlock, loc, builder.GetType(thisTy), thisTy); allocateThis->Set(loc); thisArg = allocateThis->GetResult(); } args.insert(args.begin(), thisArg); auto callee = GetSymbolTable(*expr.resolvedFunction); CJC_ASSERT(callee != nullptr && "TranslateApply: not supported callee now!"); auto funcCall = GenerateFuncCall( *callee, instTargetFuncTy, {}, builder.GetType(thisTy), args, loc); if (expr.callKind == AST::CallKind::CALL_SUPER_FUNCTION) { StaticCast(funcCall)->SetSuperCall(); } if (HasNothingTypeArg(args)) { funcCall->Set(warningLoc); } return LeftValueInfo(thisArg, {}); } // Conditions to check if this is a call to member func (constructor is not counted here) static bool IsCtorCall(const AST::CallExpr& expr) { return expr.resolvedFunction && expr.resolvedFunction->TestAttr(AST::Attribute::CONSTRUCTOR) && expr.resolvedFunction->outerDecl && (expr.resolvedFunction->outerDecl->astKind == AST::ASTKind::CLASS_DECL || expr.resolvedFunction->outerDecl->astKind == AST::ASTKind::STRUCT_DECL) && // Specially, static init is not handled here !expr.resolvedFunction->TestAttr(AST::Attribute::STATIC); } Value* Translator::TranslateStructOrClassCtorCall(const AST::CallExpr& expr) { // Translate code position info const auto& loc = TranslateLocation(expr); const auto& warningLoc = TranslateLocation(*expr.baseFunc); // Calculate instantiated callee func type auto thisTy = chirTy.TranslateType(*expr.ty); if (expr.ty->IsClass() || expr.ty->IsArray()) { thisTy = StaticCast(thisTy)->GetBaseType(); } auto [paramInstTys, retInstTy] = GetMemberFuncParamAndRetInstTypes(expr); auto paramInstTysWithoutThis = paramInstTys; paramInstTys.insert(paramInstTys.begin(), builder.GetType(thisTy)); auto instTargetFuncTy = builder.GetType(paramInstTys, builder.GetVoidTy()); // Translate arguments std::vector args; TranslateTrivialArgs(expr, args, paramInstTysWithoutThis); Value* thisArg = nullptr; if (IsSuperOrThisCall(expr)) { // For super constructor call site, the `this` arg of current constructor should be passed into super // constructor auto curFunc = GetCurrentFunc(); CJC_NULLPTR_CHECK(curFunc); thisArg = curFunc->GetParam(0); } else { // For trivial constructor call site, the object allocation is lifted out and then pass into constructor as // `this` arg auto allocateThis = TryCreate(currentBlock, loc, builder.GetType(thisTy), thisTy); allocateThis->Set(loc); thisArg = allocateThis->GetResult(); } args.insert(args.begin(), thisArg); auto callee = GetSymbolTable(*expr.resolvedFunction); CJC_ASSERT(callee != nullptr && "TranslateApply: not supported callee now!"); auto funcCall = GenerateFuncCall( *callee, instTargetFuncTy, {}, builder.GetType(thisTy), args, loc); if (expr.callKind == AST::CallKind::CALL_SUPER_FUNCTION) { StaticCast(funcCall)->SetSuperCall(); } if (HasNothingTypeArg(args)) { funcCall->Set(warningLoc); } if (expr.resolvedFunction->outerDecl->astKind == AST::ASTKind::STRUCT_DECL) { if (IsSuperOrThisCall(expr)) { return nullptr; // should be: return nullptr; } auto load = CreateAndAppendExpression(loc, thisTy, thisArg, currentBlock); // this load should be removed if it is a super/this call, but it will trigger IRChecker error in: if (IsSuperOrThisCall(expr)) { load->Set(SkipKind::SKIP_DCE_WARNING); // should be: return nullptr; } return load->GetResult(); } return thisArg; } Ptr Translator::TranslateCFuncConstructorCall(const AST::CallExpr& expr) { if (!IsValidCFuncConstructorCall(expr)) { return nullptr; } auto& arg = expr.args[0]->expr; auto argValue = TranslateExprArg(*arg, *TranslateType(*expr.ty), true); return argValue; } Ptr Translator::TranslateFuncTypeValueCall(const AST::CallExpr& expr) { // Conditions to check if this is a call to func type value if (expr.resolvedFunction != nullptr) { return nullptr; } // Translate code position info const auto& loc = TranslateLocation(expr); // translate callee before args translate // eg: foo()(a), translate foo() first, then translate args a auto callee = TranslateExprArg(*expr.baseFunc); // Translate arguments std::vector args; // we should calcuate the expected args type here TranslateTrivialArgsWithNoSugar(expr, args, std::vector{}); CJC_ASSERT(callee != nullptr && "TranslateApply: not supported callee now!"); callee = GenerateLoadIfNeccessary(*callee, false, false, false, loc); auto funcCall = GenerateFuncCall(*callee, StaticCast(callee->GetType()), {}, nullptr, args, loc); if (HasNothingTypeArg(args)) { funcCall->Set(loc); } auto targetCallResTy = TranslateType(*expr.ty); auto castedCallRes = TypeCastOrBoxIfNeeded(*funcCall->GetResult(), *targetCallResTy, loc); return castedCallRes; } namespace Cangjie::CHIR { std::string GetSplitOperatorName(const std::string& name, OverflowStrategy st) { return OverflowStrategyPrefix(st) + name; } /* public common interface I { common func foo6(): Unit { println("I::foo6 common") } } public common interface I2 <: I {} public struct A <: I2 {} public func runInCommon() { let a = A() // NOTE: foo6 call MUST NOT be devirtualized // `Invoke` should be generated instead of `Apply` // Because of implementation can be moved to more close parent(I2) a.foo6() } // NOTE: More precise check: there is common class parent that is child of those providing current implementation. */ inline bool CanActualFuncBeMovedInPlatform(const Type& thisType, CHIRBuilder& builder) { if (!thisType.IsStruct() && !thisType.IsEnum()) { return false; } auto& customType = StaticCast(thisType); auto inheritanceList = customType.GetCustomTypeDef()->GetSuperTypesRecusively(builder); for (auto parentType: inheritanceList) { auto parent = parentType->GetCustomTypeDef(); if (parent->TestAttr(Attribute::COMMON) || parent->TestAttr(Attribute::PLATFORM)) { return true; } } return false; } } bool Translator::IsOverflowOpCall(const AST::FuncDecl& func) { if (!Is(func.outerDecl)) { return false; } return IsOverflowOperator(func.identifier, *StaticCast(TranslateType(*func.ty))); } Value* Translator::TranslateNonStaticMemberFuncCall(const AST::CallExpr& expr) { auto resolvedFunction = expr.resolvedFunction; CJC_NULLPTR_CHECK(resolvedFunction); CJC_ASSERT(!resolvedFunction->TestAttr(AST::Attribute::STATIC)); bool isMutFunc = (resolvedFunction->TestAttr(AST::Attribute::MUT) || resolvedFunction->isSetter) && !resolvedFunction->isGetter; // Translate code position info const auto& loc = TranslateLocation(expr); const auto& warningLoc = TranslateLocation(*expr.baseFunc); // Note that, `thisType` might be different from the parent custom type of the callee func, // since we can call a func inherited from upstream type Type* thisType; if (auto ma = DynamicCast(expr.baseFunc.get())) { // polish here thisType = TranslateType(*ma->baseExpr->ty)->StripAllRefs(); } else { // If there is no `this` object, then we must be inside of another non-static memeber func auto currentFunc = currentBlock->GetTopLevelFunc(); CJC_NULLPTR_CHECK(currentFunc); auto outerDef = currentFunc->GetParentCustomTypeDef(); CJC_NULLPTR_CHECK(outerDef); Type* outerType = nullptr; if (outerDef->IsExtend()) { auto outerExtend = StaticCast(outerDef); outerType = outerExtend->GetExtendedType(); } else { outerType = StaticCast(outerDef->GetType()); } thisType = outerType; } CJC_ASSERT(!thisType->IsRef()); auto thisRefType = builder.GetType(thisType); auto funcInstTypeArgs = GetFuncInstArgs(expr); // polish this API auto [paramInstTys, retInstTy] = GetMemberFuncParamAndRetInstTypes(expr); auto paramInstTysWithoutThis = paramInstTys; auto tempParamInstTys = paramInstTys; tempParamInstTys.insert(tempParamInstTys.begin(), thisRefType); auto tempInstTargetFuncTy = builder.GetType(tempParamInstTys, retInstTy); // Translate arguments std::vector args; TranslateThisObjectForNonStaticMemberFuncCall(expr, args, isMutFunc || IsPropertySetterCall(expr)); TranslateTrivialArgs(expr, args, paramInstTysWithoutThis); bool calledByInheritableTy = thisType->IsClass() || thisType->IsGeneric(); bool isSuperCall = expr.callKind == AST::CallKind::CALL_SUPER_FUNCTION; Expression* funcCall = nullptr; bool mayFuncBeMovedInPlatform = CanActualFuncBeMovedInPlatform(*thisType, builder); // NOTE: Looks like current devirtualization assumptions is quite naive, it can be eager. if ((calledByInheritableTy || mayFuncBeMovedInPlatform) && !isSuperCall && IsVirtualMember(*resolvedFunction)) { CJC_ASSERT(args.size() >= 1); auto obj = args[0]; args.erase(args.begin()); auto funcName = expr.resolvedFunction->identifier.Val(); if (IsOverflowOpCall(*resolvedFunction)) { funcName = GetSplitOperatorName(funcName, expr.overflowStrategy); } auto outerDecl = expr.resolvedFunction->outerDecl; auto genericThisTy = TranslateType(*outerDecl->ty); if (IsStructMutFunction(*expr.resolvedFunction)) { genericThisTy = builder.GetType(genericThisTy); } auto instParentCustomTy = GetExactParentType(*thisType, *resolvedFunction, *tempInstTargetFuncTy, funcInstTypeArgs, true); Ptr instParentTy = instParentCustomTy; if (instParentCustomTy->IsReferenceType()) { instParentTy = builder.GetType(instParentCustomTy); } else if (instParentCustomTy->IsStruct() && isMutFunc) { instParentTy = builder.GetType(instParentCustomTy); } paramInstTys.insert(paramInstTys.begin(), instParentTy); auto instTargetFuncTy = builder.GetType(paramInstTys, retInstTy); const AST::FuncDecl* originalFuncDecl; if (auto decl = typeManager.GetTopOverriddenFuncDecl(resolvedFunction)) { originalFuncDecl = decl; } else { originalFuncDecl = resolvedFunction; } auto originalFuncType = StaticCast(TranslateType(*originalFuncDecl->ty)); if (!originalFuncDecl->TestAttr(AST::Attribute::STATIC)) { auto originalOuterDecl = originalFuncDecl->outerDecl; CJC_NULLPTR_CHECK(originalOuterDecl); auto objType = GetNominalSymbolTable(*originalOuterDecl)->GetType(); if (objType->IsReferenceType() || (originalFuncDecl->TestAttr(AST::Attribute::MUT) && objType->IsStruct())) { objType = builder.GetType(objType); } auto paramTypes = originalFuncType->GetParamTypes(); paramTypes.insert(paramTypes.begin(), objType); originalFuncType = builder.GetType(paramTypes, originalFuncType->GetReturnType()); } std::vector genericTypeParams; if (originalFuncDecl->TestAttr(AST::Attribute::GENERIC)) { for (const auto& genericTy : originalFuncDecl->funcBody->generic->typeParameters) { genericTypeParams.emplace_back(StaticCast(TranslateType(*(genericTy->ty)))); } } InstInvokeCalleeInfo dynamicDispatchFuncInfo{funcName, instTargetFuncTy, originalFuncType, funcInstTypeArgs, genericTypeParams, thisRefType}; funcCall = GenerateDynmaicDispatchFuncCall(dynamicDispatchFuncInfo, args, obj, nullptr, loc); PrintDevirtualizationMessage(expr, "invoke"); } else { auto instParentCustomTy = GetExactParentType(*thisType, *resolvedFunction, *tempInstTargetFuncTy, funcInstTypeArgs, false); Ptr instParentTy = instParentCustomTy; if (instParentCustomTy->IsReferenceType()) { instParentTy = builder.GetType(instParentCustomTy); } else if (instParentCustomTy->IsStruct() && isMutFunc) { instParentTy = builder.GetType(instParentCustomTy); } auto callee = GetSymbolTable(*resolvedFunction); CJC_ASSERT(callee != nullptr && "TranslateApply: not supported callee now!"); // call a mut func from parent type if (thisType->IsStruct() && instParentCustomTy != thisType && callee->TestAttr(Attribute::MUT)) { paramInstTys.insert(paramInstTys.begin(), thisRefType); } else { paramInstTys.insert(paramInstTys.begin(), instParentTy); } auto instTargetFuncTy = builder.GetType(paramInstTys, retInstTy); funcCall = GenerateFuncCall(*callee, instTargetFuncTy, funcInstTypeArgs, thisRefType, args, loc); PrintDevirtualizationMessage(expr, "apply"); } // polish this if (HasNothingTypeArg(args)) { funcCall->Set(warningLoc); } auto targetCallResTy = TranslateType(*expr.ty); auto castedCallRes = TypeCastOrBoxIfNeeded(*funcCall->GetResult(), *targetCallResTy, loc); return castedCallRes; } Value* Translator::CreateGetRTTIWrapper(Value* value, Block* bl, const DebugLocation& loc) { auto type = value->GetType(); Expression* expr; // GetRTTI can only be used on Class& or This&. use GetRTTIStatic otherwise if (Is(type) && (type->StripAllRefs()->IsClass() || type->StripAllRefs()->IsThis())) { expr = builder.CreateExpression(loc, builder.GetUnitTy(), value, bl); } else { expr = builder.CreateExpression(loc, builder.GetUnitTy(), type, bl); } bl->AppendExpression(expr); return expr->GetResult(); } Value* Translator::TranslateStaticMemberFuncCall(const AST::CallExpr& expr) { auto resolvedFunction = expr.resolvedFunction; CJC_NULLPTR_CHECK(resolvedFunction); CJC_ASSERT(resolvedFunction->TestAttr(AST::Attribute::STATIC)); // Translate code position info const auto& loc = TranslateLocation(expr); InstCalleeInfo instCallInfo; if (auto ma = DynamicCast(expr.baseFunc.get())) { instCallInfo = GetInstCalleeInfoFromMemberAccess(*ma); } else { auto funcRef = StaticCast(expr.baseFunc.get()); instCallInfo = GetInstCalleeInfoFromRefExpr(*funcRef); } auto paramInstTysWithoutThisTy = instCallInfo.instParamTys; if (!resolvedFunction->TestAttr(AST::Attribute::STATIC)) { paramInstTysWithoutThisTy.erase(paramInstTysWithoutThisTy.begin()); } // Translate arguments std::vector args; TranslateTrivialArgs(expr, args, paramInstTysWithoutThisTy); LocalVar* ret = nullptr; if (instCallInfo.isVirtualFuncCall) { // InvokeStatic Value* rtti = nullptr; auto topLevelFunc = currentBlock->GetTopLevelFunc(); /** * open class A { * static func foo() { return 1 } * func goo() { foo() } // we need to use `GetRTTI`, not `GetRTTIStatic` * } * class B <: A { * static func foo() { return 2 } * } * var a: A = B() * a.goo() // the return value is 2, if `goo` is inlined here, the CHIR must be like: * // InvokeStatic(GetRTTI(a)) */ if (expr.baseFunc->astKind == AST::ASTKind::REF_EXPR && !topLevelFunc->TestAttr(Attribute::STATIC)) { auto thisObj = topLevelFunc->GetParam(0); rtti = CreateAndAppendExpression(builder.GetUnitTy(), thisObj, currentBlock)->GetResult(); } else { rtti = CreateAndAppendExpression( builder.GetUnitTy(), instCallInfo.thisType->StripAllRefs(), currentBlock)->GetResult(); } auto invokeInfo = GenerateInvokeCallContext(instCallInfo, *rtti, *resolvedFunction, args); ret = TryCreate(currentBlock, loc, instCallInfo.instRetTy, invokeInfo)->GetResult(); } else { auto callee = GetSymbolTable(*resolvedFunction); auto funcCallContext = FuncCallContext { .args = args, .instTypeArgs = instCallInfo.instantiatedTypeArgs, .thisType = instCallInfo.thisType }; ret = TryCreate(currentBlock, loc, instCallInfo.instRetTy, callee, funcCallContext)->GetResult(); } if (HasNothingTypeArg(args)) { const auto& warningLoc = TranslateLocation(*expr.baseFunc); ret->GetExpr()->Set(warningLoc); } return TypeCastOrBoxIfNeeded(*ret, *TranslateType(*expr.ty), loc); } Value* Translator::TranslateMemberFuncCall(const AST::CallExpr& expr) { // Instance member function、static member function、global func. auto resolvedFunction = expr.resolvedFunction; CJC_NULLPTR_CHECK(resolvedFunction); CJC_ASSERT(!resolvedFunction->TestAttr(AST::Attribute::CONSTRUCTOR)); if (resolvedFunction->TestAttr(AST::Attribute::STATIC)) { return TranslateStaticMemberFuncCall(expr); } return TranslateNonStaticMemberFuncCall(expr); } Value* Translator::TranslateTrivialFuncCall(const AST::CallExpr& expr) { // Conditions to check if this is a call to declared function which // can be a global func or local func if (expr.resolvedFunction == nullptr) { return nullptr; } auto resolvedFunction = expr.resolvedFunction; // Translate code position info const auto& loc = TranslateLocation(expr); const auto& warningLoc = TranslateLocation(*expr.baseFunc); auto funcInstTypeArgs = GetFuncInstArgs(expr); // polish this API auto [paramInstTys, retInstTy] = GetMemberFuncParamAndRetInstTypes(expr); auto instTargetFuncTy = builder.GetType(paramInstTys, retInstTy); // Translate arguments std::vector args; TranslateTrivialArgs(expr, args, paramInstTys); auto callee = GetSymbolTable(*resolvedFunction); CJC_ASSERT(callee != nullptr && "TranslateApply: not supported callee now!"); auto funcCall = GenerateFuncCall(*callee, instTargetFuncTy, funcInstTypeArgs, nullptr, args, loc); // polish this if (HasNothingTypeArg(args)) { funcCall->Set(warningLoc); } auto targetCallResTy = TranslateType(*expr.ty); auto castedCallRes = TypeCastOrBoxIfNeeded(*funcCall->GetResult(), *targetCallResTy, loc); return castedCallRes; } static bool IsCallingConstructor(const AST::CallExpr& expr) { if (expr.resolvedFunction == nullptr) { return false; } if (expr.callKind == AST::CallKind::CALL_SUPER_FUNCTION) { return true; } // non-static init func, because expr.ty is Unit in static init return expr.resolvedFunction->TestAttr(AST::Attribute::CONSTRUCTOR) && !expr.resolvedFunction->TestAttr(AST::Attribute::STATIC); } Ptr Translator::GetMemberFuncCallerInstType(const AST::CallExpr& expr, bool needExactTy) { Type* callerType = nullptr; if (auto memAccess = DynamicCast(expr.baseFunc.get()); memAccess) { // xxx.memberFunc() if (!IsPackageMemberAccess(*memAccess)) { callerType = TranslateType(*memAccess->baseExpr->ty); } else if (IsCallingConstructor(expr)) { callerType = TranslateType(*expr.ty); } } else if (IsCallingConstructor(expr)) { callerType = TranslateType(*expr.ty); } else if (expr.resolvedFunction != nullptr && expr.resolvedFunction->outerDecl != nullptr && expr.resolvedFunction->outerDecl->IsNominalDecl()) { // call own member function in nominal decl, there are 3 cases: auto outerDef = currentBlock->GetTopLevelFunc()->GetParentCustomTypeDef(); if (outerDef != nullptr) { if (auto exDef = DynamicCast(outerDef)) { // 1. struct A { func foo() {} }; extend A { func goo() { foo() } } // ^^^ call `foo` in extend A, then return `A` callerType = exDef->GetExtendedType(); } else { // 2. struct A { func foo() {}; func goo() { foo() } } // ^^^ call `foo` in struct A, then return `A` callerType = outerDef->GetType(); } if (callerType->IsClassOrArray()) { callerType = builder.GetType(callerType); } } else if (IsStaticInit(*expr.resolvedFunction)) { // 3. in CHIR, we treat `static.init()` as global function, not member function, // because its outerDecl is something like `class A`, if it's member function, ir is as follows: // Func gv$_init() { // Apply(static.init)(A, [], Unit) // `T` is not declared in this scope // } callerType = nullptr; } else { // 4. struct A { static let a = foo(); func foo() {} } // ^^^ call `foo` while initializing static member var, then return `A` callerType = TranslateType(*expr.resolvedFunction->outerDecl->ty); } } if (needExactTy && callerType != nullptr && expr.resolvedFunction != nullptr) { auto [paramInstTys, retInstTy] = GetMemberFuncParamAndRetInstTypes(expr); if (expr.resolvedFunction != nullptr && !expr.resolvedFunction->TestAttr(AST::Attribute::STATIC)) { paramInstTys.insert(paramInstTys.begin(), callerType); } auto instFuncType = builder.GetType(paramInstTys, retInstTy); std::vector funcInstTypeArgs; if (auto nre = DynamicCast(expr.baseFunc.get()); nre) { // a constructor mustn't have generic param, `init()` is error, but `baseFunc` may have `instTys` // e.g. let x = CA() // `CA()` is CallExpr, if (expr.resolvedFunction == nullptr || !expr.resolvedFunction->TestAttr(AST::Attribute::CONSTRUCTOR)) { auto tmp = TranslateASTTypes(nre->instTys); funcInstTypeArgs.insert(funcInstTypeArgs.end(), tmp.begin(), tmp.end()); } } Type* root = callerType->IsRef() ? StaticCast(callerType)->GetBaseType() : callerType; callerType = GetExactParentType(*root, *expr.resolvedFunction, *instFuncType, funcInstTypeArgs, false); if (callerType != nullptr && callerType->IsClass()) { callerType = builder.GetType(callerType); } } // Note that, the constructor doesn't have caller type if (expr.resolvedFunction != nullptr && IsStructMutFunction(*expr.resolvedFunction) && callerType != nullptr) { callerType = builder.GetType(callerType); } return callerType; } std::pair, Type*> Translator::GetMemberFuncParamAndRetInstTypes(const AST::CallExpr& expr) { FuncType* funcType = nullptr; if (auto genericTy = DynamicCast(expr.baseFunc->ty); genericTy) { CJC_ASSERT(genericTy->upperBounds.size() == 1 && "not support multi-upperBounds for funcType in CHIR"); funcType = StaticCast(TranslateType(**genericTy->upperBounds.begin())); } else { funcType = StaticCast(TranslateType(*expr.baseFunc->ty)); } return std::pair, Type*>{funcType->GetParamTypes(), funcType->GetReturnType()}; } Value* Translator::GenerateLoadIfNeccessary(Value& arg, bool isThis, bool isMut, bool isInOut, const DebugLocation& loc) { auto argTy = arg.GetType(); auto pureArgTy = argTy; while (pureArgTy->IsRef()) { pureArgTy = StaticCast(pureArgTy)->GetBaseType(); } if (((isMut && isThis) || isInOut) && (pureArgTy->IsValueType() || pureArgTy->IsGeneric())) { // We are handling the `this` param for a mut function, thus we need a single-ref type even // it is value type if (argTy->IsCPointer()) { // CPointer is a special since it is value type but represent a pointer thus no need // to load } else { CJC_ASSERT(argTy->IsRef()); CJC_ASSERT(!StaticCast(argTy)->GetBaseType()->IsRef()); if (pureArgTy->IsGeneric()) { // But if this is a generic type, we still need to generate load cause generic itself can handle // mut semantics auto baseTy = StaticCast(argTy)->GetBaseType(); CJC_ASSERT(!baseTy->IsRef()); return CreateAndAppendExpression(loc, baseTy, &arg, currentBlock)->GetResult(); } } } else { // Otherwise, value type will always pass by copy (i.e. with no ref in type) and reference type // will always pass by reference (i.e. with single-ref in type). Specially, the generic type and // func type are treated like value type if (pureArgTy->IsValueType() || pureArgTy->IsGeneric() || pureArgTy->IsFunc()) { if (argTy->IsRef()) { // Generate load if it is a single-ref value type (due to `var`) auto baseTy = StaticCast(argTy)->GetBaseType(); CJC_ASSERT(!baseTy->IsRef()); return CreateAndAppendExpression(loc, baseTy, &arg, currentBlock)->GetResult(); } } else if (pureArgTy->IsReferenceType()) { CJC_ASSERT(argTy->IsRef()); auto baseTy = StaticCast(argTy)->GetBaseType(); if (baseTy->IsRef()) { // Generate load if it is a double-ref reference type (due to `var`) return CreateAndAppendExpression(loc, baseTy, &arg, currentBlock)->GetResult(); } } } return &arg; } bool Translator::HasNothingTypeArg(std::vector& args) const { auto it = std::find_if(args.begin(), args.end(), [](auto arg) { return arg->GetType()->IsNothing(); }); if (it != args.end()) { return true; } return false; } // Conditions to check if this is a call to member func (constructor is not counted here) static bool IsMemberFuncCall(const AST::CallExpr& expr) { return expr.resolvedFunction && !expr.resolvedFunction->TestAttr(AST::Attribute::CONSTRUCTOR) && expr.resolvedFunction->outerDecl && expr.resolvedFunction->outerDecl->IsNominalDecl(); } Ptr Translator::ProcessCallExpr(const AST::CallExpr& expr) { if (auto res = TranslateIntrinsicCall(expr); res) { return res; } if (auto res = TranslateForeignFuncCall(expr); res) { return res; } if (auto res = TranslateCFuncConstructorCall(expr)) { return res; } if (auto res = TranslateCStringCtorCall(expr); res) { return res; } if (auto res = TranslateEnumCtorCall(expr); res) { return res; } if (IsCtorCall(expr)) { return TranslateStructOrClassCtorCall(expr); } if (bool isNothingCall = !expr.resolvedFunction && expr.baseFunc->ty->kind == AST::TypeKind::TYPE_NOTHING; isNothingCall) { return TranslateExprArg(*expr.baseFunc); } if (auto res = TranslateFuncTypeValueCall(expr); res) { return res; } if (IsMemberFuncCall(expr)) { return TranslateMemberFuncCall(expr); } if (auto res = TranslateTrivialFuncCall(expr); res) { return res; } InternalError("translating unsupported CallExpr"); return nullptr; } void Translator::ProcessMapExpr(AST::Node& originExpr, bool isSubScript) { if (auto base = GetMapExpr(originExpr); base) { if (exprValueTable.Has(originExpr)) { return; } /* In the following cangjie code, we do not need to translate `base`. open class A { operator func [](y : Int64) { x } operator func [](y : Int64, value! : Int64) { x = value }} class B <: A { func f() { super[3] *= 4 } } */ if (!(isSubScript && originExpr.astKind == AST::ASTKind::REF_EXPR && StaticCast(&originExpr)->isSuper)) { auto chirNode = TranslateExprArg(originExpr); exprValueTable.Set(originExpr, *chirNode); } } } Translator::LeftValueInfo Translator::TranslateCallExprAsLeftValue(const AST::CallExpr& expr) { if (auto res = TranslateStructOrClassCtorCallAsLeftValue(expr); res.base) { return res; } auto val = TranslateASTNode(expr, *this); return LeftValueInfo(val, {}); } void Translator::TranslateCompoundAssignmentElementRef(const AST::MemberAccess& ma) { auto loc = TranslateLocation(ma); auto baseResLeftValueInfo = TranslateExprAsLeftValue(*ma.baseExpr); auto baseResLeftValuePath = baseResLeftValueInfo.path; auto baseResLeftValue = baseResLeftValueInfo.base; if (baseResLeftValuePath.empty()) { exprValueTable.Set(*ma.baseExpr, *baseResLeftValue); } else { auto baseResLeftValueCustomType = StaticCast(baseResLeftValue->GetType()->StripAllRefs()); if (baseResLeftValue->GetType()->IsReferenceTypeWithRefDims(1) || baseResLeftValue->GetType()->IsValueOrGenericTypeWithRefDims(1)) { auto getMemberRef = CreateGetElementRefWithPath( loc, baseResLeftValue, baseResLeftValuePath, currentBlock, *baseResLeftValueCustomType); exprValueTable.Set(*ma.baseExpr, *getMemberRef); } else { auto memberType = GetInstMemberTypeByName(*baseResLeftValueCustomType, baseResLeftValuePath, builder); CJC_ASSERT(baseResLeftValue->GetType()->IsValueOrGenericTypeWithRefDims(0)); auto getMember = CreateAndAppendExpression( loc, memberType, baseResLeftValue, baseResLeftValuePath, currentBlock); exprValueTable.Set(*ma.baseExpr, *getMember->GetResult()); } } } Ptr Translator::Visit(const AST::CallExpr& callExpr) { /****** Handle side-effect `mapExpr` here ******/ if (!callExpr.TestAttr(AST::Attribute::SIDE_EFFECT)) { return ProcessCallExpr(callExpr); } CJC_ASSERT(callExpr.resolvedFunction); // Case 1: a call expr like: // S.xxxSet(S.xxxGet() + k) if (callExpr.resolvedFunction->isSetter) { if (callExpr.baseFunc->astKind == AST::ASTKind::MEMBER_ACCESS) { auto ma = StaticCast(callExpr.baseFunc.get()); if (ma->baseExpr->mapExpr != nullptr) { TranslateCompoundAssignmentElementRef(*ma); } } else if (callExpr.baseFunc->astKind == AST::ASTKind::REF_EXPR) { // Nothing need to do here } } else if (callExpr.resolvedFunction->TestAttr(AST::Attribute::OPERATOR)) { CJC_ASSERT(callExpr.baseFunc->astKind == AST::ASTKind::MEMBER_ACCESS); auto ma = StaticCast(callExpr.baseFunc.get()); CJC_NULLPTR_CHECK(ma->baseExpr->mapExpr); TranslateCompoundAssignmentElementRef(*ma); } else { CJC_ABORT(); } bool isSubScript = callExpr.resolvedFunction && callExpr.resolvedFunction->identifier == "[]"; if (isSubScript) { // If the case is array access(`[]`), then callexpr's baseFunc and args may have side effect. std::vector> args = callExpr.desugarArgs.value(); CJC_ASSERT(!args.empty()); for (size_t i = 0; i < args.size() - 1; i++) { ProcessMapExpr(*args[i]->expr, isSubScript); } } return ProcessCallExpr(callExpr); } void Translator::PrintDevirtualizationMessage(const AST::CallExpr& expr, const std::string& nodeType) { if (!opts.chirDebugOptimizer) { return; } Ptr resolvedFunction = expr.resolvedFunction; std::string message = "The function call to " + resolvedFunction->identifier + " in the line " + std::to_string(expr.begin.line) + " and the column " + std::to_string(expr.begin.column) + " was an " + nodeType + " call.\n"; std::cout << message; } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateClassDecl.cpp000066400000000000000000000405541510705540100270440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/UserDefinedType.h" #include "cangjie/Mangle/CHIRTypeManglingUtils.h" #include "cangjie/Modules/ModulesUtils.h" namespace Cangjie::CHIR { Ptr Translator::Visit(const AST::ClassDecl& decl) { ClassDef* classDef = StaticCast(GetNominalSymbolTable(decl).get()); CJC_NULLPTR_CHECK(classDef); TranslateClassLikeDecl(*classDef, decl); return nullptr; } void Translator::SetClassSuperClass(ClassDef& classDef, const AST::ClassLikeDecl& decl) { if (auto astTy = DynamicCast(decl.ty); astTy && astTy->GetSuperClassTy() != nullptr) { auto type = TranslateType(*astTy->GetSuperClassTy()); // The super class must be of the reference. CJC_ASSERT(type->IsRef()); if (!classDef.HasSuperClass()) { auto realType = StaticCast(StaticCast(type.get())->GetBaseType()); classDef.SetSuperClassTy(*realType); } } } void Translator::SetClassImplementedInterface(ClassDef& classDef, const AST::ClassLikeDecl& decl) { for (auto& superInterfaceTy : decl.GetStableSuperInterfaceTys()) { auto type = TranslateType(*superInterfaceTy); // The interface must be of the reference. CJC_ASSERT(type->IsRef()); auto realType = StaticCast(StaticCast(type)->GetBaseType()); classDef.AddImplementedInterfaceTy(*realType); } } void Translator::TranslateClassLikeDecl(ClassDef& classDef, const AST::ClassLikeDecl& decl) { // set annotation info CreateAnnotationInfo(decl, classDef, &classDef); // set type auto classTy = TranslateType(*decl.ty); auto baseTy = StaticCast(RawStaticCast(classTy)->GetBaseType()); classDef.SetType(*baseTy); bool isImportedInstantiated = decl.TestAttr(AST::Attribute::IMPORTED) && decl.TestAttr(AST::Attribute::GENERIC_INSTANTIATED); classDef.Set(isImportedInstantiated ? Linkage::INTERNAL : decl.linkage); // common and platform upper bounds are same, do not set again if (!(mergingPlatform && decl.TestAttr(AST::Attribute::PLATFORM) && !decl.TestAttr(AST::Attribute::IMPORTED))) { // set super class SetClassSuperClass(classDef, decl); // set implemented interface SetClassImplementedInterface(classDef, decl); } // translate member vars, funcs and props const auto& memberDecl = decl.GetMemberDeclPtrs(); for (auto& member : memberDecl) { if (!ShouldTranslateMember(decl, *member)) { continue; } if (member->astKind == AST::ASTKind::VAR_DECL) { AddMemberVarDecl(classDef, *RawStaticCast(member)); } else if (member->astKind == AST::ASTKind::FUNC_DECL) { auto funcDecl = RawStaticCast(member); TranslateClassLikeMemberFuncDecl(classDef, *funcDecl); } else if (member->astKind == AST::ASTKind::PROP_DECL) { AddMemberPropDecl(classDef, *RawStaticCast(member)); } else if (member->astKind == AST::ASTKind::PRIMARY_CTOR_DECL) { // do nothing, primary constructor decl has been desugared to func decl } else { CJC_ABORT(); } } // collect annotation info of the type and members for annotation target check CollectTypeAnnotation(decl, classDef); // translate vars init for CJMP. Translator trans = Copy(); classDef.SetVarInitializationFunc(trans.TranslateVarsInit(decl)); } void Translator::AddMemberVarDecl(CustomTypeDef& def, const AST::VarDecl& decl) { if (decl.TestAttr(AST::Attribute::PLATFORM)) { return; } if (decl.TestAttr(AST::Attribute::STATIC)) { auto staticVar = VirtualCast(GetSymbolTable(decl)); def.AddStaticMemberVar(staticVar); if (auto gv = DynamicCast(staticVar)) { CreateAnnotationInfo(decl, *gv, &def); } } else { Ptr ty = TranslateType(*decl.ty); auto loc = TranslateLocation(decl); MemberVarInfo varInfo{ .name = decl.identifier, .rawMangledName = decl.rawMangleName, .type = ty, .attributeInfo = BuildVarDeclAttr(decl), .loc = loc, .annoInfo = CreateAnnoFactoryFuncSig(decl, &def), // Will be translated later in vars init .initializerFunc = nullptr, .outerDef = &def }; // If get deserialized one, just need update attrs auto memberVars = def.GetDirectInstanceVars(); for (size_t i = 0; i < memberVars.size(); i++) { if (memberVars[i].TestAttr(Attribute::DESERIALIZED) && memberVars[i].name == decl.identifier) { memberVars[i] = varInfo; def.SetDirectInstanceVars(memberVars); return; } } def.AddInstanceVar(varInfo); } } Func* Translator::ClearOrCreateVarInitFunc(const AST::Decl& decl) { static const std::string POSTFIX = "$varInit"; const AST::Decl& outerDecl = decl.outerDecl == nullptr ? decl : *decl.outerDecl; if (outerDecl.TestAttr(AST::Attribute::IMPORTED) || !outerDecl.TestAnyAttr(AST::Attribute::COMMON, AST::Attribute::PLATFORM)) { return nullptr; } Func* func = nullptr; BlockGroup* body = nullptr; auto mangledName = decl.mangledName + POSTFIX; if (func = TryGetFromCache(GLOBAL_VALUE_PREFIX + mangledName, deserializedVals); func) { // found deserialized one body = builder.CreateBlockGroup(*func); auto params = func->GetParams(); CJC_ASSERT(params.size() == 1); func->ReplaceBody(*body); func->AddParam(*params[0]); } else { auto identifier = decl.identifier + POSTFIX; auto rawMangledName = decl.rawMangleName + POSTFIX; auto pkgName = outerDecl.fullPackageName; auto genericParamTy = GetGenericParamType(outerDecl, chirTy); const std::vector params = {}; auto returnTy = decl.ty; if (auto varDecl = DynamicCast(&decl)) { if (varDecl->initializer) { returnTy = varDecl->initializer->ty; } } CJC_ASSERT(returnTy); auto returnType = (&decl == &outerDecl) ? builder.GetUnitTy() : TranslateType(*returnTy); auto funcType = builder.GetType(params, returnType); funcType = AdjustVarInitType(*funcType, outerDecl, builder, chirTy); auto loc = DebugLocation(TranslateLocationWithoutScope(builder.GetChirContext(), decl.begin, decl.end)); auto customTypeDef = chirTy.GetGlobalNominalCache(outerDecl); func = builder.CreateFunc(loc, funcType, mangledName, identifier, rawMangledName, pkgName, genericParamTy); customTypeDef->AddMethod(func); func->SetFuncKind(FuncKind::INSTANCEVAR_INIT); func->EnableAttr(Attribute::PRIVATE); func->EnableAttr(Attribute::COMPILER_ADD); builder.CreateParameter(funcType->GetParamType(0), loc, *func); body = builder.CreateBlockGroup(*func); func->InitBody(*body); } blockGroupStack.emplace_back(body); auto entry = builder.CreateBlock(body); body->SetEntryBlock(entry); return func; } Func* Translator::TranslateVarInit(const AST::VarDecl& varDecl) { if (!varDecl.initializer) { return nullptr; } auto funcDef = ClearOrCreateVarInitFunc(varDecl); if (!funcDef) { return nullptr; } auto loc = DebugLocation(TranslateLocationWithoutScope(builder.GetChirContext(), varDecl.begin, varDecl.end)); auto entry = funcDef->GetEntryBlock(); auto thisVar = funcDef->GetParam(0); CJC_NULLPTR_CHECK(thisVar); SetSymbolTable(*varDecl.outerDecl, *thisVar); auto initBlock = CreateBlock(); currentBlock = initBlock; Ptr value = TranslateExprArg(*varDecl.initializer); auto lastBlock = currentBlock; auto retType = funcDef->GetReturnType(); auto retVal = CreateAndAppendExpression(loc, builder.GetType(retType), retType, lastBlock)->GetResult(); funcDef->SetReturnValue(*retVal); CreateAndAppendExpression(loc, builder.GetUnitTy(), value, retVal, lastBlock); CreateAndAppendTerminator(lastBlock); CreateAndAppendTerminator(initBlock, entry); blockGroupStack.pop_back(); return funcDef; } Func* Translator::TranslateVarsInit(const AST::Decl& decl) { auto funcDef = ClearOrCreateVarInitFunc(decl); if (!funcDef) { return nullptr; } auto loc = DebugLocation(TranslateLocationWithoutScope(builder.GetChirContext(), decl.begin, decl.end)); auto entry = funcDef->GetEntryBlock(); auto thisVar = funcDef->GetParam(0); CJC_NULLPTR_CHECK(thisVar); SetSymbolTable(decl, *thisVar); auto initBlock = CreateBlock(); currentBlock = initBlock; TranslateVariablesInit(decl, *thisVar); auto lastBlock = currentBlock; CreateAndAppendTerminator(lastBlock); CreateAndAppendTerminator(initBlock, entry); blockGroupStack.pop_back(); return funcDef; } bool Translator::ShouldTranslateMember(const AST::Decl& decl, const AST::Decl& member) const { if (mergingPlatform && !decl.TestAttr(AST::Attribute::IMPORTED) && decl.TestAttr(AST::Attribute::PLATFORM) && member.TestAttr(AST::Attribute::FROM_COMMON_PART)) { // Skip decls from common part when compiling platform return false; } return true; } void Translator::AddMemberMethodToCustomTypeDef(const AST::FuncDecl& decl, CustomTypeDef& def) { if (IsStaticInit(decl)) { return; } auto func = VirtualCast(GetSymbolTable(decl)); def.AddMethod(func); for (auto& param : decl.funcBody->paramLists[0]->params) { if (param->desugarDecl != nullptr) { def.AddMethod(VirtualCast(GetSymbolTable(*param->desugarDecl))); } } auto it = genericFuncMap.find(&decl); if (it != genericFuncMap.end()) { for (auto instFunc : it->second) { CJC_NULLPTR_CHECK(instFunc->outerDecl); CJC_ASSERT(instFunc->outerDecl == decl.outerDecl); def.AddMethod(VirtualCast(GetSymbolTable(*instFunc))); for (auto& param : instFunc->funcBody->paramLists[0]->params) { if (param->desugarDecl != nullptr) { def.AddMethod(VirtualCast(GetSymbolTable(*param->desugarDecl))); } } } } CreateAnnoFactoryFuncsForFuncDecl(decl, &def); } inline bool Translator::IsOpenPlatformReplaceAbstractCommon(ClassDef& classDef, const AST::FuncDecl& decl) const { bool isAbstractClass = classDef.IsClass() && classDef.IsAbstract(); bool isOpenInAbstractClass = decl.TestAttr(AST::Attribute::OPEN) && isAbstractClass; bool isStaticAbstractInInterface = classDef.IsInterface() && decl.TestAttr(AST::Attribute::STATIC); if (decl.TestAttr(AST::Attribute::PLATFORM) && (isOpenInAbstractClass || isStaticAbstractInInterface)) { const std::string expectedMangledName = decl.mangledName; for (auto method : classDef.GetMethods()) { if (method->GetIdentifierWithoutPrefix() == expectedMangledName) { // implementation already added return false; } } return true; } return false; } inline void Translator::RemoveAbstractMethod(ClassDef& classDef, const AST::FuncDecl& decl) const { const std::string expectedMangledName = decl.mangledName; const std::vector& abstractMethods = classDef.GetAbstractMethods(); std::vector updatedAbstractMethods; std::remove_copy_if(std::begin(abstractMethods), std::end(abstractMethods), std::back_inserter(updatedAbstractMethods), [expectedMangledName](auto& abstractMethod) { return abstractMethod.GetASTMangledName() == expectedMangledName; }); classDef.SetAbstractMethods(updatedAbstractMethods); } void Translator::TranslateClassLikeMemberFuncDecl(ClassDef& classDef, const AST::FuncDecl& decl) { // 1. if func is ABSTRACT, it should be put into `abstractMethods`, not `methods` // 2. virtual func need to put into vtable // ps: an abstract func may be not a virtual func, it depends on the logic of `IsVirtualFunction` // 3. a func, not ABSTRACT, should be found in global symbol table // It is not already translated if cjmp package is imported into current, // however in other cases adding platform declaration cause duplication. bool needToTranslate = false; if (IsOpenPlatformReplaceAbstractCommon(classDef, decl)) { RemoveAbstractMethod(classDef, decl); needToTranslate = true; } needToTranslate |= !(decl.TestAttr(AST::Attribute::PLATFORM) && mergingPlatform); if (!needToTranslate) { return; } if (decl.TestAttr(AST::Attribute::ABSTRACT)) { TranslateAbstractMethod(classDef, decl, false); } else if (needToTranslate) { AddMemberMethodToCustomTypeDef(decl, classDef); if (classDef.IsInterface()) { // Member of interface should be recorded in abstract method. TranslateAbstractMethod(classDef, decl, true); } } } void Translator::AddMemberPropDecl(CustomTypeDef& def, const AST::PropDecl& decl) { // prop defined within CLASS or INTERFACE can be abstract, so we should treat it as abstract func if (def.GetCustomKind() == CustomDefKind::TYPE_CLASS) { auto classDef = StaticCast(&def); for (auto& getter : decl.getters) { TranslateClassLikeMemberFuncDecl(*classDef, *getter); } for (auto& setter : decl.setters) { TranslateClassLikeMemberFuncDecl(*classDef, *setter); } } else { // prop defined within STRUCT or ENUM can't be abstract, so just put into method for (auto& getter : decl.getters) { auto func = VirtualCast(GetSymbolTable(*getter)); def.AddMethod(func); CreateAnnoFactoryFuncsForFuncDecl(StaticCast(*getter), &def); } for (auto& setter : decl.setters) { auto func = VirtualCast(GetSymbolTable(*setter)); def.AddMethod(func); CreateAnnoFactoryFuncsForFuncDecl(StaticCast(*setter), &def); } } } void Translator::TranslateAbstractMethod(ClassDef& classDef, const AST::FuncDecl& decl, bool hasBody) { std::vector params; auto& args = decl.funcBody->paramLists[0]->params; auto funcType = StaticCast(TranslateType(*decl.ty)); if (IsInstanceMember(decl)) { // Add info of this to the instance method, needed by reflection. auto paramTypes = funcType->GetParamTypes(); auto thisTy = builder.GetType(classDef.GetType()); paramTypes.insert(paramTypes.begin(), thisTy); funcType = builder.GetType(paramTypes, funcType->GetReturnType()); params.emplace_back(AbstractMethodParam{"this", thisTy}); } for (auto& arg : args) { params.emplace_back( AbstractMethodParam{arg->identifier, TranslateType(*arg->ty), CreateAnnoFactoryFuncSig(*arg, &classDef)}); } std::vector funcGenericTypeParams; if (decl.funcBody->generic != nullptr) { for (auto& genericDecl : decl.funcBody->generic->typeParameters) { funcGenericTypeParams.emplace_back(StaticCast(TranslateType(*genericDecl->ty))); } } const AST::Decl& annotationDecl = decl.propDecl ? *decl.propDecl : StaticCast(decl); auto attr = BuildAttr(decl.GetAttrs()); attr.SetAttr(Attribute::ABSTRACT, true); auto abstractMethod = AbstractMethodInfo{decl.identifier, decl.mangledName, funcType, params, attr, CreateAnnoFactoryFuncSig(annotationDecl, &classDef), funcGenericTypeParams, hasBody, &classDef}; classDef.AddAbstractMethod(abstractMethod); } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateDoWhileExpr.cpp000066400000000000000000000063131510705540100273740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" using namespace Cangjie::CHIR; using namespace Cangjie; Ptr Translator::Visit(const AST::DoWhileExpr& doWhileExpr) { // The CHIR of DoWhileExpr is like this: // goto trueBlock: // // trueBlock: if continue, goto conditionBlock if break, goto endBlock // do-while body -----------------------------|------------------------------| // ... | | // goto conditionBlock | | // | | // conditionBlock: <------| | // %condition = ... | // Branch(%condition, trueBlock, endBlock) | // | // endBlock: <-------------------------------------| // ... auto backupBlock = currentBlock; // create condition block Ptr conditionBlock = CreateBlock(); // create end Block Ptr endBlock = CreateBlock(); // Used for checking scope info with control flow. NOTE: location of block will not be used by CodeGen. // NOTE: record scope info before update the 'ScopeContext' auto loc = TranslateLocation(doWhileExpr); endBlock->SetDebugLocation(std::move(loc)); // Set symbol table, will be used by JumpExpr. auto blocks = std::make_pair(conditionBlock, endBlock); terminatorSymbolTable.Set(doWhileExpr, blocks); ScopeContext context(*this); context.ScopePlus(); auto const& whileLoc = TranslateLocation(*doWhileExpr.condExpr); // 1.create trueBlock TranslateSubExprToDiscarded(*doWhileExpr.body); Ptr trueBlock = GetBlockByAST(*doWhileExpr.body); // 2. goto conditionBlock. CreateAndAppendTerminator(whileLoc, conditionBlock, currentBlock) ->Set(SkipKind::SKIP_DCE_WARNING); currentBlock = conditionBlock; // 3. translate condition. Ptr condtion = TranslateExprArg(*doWhileExpr.condExpr); CJC_ASSERT( condtion->GetType()->IsBoolean() || condtion->GetType()->IsNothing() || condtion->GetType()->IsGeneric()); // When translate condition,may have a new block, we need to put Branch in this block. // example code: do {...} while (break) auto newConditionBlock = currentBlock; // go to trueBlock CreateAndAppendTerminator(trueBlock, backupBlock); // Do-while expr do not need to check unreachable branch. CreateAndAppendTerminator(whileLoc, condtion, trueBlock, endBlock, newConditionBlock) ->Set(SkipKind::SKIP_DCE_WARNING); currentBlock = endBlock; return nullptr; }cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateEnumDecl.cpp000066400000000000000000000077341510705540100267060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/Modules/ModulesUtils.h" using namespace Cangjie::CHIR; using namespace Cangjie; namespace { bool ShouldTranslateConstructor(const AST::EnumDecl& decl, const AST::Decl& ctor) { CJC_ASSERT(ctor.astKind == AST::ASTKind::VAR_DECL || ctor.astKind == AST::ASTKind::FUNC_DECL); if (ctor.TestAttr(AST::Attribute::COMMON) && decl.platformImplementation) { return false; } return true; } } Ptr Translator::Visit(const AST::EnumDecl& decl) { auto def = GetNominalSymbolTable(decl); CJC_ASSERT(def->GetCustomKind() == CustomDefKind::TYPE_ENUM); auto enumDef = StaticCast(def.get()); // step 1: set annotation info CreateAnnotationInfo(decl, *enumDef, enumDef); // step 2: set type auto chirType = StaticCast(TranslateType(*decl.ty)); enumDef->SetType(*chirType); enumDef->Set(decl.TestAttr(AST::Attribute::GENERIC_INSTANTIATED) ? Linkage::INTERNAL : decl.linkage); // step 3: set constructor // e.g. enum A { red | yellow | blue(Int32) } // `red`, `yellow` and `blue(Int32)` are called constructors // `red` and `yellow` are defined as `VarDecl`, `blue(Int32)` is defined as `FuncDecl` for (auto& ctor : decl.constructors) { if (!ShouldTranslateConstructor(decl, *ctor)) { continue; } switch (ctor->astKind) { case AST::ASTKind::VAR_DECL: { // default enum member store as {} -> EnumType enumDef->AddCtor( {ctor->identifier, ctor->mangledName, builder.GetType(std::vector{}, chirType)}); break; } case AST::ASTKind::FUNC_DECL: { std::vector paramTypes; CJC_ASSERT(!ctor->ty->typeArgs.empty()); for (size_t i = 0; i < ctor->ty->typeArgs.size() - 1; i++) { if (ctor->ty->typeArgs[i] == decl.ty) { paramTypes.emplace_back(chirType); } else { paramTypes.emplace_back(TranslateType(*ctor->ty->typeArgs[i])); } } enumDef-> AddCtor({ctor->identifier, ctor->mangledName, builder.GetType(paramTypes, chirType)}); break; } default: { CJC_ABORT(); break; } } } // step 4: set member func and prop for (auto& member : decl.members) { if (!ShouldTranslateMember(decl, *member)) { continue; } if (member->astKind == AST::ASTKind::FUNC_DECL) { auto funcDecl = StaticCast(member.get()); AddMemberMethodToCustomTypeDef(*funcDecl, *enumDef); } else if (member->astKind == AST::ASTKind::PROP_DECL) { AddMemberPropDecl(*enumDef, *RawStaticCast(member.get())); } else { CJC_ABORT(); } } // step 5: set implemented interface for (auto& superInterfaceTy : decl.GetStableSuperInterfaceTys()) { auto astType = TranslateType(*superInterfaceTy); // The implemented interface type must be of reference type. CJC_ASSERT(astType->IsRef()); auto realType = StaticCast(StaticCast(astType)->GetBaseType()); enumDef->AddImplementedInterfaceTy(*realType); } // step 6: collect annotation info of the type and members for annotation target check CollectTypeAnnotation(decl, *def); return nullptr; } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateExtendDecl.cpp000066400000000000000000000110451510705540100272170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/CHIR/Type/ExtendDef.h" #include "cangjie/Modules/ModulesUtils.h" using namespace Cangjie::CHIR; using namespace Cangjie; Ptr Translator::Visit(const AST::ExtendDecl& decl) { CJC_NULLPTR_CHECK(decl.extendedType); auto extendDef = StaticCast(GetNominalSymbolTable(decl)); // step 1: set annotation info CreateAnnotationInfo(decl, *extendDef, extendDef); // step 2: set extended type auto extendedTy = chirTy.TranslateType(*decl.extendedType->ty); if (extendedTy->IsRef()) { extendedTy = StaticCast(extendedTy)->GetBaseType(); } extendDef->SetExtendedType(*extendedTy); // step 3: set member func for (auto& member : decl.members) { if (member->IsCommonMatchedWithPlatform()) { /** * Source Definitions: * // common.cj * common extend A { * common func foo:Unit {} * } * * // platform.cj * platform extend A { * platform func foo:Unit { println("hello") } * } * * After Sema Merge: * platform extend A { * common func foo:Unit; // Declaration from common extend * platform func foo:Unit { // Implementation from platform extend * println("hello") * } * } * Note: * The common declaration of `foo` should be skiped because it is already covered by the * platform-specific implementation. This ensures that the platform implementation is used, avoiding * redundancy. */ continue; } if (member->astKind == AST::ASTKind::FUNC_DECL) { auto func = VirtualCast(GetSymbolTable(*member)); extendDef->AddMethod(func); auto funcDecl = StaticCast(member.get()); for (auto& param : funcDecl->funcBody->paramLists[0]->params) { if (param->desugarDecl != nullptr) { extendDef->AddMethod(VirtualCast(GetSymbolTable(*param->desugarDecl))); auto it = genericFuncMap.find(param->desugarDecl.get().get()); if (it != genericFuncMap.end()) { for (auto instFunc : it->second) { CJC_NULLPTR_CHECK(instFunc->outerDecl); CJC_ASSERT(instFunc->outerDecl == &decl); extendDef->AddMethod(VirtualCast(GetSymbolTable(*instFunc))); } } } } auto it = genericFuncMap.find(funcDecl); if (it != genericFuncMap.end()) { for (auto instFunc : it->second) { CJC_NULLPTR_CHECK(instFunc->outerDecl); CJC_ASSERT(instFunc->outerDecl == &decl); extendDef->AddMethod(VirtualCast(GetSymbolTable(*instFunc))); } } CreateAnnoFactoryFuncsForFuncDecl(StaticCast(*member), extendDef); } else if (member->astKind == AST::ASTKind::PROP_DECL) { AddMemberPropDecl(*extendDef, *RawStaticCast(member.get())); } else { CJC_ABORT(); } } // step 4: set implemented interface for (auto& superType : decl.GetStableSuperInterfaceTys()) { auto someTy = TranslateType(*superType); auto realType = StaticCast(someTy)->GetBaseType(); extendDef->AddImplementedInterfaceTy(*StaticCast(realType)); } // step 5: fill upper bounds if (decl.TestAttr(AST::Attribute::GENERIC)) { CJC_NULLPTR_CHECK(decl.generic); auto genericDecl = decl.generic.get(); for (auto& genericTy : genericDecl->typeParameters) { chirTy.FillGenericArgType(*StaticCast(genericTy->ty)); } } // step 6: collect annotation info of the type and members for annotation target check CollectTypeAnnotation(decl, *extendDef); return nullptr; } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateForInExpr.cpp000066400000000000000000001445331510705540100270650ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. // ============CFG for ForInExpr============ // func foo() { // for (i in 0..100) { // break // //return // //continue // } // println("dd") // } // ------------------------ // forDelayExitLevel = 0 // ForIn( // { // forDelayExitLevel = 1 //break // // forDelayExitLevel = 2 //return // // forDelayExitLevel = 0 //continue // }, // { // if (forDelayExitLevel) { // Exit() // } else { // i++ // } // }, // { // if (forDelayExitLevel) { // --forDelayExitLevel // cond = false // Exit() // } else { // i < stopVar // } // }) // if (forDelayExitLevel > 0) { // --forDelayExitLevel // Exit() // } else { // println("dd") // } // ========================ForInExpr embeded==================== // func foo() { // for (i in 0..100) { // for (j in 0..100) { // break // //return // //continue // } // println("cc") // } // println("dd") // } // ------------------------ // forDelayExitLevel1 = 0 // ForIn( // { // ForIn( // { // forDelayExitLevel1 = 1 //break // // forDelayExitLevel1 = 4 //return // // forDelayExitLevel1 = 0 //continue // }, // { // if (forDelayExitLevel1) { // Exit() // } else { // j++ // } // }, // { // if (forDelayExitLevel1) { // --forDelayExitLevel1 // cond = false // Exit() // } else { // j < stopVar // } // }) // if (forDelayExitLevel1 > 0) { // --forDelayExitLevel1 // Exit() // } else { // println("cc") // } // }, // { // if (forDelayExitLevel1) { // Exit() // } else { // i++ // } // }, // { // if (forDelayExitLevel1) { // --forDelayExitLevel1 // cond = false // Exit() // } else { // i < stopVar // } // }) // if (forDelayExitLevel1 > 0) { // --forDelayExitLevel1 // Exit() // } else { // println("dd") // } // ========================IFExpr embeded==================== // func foo() { // for (i in 0..100) { // if (i < 10) { // break // //return // //continue // } else { // println("bb") // } // println("cc") // } // println("dd") // } // ------------------------ // forDelayExitLevel1 = 0 // ForIn( // { // forDelayExitLevel1 = 0 // IF( // { // forDelayExitLevel1 = 2 //break // // forDelayExitLevel1 = 3 //return // // forDelayExitLevel1 = 1 //continue // }, // { // println("bb") // }) // if (forDelayExitLevel1 > 0) { // --forDelayExitLevel1 // Exit() // } else { // println("cc") // } // }, // { // if (forDelayExitLevel1) { // Exit() // } else { // i++ // } // }, // { // if (forDelayExitLevel1) { // --forDelayExitLevel1 // cond = false // Exit() // } else { // i < stopVar // } // }) // if (forDelayExitLevel1 > 0) { // --forDelayExitLevel1 // Exit() // } else { // println("dd") // } #include "cangjie/AST/Node.h" #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/GeneratedFromForIn.h" #include "cangjie/CHIR/Package.h" using namespace Cangjie::CHIR; using namespace Cangjie; using namespace AST; using namespace std; namespace Cangjie::CHIR { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND static Ptr TranslateForInClosedRangeOne(Translator& tr, const ForInExpr& forInExpr); #endif Ptr Translator::Visit(const AST::ForInExpr& forInExpr) { Ptr forIn = nullptr; if (forInExpr.forInKind == ForInKind::FORIN_ITER) { forIn = TranslateForInIter(forInExpr); } else if (forInExpr.forInKind == ForInKind::FORIN_STRING) { forIn = TranslateForInString(forInExpr); } else if (forInExpr.forInKind == ForInKind::FORIN_RANGE) { forIn = TranslateForInRange(forInExpr); } else { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (forInExpr.IsClosedRangeOne()) { return TranslateForInClosedRangeOne(*this, forInExpr); } #endif InternalError("translating unsupported ForInExpr"); } return forIn; } Ptr Translator::GetOuterMostExpr() { Ptr finalValidExpr = nullptr; for (auto reverseBegin = blockGroupStack.crbegin(), reverseEnd = blockGroupStack.crend(); reverseBegin != reverseEnd; ++reverseBegin) { Ptr bg = *reverseBegin; Expression* ownedExpr = bg->GetOwnerExpression(); if ((ownedExpr && ownedExpr->GetExprKind() == ExprKind::LAMBDA) || (currentBlock->GetTopLevelFunc() && bg == currentBlock->GetTopLevelFunc()->GetBody())) { return finalValidExpr != nullptr ? finalValidExpr : nullptr; } if (ownedExpr && (Is(ownedExpr) || ownedExpr->GetExprKind() == ExprKind::IF)) { finalValidExpr = ownedExpr->GetResult(); } } return finalValidExpr; } void Translator::InitializeDelayExitSignal(const DebugLocation& loc) { Ptr finalValidExpr = GetOuterMostExpr(); if (finalValidExpr == nullptr) { auto allocSignal = CreateAndAppendExpression(loc, builder.GetType(builder.GetInt64Ty()), builder.GetInt64Ty(), currentBlock); allocSignal->Set(true); delayExitSignal = allocSignal->GetResult(); auto constZero = CreateAndAppendConstantExpression(builder.GetInt64Ty(), *currentBlock, 0UL)->GetResult(); CreateWrappedStore(constZero, delayExitSignal, currentBlock); } } Ptr Translator::InitializeCondVar(const DebugLocation& loc) { auto condVar = CreateAndAppendExpression(loc, builder.GetType(builder.GetBoolTy()), builder.GetBoolTy(), currentBlock); condVar->Set(true); auto constantTrue = CreateAndAppendConstantExpression(builder.GetBoolTy(), *currentBlock, static_cast(1)); constantTrue->Set(true); CreateWrappedStore(loc, constantTrue->GetResult(), condVar->GetResult(), currentBlock); auto res = condVar->GetResult(); return res; } Ptr Translator::GenerateForInRetValLocation(const DebugLocation& loc) { // gc issue, gc team can not solve it, so here is a workAround. // CFG in chir is no problem, but there is a CFG path in llvm ir, // no store value operation to func return var in this path, but // still need to load return value from func return var. // the reason for setUp DelayExitReturnVal is listed in issue: // Cangjie-manifest/issues/2442 Ptr funcRetValLocation = GetOuterBlockGroupReturnValLocation(); if (funcRetValLocation != nullptr) { Ptr forInType = funcRetValLocation->GetType(); CJC_ASSERT(forInType->IsRef()); forInType = Cangjie::StaticCast(forInType)->GetBaseType(); // store expression has NothingType var and all belowed chir will be eliminated by NothingTypeElimination pass, // it will result to an empty body of function. if (!forInType->IsNothing() && !forInType->IsUnit()) { auto forInRetValLocation = CreateAndAppendExpression(loc, builder.GetType(forInType), forInType, currentBlock); forInRetValLocation->Set(true); Ptr forInBodyVal = CreateAndAppendConstantExpression(loc, forInType, *currentBlock)->GetResult(); CreateWrappedStore(loc, forInBodyVal, forInRetValLocation->GetResult(), currentBlock); return forInRetValLocation->GetResult(); } } return nullptr; } ForIn* Translator::InitForInExprSkeleton(const AST::ForInExpr& forInExpr, Ptr& inductiveVar, Ptr& condVar) { auto forInloc = TranslateLocation(forInExpr); auto forInType = TranslateType(*forInExpr.ty); Func* parentFunc = currentBlock->GetTopLevelFunc(); CJC_NULLPTR_CHECK(parentFunc); BlockGroup* bodyBlockGrp = builder.CreateBlockGroup(*parentFunc); BlockGroup* latchBlockGrp = builder.CreateBlockGroup(*parentFunc); BlockGroup* condBlockGrp = builder.CreateBlockGroup(*parentFunc); ForIn* forIn; if (forInExpr.forInKind == ForInKind::FORIN_ITER) { forIn = CreateAndAppendExpression( forInloc, forInType, inductiveVar, condVar, currentBlock); } else { forIn = CreateAndAppendExpression( forInloc, forInType, inductiveVar, condVar, currentBlock); } CJC_ASSERT(forIn); CJC_NULLPTR_CHECK(bodyBlockGrp); CJC_NULLPTR_CHECK(latchBlockGrp); CJC_NULLPTR_CHECK(condBlockGrp); forIn->InitBlockGroups(*bodyBlockGrp, *latchBlockGrp, *condBlockGrp); // 1. Initialize body blockGroup Ptr bodyEntry = builder.CreateBlock(bodyBlockGrp); bodyEntry->SetDebugLocation(TranslateLocation(*forInExpr.body)); bodyBlockGrp->SetEntryBlock(bodyEntry); // 2. Initialize latch blockGroup Ptr latchEntry = builder.CreateBlock(latchBlockGrp); latchEntry->SetDebugLocation(inductiveVar->GetDebugLocation()); latchBlockGrp->SetEntryBlock(latchEntry); // 3. Initialize cond blockGroup Ptr condEntry = builder.CreateBlock(condBlockGrp); condBlockGrp->SetEntryBlock(condEntry); condEntry->SetDebugLocation(TranslateLocation(*forInExpr.body)); return forIn; } void Translator::TranslateForInBodyBlockGroup(const AST::ForInExpr& forInExpr) { Ptr recordBlock = currentBlock; // Translate guard var Ptr guardVar = nullptr; if (forInExpr.patternGuard.get() != nullptr) { guardVar = TranslateExprArg(*forInExpr.patternGuard); recordBlock = currentBlock; } // Translate body of forIn expr TranslateSubExprToDiscarded(*forInExpr.body); auto bodyBlock = GetBlockByAST(*forInExpr.body); auto forInBodyLoc = TranslateLocation(*forInExpr.body); recordBlock->SetDebugLocation(forInBodyLoc); CreateAndAppendTerminator(currentBlock); if (guardVar != nullptr) { // Create continue block for translate guard. Ptr continueBlock = CreateBlock(); const auto& guardVarLoc = TranslateLocation(*forInExpr.patternGuard); CreateAndAppendTerminator(guardVarLoc, continueBlock); CreateWrappedBranch(SourceExpr::FOR_IN_EXPR, guardVarLoc, guardVar, bodyBlock, continueBlock, recordBlock); } else { CreateAndAppendTerminator(forInBodyLoc, bodyBlock, recordBlock); } } void Translator::TranslateForInRangeLatchBlockGroup(const AST::Node& node) { Ptr delayExitTrueBlock = CreateBlock(); Ptr delayExitFalseBlock = CreateBlock(); // if delayExitSignal value is greater than 0, it must be break or return happened. // Now we will go out for-in loop // Block #5: // preds: // %50: Int64 = Load(%16) // %51: Int64 = Constant(0) // %52: Bool = GT(%50, %51) // Branch(%52, #16, #17) auto loc = TranslateLocation(node); auto delayExitSignalVal = CreateAndAppendExpression(builder.GetInt64Ty(), delayExitSignal, currentBlock); delayExitSignalVal->Set(true); auto constZero = CreateAndAppendConstantExpression( builder.GetInt64Ty(), *currentBlock, 0UL)->GetResult(); auto isNeedExit = CreateAndAppendExpression( builder.GetBoolTy(), ExprKind::GT, delayExitSignalVal->GetResult(), constZero, currentBlock); isNeedExit->Set(SkipKind::SKIP_DCE_WARNING); isNeedExit->Set(true); CreateAndAppendTerminator(loc, isNeedExit->GetResult(), delayExitTrueBlock, delayExitFalseBlock, currentBlock)->SetSourceExpr(SourceExpr::FOR_IN_EXPR); // before go out for-in loop, we only exit() from latch blockGroup, // as we still need to enter the next CFG: cond blockGroup. // Block #16: // preds: #5 // Exit() currentBlock = delayExitTrueBlock; CreateAndAppendTerminator(loc, currentBlock); // if delayExitSignal value is equal to 0, we translate latch as normal. // Block #17: // preds: #5 // try { // i += 1 // } catch(_: Exception) { // break; // } currentBlock = delayExitFalseBlock; TranslateSubExprToDiscarded(node); CreateAndAppendTerminator(loc, currentBlock); } void Translator::TranslateForInStringLatchBlockGroup(Ptr& inductiveVar) { Ptr delayExitTrueBlock = CreateBlock(); Ptr delayExitFalseBlock = CreateBlock(); auto loc = inductiveVar->GetDebugLocation(); auto delayExitSignalVal = CreateAndAppendExpression(builder.GetInt64Ty(), delayExitSignal, currentBlock); delayExitSignalVal->Set(true); auto constZero = CreateAndAppendConstantExpression( builder.GetInt64Ty(), *currentBlock, 0UL)->GetResult(); auto isNeedExit = CreateAndAppendExpression( builder.GetBoolTy(), ExprKind::GT, delayExitSignalVal->GetResult(), constZero, currentBlock); isNeedExit->Set(SkipKind::SKIP_DCE_WARNING); isNeedExit->Set(true); CreateAndAppendTerminator(loc, isNeedExit->GetResult(), delayExitTrueBlock, delayExitFalseBlock, currentBlock)->SetSourceExpr(SourceExpr::FOR_IN_EXPR); currentBlock = delayExitTrueBlock; CreateAndAppendTerminator(loc, currentBlock); currentBlock = delayExitFalseBlock; Ptr inductiveVal = GetDerefedValue(inductiveVar); auto constOne = CreateAndAppendConstantExpression(inductiveVal->GetType(), *currentBlock, 1UL); constOne->Set(true); auto plusOne = CreateAndAppendExpression(loc, inductiveVal->GetType(), ExprKind::ADD, inductiveVal, constOne->GetResult(), OverflowStrategy::WRAPPING, currentBlock); plusOne->Set(true); CreateWrappedStore(loc, plusOne->GetResult(), inductiveVar, currentBlock); CreateAndAppendTerminator(loc, currentBlock); } void Translator::TranslateForInCondControlFlow(Ptr& condVar) { Ptr delayExitTrueBlock = CreateBlock(); Ptr delayExitFalseBlock = CreateBlock(); // if delayExitSignal value is greater than 0, it must be break or return happened. // Now we will go out for-in loop // Block #6: // preds: // %19: Int64 = Load(%16) // %20: Int64 = Constant(0) // %21: Bool = GT(%19, %20) // Branch(%21, #7, #8) auto& loc = condVar->GetDebugLocation(); auto delayExitSignalVal = CreateAndAppendExpression(builder.GetInt64Ty(), delayExitSignal, currentBlock); delayExitSignalVal->Set(true); auto constZero = CreateAndAppendConstantExpression(builder.GetInt64Ty(), *currentBlock, 0UL); constZero->Set(true); auto isNeedExit = CreateAndAppendExpression( builder.GetBoolTy(), ExprKind::GT, delayExitSignalVal->GetResult(), constZero->GetResult(), currentBlock); isNeedExit->Set(SkipKind::SKIP_DCE_WARNING); isNeedExit->Set(true); CreateAndAppendTerminator(loc, isNeedExit->GetResult(), delayExitTrueBlock, delayExitFalseBlock, currentBlock)->SetSourceExpr(SourceExpr::FOR_IN_EXPR); // before go out for-in loop, let --delayExitSignalVal, and clear cond var to false // Block #7: // preds: #6 // %22: Int64 = Load(%16) // %23: Int64 = Constant(1) // %24: Int64 = Sub(%22, %23) // %25: Unit = Store(%24, %16) // %26: Bool = Constant(false) // %27: Unit = Store(%26, %14) // Exit() currentBlock = delayExitTrueBlock; delayExitSignalVal = CreateAndAppendExpression(builder.GetInt64Ty(), delayExitSignal, currentBlock); delayExitSignalVal->Set(true); auto constOne = CreateAndAppendConstantExpression(builder.GetInt64Ty(), *currentBlock, 1UL); constOne->Set(true); auto decreaseOne = CreateAndAppendExpression(builder.GetInt64Ty(), ExprKind::SUB, delayExitSignalVal->GetResult(), constOne->GetResult(), OverflowStrategy::WRAPPING, currentBlock); decreaseOne->Set(true); CreateWrappedStore(decreaseOne->GetResult(), delayExitSignal, currentBlock); auto constantFalse = CreateAndAppendConstantExpression(builder.GetBoolTy(), *currentBlock, false); constantFalse->Set(true); CreateWrappedStore(constantFalse->GetResult(), condVar, currentBlock); CreateAndAppendTerminator(loc, currentBlock); // if delayExitSignal value is equal to 0, we translate condition as normal, // and update the conditon value to cond var. currentBlock = delayExitFalseBlock; } void Translator::UpdateDelayExitSignalInForInEnd(const ForIn& forIn) { Ptr delayExitTrueBlock = CreateBlock(); Ptr delayExitFalseBlock = CreateBlock(); const auto& loc = forIn.GetDebugLocation(); // if delayExitSignal value is greater than 0, it must be return happened. // Now we will exit() the func // Block #endBlock: // preds: #2 // %69: Int64 = Constant(0) // %70: Int64 = Load(%16) // %71: Bool = GT(%70, %69) // Branch(%71, #26, #27) auto constZero = CreateAndAppendConstantExpression(builder.GetInt64Ty(), *currentBlock, 0UL); constZero->Set(true); auto delayExitSignalVal = CreateAndAppendExpression(builder.GetInt64Ty(), delayExitSignal, currentBlock); delayExitSignalVal->Set(true); auto isNeedExit = CreateAndAppendExpression( builder.GetBoolTy(), ExprKind::GT, delayExitSignalVal->GetResult(), constZero->GetResult(), currentBlock); isNeedExit->Set(SkipKind::SKIP_DCE_WARNING); isNeedExit->Set(true); CreateAndAppendTerminator(loc, isNeedExit->GetResult(), delayExitTrueBlock, delayExitFalseBlock, currentBlock)->SetSourceExpr(SourceExpr::FOR_IN_EXPR); // before we exit() the func, let --delayExitSignalVal, and if the current forIn expr has forInRetValLocation, // we shold load forInRetVal from it, and restore forInRetVal to outer expr or func RetValLocation. // Block #26: // preds: #3 // %72: Int64 = Load(%16) // %73: Int64 = Constant(1) // %74: Int64 = Sub(%72, %73) // %75: Unit = Store(%74, %16) // %76: Int64 = Load(%7) // %77: Unit = Store(%76, %2) // Exit() currentBlock = delayExitTrueBlock; delayExitSignalVal = CreateAndAppendExpression(builder.GetInt64Ty(), delayExitSignal, currentBlock); delayExitSignalVal->Set(true); auto constOne = CreateAndAppendConstantExpression(builder.GetInt64Ty(), *currentBlock, 1UL); constOne->Set(true); auto decreaseOne = CreateAndAppendExpression(builder.GetInt64Ty(), ExprKind::SUB, delayExitSignalVal->GetResult(), constOne->GetResult(), OverflowStrategy::WRAPPING, currentBlock); CreateWrappedStore(decreaseOne->GetResult(), delayExitSignal, currentBlock); Ptr funcRetValLocation = GetOuterBlockGroupReturnValLocation(); if (funcRetValLocation != nullptr && forInExprReturnMap.count(forIn.GetResult()) != 0) { Ptr forInRetValLocation = forInExprReturnMap[forIn.GetResult()]; Ptr forInRetVal = CreateAndAppendExpression( loc, Cangjie::StaticCast(forInRetValLocation->GetType())->GetBaseType(), forInRetValLocation, currentBlock)->GetResult(); CreateWrappedStore(loc, forInRetVal, funcRetValLocation, currentBlock); } if (!tryCatchContext.empty()) { GenerateSignalCheckForThrow(); } auto term = CreateAndAppendTerminator(currentBlock); term->Set(SkipKind::SKIP_FORIN_EXIT); // if delayExitSignal value is equal to 0, we still need to translate the rest cangjie code as normal. currentBlock = delayExitFalseBlock; } void Translator::GenerateSignalCheckForThrow() { auto constZero = CreateAndAppendConstantExpression(builder.GetInt64Ty(), *currentBlock, 0UL); constZero->Set(true); auto delayExitSignalVal = CreateAndAppendExpression(builder.GetInt64Ty(), delayExitSignal, currentBlock); delayExitSignalVal->Set(true); auto hasThrow = CreateAndAppendExpression( builder.GetBoolTy(), ExprKind::GT, delayExitSignalVal->GetResult(), constZero->GetResult(), currentBlock); auto throwBB = CreateBlock(); // if throw check is true, rethrow it to outer Exception block auto exception = CreateAndAppendExpression(builder.GetType(builder.GetObjectTy()), throwBB); auto baseETy = builder.GetType(builder.GetObjectTy()); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND IntrisicCallContext callContext{IntrinsicKind::BEGIN_CATCH, {exception->GetResult()}}; auto eVal = CreateAndAppendExpression(baseETy, callContext, throwBB); #else auto eVal = CreateAndAppendExpression( baseETy, IntrinsicKind::BEGIN_CATCH, std::vector{exception->GetResult()}, throwBB); #endif CreateAndAppendTerminator(eVal->GetResult(), tryCatchContext.top(), throwBB); auto returnBB = CreateBlock(); // if throw check is false, return from cur func auto br = CreateAndAppendTerminator(hasThrow->GetResult(), throwBB, returnBB, currentBlock); br->Set(true); currentBlock = returnBB; // contents of returnBB is filled on the caller side } Ptr Translator::TranslateForInIterCondition(Ptr& iterNextLocation, Ptr& astTy) { // Block #8: // preds: #6 // %51: Enum-_CNat6OptionIRNat6StringEE = Load(%34) // %52: Bool = Field(%51, 0) // %53: Bool = Not(%52) // %54: Unit = Store(%53, %37) // Exit() // Field(%xx, 0) auto enumIdx = GetEnumIDValue(astTy, GetDerefedValue(iterNextLocation, iterNextLocation->GetDebugLocation())); // bool == 0 euqals !bool return CreateAndAppendExpression(iterNextLocation->GetDebugLocation(), builder.GetBoolTy(), ExprKind::NOT, enumIdx, Cangjie::OverflowStrategy::NA, currentBlock)->GetResult(); } void Translator::TranslateForInIterPattern(const AST::ForInExpr& forInExpr, Ptr& iterNextLocation) { // Translate forInExpr.pattern of for-in node // { // Block Group: 1 // Block #4: // preds: // %56: Enum-_CNat6OptionIRNat6StringEE = Load(%34) // %57: Tuple = TypeCast(%56, Tuple) // %58: Struct-_CNat6StringE = Field(%57, 1) // %59: Struct-_CNat6StringE& = Allocate(Struct-_CNat6StringE) // %60: Unit = Debug(%59, i) // %61: Unit = Store(%58, %59) // %62: Struct-_CNat6StringE = Load(%59) // ...... // } auto loadIterNext = GetDerefedValue(iterNextLocation, iterNextLocation->GetDebugLocation()); CJC_ASSERT(loadIterNext->GetType()->IsEnum()); CJC_ASSERT(forInExpr.pattern->astKind == AST::ASTKind::ENUM_PATTERN); auto enumPattern = StaticCast(forInExpr.pattern.get()); HandleVarWithTupleAndEnumPattern(*enumPattern, enumPattern->patterns, loadIterNext, true); } void Translator::TranslateForInIterLatchBlockGroup( const MatchExpr& matchExpr, Ptr& iterNextLocation) { Ptr delayExitTrueBlock = CreateBlock(); Ptr delayExitFalseBlock = CreateBlock(); // if delayExitSignal value is greater than 0, it must be break or return happened. // Now we will go out for-in loop // Block #5: // preds: // %78: Int64 = Load(%39) // %79: Int64 = Constant(0) // %80: Bool = GT(%78, %79) // Branch(%80, #14, #15) auto& loc = iterNextLocation->GetDebugLocation(); auto delayExitSignalVal = CreateAndAppendExpression(builder.GetInt64Ty(), delayExitSignal, currentBlock); delayExitSignalVal->Set(true); auto constZero = CreateAndAppendConstantExpression(builder.GetInt64Ty(), *currentBlock, 0UL); auto isNeedExit = CreateAndAppendExpression( builder.GetBoolTy(), ExprKind::GT, delayExitSignalVal->GetResult(), constZero->GetResult(), currentBlock); isNeedExit->Set(SkipKind::SKIP_DCE_WARNING); isNeedExit->Set(true); CreateAndAppendTerminator(loc, isNeedExit->GetResult(), delayExitTrueBlock, delayExitFalseBlock, currentBlock)->SetSourceExpr(SourceExpr::FOR_IN_EXPR); // before go out for-in loop, we only exit() from latch blockGroup, // as we still need to enter the next CFG: cond blockGroup. // Block #14: // preds: #5 // Exit() currentBlock = delayExitTrueBlock; CreateAndAppendTerminator(loc, currentBlock); // if delayExitSignal value is equal to 0, we translate latch as normal. // Block #15: // preds: #5 // %81: Enum-_CNat6OptionIRNat6StringEE = // Invoke(next: (Class-_CNat8IteratorIRNat6StringEE&), %33) // %82: Unit = Store(%81, %34) // Exit() currentBlock = delayExitFalseBlock; Ptr iterNext = TranslateExprArg(*matchExpr.selector); CreateWrappedStore(loc, iterNext, iterNextLocation, currentBlock); CreateAndAppendTerminator(loc, currentBlock); } Ptr Translator::TranslateForInRange(const AST::ForInExpr& forInExpr) { AST::Block* inExpression = StaticCast(forInExpr.inExpression.get()); auto forInExprLoc = TranslateLocation(forInExpr); Ptr forInRetValLocation = GenerateForInRetValLocation(forInExprLoc); // this map is used for check if jump expr is inside try catch scope. forInExprAST2DebugLocMap[&forInExpr] = &forInExprLoc; // there are four nodes in forInExpr.inExpression: // nodes[0] is inductive var, // nodes[1] is stopExpr var, // nodes[2] is step update // nodes[3] is condition var, // 1. Translate inductive var of for-in node Ptr inductiveVar = TranslateExprArg(*inExpression->body[0]); StaticCast(inductiveVar)->GetExpr()->Set(true); // 2. Translate stopExpr var Ptr stopExprVar = TranslateExprArg(*inExpression->body[1]); if (auto stopExpr = DynamicCast(stopExprVar)) { stopExpr->GetExpr()->Set(true); } // 3. Translate condition auto condLoc = TranslateLocation(*inExpression->body[3]); Ptr condition = TranslateExprArg(*inExpression->body[3u]); StaticCast(condition)->GetExpr()->Set(true); auto recordBlock = currentBlock; // Create forInBody block. Ptr forInBodyBlock = CreateBlock(); forInBodyBlock->SetDebugLocation(TranslateLocation(forInExpr.body->begin, forInExpr.body->end)); currentBlock = forInBodyBlock; // 4. initialize cond var Ptr condVar = InitializeCondVar(condLoc); // 5. initialize delayExitSig InitializeDelayExitSignal(condLoc); // 6. Generate for-in expression. // ForIn(%10, %13, {bodyBlockGroup}, {latchBlockGroup}, {condBlockGroup}) ForIn* forIn = InitForInExprSkeleton(forInExpr, inductiveVar, condVar); if (forInRetValLocation != nullptr) { forInExprReturnMap.insert(std::make_pair(forIn->GetResult(), forInRetValLocation)); } // 7. Translate body blockGroup blockGroupStack.emplace_back(forIn->GetBody()); currentBlock = forIn->GetBody()->GetEntryBlock(); ScopeContext contextInExpression(*this); contextInExpression.ScopePlus(); // Translate forInExpr.pattern if (auto vp = DynamicCast(forInExpr.pattern.get())) { TranslateExprArg(*vp->varDecl); } else { CJC_ASSERT(forInExpr.pattern->astKind == AST::ASTKind::WILDCARD_PATTERN); } ScopeContext contextBody(*this); contextBody.ScopePlus(); TranslateForInBodyBlockGroup(forInExpr); blockGroupStack.pop_back(); // 8. Translate latch blockGroup blockGroupStack.emplace_back(forIn->GetLatch()); currentBlock = forIn->GetLatch()->GetEntryBlock(); TranslateForInRangeLatchBlockGroup(*inExpression->body[2u]); blockGroupStack.pop_back(); // 9. Translate cond blockGroup blockGroupStack.emplace_back(forIn->GetCond()); currentBlock = forIn->GetCond()->GetEntryBlock(); ScopeContext context(*this); context.ScopePlus(); LocalVar* localConditionVal = StaticCast(condition); auto conditionExprKind = localConditionVal->GetExpr()->GetExprKind(); TranslateForInCondControlFlow(condVar); auto conditionInCondBG = CreateAndAppendExpression(condLoc, builder.GetBoolTy(), conditionExprKind, GetDerefedValue(inductiveVar, condLoc), GetDerefedValue(stopExprVar), currentBlock)->GetResult(); CreateWrappedStore(condLoc, conditionInCondBG, condVar, currentBlock); CreateAndAppendTerminator(condLoc, currentBlock); blockGroupStack.pop_back(); // Create end branch Ptr endBlock = CreateBlock(); // GoTo #endBlock CreateAndAppendTerminator(condLoc, endBlock, forInBodyBlock); // 10. Update DelayExitSignal currentBlock = endBlock; UpdateDelayExitSignalInForInEnd(*forIn); auto br = CreateAndAppendTerminator(forInExprLoc, condition, forInBodyBlock, currentBlock, recordBlock); br->SetSourceExpr(SourceExpr::FOR_IN_EXPR); // when the range is made of constants (i.e. value can be computed by CA), this branch generates an unreachable // warning on the false branch. currentBlock->Set(SkipKind::SKIP_DCE_WARNING); return nullptr; } Ptr Translator::TranslateForInString(const AST::ForInExpr& forInExpr) { auto forInExprBlock = currentBlock; auto forInExprLoc = TranslateLocation(forInExpr); Ptr forInRetValLocation = GenerateForInRetValLocation(forInExprLoc); // this map is used for check if jump expr is inside try catch scope. forInExprAST2DebugLocMap[&forInExpr] = &forInExprLoc; // 1. Translate inExpression of for-in node TranslateExprArg(*forInExpr.inExpression); AST::Block* inExpression = StaticCast(forInExpr.inExpression.get()); auto inExpressionBlock = GetBlockByAST(*inExpression); CreateAndAppendTerminator(forInExprLoc, inExpressionBlock, forInExprBlock); // 2. Translate inductive var of inExpression // there are three nodes in forInExpr.inExpression, nodes[0] is inductive var Ptr inductiveVar = GetSymbolTable(*inExpression->body[0]); StaticCast(inductiveVar)->GetExpr()->Set(true); // 3. Translate loop condition var of for-in node // there are three nodes in forInExpr.inExpression, nodes[2] is sizeget func auto condLoc = TranslateLocation(*inExpression->body[2U]); auto stringSize = GetDerefedValue(GetSymbolTable(*inExpression->body[2U]), condLoc); auto condition = CreateAndAppendExpression(condLoc, builder.GetBoolTy(), ExprKind::LT, GetDerefedValue(inductiveVar, condLoc), stringSize, currentBlock)->GetResult(); condition->Set(SkipKind::SKIP_DCE_WARNING); condition->GetExpr()->Set(true); auto recordBlock = currentBlock; // Create forInBody block. Ptr forInBodyBlock = CreateBlock(); forInBodyBlock->SetDebugLocation(TranslateLocation(*forInExpr.body)); currentBlock = forInBodyBlock; // 4. initialize cond var Ptr condVar = InitializeCondVar(condLoc); // 5. initialize delayExitSig InitializeDelayExitSignal(condLoc); // 6. Generate for-in expression. // ForIn(%33, %37, {bodyBlockGroup}, {latchBlockGroup}, {condBlockGroup}) ForIn* forIn = InitForInExprSkeleton(forInExpr, inductiveVar, condVar); if (forInRetValLocation != nullptr) { forInExprReturnMap.insert(std::make_pair(forIn->GetResult(), forInRetValLocation)); } // 7. Translate body blockGroup blockGroupStack.emplace_back(forIn->GetBody()); currentBlock = forIn->GetBody()->GetEntryBlock(); ScopeContext contextInExpression(*this); contextInExpression.ScopePlus(); // Translate forInExpr.pattern of for-in node if (auto vp = DynamicCast(forInExpr.pattern.get()); vp) { TranslateSubExprToDiscarded(*vp->varDecl); } ScopeContext contextBody(*this); contextBody.ScopePlus(); TranslateForInBodyBlockGroup(forInExpr); blockGroupStack.pop_back(); // 8. Translate latch blockGroup blockGroupStack.emplace_back(forIn->GetLatch()); currentBlock = forIn->GetLatch()->GetEntryBlock(); TranslateForInStringLatchBlockGroup(inductiveVar); blockGroupStack.pop_back(); // 9. Translate cond blockGroup blockGroupStack.emplace_back(forIn->GetCond()); currentBlock = forIn->GetCond()->GetEntryBlock(); ScopeContext context(*this); context.ScopePlus(); TranslateForInCondControlFlow(condVar); auto conditionInCondBG = CreateAndAppendExpression( condLoc, builder.GetBoolTy(), ExprKind::LT, GetDerefedValue(inductiveVar, condLoc), stringSize, currentBlock) ->GetResult(); CreateWrappedStore(condLoc, conditionInCondBG, condVar, currentBlock); CreateAndAppendTerminator(condLoc, currentBlock); blockGroupStack.pop_back(); // Create end branch Ptr endBlock = CreateBlock(); // GoTo #endBlock CreateAndAppendTerminator(condLoc, endBlock, forInBodyBlock); // 10. Update DelayExitLevel currentBlock = endBlock; UpdateDelayExitSignalInForInEnd(*forIn); auto br = CreateAndAppendTerminator(forInExprLoc, condition, forInBodyBlock, currentBlock, recordBlock); br->Set(SkipKind::SKIP_DCE_WARNING); br->SetSourceExpr(SourceExpr::FOR_IN_EXPR); return nullptr; } Ptr Translator::MakeNone(Type& optionType, const DebugLocation& loc) { auto one = CreateAndAppendConstantExpression(builder.GetBoolTy(), *currentBlock, true); one->Set(true); auto enumTuple = CreateAndAppendExpression( loc, &optionType, std::vector{one->GetResult()}, currentBlock); enumTuple->Set(true); return enumTuple->GetResult(); } Ptr Translator::TranslateForInIter(const AST::ForInExpr& forInExpr) { AST::Block* inExpression = StaticCast(forInExpr.inExpression.get()); auto forInExprLoc = TranslateLocation(forInExpr); Ptr forInRetValLocation = GenerateForInRetValLocation(forInExprLoc); // this map is used for check if jump expr is inside try catch scope. forInExprAST2DebugLocMap[&forInExpr] = &forInExprLoc; // 1. Translate inductive var // there are two nodes in forInExpr.inExpression, nodes[0] is inductive var Ptr inductiveVar = TranslateSubExprAsValue(*inExpression->body[0]); // 2. Translate iterNext var // nodes[1] is match expr, it triggers iterator next operation AST::MatchExpr* matchExpr = StaticCast(inExpression->body[1].get()); auto matchTy = TranslateType(*matchExpr->ty); auto condLoc = TranslateLocation(*inExpression->body[1]); Ptr iterNextLocation = CreateAndAppendExpression(condLoc, builder.GetType(matchTy), matchTy, currentBlock)->GetResult(); Ptr iterInit = MakeNone(*matchTy, condLoc); CreateWrappedStore(condLoc, iterInit, iterNextLocation, currentBlock); SetSkipPrintWarning(iterInit); // 3. Translate condition var auto recordBlock = currentBlock; // Create forInBody block. Ptr forInBodyBlock = CreateBlock(); forInBodyBlock->SetDebugLocation(TranslateLocation(*forInExpr.body)); currentBlock = forInBodyBlock; // 4. initialize cond var Ptr condVar = InitializeCondVar(condLoc); // 5. initialize delayExitSig InitializeDelayExitSignal(condLoc); // 6. Generate for-in expression. // ForIn(%33, %37, {bodyBlockGroup}, {latchBlockGroup}, {condBlockGroup}) ForIn* forIn = InitForInExprSkeleton(forInExpr, inductiveVar, condVar); if (forInRetValLocation != nullptr) { forInExprReturnMap.insert(std::make_pair(forIn->GetResult(), forInRetValLocation)); } // 7. Translate body blockGroup blockGroupStack.emplace_back(forIn->GetBody()); currentBlock = forIn->GetBody()->GetEntryBlock(); ScopeContext contextIterPattern(*this); contextIterPattern.ScopePlus(); contextIterPattern.ScopePlus(); TranslateForInIterPattern(forInExpr, iterNextLocation); ScopeContext contextBody(*this); contextBody.ScopePlus(); TranslateForInBodyBlockGroup(forInExpr); blockGroupStack.pop_back(); // 8. Translate latch blockGroup blockGroupStack.emplace_back(forIn->GetLatch()); currentBlock = forIn->GetLatch()->GetEntryBlock(); TranslateForInIterLatchBlockGroup(*matchExpr, iterNextLocation); blockGroupStack.pop_back(); // 9. Translate cond blockGroup blockGroupStack.emplace_back(forIn->GetCond()); currentBlock = forIn->GetCond()->GetEntryBlock(); ScopeContext context(*this); context.ScopePlus(); TranslateForInCondControlFlow(condVar); auto conditionInCondBG = TranslateForInIterCondition(iterNextLocation, matchExpr->ty); CreateWrappedStore(condLoc, conditionInCondBG, condVar, currentBlock); CreateAndAppendTerminator(condLoc, currentBlock); blockGroupStack.pop_back(); // Create end branch Ptr endBlock = CreateBlock(); // GoTo #endBlock CreateAndAppendTerminator(condLoc, endBlock, forInBodyBlock); // 10. Update DelayExitLevel currentBlock = endBlock; UpdateDelayExitSignalInForInEnd(*forIn); CreateAndAppendTerminator(forInExprLoc, forInBodyBlock, recordBlock); return nullptr; } /* for (i in a..=b:1 where guard) { body } translates into: if a <= b { var iter = a var cond = true ForInClosedRange(iter, cond) { #body: let i = Load(iter) if guard { body } GoTo(#cond) #cond: // cond is true here cond = i != b // != instead of <= is where the optimisation goes GoTo(#latch) #latch: iter = i + 1 GoTo(#body) } } () */ class TranslateForInExpr { public: explicit TranslateForInExpr(Translator& translator) : tr{translator} {} Value* Translate(const ForInExpr& forinExpr) { forin = &forinExpr; range = forinExpr.inExpression.get(); auto forinLoc = tr.TranslateLocation(forinExpr); tr.forInExprAST2DebugLocMap[forin] = &forinLoc; auto iterBegin = TranslateBegin(); auto iterEnd = TranslateEnd(); auto fb = TranslatePreCondition(*iterBegin, *iterEnd); iterVar = TranslateIterVar(*iterBegin); auto curBlock = tr.GetCurrentBlock(); auto bodyBlock = CreateForInEntryBlock(); tr.CreateAndAppendTerminator(bodyBlock, curBlock); auto condLoc = tr.TranslateLocation(*forinExpr.inExpression); condVar = tr.InitializeCondVar(condLoc); tr.InitializeDelayExitSignal(condLoc); auto retVal = tr.GenerateForInRetValLocation(forinLoc); auto [body, cond, latch] = CreateRetAndBGs(); if (retVal) { tr.forInExprReturnMap.emplace(res->GetResult(), retVal); } TranslateBodyBG(*body); TranslateCondBG(*cond, *iterEnd); TranslateLatchBG(*latch); auto endBlock = tr.CreateBlock(); tr.CreateAndAppendTerminator(endBlock, bodyBlock); tr.SetCurrentBlock(*endBlock); tr.UpdateDelayExitSignalInForInEnd(*res); tr.CreateAndAppendTerminator(fb, tr.GetCurrentBlock()); tr.SetCurrentBlock(*fb); return nullptr; } protected: Translator& tr; const ForInExpr* forin{nullptr}; Expr* range{nullptr}; Ptr res; Ptr iterVar; Ptr iterValue; // the value of loaded iterVar. Ptr condVar; Ptr TranslateIterVar(Value& iterBegin) { auto loc = tr.TranslateLocation(*forin->pattern); auto intType = iterBegin.GetType(); auto iterType = tr.builder.GetType(intType); auto res1 = tr.CreateAndAppendExpression(loc, iterType, intType, tr.GetCurrentBlock()); res1->Set(true); tr.CreateWrappedStore(loc, &iterBegin, res1->GetResult(), tr.GetCurrentBlock()); return res1->GetResult(); } Value* TranslateBegin() { auto& rangeBegin = Is(range) ? *DynamicCast(range)->args[0]->expr : range->desugarExpr ? *StaticCast(*range->desugarExpr).args[0]->expr : *StaticCast(range)->startExpr; auto value = Translator::TranslateASTNode(rangeBegin, tr); return tr.GetDerefedValue(value, value->GetDebugLocation()); } Value* TranslateEnd() { auto& rangeEnd = Is(range) ? *DynamicCast(range)->args[1]->expr : range->desugarExpr ? *StaticCast(*range->desugarExpr).args[1]->expr : *StaticCast(range)->stopExpr; auto value = Translator::TranslateASTNode(rangeEnd, tr); return tr.GetDerefedValue(value, value->GetDebugLocation()); } /// Create body block and set currentBlock to this Block* CreateForInEntryBlock() { auto bl = tr.CreateBlock(); tr.SetCurrentBlock(*bl); return bl; } struct ForinBGs { BlockGroup* body; BlockGroup* cond; BlockGroup* latch; }; ForinBGs CreateRetAndBGs() { Func* parentFunc = tr.currentBlock->GetTopLevelFunc(); CJC_NULLPTR_CHECK(parentFunc); BlockGroup* bodyBlockGrp = tr.builder.CreateBlockGroup(*parentFunc); BlockGroup* condBlockGrp = tr.builder.CreateBlockGroup(*parentFunc); BlockGroup* latchBlockGrp = tr.builder.CreateBlockGroup(*parentFunc); ForinBGs ret{bodyBlockGrp, condBlockGrp, latchBlockGrp}; res = CreateRes(ret); Ptr bodyEntry = tr.builder.CreateBlock(bodyBlockGrp); bodyBlockGrp->SetEntryBlock(bodyEntry); Ptr condEntry = tr.builder.CreateBlock(condBlockGrp); condBlockGrp->SetEntryBlock(condEntry); condEntry->Set(true); Ptr latchEntry = tr.builder.CreateBlock(latchBlockGrp); latchBlockGrp->SetEntryBlock(latchEntry); return ret; } ForIn* CreateRes(const ForinBGs& bgs) { auto loc = tr.TranslateLocation(*forin); auto forInClosedRange = tr.CreateAndAppendExpression( loc, tr.TranslateType(*forin->ty), iterVar, condVar, tr.GetCurrentBlock()); CJC_NULLPTR_CHECK(forInClosedRange); CJC_NULLPTR_CHECK(bgs.body); CJC_NULLPTR_CHECK(bgs.latch); CJC_NULLPTR_CHECK(bgs.cond); forInClosedRange->InitBlockGroups(*bgs.body, *bgs.latch, *bgs.cond); return forInClosedRange; } void TranslateBodyBG(BlockGroup& body) { tr.blockGroupStack.emplace_back(&body); tr.SetCurrentBlock(*body.GetEntryBlock()); Translator::ScopeContext contextInExpression(tr); contextInExpression.ScopePlus(); // let i = Load(iter) // use member var iterValue to ensure iterVar is loaded only once per loop iterValue = tr.CreateAndAppendExpression( tr.TranslateLocation(*forin->pattern), StaticCast(iterVar->GetType())->GetBaseType(), iterVar, tr.GetCurrentBlock())->GetResult(); if (auto vp = DynamicCast(forin->pattern.get())) { auto& var = *vp->varDecl; tr.SetSymbolTable(var, *iterValue); } // otherwise the pattern is wildcard. In this case, the value of iterVar need not load. Translator::ScopeContext contextBody(tr); contextBody.ScopePlus(); tr.TranslateForInBodyBlockGroup(*forin); if (auto vp = DynamicCast(forin->pattern.get())) { tr.localValSymbolTable.Erase(*vp->varDecl); } tr.blockGroupStack.pop_back(); } void TranslateCondBG(BlockGroup& cond, Value& iterEnd) { tr.blockGroupStack.emplace_back(&cond); tr.SetCurrentBlock(*cond.GetEntryBlock()); Translator::ScopeContext context(tr); context.ScopePlus(); tr.TranslateForInCondControlFlow(condVar); // cond = i != b auto& iterVarLoc = iterVar->GetDebugLocation(); auto conditionInCondBG = tr.CreateAndAppendExpression(iterVarLoc, tr.builder.GetBoolTy(), ExprKind::NOTEQUAL, iterValue, &iterEnd, tr.GetCurrentBlock())->GetResult(); tr.CreateWrappedStore(iterVarLoc, conditionInCondBG, condVar, tr.GetCurrentBlock()); tr.CreateAndAppendTerminator(tr.GetCurrentBlock()); tr.blockGroupStack.pop_back(); } void TranslateLatchBG(BlockGroup& latch) { tr.blockGroupStack.emplace_back(&latch); tr.SetCurrentBlock(*latch.GetEntryBlock()); // iter = iter + 1 DelayExitEquals0(&TranslateForInExpr::IncrementIterVarIfCond); tr.blockGroupStack.pop_back(); } /// Check whether delayExitSignal equals 0, and create two blocks following the check. Do the usual logic in the /// true block, while the false block just exit from the current block group. /// \param b whatever you want to translate in the true block. void DelayExitEquals0(void (TranslateForInExpr::*b)()) { Ptr delayExitTrueBlock = tr.CreateBlock(); Ptr delayExitFalseBlock = tr.CreateBlock(); auto delayExitValue = tr.CreateAndAppendExpression(tr.builder.GetInt64Ty(), tr.delayExitSignal, tr.GetCurrentBlock())->GetResult(); auto constZero = tr.CreateAndAppendConstantExpression(tr.builder.GetInt64Ty(), *tr.GetCurrentBlock(), 0UL)->GetResult(); auto needsExit = tr.CreateAndAppendExpression(tr.builder.GetBoolTy(), ExprKind::EQUAL, delayExitValue, constZero, tr.GetCurrentBlock())->GetResult(); auto br = CreateBranch(needsExit, delayExitTrueBlock, delayExitFalseBlock, tr.GetCurrentBlock()); br->Set(SkipKind::SKIP_DCE_WARNING); tr.SetCurrentBlock(*delayExitTrueBlock); (this->*b)(); tr.CreateAndAppendTerminator(tr.GetCurrentBlock()); tr.SetCurrentBlock(*delayExitFalseBlock); auto exit = tr.CreateAndAppendTerminator(tr.GetCurrentBlock()); exit->Set(SkipKind::SKIP_FORIN_EXIT); } /// True if rangeExpr is of step 1. False if -1. No other cases exist. bool IsIncrement() { if (auto call = DynamicCast(range)) { return call->args[2u]->expr->constNumValue.asInt.Int64() == 1; } if (range->desugarExpr) { return StaticCast(*range->desugarExpr).args[2u]->expr->constNumValue.asInt.Int64() == 1; } return StaticCast(range)->stepExpr->constNumValue.asInt.Int64() == 1; } void IncrementIterVarIfCond() { auto& iterLoc = iterVar->GetDebugLocation(); // if cond { // iter = i ± 1 // } auto ifCond = tr.CreateAndAppendExpression( iterLoc, tr.builder.GetBoolTy(), condVar, tr.GetCurrentBlock())->GetResult(); auto tb = tr.CreateBlock(); auto fb = tr.CreateBlock(); auto br = CreateBranch(ifCond, tb, fb, tr.GetCurrentBlock()); // this cond is computed from iterValue != iterEnd, which is false after CP if the close range is x..=x. // in this case the unreachable warning is unnecessary because this check is always copied into two, and the // first one (which is iterBegin <= iterEnd) returns true. tb->Set(SkipKind::SKIP_DCE_WARNING); br->Set(SkipKind::SKIP_DCE_WARNING); tr.SetCurrentBlock(*tb); auto type = StaticCast(iterVar->GetType())->GetBaseType(); auto one = tr.CreateAndAppendConstantExpression(type, *tr.GetCurrentBlock(), 1UL)->GetResult(); // this add is guaranteed to be never throwing auto newValue = tr.CreateAndAppendExpression( iterLoc, type, IsIncrement() ? ExprKind::ADD : ExprKind::SUB, iterValue, one, OverflowStrategy::WRAPPING, tr.GetCurrentBlock())->GetResult(); tr.CreateWrappedStore(iterLoc, newValue, iterVar, tr.GetCurrentBlock()); tr.CreateAndAppendTerminator(fb, tb); tr.SetCurrentBlock(*fb); } /// things to be translated before allocating condVar/iterVar. This differs among forin expression kinds. Block* TranslatePreCondition(Value& iterBegin, Value& iterEnd) { auto loc = tr.TranslateLocation(*range); auto le = tr.CreateAndAppendExpression(loc, tr.builder.GetBoolTy(), IsIncrement() ? ExprKind::LE : ExprKind::GE, &iterBegin, &iterEnd, tr.GetCurrentBlock())->GetResult(); auto tb = tr.CreateBlock(); auto fb = tr.CreateBlock(); // when the forin is guaranteed to be terminated inside the body block (e.g. through unconditional // break/return), the false block is unreachable. Do not issue a diag because the forin block is reachable fb->Set(SkipKind::SKIP_DCE_WARNING); CreateBranch(le, tb, fb, tr.GetCurrentBlock()); tr.SetCurrentBlock(*tb); return fb; } template Branch* CreateBranch(Args... args) { auto br = tr.CreateAndAppendTerminator(args...); br->SetSourceExpr(SourceExpr::FOR_IN_EXPR); return br; } }; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND Ptr TranslateForInClosedRangeOne(Translator& tr, const ForInExpr& forInExpr) { TranslateForInExpr impl{tr}; return impl.Translate(forInExpr); } #endif } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateFuncArg.cpp000066400000000000000000000012051510705540100265220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/IntrinsicKind.h" using namespace Cangjie::CHIR; using namespace Cangjie; Ptr Translator::Visit(const AST::FuncArg& arg) { auto val = TranslateExprArg(*arg.expr); // not handled here; see Translator::TranslateTrivialArgWithNoSugar CJC_ASSERT(!arg.withInout); return val; } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateFuncDecl.cpp000066400000000000000000000523051510705540100266670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/CHIR/AttributeInfo.h" #include "cangjie/CHIR/Utils.h" #include static std::mutex g_memberVarMutex; namespace Cangjie::CHIR { using namespace AST; void Translator::BindingFuncParam( const AST::FuncParamList& paramList, const BlockGroup& funcBody, const BindingConfig& cfg) { const auto& params = GetFuncParams(funcBody); size_t offset = paramList.params.size() < params.size() ? 1 : 0; CJC_ASSERT(paramList.params.size() + offset == params.size()); auto block = funcBody.GetEntryBlock(); for (size_t i = 0; i < paramList.params.size(); ++i) { auto arg = params[i + offset]; auto& param = paramList.params[i]; if (cfg.setSymbol) { SetSymbolTable(*param, *arg); } if (!cfg.createDebug) { continue; } auto loc = TranslateLocation(*param); auto paramLoc = GetVarLoc(builder.GetChirContext(), *param); // Don't need to report unused parameter on defaut value parameter function. auto debug = CreateAndAppendExpression( paramLoc, loc, builder.GetUnitTy(), arg, param->identifier.GetRawText(), block); if (cfg.hasInitial) { debug->Set(SkipKind::SKIP_DCE_WARNING); } } } static Ptr GetGenericDecl(const AST::Decl* instantiation) { if (instantiation == nullptr) { return nullptr; } if (instantiation->outerDecl && instantiation->outerDecl->genericDecl) { return instantiation->outerDecl->genericDecl; } if (instantiation->genericDecl) { return instantiation->genericDecl; } return nullptr; } static bool IsCopiedSrcFuncFromInterface(const AST::Decl* decl) { if (decl == nullptr) { return false; } // Imported copied function will have rawMangled name, and do not need to record its parent. return decl->TestAttr(AST::Attribute::DEFAULT, AST::Attribute::COMPILER_ADD) && decl->outerDecl != nullptr && !decl->TestAttr(AST::Attribute::IMPORTED); } static bool IsMemberFuncOfBox(const AST::Decl* decl) { if (decl == nullptr) { return false; } if (decl->astKind != AST::ASTKind::FUNC_DECL || decl->outerDecl == nullptr) { return false; } return decl->outerDecl->identifier.Val().find("$BOX_") != std::string::npos; } static bool IsMemberFuncOfExtend(const AST::Decl* decl) { if (decl == nullptr) { return false; } if (decl->astKind != AST::ASTKind::FUNC_DECL || decl->outerDecl == nullptr) { return false; } return decl->outerDecl->astKind == AST::ASTKind::EXTEND_DECL; } void Translator::SetRawMangledNameForIncrementalCompile(const AST::FuncDecl& astFunc, Func& chirFunc) const { AST::Decl* decl = nullptr; if (astFunc.TestAttr(AST::Attribute::HAS_INITIAL)) { // this func is default param of another func, so it doesn't have rawMangledName // if this func need to be recompiled, we need to recompile its owner func CJC_NULLPTR_CHECK(astFunc.ownerFunc); decl = astFunc.ownerFunc; } // this func is a member method of class, and copied from interface, so it doesn't have rawMangledName // if this func need to be recompiled, we need to recompile its class if (IsCopiedSrcFuncFromInterface(decl)) { CJC_NULLPTR_CHECK(decl); decl = decl->outerDecl; } else if (IsCopiedSrcFuncFromInterface(&astFunc)) { decl = astFunc.outerDecl; } // this func is a member method of BOX, so it doesn't have rawMangledName // if this func need to be recompiled, we need to recompile its class if (IsMemberFuncOfBox(decl)) { CJC_NULLPTR_CHECK(decl); decl = decl->outerDecl; } else if (IsMemberFuncOfBox(&astFunc)) { decl = astFunc.outerDecl; } AST::Decl* parent = nullptr; if (IsMemberFuncOfExtend(decl)) { CJC_NULLPTR_CHECK(decl); parent = decl->outerDecl; } else if (IsMemberFuncOfExtend(&astFunc)) { parent = astFunc.outerDecl; } // this func is a instantiated func, so it doesn't have rawMangledName // if this func need to be recompiled, we need to recompile its generic decl if (auto gd1 = GetGenericDecl(decl); gd1) { decl = gd1; } else if (auto gd2 = GetGenericDecl(&astFunc); gd2) { decl = gd2; } else if (auto gd3 = GetGenericDecl(parent); gd3) { parent = gd3; } if (decl != nullptr) { if (decl->identifier.Val().find("$BOX_") != std::string::npos) { chirFunc.SetRawMangledName("$BOX"); } else { chirFunc.SetRawMangledName(decl->rawMangleName); } } else { chirFunc.SetRawMangledName(astFunc.rawMangleName); } if (parent != nullptr) { chirFunc.SetParentRawMangledName(parent->rawMangleName); } } bool NeedCreateDebugForFirstParam(const Func& func) { if (func.TestAttr(Attribute::STATIC)) { return false; } auto parentDef = func.GetParentCustomTypeDef(); // global function doesn't have `this` if (parentDef == nullptr) { return false; } auto extendDef = DynamicCast(parentDef); // function declared in class, struct, enum def need create `Debug(%0, this)` if (extendDef == nullptr) { return true; } // function declared in `extend Int32` doesn't need to create `Debug(%0, this)`, it's better in cjdb return extendDef->GetExtendedType()->IsNominal(); } Ptr Translator::Visit(const AST::FuncDecl& func) { CJC_NULLPTR_CHECK(func.funcBody); // Abstract function do not need func body. 'intrinsic' and 'foreign' function was ignored from toplevel. if (func.TestAnyAttr(AST::Attribute::INTRINSIC, AST::Attribute::ABSTRACT, AST::Attribute::FOREIGN)) { return nullptr; } if (IsCommonWithoutDefault(func)) { return nullptr; } bool isLifted = localConstFuncs.HasElement(&func); if (isLifted && !IsTopLevel(func)) { return nullptr; } if ((func.ownerFunc ? !IsGlobalOrMember(*func.ownerFunc) : !IsGlobalOrMember(func)) && !isLifted) { // local const funcs are lifted to global funcs, do not translate as nested func return TranslateNestedFunc(func); } // In incremental compilation, no need to generate body for non-recompile func, but we need to // exclude the nested func here if (increKind == IncreKind::INCR && !func.toBeCompiled && !IsSrcCodeImportedGlobalDecl(func, opts)) { return nullptr; } CJC_ASSERT(!isLifted || IsTopLevel(func)); // translateTopLevel Func* curFunc = VirtualCast(globalSymbolTable.Get(func)); if (IsCopiedSrcFuncFromInterface(&func)) { curFunc->Set(SkipKind::SKIP_DCE_WARNING); } SetRawMangledNameForIncrementalCompile(func, *curFunc); if (curFunc->GetFuncKind() == FuncKind::GETTER || curFunc->GetFuncKind() == FuncKind::SETTER) { CJC_NULLPTR_CHECK(func.propDecl); auto propLoc = TranslateLocation(*func.propDecl); curFunc->SetPropLocation(propLoc); } auto body = curFunc->GetBody(); blockGroupStack.emplace_back(body); auto entry = builder.CreateBlock(body); body->SetEntryBlock(entry); if (NeedCreateDebugForFirstParam(*curFunc)) { auto thisVar = curFunc->GetParam(0); CreateAndAppendExpression(builder.GetUnitTy(), thisVar, "this", curFunc->GetEntryBlock()); } BindingFuncParam(*func.funcBody->paramLists[0], *curFunc->GetBody(), {.hasInitial = func.TestAttr(AST::Attribute::HAS_INITIAL), .createDebug = true}); // Set return value if the return type is not Void auto retType = curFunc->GetReturnType(); if (!retType->IsVoid()) { auto retVal = CreateAndAppendExpression(DebugLocation(), builder.GetType(retType), retType, entry) ->GetResult(); curFunc->SetReturnValue(*retVal); } // Translate body. auto block = Visit(*func.funcBody.get()); CreateAndAppendTerminator(StaticCast(block.get()), entry); blockGroupStack.pop_back(); return curFunc; } Ptr Translator::Visit(const AST::FuncBody& funcBody) { Ptr ret{nullptr}; if (funcBody.funcDecl == nullptr) { // Translate lambda body (lambda body is treated as normal func body). ret = TranslateFuncBody(funcBody); } else { auto func = funcBody.funcDecl; if (IsInstanceConstructor(*func)) { CJC_NULLPTR_CHECK(func->outerDecl); if (func->outerDecl->TestAnyAttr(AST::Attribute::COMMON, AST::Attribute::PLATFORM)) { ret = TranslateConstructorFunc(*func->outerDecl, funcBody); } else { ret = TranslateConstructorFuncInline(*func->outerDecl, funcBody); } } else if (IsInstanceMember(*func)) { ret = TranslateInstanceMemberFunc(*func->outerDecl, funcBody); } else { // Translate normal function ret = TranslateFuncBody(funcBody); } } CJC_NULLPTR_CHECK(ret); return ret; } Ptr Translator::TranslateFuncBody(const AST::FuncBody& funcBody) { if (funcBody.body == nullptr) { return CreateBlock(); } CJC_ASSERT(funcBody.body); Visit(*funcBody.body); CJC_ASSERT(currentBlock); // After translation of body, // if the last block is an unreachable block, it may not have terminator and will be removed later, // otherwise it must have a terminator. CJC_ASSERT((!currentBlock->IsEntry() && currentBlock->GetPredecessors().empty()) || currentBlock->GetTerminator() != nullptr); return GetBlockByAST(*funcBody.body); } Ptr Translator::TranslateInstanceMemberFunc(const AST::Decl& parent, const AST::FuncBody& funcBody) { auto curFunc = GetCurrentFunc(); CJC_NULLPTR_CHECK(curFunc); // Binding the implicit 'this' with the first param of the function. auto thisVar = curFunc->GetParam(0); CJC_NULLPTR_CHECK(thisVar); if (parent.astKind == AST::ASTKind::EXTEND_DECL) { if (auto typeDecl = AST::Ty::GetDeclOfTy(parent.ty)) { SetSymbolTable(*typeDecl, *thisVar); } } else { SetSymbolTable(parent, *thisVar); } return TranslateFuncBody(funcBody); } namespace { // Get instance field size of super type. Always return 0 for non-class type. uint64_t GetSuperInstanceVarSize(const Type& thisType) { CJC_ASSERT(thisType.IsRef()); auto realType = StaticCast(thisType).GetBaseType(); if (!realType->IsClass()) { return 0; } auto super = StaticCast(realType)->GetClassDef()->GetSuperClassDef(); return super ? super->GetAllInstanceVarNum() : 0; } // returns true when the function body contains a delegated this constructor call bool HasDelegatedThisCall(const AST::FuncBody& func) { CJC_ASSERT(func.body); auto& body = func.body->body; CJC_ASSERT(!body.empty()); // first expression is callexpr to constructor if (auto call = DynamicCast(body[0].get())) { // the `resolvedFunction` of callExpr with call_function_ptr is null. if (call->callKind == AST::CallKind::CALL_FUNCTION_PTR) { return false; } /* example code: init() { this(1) // we want to find `this` constructor, and recognized by it's baseFunc is 'this' and resolvedFunction is constructor. } */ if (call->resolvedFunction && call->resolvedFunction->TestAttr(AST::Attribute::CONSTRUCTOR)) { // callee is a refexpr that is this if (auto ref = DynamicCast(call->baseFunc.get())) { return ref->isThis; } } } return false; } } // unnamed namespace void Translator::TranslateVariablesInit(const AST::Decl& parent, CHIR::Parameter& thisVar) { auto offset = GetSuperInstanceVarSize(*thisVar.GetType()); CustomTypeDef* typeDef = GetNominalSymbolTable(parent).get(); std::unordered_map varInfoByName; auto memberVars = typeDef->GetDirectInstanceVars(); for (auto& varDef : memberVars) { varInfoByName.emplace(varDef.name, &varDef); } auto initOrder = AST::GetVarsInitializationOrderWithPositions(parent); for (auto [member, fieldPosition] : initOrder) { // Fields declared in primary constructor cannot have initializer CJC_ASSERT(!member->isMemberParam || !member->initializer); MemberVarInfo* varInfo = varInfoByName.at(member->identifier); if (ShouldTranslateMember(parent, *member)) { Translator trans = Copy(); auto initializerFunc = trans.TranslateVarInit(*member); varInfo->initializerFunc = initializerFunc; } auto varInitFunc = varInfo->initializerFunc; if (varInitFunc) { std::vector args{&thisVar}; auto loc = TranslateLocation(*member); auto apply = CreateAndAppendExpression( loc, varInitFunc->GetReturnType(), varInitFunc, FuncCallContext{ .args = args, .thisType = thisVar.GetType()}, currentBlock); auto value = apply->GetResult(); std::vector path = {offset + fieldPosition}; auto pureCurObjType = StaticCast(thisVar.GetType())->GetBaseType(); CJC_ASSERT(!pureCurObjType->IsRef() && pureCurObjType->IsNominal()); auto pureCurObjCustomType = StaticCast(pureCurObjType); auto memberType = pureCurObjCustomType->GetInstMemberTypeByPath(path, builder); if (value->GetType() == memberType) { CreateAndAppendExpression( loc, builder.GetUnitTy(), value, &thisVar, path, currentBlock); } else { auto castedValue = TypeCastOrBoxIfNeeded(*value, *memberType, loc); CreateAndAppendExpression( loc, builder.GetUnitTy(), castedValue, &thisVar, path, currentBlock); } } } typeDef->SetDirectInstanceVars(memberVars); } Ptr Translator::TranslateConstructorFunc(const AST::Decl& parent, const AST::FuncBody& funcBody) { CJC_NULLPTR_CHECK(funcBody.funcDecl); auto curFunc = GetCurrentFunc(); CJC_NULLPTR_CHECK(curFunc); // Binding the implicit `this` with the first param of the function. auto thisVar = curFunc->GetParam(0); CJC_NULLPTR_CHECK(thisVar); SetSymbolTable(parent, *thisVar); // Create init blocks and initializers for constructor. NOTE: do not create 'GoTo' from entry to init here. // Return init block for callee to uniformly create 'GoTo' from entry in 'Visit FuncDecl'. auto initBlock = CreateBlock(); currentBlock = initBlock; // insert member decl initializer if this constructor does not have delegate this call if (!HasDelegatedThisCall(funcBody)) { CustomTypeDef* typeDef = GetNominalSymbolTable(parent).get(); FuncBase* varInitFunc = typeDef->GetVarInitializationFunc(); CJC_NULLPTR_CHECK(varInitFunc); std::vector args{thisVar}; auto loc = TranslateLocation(funcBody); CreateAndAppendExpression(loc, varInitFunc->GetReturnType(), varInitFunc, FuncCallContext{ .args = args, .thisType = thisVar->GetType()}, currentBlock); } // After generated the initializers, the 'currentBlock' may change. auto lastBlock = currentBlock; auto bodyBlock = TranslateFuncBody(funcBody); CreateAndAppendTerminator(bodyBlock, lastBlock); return initBlock; } Ptr Translator::TranslateConstructorFuncInline(const AST::Decl& parent, const AST::FuncBody& funcBody) { CJC_NULLPTR_CHECK(funcBody.funcDecl); auto curFunc = GetCurrentFunc(); CJC_NULLPTR_CHECK(curFunc); // Binding the implicit `this` with the first param of the function. auto thisVar = curFunc->GetParam(0); CJC_NULLPTR_CHECK(thisVar); SetSymbolTable(parent, *thisVar); // Create init blocks and initializers for constructor. NOTE: do not create 'GoTo' from entry to init here. // Return init block for callee to uniformly create 'GoTo' from entry in 'Visit FuncDecl'. auto initBlock = CreateBlock(); currentBlock = initBlock; // insert member decl initializer if this constructor does not have delegate this call if (!HasDelegatedThisCall(funcBody)) { auto fieldOffset = GetSuperInstanceVarSize(*thisVar->GetType()); for (auto member : parent.GetMemberDeclPtrs()) { if (member->astKind != AST::ASTKind::VAR_DECL || member->TestAttr(AST::Attribute::STATIC)) { continue; } if (auto& init = StaticCast(member)->initializer) { Ptr value; { std::unique_lock lock(g_memberVarMutex); value = TranslateExprArg(*init); } auto loc = TranslateLocation(*member); std::vector path = {fieldOffset}; auto pureCurObjType = StaticCast(thisVar->GetType())->GetBaseType(); CJC_ASSERT(!pureCurObjType->IsRef() && pureCurObjType->IsNominal()); auto pureCurObjCustomType = StaticCast(pureCurObjType); auto memberType = pureCurObjCustomType->GetInstMemberTypeByPath(path, builder); if (value->GetType() == memberType) { CreateAndAppendExpression( loc, builder.GetUnitTy(), value, thisVar, path, currentBlock); } else { auto castedValue = TypeCastOrBoxIfNeeded(*value, *memberType, loc); CreateAndAppendExpression( loc, builder.GetUnitTy(), castedValue, thisVar, path, currentBlock); } } ++fieldOffset; } } // After generated the initializers, the 'currentBlock' may change. auto lastBlock = currentBlock; auto bodyBlock = TranslateFuncBody(funcBody); CreateAndAppendTerminator(bodyBlock, lastBlock); return initBlock; } Ptr Translator::TranslateNestedFunc(const AST::FuncDecl& func) { CJC_ASSERT(func.funcBody && func.funcBody->body); if (func.TestAttr(AST::Attribute::GENERIC)) { TranslateFunctionGenericUpperBounds(chirTy, func); } auto lambdaTrans = SetupContextForLambda(*func.funcBody->body); auto funcTy = RawStaticCast(TranslateType(*func.ty)); // Create nested functions' body and parameters. CJC_NULLPTR_CHECK(currentBlock->GetTopLevelFunc()); BlockGroup* body = builder.CreateBlockGroup(*currentBlock->GetTopLevelFunc()); const auto& loc = TranslateLocation(func); std::vector genericTys; // Only collect for generic function which has not been instantiated. if (auto generic = func.funcBody->generic.get(); generic && func.TestAttr(AST::Attribute::GENERIC)) { for (auto& type : generic->typeParameters) { genericTys.emplace_back(RawStaticCast(TranslateType(*type->ty))); } } std::string lambdaMangleName = func.mangledName; Lambda* lambda = CreateAndAppendExpression( loc, funcTy, funcTy, currentBlock, true, lambdaMangleName, func.identifier, genericTys); CJC_ASSERT(lambda); lambda->InitBody(*body); if (func.isConst) { lambda->SetCompileTimeValue(); } std::vector paramLoc; for (auto& astParam : func.funcBody->paramLists[0]->params) { paramLoc.emplace_back(TranslateLocationWithoutScope(builder.GetChirContext(), astParam->begin, astParam->end)); } auto paramTypes = funcTy->GetParamTypes(); CJC_ASSERT(paramTypes.size() == paramLoc.size()); for (size_t i = 0; i < paramTypes.size(); ++i) { builder.CreateParameter(paramTypes[i], paramLoc[i], *lambda); } SetSymbolTable(func, *lambda->GetResult()); for (auto& param : func.funcBody->paramLists[0]->params) { if (param->desugarDecl) { // recursively translate default parameter function of a lambda (possibly a local func) auto defaultArgFunc = TranslateNestedFunc(*param->desugarDecl); StaticCast(StaticCast(defaultArgFunc)->GetExpr())->SetParamDftValHostFunc(*lambda); // set such functions as local symbols in the current scope but not in the lambda translation context // as a default parameter function can only be called outside the function lambdaTrans.SetSymbolTable(*param->desugarDecl, *defaultArgFunc); } } // Local function allows to call itself inside funcBody. lambdaTrans.SetSymbolTable(func, *lambda->GetResult()); return lambdaTrans.TranslateLambdaBody( lambda, *func.funcBody, {.hasInitial = func.TestAttr(AST::Attribute::HAS_INITIAL)}); } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateIfExpr.cpp000066400000000000000000000565361510705540100264130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" using namespace Cangjie::AST; namespace Cangjie::CHIR { bool Translator::CanOptimizeToSwitch(const LetPatternDestructor& let) const { if (!opts.IsOptimizationExisted(FrontendOptions::OptimizationFlag::SWITCH_OPT)) { return false; } // A wildcard pattern renders cases following it unreachable. if (let.patterns[0]->astKind == ASTKind::WILDCARD_PATTERN) { return false; } const auto& type = let.initializer->ty; bool validSelectorTy = IsOptimizableTy(type) || (type->IsEnum() && IsOptimizableEnumTy(type)); if (!validSelectorTy) { return false; } for (auto& curPattern : let.patterns) { // patterns connected by '|' should be of the same kind, so when the first is not WILDCARD, others neither. CJC_ASSERT(curPattern->astKind != ASTKind::WILDCARD_PATTERN); if (curPattern->astKind == ASTKind::CONST_PATTERN) { continue; } auto vep = DynamicCast(curPattern.get()); auto realPatternKind = vep ? vep->pattern->astKind : curPattern->astKind; if (realPatternKind != ASTKind::ENUM_PATTERN) { return false; } else if (vep) { continue; } auto enumPattern = StaticCast(curPattern.get()); if (enumPattern->patterns.empty()) { // enum patterns without params do not break any rule continue; } auto firstParamKind = enumPattern->patterns[0]->astKind; // Since types of all enum constructor have been checked, the type of constant pattern must be valid. if (firstParamKind == ASTKind::WILDCARD_PATTERN || firstParamKind == ASTKind::CONST_PATTERN) { continue; } return false; } return true; } class TranslateCondCtrlExpr { public: virtual ~TranslateCondCtrlExpr() = default; protected: struct DanglingBlock { Ptr tb; // dangling matching block Ptr fb; // dangling non-matching block. This is null when the pattern definitely matches. }; /// \param isRightmost: Only the rightmost condition branches directly to the true block and false block of the /// original if expr, when considering a condition a binary tree formed by logical 'and' and 'or'. Non-rightmost /// shall have this parameter set to false during recursive translation and do not generate a branch at the end. /// The terminator is instead set by caller. /// /// Invariant: /// If isRightmost is true, the generated block has a terminator; /// otherwise, the generated block does not have a terminator after \ref TranslateCondition call, its terminator /// is set by its caller. /// /// e.g. consider this condition (suppose all terminators are let patterns, cause normal expr is translated as /// normal expr node) /// if (a && (b || c)) {...} /// Will have this stack tree: /// DO(a && (b || c), true) /// DO(a, false) /// DO(b || c, true) /// DO(b, false) /// DO(c, true) /// BRANCH(b, TRUE-BLOCK, c) /// BRANCH(a, b || c, FALSE-BLOCK) DanglingBlock TranslateCondition(const Expr& cond, bool isRightmost) { if (auto p = DynamicCast(&cond)) { return TranslateCondition(*p->expr, isRightmost); } if (auto let = DynamicCast(&cond)) { return TranslateLetPattern(*let, isRightmost); } if (auto bin = DynamicCast(&cond); bin && (bin->op == TokenKind::AND || bin->op == TokenKind::OR) && (IsCondition(*bin->leftExpr) || IsCondition(*bin->rightExpr))) { return TranslateBinary(*bin, isRightmost); } // normal expression as condition return TranslateNormalCondition(cond, isRightmost); } static bool HasTypeOfNothing(const AST::IfExpr& ifExpr) { // Type of if expression may be type of expected target, // we need to check all branch for real nothing typed control flow. return ifExpr.elseBody && ifExpr.thenBody->ty->IsNothing() && ifExpr.elseBody->ty->IsNothing(); } DanglingBlock TranslateNormalCondition(const Expr& cond, bool isRightmost) { auto val = tr.TranslateExprArg(cond); auto loc = tr.TranslateLocation(cond.begin, cond.end); auto cur = tr.GetCurrentBlock(); if (isRightmost) { auto tb = TranslateTrueBlock(); auto fb = TranslateFalseBlock(); tr.SetCurrentBlock(*cur); auto br = CreateBranch(*val, *cur, *tb, *fb); // set debug location to the whole expr for the outermost branch br->SetDebugLocation(tr.TranslateLocation(*GetExpr())); return {}; } auto tb = CreateBlock(); auto fb = CreateBlock(); tr.SetCurrentBlock(*cur); auto br = CreateBranch(*tr.GetDerefedValue(val, loc), *cur, *tb, *fb); br->SetDebugLocation(loc); return {tb, fb}; } /// Make \param left unconditinally goes to \param right and return \param right if both are non empty. /// If only one is non empty, returns it. If both are empty, returns null Block* MergeBlocks(CHIR::Block* left, CHIR::Block* right) { if (left) { if (right) { CreateGoto(*right, *left); return right; } return left; } return right; } DanglingBlock TranslateBinary(const BinaryExpr& bin, bool isRightmost) { auto left = TranslateCondition(*bin.leftExpr, false); auto bl = CreateBlock(); tr.SetCurrentBlock(*bl); // false branch will be generated in this block auto right = TranslateCondition(*bin.rightExpr, isRightmost); auto loc = tr.TranslateLocation(bin); if (bin.op == TokenKind::AND) { CreateGoto(*bl, *left.tb); // only set to trueBlock/falseBlock of the if expression if this is not rightmost // left.fb is null when the left pattern match always returns true (i.e. the pattern is irrefutable) if (left.fb) { right.fb = MergeBlocks(left.fb, right.fb); } } else { if (left.fb) { CreateGoto(*bl, *left.fb); } else { bl->EnableAttr(Attribute::UNREACHABLE); } right.tb = MergeBlocks(left.tb, right.tb); } return right; } DanglingBlock TranslateLetPattern(const LetPatternDestructor& let, bool isRightmost) { if (tr.CanOptimizeToSwitch(let)) { return TranslateLetPatternAsTable(let, isRightmost); } else { return TranslateLetPatternAsMatch(let, isRightmost); } } DanglingBlock TranslateLetPatternAsMatch(const LetPatternDestructor& let, bool isRightmost) { // translate initializer auto selectorVal = tr.TranslateExprArg(*let.initializer); Translator::ScopeContext context{tr}; auto originLoc = tr.TranslateLocation(let); { // translate let pattern context.ScopePlus(); auto [patternFalseBlock, patternTrueBlock] = let.patterns.size() == 1 ? tr.TranslateNestingCasePattern(*let.patterns[0], selectorVal, originLoc, SourceExpr::IF_EXPR) : tr.TranslateOrPattern(let.patterns, selectorVal, originLoc); // translate pattern not match block if (isRightmost) { auto tb = TranslateTrueBlock(); auto fb = TranslateFalseBlock(); CreateGoto(*tb, *patternTrueBlock); CreateGoto(*fb, *patternFalseBlock); return {}; } return {patternTrueBlock, patternFalseBlock}; } } DanglingBlock TranslateLetPatternAsTable(const LetPatternDestructor& let, bool isRightmost) { auto selectorTy = let.initializer->ty; auto selectorVal = tr.TranslateExprArg(*let.initializer); // Previously checked that selector ty is integer, char or enum. auto enumDecl = DynamicCast(AST::Ty::GetDeclOfTy(selectorTy)); DanglingBlock res; if (!enumDecl) { // NOTE: selector's ty is Rune, UInt, Int. res = TranslateTrivialPatternAsTable(let, *selectorVal, 0); } else if (!enumDecl->hasArguments || DoesNotHaveEnumSubpattern(let)) { // Note: selector'ty is enum, and no enumPattern with param. res = TranslateTrivialPatternAsTable( let, *GetEnumIDValue(*selectorTy, *selectorVal), enumDecl->constructors.size()); } else { res = TranslateEnumPatternMatchAsTable(let, *selectorVal); } // translate pattern not match block if (isRightmost) { auto tb = TranslateTrueBlock(); auto fb = TranslateFalseBlock(); CreateGoto(*tb, *res.tb); if (res.fb) { CreateGoto(*fb, *res.fb); } return {}; } return res; } DanglingBlock TranslateEnumPatternMatchAsTable(const LetPatternDestructor& let, Value& enumVal) { bool isStillReachable = true; auto baseBlock = tr.GetCurrentBlock(); auto tb = CreateBlock(); auto fb = CreateBlock(); auto firstDefaultBlock = fb; auto firstSelectorVal = GetEnumIDValue(*let.initializer->ty, enumVal); Translator::EnumMatchInfo matchInfo; { Translator::ScopeContext context(tr); context.ScopePlus(); if (let.patterns[0]->astKind == AST::ASTKind::WILDCARD_PATTERN) { // Wildcard pattern in current case can only exited on top level. isStillReachable = false; // NOTE: only should to collect first visited wildcard pattern. // The sub pattern may contains varPattern, translate when binding second level table. firstDefaultBlock = tb; const auto& loc = tr.TranslateLocation(*let.patterns[0]); firstDefaultBlock->SetDebugLocation(loc); } // Store pattern value to block relation. for (auto& pattern : let.patterns) { tr.CollectEnumPatternInfo(*pattern, matchInfo, 0); } } // Create branch to first level switch. std::vector indices{}; std::vector blocks{}; for (auto& p : std::as_const(matchInfo.firstSwitchBlocks)) { indices.push_back(p.first); blocks.push_back(p.second); } tr.CreateAndAppendTerminator(firstSelectorVal, firstDefaultBlock, indices, blocks, baseBlock); // Create second switch block. auto branchInfos = tr.TranslateSecondLevelAsTable(matchInfo, &enumVal, firstDefaultBlock); // Finally create goto from case's true block to caseBody block. for (auto& block : std::as_const(branchInfos[0])) { CreateGoto(*tb, *block); } tr.SetCurrentBlock(*fb); return {tb, isStillReachable ? fb : nullptr}; } DanglingBlock TranslateTrivialPatternAsTable(const LetPatternDestructor& let, Value& val, size_t numOfPatterns) { std::map indexToBodies; auto baseBlock = tr.currentBlock; auto tb = CreateBlock(); // block to which all matching patterns unconditionally goes Block* defaultBlock{nullptr}; bool isStillReachable = true; Translator::ScopeContext context{tr}; context.ScopePlus(); CJC_ASSERT(!let.patterns.empty()); if (let.patterns[0]->astKind == ASTKind::WILDCARD_PATTERN) { auto loc = tr.TranslateLocation(*let.patterns[0]); auto patternBlock = CreateBlock(); isStillReachable = false; defaultBlock = patternBlock; } else { // Collect mapping: patternVal -> match-block for (auto& pattern : let.patterns) { auto patternVal = Translator::GetJumpablePatternVal(*pattern); auto loc = tr.TranslateLocation(*pattern); auto patternBlock = CreateBlock(); patternBlock->SetDebugLocation(loc); CreateGoto(*tb, *patternBlock); if (auto [_, success] = indexToBodies.emplace(patternVal, patternBlock); !success) { continue; } // Enum has limited number of patterns when all of them have been filled, rest cases are unreachable. if (indexToBodies.size() == numOfPatterns) { isStillReachable = false; defaultBlock = patternBlock; } } } if (!defaultBlock) { // create an empty block to which the control flow goes when the if let pattern does not // match defaultBlock = CreateBlock(); } std::vector indices{}; std::vector blocks{}; for (auto& p : std::as_const(indexToBodies)) { indices.push_back(p.first); blocks.push_back(p.second); } tr.currentBlock = baseBlock; Type* targetType; if (auto enumTy = DynamicCast(let.initializer->ty)) { targetType = tr.GetSelectorType(*enumTy); } else { targetType = tr.builder.GetUInt64Ty(); } auto castedVal = tr.TypeCastOrBoxIfNeeded(val, *targetType, {}); auto loc = tr.TranslateLocation(let); auto multib = tr.CreateAndAppendTerminator(castedVal, defaultBlock, indices, blocks, baseBlock); multib->SetDebugLocation(loc); tr.currentBlock = endBlock; return {tb, isStillReachable ? defaultBlock : nullptr}; } ///@{ /// Encapsulation of Translator methods. virtual Branch* CreateBranch(Value& val, CHIR::Block& cur, CHIR::Block& tb, CHIR::Block& fb) { auto res = tr.CreateAndAppendTerminator(&val, &tb, &fb, &cur); res->SetSourceExpr(GetSourceExpr()); return res; } GoTo* CreateGoto(CHIR::Block& dest, CHIR::Block& from) { return tr.CreateAndAppendTerminator(&dest, &from); } Block* CreateBlock() { return tr.CreateBlock(); } CHIRBuilder& Builder() { return tr.builder; } Ptr GetEnumIDValue(Ty& ty, Value& selectorVal) { return tr.GetEnumIDValue(&ty, &selectorVal); } Ptr GetBlockByAST(const AST::Block& block) { return tr.GetBlockByAST(block); } template void CreateWrappedStore(Args&&... args) { tr.CreateWrappedStore(std::forward(args)...); } ///@} Translator& tr; Ptr endBlock; // end block, the common successor of true block and false block const GlobalOptions& opts; explicit TranslateCondCtrlExpr(Translator& trs) : tr{trs}, opts{tr.opts} {} virtual Block* TranslateTrueBlock() = 0; virtual Block* TranslateFalseBlock() = 0; virtual SourceExpr GetSourceExpr() = 0; virtual const Expr* GetExpr() = 0; }; class TranslateIfExpr final : private TranslateCondCtrlExpr { Ptr trueBlock{}; // true block after translation. This is initialised to null, and set when a // terminator condition (i.e. no sub pattern) is first translated. Later trnaslated blocks will have their // branch target set to this pointer whenever a translated trueBlock is needed Ptr falseBlock{}; Ptr retVal{}; // location of value of this IfExpr Ptr e; // tests if both thenBody and elseBody of this IfExpr is empty static bool IsEmptyIf(const IfExpr& expr) { if (!IsEmptyBlock(*expr.thenBody)) { return false; } // thenBody empty if (expr.elseBody) { if (auto block = DynamicCast(&*expr.elseBody)) { return IsEmptyBlock(*block); } if (auto elseIf = DynamicCast(&*expr.elseBody)) { return IsEmptyIf(*elseIf); } return false; } // no elseBody, thenBody empty, returns true return true; } static bool IsEmptyBlock(const AST::Block& block) { if (block.body.size() == 1) { if (auto lit = DynamicCast(&*block.body[0])) { return lit->ty->IsUnit(); } else { return false; } } return block.body.empty(); } public: explicit TranslateIfExpr(Translator& trs) : TranslateCondCtrlExpr{trs} {} Ptr Translate(const IfExpr& e1) { e = &e1; auto ifType = tr.TranslateType(*e1.ty); // generate an Allocate(Unit&) for debugging, so that the if expr can be stepped in bool forceGenerateUnit = opts.enableCompileDebug && IsEmptyIf(e1); if ((!HasTypeOfNothing(e1) && !ifType->IsUnit()) || forceGenerateUnit) { retVal = tr.CreateAndAppendExpression(tr.TranslateLocation(e1), Builder().GetType(ifType), ifType, tr.GetCurrentBlock())->GetResult(); } auto topRes = TranslateCondition(*e->condExpr, true); if (topRes.tb) { CreateGoto(*trueBlock, *topRes.tb); } if (topRes.fb) { CreateGoto(*falseBlock, *topRes.fb); } tr.SetCurrentBlock(*endBlock); if (retVal) { auto ret = tr.GetDerefedValue(retVal, tr.TranslateLocation(e1)); if (forceGenerateUnit) { StaticCast(ret)->GetExpr()->Set(SkipKind::SKIP_DCE_WARNING); } return ret; } return nullptr; } ~TranslateIfExpr() override = default; protected: const Expr* GetExpr() override { return e; } SourceExpr GetSourceExpr() override { if (e->sugarKind == AST::Expr::SugarKind::FOR_IN_EXPR) { return SourceExpr::FOR_IN_EXPR; } return SourceExpr::IF_EXPR; } CHIR::Block* TranslateTrueBlock() override { CJC_ASSERT(!trueBlock); CJC_ASSERT(!endBlock); endBlock = CreateBlock(); tr.TranslateSubExprToLoc(*e->thenBody, retVal); trueBlock = GetBlockByAST(*e->thenBody); trueBlock->SetDebugLocation(tr.TranslateLocation(*e->thenBody)); if (e->TestAttr(AST::Attribute::IMPLICIT_ADD) && e->condExpr->TestAttr(AST::Attribute::COMPILER_ADD)) { if (auto cond = DynamicCast(&*e->condExpr); cond && cond->ty->IsBoolean() && cond->constNumValue.asBoolean) { // generated from for in desugar, condition is a constant true trueBlock->Set(SkipKind::SKIP_DCE_WARNING); } } CreateGoto(*endBlock, *tr.GetCurrentBlock())->Set(SkipKind::SKIP_DCE_WARNING); return trueBlock; } CHIR::Block* TranslateFalseBlock() override { CJC_ASSERT(!falseBlock); CJC_ASSERT(endBlock); if (e->elseBody) { auto loc = tr.TranslateLocation(*e); if (auto block = DynamicCast(e->elseBody.get())) { tr.TranslateSubExprToLoc(*block, retVal); falseBlock = GetBlockByAST(*block); } else { falseBlock = CreateBlock(); tr.SetCurrentBlock(*falseBlock); tr.TranslateSubExprToLoc(*e->elseBody, retVal); } falseBlock->SetDebugLocation(tr.TranslateLocation(*e->elseBody)); CreateGoto(*endBlock, *tr.GetCurrentBlock())->Set(SkipKind::SKIP_DCE_WARNING); } else { falseBlock = endBlock; } return falseBlock; } ///@{ /// Encapsulation of Translator methods. Branch* CreateBranch(Value& val, CHIR::Block& cur, CHIR::Block& tb, CHIR::Block& fb) override { auto res = tr.CreateAndAppendTerminator(&val, &tb, &fb, &cur); res->SetSourceExpr(GetSourceExpr()); return res; } ///@} }; Ptr Translator::Visit(const AST::IfExpr& ifExpr) { CJC_NULLPTR_CHECK(ifExpr.thenBody); TranslateIfExpr impl{*this}; return impl.Translate(ifExpr); } class TranslateWhileExpr final : public TranslateCondCtrlExpr { public: ~TranslateWhileExpr() override = default; explicit TranslateWhileExpr(Translator& trs) : TranslateCondCtrlExpr{trs} { } Ptr Translate(const WhileExpr& e1) { e = &e1; TranslateConditionBlock(); std::optional context; if (!e1.TestAttr(AST::Attribute::IMPLICIT_ADD)) { context.emplace(tr); context->ScopePlus(); } endBlock = CreateBlock(); // used for jump endBlock->SetDebugLocation(tr.TranslateLocation(e1)); endBlock->Set(SkipCheck{SkipKind::SKIP_DCE_WARNING}); auto topRes = TranslateCondition(*e->condExpr, true); if (topRes.tb) { CreateGoto(*bodyBlock, *topRes.tb); } if (topRes.fb) { CreateGoto(*endBlock, *topRes.fb); } tr.SetCurrentBlock(*endBlock); return nullptr; } protected: void TranslateConditionBlock() { conditionBlock = CreateBlock(); if (e->sugarKind == AST::Expr::SugarKind::FOR_IN_EXPR) { conditionBlock->Set(SkipKind::SKIP_DCE_WARNING); } CreateGoto(*conditionBlock, *tr.GetCurrentBlock()); tr.SetCurrentBlock(*conditionBlock); } CHIR::Block* TranslateTrueBlock() override { CJC_ASSERT(!bodyBlock); std::pair bls{conditionBlock, endBlock}; tr.terminatorSymbolTable.Set(*e, bls); { std::optional context; if (!e->TestAttr(AST::Attribute::IMPLICIT_ADD)) { context.emplace(tr); context->ScopePlus(); } tr.TranslateSubExprToDiscarded(*e->body); // value of while body is always unit bodyBlock = tr.GetBlockByAST(*e->body); bodyBlock->SetDebugLocation(tr.TranslateLocation(e->body->begin, e->body->end)); } auto bodyBlockEnd = tr.GetCurrentBlock(); auto br = CreateGoto(*conditionBlock, *bodyBlockEnd); br->SetDebugLocation(tr.TranslateLocation(*e->condExpr)); br->Set(SkipKind::SKIP_DCE_WARNING); tr.terminatorSymbolTable.Erase(*e); return bodyBlock; } Ptr conditionBlock{}; Ptr bodyBlock{}; Ptr e; Block* TranslateFalseBlock() override { return endBlock; } const Expr* GetExpr() override { return e; } SourceExpr GetSourceExpr() override { if (e->sugarKind == AST::Expr::SugarKind::FOR_IN_EXPR) { return SourceExpr::FOR_IN_EXPR; } return SourceExpr::WHILE_EXPR; } }; Ptr Translator::Visit(const AST::WhileExpr& whileExpr) { TranslateWhileExpr impl{*this}; return impl.Translate(whileExpr); } } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateInterfaceDecl.cpp000066400000000000000000000011631510705540100276700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" using namespace Cangjie::CHIR; using namespace Cangjie; Ptr Translator::Visit(const AST::InterfaceDecl& decl) { ClassDef* classDef = StaticCast(GetNominalSymbolTable(decl).get()); CJC_NULLPTR_CHECK(classDef); TranslateClassLikeDecl(*classDef, decl); return nullptr; }cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateJumpExpr.cpp000066400000000000000000000071341510705540100267560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" using namespace Cangjie::CHIR; using namespace Cangjie; int64_t Translator::CalculateDelayExitLevelForBreak() { int64_t level = 0; for (auto reverseBegin = blockGroupStack.crbegin(), reverseEnd = blockGroupStack.crend(); reverseBegin != reverseEnd; ++reverseBegin) { Ptr bg = *reverseBegin; Expression* expr = bg->GetOwnerExpression(); if (expr && Is(expr)) { ++level; break; } if (expr && expr->GetExprKind() == ExprKind::IF) { ++level; } } return level; } int64_t Translator::CalculateDelayExitLevelForContinue() { return CalculateDelayExitLevelForBreak() - 1; } void Translator::UpdateDelayExitSignal(int64_t level) { auto constDepth = CreateAndAppendConstantExpression(builder.GetInt64Ty(), *currentBlock, static_cast(level))->GetResult(); CreateWrappedStore(constDepth, delayExitSignal, currentBlock); } Ptr Translator::Visit(const AST::JumpExpr& jumpExpr) { const auto& loc = TranslateLocation(jumpExpr); Ptr terminator = nullptr; DebugLocation loopLocInfo; // If there are other blockGroup exprs, please modify the condition here. // for example: WHILE, LOOP... if (jumpExpr.refLoop->astKind == AST::ASTKind::FOR_IN_EXPR && delayExitSignal) { loopLocInfo = *forInExprAST2DebugLocMap[jumpExpr.refLoop]; } else { const auto [conditionBlock, falseBlock] = *terminatorSymbolTable.Get(*jumpExpr.refLoop); loopLocInfo = falseBlock->GetDebugLocation(); } auto scopeLevel = loopLocInfo.GetScopeInfo().size(); if (!finallyContext.empty()) { // When current is in try-finally context, and control flow's scope level is outside(smaller) the // try-finally, store the control blocks with control kind value. auto& [finallyBlock, controlBlocks] = finallyContext.top(); auto tryScopeLevel = finallyBlock->GetDebugLocation().GetScopeInfo().size(); if (scopeLevel < tryScopeLevel) { auto controlIndex = static_cast(jumpExpr.isBreak ? ControlType::BREAK : ControlType::CONTINUE); auto prevBlock = currentBlock; currentBlock = CreateBlock(); currentBlock->SetDebugLocation(loopLocInfo); // the pair of blocks is {the block before control flow, control flow's target block}. controlBlocks[controlIndex].emplace_back(prevBlock, currentBlock); } } if (jumpExpr.refLoop->astKind == AST::ASTKind::FOR_IN_EXPR && delayExitSignal) { if (jumpExpr.isBreak) { UpdateDelayExitSignal(CalculateDelayExitLevelForBreak()); } else { UpdateDelayExitSignal(CalculateDelayExitLevelForContinue()); } terminator = CreateAndAppendTerminator(loc, currentBlock); } else { const auto [conditionBlock, falseBlock] = *terminatorSymbolTable.Get(*jumpExpr.refLoop); if (jumpExpr.isBreak) { terminator = CreateAndAppendTerminator(loc, falseBlock, currentBlock); } else { terminator = CreateAndAppendTerminator(loc, conditionBlock, currentBlock); } } currentBlock = CreateBlock(); maybeUnreachable.emplace(currentBlock, terminator); return nullptr; }cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateLambdaExpr.cpp000066400000000000000000000123031510705540100272150ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/AST/Node.h" #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/CHIR/Package.h" using namespace Cangjie::CHIR; using namespace Cangjie; Ptr Translator::Visit(const AST::LambdaExpr& lambdaExpr) { CJC_ASSERT(lambdaExpr.funcBody && lambdaExpr.funcBody->body); CJC_ASSERT(!lambdaExpr.mangledName.empty()); auto lambdaTrans = SetupContextForLambda(*lambdaExpr.funcBody->body); auto funcTy = RawStaticCast(TranslateType(*lambdaExpr.ty)); // Create lambda body and parameters. CJC_ASSERT(currentBlock->GetTopLevelFunc()); BlockGroup* body = builder.CreateBlockGroup(*currentBlock->GetTopLevelFunc()); const auto& loc = TranslateLocation(lambdaExpr); auto mangledName = lambdaExpr.mangledName; if (funcTy->IsCFunc()) { mangledName += CFFI_FUNC_SUFFIX; } // cjdb need src code name to show the stack, or core dump will occurred in some case auto lambda = CreateAndAppendExpression(loc, funcTy, funcTy, currentBlock, true, mangledName, "$lambda"); lambda->InitBody(*body); std::vector paramLoc; for (auto& astParam : lambdaExpr.funcBody->paramLists[0]->params) { paramLoc.emplace_back(TranslateLocationWithoutScope(builder.GetChirContext(), astParam->begin, astParam->end)); } auto paramTypes = funcTy->GetParamTypes(); CJC_ASSERT(paramTypes.size() == paramLoc.size()); for (size_t i = 0; i < paramTypes.size(); ++i) { builder.CreateParameter(paramTypes[i], paramLoc[i], *lambda); } if (auto lambdaBody = lambda->GetBody(); lambdaBody && lambdaExpr.TestAttr(AST::Attribute::MOCK_SUPPORTED)) { lambdaBody->EnableAttr(CHIR::Attribute::NO_INLINE); } // lambda never has default parameter value return lambdaTrans.TranslateLambdaBody(lambda, *lambdaExpr.funcBody, {}); } Translator Translator::Copy() const { return {builder, chirTy, opts, gim, globalSymbolTable, localConstVars, localConstFuncs, increKind, deserializedVals, annoFactoryFuncs, maybeUnreachable, isComputingAnnos, initFuncsForAnnoFactory, typeManager}; } Translator Translator::SetupContextForLambda(const AST::Block& body) { // Copy local symbols, and update symbol for let decl which needs deref before used in lambda. Translator trans = Copy(); // Collect local variables which is captured by current funcBody. std::unordered_set> usedCapturedDecls; AST::ConstWalker(&body, [&usedCapturedDecls](auto node) { if (auto target = node->GetTarget(); (Is(target) && target->TestAttr(AST::Attribute::IS_CAPTURE)) || Is(target)) { usedCapturedDecls.emplace(target); } return AST::VisitAction::WALK_CHILDREN; }).Walk(); std::vector> capturedSymbol; for (auto [node, symbol] : localValSymbolTable.GetALL()) { if (node->astKind != AST::ASTKind::VAR_DECL) { trans.SetSymbolTable(*node, *symbol); continue; } auto vd = DynamicCast(node); if (!vd->TestAttr(AST::Attribute::IS_CAPTURE) || vd->isVar) { trans.SetSymbolTable(*node, *symbol); continue; } // Ignore local variables which is not used in current funcBody. if (usedCapturedDecls.count(node) == 0) { continue; } capturedSymbol.emplace_back(node, symbol); } std::sort(capturedSymbol.begin(), capturedSymbol.end(), [](auto& p1, auto& p2) { return AST::CompNodeByPos(p1.first, p2.first); }); for (auto [node, symbol] : capturedSymbol) { trans.SetSymbolTable(*node, *GetDerefedValue(symbol)); } // Copy block group status and current block for new lambda translator. trans.blockGroupStack = blockGroupStack; trans.currentBlock = currentBlock; // Copy 'exprValueTable' for desugared mapping expressions' value. trans.exprValueTable = exprValueTable; return trans; } Ptr Translator::TranslateLambdaBody( Ptr lambda, const AST::FuncBody& funcBody, const BindingConfig& config) { // NOTE: This method must be called with new translator. auto blockGroup = lambda->GetBody(); blockGroupStack.emplace_back(blockGroup); auto entry = builder.CreateBlock(blockGroup); blockGroup->SetEntryBlock(entry); BindingFuncParam(*funcBody.paramLists[0], *lambda->GetBody(), config); // Set return value. auto retType = lambda->GetReturnType(); auto retVal = CreateAndAppendExpression(DebugLocation(), builder.GetType(retType), retType, entry) ->GetResult(); lambda->SetReturnValue(*retVal); // Translate body. auto block = Visit(funcBody); CreateAndAppendTerminator(StaticCast(block.get()), entry); blockGroupStack.pop_back(); return lambda->GetResult(); } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateLitConstExpr.cpp000066400000000000000000000126201510705540100275760ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" using namespace Cangjie::CHIR; using namespace Cangjie; Ptr Translator::TranslateLitConstant(const AST::LitConstExpr& expr, AST::Ty& realTy) { auto chirTyToTrans = TranslateType(realTy); switch (realTy.kind) { case AST::TypeKind::TYPE_FLOAT16: case AST::TypeKind::TYPE_FLOAT64: case AST::TypeKind::TYPE_IDEAL_FLOAT: { auto value = static_cast(expr.constNumValue.asFloat.value); return builder.CreateLiteralValue(chirTyToTrans, value); } case AST::TypeKind::TYPE_FLOAT32: { auto stringVal = expr.stringValue; stringVal.erase(std::remove(stringVal.begin(), stringVal.end(), '_'), stringVal.end()); double value = static_cast(strtold(stringVal.c_str(), nullptr)); return builder.CreateLiteralValue(chirTyToTrans, value); } case AST::TypeKind::TYPE_UINT8: case AST::TypeKind::TYPE_UINT16: case AST::TypeKind::TYPE_UINT32: case AST::TypeKind::TYPE_UINT64: case AST::TypeKind::TYPE_UINT_NATIVE: { return builder.CreateLiteralValue(chirTyToTrans, expr.constNumValue.asInt.Uint64()); } case AST::TypeKind::TYPE_INT8: case AST::TypeKind::TYPE_INT16: case AST::TypeKind::TYPE_INT32: case AST::TypeKind::TYPE_INT64: case AST::TypeKind::TYPE_INT_NATIVE: case AST::TypeKind::TYPE_IDEAL_INT: { return builder.CreateLiteralValue( chirTyToTrans, static_cast(expr.constNumValue.asInt.Int64())); } case AST::TypeKind::TYPE_RUNE: { return builder.CreateLiteralValue(chirTyToTrans, expr.codepoint[0]); } case AST::TypeKind::TYPE_BOOLEAN: { return builder.CreateLiteralValue(chirTyToTrans, expr.stringValue == "true"); } case AST::TypeKind::TYPE_STRUCT: { CJC_ASSERT(expr.kind == AST::LitConstKind::STRING); return builder.CreateLiteralValue(chirTyToTrans, expr.stringValue); } // Unit literal is handled in TranslateExprArg etc. functions case AST::TypeKind::TYPE_UNIT: default: { CJC_ABORT(); return nullptr; } } } Ptr Translator::TranslateLitConstant(const AST::LitConstExpr& expr, AST::Ty& realTy, Ptr block) { auto loc = TranslateLocation(expr); auto chirTyToTrans = TranslateType(realTy); switch (realTy.kind) { case AST::TypeKind::TYPE_UNIT: { return nullptr; } case AST::TypeKind::TYPE_FLOAT16: case AST::TypeKind::TYPE_FLOAT64: case AST::TypeKind::TYPE_IDEAL_FLOAT: { auto value = static_cast(expr.constNumValue.asFloat.value); return CreateAndAppendConstantExpression(loc, chirTyToTrans, *block, value); } case AST::TypeKind::TYPE_FLOAT32: { auto stringVal = expr.stringValue; stringVal.erase(std::remove(stringVal.begin(), stringVal.end(), '_'), stringVal.end()); double value = static_cast(strtold(stringVal.c_str(), nullptr)); return CreateAndAppendConstantExpression(loc, chirTyToTrans, *block, value); } case AST::TypeKind::TYPE_UINT8: case AST::TypeKind::TYPE_UINT16: case AST::TypeKind::TYPE_UINT32: case AST::TypeKind::TYPE_UINT64: case AST::TypeKind::TYPE_UINT_NATIVE: { uint64_t value = expr.constNumValue.asInt.Uint64(); return CreateAndAppendConstantExpression(loc, chirTyToTrans, *block, value); } case AST::TypeKind::TYPE_INT8: case AST::TypeKind::TYPE_INT16: case AST::TypeKind::TYPE_INT32: case AST::TypeKind::TYPE_INT64: case AST::TypeKind::TYPE_INT_NATIVE: case AST::TypeKind::TYPE_IDEAL_INT: { int64_t value = expr.constNumValue.asInt.Int64(); return CreateAndAppendConstantExpression( loc, chirTyToTrans, *block, static_cast(value)); } case AST::TypeKind::TYPE_RUNE: { auto value = expr.codepoint[0]; return CreateAndAppendConstantExpression(loc, chirTyToTrans, *block, value); } case AST::TypeKind::TYPE_BOOLEAN: { auto value = expr.stringValue == "true"; return CreateAndAppendConstantExpression(loc, chirTyToTrans, *block, value); } case AST::TypeKind::TYPE_STRUCT: { CJC_ASSERT(expr.kind == AST::LitConstKind::STRING); auto value = expr.stringValue; return CreateAndAppendConstantExpression(loc, chirTyToTrans, *block, value); } default: { CJC_ABORT(); return nullptr; } } } Ptr Translator::Visit(const AST::LitConstExpr& expr) { Constant* c = TranslateLitConstant(expr, *expr.ty, currentBlock); if (!c) { return nullptr; } return c->GetResult(); } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateMatchExpr.cpp000066400000000000000000001316011510705540100270740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" using namespace Cangjie::CHIR; using namespace Cangjie; bool Translator::CanOptimizeMatchToSwitch(const AST::MatchExpr& matchExpr) { // The enum pattern and const pattern which match the rules below can be optimized using switch node: // 1. no pattern guard // 2. no varBindingPattern // 3. more than one case and first case is not wildCard pattern. // 4. For enum pattern, enum constructor with param that only support FIRST param is type of Int, Uint, Rune. // NOTE: it will generate up to one level of nested switch table. // NOTE: enum type is not std.core's Option. // 5. For const pattern, UInt64, UInt32, UInt16, UInt8, Int64, Int32, Int16, Int8 , Rune are supported. if (!opts.IsOptimizationExisted(FrontendOptions::OptimizationFlag::SWITCH_OPT)) { return false; } if (matchExpr.matchCases.size() <= 1 || matchExpr.matchCases[0]->patterns[0]->astKind == AST::ASTKind::WILDCARD_PATTERN) { return false; } const auto& type = matchExpr.selector->ty; bool validSelectorTy = IsOptimizableTy(type) || (type->IsEnum() && IsOptimizableEnumTy(type)); if (!validSelectorTy) { return false; } for (auto& curCase : matchExpr.matchCases) { // Rule1. no pattern guard if (curCase->patternGuard) { return false; } // Beware: case cond can have |. for example, case x1|x2: for (auto& curPattern : curCase->patterns) { // Any wildcard pattern will render cases following it unreachable. if (curPattern->astKind == AST::ASTKind::WILDCARD_PATTERN) { return true; } else if (curPattern->astKind == AST::ASTKind::CONST_PATTERN) { continue; } auto vep = DynamicCast(curPattern.get()); auto realPatternKind = vep ? vep->pattern->astKind : curPattern->astKind; if (realPatternKind != AST::ASTKind::ENUM_PATTERN) { return false; } else if (vep) { // When pattern is 'VarOrEnumPattern' which desugared to enum pattern, it must not have param. continue; } auto enumPattern = StaticCast(curPattern.get()); if (enumPattern->patterns.empty()) { // enum patterns without params do not break any rule continue; } auto firstParamKind = enumPattern->patterns[0]->astKind; // Since types of all enum constructor have been checked, the type of constant pattern must be valid. if (firstParamKind == AST::ASTKind::WILDCARD_PATTERN || firstParamKind == AST::ASTKind::CONST_PATTERN) { continue; } return false; } } return true; } namespace { void PrintDebugMessage(bool isDebug, const AST::Node& node) { if (!isDebug) { return; } std::string fileStr = " file: " + (node.curFile ? node.curFile->fileName : std::to_string(node.begin.fileID)); std::string message = "The matchExpr in the" + fileStr + " line:" + std::to_string(node.begin.line) + " and the column:" + std::to_string(node.begin.column) + " was optimized by The opt: switchOpt"; std::cout << message << std::endl; } /** * Type of match expression may be type of expected target, * we need to check all branch for real nothing typed control flow. */ inline bool HasTypeOfNothing(const AST::MatchExpr& matchExpr) { if (matchExpr.matchMode) { return std::all_of( matchExpr.matchCases.cbegin(), matchExpr.matchCases.cend(), [](auto& it) { return it->ty->IsNothing(); }); } else { return std::all_of(matchExpr.matchCaseOthers.cbegin(), matchExpr.matchCaseOthers.cend(), [](auto& it) { return it->ty->IsNothing(); }); } } inline SourceExpr GetSourceExprByMatchExpr(const AST::MatchExpr& matchExpr) { if (matchExpr.sugarKind == AST::Expr::SugarKind::QUEST) { return SourceExpr::QUEST; } return SourceExpr::MATCH_EXPR; } } // namespace Ptr Translator::Visit(const AST::MatchExpr& matchExpr) { auto matchTy = TranslateType(*matchExpr.ty); Ptr retVal = HasTypeOfNothing(matchExpr) || matchTy->IsUnit() || matchTy->IsNothing() ? nullptr : CreateAndAppendExpression(builder.GetType(matchTy), matchTy, currentBlock)->GetResult(); if (matchExpr.matchMode) { if (CanOptimizeMatchToSwitch(matchExpr)) { PrintDebugMessage(opts.chirDebugOptimizer, matchExpr); TranslateMatchAsTable(matchExpr, retVal); } else { TranslateMatchWithSelector(matchExpr, retVal); } } else { TranslateConditionMatches(matchExpr, retVal); } // When match expr have type of 'Unit' or 'Nothing', the alloca can be ignored, directly return literal. // And all store to 'retVal' alloca is ignored. if (!retVal) { return nullptr; } else { return CreateAndAppendExpression(matchTy, retVal, currentBlock)->GetResult(); } } void Translator::TranslateMatchWithSelector(const AST::MatchExpr& matchExpr, Ptr retVal) { auto selectorVal = TranslateExprArg(*matchExpr.selector); SetSkipPrintWarning(selectorVal); auto endBlock = CreateBlock(); size_t caseNum = matchExpr.matchCases.size(); bool needScope = matchExpr.sugarKind != AST::Expr::SugarKind::IS && matchExpr.sugarKind != AST::Expr::SugarKind::AS; std::optional context; if (needScope) { context.emplace(*this); } const auto& originLoc = TranslateLocation(matchExpr); SourceExpr sourceExpr = GetSourceExprByMatchExpr(matchExpr); for (size_t i = 0; i < caseNum; ++i) { if (needScope) { context->ScopePlus(); } auto current = matchExpr.matchCases[i].get(); // Generate case pattern condition and set var pattern symbol. auto [falseBlock, trueBlock] = current->patterns.size() == 1 ? TranslateNestingCasePattern(*current->patterns[0], selectorVal, originLoc, sourceExpr) : TranslateOrPattern(current->patterns, selectorVal, originLoc); TranslatePatternGuard(*current, falseBlock, trueBlock); if (sourceExpr == SourceExpr::QUEST && i == 0) { CJC_ASSERT(caseNum == 2); // 2 denote than QUEST has two cases. if (!matchExpr.matchCases[1]->exprOrDecls->body.empty()) { const auto& loc1 = TranslateLocation(matchExpr.matchCases[1]->exprOrDecls->body.front()->begin, matchExpr.matchCases[1]->exprOrDecls->body.back()->end); falseBlock->SetDebugLocation(loc1); } CJC_ASSERT(trueBlock->GetPredecessors().size() == 1); const auto& loc2 = TranslateLocation(*matchExpr.matchCases[0]); trueBlock->GetPredecessors()[0]->SetDebugLocation(loc2); } auto currentBody = TranslateMatchCaseBody(*current->exprOrDecls, retVal, endBlock); CreateAndAppendTerminator(currentBody, trueBlock); if (i == caseNum - 1) { CreateAndAppendTerminator(endBlock, falseBlock); // Last falseBlocks for all cases is unreachable, marking for later analysis. falseBlock->EnableAttr(Attribute::UNREACHABLE); } else { currentBlock = falseBlock; } } // Update block at the end. currentBlock = endBlock; } /** * Translate pattern guard if existed, and update @p 'trueBlock' . */ void Translator::TranslatePatternGuard(const AST::MatchCase& matchCase, Ptr falseBlock, Ptr& trueBlock) { if (!matchCase.patternGuard) { return; } currentBlock = trueBlock; auto newTrueBlock = CreateBlock(); auto cond = TranslateExprArg(*matchCase.patternGuard); CreateAndAppendTerminator(cond, newTrueBlock, falseBlock, currentBlock); trueBlock = newTrueBlock; } Block* Translator::TranslateMatchCaseBody( const AST::Block& caseBody, Ptr retVal, Ptr endBlock) { auto loc = caseBody.rightCurlPos.IsZero() ? caseBody.body.empty() ? TranslateLocation(caseBody.end, caseBody.end) : TranslateLocation(*caseBody.body.back()) : TranslateLocation(caseBody); TranslateSubExprToLoc(caseBody, retVal, loc); auto currentBody = GetBlockByAST(caseBody); CreateAndAppendTerminator(endBlock, currentBlock); // Return the free block for connecting control flow at outside. return currentBody; } namespace { uint64_t GetConstPatternVal(const AST::ConstPattern& constPattern) { auto litExpr = StaticCast(constPattern.literal.get()); auto ty = litExpr->ty; uint64_t curVal = 0; if (ty->IsSignedInteger()) { // Support const pattern, include Int64, Int32, Int16, Int8 curVal = static_cast(litExpr->constNumValue.asInt.Int64()); } else if (ty->IsUnsignedInteger()) { // Support const pattern, include UInt64, UInt32, UInt16, UInt8 curVal = litExpr->constNumValue.asInt.Uint64(); } else if (ty->IsRune()) { curVal = static_cast(litExpr->codepoint[0]); } else if (ty->IsBoolean()) { curVal = static_cast(litExpr->constNumValue.asBoolean); } else { // 'String' and floating type should not enter this function. InternalError("unsupported const pattern type: ", ty->String()); } return curVal; } const AST::EnumPattern& GetRealEnumPattern(const AST::Pattern& pattern) { CJC_ASSERT(pattern.astKind == AST::ASTKind::VAR_OR_ENUM_PATTERN || pattern.astKind == AST::ASTKind::ENUM_PATTERN); if (auto vep = DynamicCast(&pattern)) { return StaticCast(*vep->pattern); } else { return StaticCast(pattern); } } bool IsOptimizableEnumPatterns(const std::vector>& patterns) { for (auto& it : patterns) { CJC_ASSERT(it->astKind == AST::ASTKind::VAR_OR_ENUM_PATTERN || it->astKind == AST::ASTKind::ENUM_PATTERN); auto ep = DynamicCast(it.get()); // When enum sub pattern contains any of non-wildcard pattern, current or-pattern cannot be optimized. if (ep && std::any_of(ep->patterns.cbegin(), ep->patterns.cend(), [](auto& sub) { return sub->astKind != AST::ASTKind::WILDCARD_PATTERN; })) { return false; } } return true; } } // namespace uint64_t Translator::GetEnumPatternID(const AST::EnumPattern& enumPattern) { auto target = enumPattern.constructor->GetTarget(); return GetEnumCtorId(*target); } uint64_t Translator::GetEnumCtorId(const AST::Decl& target) { CJC_ASSERT(target.outerDecl && target.outerDecl->astKind == AST::ASTKind::ENUM_DECL); auto enumDecl = StaticCast(target.outerDecl); for (uint64_t i = 0; i < enumDecl->constructors.size(); ++i) { if (enumDecl->constructors[i].get() == &target) { return i; } } CJC_ABORT(); // Target must existed inside enum constructors. return 0; } // Sub-pattern of or-pattern must not introduce new variable. std::pair, Ptr> Translator::TranslateOrPattern( const std::vector>& patterns, Ptr selectorVal, const DebugLocation& originLoc) { CJC_ASSERT(patterns.size() > 1); // Or-pattern can only connect same type of patterns without nesting var pattern. auto patternKind = patterns[0]->astKind; if (patternKind == AST::ASTKind::WILDCARD_PATTERN) { auto trueBlock = CreateBlock(); CreateAndAppendTerminator(trueBlock, currentBlock); return {CreateBlock(), trueBlock}; } else if (patternKind == AST::ASTKind::CONST_PATTERN) { if (patterns[0]->ty->IsString() || patterns[0]->ty->IsFloating()) { return TranslateComplicatedOrPattern(patterns, selectorVal, originLoc); } else if (patterns[0]->ty->IsUnit()) { // unit literal is always match auto trueBlock = CreateBlock(); CreateAndAppendTerminator(trueBlock, currentBlock); return {CreateBlock(), trueBlock}; } std::set values; // Switch case cannot be duplicated (llvm ir restriction). for (auto& it : patterns) { values.emplace(GetConstPatternVal(StaticCast(*it))); } return TranslateConstantMultiOr(Utils::SetToVec(values), selectorVal); } else if (patternKind == AST::ASTKind::TUPLE_PATTERN || patternKind == AST::ASTKind::TYPE_PATTERN) { return TranslateComplicatedOrPattern(patterns, selectorVal, originLoc); } CJC_ASSERT(patternKind == AST::ASTKind::VAR_OR_ENUM_PATTERN || patternKind == AST::ASTKind::ENUM_PATTERN); if (IsOptimizableEnumPatterns(patterns)) { std::set values; for (auto& it : patterns) { values.emplace(GetEnumPatternID(GetRealEnumPattern(*it))); } return TranslateConstantMultiOr( Utils::SetToVec(values), GetEnumIDValue(patterns[0]->ty, selectorVal)); } return TranslateComplicatedOrPattern(patterns, selectorVal, originLoc); } Ptr Translator::GetEnumIDValue(Ptr ty, Ptr selectorVal) { CJC_ASSERT(ty && ty->kind == AST::TypeKind::TYPE_ENUM); auto enumDecl = StaticCast(AST::Ty::GetDeclOfTy(ty)); auto selectorType = GetSelectorType(StaticCast(*ty)); if (!enumDecl->hasArguments) { return CreateWrappedTypeCast(selectorType, GetDerefedValue(selectorVal), currentBlock)->GetResult(); } return CreateAndAppendExpression( selectorType, selectorVal, std::vector{0}, currentBlock)->GetResult(); } std::pair, Ptr> Translator::TranslateConstantMultiOr( const std::vector values, Ptr value) { auto falseBlock = CreateBlock(); auto trueBlock = CreateBlock(); std::vector succs(values.size(), trueBlock); CreateAndAppendTerminator( TypeCastOrBoxIfNeeded(*value, *builder.GetUInt64Ty(), value->GetDebugLocation(), false), falseBlock, values, succs, currentBlock); return {falseBlock, trueBlock}; } // Sub-pattern of or-pattern must not introduce new variable. std::pair, Ptr> Translator::TranslateComplicatedOrPattern( const std::vector>& patterns, const Ptr selectorVal, const DebugLocation& originLoc) { CJC_ASSERT(patterns.size() > 1); auto trueBlock = CreateBlock(); for (auto& it : patterns) { auto [innerFalse, innerTrue] = TranslateNestingCasePattern(*it, selectorVal, originLoc); CreateAndAppendTerminator(trueBlock, innerTrue); currentBlock = innerFalse; } return {currentBlock, trueBlock}; } // NOTE: 'blocks' is pair of {final matched block, next condition block} // 'enum pattern''s element value should be generated in 'next condition block'. // 'var pattern''s typecast should be generated in 'final matched block'. Ptr Translator::DispatchingPattern(std::queue, Ptr>>& queue, const std::pair, Ptr>& blocks, const DebugLocation& originLoc) { auto [pattern, value] = queue.front(); queue.pop(); Ptr cond = nullptr; switch (pattern->astKind) { case AST::ASTKind::VAR_OR_ENUM_PATTERN: queue.push(std::make_pair(StaticCast(pattern)->pattern.get(), value)); break; case AST::ASTKind::VAR_PATTERN: HandleVarPattern(*StaticCast(pattern), value, blocks.first); break; case AST::ASTKind::WILDCARD_PATTERN: break; case AST::ASTKind::TUPLE_PATTERN: CollectingSubPatterns(pattern->ty, StaticCast(pattern)->patterns, value, queue); break; case AST::ASTKind::ENUM_PATTERN: cond = HandleEnumPattern(*StaticCast(pattern), value, queue, blocks.second, originLoc); break; case AST::ASTKind::TYPE_PATTERN: cond = HandleTypePattern(*StaticCast(pattern), value, queue); break; case AST::ASTKind::CONST_PATTERN: cond = HandleConstPattern(*StaticCast(pattern), value, originLoc); break; default: { InternalError("translating unsupported pattern"); } } return cond; } std::pair, Ptr> Translator::TranslateNestingCasePattern(const AST::Pattern& pattern, const Ptr selectorVal, const DebugLocation& originLoc, const SourceExpr& sourceExpr) { auto falseBlock = CreateBlock(); auto trueBlock = CreateBlock(); std::queue, Ptr>> queue; queue.push(std::make_pair(&pattern, selectorVal)); auto newBlock = CreateBlock(); const auto& patternLoc = TranslateLocation(pattern); if (sourceExpr != SourceExpr::QUEST) { currentBlock->SetDebugLocation(patternLoc); } while (!queue.empty()) { // Typecast of varPattern must be generated at the real trueBlock. auto cond = DispatchingPattern(queue, {trueBlock, newBlock}, originLoc); if (cond) { auto nextBlock = queue.empty() ? trueBlock : newBlock; if (sourceExpr != SourceExpr::QUEST) { nextBlock->SetDebugLocation(patternLoc); } CreateWrappedBranch(sourceExpr, GetDerefedValue(cond), nextBlock, falseBlock, currentBlock); currentBlock = nextBlock; if (!queue.empty()) { newBlock = CreateBlock(); } } else if (queue.empty()) { if (sourceExpr != SourceExpr::QUEST) { currentBlock->SetDebugLocation(patternLoc); } CreateAndAppendTerminator(trueBlock, currentBlock); } } newBlock->RemoveSelfFromBlockGroup(); return {falseBlock, trueBlock}; } void Translator::CollectingSubPatterns(const Ptr& patternTy, const std::vector>& patterns, const Ptr value, std::queue, Ptr>>& queue, unsigned offset) { if (patterns.empty()) { return; } // Expand patterns and push to queue. std::vector indexes; auto funcTy = DynamicCast(patternTy); // NOTE: When expected pattern type is type of function, current is destructing Enum // that we can only load first level field here. bool isEnumField = funcTy != nullptr; using CollectFnType = std::function, Ptr)>; CollectFnType collectRecursively = [this, &indexes, &queue, &collectRecursively, &value, isEnumField]( auto pattern, auto ty) { if (pattern->astKind == AST::ASTKind::WILDCARD_PATTERN) { // When sub-pattern is wildcard pattern, the 'value' will be ignored. queue.push(std::make_pair(pattern, value)); return; } auto tp = DynamicCast(pattern); if (!tp || isEnumField) { auto elementTy = TranslateType(*ty); auto elementVal = CreateAndAppendExpression(elementTy, value, indexes, currentBlock)->GetResult(); queue.push(std::make_pair(pattern, elementVal)); return; } for (size_t i = 0; i < tp->patterns.size(); i++) { indexes.emplace_back(i); collectRecursively(tp->patterns[i].get(), tp->ty->typeArgs[i]); indexes.pop_back(); } }; const auto& typeArgs = funcTy ? funcTy->paramTys : patternTy->typeArgs; CJC_ASSERT(typeArgs.size() == patterns.size()); for (size_t i = 0; i < patterns.size(); i++) { // First accessing should adding the offset. indexes.emplace_back(i + offset); collectRecursively(patterns[i].get(), typeArgs[i]); indexes.pop_back(); } } void Translator::HandleVarPattern( const AST::VarPattern& varPattern, const Ptr value, const Ptr& trueBlock) { auto backupBlock = currentBlock; // NOTE: typecast should be generated in the 'matched' block. currentBlock = trueBlock; auto val = varPattern.desugarExpr ? GetDerefedValue(value, TranslateLocation(varPattern)) : value; auto varType = TranslateType(*varPattern.ty); const auto& loc = TranslateLocation(varPattern); if (opts.enableCompileDebug || opts.enableCoverage) { // When debug is enabled, the debug info of variable decl must be assigned with an alloca reference. // NOTE: debug should be generated inside 'trueBlock'. auto alloca = CreateAndAppendExpression(builder.GetType(varType), varType, trueBlock)->GetResult(); // NOTE: scope info of debug location for var pattern in enum switch case should following match case body, // should not use current scope info. if (!varPattern.varDecl->TestAttr(AST::Attribute::COMPILER_ADD)) { CreateAndAppendExpression( loc, builder.GetUnitTy(), alloca, varPattern.varDecl->identifier, trueBlock); } CreateAndAppendExpression( loc, builder.GetUnitTy(), TypeCastOrBoxIfNeeded(*val, *varType, loc, false), alloca, trueBlock); val = CreateAndAppendExpression(varType, alloca, trueBlock)->GetResult(); } else { val = TypeCastOrBoxIfNeeded(*val, *varType, loc); } val->EnableAttr(Attribute::READONLY); currentBlock = backupBlock; if (varPattern.desugarExpr) { // Mapping 'desugarExpr' for boxing/unboxing case. // The 'value' will be alloca of expected type for unboxing and real type for boxing case. exprValueTable.Set(*varPattern.desugarExpr, *val); } else { SetSymbolTable(*varPattern.varDecl, *val); } } Ptr Translator::CastEnumValueToConstructorTupleType(Ptr enumValue, const AST::EnumPattern& enumPattern) { auto target = enumPattern.constructor->GetTarget(); CJC_ASSERT(target->outerDecl && target->outerDecl->astKind == AST::ASTKind::ENUM_DECL); std::vector resTypes = { GetSelectorType(StaticCast(*target->outerDecl->ty)) }; std::vector> paramTys; if (auto funcTy = DynamicCast(enumPattern.constructor->ty)) { paramTys = funcTy->paramTys; } else { paramTys = enumPattern.constructor->ty->typeArgs; } for (auto ty : paramTys) { resTypes.emplace_back(TranslateType(*ty)); } auto res = TypeCastOrBoxIfNeeded(*enumValue, *builder.GetType(resTypes), enumValue->GetDebugLocation()); auto enumId = GetEnumPatternID(enumPattern); res->Set(enumId); return res; } Ptr Translator::HandleEnumPattern(const AST::EnumPattern& enumPattern, Ptr value, std::queue, Ptr>>& queue, const Ptr& trueBlock, const DebugLocation& originLoc) { auto enumIdx = GetEnumIDValue(enumPattern.ty, value); auto selectorTy = GetSelectorType(StaticCast(*enumPattern.ty)); auto enumId = GetEnumPatternID(enumPattern); auto constExpr = (selectorTy->IsBoolean() ? CreateAndAppendConstantExpression( selectorTy, *currentBlock, static_cast(enumId)) : CreateAndAppendConstantExpression(selectorTy, *currentBlock, enumId)); LocalVar* idValue = constExpr->GetResult(); auto cond = CreateAndAppendExpression( originLoc, builder.GetBoolTy(), ExprKind::EQUAL, enumIdx, idValue, currentBlock) ->GetResult(); CJC_ASSERT(value->GetType()->IsEnum()); // Set current to 'trueBlock' for generating enum element. auto backBlock = currentBlock; currentBlock = trueBlock; if (!enumPattern.patterns.empty()) { value = CastEnumValueToConstructorTupleType(value, enumPattern); } // Enum pattern's field has offset '1' that index 0 is the id of the enum constructor. CollectingSubPatterns(enumPattern.constructor->ty, enumPattern.patterns, value, queue, 1); currentBlock = backBlock; return cond; } Type* Translator::GetSelectorType(const AST::EnumTy& ty) const { auto kind = Cangjie::CHIR::GetSelectorType(*ty.decl); return builder.GetChirContext().ToSelectorType(kind); } Ptr Translator::HandleConstPattern( const AST::ConstPattern& constPattern, Ptr value, const DebugLocation& originLoc) { if (constPattern.operatorCallExpr == nullptr) { auto litVal = TranslateExprArg(*constPattern.literal); SetSkipPrintWarning(litVal); return CreateAndAppendExpression( originLoc, builder.GetBoolTy(), ExprKind::EQUAL, value, litVal, currentBlock) ->GetResult(); } else { // AST desugared the base of 'operatorCallExpr' as a special dummy node. // So we need to set the value of 'base' here to avoid translating invalid base of call. auto base = StaticCast( StaticCast(constPattern.operatorCallExpr.get())->baseFunc.get()) ->baseExpr.get(); exprValueTable.Set(*base, *value); return TranslateExprArg(*constPattern.operatorCallExpr); } } Ptr Translator::HandleTypePattern(const AST::TypePattern& typePattern, Ptr value, std::queue, Ptr>>& queue) { if (typePattern.matchBeforeRuntime) { if (typePattern.desugarExpr) { // Upcast boxing case. CJC_NULLPTR_CHECK(typePattern.desugarVarPattern); SetSymbolTable(*typePattern.desugarVarPattern->varDecl, *value); auto newVal = TranslateExprArg(*typePattern.desugarExpr); queue.push(std::make_pair(typePattern.pattern.get(), newVal)); } else if (typePattern.pattern->astKind == AST::ASTKind::VAR_PATTERN) { queue.push(std::make_pair(typePattern.pattern.get(), value)); } // If sub pattern is varPattern, the 'value' can be ignored. // When pattern is always matched, do not return condition value. return nullptr; } auto targetTy = TranslateType(*typePattern.type->ty); queue.push(std::make_pair(typePattern.pattern.get(), value)); if (typePattern.needRuntimeTypeCheck) { return CreateAndAppendExpression(builder.GetBoolTy(), value, targetTy, currentBlock)->GetResult(); } else { // When 'matchBeforeRuntime' and 'needRuntimeTypeCheck' are both false, the brans is unreachable. auto expr = CreateAndAppendConstantExpression(builder.GetBoolTy(), *currentBlock, false); return expr->GetResult(); } } void Translator::TranslateConditionMatches(const AST::MatchExpr& matchExpr, Ptr retVal) { auto endBlock = CreateBlock(); bool isStillReachable = true; size_t caseSize = matchExpr.matchCaseOthers.size(); for (size_t i = 0; i < caseSize; ++i) { auto current = matchExpr.matchCaseOthers[i].get(); ScopeContext context(*this); auto baseBlock = currentBlock; auto currentBody = TranslateMatchCaseBody(*current->exprOrDecls, retVal, endBlock); if (!isStillReachable) { continue; } else if (current->matchExpr->astKind == AST::ASTKind::WILDCARD_EXPR) { CreateAndAppendTerminator(currentBody, baseBlock); // All cases after wildcard expr are unreachable. isStillReachable = false; } else { currentBlock = baseBlock; auto nextBlock = CreateBlock(); auto cond = TranslateExprArg(*current->matchExpr); // Create condition brach in base block and create else block as next case block. CreateAndAppendTerminator(cond, currentBody, nextBlock, currentBlock); currentBlock = nextBlock; if (i == caseSize - 1) { // Latest 'currentBlock' for all cases is unreachable, marking for later analysis. currentBlock->EnableAttr(Attribute::UNREACHABLE); } } } // Update block at the end. currentBlock = endBlock; } namespace { bool WithoutEnumSubPattern(const AST::MatchExpr& match) { for (auto& curCase : match.matchCases) { for (auto& subPattern : curCase->patterns) { if (subPattern->astKind != AST::ASTKind::ENUM_PATTERN) { continue; } auto enumPattern = StaticCast(subPattern.get()); if (!enumPattern->patterns.empty()) { return false; } } } return true; } template std::pair, std::vector> ConvertFromMapToVectors(const std::map& inMap) { std::vector keys; std::vector values; for (auto [key, value] : inMap) { keys.emplace_back(key); values.emplace_back(value); } return {keys, values}; } } // namespace uint64_t Translator::GetJumpablePatternVal(const AST::Pattern& pattern) { CJC_ASSERT(pattern.astKind == AST::ASTKind::CONST_PATTERN || pattern.astKind == AST::ASTKind::VAR_OR_ENUM_PATTERN || pattern.astKind == AST::ASTKind::ENUM_PATTERN); if (auto cp = DynamicCast(&pattern)) { return GetConstPatternVal(*cp); } else { return Translator::GetEnumPatternID(GetRealEnumPattern(pattern)); } } bool Translator::IsOptimizableTy(Ptr ty) { return ty->IsInteger() || ty->IsRune(); } bool Translator::IsOptimizableEnumTy(Ptr ty) { CJC_ASSERT(ty->IsEnum()); auto selectorKind = Cangjie::CHIR::GetSelectorType(*StaticCast(ty)->decl); if (selectorKind == Type::TypeKind::TYPE_BOOLEAN) { return false; } auto enumDecl = StaticCast(AST::Ty::GetDeclOfTy(ty)); CJC_NULLPTR_CHECK(enumDecl); if (enumDecl->hasEllipsis) { return false; } for (auto& ctor : enumDecl->constructors) { if (auto funcTy = DynamicCast(ctor->ty)) { CJC_ASSERT(!funcTy->paramTys.empty()); if (!IsOptimizableTy(funcTy->paramTys[0])) { return false; } } } return true; } void Translator::TranslateMatchAsTable(const AST::MatchExpr& matchExpr, Ptr retVal) { auto selectorTy = matchExpr.selector->ty; auto selectorVal = TranslateExprArg(*matchExpr.selector); SetSkipPrintWarning(selectorVal); // Previously checked that selector ty is integer, char or enum. auto enumDecl = DynamicCast(AST::Ty::GetDeclOfTy(selectorTy)); if (!enumDecl) { // NOTE: selector's ty is Rune, UInt, Int. TranslateTrivialMatchAsTable(matchExpr, selectorVal, retVal, 0); } else if (!enumDecl->hasArguments || WithoutEnumSubPattern(matchExpr)) { // Note: selector'ty is enum, and no enumPattern with param. TranslateTrivialMatchAsTable( matchExpr, GetEnumIDValue(selectorTy, selectorVal), retVal, enumDecl->constructors.size()); } else { // NOTE: at least one enumPattern has one param, and use enumId as selector for first switch. // Furthermore using at most first sub-pattern's value as second switch. // It will generate up to one level of nested switch table. TranslateEnumPatternMatchAsTable(matchExpr, selectorVal, retVal); } } void Translator::TranslateTrivialMatchAsTable( const AST::MatchExpr& match, Ptr selectorVal, Ptr retVal, size_t countOfPatterns) { // NOTE: in current case, the pattern must be constant pattern or enum pattern without param(varOrEnum pattern). std::map indexToBodies; auto baseBlock = currentBlock; auto endBlock = CreateBlock(); Block* defaultBlock = endBlock; bool isStillReachable = true; ScopeContext context(*this); for (auto& curCase : match.matchCases) { context.ScopePlus(); auto currentBody = TranslateMatchCaseBody(*curCase->exprOrDecls, retVal, endBlock); CJC_ASSERT(!curCase->patterns.empty()); if (!isStillReachable) { continue; // All cases after wildcard pattern are unreachable pattern which do no need predecessor. } else if (curCase->patterns[0]->astKind == AST::ASTKind::WILDCARD_PATTERN) { // Wildcard pattern in current case must exited on top level. // NOTE: only should to collect first visited wildcard pattern. defaultBlock = currentBody; const auto& loc = TranslateLocation(*curCase->patterns[0]); defaultBlock->SetDebugLocation(loc); isStillReachable = false; continue; } // Store pattern value to block relation. for (auto& pattern : curCase->patterns) { auto patternVal = GetJumpablePatternVal(*pattern); const auto& loc = TranslateLocation(*pattern); currentBody->SetDebugLocation(loc); if (auto [_, success] = indexToBodies.emplace(patternVal, currentBody); !success) { continue; } CJC_ASSERT(!indexToBodies.empty()); // Enum has limited number of patterns when all of them have been filled, rest cases are unreachable. // NOTE: do not check reachability for integer and char. if (indexToBodies.size() == countOfPatterns) { isStillReachable = false; } } } auto [indexes, blocks] = ConvertFromMapToVectors(indexToBodies); if (defaultBlock == endBlock) { // Exhaustive cases, default block will not be reached, just set it to first valid case block. defaultBlock = blocks[0]; } currentBlock = baseBlock; Type* targetType; if (auto enumTy = DynamicCast(match.selector->ty)) { targetType = GetSelectorType(*enumTy); } else { targetType = builder.GetUInt64Ty(); } auto castedVal = TypeCastOrBoxIfNeeded(*selectorVal, *targetType, selectorVal->GetDebugLocation(), false); const auto& loc = TranslateLocation(match); CreateAndAppendTerminator(loc, castedVal, defaultBlock, indexes, blocks, baseBlock); currentBlock = endBlock; } void Translator::TranslateEnumPatternMatchAsTable(const AST::MatchExpr& match, Ptr enumVal, Ptr retVal) { bool isStillReachable = true; auto baseBlock = currentBlock; auto endBlock = CreateBlock(); auto firstDefaultBlock = endBlock; auto firstSelectorVal = GetEnumIDValue(match.selector->ty, enumVal); EnumMatchInfo matchInfo; size_t wildcardIdx = match.matchCases.size(); ScopeContext context(*this); for (size_t i = 0; i < match.matchCases.size(); ++i) { context.ScopePlus(); auto& matchCase = *match.matchCases[i]; CJC_ASSERT(!matchCase.patterns.empty()); if (!isStillReachable) { continue; // All cases after wildcard pattern are unreachable pattern which do no need predecessor. } else if (matchCase.patterns[0]->astKind == AST::ASTKind::WILDCARD_PATTERN) { // Wildcard pattern in current case can only exited on top level. isStillReachable = false; // NOTE: only should to collect first visited wildcard pattern. // The sub pattern may contains varPattern, translate when binding second level table. firstDefaultBlock = TranslateMatchCaseBody(*matchCase.exprOrDecls, retVal, endBlock); const auto& loc = TranslateLocation(matchCase.patterns[0]->begin, matchCase.patterns[0]->end); firstDefaultBlock->SetDebugLocation(loc); wildcardIdx = i; continue; } // Store pattern value to block relation. for (auto& pattern : matchCase.patterns) { CollectEnumPatternInfo(*pattern, matchInfo, i); } } // Create branch to first level switch. auto [indexes, blocks] = ConvertFromMapToVectors(matchInfo.firstSwitchBlocks); if (firstDefaultBlock == endBlock) { // Exhaustive cases, default block will not be reached, just set it to first valid case block. firstDefaultBlock = blocks[0]; } CreateAndAppendTerminator(firstSelectorVal, firstDefaultBlock, indexes, blocks, baseBlock); // Create each second switch block. auto branchInfos = TranslateSecondLevelAsTable(matchInfo, enumVal, firstDefaultBlock); // Finally create goto from case's true block to caseBody block. for (size_t i = 0; i < match.matchCases.size(); ++i) { if (i == wildcardIdx) { continue; } auto bodyBlock = TranslateMatchCaseBody(*match.matchCases[i]->exprOrDecls, retVal, endBlock); for (auto& block : std::as_const(branchInfos[i])) { CreateAndAppendTerminator(bodyBlock, block); } } currentBlock = endBlock; } void Translator::CollectEnumPatternInfo(const AST::Pattern& pattern, EnumMatchInfo& info, size_t caseId) { // When selector type is enum, the pattern must be varOrEnum or enum pattern. auto& enumPattern = GetRealEnumPattern(pattern); auto patternVal = GetEnumPatternID(enumPattern); // Collect block for second level switch table. // When enum pattern does not have sub-pattern, block is the case body block store as nullptr. // Otherwise create new block for each enum id only once. auto [blockIt, succ] = info.firstSwitchBlocks.emplace(patternVal, nullptr); if (succ) { const auto& loc = TranslateLocation(pattern); blockIt->second = CreateBlock(); blockIt->second->SetDebugLocation(loc); } SecondSwitchInfo switchInfo{enumPattern, caseId}; auto& secondSwitchMap = info.indexToBodies[patternVal]; if (enumPattern.patterns.empty()) { info.innerDefaultInfos[patternVal].emplace_back(switchInfo); return; } auto& secondValPattern = *enumPattern.patterns[0]; if (secondValPattern.astKind == AST::ASTKind::WILDCARD_PATTERN) { info.innerDefaultInfos[patternVal].emplace_back(switchInfo); } else if (auto found = info.innerDefaultInfos.find(patternVal); found != info.innerDefaultInfos.end() && enumPattern.patterns.size() != 1) { // If enum has more than one sub-patterns, and the wildcard pattern of second switch value is existed, // the current case should be added to the table of default matching case. info.innerDefaultInfos[patternVal].emplace_back(switchInfo); } else { CJC_ASSERT(secondValPattern.astKind == AST::ASTKind::CONST_PATTERN); auto secondVal = GetConstPatternVal(StaticCast(secondValPattern)); secondSwitchMap[secondVal].emplace_back(switchInfo); } } std::unordered_map>> Translator::TranslateSecondLevelAsTable( const EnumMatchInfo& info, Ptr enumVal, Ptr firstDefaultBlock) { std::unordered_map>> blockBranchInfos; // Create each second switch block. for (auto& [firstIdx, secondMap] : info.indexToBodies) { auto firstLevelBlock = info.firstSwitchBlocks.at(firstIdx); CJC_NULLPTR_CHECK(firstLevelBlock); auto found = info.innerDefaultInfos.find(firstIdx); // When there is no default pattern on first sub-pattern, set default as previous default block. // When only found one and enum does not have sub-pattern, create direct goto. // Otherwise generate all collected patterns for other enum sub-patterns. Block* secondDefaultBlock; if (found == info.innerDefaultInfos.end()) { secondDefaultBlock = firstDefaultBlock; } else if (!found->second.empty() && found->second[0].ep.patterns.empty()) { blockBranchInfos[found->second[0].caseId].emplace_back(firstLevelBlock); } else { // When 'secondMap' is emtpy, second selector is wildcard, // we need pass 'firstLevelBlock' as start block of second table detail. secondDefaultBlock = secondMap.empty() ? firstLevelBlock : CreateBlock(); TranslateSecondLevelTable(firstDefaultBlock, secondDefaultBlock, enumVal, found->second, blockBranchInfos); } if (secondMap.empty()) { continue; } currentBlock = firstLevelBlock; CJC_ASSERT(!secondMap.empty() && !secondMap.begin()->second.empty()); auto& enumPattern = secondMap.begin()->second.front().ep; CJC_ASSERT(!enumPattern.patterns.empty()); auto& firstPattern = *enumPattern.patterns[0]; PrintDebugMessage(opts.chirDebugOptimizer, enumPattern); CJC_ASSERT(firstPattern.ty->IsInteger() || firstPattern.ty->IsRune()); auto selectorTy = TranslateType(*firstPattern.ty); auto enumValueTuple = CastEnumValueToConstructorTupleType(enumVal, enumPattern); auto secondSelectVar = CreateAndAppendExpression(selectorTy, enumValueTuple, std::vector{1}, currentBlock) ->GetResult(); std::vector indexes; std::vector blocks; for (auto& [index, infos] : secondMap) { indexes.emplace_back(index); auto block = CreateBlock(); blocks.emplace_back(block); TranslateSecondLevelTable(firstDefaultBlock, block, enumVal, infos, blockBranchInfos); } currentBlock = firstLevelBlock; CreateAndAppendTerminator( TypeCastOrBoxIfNeeded( *secondSelectVar, *builder.GetUInt64Ty(), secondSelectVar->GetDebugLocation(), false), secondDefaultBlock, indexes, blocks, firstLevelBlock); } return blockBranchInfos; } void Translator::TranslateSecondLevelTable(Ptr endBlock, const Ptr tableBlock, const Ptr enumVal, const std::vector& infos, std::unordered_map>>& blockBranchInfos) { currentBlock = tableBlock; bool hasGotoBase = false; CJC_ASSERT(!infos.empty()); CJC_ASSERT(!infos[0].ep.patterns.empty()); bool isWildcardPattern = infos[0].ep.patterns[0]->astKind == AST::ASTKind::WILDCARD_PATTERN; for (size_t idx = 0; idx < infos.size(); ++idx) { const auto& current = infos[idx]; // When sub-pattern size is exactly 1, 'tableBlock' should be considered as 'trueBlock'. // NOTE: the 'tableBlock' can only be used as 'trueBlock' once. auto trueBlock = !hasGotoBase && current.ep.patterns.size() == 1 ? tableBlock.get() : CreateBlock(); hasGotoBase = hasGotoBase || current.ep.patterns.size() == 1; auto falseBlock = CreateBlock(); std::queue, Ptr>> queue; auto elementTys = StaticCast(current.ep.constructor->ty)->paramTys; // When first pattern is wildcard and current pattern is not, we need to check from first sub-pattern, // otherwise only need to start from second sub-pattern. size_t start = isWildcardPattern && current.ep.patterns[0]->astKind != AST::ASTKind::WILDCARD_PATTERN ? 0 : 1; for (size_t i = start; i < current.ep.patterns.size(); ++i) { auto elementTy = TranslateType(*elementTys[i]); auto enumValueTuple = CastEnumValueToConstructorTupleType(enumVal, current.ep); auto elementVal = CreateAndAppendExpression(elementTy, enumValueTuple, std::vector{1 + i}, currentBlock) ->GetResult(); // 1 + i: plus enum ctor id queue.emplace(std::make_pair(current.ep.patterns[i].get(), elementVal)); } auto newBlock = CreateBlock(); while (!queue.empty()) { const DebugLocation& originLoc = TranslateLocation(current.ep.ctxExpr->begin, current.ep.ctxExpr->end); // Typecast of varPattern must be generated at the real trueBlock. auto cond = DispatchingPattern(queue, {trueBlock, newBlock}, originLoc); if (cond) { auto nextBlock = queue.empty() ? trueBlock : newBlock; CreateAndAppendTerminator(cond, nextBlock, falseBlock, currentBlock); currentBlock = nextBlock; } else if (queue.empty()) { CreateAndAppendTerminator(trueBlock, currentBlock); } if (cond && !queue.empty()) { newBlock = CreateBlock(); } } newBlock->RemoveSelfFromBlockGroup(); // Create 'GOTO' for trueBlock at callee for correct binding of varPattern. blockBranchInfos[current.caseId].emplace_back(trueBlock); if (idx == infos.size() - 1) { CreateAndAppendTerminator(endBlock, falseBlock); } else { currentBlock = falseBlock; } } } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateMemberAccess.cpp000066400000000000000000000662561510705540100275470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/CHIR/ConstantUtils.h" #include "cangjie/CHIR/Package.h" #include "cangjie/Mangle/CHIRManglingUtils.h" using namespace Cangjie::CHIR; using namespace Cangjie; using namespace AST; namespace { /* Use UpperBound to replace Generic type,e.g. if T <: C1 1、if baseType is T then return C1& 2、if baseType is T& then return C1& 3、if baseType is C2 then return C2 */ Ptr CreateTypeWithUpperBounds(CHIR::Type& baseType, CHIR::CHIRBuilder& builder) { std::vector newArgs; auto srcType = &baseType; if (srcType->IsRef() && StaticCast(srcType)->GetBaseType()->IsGeneric()) { srcType = StaticCast(srcType)->GetBaseType(); } if (srcType->IsGeneric()) { for (auto type : StaticCast(srcType)->GetUpperBounds()) { if (type->IsRef() && StaticCast(type)->GetBaseType()->IsClass()) { return type; } } } else { for (auto type : srcType->GetTypeArgs()) { newArgs.emplace_back(CreateTypeWithUpperBounds(*type, builder)); } return CreateNewTypeWithArgs(*srcType, newArgs, builder); } return &baseType; } bool IsInsideCFunc(const CHIR::Block& bl) { if (bl.GetTopLevelFunc()->IsCFunc()) { return true; } auto expr = bl.GetParentBlockGroup()->GetOwnerExpression(); while (expr) { if (auto lambda = Cangjie::DynamicCast(expr)) { if (lambda->GetFuncType()->IsCFunc()) { return true; } } expr = expr->GetParentBlockGroup()->GetOwnerExpression(); } return false; } } // namespace // `obj.foo()` is a virtual func call ? bool Translator::IsVirtualFuncCall( const ClassDef& obj, const AST::FuncDecl& funcDecl, bool isSuperCall) { /** super function call must be `Apply` * open class A { * public open func foo() {} * } * class B <: A { * public func foo() {} * func goo() { * var a = super.foo // we must call A.foo, not B.foo by vtable * a() * } * } */ if (isSuperCall) { return false; } auto funcDeclIsOpen = [&funcDecl]() { if (funcDecl.TestAnyAttr(AST::Attribute::GENERIC_INSTANTIATED, AST::Attribute::PRIMARY_CONSTRUCTOR, AST::Attribute::CONSTRUCTOR, AST::Attribute::FINALIZER)) { return false; } auto funcHasOpenFlag = funcDecl.TestAnyAttr(AST::Attribute::OPEN, AST::Attribute::ABSTRACT) || (funcDecl.outerDecl && funcDecl.outerDecl->astKind == AST::ASTKind::INTERFACE_DECL); auto instanceMethodIsOpen = funcHasOpenFlag && (funcDecl.TestAttr(AST::Attribute::PUBLIC) || funcDecl.TestAttr(AST::Attribute::PROTECTED)); auto staticMethodIsOpen = funcDecl.outerDecl->IsClassLikeDecl() && funcDecl.TestAttr(AST::Attribute::STATIC) && !funcDecl.TestAttr(AST::Attribute::PRIVATE); return instanceMethodIsOpen || staticMethodIsOpen; }; return !IsInsideCFunc(*currentBlock) && obj.CanBeInherited() && funcDeclIsOpen(); } Ptr Translator::GetBaseFromMemberAccess(const AST::Expr& base) { if (AST::IsThisOrSuper(base)) { // This call or super call don't need add `Load`; // struct A { // let x:Int64 // func foo() {this.x} // } auto thisVar = GetImplicitThisParam(); return thisVar; } auto curObj = TranslateExprArg(base); auto loc = TranslateLocation(base); if (base.ty->IsClassLike() || curObj->GetType()->IsRawArray()) { // class A {func foo(){return 0} // var a = A() // a.b.c.d // a is A&& need add `Load` // let b = A() // b.c.d // b is A& don't need add `Load` // note: generic type will cast to class upper bound, do load if need auto objType = curObj->GetType(); CJC_ASSERT(objType->IsRef()); if (objType->IsRef()) { // objType is A&& or A& auto objBaseType = StaticCast(objType)->GetBaseType(); if (objBaseType->IsRef()) { // for example: objBaseType is A& curObj = CreateAndAppendExpression(loc, objBaseType, curObj, currentBlock)->GetResult(); } } } else if (curObj->GetType()->IsRef() && StaticCast(curObj->GetType())->GetBaseType()->IsGeneric()) { // a generic type variable must be non reference as a parameter in GetElementRef/StoreElementRef // Example: // Var a: T = xxx // T is a generic type // a.b // a is T&, need add Load expression, auto objBaseType = StaticCast(curObj->GetType())->GetBaseType(); curObj = CreateAndAppendExpression(loc, objBaseType, curObj, currentBlock)->GetResult(); } if (curObj->GetType()->IsGeneric()) { // type cast to upperbounds, if base is a generic auto newType = CreateTypeWithUpperBounds(*curObj->GetType(), builder); curObj = TypeCastOrBoxIfNeeded(*curObj, *newType, loc); } return curObj; } Ptr Translator::GetTypeOfInvokeStatic(const AST::Decl& funcDecl) { CJC_NULLPTR_CHECK(funcDecl.outerDecl); auto calledClassType = TranslateType(*funcDecl.outerDecl->ty); if (calledClassType->IsRef()) { calledClassType = StaticCast(calledClassType)->GetBaseType(); return calledClassType; } return calledClassType; } Translator::InstCalleeInfo Translator::GetInstCalleeInfoFromRefExpr(const AST::RefExpr& expr) { auto funcType = StaticCast(TranslateType(*expr.ty)); auto paramTys = funcType->GetParamTypes(); auto funcDecl = StaticCast(expr.ref.target); CJC_ASSERT(funcDecl->outerDecl != nullptr); auto currentFunc = GetCurrentFunc(); CJC_NULLPTR_CHECK(currentFunc); /** * class A { * static func foo() {} * static let x = if (...) { foo } * } * maybe we are translating `foo` in static member var `x`'s initializer, its initializer is a global func * which added by CHIR, and this `foo` can't be from class A's sub type, must be from class A or its parent type */ if (currentFunc->IsGVInit() || currentFunc->IsStaticInit()) { CJC_ASSERT(funcDecl->TestAttr(AST::Attribute::STATIC)); auto outerDecl = funcDecl->outerDecl; CJC_NULLPTR_CHECK(outerDecl); auto parentType = GetNominalSymbolTable(*outerDecl)->GetType(); return InstCalleeInfo { .instParentCustomTy = parentType, .thisType = parentType, .instParamTys = paramTys, .instRetTy = funcType->GetReturnType(), .isVirtualFuncCall = false}; } /** * open class A { * static public func goo() { println(1) } * } * extend A { * static func foo() { goo() } * } * class B <: A { * static public func goo() { println(2) } * } * B.foo() // should print "2" */ auto parentType = currentFunc->GetParentCustomTypeOrExtendedType(); CJC_NULLPTR_CHECK(parentType); if (parentType->IsClass() || IsStructMutFunction(*funcDecl)) { parentType = builder.GetType(parentType); } if (!funcDecl->TestAttr(AST::Attribute::STATIC)) { paramTys.insert(paramTys.begin(), parentType); } bool isVirtualFuncCall = false; auto thisType = parentType; // if a func call is in instantiated member method, it can be a virtual func call, too if (auto customType = DynamicCast(parentType->StripAllRefs()); customType && IsVirtualFuncCall(*customType->GetClassDef(), *funcDecl, false)) { thisType = builder.GetType(builder.GetType()); isVirtualFuncCall = true; } std::vector funcInstArgs; for (auto ty : expr.instTys) { funcInstArgs.emplace_back(TranslateType(*ty)); } return InstCalleeInfo { .instParentCustomTy = parentType, .thisType = thisType, .instParamTys = paramTys, .instRetTy = funcType->GetReturnType(), .instantiatedTypeArgs = funcInstArgs, .isVirtualFuncCall = isVirtualFuncCall}; } Translator::InstCalleeInfo Translator::GetInstCalleeInfoFromMemberAccess(const AST::MemberAccess& expr) { auto thisType = TranslateType(*expr.baseExpr->ty); auto funcType = StaticCast(TranslateType(*expr.ty)); auto paramTys = funcType->GetParamTypes(); auto funcDecl = StaticCast(expr.target); if (!funcDecl->TestAttr(AST::Attribute::STATIC)) { paramTys.insert(paramTys.begin(), thisType); funcType = builder.GetType(paramTys, funcType->GetReturnType()); } std::vector funcInstArgs; for (auto ty : expr.instTys) { funcInstArgs.emplace_back(TranslateType(*ty)); } auto thisDerefTy = thisType->StripAllRefs(); bool isVirtualFuncCall = false; bool isSuper = false; if (auto base = DynamicCast(expr.baseExpr.get()); (base && base->isSuper)) { isSuper = true; } // only `obj.foo()` and `T.foo()` need to calculate if is virtual func call // `classA.foo()` is Apply call if (auto customType = DynamicCast(thisDerefTy); customType && !funcDecl->TestAttr(AST::Attribute::STATIC)) { isVirtualFuncCall = IsVirtualFuncCall(*customType->GetClassDef(), *funcDecl, isSuper); } else if (auto genericType = DynamicCast(thisDerefTy)) { isVirtualFuncCall = true; // maybe T <: open class A & non-open class B, then it still not be virtual func call for (auto ty : genericType->GetUpperBounds()) { auto customDef = StaticCast(ty->StripAllRefs())->GetClassDef(); isVirtualFuncCall &= IsVirtualFuncCall(*customDef, *funcDecl, isSuper); } } auto parentType = GetExactParentType(*thisDerefTy, *funcDecl, *funcType, funcInstArgs, isVirtualFuncCall); return InstCalleeInfo { .instParentCustomTy = parentType, .thisType = thisType, .instParamTys = paramTys, .instRetTy = funcType->GetReturnType(), .instantiatedTypeArgs = funcInstArgs, .isVirtualFuncCall = isVirtualFuncCall}; } Value* Translator::GetWrapperFuncFromMemberAccess(Type& thisType, const std::string funcName, FuncType& instFuncType, bool isStatic, std::vector& funcInstTypeArgs) { FuncBase* result = nullptr; if (auto genericType = DynamicCast(&thisType)) { auto& upperBounds = genericType->GetUpperBounds(); CJC_ASSERT(!upperBounds.empty()); for (auto upperBound : upperBounds) { ClassType* upperClassType = StaticCast(StaticCast(upperBound)->GetBaseType()); return GetWrapperFuncFromMemberAccess(*upperClassType, funcName, instFuncType, isStatic, funcInstTypeArgs); } } else if (auto customTy = DynamicCast(&thisType)) { result = customTy->GetExpectedFunc(funcName, instFuncType, true, funcInstTypeArgs, builder, false).first; } else { std::unordered_map replaceTable; auto classInstArgs = thisType.GetTypeArgs(); // extend def for (auto ex : thisType.GetExtends(&builder)) { auto classGenericArgs = ex->GetExtendedType()->GetTypeArgs(); CJC_ASSERT(classInstArgs.size() == classGenericArgs.size()); for (size_t i = 0; i < classInstArgs.size(); ++i) { if (auto genericTy = DynamicCast(classGenericArgs[i])) { replaceTable.emplace(genericTy, classInstArgs[i]); } } auto [func, done] = ex->GetExpectedFunc(funcName, instFuncType, true, replaceTable, funcInstTypeArgs, builder, false); if (done) { result = func; break; } } } return result; } Ptr Translator::TranslateStaticTargetOrPackageMemberAccess(const AST::MemberAccess& member) { // only classA.foo need a wrapper, pkgA.foo doesn't if (member.target->astKind == AST::ASTKind::FUNC_DECL && member.target->outerDecl != nullptr) { auto instFuncType = GetInstCalleeInfoFromMemberAccess(member); return WrapMemberMethodByLambda(*StaticCast(member.target), instFuncType, nullptr); } auto targetNode = GetSymbolTable(*member.target); auto targetTy = TranslateType(*member.target->ty); auto resTy = TranslateType(*member.ty); auto loc = TranslateLocation(member); if (auto refExpr = DynamicCast(&*member.baseExpr)) { // this is a package member access, return the target directly if (refExpr->ref.target->ty->IsInvalid()) { // global var, load and typecast if needed if (Is(member.target)) { auto targetVal = CreateAndAppendExpression(loc, targetTy, targetNode, currentBlock)->GetResult(); auto castedTargetVal = TypeCastOrBoxIfNeeded(*targetVal, *resTy, loc); return castedTargetVal; } return targetNode; } } auto targetVal = CreateAndAppendExpression(loc, targetTy, targetNode, currentBlock)->GetResult(); auto castedTargetVal = TypeCastOrBoxIfNeeded(*targetVal, *resTy, loc); return castedTargetVal; } Ptr Translator::TranslateFuncMemberAccess(const AST::MemberAccess& member) { auto instFuncType = GetInstCalleeInfoFromMemberAccess(member); auto funcDecl = StaticCast(member.target); CJC_NULLPTR_CHECK(funcDecl->outerDecl); auto thisVal = GetCurrentThisObjectByMemberAccess(member, *funcDecl, TranslateLocation(*member.baseExpr)); return WrapMemberMethodByLambda(*funcDecl, instFuncType, thisVal); } Ptr Translator::TransformThisType(Value& rawThis, Type& expectedTy, Lambda& curLambda) { /** this function is used in lambda which generated by member access, such as: struct A { func a(): Int64 { return 1 } mut func b(): Int64 { let c = a // `a` will be translated to lambda, in lambda, there is `Apply(this.a)` return c() // Apply(lambda(a)) } } so, param `this` in function b may not be passed to `Apply(this.a)` directly we need to add load or typecast for `this`, `this` has three cases: 1. in class's member function, `this` is ref type 2. in struct's mut member function, `this` is ref type 3. in struct's immut member function, `this` is struct type considered cangjie rules, there are five cases for transform: a. struct& -> struct b. struct& -> struct& c. struct -> struct d. class& -> class& e. class& -> sub class& or super class& cangjie rules: 1. struct can't inheritance struct, only interface so struct type doesn't have sub struct or super struct 2. a mut function can't be called in an immut function so we can't transform struct type to struct ref type */ // case b, c, d if (rawThis.GetType() == &expectedTy) { return &rawThis; } // case a Expression* expr = nullptr; if (rawThis.GetType()->IsRef() && StaticCast(rawThis.GetType())->GetBaseType()->IsStruct()) { CJC_ASSERT(StaticCast(rawThis.GetType())->GetBaseType() == &expectedTy); expr = builder.CreateExpression(&expectedTy, &rawThis, curLambda.GetParentBlock()); } else { // case e expr = StaticCast(TypeCastOrBoxIfNeeded(rawThis, expectedTy, INVALID_LOCATION))->GetExpr(); } // this is really hack, should change this if (expr->GetResult() != &rawThis) { // `load` or `typecast` must be created before lambda, or we will get wrong llvm ir, and core dump in llvm-opt expr->MoveBefore(&curLambda); } return expr->GetResult(); } GenericType* Translator::TranslateCompleteGenericType(AST::GenericsTy& ty) { auto gType = StaticCast(TranslateType(ty)); chirTy.FillGenericArgType(ty); return gType; } Ptr Translator::TranslateVarMemberAccess(const AST::MemberAccess& member) { const auto& loc = TranslateLocation(member); auto leftValueInfo = TranslateMemberAccessAsLeftValue(member); auto base = leftValueInfo.base; CJC_ASSERT(!leftValueInfo.path.empty()); auto customType = StaticCast(base->GetType()->StripAllRefs()); if (base->GetType()->IsReferenceTypeWithRefDims(1) || base->GetType()->IsValueOrGenericTypeWithRefDims(1)) { base = CreateGetElementRefWithPath(loc, base, leftValueInfo.path, currentBlock, *customType); CJC_ASSERT(base && base->GetType()->IsRef()); auto loadMemberVal = CreateAndAppendExpression( loc, StaticCast(base->GetType())->GetBaseType(), base, currentBlock); return loadMemberVal->GetResult(); } else if (base->GetType()->IsValueOrGenericTypeWithRefDims(0)) { auto memberType = GetInstMemberTypeByName(*customType, leftValueInfo.path, builder); auto getMember = CreateAndAppendExpression(loc, memberType, base, leftValueInfo.path, currentBlock); return getMember->GetResult(); } CJC_ABORT(); return nullptr; } Ptr Translator::TranslateEnumMemberAccess(const AST::MemberAccess& member) { // The target is varDecl. // example cangjie code: // enum A { // C|D(Int64) // } // var a = A.c // varDecl auto enumTy = StaticCast(member.baseExpr->ty); auto enumDecl = enumTy->decl; auto& constructors = enumDecl->constructors; auto fieldIt = std::find_if(constructors.begin(), constructors.end(), [&member](auto const& decl) -> bool { return decl.get() && decl->astKind == AST::ASTKind::VAR_DECL && decl->identifier == member.field; }); CJC_ASSERT(fieldIt != constructors.end()); auto enumId = static_cast(std::distance(constructors.begin(), fieldIt)); auto ty = chirTy.TranslateType(*enumTy); const auto& loc = TranslateLocation(**fieldIt); auto selectorTy = GetSelectorType(*enumTy); if (!enumTy->decl->hasArguments) { auto intExpr = CreateAndAppendConstantExpression(loc, selectorTy, *currentBlock, enumId); return TypeCastOrBoxIfNeeded(*intExpr->GetResult(), *ty, loc); } std::vector args; if (selectorTy->IsBoolean()) { auto boolExpr = CreateAndAppendConstantExpression(loc, selectorTy, *currentBlock, enumId); args.emplace_back(boolExpr->GetResult()); } else { auto intExpr = CreateAndAppendConstantExpression(loc, selectorTy, *currentBlock, enumId); args.emplace_back(intExpr->GetResult()); } return CreateAndAppendExpression(TranslateLocation(member), ty, args, currentBlock) ->GetResult(); } Ptr Translator::TranslateInstanceMemberMemberAccess(const AST::MemberAccess& member) { Ptr res = nullptr; switch (member.target->astKind) { case ASTKind::VAR_DECL: { res = TranslateVarMemberAccess(member); break; } case ASTKind::FUNC_DECL: { res = TranslateFuncMemberAccess(member); break; } default: CJC_ABORT(); } return res; } Translator::LeftValueInfo Translator::TranslateMemberAccessAsLeftValue(const AST::MemberAccess& member) { auto target = member.target; CJC_ASSERT(target->astKind == AST::ASTKind::VAR_DECL); const auto& loc = TranslateLocation(member); // Case 1: target is case variable in enum if (target->TestAttr(AST::Attribute::ENUM_CONSTRUCTOR)) { return LeftValueInfo(TranslateASTNode(member, *this), {}); } // Case 2.2: target is global variable or static variable if (target->TestAttr(AST::Attribute::STATIC) || IsPackageMemberAccess(member)) { auto targetVal = GetSymbolTable(*target); CJC_NULLPTR_CHECK(targetVal); return LeftValueInfo(targetVal, {}); } // Case 2.4: target is non-static member variable if (target->outerDecl && !target->TestAttr(AST::Attribute::STATIC)) { // following code is used in serveral places, should wrap into an API const AST::Expr* base = &member; std::vector path; bool readOnly = false; AST::Ty* targetBaseASTTy = nullptr; for (;;) { base = base->desugarExpr ? base->desugarExpr.get().get() : base; if (auto ma = DynamicCast(base)) { bool isTargetClassOrClassUpper = ma->ty->IsClassLike() || ma->ty->IsGeneric(); if ((!isTargetClassOrClassUpper || path.empty()) && !ma->target->TestAttr(AST::Attribute::STATIC) && ma->target->astKind != ASTKind::PROP_DECL && !IsPackageMemberAccess(*ma)) { auto name = ma->target->identifier.Val(); CJC_ASSERT(!name.empty()); path.insert(path.begin(), name); readOnly = readOnly || !StaticCast(ma->target)->isVar; targetBaseASTTy = ma->target->outerDecl->ty; CJC_ASSERT(targetBaseASTTy->IsStruct() || targetBaseASTTy->IsClass()); base = ma->baseExpr.get(); continue; } break; } else if (auto ref = DynamicCast(base)) { if (!ref->isThis && !ref->isSuper && !ref->ty->IsClassLike() && !ref->ty->IsGeneric()) { auto refTarget = ref->ref.target; if (refTarget->outerDecl && (refTarget->outerDecl->astKind == AST::ASTKind::STRUCT_DECL || refTarget->outerDecl->astKind == AST::ASTKind::CLASS_DECL) && !refTarget->TestAttr(AST::Attribute::STATIC)) { auto name = refTarget->identifier.Val(); CJC_ASSERT(!name.empty()); path.insert(path.begin(), name); readOnly = readOnly || !StaticCast(refTarget)->isVar; targetBaseASTTy = refTarget->outerDecl->ty; CJC_ASSERT(targetBaseASTTy->IsStruct() || targetBaseASTTy->IsClass()); // this is a hack base = nullptr; } } break; } else { break; } } Value* baseVal = nullptr; if (base == nullptr) { baseVal = GetImplicitThisParam(); } else { auto baseLeftValueInfo = TranslateExprAsLeftValue(*base); auto baseLeftValue = baseLeftValueInfo.base; auto baseLeftValueTy = baseLeftValue->GetType(); if (baseLeftValueTy->IsReferenceTypeWithRefDims(CLASS_REF_DIM)) { baseLeftValueTy = StaticCast(baseLeftValueTy)->GetBaseType(); auto loadBaseValue = CreateAndAppendExpression(loc, baseLeftValueTy, baseLeftValue, currentBlock); baseLeftValue = loadBaseValue->GetResult(); } auto baseLeftValuePath = baseLeftValueInfo.path; if (!baseLeftValuePath.empty()) { auto baseCustomType = StaticCast(baseLeftValueTy->StripAllRefs()); if (baseLeftValueTy->IsReferenceTypeWithRefDims(1) || baseLeftValueTy->IsValueOrGenericTypeWithRefDims(1)) { auto getMemberRef = CreateGetElementRefWithPath( loc, baseLeftValue, baseLeftValuePath, currentBlock, *baseCustomType); auto memberType = StaticCast(getMemberRef->GetType())->GetBaseType(); CJC_ASSERT(memberType->IsReferenceTypeWithRefDims(1) || memberType->IsValueOrGenericTypeWithRefDims(0)); auto loadMemberValue = CreateAndAppendExpression(loc, memberType, getMemberRef, currentBlock); baseVal = loadMemberValue->GetResult(); } else if (baseLeftValueTy->IsValueOrGenericTypeWithRefDims(0)) { auto memberType = GetInstMemberTypeByName(*baseCustomType, baseLeftValuePath, builder); CJC_ASSERT(memberType->IsReferenceTypeWithRefDims(1) || memberType->IsValueOrGenericTypeWithRefDims(0)); auto getField = CreateAndAppendExpression( loc, memberType, baseLeftValue, baseLeftValuePath, currentBlock); baseVal = getField->GetResult(); } } else { CJC_ASSERT(baseLeftValueTy->IsReferenceTypeWithRefDims(1) || baseLeftValueTy->IsValueOrGenericTypeWithRefDims(1) || baseLeftValueTy->IsValueOrGenericTypeWithRefDims(0)); baseVal = baseLeftValue; } } auto baseValRefDims = baseVal->GetType()->GetRefDims(); auto baseValTy = baseVal->GetType()->StripAllRefs(); std::unordered_map instMap; if (auto baseValCustomTy = DynamicCast(baseValTy)) { baseValCustomTy->GetInstMap(instMap, builder); } else if (auto baseValGenericTy = DynamicCast(baseValTy)) { baseValGenericTy->GetInstMap(instMap, builder); } CJC_NULLPTR_CHECK(targetBaseASTTy); Type* targetBaseTy = TranslateType(*targetBaseASTTy); // Handle the case where the baseValTy is a generic which ref dims is zero baseValRefDims = std::max(targetBaseTy->GetRefDims(), baseValRefDims); targetBaseTy = targetBaseTy->StripAllRefs(); targetBaseTy = ReplaceRawGenericArgType(*targetBaseTy, instMap, builder); for (size_t i = 0; i < baseValRefDims; ++i) { targetBaseTy = builder.GetType(targetBaseTy); } auto castedBaseVal = TypeCastOrBoxIfNeeded(*baseVal, *targetBaseTy, INVALID_LOCATION); return LeftValueInfo(castedBaseVal, path); } CJC_ABORT(); return LeftValueInfo(nullptr, {}); } Ptr Translator::Visit(const AST::MemberAccess& member) { CJC_NULLPTR_CHECK(member.baseExpr); CJC_NULLPTR_CHECK(member.target); if (member.target && (member.target->TestAttr(AST::Attribute::STATIC) || IsPackageMemberAccess(member))) { return TranslateStaticTargetOrPackageMemberAccess(member); } else if (member.target->TestAttr(AST::Attribute::ENUM_CONSTRUCTOR)) { return TranslateEnumMemberAccess(member); } else if (IsInstanceMember(*member.target)) { return TranslateInstanceMemberMemberAccess(member); } InternalError("translating unsupported MemberAccess"); return nullptr; } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateParenExpr.cpp000066400000000000000000000007441510705540100271100ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" using namespace Cangjie::CHIR; using namespace Cangjie; Ptr Translator::Visit(const AST::ParenExpr& expr) { return TranslateExprArg(*expr.expr); } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslatePointerExpr.cpp000066400000000000000000000050501510705540100274560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" using namespace Cangjie::CHIR; using namespace Cangjie; Ptr Translator::Visit(const AST::PointerExpr& expr) { auto ty = TranslateType(*expr.ty); CHIR::IntrinsicKind intrinsicKind; std::vector args{}; if (expr.arg) { intrinsicKind = IntrinsicKind::CPOINTER_INIT1; auto loc = TranslateLocation(*expr.arg); Value* argVal = nullptr; if (expr.arg->withInout) { auto argLeftValInfo = TranslateExprAsLeftValue(*expr.arg->expr); argVal = argLeftValInfo.base; // polish this if (!argLeftValInfo.path.empty()) { auto lhsCustomType = StaticCast(argVal->GetType()->StripAllRefs()); if (argVal->GetType()->IsRef()) { argVal = CreateGetElementRefWithPath(TranslateLocation(expr), argVal, argLeftValInfo.path, currentBlock, *lhsCustomType); } else { auto memberType = GetInstMemberTypeByName(*lhsCustomType, argLeftValInfo.path, builder); auto getMember = CreateAndAppendExpression( TranslateLocation(expr), memberType, argVal, argLeftValInfo.path, currentBlock); argVal = getMember->GetResult(); } } auto ty1 = TranslateType(*expr.arg->ty); auto callContext = IntrisicCallContext { .kind = IntrinsicKind::INOUT_PARAM, .args = std::vector{argVal} }; argVal = CreateAndAppendExpression(loc, ty1, callContext, currentBlock)->GetResult(); } else { argVal = TranslateASTNode(*expr.arg, *this); } CJC_NULLPTR_CHECK(argVal); argVal = GenerateLoadIfNeccessary(*argVal, false, false, expr.arg->withInout, loc); args.emplace_back(argVal); } else { intrinsicKind = IntrinsicKind::CPOINTER_INIT0; } const auto& loc = TranslateLocation(expr); auto callContext = IntrisicCallContext { .kind = intrinsicKind, .args = args }; return CreateAndAppendExpression(loc, ty, callContext, currentBlock)->GetResult(); }cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateRefExpr.cpp000066400000000000000000000502441510705540100265570ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/AST/Utils.h" #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/CHIR/ConstantUtils.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/Mangle/CHIRManglingUtils.h" using namespace Cangjie::AST; using namespace Cangjie::CHIR; using namespace Cangjie; Translator::LeftValueInfo Translator::TranslateThisOrSuperRefAsLeftValue(const AST::RefExpr& refExpr) { CJC_ASSERT(refExpr.isThis || refExpr.isSuper); auto curFunc = GetCurrentFunc(); CJC_ASSERT(curFunc); auto thisParam = GetImplicitThisParam(); if (refExpr.isSuper) { auto superTy = TranslateType(*refExpr.ty); auto loc = TranslateLocation(refExpr); thisParam = TypeCastOrBoxIfNeeded(*thisParam, *superTy, loc); } return LeftValueInfo(thisParam, {}); } Translator::LeftValueInfo Translator::TranslateStructMemberVarRefAsLeftValue(const AST::RefExpr& refExpr) const { auto target = refExpr.ref.target; CJC_ASSERT(target->astKind == ASTKind::VAR_DECL); CJC_ASSERT(target->outerDecl->astKind == ASTKind::STRUCT_DECL); auto implicitThis = GetImplicitThisParam(); auto name = StaticCast(target)->identifier.Val(); CJC_ASSERT(!name.empty()); return LeftValueInfo(implicitThis, {name}); } Translator::LeftValueInfo Translator::TranslateClassMemberVarRefAsLeftValue(const AST::RefExpr& refExpr) const { auto target = refExpr.ref.target; CJC_ASSERT(target->astKind == ASTKind::VAR_DECL); CJC_ASSERT(target->outerDecl->astKind == ASTKind::CLASS_DECL); auto implicitThis = GetImplicitThisParam(); CJC_ASSERT(implicitThis->GetType()->IsReferenceTypeWithRefDims(1)); auto name = StaticCast(target)->identifier.Val(); CJC_ASSERT(!name.empty()); return LeftValueInfo(implicitThis, {name}); } Translator::LeftValueInfo Translator::TranslateEnumMemberVarRef(const AST::RefExpr& refExpr) { auto target = refExpr.ref.target; CJC_ASSERT(target->astKind == ASTKind::VAR_DECL); CJC_ASSERT(target->outerDecl->astKind == ASTKind::ENUM_DECL); CJC_ASSERT(target->TestAttr(AST::Attribute::ENUM_CONSTRUCTOR)); auto loc = TranslateLocation(refExpr); // polish here auto enumTy = StaticCast(refExpr.ty); auto enumType = StaticCast(chirTy.TranslateType(*enumTy)); uint64_t enumId = GetEnumCtorId(*target); auto selectorTy = GetSelectorType(*enumTy); if (enumTy->decl->hasArguments) { std::vector args; if (selectorTy->IsBoolean()) { auto boolExpr = CreateAndAppendConstantExpression( selectorTy, *currentBlock, static_cast(enumId)); args.emplace_back(boolExpr->GetResult()); } else { auto intExpr = CreateAndAppendConstantExpression(selectorTy, *currentBlock, enumId); args.emplace_back(intExpr->GetResult()); } auto tupleExpr = CreateAndAppendExpression(loc, enumType, args, currentBlock); return LeftValueInfo(tupleExpr->GetResult(), {}); } else { auto intExpr = CreateAndAppendConstantExpression(loc, selectorTy, *currentBlock, enumId); auto castedIntExpr = TypeCastOrBoxIfNeeded(*intExpr->GetResult(), *enumType, loc); return LeftValueInfo(castedIntExpr, {}); } } Translator::LeftValueInfo Translator::TranslateVarRefAsLeftValue(const AST::RefExpr& refExpr) { auto target = refExpr.ref.target; CJC_ASSERT(target); CJC_ASSERT(target->astKind == AST::ASTKind::VAR_DECL || target->astKind == AST::ASTKind::FUNC_PARAM); // Case 1: non static member variable if (target->outerDecl != nullptr && !target->TestAttr(AST::Attribute::STATIC)) { // Case 2.1: non static member variable in struct if (target->outerDecl->astKind == AST::ASTKind::STRUCT_DECL) { return TranslateStructMemberVarRefAsLeftValue(refExpr); } // Case 2.2: non static member variable in class if (target->outerDecl->astKind == AST::ASTKind::CLASS_DECL) { return TranslateClassMemberVarRefAsLeftValue(refExpr); } // Case 2.3: case variable in enum if (target->outerDecl->astKind == AST::ASTKind::ENUM_DECL) { return TranslateEnumMemberVarRef(refExpr); } } // Case 2: global var, static member var, local var, func param auto val = GetSymbolTable(*target); return LeftValueInfo(val, {}); } Translator::LeftValueInfo Translator::TranslateRefExprAsLeftValue(const AST::RefExpr& refExpr) { // Case 1: `this` or `super` if (refExpr.isThis || refExpr.isSuper) { return TranslateThisOrSuperRefAsLeftValue(refExpr); } // Case 2: variable auto target = refExpr.ref.target; CJC_ASSERT(target); if (target->astKind == AST::ASTKind::VAR_DECL || target->astKind == AST::ASTKind::FUNC_PARAM) { return TranslateVarRefAsLeftValue(refExpr); } CJC_ABORT(); return LeftValueInfo(nullptr, {}); } Value* Translator::TranslateThisOrSuperRef(const AST::RefExpr& refExpr) { CJC_ASSERT(refExpr.isThis || refExpr.isSuper); auto loc = TranslateLocation(refExpr); auto thisLeftValueInfo = TranslateThisOrSuperRefAsLeftValue(refExpr); CJC_ASSERT(thisLeftValueInfo.path.empty()); auto thisLeftValueBase = thisLeftValueInfo.base; auto thisLeftValueBaseTy = thisLeftValueInfo.base->GetType(); CJC_ASSERT(thisLeftValueBaseTy->GetRefDims() <= 1); if (thisLeftValueBaseTy->IsValueOrGenericTypeWithRefDims(1)) { auto loadThis = CreateAndAppendExpression( loc, StaticCast(thisLeftValueBaseTy)->GetBaseType(), thisLeftValueBase, currentBlock); return loadThis->GetResult(); } return thisLeftValueBase; } Value* Translator::TranslateVarRef(const AST::RefExpr& refExpr) { auto target = refExpr.ref.target; CJC_ASSERT(target); CJC_ASSERT(target->astKind == AST::ASTKind::VAR_DECL || target->astKind == AST::ASTKind::FUNC_PARAM); auto loc = TranslateLocation(refExpr); auto varLeftValueInfo = TranslateVarRefAsLeftValue(refExpr); auto varLeftValueBase = varLeftValueInfo.base; auto varLeftValueBaseTy = varLeftValueInfo.base->GetType(); // Case 1 non static member variables if (target->outerDecl != nullptr && !target->TestAttr(AST::Attribute::STATIC)) { auto path = varLeftValueInfo.path; // Case 1.1: non static member variables in struct or class if (target->outerDecl->astKind == AST::ASTKind::STRUCT_DECL || target->outerDecl->astKind == AST::ASTKind::CLASS_DECL) { auto thisCustomType = StaticCast(varLeftValueBaseTy->StripAllRefs()); CJC_ASSERT(varLeftValueBaseTy->IsReferenceTypeWithRefDims(1) || varLeftValueBaseTy->IsValueOrGenericTypeWithRefDims(1) || varLeftValueBaseTy->IsValueOrGenericTypeWithRefDims(0)); if (varLeftValueBaseTy->IsReferenceTypeWithRefDims(1) || varLeftValueBaseTy->IsValueOrGenericTypeWithRefDims(1)) { auto getMemberRef = CreateGetElementRefWithPath(loc, varLeftValueBase, path, currentBlock, *thisCustomType); auto memberType = StaticCast(getMemberRef->GetType())->GetBaseType(); auto loadMemberValue = CreateAndAppendExpression(loc, memberType, getMemberRef, currentBlock); return loadMemberValue->GetResult(); } else if (varLeftValueBaseTy->IsValueOrGenericTypeWithRefDims(0)) { auto memberType = GetInstMemberTypeByName(*thisCustomType, path, builder); auto getField = CreateAndAppendExpression(loc, memberType, varLeftValueBase, path, currentBlock); return getField->GetResult(); } } // Case 1.2: variable case in enum if (target->outerDecl->astKind == AST::ASTKind::ENUM_DECL) { CJC_ASSERT(path.empty()); return varLeftValueBase; } } // Case 2: global var, static member var, local var, func param CJC_ASSERT(varLeftValueInfo.path.empty()); if (varLeftValueBaseTy->IsReferenceTypeWithRefDims(CLASS_REF_DIM) || varLeftValueBaseTy->IsValueOrGenericTypeWithRefDims(1)) { auto loadVarRightValue = CreateAndAppendExpression( loc, StaticCast(varLeftValueBaseTy)->GetBaseType(), varLeftValueBase, currentBlock); return loadVarRightValue->GetResult(); } CJC_ASSERT(varLeftValueBaseTy->IsReferenceTypeWithRefDims(1) || varLeftValueBaseTy->IsValueOrGenericTypeWithRefDims(0)); return varLeftValueBase; } InvokeCallContext Translator::GenerateInvokeCallContext( const InstCalleeInfo& instFuncType, Value& caller, const AST::FuncDecl& callee, const std::vector& args) { auto funcType = builder.GetType(instFuncType.instParamTys, instFuncType.instRetTy); FuncCallType funcCallType{callee.identifier, funcType, instFuncType.instantiatedTypeArgs}; auto tempDecl = typeManager.GetTopOverriddenFuncDecl(&callee); const AST::FuncDecl* originalFuncDecl = tempDecl ? tempDecl.get() : &callee; auto originalFuncType = StaticCast(TranslateType(*originalFuncDecl->ty)); if (!originalFuncDecl->TestAttr(AST::Attribute::STATIC)) { auto outerDecl = originalFuncDecl->outerDecl; CJC_NULLPTR_CHECK(outerDecl); auto parentType = GetNominalSymbolTable(*outerDecl)->GetType(); if (parentType->IsReferenceType() || (parentType->IsStruct() && originalFuncDecl->TestAttr(AST::Attribute::MUT))) { parentType = builder.GetType(parentType); } auto paramTypes = originalFuncType->GetParamTypes(); paramTypes.insert(paramTypes.begin(), parentType); originalFuncType = builder.GetType(paramTypes, originalFuncType->GetReturnType()); } std::vector originalGenericTypeParams; if (originalFuncDecl->TestAttr(AST::Attribute::GENERIC)) { for (const auto& genericTy : originalFuncDecl->funcBody->generic->typeParameters) { originalGenericTypeParams.emplace_back(StaticCast(TranslateType(*(genericTy->ty)))); } } auto invokeInfo = InvokeCallContext { .caller = &caller, .funcCallCtx = FuncCallContext { .args = args, .instTypeArgs = instFuncType.instantiatedTypeArgs, .thisType = instFuncType.thisType }, .virMethodCtx = VirMethodContext { .srcCodeIdentifier = callee.identifier, .originalFuncType = originalFuncType, .genericTypeParams = originalGenericTypeParams } }; return invokeInfo; } Value* Translator::WrapMemberMethodByLambda( const AST::FuncDecl& funcDecl, const InstCalleeInfo& instFuncType, Value* thisObj) { // create Lambda auto topLevelFunc = currentBlock->GetTopLevelFunc(); CJC_NULLPTR_CHECK(topLevelFunc); auto lambdaMangledName = CHIRMangling::GenerateLambdaFuncMangleName(*topLevelFunc, lambdaWrapperIndex++); auto lambdaParamTypes = instFuncType.instParamTys; if (!funcDecl.TestAttr(AST::Attribute::STATIC)) { lambdaParamTypes.erase(lambdaParamTypes.begin()); } auto lambdaType = builder.GetType(lambdaParamTypes, instFuncType.instRetTy); Lambda* lambda = CreateAndAppendExpression( lambdaType, lambdaType, currentBlock, false, lambdaMangledName, funcDecl.identifier); // init body auto lambdaBlockGroup = builder.CreateBlockGroup(*topLevelFunc); lambda->InitBody(*lambdaBlockGroup); auto entry = builder.CreateBlock(lambdaBlockGroup); lambdaBlockGroup->SetEntryBlock(entry); // create parameter for (auto paramTy : lambdaParamTypes) { builder.CreateParameter(paramTy, INVALID_LOCATION, *lambda); } // create return value auto lambdaRetType = instFuncType.instRetTy; auto retVal = CreateAndAppendExpression( INVALID_LOCATION, builder.GetType(lambdaRetType), lambdaRetType, entry)->GetResult(); lambda->SetReturnValue(*retVal); // create lambda body auto currentBlockBackup = currentBlock; currentBlock = entry; auto lambdaArgs = lambda->GetParams(); std::vector args{lambdaArgs.begin(), lambdaArgs.end()}; Value* ret = nullptr; if (instFuncType.isVirtualFuncCall) { if (funcDecl.TestAttr(AST::Attribute::STATIC)) { // InvokeStatic CJC_ASSERT(thisObj == nullptr); auto rtti = CreateAndAppendExpression( builder.GetUnitTy(), instFuncType.thisType->StripAllRefs(), currentBlock)->GetResult(); auto invokeInfo = GenerateInvokeCallContext(instFuncType, *rtti, funcDecl, args); ret = CreateAndAppendExpression(lambdaRetType, invokeInfo, currentBlock)->GetResult(); } else { // Invoke CJC_NULLPTR_CHECK(thisObj); auto objExpectedType = instFuncType.instParentCustomTy; if (objExpectedType->IsReferenceType()) { objExpectedType = builder.GetType(objExpectedType); } if (thisObj->GetType() != objExpectedType) { thisObj = TypeCastOrBoxIfNeeded(*thisObj, *objExpectedType, INVALID_LOCATION); } auto invokeInfo = GenerateInvokeCallContext(instFuncType, *thisObj, funcDecl, args); ret = CreateAndAppendExpression(lambdaRetType, invokeInfo, currentBlock)->GetResult(); } } else { // Apply if (thisObj != nullptr) { auto objExpectedType = instFuncType.instParentCustomTy; if (objExpectedType->IsReferenceType()) { objExpectedType = builder.GetType(objExpectedType); } args.insert(args.begin(), TransformThisType(*thisObj, *objExpectedType, *lambda)); } CJC_ASSERT(args.size() == instFuncType.instParamTys.size()); std::vector instTypeArgs( instFuncType.instantiatedTypeArgs.begin(), instFuncType.instantiatedTypeArgs.end()); auto callee = GetSymbolTable(funcDecl); auto funcType = builder.GetType(instFuncType.instParamTys, instFuncType.instRetTy); auto wrapperFunc = GetWrapperFuncFromMemberAccess(*instFuncType.instParentCustomTy->StripAllRefs(), callee->GetSrcCodeIdentifier(), *funcType, callee->TestAttr(Attribute::STATIC), instTypeArgs); if (wrapperFunc != nullptr) { callee = wrapperFunc; } auto funcCallContext = FuncCallContext { .args = args, .instTypeArgs = instTypeArgs, .thisType = instFuncType.thisType }; ret = CreateAndAppendExpression( instFuncType.instRetTy, callee, funcCallContext, currentBlock)->GetResult(); } CreateWrappedStore(ret, retVal, currentBlock); CreateAndAppendTerminator(currentBlock); currentBlock = currentBlockBackup; return lambda->GetResult(); } Value* Translator::TranslateMemberFuncRef(const AST::RefExpr& refExpr) { auto target = refExpr.ref.target; CJC_ASSERT(target->astKind == ASTKind::FUNC_DECL); Value* thisObj = nullptr; if (!target->TestAttr(AST::Attribute::STATIC)) { thisObj = GetImplicitThisParam(); } auto instFuncType = GetInstCalleeInfoFromRefExpr(refExpr); if (!instFuncType.isVirtualFuncCall && target->TestAttr(AST::Attribute::STATIC)) { // just a shotcut, we can use `GetInstantiateValue` instead of `Lambda` auto targetFunc = GetSymbolTable(*target); return TranslateGlobalOrLocalFuncRef(refExpr, *targetFunc); } else { return WrapMemberMethodByLambda(*StaticCast(target), instFuncType, thisObj); } } Value* Translator::TranslateGlobalOrLocalFuncRef(const AST::RefExpr& refExpr, Value& originalFunc) { auto outerDeclaredTypes = GetOutDefDeclaredTypes(originalFunc); // 1. get inst types of outer custom type from current func's parent type if (originalFunc.IsFunc() && VirtualCast(&originalFunc)->IsMemberFunc() && GetCurrentFunc() && GetCurrentFunc()->GetParentCustomTypeDef() != nullptr) { /* orginalFunc may be defined in interface, try to get inst Types from current custom type interface I { static func foo() { 1 } } class B <: I { static public func me() { let a = foo a() } } we shuold get (Int32, T, Int64, T) for foo's class inst args. 1. get (T, U, V, W) from visiable generic types of I 2. replace with inst types get from me' custom type B, we got results (Int32, T, Int64, T). */ auto originCustomDef = VirtualCast(&originalFunc)->GetParentCustomTypeDef(); auto curFunc = GetCurrentFunc(); CJC_NULLPTR_CHECK(curFunc); auto parentFuncCustomDef = curFunc->GetParentCustomTypeDef(); CJC_ASSERT(originCustomDef && parentFuncCustomDef); if (originCustomDef != parentFuncCustomDef) { std::unordered_map instMap; // originCustomDef may bot be the direct parent, try get all inst map. GetInstMapFromCustomDefAndParent(*parentFuncCustomDef, instMap, builder); for (size_t i = 0; i < originCustomDef->GetGenericTypeParams().size(); i++) { if (!outerDeclaredTypes[i]->IsGeneric()) { continue; } auto found = instMap.find(StaticCast(outerDeclaredTypes[i])); if (found != instMap.end()) { outerDeclaredTypes[i] = found->second; } } } } // 2. get func inst types from AST std::vector instArgs; /** cj code like: * class A { * func foo() { * func goo() {} * var x = goo ==> create `GetInstantiateValue(goo, T1, T2, Bool)` * } * } */ // emplace back `T1` and `T2` CJC_ASSERT(outerDeclaredTypes.size() >= refExpr.instTys.size()); for (size_t i = 0; i < outerDeclaredTypes.size() - refExpr.instTys.size(); ++i) { instArgs.emplace_back(outerDeclaredTypes[i]); } // emplace back `Bool` for (auto ty : refExpr.instTys) { instArgs.emplace_back(TranslateType(*ty)); } if (instArgs.empty()) { return &originalFunc; } // 3. create GetInstantiateValue auto resTy = TranslateType(*refExpr.ty); auto loc = TranslateLocation(refExpr); return CreateAndAppendExpression(loc, resTy, &originalFunc, instArgs, currentBlock) ->GetResult(); } Value* Translator::TranslateFuncRef(const AST::RefExpr& refExpr) { auto target = refExpr.ref.target; CJC_ASSERT(target); CJC_ASSERT(target->astKind == AST::ASTKind::FUNC_DECL); auto loc = TranslateLocation(refExpr); if (target->outerDecl != nullptr && target->outerDecl->IsNominalDecl()) { // Case 1: member method return TranslateMemberFuncRef(refExpr); } else { // Case 2: global or local function auto targetFunc = GetSymbolTable(*target); return TranslateGlobalOrLocalFuncRef(refExpr, *targetFunc); } } Ptr Translator::Visit(const AST::RefExpr& refExpr) { // Case 1: `this` or `super` if (refExpr.isThis || refExpr.isSuper) { return TranslateThisOrSuperRef(refExpr); } // Case 2: variable auto target = refExpr.ref.target; CJC_ASSERT(target); if (target->astKind == AST::ASTKind::VAR_DECL || target->astKind == AST::ASTKind::FUNC_PARAM) { return TranslateVarRef(refExpr); } // Case 3: func if (target->astKind == AST::ASTKind::FUNC_DECL) { return TranslateFuncRef(refExpr); } CJC_ABORT(); return nullptr; } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateReturnExpr.cpp000066400000000000000000000105561510705540100273240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" using namespace Cangjie::CHIR; using namespace Cangjie; int64_t Translator::CalculateDelayExitLevelForReturn() { // If there are other blockGroup exprs, please modify the condition here. // for example: WHILE, LOOP... int64_t level = 0; Ptr bg = blockGroupStack.back(); for (auto rBegin = blockGroupStack.crbegin(), rEnd = blockGroupStack.crend(); rBegin != rEnd; ++rBegin) { bg = *rBegin; CJC_NULLPTR_CHECK(currentBlock->GetTopLevelFunc()); auto parentLambda = DynamicCast(bg->GetOwnerExpression()); if (bg == currentBlock->GetTopLevelFunc()->GetBody() || parentLambda != nullptr) { break; } Expression* expr = bg->GetOwnerExpression(); if (expr && Is(expr)) { // cond blockGroup need sub 1, and in the end of for-in node // need also sub 1, so here set to 2. level += 2U; } else if (expr && expr->GetExprKind() == ExprKind::IF) { ++level; } } return level; } int64_t Translator::CalculateDelayExitLevelForThrow() { return CalculateDelayExitLevelForReturn() + 1; } Ptr Translator::GetOuterBlockGroupReturnValLocation() { for (auto reverseBegin = blockGroupStack.crbegin(), reverseEnd = blockGroupStack.crend(); reverseBegin != reverseEnd; ++reverseBegin) { Ptr bg = *reverseBegin; Expression* ownedExpr = bg->GetOwnerExpression(); if (ownedExpr && (Is(ownedExpr) || ownedExpr->GetExprKind() == ExprKind::IF) && forInExprReturnMap.count(ownedExpr->GetResult()) != 0) { return forInExprReturnMap[ownedExpr->GetResult()]; } } if (blockGroupStack.empty()) { return nullptr; } auto curBlockGroup = blockGroupStack.back(); if (auto func = curBlockGroup->GetOwnerFunc()) { return func->GetReturnValue(); } else if (auto lambda = DynamicCast(curBlockGroup->GetOwnerExpression())) { return lambda->GetReturnValue(); } return nullptr; } Ptr Translator::Visit(const AST::ReturnExpr& expr) { const auto& loc = TranslateLocation(expr); CJC_NULLPTR_CHECK(expr.expr); auto retVal = TranslateExprArg(*expr.expr); int64_t level = CalculateDelayExitLevelForReturn(); Ptr ret = GetOuterBlockGroupReturnValLocation(); if (ret != nullptr) { if (expr.TestAttr(AST::Attribute::COMPILER_ADD)) { CreateWrappedStore(retVal, ret, currentBlock); } else { CreateWrappedStore(loc, retVal, ret, currentBlock); } } if (level > 0 && delayExitSignal) { UpdateDelayExitSignal(level); } Ptr terminator = nullptr; if (finallyContext.empty()) { terminator = CreateAndAppendTerminator(loc, currentBlock); /* compile add return expr should not print warning. public open func foo(): Int64 { unsafe{ ---------------> will have a compiler add return expr. return 3 } } */ if (expr.TestAttr(AST::Attribute::COMPILER_ADD)) { terminator->Set(SkipKind::SKIP_DCE_WARNING); } } else { // When current is in try-finally context, store the control blocks with control kind value. auto& [_, controlBlocks] = finallyContext.top(); auto index = static_cast(ControlType::RETURN); auto prevBlock = currentBlock; // Create return in separate block, and control flow will be redirected to this block at the end of finally. currentBlock = CreateBlock(); terminator = CreateAndAppendTerminator(loc, currentBlock); // the pair of blocks is {the block before control flow, control flow's target block}. controlBlocks[index].emplace_back(prevBlock, currentBlock); } // For following unreachable expressions, and return also has value of type 'Nothing'. currentBlock = CreateBlock(); maybeUnreachable.emplace(currentBlock, terminator); return nullptr; } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateSpawnExpr.cpp000066400000000000000000000020541510705540100271270ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" using namespace Cangjie::CHIR; using namespace Cangjie; Ptr Translator::Visit(const AST::SpawnExpr& spawnExpr) { auto loc = TranslateLocation(spawnExpr); // VarDecl of 'futureObj' can be ignored, only generate initializer here. auto futureObj = TranslateExprArg(*spawnExpr.futureObj->initializer); Value* spawnArg = nullptr; if (spawnExpr.arg && spawnExpr.arg->desugarExpr) { spawnArg = TranslateExprArg(*spawnExpr.arg->desugarExpr.get()); } if (spawnArg) { TryCreate(currentBlock, loc, chirTy.TranslateType(*spawnExpr.ty), futureObj, spawnArg); } else { TryCreate(currentBlock, loc, chirTy.TranslateType(*spawnExpr.ty), futureObj); } return futureObj; } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateStructDecl.cpp000066400000000000000000000052421510705540100272560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/Modules/ModulesUtils.h" using namespace Cangjie::CHIR; using namespace Cangjie; Ptr Translator::Visit(const AST::StructDecl& decl) { auto def = GetNominalSymbolTable(decl); CJC_ASSERT(def->GetCustomKind() == CustomDefKind::TYPE_STRUCT); auto structDef = StaticCast(def.get()); // step 1: set annotation info CreateAnnotationInfo(decl, *structDef, structDef); // set type auto chirType = StaticCast(chirTy.TranslateType(*decl.ty)); structDef->SetType(*chirType); structDef->Set(decl.TestAttr(AST::Attribute::GENERIC_INSTANTIATED) ? Linkage::INTERNAL : decl.linkage); // translate func and prop const auto& memberDecl = decl.GetMemberDeclPtrs(); for (auto& member : memberDecl) { if (!ShouldTranslateMember(decl, *member)) { continue; } if (member->astKind == AST::ASTKind::VAR_DECL) { AddMemberVarDecl(*structDef, *RawStaticCast(member)); } else if (member->astKind == AST::ASTKind::FUNC_DECL) { auto funcDecl = StaticCast(member); AddMemberMethodToCustomTypeDef(*funcDecl, *structDef); } else if (member->astKind == AST::ASTKind::PROP_DECL) { AddMemberPropDecl(*structDef, *RawStaticCast(member)); } else if (member->astKind == AST::ASTKind::PRIMARY_CTOR_DECL) { // do nothing, primary constructor decl has been desugared to func decl } else { CJC_ABORT(); } } // translate var init Translator trans = Copy(); structDef->SetVarInitializationFunc(trans.TranslateVarsInit(decl)); // set implemented interface for (auto& superInterfaceTy : decl.GetStableSuperInterfaceTys()) { auto astType = TranslateType(*superInterfaceTy); // The implemented interface type must be of reference type. CJC_ASSERT(astType->IsRef()); auto realType = StaticCast(StaticCast(astType)->GetBaseType()); structDef->AddImplementedInterfaceTy(*realType); } // collect annotation info of the type and members for annotation target check CollectTypeAnnotation(decl, *def); return nullptr; } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateSubscriptExpr.cpp000066400000000000000000000115571510705540100300250ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/IntrinsicKind.h" #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" using namespace Cangjie::CHIR; using namespace Cangjie; // Let's take an example to analyse. // Given a two-dimensional tuple named `arr`, // we first declare a tuple that contains the tuple, like: // var tuple = ( true, arr ) // If we would like to access an element in `arr`, like: // tuple[1][4][5] // -------------- // | // --> It is a `SubscriptExpr`, of which the `baseExpr` // is `tuple[1][4]`, `indexExpr` values `5` // Then as for tuple[1][4], // ----------- // | // ---> It is a `SubscriptExpr`, of which the `baseExpr` // is `tuple[1]`, `indexExpr` values `4` // With regard to tuple[1], although it is a `SubscriptExpr`, it is not a subscript // access to a tuple, thus we have reached the outermost tuple node, aka tuple[1]. // During the process, we can also collect the all indexes to access the // place of tuple element. // varray has the same process except that the index has no literal limit. Ptr Translator::Visit(const AST::SubscriptExpr& subscriptExpr) { // Currently it can only be an access to a tuple or a varray, // all the other `ob[idx]` will be desugared to a CallExpr. CJC_ASSERT(subscriptExpr.indexExprs.size() == 1); if (subscriptExpr.isTupleAccess) { return TranslateTupleAccess(subscriptExpr); } if (subscriptExpr.IsVArrayAccess()) { return TranslateVArrayAccess(subscriptExpr); } CJC_ASSERT(false && "Certainly won't get here in translating subscriptExpr."); return nullptr; } Ptr Translator::TranslateTupleAccess(const AST::SubscriptExpr& subscriptExpr) { const auto& loc = TranslateLocation(subscriptExpr); auto se = &subscriptExpr; std::list indexs; AST::Expr* baseExpr = nullptr; for (; se != nullptr && se->isTupleAccess; se = DynamicCast(se->baseExpr.get())) { baseExpr = se->baseExpr.get(); indexs.emplace_front(se->indexExprs[0]->constNumValue.asInt.Uint64()); } CJC_NULLPTR_CHECK(baseExpr); auto base = TranslateExprArg(*baseExpr); if (base->GetType()->IsRef()) { base = CreateAndAppendExpression( loc, StaticCast(base->GetType())->GetBaseType(), base, currentBlock)->GetResult(); } auto res = CreateAndAppendExpression(loc, chirTy.TranslateType(*subscriptExpr.ty), base, std::vector(indexs.cbegin(), indexs.cend()), currentBlock); /* If the SubscriptExpr is added by compiler, DCE will skip it. for example code: let a:Int64 let b:Int64 (a, b, _) = (1, 2, 3) --------> var tmp = (1, 2, 3); a = tmp[0]; b =tmp[1]; _= tmp[2] */ if (subscriptExpr.TestAttr(AST::Attribute::IMPLICIT_ADD)) { res->Set(SkipKind::SKIP_DCE_WARNING); } return res->GetResult(); } Ptr Translator::TranslateVArrayAccess(const AST::SubscriptExpr& subscriptExpr) { const auto& loc = TranslateLocation(subscriptExpr); auto se = &subscriptExpr; std::vector indexs; std::vector indexExprs; AST::Expr* baseExpr = nullptr; for (; se != nullptr && se->IsVArrayAccess(); se = DynamicCast(se->baseExpr.get())) { baseExpr = se->baseExpr.get(); indexExprs.push_back(se->indexExprs[0].get()); } CJC_NULLPTR_CHECK(baseExpr); // make sure that the more left `index` is, the earlier to be calculated. // i.e a[foo1()][foo2()] // foo1 is called earlier than foo2. // // additionally, in this example, it's a SubscriptExpr which consists of a baseExpr `a[foo1()]` // and an index `foo2()`, and then the baseExpr is also a SubscriptExpr, // which consists of a baseExpr `a` and an index `foo1()`. for (auto it = indexExprs.crbegin(); it != indexExprs.crend(); ++it) { auto index = TranslateExprArg(**it); if (index->GetType()->IsRef()) { index = CreateAndAppendExpression( StaticCast(index->GetType())->GetBaseType(), index, currentBlock)->GetResult(); } indexs.push_back(index); } auto base = TranslateExprArg(*baseExpr); indexs.insert(indexs.begin(), base); auto callContext = IntrisicCallContext { .kind = IntrinsicKind::VARRAY_GET, .args = indexs }; return CreateAndAppendExpression( loc, chirTy.TranslateType(*subscriptExpr.ty), callContext, currentBlock)->GetResult(); } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateThrowExpr.cpp000066400000000000000000000021511510705540100271400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" using namespace Cangjie::CHIR; using namespace Cangjie; Ptr Translator::Visit(const AST::ThrowExpr& throwExpr) { auto loc = TranslateLocation(throwExpr.begin, throwExpr.end); CJC_NULLPTR_CHECK(throwExpr.expr); auto eVal = TranslateExprArg(*throwExpr.expr); Ptr terminator = nullptr; if (tryCatchContext.empty()) { terminator = CreateAndAppendTerminator(loc, eVal, currentBlock); } else { auto errBB = tryCatchContext.top(); terminator = CreateAndAppendTerminator(loc, eVal, errBB, currentBlock); } // For following unreachable expressions, and throw also has value of type 'Nothing'. currentBlock = CreateBlock(); maybeUnreachable.emplace(currentBlock, terminator); return nullptr; } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateTryExpr.cpp000066400000000000000000000347451510705540100266310ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" using namespace Cangjie::CHIR; using namespace Cangjie; /** * Type of try expression may be type of expected target, * we need to check try body and all catch blocks for real nothing typed control flow. */ static inline bool HasTypeOfNothing(const AST::TryExpr& tryExpr) { if (!tryExpr.tryBlock->ty->IsNothing()) { return false; } return std::all_of( tryExpr.catchBlocks.cbegin(), tryExpr.catchBlocks.cend(), [](auto& it) { return it->ty->IsNothing(); }); } Ptr Translator::Visit(const AST::TryExpr& tryExpr) { // Try must have at least one catch block or finally block. CJC_ASSERT(!tryExpr.catchBlocks.empty() || tryExpr.finallyBlock != nullptr); auto tryTy = TranslateType(*tryExpr.ty); auto loc = TranslateLocation(tryExpr.begin, tryExpr.end); auto retVal = tryTy->IsUnit() || tryTy->IsNothing() ? nullptr : CreateAndAppendExpression(loc, builder.GetType(tryTy), tryTy, currentBlock)->GetResult(); bool hasFinally = tryExpr.finallyBlock != nullptr; Ptr endBlock = CreateBlock(); // Used for checking scope info with control flow. NOTE: record scope info before update the 'ScopeContext' endBlock->SetDebugLocation(loc); CreateFinallyExpr(hasFinally, endBlock); ScopeContext context(*this); context.ScopePlus(); tryBodyContext.emplace_back(&tryExpr); // 1. Set context for try body & generate try. Ptr exceptionBlock = CreateBlock(); { tryCatchContext.emplace(exceptionBlock); if (tryExpr.TestAttr(AST::Attribute::MACRO_INVOKE_BODY)) { auto pkgInit = builder.GetCurPackage()->GetPackageInitFunc(); CJC_NULLPTR_CHECK(pkgInit); // ApplyWithException will create and update current block. GenerateFuncCall(*pkgInit, StaticCast(pkgInit->GetType()), std::vector{}, nullptr, std::vector{}, INVALID_LOCATION); } auto baseBlock = currentBlock; auto tryVal = TranslateExprArg(*tryExpr.tryBlock); CreateAndAppendTerminator(loc, GetBlockByAST(*tryExpr.tryBlock), baseBlock); if (!HasTypeOfNothing(tryExpr)) { // Add location for print warning. // example code: // var a = try{throw A();1} catch (a:A){2} ----> the const `1` have a user:`store`, so we shoule add loc // for `store`. DebugLocation valLoc; if (tryVal->IsLocalVar()) { valLoc = StaticCast(tryVal)->GetExpr()->GetDebugLocation(); } else { valLoc = TranslateLocation(*tryExpr.tryBlock); } if (retVal) { CreateWrappedStore(valLoc, GetDerefedValue(tryVal, valLoc), retVal, currentBlock); } } CreateAndAppendTerminator(loc, endBlock, currentBlock)->Set(SkipKind::SKIP_DCE_WARNING); tryCatchContext.pop(); } // 2. Set branch for catching exception. if (!tryExpr.catchBlocks.empty()) { currentBlock = exceptionBlock; exceptionBlock->SetExceptions(GetExceptionsForTry(tryExpr)); context.ScopePlus(); exceptionBlock = TranslateCatchBlocks(tryExpr, retVal, endBlock); } // 3. Translate finally block with normal and exception cases. // NOTE: if finally is existed create 'endBlock' goto 'finally' and 'exceptionBlock' goto finally block, // then update endBlock with new end block of finally. if (hasFinally) { CJC_ASSERT(exceptionBlock); context.ScopePlus(); endBlock = TranslateFinally(*tryExpr.finallyBlock, exceptionBlock); } tryBodyContext.pop_back(); currentBlock = endBlock; if (HasTypeOfNothing(tryExpr)) { return nullptr; } if (!retVal) { return nullptr; } else { auto res = CreateAndAppendExpression(loc, tryTy, retVal, currentBlock); return res->GetResult(); } } void Translator::CreateFinallyExpr(bool hasFinally, Ptr endBlock) { if (hasFinally) { // NOTE: 'finallyContext' will be pop out inside 'TranslateFinally'. auto context = FinallyControlVal(endBlock); context.controlFlowBlocks[static_cast(ControlType::NORMAL)].emplace_back(endBlock, CreateBlock()); finallyContext.emplace(context); } } Ptr Translator::TranslateFinally(const AST::Block& finally, Ptr exceptionBlock) { CJC_ASSERT(!finallyContext.empty()); // 1. Generate rethrow finally block. NOTE: finally context should be kept for rethrow. currentBlock = exceptionBlock; TranslateFinallyRethrowFlows(finally); auto finallyControlInfo = finallyContext.top(); finallyContext.pop(); // 2. Generate normal finally block for different control flows. TranslateFinallyNormalFlows(finally, finallyControlInfo); // Return end block of normal flow. auto normalInfo = finallyControlInfo.controlFlowBlocks[static_cast(ControlType::NORMAL)]; CJC_ASSERT(normalInfo.size() == 1); return normalInfo[0].second; } void Translator::TranslateFinallyRethrowFlows(const AST::Block& finally) { // NOTE: Intrinsic/beginCatch must be branched from exception, // so the normal block of try cannot branch to the block start with Intrinsic/beginCatch. // Then the body of finally must be generated twice. auto exceptionBlock = currentBlock; Translator exceptionTrans = *this; // Copy a translator for exception finally. CJC_ASSERT(!exceptionTrans.finallyContext.empty()); // Clear control flows before generating finally for rethrow flow. for (auto& it : exceptionTrans.finallyContext.top().controlFlowBlocks) { it.clear(); } currentBlock->SetExceptions({}); auto baseETy = builder.GetType(builder.GetObjectTy()); auto exception = CreateAndAppendExpression(baseETy, currentBlock)->GetResult(); auto callContext = IntrisicCallContext { .kind = IntrinsicKind::BEGIN_CATCH, .args = std::vector{exception} }; auto eVal = CreateAndAppendExpression(baseETy, callContext, currentBlock)->GetResult(); // Generate rethrow finally block. TranslateASTNode(finally, exceptionTrans); auto finallyBlock = exceptionTrans.GetBlockByAST(finally); CreateAndAppendTerminator(finallyBlock, exceptionBlock); auto endCatchBB = CreateBlock(); CreateAndAppendTerminator(endCatchBB, exceptionTrans.currentBlock); if (tryCatchContext.empty()) { CreateAndAppendTerminator(eVal, endCatchBB); } else { auto errBB = tryCatchContext.top(); CreateAndAppendTerminator(eVal, errBB, endCatchBB); } // The control flows recorded during generating rethrow finally should be redirected to end of rethrow. auto& [_, outerControlBlocks] = exceptionTrans.finallyContext.top(); for (size_t i = 0; i < outerControlBlocks.size(); ++i) { auto& info = outerControlBlocks[i]; // Generate goto rethrow exception for other type of control flows. for (auto [prevBlock, _1] : info) { CreateAndAppendTerminator(endCatchBB, prevBlock); } } } void Translator::TranslateFinallyNormalFlows(const AST::Block& finally, const FinallyControlVal& finallyControlInfo) { for (size_t i = 0; i < finallyControlInfo.controlFlowBlocks.size(); ++i) { auto& info = finallyControlInfo.controlFlowBlocks[i]; if (info.empty()) { continue; } for (auto [prevBlock, nextBlock] : info) { Translator normalTrans = *this; // Copy a translator for normal finally. TranslateASTNode(finally, normalTrans); auto finallyBlock = normalTrans.GetBlockByAST(finally); CreateAndAppendTerminator(finallyBlock, prevBlock); finallyBlock->Set(SkipKind::SKIP_DCE_WARNING); prevBlock->Set(SkipKind::SKIP_DCE_WARNING); bool isNormalCase = i == static_cast(ControlType::NORMAL); if (normalTrans.finallyContext.empty() || isNormalCase) { CreateAndAppendTerminator(nextBlock, normalTrans.currentBlock); } else { // When there is another finally outside current tryExpr, generate control flow with outsider finally. auto& [outerBlock, outerControlBlocks] = normalTrans.finallyContext.top(); auto outerTryScopeLevel = outerBlock->GetDebugLocation().GetScopeInfo().size(); auto controlScopeLevel = nextBlock->GetDebugLocation().GetScopeInfo().size(); // When control flow type is return or its scope level is outside(smaller) the outer try-finally, // generate 'goto new end Blo' for current control type and store the control kind value. if (static_cast(i) == ControlType::RETURN || controlScopeLevel < outerTryScopeLevel) { outerControlBlocks[i].emplace_back(normalTrans.currentBlock, nextBlock); } else { CreateAndAppendTerminator(nextBlock, normalTrans.currentBlock); } } finallyContext = normalTrans.finallyContext; // Update context info. } } } std::vector Translator::GetExceptionsForTry(const AST::TryExpr& tryExpr) { // Collect exception of outer try start from nearest. std::vector> allTryExprs = tryBodyContext; allTryExprs.emplace_back(&tryExpr); for (const auto& it : allTryExprs) { // catch all Exceptions if outer or this try has finally if (it->finallyBlock != nullptr) { return {}; } } std::set> exceptionTys; auto collectTys = [&exceptionTys](auto ty) { auto gty = DynamicCast(ty); if (!gty) { exceptionTys.emplace(ty); return; } for (auto it : gty->upperBounds) { // Temporarily collecting all class upper bounds. if (it->IsClass()) { exceptionTys.emplace(it); } } }; for (auto it = allTryExprs.crbegin(); it != allTryExprs.crend(); ++it) { auto currentTry = *it; for (auto& pattern : currentTry->catchPatterns) { if (pattern->astKind == AST::ASTKind::WILDCARD_PATTERN) { collectTys(pattern->ty); continue; } auto& exceptPattern = StaticCast(*pattern); for (auto& eType : exceptPattern.types) { collectTys(eType->ty); } } } auto cmp = [](const Ptr& ty1, const Ptr& ty2) { return ty1->ToString() < ty2->ToString(); }; std::set exceptions(cmp); for (auto ty : exceptionTys) { auto classTypeRef = StaticCast(TranslateType(*ty)); exceptions.emplace(StaticCast(classTypeRef->GetBaseType())); } return std::vector(exceptions.begin(), exceptions.end()); } Ptr Translator::TranslateCatchBlocks(const AST::TryExpr& tryExpr, Ptr retVal, Ptr endBlock) { bool hasFinally = tryExpr.finallyBlock != nullptr; // When finally is not existed, do not need to create re-throw block. Ptr exceptionBlock = hasFinally ? CreateBlock() : nullptr; if (hasFinally) { tryCatchContext.push(exceptionBlock); } auto baseETy = builder.GetType(builder.GetObjectTy()); auto catchLoc = TranslateLocation(*tryExpr.catchBlocks[0]); auto exception = CreateAndAppendExpression(catchLoc, baseETy, currentBlock)->GetResult(); auto callContext = IntrisicCallContext { .kind = IntrinsicKind::BEGIN_CATCH, .args = std::vector{exception} }; auto eVal = CreateAndAppendExpression(catchLoc, baseETy, callContext, currentBlock)->GetResult(); auto catchSize = tryExpr.catchPatterns.size(); CJC_ASSERT(tryExpr.catchBlocks.size() == catchSize); for (size_t i = 0; i < catchSize; ++i) { auto [falseBlock, trueBlock] = TranslateExceptionPattern(*tryExpr.catchPatterns[i], eVal); ScopeContext context(*this); context.ScopePlus(); auto currentBody = TranslateMatchCaseBody(*tryExpr.catchBlocks[i], retVal, endBlock); CreateAndAppendTerminator(currentBody, trueBlock); currentBlock = falseBlock; } if (hasFinally) { CreateAndAppendTerminator(eVal, exceptionBlock, currentBlock); tryCatchContext.pop(); } else if (tryCatchContext.empty()) { // There exists rethrow context outside the current tryExpr. CreateAndAppendTerminator(eVal, currentBlock); } else { auto errBB = tryCatchContext.top(); CreateAndAppendTerminator(eVal, errBB, currentBlock); } return exceptionBlock; } std::pair, Ptr> Translator::TranslateExceptionPattern(const AST::Pattern& pattern, Ptr eVal) { auto trueBlock = CreateBlock(); const auto& loc = TranslateLocation(pattern); if (pattern.astKind == AST::ASTKind::WILDCARD_PATTERN) { auto typeTy = TranslateType(*pattern.ty); auto falseBlock = CreateBlock(); auto cond = CreateAndAppendExpression(loc, builder.GetBoolTy(), eVal, typeTy, currentBlock)->GetResult(); CreateAndAppendTerminator(cond, trueBlock, falseBlock, currentBlock); return {falseBlock, trueBlock}; } auto& exceptPattern = StaticCast(pattern); CJC_ASSERT(exceptPattern.pattern); if (exceptPattern.pattern->astKind == AST::ASTKind::VAR_PATTERN) { HandleVarPattern(StaticCast(*exceptPattern.pattern), eVal, trueBlock); } for (auto& eType : exceptPattern.types) { auto falseBlock = CreateBlock(); auto ty = TranslateType(*eType->ty); auto cond = CreateAndAppendExpression(loc, builder.GetBoolTy(), eVal, ty, currentBlock)->GetResult(); CreateAndAppendTerminator(cond, trueBlock, falseBlock, currentBlock); currentBlock = falseBlock; } return {currentBlock, trueBlock}; } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateTupleLit.cpp000066400000000000000000000015661510705540100267510ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" using namespace Cangjie::CHIR; using namespace Cangjie; Ptr Translator::Visit(const AST::TupleLit& tuple) { std::vector argVals; auto tupleType = StaticCast(tuple.ty); for (size_t i = 0; i < tuple.children.size(); i++) { auto arg = TranslateExprArg(*tuple.children[i], *TranslateType(*tupleType->typeArgs[i])); argVals.push_back(arg); } auto ty = TranslateType(*tuple.ty); return CreateAndAppendExpression(TranslateLocation(tuple), ty, argVals, currentBlock) ->GetResult(); }cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateTypeConvExpr.cpp000066400000000000000000000022461510705540100276110ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" using namespace Cangjie::CHIR; using namespace Cangjie; Ptr Translator::Visit(const AST::TypeConvExpr& typeConvExpr) { const auto& loc = TranslateLocation(typeConvExpr); auto chirType = TranslateType(*typeConvExpr.ty); auto operand = TranslateExprArg(*typeConvExpr.expr); auto srcTy = typeConvExpr.expr->ty; auto targetTy = typeConvExpr.ty.get(); if (srcTy->IsFunc() || srcTy->IsTuple()) { return CreateWrappedTypeCast(loc, chirType, operand, currentBlock)->GetResult(); } auto ofs = typeConvExpr.overflowStrategy; auto noException = (srcTy->IsInteger() && targetTy->IsInteger()) && ofs != OverflowStrategy::THROWING; auto opLoc = TranslateLocation(*typeConvExpr.expr); auto newNode = TryCreateWithOV(currentBlock, !noException, ofs, loc, chirType, operand); return newNode->GetResult(); } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateUnaryExpr.cpp000066400000000000000000000025211510705540100271340ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" using namespace Cangjie::CHIR; using namespace Cangjie; Ptr Translator::Visit(const AST::UnaryExpr& unaryExpr) { auto chirType = TranslateType(*unaryExpr.ty); ExprKind kd = ExprKind::INVALID; if (unaryExpr.op == Cangjie::TokenKind::NOT) { kd = unaryExpr.ty->IsBoolean() ? ExprKind::NOT : ExprKind::BITNOT; } else if (unaryExpr.op == Cangjie::TokenKind::SUB) { kd = ExprKind::NEG; } else { CJC_ASSERT(false && "Visit UnaryExpr: invalid unary operation!"); } auto chirExpr = TranslateExprArg(*unaryExpr.expr); const auto& loc = TranslateLocation(unaryExpr.begin, unaryExpr.end); auto ofs = unaryExpr.overflowStrategy; bool mayHaveException = OverloadableExprMayThrowException(unaryExpr, *chirType); auto opLoc = TranslateLocation(*unaryExpr.expr); const auto& operatorLoc = GetOperatorLoc(unaryExpr); return TryCreateWithOV( currentBlock, mayHaveException, ofs, operatorLoc, loc, chirType, kd, chirExpr) ->GetResult(); }cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateVarDecl.cpp000066400000000000000000000141511510705540100265210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/CHIR/Utils.h" using namespace Cangjie::CHIR; using namespace Cangjie; namespace { // Static member func and global func should create alloc node for print `unused variable` warning. bool ShouldCreateAlloc(const AST::VarDecl& decl, const Cangjie::GlobalOptions& opts, bool rValueIsEmpty) { if (decl.isConst) { // rvalue is empty means this is a const local variable in member function, which requires an Allocate return !rValueIsEmpty; } if (rValueIsEmpty || decl.isVar || opts.enableCompileDebug || opts.enableCoverage) { return true; } // Note: example cangjie code: let (a, b) : (Int64, Int64) = (1, 2) // In this scenario, `rValueIsEmpty` is false, but initializer of varDecl desugared by VarWithPattern is nullptr, // so we can't create an `allocate` node by determining whether the initializer is nullptr, because redundant // `allocate` and `store` will affect the performance. if (decl.initializer && decl.initializer->astKind == AST::ASTKind::REF_EXPR) { auto& refExpr = StaticCast(*decl.initializer); if (refExpr.ref.target && refExpr.ref.target->astKind == AST::ASTKind::FUNC_DECL && refExpr.ref.target->TestAnyAttr(AST::Attribute::GLOBAL, AST::Attribute::STATIC)) { return true; } } if (decl.initializer && decl.initializer->astKind == AST::ASTKind::MEMBER_ACCESS) { auto target = StaticCast(*decl.initializer).target; if (target && target->astKind == AST::ASTKind::FUNC_DECL && target->TestAttr(AST::Attribute::STATIC)) { return true; } } return false; } } // namespace Ptr Translator::TranslateLeftValueOfVarDecl(const AST::VarDecl& decl, bool rValueIsEmpty, bool isLocalVar) { Ptr leftValue = nullptr; auto varPos = TranslateLocation(decl); auto leftType = TranslateType(*(decl.ty)); if (isLocalVar) { // need to create allocate node: // 1. mutable variable: `var a = 1` // 2. has no initializer: `var a: Int32` or `let a: Int32` // 3. if 'debug' is enabled, all decl must be allocated. // 4. global func and Member static func should create `Alloc` for print `unused variable` warning. if (ShouldCreateAlloc(decl, opts, rValueIsEmpty)) { auto allocate = CreateAndAppendExpression(varPos, builder.GetType(leftType), leftType, currentBlock); leftValue = allocate->GetResult(); // Debug is only useful for allocated value, but not for implicit added // it will generate bug in cjdb if we generate debug expr for implicit added if (!decl.TestAttr(AST::Attribute::IMPLICIT_ADD)) { CreateAndAppendExpression(GetVarLoc(builder.GetChirContext(), decl), varPos, builder.GetUnitTy(), leftValue, decl.identifier, currentBlock); } leftValue->AppendAttributeInfo(BuildVarDeclAttr(decl)); } } else { leftValue = GetSymbolTable(decl); leftValue->AppendAttributeInfo(BuildVarDeclAttr(decl)); } return leftValue; } void Translator::StoreRValueToLValue(const AST::VarDecl& decl, Value& rval, Ptr& lval) { auto leftType = TranslateType(*(decl.ty)); auto varPos = TranslateLocation(decl); if (lval != nullptr) { CreateWrappedStore(varPos, TypeCastOrBoxIfNeeded(rval, *leftType, varPos), lval, currentBlock); } else { auto warnPos = GetVarLoc(builder.GetChirContext(), decl); lval = TypeCastOrBoxIfNeeded(rval, *leftType, varPos); // If disable `-g` option, let variable still need create `Debug` node for DCE print warning. // Note: there is a special case exist only disable '-g' option: the LocalVar's debug node will be overwritten, // but does not affect debugging information, because disable '-g' option, codegen will skip `Debug` node. // the example code: // let a = 1 --->%1 = Debug(1, a) the constant `1`'s Debug node is %1 // let b = a --->%2 = Debug(1, b) the constant `1`'s Debug node is replaced by %2. // Also note that Debug expr must be placed on T&, typically an Allocate, and const local variable is never // allocated. if (!decl.TestAttr(AST::Attribute::IMPLICIT_ADD) && !decl.isConst) { // doesn't need to create allocate node, but have `debug` node. // 1. immutable variable and has initializer: `let a: Int32 = 1` auto debugInfo = CreateAndAppendExpression( warnPos, varPos, builder.GetUnitTy(), lval.get(), decl.identifier, currentBlock); debugInfo->GetResult()->EnableAttr(Attribute::READONLY); } } } Ptr Translator::Visit(const AST::VarDecl& decl) { CJC_ASSERT(!decl.TestAttr(AST::Attribute::GLOBAL)); // The local const var is lifted as global variable, thus not handled here if (localConstVars.HasElement(&decl)) { return nullptr; } // Since current block will be changed during translation, // creation order should be 'alloca' -> 'debug' -> 'init' -> 'store'. Ptr init = decl.initializer.get(); Ptr leftValue = TranslateLeftValueOfVarDecl(decl, init == nullptr, true); if (init != nullptr) { // Need to try to get rvalue, otherwise next step will cast reftype to non-reftype. eg: cast Int64& to Int64. auto oldCompileTimeValueMark = builder.GetCompileTimeValueMark(); builder.SetCompileTimeValueMark(decl.isConst); Ptr initNode = TranslateExprArg(*init); builder.SetCompileTimeValueMark(oldCompileTimeValueMark); StoreRValueToLValue(decl, *initNode, leftValue); } SetSymbolTable(decl, *leftValue); return leftValue; }cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/TranslateVarWithPatternDecl.cpp000066400000000000000000000077441510705540100307250ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/Utils.h" using namespace Cangjie::CHIR; using namespace Cangjie; void Translator::HandleVarWithVarPattern( const AST::VarPattern& pattern, const Ptr& initNode, bool isLocalPattern) { auto varDecl = pattern.varDecl.get(); Ptr leftValue = TranslateLeftValueOfVarDecl(*varDecl, initNode == nullptr, isLocalPattern); if (initNode != nullptr) { StoreRValueToLValue(*varDecl, *initNode, leftValue); if (leftValue->IsGlobalVarInCurPackage()) { CJC_ASSERT(GetTopLevelFunc(*initNode)); VirtualCast(leftValue)->SetInitFunc(*GetTopLevelFunc(*initNode)); } } SetSymbolTable(*varDecl, *leftValue); } void Translator::HandleVarWithTupleAndEnumPattern(const AST::Pattern& pattern, const std::vector>& subPatterns, const Ptr& initNode, bool isLocalPattern) { auto leftType = TranslateType(*(pattern.ty)); Ptr leftValue = nullptr; if (initNode != nullptr) { leftValue = GetDerefedValue(initNode); leftValue = TypeCastOrBoxIfNeeded(*initNode, *leftType, leftValue->GetDebugLocation()); SetSymbolTable(pattern, *leftValue, isLocalPattern); } bool isEnumPattern{pattern.astKind == AST::ASTKind::ENUM_PATTERN}; for (size_t i = 0; i < subPatterns.size(); i++) { Ptr rVal = nullptr; if (leftValue != nullptr) { auto fieldType = TranslateType(*(subPatterns[i]->ty)); auto fieldIndex = isEnumPattern ? i + 1 : i; // add 1 to index when base type is enum std::vector path{fieldIndex}; auto baseValue = isEnumPattern ? CastEnumValueToConstructorTupleType( leftValue, StaticCast(pattern)) : leftValue; rVal = CreateAndAppendExpression(fieldType, baseValue, std::move(path), currentBlock)->GetResult(); } FlattenVarWithPatternDecl(*subPatterns[i], rVal, isLocalPattern); } } void Translator::FlattenVarWithPatternDecl(const AST::Pattern& pattern, const Ptr& target, bool isLocalPattern) { switch (pattern.astKind) { case AST::ASTKind::VAR_PATTERN: { auto varPattern = StaticCast(&pattern); HandleVarWithVarPattern(*varPattern, target, isLocalPattern); break; } case AST::ASTKind::TUPLE_PATTERN: { auto tuplePattern = StaticCast(&pattern); HandleVarWithTupleAndEnumPattern(*tuplePattern, tuplePattern->patterns, target, isLocalPattern); break; } case AST::ASTKind::ENUM_PATTERN: { auto enumPattern = StaticCast(&pattern); HandleVarWithTupleAndEnumPattern(*enumPattern, enumPattern->patterns, target, isLocalPattern); break; } case AST::ASTKind::WILDCARD_PATTERN: { break; } default: { Errorln("decl with unsupported pattern"); CJC_ABORT(); } } } Ptr Translator::Visit(const AST::VarWithPatternDecl& patternDecl) { CJC_ASSERT(!patternDecl.TestAttr(AST::Attribute::GLOBAL)); // The local const var is lifted as global variable, thus not handled here if (localConstVars.HasElement(&patternDecl)) { return nullptr; } Ptr initNode = nullptr; if (patternDecl.initializer.get()) { initNode = TranslateExprArg(*patternDecl.initializer); initNode->Set(SkipKind::SKIP_DCE_WARNING); } auto pattern = patternDecl.irrefutablePattern.get(); FlattenVarWithPatternDecl(*pattern, initNode, true); return nullptr; }cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/TranslateASTNode/Translator.cpp000066400000000000000000000276251510705540100254660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/TranslateASTNode/Translator.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" using namespace Cangjie::CHIR; using namespace Cangjie; void Translator::SetSymbolTable(const AST::Node& node, Value& val, bool isLocal) { CJC_ASSERT(node.IsDecl() || Utils::In(node.astKind, {AST::ASTKind::BLOCK, AST::ASTKind::TUPLE_PATTERN, AST::ASTKind::ENUM_PATTERN})); if (isLocal) { localValSymbolTable.Set(node, val); } else { globalSymbolTable.Set(node, val); } } void Translator::SetTopLevel(const AST::Decl& decl) { topLevelDecl = &decl; } bool Translator::IsTopLevel(const AST::Decl& decl) const { return topLevelDecl == &decl; } static bool ShouldInGlobalTable(const AST::Node& node) { // 1. global decl, 'NominalDecl' is stored to indicate 'this' and should be found from local table. if (node.TestAttr(AST::Attribute::GLOBAL) && !node.IsNominalDecl()) { return true; } // 2. member method or static member var if ((node.TestAttr(AST::Attribute::STATIC) && node.astKind == AST::ASTKind::VAR_DECL) || node.astKind == AST::ASTKind::FUNC_DECL) { const auto& decl = StaticCast(node); if (decl.outerDecl != nullptr && decl.outerDecl->IsNominalDecl()) { return true; } } return false; } Ptr Translator::GetSymbolTable(const AST::Node& node) const { CJC_ASSERT(node.IsDecl() || Utils::In(node.astKind, {AST::ASTKind::BLOCK, AST::ASTKind::TUPLE_PATTERN, AST::ASTKind::ENUM_PATTERN})); // Note this is a temporary solution here, we should be sure that all local consts are in global table // once the universal generics are implemented if (ShouldInGlobalTable(node)) { return globalSymbolTable.Get(node); } if (node.IsDecl() && localConstVars.HasElement(StaticCast(&node))) { return globalSymbolTable.Get(node); } if (auto func = DynamicCast(&node); func && localConstFuncs.HasElement(func)) { return globalSymbolTable.Get(node); } return localValSymbolTable.Get(node); } Ptr Translator::TranslateType(AST::Ty& ty) { return chirTy.TranslateType(ty); } Ptr Translator::CreateVirtualFuncType(const AST::FuncDecl& decl) { std::vector args; for (size_t i = 0; i < decl.funcBody->paramLists[0]->params.size(); i++) { args.emplace_back(TranslateType(*decl.funcBody->paramLists[0]->params[i]->ty)); } return builder.GetType(args, builder.GetUnitTy()); } Ptr Translator::GetNominalSymbolTable(const AST::Node& node) { return chirTy.GetGlobalNominalCache(StaticCast(node)); } DebugLocation Translator::GetValueDebugLocationInfo(const Value& value) const { DebugLocation loc; if (auto localVar = DynamicCast(&value); localVar) { loc = localVar->GetExpr()->GetDebugLocation(); } else { loc = value.GetDebugLocation(); } loc.SetScopeInfo(scopeInfo); return loc; } DebugLocation Translator::TranslateLocation(const AST::Node& node) const { return TranslateLocation(node.begin, node.end); } DebugLocation Translator::TranslateLocation(const Cangjie::Position& begin, const Cangjie::Position& end) const { if (begin.GetStatus() == PositionStatus::IGNORE) { return TranslateFileLocation(begin.fileID); } auto loc = TranslateLocationWithoutScope(builder.GetChirContext(), begin, end); loc.SetScopeInfo(scopeInfo); return loc; } DebugLocation Translator::TranslateFileLocation(unsigned fileID) const { return {builder.GetChirContext().GetSourceFileName(fileID), fileID, {0, 0}, {0, 0}}; } Ptr Translator::TypeCastOrBoxIfNeeded(Value& val, Type& expectedTy, const DebugLocation& loc, bool needCheck) { return CHIR::TypeCastOrBoxIfNeeded(val, expectedTy, builder, *currentBlock, loc, needCheck); } Ptr Translator::GetDerefedValue(Ptr val, const DebugLocation& loc) { CJC_NULLPTR_CHECK(val); auto valType = val->GetType(); if (valType->IsRef()) { valType = StaticCast(valType)->GetBaseType(); // For now, raw class type should not exist. Why, spec indicate that class is reference type. if (valType->IsClassOrArray()) { return val; } auto expr = (loc.IsInvalidPos() ? CreateAndAppendExpression(valType, val, currentBlock) : CreateAndAppendExpression(loc, valType, val, currentBlock)); return expr->GetResult(); } return val; } Ptr Translator::GetImplicitThisParam() const { auto curFunc = GetCurrentFunc(); CJC_ASSERT(curFunc); return curFunc->GetParam(0); } void Translator::SetFuncBlockGroup(BlockGroup& group) { blockGroupStack.clear(); blockGroupStack.emplace_back(&group); } void Translator::SetCurrentBlock(Block& block) { currentBlock = █ } Ptr Translator::GetCurrentBlock() const { return currentBlock; } Ptr Translator::CreateEmptyGVInitFunc(const std::string& mangledName, const std::string& identifier, const std::string& rawMangledName, const std::string& pkgName, const Linkage& linkage, const DebugLocation& loc, bool isConst) { Func* func = TryGetFromCache(GLOBAL_VALUE_PREFIX + mangledName, deserializedVals); BlockGroup* blockGroup = nullptr; if (func) { // found deserialized one blockGroup = builder.CreateBlockGroup(*func); func->ReplaceBody(*blockGroup); } else { auto funcTy = builder.GetType(std::vector{}, builder.GetVoidTy()); func = builder.CreateFunc(INVALID_LOCATION, funcTy, mangledName, identifier, rawMangledName, pkgName); blockGroup = builder.CreateBlockGroup(*func); func->SetFuncKind(FuncKind::GLOBALVAR_INIT); func->EnableAttr(Attribute::NO_REFLECT_INFO); func->EnableAttr(Attribute::NO_INLINE); func->EnableAttr(Attribute::COMPILER_ADD); func->EnableAttr(Attribute::INITIALIZER); if (isConst) { func->EnableAttr(Attribute::CONST); } func->InitBody(*blockGroup); } func->template Set(Linkage(linkage)); func->SetDebugLocation(loc); auto entry = builder.CreateBlock(blockGroup); blockGroup->SetEntryBlock(entry); SetFuncBlockGroup(*blockGroup); SetCurrentBlock(*entry); return func; } Ptr Translator::CreateGetElementRefWithPath(const DebugLocation& loc, Ptr lhsBase, const std::vector& path, Ptr block, const CustomType& customType) { auto [memberType, isReadOnly] = GetInstMemberTypeByNameCheckingReadOnly(customType, path, builder); auto memberRefType = builder.GetType(memberType); auto getMemberRef = CreateAndAppendExpression(loc, memberRefType, lhsBase, path, block)->GetResult(); if (lhsBase->TestAttr(Attribute::READONLY) || isReadOnly) { getMemberRef->EnableAttr(Attribute::READONLY); } return getMemberRef; } Value* Translator::TranslateExprArg(const AST::Node& node, Type& targetTy, bool deref) { auto value = TranslateASTNode(node, *this); if (targetTy.IsUnit()) { return CreateAndAppendConstantExpression(builder.GetUnitTy(), *GetCurrentBlock())->GetResult(); } if (node.ty->IsNothing() || targetTy.IsNothing()) { return CreateAndAppendConstantExpression(builder.GetNothingType(), *GetCurrentBlock())->GetResult(); } if (!value) { // unit can be converted to Any, or any other interface it extends if (node.ty->IsUnit() && !targetTy.IsUnit()) { value = CreateAndAppendConstantExpression( builder.GetUnitTy(), *GetCurrentBlock())->GetResult(); } else { return CreateAndAppendConstantExpression(builder.GetNothingType(), *GetCurrentBlock())->GetResult(); } } if (value->GetType()->IsNothing()) { return value; } if (deref) { value = GetDerefedValue(value, TranslateLocation(node)); } value = TypeCastOrBoxIfNeeded(*value, targetTy, TranslateLocation(node)); return value; } Value* Translator::TranslateExprArg(const AST::Node& node) { auto value = TranslateASTNode(node, *this); if (!value) { if (node.ty->IsUnit()) { return CreateAndAppendConstantExpression(builder.GetUnitTy(), *GetCurrentBlock())->GetResult(); } return CreateAndAppendConstantExpression(builder.GetNothingType(), *GetCurrentBlock())->GetResult(); } return value; } void Translator::TranslateSubExprToLoc(const AST::Node& node, Value* location) { if (!location) { TranslateASTNode(node, *this); return; } const DebugLocation* debugLoc; if (auto l = DynamicCast(location)) { debugLoc = &l->GetExpr()->GetDebugLocation(); } else { debugLoc = &location->GetDebugLocation(); } TranslateSubExprToLoc(node, location, *debugLoc); } void Translator::TranslateSubExprToLoc(const AST::Node& node, Value* location, const DebugLocation& debugLoc) { auto value = TranslateASTNode(node, *this); /// \ref loc is null if it is of Unit or Nothing type. In this case, no further typecast/store is needed. if (!location) { return; } auto targetTy = StaticCast(location->GetType())->GetBaseType(); if (targetTy->IsNothing()) { return; } // unit can be converted to Any, or any other interface it extends if (node.ty->IsUnit() && !targetTy->IsUnit()) { value = CreateAndAppendConstantExpression( debugLoc, builder.GetUnitTy(), *currentBlock)->GetResult(); } // this if never collides with the previous if, so no need to write else if if (targetTy->IsUnit() || !value) { if (!opts.enableCompileDebug) { return; } // create a null/unit value if -g if (!value) { if (node.ty->IsUnit()) { auto unit = CreateAndAppendConstantExpression( debugLoc, builder.GetUnitTy(), *currentBlock); value = unit->GetResult(); } else { /// either \ref node is of Nothing type, of every branch of \ref node is of Nothing type, create a /// Nothing value here auto null = CreateAndAppendConstantExpression( debugLoc, builder.GetNothingType(), *currentBlock); null->Set(SkipKind::SKIP_DCE_WARNING); value = null->GetResult(); } } } value = GetDerefedValue(value, debugLoc); if (value->GetType()->IsNothing()) { return; } value = TypeCastOrBoxIfNeeded(*value, *targetTy, debugLoc); CreateWrappedStore(debugLoc, value, location, GetCurrentBlock()); } Ptr Translator::TranslateSubExprAsValue(const AST::Node& node) { auto value = TranslateASTNode(node, *this); if (value && Is(node)) { value = GetDerefedValue(value, TranslateLocation(node)); } return value; } void Translator::TranslateSubExprToDiscarded(const AST::Node& node) { TranslateASTNode(node, *this); if (auto b = DynamicCast(&node)) { if (b->kind == AST::LitConstKind::UNIT) { auto unit = CreateAndAppendConstantExpression( TranslateLocation(node), builder.GetUnitTy(), *currentBlock); if (b->TestAttr(AST::Attribute::COMPILER_ADD)) { unit->GetResult()->EnableAttr(Attribute::COMPILER_ADD); } } } } cangjie_compiler-1.0.7/src/CHIR/AST2CHIR/Utils.cpp000066400000000000000000000351351510705540100213150ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/AST/Utils.h" #include "cangjie/CHIR/Utils.h" namespace Cangjie { namespace CHIR { inline std::map g_attrMap = { {Cangjie::AST::Attribute::GENERIC_INSTANTIATED, Attribute::GENERIC_INSTANTIATED}, {Cangjie::AST::Attribute::STATIC, Attribute::STATIC}, {Cangjie::AST::Attribute::FOREIGN, Attribute::FOREIGN}, {Cangjie::AST::Attribute::MUT, Attribute::MUT}, {Cangjie::AST::Attribute::INTERNAL, Attribute::INTERNAL}, {Cangjie::AST::Attribute::PUBLIC, Attribute::PUBLIC}, {Cangjie::AST::Attribute::PRIVATE, Attribute::PRIVATE}, {Cangjie::AST::Attribute::PROTECTED, Attribute::PROTECTED}, {Cangjie::AST::Attribute::ABSTRACT, Attribute::ABSTRACT}, {Cangjie::AST::Attribute::OPEN, Attribute::VIRTUAL}, {Cangjie::AST::Attribute::OVERRIDE, Attribute::OVERRIDE}, {Cangjie::AST::Attribute::REDEF, Attribute::REDEF}, {Cangjie::AST::Attribute::SEALED, Attribute::SEALED}, {Cangjie::AST::Attribute::OPERATOR, Attribute::OPERATOR}, {Cangjie::AST::Attribute::COMPILER_ADD, Attribute::COMPILER_ADD}, {Cangjie::AST::Attribute::IMPLICIT_ADD, Attribute::NO_DEBUG_INFO}, {Cangjie::AST::Attribute::GENERIC, Attribute::GENERIC}, {Cangjie::AST::Attribute::IMPORTED, Attribute::IMPORTED}, {Cangjie::AST::Attribute::NO_REFLECT_INFO, Attribute::NO_REFLECT_INFO}, {Cangjie::AST::Attribute::COMMON, Attribute::COMMON}, {Cangjie::AST::Attribute::PLATFORM, Attribute::PLATFORM}}; void TranslateFunctionGenericUpperBounds(CHIRType& chirTy, const AST::FuncDecl& func) { CJC_NULLPTR_CHECK(func.funcBody); if (func.funcBody->generic) { // We need to translate functions' generic type and fill their upperBounds during translation. for (auto& type : func.funcBody->generic->typeParameters) { chirTy.TranslateType(*type->ty); } // Must fill upper bounds after translation all generics. for (auto& type : func.funcBody->generic->typeParameters) { chirTy.FillGenericArgType(StaticCast(*type->ty)); } } } FuncType* AdjustVarInitType( const FuncType& funcType, const AST::Decl& outerDecl, CHIRBuilder& builder, CHIRType& chirType) { auto params = funcType.GetParamTypes(); std::vector paramsTy; paramsTy.reserve(params.size() + 1); // additional 1 means the type of this. auto thisTy = chirType.TranslateType(*outerDecl.ty); // ClassLike decl has already been added ref type by `TranslateType`, so we just needs add ref type to // constructor and mut function of non-classLike type. if (outerDecl.astKind == AST::ASTKind::STRUCT_DECL) { paramsTy.emplace_back(builder.GetType(thisTy)); } else { paramsTy.emplace_back(thisTy); } paramsTy.insert(paramsTy.end(), params.begin(), params.end()); return builder.GetType(paramsTy, funcType.GetReturnType()); } FuncType* AdjustFuncType(FuncType& funcType, const AST::FuncDecl& funcDecl, CHIRBuilder& builder, CHIRType& chirType) { auto params = funcType.GetParamTypes(); // Instance member function needs to add this type. if (IsInstanceMember(funcDecl)) { std::vector paramsTy; paramsTy.reserve(params.size() + 1); // additional 1 means the type of this. auto thisTy = chirType.TranslateType(*funcDecl.outerDecl->ty); // ClassLike decl has already been added ref type by `TranslateType`, so we just needs add ref type to // constructor and mut function of non-classLike type. if (IsStructMutFunction(funcDecl)) { paramsTy.emplace_back(builder.GetType(thisTy)); } else { paramsTy.emplace_back(thisTy); } paramsTy.insert(paramsTy.end(), params.begin(), params.end()); if (funcDecl.TestAttr(AST::Attribute::CONSTRUCTOR) || funcDecl.IsFinalizer()) { return builder.GetType(paramsTy, builder.GetVoidTy()); } else { return builder.GetType(paramsTy, funcType.GetReturnType()); } } return &funcType; } DebugLocation GetVarLoc(const CHIRContext& cctx, const AST::Decl& decl) { auto& begin = decl.identifier.Begin(); return TranslateLocationWithoutScope(cctx, begin, decl.identifier.End()); } DebugLocation TranslateLocationWithoutScope( const CHIRContext& context, const Cangjie::Position& beginPos, const Cangjie::Position& endPos) { // Note: AST2CHIL adaptation of scopeInfo is needed. return {context.GetSourceFileName(beginPos.fileID), beginPos.fileID, {unsigned(beginPos.line), unsigned(beginPos.column)}, {unsigned(endPos.line), unsigned(endPos.column)}, {0}}; } std::vector GetGenericParamType(const AST::Decl& decl, CHIRType& chirType) { std::vector ts; AST::Generic* generic = nullptr; if (auto funcDecl = DynamicCast(&decl); funcDecl) { generic = funcDecl->funcBody->generic.get(); } else { generic = decl.generic.get(); } if (!decl.TestAttr(AST::Attribute::GENERIC)) { return ts; } CJC_NULLPTR_CHECK(generic); for (auto& genericTy : generic->typeParameters) { ts.emplace_back(StaticCast(chirType.TranslateType(*(genericTy->ty)))); } return ts; } std::string GetNameOfDefinedPackage(const AST::FuncDecl& funcDecl) { if (funcDecl.outerDecl != nullptr && funcDecl.outerDecl->genericDecl != nullptr) { return funcDecl.outerDecl->genericDecl->fullPackageName; } if (funcDecl.genericDecl != nullptr) { return funcDecl.genericDecl->fullPackageName; } return funcDecl.fullPackageName; } AttributeInfo BuildAttr(const AST::AttributePack& attr) { AttributeInfo attrInfo; for (auto& it : std::as_const(g_attrMap)) { if (attr.TestAttr(it.first)) { attrInfo.SetAttr(it.second, true); } } return attrInfo; } AttributeInfo BuildVarDeclAttr(const AST::VarDecl& decl) { auto attrInfo = BuildAttr(decl.GetAttrs()); if (!decl.isVar) { attrInfo.SetAttr(Attribute::READONLY, true); } if (decl.isConst) { attrInfo.SetAttr(Attribute::CONST, true); } return attrInfo; } bool IsStructMutFunction(const AST::FuncDecl& function) { if (function.outerDecl == nullptr) { return false; } if (function.outerDecl->astKind == AST::ASTKind::STRUCT_DECL) { return function.TestAnyAttr(AST::Attribute::CONSTRUCTOR, AST::Attribute::MUT); } if (function.outerDecl->astKind == AST::ASTKind::EXTEND_DECL && RawStaticCast(function.outerDecl)->extendedType->ty->IsStruct()) { return function.TestAnyAttr(AST::Attribute::CONSTRUCTOR, AST::Attribute::MUT); } return false; } bool CollectFrozenFuncDecl(const AST::FuncDecl& funcDecl, const GlobalOptions& opts) { return opts.IsCHIROptimizationLevelOverO2() && funcDecl.isFrozen && !funcDecl.TestAttr(AST::Attribute::ABSTRACT); } bool IsLocalVarDecl(const AST::Decl& decl) { return decl.astKind == AST::ASTKind::VAR_DECL && !decl.TestAttr(AST::Attribute::STATIC) && !decl.TestAttr(AST::Attribute::GLOBAL); } bool IsSrcImportedDecl(const AST::Decl& decl, const GlobalOptions& opts) { if (!decl.TestAttr(AST::Attribute::IMPORTED)) { return false; } // 1. imported const func or var if (decl.IsConst()) { return true; } // 2. imported frozen func if (decl.astKind == AST::ASTKind::FUNC_DECL) { if (CollectFrozenFuncDecl(StaticCast(decl), opts)) { return true; } } // 3. static member var, need to check static init func if (decl.TestAttr(AST::Attribute::STATIC) && decl.astKind == AST::ASTKind::VAR_DECL && decl.outerDecl != nullptr && decl.outerDecl->IsNominalDecl()) { for (auto& member : decl.outerDecl->GetMemberDecls()) { if (member->TestAttr(AST::Attribute::CONSTRUCTOR) && member->TestAttr(AST::Attribute::STATIC)) { if (IsSrcCodeImportedGlobalDecl(*member, opts)) { return true; } } } } return false; } bool IsSrcCodeImportedGlobalDecl(const AST::Decl& decl, const GlobalOptions& opts) { return IsSrcImportedDecl(decl, opts) && !IsLocalVarDecl(decl); } bool IsSymbolImportedDecl(const AST::Decl& decl, const GlobalOptions& opts) { return decl.TestAttr(AST::Attribute::IMPORTED) && !IsSrcImportedDecl(decl, opts); } AST::Decl* GetOuterDecl(const AST::Decl& decl) { auto outerDecl = decl.outerDecl; if (auto func = DynamicCast(&decl); func && func->ownerFunc != nullptr) { outerDecl = func->ownerFunc->outerDecl; } return outerDecl; } bool IsLocalFunc(const AST::FuncDecl& func) { auto outerDecl = GetOuterDecl(func); return outerDecl != nullptr && (outerDecl->astKind == AST::ASTKind::FUNC_DECL || outerDecl->astKind == AST::ASTKind::VAR_DECL); } const std::unordered_set OVERFLOW_OPERATOR_NAMES{"+", "-", "*", "/"}; bool IsOverflowOperator(const std::string& name) { return OVERFLOW_OPERATOR_NAMES.find(name) != OVERFLOW_OPERATOR_NAMES.end(); } /// Check whether a type can be integer type. This may change when spec changes. bool CanBeIntegerType(const Type& type) { return type.IsInteger() || type.IsGeneric(); } ///@{ /// These two functions are used by TranslateNonStaticMemberFuncCall /// Any operator that has different behaviours on integer types when @Overflow changes, if defined in an /// interface that can be extended by integer types, needs to be split into three operator funcs, so that when /// called, the integer overflow strategy of this overloaded operator can be match the call site overflow /// strategy. /// Operators to be split: +, -, *, /(only in MIN/-1) /// Definition: when can an interface be implemented by integer types? /// The current definition is that any interface can, but it may change when spec changes, /// e.g. interface with This as return type cannot be implemented by /// structs (to which all integer types belong), but for now that feature has not been implemented. bool IsOverflowOperator(const std::string& name, const FuncType& type) { auto params = type.GetParamTypes(); if (params.size() == 1 && IsOverflowOperator(name)) { return CanBeIntegerType(*params[0]) && CanBeIntegerType(*type.GetReturnType()); } if (params.size() == 0 && name == "-") { return CanBeIntegerType(*type.GetReturnType()); } return false; } std::string OverflowStrategyPrefix(OverflowStrategy ovf) { switch (ovf) { case OverflowStrategy::WRAPPING: return "&"; case OverflowStrategy::THROWING: return "~"; default: return "%"; } } void SetCompileTimeValueFlagRecursivly(Func& initFunc) { auto setConstFlagForLambda = [](BlockGroup& body) { std::function preVisit = [&preVisit](Expression& expr) { if (expr.GetExprKind() == CHIR::ExprKind::LAMBDA) { auto& lambda = StaticCast(expr); lambda.SetCompileTimeValue(); Visitor::Visit(*lambda.GetBody(), preVisit); } return VisitResult::CONTINUE; }; Visitor::Visit(body, preVisit); }; setConstFlagForLambda(*initFunc.GetBody()); } MemberVarInfo GetMemberVarByName(const CustomTypeDef& def, const std::string& varName) { auto vars = def.GetAllInstanceVars(); for (auto it = vars.crbegin(); it != vars.crend(); ++it) { if (it->name == varName) { return *it; } } CJC_ABORT(); return MemberVarInfo{.attributeInfo = AttributeInfo{}}; } Type* GetInstMemberTypeByName(const CustomType& rootType, const std::vector& names, CHIRBuilder& builder) { return GetInstMemberTypeByNameCheckingReadOnly(rootType, names, builder).first; } std::pair GetInstMemberTypeByNameCheckingReadOnly( const CustomType& rootType, const std::vector& names, CHIRBuilder& builder) { if (names.empty()) { #ifdef NDEBUG return {const_cast(&rootType), false}; #else CJC_ABORT(); #endif } auto customTypeDef = rootType.GetCustomTypeDef(); std::unordered_map instMap; rootType.GetInstMap(instMap, builder); auto currentName = names.front(); auto member = GetMemberVarByName(*customTypeDef, currentName); // if one member is readonly in path, the result is readonly bool isReadOnly = member.TestAttr(Attribute::READONLY); auto memberTy = ReplaceRawGenericArgType(*member.type, instMap, builder); if (names.size() > 1) { auto pureMemberTy = memberTy->StripAllRefs(); if (pureMemberTy->IsNominal()) { auto subNames = names; subNames.erase(subNames.begin()); auto [memberType, isMemberReadOnly] = GetInstMemberTypeByNameCheckingReadOnly(*StaticCast(pureMemberTy), subNames, builder); return {memberType, isReadOnly || isMemberReadOnly}; } else if (pureMemberTy->IsGeneric()) { auto subNames = names; subNames.erase(subNames.begin()); auto [memberType, isMemberReadOnly] = GetInstMemberTypeByNameCheckingReadOnly(*StaticCast(pureMemberTy), subNames, builder); return {memberType, isReadOnly || isMemberReadOnly}; } else { CJC_ABORT(); } } return {memberTy, isReadOnly}; } std::pair GetInstMemberTypeByNameCheckingReadOnly( const GenericType& rootType, const std::vector& names, CHIRBuilder& builder) { // Find the most child class type Type* concreteType = nullptr; for (auto upperBound : rootType.GetUpperBounds()) { auto upperBoundCustomType = StaticCast(upperBound->StripAllRefs()); if (upperBoundCustomType->GetCustomTypeDef()->IsClassLike()) { if (concreteType == nullptr) { concreteType = upperBound; } else { if (upperBoundCustomType->IsEqualOrSubTypeOf(*concreteType, builder)) { concreteType = upperBound; } } } } CJC_NULLPTR_CHECK(concreteType); return GetInstMemberTypeByNameCheckingReadOnly( *StaticCast(concreteType->StripAllRefs()), names, builder); } } // namespace CHIR } // namespace Cangjiecangjie_compiler-1.0.7/src/CHIR/Analysis/000077500000000000000000000000001510705540100201065ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CHIR/Analysis/Arithmetic.cpp000066400000000000000000000011471510705540100227060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements arithmetic operator. */ #include "cangjie/CHIR/Analysis/Arithmetic.h" namespace Cangjie::CHIR { int64_t SignExtend64(uint64_t val, unsigned srcWidth) { CJC_ASSERT(srcWidth > 0 && srcWidth <= B64); return static_cast(val << (B64 - srcWidth)) >> (B64 - srcWidth); } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/Analysis/BoolDomain.cpp000066400000000000000000000057141510705540100226440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Analysis/BoolDomain.h" namespace Cangjie::CHIR { constexpr unsigned N{0}; constexpr unsigned F{1}; constexpr unsigned T{2}; constexpr unsigned A{3}; constexpr unsigned OP_TABLE_LEN{4}; BoolDomain::BoolDomain(const BoolDomain& other) : v{other.v} { } BoolDomain::BoolDomain(BoolDomain&& other) : v{other.v} { } BoolDomain& BoolDomain::operator=(const BoolDomain& other) { if (&other != this) { v = other.v; } return *this; } BoolDomain& BoolDomain::operator=(BoolDomain&& other) { if (&other != this) { v = other.v; } return *this; } BoolDomain::~BoolDomain() { } BoolDomain::BoolDomain(unsigned v) : v{v} { } BoolDomain BoolDomain::Bottom() { return BoolDomain{N}; } BoolDomain BoolDomain::False() { return BoolDomain{F}; } BoolDomain BoolDomain::True() { return BoolDomain{T}; } BoolDomain BoolDomain::Top() { return BoolDomain{A}; } bool BoolDomain::IsTrue() const { return v == T; } bool BoolDomain::IsFalse() const { return v == F; } bool BoolDomain::IsTop() const { return v == A; } bool BoolDomain::IsBottom() const { return v == N; } bool BoolDomain::IsNonTrivial() const { return !IsTop(); } bool BoolDomain::IsSingleValue() const { return v == T || v == F; } bool BoolDomain::GetSingleValue() const { return v == T; } BoolDomain BoolDomain::FromBool(bool v) { return v ? BoolDomain{T} : BoolDomain{F}; } BoolDomain LogicalAnd(const BoolDomain& a, const BoolDomain& b) { static constexpr unsigned logicalAndTable[OP_TABLE_LEN][OP_TABLE_LEN]{ {N, N, N, N}, {N, F, F, F}, {N, F, T, A}, {N, F, A, A}, }; return BoolDomain{logicalAndTable[a.v][b.v]}; } BoolDomain LogicalOr(const BoolDomain& a, const BoolDomain& b) { static constexpr unsigned logicalOrTable[OP_TABLE_LEN][OP_TABLE_LEN]{ {N, N, N, N}, {N, F, T, A}, {N, T, T, T}, {N, A, T, A}, }; return BoolDomain{logicalOrTable[a.v][b.v]}; } BoolDomain operator&(const BoolDomain& a, const BoolDomain& b) { return BoolDomain{a.v & b.v}; } BoolDomain operator|(const BoolDomain& a, const BoolDomain& b) { return BoolDomain{a.v | b.v}; } BoolDomain operator!(const BoolDomain& v) { static constexpr unsigned logicalNotTable[]{N, T, F, A}; return BoolDomain{logicalNotTable[v.v]}; } std::ostream& operator<<(std::ostream& out, const BoolDomain& v) { static const std::string B[OP_TABLE_LEN]{"<>", "false", "true", ""}; return out << B[v.v]; } BoolDomain BoolDomain::Union(const BoolDomain& a, const BoolDomain& b) { return a | b; } bool BoolDomain::IsSame(const BoolDomain& domain) const { return v == domain.v; } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/Analysis/CMakeLists.txt000066400000000000000000000005341510705540100226500ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB ANALYSIS_SRC *.cpp) set(CHIR_OPT_SRC ${CHIR_OPT_SRC} ${ANALYSIS_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/CHIR/Analysis/CallGraphAnalysis.cpp000066400000000000000000000304571510705540100241640ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the callgraph analysis. * The main process will be: * 1. Collect all the function in the current package, add it to call graph * (a map collect function with call graph node) in the format of call graph node. * 2. PopulateCallGraphNode by traverse all the expression in the function of current node. If * it is a apply expression add a DIRECT kind edge to the edges(functions with specific kind * that is called by the function of current node). If it is a invoke expression andd a VIRTUAL kind edge * to the edges. * 3. Finish the build of call graph with first two step then BuildSCC to collect all the Strongly * connected component(SCC) nodes in the current call graph. */ #include "cangjie/CHIR/Analysis/CallGraphAnalysis.h" #include "cangjie/CHIR/Transformation/Devirtualization.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Visitor/Visitor.h" namespace Cangjie::CHIR { CallGraph::Node::Iterator CallGraph::Node::Begin() { return calledEdges.begin(); } CallGraph::Node::Iterator CallGraph::Node::End() { return calledEdges.end(); } CallGraph::Node::ConstIterator CallGraph::Node::Begin() const { return calledEdges.begin(); } CallGraph::Node::ConstIterator CallGraph::Node::End() const { return calledEdges.end(); } bool CallGraph::Node::Empty() const { return calledEdges.empty(); } Func* CallGraph::Node::GetFunction() const { return func; } void CallGraph::Node::AddCalledEdge(const Edge& edge) { calledEdges.emplace_back(edge); } void CallGraph::Node::DeleteCalledEdge(const Edge& edge) { auto it = find(calledEdges.begin(), calledEdges.end(), edge); if (it != calledEdges.end()) { calledEdges.erase(it); } } CallGraph::Edge::Edge(Node* n, Kind k) { edgeValue = std::make_pair(n, k); } inline CallGraph::Node* CallGraph::Edge::GetNode() const { return edgeValue.first; } inline CallGraph::Edge::Kind CallGraph::Edge::GetKind() const { return edgeValue.second; } bool CallGraph::Edge::operator==(const Edge& other) const { return this->edgeValue.first == other.edgeValue.first; } CallGraph::CallGraph(const Package* package, DevirtualizationInfo& devirtFuncInfo) : devirtFuncInfo(devirtFuncInfo), entryNode(std::make_unique(nullptr)), exitNode(std::make_unique(nullptr)) { // build the call graph. for (auto func : package->GetGlobalFuncs()) { if (func->GetUsers().size() == 0) { AddToCallGraph(*func, true); } else { AddToCallGraph(*func, false); } } } void CallGraph::AddToCallGraph(const Func& func, bool isCalledByEntryNode) { CallGraph::Node* node = GetOrCreateNode(func); // If this function has zero uses, then anything could call it. // add it to the calledFunctions of entryNode. if (isCalledByEntryNode) { Edge calledEdge(node, Edge::Kind::VIRTUAL); entryNode->AddCalledEdge(calledEdge); } PopulateCallGraphNode(*node, *(node->GetFunction()->GetBody())); } // GetOrCreateNode - This method will insert a new call graph Node for the specified function // if one does not already exist. CallGraph::Node* CallGraph::GetOrCreateNode(const Func& func) { auto& callGraphNode = functionMap[&func]; if (callGraphNode) { return callGraphNode.get(); } callGraphNode = std::make_unique(const_cast(&func)); return callGraphNode.get(); } void CallGraph::PopulateCallGraphNode(Node& node, BlockGroup& funcBlockGroup) { auto preVisit = [&node, this](Expression& expr) { if (expr.GetExprKind() == ExprKind::INVOKE || expr.GetExprKind() == ExprKind::INVOKE_WITH_EXCEPTION) { // Get all the possible callee of virtual function from Devirtualization info collection. // exclude it from EntryNode and add it here. AddVirtualEdgeToNode(node, expr); } else if (expr.GetExprKind() == ExprKind::APPLY || expr.GetExprKind() == ExprKind::APPLY_WITH_EXCEPTION) { AddDirectEdgeToNode(node, expr); } else if (expr.GetExprKind() == ExprKind::LAMBDA) { PopulateCallGraphNode(node, *(StaticCast(&expr)->GetBody())); } return VisitResult::CONTINUE; }; Visitor::Visit(funcBlockGroup, preVisit, []([[maybe_unused]] Expression& e) { return VisitResult::CONTINUE; }); } void CallGraph::AddVirtualEdgeToNode(Node& node, const Expression& expression) { // Get all the possible callee of virtual function from Devirtualization info collection. // exclude it from EntryNode and add it here. Type* resTy; std::vector types; std::string methodName; if (expression.GetExprKind() == ExprKind::INVOKE) { auto invoke = StaticCast(&expression); resTy = invoke->GetObject()->GetType(); types = invoke->GetMethodType()->GetParamTypes(); methodName = invoke->GetMethodName(); } else { auto invoke = StaticCast(&expression); resTy = invoke->GetObject()->GetType(); types = invoke->GetMethodType()->GetParamTypes(); methodName = invoke->GetMethodName(); } while (resTy->IsRef()) { resTy = StaticCast(resTy)->GetBaseType(); } if (resTy->IsClass()) { std::vector paramTys; for (size_t i = 1; i < types.size(); i++) { paramTys.emplace_back(types[i]); } auto allPossibleCalleeOfInvoke = Utils::SetToVec(GetAllPossibleCalleeOfInvoke(std::make_pair(methodName, paramTys))); std::sort(allPossibleCalleeOfInvoke.begin(), allPossibleCalleeOfInvoke.end(), [](const Ptr v1, const Ptr v2) { return v1->GetIdentifier() < v2->GetIdentifier(); }); for (auto possibleCalleeOfInvoke : allPossibleCalleeOfInvoke) { if (auto callee = DynamicCast(possibleCalleeOfInvoke); callee) { Edge edge(GetOrCreateNode(*callee), Edge::Kind::VIRTUAL); entryNode->DeleteCalledEdge(edge); node.AddCalledEdge(edge); } else { Edge edge(exitNode.get(), Edge::Kind::VIRTUAL); entryNode->DeleteCalledEdge(edge); node.AddCalledEdge(edge); } } } } void CallGraph::AddDirectEdgeToNode(Node& node, const Expression& expression) { const Func* calledFunc; if (expression.GetExprKind() == ExprKind::APPLY) { calledFunc = DynamicCast(StaticCast(&expression)->GetCallee()); } else { auto apply = StaticCast(&expression); calledFunc = DynamicCast(apply->GetCallee()); } if (calledFunc) { Edge edge(GetOrCreateNode(*calledFunc), Edge::Kind::DIRECT); node.AddCalledEdge(edge); } else { Edge edge(exitNode.get(), Edge::Kind::DIRECT); node.AddCalledEdge(edge); } } // Get all the possible callee function of a virtual function from Devirtualization info collection. // Only care about the method name and type, ignore the classType of invoker. std::unordered_set CallGraph::GetAllPossibleCalleeOfInvoke( const std::pair>& method) const { (void)method; (void)devirtFuncInfo; return std::unordered_set(); } void CallGraphAnalysis::DoCallGraphAnalysis(bool isDebug) { CallGraph callGraph(package, devirtFuncInfo); BuildSCC(callGraph); if (isDebug) { PrintCallGraph(callGraph); } } void CallGraphAnalysis::PrintCallGraph(const CallGraph& callGraph) const { std::vector nodeStack; std::set nodeSet; auto entry = callGraph.GetEntryNode(); for (auto it = entry->Begin(); it != entry->End(); ++it) { nodeStack.push_back(it->GetNode()); nodeSet.insert(it->GetNode()); } while (!nodeStack.empty()) { auto node = nodeStack.back(); nodeStack.pop_back(); // No callFunctions for this node, it is a leaf node if (node->Empty()) { std::string message = "[CallGraphAnalysis] Call Graph found "; if (node->GetFunction()) { message += node->GetFunction()->GetIdentifierWithoutPrefix() + "\n"; std::cout << message; } continue; } std::string message = "[CallGraphAnalysis] Call Graph found " + node->GetFunction()->GetIdentifierWithoutPrefix(); for (auto it = node->Begin(); it != node->End(); ++it) { message += (it->GetKind()) ? ", DIRECT CALL:" : ", VIRTUAL CALL:"; message += (it->GetNode()->GetFunction()) ? it->GetNode()->GetFunction()->GetIdentifierWithoutPrefix() : "Unknown Function"; if (auto pos = nodeSet.find(it->GetNode()); pos == nodeSet.end()) { nodeStack.push_back(it->GetNode()); nodeSet.insert(it->GetNode()); } } message += "\n"; std::cout << message; } } // Enumerate the SCCs of a directed graph in reverse topological order // Implement a Tarjan's DFS algorithm using an internal stack to build // up a vector of nodes in a particular SCC. // 1. Start DFS traverse from the entryNode. Increase the DFS flag visitNum with the visit of // child node(calledFunctions) // 2. Stop the DFS traverse when there is no child node // 3. Pop out node from the VisitStack and propagate the minimum DFS flag of node to parent. // 4. If the original DFS flag number of node equal to the minimum one after propagate. // node in the sccNodeStack is SCC. void CallGraphAnalysis::BuildSCC(const CallGraph& callGraph) { DFSVisitOne(*callGraph.GetEntryNode()); do { GetNextSCC(); for (unsigned i = 0; i < currentSCC.size(); i++) { postOrderSCCFunctionlist.push_back(currentSCC[i]->GetFunction()); } } while (!currentSCC.empty()); } void CallGraphAnalysis::DFSVisitOne(CallGraph::Node& node) { ++visitNum; nodeVisitNumbers[&node] = visitNum; sccNodeStack.push_back(&node); visitStack.push_back(StackElement(&node, node.Begin(), visitNum)); } void CallGraphAnalysis::GetNextSCC() { currentSCC.clear(); // Prepare to compute the next SCC while (!visitStack.empty()) { DFSVisitChildren(); // Pop the leaf on the top of the VisitStack. CallGraph::Node* visitingN = visitStack.back().node; unsigned minVisitNum = visitStack.back().minVisited; visitStack.pop_back(); // Propagate minVisitNum to parent so we can detect the SCC starting node. if (!visitStack.empty() && visitStack.back().minVisited > minVisitNum) { visitStack.back().minVisited = minVisitNum; } if (minVisitNum != nodeVisitNumbers[visitingN]) { continue; } // A full SCC is on the sccNodeStack! It includes all nodes below // visitingN on the stack. Copy those nodes to the currentSCC, // reset their minVisit values, and return (this suspends // the DFS traversal till the GetNextSCC). do { currentSCC.push_back(sccNodeStack.back()); sccNodeStack.pop_back(); nodeVisitNumbers[currentSCC.back()] = ~0U; } while (currentSCC.back() != visitingN); return; } } void CallGraphAnalysis::DFSVisitChildren() { while (visitStack.back().nextChild != visitStack.back().node->End()) { // TOS has at least one more child so continue DFS CallGraph::Node* childN = (*visitStack.back().nextChild++).GetNode(); typename std::map::iterator visited = nodeVisitNumbers.find(childN); if (visited == nodeVisitNumbers.end()) { // this node has never been seen. DFSVisitOne(*childN); continue; } unsigned childNum = visited->second; if (visitStack.back().minVisited > childNum) { visitStack.back().minVisited = childNum; } } } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/Analysis/ConstAnalysis.cpp000066400000000000000000000530671510705540100234170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements constant analysis. */ #include "cangjie/CHIR/Analysis/ConstAnalysis.h" #include namespace Cangjie::CHIR { ConstValue::ConstValue(ConstKind kind) : kind(kind) { } ConstValue::~ConstValue() { } ConstValue::ConstKind ConstValue::GetConstKind() const { return kind; } std::optional> ConstBoolVal::Join(const ConstValue& rhs) const { if (rhs.GetConstKind() == ConstKind::BOOL && this->val == StaticCast(rhs).val) { return std::nullopt; } return nullptr; } std::string ConstBoolVal::ToString() const { return val ? "true" : "false"; } std::unique_ptr ConstBoolVal::Clone() const { return std::make_unique(val); } bool ConstBoolVal::GetVal() const { return val; } std::optional> ConstRuneVal::Join(const ConstValue& rhs) const { if (rhs.GetConstKind() == ConstKind::RUNE && this->val == StaticCast(rhs).val) { return std::nullopt; } return nullptr; } std::string ConstRuneVal::ToString() const { return std::to_string(val); } std::unique_ptr ConstRuneVal::Clone() const { return std::make_unique(val); } char32_t ConstRuneVal::GetVal() const { return val; } std::optional> ConstStrVal::Join(const ConstValue& rhs) const { if (rhs.GetConstKind() == ConstKind::STRING && this->val == StaticCast(rhs).val) { return std::nullopt; } return nullptr; } std::string ConstStrVal::ToString() const { return val; } std::unique_ptr ConstStrVal::Clone() const { return std::make_unique(val); } std::string ConstStrVal::GetVal() const { return val; } std::optional> ConstUIntVal::Join(const ConstValue& rhs) const { if (rhs.GetConstKind() == ConstKind::UINT && this->val == StaticCast(rhs).val) { return std::nullopt; } return nullptr; } std::string ConstUIntVal::ToString() const { return std::to_string(val); } std::unique_ptr ConstUIntVal::Clone() const { return std::make_unique(val); } uint64_t ConstUIntVal::GetVal() const { return val; } std::optional> ConstIntVal::Join(const ConstValue& rhs) const { if (rhs.GetConstKind() == ConstKind::INT && this->val == StaticCast(rhs).val) { return std::nullopt; } return nullptr; } std::string ConstIntVal::ToString() const { return std::to_string(val); } std::unique_ptr ConstIntVal::Clone() const { return std::make_unique(val); } int64_t ConstIntVal::GetVal() const { return val; } std::optional> ConstFloatVal::Join(const ConstValue& rhs) const { #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wfloat-equal" #else #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wfloat-equal" #endif // this float equal is intentional if (rhs.GetConstKind() == ConstKind::FLOAT && this->val == StaticCast(rhs).val) { #if defined(__clang__) #pragma clang diagnostic pop #else #pragma GCC diagnostic pop #endif return std::nullopt; } return nullptr; } std::string ConstFloatVal::ToString() const { return std::to_string(val); } std::unique_ptr ConstFloatVal::Clone() const { return std::make_unique(val); } double ConstFloatVal::GetVal() const { return val; } template <> const std::string Analysis::name = "const-analysis"; template <> const std::optional Analysis::blockLimit = std::nullopt; template <> ConstDomain::ChildrenMap ValueAnalysis::globalChildrenMap{}; template <> ConstDomain::AllocatedRefMap ValueAnalysis::globalAllocatedRefMap{}; template <> ConstDomain::AllocatedObjMap ValueAnalysis::globalAllocatedObjMap{}; template <> std::vector> ValueAnalysis::globalRefPool{}; template <> std::vector> ValueAnalysis::globalAbsObjPool{}; template <> ConstDomain ValueAnalysis::globalState{&globalChildrenMap, &globalAllocatedRefMap, nullptr, &globalAllocatedObjMap, &globalRefPool, &globalAbsObjPool}; ConstAnalysis::ConstAnalysis(const Func* func, CHIRBuilder& builder, bool isDebug, DiagAdapter* diag) : ValueAnalysis(func, builder, isDebug), diag(diag) { } ConstAnalysis::~ConstAnalysis() { } void ConstAnalysis::PrintDebugMessage(const Expression* expr, const ConstValue* absVal) const { std::string message = "[ConstAnalysis] The const value of " + expr->GetExprKindName() + ToPosInfo(expr->GetDebugLocation()) + " has been set to " + absVal->ToString() + "\n"; std::cout << message; } void ConstAnalysis::MarkExpressionAsMustNotOverflow(Expression& expr) const { if (this->isStable) { expr.Set(true); } } void ConstAnalysis::HandleArithmeticOpOfFloat( ConstDomain& state, const BinaryExpression* binaryExpr, const ConstValue* lhs, const ConstValue* rhs) const { auto dest = binaryExpr->GetResult(); if (!lhs || !rhs) { return state.SetToBound(dest, /* isTop = */ true); } auto left = StaticCast(lhs); auto right = StaticCast(rhs); double res = 0.0; switch (binaryExpr->GetExprKind()) { case ExprKind::ADD: res = left->GetVal() + right->GetVal(); break; case ExprKind::SUB: res = left->GetVal() - right->GetVal(); break; case ExprKind::MUL: res = left->GetVal() * right->GetVal(); break; case ExprKind::DIV: res = left->GetVal() / right->GetVal(); break; default: CJC_ABORT(); } if (std::isnan(res) || std::isinf(res)) { state.SetToBound(dest, /* isTop = */ true); } else { res = CutOffHighBits(res, dest->GetType()->GetTypeKind()); state.Update(dest, std::make_unique(res)); } } void ConstAnalysis::HandleNormalExpressionEffect(ConstDomain& state, const Expression* expression) { auto exceptionKind = ExceptionKind::NA; switch (expression->GetExprMajorKind()) { case ExprMajorKind::MEMORY_EXPR: return; case ExprMajorKind::UNARY_EXPR: HandleUnaryExpr(state, StaticCast(expression), exceptionKind); break; case ExprMajorKind::BINARY_EXPR: HandleBinaryExpr(state, StaticCast(expression), exceptionKind); break; case ExprMajorKind::OTHERS: HandleOthersExpr(state, expression, exceptionKind); break; case ExprMajorKind::STRUCTURED_CTRL_FLOW_EXPR: default: { CJC_ABORT(); return; } } if (exceptionKind == ExceptionKind::SUCCESS) { MarkExpressionAsMustNotOverflow(*const_cast(expression)); } if (expression->GetExprMajorKind() != ExprMajorKind::UNARY_EXPR && expression->GetExprMajorKind() != ExprMajorKind::BINARY_EXPR && expression->GetExprKind() != ExprKind::TYPECAST) { return; } auto exprType = expression->GetResult()->GetType(); if (isDebug && (exprType->IsInteger() || exprType->IsFloat() || exprType->IsRune() || exprType->IsBoolean() || exprType->IsString())) { if (auto absVal = state.CheckAbstractValue(expression->GetResult()); absVal) { PrintDebugMessage(expression, absVal); } } } void ConstAnalysis::HandleUnaryExpr(ConstDomain& state, const UnaryExpression* unaryExpr, ExceptionKind& exceptionKind) { auto operand = unaryExpr->GetOperand(); auto dest = unaryExpr->GetResult(); const ConstValue* absVal = state.CheckAbstractValue(operand); if (absVal == nullptr) { return state.SetToBound(dest, /* isTop = */ true); } switch (unaryExpr->GetExprKind()) { case ExprKind::NEG: { if (absVal->GetConstKind() == ConstValue::ConstKind::UINT) { exceptionKind = HandleNegOpOfInt(state, unaryExpr, absVal); return; } else if (absVal->GetConstKind() == ConstValue::ConstKind::INT) { exceptionKind = HandleNegOpOfInt(state, unaryExpr, absVal); return; } else if (absVal->GetConstKind() == ConstValue::ConstKind::FLOAT) { auto res = -(StaticCast(absVal)->GetVal()); res = CutOffHighBits(res, dest->GetType()->GetTypeKind()); if (std::isnan(res) || std::isinf(res)) { return state.SetToBound(dest, /* isTop = */ true); } else { return state.Update(dest, std::make_unique(res)); } } CJC_ABORT(); } case ExprKind::NOT: { auto boolVal = StaticCast(absVal)->GetVal(); return state.Update(dest, std::make_unique(!boolVal)); } case ExprKind::BITNOT: { if (absVal->GetConstKind() == ConstValue::ConstKind::UINT) { auto uintVal = StaticCast(absVal)->GetVal(); uintVal = CutOffHighBits(~uintVal, operand->GetType()->GetTypeKind()); return state.Update(dest, std::make_unique(uintVal)); } else { auto intVal = StaticCast(absVal)->GetVal(); intVal = CutOffHighBits(~intVal, operand->GetType()->GetTypeKind()); return state.Update(dest, std::make_unique(intVal)); } } default: CJC_ABORT(); } } template <> bool IsTrackedGV>(const GlobalVar& gv) { auto baseTyKind = StaticCast(gv.GetType())->GetBaseType()->GetTypeKind(); return (baseTyKind >= Type::TYPE_INT8 && baseTyKind <= Type::TYPE_UNIT) || baseTyKind == Type::TYPE_TUPLE || baseTyKind == Type::TYPE_STRUCT || baseTyKind == Type::TYPE_ENUM; } template <> ValueDomain HandleNonNullLiteralValue>(const LiteralValue* literalValue) { if (literalValue->IsBoolLiteral()) { return ValueDomain( std::make_unique(StaticCast(literalValue)->GetVal())); } else if (literalValue->IsFloatLiteral()) { // There is no proper types to represent `Float16` in Cangjie. if (literalValue->GetType()->GetTypeKind() != Type::TypeKind::TYPE_FLOAT16) { return ValueDomain( std::make_unique(StaticCast(literalValue)->GetVal())); } else { return ValueDomain(/* isTop = */true); } } else if (literalValue->IsIntLiteral()) { if (auto intTy = StaticCast(literalValue->GetType()); intTy->IsSigned()) { return ValueDomain( std::make_unique(StaticCast(literalValue)->GetSignedVal())); } else { return ValueDomain( std::make_unique(StaticCast(literalValue)->GetUnsignedVal())); } } else if (literalValue->IsRuneLiteral()) { return ValueDomain( std::make_unique(StaticCast(literalValue)->GetVal())); } else if (literalValue->IsStringLiteral()) { return ValueDomain( std::make_unique(StaticCast(literalValue)->GetVal())); } else if (literalValue->IsUnitLiteral()) { return ValueDomain(/* isTop = */true); } else { InternalError("Unsupported const val kind"); return ValueDomain(/* isTop = */true); } } void ConstAnalysis::HandleBinaryExpr( ConstDomain& state, const BinaryExpression* binaryExpr, ExceptionKind& exceptionKind) { auto kind = binaryExpr->GetExprKind(); switch (kind) { case ExprKind::ADD: case ExprKind::SUB: case ExprKind::MUL: case ExprKind::DIV: case ExprKind::MOD: { exceptionKind = HandleArithmeticOp(state, binaryExpr, kind); return; } case ExprKind::EXP: { exceptionKind = HandleExpOp(state, binaryExpr); return; } case ExprKind::LSHIFT: case ExprKind::RSHIFT: case ExprKind::BITAND: case ExprKind::BITXOR: case ExprKind::BITOR: return (void)HandleBitwiseOp(state, binaryExpr, kind); case ExprKind::LT: case ExprKind::GT: case ExprKind::LE: case ExprKind::GE: case ExprKind::EQUAL: case ExprKind::NOTEQUAL: return HandleRelationalOp(state, binaryExpr); case ExprKind::AND: case ExprKind::OR: return HandleLogicalOp(state, binaryExpr); default: CJC_ABORT(); } } void ConstAnalysis::HandleOthersExpr(ConstDomain& state, const Expression* expression, ExceptionKind& exceptionKind) { switch (expression->GetExprKind()) { case ExprKind::TYPECAST: { exceptionKind = HandleTypeCast(state, StaticCast(expression)); return; } case ExprKind::INTRINSIC: { return (void)HandleIntrinsic(state, StaticCast(expression)); } case ExprKind::CONSTANT: case ExprKind::APPLY: case ExprKind::FIELD: return; default: { auto dest = expression->GetResult(); return state.SetToTopOrTopRef(dest, /* isRef = */ dest->GetType()->IsRef()); } } } // ab, a<=b, a>=b, a!=b, a==b void ConstAnalysis::HandleRelationalOp(ConstDomain& state, const BinaryExpression* binaryExpr) { auto kind = binaryExpr->GetExprKind(); auto dest = binaryExpr->GetResult(); auto lhs = binaryExpr->GetLHSOperand(); auto rhs = binaryExpr->GetRHSOperand(); auto lhsTy = lhs->GetType(); CJC_ASSERT(lhsTy == rhs->GetType()); if (lhsTy->IsUnit() || (lhs == rhs && !lhsTy->IsFloat())) { bool res = true; if (kind == ExprKind::LT || kind == ExprKind::GT || kind == ExprKind::NOTEQUAL) { res = false; } return state.Update(dest, std::make_unique(res)); } const ConstValue* lhsAbsVal = state.CheckAbstractValue(lhs); const ConstValue* rhsAbsVal = state.CheckAbstractValue(rhs); if (!lhsAbsVal || !rhsAbsVal) { return state.SetToBound(dest, /* isTop = */ true); } switch (lhsAbsVal->GetConstKind()) { case ConstValue::ConstKind::UINT: return HandleRelationalOpOfType(state, binaryExpr, lhsAbsVal, rhsAbsVal); case ConstValue::ConstKind::INT: return HandleRelationalOpOfType(state, binaryExpr, lhsAbsVal, rhsAbsVal); case ConstValue::ConstKind::FLOAT: return HandleRelationalOpOfType(state, binaryExpr, lhsAbsVal, rhsAbsVal); case ConstValue::ConstKind::BOOL: return HandleRelationalOpOfType(state, binaryExpr, lhsAbsVal, rhsAbsVal); case ConstValue::ConstKind::RUNE: return HandleRelationalOpOfType(state, binaryExpr, lhsAbsVal, rhsAbsVal); case ConstValue::ConstKind::STRING: return HandleRelationalOpOfType(state, binaryExpr, lhsAbsVal, rhsAbsVal); default: CJC_ABORT(); } } // a&&b, a||b void ConstAnalysis::HandleLogicalOp(ConstDomain& state, const BinaryExpression* binaryExpr) const { auto lhs = binaryExpr->GetLHSOperand(); auto rhs = binaryExpr->GetRHSOperand(); auto dest = binaryExpr->GetResult(); auto lhsVal = RawStaticCast(state.CheckAbstractValue(lhs)); auto rhsVal = RawStaticCast(state.CheckAbstractValue(rhs)); if (binaryExpr->GetExprKind() == ExprKind::AND) { if (lhsVal && rhsVal) { return state.Update(dest, std::make_unique(lhsVal->GetVal() && rhsVal->GetVal())); } else if ((lhsVal && !lhsVal->GetVal()) || (rhsVal && !rhsVal->GetVal())) { return state.Update(dest, std::make_unique(false)); } } else { if (lhsVal && rhsVal) { return state.Update(dest, std::make_unique(lhsVal->GetVal() && rhsVal->GetVal())); } else if ((lhsVal && lhsVal->GetVal()) || (rhsVal && rhsVal->GetVal())) { return state.Update(dest, std::make_unique(true)); } } return state.SetToBound(dest, /* isTop = */ true); } std::optional ConstAnalysis::HandleTerminatorEffect(ConstDomain& state, const Terminator* terminator) { ConstAnalysis::ExceptionKind res = ExceptionKind::NA; switch (terminator->GetExprKind()) { // already handled by the framework // case ExprKind::ALLOCATE_WITH_EXCEPTION: // case ExprKind::RAW_ARRAY_ALLOCATE_WITH_EXCEPTION: // case ExprKind::RAW_ARRAY_LITERAL_ALLOCATE_WITH_EXCEPTION: // case ExprKind::APPLY_WITH_EXCEPTION: // case ExprKind::INVOKE_WITH_EXCEPTION: case ExprKind::GOTO: case ExprKind::EXIT: break; case ExprKind::BRANCH: return HandleBranchTerminator(state, StaticCast(terminator)); case ExprKind::MULTIBRANCH: return HandleMultiBranchTerminator(state, StaticCast(terminator)); case ExprKind::TYPECAST_WITH_EXCEPTION: res = HandleTypeCast(state, StaticCast(terminator)); break; case ExprKind::INT_OP_WITH_EXCEPTION: res = HandleIntOpWithExcepTerminator(state, StaticCast(terminator)); break; case ExprKind::INTRINSIC_WITH_EXCEPTION: res = HandleIntrinsic(state, StaticCast(terminator)); break; default: { auto dest = terminator->GetResult(); if (dest) { state.SetToTopOrTopRef(dest, dest->GetType()->IsRef()); } break; } } if (res == ExceptionKind::SUCCESS) { MarkExpressionAsMustNotOverflow(*const_cast(terminator)); return terminator->GetSuccessor(0); } else if (res == ExceptionKind::FAIL) { return terminator->GetSuccessor(1); } return std::nullopt; } std::optional ConstAnalysis::HandleBranchTerminator(const ConstDomain& state, const Branch* branch) const { auto cond = branch->GetCondition(); if (auto condVal = state.CheckAbstractValue(cond); condVal) { auto boolVal = StaticCast(condVal)->GetVal(); return boolVal ? branch->GetTrueBlock() : branch->GetFalseBlock(); } return std::nullopt; } std::optional ConstAnalysis::HandleMultiBranchTerminator( const ConstDomain& state, const MultiBranch* multi) const { auto cond = multi->GetCondition(); if (auto condVal = state.CheckAbstractValue(cond); condVal) { auto intVal = static_cast(condVal)->GetVal(); auto cases = multi->GetCaseVals(); for (size_t i = 0; i < cases.size(); ++i) { if (intVal == cases[i]) { return multi->GetCaseBlockByIndex(i); } } return multi->GetDefaultBlock(); } return std::nullopt; } ConstAnalysis::ExceptionKind ConstAnalysis::HandleIntOpWithExcepTerminator( ConstDomain& state, const IntOpWithException* intOp) { auto kind = intOp->GetOpKind(); if (kind == ExprKind::NEG) { auto operand = intOp->GetOperand(0); auto absVal = state.CheckAbstractValue(operand); if (!absVal) { state.SetToBound(intOp->GetResult(), /* isTop = */ true); return ExceptionKind::NA; } if (absVal->GetConstKind() == ConstValue::ConstKind::UINT) { return HandleNegOpOfInt(state, intOp, absVal); } else { return HandleNegOpOfInt(state, intOp, absVal); } } if (kind >= ExprKind::ADD && kind < ExprKind::EXP) { return HandleArithmeticOp(state, intOp, kind); } if (kind == ExprKind::EXP) { return HandleExpOp(state, intOp); } if (kind >= ExprKind::LSHIFT && kind <= ExprKind::RSHIFT) { return HandleBitwiseOp(state, intOp, kind); } InternalError("Unsupported IntOpWithException terminator"); return ExceptionKind::NA; } void ConstAnalysis::HandleApplyExpr(ConstDomain& state, const Apply* apply, Value* refObj) { HandleApply(state, apply, refObj); } std::optional ConstAnalysis::HandleApplyWithExceptionTerminator( ConstDomain& state, const ApplyWithException* apply, Value* refObj) { auto res = HandleApply(state, apply, refObj); if (res == ExceptionKind::SUCCESS) { return apply->GetSuccessBlock(); } else if (res == ExceptionKind::FAIL) { return apply->GetErrorBlock(); } else { return std::nullopt; } } } // namespace Cangjie::CHIRcangjie_compiler-1.0.7/src/CHIR/Analysis/ConstMemberVarCollector.cpp000066400000000000000000000121221510705540100253460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file includes de-virtualization information collector for const member. */ #include "cangjie/CHIR/Analysis/ConstMemberVarCollector.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Visitor/Visitor.h" #include "cangjie/Utils/Casting.h" namespace Cangjie::CHIR { void ConstMemberVarCollector::CollectConstMemberVarType() { // only collect non-static member since static var is treated as global in CHIR auto localCustomType = package->GetCurPkgCustomTypeDef(); for (auto& def : localCustomType) { if (def->IsExtend()) { // skip extend continue; } // because parent maybe in other package, it's initialization may not be seen in this package, // so only analyse direct class member. auto members = def->GetDirectInstanceVars(); std::unordered_map index2Type; size_t indexBase = def->GetAllInstanceVarNum() - def->GetDirectInstanceVarNum(); for (size_t i = 0; i < members.size(); i++) { auto& member = members[i]; if (!member.IsImmutable()) { // skip variable member continue; } auto memberType = member.type->StripAllRefs(); if (!memberType->IsCustomType()) { continue; } auto customDef = StaticCast(memberType)->GetCustomTypeDef(); if (!customDef->IsClassLike() || !customDef->CanBeInherited()) { // only analyse virtual member continue; } index2Type.emplace(indexBase + i, MemberInfo(StaticCast(memberType))); } JudgeIfOnlyDerivedType(*def, index2Type); if (index2Type.empty()) { continue; } std::unordered_map res; for (auto& it : index2Type) { if (it.second.derivedType == nullptr) { continue; } res.emplace(it.first, it.second.derivedType); } if (!res.empty()) { constMemberMap.emplace(def, res); } } } void ConstMemberVarCollector::JudgeIfOnlyDerivedType( const CustomTypeDef& def, std::unordered_map& index2Type) { Parameter* param = nullptr; auto preVisit = [this, ¶m, &index2Type](Expression& expr) { if (expr.GetExprKind() != ExprKind::STORE_ELEMENT_REF) { return VisitResult::CONTINUE; } auto stf = StaticCast(&expr); HandleStoreElementRef(stf, param, index2Type); if (index2Type.empty()) { return VisitResult::STOP; } return VisitResult::CONTINUE; }; auto methods = def.GetMethods(); for (auto& method : methods) { if (!method->IsConstructor()) { continue; } // only analyse constructor. auto func = StaticCast(method); param = func->GetParam(0); Visitor::Visit(*func, preVisit, []([[maybe_unused]] Expression& e) { return VisitResult::CONTINUE; }); } } void ConstMemberVarCollector::HandleStoreElementRef( const StoreElementRef* stf, const Value* firstParam, std::unordered_map& index2Type) const { if (stf->GetPath().size() != 1U) { // all members are in one path return; } auto index = stf->GetPath()[0]; if (index2Type.find(index) == index2Type.end()) { return; } auto location = stf->GetLocation(); if (location != firstParam) { // not store to this value, skip. return; } auto sourceValue = GetSourceTargetRecursively(stf->GetValue()); auto sourceType = sourceValue->GetType()->StripAllRefs(); auto& info = index2Type[index]; if (sourceType->IsCustomType() && StaticCast(sourceType)->GetCustomTypeDef()->CanBeInherited()) { // if source type can be inherited, we may get its child type, ending. index2Type.erase(index); return; } if (info.derivedType == nullptr) { // first meet an initialization to this virtual member. info.derivedType = sourceType; } else if (info.derivedType != sourceType) { // get different member type, ending index2Type.erase(index); } } const Value* ConstMemberVarCollector::GetSourceTargetRecursively(const Value* value) { if (!value->IsLocalVar()) { return value; } auto iter = StaticCast(value); while (true) { auto source = GetCastOriginalTarget(*iter->GetExpr()); if (source == nullptr) { return iter; } if (source->IsParameter()) { return source; } if (source->IsLocalVar()) { iter = StaticCast(source); } else { return source; } } } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/Analysis/ConstantRange.cpp000066400000000000000000001213751510705540100233710ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Analysis/ConstantRange.h" #include #include namespace Cangjie::CHIR { PreferredRangeType PreferFromBool(bool useUnsigned) { return useUnsigned ? Unsigned : Signed; } ConstantRange::ConstantRange(IntWidth width, bool full) : lower(full ? SInt::UMaxValue(width) : SInt::UMinValue(width)), upper(lower) { } ConstantRange::ConstantRange(const SInt& v) : lower(v), upper(lower + 1) { } ConstantRange::ConstantRange(const SInt& l, const SInt& u) : lower(l), upper(u) { CJC_ASSERT(lower.Width() == upper.Width() && "ConstantRange with unequal int widths"); CJC_ASSERT((lower != upper || (lower.IsUMaxValue() || lower.IsUMinValue())) && "lower == upper, but they aren't min or max value!"); } ConstantRange ConstantRange::From(RelationalOperation rel, const SInt& v, bool isSigned) { auto width = v.Width(); auto isMin = (isSigned && v.IsSMinValue()) || (!isSigned && v.IsUMinValue()); auto isMax = (isSigned && v.IsSMaxValue()) || (!isSigned && v.IsUMaxValue()); if ((rel == RelationalOperation::LT && isMin) || (rel == RelationalOperation::GT && isMax)) { return Empty(width); } if ((rel == RelationalOperation::LE && isMax) || (rel == RelationalOperation::GE && isMin)) { return Full(width); } auto maxVal = isSigned ? SInt::SMaxValue(width) : SInt::UMaxValue(width); auto minVal = isSigned ? SInt::SMinValue(width) : SInt::UMinValue(width); switch (rel) { case RelationalOperation::EQ: return ConstantRange{v}; case RelationalOperation::NE: // From(NE, 1) = {2, 3, 0} = [2, 1) if the full set is {0..3} return ConstantRange{v + 1, v}; case RelationalOperation::GE: return ConstantRange{v, maxVal + 1}; case RelationalOperation::GT: return ConstantRange{v + 1, maxVal + 1}; case RelationalOperation::LE: return ConstantRange{minVal, v + 1}; case RelationalOperation::LT: return ConstantRange{minVal, v}; default: CJC_ABORT(); return ConstantRange{v}; } } ConstantRange ConstantRange::Empty(IntWidth intWidth) { return ConstantRange(intWidth, false); } ConstantRange ConstantRange::Full(IntWidth intWidth) { return ConstantRange(intWidth, true); } // Create non-empty constant range with the given bounds. If lower and // upper are the same, a full range is returned. ConstantRange ConstantRange::NonEmpty(const SInt& l, const SInt& r) { if (l == r) { return Full(l.Width()); } return ConstantRange{l, r}; } // Return the lower value of this range const SInt& ConstantRange::Lower() const& { return lower; } SInt ConstantRange::Lower() && { return lower; } // Return the upper value of this range const SInt& ConstantRange::Upper() const& { return upper; } SInt ConstantRange::Upper() && { return upper; } IntWidth ConstantRange::Width() const { return lower.Width(); } bool ConstantRange::IsFullSet() const { return lower == upper && lower.IsUMaxValue(); } bool ConstantRange::IsEmptySet() const { return lower == upper && lower.IsUMinValue(); } bool ConstantRange::IsNotEmptySet() const { return !IsEmptySet(); } bool ConstantRange::IsNonTrivial() const { return !IsFullSet(); } bool ConstantRange::IsWrappedSet() const { return lower.Ugt(upper) && !upper.IsZero(); } bool ConstantRange::IsUpperWrapped() const { return lower.Ugt(upper); } bool ConstantRange::IsSignWrappedSet() const { return lower.Sgt(upper) && !upper.IsSMinValue(); } bool ConstantRange::IsUpperSignWrapped() const { return lower.Sgt(upper); } bool ConstantRange::Contains(const SInt& v) const { if (lower == upper) { return IsFullSet(); } if (!IsUpperWrapped()) { // normal case: L---------U // possible v: ^ return lower.Ule(v) && v.Ult(upper); } // wrapped set: -----U L---- // possible v: ^ return lower.Ule(v) || v.Ult(upper); } const SInt& ConstantRange::GetSingleElement() const { CJC_ASSERT(upper == lower + 1); return lower; } bool ConstantRange::IsSingleElement() const { return upper == lower + 1; } bool ConstantRange::IsSizeStrictlySmallerThan(const ConstantRange& rhs) const { CJC_ASSERT(Width() == rhs.Width()); if (IsFullSet()) { return false; } if (rhs.IsFullSet()) { return true; } // e.g. for a set {0..8}, // [1, 8) contains seven elements, upper - lower = 7 = 1111 1111 // [6, 4) six elements, upper - lower = -2 = 1111 1110 = 6 // [6, 4) is size strictly smaller than [1, 8) return (upper - lower).Ult(rhs.upper - rhs.lower); } SInt ConstantRange::MaxValue(bool isUnsigned) const { return isUnsigned ? UMaxValue() : SMaxValue(); } SInt ConstantRange::MinValue(bool isUnsigned) const { return isUnsigned ? UMinValue() : SMinValue(); } std::ostream& operator<<(std::ostream& out, const ConstantRange::Formatter& fmt) { auto& rg = fmt.range; auto& dividor = ConstantRange::Formatter::DIVIDOR; if (rg.IsSingleElement()) { // no dividor output for single element range return out << SInt::Formatter{{fmt.asUnsigned, fmt.radix}, rg.GetSingleElement()}; } // output a dividor before any range output out << dividor; struct ConstantRangeFormatterLock { ~ConstantRangeFormatterLock() { out << ConstantRange::Formatter::DIVIDOR; } std::ostream& out; } lock{out}; // output a dividor after any range output if (rg.IsEmptySet()) { return out; } if (rg.IsFullSet()) { return out << "any"; } bool asUnsigned = fmt.asUnsigned; auto width = rg.Upper().Width(); SInt upperMax{width, 0}; SInt lowerMin{width, 0}; if (fmt.asUnsigned) { upperMax = SInt::UMaxValue(width), lowerMin = SInt::UMinValue(width); } else { upperMax = SInt::SMaxValue(width), lowerMin = SInt::SMinValue(width); } auto isWrapped = asUnsigned ? rg.IsWrappedSet() : rg.IsSignWrappedSet(); auto upper = rg.Upper() - 1; auto lower = rg.Lower(); if (!isWrapped) { if (upper == upperMax) { return out << ">=" << SInt::Formatter{{fmt.asUnsigned, fmt.radix}, lower}; } if (lower == lowerMin) { return out << "<=" << SInt::Formatter{{fmt.asUnsigned, fmt.radix}, upper}; } return out << ">=" << SInt::Formatter{{fmt.asUnsigned, fmt.radix}, lower} << ",<=" << SInt::Formatter{{fmt.asUnsigned, fmt.radix}, upper}; } return out << "<=" << SInt::Formatter{{fmt.asUnsigned, fmt.radix}, upper} << "&>=" << SInt::Formatter{{fmt.asUnsigned, fmt.radix}, lower}; } ConstantRange ConstantRange::Subtract(const SInt& v) const { CJC_ASSERT(v.Width() == Width()); if (lower == upper) { return *this; } return ConstantRange{lower - v, upper - v}; } ConstantRange ConstantRange::Difference(const ConstantRange& rhs) const { return IntersectWith(rhs.Inverse()); } SInt ConstantRange::UMaxValue() const { if (IsFullSet() || IsUpperWrapped()) { return SInt::UMaxValue(Width()); } return Upper() - 1; } SInt ConstantRange::UMinValue() const { if (IsFullSet() || IsWrappedSet()) { return SInt::UMinValue(Width()); } return Lower(); } SInt ConstantRange::SMaxValue() const { if (IsFullSet() || IsUpperSignWrapped()) { return SInt::SMaxValue(Width()); } return Upper() - 1; } SInt ConstantRange::SMinValue() const { if (IsFullSet() || IsSignWrappedSet()) { return SInt::SMinValue(Width()); } return Lower(); } /// Return true if this range is equal to another range. bool ConstantRange::operator==(const ConstantRange& rhs) const { return lower == rhs.lower && upper == rhs.upper; } /// Return true if this range is not equal to another range. bool ConstantRange::operator!=(const ConstantRange& rhs) const { return !operator==(rhs); } static ConstantRange GetPreferredRange(const ConstantRange& a, const ConstantRange& b, PreferredRangeType type) { if (type == Unsigned) { if (!a.IsWrappedSet() && b.IsWrappedSet()) { return a; } if (a.IsWrappedSet() && !b.IsWrappedSet()) { return b; } } else if (type == Signed) { if (!a.IsSignWrappedSet() && b.IsSignWrappedSet()) { return a; } if (a.IsSignWrappedSet() && !b.IsSignWrappedSet()) { return b; } } // type == Smallest or both ranges are non-wrapping or all wrapping. return a.IsSizeStrictlySmallerThan(b) ? a : b; } #ifndef NDEBUG void ConstantRange::Dump(bool asUnsigned) { std::cout << ToString(asUnsigned, Radix::R10); std::cout.flush(); } #endif ConstantRange ConstantRange::Empty() const { return ConstantRange(Width(), false); } ConstantRange ConstantRange::Full() const { return ConstantRange(Width(), true); } ConstantRange ConstantRange::IntersectBothWrapped(const ConstantRange& rhs, PreferredRangeType type) const { // Both ranges are wrapped, the upper bound of rhs has three possibilities: // 1. rhs.upper < this.upper // 2. this.upper <= rhs.upper <= this.lower // 3. rhs.upper > this.lower if (rhs.upper.Ult(upper)) { // the lower bound of rhs has three possibilities: // 1. rhs.lower < this.upper // 2. this.upper <= rhs.lower < this.lower // 3. rhs.lower >= this.lower // ------U L--- : this // --U L--------- : rhs if (rhs.lower.Ult(upper)) { return GetPreferredRange(*this, rhs, type); } // -----U L-- : this // ---U L----- : rhs if (rhs.lower.Ult(lower)) { return ConstantRange{lower, rhs.upper}; } // ----U L----- : this // --U L--- : rhs return rhs; } else if (rhs.upper.Ule(lower)) { // the lower bound of rhs has two possibilities: // 1. rhs.lower < this.lower // 2. rhs.lower >= this.lower // --U L---- : this // ----U L------ : rhs if (rhs.lower.Ult(lower)) { return *this; } // --U L------- : this // ----U L----- : rhs return ConstantRange{rhs.lower, upper}; } else { // rhs.lower >= this.lower // --U L-------- : this // --------U L--- : rhs return GetPreferredRange(*this, rhs, type); } } ConstantRange ConstantRange::IntersectWrappedWithUnwrapped(const ConstantRange& rhs, PreferredRangeType type) const { // this range is wrapped, and the other isn't. The lower bound of rhs has three possibilities: // 1. rhs.lower < this.upper // 2. this.upper <= rhs.lower < this.lower // 3. rhs.lower >= this.lower if (rhs.lower.Ult(upper)) { // the upper bound of rhs has three possibilities: // 1. rhs.upper < this.upper // 2. this.upper <= rhs.upper <= this.lower // 3. rhs.upper > this.lower // ------U L--- : this // L--U : rhs if (rhs.upper.Ult(upper)) { return rhs; } // ------U L--- : this // L------U : rhs if (rhs.upper.Ule(lower)) { return ConstantRange{rhs.lower, upper}; } // ------U L--- : this // L----------U : rhs return GetPreferredRange(*this, rhs, type); } else if (rhs.lower.Ult(lower)) { // the upper bound of rhs has two possibilities // 1. rhs.upper <= this.lower // 2. rhs.upper > this.lower // ------U L-- : this // L-U : rhs if (rhs.upper.Ule(lower)) { return Empty(); } // ------U L--- : this // L-----U : rhs return ConstantRange{lower, rhs.upper}; } else { // rhs.lower >= this.lower // ------U L---- : this // L-U : rhs return rhs; } } ConstantRange ConstantRange::IntersectBothUnwrapped(const ConstantRange& rhs) const { // Both ranges are unwrapped, the lower bound of this range has two possibilities: // 1. this.lower < rhs.lower // 2. this.lower >= rhs.lower if (lower.Ult(rhs.lower)) { // the upper bound of this range has three possibilities: // 1. this.upper <= rhs.lower // 2. rhs.lower < this.upper < rhs.upper // 3. this.upper >= rhs.upper // L--U : this // L---U : rhs if (upper.Ule(rhs.lower)) { return Empty(); } // L-----U : this // L--------U : rhs if (upper.Ult(rhs.upper)) { return ConstantRange{rhs.lower, upper}; } // L--------U : this // L---U : rhs return rhs; } else { // this.lower >= rhs.lower // the upper bound of rhs has three possibilities: // 1. rhs.upper <= this.lower // 2. this.lower < rhs.upper < this.upper // 3. rhs.upper >= this.upper // L---U : this // L--U : rhs if (rhs.upper.Ule(lower)) { return Empty(); } // L-----U : this // L-------U : rhs if (rhs.upper.Ult(upper)) { return ConstantRange{lower, rhs.upper}; } // L--U : this // L----------U : rhs return *this; } } ConstantRange ConstantRange::IntersectWith(const ConstantRange& rhs, PreferredRangeType type) const { CJC_ASSERT(Width() == rhs.Width() && "ConstantRange types don't agree!"); // Handle common cases. if (IsEmptySet() || rhs.IsFullSet()) { return *this; } if (rhs.IsEmptySet() || IsFullSet()) { return rhs; } auto isThisUpperWrapped = IsUpperWrapped(); auto isRhsUpperWrapped = rhs.IsUpperWrapped(); if (!isThisUpperWrapped && isRhsUpperWrapped) { return rhs.IntersectWith(*this, type); } else if (!isThisUpperWrapped && !isRhsUpperWrapped) { return IntersectBothUnwrapped(rhs); } else if (isThisUpperWrapped && !isRhsUpperWrapped) { return IntersectWrappedWithUnwrapped(rhs, type); } else { return IntersectBothWrapped(rhs, type); } } ConstantRange ConstantRange::UnionBothWrapped(const ConstantRange& rhs) const { // There are two possibilities, the union of the two ranges covers the full set or not. // 1. the union of the two ranges covers the full set. // ------U L--- and --U L-------- : this // --U L--------- --------U L--- : rhs if (rhs.lower.Ule(upper) || lower.Ule(rhs.upper)) { return Full(); } // 2. the union of the two ranges does not cover the full set. // -----U L-- : this // ---U L----- : rhs auto newLower = rhs.lower.Ult(lower) ? rhs.lower : lower; auto newUpper = rhs.upper.Ugt(upper) ? rhs.upper : upper; return ConstantRange{newLower, newUpper}; } ConstantRange ConstantRange::UnionWrappedWithUnwrapped(const ConstantRange& rhs, PreferredRangeType type) const { // this range is wrapped, and the other isn't. The lower bound of rhs has three possibilities: // 1. rhs.lower <= this.upper // 2. this.upper < rhs.lower < this.lower // 3. rhs.lower >= this.lower if (rhs.lower.Ule(upper)) { // the upper bound of rhs has three possibilities: // 1. rhs.upper < this.upper // 2. this.upper <= rhs.upper < this.lower // 3. rhs.upper >= this.lower // ------U L--- : this // L--U : rhs if (rhs.upper.Ult(upper)) { return *this; } // ------U L--- : this // L------U : rhs if (rhs.upper.Ult(lower)) { return ConstantRange{lower, rhs.upper}; } // ------U L--- : this // L----------U : rhs return Full(); } else if (rhs.lower.Ult(lower)) { // the upper bound of rhs has two possibilities // 1. rhs.upper < this.lower // 2. rhs.upper >= this.lower // ----U L- : this // L--U : rhs // results in one of // ----U L------ : this // ----------U L- : rhs // depending on the preferred range type. if (rhs.upper.Ult(lower)) { return GetPreferredRange(ConstantRange{lower, rhs.upper}, ConstantRange{rhs.lower, upper}, type); } // ------U L-- : this // L---U : rhs return ConstantRange{rhs.lower, upper}; } else { // rhs.lower >= this.lower // ------U L---- : this // L--U : rhs return *this; } } ConstantRange ConstantRange::UnionBothUnwrapped(const ConstantRange& rhs, PreferredRangeType type) const { // There are two possibilities, the two ranges have an intersection or not. // 1. the two ranges are disjoint // L---U and L--U : this // L---U L----U : rhs // result in one of // L----------U // -----U L---- // depending on the preferred range type. if (rhs.upper.Ult(lower) || upper.Ult(rhs.lower)) { return GetPreferredRange(ConstantRange{lower, rhs.upper}, ConstantRange{rhs.lower, upper}, type); } // 2. the two ranges overlap // L-----U : this // L-------U : rhs auto newLower = rhs.lower.Ult(lower) ? rhs.lower : lower; auto newUpper = (rhs.upper - 1).Ugt(upper - 1) ? rhs.upper : upper; if (newLower.IsZero() && newUpper.IsZero()) { return Full(); } return ConstantRange{newLower, newUpper}; } ConstantRange ConstantRange::UnionWith(const ConstantRange& rhs, PreferredRangeType type) const { CJC_ASSERT(Width() == rhs.Width() && "ConstantRange types don't agree!"); if (IsFullSet() || rhs.IsEmptySet()) { return *this; } if (rhs.IsFullSet() || IsEmptySet()) { return rhs; } auto isThisUpperWrapped = IsUpperWrapped(); auto isRhsUpperWrapped = rhs.IsUpperWrapped(); if (isThisUpperWrapped) { if (isRhsUpperWrapped) { return UnionBothWrapped(rhs); } else { return UnionWrappedWithUnwrapped(rhs, type); } } else { if (isRhsUpperWrapped) { return rhs.UnionWith(*this, type); } else { return UnionBothUnwrapped(rhs, type); } } } ConstantRange ConstantRange::ZeroExtend(IntWidth width) const { if (IsEmptySet()) { return Empty(width); } CJC_ASSERT(Width() < width && "Not a value extension"); // e.g. width 8->16 // Full set: [255, 255) => [0, 256) // Wrapped set: [254, 3) => [0, 256) if (IsFullSet() || IsWrappedSet()) { return ConstantRange{SInt{width, 0}, SInt::GetOneBitSet(width, static_cast(Width()))}; } // Special case: [254, 0) = {254, 255} => [254, 256) if (upper.IsUMinValue()) { return ConstantRange{lower.ZExt(width), SInt::GetOneBitSet(width, static_cast(Width()))}; } // Normal case: return ConstantRange{lower.ZExt(width), upper.ZExt(width)}; } ConstantRange ConstantRange::SignExtend(IntWidth width) const { if (IsEmptySet()) { return Empty(width); } CJC_ASSERT(Width() < width && "Not a value extension"); // e.g. width 8->16 // Full set: Full set => [-128, 128) // Wrapped set: [126, -2) => [-128, 128) if (IsFullSet() || IsSignWrappedSet()) { unsigned lowerWidth = static_cast(Width()); CJC_ASSERT(lowerWidth > 0); // -128 = 0xFF80 SInt lo = SInt::GetHighBitsSet(width, (static_cast(width) - lowerWidth) + 1); // 128 = 0x007F + 1 SInt up = SInt::GetLowBitsSet(width, lowerWidth - 1) + 1; return ConstantRange{lo, up}; } // Special case: [-3, -128) => [-3, 128) if (upper.IsSMinValue()) { return ConstantRange{lower.SExt(width), upper.ZExt(width)}; } // Normal case: return ConstantRange{lower.SExt(width), upper.SExt(width)}; } std::pair ConstantRange::SplitWrapping(bool asUnsigned) const { CJC_ASSERT(asUnsigned ? IsWrappedSet() : IsSignWrappedSet()); return {ConstantRange::From(RelationalOperation::LT, upper, !asUnsigned), ConstantRange::From(RelationalOperation::GE, lower, !asUnsigned)}; } ConstantRange ConstantRange::Truncate(IntWidth dstWidth) const { CJC_ASSERT(Width() > dstWidth && "Not a value truncation"); if (IsEmptySet()) { return Empty(dstWidth); } if (IsFullSet()) { return Full(dstWidth); } SInt lowerDiv{lower}; SInt upperDiv{upper}; // Analyse unsigned wrapped sets in their two parts: [0, upper) ∪ [lower, maxVal], // which is equivalent to [maxVal, upper) ∪ [lower, maxVal). // -------------U L------- // [0, upper) [lower, maxVal] // We first calculate the result of [maxVal, upper), and then use the non-wrapped // set code to analysis the [lower, maxVal) part. ConstantRange wrappedPart{dstWidth, false}; if (IsUpperWrapped()) { // If upper is greater than or equal to maxVal(dstWidth), // it covers the whole truncated range. // e.g. width 16 -> 8 // original range (16-bit): --------------------------U L-------- // full range (8-bit): -------------- // | | // 0 255 // special case: when the upper is equal to maxVal(dstWidth) // e.g. width 16 -> 8 // [60000, 255) = [60000, 65535) ∪ [65535, 255) // [65535, 255) covers the full set with width 8 ({0..255}) // 65535 is 0xFFFF, and casting it to width 8 results in 255 (which is 0xFF) // Therefore, 255 is included. auto dstMaxVal = SInt::UMaxValue(dstWidth); if (upper.Uge(dstMaxVal.ZExt(Width()))) { return Full(dstWidth); } wrappedPart = ConstantRange{dstMaxVal, upper.Trunc(dstWidth)}; // We have calculate the [maxVal, upper) part, and cover the maxVal case. // Therefore, return if the remaining is just [maxVal, maxVal). // original range (16-bit): ----------U L // | | // 243 65535 if (lowerDiv.IsUMaxValue()) { return wrappedPart; } upperDiv.SetAllBits(); } // Logic for non-wrapped sets // Cut off the most significant bits that exceeds the destination width. if (lowerDiv.ActiveBits() > static_cast(dstWidth)) { // e.g. 16->8, [0x0FF0, 0xFFF4) // 0x0FF0 & 0xFF00 = 0x0F00 SInt adjust = lowerDiv & SInt::GetBitsSetFrom(Width(), static_cast(dstWidth)); lowerDiv -= adjust; // 0x00F0 upperDiv -= adjust; // 0xF0F4 } ConstantRange normalPart{dstWidth, false}; auto upperDivWidth = upperDiv.ActiveBits(); if (upperDivWidth <= dstWidth) { // e.g. lowerDiv: 0000 0000 0011 0100 // upperDiv: 0000 0000 1100 0110 // results in [0b00110100, 0b11000100) return ConstantRange{lowerDiv.Trunc(dstWidth), upperDiv.Trunc(dstWidth)}.UnionWith(wrappedPart); } else if (upperDivWidth == static_cast(dstWidth) + 1) { // Clear the the most significant bit so that upperDiv wraps around. upperDiv.ClearBit(static_cast(dstWidth)); if (upperDiv.Ult(lowerDiv)) { // e.g. lowerDiv: 0000 0000 0111 0100 // upperDiv: 0000 0001 0000 0110 // after clear bit: 0000 0000 0000 0110 // results in [0b01110100, 0b00000110) return ConstantRange{lowerDiv.Trunc(dstWidth), upperDiv.Trunc(dstWidth)}.UnionWith(wrappedPart); } else { // e.g. lowerDiv: 0000 0000 0011 0100 // upperDiv: 0000 0001 1100 0110 // after clear bit: 0000 0000 1100 0110 // results in a full set with 8-bit width return Full(dstWidth); } } else { // e.g. lowerDiv: 0000 0000 0111 0100 // upperDiv: 0000 0111 0000 0110 return Full(dstWidth); } } ConstantRange::Formatter ConstantRange::ToString(bool asUnsigned, Radix radix) const { return {{asUnsigned, radix}, *this}; } ConstantRange ConstantRange::Add(const ConstantRange& rhs) const { if (IsEmptySet() || rhs.IsEmptySet()) { return Empty(); } if (IsFullSet() || rhs.IsFullSet()) { return Full(); } auto newLower = Lower() + rhs.Lower(); // [0, 1) + [0, 3) = {0} + {0, 1, 2} = {0, 1, 2} = [0, 3) if the full set is {0..3} auto newUpper = Upper() + rhs.Upper() - 1; if (newLower == newUpper) { return Full(); } ConstantRange res{newLower, newUpper}; if (res.IsSizeStrictlySmallerThan(*this) || res.IsSizeStrictlySmallerThan(rhs)) { // We've wrapped, therefore, full set. // e.g. [2, 240) + [2, 230) results in [4, 212) // [2, 240): (2)----------------------------(240) // [2, 230): (2)------------------------(230) // [4, 212): (4)---------------(212) // actually: (4)-------------------------------(255) // (0)-------------------(212) return Full(); } return res; } ConstantRange ConstantRange::Sub(const ConstantRange& rhs) const { if (IsEmptySet() || rhs.IsEmptySet()) { return Empty(); } if (IsFullSet() || rhs.IsFullSet()) { return Full(); } auto newLower = Lower() - rhs.Upper() + 1; auto newUpper = Upper() - rhs.Lower(); if (newLower == newUpper) { return Full(); } ConstantRange res{newLower, newUpper}; if (res.IsSizeStrictlySmallerThan(*this) || res.IsSizeStrictlySmallerThan(rhs)) { // e.g. [10, 240) - [2, 231) results in [36, 238) // [10, 240): (10)--------------------- // [2, 231): (2)--------------------- // [36, 238): (36)---------------------(238) // actually: ----(8) (36)---------------------------- // (9)-----------------------------(238) return Full(); } return res; } ConstantRange ConstantRange::UMul(const ConstantRange& rhs) const { if (IsEmptySet() || rhs.IsEmptySet()) { return Empty(); } auto lhsMin = UMinValue(); auto lhsMax = UMaxValue(); auto rhsMin = rhs.UMinValue(); auto rhsMax = rhs.UMaxValue(); bool ovf1; bool ovf2; auto lo = lhsMin.UMulOvf(rhsMin, ovf1); auto up = lhsMax.UMulOvf(rhsMax, ovf2) + 1; if (ovf1 || ovf2) { return Full(); } else { return NonEmpty(lo, up); } } ConstantRange ConstantRange::SMul(const ConstantRange& rhs) const { if (IsEmptySet() || rhs.IsEmptySet()) { return Empty(); } auto lhsMin = SMinValue(); auto lhsMax = SMaxValue(); auto rhsMin = rhs.SMinValue(); auto rhsMax = rhs.SMaxValue(); bool ovf1; bool ovf2; bool ovf3; bool ovf4; auto products = {lhsMin.SMulOvf(rhsMin, ovf1), lhsMin.SMulOvf(rhsMax, ovf2), lhsMax.SMulOvf(rhsMin, ovf3), lhsMax.SMulOvf(rhsMax, ovf4)}; if (ovf1 || ovf2 || ovf3 || ovf4) { return Full(); } else { auto cmp = [](const SInt& a, const SInt& b) { return a.Slt(b); }; auto lo = std::min(products, cmp); auto up = std::max(products, cmp) + 1; return NonEmpty(lo, up); } } ConstantRange ConstantRange::UDiv(const ConstantRange& rhs) const { if (IsEmptySet() || rhs.IsEmptySet() || rhs.UMaxValue().IsZero()) { return Empty(); } // calculate the new lower bound auto newLower = UMinValue().UDiv(rhs.UMaxValue()); // calculate the new upper bound, usually it's lhsMax/rhsMin // when rhs is wrapped, the rhsMin will be zero, and we need to find the non-zero minimum. auto rhsUMin = rhs.UMinValue(); if (rhsUMin.IsZero()) { // Usually the mim value excluding zero will be 1, except for a range // in the form of [X, 1) in which case it would be X. // e.g. [6, 1) = {6,7,...,maxVal,0}, the minVal is 6 if (rhs.Upper() == 1) { rhsUMin = rhs.Lower(); } else { rhsUMin = 1; } } auto newUpper = UMaxValue().UDiv(rhsUMin) + 1; return NonEmpty(newLower, newUpper); } ConstantRange ConstantRange::SDivImpl( const ConstantRange& rhs, const std::function& div) const { // We split up the LHS and RHS into positive and negative components // and then also compute the positive and negative components of the result // separately by combining division results with the appropriate signs. auto zero = SInt::Zero(Width()); auto signedMin = SInt::SMinValue(Width()); // e.g. for a set {-5,...,5}, calculate [-4, 4) / [-2, 3) ConstantRange posFilter{SInt(Width(), 1), signedMin}; ConstantRange negFilter{signedMin, zero}; ConstantRange posL = IntersectWith(posFilter); // [-4, 4) ∪ [1, -5) = [1, 4) ConstantRange negL = IntersectWith(negFilter); // [-4, 4) ∪ [-5, 0) = [-4, 0) ConstantRange posR = rhs.IntersectWith(posFilter); // [-2, 3) ∪ [1, -5) = [1, 3) ConstantRange negR = rhs.IntersectWith(negFilter); // [-2, 3) ∪ [-5, 0) = [-2, 0) ConstantRange posDivPosRes = Empty(); if (!posL.IsEmptySet() && !posR.IsEmptySet()) { // + / + = + // the lower bound is lhsPosMin / rhsPosMax, and the upper bound is lhsPosMax / rhsPosMin posDivPosRes = ConstantRange{div(posL.lower, posR.upper - 1), div(posL.upper - 1, posR.lower) + 1}; } ConstantRange negDivNegRes = Empty(); if (!negL.IsEmptySet() && !negR.IsEmptySet()) { // - / - = + // the lower bound is lhsNegMax / rhsNegMin, and the upper bound is lhsNegMin / rhsNegMax + 1 // e.g. [-4, 0) / [-2, 0) = {-4, -3, -2, -1} / {-2, -1} // lower is -1/-2 = 0, and upper is -4/-1 + 1 = 5 auto lo = div(negL.upper - 1, negR.lower); auto up = div(negL.lower, (negR.upper - 1)) + 1; // When calculate the upper bound, // we need to deal with one tricky case here: signedMin / -1 is UB, // so we'll want to exclude this case when calculating bounds. // e.g. {-128,...} / {...,-1} // We handle this by dropping either signedMin from the lhs or -1 from the rhs. if (negL.lower.IsSMinValue() && negR.upper.IsZero()) { // we first try to drop -1 from the negRhs, and find the adjacent upper if (!negR.lower.IsAllOnes()) { // if negR includes -1 // normal case: // negR: --------------- // | | // [-X, 0) SInt adjNegRUpper = negR.upper - 1; // actually -1, and now the result is {-128,...} / {...,-2} // In a special case, we can obtain more precise upper // when rhs.lower = -1, the adjacent negative upper is -X // rhs: ------U L------------------- // | | // -X -1 // note: rhs ∪ negFilter results in [-128, 0) if (rhs.lower.IsAllOnes()) { adjNegRUpper = rhs.upper; } up = div(negL.lower, adjNegRUpper - 1) + 1; } // if -1 is the only element in the rhs, we try to drop signedMin from the negLhs, // and find the adjecant lower if (negL.upper != signedMin + 1) { // normal case: // negL: ------------ // | | // [-128, -X) SInt adjNegLLower = negL.lower + 1; // actually -127 if (upper == signedMin + 1) { // lhs: -U L--- // | | // -127 -X // note: negL.upper != -127, which means lower must be negative // lhs ∪ negFilter results in [-128, 0) // and -X is more precise than -127 adjNegLLower = lower; } up = div(adjNegLLower, (negR.upper - 1)) + 1; } } negDivNegRes = ConstantRange{lo, up}; } auto posRes = posDivPosRes.UnionWith(negDivNegRes); ConstantRange posDivNegRes = Empty(); if (!posL.IsEmptySet() && !negR.IsEmptySet()) { // + / - = - // the lower bound is lhsPosMax / rhsNegMax, and the upper bound is lhsPosMin / rhsNegMin + 1 // e.g. [2, 5) / [-4, -1) = {2, 3, 4} / {-4, -3, -2} // lower is 4/-2 = -2, and upper is 2/-4 + 1 = 1 posDivNegRes = ConstantRange{div(posL.upper - 1, negR.upper - 1), div(posL.lower, negR.lower) + 1}; } ConstantRange negDivPosRes = Empty(); if (!negL.IsEmptySet() && !posR.IsEmptySet()) { // - / + = - // the lower bound is lhsNegMin / rhsPosMin, and the upper bound is lhsNegMax / rhsPosMax + 1 // e.g. [-4, -1) / [2, 5) = {-4, -3, -2} / {2, 3, 4} // lower is -4/2 = -2, and upper is -2/4 + 1 = 1 negDivPosRes = ConstantRange{div(negL.lower, posR.lower), div(negL.upper - 1, posR.upper - 1) + 1}; } auto negRes = posDivNegRes.UnionWith(negDivPosRes); // Prefer a non-wrapping signed range here. auto res = negRes.UnionWith(posRes, PreferredRangeType::Signed); // Preserve the zero that we dropped when splitting the lhs by sign. if (Contains(zero) && (!posR.IsEmptySet() || !negR.IsEmptySet())) { res = res.UnionWith(ConstantRange(zero)); } return res; } ConstantRange ConstantRange::SDivSat(const ConstantRange& rhs) const { auto div = [](const SInt& a, const SInt& b) { return a.SDivSat(b); }; return SDivImpl(rhs, div); } ConstantRange ConstantRange::SDiv(const ConstantRange& rhs) const { auto div = [](const SInt& a, const SInt& b) { return a.SDiv(b); }; return SDivImpl(rhs, div); } ConstantRange ConstantRange::URem(const ConstantRange& rhs) const { if (IsEmptySet() || rhs.IsEmptySet() || rhs.UMaxValue().IsZero()) { return Empty(); } if (rhs.IsSingleElement()) { auto rhsInt = rhs.GetSingleElement(); if (rhsInt.IsZero()) { // X % 0 results in an empty set return Empty(); } if (this->IsSingleElement()) { auto lhsInt = this->GetSingleElement(); return ConstantRange{lhsInt.URem(rhsInt)}; } } // L % R for L < R is L. // e.g. [2, 4) % [6, 8) = {2, 3} % {6, 7} if (UMaxValue().Ult(rhs.UMinValue())) { return *this; } // L % R is <= L and < R. // lhs: ------- // rhs: ------ // ^^ makes the lower to be 0 auto lo = SInt::Zero(Width()); auto up = SInt::UMin(UMaxValue(), (rhs.UMaxValue() - 1)) + 1; return NonEmpty(lo, up); } ConstantRange ConstantRange::SRem(const ConstantRange& rhs) const { if (IsEmptySet() || rhs.IsEmptySet()) { return Empty(); } if (rhs.IsSingleElement()) { auto rhsInt = rhs.GetSingleElement(); if (rhsInt.IsZero()) { // X % 0 results in an empty set return Empty(); } if (this->IsSingleElement()) { auto lhsInt = this->GetSingleElement(); return ConstantRange{lhsInt.SRem(rhsInt)}; } } auto absRhs = rhs.Abs(); auto minAbsRhs = absRhs.UMinValue(); auto maxAbsRhs = absRhs.UMaxValue(); if (maxAbsRhs.IsZero()) { return Empty(); } if (minAbsRhs.IsZero()) { ++minAbsRhs; } auto minLhs = SMinValue(); auto maxLhs = SMaxValue(); if (minLhs.IsNonNeg()) { // lhs is a set of non-negative integers. // Same logic as URem // L % R for L < R is L. if (maxLhs.Ult(minAbsRhs)) { return *this; } // L % R is <= L and < R. auto lo = SInt::Zero(Width()); auto up = SInt::UMin(maxLhs, maxAbsRhs - 1) + 1; return ConstantRange{lo, up}; } else if (maxLhs.IsNeg()) { // lhs is a set of negative integers. // Same basic logic as above, but the result is negative. // e.g. [-6, -2) % [7, 9) if (minLhs.Ugt(-minAbsRhs)) { return *this; } // -L % R is >= -L and > R. // e.g. [-6, -2) % [4, 8) // lhs: ------ // -rhs: -------- // ^^^ makes the max result to be 0 (upper to be 1) auto lo = SInt::UMax(minLhs, -maxAbsRhs + 1); auto up = SInt(Width(), 1); return ConstantRange{lo, up}; } else { // lhs range crosses zero. // minLhs = -128, maxLhs = 127, maxAbsRhs = 127 // lo = UMax(-128, -(127) + 1) // up = UMin(127, 127 - 1) + 1 auto lo = SInt::UMax(minLhs, -maxAbsRhs + 1); auto up = SInt::UMin(maxLhs, maxAbsRhs - 1) + 1; return ConstantRange{lo, up}; } } ConstantRange ConstantRange::UAddSat(const ConstantRange& rhs) const { if (IsEmptySet() || rhs.IsEmptySet()) { return Empty(); } auto lo = UMinValue().UAddSat(rhs.UMinValue()); auto up = UMaxValue().UAddSat(rhs.UMaxValue()) + 1; return NonEmpty(lo, up); } ConstantRange ConstantRange::SAddSat(const ConstantRange& rhs) const { if (IsEmptySet() || rhs.IsEmptySet()) { return Empty(); } auto lo = SMinValue().SAddSat(rhs.SMinValue()); auto up = SMaxValue().SAddSat(rhs.SMaxValue()) + 1; return NonEmpty(lo, up); } ConstantRange ConstantRange::USubSat(const ConstantRange& rhs) const { if (IsEmptySet() || rhs.IsEmptySet()) { return Empty(); } auto lo = UMinValue().USubSat(rhs.UMaxValue()); auto up = UMaxValue().USubSat(rhs.UMinValue()) + 1; return NonEmpty(lo, up); } ConstantRange ConstantRange::SSubSat(const ConstantRange& rhs) const { if (IsEmptySet() || rhs.IsEmptySet()) { return Empty(); } auto lo = SMinValue().SSubSat(rhs.SMaxValue()); auto up = SMaxValue().SSubSat(rhs.SMinValue()) + 1; return NonEmpty(lo, up); } ConstantRange ConstantRange::UMulSat(const ConstantRange& rhs) const { if (IsEmptySet() || rhs.IsEmptySet()) { return Empty(); } auto lo = UMinValue().UMulSat(rhs.UMinValue()); auto up = UMaxValue().UMulSat(rhs.UMaxValue()) + 1; return NonEmpty(lo, up); } ConstantRange ConstantRange::SMulSat(const ConstantRange& rhs) const { if (IsEmptySet() || rhs.IsEmptySet()) { return Empty(); } auto min = SMinValue(); auto max = SMaxValue(); auto rhsMin = rhs.SMinValue(); auto rhsMax = rhs.SMaxValue(); auto products = {min.SMulSat(rhsMin), min.SMulSat(rhsMax), max.SMulSat(rhsMin), max.SMulSat(rhsMax)}; auto cmp = [](const SInt& a, const SInt& b) { return a.Slt(b); }; auto lo = std::min(products, cmp); auto up = std::max(products, cmp) + 1; return NonEmpty(lo, up); } ConstantRange ConstantRange::Abs(bool intMinIsPoison) const { if (IsEmptySet()) { return Empty(); } if (IsSignWrappedSet()) { SInt lo = SInt::UMin(lower, -upper + 1); // Check whether the range crosses zero. if (upper.IsPositive() || !lower.IsPositive()) { lo = SInt::Zero(Width()); } if (intMinIsPoison) { return {lo, SInt::SMinValue(Width())}; } else { return {lo, SInt::SMinValue(Width()) + 1}; } } SInt sMin = SMinValue(); SInt sMax = SMaxValue(); if (intMinIsPoison && sMin.IsSMinValue()) { if (sMax.IsSMinValue()) { return Empty(); } ++sMin; } // All non-negative. if (sMin.IsNonNeg()) { return *this; } // All negative. if (sMax.IsNeg()) { return ConstantRange(-sMax, -sMin + 1); } // Range crosses zero. return {SInt::Zero(Width()), SInt::UMax(-sMin, sMax) + 1}; } ConstantRange ConstantRange::Inverse() const { if (IsFullSet()) { return Empty(); } if (IsEmptySet()) { return Full(); } return ConstantRange{upper, lower}; } ConstantRange ConstantRange::Negate() const { if (IsEmptySet() || IsFullSet()) { return *this; } // e.g. for a set {-5,..,4} // normal case: // 1. [2, -5) = {2, 3, 4} => {-4, -3, -2} = [-4, -1) // 2. [4, -2) = {4, -5, -4, -3} => {3, 4, 5(-5), -4} = [3, -3) // 3. [-4, -1) = {-4, -3, -2} => {2, 3, 4} = [2, -5) auto lo = -(upper - 1); // special case: [-5, 1) = {-5, -4, -3, -2, -1, 0} = {0, 1, 2, 3, 4, 5(-5)} = [0, -4) auto up = lower.IsSMinValue() ? lower : ((-lower) + 1); return ConstantRange{lo, up}; } } // namespace cangjie::CHIRcangjie_compiler-1.0.7/src/CHIR/Analysis/DevirtualizationInfo.cpp000066400000000000000000000132451510705540100247700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the de-virtualization optimization. */ #include "cangjie/CHIR/Analysis/DevirtualizationInfo.h" #include "cangjie/CHIR/CHIRContext.h" #include "cangjie/CHIR/Type/ExtendDef.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/Utils/Casting.h" #include "cangjie/Modules/ModulesUtils.h" namespace Cangjie::CHIR { void DevirtualizationInfo::CollectInfo() { // Function that collect all global function which has more concrete type than // explicit return type in order to infer devirtualization more precise. for (auto func : package->GetGlobalFuncs()) { CollectReturnTypeMap(*func); } // Collecting inheritance relationships between type definitions. for (const auto customTypeDef : package->GetAllCustomTypeDef()) { if (customTypeDef->TestAttr(Attribute::SKIP_ANALYSIS)) { continue; } if (customTypeDef->IsClassLike() && IsClosureConversionEnvClass(*StaticCast(customTypeDef))) { continue; } Type* thisType = customTypeDef->GetType(); if (customTypeDef->IsExtend()) { thisType = StaticCast(customTypeDef)->GetExtendedType(); if (auto customTy = DynamicCast(thisType)) { thisType = customTy->GetCustomTypeDef()->GetType(); } } defsMap[thisType].emplace_back(customTypeDef); for (auto parentTy : customTypeDef->GetSuperTypesInCurDef()) { auto parentDef = parentTy->GetClassDef(); if (IsCoreObject(*parentDef)) { continue; } if (CheckCustomTypeInternal(*parentDef)) { subtypeMap[parentDef].emplace_back(InheritanceInfo{parentTy, thisType}); } } } } const DevirtualizationInfo::SubTypeMap& DevirtualizationInfo::GetSubtypeMap() const { return subtypeMap; } const std::unordered_map& DevirtualizationInfo::GetReturnTypeMap() const { return realRuntimeRetTyMap; } const ConstMemberVarCollector::ConstMemberMapType& DevirtualizationInfo::GetConstMemberMap() const { return constMemberTypeMap; } static Type* GetRuntimeTypeFromFunc(const Value* retVal, bool isInLambda); static Type* GetRuntimeTypeFromLambda(const Apply& apply, Type* type) { if (!apply.GetCallee()->IsLocalVar()) { return type; } auto localVar = StaticCast(apply.GetCallee()); if (localVar->GetExpr()->GetExprKind() != ExprKind::LAMBDA) { return type; } auto lambda = StaticCast(localVar->GetExpr()); return GetRuntimeTypeFromFunc(lambda->GetReturnValue(), true); } static Type* GetRuntimeTypeFromFunc(const Value* retVal, bool isInLambda = false) { if (!retVal) { return nullptr; } auto rtTy = retVal->GetType()->StripAllRefs(); // Do not process the return type that is not class type. if (!rtTy->IsClass()) { return nullptr; } auto users = retVal->GetUsers(); if (users.size() == 1 && users[0]->GetExprKind() == ExprKind::STORE) { auto val = StaticCast(users[0])->GetValue(); if (!val->IsLocalVar()) { return nullptr; } auto expr = StaticCast(val)->GetExpr(); auto srcTy = val->GetType(); if (expr->GetExprKind() == ExprKind::TYPECAST) { auto cast = StaticCast(expr); srcTy = cast->GetSourceTy(); } else if (expr->GetExprKind() == ExprKind::APPLY && !isInLambda) { auto applyResType = GetRuntimeTypeFromLambda(*StaticCast(expr), srcTy); srcTy = applyResType != nullptr ? applyResType : srcTy; } srcTy = srcTy->StripAllRefs(); if (srcTy == rtTy) { // if type is same, skip return nullptr; } // Ensure that the actual return value type is not a reference. return srcTy; } return nullptr; } // Function that collect all global function which has more concrete runtime type // than explicit return type in order to infer devirtualization more precise. void DevirtualizationInfo::CollectReturnTypeMap(Func& func) { auto res = GetRuntimeTypeFromFunc(func.GetReturnValue()); if (res != nullptr) { realRuntimeRetTyMap.emplace(&func, res); } } // after inline, the result type is more accuracy than before, refresh it void DevirtualizationInfo::FreshRetMap() { for (auto func : package->GetGlobalFuncs()) { auto res = GetRuntimeTypeFromFunc(func->GetReturnValue()); if (res != nullptr) { realRuntimeRetTyMap[func] = res; } } } bool DevirtualizationInfo::CheckCustomTypeInternal(const CustomTypeDef& def) const { auto relation = Modules::GetPackageRelation(def.GetPackageName(), package->GetName()); if (def.TestAttr(Attribute::PUBLIC) || def.TestAttr(Attribute::PROTECTED)) { return false; } if (def.TestAttr(Attribute::PRIVATE)) { return true; } if (def.TestAttr(Attribute::INTERNAL)) { if (relation != Modules::PackageRelation::CHILD && relation != Modules::PackageRelation::SAME_PACKAGE) { return true; } if (opts.noSubPkg) { return true; } return false; } return false; } void DevirtualizationInfo::CollectConstMemberVarType() { ConstMemberVarCollector{package, constMemberTypeMap}.CollectConstMemberVarType(); } } // namespace Cangjie::CHIRcangjie_compiler-1.0.7/src/CHIR/Analysis/GetOrThrowResultAnalysis.cpp000066400000000000000000000075431510705540100255720ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Analysis/GetOrThrowResultAnalysis.h" #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Utils.h" using namespace Cangjie::CHIR; GetOrThrowResultDomain::GetOrThrowResultDomain(std::unordered_map* ArgIdxMap) : AbstractDomain(), GetOrThrowResults(std::vector>(ArgIdxMap->size(), FlatSet(false))), ArgIdxMap(ArgIdxMap) { } bool GetOrThrowResultDomain::Join(const GetOrThrowResultDomain& rhs) { this->kind = ReachableKind::REACHABLE; return VectorJoin(GetOrThrowResults, rhs.GetOrThrowResults); } std::string GetOrThrowResultDomain::ToString() const { if (this->kind == ReachableKind::UNREACHABLE) { return "Unreachable"; } else { std::stringstream ss; ss << "{ "; for (auto& result : GetOrThrowResults) { ss << result.ToString() << ", "; } ss << "}"; return ss.str(); } } const Apply* GetOrThrowResultDomain::CheckGetOrThrowResult(const Value* location) const { if (auto it = ArgIdxMap->find(location); it != ArgIdxMap->end()) { return GetOrThrowResults[it->second].GetElem().value_or(nullptr); } else { return nullptr; } } template <> const std::string Analysis::name = "getOrThrow-result"; template <> const std::optional Analysis::blockLimit = std::nullopt; GetOrThrowResultAnalysis::GetOrThrowResultAnalysis(const Func* func, bool isDebug) : Analysis(func, isDebug) { size_t argIdx = 0; for (auto bb : func->GetBody()->GetBlocks()) { for (auto expr : bb->GetExpressions()) { if (IsGetOrThrowFunction(*expr)) { auto apply = StaticCast(expr); CJC_ASSERT(apply->GetArgs().size() > 0); auto arg = apply->GetArgs()[0]; if (auto it = ArgIdxMap.find(arg); it == ArgIdxMap.end()) { ArgIdxMap.emplace(arg, argIdx++); } } } } } GetOrThrowResultDomain GetOrThrowResultAnalysis::Bottom() { return GetOrThrowResultDomain(&ArgIdxMap); } // Set the initial state of the Function entryBB to Top to make sure the // initial state of all the BB in the function is correct. // 1. the entryBB of Function will dominate all the other BB. // 2. the Top state will dominate all the other state. void GetOrThrowResultAnalysis::InitializeFuncEntryState(GetOrThrowResultDomain& state) { state.kind = ReachableKind::REACHABLE; for (auto i = state.GetOrThrowResults.begin(); i != state.GetOrThrowResults.end(); ++i) { i->SetToBound(/* isTop = */ true); } } void GetOrThrowResultAnalysis::PropagateExpressionEffect(GetOrThrowResultDomain& state, const Expression* expression) { if (IsGetOrThrowFunction(*expression)) { auto apply = StaticCast(expression); CJC_ASSERT(apply->GetArgs().size() > 0); auto arg = apply->GetArgs()[0]; if (auto it = ArgIdxMap.find(arg); it != ArgIdxMap.end()) { // Update the result of getOrThrow when the arg of getOrThrow is // first seen in this block; if (state.GetOrThrowResults[it->second].IsBottom() || state.GetOrThrowResults[it->second].IsTop()) { state.GetOrThrowResults[it->second].UpdateElem(apply); } } } } std::optional GetOrThrowResultAnalysis::PropagateTerminatorEffect( GetOrThrowResultDomain& state, const Terminator* terminator) { (void)state; (void)terminator; return std::nullopt; } cangjie_compiler-1.0.7/src/CHIR/Analysis/MaybeInitAnalysis.cpp000066400000000000000000000130561510705540100242040ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Analysis/MaybeInitAnalysis.h" #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/CHIRCasting.h" using namespace Cangjie::CHIR; MaybeInitDomain::MaybeInitDomain(size_t domainSize, const ConstructorInitInfo* ctorInitInfo, std::unordered_map* allocateIdxMap) : GenKillDomain(domainSize), ctorInitInfo(ctorInitInfo), allocateIdxMap(allocateIdxMap) { } std::optional MaybeInitDomain::IsMaybeInitedAllocation(const Value* location) const { if (auto it = allocateIdxMap->find(location); it != allocateIdxMap->end()) { return IsTrueAt(it->second); } else { return std::nullopt; } } MaybeInitDomain::InitedMemberKind MaybeInitDomain::IsMaybeInitedMember(size_t memberIndex) const { CJC_ASSERT(ctorInitInfo); if (memberIndex < ctorInitInfo->superMemberNums) { return IsTrueAt(states.size() - 1) ? InitedMemberKind::SUPER_MEMBER : InitedMemberKind::NA; } else if (memberIndex < ctorInitInfo->superMemberNums + ctorInitInfo->localMemberNums) { return IsTrueAt(memberIndex - ctorInitInfo->superMemberNums) ? InitedMemberKind::LOCAL_MEMBER : InitedMemberKind::NA; } CJC_ABORT(); return InitedMemberKind::NA; } void MaybeInitDomain::SetAllLocalMemberInited() { for (size_t i = 0; i < ctorInitInfo->localMemberNums; ++i) { Gen(i); } } template <> const std::string Analysis::name = "maybe-init"; template <> const std::optional Analysis::blockLimit = std::nullopt; template <> const AnalysisKind GenKillDomain::mustOrMaybe = AnalysisKind::MAYBE; MaybeInitAnalysis::MaybeInitAnalysis(const Func* func, const ConstructorInitInfo* ctorInitInfo) : GenKillAnalysis(func), ctorInitInfo(ctorInitInfo) { size_t allocateIdx = ctorInitInfo->localMemberNums; SaveAllocateMap(*func->GetBody(), allocateIdx, allocateIdxMap); domainSize = ctorInitInfo->localMemberNums + allocateIdx; if (ctorInitInfo->superClassDef) { ++domainSize; } } MaybeInitDomain MaybeInitAnalysis::Bottom() { return MaybeInitDomain(domainSize, ctorInitInfo, &allocateIdxMap); } void MaybeInitAnalysis::InitializeFuncEntryState(MaybeInitDomain& state) { // all the bits have already been set to 0 (uninitialised) state.kind = ReachableKind::REACHABLE; } void MaybeInitAnalysis::PropagateExpressionEffect(MaybeInitDomain& state, const Expression* expression) { if (expression->GetExprKind() == ExprKind::ALLOCATE) { return HandleAllocateExpr(state, StaticCast(expression)); } else if (expression->GetExprKind() == ExprKind::STORE) { return HandleStoreExpr(state, StaticCast(expression)); } else if (expression->GetExprKind() == ExprKind::STORE_ELEMENT_REF) { return HandleStoreElemRefExpr(state, StaticCast(expression)); } else if (expression->GetExprKind() == ExprKind::APPLY) { return HandleApplyExpr(state, StaticCast(expression)); } } std::optional MaybeInitAnalysis::PropagateTerminatorEffect(MaybeInitDomain& state, const Terminator* expression) { (void)state; (void)expression; return std::nullopt; } void MaybeInitAnalysis::HandleAllocateExpr(MaybeInitDomain& state, const Allocate* allocate) { auto res = allocate->GetResult(); if (auto it = allocateIdxMap.find(res); it != allocateIdxMap.end()) { auto allcoateIdx = it->second; state.Kill(allcoateIdx); } } void MaybeInitAnalysis::HandleStoreExpr(MaybeInitDomain& state, const Store* store) { auto location = store->GetLocation(); if (auto it = allocateIdxMap.find(location); it != allocateIdxMap.end()) { auto allocateIdx = it->second; state.Gen(allocateIdx); } } void MaybeInitAnalysis::HandleStoreElemRefExpr(MaybeInitDomain& state, const StoreElementRef* store) const { // modify by cjmp, adapt for var init func create auto parentFunc = store->GetTopLevelFunc(); CJC_NULLPTR_CHECK(parentFunc); auto memberIdxOpt = IsInitialisingMemberVar(*parentFunc, *store); if (!memberIdxOpt.has_value()) { return; } auto memberIdx = memberIdxOpt.value(); CJC_ASSERT(memberIdx < ctorInitInfo->superMemberNums + ctorInitInfo->localMemberNums); if (memberIdx < ctorInitInfo->superMemberNums) { return; } state.Gen(memberIdx - ctorInitInfo->superMemberNums); } void MaybeInitAnalysis::HandleApplyExpr(MaybeInitDomain& state, const Apply* apply) const { if (!func->IsConstructor()) { return; } // Check if it is a call to super init function of this class if (apply->IsSuperCall()) { state.Gen(domainSize - 1); return; } // Check if it is a call to another init function of this class/struct auto callee = apply->GetCallee(); if (callee->IsFuncWithBody()) { auto calleeFunc = VirtualCast(callee); if (calleeFunc->IsConstructor() && calleeFunc->GetOuterDeclaredOrExtendedDef() == ctorInitInfo->thisCustomDef && apply->GetArgs()[0] == func->GetParam(0)) { state.SetAllLocalMemberInited(); if (ctorInitInfo->superClassDef) { state.Gen(domainSize - 1); } } } }cangjie_compiler-1.0.7/src/CHIR/Analysis/MaybeUninitAnalysis.cpp000066400000000000000000000172671510705540100245570ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Analysis/MaybeUninitAnalysis.h" #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/CHIRCasting.h" namespace Cangjie::CHIR { MaybeUninitDomain::MaybeUninitDomain(size_t domainSize, const ConstructorInitInfo* ctorInitInfo, std::unordered_map* allocateIdxMap) : GenKillDomain(domainSize), ctorInitInfo(ctorInitInfo), allocateIdxMap(allocateIdxMap), maybeInitedPos(std::vector>(domainSize)) { } bool MaybeUninitDomain::Join(const MaybeUninitDomain& rhs) { for (size_t i = 0; i < maybeInitedPos.size(); ++i) { maybeInitedPos[i].insert(rhs.maybeInitedPos[i].cbegin(), rhs.maybeInitedPos[i].cend()); } return GenKillDomain::Join(rhs); } std::optional MaybeUninitDomain::IsMaybeUninitedAllocation(const Value* location) const { if (auto it = allocateIdxMap->find(location); it != allocateIdxMap->end()) { return IsTrueAt(it->second); } else { return std::nullopt; } } const std::set& MaybeUninitDomain::GetMaybeInitedPos(const Value* location) const { return maybeInitedPos[allocateIdxMap->at(location)]; } MaybeUninitDomain::UninitedMemberKind MaybeUninitDomain::IsMaybeUninitedMember(size_t memberIndex) const { CJC_ASSERT(ctorInitInfo); if (memberIndex < ctorInitInfo->superMemberNums) { return IsTrueAt(states.size() - 1) ? UninitedMemberKind::SUPER_MEMBER : UninitedMemberKind::NA; } else if (memberIndex < ctorInitInfo->superMemberNums + ctorInitInfo->localMemberNums) { return IsTrueAt(memberIndex - ctorInitInfo->superMemberNums) ? UninitedMemberKind::LOCAL_MEMBER : UninitedMemberKind::NA; } CJC_ABORT(); return UninitedMemberKind::NA; } const std::set& MaybeUninitDomain::GetMaybeInitedPos(size_t memberIndex) const { CJC_ASSERT(memberIndex >= ctorInitInfo->superMemberNums); return maybeInitedPos[memberIndex - ctorInitInfo->superMemberNums]; } std::vector MaybeUninitDomain::GetMaybeUninitedLocalMembers() const { CJC_ASSERT(ctorInitInfo); std::vector uninitedLocalMembers; for (size_t i = 0; i < ctorInitInfo->localMemberNums; ++i) { if (IsTrueAt(i)) { uninitedLocalMembers.emplace_back(i + ctorInitInfo->superMemberNums); } } return uninitedLocalMembers; } void MaybeUninitDomain::SetAllLocalMemberInited() { for (size_t i = 0; i < ctorInitInfo->localMemberNums; ++i) { Kill(i); } } template <> const std::string Analysis::name = "maybe-uninit"; template <> const std::optional Analysis::blockLimit = std::nullopt; template <> const AnalysisKind GenKillDomain::mustOrMaybe = AnalysisKind::MAYBE; void SaveAllocateMap( const BlockGroup& body, size_t& allocateIdx, std::unordered_map& allocateIdxMap) { for (auto bb : body.GetBlocks()) { for (auto expr : bb->GetExpressions()) { auto kind = expr->GetExprKind(); if ((kind == ExprKind::ALLOCATE || kind == ExprKind::ALLOCATE_WITH_EXCEPTION) && expr->GetResult()->GetDebugExpr()) { allocateIdxMap.emplace(expr->GetResult(), allocateIdx++); } if (kind == ExprKind::LAMBDA) { SaveAllocateMap(*StaticCast(expr)->GetBody(), allocateIdx, allocateIdxMap); } } } } MaybeUninitAnalysis::MaybeUninitAnalysis(const Func* func, const ConstructorInitInfo* ctorInitInfo) : GenKillAnalysis(func), ctorInitInfo(ctorInitInfo) { size_t allocateIdx = ctorInitInfo->localMemberNums; SaveAllocateMap(*func->GetBody(), allocateIdx, allocateIdxMap); domainSize = ctorInitInfo->localMemberNums + allocateIdx; if (ctorInitInfo->superClassDef) { ++domainSize; } } MaybeUninitDomain MaybeUninitAnalysis::Bottom() { return MaybeUninitDomain(domainSize, ctorInitInfo, &allocateIdxMap); } void MaybeUninitAnalysis::InitializeFuncEntryState(MaybeUninitDomain& state) { // set all bits to 1 (uninitialised) state.kind = ReachableKind::REACHABLE; state.GenAll(); } void MaybeUninitAnalysis::PropagateExpressionEffect(MaybeUninitDomain& state, const Expression* expression) { if (expression->GetExprKind() == ExprKind::ALLOCATE) { return HandleAllocateExpr(state, StaticCast(expression)); } else if (expression->GetExprKind() == ExprKind::STORE) { return HandleStoreExpr(state, StaticCast(expression)); } else if (expression->GetExprKind() == ExprKind::STORE_ELEMENT_REF) { return HandleStoreElemRefExpr(state, StaticCast(expression)); } else if (expression->GetExprKind() == ExprKind::APPLY) { return HandleApplyExpr(state, StaticCast(expression)); } } std::optional MaybeUninitAnalysis::PropagateTerminatorEffect( MaybeUninitDomain& state, const Terminator* expression) { (void)state; (void)expression; return std::nullopt; } void MaybeUninitAnalysis::HandleAllocateExpr(MaybeUninitDomain& state, const Allocate* allocate) { auto res = allocate->GetResult(); if (auto it = allocateIdxMap.find(res); it != allocateIdxMap.end()) { auto allcoateIdx = it->second; state.Gen(allcoateIdx); } } void MaybeUninitAnalysis::HandleStoreExpr(MaybeUninitDomain& state, const Store* store) { auto location = store->GetLocation(); if (auto it = allocateIdxMap.find(location); it != allocateIdxMap.end()) { auto allcoateIdx = it->second; state.Kill(allcoateIdx); auto debugLoc = store->GetDebugLocation(); state.maybeInitedPos[allcoateIdx].emplace(debugLoc.GetBeginPos().line); } } void MaybeUninitAnalysis::HandleStoreElemRefExpr(MaybeUninitDomain& state, const StoreElementRef* store) const { // modify by cjmp, adapt for var init func create auto parentFunc = store->GetTopLevelFunc(); CJC_NULLPTR_CHECK(parentFunc); auto memberIdxOpt = IsInitialisingMemberVar(*parentFunc, *store); if (!memberIdxOpt.has_value()) { return; } auto memberIdx = memberIdxOpt.value(); CJC_ASSERT(memberIdx < ctorInitInfo->superMemberNums + ctorInitInfo->localMemberNums); if (memberIdx < ctorInitInfo->superMemberNums) { return; } auto memberStateIdx = memberIdx - ctorInitInfo->superMemberNums; state.Kill(memberStateIdx); auto& debugLoc = store->GetDebugLocation(); state.maybeInitedPos[memberStateIdx].emplace(debugLoc.GetBeginPos().line); } void MaybeUninitAnalysis::HandleApplyExpr(MaybeUninitDomain& state, const Apply* apply) const { if (!func->IsConstructor()) { return; } // Check if it is a call to super init function of this class if (apply->IsSuperCall()) { state.Kill(domainSize - 1); return; } // Check if it is a call to another init function of this class/struct auto callee = apply->GetCallee(); if (callee->IsFuncWithBody()) { auto calleeFunc = VirtualCast(callee); if (calleeFunc->IsConstructor() && calleeFunc->GetOuterDeclaredOrExtendedDef() == ctorInitInfo->thisCustomDef && apply->GetArgs()[0] == func->GetParam(0)) { state.SetAllLocalMemberInited(); if (ctorInitInfo->superClassDef) { state.Kill(domainSize - 1); } } } } } // namespace Cangjie::CHIRcangjie_compiler-1.0.7/src/CHIR/Analysis/ReachingDefinitionAnalysis.cpp000066400000000000000000000216351510705540100260560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Analysis/ReachingDefinitionAnalysis.h" #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Visitor/Visitor.h" using namespace Cangjie; using namespace Cangjie::CHIR; ReachingDefinitionDomain::ReachingDefinitionDomain(std::unordered_map* allocateIdxMap) : AbstractDomain(), reachingDefs(std::vector>(allocateIdxMap->size(), FlatSet(false))), reachingLoadDefs(std::vector>(allocateIdxMap->size(), FlatSet(false))), allocateIdxMap(allocateIdxMap) { } bool ReachingDefinitionDomain::Join(const ReachingDefinitionDomain& rhs) { this->kind = ReachableKind::REACHABLE; // should not use ||, cannot be short-circuited auto res1 = VectorJoin(reachingDefs, rhs.reachingDefs); auto res2 = VectorJoin(reachingLoadDefs, rhs.reachingLoadDefs); return res1 || res2; } std::string ReachingDefinitionDomain::ToString() const { if (this->kind == ReachableKind::UNREACHABLE) { return "Unreachable"; } else { std::stringstream ss; ss << "{ "; for (auto& def : reachingDefs) { ss << def.ToString() << ", "; } ss << "} "; ss << " { "; for (auto& def : reachingLoadDefs) { ss << def.ToString() << ", "; } ss << "}"; return ss.str(); } } const Store* ReachingDefinitionDomain::CheckReachingDef(const Value* location) const { if (auto it = allocateIdxMap->find(location); it != allocateIdxMap->end()) { return reachingDefs[it->second].GetElem().value_or(nullptr); } else { return nullptr; } } const Load* ReachingDefinitionDomain::CheckReachingLoadDef(const Value* location) const { if (auto it = allocateIdxMap->find(location); it != allocateIdxMap->end()) { return reachingLoadDefs[it->second].GetElem().value_or(nullptr); } else { return nullptr; } } template <> const std::string Analysis::name = "reaching-definition"; template <> const std::optional Analysis::blockLimit = std::nullopt; ReachingDefinitionAnalysis::ReachingDefinitionAnalysis(const Func* func) : Analysis(func) { size_t allocateIdx = 0; size_t worklistIdx = 0; auto worklist = func->GetBody()->GetBlocks(); while (worklistIdx != worklist.size()) { auto bb = worklist[worklistIdx]; for (auto expr : bb->GetExpressions()) { Type* allocatedTy = nullptr; auto kind = expr->GetExprKind(); if (kind == ExprKind::ALLOCATE) { allocatedTy = StaticCast(expr)->GetType(); } else if (kind == ExprKind::ALLOCATE_WITH_EXCEPTION) { allocatedTy = StaticCast(expr)->GetType(); } else if (kind == ExprKind::LAMBDA) { auto blocks = StaticCast(expr)->GetBody()->GetBlocks(); worklist.insert(worklist.end(), blocks.begin(), blocks.end()); } const auto canBeTracked = [](const Type* allocatedTy) { if (allocatedTy->IsClass()) { // It's useless to track an allocation like Allocate(Class-CA). // We will never directly store to a Class-CA&. return false; } if (allocatedTy->IsVArray()) { // A VArray can be passed into a CFunc. return false; } if (allocatedTy->IsStruct()) { auto structDef = StaticCast(allocatedTy)->GetStructDef(); if (structDef->IsCStruct()) { return false; } } if (allocatedTy->IsGeneric()) { return false; } return true; }; if (allocatedTy && canBeTracked(allocatedTy)) { allocateIdxMap.emplace(expr->GetResult(), allocateIdx++); } } ++worklistIdx; } } ReachingDefinitionDomain ReachingDefinitionAnalysis::Bottom() { return ReachingDefinitionDomain(&allocateIdxMap); } void ReachingDefinitionAnalysis::InitializeFuncEntryState(ReachingDefinitionDomain& state) { state.kind = ReachableKind::REACHABLE; for (size_t i = 0; i < state.allocateIdxMap->size(); ++i) { state.reachingDefs[i].SetToBound(/* isTop = */ true); state.reachingLoadDefs[i].SetToBound(/* isTop = */ true); } } void ReachingDefinitionAnalysis::HandleVarStateCapturedByLambda(ReachingDefinitionDomain& state, const Lambda* lambda) { for (auto var : GetLambdaCapturedVarsRecursively(*lambda)) { if (auto it = state.allocateIdxMap->find(var); it != state.allocateIdxMap->end()) { state.reachingDefs[it->second].SetToBound(/* isTop = */ true); state.reachingLoadDefs[it->second].SetToBound(/* isTop = */ true); } } } void ReachingDefinitionAnalysis::PropagateExpressionEffect( ReachingDefinitionDomain& state, const Expression* expression) { switch (expression->GetExprKind()) { case ExprKind::STORE: return HandleStoreExpr(state, StaticCast(expression)); case ExprKind::GET_ELEMENT_REF: return HandleGetElemRefExpr(state, StaticCast(expression)); case ExprKind::STORE_ELEMENT_REF: return HanldeStoreElemRefExpr(state, *StaticCast(expression)); case ExprKind::APPLY: return HandleApplyExpr(state, StaticCast(expression)); case ExprKind::INTRINSIC: return HandleIntrinsicExpr(state, StaticCast(expression)); case ExprKind::LOAD: return HandleLoadExpr(state, StaticCast(expression)); default: return; } } std::optional ReachingDefinitionAnalysis::PropagateTerminatorEffect( ReachingDefinitionDomain& state, const Terminator* terminator) { if (terminator->GetExprKind() == ExprKind::APPLY_WITH_EXCEPTION) { HandleApplyExpr(state, StaticCast(terminator)); } else if (terminator->GetExprKind() == ExprKind::INTRINSIC_WITH_EXCEPTION) { HandleIntrinsicExpr(state, StaticCast(terminator)); } return std::nullopt; } void ReachingDefinitionAnalysis::HandleStoreExpr(ReachingDefinitionDomain& state, const Store* store) { // If it's a store to a tracked allocation. auto target = store->GetLocation(); if (auto it = allocateIdxMap.find(target); it != allocateIdxMap.end()) { state.reachingDefs[it->second].UpdateElem(store); state.reachingLoadDefs[it->second].SetToBound(/* isTop = */ true); } } void ReachingDefinitionAnalysis::HandleGetElemRefExpr(ReachingDefinitionDomain& state, const GetElementRef* getElemRef) { // If it's a non-readonly GetElementRef to a tracked struct. auto target = getElemRef->GetLocation(); if (auto it = allocateIdxMap.find(target); it != allocateIdxMap.end()) { if (StaticCast(target->GetType())->GetBaseType()->IsStruct()) { if (!getElemRef->GetResult()->TestAttr(Attribute::READONLY)) { state.reachingDefs[it->second].SetToBound(/* isTop = */ true); state.reachingLoadDefs[it->second].SetToBound(/* isTop = */ true); } } } } void ReachingDefinitionAnalysis::HanldeStoreElemRefExpr( ReachingDefinitionDomain& state, const StoreElementRef& storeElemRef) { auto target = storeElemRef.GetLocation(); if (auto it = allocateIdxMap.find(target); it != allocateIdxMap.end()) { if (StaticCast(target->GetType())->GetBaseType()->IsStruct()) { state.reachingDefs[it->second].SetToBound(/* isTop = */ true); state.reachingLoadDefs[it->second].SetToBound(/* isTop = */ true); } } } void ReachingDefinitionAnalysis::HandleLoadExpr(ReachingDefinitionDomain& state, const Load* load) { // If it's a load to a tracked allocation. auto target = load->GetLocation(); if (auto it = allocateIdxMap.find(target); it != allocateIdxMap.end()) { // If there is a Top, it means that the current Load we are visiting is the first Load operation after the // value stored in an allocation has changed. If there is already a Load expression, it means that the // current Load we are visiting is redundant. if (state.reachingLoadDefs[it->second].IsTop()) { state.reachingLoadDefs[it->second].UpdateElem(load); } } }cangjie_compiler-1.0.7/src/CHIR/Analysis/SInt.cpp000066400000000000000000000561001510705540100214710ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Analysis/SInt.h" #include #include "cangjie/CHIR/Analysis/Arithmetic.h" namespace Cangjie::CHIR { IntWidth ToWidth(const Type& ty) { switch (ty.GetTypeKind()) { case Type::TypeKind::TYPE_INT8: case Type::TypeKind::TYPE_UINT8: return IntWidth::I8; case Type::TypeKind::TYPE_INT16: case Type::TypeKind::TYPE_UINT16: return IntWidth::I16; case Type::TypeKind::TYPE_INT32: case Type::TypeKind::TYPE_UINT32: return IntWidth::I32; default: return IntWidth::I64; } } static unsigned GetDigit(char d, Radix radix) { unsigned ans; constexpr unsigned radixDiff = 11u; constexpr unsigned raidxDiffAddition = static_cast(Radix::R10); if (radix == Radix::R16) { ans = static_cast(d) - static_cast('0'); if (ans < Radix::R10) { return ans; } ans = static_cast(d) - static_cast('A'); if (ans <= radix - radixDiff) { return ans + raidxDiffAddition; } ans = static_cast(d) - static_cast('a'); if (ans <= radix - radixDiff) { return ans + raidxDiffAddition; } radix = Radix::R10; } ans = static_cast(d) - static_cast('0'); if (ans < static_cast(radix)) { return ans; } return UINT_MAX; } SInt::SInt(IntWidth width, WordType val) : width{width}, val{val} { ClearUnusedBits(); } SInt::SInt(const SInt& other) : width{other.width}, val{other.val} { } SInt::SInt(SInt&& other) : width{other.width}, val{other.val} { } SInt::SInt(unsigned val) : SInt{FromUnsigned(), val} { } SInt::SInt(unsigned long val) : SInt{FromUnsigned(), val} { } SInt::SInt(unsigned long long val) : SInt{FromUnsigned(), val} { } SInt::SInt(IntWidth width, const std::string& str, Radix radix) : width(width) { FromString(str, radix); } SInt::WordType SInt::UVal() const { return val; } int64_t SInt::SVal() const { return SignExtend64(val, width); } IntWidth SInt::Width() const { return width; } SInt SInt::Zero(IntWidth w) { return SInt(w, 0); } SInt SInt::UMaxValue(IntWidth w) { return AllOnes(w); } SInt SInt::SMaxValue(IntWidth w) { SInt ans = AllOnes(w); CJC_ASSERT(w > 0); ans.ClearBit(w - 1); return ans; } SInt SInt::UMinValue(IntWidth w) { return Zero(w); } SInt SInt::SMinValue(IntWidth w) { SInt ans = Zero(w); CJC_ASSERT(w > 0); ans.SetBit(w - 1); return ans; } SInt SInt::AllOnes(IntWidth w) { return {w, WORD_TYPE_MAX}; } SInt SInt::BitMask(IntWidth w, unsigned no) { SInt ans = Zero(w); ans.SetBit(no); return ans; } SInt SInt::GetHighBitsSet(IntWidth w, unsigned highBits) { SInt res{w, 0}; res.SetHighBits(highBits); return res; } SInt SInt::GetLowBitsSet(IntWidth w, unsigned lowBits) { SInt res{w, 0}; res.SetLowBits(lowBits); return res; } SInt SInt::GetOneBitSet(IntWidth w, unsigned pos) { SInt ans{w, 0}; ans.SetBit(pos); return ans; } bool SInt::IsNeg() const { return IsSignBitSet(); } bool SInt::IsNonNeg() const { return IsSignBitClear(); } bool SInt::IsPositive() const { return IsNonNeg() && !IsZero(); } // Determine if this SInt is non negative bool SInt::IsSignBitSet() const { CJC_ASSERT(width > 0); return At(width - 1); } bool SInt::IsSignBitClear() const { return !IsSignBitSet(); } // Determine if this SInt has only the specified bit set. bool SInt::IsOneBitSet(unsigned no) const { return At(no) && Popcnt() == 1; } // Determine if all bits are set bool SInt::IsAllOnes() const { CJC_ASSERT(width > 0 && width <= I64); return val == (WORD_TYPE_MAX >> (BITS_PER_WORD - width)); } bool SInt::IsZero() const { return val == 0; } bool SInt::IsOne() const { return val == 1; } bool SInt::IsUMaxValue() const { return IsAllOnes(); } bool SInt::IsSMaxValue() const { CJC_ASSERT(width > 0 && width <= I64); return val == ((static_cast(1) << (width - 1)) - 1); } bool SInt::IsUMinValue() const { return IsZero(); } bool SInt::IsSMinValue() const { CJC_ASSERT(width > 0 && width <= I64); return val == (static_cast(1) << (width - 1)); } // Check if this SInt has an \p n-bits unsigned value bool SInt::IsUIntN(unsigned n) const { return ActiveBits() <= n; } // Check if this SInt has an \p n-bits signed value bool SInt::IsSIntN(unsigned n) const { return SignificantBits() <= n; } bool SInt::IsPowerOf2() const { return ::Cangjie::CHIR::IsPowerOf2(val); } bool SInt::IsNegatedPowerOf2() const { if (IsNonNeg()) { return false; } return Clo() + Ctz() == width; } // Check if this SInt is returned by SignMask bool SInt::IsSignMask() const { return IsSMinValue(); } bool SInt::ToBool() const { return !IsZero(); } // Returns the value of this SInt saturate to \p maxv uint64_t SInt::GetULimitedValue(uint64_t maxv) const { return Ugt(maxv) ? maxv : GetZExtValue(); } bool SInt::IsMask(unsigned bits) const { return val == (WORD_TYPE_MAX >> (BITS_PER_WORD - bits)); } /// format SInt to beauty format. SInt::Formatter SInt::ToString(bool asUnsigned, Radix radix) const { return {{asUnsigned, radix}, *this}; } /// Region: unary operators SInt SInt::operator++(int) { SInt ans{*this}; ++*this; return ans; } SInt& SInt::operator++() { ++val; return ClearUnusedBits(); } SInt SInt::operator--(int) { SInt ans{*this}; --*this; return ans; } SInt& SInt::operator--() { --val; return ClearUnusedBits(); } bool SInt::operator!() const { return IsZero(); } SInt& SInt::operator=(const SInt& other) { if (this != &other) { val = other.val; width = other.width; } return *this; } SInt& SInt::operator=(SInt&& other) { val = other.val; width = other.width; return *this; } SInt& SInt::operator=(uint64_t v) { val = v; return ClearUnusedBits(); } // compound assignment operators SInt& SInt::operator&=(const SInt& other) { return *this &= other.val; } SInt& SInt::operator&=(uint64_t v) { val &= v; return *this; } SInt& SInt::operator|=(const SInt& other) { return *this |= other.val; } SInt& SInt::operator|=(uint64_t v) { val |= v; return *this; } SInt& SInt::operator^=(const SInt& other) { return *this ^= other.val; } SInt& SInt::operator^=(uint64_t v) { val ^= v; return *this; } SInt& SInt::operator*=(const SInt& other) { return *this = *this * other; } SInt& SInt::operator*=(uint64_t v) { val *= v; return ClearUnusedBits(); } SInt SInt::operator*(const SInt& other) const { CJC_ASSERT(width == other.width); return {width, val * other.val}; } SInt& SInt::operator+=(const SInt& other) { CJC_ASSERT(width == other.width); val += other.val; return ClearUnusedBits(); } SInt& SInt::operator+=(uint64_t v) { val += v; return ClearUnusedBits(); } SInt& SInt::operator-=(const SInt& other) { CJC_ASSERT(width == other.width); val -= other.val; return ClearUnusedBits(); } SInt& SInt::operator-=(uint64_t v) { val -= v; return ClearUnusedBits(); } SInt& SInt::operator<<=(unsigned count) { CJC_ASSERT(count <= width); if (count == width) { val = 0; } else { val <<= count; } return ClearUnusedBits(); } SInt& SInt::operator<<=(const SInt& count) { *this <<= static_cast(count.GetULimitedValue(width)); return *this; } SInt SInt::operator<<(const SInt& count) const { return Shl(count); } SInt SInt::operator<<(unsigned count) const { return Shl(count); } SInt SInt::AShr(unsigned count) const { SInt ans{*this}; ans.AShrInPlace(count); return ans; } void SInt::AShrInPlace(unsigned count) { CJC_ASSERT(count <= width); int64_t sextVal = SignExtend64(val, width); if (count == width) { val = static_cast(sextVal >> (BITS_PER_WORD - 1)); // fill with sign bit } else { val = static_cast(sextVal >> count); } ClearUnusedBits(); } SInt SInt::LShr(unsigned count) const { SInt ans{*this}; ans.LShrInPlace(count); return ans; } void SInt::LShrInPlace(unsigned count) { CJC_ASSERT(count <= width); val = count == width ? 0 : val >> count; } SInt SInt::Ashr(const SInt& count) const { SInt ans{*this}; ans.AShrInPlace(static_cast(count.val)); return ans; } SInt SInt::LShr(const SInt& count) const { SInt ans{*this}; ans.LShrInPlace(count); return ans; } void SInt::LShrInPlace(const SInt& count) { return LShrInPlace(static_cast(count.GetULimitedValue(width))); } SInt SInt::Shl(unsigned count) const { SInt ans{*this}; ans <<= count; return ans; } SInt SInt::Shl(const SInt& count) const { SInt ans{*this}; ans <<= count; return ans; } SInt SInt::Concat(const SInt& v) const { unsigned nw = width + v.width; switch (nw) { case IntWidth::I8: case IntWidth::I16: case IntWidth::I32: case IntWidth::I64: break; default: CJC_ABORT(); break; } return {FromUnsigned(nw), (val << v.width) | v.val}; } bool SInt::At(unsigned bit) const { CJC_ASSERT(bit < width); return this->operator[](bit); } bool SInt::operator[](unsigned bit) const { return (MaskBit(bit) & val) != 0; } bool SInt::operator==(const SInt& other) const { CJC_ASSERT(width == other.width); return val == other.val; } bool SInt::operator==(uint64_t rhs) const { return GetZExtValue() == rhs; } bool SInt::operator!=(const SInt& rhs) const { return !(*this == rhs); } bool SInt::operator!=(uint64_t rhs) const { return GetZExtValue() != rhs; } bool SInt::Ult(const SInt& rhs) const { return UCmp(rhs) < 0; } bool SInt::Ult(uint64_t rhs) const { return GetZExtValue() < rhs; } bool SInt::Slt(const SInt& rhs) const { return SCmp(rhs) < 0; } bool SInt::Slt(int64_t rhs) const { return GetSExtValue() < rhs; } bool SInt::Ule(const SInt& rhs) const { return UCmp(rhs) <= 0; } bool SInt::Ule(uint64_t rhs) const { return !Ugt(rhs); } bool SInt::Sle(const SInt& rhs) const { return SCmp(rhs) <= 0; } bool SInt::Sle(int64_t rhs) const { return !Sgt(rhs); } bool SInt::Ugt(const SInt& rhs) const { return !Ule(rhs); } bool SInt::Ugt(uint64_t rhs) const { return GetZExtValue() > rhs; } bool SInt::Sgt(const SInt& rhs) const { return !Sle(rhs); } bool SInt::Sgt(int64_t rhs) const { return GetSExtValue() > rhs; } bool SInt::Uge(const SInt& rhs) const { return !Ult(rhs); } bool SInt::Uge(uint64_t rhs) const { return !Ult(rhs); } bool SInt::Sge(const SInt& rhs) const { return !Slt(rhs); } bool SInt::Sge(int64_t rhs) const { return !Slt(rhs); } SInt SInt::UMin(const SInt& lhs, const SInt& rhs) { return lhs.Ule(rhs) ? lhs : rhs; } SInt SInt::SMin(const SInt& lhs, const SInt& rhs) { return lhs.Sle(rhs) ? lhs : rhs; } SInt SInt::UMax(const SInt& lhs, const SInt& rhs) { return lhs.Ule(rhs) ? rhs : lhs; } SInt SInt::SMax(const SInt& lhs, const SInt& rhs) { return lhs.Sle(rhs) ? rhs : lhs; } bool SInt::Intersects(const SInt& other) const { CJC_ASSERT(width == other.width); return (val & other.val) != 0; } bool SInt::IsSubsetOf(const SInt& other) const { CJC_ASSERT(width == other.width); return (val & ~other.val) != 0; } void SInt::SetAllBits() { val = WORD_TYPE_MAX; ClearUnusedBits(); } // Set the selected bit \p pos to one void SInt::SetBit(unsigned pos) { CJC_ASSERT(pos < width); val |= MaskBit(pos); } // Set from \p lo to \p hi bits to one void SInt::SetBits(unsigned lo, unsigned hi) { CJC_ASSERT(lo <= hi); CJC_ASSERT(hi <= width); if (lo == hi) { return; } WordType mask = WORD_TYPE_MAX >> (BITS_PER_WORD - hi + lo); mask <<= lo; val |= mask; } void SInt::SetLowBits(unsigned lo) { SetBits(0, lo); } void SInt::SetHighBits(unsigned hi) { SetBits(width - hi, width); } void SInt::ClearBit(unsigned pos) { CJC_ASSERT(pos < width); val &= ~MaskBit(pos); } void SInt::FlipAllBits() { val ^= WORD_TYPE_MAX; ClearUnusedBits(); } // Negate this value in place void SInt::Neg() { FlipAllBits(); ++*this; } unsigned SInt::ActiveBits() const { return width - Clz(); } unsigned SInt::SignificantBits() const { return width - GetNumSignBits() + 1; } uint64_t SInt::GetZExtValue() const { return val; } int64_t SInt::GetSExtValue() const { return SignExtend64(val, width); } // Count leading zeroes unsigned SInt::Clz() const { return static_cast(::Cangjie::CHIR::Clz(val)) - (BITS_PER_WORD - width); } // Count leading ones unsigned SInt::Clo() const { CJC_ASSERT(width > 0); return static_cast(::Cangjie::CHIR::Clo(val << (BITS_PER_WORD - width))); } // Get the number of leading bits of this SInt that are equal to its sign bit unsigned SInt::GetNumSignBits() const { return IsNeg() ? Clo() : Clz(); } // Count trailing zeroes unsigned SInt::Ctz() const { return std::min(static_cast(::Cangjie::CHIR::Ctz(val)), static_cast(width)); } // Count trailing ones unsigned SInt::Cto() const { return static_cast(::Cangjie::CHIR::Cto(val)); } // Count population unsigned SInt::Popcnt() const { return ::Cangjie::CHIR::Popcnt(val); } SInt SInt::GetBitsSetFrom(IntWidth w, unsigned lo) { SInt res{w, 0u}; res.SetBits(lo, w); return res; } uint64_t SInt::MaskBit(unsigned pos) { return 1ULL << pos; } SInt& SInt::ClearUnusedBits() { CJC_ASSERT(width > 0); unsigned wordBits = (width - 1) % BITS_PER_WORD + 1; uint64_t mask = WORD_TYPE_MAX >> (BITS_PER_WORD - wordBits); val &= mask; return *this; } void SInt::FromString(const std::string& str, Radix radix) { size_t slen = str.size(); auto p = str.begin(); bool isNeg = *p == '-'; if (*p == '-' || *p == '+') { ++p; --slen; CJC_ASSERT(slen != 0); } val = 0; unsigned shift = radix == Radix::R16 ? 4 : radix == Radix::R2 ? 1 : 0; for (auto e = str.end(); p != e; ++p) { unsigned digit = GetDigit(*p, radix); CJC_ASSERT(digit < static_cast(radix)); if (slen > 1) { if (shift != 0) { val <<= shift; } else { val *= radix; } } val += digit; } if (isNeg) { Neg(); } } decltype(std::setbase(0)) SIntFormatterBase::GetBaseManip() const { if (radix == Radix::R2) { CJC_ABORT(); } return std::setbase(static_cast(radix)); } std::ostream& operator<<(std::ostream& out, const SInt::Formatter& fmt) { out << fmt.GetBaseManip(); if ((out.flags() & std::ios_base::basefield) == std::ios_base::hex) { out << std::showbase; } if (fmt.asUnsigned) { return out << fmt.value.UVal(); } return out << fmt.value.SVal(); } SInt SInt::UDiv(const SInt& rhs) const { CJC_ASSERT(width == rhs.width); return UDiv(rhs.val); } SInt SInt::UDiv(uint64_t rhs) const { CJC_ASSERT(rhs != 0); return {width, val / rhs}; } SInt SInt::SDiv(const SInt& rhs) const { CJC_ASSERT(width == rhs.width); if (IsNeg()) { if (rhs.IsNeg()) { return (-*this).UDiv(-rhs); } return -(-*this).UDiv(rhs); } if (rhs.IsNeg()) { return -UDiv(-rhs); } return UDiv(rhs); } SInt SInt::SDiv(int64_t rhs) const { CJC_ASSERT(rhs != 0); if (IsNeg()) { if (rhs < 0) { return (-*this).UDiv(static_cast(-rhs)); } return -(-*this).UDiv(static_cast(rhs)); } if (rhs < 0) { return -UDiv(static_cast(-rhs)); } return UDiv(static_cast(rhs)); } SInt SInt::URem(const SInt& rhs) const { CJC_ASSERT(width == rhs.width); return URem(rhs.val); } SInt SInt::URem(uint64_t rhs) const { CJC_ASSERT(rhs != 0); return {width, val % rhs}; } SInt SInt::SRem(const SInt& rhs) const { CJC_ASSERT(width == rhs.width); if (IsNeg()) { if (rhs.IsNeg()) { return -(-*this).URem(-rhs); } return -(-*this).URem(rhs); } if (rhs.IsNeg()) { return URem(-rhs); } return URem(rhs); } SInt SInt::SRem(int64_t rhs) const { CJC_ASSERT(rhs != 0); if (IsNeg()) { if (rhs < 0) { return -(-*this).URem(static_cast(-rhs)); } return -(-*this).URem(static_cast(rhs)); } if (rhs < 0) { return URem(static_cast(-rhs)); } return URem(static_cast(rhs)); } int SInt::UCmp(const SInt& rhs) const { CJC_ASSERT(width == rhs.width); return val < rhs.val ? -1 : val > rhs.val; } int SInt::SCmp(const SInt& rhs) const { CJC_ASSERT(width == rhs.width); int64_t lext = GetSExtValue(); int64_t rext = rhs.GetSExtValue(); return lext < rext ? -1 : lext > rext; } SInt SInt::Trunc(IntWidth w) const { CJC_ASSERT(w <= this->width); return {w, val}; } SInt SInt::TruncUSat(IntWidth w) const { CJC_ASSERT(w <= this->width); if (IsUIntN(w)) { return Trunc(w); } return UMaxValue(w); } SInt SInt::TruncSSat(IntWidth w) const { if (IsSIntN(w)) { return Trunc(w); } return IsNeg() ? SMinValue(w) : SMaxValue(w); } SInt SInt::ZExt(IntWidth w) const { CJC_ASSERT(this->width <= w); return {w, val}; } SInt SInt::SExt(IntWidth w) const { CJC_ASSERT(this->width <= w); return {w, static_cast(SignExtend64(val, static_cast(this->width)))}; } SInt SInt::SAddOvf(const SInt& rhs, bool& overflow) const { SInt res{*this + rhs}; overflow = IsNonNeg() == rhs.IsNonNeg() && res.IsNonNeg() != IsNonNeg(); return res; } SInt SInt::UAddOvf(const SInt& rhs, bool& overflow) const { SInt res{*this + rhs}; overflow = res.Ult(rhs); return res; } SInt SInt::SSubOvf(const SInt& rhs, bool& overflow) const { SInt res{*this - rhs}; overflow = IsNonNeg() != rhs.IsNonNeg() && res.IsNonNeg() != IsNonNeg(); return res; } SInt SInt::USubOvf(const SInt& rhs, bool& overflow) const { SInt res{*this - rhs}; overflow = res.Ugt(*this); return res; } SInt SInt::SMulOvf(const SInt& rhs, bool& overflow) const { SInt res{*this * rhs}; if (rhs != 0) { overflow = res.SDiv(rhs) != *this || (IsSMinValue() && rhs.IsAllOnes()); } else { overflow = false; } return res; } SInt SInt::UMulOvf(const SInt& rhs, bool& overflow) const { constexpr unsigned int k = 2; if (Clz() + rhs.Clz() + k <= width) { overflow = true; return *this * rhs; } SInt res = LShr(1) * rhs; overflow = res.IsNeg(); res <<= 1; if ((*this)[0]) { res += rhs; if (res.Ult(rhs)) { overflow = true; } } return res; } SInt SInt::SShlOvf(const SInt& count, bool& overflow) const { overflow = count.Uge(width); if (overflow) { return {width, 0}; } if (IsNonNeg()) { overflow = count.Uge(Clz()); } else { overflow = count.Uge(Clo()); } return *this << count; } SInt SInt::UShlOvf(const SInt& count, bool& overflow) const { overflow = count.Uge(width); if (overflow) { return {width, 0}; } overflow = count.Ugt(Clz()); return *this << count; } SInt SInt::SAddSat(const SInt& rhs) const { bool overflow; SInt res = SAddOvf(rhs, overflow); if (!overflow) { return res; } return IsNeg() ? SMinValue(width) : SMaxValue(width); } SInt SInt::UAddSat(const SInt& rhs) const { bool overflow; SInt res = UAddOvf(rhs, overflow); if (!overflow) { return res; } return UMaxValue(width); } SInt SInt::SSubSat(const SInt& rhs) const { bool overflow; SInt res = SSubOvf(rhs, overflow); if (!overflow) { return res; } return IsNeg() ? SMinValue(width) : SMaxValue(width); } SInt SInt::USubSat(const SInt& rhs) const { bool overflow; SInt res = USubOvf(rhs, overflow); return overflow ? UMinValue(width) : res; } SInt SInt::SMulSat(const SInt& rhs) const { bool overflow; SInt res = SMulOvf(rhs, overflow); if (!overflow) { return res; } return IsNeg() != rhs.IsNeg() ? SMinValue(width) : SMaxValue(width); } SInt SInt::UMulSat(const SInt& rhs) const { bool overflow; SInt res = UMulOvf(rhs, overflow); return overflow ? UMaxValue(width) : res; } SInt SInt::SDivSat(const SInt& rhs) const { if (rhs.SVal() == -1 && this->IsSMinValue()) { return SMaxValue(width); } return SDiv(rhs); } SInt SInt::SShlSat(const SInt& rhs) const { bool overflow; SInt res = SShlOvf(rhs, overflow); if (!overflow) { return res; } return IsNeg() ? SMinValue(width) : SMaxValue(width); } SInt SInt::UShlSat(const SInt& rhs) const { bool overflow; SInt res = UShlOvf(rhs, overflow); return overflow ? UMaxValue(width) : res; } bool operator==(uint64_t v1, const SInt& v2) { return v2 == v1; } bool operator!=(uint64_t v1, const SInt& v2) { return v2 != v1; } SInt operator-(SInt v) { v.Neg(); return v; } SInt operator+(SInt a, const SInt& b) { a += b; return a; } SInt operator+(const SInt& a, SInt&& b) { b += a; return std::move(b); } SInt operator+(SInt a, uint64_t rhs) { a += rhs; return a; } SInt operator+(uint64_t lhs, SInt b) { b += lhs; return b; } SInt operator&(SInt a, const SInt& b) { a &= b; return a; } SInt operator&(const SInt& a, SInt&& b) { b &= a; return std::move(b); } SInt operator&(SInt a, uint64_t rhs) { a &= rhs; return a; } SInt operator&(uint64_t lhs, SInt b) { b &= lhs; return b; } SInt operator|(SInt a, const SInt& b) { a |= b; return a; } SInt operator|(const SInt& a, SInt&& b) { b |= a; return std::move(b); } SInt operator|(SInt a, uint64_t rhs) { a |= rhs; return a; } SInt operator|(uint64_t lhs, SInt b) { b |= lhs; return b; } SInt operator^(SInt a, const SInt& b) { a ^= b; return a; } SInt operator^(const SInt& a, SInt&& b) { b ^= a; return std::move(b); } SInt operator^(SInt a, uint64_t rhs) { a ^= rhs; return a; } SInt operator^(uint64_t lhs, SInt b) { b ^= lhs; return b; } SInt operator-(SInt a, const SInt& b) { a -= b; return a; } SInt operator-(const SInt& a, SInt&& b) { b.Neg(); b += a; return std::move(b); } SInt operator-(SInt a, uint64_t rhs) { a -= rhs; return a; } SInt operator-(uint64_t lhs, SInt b) { b.Neg(); b += lhs; return b; } SInt operator*(SInt a, uint64_t rhs) { a *= rhs; return a; } SInt operator*(uint64_t lhs, SInt b) { b *= lhs; return b; } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/Analysis/SIntDomain.cpp000066400000000000000000001044271510705540100226270ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Analysis/SIntDomain.h" #include "cangjie/CHIR/Analysis/BoolDomain.h" namespace Cangjie::CHIR { SIntDomain::SIntDomain(const ConstantRange& numeric, bool isUnsigned) : numeric{numeric}, symbolics{}, unsignedFlag{isUnsigned} { } SIntDomain::SIntDomain(const ConstantRange& numeric, SymbolicBoundsMap&& symbolics, bool isUnsigned) : numeric{numeric}, symbolics{std::move(symbolics)}, unsignedFlag{isUnsigned} { for (const auto& it : symbolics) { CJC_ASSERT(it.second.IsNonTrivial()); } } SIntDomain::SIntDomain(const ConstantRange& numeric, PtrSymbol symbol, const ConstantRange& symbolicBound) : numeric{numeric}, symbolics{}, unsignedFlag{symbol->GetType()->IsUnsignedInteger()} { symbolics.emplace(symbol, symbolicBound); CJC_ASSERT(symbolicBound.Width() == numeric.Width()); } bool SIntDomain::IsTop() const { return numeric.IsFullSet() && symbolics.empty(); } bool SIntDomain::IsNonTrivial() const { return !IsTop(); } bool SIntDomain::IsBottom() const { return numeric.IsEmptySet(); } const SIntDomain* SIntDomain::Formatter::operator->() const { return &d; } SIntDomain::Formatter SIntDomain::ToString(bool asUnsigned, Radix radix) const { return {*this, asUnsigned, radix}; } SIntDomain::Formatter SIntDomain::ToString() const { return ToString(unsignedFlag, Radix::R10); } std::ostream& operator<<(std::ostream& out, const SIntDomain& d) { return out << d.ToString(); } Mem SIntDomain::From(const LiteralValue& literal) { CJC_ASSERT(literal.IsIntLiteral()); const IntLiteral* intLiteral = StaticCast(&literal); if (intLiteral->IsSigned()) { auto v = static_cast(intLiteral->GetSignedVal()); return {ConstantRange{{ToWidth(literal.GetType()), v}}, false}; } else { return {ConstantRange{{ToWidth(literal.GetType()), intLiteral->GetUnsignedVal()}}, true}; } } Mem SIntDomain::FromNumeric(RelationalOperation rel, const SInt& value, bool isUnsigned) { return {ConstantRange::From(rel, value, !isUnsigned), isUnsigned}; } Mem SIntDomain::FromSymbolic(RelationalOperation rel, PtrSymbol symbol, IntWidth width, bool isUnsigned) { SIntDomain res{ConstantRange::Full(width), isUnsigned}; if (rel == RelationalOperation::NE) { return res; } res.symbolics.emplace(symbol, ConstantRange::From(rel, {width, 0}, !isUnsigned)); return res; } const ConstantRange& SIntDomain::NumericBound() const& { return numeric; } ConstantRange SIntDomain::NumericBound() && { return numeric; } IntWidth SIntDomain::Width() const { return numeric.Width(); } bool SIntDomain::IsUnsigned() const { return unsignedFlag; } SIntDomain::SymbolicBoundsMapIterator::SymbolicBoundsMapIterator(const SymbolicBoundsMap& map) : map{map} { } std::map::const_iterator SIntDomain::SymbolicBoundsMapIterator::Begin() const { return map.begin(); } std::map::const_iterator SIntDomain::SymbolicBoundsMapIterator::End() const { return map.end(); } bool SIntDomain::SymbolicBoundsMapIterator::Empty() const { return map.empty(); } SIntDomain::SymbolicBoundsMapIterator SIntDomain::SymbolicBounds() const { return SymbolicBoundsMapIterator{symbolics}; } const ConstantRange* SIntDomain::FindSymbolicBound(PtrSymbol symbol) const { if (auto it = symbolics.find(symbol); it != symbolics.end()) { return &it->second; } return nullptr; } bool SIntDomain::IsSingleValue() const { return NumericBound().IsSingleElement(); } bool SIntDomain::IsSame(const SIntDomain& domain) const { return numeric == domain.numeric; } std::ostream& operator<<(std::ostream& out, const SIntDomain::SymbolicFormatter& fmt) { out << fmt.symbol->GetSrcCodeIdentifier() << ':'; // operator<< is not virtual function, cast to reference to base class would output it as a pure numeric // ConstantRange. Also, cast from reference to reference triggers no derived-class-to-base-class truncating copy. return out << static_cast(fmt); } std::ostream& operator<<(std::ostream& out, const SIntDomain::Formatter& fmt) { if (fmt->IsBottom()) { return out << fmt->NumericBound().ToString(fmt.asUnsigned, fmt.radix); } if (fmt->symbolics.empty()) { return out << fmt->NumericBound().ToString(fmt.asUnsigned, fmt.radix); } out << ConstantRange::Formatter::DIVIDOR; if (fmt->NumericBound().IsNonTrivial()) { out << fmt->NumericBound().ToString(fmt.asUnsigned, fmt.radix); } std::vectorSymbolicBounds().Begin())> pt(fmt->symbolics.size()); auto ptins{pt.begin()}; for (auto it{fmt->SymbolicBounds().Begin()}; it != fmt->SymbolicBounds().End(); ++it) { *ptins++ = it; } std::sort(pt.begin(), pt.end(), [](const auto& a, const auto& b) { return a->first->GetSrcCodeIdentifier() <= b->first->GetSrcCodeIdentifier(); }); for (auto it : pt) { out << ';' << SIntDomain::SymbolicFormatter{{{false, fmt.radix}, it->second}, it->first}; } return out << ConstantRange::Formatter::DIVIDOR; } static RelationalOperation From(const ExprKind& op) { switch (op) { case ExprKind::EQUAL: return RelationalOperation::EQ; case ExprKind::LE: return RelationalOperation::LE; case ExprKind::LT: return RelationalOperation::LT; case ExprKind::GT: return RelationalOperation::GT; case ExprKind::GE: return RelationalOperation::GE; case ExprKind::NOTEQUAL: return RelationalOperation::NE; default: CJC_ABORT(); return RelationalOperation::LE; } } RelationalOperation SymbolicNeg(RelationalOperation a) { switch (a) { case RelationalOperation::LT: return RelationalOperation::GT; case RelationalOperation::LE: return RelationalOperation::GE; case RelationalOperation::EQ: return RelationalOperation::EQ; case RelationalOperation::GE: return RelationalOperation::LE; case RelationalOperation::GT: return RelationalOperation::LT; default: return RelationalOperation::NE; } } // Region: SIntDomain namespace { inline bool IsSaturating(Cangjie::OverflowStrategy ov) { return ov == OverflowStrategy::SATURATING; } ConstantRange NumericConversionU2SSameWidth(const ConstantRange& src); // Convert the numeric bound from an SIntDomain value to signed to be stored as symbolic bound // as symbolic bounds are always stored as signed range. // That is, cast the range to signed with throwing strategy if it is an unsigned bound, if this // cast narrowed the range, the result may not cover all possible symbolic ranges, discard it; otherwise // return the range. If the range is signed itself, do nothing. std::optional NumericBoundToSignedSymbolicBound(const ConstantRange& origin, bool isUnsigned) { if (!isUnsigned) { return origin; } auto res{NumericConversionU2SSameWidth(origin)}; if (res == origin) { return res; } return std::nullopt; } SIntDomain ComputeAdd(const CHIRArithmeticBinopArgs& args) { auto& lhs = args.ld; auto& rhs = args.rd; auto& ln = lhs.NumericBound(); auto& rn = rhs.NumericBound(); bool isUnsigned = args.uns; ConstantRange numeric = !IsSaturating(args.ov) ? ln.Add(rn) : (isUnsigned ? ln.UAddSat(rn) : ln.SAddSat(rn)); // combine numeric range with arithmetic range is only safe in THROWING strategy if (numeric.IsEmptySet() || args.ov != OverflowStrategy::THROWING) { return {numeric, isUnsigned}; } SIntDomain::SymbolicBoundsMap mp{}; if (!ln.IsSingleElement() && rn.IsNonTrivial()) { if (auto rhsAsSymbolicRange{NumericBoundToSignedSymbolicBound(rn, isUnsigned)}) { mp.emplace(args.l, *rhsAsSymbolicRange); } } if (!rn.IsSingleElement() && ln.IsNonTrivial()) { if (auto lhsAsSymbolicRange{NumericBoundToSignedSymbolicBound(ln, isUnsigned)}) { mp.emplace(args.r, *lhsAsSymbolicRange); } } return {numeric, std::move(mp), isUnsigned}; } SIntDomain ComputeSub(const CHIRArithmeticBinopArgs& args) { auto& lhs = args.ld; auto& rhs = args.rd; auto& ln = lhs.NumericBound(); auto& rn = rhs.NumericBound(); ConstantRange numeric = !IsSaturating(args.ov) ? ln.Sub(rn) : (args.uns ? ln.USubSat(rn) : ln.SSubSat(rn)); if (numeric.IsEmptySet() || args.ov != OverflowStrategy::THROWING) { return {numeric, args.uns}; } if (!ln.IsSingleElement() && rn.IsNonTrivial()) { // symbola - symbolb: if a is not a single element, add a symbolic bound: // symbola:[-b.upper,-b.lower] // Also, cannot negate signed min (implementation issue) // In that case, the symbolic bound is discarded if (rn != SInt::SMinValue(rn.Width())) { return {numeric, args.l, rn.Negate()}; } } return {numeric, args.uns}; } SIntDomain ComputeMul(const CHIRArithmeticBinopArgs& args) { auto& ln = args.ld.NumericBound(); auto& rn = args.rd.NumericBound(); bool isUnsigned = args.uns; ConstantRange r{!IsSaturating(args.ov) ? (isUnsigned ? ln.UMul(rn) : ln.SMul(rn)) : (isUnsigned ? ln.UMulSat(rn) : ln.SMulSat(rn))}; return {r, args.uns}; } SIntDomain ComputeDiv(const CHIRArithmeticBinopArgs& args) { auto& ln = args.ld.NumericBound(); auto& rn = args.rd.NumericBound(); ConstantRange r{args.uns ? ln.UDiv(rn) : (IsSaturating(args.ov) ? ln.SDivSat(rn) : ln.SDiv(rn))}; return {r, args.uns}; } SIntDomain ComputeMod(const CHIRArithmeticBinopArgs& args) { auto& ln = args.ld.NumericBound(); auto& rn = args.rd.NumericBound(); return args.uns ? SIntDomain{ln.URem(rn), true} : SIntDomain{ln.SRem(rn), false}; } // Define the result of a relational operation that may yield unordered enum class CompareResult : uint8_t { UNO, TRUE, FALSE }; bool ToBool(CompareResult r) { return r == CompareResult::TRUE; } CompareResult Neg(CompareResult a) { switch (a) { case CompareResult::UNO: return CompareResult::UNO; case CompareResult::TRUE: return CompareResult::FALSE; default: return CompareResult::TRUE; } } CompareResult Unless(bool exp) { return exp ? CompareResult::TRUE : CompareResult::UNO; } CompareResult Lt(const ConstantRange& a, const ConstantRange& b, bool isUnsigned) { return Unless(isUnsigned ? a.UMaxValue().Ult(b.UMinValue()) : a.SMaxValue().Slt(b.SMinValue())); } CompareResult Le(const ConstantRange& a, const ConstantRange& b, bool isUnsigned) { return Unless(isUnsigned ? a.UMaxValue().Ule(b.UMinValue()) : a.SMaxValue().Sle(b.SMinValue())); } CompareResult Ge(const ConstantRange& a, const ConstantRange& b, bool isUnsigned) { return Unless(isUnsigned ? a.UMinValue().Uge(b.UMaxValue()) : a.SMinValue().Sge(b.SMaxValue())); } CompareResult Gt(const ConstantRange& a, const ConstantRange& b, bool isUnsigned) { return Unless(isUnsigned ? a.UMinValue().Ugt(b.UMaxValue()) : a.SMinValue().Sgt(b.SMaxValue())); } // Return the first ordered (i.e. True/False) result of a and b, or Unordered if both Unordered. // The result is undefined if both operands are not Unordered. CompareResult UnlessBothUnordered(CompareResult a, CompareResult b) { return a != CompareResult::UNO ? a : b; } // Return the operation that always returns false when the original returns true RelationalOperation Neg(RelationalOperation a) { switch (a) { case RelationalOperation::LT: return RelationalOperation::GE; case RelationalOperation::LE: return RelationalOperation::GT; case RelationalOperation::EQ: return RelationalOperation::NE; case RelationalOperation::GE: return RelationalOperation::LT; case RelationalOperation::GT: return RelationalOperation::LE; default: return RelationalOperation::EQ; } } CompareResult PartialCompare( RelationalOperation compare, const ConstantRange& a, const ConstantRange& b, bool isUnsigned) { // cannot compare wrapped set if (isUnsigned) { if (a.IsWrappedSet() || b.IsWrappedSet()) { return CompareResult::UNO; } } else { if (a.IsSignWrappedSet() || b.IsSignWrappedSet()) { return CompareResult::UNO; } } switch (compare) { case RelationalOperation::LE: return Le(a, b, isUnsigned); case RelationalOperation::LT: return Lt(a, b, isUnsigned); case RelationalOperation::GT: return Gt(a, b, isUnsigned); case RelationalOperation::GE: return Ge(a, b, isUnsigned); default: CJC_ABORT(); return CompareResult::FALSE; } } bool IsUnsignedCompareToZero(const ConstantRange& a) { if (a.IsSingleElement() && a.GetSingleElement() == 0) { return true; } return false; } CompareResult Compare(RelationalOperation compare, const ConstantRange& a, const ConstantRange& b, bool isUnsigned) { if (a.IsEmptySet() || b.IsEmptySet()) { return CompareResult::UNO; } if ((a.IsFullSet() && !IsUnsignedCompareToZero(b)) || (b.IsFullSet() && !IsUnsignedCompareToZero(a))) { return CompareResult::UNO; } return UnlessBothUnordered( PartialCompare(compare, a, b, isUnsigned), Neg(PartialCompare(Neg(compare), a, b, isUnsigned))); } // Compute the binary result for total ordering (i.e. lt, le, gt, ge) decltype(BoolDomain::Top()) ComputeTotalOrderingIntBinop(const CHIRRelIntBinopArgs& args) { auto a = args.l; auto b = args.r; CJC_ASSERT(a != b); auto op = From(args.op); bool uns = args.uns; auto& ia{args.ld}; auto& ib{args.rd}; if (ia.IsBottom() || ib.IsBottom()) { return BoolDomain::Bottom(); } auto width = ia.Width(); if (auto r = Compare(op, ia.NumericBound(), ib.NumericBound(), uns); r != CompareResult::UNO) { return BoolDomain::FromBool(ToBool(r)); } if (auto ibs = ia.FindSymbolicBound(b)) { // symbol `a' is equivalent to a[0]..a[0], so we have // a[0]..a..a[0], b[ibs.second.lower]..b..a[ibs.second.upper] if (auto r = Compare(op, *ibs, ConstantRange{{width, 0}}, false); r != CompareResult::UNO) { return BoolDomain::FromBool(ToBool(r)); } } if (auto ias = ib.FindSymbolicBound(a)) { // b[ias.second.lower].a..b[ias.second.upper], b[0]..b..b[0] if (auto r = Compare(SymbolicNeg(op), *ias, ConstantRange{{width, 0}}, false); r != CompareResult::UNO) { return BoolDomain::FromBool(ToBool(r)); } } // traverse the two symbolic bound maps to find symbolic bound against a common symbol auto ita = ia.SymbolicBounds().Begin(); auto itb = ib.SymbolicBounds().Begin(); while (ita != ia.SymbolicBounds().End() && itb != ib.SymbolicBounds().End()) { if (ita->first.get() < itb->first.get()) { ++ita; continue; } if (ita->first.get() > itb->first.get()) { ++itb; continue; } // compare the symbolic bound so we can generate range info based on transitive partial // ordering relation if (auto r = Compare(op, ita++->second, itb++->second, false); r != CompareResult::UNO) { return BoolDomain::FromBool(ToBool(r)); } } return BoolDomain::Top(); } // Try to find transitive equality relation from the symbolic bounds of two symbol // i.e. they have symbolic bound against the same symbol, and if their bounds do not intersect, the equality operation // would never yield true. // Returns null if no decisive result exists. decltype(BoolDomain::Top()) CompareTransitiveEquality(decltype(std::declval().SymbolicBounds()) ia, decltype(std::declval().SymbolicBounds()) ib, bool cne) { auto ita = ia.Begin(); auto itb = ib.Begin(); while (ita != ia.End() && itb != ib.End()) { if (ita->first.get() < itb->first.get()) { ++ita; continue; } if (ita->first.get() > itb->first.get()) { ++itb; continue; } // compare the symbolic bound so we can generate range info based on transitive equality relation auto sa = ita++->second; auto sb = itb++->second; if (sa.IsSingleElement() && sb.IsSingleElement()) { // a bound to c..[c1,c2], b bound to c..[c3,c4], if c1=c2 and c3=c4: // if c1==c3, then euqal; if c1!=c3, then not equal return BoolDomain::FromBool((sa.GetSingleElement() == sb.GetSingleElement()) != cne); } // a and b do not intersect, then never equal // note that symbolic range is always signed if (sa.IntersectWith(sb, PreferredRangeType::Signed).IsEmptySet()) { return BoolDomain::FromBool(cne); } } return BoolDomain::Top(); } decltype(BoolDomain::Top()) ComputeEqualityIntBinop( const SIntDomain& ia, const SIntDomain& ib, const Ptr& a, const Ptr& b, bool cne) { if (ia.IsBottom() || ib.IsBottom()) { return BoolDomain::Bottom(); } // single element comparison if (ia.NumericBound().IsSingleElement() && ib.NumericBound().IsSingleElement()) { auto v1 = ia.NumericBound().GetSingleElement(); auto v2 = ib.NumericBound().GetSingleElement(); // if single element identical, and relop is eq, return true; // if single element different, and relop is ne, return true; // otherwise return false return BoolDomain::FromBool((v1 == v2) != cne); } // Non-intersecting sets can never equal if (ia.NumericBound().IntersectWith(ib.NumericBound(), PreferFromBool(ia.IsUnsigned())).IsEmptySet()) { // compare neq, intersection is empty: true // compare eq, intersection is empty: false return BoolDomain::FromBool(cne); } // a bound to b..[], if range is exactly one value 0, then equal if (auto ibs = ia.FindSymbolicBound(b); ibs && ibs->IsSingleElement()) { if (ibs->IsSingleElement()) { return BoolDomain::FromBool((ibs->GetSingleElement() == 0ull) != cne); } // ibs does not contain 0, say a is bound to b..[3,7], then never equal if (!ibs->Contains(SInt::Zero(ia.Width()))) { return BoolDomain::FromBool(cne); } } // likewise, b bound to a..[], if range is exactly one value 0, then equal if (auto ias = ib.FindSymbolicBound(a)) { if (ias->IsSingleElement()) { return BoolDomain::FromBool((ias->GetSingleElement() == 0ull) != cne); } if (!ias->Contains(SInt::Zero(ia.Width()))) { return BoolDomain::FromBool(cne); } } if (auto transitiveRes = CompareTransitiveEquality(ia.SymbolicBounds(), ib.SymbolicBounds(), cne); transitiveRes.IsNonTrivial()) { return transitiveRes; } return BoolDomain::Top(); } // Returns true if both lhs and rhs refer to the same symbol (i.e. this relational operation supersets // the equality relation) bool TrueOnSameSymbol(ExprKind op) { switch (op) { case ExprKind::EQUAL: case ExprKind::LE: case ExprKind::GE: return true; default: return false; } } } // anonymous namespace decltype(BoolDomain::Top()) ComputeRelIntBinop(CHIRRelIntBinopArgs&& args) { if (args.l == args.r) { return BoolDomain::FromBool(TrueOnSameSymbol(args.op)); } auto op{From(args.op)}; if (op == RelationalOperation::EQ || op == RelationalOperation::NE) { return ComputeEqualityIntBinop(args.ld, args.rd, args.l, args.r, args.op == ExprKind::NOTEQUAL); } return ComputeTotalOrderingIntBinop(args); } decltype(BoolDomain::Top()) ComputeEqualityBoolBinop(const BoolDomain& ld, const BoolDomain& rd, ExprKind op) { if (ld.IsBottom() || rd.IsBottom()) { return BoolDomain::Bottom(); } if (ld.IsSingleValue() && rd.IsSingleValue()) { return BoolDomain::FromBool((ld.GetSingleValue() == rd.GetSingleValue()) != (op == ExprKind::NOTEQUAL)); } return BoolDomain::Top(); } // Compute a new SIntDomain with this and \p rhs by a binary operator \p op SIntDomain ComputeArithmeticBinop(CHIRArithmeticBinopArgs&& args) { switch (args.op) { case ExprKind::ADD: return ComputeAdd(args); case ExprKind::SUB: return ComputeSub(args); case ExprKind::MUL: return ComputeMul(args); case ExprKind::DIV: return ComputeDiv(args); case ExprKind::MOD: return ComputeMod(args); default: CJC_ABORT(); return {ConstantRange{{ToWidth(*args.l->GetType()), 0}}, args.uns}; } } Mem SIntDomain::Top(IntWidth width, bool isUnsigned) { return {ConstantRange::Full(width), isUnsigned}; } Mem SIntDomain::Bottom(IntWidth width, bool isUnsigned) { return {ConstantRange::Empty(width), isUnsigned}; } IntWidth SIntDomain::ToWidth(const Ptr& type) { switch (type->GetTypeKind()) { case Type::TypeKind::TYPE_INT8: case Type::TypeKind::TYPE_UINT8: return IntWidth::I8; case Type::TypeKind::TYPE_INT16: case Type::TypeKind::TYPE_UINT16: return IntWidth::I16; case Type::TypeKind::TYPE_INT32: case Type::TypeKind::TYPE_UINT32: return IntWidth::I32; default: return IntWidth::I64; } } namespace { struct SIntDomainMergeImpl { static Mem Intersects(const SIntDomain& a, const SIntDomain& b) { return MergeRangesForLogicalOperation(a, b, &SIntDomainMergeImpl::ConjunctionInternalMergeFun); } static Mem Unions(const SIntDomain& a, const SIntDomain& b) { return MergeRangesForLogicalOperation(a, b, &SIntDomainMergeImpl::DisjunctionInternalMergeFun); } private: using OptionalMergeFun = ConstantRange (*)(const ConstantRange*, const ConstantRange*); static ConstantRange MergeNumericRangesForLogicalOperation( const SIntDomain& a, const SIntDomain& b, OptionalMergeFun mergeFunc) { auto& na = a.NumericBound(); auto& nb = b.NumericBound(); return mergeFunc(&na, &nb); } static std::list> MergeSymbolicRangesForLogicalOperationWithNulloptAsMissingBound( const SIntDomain& a, const SIntDomain& b, OptionalMergeFun mergeFunc) { auto ia = a.SymbolicBounds().Begin(); auto ib = b.SymbolicBounds().Begin(); auto enda = a.SymbolicBounds().End(); auto endb = b.SymbolicBounds().End(); std::list> mergeTmps{}; while (ia != enda && ib != endb) { if (ia->first < ib->first) { mergeTmps.emplace_back(ia->first, mergeFunc(&ia->second, nullptr)); ++ia; } else if (ib->first < ia->first) { mergeTmps.emplace_back(ib->first, mergeFunc(nullptr, &ib->second)); ++ib; } else { mergeTmps.emplace_back(ia->first, mergeFunc(&ia->second, &ib->second)); ++ia; ++ib; } } while (ia != enda) { mergeTmps.emplace_back(ia->first, mergeFunc(&ia->second, nullptr)); ++ia; } while (ib != endb) { mergeTmps.emplace_back(ib->first, mergeFunc(nullptr, &ib->second)); ++ib; } return mergeTmps; } // Merge two ranges into one range using @p:mergeFunc. Missing ranges are represented by null optional value, // numeric bound is represented by a null optional symbol. A null return value of the functor @p:mergeFunc indicates // that the result cannot be calculated and the corresponding symbolic bound is ignored. static Mem MergeRangesForLogicalOperation( const SIntDomain& a, const SIntDomain& b, OptionalMergeFun mergeFunc) { ConstantRange n = MergeNumericRangesForLogicalOperation(a, b, mergeFunc); if (n.IsEmptySet()) { return SIntDomain::Bottom(a.Width(), a.IsUnsigned()); } auto mergeTmps = MergeSymbolicRangesForLogicalOperationWithNulloptAsMissingBound(a, b, mergeFunc); SIntDomain::SymbolicBoundsMap m; for (auto [symbol, boundOpt] : mergeTmps) { if (boundOpt.IsEmptySet()) { return SIntDomain::Bottom(a.Width(), a.IsUnsigned()); } if (boundOpt.IsNonTrivial()) { m.emplace(symbol, boundOpt); } } return {n, std::move(m), a.IsUnsigned()}; } static ConstantRange ConjunctionInternalMergeFun(const ConstantRange* a, const ConstantRange* b) { return !a ? *b : !b ? *a : a->IntersectWith(*b); } static ConstantRange DisjunctionInternalMergeFun(const ConstantRange* a, const ConstantRange* b) { return !a ? ConstantRange::Full(b->Width()) : !b ? ConstantRange::Full(a->Width()) : a->UnionWith(*b); } }; } // namespace Mem SIntDomain::Intersects(const Mem& lhs, const Mem& rhs) { return SIntDomainMergeImpl::Intersects(lhs, rhs); } Mem SIntDomain::Unions(const Mem& lhs, const Mem& rhs) { return SIntDomainMergeImpl::Unions(lhs, rhs); } namespace { ConstantRange NCSameSignessInner( const ConstantRange& src, IntWidth dstSize, bool isUnsigned, Cangjie::OverflowStrategy ov) { auto srcSize = src.Width(); if (src.IsFullSet()) { return ConstantRange::Full(dstSize); } if (src.IsEmptySet()) { return ConstantRange::Empty(dstSize); } auto higherPart = (isUnsigned ? ConstantRange::From(RelationalOperation::GE, SInt::UMinValue(dstSize).ZExt(srcSize), false) : ConstantRange::From(RelationalOperation::GE, SInt::SMinValue(dstSize).SExt(srcSize), true)) .IntersectWith(src, PreferFromBool(isUnsigned)); auto unchangedPart = (isUnsigned ? ConstantRange::From(RelationalOperation::LE, SInt::UMaxValue(dstSize).ZExt(srcSize), false) : ConstantRange::From(RelationalOperation::LE, SInt::SMaxValue(dstSize).SExt(srcSize), true)) .IntersectWith(higherPart, PreferFromBool(isUnsigned)); auto res = unchangedPart.Truncate(dstSize); if (src == unchangedPart || ov == OverflowStrategy::THROWING) { return res; } if (ov == OverflowStrategy::WRAPPING) { return ConstantRange::Full(dstSize); } if (src.Difference(higherPart).IsNotEmptySet()) { res = res.UnionWith( {isUnsigned ? SInt::UMinValue(dstSize) : SInt::SMinValue(dstSize)}, PreferFromBool(isUnsigned)); } if (higherPart.Difference(unchangedPart).IsNotEmptySet()) { res = res.UnionWith( {isUnsigned ? SInt::UMaxValue(dstSize) : SInt::SMaxValue(dstSize)}, PreferFromBool(isUnsigned)); } return res; } ConstantRange NumericConversionWithSameSigness( const ConstantRange& src, IntWidth dstSize, bool isUnsigned, Cangjie::OverflowStrategy ov) { auto srcSize = src.Width(); if (srcSize == dstSize) { return src; } if (srcSize < dstSize) { return isUnsigned ? src.ZeroExtend(dstSize) : src.SignExtend(dstSize); } if (isUnsigned ? src.IsWrappedSet() : src.IsSignWrappedSet()) { auto [l, r] = src.SplitWrapping(isUnsigned); auto l1 = NCSameSignessInner(l, dstSize, isUnsigned, ov); auto r1 = NCSameSignessInner(r, dstSize, isUnsigned, ov); return l1.UnionWith(r1, PreferFromBool(isUnsigned)); } return NCSameSignessInner(src, dstSize, isUnsigned, ov); } // For \p src {a...b} where b is upper wrapped but \p src not wrapped, raw truncation from srcSize to \p dstSize // would wrap b into an unexpected small value (i.e. equal to a), and this function handles this special case // to keep the range mathemetically consistent. For other non-wrapped set, this function truncates \p src as // normal. // If \p src is a wrapped set, the behaviour is undefined. ConstantRange TruncWithUpperWrapped(const ConstantRange& src, bool dstUnsigned, IntWidth dstSize) { CJC_ASSERT(dstUnsigned ? !src.IsWrappedSet() : !src.IsSignWrappedSet()); CJC_ASSERT(src.Width() > dstSize); if (src.Upper() == (dstUnsigned ? SInt::UMaxValue(dstSize).ZExt(src.Width()) : SInt::SMaxValue(dstSize).ZExt(src.Width())) + 1) { auto lower = src.Lower().Trunc(dstSize); auto upper = (src.Upper() - 1).Trunc(dstSize) + 1; if (lower == upper) { return ConstantRange::Full(dstSize); } return {lower, upper}; } return src.Truncate(dstSize); } // unsigned to signed, srcSize >= dstSize ConstantRange NCU2SInner(const ConstantRange& src, IntWidth dstSize, Cangjie::OverflowStrategy ov) { auto dstMax = SInt::SMaxValue(dstSize); auto srcSize = src.Width(); if (srcSize > dstSize) { dstMax = dstMax.SExt(srcSize); } if (src.IsEmptySet()) { return ConstantRange::Empty(dstSize); } auto unchangedRange = ConstantRange::From(RelationalOperation::LE, dstMax, false).IntersectWith(src, PreferredRangeType::Unsigned); if (unchangedRange == src || ov == OverflowStrategy::THROWING) { if (srcSize > dstSize) { return TruncWithUpperWrapped(unchangedRange, false, dstSize); } return unchangedRange; } if (ov == OverflowStrategy::SATURATING) { auto ans = unchangedRange.UnionWith(dstMax, PreferredRangeType::Signed); if (srcSize > dstSize) { return TruncWithUpperWrapped(ans, false, dstSize); } return ans; } if (srcSize == dstSize) { return src; } // too hard to compute truncating range with wrapping, return full return ConstantRange::Full(dstSize); } ConstantRange NumericConversionU2S(const ConstantRange& src, IntWidth dstSize, Cangjie::OverflowStrategy ov) { auto srcSize = src.Width(); if (dstSize > srcSize) { return src.ZeroExtend(dstSize); } if (!src.IsWrappedSet()) { return NCU2SInner(src, dstSize, ov); } auto [l, r] = src.SplitWrapping(true); return NCU2SInner(l, dstSize, ov).UnionWith(NCU2SInner(r, dstSize, ov), PreferredRangeType::Signed); } ConstantRange NumericConversionU2SSameWidth(const ConstantRange& src) { return NumericConversionU2S(src, src.Width(), OverflowStrategy::THROWING); } // signed to unsigned, dst size smaller than src size ConstantRange NCS2UInnerTrunc(const ConstantRange& src, IntWidth dstSize, Cangjie::OverflowStrategy ov) { if (src.IsEmptySet()) { return ConstantRange::Empty(dstSize); } auto srcSize = src.Width(); auto nonNegRange = src.IntersectWith( ConstantRange::From(RelationalOperation::GE, SInt::Zero(srcSize), true), PreferredRangeType::Signed); auto negRange = src.Difference(nonNegRange); auto unchangedRange = nonNegRange.IntersectWith( ConstantRange::From(RelationalOperation::LE, SInt::UMaxValue(dstSize).ZExt(srcSize), true)); if (unchangedRange == src || ov == OverflowStrategy::THROWING) { return TruncWithUpperWrapped(unchangedRange, true, dstSize); } if (ov == OverflowStrategy::SATURATING) { auto res = unchangedRange; if (negRange.IsNotEmptySet()) { res = res.UnionWith(SInt::Zero(srcSize), PreferredRangeType::Unsigned); } if (unchangedRange != nonNegRange) { res = res.UnionWith(SInt::UMaxValue(dstSize).ZExt(srcSize), PreferredRangeType::Unsigned); } return TruncWithUpperWrapped(res, true, dstSize); } return ConstantRange::Full(dstSize); } // singed to unsigned, dst size not smaller than src ConstantRange NCS2UInnerExtend(const ConstantRange& src, IntWidth dstSize, Cangjie::OverflowStrategy ov) { if (src.IsEmptySet()) { return ConstantRange::Empty(dstSize); } auto srcSize = src.Width(); auto nonNegRange = src.IntersectWith( ConstantRange::From(RelationalOperation::GE, SInt::Zero(srcSize), true), PreferredRangeType::Signed); auto negRange = src.Difference(nonNegRange); if (nonNegRange == src || ov == OverflowStrategy::THROWING) { return srcSize == dstSize ? nonNegRange : nonNegRange.ZeroExtend(dstSize); } if (ov == OverflowStrategy::SATURATING) { if (negRange.IsNotEmptySet()) { nonNegRange = nonNegRange.UnionWith({SInt::UMinValue(srcSize)}); } return srcSize == dstSize ? nonNegRange : nonNegRange.ZeroExtend(dstSize); } ConstantRange negOverflowRange{negRange.SMinValue().SExt(dstSize), negRange.SMaxValue().SExt(dstSize) + 1}; return negOverflowRange.UnionWith( srcSize == dstSize ? nonNegRange : nonNegRange.ZeroExtend(dstSize), PreferredRangeType::Unsigned); } ConstantRange NumericConversionS2U(const ConstantRange& src, IntWidth dstSize, Cangjie::OverflowStrategy ov) { auto srcSize = src.Width(); if (dstSize < srcSize) { if (src.IsSignWrappedSet()) { auto [l, r] = src.SplitWrapping(false); return NCS2UInnerTrunc(l, dstSize, ov) .UnionWith(NCS2UInnerTrunc(r, dstSize, ov), PreferredRangeType::Unsigned); } return NCS2UInnerTrunc(src, dstSize, ov); } return NCS2UInnerExtend(src, dstSize, ov); } } // anonymous namespace ConstantRange NumericConversion( const ConstantRange& src, IntWidth dstSize, bool srcUnsigned, bool dstUnsigned, Cangjie::OverflowStrategy ov) { if (srcUnsigned == dstUnsigned) { return NumericConversionWithSameSigness(src, dstSize, srcUnsigned, ov); } if (srcUnsigned) { return NumericConversionU2S(src, dstSize, ov); } return NumericConversionS2U(src, dstSize, ov); } ConstantRange ComputeTypeCastNumericBound( const SIntDomain& v, IntWidth dstSize, bool dstUnsigned, Cangjie::OverflowStrategy ov) { if (v.IsBottom()) { return ConstantRange::Empty(dstSize); } return NumericConversion(v.NumericBound(), dstSize, v.IsUnsigned(), dstUnsigned, ov); } } // namespace Cangjie::CHIRcangjie_compiler-1.0.7/src/CHIR/Analysis/TypeAnalysis.cpp000066400000000000000000000424401510705540100232430ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Analysis/TypeAnalysis.h" #include "cangjie/CHIR/Utils.h" #include namespace Cangjie::CHIR { TypeValue::TypeValue(DevirtualTyKind kind, Type* baseLineType) : kind(kind), baseLineType(baseLineType) { } std::optional> TypeValue::Join(const TypeValue& rhs) const { auto rhsClassTy = StaticCast(rhs).GetSpecificType(); if (this->baseLineType != rhsClassTy) { if (!this->baseLineType->IsClass() || !rhsClassTy->IsClass()) { return nullptr; } auto fatherTy = LeastCommonSuperClass( StaticCast(this->baseLineType), StaticCast(rhsClassTy), builder); if (fatherTy) { return std::make_unique(DevirtualTyKind::SUBTYPE_OF, fatherTy); } else { return nullptr; } } else { return std::nullopt; } } std::string TypeValue::ToString() const { return "{ " + GetKindString(kind) + ", " + baseLineType->ToString() + " }"; } std::unique_ptr TypeValue::Clone() const { return std::make_unique(kind, baseLineType); } DevirtualTyKind TypeValue::GetTypeKind() const { return kind; } Type* TypeValue::GetSpecificType() const { return baseLineType; } void TypeValue::SetCHIRBuilder(CHIRBuilder* chirBuilder) { builder = chirBuilder; } std::string TypeValue::GetKindString(DevirtualTyKind clsTyKind) const { switch (clsTyKind) { case DevirtualTyKind::SUBTYPE_OF: return "SUBTYPE_OF"; case DevirtualTyKind::EXACTLY: return "EXACTLY"; default: return "Unknown"; } } namespace { Type* GetRealGenericRetType(const Apply& apply, Type& genericType, const Func* calleeFunc, CHIRBuilder& builder) { auto instTypes = apply.GetInstantiatedTypeArgs(); auto genericTypes = calleeFunc->GetGenericTypeParams(); CJC_ASSERT(instTypes.size() == genericTypes.size()); std::unordered_map genericMap; for (size_t i = 0; i < instTypes.size(); ++i) { genericMap[genericTypes[i]] = instTypes[i]; } if (calleeFunc->GetParentCustomTypeDef() != nullptr) { auto def = calleeFunc->GetParentCustomTypeDef(); auto thisType = apply.GetThisType()->StripAllRefs(); if (thisType->IsClassOrStruct()) { auto customType = StaticCast(thisType); auto defGenericParams = def->GetGenericTypeParams(); auto genericArgs = customType->GetGenericArgs(); CJC_ASSERT(defGenericParams.size() == genericArgs.size()); for (size_t i = 0; i < customType->GetGenericArgs().size(); ++i) { genericMap[defGenericParams[i]] = genericArgs[i]; } } } return ReplaceRawGenericArgType(genericType, genericMap, builder); } } CHIRBuilder* TypeValue::builder{nullptr}; template <> const std::string Analysis::name = "type-analysis"; template <> const std::optional Analysis::blockLimit = std::nullopt; template <> TypeDomain::ChildrenMap ValueAnalysis::globalChildrenMap{}; template <> TypeDomain::AllocatedRefMap ValueAnalysis::globalAllocatedRefMap{}; template <> TypeDomain::AllocatedObjMap ValueAnalysis::globalAllocatedObjMap{}; template <> std::vector> ValueAnalysis::globalRefPool{}; template <> std::vector> ValueAnalysis::globalAbsObjPool{}; template <> TypeDomain ValueAnalysis::globalState{&globalChildrenMap, &globalAllocatedRefMap, nullptr, &globalAllocatedObjMap, &globalRefPool, &globalAbsObjPool}; template <> bool IsTrackedGV>(const GlobalVar& gv) { Type* baseTy = gv.GetType(); while (baseTy->IsRef()) { auto ty = StaticCast(baseTy)->GetBaseType(); baseTy = ty; } return baseTy->IsClass(); } TypeAnalysis::TypeAnalysis( const Func* func, CHIRBuilder& builder, bool isDebug, const DevirtualizationInfo& devirtInfo) : ValueAnalysis(func, builder, isDebug), realRetTyMap(devirtInfo.GetReturnTypeMap()), constMemberTypeMap(devirtInfo.GetConstMemberMap()) { } bool TypeAnalysis::CheckFuncHasInvoke(const BlockGroup& body) { std::vector blocks = body.GetBlocks(); for (auto bb : blocks) { auto exprs = bb->GetNonTerminatorExpressions(); for (size_t i = 0; i < exprs.size(); ++i) { if (exprs[i]->GetExprKind() == ExprKind::LAMBDA) { if (CheckFuncHasInvoke(*StaticCast(exprs[i])->GetBody())) { return true; } } if (exprs[i]->GetExprKind() == ExprKind::INVOKE) { return true; } } } return false; } bool TypeAnalysis::Filter(const Func& method) { return CheckFuncHasInvoke(*method.GetBody()); } void TypeAnalysis::PrintDebugMessage(const Expression* expr, const TypeValue* absVal) const { std::string message = "The value of " + expr->GetResult()->GetIdentifier() + " = " + expr->ToString() + " has been set to " + absVal->ToString(); std::cout << message << std::endl; } void TypeAnalysis::HandleNormalExpressionEffect(TypeDomain& state, const Expression* expression) { switch (expression->GetExprKind()) { case ExprKind::TYPECAST: return HandleTypeCastExpr(state, StaticCast(expression)); case ExprKind::BOX: return HandleBoxExpr(state, StaticCast(expression)); default: { auto res = expression->GetResult(); auto domain = state.GetAbstractDomain(res); if ((!domain || domain->IsTop()) && (res->GetType()->IsPrimitive())) { state.Update(res, std::make_unique(DevirtualTyKind::EXACTLY, res->GetType())); } else { state.TrySetToTopOrTopRef(res, res->GetType()->IsRef()); } } } auto resultType = expression->GetResult()->GetType(); if (isDebug && !resultType->IsRef() && !resultType->IsGeneric()) { if (auto absVal = state.CheckAbstractValue(expression->GetResult()); absVal) { PrintDebugMessage(expression, absVal); } } } void TypeAnalysis::HandleAllocateExpr(TypeDomain& state, const Allocate* expression, Value* newObj) { if (!newObj) { return; } auto baseTy = expression->GetType(); if (baseTy->IsClass()) { state.Update(newObj, std::make_unique(DevirtualTyKind::EXACTLY, StaticCast(baseTy))); } } std::optional TypeAnalysis::HandleTerminatorEffect(TypeDomain& state, const Terminator* terminator) { switch (terminator->GetExprKind()) { // already handled by the framework // case ExprKind::ALLOCATE_WITH_EXCEPTION: // case ExprKind::RAW_ARRAY_ALLOCATE_WITH_EXCEPTION: // case ExprKind::RAW_ARRAY_LITERAL_ALLOCATE_WITH_EXCEPTION: // case ExprKind::APPLY_WITH_EXCEPTION: // case ExprKind::INVOKE_WITH_EXCEPTION: case ExprKind::TYPECAST_WITH_EXCEPTION: HandleTypeCastExpr(state, StaticCast(terminator)); break; case ExprKind::GOTO: case ExprKind::EXIT: case ExprKind::BRANCH: case ExprKind::MULTIBRANCH: case ExprKind::INT_OP_WITH_EXCEPTION: case ExprKind::INTRINSIC_WITH_EXCEPTION: default: { auto dest = terminator->GetResult(); if (dest) { state.SetToTopOrTopRef(dest, dest->GetType()->IsRef()); } break; } } return std::nullopt; } void TypeAnalysis::HandleInvokeExpr(TypeDomain& state, const Invoke* invoke, Value* refObj) { Type* resTy = invoke->GetResult()->GetType(); while (resTy->IsRef()) { auto ty = StaticCast(resTy)->GetBaseType(); resTy = ty; } if (!resTy->IsClass()) { return; } return state.Update( refObj, std::make_unique(DevirtualTyKind::SUBTYPE_OF, StaticCast(resTy))); } void TypeAnalysis::HandleApplyExpr(TypeDomain& state, const Apply* apply, Value* refObj) { auto callee = apply->GetCallee(); if (!callee->IsFuncWithBody()) { HandleDefaultExpr(state, apply); return; } Func* calleeFunc = DynamicCast(callee); auto it = realRetTyMap.find(calleeFunc); if (it == realRetTyMap.end()) { HandleDefaultExpr(state, apply); return; } auto realRetTy = it->second; if (realRetTy->IsGenericRelated()) { realRetTy = GetRealGenericRetType(*apply, *realRetTy, calleeFunc, builder); if (realRetTy == nullptr) { // cannot get real generic return type, return. return; } } return UpdateDefaultValue(state, apply->GetResult(), refObj, realRetTy); } // =============== Transfer functions for TypeCast expression =============== // void TypeAnalysis::HandleBoxExpr(TypeDomain& state, const Box* boxExpr) const { auto result = boxExpr->GetResult(); auto obj = state.GetReferencedObjAndSetToTop(result, boxExpr); state.Propagate(boxExpr->GetSourceValue(), obj); } template void TypeAnalysis::HandleTypeCastExpr(TypeDomain& state, const TTypeCast* typecast) const { Type* srcTy = typecast->GetSourceTy(); Type* tgtTy = typecast->GetTargetTy(); while (srcTy->IsRef()) { auto ty1 = StaticCast(srcTy)->GetBaseType(); srcTy = ty1; } while (tgtTy->IsRef()) { auto ty1 = StaticCast(tgtTy)->GetBaseType(); tgtTy = ty1; } LocalVar* result = typecast->GetResult(); // Set an initial state if (result->GetType()->IsRef()) { state.GetReferencedObjAndSetToTop(result, typecast); } else { state.SetToTopOrTopRef(result, false); } if (!srcTy->IsClass() || !tgtTy->IsClass()) { return; } // Check whether sourceTy is a subclass of targetTy. auto checkSubClass = [this](ClassType* sourceTy, ClassType* targetTy) { auto fatherTy = LeastCommonSuperClass(sourceTy, targetTy, &builder); // Return the sourceTy, only if least common super class is targetTy. if (fatherTy == targetTy) { return sourceTy; } return targetTy; }; auto srcAbsVal = state.CheckAbstractObjectRefBy(typecast->GetSourceValue()); if (!srcAbsVal) { return; } auto srcVal = state.CheckAbstractValue(srcAbsVal); if (!srcVal) { return; } if (srcVal->GetTypeKind() == DevirtualTyKind::EXACTLY) { return state.Propagate(typecast->GetSourceValue(), result); } else if (srcVal->GetTypeKind() == DevirtualTyKind::SUBTYPE_OF) { ClassType* srcClsTy = StaticCast(srcTy); ClassType* tgtClsTy = StaticCast(tgtTy); ClassType* resTy = checkSubClass(srcClsTy, tgtClsTy); if (srcVal->GetSpecificType() == resTy) { return state.Propagate(typecast->GetSourceValue(), result); } else { Value* resVal = result; if (result->GetType()->IsRef()) { resVal = state.CheckAbstractObjectRefBy(result); } return state.Update(resVal, std::make_unique(DevirtualTyKind::SUBTYPE_OF, tgtClsTy)); } } } template static Type* GetInnerTypeFromGetElementRef(const MemberAccess& ma, CHIRBuilder& builder) { const auto& path = ma.GetPath(); auto locationType = ma.GetOperands()[0]->GetType(); for (size_t i = 0; i < path.size() - 1; ++i) { auto index = path[i]; locationType = GetFieldOfType(*locationType, index, builder); } return locationType; } static const CustomTypeDef* CheckMemberDefineInWhichParent(CustomType& type, size_t index) { auto def = type.GetCustomTypeDef(); auto memberInfo = def->GetInstanceVar(index); return memberInfo.outerDef == nullptr ? def : memberInfo.outerDef; } /// override value analysis, check if virtual member type has specific type. void TypeAnalysis::PreHandleGetElementRefExpr(TypeDomain& state, const GetElementRef* getElemRef) { auto locationType = GetInnerTypeFromGetElementRef(*getElemRef, builder)->StripAllRefs(); if (!locationType->IsClassOrStruct()) { return HandleDefaultExpr(state, getElemRef); } auto path = getElemRef->GetPath(); auto index = path[path.size() - 1]; if (path.size() == 1) { // %1: result type = GetElementRef(location obj, path) // when size is 1, the location type is location obj's type, this type may be analysed to a more accurate type. // when size is greater than 1, the location type is member var's type of location obj, we don't analysis it auto srcAbsVal = state.CheckAbstractObjectRefBy(getElemRef->GetLocation()); if (srcAbsVal) { auto srcVal = state.CheckAbstractValue(srcAbsVal); if (srcVal) { locationType = srcVal->GetSpecificType(); } } } auto customType = StaticCast(locationType); // get member define in which parent. auto def = CheckMemberDefineInWhichParent(*customType, index); auto it = constMemberTypeMap.find(def); if (it == constMemberTypeMap.end()) { return HandleDefaultExpr(state, getElemRef); } auto it2 = it->second.find(index); if (it2 == it->second.end()) { return HandleDefaultExpr(state, getElemRef); } auto instanceType = it2->second->StripAllRefs(); auto refObj = state.GetTwoLevelRefAndSetToTop(getElemRef->GetResult(), getElemRef); UpdateDefaultValue(state, getElemRef->GetResult(), refObj, instanceType); } /// override value analysis, check if virtual member type has specific type. void TypeAnalysis::PreHandleFieldExpr(TypeDomain& state, const Field* field) { auto locationType = GetInnerTypeFromGetElementRef(*field, builder)->StripAllRefs(); if (!locationType->IsClassOrStruct()) { return HandleDefaultExpr(state, field); } auto path = field->GetPath(); auto index = path[path.size() - 1]; if (path.size() == 1) { // %1: result type = Field(location obj, path) // when size is 1, the location type is location obj's type, this type may be analysed to a more accurate type. // when size is greater than 1, the location type is member var's type of location obj, we don't analysis it auto srcVal = state.CheckAbstractValue(field->GetBase()); if (srcVal) { locationType = srcVal->GetSpecificType(); } } auto customType = StaticCast(locationType); // get member define in which parent. auto def = CheckMemberDefineInWhichParent(*customType, index); auto it = constMemberTypeMap.find(def); if (it == constMemberTypeMap.end()) { return HandleDefaultExpr(state, field); } auto it2 = it->second.find(index); if (it2 == it->second.end()) { return HandleDefaultExpr(state, field); } auto instanceType = it2->second->StripAllRefs(); auto refObj = state.GetReferencedObjAndSetToTop(field->GetResult(), field); UpdateDefaultValue(state, field->GetResult(), refObj, instanceType); } void TypeAnalysis::HandleDefaultExpr(TypeDomain& state, const Expression* expr) const { auto refObj = PreHandleDefaultExpr(state, expr); UpdateDefaultValue(state, expr->GetResult(), refObj, nullptr); } Value* TypeAnalysis::PreHandleDefaultExpr(TypeDomain& state, const Expression* expr) const { auto result = expr->GetResult(); auto resType = result->GetType(); if (!resType->IsRef()) { state.SetToTopOrTopRef(result, false); return nullptr; } auto firstRefBaseType = StaticCast(resType)->GetBaseType(); if (!firstRefBaseType->IsRef()) { auto refObj = state.GetReferencedObjAndSetToTop(result, expr); return refObj; } auto refObj = state.GetTwoLevelRefAndSetToTop(result, expr); return refObj; } void TypeAnalysis::UpdateDefaultValue( TypeDomain& state, Value* value, Value* refObj, Type* relType) const { if (value == nullptr) { return; } auto dest = refObj ? refObj : value; auto type = relType == nullptr ? value->GetType()->StripAllRefs() : relType; if (type->IsBox()) { type = StaticCast(type)->GetBaseType(); } if (type->IsPrimitive() || type->IsStruct() || type->IsEnum()) { state.Update(dest, std::make_unique(DevirtualTyKind::EXACTLY, type)); } if (type->IsClass()) { auto classDef = StaticCast(type)->GetClassDef(); auto kind = !classDef->IsInterface() && !classDef->TestAttr(Attribute::VIRTUAL) && !classDef->IsAbstract() ? DevirtualTyKind::EXACTLY : DevirtualTyKind::SUBTYPE_OF; state.Update(dest, std::make_unique(kind, type)); } } void TypeAnalysis::HandleFuncParam(TypeDomain& state, Parameter* param, Value* refObj) { if (!refObj) { return; } UpdateDefaultValue(state, param, refObj, nullptr); } } // namespace Cangjie::CHIRcangjie_compiler-1.0.7/src/CHIR/Analysis/Utils.cpp000066400000000000000000000161201510705540100217120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Analysis/Utils.h" #include #include #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Utils.h" namespace Cangjie::CHIR { namespace { static std::unordered_map> g_superClassMap = {}; static std::mutex g_mtx; std::vector GetAllFathers(ClassType* clsTy, CHIRBuilder* builder) { std::unique_lock lock(g_mtx); if (g_superClassMap.count(clsTy) != 0) { return g_superClassMap[clsTy]; } std::queue tys; std::vector fathers; tys.push(clsTy); fathers.push_back(clsTy); while (!tys.empty()) { auto ty = tys.front(); tys.pop(); auto supers = ty->GetImplementedInterfaceTys(builder); std::for_each(supers.begin(), supers.end(), [&](auto super) { if (super) { tys.push(super); fathers.push_back(super); } }); auto super = ty->GetSuperClassTy(builder); if (super) { tys.push(super); fathers.push_back(super); } } g_superClassMap.emplace(clsTy, fathers); return fathers; } } // namespace bool IsUnsignedArithmetic(const BinaryExpression& expr) { auto ty = expr.GetLHSOperand()->GetType(); return ty->IsUnsignedInteger() || IsStructEnum(ty); } std::string GetRefName(size_t index) { return "Ref" + std::to_string(index); } std::string GetObjName(size_t index) { return "Obj" + std::to_string(index); } std::string GetObjChildName(std::string parentName, size_t fieldIdx) { return parentName + "." + std::to_string(fieldIdx); } Cangjie::Position ToPosition(const DebugLocation& loc) { return Cangjie::Position(loc.GetFileID(), static_cast(loc.GetBeginPos().line), static_cast(loc.GetBeginPos().column)); } std::string ToPosInfo(const DebugLocation& loc, bool isPrintFileName) { auto [line, column] = loc.GetBeginPos(); auto fileName = isPrintFileName ? Cangjie::FileUtil::GetFileName(loc.GetAbsPath()) + "," : ""; return " at [" + fileName + std::to_string(line) + "," + std::to_string(column) + "]"; } std::optional IsInitialisingMemberVar(const Func& func, const StoreElementRef& store) { auto location = store.GetLocation(); if ((func.IsConstructor() || func.GetFuncKind() == FuncKind::INSTANCEVAR_INIT) && location->IsParameter()) { auto& paths = store.GetPath(); // func->GetParam(0) is the `this` arugment. if (location == func.GetParam(0) && paths.size() == 1) { return paths[0]; } } return std::nullopt; } const Lambda* IsApplyToLambda(const Expression* expr) { CJC_NULLPTR_CHECK(expr); if (expr->GetExprKind() != ExprKind::APPLY && expr->GetExprKind() != ExprKind::APPLY_WITH_EXCEPTION) { return nullptr; } CJC_ASSERT(expr->GetNumOfOperands() > 0); if (!expr->GetOperand(0)->IsLocalVar()) { return nullptr; } auto callee = StaticCast(expr->GetOperand(0))->GetExpr(); if (callee->GetExprKind() != ExprKind::LAMBDA) { return nullptr; } return StaticCast(callee); } // Check if it is a getOrThrow Function bool IsGetOrThrowFunction(const Expression& expr) { static const FuncInfo GETORTHROWFUNCINFO{"getOrThrow", NOT_CARE, {NOT_CARE}, NOT_CARE, "std.core"}; if (expr.GetExprKind() != ExprKind::APPLY) { return false; } auto apply = StaticCast(&expr); auto callee = apply->GetCallee(); if (!callee->IsFunc()) { return false; } return IsExpectedFunction(*VirtualCast(callee), GETORTHROWFUNCINFO); } ClassType* LeastCommonSuperClass(ClassType* ty1, ClassType* ty2, CHIRBuilder* builder) { auto allFathers1 = GetAllFathers(ty1, builder); auto allFathers2 = GetAllFathers(ty2, builder); // In order to speed up efficiency of searching. std::unordered_set allFathersSet; allFathersSet.insert(allFathers1.begin(), allFathers1.end()); for (auto& ty : allFathers2) { if (allFathersSet.find(ty) != allFathersSet.end()) { return ty; } } return nullptr; } bool IsStructEnum(const Ptr& type) { if (type->IsEnum()) { return !static_cast(type.get())->GetEnumDef()->GetCtors().empty(); } return false; } bool IsRefEnum(const Ptr& type) { if (type->IsEnum()) { return static_cast(type.get())->GetEnumDef()->GetCtors().empty(); } return false; } Func* TryGetInstanceVarInitFromApply(const Expression& expr) { if (expr.GetExprKind() == ExprKind::APPLY) { auto applyExpr = StaticCast(&expr); auto callee = applyExpr->GetCallee(); if (callee->IsFuncWithBody()) { auto func = VirtualCast(callee); if (func->IsInstanceVarInit()) { return func; } } } return nullptr; } std::unordered_set GetLambdaCapturedVarsRecursively(const Lambda& lambda) { std::unordered_set allCapturedVars; std::unordered_set visited; std::function&)> collectRecursively = [&allCapturedVars, &collectRecursively](const Lambda& lambda, std::unordered_set& visited) { visited.emplace(&lambda); auto visitAction = [&visited, &collectRecursively](Expression& expr) { if (expr.IsApply()) { auto callee = DynamicCast(StaticCast(expr).GetCallee()); if (callee != nullptr && callee->GetExpr()->IsLambda()) { auto child = StaticCast(callee->GetExpr()); if (visited.find(child) == visited.end()) { collectRecursively(*child, visited); } } } else if (expr.IsApplyWithException()) { auto callee = DynamicCast(StaticCast(expr).GetCallee()); if (callee != nullptr && callee->GetExpr()->IsLambda()) { auto child = StaticCast(callee->GetExpr()); if (visited.find(child) == visited.end()) { collectRecursively(*child, visited); } } } else if (expr.IsLambda()) { collectRecursively(StaticCast(expr), visited); } return VisitResult::CONTINUE; }; Visitor::Visit(*lambda.GetBody(), visitAction); for (auto var : lambda.GetCapturedVariables()) { if (var->GetType()->IsRef()) { allCapturedVars.emplace(var); } } }; collectRecursively(lambda, visited); return allCapturedVars; } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/Analysis/ValueAnalysis.cpp000066400000000000000000000022101510705540100233650ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Analysis/ValueAnalysis.h" using namespace Cangjie::CHIR; template typename State::ChildrenMap ValueAnalysis::globalChildrenMap{}; template typename State::AllocatedRefMap ValueAnalysis::globalAllocatedRefMap{}; template typename State::AllocatedObjMap ValueAnalysis::globalAllocatedObjMap{}; template std::vector> ValueAnalysis::globalRefPool{}; template std::vector> ValueAnalysis::globalAbsObjPool{}; template State ValueAnalysis::globalState{ &globalChildrenMap, &globalAllocatedRefMap, &globalAllocatedObjMap, &globalRefPool, &globalAbsObjPool};cangjie_compiler-1.0.7/src/CHIR/Analysis/ValueDomain.cpp000066400000000000000000000051701510705540100230210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the abstract domain of CHIR value. */ #include "cangjie/CHIR/Analysis/ValueDomain.h" namespace Cangjie::CHIR { AbstractObject::AbstractObject(std::string identifier) : Value(nullptr, identifier, ValueKind::KIND_LOCALVAR) { } std::string AbstractObject::ToString() const { return identifier; } AbstractObject* AbstractObject::GetTopObjInstance() { static AbstractObject ins{"TopObj"}; return &ins; } bool AbstractObject::IsTopObjInstance() const { return this == GetTopObjInstance(); } Ref::Ref(std::string uniqueID, bool isStatic) : isStatic(isStatic), uniqueID(std::move(uniqueID)) { } std::string Ref::GetUniqueID() const { return isStatic ? "s" + uniqueID : uniqueID; } void Ref::AddRoots(Ref* r1, Ref* r2) { const auto add = [this](Ref* r) { if (r->roots.empty()) { roots.emplace(r); } else { roots.insert(r->roots.begin(), r->roots.end()); } }; add(r1); add(r2); } bool Ref::IsEquivalent(Ref* r) { return !roots.empty() && roots == r->roots; } bool Ref::CanRepresent(Ref* r) { if (auto cacheRes = CheckCache(r); cacheRes.has_value()) { return cacheRes.value(); } else { bool res; if (roots.empty()) { // this is a root ref res = false; } else if (r->roots.empty()) { // rhs is a root ref res = roots.find(r) != roots.end(); } else { const auto check = [this](const auto& x) { return roots.find(x) != roots.end(); }; res = r->roots.size() <= roots.size() && std::all_of(r->roots.begin(), r->roots.end(), check); } WriteCache(r, res); return res; } } std::optional Ref::CheckCache(Ref* r) { std::unique_lock guard(cacheMtx, std::defer_lock); if (isStatic) { guard.lock(); } if (auto it = cache.find(r); it != cache.end()) { return it->second; } else { return std::nullopt; } } void Ref::WriteCache(Ref* r, bool res) { std::unique_lock guard(cacheMtx, std::defer_lock); if (isStatic) { guard.lock(); } cache.emplace(r, res); } Ref* Ref::GetTopRefInstance() { static Ref ins{"TopRef", false}; return &ins; } bool Ref::IsTopRefInstance() const { return this == GetTopRefInstance(); } } // namespace Cangjie::CHIRcangjie_compiler-1.0.7/src/CHIR/Analysis/ValueRangeAnalysis.cpp000066400000000000000000000474601510705540100243620ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Analysis/ValueRangeAnalysis.h" #include #include "cangjie/CHIR/OverflowChecking.h" #include "cangjie/CHIR/Analysis/Arithmetic.h" namespace Cangjie::CHIR { ValueRange::ValueRange(RangeKind kind) : kind(kind) { } ValueRange::~ValueRange() { } ValueRange::RangeKind ValueRange::GetRangeKind() const { return kind; } BoolRange::BoolRange(BoolDomain domain) : ValueRange(RangeKind::BOOL), domain(std::move(domain)) { } std::optional> BoolRange::Join(const ValueRange& rhs) const { CJC_ASSERT(rhs.GetRangeKind() == RangeKind::BOOL); auto rhsRange = StaticCast(rhs); if (domain.IsSame(rhsRange.domain)) { return std::nullopt; } return std::make_unique(BoolRange{BoolDomain::Union(domain, rhsRange.domain)}); } std::string BoolRange::ToString() const { std::stringstream ss; ss << domain; return ss.str(); } std::unique_ptr BoolRange::Clone() const { return std::make_unique(domain); } const BoolDomain& BoolRange::GetVal() const { return domain; } SIntRange::SIntRange(SIntDomain domain) : ValueRange(RangeKind::SINT), domain(std::move(domain)) { } std::optional> SIntRange::Join(const ValueRange& rhs) const { CJC_ASSERT(rhs.GetRangeKind() == RangeKind::SINT); auto rhsRange = StaticCast(rhs); if (!domain.IsSame(rhsRange.domain) || (domain.NumericBound().IsFullSet() && !domain.SymbolicBounds().Empty())) { return std::make_unique(SIntRange{SIntDomain::Unions(domain, rhsRange.domain)}); } return std::nullopt; } std::string SIntRange::ToString() const { std::stringstream ss; ss << domain; return ss.str(); } std::unique_ptr SIntRange::Clone() const { return std::make_unique(domain); } const SIntDomain& SIntRange::GetVal() const { return domain; } template <> bool IsTrackedGV(const GlobalVar& gv) { auto baseTyKind = StaticCast(gv.GetType())->GetBaseType()->GetTypeKind(); return (baseTyKind >= Type::TYPE_INT8 && baseTyKind <= Type::TYPE_UINT_NATIVE) || baseTyKind == Type::TYPE_ENUM || baseTyKind == Type::TYPE_BOOLEAN; } template <> RangeValueDomain HandleNonNullLiteralValue(const LiteralValue* literal) { if (literal->IsBoolLiteral()) { return RangeValueDomain( std::make_unique(BoolDomain::FromBool(StaticCast(literal)->GetVal()))); } else if (literal->IsIntLiteral()) { return RangeValueDomain(std::make_unique(SIntDomain::From(*literal))); } else { return RangeValueDomain(true); } } RangeAnalysis::RangeAnalysis(const Func* func, CHIRBuilder& builder, bool isDebug, const Ptr& diag) : ValueAnalysis(func, builder, isDebug), diag(diag) { } RangeAnalysis::~RangeAnalysis() { } const int MAX_INQUEUE_TIMES = 4; bool CanAnalyse(const Ptr& type) { if (type->IsInteger() || type->IsBoolean()) { return true; } return false; } const SIntDomain& GetDefaultIntCache(const Ptr& ty) { constexpr int integerSize{4}; static SIntDomain signedRange[integerSize]{ {ConstantRange::Full(IntWidth::I8), false}, {ConstantRange::Full(IntWidth::I16), false}, {ConstantRange::Full(IntWidth::I32), false}, {ConstantRange::Full(IntWidth::I64), false}, }; static SIntDomain unsignedRange[integerSize]{ {ConstantRange::Full(IntWidth::I8), true}, {ConstantRange::Full(IntWidth::I16), true}, {ConstantRange::Full(IntWidth::I32), true}, {ConstantRange::Full(IntWidth::I64), true}, }; auto width{Ctz(static_cast(ToWidth(*ty)) / static_cast(CHAR_BIT))}; return ty->IsUnsignedInteger() ? unsignedRange[width] : signedRange[width]; } inline bool IsBasicBinaryExpr(const Expression& expr) { return expr.GetExprKind() >= ExprKind::ADD && expr.GetExprKind() <= ExprKind::MOD; } template <> const std::string Analysis::name = "range-analysis"; template <> const std::optional Analysis::blockLimit = 80; template <> RangeDomain::ChildrenMap ValueAnalysis::globalChildrenMap{}; template <> RangeDomain::AllocatedRefMap ValueAnalysis::globalAllocatedRefMap{}; template <> RangeDomain::AllocatedObjMap ValueAnalysis::globalAllocatedObjMap{}; template <> std::vector> ValueAnalysis::globalRefPool{}; template <> std::vector> ValueAnalysis::globalAbsObjPool{}; template <> RangeDomain ValueAnalysis::globalState{&globalChildrenMap, &globalAllocatedRefMap, nullptr, &globalAllocatedObjMap, &globalRefPool, &globalAbsObjPool}; BoolDomain RangeAnalysis::GetBoolDomainFromState(const RangeDomain& state, const Ptr& value) { if (!value->GetType()->IsBoolean()) { return BoolDomain::Top(); } auto domain = state.CheckAbstractValueWithTopBottom(value); if (domain == nullptr || domain->IsTop()) { return BoolDomain::Top(); } auto absVal = domain->CheckAbsVal(); if (absVal == nullptr || absVal->GetRangeKind() != ValueRange::RangeKind::BOOL) { return BoolDomain::Top(); } return StaticCast(absVal)->GetVal(); } const SIntDomain& RangeAnalysis::GetSIntDomainFromState(const RangeDomain& state, const Ptr& value) { CJC_ASSERT(value->GetType()->IsInteger()); auto domain = state.CheckAbstractValueWithTopBottom(value); if (domain == nullptr || domain->IsTop()) { return GetDefaultIntCache(value->GetType()); } auto absVal = domain->CheckAbsVal(); if (absVal == nullptr || absVal->GetRangeKind() != ValueRange::RangeKind::SINT) { return GetDefaultIntCache(value->GetType()); } return StaticCast(absVal)->GetVal(); } void RangeAnalysis::HandleNormalExpressionEffect(RangeDomain& state, const Expression* expression) { switch (expression->GetExprMajorKind()) { case ExprMajorKind::MEMORY_EXPR: return; case ExprMajorKind::UNARY_EXPR: HandleUnaryExpr(state, StaticCast(expression)); break; case ExprMajorKind::BINARY_EXPR: HandleBinaryExpr(state, StaticCast(expression)); break; case ExprMajorKind::OTHERS: HandleOthersExpr(state, expression); break; case ExprMajorKind::STRUCTURED_CTRL_FLOW_EXPR: default: { #ifndef NDEBUG CJC_ABORT(); #else return; #endif } } if (expression->GetExprMajorKind() != ExprMajorKind::UNARY_EXPR && expression->GetExprMajorKind() != ExprMajorKind::BINARY_EXPR && expression->GetExprKind() != ExprKind::TYPECAST) { return; } if (isDebug && !expression->GetResult()->GetType()->IsRef()) { if (expression->GetResult()->GetType()->IsBoolean()) { auto domain = GetBoolDomainFromState(state, expression->GetResult()); if (!domain.IsTop()) { PrintDebugMessage(expression, domain); } } else if (expression->GetResult()->GetType()->IsInteger()) { auto domain = GetSIntDomainFromState(state, expression->GetResult()); if (!domain.IsTop()) { PrintDebugMessage(expression, domain); } } } } BoolDomain RangeAnalysis::GenerateBoolRangeFromBinaryOp( RangeDomain& state, const Ptr& binaryExpr) const { auto lhs = binaryExpr->GetLHSOperand(); auto rhs = binaryExpr->GetRHSOperand(); if (lhs->GetType()->IsInteger()) { const auto& lRange = GetSIntDomainFromState(state, lhs); const auto& rRange = GetSIntDomainFromState(state, rhs); return ComputeRelIntBinop(CHIRRelIntBinopArgs{ lRange, rRange, lhs, rhs, binaryExpr->GetExprKind(), IsUnsignedArithmetic(*binaryExpr)}); } const auto& lRange = GetBoolDomainFromState(state, lhs); const auto& rRange = GetBoolDomainFromState(state, rhs); return ComputeEqualityBoolBinop(lRange, rRange, binaryExpr->GetExprKind()); } bool RangeAnalysis::CheckInQueueTimes(const Block* block, RangeDomain& curState) { if (inqueueTimes.count(block) == 0) { inqueueTimes[block] = 1; return false; } inqueueTimes[block]++; if (inqueueTimes.at(block) >= MAX_INQUEUE_TIMES) { curState.ClearState(); return true; } return false; } void RangeAnalysis::HandleUnaryExpr(RangeDomain& state, const UnaryExpression* unaryExpr) const { auto dest = unaryExpr->GetResult(); return state.SetToBound(dest, true); } std::string GenerateTypeRangePrompt(const Ptr& type) { const static std::unordered_map> TYPE_TO_RANGE = { {Type::TypeKind::TYPE_INT8, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_INT16, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_INT32, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_INT64, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_INT_NATIVE, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_UINT8, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_UINT16, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_UINT32, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_UINT64, {std::numeric_limits::min(), std::numeric_limits::max()}}, {Type::TypeKind::TYPE_UINT_NATIVE, {std::numeric_limits::min(), std::numeric_limits::max()}}}; auto [min, max] = TYPE_TO_RANGE.at(type->GetTypeKind()); return "range of " + type->ToString() + " is " + std::to_string(min) + " ~ " + std::to_string(max); } template void RaiseArithmeticOverflowError(const TBinary* expr, ExprKind kind, T leftVal, T rightVal, DiagAdapter& diag) { auto& loc = expr->GetDebugLocation(); auto ty = expr->GetResult()->GetType(); const static std::unordered_map OPS = { {ExprKind::ADD, "+"}, {ExprKind::SUB, "-"}, {ExprKind::MUL, "*"}, {ExprKind::DIV, "/"}, {ExprKind::MOD, "%"}, {ExprKind::EXP, "**"}, }; auto token = OPS.find(kind); CJC_ASSERT(token != OPS.end()); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::chir_arithmetic_operator_overflow, ToRange(loc), token->second); std::string hint = ty->ToString() + "(" + std::to_string(leftVal) + ") " + token->second + " " + expr->GetRHSOperand()->GetType()->ToString() + "(" + std::to_string(rightVal) + ")"; builder.AddMainHintArguments(hint); builder.AddNote(GenerateTypeRangePrompt(expr->GetResult()->GetType())); } template bool CheckDivZero(ExprKind exprKind, const Ptr& binary, T rVal, DiagAdapter& diag) { if (rVal == 0 && (exprKind == ExprKind::DIV || exprKind == ExprKind::MOD)) { auto& loc = binary->GetDebugLocation(); auto prompt = exprKind == ExprKind::DIV ? "divide" : "modulo"; auto builder = diag.DiagnoseRefactor(DiagKindRefactor::chir_divisor_is_zero, ToRange(loc), prompt); builder.AddMainHintArguments(prompt); return true; } return false; } SIntDomain CheckSingleValueOverflow( const CHIRArithmeticBinopArgs& args, const Ptr& expr, ExprKind exprKind, DiagAdapter& diag) { bool isOv = false; if (args.uns) { uint64_t a = args.ld.NumericBound().Lower().UVal(); uint64_t b = args.rd.NumericBound().Lower().UVal(); uint64_t res = 0; if (CheckDivZero(exprKind, expr, b, diag)) { return SIntDomain::Top(args.ld.Width(), true); } isOv = OverflowChecker::IsUIntOverflow(args.l->GetType()->GetTypeKind(), exprKind, a, b, args.ov, &res); if (isOv && args.ov == OverflowStrategy::THROWING) { RaiseArithmeticOverflowError(expr.get(), exprKind, a, b, diag); return SIntDomain::Top(args.ld.Width(), true); } return {ConstantRange{{args.ld.Width(), res}}, true}; } else { int64_t a = args.ld.NumericBound().Lower().SVal(); int64_t b = args.rd.NumericBound().Lower().SVal(); int64_t res = 0; if (CheckDivZero(exprKind, expr, b, diag)) { return SIntDomain::Top(args.ld.Width(), false); } isOv = OverflowChecker::IsIntOverflow(args.l->GetType()->GetTypeKind(), exprKind, a, b, args.ov, &res); if (isOv && args.ov == OverflowStrategy::THROWING) { RaiseArithmeticOverflowError(expr.get(), exprKind, a, b, diag); return SIntDomain::Top(args.ld.Width(), false); } return {ConstantRange{{args.ld.Width(), static_cast(res)}}, false}; } } void RangeAnalysis::HandleBinaryExpr(RangeDomain& state, const BinaryExpression* binaryExpr) { auto dest = binaryExpr->GetResult(); auto lhs = binaryExpr->GetLHSOperand(); auto rhs = binaryExpr->GetRHSOperand(); if (!CanAnalyse(dest->GetType()) || !CanAnalyse(lhs->GetType()) || !CanAnalyse(rhs->GetType())) { return state.SetToBound(binaryExpr->GetResult(), true); } if (dest->GetType()->IsInteger()) { if (!IsBasicBinaryExpr(*binaryExpr)) { return state.SetToBound(binaryExpr->GetResult(), true); } const auto& lRange = GetSIntDomainFromState(state, lhs); const auto& rRange = GetSIntDomainFromState(state, rhs); auto ov = binaryExpr->GetOverflowStrategy(); auto isUnsigned = IsUnsignedArithmetic(*binaryExpr); if (lRange.IsSingleValue() && rRange.IsSingleValue()) { auto domain = CheckSingleValueOverflow( CHIRArithmeticBinopArgs{lRange, rRange, lhs, rhs, binaryExpr->GetExprKind(), ov, isUnsigned}, binaryExpr, binaryExpr->GetExprKind(), *diag); state.Update(dest, std::make_unique(domain)); return; } auto res = ComputeArithmeticBinop( CHIRArithmeticBinopArgs{lRange, rRange, lhs, rhs, binaryExpr->GetExprKind(), ov, isUnsigned}); if (res.IsNonTrivial()) { return state.Update(dest, std::make_unique(res)); } } if (dest->GetType()->IsBoolean()) { auto res = GenerateBoolRangeFromBinaryOp(state, binaryExpr); if (res.IsNonTrivial()) { return state.Update(dest, std::make_unique(res)); } } state.SetToBound(binaryExpr->GetResult(), true); } SIntDomain RangeAnalysis::ComputeTypeCast(RangeDomain& state, PtrSymbol oldSymbol, const SIntDomain& v, IntWidth dstSize, bool dstUnsigned, OverflowStrategy ov) const { auto numericRange{ComputeTypeCastNumericBound(v, dstSize, dstUnsigned, ov)}; if (dstSize < v.Width() || v.IsUnsigned() || !dstUnsigned || ov == OverflowStrategy::SATURATING || numericRange.SMinValue().Slt({dstSize, 0u})) { return {numericRange, v.IsUnsigned()}; } // unsigned to signed, same width or larger width // in this special case, if we have a symbolic range a=0 && a>=0, // this range can be preserved SIntDomain::SymbolicBoundsMap mp{}; for (auto it = v.SymbolicBounds().Begin(); it != v.SymbolicBounds().End(); it++) { auto absVal = state.CheckAbstractValue(it->first); if (absVal != nullptr && absVal->GetRangeKind() == ValueRange::RangeKind::SINT) { auto range = StaticCast(absVal)->GetVal(); if (range.NumericBound().SMinValue().Sge(SInt{range.Width(), 0u})) { mp.emplace(it->first, NumericConversion(it->second, dstSize, false, false, OverflowStrategy::THROWING)); // this typecast can never wrap, pass THROWING for better // performance } } } mp.emplace(oldSymbol, ConstantRange{{dstSize, 0u}}); return SIntDomain{numericRange, std::move(mp), v.IsUnsigned()}; } void RangeAnalysis::HandleOthersExpr(RangeDomain& state, const Expression* expression) { switch (expression->GetExprKind()) { case ExprKind::TYPECAST: { HandleTypeCast(state, StaticCast(expression)); break; } case ExprKind::CONSTANT: case ExprKind::APPLY: case ExprKind::FIELD: return; case ExprKind::TUPLE: default: { auto dest = expression->GetResult(); return state.SetToTopOrTopRef(dest, dest->GetType()->IsRef()); } } } std::optional RangeAnalysis::HandleTerminatorEffect(RangeDomain& state, const Terminator* terminator) { RangeAnalysis::ExceptionKind res = ExceptionKind::NA; switch (terminator->GetExprKind()) { case ExprKind::GOTO: case ExprKind::EXIT: break; case ExprKind::BRANCH: return HandleBranchTerminator(state, StaticCast(terminator)); case ExprKind::MULTIBRANCH: return HandleMultiBranchTerminator(state, StaticCast(terminator)); case ExprKind::TYPECAST_WITH_EXCEPTION: res = HandleTypeCast(state, StaticCast(terminator)); break; case ExprKind::INT_OP_WITH_EXCEPTION: case ExprKind::INTRINSIC_WITH_EXCEPTION: default: { auto dest = terminator->GetResult(); if (dest) { state.SetToTopOrTopRef(dest, dest->GetType()->IsRef()); } break; } } if (res == ExceptionKind::SUCCESS) { return terminator->GetSuccessor(0); } else if (res == ExceptionKind::FAIL) { return terminator->GetSuccessor(1); } return std::nullopt; } void RangeAnalysis::PrintBranchOptMessage(const Ptr& expr, bool isTrueBlockRemained) const { std::string message = "[RangeAnalysis] The If Block" + ToPosInfo(expr->GetDebugLocation()) + " has been replace to the " + (isTrueBlockRemained ? "True Block" : "False Block") + "\n"; std::cout << message; } std::optional RangeAnalysis::HandleBranchTerminator(const RangeDomain& state, const Branch* branch) const { auto cond = branch->GetCondition(); const auto& condVal = GetBoolDomainFromState(state, cond); if (!condVal.IsSingleValue()) { return std::nullopt; } if (isDebug) { PrintBranchOptMessage(branch, condVal.IsTrue()); } return condVal.IsTrue() ? branch->GetTrueBlock() : branch->GetFalseBlock(); } std::optional RangeAnalysis::HandleMultiBranchTerminator( const RangeDomain& state, const MultiBranch* multi) const { auto cond = multi->GetCondition(); const auto& condVal = GetSIntDomainFromState(state, cond); if (!condVal.IsSingleValue()) { return std::nullopt; } auto val = condVal.NumericBound().Lower().UVal(); auto cases = multi->GetCaseVals(); for (size_t i = 0; i < cases.size(); ++i) { if (val == cases[i]) { return multi->GetSuccessor(i + 1); } } return multi->GetDefaultBlock(); } } // namespace Cangjie::CHIRcangjie_compiler-1.0.7/src/CHIR/Annotation.cpp000066400000000000000000000030001510705540100211320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Annotation.h" #include #include #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { std::string AnnotationMap::ToString() const { std::stringstream ss; ss << loc.ToString(); for (auto& pair : annotations) { auto str = pair.second->ToString(); if (str.empty()) { continue; } if (ss.str() != "") { ss << ", "; } ss << str; } return ss.str(); } std::string SkipCheck::ToString() { switch (kind) { case SkipKind::SKIP_DCE_WARNING: return "skip: dce warning"; case SkipKind::SKIP_FORIN_EXIT: return "skip: for-in exit"; case SkipKind::SKIP_VIC: return "skip: vic"; default: return ""; } } std::string WrappedRawMethod::ToString() { // WrappedRawMethod may be removed body when removeUnusedImported,do not form it. auto wrapMethod = dynamic_cast(rawMethod); if (wrapMethod != nullptr && !wrapMethod->GetBody()) { return ""; } return "wrapped raw method: " + rawMethod->GetIdentifier(); } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/Annotations/000077500000000000000000000000001510705540100206205ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CHIR/Annotations/AnnoFactoryInfo.h000066400000000000000000000024351510705540100240340ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_ANNOFACTORYINFO_H #define CANGJIE_CHIR_ANNOFACTORYINFO_H #include #include "cangjie/CHIR/Annotation.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { /// Each annotation object is translated to a global var for consteval requirements. /// This records all the translated global variables. struct AnnoFactoryInfo final : public Annotation { AnnoFactoryInfo() : value{} {} explicit AnnoFactoryInfo(std::vector values) : value{std::move(values)} {} std::unique_ptr Clone() override { return std::make_unique(value); } std::string ToString() override { std::stringstream gvs; gvs << "annoGVs:"; for (auto v : value) { gvs << v->GetIdentifier() << ','; } return gvs.str(); } static const std::vector& Extract(const AnnoFactoryInfo* label) { return label->value; } private: std::vector value; }; } #endif cangjie_compiler-1.0.7/src/CHIR/AttributeInfo.cpp000066400000000000000000000015061510705540100216100ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/AttributeInfo.h" #include #include using namespace Cangjie::CHIR; std::string AttributeInfo::ToString() const { std::stringstream ss; for (int attr = static_cast(Attribute::STATIC); attr < static_cast(Attribute::ATTR_END); attr++) { if (TestAttr(static_cast(attr))) { ss << "[" << ATTR_TO_STRING.at(static_cast(attr)) << "] "; } } return ss.str(); } void AttributeInfo::Dump() const { std::cout << ToString() << std::endl; }cangjie_compiler-1.0.7/src/CHIR/CHIR.cpp000066400000000000000000001627361510705540100175730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/CHIR.h" #include "cangjie/CHIR/Analysis/CallGraphAnalysis.h" #include "cangjie/CHIR/Analysis/DevirtualizationInfo.h" #include "cangjie/CHIR/CHIRPrinter.h" #include "cangjie/CHIR/Checker/ConstSafetyCheck.h" #include "cangjie/CHIR/Checker/UnreachableBranchCheck.h" #include "cangjie/CHIR/Checker/VarInitCheck.h" #include "cangjie/CHIR/GenerateVTable/GenerateVTable.h" #include "cangjie/CHIR/IRChecker.h" #include "cangjie/CHIR/Interpreter/ConstEval.h" #include "cangjie/CHIR/Serializer/CHIRDeserializer.h" #include "cangjie/CHIR/Serializer/CHIRSerializer.h" #include "cangjie/CHIR/Transformation/ArrayLambdaOpt.h" #include "cangjie/CHIR/Transformation/ArrayListConstStartOpt.h" #include "cangjie/CHIR/Transformation/BoxRecursionValueType.h" #include "cangjie/CHIR/Transformation/ClosureConversion.h" #include "cangjie/CHIR/Transformation/ConstPropagation.h" #include "cangjie/CHIR/Transformation/DeadCodeElimination.h" #include "cangjie/CHIR/Transformation/Devirtualization.h" #include "cangjie/CHIR/Transformation/FlatForInExpr.h" #include "cangjie/CHIR/Transformation/FunctionInline.h" #include "cangjie/CHIR/Transformation/GetRefToArrayElem.h" #include "cangjie/CHIR/Transformation/MarkClassHasInited.h" #include "cangjie/CHIR/Transformation/MergeBlocks.h" #include "cangjie/CHIR/Transformation/NoSideEffectMarker.h" #include "cangjie/CHIR/Transformation/RangePropagation.h" #include "cangjie/CHIR/Transformation/RedundantFutureRemoval.h" #include "cangjie/CHIR/Transformation/RedundantGetOrThrowElimination.h" #include "cangjie/CHIR/Transformation/RedundantLoadElimination.h" #include "cangjie/CHIR/Transformation/SanitizerCoverage.h" #include "cangjie/CHIR/Transformation/UnitUnify.h" #include "cangjie/CHIR/Transformation/UselessAllocateElimination.h" #include "cangjie/CHIR/Visitor/Visitor.h" #include "cangjie/Driver/TempFileManager.h" #include "cangjie/Utils/CheckUtils.h" #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "cangjie/MetaTransformation/MetaTransform.h" #endif #include "cangjie/Utils/ProfileRecorder.h" #include "NativeFFI/TypeCastCheck.h" namespace Cangjie::CHIR { static void FlattenEffectMap(OptEffectCHIRMap& effectMap) { auto mergeToEffectedNodes = [](std::unordered_set>& src, std::unordered_set>& patch) { for (auto node : patch) { src.emplace(node); } }; auto pushToStack = [](std::stack>& stack, std::unordered_set>& nodes) { for (auto n : nodes) { stack.push(n); } }; // flatten effect map, for example: // source effect map: // a -> b,c // b -> d,e // e -> f // after flattening: // a -> b,c,d,e,f // b -> d,e,f // e -> f OptEffectCHIRMap newEffectMap; for (auto& mapIt : effectMap) { newEffectMap.emplace(mapIt); std::stack> stack; pushToStack(stack, mapIt.second); while (!stack.empty()) { Ptr curNode = stack.top(); stack.pop(); auto it = effectMap.find(curNode); if (it != effectMap.end()) { mergeToEffectedNodes(newEffectMap[mapIt.first], it->second); pushToStack(stack, it->second); } } } effectMap = newEffectMap; } static bool IsDesugaredNoneStaticConstructor(const Value& value) { const auto func = DynamicCast(&value); if (func == nullptr) { return false; } return func->TestAttr(Attribute::COMPILER_ADD) && !func->TestAttr(Attribute::STATIC) && (func->GetFuncKind() == FuncKind::CLASS_CONSTRUCTOR || func->GetFuncKind() == FuncKind::STRUCT_CONSTRUCTOR); } static std::unordered_set GetNoneStaticMemberVars(const CustomTypeDef& def) { std::unordered_set vars; for (auto& info : def.GetDirectInstanceVars()) { vars.emplace(info.rawMangledName); } return vars; } static std::string GetRawMangledName(const Value& value) { if (auto func = DynamicCast(&value); func) { return func->GetRawMangledName(); } else { auto var = VirtualCast(&value); return var->GetRawMangledName(); } } static std::unordered_set GetRawMangledNameSet(const std::unordered_set>& set) { std::unordered_set result; for (const auto v : set) { auto name = GetRawMangledName(*v); if (!name.empty()) { result.emplace(name); } } return result; } static void UpdateOptEffectMapBecauseOfInit(OptEffectCHIRMap& oldMap, OptEffectStrMap& newMap) { auto mapIt = oldMap.begin(); while (mapIt != oldMap.end()) { std::unordered_set effectedNodePatch; auto effectedIt = mapIt->second.begin(); while (effectedIt != mapIt->second.end()) { if (IsDesugaredNoneStaticConstructor(**effectedIt)) { auto parentClass = VirtualCast(*effectedIt)->GetParentCustomTypeDef(); CJC_NULLPTR_CHECK(parentClass); effectedNodePatch.merge(GetNoneStaticMemberVars(*parentClass)); effectedIt = mapIt->second.erase(effectedIt); } else { ++effectedIt; } } if (!effectedNodePatch.empty()) { auto keyName = GetRawMangledName(*mapIt->first); if (!keyName.empty()) { newMap[keyName].merge(effectedNodePatch); } } if (IsDesugaredNoneStaticConstructor(*(mapIt->first))) { auto parentClass = VirtualCast(mapIt->first)->GetParentCustomTypeDef(); CJC_NULLPTR_CHECK(parentClass); auto effectedSet = GetRawMangledNameSet(mapIt->second); effectedSet.merge(effectedNodePatch); for (auto member : GetNoneStaticMemberVars(*parentClass)) { auto tmp = effectedSet; newMap[member].merge(tmp); } mapIt = oldMap.erase(mapIt); } else { ++mapIt; } } } static void DropOutIllegalString(OptEffectStrMap& newMap) { auto mapIt = newMap.begin(); while (mapIt != newMap.end()) { auto effectedIt = mapIt->second.begin(); while (effectedIt != mapIt->second.end()) { if ((*effectedIt).empty() || *effectedIt == mapIt->first || *effectedIt == "$BOX") { effectedIt = mapIt->second.erase(effectedIt); } else { ++effectedIt; } } if (mapIt->first.empty() || mapIt->first == "$BOX" || mapIt->second.empty()) { mapIt = newMap.erase(mapIt); } else { ++mapIt; } } } static void UpdateEffectMapToString(OptEffectCHIRMap& oldMap, OptEffectStrMap& newMap) { Utils::ProfileRecorder recorder("CHIR", "UpdateEffectMapToString"); FlattenEffectMap(oldMap); UpdateOptEffectMapBecauseOfInit(oldMap, newMap); for (auto& mapIt : oldMap) { auto keyName = GetRawMangledName(*mapIt.first); if (!keyName.empty()) { newMap[keyName].merge(GetRawMangledNameSet(mapIt.second)); } } DropOutIllegalString(newMap); } void ToCHIR::DumpCHIRDebug(const std::string& suffix, bool checkFlag) { if (!opts.chirDumpDebugMode && checkFlag) { return; } CJC_NULLPTR_CHECK(chirPkg); CJC_ASSERT(!CHIR_READABLE_FILE_EXTENSION.empty()); const std::string extension = CHIR_READABLE_FILE_EXTENSION.front() == '.' ? CHIR_READABLE_FILE_EXTENSION.substr(1) : CHIR_READABLE_FILE_EXTENSION; // do not dump when not debugging compute annotations if (!opts.computeAnnotationsDebug && isComputingAnnos) { return; } std::string fileName = std::to_string(debugFileIndex++) + "_"; if (isComputingAnnos) { fileName += "ComputeAnnotations_"; } fileName += suffix + "." + extension; std::string debugDir; if (FileUtil::IsDir(outputPath)) { debugDir = FileUtil::JoinPath(outputPath, chirPkg->GetName() + "_CHIR_Debug"); } else { debugDir = FileUtil::GetFileBase(outputPath) + "_CHIR_Debug"; } static bool checkDebugDir = false; if (!checkDebugDir) { if (FileUtil::FileExist(debugDir)) { for (auto file : FileUtil::GetAllFilesUnderCurrentPath(debugDir, extension)) { std::string fullPath = FileUtil::JoinPath(debugDir, file); FileUtil::Remove(fullPath); } } checkDebugDir = true; } std::string fullPath = FileUtil::JoinPath(debugDir, fileName); if (!FileUtil::FileExist(fullPath)) { FileUtil::CreateDirs(fullPath); } CHIRPrinter::PrintPackage(*chirPkg, fullPath); } void ToCHIR::DoClosureConversion() { Utils::ProfileRecorder recorder("CHIR", "ClosureConversion"); CJC_NULLPTR_CHECK(builder.GetObjectTy()->GetClassDef()); auto closure = ClosureConversion(*chirPkg, builder, opts, srcCodeImportedFuncs); closure.Convert(); uselessClasses = closure.GetUselessClassDef(); uselessLambda = closure.GetUselessLambda(); if (opts.enIncrementalCompilation) { ccOutFuncsRawMangle = closure.GetCCOutFuncsRawMangle(); } DumpCHIRDebug("ClosureConversion"); } void ToCHIR::UnreachableBlockReporter() { Utils::ProfileRecorder recorder("CHIR", "UnreachableBlockWarningReporter"); auto dce = CHIR::DeadCodeElimination(builder, diag, pkg.fullPackageName); dce.UnreachableBlockWarningReporter(*chirPkg, opts.GetJobs(), maybeUnreachable); } void ToCHIR::UnreachableBlockElimination() { Utils::ProfileRecorder recorder("CHIR Opt", "UnreachableBlockElimination"); auto dce = CHIR::DeadCodeElimination(builder, diag, pkg.fullPackageName); dce.UnreachableBlockElimination(*chirPkg, opts.chirDebugOptimizer); DumpCHIRDebug("UnreachableBlockElimination"); RunMergingBlocks("CHIR Opt", "MergingBlockAfterUnreachableBlock"); } void ToCHIR::RunMarkClassHasInited() { Utils::ProfileRecorder recorder("CHIR", "MarkClassHasInited"); MarkClassHasInited::RunOnPackage(*chirPkg, builder); DumpCHIRDebug("MarkClassHasInited"); } void ToCHIR::NothingTypeExprElimination() { Utils::ProfileRecorder recorder("CHIR Opt", "NothingTypeExprElimination"); auto dce = CHIR::DeadCodeElimination(builder, diag, pkg.fullPackageName); dce.NothingTypeExprElimination(*chirPkg, opts.chirDebugOptimizer); DumpCHIRDebug("NothingTypeExprElimination"); } void ToCHIR::UnreachableBranchReporter() { Utils::ProfileRecorder recorder("CHIR Opt", "UnreachableBranchReporter"); auto check = CHIR::UnreachableBranchCheck(&constAnalysisWrapper, diag, pkg.fullPackageName); check.RunOnPackage(*chirPkg, opts.GetJobs()); } void ToCHIR::UselessExprElimination() { if (!opts.IsCHIROptimizationLevelOverO2()) { return; } Utils::ProfileRecorder recorder("CHIR Opt", "UselessExprElimination"); auto dce = CHIR::DeadCodeElimination(builder, diag, pkg.fullPackageName); dce.UselessExprElimination(*chirPkg, opts.chirDebugOptimizer); DumpCHIRDebug("UselessExprElimination"); } void ToCHIR::UselessFuncElimination() { Utils::ProfileRecorder recorder("CHIR Opt", "UselessFuncElimination"); auto dce = CHIR::DeadCodeElimination(builder, diag, pkg.fullPackageName); dce.UselessFuncElimination(*chirPkg, opts); DumpCHIRDebug("UselessFuncElimination"); } void ToCHIR::ReportUnusedCode() { Utils::ProfileRecorder recorder("CHIR Opt", "ReportUnusedCode"); auto dce = CHIR::DeadCodeElimination(builder, diag, pkg.fullPackageName); dce.ReportUnusedCode(*chirPkg, opts); } void ToCHIR::RedundantLoadElimination() { if (!opts.IsOptimizationExisted(GlobalOptions::OptimizationFlag::REDUNDANT_LOAD)) { return; } Utils::ProfileRecorder recorder("CHIR Opt", "RedundantLoadElimination"); auto rle = CHIR::RedundantLoadElimination(); rle.RunOnPackage(chirPkg, opts.chirDebugOptimizer); DumpCHIRDebug("RedundantLoadElimination"); } void ToCHIR::UselessAllocateElimination() { if (!opts.IsCHIROptimizationLevelOverO2()) { return; } Utils::ProfileRecorder recorder("CHIR Opt", "UselessAllocateElimination"); UselessAllocateElimination::RunOnPackage(*chirPkg, opts.chirDebugOptimizer); DumpCHIRDebug("UselessAllocateElimination"); } void ToCHIR::RunGetRefToArrayElemOpt() { if (!opts.IsCHIROptimizationLevelOverO2() || opts.interpFullBchir) { return; } Utils::ProfileRecorder recorder("CHIR Opt", "ArrayGetRefOpt"); GetRefToArrayElem::RunOnPackage(*chirPkg, builder); DumpCHIRDebug("ArrayGetRefOpt"); } void ToCHIR::Devirtualization(DevirtualizationInfo& devirtInfo) { if (!opts.IsOptimizationExisted(GlobalOptions::OptimizationFlag::DEVIRTUALIZATION) || opts.enIncrementalCompilation) { return; } Utils::ProfileRecorder recorder("CHIR Opt", "Devirtualization"); devirtInfo.FreshRetMap(); devirtInfo.CollectConstMemberVarType(); auto funcs = CHIR::Devirtualization::CollectContainInvokeExprFuncs(chirPkg); // Only process functions containing invoke expressions. if (funcs.empty()) { return; } size_t threadNum = opts.GetJobs(); TypeValue::SetCHIRBuilder(&builder); AnalysisWrapper typeAnalysisWrapper(builder); typeAnalysisWrapper.RunOnPackage(chirPkg, opts.chirDebugOptimizer, threadNum, devirtInfo); auto devirt = CHIR::Devirtualization(&typeAnalysisWrapper, devirtInfo); devirt.RunOnFuncs(funcs, builder, opts.chirDebugOptimizer); // if get frozen inst funcs after first devirtualization, opt them in the second round if (!devirt.GetFrozenInstFuns().empty()) { auto& frozenFuncs = devirt.GetFrozenInstFuns(); for (auto& func : frozenFuncs) { auto res = typeAnalysisWrapper.RunOnFunc(func, opts.chirDebugOptimizer, devirtInfo); devirt.AppendFrozenFuncState(func, std::move(res)); } devirt.RunOnFuncs(frozenFuncs, builder, opts.chirDebugOptimizer); auto pass = FunctionInline( builder, opts.optimizationLevel, opts.chirDebugOptimizer); for (auto& func : frozenFuncs) { pass.Run(*func); } } DumpCHIRDebug("Devirtualization"); } void ToCHIR::RedundantGetOrThrowElimination() { if (!opts.enableChirRGetOrThrowE) { return; } Utils::ProfileRecorder recorder("CHIR Opt", "RedundantGetOrThrowElimination"); auto rGetOrThrowE = CHIR::RedundantGetOrThrowElimination(); rGetOrThrowE.RunOnPackage(chirPkg, opts.chirDebugOptimizer); DumpCHIRDebug("RedundantGetOrThrowElimination"); } void ToCHIR::FlatForInExpr() { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND Utils::ProfileRecorder recorder("CHIR", "FlatForInExpr"); auto flatForInExpr = CHIR::FlatForInExpr(builder); flatForInExpr.RunOnPackage(*chirPkg); DumpCHIRDebug("FlatForInExpr"); #endif } bool ToCHIR::RunVarInitChecking() { Utils::ProfileRecorder recorder("CHIR Opt", "VarInitCheck"); auto vic = CHIR::VarInitCheck(&diag); vic.RunOnPackage(chirPkg, opts.GetJobs()); return diag.GetErrorCount() == 0; } bool ToCHIR::RunNativeFFIChecks() { Utils::ProfileRecorder recorder("CHIR", "NativeFFIChecks"); auto checker = CHIR::NativeFFI::TypeCastCheck(diag); checker.RunOnPackage(*chirPkg, opts.GetJobs()); return diag.GetErrorCount() == 0; } void ToCHIR::RunArrayListConstStartOpt() { if (!opts.IsCHIROptimizationLevelOverO2()) { return; } Utils::ProfileRecorder recorder("CHIR Opt", "ArrayListOpt"); auto functionInline = FunctionInline(builder, opts.optimizationLevel, opts.chirDebugOptimizer); auto pass = ArrayListConstStartOpt(builder, opts, functionInline); pass.RunOnPackage(chirPkg); MergeEffectMap(pass.GetEffectMap(), effectMap); DumpCHIRDebug("RunArrayListConstStartOpt"); } void ToCHIR::RunFunctionInline(DevirtualizationInfo& devirtInfo) { if (!opts.IsOptimizationExisted(GlobalOptions::OptimizationFlag::FUNC_INLINING)) { return; } Utils::ProfileRecorder::Start("CHIR Opt", "FunctionInline"); CallGraphAnalysis callGraphAnalysis(chirPkg, devirtInfo); // Collect all call graph information. callGraphAnalysis.DoCallGraphAnalysis(opts.chirDebugOptimizer); auto pass = FunctionInline(builder, opts.optimizationLevel, opts.chirDebugOptimizer); for (auto func : callGraphAnalysis.postOrderSCCFunctionlist) { if (!func) { continue; } // 1. $toAny is a function needed by reflection, it's used by runtime and it mustn't be optimized // 2. annotation factory functions mustn't be optimized if (func->GetSrcCodeIdentifier() == "$toAny" || func->GetFuncKind() == FuncKind::ANNOFACTORY_FUNC) { continue; } pass.Run(*func); } MergeEffectMap(pass.GetEffectMap(), effectMap); Utils::ProfileRecorder::Stop("CHIR Opt", "FunctionInline"); DumpCHIRDebug("FunctionInline"); RunMergingBlocks("CHIR Opt", "MergingBlockAfterInline"); // do useless function elimination after inline UselessFuncElimination(); } void ToCHIR::RunUnreachableMarkBlockRemoval() { Utils::ProfileRecorder recorder("CHIR", "Clear Blocks Marked as Unreachable"); auto dce = CHIR::DeadCodeElimination(builder, diag, pkg.fullPackageName); dce.ClearUnreachableMarkBlock(*chirPkg); DumpCHIRDebug("ClearBlocksMarkAsUnreachable"); } void ToCHIR::RunMergingBlocks(const std::string& firstName, const std::string& secondName) { Utils::ProfileRecorder recorder(firstName, secondName); MergeBlocks::RunOnPackage(*chirPkg, builder, opts); DumpCHIRDebug(secondName); } void ToCHIR::RunConstantAnalysis() { Utils::ProfileRecorder recorder("CHIR Opt", "Constant Analysis"); constAnalysisWrapper.RunOnPackage(chirPkg, opts.chirDebugOptimizer, opts.GetJobs(), &diag); } bool ToCHIR::RunConstantPropagation() { Utils::ProfileRecorder recorder("CHIR Opt", "Constant Propagation & Safety Check"); size_t threadNum = opts.GetJobs(); DeadCodeElimination dce(builder, diag, pkg.fullPackageName); if (threadNum == 1) { auto cp = CHIR::ConstPropagation(builder, &constAnalysisWrapper, opts); cp.RunOnPackage(chirPkg, opts.chirDebugOptimizer, ci.isCJLint); MergeEffectMap(cp.GetEffectMap(), effectMap); dce.UnreachableBlockElimination(cp.GetFuncsNeedRemoveBlocks(), opts.chirDebugOptimizer); } else { bool isDebug = opts.chirDebugOptimizer; bool isCJLint = ci.isCJLint; std::vector globalFuncs = chirPkg->GetGlobalFuncs(); size_t funcNum = globalFuncs.size(); std::vector> builderList = ConstructSubBuilders(threadNum, funcNum); Utils::TaskQueue taskQueue(threadNum); std::vector> cpList; for (size_t idx = 0; idx < funcNum; ++idx) { auto func = globalFuncs.at(idx); auto cp = std::make_unique(*builderList[idx], &constAnalysisWrapper, opts); taskQueue.AddTask([constPropagation = cp.get(), func, isDebug, isCJLint]() { return constPropagation->RunOnFunc(func, isDebug, isCJLint); }); cpList.emplace_back(std::move(cp)); } taskQueue.RunAndWaitForAllTasksCompleted(); for (auto& cp : cpList) { MergeEffectMap(cp->GetEffectMap(), effectMap); dce.UnreachableBlockElimination(cp->GetFuncsNeedRemoveBlocks(), opts.chirDebugOptimizer); } for (auto& subBd : builderList) { (*subBd).MergeAllocatedInstance(); } builder.GetChirContext().MergeTypes(); } DumpCHIRDebug("ConstantPropagation"); return diag.GetErrorCount() == 0; } bool ToCHIR::RunConstantPropagationAndSafetyCheck() { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND return RunConstantPropagation(); #endif } void ToCHIR::RunRangePropagation() { if (!opts.IsCHIROptimizationLevelOverO2()) { return; } Utils::ProfileRecorder recorder("CHIR Opt", "Range Propagation"); AnalysisWrapper vra(builder); vra.RunOnPackage(chirPkg, opts.chirDebugOptimizer, opts.GetJobs(), &diag); size_t threadNum = opts.GetJobs(); DeadCodeElimination dce(builder, diag, pkg.fullPackageName); if (threadNum == 1) { auto cp = CHIR::RangePropagation(builder, &vra, &diag, opts.enIncrementalCompilation); cp.RunOnPackage(chirPkg, opts.chirDebugOptimizer); MergeEffectMap(cp.GetEffectMap(), effectMap); dce.UnreachableBlockElimination(cp.GetFuncsNeedRemoveBlocks(), opts.chirDebugOptimizer); } else { bool isDebug = opts.chirDebugOptimizer; std::vector globalFuncs = chirPkg->GetGlobalFuncs(); size_t funcNum = globalFuncs.size(); std::vector> builderList = CHIR::ToCHIR::ConstructSubBuilders(threadNum, funcNum); Utils::TaskQueue taskQueue(threadNum); std::vector> cpList; for (size_t idx = 0; idx < funcNum; ++idx) { auto func = globalFuncs.at(idx); auto cp = std::make_unique( *builderList[idx], &vra, &diag, opts.enIncrementalCompilation); taskQueue.AddTask( [rangePropagation = cp.get(), func, isDebug]() { return rangePropagation->RunOnFunc(func, isDebug); }); cpList.emplace_back(std::move(cp)); } taskQueue.RunAndWaitForAllTasksCompleted(); for (auto& cp : cpList) { MergeEffectMap(cp->GetEffectMap(), effectMap); dce.UnreachableBlockElimination(cp->GetFuncsNeedRemoveBlocks(), opts.chirDebugOptimizer); } for (auto& subBd : builderList) { (*subBd).MergeAllocatedInstance(); } builder.GetChirContext().MergeTypes(); } DumpCHIRDebug("RangePropagation"); } void ToCHIR::RunArrayLambdaOpt() { if (!opts.IsOptimizationExisted(GlobalOptions::OptimizationFlag::ARRAY_LAMBDA_OPT)) { return; } Utils::ProfileRecorder recorder("CHIR Opt", "ArrayLambdaOpt"); auto arrayLambdaOpt = CHIR::ArrayLambdaOpt(builder); arrayLambdaOpt.RunOnPackage(chirPkg, opts.chirDebugOptimizer); DumpCHIRDebug("ArrayLambdaOpt"); } void ToCHIR::RunRedundantFutureOpt() { if (!opts.IsOptimizationExisted(GlobalOptions::OptimizationFlag::REDUNDANT_FUTURE)) { return; } Utils::ProfileRecorder recorder("CHIR Opt", "RedundantFutureOpt"); RedundantFutureRemoval(*chirPkg, opts.chirDebugOptimizer).RunOnPackage(); DumpCHIRDebug("RedundantFutureOpt"); } void ToCHIR::RunSanitizerCoverage() { if (!opts.IsSancovOptionEnabled()) { return; } Utils::ProfileRecorder recorder("CHIR", "Sanitizer Coverage"); auto sanCovOpt = CHIR::SanitizerCoverage(opts, builder); sanCovOpt.RunOnPackage(chirPkg, diag, opts.chirDebugOptimizer); DumpCHIRDebug("Sanitizer_Coverage"); } void ToCHIR::RunNoSideEffectMarkerOpt() { Utils::ProfileRecorder recorder("CHIR Opt", "No Side Effect Marker"); CHIR::NoSideEffectMarker::RunOnPackage(chirPkg, opts.chirDebugOptimizer); DumpCHIRDebug("No_Side_Effect_Marker"); } void ToCHIR::RunUnitUnify() { if (!opts.IsCHIROptimizationLevelOverO2()) { return; } Utils::ProfileRecorder recorder("CHIR Opt", "Unit Unify"); auto unitUnify = CHIR::UnitUnify(builder); unitUnify.RunOnPackage(chirPkg, opts.chirDebugOptimizer); DumpCHIRDebug("Unit_Unify"); } DevirtualizationInfo ToCHIR::CollectDevirtualizationInfo() { DevirtualizationInfo devirtInfo(chirPkg, opts); if (opts.IsOptimizationExisted(GlobalOptions::OptimizationFlag::FUNC_INLINING) || (opts.IsOptimizationExisted(GlobalOptions::OptimizationFlag::DEVIRTUALIZATION) && !opts.enIncrementalCompilation)) { // Collect all inheritance tree information. Utils::ProfileRecorder::Start("CHIR Opt", "Collect Devirt Info"); devirtInfo.CollectInfo(); Utils::ProfileRecorder::Stop("CHIR Opt", "Collect Devirt Info"); } return devirtInfo; } void ToCHIR::MarkNoSideEffect() { Utils::ProfileRecorder recorder("CHIR Opt", "MarkNoSideEffect"); static const std::unordered_set WHITE_LIST = { "8std.math5roundFd$$d", "8std.core6StringS2==F6String$$b", "8std.core7Extend!l<:8HashableX8hashCodeF$$l", "8std.core7Extend!d<:8HashableX8hashCodeF$$l", "8std.core6StringS7compareF6String$$8Ordering", "8std.core7Extend!8Ordering<:10Comparable<8Ordering>X7compareF8Ordering$$8Ordering" }; for (auto func : chirPkg->GetGlobalFuncs()) { if (WHITE_LIST.find(func->GetRawMangledName()) != WHITE_LIST.end()) { func->EnableAttr(Attribute::NO_SIDE_EFFECT); } } for (Value* value : chirPkg->GetImportedVarAndFuncs()) { if (value->IsImportedVar()) { continue; } if (WHITE_LIST.find(VirtualCast(value)->GetRawMangledName()) != WHITE_LIST.end()) { value->EnableAttr(Attribute::NO_SIDE_EFFECT); } } DumpCHIRDebug("MarkNoSideEffect"); } bool ToCHIR::RunOptimizationPassAndRulesChecking() { Utils::ProfileRecorder recorder("CHIR", "CHIR Opt"); NothingTypeExprElimination(); RunConstantAnalysis(); if (!RunVarInitChecking()) { return false; } if (!RunNativeFFIChecks()) { return false; } UnreachableBranchReporter(); // this instantance of block elimination is to maintain dead code warnings UnreachableBlockElimination(); ReportUnusedCode(); RunArrayListConstStartOpt(); if (!RunConstantPropagationAndSafetyCheck()) { return false; } UnreachableBlockElimination(); UselessFuncElimination(); UselessExprElimination(); UselessFuncElimination(); UselessExprElimination(); MarkNoSideEffect(); RunUnitUnify(); auto devirtInfo = CollectDevirtualizationInfo(); RunFunctionInline(devirtInfo); RedundantLoadElimination(); RedundantGetOrThrowElimination(); RunRangePropagation(); RunMergingBlocks("CHIR Opt", "MergingBlockAfterRangeAnalysis"); UselessAllocateElimination(); Devirtualization(devirtInfo); RunArrayLambdaOpt(); RunRedundantFutureOpt(); RunNoSideEffectMarkerOpt(); RunGetRefToArrayElemOpt(); return true; } bool ToCHIR::RunConstantEvaluation() { if (!opts.IsConstEvalEnabled() || opts.enIncrementalCompilation || opts.commonPartCjo.has_value()) { return true; } Utils::ProfileRecorder recorder("CHIR", "Constant Evaluation"); auto ce = CHIR::Interpreter::ConstEvalPass(ci, builder, sourceManager, opts, diagEngine); std::vector bchirPackages; ce.RunOnPackage(*chirPkg, initFuncsForConstVar, bchirPackages); DumpCHIRDebug("after_const_eval"); return true; } bool ToCHIR::RunIRChecker(const Phase& phase) { if (!opts.chirWFC) { return true; } // skip CJMP common package if (opts.experimentalMode && opts.outputMode == GlobalOptions::OutputMode::CHIR) { return true; } std::string suffix; switch (phase) { case Phase::RAW: suffix = "after translation"; break; case Phase::OPT: suffix = "after compiler optimization"; break; case Phase::PLUGIN: suffix = "after perform pulgin"; break; case Phase::ANALYSIS_FOR_CJLINT: suffix = "after analysis for cjlint"; break; } Utils::ProfileRecorder recorder("CHIR", "IRCheck " + suffix); CJC_NULLPTR_CHECK(chirPkg); return IRCheck(*chirPkg, opts, builder, phase); } void ToCHIR::RecordCodeInfoAtTheBegin() { if (!opts.enableTimer && !opts.enableMemoryCollect) { return; } Utils::ProfileRecorder recorder("CHIR", "RecordCodeInfo"); std::function getASTNodeQuantity = [this]() -> int64_t { int64_t astNodeCnt = 0; AST::Walker(&pkg, [&astNodeCnt](auto /* node */) { astNodeCnt++; return AST::VisitAction::WALK_CHILDREN; }).Walk(); return astNodeCnt; }; Utils::ProfileRecorder::RecordCodeInfo("AST node", getASTNodeQuantity); Utils::ProfileRecorder::RecordCodeInfo( "generic ins func in AST", static_cast(pkg.genericInstantiatedDecls.size())); std::function getGenericInstantiatedAstNode = [this]() -> int64_t { int64_t astNodeCnt = 0; for (auto& decl : pkg.genericInstantiatedDecls) { AST::Walker(decl.get(), [&astNodeCnt](auto /* node */) { astNodeCnt++; return AST::VisitAction::WALK_CHILDREN; }).Walk(); } return astNodeCnt; }; Utils::ProfileRecorder::RecordCodeInfo("generic ins ast node", getGenericInstantiatedAstNode); Utils::ProfileRecorder::RecordCodeInfo( "import pkg", static_cast(importManager.GetAllImportedPackages(true).size())); Utils::ProfileRecorder::RecordCodeInfo("src file", static_cast(pkg.files.size())); Utils::ProfileRecorder::RecordCodeInfo( "global func in CHIR after trans", static_cast(chirPkg->GetGlobalFuncs().size())); Utils::ProfileRecorder::RecordCodeInfo("global var in CHIR", static_cast(chirPkg->GetGlobalVars().size())); int64_t funcInlineCnt = std::count_if( pkg.inlineFuncDecls.begin(), pkg.inlineFuncDecls.end(), [](auto func) { return func && func->isInline; }); Utils::ProfileRecorder::RecordCodeInfo("imported inline func", funcInlineCnt); std::function getCurPkgInstantiatedAstNode = [this]() -> int64_t { int64_t astNodeCnt = 0; for (auto& decl : pkg.genericInstantiatedDecls) { auto genericDecl = decl->genericDecl; CJC_NULLPTR_CHECK(genericDecl->curFile); if (genericDecl->curFile->curPackage->fullPackageName != pkg.fullPackageName) { continue; } AST::Walker(decl.get(), [&astNodeCnt](auto /* node */) { astNodeCnt++; return AST::VisitAction::WALK_CHILDREN; }).Walk(); } return astNodeCnt; }; Utils::ProfileRecorder::RecordCodeInfo("cur pkg generic ins ast node", getCurPkgInstantiatedAstNode); } void ToCHIR::RecordCodeInfoAtTheEnd() { if (!opts.enableTimer && !opts.enableMemoryCollect) { return; } Utils::ProfileRecorder recorder("CHIR", "RecordCodeInfo"); Utils::ProfileRecorder::RecordCodeInfo("all CHIR node", static_cast(builder.GetAllNodesNum())); Utils::ProfileRecorder::RecordCodeInfo("all CHIR type", static_cast(builder.GetTypesNum())); Utils::ProfileRecorder::RecordCodeInfo( "global func after CHIR stage", static_cast(chirPkg->GetGlobalFuncs().size())); int64_t wrapperFuncNum = 0; int64_t funcExprNum = 0; int64_t wrapperFuncExprNum = 0; for (auto func : chirPkg->GetGlobalFuncs()) { if (!func->GetBody()) { continue; } int64_t exprNum = 0; for (auto b : func->GetBody()->GetBlocks()) { exprNum += static_cast(b->GetExpressions().size()); } funcExprNum += exprNum; if (func->Get() != nullptr) { wrapperFuncNum++; wrapperFuncExprNum += exprNum; } } Utils::ProfileRecorder::RecordCodeInfo("wrapper func after CHIR stage", wrapperFuncNum); Utils::ProfileRecorder::RecordCodeInfo("expr num in global func after CHIR stage", funcExprNum); Utils::ProfileRecorder::RecordCodeInfo("expr num in wrapper func after CHIR stage", wrapperFuncExprNum); } void ToCHIR::RecordCHIRExprNum(const std::string& suffix) { if (!opts.enableTimer && !opts.enableMemoryCollect) { return; } Utils::ProfileRecorder recorder("CHIR", "RecordCodeInfo"); Utils::ProfileRecorder::RecordCodeInfo( "valid CHIR func num after " + suffix, static_cast(chirPkg->GetGlobalFuncs().size())); int64_t allNonGenericExprsNum = 0; int64_t allInstantiatedExprsNum = 0; int64_t curPkgInstantiatedExprsNum = 0; for (auto func : chirPkg->GetGlobalFuncs()) { auto exprsNum = func->GetExpressionsNum(); allNonGenericExprsNum += static_cast(exprsNum); if (func->GetGenericDecl() == nullptr) { continue; } if (func->TestAttr(Attribute::GENERIC_INSTANTIATED) || (func->GetOuterDeclaredOrExtendedDef() && func->GetOuterDeclaredOrExtendedDef()->TestAttr(Attribute::GENERIC_INSTANTIATED))) { allInstantiatedExprsNum += static_cast(exprsNum); if (func->GetGenericDecl() && !func->GetGenericDecl()->TestAttr(Attribute::IMPORTED)) { curPkgInstantiatedExprsNum += static_cast(exprsNum); } } } Utils::ProfileRecorder::RecordCodeInfo("valid CHIR expr num after " + suffix, allNonGenericExprsNum); Utils::ProfileRecorder::RecordCodeInfo("valid CHIR generic ins expr num after " + suffix, allInstantiatedExprsNum); Utils::ProfileRecorder::RecordCodeInfo( "valid CHIR cur pkg generic ins expr num after " + suffix, curPkgInstantiatedExprsNum); int64_t allGenericExprsNum = 0; int64_t curPkgGenericExprsNum = 0; int64_t allGenericFuncNum = 0; int64_t curPkgGenericFuncNum = 0; for (auto func : chirPkg->GetGlobalFuncs()) { if (!func->IsInGenericContext()) { continue; } auto exprsNum = func->GetExpressionsNum(); allGenericExprsNum += static_cast(exprsNum); allGenericFuncNum++; if (!func->TestAttr(Attribute::IMPORTED)) { curPkgGenericExprsNum += static_cast(exprsNum); curPkgGenericFuncNum++; } } Utils::ProfileRecorder::RecordCodeInfo("valid CHIR generic func num after " + suffix, allGenericFuncNum); Utils::ProfileRecorder::RecordCodeInfo("valid CHIR generic expr num after " + suffix, allGenericExprsNum); Utils::ProfileRecorder::RecordCodeInfo("valid CHIR cur pkg generic func num after " + suffix, curPkgGenericFuncNum); Utils::ProfileRecorder::RecordCodeInfo( "valid CHIR cur pkg generic expr num after " + suffix, curPkgGenericExprsNum); } bool ToCHIR::RunAnalysisForCJLint() { Utils::ProfileRecorder recorder("CHIR", "CHIR Opt"); NothingTypeExprElimination(); RunConstantAnalysis(); if (!RunVarInitChecking()) { return false; } if (!RunNativeFFIChecks()) { return false; } UnreachableBlockElimination(); if (RunConstantPropagationAndSafetyCheck()) { constAnalysisWrapper.InvalidateAllAnalysisResults(); RunConstantAnalysis(); return true; } return false; } void ToCHIR::EraseDebugExpr() { Utils::ProfileRecorder recorder("CHIR", "EraseUselessDebugExpr"); // Erase useless debug expressions for codegen in not -g mode, // For the reason of error when enable parallel mode in codegen. // These expressions are needed for report warning, so only can be removed at the end of CHIR stage. for (auto func : chirPkg->GetGlobalFuncs()) { for (auto block : func->GetBody()->GetBlocks()) { auto exprs = block->GetExpressions(); for (size_t i = 0; i < exprs.size(); i++) { if (exprs[i]->GetExprKind() == ExprKind::DEBUGEXPR && (exprs[i]->GetOperand(0)->IsGlobal() || exprs[i]->GetOperand(0)->IsCompileTimeValue())) { exprs[i]->RemoveSelfFromBlock(); } } } } DumpCHIRDebug("EraseUselessDebugExpr"); } void ToCHIR::CFFIFuncWrapper() { Utils::ProfileRecorder recorder("CHIR", "CFFIFuncWrapper"); std::vector cfuncs; std::vector foreignFuncs; for (auto curFunc : chirPkg->GetGlobalFuncs()) { if (curFunc->GetType()->IsCFunc()) { cfuncs.emplace_back(curFunc); } } for (auto foreignFunc : chirPkg->GetImportedVarAndFuncs()) { if (foreignFunc->GetType()->IsCFunc()) { CJC_ASSERT(foreignFunc->IsImportedFunc()); foreignFuncs.emplace_back(StaticCast(foreignFunc)); } } for (auto curFunc : cfuncs) { if (curFunc->Get() != Linkage::EXTERNAL) { if (!IsAllApply(curFunc)) { auto [wrapperFunc, res] = DoCFFIFuncWrapper(*curFunc, false, false); ReplaceUsesWithWrapper(*curFunc, res, *wrapperFunc); } continue; } auto [wrapperFunc, res] = DoCFFIFuncWrapper(*curFunc, false); if (!IsAllApply(curFunc)) { ReplaceUsesWithWrapper(*curFunc, res, *wrapperFunc); } } for (auto curFunc : foreignFuncs) { // wrapper function for it. if (curFunc->Get() != Linkage::EXTERNAL) { continue; } bool isForeign = curFunc->TestAttr(Attribute::FOREIGN); auto [wrapperFunc, res] = DoCFFIFuncWrapper(*curFunc, isForeign); // sanitizer_cov func have empty package name. const auto& funcPkgName = curFunc->GetPackageName().empty() ? pkg.fullPackageName : curFunc->GetPackageName(); if (funcPkgName != pkg.fullPackageName) { // NOTE: res maybe null! ReplaceUsesWithWrapper(*curFunc, res, *wrapperFunc, true); } } DumpCHIRDebug("CFFIFuncWrapper"); } template std::pair ToCHIR::DoCFFIFuncWrapper(T& curFunc, bool isForeign, bool isExternal) { auto ident = curFunc.GetSrcCodeIdentifier(); if (curFunc.IsLambda()) { ident = curFunc.GetIdentifierWithoutPrefix(); auto pos = ident.find(CFFI_FUNC_SUFFIX); if (pos != std::string::npos) { ident.erase(pos, CFFI_FUNC_SUFFIX.size()); } } const auto& funcPkgName = curFunc.GetPackageName().empty() ? pkg.fullPackageName : curFunc.GetPackageName(); if (isForeign || (curFunc.TestAttr(Attribute::PRIVATE) && isExternal)) { ident = funcPkgName + ":" + ident; } if (funcPkgName != pkg.fullPackageName) { auto wrapperFunc = builder.CreateImportedVarOrFunc( curFunc.GetFuncType(), ident, ident, "", funcPkgName, curFunc.GetGenericTypeParams()); wrapperFunc->SetCFFIWrapper(true); return std::make_pair(wrapperFunc, nullptr); } auto wrapperFunc = builder.CreateFunc(curFunc.GetDebugLocation(), curFunc.GetFuncType(), ident, ident, "", funcPkgName); wrapperFunc->SetFuncKind(curFunc.GetFuncKind()); if (wrapperFunc->IsLambda()) { auto originalLambdaInfo = FuncSigInfo { .funcName = curFunc.GetSrcCodeIdentifier(), .funcType = curFunc.GetOriginalLambdaType(), .genericTypeParams = curFunc.GetGenericTypeParams() }; wrapperFunc->SetOriginalLambdaInfo(originalLambdaInfo); } auto body = builder.CreateBlockGroup(*wrapperFunc); wrapperFunc->SetCFFIWrapper(true); curFunc.EnableAttr(Attribute::NO_INLINE); curFunc.template Set(Linkage::INTERNAL); wrapperFunc->InitBody(*body); wrapperFunc->EnableAttr(Attribute::COMPILER_ADD); auto linkage = isExternal ? Linkage::EXTERNAL : Linkage::INTERNAL; wrapperFunc->template Set(linkage); std::vector args{}; auto curFuncType = curFunc.GetFuncType(); auto paramTys = curFuncType->GetParamTypes(); for (auto paramTy : paramTys) { builder.CreateParameter(paramTy, INVALID_LOCATION, *wrapperFunc); } auto& funcParams = wrapperFunc->GetParams(); std::for_each(funcParams.begin(), funcParams.end(), [&args](auto item) { args.emplace_back(item); }); // create body auto entry = builder.CreateBlock(body); body->SetEntryBlock(entry); auto retType = curFunc.GetReturnType(); auto res = builder.CreateExpression( curFunc.GetDebugLocation(), retType, &curFunc, FuncCallContext{.args = args}, entry); entry->AppendExpression(res); auto retAlloc = builder.CreateExpression(DebugLocation(), builder.GetType(retType), retType, entry); entry->AppendExpression(retAlloc); wrapperFunc->SetReturnValue(*retAlloc->GetResult()); // create exit entry->AppendExpression(builder.CreateExpression( builder.GetType(), res->GetResult(), retAlloc->GetResult(), entry)); entry->AppendExpression(builder.CreateTerminator(entry)); return std::make_pair(wrapperFunc, res); } template bool ToCHIR::IsAllApply(const T* curFunc) { auto users = curFunc->GetUsers(); for (size_t index = 0; index < users.size(); index++) { if (users[index]->GetExprKind() != ExprKind::APPLY && users[index]->GetExprKind() != ExprKind::APPLY_WITH_EXCEPTION) { return false; } if (users[index]->GetExprKind() == ExprKind::APPLY) { auto applyNode = StaticCast(users[index]); if (applyNode->GetCallee() != curFunc) { return false; } } if (users[index]->GetExprKind() == ExprKind::APPLY_WITH_EXCEPTION) { auto applyNode = StaticCast(users[index]); if (applyNode->GetCallee() != curFunc) { return false; } } } return true; } void ToCHIR::CreateBoxTypeForRecursionValueType() { Utils::ProfileRecorder recorder("CHIR", "BoxRecursionEnum"); auto transformer = BoxRecursionValueType(*chirPkg, builder); transformer.CreateBoxTypeForRecursionValueType(); DumpCHIRDebug("BoxRecursionEnum"); } namespace { void EmitCHIR(const std::string& outputPath, const CHIR::Package& package, ToCHIR::Phase phase, bool dump) { std::string path; if (FileUtil::IsDir(outputPath)) { path = FileUtil::JoinPath(outputPath, package.GetName() + CHIR_SERIALIZATION_FILE_EXTENSION); } else { path = outputPath; } CHIRSerializer::Serialize(package, path, phase); if (dump) { const std::string dumpFileName = FileUtil::GetFileNameWithoutExtension(path) + "_Emit_Debug" + CHIR_READABLE_FILE_EXTENSION; // print serialize extension info which just for serialization not necessary for chir nodes CHIRPrinter::PrintCHIRSerializeInfo(phase, dumpFileName); // print package CHIRPrinter::PrintPackage(package, dumpFileName); } } } // namespace bool ToCHIR::ComputeAnnotations(std::vector&& annoOnly) { isComputingAnnos = true; if (!TranslateToCHIR(std::move(annoOnly))) { return false; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (opts.outputMode == GlobalOptions::OutputMode::CHIR) { auto fileName = FileUtil::JoinPath(opts.output, chirPkg->GetName()) + CHIR_SERIALIZATION_FILE_EXTENSION; CHIRSerializer::Serialize(*chirPkg, fileName, CHIR::ToCHIR::RAW); return true; } #endif CreateVTableAndUpdateFuncCall(); if (!opts.enIncrementalCompilation && !RunIRChecker(Phase::RAW)) { return false; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND /// =============== Meta Transformation for CHIR =============== if (!PerformPlugin(*chirPkg)) { return false; } #endif RunUnreachableMarkBlockRemoval(); RunMergingBlocks("CHIR", "MergingBlock"); NothingTypeExprElimination(); UnreachableBlockElimination(); DoClosureConversion(); if (!RunConstantEvaluation()) { return false; } isComputingAnnos = false; debugFileIndex = 0; return true; } bool ToCHIR::Run() { CJC_NULLPTR_CHECK(typeManager); CJC_NULLPTR_CHECK(gim); CJC_NULLPTR_CHECK(&needToOptGenericDecl); CJC_NULLPTR_CHECK(&needToOptString); CJC_NULLPTR_CHECK(&releaseCHIRMemory); CJC_NULLPTR_CHECK(&cangjieHome); if (!TranslateToCHIR({})) { return false; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (opts.outputMode == GlobalOptions::OutputMode::CHIR) { auto fileName = FileUtil::JoinPath(opts.output, chirPkg->GetName()) + CHIR_SERIALIZATION_FILE_EXTENSION; CHIRSerializer::Serialize(*chirPkg, fileName, CHIR::ToCHIR::RAW); return true; } #endif CreateVTableAndUpdateFuncCall(); if (!opts.enIncrementalCompilation && !RunIRChecker(Phase::RAW)) { return false; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND /// =============== Meta Transformation for CHIR =============== if (!PerformPlugin(*chirPkg)) { return false; } #endif if (opts.emitCHIRPhase == GlobalOptions::CandidateEmitCHIRPhase::RAW) { EmitCHIR(outputPath, *chirPkg, Phase::RAW, opts.chirDumpDebugMode); return true; } RecordCHIRExprNum("trans"); RecordCodeInfoAtTheBegin(); UnreachableBlockReporter(); RunUnreachableMarkBlockRemoval(); RunMergingBlocks("CHIR", "MergingBlock"); RunMarkClassHasInited(); if (ci.isCJLint) { return RunAnalysisForCJLint() && RunIRChecker(Phase::ANALYSIS_FOR_CJLINT); } if (!RunOptimizationPassAndRulesChecking()) { return false; } RecordCHIRExprNum("opt"); RemoveUnusedImports(false); DoClosureConversion(); RecordCHIRExprNum("cc"); CreateBoxTypeForRecursionValueType(); if (!RunConstantEvaluation()) { return false; } RemoveUnusedImports(true); // annotation check depends on const eval if (!RunAnnotationChecks()) { return false; } RunSanitizerCoverage(); if (opts.enIncrementalCompilation) { UpdateEffectMapToString(effectMap, strEffectMap); } UpdatePosOfMacroExpandNode(); EraseDebugExpr(); CFFIFuncWrapper(); RecordCHIRExprNum("CHIR stage"); RecordCodeInfoAtTheEnd(); if (!RunIRChecker(Phase::OPT)) { return false; } if (opts.emitCHIRPhase == GlobalOptions::CandidateEmitCHIRPhase::OPT) { EmitCHIR(outputPath, *chirPkg, Phase::OPT, opts.chirDumpDebugMode); } else if (opts.saveTemps) { auto tempFileInfo = TempFileManager::Instance().CreateNewFileInfo({.fileName = chirPkg->GetName()}, TempFileKind::O_CHIR); CHIRSerializer::Serialize(*chirPkg, tempFileInfo.filePath, Phase::OPT); } return true; } namespace { Cangjie::Position ConvertCHIRPos2ASTPos(unsigned int fileId, const Position& pos) { return Cangjie::Position{fileId, static_cast(pos.line), static_cast(pos.column)}; } Position ConvertASTPos2CHIRPos(const Cangjie::Position& pos) { return Position{static_cast(pos.line), static_cast(pos.column)}; } } // namespace void ToCHIR::UpdatePosOfMacroExpandNode() { if (!opts.enableCompileDebug && !opts.displayLineInfo) { return; } Utils::ProfileRecorder recorder("CHIR", "UpdatePosOfMacroExpandNode"); for (auto& func : chirPkg->GetGlobalFuncs()) { bool isCommonFunctionWithoutBody = func->TestAttr(Attribute::SKIP_ANALYSIS); if (isCommonFunctionWithoutBody) { continue; // Nothing to visit } Visitor::Visit(*func, [this](Expression& expression) { auto& pos = expression.GetDebugLocation(); if (pos.IsInvalidMacroPos()) { return VisitResult::CONTINUE; } auto begin = ConvertCHIRPos2ASTPos(pos.GetFileID(), pos.GetBeginPos()); auto key = static_cast(begin.Hash64()); const auto it = std::as_const(diag.posRange2MacroCallMap).lower_bound(key); if (it == diag.posRange2MacroCallMap.cend()) { // means this expression is not from macro expanded ast node. return VisitResult::CONTINUE; } if (auto macrocall = it->second; macrocall) { auto modifiedPos = pos; modifiedPos.SetBeginPos(ConvertASTPos2CHIRPos(macrocall->GetDebugPos(begin))); auto end = ConvertCHIRPos2ASTPos(pos.GetFileID(), pos.GetEndPos()); modifiedPos.SetEndPos(ConvertASTPos2CHIRPos(macrocall->GetDebugPos(end))); expression.SetDebugLocation(modifiedPos); } return VisitResult::CONTINUE; }); } } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool ToCHIR::PerformPlugin(CHIR::Package& package) { bool succeed = true; bool hasPluginForCHIR = false; #ifndef CANGJIE_ENABLE_GCOV try { #endif Utils::ProfileRecorder recorder("CHIR", "Plugin Execution"); CHIRPluginManager chirPluginManager = ci.metaTransformPluginBuilder.BuildCHIRPluginManager(builder); chirPluginManager.ForEachMetaTransformConcept([&package, &hasPluginForCHIR](MetaTransformConcept& mtc) { if (!mtc.IsForCHIR()) { return; } hasPluginForCHIR = true; if (mtc.IsForFunc()) { for (auto func : package.GetGlobalFuncs()) { static_cast*>(&mtc)->Run(*func); } } else if (mtc.IsForPackage()) { static_cast*>(&mtc)->Run(package); } else { CJC_ASSERT(false && "Should not reach here."); } }); #ifndef CANGJIE_ENABLE_GCOV } catch (...) { succeed = false; } #endif if (!succeed) { diag.DiagnoseRefactor(DiagKindRefactor::plugin_throws_exception, DEFAULT_POSITION); } else if (hasPluginForCHIR && builder.IsEnableIRCheckerAfterPlugin()) { DumpCHIRDebug("PLUGIN"); Utils::ProfileRecorder rec2("CHIR", "IRCheck after plugins"); succeed = IRCheck(package, opts, builder, Phase::PLUGIN); } return succeed; } #endif // var inst type, var offset std::pair GetIndexByName( const CustomType& baseType, const std::string& name, CHIRBuilder& builder) { Type* memberVarInstType = nullptr; std::unordered_map instMap; baseType.GetInstMap(instMap, builder); auto allMemberVars = baseType.GetCustomTypeDef()->GetAllInstanceVars(); uint64_t index = allMemberVars.size(); // be sure to use reverse order traversal // because sub class and parent class may have private member vars with same name for (auto it = allMemberVars.crbegin(); it != allMemberVars.crend(); ++it) { --index; if (it->name == name) { memberVarInstType = ReplaceRawGenericArgType(*it->type, instMap, builder); break; } } CJC_NULLPTR_CHECK(memberVarInstType); return {memberVarInstType, index}; } std::vector ChangeNameToPath( CustomType& rootType, const std::vector& names, CHIRBuilder& builder) { std::vector path; CustomType* baseType = &rootType; for (const auto& name : names) { CJC_NULLPTR_CHECK(baseType); auto res = GetIndexByName(*baseType, name, builder); path.emplace_back(res.second); baseType = DynamicCast(res.first); } return path; } void UpdateToGetElementRef(GetElementByName& rawExpr, CHIRBuilder& builder) { auto locationVal = rawExpr.GetLocation(); auto locationType = StaticCast(locationVal->GetType()->StripAllRefs()); auto path = ChangeNameToPath(*locationType, rawExpr.GetNames(), builder); auto loc = rawExpr.GetDebugLocation(); auto retType = rawExpr.GetResult()->GetType(); auto parentBlock = rawExpr.GetParentBlock(); auto newExpr = builder.CreateExpression(loc, retType, locationVal, path, parentBlock); newExpr->SetAnnotation(rawExpr.MoveAnnotation()); newExpr->GetResult()->SetAnnotation(rawExpr.GetResult()->MoveAnnotation()); newExpr->GetResult()->AppendAttributeInfo(rawExpr.GetResult()->GetAttributeInfo()); rawExpr.ReplaceWith(*newExpr); } void UpdateToStoreElementRef(StoreElementByName& rawExpr, CHIRBuilder& builder) { auto locationVal = rawExpr.GetLocation(); auto locationType = StaticCast(locationVal->GetType()->StripAllRefs()); auto path = ChangeNameToPath(*locationType, rawExpr.GetNames(), builder); auto value = rawExpr.GetValue(); auto loc = rawExpr.GetDebugLocation(); auto retType = rawExpr.GetResult()->GetType(); auto parentBlock = rawExpr.GetParentBlock(); auto newExpr = builder.CreateExpression(loc, retType, value, locationVal, path, parentBlock); newExpr->SetAnnotation(rawExpr.MoveAnnotation()); newExpr->GetResult()->SetAnnotation(rawExpr.GetResult()->MoveAnnotation()); newExpr->GetResult()->AppendAttributeInfo(rawExpr.GetResult()->GetAttributeInfo()); rawExpr.ReplaceWith(*newExpr); } void UpdateToField(FieldByName& rawExpr, CHIRBuilder& builder) { auto locationVal = rawExpr.GetBase(); auto locationType = StaticCast(locationVal->GetType()->StripAllRefs()); auto path = ChangeNameToPath(*locationType, rawExpr.GetNames(), builder); auto loc = rawExpr.GetDebugLocation(); auto retType = rawExpr.GetResult()->GetType(); auto parentBlock = rawExpr.GetParentBlock(); auto newExpr = builder.CreateExpression(loc, retType, locationVal, path, parentBlock); newExpr->SetAnnotation(rawExpr.MoveAnnotation()); newExpr->GetResult()->SetAnnotation(rawExpr.GetResult()->MoveAnnotation()); newExpr->GetResult()->AppendAttributeInfo(rawExpr.GetResult()->GetAttributeInfo()); rawExpr.ReplaceWith(*newExpr); } void ToCHIR::UpdateMemberVarPath() { std::vector getByName; std::vector storeByName; std::vector fieldByName; std::function preVisit = [&preVisit, &getByName, &storeByName, &fieldByName](Expression& e) { if (auto lambda = DynamicCast(&e)) { Visitor::Visit(*lambda->GetBody(), preVisit); } else if (auto get = DynamicCast(&e)) { getByName.emplace_back(get); } else if (auto store = DynamicCast(&e)) { storeByName.emplace_back(store); } else if (auto field = DynamicCast(&e)) { fieldByName.emplace_back(field); } return VisitResult::CONTINUE; }; for (auto func : chirPkg->GetGlobalFuncs()) { Visitor::Visit(*func, preVisit); } for (auto e : getByName) { UpdateToGetElementRef(*e, builder); } for (auto e : storeByName) { UpdateToStoreElementRef(*e, builder); } for (auto e : fieldByName) { UpdateToField(*e, builder); } } void ToCHIR::CreateVTableAndUpdateFuncCall() { Utils::ProfileRecorder record("CHIR", "CreateVTableAndUpdateFuncCall"); auto generator = GenerateVTable(*chirPkg, builder, opts); generator.CreateVTable(); generator.UpdateOperatorVirFunc(); generator.CreateVirtualFuncWrapper(kind, cachedInfo, curVirtFuncWrapDep, delVirtFuncWrapForIncr); generator.CreateMutFuncWrapper(); generator.UpdateFuncCall(); UpdateMemberVarPath(); DumpCHIRDebug("CreateVTableAndUpdateFuncCall"); } bool ToCHIR::TranslateToCHIR(std::vector&& annoOnly) { Utils::ProfileRecorder recorder("CHIR", "AST to CHIR Translation"); std::unordered_map typeMap; CHIR::CHIRTypeCache chirTypeCache(typeMap); auto chirType = std::make_unique(builder, chirTypeCache); auto ast2chirBuilder = AST2CHIR::AST2CHIRBuilder(); auto ast2CHIR = ast2chirBuilder.SetGenericInstantiationManager(gim) ->SetImportManager(importManager) ->SetSourceManager(sourceManager) ->SetTypeManager(*typeManager) ->SetGlobalOptions(opts) ->SetIncreKind(kind) ->SetCachedInfo(cachedInfo) ->SetDiag(diag) ->SetCHIRBuilder(builder) ->SetCHIRType(*chirType) ->SetOutputPath(outputPath) ->SetComputeAnnotations(isComputingAnnos) ->Build(); ast2CHIR.SetAnnoOnlyDecls(std::move(annoOnly)); auto res = ast2CHIR.ToCHIRPackage(pkg); if (diag.GetErrorCount() != 0 || !res) { return false; } chirPkg = ast2CHIR.GetPackage(); DumpCHIRDebug("AST_CHIR"); FlatForInExpr(); srcCodeImportedFuncs = ast2CHIR.GetSrcCodeImportedFuncs(); srcCodeImportedVars = ast2CHIR.GetSrcCodeImportedVars(); implicitFuncs = ast2CHIR.GetImplicitFuncs(); initFuncsForConstVar = ast2CHIR.GetInitFuncsForConstVar(); maybeUnreachable = ast2CHIR.GetMaybeUnreachableBlocks(); if (isComputingAnnos) { annoFactoryFuncs = ast2CHIR.GetAnnoFactoryFuncs(); globalNominalCache = std::move(chirTypeCache.globalNominalCache); } for (auto& file : pkg.files) { for (auto& macrocall : file->originalMacroCallNodes) { auto key = static_cast(macrocall->begin.Hash64()); diag.posRange2MacroCallMap[key] = macrocall.get(); key = static_cast(macrocall->end.Hash64()); diag.posRange2MacroCallMap[key] = nullptr; } } return true; } VarInitDepMap ToCHIR::GetVarInitDepMap() const { VarInitDepMap dep; for (auto var : chirPkg->GetGlobalVars()) { // local const doesn't have raw mangle name if (var->GetRawMangledName().empty()) { continue; } // some simple init doesn't have init func, e.g literal init if (!var->GetInitFunc()) { continue; } dep[var->GetRawMangledName()] = var->GetInitFunc()->GetIdentifierWithoutPrefix(); } return dep; } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/CHIRBuilder.cpp000066400000000000000000000233141510705540100210660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the CHIRBuilder class in CHIR. */ #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/CHIRContext.h" #include "cangjie/Basic/Print.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Value.h" #include "cangjie/Mangle/CHIRMangler.h" using namespace Cangjie::CHIR; CHIRBuilder::CHIRBuilder(CHIRContext& context, size_t threadIdx) : context(context), threadIdx(threadIdx) { } CHIRBuilder::~CHIRBuilder() { MergeAllocatedInstance(); } // ===--------------------------------------------------------------------=== // // BlockGroup API // ===--------------------------------------------------------------------=== // BlockGroup* CHIRBuilder::CreateBlockGroup(Func& func) { auto blockGroup = new BlockGroup(std::to_string(func.GenerateBlockGroupId())); this->allocatedBlockGroups.push_back(blockGroup); return blockGroup; } // ===--------------------------------------------------------------------===// // Block API // ===--------------------------------------------------------------------===// Block* CHIRBuilder::CreateBlock(BlockGroup* parentGroup) { CJC_NULLPTR_CHECK(parentGroup); auto func = parentGroup->GetTopLevelFunc(); CJC_NULLPTR_CHECK(func); std::string idstr = "#" + std::to_string(func->GenerateBlockId()); auto basicBlock = new Block(idstr, parentGroup); this->allocatedBlocks.push_back(basicBlock); if (markAsCompileTimeValue) { basicBlock->EnableAttr(Attribute::CONST); } return basicBlock; } // split one block to two blocks, and remove separator std::pair CHIRBuilder::SplitBlock(const Expression& separator) { auto block1 = separator.GetParentBlock(); auto block2 = CreateBlock(block1->GetParentBlockGroup()); bool needMove = false; for (auto expr : block1->GetExpressions()) { if (expr == &separator) { needMove = true; expr->RemoveSelfFromBlock(); auto term = CreateTerminator(block2, block1); block1->AppendExpression(term); continue; } if (needMove) { expr->MoveTo(*block2); } } return std::pair{block1, block2}; } // ===--------------------------------------------------------------------===// // Value API // ===--------------------------------------------------------------------===// Parameter* CHIRBuilder::CreateParameter(Type* ty, const DebugLocation& loc, Func& parentFunc) { auto id = parentFunc.GenerateLocalId(); auto param = new Parameter(ty, "%" + std::to_string(id), &parentFunc); param->EnableAttr(Attribute::READONLY); param->SetDebugLocation(loc); this->allocatedValues.push_back(param); return param; } Parameter* CHIRBuilder::CreateParameter(Type* ty, const DebugLocation& loc, Lambda& parentLambda) { CJC_NULLPTR_CHECK(parentLambda.GetTopLevelFunc()); auto id = parentLambda.GetTopLevelFunc()->GenerateLocalId(); auto param = new Parameter(ty, "%" + std::to_string(id), parentLambda); param->EnableAttr(Attribute::READONLY); param->SetDebugLocation(loc); this->allocatedValues.push_back(param); return param; } GlobalVar* CHIRBuilder::CreateGlobalVar(const DebugLocation& loc, RefType* ty, const std::string& mangledName, const std::string& srcCodeIdentifier, const std::string& rawMangledName, const std::string& packageName) { GlobalVar* globalVar = new GlobalVar(ty, "@" + mangledName, srcCodeIdentifier, rawMangledName, packageName); globalVar->SetDebugLocation(loc); this->allocatedValues.push_back(globalVar); if (context.GetCurPackage() != nullptr) { context.GetCurPackage()->AddGlobalVar(globalVar); } return globalVar; } // ===--------------------------------------------------------------------===// // Expression API // ===--------------------------------------------------------------------===// Func* CHIRBuilder::CreateFunc(const DebugLocation& loc, FuncType* funcTy, const std::string& mangledName, const std::string& srcCodeIdentifier, const std::string& rawMangledName, const std::string& packageName, const std::vector& genericTypeParams) { Func* func = new Func(funcTy, "@" + mangledName, srcCodeIdentifier, rawMangledName, packageName, genericTypeParams); this->allocatedValues.push_back(func); if (context.GetCurPackage() != nullptr) { context.GetCurPackage()->AddGlobalFunc(func); } func->SetDebugLocation(loc); return func; } // ===--------------------------------------------------------------------===// // StructDef API // ===--------------------------------------------------------------------===// StructDef* CHIRBuilder::CreateStruct(const DebugLocation& loc, const std::string& srcCodeIdentifier, const std::string& mangledName, const std::string& pkgName, bool isImported) { StructDef* ret = new StructDef(srcCodeIdentifier, "@" + mangledName, pkgName); this->allocatedStructs.push_back(ret); if (context.GetCurPackage() != nullptr) { if (isImported) { context.GetCurPackage()->AddImportedStruct(ret); ret->EnableAttr(Attribute::IMPORTED); } else { context.GetCurPackage()->AddStruct(ret); } } ret->SetDebugLocation(loc); return ret; } // ===--------------------------------------------------------------------===// // ClassDef API // ===--------------------------------------------------------------------===// ClassDef* CHIRBuilder::CreateClass(const DebugLocation& loc, const std::string& srcCodeIdentifier, const std::string& mangledName, const std::string& pkgName, bool isClass, bool isImported) { ClassDef* ret = new ClassDef(srcCodeIdentifier, "@" + mangledName, pkgName, isClass); this->allocatedClasses.push_back(ret); if (context.GetCurPackage() != nullptr) { if (isImported) { context.GetCurPackage()->AddImportedClass(ret); ret->EnableAttr(Attribute::IMPORTED); } else { context.GetCurPackage()->AddClass(ret); } } ret->SetDebugLocation(loc); return ret; } // ===--------------------------------------------------------------------===// // EnumDef API // ===--------------------------------------------------------------------===// EnumDef* CHIRBuilder::CreateEnum(const DebugLocation& loc, const std::string& srcCodeIdentifier, const std::string& mangledName, const std::string& pkgName, bool isImported, bool isNonExhaustive) { EnumDef* ret = new EnumDef(srcCodeIdentifier, "@" + mangledName, pkgName, isNonExhaustive); this->allocatedEnums.push_back(ret); if (context.GetCurPackage() != nullptr) { if (isImported) { context.GetCurPackage()->AddImportedEnum(ret); ret->EnableAttr(Attribute::IMPORTED); } else { context.GetCurPackage()->AddEnum(ret); } } ret->SetDebugLocation(loc); return ret; } // ===--------------------------------------------------------------------===// // ExtendDef API // ===--------------------------------------------------------------------===// ExtendDef* CHIRBuilder::CreateExtend(const DebugLocation& loc, const std::string& mangledName, const std::string& pkgName, bool isImported, const std::vector genericParams) { ExtendDef* ret = new ExtendDef("@" + mangledName, pkgName, genericParams); this->allocatedExtends.emplace_back(ret); if (context.GetCurPackage() != nullptr) { if (isImported) { context.GetCurPackage()->AddImportedExtend(ret); ret->EnableAttr(Attribute::IMPORTED); } else { context.GetCurPackage()->AddExtend(ret); } } ret->SetDebugLocation(loc); return ret; } // ===--------------------------------------------------------------------===// // Package API // ===--------------------------------------------------------------------===// Package* CHIRBuilder::CreatePackage(const std::string& name) { Package* pkg = new Package(name); context.SetCurPackage(pkg); return pkg; } Package* CHIRBuilder::GetCurPackage() const { return context.GetCurPackage(); } std::unordered_set CHIRBuilder::GetAllCustomTypes() const { std::unordered_set result; for (auto ty : context.dynamicAllocatedTys) { if (auto customTy = DynamicCast(ty); customTy) { result.emplace(customTy); } } for (auto ty : context.constAllocatedTys) { if (auto customTy = DynamicCast(ty); customTy) { result.emplace(customTy); } } return result; } std::unordered_set CHIRBuilder::GetAllGenericTypes() const { std::unordered_set result; for (auto ty : context.dynamicAllocatedTys) { if (auto genericTy = DynamicCast(ty); genericTy) { result.emplace(genericTy); } } for (auto ty : context.constAllocatedTys) { if (auto genericTy = DynamicCast(ty); genericTy) { result.emplace(genericTy); } } return result; } void CHIRBuilder::EnableIRCheckerAfterPlugin() { enableIRCheckerAfterPlugin = true; } void CHIRBuilder::DisableIRCheckerAfterPlugin() { enableIRCheckerAfterPlugin = false; } bool CHIRBuilder::IsEnableIRCheckerAfterPlugin() const { return enableIRCheckerAfterPlugin; } cangjie_compiler-1.0.7/src/CHIR/CHIRChecker.cpp000066400000000000000000004537141510705540100210570ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/CHIRChecker.h" #include "cangjie/CHIR/ToStringUtils.h" using namespace Cangjie::CHIR; namespace { std::string IndexToString(const size_t index) { auto unitDigit = index % 10; std::string suffix; if (index > 10 && index < 20) { suffix = "th"; } else if (unitDigit == 1) { suffix = "st"; } else if (unitDigit == 2) { suffix = "nd"; } else if (unitDigit == 3) { suffix = "rd"; } else { suffix = "th"; } return std::to_string(index) + suffix; } bool IsStructOrCStruct(const Type& type, bool needToBeCStruct) { if (!type.IsStruct()) { return false; } if (!needToBeCStruct) { return true; } return Cangjie::StaticCast(type).GetStructDef()->IsCStruct(); } bool IsCType(const Type& type, bool needToBeCStruct = true) { if (auto varray = Cangjie::DynamicCast(&type)) { return IsCType(*varray->GetElementType(), needToBeCStruct); } else if (auto tuple = Cangjie::DynamicCast(&type)) { for (auto t : tuple->GetElementTypes()) { if (!IsCType(*t, needToBeCStruct)) { return false; } } return true; } else if (auto ref = Cangjie::DynamicCast(&type)) { return IsCType(*ref->GetBaseType(), needToBeCStruct); } auto k = type.GetTypeKind(); return (k >= Type::TypeKind::TYPE_INT8 && k <= Type::TypeKind::TYPE_NOTHING) || type.IsCPointer() || type.IsCString() || type.IsCFunc() || IsStructOrCStruct(type, needToBeCStruct); } std::string GetExpressionString(const Expression& expr) { if (auto lambda = Cangjie::DynamicCast(&expr)) { return "lambda " + lambda->GetIdentifier(); } else if (auto result = expr.GetResult()) { return result->ToString(); } else { return expr.ToString(); } } // type must be `ValueType&`, `ReferenceType&&`, or `GenericType&` bool CheckTypeMustBeRef(const Type& type) { // `ValueType`, `ReferenceType` and `GenericType` are error if (!type.IsRef()) { return false; } auto baseType = Cangjie::StaticCast(type).GetBaseType(); // `ReferenceType&` is error if (baseType->IsReferenceType()) { return false; } // `ValueType&` and `GenericType&` are correct if (baseType->IsValueOrGenericType()) { return true; } baseType = Cangjie::StaticCast(baseType)->GetBaseType(); // `ReferenceType&&` is correct if (baseType->IsReferenceType()) { return true; } // `ValueType&&`, `GenericType&&` and `SomeType&&&...` are error return false; } std::string ValueSymbolToString(const Value& value) { if (auto func = Cangjie::DynamicCast(&value)) { return FuncSymbolStr(*func); } else { return value.ToString(); } } /** * @brief generic type includes `T` and `class-A`, we want to know `T` and `U` if is in container */ bool GenericTypeIsInContainer(const Type& type, const std::vector& container) { if (type.IsGeneric()) { if (auto& genericTy = Cangjie::StaticCast(type); genericTy.orphanFlag) { auto upperBounds = genericTy.GetUpperBounds(); CJC_ASSERT(upperBounds.size() == 1); return GenericTypeIsInContainer(*upperBounds[0], container); } else { return std::find(container.begin(), container.end(), &type) != container.end(); } } for (auto ty : type.GetTypeArgs()) { if (!GenericTypeIsInContainer(*ty, container)) { return false; } } return true; } bool IsUnreachableApply(const Type* thisType) { if (thisType == nullptr) { return false; } return thisType->StripAllRefs()->IsNothing(); } bool FuncCanBeDynamicDispatch(const FuncBase& func) { if (func.TestAttr(Attribute::STATIC)) { return !func.TestAttr(Attribute::PRIVATE); } else { return (func.TestAttr(Attribute::PUBLIC) || func.TestAttr(Attribute::PROTECTED)) && func.TestAttr(Attribute::VIRTUAL); } } std::vector CalculateAllUpperBounds(const GenericType& genericType, CHIRBuilder& builder) { /** * class A where T <: B {} * open class B where U <: C {} * open class C {} * * generic type `T` in class A, its direct upper bound is `B`, * but generic type `U` in class B also has constraints `U <: C`, * so upper bounds of `T` are `B` and `C` */ auto upperBounds = genericType.GetUpperBounds(); std::vector extraUpperBounds; std::unordered_map replaceTable; for (auto upperBound : upperBounds) { // a hack way, `genericType` may be an orphan generic type auto derefUpperBound = Cangjie::DynamicCast(upperBound->StripAllRefs()); if (derefUpperBound == nullptr) { continue; } auto originalType = derefUpperBound->GetClassDef()->GetType(); auto [res, instMap] = originalType->CalculateGenericTyMapping(*derefUpperBound); CJC_ASSERT(res); for (auto& it : instMap) { if (it.second == &genericType) { auto temp = it.first->GetUpperBounds(); extraUpperBounds.insert(extraUpperBounds.begin(), temp.begin(), temp.end()); replaceTable.emplace(it); } } } for (auto type : extraUpperBounds) { upperBounds.emplace_back(ReplaceRawGenericArgType(*type, replaceTable, builder)); } return upperBounds; } bool Generic1IsEqualOrSubTypeOfGeneric2(GenericType& generic1, const GenericType& generic2, CHIRBuilder& builder) { auto parentUpperBounds = generic2.GetUpperBounds(); std::unordered_map replaceTable{{&generic2, &generic1}}; auto subUpperBounds = CalculateAllUpperBounds(generic1, builder); while (!parentUpperBounds.empty()) { /** * class A where T <: X1 & X2 & X3 { * public func foo(a: T) {} * } * func goo(b: U) where U <: X1 & X2 { * A().foo(b) // not care about type `M`, foo's arg is `b` which is sub type of X1 and X2 * // but foo's param type has more constraints, so we can't send `b` to `foo` * } * * class A where T <: X1 & X2 { * public func foo(a: T) {} * } * func goo(b: U) where U <: X1 & X2 & X3 { * A().foo(b) // not care about type `M`, foo's arg is `b` which is sub type of X1, X2 and X3 * // foo's param type has less constraints, so we can send `b` to `foo` * } */ if (subUpperBounds.empty()) { return false; } auto subType = subUpperBounds.back(); subUpperBounds.pop_back(); for (auto it = parentUpperBounds.begin(); it != parentUpperBounds.end();) { auto parentType = ReplaceRawGenericArgType(**it, replaceTable, builder); if (subType->IsEqualOrSubTypeOf(*parentType, builder)) { /** * erase but not break, because there may be other parent types which meet the condition * interface I {} * class A <: I {} * if `T1 <: I & A` is sub type and `T2 <: I & A` is parent type, then `subType` is class A and * parent type is interface I in first loop, so `parentUpperBounds` only left class A after erasing, * and if we break this loop, then `subType` is interface I and parent type is class A in second loop, * that won't meet the condition of `IsEqualOrSubTypeOf` */ it = parentUpperBounds.erase(it); } else { ++it; } } } return true; } /** * print a vector to string, format: firstChar + ty1, ty2 ... + lastChar * for example, if we want to print instantiated type args, then it will be something like ``, * if we want to print parameter types, then it will be something like `(Int32, Bool, Rune)` */ template std::string VectorTypesToString( const std::string& firstChar, const std::vector& types, const std::string& lastChar) { auto res = firstChar; for (auto ty : types) { res += ty->ToString() + ", "; } res.pop_back(); res.pop_back(); res += lastChar; return res; } bool HasOrphanGeneric(const Type& type) { bool flag = false; auto visitor = [&flag](const Type& ty) { if (auto genericType = Cangjie::DynamicCast(&ty); genericType && genericType->orphanFlag) { flag = true; return false; } return true; }; type.VisitTypeRecursively(visitor); return flag; } bool IsFuncTypeOrClosureBaseRefType(const Type& type) { if (type.IsFunc()) { return true; } auto refType = Cangjie::DynamicCast(&type); if (refType == nullptr) { return false; } return refType->GetBaseType()->IsAutoEnvBase(); } /** * if value is %0 of `%0 = Constant(null)`, then return `NullLiteral`, otherwise, return `nullptr` */ NullLiteral* ConvertToNullLiteral(const Value& value) { // e.g. var x = VArray(repeat: return) // %0: Nothing = Constant(null) // %1: (Int64) -> Nothing = Constant(null) // %2: Int64 = Constant(1i) // %3: VArray = VArrayBuilder(%2, %1, %0) // there are two `Constant(null)`, %0 and %1, so we need to regard %0 as non-NullLiteral if (value.GetType()->IsNothing()) { return nullptr; } auto localVar = Cangjie::DynamicCast(&value); if (localVar == nullptr) { return nullptr; } auto constExpr = Cangjie::DynamicCast(localVar->GetExpr()); if (constExpr == nullptr) { return nullptr; } return Cangjie::DynamicCast(constExpr->GetValue()); } Value* GetOwnerFuncOrLambda(const BlockGroup& blockGroup) { if (auto ownerFunc = blockGroup.GetOwnerFunc()) { return ownerFunc; } auto ownerExpr = blockGroup.GetOwnerExpression(); CJC_NULLPTR_CHECK(ownerExpr); if (ownerExpr->IsLambda()) { return ownerExpr->GetResult(); } auto parentBG = ownerExpr->GetParentBlockGroup(); CJC_NULLPTR_CHECK(parentBG); return GetOwnerFuncOrLambda(*parentBG); } void CollectFuncAndGenericTypesRecursively( const Value& func, std::vector>>& result) { if (auto lambdaRes = Cangjie::DynamicCast(&func)) { auto lambda = Cangjie::StaticCast(lambdaRes->GetExpr()); auto parentBG = lambda->GetParentBlockGroup(); CJC_NULLPTR_CHECK(parentBG); auto parentFuncOrLambda = GetOwnerFuncOrLambda(*parentBG); CollectFuncAndGenericTypesRecursively(*parentFuncOrLambda, result); result.emplace_back(lambdaRes, lambda->GetGenericTypeParams()); } else { auto funcBase = Cangjie::VirtualCast(&func); result.emplace_back(funcBase, funcBase->GetGenericTypeParams()); } } std::string GetFuncIdentifier(const Value& func) { if (auto funcBase = Cangjie::DynamicCast(&func)) { return funcBase->GetIdentifier(); } else { auto lambda = Cangjie::StaticCast(func).GetExpr(); return Cangjie::StaticCast(lambda)->GetIdentifier(); } } } // namespace CHIRChecker::CHIRChecker(const Package& package, const Cangjie::GlobalOptions& opts, CHIRBuilder& builder) : package(package), opts(opts), builder(builder) { } bool CHIRChecker::CheckPackage(const std::unordered_set& r) { optionalRules = r; ParallelCheck(&CHIRChecker::CheckFunc, package.GetGlobalFuncs()); ParallelCheck(&CHIRChecker::CheckGlobalVar, package.GetGlobalVars()); ParallelCheck(&CHIRChecker::CheckImportedVarAndFuncs, package.GetImportedVarAndFuncs()); ParallelCheck(&CHIRChecker::CheckStructDef, package.GetAllStructDef()); for (auto id : duplicatedGlobalIds) { Errorln("duplicated identifier `" + id + "` found."); } return checkResult; } void CHIRChecker::Warningln(const std::string& info) { errorMessage << "chir checker warning:\n " + info + "\n"; } void CHIRChecker::Errorln(const std::string& info) { errorMessage << "chir checker error:\n " + info + "\n"; checkResult = false; } void CHIRChecker::WarningInFunc(const Value& func, const std::string& info) { Warningln("in function " + func.GetIdentifier() + ", " + info); } void CHIRChecker::WarningInExpr(const Value& func, const Expression& expr, const std::string& info) { WarningInFunc(func, "in `" + GetExpressionString(expr) + "`, " + info); } void CHIRChecker::ErrorInFunc(const Value& func, const std::string& info) { Errorln("in function " + func.GetIdentifier() + ", " + info); } void CHIRChecker::ErrorInLambdaOrFunc(const FuncBase& func, const Lambda* lambda, const std::string& info) { if (lambda == nullptr) { ErrorInFunc(func, info); } else { ErrorInExpr(func, *lambda, info); } } void CHIRChecker::ErrorInExpr(const Value& func, const Expression& expr, const std::string& info) { ErrorInFunc(func, "in `" + GetExpressionString(expr) + "`, " + info); } void CHIRChecker::TypeCheckError( const Expression& expr, const Value& value, const std::string& expectedType, const Func& topLevelFunc) { auto errMsg = "value " + value.GetIdentifier() + " used in " + GetExpressionString(expr) + " has type " + value.GetType()->ToString() + ", but " + expectedType + " type is expected."; ErrorInFunc(topLevelFunc, errMsg); } bool CHIRChecker::OperandNumIsEqual(size_t expectedNum, const Expression& expr, const Func& topLevelFunc) { auto realNum = expr.GetOperands().size(); if (expectedNum != realNum) { auto errMsg = "expect " + std::to_string(expectedNum) + " operand(s), but there are " + std::to_string(realNum) + " in fact."; ErrorInExpr(topLevelFunc, expr, errMsg); return false; } return true; } bool CHIRChecker::OperandNumIsEqual( const std::vector& expectedNum, const Expression& expr, const Func& topLevelFunc) { CJC_ASSERT(!expectedNum.empty()); auto realNum = expr.GetOperands().size(); for (auto n : expectedNum) { if (n == realNum) { return true; } } std::string expectedNumStr = "{"; for (auto n : expectedNum) { expectedNumStr += std::to_string(n) + ", "; } // remove last ", " expectedNumStr.pop_back(); expectedNumStr.pop_back(); expectedNumStr += "}"; auto errMsg = "expect " + expectedNumStr + " operand(s), but there are " + std::to_string(realNum) + " in fact."; ErrorInExpr(topLevelFunc, expr, errMsg); return false; } bool CHIRChecker::SuccessorNumIsEqual(size_t expectedNum, const Terminator& expr, const Func& topLevelFunc) { auto realNum = expr.GetSuccessors().size(); if (expectedNum != realNum) { auto errMsg = "expect " + std::to_string(expectedNum) + " successor(s), but there are " + std::to_string(realNum) + " in fact."; ErrorInExpr(topLevelFunc, expr, errMsg); return false; } return true; } bool CHIRChecker::OperandNumAtLeast(size_t expectedNum, const Expression& expr, const Func& topLevelFunc) { auto realNum = expr.GetOperands().size(); if (expectedNum > realNum) { auto errMsg = "expect at least " + std::to_string(expectedNum) + " operand(s), but there are " + std::to_string(realNum) + " in fact."; ErrorInExpr(topLevelFunc, expr, errMsg); return false; } return true; } bool CHIRChecker::SuccessorNumAtLeast(size_t expectedNum, const Terminator& expr, const Func& topLevelFunc) { auto realNum = expr.GetSuccessors().size(); if (expectedNum > realNum) { auto errMsg = "expect at least " + std::to_string(expectedNum) + " successor(s), but there are " + std::to_string(realNum) + " in fact."; ErrorInExpr(topLevelFunc, expr, errMsg); return false; } return true; } bool CHIRChecker::CheckHaveResult(const Expression& expr, const Func& topLevelFunc) { auto result = expr.GetResult(); if (result == nullptr) { ErrorInExpr(topLevelFunc, expr, "this expression should have result."); return false; } return true; } void CHIRChecker::ShouldNotHaveResult(const Terminator& expr, const Func& topLevelFunc) { if (auto result = expr.GetResult()) { ErrorInExpr(topLevelFunc, expr, "this terminator shouldn't have result."); } } bool CHIRChecker::TypeIsExpected(const Type& srcType, const Type& dstType) { if (&srcType == &dstType) { return true; } // `NothingType` can be casted to any type implicitly in CHIR if (srcType.IsNothing()) { return true; } // we can set a sub type to a parent type, it's safe in llvm ir, // but for legal CHIR, we still need to check this case later if (srcType.IsEqualOrSubTypeOf(dstType, builder)) { return true; } // it's a hack, in mut wrapper func, CHIR cast param to Any&, this will be corrected later if (srcType.StripAllRefs()->IsAny() && dstType.IsClassRef()) { return true; } // orphan generic type is a hack, will be removed later if (HasOrphanGeneric(srcType) || HasOrphanGeneric(dstType)) { return true; } return false; } void CHIRChecker::CheckGlobalIdentifier(const Value& value) { auto identifier = value.GetIdentifierWithoutPrefix(); // 1. identifier can't be empty if (identifier.empty()) { auto errMsg = ValueSymbolToString(value) + " doesn't have identifier."; Errorln(errMsg); return; } // 2. imported C func doesn't need to check duplicated id, there may be same id from different packages if (value.TestAttr(Attribute::IMPORTED) && value.GetType()->IsCFunc()) { return; } // 3. identifier can't be duplicated std::unique_lock lock(checkIdentMutex); if (!identifiers.emplace(identifier).second) { duplicatedGlobalIds.emplace(identifier); } } void CHIRChecker::CheckGlobalVar(const GlobalVarBase& var) { // 1. check identifier CheckGlobalIdentifier(var); // 2. type must be ref if (!CheckTypeMustBeRef(*var.GetType())) { auto valueType = var.GetType()->StripAllRefs(); std::string expectedType; if (valueType->IsReferenceType()) { expectedType = valueType->ToString() + "&&"; } else { expectedType = valueType->ToString() + "&"; } auto errMsg = "the type of global var " + var.GetIdentifier() + " should be " + expectedType + ", but now it's " + var.GetType()->ToString(); Errorln(errMsg); } // 3. must have initializer or init func if (auto globalVar = DynamicCast(&var)) { bool initByFunc = globalVar->GetInitFunc() != nullptr; bool initByLiteral = globalVar->GetInitializer() != nullptr; auto errMsg = "global var " + globalVar->GetIdentifier() + " should be initialized by literal or function, "; std::string hint; if (initByFunc && initByLiteral) { hint = "can't be both."; } else if (!initByFunc && !initByLiteral) { // why doesn't it have init func if (!globalVar->TestAttr(Attribute::COMMON)) { hint = "it must have one of them."; } } if (!hint.empty()) { Errorln(errMsg); } } // 4. must have src code identifier if (!var.TestAttr(Attribute::COMPILER_ADD) && var.GetSrcCodeIdentifier().empty()) { auto errMsg = "global var " + var.GetIdentifier() + " doesn't have src code identifier."; Errorln(errMsg); } // 5. must have package name if (var.GetPackageName().empty()) { auto errMsg = "global var " + var.GetIdentifier() + " doesn't have package name."; Errorln(errMsg); } // 6. check static member var auto def = var.GetParentCustomTypeDef(); if (def != nullptr && !var.TestAttr(Attribute::STATIC)) { auto errMsg = "global var " + var.GetIdentifier() + " should be a static member var, but it doesn't have Attribute `STATIC`."; Errorln(errMsg); } else if (def == nullptr && var.TestAttr(Attribute::STATIC)) { auto errMsg = "global var " + var.GetIdentifier() + " has Attribute `STATIC` but it isn't member var."; Errorln(errMsg); } } void CHIRChecker::CheckImportedVarAndFuncs(const ImportedValue& value) { if (auto importedVar = DynamicCast(&value)) { CheckGlobalVar(*importedVar); } else { CheckFuncBase(StaticCast(value)); } } bool CHIRChecker::CheckFuncBase(const FuncBase& func) { // 1. check identifier CheckGlobalIdentifier(func); // 2. check func type if (!CheckFuncType(func.GetType(), nullptr, func)) { return false; } // 3. check parent CustomTypeDef if (auto parentDef = func.GetParentCustomTypeDef()) { if (!CheckParentCustomTypeDef(func, *parentDef)) { return false; } } // 4. must have package name if (func.GetPackageName().empty()) { auto errMsg = "we need to set package name in func " + func.GetIdentifier() + "."; Errorln(errMsg); return false; } // 5. check original lambda info if (!CheckOriginalLambdaInfo(func)) { return false; } return true; } bool CHIRChecker::CheckFuncType(const Type* type, const Lambda* lambda, const FuncBase& topLevelFunc) { // 1. must have func type if (type == nullptr) { if (lambda == nullptr) { Errorln("func " + topLevelFunc.GetIdentifier() + " doesn't have type."); } else { ErrorInFunc(topLevelFunc, "lambda " + lambda->GetIdentifier() + " doesn't have type."); } return false; } else if (!type->IsFunc()) { if (lambda == nullptr) { Errorln("the type of func " + topLevelFunc.GetIdentifier() + " isn't func type."); } else { ErrorInFunc(topLevelFunc, "the type of lambda " + lambda->GetIdentifier() + " isn't func type."); } return false; } // 2. only CFunc can have variable length parameters auto funcType = StaticCast(type); auto isCFunc = funcType->IsCFunc(); if (!isCFunc && funcType->HasVarArg()) { auto errMsg = "this isn't CFunc, but it has variable length parameters."; ErrorInLambdaOrFunc(topLevelFunc, lambda, errMsg); return false; } // 3. check CFunc type if (isCFunc) { return CheckCFuncType(*funcType, lambda, topLevelFunc); } // 4. check lifted lambda type if (lambda == nullptr && !CheckLiftedLambdaType(topLevelFunc)) { return false; } // 5. global var init func's return type must be `Void` if (lambda == nullptr && topLevelFunc.IsGVInit() && !funcType->GetReturnType()->IsVoid()) { auto errMsg = "function " + topLevelFunc.GetIdentifier() + "is global var init, its return type is " + funcType->GetReturnType()->ToString() + ", but `Void` is expected"; Errorln(errMsg); return false; } // 6. check param type return CheckParamTypes(funcType->GetParamTypes(), lambda, topLevelFunc); } bool CHIRChecker::CheckParentCustomTypeDef(const FuncBase& func, const CustomTypeDef& def) { auto parentDef = func.GetParentCustomTypeDef(); if (parentDef == nullptr) { auto errMsg = "func " + func.GetIdentifier() + " is member func of " + def.GetIdentifier() +", but it doesn't set parent CustomTypeDef."; Errorln(errMsg); return false; } if (parentDef != &def) { auto errMsg = "parent CustomTypeDef of func " + func.GetIdentifier() + " is " + parentDef->GetIdentifier() +", but this function is also member method of " + def.GetIdentifier() + "."; Errorln(errMsg); return false; } for (auto method : def.GetMethods()) { if (method == &func) { return true; } } auto errMsg = "parent CustomTypeDef of func " + func.GetIdentifier() + " is " + parentDef->GetIdentifier() +", but there isn't this method in this CustomTypeDef."; Errorln(errMsg); return false; } bool CHIRChecker::CheckOriginalLambdaInfo(const FuncBase& func) { if (!func.IsLambda()) { return true; } bool res = true; // 1. must set original lambda type auto originalFuncType = func.GetOriginalLambdaType(); if (originalFuncType == nullptr) { auto errMsg = "func " + func.GetIdentifier() + " is lifted lambda, original lambda type should be set."; Errorln(errMsg); res = false; } // 2. check original generic type params auto originalTypeParams = func.GetOriginalGenericTypeParams(); auto curTypeParams = func.GetGenericTypeParams(); if (originalTypeParams.size() > curTypeParams.size()) { auto errMsg = "func " + func.GetIdentifier() + " is lifted lambda, original lambda generic type params' size is " + std::to_string(originalTypeParams.size()) + ", current func's generic type params' size is " + std::to_string(curTypeParams.size()) + ", original size should be equal or less than current size."; Errorln(errMsg); res = false; } return res; } bool CHIRChecker::CheckCFuncType(const FuncType& funcType, const Lambda* lambda, const FuncBase& topLevelFunc) { // 1. CFunc can't be member method, can only be lambda or global func if (lambda == nullptr && topLevelFunc.GetParentCustomTypeDef() != nullptr) { auto errMsg = "this is a member method, but @C can only used in global function."; ErrorInLambdaOrFunc(topLevelFunc, lambda, errMsg); return false; } // 2. parameter type in CFunc must be CType bool typeMatched = true; auto paramTypes = funcType.GetParamTypes(); for (size_t i = 0; i < paramTypes.size(); ++i) { auto pType = paramTypes[i]; if (!IsCType(*pType)) { auto errMsg = "this is a CFunc, but the " + IndexToString(i) + " paramter type is " + pType->ToString() + ", it's not a CType."; ErrorInLambdaOrFunc(topLevelFunc, lambda, errMsg); typeMatched = false; } } // 3. check return type auto retType = funcType.GetReturnType(); if (!IsCType(*retType)) { // 3.1 return type must be CType auto errMsg = "this is a CFunc, but return type is " + retType->ToString() + ", it's not a CType."; ErrorInLambdaOrFunc(topLevelFunc, lambda, errMsg); typeMatched = false; } else if (retType->IsVArray()) { // 3.2 VArray can't be CFunc's return type ErrorInLambdaOrFunc(topLevelFunc, lambda, "VArray can't be CFunc's return type."); typeMatched = false; } return typeMatched; } bool CHIRChecker::CheckLiftedLambdaType(const FuncBase& func) { if (!func.IsLambda()) { return true; } // 1. the first param type must be Class-Env& auto errMsg = "func " + func.GetIdentifier() + " is lifted lambda, its first parameter type should be Class-Env&, but now func type is " + func.GetFuncType()->ToString() + "."; auto paramTypes = func.GetFuncType()->GetParamTypes(); if (paramTypes.empty()) { Errorln(errMsg); return false; } auto firstParamType = DynamicCast(paramTypes[0]); if (firstParamType == nullptr) { Errorln(errMsg); return false; } auto firstParamTypeDeref = firstParamType->GetBaseType(); if (!firstParamTypeDeref->IsAutoEnv()) { Errorln(errMsg); return false; } if (firstParamTypeDeref->IsAutoEnvBase()) { errMsg = "func " + func.GetIdentifier() + " is lifted lambda, its first parameter type should be Class-Env&, but now it's Class-EnvBase&."; Errorln(errMsg); return false; } return true; } bool CHIRChecker::CheckParamTypes( const std::vector& paramTypes, const Lambda* lambda, const FuncBase& topLevelFunc) { auto parentType = topLevelFunc.GetParentCustomTypeOrExtendedType(); bool firstParamIsThis = (lambda == nullptr) && (parentType != nullptr) && !topLevelFunc.TestAttr(Attribute::STATIC); auto funcIsInit = [&topLevelFunc]() { if (topLevelFunc.IsConstructor()) { return true; } // a hack way, in cjmp, member var init func is like xxx$varInit return topLevelFunc.GetSrcCodeIdentifier().find("$varInit") != std::string::npos; }; bool firstParamIsRef = firstParamIsThis && (parentType->IsReferenceType() || topLevelFunc.TestAttr(Attribute::MUT) || funcIsInit()); bool typeMatched = true; for (size_t i = 0; i < paramTypes.size(); ++i) { auto pType = paramTypes[i]; if (i == 0 && firstParamIsThis) { // a hack way, we need to replace Any& to correct type auto anyTyRef = builder.GetType(builder.GetAnyTy()); Type* expectedType = firstParamIsRef ? builder.GetType(parentType) : parentType; // 1. check the 1st parameter type if (pType != expectedType && pType != anyTyRef) { auto errMsg = expectedType->ToString() + " type is expected for the 1st parameter, but now it's " + pType->ToString() + "."; ErrorInFunc(topLevelFunc, errMsg); typeMatched = false; } continue; } if (pType->IsValueType() || pType->IsGeneric()) { continue; } auto errMsg = "the " + IndexToString(i) + " paramter type is " + pType->ToString(); // 2. class type must add `&` if (pType->IsReferenceType()) { ErrorInLambdaOrFunc(topLevelFunc, lambda, errMsg + ", but you should add `&` behind it."); typeMatched = false; continue; } if (pType->IsRef()) { auto baseType = StaticCast(pType)->GetBaseType(); // 3. can't use This& as parameter type if (baseType->IsThis()) { ErrorInLambdaOrFunc(topLevelFunc, lambda, errMsg + ", it shouldn't be parameter's type."); typeMatched = false; } else if (!baseType->IsClassOrArray() && !baseType->IsBox()) { // 4. value type and generic type can't use ref type, only class type, Array and box type ErrorInLambdaOrFunc(topLevelFunc, lambda, errMsg + ", but only Class type, RawArray type and Box type can use `&`."); typeMatched = false; } } } return typeMatched; } void CHIRChecker::CheckStructDef(const StructDef& def) { for (auto method : def.GetMethods()) { CheckParentCustomTypeDef(*method, def); } } void CHIRChecker::CheckFunc(const Func& func) { // 1. check FuncBase if (!CheckFuncBase(func)) { return; } // 2. func must have body if (func.GetBody() == nullptr) { Errorln("func " + func.GetIdentifier() + " doesn't have body."); return; } // 3. check func params CheckFuncParams(func.GetParams(), *func.GetFuncType(), func.GetIdentifier()); // 4. check func return value CheckFuncRetValue(func.GetReturnValue(), *func.GetFuncType()->GetReturnType(), nullptr, func); // 5. check func body // a hack way, there are some wrong AST nodes in this kind of func, we will fix later if (auto id = func.GetIdentifier(); id.find("$Mocked") == std::string::npos) { CheckBlockGroup(*func.GetBody(), func); } // 6. check local identifier CheckLocalId(*func.GetBody(), func); // 7. check unreachable op and unreachable generic type CheckUnreachableOpAndGenericTyInFuncBody(*func.GetBody()); } void CHIRChecker::CheckFuncParams( const std::vector& params, const FuncType& funcType, const std::string& funcIdentifier) { auto paramTypesToString = [](const std::vector& paramTypes) { std::string paramsStr = "("; for (size_t i = 0; i < paramTypes.size(); ++i) { if (i > 0) { paramsStr += ", "; } paramsStr += paramTypes[i]->ToString(); } paramsStr += ")"; return paramsStr; }; std::vector paramTypesInBody; for (auto p : params) { paramTypesInBody.emplace_back(p->GetType()); } auto paramsStrInBody = paramTypesToString(paramTypesInBody); auto paramTypesInFuncType = funcType.GetParamTypes(); auto paramsStrInFuncType = paramTypesToString(paramTypesInFuncType); // 1. size must be equal if (paramTypesInFuncType.size() != params.size()) { auto errMsg = "inconsistent number of parameters in func " + funcIdentifier + ", there are " + std::to_string(params.size()) + " parameter(s) in func body, but there are " + std::to_string(paramTypesInFuncType.size()) + " parameter(s) in func type.\n"; auto hint1 = " parameter type in func body is " + paramsStrInBody + "\n"; auto hint2 = " parameter type in func type is " + paramsStrInFuncType + "\n"; Errorln(errMsg + hint1 + hint2); return; } // 2. type must be equal std::vector errIdx; for (size_t i = 0; i < paramTypesInFuncType.size(); i++) { if (paramTypesInFuncType[i] != params[i]->GetType()) { errIdx.emplace_back(i); } } std::string errMsg; if (!errIdx.empty()) { errMsg = "inconsistent parameter type between func body and func type"; } for (auto i : errIdx) { errMsg += ", " + std::to_string(i) + "-th"; } if (!errIdx.empty()) { errMsg += " parameter type(s) are mismatched.\n"; auto hint1 = " parameter type in func body is " + paramsStrInBody + "\n"; auto hint2 = " parameter type in func type is " + paramsStrInFuncType + "\n"; Errorln(errMsg + hint1 + hint2); } } void CHIRChecker::CheckFuncRetValue( const LocalVar* retVal, const Type& retType, const Lambda* lambda, const Func& topLevelFunc) { if (retVal == nullptr) { // 1. return value can be null only when return type is Unit, Nothing or Void if (!retType.IsUnit() && !retType.IsNothing() && !retType.IsVoid()) { ErrorInLambdaOrFunc(topLevelFunc, lambda, "you should set a return value."); } } else if (!retVal->GetType()->IsRef()) { // 2. return value's type must be ref type, it's a memory address ErrorInLambdaOrFunc(topLevelFunc, lambda, "its return value is " + retVal->GetIdentifier() + ", this value's type should be reference type."); } else if (StaticCast(retVal->GetType())->GetBaseType() != &retType) { // 3. return value's type must be func's return type auto errMsg = "its return value is " + retVal->GetIdentifier() + ", this value's type should be " + retType.ToString() + "&, but not " + retVal->GetType()->ToString() + "."; ErrorInLambdaOrFunc(topLevelFunc, lambda, errMsg); } } void CHIRChecker::CheckLocalId(BlockGroup& blockGroup, const Func& topLevelFunc) { std::unordered_set allIds; std::set duplicatedLocalIds; std::vector exprResWithoutId; for (auto p : topLevelFunc.GetParams()) { if (!allIds.emplace(p->GetIdentifier()).second) { duplicatedLocalIds.emplace(p->GetIdentifier()); } } // 1. local id can't be duplicated in one block group // 2. local id can't be empty in one expression std::function preVisit = [&allIds, &duplicatedLocalIds, &exprResWithoutId, &preVisit](Expression& expr) { auto result = expr.GetResult(); if (result == nullptr) { return VisitResult::CONTINUE; } if (result->GetIdentifier().empty()) { exprResWithoutId.emplace_back(&expr); } else if (!allIds.emplace(result->GetIdentifier()).second) { duplicatedLocalIds.emplace(result->GetIdentifier()); } if (expr.IsLambda()) { for (auto p : StaticCast(expr).GetParams()) { if (!allIds.emplace(p->GetIdentifier()).second) { duplicatedLocalIds.emplace(p->GetIdentifier()); } } Visitor::Visit(*StaticCast(expr).GetBody(), preVisit); } return VisitResult::CONTINUE; }; Visitor::Visit(blockGroup, preVisit); if (!duplicatedLocalIds.empty()) { std::string errMsg = "there are duplicated local id: "; for (auto id : duplicatedLocalIds) { errMsg += id + ", "; } errMsg.pop_back(); errMsg.pop_back(); ErrorInFunc(topLevelFunc, errMsg); } for (auto expr : exprResWithoutId) { auto errMsg = "the result of expression " + expr->ToString() + " doesn't have identifier."; ErrorInFunc(topLevelFunc, errMsg); } } void CHIRChecker::CheckUnreachableOpAndGenericTyInFuncBody(const BlockGroup& blockGroup) { std::vector reachableValues; std::vector reachableGenericTypes; // 1. check in func body CheckUnreachableOpAndGenericTyInBG(blockGroup, reachableValues, reachableGenericTypes); } void CHIRChecker::CheckUnreachableOpAndGenericTyInBG(const BlockGroup& blockGroup, std::vector& reachableValues, std::vector& reachableGenericTypes) { auto valSize = reachableValues.size(); auto tySize = reachableGenericTypes.size(); if (auto e = blockGroup.GetOwnerExpression(); e && e->IsLambda()) { auto lambda = StaticCast(e); for (auto genericTy : lambda->GetGenericTypeParams()) { reachableGenericTypes.emplace_back(genericTy); } const auto& params = lambda->GetParams(); for (size_t i = 0; i < params.size(); ++i) { reachableValues.emplace_back(params[i]); // 1. generic type in lambda parameter must be reachable if (!GenericTypeIsInContainer(*params[i]->GetType(), reachableGenericTypes)) { auto errMsg = "generic type " + params[i]->ToString() + "is unreachable, the type is " + std::to_string(i) + "-th parameter in lambda " + lambda->GetIdentifier() + "."; ErrorInFunc(*blockGroup.GetTopLevelFunc(), errMsg); } } } else if (auto func = blockGroup.GetOwnerFunc()) { if (auto parentDef = func->GetParentCustomTypeDef()) { for (auto genericTy : parentDef->GetGenericTypeParams()) { reachableGenericTypes.emplace_back(genericTy); } } for (auto genericTy : func->GetGenericTypeParams()) { reachableGenericTypes.emplace_back(genericTy); } const auto& params = func->GetParams(); for (size_t i = 0; i < params.size(); ++i) { reachableValues.emplace_back(params[i]); // 2. generic type in global func parameter must be reachable if (!GenericTypeIsInContainer(*params[i]->GetType(), reachableGenericTypes)) { auto errMsg = "generic type " + params[i]->ToString() + "is unreachable, the type is " + std::to_string(i) + "-th parameter in function " + func->GetIdentifier() + "."; ErrorInFunc(*func, errMsg); } } } // 3. check in entry block if (auto entryBlock = blockGroup.GetEntryBlock()) { std::unordered_set visitedBlocks; CheckUnreachableOpAndGenericTyInBlock(*entryBlock, reachableValues, reachableGenericTypes, visitedBlocks); } if (reachableValues.size() > valSize) { reachableValues.erase(reachableValues.begin() + static_cast(valSize), reachableValues.end()); } if (reachableGenericTypes.size() > tySize) { reachableGenericTypes.erase( reachableGenericTypes.begin() + static_cast(tySize), reachableGenericTypes.end()); } } void CHIRChecker::CheckUnreachableOpAndGenericTyInBlock(const Block& block, std::vector& reachableValues, std::vector& reachableGenericTypes, std::unordered_set& visitedBlocks) { if (!visitedBlocks.emplace(&block).second) { return; } auto valSize = reachableValues.size(); for (auto expr : block.GetExpressions()) { auto typeSize = reachableGenericTypes.size(); if (auto lambda = DynamicCast(expr)) { auto tempTypes = lambda->GetGenericTypeParams(); reachableGenericTypes.insert(reachableGenericTypes.end(), tempTypes.begin(), tempTypes.end()); } // 1. check in expressions CheckUnreachableOpAndGenericTyInExpr(*expr, reachableValues, reachableGenericTypes); if (auto result = expr->GetResult()) { reachableValues.emplace_back(result); } // 2. check in sub block groups for (auto bg : expr->GetBlockGroups()) { CheckUnreachableOpAndGenericTyInBG(*bg, reachableValues, reachableGenericTypes); } if (reachableGenericTypes.size() > typeSize) { reachableGenericTypes.erase( reachableGenericTypes.begin() + static_cast(typeSize), reachableGenericTypes.end()); } // 3. check successor blocks if (expr->IsTerminator()) { auto terminator = Cangjie::StaticCast(expr); for (auto suc : terminator->GetSuccessors()) { CheckUnreachableOpAndGenericTyInBlock(*suc, reachableValues, reachableGenericTypes, visitedBlocks); } } } if (reachableValues.size() > valSize) { reachableValues.erase(reachableValues.begin() + static_cast(valSize), reachableValues.end()); } } void CHIRChecker::CheckUnreachableOpAndGenericTyInExpr( const Expression& expr, std::vector& reachableValues, std::vector& reachableGenericTypes) { // 1. operand in expression must be reachable CheckUnreachableOperandInExpr(expr, reachableValues); // 2. generic type in expression must be reachable CheckUnreachableGenericTypeInExpr(expr, reachableGenericTypes); } void CHIRChecker::CheckUnreachableOperandInExpr(const Expression& expr, std::vector& reachableValues) { for (auto op : expr.GetOperands()) { if (op->IsLiteral() || op->IsGlobal()) { continue; } if (std::find(reachableValues.begin(), reachableValues.end(), op) == reachableValues.end()) { ErrorInFunc(*expr.GetTopLevelFunc(), op->GetIdentifier() + " in " + expr.ToString() + " is unreachable."); } } } void CHIRChecker::CheckUnreachableGenericTypeInExpr( const Expression& expr, std::vector& reachableGenericTypes) { // 1. generic type in result must be reachable if (auto result = expr.GetResult()) { if (!GenericTypeIsInContainer(*result->GetType(), reachableGenericTypes)) { auto errMsg = "generic type " + result->GetType()->ToString() + " is unreachable, the type is from result in expression " + expr.ToString() + "."; ErrorInFunc(*expr.GetTopLevelFunc(), errMsg); } } auto eKind = expr.GetExprKind(); if (eKind == ExprKind::ALLOCATE || eKind == ExprKind::ALLOCATE_WITH_EXCEPTION) { auto base = AllocateBase(&expr); // 2. generic type in Allocate must be reachable if (!GenericTypeIsInContainer(*base.GetType(), reachableGenericTypes)) { auto errMsg = "generic type " + base.GetType()->ToString() + " is unreachable, the type is allocated type in expression " + expr.ToString() + "."; ErrorInFunc(*expr.GetTopLevelFunc(), errMsg); } } else if (Is(expr) || Is(expr)) { auto base = FuncCallBase(&expr); // 3. generic type in instantiated type args must be reachable const auto& instantiatedTypeArgs = base.GetInstantiatedTypeArgs(); for (size_t i = 0; i < instantiatedTypeArgs.size(); ++i) { if (!GenericTypeIsInContainer(*instantiatedTypeArgs[i], reachableGenericTypes)) { auto errMsg = "generic type " + instantiatedTypeArgs[i]->ToString() + "is unreachable, the type is " + std::to_string(i) + "-th instantiated type args in expression " + expr.ToString() + "."; ErrorInFunc(*expr.GetTopLevelFunc(), errMsg); } } // 4. generic type in ThisType must be reachable if (auto thisType = base.GetThisType()) { if (!GenericTypeIsInContainer(*thisType, reachableGenericTypes)) { auto errMsg = "generic type " + thisType->ToString() + " is unreachable, the type is ThisType in expression " + expr.ToString() + "."; ErrorInFunc(*expr.GetTopLevelFunc(), errMsg); } } } else if (auto rtti = DynamicCast(&expr)) { // 5. generic type in GetRTTIStatic must be reachable if (!GenericTypeIsInContainer(*rtti->GetRTTIType(), reachableGenericTypes)) { auto errMsg = "generic type " + rtti->GetRTTIType()->ToString() + " is unreachable, the type is rtti type in expression " + expr.ToString() + "."; ErrorInFunc(*expr.GetTopLevelFunc(), errMsg); } } else if (auto instanceOf = DynamicCast(&expr)) { // 6. generic type in InstanceOf must be reachable if (!GenericTypeIsInContainer(*instanceOf->GetType(), reachableGenericTypes)) { auto errMsg = "generic type " + instanceOf->GetType()->ToString() + " is unreachable, the type is instanceOf type in expression " + expr.ToString() + "."; ErrorInFunc(*expr.GetTopLevelFunc(), errMsg); } } else if (eKind == ExprKind::RAW_ARRAY_ALLOCATE || eKind == ExprKind::RAW_ARRAY_ALLOCATE_WITH_EXCEPTION) { auto base = RawArrayAllocateBase(&expr); // 7. generic type in RawArrayAllocate must be reachable if (!GenericTypeIsInContainer(*base.GetElementType(), reachableGenericTypes)) { auto errMsg = "generic type " + base.GetElementType()->ToString() + " is unreachable, the type is element type in expression " + expr.ToString() + "."; ErrorInFunc(*expr.GetTopLevelFunc(), errMsg); } } else if (eKind == ExprKind::INTRINSIC || eKind == ExprKind::INTRINSIC_WITH_EXCEPTION) { auto base = IntrinsicBase(&expr); // 8. generic type in Intrinsic must be reachable auto instantiatedTypeArgs = base.GetInstantiatedTypeArgs(); for (size_t i = 0; i < instantiatedTypeArgs.size(); ++i) { if (!GenericTypeIsInContainer(*instantiatedTypeArgs[i], reachableGenericTypes)) { auto errMsg = "generic type " + instantiatedTypeArgs[i]->ToString() + "is unreachable, the type is " + std::to_string(i) + "-th instantiated type args in expression " + expr.ToString() + "."; ErrorInFunc(*expr.GetTopLevelFunc(), errMsg); } } } } void CHIRChecker::CheckBlockGroup(const BlockGroup& blockGroup, const Func& topLevelFunc) { if (optionalRules.find(Rule::CHECK_FUNC_BODY) == optionalRules.end()) { return; } // 1. block group's identifier can't be empty if (blockGroup.GetIdentifier().empty()) { ErrorInFunc(topLevelFunc, "there is block group without identifier."); return; } // 2. block group's top-level function must be correct CheckTopLevelFunc(blockGroup.GetTopLevelFunc(), topLevelFunc, "block group", blockGroup.GetIdentifier()); // 3. there must be entry block in block group if (blockGroup.GetEntryBlock() == nullptr) { ErrorInFunc(topLevelFunc, "there is no entry block in block group " + blockGroup.GetIdentifier() + "."); } // 4. owner func and owner expression, there can only be one auto ownerFunc = blockGroup.GetOwnerFunc(); auto ownerExpr = blockGroup.GetOwnerExpression(); if (ownerFunc == nullptr && ownerExpr == nullptr) { ErrorInFunc(topLevelFunc, "we need to set owner func or owner expression for block group " + blockGroup.GetIdentifier() + "."); } else if (ownerFunc != nullptr && ownerExpr != nullptr) { ErrorInFunc(topLevelFunc, "we can only set owner func or owner expression, not both for block group " + blockGroup.GetIdentifier() + "."); } // 5. there must be blocks in block group auto blocks = blockGroup.GetBlocks(); if (blocks.empty()) { ErrorInFunc(topLevelFunc, "there is no block in block group " + blockGroup.GetIdentifier() + "."); } // 6. block's id can not be empty and not be same std::unordered_set ids; for (size_t i = 0; i < blocks.size(); ++i) { auto id = blocks[i]->GetIdentifier(); if (id.empty()) { ErrorInFunc(topLevelFunc, "the " + IndexToString(i) + " block's id is empty."); continue; } auto res = ids.emplace(id).second; if (!res) { ErrorInFunc(topLevelFunc, "block's id " + id + " is duplicated."); } } // 7. check every block for (auto block : blocks) { CheckBlock(*block, topLevelFunc); } } void CHIRChecker::CheckTopLevelFunc( const Func* calculatedFunc, const Func& realFunc, const std::string& valueName, const std::string& valueId) { if (calculatedFunc == nullptr) { ErrorInFunc(realFunc, "can't get top-level function from " + valueName + " " + valueId + "."); } else if (calculatedFunc != &realFunc) { auto errMsg = "get a wrong top-level function from " + valueName + " " + valueId + ".\n"; auto hint = " wrong func name is " + calculatedFunc->GetIdentifier() + "."; ErrorInFunc(realFunc, errMsg + hint); } } void CHIRChecker::CheckBlock(const Block& block, const Func& topLevelFunc) { // 1. block's identifier can't be empty if (block.GetIdentifier().empty()) { ErrorInFunc(topLevelFunc, "there is block without identifier."); return; } // 2. a block must have parent block group if (block.GetParentBlockGroup() == nullptr) { ErrorInFunc(topLevelFunc, "block " + block.GetIdentifier() + "'s parent block group is null."); } // 3. block's top-level func must be correct CheckTopLevelFunc(block.GetTopLevelFunc(), topLevelFunc, "block", block.GetIdentifier()); // 4. expressions in block can't be empty auto exprs = block.GetExpressions(); if (optionalRules.find(Rule::EMPTY_BLOCK) != optionalRules.end() && exprs.size() == 0) { ErrorInFunc(topLevelFunc, "there is no expression in block " + block.GetIdentifier() + "."); } if (!exprs.empty()) { // 5. terminator can't appear in the middle of expressions for (size_t i = 0; i + 1 < exprs.size(); i++) { auto expr = exprs[i]; if (expr->IsTerminator()) { ErrorInFunc(topLevelFunc, "terminator found in the middle of block " + block.GetIdentifier() + "."); break; } } // 6. the last expression must be terminator if (!exprs.back()->IsTerminator()) { ErrorInFunc(topLevelFunc, "the last expression in block " + block.GetIdentifier() + " is not terminator."); } else { // 7. check terminator's jump CheckTerminatorJump(*StaticCast(exprs.back()), topLevelFunc); } } // 8. check predecessors CheckPredecessors(block, topLevelFunc); // 9. check every expression for (auto e : exprs) { CheckExpression(*e, topLevelFunc); } } void CHIRChecker::CheckTerminatorJump(const Terminator& terminator, const Func& topLevelFunc) { // 1. terminator can't jump to another block group auto curBlockGroup = terminator.GetParentBlock()->GetParentBlockGroup(); for (auto suc : terminator.GetSuccessors()) { if (suc->GetParentBlockGroup() != curBlockGroup) { ErrorInFunc(topLevelFunc, "terminator " + terminator.ToString() + " in block group " + curBlockGroup->GetIdentifier() + " jumps to an unreachable block " + suc->GetIdentifier() + " in block group " + suc->GetParentBlockGroup()->GetIdentifier()); } } } void CHIRChecker::CheckPredecessors(const Block& block, const Func& topLevelFunc) { // 1. the successor of current block's predecessor must be current block for (auto b : block.GetPredecessors()) { auto successors = b->GetTerminator()->GetSuccessors(); if (std::find(successors.begin(), successors.end(), &block) == successors.end()) { ErrorInFunc(topLevelFunc, "block " + block.GetIdentifier() + "'s predecessor is " + b->GetIdentifier() + ", but block " + b->GetIdentifier() + "'s successor is not " + block.GetIdentifier() + "."); } } } void CHIRChecker::CheckExpression(const Expression& expr, const Func& topLevelFunc) { const std::unordered_map> actionMap = { {ExprMajorKind::TERMINATOR, [this, &expr, &topLevelFunc]() { CheckTerminator(expr, topLevelFunc); }}, {ExprMajorKind::UNARY_EXPR, [this, &expr, &topLevelFunc]() { CheckUnaryExpression(StaticCast(expr), topLevelFunc); }}, {ExprMajorKind::BINARY_EXPR, [this, &expr, &topLevelFunc]() { CheckBinaryExpression(StaticCast(expr), topLevelFunc); }}, {ExprMajorKind::MEMORY_EXPR, [this, &expr, &topLevelFunc]() { CheckMemoryExpression(expr, topLevelFunc); }}, {ExprMajorKind::STRUCTURED_CTRL_FLOW_EXPR, [this, &expr, &topLevelFunc]() { CheckControlFlowExpression(expr, topLevelFunc); }}, {ExprMajorKind::OTHERS, [this, &expr, &topLevelFunc]() { CheckOtherExpression(expr, topLevelFunc); }}, }; if (!expr.IsTerminator() && !CheckHaveResult(expr, topLevelFunc)) { return; } if (auto it = actionMap.find(expr.GetExprMajorKind()); it != actionMap.end()) { it->second(); } } void CHIRChecker::CheckTerminator(const Expression& expr, const Func& topLevelFunc) { const std::unordered_map> actionMap = { {ExprKind::GOTO, [this, &expr, &topLevelFunc]() { CheckGoTo(StaticCast(expr), topLevelFunc); }}, {ExprKind::EXIT, [this, &expr, &topLevelFunc]() { CheckExit(StaticCast(expr), topLevelFunc); }}, {ExprKind::RAISE_EXCEPTION, [this, &expr, &topLevelFunc]() { CheckRaiseException(StaticCast(expr), topLevelFunc); }}, {ExprKind::BRANCH, [this, &expr, &topLevelFunc]() { CheckBranch(StaticCast(expr), topLevelFunc); }}, {ExprKind::MULTIBRANCH, [this, &expr, &topLevelFunc]() { CheckMultiBranch(StaticCast(expr), topLevelFunc); }}, {ExprKind::APPLY_WITH_EXCEPTION, [this, &expr, &topLevelFunc]() { CheckApplyWithException(StaticCast(expr), topLevelFunc); }}, {ExprKind::INVOKE_WITH_EXCEPTION, [this, &expr, &topLevelFunc]() { CheckInvokeWithException(StaticCast(expr), topLevelFunc); }}, {ExprKind::INVOKESTATIC_WITH_EXCEPTION, [this, &expr, &topLevelFunc]() { CheckInvokeStaticWithException(StaticCast(expr), topLevelFunc); }}, {ExprKind::INT_OP_WITH_EXCEPTION, [this, &expr, &topLevelFunc]() { CheckIntOpWithException(StaticCast(expr), topLevelFunc); }}, {ExprKind::SPAWN_WITH_EXCEPTION, [this, &expr, &topLevelFunc]() { CheckSpawnWithException(StaticCast(expr), topLevelFunc); }}, {ExprKind::TYPECAST_WITH_EXCEPTION, [this, &expr, &topLevelFunc]() { CheckTypeCastWithException(StaticCast(expr), topLevelFunc); }}, {ExprKind::INTRINSIC_WITH_EXCEPTION, [this, &expr, &topLevelFunc]() { CheckIntrinsicWithException(StaticCast(expr), topLevelFunc); }}, {ExprKind::ALLOCATE_WITH_EXCEPTION, [this, &expr, &topLevelFunc]() { CheckAllocateWithException(StaticCast(expr), topLevelFunc); }}, {ExprKind::RAW_ARRAY_ALLOCATE_WITH_EXCEPTION, [this, &expr, &topLevelFunc]() { CheckRawArrayAllocateWithException( StaticCast(expr), topLevelFunc); }}, }; if (auto it = actionMap.find(expr.GetExprKind()); it != actionMap.end()) { it->second(); } else { WarningInExpr(topLevelFunc, expr, "find unrecongnized ExprKind `" + expr.GetExprKindName() + "."); } } void CHIRChecker::CheckGoTo(const GoTo& expr, const Func& topLevelFunc) { // 1. don't have operand OperandNumIsEqual(0, expr, topLevelFunc); // 2. only have 1 successor SuccessorNumIsEqual(1, expr, topLevelFunc); // 3. don't have result ShouldNotHaveResult(expr, topLevelFunc); } void CHIRChecker::CheckExit(const Exit& expr, const Func& topLevelFunc) { // 1. don't have operand OperandNumIsEqual(0, expr, topLevelFunc); // 2. don't have successor SuccessorNumIsEqual(0, expr, topLevelFunc); // 3. don't have result ShouldNotHaveResult(expr, topLevelFunc); } void CHIRChecker::CheckRaiseException(const RaiseException& expr, const Func& topLevelFunc) { // 1. don't have result ShouldNotHaveResult(expr, topLevelFunc); // 2. there is 1 operand at least if (!OperandNumAtLeast(1, expr, topLevelFunc)) { return; } auto exceptionValue = expr.GetExceptionValue(); auto exceptionType = exceptionValue->GetType(); // 3. exception value's type must be class ref or generic type if (!exceptionType->IsClassRef() && !exceptionType->IsGeneric()) { TypeCheckError(expr, *exceptionValue, "Class& or Generic", topLevelFunc); return; } // 4. exception value's type must be equal or sub type of class Exception or class Error auto isEqualOrSubTypeOfClassInCore = [this](ClassType& type, const std::string& className) { if (CheckCustomTypeDefIsExpected(*type.GetClassDef(), CORE_PACKAGE_NAME, className)) { return true; } for (auto superType : type.GetSuperTypesRecusively(builder)) { if (CheckCustomTypeDefIsExpected(*superType->GetClassDef(), CORE_PACKAGE_NAME, className)) { return true; } } return false; }; auto isLegalExceptionValueType = [&isEqualOrSubTypeOfClassInCore](ClassType& type) { return isEqualOrSubTypeOfClassInCore(type, CLASS_EXCEPTION) || isEqualOrSubTypeOfClassInCore(type, CLASS_ERROR) || CheckCustomTypeDefIsExpected(*type.GetClassDef(), CORE_PACKAGE_NAME, OBJECT_NAME) || // these three classes are from effect handler plan, we set white list temprorarily CheckCustomTypeDefIsExpected(*type.GetClassDef(), EFFECT_PACKAGE_NAME, "ImmediateEarlyReturn") || CheckCustomTypeDefIsExpected(*type.GetClassDef(), EFFECT_PACKAGE_NAME, "ImmediateFrameErrorWrapper") || CheckCustomTypeDefIsExpected(*type.GetClassDef(), EFFECT_PACKAGE_NAME, "ImmediateFrameExceptionWrapper"); }; auto derefType = exceptionType->StripAllRefs(); if (auto classType = DynamicCast(derefType)) { if (!isLegalExceptionValueType(*classType)) { auto errMsg = "value " + exceptionValue->GetIdentifier() + " used in " + GetExpressionString(expr) + " has type " + exceptionType->ToString() + ", but this type isn't class Exception, class Error or their sub type."; ErrorInFunc(topLevelFunc, errMsg); } } else if (auto genericType = DynamicCast(derefType)) { bool ok = false; for (auto upperBound : genericType->GetUpperBounds()) { if (isLegalExceptionValueType(*StaticCast(upperBound->StripAllRefs()))) { ok = true; break; } } if (!ok) { auto errMsg = "value " + exceptionValue->GetIdentifier() + " used in " + GetExpressionString(expr) + " has type " + exceptionType->ToString() + ", but none of its upper bounds is class Exception, class Error or their sub type."; ErrorInFunc(topLevelFunc, errMsg); } } } void CHIRChecker::CheckBranch(const Branch& expr, const Func& topLevelFunc) { // 1. don't have result ShouldNotHaveResult(expr, topLevelFunc); // 2. only have 1 operand if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } // 3. have 2 successors if (!SuccessorNumIsEqual(2, expr, topLevelFunc)) { return; } // 4. condition type must be Bool or Nothing auto condType = expr.GetCondition()->GetType(); if (!condType->IsBoolean() && !condType->IsNothing()) { TypeCheckError(expr, *expr.GetCondition(), "Bool", topLevelFunc); } } void CHIRChecker::CheckMultiBranch(const MultiBranch& expr, const Func& topLevelFunc) { // 1. don't have result ShouldNotHaveResult(expr, topLevelFunc); // 2. only have 1 operand if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } // 3. have 2 successors at least, one is default block, others are normal blocks if (!SuccessorNumAtLeast(2, expr, topLevelFunc)) { return; } // 4. condition type must be Int auto condType = expr.GetCondition()->GetType(); if (!condType->IsInteger()) { TypeCheckError(expr, *expr.GetCondition(), "Int", topLevelFunc); } // 5. normal blocks' size must equal to cases' size if (expr.GetNormalBlocks().size() != expr.GetCaseVals().size()) { auto errMsg = "value's size and normal block's size must be equal, but now there are " + std::to_string(expr.GetCaseVals().size()) + " value(s) and " + std::to_string(expr.GetNormalBlocks().size()) + "normal block(s)."; ErrorInExpr(topLevelFunc, expr, errMsg); } // 6. must have default block if (expr.GetDefaultBlock() == nullptr) { ErrorInExpr(topLevelFunc, expr, "default block shouldn't be nullptr."); } } void CHIRChecker::CheckApplyWithException(const ApplyWithException& expr, const Func& topLevelFunc) { // 1. have result if (!CheckHaveResult(expr, topLevelFunc)) { return; } // 2. there is 1 operand at least, must have callee if (!OperandNumAtLeast(1, expr, topLevelFunc)) { return; } // 3. must have 2 successors, normal block and exception block if (!SuccessorNumIsEqual(2, expr, topLevelFunc)) { return; } CheckApplyBase(ApplyBase(&expr), topLevelFunc); } void CHIRChecker::CheckApplyBase(const ApplyBase& expr, const Func& topLevelFunc) { // 1. if this Apply can't be executed in runtime, we don't need to check if (IsUnreachableApply(expr.GetThisType())) { return; } // 2. check callee if (!CheckCallee(*expr.GetCallee(), *expr.GetRawExpr(), topLevelFunc)) { return; } // 3. check instantiated type args std::vector genericTypeParams; if (auto func = DynamicCast(expr.GetCallee())) { genericTypeParams = func->GetGenericTypeParams(); } else if (auto localVar = DynamicCast(expr.GetCallee())) { if (auto lambda = DynamicCast(localVar->GetExpr())) { genericTypeParams = lambda->GetGenericTypeParams(); } } auto instTypeArgs = expr.GetInstantiatedTypeArgs(); if (!CheckInstantiatedTypeArgs(instTypeArgs, genericTypeParams, *expr.GetRawExpr(), topLevelFunc)) { return; } // 4. check thisType if (!CheckApplyThisType(*expr.GetCallee(), expr.GetThisType(), *expr.GetRawExpr(), topLevelFunc)) { return; } // 5. check func args auto calleeType = StaticCast(expr.GetCallee()->GetType()); auto instFuncType = CalculateInstFuncType( *calleeType, instTypeArgs, genericTypeParams, expr.GetInstParentCustomTyOfCallee(builder)); CheckApplyFuncArgs( expr.GetArgs(), instFuncType->GetParamTypes(), calleeType->HasVarArg(), *expr.GetRawExpr(), topLevelFunc); // 6. check return value CheckApplyFuncRetValue(*instFuncType->GetReturnType(), *expr.GetRawExpr(), topLevelFunc); } bool CHIRChecker::CheckCallee(const Value& callee, const Expression& expr, const Func& topLevelFunc) { // 1. callee's type must be func type if (!callee.GetType()->IsFunc()) { ErrorInFunc(topLevelFunc, "callee of `" + GetExpressionString(expr) + "` should have func type, not " + callee.GetType()->ToString() + "."); return false; } return true; } bool CHIRChecker::CheckInstantiatedTypeArgs(const std::vector& instantiatedTypeArgs, const std::vector& genericTypeParams, const Expression& expr, const Func& topLevelFunc) { // 1. instantiated type args' size must equal to generic type params' size if (instantiatedTypeArgs.size() != genericTypeParams.size()) { auto errMsg = "size mismatched, there are " + std::to_string(instantiatedTypeArgs.size()) + " instantiated type args in function call `" + GetExpressionString(expr) + "`, but there are " + std::to_string(genericTypeParams.size()) + " generic type args in function declare."; ErrorInFunc(topLevelFunc, errMsg); return false; } return true; } bool CHIRChecker::CheckThisTypeIsEqualOrSubTypeOfFuncParentType( Type& thisType, const FuncBase& func, const Expression& expr, const Func& topLevelFunc) { CJC_ASSERT(thisType.IsBuiltinType() || thisType.IsCustomType()); auto funcParentType = func.GetParentCustomTypeOrExtendedType(); // builtin type can compare type directly if (thisType.IsBuiltinType() && funcParentType->IsBuiltinType()) { return &thisType == funcParentType; } auto funcParentCustomType = DynamicCast(funcParentType); if (auto thisCustomType = DynamicCast(&thisType); thisCustomType && funcParentCustomType && thisCustomType->GetCustomTypeDef() == funcParentCustomType->GetCustomTypeDef()) { return true; } for (auto parentTy : thisType.GetSuperTypesRecusively(builder)) { // change to generic def to compare if (parentTy->GetClassDef() == funcParentCustomType->GetCustomTypeDef()) { return true; } } auto parentDef = func.GetParentCustomTypeDef(); CJC_NULLPTR_CHECK(parentDef); auto errMsg = "callee is " + func.GetIdentifier() + ", its parent custom type def is " + parentDef->GetIdentifier() + ", but ThisType is " + thisType.ToString() + ", not this parent custom type or its sub type."; ErrorInExpr(topLevelFunc, expr, errMsg); return false; } bool CHIRChecker::CheckApplyThisType( const Value& callee, const Type* thisType, const Expression& expr, const Func& topLevelFunc) { Type* thisDerefTy = thisType == nullptr ? nullptr : thisType->StripAllRefs(); // 1. thisType must be builtin type, custom type, generic type or nullptr /** * generic type is a little weird, but the following code is allowed in Cangjie: * class A { * static public func foo() {} * } * func goo() where T <: A { * T.foo() // will be translated to `Apply` * } * generic type T's constraint is a non-open class `A`, that means `T` can only be `A` * so we can translate `T.foo` to `Apply`, not `Invoke`, for better runtime performance */ if (thisDerefTy != nullptr && !thisDerefTy->IsBuiltinType() && !thisDerefTy->IsCustomType() && !thisDerefTy->IsGeneric()) { auto errMsg = "ThisType must be builtin type, custom type or generic type can't be " + thisType->ToString() + "."; ErrorInExpr(topLevelFunc, expr, errMsg); return false; } auto func = DynamicCast(&callee); // 2. thisType must be nullptr if callee Parameter or LocalVar if (func == nullptr && thisDerefTy != nullptr) { auto errMsg = "callee isn't member method, but there is ThisType: " + thisType->ToString() + "."; ErrorInExpr(topLevelFunc, expr, errMsg); return false; } // 3. if callee is Paramter or LocalVar, just return if (func == nullptr) { return true; } // 4. thisType can't be nullptr if callee is member method if (func->IsMemberFunc() && thisDerefTy == nullptr) { auto errMsg = "callee is member method, but there is no ThisType."; ErrorInExpr(topLevelFunc, expr, errMsg); return false; } // 5. thisType must be nullptr if callee isn't member method if (!func->IsMemberFunc() && thisDerefTy != nullptr) { auto errMsg = "callee isn't member method, but there is ThisType: " + thisType->ToString() + "."; ErrorInExpr(topLevelFunc, expr, errMsg); return false; } if (!func->IsMemberFunc() && thisDerefTy == nullptr) { return true; } // 6. thisType must be equal or sub type of callee's parent type if (thisDerefTy->IsBuiltinType() || thisDerefTy->IsCustomType()) { return CheckThisTypeIsEqualOrSubTypeOfFuncParentType(*thisDerefTy, *func, expr, topLevelFunc); } else if (auto gType = DynamicCast(thisDerefTy)) { // 7. we will know if this func can be dynamic dispatched by judging func and upper bound if (!FuncCanBeDynamicDispatch(*func)) { return true; } for (auto upperBound : gType->GetUpperBounds()) { if (!StaticCast(upperBound->StripAllRefs())->GetCustomTypeDef()->CanBeInherited()) { return true; } } auto errMsg = "ThisType is " + thisType->ToString() + ", but its all upper bounds can be inherited and callee can be dynamic dispatched, " + "so you should use `Invoke`, not `Apply`."; ErrorInExpr(topLevelFunc, expr, errMsg); return false; } auto errMsg = "ThisType is " + thisType->ToString() + ", an unknown type."; ErrorInExpr(topLevelFunc, expr, errMsg); return false; } FuncType* CHIRChecker::CalculateInstFuncType( FuncType& originalFuncType, const std::vector& instantiatedTypeArgs, const std::vector& genericTypeParams, Type* instOuterType) { std::unordered_map replaceTable; if (auto customType = DynamicCast(instOuterType)) { replaceTable = GetInstMapFromCurDefAndExDefToCurType(*customType); } CJC_ASSERT(genericTypeParams.size() == instantiatedTypeArgs.size()); for (size_t i = 0; i < genericTypeParams.size(); ++i) { replaceTable.emplace(genericTypeParams[i], instantiatedTypeArgs[i]); } return StaticCast(ReplaceRawGenericArgType(originalFuncType, replaceTable, builder)); } void CHIRChecker::CheckApplyFuncArgs(const std::vector& args, const std::vector& instParamTypes, bool varArgs, const Expression& expr, const Func& topLevelFunc) { // 1. don't check variable args's size // 2. func args' size must be func params' size if (!varArgs && args.size() != instParamTypes.size()) { auto errMsg = "size mismatched, there are " + std::to_string(args.size()) + " arg(s) in function call `" + GetExpressionString(expr) + "`, but there are " + std::to_string(instParamTypes.size()) + " parameter(s) in function declare."; ErrorInFunc(topLevelFunc, errMsg); return; } // 3. func arg can set to func param for (size_t i = 0; i < instParamTypes.size(); ++i) { if (!TypeIsExpected(*args[i]->GetType(), *instParamTypes[i])) { TypeCheckError(expr, *args[i], instParamTypes[i]->ToString(), topLevelFunc); } } } void CHIRChecker::CheckApplyFuncRetValue(const Type& instRetType, const Expression& expr, const Func& topLevelFunc) { // 1. check func return value's type, `instRetType` should be src, the second condition is a hack, // because some functions' return type is `This`, need to fix auto retValue = expr.GetResult(); CJC_NULLPTR_CHECK(retValue); if (!TypeIsExpected(instRetType, *retValue->GetType()) && !TypeIsExpected(*retValue->GetType(), instRetType)) { TypeCheckError(expr, *retValue, instRetType.ToString(), topLevelFunc); } } void CHIRChecker::CheckInvokeWithException(const InvokeWithException& expr, const Func& topLevelFunc) { // 1. have result if (!CheckHaveResult(expr, topLevelFunc)) { return; } // 2. there is 1 operand at least, must have object if (!OperandNumAtLeast(1, expr, topLevelFunc)) { return; } // 3. must have 2 successors, normal block and exception block if (!SuccessorNumIsEqual(2, expr, topLevelFunc)) { return; } CheckInvokeBase(InvokeBase(&expr), topLevelFunc); } void CHIRChecker::CheckInvokeBase(const InvokeBase& expr, const Func& topLevelFunc) { // 1. check instantiated type args const auto& genericTypeParams = expr.GetGenericTypeParams(); auto instTypeArgs = expr.GetInstantiatedTypeArgs(); if (!CheckInstantiatedTypeArgs(instTypeArgs, genericTypeParams, *expr.GetRawExpr(), topLevelFunc)) { return; } // 2. check thisType if (!CheckInvokeThisType(*expr.GetObject()->GetType(), expr.GetThisType(), *expr.GetRawExpr(), topLevelFunc)) { return; } // a hack way, skip AutoEnvBase for now, we will fix later auto thisType = expr.GetThisType()->StripAllRefs(); if (!thisType->IsAutoEnvBase()) { // 3. check vritual method auto virMethodCtx = VirMethodFullContext { .srcCodeIdentifier = expr.GetMethodName(), .originalFuncType = expr.GetMethodType(), .genericTypeParams = expr.GetGenericTypeParams(), .offset = expr.GetVirtualMethodOffset(), .thisType = thisType, .srcParentType = expr.GetInstSrcParentCustomTypeOfMethod(builder) }; if (!CheckVirtualMethod(virMethodCtx, *expr.GetRawExpr(), topLevelFunc)) { return; } } // 4. check func args auto paramTypes = expr.GetMethodType()->GetParamTypes(); CheckInvokeFuncArgs(expr.GetArgs(), paramTypes, *expr.GetRawExpr(), topLevelFunc); } bool CHIRChecker::CheckInvokeThisType( Type& objType, const Type* thisType, const Expression& expr, const Func& topLevelFunc) { // 1. there must be thisType in `Invoke` and `InvokeStatic` if (thisType == nullptr) { auto errMsg = "we must have `ThisType`, can't be nullptr."; ErrorInExpr(topLevelFunc, expr, errMsg); return false; } auto thisDerefTy = thisType->StripAllRefs(); // 2. thisType must be builtin type, custom type, `This` or generic type if (!thisDerefTy->IsBuiltinType() && !thisDerefTy->IsCustomType() && !thisDerefTy->IsThis() && !thisDerefTy->IsGeneric()) { auto errMsg = "ThisType must be builtin type, custom type, `This` type or generic type, can't be " + thisType->ToString() + "."; ErrorInExpr(topLevelFunc, expr, errMsg); return false; } if (auto genericType = DynamicCast(thisDerefTy)) { const auto& upperBounds = genericType->GetUpperBounds(); // 3. thisType must have upper bounds if it's generic type if (upperBounds.empty()) { auto errMsg = "ThisType is " + thisType->ToString() + ", a generic type, but it doesn't have upper bounds."; ErrorInExpr(topLevelFunc, expr, errMsg); return false; } auto objDerefType = objType.StripAllRefs(); bool ok = false; if (auto gType = DynamicCast(objDerefType)) { ok = Generic1IsEqualOrSubTypeOfGeneric2(*genericType, *gType, builder); } else { // 4. object's type must be parent type of one of thisType's upper bounds /** * interface I1 { * func foo() {} * } * interface I2 <: I1 {} * interface I3 {} * func goo(a: T) where T <: I2 & I3 { * a.foo() // a's type is `I1`, thisType's upper bounds are {I2, I3} * // a's type only need to be I2's parent type, not be all upper bounds' parent type * } */ for (auto upperBound : upperBounds) { if (upperBound->StripAllRefs()->IsEqualOrSubTypeOf(*objDerefType, builder)) { ok = true; break; } } } if (!ok) { auto errMsg = "type mismatched, in `" + GetExpressionString(expr) + "`, ThisType is " + thisType->ToString() + ", its upper bounds are " + VectorTypesToString("(", genericType->GetUpperBounds(), ")") + ", but object's type is " + objType.ToString() + ", any one of ThisType's upper bounds is not equal or sub type of object's type."; ErrorInFunc(topLevelFunc, errMsg); return false; } } else if (thisDerefTy->IsThis()) { // 5. `This` must be used in expression which is created by member method if (!topLevelFunc.IsMemberFunc()) { auto errMsg = "use a `This` type in `" + GetExpressionString(expr) + "`, this expression is in function `" + topLevelFunc.GetIdentifier() + "` which isn't member method."; ErrorInFunc(topLevelFunc, errMsg); return false; } /** * open class A { * open public func foo() {} * } * extend A { * public func goo() { * foo() // this `foo` should be invoked by `This&` * } * } * so we need to check `class A` whether can be inherited, not `extend A`, * so we should use `GetOuterDeclaredOrExtendedDef`, not `GetParentCustomTypeDef` */ auto parentDef = topLevelFunc.GetOuterDeclaredOrExtendedDef(); // 6. custom type def must be inheritable if (parentDef == nullptr || !parentDef->CanBeInherited()) { auto errMsg = "use a `This` type in `" + GetExpressionString(expr) + "`, this expression is in function `" + topLevelFunc.GetIdentifier() + "` which is member method of `" + topLevelFunc.GetParentCustomTypeDef()->GetIdentifier() + "`, but this type can't be inherited."; ErrorInFunc(topLevelFunc, errMsg); return false; } } else if (!thisDerefTy->IsEqualOrSubTypeOf(*objType.StripAllRefs(), builder)) { // 7. thisType is custom type or builtin type now, so object's type must be thisType's parent type auto errMsg = "type mismatched, in `" + GetExpressionString(expr) + "`, ThisType is " + thisType->ToString() + ", but object's type is " + objType.ToString() + ", ThisType is not equal or sub type of object's type."; ErrorInFunc(topLevelFunc, errMsg); return false; } return true; } bool CHIRChecker::CheckVirtualMethod( const VirMethodFullContext& methodCtx, const Expression& expr, const Func& topLevelFunc) { // 1. check vtable must exist auto vtable = CheckVTableExist(*methodCtx.thisType, *methodCtx.srcParentType, expr, topLevelFunc); if (vtable.empty()) { return false; } // 2. offset can't be out of bounds if (methodCtx.offset >= vtable.size()) { auto errMsg = "invoke a wrong virtual method in " + GetExpressionString(expr) + ", parent type " + methodCtx.srcParentType->ToString() + " in vtable of " + methodCtx.thisType->ToString() + ", only has " + std::to_string(vtable.size()) + " virtual method(s), but the offset is " + std::to_string(methodCtx.offset) + "."; ErrorInFunc(topLevelFunc, errMsg); return false; } const auto& funcInfo = vtable[methodCtx.offset]; auto errMsgBase = "invoke a wrong virtual method in " + GetExpressionString(expr) + ", in vtable of [" + methodCtx.thisType->ToString() + "][" + methodCtx.srcParentType->ToString() + "], the " + std::to_string(methodCtx.offset) + "-th "; // 3. `InvokeStatic` must call a static member method // 4. `Invoke` must call a non-static member method if (expr.IsInvokeStaticBase() && !funcInfo.attr.TestAttr(Attribute::STATIC)) { auto errMsg = errMsgBase + "virtual method is not static, `InvokeStatic` should call a static method."; ErrorInFunc(topLevelFunc, errMsg); return false; } else if (!expr.IsInvokeStaticBase() && funcInfo.attr.TestAttr(Attribute::STATIC)) { auto errMsg = errMsgBase + "virtual method is static, `Invoke` shouldn't call a static method."; ErrorInFunc(topLevelFunc, errMsg); return false; } bool result = true; // 5. src code identifer must be same if (funcInfo.srcCodeIdentifier != methodCtx.srcCodeIdentifier) { auto errMsg = errMsgBase + "virtual method's name is " + funcInfo.srcCodeIdentifier + ", but the method name in expression is " + methodCtx.srcCodeIdentifier + "."; ErrorInFunc(topLevelFunc, errMsg); result = false; } return result; } void CHIRChecker::CheckInvokeFuncArgs(const std::vector& args, const std::vector& originalParamTypes, const Expression& expr, const Func& topLevelFunc) { if (args.size() != originalParamTypes.size()) { auto errMsg = "size mismatched, there are " + std::to_string(args.size()) + " arg(s) in function call `" + GetExpressionString(expr) + "`, but there are " + std::to_string(originalParamTypes.size()) + " parameter(s) in function declare."; ErrorInFunc(topLevelFunc, errMsg); return; } for (size_t i = 0; i < originalParamTypes.size(); ++i) { // a hack way, skip to check 1st param type, will check it later if (i == 0) { continue; } if (!InstTypeCanSetToGenericRelatedType(*args[i]->GetType(), *originalParamTypes[i])) { TypeCheckError(expr, *args[i], originalParamTypes[i]->ToString(), topLevelFunc); } } } bool CHIRChecker::InstTypeCanSetToGenericRelatedType(Type& instType, const Type& genericRelatedType) { if (instType.IsNothing() || genericRelatedType.IsAny() || &instType == &genericRelatedType) { return true; } if (instType.IsRef() && genericRelatedType.IsRef()) { auto instBaseType = Cangjie::StaticCast(instType).GetBaseType(); auto genericBaseType = Cangjie::StaticCast(genericRelatedType).GetBaseType(); return InstTypeCanSetToGenericRelatedType(*instBaseType, *genericBaseType); } auto instCustomType = Cangjie::DynamicCast(&instType); auto genericCustomType = Cangjie::DynamicCast(&genericRelatedType); if (instCustomType != nullptr && genericCustomType != nullptr) { // 1. `Class-A` can be set to `Class-A`, only if `Int64` satisfy Generic-T's constraints if (instCustomType->GetCustomTypeDef() == genericCustomType->GetCustomTypeDef()) { return InstTypeArgsSatisfyGenericConstraints( instCustomType->GetGenericArgs(), genericCustomType->GetGenericArgs()); } else { // 2. `Class-A` can be set to `Class-I`, only if Class-A's parent type satisfy Class-I's constraints /** * interface I1 {} * interface I2 {} * interface I3 where T <: I1 { * func foo(this: I3) {} * } * open class A <: I3 {} * open class B <: I3 {} * var x: A = xxx * x.foo() // Class-A's parent type is `Class-I3`, satisfy Class-I3's constraints * var y: B = xxx * y.foo() // Class-B's parent type is `Class-I3`, doesn't satisfy Class-I3's constraints * // this case failed to pass Sema check, but you can write ir by CHIR directly */ auto superTypes = Cangjie::StaticCast(instCustomType->StripAllRefs())->GetSuperTypesRecusively(builder); for (auto superType : superTypes) { if (superType->GetCustomTypeDef() == genericCustomType->GetCustomTypeDef() && InstTypeArgsSatisfyGenericConstraints( superType->GetGenericArgs(), genericCustomType->GetGenericArgs())) { return true; } } return false; } } if (auto genericType = Cangjie::DynamicCast(&genericRelatedType)) { if (auto gType = Cangjie::DynamicCast(&instType)) { // 3. `Generic-U` can set to `Generic-T`, only if Generic-U's upper bounds satisfy Generic-T's constraints return Generic1IsEqualOrSubTypeOfGeneric2(*gType, *genericType, builder); } else { // 4. `Class-A` can set to `Generic-T`, only if `Class-A` can satisfy Generic-T's constraints std::unordered_map emptyMap; return instType.SatisfyGenericConstraints(*genericType, builder, emptyMap); } } if (instType.GetTypeKind() != genericRelatedType.GetTypeKind()) { return false; } auto instTypeArgs = instType.GetTypeArgs(); auto genericTypeArgs = genericRelatedType.GetTypeArgs(); if (instTypeArgs.size() != genericTypeArgs.size()) { return false; } for (size_t i = 0; i < instTypeArgs.size(); ++i) { if (!InstTypeCanSetToGenericRelatedType(*instTypeArgs[i], *genericTypeArgs[i])) { return false; } } return true; } bool CHIRChecker::InstTypeArgsSatisfyGenericConstraints( const std::vector& instTypeArgs, const std::vector& genericTypeArgs) { if (instTypeArgs.size() != genericTypeArgs.size()) { return false; } for (size_t i = 0; i < instTypeArgs.size(); ++i) { if (!InstTypeCanSetToGenericRelatedType(*instTypeArgs[i], *genericTypeArgs[i])) { return false; } } return true; } std::vector CHIRChecker::CheckVTableExist(const BuiltinType& thisType, const ClassType& srcParentType) { for (auto extendDef : thisType.GetExtends(&builder)) { auto [res, replaceTable] = extendDef->GetExtendedType()->CalculateGenericTyMapping(thisType); CJC_ASSERT(res); for (auto& it : extendDef->GetVTable()) { auto instType = ReplaceRawGenericArgType(*it.first, replaceTable, builder); if (instType == &srcParentType) { return it.second; } } } return std::vector{}; } std::vector CHIRChecker::CheckVTableExist(const CustomType& thisType, const ClassType& srcParentType) { auto replaceTable = GetInstMapFromCurDefToCurType(thisType); for (auto& it : thisType.GetCustomTypeDef()->GetVTable()) { auto instType = ReplaceRawGenericArgType(*it.first, replaceTable, builder); if (instType == &srcParentType) { return it.second; } } for (auto extendDef : thisType.GetCustomTypeDef()->GetExtends()) { // maybe we can meet `extend A> {}`, and `curType` is A, then ignore this def, // so not need to check `res` auto [res, replaceTable2] = extendDef->GetExtendedType()->CalculateGenericTyMapping(thisType); for (auto& it : extendDef->GetVTable()) { auto instType = ReplaceRawGenericArgType(*it.first, replaceTable2, builder); if (instType == &srcParentType) { return it.second; } } } return std::vector{}; } std::vector CHIRChecker::CheckVTableExist(const ClassType& srcParentType, const Func& topLevelFunc) { auto parentType = topLevelFunc.GetParentCustomTypeOrExtendedType(); CJC_NULLPTR_CHECK(parentType); if (auto builtinType = DynamicCast(parentType)) { for (auto def : GetBuiltinTypeWithVTable(*builtinType, builder)->GetExtends()) { for (auto& it : def->GetVTable()) { if (it.first == &srcParentType) { return it.second; } } } } else { auto customType = StaticCast(parentType); auto customDef = customType->GetCustomTypeDef(); auto [res, replaceTable] = customDef->GetType()->CalculateGenericTyMapping(*customType); CJC_ASSERT(res); for (auto& it : customDef->GetVTable()) { if (ReplaceRawGenericArgType(*it.first, replaceTable, builder) == &srcParentType) { return it.second; } } for (auto extendDef : customDef->GetExtends()) { auto [res2, replaceTable2] = extendDef->GetType()->CalculateGenericTyMapping(*customType); if (!res2) { continue; } for (auto& it : extendDef->GetVTable()) { if (ReplaceRawGenericArgType(*it.first, replaceTable2, builder) == &srcParentType) { return it.second; } } } } return std::vector{}; } std::vector CHIRChecker::CheckVTableExist(const GenericType& thisType, const ClassType& srcParentType) { for (auto upperBound : thisType.GetUpperBounds()) { auto res = CheckVTableExist(*StaticCast(upperBound->StripAllRefs()), srcParentType); if (!res.empty()) { return res; } } return std::vector{}; } std::vector CHIRChecker::CheckVTableExist( const Type& thisType, const ClassType& srcParentType, const Expression& expr, const Func& topLevelFunc) { // 1. in thisType's vtable, we must find `srcParentType` std::vector res; if (auto bType = DynamicCast(&thisType)) { res = CheckVTableExist(*bType, srcParentType); } else if (auto cType = DynamicCast(&thisType)) { res = CheckVTableExist(*cType, srcParentType); } else if (auto tType = DynamicCast(&thisType)) { res = CheckVTableExist(srcParentType, topLevelFunc); } else if (auto gType = DynamicCast(&thisType)) { res = CheckVTableExist(*gType, srcParentType); } else { CJC_ABORT(); } if (res.empty()) { std::string errMsg; if (thisType.IsBuiltinType() || thisType.IsCustomType()) { errMsg = "invoke a wrong virtual method in `" + GetExpressionString(expr) + "`, we can't find super type " + srcParentType.ToString() + " in type " + thisType.ToString() + "."; } else if (thisType.IsThis()) { auto parentDef = topLevelFunc.GetParentCustomTypeDef(); CJC_NULLPTR_CHECK(parentDef); errMsg = "invoke a wrong virtual method in " + GetExpressionString(expr) + ", we can't find super type " + srcParentType.ToString() + " in custom type def " + parentDef->GetIdentifier() + "."; } else if (thisType.IsGeneric()) { errMsg = "invoke a wrong virtual method in " + GetExpressionString(expr) + ", we can't find super type " + srcParentType.ToString() + " in all upper bounds of " + thisType.ToString() + ".\n"; auto hint = " upper bounds are " + VectorTypesToString("(", StaticCast(thisType).GetUpperBounds(), ")") + "."; errMsg += hint; } ErrorInFunc(topLevelFunc, errMsg); } return res; } void CHIRChecker::CheckInvokeStaticWithException(const InvokeStaticWithException& expr, const Func& topLevelFunc) { // 1. have result if (!CheckHaveResult(expr, topLevelFunc)) { return; } // 2. there is 1 operand at least, must have rtti if (!OperandNumAtLeast(1, expr, topLevelFunc)) { return; } // 3. must have 2 successors, normal block and exception block if (!SuccessorNumIsEqual(2, expr, topLevelFunc)) { return; } CheckInvokeStaticBase(InvokeStaticBase(&expr), topLevelFunc); } void CHIRChecker::CheckInvokeStaticBase(const InvokeStaticBase& expr, const Func& topLevelFunc) { // 1. check instantiated type args const auto& genericTypeParams = expr.GetGenericTypeParams(); auto instTypeArgs = expr.GetInstantiatedTypeArgs(); if (!CheckInstantiatedTypeArgs(instTypeArgs, genericTypeParams, *expr.GetRawExpr(), topLevelFunc)) { return; } // 2. check rtti auto rtti = DynamicCast(expr.GetRTTIValue()); if (rtti == nullptr) { auto errMsg = "its first operand must be from `GetRTTI` or `GetRTTIStatic`."; ErrorInExpr(topLevelFunc, *expr.GetRawExpr(), errMsg); return; } Type* objectType = nullptr; if (auto getRtti = DynamicCast(rtti->GetExpr())) { objectType = getRtti->GetOperand()->GetType(); } else if (auto getRttiStatic = DynamicCast(rtti->GetExpr())) { objectType = getRttiStatic->GetRTTIType(); } else { auto errMsg = "its first operand must be from `GetRTTI` or `GetRTTIStatic`."; ErrorInExpr(topLevelFunc, *expr.GetRawExpr(), errMsg); return; } // 3. check thisType if (!CheckInvokeThisType(*objectType, expr.GetThisType(), *expr.GetRawExpr(), topLevelFunc)) { return; } // 4. check vritual method auto virMethodCtx = VirMethodFullContext { .srcCodeIdentifier = expr.GetMethodName(), .originalFuncType = expr.GetMethodType(), .genericTypeParams = expr.GetGenericTypeParams(), .offset = expr.GetVirtualMethodOffset(), .thisType = expr.GetThisType()->StripAllRefs(), .srcParentType = expr.GetInstSrcParentCustomTypeOfMethod(builder) }; if (!CheckVirtualMethod(virMethodCtx, *expr.GetRawExpr(), topLevelFunc)) { return; } // 5. check func args auto paramTypes = expr.GetMethodType()->GetParamTypes(); CheckInvokeFuncArgs(expr.GetArgs(), paramTypes, *expr.GetRawExpr(), topLevelFunc); } void CHIRChecker::CheckIntOpWithException(const IntOpWithException& expr, const Func& topLevelFunc) { // 1. must have result if (!CheckHaveResult(expr, topLevelFunc)) { return; } // 2. only have 1 or 2 operands if (!OperandNumIsEqual({1, 2}, expr, topLevelFunc)) { return; } // 3. have 2 successors if (!SuccessorNumIsEqual(2, expr, topLevelFunc)) { return; } // 4. check all kinds of expressions auto exprKind = expr.GetOpKind(); if (exprKind >= ExprKind::NEG && exprKind <= ExprKind::BITNOT) { CheckUnaryExprBase(UnaryExprBase(&expr), topLevelFunc); } else if (exprKind >= ExprKind::ADD && exprKind <= ExprKind::OR) { CheckBinaryExprBase(BinaryExprBase(&expr), topLevelFunc); } else { WarningInExpr(topLevelFunc, expr, "find unrecongnized ExprKind " + expr.GetOpKindName() + "."); } } void CHIRChecker::CheckUnaryExprBase(const UnaryExprBase& expr, const Func& topLevelFunc) { // 1. skip checking if operand type is Nothing auto operand = expr.GetOperand(); auto operandType = operand->GetType(); if (operandType->IsNothing()) { return; } // 2. check operand type if (expr.GetOpKind() == ExprKind::NOT && !operandType->IsBoolean()) { // 2.1 must be Bool when it's NOT (!a) TypeCheckError(*expr.GetRawExpr(), *operand, "Bool", topLevelFunc); } else if (expr.GetOpKind() == ExprKind::BITNOT && !operandType->IsInteger()) { // 2.2 must be Int when it's BITNOT (!a) TypeCheckError(*expr.GetRawExpr(), *operand, "Int", topLevelFunc); } else if (expr.GetOpKind() == ExprKind::NEG && !operandType->IsInteger() && !operandType->IsFloat()) { // 2.3 must be Int or Float when it's NEG (-a) TypeCheckError(*expr.GetRawExpr(), *operand, "Int or Float", topLevelFunc); } // 3. result type must equal to operand type auto result = expr.GetResult(); auto resultType = result->GetType(); if (operandType != resultType) { TypeCheckError(*expr.GetRawExpr(), *result, operandType->ToString(), topLevelFunc); } // 4. check overflow strategy if (expr.GetOpKind() == ExprKind::NEG) { OverflowStrategyMustBeValid(expr.GetOverflowStrategy(), *expr.GetRawExpr(), topLevelFunc); } } void CHIRChecker::CheckBinaryExprBase(const BinaryExprBase& expr, const Func& topLevelFunc) { // 1. skip checking if operand type is Nothing if (expr.GetLHSOperand()->GetType()->IsNothing() || expr.GetRHSOperand()->GetType()->IsNothing()) { return; } // 2. check all kinds of binary expressions auto exprKind = expr.GetOpKind(); if (exprKind >= ExprKind::ADD && exprKind <= ExprKind::MOD) { CheckCalculExpression(expr, topLevelFunc); } else if (exprKind == ExprKind::EXP) { CheckExponentiationExpression(expr, topLevelFunc); } else if (exprKind >= ExprKind::LSHIFT && exprKind <= ExprKind::BITXOR) { CheckBitExpression(expr, topLevelFunc); } else if (exprKind >= ExprKind::LT && exprKind <= ExprKind::NOTEQUAL) { CheckCompareExpression(expr, topLevelFunc); } else if (exprKind >= ExprKind::AND && exprKind <= ExprKind::OR) { CheckLogicExpression(expr, topLevelFunc); } else { WarningInExpr(topLevelFunc, *expr.GetRawExpr(), "find unrecongnized ExprKind " + expr.GetExprKindName() + "."); } } void CHIRChecker::OverflowStrategyMustBeValid( const OverflowStrategy& ofs, const Expression& expr, const Func& topLevelFunc) { // we will add `NA` later if (ofs == OverflowStrategy::OVERFLOW_STRATEGY_END) { ErrorInExpr(topLevelFunc, expr, "overflow strategy is invalid."); } } void CHIRChecker::CheckCalculExpression(const BinaryExprBase& expr, const Func& topLevelFunc) { auto leftOperand = expr.GetLHSOperand(); auto rightOperand = expr.GetRHSOperand(); auto leftOpType = leftOperand->GetType(); auto rightOpType = rightOperand->GetType(); auto result = expr.GetResult(); // 1. check operand type if (expr.GetOpKind() == ExprKind::MOD) { // 1.1 mod's operands' type must be Int or UInt, can't be Bool or Float if (!leftOpType->IsInteger()) { TypeCheckError(*expr.GetRawExpr(), *leftOperand, "Int or UInt", topLevelFunc); } if (!rightOpType->IsInteger()) { TypeCheckError(*expr.GetRawExpr(), *rightOperand, "Int or UInt", topLevelFunc); } } else { // 1.2 other operands' type must be numeric type if (!leftOpType->IsNumeric()) { TypeCheckError(*expr.GetRawExpr(), *leftOperand, "numeric", topLevelFunc); } if (!rightOpType->IsNumeric()) { TypeCheckError(*expr.GetRawExpr(), *rightOperand, "numeric", topLevelFunc); } } // 2. left operand's type, right operand's type and result type must be same if (leftOpType != rightOpType) { auto errMsg = "left operand and right operand don't have same type in " + result->ToString() + "."; ErrorInFunc(topLevelFunc, errMsg); } else if (result->GetType() != leftOpType) { auto errMsg = "the result value and operand don't have same type in " + result->ToString() + "."; ErrorInFunc(topLevelFunc, errMsg); } // 3. check overflow strategy OverflowStrategyMustBeValid(expr.GetOverflowStrategy(), *expr.GetRawExpr(), topLevelFunc); } void CHIRChecker::CheckExponentiationExpression(const BinaryExprBase& expr, const Func& topLevelFunc) { auto leftOperand = expr.GetLHSOperand(); auto rightOperand = expr.GetRHSOperand(); auto leftOpType = leftOperand->GetType(); auto rightOpType = rightOperand->GetType(); // 1. left operand's type must be Int64 or Float64 if (leftOpType->GetTypeKind() != Type::TypeKind::TYPE_INT64 && leftOpType->GetTypeKind() != Type::TypeKind::TYPE_FLOAT64) { TypeCheckError(*expr.GetRawExpr(), *leftOperand, "Int64 or Float64", topLevelFunc); } auto result = expr.GetResult(); auto resultType = result->GetType(); if (leftOpType->GetTypeKind() == Type::TypeKind::TYPE_INT64) { // 2. if left operand's type is Int64, right operand's type must be UInt64, and result type must be Int64 if (rightOpType->GetTypeKind() != Type::TypeKind::TYPE_UINT64) { TypeCheckError(*expr.GetRawExpr(), *rightOperand, "UInt64", topLevelFunc); } if (resultType->GetTypeKind() != Type::TypeKind::TYPE_INT64) { TypeCheckError(*expr.GetRawExpr(), *result, "Int64", topLevelFunc); } } else if (leftOpType->GetTypeKind() == Type::TypeKind::TYPE_FLOAT64) { // 3. if left operand's type is Float64, right operand's type must be Int64 or Float64, // and result type must be Float64 if (rightOpType->GetTypeKind() != Type::TypeKind::TYPE_INT64 && rightOpType->GetTypeKind() != Type::TypeKind::TYPE_FLOAT64) { TypeCheckError(*expr.GetRawExpr(), *rightOperand, "Int64 or Float64", topLevelFunc); } if (resultType->GetTypeKind() != Type::TypeKind::TYPE_FLOAT64) { TypeCheckError(*expr.GetRawExpr(), *result, "Float64", topLevelFunc); } } // 4. check overflow strategy OverflowStrategyMustBeValid(expr.GetOverflowStrategy(), *expr.GetRawExpr(), topLevelFunc); } void CHIRChecker::CheckBitExpression(const BinaryExprBase& expr, const Func& topLevelFunc) { auto leftOperand = expr.GetLHSOperand(); auto rightOperand = expr.GetRHSOperand(); auto leftOpType = leftOperand->GetType(); auto rightOpType = rightOperand->GetType(); // 1. operands' type must be Int if (!leftOpType->IsInteger()) { TypeCheckError(*expr.GetRawExpr(), *leftOperand, "Int", topLevelFunc); } if (!rightOpType->IsInteger()) { TypeCheckError(*expr.GetRawExpr(), *rightOperand, "Int", topLevelFunc); } // 2. check overflow strategy if (expr.GetOpKind() == ExprKind::LSHIFT || expr.GetOpKind() == ExprKind::RSHIFT) { OverflowStrategyMustBeValid(expr.GetOverflowStrategy(), *expr.GetRawExpr(), topLevelFunc); } } void CHIRChecker::CheckCompareExpression(const BinaryExprBase& expr, const Func& topLevelFunc) { auto leftOperand = expr.GetLHSOperand(); auto rightOperand = expr.GetRHSOperand(); auto leftOpType = leftOperand->GetType(); auto rightOpType = rightOperand->GetType(); auto result = expr.GetResult(); // 1. operands' type must be same if (leftOpType != rightOpType) { auto errMsg = "left operand and right operand don't have same type in " + result->ToString() + "."; ErrorInFunc(topLevelFunc, errMsg); } // 2. result type must be Bool if (!result->GetType()->IsBoolean()) { TypeCheckError(*expr.GetRawExpr(), *result, "Bool", topLevelFunc); } } void CHIRChecker::CheckLogicExpression(const BinaryExprBase& expr, const Func& topLevelFunc) { auto leftOperand = expr.GetLHSOperand(); auto rightOperand = expr.GetRHSOperand(); auto leftOpType = leftOperand->GetType(); auto rightOpType = rightOperand->GetType(); // 1. operands' type must be Bool if (!leftOpType->IsBoolean()) { TypeCheckError(*expr.GetRawExpr(), *leftOperand, "Bool", topLevelFunc); } if (!rightOpType->IsBoolean()) { TypeCheckError(*expr.GetRawExpr(), *rightOperand, "Bool", topLevelFunc); } // 2. result type must be Bool auto result = expr.GetResult(); if (!result->GetType()->IsBoolean()) { TypeCheckError(*expr.GetRawExpr(), *result, "Bool", topLevelFunc); } } void CHIRChecker::CheckSpawnWithException(const SpawnWithException& expr, const Func& topLevelFunc) { // 1. must have result if (!CheckHaveResult(expr, topLevelFunc)) { return; } // 2. have 1 or 2 operands if (!OperandNumIsEqual({1, 2}, expr, topLevelFunc)) { return; } // 3. have 2 successors if (!SuccessorNumIsEqual(2, expr, topLevelFunc)) { return; } CheckSpawnBase(SpawnBase(&expr), topLevelFunc); } void CHIRChecker::CheckSpawnBase(const SpawnBase& expr, const Func& topLevelFunc) { // 1. the first operand's type must be class& auto obj = expr.GetObject(); auto objType = obj->GetType(); if (!objType->IsRef() || !StaticCast(objType)->GetBaseType()->IsClass()) { TypeCheckError(*expr.GetRawExpr(), *obj, "Class&", topLevelFunc); return; } // 2. the first operand's type must be Class-Future& or closure type auto def = StaticCast(objType->StripAllRefs())->GetClassDef(); if (!IsCoreFuture(*def) && !IsFuncTypeOrClosureBaseRefType(*objType)) { auto errMsg = "the first operand's type is `" + objType->ToString() + "`, but Class-Future& type or closure type are expected."; ErrorInExpr(topLevelFunc, *expr.GetRawExpr(), errMsg); } // 3. if the first operand's type is closure type, `spawn` must set function `executeClosure` if (IsFuncTypeOrClosureBaseRefType(*objType) && !expr.IsExecuteClosure()) { auto errMsg = "the first operand's type is closure type but you don't set `executeClosure` function"; ErrorInExpr(topLevelFunc, *expr.GetRawExpr(), errMsg); } } void CHIRChecker::CheckTypeCastWithException( [[maybe_unused]] const TypeCastWithException& expr, [[maybe_unused]] const Func& topLevelFunc) { } void CHIRChecker::CheckIntrinsicWithException(const IntrinsicWithException& expr, const Func& topLevelFunc) { // 1. must have result if (!CheckHaveResult(expr, topLevelFunc)) { return; } // 2. have 2 successors if (!SuccessorNumIsEqual(2, expr, topLevelFunc)) { return; } CheckIntrinsicBase(IntrinsicBase(&expr), topLevelFunc); } void CHIRChecker::CheckIntrinsicBase(const IntrinsicBase& expr, const Func& topLevelFunc) { if (topLevelFunc.GetIdentifier() == "@_CNbv4mockIG_HRNat5ArrayINNbv8StubModeEE" || topLevelFunc.GetIdentifier() == "@_CNbv3spyIG_HG_" || topLevelFunc.GetIdentifier() == "@_CNbv4mockIG_Hv") { return; } // 1. must have valid intrinsic kind if (expr.GetIntrinsicKind() == IntrinsicKind::NOT_INTRINSIC || expr.GetIntrinsicKind() == IntrinsicKind::NOT_IMPLEMENTED) { ErrorInExpr(topLevelFunc, *expr.GetRawExpr(), "intrinsic kind must be valid."); } } void CHIRChecker::CheckAllocateWithException(const AllocateWithException& expr, const Func& topLevelFunc) { // 1. must have result if (!CheckHaveResult(expr, topLevelFunc)) { return; } // 2. don't have operands if (!OperandNumIsEqual(0, expr, topLevelFunc)) { return; } // 3. have 2 successors if (!SuccessorNumIsEqual(2, expr, topLevelFunc)) { return; } CheckAllocateBase(AllocateBase(&expr), topLevelFunc); } void CHIRChecker::CheckAllocateBase(const AllocateBase& expr, const Func& topLevelFunc) { auto result = expr.GetResult(); auto resultTy = result->GetType(); auto allocatedType = expr.GetType(); // 1. result type must be ref of allocated type if (!resultTy->IsRef() || StaticCast(resultTy)->GetBaseType() != allocatedType) { TypeCheckError(*expr.GetRawExpr(), *result, allocatedType->ToString() + "&", topLevelFunc); } // 2. can't allocate `Void` type if (allocatedType->IsVoid()) { ErrorInFunc(topLevelFunc, "can't allocate `Void` type in expression " + result->ToString()); } // 3. if allocated type is CustomType, then must be a valid type CheckTypeIsValid(*allocatedType, "allocated", *expr.GetRawExpr(), topLevelFunc); } bool CHIRChecker::CheckTypeIsValid( const Type& type, const std::string& typeName, const Expression& expr, const Func& topLevelFunc) { if (auto customType = DynamicCast(&type)) { auto genericType = customType->GetCustomTypeDef()->GetType(); auto declaredSize = genericType->GetTypeArgs().size(); auto callSiteSize = customType->GetTypeArgs().size(); if (declaredSize != callSiteSize) { auto errMsg = "type args' size NOT matched, the " + typeName + " type is `" + customType->ToString() + "` which has " + std::to_string(callSiteSize) + " type arg(s), but its original type is `" + genericType->ToString() + "` which has " + std::to_string(declaredSize) + " type arg(s)."; ErrorInExpr(topLevelFunc, expr, errMsg); return false; } } return true; } void CHIRChecker::CheckRawArrayAllocateWithException( const RawArrayAllocateWithException& expr, const Func& topLevelFunc) { // 1. must have result if (!CheckHaveResult(expr, topLevelFunc)) { return; } // 2. only have 1 operand if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } // 3. have 2 successors if (!SuccessorNumIsEqual(2, expr, topLevelFunc)) { return; } CheckRawArrayAllocateBase(RawArrayAllocateBase(&expr), topLevelFunc); } void CHIRChecker::CheckRawArrayAllocateBase(const RawArrayAllocateBase& expr, const Func& topLevelFunc) { // 1. result type must be RawArray& auto result = expr.GetResult(); if (!result->GetType()->IsRef() || !result->GetType()->StripAllRefs()->IsRawArray()) { TypeCheckError(*expr.GetRawExpr(), *result, "RawArray&", topLevelFunc); return; } // 2. if element type is CustomType, then must be a valid type if (!CheckTypeIsValid(*expr.GetElementType(), "element", *expr.GetRawExpr(), topLevelFunc)) { return; } // 3. result type and element type must be matched auto resultTypeArg = StaticCast(result->GetType()->StripAllRefs())->GetElementType(); if (resultTypeArg != expr.GetElementType()) { TypeCheckError( *expr.GetRawExpr(), *result, "RawArray<" + expr.GetElementType()->ToString() + ">&", topLevelFunc); } // 4. size type must be Int64 auto size = expr.GetSize(); if (size->GetType()->GetTypeKind() != Type::TypeKind::TYPE_INT64) { TypeCheckError(*expr.GetRawExpr(), *size, "Int64", topLevelFunc); } } void CHIRChecker::CheckUnaryExpression(const UnaryExpression& expr, const Func& topLevelFunc) { if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } CheckUnaryExprBase(UnaryExprBase(&expr), topLevelFunc); } void CHIRChecker::CheckBinaryExpression(const BinaryExpression& expr, const Func& topLevelFunc) { // 1. must have 2 operands if (!OperandNumIsEqual(2, expr, topLevelFunc)) { return; } CheckBinaryExprBase(BinaryExprBase(&expr), topLevelFunc); } void CHIRChecker::CheckControlFlowExpression(const Expression& expr, const Func& topLevelFunc) { const std::unordered_map> actionMap = { {ExprKind::LAMBDA, [this, &expr, &topLevelFunc]() { CheckLambda(StaticCast(expr), topLevelFunc); }}, }; if (auto it = actionMap.find(expr.GetExprKind()); it != actionMap.end()) { it->second(); } else { WarningInExpr(topLevelFunc, expr, "find unrecongnized ExprKind `" + expr.GetExprKindName() + "."); } } void CHIRChecker::CheckLambda(const Lambda& expr, const Func& topLevelFunc) { // 1. identifier can't be empty auto result = expr.GetResult(); if (expr.GetIdentifier().empty()) { ErrorInFunc(topLevelFunc, "lambda " + result->GetIdentifier()+ " doesn't have identifier."); return; } // 2. result type must be func type if (!result->GetType()->IsFunc()) { TypeCheckError(expr, *result, "Func", topLevelFunc); } // 3. top-level func must be correct if (expr.GetTopLevelFunc() != &topLevelFunc) { ErrorInExpr(topLevelFunc, expr, "its top-level func is " + expr.GetTopLevelFunc()->GetIdentifier() + " by calculated, but it should be " + topLevelFunc.GetIdentifier() + " in fact."); } // 4. check func type auto funcTy = expr.GetFuncType(); if (!CheckFuncType(funcTy, &expr, topLevelFunc)) { return; } // 5. check func parameters CheckFuncParams(expr.GetParams(), *funcTy, expr.GetIdentifier()); // 6. check func body CheckBlockGroup(*expr.GetBody(), topLevelFunc); // 7. check return value CheckFuncRetValue(expr.GetReturnValue(), *funcTy->GetReturnType(), &expr, topLevelFunc); } void CHIRChecker::CheckOtherExpression(const Expression& expr, const Func& topLevelFunc) { const std::unordered_map> actionMap = { {ExprKind::CONSTANT, [this, &expr, &topLevelFunc]() { CheckConstant(StaticCast(expr), topLevelFunc); }}, {ExprKind::DEBUGEXPR, [this, &expr, &topLevelFunc]() { CheckDebug(StaticCast(expr), topLevelFunc); }}, {ExprKind::TUPLE, [this, &expr, &topLevelFunc]() { CheckTuple(StaticCast(expr), topLevelFunc); }}, {ExprKind::FIELD, [this, &expr, &topLevelFunc]() { CheckField(StaticCast(expr), topLevelFunc); }}, {ExprKind::FIELD_BY_NAME, [this, &expr, &topLevelFunc]() { CheckFieldByName(StaticCast(expr), topLevelFunc); }}, {ExprKind::APPLY, [this, &expr, &topLevelFunc]() { CheckApply(StaticCast(expr), topLevelFunc); }}, {ExprKind::INVOKE, [this, &expr, &topLevelFunc]() { CheckInvoke(StaticCast(expr), topLevelFunc); }}, {ExprKind::INVOKESTATIC, [this, &expr, &topLevelFunc]() { CheckInvokeStatic(StaticCast(expr), topLevelFunc); }}, {ExprKind::INSTANCEOF, [this, &expr, &topLevelFunc]() { CheckInstanceOf(StaticCast(expr), topLevelFunc); }}, {ExprKind::TYPECAST, [this, &expr, &topLevelFunc]() { CheckTypeCast(StaticCast(expr), topLevelFunc); }}, {ExprKind::GET_EXCEPTION, [this, &expr, &topLevelFunc]() { CheckGetException(StaticCast(expr), topLevelFunc); }}, {ExprKind::SPAWN, [this, &expr, &topLevelFunc]() { CheckSpawn(StaticCast(expr), topLevelFunc); }}, {ExprKind::RAW_ARRAY_ALLOCATE, [this, &expr, &topLevelFunc]() { CheckRawArrayAllocate(StaticCast(expr), topLevelFunc); }}, {ExprKind::RAW_ARRAY_LITERAL_INIT, [this, &expr, &topLevelFunc]() { CheckRawArrayLiteralInit(StaticCast(expr), topLevelFunc); }}, {ExprKind::RAW_ARRAY_INIT_BY_VALUE, [this, &expr, &topLevelFunc]() { CheckRawArrayInitByValue(StaticCast(expr), topLevelFunc); }}, {ExprKind::VARRAY, [this, &expr, &topLevelFunc]() { CheckVArray(StaticCast(expr), topLevelFunc); }}, {ExprKind::VARRAY_BUILDER, [this, &expr, &topLevelFunc]() { CheckVArrayBuilder(StaticCast(expr), topLevelFunc); }}, {ExprKind::INTRINSIC, [this, &expr, &topLevelFunc]() { CheckIntrinsic(StaticCast(expr), topLevelFunc); }}, {ExprKind::BOX, [this, &expr, &topLevelFunc]() { CheckBox(StaticCast(expr), topLevelFunc); }}, {ExprKind::UNBOX, [this, &expr, &topLevelFunc]() { CheckUnBox(StaticCast(expr), topLevelFunc); }}, {ExprKind::TRANSFORM_TO_GENERIC, [this, &expr, &topLevelFunc]() { CheckTransformToGeneric(StaticCast(expr), topLevelFunc); }}, {ExprKind::TRANSFORM_TO_CONCRETE, [this, &expr, &topLevelFunc]() { CheckTransformToConcrete(StaticCast(expr), topLevelFunc); }}, {ExprKind::GET_INSTANTIATE_VALUE, [this, &expr, &topLevelFunc]() { CheckGetInstantiateValue(StaticCast(expr), topLevelFunc); }}, {ExprKind::UNBOX_TO_REF, [this, &expr, &topLevelFunc]() { CheckUnBoxToRef(StaticCast(expr), topLevelFunc); }}, {ExprKind::GET_RTTI, [this, &expr, &topLevelFunc]() { CheckGetRTTI(StaticCast(expr), topLevelFunc); }}, {ExprKind::GET_RTTI_STATIC, [this, &expr, &topLevelFunc]() { CheckGetRTTIStatic(StaticCast(expr), topLevelFunc); }}, }; if (auto it = actionMap.find(expr.GetExprKind()); it != actionMap.end()) { it->second(); } else { WarningInExpr(topLevelFunc, expr, "find unrecongnized ExprKind `" + expr.GetExprKindName() + "."); } } void CHIRChecker::CheckConstant(const Constant& expr, const Func& topLevelFunc) { // 1. only have 1 operand if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } // 2. operand must be literal value auto value = expr.GetValue(); auto litVal = DynamicCast(value); if (litVal == nullptr) { ErrorInExpr(topLevelFunc, expr, "value must be `LiteralValue`"); return; } // we will check it later if (litVal->IsNullLiteral()) { return; } // 3. operand type must be Bool, Rune, String, Int, Float or Unit auto valueType = value->GetType(); if (!valueType->IsBoolean() && !valueType->IsRune() && !valueType->IsString() && !valueType->IsInteger() && !valueType->IsFloat() && !valueType->IsUnit()) { TypeCheckError(expr, *value, "Bool, Rune, String, Int, Float, Unit", topLevelFunc); } // 4. result type must equal to operand type auto result = expr.GetResult(); if (result->GetType() != valueType) { TypeCheckError(expr, *result, valueType->ToString(), topLevelFunc); } } void CHIRChecker::CheckDebug(const Debug& expr, const Func& topLevelFunc) { // 1. only have 1 operand if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } // 2. result type must be `Unit` auto result = expr.GetResult(); if (!result->GetType()->IsUnit()) { TypeCheckError(expr, *result, "Unit", topLevelFunc); } auto value = expr.GetValue(); if (!value->IsParameter() && opts.enableCompileDebug) { // 3. source value's type must be `ReferenceType&&` or `ValueType&` if (!CheckTypeMustBeRef(*value->GetType())) { auto valueType = value->GetType()->StripAllRefs(); if (valueType->IsReferenceType()) { TypeCheckError(expr, *value, valueType->ToString() + "&&", topLevelFunc); } else { TypeCheckError(expr, *value, valueType->ToString() + "&", topLevelFunc); } } } } void CHIRChecker::CheckTuple(const Tuple& expr, const Func& topLevelFunc) { // 1. there is 1 operand at least if (!OperandNumAtLeast(1, expr, topLevelFunc)) { return; } auto result = expr.GetResult(); auto resultTy = result->GetType(); if (resultTy->IsEnum()) { // 2. check tuple if result type is Enum CheckEnumTuple(expr, topLevelFunc); } else if (resultTy->IsTuple()) { // 3. check tuple if result type is Tuple CheckNormalTuple(expr, topLevelFunc); } else if (resultTy->IsStruct()) { // 4. check tuple if result type is Struct CheckStructTuple(expr, topLevelFunc); } else { // 5. result type must be Enum, Tuple or Struct TypeCheckError(expr, *result, "Enum, Tuple or Struct", topLevelFunc); } } void CHIRChecker::CheckEnumTuple(const Tuple& expr, const Func& topLevelFunc) { // case: Enum-xxx = Tuple(a, b, c...) // 1. operands can't be empty auto result = expr.GetResult(); const auto& operands = expr.GetOperands(); if (operands.empty()) { auto errMsg = "must have one operand in `" + result->ToString() + "` at least, and the 1st operand's type is UInt32 or Bool."; ErrorInFunc(topLevelFunc, errMsg); return; } // 2. the 1st operand is enum constructor's index, so its type must be Bool or UInt32 auto index = operands[0]; if (!IsEnumSelectorType(*index->GetType())) { TypeCheckError(expr, *index, "UInt32 or Bool", topLevelFunc); return; } // 3. the 1st operand must be a local var auto localVar = DynamicCast(index); if (localVar == nullptr) { auto errMsg = "the 1st operand in `" + result->ToString() + "` must be from Constant UInt32 or Constant Bool."; ErrorInFunc(topLevelFunc, errMsg); return; } // 4. the 1st operand must be from `Constant` auto indexExpr = localVar->GetExpr(); if (!indexExpr->IsConstantInt() && !indexExpr->IsConstantBool()) { auto errMsg = "the 1st operand in `" + result->ToString() + "` must be from Constant UInt32 or Constant Bool."; ErrorInFunc(topLevelFunc, errMsg); return; } if (operands.size() == 1) { return; } // 5. check the other operands, they are enum constructor's parameter types auto resultTy = StaticCast(result->GetType()); auto ctors = resultTy->GetConstructorInfos(builder); size_t idx = 0; auto constantExpr = StaticCast(indexExpr); if (constantExpr->IsBoolLit()) { idx = constantExpr->GetBoolLitVal() ? 1 : 0; } else { idx = constantExpr->GetUnsignedIntLitVal(); } if (idx >= ctors.size()) { auto errMsg = "index out of range, in `" + result->ToString() + "` its enum constructor's number is " + std::to_string(ctors.size()) + " but the index is " + std::to_string(idx) + "."; ErrorInFunc(topLevelFunc, errMsg); return; } auto paramTypes = ctors[idx].funcType->GetParamTypes(); if (operands.size() - 1 != paramTypes.size()) { auto errMsg = "size mismatched, there are " + std::to_string(paramTypes.size()) + " parameter(s) in the " + std::to_string(idx) + "-th constructor, but " + std::to_string(operands.size() - 1) + " arguments are provided in `" + result->ToString() + "`."; ErrorInFunc(topLevelFunc, errMsg); return; } for (size_t i = 1; i < operands.size(); ++i) { if (!TypeIsExpected(*operands[i]->GetType(), *paramTypes[i - 1])) { auto errMsg = "type mismatched, the " + std::to_string(i - 1) + "-th parameter type is " + paramTypes[i - 1]->ToString() + ", but " + operands[i]->GetIdentifier() + "'s type is " + operands[i]->GetType()->ToString() + " in `" + result->ToString() + "`."; ErrorInFunc(topLevelFunc, errMsg); } } } void CHIRChecker::CheckStructTuple(const Tuple& expr, const Func& topLevelFunc) { // case: Struct-xxx = Tuple(a, b, c...) // 1. tuple's operands must be struct's instance member vars, so their sizes and types must be equal auto result = expr.GetResult(); auto resultTy = StaticCast(result->GetType()); const auto& operands = expr.GetOperands(); auto structDef = resultTy->GetStructDef(); auto memberVarTypes = resultTy->GetInstantiatedMemberTys(builder); if (operands.size() != memberVarTypes.size()) { auto errMsg = "size mismatched, there are " + std::to_string(memberVarTypes.size()) + " instance member var(s) in the struct " + structDef->GetIdentifier() + ", but " + std::to_string(operands.size()) + " operand(s) are provided in `" + result->ToString() + "`."; ErrorInFunc(topLevelFunc, errMsg); return; } for (size_t i = 0; i < memberVarTypes.size(); ++i) { if (!TypeIsExpected(*operands[i]->GetType(), *memberVarTypes[i])) { auto errMsg = "type mismatched, the " + std::to_string(i) + "-th member var type in struct " + structDef->GetIdentifier() + " is " + memberVarTypes[i]->ToString() + ", but " + operands[i]->GetIdentifier() + "'s type is " + operands[i]->GetType()->ToString() + " in `" + result->ToString() + "`."; ErrorInFunc(topLevelFunc, errMsg); } } } void CHIRChecker::CheckNormalTuple(const Tuple& expr, const Func& topLevelFunc) { // case: Tuple(type1, type2, type3...) = Tuple(a, b, c...) // 1. operands's size must equal to size of TupleType's element type auto result = expr.GetResult(); auto resultTy = StaticCast(result->GetType()); const auto& operands = expr.GetOperands(); auto elementTypes = StaticCast(resultTy)->GetElementTypes(); if (operands.size() != elementTypes.size()) { auto errMsg = "size mismatched, there are " + std::to_string(elementTypes.size()) + " element(s) in the tuple type `" + resultTy->ToString() + "`, but " + std::to_string(operands.size()) + " operand(s) are provided in `" + result->ToString() + "`."; ErrorInFunc(topLevelFunc, errMsg); return; } // 2. operand's type must equal to element type for (size_t i = 0; i < elementTypes.size(); ++i) { if (!TypeIsExpected(*operands[i]->GetType(), *elementTypes[i])) { auto errMsg = "type mismatched, the " + std::to_string(i) + "-th element type in tuple type `" + resultTy->ToString() + "` is " + elementTypes[i]->ToString() + ", but " + operands[i]->GetIdentifier() + "'s type is " + operands[i]->GetType()->ToString() + " in `" + result->ToString() + "`."; ErrorInFunc(topLevelFunc, errMsg); } } } void CHIRChecker::CheckField(const Field& expr, const Func& topLevelFunc) { // 1. only have 1 operand if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } // 2. location type can't be ref, must be Struct, Enum or Tuple auto location = expr.GetBase(); auto locationType = location->GetType(); if (locationType->IsRef()) { TypeCheckError(expr, *location, locationType->StripAllRefs()->ToString(), topLevelFunc); } else if (!locationType->IsStruct() && !locationType->IsEnum() && !locationType->IsTuple()) { TypeCheckError(expr, *location, "Struct, Enum or Tuple", topLevelFunc); } // 3. path can't be out of bounds const auto& path = expr.GetPath(); auto result = expr.GetResult(); if (path.empty()) { auto errMsg = "path is empty in Field `" + result->ToString() + "`."; ErrorInFunc(topLevelFunc, errMsg); } std::string errMsg = "wrong path: "; for (uint64_t i = 0; i < path.size(); ++i) { errMsg += locationType->ToString() + "[index: " + std::to_string(path[i]) + "] --> "; locationType = GetFieldOfType(*locationType, path[i], builder); if (locationType == nullptr) { errMsg += "unknown type, in Field `" + result->ToString() + "`."; ErrorInFunc(topLevelFunc, errMsg); return; } } // 4. calculated type by path can set to result type if (!TypeIsExpected(*locationType, *result->GetType())) { TypeCheckError(expr, *result, locationType->ToString(), topLevelFunc); } } void CHIRChecker::CheckFieldByName(const FieldByName& expr, const Func& topLevelFunc) { ErrorInExpr(topLevelFunc, expr, "you should convert this expression to `Field`."); } void CHIRChecker::CheckApply(const Apply& expr, const Func& topLevelFunc) { // 1. there is 1 operand at least if (!OperandNumAtLeast(1, expr, topLevelFunc)) { return; } CheckApplyBase(ApplyBase(&expr), topLevelFunc); } void CHIRChecker::CheckInvoke(const Invoke& expr, const Func& topLevelFunc) { if (!OperandNumAtLeast(1, expr, topLevelFunc)) { return; } CheckInvokeBase(InvokeBase(&expr), topLevelFunc); } void CHIRChecker::CheckInvokeStatic(const InvokeStatic& expr, const Func& topLevelFunc) { if (!OperandNumAtLeast(1, expr, topLevelFunc)) { return; } CheckInvokeStaticBase(InvokeStaticBase(&expr), topLevelFunc); } void CHIRChecker::CheckInstanceOf(const InstanceOf& expr, const Func& topLevelFunc) { if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } auto result = expr.GetResult(); // 1. return type must be bool if (!result->GetType()->IsBoolean()) { TypeCheckError(expr, *result, "Bool", topLevelFunc); } // 2. target type must be valid CheckTypeIsValid(*expr.GetType(), "target", expr, topLevelFunc); } void CHIRChecker::CheckTypeCast([[maybe_unused]] const TypeCast& expr, [[maybe_unused]] const Func& topLevelFunc) { } void CHIRChecker::CheckGetException(const GetException& expr, const Func& topLevelFunc) { if (!OperandNumIsEqual(0, expr, topLevelFunc)) { return; } auto result = expr.GetResult(); // 1. return type must be class& if (!result->GetType()->IsRef() || !result->GetType()->StripAllRefs()->IsClass()) { TypeCheckError(expr, *result, "Class&", topLevelFunc); } OperandNumIsEqual(0, expr, topLevelFunc); } void CHIRChecker::CheckSpawn(const Spawn& expr, const Func& topLevelFunc) { if (!OperandNumAtLeast(1, expr, topLevelFunc)) { return; } CheckSpawnBase(SpawnBase(&expr), topLevelFunc); } void CHIRChecker::CheckRawArrayAllocate(const RawArrayAllocate& expr, const Func& topLevelFunc) { if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } CheckRawArrayAllocateBase(RawArrayAllocateBase(&expr), topLevelFunc); } void CHIRChecker::CheckRawArrayLiteralInit(const RawArrayLiteralInit& expr, const Func& topLevelFunc) { if (!OperandNumAtLeast(1, expr, topLevelFunc)) { return; } auto result = expr.GetResult(); if (!result->GetType()->IsUnit()) { TypeCheckError(expr, *result, "Unit", topLevelFunc); } auto rawArray = expr.GetRawArray(); if (!rawArray->GetType()->IsRef() || !rawArray->GetType()->StripAllRefs()->IsRawArray()) { TypeCheckError(expr, *rawArray, "RawArray&", topLevelFunc); } auto rawArrayEleTy = StaticCast(rawArray->GetType()->StripAllRefs())->GetElementType(); for (auto e : expr.GetElements()) { if (!e->GetType()->IsEqualOrSubTypeOf(*rawArrayEleTy, builder)) { TypeCheckError(expr, *e, rawArrayEleTy->ToString(), topLevelFunc); } } } void CHIRChecker::CheckRawArrayInitByValue(const RawArrayInitByValue& expr, const Func& topLevelFunc) { // 1. have 3 operands if (!OperandNumIsEqual(3, expr, topLevelFunc)) { return; } auto result = expr.GetResult(); if (!result->GetType()->IsUnit()) { TypeCheckError(expr, *result, "Unit", topLevelFunc); } auto size = expr.GetSize(); if (size->GetType()->GetTypeKind() != Type::TypeKind::TYPE_INT64) { TypeCheckError(expr, *size, "Int64", topLevelFunc); } auto rawArray = expr.GetRawArray(); if (!rawArray->GetType()->IsRef() || !rawArray->GetType()->StripAllRefs()->IsRawArray()) { TypeCheckError(expr, *rawArray, "RawArray&", topLevelFunc); } else { auto rawArrayEleTy = StaticCast(rawArray->GetType()->StripAllRefs())->GetElementType(); if (rawArrayEleTy != expr.GetInitValue()->GetType()) { TypeCheckError(expr, *expr.GetInitValue(), rawArrayEleTy->ToString(), topLevelFunc); } } } void CHIRChecker::CheckVArray(const VArray& expr, const Func& topLevelFunc) { auto result = expr.GetResult(); if (!result->GetType()->IsVArray()) { TypeCheckError(expr, *result, "VArray", topLevelFunc); return; } // elements type check auto elemType = StaticCast(result->GetType())->GetElementType(); // only use VArray to be parameter type in CFunc, its element type shouldn't be normal struct, // must be marked with @C if (!IsCType(*elemType, false)) { auto errMsg = "its element type is " + elemType->ToString() + ", but a C type is expected.\n"; auto hint = " C type includes: numeric data types, Rune, Bool, Unit, Nothing, CPointer, CString, CFunc and Struct."; ErrorInExpr(topLevelFunc, expr, errMsg + hint); return; } for (auto arg : expr.GetOperands()) { if (!arg->GetType()->IsEqualOrSubTypeOf(*elemType, builder)) { TypeCheckError(expr, *arg, elemType->ToString(), topLevelFunc); } } } void CHIRChecker::CheckVArrayBuilder(const VArrayBuilder& expr, const Func& topLevelFunc) { // 1. have 3 operands if (!OperandNumIsEqual(3, expr, topLevelFunc)) { return; } auto result = expr.GetResult(); if (!result->GetType()->IsVArray()) { TypeCheckError(expr, *result, "VArray", topLevelFunc); } auto size = expr.GetSize(); if (size->GetType()->GetTypeKind() != Type::TypeKind::TYPE_INT64) { TypeCheckError(expr, *size, "Int64", topLevelFunc); } auto item = ConvertToNullLiteral(*expr.GetItem()); auto initFunc = ConvertToNullLiteral(*expr.GetInitFunc()); if (item == nullptr && initFunc == nullptr) { ErrorInExpr(topLevelFunc, expr, "one of item and init func should be null."); } else if (item != nullptr && initFunc != nullptr) { ErrorInExpr(topLevelFunc, expr, "item and init func can't be both null."); } auto funcType = expr.GetInitFunc()->GetType(); if (!IsFuncTypeOrClosureBaseRefType(*funcType)) { TypeCheckError(expr, *expr.GetInitFunc(), "Func type or closure ref", topLevelFunc); } } void CHIRChecker::CheckIntrinsic(const Intrinsic& expr, const Func& topLevelFunc) { CheckIntrinsicBase(IntrinsicBase(&expr), topLevelFunc); } void CHIRChecker::CheckBox(const Box& expr, const Func& topLevelFunc) { if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } auto targetType = expr.GetTargetTy(); auto sourceType = expr.GetSourceTy(); if (!targetType->IsRef()) { TypeCheckError(expr, *expr.GetResult(), "reference", topLevelFunc); } else if (targetType->StripAllRefs()->IsBox()) { if (StaticCast(targetType->StripAllRefs())->GetBaseType() != sourceType) { auto errMsg = "the target type is " + targetType->ToString() + ", then source type must be its base type, can't be " + sourceType->ToString() + "."; ErrorInExpr(topLevelFunc, expr, errMsg); } } else if (!targetType->StripAllRefs()->IsClass()) { auto errMsg = "the target type must be Class& or Box&, can't be " + targetType->ToString() + "."; ErrorInExpr(topLevelFunc, expr, errMsg); } if (!sourceType->IsValueType()) { TypeCheckError(expr, *expr.GetSourceValue(), "value", topLevelFunc); } } void CHIRChecker::CheckUnBox(const UnBox& expr, const Func& topLevelFunc) { if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } auto targetType = expr.GetTargetTy(); auto sourceType = expr.GetSourceTy(); if (!sourceType->IsRef()) { TypeCheckError(expr, *expr.GetSourceValue(), "reference", topLevelFunc); } else if (sourceType->StripAllRefs()->IsBox()) { if (StaticCast(sourceType->StripAllRefs())->GetBaseType() != targetType) { auto errMsg = "the source type is " + sourceType->ToString() + ", then target type must be its base type, can't be " + targetType->ToString() + "."; ErrorInExpr(topLevelFunc, expr, errMsg); } } else if (!sourceType->StripAllRefs()->IsClass()) { auto errMsg = "the source type must be Class& or Box&, can't be " + sourceType->ToString() + "."; ErrorInExpr(topLevelFunc, expr, errMsg); } if (!targetType->IsValueType()) { TypeCheckError(expr, *expr.GetResult(), "value", topLevelFunc); } } void CHIRChecker::CheckTransformToGeneric(const TransformToGeneric& expr, const Func& topLevelFunc) { if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } if (!expr.GetTargetTy()->IsGenericRelated()) { TypeCheckError(expr, *expr.GetResult(), "generic related", topLevelFunc); } if (expr.GetSourceTy()->IsGeneric()) { TypeCheckError(expr, *expr.GetSourceValue(), "concrete", topLevelFunc); } } void CHIRChecker::CheckTransformToConcrete(const TransformToConcrete& expr, const Func& topLevelFunc) { if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } if (expr.GetTargetTy()->IsGeneric()) { TypeCheckError(expr, *expr.GetResult(), "concrete", topLevelFunc); } if (!expr.GetSourceTy()->IsGenericRelated()) { TypeCheckError(expr, *expr.GetSourceValue(), "generic related", topLevelFunc); } } void CHIRChecker::CheckGetInstantiateValue(const GetInstantiateValue& expr, const Func& topLevelFunc) { if (optionalRules.find(Rule::GET_INSTANTIATE_VALUE_SHOULD_GONE) != optionalRules.end()) { ErrorInExpr(topLevelFunc, expr, "`GetInstantiateValue` should be removed now, it can't be seen in CodeGen Stage."); return; } if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } auto func = expr.GetGenericResult(); if (!func->GetType()->IsFunc()) { TypeCheckError(expr, *func, "Func", topLevelFunc); return; } auto instTypeArgs = expr.GetInstantiateTypes(); if (instTypeArgs.empty()) { auto errMsg = "there must be instantiated type in `GetInstantiateValue`"; ErrorInExpr(topLevelFunc, expr, errMsg); return; } std::vector>> funcAndGenericTypes; CollectFuncAndGenericTypesRecursively(*func, funcAndGenericTypes); CJC_ASSERT(!funcAndGenericTypes.empty()); size_t allGenericTypes = 0; for (auto& it : funcAndGenericTypes) { allGenericTypes += it.second.size(); } auto funcBase = VirtualCast(funcAndGenericTypes.front().first); if (auto parentType = funcBase->GetParentCustomTypeOrExtendedType()) { allGenericTypes += parentType->GetTypeArgs().size(); } if (allGenericTypes != instTypeArgs.size()) { auto genericTypeSize = funcAndGenericTypes.back().second.size(); auto errMsg = "current func is " + GetFuncIdentifier(*func) + " with " + std::to_string(genericTypeSize) + " generic param type(s), and its outer func is "; funcAndGenericTypes.pop_back(); for (auto it = funcAndGenericTypes.crbegin(); it != funcAndGenericTypes.crend(); ++it) { errMsg += "[" + GetFuncIdentifier(*it->first) + " with " + std::to_string(it->second.size()) + " generic param type(s) ], "; } if (auto parentType = funcBase->GetParentCustomTypeOrExtendedType()) { errMsg += "and the parent Custom type is " + parentType->ToString() + " with " + std::to_string(parentType->GetTypeArgs().size()) + " generic param type(s), "; } errMsg += "they have " + std::to_string(allGenericTypes) + " generic param type(s) in total, but there are " + std::to_string(instTypeArgs.size()) + " instantiated type arg(s) in current `GetInstantiateValue`."; ErrorInExpr(topLevelFunc, expr, errMsg); return; } size_t typeIdx = 0; std::string errMsg = "the "; bool hasError = false; if (auto parentType = funcBase->GetParentCustomTypeOrExtendedType()) { if (auto customType = DynamicCast(parentType)) { for (auto arg : customType->GetGenericArgs()) { if (!instTypeArgs[typeIdx]->IsEqualOrInstantiatedTypeOf(*arg, builder)) { errMsg += IndexToString(typeIdx + 1) + ", "; hasError = true; } ++typeIdx; } } } for (auto& it : funcAndGenericTypes) { for (auto genericType : it.second) { if (!instTypeArgs[typeIdx]->IsEqualOrInstantiatedTypeOf(*genericType, builder)) { errMsg += IndexToString(typeIdx + 1) + ", "; hasError = true; } ++typeIdx; } } if (hasError) { // remove ", " errMsg.pop_back(); errMsg.pop_back(); errMsg += " instantiated type(s) don't satisfy the generic constraints."; ErrorInExpr(topLevelFunc, expr, errMsg); } } void CHIRChecker::CheckUnBoxToRef(const UnBoxToRef& expr, const Func& topLevelFunc) { if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } auto targetType = expr.GetTargetTy(); auto isValueRefType = targetType->IsRef() && StaticCast(targetType)->GetBaseType()->IsValueType(); if (!isValueRefType) { TypeCheckError(expr, *expr.GetResult(), "Value&", topLevelFunc); } auto sourceType = expr.GetSourceTy(); auto isReferenceRefType = sourceType->IsRef() && StaticCast(sourceType)->GetBaseType()->IsReferenceType(); if (!isReferenceRefType) { TypeCheckError(expr, *expr.GetSourceValue(), "Reference&", topLevelFunc); } } void CHIRChecker::CheckGetRTTI(const GetRTTI& expr, const Func& topLevelFunc) { if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } auto result = expr.GetResult(); if (!result->GetType()->IsUnit()) { TypeCheckError(expr, *result, "Unit", topLevelFunc); } auto operand = expr.GetOperand(); auto operandType = operand->GetType(); if (!operandType->IsRef()) { TypeCheckError(expr, *operand, "Class& or This&", topLevelFunc); } else { auto baseType = StaticCast(operandType)->GetBaseType(); if (!baseType->IsClass() && !baseType->IsThis()) { TypeCheckError(expr, *operand, "Class& or This&", topLevelFunc); } } } void CHIRChecker::CheckGetRTTIStatic(const GetRTTIStatic& expr, const Func& topLevelFunc) { if (!OperandNumIsEqual(0, expr, topLevelFunc)) { return; } auto result = expr.GetResult(); if (!result->GetType()->IsUnit()) { TypeCheckError(expr, *result, "Unit", topLevelFunc); } if (optionalRules.find(Rule::CHIR_GET_RTTI_STATIC_TYPE) == optionalRules.end()) { return; } auto type = expr.GetRTTIType()->StripAllRefs(); if (!type->IsGeneric() && !type->IsThis()) { ErrorInExpr(topLevelFunc, expr, "RTTI type should be `This` or Generic type, but now it's " + expr.GetRTTIType()->ToString() + "."); } } void CHIRChecker::CheckMemoryExpression(const Expression& expr, const Func& topLevelFunc) { const std::unordered_map> actionMap = { {ExprKind::ALLOCATE, [this, &expr, &topLevelFunc]() { CheckAllocate(StaticCast(expr), topLevelFunc); }}, {ExprKind::LOAD, [this, &expr, &topLevelFunc]() { CheckLoad(StaticCast(expr), topLevelFunc); }}, {ExprKind::STORE, [this, &expr, &topLevelFunc]() { CheckStore(StaticCast(expr), topLevelFunc); }}, {ExprKind::GET_ELEMENT_REF, [this, &expr, &topLevelFunc]() { CheckGetElementRef(StaticCast(expr), topLevelFunc); }}, {ExprKind::GET_ELEMENT_BY_NAME, [this, &expr, &topLevelFunc]() { CheckGetElementByName(StaticCast(expr), topLevelFunc); }}, {ExprKind::STORE_ELEMENT_REF, [this, &expr, &topLevelFunc]() { CheckStoreElementRef(StaticCast(expr), topLevelFunc); }}, {ExprKind::STORE_ELEMENT_BY_NAME, [this, &expr, &topLevelFunc]() { CheckStoreElementByName(StaticCast(expr), topLevelFunc); }}, }; if (auto it = actionMap.find(expr.GetExprKind()); it != actionMap.end()) { it->second(); } else { WarningInExpr(topLevelFunc, expr, "find unrecongnized ExprKind `" + expr.GetExprKindName() + "."); } } void CHIRChecker::CheckAllocate(const Allocate& expr, const Func& topLevelFunc) { // 1. don't have operands OperandNumIsEqual(0, expr, topLevelFunc); CheckAllocateBase(AllocateBase(&expr), topLevelFunc); } void CHIRChecker::CheckLoad(const Load& expr, const Func& topLevelFunc) { // 1. only have 1 operand if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } // 2. location type must be ref auto location = expr.GetLocation(); auto result = expr.GetResult(); if (!location->GetType()->IsRef()) { TypeCheckError(expr, *location, result->GetType()->ToString() + "&", topLevelFunc); return; } // 3. location type must be ref of result type if (auto expectedTy = StaticCast(location->GetType())->GetBaseType(); expectedTy != result->GetType()) { TypeCheckError(expr, *result, expectedTy->ToString(), topLevelFunc); } } void CHIRChecker::CheckStore(const Store& expr, const Func& topLevelFunc) { // 1. have 2 operands if (!OperandNumIsEqual(2, expr, topLevelFunc)) { return; } // 2. location type must be ref auto location = expr.GetLocation(); if (!location->GetType()->IsRef()) { TypeCheckError(expr, *location, location->GetType()->ToString() + "&", topLevelFunc); return; } // 3. location type must be ref of value type auto value = expr.GetValue(); if (auto expectedTy = StaticCast(location->GetType())->GetBaseType(); !TypeIsExpected(*value->GetType(), *expectedTy)) { TypeCheckError(expr, *value, expectedTy->ToString(), topLevelFunc); } // 4. result type must be `Unit` auto result = expr.GetResult(); if (!result->GetType()->IsUnit()) { TypeCheckError(expr, *result, "Unit", topLevelFunc); } // 5. can't store a value to GetElementRef's result if (auto res = DynamicCast(location)) { if (res->GetExpr()->GetExprKind() == ExprKind::GET_ELEMENT_REF) { auto errMsg = "destination of Store `" + result->GetIdentifier() + "` should not be GetElementRef."; ErrorInFunc(topLevelFunc, errMsg); } } } void CHIRChecker::CheckGetElementRef(const GetElementRef& expr, const Func& topLevelFunc) { // 1. only have 1 operand if (!OperandNumIsEqual(1, expr, topLevelFunc)) { return; } // 2. location type must be ref type auto location = expr.GetLocation(); auto locationType = location->GetType(); if (!locationType->IsRef()) { TypeCheckError(expr, *location, locationType->ToString() + "&", topLevelFunc); return; } // 3. location type must be Class&, Struct&, Enum&, RawArray& or Tuple& if (auto baseType = StaticCast(locationType)->GetBaseType(); !baseType->IsClass() && !baseType->IsStruct() && !baseType->IsEnum() && !baseType->IsTuple() && !baseType->IsRawArray()) { TypeCheckError(expr, *location, "Class&, Struct&, Enum&, RawArray& or Tuple&", topLevelFunc); return; } // 4. path can't be out of bounds const auto& path = expr.GetPath(); auto result = expr.GetResult(); if (path.empty()) { auto errMsg = "path is empty in GetElementRef `" + result->ToString() + "`."; ErrorInFunc(topLevelFunc, errMsg); } std::string errMsg = "wrong path: "; for (uint64_t i = 0; i < path.size(); ++i) { errMsg += locationType->ToString() + "[index: " + std::to_string(path[i]) + "] --> "; locationType = GetFieldOfType(*locationType, path[i], builder); if (locationType == nullptr) { errMsg += "unknown type, in GetElementRef `" + result->ToString() + "`."; ErrorInFunc(topLevelFunc, errMsg); } } // 5. result type must be ref, `&` or `&&` if (!result->GetType()->IsRef()) { TypeCheckError(expr, *result, result->GetType()->ToString() + "&", topLevelFunc); return; } // 6. calculated type by path must be matched with result type if (auto baseType = StaticCast(result->GetType())->GetBaseType(); !TypeIsExpected(*locationType, *baseType)) { TypeCheckError(expr, *result, locationType->ToString() + "&", topLevelFunc); } } void CHIRChecker::CheckGetElementByName(const GetElementByName& expr, const Func& topLevelFunc) { ErrorInExpr(topLevelFunc, expr, "you should convert this expression to `GetElementRef`."); } void CHIRChecker::CheckStoreElementRef(const StoreElementRef& expr, const Func& topLevelFunc) { // 1. have 2 operands if (!OperandNumIsEqual(2, expr, topLevelFunc)) { return; } // 2. location type must be ref auto location = expr.GetLocation(); auto locationType = location->GetType(); if (!locationType->IsRef()) { TypeCheckError(expr, *location, locationType->ToString() + "&", topLevelFunc); return; } // 3. location type must be Class&, Struct&, RawArray& or Tuple& auto baseType = StaticCast(locationType)->GetBaseType(); if (!baseType->IsClass() && !baseType->IsStruct() && !baseType->IsTuple() && !baseType->IsRawArray()) { TypeCheckError(expr, *location, "Class&, Struct&, RawArray& or Tuple&", topLevelFunc); } // 4. path can't be out of bounds const auto& path = expr.GetPath(); auto result = expr.GetResult(); if (path.empty()) { auto errMsg = "path is empty in StoreElementRef `" + result->ToString() + "`."; ErrorInFunc(topLevelFunc, errMsg); } std::string errMsg = "wrong path: "; for (uint64_t i = 0; i < path.size(); ++i) { errMsg += locationType->ToString() + "[index: " + std::to_string(path[i]) + "] --> "; locationType = GetFieldOfType(*locationType, path[i], builder); if (locationType == nullptr) { errMsg += "unknown type, in StoreElementRef `" + result->ToString() + "`."; ErrorInFunc(topLevelFunc, errMsg); } } // 5. calculated type by path must be matched with value type if (locationType != nullptr) { auto valueType = expr.GetValue()->GetType(); if (!TypeIsExpected(*valueType, *locationType)) { TypeCheckError(expr, *expr.GetValue(), locationType->ToString(), topLevelFunc); } } // 6. result type must be `Unit` if (!result->GetType()->IsUnit()) { TypeCheckError(expr, *result, "Unit", topLevelFunc); } } void CHIRChecker::CheckStoreElementByName(const StoreElementByName& expr, const Func& topLevelFunc) { ErrorInExpr(topLevelFunc, expr, "you should convert this expression to `StoreElementRef`."); }cangjie_compiler-1.0.7/src/CHIR/CHIRContext.cpp000066400000000000000000000217711510705540100211310ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the CHIRContext class in CHIR. */ #include "cangjie/CHIR/CHIRContext.h" #include #include "cangjie/Basic/Print.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/ExtendDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Value.h" using namespace Cangjie::CHIR; namespace { const int ALLOCATED_VALUES_START_IDX = 0; const int ALLOCATED_VALUES_END_IDX = 1; const int ALLOCATED_EXPRS_START_IDX = 2; const int ALLOCATED_EXPRS_END_IDX = 3; const int ALLOCATED_BLOCKGROUPS_START_IDX = 4; const int ALLOCATED_BLOCKGROUPS_END_IDX = 5; const int ALLOCATED_BLOCKS_START_IDX = 6; const int ALLOCATED_BLOCKS_END_IDX = 7; const int ALLOCATED_STRUCTS_START_IDX = 8; const int ALLOCATED_STRUCTS_END_IDX = 9; const int ALLOCATED_CLASSES_START_IDX = 10; const int ALLOCATED_CLASSES_END_IDX = 11; const int ALLOCATED_ENUMS_START_IDX = 12; const int ALLOCATED_ENUMS_END_IDX = 13; } std::mutex CHIRContext::dynamicAllocatedTysMtx; size_t TypePtrHash::operator()(const Type* ptr) const { return ptr != nullptr ? ptr->Hash() : 0; } bool TypePtrEqual::operator()(const Type* ptr1, const Type* ptr2) const { if (ptr1 == nullptr && ptr2 == nullptr) { return true; } return ptr1 != nullptr && ptr2 != nullptr && *ptr1 == *ptr2; } void CHIRContext::DeleteAllocatedInstance(std::vector& idxs) { // Delete the allocated instances. for (size_t i = idxs[ALLOCATED_VALUES_START_IDX]; i < idxs[ALLOCATED_VALUES_END_IDX]; i++) { delete allocatedValues[i]; } for (size_t i = idxs[ALLOCATED_EXPRS_START_IDX]; i < idxs[ALLOCATED_EXPRS_END_IDX]; i++) { delete allocatedExprs[i]; } for (size_t i = idxs[ALLOCATED_BLOCKGROUPS_START_IDX]; i < idxs[ALLOCATED_BLOCKGROUPS_END_IDX]; i++) { delete allocatedBlockGroups[i]; } for (size_t i = idxs[ALLOCATED_BLOCKS_START_IDX]; i < idxs[ALLOCATED_BLOCKS_END_IDX]; i++) { delete allocatedBlocks[i]; } for (size_t i = idxs[ALLOCATED_STRUCTS_START_IDX]; i < idxs[ALLOCATED_STRUCTS_END_IDX]; i++) { delete allocatedStructs[i]; } for (size_t i = idxs[ALLOCATED_CLASSES_START_IDX]; i < idxs[ALLOCATED_CLASSES_END_IDX]; i++) { delete allocatedClasses[i]; } for (size_t i = idxs[ALLOCATED_ENUMS_START_IDX]; i < idxs[ALLOCATED_ENUMS_END_IDX]; i++) { delete allocatedEnums[i]; } } void CHIRContext::DeleteAllocatedTys() { for (auto inst : std::as_const(this->dynamicAllocatedTys)) { delete inst; } this->dynamicAllocatedTys.clear(); for (auto inst : std::as_const(this->constAllocatedTys)) { delete inst; } this->constAllocatedTys.clear(); for (auto inst : std::as_const(this->allocatedExtends)) { delete inst; } this->allocatedExtends.clear(); if (this->curPackage != nullptr) { delete this->curPackage; this->curPackage = nullptr; } } Package* CHIRContext::GetCurPackage() const { return this->curPackage; } void CHIRContext::SetCurPackage(Package* pkg) { this->curPackage = pkg; } // Tasks are evenly distributed to obtain the start and end subscripts of the data to be processed by each thread. static void DivideArray(size_t len, size_t threadNum, std::vector>& indexs) { size_t size = len / threadNum; size_t remainder = len % threadNum; size_t start = 0; size_t end = 0; for (size_t i = 0; i < threadNum; i++) { start = end; end = start + size; if (remainder > 0) { end++; remainder--; } indexs[i].push_back(start); indexs[i].push_back(end); } return; } CHIRContext::CHIRContext(std::unordered_map* fnMap, size_t threadsNum) : curPackage(nullptr), fileNameMap(fnMap), threadsNum(threadsNum) { unitTy = GetType(); boolTy = GetType(); runeTy = GetType(); nothingTy = GetType(); int8Ty = GetType(Type::TypeKind::TYPE_INT8); int16Ty = GetType(Type::TypeKind::TYPE_INT16); int32Ty = GetType(Type::TypeKind::TYPE_INT32); int64Ty = GetType(Type::TypeKind::TYPE_INT64); intNativeTy = GetType(Type::TypeKind::TYPE_INT_NATIVE); uint8Ty = GetType(Type::TypeKind::TYPE_UINT8); uint16Ty = GetType(Type::TypeKind::TYPE_UINT16); uint32Ty = GetType(Type::TypeKind::TYPE_UINT32); uint64Ty = GetType(Type::TypeKind::TYPE_UINT64); uintNativeTy = GetType(Type::TypeKind::TYPE_UINT_NATIVE); float16Ty = GetType(Type::TypeKind::TYPE_FLOAT16); float32Ty = GetType(Type::TypeKind::TYPE_FLOAT32); float64Ty = GetType(Type::TypeKind::TYPE_FLOAT64); cstringTy = GetType(); voidTy = GetType(); } CHIRContext::~CHIRContext() { if (threadsNum == 1) { std::vector indexs{0, allocatedValues.size(), 0, allocatedExprs.size(), 0, allocatedBlockGroups.size(), 0, allocatedBlocks.size(), 0, allocatedStructs.size(), 0, allocatedClasses.size(), 0, allocatedEnums.size()}; DeleteAllocatedInstance(indexs); DeleteAllocatedTys(); } else { // Delete the allocated instances. std::vector threads; threads.reserve(threadsNum); std::vector> indexs(threadsNum, std::vector()); DivideArray(allocatedValues.size(), threadsNum - 1, indexs); DivideArray(allocatedExprs.size(), threadsNum - 1, indexs); DivideArray(allocatedBlockGroups.size(), threadsNum - 1, indexs); DivideArray(allocatedBlocks.size(), threadsNum - 1, indexs); DivideArray(allocatedStructs.size(), threadsNum - 1, indexs); DivideArray(allocatedClasses.size(), threadsNum - 1, indexs); DivideArray(allocatedEnums.size(), threadsNum - 1, indexs); for (size_t i = 0; i < threadsNum - 1; i++) { std::vector& idxs = indexs[i]; threads.emplace_back([&idxs, this]() { DeleteAllocatedInstance(idxs); }); } threads.emplace_back([this]() { DeleteAllocatedTys(); }); for (auto& thread : threads) { if (thread.joinable()) { thread.join(); } } } allocatedExprs.clear(); allocatedValues.clear(); allocatedBlockGroups.clear(); allocatedBlocks.clear(); allocatedStructs.clear(); allocatedClasses.clear(); allocatedEnums.clear(); } // FileName API void CHIRContext::RegisterSourceFileName(unsigned fileId, const std::string& fileName) const { // we need to insert or assign, because this `fileNameMap` may be set in deserialization when // we are compiling platform package, so this old `fileNameMap` is from common package, // it's not guaranteed that common package's file order and size are same with platform's fileNameMap->insert_or_assign(fileId, fileName); } const std::string& CHIRContext::GetSourceFileName(unsigned fileId) const { if (auto it = this->fileNameMap->find(fileId); it != this->fileNameMap->end()) { return it->second; } return INVALID_NAME; } const std::unordered_map* CHIRContext::GetFileNameMap() const { return this->fileNameMap; } StructType* CHIRContext::GetStructType( const std::string& package, const std::string& name, const std::vector& genericType) const { std::vector structs = this->curPackage->GetStructs(); std::vector importStructs = this->curPackage->GetImportedStructs(); structs.insert(structs.end(), importStructs.cbegin(), importStructs.cend()); for (auto it : structs) { if (it->GetPackageName() != package || it->GetSrcCodeIdentifier() != name) { continue; } if (it->TestAttr(Attribute::GENERIC_INSTANTIATED)) { continue; } auto structType = StaticCast(it->GetType()); auto argTypes = structType->GetGenericArgs(); if (std::equal(genericType.begin(), genericType.end(), argTypes.begin(), [](const std::string& a, const Type* b) { return a == b->ToString(); })) { return structType; } } return nullptr; } void CHIRContext::MergeTypes() { this->constAllocatedTys.merge(this->dynamicAllocatedTys); } StructType* CHIRContext::GetStringTy() const { return GetStructType("std.core", "String"); } Type* CHIRContext::ToSelectorType(Type::TypeKind kind) const { switch (kind) { case Type::TypeKind::TYPE_UINT32: return GetUInt32Ty(); default: return GetBoolTy(); } } cangjie_compiler-1.0.7/src/CHIR/CHIRPrinter.cpp000066400000000000000000000104041510705540100211170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the CHIRPrinter class in CHIR. */ #include "cangjie/CHIR/CHIRPrinter.h" #include #include "cangjie/CHIR/CHIR.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" #include "cangjie/CHIR/Visitor/Visitor.h" using namespace Cangjie::CHIR; static void ReplaceAll(std::string& str, const std::string& o, const std::string& n) { std::string::size_type pos = 0; while ((pos = str.find(o, pos)) != std::string::npos) { str.replace(pos, o.length(), n); pos += n.length(); } } void CHIRPrinter::PrintCFG(const Func& func, const std::string& path) { std::fstream fout; std::string id = func.GetIdentifierWithoutPrefix(); ReplaceAll(id, "$", "_"); ReplaceAll(id, "<", "_"); ReplaceAll(id, ">", "_"); std::string filePath = path + id + ".dot"; fout.open(filePath, std::ios::out); if (!fout.is_open()) { std::cerr << "open file: " << filePath << " failed!" << std::endl; return; } fout << "digraph " << id << "{" << std::endl; fout << "graph [fontname=\"Courier, monospace\"];" << std::endl; fout << "node [fontname=\"Courier, monospace\"];" << std::endl; fout << "edge [fontname=\"Courier, monospace\"];" << std::endl; Visitor::Visit(func, [&fout](Block& block) { fout << block.GetIdentifierWithoutPrefix(); fout << " [shape=none, "; fout << "label=<"; fout << ""; for (auto expr : block.GetExpressions()) { std::string info = ""; if (LocalVar* res = expr->GetResult(); res != nullptr) { info += res->GetIdentifier() + ": " + res->GetType()->ToString() + " = "; } info += expr->ToString(); ReplaceAll(info, "&", "&"); ReplaceAll(info, "<", "<"); ReplaceAll(info, ">", ">"); fout << ""; } fout << "
"; fout << "Block " << block.GetIdentifier() << "
" << info << "
>];" << std::endl; for (auto suc : block.GetSuccessors()) { fout << block.GetIdentifierWithoutPrefix() << " -> " << suc->GetIdentifierWithoutPrefix() << ";" << std::endl; } return VisitResult::CONTINUE; }); fout << "}" << std::endl; fout.close(); } void CHIRPrinter::PrintPackage(const Package& package, const std::string& fullPath) { std::fstream fout; fout.open(fullPath, std::ios::out | std::ios::app); if (!fout.is_open()) { std::cerr << "open file: " << fullPath << " failed!" << std::endl; return; } fout << package.ToString() << std::endl; fout.close(); } void CHIRPrinter::PrintCHIRSerializeInfo(ToCHIR::Phase phase, const std::string& path) { if (path.empty()) { Errorln("path empty"); return; } auto realDirPath = FileUtil::GetAbsPath(FileUtil::GetDirPath(path)); if (!realDirPath.has_value()) { Errorln("realDirPath false"); return; } auto fileNameWithExt = FileUtil::GetFileName(path); auto ret = FileUtil::JoinPath(realDirPath.value(), fileNameWithExt); std::fstream fout; fout.open(ret, std::ofstream::out); if (!fout.is_open()) { std::cerr << "open file: " << ret << " failed!" << std::endl; return; } std::string phaseStr{}; switch (phase) { case ToCHIR::Phase::RAW: phaseStr = "raw"; break; case ToCHIR::Phase::OPT: phaseStr = "opt"; break; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND case ToCHIR::Phase::PLUGIN: phaseStr = "plugin"; break; case ToCHIR::Phase::ANALYSIS_FOR_CJLINT: phaseStr = "analysis for cjlint"; break; #endif } fout << "ToCHIRPhase: " << phaseStr << std::endl; fout.close(); }cangjie_compiler-1.0.7/src/CHIR/CMakeLists.txt000066400000000000000000000031471510705540100210700ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB CHIR_BASE_SRC *.cpp) add_subdirectory(Analysis) add_subdirectory(AST2CHIR) add_subdirectory(Checker) add_subdirectory(Expression) add_subdirectory(GenerateVTable) add_subdirectory(Type) add_subdirectory(Visitor) add_subdirectory(NativeFFI) add_library(CangjieCHIRBase OBJECT ${CHIR_BASE_SRC}) add_subdirectory(Interpreter) add_subdirectory(Serializer) add_subdirectory(Transformation) add_library(CangjieCHIRExtra OBJECT ${CHIR_OPT_SRC} ${CHIRInterpreter} ${CHIRSerializer} ${ComputeAnnotations}) target_include_directories(CangjieCHIRExtra PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/AST2CHIR/TranslateASTNode/) # the interpreter needs third-party and also flatbuffers target_include_directories(CangjieCHIRExtra PRIVATE ${BOUNDSCHECK}/include) add_dependencies(CangjieCHIRExtra CangjieFlatbuffersHeaders) target_include_directories(CangjieCHIRExtra PRIVATE ${FLATBUFFERS_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/Annotations) # CHIR.cpp indirectly includes PackageFormat_generated.h, this will be refactored later. target_include_directories(CangjieCHIRBase PRIVATE ${FLATBUFFERS_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/Annotations) add_dependencies(CangjieCHIRBase CangjieFlatbuffersHeaders) target_link_libraries(CangjieCHIRExtra) target_compile_options(CangjieCHIRBase PRIVATE ${CJC_EXTRA_WARNINGS}) target_compile_options(CangjieCHIRExtra PRIVATE ${CJC_EXTRA_WARNINGS}) cangjie_compiler-1.0.7/src/CHIR/Checker/000077500000000000000000000000001510705540100176675ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CHIR/Checker/AnnotationChecker.cpp000066400000000000000000000516261510705540100240040ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/CHIR.h" #include "cangjie/CHIR/Interpreter/ConstEval.h" #include "cangjie/Utils/ProfileRecorder.h" namespace Cangjie::CHIR { using namespace AST; namespace { // used-defined @Annotation on a decl struct AnnotationInfo { const ClassDecl* decl; AST::Annotation* anno; // non-const for write back annotation result after consteval std::vector vars; // one global variable for each element in the argument `target` array; // empty if no argument is provided std::vector funcs; // init funcs of vars, to be called in file init function }; // custom annotation defined on a decl struct CustomAnnoInstance { const AST::Annotation* src; // from which ast node this annotation check info is generated. used for diagnostics const ClassDef* def; }; // all custom annotations on a TypeDef struct CustomAnnoInfoOnType { const CustomTypeDef* type; std::vector annos; }; struct CustomAnnoInfoOnDecl { const Decl* decl; // abstract function do not have CHIR symbol, use AST node std::vector annos; }; // static variables that records the info first and are used to check later // in case packages need compile in parallel, record g_annoInfo as member variable of class // AnnotationTranslator, store it somewhere else; the two collections can be collected as well for better performance // (one whole ast traversal less), or more conveniently not to store those two collections and to traverse the ast and // to check against the stored g_annoInfo instead. // record @Annotation info of translated Annotation classes std::unordered_map g_annoInfo{}; // record custom annotation info of any cangjie decl std::vector g_typeAnnoInfo{}; std::vector g_valueAnnoInfo{}; // custom annotation info on var, func, and prop } // namespace class AnnotationTranslator { public: AnnotationTranslator(Translator& translator, CHIRBuilder& builder, const GlobalOptions& opts) : tr{translator}, bd{builder}, parallel{opts.GetJobs() > 1UL} { } static bool IsImported(const Decl& decl) { if (decl.outerDecl) { if (auto generic = decl.outerDecl->genericDecl) { return generic->TestAttr(AST::Attribute::IMPORTED); } } if (decl.genericDecl) { return decl.genericDecl->TestAttr(AST::Attribute::IMPORTED); } return decl.TestAttr(AST::Attribute::IMPORTED); } void CollectAnnoInfo(const Decl& decl, const CustomTypeDef& type) && { if (SkipCollectCustomAnnoInfo(decl)) { // from ast node directly read annotation target info for (auto& anno : decl.annotations) { if (anno->kind == AST::AnnotationKind::ANNOTATION) { PushAnnotation(StaticCast(type), StaticCast(decl), *anno); // no custom annotation check required for imported decl return; } } return; } // collect both @Annotation and custom annotation in one annotation traverse std::vector res; for (size_t annoIndex = 0, customAnnoIdx = 0; annoIndex < decl.annotations.size(); ++annoIndex) { auto& anno = decl.annotations[annoIndex]; // collect @Annotation info if (anno->kind == AST::AnnotationKind::ANNOTATION) { CollectAnnotationInfo(*anno, StaticCast(decl), type); break; // cannot have both @Annotation and custom annotation } if (anno->kind == AnnotationKind::CUSTOM) { // collect custom annotation info for later check auto classDef = GetDefFromAnnotationInstance(*decl.annotationsArray->children[customAnnoIdx]); res.emplace_back(CustomAnnoInstance{anno.get(), classDef}); ++customAnnoIdx; } } if (!res.empty()) { PushTypeAnno(type, std::move(res)); } // collect custom annotations for members for (auto member : decl.GetMemberDeclPtrs()) { if (auto prop = DynamicCast(member)) { CollectPropAnnoInfo(*prop); } else { CollectAnnoInfo(*member); } } if (auto enDecl = DynamicCast(&decl)) { for (auto& cons : enDecl->constructors) { CollectAnnoInfo(*cons); } } } void CollectAnnoInfo(const Decl& decl) const { if (SkipCollectCustomAnnoInfo(decl)) { return; } if (auto res = CollectCustomAnnoInstances(decl); !res.empty()) { PushValueAnno(decl, std::move(res)); } if (auto func = DynamicCast(&decl)) { if (func->funcBody) { for (auto& param : func->funcBody->paramLists[0]->params) { if (auto res = CollectCustomAnnoInstances(*param); !res.empty()) { PushValueAnno(*param, std::move(res)); } } } } } private: Translator& tr; CHIRBuilder& bd; bool parallel; static std::mutex valueLock; static std::mutex typeLock; static std::mutex annoLock; void PushValueAnno(const Decl& decl, std::vector&& annos) const { if (parallel) { std::lock_guard g{valueLock}; g_valueAnnoInfo.emplace_back(CustomAnnoInfoOnDecl{&decl, std::move(annos)}); } else { g_valueAnnoInfo.emplace_back(CustomAnnoInfoOnDecl{&decl, std::move(annos)}); } } void PushTypeAnno(const CustomTypeDef& def, std::vector&& annos) const { if (parallel) { std::lock_guard g{typeLock}; g_typeAnnoInfo.emplace_back(CustomAnnoInfoOnType{&def, std::move(annos)}); } else { g_typeAnnoInfo.emplace_back(CustomAnnoInfoOnType{&def, std::move(annos)}); } } void PushAnnotation(const ClassDef& def, const ClassDecl& decl, AST::Annotation& anno) const { if (parallel) { std::lock_guard g{annoLock}; g_annoInfo.emplace(&def, AnnotationInfo{&decl, &anno}); } else { g_annoInfo.emplace(&def, AnnotationInfo{&decl, &anno}); } } void PushAnnotation(const ClassDef& def, AnnotationInfo&& info) const { if (parallel) { std::lock_guard g{annoLock}; g_annoInfo.emplace(&def, std::move(info)); } else { g_annoInfo.emplace(&def, std::move(info)); } } bool SkipCollectCustomAnnoInfo(const Decl& decl) const { if (tr.opts.enIncrementalCompilation && !decl.toBeCompiled) { return true; } return IsImported(decl); } // collect @Annotation info placed on `decl` void CollectAnnotationInfo(AST::Annotation& anno, const ClassDecl& decl, const CustomTypeDef& type) { if (anno.args.empty()) { // emplace empty annotation info for @Annotation without argument PushAnnotation(StaticCast(type), decl, anno); return; } auto info = CreateAnnotationInfo(decl, anno); PushAnnotation(StaticCast(type), std::move(info)); } // get the referenced CHIR Annotation class from a custom annotation instance (constructor call) ClassDef* GetDefFromAnnotationInstance(const Expr& expr) const { auto resolvedFunction = StaticCast(expr).resolvedFunction; CJC_NULLPTR_CHECK(resolvedFunction); auto type = tr.TranslateType(*resolvedFunction->outerDecl->ty); return StaticCast(StaticCast(type)->GetBaseType())->GetClassDef(); } // create a global variable for each element in the @Annotation targets. This can be reduced to a single var should // cangjie support array as literal type static std::string GetAnnotationVarName(const std::string& basename, size_t index) { return basename + ANNOTATION_VAR_POSTFIX + std::to_string(index); } // create a struct that contains all information of a @Annotation placed on a class AnnotationInfo CreateAnnotationInfo(const ClassDecl& decl, AST::Annotation& anno) { auto annoTargetNum = StaticCast(*anno.args[0]->expr).children.size(); std::vector vars(annoTargetNum); std::vector funcs(annoTargetNum); for (size_t i{0}; i < annoTargetNum; ++i) { auto var = CreateAnnotationVar(decl, anno, i); auto func = CreateAnnoVarInit(*var, anno, i); vars[i] = var; funcs[i] = func; } return {&decl, &anno, std::move(vars), std::move(funcs)}; } GlobalVar* CreateAnnotationVar(const ClassDecl& decl, const AST::Annotation& anno, size_t i) { auto& basename = decl.mangledName; auto varname = GetAnnotationVarName(basename, i); auto& expr = *StaticCast(*anno.args[0]->expr).children[i]; auto loc = tr.TranslateLocation(expr); auto annotationTargetType = tr.TranslateType(*expr.ty); auto varType = bd.GetType(annotationTargetType); auto srcCodeIdentifier = decl.identifier.Val() + ANNOTATION_VAR_POSTFIX + std::to_string(i); auto var = bd.CreateGlobalVar( std::move(loc), varType, varname, std::move(srcCodeIdentifier), varname, decl.fullPackageName); var->Set(SkipKind::SKIP_DCE_WARNING); var->EnableAttr(Attribute::COMPILER_ADD); var->EnableAttr(Attribute::NO_DEBUG_INFO); var->EnableAttr(Attribute::CONST); var->EnableAttr(Attribute::NO_REFLECT_INFO); var->Set(Linkage::INTERNAL); return var; } // we need a unique name for each annotation target element for each @Annotation instance. // @Annotation placed on VarWithPatternDecl cannot use the variable's identifier (it is empty), and we do not have // mangledName on CHIR, so we use its raw mangled name. static std::string GetAnnotationTargetElementInitName(const GlobalVar& var) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto name = MANGLE_CANGJIE_PREFIX + MANGLE_GLOBAL_VARIABLE_INIT_PREFIX; if (var.GetSrcCodeIdentifier().empty()) { name += var.GetRawMangledName(); } else { name += MangleUtils::MangleName(var.GetPackageName()) + MangleUtils::MangleName(var.GetSrcCodeIdentifier()); } return name + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX; #endif } Func* CreateAnnoVarInit(GlobalVar& var, AST::Annotation& anno, size_t i) { auto funcname = GetAnnotationTargetElementInitName(var); auto& expr = *StaticCast(*anno.args[0]->expr).children[i]; auto loc = tr.TranslateLocation(expr); auto func = tr.CreateEmptyGVInitFunc(funcname, "", funcname, var.GetPackageName(), Linkage::INTERNAL, loc, true); func->EnableAttr(Attribute::COMPILER_ADD); func->EnableAttr(Attribute::NO_DEBUG_INFO); auto value = Translator::TranslateASTNode(expr, tr); tr.GetCurrentBlock()->AppendExpression( bd.CreateExpression(std::move(loc), bd.GetUnitTy(), value, &var, tr.GetCurrentBlock())); if (auto curBlock = tr.GetCurrentBlock(); curBlock->GetTerminator() == nullptr) { curBlock->AppendExpression(bd.CreateTerminator(curBlock)); } var.SetInitFunc(*func); return func; } std::vector CollectCustomAnnoInstances(const Decl& decl) const { std::vector res; for (size_t annoIndex{0}, customIndex{0}; annoIndex < decl.annotations.size(); ++annoIndex) { auto& anno = decl.annotations[annoIndex]; if (anno->kind == AST::AnnotationKind::CUSTOM) { auto classDef = GetDefFromAnnotationInstance(*decl.annotationsArray->children[customIndex++]); res.emplace_back(CustomAnnoInstance{anno.get(), classDef}); } } return res; } void CollectPropAnnoInfo(const PropDecl& decl) const { if (auto res = CollectCustomAnnoInstances(decl); !res.empty()) { PushValueAnno(decl, std::move(res)); } } }; std::mutex AnnotationTranslator::valueLock{}; std::mutex AnnotationTranslator::typeLock{}; std::mutex AnnotationTranslator::annoLock{}; // interfaces between AnnotationTranslator and other modules are implemented here void Translator::CollectTypeAnnotation(const InheritableDecl& decl, const CustomTypeDef& cl) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // neither collect nor check annotations in compute annotations stage if (isComputingAnnos) { return; } #endif AnnotationTranslator{*this, builder, opts}.CollectAnnoInfo(decl, cl); } void Translator::CollectValueAnnotation(const Decl& decl) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (isComputingAnnos) { return; } #endif if (decl.TestAttr(AST::Attribute::IMPORTED) || decl.TestAttr(AST::Attribute::GENERIC_INSTANTIATED)) { return; } AnnotationTranslator{*this, builder, opts}.CollectAnnoInfo(decl); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void GlobalVarInitializer::InsertAnnotationVarInitInto(Func& packageInit) { for (auto& info : g_annoInfo) { for (auto init : info.second.funcs) { initFuncsForConstVar.emplace_back(init); InsertInitializerIntoPackageInitializer(*init, packageInit); } } } #endif namespace { struct AnnotationTarget { unsigned val; bool Matches(unsigned v) const { return (val & v) != 0; } }; const std::string_view ANNOTATION_TARGET_2_STRING[]{ "type", "parameter", "init", "member property", "member function", "member variable", "enum constructor", "global function", "global variable", "extend"}; class AnnotationChecker { public: explicit AnnotationChecker(const DiagAdapter& d) : diag{d} { } bool CheckAnnotations() && { Utils::ProfileRecorder p{"CHIR", "AnnotationTargetCheck"}; WriteBackAnnotations(); auto res = CheckAnnotationsImpl(); return res; } private: const DiagAdapter& diag; // write @Annotation info from the result of consteval back to the AST nodes void WriteBackAnnotations() { std::unordered_map cmap; for (auto info : g_annoInfo) { if (!info.first->TestAttr(Attribute::IMPORTED)) { // write back to AST if not imported WriteBackAnnotation(info.second); } // this operation is mutable and cannot be run in parallel cmap.emplace(info.first, AnnotationTarget{info.second.anno->target}); } g_annoInfo.clear(); checkMap = std::move(cmap); } void WriteBackAnnotation(const AnnotationInfo& info) const { auto& vars = info.vars; if (vars.empty()) { info.anno->EnableAllTargets(); return; } for (size_t i{0}; i < vars.size(); ++i) { if (!vars[i]->GetInitializer() || vars[i]->GetInitializer()->IsNullLiteral()) { // if const eval fails to replace the initializer with a constant value, find the store // statement from its init func info.anno->EnableTarget(static_cast(GetUnsignedValFromInit(*info.funcs[i]))); } else { info.anno->EnableTarget(static_cast( StaticCast(vars[i]->GetInitializer())->GetUnsignedVal())); } } } // check map, from Annotation class to AnnotationTarget std::unordered_map checkMap; static unsigned GetUnsignedValFromInit(const Func& func) { // the init func after consteval shall be // %1: unsigned64 = ConstantInt(xxx) // %2: Enum-AnnotationTarget = TypeCast(Enum-AnnotationTarget, %1) // %3 = Store(%2, `var`) // just find the int literal and retrieve its value for (auto exp : func.GetEntryBlock()->GetNonTerminatorExpressions()) { if (auto uval = DynamicCast(exp)) { return static_cast(uval->GetUnsignedIntLitVal()); } } InternalError("Annotation checking failed"); return 0; } // paralle version. Current impl uses the serialised version [[maybe_unused]] bool CheckAnnotationsParallelImpl() const { constexpr unsigned f{8}; constexpr unsigned g{9}; size_t threadNum = std::thread::hardware_concurrency() * f / g; Utils::TaskQueue qu{threadNum}; std::vector> results; for (auto& info : g_typeAnnoInfo) { results.emplace_back(qu.AddTask([&info, this] { return CheckType(info); })); } for (auto& info : g_valueAnnoInfo) { results.emplace_back(qu.AddTask([&info, this] { return CheckValue(info); })); } qu.RunAndWaitForAllTasksCompleted(); g_typeAnnoInfo.clear(); g_valueAnnoInfo.clear(); return std::all_of(results.begin(), results.end(), [](auto& res) { return res.get(); }); } bool CheckAnnotationsImpl() const { bool res = true; for (auto& info : g_typeAnnoInfo) { res = CheckType(info) && res; } for (auto& info : g_valueAnnoInfo) { res = CheckValue(info) && res; } return res; } using TargetT = AST::AnnotationTargetT; static TargetT GetTarget(const Decl& decl) { if (Is(decl)) { return static_cast(AST::AnnotationTarget::PARAMETER); } if (Is(decl)) { return static_cast(AST::AnnotationTarget::MEMBER_PROPERTY); } // enum constructor must be checked before var and func decl if (decl.TestAttr(AST::Attribute::ENUM_CONSTRUCTOR)) { return static_cast(AST::AnnotationTarget::ENUM_CONSTRUCTOR); } if (Is(decl)) { if (decl.TestAttr(AST::Attribute::GLOBAL)) { return static_cast(AST::AnnotationTarget::GLOBAL_VARIABLE); } return static_cast(AST::AnnotationTarget::MEMBER_VARIABLE); } auto func = StaticCast(&decl); if (func->TestAttr(AST::Attribute::CONSTRUCTOR)) { return static_cast(AST::AnnotationTarget::INIT); } if (decl.TestAttr(AST::Attribute::GLOBAL)) { return static_cast(AST::AnnotationTarget::GLOBAL_FUNCTION); } return static_cast(AST::AnnotationTarget::MEMBER_FUNCTION); } bool CheckValue(const CustomAnnoInfoOnDecl& info) const { auto targetid = GetTarget(*info.decl); unsigned target = 1u << targetid; bool res{true}; for (auto& annotation : info.annos) { auto targets = checkMap.at(annotation.def); if (!targets.Matches(target)) { (void)diag.diag.DiagnoseRefactor(DiagKindRefactor::chir_annotation_not_applicable, *annotation.src, annotation.src->identifier, std::string{ANNOTATION_TARGET_2_STRING[targetid]}); res = false; } } return res; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND static TargetT GetTarget(const CustomTypeDef& type) { return static_cast(Is(type) ? AST::AnnotationTarget::EXTEND : AST::AnnotationTarget::TYPE); } #endif bool CheckType(const CustomAnnoInfoOnType& info) const { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto targetid = GetTarget(*info.type); #endif unsigned target = 1u << targetid; bool res{true}; for (auto& annotation : info.annos) { auto targets = checkMap.at(annotation.def); if (!targets.Matches(target)) { (void)diag.diag.DiagnoseRefactor(DiagKindRefactor::chir_annotation_not_applicable, *annotation.src, annotation.src->identifier, std::string{ANNOTATION_TARGET_2_STRING[targetid]}); res = false; } } return res; } }; } // unnamed namespace bool ToCHIR::RunAnnotationChecks() { Utils::ProfileRecorder r{"CHIR", "AnnotationCheck"}; if (!AnnotationChecker{diag}.CheckAnnotations()) { return false; } return true; } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/Checker/CMakeLists.txt000066400000000000000000000005321510705540100224270ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB CHECKER_SRC *.cpp) set(CHIR_OPT_SRC ${CHIR_OPT_SRC} ${CHECKER_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/CHIR/Checker/ConstSafetyCheck.cpp000066400000000000000000000027141510705540100235770ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Checker/ConstSafetyCheck.h" #include "cangjie/CHIR/Analysis/Engine.h" using namespace Cangjie::CHIR; ConstSafetyCheck::ConstSafetyCheck(ConstAnalysisWrapper* constAnalysisWrapper) : analysisWrapper(constAnalysisWrapper) { } void ConstSafetyCheck::RunOnPackage(const Package& package, size_t threadNum) const { if (threadNum == 1) { for (auto func : package.GetGlobalFuncs()) { RunOnFunc(func); } } else { Utils::TaskQueue taskQueue(threadNum); for (auto func : package.GetGlobalFuncs()) { taskQueue.AddTask([this, func]() { return RunOnFunc(func); }); } taskQueue.RunAndWaitForAllTasksCompleted(); } } void ConstSafetyCheck::RunOnFunc(const Func* func) const { auto result = analysisWrapper->CheckFuncResult(func); CJC_ASSERT(result); const auto actionBeforeVisitExpr = [](const ConstDomain&, Expression*, size_t) {}; const auto actionAfterVisitExpr = [](const ConstDomain&, Expression*, size_t) {}; const auto actionOnTerminator = [](const ConstDomain&, Terminator*, std::optional) {}; result->VisitWith(actionBeforeVisitExpr, actionAfterVisitExpr, actionOnTerminator); }cangjie_compiler-1.0.7/src/CHIR/Checker/UnreachableBranchCheck.cpp000066400000000000000000000145531510705540100246700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Checker/UnreachableBranchCheck.h" using namespace Cangjie::CHIR; UnreachableBranchCheck::UnreachableBranchCheck( ConstAnalysisWrapper* constAnalysisWrapper, DiagAdapter& diag, const std::string& packageName) : diag(diag), analysisWrapper(constAnalysisWrapper), currentPackageName(packageName) { } namespace { std::string GetKeyWordBySourceExpr(const Branch& branch) { if (branch.GetSourceExpr() == SourceExpr::IF_EXPR) { return "if"; } else if (branch.GetSourceExpr() == SourceExpr::WHILE_EXPR) { return "while"; } else if (branch.GetSourceExpr() == SourceExpr::FOR_IN_EXPR) { return "for"; } return ""; } } // namespace void UnreachableBranchCheck::RunOnPackage(const Package& package, size_t threadNum) { if (threadNum == 1) { for (auto func : package.GetGlobalFuncs()) { /* The following code should not report warning. interface I { func test() : Bool { match(this) { case v: ?Int64 => false case v: Int64 => true case _ => false }}} */ if (func->Get() == SkipKind::SKIP_DCE_WARNING) { continue; } RunOnFunc(func); } } else { Utils::TaskQueue taskQueue(threadNum); // Check in generic decl is not currently supported, as constant analysis does not yet support. for (auto func : package.GetGlobalFuncs()) { if (func->Get() == SkipKind::SKIP_DCE_WARNING) { continue; } taskQueue.AddTask([this, func]() { return RunOnFunc(func); }); } taskQueue.RunAndWaitForAllTasksCompleted(); } } void UnreachableBranchCheck::PrintWarning( const Terminator& node, Block& block, std::set& hasProcessed, bool isRecursive) { if (hasProcessed.find(&block) != hasProcessed.end()) { return; } hasProcessed.emplace(&block); // Do-while expr do not need to check unreachable branch. if (block.Get() == SkipKind::SKIP_DCE_WARNING || node.Get() == SkipKind::SKIP_DCE_WARNING) { return; } CJC_ASSERT(block.GetTerminator()); if (block.GetTerminator()->GetExprKind() == ExprKind::BRANCH || block.GetTerminator()->GetExprKind() == ExprKind::MULTIBRANCH) { auto branchNode = StaticCast(block.GetTerminator()); bool rec{true}; if (auto br = DynamicCast(branchNode)) { if (br->GetSourceExpr() == SourceExpr::FOR_IN_EXPR) { rec = false; } } if (rec) { for (auto successor : branchNode->GetSuccessors()) { PrintWarning(*branchNode, *successor, hasProcessed, true); } } } auto [res, range] = ToRangeIfNotZero(block.GetDebugLocation()); if (!res) { return; } if (IsCrossPackage(range.begin, currentPackageName, diag)) { return; } if (node.GetExprKind() == ExprKind::BRANCH) { auto branch = StaticCast(&node); if (branch->GetSourceExpr() == SourceExpr::QUEST || branch->GetSourceExpr() == SourceExpr::BINARY) { if (!isRecursive) { (void)diag.DiagnoseRefactor(DiagKindRefactor::chir_dce_unreachable_expression, range); } } else if (branch->GetSourceExpr() == SourceExpr::MATCH_EXPR) { (void)diag.DiagnoseRefactor(DiagKindRefactor::chir_unreachable_pattern, range); } else { auto keyWord = GetKeyWordBySourceExpr(*branch); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::chir_dce_unreachable_block_in_expression, range, keyWord); builder.AddMainHintArguments(keyWord); } } else { (void)diag.DiagnoseRefactor(DiagKindRefactor::chir_unreachable_pattern, range); } } void UnreachableBranchCheck::RunOnFunc(const Ptr func) { // we should check the generic func, not the instantiated func. if (func->TestAttr(Attribute::GENERIC_INSTANTIATED)) { return; } bool isCommonFunctionWithoutBody = func->TestAttr(Attribute::SKIP_ANALYSIS); if (isCommonFunctionWithoutBody) { return; // Nothing to visit } auto result = analysisWrapper->CheckFuncResult(func); CJC_ASSERT(result); const auto actionBeforeVisitExpr = [](const ConstDomain&, Expression*, size_t) {}; const auto actionAfterVisitExpr = [](const ConstDomain&, Expression*, size_t) {}; const auto actionOnTerminator = [this]( const ConstDomain&, Terminator* terminator, std::optional targetSucc) { switch (terminator->GetExprKind()) { case ExprKind::BRANCH: { if (targetSucc.has_value()) { auto branchNode = StaticCast(terminator); auto successors = terminator->GetSuccessors(); std::set hasProcessed; for (auto successor : successors) { if (successor == targetSucc.value()) { continue; } PrintWarning(*branchNode, *successor, hasProcessed); } } break; } case ExprKind::MULTIBRANCH: { if (targetSucc.has_value()) { auto multiBranchNode = StaticCast(terminator); auto successors = terminator->GetSuccessors(); std::set hasProcessed; for (auto successor : successors) { if (successor == targetSucc.value()) { continue; } PrintWarning(*multiBranchNode, *successor, hasProcessed); } } break; } default: break; } }; result->VisitWith(actionBeforeVisitExpr, actionAfterVisitExpr, actionOnTerminator); } cangjie_compiler-1.0.7/src/CHIR/Checker/VarInitCheck.cpp000066400000000000000000000507121510705540100227120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Checker/VarInitCheck.h" #include "cangjie/CHIR/Analysis/Engine.h" #include "cangjie/CHIR/Analysis/MaybeInitAnalysis.h" #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/Utils/TaskQueue.h" using namespace Cangjie::CHIR; VarInitCheck::VarInitCheck(DiagAdapter* diag) : diag(diag) { } void VarInitCheck::RunOnPackage(const Package* package, size_t threadNum) { std::vector funcs; for (auto func : package->GetGlobalFuncs()) { if (func->GetSrcCodeIdentifier().find("__ad_") == std::string::npos && func->GetSrcCodeIdentifier() != STATIC_INIT_FUNC && func->GetIdentifier().find("$Mocked") == std::string::npos && func->GetFuncKind() != FuncKind::INSTANCEVAR_INIT && // only cjnative for cjmp func->Get() != SkipKind::SKIP_VIC) { funcs.emplace_back(func); } } if (funcs.empty()) { return; } if (threadNum == 1) { for (auto func : funcs) { RunOnFunc(func); } } else { Utils::TaskQueue taskQueue(threadNum); for (auto func : funcs) { taskQueue.AddTask([this, func]() { return RunOnFunc(func); }); } taskQueue.RunAndWaitForAllTasksCompleted(); } } void VarInitCheck::RunOnFunc(const Func* func) { std::vector members; auto ctorInitInfo = std::make_unique(); if (func->IsConstructor()) { auto customTypeDef = func->GetOuterDeclaredOrExtendedDef(); CJC_NULLPTR_CHECK(customTypeDef); members = customTypeDef->GetAllInstanceVars(); ctorInitInfo->thisCustomDef = customTypeDef; if (customTypeDef->GetCustomKind() == CustomDefKind::TYPE_CLASS) { auto classDef = StaticCast(customTypeDef); ctorInitInfo->localMemberNums = classDef->GetDirectInstanceVarNum(); // We do a check here as class-Object does not have a super class. if (auto superClassDef = classDef->GetSuperClassDef(); superClassDef) { ctorInitInfo->superClassDef = superClassDef; ctorInitInfo->superMemberNums = members.size() - ctorInitInfo->localMemberNums; } } else { ctorInitInfo->localMemberNums = members.size(); } } UseBeforeInitCheck(func, ctorInitInfo.get(), members); ReassignInitedLetVarCheck(func, ctorInitInfo.get(), members); } template void VarInitCheck::CheckMemberFuncCall(const MaybeUninitDomain& state, const Func& initFunc, const TApply& apply) const { // Calling any member function in an initializer before all the member of this class/struct has // been initialised is illegal. auto callee = apply.GetCallee(); if (!callee->IsFuncWithBody()) { return; } auto calleeFunc = VirtualCast(callee); auto thisArg = initFunc.GetParam(0); // check the callee is a member function and if all the member has been initialised // note: calling other initialiser of this class/struct is ok if (calleeFunc->GetOuterDeclaredOrExtendedDef() == initFunc.GetOuterDeclaredOrExtendedDef() && !calleeFunc->IsConstructor() && !calleeFunc->TestAttr(Attribute::STATIC) && !state.GetMaybeUninitedLocalMembers().empty() && !calleeFunc->IsInstanceVarInit()) { if (auto arg = apply.GetArgs()[0]; arg == thisArg) { // class member function // func init(%0 : CA&) // ... // %1 = Apply(@memberFunc, %0) illegal if not all the member has been inited currently // ... RaiseIllegalMemberFunCallError(&apply, calleeFunc); } else if (arg->IsLocalVar()) { // struct member function // func init(%0 : SA&) // ... // %1 : SA = Load(%0) // %2 = Apply(@memberFunc, %1) illegal if not all the member has been inited currently // ... auto argExpr = static_cast(arg)->GetExpr(); if (argExpr->GetExprKind() == ExprKind::LOAD && static_cast(argExpr)->GetLocation() == thisArg) { RaiseIllegalMemberFunCallError(&apply, calleeFunc); } } } } // Update only cjnative for cjmp void VarInitCheck::UseBeforeInitCheck( const Func* func, const ConstructorInitInfo* ctorInitInfo, const std::vector& members) { bool isCommonFunctionWithoutBody = func->TestAttr(Attribute::SKIP_ANALYSIS); if (isCommonFunctionWithoutBody) { return; // Nothing to visit } auto analysis = std::make_unique(func, ctorInitInfo); auto engine = Engine(func, std::move(analysis)); auto result = engine.IterateToFixpoint(); CJC_NULLPTR_CHECK(result); std::vector callStack{func}; const auto actionBeforeVisitExpr = [this, &callStack, &members]( const MaybeUninitDomain& state, Expression* expr, size_t) { if (expr->GetExprKind() == ExprKind::APPLY) { if (auto varInitFunc = TryGetInstanceVarInitFromApply(*expr)) { callStack.push_back(varInitFunc); } } auto initialFunc = callStack[0]; auto curFunc = callStack.back(); if (expr->GetExprKind() == ExprKind::LOAD) { if (CheckLoadToUninitedAllocation(state, *StaticCast(expr))) { return; } } if (expr->GetExprKind() == ExprKind::GET_ELEMENT_REF) { if (CheckGetElementRefToUninitedAllocation(state, *StaticCast(expr))) { return; } } if (!initialFunc->IsConstructor()) { return; } if (expr->GetExprKind() == ExprKind::LOAD) { CheckLoadToUninitedCustomDefMember(state, curFunc, StaticCast(expr), members); } else if (expr->GetExprKind() == ExprKind::STORE_ELEMENT_REF) { CheckStoreToUninitedCustomDefMember(state, curFunc, StaticCast(expr), members); } else if (expr->GetExprKind() == ExprKind::APPLY) { CheckMemberFuncCall(state, *curFunc, *StaticCast(expr)); } }; const auto actionAfterVisitExpr = [&callStack](const MaybeUninitDomain&, Expression* expr, size_t) { if (expr->GetExprKind() == ExprKind::APPLY) { if (auto varInitFunc = TryGetInstanceVarInitFromApply(*expr)) { callStack.pop_back(); } } }; const auto actionOnTerminator = [this, func, &members](const MaybeUninitDomain& state, const Terminator* terminator, std::optional) { if (!func->IsConstructor()) { return; } // If the current function is a initialiesr of a class/struct. We need to check if all the member // variables has been initialised before exiting the initialiser. // In CHIR, we will this when the currently processed expression is an Exit. We does not check // RaiseException terminators as that means an exception has occurred. // However, there are still some unusual Exit terminators that we won't check. These are: // a) Exit generated by For-in translator; // During the translation for for-in nodes, there are some compiler-added Exit terminators. // Checking these Exits can lead to false positives as these are `fake` Exits. // b) Exit in a local func // An initialiser can not be a local func. We only need to check the exit point of the initialiser. // c) Exit due to Nothing expressions // For example, // CJ: // func foo(): Nothing { throw Exception() } // class CA { var a: Int32; init() { foo() } } => no vic error, as as exception must be thrown // ==================================================== // CHIR: // func @CA_init() { ...; %0: Nothing = Apply(@foo); Exit() } this Exit doesn't need to be checked // // In a CHIR function, only function calls with a return type of Nothing can produce a value of Nothing // type (does not include Constant(Null)). And the callee must exit the function by throwing an exception. // Thus, we don't need to check Exit due to Nothing expressions. if (terminator->GetExprKind() == ExprKind::EXIT) { auto parent = terminator->GetParentBlock(); bool isForInGeneratedExit = terminator->Get() == SkipKind::SKIP_FORIN_EXIT; bool isExitForLocalFunc = parent->GetParentBlockGroup() != func->GetBody(); auto parentBlockSize = parent->GetExpressions().size(); bool isNothingExit = parentBlockSize > 1U && parent->GetExpressionByIdx(parentBlockSize - 2U)->GetResultType()->IsNothing(); if (isForInGeneratedExit || isExitForLocalFunc || isNothingExit) { return; } if (auto uninitedMembers = state.GetMaybeUninitedLocalMembers(); !uninitedMembers.empty()) { RaiseUninitedDefMemberError(state, func, members, uninitedMembers); } } else if (terminator->GetExprKind() == ExprKind::APPLY_WITH_EXCEPTION) { CheckMemberFuncCall(state, *func, *StaticCast(terminator)); } }; result->VisitWith(actionBeforeVisitExpr, actionAfterVisitExpr, actionOnTerminator); } bool VarInitCheck::CheckLoadToUninitedAllocation(const MaybeUninitDomain& state, const Load& load) const { auto targetVal = load.GetLocation(); if (state.IsMaybeUninitedAllocation(targetVal).value_or(false)) { auto identifier = targetVal->GetSrcCodeIdentifier(); auto builder = diag->DiagnoseRefactor( DiagKindRefactor::chir_used_before_initialization, ToPosition(load.GetDebugLocation()), identifier); AddMaybeInitedPosNote(builder, identifier, state.GetMaybeInitedPos(targetVal)); return true; } return false; } bool VarInitCheck::CheckGetElementRefToUninitedAllocation( const MaybeUninitDomain& state, const GetElementRef& getElementRef) const { auto targetVal = getElementRef.GetLocation(); auto oriType = targetVal->GetType()->StripAllRefs(); if (!oriType->IsStruct() && !oriType->IsEnum()) { // class need load before use, do not check it, only check struct and enum return false; } if (state.IsMaybeUninitedAllocation(targetVal).value_or(false)) { auto identifier = targetVal->GetSrcCodeIdentifier(); auto builder = diag->DiagnoseRefactor( DiagKindRefactor::chir_used_before_initialization, ToPosition(getElementRef.GetDebugLocation()), identifier); AddMaybeInitedPosNote(builder, identifier, state.GetMaybeInitedPos(targetVal)); return true; } return false; } namespace { std::optional IsGetElemRefViaDefMember(const Expression& expr, const Parameter& thisArg) { if (expr.GetExprKind() != ExprKind::GET_ELEMENT_REF) { return std::nullopt; } auto getElemRef = Cangjie::StaticCast(&expr); auto location = getElemRef->GetLocation(); if (location == &thisArg) { return getElemRef->GetPath()[0]; } else if (location->IsLocalVar()) { auto locExpr = Cangjie::StaticCast(location)->GetExpr(); return IsGetElemRefViaDefMember(*locExpr, thisArg); } return std::nullopt; } } // namespace void VarInitCheck::CheckLoadToUninitedCustomDefMember( const MaybeUninitDomain& state, const Func* func, const Load* load, const std::vector& members) const { auto targetVal = load->GetLocation(); if (targetVal->IsLocalVar()) { auto expr = StaticCast(targetVal)->GetExpr(); // Check if we are accessing an uninitialised class/struct member or a member of an uninitialised member. // For example: // Func @(%ca: CA&) // ... // %0 = GetElementRef(%ca, 0) or GetElementRef(%ca, 0, 1) // %1 = Load(%0) // ... // illegal if %ca.0 has not been initialised auto index = IsGetElemRefViaDefMember(*expr, *func->GetParam(0)); if (index.has_value()) { return CheckUninitedDefMember(state, load, members, index.value()); } } } namespace { std::optional IsStoreViaDefMember( const StoreElementRef& store, const Parameter& thisArg, bool shouldBeNested = false) { auto location = store.GetLocation(); // The parameter `shouldBeNested` means if the target of the xxxGetElementRef must be // a nested class/struct member. if (location == &thisArg && (!shouldBeNested || store.GetPath().size() > 1)) { return store.GetPath()[0]; } else if (location->IsLocalVar()) { auto expr = Cangjie::StaticCast(location)->GetExpr(); return IsGetElemRefViaDefMember(*expr, thisArg); } return std::nullopt; } } // namespace void VarInitCheck::CheckStoreToUninitedCustomDefMember(const MaybeUninitDomain& state, const Func* func, const StoreElementRef* store, const std::vector& members) const { // a) // Check if we are assigning values to a nested member of an unitialised class/struct member. // note: assigning values to an unitialised member is totally fine. // For example: // Func @(%ca: CA&) // ... // %0 = StoreElementRef(%ca, 0, 1) illegal if %ca.0 has not been initialised // ... // %1 = GetElementRef(%ca, 1) // %2 = StoreGetElementRef(xx, %1, 0) illegal if %ca.1 has not been initialised // ... if (auto index = IsStoreViaDefMember(*store, *func->GetParam(0), /* shouldBeNested = */ true)) { return CheckUninitedDefMember(state, store, members, index.value()); } // b) // Check if we are assigning values to an uninitialised member of the super class, or a nested // member of the uninitialised member. // For example: // Func @(%ca: CA&) // ... // %0 = StoreElementRef(%ca, 0) or StoreElementRef(%ca, 0, 1) // ... // illegal if %ca.0 is an uninitialised super member when super() has not been called. if (!func->IsClassMethod()) { return; } if (auto index = IsStoreViaDefMember(*store, *func->GetParam(0))) { return CheckUninitedDefMember(state, store, members, index.value(), /* onlyCheckSuper = */ true); } } void VarInitCheck::RaiseUninitedDefMemberError(const MaybeUninitDomain& state, const Func* func, const std::vector& members, const std::vector& uninitedMemberIdx) const { // Skip report error when uninitedMember are all common member for CJMP. if (std::all_of(uninitedMemberIdx.begin(), uninitedMemberIdx.end(), [&members](size_t idx) { auto member = members[idx]; // Skip COMMON which is just added(not deserialized ones), they may not yet be initialized and it's ok. return member.TestAttr(Attribute::COMMON) && !member.TestAttr(Attribute::DESERIALIZED); })) { return; } auto builder = diag->DiagnoseRefactor(DiagKindRefactor::chir_class_uninitialized_field, ToPosition(func->GetDebugLocation())); for (auto idx : uninitedMemberIdx) { auto identifier = members[idx].name; std::string msg = "in line " + std::to_string(members[idx].loc.GetBeginPos().line) + ", variable '" + identifier + "' need to be initialized"; builder.AddNote(msg); AddMaybeInitedPosNote(builder, identifier, state.GetMaybeInitedPos(idx)); } } void VarInitCheck::CheckUninitedDefMember(const MaybeUninitDomain& state, const Expression* expr, const std::vector& members, size_t index, bool onlyCheckSuper) const { auto checkUninitedRes = state.IsMaybeUninitedMember(index); if (checkUninitedRes == MaybeUninitDomain::UninitedMemberKind::SUPER_MEMBER) { diag->DiagnoseRefactor(DiagKindRefactor::chir_illegal_usage_of_super_member, ToPosition(expr->GetDebugLocation()), members[index].name); } else if (checkUninitedRes == MaybeUninitDomain::UninitedMemberKind::LOCAL_MEMBER && !onlyCheckSuper) { auto identifier = members[index].name; auto builder = diag->DiagnoseRefactor( DiagKindRefactor::chir_used_before_initialization, ToPosition(expr->GetDebugLocation()), identifier); AddMaybeInitedPosNote(builder, identifier, state.GetMaybeInitedPos(index)); } } void VarInitCheck::AddMaybeInitedPosNote( DiagnosticBuilder& builder, const std::string& identifier, const std::set& maybeInitedPos) const { if (maybeInitedPos.empty()) { return; } std::stringstream ss; ss << "we detected variable '" << identifier << "' is assigned in line "; for (auto pos : maybeInitedPos) { ss << pos << ", "; } ss << "but in runtime, these codes might not be executed."; builder.AddNote(ss.str()); } void VarInitCheck::RaiseIllegalMemberFunCallError(const Expression* apply, const Func* memberFunc) const { auto identifier = memberFunc->GetSrcCodeIdentifier(); auto calleeFuncKind = memberFunc->GetFuncKind(); if (calleeFuncKind == FuncKind::GETTER || calleeFuncKind == FuncKind::SETTER) { constexpr size_t getterNameLength{4}; // length of "$get" or "$set" identifier = identifier.substr(1, identifier.length() - getterNameLength); } diag->DiagnoseRefactor( DiagKindRefactor::chir_illegal_usage_of_member, ToPosition(apply->GetDebugLocation()), identifier); } void VarInitCheck::ReassignInitedLetVarCheck(const Func* func, const ConstructorInitInfo* ctorInitInfo, const std::vector& members) const { bool isCommonFunctionWithoutBody = func->TestAttr(Attribute::SKIP_ANALYSIS); if (isCommonFunctionWithoutBody) { return; // Nothing to visit } auto analysis = std::make_unique(func, ctorInitInfo); auto engine = Engine(func, std::move(analysis)); auto result = engine.IterateToFixpoint(); CJC_NULLPTR_CHECK(result); const auto actionBeforeVisitExpr = [this, func, &members](const MaybeInitDomain& state, Expression* expr, size_t) { if (expr->GetExprKind() == ExprKind::STORE) { auto store = StaticCast(expr); auto targetVal = store->GetLocation(); // check reassign to let-defined local variables; if (!targetVal->IsLocalVar()) { return; } if (targetVal->TestAttr(Attribute::READONLY) && state.IsMaybeInitedAllocation(targetVal).value_or(false)) { diag->DiagnoseRefactor(DiagKindRefactor::chir_cannot_assign_initialized_let_variable, ToPosition(expr->GetDebugLocation())); } } else if (expr->GetExprKind() == ExprKind::STORE_ELEMENT_REF) { CheckStoreToInitedCustomDefMember(state, func, StaticCast(expr), members); } }; const auto actionAfterVisitExpr = [](const MaybeInitDomain&, Expression*, size_t) {}; const auto actionOnTerminator = [](const MaybeInitDomain&, Terminator*, std::optional) {}; result->VisitWith(actionBeforeVisitExpr, actionAfterVisitExpr, actionOnTerminator); } void VarInitCheck::CheckStoreToInitedCustomDefMember(const MaybeInitDomain& state, const Func* func, const StoreElementRef* store, const std::vector& members) const { // If the function is an initialiser, we will check if it's a StoreElementRef to an unitialised member of the // class/struct. If it is, this StoreElementRef is the initialisation operation and legal. Otherwise, we will // raise an error. if (!func->IsConstructor()) { return; } auto& paths = store->GetPath(); if (store->GetLocation() == func->GetParam(0) && paths.size() == 1) { if (members[paths[0]].TestAttr(Attribute::READONLY) && state.IsMaybeInitedMember(paths[0]) != MaybeInitDomain::InitedMemberKind::NA) { // this `let-defined` member variable has already been initialised diag->DiagnoseRefactor( DiagKindRefactor::chir_cannot_assign_initialized_let_variable, ToPosition(store->GetDebugLocation())); } return; } } cangjie_compiler-1.0.7/src/CHIR/ComputeAnnotations.cpp000066400000000000000000000616201510705540100226660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. // This files declares the entry of ComputeAnnotationsBeforeCheck stage. #include #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/CHIR/Checker/ComputeAnnotations.h" namespace Cangjie::CHIR { using namespace AST; namespace { static_assert(sizeof(Number) == 8); std::unordered_map g_typeMap{}; std::unordered_map g_valueMap{}; /// map annoFactoryFunc to original Decl, used to write back annotation info std::unordered_map g_annoFactoryMap{}; AnnoInstanceValue GetValue(const Value& v); AnnoInstanceValue CreateValue(const LiteralValue& v) { if (auto lit = DynamicCast(&v)) { return {static_cast(lit->GetUnsignedVal())}; } if (auto lit = DynamicCast(&v)) { return {static_cast(lit->GetVal())}; } if (auto lit = DynamicCast(&v)) { return {lit->GetVal()}; } if (auto lit = DynamicCast(&v)) { return {static_cast(lit->GetVal())}; } if (auto lit = DynamicCast(&v)) { return {lit->GetVal()}; } // null or unit return {std::monostate{}}; } std::vector> GetTupleArgs(const Tuple& tuple) { std::vector> args{}; for (auto v : tuple.GetOperands()) { args.emplace_back("", GetValue(*v)); } return args; } AnnoInstanceValue CreateEnumValue(const LocalVar& obj) { if (auto tuple = DynamicCast(obj.GetExpr())) { // enum with args return {std::make_shared(AnnoInstanceClassInst{ g_typeMap.at(StaticCast(obj.GetType())->GetEnumDef()), GetTupleArgs(*tuple)})}; } // enum by integer auto tc = StaticCast(obj.GetExpr()); return GetValue(*tc->GetSourceValue()); } /// create value of CustomTypeDef type, that is, struct/enum/class AnnoInstanceValue CreateValueOfCustomTypeDef(const Value& v) { auto obj = StaticCast(&v); CustomTypeDef* def; auto alloc = DynamicCast(obj->GetExpr()); if (alloc) { // the value is an alloc, so it is a class type object def = StaticCast(alloc->GetType()->StripAllRefs())->GetClassDef(); } else { // the value is a load of an alloc, it is a struct type object auto loadOfAlloc = StaticCast(obj->GetExpr()); alloc = StaticCast(StaticCast(loadOfAlloc->GetLocation())->GetExpr()); def = StaticCast(obj->GetType())->GetStructDef(); } auto fields = def->GetAllInstanceVars(); std::vector> ctorArgs{}; // store StorElementRef values to the result for (auto user : alloc->GetResult()->GetUsers()) { if (auto store = DynamicCast(user); store && store->GetLocation() == alloc->GetResult()) { if (auto ty = store->GetValue()->GetType(); ty->IsUnit() || ty->IsNothing()) { continue; } #ifndef NDEBUG auto nothingExprCheck = StaticCast(store->GetValue())->GetExpr(); if (auto constant = DynamicCast(nothingExprCheck)) { if (constant->GetValue()->IsNullLiteral()) { std::cerr << nothingExprCheck->GetResult()->ToString() << '\n'; CJC_ASSERT(false && "Invalid node from interpreter."); } } #endif auto value = GetValue(*store->GetValue()); CJC_ASSERT(value); ctorArgs.emplace_back(fields[store->GetPath()[0]].name, std::move(value)); } } AnnoInstanceValue res{ std::make_shared(AnnoInstanceClassInst{g_typeMap.at(def), std::move(ctorArgs)})}; g_valueMap[&v] = res; return res; } AnnoInstanceValue GetValue(const GlobalVar& gv) { for (auto user : gv.GetUsers()) { if (auto store = DynamicCast(user); store && store->GetLocation() == &gv) { return GetValue(*store->GetValue()); } } CJC_ABORT(); #ifdef NDEBUG return {std::monostate{}}; #endif } AnnoInstanceValue CreateValue(const Value& v) { if (auto lit = DynamicCast(&v)) { return CreateValue(*lit); } if (auto obj = DynamicCast(&v)) { if (auto c = DynamicCast(obj->GetExpr())) { return GetValue(*c->GetValue()); } if (obj->GetType()->IsEnum()) { return CreateEnumValue(*obj); } if (obj->GetType()->StripAllRefs()->IsBox()) { return GetValue(*StaticCast(obj->GetExpr())->GetSourceValue()); } if (auto t = DynamicCast(obj->GetExpr())) { return {std::make_shared(AnnoInstanceClassInst{nullptr, GetTupleArgs(*t)})}; } if (auto c = DynamicCast(obj->GetExpr())) { if (c->GetSourceTy()->IsClassRef() && c->GetTargetTy()->IsClassRef()) { // cast between subtypes, we cannot test subtype relation without CHIRBuilder // so we skip this assertion return GetValue(*c->GetSourceValue()); } } if (auto l = DynamicCast(obj->GetExpr())) { if (auto gv = DynamicCast(l->GetLocation())) { // load of gv, valid iff the location is a const global variable lifted from annotationsArray. // Threotically we check it's of Annotation class type, but we do not have such utilities. CJC_ASSERT(gv->GetType()->StripAllRefs()->IsClass()); auto ret = GetValue(*gv); CJC_ASSERT(ret.Object()); return ret; } } // not a constant, v must be of const class/struct/enum type return CreateValueOfCustomTypeDef(v); } CJC_ABORT(); #ifdef NDEBUG return {std::monostate{}}; #endif } AnnoInstanceValue GetValue(const Value& v) { if (auto it = g_valueMap.find(&v); it != g_valueMap.end()) { return it->second; } return CreateValue(v); } std::vector CreateAnnoInstFromConstEval(const Func& func) { auto arrRef = func.GetReturnValue(); auto users = arrRef->GetUsers(); CJC_ASSERT(!users.empty()); auto args = StaticCast(arrRef->GetUsers()[0])->GetArgs(); CJC_ASSERT(args[0] == arrRef); auto rawArray = StaticCast(args[1]); size_t last{0}; std::vector ret{}; for (auto use : rawArray->GetUsers()) { if (auto store = DynamicCast(use); store && store->GetLocation() == rawArray) { CJC_ASSERT(store->GetLocation() == rawArray); auto& path = store->GetPath(); CJC_ASSERT(path.size() == 1); CJC_ASSERT(path[0] >= last); auto obj = StaticCast(store->GetValue()); if (obj->TestAttr(CHIR::Attribute::NO_REFLECT_INFO)) { continue; } auto typecast = StaticCast(obj->GetExpr())->GetSourceValue(); auto load = StaticCast(StaticCast(typecast)->GetExpr()); ret.push_back(GetValue(*load->GetResult()).Object()); last = path[0]; } } return ret; } AnnoMap CreateAnnoInstFromConstEvalRes(ConstEvalResult& ev) { std::unordered_map> annoInstMap{}; for (auto func : ev.pkg->GetGlobalFuncs()) { if (func->GetFuncKind() == FuncKind::ANNOFACTORY_FUNC) { auto v = CreateAnnoInstFromConstEval(*func); if (!v.empty()) { annoInstMap[g_annoFactoryMap[func]] = std::move(v); } } } return annoInstMap; } } // namespace void ConstEvalResult::Dump() const { std::vector*>> ordered; for (auto& v : map) { ordered.emplace_back(v.first, &v.second); } std::sort(ordered.begin(), ordered.end(), [](auto& l, auto& r) { return l.first->begin < r.first->begin; }); for (auto& v : ordered) { std::cout << v.first->mangledName << ":\n"; for (auto& k : *v.second) { std::cout << " " << k->ToString() << '\n'; } } } Number AnnoInstanceValue::Value() const { return std::get(*this); } long long AnnoInstanceValue::SignedValue() const { return static_cast(std::get(*this)); } unsigned AnnoInstanceValue::Rune() const { return static_cast(std::get(*this)); } bool AnnoInstanceValue::Bool() const { return static_cast(std::get(*this)); } const std::string& AnnoInstanceValue::String() const { return std::get(*this); } std::shared_ptr AnnoInstanceValue::Object() const { return std::get>(*this); } AnnoInstanceValue::operator bool() const noexcept { return !std::holds_alternative(*this); } std::string AnnoInstanceValue::ToString() const noexcept { if (std::holds_alternative(*this)) { return std::to_string(std::get(*this)); } if (std::holds_alternative(*this)) { return std::string{'"'} + std::get(*this) + '"'; } if (std::holds_alternative(*this)) { return std::to_string(std::get(*this)); } if (std::holds_alternative>(*this)) { return std::get>(*this)->ToString(); } return "UNINITIALIZED"; } std::string AnnoInstanceClassInst::ToString() const { std::stringstream ss; if (cl) { ss << cl->identifier.Val() << "{"; } else { ss << "("; } for (size_t i{0}; i < a.size(); ++i) { auto& field{a[i]}; if (!field.first.empty()) { ss << '"' << field.first << "\": "; } ss << field.second.ToString(); if (i != a.size() - 1) { ss << ","; } } if (cl) { ss << '}'; } else { ss << ')'; } return ss.str(); } namespace { /// The spec states that enum ctor call with all args const expression is a const expression, but does not state which /// ctors are const expressions. /// We suppose if a ctor is possibly called in a enum ctor call is a const ctor. struct EnumCtorConstChecker { bool Check(const Decl& cons) { visited.insert(StaticCast(cons.outerDecl)); return IsConstEnumCtor(cons); } private: // enum ctor param type may recursively depend on other types. In such case, this ctor is possibly const. std::set visited; /// A type decl is const, if and only if /// 1. it has a const init, or /// 2. it has const member and can be constructed in a const /// expression without explcit call of init (in this case, it must be a builtin type). /// 3. enum decl, if any constructor is 1) either parameterless, 2) or all parameters are of const type, then it is /// a const decl. bool IsConst(InheritableDecl& type) { if (type.ty->IsString()) { return true; } if (auto em = DynamicCast(&type)) { if (auto [_, suc] = visited.insert(em); suc) { return true; } for (auto& cons : em->constructors) { if (IsConstEnumCtor(*cons)) { return true; } } return false; } for (auto decl : type.GetMemberDeclPtrs()) { if (decl->IsConst() && decl->TestAttr(AST::Attribute::CONSTRUCTOR)) { return true; } } return false; } bool IsConstEnumCtor(const Decl& cons) { if (Is(cons)) { return true; } auto& fun = StaticCast(cons); for (auto& p : fun.funcBody->paramLists[0]->params) { if (!IsConst(p->ty)) { return false; } } return true; } bool IsConst(Ptr ty) { if (Is(ty)) { return true; } auto decl = Ty::GetDeclPtrOfTy(ty); // get the generic version decl if (!decl) { if (ty->kind == TypeKind::TYPE_VARRAY) { return false; } // all other builtin types are const type return ty->IsBuiltin(); } return IsConst(*StaticCast(decl)); } }; bool IsConstEnumCtor(const Decl& cons) { if (Is(cons)) { return true; } EnumCtorConstChecker checker{}; return checker.Check(cons); } /// Used to restore the original AST after consteval of const sub-AST. /// a DeclHolder is either a placeholder, meaning the original decl was selected into sub-AST. This is replaced with /// the original decl when restoring; /// or an OwnedPtr, meaning the original decl was not selected. struct DeclHolder : public std::variant> { bool IsPlaceholder() const noexcept { return std::holds_alternative(*this); } OwnedPtr GetDecl() && { return std::move(std::get>(*this)); } #ifndef NDEBUG std::string ToString() const __attribute__((used)) { if (IsPlaceholder()) { return "{}"; } return std::get>(*this)->identifier.Val(); } #endif }; /// Hold the member decls of a decl, to be restored later struct MemberDeclHolder { std::vector m; /// Test this cache is full, that is, all members are not empty. /// In this case, this cache may not be needed, because all members of the original decls are to be discarded. bool IsFull() const { for (auto& holder : m) { if (holder.IsPlaceholder()) { return false; } } return true; } #ifndef NDEBUG std::string ToString() const __attribute__((used)) { std::stringstream out; for (auto& holder : m) { out << holder.ToString() << ','; } return out.str(); } #endif }; /// Get const sub-pkg of a package. Only decls that are possibly valid in const expressions are kept. /// After that, use \ref RestoreOriginalPkg to restore to the previous state of the package. /// Both the member functions can be called only once. struct ConstSubPkgGetter { explicit ConstSubPkgGetter(AST::Package& p) : pkg{p} {} void GetSubPkg() { for (auto& file : pkg.files) { TakeSubPkg(*file); } KeepConstMembersIfAny(pkg.genericInstantiatedDecls, m[&pkg]); } void RestoreOriginalPkg() { for (auto& file : pkg.files) { RestoreOriginalFile(*file); } if (auto c = m.find(&pkg); c != m.end()) { RestoreOriginalMembers(pkg.genericInstantiatedDecls, c->second); } } std::vector annoOnlyDecls{}; private: AST::Package& pkg; // main decl cache // For Package, genericInstantiatedDecls. files of pkg are always kept. std::unordered_map m; // additional cache. Some Node has more than one member decl containers. // For EnumDecl, constructors // For File, exportedInternalDecls std::unordered_map cache2; std::unordered_map isConstCache; void RestoreOriginalFile(File& file) { if (auto c = m.find(&file); c != m.end()) { RestoreOriginalMembers(file.decls, c->second); } if (auto c = cache2.find(&file); c != cache2.end()) { RestoreOriginalMembers(file.exportedInternalDecls, c->second); } } void RestoreOriginalMembers(std::vector>& decls, MemberDeclHolder& cache) { if (cache.m.empty()) { // no cache, nothing to restore return; } std::vector> res{}; size_t i{0}; for (auto& holder : cache.m) { if (holder.IsPlaceholder()) { res.push_back(std::move(decls[i++])); } else { res.push_back(std::move(holder).GetDecl()); } RestoreOriginalDecl(*res.back()); } CJC_ASSERT(i == decls.size()); std::swap(decls, res); } void RestoreOriginalDecl(Decl& decl) { // VarWithPatternDecl need not restore, because it is selected as one. // all other decls do not have member decls. if (!Is(decl)) { return; } if (auto c = m.find(&decl); c != m.end()) { RestoreOriginalMembers(decl.GetMemberDecls(), c->second); } if (auto e = DynamicCast(&decl)) { if (auto c = cache2.find(&decl); c != cache2.end()) { RestoreOriginalMembers(e->constructors, c->second); } } } /// Keep only const members of input node, cache the original member decls and order in cache. /// \return true if this node is to be kept by its parent decl. bool TakeSubPkg(File& file) { KeepConstMembersIfAny(file.decls, m[&file]); KeepConstMembersIfAny(file.exportedInternalDecls, cache2[&file]); // keep all files anyway, an empty file is not a performance hit return true; } void KeepConstMembersIfAny(std::vector>& members, MemberDeclHolder& loc) { loc = KeepConstMembers(members); } bool TakeSubPkg(Decl& decl) { if (decl.HasAnno(AnnotationKind::CONSTSAFE)) { return true; } if (auto d = DynamicCast(&decl)) { return TakeSubPkg(*d); } if (auto t = DynamicCast(&decl)) { /// type alias are replaced after SEMA return false; } if (decl.IsConst()) { return true; } if (Is(decl)) { return false; } if (auto var = DynamicCast(&decl); var && !var->isVar && !var->TestAttr(AST::Attribute::STATIC) && IsConstType(var->ty)) { if (var->outerDecl) { return true; } if (auto vp = DynamicCast(decl.outerDecl)) { return vp->IsConst(); } } if (auto func = DynamicCast(&decl); func && func->TestAttr(AST::Attribute::FOREIGN) && func->fullPackageName == "std.core") { if (func->identifier.Val().find("CJ_CORE_") == 0) { return true; } if (func->identifier == "memcpy_s" || func->identifier == "strlen") { return true; } } return false; } static bool HasCompileTimeCustomAnno(const Decl& decl) { if (!decl.annotationsArray) { return false; } for (auto& anno : decl.annotationsArray->children) { if (!anno->TestAttr(AST::Attribute::NO_REFLECT_INFO)) { return true; } } return false; } [[nodiscard]] MemberDeclHolder KeepConstMembers(std::vector>& members) { std::vector> selectedDecls{}; MemberDeclHolder cache; for (auto& member : members) { if (TakeSubPkg(*member)) { selectedDecls.push_back(std::move(member)); cache.m.push_back({std::monostate{}}); } else { if (HasCompileTimeCustomAnno(*member)) { annoOnlyDecls.push_back(&*member); } cache.m.push_back({std::move(member)}); } } members = std::move(selectedDecls); return cache; } bool IsConstType(Ptr ty) { if (Is(ty)) { return true; } auto decl = Ty::GetDeclPtrOfTy(ty); // get the generic version decl if (!decl) { if (ty->kind == TypeKind::TYPE_VARRAY) { return false; } // all other builtin types are const type return ty->IsBuiltin(); } return TakeSubPkg(*StaticCast(decl)); } /// \returns True if constness of \ref type depends fully on its members. /// Note that interface/extend are always kept even if they do not have any const members, because a const class /// object may cast to it, and such cast is still const expression, although the result value cannot be used to /// call any function. bool MustSave(InheritableDecl& type) { if (auto extend = DynamicCast(&type)) { if (auto decl = DynamicCast(Ty::GetDeclOfTy(extend->ty))) { return TakeSubPkg(*decl); } auto ty = extend->ty; return ty->kind != TypeKind::TYPE_VARRAY; } return (Is(type) && type.TestAnyAttr(AST::Attribute::OPEN, AST::Attribute::ABSTRACT)) || Is(type); } bool TakeSubPkg(InheritableDecl& type) { if (auto it = isConstCache.find(&type); it != isConstCache.cend()) { return it->second; } bool ret{false}; if (type.ty->IsString()) { ret = true; } // save extend because it may contain a member decl with @!Annotations. if (MustSave(type)) { ret = true; } /// Check if \ref type is const type if (auto em = DynamicCast(&type)) { MemberDeclHolder ctors{}; std::vector> selectedCtors; for (auto& cons : em->constructors) { if (IsConstEnumCtor(*cons)) { ctors.m.push_back({std::monostate{}}); selectedCtors.push_back({std::move(cons)}); ret = true; } else { ctors.m.push_back({std::move(cons)}); } } if (ret) { cache2[em] = std::move(ctors); std::swap(em->constructors, selectedCtors); } } for (auto decl : type.GetMemberDeclPtrs()) { if (decl->IsConst() && decl->TestAttr(AST::Attribute::CONSTRUCTOR)) { ret = true; } } KeepConstMembersIfAny(type.GetMemberDecls(), m[&type]); if (!ret && !type.GetMemberDecls().empty()) { // although the type is itself non-const, it still has const members // static const members in this case can still be present in const eval. // we do not differ between instance and static const members here. ret = true; annoOnlyDecls.push_back(&type); } isConstCache[&type] = ret; return ret; } }; } OwnedPtr ComputeAnnotations(AST::Package& pkg, CompilerInstance& ci) { // only do compute annotations when its debug option is on // set the default value to true when this feature is enabled auto& opts = ci.invocation.globalOptions; bool doCompute{opts.computeAnnotationsDebug}; // no need to recheck when no change if (opts.enIncrementalCompilation && ci.kind == IncreKind::NO_CHANGE) { doCompute = false; } // CJMP does not fully support Annotation if (ci.invocation.globalOptions.commonPartCjo != std::nullopt || ci.invocation.globalOptions.outputMode == GlobalOptions::OutputMode::CHIR) { doCompute = false; } if (!doCompute) { return {}; } auto res = MakeOwned(ci.GetFileNameMap(), ci.invocation.globalOptions.GetJobs()); CHIR::AnalysisWrapper constAnalysisWrapper{res->builder}; CHIR::ToCHIR convertor(ci, pkg, constAnalysisWrapper, res->builder); bool computeSuccess = convertor.ComputeAnnotations({}); if (!computeSuccess) { return {}; } res->pkg = convertor.GetPackage(); auto& annoFactorys = convertor.GetAnnoFactoryFuncs(); for (auto& v : annoFactorys) { g_annoFactoryMap[v.second] = v.first; } for (auto& v : convertor.GetGlobalNominalCache().GetALL()) { g_typeMap[v.second] = StaticCast(v.first); } res->map = CreateAnnoInstFromConstEvalRes(*res); if (ci.invocation.globalOptions.computeAnnotationsDebug) { res->Dump(); } g_typeMap.clear(); g_valueMap.clear(); g_annoFactoryMap.clear(); return res; } } // namespace Cangjie cangjie_compiler-1.0.7/src/CHIR/DebugLocation.cpp000066400000000000000000000050131510705540100215450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the DebugLocation in CHIR. */ #include "cangjie/CHIR/DebugLocation.h" #include #include using namespace Cangjie::CHIR; Position DebugLocation::GetBeginPos() const { return beginPos; } bool DebugLocation::operator==(const DebugLocation& other) const { return beginPos.line == other.beginPos.line && beginPos.column == other.beginPos.column && endPos.line == other.endPos.line && endPos.column == other.endPos.column && fileID == other.fileID; } Position DebugLocation::GetEndPos() const { return endPos; } void DebugLocation::SetBeginPos(const Position& pos) { beginPos = pos; } void DebugLocation::SetEndPos(const Position& pos) { endPos = pos; } void DebugLocation::SetScopeInfo(const std::vector& scope) { scopeInfo = scope; } /** * @brief get the ID of the file. */ unsigned DebugLocation::GetFileID() const { return fileID; } const std::string& DebugLocation::GetAbsPath() const { return *absPath; } std::vector DebugLocation::GetScopeInfo() const { return scopeInfo; } bool DebugLocation::IsInvalidPos() const { return beginPos.line == 0 || beginPos.column == 0 || endPos.line == 0 || endPos.column == 0; } bool DebugLocation::IsInvalidMacroPos() const { return beginPos.line == 0 || beginPos.column == 0; } std::string DebugLocation::GetFileName() const { #ifdef _WIN32 const std::string dirSeparator = "\\/"; #else const std::string dirSeparator = "/"; #endif auto fileName = absPath->substr(absPath->find_last_of(dirSeparator) + 1); return fileName; } std::string DebugLocation::ToString() const { if (*this == INVALID_LOCATION) { return ""; } #ifdef _WIN32 const std::string dirSeparator = "\\/"; #else const std::string dirSeparator = "/"; #endif std::stringstream ss; std::string name = absPath->substr(absPath->find_last_of(dirSeparator) + 1); ss << "loc: \"" << name << "\"-" << beginPos.line << "-" << beginPos.column; if (!scopeInfo.empty()) { ss << ", scope: " << scopeInfo[0]; } for (size_t t = 1; t < scopeInfo.size(); t++) { ss << "-" << scopeInfo[t]; } return ss.str(); } void DebugLocation::Dump() const { std::cout << ToString() << std::endl; } cangjie_compiler-1.0.7/src/CHIR/Expression/000077500000000000000000000000001510705540100204625ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CHIR/Expression/CMakeLists.txt000066400000000000000000000005421510705540100232230ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB EXPRESSION_SRC *.cpp) set(CHIR_BASE_SRC ${CHIR_BASE_SRC} ${EXPRESSION_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/CHIR/Expression/Expression.cpp000066400000000000000000001713301510705540100233320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/StringWrapper.h" #include "cangjie/CHIR/ToStringUtils.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Value.h" #include "cangjie/CHIR/Visitor/Visitor.h" #include "cangjie/Utils/CheckUtils.h" using namespace Cangjie::CHIR; namespace { uint64_t g_lambdaClonedIdx = 0; std::pair GetAllocExprOfRetVal(const BlockGroup& body) { auto blocks = body.GetBlocks(); for (size_t i = 0; i < blocks.size(); ++i) { auto exprs = blocks[i]->GetExpressions(); for (size_t j = 0; j < exprs.size(); ++j) { auto res = exprs[j]->GetResult(); if (res != nullptr && res->IsRetValue()) { return {i, j}; } } } CJC_ABORT(); return {0, 0}; } void GetAllSuperClassDefs(const ClassDef& def, std::unordered_set& allSuperDefs) { for (auto parent : def.GetImplementedInterfaceDefs()) { allSuperDefs.emplace(parent); GetAllSuperClassDefs(*parent, allSuperDefs); } if (auto superClass = def.GetSuperClassDef()) { allSuperDefs.emplace(superClass); GetAllSuperClassDefs(*superClass, allSuperDefs); } for (auto extendDef : def.GetExtends()) { for (auto parent : extendDef->GetImplementedInterfaceDefs()) { allSuperDefs.emplace(parent); GetAllSuperClassDefs(*parent, allSuperDefs); } } } } Expression::Expression( ExprKind kind, const std::vector& operands, const std::vector& blockGroups, Block* parent) : kind(kind), operands(operands), blockGroups(blockGroups), parent(parent) { CJC_NULLPTR_CHECK(parent); for (auto op : operands) { // note: a hack here, remove later if (op == nullptr) { continue; } op->AddUserOnly(this); } for (auto blockGroup : blockGroups) { blockGroup->AddUserOnly(this); } } bool Expression::IsConstantNull() const { if (kind == ExprKind::CONSTANT) { auto constant = StaticCast(this); return constant->IsNullLit(); } return false; } bool Expression::IsConstantInt() const { if (kind == ExprKind::CONSTANT) { auto constant = StaticCast(this); return constant->IsIntLit(); } return false; } bool Expression::IsConstantFloat() const { if (kind == ExprKind::CONSTANT) { auto constant = StaticCast(this); return constant->IsFloatLit(); } return false; } bool Expression::IsConstantBool() const { if (kind == ExprKind::CONSTANT) { auto constant = StaticCast(this); return constant->IsBoolLit(); } return false; } bool Expression::IsConstantString() const { if (kind == ExprKind::CONSTANT) { auto constant = StaticCast(this); return constant->IsStringLit(); } return false; } ExprMajorKind Expression::GetExprMajorKind() const { return ExprKindMgr::Instance()->GetMajorKind(kind); } ExprKind Expression::GetExprKind() const { return kind; } bool Expression::IsConstant() const { return kind == ExprKind::CONSTANT; } bool Expression::IsTerminator() const { return ExprKindMgr::Instance()->GetMajorKind(kind) == ExprMajorKind::TERMINATOR; } bool Expression::IsUnaryExpr() const { return ExprKindMgr::Instance()->GetMajorKind(kind) == ExprMajorKind::UNARY_EXPR; } bool Expression::IsLoad() const { return kind == ExprKind::LOAD; } bool Expression::IsTypeCast() const { return kind == ExprKind::TYPECAST; } bool Expression::IsDynamicDispatch() const { return kind == ExprKind::INVOKE || kind == ExprKind::INVOKESTATIC; } bool Expression::IsFuncCall() const { return kind == ExprKind::APPLY || kind == ExprKind::INVOKE || kind == ExprKind::INVOKESTATIC; } bool Expression::IsField() const { return kind == ExprKind::FIELD; } bool Expression::IsApply() const { return kind == ExprKind::APPLY; } bool Expression::IsApplyWithException() const { return kind == ExprKind::APPLY_WITH_EXCEPTION; } bool Expression::IsInvoke() const { return kind == ExprKind::INVOKE; } bool Expression::IsLambda() const { return kind == ExprKind::LAMBDA; } bool Expression::IsBinaryExpr() const { return ExprKindMgr::Instance()->GetMajorKind(kind) == ExprMajorKind::BINARY_EXPR; } bool Expression::IsIntOpWithException() const { return kind == ExprKind::INT_OP_WITH_EXCEPTION; } bool Expression::IsInvokeStaticBase() const { return kind == ExprKind::INVOKESTATIC || kind == ExprKind::INVOKESTATIC_WITH_EXCEPTION; } bool Expression::IsDebug() const { return kind == ExprKind::DEBUGEXPR; } std::string Expression::GetExprKindName() const { return ExprKindMgr::Instance()->GetKindName(static_cast(kind)); } Block* Expression::GetParentBlock() const { return parent; } const std::vector& Expression::GetBlockGroups() const { return blockGroups; } size_t Expression::GetNumOfOperands() const { return operands.size(); } std::vector Expression::GetOperands() const { return operands; } Value* Expression::GetOperand(size_t idx) const { CJC_ASSERT(idx < operands.size()); return operands[idx]; } LocalVar* Expression::GetResult() const { return result; } Type* Expression::GetResultType() const { return result ? result->GetType() : nullptr; } /** * @brief Get the block group to which the expression belongs. */ BlockGroup* Expression::GetParentBlockGroup() const { if (parent == nullptr) { return nullptr; } return parent->GetParentBlockGroup(); } Func* Expression::GetTopLevelFunc() const { if (auto blockGroup = GetParentBlockGroup(); blockGroup != nullptr) { return blockGroup->GetTopLevelFunc(); } return nullptr; } /** * @brief Remove this expression from its parent basicblock. */ void Expression::RemoveSelfFromBlock() { if (parent != nullptr) { parent->RemoveExprOnly(*this); parent = nullptr; } EraseOperands(); } void Expression::ReplaceWith(Expression& newExpr) { CJC_ASSERT(!newExpr.IsTerminator()); // 1. replace result for (auto user : result->GetUsers()) { user->ReplaceOperand(result, newExpr.GetResult()); } // 2. break double linkage between operands and current expr EraseOperands(); // 3. remove new expr from its parent block if (newExpr.parent != nullptr) { newExpr.parent->RemoveExprOnly(newExpr); } // 4. replace to new expr in parent block CJC_NULLPTR_CHECK(parent); for (size_t i = 0; i < parent->exprs.size(); ++i) { if (parent->exprs[i] == this) { parent->exprs[i] = &newExpr; } } newExpr.parent = parent; parent = nullptr; } /** * @brief Remove this expression from its parent basicblock, and insert it into before the @expr expression. * * @param expr The destination position which is before this reference expression. * */ void Expression::MoveBefore(Expression* expr) { CJC_ASSERT(!this->IsTerminator()); CJC_NULLPTR_CHECK(expr); if (this == expr) { return; } // 1. remove current expr from parent block if (parent != nullptr) { parent->RemoveExprOnly(*this); } // 2. insert current expr before `expr` CJC_NULLPTR_CHECK(expr->parent); auto pos = std::find(expr->parent->exprs.begin(), expr->parent->exprs.end(), expr); CJC_ASSERT(pos != expr->parent->exprs.end()); expr->parent->exprs.insert(pos, this); // 3. change current expr's parent parent = expr->parent; } /* * @brief Remove this expression from its parent basicblock, and insert it into after the @expr expression. * * @param expr The destination position which is after this reference expression. * */ void Expression::MoveAfter(Expression* expr) { CJC_NULLPTR_CHECK(expr); // you shouldn't move an expression after a terminator, that's illegal ir CJC_ASSERT(!expr->IsTerminator()); if (parent != nullptr) { parent->RemoveExprOnly(*this); if (IsTerminator()) { for (auto suc : StaticCast(this)->GetSuccessors()) { suc->RemovePredecessor(*parent); } } } CJC_NULLPTR_CHECK(expr->parent); auto pos = std::find(expr->parent->exprs.begin(), expr->parent->exprs.end(), expr); CJC_ASSERT(pos != expr->parent->exprs.end()); expr->parent->exprs.insert(pos + 1, this); parent = expr->parent; } void Expression::MoveTo(Block& block) { CJC_NULLPTR_CHECK(parent); parent->RemoveExprOnly(*this); if (IsTerminator()) { for (auto suc : StaticCast(this)->GetSuccessors()) { suc->RemovePredecessor(*parent); } } block.AppendExpression(this); } void Expression::SetParent(Block* newParent) { parent = newParent; } void Expression::ReplaceOperand(Value* oldOperand, Value* newOperand) { CJC_NULLPTR_CHECK(oldOperand); CJC_NULLPTR_CHECK(newOperand); if (oldOperand == newOperand) { return; } for (unsigned i = 0; i < operands.size(); i++) { if (operands[i] == oldOperand) { operands[i] = newOperand; operands[i]->AddUserOnly(this); oldOperand->RemoveUserOnly(this); } } } void Expression::ReplaceOperand(size_t idx, Value* newOperand) { CJC_ASSERT(idx < operands.size()); auto oldOperand = operands[idx]; if (oldOperand == newOperand) { return; } operands[idx] = newOperand; newOperand->AddUserOnly(this); bool needRemoveOldOpUser = true; for (auto op : operands) { if (op == oldOperand) { needRemoveOldOpUser = false; } } if (needRemoveOldOpUser) { oldOperand->RemoveUserOnly(this); } } void Expression::EraseOperands() { for (auto op : operands) { op->RemoveUserOnly(this); } operands.clear(); } std::string Expression::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << ExprOperandsToString(operands); ss << ")"; ss << CommentToString(); return ss.str(); } void Expression::Dump() const { std::cout << ToString() << std::endl; } void Expression::AppendOperand(Value& op) { operands.emplace_back(&op); op.AddUserOnly(this); } std::string Expression::CommentToString() const { auto comment = StringWrapper(this->ToStringAnnotationMap()); if (result != nullptr) { comment.Append(result->ToStringAnnotationMap(), ","); } if (comment.Str().empty()) { return ""; } return " // " + comment.Str(); } std::string Expression::AddExtraComment(const std::string& comment) const { CJC_ASSERT(!comment.empty()); if ((result && result->ToStringAnnotationMap().empty()) || this->ToStringAnnotationMap().empty()) { return " // " + comment; } else { return ", " + comment; } } Value* UnaryExpression::GetOperand() const { return GetOperand(0); } Cangjie::OverflowStrategy UnaryExpression::GetOverflowStrategy() const { return overflowStrategy; } std::string UnaryExpression::ToString(size_t indent) const { std::stringstream ss; ss << Expression::ToString(indent); ss << AddExtraComment(OverflowToString(overflowStrategy)); return ss.str(); } BinaryExpression::BinaryExpression(ExprKind kind, Value* lhs, Value* rhs, OverflowStrategy ofs, Block* parent) : Expression(kind, {lhs, rhs}, {}, parent), overflowStrategy(ofs) { } BinaryExpression::BinaryExpression(ExprKind kind, Value* lhs, Value* rhs, Block* parent) : Expression(kind, {lhs, rhs}, {}, parent) { CJC_ASSERT(kind >= ExprKind::BITAND); } Value* BinaryExpression::GetLHSOperand() const { return GetOperand(0); } Value* BinaryExpression::GetRHSOperand() const { return GetOperand(1); } Cangjie::OverflowStrategy BinaryExpression::GetOverflowStrategy() const { return overflowStrategy; } std::string BinaryExpression::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << Expression::ToString(indent); ss << AddExtraComment(OverflowToString(overflowStrategy)); return ss.str(); } std::string Constant::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << GetValue()->ToString(); ss << ")"; ss << CommentToString(); return ss.str(); } LiteralValue* Constant::GetValue() const { CJC_ASSERT(GetOperand(0)->IsLiteral()); return StaticCast(GetOperand(0)); } bool Constant::GetBoolLitVal() const { auto val = GetValue(); CJC_ASSERT(val->IsBoolLiteral()); return static_cast(val)->GetVal(); } bool Constant::IsBoolLit() const { return GetValue()->IsBoolLiteral(); } char32_t Constant::GetRuneLitVal() const { auto val = GetValue(); CJC_ASSERT(val->IsRuneLiteral()); return static_cast(val)->GetVal(); } bool Constant::IsRuneLit() const { return GetValue()->IsRuneLiteral(); } std::string Constant::GetStringLitVal() const { auto val = GetValue(); CJC_ASSERT(val->IsStringLiteral()); return static_cast(val)->GetVal(); } bool Constant::IsStringLit() const { return GetValue()->IsStringLiteral(); } int64_t Constant::GetSignedIntLitVal() const { auto val = GetValue(); CJC_ASSERT(val->IsIntLiteral()); return static_cast(val)->GetSignedVal(); } uint64_t Constant::GetUnsignedIntLitVal() const { auto val = GetValue(); CJC_ASSERT(val->IsIntLiteral()); return static_cast(val)->GetUnsignedVal(); } bool Constant::IsIntLit() const { return GetValue()->IsIntLiteral(); } bool Constant::IsSignedIntLit() const { if (!IsIntLit()) { return false; } return static_cast(GetValue())->IsSigned(); } bool Constant::IsUnSignedIntLit() const { if (!IsIntLit()) { return false; } return !static_cast(GetValue())->IsSigned(); } bool Constant::IsFloatLit() const { return GetValue()->IsFloatLiteral(); } double Constant::GetFloatLitVal() const { auto val = GetValue(); CJC_ASSERT(val->IsFloatLiteral()); return static_cast(val)->GetVal(); } bool Constant::IsNullLit() const { return GetValue()->IsNullLiteral(); } bool Constant::IsUnitLit() const { return GetValue()->IsUnitLiteral(); } // Allocate Type* Allocate::GetType() const { return ty; } std::string Allocate::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "(" << ty->ToString() << ")"; ss << CommentToString(); return ss.str(); } Value* Load::GetLocation() const { return operands[0]; } Value* Store::GetValue() const { return operands[0]; } Value* Store::GetLocation() const { return operands[1]; } // GetElementRef Value* GetElementRef::GetLocation() const { return operands[0]; } const std::vector& GetElementRef::GetPath() const { return path; } std::string GetElementRef::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << GetLocation()->GetIdentifier(); for (auto p : GetPath()) { ss << ", " << p; } ss << ")"; ss << CommentToString(); return ss.str(); } // GetElementByName Value* GetElementByName::GetLocation() const { return operands[0]; } const std::vector& GetElementByName::GetNames() const { return names; } std::string GetElementByName::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << GetLocation()->GetIdentifier(); for (const auto& p : GetNames()) { ss << ", " << p; } ss << ")"; ss << CommentToString(); return ss.str(); } GetElementByName* GetElementByName::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetLocation(), GetNames(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } // StoreElementRef Value* StoreElementRef::GetValue() const { return operands[0]; } Value* StoreElementRef::GetLocation() const { return operands[1]; } const std::vector& StoreElementRef::GetPath() const { return path; } // StoreElementByName Value* StoreElementByName::GetValue() const { return operands[0]; } Value* StoreElementByName::GetLocation() const { return operands[1]; } const std::vector& StoreElementByName::GetNames() const { return names; } std::string StoreElementByName::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << GetValue()->GetIdentifier() << ", "; ss << GetLocation()->GetIdentifier(); for (const auto& p : GetNames()) { ss << ", " << p; } ss << ")"; ss << CommentToString(); return ss.str(); } StoreElementByName* StoreElementByName::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetValue(), GetLocation(), GetNames(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } FuncCall::FuncCall(ExprKind kind, const FuncCallContext& funcCallCtx, Block* parent) : Expression(kind, {}, {}, parent), instantiatedTypeArgs(funcCallCtx.instTypeArgs), thisType(funcCallCtx.thisType) { } Type* FuncCall::GetThisType() const { return thisType; } void FuncCall::SetThisType(Type* type) { thisType = type; } const std::vector& FuncCall::GetInstantiatedTypeArgs() const { return instantiatedTypeArgs; } // Apply Apply::Apply(Value* callee, const FuncCallContext& callContext, Block* parent) : FuncCall(ExprKind::APPLY, callContext, parent) { CJC_NULLPTR_CHECK(parent); AppendOperand(*callee); for (auto op : callContext.args) { AppendOperand(*op); } } Value* Apply::GetCallee() const { return operands[0]; } void Apply::SetSuperCall() { this->isSuperCall = true; } bool Apply::IsSuperCall() const { return isSuperCall; } Type* Apply::GetInstParentCustomTyOfCallee(CHIRBuilder& builder) const { return GetInstParentCustomTypeForApplyCallee(*this, builder); } std::vector Apply::GetArgs() const { std::vector args; if (operands.size() > 1) { args.assign(operands.begin() + 1, operands.end()); } return args; } std::string Apply::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << ThisTypeToString(thisType).AddDelimiterOrNot(", ").Str(); ss << GetCallee()->GetIdentifier(); ss << InstTypeArgsToString(instantiatedTypeArgs); ss << StringWrapper(", ").AppendOrClear(ExprOperandsToString(GetArgs())).Str(); ss << ")"; ss << CommentToString(); if (IsSuperCall()) { ss << AddExtraComment("isSuperCall"); } return ss.str(); } DynamicDispatch::DynamicDispatch(ExprKind kind, const InvokeCallContext& callContext, Block* parent) : FuncCall(kind, callContext.funcCallCtx, parent), virMethodCtx(callContext.virMethodCtx) { CJC_ASSERT(!virMethodCtx.srcCodeIdentifier.empty()); CJC_NULLPTR_CHECK(virMethodCtx.originalFuncType); CJC_NULLPTR_CHECK(callContext.funcCallCtx.thisType); } const std::string& DynamicDispatch::GetMethodName() const { return virMethodCtx.srcCodeIdentifier; } FuncType* DynamicDispatch::GetMethodType() const { return virMethodCtx.originalFuncType; } std::vector DynamicDispatch::GetVirtualMethodInfo(CHIRBuilder& builder) const { auto thisTypeDeref = thisType->StripAllRefs(); if (thisTypeDeref->IsThis()) { thisTypeDeref = GetTopLevelFunc()->GetParentCustomTypeDef()->GetType(); } std::vector instParamTypes; for (auto arg : GetArgs()) { instParamTypes.emplace_back(arg->GetType()); } auto instFuncType = builder.GetType(instParamTypes, builder.GetUnitTy()); FuncCallType funcCallType{virMethodCtx.srcCodeIdentifier, instFuncType, instantiatedTypeArgs}; auto res = GetFuncIndexInVTable(*thisTypeDeref, funcCallType, IsInvokeStaticBase(), builder); CJC_ASSERT(!res.empty()); return res; } const std::vector& DynamicDispatch::GetGenericTypeParams() const { return virMethodCtx.genericTypeParams; } size_t DynamicDispatch::GetVirtualMethodOffset(CHIRBuilder* builder) const { auto offset = Get(); if (offset.has_value()) { return offset.value(); } else { CJC_NULLPTR_CHECK(builder); return GetVirtualMethodInfo(*builder)[0].offset; } } ClassType* DynamicDispatch::GetInstSrcParentCustomTypeOfMethod(CHIRBuilder& builder) const { for (auto& r : GetVirtualMethodInfo(builder)) { if (r.offset == GetVirtualMethodOffset()) { CJC_NULLPTR_CHECK(r.instSrcParentType); return r.instSrcParentType; } } CJC_ABORT(); return nullptr; } AttributeInfo DynamicDispatch::GetVirtualMethodAttr(CHIRBuilder& builder) const { for (auto& r : GetVirtualMethodInfo(builder)) { if (r.offset == GetVirtualMethodOffset()) { CJC_NULLPTR_CHECK(r.instSrcParentType); return r.attr; } } CJC_ABORT(); return AttributeInfo{}; } // Invoke Invoke::Invoke(const InvokeCallContext& callContext, Block* parent) : DynamicDispatch(ExprKind::INVOKE, callContext, parent) { CJC_NULLPTR_CHECK(parent); AppendOperand(*callContext.caller); for (auto op : callContext.funcCallCtx.args) { AppendOperand(*op); } } Value* Invoke::GetObject() const { return operands[0]; } void Invoke::UpdateThisType() { auto objType = GetObject()->GetType(); auto objDerefType = DynamicCast(objType->StripAllRefs()); auto thisDerefType = DynamicCast(thisType->StripAllRefs()); // for now, we only care about class type, maybe we can handle other types later if (objDerefType == thisDerefType || objDerefType == nullptr || thisDerefType == nullptr) { return; } // `ThisType` must be sub type or equal to object's type, in fact, they should be same, // but original object may be casted to parent type for func param type matched. // after function inlining, object may be changed to sub type of `ThisType`, so we need to update `ThisType` auto parentDef = objDerefType->GetClassDef(); std::unordered_set allSuperDefs; GetAllSuperClassDefs(*thisDerefType->GetClassDef(), allSuperDefs); auto it = allSuperDefs.find(parentDef); if (it == allSuperDefs.end()) { SetThisType(objType); } } void Invoke::ReplaceOperand(Value* oldOperand, Value* newOperand) { auto needUpdateThisType = false; if (GetObject() == oldOperand) { needUpdateThisType = true; } Expression::ReplaceOperand(oldOperand, newOperand); if (needUpdateThisType) { UpdateThisType(); } } void Invoke::ReplaceOperand(size_t idx, Value* newOperand) { Expression::ReplaceOperand(idx, newOperand); if (idx == 0) { UpdateThisType(); } } std::string Invoke::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << ThisTypeToString(thisType).AddDelimiterOrNot(", ").Str(); ss << GetMethodName(); ss << InstTypeArgsToString(instantiatedTypeArgs) << ": "; ss << GetMethodType()->ToString() << ", "; ss << ExprOperandsToString(GetArgs()); ss << ")"; ss << CommentToString(); return ss.str(); } std::vector Invoke::GetArgs() const { return operands; } InvokeStatic::InvokeStatic(const InvokeCallContext& callContext, Block* parent) : DynamicDispatch(ExprKind::INVOKESTATIC, callContext, parent) { CJC_NULLPTR_CHECK(parent); AppendOperand(*callContext.caller); for (auto op : callContext.funcCallCtx.args) { AppendOperand(*op); } } Value* InvokeStatic::GetRTTIValue() const { return operands[0]; } std::string InvokeStatic::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << ThisTypeToString(thisType).AddDelimiterOrNot(", ").Str(); ss << GetMethodName(); ss << InstTypeArgsToString(instantiatedTypeArgs) << ": "; ss << GetMethodType()->ToString() << ", "; ss << GetRTTIValue()->GetIdentifier(); ss << StringWrapper(", ").AppendOrClear(ExprOperandsToString(GetArgs())).Str(); ss << ")"; ss << CommentToString(); return ss.str(); } std::vector InvokeStatic::GetArgs() const { return {operands.begin() + 1, operands.end()}; } InvokeStatic* InvokeStatic::Clone(CHIRBuilder& builder, Block& parent) const { auto invokeInfo = InvokeCallContext { .caller = GetRTTIValue(), .funcCallCtx = FuncCallContext { .args = GetArgs(), .instTypeArgs = instantiatedTypeArgs, .thisType = thisType }, .virMethodCtx = virMethodCtx }; auto newNode = builder.CreateExpression(result->GetType(), invokeInfo, &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } // TypeCast TypeCast::TypeCast(Value* operand, Block* parent) : Expression(ExprKind::TYPECAST, {operand}, {}, parent) { } TypeCast::TypeCast(Value* operand, Cangjie::OverflowStrategy overflow, Block* parent) : Expression(ExprKind::TYPECAST, {operand}, {}, parent), overflowStrategy(overflow) { } Value* TypeCast::GetSourceValue() const { return operands[0]; } Type* TypeCast::GetSourceTy() const { return operands[0]->GetType(); } Type* TypeCast::GetTargetTy() const { return result->GetType(); } Cangjie::OverflowStrategy TypeCast::GetOverflowStrategy() const { return overflowStrategy; } std::string TypeCast::ToString(size_t indent) const { std::stringstream ss; ss << Expression::ToString(indent); ss << AddExtraComment(OverflowToString(overflowStrategy)); return ss.str(); } // InstanceOf InstanceOf::InstanceOf(Value* operand, Type* ty, Block* parent) : Expression(ExprKind::INSTANCEOF, {operand}, {}, parent), ty(ty) { } Value* InstanceOf::GetObject() const { return operands[0]; } Type* InstanceOf::GetType() const { return ty; } std::string InstanceOf::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "(" << GetObject()->GetIdentifier() << ", " << GetType()->ToString() << ")"; ss << CommentToString(); return ss.str(); } // Box Box::Box(Value* operand, Block* parent) : Expression(ExprKind::BOX, {operand}, {}, parent) { } Value* Box::GetSourceValue() const { return operands[0]; } Type* Box::GetSourceTy() const { return GetSourceValue()->GetType(); } Type* Box::GetTargetTy() const { return result->GetType(); } Box* Box::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetSourceValue(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } // UnBox UnBox::UnBox(Value* operand, Block* parent) : Expression(ExprKind::UNBOX, {operand}, {}, parent) { } Value* UnBox::GetSourceValue() const { return operands[0]; } Type* UnBox::GetSourceTy() const { return GetSourceValue()->GetType(); } Type* UnBox::GetTargetTy() const { return result->GetType(); } UnBox* UnBox::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetSourceValue(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } // TransformToGeneric TransformToGeneric::TransformToGeneric(Value* operand, Block* parent) : Expression(ExprKind::TRANSFORM_TO_GENERIC, {operand}, {}, parent) { } Value* TransformToGeneric::GetSourceValue() const { return operands[0]; } Type* TransformToGeneric::GetSourceTy() const { return GetSourceValue()->GetType(); } Type* TransformToGeneric::GetTargetTy() const { return result->GetType(); } TransformToGeneric* TransformToGeneric::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetSourceValue(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } // TransformToConcrete TransformToConcrete::TransformToConcrete(Value* operand, Block* parent) : Expression(ExprKind::TRANSFORM_TO_CONCRETE, {operand}, {}, parent) { } Value* TransformToConcrete::GetSourceValue() const { return operands[0]; } Type* TransformToConcrete::GetSourceTy() const { return GetSourceValue()->GetType(); } Type* TransformToConcrete::GetTargetTy() const { return result->GetType(); } TransformToConcrete* TransformToConcrete::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetSourceValue(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } // UnBoxToRef UnBoxToRef::UnBoxToRef(Value* operand, Block* parent) : Expression(ExprKind::UNBOX_TO_REF, {operand}, {}, parent) { } Value* UnBoxToRef::GetSourceValue() const { return operands[0]; } Type* UnBoxToRef::GetSourceTy() const { return GetSourceValue()->GetType(); } Type* UnBoxToRef::GetTargetTy() const { return result->GetType(); } UnBoxToRef* UnBoxToRef::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetSourceValue(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } // Tuple Tuple::Tuple(const std::vector& values, Block* parent) : Expression(ExprKind::TUPLE, values, {}, parent) { } std::vector Tuple::GetElementValues() const { return operands; } std::vector Tuple::GetElementTypes() const { std::vector types; for (auto op : operands) { types.emplace_back(op->GetType()); } return types; } // Field Field::Field(Value* val, const std::vector& path, Block* parent) : Expression(ExprKind::FIELD, {val}, {}, parent), path(path) { } Value* Field::GetBase() const { return operands[0]; } std::vector Field::GetPath() const { return path; } std::string Field::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << GetBase()->GetIdentifier(); for (auto p : GetPath()) { ss << ", " << p; } ss << ")"; ss << CommentToString(); return ss.str(); } // Field FieldByName::FieldByName(Value* val, const std::vector& names, Block* parent) : Expression(ExprKind::FIELD_BY_NAME, {val}, {}, parent), names(names) { } Value* FieldByName::GetBase() const { return operands[0]; } const std::vector& FieldByName::GetNames() const { return names; } std::string FieldByName::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << GetBase()->GetIdentifier(); for (const auto& p : GetNames()) { ss << ", " << p; } ss << ")"; ss << CommentToString(); return ss.str(); } FieldByName* FieldByName::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetBase(), GetNames(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } // RawArrayAllocate RawArrayAllocate::RawArrayAllocate(Type* eleTy, Value* size, Block* parent) : Expression(ExprKind::RAW_ARRAY_ALLOCATE, {size}, {}, parent), elementType(eleTy) { } Value* RawArrayAllocate::GetSize() const { CJC_ASSERT(!operands.empty()); return operands[0]; } Type* RawArrayAllocate::GetElementType() const { return elementType; } std::string RawArrayAllocate::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << elementType->ToString() << ", "; ss << GetSize()->GetIdentifier(); ss << ")"; ss << CommentToString(); return ss.str(); } // RawArrayLiteralInit RawArrayLiteralInit::RawArrayLiteralInit(const Ptr raw, std::vector elements, Block* parent) : Expression(ExprKind::RAW_ARRAY_LITERAL_INIT, {}, {}, parent) { CJC_NULLPTR_CHECK(parent); AppendOperand(*raw); for (auto op : elements) { AppendOperand(*op); } } Value* RawArrayLiteralInit::GetRawArray() const { return operands[0]; } size_t RawArrayLiteralInit::GetSize() const { CJC_ASSERT(operands.size() > 0); return operands.size() - 1; } std::vector RawArrayLiteralInit::GetElements() const { CJC_ASSERT(operands.size() > 0); return {operands.begin() + 1, operands.end()}; } // Intrinsic Intrinsic::Intrinsic(const IntrisicCallContext& callContext, Block* parent) : Expression(ExprKind::INTRINSIC, callContext.args, {}, parent), intrinsicKind(callContext.kind), instantiatedTypeArgs(callContext.instTypeArgs) { } IntrinsicKind Intrinsic::GetIntrinsicKind() const { return intrinsicKind; } const std::vector& Intrinsic::GetInstantiatedTypeArgs() const { return instantiatedTypeArgs; } const std::vector& Intrinsic::GetArgs() const { return operands; } std::string Intrinsic::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName() << "/" << INTRINSIC_KIND_TO_STRING_MAP.at(intrinsicKind); ss << InstTypeArgsToString(instantiatedTypeArgs); ss << "("; ss << ExprOperandsToString(operands); ss << ")"; ss << CommentToString(); return ss.str(); } // If If::If(Value* cond, BlockGroup* thenBody, BlockGroup* elseBody, Block* parent) : Expression(ExprKind::IF, {cond}, {thenBody, elseBody}, parent) { } Value* If::GetCondition() const { return operands[0]; } BlockGroup* If::GetTrueBranch() const { return blockGroups[0]; } BlockGroup* If::GetFalseBranch() const { return blockGroups[1]; } std::string If::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "(" << operands[0]->GetIdentifier() << ", " << std::endl; ss << GetBlockGroupStr(*blockGroups[0], indent + 1); ss << "," << std::endl; ss << GetBlockGroupStr(*blockGroups[1], indent + 1); ss << ")"; ss << CommentToString(); return ss.str(); } // Loop Loop::Loop(BlockGroup* loopBody, Block* parent) : Expression(ExprKind::LOOP, {}, {loopBody}, parent) { } BlockGroup* Loop::GetLoopBody() const { return blockGroups[0]; } std::string Loop::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName() << "(" << std::endl; ss << GetBlockGroupStr(*blockGroups[0], indent + 1); ss << ")"; ss << CommentToString(); return ss.str(); } // ForIn ForIn::ForIn(ExprKind kind, Value* inductionVar, Value* loopCondVar, Block* parent) : Expression(kind, {inductionVar, loopCondVar}, parent) { } Value* ForIn::GetInductionVar() const { return operands[0]; } Value* ForIn::GetLoopCondVar() const { return operands[1]; } BlockGroup* ForIn::GetBody() const { return blockGroups[0]; } BlockGroup* ForIn::GetLatch() const { return blockGroups[1]; } BlockGroup* ForIn::GetCond() const { return blockGroups[2U]; } void ForIn::InitBlockGroups(BlockGroup& body, BlockGroup& latch, BlockGroup& cond) { CJC_ASSERT(blockGroups.empty()); body.SetOwnerExpression(*this); blockGroups.emplace_back(&body); latch.SetOwnerExpression(*this); blockGroups.emplace_back(&latch); cond.SetOwnerExpression(*this); blockGroups.emplace_back(&cond); } std::string ForIn::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << GetInductionVar()->GetIdentifier(); ss << ", " << GetLoopCondVar()->GetIdentifier() << "," << std::endl; ss << GetBlockGroupStr(*GetBody(), indent + 1); ss << "," << std::endl; ss << GetBlockGroupStr(*GetLatch(), indent + 1); ss << "," << std::endl; ss << GetBlockGroupStr(*GetCond(), indent + 1); ss << ")"; ss << CommentToString(); return ss.str(); } // Lambda Lambda::Lambda(FuncType* ty, Block* parent, bool isLocalFunc, const std::string& identifier, const std::string& srcCodeIdentifier, const std::vector& genericTypeParams) : Expression(ExprKind::LAMBDA, /* operands = */ {}, /* block group = */ {}, parent), identifier(identifier), srcCodeIdentifier(srcCodeIdentifier), funcTy(ty), isLocalFunc(isLocalFunc), genericTypeParams(genericTypeParams) { CJC_NULLPTR_CHECK(ty); CJC_NULLPTR_CHECK(parent); } bool Lambda::IsCompileTimeValue() const { return isCompileTimeValue; } void Lambda::SetCompileTimeValue() { isCompileTimeValue = true; } Type* Lambda::GetReturnType() const { return funcTy->GetReturnType(); } std::string Lambda::ToString([[maybe_unused]] size_t indent) const { return GetLambdaStr(*this, indent); } std::vector Lambda::GetCapturedVariables() const { std::vector envs; auto preVisit = [&envs, this](Expression& e) { for (auto op : e.GetOperands()) { bool isEnv = false; if (op->IsLocalVar()) { auto localVar = static_cast(op); if (localVar != GetResult() && localVar->GetOwnerBlockGroup() != GetBody()) { isEnv = true; } } else if (op->IsParameter()) { auto arg = static_cast(op); if (arg->GetOwnerLambda() != this) { isEnv = true; } } if (isEnv && std::find(envs.begin(), envs.end(), op) == envs.end()) { envs.emplace_back(op); } } return VisitResult::CONTINUE; }; Visitor::Visit(*GetBody(), preVisit, [](Expression&) { return VisitResult::CONTINUE; }); return envs; } // Debug Debug::Debug(Value* local, std::string srcCodeIdentifier, Block* parent) : Expression(ExprKind::DEBUGEXPR, {local}, parent), srcCodeIdentifier(srcCodeIdentifier) { CJC_NULLPTR_CHECK(parent); } std::string Debug::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << GetValue()->GetIdentifier() << ", "; ss << GetSrcCodeIdentifier(); ss << ")"; ss << CommentToString(); return ss.str(); } Spawn::Spawn(Value* val, Block* parent) : Expression(ExprKind::SPAWN, {val}, {}, parent) { } Spawn::Spawn(Value* val, Value* arg, Block* parent) : Expression(ExprKind::SPAWN, {val, arg}, {}, parent) { } Value* Spawn::GetFuture() const { CJC_ASSERT(!IsExecuteClosure()); return operands[0]; } Value* Spawn::GetSpawnArg() const { if (operands.size() > 1) { return operands[1]; } return nullptr; } Value* Spawn::GetClosure() const { CJC_ASSERT(IsExecuteClosure()); return operands[0]; } FuncBase* Spawn::GetExecuteClosure() const { return executeClosure; } bool Spawn::IsExecuteClosure() const { return executeClosure != nullptr; } void Spawn::SetExecuteClosure(FuncBase& func) { executeClosure = &func; } std::string Spawn::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << Expression::ToString(indent); if (IsExecuteClosure()) { ss << AddExtraComment("executeClosure: " + GetExecuteClosure()->GetIdentifier()); } return ss.str(); } UnaryExpression* UnaryExpression::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression( result->GetType(), GetExprKind(), GetOperand(), GetOverflowStrategy(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } BinaryExpression* BinaryExpression::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression( result->GetType(), GetExprKind(), GetLHSOperand(), GetRHSOperand(), GetOverflowStrategy(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } Constant* Constant::Clone(CHIRBuilder& builder, Block& parent) const { Constant* newNode = nullptr; auto litVal = StaticCast(GetValue()); if (litVal->IsNullLiteral()) { litVal = builder.CreateLiteralValue(litVal->GetType()); } newNode = builder.CreateExpression(result->GetType(), litVal, &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } Allocate* Allocate::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetType(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } Load* Load::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetLocation(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } Store* Store::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetValue(), GetLocation(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } GetElementRef* GetElementRef::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetLocation(), GetPath(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } std::string StoreElementRef::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << GetValue()->GetIdentifier() << ", "; ss << GetLocation()->GetIdentifier(); for (auto p : GetPath()) { ss << ", " << p; } ss << ")"; ss << CommentToString(); return ss.str(); } StoreElementRef* StoreElementRef::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetValue(), GetLocation(), GetPath(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } Apply* Apply::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetCallee(), FuncCallContext{ .args = GetArgs(), .instTypeArgs = GetInstantiatedTypeArgs(), .thisType = GetThisType()}, &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } Invoke* Invoke::Clone(CHIRBuilder& builder, Block& parent) const { auto args = GetArgs(); args.erase(args.begin()); auto invokeInfo = InvokeCallContext { .caller = GetObject(), .funcCallCtx = FuncCallContext { .args = args, .instTypeArgs = instantiatedTypeArgs, .thisType = thisType }, .virMethodCtx = virMethodCtx }; auto newNode = builder.CreateExpression(result->GetType(), invokeInfo, &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } TypeCast* TypeCast::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetSourceValue(), GetOverflowStrategy(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); // Need to keep the NeedCheckCast for TypeCast Clone newNode->Set(this->Get()); newNode->GetResult()->Set(result->Get()); return newNode; } InstanceOf* InstanceOf::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetObject(), GetType(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } RawArrayInitByValue::RawArrayInitByValue(Value* raw, Value* size, Value* initVal, Block* parent) : Expression(ExprKind::RAW_ARRAY_INIT_BY_VALUE, {raw, size, initVal}, {}, parent) { } Value* RawArrayInitByValue::GetRawArray() const { return operands[0]; } Value* RawArrayInitByValue::GetSize() const { return operands[1]; } Value* RawArrayInitByValue::GetInitValue() const { constexpr int initIdx = 2; return operands[initIdx]; } VArray::VArray(std::vector elements, Block* parent) : Expression(ExprKind::VARRAY, elements, {}, parent) { } int64_t VArray::GetSize() const { return static_cast(operands.size()); } VArrayBuilder::VArrayBuilder(Value* size, Value* item, Value* initFunc, Block* parent) : Expression(ExprKind::VARRAY_BUILDER, {size, item, initFunc}, {}, parent) { } Value* VArrayBuilder::GetSize() const { return GetOperand(static_cast(ElementIdx::SIZE_IDX)); } Value* VArrayBuilder::GetItem() const { return GetOperand(static_cast(ElementIdx::ITEM_IDX)); } Value* VArrayBuilder::GetInitFunc() const { return GetOperand(static_cast(ElementIdx::INIT_FUNC_IDX)); } GetException::GetException(Block* parent) : Expression(ExprKind::GET_EXCEPTION, {}, {}, parent) { } ForIn::BGExecutionOrder ForInRange::GetExecutionOrder() const { return {GetCond(), GetBody(), GetLatch()}; } ForInIter::ForInIter(Value* inductionVar, Value* loopCondVar, Block* parent) : ForIn(ExprKind::FORIN_ITER, inductionVar, loopCondVar, parent) { } ForIn::BGExecutionOrder ForInIter::GetExecutionOrder() const { return {GetLatch(), GetCond(), GetBody()}; } ForInClosedRange::ForInClosedRange(Value* inductionVar, Value* loopCondVar, Block* parent) : ForIn(ExprKind::FORIN_CLOSED_RANGE, inductionVar, loopCondVar, parent) { } ForIn::BGExecutionOrder ForInClosedRange::GetExecutionOrder() const { return {GetBody(), GetCond(), GetLatch()}; } RaiseException* RaiseException::Clone(CHIRBuilder& builder, Block& parent) const { CJC_ASSERT(result == nullptr); auto excp = GetExceptionBlock(); auto newNode = (excp ? builder.CreateTerminator(GetExceptionValue(), excp, &parent) : builder.CreateTerminator(GetExceptionValue(), &parent)); parent.AppendExpression(newNode); return newNode; } Tuple* Tuple::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetOperands(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } Field* Field::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetBase(), GetPath(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } RawArrayAllocate* RawArrayAllocate::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetElementType(), GetSize(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } RawArrayLiteralInit* RawArrayLiteralInit::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetRawArray(), GetElements(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } RawArrayInitByValue* RawArrayInitByValue::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression( result->GetType(), GetRawArray(), GetSize(), GetInitValue(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } VArray* VArray::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetOperands(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } VArrayBuilder* VArrayBuilder::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetSize(), GetItem(), GetInitFunc(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } GetException* GetException::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } Intrinsic* Intrinsic::Clone(CHIRBuilder& builder, Block& parent) const { auto callContext = IntrisicCallContext { .kind = GetIntrinsicKind(), .args = GetArgs(), .instTypeArgs = GetInstantiatedTypeArgs() }; auto newNode = builder.CreateExpression(result->GetType(), callContext, &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } If* If::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetCondition(), GetTrueBranch(), GetFalseBranch(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } Loop* Loop::Clone(CHIRBuilder& builder, Block& parent) const { BlockGroup* newBody = nullptr; if (parent.GetParentBlockGroup()->GetOwnerFunc()) { newBody = GetLoopBody()->Clone(builder, *parent.GetParentBlockGroup()->GetOwnerFunc()); } else { auto parentLambda = StaticCast(parent.GetParentBlockGroup()->GetOwnerExpression()); newBody = GetLoopBody()->Clone(builder, *parentLambda); } auto newNode = builder.CreateExpression(result->GetType(), newBody, &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } template T* ForIn::CloneBase(CHIRBuilder& builder, Block& parent) const { static_assert(std::is_base_of_v); BlockGroup* newCond = nullptr; BlockGroup* newBody = nullptr; BlockGroup* newLatch = nullptr; if (parent.GetParentBlockGroup()->GetOwnerFunc()) { newCond = GetCond()->Clone(builder, *parent.GetParentBlockGroup()->GetOwnerFunc()); newBody = GetBody()->Clone(builder, *parent.GetParentBlockGroup()->GetOwnerFunc()); newLatch = GetLatch()->Clone(builder, *parent.GetParentBlockGroup()->GetOwnerFunc()); } else { auto parentLambda = StaticCast(parent.GetParentBlockGroup()->GetOwnerExpression()); newCond = GetCond()->Clone(builder, *parentLambda); newBody = GetBody()->Clone(builder, *parentLambda); newLatch = GetLatch()->Clone(builder, *parentLambda); } T* newNode = builder.CreateExpression(result->GetType(), GetInductionVar(), GetLoopCondVar(), &parent); newNode->InitBlockGroups(*newBody, *newLatch, *newCond); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } ForInRange* ForInRange::Clone(CHIRBuilder &builder, Block &parent) const { return CloneBase(builder, parent); } ForInIter* ForInIter::Clone(CHIRBuilder &builder, Block &parent) const { return CloneBase(builder, parent); } ForInClosedRange* ForInClosedRange::Clone(CHIRBuilder& builder, Block& parent) const { return CloneBase(builder, parent); } Lambda* Lambda::Clone(CHIRBuilder& builder, Block& parent) const { auto newIdentifier = identifier + "." + std::to_string(g_lambdaClonedIdx++); auto newNode = builder.CreateExpression(result->GetType(), funcTy, &parent, IsLocalFunc(), newIdentifier, GetSrcCodeIdentifier(), GetGenericTypeParams()); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); auto newBody = GetBody()->Clone(builder, *newNode); for (auto p : GetParams()) { builder.CreateParameter(p->GetType(), INVALID_LOCATION, *newNode); } auto [blockIdx, exprIdx] = GetAllocExprOfRetVal(*GetBody()); auto newRet = newBody->GetBlockByIdx(blockIdx)->GetExpressionByIdx(exprIdx)->GetResult(); CJC_NULLPTR_CHECK(newRet); newNode->SetReturnValue(*newRet); return newNode; } Debug* Debug::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(result->GetType(), GetValue(), GetSrcCodeIdentifier(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } Spawn* Spawn::Clone(CHIRBuilder& builder, Block& parent) const { Spawn* newNode = nullptr; auto arg = GetSpawnArg(); if (arg != nullptr) { newNode = builder.CreateExpression(result->GetType(), GetFuture(), arg, &parent); } else { newNode = builder.CreateExpression(result->GetType(), GetFuture(), &parent); } if (executeClosure) { newNode->SetExecuteClosure(*executeClosure); } parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } GetInstantiateValue* GetInstantiateValue::Clone(CHIRBuilder& builder, Block& parent) const { GetInstantiateValue* newNode = nullptr; auto val = GetGenericResult(); if (val->IsLocalVar()) { /* `val` must be lambda, if we step in here, it means lambda's parent func is inlined e.g. func foo() { func goo() {} var a = goo // GetInstantiateValue(goo, T1, Int32) } main() { foo() // inlined } ===========> after inline, `main` body should be: main() { func goo() {} var a = goo // GetInstantiateValue(goo, Int32) } instantiated types in GetInstantiateValue are changed */ auto oldOutDefTypes = GetOutDefDeclaredTypes(*result); auto oldAllTypes = GetInstantiateTypes(); auto newAllTypes = GetOutDefDeclaredTypes(parent); for (size_t i = oldOutDefTypes.size(); i < oldAllTypes.size(); ++i) { newAllTypes.emplace_back(oldAllTypes[i]); } newNode = builder.CreateExpression(result->GetType(), val, newAllTypes, &parent); } else { newNode = builder.CreateExpression(result->GetType(), val, instantiateTys, &parent); } parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } std::vector GetInstantiateValue::GetInstantiateTypes() const { return instantiateTys; } GetInstantiateValue::GetInstantiateValue(Value* val, std::vector insTypes, Block* parent) : Expression(ExprKind::GET_INSTANTIATE_VALUE, {val}, {}, parent), instantiateTys(insTypes) { } Value* GetInstantiateValue::GetGenericResult() const { return operands[0]; } std::string GetInstantiateValue::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << GetGenericResult()->GetIdentifier(); for (auto ty : GetInstantiateTypes()) { ss << ", " << ty->ToString(); } ss << ")"; ss << CommentToString(); return ss.str(); } void Lambda::InitBody(BlockGroup& newBody) { CJC_ASSERT(body.body == nullptr); CJC_ASSERT(blockGroups.empty()); // newBody -> lambda newBody.SetOwnerExpression(*this); // lambda -> newBody body.body = &newBody; blockGroups.emplace_back(&newBody); } FuncType* Lambda::GetFuncType() const { return funcTy; } BlockGroup* Lambda::GetBody() const { return body.GetBody(); } void Lambda::RemoveBody() { blockGroups.clear(); body.RemoveBody(); } Block* Lambda::GetEntryBlock() const { return body.GetBody()->GetEntryBlock(); } std::string Lambda::GetIdentifier() const { return identifier; } /** * Get identifier without prefix '@' * '@' stands for global decl, so lambda's identifier doesn't have it */ std::string Lambda::GetIdentifierWithoutPrefix() const { return identifier; } std::string Lambda::GetSrcCodeIdentifier() const { return srcCodeIdentifier; } void Lambda::AddParam(Parameter& arg) { body.AddParam(arg); arg.SetOwnerLambda(this); } size_t Lambda::GetNumOfParams() const { return body.GetParams().size(); } Parameter* Lambda::GetParam(size_t index) const { return body.GetParam(index); } const std::vector& Lambda::GetParams() const { return body.GetParams(); } const std::vector& Lambda::GetGenericTypeParams() const { return genericTypeParams; } void Lambda::SetReturnValue(LocalVar& ret) { ret.SetRetValue(); body.SetReturnValue(ret); } LocalVar* Lambda::GetReturnValue() const { return body.GetReturnValue(); } bool Lambda::IsLocalFunc() const { return isLocalFunc; } void Lambda::SetParamDftValHostFunc(Lambda& hostFunc) { paramDftValHostFunc = &hostFunc; } Lambda* Lambda::GetParamDftValHostFunc() const { return paramDftValHostFunc; } void Lambda::RemoveSelfFromBlock() { if (auto blockGroup = body.GetBody()) { blockGroup->ClearBlockGroup(); body.RemoveBody(); } Expression::RemoveSelfFromBlock(); } std::string Debug::GetSrcCodeIdentifier() const { return srcCodeIdentifier; } Value* Debug::GetValue() const { return operands[0]; } std::string GetRTTI::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << Expression::ToString(); return ss.str(); } GetRTTI::GetRTTI(Value* val, Block* parent) : Expression{ExprKind::GET_RTTI, {val}, {}, parent} { CJC_ASSERT(Is(val->GetType()) && (val->GetType()->StripAllRefs()->IsClass() || val->GetType()->StripAllRefs()->IsClass()) && "GetRTTI must be used on class ref type"); } Value* GetRTTI::GetOperand() const { return operands[0]; } GetRTTI* GetRTTI::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(GetDebugLocation(), GetResultType(), GetOperand(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } std::string GetRTTIStatic::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName() << "(" << GetRTTIType()->ToString() << ")"; ss << CommentToString(); return ss.str(); } GetRTTIStatic* GetRTTIStatic::Clone(CHIRBuilder& builder, Block& parent) const { auto newNode = builder.CreateExpression(GetDebugLocation(), GetResultType(), GetRTTIType(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } GetRTTIStatic::GetRTTIStatic(Type* type, Block* parent) : Expression{ExprKind::GET_RTTI_STATIC, {}, {}, parent}, ty{type} { } Type* GetRTTIStatic::GetRTTIType() const { return ty; } cangjie_compiler-1.0.7/src/CHIR/Expression/ExpressionWrapper.cpp000066400000000000000000000272301510705540100246720ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Expression/ExpressionWrapper.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/Utils/CastingTemplate.h" using namespace Cangjie::CHIR; ExpressionBase::ExpressionBase(const Expression* e) : expr(e) { CJC_NULLPTR_CHECK(e); } const Expression* ExpressionBase::GetRawExpr() const { return expr; } LocalVar* ExpressionBase::GetResult() const { return expr->GetResult(); } std::vector ExpressionBase::GetOperands() const { return expr->GetOperands(); } FuncCallBase::FuncCallBase(const Expression* e) : ExpressionBase(e) { CJC_NULLPTR_CHECK(e); if (auto funcCall = Cangjie::DynamicCast(e)) { expr = funcCall; exprE = nullptr; } else { expr = nullptr; exprE = Cangjie::StaticCast(e); } } FuncCallBase::FuncCallBase(const FuncCall* expr) : ExpressionBase(expr), expr(expr), exprE(nullptr) { CJC_NULLPTR_CHECK(expr); } FuncCallBase::FuncCallBase(const FuncCallWithException* exprE) : ExpressionBase(exprE), expr(nullptr), exprE(exprE) { CJC_NULLPTR_CHECK(exprE); } std::vector FuncCallBase::GetArgs() const { return expr ? expr->GetArgs() : exprE->GetArgs(); } Type* FuncCallBase::GetThisType() const { return expr ? expr->GetThisType() : exprE->GetThisType(); } std::vector FuncCallBase::GetInstantiatedTypeArgs() const { return expr ? expr->GetInstantiatedTypeArgs() : exprE->GetInstantiatedTypeArgs(); } ApplyBase::ApplyBase(const Expression* e) : FuncCallBase(e) { CJC_NULLPTR_CHECK(e); if (e->GetExprKind() == ExprKind::APPLY) { expr = Cangjie::StaticCast(e); exprE = nullptr; } else { expr = nullptr; exprE = Cangjie::StaticCast(e); } } ApplyBase::ApplyBase(const Apply* expr) : FuncCallBase(expr), expr(expr), exprE(nullptr) { CJC_NULLPTR_CHECK(expr); } ApplyBase::ApplyBase(const ApplyWithException* exprE) : FuncCallBase(exprE), expr(nullptr), exprE(exprE) { CJC_NULLPTR_CHECK(exprE); } Value* ApplyBase::GetCallee() const { return expr ? expr->GetCallee() : exprE->GetCallee(); } Type* ApplyBase::GetInstParentCustomTyOfCallee(CHIRBuilder& builder) const { return expr ? expr->GetInstParentCustomTyOfCallee(builder) : exprE->GetInstParentCustomTyOfCallee(builder); } DynamicDispatchBase::DynamicDispatchBase(const Expression* e) : FuncCallBase(e) { CJC_NULLPTR_CHECK(e); if (auto funcCall = Cangjie::DynamicCast(e)) { expr = funcCall; exprE = nullptr; } else { expr = nullptr; exprE = Cangjie::StaticCast(e); } } DynamicDispatchBase::DynamicDispatchBase(const DynamicDispatch* expr) : FuncCallBase(expr), expr(expr), exprE(nullptr) { CJC_NULLPTR_CHECK(expr); } DynamicDispatchBase::DynamicDispatchBase(const DynamicDispatchWithException* exprE) : FuncCallBase(exprE), expr(nullptr), exprE(exprE) { CJC_NULLPTR_CHECK(exprE); } std::vector DynamicDispatchBase::GetGenericTypeParams() const { return expr ? expr->GetGenericTypeParams() : exprE->GetGenericTypeParams(); } std::string DynamicDispatchBase::GetMethodName() const { return expr ? expr->GetMethodName() : exprE->GetMethodName(); } FuncType* DynamicDispatchBase::GetMethodType() const { return expr ? expr->GetMethodType() : exprE->GetMethodType(); } size_t DynamicDispatchBase::GetVirtualMethodOffset() const { return expr ? expr->GetVirtualMethodOffset() : exprE->GetVirtualMethodOffset(); } ClassType* DynamicDispatchBase::GetInstSrcParentCustomTypeOfMethod(CHIRBuilder& builder) const { return expr ? expr->GetInstSrcParentCustomTypeOfMethod(builder) : exprE->GetInstSrcParentCustomTypeOfMethod(builder); } InvokeBase::InvokeBase(const Expression* e) : DynamicDispatchBase(e) { CJC_NULLPTR_CHECK(e); if (e->GetExprKind() == ExprKind::INVOKE) { expr = Cangjie::StaticCast(e); exprE = nullptr; } else { expr = nullptr; exprE = Cangjie::StaticCast(e); } } InvokeBase::InvokeBase(const Invoke* expr) : DynamicDispatchBase(expr), expr(expr), exprE(nullptr) { CJC_NULLPTR_CHECK(expr); } InvokeBase::InvokeBase(const InvokeWithException* exprE) : DynamicDispatchBase(exprE), expr(nullptr), exprE(exprE) { CJC_NULLPTR_CHECK(exprE); } Value* InvokeBase::GetObject() const { return expr ? expr->GetObject() : exprE->GetObject(); } InvokeStaticBase::InvokeStaticBase(const Expression* e) : DynamicDispatchBase(e) { CJC_NULLPTR_CHECK(e); if (e->GetExprKind() == ExprKind::INVOKESTATIC) { expr = Cangjie::StaticCast(e); exprE = nullptr; } else { expr = nullptr; exprE = Cangjie::StaticCast(e); } } InvokeStaticBase::InvokeStaticBase(const InvokeStatic* expr) : DynamicDispatchBase(expr), expr(expr), exprE(nullptr) { CJC_NULLPTR_CHECK(expr); } InvokeStaticBase::InvokeStaticBase(const InvokeStaticWithException* exprE) : DynamicDispatchBase(exprE), expr(nullptr), exprE(exprE) { CJC_NULLPTR_CHECK(exprE); } Value* InvokeStaticBase::GetRTTIValue() const { return expr ? expr->GetRTTIValue() : exprE->GetRTTIValue(); } UnaryExprBase::UnaryExprBase(const Expression* e) : ExpressionBase(e) { CJC_NULLPTR_CHECK(e); if (e->GetExprMajorKind() == ExprMajorKind::UNARY_EXPR) { expr = Cangjie::StaticCast(e); exprE = nullptr; } else { expr = nullptr; exprE = Cangjie::StaticCast(e); } } UnaryExprBase::UnaryExprBase(const UnaryExpression* expr) : ExpressionBase(expr), expr(expr), exprE(nullptr) { CJC_NULLPTR_CHECK(expr); } UnaryExprBase::UnaryExprBase(const IntOpWithException* exprE) : ExpressionBase(exprE), expr(nullptr), exprE(exprE) { CJC_NULLPTR_CHECK(exprE); } ExprKind UnaryExprBase::GetOpKind() const { return expr ? expr->GetExprKind() : exprE->GetOpKind(); } std::string UnaryExprBase::GetExprKindName() const { return expr ? expr->GetExprKindName() : exprE->GetOpKindName(); } Value* UnaryExprBase::GetOperand() const { return expr ? expr->GetOperand() : exprE->GetLHSOperand(); } Cangjie::OverflowStrategy UnaryExprBase::GetOverflowStrategy() const { return expr ? expr->GetOverflowStrategy() : exprE->GetOverflowStrategy(); } BinaryExprBase::BinaryExprBase(const Expression* e) : ExpressionBase(e) { CJC_NULLPTR_CHECK(e); if (e->GetExprMajorKind() == ExprMajorKind::BINARY_EXPR) { expr = Cangjie::StaticCast(e); exprE = nullptr; } else { expr = nullptr; exprE = Cangjie::StaticCast(e); } } BinaryExprBase::BinaryExprBase(const BinaryExpression* expr) : ExpressionBase(expr), expr(expr), exprE(nullptr) { CJC_NULLPTR_CHECK(expr); } BinaryExprBase::BinaryExprBase(const IntOpWithException* exprE) : ExpressionBase(exprE), expr(nullptr), exprE(exprE) { CJC_NULLPTR_CHECK(exprE); } ExprKind BinaryExprBase::GetOpKind() const { return expr ? expr->GetExprKind() : exprE->GetOpKind(); } std::string BinaryExprBase::GetExprKindName() const { return expr ? expr->GetExprKindName() : exprE->GetOpKindName(); } Value* BinaryExprBase::GetLHSOperand() const { return expr ? expr->GetLHSOperand() : exprE->GetLHSOperand(); } Value* BinaryExprBase::GetRHSOperand() const { return expr ? expr->GetRHSOperand() : exprE->GetRHSOperand(); } Cangjie::OverflowStrategy BinaryExprBase::GetOverflowStrategy() const { return expr ? expr->GetOverflowStrategy() : exprE->GetOverflowStrategy(); } SpawnBase::SpawnBase(const Expression* e) : ExpressionBase(e) { CJC_NULLPTR_CHECK(e); if (e->GetExprKind() == ExprKind::SPAWN) { expr = Cangjie::StaticCast(e); exprE = nullptr; } else { expr = nullptr; exprE = Cangjie::StaticCast(e); } } SpawnBase::SpawnBase(const Spawn* expr) : ExpressionBase(expr), expr(expr), exprE(nullptr) { CJC_NULLPTR_CHECK(expr); } SpawnBase::SpawnBase(const SpawnWithException* exprE) : ExpressionBase(exprE), expr(nullptr), exprE(exprE) { CJC_NULLPTR_CHECK(exprE); } Value* SpawnBase::GetObject() const { return expr ? expr->GetOperands()[0] : exprE->GetOperands()[0]; } bool SpawnBase::IsExecuteClosure() const { return expr ? expr->IsExecuteClosure() : exprE->IsExecuteClosure(); } IntrinsicBase::IntrinsicBase(const Expression* e) : ExpressionBase(e) { CJC_NULLPTR_CHECK(e); if (e->GetExprKind() == ExprKind::INTRINSIC) { expr = Cangjie::StaticCast(e); exprE = nullptr; } else { expr = nullptr; exprE = Cangjie::StaticCast(e); } } IntrinsicBase::IntrinsicBase(const Intrinsic* expr) : ExpressionBase(expr), expr(expr), exprE(nullptr) { CJC_NULLPTR_CHECK(expr); } IntrinsicBase::IntrinsicBase(const IntrinsicWithException* exprE) : ExpressionBase(exprE), expr(nullptr), exprE(exprE) { CJC_NULLPTR_CHECK(exprE); } IntrinsicKind IntrinsicBase::GetIntrinsicKind() const { return expr ? expr->GetIntrinsicKind() : exprE->GetIntrinsicKind(); } std::vector IntrinsicBase::GetInstantiatedTypeArgs() const { return expr ? expr->GetInstantiatedTypeArgs() : exprE->GetInstantiatedTypeArgs(); } AllocateBase::AllocateBase(const Expression* e) : ExpressionBase(e) { CJC_NULLPTR_CHECK(e); if (e->GetExprKind() == ExprKind::ALLOCATE) { expr = Cangjie::StaticCast(e); exprE = nullptr; } else { expr = nullptr; exprE = Cangjie::StaticCast(e); } } AllocateBase::AllocateBase(const Allocate* expr) : ExpressionBase(expr), expr(expr), exprE(nullptr) { CJC_NULLPTR_CHECK(expr); } AllocateBase::AllocateBase(const AllocateWithException* exprE) : ExpressionBase(exprE), expr(nullptr), exprE(exprE) { CJC_NULLPTR_CHECK(exprE); } Type* AllocateBase::GetType() const { return expr ? expr->GetType() : exprE->GetType(); } RawArrayAllocateBase::RawArrayAllocateBase(const Expression* e) : ExpressionBase(e) { CJC_NULLPTR_CHECK(e); if (e->GetExprKind() == ExprKind::RAW_ARRAY_ALLOCATE) { expr = Cangjie::StaticCast(e); exprE = nullptr; } else { expr = nullptr; exprE = Cangjie::StaticCast(e); } } RawArrayAllocateBase::RawArrayAllocateBase(const RawArrayAllocate* expr) : ExpressionBase(expr), expr(expr), exprE(nullptr) { CJC_NULLPTR_CHECK(expr); } RawArrayAllocateBase::RawArrayAllocateBase(const RawArrayAllocateWithException* exprE) : ExpressionBase(exprE), expr(nullptr), exprE(exprE) { CJC_NULLPTR_CHECK(exprE); } Type* RawArrayAllocateBase::GetElementType() const { return expr ? expr->GetElementType() : exprE->GetElementType(); } Value* RawArrayAllocateBase::GetSize() const { return expr ? expr->GetSize() : exprE->GetSize(); }cangjie_compiler-1.0.7/src/CHIR/Expression/Terminator.cpp000066400000000000000000000701621510705540100233200ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Expression/Terminator.h" #include #include #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/ToStringUtils.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Value.h" #include "cangjie/Utils/CheckUtils.h" using namespace Cangjie::CHIR; namespace { constexpr size_t WITH_EXCEPTION_SUCCESSOR_NUM = 2; } // Terminator Terminator::Terminator( ExprKind kind, const std::vector& operands, const std::vector& successors, Block* parent) : Expression(kind, operands, parent) { CJC_NULLPTR_CHECK(parent); for (auto succ : successors) { AppendSuccessor(*succ); } } size_t Terminator::GetFirstSuccessorIndex() const { CJC_ASSERT(GetExprMajorKind() == ExprMajorKind::TERMINATOR); if (kind == ExprKind::BRANCH || kind == ExprKind::MULTIBRANCH) { return 1; } if (kind == ExprKind::RAISE_EXCEPTION) { return 1; } if (static_cast(ExprKind::APPLY_WITH_EXCEPTION) <= static_cast(kind) && static_cast(kind) <= static_cast(ExprKind::RAW_ARRAY_ALLOCATE_WITH_EXCEPTION)) { CJC_ASSERT(operands.size() >= WITH_EXCEPTION_SUCCESSOR_NUM); return operands.size() - WITH_EXCEPTION_SUCCESSOR_NUM; } return 0; } void Terminator::ReplaceWith(Expression& newTerminator) { CJC_ASSERT(newTerminator.IsTerminator()); if (result != nullptr) { CJC_NULLPTR_CHECK(newTerminator.GetResult()); for (auto user : result->GetUsers()) { user->ReplaceOperand(result, newTerminator.GetResult()); } } auto tempParent = parent; RemoveSelfFromBlock(); tempParent->AppendTerminator(StaticCast(&newTerminator)); } size_t Terminator::GetNumOfSuccessor() const { return operands.size() - GetFirstSuccessorIndex(); } Block* Terminator::GetSuccessor(size_t index) const { CJC_ASSERT(operands.size() == 0 || index + GetFirstSuccessorIndex() < operands.size()); return StaticCast(operands[GetFirstSuccessorIndex() + index]); } size_t Terminator::GetNumOfOperands() const { return GetFirstSuccessorIndex(); } std::vector Terminator::GetOperands() const { CJC_ASSERT(operands.size() >= GetFirstSuccessorIndex()); return {operands.begin(), operands.begin() + GetFirstSuccessorIndex()}; } Value* Terminator::GetOperand(size_t idx) const { CJC_ASSERT(idx < GetFirstSuccessorIndex()); return operands[idx]; } const std::vector Terminator::GetSuccessors() const { std::vector succs; for (auto i = GetFirstSuccessorIndex(); i < operands.size(); ++i) { succs.emplace_back(StaticCast(operands[i])); } return succs; } void Terminator::ReplaceSuccessor(Block& oldSuccessor, Block& newSuccessor) { oldSuccessor.RemovePredecessor(*GetParentBlock()); newSuccessor.AddPredecessor(GetParentBlock()); auto it = std::find(operands.begin(), operands.end(), &oldSuccessor); CJC_ASSERT(it != operands.end()); while (it != operands.end()) { *it = &newSuccessor; it = std::find(std::next(it), operands.end(), &oldSuccessor); } } void Terminator::ReplaceSuccessor(size_t index, Block& newSuccessor) { CJC_ASSERT(operands.size() == 0 || index + GetFirstSuccessorIndex() < operands.size()); GetSuccessor(index)->RemovePredecessor(*GetParentBlock()); newSuccessor.AddPredecessor(GetParentBlock()); operands[GetFirstSuccessorIndex() + index] = &newSuccessor; } void Terminator::LetSuccessorsRemoveCurBlock() const { auto succs = GetSuccessors(); for (auto suc : succs) { suc->RemovePredecessor(*parent); } } void Terminator::RemoveSelfFromBlock() { LetSuccessorsRemoveCurBlock(); Expression::RemoveSelfFromBlock(); } void Terminator::AppendSuccessor(Block& block) { operands.emplace_back(&block); } GoTo::GoTo(Block* destBlock, Block* parent) : Terminator(ExprKind::GOTO, {}, {destBlock}, parent) { } Block* GoTo::GetDestination() const { return GetSuccessor(0); } // MultiBranch MultiBranch::MultiBranch(Value* cond, Block* defaultBlock, const std::vector& vals, const std::vector& succs, Block* parent) : Terminator(ExprKind::MULTIBRANCH, {cond}, {}, parent), caseVals(vals) { CJC_NULLPTR_CHECK(cond); CJC_NULLPTR_CHECK(defaultBlock); CJC_NULLPTR_CHECK(parent); /* Note that successors[0] is used to store the default basic block */ AppendSuccessor(*defaultBlock); for (auto b : succs) { AppendSuccessor(*b); } } Value* MultiBranch::GetCondition() const { return operands[0]; } const std::vector& MultiBranch::GetCaseVals() const { return caseVals; } uint64_t MultiBranch::GetCaseValByIndex(size_t index) const { return caseVals[index]; } Block* MultiBranch::GetCaseBlockByIndex(size_t index) const { return GetSuccessor(index + 1); } Block* MultiBranch::GetDefaultBlock() const { return GetSuccessor(0); } std::vector MultiBranch::GetNormalBlocks() const { auto succs = GetSuccessors(); return {succs.begin() + 1, succs.end()}; } std::string MultiBranch::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << GetCondition()->GetIdentifier(); ss << ", "; ss << GetSuccessor(0)->GetIdentifier(); for (size_t i = 1; i < GetNumOfSuccessor(); i++) { ss << ", ["; ss << GetCaseValByIndex(i - 1) << ", "; ss << GetSuccessor(i)->GetIdentifier() << "]"; } ss << ")"; ss << CommentToString(); return ss.str(); } ExpressionWithException::ExpressionWithException(ExprKind kind, Block* parent) : Terminator(kind, {}, {}, parent) { } ExpressionWithException::ExpressionWithException( ExprKind kind, const std::vector& operands, const std::vector& successors, Block* parent) : Terminator(kind, operands, successors, parent) { } Block* ExpressionWithException::GetSuccessBlock() const { return GetSuccessor(0); } Block* ExpressionWithException::GetErrorBlock() const { return GetSuccessor(1); } FuncCallWithException::FuncCallWithException(ExprKind kind, const FuncCallContext& funcCallCtx, Block* parent) : ExpressionWithException(kind, parent), instantiatedTypeArgs(funcCallCtx.instTypeArgs), thisType(funcCallCtx.thisType) { } Type* FuncCallWithException::GetThisType() const { return thisType; } void FuncCallWithException::SetThisType(Type* type) { thisType = type; } const std::vector& FuncCallWithException::GetInstantiatedTypeArgs() const { return instantiatedTypeArgs; } ApplyWithException::ApplyWithException( Value* callee, const FuncCallContext& callContext, Block* sucBlock, Block* errBlock, Block* parent) : FuncCallWithException(ExprKind::APPLY_WITH_EXCEPTION, callContext, parent) { CJC_NULLPTR_CHECK(callee); CJC_NULLPTR_CHECK(sucBlock); CJC_NULLPTR_CHECK(errBlock); CJC_NULLPTR_CHECK(parent); AppendOperand(*callee); for (auto op : callContext.args) { AppendOperand(*op); } AppendSuccessor(*sucBlock); AppendSuccessor(*errBlock); } Value* ApplyWithException::GetCallee() const { return operands[0]; } /** @brief Get a list of the ApplyWithException operation argument nodes */ std::vector ApplyWithException::GetArgs() const { if (GetFirstSuccessorIndex() <= 1) { return {}; } else { return {operands.begin() + 1, operands.begin() + GetFirstSuccessorIndex()}; } } Type* ApplyWithException::GetInstParentCustomTyOfCallee(CHIRBuilder& builder) const { return GetInstParentCustomTypeForAweCallee(*this, builder); } std::string ApplyWithException::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << ThisTypeToString(thisType).AddDelimiterOrNot(", ").Str(); ss << GetCallee()->GetIdentifier(); ss << InstTypeArgsToString(instantiatedTypeArgs); ss << StringWrapper(", ").AppendOrClear(ExprWithExceptionOperandsToString(GetArgs(), GetSuccessors())).Str(); ss << ")"; ss << CommentToString(); return ss.str(); } inline static void CheckVirFuncInvokeInfo(const InvokeCallContext& callContext) { CJC_NULLPTR_CHECK(callContext.caller); CJC_NULLPTR_CHECK(callContext.funcCallCtx.thisType); CJC_ASSERT(!callContext.virMethodCtx.srcCodeIdentifier.empty()); CJC_NULLPTR_CHECK(callContext.virMethodCtx.originalFuncType); } DynamicDispatchWithException::DynamicDispatchWithException( ExprKind kind, const InvokeCallContext& callContext, Block* sucBlock, Block* errBlock, Block* parent) : FuncCallWithException(kind, callContext.funcCallCtx, parent), virMethodCtx(callContext.virMethodCtx) { CJC_NULLPTR_CHECK(sucBlock); CJC_NULLPTR_CHECK(errBlock); CJC_NULLPTR_CHECK(parent); CheckVirFuncInvokeInfo(callContext); AppendOperand(*callContext.caller); for (auto op : callContext.funcCallCtx.args) { AppendOperand(*op); } AppendSuccessor(*sucBlock); AppendSuccessor(*errBlock); } const std::string& DynamicDispatchWithException::GetMethodName() const { return virMethodCtx.srcCodeIdentifier; } FuncType* DynamicDispatchWithException::GetMethodType() const { return virMethodCtx.originalFuncType; } std::vector DynamicDispatchWithException::GetVirtualMethodInfo(CHIRBuilder& builder) const { auto thisTypeDeref = thisType->StripAllRefs(); if (thisTypeDeref->IsThis()) { thisTypeDeref = GetTopLevelFunc()->GetParentCustomTypeDef()->GetType(); } std::vector instParamTypes; for (auto arg : GetArgs()) { instParamTypes.emplace_back(arg->GetType()); } auto instFuncType = builder.GetType(instParamTypes, builder.GetUnitTy()); FuncCallType funcCallType{virMethodCtx.srcCodeIdentifier, instFuncType, instantiatedTypeArgs}; auto res = GetFuncIndexInVTable(*thisTypeDeref, funcCallType, IsInvokeStaticBase(), builder); CJC_ASSERT(!res.empty()); return res; } const std::vector& DynamicDispatchWithException::GetGenericTypeParams() const { return virMethodCtx.genericTypeParams; } size_t DynamicDispatchWithException::GetVirtualMethodOffset(CHIRBuilder* builder) const { auto offset = Get(); if (offset.has_value()) { return offset.value(); } else { CJC_NULLPTR_CHECK(builder); return GetVirtualMethodInfo(*builder)[0].offset; } } ClassType* DynamicDispatchWithException::GetInstSrcParentCustomTypeOfMethod(CHIRBuilder& builder) const { for (auto& r : GetVirtualMethodInfo(builder)) { if (r.offset == GetVirtualMethodOffset()) { CJC_NULLPTR_CHECK(r.instSrcParentType); return r.instSrcParentType; } } CJC_ABORT(); return nullptr; } AttributeInfo DynamicDispatchWithException::GetVirtualMethodAttr(CHIRBuilder& builder) const { for (auto& r : GetVirtualMethodInfo(builder)) { if (r.offset == GetVirtualMethodOffset()) { CJC_NULLPTR_CHECK(r.instSrcParentType); return r.attr; } } CJC_ABORT(); return AttributeInfo{}; } InvokeWithException::InvokeWithException( const InvokeCallContext& callContext, Block* sucBlock, Block* errBlock, Block* parent) : DynamicDispatchWithException(ExprKind::INVOKE_WITH_EXCEPTION, callContext, sucBlock, errBlock, parent) { } Value* InvokeWithException::GetObject() const { return operands[0]; } /** @brief Get the call args of this InvokeWithException operation */ std::vector InvokeWithException::GetArgs() const { return {operands.begin(), operands.begin() + GetFirstSuccessorIndex()}; } std::string InvokeWithException::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << ThisTypeToString(thisType).AddDelimiterOrNot(", ").Str(); ss << GetMethodName(); ss << InstTypeArgsToString(instantiatedTypeArgs) << ": "; ss << GetMethodType()->ToString() << ", "; ss << ExprWithExceptionOperandsToString(GetArgs(), GetSuccessors()); ss << ")"; ss << CommentToString(); return ss.str(); } InvokeStaticWithException::InvokeStaticWithException( const InvokeCallContext& callContext, Block* sucBlock, Block* errBlock, Block* parent) : DynamicDispatchWithException(ExprKind::INVOKESTATIC_WITH_EXCEPTION, callContext, sucBlock, errBlock, parent) { } Value* InvokeStaticWithException::GetRTTIValue() const { return operands[0]; } std::vector InvokeStaticWithException::GetArgs() const { return {operands.begin() + 1, operands.begin() + GetFirstSuccessorIndex()}; } std::string InvokeStaticWithException::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << ThisTypeToString(thisType).AddDelimiterOrNot(", ").Str(); ss << GetMethodName(); ss << InstTypeArgsToString(instantiatedTypeArgs) << ": "; ss << GetMethodType()->ToString() << ", "; ss << GetRTTIValue()->GetIdentifier(); ss << StringWrapper(", ").AppendOrClear(ExprWithExceptionOperandsToString(GetArgs(), GetSuccessors())).Str(); ss << ")"; ss << CommentToString(); return ss.str(); } // IntOpWithException IntOpWithException::IntOpWithException( ExprKind unaryKind, Value* operand, Block* normal, Block* exception, Block* parent) : ExpressionWithException(ExprKind::INT_OP_WITH_EXCEPTION, {operand}, {normal, exception}, parent), opKind(unaryKind) { } IntOpWithException::IntOpWithException( ExprKind binaryKind, Value* lhs, Value* rhs, Block* normal, Block* exception, Block* parent) : ExpressionWithException(ExprKind::INT_OP_WITH_EXCEPTION, {lhs, rhs}, {normal, exception}, parent), opKind(binaryKind) { } ExprKind IntOpWithException::GetOpKind() const { return opKind; } std::string IntOpWithException::GetOpKindName() const { return ExprKindMgr::Instance()->GetKindName(static_cast(opKind)) + "WithException"; } Value* IntOpWithException::GetLHSOperand() const { return GetOperand(0); } Value* IntOpWithException::GetRHSOperand() const { return GetOperand(1); } Cangjie::OverflowStrategy IntOpWithException::GetOverflowStrategy() const { if (opKind == ExprKind::DIV) { return overflowStrategy; } return Cangjie::OverflowStrategy::THROWING; } std::string IntOpWithException::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetOpKindName(); ss << "("; ss << ExprWithExceptionOperandsToString(GetOperands(), GetSuccessors()); ss << ")"; ss << CommentToString(); return ss.str(); } // TypeCastWithException TypeCastWithException::TypeCastWithException(Value* operand, Block* normal, Block* exception, Block* parent) : ExpressionWithException(ExprKind::TYPECAST_WITH_EXCEPTION, {operand}, {normal, exception}, parent) { } Cangjie::OverflowStrategy TypeCastWithException::GetOverflowStrategy() const { return Cangjie::OverflowStrategy::THROWING; } /** @brief Get the source value of this cast operation */ Value* TypeCastWithException::GetSourceValue() const { return operands[0]; } Type* TypeCastWithException::GetSourceTy() const { return operands[0]->GetType(); } Type* TypeCastWithException::GetTargetTy() const { return result->GetType(); } std::string TypeCastWithException::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; auto args = std::vector{GetSourceValue()}; ss << ExprWithExceptionOperandsToString(args, GetSuccessors()) << ", "; ss << ")"; ss << CommentToString(); return ss.str(); } // IntrinsicWithException IntrinsicWithException::IntrinsicWithException( const IntrisicCallContext& callContext, Block* normal, Block* exception, Block* parent) : ExpressionWithException(ExprKind::INTRINSIC_WITH_EXCEPTION, callContext.args, {normal, exception}, parent), intrinsicKind(callContext.kind), instantiatedTypeArgs(callContext.instTypeArgs) { } IntrinsicKind IntrinsicWithException::GetIntrinsicKind() const { return intrinsicKind; } const std::vector& IntrinsicWithException::GetInstantiatedTypeArgs() const { return instantiatedTypeArgs; } const std::vector IntrinsicWithException::GetArgs() const { return GetOperands(); } std::string IntrinsicWithException::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName() << "/" << INTRINSIC_KIND_TO_STRING_MAP.at(intrinsicKind); ss << InstTypeArgsToString(instantiatedTypeArgs); ss << "("; ss << ExprWithExceptionOperandsToString(GetOperands(), GetSuccessors()); ss << ")"; ss << CommentToString(); return ss.str(); } // AllocateWithException AllocateWithException::AllocateWithException(Type* ty, Block* normal, Block* exception, Block* parent) : ExpressionWithException(ExprKind::ALLOCATE_WITH_EXCEPTION, {}, {normal, exception}, parent), ty(ty) { } Type* AllocateWithException::GetType() const { return ty; } std::string AllocateWithException::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << ty->ToString() << ", "; ss << ExprWithExceptionOperandsToString(std::vector{}, GetSuccessors()); ss << ")"; ss << CommentToString(); return ss.str(); } // RawArrayAllocateWithException RawArrayAllocateWithException::RawArrayAllocateWithException( Type* eleTy, Value* size, Block* normal, Block* exception, Block* parent) : ExpressionWithException(ExprKind::RAW_ARRAY_ALLOCATE_WITH_EXCEPTION, {size}, {normal, exception}, parent), elementType(eleTy) { } Value* RawArrayAllocateWithException::GetSize() const { return operands[0]; } Type* RawArrayAllocateWithException::GetElementType() const { return elementType; } std::string RawArrayAllocateWithException::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; ss << elementType->ToString() << ", "; auto args = std::vector{GetSize()}; ss << ExprWithExceptionOperandsToString(args, GetSuccessors()); ss << ")"; ss << CommentToString(); return ss.str(); } SpawnWithException::SpawnWithException( Value* val, Value* arg, Block* normal, Block* exception, Block* parent) : ExpressionWithException(ExprKind::SPAWN_WITH_EXCEPTION, {val, arg}, {normal, exception}, parent) { } SpawnWithException::SpawnWithException( Value* val, Block* normal, Block* exception, Block* parent) : ExpressionWithException(ExprKind::SPAWN_WITH_EXCEPTION, {val}, {normal, exception}, parent) { } Value* SpawnWithException::GetFuture() const { CJC_ASSERT(!IsExecuteClosure()); return operands[0]; } void SpawnWithException::SetExecuteClosure(FuncBase& func) { executeClosure = &func; } Value* SpawnWithException::GetSpawnArg() const { if (GetFirstSuccessorIndex() > 1) { return operands[1]; } return nullptr; } Value* SpawnWithException::GetClosure() const { CJC_ASSERT(IsExecuteClosure()); return operands[0]; } FuncBase* SpawnWithException::GetExecuteClosure() const { return executeClosure; } bool SpawnWithException::IsExecuteClosure() const { return executeClosure != nullptr; } std::string SpawnWithException::ToString([[maybe_unused]] size_t indent) const { std::stringstream ss; ss << GetExprKindName(); ss << "("; auto args = std::vector{GetFuture()}; if (auto arg = GetSpawnArg()) { args.emplace_back(arg); } ss << ExprWithExceptionOperandsToString(args, GetSuccessors()); ss << ")"; ss << CommentToString(); if (IsExecuteClosure()) { ss << AddExtraComment("executeClosure: " + GetExecuteClosure()->GetIdentifier()); } return ss.str(); } SpawnWithException* SpawnWithException::Clone(CHIRBuilder& builder, Block& parent) const { SpawnWithException* newNode = nullptr; auto arg = GetSpawnArg(); if (arg != nullptr) { newNode = builder.CreateExpression( result->GetType(), GetFuture(), arg, GetSuccessBlock(), GetErrorBlock(), &parent); } else { newNode = builder.CreateExpression( result->GetType(), GetFuture(), GetSuccessBlock(), GetErrorBlock(), &parent); } if (executeClosure) { newNode->SetExecuteClosure(*executeClosure); } parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } GoTo* GoTo::Clone(CHIRBuilder& builder, Block& parent) const { CJC_ASSERT(result == nullptr); auto newNode = builder.CreateTerminator(GetDestination(), &parent); parent.AppendExpression(newNode); return newNode; } Branch::Branch(Value* cond, Block* trueBlock, Block* falseBlock, Block* parent) : Terminator(ExprKind::BRANCH, {cond}, {trueBlock, falseBlock}, parent) { } Value* Branch::GetCondition() const { return operands[0]; } Block* Branch::GetTrueBlock() const { return GetSuccessor(0); } Block* Branch::GetFalseBlock() const { return GetSuccessor(1); } void Branch::SetSourceExpr(SourceExpr srcExpr) { sourceExpr = srcExpr; } SourceExpr Branch::GetSourceExpr() const { return sourceExpr; } std::string Branch::ToString([[maybe_unused]] size_t indent) const { const static std::unordered_map SOURCE_EXPR_MAP = { {SourceExpr::IF_EXPR, "IF_EXPR"}, {SourceExpr::WHILE_EXPR, "WHILE_EXPR"}, {SourceExpr::DO_WHILE_EXPR, "DO_WHILE_EXPR"}, {SourceExpr::MATCH_EXPR, "MATCH_EXPR"}, {SourceExpr::IF_LET_OR_WHILE_LET, "IF_LET_OR_WHILE_LET"}, {SourceExpr::QUEST, "QUEST"}, {SourceExpr::BINARY, "BINARY"}, {SourceExpr::FOR_IN_EXPR, "FOR_IN_EXPR"}, {SourceExpr::OTHER, "OTHER"}, }; std::stringstream ss; ss << Expression::ToString(indent); ss << CommentToString(); ss << AddExtraComment("sourceExpr: " + SOURCE_EXPR_MAP.at(sourceExpr)); return ss.str(); } Exit::Exit(Block* parent) : Terminator(ExprKind::EXIT, {}, {}, parent) { } RaiseException::RaiseException(Value* value, Block* parent) : Terminator(ExprKind::RAISE_EXCEPTION, {value}, {}, parent) { } RaiseException::RaiseException(Value* value, Block* successor, Block* parent) : Terminator(ExprKind::RAISE_EXCEPTION, {value}, {successor}, parent) { } Value* RaiseException::GetExceptionValue() const { return operands[0]; } Block* RaiseException::GetExceptionBlock() const { auto succs = GetSuccessors(); if (!succs.empty()) { return succs[0]; } return nullptr; } Branch* Branch::Clone(CHIRBuilder& builder, Block& parent) const { CJC_ASSERT(result == nullptr); auto newNode = builder.CreateTerminator(GetCondition(), GetTrueBlock(), GetFalseBlock(), &parent); parent.AppendExpression(newNode); return newNode; } MultiBranch* MultiBranch::Clone(CHIRBuilder& builder, Block& parent) const { CJC_ASSERT(result == nullptr); auto newNode = builder.CreateTerminator( GetCondition(), GetDefaultBlock(), GetCaseVals(), GetNormalBlocks(), &parent); parent.AppendExpression(newNode); return newNode; } Exit* Exit::Clone(CHIRBuilder& builder, Block& parent) const { CJC_ASSERT(result == nullptr); auto newNode = builder.CreateTerminator(&parent); parent.AppendExpression(newNode); return newNode; } ApplyWithException* ApplyWithException::Clone(CHIRBuilder& builder, Block& parent) const { CJC_NULLPTR_CHECK(result); ApplyWithException* newNode = builder.CreateExpression( result->GetType(), GetCallee(), FuncCallContext{ .args = GetArgs(), .instTypeArgs = GetInstantiatedTypeArgs(), .thisType = GetThisType()}, GetSuccessBlock(), GetErrorBlock(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } InvokeWithException* InvokeWithException::Clone(CHIRBuilder& builder, Block& parent) const { CJC_NULLPTR_CHECK(result); auto args = GetArgs(); args.erase(args.begin()); auto invokeInfo = InvokeCallContext { .caller = GetObject(), .funcCallCtx = FuncCallContext { .args = args, .instTypeArgs = instantiatedTypeArgs, .thisType = thisType }, .virMethodCtx = virMethodCtx }; InvokeWithException* newNode = builder.CreateExpression( result->GetType(), invokeInfo, GetSuccessBlock(), GetErrorBlock(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } InvokeStaticWithException* InvokeStaticWithException::Clone(CHIRBuilder& builder, Block& parent) const { CJC_NULLPTR_CHECK(result); auto invokeInfo = InvokeCallContext { .caller = GetRTTIValue(), .funcCallCtx = FuncCallContext { .args = GetArgs(), .instTypeArgs = instantiatedTypeArgs, .thisType = thisType }, .virMethodCtx = virMethodCtx }; InvokeStaticWithException* newNode = builder.CreateExpression( result->GetType(), invokeInfo, GetSuccessBlock(), GetErrorBlock(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } IntOpWithException* IntOpWithException::Clone(CHIRBuilder& builder, Block& parent) const { CJC_NULLPTR_CHECK(result); IntOpWithException* newNode = nullptr; if (GetFirstSuccessorIndex() == 1) { newNode = builder.CreateExpression( result->GetType(), GetOpKind(), GetLHSOperand(), GetSuccessBlock(), GetErrorBlock(), &parent); } else { newNode = builder.CreateExpression(result->GetType(), GetOpKind(), GetLHSOperand(), GetRHSOperand(), GetSuccessBlock(), GetErrorBlock(), &parent); } parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } TypeCastWithException* TypeCastWithException::Clone(CHIRBuilder& builder, Block& parent) const { CJC_NULLPTR_CHECK(result); auto newNode = builder.CreateExpression( result->GetType(), GetSourceValue(), GetSuccessBlock(), GetErrorBlock(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } IntrinsicWithException* IntrinsicWithException::Clone(CHIRBuilder& builder, Block& parent) const { CJC_NULLPTR_CHECK(result); auto callContext = IntrisicCallContext { .kind = GetIntrinsicKind(), .args = GetArgs(), .instTypeArgs = GetInstantiatedTypeArgs() }; auto newNode = builder.CreateExpression( result->GetType(), callContext, GetSuccessBlock(), GetErrorBlock(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } AllocateWithException* AllocateWithException::Clone(CHIRBuilder& builder, Block& parent) const { CJC_NULLPTR_CHECK(result); auto newNode = builder.CreateExpression( result->GetType(), GetType(), GetSuccessBlock(), GetErrorBlock(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } RawArrayAllocateWithException* RawArrayAllocateWithException::Clone(CHIRBuilder& builder, Block& parent) const { CJC_NULLPTR_CHECK(result); auto newNode = builder.CreateExpression( result->GetType(), GetElementType(), GetSize(), GetSuccessBlock(), GetErrorBlock(), &parent); parent.AppendExpression(newNode); newNode->GetResult()->AppendAttributeInfo(result->GetAttributeInfo()); return newNode; } cangjie_compiler-1.0.7/src/CHIR/GenerateVTable/000077500000000000000000000000001510705540100211535ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CHIR/GenerateVTable/CMakeLists.txt000066400000000000000000000005441510705540100237160ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB GENERATE_VTABLE *.cpp) set(CHIR_BASE_SRC ${CHIR_BASE_SRC} ${GENERATE_VTABLE} PARENT_SCOPE)cangjie_compiler-1.0.7/src/CHIR/GenerateVTable/GenerateVTable.cpp000066400000000000000000000143651510705540100245200ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/GenerateVTable/GenerateVTable.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/GenerateVTable/UpdateOperatorVTable.h" #include "cangjie/CHIR/GenerateVTable/VTableGenerator.h" #include "cangjie/CHIR/GenerateVTable/WrapMutFunc.h" #include "cangjie/CHIR/GenerateVTable/WrapVirtualFunc.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Visitor/Visitor.h" #include "cangjie/Mangle/CHIRManglingUtils.h" #include "cangjie/Utils/ProfileRecorder.h" using namespace Cangjie; using namespace Cangjie::CHIR; namespace { bool CalleeIsMutFuncFromParent(Type* thisType, FuncBase* callee, const Func& topLevelFunc) { // thisType must be Struct if (thisType == nullptr || !thisType->StripAllRefs()->IsStruct()) { return false; } // callee must be mut func if (callee == nullptr || !callee->TestAttr(Attribute::MUT)) { return false; } // callee's parent must be from interface if (!callee->GetParentCustomTypeOrExtendedType()->IsClass()) { return false; } // current Apply is not in wrapper func return topLevelFunc.Get() == nullptr; } } // namespace GenerateVTable::GenerateVTable(Package& pkg, CHIRBuilder& b, const Cangjie::GlobalOptions& opts) : package(pkg), builder(b), opts(opts) { } void GenerateVTable::CreateVTable() { Utils::ProfileRecorder recorder("GenerateVTable", "CreateVTable"); auto vtableGenerator = VTableGenerator(builder); for (auto customDef : package.GetAllCustomTypeDef()) { if (customDef->TestAttr(Attribute::SKIP_ANALYSIS)) { continue; } vtableGenerator.GenerateVTable(*customDef); } } void GenerateVTable::UpdateOperatorVirFunc() { UpdateOperatorVTable(package, builder).Update(); } void GenerateVTable::CreateVirtualFuncWrapper(const IncreKind& kind, const CompilationCache& increCachedInfo, VirtualWrapperDepMap& curVirtFuncWrapDep, VirtualWrapperDepMap& delVirtFuncWrapForIncr) { Utils::ProfileRecorder recorder("GenerateVTable", "CreateVirtualFuncWrapper"); bool targetIsWin = opts.target.os == Triple::OSType::WINDOWS; IncreKind tempKind = opts.enIncrementalCompilation ? kind : IncreKind::INVALID; auto wrapper = WrapVirtualFunc(builder, increCachedInfo, tempKind, targetIsWin); for (auto customDef : package.GetAllCustomTypeDef()) { if (customDef->TestAttr(Attribute::SKIP_ANALYSIS)) { continue; } wrapper.CheckAndWrap(*customDef); } curVirtFuncWrapDep = wrapper.GetCurVirtFuncWrapDep(); delVirtFuncWrapForIncr = wrapper.GetDelVirtFuncWrapForIncr(); } void GenerateVTable::CreateMutFuncWrapper() { Utils::ProfileRecorder recorder("GenerateVTable", "CreateMutFuncWrapper"); auto wrapper = WrapMutFunc(builder); for (auto customDef : package.GetAllCustomTypeDef()) { if (customDef->TestAttr(Attribute::SKIP_ANALYSIS)) { continue; } wrapper.Run(*customDef); } mutFuncWrappers = wrapper.GetWrappers(); } FuncBase* GenerateVTable::GetMutFuncWrapper(const Type& thisType, const std::vector& args, const std::vector& instTypeArgs, Type& retType, const FuncBase& callee) { std::vector paramTypes; for (auto arg : args) { paramTypes.emplace_back(arg->GetType()); } auto funcCallType = FuncCallType { .funcName = callee.GetSrcCodeIdentifier(), .funcType = builder.GetType(paramTypes, &retType), .genericTypeArgs = instTypeArgs }; auto vtableRes = GetFuncIndexInVTable( *thisType.StripAllRefs(), funcCallType, callee.TestAttr(Attribute::STATIC), builder); CJC_ASSERT(vtableRes.size() == 1); auto wrapperName = CHIRMangling::GenerateVirtualFuncMangleName( &callee, *vtableRes[0].originalDef, vtableRes[0].halfInstSrcParentType, false); auto it = mutFuncWrappers.find(wrapperName); CJC_ASSERT(it != mutFuncWrappers.end()); return it->second; } void GenerateVTable::UpdateFuncCall() { Utils::ProfileRecorder recorder("GenerateVTable", "UpdateFuncCall"); std::vector applys; std::vector applyEs; std::function preVisit = [this, &preVisit, &applys, &applyEs](Expression& e) { if (auto lambda = DynamicCast(&e)) { Visitor::Visit(*lambda->GetBody(), preVisit); } else if (auto dyExpr = DynamicCast(&e)) { e.Set(dyExpr->GetVirtualMethodOffset(&builder)); } else if (auto dyExprE = DynamicCast(&e)) { e.Set(dyExprE->GetVirtualMethodOffset(&builder)); } else if (auto apply = DynamicCast(&e)) { auto callee = DynamicCast(apply->GetCallee()); if (CalleeIsMutFuncFromParent(apply->GetThisType(), callee, *e.GetTopLevelFunc())) { applys.emplace_back(apply); } } else if (auto applyE = DynamicCast(&e)) { auto callee = DynamicCast(applyE->GetCallee()); if (CalleeIsMutFuncFromParent(applyE->GetThisType(), callee, *e.GetTopLevelFunc())) { applyEs.emplace_back(applyE); } } return VisitResult::CONTINUE; }; for (auto func : package.GetGlobalFuncs()) { Visitor::Visit(*func, preVisit); } for (auto apply : applys) { auto callee = VirtualCast(apply->GetCallee()); auto wrapperFunc = GetMutFuncWrapper(*apply->GetThisType(), apply->GetArgs(), apply->GetInstantiatedTypeArgs(), *apply->GetResult()->GetType(), *callee); apply->ReplaceOperand(callee, wrapperFunc); } for (auto apply : applyEs) { auto callee = VirtualCast(apply->GetCallee()); auto wrapperFunc = GetMutFuncWrapper(*apply->GetThisType(), apply->GetArgs(), apply->GetInstantiatedTypeArgs(), *apply->GetResult()->GetType(), *callee); apply->ReplaceOperand(callee, wrapperFunc); } }cangjie_compiler-1.0.7/src/CHIR/GenerateVTable/UpdateOperatorVTable.cpp000066400000000000000000000253151510705540100257210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/GenerateVTable/UpdateOperatorVTable.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/ExtendDef.h" #include "cangjie/Mangle/CHIRManglingUtils.h" #include "cangjie/Utils/ConstantsUtils.h" using namespace Cangjie; using namespace Cangjie::CHIR; namespace { constexpr OverflowStrategy OVF_STRATEGIES[]{ OverflowStrategy::WRAPPING, OverflowStrategy::THROWING, OverflowStrategy::SATURATING}; bool IsPossiblyOverflowOperator(const VirtualFuncInfo& info) { auto& name = info.srcCodeIdentifier; auto& type = *info.typeInfo.sigType; auto params = type.GetParamTypes(); if (params.size() == 1 && IsOverflowOperator(name)) { return CanBeIntegerType(*params[0]) && CanBeIntegerType(*info.typeInfo.returnType); } if (params.size() == 0 && name == "-") { return CanBeIntegerType(*info.typeInfo.returnType); } return false; } bool IsBuiltinOverflowOperator(const CustomTypeDef& def, const VirtualFuncInfo& info) { auto defType = def.GetType(); auto builtinDefType = DynamicCast(defType); if (!builtinDefType || !builtinDefType->IsInteger()) { return false; } auto& name = info.srcCodeIdentifier; auto& type = *info.typeInfo.sigType; auto params = type.GetParamTypes(); if (params.size() == 1 && params[0] == defType && params[0]->IsInteger() && params[0] == info.typeInfo.returnType) { return IsOverflowOperator(name); } if (params.size() == 0 && info.typeInfo.returnType->IsInteger()) { return name == "-"; } return false; } CHIR::ExprKind ToExprKind(const std::string& name) { if (name == "+") { return CHIR::ExprKind::ADD; } if (name == "-") { return CHIR::ExprKind::SUB; } if (name == "*") { return CHIR::ExprKind::MUL; } CJC_ASSERT(name == "/"); return CHIR::ExprKind::DIV; } } // namespace bool UpdateOperatorVTable::RewriteInfoOrdering::operator()(ClassDef* one, ClassDef* another) const { return one->GetIdentifier() < another->GetIdentifier(); } UpdateOperatorVTable::UpdateOperatorVTable(const Package& package, CHIRBuilder& builder) : package(package), builder(builder) { } void UpdateOperatorVTable::CollectOverflowOperators() { // collect all imported and source interfaces for (auto def : package.GetClasses()) { if (def->IsInterface()) { CollectOverflowOperatorsOnInterface(*def); } } for (auto def : package.GetImportedClasses()) { if (def->IsInterface()) { CollectOverflowOperatorsOnInterface(*def); } } } void UpdateOperatorVTable::AddRewriteInfo(ClassDef& def, size_t index) { interRewriteInfo[&def].ov.insert(index); } void UpdateOperatorVTable::CollectOverflowOperatorsOnInterface(ClassDef& def) { auto& vtable = def.GetVTable(); for (auto& pair : vtable) { if (pair.first->GetClassDef() != &def) { continue; } for (size_t i{0}; i < pair.second.size(); ++i) { auto& entry = pair.second[i]; if (IsPossiblyOverflowOperator(entry)) { AddRewriteInfo(def, i); } } } } Func* UpdateOperatorVTable::GenerateBuiltinOverflowOperatorFunc( const std::string& name, OverflowStrategy ovf, const ExtendDef& user, bool isBinary) { auto type = StaticCast(user.GetExtendedType()); auto mangledName = CHIRMangling::GenerateOverflowOperatorFuncMangleName(name, ovf, isBinary, *type); if (auto it = cache.find(mangledName); it != cache.cend()) { return it->second; } auto rawMangledName = OverflowStrategyPrefix(ovf) + name; auto packageName = user.GetPackageName(); auto funcType = isBinary ? builder.GetType(std::vector{type, type}, type) : builder.GetType(std::vector{type}, type); auto func = builder.CreateFunc( INVALID_LOCATION, funcType, mangledName, std::move(rawMangledName), "", packageName); cache[std::move(mangledName)] = func; func->EnableAttr(Attribute::NO_REFLECT_INFO); // because it is in extend func->EnableAttr(Attribute::NO_DEBUG_INFO); func->EnableAttr(Attribute::COMPILER_ADD); func->EnableAttr(Attribute::OPERATOR); func->Set(Linkage::INTERNAL); BlockGroup* body = builder.CreateBlockGroup(*func); func->InitBody(*body); auto block = builder.CreateBlock(body); body->SetEntryBlock(block); auto retValue = builder.CreateExpression(INVALID_LOCATION, builder.GetType(type), type, block); block->AppendExpression(retValue); func->SetReturnValue(*retValue->GetResult()); if (isBinary) { auto p1 = builder.CreateParameter(type, INVALID_LOCATION, *func); auto p2 = builder.CreateParameter(type, INVALID_LOCATION, *func); auto add = builder.CreateExpression( INVALID_LOCATION, type, ToExprKind(name), p1, p2, ovf, block); block->AppendExpression(add); auto store = builder.CreateExpression( INVALID_LOCATION, builder.GetUnitTy(), add->GetResult(), retValue->GetResult(), block); block->AppendExpression(store); } else { auto p1 = builder.CreateParameter(type, INVALID_LOCATION, *func); CJC_ASSERT(name == "-"); auto add = builder.CreateExpression( INVALID_LOCATION, type, ExprKind::NEG, p1, ovf, block); block->AppendExpression(add); auto store = builder.CreateExpression( INVALID_LOCATION, builder.GetUnitTy(), add->GetResult(), retValue->GetResult(), block); block->AppendExpression(store); } auto exit = builder.CreateTerminator(INVALID_LOCATION, block); block->AppendExpression(exit); func->EnableAttr(Attribute::VIRTUAL); func->EnableAttr(Attribute::FINAL); return func; } void UpdateOperatorVTable::RewriteOneVtableEntry( ClassType& infType, CustomTypeDef& user, const VirtualFuncInfo& funcInfo, size_t index) { static std::vector SPLIT_OPERATOR_NAME_PREFIX = {"&", "~", "%"}; if (IsBuiltinOverflowOperator(user, funcInfo)) { // for builtin overflow operator, insert compiler generated functions into vtable auto& extDef = StaticCast(user); for (size_t j{0}; j < SPLIT_OPERATOR_NAME_PREFIX.size(); ++j) { auto name = SPLIT_OPERATOR_NAME_PREFIX[j] + funcInfo.srcCodeIdentifier; auto ovfFunc = GenerateBuiltinOverflowOperatorFunc(funcInfo.srcCodeIdentifier, OVF_STRATEGIES[j], extDef, funcInfo.typeInfo.sigType->GetNumOfParams() == 1); extDef.AddMethod(ovfFunc); if (j == SPLIT_OPERATOR_NAME_PREFIX.size() - 1) { // check again to prevent incorrect rewrite auto& vt = user.GetVTable().at(&infType)[index]; CJC_ASSERT(vt.srcCodeIdentifier == funcInfo.srcCodeIdentifier); CJC_ASSERT(vt.typeInfo.sigType == funcInfo.typeInfo.sigType); // reuse the vtable entry to keep the vector index user.UpdateVtableItem(infType, index, ovfFunc, extDef.GetExtendedType(), std::move(name)); } else { auto newFuncInfo = VirtualFuncInfo{std::move(name), ovfFunc, funcInfo.attr, funcInfo.typeInfo}; newFuncInfo.typeInfo.parentType = extDef.GetExtendedType(); newFuncInfo.typeInfo.originalType = ovfFunc->GetFuncType(); user.AddVtableItem(infType, std::move(newFuncInfo)); } } } else { // otherwise for normal overflow operator, repeat the user defined function in vtable for (size_t j{0}; j < SPLIT_OPERATOR_NAME_PREFIX.size(); ++j) { auto name = SPLIT_OPERATOR_NAME_PREFIX[j] + funcInfo.srcCodeIdentifier; if (j == SPLIT_OPERATOR_NAME_PREFIX.size() - 1) { // check again to prevent incorrect rewrite auto& vt = user.GetVTable().at(&infType)[index]; CJC_ASSERT(vt.srcCodeIdentifier == funcInfo.srcCodeIdentifier); CJC_ASSERT(vt.typeInfo.sigType == funcInfo.typeInfo.sigType); user.UpdateVtableItem(infType, index, funcInfo.instance, nullptr, std::move(name)); } else { user.AddVtableItem(infType, {std::move(name), funcInfo.instance, funcInfo.attr, funcInfo.typeInfo}); } } } } void UpdateOperatorVTable::RewriteVtableEntryRec( const ClassDef& inf, CustomTypeDef& user, const RewriteVtableInfo& info) { auto& vtable = user.GetVTable(); // use the checked vtable indices, rather than check them again // check subclass vtable is inaccurate for generic interface inherited by non generic class def. // e.g. interface B { operator func+(other: Int8): T } // this is possibly overflow operator // struct C <: B { public operator func+(_: Int8): C } // while this is not for (auto& pair : vtable) { if (pair.first->GetClassDef() != &inf) { continue; } for (auto i : info.ov) { // copy it, as funcInfo changes during rewrite auto funcInfo = pair.second[i]; RewriteOneVtableEntry(*pair.first, user, funcInfo, i); } } } void UpdateOperatorVTable::RewriteVtable() { for (auto& pair : interRewriteInfo) { auto& info = pair.second; auto inf = pair.first; auto it = vtableUsers.find(inf); if (it != vtableUsers.end()) { for (auto user : it->second) { RewriteVtableEntryRec(*inf, *user, info); } } } } void UpdateOperatorVTable::CollectVTableUsers() { for (auto def : package.GetAllCustomTypeDef()) { if (def->TestAttr(Attribute::SKIP_ANALYSIS)) { continue; } for (const auto& it : def->GetVTable()) { auto parentDef = it.first->GetClassDef(); auto resIt = vtableUsers.find(parentDef); if (resIt == vtableUsers.end()) { vtableUsers[parentDef].emplace_back(def); } else if (std::find(resIt->second.begin(), resIt->second.end(), def) == resIt->second.end()) { resIt->second.emplace_back(def); } } } } void UpdateOperatorVTable::Update() { CollectOverflowOperators(); CollectVTableUsers(); RewriteVtable(); } cangjie_compiler-1.0.7/src/CHIR/GenerateVTable/VTableGenerator.cpp000066400000000000000000000565721510705540100247220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/GenerateVTable/VTableGenerator.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/ExtendDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Utils.h" using namespace Cangjie; using namespace Cangjie::CHIR; namespace { // `key` is generic type param in def and def's extends // `value` is instantiated type args in current CustomType std::unordered_map GetInstMapFromDefAndExtends(const CustomType& type) { std::unordered_map replaceTable; auto def = type.GetCustomTypeDef(); // `def` may be generic decl, generic instantiated decl or just normal decl // only generic decl can generate replaceTable if (!def->TestAttr(Attribute::GENERIC)) { return replaceTable; } auto genericParams = def->GetGenericTypeParams(); auto instantiatedArgs = type.GetTypeArgs(); if (!genericParams.empty()) { CJC_ASSERT(genericParams.size() == instantiatedArgs.size()); } for (size_t i = 0; i < genericParams.size(); ++i) { // e.g. interface I {}; open class A <: I<(T, T)> {}; class B <: A {} // when `classType` is `A`, item in `replaceTable` is {A::T, Int32} replaceTable.emplace(genericParams[i], instantiatedArgs[i]); } for (auto exDef : def->GetExtends()) { auto genericParamsInExtendTy = exDef->GetExtendedType()->GetTypeArgs(); CJC_ASSERT(genericParamsInExtendTy.size() == instantiatedArgs.size()); for (size_t i = 0; i < genericParamsInExtendTy.size(); ++i) { if (auto genericTy = DynamicCast(genericParamsInExtendTy[i])) { replaceTable.emplace(genericTy, instantiatedArgs[i]); } } } return replaceTable; } void AddNewItemToVTable(ClassType& srcParent, VirtualFuncInfo& funcInfo, VTableType& vtable) { vtable[&srcParent].emplace_back(funcInfo); } bool FuncMayBeInVtable(const FuncBase& func) { if (func.IsConstructor() || func.IsFinalizer()) { return false; } if (func.GetFuncKind() == FuncKind::ANNOFACTORY_FUNC) { return false; } if (func.Get() != nullptr) { return false; } if (func.TestAttr(Attribute::GENERIC_INSTANTIATED)) { return false; } return func.TestAttr(Attribute::PUBLIC) || func.TestAttr(Attribute::PROTECTED) || (func.TestAttr(Attribute::STATIC) && !func.TestAttr(Attribute::PRIVATE)); } void UpdateInstanceAttr(VTableType& vtable) { // copy attr to instance for (auto vtableIt : vtable) { for (auto infoIt : vtableIt.second) { if (infoIt.instance == nullptr) { continue; } if (!infoIt.instance->TestAttr(Attribute::VIRTUAL)) { infoIt.instance->EnableAttr(Attribute::FINAL); } } } } void UpdateFuncInfo(VirtualFuncInfo& oldItem, const VirtualFuncInfo& newItem) { auto originalFuncType = oldItem.typeInfo.originalType; oldItem = newItem; oldItem.typeInfo.originalType = originalFuncType; } bool TypeIsMatched(const Type& type1, const Type& type2) { if (&type1 == &type2) { return true; } if (type1.IsGeneric() || type2.IsGeneric()) { return true; } if (!type1.IsSameTypeKind(type2)) { return false; } if (auto customType1 = DynamicCast(&type1)) { if (customType1->GetCustomTypeDef() != StaticCast(type2).GetCustomTypeDef()) { return false; } } auto type1Args = type1.GetTypeArgs(); auto type2Args = type2.GetTypeArgs(); if (type1Args.size() != type2Args.size()) { return false; } for (size_t i = 0; i < type1Args.size(); ++i) { if (!TypeIsMatched(*type1Args[i], *type2Args[i])) { return false; } } return true; } std::vector CollectBrotherDefs(const ExtendDef& curExtend, CHIRBuilder& builder) { auto extendDefs = curExtend.GetExtendedType()->GetExtends(&builder); CJC_ASSERT(!extendDefs.empty()); std::vector brotherDefs; for (auto def : extendDefs) { if (def != &curExtend && TypeIsMatched(*def->GetExtendedType(), *curExtend.GetExtendedType())) { brotherDefs.emplace_back(def); } } auto extendedTy = DynamicCast(curExtend.GetExtendedType()); if (extendedTy != nullptr) { brotherDefs.emplace_back(extendedTy->GetCustomTypeDef()); } return brotherDefs; } } VTableGenerator::VTableGenerator(CHIRBuilder& builder) : builder(builder) { } std::vector VTableGenerator::GetAllMethods(const Type& ty) { std::vector methods; // all methods belonging to some type, include methods in class/sturct/enum/interface/extend if (auto customTy = DynamicCast(&ty); customTy) { auto other = customTy->GetCustomTypeDef()->GetMethods(); methods.insert(methods.end(), other.begin(), other.end()); } for (auto extendDef : ty.GetExtends(&builder)) { auto m = extendDef->GetMethods(); methods.insert(methods.end(), m.begin(), m.end()); } return methods; } std::vector VTableGenerator::GetAllMethods(const CustomTypeDef& def) { std::vector allMethods; if (auto extendDef = DynamicCast(&def); extendDef) { if (const auto ty = extendDef->GetExtendedType(); ty) { allMethods = GetAllMethods(*ty); } } else { allMethods = GetAllMethods(*def.GetType()); } return allMethods; } void VTableGenerator::CollectCurDefMethodsMayBeInVtable(const CustomTypeDef& def, std::vector& publicFuncs) { for (auto func : GetAllMethods(def)) { if (FuncMayBeInVtable(*func)) { publicFuncs.emplace_back(func); } } } VirtualFuncInfo VTableGenerator::CreateVirtualFuncInfo(const AbstractMethodInfo& method, Type& originalParentType, const std::unordered_map& replaceTable) { auto originalFuncType = StaticCast(method.methodTy); auto originalParamTypes = originalFuncType->GetParamTypes(); if (!method.TestAttr(Attribute::STATIC)) { originalParamTypes.erase(originalParamTypes.begin()); } std::vector instParamTypes; for (auto type : originalParamTypes) { instParamTypes.emplace_back(ReplaceRawGenericArgType(*type, replaceTable, builder)); } auto sigType = builder.GetType(instParamTypes, builder.GetUnitTy()); auto instParentType = ReplaceRawGenericArgType(originalParentType, replaceTable, builder); auto instRetType = ReplaceRawGenericArgType(*originalFuncType->GetReturnType(), replaceTable, builder); auto typeInfo = VirtualFuncTypeInfo{ sigType, originalFuncType, instParentType, instRetType, method.methodGenericTypeParams}; return VirtualFuncInfo{method.methodName, nullptr, method.attributeInfo, typeInfo}; } VirtualFuncInfo VTableGenerator::CreateVirtualFuncInfo( FuncBase& method, Type& originalParentType, const std::unordered_map& replaceTable) { auto originalFuncType = StaticCast(method.GetType()); auto originalParamTypes = originalFuncType->GetParamTypes(); if (!method.TestAttr(Attribute::STATIC)) { originalParamTypes.erase(originalParamTypes.begin()); } std::vector instParamTypes; for (auto type : originalParamTypes) { instParamTypes.emplace_back(ReplaceRawGenericArgType(*type, replaceTable, builder)); } auto sigType = builder.GetType(instParamTypes, builder.GetUnitTy()); auto instParentType = ReplaceRawGenericArgType(originalParentType, replaceTable, builder); auto instRetType = ReplaceRawGenericArgType(*originalFuncType->GetReturnType(), replaceTable, builder); auto typeInfo = VirtualFuncTypeInfo{ sigType, originalFuncType, instParentType, instRetType, method.GetGenericTypeParams()}; return VirtualFuncInfo{method.GetSrcCodeIdentifier(), &method, method.GetAttributeInfo(), typeInfo}; } bool VTableGenerator::IsSigTypeMatched(const VirtualFuncInfo& curFuncInfo, const VirtualFuncInfo& funcInfoInVtable) { // static method can not override non-static method if (curFuncInfo.attr.TestAttr(Attribute::STATIC) != funcInfoInVtable.attr.TestAttr(Attribute::STATIC)) { return false; } // abstract method can not override implemented method if (curFuncInfo.instance == nullptr && funcInfoInVtable.instance != nullptr) { return false; } // func name not matched if (curFuncInfo.srcCodeIdentifier != funcInfoInVtable.srcCodeIdentifier) { return false; } auto paramTysInMethod = curFuncInfo.typeInfo.sigType->GetParamTypes(); auto paramTysInVtable = funcInfoInVtable.typeInfo.sigType->GetParamTypes(); // param size not matched if (paramTysInVtable.size() != paramTysInMethod.size()) { return false; } auto genericTyParamsInMethod = curFuncInfo.typeInfo.methodGenericTypeParams; auto genericTyParamsInVtable = funcInfoInVtable.typeInfo.methodGenericTypeParams; // generic type param size not matched if (genericTyParamsInMethod.size() != genericTyParamsInVtable.size()) { return false; } std::unordered_map replaceTable; for (size_t i = 0; i < genericTyParamsInVtable.size(); ++i) { replaceTable[genericTyParamsInMethod[i]] = genericTyParamsInVtable[i]; } bool typeMatch = true; // check param types for (size_t i = 0; i < paramTysInMethod.size(); ++i) { auto paramTyInMethod = paramTysInMethod[i]; if (paramTyInMethod->IsGeneric() && StaticCast(paramTyInMethod)->orphanFlag) { auto& upperBounds = StaticCast(paramTyInMethod)->GetUpperBounds(); CJC_ASSERT(upperBounds.size() == 1); paramTyInMethod = upperBounds[0]; } if (paramTysInVtable[i] != ReplaceRawGenericArgType(*paramTyInMethod, replaceTable, builder)) { typeMatch = false; break; } } return typeMatch; } bool VTableGenerator::VirtualFuncShouldAddToVTableInItsOwnParent(ClassType& ownParent, ClassType& alreadyIn) { std::vector inheritanceList; auto res = ownParent.GetCustomTypeDef()->GetType()->CalculateGenericTyMapping(ownParent); CJC_ASSERT(res.first); for (auto ty : ownParent.GetCustomTypeDef()->GetImplementedInterfaceTys()) { auto instTy = StaticCast(ReplaceRawGenericArgType(*ty, res.second, builder)); GetAllInstantiatedParentType(*instTy, builder, inheritanceList); } if (auto superClass = ownParent.GetSuperClassTy(&builder)) { GetAllInstantiatedParentType(*superClass, builder, inheritanceList); } return std::find(inheritanceList.begin(), inheritanceList.end(), &alreadyIn) == inheritanceList.end(); } bool VTableGenerator::UpdateVtable(VirtualFuncInfo& curFuncInfo, VTableType& vtable) { // update rules: // 1. method in sub type udpate method in parent type // 2. method in interface update method in brother interface // 3. method in class update method in brother interface // 4. method in interface can NOT update method in brother class bool maybeAddNewItemToVtable = true; for (auto& vtableIt : vtable) { for (auto& funcInfo : vtableIt.second) { if (IsSigTypeMatched(curFuncInfo, funcInfo)) { UpdateFuncInfo(funcInfo, curFuncInfo); // if a function declared in sub type updates its parent type's vtable, then don't need to // add new item to vtable // but if a function updates its brother type's vtable(rules 2 and 3), then maybe this function // will be added in vtable under its parent type /* e.g. interface I1 { func foo() {} } open class C1 { open public func foo() {} } class C2 <: C1 & I1 {} in class C2's vtable, interface I1 is brother type of class C1, method `C1::foo` overrides method `I::foo`, but `C1::foo` still need to be added under class C1 C2's vtable { I1: { foo: ()->Unit, C1::foo } --> `C1::foo` updated I1's vtable C1: { foo: ()->Unit, C1::foo } --> but still need to add it in C1's vtable } */ auto parentTy = DynamicCast(curFuncInfo.typeInfo.parentType); if (parentTy == nullptr) { // if current function is declared in struct or enum, it must be sub type maybeAddNewItemToVtable = false; } else { // maybe update many times // update one function from class's parent interface or class's brother interface, // but it can't be guaranteed which function is visited first, so `&=` is needed maybeAddNewItemToVtable &= VirtualFuncShouldAddToVTableInItsOwnParent(*parentTy, *vtableIt.first); } break; } } // must visit all src parent types, maybe one function update many virtual function in different src parent type } return maybeAddNewItemToVtable; } void VTableGenerator::MergeVtable(ClassType& instParentTy, VTableType& vtable) { auto replaceTable = GetInstMapFromDefAndExtends(instParentTy); auto parentDef = instParentTy.GetClassDef(); // not include abstract methods std::vector publicAndProtectedFuncs; CollectCurDefMethodsMayBeInVtable(*parentDef, publicAndProtectedFuncs); // update vtable // 1. visit all abstract methods in parent def for (auto abstractMethod : parentDef->GetAbstractMethods()) { if (abstractMethod.hasBody) { continue; } auto funcInfo = CreateVirtualFuncInfo(abstractMethod, *parentDef->GetType(), replaceTable); auto maybeAddNewItemToVtable = UpdateVtable(funcInfo, vtable); if (maybeAddNewItemToVtable) { AddNewItemToVTable(instParentTy, funcInfo, vtable); } } // 2. visit all public and protected non-abstract methods, including // a. static and non-static methods // b. methods in parent def and parent's extend def for (auto func : publicAndProtectedFuncs) { auto parentType = func->GetParentCustomTypeOrExtendedType(); CJC_NULLPTR_CHECK(parentType); auto funcInfo = CreateVirtualFuncInfo(*func, *parentType, replaceTable); auto maybeAddNewItemToVtable = UpdateVtable(funcInfo, vtable); if (maybeAddNewItemToVtable && IsVirtualFunction(*funcInfo.instance)) { AddNewItemToVTable(instParentTy, funcInfo, vtable); } } } void VTableGenerator::UpdateAbstractMethodWithImplementedMethod( VTableType& vtable, const ClassType& curParentTy, VirtualFuncInfo& abstractFuncInfo) { bool done = false; for (auto& vtableIt : vtable) { // must be from brother interface // Note: method declared in class also can override method in interface, // but this case has been handled in front step if (vtableIt.first->GetClassDef()->IsClass() || vtableIt.first == &curParentTy) { continue; } for (auto& funcInfo : vtableIt.second) { // skip abstract method, need use implemented method to override abstract method if (funcInfo.instance == nullptr) { continue; } if (IsSigTypeMatched(funcInfo, abstractFuncInfo)) { abstractFuncInfo = funcInfo; done = true; break; } } if (done) { break; } } } void VTableGenerator::UpdateAbstractMethodInVtable(VTableType& vtable) { for (auto& vtableIt : vtable) { for (auto& funcInfo : vtableIt.second) { // skip non-abstract method, only update abstract method if (funcInfo.instance != nullptr) { continue; } UpdateAbstractMethodWithImplementedMethod(vtable, *vtableIt.first, funcInfo); } } } // `key` is generic type param in def, def's parents and def's parents' extend // `value` is instantiated type args in current Type std::unordered_map VTableGenerator::GetInstMapFromDefIncludeParents( const CustomTypeDef& def, const Type& curType) { auto inheritanceList = def.GetSuperTypesRecusively(builder); /* e.g. interface I { func foo(a: T) {} } class A {} extend A <: I { func goo(a: U1) {} } if input `def` is `extend A` and `curType` is A, then we need to get correct `replaceTable` */ std::unordered_map replaceTable; // 1. collect table from def's parent type, in this case, def is `extend A` // its parent type is `I`, then replaceTable is {I::T -> U1} for (auto parent : inheritanceList) { replaceTable.merge(GetInstMapFromDefAndExtends(*parent)); } // 2. collect table from input def, then temp table is {U1 -> Int32} // maybe we can meet `extend A> {}`, and `curType` is A, then ignore this def, // so not need to check `res` auto [res, tmpTable] = def.GetType()->CalculateGenericTyMapping(curType); // 3. update replaceTable with temp table, from {I::T -> U1} to {I::T -> Int32} for (auto& it : replaceTable) { if (auto genericTy = DynamicCast(it.second)) { auto tmp = tmpTable.find(genericTy); if (tmp != tmpTable.end()) { it.second = tmp->second; } } } // 4. merge temp table to replaceTable, then result is {U1 -> Int32}, {I::T -> Int32} replaceTable.merge(tmpTable); return replaceTable; } void VTableGenerator::CollectMethodsFromAncestorInterfaceMayBeInVTable( const CustomTypeDef& curDef, std::vector& methods) { for (auto parent : curDef.GetImplementedInterfaceDefs()) { CollectCurDefMethodsMayBeInVtable(*parent, methods); CollectMethodsFromAncestorInterfaceMayBeInVTable(*parent, methods); } } std::vector VTableGenerator::CollectMethodsIncludeParentsMayBeInVtable(const CustomTypeDef& curDef) { std::vector methods; // scan methods from all ancestors CollectMethodsFromAncestorInterfaceMayBeInVTable(curDef, methods); if (auto clsDef = DynamicCast(&curDef)) { auto super = clsDef->GetSuperClassDef(); while (super != nullptr) { CollectCurDefMethodsMayBeInVtable(*super, methods); CollectMethodsFromAncestorInterfaceMayBeInVTable(*super, methods); super = super->GetSuperClassDef(); } } for (auto func : curDef.GetMethods()) { if (FuncMayBeInVtable(*func)) { methods.emplace_back(func); } } return methods; } std::vector VTableGenerator::CollectAllPublicAndProtectedMethods(const CustomTypeDef& curDef) { std::vector allMethods; std::unordered_map emptyTable; if (auto extendDef = DynamicCast(&curDef)) { auto brotherDefs = CollectBrotherDefs(*extendDef, builder); // get methods from brother extend's parent def and brother extend's def for (auto brotherDef : brotherDefs) { auto replaceTable = GetInstMapFromDefIncludeParents(*brotherDef, *extendDef->GetExtendedType()); auto publicFuncs = CollectMethodsIncludeParentsMayBeInVtable(*brotherDef); for (auto func : publicFuncs) { auto parentType = func->GetParentCustomTypeOrExtendedType(); CJC_NULLPTR_CHECK(parentType); auto funcInfo = CreateVirtualFuncInfo(*func, *parentType, replaceTable); allMethods.emplace_back(funcInfo); } } } else if (auto classDef = DynamicCast(&curDef)) { // get methods from current def for (auto aMethod : classDef->GetAbstractMethods()) { if (aMethod.hasBody) { continue; } if (aMethod.attributeInfo.TestAttr(Attribute::PUBLIC) || aMethod.attributeInfo.TestAttr(Attribute::PROTECTED)) { auto funcInfo = CreateVirtualFuncInfo(aMethod, *classDef->GetType(), emptyTable); allMethods.emplace_back(funcInfo); } } } // get methods from current def for (auto func : curDef.GetMethods()) { if (FuncMayBeInVtable(*func)) { auto parentType = func->GetParentCustomTypeOrExtendedType(); CJC_NULLPTR_CHECK(parentType); auto funcInfo = CreateVirtualFuncInfo(*func, *parentType, emptyTable); allMethods.emplace_back(funcInfo); } } return allMethods; } void VTableGenerator::GenerateVTable(CustomTypeDef& customTypeDef) { if (customTypeDef.TestAttr(Attribute::GENERIC_INSTANTIATED)) { return; } auto inheritanceList = customTypeDef.GetSuperTypesRecusively(builder); VTableType vtable; // 1. merge all parent vtable, from grand-parent to parent for (auto parent : inheritanceList) { MergeVtable(*parent, vtable); } // 2. use implemented method which declared in parent to override abstract method which declared in another parent /* e.g. interface A { func foo() {} } interface B { func foo(): Unit } class C <: A & B {} after step 1, class C's vtable is as follows: vtable { interface-A: [ foo: ()->Unit, A::foo ] interface-B: [ foo: ()->Unit, nullptr ] } we need to override abstract `foo` with `A::foo`, then it will be as follows: vtable { interface-A: [ foo: ()->Unit, A::foo ] interface-B: [ foo: ()->Unit, A::foo ] } */ UpdateAbstractMethodInVtable(vtable); // 3. visit all public and protected methods related with current def, including // a. static and non-static methods // b. abstract and non-abstract methods // c. methods in current def, current extended def and current extended parent def // not include methods declared in current parent def, they have been handled in step 1 auto publicAndProtectedMethods = CollectAllPublicAndProtectedMethods(customTypeDef); for (auto funcInfo : publicAndProtectedMethods) { auto maybeAddNewItemToVtable = UpdateVtable(funcInfo, vtable); if (!maybeAddNewItemToVtable) { continue; } if (funcInfo.instance == nullptr) { AddNewItemToVTable(*StaticCast(customTypeDef.GetType()), funcInfo, vtable); } else if (customTypeDef.IsClassLike() && IsVirtualFunction(*funcInfo.instance)) { auto srcParentType = StaticCast(funcInfo.instance->GetParentCustomTypeOrExtendedType()); AddNewItemToVTable(*srcParentType, funcInfo, vtable); } } UpdateInstanceAttr(vtable); customTypeDef.SetVTable(vtable); } cangjie_compiler-1.0.7/src/CHIR/GenerateVTable/WrapMutFunc.cpp000066400000000000000000000255361510705540100241050ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/GenerateVTable/WrapMutFunc.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Type/ExtendDef.h" #include "cangjie/CHIR/UserDefinedType.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Mangle/CHIRManglingUtils.h" using namespace Cangjie::CHIR; using namespace Cangjie; namespace { void GetAllInstantiatedParentType2(ClassType& cur, ClassType& targetParent, CHIRBuilder& builder, std::vector& parents, std::unordered_map& replaceTable, std::unordered_set& visited, bool& stop) { if (stop) { return; } if (std::find(visited.begin(), visited.end(), &cur) != visited.end()) { return; } if (&cur == &targetParent) { stop = true; } for (auto ex : cur.GetCustomTypeDef()->GetExtends()) { // maybe we can meet `extend A> {}`, and `curType` is A, then ignore this def, // so not need to check `res` auto [res, extendTable] = ex->GetExtendedType()->CalculateGenericTyMapping(cur); for (auto interface : ex->GetImplementedInterfaceTys()) { GetAllInstantiatedParentType2(*interface, targetParent, builder, parents, extendTable, visited, stop); if (!stop) { CJC_ASSERT(parents.back() == interface); parents.pop_back(); } } } for (auto interface : cur.GetImplementedInterfaceTys(&builder)) { GetAllInstantiatedParentType2(*interface, targetParent, builder, parents, replaceTable, visited, stop); if (!stop) { CJC_ASSERT(parents.back() == interface); parents.pop_back(); } } if (cur.GetSuperClassTy(&builder) != nullptr) { auto superClass = cur.GetSuperClassTy(&builder); GetAllInstantiatedParentType2(*superClass, targetParent, builder, parents, replaceTable, visited, stop); if (!stop) { CJC_ASSERT(parents.back() == superClass); parents.pop_back(); } } visited.emplace(&cur); parents.emplace_back(Cangjie::StaticCast(ReplaceRawGenericArgType(cur, replaceTable, builder))); } std::vector GetTargetInheritanceList(CustomTypeDef& curDef, ClassType& targetParent, CHIRBuilder& builder) { std::vector inheritanceList; std::unordered_set visited; std::unordered_map emptyTable; bool stop = false; for (auto interface : curDef.GetImplementedInterfaceTys()) { GetAllInstantiatedParentType2(*interface, targetParent, builder, inheritanceList, emptyTable, visited, stop); if (!stop) { CJC_ASSERT(inheritanceList.back() == interface); inheritanceList.pop_back(); } } if (curDef.IsClassLike()) { auto superClass = StaticCast(&curDef)->GetSuperClassTy(); if (superClass != nullptr) { GetAllInstantiatedParentType2( *superClass, targetParent, builder, inheritanceList, emptyTable, visited, stop); if (!stop) { CJC_ASSERT(inheritanceList.back() == superClass); inheritanceList.pop_back(); } } } CJC_ASSERT(stop); return inheritanceList; } std::unordered_map CollectReplaceTableFromAllParents( CustomTypeDef& curDef, ClassType& srcClassTy, CHIRBuilder& builder) { std::unordered_map replaceTable; auto parentTypes = GetTargetInheritanceList(curDef, srcClassTy, builder); for (auto parentType : parentTypes) { auto instTypeArgs = parentType->GetTypeArgs(); auto genericTypeArgs = parentType->GetCustomTypeDef()->GetGenericTypeParams(); for (size_t i = 0; i < genericTypeArgs.size(); ++i) { replaceTable.emplace(genericTypeArgs[i], instTypeArgs[i]); } } return replaceTable; } /** * @brief Try to get instantiated sub type. * * @param genericSubType generic sub type. * @param instParentType instantiated parent type * @param builder CHIR builder * @return return genericSubType when * 1. `genericSubType` is not generic related * 2. `genericSubType` equals to `instParentType` * 3. `genericSubType` and `instParentType` don't have parent-child relationship * return instantiated sub type when * 1. `genericSubType` and `instParentType` are same CustomTypeDef, * but one is generic related, the other is instantiated type * 2. `genericSubType` and `instParentType` have parent-child relationship, * and `genericSubType` is generic related sub type, `instParentType` is instantiated parent type */ Type* GetInstSubType(Type& genericSubType, const ClassType& instParentType, CHIRBuilder& builder) { if (!genericSubType.IsGenericRelated()) { return &genericSubType; } if (&genericSubType == &instParentType) { return &genericSubType; } auto [res, replaceTable] = genericSubType.CalculateGenericTyMapping(instParentType); if (!res) { for (auto p : genericSubType.GetSuperTypesRecusively(builder)) { std::tie(res, replaceTable) = p->CalculateGenericTyMapping(instParentType); if (res) { break; } } } CJC_ASSERT(res); return ReplaceRawGenericArgType(genericSubType, replaceTable, builder); } } // namespace void WrapMutFunc::CreateMutFuncWrapper(FuncBase* rawFunc, CustomTypeDef& curDef, ClassType& srcClassTy) { // create the wrapper func auto replaceTable = CollectReplaceTableFromAllParents(curDef, srcClassTy, builder); auto instFuncTy = StaticCast(ReplaceRawGenericArgType(*rawFunc->GetFuncType(), replaceTable, builder)); auto wrapperParamsTy = instFuncTy->GetParamTypes(); auto parentDefType = curDef.IsExtend() ? StaticCast(curDef).GetExtendedType() : curDef.GetType(); wrapperParamsTy[0] = builder.GetType(parentDefType); auto retTy = instFuncTy->GetReturnType(); auto wrapperFuncTy = builder.GetType(wrapperParamsTy, retTy); auto funcIdentifier = CHIRMangling::GenerateVirtualFuncMangleName(rawFunc, curDef, &srcClassTy, false); auto pkgName = curDef.GetPackageName(); bool isImported = curDef.TestAttr(Attribute::IMPORTED); FuncBase* funcBase = nullptr; if (isImported) { funcBase = builder.CreateImportedVarOrFunc(wrapperFuncTy, funcIdentifier, "", "", pkgName); } else { funcBase = builder.CreateFunc(INVALID_LOCATION, wrapperFuncTy, funcIdentifier, "", "", pkgName); } wrapperFuncs.emplace(funcIdentifier, funcBase); CJC_NULLPTR_CHECK(funcBase); funcBase->Set(rawFunc); funcBase->AppendAttributeInfo(rawFunc->GetAttributeInfo()); funcBase->DisableAttr(Attribute::VIRTUAL); funcBase->EnableAttr(Attribute::NO_REFLECT_INFO); curDef.AddMethod(funcBase); if (isImported) { return; } auto func = DynamicCast(funcBase); CJC_NULLPTR_CHECK(func); // create the func body BlockGroup* body = builder.CreateBlockGroup(*func); func->InitBody(*body); std::vector args; for (auto paramTy : wrapperParamsTy) { args.emplace_back(builder.CreateParameter(paramTy, INVALID_LOCATION, *func)); } auto entry = builder.CreateBlock(body); body->SetEntryBlock(entry); auto ret = Cangjie::CHIR::CreateAndAppendExpression(builder, builder.GetType(retTy), retTy, entry); func->SetReturnValue(*ret->GetResult()); auto rawFuncFirstArgType = rawFunc->GetFuncType()->GetParamTypes()[0]->StripAllRefs(); auto firstArgType = GetInstSubType(*rawFuncFirstArgType, srcClassTy, builder); if (!firstArgType->IsValueType() || rawFunc->TestAttr(Attribute::MUT)) { firstArgType = builder.GetType(firstArgType); } args[0] = Cangjie::CHIR::TypeCastOrBoxIfNeeded(*args[0], *firstArgType, builder, *entry, INVALID_LOCATION); auto apply = Cangjie::CHIR::CreateAndAppendExpression(builder, retTy, rawFunc, FuncCallContext{ .args = args, .thisType = curDef.GetType()}, entry); Cangjie::CHIR::CreateAndAppendExpression( builder, builder.GetUnitTy(), apply->GetResult(), func->GetReturnValue(), entry); auto tempThis = Cangjie::CHIR::TypeCastOrBoxIfNeeded(*args[0], *wrapperParamsTy[0], builder, *entry, INVALID_LOCATION); auto load = Cangjie::CHIR::CreateAndAppendExpression(builder, parentDefType, tempThis, entry)->GetResult(); auto structMemberTypes = StaticCast(parentDefType)->GetInstantiatedMemberTys(builder); for (size_t i = 0; i < structMemberTypes.size(); ++i) { auto path = std::vector{i}; auto field = Cangjie::CHIR::CreateAndAppendExpression(builder, structMemberTypes[i], load, path, entry) ->GetResult(); Cangjie::CHIR::CreateAndAppendExpression( builder, builder.GetUnitTy(), field, func->GetParam(0), path, entry); } entry->AppendExpression(builder.CreateTerminator(entry)); } void WrapMutFunc::Run(CustomTypeDef& customTypeDef) { Type* structTy = nullptr; if (customTypeDef.GetCustomKind() == CustomDefKind::TYPE_EXTEND && StaticCast(customTypeDef).GetExtendedType()->IsStruct()) { structTy = StaticCast(customTypeDef).GetExtendedType(); } else if (customTypeDef.GetCustomKind() == CustomDefKind::TYPE_STRUCT && StaticCast(customTypeDef).GetImplementedInterfacesNum() > 0) { structTy = customTypeDef.GetType(); } if (!structTy) { return; } for (auto& [srcTy, infos] : customTypeDef.GetVTable()) { for (size_t i = 0; i < infos.size(); ++i) { if (!infos[i].instance) { continue; } CJC_NULLPTR_CHECK(infos[i].instance); auto rawFunc = infos[i].instance; while (auto base = rawFunc->Get()) { rawFunc = base; } if (!rawFunc->TestAttr(Attribute::MUT) || rawFunc->GetParentCustomTypeDef() == &customTypeDef) { continue; } if (auto ex = DynamicCast(&customTypeDef); ex && ex->GetExtendedCustomTypeDef() == rawFunc->GetParentCustomTypeDef()) { continue; } CreateMutFuncWrapper(rawFunc, customTypeDef, *StaticCast(srcTy)); } } } WrapMutFunc::WrapMutFunc(CHIRBuilder& b) : builder(b) { } std::unordered_map&& WrapMutFunc::GetWrappers() { return std::move(wrapperFuncs); } cangjie_compiler-1.0.7/src/CHIR/GenerateVTable/WrapVirtualFunc.cpp000066400000000000000000000472451510705540100247670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/GenerateVTable/WrapVirtualFunc.h" #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/AST2CHIR/Utils.h" #include "cangjie/Mangle/CHIRManglingUtils.h" using namespace Cangjie; using namespace Cangjie::CHIR; namespace { bool FuncTypeHasOuterGenericType(const FuncType& funcType, const std::vector& funcGenericTypeParams) { auto unorderedGenericTypeParams = std::unordered_set(funcGenericTypeParams.begin(), funcGenericTypeParams.end()); bool hasOuterGenericType = false; auto visitor = [&unorderedGenericTypeParams, &hasOuterGenericType](const Type& type) { if (hasOuterGenericType) { return true; } if (type.IsGeneric() && unorderedGenericTypeParams.find(&type) == unorderedGenericTypeParams.end()) { hasOuterGenericType = true; } return true; }; funcType.VisitTypeRecursively(visitor); return hasOuterGenericType; } bool FuncTypeMatch(const FuncType& parentFuncType, const FuncType& curFuncType) { std::function typeIsMatched = [&typeIsMatched](const Type& type1, const Type& type2) { if (type1.GetTypeKind() != type2.GetTypeKind()) { return false; } if (type1.IsGeneric() && type2.IsGeneric()) { return true; } auto typeArgs1 = type1.GetTypeArgs(); auto typeArgs2 = type2.GetTypeArgs(); if (typeArgs1.size() != typeArgs2.size()) { return false; } for (size_t i = 0; i < typeArgs1.size(); ++i) { if (!typeIsMatched(*typeArgs1[i], *typeArgs2[i])) { return false; } } return true; }; auto paramTypeIsMatched = [&typeIsMatched](const Type& type1, const Type& type2) { if ((type1.IsGeneric() || type1.IsRef()) && (type2.IsGeneric() || type2.IsRef())) { return true; } else { return typeIsMatched(type1, type2); } }; auto parentFuncParamTypes = parentFuncType.GetParamTypes(); auto curFuncParamTypes = curFuncType.GetParamTypes(); CJC_ASSERT(parentFuncParamTypes.size() == curFuncParamTypes.size()); for (size_t i = 0; i < parentFuncParamTypes.size(); ++i) { if (!paramTypeIsMatched(*parentFuncParamTypes[i], *curFuncParamTypes[i])) { return false; } } auto returnTypeIsMatched = [&typeIsMatched](const Type& type1, const Type& type2) { if (type1.IsGeneric() && type2.IsGeneric()) { return true; } if (type1.IsRef() && type2.IsRef()) { return true; } return typeIsMatched(type1, type2); }; return returnTypeIsMatched(*parentFuncType.GetReturnType(), *curFuncType.GetReturnType()); } bool FuncIsOverride(const FuncBase& curFunc, const Type& selfTy) { return curFunc.GetParentCustomTypeDef()->GetType() == &selfTy; } bool JudgeIfNeedVirtualWrapper(const VirtualFuncInfo& parentFuncInfo, const FuncBase& virtualFunc, const Type& selfTy) { /** if struct and enum inherit interface, for non-static function, wrapper func is needed * because `this` in struct is value type, and in interface is ref type * but for static function, we need to follow rules below */ if (!selfTy.IsClassOrArray() && !virtualFunc.TestAttr(Attribute::STATIC)) { return true; } /* first, we need to clear a defination `generic-related`, if we say a function is generic-related, that means there is generic type in function type, and this generic type must be from current function, not from its parent class e.g. class A { func foo1(a: T) {} // foo1 is NOT generic-related, because T is from class A func foo2(a: A) {} // foo2 is NOT generic-related, because T is from class A func foo3() {} // foo3 is NOT generic-related, because there isn't generic type func goo1(b: U) {} // goo1 is generic-related, because U is from func goo1 func goo2(): A {} // goo2 is generic-related, because U is from func goo2 } there are some rules to create or NOT create wrapper function: 1. virtual function is NOT generic-related, and its parent CustomTypeDef doesn't have generic type param wrapper function doesn't need to be created 2. virtual function is NOT generic-related, but its parent CustomTypeDef has generic type param 2.1 there is NOT override function in sub CustomTypeDef wrapper function needs to be created 2.2 there is override function in sub CustomTypeDef wrapper function doesn't need to be created 3. virtual function is generic-related 3.1 there is NOT override function in sub CustomTypeDef wrapper function needs to be created 3.2 there is override function in sub CustomTypeDef 3.2.1 if the function type in sub CustomTypeDef's virtual function is matched the function type in parent CustomTypeDef's virtual function, wrapper function isn't needed 3.2.2 if the function type in sub CustomTypeDef's virtual function is mismatched the function type in parent CustomTypeDef's virtual function, wrapper function is needed there are some rules to explain what is function type MATCHED: 1. for param type generic type and ref type are matched, including generic T and generic U is matched, class A and class B is matched, generic T and class A is matched for other types, CHIR pointer must be same 2. for return type class A and class B are matched, generic T and generic U are matched, class A and generic T are mismatched for other types, CHIR pointer must be same */ auto parentDef = virtualFunc.GetParentCustomTypeDef(); CJC_NULLPTR_CHECK(parentDef); bool parentDefHasGenericType = !parentDef->GetGenericTypeParams().empty(); auto parentFuncType = parentFuncInfo.typeInfo.originalType; auto parentFuncTypeParams = parentFuncInfo.typeInfo.methodGenericTypeParams; bool funcHasOuterGenericType = FuncTypeHasOuterGenericType(*parentFuncType, parentFuncTypeParams); if (!funcHasOuterGenericType && !parentDefHasGenericType) { return false; } else if (!funcHasOuterGenericType && parentDefHasGenericType) { return !FuncIsOverride(virtualFunc, selfTy); } else { if (!FuncIsOverride(virtualFunc, selfTy)) { return true; } else { return !FuncTypeMatch(*parentFuncType, *virtualFunc.GetFuncType()); } } } // maybe we can not deserialize virutal wrapper function, because it's not in source code void TryDeleteVirtuallWrapperFunc(CustomTypeDef& customTypeDef, FuncBase& finalFunc, CHIRBuilder& builder) { if (!customTypeDef.TestAttr(Attribute::PLATFORM)) { return; } if (!finalFunc.TestAttr(Attribute::FINAL)) { return; } auto allMethods = customTypeDef.GetMethods(); for (auto method : allMethods) { if (!method->TestAttr(Attribute::DESERIALIZED) || !method->TestAttr(Attribute::VIRTUAL)) { continue; } if (finalFunc.Get()->GetSrcCodeIdentifier() != method->Get()->GetSrcCodeIdentifier()) { continue; } if (finalFunc.GetFuncType()->IsEqualOrSubTypeOf(*method->GetFuncType(), builder)) { method->FuncBase::DestroySelf(); return; } } } } // namespace WrapVirtualFunc::WrapVirtualFunc(CHIRBuilder& builder, const CompilationCache& increCachedInfo, const IncreKind incrementalKind, const bool targetIsWin) : builder(builder), increCachedInfo(increCachedInfo), incrementalKind(incrementalKind), targetIsWin(targetIsWin) { } void WrapVirtualFunc::CheckAndWrap(CustomTypeDef& customTypeDef) { auto selfTy = customTypeDef.GetType(); for (auto& [parentTy, infos] : customTypeDef.GetVTable()) { if (parentTy == selfTy) { continue; } auto parentDef = parentTy->GetCustomTypeDef(); auto parentVTableIt = parentDef->GetVTable().find(StaticCast(parentDef->GetType())); CJC_ASSERT(parentVTableIt != parentDef->GetVTable().end()); CJC_ASSERT(parentVTableIt->second.size() == infos.size()); for (size_t i = 0; i < infos.size(); ++i) { auto& info = infos[i]; if (!info.instance) { continue; } auto& parentInfo = parentVTableIt->second[i]; auto wrapper = CreateVirtualWrapperIfNeeded(info, parentInfo, *selfTy, customTypeDef, *parentTy); if (incrementalKind != IncreKind::INVALID) { HandleVirtualFuncWrapperForIncrCompilation(wrapper, *info.instance); } if (!wrapper) { continue; } customTypeDef.UpdateVtableItem(*parentTy, i, wrapper, selfTy); } } } void WrapVirtualFunc::HandleVirtualFuncWrapperForIncrCompilation(const FuncBase* wrapper, const FuncBase& curFunc) { CJC_ASSERT(!curFunc.GetSrcCodeIdentifier().empty()); const auto& cachedVirDep = increCachedInfo.virtualFuncDep; const std::string& rawMangleName = curFunc.GetRawMangledName(); // compiler add func doesn't have rawMangle if (curFunc.TestAttr(Attribute::COMPILER_ADD) && rawMangleName.empty()) { return; } if (rawMangleName.empty()) { CJC_ABORT(); return; } if (wrapper) { curVirtFuncWrapDep.emplace(rawMangleName, wrapper->GetIdentifierWithoutPrefix()); } if (incrementalKind != IncreKind::INCR || cachedVirDep.empty()) { return; } if (auto it = cachedVirDep.find(rawMangleName); it != cachedVirDep.end()) { if (wrapper) { if (it->second != wrapper->GetIdentifierWithoutPrefix()) { delVirtFuncWrapForIncr[rawMangleName] = it->second; } } else { delVirtFuncWrapForIncr[rawMangleName] = it->second; } } } WrapVirtualFunc::WrapperFuncGenericTable WrapVirtualFunc::GetReplaceTableForVirtualFunc( const ClassType& parentTy, const std::string& funcIdentifier, const VirtualFuncInfo& parentFuncInfo) { auto genericParentTypeArgs = parentTy.GetCustomTypeDef()->GetGenericTypeParams(); auto instParentTypeArgs = parentTy.GetTypeArgs(); if (!genericParentTypeArgs.empty()) { CJC_ASSERT(genericParentTypeArgs.size() == instParentTypeArgs.size()); } WrapperFuncGenericTable resultTable; size_t newGenericIdx = 0; for (size_t i = 0; i < genericParentTypeArgs.size(); ++i) { auto instArgTy = instParentTypeArgs[i]; if (instArgTy->IsGeneric() || instArgTy->IsClassOrArray()) { resultTable.replaceTable.emplace(genericParentTypeArgs[i], instArgTy); } else { auto srcIdentifier = 'T' + std::to_string(newGenericIdx++); auto tyIdentifier = funcIdentifier + '_' + srcIdentifier; auto funcGenericParam = builder.GetType(tyIdentifier, srcIdentifier); funcGenericParam->orphanFlag = true; funcGenericParam->SetUpperBounds(std::vector{instArgTy}); funcGenericParam->skipCheck = true; resultTable.replaceTable.emplace(genericParentTypeArgs[i], funcGenericParam); resultTable.inverseReplaceTable.emplace(funcGenericParam, instArgTy); } } auto& parentMethodGenericTys = parentFuncInfo.typeInfo.methodGenericTypeParams; for (auto& parentMethodGenericTy : parentMethodGenericTys) { auto srcIdentifier = "fT" + std::to_string(newGenericIdx++); auto tyIdentifier = funcIdentifier + '_' + srcIdentifier; auto funcGenericParam = builder.GetType(tyIdentifier, srcIdentifier); resultTable.funcGenericTypeParams.emplace_back(funcGenericParam); resultTable.replaceTable.emplace(parentMethodGenericTy, funcGenericParam); } CJC_ASSERT(resultTable.funcGenericTypeParams.size() == parentMethodGenericTys.size()); for (size_t i = 0; i < resultTable.funcGenericTypeParams.size(); ++i) { std::vector newUpperBounds; for (auto ty : parentMethodGenericTys[i]->GetUpperBounds()) { newUpperBounds.emplace_back(ReplaceRawGenericArgType(*ty, resultTable.replaceTable, builder)); } resultTable.funcGenericTypeParams[i]->SetUpperBounds(newUpperBounds); } return resultTable; } void WrapVirtualFunc::CreateVirtualWrapperFunc(Func& func, FuncType& wrapperTy, const VirtualFuncInfo& funcInfo, Type& selfTy, WrapVirtualFunc::WrapperFuncGenericTable& genericTable) { auto rawFunc = funcInfo.instance; auto wrapperRetTy = wrapperTy.GetReturnType(); BlockGroup* body = builder.CreateBlockGroup(func); func.InitBody(*body); std::vector args; for (auto ty : wrapperTy.GetParamTypes()) { args.emplace_back(builder.CreateParameter(ty, INVALID_LOCATION, func)); } auto entry = builder.CreateBlock(body); body->SetEntryBlock(entry); auto ret = CreateAndAppendExpression(builder, builder.GetType(wrapperRetTy), wrapperRetTy, entry); func.SetReturnValue(*ret->GetResult()); Type* instParentType = funcInfo.typeInfo.parentType; if (instParentType->IsClassOrArray() || (instParentType->IsStruct() && rawFunc->TestAttr(Attribute::MUT))) { instParentType = builder.GetType(instParentType); } auto instTy = StaticCast(ReplaceRawGenericArgType(wrapperTy, genericTable.inverseReplaceTable, builder)); auto paramInstTy = instTy->GetParamTypes(); if (!rawFunc->TestAttr(Attribute::STATIC)) { paramInstTy[0] = instParentType; } Type* applyRetTy = rawFunc->GetFuncType()->GetReturnType(); if (applyRetTy->IsGenericRelated()) { applyRetTy = instTy->GetReturnType(); } CJC_ASSERT(args.size() == paramInstTy.size()); for (size_t i = 0; i < paramInstTy.size(); ++i) { args[i] = TypeCastOrBoxIfNeeded(*args[i], *paramInstTy[i], builder, *entry, INVALID_LOCATION); } std::vector instArgTypes; instArgTypes.reserve(genericTable.funcGenericTypeParams.size()); for (auto& funcGenericParam : genericTable.funcGenericTypeParams) { instArgTypes.emplace_back(funcGenericParam); } auto thisInstTy = &selfTy; if (thisInstTy->IsClassOrArray()) { thisInstTy = builder.GetType(thisInstTy); } auto apply = CreateAndAppendExpression(builder, applyRetTy, rawFunc, FuncCallContext{ .args = args, .instTypeArgs = instArgTypes, .thisType = thisInstTy}, entry); auto res = TypeCastOrBoxIfNeeded(*apply->GetResult(), *wrapperRetTy, builder, *entry, INVALID_LOCATION); CreateAndAppendExpression(builder, builder.GetUnitTy(), res, func.GetReturnValue(), entry); entry->AppendExpression(builder.CreateTerminator(entry)); } FuncType* WrapVirtualFunc::GetWrapperFuncType(FuncType& parentFuncTyWithoutThisArg, Type& selfTy, const std::unordered_map& replaceTable, bool isStatic) { auto erasedFuncTy = StaticCast(ReplaceRawGenericArgType(parentFuncTyWithoutThisArg, replaceTable, builder)); auto wrapperParamTy = erasedFuncTy->GetParamTypes(); auto wrapperRetTy = erasedFuncTy->GetReturnType(); if (!isStatic) { Type* thisArgTy = builder.GetType(&selfTy); // builder.GetType(funcInfo.typeInfo.parentType); if (!selfTy.IsClassOrArray()) { thisArgTy = builder.GetType(builder.GetAnyTy()); } wrapperParamTy.insert(wrapperParamTy.begin(), thisArgTy); } return builder.GetType(wrapperParamTy, wrapperRetTy); } FuncType* WrapVirtualFunc::RemoveThisArg(FuncType* funcTy) { auto paramTys = funcTy->GetParamTypes(); auto retTy = funcTy->GetReturnType(); return builder.GetType(std::vector(paramTys.begin() + 1, paramTys.end()), retTy); } // change this function to class method, to much args in functions called by this function FuncBase* WrapVirtualFunc::CreateVirtualWrapperIfNeeded(const VirtualFuncInfo& funcInfo, const VirtualFuncInfo& parentFuncInfo, Type& selfTy, CustomTypeDef& customTypeDef, const ClassType& parentTy) { auto curFunc = funcInfo.instance; // 1. Judge if need virtual wrapper if (!JudgeIfNeedVirtualWrapper(parentFuncInfo, *curFunc, selfTy)) { return nullptr; } auto isStatic = curFunc->TestAttr(Attribute::STATIC); auto parentFuncTy = parentFuncInfo.typeInfo.originalType; auto parentFuncTyWithoutThisArg = !isStatic ? RemoveThisArg(parentFuncTy) : parentFuncTy; auto funcIdentifier = CHIRMangling::GenerateVirtualFuncMangleName(curFunc, customTypeDef, &parentTy, true); // ensure every method is wrapped exactly once. This occurs in split operator, where three vtable entries use // the same function if (auto ca = wrapperCache.find(funcIdentifier); ca != wrapperCache.cend()) { return ca->second; } // 2. Collect replace map for generic type params auto genericTable = GetReplaceTableForVirtualFunc(parentTy, funcIdentifier, parentFuncInfo); // 3. Get func type auto wrapperTy = GetWrapperFuncType(*parentFuncTyWithoutThisArg, selfTy, genericTable.replaceTable, isStatic); // 4. Create the function auto funcSrcIdentifier = ""; auto rawMangledName = ""; auto packageName = customTypeDef.GetPackageName(); bool isImported = customTypeDef.TestAttr(Attribute::IMPORTED) && parentTy.GetCustomTypeDef()->TestAttr(Attribute::IMPORTED); bool parentIsFromExtend = ParentDefIsFromExtend(customTypeDef, *parentTy.GetClassDef()); FuncBase* funcBase = nullptr; if (isImported && !parentIsFromExtend) { funcBase = builder.CreateImportedVarOrFunc(wrapperTy, funcIdentifier, funcSrcIdentifier, rawMangledName, packageName, genericTable.funcGenericTypeParams); } else { funcBase = builder.CreateFunc(INVALID_LOCATION, wrapperTy, funcIdentifier, funcSrcIdentifier, "", packageName, genericTable.funcGenericTypeParams); } CJC_NULLPTR_CHECK(funcBase); funcBase->Set(curFunc); funcBase->AppendAttributeInfo(curFunc->GetAttributeInfo()); funcBase->EnableAttr(Attribute::NO_REFLECT_INFO); customTypeDef.AddMethod(funcBase); // For cjmp, try delete virutal wrapper function when final func gererated. TryDeleteVirtuallWrapperFunc(customTypeDef, *funcBase, builder); funcBase->Set(Linkage::EXTERNAL); if (parentIsFromExtend) { // weak_odr is invalid in windows, we have to set internal if (targetIsWin) { funcBase->Set(Linkage::INTERNAL); } else { funcBase->Set(Linkage::WEAK_ODR); } } wrapperCache[std::move(funcIdentifier)] = funcBase; if (isImported && !parentIsFromExtend) { return funcBase; } // 5. Create the function body if the raw function is not imported auto func = StaticCast(funcBase); CreateVirtualWrapperFunc(*func, *wrapperTy, funcInfo, selfTy, genericTable); return func; } VirtualWrapperDepMap&& WrapVirtualFunc::GetCurVirtFuncWrapDep() { return std::move(curVirtFuncWrapDep); } VirtualWrapperDepMap&& WrapVirtualFunc::GetDelVirtFuncWrapForIncr() { return std::move(delVirtFuncWrapForIncr); } cangjie_compiler-1.0.7/src/CHIR/IRChecker.cpp000066400000000000000000003451051510705540100206360ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/IRChecker.h" #include #include #include #include #include #include #include #include #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/ToStringUtils.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Value.h" #include "cangjie/CHIR/Visitor/Visitor.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/TaskQueue.h" #include "cangjie/Utils/ConstantsUtils.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie::CHIR; namespace { bool IsExpectedType(const Type& expectedTy, const Type& curTy) { if (&expectedTy == &curTy) { return true; } if (curTy.IsNothing()) { return true; } auto isRefAny = [](const Type& t1) { return t1.IsRef() && Cangjie::StaticCast(&t1)->GetBaseType()->IsAny(); }; if (isRefAny(expectedTy) || isRefAny(curTy)) { // Any type should be checked return true; } return false; } bool IsExpectedType(const Type& ty, const Type::TypeKind& kind) { if (ty.GetTypeKind() == kind) { return true; } if (ty.IsNothing()) { return true; } return false; } bool CheckFuncArgType(const Type& t, bool isCFunc, bool isMutThisType = false) { if (isMutThisType) { if (t.IsRef() && !Cangjie::StaticCast(&t)->GetBaseType()->IsRef()) { return true; } return false; } if (t.IsValueType() || t.IsFunc() || t.IsGeneric()) { // value_type/func/closure/generic pass by value return true; } if (t.IsRef()) { if (auto baseType = Cangjie::StaticCast(&t)->GetBaseType(); baseType->IsClassOrArray() || baseType->IsBox() || isCFunc) { // class and array are treated as reference type // in c function, value with reference is valid with inout intrinsic return true; } } return false; } bool IsAbstractCustomDef(const CustomTypeDef& def) { using namespace Cangjie; static auto isInterfaceOrAbstractClass = [](const CustomTypeDef& def) { return def.IsClassLike() && (StaticCast(&def)->IsInterface() || def.TestAttr(Attribute::ABSTRACT)); }; if (isInterfaceOrAbstractClass(def)) { return true; } if (def.IsExtend()) { auto extendedDef = StaticCast(&def)->GetExtendedCustomTypeDef(); // all builtin defs are irrelevant in this case return extendedDef != nullptr && isInterfaceOrAbstractClass(*extendedDef); } return false; } bool CheckIfGenericTypeInSet(const Type& type, const std::set& genericTypes) { if (type.IsBox()) { return true; } if (type.IsGeneric()) { if (Cangjie::StaticCast(&type)->skipCheck == true) { return true; } if (genericTypes.count(&type) == 0) { return false; } } for (auto it : type.GetTypeArgs()) { bool ret = CheckIfGenericTypeInSet(*it, genericTypes); if (!ret) { return false; } } return true; } class IRChecker { public: /** @brief Entry function of the Well-formedness Checker */ static bool Check(const Package& root, std::ostream& outStream, const Cangjie::GlobalOptions& options, CHIRBuilder& builder, const ToCHIR::Phase& phase) { bool ret = true; curCheckPhase = phase; auto irChecker = IRChecker(outStream, options, builder); ret = ParallelCheck(irChecker, &IRChecker::CheckFunc, root.GetGlobalFuncs()) && ret; ret = ParallelCheck(irChecker, &IRChecker::CheckGlobalValue, root.GetGlobalVars()) && ret; ret = ParallelCheck(irChecker, &IRChecker::CheckImportedVarAndFuncs, root.GetImportedVarAndFuncs()) && ret; ret = ParallelCheck(irChecker, &IRChecker::CheckStruct, root.GetImportedStructs()) && ret; ret = ParallelCheck(irChecker, &IRChecker::CheckClass, root.GetImportedClasses()) && ret; ret = ParallelCheck(irChecker, &IRChecker::CheckEnum, root.GetImportedEnums()) && ret; ret = ParallelCheck(irChecker, &IRChecker::CheckStruct, root.GetStructs()) && ret; ret = ParallelCheck(irChecker, &IRChecker::CheckClass, root.GetClasses()) && ret; ret = ParallelCheck(irChecker, &IRChecker::CheckEnum, root.GetEnums()) && ret; ret = ParallelCheck(irChecker, &IRChecker::CheckExtend, root.GetExtends()) && ret; if (root.GetPackageInitFunc() == nullptr) { irChecker.Errorln("package " + root.GetName() + " need a global init func."); ret = false; } if (!ret) { switch (phase) { case ToCHIR::Phase::RAW: irChecker.Errorln("after translation."); break; case ToCHIR::Phase::PLUGIN: irChecker.Errorln("after plugin."); break; case ToCHIR::Phase::ANALYSIS_FOR_CJLINT: irChecker.Errorln("after analysis for cjlint."); break; case ToCHIR::Phase::OPT: irChecker.Errorln("after opt."); break; } } return ret; } static ToCHIR::Phase curCheckPhase; private: std::ostream& out; const Cangjie::GlobalOptions& opts; CHIRBuilder& builder; explicit IRChecker(std::ostream& outStream, const Cangjie::GlobalOptions& opts, CHIRBuilder& builder) : out(outStream), opts(opts), builder(builder) {}; template static bool ParallelCheck(IRChecker& irChecker, F check, const std::vector& items) { size_t threadNum = irChecker.opts.GetJobs(); Cangjie::Utils::TaskQueue taskQueue(threadNum); // Place for holding the results. std::vector> results; for (auto item : items) { results.emplace_back( taskQueue.AddTask([check, &irChecker, item]() { return (irChecker.*check)(*item); })); } taskQueue.RunAndWaitForAllTasksCompleted(); bool res = true; for (auto& result : results) { res = result.get() && res; } return res; } std::unordered_set identifiers; std::mutex checkIdentMutex; void WarningInFunc(const Value* curFunc, const std::string& info) const { CJC_NULLPTR_CHECK(curFunc); Warningln("in function " + curFunc->GetIdentifier() + ", " + info); } void Errorln(const std::string& info) const { out << "chir checker error: " + info + "\n"; } void Warningln(const std::string& info) const { out << "chir checker warning: " + info + "\n"; } void ErrorInFunc(const Value* curFunc, const std::string& info) const { CJC_NULLPTR_CHECK(curFunc); Errorln("in function " + curFunc->GetIdentifier() + ", " + info); } void TypeCheckError(const Value& value, const std::string& expectedTy) const { auto err = "value " + value.GetIdentifier() + " has type " + value.GetType()->ToString() + " but type " + expectedTy + " is expected."; ErrorInFunc(GetTopLevelFunc(value), err); } void TypeCheckError(const Expression& expr, const Value& input, const std::string& expectedTy) const { std::string location = ""; if (auto result = expr.GetResult(); result != nullptr) { location = result->GetIdentifier(); } else { location = expr.GetExprKindName(); } auto err = "value " + input.GetIdentifier() + " used in " + location + " has type " + input.GetType()->ToString() + " but type " + expectedTy + " is expected."; ErrorInFunc(expr.GetTopLevelFunc(), err); } void TypeCheckError(const Expression& expr, size_t index, const Value& child, const std::string& expectedTy) const { std::string location = ""; if (auto result = expr.GetResult(); result != nullptr) { location = result->GetIdentifier(); } else { location = expr.GetExprKindName(); } auto err = "in " + location + ": the " + std::to_string(index) + "-th element (" + child.GetIdentifier() + ") has type " + child.GetType()->ToString() + " but type " + expectedTy + " is expected."; ErrorInFunc(expr.GetTopLevelFunc(), err); } bool MemberFuncCheckError( const Value& func, const std::string& expectedTy, const std::string& location = "first parameter type") const { auto err = "func " + func.GetIdentifier() + " has type " + func.GetType()->ToString() + ", but " + expectedTy + " is expected in " + location + "."; Errorln(err); return false; } bool CheckIdentifier(const std::string& identifier) { std::unique_lock lock(checkIdentMutex); if (identifiers.find(identifier) != identifiers.end()) { Errorln("Duplicated identifier `" + identifier + "` found."); return false; } identifiers.insert(identifier); return true; } // globalValue mangledName can not be the same as any other node. bool CheckGlobalValue(const GlobalVar& value) { auto ret = CheckIdentifier(value.GetIdentifier()); ret = CheckValueReferenceType(value) && ret; bool noInitializer = value.GetInitFunc() == nullptr && value.GetInitializer() == nullptr; if (noInitializer) { if (value.TestAttr(Attribute::COMMON)) { // common can have no initalizer only when compiling common part. return ret; } Errorln("GlobalVar " + value.GetIdentifier() + " should have either initializer or initFunc"); ret = false; } return ret; } bool CheckImportedVarAndFuncs(const ImportedValue& value) { auto ret = CheckValueReferenceType(value); if (value.GetType()->IsFunc() && Cangjie::StaticCast(value.GetType())->IsCFunc()) { return ret; } ret = CheckIdentifier(value.GetIdentifier()) && ret; return ret; } bool CheckDeclaredParent(const Value& val, const CustomTypeDef& expectedParent) const { if (auto var = Cangjie::DynamicCast(&val)) { CJC_ASSERT(var->TestAttr(Attribute::STATIC)); auto realParent = var->GetParentCustomTypeDef(); if (realParent != &expectedParent) { auto err = "static member var " + var->GetIdentifier() + " in " + expectedParent.GetIdentifier(); if (realParent == nullptr) { err += " doesn't have declared parent."; } else { err += " has wrong declared parent: " + realParent->GetIdentifier(); } Errorln(err); return false; } } else if (auto func = Cangjie::DynamicCast(&val)) { auto realParent = func->GetParentCustomTypeDef(); if (realParent != &expectedParent) { auto err = "member func " + func->GetIdentifier() + " in " + expectedParent.GetIdentifier(); if (realParent == nullptr) { err += " doesn't have declared parent."; } else { err += " has wrong declared parent: " + realParent->GetIdentifier(); } Errorln(err); return false; } } else { auto err = "value " + val.GetIdentifier() + " in " + expectedParent.GetIdentifier() + ", shouldn't check declared parent."; Errorln(err); return false; } return true; } bool CheckCustomTypeDefPackageName(const CustomTypeDef& def) const { if (def.GetGenericDecl() && def.GetGenericDecl()->GetPackageName() != def.GetPackageName()) { std::string msg = "customDef package name error, current def " + def.GetIdentifier() + ", current package name is " + def.GetPackageName() + ", expected package name is " + def.GetGenericDecl()->GetPackageName(); Errorln(msg); return false; } return true; } bool CheckCustomTypeDefCommonRules(const CustomTypeDef& def) const { bool ret = true; ret = CheckCustomTypeDefPackageName(def); for (auto func : def.GetMethods()) { ret = CheckDeclaredParent(*func, def) && ret; } for (auto var : def.GetStaticMemberVars()) { ret = CheckValueReferenceType(*var) && ret; ret = CheckDeclaredParent(*var, def) && ret; } for (auto var : def.GetAllInstanceVars()) { ret = CheckMemberVarReferenceType(var, def.GetIdentifier()) && ret; } return ret; } void DumpVTableError(const CustomTypeDef& leafDef, const CustomTypeDef& rootDef, const std::string& msg) const { std::stringstream ss; ss << msg << ", derived and base vtables are described below:\n"; ss << leafDef.ToString() << "\n"; ss << rootDef.ToString() << "\n"; Errorln(ss.str()); } void DumpVTableErrorFuncInfo(const CustomTypeDef& customDef, const std::string& funcName, const std::string& funcTypeName, const std::string& wrongInfo) const { auto err = CustomTypeKindToString(customDef) + " " + customDef.GetIdentifier() + " has error in vtable, api name: " + funcName + ", api type: " + funcTypeName + ", msg: " + wrongInfo + "."; Errorln(err); } bool CheckImportedCustomDef(const CustomTypeDef& customDef) const { if (!customDef.TestAttr(Attribute::IMPORTED)) { CJC_ABORT(); return false; } for (auto& thisVtableIter : customDef.GetVTable()) { auto vtableKey = thisVtableIter.first; for (size_t funcId = 0; funcId < thisVtableIter.second.size(); ++funcId) { if (thisVtableIter.second[funcId].instance) { bool imported = thisVtableIter.second[funcId].instance->TestAttr(Attribute::IMPORTED); bool vImported = thisVtableIter.second[funcId].attr.TestAttr(Attribute::IMPORTED); if (vImported != imported) { DumpVTableError(customDef, *vtableKey->GetCustomTypeDef(), "vtable import error"); return false; } if (!thisVtableIter.second[funcId].instance->GetParentCustomTypeDef()) { std::string msg = "vtable instance parent error " + thisVtableIter.second[funcId].instance->GetIdentifier(); DumpVTableError(customDef, *vtableKey->GetCustomTypeDef(), msg); return false; } if (auto f = Cangjie::DynamicCast(thisVtableIter.second[funcId].instance); f && !f->GetBody()) { std::string msg = "vtable instance func body error " + thisVtableIter.second[funcId].instance->GetIdentifier(); DumpVTableError(customDef, *vtableKey->GetCustomTypeDef(), msg); return false; } } } } return true; } bool CheckVTable(const CustomTypeDef& customDef) { if (customDef.TestAttr(Attribute::IMPORTED)) { return CheckImportedCustomDef(customDef); } for (auto& thisVtableIter : customDef.GetVTable()) { auto vtableKey = thisVtableIter.first; // only interface and class can be inherited auto& parentVTable = vtableKey->GetClassDef()->GetVTable(); auto parentKey = vtableKey->GetCustomTypeDef()->GetType(); auto parentVTableIter = parentVTable.find(Cangjie::StaticCast(parentKey)); if (parentVTableIter == parentVTable.end()) { DumpVTableError(customDef, *vtableKey->GetCustomTypeDef(), "vtable match fail"); return false; } if (parentVTableIter->second.size() != thisVtableIter.second.size()) { DumpVTableError(customDef, *vtableKey->GetCustomTypeDef(), "wrong vtable size"); return false; } for (size_t funcId = 0; funcId < thisVtableIter.second.size(); ++funcId) { if (auto f = Cangjie::DynamicCast(thisVtableIter.second[funcId].instance); f) { if (!f->GetBody()) { std::string msg = "vtable instance func body error " + thisVtableIter.second[funcId].instance->GetIdentifier(); DumpVTableError(customDef, *vtableKey->GetCustomTypeDef(), msg); return false; } } auto thisFuncInfo = thisVtableIter.second[funcId].typeInfo.originalType; auto parentFuncInfo = parentVTableIter->second[funcId].typeInfo.originalType; auto& funcName = thisVtableIter.second[funcId].srcCodeIdentifier; auto funcType = thisVtableIter.second[funcId].typeInfo.originalType->ToString(); // interface and extend can have unimplemented api if (!IsAbstractCustomDef(customDef) && thisVtableIter.second[funcId].instance == nullptr) { DumpVTableErrorFuncInfo(customDef, funcName, funcType, "unimplemented api in vtable"); return false; } if (thisFuncInfo->GetParamTypes().size() != parentFuncInfo->GetParamTypes().size()) { DumpVTableErrorFuncInfo(customDef, funcName, funcType, "wrong arg size"); return false; } if (thisVtableIter.second[funcId].instance == nullptr) { // abstract function do not check its params continue; } size_t offset = 0; if (!thisVtableIter.second[funcId].instance->TestAttr(Attribute::STATIC)) { // check this type if function is marked non-static if (!CheckTypeMatchInVTable( *thisFuncInfo->GetParamType(0), *parentFuncInfo->GetParamType(0), true, true)) { DumpVTableErrorFuncInfo(customDef, funcName, funcType, "wrong arg type"); return false; } offset = 1; } for (size_t paramId = offset; paramId < thisFuncInfo->GetParamTypes().size(); ++paramId) { if (!CheckTypeMatchInVTable( *thisFuncInfo->GetParamType(paramId), *parentFuncInfo->GetParamType(paramId), true)) { DumpVTableErrorFuncInfo(customDef, funcName, funcType, "wrong arg type"); return false; } } } } return true; } bool CheckIfGenericMemberValid(const CustomTypeDef& def) { bool ret = true; auto genericArgs = def.GetGenericTypeParams(); std::set genericTypes{genericArgs.begin(), genericArgs.end()}; for (auto it : def.GetDirectInstanceVars()) { if (!it.type->IsGenericRelated()) { continue; } bool res = CheckIfGenericTypeInSet(*it.type, genericTypes); if (!res) { Errorln("Generic Type of member " + it.name + ": " + it.type->ToString() + " is not found in " + def.GetIdentifier()); } ret = ret && res; } for (auto it : def.GetStaticMemberVars()) { if (!it->GetType()->IsGenericRelated()) { continue; } bool res = CheckIfGenericTypeInSet(*it->GetType(), genericTypes); if (!res) { Errorln("Generic Type of member " + it->GetSrcCodeIdentifier() + ": " + it->GetType()->ToString() + " is not found in " + def.GetIdentifier()); } ret = ret && res; } return ret; } // struct mangledName can not be the same as other node. // struct mut function or constructor first input must be Struct&. // struct not mut function and not construcor first input must be Struct. // struct constructor return type must be Unit bool CheckStruct(const StructDef& structT) { bool ret = CheckIdentifier(structT.GetIdentifier()); for (auto& func : structT.GetMethods()) { if (func->TestAttr(Attribute::STATIC)) { continue; } std::string expectTy = "Struct-" + structT.GetIdentifierWithoutPrefix(); if (func->IsConstructor() || func->TestAttr(Attribute::MUT) || func->IsInstanceVarInit()) { expectTy = expectTy + "&"; } CJC_ASSERT(func->GetType()->IsFunc()); auto funcTy = Cangjie::StaticCast(func->GetType()); if (funcTy->GetParamTypes().size() == 0) { ret = MemberFuncCheckError(*func, expectTy) && ret; continue; } Type* baseTy = funcTy->GetParamTypes()[0]; if (IsConstructor(*func) || func->TestAttr(Attribute::MUT) || func->IsInstanceVarInit()) { if (!baseTy->IsRef()) { ret = MemberFuncCheckError(*func, expectTy) && ret; continue; } baseTy = Cangjie::StaticCast(baseTy)->GetBaseType(); } if (!func->Get()) { if (!baseTy->IsStruct()) { ret = MemberFuncCheckError(*func, expectTy) && ret; continue; } if (baseTy != structT.GetType()) { ret = MemberFuncCheckError(*func, expectTy) && ret; continue; } } if (func->IsConstructor() && !funcTy->GetReturnType()->IsVoid()) { ret = MemberFuncCheckError(*func, "Void", "return type") && ret; } } ret = CheckCustomTypeDefCommonRules(structT) && ret; ret = CheckVTable(structT) && ret; ret = CheckIfGenericMemberValid(structT) && ret; if (!ret) { structT.Dump(); } return ret; } bool CheckAbstractParamInfo(const ClassDef& classT) const { for (auto& method : classT.GetAbstractMethods()) { if (Cangjie::StaticCast(method.methodTy)->GetParamTypes().size() != method.paramInfos.size()) { return false; } for (size_t i = 0; i < method.paramInfos.size(); ++i) { if (method.paramInfos[i].type != Cangjie::StaticCast(method.methodTy)->GetParamType(i)) { return false; } } } return true; } bool CheckTypeMatchInVTable(const Type& leaf, const Type& root, bool isFirst, bool isThisType = false) const { // this type match if both are class refernce types or types are same // other parameters match if types are same auto isRef = [](const Type& t) { return (t.IsRef() && Cangjie::StaticCast(&t)->GetBaseType()->IsClass()) || t.IsGeneric(); }; if ((root.IsGeneric() && isRef(leaf)) || (leaf.IsGeneric() && isRef(root))) { return true; } if (&leaf == &root) { return true; } // leaf: Class-CA&, root: Class-CA& if (!isFirst && root.IsGeneric()) { return true; } if (isThisType && leaf.StripAllRefs()->IsClass() && root.StripAllRefs()->IsClass()) { return true; } if (leaf.GetTypeKind() != root.GetTypeKind()) { return false; } if ((leaf.IsClass() || leaf.IsStruct() || leaf.IsEnum()) && Cangjie::StaticCast(&leaf)->GetCustomTypeDef() != Cangjie::StaticCast(&root)->GetCustomTypeDef()) { return false; } auto t1ArgTys = leaf.GetTypeArgs(); auto t2ArgTys = root.GetTypeArgs(); if (t1ArgTys.size() != t2ArgTys.size()) { return false; } bool res = true; for (size_t i = 0; i < t1ArgTys.size(); ++i) { res = res & CheckTypeMatchInVTable(*t1ArgTys[i], *t2ArgTys[i], false, isThisType); } return res; } // class mangledName can not be same with other node. // class member function first input must be Class& // class construcor return type must be Unit // class vtable can not has abstract function bool CheckClass(const ClassDef& classT) { bool ret = CheckIdentifier(classT.GetIdentifier()); for (auto& func : classT.GetMethods()) { if (func->TestAttr(Attribute::STATIC)) { continue; } std::string expectTy = "Class-" + classT.GetIdentifierWithoutPrefix() + "&"; CJC_ASSERT(func->GetType()->IsFunc()); auto funcTy = Cangjie::StaticCast(func->GetType()); if (funcTy->GetParamTypes().size() == 0) { ret = MemberFuncCheckError(*func, expectTy) && ret; continue; } Type* param0Ty = funcTy->GetParamTypes()[0]; if (!param0Ty->IsRef()) { ret = MemberFuncCheckError(*func, expectTy) && ret; continue; } auto param0BaseTy = Cangjie::StaticCast(param0Ty)->GetBaseType(); if (!param0BaseTy->IsClass()) { ret = MemberFuncCheckError(*func, expectTy) && ret; continue; } if (param0BaseTy != classT.GetType()) { ret = MemberFuncCheckError(*func, expectTy) && ret; continue; } if (func->IsConstructor()) { if (func->TestAttr(Attribute::STATIC) && !funcTy->GetReturnType()->IsUnit()) { ret = MemberFuncCheckError(*func, "Unit", "return type") && ret; } else if (!funcTy->GetReturnType()->IsVoid()) { ret = MemberFuncCheckError(*func, "Void", "return type") && ret; } } if (func->IsFinalizer() && !funcTy->GetReturnType()->IsVoid()) { ret = MemberFuncCheckError(*func, "Void", "return type") && ret; } } for (auto var : classT.GetStaticMemberVars()) { ret = CheckValueReferenceType(*var) && ret; } for (auto var : classT.GetAllInstanceVars()) { ret = CheckMemberVarReferenceType(var, classT.GetIdentifier()) && ret; } ret = CheckCustomTypeDefCommonRules(classT) && ret; ret = CheckVTable(classT) && ret; ret = CheckIfGenericMemberValid(classT) && ret; if (classT.IsInterface()) { ret = ret && CheckInterface(classT); } if (!ret) { classT.Dump(); } return ret; } // member func(include instance member and static member) in interface should be abstract. bool CheckInterface(const ClassDef& classT) const { bool ret = true; // skip check the anno factory func in interface. for (auto& method : classT.GetAbstractMethods()) { if (!method.TestAttr(Attribute::ABSTRACT)) { auto err = "func: " + method.methodName + " in Interface:" + classT.GetIdentifier() + "should has attribute: ABSTRACT"; Errorln(err); ret = false; } } return ret; } // enum mangledName can not be same with other node. // enum member function first input must be Enum bool CheckEnum(const EnumDef& enumT) { bool ret = CheckIdentifier(enumT.GetIdentifier()); for (auto& func : enumT.GetMethods()) { if (func->TestAttr(Attribute::STATIC)) { continue; } std::string expectTy = "Enum-" + enumT.GetIdentifierWithoutPrefix(); CJC_ASSERT(func->GetType()->IsFunc()); auto funcTy = Cangjie::StaticCast(func->GetType()); if (funcTy->GetParamTypes().size() == 0) { ret = MemberFuncCheckError(*func, expectTy) && ret; continue; } if (!func->Get()) { Type* baseTy = funcTy->GetParamTypes()[0]; if (!baseTy->IsEnum()) { ret = MemberFuncCheckError(*func, expectTy) && ret; continue; } if (baseTy != enumT.GetType()) { ret = MemberFuncCheckError(*func, expectTy) && ret; continue; } } } ret = CheckCustomTypeDefCommonRules(enumT) && ret; ret = CheckVTable(enumT) && ret; ret = CheckIfGenericMemberValid(enumT) && ret; if (!ret) { enumT.Dump(); } return ret; } bool CheckExtend(const ExtendDef& extendT) { // can't check identifier, maybe repeat, need Sema to fix // CHIR will create some extend defs in the end, it's a hack for codegen, will be removed later if (extendT.TestAttr(Attribute::COMPILER_ADD)) { return true; } bool ret = CheckVTable(extendT); ret = CheckIfGenericMemberValid(extendT) && ret; if (!ret) { extendT.Dump(); } return ret; } // 1. type of value semantics, its reference level is one at most // e.g. `int64` and `int64&` are ok, `int64&&` is wrong type // 2. type of ref semantics, its reference level is two at most // e.g. `class`, `class&` and `class&&` are ok, `class&&&` is wrong bool CheckRefType(const Type& srcType, const std::string& extraMsg) const { if (!srcType.IsRef()) { return true; } // the first-level ref type auto baseType = Cangjie::StaticCast(srcType).GetBaseType(); if (!baseType->IsRef()) { return true; } // the second-level ref type baseType = Cangjie::StaticCast(baseType)->GetBaseType(); if (baseType->IsRef()) { std::string err = srcType.ToString() + " has more than two level reference, " + extraMsg; Errorln(err); return false; } if (!baseType->IsClassOrArray() && !baseType->IsBox()) { std::string err = srcType.ToString() + " has two level reference, " + extraMsg + ", but base type isn't class or array."; Errorln(err); return false; } return true; } bool CheckMemberVarReferenceType(const MemberVarInfo& memberVar, const std::string& extraMsg) const { CJC_NULLPTR_CHECK(memberVar.type); CJC_ASSERT(!memberVar.name.empty()); CJC_ASSERT(!extraMsg.empty()); return CheckRefType(*memberVar.type, "in member " + memberVar.name + " of " + extraMsg); } bool CheckValueReferenceType(const Value& value) const { CJC_NULLPTR_CHECK(value.GetType()); CJC_ASSERT(!value.GetIdentifier().empty()); return CheckRefType(*value.GetType(), "in value " + value.GetIdentifier()); } bool CheckReferenceTypeInFunc(const Func& func) const { auto ret = CheckValueReferenceType(func); for (auto param : func.GetParams()) { ret = CheckValueReferenceType(*param) && ret; } Visitor::Visit(*func.GetBody(), [this, &ret](Expression& expr) { if (!expr.IsTerminator()) { ret = CheckValueReferenceType(*expr.GetResult()) && ret; } return VisitResult::CONTINUE; }); return ret; } bool CheckLocalId(const BlockGroup& blockGroup, std::unordered_set& idSet) const { const std::vector& blocks = blockGroup.GetBlocks(); for (const Block* block : blocks) { const std::vector& expressions = block->GetExpressions(); for (const Expression* expr : expressions) { if (!expr->GetResult()) { continue; } const std::string& id = expr->GetResult()->GetIdentifier(); if (id.empty()) { CJC_NULLPTR_CHECK(block->GetTopLevelFunc()); Errorln("The result of expression " + expr->ToString() + " in the func " + block->GetTopLevelFunc()->GetIdentifier() + " does not have identifier."); return false; } if (!idSet.insert(id).second) { CJC_NULLPTR_CHECK(block->GetTopLevelFunc()); Errorln(block->GetTopLevelFunc()->GetIdentifier() + " has duplicate id:" + id); return false; } } } return true; } bool CheckApplyExtraTypes(const std::vector& instantiateArgs, const Type* thisType, const std::set& visibleGenericTypes, const Expression& expr) const { bool ret = true; auto errInfoSuffix = "generic type is unknown in expression " + expr.GetResult()->ToString() + ", of function " + expr.GetTopLevelFunc()->GetIdentifier(); for (size_t i = 0; i < instantiateArgs.size(); ++i) { if (!CheckIfGenericTypeInSet(*instantiateArgs[i], visibleGenericTypes)) { auto errInfo = "In instantiated type args, " + std::to_string(i) + "-th arg's " + errInfoSuffix; Errorln(errInfo); ret = false; } } if (thisType != nullptr) { if (!CheckIfGenericTypeInSet(*thisType, visibleGenericTypes)) { auto errInfo = "Instantiated This type's " + errInfoSuffix; Errorln(errInfo); ret = false; } } return ret; } bool CheckGenericTypeValidInFunc(BlockGroup& body, const std::set& genericArgs) const { bool ret = true; auto genericTypes = std::set(genericArgs.begin(), genericArgs.end()); auto func = body.GetTopLevelFunc(); CJC_NULLPTR_CHECK(func); if (func->GetFuncKind() == FuncKind::DEFAULT_PARAMETER_FUNC) { // need check default parameter func return ret; } if (func->GetParentCustomTypeDef() != nullptr) { auto def = func->GetParentCustomTypeDef(); for (auto it : def->GetGenericTypeParams()) { genericTypes.insert(it); } } // Check func body Visitor::Visit(body, [this, &ret, &genericTypes](Expression& expr) { if (expr.GetResult() == nullptr) { return VisitResult::CONTINUE; } if (expr.GetExprKind() == ExprKind::LAMBDA) { auto& lambda = Cangjie::StaticCast(expr); auto genericTypesWithLamdba = std::set(lambda.GetGenericTypeParams().begin(), lambda.GetGenericTypeParams().end()); genericTypesWithLamdba.insert(genericTypes.begin(), genericTypes.end()); if (!CheckIfGenericTypeInSet(*lambda.GetResult()->GetType(), genericTypesWithLamdba)) { ret = false; Errorln("Generic Type of labmda " + lambda.GetIdentifier() + " is not found in function " + lambda.GetTopLevelFunc()->GetIdentifier()); } // exprs in Lambda body will be check in 'CheckLambda' return VisitResult::SKIP; } if (!CheckIfGenericTypeInSet(*expr.GetResult()->GetType(), genericTypes)) { ret = false; Errorln("Generic Type of variable " + expr.GetResult()->ToString() + " is not found in function " + expr.GetTopLevelFunc()->GetIdentifier()); } if (expr.GetExprKind() == Cangjie::CHIR::ExprKind::APPLY) { auto& apply = Cangjie::StaticCast(expr); auto instantiatedTypeArgs = apply.GetInstantiatedTypeArgs(); if (!CheckApplyExtraTypes(instantiatedTypeArgs, apply.GetThisType(), genericTypes, expr)) { ret = false; } } else if (expr.GetExprKind() == Cangjie::CHIR::ExprKind::APPLY_WITH_EXCEPTION) { auto& apply = Cangjie::StaticCast(expr); auto instantiatedTypeArgs = apply.GetInstantiatedTypeArgs(); if (!CheckApplyExtraTypes(instantiatedTypeArgs, apply.GetThisType(), genericTypes, expr)) { ret = false; } } return VisitResult::CONTINUE; }); // Check Params const auto& params = GetFuncParams(body); for (auto param : params) { if (!param->GetType()->IsGenericRelated()) { continue; } bool res = CheckIfGenericTypeInSet(*param->GetType(), genericTypes); if (!res) { Errorln("Generic Type of params " + param->ToString() + " is not found in function " + func->GetIdentifier()); } ret = ret && res; } return ret; } // func mangledName can not be same with other node. bool CheckFunc(const Func& func) { if (func.TestAttr(Attribute::SKIP_ANALYSIS)) { return true; // Nothing to visit } auto ret = CheckIdentifier(func.GetIdentifier()); if (!func.GetBody()) { auto err = "func " + func.GetIdentifier() + " doesn't have body"; Errorln(err); ret = false; } if (!func.GetFuncType()) { auto err = "func " + func.GetIdentifier() + " doesn't have type"; Errorln(err); ret = false; } ret = CheckFuncParams(func.GetParams(), *func.GetFuncType(), func) && ret; std::set genericTys = { func.GetGenericTypeParams().begin(), func.GetGenericTypeParams().end()}; if (func.GetBody()) { ret = CheckFuncBody(*func.GetBody(), genericTys) && ret; } ret = CheckFuncRetValue(func, *func.GetFuncType()->GetReturnType(), func.GetReturnValue()) && ret; ret = CheckReferenceTypeInFunc(func) && ret; ret = CheckGenericTypeValidInFunc(*func.GetBody(), genericTys) && ret; if (func.IsGVInit() && !func.GetFuncType()->GetReturnType()->IsVoid()) { auto err = "func " + func.GetIdentifier() + " has type " + func.GetType()->ToString() + ", but Void is expected"; Errorln(err); ret = false; } if (func.TestAttr(Attribute::ABSTRACT)) { auto err = "func " + func.GetIdentifier() + " should not has attribute: ABSTRACT"; Errorln(err); ret = false; } if (!ret) { func.Dump(); } return ret; } bool CheckFuncParams( const std::vector& params, const FuncType& funcType, const Func& parentFunc) const { std::string paramsStr = "("; for (size_t loop = 0; loop < params.size(); loop++) { if (loop > 0) { paramsStr += ", "; } paramsStr += params[loop]->GetType()->ToString(); } paramsStr += ")"; // check paramTypes equal to funcBody param types auto paramTys = funcType.GetParamTypes(); if (paramTys.size() != params.size()) { auto paramSize = "param size is " + std::to_string(params.size()); auto funcParamSize = "param size is " + std::to_string(paramTys.size()); auto errInfo = paramSize + " in param type " + paramsStr + "but " + funcParamSize + " in funcType: " + funcType.ToString() + "."; ErrorInFunc(&parentFunc, errInfo); return false; } for (size_t i = 0; i < paramTys.size(); i++) { if (paramTys[i] != params[i]->GetType()) { auto errInfo = std::to_string(i) + "-th param in params type " + paramsStr + " mismatch funcType: " + funcType.ToString() + "."; ErrorInFunc(&parentFunc, errInfo); return false; } } return true; } // funcBody parameters and func input must have same size and same type. // funcBody blocks must have more than one exit or raise // funcBody return type must be same with funcBody block exit type. bool CheckFuncBody(BlockGroup& body, std::set& genericTypes, bool isGlobalBody = true) { bool ret = true; std::unordered_set idSet; if (body.TestAttr(Attribute::SKIP_ANALYSIS)) { return true; } Visitor::Visit(body, [this, &ret, &idSet](BlockGroup& blockGroup) { ret = CheckBlockGroup(blockGroup) && ret; ret = CheckLocalId(blockGroup, idSet) && ret; return VisitResult::CONTINUE; }); Visitor::Visit(body, [this, &ret](Block& block) { ret = CheckBlock(block) && ret; return VisitResult::CONTINUE; }); // if control flow is right and current is not the body of the local lambda, // check expr operand use-defined access. if (ret && isGlobalBody) { std::vector values; const auto& params = GetFuncParams(body); for (auto param : params) { values.emplace_back(param); } ret = ExprOperandCheck(body, values) && ret; } Visitor::Visit(body, [this, &ret, &genericTypes](Expression& expr) { ret = CheckExpression(expr, genericTypes) && ret; return VisitResult::CONTINUE; }); return ret; } bool CheckFuncRetValue(const Func& func, const Type& expTy, const Value* retValue) const { if (retValue == nullptr) { if (!expTy.IsUnit() && !expTy.IsNothing() && !expTy.IsVoid()) { Errorln(func.GetIdentifier() + " return type Unit is expected."); return false; } } else { if (!retValue->GetType()->IsRef()) { TypeCheckError(*retValue, expTy.ToString() + "&"); return false; } auto baseTy = Cangjie::StaticCast(retValue->GetType())->GetBaseType(); if (!IsExpectedType(expTy, *baseTy)) { TypeCheckError(*retValue, expTy.ToString() + "&"); return false; } } return true; } // block group must have block and entry block. bool CheckBlockGroup(const BlockGroup& blockGroup) const { auto curFunc = blockGroup.GetTopLevelFunc(); CJC_NULLPTR_CHECK(curFunc); std::string location = curFunc->GetIdentifier(); if (!blockGroup.GetUsers().empty()) { auto result = blockGroup.GetUsers()[0]->GetResult(); CJC_NULLPTR_CHECK(result); location = result->GetIdentifier(); } auto blocks = blockGroup.GetBlocks(); if (blocks.size() == 0) { ErrorInFunc(blockGroup.GetTopLevelFunc(), "block group in " + location + " has no block."); return false; } else if (blockGroup.GetEntryBlock() == nullptr) { ErrorInFunc(blockGroup.GetTopLevelFunc(), "block group in " + location + " has no entry block."); return false; } bool hasReturn = false; for (auto& block : blockGroup.GetBlocks()) { auto term = block->GetTerminator(); if (term == nullptr) { continue; } if (term->GetExprKind() != ExprKind::EXIT && term->GetExprKind() != ExprKind::RAISE_EXCEPTION) { continue; } hasReturn = true; } if (!hasReturn) { // Report warning now, to distinguish more specific scenarios in future. WarningInFunc(blockGroup.GetTopLevelFunc(), "block group has no exit or raise terminator."); return true; } return true; } bool CheckTerminator(const Block& block) const { // skip dead blocks if (!block.IsEntry() && block.GetPredecessors().empty()) { return true; } return block.GetTerminator() && block.GetTerminator()->IsTerminator(); } bool CheckSuccessors(const Block& block) const { if (!block.GetTerminator()) { // skip dead blocks return true; } auto successors = block.GetTerminator()->GetSuccessors(); for (auto& successor : successors) { auto spredecessors = successor->GetPredecessors(); bool findFlag = false; for (auto& spredecessor : spredecessors) { if (spredecessor->GetIdentifierWithoutPrefix() == block.GetIdentifierWithoutPrefix()) { findFlag = true; break; } } if (!findFlag) { ErrorInFunc(block.GetTopLevelFunc(), successor->GetIdentifier() + " is " + block.GetIdentifier() + "'s successor, but " + block.GetIdentifier() + " is not " + successor->GetIdentifier() + "'s predecessor."); return false; } } return true; } // block must have expression. // block last expression must be terminator. // block middle expression must not be terminator. bool CheckBlock(const Block& block) const { CJC_NULLPTR_CHECK(block.GetParentBlockGroup()); auto exprs = block.GetExpressions(); if (IsEndPhase() && exprs.size() == 0) { ErrorInFunc(block.GetTopLevelFunc(), "block " + block.GetIdentifier() + " has no expression."); return false; } for (size_t loop = 0; loop + 1 < exprs.size(); loop++) { auto expr = exprs[loop]; if (expr->IsTerminator()) { ErrorInFunc( block.GetTopLevelFunc(), "terminator found in the middle of block " + block.GetIdentifier() + "."); return false; } } if (!CheckTerminator(block)) { ErrorInFunc(block.GetTopLevelFunc(), "block " + block.GetIdentifier() + " does not have terminator."); return false; } return CheckSuccessors(block); } bool ExprOperandCheck(const Expression& expr, std::vector& values) const { const auto& operands = expr.GetOperands(); for (auto& operand : operands) { if (operand->IsLiteral()) { continue; } if (operand->IsGlobal()) { continue; } if (operand->TestAttr(Attribute::IMPORTED)) { continue; } if (IsEndPhase() && std::find(values.begin(), values.end(), operand) == values.end()) { if (expr.IsTerminator()) { ErrorInFunc(expr.GetTopLevelFunc(), operand->GetIdentifier() + " in terminator of block " + expr.GetParentBlock()->GetIdentifier() + " is unreachable."); } else { ErrorInFunc(expr.GetTopLevelFunc(), operand->GetIdentifier() + " in " + expr.GetResult()->GetIdentifier() + " is unreachable."); } return false; } } return true; } bool ExprOperandCheck(const Terminator& terminator, std::vector& values, std::set& blocks) { auto ret = true; if (auto res = terminator.GetResult(); res != nullptr) { values.emplace_back(res); } for (auto& suc : terminator.GetSuccessors()) { auto size = values.size(); ret = ExprOperandCheck(*suc, values, blocks) && ret; // clear branch values if (values.size() > size) { values.erase(values.begin() + static_cast(size), values.end()); } } return ret; } bool ExprOperandCheck(const ForIn& forin, std::vector& values) { bool ret = true; for (auto bg : forin.GetExecutionOrder()) { ret = ExprOperandCheck(*bg, values) && ret; } return ret; } bool ExprOperandCheck(const Block& block, std::vector& values, std::set& blocks) { if (blocks.count(&block) != 0) { return true; } blocks.insert(&block); auto ret = true; for (auto expr : block.GetExpressions()) { ret = ExprOperandCheck(*expr, values) && ret; if (expr->IsTerminator()) { auto terminator = Cangjie::StaticCast(expr); ret = ExprOperandCheck(*terminator, values, blocks) && ret; } else { auto exprKind = expr->GetExprKind(); if (exprKind == ExprKind::IF) { auto ifExpr = Cangjie::StaticCast(expr); ret = ExprOperandCheck(*ifExpr->GetTrueBranch(), values) && ret; ret = ExprOperandCheck(*ifExpr->GetFalseBranch(), values) && ret; } else if (exprKind == ExprKind::LOOP) { auto loopExpr = Cangjie::StaticCast(expr); ret = ExprOperandCheck(*loopExpr->GetLoopBody(), values) && ret; } else if (auto forin = Cangjie::DynamicCast(expr)) { ret = ExprOperandCheck(*forin, values) && ret; } else if (exprKind == ExprKind::LAMBDA) { auto size = values.size(); auto lambdaExpr = Cangjie::StaticCast(expr); if (lambdaExpr->IsLocalFunc()) { // Local function can used itself inside funcBody. values.emplace_back(expr->GetResult()); } const auto& params = lambdaExpr->GetParams(); values.insert(values.end(), params.begin(), params.end()); ret = ExprOperandCheck(*lambdaExpr->GetEntryBlock(), values, blocks); // Clear values add in this lambda. values.erase(values.begin() + static_cast(size), values.end()); } values.emplace_back(expr->GetResult()); } } return ret; } bool ExprOperandCheck(const BlockGroup& groupBody, std::vector& values) { auto size = values.size(); auto entryBlock = groupBody.GetEntryBlock(); // already checked blocks std::set blocks; auto ret = ExprOperandCheck(*entryBlock, values, blocks); // clear values add in this block group. if (values.size() != 0) { values.erase(values.begin() + static_cast(size), values.end()); } return ret; } bool CheckExpression(const Expression& expr, std::set& genericTypes) { CJC_NULLPTR_CHECK(expr.GetParentBlock()); auto ret = true; const std::unordered_map> actionMap = { {ExprMajorKind::TERMINATOR, [&ret, &expr, this]() { ret = CheckTerminator(Cangjie::StaticCast(expr)); }}, {ExprMajorKind::UNARY_EXPR, [&ret, &expr, this]() { ret = CheckUnaryExpression(expr); }}, {ExprMajorKind::BINARY_EXPR, [&ret, &expr, this]() { ret = CheckBinaryExpression(expr); }}}; if (auto iter = actionMap.find(expr.GetExprMajorKind()); iter != actionMap.end()) { iter->second(); } else { ret = CheckNormalExpr(expr, genericTypes); } return ret; } bool CheckNormalExpr(const Expression& expr, std::set& genericTypes) { bool ret = true; const std::unordered_map> actionMap = { {ExprKind::CONSTANT, [&ret, &expr, this]() { ret = CheckConstant(Cangjie::StaticCast(expr)); }}, {ExprKind::ALLOCATE, [&ret, &expr, this]() { ret = CheckAllocate(Cangjie::StaticCast(expr)); }}, {ExprKind::LOAD, [&ret, &expr, this]() { ret = CheckLoad(Cangjie::StaticCast(expr)); }}, {ExprKind::STORE, [&ret, &expr, this]() { ret = CheckStore(Cangjie::StaticCast(expr)); }}, {ExprKind::GET_ELEMENT_REF, [&ret, &expr, this]() { ret = CheckGetElementRef(Cangjie::StaticCast(expr)); }}, {ExprKind::STORE_ELEMENT_REF, [&ret, &expr, this]() { ret = CheckStoreElementRef(Cangjie::StaticCast(expr)); }}, {ExprKind::IF, [&ret, &expr, this]() { ret = CheckIf(Cangjie::StaticCast(expr)); }}, {ExprKind::LOOP, [&ret, &expr, this]() { ret = CheckLoop(Cangjie::StaticCast(expr)); }}, {ExprKind::FORIN_RANGE, [&ret, &expr, this]() { ret = CheckForIn(Cangjie::StaticCast(expr)); }}, {ExprKind::FORIN_ITER, [&ret, &expr, this]() { ret = CheckForIn(Cangjie::StaticCast(expr)); }}, {ExprKind::FORIN_CLOSED_RANGE, [&ret, &expr, this]() { ret = CheckForIn(Cangjie::StaticCast(expr)); }}, {ExprKind::DEBUGEXPR, [&ret, &expr, this]() { ret = CheckDebug(Cangjie::StaticCast(expr)); }}, {ExprKind::TUPLE, [&ret, &expr, this]() { ret = CheckTuple(Cangjie::StaticCast(expr)); }}, {ExprKind::FIELD, [&ret, &expr, this]() { ret = CheckField(Cangjie::StaticCast(expr)); }}, {ExprKind::APPLY, [&ret, &expr, this]() { ret = CheckApply(Cangjie::StaticCast(expr)); }}, {ExprKind::INVOKE, [&ret, &expr, this]() { ret = CheckInvoke(Cangjie::StaticCast(expr)); }}, {ExprKind::INVOKESTATIC, [&ret, &expr, this]() { ret = CheckInvokeStatic(Cangjie::StaticCast(expr)); }}, {ExprKind::INSTANCEOF, [&ret, &expr, this]() { ret = CheckInstanceOf(Cangjie::StaticCast(expr)); }}, {ExprKind::TYPECAST, [&ret, &expr, this]() { ret = CheckTypeCast(expr); }}, {ExprKind::GET_EXCEPTION, [&ret, &expr, this]() { ret = CheckGetException(Cangjie::StaticCast(expr)); }}, {ExprKind::SPAWN, [&ret, &expr, this]() { ret = CheckSpawn(expr); }}, {ExprKind::RAW_ARRAY_ALLOCATE, [&ret, &expr, this]() { ret = CheckRawArrayAllocate(Cangjie::StaticCast(expr)); }}, {ExprKind::RAW_ARRAY_LITERAL_INIT, [&ret, &expr, this]() { ret = CheckRawArrayLiteralInit(Cangjie::StaticCast(expr)); }}, {ExprKind::RAW_ARRAY_INIT_BY_VALUE, [&ret, &expr, this]() { ret = CheckRawArrayInitByValue(Cangjie::StaticCast(expr)); }}, {ExprKind::VARRAY, [&ret, &expr, this]() { ret = CheckVArray(Cangjie::StaticCast(expr)); }}, {ExprKind::VARRAY_BUILDER, [&ret, &expr, this]() { ret = CheckVArrayBuilder(Cangjie::StaticCast(expr)); }}, {ExprKind::INTRINSIC, [&ret, &expr, this]() { ret = CheckIntrinsic(Cangjie::StaticCast(expr)); }}, {ExprKind::LAMBDA, [&ret, &expr, this, &genericTypes]() { ret = CheckLambda(Cangjie::StaticCast(expr), genericTypes); }}, {ExprKind::BOX, [&ret, &expr, this]() { ret = CheckBox(Cangjie::StaticCast(expr)); }}, {ExprKind::UNBOX, [&ret, &expr, this]() { ret = CheckUnBox(Cangjie::StaticCast(expr)); }}, {ExprKind::TRANSFORM_TO_GENERIC, [&ret, &expr, this]() { ret = CheckTransformToGeneric(Cangjie::StaticCast(expr)); }}, {ExprKind::TRANSFORM_TO_CONCRETE, [&ret, &expr, this]() { ret = CheckTransformToConcrete(Cangjie::StaticCast(expr)); }}, {ExprKind::GET_INSTANTIATE_VALUE, [&ret, &expr, this]() { ret = CheckGetInstantiateValue(Cangjie::StaticCast(expr)); }}, {ExprKind::UNBOX_TO_REF, [&ret, &expr, this]() { ret = CheckUnBoxToRef(Cangjie::StaticCast(expr)); }}, {ExprKind::GET_RTTI, [&ret, &expr, this]() { ret = CheckGetRTTI(Cangjie::StaticCast(expr)); }}, {ExprKind::GET_RTTI_STATIC, [&ret, &expr, this]() { ret = CheckGetRTTI(Cangjie::StaticCast(expr)); }}, {ExprKind::GET_ELEMENT_BY_NAME, [&ret, &expr, this]() { ret = CheckGetElementByName(Cangjie::StaticCast(expr)); }}, {ExprKind::STORE_ELEMENT_BY_NAME, [&ret, &expr, this]() { ret = CheckStoreElementByName(Cangjie::StaticCast(expr)); }}, {ExprKind::FIELD_BY_NAME, [&ret, &expr, this]() { ret = CheckFieldByName(Cangjie::StaticCast(expr)); }}, }; if (auto iter = actionMap.find(expr.GetExprKind()); iter != actionMap.end()) { iter->second(); } else { WarningInFunc(expr.GetTopLevelFunc(), "find unrecongnized ExprKind " + expr.GetExprKindName() + "."); } return ret; } bool CheckGetRTTI(const GetRTTI& expr) const { auto result = expr.GetResult(); if (!result->GetType()->IsUnit()) { TypeCheckError(*result, "Unit"); return false; } auto operandType = expr.GetOperand()->GetType(); if (!Cangjie::Is(operandType) || (!operandType->StripAllRefs()->IsClass() && !operandType->StripAllRefs()->IsThis())) { ErrorInFunc(expr.GetTopLevelFunc(), "GetRTTI must be used on Class& or This&"); return false; } return true; } bool CheckGetRTTI(const GetRTTIStatic& expr) const { auto result = expr.GetResult(); if (!result->GetType()->IsUnit()) { TypeCheckError(*result, "Unit"); return false; } auto func = expr.GetTopLevelFunc(); bool skipCheckUse = false; if (!IsEndPhase()) { skipCheckUse = true; } if (!skipCheckUse && expr.GetRTTIType()->StripAllRefs()->IsThis() && !func->TestAttr(Attribute::STATIC)) { ErrorInFunc(func, "Cannot use GetRTTIStatic on This type in non-static function"); return false; } return true; } bool CheckGetElementByName([[maybe_unused]] const GetElementByName& expr) const { return false; } bool CheckStoreElementByName([[maybe_unused]] const StoreElementByName& expr) const { return false; } bool CheckFieldByName([[maybe_unused]] const FieldByName& expr) const { return false; } bool CheckConstant(const Constant& expr) const { auto result = expr.GetResult(); auto expTy = expr.GetOperand(0)->GetType(); if (!IsExpectedType(*result->GetType(), *expTy)) { TypeCheckError(expr, *expr.GetOperand(0), result->GetType()->ToString()); return false; } return true; } // Unary Expression // UnaryExpression-Not input and result must be Boolean. // UnaryExpression result type must be same with operand[0]. bool CheckUnaryExpression(const Expression& expr) const { auto ty = expr.GetResult()->GetType(); auto expTy = expr.GetOperand(0)->GetType(); if (expTy->IsNothing()) { return true; } if (expr.GetExprKind() == ExprKind::NOT && !expTy->IsBoolean()) { TypeCheckError(expr, *expr.GetOperand(0), "Boolean"); return false; } if (expr.GetExprKind() == ExprKind::BITNOT && !expTy->IsInteger()) { TypeCheckError(expr, *expr.GetOperand(0), "Integer"); return false; } if (expr.GetExprKind() == ExprKind::NEG && !(expTy->IsInteger() || expTy->IsFloat())) { TypeCheckError(expr, *expr.GetOperand(0), "Integer or Float"); return false; } if (!IsExpectedType(*expTy, *ty)) { TypeCheckError(*expr.GetResult(), expTy->ToString()); return false; } return true; } // Binary Expression bool CheckBinaryExpression(const Expression& expr) const { auto exprKind = expr.GetExprKind(); if (exprKind >= ExprKind::ADD && exprKind <= ExprKind::MOD) { return CheckCalculExpression(expr); } else if (exprKind == ExprKind::EXP) { return CheckExpExpression(expr); } else if (exprKind >= ExprKind::LSHIFT && exprKind <= ExprKind::BITXOR) { return CheckBitExpression(expr); } else if (exprKind >= ExprKind::LT && exprKind <= ExprKind::NOTEQUAL) { return CheckCmpExpression(expr); } else if (exprKind >= ExprKind::AND && exprKind <= ExprKind::OR) { return CheckLogicExpression(expr); } WarningInFunc(expr.GetTopLevelFunc(), "find unrecongnized ExprKind " + expr.GetExprKindName() + "."); return true; } // BinaryExpression-Mod input must be Integer. // BinaryExpression-Add Sub Mul Div Mod operands types, result type must be same. bool CheckCalculExpression(const Expression& expr) const { auto expTy0 = expr.GetOperand(0)->GetType(); auto expTy1 = expr.GetOperand(1)->GetType(); if (expTy0->IsNothing() && expTy1->IsNothing()) { // both operand types are 'Nothing', no need check return true; } if (expr.GetExprKind() == ExprKind::MOD) { if (!expTy0->IsNothing() && !expTy0->IsInteger()) { TypeCheckError(expr, *expr.GetOperand(0), "Integer"); return false; } if (!expTy1->IsNothing() && !expTy1->IsInteger()) { TypeCheckError(expr, *expr.GetOperand(0), "Integer"); return false; } } if (!expTy0->IsNothing() && !expTy1->IsNothing()) { // no 'Nothing' type in operands check operand and result if (!IsExpectedType(*expTy0, *expTy1)) { TypeCheckError(expr, *expr.GetOperand(1), expTy0->ToString()); return false; } if (!expTy0->IsNothing() && !IsExpectedType(*expTy0, *expr.GetResult()->GetType())) { TypeCheckError(expr, *expr.GetResult(), expTy0->ToString()); return false; } } else { // 'Nothing' type in operands just check result if (!IsExpectedType(*expr.GetResult()->GetType(), *expTy0)) { TypeCheckError(expr, *expr.GetOperand(0), expr.GetResult()->GetType()->ToString()); return false; } if (!IsExpectedType(*expr.GetResult()->GetType(), *expTy1)) { TypeCheckError(expr, *expr.GetOperand(1), expr.GetResult()->GetType()->ToString()); return false; } } return true; } // BinaryExpression-Exp must be (Int64, UInt64)->Int64 or (Float64, Int64/Float64)->Float64. bool CheckExpExpression(const Expression& expr) const { auto expTy0 = expr.GetOperand(0)->GetType(); auto expTy1 = expr.GetOperand(1)->GetType(); auto result = expr.GetResult(); if (!IsExpectedType(*expTy0, Type::TypeKind::TYPE_INT64) && !IsExpectedType(*expTy0, Type::TypeKind::TYPE_FLOAT64)) { TypeCheckError(expr, *expr.GetOperand(0), "Int64 or Float64"); return false; } auto resultTy = result->GetType(); if (expTy0->GetTypeKind() == Type::TypeKind::TYPE_INT64) { if (!IsExpectedType(*expTy1, Type::TypeKind::TYPE_UINT64)) { TypeCheckError(expr, *expr.GetOperand(1), "UInt64"); return false; } } else if (expTy0->GetTypeKind() == Type::TypeKind::TYPE_FLOAT64) { if (!IsExpectedType(*expTy1, Type::TypeKind::TYPE_INT64) && !IsExpectedType(*expTy1, Type::TypeKind::TYPE_FLOAT64)) { TypeCheckError(expr, *expr.GetOperand(1), "Int64 or Float64"); return false; } } if (!IsExpectedType(*resultTy, *expTy0)) { TypeCheckError(*result, expTy0->ToString()); return false; } return true; } // BinaryExpression-Lshift Rshift BitAnd BitOr BitXOr operands types must be Integer. bool CheckBitExpression(const Expression& expr) const { auto expTy0 = expr.GetOperand(0)->GetType(); if (!expTy0->IsInteger() && !expTy0->IsNothing()) { TypeCheckError(expr, *expr.GetOperand(0), "Integer"); return false; } auto expTy1 = expr.GetOperand(1)->GetType(); if (!expTy1->IsInteger() && !expTy1->IsNothing()) { TypeCheckError(expr, *expr.GetOperand(1), "Integer"); return false; } return true; } // BinaryExpression-Lt Gt Le Ge Equal NotEqual operands types must be same. // BinaryExpression-Lt Gt Le Ge Equal NotEqual result type must be Boolean. bool CheckCmpExpression(const Expression& expr) const { auto expTy0 = expr.GetOperand(0)->GetType(); auto expTy1 = expr.GetOperand(1)->GetType(); auto result = expr.GetResult(); if (!IsExpectedType(*expTy0, *expTy1) && !expTy0->IsNothing()) { TypeCheckError(expr, *expr.GetOperand(1), expTy0->ToString()); return false; } auto resultTy = result->GetType(); if (!resultTy->IsBoolean()) { TypeCheckError(*result, "Boolean"); return false; } return true; } // BinaryExpression-And Or operands and result types must be Boolean. bool CheckLogicExpression(const Expression& expr) const { auto result = expr.GetResult(); auto expTy0 = expr.GetOperand(0)->GetType(); auto expTy1 = expr.GetOperand(1)->GetType(); if (!expTy0->IsNothing() || !expTy1->IsNothing()) { if (!expTy0->IsBoolean()) { TypeCheckError(expr, *expr.GetOperand(0), "Boolean"); return false; } if (!expTy1->IsBoolean()) { TypeCheckError(expr, *expr.GetOperand(1), "Boolean"); return false; } } auto resultTy = result->GetType(); if (!resultTy->IsBoolean()) { TypeCheckError(*result, "Boolean"); return false; } return true; } bool CheckGenericArgs(Type& baseType) const { auto baseTy = &baseType; while (baseTy->IsRef()) { baseTy = Cangjie::StaticCast(baseTy)->GetBaseType(); } if (auto customType = Cangjie::DynamicCast(baseTy)) { auto customDef = customType->GetCustomTypeDef(); if (auto customDefType = Cangjie::DynamicCast(customDef->GetType()); customDefType && customType->GetGenericArgs().size() != customDefType->GetGenericArgs().size()) { Errorln("arg size of " + customDefType->ToString() + " is not equal to " + customType->ToString()); return false; } } return true; } // Memory Expression // Allocate result type must be operand[0]'s RefType. bool CheckAllocate(const Allocate& expr) const { auto result = expr.GetResult(); auto resultTy = result->GetType(); auto baseType = expr.GetType(); if (!resultTy->IsRef() || baseType != Cangjie::StaticCast(resultTy)->GetBaseType()) { TypeCheckError(*result, baseType->ToString() + "&"); return false; } if (baseType->IsVoid()) { Errorln("Unexpected allocation of Void type"); return false; } if (!CheckGenericArgs(*baseType)) { return false; } return true; } // Load location type must be result type's RefType. bool CheckLoad(const Load& expr) const { auto location = expr.GetLocation(); auto result = expr.GetResult(); if (!location->GetType()->IsRef()) { TypeCheckError(expr, *location, result->GetType()->ToString() + "&"); return false; } auto expTy = Cangjie::StaticCast(location->GetType())->GetBaseType(); if (!IsExpectedType(*expTy, *result->GetType())) { TypeCheckError(*result, expTy->ToString()); return false; } return true; } // Store location type must be value type's RefType. // Store result type must be Unit. bool CheckStore(const Store& expr) const { auto location = expr.GetLocation(); auto result = expr.GetResult(); auto value = expr.GetValue(); if (!location->GetType()->IsRef()) { TypeCheckError(expr, *location, location->GetType()->ToString() + "&"); return false; } auto expTy = Cangjie::StaticCast(location->GetType())->GetBaseType(); if (!IsExpectedType(*expTy, *value->GetType())) { TypeCheckError(expr, *value, expTy->ToString()); return false; } if (!result->GetType()->IsUnit()) { TypeCheckError(*result, "Unit"); return false; } if (auto res = Cangjie::DynamicCast(location)) { if (res->GetExpr()->GetExprKind() == Cangjie::CHIR::ExprKind::GET_ELEMENT_REF) { auto err = "Location of Store:" + result->GetIdentifier() + " should not be GetElementRef."; ErrorInFunc(expr.GetTopLevelFunc(), err); return false; } } return true; } Type* CheckPathIsAvailable(const Expression& expr, const std::vector& path, Type& locationType) const { auto baseType = &locationType; std::string indexStr = "("; for (size_t i = 0; i < path.size(); i++) { auto index = path[i]; if (i != 0) { indexStr += ", "; } indexStr += std::to_string(index); baseType = GetFieldOfType(*baseType, index, builder); if (baseType == nullptr) { ErrorInFunc(expr.GetTopLevelFunc(), "value " + expr.GetResult()->GetIdentifier() + " can not find path " + indexStr + ") from type " + locationType.ToString()); return nullptr; } } return baseType; } // GetElementRef baseType must be Class, Struct or RawArray. // GetElementRef result type must be same with type of Class/Struct's member var or RawArray's element. bool CheckGetElementRef(const GetElementRef&) const { return true; } bool CheckStoreElementRef(const StoreElementRef& expr) const { auto location = expr.GetLocation(); auto result = expr.GetResult(); auto baseType = location->GetType(); if (!baseType->IsRef() || baseType->GetRefDims() != 1) { TypeCheckError(expr, *location, "Class& Struct& Tuple& Closure& or RawArray&"); return false; } baseType = Cangjie::StaticCast(baseType)->GetBaseType(); if (baseType->IsGeneric()) { ErrorInFunc(expr.GetTopLevelFunc(), "Location of StoreElementRef: " + result->GetIdentifier() + "should not be generic"); return false; } auto fieldType = CheckPathIsAvailable(expr, expr.GetPath(), *baseType); if (fieldType == nullptr) { return false; } if (!CheckType(*expr.GetValue()->GetType(), *fieldType)) { auto errInfo = "Stored value type " + expr.GetValue()->GetType()->ToString() + " of value " + expr.GetValue()->GetIdentifier() + " does not match pointer operand " + fieldType->ToString() + " in StoreElementRef " + expr.GetResult()->GetIdentifier(); ErrorInFunc(expr.GetTopLevelFunc(), errInfo); } if (!result->GetType()->IsUnit()) { TypeCheckError(*result, "Unit"); return false; } return true; } // Struct Control // If condition type must be Boolean. // If true and false branch exit type must be same. bool CheckIf(const If& expr) const { auto cond = expr.GetCondition(); if (!cond->GetType()->IsBoolean() && !cond->GetType()->IsNothing()) { TypeCheckError(expr, *cond, "Boolean"); return false; } return true; } bool CheckLoop(const Loop& expr) const { (void)expr; // To be implemented return true; } bool CheckForIn(const ForIn& expr) const { (void)expr; // To be implemented return true; } // Other // Debug return type must be Unit. bool CheckDebug(const Debug& expr) const { auto result = expr.GetResult(); if (!result->GetType()->IsUnit()) { TypeCheckError(*result, "Unit"); return false; } auto base = expr.GetValue(); if (!base->IsParameter() && opts.enableCompileDebug) { if (!base->GetType()->IsRef()) { if (base->GetType()->IsClassOrArray()) { TypeCheckError(*base, base->GetType()->ToString() + "&&"); return false; } else { TypeCheckError(*base, base->GetType()->ToString() + "&"); return false; } } else { auto baseType = Cangjie::StaticCast(base->GetType())->GetBaseType(); if (baseType->IsClassOrArray()) { TypeCheckError(*base, base->GetType()->ToString() + "&"); return false; } else if (baseType->IsRef()) { auto baseTypeTemp = Cangjie::StaticCast(baseType)->GetBaseType(); if (!baseTypeTemp->IsClassOrArray()) { Errorln("Unexpected type In:" + expr.ToString()); } } } } return true; } // Tuple inputs types and output type must be satisfied. bool CheckTuple(const Tuple& expr) const { auto result = expr.GetResult(); auto resultTy = result->GetType(); const auto& operands = expr.GetOperands(); if (resultTy->IsEnum()) { if (operands.size() == 0) { auto err = result->GetIdentifier() + " first operand type is expected to be UInt32 or Bool."; ErrorInFunc(expr.GetTopLevelFunc(), err); return false; } if (!IsEnumSelectorType(*operands[0]->GetType())) { TypeCheckError(expr, *operands[0], "Constant UInt32 or Constant Bool"); return false; } if (auto operand = Cangjie::DynamicCast(operands[0]); operand == nullptr || !(operand->GetExpr()->IsConstantInt() || operand->GetExpr()->IsConstantBool())) { TypeCheckError(expr, *operands[0], "Constant UInt32 or Constant Bool"); return false; } return true; } else if (!resultTy->IsTuple() && !resultTy->IsStruct()) { TypeCheckError(*result, "Enum, Tuple or Struct"); return false; } size_t resultTySize = 0; if (resultTy->IsTuple()) { resultTySize = Cangjie::StaticCast(resultTy)->GetElementTypes().size(); } else { resultTySize = Cangjie::StaticCast(resultTy)->GetStructDef()->GetAllInstanceVarNum(); } if (operands.size() != resultTySize) { TypeCheckError(*result, "Tuple size " + std::to_string(operands.size())); return false; } for (size_t i = 0; i < resultTySize; i++) { auto fieldTy = GetFieldOfType(*resultTy, i, builder); if (!IsExpectedType(*fieldTy, *operands[i]->GetType())) { TypeCheckError(expr, i, *operands[i], fieldTy->ToString()); return false; } } return true; } // Field inputs types and output type must be satisfied. bool CheckField(const Field& expr) const { auto base = expr.GetBase(); auto index = expr.GetPath(); auto result = expr.GetResult(); auto type = base->GetType(); if (type->IsGeneric()) { ErrorInFunc( expr.GetTopLevelFunc(), "Location of Field: " + result->GetIdentifier() + "should not be generic"); return false; } std::string indexStr = "("; CJC_ASSERT(!type->IsEnum() || index.size() > 0); if (type->IsEnum() && index.back() == 0) { auto kind = result->GetType()->GetTypeKind(); if (kind != Type::TypeKind::TYPE_UINT32 && kind != Type::TypeKind::TYPE_BOOLEAN) { ErrorInFunc(expr.GetTopLevelFunc(), "value " + expr.GetResult()->GetIdentifier() + " has type " + expr.GetResultType()->ToString() + " but type UInt32 or Boolean is expected."); return false; } return true; } for (size_t i = 0; i < index.size(); i++) { if (i != 0) { indexStr = indexStr + ", "; } indexStr = indexStr + std::to_string(index[i]); if (type->IsClass() || type->IsRef()) { ErrorInFunc(expr.GetTopLevelFunc(), "value " + result->GetIdentifier() + " can not find indexs " + indexStr + ") from input type " + base->GetType()->ToString()); return false; } if (type->IsGeneric()) { continue; } type = GetFieldOfType(*type, index[i], builder); if (type == nullptr) { ErrorInFunc(expr.GetTopLevelFunc(), "value " + result->GetIdentifier() + " can not find indexs " + indexStr + ") from input type " + base->GetType()->ToString()); return false; } } if (!IsExpectedType(*type, *result->GetType())) { TypeCheckError(*result, type->ToString()); return false; } return true; } bool IsFuncType(const Type& type) const { if (type.IsFunc()) { return true; } if (auto gType = Cangjie::DynamicCast(&type); gType) { for (auto upperBound : gType->GetUpperBounds()) { if (IsFuncType(*upperBound)) { return true; } } } return false; } FuncType* GetFuncType(Type& type) const { if (auto fType = Cangjie::DynamicCast(&type); fType) { return fType; } if (auto gType = Cangjie::DynamicCast(&type); gType) { for (auto upperBound : gType->GetUpperBounds()) { auto ty = GetFuncType(*upperBound); if (ty != nullptr) { return ty; } } } CJC_ABORT(); return nullptr; } bool IsMemberFunc(const Ptr func) const { if (auto calleeFunc = Cangjie::DynamicCast(func); calleeFunc && calleeFunc->IsMemberFunc()) { return true; } return false; } bool CheckReturnType(const FuncType& declaredFuncType, const Type& instRetType) const { auto declaredRetType = declaredFuncType.GetReturnType(); return CheckType(*declaredRetType, instRetType) || CheckType(instRetType, *declaredRetType); } template bool CheckInstantiatedType( const T& expr, Ptr funcType, bool isConstructor) const { if (!isConstructor) { if (!CheckReturnType(*funcType, *expr.GetResult()->GetType())) { std::string info = "Value:" + expr.GetResult()->ToString() + " type mismatch between instantiated return type '" + expr.GetResultType()->ToString() + "' and arg type '" + funcType->GetReturnType()->ToString() + "'."; ErrorInFunc(expr.GetTopLevelFunc(), info); return false; } } return true; } bool CheckTypeArgs(const Type& newInstantiatedType, const Type& newOriginType) const { if (newInstantiatedType.GetTypeArgs().size() != newOriginType.GetTypeArgs().size()) { return false; } for (size_t index = 0; index < newInstantiatedType.GetTypeArgs().size(); ++index) { if (!CheckType(*newInstantiatedType.GetTypeArgs()[index], *newOriginType.GetTypeArgs()[index])) { return false; } } return true; } bool CheckType(const Type& subType, const Type& parentType) const { if (subType.IsNothing()) { return true; } if (parentType.IsGeneric() || subType.IsGeneric()) { return true; } if (&subType == &parentType) { return true; } if (subType.GetTypeKind() != parentType.GetTypeKind()) { return false; } if (subType.IsEqualOrSubTypeOf(parentType, builder)) { return true; } if (auto subCustomType = Cangjie::DynamicCast(&subType)) { auto parentCustomType = Cangjie::StaticCast(&parentType); if (subCustomType->GetCustomTypeDef() != parentCustomType->GetCustomTypeDef()) { return false; } } if (CheckTypeArgs(subType, parentType)) { return true; } return false; } // Apply callee must be funcType. // Apply result type must same with func return types, and input types must same with func input types bool CheckApply(const Apply& expr) const { auto func = expr.GetCallee(); auto type = func->GetType(); auto result = expr.GetResult(); // check callee type must function type if (!IsFuncType(*type)) { TypeCheckError(expr, *func, "FuncType"); return false; } auto funcType = GetFuncType(*type); if (funcType->HasVarArg()) { return true; } if (auto callee = Cangjie::DynamicCast(func); callee && callee->IsMemberFunc()) { if (expr.GetThisType() == nullptr) { std::string info = expr.GetResult()->ToString() + ", callee is method func, but don't have ThisType."; ErrorInFunc(expr.GetTopLevelFunc(), info); return false; } } if (!CheckInstantiatedType(expr, Cangjie::StaticCast(func->GetType()), IsConstructor(*func))) { return false; } // check arg size auto args = expr.GetArgs(); auto funcParamsTy = funcType->GetParamTypes(); if (args.size() != funcParamsTy.size()) { std::string info = "value " + result->GetIdentifier() + " has " + std::to_string(args.size()) + " arguments but the function type has " + std::to_string(funcParamsTy.size()) + " parameters."; ErrorInFunc(expr.GetTopLevelFunc(), info); return false; } // check ref info of args for (size_t i = 0; i < args.size(); i++) { bool refOK = true; if (i == 0 && (func->TestAttr(Attribute::MUT) || IsConstructor(*func) || IsInstanceVarInit(*func))) { refOK = CheckFuncArgType(*args[i]->GetType(), funcType->IsCFunc(), true); } else { refOK = CheckFuncArgType(*args[i]->GetType(), funcType->IsCFunc()); } if (!refOK) { std::string errMsg = "reference info of arg " + args[i]->ToString() + " in " + result->ToString() + " is error."; Errorln(errMsg); return false; } } return true; } // used for invoke and tryWithInvoke input object check bool IsClassRefSubType(const Type& leaf, const Type& root) const { if (!leaf.IsRef() || !root.IsRef()) { return false; } auto leafTy = Cangjie::StaticCast(leaf).GetBaseType(); auto rootTy = Cangjie::StaticCast(root).GetBaseType(); return leafTy->IsEqualOrSubTypeOf(*rootTy, builder); } // Invoke method type must be funcType. // Invoke result type must same with func return type, and input types must same with func input types // Invoke object type must be subtype of func first input type bool CheckInvoke(const Invoke& expr) const { // Need to adapt here. (void)expr; return true; } // InvokeStatic method type must be funcType. // InvokeStatic result type must same with func return type, and input types must same with func input types template bool CheckInvokeStatic(const T& expr) const { auto type = expr.GetMethodType(); auto result = expr.GetResult(); if (!type->IsFunc()) { auto err = "method " + expr.GetMethodName() + " used in " + result->GetIdentifier() + " has type " + type->ToString() + " but type FuncType is expected."; ErrorInFunc(expr.GetTopLevelFunc(), err); return false; } auto funcType = Cangjie::StaticCast(type); const auto& args = expr.GetOperands(); if (!CheckInstantiatedType(expr, Cangjie::StaticCast(type), false)) { return false; } // check ref info of args for (size_t i = 0; i < args.size(); i++) { bool refOK = CheckFuncArgType(*args[i]->GetType(), funcType->IsCFunc()); if (!refOK) { std::string errMsg = "reference info of arg " + args[i]->ToString() + " in " + result->ToString() + " is error."; Errorln(errMsg); return false; } } auto rttiValue = expr.GetRTTIValue(); auto rttiExpr = Cangjie::StaticCast(rttiValue)->GetExpr(); if (!Cangjie::Is(rttiExpr) && !Cangjie::Is(rttiExpr)) { ErrorInFunc(rttiExpr->GetTopLevelFunc(), "RTTI value of InvokeStatic must be either GetRTTI or GetRTTIStatic, got " + rttiValue->ToString()); return false; } return true; } bool IsClassRefType(const Type& type) const // should be replaced by Type::IsClassRef on dev. { if (auto reft = Cangjie::DynamicCast(&type)) { return reft->GetBaseType()->IsClass(); } return false; } bool IsBoxRefType(const Type& type) const // should be replaced by Type::IsClassRef on dev. { if (auto reft = Cangjie::DynamicCast(&type)) { return reft->GetBaseType()->IsBox(); } return false; } // InstanceOf return type must be Boolean. bool CheckInstanceOf(const InstanceOf& expr) const { auto result = expr.GetResult(); if (!result->GetType()->IsBoolean()) { TypeCheckError(*result, "Boolean"); return false; } // instanceof rule: // 1. both value and targetType are & to class type (which may be boxed type) // 2. the sourceType or the targetType generic is generic auto targetTy = expr.GetType(); if (!CheckGenericArgs(*targetTy)) { return false; } return true; } Type* GetRefBaseType(Type& ty) { if (!ty.IsRef()) { return &ty; } return GetRefBaseType(*Cangjie::StaticCast(ty).GetBaseType()); } bool CheckTypeCastEnum( const Expression& expr, const Ptr srcTy, const Ptr targetTy, const Ptr result) const { auto enumTy = Cangjie::StaticCast(srcTy); if (enumTy->GetEnumDef()->IsAllCtorsTrivial()) { // expected: Trivial Enum -> UInt32 if (targetTy->GetTypeKind() != Type::TypeKind::TYPE_UINT32) { TypeCheckError(*result, "UInt32"); return false; } } else { // expected: Trivial Enum -> UInt32 if (targetTy->GetTypeKind() == Type::TypeKind::TYPE_UINT32) { TypeCheckError(expr, *expr.GetOperand(0), "trivial Enum"); return false; } // expected: Non-Trivial Enum -> Tuple if (!targetTy->IsTuple()) { TypeCheckError(*result, "Tuple"); return false; } } return true; } bool CheckTypeCastInteger(const Ptr srcTy, const Ptr targetTy, const Ptr result) const { std::string expTy = "Integer Float or Rune"; // Integer to Rune, Integer, Float if (targetTy->IsInteger() || targetTy->IsFloat() || targetTy->IsRune()) { return true; } // UInt32 to Enum if (srcTy->GetTypeKind() == Type::TypeKind::TYPE_UINT32) { expTy = "Integer, Float, Rune or trivial Enum"; if (targetTy->IsEnum()) { auto enumTy = Cangjie::StaticCast(targetTy); if (enumTy->GetEnumDef()->IsAllCtorsTrivial()) { return true; } } } TypeCheckError(*result, expTy); return false; } bool CheckTypeCast(const Expression& expr) const { // Please have a try on: // class CA { // var x: T // init(_x: T) { x = _x } // } // class CA2 { // var x: Int64 // init(_x: Int64) { x = _x } // } // interface I1 { // func foo(): Int64 // } // extend CA <: I1 { // public func foo(): Int64 { // 6 // } // } // class CC { // var x = CA(0) // var y = 5 // } // main() { // var x: I1 = CC().x // <--- an error reported here // x.foo() // } (void)expr; return true; } bool CheckGetException(const GetException& expr) const { auto result = expr.GetResult(); auto expTy = result->GetType(); if (!expTy->IsRef() || !expTy->GetTypeArgs()[0]->IsClass()) { TypeCheckError(*result, "Class&"); return false; } return true; } // terminal check bool JumpFieldCheck(const Ptr srcBlock, const Ptr dstBlock) const { if (IsEndPhase() && dstBlock->GetParentBlockGroup() != srcBlock->GetParentBlockGroup()) { ErrorInFunc(srcBlock->GetTopLevelFunc(), "terminator in block " + srcBlock->GetIdentifier() + " jump to a unreachable block " + dstBlock->GetIdentifier() + "."); return false; } return true; } // terminator must jump to reachable blocks bool CheckTerminator(const Terminator& expr) const { auto srcBlock = expr.GetParentBlock(); bool ret = true; auto size = expr.GetNumOfSuccessor(); for (size_t i = 0; i < size; i++) { auto successor = expr.GetSuccessor(i); CJC_NULLPTR_CHECK(successor); ret = JumpFieldCheck(srcBlock, successor) && ret; } const std::unordered_map> actionMap = { {ExprKind::BRANCH, [&ret, &expr, this]() { ret = CheckBranch(Cangjie::StaticCast(expr)); }}, {ExprKind::MULTIBRANCH, [&ret, &expr, this]() { ret = CheckMultiBranch(Cangjie::StaticCast(expr)); }}, {ExprKind::APPLY_WITH_EXCEPTION, [&ret, &expr, this]() { ret = CheckApplyWithException(Cangjie::StaticCast(expr)); }}, {ExprKind::INVOKE_WITH_EXCEPTION, [&ret, &expr, this]() { ret = CheckInvokeWithException(Cangjie::StaticCast(expr)); }}, {ExprKind::INVOKESTATIC_WITH_EXCEPTION, [&ret, &expr, this]() { ret = CheckInvokeStatic(Cangjie::StaticCast(expr)); }}, {ExprKind::GOTO, [&ret, &expr, this]() { ret = CheckGoTo(Cangjie::StaticCast(expr)); }}, {ExprKind::EXIT, [&ret, &expr, this]() { ret = CheckExit(Cangjie::StaticCast(expr)); }}, {ExprKind::RAISE_EXCEPTION, [&ret, &expr, this]() { ret = CheckRaiseException(Cangjie::StaticCast(expr)); }}, {ExprKind::INT_OP_WITH_EXCEPTION, [&ret, &expr, this]() { ret = CheckIntOpWithException(Cangjie::StaticCast(expr)); }}, {ExprKind::SPAWN_WITH_EXCEPTION, [&ret, &expr, this]() { ret = CheckSpawnWithException(expr); }}, {ExprKind::TYPECAST_WITH_EXCEPTION, [&ret, &expr, this]() { ret = CheckTypeCastWithException(Cangjie::StaticCast(expr)); }}, {ExprKind::INTRINSIC_WITH_EXCEPTION, [&ret, &expr, this]() { ret = CheckIntrinsicWithException(Cangjie::StaticCast(expr)); }}, {ExprKind::ALLOCATE_WITH_EXCEPTION, [&ret, &expr, this]() { ret = CheckAllocateWithException(Cangjie::StaticCast(expr)); }}, {ExprKind::RAW_ARRAY_ALLOCATE_WITH_EXCEPTION, [&ret, &expr, this]() { ret = CheckRawArrayAllocateWithException( Cangjie::StaticCast(expr)); }}}; if (auto iter = actionMap.find(expr.GetExprKind()); iter != actionMap.end()) { iter->second(); } else { WarningInFunc(expr.GetTopLevelFunc(), "find unrecongnized ExprKind " + expr.GetExprKindName() + "."); } return ret; } bool CheckBranch(const Branch& expr) const { auto cond = expr.GetOperand(0); if (!cond->GetType()->IsBoolean() && !cond->GetType()->IsNothing()) { TypeCheckError(expr, *cond, "Boolean"); return false; } return true; } bool CheckMultiBranch(const MultiBranch& expr) const { auto cond = expr.GetCondition(); if (!cond->GetType()->IsInteger()) { TypeCheckError(expr, *cond, "Integer"); return false; } return true; } bool CheckApplyWithException(const ApplyWithException& expr) const { auto func = expr.GetCallee(); auto type = func->GetType(); auto result = expr.GetResult(); if (!IsFuncType(*type)) { TypeCheckError(expr, *func, "FuncType"); return false; } auto funcType = GetFuncType(*type); auto args = expr.GetArgs(); auto funcParamsTy = funcType->GetParamTypes(); if (args.size() != funcParamsTy.size()) { std::string info = "value " + result->GetIdentifier() + " has " + std::to_string(args.size()) + " arguments but the function type has " + std::to_string(funcParamsTy.size()) + " parameters."; ErrorInFunc(expr.GetTopLevelFunc(), info); return false; } if (auto callee = Cangjie::DynamicCast(func); callee && callee->IsMemberFunc()) { if (expr.GetThisType() == nullptr) { std::string info = expr.GetResult()->ToString() + ", callee is method func, but don't have ThisType."; ErrorInFunc(expr.GetTopLevelFunc(), info); return false; } } if (!CheckInstantiatedType(expr, Cangjie::StaticCast(func->GetType()), IsConstructor(*func))) { return false; } // check ref info of args for (size_t i = 0; i < args.size(); i++) { bool refOK = true; if (i == 0 && (func->TestAttr(Attribute::MUT) || IsConstructor(*func) || IsInstanceVarInit(*func))) { refOK = CheckFuncArgType(*args[i]->GetType(), funcType->IsCFunc(), true); } else { refOK = CheckFuncArgType(*args[i]->GetType(), funcType->IsCFunc()); } if (!refOK) { std::string errMsg = "reference info of arg " + args[i]->ToString() + " in " + result->ToString() + " is error."; Errorln(errMsg); return false; } } return true; } bool CheckInvokeWithException(const InvokeWithException& expr) const { // Need to adapt (void)expr; return true; } bool CheckGoTo(const GoTo& expr) const { if (auto result = expr.GetResult(); result != nullptr) { TypeCheckError(*result, "nullptr"); return false; } return true; } bool CheckExit(const Exit& expr) const { if (auto result = expr.GetResult(); result != nullptr) { TypeCheckError(*result, "nullptr"); return false; } return true; } bool CheckRaiseException(const RaiseException& expr) const { if (auto result = expr.GetResult(); result != nullptr) { TypeCheckError(*result, "nullptr"); return false; } auto expTy = expr.GetExceptionValue()->GetType(); if (!expTy->IsRef() || !expTy->GetTypeArgs()[0]->IsClass()) { if (!expTy->IsGeneric()) { TypeCheckError(expr, *expr.GetExceptionValue(), "Class&"); return false; } } return true; } bool CheckIntOpWithException(const IntOpWithException& expr) const { auto exprKind = expr.GetOpKind(); if (exprKind >= ExprKind::ADD && exprKind <= ExprKind::MOD) { return CheckCalculExpression(expr); } else if (exprKind == ExprKind::EXP) { return CheckExpExpression(expr); } else if (exprKind >= ExprKind::LSHIFT && exprKind <= ExprKind::BITXOR) { return CheckBitExpression(expr); } else if (exprKind >= ExprKind::LT && exprKind <= ExprKind::NOTEQUAL) { return CheckCmpExpression(expr); } else if (exprKind >= ExprKind::AND && exprKind <= ExprKind::OR) { return CheckLogicExpression(expr); } else if (exprKind >= ExprKind::NEG && exprKind <= ExprKind::BITNOT) { return CheckUnaryExpression(expr); } else { auto result = expr.GetResult(); TypeCheckError(*result, "Add, Sub, Mul, Div, Mod or Exp"); return false; } } bool CheckSpawnWithException(const Expression& expr) const { return CheckSpawn(expr); } bool CheckTypeCastWithException(const TypeCastWithException& expr) const { return CheckTypeCast(expr); } bool CheckIntrinsicWithException(const IntrinsicWithException& expr) const { (void)expr; // To be implemented return true; } bool CheckAllocateWithException(const AllocateWithException& expr) const { auto result = expr.GetResult(); auto resultTy = result->GetType(); auto baseType = expr.GetType(); if (!resultTy->IsRef() || baseType != Cangjie::StaticCast(resultTy)->GetBaseType()) { TypeCheckError(*result, baseType->ToString() + "&"); return false; } if (!CheckGenericArgs(*baseType)) { return false; } return true; } // check result is RawArray& type // check size is Int64 type // check element type is RawArray element type bool CheckRawArrayAllocateWithException(const RawArrayAllocateWithException& expr) const { auto result = expr.GetResult(); if (!result->GetType()->IsRef() || !result->GetType()->GetTypeArgs()[0]->IsRawArray()) { TypeCheckError(*result, "RawArray&"); return false; } auto size = expr.GetSize(); if (!IsExpectedType(*size->GetType(), Type::TypeKind::TYPE_INT64)) { TypeCheckError(expr, *size, "Int64"); return false; } auto rawArrayTy = result->GetType()->GetTypeArgs()[0]; if (!IsExpectedType(*rawArrayTy->GetTypeArgs()[0], *expr.GetElementType())) { TypeCheckError(*result, "RawArray<" + expr.GetElementType()->ToString() + ">&"); return false; } auto elementType = expr.GetElementType(); if (!CheckGenericArgs(*elementType)) { return false; } return true; } bool CheckSpawn(const Expression& expr) const { if (expr.GetOperands().size() != 0) { auto ty = expr.GetOperand(0)->GetType(); if (!(ty->IsRef() && ty->GetTypeArgs()[0]->IsClass())) { TypeCheckError(expr, *expr.GetOperand(0), "closure or ref to class"); return false; } } return true; } // check result is RawArray& type // check size is Int64 type // check element type is RawArray element type bool CheckRawArrayAllocate(const RawArrayAllocate& expr) const { auto result = expr.GetResult(); if (!result->GetType()->IsRef() || !result->GetType()->GetTypeArgs()[0]->IsRawArray()) { TypeCheckError(*result, "RawArray&"); return false; } auto size = expr.GetSize(); if (!IsExpectedType(*size->GetType(), Type::TypeKind::TYPE_INT64)) { TypeCheckError(expr, *size, "Int64"); return false; } auto rawArrayTy = result->GetType()->GetTypeArgs()[0]; if (!IsExpectedType(*rawArrayTy->GetTypeArgs()[0], *expr.GetElementType())) { TypeCheckError(*result, "RawArray<" + expr.GetElementType()->ToString() + ">&"); return false; } auto elementType = expr.GetElementType(); if (!CheckGenericArgs(*elementType)) { return false; } return true; } // check result is Unit type // check operand0 is RawArray type // check other operands is the element type of operand0 bool CheckRawArrayLiteralInit(const RawArrayLiteralInit& expr) const { auto result = expr.GetResult(); if (!result->GetType()->IsUnit()) { TypeCheckError(*result, "Unit"); return false; } auto rawArray = expr.GetRawArray(); if (!rawArray->GetType()->IsRef() || !rawArray->GetType()->GetTypeArgs()[0]->IsRawArray()) { TypeCheckError(expr, *rawArray, "RawArray&"); return false; } auto rawArrayTy = rawArray->GetType()->GetTypeArgs()[0]; for (auto elem : expr.GetElements()) { if (!IsExpectedType(*rawArrayTy->GetTypeArgs()[0], *elem->GetType())) { TypeCheckError(expr, *elem, rawArrayTy->GetTypeArgs()[0]->ToString()); return false; } } return true; } // check result is Unit type // check size is Int64 type // check operand0 is RawArray type // check size is Int64 type // check operand2 is RawArray element type bool CheckRawArrayInitByValue(const RawArrayInitByValue& expr) const { auto result = expr.GetResult(); if (!result->GetType()->IsUnit()) { TypeCheckError(*result, "Unit"); return false; } auto size = expr.GetSize(); if (!IsExpectedType(*size->GetType(), Type::TypeKind::TYPE_INT64)) { TypeCheckError(expr, *size, "Int64"); return false; } auto rawArray = expr.GetRawArray(); if (!rawArray->GetType()->IsRef() || !rawArray->GetType()->GetTypeArgs()[0]->IsRawArray()) { TypeCheckError(expr, *rawArray, "RawArray&"); return false; } auto rawArrayTy = rawArray->GetType()->GetTypeArgs()[0]; if (!IsExpectedType(*rawArrayTy->GetTypeArgs()[0], *expr.GetInitValue()->GetType())) { TypeCheckError(expr, *expr.GetInitValue(), rawArrayTy->GetTypeArgs()[0]->ToString()); return false; } return true; } bool CheckVArray(const VArray& expr) const { auto result = expr.GetResult(); if (!result->GetType()->IsVArray()) { TypeCheckError(*result, "VArray"); return false; } // size check const auto& args = expr.GetOperands(); auto type = Cangjie::StaticCast(result->GetType()); auto elemType = type->GetElementType(); if (type->GetSize() != args.size()) { TypeCheckError(*result, "VArray<" + elemType->ToString() + ", $" + std::to_string(args.size()) + ">"); return false; } // elements type check for (size_t i = 0; i < args.size(); i++) { auto argtype = args[i]->GetType(); if (!IsExpectedType(*elemType, *argtype)) { TypeCheckError(expr, *args[i], elemType->ToString()); return false; } } return true; } bool CheckVArrayBuilder(const VArrayBuilder& expr) const { auto result = expr.GetResult(); auto size = expr.GetOperand(0); if (!IsExpectedType(*size->GetType(), Type::TypeKind::TYPE_INT64)) { TypeCheckError(expr, *size, "Int64"); return false; } if (!result->GetType()->IsVArray()) { TypeCheckError(*result, "VArray"); return false; } return true; } bool CheckIntrinsic(const Intrinsic& expr) const { // To be implemented (void)expr; return true; } bool CheckLambda(const Lambda& expr, std::set& genericTypes) { auto result = expr.GetResult(); if (!result->GetType()->IsFunc()) { TypeCheckError(*result, "Func"); return false; } auto funcTy = expr.GetFuncType(); CJC_NULLPTR_CHECK(funcTy); CJC_NULLPTR_CHECK(expr.GetTopLevelFunc()); auto ret = !expr.GetIdentifier().empty(); ret = CheckFuncParams(expr.GetParams(), *funcTy, *expr.GetTopLevelFunc()) && ret; genericTypes.insert(expr.GetGenericTypeParams().begin(), expr.GetGenericTypeParams().end()); ret = CheckFuncBody(*expr.GetBody(), genericTypes, false) && ret; ret = CheckFuncRetValue(*expr.GetTopLevelFunc(), *funcTy->GetReturnType(), expr.GetReturnValue()) && ret; ret = CheckGenericTypeValidInFunc(*expr.GetBody(), genericTypes) && ret; if (!ret) { expr.Dump(); } for (auto ty : expr.GetGenericTypeParams()) { genericTypes.erase(ty); } return ret; } bool CheckGetInstantiateValue(const GetInstantiateValue& expr) { if (!IsEndPhase()) { return true; } std::string err = "expr: " + expr.ToString() + ", should be removed, can't be seen in CodeGen Stage."; ErrorInFunc(expr.GetTopLevelFunc(), err); return false; } Type* GetBaseTypeFromBoxRefType(Type& type) { auto refBaseType = Cangjie::StaticCast(type).GetBaseType(); return Cangjie::StaticCast(refBaseType)->GetBaseType(); } bool CheckBox(const Box& expr) { auto result = expr.GetResult(); // Box(%1: Int32, BoxType&), is ok if (IsBoxRefType(*result->GetType()) && GetBaseTypeFromBoxRefType(*result->GetType()) == expr.GetSourceTy()) { return true; } if (!IsClassRefType(*result->GetType()) && !IsBoxRefType(*result->GetType())) { TypeCheckError(*result, "class or box reference type"); return false; } if (expr.GetSourceTy()->IsRef() && Cangjie::StaticCast(expr.GetSourceTy())->GetBaseType()->IsReferenceType()) { std::stringstream ms{}; ms << "In value " << expr.ToString() << ":\n" "Operand must be value type, got " << expr.GetSourceTy()->ToString(); Errorln(ms.str()); return false; } return true; } bool CheckUnBox(const UnBox& expr) const { // Need to adapt (void)expr; return true; } bool CheckTransformToGeneric(const TransformToGeneric& expr) { auto result = expr.GetResult(); if (!result->GetType()->IsGenericRelated()) { TypeCheckError(*result, "value type"); return false; } return true; } bool CheckTransformToConcrete(const TransformToConcrete& expr) { if (!expr.GetSourceTy()->IsGenericRelated()) { TypeCheckError(*expr.GetResult(), "value type"); return false; } return true; } bool CheckUnBoxToRef(const UnBoxToRef& expr) { auto expectTy = expr.GetResult()->GetType(); auto isExpTyValueReference = expectTy->IsRef() && Cangjie::StaticCast(expectTy)->GetBaseType()->IsValueType(); if (!isExpTyValueReference) { TypeCheckError(*expr.GetResult(), "value type with reference"); return false; } auto srcTy = expr.GetSourceTy(); auto srcIsReferenceTy = srcTy->IsRef() && Cangjie::StaticCast(srcTy)->GetBaseType()->IsReferenceType(); if (!srcIsReferenceTy) { TypeCheckError(*expr.GetSourceValue(), "reference type"); return false; } return true; } bool IsEndPhase() const { return curCheckPhase == ToCHIR::Phase::OPT || curCheckPhase == ToCHIR::Phase::ANALYSIS_FOR_CJLINT; } }; ToCHIR::Phase IRChecker::curCheckPhase = ToCHIR::Phase::OPT; } // unnamed namespace namespace Cangjie::CHIR { bool IRCheck(const Package& root, const Cangjie::GlobalOptions& opts, CHIRBuilder& builder, const ToCHIR::Phase& phase, std::ostream& out) { return IRChecker::Check(root, out, opts, builder, phase); } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/ImplicitImportedFuncMgr.cpp000066400000000000000000000032041510705540100235660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/ImplicitImportedFuncMgr.h" using namespace Cangjie; using namespace CHIR; ImplicitImportedFuncMgr& ImplicitImportedFuncMgr::Instance() noexcept { static ImplicitImportedFuncMgr instance; return instance; } void ImplicitImportedFuncMgr::RegImplicitImportedFunc(const ImplicitImportedFunc& func, FuncKind funcKind) noexcept { if (funcKind == FuncKind::GENERIC) { implicitImportedGenericFuncs.emplace_back(func); } else if (funcKind == FuncKind::NONE_GENERIC) { implicitImportedNonGenericFuncs.emplace_back(func); } else { CJC_ASSERT(false && "Invalid funcKind."); } } std::vector ImplicitImportedFuncMgr::GetImplicitImportedFuncs(FuncKind funcKind) { static const auto COMP = [](const ImplicitImportedFunc& lhs, const ImplicitImportedFunc& rhs) { return lhs.parentName + lhs.identifier < rhs.parentName + rhs.identifier; }; if (funcKind == FuncKind::GENERIC) { sort(implicitImportedGenericFuncs.begin(), implicitImportedGenericFuncs.end(), COMP); return implicitImportedGenericFuncs; } else if (funcKind == FuncKind::NONE_GENERIC) { sort(implicitImportedNonGenericFuncs.begin(), implicitImportedNonGenericFuncs.end(), COMP); return implicitImportedNonGenericFuncs; } else { CJC_ASSERT(false && "Invalid funcKind."); return {}; } } cangjie_compiler-1.0.7/src/CHIR/Interpreter/000077500000000000000000000000001510705540100206265ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CHIR/Interpreter/BCHIR.cpp000066400000000000000000000232421510705540100221640ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements BCHIR representation. */ #include #include "cangjie/CHIR/Interpreter/BCHIR.h" using namespace Cangjie::CHIR::Interpreter; void Bchir::Definition::Push(OpCode opcode) { CJC_ASSERT(bytecode.size() < BYTECODE_CONTENT_MAX); (void)bytecode.emplace_back(static_cast(opcode)); } void Bchir::Definition::Push(Bchir::ByteCodeContent value) { (void)bytecode.emplace_back(value); } void Bchir::Definition::Push8bytes(uint64_t value) { CJC_ASSERT(bytecode.size() + 1 < BYTECODE_CONTENT_MAX); (void)bytecode.emplace_back(static_cast(value)); (void)bytecode.emplace_back(static_cast(value >> byteCodeContentWidth)); } void Bchir::Definition::Set(ByteCodeIndex index, Bchir::ByteCodeContent value) { CJC_ASSERT(index < bytecode.size()); bytecode[static_cast(index)] = value; } void Bchir::Definition::SetOp(ByteCodeIndex index, OpCode opcode) { CJC_ASSERT(index < bytecode.size()); bytecode[static_cast(index)] = static_cast(opcode); } size_t Bchir::Definition::Size() const { return bytecode.size(); } void Bchir::Definition::Resize(size_t newSize) { CJC_ASSERT(newSize > bytecode.size()); bytecode.resize(newSize); } Bchir::ByteCodeIndex Bchir::Definition::NextIndex() const { CJC_ASSERT(this->Size() <= static_cast(Bchir::BYTECODE_CONTENT_MAX)); return static_cast(this->Size()); } const std::vector& Bchir::Definition::GetByteCode() const { return bytecode; } void Bchir::Definition::SetNumLVars(ByteCodeContent num) { this->numLVars = num; } Bchir::ByteCodeContent Bchir::Definition::GetNumLVars() const { return this->numLVars; } void Bchir::Definition::SetNumArgs(ByteCodeContent num) { this->numArgs = num; } const Bchir::Definition& Bchir::GetLinkedByteCode() const { return linkedByteCode; } void Bchir::Definition::AddMangledNameAnnotation(ByteCodeIndex idx, const std::string& mangledName) { mangledNamesAnnotations.emplace(idx, mangledName); } void Bchir::Definition::AddCodePositionAnnotation(ByteCodeIndex idx, const CodePosition& pos) { codePositionsAnnotations.emplace(idx, pos); } const std::string Bchir::DEFAULT_MANGLED_NAME = ""; const Bchir::CodePosition Bchir::DEFAULT_POSITION = CodePosition{0, 0, 0}; const std::string& Bchir::Definition::GetMangledNameAnnotation(ByteCodeIndex idx) const { auto mangled = mangledNamesAnnotations.find(idx); if (mangled != mangledNamesAnnotations.end()) { return mangled->second; } else { return DEFAULT_MANGLED_NAME; } } const Bchir::CodePosition& Bchir::Definition::GetCodePositionAnnotation(ByteCodeIndex idx) const { auto pos = codePositionsAnnotations.find(idx); if (pos != codePositionsAnnotations.end()) { return pos->second; } else { return DEFAULT_POSITION; } } const std::unordered_map& Bchir::Definition::GetMangledNamesAnnotations() const { return mangledNamesAnnotations; } const std::unordered_map& Bchir::Definition::GetCodePositionsAnnotations() const { return codePositionsAnnotations; } const std::string& Bchir::GetString(size_t index) const { return strings[index]; } size_t Bchir::AddString(std::string str) { (void)strings.emplace_back(std::move(str)); return strings.size() - 1; } const std::vector& Bchir::GetStrings() const { return strings; } IVal* Bchir::StoreStringArray(IArray&& array) { return stringArrays.emplace_back(std::make_unique(std::move(array))).get(); } size_t Bchir::AddType(Cangjie::CHIR::Type& ty) { auto idx = types.size(); types.emplace_back(&ty); return idx; } const std::vector& Bchir::GetTypes() const { return types; } const Cangjie::CHIR::Type* Bchir::GetTypeAt(size_t idx) const { CJC_ASSERT(idx < types.size()); return types.at(idx); } void Bchir::AddFunction(std::string mangledName, Bchir::Definition&& def) { functions.emplace(mangledName, def); } const std::map& Bchir::GetFunctions() const { return functions; } void Bchir::AddGlobalVar(std::string mangledName, Definition&& def) { globalVars.emplace(mangledName, def); } const std::map& Bchir::GetGlobalVars() const { return globalVars; } Bchir::ByteCodeIndex Bchir::GetDefaultFunctionPointer(Bchir::DefaultFunctionKind f) const { CJC_ASSERT(defaultFuncPtrs.size() == static_cast(DefaultFunctionKind::INVALID)); return defaultFuncPtrs[static_cast(f)]; } const std::vector& Bchir::GetDefaultFuncPtrs() const { return defaultFuncPtrs; } const std::string& Bchir::GetMainMangledName() const { return mainMangledName; } void Bchir::LinkDefaultFunctions(const std::unordered_map& mangle2ptr) { for (size_t i = 0; i < defaultFunctionsManledNames.size(); ++i) { auto it = mangle2ptr.find(defaultFunctionsManledNames[i]); if (it != mangle2ptr.end()) { defaultFuncPtrs[i] = it->second; } // else the interpreter was run without linking core } auto mainIt = mangle2ptr.find(mainMangledName); if (mainIt != mangle2ptr.end()) { defaultFuncPtrs[static_cast(DefaultFunctionKind::MAIN)] = mainIt->second; } } void Bchir::SetMainMangledName(const std::string& mangledName) { mainMangledName = mangledName; } size_t Bchir::GetMainExpectedArgs() const { return expectedNumberOfArgumentsByMain; } void Bchir::SetMainExpectedArgs(size_t v) { expectedNumberOfArgumentsByMain = v; } void Bchir::SetAsCore() { isCore = true; } bool Bchir::IsCore() const { return isCore; } const std::vector& Bchir::GetFileNames() const { return fileNames; } size_t Bchir::AddFileName(const std::string& name) { auto idx = fileNames.size(); fileNames.emplace_back(name); return idx; } const std::string& Bchir::GetFileName(size_t idx) const { return fileNames[idx]; } size_t Bchir::GetNumGlobalVars() const { return numGlobalVars; } void Bchir::SetNumGlobalVars(size_t num) { numGlobalVars = num; } void Bchir::SetGlobalInitFunc(const std::string& name) { globalInitFunc = name; } const std::string& Bchir::GetGlobalInitFunc() const { return globalInitFunc; } void Bchir::AddSClass(const std::string& mangledName, SClassInfo&& classInfo) { sClassTable.emplace(mangledName, classInfo); } Bchir::SClassInfo* Bchir::GetSClass(const std::string& mangledName) { auto it = sClassTable.find(mangledName); if (it == sClassTable.end()) { return nullptr; } else { return &it->second; } } const Bchir::SClassInfo* Bchir::GetSClass(const std::string& mangledName) const { auto it = sClassTable.find(mangledName); if (it == sClassTable.end()) { return nullptr; } else { return &it->second; } } const Bchir::SClassTable& Bchir::GetSClassTable() const { return sClassTable; } void Bchir::AddClass(ByteCodeContent id, ClassInfo&& classInfo) { classTable.emplace(id, classInfo); } const Bchir::ClassInfo& Bchir::GetClass(ByteCodeContent id) const { auto it = classTable.find(id); CJC_ASSERT(it != classTable.end()); return it->second; } bool Bchir::ClassExists(ByteCodeContent id) const { return classTable.find(id) != classTable.end(); } Bchir::ByteCodeIndex Bchir::GetClassFinalizer(ByteCodeContent classId) { auto classIt = classTable.find(classId); CJC_ASSERT(classIt != classTable.end()); return classIt->second.finalizerIdx; } const Bchir::ClassTable& Bchir::GetClassTable() const { return classTable; } void Bchir::RemoveFunction(const std::string& name) { functions.erase(name); } void Bchir::RemoveGlobalVar(const std::string& name) { globalVars.erase(name); } void Bchir::RemoveClass(const std::string& name) { sClassTable.erase(name); } void Bchir::Set(ByteCodeIndex index, Bchir::ByteCodeContent value) { linkedByteCode.Set(index, value); } void Bchir::SetOp(ByteCodeIndex index, OpCode opcode) { linkedByteCode.SetOp(index, opcode); } void Bchir::Resize(size_t newSize) { linkedByteCode.Resize(newSize); } const std::string Bchir::throwArithmeticException = "rt$ThrowArithmeticException"; const std::string Bchir::throwOverflowException = "rt$ThrowOverflowException"; const std::string Bchir::throwIndexOutOfBoundsException = "rt$ThrowIndexOutOfBoundsException"; const std::string Bchir::throwNegativeArraySizeException = "rt$ThrowNegativeArraySizeException"; const std::string Bchir::callToString = "rt$CallToString"; const std::string Bchir::throwArithmeticExceptionMsg = "rt$ThrowArithmeticException_msg"; const std::string Bchir::throwOutOfMemoryError = "rt$ThrowOutOfMemoryError"; const std::string Bchir::checkIsError = "rt$CheckIsError"; const std::string Bchir::throwError = "rt$ThrowError"; const std::string Bchir::callPrintStackTrace = "rt$CallPrintStackTrace"; const std::string Bchir::callPrintStackTraceError = "rt$CallPrintStackTraceError"; const std::vector Bchir::defaultFunctionsManledNames = {throwArithmeticException, throwOverflowException, throwIndexOutOfBoundsException, throwNegativeArraySizeException, callToString, throwArithmeticExceptionMsg, throwOutOfMemoryError, checkIsError, throwError, callPrintStackTrace, callPrintStackTraceError, };cangjie_compiler-1.0.7/src/CHIR/Interpreter/BCHIRInterpreter.cpp000066400000000000000000002077431510705540100244220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements BCHIR representation. */ #include "cangjie/CHIR/Interpreter/BCHIRInterpreter.h" #include using namespace Cangjie::CHIR::Interpreter; const IVal& BCHIRInterpreter::PeekValueOfGlobal(Bchir::VarIdx id) const { return env.PeekGlobal(id); } void BCHIRInterpreter::SetGlobalVars(std::unordered_map&& gVarInitIVals) { for (auto& [id, iVal] : gVarInitIVals) { env.SetGlobal(id, std::move(iVal)); } } const Bchir& BCHIRInterpreter::GetBchir() const { return bchir; } void BCHIRInterpreter::Interpret() { CJC_ASSERT(pc == baseIndex); // no bound variables in the top-level thunk while (!interpreterError) { auto current = static_cast(bchir.Get(pc)); #ifndef NDEBUG PrintDebugInfo(pc); #endif // when interpreting an X_EXC operation // 1. set pcExcOffset to 1 // 2. interpret operation X // // after interpreting X operation // 1. increment pc taking into account pcExcOffset // // basically when updating pc after interpreting X, // pcExcOffset is going to 0 if entry point was X or 1 is entry point was X_EXC Bchir::ByteCodeIndex pcExcOffset{0}; switch (current) { case OpCode::ALLOCATE_RAW_ARRAY: { InterpretAllocateRawArray(); continue; } case OpCode::ALLOCATE_EXC: pcExcOffset = 1; // intended missing break // for the time being allocate never raises exception case OpCode::ALLOCATE: { auto ptr = IPointer(); ptr.content = AllocateValue(INullptr()); interpStack.ArgsPush(ptr); pc += 1 + pcExcOffset; continue; } case OpCode::ALLOCATE_STRUCT_EXC: pcExcOffset = 1; // intended missing break // for the time being allocate never raises exception case OpCode::ALLOCATE_STRUCT: { auto numField = bchir.Get(pc + 1); std::vector content; for (size_t i = 0; i < numField; i++) { content.emplace_back(INullptr()); } auto ptr = IPointer(); ptr.content = AllocateValue(ITuple{std::move(content)}); interpStack.ArgsPush(ptr); pc += Bchir::FLAG_TWO + pcExcOffset; continue; } case OpCode::ALLOCATE_CLASS_EXC: pcExcOffset = 1; // intended missing break // for the time being allocate never raises exception case OpCode::ALLOCATE_CLASS: { auto classId = bchir.Get(pc + 1); auto numField = bchir.Get(pc + Bchir::FLAG_TWO); std::vector content; for (size_t i = 0; i < numField; i++) { content.emplace_back(INullptr()); } auto ptr = IPointer(); ptr.content = AllocateValue(IObject{classId, std::move(content)}); interpStack.ArgsPush(ptr); pc += Bchir::FLAG_THREE + pcExcOffset; continue; } case OpCode::FRAME: { auto num = bchir.Get(pc + 1); env.AllocateLocalVarsForFrame(static_cast(num)); pc += Bchir::FLAG_TWO; continue; } case OpCode::LVAR: { auto varIdx = pc + 1; auto var = bchir.Get(varIdx); // update pc for next operation pc += Bchir::FLAG_TWO; interpStack.ArgsPushIValRef(env.GetLocal(var)); continue; } case OpCode::GVAR: { auto varId = bchir.Get(pc + 1); auto& val = env.GetGlobal(varId); interpStack.ArgsPush(IPointer{&val}); // update pc for next operation pc += Bchir::FLAG_TWO; continue; } case OpCode::GVAR_SET: { auto varId = bchir.Get(pc + 1); env.SetGlobal(varId, interpStack.ArgsPopIVal()); pc = pc + 1 + 1; continue; } case OpCode::LVAR_SET: { auto varId = bchir.Get(pc + 1); env.SetLocal(varId, interpStack.ArgsPopIVal()); pc = pc + 1 + 1; continue; } case OpCode::UINT8: { auto valIdx = pc + 1; interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(bchir.Get(valIdx)))); // update pc for next operation pc += Bchir::FLAG_TWO; continue; } case OpCode::UINT16: { auto valIdx = pc + 1; interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(bchir.Get(valIdx)))); // update pc for next operation pc += Bchir::FLAG_TWO; continue; } case OpCode::UINT32: { auto valIdx = pc + 1; interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(bchir.Get(valIdx)))); // update pc for next operation pc += Bchir::FLAG_TWO; continue; } case OpCode::UINT64: { auto valIdx = pc + 1; interpStack.ArgsPush( IValUtils::PrimitiveValue(static_cast(bchir.Get8bytes(valIdx)))); // update pc for next operation pc += Bchir::FLAG_THREE; continue; } case OpCode::UINTNAT: { auto valIdx = pc + 1; #if (defined(__x86_64__) || defined(__aarch64__)) interpStack.ArgsPush( IValUtils::PrimitiveValue(static_cast(bchir.Get8bytes(valIdx)))); #else interpStack.ArgsPush( IValUtils::PrimitiveValue(static_cast(bchir.Get8bytes(valIdx)))); #endif // update pc for next operation pc += Bchir::FLAG_THREE; continue; } case OpCode::INT8: { auto valIdx = pc + 1; interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(bchir.Get(valIdx)))); // update pc for next operation pc += Bchir::FLAG_TWO; continue; } case OpCode::INT16: { auto valIdx = pc + 1; interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(bchir.Get(valIdx)))); // update pc for next operation pc += Bchir::FLAG_TWO; continue; } case OpCode::INT32: { auto valIdx = pc + 1; interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(bchir.Get(valIdx)))); // update pc for next operation pc += Bchir::FLAG_TWO; continue; } case OpCode::INT64: { auto valIdx = pc + 1; interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(bchir.Get8bytes(valIdx)))); // update pc for next operation pc += Bchir::FLAG_THREE; continue; } case OpCode::INTNAT: { auto valIdx = pc + 1; #if (defined(__x86_64__) || defined(__aarch64__)) interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(bchir.Get8bytes(valIdx)))); #else interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(bchir.Get8bytes(valIdx)))); #endif // update pc for next operation pc += Bchir::FLAG_THREE; continue; } case OpCode::FLOAT16: { auto valIdx = pc + 1; auto tmp = bchir.Get(valIdx); // Value for a FLOAT16 instruction is a 32-bit float. float f; auto ret = memcpy_s(&f, sizeof(f), &tmp, sizeof(tmp)); if (ret != EOK) { return; } interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(f))); pc += Bchir::FLAG_TWO; continue; } case OpCode::FLOAT32: { auto valIdx = pc + 1; auto tmp = bchir.Get(valIdx); float f; auto ret = memcpy_s(&f, sizeof(f), &tmp, sizeof(tmp)); if (ret != EOK) { return; } interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(f))); pc += Bchir::FLAG_TWO; continue; } case OpCode::FLOAT64: { auto valIdx = pc + 1; auto tmp = bchir.Get8bytes(valIdx); double d; auto ret = memcpy_s(&d, sizeof(d), &tmp, sizeof(double)); if (ret != EOK) { return; } interpStack.ArgsPush(IValUtils::PrimitiveValue(d)); pc += Bchir::FLAG_THREE; continue; } case OpCode::RUNE: { auto valIdx = pc + 1; interpStack.ArgsPush(IValUtils::PrimitiveValue(bchir.Get(static_cast(valIdx)))); // update pc for next operation pc += Bchir::FLAG_TWO; continue; } case OpCode::BOOL: { auto valIdx = pc + 1; interpStack.ArgsPush(IValUtils::PrimitiveValue(bchir.Get(valIdx))); // update pc for next operation pc += Bchir::FLAG_TWO; continue; } case OpCode::UNIT: { interpStack.ArgsPush(IUnit()); // update pc for next operation pc += 1; continue; } case OpCode::NULLPTR: { interpStack.ArgsPush(INullptr()); // update pc for next operation pc += 1; continue; } case OpCode::STRING: { InterpretString(); pc += Bchir::FLAG_TWO; continue; } case OpCode::TUPLE: { auto sizeIdx = pc + 1; auto size = bchir.Get(sizeIdx); pc = sizeIdx + 1; auto tuple = ITuple(); interpStack.ArgsPop(size, tuple.content); interpStack.ArgsPush(std::move(tuple)); continue; } case OpCode::VARRAY: { auto sizeIdx = pc + 1; auto size = bchir.Get(sizeIdx); auto array = IArray(); interpStack.ArgsPop(size, array.content); interpStack.ArgsPush(std::move(array)); pc = sizeIdx + 1; continue; } case OpCode::VARRAY_GET: { InterpretVArrayGet(); continue; } case OpCode::RAW_ARRAY_LITERAL_INIT: { InterpretRawArrayLiteralInit(); continue; } case OpCode::FUNC: { // FUNC :: THUNK_IDX :: NEXT_OP auto thunkIdx = pc + 1; auto func = IFunc{bchir.Get(thunkIdx)}; interpStack.ArgsPush(func); pc = thunkIdx + 1; continue; } case OpCode::RETURN: { InterpretReturn(); continue; } case OpCode::EXIT: { // we are done return; } case OpCode::DROP: { interpStack.ArgsPopBack(); pc += 1; continue; } case OpCode::JUMP: { pc = bchir.Get(pc + 1); continue; } case OpCode::BRANCH: { auto cond = interpStack.ArgsPop(); if (cond.content) { pc = bchir.Get(pc + 1); } else { pc = bchir.Get(pc + Bchir::FLAG_TWO); } continue; } case OpCode::UN_NEG_EXC: { BinOp(); continue; } case OpCode::BIN_ADD_EXC: { BinOp(); continue; } case OpCode::BIN_SUB_EXC: { BinOp(); continue; } case OpCode::BIN_MUL_EXC: { BinOp(); continue; } case OpCode::BIN_DIV_EXC: { BinOp(); continue; } case OpCode::BIN_MOD_EXC: { BinOp(); continue; } case OpCode::BIN_EXP_EXC: { BinOp(); continue; } case OpCode::BIN_LSHIFT_EXC: { BinOp(); continue; } case OpCode::BIN_RSHIFT_EXC: { BinOp(); continue; } case OpCode::UN_NEG: { BinOp(); continue; } case OpCode::UN_DEC: { BinOp(); continue; } case OpCode::UN_INC: { BinOp(); continue; } case OpCode::UN_NOT: { BinOpFixedBool(); continue; } case OpCode::UN_BITNOT: { BinOp(); continue; } case OpCode::BIN_ADD: { BinOp(); continue; } case OpCode::BIN_SUB: { BinOp(); continue; } case OpCode::BIN_MUL: { BinOp(); continue; } case OpCode::BIN_DIV: { BinOp(); continue; } case OpCode::BIN_MOD: { BinOp(); continue; } case OpCode::BIN_EXP: { BinOp(); continue; } case OpCode::BIN_LT: { BinOp(); continue; } case OpCode::BIN_GT: { BinOp(); continue; } case OpCode::BIN_LE: { BinOp(); continue; } case OpCode::BIN_GE: { BinOp(); continue; } case OpCode::BIN_NOTEQ: { BinOp(); continue; } case OpCode::BIN_EQUAL: { BinOp(); continue; } case OpCode::BIN_BITAND: { BinOp(); continue; } case OpCode::BIN_BITOR: { BinOp(); continue; } case OpCode::BIN_BITXOR: { BinOp(); continue; } case OpCode::BIN_LSHIFT: { BinOp(); continue; } case OpCode::BIN_RSHIFT: { BinOp(); continue; } case OpCode::FIELD_TPL: { InterpretFieldTpl(); continue; } case OpCode::FIELD: { auto fieldIdx = pc + 1; auto field = bchir.Get(fieldIdx); // OPTIMIZE auto arg = interpStack.ArgsPopIVal(); if (auto tuple = IValUtils::GetIf(&arg)) { interpStack.ArgsPushIVal(std::move(tuple->content[field])); } else { CJC_ASSERT(std::holds_alternative(arg)); auto object = IValUtils::Get(std::move(arg)); // -1 because we don't have the class node anymore interpStack.ArgsPushIVal(std::move(object.content[field - 1])); } pc = fieldIdx + 1; continue; } case OpCode::INVOKE_EXC: { InterpretInvoke(); continue; } case OpCode::INVOKE: { InterpretInvoke(); continue; } case OpCode::TYPECAST: { InterpretTypeCast(); if (raiseExnToTopLevel) { return; } continue; } case OpCode::INSTANCEOF: { auto ptr = interpStack.ArgsPop(); auto& obj = IValUtils::Get(*ptr.content); auto lhs = obj.classId; auto rhs = bchir.Get(pc + 1); interpStack.ArgsPush(IBool{IsSubclass(lhs, rhs)}); pc += Bchir::FLAG_TWO; continue; } case OpCode::BOX: { auto classId = bchir.Get(pc + 1); std::vector content; interpStack.ArgsPop(1, content); auto ptr = IPointer(); ptr.content = AllocateValue(IObject{classId, std::move(content)}); interpStack.ArgsPush(std::move(ptr)); pc += Bchir::FLAG_TWO; continue; } case OpCode::UNBOX: { auto ptr = interpStack.ArgsPop(); auto& obj = IValUtils::Get(*ptr.content); auto value = obj.content[0]; interpStack.ArgsPushIVal(std::move(value)); pc++; continue; } case OpCode::UNBOX_REF: { auto ptr = interpStack.ArgsPop(); auto& obj = IValUtils::Get(*ptr.content); ptr.content = &obj.content[0]; // reusing ptr interpStack.ArgsPush(std::move(ptr)); pc++; continue; } case OpCode::APPLY: { InterpretApply(); continue; } case OpCode::APPLY_EXC: { InterpretApply(); continue; } case OpCode::ASG: { auto ptr = interpStack.ArgsPop(); auto value = interpStack.ArgsPopIVal(); *ptr.content = std::move(value); interpStack.ArgsPush(IUnit()); pc += 1; continue; } case OpCode::STOREINREF: { InterpretStoreInRef(); continue; } case OpCode::STORE: { auto ptr = interpStack.ArgsPop(); auto value = interpStack.ArgsPopIVal(); *ptr.content = std::move(value); pc += 1; continue; } case OpCode::DEREF: { InterpretDeref(); continue; } case OpCode::INTRINSIC0: { InterpretIntrinsic(); if (raiseExnToTopLevel) { return; } continue; } case OpCode::INTRINSIC1: { InterpretIntrinsic(); if (raiseExnToTopLevel) { return; } continue; } case OpCode::SWITCH: { InterpretSwitch(); continue; } case OpCode::GETREF: { InterpretGetRef(); continue; } case OpCode::SYSCALL: case OpCode::CAPPLY: case OpCode::ABORT: { if (!isConstEval) { FailWith(pc, "operation not currently supported in const eval", DiagKind::const_eval_unsupported); } interpreterError = true; return; } case OpCode::SPAWN: case OpCode::NOT_SUPPORTED: case OpCode::INVALID: default: { FailWith(pc, "operation not currently supported in interpreter", DiagKind::interp_unsupported, "Interpret", GetOpCodeLabel(current)); CJC_ABORT(); } } } } void BCHIRInterpreter::InterpretString() { // String values in the interpreter must match the definition of strings in the core library /* Possible optimization. Instead of having a section of std::strings literals and converting them to match core library RawArray during interpretation, we can have a section of IArray strings, and during interpretation we just create an IPtr pointing to the corresponding IArray string. This is correct because strings are immutable in CJ. Also, the Unicode size of the string should be calculated on translation */ auto strIdxIdx = pc + 1; auto strIdx = bchir.Get(strIdxIdx); auto& str = bchir.GetString(strIdx); auto array = IValUtils::StringToArray(str); auto ptr = IPointer(); ptr.content = AllocateValue(std::move(array)); // The size is not the size of the array, but the // amount of Unicode characters that it represents int64_t realSize = 0; for (auto& c : str) { if ((c & 0xC0) != 0x80) { realSize++; } } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND (void)realSize; auto tuple = ITuple{{ptr, IValUtils::PrimitiveValue(uint32_t(0)), IValUtils::PrimitiveValue(static_cast(str.size()))}}; #endif interpStack.ArgsPush(std::move(tuple)); } void BCHIRInterpreter::InterpretStoreInRef() { // ASG_TPL | ASG_OBJ :: FIXED_ARGS :: SIZE_OF_PATH :: p1 :: ... pc++; auto target = interpStack.ArgsPop(); auto targetVal = target.content; auto value = interpStack.ArgsPopIVal(); auto pathSize = bchir.Get(pc++); for (uint32_t p = 0; p < pathSize; ++p) { // OPTIMIZE: we can prolly just use ITuple to represent IObject as well and so we dont need // this check if (auto tuple = IValUtils::GetIf(targetVal)) { targetVal = &tuple->content[bchir.Get(pc + p)]; } else if (auto object = IValUtils::GetIf(targetVal)) { // -1 because we don't have the class node anymore targetVal = &object->content[bchir.Get(pc + p)]; } else if (auto arr = IValUtils::GetIf(targetVal)) { // + 1 because content[0] is the array size. auto index = bchir.Get(pc + p) + static_cast(1); CJC_ASSERT(index < arr->content.size()); targetVal = &arr->content[index]; } else { CJC_ABORT(); } } *targetVal = value; interpStack.ArgsPush(IUnit()); pc += pathSize; } void BCHIRInterpreter::InterpretFieldTpl() { pc++; auto pathSize = bchir.Get(pc++); auto tuple = interpStack.ArgsPop(); for (Bchir::ByteCodeContent i = 0; i < pathSize - 1; ++i) { auto innerTpl = std::move(tuple.content[static_cast(bchir.Get(pc++))]); tuple = std::move(IValUtils::Get(innerTpl)); } interpStack.ArgsPushIVal(std::move(tuple.content[static_cast(bchir.Get(pc++))])); } void BCHIRInterpreter::InterpretReturn() { auto ctrl = interpStack.CtrlPop(); auto opCode = static_cast(bchir.Get(ctrl.byteCodePtr)); CJC_ASSERT(opCode == OpCode::CAPPLY || opCode == OpCode::APPLY || opCode == OpCode::INVOKE || opCode == OpCode::APPLY_EXC || opCode == OpCode::INVOKE_EXC); // it might happen that a function doesn't have any variables/atoms CJC_ASSERT(ctrl.envBP <= env.GetBP()); env.RestoreStackFrameTo(ctrl.envBP); switch (static_cast(bchir.Get(ctrl.byteCodePtr))) { case OpCode::CAPPLY: { auto numberOfArgs = bchir.Get(ctrl.byteCodePtr + 1); // in the CAPPLY we need to skip the type of the result and the type of the arguments // ctrl.byteCodePtr + NUMBER_OF_ARGS + ARG_TY_1 + ... + ARG_TY_N + 1 pc = ctrl.byteCodePtr + 1 + numberOfArgs + 1 + 1; break; } case OpCode::INVOKE_EXC: { // skip the index of the exception target bb ctrl.byteCodePtr += 1; // intended missing break [[fallthrough]]; } case OpCode::INVOKE: { // ctrl.byteCodePtr + NUMBER_OF_ARGS + 1 pc = ctrl.byteCodePtr + 1 + 1 + 1; break; } case OpCode::APPLY_EXC: { // skip the index of the exception target bb ctrl.byteCodePtr += 1; // intended missing break [[fallthrough]]; } case OpCode::APPLY: { // ctrl.byteCodePtr + NUMBER_OF_ARGS + 1 pc = ctrl.byteCodePtr + 1 + 1; break; } default: { CJC_ASSERT(false); } } return; } void BCHIRInterpreter::RaiseError(Bchir::ByteCodeIndex, const std::string&) { interpreterError = true; } void BCHIRInterpreter::RaiseArithmeticExceptionMsg(Bchir::ByteCodeIndex sourcePc, const std::string& str) { ReportConstEvalException(sourcePc, "ArithmeticException: " + str); interpreterError = true; } void BCHIRInterpreter::RaiseArithmeticException(Bchir::ByteCodeIndex sourcePc) { ReportConstEvalException(sourcePc, "ArithmeticException"); } void BCHIRInterpreter::RaiseOverflowException(Bchir::ByteCodeIndex sourcePc) { ReportConstEvalException(sourcePc, "OverflowException"); } void BCHIRInterpreter::RaiseIndexOutOfBoundsException(Bchir::ByteCodeIndex sourcePc) { ReportConstEvalException(sourcePc, "IndexOutOfBoundsException"); } void BCHIRInterpreter::RaiseNegativeArraySizeException(Bchir::ByteCodeIndex sourcePc) { ReportConstEvalException(sourcePc, "NegativeArraySizeException"); } void BCHIRInterpreter::RaiseOutOfMemoryError(Bchir::ByteCodeIndex sourcePc) { ReportConstEvalException(sourcePc, "OutOfMemoryError"); } // HACK: Work around exceptions not being const void BCHIRInterpreter::ReportConstEvalException(Bchir::ByteCodeIndex opIdx, std::string exceptionName) { interpreterError = true; Cangjie::Position errorPosition = DEFAULT_POSITION; auto applyPosition = bchir.GetLinkedByteCode().GetCodePositionAnnotation(opIdx); auto fileName = bchir.GetFileName(applyPosition.fileID); auto fileId = diag.GetSourceManager().GetFileID(fileName); if (fileId != -1 && !diag.GetSourceManager().GetSource(static_cast(fileId)).buffer.empty()) { errorPosition = Cangjie::Position(static_cast(fileId), static_cast(applyPosition.line), static_cast(applyPosition.column)); } auto error = diag.Diagnose(errorPosition, DiagKind::const_eval_exception); error.AddNote(exceptionName); } void BCHIRInterpreter::InterpretGetRef() { // GETREF :: SIZE_OF_PATH :: p1 :: ... pc += 1; auto target = interpStack.ArgsPop(); auto targetVal = target.content; auto pathSize = bchir.Get(pc++); for (size_t p = 0; p < pathSize; ++p) { if (auto object = IValUtils::GetIf(targetVal)) { targetVal = &object->content[bchir.Get(static_cast(pc + p))]; } else if (auto tuple = IValUtils::GetIf(targetVal)) { targetVal = &tuple->content[bchir.Get(static_cast(pc + p))]; } else { auto array = IValUtils::GetIf(targetVal); CJC_ASSERT(array != nullptr); targetVal = &array->content[bchir.Get(pc + static_cast(p)) + 1]; // index 0 of array is the size of the array } } auto ptr = IPointer(); ptr.content = targetVal; interpStack.ArgsPush(ptr); pc += pathSize; } template bool BCHIRInterpreter::CastOrRaiseExceptionForInt(SourceTyRaw v, OverflowStrategy strategy, Bchir::ByteCodeIndex opIdx) { TargetTyRaw res = 0; bool isOverflow = CHIR::OverflowChecker::IsTypecastOverflowForInt(v, &res, strategy); if (isOverflow && strategy == OverflowStrategy::THROWING) { RaiseOverflowException(opIdx); return true; } else { interpStack.ArgsPush(IValUtils::PrimitiveValue(res)); return false; } } template static bool IsTypecastOverflowForFloat(T x, K* res) { CJC_NULLPTR_CHECK(res); CJC_ASSERT((std::is_same::value) || (std::is_same::value)); bool bMax = x > static_cast(std::numeric_limits::max()); bool bMin = x < static_cast(std::numeric_limits::min()); bool isOverflow = bMax || bMin; *res = static_cast(x); return isOverflow; } template bool BCHIRInterpreter::CastOrRaiseExceptionForFloat(SourceTyRaw floatVal, Bchir::ByteCodeIndex opIdx) { CJC_ASSERT((std::is_same::value) || (std::is_same::value)); TargetTyRaw res = 0; bool isOverflow = IsTypecastOverflowForFloat(floatVal, &res); // overflow semantics for floats is always THROWING if (isOverflow) { RaiseOverflowException(opIdx); return true; } else { interpStack.ArgsPush(IValUtils::PrimitiveValue(res)); return false; } } template bool BCHIRInterpreter::InterpretTypeCastForInt( T val, CHIR::Type::TypeKind targetKind, OverflowStrategy strategy, Bchir::ByteCodeIndex opIdx) { switch (targetKind) { case CHIR::Type::TypeKind::TYPE_RUNE: return CastOrRaiseExceptionForInt(val, strategy, opIdx); case CHIR::Type::TypeKind::TYPE_INT8: return CastOrRaiseExceptionForInt(val, strategy, opIdx); case CHIR::Type::TypeKind::TYPE_INT16: return CastOrRaiseExceptionForInt(val, strategy, opIdx); case CHIR::Type::TypeKind::TYPE_INT32: return CastOrRaiseExceptionForInt(val, strategy, opIdx); case CHIR::Type::TypeKind::TYPE_INT64: return CastOrRaiseExceptionForInt(val, strategy, opIdx); case CHIR::Type::TypeKind::TYPE_INT_NATIVE: #if (defined(__x86_64__) || defined(__aarch64__)) return CastOrRaiseExceptionForInt(val, strategy, opIdx); #else return CastOrRaiseExceptionForInt(val, strategy, opIdx); #endif case CHIR::Type::TypeKind::TYPE_UINT8: return CastOrRaiseExceptionForInt(val, strategy, opIdx); case CHIR::Type::TypeKind::TYPE_UINT16: return CastOrRaiseExceptionForInt(val, strategy, opIdx); case CHIR::Type::TypeKind::TYPE_UINT32: return CastOrRaiseExceptionForInt(val, strategy, opIdx); case CHIR::Type::TypeKind::TYPE_UINT64: return CastOrRaiseExceptionForInt(val, strategy, opIdx); case CHIR::Type::TypeKind::TYPE_UINT_NATIVE: return CastOrRaiseExceptionForInt(val, strategy, opIdx); case CHIR::Type::TypeKind::TYPE_FLOAT16: interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(val))); return false; case CHIR::Type::TypeKind::TYPE_FLOAT32: interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(val))); return false; case CHIR::Type::TypeKind::TYPE_FLOAT64: interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(val))); return false; default: { FailWith(opIdx, "interpreter cannot perform integer typecast", DiagKind::interp_cannot_interp_node, "InterpretTypeCastForInt"); return true; } } } template bool BCHIRInterpreter::InterpretTypeCastForFloat( T floatVal, CHIR::Type::TypeKind targetKind, Bchir::ByteCodeIndex opIdx) { CJC_ASSERT((std::is_same::value) || (std::is_same::value)); switch (targetKind) { case CHIR::Type::TypeKind::TYPE_INT8: return CastOrRaiseExceptionForFloat(floatVal, opIdx); case CHIR::Type::TypeKind::TYPE_INT16: return CastOrRaiseExceptionForFloat(floatVal, opIdx); case CHIR::Type::TypeKind::TYPE_INT32: return CastOrRaiseExceptionForFloat(floatVal, opIdx); case CHIR::Type::TypeKind::TYPE_INT64: return CastOrRaiseExceptionForFloat(floatVal, opIdx); case CHIR::Type::TypeKind::TYPE_INT_NATIVE: #if (defined(__x86_64__) || defined(__aarch64__)) return CastOrRaiseExceptionForFloat(floatVal, opIdx); #else return CastOrRaiseExceptionForFloat(floatVal, opIdx); #endif case CHIR::Type::TypeKind::TYPE_UINT8: return CastOrRaiseExceptionForFloat(floatVal, opIdx); case CHIR::Type::TypeKind::TYPE_UINT16: return CastOrRaiseExceptionForFloat(floatVal, opIdx); case CHIR::Type::TypeKind::TYPE_UINT32: return CastOrRaiseExceptionForFloat(floatVal, opIdx); case CHIR::Type::TypeKind::TYPE_UINT64: return CastOrRaiseExceptionForFloat(floatVal, opIdx); case CHIR::Type::TypeKind::TYPE_UINT_NATIVE: return CastOrRaiseExceptionForFloat(floatVal, opIdx); case CHIR::Type::TypeKind::TYPE_FLOAT16: interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(floatVal))); return false; case CHIR::Type::TypeKind::TYPE_FLOAT32: interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(floatVal))); return false; case CHIR::Type::TypeKind::TYPE_FLOAT64: interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(floatVal))); return false; default: { FailWith(opIdx, "interpreter cannot perform float typecast", DiagKind::interp_cannot_interp_node, "InterpretTypeCastForFloat"); return true; } } } template void BCHIRInterpreter::InterpretTypeCast() { auto opIdx = pc; pc += 1; auto srcKind = static_cast(bchir.Get(pc++)); auto targetKind = static_cast(bchir.Get(pc++)); auto strat = static_cast(bchir.Get(pc++)); bool raisedExc = false; switch (srcKind) { case CHIR::Type::TypeKind::TYPE_RUNE: { if (targetKind == CHIR::Type::TypeKind::TYPE_UINT32) { interpStack.ArgsPush(IValUtils::PrimitiveValue(interpStack.ArgsPop().content)); } else { CJC_ASSERT(targetKind == CHIR::Type::TypeKind::TYPE_UINT64); interpStack.ArgsPush(IValUtils::PrimitiveValue(interpStack.ArgsPop().content)); } break; } case CHIR::Type::TypeKind::TYPE_INT8: raisedExc = InterpretTypeCastForInt(interpStack.ArgsPop().content, targetKind, strat, opIdx); break; case CHIR::Type::TypeKind::TYPE_INT16: raisedExc = InterpretTypeCastForInt(interpStack.ArgsPop().content, targetKind, strat, opIdx); break; case CHIR::Type::TypeKind::TYPE_INT32: raisedExc = InterpretTypeCastForInt(interpStack.ArgsPop().content, targetKind, strat, opIdx); break; case CHIR::Type::TypeKind::TYPE_INT64: raisedExc = InterpretTypeCastForInt(interpStack.ArgsPop().content, targetKind, strat, opIdx); break; case CHIR::Type::TypeKind::TYPE_INT_NATIVE: raisedExc = InterpretTypeCastForInt(interpStack.ArgsPop().content, targetKind, strat, opIdx); break; case CHIR::Type::TypeKind::TYPE_UINT8: raisedExc = InterpretTypeCastForInt(interpStack.ArgsPop().content, targetKind, strat, opIdx); break; case CHIR::Type::TypeKind::TYPE_UINT16: raisedExc = InterpretTypeCastForInt(interpStack.ArgsPop().content, targetKind, strat, opIdx); break; case CHIR::Type::TypeKind::TYPE_UINT32: raisedExc = InterpretTypeCastForInt(interpStack.ArgsPop().content, targetKind, strat, opIdx); break; case CHIR::Type::TypeKind::TYPE_UINT64: raisedExc = InterpretTypeCastForInt(interpStack.ArgsPop().content, targetKind, strat, opIdx); break; case CHIR::Type::TypeKind::TYPE_UINT_NATIVE: raisedExc = InterpretTypeCastForInt(interpStack.ArgsPop().content, targetKind, strat, opIdx); break; case CHIR::Type::TypeKind::TYPE_FLOAT16: raisedExc = InterpretTypeCastForFloat(interpStack.ArgsPop().content, targetKind, opIdx); break; case CHIR::Type::TypeKind::TYPE_FLOAT32: raisedExc = InterpretTypeCastForFloat(interpStack.ArgsPop().content, targetKind, opIdx); break; case CHIR::Type::TypeKind::TYPE_FLOAT64: raisedExc = InterpretTypeCastForFloat(interpStack.ArgsPop().content, targetKind, opIdx); break; case CHIR::Type::TypeKind::TYPE_BOOLEAN: raisedExc = InterpretTypeCastForInt(interpStack.ArgsPop().content, targetKind, strat, opIdx); break; case CHIR::Type::TypeKind::TYPE_INVALID: default: { CJC_ABORT(); } }; (void)raisedExc; } template void BCHIRInterpreter::InterpretApply() { // APPLY :: NUMBER_OF_ARGS auto numberArgsIdx = pc + 1; size_t numberArgs = bchir.Get(numberArgsIdx); CJC_ASSERT(numberArgs > 0); CJC_ASSERT(interpStack.ArgsSize() >= numberArgs); // argStack = ... :: FUNC :: ARG_1 :: ... :: ARG_N auto func = IValUtils::Get(interpStack.ArgsGet(numberArgs, 0)); auto funcThunkIdx = func.content; // add apply to opStack so that we know where to continue when we reach RETURN interpStack.CtrlPush({op, funcThunkIdx, pc, env.GetBP()}); env.StartStackFrame(); pc = static_cast(funcThunkIdx); } Bchir::ByteCodeIndex BCHIRInterpreter::FindMethod(Bchir::ByteCodeContent classId, Bchir::ByteCodeContent nameId) { auto classInfoIt = bchir.GetClassTable().find(classId); CJC_ASSERT(classInfoIt != bchir.GetClassTable().end()); auto& classInfo = classInfoIt->second; auto methodIt = classInfo.vtable.find(nameId); CJC_ASSERT(methodIt != classInfo.vtable.end()); return methodIt->second; } bool BCHIRInterpreter::IsSubclass(Bchir::ByteCodeContent lhs, Bchir::ByteCodeContent rhs) { if (lhs == rhs) { return true; } auto& classTable = bchir.GetClassTable(); auto it = classTable.find(lhs); CJC_ASSERT(it != classTable.end()); return it->second.superClasses.find(rhs) != it->second.superClasses.end(); } template void BCHIRInterpreter::InterpretInvoke() { // INVOKE :: NUMBER_OF_ARGS auto numberArgsIdx = pc + 1; size_t numberArgs = bchir.Get(numberArgsIdx); size_t nameId = bchir.Get(numberArgsIdx + 1); CJC_ASSERT(numberArgs > 0); CJC_ASSERT(interpStack.ArgsSize() >= numberArgs); // argStack = ... :: DUMMY :: PTR :: ARG_1 :: ... :: ARG_N auto ptr = IValUtils::Get(interpStack.ArgsGet(numberArgs, 0)); auto& object = IValUtils::Get(*ptr.content); auto classId = object.classId; auto funcThunkIdx = FindMethod(classId, static_cast(nameId)); // add apply to opStack so that we know where to continue when we reach RETURN interpStack.CtrlPush({op, funcThunkIdx, pc, env.GetBP()}); env.StartStackFrame(); pc = funcThunkIdx; } IPointer BCHIRInterpreter::ToArena(IVal&& value) { auto ptr = IPointer(); ptr.content = AllocateValue(std::move(value)); return ptr; } void BCHIRInterpreter::InterpretDeref() { auto ptr = interpStack.ArgsPop(); pc += 1; interpStack.ArgsPushIValRef(*ptr.content); } static constexpr int INSTRUCTION_DIFF{3}; template bool BCHIRInterpreter::PushIfNotOverflow(bool overflow, S res, Cangjie::OverflowStrategy strat) { if (overflow && strat == OverflowStrategy::THROWING) { // pc is already pointing to the next instruction, // thus sourcePc = pc - 1 (strategy) - 1 (kind idx) - 1 RaiseOverflowException(pc - INSTRUCTION_DIFF); return true; } else if (strat == OverflowStrategy::CHECKED) { if (overflow) { // option type - None auto enumNone = ITuple{{IValUtils::PrimitiveValue(true)}}; interpStack.ArgsPush(std::move(enumNone)); } else { // option type - Some(res) auto enumSome = ITuple{{IValUtils::PrimitiveValue(false), IValUtils::PrimitiveValue(res)}}; interpStack.ArgsPush(std::move(enumSome)); } } else { interpStack.ArgsPush(IValUtils::PrimitiveValue(res)); } return false; } template bool BCHIRInterpreter::BinOpInt(Cangjie::OverflowStrategy strat) { if constexpr (op == OpCode::BIN_EXP || op == OpCode::BIN_EXP_EXC) { // this is a special case: only Int64 ** UInt64 is allowed by the spec. CJC_ASSERT((std::is_same_v)); CJC_ASSERT((std::is_same_v)); return BinExpOpInt(strat); } else if constexpr (op == OpCode::UN_NEG || op == OpCode::UN_NEG_EXC) { // this is another special case because SUB is a unary operator auto a = interpStack.ArgsPop(); S res; bool overflow = CHIR::OverflowChecker::IsOverflowAfterSub(0, a.content, strat, &res); return PushIfNotOverflow(overflow, res, strat); } else if constexpr (op == OpCode::UN_INC) { // this is another special case because INC is a unary operator auto a = interpStack.ArgsPop(); S res; bool overflow = CHIR::OverflowChecker::IsOverflowAfterAdd(a.content, 1, strat, &res); return PushIfNotOverflow(overflow, res, strat); } else if constexpr (op == OpCode::UN_DEC) { // this is another special case because DEC is a unary operator auto a = interpStack.ArgsPop(); S res; bool overflow = CHIR::OverflowChecker::IsOverflowAfterSub(a.content, 1, strat, &res); return PushIfNotOverflow(overflow, res, strat); } else if constexpr (op == OpCode::UN_BITNOT) { auto a1 = interpStack.ArgsPop(); S res = ~a1.content; interpStack.ArgsPush(IValUtils::PrimitiveValue(res)); return false; } else if constexpr (op == OpCode::BIN_LSHIFT || op == OpCode::BIN_RSHIFT || op == OpCode::BIN_LSHIFT_EXC || op == OpCode::BIN_RSHIFT_EXC) { return BinShiftOpInt(); } else { return BinRegOpInt(strat); } } template static bool IsOverflowAfterExp(T x, T y, Cangjie::OverflowStrategy strategy, T* res) { CJC_NULLPTR_CHECK(res); *res = 1; bool isOverflow = false; for (T j = 1; j <= y; j++) { if (__builtin_mul_overflow(x, *res, res) && !isOverflow) { isOverflow = true; } } if (isOverflow && strategy == Cangjie::OverflowStrategy::SATURATING) { T magicNumber = 2; if (x < 0 && (y % magicNumber == 0)) { *res = std::numeric_limits::max(); } else if (x < 0 && (y % magicNumber == 1)) { *res = std::numeric_limits::min(); } else { *res = std::numeric_limits::max(); } } return isOverflow; } bool BCHIRInterpreter::BinExpOpInt(Cangjie::OverflowStrategy strat) { auto a2 = interpStack.ArgsPop(); auto a1 = interpStack.ArgsPop(); int64_t res; bool overflow = CHIR::OverflowChecker::IsExpOverflow(a1.content, a2.content, strat, &res); return PushIfNotOverflow(overflow, res, strat); } template bool BCHIRInterpreter::BinRegOpInt(Cangjie::OverflowStrategy strat) { auto a2 = interpStack.ArgsPop(); auto a1 = interpStack.ArgsPop(); S gRes; if constexpr (op == OpCode::BIN_ADD || op == OpCode::BIN_ADD_EXC) { auto isOverflow = CHIR::OverflowChecker::IsOverflowAfterAdd(a1.content, a2.content, strat, &gRes); return PushIfNotOverflow(isOverflow, gRes, strat); } else if constexpr (op == OpCode::BIN_SUB || op == OpCode::BIN_SUB_EXC) { auto isOverflow = CHIR::OverflowChecker::IsOverflowAfterSub(a1.content, a2.content, strat, &gRes); return PushIfNotOverflow(isOverflow, gRes, strat); } else if constexpr (op == OpCode::BIN_MUL || op == OpCode::BIN_MUL_EXC) { auto isOverflow = CHIR::OverflowChecker::IsOverflowAfterMul(a1.content, a2.content, strat, &gRes); return PushIfNotOverflow(isOverflow, gRes, strat); } else if constexpr (op == OpCode::BIN_DIV || op == OpCode::BIN_DIV_EXC) { if (a2.content == 0) { // pc is already pointing to the next instruction, // thus sourcePc = pc - 1 (strategy) - 1 (kind idx) - 1 RaiseArithmeticException(pc - INSTRUCTION_DIFF); return true; } auto isOverflow = CHIR::OverflowChecker::IsOverflowAfterDiv(a1.content, a2.content, strat, &gRes); return PushIfNotOverflow(isOverflow, gRes, strat); } else if constexpr (op == OpCode::BIN_MOD || op == OpCode::BIN_MOD_EXC) { if (a2.content == 0) { // pc is already pointing to the next instruction, // thus sourcePc = pc - 1 (strategy) - 1 (kind idx) - 1 RaiseArithmeticException(pc - INSTRUCTION_DIFF); return true; } auto isOverflow = CHIR::OverflowChecker::IsOverflowAfterMod(a1.content, a2.content, &gRes); return PushIfNotOverflow(isOverflow, gRes, strat); } else if constexpr (op == OpCode::BIN_BITAND) { // BIN_BITOR, BIN_BITAND and BIN_BITXOR Bitwise operations can not raise exceptions // but these operations are handled like the rest to keep code homogeneity S res = a1.content & a2.content; interpStack.ArgsPush(IValUtils::PrimitiveValue(res)); return false; } else if constexpr (op == OpCode::BIN_BITOR) { S res = a1.content | a2.content; interpStack.ArgsPush(IValUtils::PrimitiveValue(res)); return false; } else if constexpr (op == OpCode::BIN_BITXOR) { S res = a1.content ^ a2.content; interpStack.ArgsPush(IValUtils::PrimitiveValue(res)); return false; } else if constexpr (op == OpCode::BIN_EQUAL || op == OpCode::BIN_NOTEQ || op == OpCode::BIN_LT || op == OpCode::BIN_GT || op == OpCode::BIN_LE || op == OpCode::BIN_GE) { return BinOpCompare(a1.content, a2.content); } else { CJC_ABORT(); } return false; } template bool BCHIRInterpreter::BinShiftOpIntCase() { auto a2 = interpStack.ArgsPop(); auto a1 = interpStack.ArgsPop(); if constexpr (std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) { if (a2.content < 0) { const std::string msg = "Overshift: Value of right operand is less than 0!"; // pc is already pointing to the next instruction, // thus sourcePc = pc - 1 (rhs kind idx) - 1 (strategy) - 1 (kind idx) - 1 RaiseArithmeticExceptionMsg(pc - Bchir::FLAG_FOUR, msg); return true; } } auto shiftValue = static_cast(a2.content); if (shiftValue >= static_cast(IValUtils::SizeOf())) { const std::string msg = "Overshift: Value of right operand is greater than or equal to the width of left operand!"; // pc is already pointing to the next instruction, // thus sourcePc = pc - 1 (rhs kind idx) - 1 (strategy) - 1 (kind idx) - 1 RaiseArithmeticExceptionMsg(pc - Bchir::FLAG_FOUR, msg); return true; } if constexpr (op == OpCode::BIN_LSHIFT || op == OpCode::BIN_LSHIFT_EXC) { interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(a1.content << shiftValue))); } else { static_assert((op == OpCode::BIN_RSHIFT || op == OpCode::BIN_RSHIFT_EXC)); interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(a1.content >> shiftValue))); } return false; } template bool BCHIRInterpreter::BinShiftOpInt() { auto rhsTy = static_cast(bchir.Get(pc++)); switch (rhsTy) { case CHIR::Type::TypeKind::TYPE_INT8: { return BinShiftOpIntCase(); } case CHIR::Type::TypeKind::TYPE_INT16: { return BinShiftOpIntCase(); } case CHIR::Type::TypeKind::TYPE_INT32: { return BinShiftOpIntCase(); } case CHIR::Type::TypeKind::TYPE_INT64: { return BinShiftOpIntCase(); } case CHIR::Type::TypeKind::TYPE_INT_NATIVE: { return BinShiftOpIntCase(); } case CHIR::Type::TypeKind::TYPE_UINT8: { return BinShiftOpIntCase(); } case CHIR::Type::TypeKind::TYPE_UINT16: { return BinShiftOpIntCase(); } case CHIR::Type::TypeKind::TYPE_UINT32: { return BinShiftOpIntCase(); } case CHIR::Type::TypeKind::TYPE_UINT64: { return BinShiftOpIntCase(); } case CHIR::Type::TypeKind::TYPE_UINT_NATIVE: { return BinShiftOpIntCase(); } default: CJC_ABORT(); } return false; } template bool BCHIRInterpreter::BinOpFloat() { if constexpr (op == OpCode::BIN_EXP) { // this is a special case because the type of the values are different auto rhs = interpStack.ArgsPopIVal(); auto a1 = interpStack.ArgsPop(); if constexpr (std::is_same_v) { CJC_ASSERT(typeid(S) == typeid(double)); if (auto a2 = IValUtils::GetIf(&rhs)) { interpStack.ArgsPush( IValUtils::PrimitiveValue(pow(a1.content, static_cast(a2->content)))); return false; } else { CJC_ASSERT(std::holds_alternative(rhs)); auto ea2 = IValUtils::Get(std::move(rhs)); interpStack.ArgsPush(IValUtils::PrimitiveValue(pow(a1.content, ea2.content))); return false; } } auto a2 = IValUtils::Get(std::move(rhs)); interpStack.ArgsPush(IValUtils::PrimitiveValue(static_cast(pow(a1.content, a2.content)))); } auto a2 = interpStack.ArgsPop(); if constexpr (op == OpCode::UN_NEG) { // this is a special case because SUB is a unary operator interpStack.ArgsPush(IValUtils::PrimitiveValue(-a2.content)); return false; } auto a1 = interpStack.ArgsPop(); if constexpr (op == OpCode::BIN_ADD) { interpStack.ArgsPush(IValUtils::PrimitiveValue(a1.content + a2.content)); } else if constexpr (op == OpCode::BIN_SUB) { interpStack.ArgsPush(IValUtils::PrimitiveValue(a1.content - a2.content)); } else if constexpr (op == OpCode::BIN_MUL) { interpStack.ArgsPush(IValUtils::PrimitiveValue(a1.content * a2.content)); } else if constexpr (op == OpCode::BIN_DIV) { interpStack.ArgsPush(IValUtils::PrimitiveValue(a1.content / a2.content)); } else if constexpr (op == OpCode::BIN_EQUAL || op == OpCode::BIN_NOTEQ || op == OpCode::BIN_LT || op == OpCode::BIN_GT || op == OpCode::BIN_LE || op == OpCode::BIN_GE) { return BinOpCompare(a1.content, a2.content); } else { CJC_ABORT(); } return false; } template bool BCHIRInterpreter::BinOpTyKindAndOverflowStrat(CHIR::Type::TypeKind kind, Cangjie::OverflowStrategy strat) { switch (kind) { case CHIR::Type::TypeKind::TYPE_UINT8: { return BinOpInt(strat); } case CHIR::Type::TypeKind::TYPE_UINT16: { return BinOpInt(strat); } case CHIR::Type::TypeKind::TYPE_UINT32: { return BinOpInt(strat); } case CHIR::Type::TypeKind::TYPE_UINT64: { return BinOpInt(strat); } case CHIR::Type::TypeKind::TYPE_UINT_NATIVE: { return BinOpInt(strat); } case CHIR::Type::TypeKind::TYPE_INT8: { return BinOpInt(strat); } case CHIR::Type::TypeKind::TYPE_INT16: { return BinOpInt(strat); } case CHIR::Type::TypeKind::TYPE_INT32: { return BinOpInt(strat); } case CHIR::Type::TypeKind::TYPE_INT64: { return BinOpInt(strat); } case CHIR::Type::TypeKind::TYPE_INT_NATIVE: { #if (defined(__x86_64__) || defined(__aarch64__)) return BinOpInt(strat); #else return BinOpInt(strat); #endif } case CHIR::Type::TypeKind::TYPE_FLOAT16: { return BinOpFloat(); } case CHIR::Type::TypeKind::TYPE_FLOAT32: { return BinOpFloat(); } case CHIR::Type::TypeKind::TYPE_FLOAT64: { return BinOpFloat(); } case CHIR::Type::TypeKind::TYPE_BOOLEAN: { return BinOpBool(); } case CHIR::Type::TypeKind::TYPE_RUNE: { return BinOpRune(); } case CHIR::Type::TypeKind::TYPE_UNIT: { return BinOpUnit(); } default: CJC_ABORT(); } return false; } template void BCHIRInterpreter::BinOp() { auto initPc = pc; auto kindIdx = pc + 1; auto stratIdx = kindIdx + 1; auto kind = static_cast(bchir.Get(kindIdx)); auto strat = static_cast(bchir.Get(stratIdx)); pc = stratIdx + 1; if constexpr (OpHasExceptionHandler(op)) { interpStack.CtrlPush({op, 0, initPc, env.GetBP()}); } auto raisedException = BinOpTyKindAndOverflowStrat(kind, strat); if constexpr (OpHasExceptionHandler(op)) { if (!raisedException) { CJC_ASSERT(interpStack.CtrlTop().opCode == op); CJC_ASSERT(interpStack.CtrlTop().byteCodePtr == initPc); interpStack.CtrlDrop(); pc++; } } else { (void)initPc; (void)raisedException; } } template void BCHIRInterpreter::BinOpFixedBool() { pc = pc + Bchir::FLAG_THREE; BinOpBool(); } template bool BCHIRInterpreter::BinOpBool() { if constexpr (op == OpCode::UN_NOT) { auto v = interpStack.ArgsPop(); interpStack.ArgsPush(IValUtils::PrimitiveValue(!v.content)); } else if constexpr (op == OpCode::BIN_EQUAL) { auto v1 = interpStack.ArgsPop(); auto v2 = interpStack.ArgsPop(); interpStack.ArgsPush(IValUtils::PrimitiveValue(v1.content == v2.content)); } else if constexpr (op == OpCode::BIN_NOTEQ) { auto v1 = interpStack.ArgsPop(); auto v2 = interpStack.ArgsPop(); interpStack.ArgsPush(IValUtils::PrimitiveValue(v1.content != v2.content)); } else { // should not happen CJC_ABORT(); } return false; } template bool BCHIRInterpreter::BinOpRune() { auto v2 = interpStack.ArgsPop(); auto v1 = interpStack.ArgsPop(); if constexpr (op == OpCode::BIN_EQUAL || op == OpCode::BIN_NOTEQ || op == OpCode::BIN_LT || op == OpCode::BIN_GT || op == OpCode::BIN_LE || op == OpCode::BIN_GE) { return BinOpCompare(v1.content, v2.content); } else { // should not happen CJC_ABORT(); } return false; } template bool BCHIRInterpreter::BinOpCompare(T x, T y) { if constexpr (op == OpCode::BIN_EQUAL) { #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wfloat-equal" #endif interpStack.ArgsPush(IValUtils::PrimitiveValue(x == y)); #if defined(__clang__) #pragma clang diagnostic pop #endif } else if constexpr (op == OpCode::BIN_NOTEQ) { #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wfloat-equal" #endif interpStack.ArgsPush(IValUtils::PrimitiveValue(x != y)); #if defined(__clang__) #pragma clang diagnostic pop #endif } else if constexpr (op == OpCode::BIN_LT) { interpStack.ArgsPush(IValUtils::PrimitiveValue(x < y)); } else if constexpr (op == OpCode::BIN_GT) { interpStack.ArgsPush(IValUtils::PrimitiveValue(x > y)); } else if constexpr (op == OpCode::BIN_LE) { interpStack.ArgsPush(IValUtils::PrimitiveValue(x <= y)); } else if constexpr (op == OpCode::BIN_GE) { interpStack.ArgsPush(IValUtils::PrimitiveValue(x >= y)); } return false; } template bool BCHIRInterpreter::BinOpUnit() { CJC_ASSERT(std::holds_alternative(interpStack.ArgsTopIVal())); interpStack.ArgsPopBack(); CJC_ASSERT(std::holds_alternative(interpStack.ArgsTopIVal())); interpStack.ArgsPopBack(); if constexpr (op == OpCode::BIN_EQUAL) { interpStack.ArgsPush(IValUtils::PrimitiveValue(true)); } else if constexpr (op == OpCode::BIN_NOTEQ) { interpStack.ArgsPush(IValUtils::PrimitiveValue(false)); } else { // should not happen CJC_ABORT(); } return false; } IResult BCHIRInterpreter::Run(size_t baseIdx, bool expectsReturn) { baseIndex = static_cast(baseIdx); pc = baseIndex; playgroundIdx = playgroundIdxBase; raiseExnToTopLevel = false; Interpret(); if (interpreterError || raiseExnToTopLevel) { return IException{IInvalid{}}; } else if (!expectsReturn) { // this happens when we run the top-level CJC_ASSERT(interpStack.ArgsSize() == 0); CJC_ASSERT(interpStack.CtrlIsEmpty()); result = INotRun{}; return result; } else { CJC_ASSERT(interpStack.ArgsSize() == 1); CJC_ASSERT(interpStack.CtrlIsEmpty()); result = ISuccess{interpStack.ArgsPopIVal()}; return result; } } const IResult& BCHIRInterpreter::GetLastResult() const { return result; } void BCHIRInterpreter::InterpretSwitch() { pc += 1; switch (static_cast(bchir.Get(pc))) { case CHIR::Type::TypeKind::TYPE_UINT8: { InterpretSwitchWithType(); break; } case CHIR::Type::TypeKind::TYPE_UINT16: { InterpretSwitchWithType(); break; } case CHIR::Type::TypeKind::TYPE_UINT32: { InterpretSwitchWithType(); break; } case CHIR::Type::TypeKind::TYPE_UINT64: { InterpretSwitchWithType(); break; } case CHIR::Type::TypeKind::TYPE_UINT_NATIVE: { InterpretSwitchWithType(); break; } case CHIR::Type::TypeKind::TYPE_INT8: { InterpretSwitchWithType(); break; } case CHIR::Type::TypeKind::TYPE_INT16: { InterpretSwitchWithType(); break; } case CHIR::Type::TypeKind::TYPE_INT32: { InterpretSwitchWithType(); break; } case CHIR::Type::TypeKind::TYPE_INT64: { InterpretSwitchWithType(); break; } case CHIR::Type::TypeKind::TYPE_INT_NATIVE: { InterpretSwitchWithType(); break; } case CHIR::Type::TypeKind::TYPE_RUNE: { InterpretSwitchWithType(); break; } case CHIR::Type::TypeKind::TYPE_BOOLEAN: { InterpretSwitchWithType(); break; } default: { CJC_ABORT(); } } } template void BCHIRInterpreter::InterpretSwitchWithType() { size_t realValue = static_cast(interpStack.ArgsPop().content); // sorted case auto casesIdx = pc + 1; auto cases = bchir.Get(casesIdx); // each case is a 8 bytes value auto from = reinterpret_cast(&*(bchir.GetLinkedByteCode().GetByteCode().begin() + casesIdx + 1)); auto until = from + cases; auto found = std::lower_bound(from, until, realValue); if (found == until || *found != realValue) { // default target pc = bchir.Get(casesIdx + 1 + cases * Bchir::FLAG_TWO); } else { pc = bchir.Get(static_cast(casesIdx + 1 + cases * Bchir::FLAG_TWO + 1 + (found - from))); } } IVal* BCHIRInterpreter::AllocateValue(IVal&& value) { IVal* ptr = arena.Allocate(std::move(value)); auto objectVal = IValUtils::GetIf(ptr); if (objectVal == nullptr) { return ptr; } auto finalizerIdx = bchir.GetClassFinalizer(objectVal->classId); if (finalizerIdx != 0) { (void)arena.finalizingObjects.emplace_back(ptr); } return ptr; } #ifndef NDEBUG std::string BCHIRInterpreter::DebugGetPosition(Bchir::ByteCodeIndex index) { auto pos = bchir.GetLinkedByteCode().GetCodePositionAnnotation(index); auto fileName = bchir.GetFileName(pos.fileID); auto posStr = fileName + ":" + std::to_string(pos.line) + ":" + std::to_string(pos.column); return posStr; } std::string BCHIRInterpreter::DebugGetMangledName(Bchir::ByteCodeIndex index) const { return bchir.GetLinkedByteCode().GetMangledNameAnnotation(index); } void BCHIRInterpreter::PrintDebugInfo(Bchir::ByteCodeIndex currentPc) { if (printRuntimeDebugInfo) { CJC_ASSERT(debugFile.is_open()); auto currentOp = static_cast(bchir.Get(currentPc)); auto mangled = bchir.GetLinkedByteCode().GetMangledNameAnnotation(currentPc); auto position = bchir.GetLinkedByteCode().GetCodePositionAnnotation(currentPc); auto file = bchir.GetFileName(position.fileID); debugFile << std::to_string(currentPc) << " - " << GetOpCodeLabel(currentOp); if (mangled != "") { debugFile << " - " << mangled; } if (position.fileID != 0 || position.line != 0 || position.column != 0) { debugFile << " - " << file << ":" << position.line << ":" << position.column; } debugFile << std::endl; } } void BCHIRInterpreter::PrepareRuntimeDebug(const GlobalOptions& options) { if (debugFile.is_open()) { debugFile.close(); } printRuntimeDebugInfo = options.PrintBchir(GlobalOptions::PrintBCHIROption::INTERPRETER); if (printRuntimeDebugInfo) { auto stageName = isConstEval ? "ce-interpreted" : "interpreted"; debugFile = BCHIRPrinter::GetBCHIROutputFile(options, bchir.packageName, stageName); } } #endif template void BCHIRInterpreter::InterpretAllocateRawArray() { auto initPc = pc++; auto array = IArray(); if constexpr (isLiteral) { size_t size = static_cast(bchir.Get(pc++)); interpStack.ArgsPop(size, array.content); CJC_ASSERT(array.content.size() == size); (void)initPc; } else { auto sizeIVal = interpStack.ArgsPop(); auto size = sizeIVal.content; if (size < 0) { return RaiseNegativeArraySizeException(initPc); } // overflow or greater than max_size if (static_cast(size + 1) > array.content.max_size()) { return RaiseOutOfMemoryError(initPc); } array.content.reserve(static_cast(size) + 1); array.content.emplace_back(std::move(sizeIVal)); for (size_t i = 0; i < static_cast(size); ++i) { array.content.emplace_back(INullptr()); } CJC_ASSERT(array.content.size() == static_cast(size) + 1); } auto ptr = IPointer(); ptr.content = AllocateValue(std::move(array)); interpStack.ArgsPush(ptr); if constexpr (isExc) { pc++; } } template void BCHIRInterpreter::InterpretIntrinsic() { if constexpr (op == OpCode::INTRINSIC0) { InterpretIntrinsic0(); } else if constexpr (op == OpCode::INTRINSIC1) { InterpretIntrinsic1(); } } void BCHIRInterpreter::InterpretVArrayGet() { auto initPc = pc++; auto pathSize = bchir.Get(pc++); std::vector path; interpStack.ArgsPop(pathSize, path); auto array = interpStack.ArgsPop(); for (size_t i = 0; i < pathSize - 1; ++i) { auto arrayIndex = IValUtils::Get(path[i]).content; array = IValUtils::Get(array.content[static_cast(arrayIndex)]); } auto finalIndex = IValUtils::Get(path[static_cast(pathSize - 1)]); auto getIdx = finalIndex.content; if (getIdx < 0 || getIdx > static_cast(array.content.size())) { RaiseIndexOutOfBoundsException(static_cast(initPc)); } else { interpStack.ArgsPushIVal(std::move(array.content[static_cast(getIdx)])); } } void BCHIRInterpreter::InterpretRawArrayLiteralInit() { pc++; // to array size auto size = bchir.Get(pc); pc++; // to next operation std::vector elems; interpStack.ArgsPop(size, elems); auto arrayPtr = interpStack.ArgsPop(); auto& array = IValUtils::Get(*arrayPtr.content); CJC_ASSERT(static_cast(IValUtils::Get(array.content[0]).content) == size); for (size_t i = 0; i < static_cast(size); ++i) { array.content[i + 1] = std::move(elems[i]); } interpStack.ArgsPush(IUnit()); } cangjie_compiler-1.0.7/src/CHIR/Interpreter/BCHIRIntrinsic.cpp000066400000000000000000000123541510705540100240510ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements intrinsics functions in the interpreter for the standard library. */ #include #include #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Interpreter/BCHIRInterpreter.h" #include "cangjie/CHIR/IntrinsicKind.h" #include "cangjie/Utils/ConstantsUtils.h" using namespace Cangjie; using namespace CHIR; using namespace Interpreter; bool BCHIRInterpreter::InterpretIntrinsic0() { CJC_ASSERT(static_cast(bchir.Get(pc)) == OpCode::INTRINSIC0 || static_cast(bchir.Get(pc)) == OpCode::INTRINSIC0_EXC); // INTRINSIC :: INTRINSIC_KIND auto opIdx = pc++; auto intrinsicKind = static_cast(bchir.Get(pc++)); switch (intrinsicKind) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND case RAW_ARRAY_REFEQ: [[fallthrough]]; #endif case OBJECT_REFEQ: InterpretRefEq(); return false; default: { auto intrinsic = static_cast(intrinsicKind); std::string concurrencyKeyword = intrinsicKind >= GET_THREAD_OBJECT && intrinsicKind <= SET_THREAD_OBJECT ? "concurrency " : ""; std::string errorMSg = "interpreter does not support " + concurrencyKeyword + "intrinsic function " + std::to_string(intrinsic); FailWith(opIdx, errorMSg, DiagKind::interp_unsupported, "InterpretIntrinsic", GetOpCodeLabel(OpCode::INTRINSIC0)); return true; } } } bool BCHIRInterpreter::InterpretIntrinsic1() { CJC_ASSERT(static_cast(bchir.Get(pc)) == OpCode::INTRINSIC1 || static_cast(bchir.Get(pc)) == OpCode::INTRINSIC1_EXC); // INTRINSIC1 :: INTRINSIC_KIND :: AUX_INFO1 auto opIdx = pc; // skip pc pc++; // get and skip intrinsic kind auto intrinsicKind = static_cast(bchir.Get(pc++)); // skip auxiliary type info pc++; switch (intrinsicKind) { // There's no need for these functions to be INTRINSIC1 instead of INTRINSIC0. // We just mark them as INTRINSIC1 in CHIR2BCHIR to know that the dummy function argument // needs to be popped from the argument stack. Revert commit once the functions from // syscallIntrinsicMap are marked as intrinsic in CHIR. #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND case CJ_CORE_CAN_USE_SIMD: InterpretCJCodeCanUseSIMD(); return false; #endif case ARRAY_GET_UNCHECKED: return InterpretArrayGetIntrinsic(opIdx, false); default: { auto intrinsic = static_cast(intrinsicKind); std::string concurrencyKeyword = intrinsicKind >= GET_THREAD_OBJECT && intrinsicKind <= SET_THREAD_OBJECT ? "concurrency " : ""; std::string errorMSg = "interpreter does not support " + concurrencyKeyword + "intrinsic function " + std::to_string(intrinsic); FailWith(opIdx, errorMSg, DiagKind::interp_unsupported, "InterpretIntrinsic", GetOpCodeLabel(OpCode::INTRINSIC1)); return true; } } } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void BCHIRInterpreter::InterpretCJCodeCanUseSIMD() { // this is an hack to remove the dummy func value interpStack.ArgsRemove(1); static int8_t simdSUPPORT = -1; if (simdSUPPORT < 0) { #if defined(__linux__) || defined(__APPLE__) #if defined(__x86_64__) __builtin_cpu_init(); simdSUPPORT = __builtin_cpu_supports("avx") && __builtin_cpu_supports("avx2"); #elif defined(__aarch64__) simdSUPPORT = 1; #else simdSUPPORT = 0; #endif #else simdSUPPORT = 0; #endif } bool ret = (simdSUPPORT > 0); interpStack.ArgsPush(IValUtils::PrimitiveValue(ret)); } #endif bool BCHIRInterpreter::InterpretArrayGetIntrinsic(Bchir::ByteCodeIndex idx, bool indexCheck) { auto popIndex = interpStack.ArgsPop(); auto arrayPtr = interpStack.ArgsPop(); return InterpretArrayGet(idx, indexCheck, arrayPtr, popIndex.content); } bool BCHIRInterpreter::InterpretArrayGet( Bchir::ByteCodeIndex idx, bool indexCheck, IPointer& arrayPtr, int64_t argIndex) { (void)idx; CJC_ASSERT(!indexCheck); if (auto array = IValUtils::GetIf(arrayPtr.content)) { // this is a normal CHIR array CJC_ASSERT(argIndex >= 0); auto element = array->content[static_cast(argIndex) + 1]; interpStack.ArgsPushIVal(std::move(element)); } return false; } void BCHIRInterpreter::InterpretRefEq() { auto v1 = interpStack.ArgsPopIVal(); auto v2 = interpStack.ArgsPopIVal(); bool temp; if (IValUtils::GetIf(&v1) != nullptr) { temp = IValUtils::GetIf(&v2) != nullptr; } else if (IValUtils::GetIf(&v2) != nullptr) { temp = false; } else { temp = IValUtils::GetIf(&v1)->content == IValUtils::GetIf(&v2)->content; } interpStack.ArgsPush(IValUtils::PrimitiveValue(temp)); } cangjie_compiler-1.0.7/src/CHIR/Interpreter/BCHIRLinker.cpp000066400000000000000000000616331510705540100233370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements a linker for the BCHIR Interpreter. */ #include #include "cangjie/CHIR/Interpreter/BCHIRPrinter.h" #include "cangjie/CHIR/Interpreter/Utils.h" #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/CHIR/Interpreter/BCHIRLinker.h" using namespace Cangjie::CHIR::Interpreter; template std::unordered_map BCHIRLinker::Run( std::vector& packages, const GlobalOptions& options) { std::unordered_map gvarId2InitIVal; // Jump over the dummy function definition topDef.Push(OpCode::JUMP); // Store the index to set the jump target later auto targetJumpIdx = topDef.NextIndex(); topDef.Push(0); // 0 is just a dummy value. The real value will be set below with `targetJumpIdx`. // Generate dummy function of the form FRAME :: 0 :: ABORT // A call to this function will be generated when a mangled name is not found in mName2FuncBodyIdx GenerateDummyAbortFunction(); // Set the jump target. The interpretation of the top-level definitions should start at `topDef.NextIndex()`. topDef.Set(targetJumpIdx, topDef.NextIndex()); // First traversal to link global vars and set main mangled name for (size_t i = 0; i < packages.size(); ++i) { const auto& bchir = packages[i]; if constexpr (ForConstEval) { LinkAndInitGlobalVars(bchir, gvarId2InitIVal, i == packages.size() - 1); } if (bchir.GetMainMangledName() != "") { // always get the main from the most recent package topBchir.SetMainMangledName(bchir.GetMainMangledName()); topBchir.SetMainExpectedArgs(bchir.GetMainExpectedArgs()); } } // Jump over all the definitions topDef.Push(OpCode::JUMP); // Store the index to set the jump target later targetJumpIdx = topDef.NextIndex(); topDef.Push(0); // 0 is just a dummy value. The real value will be set below with `targetJumpIdx`. // Second traversal to link functions LinkFunctions(packages); // Set the jump target. The interpretation of the top-level definitions should start at `topDef.NextIndex()`. topDef.Set(targetJumpIdx, topDef.NextIndex()); // BEGIN of top-level initialization if constexpr (ForConstEval) { // When running const-evaluation we initialize some variables manually instead of relying on linked BCHIR // to do that. GenerateCallsToConstInitFunctions(packages.back().initFuncsForConsts); } // End of top-level initialization topDef.Push(OpCode::EXIT); // we use this to reserve global env space topBchir.SetNumGlobalVars(gvarId); // third traversal to link classes for (const auto& bchir : packages) { LinkClasses(bchir); } topBchir.LinkDefaultFunctions(mName2FuncBodyIdx); // Used for filename when debugging both below and when running interpreter topBchir.packageName = packages.back().packageName; auto printBchirOption = ForConstEval ? GlobalOptions::PrintBCHIROption::CE_LINKED : GlobalOptions::PrintBCHIROption::LINKED; if (options.PrintBchir(printBchirOption)) { auto stageName = ForConstEval ? "ce-linked" : "linked"; auto bchirFile = CHIR::Interpreter::BCHIRPrinter::GetBCHIROutputFile( options, topBchir.packageName, stageName); auto bchirPrinter = CHIR::Interpreter::BCHIRPrinter(bchirFile, topBchir); bchirPrinter.PrintAll("Linked bytecode"); } return gvarId2InitIVal; } template std::unordered_map BCHIRLinker::Run( std::vector& packages, const GlobalOptions& options); template std::unordered_map BCHIRLinker::Run( std::vector& packages, const GlobalOptions& options); void BCHIRLinker::GenerateCallsToConstInitFunctions(const std::vector& constInitFuncs) { for (auto& nm : constInitFuncs) { auto funcIdxIt = mName2FuncBodyIdx.find(nm); if (funcIdxIt != mName2FuncBodyIdx.end()) { topDef.Push(OpCode::FUNC); topDef.Push(funcIdxIt->second); topDef.Push(OpCode::APPLY); topDef.Push(1); // only has the func arg topDef.Push(OpCode::DROP); // always returns a UNIT } // else the constant and function was removed by some CHIR optimization } } void BCHIRLinker::GenerateDummyAbortFunction() { dummyAbortFuncIdx = topDef.NextIndex(); topDef.Push(OpCode::FRAME); topDef.AddMangledNameAnnotation(topDef.NextIndex() - 1, "linker_dummy_abort_function"); topDef.Push(0); topDef.Push(OpCode::ABORT); } Bchir::ByteCodeContent BCHIRLinker::GetClassId(const std::string& classMangledName) { auto it = mName2ClassId.find(classMangledName); if (it != mName2ClassId.end()) { return it->second; } auto thisClassId = classId++; mName2ClassId.emplace_hint(it, classMangledName, thisClassId); return thisClassId; } Bchir::ByteCodeContent BCHIRLinker::GetMethodId(const std::string& methodName) { /* Note that two methods with the same name will have the same ID even if they are from different classes (in the same class, method's names/ids are unique). This is however not a problem because the invoke always takes into account the class ID and the method ID. Thus the method ID for a class ID will still be unique. */ auto it = mName2MethodId.find(methodName); if (it != mName2MethodId.end()) { return it->second; } auto thisMethodId = methodId++; mName2MethodId.emplace_hint(it, methodName, thisMethodId); return thisMethodId; } void BCHIRLinker::LinkClasses(const Bchir& bchir) { for (const auto& p : bchir.GetSClassTable()) { auto& mangledName = p.first; LinkClass(bchir, mangledName); } } void BCHIRLinker::LinkClass(const Bchir& bchir, const std::string& mangledName) { auto thisClassIt = mName2ClassId.find(mangledName); Bchir::ByteCodeIndex thisClassId = 0; if (thisClassIt == mName2ClassId.end()) { thisClassId = classId++; mName2ClassId.emplace_hint(thisClassIt, mangledName, thisClassId); } else { // class ID was already generated when linking bytecode thisClassId = thisClassIt->second; if (topBchir.ClassExists(thisClassId)) { // ClassInfo was already generated for this class return; } // else ClassInfo was not yet generated for this class } Bchir::ClassInfo classInfo; classInfo.mangledName = mangledName; // only reached for classes from this package; otherwise it was already encoded and this point is not reached auto sClassInfo = bchir.GetSClass(mangledName); if (sClassInfo == nullptr) { // This can only happen when linking packages for const-eval. The reason is that some packages are not loaded // because of CompilerInstance::AddRequiredPackagesForConstEval. At this point we assume that if a package is // not loaded then the information about the classes/interfaces in it is not required. If this assumptions is // proved incorrect we should load packages that contain super classes/interfaces for the declared // classes/interfaces in the current package. topBchir.AddClass(thisClassId, std::move(classInfo)); return; } for (const auto& superClass : sClassInfo->superClasses) { // make sure all the super classes are encoded LinkClass(bchir, superClass); } // append **all** superclasses including indirect ones for (const auto& superClass : sClassInfo->superClasses) { auto superClassIt = mName2ClassId.find(superClass); CJC_ASSERT(superClassIt != mName2ClassId.end()); auto superId = superClassIt->second; classInfo.superClasses.emplace(superId); // all the super classes have been encoded before auto& superSuperClasses = topBchir.GetClass(superId).superClasses; for (auto scit : superSuperClasses) { (void)classInfo.superClasses.emplace(scit); } } for (const auto& [methodName, funcMangledName] : sClassInfo->vtable) { Bchir::ByteCodeIndex thisMethodId = GetMethodId(methodName); auto thisMethdIdxIt = mName2FuncBodyIdx.find(funcMangledName); if (thisMethdIdxIt != mName2FuncBodyIdx.end()) { (void)classInfo.vtable.emplace(thisMethodId, thisMethdIdxIt->second); } else { // This can happen because we only load packages that are required for const-eval - see // requiredConstEvalDependencies in the RunConstantEvaluation function; However, a class type might appear // as an import CHIR type, but the package containing the definition is never loaded - we are currently // assuming that those methods won't be used in const contexts. (void)classInfo.vtable.emplace(thisMethodId, dummyAbortFuncIdx); } } auto finalizerIdxIt = mName2FuncBodyIdx.find(sClassInfo->finalizer); if (sClassInfo->finalizer != "" && finalizerIdxIt != mName2FuncBodyIdx.end()) { classInfo.finalizerIdx = finalizerIdxIt->second; } else { classInfo.finalizerIdx = 0; } topBchir.AddClass(thisClassId, std::move(classInfo)); } void BCHIRLinker::LinkAndInitGlobalVars( const Bchir& bchir, std::unordered_map& gvarId2InitIVal, bool isLast) { for (auto& p : bchir.GetGlobalVars()) { auto& name = p.first; CJC_ASSERT(name.size() > 0); if (mName2GvarId.find(name) != mName2GvarId.end()) { continue; } auto& def = p.second; auto id = FreshGVarId(); mName2GvarId.emplace(name, id); auto initIVal = Interpreter::ByteCodeToIval(def, bchir, topBchir); CJC_ASSERT(isLast); gvarId2InitIVal.emplace(id, initIVal); } } std::vector BCHIRLinker::UnlinkedFiles2LinkedFiles(const std::vector& fileNames) { std::vector fileMap; for (auto& fileName : fileNames) { auto fileIdxIt = fileName2IndexMemoization.find(fileName); if (fileIdxIt == fileName2IndexMemoization.end()) { fileMap.emplace_back(static_cast(topBchir.AddFileName(fileName))); } else { fileMap.emplace_back(fileIdxIt->second); } } return fileMap; } std::vector BCHIRLinker::UnlinkedTypes2LinkedTypes(const std::vector& types) { std::vector typeMap; for (auto ty : types) { auto typeIdxIt = type2IndexMemoization.find(ty); if (typeIdxIt == type2IndexMemoization.end()) { typeMap.emplace_back(static_cast(topBchir.AddType(*ty))); } else { typeMap.emplace_back(typeIdxIt->second); } } return typeMap; } std::vector BCHIRLinker::UnlinkedStrings2LinkedStrings(const std::vector& strings) { std::vector stringMap; for (auto& str : strings) { auto stringIdxIt = strings2IndexMemoization.find(str); if (stringIdxIt == strings2IndexMemoization.end()) { stringMap.emplace_back(static_cast(topBchir.AddString(str))); } else { stringMap.emplace_back(stringIdxIt->second); } } return stringMap; } void BCHIRLinker::LinkFunctions(const std::vector& packages) { // we traverse the bytecode each function definition // and append that to topDef while adjusting some indexes // and resolving GVAR ids and class ids for (const auto& bchir : packages) { auto fileMap = UnlinkedFiles2LinkedFiles(bchir.GetFileNames()); auto typeMap = UnlinkedTypes2LinkedTypes(bchir.GetTypes()); auto stringMap = UnlinkedStrings2LinkedStrings(bchir.GetStrings()); for (auto& p : bchir.GetFunctions()) { auto& mangledName = p.first; auto& defPtr = p.second; auto bodyIdx = topDef.NextIndex(); ResolveMName2FuncBodyIdxPlaceHolder(mangledName, bodyIdx); // reserving space for a frame topDef.Push(OpCode::FRAME); topDef.AddMangledNameAnnotation(topDef.NextIndex() - 1, mangledName); topDef.Push(defPtr.GetNumLVars()); // encode the body TraverseAndLink(bchir, defPtr, fileMap, typeMap, stringMap); } } } void BCHIRLinker::AddPosition(const std::unordered_map& positions, const std::vector& fileMap, Bchir::ByteCodeIndex curr) { auto positionIt = positions.find(curr); if (positionIt != positions.end()) { auto position = positionIt->second; position.fileID = fileMap[position.fileID]; topDef.AddCodePositionAnnotation(topDef.NextIndex() - 1, position); } } void BCHIRLinker::AddMangledName( const std::unordered_map& mangledNames, Bchir::ByteCodeIndex curr) { auto mangled = mangledNames.find(curr); if (mangled != mangledNames.end()) { topDef.AddMangledNameAnnotation(topDef.NextIndex() - 1, mangled->second); } } void BCHIRLinker::TraverseAndLink(const Bchir& bchir, const Bchir::Definition& currentDef, const std::vector& fileMap, const std::vector& typeMap, const std::vector& stringMap) { const auto offset = topDef.NextIndex(); Bchir::ByteCodeIndex curr{0}; auto& positions = currentDef.GetCodePositionsAnnotations(); auto& mangledNames = currentDef.GetMangledNamesAnnotations(); while (curr < currentDef.NextIndex()) { auto op = static_cast(currentDef.Get(curr)); CJC_ASSERT(op <= OpCode::INVALID); // pushing the opcode topDef.Push(op); // propagate code positions for call stack printing purposes AddPosition(positions, fileMap, curr); #ifndef NDEBUG // propagate all the mangled names for debugging purposes AddMangledName(mangledNames, curr); #endif auto next = curr + GetOpCodeArgSize(op) + 1; // pushing anything else switch (op) { case OpCode::GVAR: { auto& mgl = currentDef.GetMangledNameAnnotation(curr); auto gVarIt = mName2GvarId.find(mgl); if (gVarIt != mName2GvarId.end()) { topDef.Push(mName2GvarId[mgl]); break; } // else we treat it as a function topDef.SetOp(topDef.NextIndex() - 1, OpCode::FUNC); [[fallthrough]]; } case OpCode::FUNC: { #ifdef NDEBUG AddMangledName(mangledNames, curr); // #else debug AddMangledName is performed for all the operations anyway #endif auto& mgl = currentDef.GetMangledNameAnnotation(curr); auto it = mName2FuncBodyIdx.find(mgl); if (it != mName2FuncBodyIdx.end()) { topDef.Push(it->second); } else { AddToMName2FuncBodyIdxPlaceHolder(mgl, topDef.NextIndex()); // Most of the time `dummyAbortFuncIdx` will be modified when function is visited, otherwise // something went wrong and the interpretation will abort. topDef.Push(dummyAbortFuncIdx); } break; } case OpCode::STRING: { topDef.Push(stringMap[currentDef.Get(curr + 1)]); break; } case OpCode::ALLOCATE_CLASS: { auto& mgl = currentDef.GetMangledNameAnnotation(curr); auto thisClassId = GetClassId(mgl); topDef.Push(thisClassId); topDef.Push(currentDef.Get(curr + Bchir::FLAG_TWO)); break; } case OpCode::ALLOCATE_CLASS_EXC: { auto& mgl = currentDef.GetMangledNameAnnotation(curr); auto thisClassId = GetClassId(mgl); topDef.Push(thisClassId); topDef.Push(currentDef.Get(curr + Bchir::FLAG_TWO)); topDef.Push(currentDef.Get(curr + Bchir::FLAG_THREE) + offset); break; } case OpCode::ALLOCATE_STRUCT_EXC: { topDef.Push(currentDef.Get(curr + 1)); // number of fields topDef.Push(currentDef.Get(curr + Bchir::FLAG_TWO) + offset); // jump target for when exception break; } case OpCode::ALLOCATE_EXC: { topDef.Push(currentDef.Get(curr + 1) + offset); // jump target for when exception break; } case OpCode::BOX: case OpCode::INSTANCEOF: { auto& mgl = currentDef.GetMangledNameAnnotation(curr); auto thisClassId = GetClassId(mgl); // Create class info if the type doesn't have a class table // (e.g. enums). We don't do this for classes with a class // table because we need to finish converting their methods // to BCHIR before creating their vtables. if (bchir.GetSClass(mgl) == nullptr) { LinkClass(bchir, mgl); } topDef.Push(thisClassId); break; } case OpCode::INVOKE_EXC: { topDef.Push(currentDef.Get(curr + 1)); // number of arguments auto& mgl = currentDef.GetMangledNameAnnotation(curr); topDef.Push(GetMethodId(mgl)); // method ID topDef.Push(currentDef.Get(curr + Bchir::FLAG_THREE) + offset); // jump target for when exception break; } case OpCode::INVOKE: { topDef.Push(currentDef.Get(curr + 1)); auto& mgl = currentDef.GetMangledNameAnnotation(curr); topDef.Push(GetMethodId(mgl)); break; } case OpCode::JUMP: { auto nextIdx = currentDef.Get(curr + 1) + offset; topDef.Push(nextIdx); break; } case OpCode::BRANCH: { auto trueIdx = currentDef.Get(curr + 1) + offset; topDef.Push(trueIdx); auto falseIdx = currentDef.Get(curr + Bchir::FLAG_TWO) + offset; topDef.Push(falseIdx); break; } case OpCode::INTRINSIC1: { topDef.Push(currentDef.Get(curr + 1)); // intrinsic kind auto oldTyIdx = currentDef.Get(curr + Bchir::FLAG_TWO); // type Bchir::ByteCodeContent newTyIdx = Bchir::BYTECODE_CONTENT_MAX; if (oldTyIdx != Bchir::BYTECODE_CONTENT_MAX) { newTyIdx = typeMap[oldTyIdx]; } topDef.Push(newTyIdx); break; } case OpCode::SYSCALL: { auto nameId = stringMap[currentDef.Get(curr + 1)]; topDef.Push(nameId); auto numberOfArgs = currentDef.Get(curr + Bchir::FLAG_TWO); topDef.Push(numberOfArgs); auto resTyId = typeMap[currentDef.Get(curr + Bchir::FLAG_THREE)]; topDef.Push(resTyId); for (Bchir::ByteCodeIndex j = 0; j < numberOfArgs; ++j) { auto tyId = typeMap[currentDef.Get(curr + Bchir::FLAG_TWO + Bchir::FLAG_TWO + j)]; topDef.Push(static_cast(tyId)); } next = curr + GetOpCodeArgSize(op) + 1 + numberOfArgs + 1; break; } case OpCode::SWITCH: { auto casesIdx = curr + Bchir::FLAG_TWO; auto cases = currentDef.Get(casesIdx); auto firstJump = casesIdx + 1 + cases * Bchir::FLAG_TWO; topDef.Push(currentDef.Get(curr + 1)); topDef.Push(currentDef.Get(curr + Bchir::FLAG_TWO)); for (size_t j = casesIdx + 1; j < firstJump; j += Bchir::FLAG_TWO) { topDef.Push8bytes(currentDef.Get8bytes(static_cast(j))); } for (size_t j = firstJump; j < firstJump + cases + 1; ++j) { topDef.Push(currentDef.Get(static_cast(j)) + offset); } next = curr + GetOpCodeArgSize(op) + /* type and number of cases */ cases * Bchir::FLAG_TWO + /* cases (8 bytes each) */ 1 + /* default target */ cases + /* other targets */ 1; break; } case OpCode::APPLY_EXC: { topDef.Push(currentDef.Get(curr + 1)); // number of arguments topDef.Push(currentDef.Get(curr + Bchir::FLAG_TWO) + offset); // jump target for when exception break; } case OpCode::UN_NEG_EXC: case OpCode::BIN_ADD_EXC: case OpCode::BIN_SUB_EXC: case OpCode::BIN_MUL_EXC: case OpCode::BIN_DIV_EXC: case OpCode::BIN_MOD_EXC: case OpCode::BIN_EXP_EXC: { topDef.Push(currentDef.Get(curr + 1)); // type kind topDef.Push(currentDef.Get(curr + Bchir::FLAG_TWO)); // overflow strategy topDef.Push(currentDef.Get(curr + Bchir::FLAG_THREE) + offset); // jump target for when exception break; } case OpCode::BIN_RSHIFT_EXC: case OpCode::BIN_LSHIFT_EXC: { topDef.Push(currentDef.Get(curr + 1)); // type kind topDef.Push(currentDef.Get(curr + Bchir::FLAG_TWO)); // overflow strategy topDef.Push(currentDef.Get(curr + Bchir::FLAG_THREE)); // type kind for rhs topDef.Push(currentDef.Get(curr + Bchir::FLAG_FOUR) + offset); // jump target for when exception break; } case OpCode::CAPPLY: { auto argsSize = currentDef.Get(curr + 1); topDef.Push(argsSize); // num of Args next = curr + argsSize + Bchir::FLAG_THREE; for (auto j = curr + 2; j < next; ++j) { auto oldTyIdx = currentDef.Get(j); // type Bchir::ByteCodeContent newTyIdx = Bchir::BYTECODE_CONTENT_MAX; newTyIdx = typeMap[oldTyIdx]; topDef.Push(newTyIdx); } break; } case OpCode::STOREINREF: case OpCode::GETREF: { auto pathSize = currentDef.Get(curr + 1); next = curr + GetOpCodeArgSize(op) + pathSize + 1; for (auto j = curr + 1; j < next; ++j) { topDef.Push(currentDef.Get(j)); } break; } case OpCode::FIELD_TPL: { auto pathSize = currentDef.Get(curr + 1); next = curr + GetOpCodeArgSize(op) + pathSize + 1; for (auto j = curr + 1; j < next; ++j) { topDef.Push(currentDef.Get(j)); } break; } default: { for (auto j = curr + 1; j < next; ++j) { topDef.Push(currentDef.Get(j)); } } } curr = next; } } Bchir::ByteCodeContent BCHIRLinker::FreshGVarId() { return gvarId++; } void BCHIRLinker::AddToMName2FuncBodyIdxPlaceHolder(const std::string& mName, Bchir::ByteCodeIndex idx) { auto it = mName2FuncBodyIdxPlaceHolder.find(mName); if (it == mName2FuncBodyIdxPlaceHolder.end()) { std::vector vec{idx}; mName2FuncBodyIdxPlaceHolder.emplace(mName, vec); } else { it->second.emplace_back(idx); } } void BCHIRLinker::ResolveMName2FuncBodyIdxPlaceHolder(const std::string& mName, Bchir::ByteCodeIndex idx) { mName2FuncBodyIdx.emplace(mName, idx); auto it = mName2FuncBodyIdxPlaceHolder.find(mName); if (it != mName2FuncBodyIdxPlaceHolder.end()) { for (auto ph : it->second) { topDef.Set(ph, idx); } mName2FuncBodyIdxPlaceHolder.erase(mName); } } int BCHIRLinker::GetGVARId(const std::string& name) const { auto it = mName2GvarId.find(name); if (it != mName2GvarId.end()) { return static_cast(it->second); } return -1; } cangjie_compiler-1.0.7/src/CHIR/Interpreter/BCHIRPrinter.cpp000066400000000000000000000532111510705540100235270ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements a BCHIR printer. */ #include #include #include "cangjie/CHIR/Interpreter/BCHIRPrinter.h" #include "cangjie/Basic/Print.h" #include "cangjie/CHIR/IntrinsicKind.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/Utils/FileUtil.h" using namespace Cangjie::CHIR::Interpreter; static std::string DefaultFunctionKind2String(Bchir::DefaultFunctionKind kind) { switch (kind) { case Bchir::DefaultFunctionKind::THROW_ARITHMETIC_EXCEPTION: return "THROW_ARITHMETIC_EXCEPTION"; case Bchir::DefaultFunctionKind::THROW_OVERFLOW_EXCEPTION: return "THROW_OVERFLOW_EXCEPTION"; case Bchir::DefaultFunctionKind::THROW_INDEX_OUT_OF_BOUNDS_EXCEPTION: return "THROW_INDEX_OUT_OF_BOUNDS_EXCEPTION"; case Bchir::DefaultFunctionKind::THROW_NEGATIVA_ARRAY_SIZE_EXCEPTION: return "THROW_NEGATIVA_ARRAY_SIZE_EXCEPTION"; case Bchir::DefaultFunctionKind::CALL_TO_STRING: return "CALL_TO_STRING"; case Bchir::DefaultFunctionKind::THROW_ARITHMETIC_EXCEPTION_MSG: return "THROW_ARITHMETIC_EXCEPTION_MSG"; case Bchir::DefaultFunctionKind::THROW_OUT_OF_MEMORY_ERROR: return "THROW_OUT_OF_MEMORY_ERROR"; case Bchir::DefaultFunctionKind::CHECK_IS_ERROR: return "CHECK_IS_ERROR"; case Bchir::DefaultFunctionKind::THROW_ERROR: return "THROW_ERROR"; case Bchir::DefaultFunctionKind::CALL_PRINT_STACK_TRACE: return "CALL_PRINT_STACK_TRACE"; case Bchir::DefaultFunctionKind::CALL_PRINT_STACK_TRACE_ERROR: return "CALL_PRINT_STACK_TRACE_ERROR"; case Bchir::DefaultFunctionKind::MAIN: return "MAIN"; case Bchir::DefaultFunctionKind::INVALID: default: CJC_ASSERT(false); } return ""; } void BCHIRPrinter::Print(std::string header) { os << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << std::endl; os << "+++ " << header << std::endl; auto linkedBchir = bchir.GetLinkedByteCode(); if (linkedBchir.Size() > 0) { os << "====== Linked bytecode ======" << std::endl; auto mainDefPrinter = DefinitionPrinter(bchir, linkedBchir, os); mainDefPrinter.Print(); } else { os << "====== Global vars before linkage ======" << std::endl; for (auto& [mangled, def] : bchir.GetGlobalVars()) { os << "---- " << mangled << std::endl; auto defPrinter = DefinitionPrinter(bchir, def, os); defPrinter.Print(); } os << "====== Functions before linkage ======" << std::endl; for (auto& [mangled, def] : bchir.GetFunctions()) { os << "---- " << mangled << std::endl; auto defPrinter = DefinitionPrinter(bchir, def, os); defPrinter.Print(); } } os << "====== Default functions mangled names ======" << std::endl; os << "main mangled name: " << bchir.GetMainMangledName() << std::endl; os << "main expected arguments: " << bchir.GetMainExpectedArgs() << std::endl; os << "Global init function: " << bchir.GetGlobalInitFunc() << std::endl; if (linkedBchir.Size() > 0) { // these functions are only linked by BCHIRLinker auto& defaultFuncsPtrs = bchir.GetDefaultFuncPtrs(); for (size_t i = 0; i < defaultFuncsPtrs.size(); ++i) { os << DefaultFunctionKind2String(static_cast(i)) << ": " << defaultFuncsPtrs[i] << std::endl; } } } void BCHIRPrinter::DefinitionPrinter::Print() { index = 0; os << "byte code" << std::endl; while (index < bytecode.size()) { PrintOP(); } // Flush the buffer os << std::endl; } void BCHIRPrinter::DefinitionPrinter::PrintOpCode() { auto opIdx = index++; os << LEFT << opIdx << ARGSEP << OpCodeLabel[bytecode[opIdx]]; auto& mangledNames = def.GetMangledNamesAnnotations(); auto& codePositions = def.GetCodePositionsAnnotations(); auto mangledNameIt = mangledNames.find(static_cast(opIdx)); auto codePosIt = codePositions.find(static_cast(opIdx)); if (mangledNameIt != mangledNames.end() || codePosIt != codePositions.end()) { os << " { "; } if (mangledNameIt != mangledNames.end()) { os << "mangledName: " << mangledNameIt->second; if (codePosIt != codePositions.end()) { os << ", "; } } if (codePosIt != codePositions.end()) { auto codePos = codePosIt->second; os << bchir.GetFileName(codePos.fileID) << ":" << codePos.line << ":" << codePos.column; } if (mangledNameIt != mangledNames.end() || codePosIt != codePositions.end()) { os << "}"; } os << RIGHT << OPSEP; CJC_ASSERT(opIdx + 1 == index); } void BCHIRPrinter::DefinitionPrinter::PrintTy() { size_t typeIdx = bytecode[index]; os << LEFT << index << ARGSEP << "Ty (idx: " << typeIdx << "):"; auto ty = bchir.GetTypeAt(typeIdx); os << ty->ToString() << RIGHT << OPSEP; index++; } void BCHIRPrinter::DefinitionPrinter::PrintAtIndex() { os << LEFT << index << ARGSEP << bytecode[index] << RIGHT << OPSEP; index++; } void BCHIRPrinter::DefinitionPrinter::PrintAtIndex8bytes() { auto val = *reinterpret_cast(&bytecode[index]); os << LEFT << index << ARGSEP << std::to_string(val) << RIGHT << OPSEP; index++; index++; } void BCHIRPrinter::DefinitionPrinter::Print(size_t argIndex, const std::string& str) { os << LEFT << argIndex << ARGSEP << str << RIGHT << OPSEP; } void BCHIRPrinter::DefinitionPrinter::PrintOPFloat() { float d; if (memcpy_s(&d, sizeof(float), &bytecode[index], sizeof(float)) != EOK) { CJC_ABORT(); } else { Print(index, std::to_string(d)); index++; } return; } void BCHIRPrinter::DefinitionPrinter::PrintOPFloat8bytes() { double d; if (memcpy_s(&d, sizeof(double), &bytecode[index], sizeof(double)) != EOK) { CJC_ABORT(); } else { Print(index, std::to_string(d)); index++; index++; } return; } void BCHIRPrinter::DefinitionPrinter::PrintOPRune() { auto val = static_cast(bytecode[index]); std::stringstream stream; stream << "r'"; if (val >= ' ' && val <= '~') { if (val == '\'' || val == '\\') { stream << '\\' << static_cast(val); } else { stream << static_cast(val); } } else { stream << "\\u{" << std::hex << val << "}"; } stream << '\''; Print(index, stream.str()); index++; } void BCHIRPrinter::DefinitionPrinter::PrintOPSwitch() { // switch overloading type CHIR::Type::TypeKind tyKind = static_cast(bytecode[index]); auto it = CHIR::TYPEKIND_TO_STRING.find(tyKind); CJC_ASSERT(it != CHIR::TYPEKIND_TO_STRING.end()); Print(index, it->second); index++; size_t cases = bytecode[index]; PrintAtIndex(); // Print cases for (size_t i = 0; i < cases; i++) { PrintAtIndex8bytes(); } // Print target index for (size_t i = 0; i <= cases; i++) { PrintAtIndex(); } } const std::unordered_map BCHIRPrinter::OVERFLOW_STRAT2STRING = { {Cangjie::OverflowStrategy::NA, "NA"}, {Cangjie::OverflowStrategy::CHECKED, "CHECKED"}, {Cangjie::OverflowStrategy::WRAPPING, "WRAPPING"}, {Cangjie::OverflowStrategy::THROWING, "THROWING"}, {Cangjie::OverflowStrategy::SATURATING, "SATURATING"}, }; void BCHIRPrinter::DefinitionPrinter::PrintOPBinRshift(OpCode opCode) { // binary operation overloading type CHIR::Type::TypeKind tyKind = static_cast(bytecode[index]); auto it = CHIR::TYPEKIND_TO_STRING.find(tyKind); CJC_ASSERT(it != CHIR::TYPEKIND_TO_STRING.end()); Print(index, it->second); index++; OverflowStrategy overflowStrat = static_cast(bytecode[index]); auto sit = OVERFLOW_STRAT2STRING.find(overflowStrat); CJC_ASSERT(sit != OVERFLOW_STRAT2STRING.end()); Print(index, sit->second); index++; if (opCode == OpCode::BIN_LSHIFT || opCode == OpCode::BIN_RSHIFT || opCode == OpCode::BIN_RSHIFT_EXC || opCode == OpCode::BIN_LSHIFT_EXC) { // type for rhs CHIR::Type::TypeKind rhsTyKind = static_cast(bytecode[index]); auto it2 = CHIR::TYPEKIND_TO_STRING.find(rhsTyKind); CJC_ASSERT(it2 != CHIR::TYPEKIND_TO_STRING.end()); auto typeKind = it2->second; Print(index, std::move(typeKind)); index++; } } void BCHIRPrinter::DefinitionPrinter::PrintOPTypeCast() { CHIR::Type::TypeKind tyKind = static_cast(bytecode[index]); auto it = CHIR::TYPEKIND_TO_STRING.find(tyKind); CJC_ASSERT(it != CHIR::TYPEKIND_TO_STRING.end()); Print(index, it->second); index++; tyKind = static_cast(bytecode[index]); it = CHIR::TYPEKIND_TO_STRING.find(tyKind); CJC_ASSERT(it != CHIR::TYPEKIND_TO_STRING.end()); Print(index, it->second); index++; OverflowStrategy overflowStrat = static_cast(bytecode[index]); auto sit = OVERFLOW_STRAT2STRING.find(overflowStrat); CJC_ASSERT(sit != OVERFLOW_STRAT2STRING.end()); Print(index, sit->second); index++; } void BCHIRPrinter::DefinitionPrinter::PrintPath() { auto pathSize = bytecode[index]; PrintAtIndex(); for (size_t i = 0; i < pathSize; ++i) { PrintAtIndex(); } } void BCHIRPrinter::DefinitionPrinter::PrintOPIntrinsic(OpCode opCode) { auto kind = static_cast(bytecode[index]); Print(index, "INTRINSIC_KIND: " + std::to_string(bytecode[index])); index++; if (opCode == OpCode::INTRINSIC0 || opCode == OpCode::INTRINSIC0_EXC) { return; } Bchir::ByteCodeContent tyIdx = bytecode[index]; if (tyIdx != Bchir::BYTECODE_CONTENT_MAX) { PrintTy(); // it increments index } else { Print(index++, "(no type)"); } if (opCode == OpCode::INTRINSIC1 || opCode == OpCode::INTRINSIC1_EXC) { return; } // in this case we have an additional argument CJC_ASSERT(kind == CHIR::IntrinsicKind::ARRAY_SLICE || kind == CHIR::IntrinsicKind::ARRAY_SLICE_GET_ELEMENT || kind == CHIR::IntrinsicKind::ARRAY_SLICE_SET_ELEMENT); auto stratIndex = index++; OverflowStrategy overflowStrat = static_cast(bytecode[stratIndex]); auto sit = OVERFLOW_STRAT2STRING.find(overflowStrat); CJC_ASSERT(sit != OVERFLOW_STRAT2STRING.end()); auto overflow = sit->second; Print(index, std::move(overflow)); } void BCHIRPrinter::DefinitionPrinter::PrintOP() { auto opCode = static_cast(bytecode[index]); PrintOpCode(); switch (opCode) { case OpCode::GVAR_SET: case OpCode::LVAR_SET: case OpCode::GVAR: case OpCode::LVAR: { PrintAtIndex(); return; } case OpCode::ALLOCATE_RAW_ARRAY_LITERAL: { PrintAtIndex(); // size return; } case OpCode::ALLOCATE_RAW_ARRAY_LITERAL_EXC: { PrintAtIndex(); // size PrintAtIndex(); // jump target for when exception is raised return; } case OpCode::RAW_ARRAY_LITERAL_INIT: { PrintAtIndex(); // size return; } case OpCode::RAW_ARRAY_INIT_BY_VALUE: case OpCode::ALLOCATE_RAW_ARRAY: { return; } case OpCode::ALLOCATE_RAW_ARRAY_EXC: { PrintAtIndex(); return; } case OpCode::ALLOCATE_CLASS: { // class id PrintAtIndex(); // number of fields PrintAtIndex(); return; } case OpCode::ALLOCATE_CLASS_EXC: { // class id PrintAtIndex(); // number of fields PrintAtIndex(); // jump target for when exception is raised PrintAtIndex(); return; } case OpCode::ALLOCATE_STRUCT: { PrintAtIndex(); // number of fields return; } case OpCode::ALLOCATE_STRUCT_EXC: { PrintAtIndex(); // number of fields PrintAtIndex(); // jump target for when exception is raised return; } case OpCode::ALLOCATE: { return; } case OpCode::ALLOCATE_EXC: { PrintAtIndex(); // jump target for when exception is raised return; } case OpCode::FRAME: { PrintAtIndex(); return; } case OpCode::UINT8: case OpCode::UINT16: case OpCode::UINT32: { PrintAtIndex(); return; } case OpCode::UINT64: case OpCode::UINTNAT: { PrintAtIndex8bytes(); return; } case OpCode::INT8: case OpCode::INT16: case OpCode::INT32: { auto val = static_cast(bytecode[index]); Print(index, std::to_string(val)); index++; return; } case OpCode::INT64: case OpCode::INTNAT: { auto val = *reinterpret_cast(&bytecode[index]); Print(index, std::to_string(val)); index++; index++; return; } case OpCode::FLOAT16: case OpCode::FLOAT32: { PrintOPFloat(); return; } case OpCode::FLOAT64: { PrintOPFloat8bytes(); return; } case OpCode::RUNE: { PrintOPRune(); return; } case OpCode::BOOL: { auto val = static_cast(bytecode[index]); Print(index, std::to_string(val)); index++; return; } case OpCode::UNIT: case OpCode::NULLPTR: { return; } case OpCode::STRING: { auto strIdx = bytecode[index]; auto str = bchir.GetString(strIdx); Print(index, "(str: " + std::to_string(strIdx) + ")" + " -> " + str); index++; return; } case OpCode::TUPLE: { // tuple size PrintAtIndex(); return; } case OpCode::ARRAY: { PrintAtIndex(); return; } case OpCode::VARRAY_BY_VALUE: { return; } case OpCode::VARRAY: { PrintAtIndex(); return; } case OpCode::VARRAY_GET: { PrintAtIndex(); return; } case OpCode::FUNC: { // thunk index PrintAtIndex(); return; } case OpCode::STORE: case OpCode::RETURN: case OpCode::EXIT: case OpCode::DROP: { return; } case OpCode::JUMP: { // target index PrintAtIndex(); return; } case OpCode::BRANCH: { PrintAtIndex(); PrintAtIndex(); return; } case OpCode::SWITCH: { PrintOPSwitch(); return; } case OpCode::UN_NEG: case OpCode::UN_NOT: case OpCode::UN_DEC: case OpCode::UN_INC: case OpCode::UN_BITNOT: case OpCode::BIN_ADD: case OpCode::BIN_SUB: case OpCode::BIN_MUL: case OpCode::BIN_DIV: case OpCode::BIN_MOD: case OpCode::BIN_EXP: case OpCode::BIN_LT: case OpCode::BIN_GT: case OpCode::BIN_LE: case OpCode::BIN_GE: case OpCode::BIN_NOTEQ: case OpCode::BIN_EQUAL: case OpCode::BIN_BITAND: case OpCode::BIN_BITOR: case OpCode::BIN_BITXOR: case OpCode::BIN_LSHIFT: case OpCode::BIN_RSHIFT: { PrintOPBinRshift(opCode); return; } case OpCode::UN_NEG_EXC: case OpCode::BIN_ADD_EXC: case OpCode::BIN_SUB_EXC: case OpCode::BIN_MUL_EXC: case OpCode::BIN_DIV_EXC: case OpCode::BIN_MOD_EXC: case OpCode::BIN_EXP_EXC: case OpCode::BIN_LSHIFT_EXC: case OpCode::BIN_RSHIFT_EXC: { PrintOPBinRshift(opCode); // type kind, overflow strategy PrintAtIndex(); // jump target for when exception is raised return; } case OpCode::FIELD: { // field index PrintAtIndex(); return; } case OpCode::FIELD_TPL: { auto pathSize = bytecode[index]; PrintAtIndex(); for (Bchir::ByteCodeContent i = 0; i < pathSize; ++i) { PrintAtIndex(); } break; } case OpCode::INVOKE: { // number of args PrintAtIndex(); // name id PrintAtIndex(); return; } case OpCode::INVOKE_EXC: { // number of args PrintAtIndex(); // name id PrintAtIndex(); // jump target for when exception is raised PrintAtIndex(); return; } case OpCode::TYPECAST: { PrintOPTypeCast(); return; } case OpCode::TYPECAST_EXC: { PrintOPTypeCast(); // source type kind, target type kind, overflow strategy PrintAtIndex(); // jump target for when exception is raised return; } case OpCode::INSTANCEOF: { // class id PrintAtIndex(); return; } case OpCode::APPLY: { // number of arguments PrintAtIndex(); return; } case OpCode::APPLY_EXC: { // number of arguments PrintAtIndex(); // jump target for when exception is raised PrintAtIndex(); return; } case OpCode::CAPPLY: { return; } case OpCode::ASG: { return; } case OpCode::GETREF: case OpCode::STOREINREF: { PrintPath(); return; } case OpCode::DEREF: { return; } case OpCode::SYSCALL: { return; } case OpCode::INTRINSIC0: case OpCode::INTRINSIC1: case OpCode::INTRINSIC2: { PrintOPIntrinsic(opCode); return; } case OpCode::INTRINSIC0_EXC: case OpCode::INTRINSIC1_EXC: case OpCode::INTRINSIC2_EXC: { PrintOPIntrinsic(opCode); PrintAtIndex(); return; } case OpCode::SPAWN_EXC: { PrintAtIndex(); return; } case OpCode::RAISE_EXC: { PrintAtIndex(); // target block return; } case OpCode::ABORT: { return; } case OpCode::RAISE: case OpCode::GET_EXCEPTION: case OpCode::SPAWN: case OpCode::NOT_SUPPORTED: { return; } case OpCode::BOX: { // class id PrintAtIndex(); return; } case OpCode::UNBOX: case OpCode::UNBOX_REF: { return; } default: { CJC_ASSERT(false); Errorln("Printer not implemented."); } } } void BCHIRPrinter::PrintAll(std::string header) { Print(header); PrintSClassTable(); PrintStrings(); PrintTypes(); PrintSourceFiles(); } void BCHIRPrinter::PrintSClassTable() { os << "====== Class table ======" << std::endl; for (auto& bclass : bchir.GetSClassTable()) { os << bclass.first << ":" << std::endl; auto& classInfo = bclass.second; for (auto& sc : classInfo.superClasses) { os << "\t" << sc << std::endl; } os << std::endl; for (auto& entry : classInfo.vtable) { os << "\t" << entry.first << ": " << entry.second << std::endl; } os << "\tfinalizer: " << classInfo.finalizer << std::endl; } } void BCHIRPrinter::PrintStrings() { os << "====== Strings ======\n"; auto& strings = bchir.GetStrings(); for (size_t i = 0; i < strings.size(); ++i) { os << i << " - " << strings[i] << std::endl; } } void BCHIRPrinter::PrintTypes() { os << "====== Types ======\n"; auto& types = bchir.GetTypes(); for (size_t i = 0; i < types.size(); ++i) { os << i << " - " << types[i]->ToString() << std::endl; } } void BCHIRPrinter::PrintSourceFiles() { os << "====== Source files ======" << std::endl; auto& fileNames = bchir.GetFileNames(); for (size_t i = 0; i < fileNames.size(); ++i) { os << i << " - " << fileNames[i] << std::endl; } } std::fstream BCHIRPrinter::GetBCHIROutputFile( const Cangjie::GlobalOptions& options, const std::string& fullPackageName, const std::string& stageName) { std::fstream f; std::string bchirDir; auto& outputPath = options.output; if (FileUtil::IsDir(outputPath)) { bchirDir = FileUtil::JoinPath(outputPath, "BCHIR_Debug"); } else { bchirDir = FileUtil::GetFileBase(outputPath) + "_BCHIR_Debug"; } auto path = FileUtil::JoinPath(bchirDir, fullPackageName + "_" + stageName + ".bchir"); FileUtil::CreateDirs(path); f.open(path, std::ios::out); return f; } cangjie_compiler-1.0.7/src/CHIR/Interpreter/CHIR2BCHIR.cpp000066400000000000000000000624331510705540100227210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements a translation from CHIR to BCHIR. */ #include "cangjie/CHIR/Interpreter/CHIR2BCHIR.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Interpreter/Utils.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Value.h" using namespace Cangjie::CHIR; using namespace Interpreter; /** @brief set of non-const exceptions that can be thrown during const-eval evaluation. */ static const std::set CONST_EXCEPTIONS = {"@_CNat19ArithmeticExceptionE", "@_CNat5ErrorE", "@_CNat9ExceptionE", "@_CNat24IllegalArgumentExceptionE", "@_CNat25IndexOutOfBoundsExceptionE", "@_CNat18NoneValueExceptionE", "@_CNat17OverflowExceptionE"}; /** @brief set of non-const functions that can be executed during const-eval evaluation. */ static const std::set CONST_FUNCTIONS = {"@_CNat19ArithmeticException6Hv", "@_CNat19ArithmeticException6HRNat6StringE", "@_CNat9Exception6Hv", "@_CNat9Exception6HRNat6StringE", "@_CNat5Error6Hv", "@_CNat5Error6HRNat6StringE", "@_CNat24IllegalArgumentException6Hv", "@_CNat24IllegalArgumentException6HRNat6StringE", "@_CNat25IndexOutOfBoundsException6Hv", "@_CNat25IndexOutOfBoundsException6HRNat6StringE", "@_CNat17OverflowException6Hv", "@_CNat17OverflowException6HRNat6StringE", "@_CNat5ArrayIhE5cloneHv", "@_CNat6String7toArrayHv"}; template void CHIR2BCHIR::TranslatePackage( const Package& chirPkg, const std::vector& initFuncsForConstVar) { bchir.packageName = chirPkg.GetName(); if (chirPkg.GetName() == CORE_PACKAGE_NAME) { bchir.SetAsCore(); // the object class isn't defined anywhere, so we force it's generation when compiling core bchir.AddSClass("_CN8std$core6ObjectE", Bchir::SClassInfo()); } TranslateClassesLike(chirPkg); TranslateGlobalVars(chirPkg); TranslateFunctions(chirPkg); bchir.SetGlobalInitFunc(chirPkg.GetPackageInitFunc()->GetIdentifierWithoutPrefix()); if constexpr (ForConstEval) { for (const auto v : initFuncsForConstVar) { bchir.initFuncsForConsts.emplace_back(v->GetIdentifierWithoutPrefix()); } } } static void CollectMethods(const CustomTypeDef& chirClass, Bchir::SClassInfo& classInfo) { for (const auto& it : chirClass.GetVTable()) { for (const auto& funcInfo : it.second) { if (!funcInfo.typeInfo.sigType) { continue; } auto methodName = MangleMethodName(funcInfo.srcCodeIdentifier, *funcInfo.typeInfo.sigType); if (funcInfo.instance != nullptr) { classInfo.vtable.emplace(methodName, funcInfo.instance->GetIdentifierWithoutPrefix()); } // else, this class/interface does not implement the method } } } static void AddClassInfo( std::string&& classMangledName, Bchir::SClassInfo&& classInfo, Bchir& bchir, bool isIncremental) { if (isIncremental) { bchir.RemoveClass(classMangledName); } bchir.AddSClass(classMangledName, std::move(classInfo)); } bool CHIR2BCHIR::IsConstClass(const CustomTypeDef& def) const { if (CONST_EXCEPTIONS.count(def.GetIdentifier()) > 0) { return true; } if (def.IsClassLike()) { if (def.TestAttr(Attribute::COMPILER_ADD)) { // all typedefs added by compiler are considered const declarations return true; } auto methods = def.GetMethods(); for (auto ex : def.GetExtends()) { auto m = ex->GetMethods(); methods.insert(methods.end(), m.begin(), m.end()); } for (auto method : methods) { if (method->TestAttr(Attribute::CONST)) { // if a method is const we assume the class can be const return true; } } // if the class does not contain any methods we assume it can be used in a const context return methods.empty(); } else { return true; } }; template void CHIR2BCHIR::TranslateClassesLike(const Package& chirPkg) { TranslateClasses(chirPkg); TranslateStucts(chirPkg); TranslateEnums(chirPkg); TranslateExtends(chirPkg); } template void CHIR2BCHIR::TranslateClasses(const Package& chirPkg) { for (const auto chirClass : chirPkg.GetAllClassDef()) { if constexpr (ForConstEval) { if (!chirClass->IsInterface() && !IsConstClass(*chirClass)) { continue; } } Bchir::SClassInfo classInfo; auto super = chirClass->GetSuperClassDef(); if (super != nullptr) { classInfo.superClasses.emplace_back(super->GetIdentifierWithoutPrefix()); } auto finalizer = chirClass->GetFinalizer(); classInfo.finalizer = finalizer != nullptr ? finalizer->GetIdentifierWithoutPrefix() : ""; CollectMethods(*chirClass, classInfo); AddClassInfo(chirClass->GetIdentifierWithoutPrefix(), std::move(classInfo), bchir, isIncremental); } } template void CHIR2BCHIR::TranslateStucts(const Package& chirPkg) { for (const auto chirClass : chirPkg.GetAllStructDef()) { if constexpr (ForConstEval) { if (!IsConstClass(*chirClass)) { continue; } } Bchir::SClassInfo classInfo; CollectMethods(*chirClass, classInfo); AddClassInfo(chirClass->GetIdentifierWithoutPrefix(), std::move(classInfo), bchir, isIncremental); } } template void CHIR2BCHIR::TranslateEnums(const Package& chirPkg) { for (const auto chirClass : chirPkg.GetAllEnumDef()) { Bchir::SClassInfo classInfo; CollectMethods(*chirClass, classInfo); AddClassInfo(chirClass->GetIdentifierWithoutPrefix(), std::move(classInfo), bchir, isIncremental); } } template void CHIR2BCHIR::TranslateExtends(const Package& chirPkg) { for (const auto chirClass : chirPkg.GetAllExtendDef()) { auto extendedDef = chirClass->GetExtendedCustomTypeDef(); if constexpr (ForConstEval) { if (extendedDef != nullptr && !IsConstClass(*extendedDef)) { continue; } } if (extendedDef == nullptr) { // assumption: this is a primitive type auto ty = chirClass->GetExtendedType(); CJC_ASSERT(ty->IsPrimitive() || ty->IsCString() || ty->IsCPointer()); auto primitiveClassName = ty->ToString(); auto classInfo = bchir.GetSClass(primitiveClassName); if (classInfo == nullptr) { // first time extending primitive type Bchir::SClassInfo freshClassInfo; CollectMethods(*chirClass, freshClassInfo); AddClassInfo(std::move(primitiveClassName), std::move(freshClassInfo), bchir, isIncremental); } else { CollectMethods(*chirClass, *classInfo); } } else { auto classInfo = bchir.GetSClass(chirClass->GetExtendedCustomTypeDef()->GetIdentifierWithoutPrefix()); if (classInfo == nullptr) { // This should only happen when we are translating CHIR for const-eval CJC_ASSERT(ForConstEval); continue; } CollectMethods(*chirClass, *classInfo); } } } template void CHIR2BCHIR::TranslateGlobalVars(const Package& chirPkg) { for (const auto gv : chirPkg.GetGlobalVars()) { if constexpr (ForConstEval) { if (!gv->IsCompileTimeValue()) { // Global variable not required for const-evaluation. continue; } } auto ctx = TranslateGlobalVar(*gv); auto mangledName = gv->GetIdentifierWithoutPrefix(); if (mangledName == CHIR::GV_PKG_INIT_ONCE_FLAG) { // This is an hack because $has_applied_pkg_init_func is not a real mangled name. It's // not unique amongst packages. T0D0: issue 2079 auto fixedMangledName = CHIR::GV_PKG_INIT_ONCE_FLAG + "-" + bchir.packageName; bchir.RemoveGlobalVar(fixedMangledName); if (isIncremental) { } bchir.AddGlobalVar(fixedMangledName, std::move(ctx.def)); } else { if (isIncremental) { bchir.RemoveGlobalVar(mangledName); } bchir.AddGlobalVar(mangledName, std::move(ctx.def)); } } } template void CHIR2BCHIR::TranslateFunctions(const Package& chirPkg) { for (const auto f : chirPkg.GetGlobalFuncs()) { auto fIdent = f->GetIdentifierWithoutPrefix(); if (isIncremental && bchir.GetFunctions().find(fIdent) != bchir.GetFunctions().end()) { bchir.RemoveFunction(fIdent); } if constexpr (ForConstEval) { bool missingBody = f->TestAttr(Attribute::SKIP_ANALYSIS) && !f->GetBody(); if (missingBody) { continue; } // If ForConstEval we only need to translate the IsCompileTimeValue expressions. if ( // it is a const function f->IsCompileTimeValue() || // function that can be executed during const-eval CONST_FUNCTIONS.count(f->GetIdentifier()) > 0 || // interpreter relies on this set of functions (bchir.IsCore() && std::find(Bchir::defaultFunctionsManledNames.begin(), Bchir::defaultFunctionsManledNames.end(), fIdent) != Bchir::defaultFunctionsManledNames.end()) || // it is a finalizer -- at the moment there is no easy way to find out if a class is const. // The right way to do this would be to mark the finalizer as const, when there is a const init. f->IsFinalizer()) { // need to translate function for const eval } else { // no need to translate function for const eval continue; } } CJC_ASSERT(f->GetBody()); Context ctx; TranslateFuncDef(ctx, *f); bchir.AddFunction(fIdent, std::move(ctx.def)); if (f->GetFuncKind() == FuncKind::MAIN_ENTRY) { bchir.SetMainMangledName(fIdent); bchir.SetMainExpectedArgs(f->GetNumOfParams()); } } } // force instantiation of TranslatePackage with ForConstEval = True and ForConstEval = false template void CHIR2BCHIR::TranslatePackage( const Package& chirPkg, const std::vector& initFuncsForConstVar); template void CHIR2BCHIR::TranslatePackage( const Package& chirPkg, const std::vector& initFuncsForConstVar); Bchir::ByteCodeContent CHIR2BCHIR::GetTypeIdx(Cangjie::CHIR::Type& chirType) { auto itTy = typesMemoization.find(&chirType); if (itTy != typesMemoization.end()) { return itTy->second; } auto tyIdx = bchir.AddType(chirType); CJC_ASSERT(tyIdx < static_cast(Bchir::BYTECODE_CONTENT_MAX)); typesMemoization.emplace_hint(itTy, &chirType, static_cast(tyIdx)); switch (chirType.GetTypeKind()) { case CHIR::Type::TYPE_INT8: case CHIR::Type::TYPE_INT16: case CHIR::Type::TYPE_INT32: case CHIR::Type::TYPE_INT64: case CHIR::Type::TYPE_INT_NATIVE: case CHIR::Type::TYPE_UINT8: case CHIR::Type::TYPE_UINT16: case CHIR::Type::TYPE_UINT32: case CHIR::Type::TYPE_UINT64: case CHIR::Type::TYPE_UINT_NATIVE: case CHIR::Type::TYPE_FLOAT16: case CHIR::Type::TYPE_FLOAT32: case CHIR::Type::TYPE_FLOAT64: case CHIR::Type::TYPE_BOOLEAN: case CHIR::Type::TYPE_RUNE: case CHIR::Type::TYPE_NOTHING: case CHIR::Type::TYPE_CSTRING: case CHIR::Type::TYPE_ENUM: case CHIR::Type::TYPE_STRUCT: case CHIR::Type::TYPE_UNIT: break; case CHIR::Type::TYPE_CPOINTER: { auto& cType = StaticCast(chirType); auto elemType = cType.GetElementType(); (void)GetTypeIdx(*elemType); break; } case CHIR::Type::TYPE_TUPLE: { auto& tType = StaticCast(chirType); for (auto& it : tType.GetElementTypes()) { (void)GetTypeIdx(*it); } break; } case CHIR::Type::TYPE_FUNC: { auto& fType = StaticCast(chirType); for (auto& it : fType.GetParamTypes()) { (void)GetTypeIdx(*it); } auto retType = fType.GetReturnType(); (void)GetTypeIdx(*retType); break; } case CHIR::Type::TYPE_RAWARRAY: { auto& rType = StaticCast(chirType); auto elemType = rType.GetElementType(); (void)GetTypeIdx(*elemType); break; } case CHIR::Type::TYPE_REFTYPE: { // we can have reference types for CTypes generated by the inout auto& refType = StaticCast(chirType); auto elemType = refType.GetBaseType(); (void)GetTypeIdx(*elemType); break; } case CHIR::Type::TYPE_CLASS: { // Temporarily disable CJC_ASSERT(false) break; } default: break; } return static_cast(tyIdx); } template CHIR2BCHIR::Context CHIR2BCHIR::TranslateGlobalVar(const GlobalVar& gv) { Context ctx; if (auto init = gv.GetInitializer()) { // init is always a literal value, otherwise var will be initialized by a function TranslateLiteralValue(ctx, *init); } else { CJC_ASSERT(gv.GetInitFunc() != nullptr); ctx.def.Push(OpCode::NULLPTR); if constexpr (ForConstEval) { // store the mangled name of the initializer function ctx.def.AddMangledNameAnnotation(ctx.def.NextIndex() - 1, gv.GetInitFunc()->GetIdentifierWithoutPrefix()); } } // we will append OpCode::GVAR_SET during linking] return ctx; } void CHIR2BCHIR::TranslateFuncDef(Context& ctx, const Func& func) { auto args = func.GetParams(); // function parameters are simply local variables // reverse iterator is because the last argument will be at the top of the stack for (auto arg = args.crbegin(); arg != args.crend(); ++arg) { ctx.def.Push(OpCode::LVAR_SET); CJC_ASSERT(ctx.val2lvarId.find(*arg) == ctx.val2lvarId.end()); ctx.def.Push(LVarId(ctx, **arg)); } // dropping the func arg itself ctx.def.Push(OpCode::DROP); // start translating the entry block auto bbGroup = func.GetBody(); CJC_ASSERT(bbGroup->GetEntryBlock()); TranslateBlock(ctx, *bbGroup->GetEntryBlock()); // translate all other blocks for (auto bb : bbGroup->GetBlocks()) { if (bb == bbGroup->GetEntryBlock()) { continue; } TranslateBlock(ctx, *bb); } // we use this number to pop from argStack ctx.def.SetNumArgs(static_cast(func.GetNumOfParams())); // we use this number to reserve space for a env(stack) frame ctx.def.SetNumLVars(ctx.localVarId); } void CHIR2BCHIR::TranslateBlock(Context& ctx, const Block& bb) { ResolveBB2IndexPlaceHolder(ctx, bb, ctx.def.NextIndex()); for (auto expr : bb.GetExpressions()) { if (expr->GetExprKind() == ExprKind::DEBUGEXPR) { continue; } TranslateExpression(ctx, *expr); // Statements without result, i.e. terminators are not required to have a local var // Only terminator expr has no result and they do not push values on stack // A statement "%1 = expr" essentially represents a local var if (!expr->IsTerminator()) { PushOpCodeWithAnnotations(ctx, OpCode::LVAR_SET, *expr, LVarId(ctx, *expr->GetResult())); } } } void CHIR2BCHIR::TranslateExpression(Context& ctx, const Expression& expr) { if (expr.GetExprKind() == ExprKind::INVOKE || expr.GetExprKind() == ExprKind::INVOKE_WITH_EXCEPTION) { // Create a dummy value on the arg stack, to be dropped when entering the function implementing the method // Functions in BCHIR have the strict form LVAR_SET :: ID1 :: ... :: LVAR_SET :: IDn :: DROP :: body, where // - LVAR_SET :: ID1 :: ... :: LVAR_SET :: IDn initializes the function arguments by popping args from the stack // - DROP drops the FUNC value from the arg stack (or NULLPTR when the function was INVOKED) PushOpCodeWithAnnotations(ctx, OpCode::NULLPTR, expr); } // translate all operands for (auto value : expr.GetOperands()) { TranslateValue(ctx, *value); } switch (expr.GetExprMajorKind()) { case ExprMajorKind::TERMINATOR: TranslateTerminatorExpression(ctx, expr); break; case ExprMajorKind::UNARY_EXPR: CJC_ASSERT(expr.GetResult()); TranslateUnaryExpression(ctx, expr); break; case ExprMajorKind::BINARY_EXPR: CJC_ASSERT(expr.GetResult()); TranslateBinaryExpression(ctx, expr); break; case ExprMajorKind::MEMORY_EXPR: CJC_ASSERT(expr.GetResult()); TranslateMemoryExpression(ctx, expr); break; case ExprMajorKind::STRUCTURED_CTRL_FLOW_EXPR: // unreachable CJC_ASSERT(false); PushOpCodeWithAnnotations(ctx, OpCode::ABORT, expr); break; case ExprMajorKind::OTHERS: CJC_ASSERT(expr.GetResult()); TranslateOthersExpression(ctx, expr); break; } } Bchir::ByteCodeContent CHIR2BCHIR::BlockIndex(Context& ctx, const Block& bb, Bchir::ByteCodeIndex indexPlaceHolder) { auto bb2IndexIt = ctx.bb2Index.find(&bb); if (bb2IndexIt != ctx.bb2Index.end()) { return bb2IndexIt->second; } auto it = ctx.bb2IndexPlaceHolder.find(&bb); if (it == ctx.bb2IndexPlaceHolder.end()) { std::vector vec{indexPlaceHolder}; ctx.bb2IndexPlaceHolder.emplace(&bb, vec); } else { it->second.emplace_back(indexPlaceHolder); } return static_cast(0); } void CHIR2BCHIR::ResolveBB2IndexPlaceHolder(Context& ctx, const Block& bb, Bchir::ByteCodeIndex idx) { ctx.bb2Index.emplace(&bb, idx); auto it = ctx.bb2IndexPlaceHolder.find(&bb); if (it != ctx.bb2IndexPlaceHolder.end()) { for (auto ph : it->second) { ctx.def.Set(ph, idx); } } ctx.bb2IndexPlaceHolder.erase(&bb); } Bchir::ByteCodeContent CHIR2BCHIR::GetStringIdx(std::string str) { auto literalId = constStringsMemoization.find(str); if (literalId == constStringsMemoization.end()) { auto constIdx = bchir.AddString(str); CJC_ASSERT(constIdx <= static_cast(Bchir::BYTECODE_CONTENT_MAX)); constStringsMemoization.emplace(str, static_cast(constIdx)); return static_cast(constIdx); } return literalId->second; } Bchir::ByteCodeContent CHIR2BCHIR::LVarId(Context& ctx, const Value& value) { auto it = ctx.val2lvarId.find(&value); if (it != ctx.val2lvarId.end()) { return it->second; } ctx.val2lvarId.emplace_hint(it, &value, ctx.localVarId); return ctx.localVarId++; // increment ctx.localVarId } void CHIR2BCHIR::TranslateAllocate(Context& ctx, const Expression& expr) { CHIR::Type* ty; bool withException = false; if (expr.GetExprKind() == ExprKind::ALLOCATE) { auto allocate = StaticCast(&expr); ty = allocate->GetType(); } else { CJC_ASSERT(expr.GetExprKind() == ExprKind::ALLOCATE_WITH_EXCEPTION); auto allocate = StaticCast(&expr); ty = allocate->GetType(); withException = true; } if (ty->IsClass()) { auto classTy = StaticCast(ty); auto numberOfFields = classTy->GetClassDef()->GetAllInstanceVarNum(); auto idx = ctx.def.NextIndex(); auto opCode = withException ? OpCode::ALLOCATE_CLASS_EXC : OpCode::ALLOCATE_CLASS; PushOpCodeWithAnnotations(ctx, opCode, expr, 0, static_cast(numberOfFields)); ctx.def.AddMangledNameAnnotation(idx, classTy->GetClassDef()->GetIdentifierWithoutPrefix()); } else if (ty->IsStruct()) { auto structTy = StaticCast(ty); auto opCode = withException ? OpCode::ALLOCATE_STRUCT_EXC : OpCode::ALLOCATE_STRUCT; PushOpCodeWithAnnotations( ctx, opCode, expr, static_cast(structTy->GetStructDef()->GetAllInstanceVarNum())); } else { auto opCode = withException ? OpCode::ALLOCATE_EXC : OpCode::ALLOCATE; PushOpCodeWithAnnotations(ctx, opCode, expr); } } const std::unordered_map CHIR2BCHIR::syscall2IntrinsicKind = { {CJ_AST_LEX, IntrinsicKind::FFI_CJ_AST_LEX}, {CJ_ASTPARSEEXPR, IntrinsicKind::FFI_CJ_AST_PARSEEXPR}, {CJ_ASTPARSEDECL, IntrinsicKind::FFI_CJ_AST_PARSEDECL}, {CJ_ASTPARSE_PROPMEMBERDECL, IntrinsicKind::FFI_CJ_AST_PARSE_PROPMEMBERDECL}, {CJ_ASTPARSE_PRICONSTRUCTOR, IntrinsicKind::FFI_CJ_AST_PARSE_PRICONSTRUCTOR}, {CJ_ASTPARSEPATTERN, IntrinsicKind::FFI_CJ_AST_PARSE_PATTERN}, {CJ_ASTPARSETYPE, IntrinsicKind::FFI_CJ_AST_PARSE_TYPE}, {CJ_ASTPARSETOPLEVEL, IntrinsicKind::FFI_CJ_AST_PARSETOPLEVEL}, {CJ_PARENT_CONTEXT, IntrinsicKind::FFI_CJ_PARENT_CONTEXT}, {CJ_MACRO_ITEM_INFO, IntrinsicKind::FFI_CJ_MACRO_ITEM_INFO}, {CJ_GET_CHILD_MESSAGES, IntrinsicKind::FFI_CJ_GET_CHILD_MESSAGES}, {CJ_CHECK_ADD_SPACE, IntrinsicKind::FFI_CJ_CHECK_ADD_SPACE}, {CJ_AST_DIAGREPORT, IntrinsicKind::FFI_CJ_AST_DIAGREPORT}, {CJ_TLS_DYN_SET_SESSION_CALLBACK_NAME, IntrinsicKind::CJ_TLS_DYN_SET_SESSION_CALLBACK}, {CJ_TLS_DYN_SSL_INIT_NAME, IntrinsicKind::CJ_TLS_DYN_SSL_INIT}, #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND {CJ_CORE_CAN_USE_SIMD_NAME, IntrinsicKind::CJ_CORE_CAN_USE_SIMD}, {"std.core:CJ_CORE_CanUseSIMD", IntrinsicKind::CJ_CORE_CAN_USE_SIMD}, #endif }; Bchir::CodePosition CHIR2BCHIR::CHIRPos2BCHIRPos(const DebugLocation& loc) { auto fileName = sourceManager.GetSource(static_cast(loc.GetFileID())).path; auto fileId = fileNameToIndexMemoization.find(fileName); size_t fileIdx = 0; if (fileId == fileNameToIndexMemoization.end()) { fileIdx = bchir.AddFileName(fileName); fileNameToIndexMemoization.emplace_hint(fileId, fileName, fileIdx); } else { fileIdx = fileId->second; } return Bchir::CodePosition{fileIdx, loc.GetBeginPos().line, loc.GetBeginPos().column}; } template void CHIR2BCHIR::AddAnnotations(Context& ctx, const Expression& expr, Bchir::ByteCodeIndex opCodeIndex) { if constexpr (StoreMangledName) { auto result = expr.GetResult(); if (result != nullptr) { auto mangled = result->GetIdentifierWithoutPrefix(); ctx.def.AddMangledNameAnnotation(opCodeIndex, mangled); } } if constexpr (StoreCodePos) { auto& loc = expr.GetDebugLocation(); auto bchirPos = CHIRPos2BCHIRPos(loc); ctx.def.AddCodePositionAnnotation(opCodeIndex, bchirPos); } } template void CHIR2BCHIR::AddAnnotations(Context& ctx, const Value& value, Bchir::ByteCodeContent opCodeIndex) { if constexpr (StoreMangledName) { auto mangled = value.GetIdentifierWithoutPrefix(); ctx.def.AddMangledNameAnnotation(opCodeIndex, mangled); } if constexpr (StoreCodePos) { auto& loc = value.GetDebugLocation(); auto bchirPos = CHIRPos2BCHIRPos(loc); ctx.def.AddCodePositionAnnotation(opCodeIndex, bchirPos); } } template void CHIR2BCHIR::AddAnnotations( Context& ctx, const Value& value, Bchir::ByteCodeContent opCodeIndex); template void CHIR2BCHIR::AddAnnotations( Context& ctx, const Value& value, Bchir::ByteCodeContent opCodeIndex); template void CHIR2BCHIR::AddAnnotations( Context& ctx, const Value& value, Bchir::ByteCodeContent opCodeIndex); template void CHIR2BCHIR::AddAnnotations( Context& ctx, const Value& value, Bchir::ByteCodeContent opCodeIndex); template void CHIR2BCHIR::AddAnnotations( Context& ctx, const Expression& expr, Bchir::ByteCodeIndex opCodeIndex); template void CHIR2BCHIR::AddAnnotations( Context& ctx, const Expression& expr, Bchir::ByteCodeIndex opCodeIndex); template void CHIR2BCHIR::AddAnnotations( Context& ctx, const Expression& expr, Bchir::ByteCodeIndex opCodeIndex); template void CHIR2BCHIR::AddAnnotations( Context& ctx, const Expression& expr, Bchir::ByteCodeIndex opCodeIndex);cangjie_compiler-1.0.7/src/CHIR/Interpreter/CHIR2BCHIR/000077500000000000000000000000001510705540100222055ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CHIR/Interpreter/CHIR2BCHIR/CMakeLists.txt000066400000000000000000000005521510705540100247470ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB BCHIRInterpreter *.cpp) set(CHIRInterpreter ${CHIRInterpreter} ${BCHIRInterpreter} PARENT_SCOPE)cangjie_compiler-1.0.7/src/CHIR/Interpreter/CHIR2BCHIR/TranslateBinaryExpression.cpp000066400000000000000000000023561510705540100301010ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements a translation from CHIR to BCHIR. */ #include "cangjie/CHIR/Interpreter/CHIR2BCHIR.h" #include "cangjie/CHIR/Interpreter/Utils.h" using namespace Cangjie::CHIR; using namespace Interpreter; void CHIR2BCHIR::TranslateBinaryExpression(Context& ctx, const Expression& expr) { CJC_ASSERT(expr.GetNumOfOperands() == Bchir::FLAG_TWO); auto binaryExpression = StaticCast(&expr); auto opCode = Cangjie::CHIR::Interpreter::BinExprKind2OpCode(expr.GetExprKind()); auto typeKind = binaryExpression->GetOperand(0)->GetType()->GetTypeKind(); auto overflowStrat = static_cast(binaryExpression->GetOverflowStrategy()); PushOpCodeWithAnnotations(ctx, opCode, expr, typeKind, overflowStrat); if (opCode == OpCode::BIN_LSHIFT || opCode == OpCode::BIN_RSHIFT) { ctx.def.Push(static_cast(binaryExpression->GetOperand(1)->GetType()->GetTypeKind())); } }cangjie_compiler-1.0.7/src/CHIR/Interpreter/CHIR2BCHIR/TranslateIntrinsic.cpp000066400000000000000000000111031510705540100265250ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements a translation from CHIR intrinsics to BCHIR intrinsics. */ #include "cangjie/CHIR/Interpreter/BCHIR.h" #include "cangjie/CHIR/Interpreter/CHIR2BCHIR.h" #include "cangjie/CHIR/Interpreter/Utils.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Type/StructDef.h" using namespace Cangjie::CHIR; using namespace Interpreter; template void CHIR2BCHIR::TranslateIntrinsicExpression(Context& ctx, const T& intrinsic) { /* One of the following depending on the necessary additional information bchir :: INTRINSIC0 :: INTRINSIC_KIND or bchir :: INTRINSIC0 :: INTRINSIC_KIND :: AUX_INFO1 or bchir :: INTRINSIC2 :: INTRINSIC_KIND :: AUX_INFO2 :: AUX_INFO2 Note that we insert an additional argument to store additional information. For some intrinsic functions we need to store some type information. Depending on the intrinsic operation this can represent different things. */ if (intrinsic.GetIntrinsicKind() == CHIR::IntrinsicKind::CG_UNSAFE_BEGIN || intrinsic.GetIntrinsicKind() == CHIR::IntrinsicKind::CG_UNSAFE_END) { return; } auto isCType = [](const CHIR::Type& ty) { // T0D0: is an array of arrays a C type? probably yes! return ty.IsPrimitive() || ty.IsCString() || (ty.IsStruct() && StaticCast(&ty)->GetStructDef()->IsCStruct()); }; std::vector auxInfo{}; switch (intrinsic.GetIntrinsicKind()) { case CHIR::IntrinsicKind::ARRAY_BUILT_IN_COPY_TO: case CHIR::IntrinsicKind::ARRAY_CLONE: case CHIR::IntrinsicKind::ARRAY_ACQUIRE_RAW_DATA: { auto refTy = StaticCast(intrinsic.GetOperands()[0]->GetType()); auto arrayTy = StaticCast(refTy->GetTypeArgs()[0]); auto valueTy = arrayTy->GetTypeArgs()[0]; if (isCType(*valueTy)) { // T0D0: can we store the type of the array content instead? auxInfo.emplace_back(GetTypeIdx(*arrayTy)); } else { auxInfo.emplace_back(UINT32_MAX); } break; } case CHIR::IntrinsicKind::ARRAY_GET: case CHIR::IntrinsicKind::ARRAY_GET_UNCHECKED: case CHIR::IntrinsicKind::ARRAY_SET: case CHIR::IntrinsicKind::ARRAY_SET_UNCHECKED: { auto refTy = StaticCast(intrinsic.GetOperands()[0]->GetType()); auto arrayTy = StaticCast(refTy->GetTypeArgs()[0]); auto valueTy = arrayTy->GetTypeArgs()[0]; if (isCType(*valueTy)) { auxInfo.emplace_back(GetTypeIdx(*valueTy)); } else { auxInfo.emplace_back(UINT32_MAX); } break; } case CHIR::IntrinsicKind::VARRAY_GET: { auto pathSize = static_cast(intrinsic.GetNumOfOperands()); PushOpCodeWithAnnotations(ctx, OpCode::VARRAY_GET, intrinsic, pathSize - 1); return; } case CHIR::IntrinsicKind::BEGIN_CATCH: // Behaves like id function. return; case CHIR::IntrinsicKind::OBJECT_REFEQ: { break; // nothing to do here, just trying to be exhaustive } default: { // by default translate unlisted intrinsic as INTRINSIC0 break; } } auto intrinsicKind = static_cast(intrinsic.GetIntrinsicKind()); CJC_ASSERT(auxInfo.size() <= Bchir::FLAG_THREE); auto opCode = auxInfo.size() == 0 ? OpCode::INTRINSIC0 : (auxInfo.size() == 1 ? OpCode::INTRINSIC1 : OpCode::INTRINSIC2); if (intrinsic.GetExprKind() != ExprKind::INTRINSIC) { CJC_ASSERT(intrinsic.GetExprKind() == ExprKind::INTRINSIC_WITH_EXCEPTION); opCode = static_cast(static_cast(opCode) + Bchir::FLAG_THREE); } PushOpCodeWithAnnotations(ctx, opCode, intrinsic, intrinsicKind); if (auxInfo.size() > 0) { ctx.def.Push(auxInfo[0]); if (auxInfo.size() > 1) { ctx.def.Push(auxInfo[1]); } } } // force instantiation of TranslateIntrinsicExpression with Intrinsic and IntrinsicWithException template void CHIR2BCHIR::TranslateIntrinsicExpression(Context& ctx, const Intrinsic& intrinsic); cangjie_compiler-1.0.7/src/CHIR/Interpreter/CHIR2BCHIR/TranslateMemoryExpression.cpp000066400000000000000000000046311510705540100301230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements a translation from CHIR to BCHIR. */ #include "cangjie/CHIR/Interpreter/CHIR2BCHIR.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/StructDef.h" using namespace Cangjie::CHIR; using namespace Interpreter; void CHIR2BCHIR::TranslateMemoryExpression(Context& ctx, const Expression& expr) { switch (expr.GetExprKind()) { case ExprKind::ALLOCATE: { CJC_ASSERT(expr.GetNumOfOperands() == 0U); TranslateAllocate(ctx, expr); break; } case ExprKind::LOAD: { CJC_ASSERT(expr.GetNumOfOperands() == 1U); PushOpCodeWithAnnotations(ctx, OpCode::DEREF, expr); break; } case ExprKind::STORE: { CJC_ASSERT(expr.GetNumOfOperands() == Bchir::FLAG_TWO); PushOpCodeWithAnnotations(ctx, OpCode::ASG, expr); break; } case ExprKind::GET_ELEMENT_REF: { CJC_ASSERT(expr.GetNumOfOperands() == 1U); auto getElementRefExpr = StaticCast(&expr); PushOpCodeWithAnnotations( ctx, OpCode::GETREF, expr, static_cast(getElementRefExpr->GetPath().size())); for (auto i : getElementRefExpr->GetPath()) { CJC_ASSERT(i <= Bchir::BYTECODE_CONTENT_MAX); ctx.def.Push(static_cast(i)); } break; } case ExprKind::STORE_ELEMENT_REF: { CJC_ASSERT(expr.GetNumOfOperands() == 2U); auto storeElementRefExpr = StaticCast(&expr); PushOpCodeWithAnnotations( ctx, OpCode::STOREINREF, expr, static_cast(storeElementRefExpr->GetPath().size())); for (auto i : storeElementRefExpr->GetPath()) { CJC_ASSERT(i <= Bchir::BYTECODE_CONTENT_MAX); ctx.def.Push(static_cast(i)); } break; } default: { // unreachable CJC_ASSERT(false); PushOpCodeWithAnnotations(ctx, OpCode::ABORT, expr); } } } cangjie_compiler-1.0.7/src/CHIR/Interpreter/CHIR2BCHIR/TranslateOthersExpression.cpp000066400000000000000000000275001510705540100301170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements a translation from CHIR to BCHIR. */ #include "cangjie/CHIR/Interpreter/CHIR2BCHIR.h" #include "cangjie/CHIR/Interpreter/Utils.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Utils.h" using namespace Cangjie::CHIR; using namespace Interpreter; void CHIR2BCHIR::TranslateOthersExpression(Context& ctx, const Expression& expr) { switch (expr.GetExprKind()) { case ExprKind::DEBUGEXPR: { CJC_ASSERT(false); break; } case ExprKind::CONSTANT: { // Nothing to be done here. The literal was already encoded because it is the argument 0 // of expr. break; } case ExprKind::TUPLE: { CJC_ASSERT(expr.GetNumOfOperands() > 0); CJC_ASSERT(expr.GetNumOfOperands() <= static_cast(Bchir::BYTECODE_CONTENT_MAX)); PushOpCodeWithAnnotations(ctx, OpCode::TUPLE, expr, static_cast(expr.GetNumOfOperands())); break; } case ExprKind::FIELD: { TranslateField(ctx, expr); break; } case ExprKind::APPLY: { CJC_ASSERT(expr.GetNumOfOperands() > 0); CJC_ASSERT(expr.GetNumOfOperands() <= static_cast(Bchir::BYTECODE_CONTENT_MAX)); TranslateApplyExpression(ctx, *StaticCast(&expr)); break; } case ExprKind::INVOKE: { TranslateInvoke(ctx, expr); break; } case ExprKind::INSTANCEOF: { CJC_ASSERT(expr.GetNumOfOperands() == 1); auto instanceOfExpr = StaticCast(&expr); TranslateInstanceOf(ctx, *instanceOfExpr); break; } case ExprKind::TYPECAST: { TranslateTypecast(ctx, expr); break; } case ExprKind::INTRINSIC: { TranslateIntrinsicExpression(ctx, *StaticCast(&expr)); break; } case ExprKind::GET_EXCEPTION: { PushOpCodeWithAnnotations(ctx, OpCode::GET_EXCEPTION, expr); break; } case ExprKind::RAW_ARRAY_ALLOCATE: { PushOpCodeWithAnnotations(ctx, OpCode::ALLOCATE_RAW_ARRAY, expr); break; } case ExprKind::RAW_ARRAY_LITERAL_INIT: { CJC_ASSERT(expr.GetNumOfOperands() > 0); // array + arguments PushOpCodeWithAnnotations( ctx, OpCode::RAW_ARRAY_LITERAL_INIT, expr, static_cast(expr.GetNumOfOperands() - 1)); break; } case ExprKind::RAW_ARRAY_INIT_BY_VALUE: { PushOpCodeWithAnnotations(ctx, OpCode::RAW_ARRAY_INIT_BY_VALUE, expr); break; } case ExprKind::VARRAY: { CJC_ASSERT(expr.GetNumOfOperands() < static_cast(Bchir::BYTECODE_CONTENT_MAX)); auto vArraySize = static_cast(expr.GetNumOfOperands()); PushOpCodeWithAnnotations(ctx, OpCode::VARRAY, expr, vArraySize); break; } case ExprKind::BOX: { CJC_ASSERT(expr.GetNumOfOperands() == 1); auto boxExpr = StaticCast(&expr); TranslateBox(ctx, *boxExpr); break; } case ExprKind::UNBOX: { CJC_ASSERT(expr.GetNumOfOperands() == 1); PushOpCodeWithAnnotations(ctx, OpCode::UNBOX, expr); break; } case ExprKind::UNBOX_TO_REF: { CJC_ASSERT(expr.GetNumOfOperands() == 1); PushOpCodeWithAnnotations(ctx, OpCode::UNBOX_REF, expr); break; } case ExprKind::VARRAY_BUILDER: case ExprKind::SPAWN: case ExprKind::INVOKESTATIC: case ExprKind::GET_RTTI: case ExprKind::GET_RTTI_STATIC: case ExprKind::TRANSFORM_TO_CONCRETE: case ExprKind::TRANSFORM_TO_GENERIC: { // We currently don't support these operations. If they are reached during interpretation // the interpreter will terminate with exception. PushOpCodeWithAnnotations(ctx, OpCode::ABORT, expr); break; } default: { // unreachable CJC_ASSERT(false); PushOpCodeWithAnnotations(ctx, OpCode::ABORT, expr); } } } void CHIR2BCHIR::TranslateField(Context& ctx, const Expression& expr) { auto fieldExpr = StaticCast(&expr); auto indexes = fieldExpr->GetPath(); CJC_ASSERT(indexes.size() > 0); if (indexes.size() == 1) { PushOpCodeWithAnnotations(ctx, OpCode::FIELD, expr, static_cast(indexes[0])); } else { CJC_ASSERT(fieldExpr->GetOperands()[0]->GetType()->IsStruct() || fieldExpr->GetOperands()[0]->GetType()->IsEnum() || fieldExpr->GetOperands()[0]->GetType()->IsTuple()); PushOpCodeWithAnnotations(ctx, OpCode::FIELD_TPL, expr, static_cast(indexes.size())); for (auto i : indexes) { ctx.def.Push(static_cast(i)); } } } void CHIR2BCHIR::TranslateInvoke(Context& ctx, const Expression& expr) { CJC_ASSERT(expr.GetNumOfOperands() > 0); CJC_ASSERT(expr.GetNumOfOperands() <= static_cast(Bchir::BYTECODE_CONTENT_MAX)); auto invokeExpr = StaticCast(&expr); auto idx = ctx.def.NextIndex(); // we dont store mangled name here PushOpCodeWithAnnotations( ctx, OpCode::INVOKE, expr, static_cast(expr.GetNumOfOperands()), 0); auto methodName = MangleMethodName(invokeExpr->GetMethodName(), *invokeExpr->GetMethodType()); ctx.def.AddMangledNameAnnotation(idx, methodName); } void CHIR2BCHIR::TranslateTypecast(Context& ctx, const Expression& expr) { CJC_ASSERT(expr.GetNumOfOperands() == 1); auto typeCastExpr = StaticCast(&expr); auto srcTy = typeCastExpr->GetSourceTy(); auto dstTy = typeCastExpr->GetTargetTy(); if (srcTy->IsPrimitive() && dstTy->IsPrimitive()) { auto srcTyIdx = srcTy->GetTypeKind(); auto dstTyIdx = dstTy->GetTypeKind(); auto overflowStrat = static_cast(typeCastExpr->GetOverflowStrategy()); PushOpCodeWithAnnotations(ctx, OpCode::TYPECAST, expr, srcTyIdx, dstTyIdx, overflowStrat); } else { CJC_ASSERT((!srcTy->IsPrimitive() && !dstTy->IsPrimitive()) || (srcTy->IsEnum() && IsEnumSelectorType(*dstTy)) || (IsEnumSelectorType(*srcTy) && dstTy->IsEnum())); } } void CHIR2BCHIR::TranslateInstanceOf(Context& ctx, const InstanceOf& expr) { auto opIdx = ctx.def.Size(); CJC_ASSERT(opIdx <= static_cast(Bchir::BYTECODE_CONTENT_MAX)); PushOpCodeWithAnnotations(ctx, OpCode::INSTANCEOF, expr); ctx.def.Push(0); // dummy value, this will be resolved during linking if (expr.GetType()->IsRef()) { auto refTy = StaticCast(expr.GetType()); auto classTy = StaticCast(refTy->GetBaseType()); auto classDef = classTy->GetClassDef(); ctx.def.AddMangledNameAnnotation(static_cast(opIdx), classDef->GetIdentifierWithoutPrefix()); } else if (expr.GetType()->IsPrimitive()) { auto primitiveClassName = expr.GetType()->ToString(); ctx.def.AddMangledNameAnnotation(static_cast(opIdx), primitiveClassName); } else { auto customTy = StaticCast(expr.GetType()); auto customDef = customTy->GetCustomTypeDef(); ctx.def.AddMangledNameAnnotation( static_cast(opIdx), customDef->GetIdentifierWithoutPrefix()); } } void CHIR2BCHIR::TranslateBox(Context& ctx, const Box& expr) { auto opIdx = ctx.def.Size(); CJC_ASSERT(opIdx <= static_cast(Bchir::BYTECODE_CONTENT_MAX)); PushOpCodeWithAnnotations(ctx, OpCode::BOX, expr, 0); auto ty = expr.GetSourceTy(); if (ty->IsStruct()) { auto structTy = StaticCast(ty); auto structDef = structTy->GetStructDef(); ctx.def.AddMangledNameAnnotation( static_cast(opIdx), structDef->GetIdentifierWithoutPrefix()); } else if (ty->IsEnum()) { auto enumTy = StaticCast(ty); auto enumDef = enumTy->GetEnumDef(); ctx.def.AddMangledNameAnnotation( static_cast(opIdx), enumDef->GetIdentifierWithoutPrefix()); } else { // this is a primitive type CJC_ASSERT(ty->IsPrimitive()); ctx.def.AddMangledNameAnnotation(static_cast(opIdx), ty->ToString()); } } void CHIR2BCHIR::TranslateCApplyExpression(Context& ctx, const Apply& apply, const Cangjie::CHIR::FuncType& funcTy) { // bchir :: CAPPLY // :: CFUNC_NUMBER_OF_ARGS :: CFUNC_RESULT_TY :: CFUNC_ARG1_TY :: ... :: CFUNC_ARGN_TY // The number of funcTy and GetNumOfOperands size_t numberArgs = apply.GetNumOfOperands() - 1; // remove param 0 func; CJC_ASSERT(numberArgs == funcTy.GetParamTypes().size()); CJC_ASSERT(numberArgs <= static_cast(Bchir::BYTECODE_CONTENT_MAX)); PushOpCodeWithAnnotations( ctx, OpCode::CAPPLY, apply, static_cast(funcTy.GetParamTypes().size())); auto addTyAnnotation = [this, &ctx](CHIR::Type& type) { ctx.def.Push(GetTypeIdx(type)); }; addTyAnnotation(*funcTy.GetReturnType()); for (auto ty : funcTy.GetParamTypes()) { addTyAnnotation(*ty); } } void CHIR2BCHIR::TranslateApplyExpression(Context& ctx, const Apply& apply) { auto operands = apply.GetOperands(); auto funcExpr = operands[0]; auto funcTy = funcExpr->GetType(); if ((funcExpr->IsImportedFunc() && funcExpr->GetAttributeInfo().TestAttr(Attribute::FOREIGN)) || funcExpr->GetSrcCodeIdentifier() == "std.core:CJ_CORE_CanUseSIMD") { // This is an hack. These functions should be intrinsic in CHIR 2.0. For the time being // we simply translate them as INTRINSIC1. auto it = syscall2IntrinsicKind.find(funcExpr->GetSrcCodeIdentifier()); if (it != syscall2IntrinsicKind.end()) { // We use INTRINSIC1 instead of INTRINSIC0 so that we know that the dummy function node // needs to be popped from the argument stack. Revert changes once these functions are marked // as intrinsic in CHIR 2.0. PushOpCodeWithAnnotations(ctx, OpCode::INTRINSIC1, apply, it->second, UINT32_MAX); return; } // bchir :: SYSCALL :: syscallName_STRING_IDX :: NUMBER_OF_ARGS // :: ANNOTATION_RESULT_TY :: ANNOTATION_ARG1_TY :: ... :: ANNOTATION_ARGN_TY auto strIdx = GetStringIdx(funcExpr->GetSrcCodeIdentifier()); auto numberArgs = apply.GetNumOfOperands() - 1; CJC_ASSERT(numberArgs <= static_cast(Bchir::BYTECODE_CONTENT_MAX)); PushOpCodeWithAnnotations( ctx, OpCode::SYSCALL, apply, strIdx, static_cast(numberArgs)); auto addTyAnnotation = [this, &ctx](CHIR::Type& type) { ctx.def.Push(GetTypeIdx(type)); }; addTyAnnotation(*apply.GetResult()->GetType()); // skip the first operand which is the function for (size_t i = 1; i < operands.size(); ++i) { addTyAnnotation(*operands[i]->GetType()); } return; } else if (StaticCast(*funcTy).IsCFunc()) { TranslateCApplyExpression(ctx, apply, StaticCast(*funcTy)); return; } PushOpCodeWithAnnotations(ctx, OpCode::APPLY, apply, static_cast(apply.GetNumOfOperands())); } cangjie_compiler-1.0.7/src/CHIR/Interpreter/CHIR2BCHIR/TranslateTerminatorExpression.cpp000066400000000000000000000216121510705540100307750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements a translation from CHIR to BCHIR. */ #include "cangjie/CHIR/Interpreter/CHIR2BCHIR.h" #include "cangjie/CHIR/Interpreter/Utils.h" #include "cangjie/CHIR/Type/StructDef.h" using namespace Cangjie::CHIR; using namespace Interpreter; void CHIR2BCHIR::TranslateTerminatorExpression(Context& ctx, const Expression& expr) { switch (expr.GetExprKind()) { case ExprKind::GOTO: { CJC_ASSERT(expr.GetNumOfOperands() == 0); auto gotoExpr = StaticCast(&expr); CJC_ASSERT(gotoExpr->GetNumOfSuccessor() == 1); auto dest = gotoExpr->GetSuccessor(0); PushOpCodeWithAnnotations(ctx, OpCode::JUMP, expr); auto bbIndex = BlockIndex(ctx, *dest, ctx.def.NextIndex()); ctx.def.Push(bbIndex); break; } case ExprKind::BRANCH: { CJC_ASSERT(expr.GetNumOfOperands() == 1); auto branchExpr = StaticCast(&expr); CJC_ASSERT(branchExpr->GetNumOfSuccessor() == Bchir::FLAG_TWO); PushOpCodeWithAnnotations(ctx, OpCode::BRANCH, expr); auto trueBB = branchExpr->GetSuccessor(0); auto bbTrueIndex = BlockIndex(ctx, *trueBB, ctx.def.NextIndex()); ctx.def.Push(bbTrueIndex); auto falseBB = branchExpr->GetSuccessor(1); auto bbFalseIndex = BlockIndex(ctx, *falseBB, ctx.def.NextIndex()); ctx.def.Push(bbFalseIndex); break; } case ExprKind::MULTIBRANCH: { auto multi = StaticCast(&expr); TranslateMultiBranch(ctx, *multi); break; } case ExprKind::EXIT: { CJC_ASSERT(expr.GetNumOfOperands() == 0); CJC_ASSERT(expr.GetTopLevelFunc() != nullptr); auto ret = expr.GetTopLevelFunc()->GetReturnValue(); if (ret == nullptr) { // this function does not have a return var PushOpCodeWithAnnotations(ctx, OpCode::UNIT, expr); } else { // load the return value PushOpCodeWithAnnotations(ctx, OpCode::LVAR, expr, LVarId(ctx, *ret)); PushOpCodeWithAnnotations(ctx, OpCode::DEREF, expr); } PushOpCodeWithAnnotations(ctx, OpCode::RETURN, expr); break; } case ExprKind::RAISE_EXCEPTION: { CJC_ASSERT(expr.GetNumOfOperands() == 1); auto raise = StaticCast(&expr); if (raise->GetNumOfSuccessor() == 0) { PushOpCodeWithAnnotations(ctx, OpCode::RAISE, expr); } else { CJC_ASSERT(raise->GetNumOfSuccessor() == 1); PushOpCodeWithAnnotations(ctx, OpCode::RAISE_EXC, expr); auto exceptionBbIndex = BlockIndex(ctx, *raise->GetSuccessor(0), ctx.def.NextIndex()); ctx.def.Push(exceptionBbIndex); } break; } case ExprKind::APPLY_WITH_EXCEPTION: { // :: APPLY_EXC :: number_of_args :: idx_when_exception :: LVAR_SET :: lvar_id // :: JUMP :: idx_when_normal_return auto apply = StaticCast(&expr); TranslateApplyWithExceptionExpression(ctx, *apply); break; } case ExprKind::INVOKE_WITH_EXCEPTION: { // :: INVOKE_EXC :: number_of_args :: method_name :: idx_when_exception :: LVAR_SET :: lvar_id // :: JUMP :: idx_when_normal_return CJC_ASSERT(expr.GetNumOfOperands() > 0); CJC_ASSERT(expr.GetNumOfOperands() <= static_cast(Bchir::BYTECODE_CONTENT_MAX)); auto invoke = StaticCast(&expr); auto idx = ctx.def.NextIndex(); // we dont store mangled name here PushOpCodeWithAnnotations( ctx, OpCode::INVOKE_EXC, expr, static_cast(expr.GetNumOfOperands()), 0); auto methodName = MangleMethodName(invoke->GetMethodName(), *invoke->GetMethodType()); ctx.def.AddMangledNameAnnotation(idx, methodName); TranslateTryTerminatorJumps(ctx, *invoke); break; } case ExprKind::INVOKESTATIC_WITH_EXCEPTION: { PushOpCodeWithAnnotations(ctx, OpCode::ABORT, expr); break; } case ExprKind::INT_OP_WITH_EXCEPTION: { auto intOpWithException = StaticCast(&expr); TranslateIntOpWithException(ctx, *intOpWithException); TranslateTryTerminatorJumps(ctx, *intOpWithException); break; } case ExprKind::ALLOCATE_WITH_EXCEPTION: { CJC_ASSERT(expr.GetNumOfOperands() == 0); TranslateAllocate(ctx, expr); TranslateTryTerminatorJumps(ctx, *StaticCast(&expr)); break; } default: { // unreachable CJC_ASSERT(false); PushOpCodeWithAnnotations(ctx, OpCode::ABORT, expr); break; } } } void CHIR2BCHIR::TranslateApplyWithExceptionExpression(Context& ctx, const ApplyWithException& apply) { PushOpCodeWithAnnotations( ctx, OpCode::APPLY_EXC, apply, static_cast(apply.GetNumOfOperands())); TranslateTryTerminatorJumps(ctx, apply); } void CHIR2BCHIR::TranslateTryTerminatorJumps(Context& ctx, const Terminator& expr) { CJC_ASSERT(expr.GetNumOfSuccessor() == Bchir::FLAG_TWO); auto exceptionBB = expr.GetSuccessor(1); auto exceptionBbIndex = BlockIndex(ctx, *exceptionBB, ctx.def.NextIndex()); ctx.def.Push(exceptionBbIndex); // a statement "%1 = expr" essentially represents a local var PushOpCodeWithAnnotations(ctx, OpCode::LVAR_SET, expr, LVarId(ctx, *expr.GetResult())); PushOpCodeWithAnnotations(ctx, OpCode::JUMP, expr); auto normalBB = expr.GetSuccessor(0); auto normalBbIndex = BlockIndex(ctx, *normalBB, ctx.def.NextIndex()); ctx.def.Push(normalBbIndex); } void CHIR2BCHIR::TranslateMultiBranch(Context& ctx, const MultiBranch& branch) { // Assuming there are no values repeated // [| MultiBranch(selector, b0, [c1, b1], ..., [cn, bn] |] = // BSEARCH // SWITCH :: TYPE :: number_values :: case_1 (8 bytes) :: ... :: case_n (8 bytes) :: // default_target :: case_1_target :: ... :: case_n_target auto& cases = branch.GetCaseVals(); auto& successors = branch.GetSuccessors(); auto ty = branch.GetOperand(0)->GetType(); auto tyKind = ty->IsEnum() ? CHIR::Type::TypeKind::TYPE_UINT64 : ty->GetTypeKind(); std::vector> casesToSuccessors; for (size_t i = 0; i < cases.size(); ++i) { casesToSuccessors.emplace_back(cases[i], successors[i + 1]); } std::sort(casesToSuccessors.begin(), casesToSuccessors.end(), [](auto& left, auto& right) { return left.first < right.first; }); PushOpCodeWithAnnotations(ctx, OpCode::SWITCH, branch, static_cast(tyKind), static_cast(cases.size())); for (auto it = casesToSuccessors.begin(); it != casesToSuccessors.end(); ++it) { // These are sorted ctx.def.Push8bytes(it->first); } auto defaultBB = BlockIndex(ctx, *successors[0], ctx.def.NextIndex()); ctx.def.Push(defaultBB); for (auto it = casesToSuccessors.begin(); it != casesToSuccessors.end(); ++it) { // These are sorted auto bbIndex = BlockIndex(ctx, *it->second, ctx.def.NextIndex()); ctx.def.Push(bbIndex); } } void CHIR2BCHIR::TranslateIntOpWithException(Context& ctx, const IntOpWithException& expr) { auto opCode = Cangjie::CHIR::Interpreter::BinExprKindWitException2OpCode(expr.GetOpKind()); auto typeKind = expr.GetOperand(0)->GetType()->GetTypeKind(); auto overflowStrat = static_cast(Cangjie::OverflowStrategy::THROWING); if (opCode == OpCode::UN_NEG_EXC) { CJC_ASSERT(expr.GetNumOfOperands() == 1); } else { CJC_ASSERT(expr.GetNumOfOperands() == Bchir::FLAG_TWO); CJC_ASSERT(opCode == OpCode::BIN_ADD_EXC || opCode == OpCode::BIN_SUB_EXC || opCode == OpCode::BIN_MUL_EXC || opCode == OpCode::BIN_DIV_EXC || opCode == OpCode::BIN_MOD_EXC || opCode == OpCode::BIN_EXP_EXC || opCode == OpCode::BIN_LSHIFT_EXC || opCode == OpCode::BIN_RSHIFT_EXC); } PushOpCodeWithAnnotations(ctx, opCode, expr, typeKind, overflowStrat); if (opCode == OpCode::BIN_LSHIFT_EXC || opCode == OpCode::BIN_RSHIFT_EXC) { ctx.def.Push(static_cast(expr.GetOperand(1)->GetType()->GetTypeKind())); } } cangjie_compiler-1.0.7/src/CHIR/Interpreter/CHIR2BCHIR/TranslateUnaryExpression.cpp000066400000000000000000000020141510705540100277420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements a translation from CHIR to BCHIR. */ #include "cangjie/CHIR/Interpreter/CHIR2BCHIR.h" #include "cangjie/CHIR/Interpreter/Utils.h" using namespace Cangjie::CHIR; using namespace Interpreter; void CHIR2BCHIR::TranslateUnaryExpression(Context& ctx, const Expression& expr) { CJC_ASSERT(expr.GetNumOfOperands() == Bchir::FLAG_ONE); auto opCode = Cangjie::CHIR::Interpreter::UnExprKind2OpCode(expr.GetExprKind()); auto unaryExpression = StaticCast(&expr); auto typeKind = expr.GetResult()->GetType()->GetTypeKind(); auto overflow = static_cast(unaryExpression->GetOverflowStrategy()); PushOpCodeWithAnnotations(ctx, opCode, expr, typeKind, overflow); }cangjie_compiler-1.0.7/src/CHIR/Interpreter/CHIR2BCHIR/TranslateValue.cpp000066400000000000000000000113511510705540100256440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements a translation from CHIR to BCHIR. */ #include "cangjie/CHIR/Interpreter/CHIR2BCHIR.h" #include #include "cangjie/CHIR/Interpreter/Utils.h" #include "cangjie/CHIR/Utils.h" using namespace Cangjie::CHIR; using namespace Interpreter; void CHIR2BCHIR::TranslateValue(Context& ctx, const Value& value) { if (value.IsParameter()) { // argument are translated as LVar PushOpCodeWithAnnotations(ctx, OpCode::LVAR, value, LVarId(ctx, value)); } else if (value.IsLocalVar()) { // local vars PushOpCodeWithAnnotations(ctx, OpCode::LVAR, value, LVarId(ctx, value)); } else if (value.IsLiteral()) { auto literal = StaticCast(&value); TranslateLiteralValue(ctx, *literal); } else if (value.IsImportedFunc() && value.GetAttributeInfo().TestAttr(Attribute::FOREIGN)) { // this is a syscall and will never be used PushOpCodeWithAnnotations(ctx, OpCode::NULLPTR, value); } else if (value.IsFuncWithBody()) { PushOpCodeWithAnnotations(ctx, OpCode::FUNC, value, UINT32_MAX); } else if (Is(value) || value.IsImportedFunc()) { // global vars and imported vars will be resolved during linking auto mangledName = value.GetIdentifierWithoutPrefix(); if (mangledName == CHIR::GV_PKG_INIT_ONCE_FLAG) { // This is an hack because $has_applied_pkg_init_func is not a real mangled name. It's // not unique amongst packages. T0D0: issue 2079 auto opIdx = ctx.def.NextIndex(); PushOpCodeWithAnnotations(ctx, OpCode::GVAR, value, 0); auto fixedMangledName = CHIR::GV_PKG_INIT_ONCE_FLAG + "-" + bchir.packageName; ctx.def.AddMangledNameAnnotation(opIdx, fixedMangledName); } else { PushOpCodeWithAnnotations(ctx, OpCode::GVAR, value, 0u); } } else { CJC_ABORT(); } } void CHIR2BCHIR::TranslateLiteralValue(Context& ctx, const LiteralValue& value) { if (value.IsBoolLiteral()) { auto boolVal = StaticCast(&value); PushOpCodeWithAnnotations(ctx, OpCode::BOOL, value, boolVal->GetVal()); } else if (value.IsFloatLiteral()) { auto floatLit = StaticCast(&value); TranslateFloatValue(ctx, *floatLit); } else if (value.IsIntLiteral()) { auto intLit = StaticCast(&value); TranslateIntValue(ctx, *intLit); } else if (value.IsNullLiteral()) { PushOpCodeWithAnnotations(ctx, OpCode::NULLPTR, value); } else if (value.IsRuneLiteral()) { auto charLit = StaticCast(&value); PushOpCodeWithAnnotations(ctx, OpCode::RUNE, value, charLit->GetVal()); } else if (value.IsStringLiteral()) { auto stringLit = StaticCast(&value); PushOpCodeWithAnnotations(ctx, OpCode::STRING, *stringLit, GetStringIdx(stringLit->GetVal())); } else if (value.IsUnitLiteral()) { PushOpCodeWithAnnotations(ctx, OpCode::UNIT, value); } else { CJC_ABORT(); } } void CHIR2BCHIR::TranslateIntValue(Context& ctx, const IntLiteral& value) { auto typeKind = value.GetType()->GetTypeKind(); auto opcode = PrimitiveTypeKind2OpCode(typeKind); PushOpCodeWithAnnotations(ctx, opcode, value); if (typeKind == Type::TypeKind::TYPE_INT64 || typeKind == Type::TypeKind::TYPE_UINT64 || typeKind == Type::TypeKind::TYPE_INT_NATIVE || typeKind == Type::TypeKind::TYPE_UINT_NATIVE) { ctx.def.Push8bytes(value.GetUnsignedVal()); } else { ctx.def.Push(static_cast(value.GetUnsignedVal())); } } void CHIR2BCHIR::TranslateFloatValue(Context& ctx, const FloatLiteral& value) { auto typeKind = value.GetType()->GetTypeKind(); auto opcode = PrimitiveTypeKind2OpCode(typeKind); PushOpCodeWithAnnotations(ctx, opcode, value); if (typeKind == Type::TypeKind::TYPE_FLOAT64) { uint64_t tmp{0}; double d = value.GetVal(); auto ret = memcpy_s(&tmp, sizeof(tmp), &d, sizeof(d)); if (ret != EOK) { CJC_ABORT(); } else { ctx.def.Push8bytes(tmp); } } else { Bchir::ByteCodeContent tmp{0}; float d = static_cast(value.GetVal()); auto ret = memcpy_s(&tmp, sizeof(tmp), &d, sizeof(d)); if (ret != EOK) { CJC_ABORT(); } else { ctx.def.Push(tmp); } } return; }cangjie_compiler-1.0.7/src/CHIR/Interpreter/CHIR2BCHIRAtomic.cpp000066400000000000000000000005511510705540100240470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements a translation from CHIR to BCHIR for atomic operations. */ cangjie_compiler-1.0.7/src/CHIR/Interpreter/CMakeLists.txt000066400000000000000000000005621510705540100233710ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB CHIRInterpreter *.cpp) add_subdirectory(CHIR2BCHIR) set(CHIRInterpreter ${CHIRInterpreter} PARENT_SCOPE)cangjie_compiler-1.0.7/src/CHIR/Interpreter/ConstEval.cpp000066400000000000000000000545041510705540100232400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements a translation from CHIR to BCHIR for atomic operations. */ #include #include #include #include #include #include #include #include #include #include using Cangjie::CHIR::Interpreter::ConstEvalPass; using Cangjie::CHIR::Interpreter::IVal2CHIR; Cangjie::CHIR::Constant* IVal2CHIR::TryConvertToConstant(Type& ty, const IVal& val, Block& parent) { switch (ty.GetTypeKind()) { case Type::TYPE_INT8: return chirBuilder.CreateConstantExpression( &ty, &parent, static_cast(IValUtils::Get(val).content)); case Type::TYPE_INT16: return chirBuilder.CreateConstantExpression( &ty, &parent, static_cast(IValUtils::Get(val).content)); case Type::TYPE_INT32: return chirBuilder.CreateConstantExpression( &ty, &parent, static_cast(IValUtils::Get(val).content)); case Type::TYPE_INT64: return chirBuilder.CreateConstantExpression( &ty, &parent, static_cast(IValUtils::Get(val).content)); case Type::TYPE_INT_NATIVE: return chirBuilder.CreateConstantExpression( &ty, &parent, static_cast(IValUtils::Get(val).content)); case Type::TYPE_UINT8: return chirBuilder.CreateConstantExpression(&ty, &parent, IValUtils::Get(val).content); case Type::TYPE_UINT16: return chirBuilder.CreateConstantExpression(&ty, &parent, IValUtils::Get(val).content); case Type::TYPE_UINT32: return chirBuilder.CreateConstantExpression(&ty, &parent, IValUtils::Get(val).content); case Type::TYPE_UINT64: return chirBuilder.CreateConstantExpression(&ty, &parent, IValUtils::Get(val).content); case Type::TYPE_UINT_NATIVE: return chirBuilder.CreateConstantExpression( &ty, &parent, IValUtils::Get(val).content); case Type::TYPE_FLOAT16: return chirBuilder.CreateConstantExpression( &ty, &parent, IValUtils::Get(val).content); case Type::TYPE_FLOAT32: return chirBuilder.CreateConstantExpression( &ty, &parent, IValUtils::Get(val).content); case Type::TYPE_FLOAT64: return chirBuilder.CreateConstantExpression( &ty, &parent, IValUtils::Get(val).content); case Type::TYPE_RUNE: return chirBuilder.CreateConstantExpression(&ty, &parent, IValUtils::Get(val).content); case Type::TYPE_BOOLEAN: return chirBuilder.CreateConstantExpression(&ty, &parent, IValUtils::Get(val).content); default: if (IValUtils::GetIf(&val)) { return chirBuilder.CreateConstantExpression(&ty, &parent); } return nullptr; } } Cangjie::CHIR::Value* IVal2CHIR::ConvertToChir( Type& ty, const IVal& val, std::function& insertExpr, Block& parent) { auto constant = TryConvertToConstant(ty, val, parent); if (constant) { insertExpr(constant); return constant->GetResult(); } switch (ty.GetTypeKind()) { case Type::TYPE_TUPLE: case Type::TYPE_STRUCT: { auto tuple = IValUtils::Get(val); if (ty.IsString()) { // Codegen doesn't support strings in constant initializers, so // we have strings here and not in TryConvertToConstant. return ConvertStringToChir(ty, tuple, insertExpr, parent); } return ConvertTupleToChir(ty, tuple, insertExpr, parent); } case Type::TYPE_ENUM: { return ConvertEnumToChir(StaticCast(ty), val, insertExpr, parent); } case Type::TYPE_REFTYPE: { return ConvertRefToChir(StaticCast(ty), val, insertExpr, parent); } case Type::TYPE_UNIT: { // Unit is not supported as a global variable initializer auto expr = chirBuilder.CreateConstantExpression(&ty, &parent); insertExpr(expr); return expr->GetResult(); } case Type::TYPE_VARRAY: { return ConvertArrayToChir(StaticCast(ty), IValUtils::Get(val), insertExpr, parent); } // Should always be behind RefType. case Type::TYPE_CLASS: // Not supported as constants. case Type::TYPE_FUNC: case Type::TYPE_GENERIC: case Type::TYPE_NOTHING: case Type::TYPE_RAWARRAY: case Type::TYPE_CPOINTER: case Type::TYPE_CSTRING: case Type::TYPE_VOID: case Type::TYPE_INVALID: return nullptr; default: Cangjie::InternalError("unsupported type kind"); return nullptr; } } Cangjie::CHIR::Value* IVal2CHIR::ConvertStringToChir( Type& ty, const ITuple& val, const std::function& insertExpr, Block& parent) { std::string stringVal; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto& arrPtr = IValUtils::Get(val.content[0]); auto& arrData = IValUtils::Get(*arrPtr.content).content; auto start = IValUtils::Get(val.content[1]).content; CJC_ASSERT(start == static_cast(0) && "const strings always start at zero"); auto len = IValUtils::Get(val.content[2]).content; stringVal.reserve(len); CJC_ASSERT(arrData.size() > static_cast(len)); for (uint32_t el = 0; el < len; el++) { stringVal.push_back(static_cast(IValUtils::Get(arrData[el + 1]).content)); } #endif auto expr = chirBuilder.CreateConstantExpression(&ty, &parent, stringVal); insertExpr(expr); return expr->GetResult(); } Cangjie::CHIR::Value* IVal2CHIR::ConvertTupleToChir( Type& ty, const ITuple& val, std::function& insertExpr, Block& parent) { std::vector elementTys; if (ty.GetTypeKind() == Type::TYPE_STRUCT) { elementTys = StaticCast(ty).GetInstantiatedMemberTys(chirBuilder); } else { elementTys = ty.GetTypeArgs(); } auto elementVals = val.content; std::vector elements; CJC_ASSERT(elementVals.size() == elementTys.size()); for (std::size_t idx = 0; idx < elementVals.size(); idx++) { auto elementVal = ConvertToChir(*elementTys[idx], elementVals[idx], insertExpr, parent); if (!elementVal) { return nullptr; } elements.push_back(elementVal); } if (ty.IsStruct()) { // Codegen doesn't support the use of tuples for structs, so we need to allocate the struct, and assign its // fields auto refTy = chirBuilder.GetType(&ty); auto allocate = chirBuilder.CreateExpression(refTy, &ty, &parent); insertExpr(allocate); for (size_t i = 0; i < elements.size(); ++i) { auto store = chirBuilder.CreateExpression( chirBuilder.GetUnitTy(), elements[i], allocate->GetResult(), std::vector{i}, &parent); insertExpr(store); } auto load = chirBuilder.CreateExpression(&ty, allocate->GetResult(), &parent); insertExpr(load); return load->GetResult(); } auto expr = chirBuilder.CreateExpression(&ty, elements, &parent); insertExpr(expr); return expr->GetResult(); } Cangjie::CHIR::Value* IVal2CHIR::ConvertEnumToChir( EnumType& ty, const IVal& val, std::function& insertExpr, Block& parent) { auto selectorTyKind = GetSelectorType(*ty.GetEnumDef()); auto selectorTy = chirBuilder.GetChirContext().ToSelectorType(selectorTyKind); if (ty.GetEnumDef()->IsAllCtorsTrivial()) { auto expr = chirBuilder.CreateConstantExpression( selectorTy, &parent, IValUtils::Get(val).content); auto ret = chirBuilder.CreateExpression(&ty, expr->GetResult(), &parent); insertExpr(expr); insertExpr(ret); return ret->GetResult(); } else { auto elementVals = IValUtils::Get(val).content; std::vector elements; uint64_t variantIndex; if (selectorTy->IsBoolean()) { variantIndex = static_cast(IValUtils::Get(elementVals[0]).content); elements.push_back(ConvertToChir(*selectorTy, elementVals[0], insertExpr, parent)); } else { variantIndex = IValUtils::Get(elementVals[0]).content; elements.push_back(ConvertToChir(*selectorTy, elementVals[0], insertExpr, parent)); } auto elementTys = ty.GetConstructorInfos(chirBuilder)[variantIndex].funcType->GetParamTypes(); CJC_ASSERT(elementVals.size() == elementTys.size() + 1); for (std::size_t idx = 0; idx < elementTys.size(); idx++) { auto elementVal = ConvertToChir(*elementTys[idx], elementVals[idx + 1], insertExpr, parent); if (!elementVal) { return nullptr; } elements.push_back(elementVal); } auto expr = chirBuilder.CreateExpression(&ty, elements, &parent); insertExpr(expr); return expr->GetResult(); } } Cangjie::CHIR::Value* IVal2CHIR::ConvertRefToChir( RefType& ty, const IVal& val, std::function& insertExpr, Block& parent) { auto referencedType = ty.GetBaseType(); auto valContent = IValUtils::Get(val).content; switch (referencedType->GetTypeKind()) { case Type::TYPE_CLASS: { auto obj = IValUtils::GetIf(valContent); // This is only partially implemeneted: // If a constant references another constant, then it should not create a deep copy of that constant. auto classType = StaticCast(referencedType); auto& valClassName = bchir.GetClassTable().find(obj->classId)->second.mangledName; auto refType = &ty; auto needCast = false; if (valClassName != classType->GetClassDef()->GetIdentifierWithoutPrefix()) { needCast = true; classType = FindClassType(valClassName); if (classType == nullptr) { return nullptr; } refType = chirBuilder.GetType(classType); } auto allocExpr = chirBuilder.CreateExpression(refType, classType, &parent); insertExpr(allocExpr); auto classVal = allocExpr->GetResult(); auto members = classType->GetClassDef()->GetAllInstanceVars(); CJC_ASSERT(members.size() == obj->content.size()); for (std::uint64_t idx = 0; idx < members.size(); idx++) { auto memberTy = members[idx].type; auto memberVal = ConvertToChir(*memberTy, obj->content[idx], insertExpr, parent); if (!memberVal) { return nullptr; } insertExpr(chirBuilder.CreateExpression( chirBuilder.GetUnitTy(), memberVal, classVal, std::vector{idx}, &parent)); } if (needCast) { auto typeCast = chirBuilder.CreateExpression(&ty, classVal, &parent); insertExpr(typeCast); return typeCast->GetResult(); } return classVal; } default: { // Other types not supported by const eval return nullptr; } } } Cangjie::CHIR::ClassType* IVal2CHIR::FindClassType(const std::string& mangledName) { // Object was upcast, try to locate the dynamic type. auto classes = package.GetClasses(); auto result = std::find_if(classes.begin(), classes.end(), [&mangledName](const ClassDef* classDef) { return mangledName == classDef->GetIdentifierWithoutPrefix(); }); ClassDef* resultClassDef; if (result != classes.end()) { resultClassDef = *result; } else { auto imports = package.GetImportedClasses(); auto importResult = std::find_if(imports.begin(), imports.end(), [&mangledName](const ClassDef* classDef) { return mangledName == classDef->GetIdentifierWithoutPrefix(); }); if (importResult == imports.end()) { return nullptr; } resultClassDef = *importResult; } if (resultClassDef->GetGenericTypeParams().size() != 0) { return nullptr; } return chirBuilder.GetType(resultClassDef); } Cangjie::CHIR::Value* IVal2CHIR::ConvertArrayToChir( VArrayType& ty, const IArray& val, std::function& insertExpr, Block& parent) { auto& elementVals = val.content; std::vector elements; elements.reserve(elementVals.size()); auto elementTy = ty.GetElementType(); for (std::size_t idx = 0; idx < elementVals.size(); idx++) { auto elementVal = ConvertToChir(*elementTy, elementVals[idx], insertExpr, parent); if (!elementVal) { return nullptr; } elements.push_back(elementVal); } auto expr = chirBuilder.CreateExpression(&ty, elements, &parent); insertExpr(expr); return expr->GetResult(); } void ConstEvalPass::RunOnPackage(Package& package, const std::vector& initFuncsForConstVar, std::vector& bchirPackages) { RunInterpreter(package, bchirPackages, initFuncsForConstVar, [this](auto& package, auto& interpreter, auto& linker) { ReplaceGlobalConstantInitializers(package, interpreter, linker); }); } void ConstEvalPass::RunInterpreter(Package& package, std::vector& bchirPackages, const std::vector& initFuncsForConstVar, std::function onSuccess) { Utils::ProfileRecorder::Start("Constant Evaluation", "CHIR2BCHIR for const-eval"); auto printBchir = opts.PrintBchir(GlobalOptions::PrintBCHIROption::CE_CHIR2BCHIR); auto& packageBchir = bchirPackages.emplace_back(); CHIR2BCHIR::CompileToBCHIR(package, packageBchir, initFuncsForConstVar, sourceManager, opts, printBchir, ci.kind == IncreKind::INCR); Utils::ProfileRecorder::Stop("Constant Evaluation", "CHIR2BCHIR for const-eval"); Utils::ProfileRecorder::Start("Constant Evaluation", "BCHIR linker for const-eval"); Bchir linkedBchir; BCHIRLinker linker(linkedBchir); printBchir = opts.PrintBchir(GlobalOptions::PrintBCHIROption::CE_LINKED); auto gVarInitIVals = linker.Run(bchirPackages, opts); Utils::ProfileRecorder::Stop("Constant Evaluation", "BCHIR linker for const-eval"); auto fePlayground = linkedBchir.GetLinkedByteCode().Size(); auto interpPlayground = fePlayground + BCHIRInterpreter::EXTERNAL_PLAYGROUND_SIZE; const size_t extraReqSpace = BCHIRInterpreter::INTERNAL_PLAYGROUND_SIZE + BCHIRInterpreter::EXTERNAL_PLAYGROUND_SIZE; // resize vector for the playground linkedBchir.Resize(fePlayground + extraReqSpace); std::unordered_map dyHandles{}; BCHIRInterpreter interpreter(linkedBchir, diag, dyHandles, static_cast(fePlayground), static_cast(interpPlayground), true); #ifndef NDEBUG interpreter.PrepareRuntimeDebug(opts); #endif interpreter.SetGlobalVars(std::move(gVarInitIVals)); gVarInitIVals = {}; Utils::ProfileRecorder::Start("Constant Evaluation", "Evaluate global vars"); auto res = interpreter.Run(0, false); Utils::ProfileRecorder::Stop("Constant Evaluation", "Evaluate global vars"); if (std::holds_alternative(res)) { onSuccess(package, interpreter, linker); } else if (std::holds_alternative(res)) { // Suppress error, no way to know whether exception is legitimate } } void ConstEvalPass::ReplaceGlobalConstantInitializers( Package& package, BCHIRInterpreter& interpreter, BCHIRLinker& linker) { Utils::ProfileRecorder recorder("Constant Evaluation", "Replace Global Constants"); auto allFuncs = package.GetGlobalFuncs(); std::vector funcsToBeRemoved; std::unordered_set expressionsToBeRemoved; auto it = allFuncs.begin(); while (it != allFuncs.end()) { if ((*it)->GetFuncKind() != FuncKind::GLOBALVAR_INIT || !(*it)->TestAttr(Attribute::CONST)) { ++it; continue; } auto optNewBody = CreateNewInitializer(**it, interpreter, linker, package); if (opts.chirDebugOptimizer) { auto& pos = (*it)->GetDebugLocation(); PrintDebugMessage(pos, **it, optNewBody); } if (!optNewBody.has_value()) { ++it; continue; } else if (optNewBody.value() == nullptr) { auto users = (*it)->GetUsers(); expressionsToBeRemoved.insert(users.begin(), users.end()); funcsToBeRemoved.emplace_back(*it); it = allFuncs.erase(it); } else { (*optNewBody) ->GetEntryBlock() ->AppendExpression(builder.CreateTerminator((*optNewBody)->GetEntryBlock())); (*it)->DestroySelf(); (*it)->InitBody(**optNewBody); ++it; } } for (auto e : expressionsToBeRemoved) { e->RemoveSelfFromBlock(); } package.SetGlobalFuncs(allFuncs); } std::optional ConstEvalPass::CreateNewInitializer( Func& oldInitializer, const BCHIRInterpreter& interpreter, const BCHIRLinker& linker, const Package& package) { BlockGroup* newBody = nullptr; if (ci.invocation.globalOptions.enIncrementalCompilation) { newBody = builder.CreateBlockGroup(oldInitializer); newBody->SetOwnerFunc(&oldInitializer); newBody->SetEntryBlock(builder.CreateBlock(newBody)); } for (auto block : oldInitializer.GetBody()->GetBlocks()) { for (auto expr : block->GetExpressions()) { if (expr->GetExprKind() != ExprKind::STORE) { continue; } auto location = StaticCast(expr)->GetLocation(); if (!location->IsGlobalVarInCurPackage()) { continue; } auto global = VirtualCast(location); auto varId = linker.GetGVARId(global->GetIdentifierWithoutPrefix()); CJC_ASSERT(varId != -1); auto& val = interpreter.PeekValueOfGlobal(static_cast(varId)); IVal2CHIR val2chir(builder, interpreter.GetBchir(), package); auto varRefType = global->GetType(); CJC_ASSERT(varRefType->IsRef()); auto varType = varRefType->GetTypeArgs()[0]; auto constant = val2chir.TryConvertToConstant(*varType, val, *block); if (constant) { block->AppendExpression(constant); } if (constant && !constant->IsConstantNull()) { global->SetInitializer(*constant->GetValue()); } else { if (!newBody) { newBody = builder.CreateBlockGroup(oldInitializer); newBody->SetOwnerFunc(&oldInitializer); newBody->SetEntryBlock(builder.CreateBlock(newBody)); } auto newBlock = newBody->GetEntryBlock(); std::function insertFunction = [newBlock](Expression* expr) { newBlock->AppendExpression(expr); }; auto constValue = val2chir.ConvertToChir(*varType, val, insertFunction, *newBlock); if (!constValue) { return std::nullopt; } newBlock->AppendExpression( builder.CreateExpression(builder.GetUnitTy(), constValue, global, newBlock)); } } } return newBody; } void ConstEvalPass::PrintDebugMessage( const DebugLocation& loc, const Func& oldInit, const std::optional& newInit) const { auto file = FileUtil::GetFileName(loc.GetAbsPath()); std::string begin = file + ":" + std::to_string(loc.GetBeginPos().line) + ":" + std::to_string(loc.GetBeginPos().column); std::string end = file + ":" + std::to_string(loc.GetEndPos().line) + ":" + std::to_string(loc.GetEndPos().column); if (!newInit.has_value()) { std::cout << "debug: consteval at " << begin << " - " << end << " function `" << oldInit.GetSrcCodeIdentifier() << "` not evaluated successfully." << std::endl; } else if (newInit.value() == nullptr) { std::cout << "debug: consteval at " << begin << " - " << end << " replaced initializer function `" << oldInit.GetSrcCodeIdentifier() << "` with initializer constant(s)." << std::endl; } else { auto oldBody = oldInit.GetBody()->GetBlocks(); auto newBody = (*newInit)->GetBlocks(); std::cout << "debug: consteval at " << begin << " - " << end << " evaluated initializer function `" << oldInit.GetSrcCodeIdentifier() << "` of " << std::accumulate(oldBody.cbegin(), oldBody.cend(), static_cast(0), [](auto acc, auto& block) { return acc + block->GetExpressions().size(); }) << " expressions to one of " << std::accumulate(newBody.cbegin(), newBody.cend(), static_cast(0), [](auto acc, auto& block) { return acc + block->GetExpressions().size(); }) << " expressions." << std::endl; } } cangjie_compiler-1.0.7/src/CHIR/Interpreter/InterpreterValueUtils.cpp000066400000000000000000000006031510705540100256520ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements interpreter values. */ #include "cangjie/CHIR/Interpreter/InterpreterValueUtils.h" cangjie_compiler-1.0.7/src/CHIR/Interpreter/Utils.cpp000066400000000000000000000177171510705540100224470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements some utility functions for interpreter module. */ #include "cangjie/CHIR/Interpreter/Utils.h" #include #include "cangjie/CHIR/Interpreter/InterpreterValueUtils.h" using namespace Cangjie::CHIR; using namespace Cangjie::CHIR::Interpreter; OpCode Interpreter::PrimitiveTypeKind2OpCode(Type::TypeKind kind) { switch (kind) { case Type::TypeKind::TYPE_INT64: return OpCode::INT64; case Type::TypeKind::TYPE_INT32: return OpCode::INT32; case Type::TypeKind::TYPE_INT16: return OpCode::INT16; case Type::TypeKind::TYPE_INT8: return OpCode::INT8; case Type::TypeKind::TYPE_INT_NATIVE: return OpCode::INTNAT; case Type::TypeKind::TYPE_UINT64: return OpCode::UINT64; case Type::TypeKind::TYPE_UINT32: return OpCode::UINT32; case Type::TypeKind::TYPE_UINT16: return OpCode::UINT16; case Type::TypeKind::TYPE_UINT8: return OpCode::UINT8; case Type::TypeKind::TYPE_UINT_NATIVE: return OpCode::UINTNAT; case Type::TypeKind::TYPE_FLOAT64: return OpCode::FLOAT64; case Type::TypeKind::TYPE_FLOAT32: return OpCode::FLOAT32; case Type::TypeKind::TYPE_FLOAT16: return OpCode::FLOAT16; default: CJC_ASSERT(false); return OpCode::INVALID; } } OpCode Interpreter::UnExprKind2OpCode(Cangjie::CHIR::ExprKind exprKind) { switch (exprKind) { case ExprKind::NEG: return OpCode::UN_NEG; case ExprKind::NOT: return OpCode::UN_NOT; case ExprKind::BITNOT: return OpCode::UN_BITNOT; default: { CJC_ASSERT(false); return OpCode::INVALID; } } } OpCode Interpreter::BinExprKind2OpCode(Cangjie::CHIR::ExprKind exprKind) { switch (exprKind) { case ExprKind::ADD: return OpCode::BIN_ADD; case ExprKind::SUB: return OpCode::BIN_SUB; case ExprKind::MUL: return OpCode::BIN_MUL; case ExprKind::DIV: return OpCode::BIN_DIV; case ExprKind::MOD: return OpCode::BIN_MOD; case ExprKind::LSHIFT: return OpCode::BIN_LSHIFT; case ExprKind::RSHIFT: return OpCode::BIN_RSHIFT; case ExprKind::BITAND: return OpCode::BIN_BITAND; case ExprKind::BITOR: return OpCode::BIN_BITOR; case ExprKind::BITXOR: return OpCode::BIN_BITXOR; case ExprKind::LT: return OpCode::BIN_LT; case ExprKind::GT: return OpCode::BIN_GT; case ExprKind::LE: return OpCode::BIN_LE; case ExprKind::GE: return OpCode::BIN_GE; case ExprKind::EQUAL: return OpCode::BIN_EQUAL; case ExprKind::NOTEQUAL: return OpCode::BIN_NOTEQ; case ExprKind::EXP: return OpCode::BIN_EXP; case ExprKind::AND: case ExprKind::OR: // should have already been desugared at this point // missing break/return intended default: { CJC_ASSERT(false); return OpCode::INVALID; } } } OpCode Interpreter::BinExprKindWitException2OpCode(Cangjie::CHIR::ExprKind exprKind) { switch (exprKind) { case ExprKind::ADD: return OpCode::BIN_ADD_EXC; case ExprKind::SUB: return OpCode::BIN_SUB_EXC; case ExprKind::MUL: return OpCode::BIN_MUL_EXC; case ExprKind::DIV: return OpCode::BIN_DIV_EXC; case ExprKind::MOD: return OpCode::BIN_MOD_EXC; case ExprKind::EXP: return OpCode::BIN_EXP_EXC; case ExprKind::LSHIFT: return OpCode::BIN_LSHIFT_EXC; case ExprKind::RSHIFT: return OpCode::BIN_RSHIFT_EXC; case ExprKind::NEG: return OpCode::UN_NEG_EXC; default: { CJC_ASSERT(false); return OpCode::INVALID; } } } IVal Interpreter::ByteCodeToIval(const Bchir::Definition& def, const Bchir& bchir, Bchir& topBchir) { switch (static_cast(def.Get(0))) { case OpCode::UINT8: return {IValUtils::PrimitiveValue(static_cast(def.Get(1)))}; case OpCode::UINT16: return {IValUtils::PrimitiveValue(static_cast(def.Get(1)))}; case OpCode::UINT32: return {IValUtils::PrimitiveValue(static_cast(def.Get(1)))}; case OpCode::UINT64: return {IValUtils::PrimitiveValue(static_cast(def.Get8bytes(1)))}; case OpCode::UINTNAT: #if (defined(__x86_64__) || defined(__aarch64__)) return {IValUtils::PrimitiveValue(static_cast(def.Get8bytes(1)))}; #else return {IValUtils::PrimitiveValue(static_cast(def.Get8bytes(1)))}; #endif case OpCode::INT8: return {IValUtils::PrimitiveValue(static_cast(def.Get(1)))}; case OpCode::INT16: return {IValUtils::PrimitiveValue(static_cast(def.Get(1)))}; case OpCode::INT32: return {IValUtils::PrimitiveValue(static_cast(def.Get(1)))}; case OpCode::INT64: return {IValUtils::PrimitiveValue(static_cast(def.Get8bytes(1)))}; case OpCode::INTNAT: #if (defined(__x86_64__) || defined(__aarch64__)) return {IValUtils::PrimitiveValue(static_cast(def.Get8bytes(1)))}; #else return {IValUtils::PrimitiveValue(static_cast(def.Get8bytes(1)))}; #endif case OpCode::FLOAT16: return {IValUtils::PrimitiveValue(static_cast(def.Get(1)))}; case OpCode::FLOAT32: return {IValUtils::PrimitiveValue(static_cast(def.Get(1)))}; case OpCode::FLOAT64: { auto tmp = def.Get8bytes(1); double d; auto ret = memcpy_s(&d, sizeof(double), &tmp, sizeof(double)); if (ret != EOK) { CJC_ASSERT(false); return {INullptr()}; } return {IValUtils::PrimitiveValue(d)}; } case OpCode::RUNE: return {IValUtils::PrimitiveValue(def.Get(1))}; case OpCode::BOOL: return {IValUtils::PrimitiveValue(def.Get(1))}; case OpCode::NULLPTR: return {INullptr()}; case OpCode::STRING: { // string values in the interpreter need to match the definition in core /* OPTIMIZE: Instead of having a section of std::strings literals and converting them to match core library RawArray during interpretation, we can have a section of IArray strings, and during interpretation we just create an IPtr pointing to the corresponding IArray string. This is correct because strings are immutable in CJ. Also, the Unicode size of the string should be calculated on translation */ auto strIdx = def.Get(1); auto& str = bchir.GetString(strIdx); auto array = topBchir.StoreStringArray(IValUtils::StringToArray(str)); auto ptr = IPointer(); ptr.content = array; auto tuple = ITuple{{ptr, IValUtils::PrimitiveValue(uint32_t(0)), IValUtils::PrimitiveValue(static_cast(str.size()))}}; return {tuple}; } default: CJC_ASSERT(false); return {INullptr()}; } } cangjie_compiler-1.0.7/src/CHIR/LiteralValue.cpp000066400000000000000000000076011510705540100214240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the literal value related class in CHIR. */ #include "cangjie/CHIR/LiteralValue.h" #include #include #include #include "cangjie/Basic/StringConvertor.h" using namespace Cangjie::CHIR; LiteralValue::LiteralValue(Type* ty, ConstantValueKind literalKind) : Value(ty, "", ValueKind::KIND_LITERAL), literalKind(literalKind) { CJC_ASSERT(literalKind != ConstantValueKind::KIND_FUNC); } bool LiteralValue::IsNullLiteral() const { return literalKind == ConstantValueKind::KIND_NULL; } bool LiteralValue::IsBoolLiteral() const { return literalKind == ConstantValueKind::KIND_BOOL; } bool LiteralValue::IsRuneLiteral() const { return literalKind == ConstantValueKind::KIND_RUNE; } bool LiteralValue::IsStringLiteral() const { return literalKind == ConstantValueKind::KIND_STRING; } bool LiteralValue::IsIntLiteral() const { return literalKind == ConstantValueKind::KIND_INT; } bool LiteralValue::IsFloatLiteral() const { return literalKind == ConstantValueKind::KIND_FLOAT; } bool LiteralValue::IsUnitLiteral() const { return literalKind == ConstantValueKind::KIND_UNIT; } ConstantValueKind LiteralValue::GetConstantValueKind() const { return literalKind; } BoolLiteral::BoolLiteral(Type* ty, bool val) : LiteralValue(ty, ConstantValueKind::KIND_BOOL), val(val) { CJC_ASSERT(ty->IsBoolean()); } bool BoolLiteral::GetVal() const { return val; } std::string BoolLiteral::ToString() const { std::stringstream ss; ss << std::boolalpha << val; return ss.str(); } RuneLiteral::RuneLiteral(Type* ty, char32_t val) : LiteralValue(ty, ConstantValueKind::KIND_RUNE), val(val) { CJC_ASSERT(ty->IsRune()); } char32_t RuneLiteral::GetVal() const { return val; } std::string RuneLiteral::ToString() const { std::stringstream ss; ss << '\'' << val << '\''; return ss.str(); } StringLiteral::StringLiteral(Type* ty, std::string val) : LiteralValue(ty, ConstantValueKind::KIND_STRING), val(val) { CJC_ASSERT(ty->IsString()); } std::string StringLiteral::GetVal() const& { return val; } std::string StringLiteral::GetVal() && { return std::move(val); } std::string StringLiteral::ToString() const { std::stringstream ss; ss << '"' << StringConvertor::Normalize(val) << '"'; return ss.str(); } IntLiteral::IntLiteral(Type* ty, uint64_t val) : LiteralValue(ty, ConstantValueKind::KIND_INT), val(val) { CJC_ASSERT(ty->IsInteger()); } int64_t IntLiteral::GetSignedVal() const { return static_cast(val); } uint64_t IntLiteral::GetUnsignedVal() const { return val; } bool IntLiteral::IsSigned() const { return static_cast(ty)->IsSigned(); } std::string IntLiteral::ToString() const { std::stringstream ss; if (IsSigned()) { ss << GetSignedVal(); ss << 'i'; } else { ss << GetUnsignedVal(); ss << 'u'; } return ss.str(); } FloatLiteral::FloatLiteral(Type* ty, double val) : LiteralValue(ty, ConstantValueKind::KIND_FLOAT), val(val) { } double FloatLiteral::GetVal() const { return val; } std::string FloatLiteral::ToString() const { std::stringstream ss; ss << std::fixed << val << 'f'; return ss.str(); } UnitLiteral::UnitLiteral(Type* ty) : LiteralValue(ty, ConstantValueKind::KIND_UNIT) { } std::string UnitLiteral::ToString() const { std::stringstream ss; ss << "unit"; return ss.str(); } NullLiteral::NullLiteral(Type* ty) : LiteralValue(ty, ConstantValueKind::KIND_NULL) { } std::string NullLiteral::ToString() const { std::stringstream ss; ss << "null"; return ss.str(); } cangjie_compiler-1.0.7/src/CHIR/NativeFFI/000077500000000000000000000000001510705540100200765ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CHIR/NativeFFI/CMakeLists.txt000066400000000000000000000005431510705540100226400ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB NATIVE_FFI_SRC *.cpp) set(CHIR_BASE_SRC ${CHIR_BASE_SRC} ${NATIVE_FFI_SRC} PARENT_SCOPE) cangjie_compiler-1.0.7/src/CHIR/NativeFFI/TypeCastCheck.cpp000066400000000000000000000062751510705540100233060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCastCheck.h" #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Expression/Expression.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/Utils/TaskQueue.h" namespace Cangjie::CHIR::NativeFFI { TypeCastCheck::TypeCastCheck(DiagAdapter& diag) : diag(diag) { } void TypeCastCheck::RunOnPackage(const Package& package, size_t threadNum) { std::vector funcs; for (auto func : package.GetGlobalFuncs()) { if (!func->TestAttr(Attribute::UNSAFE)) { funcs.emplace_back(func); } } if (funcs.empty()) { return; } if (threadNum == 1) { for (auto func : funcs) { RunOnFunc(*func); } } else { Utils::TaskQueue taskQueue(threadNum); for (auto func : funcs) { taskQueue.AddTask([this, func]() { return RunOnFunc(*func); }); } taskQueue.RunAndWaitForAllTasksCompleted(); } } namespace { bool IsNativeFIIType(Type& type) { auto valueType = type.StripAllRefs(); if (!valueType->IsClass()) { return false; } auto classType = StaticCast(valueType); auto classDef = classType->GetClassDef(); return classDef->TestAttr(Attribute::JAVA_IMPL) || classDef->TestAttr(Attribute::JAVA_MIRROR); } std::string GetClassName(Type& type) { auto valueType = type.StripAllRefs(); CJC_ASSERT(valueType->IsClass()); return StaticCast(valueType)->GetClassDef()->GetSrcCodeIdentifier(); } std::optional> TryExtractTypeCast(Expression& expr) { if (expr.GetExprKind() == ExprKind::TYPECAST) { auto& typeCastExpr = StaticCast(expr); return {{typeCastExpr.GetSourceTy(), typeCastExpr.GetTargetTy()}}; } if (expr.GetExprKind() == ExprKind::TYPECAST_WITH_EXCEPTION) { auto& typeCastExpr = StaticCast(expr); return {{typeCastExpr.GetSourceTy(), typeCastExpr.GetTargetTy()}}; } return std::nullopt; } } // namespace void TypeCastCheck::RunOnFunc(const Func& func) { Visitor::Visit(func, [this, &func](Expression& expr) { auto typeCast = TryExtractTypeCast(expr); if (!typeCast) { return VisitResult::CONTINUE; } auto [sourceTy, targetTy] = *typeCast; if (!IsNativeFIIType(*sourceTy)) { return VisitResult::CONTINUE; } if (!IsNativeFIIType(*targetTy)) { auto [hasDebugPos, debugPos] = GetDebugPos(expr); if (!hasDebugPos) { debugPos = ToRange(func.GetDebugLocation()); } CJC_ASSERT(sourceTy->GetTypeKind()); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::chir_native_ffi_java_illegal_type_cast, debugPos, GetClassName(*sourceTy), GetClassName(*targetTy)); } return VisitResult::CONTINUE; }); } } // namespace Cangjie::CHIR::NativeFFI cangjie_compiler-1.0.7/src/CHIR/NativeFFI/TypeCastCheck.h000066400000000000000000000013631510705540100227440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_NATIVE_FFI_TYPE_CAST_CHECK_H #define CANGJIE_CHIR_NATIVE_FFI_TYPE_CAST_CHECK_H #include "cangjie/CHIR/DiagAdapter.h" #include "cangjie/CHIR/Package.h" namespace Cangjie::CHIR::NativeFFI { class TypeCastCheck final { public: explicit TypeCastCheck(DiagAdapter& diag); void RunOnPackage(const Package& package, size_t threadNum); private: void RunOnFunc(const Func& func); private: DiagAdapter& diag; }; } // namespace Cangjie::CHIR::NativeFFI #endif cangjie_compiler-1.0.7/src/CHIR/OverflowChecking.cpp000066400000000000000000000136411510705540100222730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the overflow checking APIs in CHIR. */ #include "cangjie/CHIR/OverflowChecking.h" #include "cangjie/CHIR/Value.h" using namespace Cangjie::CHIR; bool OverflowChecker::IsIntOverflow(const Type::TypeKind& typeKind, const ExprKind& exprKind, int64_t x, int64_t y, const OverflowStrategy& strategy, int64_t* res) { bool isOverflow = true; switch (typeKind) { case Type::TypeKind::TYPE_INT8: { int8_t tmpRes = 0; isOverflow = IsOverflow(static_cast(x), static_cast(y), exprKind, strategy, &tmpRes); *res = tmpRes; break; } case Type::TypeKind::TYPE_INT16: { int16_t tmpRes = 0; isOverflow = IsOverflow(static_cast(x), static_cast(y), exprKind, strategy, &tmpRes); *res = tmpRes; break; } case Type::TypeKind::TYPE_INT32: { int32_t tmpRes = 0; isOverflow = IsOverflow(static_cast(x), static_cast(y), exprKind, strategy, &tmpRes); *res = tmpRes; break; } case Type::TypeKind::TYPE_INT64: { int64_t tmpRes = 0; isOverflow = IsOverflow(static_cast(x), static_cast(y), exprKind, strategy, &tmpRes); *res = tmpRes; break; } case Type::TypeKind::TYPE_INT_NATIVE: { ssize_t tmpRes = 0; isOverflow = IsOverflow(static_cast(x), static_cast(y), exprKind, strategy, &tmpRes); *res = tmpRes; break; } default: CJC_ABORT(); } return isOverflow; } bool OverflowChecker::IsUIntOverflow(const Type::TypeKind& typeKind, const ExprKind& exprKind, uint64_t x, uint64_t y, const OverflowStrategy& strategy, uint64_t* res) { bool isOverflow = true; switch (typeKind) { case Type::TypeKind::TYPE_UINT8: { uint8_t tmpRes = 0; isOverflow = IsOverflow(static_cast(x), static_cast(y), exprKind, strategy, &tmpRes); *res = tmpRes; break; } case Type::TypeKind::TYPE_UINT16: { uint16_t tmpRes = 0; isOverflow = IsOverflow(static_cast(x), static_cast(y), exprKind, strategy, &tmpRes); *res = tmpRes; break; } case Type::TypeKind::TYPE_UINT32: { uint32_t tmpRes = 0; isOverflow = IsOverflow(static_cast(x), static_cast(y), exprKind, strategy, &tmpRes); *res = tmpRes; break; } case Type::TypeKind::TYPE_UINT64: { uint64_t tmpRes = 0; isOverflow = IsOverflow(static_cast(x), static_cast(y), exprKind, strategy, &tmpRes); *res = tmpRes; break; } case Type::TypeKind::TYPE_UINT_NATIVE: { size_t tmpRes = 0; isOverflow = IsOverflow(static_cast(x), static_cast(y), exprKind, strategy, &tmpRes); *res = tmpRes; break; } default: CJC_ABORT(); } return isOverflow; } template bool OverflowChecker::IsOverflow(T x, T y, ExprKind kind, const OverflowStrategy& strategy, T* res) { *res = 0; bool isOverflow = true; switch (kind) { case ExprKind::ADD: { isOverflow = IsOverflowAfterAdd(x, y, strategy, res); break; } case ExprKind::NEG: { static_assert(!std::is_same_v && !std::is_same_v); isOverflow = IsOverflowAfterSub(0, y, strategy, res); break; } case ExprKind::SUB: { isOverflow = IsOverflowAfterSub(x, y, strategy, res); break; } case ExprKind::MUL: { isOverflow = IsOverflowAfterMul(x, y, strategy, res); break; } case ExprKind::DIV: { isOverflow = IsOverflowAfterDiv(x, y, strategy, res); break; } case ExprKind::MOD: { isOverflow = IsOverflowAfterMod(x, y, res); break; } default: CJC_ABORT(); } return isOverflow; } namespace { bool Binpow(int64_t a, uint64_t b, int64_t* res) { *res = 1; if (b == 0) { return false; } bool isOverflow = false; while (b > 1) { if ((b & 1) == 1) { if (__builtin_mul_overflow(*res, a, res) && !isOverflow) { isOverflow = true; } } if (__builtin_mul_overflow(a, a, &a) && !isOverflow) { isOverflow = true; } b >>= 1; } if (__builtin_mul_overflow(*res, a, res) && !isOverflow) { isOverflow = true; } return isOverflow; } } // namespace bool OverflowChecker::IsExpOverflow(int64_t x, uint64_t y, OverflowStrategy strategy, int64_t* res) { bool isOverflow = Binpow(x, y, res); if (isOverflow && strategy == OverflowStrategy::SATURATING) { uint64_t magicNumber = 2; if (x < 0 && (y % magicNumber == 0)) { *res = std::numeric_limits::max(); } else if (x < 0 && (y % magicNumber == 1)) { *res = std::numeric_limits::min(); } else { *res = std::numeric_limits::max(); } } return isOverflow; } cangjie_compiler-1.0.7/src/CHIR/Package.cpp000066400000000000000000000206371510705540100203720ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/ToStringUtils.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/ExtendDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Value.h" using namespace Cangjie::CHIR; Package::Package(const std::string& name) : name(name) { } std::string Package::GetName() const { return name; } void Package::AddGlobalVar(GlobalVar* item) { globalVars.emplace_back(item); } void Package::AddGlobalFunc(Func* item) { globalFuncs.emplace_back(item); } void Package::AddClass(ClassDef* item) { classes.emplace_back(item); } std::vector Package::GetClasses() const { return classes; } void Package::SetClasses(std::vector&& items) { classes = std::move(items); } std::vector Package::GetEnums() const { return enums; } void Package::SetPackageInitFunc(Func* func) { packageInitFunc = func; } void Package::SetPackageLiteralInitFunc(Func* func) { packageLiteralInitFunc = func; } Func* Package::GetPackageLiteralInitFunc() const { return packageLiteralInitFunc; } void Package::SetImportedVarAndFuncs(std::vector&& items) { importedVarAndFuncs = std::move(items); } Func* Package::GetPackageInitFunc() const { return packageInitFunc; } void Package::SetImportedExtends(std::vector&& items) { importedExtends = std::move(items); } void Package::SetExtends(std::vector&& items) { extends = std::move(items); } void Package::AddImportedClass(ClassDef* item) { importedClasses.emplace_back(item); } void Package::AddImportedExtend(ExtendDef* item) { importedExtends.emplace_back(item); } std::vector Package::GetImportedEnums() const { return importedEnums; } void Package::SetPackageAccessLevel(const AccessLevel& level) { pkgAccessLevel = level; } void Package::SetImportedStructs(std::vector&& s) { importedStructs = std::move(s); } void Package::SetStructs(std::vector&& s) { structs = std::move(s); } void Package::SetImportedClasses(std::vector&& s) { importedClasses = std::move(s); } void Package::SetImportedEnums(std::vector&& s) { importedEnums = std::move(s); } void Package::SetEnums(std::vector&& s) { enums = std::move(s); } Package::AccessLevel Package::GetPackageAccessLevel() const { return pkgAccessLevel; } std::vector Package::GetImportedExtends() const { return importedExtends; } void Package::AddImportedEnum(EnumDef* item) { importedEnums.emplace_back(item); } std::vector Package::GetImportedClasses() const { return importedClasses; } std::vector Package::GetImportedStructs() const { return importedStructs; } void Package::AddImportedStruct(StructDef* item) { importedStructs.emplace_back(item); } std::vector Package::GetImportedVarAndFuncs() const { return importedVarAndFuncs; } std::vector Package::GetExtends() const { return extends; } void Package::AddExtend(ExtendDef* item) { extends.emplace_back(item); } void Package::AddEnum(EnumDef* item) { enums.emplace_back(item); } std::vector Package::GetStructs() const { return structs; } void Package::AddStruct(StructDef* item) { structs.emplace_back(item); } void Package::SetGlobalFuncs(const std::vector& funcs) { globalFuncs = funcs; } void Package::SetGlobalVars(std::vector&& vars) { std::swap(globalVars, vars); } std::vector Package::GetGlobalFuncs() const { return globalFuncs; } std::vector Package::GetGlobalVars() const { return globalVars; } std::string Package::ToString() const { std::stringstream ss; ss << "package: " << name << "\n"; ss << "packageAccessLevel: " << PackageAccessLevelToString(pkgAccessLevel) << "\n"; ss << "packageInitFunc: " << GetPackageInitFunc()->GetIdentifier() << "\n"; ss << "\n==========================imports===============================\n"; for (auto& it : importedVarAndFuncs) { ss << GetImportedValueStr(*it) << "\n"; } ss << "\n\n"; for (auto& it : importedStructs) { ss << it->ToString() << "\n\n"; } for (auto& it : importedClasses) { ss << it->ToString() << "\n\n"; } for (auto& it : importedEnums) { ss << it->ToString() << "\n\n"; } for (auto& it : importedExtends) { ss << it->ToString() << "\n\n"; } ss << "\n===========================vars=================================\n"; for (auto& it : globalVars) { ss << it->ToString() << "\n"; } ss << "\n==========================types=================================\n"; for (auto& it : structs) { ss << it->ToString() << "\n\n"; } for (auto& it : classes) { ss << it->ToString() << "\n\n"; } for (auto& it : enums) { ss << it->ToString() << "\n\n"; } for (auto& it : extends) { ss << it->ToString() << "\n\n"; } ss << "\n==========================funcs=================================\n"; for (auto& it : GetGlobalFuncs()) { ss << GetFuncStr(*it); ss << "\n\n"; } return ss.str(); } void Package::AddImportedVarAndFunc(ImportedValue* item) { importedVarAndFuncs.emplace_back(item); } void Package::Dump() const { std::cout << ToString() << std::endl; } std::vector Package::GetAllCustomTypeDef() const { std::vector all; all.insert(all.end(), importedStructs.begin(), importedStructs.end()); all.insert(all.end(), importedClasses.begin(), importedClasses.end()); all.insert(all.end(), importedEnums.begin(), importedEnums.end()); all.insert(all.end(), importedExtends.begin(), importedExtends.end()); all.insert(all.end(), structs.begin(), structs.end()); all.insert(all.end(), classes.begin(), classes.end()); all.insert(all.end(), enums.begin(), enums.end()); all.insert(all.end(), extends.begin(), extends.end()); return all; } std::vector Package::GetAllImportedCustomTypeDef() const { std::vector all; all.insert(all.end(), importedStructs.begin(), importedStructs.end()); all.insert(all.end(), importedClasses.begin(), importedClasses.end()); all.insert(all.end(), importedEnums.begin(), importedEnums.end()); all.insert(all.end(), importedExtends.begin(), importedExtends.end()); return all; } std::vector Package::GetCurPkgCustomTypeDef() const { std::vector all; all.insert(all.end(), structs.begin(), structs.end()); all.insert(all.end(), classes.begin(), classes.end()); all.insert(all.end(), enums.begin(), enums.end()); all.insert(all.end(), extends.begin(), extends.end()); return all; } std::vector Package::GetAllStructDef() const { std::vector all; all.insert(all.end(), structs.begin(), structs.end()); all.insert(all.end(), importedStructs.begin(), importedStructs.end()); return all; } std::vector Package::GetAllEnumDef() const { std::vector all; all.insert(all.end(), enums.begin(), enums.end()); all.insert(all.end(), importedEnums.begin(), importedEnums.end()); return all; } std::vector Package::GetAllClassDef() const { std::vector all; all.insert(all.end(), classes.begin(), classes.end()); all.insert(all.end(), importedClasses.begin(), importedClasses.end()); return all; } std::vector Package::GetAllExtendDef() const { std::vector all; all.insert(all.end(), extends.begin(), extends.end()); all.insert(all.end(), importedExtends.begin(), importedExtends.end()); return all; }cangjie_compiler-1.0.7/src/CHIR/Serializer/000077500000000000000000000000001510705540100204345ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CHIR/Serializer/CHIRDeserializer.cpp000066400000000000000000003557761510705540100242570ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/GeneratedFromForIn.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/CustomTypeDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/UserDefinedType.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/ICEUtil.h" #include "cangjie/Basic/Version.h" #include "CHIRDeserializerImpl.h" #include "cangjie/CHIR/Serializer/CHIRDeserializer.h" using namespace Cangjie::CHIR; // explicit specialization template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::EnumDef* buffer, EnumDef& obj); template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::StructDef* buffer, StructDef& obj); template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::ClassDef* buffer, ClassDef& obj); template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::ExtendDef* buffer, ExtendDef& obj); template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::ExtendDef* buffer, ExtendDef& obj); // =========================== Generic Deserializer ============================== bool CHIRDeserializer::Deserialize(const std::string& fileName, Cangjie::CHIR::CHIRBuilder& chirBuilder, Cangjie::CHIR::ToCHIR::Phase& phase, bool compilePlatform) { if (FileUtil::IsDir(fileName)) { Errorln(fileName, " is a directory."); return false; } if (!FileUtil::FileExist(fileName)) { Errorln(fileName, " not exist."); return false; } CHIRDeserializerImpl deserializer(chirBuilder, compilePlatform); std::vector serializationInfo; std::string failedReason; if (!FileUtil::ReadBinaryFileToBuffer(fileName, serializationInfo, failedReason)) { Errorln(failedReason, "."); return false; } flatbuffers::Verifier verifier(serializationInfo.data(), serializationInfo.size()); if (!verifier.VerifyBuffer()) { Errorln("validation of '", fileName, "' failed, please confirm it was created by compiler whose version is '", CANGJIE_VERSION, "'."); return false; } const PackageFormat::CHIRPackage* package = PackageFormat::GetCHIRPackage(serializationInfo.data()); deserializer.Run(package); phase = Cangjie::CHIR::ToCHIR::Phase(package->phase()); return true; } template std::vector CHIRDeserializer::CHIRDeserializerImpl::Create(const flatbuffers::Vector* vec) { std::vector retval; for (auto obj : *vec) { retval.emplace_back(Create(obj)); } return retval; } template std::vector CHIRDeserializer::CHIRDeserializerImpl::GetValue(const flatbuffers::Vector* vec) { if (vec == nullptr) { return {}; } std::vector retval; for (auto obj : *vec) { if (auto elem = GetValue(obj)) { retval.emplace_back(elem); } } return retval; } template std::vector CHIRDeserializer::CHIRDeserializerImpl::GetType(const flatbuffers::Vector* vec) { if (vec == nullptr) { return {}; } std::vector retval; for (auto obj : *vec) { if (auto elem = GetType(obj)) { retval.emplace_back(elem); } } return retval; } template std::vector CHIRDeserializer::CHIRDeserializerImpl::GetExpression(const flatbuffers::Vector* vec) { if (vec == nullptr) { return {}; } std::vector retval; for (auto obj : *vec) { if (auto elem = GetExpression(obj)) { retval.emplace_back(elem); } } return retval; } template std::vector CHIRDeserializer::CHIRDeserializerImpl::GetCustomTypeDef(const flatbuffers::Vector* vec) { if (vec == nullptr) { return {}; } std::vector retval; for (auto obj : *vec) { if (auto elem = GetCustomTypeDef(obj)) { retval.emplace_back(elem); } } return retval; } // =========================== Helper Deserializer ============================== namespace { AttributeInfo CreateAttr(const uint64_t attrs) { return AttributeInfo(attrs); } std::string GetMangleNameFromIdentifier(std::string& identifier) { CJC_ASSERT(!identifier.empty()); if (identifier.empty()) { return ""; } if (identifier[0] == '@') { return identifier.substr(1); } else { return identifier; } } } // namespace template <> Position CHIRDeserializer::CHIRDeserializerImpl::Create(const PackageFormat::Pos* obj) { return Position{static_cast(obj->line()), static_cast(obj->column())}; } template <> DebugLocation CHIRDeserializer::CHIRDeserializerImpl::Create(const PackageFormat::DebugLocation* obj) { auto filePath = obj->filePath()->str(); auto fileId = obj->fileId(); auto beginPos = Create(obj->beginPos()); auto endPos = Create(obj->endPos()); auto scope = std::vector(obj->scope()->begin(), obj->scope()->end()); if (builder.GetChirContext().GetSourceFileName(fileId) == INVALID_NAME) { builder.GetChirContext().RegisterSourceFileName(fileId, filePath); } // DebugLocation stores a string pointer, so here can't just pass filePath. return DebugLocation(builder.GetChirContext().GetSourceFileName(fileId), fileId, beginPos, endPos, scope); } template <> AnnoInfo CHIRDeserializer::CHIRDeserializerImpl::Create(const PackageFormat::AnnoInfo* obj) { auto mangledName = obj->mangledName()->str(); return AnnoInfo{mangledName}; } template <> Tuple* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Tuple* obj); template <> MemberVarInfo CHIRDeserializer::CHIRDeserializerImpl::Create(const PackageFormat::MemberVarInfo* obj) { auto name = obj->name()->str(); auto rawMangledName = obj->rawMangledName()->str(); auto type = GetType(obj->type()); auto attributeInfo = CreateAttr(obj->attributes()); if (compilePlatform) { attributeInfo.SetAttr(Attribute::DESERIALIZED, true); } auto loc = Create(obj->loc()); auto annoInfo = Create(obj->annoInfo()); auto initializerFunc = GetValue(obj->initializerFunc()); auto outerDef = GetCustomTypeDef(obj->outerDef()); return MemberVarInfo{name, rawMangledName, type, attributeInfo, loc, annoInfo, initializerFunc, outerDef}; } template <> EnumCtorInfo CHIRDeserializer::CHIRDeserializerImpl::Create(const PackageFormat::EnumCtorInfo* obj) { auto name = obj->identifier()->str(); auto mangledName = obj->mangledName()->str(); auto funcType = GetType(obj->funcType()); return EnumCtorInfo{name, mangledName, funcType}; } template <> AbstractMethodParam CHIRDeserializer::CHIRDeserializerImpl::Create(const PackageFormat::AbstractMethodParam* obj) { auto paramName = obj->paramName()->str(); auto paramType = GetType(obj->paramType()); auto annoInfo = Create(obj->annoInfo()); return AbstractMethodParam{paramName, paramType, annoInfo}; } template <> AbstractMethodInfo CHIRDeserializer::CHIRDeserializerImpl::Create(const PackageFormat::AbstractMethodInfo* obj) { auto name = obj->methodName()->str(); auto mangledName = obj->mangledName()->str(); auto methodTy = GetType(obj->methodType()); auto paramInfos = Create(obj->paramsInfo()); auto attributeInfo = CreateAttr(obj->attributes()); auto annoInfo = Create(obj->annoInfo()); auto methodGenericTypeParams = GetType(obj->methodGenericTypeParams()); auto hasBody = obj->hasBody(); auto parent = GetCustomTypeDef(obj->parent()); return AbstractMethodInfo{ name, mangledName, methodTy, paramInfos, attributeInfo, annoInfo, methodGenericTypeParams, hasBody, parent}; } template <> VirtualFuncTypeInfo CHIRDeserializer::CHIRDeserializerImpl::Create(const PackageFormat::VirtualFuncTypeInfo* obj) { auto sigType = GetType(obj->sigType()); auto originalType = GetType(obj->originalType()); auto parentType = GetType(obj->parentType()); auto returnType = GetType(obj->returnType()); auto methodGenericTypeParams = GetType(obj->methodGenericTypeParams()); return VirtualFuncTypeInfo{sigType, originalType, parentType, returnType, methodGenericTypeParams}; } template <> VirtualFuncInfo CHIRDeserializer::CHIRDeserializerImpl::Create(const PackageFormat::VirtualFuncInfo* obj) { auto srcCodeIdentifier = obj->srcCodeIdentifier()->str(); auto instance = GetValue(obj->instance()); auto attributeInfo = CreateAttr(obj->attributes()); auto typeInfo = Create(obj->typeInfo()); return VirtualFuncInfo{srcCodeIdentifier, instance, attributeInfo, typeInfo}; } template <> VTableType CHIRDeserializer::CHIRDeserializerImpl::Create( const flatbuffers::Vector>* obj) { VTableType vtable = {}; for (auto item : *obj) { auto ty = GetType(item->ty()); std::vector info = Create(item->info()); vtable[ty] = info; } return vtable; } // =========================== Custom Type Define Deserializer ============================== template <> EnumDef* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::EnumDef* obj) { auto srcCodeIdentifier = obj->base()->srcCodeIdentifier()->str(); auto identifier = obj->base()->identifier()->str(); auto packageName = obj->base()->packageName()->str(); auto attrs = CreateAttr(obj->base()->attributes()); if (compilePlatform) { attrs.SetAttr(CHIR::Attribute::DESERIALIZED, true); } auto imported = attrs.TestAttr(CHIR::Attribute::IMPORTED); auto result = builder.CreateEnum(DebugLocation(), srcCodeIdentifier, GetMangleNameFromIdentifier(identifier), packageName, imported, obj->nonExhaustive()); result->AppendAttributeInfo(attrs); return result; } template <> StructDef* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::StructDef* obj) { auto srcCodeIdentifier = obj->base()->srcCodeIdentifier()->str(); auto identifier = obj->base()->identifier()->str(); auto packageName = obj->base()->packageName()->str(); auto attrs = CreateAttr(obj->base()->attributes()); if (compilePlatform) { attrs.SetAttr(CHIR::Attribute::DESERIALIZED, true); } auto imported = attrs.TestAttr(CHIR::Attribute::IMPORTED); auto result = builder.CreateStruct( DebugLocation(), srcCodeIdentifier, GetMangleNameFromIdentifier(identifier), packageName, imported); result->AppendAttributeInfo(attrs); return result; } template <> ClassDef* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::ClassDef* obj) { auto srcCodeIdentifier = obj->base()->srcCodeIdentifier()->str(); auto identifier = obj->base()->identifier()->str(); auto packageName = obj->base()->packageName()->str(); auto isClass = obj->kind() == PackageFormat::ClassDefKind::ClassDefKind_CLASS; auto attrs = CreateAttr(obj->base()->attributes()); if (compilePlatform) { attrs.SetAttr(CHIR::Attribute::DESERIALIZED, true); } auto imported = attrs.TestAttr(CHIR::Attribute::IMPORTED); auto result = builder.CreateClass( DebugLocation(), srcCodeIdentifier, GetMangleNameFromIdentifier(identifier), packageName, isClass, imported); result->AppendAttributeInfo(attrs); return result; } template <> ExtendDef* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::ExtendDef* obj) { auto srcCodeIndentifier = obj->base()->srcCodeIdentifier()->str(); auto identifier = obj->base()->identifier()->str(); auto packageName = obj->base()->packageName()->str(); auto attrs = CreateAttr(obj->base()->attributes()); auto imported = attrs.TestAttr(CHIR::Attribute::IMPORTED); if (compilePlatform) { attrs.SetAttr(CHIR::Attribute::DESERIALIZED, true); } auto genericParams = GetType(obj->genericParams()); auto result = builder.CreateExtend( DebugLocation(), GetMangleNameFromIdentifier(identifier), packageName, imported, genericParams); result->AppendAttributeInfo(attrs); return result; } // =========================== Type Deserializer ============================== template <> RuneType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize([[maybe_unused]] const PackageFormat::RuneType* obj) { return builder.GetType(); } template <> BooleanType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize([[maybe_unused]] const PackageFormat::BooleanType* obj) { return builder.GetType(); } template <> UnitType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize([[maybe_unused]] const PackageFormat::UnitType* obj) { return builder.GetType(); } template <> NothingType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize([[maybe_unused]] const PackageFormat::NothingType* obj) { return builder.GetType(); } template <> TupleType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::TupleType* obj) { CJC_NULLPTR_CHECK(obj->base()->argTys()); auto argTys = GetType(obj->base()->argTys()); return builder.GetType(argTys); } template <> RawArrayType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::RawArrayType* obj) { CJC_NULLPTR_CHECK(obj->base()->argTys()); auto elemTy = GetType(obj->base()->argTys()->Get(0)); auto dims = obj->dims(); return builder.GetType(elemTy, static_cast(dims)); } template <> VArrayType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::VArrayType* obj) { CJC_NULLPTR_CHECK(obj->base()->argTys()); auto elemTy = GetType(obj->base()->argTys()->Get(0)); auto size = obj->size(); return builder.GetType(elemTy, size); } template <> FuncType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::FuncType* obj) { CJC_NULLPTR_CHECK(obj->base()->argTys()); auto argTys = obj->base()->argTys(); auto retTy = GetType(argTys->Get(argTys->size() - 1)); auto hasVarLenParam = obj->hasVarArg(); auto isCFuncType = obj->isCFuncType(); std::vector paramTys; CJC_ASSERT(argTys->size() > 0); for (size_t i = 0; i < argTys->size() - 1; ++i) { paramTys.emplace_back(GetType(argTys->Get(static_cast(i)))); } return builder.GetType(paramTys, retTy, hasVarLenParam, isCFuncType); } template <> CustomType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::CustomType* obj) { auto kind = Type::TypeKind(obj->base()->kind()); auto def = GetCustomTypeDef(obj->customTypeDef()); auto typeArgs = GetType(obj->base()->argTys()); return builder.GetType(kind, def, typeArgs); } template <> CStringType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize([[maybe_unused]] const PackageFormat::CStringType* obj) { return builder.GetType(); } template <> CPointerType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::CPointerType* obj) { CJC_NULLPTR_CHECK(obj->base()->argTys()); auto elemTy = GetType(obj->base()->argTys()->Get(0)); return builder.GetType(elemTy); } template <> GenericType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::GenericType* obj) { auto identifier = obj->identifier()->str(); auto srcCodeIndentifier = obj->srcCodeIdentifier()->str(); auto genericType = builder.GetType(identifier, srcCodeIndentifier); genericType->orphanFlag = obj->orphanFlag(); genericType->skipCheck = obj->skipCheck(); genericTypeConfig.emplace_back(genericType, obj); return genericType; } template <> RefType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::RefType* obj) { CJC_NULLPTR_CHECK(obj->base()->argTys()); auto baseType = GetType(obj->base()->argTys()->Get(0)); return builder.GetType(baseType); } template <> BoxType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::BoxType* obj) { CJC_NULLPTR_CHECK(obj->base()->argTys()); auto baseType = GetType(obj->base()->argTys()->Get(0)); return builder.GetType(baseType); } template <> ThisType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize([[maybe_unused]] const PackageFormat::ThisType* obj) { return builder.GetType(); } template <> VoidType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize([[maybe_unused]] const PackageFormat::VoidType* obj) { return builder.GetType(); } // =========================== Custom Type Deserializer ============================== template <> EnumType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::EnumType* obj) { auto def = GetCustomTypeDef(obj->base()->customTypeDef()); auto argTys = GetType(obj->base()->base()->argTys()); return builder.GetType(def, argTys); } template <> StructType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::StructType* obj) { auto def = GetCustomTypeDef(obj->base()->customTypeDef()); auto argTys = GetType(obj->base()->base()->argTys()); return builder.GetType(def, argTys); } template <> ClassType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::ClassType* obj) { [[maybe_unused]] auto tyId = obj->base()->base()->typeID(); auto def = GetCustomTypeDef(obj->base()->customTypeDef()); auto argTys = GetType(obj->base()->base()->argTys()); return builder.GetType(def, argTys); } // =========================== Numeric Type Deserializer ============================== template <> IntType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::IntType* obj) { auto kind = Type::TypeKind(obj->base()->base()->kind()); return builder.GetType(kind); } template <> FloatType* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::FloatType* obj) { auto kind = Type::TypeKind(obj->base()->base()->kind()); return builder.GetType(kind); } // =========================== Value Deserializer ============================== template <> BoolLiteral* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::BoolLiteral* obj) { auto type = GetType(obj->base()->base()->type()); auto val = obj->val(); return builder.CreateLiteralValue(type, val); } template <> RuneLiteral* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::RuneLiteral* obj) { auto type = GetType(obj->base()->base()->type()); auto val = obj->val(); return builder.CreateLiteralValue(type, static_cast(val)); } template <> StringLiteral* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::StringLiteral* obj) { auto type = GetType(obj->base()->base()->type()); auto val = obj->val()->str(); return builder.CreateLiteralValue(type, val); } template <> IntLiteral* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::IntLiteral* obj) { auto type = GetType(obj->base()->base()->type()); auto val = obj->val(); return builder.CreateLiteralValue(type, val); } template <> FloatLiteral* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::FloatLiteral* obj) { auto type = GetType(obj->base()->base()->type()); auto val = obj->val(); return builder.CreateLiteralValue(type, val); } template <> UnitLiteral* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::UnitLiteral* obj) { auto type = GetType(obj->base()->base()->type()); return builder.CreateLiteralValue(type); } template <> NullLiteral* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::NullLiteral* obj) { auto type = GetType(obj->base()->base()->type()); return builder.CreateLiteralValue(type); } template <> Func* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Func* obj) { auto funcTy = GetType(obj->base()->type()); auto identifier = obj->base()->identifier()->str(); auto srcCodeIdentifier = obj->srcCodeIdentifier()->str(); auto rawMangledName = obj->rawMangledName()->str(); auto packageName = obj->packageName()->str(); auto genericTypeParams = GetType(obj->genericTypeParams()); auto result = builder.CreateFunc(DebugLocation(), funcTy, GetMangleNameFromIdentifier(identifier), srcCodeIdentifier, rawMangledName, packageName, genericTypeParams); // Indexes below are needed to modify correctly function body when merging CJMP initializers result->SetLocalId(obj->localId()); result->SetBlockId(obj->localId()); result->SetBlockGroupId(obj->localId()); return result; } template <> BlockGroup* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::BlockGroup* obj) { BlockGroup* blockGroup = nullptr; if (obj->ownedFunc() != 0) { if (auto ownedFunc = GetValue(obj->ownedFunc())) { blockGroup = builder.CreateBlockGroup(*ownedFunc); blockGroup->SetOwnerFunc(ownedFunc); } else { CJC_ABORT(); } } else if (obj->ownedExpression() != 0) { auto exprType = PackageFormat::ExpressionElem(pool->exprs_type()->Get(obj->ownedExpression() - 1)); switch (exprType) { case PackageFormat::ExpressionElem_Lambda: { auto ownedLambda = GetExpression(obj->ownedExpression()); CJC_NULLPTR_CHECK(ownedLambda); if (ownedLambda->GetBody() != nullptr) { return ownedLambda->GetBody(); } CJC_NULLPTR_CHECK(ownedLambda->GetTopLevelFunc()); blockGroup = builder.CreateBlockGroup(*ownedLambda->GetTopLevelFunc()); ownedLambda->InitBody(*blockGroup); break; } case PackageFormat::ExpressionElem_ForInRange: { auto ownedForInRange = GetExpression(obj->ownedExpression()); CJC_NULLPTR_CHECK(ownedForInRange); blockGroup = builder.CreateBlockGroup(*ownedForInRange->GetTopLevelFunc()); break; } case PackageFormat::ExpressionElem_ForInClosedRange: { auto ownedForInClosedRange = GetExpression(obj->ownedExpression()); CJC_NULLPTR_CHECK(ownedForInClosedRange); blockGroup = builder.CreateBlockGroup(*ownedForInClosedRange->GetTopLevelFunc()); break; } case PackageFormat::ExpressionElem_ForInIter: { auto ownedForInIter = GetExpression(obj->ownedExpression()); CJC_NULLPTR_CHECK(ownedForInIter); blockGroup = builder.CreateBlockGroup(*ownedForInIter->GetTopLevelFunc()); break; } default: CJC_ABORT(); } } return blockGroup; } template <> Block* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Block* obj) { auto parentGroup = GetValue(obj->parentGroup()); return builder.CreateBlock(parentGroup); } template <> Parameter* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Parameter* obj) { CJC_ASSERT(obj->base()->kind() == PackageFormat::ValueKind_PARAMETER); auto type = GetType(obj->base()->type()); Parameter* result = nullptr; if (auto ownedFunc = GetValue(obj->ownedFunc())) { result = builder.CreateParameter(type, INVALID_LOCATION, *ownedFunc); } else if (auto ownedLambda = GetExpression(obj->ownedLambda())) { result = builder.CreateParameter(type, INVALID_LOCATION, *ownedLambda); } else { CJC_ABORT(); } result->SetAnnoInfo(Create(obj->base()->annoInfo())); StaticCast(result)->identifier = obj->base()->identifier()->str(); return result; } template <> LocalVar* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::LocalVar* obj) { auto associatedExpr = GetExpression(obj->associatedExpr()); CJC_NULLPTR_CHECK(associatedExpr); auto result = associatedExpr->GetResult(); return result; } template <> GlobalVar* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::GlobalVar* obj) { auto type = GetType(obj->base()->type()); auto identifier = obj->base()->identifier()->str(); auto srcCodeIdentifier = obj->srcCodeIdentifier()->str(); auto packageName = obj->packageName()->str(); auto rawMangledName = obj->rawMangledName()->str(); auto attrs = CreateAttr(obj->base()->attributes()); if (compilePlatform) { attrs.SetAttr(Attribute::DESERIALIZED, true); } auto result = builder.CreateGlobalVar( DebugLocation(), type, GetMangleNameFromIdentifier(identifier), srcCodeIdentifier, rawMangledName, packageName); result->AppendAttributeInfo(attrs); return result; } template <> ImportedFunc* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::ImportedFunc* obj) { auto type = GetType(obj->base()->base()->type()); auto identifier = obj->base()->base()->identifier()->str(); auto srcCodeIdentifier = obj->srcCodeIdentifier()->str(); auto rawMangledName = obj->rawMangledName()->str(); auto packageName = obj->packageName()->str(); auto genericTypeParams = GetType(obj->genericTypeParams()); auto attrs = CreateAttr(obj->base()->base()->attributes()); if (compilePlatform) { attrs.SetAttr(Attribute::DESERIALIZED, true); } auto importedFunc = builder.CreateImportedVarOrFunc(type, GetMangleNameFromIdentifier(identifier), srcCodeIdentifier, rawMangledName, packageName, genericTypeParams); // Object configuration importedFunc->AppendAttributeInfo(attrs); importedFunc->SetFuncKind(static_cast(obj->funcKind())); importedFunc->SetRawMangledName(obj->rawMangledName()->str()); importedFunc->SetParamInfo(Create(obj->paramInfo())); importedFunc->SetAnnoInfo(Create(obj->base()->base()->annoInfo())); importedFunc->SetFastNative(obj->isFastNative()); importedFunc->SetCFFIWrapper(obj->isCFFIWrapper()); return importedFunc; } template <> ImportedVar* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::ImportedVar* obj) { auto type = GetType(obj->base()->base()->type()); auto identifier = obj->base()->base()->identifier()->str(); auto srcCodeIdentifier = obj->srcCodeIdentifier()->str(); auto rawMangledName = obj->rawMangledName()->str(); auto packageName = obj->packageName()->str(); auto attrs = CreateAttr(obj->base()->base()->attributes()); if (compilePlatform) { attrs.SetAttr(Attribute::DESERIALIZED, true); } auto importedVar = builder.CreateImportedVarOrFunc( type, GetMangleNameFromIdentifier(identifier), srcCodeIdentifier, rawMangledName, packageName); // Object configuration importedVar->AppendAttributeInfo(attrs); importedVar->SetAnnoInfo(Create(obj->base()->base()->annoInfo())); return importedVar; } // =========================== Expression Deserializer ============================== template <> UnaryExpression* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::UnaryExpression* obj) { auto kind = ExprKind(obj->base()->kind()); auto operand = GetValue(obj->base()->operands()->Get(0)); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); auto ofs = OverflowStrategy(obj->overflowStrategy()); return builder.CreateExpression(resultTy, kind, operand, ofs, parentBlock); } template <> BinaryExpression* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::BinaryExpression* obj) { auto kind = ExprKind(obj->base()->kind()); auto lhs = GetValue(obj->base()->operands()->Get(0)); auto rhs = GetValue(obj->base()->operands()->Get(1)); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); auto ofs = OverflowStrategy(obj->overflowStrategy()); return builder.CreateExpression(resultTy, kind, lhs, rhs, ofs, parentBlock); } template <> Constant* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Constant* obj) { auto val = static_cast(GetValue(obj->base()->operands()->Get(0))); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); if (val->IsBoolLiteral()) { auto litVal = StaticCast(val); return builder.CreateConstantExpression(resultTy, parentBlock, litVal->GetVal()); } else if (val->IsFloatLiteral()) { auto litVal = StaticCast(val); return builder.CreateConstantExpression(resultTy, parentBlock, litVal->GetVal()); } else if (val->IsIntLiteral()) { auto litVal = StaticCast(val); return builder.CreateConstantExpression(resultTy, parentBlock, litVal->GetUnsignedVal()); } else if (val->IsNullLiteral()) { return builder.CreateConstantExpression(resultTy, parentBlock); } else if (val->IsRuneLiteral()) { auto litVal = StaticCast(val); return builder.CreateConstantExpression(resultTy, parentBlock, litVal->GetVal()); } else if (val->IsStringLiteral()) { auto litVal = StaticCast(val); return builder.CreateConstantExpression( resultTy, parentBlock, litVal->GetVal()); } else if (val->IsUnitLiteral()) { return builder.CreateConstantExpression(resultTy, parentBlock); } else { CJC_ABORT(); } return nullptr; } template <> Allocate* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Allocate* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); auto targetType = GetType(obj->targetType()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, targetType, parentBlock); } template <> Cangjie::CHIR::Load* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Load* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); auto location = GetValue(obj->base()->operands()->Get(0)); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, location, parentBlock); } template <> Store* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Store* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); auto value = GetValue(obj->base()->operands()->Get(0)); auto location = GetValue(obj->base()->operands()->Get(1)); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, value, location, parentBlock); } template <> GetElementRef* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::GetElementRef* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); auto location = GetValue(obj->base()->operands()->Get(0)); CJC_NULLPTR_CHECK(obj->path()); auto path = std::vector(obj->path()->begin(), obj->path()->end()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, location, path, parentBlock); } template <> GetElementByName* CHIRDeserializer::CHIRDeserializerImpl::Deserialize( const PackageFormat::GetElementByName* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); auto location = GetValue(obj->base()->operands()->Get(0)); std::vector names; names.reserve(obj->names()->size()); for (const auto& name : *obj->names()) { names.emplace_back(name->str()); } auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, location, names, parentBlock); } template <> StoreElementRef* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::StoreElementRef* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); auto value = GetValue(obj->base()->operands()->Get(0)); auto location = GetValue(obj->base()->operands()->Get(1)); CJC_NULLPTR_CHECK(obj->path()); auto path = std::vector(obj->path()->begin(), obj->path()->end()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, value, location, path, parentBlock); } template <> StoreElementByName* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::StoreElementByName* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); auto value = GetValue(obj->base()->operands()->Get(0)); auto location = GetValue(obj->base()->operands()->Get(1)); std::vector names; names.reserve(obj->names()->size()); for (const auto& name : *obj->names()) { names.emplace_back(name->str()); } auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, value, location, names, parentBlock); } template <> Apply* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Apply* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); CJC_NULLPTR_CHECK(obj->base()->operands()); auto operands = GetValue(obj->base()->operands()); auto callee = operands[0]; auto args = std::vector(operands.begin() + 1, operands.end()); auto resultTy = GetType(obj->base()->resultTy()); auto instantiatedTypeArgs = GetType(obj->instantiatedTypeArgs()); auto thisType = GetType(obj->thisType()); std::vector genericInfo; std::transform(instantiatedTypeArgs.begin(), instantiatedTypeArgs.end(), std::back_inserter(genericInfo), [](Type* type) { return Ptr(type); }); auto result = builder.CreateExpression(resultTy, callee, FuncCallContext{ .args = args, .instTypeArgs = genericInfo, .thisType = thisType}, parentBlock); if (obj->isSuperCall()) { result->SetSuperCall(); } return result; } template <> Invoke* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Invoke* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); CJC_NULLPTR_CHECK(obj->base()->operands()); auto operands = GetValue(obj->base()->operands()); auto resultTy = GetType(obj->base()->resultTy()); auto tempTypes = GetType(obj->virMethodCtx()->genericTypeParams()); std::vector genericTypes; for (auto ty : tempTypes) { genericTypes.emplace_back(Cangjie::StaticCast(ty)); } auto invokeInfo = InvokeCallContext { .caller = operands[0], .funcCallCtx = FuncCallContext { .args = std::vector(operands.begin() + 1, operands.end()), .instTypeArgs = GetType(obj->instantiatedTypeArgs()), .thisType = GetType(obj->thisType()) }, .virMethodCtx = VirMethodContext { .srcCodeIdentifier = obj->virMethodCtx()->srcCodeIdentifier()->data(), .originalFuncType = GetType(obj->virMethodCtx()->originalFuncType()), .genericTypeParams = std::move(genericTypes) } }; return builder.CreateExpression(resultTy, invokeInfo, parentBlock); } template <> InvokeStatic* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::InvokeStatic* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); auto argsSize = obj->base()->operands()->size(); std::vector args; for (unsigned i{1}; i < argsSize; ++i) { args.push_back(GetValue(obj->base()->operands()->Get(i))); } auto resultTy = GetType(obj->base()->resultTy()); auto tempTypes = GetType(obj->virMethodCtx()->genericTypeParams()); std::vector genericTypes; for (auto ty : tempTypes) { genericTypes.emplace_back(Cangjie::StaticCast(ty)); } auto invokeInfo = InvokeCallContext { .caller = GetValue(obj->base()->operands()->Get(0)), .funcCallCtx = FuncCallContext { .args = args, .instTypeArgs = GetType(obj->instantiatedTypeArgs()), .thisType = GetType(obj->thisType()) }, .virMethodCtx = VirMethodContext { .srcCodeIdentifier = obj->virMethodCtx()->srcCodeIdentifier()->data(), .originalFuncType = GetType(obj->virMethodCtx()->originalFuncType()), .genericTypeParams = std::move(genericTypes) } }; return builder.CreateExpression(resultTy, invokeInfo, parentBlock); } template <> InvokeStaticWithException* CHIRDeserializer::CHIRDeserializerImpl::Deserialize( const PackageFormat::InvokeStaticWithException* obj) { auto parentBlock = GetValue(obj->base()->base()->parentBlock()); auto argsSize = obj->base()->base()->operands()->size(); std::vector args; for (unsigned i{1}; i < argsSize; ++i) { args.push_back(GetValue(obj->base()->base()->operands()->Get(i))); } auto resultTy = GetType(obj->base()->base()->resultTy()); auto sucBlock = GetValue(obj->base()->successors()->Get(0)); auto errBlock = GetValue(obj->base()->successors()->Get(1)); auto tempTypes = GetType(obj->virMethodCtx()->genericTypeParams()); std::vector genericTypes; for (auto ty : tempTypes) { genericTypes.emplace_back(Cangjie::StaticCast(ty)); } auto invokeInfo = InvokeCallContext { .caller = GetValue(obj->base()->base()->operands()->Get(0)), .funcCallCtx = FuncCallContext { .args = args, .instTypeArgs = GetType(obj->instantiatedTypeArgs()), .thisType = GetType(obj->thisType()) }, .virMethodCtx = VirMethodContext { .srcCodeIdentifier = obj->virMethodCtx()->srcCodeIdentifier()->data(), .originalFuncType = GetType(obj->virMethodCtx()->originalFuncType()), .genericTypeParams = std::move(genericTypes) } }; return builder.CreateExpression(resultTy, invokeInfo, sucBlock, errBlock, parentBlock); } template <> GetInstantiateValue* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::GetInstantiateValue* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); auto val = GetValue(obj->base()->operands()->Get(0)); auto insTypes = GetType(obj->instantiateTys()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, val, insTypes, parentBlock); } template <> TransformToConcrete* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::TransformToConcrete* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); auto operand = GetValue(obj->base()->operands()->Get(0)); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, operand, parentBlock); } template <> TransformToGeneric* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::TransformToGeneric* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); auto operand = GetValue(obj->base()->operands()->Get(0)); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, operand, parentBlock); } template <> UnBoxToRef* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::UnBoxToRef* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); auto operand = GetValue(obj->base()->operands()->Get(0)); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, operand, parentBlock); } template <> TypeCast* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::TypeCast* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); auto operand = GetValue(obj->base()->operands()->Get(0)); auto resultTy = GetType(obj->base()->resultTy()); auto ofs = OverflowStrategy(obj->overflowStrategy()); return builder.CreateExpression(resultTy, operand, ofs, parentBlock); } template <> InstanceOf* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::InstanceOf* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); auto operand = GetValue(obj->base()->operands()->Get(0)); auto targetType = GetType(obj->targetType()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, operand, targetType, parentBlock); } template <> Box* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Box* expr) { auto parentBlock = GetValue(expr->base()->parentBlock()); auto operand = GetValue(expr->base()->operands()->Get(0)); auto resultTy = GetType(expr->base()->resultTy()); return builder.CreateExpression(resultTy, operand, parentBlock); } template <> UnBox* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::UnBox* expr) { auto parentBlock = GetValue(expr->base()->parentBlock()); auto operand = GetValue(expr->base()->operands()->Get(0)); auto resultTy = GetType(expr->base()->resultTy()); return builder.CreateExpression(resultTy, operand, parentBlock); } template <> Tuple* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Tuple* obj) { auto operands = GetValue(obj->base()->operands()); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, operands, parentBlock); } template <> Field* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Field* obj) { auto val = GetValue(obj->base()->operands()->Get(0)); CJC_NULLPTR_CHECK(obj->path()); auto indexes = std::vector(obj->path()->begin(), obj->path()->end()); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, val, indexes, parentBlock); } template <> FieldByName* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::FieldByName* obj) { auto val = GetValue(obj->base()->operands()->Get(0)); std::vector names; names.reserve(obj->names()->size()); for (const auto& name : *obj->names()) { names.emplace_back(name->str()); } auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, val, names, parentBlock); } template <> RawArrayAllocate* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::RawArrayAllocate* obj) { auto size = GetValue(obj->base()->operands()->Get(0)); auto elementType = GetType(obj->elementType()); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, elementType, size, parentBlock); } template <> RawArrayLiteralInit* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::RawArrayLiteralInit* obj) { auto operands = GetValue(obj->base()->operands()); auto raw = Ptr(operands[0]); auto elements = std::vector(operands.begin() + 1, operands.end()); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, raw, elements, parentBlock); } template <> RawArrayInitByValue* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::RawArrayInitByValue* obj) { auto raw = GetValue(obj->base()->operands()->Get(0)); auto size = GetValue(obj->base()->operands()->Get(1)); auto initVal = GetValue(obj->base()->operands()->Get(2)); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, raw, size, initVal, parentBlock); } template <> VArray* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::VArray* obj) { auto operands = GetValue(obj->base()->operands()); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, operands, parentBlock); } template <> VArrayBuilder* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::VArrayBd* obj) { auto size = GetValue(obj->base()->operands()->Get(0)); auto item = GetValue(obj->base()->operands()->Get(1)); auto initFunc = GetValue(obj->base()->operands()->Get(2)); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, size, item, initFunc, parentBlock); } template <> GetException* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::GetException* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, parentBlock); } template <> Intrinsic* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Intrinsic* obj) { auto kind = CHIR::IntrinsicKind(obj->intrinsicKind()); auto args = GetValue(obj->base()->operands()); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); auto callContext = IntrisicCallContext { .kind = kind, .args = args, .instTypeArgs = GetType(obj->instantiatedTypeArgs()) }; return builder.CreateExpression(resultTy, callContext, parentBlock); } template <> If* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::If* obj) { auto cond = GetValue(obj->base()->operands()->Get(0)); auto thenBody = GetValue(obj->base()->blockGroups()->Get(0)); auto elseBody = GetValue(obj->base()->blockGroups()->Get(1)); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, cond, thenBody, elseBody, parentBlock); } template <> Loop* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Loop* obj) { auto loopBody = GetValue(obj->base()->blockGroups()->Get(0)); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, loopBody, parentBlock); } template <> ForInRange* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::ForInRange* obj) { auto inductionVar = GetValue(obj->base()->operands()->Get(0)); auto loopCondVar = GetValue(obj->base()->operands()->Get(1)); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, inductionVar, loopCondVar, parentBlock); } template <> ForInClosedRange* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::ForInClosedRange* obj) { auto inductionVar = GetValue(obj->base()->operands()->Get(0)); auto loopCondVar = GetValue(obj->base()->operands()->Get(1)); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression( resultTy, inductionVar, loopCondVar, parentBlock); } template <> ForInIter* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::ForInIter* obj) { auto inductionVar = GetValue(obj->base()->operands()->Get(0)); auto loopCondVar = GetValue(obj->base()->operands()->Get(1)); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); return builder.CreateExpression(resultTy, inductionVar, loopCondVar, parentBlock); } template <> Debug* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Debug* obj) { CJC_ASSERT(obj->base()->operands()->size() == 1); auto local = GetValue(obj->base()->operands()->Get(0)); auto srcCodeIdentifier = obj->srcCodeIdentifier()->str(); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); auto result = builder.CreateExpression(resultTy, local, srcCodeIdentifier, parentBlock); return result; } template <> Spawn* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Spawn* obj) { auto operands = GetValue(obj->base()->operands()); auto val = operands[0]; auto func = GetValue(obj->executeClosure()); auto parentBlock = GetValue(obj->base()->parentBlock()); auto resultTy = GetType(obj->base()->resultTy()); Spawn* spawn = nullptr; if (operands.size() > 1) { auto arg = operands[1]; spawn = builder.CreateExpression(resultTy, val, arg, parentBlock); } else { spawn = builder.CreateExpression(resultTy, val, parentBlock); } if (func) { spawn->SetExecuteClosure(*func); } return spawn; } template <> Lambda* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Lambda* obj) { auto parentBlock = GetValue(obj->base()->parentBlock()); auto funcTy = GetType(obj->funcTy()); auto isLocalFunc = obj->isLocalFunc(); auto identifier = obj->identifier()->str(); auto srcCodeIdentifier = obj->srcCodeIdentifier()->str(); auto genericTypeParams = GetType(obj->genericTypeParams()); auto resultTy = GetType(obj->base()->resultTy()); auto lambda = builder.CreateExpression( resultTy, funcTy, parentBlock, isLocalFunc, identifier, srcCodeIdentifier, genericTypeParams); if (obj->isCompileTimeValue()) { lambda->SetCompileTimeValue(); } return lambda; } // =========================== Terminator Deserializer ============================== template <> GoTo* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::GoTo* obj) { auto parentBlock = GetValue(obj->base()->base()->parentBlock()); auto destination = GetValue(obj->base()->successors()->Get(0)); return builder.CreateTerminator(destination, parentBlock); } template <> Branch* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Branch* obj) { auto parentBlock = GetValue(obj->base()->base()->parentBlock()); auto cond = GetValue(obj->base()->base()->operands()->Get(0)); auto trueBlock = GetValue(obj->base()->successors()->Get(0)); auto falseBlock = GetValue(obj->base()->successors()->Get(1)); auto sourceExpr = CHIR::SourceExpr(obj->sourceExpr()); auto result = builder.CreateTerminator(cond, trueBlock, falseBlock, parentBlock); result->SetSourceExpr(sourceExpr); return result; } template <> MultiBranch* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::MultiBranch* obj) { auto cond = GetValue(obj->base()->base()->operands()->Get(0)); auto successors = GetValue(obj->base()->successors()); auto defaultBlock = successors[0]; auto succs = std::vector(successors.begin() + 1, successors.end()); CJC_NULLPTR_CHECK(obj->caseVals()); auto caseVals = std::vector(obj->caseVals()->begin(), obj->caseVals()->end()); auto parentBlock = GetValue(obj->base()->base()->parentBlock()); return builder.CreateTerminator(cond, defaultBlock, caseVals, succs, parentBlock); } template <> Exit* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::Exit* obj) { auto parentBlock = GetValue(obj->base()->base()->parentBlock()); return builder.CreateTerminator(parentBlock); } template <> RaiseException* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::RaiseException* obj) { auto value = GetValue(obj->base()->base()->operands()->Get(0)); auto parentBlock = GetValue(obj->base()->base()->parentBlock()); if (!obj->base()->successors() || obj->base()->successors()->size() == 0) { return builder.CreateTerminator(value, parentBlock); } else { auto succs = GetValue(obj->base()->successors()); CJC_ASSERT(succs.size() == 1); return builder.CreateTerminator(value, succs[0], parentBlock); } } template <> ApplyWithException* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::ApplyWithException* obj) { auto operands = GetValue(obj->base()->base()->operands()); auto callee = operands[0]; auto args = std::vector(operands.begin() + 1, operands.end()); // Exceptions auto sucBlock = GetValue(obj->base()->successors()->Get(0)); auto errBlock = GetValue(obj->base()->successors()->Get(1)); auto parentBlock = GetValue(obj->base()->base()->parentBlock()); auto resultTy = GetType(obj->base()->base()->resultTy()); auto thisType = GetType(obj->thisType()); auto instantiatedTypeArgs = GetType(obj->instantiatedTypeArgs()); auto result = builder.CreateExpression(resultTy, callee, FuncCallContext{ .args = args, .instTypeArgs = instantiatedTypeArgs, .thisType = thisType}, sucBlock, errBlock, parentBlock); return result; } template <> InvokeWithException* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::InvokeWithException* obj) { auto operands = GetValue(obj->base()->base()->operands()); auto args = std::vector(operands.begin() + 1, operands.end()); // Exceptions auto sucBlock = GetValue(obj->base()->successors()->Get(0)); auto errBlock = GetValue(obj->base()->successors()->Get(1)); auto parentBlock = GetValue(obj->base()->base()->parentBlock()); auto resultTy = GetType(obj->base()->base()->resultTy()); auto tempTypes = GetType(obj->virMethodCtx()->genericTypeParams()); std::vector genericTypes; for (auto ty : tempTypes) { genericTypes.emplace_back(Cangjie::StaticCast(ty)); } auto invokeInfo = InvokeCallContext { .caller = operands[0], .funcCallCtx = FuncCallContext { .args = std::vector(operands.begin() + 1, operands.end()), .instTypeArgs = GetType(obj->instantiatedTypeArgs()), .thisType = GetType(obj->thisType()) }, .virMethodCtx = VirMethodContext { .srcCodeIdentifier = obj->virMethodCtx()->srcCodeIdentifier()->data(), .originalFuncType = GetType(obj->virMethodCtx()->originalFuncType()), .genericTypeParams = std::move(genericTypes) } }; return builder.CreateExpression(resultTy, invokeInfo, sucBlock, errBlock, parentBlock); } template <> IntOpWithException* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::IntOpWithException* obj) { auto opKind = obj->opKind(); auto lhs = GetValue(obj->base()->base()->operands()->Get(0)); auto resultTy = GetType(obj->base()->base()->resultTy()); // Exceptions auto sucBlock = GetValue(obj->base()->successors()->Get(0)); auto errBlock = GetValue(obj->base()->successors()->Get(1)); auto parentBlock = GetValue(obj->base()->base()->parentBlock()); if (obj->base()->base()->operands()->size() == 1) { return builder.CreateExpression( resultTy, ExprKind(opKind), lhs, sucBlock, errBlock, parentBlock); } auto rhs = GetValue(obj->base()->base()->operands()->Get(1)); auto intOpWithExcept = builder.CreateExpression( resultTy, ExprKind(opKind), lhs, rhs, sucBlock, errBlock, parentBlock); return intOpWithExcept; } template <> TypeCastWithException* CHIRDeserializer::CHIRDeserializerImpl::Deserialize( const PackageFormat::TypeCastWithException* obj) { auto operand = GetValue(obj->base()->base()->operands()->Get(0)); auto resultTy = GetType(obj->base()->base()->resultTy()); // Exceptions auto sucBlock = GetValue(obj->base()->successors()->Get(0)); auto errBlock = GetValue(obj->base()->successors()->Get(1)); auto parentBlock = GetValue(obj->base()->base()->parentBlock()); return builder.CreateExpression(resultTy, operand, sucBlock, errBlock, parentBlock); } template <> IntrinsicWithException* CHIRDeserializer::CHIRDeserializerImpl::Deserialize( const PackageFormat::IntrinsicWithException* obj) { auto kind = CHIR::IntrinsicKind(obj->intrinsicKind()); auto args = GetValue(obj->base()->base()->operands()); auto resultTy = GetType(obj->base()->base()->resultTy()); // Exceptions auto sucBlock = GetValue(obj->base()->successors()->Get(0)); auto errBlock = GetValue(obj->base()->successors()->Get(1)); auto parentBlock = GetValue(obj->base()->base()->parentBlock()); auto callContext = IntrisicCallContext { .kind = kind, .args = args, .instTypeArgs = GetType(obj->instantiatedTypeArgs()) }; return builder.CreateExpression(resultTy, callContext, sucBlock, errBlock, parentBlock); } template <> AllocateWithException* CHIRDeserializer::CHIRDeserializerImpl::Deserialize( const PackageFormat::AllocateWithException* obj) { auto targetType = GetType(obj->targetType()); auto resultTy = GetType(obj->base()->base()->resultTy()); // Exceptions auto sucBlock = GetValue(obj->base()->successors()->Get(0)); auto errBlock = GetValue(obj->base()->successors()->Get(1)); auto parentBlock = GetValue(obj->base()->base()->parentBlock()); return builder.CreateExpression(resultTy, targetType, sucBlock, errBlock, parentBlock); } template <> RawArrayAllocateWithException* CHIRDeserializer::CHIRDeserializerImpl::Deserialize( const PackageFormat::RawArrayAllocateWithException* obj) { auto elementType = GetType(obj->elementType()); auto size = GetValue(obj->base()->base()->operands()->Get(0)); auto resultTy = GetType(obj->base()->base()->resultTy()); // Exceptions auto sucBlock = GetValue(obj->base()->successors()->Get(0)); auto errBlock = GetValue(obj->base()->successors()->Get(1)); auto parentBlock = GetValue(obj->base()->base()->parentBlock()); return builder.CreateExpression( resultTy, elementType, size, sucBlock, errBlock, parentBlock); } template <> SpawnWithException* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::SpawnWithException* obj) { auto operands = GetValue(obj->base()->base()->operands()); auto val = operands[0]; auto func = GetValue(obj->executeClosure()); auto resultTy = GetType(obj->base()->base()->resultTy()); // Exceptions auto sucBlock = GetValue(obj->base()->successors()->Get(0)); auto errBlock = GetValue(obj->base()->successors()->Get(1)); auto parentBlock = GetValue(obj->base()->base()->parentBlock()); SpawnWithException* spawn = nullptr; if (operands.size() > 1) { auto arg = operands[1]; spawn = builder.CreateExpression( resultTy, val, arg, sucBlock, errBlock, parentBlock); } else { spawn = builder.CreateExpression( resultTy, val, sucBlock, errBlock, parentBlock); } if (func) { spawn->SetExecuteClosure(*func); } return spawn; } template <> GetRTTI* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::GetRTTI* obj) { auto operands = GetValue(obj->base()->operands()); auto val = operands[0]; auto resultTy = GetType(obj->base()->resultTy()); auto parentBlock = GetValue(obj->base()->parentBlock()); return builder.CreateExpression(resultTy, val, parentBlock); } template <> GetRTTIStatic* CHIRDeserializer::CHIRDeserializerImpl::Deserialize(const PackageFormat::GetRTTIStatic* obj) { auto resultTy = GetType(obj->base()->resultTy()); auto rttiType = GetType(obj->rttiType()); auto parentBlock = GetValue(obj->base()->parentBlock()); return builder.CreateExpression(resultTy, rttiType, parentBlock); } // =========================== Configuration ============================== void CHIRDeserializer::CHIRDeserializerImpl::ConfigBase(const PackageFormat::Base* buffer, Base& obj) { CJC_NULLPTR_CHECK(buffer); Base base; auto annos = buffer->annos(); auto annoTypes = buffer->annos_type(); for (unsigned i = 0; i < annos->size(); ++i) { auto anno = annos->Get(i); switch (annoTypes->Get(i)) { case PackageFormat::Annotation::Annotation_needCheckArrayBound: base.Set(static_cast(anno)->need()); break; case PackageFormat::Annotation::Annotation_needCheckCast: base.Set(static_cast(anno)->need()); break; case PackageFormat::Annotation::Annotation_debugLocationInfo: base.SetDebugLocation(Create(static_cast(anno))); break; case PackageFormat::Annotation::Annotation_debugLocationInfoForWarning: base.Set( Create(static_cast(anno))); break; case PackageFormat::Annotation::Annotation_linkTypeInfo: base.Set( Cangjie::Linkage(static_cast(anno)->linkage())); break; case PackageFormat::Annotation::Annotation_skipCheck: base.Set( CHIR::SkipKind(static_cast(anno)->skipKind())); break; case PackageFormat::Annotation::Annotation_neverOverflowInfo: base.Set( static_cast(anno)->neverOverflow()); break; case PackageFormat::Annotation::Annotation_generatedFromForIn: base.Set( static_cast(anno)->value()); break; case PackageFormat::Annotation::Annotation_isAutoEnvClass: base.Set(static_cast(anno)->value()); break; case PackageFormat::Annotation::Annotation_isCapturedClassInCC: base.Set( static_cast(anno)->value()); break; case PackageFormat::Annotation::Annotation_enumCaseIndex: { int64_t index = static_cast(anno)->index(); if (index != -1) { base.Set(static_cast(index)); } break; } case PackageFormat::Annotation::Annotation_virMethodOffset: { int64_t offset = static_cast(anno)->offset(); if (offset != -1) { base.Set(static_cast(offset)); } break; } case PackageFormat::Annotation::Annotation_wrappedRawMethod: base.Set( GetValue(static_cast(anno)->rawMethod())); break; default: continue; } } obj.CopyAnnotationMapFrom(base); } void CHIRDeserializer::CHIRDeserializerImpl::ConfigValue(const PackageFormat::Value* buffer, Value& obj) { CJC_NULLPTR_CHECK(buffer); ConfigBase(buffer->base(), obj); obj.AppendAttributeInfo(CreateAttr(buffer->attributes())); if (buffer->identifier()) { obj.identifier = buffer->identifier()->str(); } } void CHIRDeserializer::CHIRDeserializerImpl::ConfigCustomTypeDef( const PackageFormat::CustomTypeDef* buffer, CustomTypeDef& obj) { CJC_NULLPTR_CHECK(buffer); ConfigBase(buffer->base(), obj); // kind is setted when create classDef/structDef... auto srcCodeIdentifiers = buffer->srcCodeIdentifier()->str(); obj.srcCodeIdentifier = srcCodeIdentifiers; auto identifier = buffer->identifier()->str(); obj.identifier = identifier; auto packageNames = buffer->packageName()->str(); obj.packageName = packageNames; auto type = GetType(buffer->type()); obj.type = type; if (buffer->genericDecl() != 0) { auto genericDecl = GetCustomTypeDef(buffer->genericDecl()); CJC_NULLPTR_CHECK(genericDecl); obj.SetGenericDecl(*genericDecl); } auto declaredMethods = GetValue(buffer->methods()); for (auto declaredMethod : declaredMethods) { CJC_NULLPTR_CHECK(declaredMethod); obj.AddMethod(declaredMethod); } auto implementedInterfaces = GetType(buffer->implementedInterfaces()); for (auto implementedInterface : implementedInterfaces) { CJC_NULLPTR_CHECK(implementedInterface); obj.AddImplementedInterfaceTy(*implementedInterface); } auto instanceMemberVars = Create(buffer->instanceMemberVars()); for (auto var : instanceMemberVars) { obj.AddInstanceVar(var); } auto staticMemberVars = GetValue(buffer->staticMemberVars()); for (auto var : staticMemberVars) { CJC_NULLPTR_CHECK(var); obj.AddStaticMemberVar(var); } obj.AppendAttributeInfo(CreateAttr(buffer->attributes())); if (compilePlatform) { obj.EnableAttr(Attribute::DESERIALIZED); } obj.SetAnnoInfo(Create(buffer->annoInfo())); auto vtable = Create>>(buffer->vtable()); obj.SetVTable(vtable); auto varInitializationFunc = GetValue(buffer->varInitializationFunc()); if (varInitializationFunc) { obj.SetVarInitializationFunc(varInitializationFunc); } } void CHIRDeserializer::CHIRDeserializerImpl::ConfigExpression(const PackageFormat::Expression* buffer, Expression& obj) { CJC_NULLPTR_CHECK(buffer); ConfigBase(buffer->base(), obj); } template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::Lambda* buffer, Lambda& obj) { CJC_NULLPTR_CHECK(buffer); ConfigExpression(buffer->base(), obj); // the parameter will be inserted into Lambda when Parameter is created. GetValue(buffer->params()); if (auto *retVal = GetValue(buffer->retVal()); retVal != nullptr) { obj.SetReturnValue(*retVal); } } template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::ForInRange* buffer, ForInRange& obj) { CJC_NULLPTR_CHECK(buffer); CJC_NULLPTR_CHECK(buffer->base()); ConfigExpression(buffer->base(), obj); auto body = GetValue(buffer->base()->blockGroups()->Get(0)); auto latch = GetValue(buffer->base()->blockGroups()->Get(1)); auto cond = GetValue(buffer->base()->blockGroups()->Get(2)); CJC_NULLPTR_CHECK(body); CJC_NULLPTR_CHECK(latch); CJC_NULLPTR_CHECK(cond); obj.InitBlockGroups(*body, *latch, *cond); } template <> void CHIRDeserializer::CHIRDeserializerImpl::Config( const PackageFormat::ForInClosedRange* buffer, ForInClosedRange& obj) { CJC_NULLPTR_CHECK(buffer); CJC_NULLPTR_CHECK(buffer->base()); ConfigExpression(buffer->base(), obj); auto body = GetValue(buffer->base()->blockGroups()->Get(0)); auto latch = GetValue(buffer->base()->blockGroups()->Get(1)); auto cond = GetValue(buffer->base()->blockGroups()->Get(2)); CJC_NULLPTR_CHECK(body); CJC_NULLPTR_CHECK(latch); CJC_NULLPTR_CHECK(cond); obj.InitBlockGroups(*body, *latch, *cond); } template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::ForInIter* buffer, ForInIter& obj) { CJC_NULLPTR_CHECK(buffer); CJC_NULLPTR_CHECK(buffer->base()); ConfigExpression(buffer->base(), obj); auto body = GetValue(buffer->base()->blockGroups()->Get(0)); auto latch = GetValue(buffer->base()->blockGroups()->Get(1)); auto cond = GetValue(buffer->base()->blockGroups()->Get(2)); CJC_NULLPTR_CHECK(body); CJC_NULLPTR_CHECK(latch); CJC_NULLPTR_CHECK(cond); obj.InitBlockGroups(*body, *latch, *cond); } template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::ImportedFunc* buffer, ImportedFunc& obj) { ConfigBase(buffer->base()->base()->base(), obj); if (buffer->paramDftValHostFunc() != 0) { auto paramDftValHostFunc = GetValue(buffer->paramDftValHostFunc()); CJC_NULLPTR_CHECK(paramDftValHostFunc); obj.SetParamDftValHostFunc(*paramDftValHostFunc); } if (buffer->genericDecl() != 0) { auto genericDecl = GetValue(buffer->genericDecl()); CJC_NULLPTR_CHECK(genericDecl); obj.SetGenericDecl(*genericDecl); } } template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::Func* buffer, Func& obj) { ConfigBase(buffer->base()->base(), obj); // the parameter will be inserted into Func when Parameter is created. auto params = GetValue(buffer->params()); obj.RemoveParams(); for (auto p : params) { CJC_NULLPTR_CHECK(p); obj.AddParam(*p); } auto body = GetValue(buffer->body()); if (body) { obj.InitBody(*body); } else { // 'common' function can be without body CJC_ASSERT(obj.TestAttr(Attribute::COMMON)); } obj.SetFuncKind(FuncKind(buffer->funcKind())); if (buffer->retVal() != 0) { obj.SetReturnValue(*GetValue(buffer->retVal())); } obj.SetRawMangledName(buffer->rawMangledName()->str()); obj.SetAnnoInfo(Create(buffer->base()->annoInfo())); obj.SetParentRawMangledName(buffer->parentName()->str()); obj.AppendAttributeInfo(CreateAttr(buffer->base()->attributes())); if (compilePlatform) { obj.EnableAttr(Attribute::DESERIALIZED); } obj.SetPropLocation(Create(buffer->propLoc())); if (buffer->originalLambdaFuncType() != 0) { FuncSigInfo funcSig; funcSig.funcName = obj.GetSrcCodeIdentifier(); funcSig.funcType = GetType(buffer->originalLambdaFuncType()); funcSig.genericTypeParams = GetType(buffer->originalLambdaGenericTypeParams()); obj.SetOriginalLambdaInfo(funcSig); } if (buffer->paramDftValHostFunc() != 0) { auto paramDftValHostFunc = GetValue(buffer->paramDftValHostFunc()); CJC_NULLPTR_CHECK(paramDftValHostFunc); obj.SetParamDftValHostFunc(*paramDftValHostFunc); } obj.SetFastNative(buffer->isFastNative()); obj.SetCFFIWrapper(buffer->isCFFIWrapper()); if (auto* retVal = GetValue(buffer->retVal()); retVal != nullptr) { obj.SetReturnValue(*retVal); } if (buffer->genericDecl() != 0) { auto genericDecl = GetValue(buffer->genericDecl()); CJC_NULLPTR_CHECK(genericDecl); obj.SetGenericDecl(*genericDecl); } } template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::LocalVar* buffer, LocalVar& obj) { ConfigValue(buffer->base(), obj); // set identifier for convenient comparision. obj.identifier = buffer->base()->identifier()->str(); if (buffer->isRetVal()) { obj.SetRetValue(); } } template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::BlockGroup* buffer, BlockGroup& obj) { ConfigBase(buffer->base()->base(), obj); GetValue(buffer->blocks()); obj.SetEntryBlock(GetValue(buffer->entryBlock())); obj.identifier = buffer->base()->identifier()->str(); } template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::Block* buffer, Block& obj) { ConfigBase(buffer->base()->base(), obj); obj.AppendAttributeInfo(CreateAttr(buffer->base()->attributes())); if (buffer->isLandingPadBlock()) { obj.SetExceptions(GetType(buffer->exceptionCatchList())); } obj.AppendExpressions(GetExpression(buffer->exprs())); obj.identifier = buffer->base()->identifier()->str(); obj.predecessors.clear(); obj.predecessors = GetValue(buffer->predecessors()); } template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::GlobalVar* buffer, GlobalVar& obj) { ConfigBase(buffer->base()->base(), obj); obj.SetAnnoInfo(Create(buffer->base()->annoInfo())); if (auto initializer = DynamicCast(GetValue(buffer->defaultInitVal()))) { obj.SetInitializer(*initializer); } else if (auto initFunc = GetValue(buffer->associatedInitFunc())) { obj.SetInitFunc(*initFunc); } } template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::EnumDef* buffer, EnumDef& obj) { ConfigCustomTypeDef(buffer->base(), obj); for (auto info : Create(buffer->ctors())) { obj.AddCtor(info); } } template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::StructDef* buffer, StructDef& obj) { ConfigCustomTypeDef(buffer->base(), obj); obj.SetCStruct(buffer->isCStruct()); } template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::ClassDef* buffer, ClassDef& obj) { ConfigCustomTypeDef(buffer->base(), obj); obj.SetAnnotation(buffer->isAnnotation()); auto superClass = GetType(buffer->superClass()); if (superClass) { obj.SetSuperClassTy(*superClass); } if (buffer->abstractMethods()) { for (auto info : Create(buffer->abstractMethods())) { obj.AddAbstractMethod(info); } } } template <> void CHIRDeserializer::CHIRDeserializerImpl::Config(const PackageFormat::ExtendDef* buffer, ExtendDef& obj) { ConfigCustomTypeDef(buffer->base(), obj); auto info = GetType(buffer->extendedType()); if (info) { obj.SetExtendedType(*info); } } // =========================== Fetch by ID ============================== Value* CHIRDeserializer::CHIRDeserializerImpl::GetValue(uint32_t id) { if (id2Value.count(id) == 0) { switch (PackageFormat::ValueElem(pool->values_type()->Get(id - 1))) { case PackageFormat::ValueElem_BoolLiteral: id2Value[id] = Deserialize( static_cast(pool->values()->Get(id - 1))); ConfigValue(static_cast(pool->values()->Get(id - 1))->base()->base(), *id2Value[id]); break; case PackageFormat::ValueElem_RuneLiteral: id2Value[id] = Deserialize( static_cast(pool->values()->Get(id - 1))); ConfigValue(static_cast(pool->values()->Get(id - 1))->base()->base(), *id2Value[id]); break; case PackageFormat::ValueElem_StringLiteral: id2Value[id] = Deserialize( static_cast(pool->values()->Get(id - 1))); ConfigValue( static_cast(pool->values()->Get(id - 1))->base()->base(), *id2Value[id]); break; case PackageFormat::ValueElem_IntLiteral: id2Value[id] = Deserialize(static_cast(pool->values()->Get(id - 1))); ConfigValue(static_cast(pool->values()->Get(id - 1))->base()->base(), *id2Value[id]); break; case PackageFormat::ValueElem_FloatLiteral: id2Value[id] = Deserialize( static_cast(pool->values()->Get(id - 1))); ConfigValue( static_cast(pool->values()->Get(id - 1))->base()->base(), *id2Value[id]); break; case PackageFormat::ValueElem_UnitLiteral: id2Value[id] = Deserialize( static_cast(pool->values()->Get(id - 1))); ConfigValue(static_cast(pool->values()->Get(id - 1))->base()->base(), *id2Value[id]); break; case PackageFormat::ValueElem_NullLiteral: id2Value[id] = Deserialize( static_cast(pool->values()->Get(id - 1))); ConfigValue(static_cast(pool->values()->Get(id - 1))->base()->base(), *id2Value[id]); break; case PackageFormat::ValueElem_Parameter: id2Value[id] = Deserialize(static_cast(pool->values()->Get(id - 1))); ConfigValue( static_cast(pool->values()->Get(id - 1))->base(), *id2Value[id]); break; case PackageFormat::ValueElem_LocalVar: id2Value[id] = Deserialize(static_cast(pool->values()->Get(id - 1))); Config(static_cast(pool->values()->Get(id - 1)), *StaticCast(id2Value[id])); break; case PackageFormat::ValueElem_GlobalVar: id2Value[id] = Deserialize(static_cast(pool->values()->Get(id - 1))); ConfigValue(static_cast(pool->values()->Get(id - 1))->base(), *id2Value[id]); break; case PackageFormat::ValueElem_Func: id2Value[id] = Deserialize(static_cast(pool->values()->Get(id - 1))); break; case PackageFormat::ValueElem_ImportedVar: id2Value[id] = Deserialize( static_cast(pool->values()->Get(id - 1))); ConfigValue(static_cast(pool->values()->Get(id - 1))->base()->base(), *id2Value[id]); break; case PackageFormat::ValueElem_ImportedFunc: id2Value[id] = Deserialize( static_cast(pool->values()->Get(id - 1))); break; case PackageFormat::ValueElem_Block: id2Value[id] = Deserialize(static_cast(pool->values()->Get(id - 1))); ConfigValue( static_cast(pool->values()->Get(id - 1))->base(), *id2Value[id]); break; case PackageFormat::ValueElem_BlockGroup: id2Value[id] = Deserialize(static_cast(pool->values()->Get(id - 1))); ConfigValue( static_cast(pool->values()->Get(id - 1))->base(), *id2Value[id]); break; case PackageFormat::ValueElem_NONE: InternalError("Unsupported value type."); break; } } return id2Value[id]; } template T* CHIRDeserializer::CHIRDeserializerImpl::GetValue(uint32_t id) { if (id == 0) { return nullptr; } return DynamicCast(GetValue(id)); } Type* CHIRDeserializer::CHIRDeserializerImpl::GetType(uint32_t id) { if (id2Type.count(id) == 0) { switch (PackageFormat::TypeElem(pool->types_type()->Get(id - 1))) { case PackageFormat::TypeElem_RuneType: id2Type[id] = Deserialize(static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_BooleanType: id2Type[id] = Deserialize( static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_UnitType: id2Type[id] = Deserialize(static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_NothingType: id2Type[id] = Deserialize( static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_IntType: id2Type[id] = Deserialize(static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_FloatType: id2Type[id] = Deserialize(static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_TupleType: id2Type[id] = Deserialize(static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_RawArrayType: id2Type[id] = Deserialize( static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_VArrayType: id2Type[id] = Deserialize(static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_FuncType: id2Type[id] = Deserialize(static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_CustomType: id2Type[id] = Deserialize(static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_EnumType: id2Type[id] = Deserialize(static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_StructType: id2Type[id] = Deserialize(static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_ClassType: id2Type[id] = Deserialize(static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_CStringType: id2Type[id] = Deserialize( static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_CPointerType: id2Type[id] = Deserialize( static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_GenericType: id2Type[id] = Deserialize( static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_RefType: id2Type[id] = Deserialize(static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_BoxType: id2Type[id] = Deserialize(static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_VoidType: id2Type[id] = Deserialize(static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_ThisType: id2Type[id] = Deserialize(static_cast(pool->types()->Get(id - 1))); break; case PackageFormat::TypeElem_NONE: id2Type[id] = nullptr; break; } } return id2Type[id]; } template T* CHIRDeserializer::CHIRDeserializerImpl::GetType(uint32_t id) { if (id == 0) { return nullptr; } return StaticCast(GetType(id)); } Expression* CHIRDeserializer::CHIRDeserializerImpl::GetExpression(uint32_t id) { if (id2Expression.count(id) == 0) { switch (PackageFormat::ExpressionElem(pool->exprs_type()->Get(id - 1))) { case PackageFormat::ExpressionElem_UnaryExpression: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression( static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_BinaryExpression: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression( static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_Constant: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_Allocate: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_Load: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_Store: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_GetElementRef: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_GetElementByName: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression( static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_StoreElementRef: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression( static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_StoreElementByName: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression( static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_Apply: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_Invoke: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_TypeCast: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_InstanceOf: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_Box: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_UnBox: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_GoTo: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base()->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_Branch: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression( static_cast(pool->exprs()->Get(id - 1))->base()->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_MultiBranch: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression( static_cast(pool->exprs()->Get(id - 1))->base()->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_Exit: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base()->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_RaiseException: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1)) ->base() ->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_ApplyWithException: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1)) ->base() ->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_InvokeWithException: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1)) ->base() ->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_InvokeStatic: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_InvokeStaticWithException: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression( static_cast(pool->exprs()->Get(id - 1)) ->base() ->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_IntOpWithException: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1)) ->base() ->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_TypeCastWithException: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1)) ->base() ->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_IntrinsicWithException: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1)) ->base() ->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_AllocateWithException: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1)) ->base() ->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_RawArrayAllocateWithException: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression( static_cast(pool->exprs()->Get(id - 1)) ->base() ->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_SpawnWithException: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1)) ->base() ->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_Tuple: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_Field: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_FieldByName: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_RawArrayAllocate: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression( static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_RawArrayLiteralInit: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression( static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_RawArrayInitByValue: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression( static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_VArray: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_VArrayBd: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_GetException: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_Intrinsic: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_If: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_Loop: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_ForInRange: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); Config(static_cast(pool->exprs()->Get(id - 1)), *GetExpression(id)); break; case PackageFormat::ExpressionElem_ForInIter: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); Config(static_cast(pool->exprs()->Get(id - 1)), *GetExpression(id)); break; case PackageFormat::ExpressionElem_ForInClosedRange: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); Config(static_cast(pool->exprs()->Get(id - 1)), *GetExpression(id)); break; case PackageFormat::ExpressionElem_Debug: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_Spawn: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_Lambda: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); Config( static_cast(pool->exprs()->Get(id - 1)), *GetExpression(id)); break; case PackageFormat::ExpressionElem_GetInstantiateValue: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression( static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_TransformToConcrete: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression( static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_TransformToGeneric: id2Expression[id] = Deserialize( static_cast(pool->exprs()->Get(id - 1))); ConfigExpression( static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_UnBoxToRef: id2Expression[id] = Deserialize(static_cast(pool->exprs()->Get(id - 1))); ConfigExpression(static_cast(pool->exprs()->Get(id - 1))->base(), *id2Expression[id]); break; case PackageFormat::ExpressionElem_GetRTTI: { auto v = static_cast(pool->exprs()->Get(id - 1)); id2Expression[id] = Deserialize(v); ConfigExpression(v->base(), *id2Expression[id]); break; } case PackageFormat::ExpressionElem_GetRTTIStatic: { auto v = static_cast(pool->exprs()->Get(id - 1)); id2Expression[id] = Deserialize(v); ConfigExpression(v->base(), *id2Expression[id]); break; } case PackageFormat::ExpressionElem_NONE: InternalError("unsupported expression type in chir deserialization."); break; } } return id2Expression[id]; } template T* CHIRDeserializer::CHIRDeserializerImpl::GetExpression(uint32_t id) { if (id == 0) { return nullptr; } return StaticCast(GetExpression(id)); } CustomTypeDef* CHIRDeserializer::CHIRDeserializerImpl::GetCustomTypeDef(uint32_t id) { if (id2CustomTypeDef.count(id) == 0) { switch (PackageFormat::CustomTypeDefElem(pool->defs_type()->Get(id - 1))) { case PackageFormat::CustomTypeDefElem_EnumDef: id2CustomTypeDef[id] = Deserialize(static_cast(pool->defs()->Get(id - 1))); break; case PackageFormat::CustomTypeDefElem_StructDef: id2CustomTypeDef[id] = Deserialize(static_cast(pool->defs()->Get(id - 1))); break; case PackageFormat::CustomTypeDefElem_ClassDef: id2CustomTypeDef[id] = Deserialize(static_cast(pool->defs()->Get(id - 1))); break; case PackageFormat::CustomTypeDefElem_ExtendDef: id2CustomTypeDef[id] = Deserialize(static_cast(pool->defs()->Get(id - 1))); break; case PackageFormat::CustomTypeDefElem_NONE: CJC_ABORT(); id2CustomTypeDef[id] = nullptr; break; } } return id2CustomTypeDef[id]; } template T* CHIRDeserializer::CHIRDeserializerImpl::GetCustomTypeDef(uint32_t id) { if (id == 0) { return nullptr; } return StaticCast(GetCustomTypeDef(id)); } void CHIRDeserializer::CHIRDeserializerImpl::ResetImportedValuesUnderPackage() { CJC_NULLPTR_CHECK(pool); auto package = pool; std::vector importedVarAndFuncs; // store import var and decls for (uint32_t i = 1; i <= package->maxImportedValueId(); i++) { auto imported = GetValue(i); CJC_NULLPTR_CHECK(imported); importedVarAndFuncs.emplace_back(imported); } builder.GetCurPackage()->SetImportedVarAndFuncs(std::move(importedVarAndFuncs)); } void CHIRDeserializer::CHIRDeserializerImpl::ResetImportedDefsUnderPackage() { CJC_NULLPTR_CHECK(pool); auto package = pool; std::vector importedStructs; std::vector importedClasses; std::vector importedEnums; std::vector importedExtends; for (uint32_t i = 1; i <= package->maxImportedStructId(); i++) { auto def = GetCustomTypeDef(i); CJC_NULLPTR_CHECK(def); importedStructs.emplace_back(def); } for (uint32_t i = package->maxImportedStructId() + 1; i <= package->maxImportedClassId(); i++) { auto def = GetCustomTypeDef(i); CJC_NULLPTR_CHECK(def); importedClasses.emplace_back(def); } for (uint32_t i = package->maxImportedClassId() + 1; i <= package->maxImportedEnumId(); i++) { auto def = GetCustomTypeDef(i); CJC_NULLPTR_CHECK(def); importedEnums.emplace_back(def); } for (uint32_t i = package->maxImportedEnumId() + 1; i <= package->maxImportedExtendId(); i++) { auto def = GetCustomTypeDef(i); CJC_NULLPTR_CHECK(def); importedExtends.emplace_back(def); } builder.GetCurPackage()->SetImportedStructs(std::move(importedStructs)); builder.GetCurPackage()->SetImportedClasses(std::move(importedClasses)); builder.GetCurPackage()->SetImportedEnums(std::move(importedEnums)); builder.GetCurPackage()->SetImportedExtends(std::move(importedExtends)); } // =========================== Entry ================================== void CHIRDeserializer::CHIRDeserializerImpl::Run(const PackageFormat::CHIRPackage* package) { pool = package; builder.CreatePackage(pool->name()->str()); builder.GetCurPackage()->SetPackageAccessLevel(Package::AccessLevel(pool->pkgAccessLevel())); // To keep order, get CustomTypeDef first for (unsigned id = 1; id <= pool->defs()->size(); ++id) { GetCustomTypeDef(id); } // deserialize top level and local var for order for (unsigned id = 1; id <= pool->values()->size(); ++id) { auto valueElemKind = PackageFormat::ValueElem(pool->values_type()->Get(id - 1)); if (valueElemKind != PackageFormat::ValueElem_Func && valueElemKind != PackageFormat::ValueElem_ImportedFunc && valueElemKind != PackageFormat::ValueElem_GlobalVar && valueElemKind != PackageFormat::ValueElem_ImportedVar && valueElemKind != PackageFormat::ValueElem_LocalVar) { continue; } // NOTE: GetValue also save result in Package via Builder GetValue(id); } for (unsigned id = 1; id <= pool->values()->size(); ++id) { // lazy config Func to keep order switch (pool->values_type()->Get(id - 1)) { case PackageFormat::ValueElem_Block: Config(static_cast(pool->values()->Get(id - 1)), *GetValue(id)); break; case PackageFormat::ValueElem_BlockGroup: Config(static_cast(pool->values()->Get(id - 1)), *GetValue(id)); break; case PackageFormat::ValueElem_GlobalVar: Config(static_cast(pool->values()->Get(id - 1)), *GetValue(id)); break; case PackageFormat::ValueElem_Func: Config(static_cast(pool->values()->Get(id - 1)), *GetValue(id)); break; case PackageFormat::ValueElem_ImportedFunc: Config(static_cast(pool->values()->Get(id - 1)), *GetValue(id)); break; default: // do nothing break; } } // Config CustomTypeDef for (unsigned id = 1; id <= pool->defs()->size(); ++id) { switch (pool->defs_type()->Get(id - 1)) { case PackageFormat::CustomTypeDefElem_EnumDef: Config(static_cast(pool->defs()->Get(id - 1)), *GetCustomTypeDef(id)); break; case PackageFormat::CustomTypeDefElem_StructDef: Config(static_cast(pool->defs()->Get(id - 1)), *GetCustomTypeDef(id)); break; case PackageFormat::CustomTypeDefElem_ClassDef: Config(static_cast(pool->defs()->Get(id - 1)), *GetCustomTypeDef(id)); break; case PackageFormat::CustomTypeDefElem_ExtendDef: Config(static_cast(pool->defs()->Get(id - 1)), *GetCustomTypeDef(id)); break; default: break; } } // Config generic types for (unsigned i = 0; i < genericTypeConfig.size(); i++) { // for-loop is used because genericTypeConfig can be modified on the fly auto genericType = genericTypeConfig[i].first; auto rawGenericType = genericTypeConfig[i].second; // Fill in sub-fields auto upperBounds = GetType(rawGenericType->upperBounds()); genericType->SetUpperBounds(upperBounds); } // Package self's member builder.GetCurPackage()->SetPackageInitFunc(GetValue(pool->packageInitFunc())); auto* reInitFunc = GetValue(pool->packageLiteralInitFunc()); CJC_ASSERT(reInitFunc != nullptr); builder.GetCurPackage()->SetPackageLiteralInitFunc(reInitFunc); // Reset Imported values and defs under Package ResetImportedValuesUnderPackage(); ResetImportedDefsUnderPackage(); } cangjie_compiler-1.0.7/src/CHIR/Serializer/CHIRDeserializerImpl.h000066400000000000000000000063041510705540100245220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_DESERIALIZER_IMPL_H #define CANGJIE_CHIR_DESERIALIZER_IMPL_H #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wfloat-equal" #endif #include #if defined(__clang__) #pragma clang diagnostic pop #endif #include #include #include #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/CHIRContext.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Serializer/CHIRDeserializer.h" #include "cangjie/CHIR/Type/CustomTypeDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { class CHIRDeserializer::CHIRDeserializerImpl { public: void ConfigBase(const PackageFormat::Base* buffer, Base& obj); void ConfigValue(const PackageFormat::Value* buffer, Value& obj); void ConfigCustomTypeDef(const PackageFormat::CustomTypeDef* buffer, CustomTypeDef& obj); void ConfigExpression(const PackageFormat::Expression* buffer, Expression& obj); template T Create(const FBT* obj); template std::vector Create(const flatbuffers::Vector* vec); template T* Deserialize(const FBT* obj); template std::vector GetValue(const flatbuffers::Vector* vec); template std::vector GetType(const flatbuffers::Vector* vec); template std::vector GetExpression(const flatbuffers::Vector* vec); template std::vector GetCustomTypeDef(const flatbuffers::Vector* vec); template T* GetValue(uint32_t id); Value* GetValue(uint32_t id); template T* GetType(uint32_t id); Type* GetType(uint32_t id); template T* GetExpression(uint32_t id); Expression* GetExpression(uint32_t id); template T* GetCustomTypeDef(uint32_t id); CustomTypeDef* GetCustomTypeDef(uint32_t id); template void Config(const FBT* buffer, T& obj); void Run(const PackageFormat::CHIRPackage* package); explicit CHIRDeserializerImpl(CHIRBuilder& chirBuilder, bool compilePlatform = false) : builder(chirBuilder), compilePlatform(compilePlatform){}; private: Cangjie::CHIR::CHIRBuilder& builder; bool compilePlatform = false; const PackageFormat::CHIRPackage* pool{}; // Package object maps std::unordered_map id2Type{{0, nullptr}}; std::unordered_map id2Value{{0, nullptr}}; std::unordered_map id2Expression{{0, nullptr}}; std::unordered_map id2CustomTypeDef{{0, nullptr}}; // lazy GenericType config std::vector> genericTypeConfig; void ResetImportedValuesUnderPackage(); void ResetImportedDefsUnderPackage(); }; } #endif cangjie_compiler-1.0.7/src/CHIR/Serializer/CHIRSerializer.cpp000066400000000000000000002603011510705540100237210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Serializer/CHIRSerializer.h" #include "AnnoFactoryInfo.h" #include "CHIRSerializerImpl.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/GeneratedFromForIn.h" #include "cangjie/CHIR/IntrinsicKind.h" #include "cangjie/CHIR/ToStringUtils.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/ExtendDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/UserDefinedType.h" #include "cangjie/Utils/ICEUtil.h" #include "flatbuffers/PackageFormat_generated.h" #include "flatbuffers/buffer.h" #include #include #include #include using namespace Cangjie::CHIR; void CHIRSerializer::Serialize(const Package& package, const std::string filename, ToCHIR::Phase phase) { CHIRSerializerImpl serializer(package); serializer.Initialize(); serializer.Dispatch(); serializer.Save(filename, phase); } // ========================== ID Fetchers ============================== template std::vector CHIRSerializer::CHIRSerializerImpl::GetId(std::vector vec) { std::vector indices; for (E* elem : vec) { uint32_t id = GetId(static_cast(elem)); indices.emplace_back(id); } return indices; } template std::vector CHIRSerializer::CHIRSerializerImpl::GetId(std::vector> vec) { std::vector indices; for (Ptr elem : vec) { uint32_t id = GetId(static_cast(elem.get())); indices.emplace_back(id); } return indices; } template std::vector CHIRSerializer::CHIRSerializerImpl::GetId(const std::unordered_set& set) const { std::vector indices; for (E* elem : set) { uint32_t id = GetId(static_cast(elem)); indices.emplace_back(id); } return indices; } template <> uint32_t CHIRSerializer::CHIRSerializerImpl::GetId(const Value* obj) { if (value2Id.count(obj) == 0) { value2Id[obj] = ++valueCount; allValue.emplace_back(0); valueKind.emplace_back(0); valueQueue.push_back(obj); } return value2Id[obj]; } template <> uint32_t CHIRSerializer::CHIRSerializerImpl::GetId(const Type* obj) { if (type2Id.count(obj) == 0) { type2Id[obj] = ++typeCount; allType.emplace_back(0); typeKind.emplace_back(0); typeQueue.push(obj); } return type2Id[obj]; } template <> uint32_t CHIRSerializer::CHIRSerializerImpl::GetId(const Expression* obj) { if (expr2Id.count(obj) == 0) { expr2Id[obj] = ++exprCount; allExpression.emplace_back(0); exprKind.emplace_back(0); exprQueue.push(obj); } return expr2Id[obj]; } template <> uint32_t CHIRSerializer::CHIRSerializerImpl::GetId(const CustomTypeDef* obj) { if (def2Id.count(obj) == 0) { def2Id[obj] = ++defCount; allCustomTypeDef.emplace_back(0); defKind.emplace_back(0); defQueue.push_back(obj); } return def2Id[obj]; } // ========================== Helper Serializers =============================== template std::vector> CHIRSerializer::CHIRSerializerImpl::SerializeSetToVec( const std::unordered_set& set) const { std::vector> retval; for (T elem : set) { retval.emplace_back(Serialize(elem)); } return retval; } template std::vector> CHIRSerializer::CHIRSerializerImpl::SerializeVec(const std::vector& vec) { std::vector> retval; for (T elem : vec) { retval.emplace_back(Serialize(elem)); } return retval; } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const DebugLocation& obj) { auto beginPos = PackageFormat::CreatePos(builder, obj.GetBeginPos().line, obj.GetBeginPos().column); auto endPos = PackageFormat::CreatePos(builder, obj.GetEndPos().line, obj.GetEndPos().column); auto scope = obj.GetScopeInfo(); return PackageFormat::CreateDebugLocationDirect( builder, obj.GetAbsPath().c_str(), obj.GetFileID(), beginPos, endPos, &scope); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const AnnoInfo& obj) { return PackageFormat::CreateAnnoInfoDirect(builder, obj.mangledName.data()); } [[maybe_unused]] static void Empty(Annotation*) { } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Base& obj) { auto annoTypes = std::vector(); auto annos = std::vector>(); std::unordered_map> annoHandler; // NeedCheckArrayBound annoHandler[typeid(CHIR::NeedCheckArrayBound)] = [this, &annos, &annoTypes](Annotation* anno) { annoTypes.push_back(PackageFormat::Annotation::Annotation_needCheckArrayBound); annos.emplace_back(PackageFormat::CreateNeedCheckArrayBound( builder, NeedCheckArrayBound::Extract(StaticCast(anno))) .Union()); }; // NeedCheckCast annoHandler[typeid(CHIR::NeedCheckCast)] = [this, &annos, &annoTypes](Annotation* anno) { annoTypes.push_back(PackageFormat::Annotation::Annotation_needCheckCast); annos.emplace_back( PackageFormat::CreateNeedCheckCast(builder, NeedCheckCast::Extract(StaticCast(anno))) .Union()); }; // DebugLocationInfo annoTypes.emplace_back(PackageFormat::Annotation::Annotation_debugLocationInfo); annos.emplace_back(Serialize(obj.Base::GetDebugLocation()).Union()); // DebugLocationInfoForWarning annoHandler[typeid(CHIR::DebugLocationInfoForWarning)] = [this, &annos, &annoTypes](Annotation* anno) { annoTypes.push_back(PackageFormat::Annotation::Annotation_debugLocationInfoForWarning); annos.emplace_back(Serialize( DebugLocationInfoForWarning::Extract(StaticCast(anno))) .Union()); }; // LinkTypeInfo annoHandler[typeid(CHIR::LinkTypeInfo)] = [this, &annos, &annoTypes](Annotation* anno) { annoTypes.push_back(PackageFormat::Annotation::Annotation_linkTypeInfo); annos.emplace_back(PackageFormat::CreateLinkTypeInfo( builder, PackageFormat::Linkage(LinkTypeInfo::Extract(StaticCast(anno)))) .Union()); }; // SkipCheck annoHandler[typeid(CHIR::SkipCheck)] = [this, &annos, &annoTypes](Annotation* anno) { annoTypes.push_back(PackageFormat::Annotation::Annotation_skipCheck); annos.emplace_back(PackageFormat::CreateSkipCheck( builder, PackageFormat::SkipKind(SkipCheck::Extract(StaticCast(anno)))) .Union()); }; // WrappedRawMethod may be removed body when removeUnusedImported, do not serializer it auto wrapMethod = dynamic_cast(obj.Get()); if (wrapMethod != nullptr && !wrapMethod->GetBody()) { annoHandler[typeid(CHIR::WrappedRawMethod)] = Empty; } else { annoHandler[typeid(CHIR::WrappedRawMethod)] = [this, &annos, &annoTypes](Annotation* anno) { annoTypes.push_back(PackageFormat::Annotation::Annotation_wrappedRawMethod); auto rawMethod = GetId(StaticCast(WrappedRawMethod::Extract(StaticCast(anno)))); annos.emplace_back(PackageFormat::CreateWrappedRawMethod(builder, rawMethod).Union()); }; } // NeverOverflowInfo annoHandler[typeid(CHIR::NeverOverflowInfo)] = [this, &annos, &annoTypes](Annotation* anno) { annoTypes.push_back(PackageFormat::Annotation::Annotation_neverOverflowInfo); annos.emplace_back(PackageFormat::CreateNeverOverflowInfo( builder, NeverOverflowInfo::Extract(StaticCast(anno))) .Union()); }; // GeneratedFromForIn annoHandler[typeid(CHIR::GeneratedFromForIn)] = [this, &annos, &annoTypes](Annotation* anno) { annoTypes.push_back(PackageFormat::Annotation::Annotation_generatedFromForIn); annos.emplace_back(PackageFormat::CreateGeneratedFromForIn( builder, GeneratedFromForIn::Extract(StaticCast(anno))) .Union()); }; // IsAutoEnvClass annoHandler[typeid(CHIR::IsAutoEnvClass)] = [this, &annos, &annoTypes](Annotation* anno) { annoTypes.push_back(PackageFormat::Annotation::Annotation_isAutoEnvClass); annos.emplace_back(PackageFormat::CreateIsAutoEnvClass( builder, IsAutoEnvClass::Extract(StaticCast(anno))) .Union()); }; // IsCapturedClassInCC annoHandler[typeid(CHIR::IsCapturedClassInCC)] = [this, &annos, &annoTypes](Annotation* anno) { annoTypes.push_back(PackageFormat::Annotation::Annotation_isCapturedClassInCC); annos.emplace_back(PackageFormat::CreateIsCapturedClassInCC( builder, IsCapturedClassInCC::Extract(StaticCast(anno))) .Union()); }; // EnumCaseIndex annoHandler[typeid(CHIR::EnumCaseIndex)] = [this, &annos, &annoTypes](Annotation* anno) { annoTypes.push_back(PackageFormat::Annotation::Annotation_enumCaseIndex); auto index = EnumCaseIndex::Extract(StaticCast(anno)); int64_t indexNum = -1; if (index.has_value()) { indexNum = static_cast(index.value()); } annos.emplace_back(PackageFormat::CreateEnumCaseIndex(builder, indexNum).Union()); }; // VirMethodOffset annoHandler[typeid(CHIR::VirMethodOffset)] = [this, &annos, &annoTypes](Annotation* anno) { annoTypes.push_back(PackageFormat::Annotation::Annotation_virMethodOffset); auto offset = VirMethodOffset::Extract(StaticCast(anno)); int64_t offsetNum = -1; if (offset.has_value()) { offsetNum = static_cast(offset.value()); } annos.emplace_back(PackageFormat::CreateVirMethodOffset(builder, offsetNum).Union()); }; annoHandler[typeid(CHIR::AnnoFactoryInfo)] = Empty; for (auto& entry : obj.GetAnno().GetAnnos()) { if (annoHandler.count(entry.first) != 0) { annoHandler.at(entry.first)(entry.second.get()); } else { CJC_ABORT(); } } return PackageFormat::CreateBaseDirect(builder, &annoTypes, &annos); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Expression& obj); template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Tuple& obj); template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const MemberVarInfo& obj) { auto name = obj.name; auto rawMangledName = obj.rawMangledName; auto type = GetId(obj.type); auto attributes = obj.attributeInfo.GetRawAttrs().to_ulong(); auto loc = Serialize(obj.loc); auto annoInfo = Serialize(obj.annoInfo); auto initializerFunc = GetId(obj.initializerFunc); auto outerDef = GetId(obj.outerDef); return PackageFormat::CreateMemberVarInfoDirect( builder, name.data(), rawMangledName.data(), type, attributes, loc, annoInfo, initializerFunc, outerDef); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const EnumCtorInfo& obj) { return PackageFormat::CreateEnumCtorInfoDirect( builder, obj.name.data(), obj.mangledName.data(), GetId(obj.funcType)); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const AbstractMethodParam& obj) { auto paramName = obj.paramName; auto paramType = GetId(obj.type); auto annoInfo = Serialize(obj.annoInfo); return PackageFormat::CreateAbstractMethodParamDirect(builder, paramName.data(), paramType, annoInfo); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const AbstractMethodInfo& obj) { auto methodName = obj.methodName; auto mangledName = obj.GetASTMangledName(); auto methodType = GetId(obj.methodTy); auto paramsInfo = SerializeVec(obj.paramInfos); auto attributes = obj.attributeInfo.GetRawAttrs().to_ulong(); auto annoInfo = Serialize(obj.annoInfo); auto methodGenericTypeParams = GetId(obj.methodGenericTypeParams); auto hasBody = obj.hasBody; auto parent = GetId(obj.parent); return PackageFormat::CreateAbstractMethodInfoDirect(builder, methodName.data(), mangledName.data(), methodType, ¶msInfo, attributes, annoInfo, methodGenericTypeParams.empty() ? nullptr : &methodGenericTypeParams, hasBody, parent); } // ========================== Type Serializers ================================= template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Type& obj) { auto typeId = GetId(&obj); auto kind = PackageFormat::CHIRTypeKind(obj.GetTypeKind()); auto argTys = GetId(obj.GetTypeArgs()); auto refDims = obj.GetRefDims(); return PackageFormat::CreateTypeDirect(builder, kind, typeId, argTys.empty() ? nullptr : &argTys, refDims); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const RuneType& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateRuneTypeDirect(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const BooleanType& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateBooleanTypeDirect(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const UnitType& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateUnitTypeDirect(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const NothingType& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateNothingTypeDirect(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const NumericType& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateNumericTypeDirect(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const TupleType& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateTupleType(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const RawArrayType& obj) { auto base = Serialize(static_cast(obj)); auto dims = obj.GetDims(); return PackageFormat::CreateRawArrayType(builder, base, dims); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const VArrayType& obj) { auto base = Serialize(static_cast(obj)); auto size = obj.GetSize(); return PackageFormat::CreateVArrayType(builder, base, size); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const FuncType& obj) { auto base = Serialize(static_cast(obj)); auto isCFuncType = obj.IsCFunc(); auto hasVarArg = obj.HasVarArg(); return PackageFormat::CreateFuncType(builder, base, isCFuncType, hasVarArg); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const CustomType& obj) { auto base = Serialize(static_cast(obj)); auto customTypeDef = GetId(obj.GetCustomTypeDef()); return PackageFormat::CreateCustomTypeDirect(builder, base, nullptr, nullptr, customTypeDef); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const CStringType& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateCStringTypeDirect(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const CPointerType& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateCPointerTypeDirect(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const GenericType& obj) { auto base = Serialize(static_cast(obj)); auto identifier = obj.GetIdentifier(); auto srcCodeIndentifier = obj.GetSrcCodeIdentifier(); auto upperBounds = GetId(obj.GetUpperBounds()); return PackageFormat::CreateGenericTypeDirect(builder, base, obj.orphanFlag, obj.skipCheck, identifier.data(), srcCodeIndentifier.data(), upperBounds.empty() ? nullptr : &upperBounds); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const RefType& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateRefType(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const BoxType& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateBoxType(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const VoidType& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateVoidType(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const ThisType& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateThisType(builder, base); } // ========================== Numeric Type Serializers ========================= template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const IntType& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateIntType(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const FloatType& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateFloatType(builder, base); } // ========================== Custom Type Serializers ========================== template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const EnumType& obj) { auto base = Serialize(static_cast(obj)); bool isBoxed = obj.IsBox(); return PackageFormat::CreateEnumType(builder, base, isBoxed); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const StructType& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateStructType(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const ClassType& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateClassType(builder, base); } // ======================= Value Serializers =================================== template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Value& obj) { auto base = Serialize(static_cast(obj)); auto valueId = GetId(&obj); auto identifier = obj.GetIdentifier(); auto type = GetId(obj.GetType()); auto kind = PackageFormat::ValueKind(obj.GetValueKind()); auto attributes = obj.GetAttributeInfo().GetRawAttrs().to_ulong(); auto annoInfo = Serialize(obj.GetAnnoInfo()); return PackageFormat::CreateValueDirect( builder, base, type, identifier.data(), kind, valueId, attributes, annoInfo); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Parameter& obj) { auto base = Serialize(static_cast(obj)); auto ownedFunc = GetId(obj.GetOwnerFunc()); auto ownedLambda = GetId(obj.GetOwnerLambda()); return PackageFormat::CreateParameter(builder, base, ownedFunc, ownedLambda); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const LocalVar& obj) { auto base = Serialize(static_cast(obj)); auto associatedExpr = GetId(obj.GetExpr()); auto isRetVal = obj.IsRetValue(); return PackageFormat::CreateLocalVar(builder, base, associatedExpr, isRetVal); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const GlobalVar& obj) { auto base = Serialize(static_cast(obj)); auto srcCodeIdentifier = obj.GetSrcCodeIdentifier(); auto packageName = obj.GetPackageName(); auto rawMangledName = obj.GetRawMangledName(); auto defaultInitVal = GetId(obj.GetInitializer()); auto associatedInitFunc = GetId(obj.GetInitFunc()); auto declaredParent = GetId(obj.GetParentCustomTypeDef()); return PackageFormat::CreateGlobalVarDirect(builder, base, rawMangledName.data(), srcCodeIdentifier.data(), packageName.data(), defaultInitVal, associatedInitFunc, declaredParent); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Block& obj) { auto base = Serialize(static_cast(obj)); auto parentGroup = GetId(obj.GetParentBlockGroup()); auto exprs = GetId(obj.GetExpressions()); auto predecessors = GetId(obj.GetPredecessors()); auto exceptionCatchList = GetId(obj.IsLandingPadBlock() ? obj.GetExceptions() : std::vector()); return PackageFormat::CreateBlockDirect(builder, base, parentGroup, exprs.empty() ? nullptr : &exprs, predecessors.empty() ? nullptr : &predecessors, obj.IsLandingPadBlock(), exceptionCatchList.empty() ? nullptr : &exceptionCatchList); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const BlockGroup& obj) { CJC_ASSERT(obj.GetOwnerFunc() || obj.GetOwnerExpression()); auto base = Serialize(static_cast(obj)); auto entryBlock = GetId(obj.GetEntryBlock()); auto blocks = GetId(obj.GetBlocks()); auto ownedFunc = GetId(obj.GetOwnerFunc()); auto ownedExpression = GetId(obj.GetOwnerExpression()); return PackageFormat::CreateBlockGroupDirect( builder, base, entryBlock, blocks.empty() ? nullptr : &blocks, ownedFunc, ownedExpression); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Func& obj) { auto base = Serialize(static_cast(obj)); // FuncBase auto srcCodeIdentifier = obj.GetSrcCodeIdentifier(); auto rawMangledName = obj.GetRawMangledName(); auto packageName = obj.GetPackageName(); auto declaredParent = GetId(obj.GetParentCustomTypeDef()); bool skipGenericDecl = false; uint32_t genericDecl = 0; // genericFunc may be removed body when removeUnusedImported, do not serializer it if (auto genericFunc = DynamicCast(obj.GetGenericDecl()); genericFunc && !genericFunc->GetBody()) { skipGenericDecl = true; } if (!skipGenericDecl) { genericDecl = GetId(obj.GetGenericDecl()); } auto funcKind = PackageFormat::FuncKind(obj.GetFuncKind()); uint32_t oriLambdaFuncTy = obj.GetFuncKind() == LAMBDA ? GetId(obj.GetOriginalLambdaType()) : 0; std::vector oriLambdaGenericTypeParams{}; if (obj.GetFuncKind() == LAMBDA) { oriLambdaGenericTypeParams = GetId(obj.GetOriginalGenericTypeParams()); } auto genericTypeParams = GetId(obj.GetGenericTypeParams()); auto paramDftValHostFunc = GetId(obj.GetParamDftValHostFunc()); // FuncBody CJC_NULLPTR_CHECK(obj.GetBody()); auto body = GetId(obj.GetBody()); auto params = GetId(obj.GetParams()); auto retVal = GetId(obj.GetReturnValue()); auto parentName = obj.GetParentRawMangledName(); auto propLoc = Serialize(obj.GetPropLocation()); auto localId = obj.localId; auto blockId = obj.blockId; auto blockGroupId = obj.blockGroupId; return PackageFormat::CreateFuncDirect(builder, base, srcCodeIdentifier.data(), rawMangledName.data(), packageName.data(), declaredParent, genericDecl, funcKind, obj.IsFastNative(), obj.IsCFFIWrapper(), oriLambdaFuncTy, oriLambdaGenericTypeParams.empty() ? nullptr : &oriLambdaGenericTypeParams, genericTypeParams.empty() ? nullptr : &genericTypeParams, paramDftValHostFunc, body, params.empty() ? nullptr : ¶ms, retVal, parentName.data(), propLoc, localId, blockId, blockGroupId); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const ImportedValue& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateImportedValue(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const LiteralValue& obj) { auto base = Serialize(static_cast(obj)); auto literalKind = PackageFormat::ConstantValueKind(obj.GetConstantValueKind()); return PackageFormat::CreateLiteralValue(builder, base, literalKind); } // ======================= Imported Value Serializers =========================== template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const ImportedFunc& obj) { auto base = Serialize(static_cast(obj)); // FuncBase auto srcCodeIdentifier = obj.GetSrcCodeIdentifier(); auto rawMangledName = obj.GetRawMangledName(); auto packageName = obj.GetPackageName(); auto declaredParent = GetId(obj.GetParentCustomTypeDef()); bool skipGenericDecl = false; uint32_t genericDecl = 0; // genericFunc may be removed body when removeUnusedImported, do not serializer it if (auto genericFunc = DynamicCast(obj.GetGenericDecl()); genericFunc && !genericFunc->GetBody()) { skipGenericDecl = true; } if (!skipGenericDecl) { genericDecl = GetId(obj.GetGenericDecl()); } auto funcKind = PackageFormat::FuncKind(obj.GetFuncKind()); uint32_t oriLambdaFuncTy = obj.GetFuncKind() == LAMBDA ? GetId(obj.GetOriginalLambdaType()) : 0; std::vector oriLambdaGenericTypeParams{}; if (obj.GetFuncKind() == LAMBDA) { oriLambdaGenericTypeParams = GetId(obj.GetOriginalGenericTypeParams()); } auto genericTypeParams = GetId(obj.GetGenericTypeParams()); auto paramDftValHostFunc = GetId(obj.GetParamDftValHostFunc()); auto paramInfo = SerializeVec(obj.GetParamInfo()); return PackageFormat::CreateImportedFuncDirect(builder, base, srcCodeIdentifier.data(), rawMangledName.data(), packageName.data(), declaredParent, genericDecl, funcKind, obj.IsFastNative(), obj.IsCFFIWrapper(), oriLambdaFuncTy, oriLambdaGenericTypeParams.empty() ? nullptr : &oriLambdaGenericTypeParams, genericTypeParams.empty() ? nullptr : &genericTypeParams, paramDftValHostFunc, ¶mInfo); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const ImportedVar& obj) { auto base = Serialize(static_cast(obj)); auto packageName = obj.GetPackageName(); auto srcCodeIdentifier = obj.GetSrcCodeIdentifier(); auto rawMangledName = obj.GetRawMangledName(); return PackageFormat::CreateImportedVarDirect( builder, base, packageName.c_str(), srcCodeIdentifier.c_str(), rawMangledName.c_str()); } // ======================= Literal Value Serializers =========================== template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const BoolLiteral& obj) { auto base = Serialize(static_cast(obj)); auto val = obj.GetVal(); return PackageFormat::CreateBoolLiteral(builder, base, val); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const RuneLiteral& obj) { auto base = Serialize(static_cast(obj)); auto val = obj.GetVal(); return PackageFormat::CreateRuneLiteral(builder, base, val); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const StringLiteral& obj) { auto base = Serialize(static_cast(obj)); auto val = builder.CreateSharedString(obj.GetVal()); return PackageFormat::CreateStringLiteral(builder, base, val); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const IntLiteral& obj) { auto base = Serialize(static_cast(obj)); auto val = obj.GetUnsignedVal(); return PackageFormat::CreateIntLiteral(builder, base, val); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const FloatLiteral& obj) { auto base = Serialize(static_cast(obj)); auto val = obj.GetVal(); return PackageFormat::CreateFloatLiteral(builder, base, val); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const UnitLiteral& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateUnitLiteral(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const NullLiteral& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateNullLiteral(builder, base); } // ======================= Expression Serializers ============================== template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Expression& obj) { auto base = Serialize(static_cast(obj)); auto expressionId = GetId(&obj); auto kind = PackageFormat::CHIRExprKind(obj.GetExprKind()); auto operands = GetId(obj.GetOperands()); auto blockGroups = GetId(obj.GetBlockGroups()); auto parentBlock = GetId(obj.GetParentBlock()); auto resultLocalVar = GetId(obj.GetResult()); auto resultTy = GetId(obj.GetResult() ? obj.GetResult()->GetType() : nullptr); return PackageFormat::CreateExpressionDirect(builder, base, kind, expressionId, operands.empty() ? nullptr : &operands, blockGroups.empty() ? nullptr : &blockGroups, parentBlock, resultLocalVar, resultTy); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const UnaryExpression& obj) { auto base = Serialize(static_cast(obj)); auto overflowStrategy = PackageFormat::OverflowStrategy(obj.GetOverflowStrategy()); return PackageFormat::CreateUnaryExpression(builder, base, overflowStrategy); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const BinaryExpression& obj) { auto base = Serialize(static_cast(obj)); auto overflowStrategy = PackageFormat::OverflowStrategy(obj.GetOverflowStrategy()); return PackageFormat::CreateBinaryExpression(builder, base, overflowStrategy); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Constant& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateConstant(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Allocate& obj) { auto base = Serialize(static_cast(obj)); auto targetType = GetId(obj.GetType()); return PackageFormat::CreateAllocate(builder, base, targetType); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Load& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateLoad(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Store& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateStore(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const GetElementRef& obj) { auto base = Serialize(static_cast(obj)); auto path = obj.GetPath(); return PackageFormat::CreateGetElementRefDirect(builder, base, path.empty() ? nullptr : &path); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const GetElementByName& obj) { auto base = Serialize(static_cast(obj)); auto names = builder.CreateVectorOfStrings(obj.GetNames()); return PackageFormat::CreateGetElementByName(builder, base, names); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const StoreElementRef& obj) { auto base = Serialize(static_cast(obj)); auto path = obj.GetPath(); return PackageFormat::CreateStoreElementRefDirect(builder, base, &path); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const StoreElementByName& obj) { auto base = Serialize(static_cast(obj)); auto names = builder.CreateVectorOfStrings(obj.GetNames()); return PackageFormat::CreateStoreElementByName(builder, base, names); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Apply& obj) { auto base = Serialize(static_cast(obj)); auto instTypeArgs = GetId(obj.GetInstantiatedTypeArgs()); auto isSuperCall = obj.IsSuperCall(); auto thisType = GetId(obj.GetThisType()); return PackageFormat::CreateApplyDirect( builder, base, isSuperCall, instTypeArgs.empty() ? nullptr : &instTypeArgs, thisType); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Invoke& obj) { auto base = Serialize(static_cast(obj)); auto instTypeArgs = GetId(obj.GetInstantiatedTypeArgs()); auto thisType = GetId(obj.GetThisType()); auto tempTypes = GetId(obj.GetGenericTypeParams()); auto virMethodCtx = PackageFormat::CreateVirMethodContextDirect(builder, obj.GetMethodName().data(), GetId(obj.GetMethodType()), tempTypes.empty() ? nullptr : &tempTypes); return PackageFormat::CreateInvokeDirect( builder, base, instTypeArgs.empty() ? nullptr : &instTypeArgs, thisType, virMethodCtx); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const InvokeStatic& obj) { auto base = Serialize(static_cast(obj)); auto instTypeArgs = GetId(obj.GetInstantiatedTypeArgs()); auto thisType = GetId(obj.GetThisType()); auto tempTypes = GetId(obj.GetGenericTypeParams()); auto virMethodCtx = PackageFormat::CreateVirMethodContextDirect(builder, obj.GetMethodName().data(), GetId(obj.GetMethodType()), tempTypes.empty() ? nullptr : &tempTypes); return PackageFormat::CreateInvokeStaticDirect( builder, base, instTypeArgs.empty() ? nullptr : &instTypeArgs, thisType, virMethodCtx); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const TypeCast& obj) { auto base = Serialize(static_cast(obj)); auto overflowStrategy = PackageFormat::OverflowStrategy(obj.GetOverflowStrategy()); return PackageFormat::CreateTypeCast(builder, base, overflowStrategy); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const InstanceOf& obj) { auto base = Serialize(static_cast(obj)); auto targetType = GetId(obj.GetType()); return PackageFormat::CreateInstanceOf(builder, base, targetType); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Box& expr) { auto base = Serialize(static_cast(expr)); return PackageFormat::CreateBox(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const UnBox& expr) { auto base = Serialize(static_cast(expr)); return PackageFormat::CreateUnBox(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Terminator& obj) { auto base = Serialize(static_cast(obj)); auto successors = GetId(obj.GetSuccessors()); return PackageFormat::CreateTerminatorDirect(builder, base, successors.empty() ? nullptr : &successors); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const GoTo& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateGoTo(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Branch& obj) { auto base = Serialize(static_cast(obj)); auto sourceExpr = obj.GetSourceExpr(); return PackageFormat::CreateBranch(builder, base, PackageFormat::SourceExpr(sourceExpr)); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const MultiBranch& obj) { auto base = Serialize(static_cast(obj)); auto caseVals = obj.GetCaseVals(); return PackageFormat::CreateMultiBranchDirect(builder, base, caseVals.empty() ? nullptr : &caseVals); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Exit& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateExit(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const RaiseException& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateRaiseException(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const ApplyWithException& obj) { CJC_ASSERT(!obj.GetOperands().empty()); auto base = Serialize(static_cast(obj)); auto thisType = GetId(obj.GetThisType()); auto instantiateArgs = GetId(obj.GetInstantiatedTypeArgs()); return PackageFormat::CreateApplyWithExceptionDirect( builder, base, thisType, instantiateArgs.empty() ? nullptr : &instantiateArgs); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const InvokeWithException& obj) { CJC_ASSERT(!obj.GetOperands().empty()); auto base = Serialize(static_cast(obj)); auto instTypeArgs = GetId(obj.GetInstantiatedTypeArgs()); auto thisType = GetId(obj.GetThisType()); auto tempTypes = GetId(obj.GetGenericTypeParams()); auto virMethodCtx = PackageFormat::CreateVirMethodContextDirect(builder, obj.GetMethodName().data(), GetId(obj.GetMethodType()), tempTypes.empty() ? nullptr : &tempTypes); return PackageFormat::CreateInvokeWithExceptionDirect( builder, base, instTypeArgs.empty() ? nullptr : &instTypeArgs, thisType, virMethodCtx); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const InvokeStaticWithException& obj) { auto base = Serialize(static_cast(obj)); auto instTypeArgs = GetId(obj.GetInstantiatedTypeArgs()); auto thisType = GetId(obj.GetThisType()); auto tempTypes = GetId(obj.GetGenericTypeParams()); auto virMethodCtx = PackageFormat::CreateVirMethodContextDirect(builder, obj.GetMethodName().data(), GetId(obj.GetMethodType()), tempTypes.empty() ? nullptr : &tempTypes); return PackageFormat::CreateInvokeStaticWithExceptionDirect( builder, base, instTypeArgs.empty() ? nullptr : &instTypeArgs, thisType, virMethodCtx); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const GetInstantiateValue& obj) { auto base = Serialize(static_cast(obj)); auto instantiateTys = GetId(obj.GetInstantiateTypes()); return PackageFormat::CreateGetInstantiateValueDirect( builder, base, instantiateTys.empty() ? nullptr : &instantiateTys); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const TransformToConcrete& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateTransformToConcrete(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const TransformToGeneric& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateTransformToGeneric(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const UnBoxToRef& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateUnBoxToRef(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const GetRTTI& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateGetRTTI(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const GetRTTIStatic& obj) { auto base = Serialize(static_cast(obj)); auto rtti = GetId(obj.GetRTTIType()); return PackageFormat::CreateGetRTTIStatic(builder, base, rtti); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const IntOpWithException& obj) { auto base = Serialize(static_cast(obj)); auto opKind = PackageFormat::CHIRExprKind(obj.GetOpKind()); auto overflowStrategy = PackageFormat::OverflowStrategy(obj.GetOverflowStrategy()); return PackageFormat::CreateIntOpWithException(builder, base, opKind, overflowStrategy); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const TypeCastWithException& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateTypeCastWithException(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const IntrinsicWithException& obj) { auto base = Serialize(static_cast(obj)); auto intrinsicKind = PackageFormat::IntrinsicKind(obj.GetIntrinsicKind()); auto instantiatedTypeArgs = GetId(obj.GetInstantiatedTypeArgs()); return PackageFormat::CreateIntrinsicWithExceptionDirect( builder, base, intrinsicKind, instantiatedTypeArgs.empty() ? nullptr : &instantiatedTypeArgs); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const AllocateWithException& obj) { auto base = Serialize(static_cast(obj)); auto targetType = GetId(obj.GetType()); return PackageFormat::CreateAllocateWithException(builder, base, targetType); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const RawArrayAllocateWithException& obj) { auto base = Serialize(static_cast(obj)); auto elementType = GetId(obj.GetElementType()); return PackageFormat::CreateRawArrayAllocateWithException(builder, base, elementType); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const SpawnWithException& obj) { auto base = Serialize(static_cast(obj)); auto executeClosure = GetId(obj.GetExecuteClosure()); return PackageFormat::CreateSpawnWithException(builder, base, executeClosure); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Tuple& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateTuple(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Field& obj) { auto base = Serialize(static_cast(obj)); auto path = obj.GetPath(); return PackageFormat::CreateFieldDirect(builder, base, path.empty() ? nullptr : &path); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const FieldByName& obj) { auto base = Serialize(static_cast(obj)); auto names = builder.CreateVectorOfStrings(obj.GetNames()); return PackageFormat::CreateFieldByName(builder, base, names); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const RawArrayAllocate& obj) { auto base = Serialize(static_cast(obj)); auto elementType = GetId(obj.GetElementType()); return PackageFormat::CreateRawArrayAllocate(builder, base, elementType); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const RawArrayLiteralInit& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateRawArrayLiteralInit(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const RawArrayInitByValue& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateRawArrayInitByValue(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const VArray& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateVArray(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const VArrayBuilder& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateVArrayBd(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const GetException& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateGetException(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Intrinsic& obj) { auto base = Serialize(static_cast(obj)); auto intrinsicKind = PackageFormat::IntrinsicKind(obj.GetIntrinsicKind()); auto instantiatedTypeArgs = GetId(obj.GetInstantiatedTypeArgs()); return PackageFormat::CreateIntrinsicDirect( builder, base, intrinsicKind, instantiatedTypeArgs.empty() ? nullptr : &instantiatedTypeArgs); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const If& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateIf(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Loop& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateLoop(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const ForInRange& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateForInRange(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const ForInClosedRange& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateForInClosedRange(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const ForInIter& obj) { auto base = Serialize(static_cast(obj)); return PackageFormat::CreateForInIter(builder, base); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Debug& obj) { auto base = Serialize(static_cast(obj)); auto srcCodeIdentifier = obj.GetSrcCodeIdentifier(); CJC_ASSERT(!obj.GetOperands().empty()); return PackageFormat::CreateDebugDirect(builder, base, srcCodeIdentifier.data()); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Spawn& obj) { auto base = Serialize(static_cast(obj)); auto executeClosure = GetId(obj.GetExecuteClosure()); return PackageFormat::CreateSpawn(builder, base, executeClosure); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const Lambda& obj) { CJC_ASSERT(obj.GetBlockGroups().size() == 1); CJC_ASSERT(obj.GetBody()); auto base = Serialize(static_cast(obj)); auto funcTy = GetId(obj.GetFuncType()); // use typeID auto isLocalFunc = obj.IsLocalFunc(); auto identifier = obj.GetIdentifier(); auto srcCodeIdentifier = obj.GetSrcCodeIdentifier(); auto params = GetId(obj.GetParams()); auto genericTypeParams = GetId(obj.GetGenericTypeParams()); auto body = GetId(obj.GetBody()); auto retVal = GetId(obj.GetReturnValue()); auto isConst = obj.IsCompileTimeValue(); return PackageFormat::CreateLambdaDirect(builder, base, funcTy, isLocalFunc, identifier.data(), srcCodeIdentifier.data(), params.empty() ? nullptr : ¶ms, genericTypeParams.empty() ? nullptr : &genericTypeParams, body, retVal, isConst); } // ======================= Custom Type Def Serializers ========================= template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const VirtualFuncTypeInfo& obj) { auto sigType = GetId(obj.sigType); auto originalType = GetId(obj.originalType); auto parentType = GetId(obj.parentType); auto returnType = GetId(obj.returnType); auto methodGenericTypeParams = GetId(obj.methodGenericTypeParams); return PackageFormat::CreateVirtualFuncTypeInfoDirect(builder, sigType, originalType, parentType, returnType, methodGenericTypeParams.empty() ? nullptr : &methodGenericTypeParams); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const VirtualFuncInfo& obj) { std::string srcId = obj.srcCodeIdentifier; bool needSkipInstance = false; if (obj.instance && obj.instance->IsFuncWithBody()) { if (auto f = DynamicCast(obj.instance); f && !f->GetBody()) { // instance may be removed body when removeUnusedImported, do not serializer it needSkipInstance = true; } } uint32_t ins = needSkipInstance ? 0 : GetId(obj.instance); auto attributes = obj.attr.GetRawAttrs().to_ulong(); auto typeInfo = Serialize(obj.typeInfo); return PackageFormat::CreateVirtualFuncInfoDirect(builder, srcId.data(), ins, attributes, typeInfo); } std::vector> CHIRSerializer::CHIRSerializerImpl::SerializeVTable( const VTableType& obj) { std::vector> retval; for (auto& elem : obj) { auto ty = GetId(elem.first); auto info = SerializeVec(elem.second); retval.push_back(PackageFormat::CreateVTableElementDirect(builder, ty, &info)); } return retval; } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize( const CustomTypeDef& obj) { auto base = Serialize(static_cast(obj)); auto kind = PackageFormat::CustomDefKind(obj.GetCustomKind()); auto customTypeDefID = GetId(&obj); auto srcCodeIdentifier = obj.GetSrcCodeIdentifier(); auto identifier = obj.GetIdentifier(); auto packageName = obj.GetPackageName(); auto type = GetId(obj.CustomTypeDef::GetType()); auto genericDecl = GetId(obj.GetGenericDecl()); auto methods = GetId(obj.GetMethods()); auto implementedInterfaces = GetId(obj.GetImplementedInterfaceTys()); auto instanceMemberVars = obj.GetCustomKind() == CustomDefKind::TYPE_CLASS ? SerializeVec(StaticCast(obj).GetDirectInstanceVars()) : SerializeVec(obj.GetAllInstanceVars()); auto staticMemberVars = GetId(obj.GetStaticMemberVars()); auto attributes = obj.GetAttributeInfo().GetRawAttrs().to_ulong(); auto annoInfo = Serialize(obj.GetAnnoInfo()); auto vtable = SerializeVTable(static_cast(obj.GetVTable())); auto varInitializationFunc = GetId(obj.GetVarInitializationFunc()); return PackageFormat::CreateCustomTypeDefDirect(builder, base, kind, customTypeDefID, srcCodeIdentifier.data(), identifier.data(), packageName.data(), type, genericDecl, methods.empty() ? nullptr : &methods, implementedInterfaces.empty() ? nullptr : &implementedInterfaces, &instanceMemberVars, staticMemberVars.empty() ? nullptr : &staticMemberVars, attributes, annoInfo, &vtable, nullptr, varInitializationFunc); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const EnumDef& obj) { auto base = Serialize(static_cast(obj)); auto ctors = SerializeVec(obj.GetCtors()); auto nonExhaustive = !obj.IsExhaustive(); return PackageFormat::CreateEnumDefDirect(builder, base, &ctors, nonExhaustive); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const StructDef& obj) { auto base = Serialize(static_cast(obj)); auto isCStruct = obj.IsCStruct(); return PackageFormat::CreateStructDef(builder, base, isCStruct); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const ClassDef& obj) { auto base = Serialize(static_cast(obj)); auto kind = obj.IsInterface() ? PackageFormat::ClassDefKind::ClassDefKind_INTERFACE : PackageFormat::ClassDefKind::ClassDefKind_CLASS; auto isAnnotation = obj.IsAnnotation(); auto superClass = GetId(obj.GetSuperClassTy()); auto abstractMethods = SerializeVec(obj.GetAbstractMethods()); return PackageFormat::CreateClassDefDirect(builder, base, kind, isAnnotation, superClass, &abstractMethods); } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Serialize(const ExtendDef& obj) { auto base = Serialize(static_cast(obj)); auto extendedType = GetId(obj.GetExtendedType()); auto genericParams = GetId(obj.GetGenericTypeParams()); return PackageFormat::CreateExtendDefDirect( builder, base, extendedType, genericParams.empty() ? nullptr : &genericParams); } // ========================== Dispatchers =============================== template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Dispatch(const Type& obj) { switch (obj.GetTypeKind()) { case Type::TypeKind::TYPE_INT8: case Type::TypeKind::TYPE_INT16: case Type::TypeKind::TYPE_INT32: case Type::TypeKind::TYPE_INT64: case Type::TypeKind::TYPE_INT_NATIVE: case Type::TypeKind::TYPE_UINT8: case Type::TypeKind::TYPE_UINT16: case Type::TypeKind::TYPE_UINT32: case Type::TypeKind::TYPE_UINT64: case Type::TypeKind::TYPE_UINT_NATIVE: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_IntType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_FLOAT16: case Type::TypeKind::TYPE_FLOAT32: case Type::TypeKind::TYPE_FLOAT64: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_FloatType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_RUNE: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_RuneType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_BOOLEAN: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_BooleanType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_UNIT: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_UnitType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_NOTHING: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_NothingType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_VOID: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_VoidType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_TUPLE: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_TupleType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_STRUCT: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_StructType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_ENUM: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_EnumType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_FUNC: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_FuncType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_CLASS: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_ClassType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_RAWARRAY: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_RawArrayType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_VARRAY: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_VArrayType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_CPOINTER: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_CPointerType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_CSTRING: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_CStringType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_GENERIC: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_GenericType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_REFTYPE: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_RefType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_BOXTYPE: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_BoxType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_THIS: typeKind[GetId(&obj) - 1] = static_cast(PackageFormat::TypeElem_ThisType); return Serialize(static_cast(obj)).Union(); case Type::TypeKind::TYPE_INVALID: case Type::TypeKind::MAX_TYPE_KIND: CJC_ABORT(); return 0; } } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Dispatch(const LiteralValue& obj) { switch (obj.GetConstantValueKind()) { case ConstantValueKind::KIND_BOOL: valueKind[GetId(&obj) - 1] = PackageFormat::ValueElem_BoolLiteral; return Serialize(static_cast(obj)).Union(); case ConstantValueKind::KIND_RUNE: valueKind[GetId(&obj) - 1] = PackageFormat::ValueElem_RuneLiteral; return Serialize(static_cast(obj)).Union(); case ConstantValueKind::KIND_INT: valueKind[GetId(&obj) - 1] = PackageFormat::ValueElem_IntLiteral; return Serialize(static_cast(obj)).Union(); case ConstantValueKind::KIND_FLOAT: valueKind[GetId(&obj) - 1] = PackageFormat::ValueElem_FloatLiteral; return Serialize(static_cast(obj)).Union(); case ConstantValueKind::KIND_STRING: valueKind[GetId(&obj) - 1] = PackageFormat::ValueElem_StringLiteral; return Serialize(static_cast(obj)).Union(); case ConstantValueKind::KIND_UNIT: valueKind[GetId(&obj) - 1] = PackageFormat::ValueElem_UnitLiteral; return Serialize(static_cast(obj)).Union(); case ConstantValueKind::KIND_NULL: valueKind[GetId(&obj) - 1] = PackageFormat::ValueElem_NullLiteral; return Serialize(static_cast(obj)).Union(); case ConstantValueKind::KIND_FUNC: return 0; } } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Dispatch(const Value& obj) { switch (obj.GetValueKind()) { case Value::ValueKind::KIND_LITERAL: return Dispatch(static_cast(obj)).Union(); case Value::ValueKind::KIND_GLOBALVAR: valueKind[GetId(&obj) - 1] = PackageFormat::ValueElem_GlobalVar; return Serialize(dynamic_cast(obj)).Union(); case Value::ValueKind::KIND_PARAMETER: valueKind[GetId(&obj) - 1] = PackageFormat::ValueElem_Parameter; return Serialize(static_cast(obj)).Union(); case Value::ValueKind::KIND_IMP_FUNC: valueKind[GetId(&obj) - 1] = PackageFormat::ValueElem_ImportedFunc; return Serialize(dynamic_cast(obj)).Union(); case Value::ValueKind::KIND_IMP_VAR: valueKind[GetId(&obj) - 1] = PackageFormat::ValueElem_ImportedVar; return Serialize(dynamic_cast(obj)).Union(); case Value::ValueKind::KIND_LOCALVAR: valueKind[GetId(&obj) - 1] = PackageFormat::ValueElem_LocalVar; return Serialize(static_cast(obj)).Union(); case Value::ValueKind::KIND_FUNC: valueKind[GetId(&obj) - 1] = PackageFormat::ValueElem_Func; return Serialize(dynamic_cast(obj)).Union(); case Value::ValueKind::KIND_BLOCK: valueKind[GetId(&obj) - 1] = PackageFormat::ValueElem_Block; return Serialize(static_cast(obj)).Union(); case Value::ValueKind::KIND_BLOCK_GROUP: valueKind[GetId(&obj) - 1] = PackageFormat::ValueElem_BlockGroup; return Serialize(static_cast(obj)).Union(); } } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Dispatch(const Expression& obj) { switch (obj.GetExprKind()) { case ExprKind::GOTO: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_GoTo; return Serialize(static_cast(obj)).Union(); case ExprKind::BRANCH: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_Branch; return Serialize(static_cast(obj)).Union(); case ExprKind::MULTIBRANCH: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_MultiBranch; return Serialize(static_cast(obj)).Union(); case ExprKind::EXIT: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_Exit; return Serialize(static_cast(obj)).Union(); case ExprKind::APPLY_WITH_EXCEPTION: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_ApplyWithException; return Serialize(static_cast(obj)).Union(); case ExprKind::INVOKE_WITH_EXCEPTION: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_InvokeWithException; return Serialize(static_cast(obj)).Union(); case ExprKind::RAISE_EXCEPTION: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_RaiseException; return Serialize(static_cast(obj)).Union(); case ExprKind::INT_OP_WITH_EXCEPTION: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_IntOpWithException; return Serialize(static_cast(obj)).Union(); case ExprKind::SPAWN_WITH_EXCEPTION: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_SpawnWithException; return Serialize(static_cast(obj)).Union(); case ExprKind::TYPECAST_WITH_EXCEPTION: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_TypeCastWithException; return Serialize(static_cast(obj)) .Union(); case ExprKind::INTRINSIC_WITH_EXCEPTION: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_IntrinsicWithException; return Serialize(static_cast(obj)) .Union(); case ExprKind::ALLOCATE_WITH_EXCEPTION: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_AllocateWithException; return Serialize(static_cast(obj)) .Union(); case ExprKind::INVOKESTATIC_WITH_EXCEPTION: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_InvokeStaticWithException; return Serialize( static_cast(obj)) .Union(); case ExprKind::RAW_ARRAY_ALLOCATE_WITH_EXCEPTION: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_RawArrayAllocateWithException; return Serialize( static_cast(obj)) .Union(); case ExprKind::NEG: case ExprKind::NOT: case ExprKind::BITNOT: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_UnaryExpression; return Serialize(static_cast(obj)).Union(); case ExprKind::ADD: case ExprKind::SUB: case ExprKind::MUL: case ExprKind::DIV: case ExprKind::MOD: case ExprKind::EXP: case ExprKind::LSHIFT: case ExprKind::RSHIFT: case ExprKind::BITAND: case ExprKind::BITOR: case ExprKind::BITXOR: case ExprKind::LT: case ExprKind::GT: case ExprKind::LE: case ExprKind::GE: case ExprKind::EQUAL: case ExprKind::NOTEQUAL: case ExprKind::AND: case ExprKind::OR: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_BinaryExpression; return Serialize(static_cast(obj)).Union(); case ExprKind::ALLOCATE: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_Allocate; return Serialize(static_cast(obj)).Union(); case ExprKind::LOAD: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_Load; return Serialize(static_cast(obj)).Union(); case ExprKind::STORE: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_Store; return Serialize(static_cast(obj)).Union(); case ExprKind::GET_ELEMENT_REF: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_GetElementRef; return Serialize(static_cast(obj)).Union(); case ExprKind::GET_ELEMENT_BY_NAME: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_GetElementByName; return Serialize(static_cast(obj)).Union(); case ExprKind::STORE_ELEMENT_REF: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_StoreElementRef; return Serialize(static_cast(obj)).Union(); case ExprKind::STORE_ELEMENT_BY_NAME: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_StoreElementByName; return Serialize(static_cast(obj)).Union(); case ExprKind::IF: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_If; return Serialize(static_cast(obj)).Union(); case ExprKind::LOOP: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_Loop; return Serialize(static_cast(obj)).Union(); case ExprKind::FORIN_RANGE: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_ForInRange; return Serialize(static_cast(obj)).Union(); case ExprKind::FORIN_ITER: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_ForInIter; return Serialize(static_cast(obj)).Union(); case ExprKind::FORIN_CLOSED_RANGE: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_ForInClosedRange; return Serialize(static_cast(obj)).Union(); case ExprKind::LAMBDA: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_Lambda; return Serialize(static_cast(obj)).Union(); case ExprKind::CONSTANT: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_Constant; return Serialize(static_cast(obj)).Union(); case ExprKind::DEBUGEXPR: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_Debug; return Serialize(static_cast(obj)).Union(); case ExprKind::TUPLE: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_Tuple; return Serialize(static_cast(obj)).Union(); case ExprKind::FIELD: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_Field; return Serialize(static_cast(obj)).Union(); case ExprKind::FIELD_BY_NAME: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_FieldByName; return Serialize(static_cast(obj)).Union(); case ExprKind::APPLY: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_Apply; return Serialize(static_cast(obj)).Union(); case ExprKind::INVOKE: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_Invoke; return Serialize(static_cast(obj)).Union(); case ExprKind::INVOKESTATIC: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_InvokeStatic; return Serialize(static_cast(obj)).Union(); case ExprKind::INSTANCEOF: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_InstanceOf; return Serialize(static_cast(obj)).Union(); case ExprKind::BOX: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_Box; return Serialize(static_cast(obj)).Union(); case ExprKind::UNBOX: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_UnBox; return Serialize(static_cast(obj)).Union(); case ExprKind::TYPECAST: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_TypeCast; return Serialize(static_cast(obj)).Union(); case ExprKind::GET_EXCEPTION: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_GetException; return Serialize(static_cast(obj)).Union(); case ExprKind::RAW_ARRAY_ALLOCATE: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_RawArrayAllocate; return Serialize(static_cast(obj)).Union(); case ExprKind::RAW_ARRAY_LITERAL_INIT: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_RawArrayLiteralInit; return Serialize(static_cast(obj)).Union(); case ExprKind::RAW_ARRAY_INIT_BY_VALUE: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_RawArrayInitByValue; return Serialize(static_cast(obj)).Union(); case ExprKind::VARRAY: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_VArray; return Serialize(static_cast(obj)).Union(); case ExprKind::VARRAY_BUILDER: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_VArrayBd; return Serialize(static_cast(obj)).Union(); case ExprKind::INTRINSIC: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_Intrinsic; return Serialize(static_cast(obj)).Union(); case ExprKind::SPAWN: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_Spawn; return Serialize(static_cast(obj)).Union(); case ExprKind::GET_INSTANTIATE_VALUE: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_GetInstantiateValue; return Serialize(static_cast(obj)).Union(); case ExprKind::TRANSFORM_TO_GENERIC: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_TransformToGeneric; return Serialize(static_cast(obj)).Union(); case ExprKind::TRANSFORM_TO_CONCRETE: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_TransformToConcrete; return Serialize(static_cast(obj)).Union(); case ExprKind::UNBOX_TO_REF: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_UnBoxToRef; return Serialize(static_cast(obj)).Union(); case ExprKind::GET_RTTI: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_GetRTTI; return Serialize(static_cast(obj)).Union(); case ExprKind::GET_RTTI_STATIC: exprKind[GetId(&obj) - 1] = PackageFormat::ExpressionElem_GetRTTIStatic; return Serialize(static_cast(obj)).Union(); case ExprKind::INVALID: case ExprKind::MAX_EXPR_KINDS: CJC_ABORT(); return 0; } } template <> flatbuffers::Offset CHIRSerializer::CHIRSerializerImpl::Dispatch(const CustomTypeDef& obj) { switch (obj.GetCustomKind()) { case CustomDefKind::TYPE_STRUCT: defKind[GetId(&obj) - 1] = PackageFormat::CustomTypeDefElem_StructDef; return Serialize(static_cast(obj)).Union(); case CustomDefKind::TYPE_ENUM: defKind[GetId(&obj) - 1] = PackageFormat::CustomTypeDefElem_EnumDef; return Serialize(static_cast(obj)).Union(); case CustomDefKind::TYPE_CLASS: defKind[GetId(&obj) - 1] = PackageFormat::CustomTypeDefElem_ClassDef; return Serialize(static_cast(obj)).Union(); case CustomDefKind::TYPE_EXTEND: defKind[GetId(&obj) - 1] = PackageFormat::CustomTypeDefElem_ExtendDef; return Serialize(static_cast(obj)).Union(); } } void CHIRSerializer::CHIRSerializerImpl::Dispatch() { while (!(typeQueue.empty() && valueQueue.empty() && exprQueue.empty() && defQueue.empty())) { while (!typeQueue.empty()) { auto type = typeQueue.front(); allType[GetId(type) - 1] = Dispatch(*type); typeQueue.pop(); } while (!valueQueue.empty()) { auto value = valueQueue.front(); allValue[GetId(value) - 1] = Dispatch(*value); valueQueue.pop_front(); } while (!exprQueue.empty()) { auto expr = exprQueue.front(); allExpression[GetId(expr) - 1] = Dispatch(*expr); exprQueue.pop(); } while (!defQueue.empty()) { auto def = defQueue.front(); allCustomTypeDef[GetId(def) - 1] = Dispatch(*def); defQueue.pop_front(); } } packageInitFunc = GetId(package.GetPackageInitFunc()); packageLiteralInitFunc = GetId(package.GetPackageLiteralInitFunc()); } // ========================== Utilities ========================================== void CHIRSerializer::CHIRSerializerImpl::Save(const std::string& filename, ToCHIR::Phase phase) { auto accesslevel = package.GetPackageAccessLevel(); auto packageName = package.GetName(); auto serializedPackage = PackageFormat::CreateCHIRPackageDirect(builder, packageName.c_str(), "", PackageFormat::PackageAccessLevel(accesslevel), &typeKind, &allType, &valueKind, &allValue, &exprKind, &allExpression, &defKind, &allCustomTypeDef, packageInitFunc, PackageFormat::Phase(phase), packageLiteralInitFunc, maxImportedValueId, maxImportedStructId, maxImportedClassId, maxImportedEnumId, maxImportedExtendId); builder.Finish(serializedPackage); const uint8_t* buf = builder.GetBufferPointer(); auto size = builder.GetSize(); std::ofstream output(filename, std::ios::out | std::ofstream::binary); CJC_ASSERT(output.is_open()); output.write(reinterpret_cast(buf), static_cast(size)); output.close(); } void CHIRSerializer::CHIRSerializerImpl::Initialize() { // imports for (auto value : package.GetImportedVarAndFuncs()) { valueQueue.push_back(value); } maxImportedValueId = static_cast(valueQueue.size()); for (auto def : package.GetImportedStructs()) { defQueue.push_back(def); } maxImportedStructId = static_cast(defQueue.size()); for (auto def : package.GetImportedClasses()) { defQueue.push_back(def); } maxImportedClassId = static_cast(defQueue.size()); for (auto def : package.GetImportedEnums()) { defQueue.push_back(def); } maxImportedEnumId = static_cast(defQueue.size()); for (auto def : package.GetImportedExtends()) { defQueue.push_back(def); } maxImportedExtendId = static_cast(defQueue.size()); // current package for (auto value : package.GetGlobalVars()) { valueQueue.push_back(value); } for (auto def : package.GetStructs()) { defQueue.push_back(def); } for (auto def : package.GetClasses()) { defQueue.push_back(def); } for (auto def : package.GetEnums()) { defQueue.push_back(def); } for (auto def : package.GetExtends()) { defQueue.push_back(def); } for (auto value : package.GetGlobalFuncs()) { valueQueue.push_back(value); } // allocate def id earlier for (auto def : std::as_const(defQueue)) { def2Id[def] = ++defCount; allCustomTypeDef.emplace_back(0); defKind.emplace_back(0); } // allocate value id earlier for (auto obj : std::as_const(valueQueue)) { value2Id[obj] = ++valueCount; allValue.emplace_back(0); valueKind.emplace_back(0); } } cangjie_compiler-1.0.7/src/CHIR/Serializer/CHIRSerializerImpl.h000066400000000000000000000067201510705540100242130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CHIR_SERIALIZER_IMPL_H #define CANGJIE_CHIR_SERIALIZER_IMPL_H #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wfloat-equal" #endif #include #if defined(__clang__) #pragma clang diagnostic pop #endif #include #include #include #include #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/CHIRContext.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Serializer/CHIRSerializer.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/UserDefinedType.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CHIR { class CHIRSerializer::CHIRSerializerImpl { public: explicit CHIRSerializerImpl(const Package& package) : package(package){}; // Utility void Save(const std::string& filename, ToCHIR::Phase phase); void Initialize(); void Dispatch(); private: const Package& package; flatbuffers::FlatBufferBuilder builder; std::deque valueQueue; std::queue typeQueue; std::queue exprQueue; std::deque defQueue; uint32_t typeCount = 0; uint32_t valueCount = 0; uint32_t exprCount = 0; uint32_t defCount = 0; // Id maps std::unordered_map type2Id{{nullptr, 0}}; std::unordered_map value2Id{{nullptr, 0}}; std::unordered_map expr2Id{{nullptr, 0}}; std::unordered_map def2Id{{nullptr, 0}}; // Kind Indicators std::vector typeKind{}; std::vector valueKind{}; std::vector exprKind{}; std::vector defKind{}; // Containers std::vector> allType{}; std::vector> allValue{}; std::vector> allExpression{}; std::vector> allCustomTypeDef{}; // Serializers template flatbuffers::Offset Serialize(const T& obj); template std::vector> SerializeVec(const std::vector& vec); template std::vector> SerializeSetToVec(const std::unordered_set& set) const; std::vector> SerializeVTable(const VTableType& obj); // Dispatchers template flatbuffers::Offset Dispatch(const T& obj); // Fetch ID template uint32_t GetId(const T* obj); template std::vector GetId(std::vector vec); template std::vector GetId(std::vector> vec); template std::vector GetId(const std::unordered_set& set) const; // other to save unsigned packageInitFunc{}; unsigned packageLiteralInitFunc{}; uint32_t maxImportedValueId = 0; uint32_t maxImportedStructId = 0; uint32_t maxImportedClassId = 0; uint32_t maxImportedEnumId = 0; uint32_t maxImportedExtendId = 0; }; } // namespace Cangjie::CHIR #endif cangjie_compiler-1.0.7/src/CHIR/Serializer/CMakeLists.txt000066400000000000000000000005221510705540100231730ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB CHIRSerializer *.cpp) set(CHIRSerializer ${CHIRSerializer} PARENT_SCOPE)cangjie_compiler-1.0.7/src/CHIR/StringWrapper.cpp000066400000000000000000000023221510705540100216350ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/StringWrapper.h" using namespace Cangjie::CHIR; StringWrapper::StringWrapper(const std::string& initVal) : value(initVal) { } const std::string& StringWrapper::Str() const { return value; } void StringWrapper::Append(const std::string& newValue) { value += newValue; } void StringWrapper::Append(const std::string& newValue, const std::string& delimiter) { if (!newValue.empty() && !value.empty()) { value += delimiter + " " + newValue; } } void StringWrapper::RemoveLastNChars(const size_t n) { for (size_t i = 0; i < n; ++i) { value.pop_back(); } } StringWrapper& StringWrapper::AddDelimiterOrNot(const std::string& delimiter) { if (!value.empty()) { value += delimiter; } return *this; } StringWrapper& StringWrapper::AppendOrClear(const std::string& newValue) { if (newValue.empty()) { value.clear(); } else { value += newValue; } return *this; }cangjie_compiler-1.0.7/src/CHIR/ToStringUtils.cpp000066400000000000000000000376031510705540100216320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements tostring utils API for CHIR */ #include "cangjie/CHIR/ToStringUtils.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/Utils/Utils.h" namespace Cangjie::CHIR { void PrintIndent(std::ostream& stream, size_t indent) { for (unsigned i = 0; i < indent * 2; i++) { // 2 denote width of indent. stream << " "; } } std::string GetGenericConstaintsStr(const std::vector& genericTypeParams) { std::stringstream ss; ss << "["; size_t i = 0; for (auto genericTypeParam : genericTypeParams) { auto upperBounds = genericTypeParam->GetUpperBounds(); if (upperBounds.empty()) { ++i; continue; } ss << genericTypeParam->ToString(); ss << " <: "; size_t j = 0; for (auto upperBound : upperBounds) { ss << upperBound->ToString(); if (j < upperBounds.size() - 1) { ss << " & "; } ++j; } if (i < genericTypeParams.size() - 1) { ss << ", "; } ++i; } ss << "]"; // Skip the "[]" if there is no constraints constexpr int bracketsSize{2}; if (ss.str().size() == bracketsSize) { return ""; } return ss.str(); } std::string GetBlockStr(const Block& block, size_t indent) { std::stringstream ss; PrintIndent(ss, indent); ss << block.GetAttributeInfo().ToString(); ss << "Block " << block.GetIdentifier() << ": "; ss << "// preds: "; auto predecessors = block.GetPredecessors(); for (size_t i = 0; i < predecessors.size(); i++) { if (i > 0) { ss << ", "; } ss << "#" << predecessors[i]->GetIdentifierWithoutPrefix(); } if (auto annostr = block.ToStringAnnotationMap(); !annostr.empty()) { ss << " // " << annostr; } if (block.IsLandingPadBlock()) { PrintIndent(ss, indent + 1); ss << "// exceptions: " << GetExceptionsStr(block.GetExceptions()); } ss << std::endl; auto exprs = block.GetExpressions(); for (auto& expr : exprs) { PrintIndent(ss, indent + 1); if (auto res = expr->GetResult(); res != nullptr) { if (expr->GetResult()->IsRetValue()) { ss << "[ret] "; } ss << res->GetAttributeInfo().ToString(); ss << res->GetIdentifier() << ": " << res->GetType()->ToString() << " = "; } ss << expr->ToString(indent + 1); ss << "\n"; } return ss.str(); } std::string GetBlockGroupStr(const BlockGroup& blockGroup, size_t indent) { std::stringstream ss; PrintIndent(ss, indent); ss << "{ " << "// Block Group: " << blockGroup.GetIdentifier() << "\n"; if (!blockGroup.GetBlocks().empty()) { auto cmp = [](const Ptr b1, const Ptr b2) { return b1->GetIdentifier() < b2->GetIdentifier(); }; auto blocks = Utils::VecToSortedSet(blockGroup.GetBlocks(), cmp); auto sortedBlock = TopologicalSort(blockGroup.GetEntryBlock()); for (auto block : sortedBlock) { ss << GetBlockStr(*block, indent); blocks.erase(block); } // print orphan block for (auto block : blocks) { ss << GetBlockStr(*block, indent); } } PrintIndent(ss, indent); ss << "}"; return ss.str(); } std::string GetGenericTypeParamsStr(const std::vector& genericTypeParams) { std::string res; if (!genericTypeParams.empty()) { res += "<"; size_t i = 0; for (auto genericTypeParam : genericTypeParams) { res += genericTypeParam->ToString(); if (i < genericTypeParams.size() - 1) { res += ", "; } ++i; } res += ">"; } return res; } std::string GetGenericTypeParamsConstraintsStr(const std::vector& genericTypeParams) { std::string res; if (!genericTypeParams.empty()) { auto constraintsStr = GetGenericConstaintsStr(genericTypeParams); if (!constraintsStr.empty()) { res += "genericConstraints: " + constraintsStr; } } return res; } std::string GetLambdaStr(const Lambda& lambda, size_t indent) { std::stringstream ss; if (lambda.IsCompileTimeValue()) { ss << "[compileTimeVal] "; } if (lambda.IsLocalFunc()) { ss << "[localFunc] "; } ss << "Lambda"; if (!lambda.GetIdentifier().empty()) { ss << "[" << lambda.GetIdentifier() << "]"; } ss << GetGenericTypeParamsStr(lambda.GetGenericTypeParams()); ss << "("; auto& params = lambda.GetParams(); if (!params.empty()) { ss << params[0]->GetAttributeInfo().ToString(); ss << params[0]->GetIdentifier() << ": " << params[0]->GetType()->ToString(); } for (size_t i = 1; i < params.size(); i++) { ss << ", "; ss << params[i]->GetAttributeInfo().ToString(); ss << params[i]->GetIdentifier() << ": " << params[i]->GetType()->ToString(); } ss << ")"; ss << "=> {"; ss << " // "; ss << " srcCodeIdentifier: " << lambda.GetSrcCodeIdentifier(); ss << GetGenericTypeParamsConstraintsStr(lambda.GetGenericTypeParams()); ss << "\n" << GetBlockGroupStr(*lambda.GetBody(), indent + 1); ss << "}"; return ss.str(); } std::string FuncSymbolStr(const Func& func) { std::stringstream ss; ss << func.GetAttributeInfo().ToString(); if (func.IsFastNative()) { ss << "[fastNative] "; } if (func.IsCFFIWrapper()) { ss << "[CFFIWrapper] "; } ss << "Func " << func.GetIdentifier(); ss << GetGenericTypeParamsStr(func.GetGenericTypeParams()); ss << "("; auto& params = func.GetParams(); if (!params.empty()) { ss << params[0]->GetAttributeInfo().ToString(); ss << params[0]->GetIdentifier() << ": " << params[0]->GetType()->ToString(); } for (size_t i = 1; i < params.size(); i++) { ss << ", "; ss << params[i]->GetAttributeInfo().ToString(); ss << params[i]->GetIdentifier() << ": " << params[i]->GetType()->ToString(); } ss << ") : " << func.GetReturnType()->ToString(); std::stringstream attrss; attrss << GetGenericTypeParamsConstraintsStr(func.GetGenericTypeParams()); if (auto kind = func.GetFuncKind(); kind != FuncKind::DEFAULT) { if (attrss.str() != "") { attrss << ", "; } attrss << "kind: " << FUNCKIND_TO_STRING.at(kind); } auto annostr = func.ToStringAnnotationMap(); if (attrss.str() != "" && annostr != "") { attrss << ", "; } attrss << annostr; if (func.GetGenericDecl() != nullptr) { bool skipFlag = false; if (auto genericFunc = DynamicCast(func.GetGenericDecl()); genericFunc && !genericFunc->GetBody()) { skipFlag = true; } if (!skipFlag) { attrss << ", genericDecl: " << func.GetGenericDecl()->GetIdentifierWithoutPrefix(); } } if (func.GetParentCustomTypeDef() != nullptr) { attrss << ", declared parent: " << func.GetParentCustomTypeDef()->GetIdentifierWithoutPrefix(); } if (attrss.str() != "") { ss << " // " << attrss.str(); } if (func.GetParamDftValHostFunc()) { ss << " paramDftValHostFunc: " << func.GetParamDftValHostFunc()->GetIdentifier(); } if (func.GetFuncKind() == FuncKind::LAMBDA) { if (func.GetOriginalLambdaType()) { ss << " originalLambdaInfo: " << GetGenericTypeParamsStr(func.GetOriginalGenericTypeParams()) << func.GetOriginalLambdaType()->ToString(); } } if (!func.GetParentRawMangledName().empty()) { ss << " extendParentName: " << func.GetParentRawMangledName(); } auto funcAnno = func.GetAnnoInfo(); if (funcAnno.IsAvailable()) { ss << " funcAnnoInfo: " << funcAnno.mangledName; } if (!func.GetSrcCodeIdentifier().empty()) { ss << " srcCodeIdentifier: " << func.GetSrcCodeIdentifier(); } std::vector paramWithAnnos; paramWithAnnos.reserve(params.size()); std::copy_if(params.begin(), params.end(), std::back_inserter(paramWithAnnos), [](const Ptr& param) { return param->GetAnnoInfo().IsAvailable(); }); if (!paramWithAnnos.empty()) { ss << " paramAnnoInfo: "; for (size_t i = 0; i < paramWithAnnos.size(); ++i) { if (i != paramWithAnnos.size() - 1) { ss << ", "; } auto paramAnno = paramWithAnnos[i]->GetAnnoInfo(); ss << paramWithAnnos[i]->GetSrcCodeIdentifier() + " : " + paramAnno.mangledName; } } ss << "\n"; return ss.str(); } std::string GetFuncStr(const Func& func, size_t indent) { std::stringstream ss; ss << FuncSymbolStr(func); if (func.GetBody()) { ss << GetBlockGroupStr(*func.GetBody(), indent); } return ss.str(); } std::string GetImportedValueStr(const ImportedValue& value) { if (auto f = DynamicCast(&value)) { return GetImportedFuncStr(*f); } std::stringstream ss; ss << "#from <" << value.GetSourcePackageName() << "> "; ss << "import " << value.GetAttributeInfo().ToString() << value.GetIdentifier() << ": " << value.GetType()->ToString(); std::stringstream attrss; auto annostr = value.ToStringAnnotationMap(); if (attrss.str() != "" && annostr != "") { attrss << ", "; } attrss << annostr; if (attrss.str() != "") { ss << " // " << attrss.str(); } return ss.str(); } std::string GetImportedFuncStr(const ImportedFunc& value) { std::stringstream ss; ss << "#from <" << value.GetSourcePackageName() << "> "; ss << "import " << value.GetAttributeInfo().ToString(); if (value.IsFastNative()) { ss << "[fastNative] "; } if (value.IsCFFIWrapper()) { ss << "[CFFIWrapper] "; } ss << value.GetIdentifier(); // params auto params = value.GetParamInfo(); ss << "("; for (size_t i = 0; i < params.size(); ++i) { ss << params[i].paramName; if (i != params.size() - 1) { ss << ", "; } } ss << ")"; ss << ": " << value.GetType()->ToString(); std::stringstream attrss; if (auto genericTypeParams = value.GetGenericTypeParams(); !genericTypeParams.empty()) { auto constraintsStr = GetGenericConstaintsStr(genericTypeParams); if (!constraintsStr.empty()) { attrss << "genericConstraints: " << constraintsStr; } } if (auto kind = value.GetFuncKind(); kind != FuncKind::DEFAULT) { if (attrss.str() != "") { attrss << ", "; } attrss << "kind: " << FUNCKIND_TO_STRING.at(kind); } auto annostr = value.ToStringAnnotationMap(); if (attrss.str() != "" && annostr != "") { attrss << ", "; } attrss << annostr; if (value.GetGenericDecl() != nullptr) { bool skipFlag = false; if (auto genericFunc = DynamicCast(value.GetGenericDecl()); genericFunc && !genericFunc->GetBody()) { skipFlag = true; } if (!skipFlag) { attrss << ", genericDecl: " << value.GetGenericDecl()->GetIdentifierWithoutPrefix(); } } if (attrss.str() != "") { ss << " // " << attrss.str(); } if (value.GetParamDftValHostFunc()) { ss << " paramDftValHostFunc: " << value.GetParamDftValHostFunc()->GetIdentifier(); } if (auto anno = value.GetAnnoInfo(); anno.IsAvailable()) { ss << " funcAnnoInfo: " << anno.mangledName; } ss << " srcCodeIdentifier: " << value.GetSrcCodeIdentifier(); if (auto rawMangledName = value.GetRawMangledName(); !rawMangledName.empty()) { ss << " rawMangledName: " << rawMangledName; } return ss.str(); } std::string GetExceptionsStr(const std::vector& exceptions) { std::stringstream ss; ss << "[ "; if (exceptions.empty()) { ss << "ALL"; } else { size_t i = 0; for (auto& e : exceptions) { ss << e->ToString(); if (i != exceptions.size() - 1) { ss << ", "; } ++i; } } ss << " ]"; return ss.str(); } void AddCommaOrNot(std::stringstream& ss) { if (ss.str() != "") { ss << ", "; } } std::string PackageAccessLevelToString(const Package::AccessLevel& level) { auto it = PACKAGE_ACCESS_LEVEL_TO_STRING_MAP.find(level); CJC_ASSERT(it != PACKAGE_ACCESS_LEVEL_TO_STRING_MAP.end()); return it->second; } std::string CustomTypeKindToString(const CustomTypeDef& def) { if (def.IsInterface()) { return "interface"; } else if (def.IsClass()) { return "class"; } else if (def.IsStruct()) { return "struct"; } else if (def.IsEnum()) { return "enum"; } else if (def.IsExtend()) { return "extend"; } CJC_ABORT(); return ""; } std::string BoolToString(bool flag) { if (flag) { return "true"; } else { return "false"; } } StringWrapper ThisTypeToString(const Type* thisType) { StringWrapper res; if (thisType) { res.Append("ThisType: "); res.Append(thisType->ToString()); } return res; } std::string InstTypeArgsToString(const std::vector& instTypeArgs) { std::string res; if (!instTypeArgs.empty()) { res += "<"; for (auto type : instTypeArgs) { res += type->ToString(); res += ", "; } // remove the last ", " res.pop_back(); res.pop_back(); res += ">"; } return res; } std::string SuccessorsToString(const std::vector& successors) { std::string res; if (successors.size() > 0) { // normal case res += "normal: " + successors[0]->GetIdentifier(); } if (successors.size() > 1) { // exception case, exception list res += ", exception"; if (successors[1]->IsLandingPadBlock()) { res += " " + GetExceptionsStr(successors[1]->GetExceptions()) + ": "; } else { res += ": "; } res += successors[1]->GetIdentifier(); } constexpr size_t rethrowIndex = 2; if (successors.size() > rethrowIndex) { // rethrow case res += ", rethrow: " + successors[rethrowIndex]->GetIdentifier(); } return res; } std::string ExprOperandsToString(const std::vector& args) { std::string res; if (!args.empty()) { for (auto arg : args) { res += arg->GetIdentifier(); res += ", "; } res.pop_back(); res.pop_back(); } return res; } std::string ExprWithExceptionOperandsToString(const std::vector& args, const std::vector& successors) { std::string res; for (auto arg : args) { res += arg->GetIdentifier(); res += ", "; } res += SuccessorsToString(successors); return res; } std::string OverflowToString(Cangjie::OverflowStrategy ofStrategy) { static const std::unordered_map OVERFLOW_STR_MAP { {Cangjie::OverflowStrategy::NA, "NA"}, {Cangjie::OverflowStrategy::CHECKED, "CHECKED"}, {Cangjie::OverflowStrategy::WRAPPING, "WRAPPING"}, {Cangjie::OverflowStrategy::THROWING, "THROWING"}, {Cangjie::OverflowStrategy::SATURATING, "SATURATING"}, }; return "Overflow: " + OVERFLOW_STR_MAP.at(ofStrategy); } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/Transformation/000077500000000000000000000000001510705540100213315ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CHIR/Transformation/ArrayLambdaOpt.cpp000066400000000000000000000141431510705540100247020ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/ArrayLambdaOpt.h" #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Visitor/Visitor.h" using namespace Cangjie::CHIR; ArrayLambdaOpt::ArrayLambdaOpt(CHIRBuilder& builder) : builder(builder) { } void ArrayLambdaOpt::RunOnPackage(const Ptr& package, bool isDebug) { for (auto func : package->GetGlobalFuncs()) { RunOnFunc(func, isDebug); } } void ArrayLambdaOpt::RunOnFunc(const Ptr& func, bool isDebug) { auto preAcation = [this, isDebug](Expression& expr) { if (auto constVal = CheckCanRewriteLambda(&expr); constVal) { RewriteArrayInitFunc(StaticCast(expr), constVal); if (isDebug) { std::string message = "[ArrayLambda] The call to arrayInitByFunction" + ToPosInfo(expr.GetDebugLocation()) + " has been optimised to a call to arrayInitByValue.\n"; std::cout << message; } } else if (auto zeroValue = CheckCanRewriteZeroValue(&expr); zeroValue) { RewriteZeroValue(StaticCast(&expr), zeroValue); if (isDebug) { std::string message = "[ArrayLambda] The call to arrayInitByValue" + ToPosInfo(expr.GetDebugLocation()) + " has been optimised due to the item is ZeroValue.\n"; std::cout << message; } } return VisitResult::CONTINUE; }; Visitor::Visit(*func, preAcation); } static const FuncInfo ARRAY_INIT_FUNC_INFO{"arrayInitByFunction", "", {NOT_CARE}, NOT_CARE, Cangjie::CORE_PACKAGE_NAME}; Ptr ArrayLambdaOpt::CheckCanRewriteLambda(const Ptr& expr) const { if (expr->GetExprKind() != ExprKind::APPLY) { return nullptr; } auto apply = StaticCast(expr); auto callee = apply->GetCallee(); if (!callee->IsFuncWithBody()) { return nullptr; } if (!IsExpectedFunction(*VirtualCast(callee), ARRAY_INIT_FUNC_INFO)) { return nullptr; } constexpr size_t INIT_LAMBDA_INDEX = 1; CJC_ASSERT(apply->GetArgs().size() == INIT_LAMBDA_INDEX + 1); auto closureVar = apply->GetArgs()[INIT_LAMBDA_INDEX]; if (!closureVar->IsLocalVar()) { return nullptr; } auto closure = StaticCast(closureVar)->GetExpr(); if (closure->GetExprKind() != ExprKind::LAMBDA) { return nullptr; } return CheckIfLambdaReturnConst(*StaticCast(closure)); } Ptr ArrayLambdaOpt::CheckIfLambdaReturnConst(const Lambda& lambda) const { auto ret = lambda.GetReturnValue(); if (!ret) { return nullptr; } CJC_ASSERT(ret->GetExpr()->GetExprKind() == ExprKind::ALLOCATE); if (auto users = ret->GetUsers(); users.size() == 1) { if (auto store = users[0]; store->GetExprKind() == ExprKind::STORE) { auto retVal = StaticCast(store)->GetValue(); if (!retVal->IsLocalVar()) { return nullptr; } auto constant = StaticCast(retVal)->GetExpr(); if (constant->GetExprKind() != ExprKind::CONSTANT) { return nullptr; } std::unordered_set validExprs{ret->GetExpr(), store, constant}; auto blocksInLambda = lambda.GetBody()->GetBlocks(); if (blocksInLambda.size() > 1) { return nullptr; } for (auto e : blocksInLambda[0]->GetExpressions()) { if (e->IsDebug() || e->IsTerminator()) { continue; } if (validExprs.find(e) == validExprs.end()) { return nullptr; } } return StaticCast(constant); } } return nullptr; } void ArrayLambdaOpt::RewriteArrayInitFunc(Apply& apply, const Ptr& constant) { auto& loc = apply.GetDebugLocation(); auto rawArray = apply.GetArgs()[0]; auto size = StaticCast(rawArray)->GetExpr()->GetOperand(0); auto parent = apply.GetParentBlock(); auto initVal = builder.CreateExpression(constant->GetDebugLocation(), constant->GetResult()->GetType(), StaticCast(constant->GetValue()), parent); initVal->SetDebugLocation(constant->GetDebugLocation()); auto newExpr = builder.CreateExpression( loc, builder.GetUnitTy(), rawArray, size, initVal->GetResult(), parent); initVal->MoveBefore(&apply); apply.ReplaceWith(*newExpr); newExpr->GetResult()->ReplaceWith(*rawArray, parent->GetParentBlockGroup()); } Ptr ArrayLambdaOpt::CheckCanRewriteZeroValue(const Ptr& expr) const { if (expr->GetExprKind() != ExprKind::RAW_ARRAY_INIT_BY_VALUE) { return nullptr; } auto init = StaticCast(expr); if (!init->GetInitValue()->IsLocalVar()) { return nullptr; } auto initVal = StaticCast(init->GetInitValue()); auto initExpr = initVal->GetExpr(); if (initExpr->GetExprKind() != ExprKind::INTRINSIC) { return nullptr; } auto intrinsic = StaticCast(initExpr); if (intrinsic->GetIntrinsicKind() != IntrinsicKind::OBJECT_ZERO_VALUE) { return nullptr; } return intrinsic; } void ArrayLambdaOpt::RewriteZeroValue(const Ptr& init, const Ptr& zeroVal) const { CJC_ASSERT(init->GetResult()->GetUsers().empty()); init->RemoveSelfFromBlock(); auto users = zeroVal->GetResult()->GetUsers(); if (users.empty()) { zeroVal->RemoveSelfFromBlock(); } else if (users.size() == 1 && users[0]->GetExprKind() == ExprKind::DEBUGEXPR) { users[0]->RemoveSelfFromBlock(); zeroVal->RemoveSelfFromBlock(); } } cangjie_compiler-1.0.7/src/CHIR/Transformation/ArrayListConstStartOpt.cpp000066400000000000000000000171331510705540100264640ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/ArrayListConstStartOpt.h" #include "cangjie/CHIR/Analysis/Analysis.h" #include "cangjie/CHIR/Analysis/ConstAnalysis.h" #include "cangjie/CHIR/Utils.h" namespace Cangjie::CHIR { static const std::vector ARRAY_FUNC_INLINE_WHITE_LIST = { FuncInfo("getUnchecked", "Array", {NOT_CARE}, ANY_TYPE, "std.core"), FuncInfo("setUnchecked", "Array", {NOT_CARE}, ANY_TYPE, "std.core"), }; static const std::vector ARRAYLIST_FUNC_LIST = { FuncInfo("[]", "ArrayList", {NOT_CARE}, ANY_TYPE, "std.collection"), FuncInfo("==", "ArrayList", {NOT_CARE}, ANY_TYPE, "std.collection"), }; static const std::vector ARRAYLIST_ITERATOR_FUNC_LIST = { FuncInfo("next", "ArrayListIterator", {NOT_CARE}, ANY_TYPE, "std.collection"), }; namespace { bool InWhiteList(const Func& func, const std::vector& whiteList) { for (auto element : whiteList) { if (IsExpectedFunction(func, element)) { return true; } } return false; } } // namespace const OptEffectCHIRMap& ArrayListConstStartOpt::GetEffectMap() const { return effectMap; } bool ArrayListConstStartOpt::CheckNeedRewrite(const Apply& apply) const { if (!apply.GetCallee()->IsFuncWithBody()) { return false; } auto callee = DynamicCast(apply.GetCallee()); CJC_NULLPTR_CHECK(callee); return InWhiteList(*callee, ARRAY_FUNC_INLINE_WHITE_LIST); } bool ArrayListConstStartOpt::IsStartAddIndexExpression(const Field& field, bool isIteratorFunc) const { // After inline, CHIR will be like: // %0: Class-_CNac9ArrayListIlE& // ... // %39: Struct-_CNat5ArrayIlE& =GetElementRef(%0, 0) // %40: Struct-_CNat5ArrayIlE = Load(%39) // ... // %58: Int64 = Field(40%, 1) // %59: Int64 = Add(%58, %1) //%58 is start and %1 is index // check the Field expression is or not the expected: // 1.returnTy is TYPE_INT64 auto resultTy = field.GetResult()->GetType()->GetTypeKind(); if (resultTy != Type::TypeKind::TYPE_INT64) { return false; } // 2.the first index must be 1 cause the index of start in struct Array is 1 if (field.GetPath().at(0) != 1) { return false; } // 3.check the Field expr operand is the array member of class arrayList // e.g. get %40 experssion and check ExprKind auto expr = StaticCast(field.GetBase())->GetExpr(); if (expr->GetExprKind() != ExprKind::LOAD) { return false; } // e.g. get %39 experssion and check ExprKind expr = StaticCast(expr->GetOperand(0))->GetExpr(); if (expr->GetExprKind() != ExprKind::GET_ELEMENT_REF) { return false; } auto getElementRef = StaticCast(expr); // the index of member Array myData of Class ArrayList is 0 if (getElementRef->GetPath().at(0) != 0) { return false; } auto location = getElementRef->GetLocation(); if (!isIteratorFunc && location->IsParameter()) { auto param = StaticCast(location); // the index 0 or 1 parameter of ArrayList Func is class ArrayList if (param == field.GetTopLevelFunc()->GetParam(0) || param == field.GetTopLevelFunc()->GetParam(1)) { return true; } } // ArrayListIterator func need check more expression: // After inline,ArrayListIterator func CHIR will be like: // %0: Class-_CNac17ArrayListIteratorIlE& // ... // %27: Class-_CNac9ArrayListIlE&& = GetElementRef(%0, 1) // %28: Class-_CNac9ArrayListIlE& = Load(%27) // %29: Struct-_CNat5ArrayIlE& = GetElementRef(%28, 0) // %30: Struct-_CNat5Array0IlE = Load(%29) // ... // %58: Int64 = Field(30%, 1) // %59: Int64 = Add(%58, %1) //%58 is start and %1 is index if (isIteratorFunc) { expr = StaticCast(location)->GetExpr(); if (expr->GetExprKind() != ExprKind::LOAD) { return false; } expr = StaticCast(expr->GetOperand(0))->GetExpr(); if (expr->GetExprKind() != ExprKind::GET_ELEMENT_REF) { return false; } getElementRef = StaticCast(expr); // the index of member ArrayList myData of Class ArrayListIterator is 1 if (getElementRef->GetPath().at(0) != 1) { return false; } location = getElementRef->GetLocation(); if (!location->IsParameter()) { return false; } // the index 0 or 1 parameter of ArrayListIterator Func is class ArrayListIterator return StaticCast(location) == field.GetTopLevelFunc()->GetParam(0) || StaticCast(location) == field.GetTopLevelFunc()->GetParam(1); } return false; } void ArrayListConstStartOpt::RewriteStartWithConstZero(Expression& oldExpr) const { auto oldExprResult = oldExpr.GetResult(); auto oldExprParent = oldExpr.GetParentBlock(); Ptr literalValueZero = builder.CreateLiteralValue(builder.GetInt64Ty(), 0UL); auto newExpr = builder.CreateExpression(oldExprResult->GetType(), literalValueZero, oldExprParent); newExpr->SetDebugLocation(oldExpr.GetDebugLocation()); oldExpr.ReplaceWith(*newExpr); oldExprResult->ReplaceWith(*newExpr->GetResult(), newExpr->GetParentBlockGroup()); if (opts.chirDebugOptimizer) { std::string message = "[ArrayListConstStartOpt] The " + ExprKindMgr::Instance()->GetKindName(static_cast(oldExpr.GetExprKind())) + ToPosInfo(oldExpr.GetDebugLocation()) + " has been rewrited to a constant\n"; std::cout << message; } } void ArrayListConstStartOpt::RunOnPackage(const Ptr& package) { for (auto func : package->GetGlobalFuncs()) { bool isArrayListIteratorFunc = InWhiteList(*func, ARRAYLIST_ITERATOR_FUNC_LIST); // only inline array func into arrayList and arrayListIterator func if (!isArrayListIteratorFunc && !InWhiteList(*func, ARRAYLIST_FUNC_LIST)) { continue; } auto postVisit = [this](Expression& e) { if (e.GetExprKind() != ExprKind::APPLY) { return VisitResult::CONTINUE; } auto& apply = StaticCast(e); if (CheckNeedRewrite(apply)) { pass.DoFunctionInline(apply, optPassName); MergeEffectMap(pass.GetEffectMap(), effectMap); } return VisitResult::CONTINUE; }; Visitor::Visit( *func, [](Expression&) { return VisitResult::CONTINUE; }, postVisit); // remove start opt auto removeStartVisit = [this, isArrayListIteratorFunc](Expression& e) { if (e.GetExprKind() != ExprKind::FIELD) { return VisitResult::CONTINUE; } auto& field = StaticCast(e); // find the Field expression related to the member start of myData in ArrayList if (IsStartAddIndexExpression(field, isArrayListIteratorFunc)) { // change this Field expression to Constant(0) RewriteStartWithConstZero(e); } return VisitResult::CONTINUE; }; Visitor::Visit( *func, [](Expression&) { return VisitResult::CONTINUE; }, removeStartVisit); } } } // namespace Cangjie::CHIR2 cangjie_compiler-1.0.7/src/CHIR/Transformation/BlockGroupCopyHelper.cpp000066400000000000000000000305751510705540100261110ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file instantiate functions for CHIR pass Function Inline and Devirtualization */ #include "cangjie/CHIR/Transformation/BlockGroupCopyHelper.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Visitor/Visitor.h" #include "cangjie/CHIR/Type/ExtendDef.h" #include "cangjie/CHIR/Type/PrivateTypeConverter.h" namespace Cangjie::CHIR { std::pair BlockGroupCopyHelper::CloneBlockGroup( const BlockGroup& other, Func& parentFunc) { auto newGroup = other.Clone(builder, parentFunc); InstBlockGroup(newGroup); // including local var and param std::unordered_map valueMap; std::unordered_set newDebugs; CollectValueMap(other, *newGroup, valueMap, newDebugs); ReplaceExprOperands(*newGroup, valueMap); for (auto dbg : newDebugs) { dbg->RemoveSelfFromBlock(); } LocalVar* newBlockGroupRetValue = nullptr; LocalVar* oldFuncRetValue = nullptr; if (auto func = other.GetOwnerFunc()) { oldFuncRetValue = func->GetReturnValue(); } else if (auto lambda = DynamicCast(other.GetOwnerExpression())) { oldFuncRetValue = lambda->GetReturnValue(); } // some functions don't have return value, such as `init` if (oldFuncRetValue != nullptr) { CJC_ASSERT(oldFuncRetValue->IsLocalVar()); auto it = valueMap.find(oldFuncRetValue); CJC_ASSERT(it != valueMap.end()); newBlockGroupRetValue = StaticCast(it->second); } return {newGroup, newBlockGroupRetValue}; } void BlockGroupCopyHelper::SubstituteValue(Ptr block, std::unordered_map& valueMap) { ReplaceExprOperands(*block, valueMap); } void BlockGroupCopyHelper::CollectValueMap(const Lambda& oldLambda, const Lambda& newLambda, std::unordered_map& valueMap, std::unordered_set& newDebugs) { auto oldParams = oldLambda.GetParams(); auto newParams = newLambda.GetParams(); CJC_ASSERT(oldParams.size() == newParams.size()); for (size_t i = 0; i < oldParams.size(); ++i) { valueMap.emplace(oldParams[i], newParams[i]); } CollectValueMap(*oldLambda.GetBody(), *newLambda.GetBody(), valueMap, newDebugs); } void BlockGroupCopyHelper::CollectValueMap(const Block& oldBlk, const Block& newBlk, std::unordered_map& valueMap, std::unordered_set& newDebugs) { auto oldExprs = oldBlk.GetExpressions(); auto newExprs = newBlk.GetExpressions(); CJC_ASSERT(oldExprs.size() == newExprs.size()); for (size_t i = 0; i < oldExprs.size(); ++i) { auto oldExpr = oldExprs[i]; auto newExpr = newExprs[i]; CJC_ASSERT(oldExpr->GetExprKind() == newExpr->GetExprKind()); if (oldExpr->GetResult() == nullptr) { CJC_ASSERT(newExpr->GetResult() == nullptr); continue; } CJC_ASSERT(newExpr->GetResult() != nullptr); valueMap.emplace(oldExpr->GetResult(), newExpr->GetResult()); if (oldExpr->GetExprKind() == ExprKind::DEBUGEXPR) { newDebugs.emplace(newExpr); } if (oldExpr->GetExprKind() == ExprKind::LAMBDA) { CollectValueMap(*StaticCast(oldExpr), *StaticCast(newExpr), valueMap, newDebugs); } } } void BlockGroupCopyHelper::CollectValueMap(const BlockGroup& oldBG, const BlockGroup& newBG, std::unordered_map& valueMap, std::unordered_set& newDebugs) { auto oldBlocks = oldBG.GetBlocks(); auto newBlocks = newBG.GetBlocks(); CJC_ASSERT(oldBlocks.size() == newBlocks.size()); for (size_t i = 0; i < oldBlocks.size(); ++i) { CollectValueMap(*oldBlocks[i], *newBlocks[i], valueMap, newDebugs); } } void BlockGroupCopyHelper::ReplaceExprOperands(const Block& block, const std::unordered_map& valueMap) { for (auto expr : block.GetExpressions()) { // note: a hack here, remove later if (expr->GetExprKind() == ExprKind::DEBUGEXPR) { continue; } for (auto op : expr->GetOperands()) { auto it = valueMap.find(op); if (it == valueMap.end()) { continue; } expr->ReplaceOperand(op, it->second); } if (expr->GetExprKind() == ExprKind::LAMBDA) { auto lambda = StaticCast(expr); ReplaceExprOperands(*lambda->GetBody(), valueMap); } } } void BlockGroupCopyHelper::ReplaceExprOperands( const BlockGroup& bg, const std::unordered_map& valueMap) { for (auto block : bg.GetBlocks()) { ReplaceExprOperands(*block, valueMap); } } void BlockGroupCopyHelper::GetInstMapFromApply(const Apply& apply) { if (apply.GetCallee()->IsLocalVar()) { auto lambda = DynamicCast(StaticCast(apply.GetCallee())->GetExpr()); CJC_NULLPTR_CHECK(lambda); // get inst map from function size_t index = 0; for (auto& genericType : lambda->GetGenericTypeParams()) { instMap.emplace(genericType, apply.GetInstantiatedTypeArgs()[index]); ++index; } thisType = builder.GetType(); } else { auto func = VirtualCast(apply.GetCallee()); if (auto customDef = func->GetParentCustomTypeDef(); customDef && customDef->IsGenericDef()) { // 1. get customType where function in. auto instParentCustomType = apply.GetInstParentCustomTyOfCallee(builder); if (instParentCustomType == nullptr) { instParentCustomType = customDef->IsExtend() ? StaticCast(customDef)->GetExtendedType() : customDef->GetType(); } instParentCustomType = instParentCustomType->StripAllRefs(); // 2. get inst map from custom type if (auto exDef = DynamicCast(customDef)) { auto newMap = exDef->GetExtendedType()->CalculateGenericTyMapping(*instParentCustomType); CJC_ASSERT(newMap.first); instMap.merge(newMap.second); } else { instMap = GetInstMapFromCurDefToCurType(StaticCast(*instParentCustomType)); } } // 3. get inst map from function size_t index = 0; for (auto& genericType : func->GetGenericTypeParams()) { instMap.emplace(genericType, apply.GetInstantiatedTypeArgs()[index]); ++index; } thisType = apply.GetThisType(); } } void BlockGroupCopyHelper::InstBlockGroup(Ptr group) { GenericTypeConvertor gConverter(instMap, builder); ConvertTypeFunc convertFunc = [&gConverter, this](Type& type) { auto res = ReplaceThisTypeToConcreteType(type, *thisType, builder); if (res != &type) { return res; } else { return gConverter.ConvertToInstantiatedType(type); } }; PrivateTypeConverterNoInvokeOriginal converter(convertFunc, builder); auto postVisit = [&converter](Expression& e) { converter.VisitExpr(e); return VisitResult::CONTINUE; }; Visitor::Visit(*group, [](Expression&) { return VisitResult::CONTINUE; }, postVisit); } void FixCastProblemAfterInst(Ptr group, CHIRBuilder& builder) { auto postVisit = [&builder](Expression& e) { if (e.GetExprKind() == ExprKind::LAMBDA) { auto lambda = StaticCast(&e); FixCastProblemAfterInst(lambda->GetBody(), builder); } if (e.GetExprKind() == ExprKind::INSTANCEOF) { // fix instanceOf problem auto instance = StaticCast(&e); auto objType = instance->GetObject()->GetType(); if (objType->IsGenericRelated() || (objType->IsRef() && StaticCast(objType)->GetBaseType()->IsClass()) || instance->GetType()->IsGenericRelated()) { return VisitResult::CONTINUE; } if (objType->IsEqualOrSubTypeOf(*instance->GetType(), builder)) { auto trueExpr = builder.CreateConstantExpression(builder.GetBoolTy(), e.GetParentBlock(), true); e.ReplaceWith(*trueExpr); } else { auto falseExpr = builder.CreateConstantExpression(builder.GetBoolTy(), e.GetParentBlock(), false); e.ReplaceWith(*falseExpr); } } if (e.GetExprKind() == ExprKind::TRANSFORM_TO_CONCRETE) { // change transformToConcrete to box/unbox/typecast auto& cast = StaticCast(e); if (!cast.GetSourceTy()->IsGenericRelated()) { auto newCast = TypeCastOrBoxIfNeeded( *cast.GetSourceValue(), *cast.GetTargetTy(), builder, *e.GetParentBlock(), e.GetDebugLocation()); if (newCast == cast.GetSourceValue()) { for (auto user : e.GetResult()->GetUsers()) { user->ReplaceOperand(e.GetResult(), cast.GetSourceValue()); } e.RemoveSelfFromBlock(); return VisitResult::CONTINUE; } for (auto user : e.GetResult()->GetUsers()) { user->ReplaceOperand(e.GetResult(), newCast); } StaticCast(newCast)->GetExpr()->MoveBefore(&e); e.RemoveSelfFromBlock(); } } if (e.GetExprKind() == ExprKind::TRANSFORM_TO_GENERIC) { // change TransformToGeneric to box/unbox/typecast auto cast = StaticCast(&e); if (!cast->GetTargetTy()->IsGenericRelated()) { auto newCast = TypeCastOrBoxIfNeeded(*cast->GetSourceValue(), *cast->GetTargetTy(), builder, *e.GetParentBlock(), e.GetDebugLocation()); if (newCast == cast->GetSourceValue()) { for (auto user : e.GetResult()->GetUsers()) { user->ReplaceOperand(e.GetResult(), cast->GetSourceValue()); } e.RemoveSelfFromBlock(); return VisitResult::CONTINUE; } for (auto user : e.GetResult()->GetUsers()) { user->ReplaceOperand(e.GetResult(), newCast); } StaticCast(newCast)->GetExpr()->MoveBefore(&e); e.RemoveSelfFromBlock(); } } if (e.GetExprKind() == ExprKind::TYPECAST) { /* change typecast to unbox/box * func foo(a: T) { * let b: CA = TypeCast(a, CA) // typecast to CA * } * * when foo inline to other function and inst to int64 type, the typecast would change to: * (a: Int64) * let b: CA = Box(a, CA) */ auto cast = StaticCast(&e); if (cast->GetSourceValue()->GetType()->IsGenericRelated()) { return VisitResult::CONTINUE; } auto newCastRes = TypeCastOrBoxIfNeeded(*cast->GetSourceValue(), *e.GetResult()->GetType(), builder, *e.GetParentBlock(), e.GetDebugLocation()); if (newCastRes == cast->GetSourceValue()) { return VisitResult::CONTINUE; } auto newCast = StaticCast(newCastRes); if (newCast->GetExpr()->GetExprKind() == ExprKind::TYPECAST) { newCast->GetExpr()->RemoveSelfFromBlock(); return VisitResult::CONTINUE; } for (auto user : e.GetResult()->GetUsers()) { user->ReplaceOperand(e.GetResult(), newCast); } newCast->GetExpr()->MoveBefore(&e); e.RemoveSelfFromBlock(); } return VisitResult::CONTINUE; }; Visitor::Visit(*group, [](Expression&) { return VisitResult::CONTINUE; }, postVisit); } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/Transformation/BoxRecursionValueType.cpp000066400000000000000000000405331510705540100263230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/BoxRecursionValueType.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Visitor/Visitor.h" using namespace Cangjie; using namespace Cangjie::CHIR; namespace { bool IsExpectedValueType(const Type& expectedValueType, Type& curType, CHIRBuilder& builder, bool doCheck, std::unordered_set& visited) { if (doCheck && &curType == &expectedValueType) { return true; } auto [_, res] = visited.emplace(&curType); if (!res) { return false; } if (curType.IsEnum()) { auto& enumType = StaticCast(curType); for (auto& ctorInfo : enumType.GetConstructorInfos(builder)) { for (auto ty : ctorInfo.funcType->GetParamTypes()) { if (IsExpectedValueType(expectedValueType, *ty, builder, true, visited)) { return true; } } } } else if (curType.IsStruct()) { auto& structType = StaticCast(curType); for (auto ty : structType.GetInstantiatedMemberTys(builder)) { if (IsExpectedValueType(expectedValueType, *ty, builder, true, visited)) { return true; } } } else if (curType.IsTuple()) { auto& tupleType = StaticCast(curType); for (auto ty : tupleType.GetElementTypes()) { if (IsExpectedValueType(expectedValueType, *ty, builder, true, visited)) { return true; } } } return false; } bool IsRecursionType(Type& specifiedType, CHIRBuilder& builder) { std::unordered_set visited; return IsExpectedValueType(specifiedType, specifiedType, builder, false, visited); } Type* GetTargetType(Type& baseType, const std::vector& path, CHIRBuilder& builder) { auto subType = &baseType; for (auto p : path) { subType = GetFieldOfType(*subType, p, builder); CJC_NULLPTR_CHECK(subType); } return subType; } bool LeftIsBoxTypeOfRight(const Type& left, const Type& right) { std::function leftIsGenericOrEqualToRight = [&leftIsGenericOrEqualToRight](const Type& left, const Type& right) { auto leftType = left.StripAllRefs(); auto rightType = right.StripAllRefs(); if (leftType == rightType) { return true; } if (leftType->IsGeneric()) { return true; } if (leftType->GetTypeKind() != rightType->GetTypeKind()) { return false; } auto leftTypeArgs = leftType->GetTypeArgs(); auto rightTypeArgs = rightType->GetTypeArgs(); if (leftTypeArgs.size() != rightTypeArgs.size()) { return false; } if ((leftType->IsClass() || leftType->IsStruct() || leftType->IsEnum()) && StaticCast(leftType)->GetCustomTypeDef() != StaticCast(rightType)->GetCustomTypeDef()) { return false; } for (size_t i = 0; i < leftTypeArgs.size(); ++i) { if (!leftIsGenericOrEqualToRight(*leftTypeArgs[i], *rightTypeArgs[i])) { return false; } } return true; }; auto leftType = left.StripAllRefs(); if (!leftType->IsBox()) { return false; } return leftIsGenericOrEqualToRight(*StaticCast(leftType)->GetBaseType(), right); } bool StoreElementRefNeedBox(const StoreElementRef& ser, CHIRBuilder& builder) { auto targetType = GetTargetType(*ser.GetLocation()->GetType(), ser.GetPath(), builder); auto srcType = ser.GetValue()->GetType(); if (LeftIsBoxTypeOfRight(*targetType, *srcType)) { return true; } else { return false; } } bool GetElementRefNeedUnBox(const GetElementRef& ger, CHIRBuilder& builder) { // If the base of the GetElementRef expression is enum, it must be the index on which we want to get enum. // Therefore, we do not need to box the targetType. auto baseType = ger.GetLocation()->GetType()->StripAllRefs(); if (baseType->IsEnum()) { return false; } auto srcType = GetTargetType(*baseType, ger.GetPath(), builder); auto targetType = ger.GetResult()->GetType()->StripAllRefs(); if (LeftIsBoxTypeOfRight(*srcType, *targetType)) { auto users = ger.GetResult()->GetUsers(); CJC_ASSERT(users.size() == 1 && users[0]->GetExprKind() == CHIR::ExprKind::LOAD); return true; } else { return false; } } std::pair> TupleNeedBox(const Tuple& tuple) { std::pair> retVal = {false, {}}; auto tupleRes = tuple.GetResult(); if (!tupleRes->GetType()->IsEnum()) { return retVal; } auto operands = tuple.GetOperands(); auto indexExpr = StaticCast(StaticCast(operands[0])->GetExpr()); size_t index = 0; if (indexExpr->IsBoolLit()) { index = indexExpr->GetBoolLitVal() ? 1 : 0; } else { index = static_cast(indexExpr->GetUnsignedIntLitVal()); } operands.erase(operands.begin()); auto enumDef = StaticCast(tupleRes->GetType())->GetEnumDef(); auto ctor = enumDef->GetCtor(index); auto paramTypes = ctor.funcType->GetParamTypes(); CJC_ASSERT(operands.size() == paramTypes.size()); for (size_t i = 0; i < paramTypes.size(); ++i) { auto paramType = paramTypes[i]; auto targetType = operands[i]->GetType(); if (LeftIsBoxTypeOfRight(*paramType, *targetType)) { retVal.first = true; retVal.second.emplace_back(i + 1); } } return retVal; } std::pair>, Field*> FieldAndTypeCastNeedUnBox( Field& field, const std::vector>>& collected, CHIRBuilder& builder) { /** enum E { * A | Box& * } * %0: Enum-E = ... * %1: Tuple = TypeCast(%0) * %2: Struct-S = Field(%1, 1) * * should be transform to: * %0: Enum-E = ... * %1: Tuple&> = TypeCast(%0) * %2: Box& = Field(%1, 1) * %3: Struct-S = UnBox(%2) */ auto index = field.GetPath(); if (index.size() != 1) { return {{nullptr, {}}, nullptr}; } if (index[0] == 0) { return {{nullptr, {}}, nullptr}; } auto base = field.GetBase(); if (!base->IsLocalVar()) { return {{nullptr, {}}, nullptr}; } auto baseExpr = StaticCast(base)->GetExpr(); if (baseExpr->GetExprKind() != CHIR::ExprKind::TYPECAST) { return {{nullptr, {}}, nullptr}; } auto typecast = StaticCast(baseExpr); auto srcType = typecast->GetSourceTy(); if (!srcType->IsEnum()) { return {{nullptr, {}}, nullptr}; } auto targetType = typecast->GetTargetTy(); if (!targetType->IsTuple()) { return {{nullptr, {}}, nullptr}; } for (auto& cast : collected) { if (cast.first != typecast) { continue; } for (auto i : cast.second) { if (i == index[0]) { return {{nullptr, {}}, &field}; } } return {{nullptr, {}}, nullptr}; } auto enumDef = StaticCast(srcType)->GetEnumDef(); auto tupleArgs = StaticCast(targetType)->GetElementTypes(); tupleArgs.erase(tupleArgs.begin()); std::vector path; Field* fieldRes = nullptr; size_t offset = 1; for (auto& ctor : enumDef->GetCtors()) { auto paramTypes = ctor.funcType->GetParamTypes(); if (paramTypes.size() != tupleArgs.size()) { continue; } for (size_t i = 0; i < paramTypes.size(); ++i) { auto paramType = paramTypes[i]; auto argType = tupleArgs[i]; if (LeftIsBoxTypeOfRight(*paramType, *argType)) { path.emplace_back(i + offset); if (i + offset == index[0]) { fieldRes = &field; } } else if (!paramType->IsEqualOrSubTypeOf(*argType, builder)) { path.clear(); fieldRes = nullptr; break; } } if (!path.empty()) { break; } } if (path.empty()) { return {{nullptr, {}}, nullptr}; } else { return {{typecast, path}, fieldRes}; } } bool FieldNeedUnBox(Field& field, CHIRBuilder& builder) { auto baseType = field.GetBase()->GetType(); if (baseType->IsEnum()) { return false; } auto targetType = GetTargetType(*baseType, field.GetPath(), builder); auto srcType = field.GetResult()->GetType(); if (LeftIsBoxTypeOfRight(*targetType, *srcType)) { return true; } else { return false; } } void InsertBoxBeforeStoreElementRef(StoreElementRef& ser, CHIRBuilder& builder) { auto parent = ser.GetParentBlock(); auto srcValue = ser.GetValue(); auto boxType = builder.GetType(builder.GetType(srcValue->GetType())); auto boxExpr = builder.CreateExpression(boxType, srcValue, parent); boxExpr->MoveBefore(&ser); ser.ReplaceOperand(srcValue, boxExpr->GetResult()); } void InsertUnBoxAfterGetElementRef(GetElementRef& ger, CHIRBuilder& builder) { auto parent = ger.GetParentBlock(); auto location = ger.GetLocation(); auto gerLoc = ger.GetDebugLocation(); auto& path = ger.GetPath(); auto targetType = ger.GetResult()->GetType()->StripAllRefs(); auto boxType = builder.GetType(builder.GetType(targetType)); auto gerResType = builder.GetType(boxType); auto newGer = builder.CreateExpression(gerLoc, gerResType, location, path, parent); newGer->MoveBefore(&ger); auto load = ger.GetResult()->GetUsers()[0]; auto loadLoc = load->GetDebugLocation(); auto newLoad = builder.CreateExpression(loadLoc, boxType, newGer->GetResult(), parent); newLoad->MoveBefore(&ger); auto unbox = builder.CreateExpression(targetType, newLoad->GetResult(), parent); load->ReplaceWith(*unbox); ger.RemoveSelfFromBlock(); } void InsertBoxBeforeTuple(Tuple& tuple, const std::vector& path, CHIRBuilder& builder) { auto parent = tuple.GetParentBlock(); for (auto i : path) { auto srcValue = tuple.GetOperand(i); auto boxType = builder.GetType(builder.GetType(srcValue->GetType())); auto boxExpr = builder.CreateExpression(boxType, srcValue, parent); boxExpr->MoveBefore(&tuple); tuple.ReplaceOperand(srcValue, boxExpr->GetResult()); } } void TypeCastToBoxType(TypeCast& typecast, const std::vector& path, CHIRBuilder& builder) { auto targetType = StaticCast(typecast.GetTargetTy()); auto eleTypes = targetType->GetElementTypes(); for (auto i : path) { auto boxType = builder.GetType(builder.GetType(eleTypes[i])); CJC_ASSERT(i < eleTypes.size()); eleTypes[i] = boxType; } auto newTargetType = builder.GetType(eleTypes); auto parent = typecast.GetParentBlock(); auto loc = typecast.GetDebugLocation(); auto newTypeCast = builder.CreateExpression(loc, newTargetType, typecast.GetSourceValue(), parent); newTypeCast->MoveBefore(&typecast); auto newResult = newTypeCast->GetResult(); newResult->Set(typecast.GetResult()->Get()); typecast.ReplaceWith(*newTypeCast); } void InsertUnBoxAfterField(Field& field, CHIRBuilder& builder) { auto resType = field.GetResult()->GetType(); auto boxType = builder.GetType(builder.GetType(resType)); auto parent = field.GetParentBlock(); auto loc = field.GetDebugLocation(); auto newField = builder.CreateExpression(loc, boxType, field.GetBase(), field.GetPath(), parent); newField->MoveBefore(&field); auto unbox = builder.CreateExpression(resType, newField->GetResult(), parent); field.ReplaceWith(*unbox); } } BoxRecursionValueType::BoxRecursionValueType(Package& pkg, CHIRBuilder& builder) : pkg(pkg), builder(builder) { } void BoxRecursionValueType::CreateBoxTypeForRecursionEnum() { for (auto def : pkg.GetAllEnumDef()) { auto allCtors = def->GetCtors(); for (auto& ctor : allCtors) { auto paramTypes = ctor.funcType->GetParamTypes(); bool hasRecursionType = false; for (size_t i = 0; i < paramTypes.size(); ++i) { if (IsRecursionType(*paramTypes[i], builder)) { hasRecursionType = true; paramTypes[i] = builder.GetType(builder.GetType(paramTypes[i])); } } if (hasRecursionType) { auto returnType = ctor.funcType->GetReturnType(); ctor.funcType = builder.GetType(paramTypes, returnType); } } def->SetCtors(allCtors); } } void BoxRecursionValueType::CreateBoxTypeForRecursionStruct() { for (auto def : pkg.GetAllStructDef()) { auto memberVars = def->GetDirectInstanceVars(); for (auto& memberVar : memberVars) { if (IsRecursionType(*memberVar.type, builder)) { memberVar.type = builder.GetType(builder.GetType(memberVar.type)); } } def->SetDirectInstanceVars(memberVars); } } void BoxRecursionValueType::CreateBoxTypeForRecursionValueType() { CreateBoxTypeForRecursionEnum(); CreateBoxTypeForRecursionStruct(); for (auto type : builder.GetAllCustomTypes()) { type->ResetAllInstantiatedType(); } InsertBoxAndUnboxExprForRecursionValueType(); } void BoxRecursionValueType::InsertBoxAndUnboxExprForRecursionValueType() { std::vector storeElementRefs; std::vector getElementRefs; std::vector>> tuples; std::vector>> typecasts; std::vector fields; auto visitor = [&storeElementRefs, &getElementRefs, &tuples, &typecasts, &fields, this](Expression& e) { if (e.GetExprKind() == CHIR::ExprKind::STORE_ELEMENT_REF) { auto& ser = StaticCast(e); if (StoreElementRefNeedBox(ser, builder)) { storeElementRefs.emplace_back(&ser); } } else if (e.GetExprKind() == CHIR::ExprKind::GET_ELEMENT_REF) { auto& ger = StaticCast(e); if (GetElementRefNeedUnBox(ger, builder)) { getElementRefs.emplace_back(&ger); } } else if (e.GetExprKind() == CHIR::ExprKind::TUPLE) { auto& tuple = StaticCast(e); if (auto res = TupleNeedBox(tuple); res.first) { tuples.emplace_back(&tuple, res.second); } } else if (e.GetExprKind() == CHIR::ExprKind::FIELD) { auto& field = StaticCast(e); auto res = FieldAndTypeCastNeedUnBox(field, typecasts, builder); if (res.first.first != nullptr) { typecasts.emplace_back(res.first); } if (res.second != nullptr) { fields.emplace_back(res.second); } if (FieldNeedUnBox(field, builder)) { fields.emplace_back(&field); } } return VisitResult::CONTINUE; }; for (auto func : pkg.GetGlobalFuncs()) { Visitor::Visit(*func, visitor); } for (auto e : storeElementRefs) { InsertBoxBeforeStoreElementRef(*e, builder); } for (auto e : getElementRefs) { InsertUnBoxAfterGetElementRef(*e, builder); } for (auto& e : tuples) { InsertBoxBeforeTuple(*e.first, e.second, builder); } for (auto& e : typecasts) { TypeCastToBoxType(*e.first, e.second, builder); } for (auto e : fields) { InsertUnBoxAfterField(*e, builder); } } cangjie_compiler-1.0.7/src/CHIR/Transformation/CMakeLists.txt000066400000000000000000000005501510705540100240710ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB TRANSFORMATION_SRC *.cpp) set(CHIR_OPT_SRC ${CHIR_OPT_SRC} ${TRANSFORMATION_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/CHIR/Transformation/ClosureConversion.cpp000066400000000000000000004003111510705540100255160ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/ClosureConversion.h" #include "cangjie/CHIR/Annotation.h" #include "cangjie/CHIR/GenerateVTable/VTableGenerator.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Transformation/FunctionInline.h" #include "cangjie/CHIR/Transformation/LambdaInline.h" #include "cangjie/CHIR/Type/PrivateTypeConverter.h" #include "cangjie/CHIR/Visitor/Visitor.h" #include "cangjie/Mangle/CHIRManglingUtils.h" #include "cangjie/Mangle/CHIRTypeManglingUtils.h" #include "cangjie/Option/Option.h" using namespace Cangjie; using namespace Cangjie::CHIR; namespace { const std::string GENERIC_THIS_SRC_NAME = "This"; bool IsCalleeOfApply(const Expression& user, const Value& op) { if (auto apply = DynamicCast(&user)) { return apply->GetCallee() == &op; } else if (auto applyWithException = DynamicCast(&user)) { return applyWithException->GetCallee() == &op; } else if (auto invoke = DynamicCast(&user)) { return invoke->GetObject() == &op; } else if (auto invokeWithException = DynamicCast(&user)) { return invokeWithException->GetObject() == &op; } return false; } bool IsMutableVarType(const Value& value) { if (value.IsParameter()) { return false; } auto ref = DynamicCast(value.GetType()); if (ref == nullptr) { return false; } // type is ref, but not ref a class or array, that means this type is a mutable var's type return !ref->GetBaseType()->IsClassOrArray(); } void CollectNestedLambdaExpr( const Lambda& root, std::vector& nestedFuncs, Func* curOutFunc, std::set& ccOutFuncs) { CJC_NULLPTR_CHECK(curOutFunc); auto preVisit = [&nestedFuncs, &curOutFunc, &ccOutFuncs](Expression& e) { if (auto lambdaExpr = DynamicCast(&e); lambdaExpr) { CollectNestedLambdaExpr(*lambdaExpr, nestedFuncs, curOutFunc, ccOutFuncs); nestedFuncs.emplace_back(lambdaExpr); } return VisitResult::CONTINUE; }; VisitFuncBlocksInTopoSort(*root.GetBody(), preVisit); } void GetAllGenericType(Type& type, std::vector& result) { if (type.IsGeneric() && std::find(result.begin(), result.end(), &type) == result.end()) { result.emplace_back(&type); } else { for (auto argTy : type.GetTypeArgs()) { GetAllGenericType(*argTy, result); } } } Ptr GetInstTypeFromOrphanGenericType(Ptr type) { if (!type->IsGeneric()) { return nullptr; } auto gTy = StaticCast(type); if (gTy->GetUpperBounds().size() == 1 && gTy->orphanFlag) { return gTy->GetUpperBounds()[0]; } return nullptr; } Ptr GetOriFuncTypeFromOrphanType(Ptr type, CHIRBuilder& builder) { if (!type->IsFunc()) { return type; } auto funcType = StaticCast(type); std::vector originalArgs; bool isOrphan = false; for (auto argType : funcType->GetParamTypes()) { if (auto oriType = GetInstTypeFromOrphanGenericType(argType); oriType != nullptr) { originalArgs.push_back(oriType); isOrphan = true; continue; } else { originalArgs.push_back(argType); } } auto retType = funcType->GetReturnType(); if (auto oriRetType = GetInstTypeFromOrphanGenericType(funcType->GetReturnType()); oriRetType != nullptr) { isOrphan = true; retType = oriRetType; } if (isOrphan) { funcType = builder.GetType(originalArgs, retType); } return funcType; } void SetAutoEnvImplDefAttr(ClassDef& def) { def.EnableAttr(Attribute::COMPILER_ADD); def.EnableAttr(Attribute::NO_REFLECT_INFO); def.Set(true); def.Set(Linkage::INTERNAL); } void SetAutoEnvBaseDefAttr(ClassDef& def) { SetAutoEnvImplDefAttr(def); def.EnableAttr(Attribute::ABSTRACT); } void SetLiftedLambdaAttr(Func& func, Lambda& lambda) { func.EnableAttr(Attribute::COMPILER_ADD); func.EnableAttr(Attribute::NO_REFLECT_INFO); func.EnableAttr(Attribute::INTERNAL); func.Set(Linkage::INTERNAL); func.SetFuncKind(FuncKind::LAMBDA); if (!lambda.GetGenericTypeParams().empty()) { func.EnableAttr(Attribute::GENERIC); } if (lambda.IsCompileTimeValue()) { func.EnableAttr(Attribute::CONST); } if (auto body = lambda.GetBody(); body && body->TestAttr(Attribute::NO_INLINE)) { func.EnableAttr(Attribute::NO_INLINE); } } void SetMemberMethodAttr(Func& func, bool isConst) { func.EnableAttr(Attribute::COMPILER_ADD); func.EnableAttr(Attribute::NO_REFLECT_INFO); func.EnableAttr(Attribute::INTERNAL); func.EnableAttr(Attribute::PUBLIC); func.Set(Linkage::INTERNAL); if (isConst) { func.EnableAttr(Attribute::CONST); } } bool FuncTypeHasGenericT(const FuncBase& func) { if (func.GetFuncKind() == FuncKind::LAMBDA) { auto funcType = func.GetFuncType(); auto paramTypes = funcType->GetParamTypes(); paramTypes.erase(paramTypes.begin()); for (auto ty : paramTypes) { if (ty->IsGenericRelated()) { return true; } } return funcType->GetReturnType()->IsGenericRelated(); } else { return func.GetFuncType()->IsGenericRelated(); } } bool IsCFuncOrInCFunc(const Lambda& srcFunc) { if (srcFunc.GetFuncType()->IsCFunc()) { return true; } auto blockGroup = srcFunc.GetParentBlockGroup(); CJC_NULLPTR_CHECK(blockGroup); while (blockGroup->GetOwnerFunc() == nullptr) { auto ownedExpr = blockGroup->GetOwnerExpression(); if (auto lambda = DynamicCast(ownedExpr)) { if (lambda->GetFuncType()->IsCFunc()) { return true; } blockGroup = lambda->GetParentBlockGroup(); } else { blockGroup = ownedExpr->GetParentBlockGroup(); } } return false; } bool NeedAddThisType(const Value& srcFunc) { auto localVar = DynamicCast(&srcFunc); // only lambda need `This` if (localVar == nullptr) { return false; } if (IsCFuncOrInCFunc(*StaticCast(localVar->GetExpr()))) { return false; } // only lambda in member method need `This` auto outerDecl = localVar->GetTopLevelFunc()->GetOuterDeclaredOrExtendedDef(); if (outerDecl == nullptr) { return false; } // custom type def must be inheritable, if in extend def, we check extended custom type def if (outerDecl->IsEnum() || outerDecl->IsStruct()) { return false; } return outerDecl->CanBeInherited(); } size_t GetFuncGenericTypeParamNum(const Value& srcFunc) { size_t num = 0; if (auto funcBase = DynamicCast(&srcFunc)) { num = funcBase->GetGenericTypeParams().size(); } else { auto lambda = StaticCast(StaticCast(srcFunc).GetExpr()); num = lambda->GetGenericTypeParams().size(); } return num; } /** `givInstTypes` store all instantiated info, we need to know what CustomTypeDef's generic params are instantiated. * extend def is different with other CustomTypeDefs, let's view some cases * case 1: * class A {} * extend A> {} * if there is a type `A>`, we need to know T1 is Int32, T2 is Bool, T3 is Unit * so return value is expected to be vector{Int32, Bool, Unit} * * case 2: * extend CPointer {} * there isn't generic type param in extend def, so if we meet type `CPointer`, * so return value is expected to be an empty vector * * case 3: * if extended type in extend def is not matched with input type, core dump will come * that means if extend def is `extend CPointer {}`, its extended type is `CPointer`, * but we set `CPointer` as input(`givInstTypes` is {Int32, ...}), ir must be wrong */ std::vector GetInstTypeParamsForCustomTypeDef( const std::vector& givInstTypes, const Value& srcFunc) { auto memberFunc = DynamicCast(&srcFunc); if (memberFunc == nullptr) { memberFunc = GetTopLevelFunc(srcFunc); } CJC_NULLPTR_CHECK(memberFunc); auto parentCustomTypeDef = memberFunc->GetParentCustomTypeDef(); // only member function need to modify instantiated types if (parentCustomTypeDef == nullptr) { return std::vector{}; } if (auto extendDef = DynamicCast(parentCustomTypeDef)) { auto extendedType = extendDef->GetExtendedType(); auto genericTypeArgs = extendedType->GetTypeArgs(); auto instTypeArgs = std::vector(givInstTypes.begin(), givInstTypes.begin() + static_cast(genericTypeArgs.size())); std::unordered_map genericMap; CJC_ASSERT(genericTypeArgs.size() == instTypeArgs.size()); for (size_t i = 0; i < genericTypeArgs.size(); ++i) { auto newMap = genericTypeArgs[i]->CalculateGenericTyMapping(*instTypeArgs[i]); CJC_ASSERT(newMap.first); genericMap.merge(newMap.second); } std::vector result; for (auto ty : extendDef->GetGenericTypeParams()) { result.emplace_back(genericMap.at(ty)); } return result; } else { size_t offset = parentCustomTypeDef->GetGenericTypeParams().size(); return std::vector(givInstTypes.begin(), givInstTypes.begin() + static_cast(offset)); } } void RemoveGetInstantiateValue(std::vector& maybeGetInsValue) { for (auto expr : maybeGetInsValue) { auto getInsValue = DynamicCast(expr); if (getInsValue == nullptr) { continue; } auto op = getInsValue->GetGenericResult(); for (auto user : getInsValue->GetResult()->GetUsers()) { user->ReplaceOperand(getInsValue->GetResult(), op); } expr->RemoveSelfFromBlock(); } } FuncType* GetFuncTypeWithoutThisPtrFromAutoEnvType(ClassType& autoEnvType, CHIRBuilder& builder) { CJC_ASSERT(autoEnvType.IsAutoEnv()); if (autoEnvType.IsAutoEnvBase()) { auto [paramTypes, retType] = GetFuncTypeWithoutThisPtrFromAutoEnvBaseType(autoEnvType); return builder.GetType(paramTypes, retType); } else { return GetFuncTypeWithoutThisPtrFromAutoEnvType(*autoEnvType.GetSuperClassTy(&builder), builder); } } void PrintMutableVarInfo(const CHIR::Position& pos) { auto posMsg = " in the line:" + std::to_string(pos.line) + " and the column:" + std::to_string(pos.column); auto msg = "The mut var" + posMsg + " was boxed"; std::cout << msg << std::endl; } void PrintNestedFuncInfo(const CHIR::Position& pos) { auto posMsg = " in the line:" + std::to_string(pos.line) + " and the column:" + std::to_string(pos.column); auto msg = "The nested func" + posMsg + " was closure converted"; std::cout << msg << std::endl; } ClassType* InstantiateAutoEnvBaseType(ClassDef& autoEnvBaseDef, const FuncType& funcType, CHIRBuilder& builder) { std::vector typeArgs; if (autoEnvBaseDef.TestAttr(Attribute::GENERIC)) { typeArgs = funcType.GetTypeArgs(); } return builder.GetType(&autoEnvBaseDef, typeArgs); } void LiftCustomDefType(CustomTypeDef& def, TypeConverterForCC& converter) { auto postVisit = [&converter](Expression& e) { converter.VisitExpr(e); return VisitResult::CONTINUE; }; for (auto method : def.GetMethods()) { Visitor::Visit( *StaticCast(method), [](Expression&) { return VisitResult::CONTINUE; }, postVisit); converter.VisitValue(*method); } converter.VisitDef(def); } void CreateVirtualFuncInAutoEnvBaseDef(ClassDef& autoEnvBaseDef, const std::string& funcName, const std::vector& paramsAndRetType, CHIRBuilder& builder) { std::vector params; std::vector paramTypes{builder.GetType(autoEnvBaseDef.GetType())}; CJC_ASSERT(!paramsAndRetType.empty()); for (size_t i = 0; i < paramsAndRetType.size() - 1; ++i) { params.emplace_back(AbstractMethodParam{"p" + std::to_string(i), paramsAndRetType[i]}); paramTypes.emplace_back(paramsAndRetType[i]); } auto methodTy = builder.GetType(paramTypes, paramsAndRetType.back()); AttributeInfo attr; attr.SetAttr(Attribute::ABSTRACT, true); attr.SetAttr(Attribute::PUBLIC, true); std::string mangleName; if (funcName == GENERIC_VIRTUAL_FUNC) { mangleName = CHIRMangling::ClosureConversion::GenerateGenericAbstractFuncMangleName(autoEnvBaseDef); } else if (funcName == INST_VIRTUAL_FUNC) { mangleName = CHIRMangling::ClosureConversion::GenerateInstantiatedAbstractFuncMangleName(autoEnvBaseDef); } autoEnvBaseDef.AddAbstractMethod( AbstractMethodInfo{funcName, autoEnvBaseDef.GetIdentifier() + funcName, methodTy, params, attr, AnnoInfo{}, std::vector{}, false, &autoEnvBaseDef}); } std::pair, Type*> CreateFuncTypeWithOrphanT( const std::string& typePrefix, const std::vector& instFuncTypeArgs, const std::unordered_map& replaceTable, CHIRBuilder& builder) { std::vector paramTypes; CJC_ASSERT(!instFuncTypeArgs.empty()); for (size_t i = 0; i < instFuncTypeArgs.size() - 1; ++i) { auto ty = instFuncTypeArgs[i]; auto tyDeref = ty->StripAllRefs(); if (tyDeref->IsGeneric() || tyDeref->IsClass() || tyDeref->IsFunc()) { paramTypes.emplace_back(ReplaceRawGenericArgType(*ty, replaceTable, builder)); continue; } auto tName = "T" + std::to_string(i); auto tMangledName = typePrefix + "_cc_" + tName; auto genericType = builder.GetType(tMangledName, tName); genericType->orphanFlag = true; genericType->skipCheck = true; genericType->SetUpperBounds(std::vector{ReplaceRawGenericArgType(*ty, replaceTable, builder)}); paramTypes.emplace_back(genericType); } Type* retType = ReplaceRawGenericArgType(*instFuncTypeArgs.back(), replaceTable, builder); if (!retType->StripAllRefs()->IsGeneric()) { auto tName = "rT"; auto tMangledName = typePrefix + "_cc_" + tName; auto genericType = builder.GetType(tMangledName, tName); genericType->orphanFlag = true; genericType->skipCheck = true; genericType->SetUpperBounds(std::vector{ReplaceRawGenericArgType(*retType, replaceTable, builder)}); retType = genericType; } return {paramTypes, retType}; } std::string GenerateSrcCodeIdentifier(Value& memberVar) { auto memberName = memberVar.GetSrcCodeIdentifier(); if (memberName.empty()) { // let x = 1; let y = { => x }; if compile with `-g`, like `cjc -g xxx.cj` // CHIR is: // %0: Int64& = Allocate(Int64) // %1: Unit = Debug(%0, x) // %2: Unit = Store(1, %0) // %3: Int64 = Load(%0) --> %3 is captured by lambda expr if (auto envLocalVar = DynamicCast(&memberVar)) { if (auto loadEnvVar = DynamicCast(envLocalVar->GetExpr())) { memberName = loadEnvVar->GetLocation()->GetSrcCodeIdentifier(); } } } if (memberName.empty()) { memberName = memberVar.GetIdentifier(); } return memberName; } void ReplaceEnvVarWithMemberVar(const std::vector& capturedValues, Expression* firstExpr, Func& globalFunc, Value& curCalss, CHIRBuilder& builder) { uint64_t capturedValueIndex = 0; Expression* lastExpr = firstExpr; auto parentBlock = firstExpr ? firstExpr->GetParentBlock() : globalFunc.GetEntryBlock(); for (auto capturedVal : capturedValues) { auto envTy = IsMutableVarType(*capturedVal) ? capturedVal->GetType() : builder.GetType(capturedVal->GetType()); Expression* field = builder.CreateExpression( envTy, &curCalss, std::vector{capturedValueIndex}, parentBlock); if (lastExpr == nullptr) { globalFunc.GetEntryBlock()->InsertExprIntoHead(*field); } else { field->MoveAfter(lastExpr); } lastExpr = field; field = builder.CreateExpression(capturedVal->GetType(), field->GetResult(), parentBlock); field->MoveAfter(lastExpr); lastExpr = field; for (auto user : capturedVal->GetUsers()) { if (user->GetTopLevelFunc() != &globalFunc) { continue; } user->ReplaceOperand(capturedVal, field->GetResult()); } CJC_ASSERT(!field->GetResult()->GetUsers().empty()); ++capturedValueIndex; } } void ReplaceThisTypeInApplyAndInvoke( Lambda& nestedFunc, const std::vector& genericTypeParams, CHIRBuilder& builder) { if (!NeedAddThisType(*nestedFunc.GetResult())) { return; } CJC_ASSERT(!genericTypeParams.empty()); auto ThisType = genericTypeParams[0]; ConvertTypeFunc convertFunc = [&ThisType, &builder](Type& type) { // convert `This&` and `This` to generic type which declared in current scope return ReplaceThisTypeToConcreteType(type, *ThisType, builder); }; PrivateTypeConverter converter(convertFunc, builder); auto postVisit = [&converter](Expression& e) { converter.VisitExpr(e); return VisitResult::CONTINUE; }; Visitor::Visit( *nestedFunc.GetBody(), [](Expression&) { return VisitResult::CONTINUE; }, postVisit); } std::vector CreateGenericTypeParamForAutoEnvImplDef(const Value& srcFunc, CHIRBuilder& builder) { auto visiableGenericTypes = GetVisiableGenericTypes(srcFunc); if (NeedAddThisType(srcFunc)) { visiableGenericTypes.insert( visiableGenericTypes.begin(), builder.GetType("This", GENERIC_THIS_SRC_NAME)); } return visiableGenericTypes; } Value* CastTypeFromAutoEnvRefToFuncType(Type& srcFuncType, Value& autoEnvObj, Expression& user, const std::vector& genericTypeParams, CHIRBuilder& builder) { CJC_ASSERT(srcFuncType.IsFunc()); auto unboxRetTy = &srcFuncType; /** * when a class inherit an instantiated class, this child class does not rewrite parent's one function * Interface A { * func foo(a: T) {} * } * class B <: A { * } * In B, we will generate a func B::foo with type T->T to meet consistent function signatures in vtable * However when we got a function call B::foo(true), we should get its real function type Bool->Bool, * and type info is stored in orphan flag in generic type. */ unboxRetTy = GetOriFuncTypeFromOrphanType(unboxRetTy, builder); if (auto giv = DynamicCast(&user)) { auto allInstTypes = giv->GetInstantiateTypes(); std::unordered_map replaceMap; CJC_ASSERT(allInstTypes.size() >= genericTypeParams.size()); size_t offset = allInstTypes.size() - genericTypeParams.size(); for (size_t i = offset, j = 0; i < allInstTypes.size(); ++i, ++j) { if (auto gTy = DynamicCast(genericTypeParams[j])) { replaceMap.emplace(gTy, allInstTypes[i]); } } /* maybe lambda's return type carry with generic type declared in lambda, these generic types must be replaced to types declared in current scope func foo() { func goo(a: T) {a} return goo } lambda goo's return type is `T`, we can't generate UnBox like: UnBox(Class-$Auto_Env_xxx&, (T)->T) UnBox(Class-$Auto_Env_xxx&, (Int32)->Int32) is correct */ unboxRetTy = ReplaceRawGenericArgType(*unboxRetTy, replaceMap, builder); } else { /* we need to get instantiated func type, it must be from $Auto_Env_Base_xxx, but not operand of user the type of operand may contain generic type which not declared in current scope. e.g. func foo(x: T1) { func goo(y: T1): T1 { var a = goo a(y) // there is a `%0 = Load(a)`, and then is `Apply(%0, y)` // type of `a` is `(T1)->T1`, T1 can't be recognized after `func goo` being lifted // to member function, so `T1` must be converted to new generic type which declared in // $Auto_Env_Base_xxx } goo(x) } */ auto autoEnvObjTy = StaticCast(StaticCast(autoEnvObj.GetType())->GetBaseType()); unboxRetTy = GetFuncTypeWithoutThisPtrFromAutoEnvType(*autoEnvObjTy, builder); } // typecast from Class-$Auto_Env_xxx& to func type auto castExpr = builder.CreateExpression(unboxRetTy, &autoEnvObj, user.GetParentBlock()); castExpr->MoveBefore(&user); return castExpr->GetResult(); } void PrintGlobalFuncInfo(const CHIR::Position& pos) { std::string posMsg = " in the line:" + std::to_string(pos.line) + " and the column:" + std::to_string(pos.column); auto msg = "The top level func" + posMsg + " was closure converted"; std::cout << msg << std::endl; } void PrintImportedFuncInfo(const ImportedFunc& func) { std::string msg = "The imported func " + func.GetSrcCodeIdentifier() + " from package " + func.GetSourcePackageName() + " was closure converted"; std::cout << msg << std::endl; } void ConvertTypeCastToBox(const std::vector& typecastExprs, CHIRBuilder& builder) { for (auto e : typecastExprs) { auto box = builder.CreateExpression(e->GetResult()->GetType(), e->GetSourceValue(), e->GetParentBlock()); e->ReplaceWith(*box); } } void ConvertTypeCastToUnBox(const std::vector& typecastExprs, CHIRBuilder& builder) { for (auto e : typecastExprs) { auto unbox = builder.CreateExpression(e->GetResult()->GetType(), e->GetSourceValue(), e->GetParentBlock()); e->ReplaceWith(*unbox); } } void ConvertBoxToTypeCast(const std::vector& boxExprs, CHIRBuilder& builder) { for (auto e : boxExprs) { auto typecast = builder.CreateExpression(e->GetResult()->GetType(), e->GetSourceValue(), e->GetParentBlock()); e->ReplaceWith(*typecast); } } void ConvertUnBoxToTypeCast(const std::vector& unboxExprs, CHIRBuilder& builder) { for (auto e : unboxExprs) { auto typecast = builder.CreateExpression(e->GetResult()->GetType(), e->GetSourceValue(), e->GetParentBlock()); e->ReplaceWith(*typecast); } } bool ApplyNeedConvertToInvoke(Expression& e) { if (e.GetExprKind() != CHIR::ExprKind::APPLY) { return false; } /* if callee is local var or parameter, not a function, we need to replace Apply expr with Invoke expr func foo(a: ()->Unit) { a() // callee `a` is paramter, `Apply(a)` need be converted to `Invoke(a)` // note: except for CFunc type } */ auto apply = StaticCast(&e); return !apply->GetCallee()->IsFunc() && apply->GetCallee()->GetType()->IsCJFunc(); } bool ApplyWithExceptionNeedConvertToInvokeWithException(Expression& e) { if (e.GetExprKind() != CHIR::ExprKind::APPLY_WITH_EXCEPTION) { return false; } auto apply = StaticCast(&e); return !apply->GetCallee()->IsFunc() && apply->GetCallee()->GetType()->IsCJFunc(); } bool TypeCastNeedConvertToBox(Expression& e) { if (e.GetExprKind() != CHIR::ExprKind::TYPECAST) { return false; } // TypeCast(value type, func type) need to be converted to Box(value type, Class-$Auto_Env_xxx&) // ps: value type is non-func type, func type is not CFunc type auto typecast = StaticCast(&e); return typecast->GetTargetTy()->IsCJFunc() && !typecast->GetSourceTy()->IsCJFunc(); } bool TypeCastNeedConvertToUnBox(Expression& e) { if (e.GetExprKind() != CHIR::ExprKind::TYPECAST) { return false; } // TypeCast(func type, value type) need to be converted to UnBox(Class-$Auto_Env_xxx&, value type) // ps: value type is non-func type, func type is not CFunc type auto typecast = StaticCast(&e); return typecast->GetSourceTy()->IsCJFunc() && !typecast->GetTargetTy()->IsCJFunc(); } bool BoxNeedConvertToTypeCast(Expression& e) { if (e.GetExprKind() != CHIR::ExprKind::BOX) { return false; } // Box(func type, ref type) need to be converted to TypeCast(Class-$Auto_Env_xxx&, ref type) // ps: func type is not CFunc type auto box = StaticCast(&e); return box->GetSourceTy()->IsCJFunc(); } bool UnBoxNeedConvertToTypeCast(Expression& e) { if (e.GetExprKind() != CHIR::ExprKind::UNBOX) { return false; } // UnBox(ref type, func type) need to be converted to TypeCast(ref type, Class-$Auto_Env_xxx&) // ps: func type is not CFunc type auto box = StaticCast(&e); return box->GetTargetTy()->IsCJFunc(); } bool ApplyRetValNeedWrapper(Expression& e) { if (e.GetExprKind() != CHIR::ExprKind::APPLY) { return false; } auto& apply = StaticCast(e); auto calleeType = apply.GetCallee()->GetType(); if (!calleeType->IsCJFunc()) { return false; } /** func foo(a: ()->T): ()->T { * return a * } * func goo(): Bool { * return true * } * var x: ()->Bool = foo(goo) * return type of func foo is `()->T`, but x's type is ()->Bool * closure type of `()->T` is Class-$AutoEnvGenericBase, closure type of `()->Bool` is Class-$AutoEnvInstBase * Class-$AutoEnvGenericBase is parent type of Class-$AutoEnvInstBase * we can't cast type from parent type to sub type, a wrapper class is needed */ auto declaredRetType = StaticCast(calleeType)->GetReturnType()->StripAllRefs(); auto applyRetType = apply.GetResult()->GetType()->StripAllRefs(); return !declaredRetType->IsAutoEnvInstBase() && applyRetType->IsAutoEnvInstBase(); } std::vector OperandNeedTypeCast( const std::vector& args, const std::vector& paramTypes, size_t offset) { std::vector index; CJC_ASSERT(args.size() == paramTypes.size()); for (size_t i = 0; i < paramTypes.size(); ++i) { auto paramTy = paramTypes[i]->StripAllRefs(); auto argTy = args[i]->GetType()->StripAllRefs(); if (!paramTy->IsAutoEnvBase() || !argTy->IsAutoEnvBase()) { continue; } auto paramTyDef = StaticCast(paramTy)->GetClassDef(); auto argTyDef = StaticCast(argTy)->GetClassDef(); // only typecast when argTy is Auto_Env_instBase and paramTy is Auto_Env_genericBase if (argTyDef->GetSuperClassDef() == paramTyDef) { index.emplace_back(i + offset); } } return index; } std::pair> ApplyArgNeedTypeCast(const Apply& e) { std::vector index; auto calleeType = e.GetCallee()->GetType(); if (!calleeType->IsCJFunc()) { return {false, index}; } /** func foo(a: ()->T): Int32 { * return true * } * func goo(): Bool { * return true * } * var x: Int32 = foo(goo) * closure type of `()->T` is Class-$AutoEnvGenericBase, closure type of `()->Bool` is Class-$AutoEnvInstBase * Class-$AutoEnvGenericBase is parent type of Class-$AutoEnvInstBase * we can cast type from sub type to parent type */ auto args = e.GetArgs(); auto paramTypes = StaticCast(calleeType)->GetParamTypes(); index = OperandNeedTypeCast(args, paramTypes, 1); return {!index.empty(), index}; } bool ApplyWithExceptionRetValNeedWrapper(const ApplyWithException& e) { auto calleeType = e.GetCallee()->GetType(); if (!calleeType->IsCJFunc()) { return false; } auto declaredRetType = StaticCast(calleeType)->GetReturnType()->StripAllRefs(); auto applyRetType = e.GetResult()->GetType()->StripAllRefs(); return !declaredRetType->IsAutoEnvInstBase() && applyRetType->IsAutoEnvInstBase(); } bool InvokeRetValNeedWrapper(const Expression& e) { if (e.GetExprKind() != CHIR::ExprKind::INVOKE && e.GetExprKind() != CHIR::ExprKind::INVOKESTATIC) { return false; } // same reason with Apply Type* declaredRetType = nullptr; if (auto invoke = DynamicCast(&e)) { declaredRetType = invoke->GetMethodType()->GetReturnType()->StripAllRefs(); } else { declaredRetType = StaticCast(e).GetMethodType()->GetReturnType()->StripAllRefs(); } auto invokeRetType = e.GetResult()->GetType()->StripAllRefs(); return !declaredRetType->IsAutoEnvInstBase() && invokeRetType->IsAutoEnvInstBase(); } bool InvokeWithExceptionRetValNeedWrapper(const Expression& e) { if (e.GetExprKind() != CHIR::ExprKind::INVOKE_WITH_EXCEPTION && e.GetExprKind() != CHIR::ExprKind::INVOKESTATIC_WITH_EXCEPTION) { return false; } // same reason with Apply Type* declaredRetType = nullptr; if (auto invoke = DynamicCast(&e)) { declaredRetType = invoke->GetMethodType()->GetReturnType()->StripAllRefs(); } else { declaredRetType = StaticCast(e).GetMethodType()->GetReturnType()->StripAllRefs(); } auto invokeRetType = e.GetResult()->GetType()->StripAllRefs(); return !declaredRetType->IsAutoEnvInstBase() && invokeRetType->IsAutoEnvInstBase(); } bool GetElementRefRetValNeedWrapper(const Expression& e, CHIRBuilder& builder) { if (e.GetExprKind() != CHIR::ExprKind::GET_ELEMENT_REF) { return false; } const auto& getEleRef = StaticCast(e); auto getEleRefRetType = getEleRef.GetResult()->GetType()->StripAllRefs(); // only Auto_Env_InstBase need wrapper if (!getEleRefRetType->IsAutoEnvInstBase()) { return false; } /** class A { * var a: ()->T * } * var b: ()->Bool = A().a * * a's type is `()->T`, but b's type is ()->Bool * closure type of `()->T` is Class-$AutoEnvGenericBase, closure type of `()->Bool` is Class-$AutoEnvInstBase * Class-$AutoEnvGenericBase is parent type of Class-$AutoEnvInstBase * we can't cast type from parent type to sub type, a wrapper class is needed */ auto locationType = getEleRef.GetLocation()->GetType(); for (auto& idx : getEleRef.GetPath()) { locationType = GetFieldOfType(*locationType, idx, builder); CJC_NULLPTR_CHECK(locationType); } return !locationType->StripAllRefs()->IsAutoEnvInstBase(); } Type* GetFieldBaseType(const Value& base) { if (!base.IsLocalVar()) { return base.GetType(); } auto expr = StaticCast(base).GetExpr(); if (expr->GetExprKind() != CHIR::ExprKind::TYPECAST) { return base.GetType(); } // there may be a typecast from enum type to tuple type: // %1: Enum-Option = xxx // %2: Tuple = TypeCast(%1, Tuple) // this typecast is for codegen, expect to be removed later auto srcValType = StaticCast(expr)->GetSourceValue()->GetType(); if (srcValType->IsEnum()) { return nullptr; } else { return base.GetType(); } } bool FieldRetValNeedWrapper(const Expression& e, CHIRBuilder& builder) { if (e.GetExprKind() != CHIR::ExprKind::FIELD) { return false; } const auto& field = StaticCast(e); auto fieldRetType = field.GetResult()->GetType()->StripAllRefs(); // only Auto_Env_InstBase need wrapper if (!fieldRetType->IsAutoEnvInstBase()) { return false; } Type* declaredType = GetFieldBaseType(*field.GetBase()); if (declaredType == nullptr) { return true; } // same reason with `GetElementRef` for (auto& idx : field.GetPath()) { declaredType = GetFieldOfType(*declaredType, idx, builder); CJC_NULLPTR_CHECK(declaredType); } declaredType = declaredType->StripAllRefs(); return !declaredType->IsAutoEnvInstBase(); } bool IsAutoEnvGenericType(const Type& type) { if (!type.IsAutoEnv()) { return false; } if (type.IsAutoEnvGenericBase()) { return true; } return StaticCast(type).GetClassDef()->GetMethods().size() == 1; } bool TypeCastSrcValNeedWrapper(const Expression& e) { if (e.GetExprKind() != CHIR::ExprKind::TYPECAST) { return false; } /** Class-$AutoEnvGenericBase is parent type of Class-$AutoEnvInstBase * we can't cast type from parent type to sub type, a wrapper class is needed */ const auto& typecast = StaticCast(e); auto srcType = typecast.GetSourceTy()->StripAllRefs(); auto targetType = typecast.GetTargetTy()->StripAllRefs(); return IsAutoEnvGenericType(*srcType) && targetType->IsAutoEnvInstBase(); } std::pair> ApplyWithExceptionArgNeedTypeCast(const ApplyWithException& e) { std::vector index; const auto& apply = StaticCast(e); auto calleeType = apply.GetCallee()->GetType(); if (!calleeType->IsCJFunc()) { return {false, index}; } auto args = apply.GetArgs(); auto paramTypes = StaticCast(calleeType)->GetParamTypes(); index = OperandNeedTypeCast(args, paramTypes, 1); return {!index.empty(), index}; } std::pair> EnumConstructorNeedTypeCast(const Expression& e) { std::vector index; if (e.GetExprKind() != CHIR::ExprKind::TUPLE) { return {false, index}; } const auto& tuple = StaticCast(e); auto resultType = tuple.GetResult()->GetType(); if (!resultType->IsEnum()) { return {false, index}; } auto enumDef = StaticCast(resultType)->GetEnumDef(); auto idxExpr = StaticCast(StaticCast(tuple.GetOperand(0))->GetExpr()); size_t idx = 0; if (idxExpr->IsIntLit()) { idx = idxExpr->GetUnsignedIntLitVal(); } else if (idxExpr->IsBoolLit()) { idx = idxExpr->GetBoolLitVal() ? 1 : 0; } else { CJC_ABORT(); } /** enum E { * A(()->T) * } * %0: E = Tuple(0, ()->Bool) * param type of E.A is `()->T`, but Tuple arg's type is `()->Bool`, a TypeCast is needed * closure type of `()->T` is Class-$AutoEnvGenericBase, closure type of `()->Bool` is Class-$AutoEnvInstBase * Class-$AutoEnvGenericBase is parent type of Class-$AutoEnvInstBase * we can cast type from sub type to parent type */ auto constructor = enumDef->GetCtor(idx); auto paramTypes = constructor.funcType->GetParamTypes(); auto args = tuple.GetOperands(); args.erase(args.begin()); index = OperandNeedTypeCast(args, paramTypes, 1); return {!index.empty(), index}; } std::pair> TupleNeedTypeCast(const Expression& e) { std::vector index; if (e.GetExprKind() != CHIR::ExprKind::TUPLE) { return {false, index}; } const auto& tuple = StaticCast(e); auto resultType = tuple.GetResult()->GetType(); if (!resultType->IsTuple()) { return {false, index}; } /** func foo(a:(Bool, ()->T)) {} * func goo(): Bool { true } * foo((true, goo)) * * closure type of `()->T` is Class-$AutoEnvGenericBase, closure type of `()->Bool` is Class-$AutoEnvInstBase * Class-$AutoEnvGenericBase is parent type of Class-$AutoEnvInstBase * we can cast type from sub type to parent type */ auto expectedTypes = StaticCast(resultType)->GetElementTypes(); auto args = tuple.GetOperands(); index = OperandNeedTypeCast(args, expectedTypes, 0); return {!index.empty(), index}; } std::pair> RawArrayInitByValueNeedTypeCast(const Expression& e) { std::vector index; if (e.GetExprKind() != CHIR::ExprKind::RAW_ARRAY_INIT_BY_VALUE) { return {false, index}; } /** %0: Int64 = Constant(1) * %1: RawArray<$AutoEnvGenericBase>& = RawArrayAllocate(RawArray<$AutoEnvGenericBase>, %0) * %2: $AutoEnvInstBase_Bool = xxx * %3: Unit = RawArrayInitByValue(%1, %0, %2) * * in `RawArrayInitByValue`, type between %1 and %2 is mismatched, * $AutoEnvInstBase_Bool is sub type of $AutoEnvGenericBase, we can cast type from sub type to parent type */ const auto& init = StaticCast(e); auto rawArrayType = StaticCast(init.GetRawArray()->GetType()->StripAllRefs()); auto expectedType = std::vector{rawArrayType->GetElementType()}; auto initValue = std::vector{init.GetInitValue()}; const size_t opOffset = 2; index = OperandNeedTypeCast(initValue, expectedType, opOffset); return {!index.empty(), index}; } void AddTypeCastForOperand(const std::pair>& e, CHIRBuilder& builder) { auto expr = e.first; auto parentBlock = expr->GetParentBlock(); for (auto idx : e.second) { auto op = expr->GetOperand(idx); auto expectedType = builder.GetType( StaticCast(op->GetType()->StripAllRefs())->GetClassDef()->GetSuperClassTy()); auto typecast = builder.CreateExpression(expectedType, op, parentBlock); typecast->MoveBefore(expr); expr->ReplaceOperand(idx, typecast->GetResult()); } } std::pair> SpawnNeedTypeCast(const Expression& e) { std::vector index; if (e.GetExprKind() != CHIR::ExprKind::SPAWN) { return {false, index}; } const auto& spawn = StaticCast(e); if (!spawn.IsExecuteClosure()) { // only check executeClosure, skip future Execute return {false, index}; } /** cj code `spawn { 0 }` is translated to * %0: ()->Int64 = lambda { reteurn 0 } * %1: Future = Spawn(%0) * * after redundant future removing, codegen is expected to use Future.executeClosure which declared in std.core * however, param type of `executeClosure` is `()->T`, it has gap to %0 which type is `()->Int64` * closure type of `()->T` is Class-$AutoEnvGenericBase, closure type of `()->Int64` is Class-$AutoEnvInstBase * Class-$AutoEnvGenericBase is parent type of Class-$AutoEnvInstBase * we can cast type from sub type to parent type */ auto funcType = spawn.GetExecuteClosure()->GetFuncType(); index = OperandNeedTypeCast(spawn.GetOperands(), funcType->GetParamTypes(), 0); return {!index.empty(), index}; } void ReplaceOperandWithAutoEnvWrapperClass( Value& op, Value& autoEnvWrapperClass, const std::unordered_set& skip) { for (auto user : op.GetUsers()) { if (skip.find(user) != skip.end() || user->GetExprKind() == CHIR::ExprKind::INSTANCEOF) { continue; } user->ReplaceOperand(&op, &autoEnvWrapperClass); } } } // namespace ClosureConversion::ClosureConversion(Package& package, CHIRBuilder& builder, const GlobalOptions& opts, const std::unordered_set& srcCodeImportedFuncs) : package(package), builder(builder), objClass(*builder.GetObjectTy()->GetClassDef()), opts(opts), srcCodeImportedFuncs(srcCodeImportedFuncs) { } std::set ClosureConversion::GetCCOutFuncsRawMangle() const { return ccOutFuncsRawMangle; } std::unordered_set ClosureConversion::GetUselessClassDef() const { return uselessClasses; } std::unordered_set ClosureConversion::GetUselessLambda() const { return uselessLambda; } std::vector ClosureConversion::CollectNestedFunctions() { std::vector nestedFuncs; Func* curOutFunc = nullptr; auto preVisit = [&nestedFuncs, &curOutFunc, this](Expression& e) { if (auto lambdaExpr = DynamicCast(&e); lambdaExpr) { // maybe there is another lambda in current lambda's body, but we can't visit another lambda // by walking Func node, so we need to walk current lambda body manually CollectNestedLambdaExpr(*lambdaExpr, nestedFuncs, curOutFunc, ccOutFuncsRawMangle); // we must collect inner lambda first, and then outer lambda, // because we need to lift lambda from inner to outer nestedFuncs.emplace_back(lambdaExpr); if (opts.enIncrementalCompilation) { if (!curOutFunc->GetRawMangledName().empty()) { ccOutFuncsRawMangle.emplace(curOutFunc->GetRawMangledName()); } } } return VisitResult::CONTINUE; }; for (auto func : package.GetGlobalFuncs()) { if (func->TestAttr(Attribute::SKIP_ANALYSIS)) { continue; } curOutFunc = func; VisitFuncBlocksInTopoSort(*func->GetBody(), preVisit); } return nestedFuncs; } std::string ClosureConversion::GenerateGlobalFuncIdentifier(const Lambda& lambda) { auto it = duplicateLambdaName.find(lambda.GetIdentifier()); CJC_ASSERT(it != duplicateLambdaName.end()); if (it->second == 0) { return lambda.GetIdentifier(); } else { return lambda.GetIdentifier() + "$" + std::to_string(it->second); } } Ptr ClosureConversion::CreateAutoEnvImplObject( Block& parent, ClassType& autoEnvImplType, const std::vector& envs, Expression& user, Value& srcFunc) { auto instantiateClassTy = GenerateInstantiatedClassType(autoEnvImplType, user, srcFunc); auto refType = builder.GetType(instantiateClassTy); Expression* expr = builder.CreateExpression(refType, instantiateClassTy, &parent); auto obj = expr->GetResult(); uint64_t index = 0; for (auto env : envs) { auto store = builder.CreateExpression( builder.GetUnitTy(), env, obj, std::vector{index}, &parent); store->MoveBefore(&user); index++; } // if not move to head, in some cases which call lambda recursively, we will get unreachable expression // func foo1() { // func foo2(a!: Int32 = 1) { foo2(); return 1} // foo2() // } parent.InsertExprIntoHead(*expr); return expr->GetResult(); } ClassType* ClosureConversion::GenerateInstantiatedClassType( ClassType& autoEnvImplType, const Expression& user, Value& srcFunc) { // The instantiated type args include two parts: // 1) args for the generic param of the outer scope decls // 2) args for the generic param of func itself auto visibleGenericTypes = GetVisiableGenericTypes(srcFunc); auto autoEnvInstTypeArgs = std::vector(visibleGenericTypes.begin(), visibleGenericTypes.end()); if (auto giv = DynamicCast(&user); giv) { auto givInstTypes = giv->GetInstantiateTypes(); auto funcGenericParamNum = GetFuncGenericTypeParamNum(srcFunc); CJC_ASSERT(funcGenericParamNum <= autoEnvInstTypeArgs.size()); CJC_ASSERT(funcGenericParamNum <= givInstTypes.size()); size_t instArgsOffset = autoEnvInstTypeArgs.size() - funcGenericParamNum; size_t givInstTysOffset = givInstTypes.size() - funcGenericParamNum; for (size_t i = 0; i < funcGenericParamNum; ++i) { autoEnvInstTypeArgs[instArgsOffset + i] = givInstTypes[givInstTysOffset + i]; } auto instTypeParams = GetInstTypeParamsForCustomTypeDef(givInstTypes, srcFunc); for (size_t i = 0; i < instTypeParams.size(); ++i) { autoEnvInstTypeArgs[i] = instTypeParams[i]; } } else if (auto apply = DynamicCast(&user); apply && apply->GetCallee() == &srcFunc) { auto& partInstTys = apply->GetInstantiatedTypeArgs(); CJC_ASSERT(autoEnvInstTypeArgs.size() >= partInstTys.size()); size_t offset = autoEnvInstTypeArgs.size() - partInstTys.size(); for (size_t i = offset, j = 0; i < autoEnvInstTypeArgs.size(); ++i, ++j) { autoEnvInstTypeArgs[i] = partInstTys[j]; } } else if (auto awe = DynamicCast(&user); awe && awe->GetCallee() == &srcFunc) { auto& partInstTys = awe->GetInstantiatedTypeArgs(); CJC_ASSERT(autoEnvInstTypeArgs.size() >= partInstTys.size()); size_t offset = autoEnvInstTypeArgs.size() - partInstTys.size(); for (size_t i = offset, j = 0; i < autoEnvInstTypeArgs.size(); ++i, ++j) { autoEnvInstTypeArgs[i] = partInstTys[j]; } } if (NeedAddThisType(srcFunc)) { autoEnvInstTypeArgs.insert(autoEnvInstTypeArgs.begin(), builder.GetType()); } CJC_ASSERT(autoEnvImplType.GetGenericArgs().size() == autoEnvInstTypeArgs.size()); return builder.GetType(autoEnvImplType.GetClassDef(), autoEnvInstTypeArgs); } ClassDef* ClosureConversion::CreateBoxClassDef(Type& type) { auto it = boxClassMap.find(&type); if (it != boxClassMap.end()) { return it->second; } auto className = "$Captured_" + type.ToString(); std::vector genericArgTypes; GetAllGenericType(type, genericArgTypes); auto mangledName = MangleUtils::GetMangledNameOfCompilerAddedClass(className); auto classDef = builder.CreateClass(INVALID_LOCATION, className, mangledName, package.GetName(), true, false); auto classTy = builder.GetType(classDef, genericArgTypes); classDef->SetType(*classTy); classDef->SetSuperClassTy(*StaticCast(objClass.GetType())); classDef->Set(true); classDef->EnableAttr(Attribute::COMPILER_ADD); classDef->EnableAttr(Attribute::NO_REFLECT_INFO); if (!genericArgTypes.empty()) { classDef->EnableAttr(Attribute::GENERIC); } AttributeInfo attributeInfo; attributeInfo.SetAttr(Attribute::PUBLIC, true); classDef->AddInstanceVar(MemberVarInfo{"value", "", &type, attributeInfo, INVALID_LOCATION}); boxClassMap.emplace(&type, classDef); return classDef; } LocalVar* ClosureConversion::CreateBoxClassObj(const LocalVar& env, const ClassDef& classDef) { auto& loc = env.GetExpr()->GetDebugLocation(); auto classTy = classDef.GetType(); auto retTy = builder.GetType(classTy); auto obj = builder.CreateExpression(loc, retTy, classTy, env.GetExpr()->GetParentBlock()); obj->MoveBefore(env.GetExpr()); return obj->GetResult(); } void ClosureConversion::ReplaceEnvWithBoxObjMemberVar(LocalVar& env, LocalVar& boxObj, LocalVar& lValue) { auto debugExpr = env.GetDebugExpr(); for (auto user : env.GetUsers()) { if (user->GetExprKind() == CHIR::ExprKind::STORE) { auto storeNode = StaticCast(user); auto base = storeNode->GetValue(); auto value = builder.CreateExpression( builder.GetUnitTy(), base, &boxObj, std::vector{0}, user->GetParentBlock()); value->MoveBefore(user); user->RemoveSelfFromBlock(); continue; } auto value = builder.CreateExpression( env.GetType(), &boxObj, std::vector{0}, user->GetParentBlock()); value->MoveBefore(user); user->ReplaceOperand(&env, value->GetResult()); } if (debugExpr != nullptr) { auto loc = debugExpr->GetDebugLocation(); auto newDebug = builder.CreateExpression( loc, builder.GetUnitTy(), &lValue, debugExpr->GetSrcCodeIdentifier(), debugExpr->GetParentBlock()); newDebug->MoveBefore(debugExpr); debugExpr->RemoveSelfFromBlock(); } if (opts.chirDebugOptimizer) { PrintMutableVarInfo(env.GetExpr()->GetDebugLocation().GetBeginPos()); } env.GetExpr()->RemoveSelfFromBlock(); } std::pair ClosureConversion::SetBoxClassAsMutableVar(LocalVar& rValue) { auto retTy = builder.GetType(rValue.GetType()); auto lValue = builder.CreateExpression(retTy, rValue.GetType(), rValue.GetExpr()->GetParentBlock()); lValue->MoveAfter(rValue.GetExpr()); auto asg = builder.CreateExpression(builder.GetUnitTy(), &rValue, lValue->GetResult(), lValue->GetParentBlock()); asg->MoveAfter(lValue); auto load = builder.CreateExpression(rValue.GetType(), lValue->GetResult(), asg->GetParentBlock()); load->MoveAfter(asg); return {lValue->GetResult(), load->GetResult()}; } Value* ClosureConversion::BoxMutableVar(LocalVar& env) { // let a: Class-$BOX_xxx = $BOX_xxx() auto classDef = CreateBoxClassDef(*StaticCast(env.GetType())->GetBaseType()); auto boxObj = CreateBoxClassObj(env, *classDef); LocalVar* allocRes = boxObj; // if not use `-g`, we generate less expressions to improve runtime performance if (opts.enableCompileDebug) { // var a: Class-$BOX_xxx = $BOX_xxx() auto [lValue, rValue] = SetBoxClassAsMutableVar(*boxObj); allocRes = lValue; boxObj = rValue; } ReplaceEnvWithBoxObjMemberVar(env, *boxObj, *allocRes); return boxObj; } std::vector ClosureConversion::BoxAllMutableVars(const std::vector& rawEnvs) { std::vector boxedEnvs; for (auto env : rawEnvs) { if (IsMutableVarType(*env)) { // only LocalVar and Parameter can be environment, but Parameter is read only, can't be mutable var CJC_ASSERT(env->IsLocalVar()); boxedEnvs.emplace_back(BoxMutableVar(*StaticCast(env))); } else { boxedEnvs.emplace_back(env); } } return boxedEnvs; } void ClosureConversion::LiftNestedFunctionWithCFuncType(Lambda& nestedFunc) { const auto& loc = nestedFunc.GetResult()->GetDebugLocation(); // move lambda to global function auto globalFuncId = GenerateGlobalFuncIdentifier(nestedFunc); auto genericParamTypes = nestedFunc.GetGenericTypeParams(); auto globalFunc = builder.CreateFunc(loc, StaticCast(nestedFunc.GetResult()->GetType()), globalFuncId, nestedFunc.GetSrcCodeIdentifier(), "", package.GetName(), genericParamTypes); // After globalFunc is used, new localId needs to be generated based on the ID in oldFunc during subsequent // optimization. Otherwise, duplicate IDs may exist. Therefore, the ID in oldFunc is transferred to globalFunc. CJC_NULLPTR_CHECK(nestedFunc.GetBody()->GetTopLevelFunc()); globalFunc->InheritIDFromFunc(*nestedFunc.GetBody()->GetTopLevelFunc()); globalFunc->InitBody(*nestedFunc.GetBody()); SetLiftedLambdaAttr(*globalFunc, nestedFunc); auto sigInfo = FuncSigInfo{ .funcName = nestedFunc.GetSrcCodeIdentifier(), .funcType = nestedFunc.GetFuncType(), .genericTypeParams = nestedFunc.GetGenericTypeParams() }; globalFunc->SetOriginalLambdaInfo(sigInfo); globalFunc->SetReturnValue(*nestedFunc.GetReturnValue()); convertedCache.emplace(&nestedFunc, globalFunc); // set args of global function for (auto arg : nestedFunc.GetParams()) { globalFunc->AddParam(*arg); } for (auto user : nestedFunc.GetResult()->GetUsers()) { user->ReplaceOperand(nestedFunc.GetResult(), globalFunc); } nestedFunc.RemoveSelfFromBlock(); } void ClosureConversion::RecordDuplicateLambdaName(const Lambda& func) { auto it = duplicateLambdaName.find(func.GetIdentifier()); if (it == duplicateLambdaName.end()) { duplicateLambdaName.emplace(func.GetIdentifier(), 0); } else { it->second++; } } bool ClosureConversion::LambdaCanBeInlined(const Expression& user, const FuncBase& lambda) { if (user.GetExprKind() != CHIR::ExprKind::APPLY) { return false; } auto callee = StaticCast(user).GetCallee(); // must be callee of apply if (&lambda != callee) { return false; } bool isRecursive = false; auto preVisit = [&callee, &isRecursive](Expression& e) { if (e.GetExprKind() == CHIR::ExprKind::APPLY && StaticCast(e).GetCallee() == callee) { isRecursive = true; return VisitResult::STOP; } else if (e.GetExprKind() == CHIR::ExprKind::APPLY_WITH_EXCEPTION && StaticCast(e).GetCallee() == callee) { isRecursive = true; return VisitResult::STOP; } return VisitResult::CONTINUE; }; Visitor::Visit(*VirtualCast(callee), preVisit); // recursive function doesn't need to be inlined return !isRecursive; } void ClosureConversion::DoFunctionInlineForLambda() { if (!opts.IsOptimizationExisted(GlobalOptions::OptimizationFlag::FUNC_INLINING)) { return; } auto funcInline = FunctionInline(builder, GlobalOptions::OptimizationLevel::O2, opts.chirDebugOptimizer); for (auto& it : autoEnvImplDefs) { auto methods = it.second->GetMethods(); for (auto lambda : it.second->GetMethods()) { // after inlining generic virtual func, we should remvoe Box and Unbox expr, we will do it later if (lambda->GetSrcCodeIdentifier() == GENERIC_VIRTUAL_FUNC) { continue; } for (auto user : lambda->GetUsers()) { if (LambdaCanBeInlined(*user, *lambda)) { funcInline.DoFunctionInline(*StaticCast(user), "functionInlineForLambda"); } } } } } ClassDef* ClosureConversion::GetOrCreateAutoEnvBaseDef(const FuncType& funcType) { auto autoEnvBaseDef = GetOrCreateGenericAutoEnvBaseDef(funcType.GetParamTypes().size()); if (!funcType.IsGenericRelated()) { autoEnvBaseDef = GetOrCreateInstAutoEnvBaseDef(funcType, *autoEnvBaseDef); } return autoEnvBaseDef; } void ClosureConversion::InlineLambda(const std::vector& funcs) { LambdaInline(builder, opts).InlineLambda(funcs); } void ClosureConversion::ConvertNestedFunctions(const std::vector& funcs) { for (auto func : funcs) { RecordDuplicateLambdaName(*func); if (func->GetFuncType()->IsCFunc()) { LiftNestedFunctionWithCFuncType(*func); continue; } auto rawEnvs = func->GetCapturedVariables(); auto boxedEnvs = BoxAllMutableVars(rawEnvs); auto autoEnvBaseDef = GetOrCreateAutoEnvBaseDef(*func->GetFuncType()); auto autoEnvImplDef = GetOrCreateAutoEnvImplDef(*func, *autoEnvBaseDef, boxedEnvs); // `users` may be refreshed after the above processes. auto users = func->GetResult()->GetUsers(); for (auto user : users) { ReplaceUserPoint(*func, *user, boxedEnvs, *autoEnvImplDef); } if (opts.chirDebugOptimizer) { PrintNestedFuncInfo(func->GetDebugLocation().GetBeginPos()); } RemoveGetInstantiateValue(users); func->RemoveSelfFromBlock(); } DoFunctionInlineForLambda(); } std::vector ClosureConversion::ConvertArgsType(const std::vector& types) { std::vector argsType; argsType.reserve(types.size()); for (const auto ty : types) { argsType.emplace_back(ConvertTypeToClosureType(*ty)); } return argsType; } Type* ClosureConversion::ConvertTupleType(const TupleType& type) { return builder.GetType(ConvertArgsType(type.GetElementTypes())); } Type* ClosureConversion::ConvertFuncType(const FuncType& type) { auto autoEnvBaseDef = GetOrCreateGenericAutoEnvBaseDef(type.GetParamTypes().size()); auto convertedFuncType = ConvertFuncArgsAndRetType(type); return builder.GetType(InstantiateAutoEnvBaseType(*autoEnvBaseDef, *convertedFuncType, builder)); } Type* ClosureConversion::ConvertEnumType(const EnumType& type) { return builder.GetType(type.GetEnumDef(), ConvertArgsType(type.GetGenericArgs())); } Type* ClosureConversion::ConvertStructType(const StructType& type) { return builder.GetType(type.GetStructDef(), ConvertArgsType(type.GetGenericArgs())); } Type* ClosureConversion::ConvertClassType(const ClassType& type) { return builder.GetType(type.GetClassDef(), ConvertArgsType(type.GetGenericArgs())); } Type* ClosureConversion::ConvertRawArrayType(const RawArrayType& type) { return builder.GetType(ConvertTypeToClosureType(*type.GetElementType()), type.GetDims()); } Type* ClosureConversion::ConvertVArrayType(const VArrayType& type) { return builder.GetType(ConvertTypeToClosureType(*type.GetElementType()), type.GetSize()); } Type* ClosureConversion::ConvertCPointerType(const CPointerType& type) { return builder.GetType(ConvertTypeToClosureType(*type.GetElementType())); } Type* ClosureConversion::ConvertRefType(const RefType& type) { return builder.GetType(ConvertTypeToClosureType(*type.GetBaseType())); } Type* ClosureConversion::ConvertBoxType(const BoxType& type) { return builder.GetType(ConvertTypeToClosureType(*type.GetBaseType())); } Type* ClosureConversion::ConvertCompositionalType(const Type& type) { Type* curConvertedTy = nullptr; switch (type.GetTypeKind()) { case Type::TypeKind::TYPE_TUPLE: curConvertedTy = ConvertTupleType(StaticCast(type)); break; case Type::TypeKind::TYPE_FUNC: curConvertedTy = ConvertFuncType(StaticCast(type)); break; case Type::TypeKind::TYPE_ENUM: curConvertedTy = ConvertEnumType(StaticCast(type)); break; case Type::TypeKind::TYPE_STRUCT: curConvertedTy = ConvertStructType(StaticCast(type)); break; case Type::TypeKind::TYPE_CLASS: curConvertedTy = ConvertClassType(StaticCast(type)); break; case Type::TypeKind::TYPE_RAWARRAY: curConvertedTy = ConvertRawArrayType(StaticCast(type)); break; case Type::TypeKind::TYPE_VARRAY: curConvertedTy = ConvertVArrayType(StaticCast(type)); break; case Type::TypeKind::TYPE_CPOINTER: curConvertedTy = ConvertCPointerType(StaticCast(type)); break; case Type::TypeKind::TYPE_REFTYPE: curConvertedTy = ConvertRefType(StaticCast(type)); break; case Type::TypeKind::TYPE_BOXTYPE: curConvertedTy = ConvertBoxType(StaticCast(type)); break; default: CJC_ABORT(); } CJC_NULLPTR_CHECK(curConvertedTy); return curConvertedTy; } Type* ClosureConversion::ConvertTypeToClosureType(Type& type) { // case 1: just return original type if (type.IsPrimitive() || type.IsInvalid() || type.IsAny() || type.IsVoid() || type.IsCFunc() || type.IsCString() || type.IsThis()) { return &type; } // case 2: store generic type and convert later if (type.IsGeneric()) { needConvertedGenericTys.emplace(StaticCast(&type)); return &type; } const auto tyCollected = typeConvertMap.find(&type); if (tyCollected != typeConvertMap.end()) { return tyCollected->second; } // case 3: compositional type auto curConvertedTy = ConvertCompositionalType(type); typeConvertMap[&type] = curConvertedTy; return curConvertedTy; } FuncType* ClosureConversion::ConvertFuncArgsAndRetType(const FuncType& oldFuncTy) { std::vector newArgsTy; for (auto ty : oldFuncTy.GetParamTypes()) { newArgsTy.emplace_back(ConvertTypeToClosureType(*ty)); } auto newRetTy = ConvertTypeToClosureType(*oldFuncTy.GetReturnType()); return builder.GetType(newArgsTy, newRetTy, oldFuncTy.HasVarArg(), oldFuncTy.IsCFunc()); } void ClosureConversion::LiftGenericTypes() { // we can't set upper bounds in `ConvertCompositionalType`, it will cause unlimited recursion // e.g. interface A where T <: A auto temp = needConvertedGenericTys; for (auto ty : temp) { std::vector newUpperBounds; for (auto upperBound : ty->GetUpperBounds()) { newUpperBounds.emplace_back(ConvertTypeToClosureType(*upperBound)); } ty->SetUpperBounds(newUpperBounds); } } void ClosureConversion::LiftType() { ConvertTypeFunc convertTypeToClosure = [this](Type& type) { return ConvertTypeToClosureType(type); }; ConvertTypeFunc convertTypeToInstBase = [this, &convertTypeToClosure, &convertTypeToInstBase](Type& type) { auto& funcType = StaticCast(type); auto autoEnvBaseDef = GetOrCreateAutoEnvBaseDef(funcType); TypeConverterForCC converter(convertTypeToClosure, convertTypeToInstBase, builder); LiftCustomDefType(*autoEnvBaseDef, converter); auto convertedFuncType = ConvertFuncArgsAndRetType(funcType); return builder.GetType(InstantiateAutoEnvBaseType(*autoEnvBaseDef, *convertedFuncType, builder)); }; TypeConverterForCC converter(convertTypeToClosure, convertTypeToInstBase, builder); auto postVisit = [&converter](Expression& e) { converter.VisitExpr(e); return VisitResult::CONTINUE; }; for (auto func : package.GetGlobalFuncs()) { if (func->TestAttr(Attribute::SKIP_ANALYSIS)) { continue; } Visitor::Visit( *func, [](Expression&) { return VisitResult::CONTINUE; }, postVisit); converter.VisitValue(*func); } for (auto& importedValue : package.GetImportedVarAndFuncs()) { converter.VisitValue(*importedValue); } for (auto& globalVar : package.GetGlobalVars()) { converter.VisitValue(*globalVar); } for (auto def : package.GetAllCustomTypeDef()) { if (def->TestAttr(Attribute::SKIP_ANALYSIS)) { continue; } converter.VisitDef(*def); } LiftGenericTypes(); for (auto type : builder.GetAllCustomTypes()) { type->ResetAllInstantiatedType(); } } ClassDef* ClosureConversion::GetOrCreateGenericAutoEnvBaseDef(size_t paramNum) { auto className = CHIRMangling::ClosureConversion::GenerateGenericBaseClassMangleName(paramNum); auto it = genericAutoEnvBaseDefs.find(className); if (it != genericAutoEnvBaseDefs.end()) { return it->second; } /* abstract class $Auto_Env_Base_param_n <: core.Object { public func $VirtualFuncInCC(p0: T0, p1: T1, ...): Tn } */ // create generic type params std::vector genericTypeParams; auto paramTypePrefix = "ccbase_p" + std::to_string(paramNum) + "_"; for (size_t i = 0; i < paramNum; ++i) { auto paramTypeName = paramTypePrefix + std::to_string(i); genericTypeParams.emplace_back(builder.GetType(paramTypeName, paramTypeName)); } auto returnTypeName = "ccbase_r" + std::to_string(paramNum); genericTypeParams.emplace_back(builder.GetType(returnTypeName, returnTypeName)); // create class def auto classDef = builder.CreateClass(INVALID_LOCATION, "", className, package.GetName(), true, false); auto classTy = builder.GetType(classDef, genericTypeParams); classDef->SetType(*classTy); // set attribute SetAutoEnvBaseDefAttr(*classDef); classDef->EnableAttr(Attribute::GENERIC); // add abstract method CreateVirtualFuncInAutoEnvBaseDef(*classDef, GENERIC_VIRTUAL_FUNC, genericTypeParams, builder); // cache class def genericAutoEnvBaseDefs.emplace(className, classDef); return classDef; } void ClosureConversion::CreateGenericOverrideMethodInAutoEnvImplDef(ClassDef& autoEnvImplDef, FuncBase& srcFunc, const std::unordered_map& originalTypeToNewType) { // create new func type auto srcFuncParamTypesAndRetType = srcFunc.GetType()->GetTypeArgs(); if (srcFunc.GetFuncKind() == FuncKind::LAMBDA) { // first param in lambda is `env` srcFuncParamTypesAndRetType.erase(srcFuncParamTypesAndRetType.begin()); } auto [newFuncParamTypes, newFuncRetType] = CreateFuncTypeWithOrphanT( srcFunc.GetIdentifierWithoutPrefix(), srcFuncParamTypesAndRetType, originalTypeToNewType, builder); auto classRefTy = builder.GetType(autoEnvImplDef.GetType()); newFuncParamTypes.insert(newFuncParamTypes.begin(), classRefTy); auto newFuncTy = builder.GetType(newFuncParamTypes, newFuncRetType); // create override func auto mangledName = CHIRMangling::ClosureConversion::GenerateGenericOverrideFuncMangleName(srcFunc); auto newFunc = builder.CreateFunc( INVALID_LOCATION, newFuncTy, mangledName, GENERIC_VIRTUAL_FUNC, "", package.GetName()); autoEnvImplDef.AddMethod(newFunc); // set attribute SetMemberMethodAttr(*newFunc, srcFunc.TestAttr(Attribute::CONST)); // create func body auto blockGroup = builder.CreateBlockGroup(*newFunc); newFunc->InitBody(*blockGroup); auto entry = builder.CreateBlock(blockGroup); blockGroup->SetEntryBlock(entry); // create parameters for (auto paramTy : newFuncParamTypes) { builder.CreateParameter(paramTy, INVALID_LOCATION, *newFunc); } // create return value auto retRefTy = builder.GetType(newFuncRetType); auto retVal = CreateAndAppendExpression(builder, retRefTy, newFuncRetType, entry); newFunc->SetReturnValue(*retVal->GetResult()); auto expectedParamTypes = srcFunc.GetFuncType()->GetParamTypes(); for (size_t i = 0; i < expectedParamTypes.size(); ++i) { expectedParamTypes[i] = ReplaceRawGenericArgType(*expectedParamTypes[i], originalTypeToNewType, builder); } auto& params = newFunc->GetParams(); std::vector applyArgs; size_t offset = srcFunc.GetFuncKind() == FuncKind::LAMBDA ? 0 : 1; CJC_ASSERT(expectedParamTypes.size() == params.size() - offset); for (size_t i = offset; i < params.size(); ++i) { /** * we only need to add TypeCast in `$GenericVirtualFunc`, not in `$InstVirtualFunc`, * because we need a $AutoEnvWrapperClass here, we can view an example: * func foo

(x:P) { return () } * func segFault

(lam: ((P) -> Unit) -> Unit): Unit { * lam(foo

) * } * func zoo(t:((Int64) -> Unit)) { * t(0) * } * main() { * segFault(zoo) * } * ---------------- after closure conversion, it should be: * abstract class autoEnv_generic { * open public func $GenericVirtualFunc(a: T1): T2 * } * abstract class autoEnv_inst_Int64_Unit <: autoEnv_generic { * open public func $InstVirtualFunc(a: Int64): Unit * } * abstract class autoEnv_inst_Int64_Unit_Unit <: autoEnv_generic, Unit> { * open public func $InstVirtualFunc(a: autoEnv_inst_Int64_Unit): Unit * } * class autoEnvWrapper <: autoEnv_inst_Int64_Unit_Unit { * let v: autoEnv_generic, Unit> * public func $GenericVirtualFunc(a: T1): T2 { * return v.$GenericVirtualFunc(a) * } * public func $InstVirtualFunc(a: autoEnv_generic): Unit { * return unbox($GenericVirtualFunc(box(a))) * } * } * class autoEnv_inst_Int64_Unit_Unit_Unit <: autoEnv_generic, Unit>, Unit> { * public func $InstVirtualFunc(a: autoEnv_inst_Int64_Unit_Unit): Unit * } * class autoEnv_zoo <: autoEnv_inst_Int64_Unit_Unit_Unit { * public func $GenericVirtualFunc(a: autoEnv_generic, Unit>): T2 { * let w = autoEnvWrapper() // we need a wrapper class here * w.v = a * let p: autoEnv_inst_Int64_Unit_Unit = TypeCast(w) * return box(zoo(p)) * } * public func $InstVirtualFunc(a: autoEnv_inst_Int64_Unit_Unit): Unit { * return zoo(a) * } * } * class autoEnv_foo

<: autoEnv_generic { * public func $GenericVirtualFunc(a: P): T2 { * let r: Unit = foo

(a) * return box(r) * } * } * func segFault

Ev"() gc "cangjie" personality i32 (...)* @"__cj_personality_v0$" { +bb0: + %token = call token (...) @llvm.cj.gc.statepoint(i64 4, i32 0, void ()* @CJ_MCC_StackCheck, i32 0, i32 0) + %0 = alloca %Unit.Type, align 8 + %token2 = call cangjiegccc token (...) @llvm.cj.gc.statepoint(i64 3, i32 0, void ()* @MCC_SafepointStub, i32 0, i32 0) + %token4 = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%Unit.Type*, i8*, %record._ZN8std.core6StringE*)* @_ZN8std.core7printlnER_ZN8std.core6StringE, i32 3, i32 0, %Unit.Type* sret(%Unit.Type) %0, i8* null, %record._ZN8std.core6StringE* bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.4RCxJdy1D-V" to %record._ZN8std.core6StringE*)) + ret i64 0 +} + +define private i32 @"__cj_personality_v0$"(...) { +entry: + ret i32 0 +} diff --git a/llvm/test/CodeGen/X86/CangjieGC/CFile/CFile-mac.ll b/llvm/test/CodeGen/X86/CangjieGC/CFile/CFile-mac.ll new file mode 100644 index 000000000..c63391e71 --- /dev/null +++ b/llvm/test/CodeGen/X86/CangjieGC/CFile/CFile-mac.ll @@ -0,0 +1,41 @@ +; RUN: llc -mtriple=x86_64-apple-macosx --cangjie-pipeline -o - %s | FileCheck %s --check-prefixes=X86 +; RUN: llc -mtriple=arm64-apple-darwin --cangjie-pipeline -o - %s | FileCheck %s --check-prefixes=ARM + +%Unit.Type = type { i8 } +%record._ZN8std.core6StringE = type { %record._ZN8std.core5ArrayIhE, i64 } +%record._ZN8std.core5ArrayIhE = type { i8 addrspace(1)*, i64, i64 } +%KlassInfo.0 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i8*, i64*, i32, [0 x %KlassInfo.0*] } +%BitMap = type { i32, [0 x i8] } +%KlassInfo.1 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i8*, i64*, i32, [1 x %KlassInfo.0*] } + +@"$const_cjstring.4RCxJdy1D-V" = internal constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [54 x i8] }* @"$const_cjstring_data.2kj5CN6GcdE" to i8*) to i8 addrspace(1)*), i64 0, i64 11 }, i64 0 } +@"$const_cjstring_data.2kj5CN6GcdE" = internal constant { i8*, i64, [54 x i8] } { i8* bitcast (%KlassInfo.0* @roUInt8.arrayKlass to i8*), i64 54, [54 x i8] c"hello worldAn exception has occurred: Out of memory" } +@roUInt8.arrayKlass = weak_odr global %KlassInfo.0 { i32 2, i32 1, %BitMap* null, %KlassInfo.0* @array_int8.primKlass, i8** null, i64* null, i64* null, i8* null, i64* null, i32 0, [0 x %KlassInfo.0*] zeroinitializer }, no_sanitize_address +@array_int8.primKlass = weak_odr hidden global %KlassInfo.0 { i32 1, i32 1, %BitMap* null, %KlassInfo.0* null, i8** null, i64* null, i64* null, i8* null, i64* @array_int8.uniqueAddr, i32 0, [0 x %KlassInfo.0*] zeroinitializer }, no_sanitize_address +@array_int8.uniqueAddr = weak_odr hidden global i64 0 + +declare token @llvm.cj.gc.statepoint(...) +declare void @CJ_MCC_StackCheck() +declare void @_ZN8std.core7printlnER_ZN8std.core6StringE(%Unit.Type* sret(%Unit.Type), i8*, %record._ZN8std.core6StringE*) local_unnamed_addr gc "cangjie" + +; X86: leaq ".Lmethod_desc.default.__ZN7default6
Ev"(%rip), %r10 +; X86: pushq %r10 +; ARM: adr x9, Lfunc_begin0 +; ARM: adrp x10, ".Lmethod_desc.default.__ZN7default6
Ev"@PAGE +; ARM: add x10, x10, ".Lmethod_desc.default.__ZN7default6
Ev"@PAGEOFF +; ARM: stp x10, x9, [x29, #-16] +define i64 @"_ZN7default6
Ev"() gc "cangjie" personality i32 (...)* @"__cj_personality_v0$" { +bb0: + %token = call token (...) @llvm.cj.gc.statepoint(i64 4, i32 0, void ()* @CJ_MCC_StackCheck, i32 0, i32 0) + %0 = alloca %Unit.Type, align 8 + %token4 = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%Unit.Type*, i8*, %record._ZN8std.core6StringE*)* @_ZN8std.core7printlnER_ZN8std.core6StringE, i32 3, i32 0, %Unit.Type* sret(%Unit.Type) %0, i8* null, %record._ZN8std.core6StringE* bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.4RCxJdy1D-V" to %record._ZN8std.core6StringE*)) + ret i64 0 +} + +define private i32 @"__cj_personality_v0$"(...) { +entry: + ret i32 0 +} + +!llvm.module.flags = !{!0} +!0 = !{i32 2, !"Cangjie_PACKAGE_ID", !"default"} diff --git a/llvm/test/CodeGen/X86/CangjieGC/CFile/emit-stringpool.ll b/llvm/test/CodeGen/X86/CangjieGC/CFile/emit-stringpool.ll new file mode 100644 index 000000000..a0a9194f0 --- /dev/null +++ b/llvm/test/CodeGen/X86/CangjieGC/CFile/emit-stringpool.ll @@ -0,0 +1,17 @@ +; REQUIRES: x86_64-linux +; RUN: llc -mtriple=x86_64-- --cangjie-pipeline -o - %s | FileCheck %s + +define void @main() gc "cangjie" personality i32 (...)* @"__cj_personality_v0$" { +entry: + ret void +} + +define private i32 @"__cj_personality_v0$"(...) { +entry: + ret i32 0 +} + +; CHECK: .section .cjmetadata.stringpooldict,"aw",@progbits +; CHECK: .Lstr_pool_dict_offsets: +; CHECK: .long .Lstr_pool_dict.1-.Lstr_pool_dict_offsets +; CHECK: .Lstr_pool_dict.1: diff --git a/llvm/test/CodeGen/X86/CangjieGC/atomic.ll b/llvm/test/CodeGen/X86/CangjieGC/atomic.ll new file mode 100644 index 000000000..22e076695 --- /dev/null +++ b/llvm/test/CodeGen/X86/CangjieGC/atomic.ll @@ -0,0 +1,59 @@ +; RUN: llc --cangjie-pipeline -mtriple=x86_64 < %s | FileCheck %s +; XFAIL: * + +define internal void @func_atomic_store(i8 addrspace(1)* %this, i8 addrspace(1)* %that) gc "cangjie" { +entry: +; CHECK: func_atomic_store +; CHECK: movq 8(%r15), %rax +; CHECK: movl (%rax), %eax +; CHECK: cmpl $9, %eax +; CHECK: gcNoRunning +; CHECK: xchgq %rdi, (%rsi) +; CHECK: gcRunning +; CHECK: callq CJ_MCC_AtomicWriteReference@PLT + + %0 = bitcast i8 addrspace(1)* %that to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.atomic.store(i8 addrspace(1)* %this, i8 addrspace(1)* %that, i8 addrspace(1)* addrspace(1)* %0, i32 5) + ret void +} + +define internal void @func_atomic_compare_swap(i8 addrspace(1)* %this, i8 addrspace(1)* %that) gc "cangjie" { +entry: +; CHECK: func_atomic_compare_swap +; CHECK: movq 8(%r15), %rax +; CHECK: movl (%rax), %eax +; CHECK: cmpl $9, %eax +; CHECK: gcNoRunning +; CHECK: lock cmpxchgq %rsi, (%rsi) +; CHECK: gcRunning +; CHECK: callq CJ_MCC_CompareAndSwapReference@PLT + + %0 = bitcast i8 addrspace(1)* %that to i8 addrspace(1)* addrspace(1)* + %1 = call i1 @llvm.cj.atomic.compare.swap(i8 addrspace(1)* %this, i8 addrspace(1)* %that, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %0, i32 5, i32 5) + ret void +} + +define internal void @func_atomic_swap(i8 addrspace(1)* %this, i8 addrspace(1)* %that) gc "cangjie" { +entry: +; CHECK: func_atomic_swap +; CHECK: movq 8(%r15), %rax +; CHECK: movl (%rax), %eax +; CHECK: cmpl $9, %eax +; CHECK: gcNoRunning +; CHECK: xchgq %rdi, (%rsi) +; CHECK: gcRunning +; CHECK: callq CJ_MCC_AtomicSwapReference@PLT + + %0 = bitcast i8 addrspace(1)* %that to i8 addrspace(1)* addrspace(1)* + %1 = call i8 addrspace(1)* @llvm.cj.atomic.swap(i8 addrspace(1)* %this, i8 addrspace(1)* %that, i8 addrspace(1)* addrspace(1)* %0, i32 5) + ret void +} + +; Function Attrs: nounwind +declare void @llvm.cj.atomic.store(i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*, i32) + +; Function Attrs: nounwind +declare i8 addrspace(1)* @llvm.cj.atomic.swap(i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*, i32) + +; Function Attrs: nounwind +declare i1 @llvm.cj.atomic.compare.swap(i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*, i32, i32) diff --git a/llvm/test/CodeGen/X86/CangjieGC/loop-state.ll b/llvm/test/CodeGen/X86/CangjieGC/loop-state.ll new file mode 100644 index 000000000..6558b2dd3 --- /dev/null +++ b/llvm/test/CodeGen/X86/CangjieGC/loop-state.ll @@ -0,0 +1,38 @@ +; RUN: llc --cangjie-pipeline -mtriple=x86_64 -cj-gcstate-dup-loop=true < %s | FileCheck %s + +define void @foo1(i8 addrspace(1)* %arg0, i64 %arg1, i8 addrspace(1)* %arg2) gc "cangjie" { +; CHECK: foo1 +; CHECK: %entry +; CHECK: movq 8(%r15), %rax +; CHECK: movl (%rax), %eax +; CHECK: cmpl $8, %eax +; CHECK: %arr.end1 +; CHECK: movq %r14, (%r13) +; CHECK: %gcNoRunning +; CHECK: movq %r14, (%r13) +; CHECK: %arr.end1.pin +; CHECK: movq 8(%r15), %rax +; CHECK: movl (%rax), %eax +; CHECK: cmpl $9, %eax +; CHECK: %gcRunning +; CHECK: callq CJ_MCC_WriteRefField@PLT + +entry: + %a = bitcast i8 addrspace(1)* %arg0 to i8 addrspace(1)* addrspace(1) * + br label %loop.preheader + +loop.preheader: + br label %arr.end1 + +arr.end1: + %i = phi i64 [ %add.i, %arr.end1 ], [ 0, %loop.preheader] + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %arg2, i8 addrspace(1)* %arg0, i8 addrspace(1)* addrspace(1)* %a) + %add.i = add i64 %i, 1 + %icmpslt = icmp slt i64 %add.i, %arg1 + br i1 %icmpslt, label %arr.end1, label %loopexit + +loopexit: + ret void +} + +declare void @llvm.cj.gcwrite.ref(i8 addrspace(1)*, i8 addrspace(1)* nocapture, i8 addrspace(1)* addrspace(1)* nocapture) diff --git a/llvm/test/CodeGen/X86/CangjieGC/no-stack-trace.ll b/llvm/test/CodeGen/X86/CangjieGC/no-stack-trace.ll new file mode 100644 index 000000000..d174d772a --- /dev/null +++ b/llvm/test/CodeGen/X86/CangjieGC/no-stack-trace.ll @@ -0,0 +1,15 @@ +; RUN: llc --cangjie-pipeline -mtriple=x86_64 -no-stacktrace-info < %s | FileCheck %s +; RUN: llc --cangjie-pipeline -mtriple=x86_64 -stack-trace-format=simple < %s + +; CHECK: .Lmethod_desc.cj_entry$: +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long .Lfunc_end0-.Lfunc_begin0 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 0 + +define i32 @"cj_entry$"() #11 gc "cangjie" { +entry: + ret i32 2 +} diff --git a/llvm/test/CodeGen/X86/CangjieGC/test-stack-size.ll b/llvm/test/CodeGen/X86/CangjieGC/test-stack-size.ll new file mode 100644 index 000000000..54e71126f --- /dev/null +++ b/llvm/test/CodeGen/X86/CangjieGC/test-stack-size.ll @@ -0,0 +1,55 @@ +; Warn. +; RUN: llc -mtriple=x86_64 < %s 2>&1 | FileCheck %s + +; CHECK: warning: stack frame size (4194232) exceeds limit (1048576) in function 'main1' + +define i32 @main1() gc "cangjie" { +mainEntry: + + %array = alloca [1048590 x i32], align 4 + + %gep = getelementptr [1048590 x i32], [1048590 x i32]* %array, i32 0, i32 0 + + store i32 1, i32* %gep, align 4 + + %gep1 = getelementptr [1048590 x i32], [1048590 x i32]* %array, i32 0, i32 1 + + store i32 2, i32* %gep1, align 4 + + %gep2 = getelementptr [1048590 x i32], [1048590 x i32]* %array, i32 0, i32 2 + + store i32 3, i32* %gep2, align 4 + + %arr_index_pointer = getelementptr [1048590 x i32], [1048590 x i32]* %array, i32 0, i32 1 + + %a = load i32, i32* %arr_index_pointer, align 4 + + ret i32 %a +} + +; CHECK-NOT: warning: stack frame size + +; No warn. +define i32 @main2() gc "cangjie" { +mainEntry: + + %array = alloca [100 x i32], align 4 + + %gep = getelementptr [100 x i32], [100 x i32]* %array, i32 0, i32 0 + + store i32 1, i32* %gep, align 4 + + %gep1 = getelementptr [100 x i32], [100 x i32]* %array, i32 0, i32 1 + + store i32 2, i32* %gep1, align 4 + + %gep2 = getelementptr [100 x i32], [100 x i32]* %array, i32 0, i32 2 + + store i32 3, i32* %gep2, align 4 + + %arr_index_pointer = getelementptr [100 x i32], [100 x i32]* %array, i32 0, i32 1 + + %a = load i32, i32* %arr_index_pointer, align 4 + + ret i32 %a +} diff --git a/llvm/test/CodeGen/X86/cj-stack-grow.ll b/llvm/test/CodeGen/X86/cj-stack-grow.ll new file mode 100644 index 000000000..5ecf25237 --- /dev/null +++ b/llvm/test/CodeGen/X86/cj-stack-grow.ll @@ -0,0 +1,46 @@ +; RUN: llc -O0 --cangjie-pipeline -mtriple x86_64-pc-linux-gnu < %s | FileCheck %s + +%record = type { i64, i8 addrspace(1)* } + +declare void @cj_stack_grow() + +declare void @test(i8 addrspace(1)*, %record addrspace(1)*) #0 gc "cangjie" + +define i64 @foo(i8 addrspace(1)* %a) #0 gc "cangjie" { +; CHECK: subq $24, %rsp +; CHECK-NEXT: leaq -24(%rbp), %rdi +; CHECK-NEXT: xorl %esi, %esi +; CHECK-NEXT: movl $16, %edx +; CHECK-NEXT: callq memset@PLT +; CHECK-NEXT: # %bb.1: # %body +; CHECK-NEXT: callq cj_stack_grow@PLT +; CHECK-LABEL: .Ltmp1: +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: movl %eax, %edi +; CHECK-NEXT: leaq -24(%rbp), %rsi +; CHECK-NEXT: callq test@PLT + +entry: + %r = alloca %record, align 8 + %r.cast = bitcast %record* %r to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %r.cast, i8 0, i64 16, i1 false) + br label %body + +body: ; preds = %entry + %token = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @cj_stack_grow, i32 0, i32 0) [ "struct-live"(%record* %r) ] + %var = addrspacecast %record* %r to %record addrspace(1)* + %token1 = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record addrspace(1)*)* @test, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %var) [ "struct-live"(%record* %r) ] + ret i64 0 +} + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn writeonly +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #1 + +declare token @llvm.cj.gc.statepoint(...) + +attributes #0 = { "hasRcdParam" } +attributes #1 = { argmemonly nocallback nofree nounwind willreturn writeonly } + +!llvm.module.flags = !{!0} + +!0 = !{i32 2, !"HasRewrittenStatepoint", i32 1} diff --git a/llvm/test/CodeGen/X86/cj-stack-grow1.ll b/llvm/test/CodeGen/X86/cj-stack-grow1.ll new file mode 100644 index 000000000..a6e045136 --- /dev/null +++ b/llvm/test/CodeGen/X86/cj-stack-grow1.ll @@ -0,0 +1,56 @@ +; RUN: llc -O2 --cangjie-pipeline -mtriple x86_64-pc-linux-gnu < %s | FileCheck %s + +%record = type { i64, i8 addrspace(1)* } + +declare void @cj_stack_grow() + +declare void @test(i8 addrspace(1)*, %record addrspace(1)*) #0 gc "cangjie" + +define i64 @foo(i8 addrspace(1)* %a) #0 gc "cangjie" { +; CHECK: subq $24, %rsp +; CHECK-NEXT: callq cj_stack_grow@PLT +; CHECK-LABEL: .Ltmp1: +; CHECK-NEXT: xorps %xmm0, %xmm0 +; CHECK-NEXT: movaps %xmm0, -32(%rbp) +; CHECK-NEXT: leaq -32(%rbp), %rsi +; CHECK-NEXT: xorl %edi, %edi +; CHECK-NEXT: callq test@PLT +; CHECK-LABEL: .Ltmp2: + +; CHECK-LABEL: .Lstack_map.foo: +; CHECK: .long .Ltmp2-foo +; CHECK-NEXT: #[RegIdx: -1, SlotIdx: 0, LNIdx: -1, DerivedStartIdx: -1, SPRegIdx: -1, SPSlotIdx: -1] +; CHECK-NEXT: .byte 2 +; CHECK-NEXT: #RegNums: 0 +; CHECK-NEXT: #SlotsNums: 1 +; CHECK-NEXT: #Idx[0]: BaseOffset: -24, SlotBits: 0x1[ -24 ] + +entry: + %r = alloca %record, align 8 + %r.cast = bitcast %record* %r to i8* + %var = addrspacecast %record* %r to %record addrspace(1)* + br label %body + +body: ; preds = %entry + %token = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @cj_stack_grow, i32 0, i32 0) + call void @llvm.lifetime.start.p0i8(i64 16, i8* %r.cast) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %r.cast, i8 0, i64 16, i1 false) + %token1 = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record addrspace(1)*)* @test, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %var) [ "struct-live"(%record* %r) ] + ret i64 0 +} + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn writeonly +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #1 + +; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2 + +declare token @llvm.cj.gc.statepoint(...) + +attributes #0 = { "hasRcdParam" } +attributes #1 = { argmemonly nocallback nofree nounwind willreturn writeonly } +attributes #2 = { argmemonly nocallback nofree nosync nounwind willreturn } + +!llvm.module.flags = !{!0} + +!0 = !{i32 2, !"HasRewrittenStatepoint", i32 1} diff --git a/llvm/test/CodeGen/X86/cj-stack-grow2.ll b/llvm/test/CodeGen/X86/cj-stack-grow2.ll new file mode 100644 index 000000000..cb731dcbc --- /dev/null +++ b/llvm/test/CodeGen/X86/cj-stack-grow2.ll @@ -0,0 +1,48 @@ +; RUN: llc -O0 --cangjie-pipeline -mtriple x86_64-pc-linux-gnu < %s | FileCheck %s + +%Unit.Type = type { i8 } +%TypeInfo = type { i8*, i8, i8, i16, i32, %BitMap*, i32, i8, i8, i32*, i8*, i8*, i8*, %TypeInfo*, i8*, i8* } +%BitMap = type { i32, [0 x i8] } +%string = type { %record, i64 } +%record = type { i8 addrspace(1)*, i64, i64 } +declare void @cj_stack_grow() + +define internal void @test_vec_use_fp(%record* nocapture sret(%record) %0, i8 addrspace(1)* nocapture %1, %TypeInfo* nocapture %2, %string* %3, i64 %4, <2 x i64> %5, void (%Unit.Type*, i8 addrspace(1)*, %record*)* %6) #14 gc "cangjie" { +; CHECK-LABEL: .Lstack.check.end0: +; CHECK-NEXT: movq %rdi, -40(%rbp) +; CHECK-NEXT: movdqu %xmm0, -24(%rbp) +; CHECK-NEXT: # %bb.1: +; CHECK-NEXT: leaq -32(%rbp), %rax +; CHECK-NEXT: movq %rax, -48(%rbp) +; CHECK-NEXT: callq cj_stack_grow@PLT +; CHECK-LABEL: .Ltmp1: +; CHECK-LABEL: .Ltmp2: +; CHECK: .long .Ltmp1-test_vec_use_fp +; CHECK-NEXT: #[RegIdx: -1, SlotIdx: -1, LNIdx: -1, DerivedStartIdx: -1, SPRegIdx: -1, SPSlotIdx: 0] +; CHECK: .long .Ltmp2-test_vec_use_fp +; CHECK-NEXT: #[RegIdx: -1, SlotIdx: -1, LNIdx: -1, DerivedStartIdx: -1, SPRegIdx: 0, SPSlotIdx: -1] +; CHECK: #RegNums: 1 +; CHECK: #Idx[0]: (0x236=566), rdx, rcx, rsi, rdi, r9 +; CHECK: #SlotsNums: 1 +; CHECK: #Idx[0]: BaseOffset: -40, SlotBits: 0x3[ -40 -48 ] +; CHECK: #LineNumbersNums: 0 +; CHECK: #DerivedInfoNums: 0 +entry: + %token = call cangjiegccc token (...) @llvm.cj.gc.statepoint(i64 4, i32 0, void ()* @CJ_MCC_StackCheck, i32 0, i32 0) + %7 = alloca %record, align 8 + %sroa = getelementptr inbounds %record, %record* %7, i64 0, i32 1 + %8 = bitcast i64* %sroa to <2 x i64>* + store <2 x i64> %5, <2 x i64>* %8, align 8 + br label %body + +body: + %r.cast = bitcast %record* %7 to i8* + %token1 = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @cj_stack_grow, i32 0, i32 0) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %r.cast, i8 0, i64 16, i1 false) + ret void +} + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn writeonly +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #1 +declare void @CJ_MCC_StackCheck() +declare token @llvm.cj.gc.statepoint(...) \ No newline at end of file diff --git a/llvm/test/CodeGen/X86/statepoint-cmp-sunk-past-statepoint.ll b/llvm/test/CodeGen/X86/statepoint-cmp-sunk-past-statepoint.ll new file mode 100644 index 000000000..9c881dd81 --- /dev/null +++ b/llvm/test/CodeGen/X86/statepoint-cmp-sunk-past-statepoint.ll @@ -0,0 +1,113 @@ +; RUN: llc -max-registers-for-gc-values=256 -verify-machineinstrs -stop-after twoaddressinstruction < %s | FileCheck --check-prefixes CHECK,CHECK-LV %s +; RUN: llc -max-registers-for-gc-values=256 -verify-machineinstrs -stop-after twoaddressinstruction -early-live-intervals < %s | FileCheck --check-prefixes CHECK,CHECK-LIS %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128-ni:1-p2:32:8:8:32-ni:2" +target triple = "x86_64-unknown-linux-gnu" + +declare void @foo() gc "statepoint-example" +declare void @bar(i8 addrspace(1)*) gc "statepoint-example" + +declare i32* @fake_personality_function() + +; Simplest possible test demonstrating the problem + +; CHECK-LABEL: name: test +; CHECK: bb.0 +; CHECK-LV: %0:gr64 = COPY killed $rdi +; CHECK-LIS: %0:gr64 = COPY $rdi +; CHECK: %1:gr64 = COPY %0 +; CHECK: %1:gr64 = STATEPOINT 2, 5, 0, undef %2:gr64, 2, 0, 2, 0, 2, 0, 2, 1, %1(tied-def 0), 2, 0, 2, 1, 0, 0 +; CHECK-LV: TEST64rr killed %0, %0, implicit-def $eflags +; CHECK-LIS: TEST64rr %0, %0, implicit-def $eflags +; CHECK: JCC_1 %bb.2, 4, implicit killed $eflags +; CHECK: JMP_1 %bb.1 +; CHECK: bb.1 +; CHECK-LV: $rdi = COPY killed %1 +; CHECK-LV: STATEPOINT 2, 5, 1, undef %3:gr64, killed $rdi, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0 +; CHECK-LIS: $rdi = COPY %1 +; CHECK-LIS: STATEPOINT 2, 5, 1, undef %3:gr64, $rdi, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0 +; CHECK: RET 0 +; CHECK: bb.2 +; CHECK: RET 0 +define void @test(i8 addrspace(1)* %a) gc "statepoint-example" { +entry: + %not7 = icmp eq i8 addrspace(1)* %a, null + %statepoint_token1745 = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2, i32 5, void ()* nonnull elementtype(void ()) @foo, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(i8 addrspace(1)* %a) ] + br i1 %not7, label %zero, label %not_zero + +not_zero: + %a.relocated = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %statepoint_token1745, i32 0, i32 0) ; (%a, %a) + %statepoint_token1752 = call token (i64, i32, void (i8 addrspace(1)*)*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidp1i8f(i64 2, i32 5, void (i8 addrspace(1)*)* nonnull elementtype(void (i8 addrspace(1)*)) @bar, i32 1, i32 0, i8 addrspace(1)* %a.relocated, i32 0, i32 0) [ "deopt"(), "gc-live"() ] + ret void + +zero: + ret void +} + +; A bit more complex test, where both registers are used in same successor BB + +; CHECK-LABEL: name: test2 +; CHECK: bb.2 +; CHECK: %1:gr64 = STATEPOINT 2882400000, 0, 0, undef %11:gr64, 2, 0, 2, 0, 2, 0, 2, 1, %1(tied-def 0), 2, 0, 2, 1, 0, 0, csr_64 +; CHECK: %10:gr64 = COPY %1 +; CHECK: %10:gr64 = STATEPOINT 2882400000, 0, 0, undef %13:gr64, 2, 0, 2, 0, 2, 0, 2, 1, %10(tied-def 0), 2, 0, 2, 1, 0, 0, csr_64 +; CHECK: JMP_1 %bb.3 +; CHECK: bb.3 +; CHECK: %18:gr8 = COPY %17.sub_8bit +; CHECK-LV: TEST8rr killed %18, %18, implicit-def $eflags +; CHECK-LIS: TEST8rr %18, %18, implicit-def $eflags +; CHECK: JCC_1 %bb.5, 5, implicit killed $eflags +; CHECK: JMP_1 %bb.4 +; CHECK: bb.4 +; CHECK: bb.5 +; CHECK: %4:gr64 = LEA64r %10, 1, $noreg, 8, $noreg +; CHECK-LV: %3:gr64 = COPY killed %10 +; CHECK-LIS: %3:gr64 = COPY %10 +; CHECK-LV: TEST64rr killed %1, %1, implicit-def $eflags +; CHECK: JCC_1 %bb.1, 5, implicit killed $eflags +; CHECK: JMP_1 %bb.6 +define void @test2(i8 addrspace(1)* %this, i32 %0, i32 addrspace(1)* %p0, i8 addrspace(1)* %p1) gc "statepoint-example" personality i32* ()* @fake_personality_function { +preheader: + br label %loop.head + +loop.head: + %phi1 = phi i32 addrspace(1)* [ %p0, %preheader ], [ %addr.i.i46797.remat64523, %tail ] + %v1 = phi i8 addrspace(1)* [ %p1, %preheader ], [ %v3, %tail ] + %not3= icmp ne i32 addrspace(1)* %phi1, null + br i1 %not3, label %BB1, label %BB3 + +BB3: + %token1 = call token (i64, i32, i8 addrspace(1)* ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_p1i8f(i64 2882400000, i32 0, i8 addrspace(1)* ()* elementtype(i8 addrspace(1)* ()) undef, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(i8 addrspace(1)* %v1) ] + %v2 = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %token1, i32 0, i32 0) ; (%v1, %v1) + %cond = icmp eq i8 addrspace(1)* null, %v2 + %token2 = invoke token (i64, i32, i8 addrspace(1)* ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_p1i8f(i64 2882400000, i32 0, i8 addrspace(1)* ()* elementtype(i8 addrspace(1)* ()) undef, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(i8 addrspace(1)* %v2, i32 addrspace(1)* %phi1) ] + to label %BB2 unwind label %BB6 + +BB2: + %v3 = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %token2, i32 0, i32 0) ; (%v2, %v2) + %.remat64522 = getelementptr inbounds i8, i8 addrspace(1)* %v3, i64 8 + %addr.i.i46797.remat64523 = bitcast i8 addrspace(1)* %.remat64522 to i32 addrspace(1)* + br i1 undef, label %BB4, label %tail + +BB4: + %dummy = ptrtoint i64* undef to i64 + br label %tail + +tail: + br i1 %cond, label %BB1, label %loop.head + +BB1: + ret void + +BB6: + %lpad.split-lp = landingpad token + cleanup + ret void + +} + + +declare i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token, i32 immarg, i32 immarg) #5 +declare token @llvm.experimental.gc.statepoint.p0f_p1i8f(i64 immarg, i32 immarg, i8 addrspace(1)* ()*, i32 immarg, i32 immarg, ...) +declare token @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 immarg, i32 immarg, void ()*, i32 immarg, i32 immarg, ...) +declare token @llvm.experimental.gc.statepoint.p0f_isVoidp1i8f(i64 immarg, i32 immarg, void (i8 addrspace(1)*)*, i32 immarg, i32 immarg, ...) diff --git a/llvm/test/CodeGen/X86/statepoint-live-in.ll b/llvm/test/CodeGen/X86/statepoint-live-in.ll index 0647c0b0e..9dfd31ebd 100644 --- a/llvm/test/CodeGen/X86/statepoint-live-in.ll +++ b/llvm/test/CodeGen/X86/statepoint-live-in.ll @@ -372,8 +372,8 @@ define void @test10(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 ; CHECK-NEXT: .cfi_offset %r14, -32 ; CHECK-NEXT: .cfi_offset %r15, -24 ; CHECK-NEXT: .cfi_offset %rbp, -16 -; CHECK-NEXT: movl %r9d, %r15d -; CHECK-NEXT: movl %r8d, %r14d +; CHECK-NEXT: movl %r9d, %r14d +; CHECK-NEXT: movl %r8d, %r15d ; CHECK-NEXT: movl %ecx, %r12d ; CHECK-NEXT: movl %edx, %r13d ; CHECK-NEXT: movl %esi, %ebx diff --git a/llvm/test/CodeGen/X86/statepoint-ra.ll b/llvm/test/CodeGen/X86/statepoint-ra.ll index 25e2f6a11..f783b6e46 100644 --- a/llvm/test/CodeGen/X86/statepoint-ra.ll +++ b/llvm/test/CodeGen/X86/statepoint-ra.ll @@ -12,19 +12,15 @@ target triple = "x86_64-unknown-linux-gnu" ;YAML: Args: ;YAML: - NumSpills: '10' ;YAML: - String: ' spills ' -;YAML: - TotalSpillsCost: '7.000000e+00' +;YAML: - TotalSpillsCost: '8.500000e+00' ;YAML: - String: ' total spills cost ' ;YAML: - NumReloads: '7' ;YAML: - String: ' reloads ' ;YAML: - TotalReloadsCost: '3.109004e-15' ;YAML: - String: ' total reloads cost ' -;YAML: - NumZeroCostFoldedReloads: '20' +;YAML: - NumZeroCostFoldedReloads: '22' ;YAML: - String: ' zero cost folded reloads ' -;YAML: - NumVRCopies: '2' -;YAML: - String: ' virtual registers copies ' -;YAML: - TotalCopiesCost: '8.882868e-16' -;YAML: - String: ' total copies cost ' -;YAML: - String: generated in function + define void @barney(ptr addrspace(1) %arg, double %arg1, double %arg2, double %arg3, double %arg4, double %arg5, double %arg6, double %arg7, double %arg8, double %arg9, double %arg10, double %arg11, double %arg12) gc "statepoint-example" personality ptr @widget { bb: @@ -69,53 +65,51 @@ declare token @llvm.experimental.gc.statepoint.p0(i64 , i32 , ptr, i32 , i32 , . ;CHECK: bb.0.bb: ;CHECK: successors: %bb.2(0x40000000), %bb.1(0x40000000) ;CHECK: liveins: $rdi, $xmm0, $xmm1, $xmm2, $xmm3, $xmm4, $xmm5, $xmm6, $xmm7 -;CHECK: %49:fr64 = COPY $xmm7 +;CHECK: MOVSDmr %stack.3, 1, $noreg, 0, $noreg, $xmm7 :: (store (s64) into %stack.3) ;CHECK: %10:fr64 = COPY $xmm6 ;CHECK: %41:fr64 = COPY $xmm5 -;CHECK: %45:fr64 = COPY $xmm4 -;CHECK: %53:fr64 = COPY $xmm3 +;CHECK: %44:fr64 = COPY $xmm4 +;CHECK: MOVSDmr %stack.4, 1, $noreg, 0, $noreg, $xmm3 :: (store (s64) into %stack.4) ;CHECK: %6:fr64 = COPY $xmm2 -;CHECK: %58:fr64 = COPY $xmm1 -;CHECK: %62:fr64 = COPY $xmm0 +;CHECK: %52:fr64 = COPY $xmm1 +;CHECK: %55:fr64 = COPY $xmm0 ;CHECK: %3:gr64 = COPY $rdi -;CHECK: %76:fr64 = MOVSDrm_alt %fixed-stack.0, 1, $noreg, 0, $noreg :: (load (s64) from %fixed-stack.0) +;CHECK: %67:fr64 = MOVSDrm_alt %fixed-stack.0, 1, $noreg, 0, $noreg :: (load (s64) from %fixed-stack.0) +;CHECK: MOVSDmr %stack.7, 1, $noreg, 0, $noreg, %67 :: (store (s64) into %stack.7) ;CHECK: %14:fr64 = MOVSDrm_alt %fixed-stack.1, 1, $noreg, 0, $noreg :: (load (s64) from %fixed-stack.1, align 16) -;CHECK: %66:fr64 = MOVSDrm_alt %fixed-stack.2, 1, $noreg, 0, $noreg :: (load (s64) from %fixed-stack.2) -;CHECK: %71:fr64 = MOVSDrm_alt %fixed-stack.3, 1, $noreg, 0, $noreg :: (load (s64) from %fixed-stack.3, align 16) +;CHECK: %58:fr64 = MOVSDrm_alt %fixed-stack.2, 1, $noreg, 0, $noreg :: (load (s64) from %fixed-stack.2) +;CHECK: %62:fr64 = MOVSDrm_alt %fixed-stack.3, 1, $noreg, 0, $noreg :: (load (s64) from %fixed-stack.3, align 16) ;CHECK: MOV64mr %stack.0, 1, $noreg, 0, $noreg, %3 :: (store (s64) into %stack.0) ;CHECK: ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp -;CHECK: STATEPOINT 2882400000, 0, 0, target-flags(x86-plt) @blam, 2, 9, 2, 0, 2, 59, 2, 0, 2, 1, 2, 0, 2, 0, 2, 0, 2, 26, 2, 0, 2, 0, 1, 8, %stack.0, 0, 2, 4, %62, 2, 7, 2, 0, 2, 4, %58, 2, 7, 2, 0, 2, 4, %6, 2, 7, 2, 0, 2, 4, %53, 2, 7, 2, 0, 2, 4, %45, 2, 7, 2, 0, 2, 4, %41, 2, 7, 2, 0, 2, 4, %10, 2, 7, 2, 0, 2, 4, %49, 2, 7, 2, 0, 2, 4, %71, 2, 7, 2, 0, 2, 4, %66, 2, 7, 2, 0, 2, 4, %14, 2, 7, 2, 0, 2, 4, %76, 2, 7, 2, 0, 2, 7, 2, 0, 2, 1, 1, 8, %stack.0, 0, 2, 0, 2, 1, 0, 0, csr_64_mostregs, implicit-def $rsp, implicit-def $ssp :: (volatile load store (s64) on %stack.0) +;CHECK: STATEPOINT 2882400000, 0, 0, target-flags(x86-plt) @blam, 2, 9, 2, 0, 2, 59, 2, 0, 2, 1, 2, 0, 2, 0, 2, 0, 2, 26, 2, 0, 2, 0, 1, 8, %stack.0, 0, 2, 4, %55, 2, 7, 2, 0, 2, 4, %52, 2, 7, 2, 0, 2, 4, %6, 2, 7, 2, 0, 2, 4, 1, 8, %stack.4, 0, 2, 7, 2, 0, 2, 4, %44, 2, 7, 2, 0, 2, 4, %41, 2, 7, 2, 0, 2, 4, %10, 2, 7, 2, 0, 2, 4, 1, 8, %stack.3, 0, 2, 7, 2, 0, 2, 4, %62, 2, 7, 2, 0, 2, 4, %58, 2, 7, 2, 0, 2, 4, %14, 2, 7, 2, 0, 2, 4, 1, 8, %fixed-stack.0, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 1, 1, 8, %stack.0, 0, 2, 0, 2, 1, 0, 0, csr_64_mostregs, implicit-def $rsp, implicit-def $ssp :: (volatile load store (s64) on %stack.0), (load (s64) from %stack.3), (load (s64) from %stack.4), (load (s64) from %fixed-stack.0) ;CHECK: ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp ;CHECK: %17:gr32 = MOV32r0 implicit-def dead $eflags ;CHECK: TEST8rr %17.sub_8bit, %17.sub_8bit, implicit-def $eflags ;CHECK: MOVSDmr %stack.1, 1, $noreg, 0, $noreg, %41 :: (store (s64) into %stack.1) -;CHECK: MOVSDmr %stack.2, 1, $noreg, 0, $noreg, %45 :: (store (s64) into %stack.2) -;CHECK: MOVSDmr %stack.5, 1, $noreg, 0, $noreg, %58 :: (store (s64) into %stack.5) -;CHECK: MOVSDmr %stack.6, 1, $noreg, 0, $noreg, %62 :: (store (s64) into %stack.6) +;CHECK: MOVSDmr %stack.2, 1, $noreg, 0, $noreg, %44 :: (store (s64) into %stack.2) +;CHECK: MOVSDmr %stack.5, 1, $noreg, 0, $noreg, %52 :: (store (s64) into %stack.5) +;CHECK: MOVSDmr %stack.6, 1, $noreg, 0, $noreg, %55 :: (store (s64) into %stack.6) ;CHECK: JCC_1 %bb.2, 4, implicit killed $eflags ;CHECK: bb.1: ;CHECK: successors: %bb.3(0x80000000) -;CHECK: %54:fr64 = MOVSDrm_alt $rip, 1, $noreg, %const.0, $noreg :: (load (s64) from constant-pool) -;CHECK: MOVSDmr %stack.3, 1, $noreg, 0, $noreg, %54 :: (store (s64) into %stack.3) -;CHECK: MOVSDmr %stack.4, 1, $noreg, 0, $noreg, %54 :: (store (s64) into %stack.4) -;CHECK: MOVSDmr %stack.7, 1, $noreg, 0, $noreg, %54 :: (store (s64) into %stack.7) +;CHECK: %49:fr64 = MOVSDrm_alt $rip, 1, $noreg, %const.0, $noreg :: (load (s64) from constant-pool) +;CHECK: MOVSDmr %stack.3, 1, $noreg, 0, $noreg, %49 :: (store (s64) into %stack.3) +;CHECK: MOVSDmr %stack.4, 1, $noreg, 0, $noreg, %49 :: (store (s64) into %stack.4) +;CHECK: MOVSDmr %stack.7, 1, $noreg, 0, $noreg, %49 :: (store (s64) into %stack.7) ;CHECK: JMP_1 %bb.3 ;CHECK: bb.2.bb13: ;CHECK: successors: %bb.3(0x80000000) ;CHECK: ADJCALLSTACKDOWN64 8, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp ;CHECK: MOVSDmr $rsp, 1, $noreg, 0, $noreg, %14 :: (store (s64) into stack) ;CHECK: dead $edi = MOV32r0 implicit-def dead $eflags, implicit-def $rdi -;CHECK: $xmm0 = COPY %62 -;CHECK: $xmm1 = COPY %58 +;CHECK: $xmm0 = COPY %55 +;CHECK: $xmm1 = COPY %52 ;CHECK: $xmm2 = COPY %6 -;CHECK: $xmm3 = COPY %45 +;CHECK: $xmm3 = COPY %44 ;CHECK: $xmm4 = COPY %41 ;CHECK: $xmm5 = COPY %10 -;CHECK: $xmm6 = COPY %71 -;CHECK: $xmm7 = COPY %66 -;CHECK: MOVSDmr %stack.3, 1, $noreg, 0, $noreg, %49 :: (store (s64) into %stack.3) -;CHECK: MOVSDmr %stack.4, 1, $noreg, 0, $noreg, %53 :: (store (s64) into %stack.4) -;CHECK: MOVSDmr %stack.7, 1, $noreg, 0, $noreg, %76 :: (store (s64) into %stack.7) +;CHECK: $xmm6 = COPY %62 +;CHECK: $xmm7 = COPY %58 ;CHECK: STATEPOINT 2, 5, 9, undef %22:gr64, $rdi, $xmm0, $xmm1, $xmm2, $xmm3, $xmm4, $xmm5, $xmm6, $xmm7, 2, 0, 2, 0, 2, 59, 2, 0, 2, 2, 2, 0, 2, 70, 2, 0, 2, 26, 2, 0, 2, 0, 2, 0, 2, 4, 1, 8, %stack.6, 0, 2, 7, 2, 0, 2, 4, 1, 8, %stack.5, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 4, 1, 8, %stack.4, 0, 2, 7, 2, 0, 2, 4, 1, 8, %stack.2, 0, 2, 7, 2, 0, 2, 4, 1, 8, %stack.1, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 4, 1, 8, %stack.3, 0, 2, 7, 2, 0, 2, 4, 1, 8, %fixed-stack.3, 0, 2, 7, 2, 0, 2, 4, 1, 8, %fixed-stack.2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 4, 1, 8, %fixed-stack.0, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 1, 2, 0, 2, 0, 2, 1, 0, 0, csr_64, implicit-def $rsp, implicit-def $ssp, implicit-def dead $eax :: (load (s64) from %stack.1), (load (s64) from %stack.2), (load (s64) from %stack.3), (load (s64) from %stack.4), (load (s64) from %stack.5), (load (s64) from %stack.6), (load (s64) from %fixed-stack.2), (load (s64) from %fixed-stack.3, align 16), (load (s64) from %fixed-stack.0) ;CHECK: ADJCALLSTACKUP64 8, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp ;CHECK: bb.3.bb15: @@ -136,21 +130,19 @@ declare token @llvm.experimental.gc.statepoint.p0(i64 , i32 , ptr, i32 , i32 , . ;CHECK: bb.5.bb21: ;CHECK: successors: ;CHECK: ADJCALLSTACKDOWN64 8, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp -;CHECK: %81:fr64 = MOVSDrm_alt %stack.7, 1, $noreg, 0, $noreg :: (load (s64) from %stack.7) -;CHECK: MOVSDmr $rsp, 1, $noreg, 0, $noreg, %81 :: (store (s64) into stack) +;CHECK: %68:fr64 = MOVSDrm_alt %stack.7, 1, $noreg, 0, $noreg :: (load (s64) from %stack.7) +;CHECK: MOVSDmr $rsp, 1, $noreg, 0, $noreg, %68 :: (store (s64) into stack) ;CHECK: $xmm0 = MOVSDrm_alt %stack.6, 1, $noreg, 0, $noreg :: (load (s64) from %stack.6) ;CHECK: $xmm1 = MOVSDrm_alt %stack.5, 1, $noreg, 0, $noreg :: (load (s64) from %stack.5) ;CHECK: $xmm2 = MOVSDrm_alt %stack.4, 1, $noreg, 0, $noreg :: (load (s64) from %stack.4) ;CHECK: $xmm3 = MOVSDrm_alt %stack.2, 1, $noreg, 0, $noreg :: (load (s64) from %stack.2) ;CHECK: $xmm4 = MOVSDrm_alt %stack.1, 1, $noreg, 0, $noreg :: (load (s64) from %stack.1) ;CHECK: $xmm5 = MOVSDrm_alt %stack.3, 1, $noreg, 0, $noreg :: (load (s64) from %stack.3) -;CHECK: %74:fr64 = MOVSDrm_alt %fixed-stack.3, 1, $noreg, 0, $noreg :: (load (s64) from %fixed-stack.3, align 16) -;CHECK: %95:fr64 = COPY %74 -;CHECK: $xmm6 = COPY %95 +;CHECK: %69:fr64 = MOVSDrm_alt %fixed-stack.3, 1, $noreg, 0, $noreg :: (load (s64) from %fixed-stack.3, align 16) +;CHECK: $xmm6 = COPY %69 ;CHECK: $esi = MOV32ri 51 -;CHECK: %69:fr64 = MOVSDrm_alt %fixed-stack.2, 1, $noreg, 0, $noreg :: (load (s64) from %fixed-stack.2) -;CHECK: %97:fr64 = COPY %69 -;CHECK: $xmm7 = COPY %97 +;CHECK: %70:fr64 = MOVSDrm_alt %fixed-stack.2, 1, $noreg, 0, $noreg :: (load (s64) from %fixed-stack.2) +;CHECK: $xmm7 = COPY %70 ;CHECK: STATEPOINT 2, 5, 10, undef %36:gr64, undef $rdi, $xmm0, $xmm1, $xmm2, $xmm3, $xmm4, $xmm5, $xmm6, $xmm7, killed $esi, 2, 0, 2, 0, 2, 105, 2, 0, 2, 2, 2, 0, 2, 97, 2, 0, 2, 26, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 7, 2, 0, 2, 2, 2, 2, 2, 46, 2, 0, 2, 20, 2, 0, 2, 0, 2, 4278124286, 2, 4, 1, 8, %stack.6, 0, 2, 7, 2, 0, 2, 4, 1, 8, %stack.5, 0, 2, 7, 2, 0, 2, 4, 1, 8, %stack.4, 0, 2, 7, 2, 0, 2, 4, 1, 8, %stack.2, 0, 2, 7, 2, 0, 2, 4, 1, 8, %stack.1, 0, 2, 7, 2, 0, 2, 4, 1, 8, %stack.3, 0, 2, 7, 2, 0, 2, 4, 1, 8, %fixed-stack.3, 0, 2, 7, 2, 0, 2, 4, 1, 8, %fixed-stack.2, 0, 2, 7, 2, 0, 2, 4, 1, 8, %stack.7, 0, 2, 7, 2, 0, 2, 3, 2, 51, 2, 1, 2, 4278124286, 2, 0, 2, 1, 0, 0, csr_64, implicit-def $rsp, implicit-def $ssp :: (load (s64) from %stack.7), (load (s64) from %stack.6), (load (s64) from %stack.5), (load (s64) from %stack.4), (load (s64) from %stack.2), (load (s64) from %stack.1), (load (s64) from %stack.3), (load (s64) from %fixed-stack.3, align 16), (load (s64) from %fixed-stack.2) ;CHECK: ADJCALLSTACKUP64 8, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp ;CHECK: bb.6.bb23 (landing-pad): diff --git a/llvm/test/CodeGen/X86/statepoint-regs.ll b/llvm/test/CodeGen/X86/statepoint-regs.ll index a30c4c697..c36cb8d35 100644 --- a/llvm/test/CodeGen/X86/statepoint-regs.ll +++ b/llvm/test/CodeGen/X86/statepoint-regs.ll @@ -484,8 +484,8 @@ define void @test10(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 ; CHECK-NEXT: .cfi_offset %r14, -32 ; CHECK-NEXT: .cfi_offset %r15, -24 ; CHECK-NEXT: .cfi_offset %rbp, -16 -; CHECK-NEXT: movl %r9d, %r15d -; CHECK-NEXT: movl %r8d, %r14d +; CHECK-NEXT: movl %r9d, %r14d +; CHECK-NEXT: movl %r8d, %r15d ; CHECK-NEXT: movl %ecx, %r12d ; CHECK-NEXT: movl %edx, %r13d ; CHECK-NEXT: movl %esi, %ebx diff --git a/llvm/test/CodeGen/X86/statepoint-vreg-details.ll b/llvm/test/CodeGen/X86/statepoint-vreg-details.ll index 5bc6d43be..32e733426 100644 --- a/llvm/test/CodeGen/X86/statepoint-vreg-details.ll +++ b/llvm/test/CodeGen/X86/statepoint-vreg-details.ll @@ -86,7 +86,7 @@ define ptr addrspace(1) @test_alloca(ptr addrspace(1) %ptr) gc "statepoint-examp ; CHECK-VREG-LABEL: name: test_alloca ; CHECK-VREG: %0:gr64 = COPY $rdi ; CHECK-VREG: MOV64mr %stack.0.alloca, 1, $noreg, 0, $noreg, %0 :: (store (s64) into %ir.alloca) -; CHECK-VREG: %1:gr64 = STATEPOINT 0, 0, 0, @return_i1, 2, 0, 2, 0, 2, 0, 2, 1, %0(tied-def 0), 2, 1, 0, %stack.0.alloca, 0, 2, 1, 0, 0, csr_64, implicit-def $rsp, implicit-def $ssp, implicit-def $al :: (volatile load store (s64) on %stack.0.alloca) +; CHECK-VREG: %1:gr64 = STATEPOINT 0, 0, 0, @return_i1, 2, 0, 2, 0, 2, 0, 2, 1, %0(tied-def 0), 2, 1, 0, %stack.0.alloca, 0, 0, 2, 1, 0, 0, csr_64, implicit-def $rsp, implicit-def $ssp, implicit-def $al :: (volatile load store (s64) on %stack.0.alloca) ; CHECK-VREG: %2:gr8 = COPY $al ; CHECK-VREG: %3:gr64 = MOV64rm %stack.0.alloca, 1, $noreg, 0, $noreg :: (dereferenceable load (s64) from %ir.alloca) ; CHECK-VREG: $rdi = COPY %1 @@ -95,7 +95,7 @@ define ptr addrspace(1) @test_alloca(ptr addrspace(1) %ptr) gc "statepoint-examp ; CHECK-PREG-LABEL: name: test_alloca ; CHECK-PREG: renamable $rbx = COPY $rdi ; CHECK-PREG: MOV64mr %stack.0.alloca, 1, $noreg, 0, $noreg, renamable $rbx :: (store (s64) into %ir.alloca) -; CHECK-PREG: renamable $rbx = STATEPOINT 0, 0, 0, @return_i1, 2, 0, 2, 0, 2, 0, 2, 1, killed renamable $rbx(tied-def 0), 2, 1, 0, %stack.0.alloca, 0, 2, 1, 0, 0, csr_64, implicit-def $rsp, implicit-def $ssp, implicit-def dead $al :: (volatile load store (s64) on %stack.0.alloca) +; CHECK-PREG: renamable $rbx = STATEPOINT 0, 0, 0, @return_i1, 2, 0, 2, 0, 2, 0, 2, 1, killed renamable $rbx(tied-def 0), 2, 1, 0, %stack.0.alloca, 0, 0, 2, 1, 0, 0, csr_64, implicit-def $rsp, implicit-def $ssp, implicit-def dead $al :: (volatile load store (s64) on %stack.0.alloca) ; CHECK-PREG: renamable $r14 = MOV64rm %stack.0.alloca, 1, $noreg, 0, $noreg :: (dereferenceable load (s64) from %ir.alloca) ; CHECK-PREG: $rdi = COPY killed renamable $rbx ; CHECK-PREG: CALL64pcrel32 @consume, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp diff --git a/llvm/test/CodeGen/X86/statepoint-vreg-twoaddr2.ll b/llvm/test/CodeGen/X86/statepoint-vreg-twoaddr2.ll new file mode 100644 index 000000000..5ffcd8c70 --- /dev/null +++ b/llvm/test/CodeGen/X86/statepoint-vreg-twoaddr2.ll @@ -0,0 +1,37 @@ +; RUN: llc -O2 --enable-callee-saved-stackmap --max-registers-for-gc-values=16 --mtriple x86_64-pc-linux-gnu --print-after='twoaddressinstruction' < %s --filetype=obj 2>&1 | FileCheck %s + +; This test checks that TwoAddressInstruction pass will create COPY instruction for registers +; with inconsistent types. + +declare i1 @return_i1() +declare i32 @consume() + +; CHECK-LABEL: test_relocate_norex +; CHECK: [[VREG0:%[0-9]]]:gr64 = COPY killed $rdi +; CHECK: [[VREG1:%[0-9]]]:gr64_norex = COPY killed [[VREG0]]:gr64 +; CHECK: [[VREG1]]:gr64_norex = STATEPOINT 0, 0, 0, target-flags(x86-plt) @return_i1, 2, 0, 2, 0, 2, 0, 2, 1, [[VREG1]]:gr64_norex(tied-def 0), 2, 0, 2, 1, 0, 0, , implicit-def $rsp, implicit-def $ssp, implicit-def $al +; CHECK: %4:gr8_norex = COPY killed %3.sub_8bit_hi:gr32_abcd +; CHECK: MOV8mr_NOREX killed [[VREG1]]:gr64_norex, 1, $noreg, 53, $noreg, killed %4:gr8_norex :: (store (s8) into %ir.ptr, addrspace 1) + +define i1 @test_relocate_norex(i8 addrspace(1)* %a) gc "statepoint-example" { +entry: +%safepoint_token = tail call token (i64, i32, i1 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_i1f(i64 0, i32 0, i1 ()* elementtype(i1 ()) @return_i1, i32 0, i32 0, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* %a) ] +%rel1 = call i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %safepoint_token, i32 0, i32 0) ; (%a, %a) +%res1 = call zeroext i1 @llvm.experimental.gc.result.i1(token %safepoint_token) +%var1 = call i32 @consume() +%tag1 = lshr i32 %var1, 8 +%tag2 = trunc i32 %tag1 to i8 +%ptr = getelementptr inbounds i8, i8 addrspace(1)* %rel1, i64 53 +store i8 %tag2, i8 addrspace(1)* %ptr, align 1 +ret i1 %res1 +} + +; Function Attrs: nounwind readnone +declare i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token, i32 immarg, i32 immarg) #0 + +declare token @llvm.experimental.gc.statepoint.p0f_i1f(i64 immarg, i32 immarg, i1 ()*, i32 immarg, i32 immarg, ...) + +; Function Attrs: nounwind readnone +declare i1 @llvm.experimental.gc.result.i1(token) #0 + +attributes #0 = { nounwind readnone } \ No newline at end of file diff --git a/llvm/test/Instrumentation/AddressSanitizer/CJAsan/global_filter.ll b/llvm/test/Instrumentation/AddressSanitizer/CJAsan/global_filter.ll new file mode 100644 index 000000000..683afef7e --- /dev/null +++ b/llvm/test/Instrumentation/AddressSanitizer/CJAsan/global_filter.ll @@ -0,0 +1,46 @@ +; This test checks global variable instrumentation capability in cangjie. +; RUN: opt < %s -passes='asan-module' -cj-asan=true --cangjie-pipeline -S | FileCheck %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%TypeWithAddrSpace = type { i8 addrspace(1)* } +%Wrapper = type { %TypeWithAddrSpace } + +; not a user-defined global +; +@no_instrument = global i32 1 +; CHECK: @no_instrument = global i32 1 + +@no_instrument_gc_ptr = global i8 addrspace(1)* null +; CHECK: @no_instrument_gc_ptr = global i8 addrspace(1)* null + +; user-defined global, marked with sanitize_global +; +@val_global = global i32 1 #0 +; CHECK: @val_global = global { i32, [12 x i8] } { i32 1, [12 x i8] zeroinitializer }, align 16 #0 + +@val_array = global [16 x i8] zeroinitializer #0 +; CHECK: @val_array = global { [16 x i8], [16 x i8] } zeroinitializer, align 16 #0 + +@val_ptr = global %Wrapper* null #0 +; CHECK: @val_ptr = global { %Wrapper*, [8 x i8] } zeroinitializer, align 16 #0 + +; cannot instrument, is a GC ptr +@gc_val_global = global i8 addrspace(1)* null #0 +; CHECK: @gc_val_global = global i8 addrspace(1)* null #0 + +; cannot instrument, contains a GC ptr +@gc_array = global [16 x i8 addrspace(1)*] zeroinitializer #0 +; CHECK: @gc_array = global [16 x i8 addrspace(1)*] zeroinitializer #0 + +@gc_vector = global <2 x i8 addrspace(1)*> zeroinitializer #0 +; CHECK: @gc_vector = global <2 x i8 addrspace(1)*> zeroinitializer #0 + +@gc_struct = global %Wrapper zeroinitializer #0 +; CHECK: @gc_struct = global %Wrapper zeroinitializer #0 + +@gc_struct_array = global [2 x %Wrapper] zeroinitializer #0 +; CHECK: @gc_struct_array = global [2 x %Wrapper] zeroinitializer #0 + +attributes #0 = { "address_sanitize_global" } \ No newline at end of file diff --git a/llvm/test/Instrumentation/AddressSanitizer/CJAsan/module_ctor.ll b/llvm/test/Instrumentation/AddressSanitizer/CJAsan/module_ctor.ll new file mode 100644 index 000000000..1cedea75c --- /dev/null +++ b/llvm/test/Instrumentation/AddressSanitizer/CJAsan/module_ctor.ll @@ -0,0 +1,9 @@ +; This test checks asan.module_ctor must have no comdat section. +; This patch is a intermediate fix refering to llvm pull request 67745. +; RUN: opt < %s -passes='asan-module' -cj-asan=true --cangjie-pipeline -S | FileCheck %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; CHECK-NOT: $asan.module_ctor = comdat any +; CHECK: define internal void @asan.module_ctor() #[[#ATTR:]] { \ No newline at end of file diff --git a/llvm/test/Instrumentation/AddressSanitizer/CJAsan/stack_filter.ll b/llvm/test/Instrumentation/AddressSanitizer/CJAsan/stack_filter.ll new file mode 100644 index 000000000..bbcbb0143 --- /dev/null +++ b/llvm/test/Instrumentation/AddressSanitizer/CJAsan/stack_filter.ll @@ -0,0 +1,232 @@ +; This test checks stack variable instrumentation capability in cangjie. +; RUN: opt < %s -passes='asan-module' -cj-asan=true --cangjie-pipeline -S | FileCheck %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%TypeWithAddrSpace = type { i8 addrspace(1)* } +%Wrapper = type { %TypeWithAddrSpace } + +define void @use_val(i8* %a) { + ret void +} + +define void @use_val_addrspace(i8 addrspace(1)** %a) { + ret void +} + +define void @use_val_struct(%Wrapper* %a) { + ret void +} + +define void @use_val_struct_ptr(%Wrapper** %a) { + ret void +} + +define void @use_val_array([2 x i8 addrspace(1)*]* %a) { + ret void +} + +define void @use_val_vector(<2 x i8 addrspace(1)*>* %a) { + ret void +} + +define void @use_val_struct_array([2 x %Wrapper]* %a) { + ret void +} + + +; function without inout (i.e., no address_sanitize_stack annotation) +; which should not be instrumented +define void @no_stack_instrument() { +entry: + %0 = alloca i8, align 8 + %1 = alloca i8, align 8 + call void @use_val(i8* %0) + call void @use_val(i8* %1) + ret void +} + +; CHECK-LABEL: @no_stack_instrument +; CHECK: %0 = alloca i8, align 8 +; CHECK: %1 = alloca i8, align 8 + +; CHECK-NOT: %MyAlloca = alloca [64 x i8], align 16 +; CHECK-NOT: %asan_local_stack_base = alloca i64, align 8 + +; CHECK: call void @use_val(i8* %0) +; CHECK: call void @use_val(i8* %1) + + +; function with inout which should not be instrumented +define void @stack_instrument_var() "address_sanitize_stack" { +entry: + %0 = alloca i8, align 8 + %1 = alloca i8, align 8 + call void @use_val(i8* %0) + call void @use_val(i8* %1) + ret void +} + +; CHECK-LABEL: @stack_instrument_var +; CHECK-NOT: %0 = alloca i8, align 8 +; CHECK-NOT: %1 = alloca i8, align 8 + +; CHECK: %MyAlloca = alloca [64 x i8], align 16 +; CHECK: %asan_local_stack_base = alloca i64, align 8 +; CHECK: store i64 %10, i64* %asan_local_stack_base, align 8 +; CHECK: %11 = add i64 %10, 32 +; CHECK: %12 = inttoptr i64 %11 to i8* +; CHECK: %13 = add i64 %10, 48 +; CHECK: %14 = inttoptr i64 %13 to i8* + +; CHECK: call void @use_val(i8* %12) +; CHECK: call void @use_val(i8* %14) + + +; stack variable point to heap (i.e., marked by addrspace(1)) +; should not be instrumented +define void @stack_instrument_addrspace() "address_sanitize_stack" { +entry: + %0 = alloca i8 addrspace(1)*, align 8 + %1 = alloca i8 addrspace(1)*, align 8 + call void @use_val_addrspace(i8 addrspace(1)** %0) + call void @use_val_addrspace(i8 addrspace(1)** %1) + ret void +} + +; CHECK-LABEL: @stack_instrument_addrspace +; CHECK: %0 = alloca i8 addrspace(1)*, align 8 +; CHECK: %1 = alloca i8 addrspace(1)*, align 8 + +; CHECK-NOT: %asan_local_stack_base = alloca i64, align 8 +; CHECK-NOT: %12 = inttoptr i64 %11 to i8 addrspace(1)** +; CHECK-NOT: %14 = inttoptr i64 %13 to i8 addrspace(1)** + +; CHECK: call void @use_val_addrspace(i8 addrspace(1)** %0) +; CHECK: call void @use_val_addrspace(i8 addrspace(1)** %1) + + +; stack variable point to heap (i.e., marked by addrspace(1)) +; should not be instrumented +define void @stack_instrument_mixed() "address_sanitize_stack" { +entry: + %0 = alloca i8, align 8 + %1 = alloca i8 addrspace(1)*, align 8 + call void @use_val(i8 * %0) + call void @use_val_addrspace(i8 addrspace(1)** %1) + ret void +} + +; CHECK-LABEL: @stack_instrument_mixed +; CHECK-NOT: %0 = alloca i8, align 8 +; CHECK-NOT: %1 = alloca i8 addrspace(1)*, align 8 + +; CHECK: %0 = alloca i8 addrspace(1)*, align 8 +; CHECK: %MyAlloca = alloca [64 x i8], align 16 +; CHECK: %asan_local_stack_base = alloca i64, align 8 +; CHECK: store i64 %11, i64* %asan_local_stack_base, align 8 +; CHECK: %12 = add i64 %11, 32 +; CHECK: %13 = inttoptr i64 %12 to i8* + +; CHECK: call void @use_val(i8* %13) +; CHECK: call void @use_val_addrspace(i8 addrspace(1)** %0) + + +; heap pointer in struct should not be instrumented +define void @stack_instrument_addrspace_in_struct() "address_sanitize_stack" { +entry: + %0 = alloca %Wrapper, align 8 + call void @use_val_struct(%Wrapper* %0) + ret void +} + +; CHECK-LABEL: @stack_instrument_addrspace_in_struct +; CHECK: %0 = alloca %Wrapper, align 8 + +; CHECK-NOT: %MyAlloca = alloca [64 x i8], align 16 +; CHECK-NOT: %asan_local_stack_base = alloca i64, align 8 +; CHECK-NOT: store i64 %10, i64* %asan_local_stack_base, align 8 +; CHECK-NOT: %12 = inttoptr i64 %11 to %Wrapper* + +; CHECK: call void @use_val_struct(%Wrapper* %0) + + +; a pointer points to stack variable which contains GC ptr +; Asan should handle this ptr since: +; if ptr is point to stack, stack map is not generated for this ptr: ignore +; if ptr is point to heap, it already has addrspace(1) which is already handled +define void @stack_instrument_addrspace_in_struct_ptr() "address_sanitize_stack" { +entry: + %0 = alloca %Wrapper*, align 8 + call void @use_val_struct_ptr(%Wrapper** %0) + ret void +} + +; CHECK-LABEL: @stack_instrument_addrspace_in_struct_ptr +; CHECK-NOT: %0 = alloca %Wrapper*, align 8 + +; CHECK: %MyAlloca = alloca [64 x i8], align 16 +; CHECK: %asan_local_stack_base = alloca i64, align 8 +; CHECK: store i64 %10, i64* %asan_local_stack_base, align 8 +; CHECK: %11 = add i64 %10, 32 +; CHECK: %12 = inttoptr i64 %11 to %Wrapper** + +; CHECK: call void @use_val_struct_ptr(%Wrapper** %12) + + +; heap pointer in array should not be instrumented +define void @stack_instrument_addrspace_in_array() "address_sanitize_stack" { +entry: + %0 = alloca [2 x i8 addrspace(1)*], align 8 + call void @use_val_array([2 x i8 addrspace(1)*]* %0) + ret void +} + +; CHECK-LABEL: @stack_instrument_addrspace_in_array +; CHECK: %0 = alloca [2 x i8 addrspace(1)*], align 8 + +; CHECK-NOT: %MyAlloca = alloca [64 x i8], align 16 +; CHECK-NOT: %asan_local_stack_base = alloca i64, align 8 +; CHECK-NOT: store i64 %10, i64* %asan_local_stack_base, align 8 +; CHECK-NOT: %12 = inttoptr i64 %11 to [2 x i8 addrspace(1)*]* + +; CHECK: call void @use_val_array([2 x i8 addrspace(1)*]* %0) + + +; heap pointer in struct inside array should not be instrumented +define void @stack_instrument_addrspace_in_struct_array() "address_sanitize_stack" { +entry: + %0 = alloca [2 x %Wrapper], align 8 + call void @use_val_struct_array([2 x %Wrapper]* %0) + ret void +} + +; CHECK-LABEL: @stack_instrument_addrspace_in_struct_array +; CHECK: %0 = alloca [2 x %Wrapper], align 8 + +; CHECK-NOT: %MyAlloca = alloca [64 x i8], align 16 +; CHECK-NOT: %asan_local_stack_base = alloca i64, align 8 +; CHECK-NOT: store i64 %10, i64* %asan_local_stack_base, align 8 +; CHECK-NOT: %12 = inttoptr i64 %11 to [2 x %Wrapper]* + +; CHECK: call void @use_val_struct_array([2 x %Wrapper]* %0) + + +; heap pointer in struct inside vector should not be instrumented +define void @stack_instrument_addrspace_in_vector() "address_sanitize_stack" { +entry: + %0 = alloca <2 x i8 addrspace(1)*>, align 8 + call void @use_val_vector(<2 x i8 addrspace(1)*>* %0) + ret void +} + +; CHECK-LABEL: @stack_instrument_addrspace_in_vector +; CHECK: %0 = alloca <2 x i8 addrspace(1)*>, align 8 + +; CHECK-NOT: %MyAlloca = alloca [64 x i8], align 16 +; CHECK-NOT: %asan_local_stack_base = alloca i64, align 8 +; CHECK-NOT: store i64 %10, i64* %asan_local_stack_base, align 8 +; CHECK-NOT: %12 = inttoptr i64 %11 to <2 x i8 addrspace(1)*>* + +; CHECK: call void @use_val_vector(<2 x i8 addrspace(1)*>* %0) diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/CJHwASan/global-filter.ll b/llvm/test/Instrumentation/HWAddressSanitizer/CJHwASan/global-filter.ll new file mode 100644 index 000000000..6b9d40f6d --- /dev/null +++ b/llvm/test/Instrumentation/HWAddressSanitizer/CJHwASan/global-filter.ll @@ -0,0 +1,45 @@ +; RUN: opt < %s -passes=hwasan -cj-hwasan=true --cangjie-pipeline -S | FileCheck %s + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-linux" + +%TypeWithAddrSpace = type { i8 addrspace(1)* } +%Wrapper = type { %TypeWithAddrSpace } + +; not a user-defined global +; +@no_instrument = global i32 1 +@no_instrument_gc_ptr = global i8 addrspace(1)* null + +; user-defined global, marked with sanitize_global +; +@val_global = global i32 1 #0 +@val_array = global [16 x i8] zeroinitializer #0 +@val_ptr = global %Wrapper* null #0 + +; cannot instrument, is a GC ptr +@gc_val_global = global i8 addrspace(1)* null #0 + +; cannot instrument, contains a GC ptr +@gc_array = global [16 x i8 addrspace(1)*] zeroinitializer #0 +@gc_vector = global <2 x i8 addrspace(1)*> zeroinitializer #0 +@gc_struct = global %Wrapper zeroinitializer #0 +@gc_struct_array = global [2 x %Wrapper] zeroinitializer #0 + +attributes #0 = { "address_sanitize_global" } + +; CHECK: @no_instrument = global i32 1 +; CHECK: @no_instrument_gc_ptr = global i8 addrspace(1)* null +; CHECK: @gc_val_global = global i8 addrspace(1)* null #0 +; CHECK: @gc_array = global [16 x i8 addrspace(1)*] zeroinitializer #0 +; CHECK: @gc_vector = global <2 x i8 addrspace(1)*> zeroinitializer #0 +; CHECK: @gc_struct = global %Wrapper zeroinitializer #0 +; CHECK: @gc_struct_array = global [2 x %Wrapper] zeroinitializer #0 + +; CHECK: @val_global.hwasan = private global { i32, [12 x i8] } { i32 1, [12 x i8] c"\00\00\00\00\00\00\00\00\00\00\00I" }, align 16 #0 +; CHECK: @val_array.hwasan = private global [16 x i8] zeroinitializer, align 16 #0 +; CHECK: @val_ptr.hwasan = private global { %Wrapper*, [8 x i8] } { %Wrapper* null, [8 x i8] c"\00\00\00\00\00\00\00K" }, align 16 #0 + +; CHECK: @val_global = alias i32, inttoptr (i64 add (i64 ptrtoint ({ i32, [12 x i8] }* @val_global.hwasan to i64), i64 5260204364768739328) to i32*) +; CHECK: @val_array = alias [16 x i8], inttoptr (i64 add (i64 ptrtoint ([16 x i8]* @val_array.hwasan to i64), i64 5332261958806667264) to [16 x i8]*) +; CHECK: @val_ptr = alias %Wrapper*, inttoptr (i64 add (i64 ptrtoint ({ %Wrapper*, [8 x i8] }* @val_ptr.hwasan to i64), i64 5404319552844595200) to %Wrapper**) \ No newline at end of file diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/CJHwASan/stack-filter.ll b/llvm/test/Instrumentation/HWAddressSanitizer/CJHwASan/stack-filter.ll new file mode 100644 index 000000000..607bd41c6 --- /dev/null +++ b/llvm/test/Instrumentation/HWAddressSanitizer/CJHwASan/stack-filter.ll @@ -0,0 +1,182 @@ +; RUN: opt < %s -passes=hwasan -cj-hwasan=true --cangjie-pipeline -S | FileCheck %s + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-linux" + +%TypeWithAddrSpace = type { i8 addrspace(1)* } +%Wrapper = type { %TypeWithAddrSpace } + +declare void @use_val(i8* %a) + +declare void @use_val_addrspace(i8 addrspace(1)** %a) + +declare void @use_val_struct(%Wrapper* %a) + +declare void @use_val_struct_ptr(%Wrapper** %a) + +declare void @use_val_array([2 x i8 addrspace(1)*]* %a) + +declare void @use_val_vector(<2 x i8 addrspace(1)*>* %a) + +declare void @use_val_struct_array([2 x %Wrapper]* %a) + + +; function without inout (i.e., no address_sanitize_stack annotation) +; which should not be instrumented +define void @no_stack_instrument() { +entry: + %0 = alloca i8, align 8 + %1 = alloca i8, align 8 + call void @use_val(i8* %0) + call void @use_val(i8* %1) + ret void +} + +; CHECK-LABEL: @no_stack_instrument +; CHECK: %0 = alloca i8, align 8 +; CHECK: %1 = alloca i8, align 8 + +; CHECK-NOT: %alloca.0.hwasan = inttoptr i64 %20 to i8* +; CHECK-NOT: %alloca.1.hwasan = inttoptr i64 %32 to i8* + +; CHECK: call void @use_val(i8* %0) +; CHECK: call void @use_val(i8* %1) + + +; function with inout which should not be instrumented +define void @stack_instrument_var() "address_sanitize_stack" { +entry: + %0 = alloca i8, align 8 + %1 = alloca i8, align 8 + call void @use_val(i8* %0) + call void @use_val(i8* %1) + ret void +} + +; CHECK-LABEL: @stack_instrument_var +; CHECK-NOT: %0 = alloca i8, align 8 +; CHECK-NOT: %1 = alloca i8, align 8 + +; CHECK: %alloca.0.hwasan = inttoptr i64 %20 to i8* +; CHECK: %alloca.1.hwasan = inttoptr i64 %32 to i8* + +; CHECK: call void @use_val(i8* %alloca.0.hwasan) +; CHECK: call void @use_val(i8* %alloca.1.hwasan) + + +; stack variable point to heap (i.e., marked by addrspace(1)) +; should not be instrumented +define void @stack_instrument_addrspace() "address_sanitize_stack" { +entry: + %0 = alloca i8 addrspace(1)*, align 8 + %1 = alloca i8 addrspace(1)*, align 8 + call void @use_val_addrspace(i8 addrspace(1)** %0) + call void @use_val_addrspace(i8 addrspace(1)** %1) + ret void +} + +; CHECK-LABEL: @stack_instrument_addrspace +; CHECK: %0 = alloca i8 addrspace(1)*, align 8 +; CHECK: %1 = alloca i8 addrspace(1)*, align 8 + +; CHECK-NOT: %alloca.0.hwasan = inttoptr i64 %20 to i8 addrspace(1)** +; CHECK-NOT: %alloca.1.hwasan = inttoptr i64 %32 to i8 addrspace(1)** + +; CHECK: call void @use_val_addrspace(i8 addrspace(1)** %0) +; CHECK: call void @use_val_addrspace(i8 addrspace(1)** %1) + + +; stack variable point to heap (i.e., marked by addrspace(1)) +; should not be instrumented +define void @stack_instrument_mixed() "address_sanitize_stack" { +entry: + %0 = alloca i8, align 8 + %1 = alloca i8 addrspace(1)*, align 8 + call void @use_val(i8 * %0) + call void @use_val_addrspace(i8 addrspace(1)** %1) + ret void +} + +; CHECK-LABEL: @stack_instrument_mixed +; CHECK-NOT: %0 = alloca i8, align 8 +; CHECK-NOT: %1 = alloca i8 addrspace(1)*, align 8 + +; CHECK: %alloca.0.hwasan = inttoptr i64 %20 to i8* +; CHECK: %27 = alloca i8 addrspace(1)*, align 8 + +; CHECK: call void @use_val(i8* %alloca.0.hwasan) +; CHECK: call void @use_val_addrspace(i8 addrspace(1)** %27) + + +; heap pointer in struct should not be instrumented +define void @stack_instrument_addrspace_in_struct() "address_sanitize_stack" { +entry: + %0 = alloca %Wrapper, align 8 + call void @use_val_struct(%Wrapper* %0) + ret void +} + +; CHECK-LABEL: @stack_instrument_addrspace_in_struct +; CHECK: %0 = alloca %Wrapper, align 8 + +; CHECK-NOT: %alloca.0.hwasan = inttoptr i64 %20 to %Wrapper* + +; CHECK: call void @use_val_struct(%Wrapper* %0) + + +; a pointer points to stack variable which contains GC ptr +; Asan should handle this ptr since: +; if ptr is point to stack, stack map is not generated for this ptr: ignore +; if ptr is point to heap, it already has addrspace(1) which is already handled +define void @stack_instrument_addrspace_in_struct_ptr() "address_sanitize_stack" { +entry: + %0 = alloca %Wrapper*, align 8 + call void @use_val_struct_ptr(%Wrapper** %0) + ret void +} + +; CHECK-LABEL: @stack_instrument_addrspace_in_struct_ptr +; CHECK-NOT: %0 = alloca %Wrapper*, align 8 + +; CHECK: %alloca.0.hwasan = inttoptr i64 %20 to %Wrapper** + +; CHECK: call void @use_val_struct_ptr(%Wrapper** %alloca.0.hwasan) + + +; heap pointer in array should not be instrumented +define void @stack_instrument_addrspace_in_array() "address_sanitize_stack" { +entry: + %0 = alloca [2 x i8 addrspace(1)*], align 8 + call void @use_val_array([2 x i8 addrspace(1)*]* %0) + ret void +} + +; CHECK-LABEL: @stack_instrument_addrspace_in_array +; CHECK: %0 = alloca [2 x i8 addrspace(1)*], align 8 +; CHECK: call void @use_val_array([2 x i8 addrspace(1)*]* %0) + + +; heap pointer in struct inside array should not be instrumented +define void @stack_instrument_addrspace_in_struct_array() "address_sanitize_stack" { +entry: + %0 = alloca [2 x %Wrapper], align 8 + call void @use_val_struct_array([2 x %Wrapper]* %0) + ret void +} + +; CHECK-LABEL: @stack_instrument_addrspace_in_struct_array +; CHECK: %0 = alloca [2 x %Wrapper], align 8 +; CHECK: call void @use_val_struct_array([2 x %Wrapper]* %0) + + +; heap pointer in struct inside vector should not be instrumented +define void @stack_instrument_addrspace_in_vector() "address_sanitize_stack" { +entry: + %0 = alloca <2 x i8 addrspace(1)*>, align 8 + call void @use_val_vector(<2 x i8 addrspace(1)*>* %0) + ret void +} + +; CHECK-LABEL: @stack_instrument_addrspace_in_vector +; CHECK: %0 = alloca <2 x i8 addrspace(1)*>, align 8 +; CHECK: call void @use_val_vector(<2 x i8 addrspace(1)*>* %0) diff --git a/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop1.ll b/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop1.ll new file mode 100644 index 000000000..eddbaf6b7 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop1.ll @@ -0,0 +1,87 @@ +; RUN: not not opt --cj-barrier-opt -S < %s -disable-output 2>&1 | FileCheck %s -check-prefixes=ABORT + +%record3 = type { i32, i8 addrspace(1)* } +%record2 = type { i32, %record3 } +%record1 = type { i32, i8 addrspace(1)*, %record2 } + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i64*, i32, [0 x %KlassInfo.0*] } +%KlassInfo.1 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i64*, i32, [1 x %KlassInfo.0*] } +@"Klass1.objKlass" = external global %KlassInfo.1 +@Global0 = internal global %record3 zeroinitializer +@"Klass0.objKlass" = external global %KlassInfo.0 + +declare void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** noalias nocapture writeonly, i8 addrspace(1)** noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)*, i8*) #1 +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1) +declare cangjiegccc void @MCC_SafepointStub() +declare i32 @personality_function() +declare i8 addrspace(1)* @CJ_MCC_BeginCatch(i8*) +declare void @CJ_MCC_EndCatch() +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare i8* @CJ_MCC_GetExceptionWrapper() +declare i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8*, i32) + +define void @foo1(i8 addrspace(1)* %this, %record3 addrspace(1)* %0) #0 gc "cangjie" personality i32 ()* @personality_function { +entry: + %test0 = alloca %record3 + %test1 = bitcast %record3* %test0 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %test1, i8 0, i64 32, i1 false) + %test2 = load i8, i8 addrspace(1)* %this + %cond = icmp ne i8 %test2, 0 + br i1 %cond, label %if, label %else + +if: ; preds = %entry + %test3 = getelementptr inbounds %record3, %record3* %test0, i64 0, i32 1 + br label %thunk + +else: ; preds = %entry + %test4.0 = invoke i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*), i32 56) + to label %thunk0 unwind label %finallyOne + +thunk0: ; preds = %else + %test4.1 = bitcast i8 addrspace(1)* %test4.0 to %record3 addrspace(1)* + %test4.2 = addrspacecast %record3 addrspace(1)* %test4.1 to %record3* + %test4 = getelementptr inbounds %record3, %record3* %test4.2, i64 0, i32 1 + br label %thunk + +thunk: ; preds = %if, %thunk0 + %test5 = phi i8 addrspace(1)** [ %test3, %if ], [ %test4, %thunk0 ] + br label %body + +body: ; preds = %thunk, %body + %test = phi i8 [%test2, %thunk], [%test.1, %body] + %1 = bitcast i8 addrspace(1)* %this to %record1 addrspace(1)* + %2 = getelementptr inbounds %record1, %record1 addrspace(1)* %1, i32 0, i32 2 + %3 = getelementptr inbounds %record2, %record2 addrspace(1)* %2, i32 0, i32 1 + %4 = bitcast %record3 addrspace(1)* %3 to i8 addrspace(1)* + %5 = bitcast %record3 addrspace(1)* %0 to i8 addrspace(1)* + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + %6 = getelementptr inbounds %record3, %record3 addrspace(1)* %0, i64 0, i32 1 + %7 = addrspacecast i8 addrspace(1)* addrspace(1)* %6 to i8 addrspace(1)** + call void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** align 8 %test5, i8 addrspace(1)** align 8 %7, i64 8, i1 false) + %cond1 = icmp eq i8 %test, 0 + %test.1 = add i8 %test, 1 + call cangjiegccc void @MCC_SafepointStub() + br i1 %cond1, label %body, label %end + +end: ; preds = %body + ret void + +finallyOne: ; preds = %else + %8 = landingpad token + catch i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*) + catch i8* null + %9 = call i8* @CJ_MCC_GetExceptionWrapper() + %10 = call i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %9) + call void @CJ_MCC_EndCatch() + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %10) + unreachable +} + +; ABORT: LLVM ERROR: Need write barrier in function +; ABORT: error: Aborted + +attributes #0 = { "hasRcdParam" } +attributes #1 = { "cj-runtime" "gc-leaf-function" } \ No newline at end of file diff --git a/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop2.ll b/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop2.ll new file mode 100644 index 000000000..9e8240e20 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop2.ll @@ -0,0 +1,90 @@ +; RUN: not not opt --cj-barrier-opt -S < %s -disable-output 2>&1 | FileCheck %s -check-prefixes=ABORT + +%record3 = type { i32, i8 addrspace(1)* } +%record2 = type { i32, %record3 } +%record1 = type { i32, i8 addrspace(1)*, %record2 } + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i64*, i32, [0 x %KlassInfo.0*] } +%KlassInfo.1 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i64*, i32, [1 x %KlassInfo.0*] } +@"Klass1.objKlass" = external global %KlassInfo.1 +@Global0 = internal global %record3 zeroinitializer +@"Klass0.objKlass" = external global %KlassInfo.0 + +declare void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** noalias nocapture writeonly, i8 addrspace(1)** noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)*, i8*) #1 +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1) +declare cangjiegccc void @MCC_SafepointStub() +declare i32 @personality_function() +declare i8 addrspace(1)* @CJ_MCC_BeginCatch(i8*) +declare void @CJ_MCC_EndCatch() +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare i8* @CJ_MCC_GetExceptionWrapper() +declare i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8*, i32) + +define void @foo1(i8 addrspace(1)* %this, %record3 addrspace(1)* %0) #0 gc "cangjie" personality i32 ()* @personality_function { +entry: + %test0 = alloca %record3 + %test1 = bitcast %record3* %test0 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %test1, i8 0, i64 32, i1 false) + %test2 = load i8, i8 addrspace(1)* %this + br label %begin + +begin: ; preds = %entry, %body + %test = phi i8 [%test2, %entry], [%test.1, %body] + %cond = icmp ne i8 %test2, 0 + br i1 %cond, label %if, label %else + +if: ; preds = %entry + %test3 = getelementptr inbounds %record3, %record3* %test0, i64 0, i32 1 + br label %thunk + +else: ; preds = %entry + %test4.0 = invoke i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*), i32 56) + to label %thunk0 unwind label %finallyOne + +thunk0: ; preds = %else + %test4.1 = bitcast i8 addrspace(1)* %test4.0 to %record3 addrspace(1)* + %test4.2 = addrspacecast %record3 addrspace(1)* %test4.1 to %record3* + %test4 = getelementptr inbounds %record3, %record3* %test4.2, i64 0, i32 1 + br label %thunk + +thunk: ; preds = %if, %thunk0 + %test5 = phi i8 addrspace(1)** [ %test3, %if ], [ %test4, %thunk0 ] + br label %body + +body: ; preds = %thunk + %1 = bitcast i8 addrspace(1)* %this to %record1 addrspace(1)* + %2 = getelementptr inbounds %record1, %record1 addrspace(1)* %1, i32 0, i32 2 + %3 = getelementptr inbounds %record2, %record2 addrspace(1)* %2, i32 0, i32 1 + %4 = bitcast %record3 addrspace(1)* %3 to i8 addrspace(1)* + %5 = bitcast %record3 addrspace(1)* %0 to i8 addrspace(1)* + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + %6 = getelementptr inbounds %record3, %record3 addrspace(1)* %0, i64 0, i32 1 + %7 = addrspacecast i8 addrspace(1)* addrspace(1)* %6 to i8 addrspace(1)** + call void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** align 8 %test5, i8 addrspace(1)** align 8 %7, i64 8, i1 false) + %cond1 = icmp eq i8 %test, 0 + %test.1 = add i8 %test, 1 + call cangjiegccc void @MCC_SafepointStub() + br i1 %cond1, label %begin, label %end + +end: ; preds = %body + ret void + +finallyOne: ; preds = %else + %8 = landingpad token + catch i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*) + catch i8* null + %9 = call i8* @CJ_MCC_GetExceptionWrapper() + %10 = call i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %9) + call void @CJ_MCC_EndCatch() + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %10) + unreachable +} + +; ABORT: LLVM ERROR: Need write barrier in function +; ABORT: error: Aborted + +attributes #0 = { "hasRcdParam" } +attributes #1 = { "cj-runtime" "gc-leaf-function" } \ No newline at end of file diff --git a/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop3.ll b/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop3.ll new file mode 100644 index 000000000..caf2c6861 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop3.ll @@ -0,0 +1,82 @@ +; RUN: not not opt --cj-barrier-opt -S < %s -disable-output 2>&1 | FileCheck %s -check-prefixes=ABORT + +%record3 = type { i32, i8 addrspace(1)* } +%record2 = type { i32, %record3 } +%record1 = type { i32, i8 addrspace(1)*, %record2 } + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i64*, i32, [0 x %KlassInfo.0*] } +%KlassInfo.1 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i64*, i32, [1 x %KlassInfo.0*] } +@"Klass1.objKlass" = external global %KlassInfo.1 +@Global0 = internal global %record3 zeroinitializer +@"Klass0.objKlass" = external global %KlassInfo.0 + +declare void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** noalias nocapture writeonly, i8 addrspace(1)** noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)*, i8*) #1 +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1) +declare cangjiegccc void @MCC_SafepointStub() +declare i32 @personality_function() +declare i8 addrspace(1)* @CJ_MCC_BeginCatch(i8*) +declare void @CJ_MCC_EndCatch() +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare i8* @CJ_MCC_GetExceptionWrapper() +declare i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8*, i32) + +define void @foo1(i8 addrspace(1)* %this, %record3 addrspace(1)* %0) #0 gc "cangjie" personality i32 ()* @personality_function { +entry: + %test0 = alloca %record3 + %test1 = bitcast %record3* %test0 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %test1, i8 0, i64 32, i1 false) + %test2 = load i8, i8 addrspace(1)* %this + br label %begin + +begin: ; preds = %entry, %end0 + %test = phi i8 [%test2, %entry], [%test.1, %end0] + %cond = icmp ne i8 %test2, 0 + %test4.0 = invoke i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*), i32 56) + to label %thunk0 unwind label %finallyOne + +thunk0: ; preds = %begin + %test4.1 = bitcast i8 addrspace(1)* %test4.0 to %record3 addrspace(1)* + %test4.2 = addrspacecast %record3 addrspace(1)* %test4.1 to %record3* + %test4 = getelementptr inbounds %record3, %record3* %test4.2, i64 0, i32 1 + br label %body + +body: ; preds = %thunk0, %body + %1 = bitcast i8 addrspace(1)* %this to %record1 addrspace(1)* + %2 = getelementptr inbounds %record1, %record1 addrspace(1)* %1, i32 0, i32 2 + %3 = getelementptr inbounds %record2, %record2 addrspace(1)* %2, i32 0, i32 1 + %4 = bitcast %record3 addrspace(1)* %3 to i8 addrspace(1)* + %5 = bitcast %record3 addrspace(1)* %0 to i8 addrspace(1)* + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + %6 = getelementptr inbounds %record3, %record3 addrspace(1)* %0, i64 0, i32 1 + %7 = addrspacecast i8 addrspace(1)* addrspace(1)* %6 to i8 addrspace(1)** + call void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** align 8 %test4, i8 addrspace(1)** align 8 %7, i64 8, i1 false) + %cond1 = icmp eq i8 %test, 0 + %test.1 = add i8 %test, 1 + call cangjiegccc void @MCC_SafepointStub() + br i1 %cond1, label %body, label %end0 + +end0: ; preds = %body + br i1 %cond1, label %begin, label %end + +end: ; preds = %body + ret void + +finallyOne: ; preds = %else + %8 = landingpad token + catch i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*) + catch i8* null + %9 = call i8* @CJ_MCC_GetExceptionWrapper() + %10 = call i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %9) + call void @CJ_MCC_EndCatch() + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %10) + unreachable +} + +; ABORT: LLVM ERROR: Need write barrier in function +; ABORT: error: Aborted + +attributes #0 = { "hasRcdParam" } +attributes #1 = { "cj-runtime" "gc-leaf-function" } \ No newline at end of file diff --git a/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop4.ll b/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop4.ll new file mode 100644 index 000000000..0c6fe5f3c --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop4.ll @@ -0,0 +1,87 @@ +; RUN: opt --cj-barrier-opt -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-opt -S < %s | FileCheck %s + +%record3 = type { i32, i8 addrspace(1)* } +%record2 = type { i32, %record3 } +%record1 = type { i32, i8 addrspace(1)*, %record2 } + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i64*, i32, [0 x %KlassInfo.0*] } +%KlassInfo.1 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i64*, i32, [1 x %KlassInfo.0*] } +@"Klass1.objKlass" = external global %KlassInfo.1 +@Global0 = internal global %record3 zeroinitializer +@"Klass0.objKlass" = external global %KlassInfo.0 + +declare void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** noalias nocapture writeonly, i8 addrspace(1)** noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)*, i8*) #1 +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1) +declare cangjiegccc void @MCC_SafepointStub() +declare i32 @personality_function() +declare i8 addrspace(1)* @CJ_MCC_BeginCatch(i8*) +declare void @CJ_MCC_EndCatch() +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare i8* @CJ_MCC_GetExceptionWrapper() +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) + +; CHECK: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) +; CHECK: call void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** align 8 %test5, i8 addrspace(1)** align 8 %7, i64 8, i1 false) + +define void @foo1(i8 addrspace(1)* %this, %record3 addrspace(1)* %0) #0 gc "cangjie" personality i32 ()* @personality_function { +entry: + %test0 = alloca %record3 + %test1 = bitcast %record3* %test0 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %test1, i8 0, i64 32, i1 false) + %test2 = load i8, i8 addrspace(1)* %this + %cond = icmp ne i8 %test2, 0 + br i1 %cond, label %if, label %else + +if: ; preds = %entry + %test3 = getelementptr inbounds %record3, %record3* %test0, i64 0, i32 1 + br label %thunk + +else: ; preds = %entry + %test4.0 = invoke i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*), i32 56) + to label %thunk0 unwind label %finallyOne + +thunk0: ; preds = %else + %test4.1 = bitcast i8 addrspace(1)* %test4.0 to %record3 addrspace(1)* + %test4.2 = addrspacecast %record3 addrspace(1)* %test4.1 to %record3* + %test4 = getelementptr inbounds %record3, %record3* %test4.2, i64 0, i32 1 + br label %thunk + +thunk: ; preds = %if, %thunk0 + %test5 = phi i8 addrspace(1)** [ %test3, %if ], [ %test4, %thunk0 ] + br label %body + +body: ; preds = %thunk, %body + %test = phi i8 [%test2, %thunk], [%test.1, %body] + %1 = bitcast i8 addrspace(1)* %this to %record1 addrspace(1)* + %2 = getelementptr inbounds %record1, %record1 addrspace(1)* %1, i32 0, i32 2 + %3 = getelementptr inbounds %record2, %record2 addrspace(1)* %2, i32 0, i32 1 + %4 = bitcast %record3 addrspace(1)* %3 to i8 addrspace(1)* + %5 = bitcast %record3 addrspace(1)* %0 to i8 addrspace(1)* + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + %6 = getelementptr inbounds %record3, %record3 addrspace(1)* %0, i64 0, i32 1 + %7 = addrspacecast i8 addrspace(1)* addrspace(1)* %6 to i8 addrspace(1)** + call void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** align 8 %test5, i8 addrspace(1)** align 8 %7, i64 8, i1 false) + %cond1 = icmp eq i8 %test, 0 + %test.1 = add i8 %test, 1 + br i1 %cond1, label %body, label %end + +end: ; preds = %body + ret void + +finallyOne: ; preds = %else + %8 = landingpad token + catch i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*) + catch i8* null + %9 = call i8* @CJ_MCC_GetExceptionWrapper() + %10 = call i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %9) + call void @CJ_MCC_EndCatch() + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %10) + unreachable +} + +attributes #0 = { "hasRcdParam" } +attributes #1 = { "cj-runtime" "gc-leaf-function" } diff --git a/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop5.ll b/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop5.ll new file mode 100644 index 000000000..d5abb4e78 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop5.ll @@ -0,0 +1,93 @@ +; RUN: opt -enable-new-pm=false --cj-barrier-opt -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-opt -S < %s | FileCheck %s + +%record3 = type { i32, i8 addrspace(1)* } +%record2 = type { i32, %record3 } +%record1 = type { i32, i8 addrspace(1)*, %record2 } + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i64*, i32, [0 x %KlassInfo.0*] } +%KlassInfo.1 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i64*, i32, [1 x %KlassInfo.0*] } +@"Klass1.objKlass" = external global %KlassInfo.1 +@Global0 = internal global %record3 zeroinitializer +@"Klass0.objKlass" = external global %KlassInfo.0 + +declare void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** noalias nocapture writeonly, i8 addrspace(1)** noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)*, i8*) #1 +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1) +declare cangjiegccc void @MCC_SafepointStub() +declare i32 @personality_function() +declare i8 addrspace(1)* @CJ_MCC_BeginCatch(i8*) +declare void @CJ_MCC_EndCatch() +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare i8* @CJ_MCC_GetExceptionWrapper() +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) + +; CHECK: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) +; CHECK: call void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** align 8 %test5, i8 addrspace(1)** align 8 %7, i64 8, i1 false) + +define void @foo1(i8 addrspace(1)* %this, %record3 addrspace(1)* %0) #0 gc "cangjie" personality i32 ()* @personality_function { +entry: + %test0 = alloca %record3 + %test1 = bitcast %record3* %test0 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %test1, i8 0, i64 32, i1 false) + %test2 = load i8, i8 addrspace(1)* %this + br label %begin + +begin: ; preds = %entry + %cond = icmp ne i8 %test2, 0 + br i1 %cond, label %if, label %else + +if: ; preds = %entry + %test3 = getelementptr inbounds %record3, %record3* %test0, i64 0, i32 1 + br label %thunk + +else: ; preds = %entry + %test4.0 = invoke i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*), i32 56) + to label %thunk0 unwind label %finallyOne + +thunk0: ; preds = %else + %test4.1 = bitcast i8 addrspace(1)* %test4.0 to %record3 addrspace(1)* + %test4.2 = addrspacecast %record3 addrspace(1)* %test4.1 to %record3* + %test4 = getelementptr inbounds %record3, %record3* %test4.2, i64 0, i32 1 + br label %thunk + +thunk: ; preds = %if, %thunk0, %body + %test5 = phi i8 addrspace(1)** [ %test3, %if ], [ %test4, %thunk0 ], [%test5, %body] + %test = phi i8 [%test2, %if], [%test2, %thunk0], [%test.1, %body] + br label %body + +body: ; preds = %thunk + %1 = bitcast i8 addrspace(1)* %this to %record1 addrspace(1)* + %2 = getelementptr inbounds %record1, %record1 addrspace(1)* %1, i32 0, i32 2 + %3 = getelementptr inbounds %record2, %record2 addrspace(1)* %2, i32 0, i32 1 + %4 = bitcast %record3 addrspace(1)* %3 to i8 addrspace(1)* + %5 = bitcast %record3 addrspace(1)* %0 to i8 addrspace(1)* + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + %6 = getelementptr inbounds %record3, %record3 addrspace(1)* %0, i64 0, i32 1 + %7 = addrspacecast i8 addrspace(1)* addrspace(1)* %6 to i8 addrspace(1)** + call void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** align 8 %test5, i8 addrspace(1)** align 8 %7, i64 8, i1 false) + %cond1 = icmp eq i8 %test, 0 + %test.1 = add i8 %test, 1 + br i1 %cond1, label %thunk, label %end + +end: ; preds = %body + ret void + +finallyOne: ; preds = %else + %8 = landingpad token + catch i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*) + catch i8* null + %9 = call i8* @CJ_MCC_GetExceptionWrapper() + %10 = call i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %9) + call void @CJ_MCC_EndCatch() + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %10) + unreachable +} + +; ABORT: LLVM ERROR: Need write barrier in function +; ABORT: error: Aborted + +attributes #0 = { "hasRcdParam" } +attributes #1 = { "cj-runtime" "gc-leaf-function" } diff --git a/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop6.ll b/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop6.ll new file mode 100644 index 000000000..4fa1039e7 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/barrier_verifier_loop6.ll @@ -0,0 +1,83 @@ +; RUN: opt --enable-new-pm=false -cj-barrier-opt -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-opt -S < %s | FileCheck %s + +%record3 = type { i32, i8 addrspace(1)* } +%record2 = type { i32, %record3 } +%record1 = type { i32, i8 addrspace(1)*, %record2 } + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i64*, i32, [0 x %KlassInfo.0*] } +%KlassInfo.1 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i64*, i32, [1 x %KlassInfo.0*] } +@"Klass1.objKlass" = external global %KlassInfo.1 +@Global0 = internal global %record3 zeroinitializer +@"Klass0.objKlass" = external global %KlassInfo.0 + +declare void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** noalias nocapture writeonly, i8 addrspace(1)** noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)*, i8*) #1 +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1) +declare cangjiegccc void @MCC_SafepointStub() +declare i32 @personality_function() +declare i8 addrspace(1)* @CJ_MCC_BeginCatch(i8*) +declare void @CJ_MCC_EndCatch() +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare i8* @CJ_MCC_GetExceptionWrapper() +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) + +; CHECK: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) +; CHECK: call void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** align 8 %test5, i8 addrspace(1)** align 8 %7, i64 8, i1 false) + +define void @foo1(i8 addrspace(1)* %this, %record3 addrspace(1)* %0) #0 gc "cangjie" personality i32 ()* @personality_function { +entry: + %test0 = alloca %record3 + %test1 = bitcast %record3* %test0 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %test1, i8 0, i64 32, i1 false) + %test2 = load i8, i8 addrspace(1)* %this + br label %begin + +begin: ; preds = %entry + %cond = icmp ne i8 %test2, 0 + br i1 %cond, label %if, label %else + +if: ; preds = %entry + %test3 = getelementptr inbounds %record3, %record3* %test0, i64 0, i32 1 + br label %thunk + +else: ; preds = %entry + %test4.0 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*), i32 56) + br label %thunk0 + +thunk0: ; preds = %else + %test4.1 = bitcast i8 addrspace(1)* %test4.0 to %record3 addrspace(1)* + %test4.2 = addrspacecast %record3 addrspace(1)* %test4.1 to %record3* + %test4 = getelementptr inbounds %record3, %record3* %test4.2, i64 0, i32 1 + br label %thunk + +thunk: ; preds = %if, %thunk0, %body + %test5 = phi i8 addrspace(1)** [ %test3, %if ], [ %test4, %thunk0 ], [%test5, %body] + %test = phi i8 [%test2, %if], [%test2, %thunk0], [%test.1, %body] + br label %body + +body: ; preds = %thunk + %1 = bitcast i8 addrspace(1)* %this to %record1 addrspace(1)* + %2 = getelementptr inbounds %record1, %record1 addrspace(1)* %1, i32 0, i32 2 + %3 = getelementptr inbounds %record2, %record2 addrspace(1)* %2, i32 0, i32 1 + %4 = bitcast %record3 addrspace(1)* %3 to i8 addrspace(1)* + %5 = bitcast %record3 addrspace(1)* %0 to i8 addrspace(1)* + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + %6 = getelementptr inbounds %record3, %record3 addrspace(1)* %0, i64 0, i32 1 + %7 = addrspacecast i8 addrspace(1)* addrspace(1)* %6 to i8 addrspace(1)** + call void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** align 8 %test5, i8 addrspace(1)** align 8 %7, i64 8, i1 false) + %cond1 = icmp eq i8 %test, 0 + %test.1 = add i8 %test, 1 + br i1 %cond1, label %thunk, label %end + +end: ; preds = %body + ret void +} + +; ABORT: LLVM ERROR: Need write barrier in function +; ABORT: error: Aborted + +attributes #0 = { "hasRcdParam" } +attributes #1 = { "cj-runtime" "gc-leaf-function" } diff --git a/llvm/test/Transforms/CJBarrierOpt/barrierarray2.ll b/llvm/test/Transforms/CJBarrierOpt/barrierarray2.ll new file mode 100644 index 000000000..e1d36c9f7 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/barrierarray2.ll @@ -0,0 +1,71 @@ +; RUN: opt --cj-barrier-opt -enable-new-pm=false -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-opt -S < %s | FileCheck %s + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +%Unit.Type = type { i8 } +%ArrayLayout.Unit = type { %ArrayBase, [0 x %Unit.Type] } +%ArrayBase = type { %ObjLayout.Object, i64 } +%ObjLayout.Object = type { %KlassInfo.0* } +%"record._ZN7default11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN7default11std$FS$core5ArrayIuE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN7default11std$FS$core5RangeIjE" = type { i32, i32, i64, i1, i1, i1 } +%"T5_V1_uER_ZN11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEEdR_ZN11std$FS$core5ArrayIuER_ZN11std$FS$core5RangeIjEE" = type { [1 x %Unit.Type], %"record._ZN7default11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEE", double, %"record._ZN7default11std$FS$core5ArrayIuE", %"record._ZN7default11std$FS$core5RangeIjE" } + +@Unit.Val = weak_odr global %Unit.Type zeroinitializer +@A1_uE.className = weak_odr global [6 x i8] c"A1_uE\00", align 1 +@Unit.arrayKlass = weak_odr global %KlassInfo.0 { i32 2, i32 1, %BitMap* null, i8* bitcast (%KlassInfo.0* @Unit.Type.structKlass to i8*), i8** null, i64* null, i64* null, i64* bitcast ([6 x i8]* @A1_uE.className to i64*), i32 0, [0 x i8*] zeroinitializer } +@Unit.Type.structKlass = weak_odr global %KlassInfo.0 { i32 8, i32 1, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i64* @Unit.Type.uniqueAddr, i32 0, [0 x i8*] zeroinitializer } +@Unit.Type.uniqueAddr = weak_odr global i64 0 +@_ZN7default22var_1691532726212_1148E = global %"record._ZN7default11std$FS$core5RangeIjE" zeroinitializer + +declare i8 addrspace(1)* @CJ_MCC_NewArrayStub(i64 %0, i8* %1) +declare void @"T5_V1_uER_ZN11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEEdR_ZN11std$FS$core5ArrayIuER_ZN11std$FS$core5RangeIjEE.create"(%"T5_V1_uER_ZN11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEEdR_ZN11std$FS$core5ArrayIuER_ZN11std$FS$core5RangeIjEE"* noalias sret(%"T5_V1_uER_ZN11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEEdR_ZN11std$FS$core5ArrayIuER_ZN11std$FS$core5RangeIjEE"), i8 addrspace(1)*, [1 x %Unit.Type] addrspace(1)*, i8 addrspace(1)*, %"record._ZN7default11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEE" addrspace(1)*, double, i8 addrspace(1)*, %"record._ZN7default11std$FS$core5ArrayIuE" addrspace(1)*, i8 addrspace(1)*, %"record._ZN7default11std$FS$core5RangeIjE" addrspace(1)*) +declare void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)*, i8 addrspace(1)*, i8*, i64) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1) +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1) + +; CHECK: store i8 18, i8 addrspace(1)* %15, align 1 + +define void @foo(i8 addrspace(1)* %this) gc "cangjie" { +entry: + %0 = alloca %"T5_V1_uER_ZN11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEEdR_ZN11std$FS$core5ArrayIuER_ZN11std$FS$core5RangeIjEE", align 8 + %1 = alloca %"record._ZN7default11std$FS$core5ArrayIuE", align 8 + %2 = alloca %"record._ZN7default11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEE", align 8 + %3 = alloca [1 x %Unit.Type], align 8 + %4 = alloca [1 x %Unit.Type], align 8 + %5 = getelementptr inbounds [1 x %Unit.Type], [1 x %Unit.Type]* %4, i64 0, i64 0 + %6 = load %Unit.Type, %Unit.Type* @Unit.Val, align 1 + store %Unit.Type %6, %Unit.Type* %5, align 1 + %7 = bitcast [1 x %Unit.Type]* %3 to i8* + %8 = bitcast [1 x %Unit.Type]* %4 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %7, i8* align 1 %8, i64 1, i1 false) + %9 = call noalias i8 addrspace(1)* @CJ_MCC_NewArrayStub(i64 10, i8* bitcast (%KlassInfo.0* @Unit.arrayKlass to i8*)) + %10 = bitcast i8 addrspace(1)* %this to %ArrayLayout.Unit addrspace(1)* + %11 = getelementptr inbounds %ArrayLayout.Unit, %ArrayLayout.Unit addrspace(1)* %10, i32 0, i32 1, i32 0 + %12 = bitcast %Unit.Type addrspace(1)* %11 to i8 addrspace(1)* + %13 = getelementptr inbounds %Unit.Type, %Unit.Type addrspace(1)* %11, i32 0, i32 0 + store i8 30, i8 addrspace(1)* %13, align 1 + %14 = getelementptr inbounds %Unit.Type, %Unit.Type addrspace(1)* %11, i64 1 + %15 = getelementptr inbounds %Unit.Type, %Unit.Type addrspace(1)* %14, i32 0, i32 0 + store i8 18, i8 addrspace(1)* %15, align 1 + %16 = getelementptr inbounds %Unit.Type, %Unit.Type addrspace(1)* %14, i64 1 + %17 = getelementptr inbounds %Unit.Type, %Unit.Type addrspace(1)* %16, i32 0, i32 0 + store i8 22, i8 addrspace(1)* %17, align 1 + %18 = bitcast %"record._ZN7default11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEE"* %2 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %18, i8 0, i64 24, i1 false) + %19 = bitcast %"record._ZN7default11std$FS$core5ArrayIuE"* %1 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %19, i8 0, i64 24, i1 false) + %20 = addrspacecast [1 x %Unit.Type]* %3 to [1 x %Unit.Type] addrspace(1)* + %21 = addrspacecast %"record._ZN7default11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEE"* %2 to %"record._ZN7default11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEE" addrspace(1)* + %22 = addrspacecast %"record._ZN7default11std$FS$core5ArrayIuE"* %1 to %"record._ZN7default11std$FS$core5ArrayIuE" addrspace(1)* + %23 = bitcast %"T5_V1_uER_ZN11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEEdR_ZN11std$FS$core5ArrayIuER_ZN11std$FS$core5RangeIjEE"* %0 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %23, i8 0, i64 88, i1 false) + call void @"T5_V1_uER_ZN11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEEdR_ZN11std$FS$core5ArrayIuER_ZN11std$FS$core5RangeIjEE.create"(%"T5_V1_uER_ZN11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEEdR_ZN11std$FS$core5ArrayIuER_ZN11std$FS$core5RangeIjEE"* noalias sret(%"T5_V1_uER_ZN11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEEdR_ZN11std$FS$core5ArrayIuER_ZN11std$FS$core5RangeIjEE") %0, i8 addrspace(1)* null, [1 x %Unit.Type] addrspace(1)* %20, i8 addrspace(1)* null, %"record._ZN7default11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEE" addrspace(1)* %21, double 1.200000e+01, i8 addrspace(1)* null, %"record._ZN7default11std$FS$core5ArrayIuE" addrspace(1)* %22, i8 addrspace(1)* null, %"record._ZN7default11std$FS$core5RangeIjE" addrspace(1)* addrspacecast (%"record._ZN7default11std$FS$core5RangeIjE"* @_ZN7default22var_1691532726212_1148E to %"record._ZN7default11std$FS$core5RangeIjE" addrspace(1)*)) + %24 = getelementptr inbounds %"T5_V1_uER_ZN11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEEdR_ZN11std$FS$core5ArrayIuER_ZN11std$FS$core5RangeIjEE", %"T5_V1_uER_ZN11std$FS$core5ArrayIR_ZN11std$FS$core5ArrayImEEdR_ZN11std$FS$core5ArrayIuER_ZN11std$FS$core5RangeIjEE"* %0, i32 0, i32 0 + %25 = getelementptr [1 x %Unit.Type], [1 x %Unit.Type]* %24, i64 0, i64 0 + %26 = getelementptr inbounds i8, i8 addrspace(1)* %this, i64 8 + %27 = bitcast %Unit.Type* %25 to i8* + call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* %this, i8 addrspace(1)* %26, i8* %27, i64 1) + ret void +} diff --git a/llvm/test/Transforms/CJBarrierOpt/barrierarray5.ll b/llvm/test/Transforms/CJBarrierOpt/barrierarray5.ll new file mode 100644 index 000000000..7ff42d7f3 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/barrierarray5.ll @@ -0,0 +1,126 @@ +; RUN: opt --cj-barrier-opt -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-opt -S < %s | FileCheck %s + +%"record._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEEE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE" = type { i1, %"T2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEE" } +%"T2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEE" = type { %Unit.Type, %"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEE" } +%"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEE" = type { i1, %"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEE" } +%Unit.Type = type { i8 } +%"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEE" = type { i1, %"record._ZN7default11std$FS$core6OptionIbE" } +%"record._ZN7default11std$FS$core6OptionIbE" = type { i1, i1 } +%_ZN07ClosureE = type { i8*, i8 addrspace(1)* } +%"ArrayLayout._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE" = type { %ArrayBase, [0 x %"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE"] } +%ArrayBase = type { %ObjLayout.Object, i64 } +%ObjLayout.Object = type { %KlassInfo.0* } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i8*, i64*, i32, [0 x i8*] } +%BitMap = type { i32, [0 x i8] } + +@"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" = external global %KlassInfo.0 +@"_ZN11std$FS$core26NegativeArraySizeExceptionE.objKlass" = external global %KlassInfo.0 +@"A1_N_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEEE.typeName" = weak_odr global [115 x i8] c"A1_N_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEEE\00", align 1 +@"_ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE.arrayKlass" = weak_odr hidden local_unnamed_addr global %KlassInfo.0 { i32 2, i32 6, %BitMap* null, i8* bitcast (%KlassInfo.0* @"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE.structKlass" to i8*), i8** null, i64* null, i64* null, i8* null, i64* bitcast ([115 x i8]* @"A1_N_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEEE.typeName" to i64*), i32 0, [0 x i8*] zeroinitializer } +@"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE.structKlass" = weak_odr hidden global %KlassInfo.0 { i32 8, i32 6, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i8* null, i64* @"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE.uniqueAddr", i32 0, [0 x i8*] zeroinitializer } +@"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE.uniqueAddr" = weak_odr hidden global i64 0 + +declare i8 addrspace(1)* @CJ_MCC_NewArrayStub(i64, i8*) +declare i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8*, i32) +declare void @CJ_MCC_StackCheck() +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare void @MCC_SafepointStub() +declare void @"_ZN11std$FS$core25IndexOutOfBoundsException6Ev"(i8 addrspace(1)*) +declare void @"_ZN11std$FS$core26NegativeArraySizeException6Ev"(i8 addrspace(1)*) +declare void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)*, i8 addrspace(1)*, i8*, i64) +declare void @llvm.lifetime.end.p0i8(i64, i8*) +declare void @llvm.lifetime.start.p0i8(i64, i8*) +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1) + +; CHECK: call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* %3, i8 addrspace(1)* %7, i8* nonnull %.sub, i64 6) +; CHECK: call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* align 8 %20, i8* align 8 %9, i64 6, i1 false) + +define internal void @foo(i8 addrspace(1)* nocapture readnone %"__auto_v_889$BP", %"record._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEEE" addrspace(1)* nocapture writeonly %this, i64 %size, i8 addrspace(1)* nocapture readnone %"initElement$BP", %_ZN07ClosureE addrspace(1)* nocapture readonly %initElement) gc "cangjie" { +entry: + call void @CJ_MCC_StackCheck() + %callRet.i = alloca %"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE", align 8 + %0 = alloca [6 x i8], align 8 + call cangjiegccc void @MCC_SafepointStub() + %.sub = getelementptr inbounds [6 x i8], [6 x i8]* %0, i64 0, i64 0 + %1 = getelementptr inbounds %"record._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEEE", %"record._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEEE" addrspace(1)* %this, i64 0, i32 1 + store i64 0, i64 addrspace(1)* %1, align 8 + call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(6) %.sub, i8 0, i64 6, i1 false) + %arr.alloc.size.valid = icmp sgt i64 %size, -1 + br i1 %arr.alloc.size.valid, label %arr.alloc.body, label %arr.alloc.throw + +arr.alloc.throw: ; preds = %entry + %2 = tail call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core26NegativeArraySizeExceptionE.objKlass" to i8*), i32 72) + tail call void @"_ZN11std$FS$core26NegativeArraySizeException6Ev"(i8 addrspace(1)* %2) + tail call void @CJ_MCC_ThrowException(i8 addrspace(1)* %2) + unreachable + +arr.alloc.body: ; preds = %entry + %3 = tail call noalias i8 addrspace(1)* @CJ_MCC_NewArrayStub(i64 %size, i8* bitcast (%KlassInfo.0* @"_ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE.arrayKlass" to i8*)) + %size.is-zero = icmp eq i64 %size, 0 + br i1 %size.is-zero, label %arr.init.opt, label %arr.init.body.preheader + +arr.init.body.preheader: ; preds = %arr.alloc.body + %4 = getelementptr inbounds i8, i8 addrspace(1)* %3, i64 16 + %5 = bitcast i8 addrspace(1)* %4 to %"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE" addrspace(1)* + br label %arr.init.body + +arr.init.body: ; preds = %arr.init.body.preheader, %arr.init.body + %iterator.0 = phi i64 [ %8, %arr.init.body ], [ 0, %arr.init.body.preheader ] + %6 = getelementptr inbounds %"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE", %"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE" addrspace(1)* %5, i64 %iterator.0 + %7 = bitcast %"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE" addrspace(1)* %6 to i8 addrspace(1)* + call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* %3, i8 addrspace(1)* %7, i8* nonnull %.sub, i64 6) + %8 = add nuw nsw i64 %iterator.0, 1 + %exitcond.not = icmp eq i64 %8, %size + call cangjiegccc void @MCC_SafepointStub() + br i1 %exitcond.not, label %arr.init.opt, label %arr.init.body + +arr.init.opt: ; preds = %arr.init.body, %arr.alloc.body + %9 = bitcast %"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE"* %callRet.i to i8* + call void @llvm.lifetime.start.p0i8(i64 6, i8* nonnull %9) + %10 = getelementptr inbounds i8, i8 addrspace(1)* %3, i64 8 + %11 = bitcast i8 addrspace(1)* %10 to i64 addrspace(1)* + %12 = load i64, i64 addrspace(1)* %11, align 8 + %icmpslt6.i = icmp sgt i64 %12, 0 + br i1 %icmpslt6.i, label %while.body.lr.ph.i, label %"_ZN7default11std$FS$core19arrayInitByFunctionIN_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEEEA1_N_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEEEC_ZN0126$LambdaCommon_1N_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEElE.exit" + +while.body.lr.ph.i: ; preds = %arr.init.opt + %13 = bitcast %_ZN07ClosureE addrspace(1)* %initElement to void (%"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE"*, i8 addrspace(1)*, i64)* addrspace(1)* + %14 = getelementptr inbounds %_ZN07ClosureE, %_ZN07ClosureE addrspace(1)* %initElement, i64 0, i32 1 + %15 = bitcast i8 addrspace(1)* %3 to %"ArrayLayout._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE" addrspace(1)* + br label %while.body.i + +while.body.i: ; preds = %arr.if.else.i, %while.body.lr.ph.i + %atom.07.i = phi i64 [ 0, %while.body.lr.ph.i ], [ %add.i, %arr.if.else.i ] + %16 = load void (%"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE"*, i8 addrspace(1)*, i64)*, void (%"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE"*, i8 addrspace(1)*, i64)* addrspace(1)* %13, align 8 + %17 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %14, align 8 + call void %16(%"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE"* noalias nonnull sret(%"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE") %callRet.i, i8 addrspace(1)* %17, i64 %atom.07.i) + %18 = load i64, i64 addrspace(1)* %11, align 8 + %.not.i = icmp slt i64 %atom.07.i, %18 + br i1 %.not.i, label %arr.if.else.i, label %arr.if.then.i + +arr.if.then.i: ; preds = %while.body.i + %19 = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 72) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException6Ev"(i8 addrspace(1)* %19) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %19) + unreachable + +arr.if.else.i: ; preds = %while.body.i + %arr.idx.set.gep.i = getelementptr inbounds %"ArrayLayout._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE", %"ArrayLayout._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE" addrspace(1)* %15, i64 0, i32 1, i64 %atom.07.i + %20 = bitcast %"record._ZN7default11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEE" addrspace(1)* %arr.idx.set.gep.i to i8 addrspace(1)* + call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* %3, i8 addrspace(1)* %20, i8* nonnull %9, i64 6) + %add.i = add nuw nsw i64 %atom.07.i, 1 + %21 = load i64, i64 addrspace(1)* %11, align 8 + %icmpslt.i = icmp slt i64 %add.i, %21 + call cangjiegccc void @MCC_SafepointStub() + br i1 %icmpslt.i, label %while.body.i, label %"_ZN7default11std$FS$core19arrayInitByFunctionIN_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEEEA1_N_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEEEC_ZN0126$LambdaCommon_1N_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEElE.exit" + +"_ZN7default11std$FS$core19arrayInitByFunctionIN_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEEEA1_N_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEEEC_ZN0126$LambdaCommon_1N_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEElE.exit": ; preds = %arr.if.else.i, %arr.init.opt + call void @llvm.lifetime.end.p0i8(i64 6, i8* nonnull %9) + %22 = getelementptr inbounds %"record._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEEE", %"record._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEEE" addrspace(1)* %this, i64 0, i32 0 + store i8 addrspace(1)* %3, i8 addrspace(1)* addrspace(1)* %22, align 8 + %23 = getelementptr inbounds %"record._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEEE", %"record._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIT2_uN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIN_ZN11std$FS$core6OptionIbEEEEEE" addrspace(1)* %this, i64 0, i32 2 + store i64 %size, i64 addrspace(1)* %23, align 8 + ret void +} diff --git a/llvm/test/Transforms/CJBarrierOpt/barrierremove-struct1.ll b/llvm/test/Transforms/CJBarrierOpt/barrierremove-struct1.ll new file mode 100644 index 000000000..10c80c9a7 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/barrierremove-struct1.ll @@ -0,0 +1,122 @@ +; RUN: opt -enable-new-pm=false --cj-barrier-opt -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-opt -S < %s | FileCheck %s + +%KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i8*, i64*, i32, [1 x i8*] } +%"ObjLayout._ZN11net$FS$http6ServerE" = type { %"ObjLayout._ZN11std$FS$core6ObjectE", i8 addrspace(1)*, i8 addrspace(1)*, i64, i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)*, %"record._ZN11net$FS$http15TransportConfigE", %"record._ZN11std$FS$core6OptionIR_ZN10net$FS$tls15TlsServerConfigEE", %"record._ZN11std$FS$time8DurationE", %"record._ZN11std$FS$time8DurationE", %"record._ZN11std$FS$time8DurationE", %"record._ZN11std$FS$time8DurationE", i64, i64, i32, i32, i32, i32, i32, i1, %_ZN07ClosureE, %"record._ZN11net$FS$http17ServicePoolConfigE", i8 addrspace(1)* } +%"ObjLayout._ZN11std$FS$core6ObjectE" = type { %ObjLayout.Object } +%ObjLayout.Object = type { %KlassInfo.0* } +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i8*, i64*, i32, [0 x i8*] } +%"record._ZN11net$FS$http15TransportConfigE" = type { %"record._ZN11std$FS$time8DurationE", %"record._ZN11std$FS$time8DurationE", %"record._ZN11std$FS$core6OptionIlE", %"record._ZN11std$FS$core6OptionIlE", %"record._ZN13std$FS$socket21SocketKeepAliveConfigE" } +%"record._ZN11std$FS$time8DurationE" = type { i64 } +%"record._ZN11std$FS$core6OptionIlE" = type { i1, i64 } +%"record._ZN13std$FS$socket21SocketKeepAliveConfigE" = type { %"record._ZN11std$FS$time8DurationE", %"record._ZN11std$FS$time8DurationE", i32 } +%"record._ZN11std$FS$core6OptionIR_ZN10net$FS$tls15TlsServerConfigEE" = type { i1, %"record._ZN10net$FS$tls15TlsServerConfigE" } +%"record._ZN10net$FS$tls15TlsServerConfigE" = type { i64, i64, %"record._ZN11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE", %"record._ZN11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE", %"record._ZN11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE", %"record._ZN11std$FS$core6OptionIT2_R_ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEEC_ZN14crypto$FS$x50910PrivateKeyEEE", %record._ZN014Enum-Common_IXE, i64 } +%"record._ZN11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN11std$FS$core6OptionIT2_R_ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEEC_ZN14crypto$FS$x50910PrivateKeyEEE" = type { i1, %"T2_R_ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEEC_ZN14crypto$FS$x50910PrivateKeyEE" } +%"T2_R_ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEEC_ZN14crypto$FS$x50910PrivateKeyEE" = type { %"record._ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEE", i8 addrspace(1)* } +%"record._ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEE" = type { i8 addrspace(1)*, i64, i64 } +%record._ZN014Enum-Common_IXE = type { i64, i8 addrspace(1)* } +%_ZN07ClosureE = type { i8*, i8 addrspace(1)* } +%"record._ZN11net$FS$http17ServicePoolConfigE" = type { i64, i64, i64 } +%"record._ZN11std$FS$core6StringE" = type { %"record._ZN11std$FS$core5ArrayIhE", i64 } +%"record._ZN11std$FS$core5ArrayIhE" = type { i8 addrspace(1)*, i64, i64 } +%Unit.Type = type { i8 } +%"_ZN11net$FS$http13HttpExceptionE.objKlass.reflectType" = type { i32, i32, i32, i32, i32, i8* } + +@"$const_cjstring.293" = internal constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [28196 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 21790, i64 38 }, i64 0 } +@"$const_cjstring_data.0" = internal constant { i8*, i64, [28196 x i8] } { i8* bitcast (%KlassInfo.0* @roUInt8.arrayKlass to i8*), i64 28196, [28196 x i8] c"request header Upgrade is only supported in method upgrade or upgradeFromClientonly HTTP/1.1 support upgraderequest header must contain Upgrade headernot supported protocolonly the upgrade from HTTP1_1 and HTTP2_0 to WebSocket is supported.Response's Location header can only contain a single URI-referenceinvalid cookie pathinvalid cookie maxAgeinvalid cookie namehttponlysecureinvalid cookie domainmultipart/form-dataget connection failedinvalid url address or proxy address port:[HttpClient1#request] request host: request cannot contain content[HttpServer1#process] Exception during process: [HttpEngineConn1 readTimer] read request timeout[HttpEngineConn1 readHeaderTimer] read header timeout[HttpServer1#consumeRequestAndWriteResponse] failed: [HttpEngineConn1#writeResponse] write response timeout[HttpEngineConn1#consumeRequest]: finish read body[HttpEngineConn1#consumeRequest]: start read unfinished bodyonly HTTP/1.1 or HTTP/2.0 to WebSocket upgrade is supportedupgrade to websocket failed, the connection has been upgraded.the upgrade request lacks sec-websocket-key header fieldthe upgrade request's sec-websocket-version must be 13the upgrade request's origin is not allowed by server[HttpEngineConn1#writeResponse] response:\0D\0A[HttpEngineConn1#checkResponse] The final status code cannot be 100The body is not allowed in this scenarioThe connection is disconnected response cannot be written.[HttpEngine1#sendRequest] Client1_1 connection failed, reconnect and resend requestClient1.1 write request timeout and the connection is closed.[ConnNode#sendRequestTimeOut] Client1.1 read response [ConnNode#sendRequestTimeOut] Client1.1 send request bodyPATCH[HttpServer2#serve] Http/2.0 serve...[HttpServer2#mainThread] connection closed, read thread returned[HttpServer2#mainThread] exchange preface failed, unexpected frame on idle stream , which is set by server[HttpServer2#readFrames] received frame: received trailers without streamEnd flaginvalid trailer field, [HttpServer2#handleRequests] connection closed, handle thread returned[HttpServer2#handleRequests] handle thread init[Stream#handle] handle failed, Failed to fetch data, the connection or stream is closedcontent too largecontent-length and transfer-encoding can not be set togetherthe last value of transfer-encoding must be chunkedInvalid request line, more than three elements: [HttpEngineConn1#checkResponse] response headers: Invalid request target, request line: write response timeout on stream application/x-www-form-urlencodedmalformed trailer field in header[Stream#processHeaders] cancel read header timer when received headers on stream malformed request, malformed request, invalid trailer value in header, set queuePriority = [Stream#checkAndSetRequestHeaders] stream malformed request, value of :authority and host should be the samemalformed request, te header field must not contain any value other than \22trailers\22malformed request, h2 headers should not contain Invalid request method, request line: malformed request, h2 headers should be lower cases should procedure headersmalformed request, h2 pseudoHeaders No host in request URLNot HTTP protocol scheme: push request lostresponse queue closedupgrade to websocket failed, websocket extensions not supported yet[HttpEngineConn1#checkResponse] status line: upgrade to websocket failed, invalid URL scheme, the scheme must be ws or wssThe server doesn`t support HTTP/2[HttpClientEngine2#request] write request on transport error on stream: client2_0 receive response for stream Malformed responsestream closedtext/plain; charset=utf-8transport error on connection: [HttpEngineConn1#readHeaderFields] header line: content-length should not be negativeThe filename field contains \22..\22H2 body brokencookie2Filename not foundNo `:` in headercontent-disposition not foundhost should be and only oneinvalid value for expectstatus of response to upgrade request must be 101, if upgraded successfullythe handshake response has wrong subprotocolthe handshake response must have one or null subprotocol[Client#doRequest] redirect response: \0Asec-websocket-protocolthe status code should be 200, but received single header size out of limit the handshake response lacks Connection: Upgrade header fieldthe handshake response lacks Upgrade: websocket header fieldH2 upgrade failed, since ALPN negotiate failed[Client#doRequest] start send request: \0A[Client#getClient] Using HTTP/2 client[ClientStream#decodeResponseHeader] decode header ok[Client#getClient] Using HTTP/1.1 clientHTTP/2 is not enabled[HttpClientEngine2#send] start new stream,id:, please wait a secondrequest headers must contain host field100-continue[ClientStream#writeRequest] start write request of stream: the upgrade request's sec-websocket-key header value is invalid[ClientStream#writeHeaders] finish write header of stream: sec-websocket-accepthost not setredirect loop exceed 10 timesunknown body size\0D\0Abody size: 0\0D\0Acontent-length should not has different values[ClientStream#decodeResponseHeader] start decode headersThe port number is not set.The IP address number is not set.initial window size should not exceed 2^31-1, but got client2_0 write request timeout] [Server#serve] failed to submit task servicing a client connection, [Server#serve] failed to serve a client connection, [Server#serve] serve client connection beginin http/2 build a tunnel is supported only when request method is CONNECT[Server#serve] bindAndListen(pool is busy, waiting...[CoroutinePool#submit] Created new worker, current size/capacity: worker is free[Server#close] Server already closed[Server#closeGracefully] Server already closed[Server#closeGracefully] Server closing...CANGJIEUSERAGENT_1_1Connection closed.[CoroutinePool#submit] Pool is busy, discard taskFailed to fetch data, the connection is closed[HttpClientEngine2#sendRequestFrames] SocketException during sending request: [HttpClientEngine2#sendRequestFrames] connection exception occurred: [HttpClientEngine2#sendRequestFrames] send frame [HttpClientEngine2#sendRequestFrames] send cached fieldsFrame: [HttpServer2#writeFrames] connection closed, write thread returned[HttpServer2#writeFrames] write thread initshould not receive PushPromise[Client#doRequest] request finished, response: \0A[HttpClientEngine2#receiveResponse] engine already quit, ignore SocketException[HttpClientEngine2#receiveResponse] connection exception occurred when reading response: [HttpClientEngine2#receiveResponse] stream exception occurred when reading response: [HttpClientEngine2#receiveResponse] receive frame:unknown global frame[HttpClientEngine2#postProcessGlobalFrame] received unexpected global frame: Must use https scheme for HTTP/2 requestthis frame should not appear [HttpServer2#writeFrames] cancel write timer after write fields (headers or trailers) on stream [HttpServer2#writeFrames] write frame: a TRACE request can not contain content[HttpServer2#writeFieldsFrame] write frame: HTTP/2 FieldsFrame has already been accessed..Encoder#encode] header:(the upgrade request lacks sec-websocket-version header fieldresponse header lost), NameIndex(), EntryIndex(.Encoder#encode] sensitive header:(0001\09+-------------------------------+[Stream#processHeaders] cancel read timer when received headers on stream [ClientStream#writeRequest] finish write trailer of stream: |` -->[ConnNode#sendRequestTimeOut] Client1.1 send request headerInvalid prefix: Invalid strValue: invalid chunked data closed, write response failedtrailer section can not contain raw CRthis client has already closed[Client#resolveCookie] Store cookie from domain:Invalid lineThe Tls config cannot contain null character!HTTP/2 connection initial failedreadLine too longHas Priority: Some([ConnNode#sendBody] the content-length is wrongno more data frames can be sent after sending a Close frameinvalid close status codeinvalid frame type, the type must be Text, Binary, Close, Ping, Pong[Stream#writePush] should not call server push on push stream parameter out of range unexpected Continuation on stream initialize h2 connection failed, settings frame is requiredthe upgrade request to websocket must be a GET requestinitialize h2 connection failed, client preface is required[ConnNode#sendRequestTimeOut] Client1.1 send body failed, since Payload length exceed settingsIllegal data paddingIllegal headers paddingread buffer size can not be zero!Illegal push frameIllegal paddingGoaway should have at least 8 bytes payloadIllegal priority_update frame[HttpEngineConn1#checkResponse] HTTP/1.0 response is not supportedPayload length overflow[ClientStream#writeRequest] maxFrameSize=[ClientStream#writeRequest] writing body size = Header field too long[Stream#writeBody] server stream send data to queue, size [HttpServer2#exchangePreface] initialize h2 connection succeedreceiving a control frame has a payload length more than 125 bytesscheme must be https! stop:receiving an invalid mask messageInvalid method: all control frames must have a payload length of 125 bytes or lessinvalid status code, start is The size of Array is CANGJIEUSERAGENT_2_0 is not equal to array size:Range size:, step should be 1received headers and continuation frame on stream trailer must have streamEnd flagCall readRaw & writeRaw for tunnelsend header in Open state, but request don`t have trailerHTTP/2 client must have a tls config[Client#doRequest] redirect url is [ClientBuilder#build] max concurrent streams num is recommended to be over 100MaxFrameSize should between 2^14 and 2^24-1InitialWindowSize should between 1 and ?:80:443[HttpClientEngine2#processStream] connection exception occurred: [HttpClientEngine2#processStream] get stream failed Illegal setting lengthreceive data when remote closed, current window:worker is full[HttpClientEngine2#sendWindowUpdate] send global window_update:[Stream#onDataRead] connection closed, write WINDOW_UPDATE frame failedinvalid response status codeIllegal headers frame[Stream#onDataRead] purge stream when read data with streamEnd on stream , status is received data frame on stream body size exceeds content-length must not generate a 100-continue expectation in a request that does not include contentframeSize must > 0payload length of data frame exceeds window on stream [HttpServer2#sendWindowUpdate] connection closed, send WINDOW_UPDATE frame failedIllegal HTTP/2 setting SettingsMaxFrameSize value:Illegal HTTP/2 setting SettingsInitialWindowSize value:[Stream#onFieldsRead] purge stream when read headers with headerEnd on stream Unknown HTTP/2 settingread window update frame on idle streamreceived invalid frame on stream 0invalid client settings, SettingsInitialWindowSize should not exceeds 2^31invalid client settings, SettingsEnablePush should be 0 or 1the upgrade request lacks Upgrade: websocket header fieldsettings with ack should not have payload exceeds max valueSettingsInitialWindowSize cause remote window of stream exceeds 2^31-1WINDOW_UPDATE frame cause remote window on stream RstStream frame payload illegal value:stream id used upthe status code should be 101, but received outputQueue closed while writing bodyIllegal header frame finished, wait for receiving responseunexpected frame to write[HttpEngineConn1#checkResponse] the body should be empty[ClientStream#writeRequest] finish write body of stream: [ServerBuilder#build] max concurrent streams num is recommended to be over 100[Stream#onDataWrite] write data with streamEnd on open stream write data frame on [Stream#onHeadersFieldsWrite] purge stream when write headers with streamEnd on stream [Stream#writeResponse] status of final response should not be 100write headers frame on received frame on stream read request timeout on stream [Stream#startReadTimer] set read header timer, readTimeout = Invalid field line: HpackExceptionunblock framecapacity should greater than 0, but got readHeaderTimeout not set yetClosed[HttpClient2#close] client closedExpectation Failedunexpected frame: HttpConnectionExceptionframe size exceeds max frame size PongWebFrame:schemePingWebFrameupgrade to websocket failed, a response has been sent to the client.BinaryWebFramethe upgrade request to websocket on http/2.0 must be a CONNECT request, bufferLen:no settings in queue when receive ackThe result would be greater than UInt32.Max.The result would be less than UInt32.Min.the handshake response lacks Sec-Websocket-Accept header field.htm*the upgrade request to websocket must be an HTTP/1.1 or higher requestWINDOW_UPDATE frame cause remote window exceeds 2^31-1 on connection levelCopy length out of boundsFailed to update dynamic table size, new size: please use connect method to send a CONNECT requestHalfClosedLocal[Client#doRequest] request auto redirect[HttpClientEngine2#receive] start construct response of stream status line or header section can not contain raw CRJuncontent-languageReservedLocal, but found buffer:push stream id exceeds 2^31-1viatext/html; charset=utf-8[Stream#onDataWrite] purge stream when write data with streamEnd on stream Total size:content-range.htmlheaders size shouldn't be negative, got refererproxy-authorizationAn exception has occurred:Increment: CANGJIE-SERVER[Stream#writeHeader] set write timer, writeTimeout = if-unmodified-since[HttpEngineConn1#writeResponse writeTimer] write response timeoutoutputQueue closed while writing headersif-modified-sinceUnknownWebFrame.pdfinvalid size of HashMap: Length Illegal step www-authenticateHttpTimeoutException[ConnNode#sendRequestTimeOut readTimer] Client1.1 read response timeoutFeb.jpgif-matchUnsupported Media Typequeue capacity should greater than 0, but got accept-rangesNo Contentaccept-language exceeds the limit: [CoroutinePool#taskEnqueue] Endinvalid client settings, SettingsMaxFrameSize should be in range [2^14..2^24-1]500404Illegal multipart header valueif-none-matchLast Stream Id: expires204TLS out of SettingsMaxHeaderListSize :Illegal push padding[Server#protocolService] got TlsSocketpseudo header field \22:protocol\22 should be single valued and the value should be \22HTTP\22, \22TLS\22, \22WebSocket\22, \22Websocket\22, \22h2c\22, \22connect-udp\22 or \22connect-ip\22Opaque data must be 8 bytesproxy-authenticateIllegal HTTP/2 setting value:POST.pngtext/xml; charset=utf-8invalid cookie expiresimage/webp[CoroutinePool#submit] Create new workerService UnavailableConnection terminated by client.[HttpServer2#sendSettings] connection closed, send Settings frame failed[Stream#waitIfWindowNegative] server stream [HttpClientEngine2#sendRequestFrames] write thread startstream initiated by client should be identified with odd num[Stream#startReadTimer] set read header timer, readHeaderTimeout = application/wasm.wasmsocket is closed.svgThe result would be less than UInt16.Min.image/png.mjs.HeaderTable#reduceSizeTo] new limit: connection closed, write trailer failed.jsonhttp/1.0PushPromise Frame [ Unavailable For Legal Reasonsthe upgrade request lacks Connection: Upgrade header fieldreceive headers frame on state setting is nonethe upgrade request lacks Origin fieldsize of BlockingQueue should be equal or greater than Moved PermanentlyProtocol unknown, num of active client streams exceeds SettingsMaxConcurrentStreams, which is set by serverstrict-transport-security[Stream#onRSTRead] stream received rst frame, stream id: Read body timeout and the connection is closed..webpMethod Not AllowedFailed to execute task, coroutine pool is closedSunreceiving a message with invalid frame typehttp://Invalid size of LinkedList: Unauthorized.avifinvalid size of ArrayList: \22>last-modifiedPermanent RedirectOPTIONS, GET, HEAD, POST, PUT, DELETEapplication/pdf[Client#setCookie] Add cookie to requestFound), NoneIndex ]CloseWebFramePartial Content[Server#close] Server closedPayment Requiredyyyy-MM-ddTHH:mm:ssOOOOMulti-Statusimage/svg+xmlkeep-alivestream id reaches the max, 2^31 - 1Use ProxyContinueLength Required[HttpClientEngine2#onGoawayRead] HTTP/2 client received goaway, error code:Not AcceptableEarly Hints[HttpClientEngine2#startReceiveLoop] read thread startRequest URI Too LongInvalid padding size: LockedIllegal odd IdHttpException[HttpServer1#process] Exception due to read request body or trailer: Bad GatewaySrcStart is greater than or equal to the size of this arrayConflictNot Foundstream is closed when waiting window update on stream Request Entity Too LargeThe result would be less than UInt8.Min. remoteWindow=Not ExtendedmaxRequestHeaderSize not set yetFile already existupgrade to websocket failed, no host in request URLToo Earlyproxy-connectionuser-agentstatus of response to CONNECT request must be 2XX, if the tunnel is built successfullyWhen step is Casting Infinite or NaN value to integer.sec-websocket-extensionserrorCode: EndHeader: http/1.1Incorrect version: .cssBad Requestwindow over flowmethod should be tokensmax-forwards[HttpClientEngine2#sendRequestFrames] stream exception occurred: headers frame can only send in Idle or Open or HalfClosedRemote9.gifcontent-length or transfer-encoding should be set if there is a body206Invalid request method: should not set timeout on h2 streamu=The result would be less than Int32.Min., header:(chunked should not be set more than onceWebSocketExceptionProcessingthe handshake response has a wrong accept string#encodeString] `invalid hex stringOvershift: Negative shift count!Reduce size is not supported[Client#doRequest] h2 negotiate failed, and try h1 request again[HttpClientEngine2#exchangePreface] HTTP/2 connection initial successnot Connect response, call readLoop Detected which cannot be upgraded to websocket, length is Gone[Stream#onDataRead] cancel read timer after read body on stream cache-controlFailed to decode String, header string size:\09| Unprocessable Entity.xml index=MonUpgrade Required[]Temporary Redirect\00ReservedRemote\09+---+---------------------------+; Securewrote body size less than declared content-lengthIllegal stream idReset Contentpriority not supported:pathHTTP/1.0 request is not supportedNegotiateExceptionreadTimeout not set yetmax frame size should not be under 2^14 or over 2^24-1Overshift: Value of right operand is greater than or equal to the width of left operand!already responded[ClientStream#constructResponse] read continuation response400content-locationinvalid close payload[HttpServer2#close] connection closing, received window update frame on idle streamMultiple Choices[Stream#processHeaders] cancel read header timer and read timer when received trailers on stream OKlinkRequest Header Fields Too LargeTextWebFrametext/javascript; charset=utf-8, staticTableSize: The result would be greater than Int64.Max.illegal header, priority urgency should be in range 0 ~ 7, inclusiveunknown stream :Malformed multipart bodyNot Implemented0123456789abcdefNon-Authoritative InformationThe result would be greater than UInt16.Max.Switching Protocols wait for window_update, current window is negative: EndStream: ThuNot supported content-disposition typeI'm a teapotValue does not exist!\0Aindex: \09| H | Value Length: the websocket connection is failed, since Precondition RequiredToo Many RequestsRequested Range Not Satisfiableset-cookieMust have stream dependency id if priority flag setGateway TimeoutNegative copy length[client2_0] stream remote window is zeroIllegal window update frame, payload length should be 4Internal Server Errorimage/gifForbidden[HttpServer2#waitStreamComplete] connection closed gracefullyInvalid request version, request line: and the end is invalid header name: the logic of content-length and transfer-encoding is incorrect.Variant Also Negotiateswrite response end, close stream image/jpegPrecondition FailedIllegal srcDecIM Used<[HttpClientEngine2#sendRequestFrames] exception occurred in write thread: Promised Stream: Misdirected Request.jpegUnsupported msg type!Insufficient Storage[HttpClientEngine2#createPushStream] received server push on id:too many connections to the same server!, start:preheat size should between 0 and \09| 0 | Value Length: Fields Frame for id:gzip, deflate, stream.status: HttpStatusExceptionClient Stream; Path=[HttpClientEngine2#sendRequestFrames] engine already quit, ignore SocketExceptionIllegal increment/index.htmlResponse missing Location header, status code: Already Reportedmeet unexpected frame: Failed to decode string: Weight: The result would be less than Int64.Min.ping timeout!FriIllegal idJanContinuationWebFrameNetwork Authentication Requiredunexpected ack settingsMarAcceptedThe result would be greater than Int32.Max.[ConnNode#sendRequestTimeOut] connection was not returned to connPoolAprreceive data in state content-typethe poolSize must > 0[HttpClientEngine2#receiveResponse] SocketException during receiving response: connection closed, write body failedMaythe only expectation supported yet is 100-continue[Server#closeGracefully] Server closedws meet unexpected frame , should be 0 or 1[HttpClientEngine2#processStream] stream exception occurred: AugWedvarysize of initial settings exceeds default max frame size text/css; charset=utf-8[HttpClient2#request] start engine to wrong http version The node is not a member of this LinkedList.Proxy must have http scheme.:statusretry-aftercontent-encoding[HttpServer2#onWindowUpdateRead] connection closed, send internal frame failedJulOctNovapplication/json[HttpServer1#process] start handle req: Not Modified.HeaderTable#insert] index: Request Timeoutimage/avifcontent-length invalidMethodUnsupportedExceptionwrite data in HttpStreamExceptiondivThreadPoolRejectExceptionFailed to get from DummyFutureTaskFailed to get from FutureTask, task cancelled[HttpEngine1#getConn] Client1_1 get conn from poolconnection closed, send push request failedunexpected push id[ClientStream#writeRequest] start write body of stream: , size: .Decoder#parseLiteral] indexinvalid capacity of ArrayList: Idle stream received push.HeaderTable#get] Invalid index: [HttpServer2#mainThread] read frame failed, , dynamic table size:, evicted count:too many 1xx responsesDynamic table size update MUST occur at the beginning of the first header blockPadLen: .Decoder#decode] Max size: malformed request, value of pseudo header \22:path\22 should not be emptyinvalid header value: TrailerHTTP_PROXYwww, dd MMM yyyy HH:mm:ss z; Max-Age=Invalid line contains bare CRinvalid port string: Error occurred when reading framesStatus code should be 3xx besides 304Invalid pathPath: already registeredcontent-length should not be none!write push related to stream , num of active push streams exceeds SettingsMaxConcurrentStreams, which is set by clientid of new stream should be the largestmax-age[HttpEngineConn1#readRequest] exception: This client has already closed[Stream#close] connection closed, write rst failedread request headers timeout on stream " } +@"_ZN11net$FS$http13HttpExceptionE.objKlass" = weak_odr global %KlassInfo.0 { i32 36, i32 104, %BitMap* inttoptr (i64 -9223372036854775198 to %BitMap*), i8* bitcast (%KlassInfo.1* @"_ZN11std$FS$core9ExceptionE.objKlass" to i8*), i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @"_ZN11net$FS$http13HttpExceptionE.vtable", i32 0, i32 0), i64* bitcast ({ [23 x i8*], i32* }* @"_ZN11net$FS$http13HttpExceptionE.itable" to i64*), i64* null, i8* bitcast (%"_ZN11net$FS$http13HttpExceptionE.objKlass.reflectType"* @"_ZN11net$FS$http13HttpExceptionE.objKlass.reflect" to i8*), i64* bitcast ([34 x i8]* @"C_ZN11net$FS$http13HttpExceptionE.typeName" to i64*), i32 0, [0 x i8*] zeroinitializer } +@"C_ZN11net$FS$http13HttpExceptionE.typeName" = internal global [34 x i8] c"C_ZN11net$FS$http13HttpExceptionE\00", align 1 +@"_ZN11std$FS$core9ExceptionE.objKlass" = external global %KlassInfo.1 +@"_ZN11net$FS$http13HttpExceptionE.vtable" = internal global [4 x i8*] [i8* null, i8* bitcast (void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN11net$FS$http13HttpException12getClassNameEv" to i8*), i8* bitcast (void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception11$messagegetEv" to i8*), i8* bitcast (void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception8toStringEv" to i8*)] +@"_ZN11net$FS$http13HttpExceptionE.itable" = internal global { [23 x i8*], i32* } { [23 x i8*] [i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* bitcast (void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception8toStringEv" to i8*), i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null], i32* null } +@"_ZN11net$FS$http13HttpExceptionE.objKlass.reflect" = internal global %"_ZN11net$FS$http13HttpExceptionE.objKlass.reflectType" { i32 8, i32 0, i32 0, i32 0, i32 0, i8* null } +@roUInt8.arrayKlass = weak_odr global %KlassInfo.0 { i32 2, i32 1, %BitMap* null, i8* bitcast (%KlassInfo.0* @array_int8.primKlass to i8*), i8** null, i64* null, i64* null, i8* null, i64* null, i32 0, [0 x i8*] zeroinitializer } +@array_int8.primKlass = weak_odr hidden global %KlassInfo.0 { i32 1, i32 1, %BitMap* null, i8* null, i8** null, i64* null, i64* null, i8* null, i64* @array_int8.uniqueAddr, i32 0, [0 x i8*] zeroinitializer } +@array_int8.uniqueAddr = weak_odr hidden global i64 0 + +declare i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8*, i32) +declare void @CJ_MCC_StackCheck() +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare void @MCC_SafepointStub() +declare void @"_ZN11net$FS$http13HttpException12getClassNameEv"(%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*) +declare void @"_ZN11std$FS$core9Exception11$messagegetEv"(%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*) +declare void @"_ZN11std$FS$core9Exception8toStringEv"(%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*) +declare void @"_ZN11std$FS$core9Exception6ER_ZN11std$FS$core6StringE"(i8 addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) +declare void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)*, i8 addrspace(1)*, i8*, i64) +declare void @llvm.lifetime.end.p0i8(i64, i8*) +declare void @llvm.lifetime.start.p0i8(i64, i8*) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1) +declare void @llvm.memcpy.p0i8.p1i8.i64(i8*, i8 addrspace(1)*, i64, i1) +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1) + +; CHECK: call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* align 8 %12, i8* align 8 %9, i64 40, i1 false) + +define void @foo(%Unit.Type* noalias nocapture readnone sret(%Unit.Type) %0, i8 addrspace(1)* %this, i8 addrspace(1)* nocapture readnone %"certChain$BP", %"record._ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEE" addrspace(1)* nocapture readonly %certChain, i8 addrspace(1)* %certKey) gc "cangjie" { +entry: + call void @CJ_MCC_StackCheck() + %enum.val.i = alloca %"record._ZN11std$FS$core6OptionIT2_R_ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEEC_ZN14crypto$FS$x50910PrivateKeyEEE", align 8 + %enum.val = alloca %"record._ZN11std$FS$core6OptionIR_ZN10net$FS$tls15TlsServerConfigEE", align 8 + %.sroa.0 = alloca %"record._ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEE", align 8 + %cfg = alloca %"record._ZN10net$FS$tls15TlsServerConfigE", align 8 + %x = alloca %"record._ZN10net$FS$tls15TlsServerConfigE", align 8 + %ifvalue = alloca %"record._ZN10net$FS$tls15TlsServerConfigE", align 8 + %1 = bitcast i8 addrspace(1)* %this to %"ObjLayout._ZN11net$FS$http6ServerE" addrspace(1)* + %2 = getelementptr inbounds %"ObjLayout._ZN11net$FS$http6ServerE", %"ObjLayout._ZN11net$FS$http6ServerE" addrspace(1)* %1, i64 0, i32 9 + %3 = getelementptr inbounds %"record._ZN11std$FS$core6OptionIR_ZN10net$FS$tls15TlsServerConfigEE", %"record._ZN11std$FS$core6OptionIR_ZN10net$FS$tls15TlsServerConfigEE" addrspace(1)* %2, i64 0, i32 0 + %4 = load i1, i1 addrspace(1)* %3, align 1 + call cangjiegccc void @MCC_SafepointStub() + br i1 %4, label %if.else, label %if.then + +if.then: ; preds = %entry + %5 = getelementptr inbounds %"ObjLayout._ZN11net$FS$http6ServerE", %"ObjLayout._ZN11net$FS$http6ServerE" addrspace(1)* %1, i64 0, i32 9, i32 1 + %x.0..sroa_cast = bitcast %"record._ZN10net$FS$tls15TlsServerConfigE"* %x to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %x.0..sroa_cast, i8 0, i64 152, i1 false) + %6 = bitcast %"record._ZN10net$FS$tls15TlsServerConfigE" addrspace(1)* %5 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(152) %x.0..sroa_cast, i8 addrspace(1)* noundef align 8 dereferenceable(152) %6, i64 152, i1 false) + %ifvalue.0..sroa_cast = bitcast %"record._ZN10net$FS$tls15TlsServerConfigE"* %ifvalue to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %ifvalue.0..sroa_cast, i8 0, i64 152, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(152) %ifvalue.0..sroa_cast, i8* noundef nonnull align 8 dereferenceable(152) %x.0..sroa_cast, i64 152, i1 false) + %7 = bitcast %"record._ZN10net$FS$tls15TlsServerConfigE"* %cfg to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %7, i8 0, i64 152, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(152) %7, i8* noundef nonnull align 8 dereferenceable(152) %ifvalue.0..sroa_cast, i64 152, i1 false) + %.sroa.0.0..sroa_cast8 = bitcast %"record._ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEE"* %.sroa.0 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %.sroa.0.0..sroa_cast8, i8 0, i64 24, i1 false) + %.sroa.0.0..sroa_cast5 = bitcast %"record._ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEE" addrspace(1)* %certChain to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(24) %.sroa.0.0..sroa_cast8, i8 addrspace(1)* noundef align 8 dereferenceable(24) %.sroa.0.0..sroa_cast5, i64 24, i1 false) + %8 = addrspacecast %"record._ZN10net$FS$tls15TlsServerConfigE"* %cfg to %"record._ZN10net$FS$tls15TlsServerConfigE" addrspace(1)* + %9 = bitcast %"record._ZN11std$FS$core6OptionIT2_R_ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEEC_ZN14crypto$FS$x50910PrivateKeyEEE"* %enum.val.i to i8* + call void @llvm.lifetime.start.p0i8(i64 40, i8* nonnull %9) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %9, i8 0, i64 40, i1 false) + %10 = getelementptr inbounds %"record._ZN11std$FS$core6OptionIT2_R_ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEEC_ZN14crypto$FS$x50910PrivateKeyEEE", %"record._ZN11std$FS$core6OptionIT2_R_ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEEC_ZN14crypto$FS$x50910PrivateKeyEEE"* %enum.val.i, i64 0, i32 0 + store i1 false, i1* %10, align 8 + %.sroa.0.0..sroa_idx = getelementptr inbounds %"record._ZN11std$FS$core6OptionIT2_R_ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEEC_ZN14crypto$FS$x50910PrivateKeyEEE", %"record._ZN11std$FS$core6OptionIT2_R_ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEEC_ZN14crypto$FS$x50910PrivateKeyEEE"* %enum.val.i, i64 0, i32 1 + %.sroa.0.0..sroa_cast3 = bitcast %"T2_R_ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEEC_ZN14crypto$FS$x50910PrivateKeyEE"* %.sroa.0.0..sroa_idx to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %.sroa.0.0..sroa_cast3, i8* align 8 %.sroa.0.0..sroa_cast8, i64 24, i1 false) + %.sroa.3.0..sroa_idx7 = getelementptr inbounds %"record._ZN11std$FS$core6OptionIT2_R_ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEEC_ZN14crypto$FS$x50910PrivateKeyEEE", %"record._ZN11std$FS$core6OptionIT2_R_ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEEC_ZN14crypto$FS$x50910PrivateKeyEEE"* %enum.val.i, i64 0, i32 1, i32 1 + store i8 addrspace(1)* %certKey, i8 addrspace(1)** %.sroa.3.0..sroa_idx7, align 8 + %11 = getelementptr inbounds %"record._ZN10net$FS$tls15TlsServerConfigE", %"record._ZN10net$FS$tls15TlsServerConfigE" addrspace(1)* %8, i64 0, i32 5 + %12 = bitcast %"record._ZN11std$FS$core6OptionIT2_R_ZN11std$FS$core5ArrayIC_ZN14crypto$FS$x50915X509CertificateEEC_ZN14crypto$FS$x50910PrivateKeyEEE" addrspace(1)* %11 to i8 addrspace(1)* + call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* null, i8 addrspace(1)* %12, i8* nonnull %9, i64 40) + call void @llvm.lifetime.end.p0i8(i64 40, i8* nonnull %9) + %13 = bitcast %"record._ZN11std$FS$core6OptionIR_ZN10net$FS$tls15TlsServerConfigEE"* %enum.val to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %13, i8 0, i64 160, i1 false) + %14 = getelementptr inbounds %"record._ZN11std$FS$core6OptionIR_ZN10net$FS$tls15TlsServerConfigEE", %"record._ZN11std$FS$core6OptionIR_ZN10net$FS$tls15TlsServerConfigEE"* %enum.val, i64 0, i32 0 + store i1 false, i1* %14, align 8 + %15 = getelementptr inbounds %"record._ZN11std$FS$core6OptionIR_ZN10net$FS$tls15TlsServerConfigEE", %"record._ZN11std$FS$core6OptionIR_ZN10net$FS$tls15TlsServerConfigEE"* %enum.val, i64 0, i32 1 + %16 = bitcast %"record._ZN10net$FS$tls15TlsServerConfigE"* %15 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(152) %16, i8* noundef nonnull align 8 dereferenceable(152) %7, i64 152, i1 false) + %17 = bitcast %"record._ZN11std$FS$core6OptionIR_ZN10net$FS$tls15TlsServerConfigEE" addrspace(1)* %2 to i8 addrspace(1)* + call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* %this, i8 addrspace(1)* %17, i8* nonnull %13, i64 160) + ret void + +if.else: ; preds = %entry + %18 = tail call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11net$FS$http13HttpExceptionE.objKlass" to i8*), i32 104) + tail call void @"_ZN11std$FS$core9Exception6ER_ZN11std$FS$core6StringE"(i8 addrspace(1)* %18, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* addrspacecast (%"record._ZN11std$FS$core6StringE"* bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.293" to %"record._ZN11std$FS$core6StringE"*) to %"record._ZN11std$FS$core6StringE" addrspace(1)*)) + %19 = getelementptr inbounds i8, i8 addrspace(1)* %18, i64 72 + tail call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* %18, i8 addrspace(1)* %19, i8* bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.293" to i8*), i64 32) + tail call void @CJ_MCC_ThrowException(i8 addrspace(1)* %18) + unreachable +} diff --git a/llvm/test/Transforms/CJBarrierOpt/barrierremove-struct2.ll b/llvm/test/Transforms/CJBarrierOpt/barrierremove-struct2.ll new file mode 100644 index 000000000..76907d929 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/barrierremove-struct2.ll @@ -0,0 +1,521 @@ +; RUN: opt -enable-new-pm=false --cj-barrier-opt -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-opt -S < %s | FileCheck %s + +%"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" = type { i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE", i64, i64, i1, i64 } +%"record._ZN11std$FS$core6StringE" = type { %"record._ZN11std$FS$core5ArrayIhE", i64 } +%"record._ZN11std$FS$core5ArrayIhE" = type { i8 addrspace(1)*, i64, i64 } +%"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" = type { %"ObjLayout._ZN11std$FS$core6ObjectE", i64, i64, i64, i64, %"record._ZN11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core5ArrayIT4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEEE" } +%"ObjLayout._ZN11std$FS$core6ObjectE" = type { %ObjLayout.Object } +%ObjLayout.Object = type { %KlassInfo.0* } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i8*, i64*, i32, [0 x i8*] } +%BitMap = type { i32, [0 x i8] } +%"record._ZN11std$FS$core5ArrayIlE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN11std$FS$core5ArrayIT4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEEE" = type { i8 addrspace(1)*, i64, i64 } +%"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" = type { %ArrayBase, [0 x %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE"] } +%ArrayBase = type { %ObjLayout.Object, i64 } +%"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" = type { i64, i64, %"record._ZN11std$FS$core6StringE", i8 addrspace(1)* } +%ArrayLayout.Int64 = type { %ArrayBase, [0 x i64] } +%KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i8*, i64*, i32, [1 x i8*] } +%"_ZN11std$FS$core6ObjectE.objKlass.reflectType" = type { i32, i32, i32, i32, i32, i8*, i32, i32, void (i8 addrspace(1)*)*, [5 x i8]*, [2 x i8]*, %"_ZN11std$FS$core6ObjectE.objKlass.0.init.ParamInfoType"*, %KlassInfo.1*, i8* } +%"_ZN11std$FS$core6ObjectE.objKlass.0.init.ParamInfoType" = type { i32, [26 x i8]*, [7 x i8]*, i8* } +%"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass.reflectType" = type { i32, i32, i32, i32, i32, i8*, i32, i32, void (i8 addrspace(1)*)*, [5 x i8]*, [2 x i8]*, %"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass.0.init.ParamInfoType"*, %KlassInfo.0*, i8*, i32, i32, void (i8 addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*)*, [5 x i8]*, [2 x i8]*, %"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass.1.init.ParamInfoType"*, %KlassInfo.0*, i8* } +%"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass.0.init.ParamInfoType" = type { i32, [46 x i8]*, [26 x i8]*, i8* } +%"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass.1.init.ParamInfoType" = type { i32, [46 x i8]*, [26 x i8]*, i8*, i32, [26 x i8]*, [8 x i8]*, i8* } +%"_ZN11std$FS$core9ExceptionE.objKlass.reflectType" = type { i32, i32, i32, i32, i32, i8*, i32, i32, void (i8 addrspace(1)*)*, [5 x i8]*, [2 x i8]*, %"_ZN11std$FS$core9ExceptionE.objKlass.0.init.ParamInfoType"*, %KlassInfo.1*, i8*, i32, i32, void (i8 addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*)*, [5 x i8]*, [2 x i8]*, %"_ZN11std$FS$core9ExceptionE.objKlass.1.init.ParamInfoType"*, %KlassInfo.1*, i8*, i32, i32, void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)*, [12 x i8]*, [26 x i8]*, %"_ZN11std$FS$core9ExceptionE.objKlass.2.$messageget.ParamInfoType"*, %KlassInfo.1*, i8*, i32, i32, void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)*, [9 x i8]*, [26 x i8]*, %"_ZN11std$FS$core9ExceptionE.objKlass.3.toString.ParamInfoType"*, %KlassInfo.1*, i8*, i32, i32, void (%Unit.Type*, i8 addrspace(1)*)*, [16 x i8]*, [2 x i8]*, %"_ZN11std$FS$core9ExceptionE.objKlass.4.printStackTrace.ParamInfoType"*, %KlassInfo.1*, i8*, i32, i32, void (%"record._ZN11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE"*, i8 addrspace(1)*)*, [14 x i8]*, [63 x i8]*, %"_ZN11std$FS$core9ExceptionE.objKlass.5.getStackTrace.ParamInfoType"*, %KlassInfo.1*, i8* } +%"_ZN11std$FS$core9ExceptionE.objKlass.0.init.ParamInfoType" = type { i32, [29 x i8]*, [10 x i8]*, i8* } +%"_ZN11std$FS$core9ExceptionE.objKlass.1.init.ParamInfoType" = type { i32, [29 x i8]*, [10 x i8]*, i8*, i32, [26 x i8]*, [8 x i8]*, i8* } +%"_ZN11std$FS$core9ExceptionE.objKlass.2.$messageget.ParamInfoType" = type { i32, [29 x i8]*, [10 x i8]*, i8* } +%"_ZN11std$FS$core9ExceptionE.objKlass.3.toString.ParamInfoType" = type { i32, [29 x i8]*, [10 x i8]*, i8* } +%"_ZN11std$FS$core9ExceptionE.objKlass.4.printStackTrace.ParamInfoType" = type { i32, [29 x i8]*, [10 x i8]*, i8* } +%"_ZN11std$FS$core9ExceptionE.objKlass.5.getStackTrace.ParamInfoType" = type { i32, [29 x i8]*, [10 x i8]*, i8* } +%"record._ZN11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE" = type { i8 addrspace(1)*, i64, i64 } +%Unit.Type = type { i8 } +%"_ZN11std$FS$core3AnyE.objKlass.reflectType" = type { i32, i32, i32, i32, i32, i8* } +%"_ZN11std$FS$core8ToStringE.objKlass.reflectType" = type { i32, i32, i32, i32, i32, i8*, i32, i32, i8*, [9 x i8]*, [26 x i8]*, %"_ZN11std$FS$core8ToStringE.objKlass.0.toString.ParamInfoType"*, %KlassInfo.0*, i8* } +%"_ZN11std$FS$core8ToStringE.objKlass.0.toString.ParamInfoType" = type { i32, [28 x i8]*, [9 x i8]*, i8* } +%"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass.reflectType" = type { i32, i32, i32, i32, i32, i8*, i32, i32, void (i8 addrspace(1)*)*, [5 x i8]*, [2 x i8]*, %"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass.0.init.ParamInfoType"*, %KlassInfo.0*, i8*, i32, i32, void (i8 addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*)*, [5 x i8]*, [2 x i8]*, %"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass.1.init.ParamInfoType"*, %KlassInfo.0*, i8* } +%"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass.1.init.ParamInfoType" = type { i32, [58 x i8]*, [32 x i8]*, i8*, i32, [26 x i8]*, [8 x i8]*, i8* } +%"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass.0.init.ParamInfoType" = type { i32, [58 x i8]*, [32 x i8]*, i8* } + +@"$const_cjstring.8" = internal constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [1709 x i8] }* @"$const_cjstring_data.0.3" to i8*) to i8 addrspace(1)*), i64 969, i64 3 }, i64 0 } +@"$const_cjstring.7.2" = internal constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [1709 x i8] }* @"$const_cjstring_data.0.3" to i8*) to i8 addrspace(1)*), i64 574, i64 3 }, i64 0 } +@"$const_cjstring_data.0.312" = internal constant { i8*, i64, [31 x i8] } { i8* bitcast (%KlassInfo.0* @roUInt8.arrayKlass to i8*), i64 31, [31 x i8] c"ConcurrentModificationException" } +@roUInt8.arrayKlass = internal global %KlassInfo.0 { i32 2, i32 1, %BitMap* null, i8* bitcast (%KlassInfo.0* @array_int8.primKlass to i8*), i8** null, i64* null, i64* null, i8* null, i64* null, i32 0, [0 x i8*] zeroinitializer } +@array_int8.primKlass = internal global %KlassInfo.0 { i32 1, i32 1, %BitMap* null, i8* null, i8** null, i64* null, i64* null, i8* null, i64* @array_int8.uniqueAddr, i32 0, [0 x i8*] zeroinitializer } +@array_int8.uniqueAddr = internal global i64 0 +@"$const_cjstring_data.0.3" = internal constant { i8*, i64, [1709 x i8] } { i8* bitcast (%KlassInfo.0* @roUInt8.arrayKlass to i8*), i64 1709, [1709 x i8] c"The result would be less than UIntNative.Min.[ parameter out of range stop:Json String is empty!the json data is Non-standard, please check:\0AParse Error: [Line]: The Value of JsonObject does not existFail to convert to JsonFloat is not equal to array size:Array negative index accessOvershift: Value of right operand is greater than or equal to the width of left operand!Negative copy length\\0Fail to convert to JsonArray, [Pos]: \\vDstStart is greater than or equal to the size of the target arrayJsonExceptionOvershift: Negative shift count!Fail to convert to JsonString\\fadd, length is nullCasting Infinite or NaN value to integer., but the start is , [Error]: Unexpected character: 'Fail to convert to JsonNullFail to parseJsonStringFail to convert to JsonObjectIllegal step SrcStart is greater than or equal to the size of this array\\rThe result would be less than UInt32.Min.The size of Array is the value of the step should not be zero.\0AThe size of ArrayList is sub, start: and the end is Copy length out of boundsThe index \\ntrueThe result would be less than UInt8.Min.invalid size of ArrayList: falseThe result would be greater than UInt8.Max.mulThe result would be greater than Int64.Max.The result would be greater than UInt32.Max.When step is '.this data is not DataModelSeqin Char(num), num is not a valid Unicode scalar value!The result would be less than Int64.Min. of JsonArray does not exist.the value of the step should be '1'Fail to convert to JsonIntindex: \\b, size: invalid capacity of ArrayList: The result would be greater than UIntNative.Max.[]invalid size of HashMap: Value does not exist!\0AUnmatched DataModel type!, step should be 1\\tRange size:Fail to convert to JsonBool, start is " } +@"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" = internal global %KlassInfo.0 { i32 36, i32 72, %BitMap* inttoptr (i64 -9223372036854775710 to %BitMap*), i8* bitcast (%KlassInfo.1* @"_ZN11std$FS$core9ExceptionE.objKlass" to i8*), i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.vtable", i32 0, i32 0), i64* bitcast ({ [23 x i8*], i32* }* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.itable" to i64*), i64* null, i8* bitcast (%"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass.reflectType"* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass.reflect" to i8*), i64* bitcast ([46 x i8]* @"C_ZN11std$FS$core25IndexOutOfBoundsExceptionE.typeName" to i64*), i32 0, [0 x i8*] zeroinitializer } +@"_ZN11std$FS$core9ExceptionE.objKlass" = internal global %KlassInfo.1 { i32 36, i32 72, %BitMap* inttoptr (i64 -9223372036854775710 to %BitMap*), i8* bitcast (%KlassInfo.1* @"_ZN11std$FS$core6ObjectE.objKlass" to i8*), i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @"_ZN11std$FS$core9ExceptionE.vtable", i32 0, i32 0), i64* bitcast ({ [23 x i8*], i32* }* @"_ZN11std$FS$core9ExceptionE.itable" to i64*), i64* null, i8* bitcast (%"_ZN11std$FS$core9ExceptionE.objKlass.reflectType"* @"_ZN11std$FS$core9ExceptionE.objKlass.reflect" to i8*), i64* bitcast ([29 x i8]* @"C_ZN11std$FS$core9ExceptionE.typeName" to i64*), i32 1, [1 x i8*] [i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core8ToStringE.objKlass" to i8*)] } +@"_ZN11std$FS$core6ObjectE.objKlass" = internal global %KlassInfo.1 { i32 4, i32 8, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* bitcast (%KlassInfo.0* @Object.objKlass to i8*), i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"_ZN11std$FS$core6ObjectE.vtable", i32 0, i32 0), i64* bitcast (i64** @"_ZN11std$FS$core6ObjectE.itable" to i64*), i64* null, i8* bitcast (%"_ZN11std$FS$core6ObjectE.objKlass.reflectType"* @"_ZN11std$FS$core6ObjectE.objKlass.reflect" to i8*), i64* bitcast ([26 x i8]* @"C_ZN11std$FS$core6ObjectE.typeName" to i64*), i32 1, [1 x i8*] [i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core3AnyE.objKlass" to i8*)] } +@Object.objKlass = internal global %KlassInfo.0 { i32 4, i32 8, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i8* null, i64* null, i32 0, [0 x i8*] zeroinitializer } +@"_ZN11std$FS$core6ObjectE.vtable" = internal global [1 x i8*] zeroinitializer +@"_ZN11std$FS$core6ObjectE.itable" = internal global i64* null +@"_ZN11std$FS$core6ObjectE.objKlass.reflect" = internal global %"_ZN11std$FS$core6ObjectE.objKlass.reflectType" { i32 72, i32 0, i32 0, i32 1, i32 0, i8* null, i32 8, i32 1, void (i8 addrspace(1)*)* @"_ZN11std$FS$core6Object6Ev", [5 x i8]* @init.ReflectStr.396, [2 x i8]* @u.ReflectStr.397, %"_ZN11std$FS$core6ObjectE.objKlass.0.init.ParamInfoType"* @"_ZN11std$FS$core6ObjectE.objKlass.0.init.ParamInfo", %KlassInfo.1* @"_ZN11std$FS$core6ObjectE.objKlass", i8* null } +@"C_ZN11std$FS$core25IndexOutOfBoundsExceptionE.typeName" = internal global [46 x i8] c"C_ZN11std$FS$core25IndexOutOfBoundsExceptionE\00", align 1 +@"C_ZN11std$FS$core6ObjectE.typeName" = internal global [26 x i8] c"C_ZN11std$FS$core6ObjectE\00", align 1 +@"C_ZN11std$FS$core9ExceptionE.typeName" = internal global [29 x i8] c"C_ZN11std$FS$core9ExceptionE\00", align 1 +@"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.itable" = internal global { [23 x i8*], i32* } { [23 x i8*] [i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* bitcast (void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception8toStringEv" to i8*), i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null], i32* null } +@"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass.reflect" = internal global %"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass.reflectType" { i32 8, i32 0, i32 0, i32 2, i32 0, i8* null, i32 8, i32 1, void (i8 addrspace(1)*)* @"_ZN11std$FS$core25IndexOutOfBoundsException6Ev", [5 x i8]* @init.ReflectStr.396, [2 x i8]* @u.ReflectStr.397, %"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass.0.init.ParamInfoType"* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass.0.init.ParamInfo", %KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass", i8* null, i32 8, i32 2, void (i8 addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*)* @"_ZN11std$FS$core25IndexOutOfBoundsException6ER_ZN11std$FS$core6StringE", [5 x i8]* @init.ReflectStr.396, [2 x i8]* @u.ReflectStr.397, %"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass.1.init.ParamInfoType"* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass.1.init.ParamInfo", %KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass", i8* null } +@"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass.0.init.ParamInfo" = internal global %"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass.0.init.ParamInfoType" { i32 0, [46 x i8]* @"C_ZN11std$FS$core25IndexOutOfBoundsExceptionE.ReflectStr", [26 x i8]* @IndexOutOfBoundsException.ReflectStr, i8* null } +@"C_ZN11std$FS$core25IndexOutOfBoundsExceptionE.ReflectStr" = internal global [46 x i8] c"C_ZN11std$FS$core25IndexOutOfBoundsExceptionE\00", align 1 +@IndexOutOfBoundsException.ReflectStr = internal global [26 x i8] c"IndexOutOfBoundsException\00", align 1 +@"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass.1.init.ParamInfo" = internal global %"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass.1.init.ParamInfoType" { i32 0, [46 x i8]* @"C_ZN11std$FS$core25IndexOutOfBoundsExceptionE.ReflectStr", [26 x i8]* @IndexOutOfBoundsException.ReflectStr, i8* null, i32 1, [26 x i8]* @"R_ZN11std$FS$core6StringE.ReflectStr.398", [8 x i8]* @message.ReflectStr.399, i8* null } +@"R_ZN11std$FS$core6StringE.ReflectStr.398" = internal global [26 x i8] c"R_ZN11std$FS$core6StringE\00", align 1 +@"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.vtable" = internal global [4 x i8*] [i8* null, i8* bitcast (void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN11std$FS$core25IndexOutOfBoundsException12getClassNameEv" to i8*), i8* bitcast (void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception11$messagegetEv" to i8*), i8* bitcast (void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception8toStringEv" to i8*)] +@"_ZN11std$FS$core3AnyE.objKlass" = internal global %KlassInfo.0 { i32 16, i32 8, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i8* bitcast (%"_ZN11std$FS$core3AnyE.objKlass.reflectType"* @"_ZN11std$FS$core3AnyE.objKlass.reflect" to i8*), i64* bitcast ([23 x i8]* @"C_ZN11std$FS$core3AnyE.typeName" to i64*), i32 0, [0 x i8*] zeroinitializer } +@"C_ZN11std$FS$core3AnyE.typeName" = internal global [23 x i8] c"C_ZN11std$FS$core3AnyE\00", align 1 +@"_ZN11std$FS$core3AnyE.objKlass.reflect" = internal global %"_ZN11std$FS$core3AnyE.objKlass.reflectType" { i32 72, i32 0, i32 0, i32 0, i32 0, i8* null } +@"_ZN11std$FS$core6ObjectE.objKlass.0.init.ParamInfo" = internal global %"_ZN11std$FS$core6ObjectE.objKlass.0.init.ParamInfoType" { i32 0, [26 x i8]* @"C_ZN11std$FS$core6ObjectE.ReflectStr.413", [7 x i8]* @Object.ReflectStr, i8* null } +@"C_ZN11std$FS$core6ObjectE.ReflectStr.413" = internal global [26 x i8] c"C_ZN11std$FS$core6ObjectE\00", align 1 +@Object.ReflectStr = internal global [7 x i8] c"Object\00", align 1 +@"_ZN11std$FS$core8ToStringE.objKlass" = internal global %KlassInfo.0 { i32 16, i32 8, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i8* bitcast (%"_ZN11std$FS$core8ToStringE.objKlass.reflectType"* @"_ZN11std$FS$core8ToStringE.objKlass.reflect" to i8*), i64* bitcast ([28 x i8]* @"C_ZN11std$FS$core8ToStringE.typeName" to i64*), i32 0, [0 x i8*] zeroinitializer } +@"C_ZN11std$FS$core8ToStringE.typeName" = internal global [28 x i8] c"C_ZN11std$FS$core8ToStringE\00", align 1 +@"_ZN11std$FS$core8ToStringE.objKlass.reflect" = internal global %"_ZN11std$FS$core8ToStringE.objKlass.reflectType" { i32 72, i32 0, i32 0, i32 1, i32 0, i8* null, i32 584, i32 1, i8* null, [9 x i8]* @toString.ReflectStr.408, [26 x i8]* @"R_ZN11std$FS$core6StringE.ReflectStr.398", %"_ZN11std$FS$core8ToStringE.objKlass.0.toString.ParamInfoType"* @"_ZN11std$FS$core8ToStringE.objKlass.0.toString.ParamInfo", %KlassInfo.0* @"_ZN11std$FS$core8ToStringE.objKlass", i8* null } +@"_ZN11std$FS$core8ToStringE.objKlass.0.toString.ParamInfo" = internal global %"_ZN11std$FS$core8ToStringE.objKlass.0.toString.ParamInfoType" { i32 0, [28 x i8]* @"C_ZN11std$FS$core8ToStringE.ReflectStr", [9 x i8]* @ToString.ReflectStr, i8* null } +@"C_ZN11std$FS$core8ToStringE.ReflectStr" = internal global [28 x i8] c"C_ZN11std$FS$core8ToStringE\00", align 1 +@ToString.ReflectStr = internal global [9 x i8] c"ToString\00", align 1 +@"_ZN11std$FS$core9ExceptionE.itable" = internal global { [23 x i8*], i32* } { [23 x i8*] [i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* bitcast (void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception8toStringEv" to i8*), i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null], i32* null } +@"_ZN11std$FS$core9ExceptionE.objKlass.reflect" = internal global %"_ZN11std$FS$core9ExceptionE.objKlass.reflectType" { i32 72, i32 0, i32 0, i32 6, i32 0, i8* null, i32 8, i32 1, void (i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception6Ev", [5 x i8]* @init.ReflectStr.396, [2 x i8]* @u.ReflectStr.397, %"_ZN11std$FS$core9ExceptionE.objKlass.0.init.ParamInfoType"* @"_ZN11std$FS$core9ExceptionE.objKlass.0.init.ParamInfo", %KlassInfo.1* @"_ZN11std$FS$core9ExceptionE.objKlass", i8* null, i32 8, i32 2, void (i8 addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*)* @"_ZN11std$FS$core9Exception6ER_ZN11std$FS$core6StringE", [5 x i8]* @init.ReflectStr.396, [2 x i8]* @u.ReflectStr.397, %"_ZN11std$FS$core9ExceptionE.objKlass.1.init.ParamInfoType"* @"_ZN11std$FS$core9ExceptionE.objKlass.1.init.ParamInfo", %KlassInfo.1* @"_ZN11std$FS$core9ExceptionE.objKlass", i8* null, i32 72, i32 1, void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception11$messagegetEv", [12 x i8]* @"$messageget.ReflectStr", [26 x i8]* @"R_ZN11std$FS$core6StringE.ReflectStr.398", %"_ZN11std$FS$core9ExceptionE.objKlass.2.$messageget.ParamInfoType"* @"_ZN11std$FS$core9ExceptionE.objKlass.2.$messageget.ParamInfo", %KlassInfo.1* @"_ZN11std$FS$core9ExceptionE.objKlass", i8* null, i32 72, i32 1, void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception8toStringEv", [9 x i8]* @toString.ReflectStr.408, [26 x i8]* @"R_ZN11std$FS$core6StringE.ReflectStr.398", %"_ZN11std$FS$core9ExceptionE.objKlass.3.toString.ParamInfoType"* @"_ZN11std$FS$core9ExceptionE.objKlass.3.toString.ParamInfo", %KlassInfo.1* @"_ZN11std$FS$core9ExceptionE.objKlass", i8* null, i32 8, i32 1, void (%Unit.Type*, i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception15printStackTraceEv", [16 x i8]* @printStackTrace.ReflectStr, [2 x i8]* @u.ReflectStr.397, %"_ZN11std$FS$core9ExceptionE.objKlass.4.printStackTrace.ParamInfoType"* @"_ZN11std$FS$core9ExceptionE.objKlass.4.printStackTrace.ParamInfo", %KlassInfo.1* @"_ZN11std$FS$core9ExceptionE.objKlass", i8* null, i32 8, i32 1, void (%"record._ZN11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE"*, i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception13getStackTraceEv", [14 x i8]* @getStackTrace.ReflectStr, [63 x i8]* @"R_ZN11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE.ReflectStr", %"_ZN11std$FS$core9ExceptionE.objKlass.5.getStackTrace.ParamInfoType"* @"_ZN11std$FS$core9ExceptionE.objKlass.5.getStackTrace.ParamInfo", %KlassInfo.1* @"_ZN11std$FS$core9ExceptionE.objKlass", i8* null } +@"$messageget.ReflectStr" = internal global [12 x i8] c"$messageget\00", align 1 #3 +@"_ZN11std$FS$core9ExceptionE.objKlass.2.$messageget.ParamInfo" = internal global %"_ZN11std$FS$core9ExceptionE.objKlass.2.$messageget.ParamInfoType" { i32 0, [29 x i8]* @"C_ZN11std$FS$core9ExceptionE.ReflectStr", [10 x i8]* @Exception.ReflectStr, i8* null } #3 +@"_ZN11std$FS$core9ExceptionE.objKlass.3.toString.ParamInfo" = internal global %"_ZN11std$FS$core9ExceptionE.objKlass.3.toString.ParamInfoType" { i32 0, [29 x i8]* @"C_ZN11std$FS$core9ExceptionE.ReflectStr", [10 x i8]* @Exception.ReflectStr, i8* null } #3 +@printStackTrace.ReflectStr = internal global [16 x i8] c"printStackTrace\00", align 1 #3 +@"_ZN11std$FS$core9ExceptionE.objKlass.4.printStackTrace.ParamInfo" = internal global %"_ZN11std$FS$core9ExceptionE.objKlass.4.printStackTrace.ParamInfoType" { i32 0, [29 x i8]* @"C_ZN11std$FS$core9ExceptionE.ReflectStr", [10 x i8]* @Exception.ReflectStr, i8* null } #3 +@getStackTrace.ReflectStr = internal global [14 x i8] c"getStackTrace\00", align 1 #3 +@"_ZN11std$FS$core9ExceptionE.objKlass.5.getStackTrace.ParamInfo" = internal global %"_ZN11std$FS$core9ExceptionE.objKlass.5.getStackTrace.ParamInfoType" { i32 0, [29 x i8]* @"C_ZN11std$FS$core9ExceptionE.ReflectStr", [10 x i8]* @Exception.ReflectStr, i8* null } #3 +@"C_ZN11std$FS$core9ExceptionE.ReflectStr" = internal global [29 x i8] c"C_ZN11std$FS$core9ExceptionE\00", align 1 +@Exception.ReflectStr = internal global [10 x i8] c"Exception\00", align 1 +@"R_ZN11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE.ReflectStr" = internal global [63 x i8] c"R_ZN11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE\00", align 1 +@"_ZN11std$FS$core9ExceptionE.objKlass.0.init.ParamInfo" = internal global %"_ZN11std$FS$core9ExceptionE.objKlass.0.init.ParamInfoType" { i32 0, [29 x i8]* @"C_ZN11std$FS$core9ExceptionE.ReflectStr", [10 x i8]* @Exception.ReflectStr, i8* null } +@"_ZN11std$FS$core9ExceptionE.objKlass.1.init.ParamInfo" = internal global %"_ZN11std$FS$core9ExceptionE.objKlass.1.init.ParamInfoType" { i32 0, [29 x i8]* @"C_ZN11std$FS$core9ExceptionE.ReflectStr", [10 x i8]* @Exception.ReflectStr, i8* null, i32 1, [26 x i8]* @"R_ZN11std$FS$core6StringE.ReflectStr.398", [8 x i8]* @message.ReflectStr.399, i8* null } +@"_ZN11std$FS$core9ExceptionE.vtable" = internal global [4 x i8*] [i8* null, i8* bitcast (void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception12getClassNameEv" to i8*), i8* bitcast (void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception11$messagegetEv" to i8*), i8* bitcast (void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception8toStringEv" to i8*)] +@"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass" = internal global %KlassInfo.0 { i32 36, i32 72, %BitMap* inttoptr (i64 -9223372036854775710 to %BitMap*), i8* bitcast (%KlassInfo.1* @"_ZN11std$FS$core9ExceptionE.objKlass" to i8*), i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @"_ZN17std$FS$collection31ConcurrentModificationExceptionE.vtable", i32 0, i32 0), i64* bitcast ({ [23 x i8*], i32* }* @"_ZN17std$FS$collection31ConcurrentModificationExceptionE.itable" to i64*), i64* null, i8* bitcast (%"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass.reflectType"* @"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass.reflect" to i8*), i64* bitcast ([58 x i8]* @"C_ZN17std$FS$collection31ConcurrentModificationExceptionE.typeName" to i64*), i32 0, [0 x i8*] zeroinitializer } +@"C_ZN17std$FS$collection31ConcurrentModificationExceptionE.typeName" = internal global [58 x i8] c"C_ZN17std$FS$collection31ConcurrentModificationExceptionE\00", align 1 +@"_ZN17std$FS$collection31ConcurrentModificationExceptionE.itable" = internal global { [23 x i8*], i32* } { [23 x i8*] [i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* bitcast (void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception8toStringEv" to i8*), i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null], i32* null } +@"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass.reflect" = internal global %"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass.reflectType" { i32 8, i32 0, i32 0, i32 2, i32 0, i8* null, i32 8, i32 1, void (i8 addrspace(1)*)* @"_ZN17std$FS$collection31ConcurrentModificationException6Ev", [5 x i8]* @init.ReflectStr.320, [2 x i8]* @u.ReflectStr.321, %"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass.0.init.ParamInfoType"* @"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass.0.init.ParamInfo", %KlassInfo.0* @"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass", i8* null, i32 8, i32 2, void (i8 addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*)* @"_ZN17std$FS$collection31ConcurrentModificationException6ER_ZN11std$FS$core6StringE", [5 x i8]* @init.ReflectStr.320, [2 x i8]* @u.ReflectStr.321, %"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass.1.init.ParamInfoType"* @"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass.1.init.ParamInfo", %KlassInfo.0* @"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass", i8* null } +@"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass.0.init.ParamInfo" = internal global %"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass.0.init.ParamInfoType" { i32 0, [58 x i8]* @"C_ZN17std$FS$collection31ConcurrentModificationExceptionE.ReflectStr", [32 x i8]* @ConcurrentModificationException.ReflectStr, i8* null } +@"C_ZN17std$FS$collection31ConcurrentModificationExceptionE.ReflectStr" = internal global [58 x i8] c"C_ZN17std$FS$collection31ConcurrentModificationExceptionE\00", align 1 +@ConcurrentModificationException.ReflectStr = internal global [32 x i8] c"ConcurrentModificationException\00", align 1 +@"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass.1.init.ParamInfo" = internal global %"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass.1.init.ParamInfoType" { i32 0, [58 x i8]* @"C_ZN17std$FS$collection31ConcurrentModificationExceptionE.ReflectStr", [32 x i8]* @ConcurrentModificationException.ReflectStr, i8* null, i32 1, [26 x i8]* @"R_ZN11std$FS$core6StringE.ReflectStr.322", [8 x i8]* @message.ReflectStr.323, i8* null } +@"R_ZN11std$FS$core6StringE.ReflectStr.322" = internal global [26 x i8] c"R_ZN11std$FS$core6StringE\00", align 1 +@"_ZN17std$FS$collection31ConcurrentModificationExceptionE.vtable" = internal global [4 x i8*] [i8* null, i8* bitcast (void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN17std$FS$collection31ConcurrentModificationException12getClassNameEv" to i8*), i8* bitcast (void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception11$messagegetEv" to i8*), i8* bitcast (void (%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*)* @"_ZN11std$FS$core9Exception8toStringEv" to i8*)] +@init.ReflectStr.320 = internal global [5 x i8] c"init\00", align 1 +@init.ReflectStr.396 = internal global [5 x i8] c"init\00", align 1 +@message.ReflectStr.323 = internal global [8 x i8] c"message\00", align 1 +@message.ReflectStr.399 = internal global [8 x i8] c"message\00", align 1 +@toString.ReflectStr.408 = internal global [9 x i8] c"toString\00", align 1 +@u.ReflectStr.321 = internal global [2 x i8] c"u\00", align 1 +@u.ReflectStr.397 = internal global [2 x i8] c"u\00", align 1 + +declare i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8*, i32) +declare void @CJ_MCC_StackCheck() +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare void @MCC_SafepointStub() +declare void @"_ZN11std$FS$core25IndexOutOfBoundsException6Ev"(i8 addrspace(1)*) +declare void @"_ZN11std$FS$core9Exception8toStringEv"(%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*) +declare void @"_ZN11std$FS$core25IndexOutOfBoundsException6ER_ZN11std$FS$core6StringE"(i8 addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) +declare void @"_ZN11std$FS$core25IndexOutOfBoundsException12getClassNameEv"(%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*) +declare void @"_ZN11std$FS$core6Object6Ev"(i8 addrspace(1)*) +declare void @"_ZN11std$FS$core9Exception11$messagegetEv"(%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*) +declare void @"_ZN11std$FS$core9Exception13getStackTraceEv"(%"record._ZN11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE"*, i8 addrspace(1)*) +declare void @"_ZN11std$FS$core9Exception15printStackTraceEv"(%Unit.Type*, i8 addrspace(1)*) +declare void @"_ZN11std$FS$core9Exception6ER_ZN11std$FS$core6StringE"(i8 addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) +declare void @"_ZN11std$FS$core9Exception6Ev"(i8 addrspace(1)*) +declare void @"_ZN11std$FS$core9Exception12getClassNameEv"(%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*) +declare void @"_ZN17std$FS$collection31ConcurrentModificationException6Ev"(i8 addrspace(1)*) +declare void @"_ZN17std$FS$collection31ConcurrentModificationException6ER_ZN11std$FS$core6StringE"(i8 addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) +declare void @"_ZN17std$FS$collection31ConcurrentModificationException12getClassNameEv"(%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*) +declare i1 @"_ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueE6resizeEl"(i8 addrspace(1)*, i64) +declare void @"_ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueE9entryViewER_ZN11std$FS$core6StringE"(%"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE"*, i8 addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) +declare void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)*, i8 addrspace(1)*, i8*, i64) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1) +declare void @llvm.memcpy.p0i8.p1i8.i64(i8*, i8 addrspace(1)*, i64, i1) +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1) +declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) +declare { i64, i1 } @llvm.ssub.with.overflow.i64(i64, i64) +declare i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) + +; CHECK: store i64 %20, i64 addrspace(1)* %21 +; CHECK: store i64 %56, i64 addrspace(1)* %57 +; CHECK: store i64 %105, i64 addrspace(1)* %0 +; CHECK: store i1 false, i1 addrspace(1)* %8 + +define internal i8 addrspace(1)* @foo1(i8 addrspace(1)* nocapture %"__auto_v_1954$BP", %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* nocapture %this, i8 addrspace(1)* %v) gc "cangjie" { +entry: + call void @CJ_MCC_StackCheck() + %value108 = alloca %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", align 8 + %.sroa.444 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %temp.sroa.4 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %arr.get102.sroa.4 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %value = alloca %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", align 8 + %.sroa.4 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %0 = getelementptr inbounds %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %this, i64 0, i32 5 + %1 = load i64, i64 addrspace(1)* %0, align 8 + %2 = getelementptr inbounds %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %this, i64 0, i32 0 + %3 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %2, align 8 + %4 = getelementptr inbounds i8, i8 addrspace(1)* %3, i64 32 + %5 = bitcast i8 addrspace(1)* %4 to i64 addrspace(1)* + %6 = load i64, i64 addrspace(1)* %5, align 8 + %icmpne.not = icmp eq i64 %1, %6 + call cangjiegccc void @MCC_SafepointStub() + br i1 %icmpne.not, label %if.else, label %if.then + +if.then: ; preds = %entry + %7 = tail call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN17std$FS$collection31ConcurrentModificationExceptionE.objKlass" to i8*), i32 72) + tail call void @"_ZN17std$FS$collection31ConcurrentModificationException6Ev"(i8 addrspace(1)* %7) + tail call void @CJ_MCC_ThrowException(i8 addrspace(1)* %7) + unreachable + +if.else: ; preds = %entry + %8 = getelementptr inbounds %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %this, i64 0, i32 4 + %9 = load i1, i1 addrspace(1)* %8, align 1 + br i1 %9, label %if.then5, label %if.else4 + +if.then5: ; preds = %if.else + %10 = getelementptr inbounds %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %this, i64 0, i32 2 + %11 = load i64, i64 addrspace(1)* %10, align 8 + %12 = getelementptr inbounds i8, i8 addrspace(1)* %3, i64 56 + %13 = bitcast i8 addrspace(1)* %12 to i64 addrspace(1)* + %14 = load i64, i64 addrspace(1)* %13, align 8 + %sub = add i64 %14, -1 + %and = and i64 %sub, %11 + %15 = getelementptr inbounds i8, i8 addrspace(1)* %3, i64 24 + %16 = bitcast i8 addrspace(1)* %15 to i64 addrspace(1)* + %17 = load i64, i64 addrspace(1)* %16, align 8 + %icmpsgt = icmp sgt i64 %17, 0 + br i1 %icmpsgt, label %if.then13, label %if.else12 + +if.then13: ; preds = %if.then5 + %18 = getelementptr inbounds i8, i8 addrspace(1)* %3, i64 16 + %19 = bitcast i8 addrspace(1)* %18 to i64 addrspace(1)* + %20 = load i64, i64 addrspace(1)* %19, align 8 + %21 = getelementptr inbounds %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %this, i64 0, i32 3 + store i64 %20, i64 addrspace(1)* %21, align 8 + %22 = bitcast %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %this to %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* addrspace(1)* + %23 = load %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)*, %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* addrspace(1)* %22, align 8 + %24 = load i64, i64 addrspace(1)* %21, align 8 + %25 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %23, i64 0, i32 6, i32 2 + %26 = load i64, i64 addrspace(1)* %25, align 8 + %icmpuge.not = icmp ult i64 %24, %26 + br i1 %icmpuge.not, label %if.else18, label %if.then19 + +if.then19: ; preds = %if.then13 + %27 = tail call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 72) + tail call void @"_ZN11std$FS$core25IndexOutOfBoundsException6Ev"(i8 addrspace(1)* %27) + tail call void @CJ_MCC_ThrowException(i8 addrspace(1)* %27) + unreachable + +if.else18: ; preds = %if.then13 + %28 = bitcast %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %23 to i8 addrspace(1)* + %29 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %23, i64 0, i32 6 + %30 = bitcast %"record._ZN11std$FS$core5ArrayIT4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEEE" addrspace(1)* %29 to %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* addrspace(1)* + %31 = load %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)*, %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* addrspace(1)* %30, align 8 + %32 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %23, i64 0, i32 6, i32 1 + %33 = load i64, i64 addrspace(1)* %32, align 8 + %add = add i64 %33, %24 + %arr.get.sroa.2.0..sroa_idx5 = getelementptr inbounds %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %31, i64 0, i32 1, i64 %add, i32 1 + %arr.get.sroa.2.0.copyload = load i64, i64 addrspace(1)* %arr.get.sroa.2.0..sroa_idx5, align 8 + %34 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %23, i64 0, i32 2 + store i64 %arr.get.sroa.2.0.copyload, i64 addrspace(1)* %34, align 8 + %35 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %2, align 8 + %36 = getelementptr inbounds i8, i8 addrspace(1)* %35, i64 24 + %37 = bitcast i8 addrspace(1)* %36 to i64 addrspace(1)* + %38 = load i64, i64 addrspace(1)* %37, align 8 + %39 = tail call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %38, i64 -1) + %.fca.1.extract = extractvalue { i64, i1 } %39, 1 + br i1 %.fca.1.extract, label %overflow, label %normal + +normal: ; preds = %if.else18 + %.fca.0.extract = extractvalue { i64, i1 } %39, 0 + store i64 %.fca.0.extract, i64 addrspace(1)* %37, align 8 + br label %if.end11 + +overflow: ; preds = %if.else18 + %40 = tail call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* addrspacecast (%"record._ZN11std$FS$core6StringE"* bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.8" to %"record._ZN11std$FS$core6StringE"*) to %"record._ZN11std$FS$core6StringE" addrspace(1)*)) + tail call void @CJ_MCC_ThrowException(i8 addrspace(1)* %40) + unreachable + +if.else12: ; preds = %if.then5 + %41 = getelementptr inbounds i8, i8 addrspace(1)* %3, i64 8 + %42 = bitcast i8 addrspace(1)* %41 to i64 addrspace(1)* + %43 = load i64, i64 addrspace(1)* %42, align 8 + %44 = tail call { i64, i1 } @llvm.ssub.with.overflow.i64(i64 %43, i64 %17) + %.fca.1.extract9 = extractvalue { i64, i1 } %44, 1 + br i1 %.fca.1.extract9, label %overflow26, label %normal27 + +normal27: ; preds = %if.else12 + %.fca.0.extract8 = extractvalue { i64, i1 } %44, 0 + %45 = tail call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %.fca.0.extract8, i64 1) + %.fca.1.extract12 = extractvalue { i64, i1 } %45, 1 + br i1 %.fca.1.extract12, label %overflow31, label %normal32 + +overflow26: ; preds = %if.else12 + %46 = tail call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* addrspacecast (%"record._ZN11std$FS$core6StringE"* bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.8" to %"record._ZN11std$FS$core6StringE"*) to %"record._ZN11std$FS$core6StringE" addrspace(1)*)) + tail call void @CJ_MCC_ThrowException(i8 addrspace(1)* %46) + unreachable + +normal32: ; preds = %normal27 + %.fca.0.extract11 = extractvalue { i64, i1 } %45, 0 + %47 = tail call fastcc i1 @"_ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueE6resizeEl"(i8 addrspace(1)* %3, i64 %.fca.0.extract11) + br i1 %47, label %if.then36, label %normal32.if.end34_crit_edge + +normal32.if.end34_crit_edge: ; preds = %normal32 + %.phi.trans.insert = bitcast %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %this to %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* addrspace(1)* + %.pre = load %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)*, %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* addrspace(1)* %.phi.trans.insert, align 8 + br label %if.end34 + +overflow31: ; preds = %normal27 + %48 = tail call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* addrspacecast (%"record._ZN11std$FS$core6StringE"* bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.7.2" to %"record._ZN11std$FS$core6StringE"*) to %"record._ZN11std$FS$core6StringE" addrspace(1)*)) + tail call void @CJ_MCC_ThrowException(i8 addrspace(1)* %48) + unreachable + +if.then36: ; preds = %normal32 + %49 = load i64, i64 addrspace(1)* %10, align 8 + %50 = bitcast %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %this to %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* addrspace(1)* + %51 = load %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)*, %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* addrspace(1)* %50, align 8 + %52 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %51, i64 0, i32 5, i32 2 + %53 = load i64, i64 addrspace(1)* %52, align 8 + %sub42 = add i64 %53, -1 + %and43 = and i64 %sub42, %49 + br label %if.end34 + +if.end34: ; preds = %if.then36, %normal32.if.end34_crit_edge + %54 = phi %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* [ %51, %if.then36 ], [ %.pre, %normal32.if.end34_crit_edge ] + %bucketIndex.0 = phi i64 [ %and43, %if.then36 ], [ %and, %normal32.if.end34_crit_edge ] + %55 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %54, i64 0, i32 1 + %56 = load i64, i64 addrspace(1)* %55, align 8 + %57 = getelementptr inbounds %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %this, i64 0, i32 3 + store i64 %56, i64 addrspace(1)* %57, align 8 + %58 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %2, align 8 + %59 = getelementptr inbounds i8, i8 addrspace(1)* %58, i64 8 + %60 = bitcast i8 addrspace(1)* %59 to i64 addrspace(1)* + %61 = load i64, i64 addrspace(1)* %60, align 8 + %62 = tail call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %61, i64 1) + %.fca.1.extract15 = extractvalue { i64, i1 } %62, 1 + br i1 %.fca.1.extract15, label %overflow47, label %normal48 + +normal48: ; preds = %if.end34 + %.fca.0.extract14 = extractvalue { i64, i1 } %62, 0 + store i64 %.fca.0.extract14, i64 addrspace(1)* %60, align 8 + br label %if.end11 + +overflow47: ; preds = %if.end34 + %63 = tail call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* addrspacecast (%"record._ZN11std$FS$core6StringE"* bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.7.2" to %"record._ZN11std$FS$core6StringE"*) to %"record._ZN11std$FS$core6StringE" addrspace(1)*)) + tail call void @CJ_MCC_ThrowException(i8 addrspace(1)* %63) + unreachable + +if.end11: ; preds = %normal48, %normal + %bucketIndex.1 = phi i64 [ %and, %normal ], [ %bucketIndex.0, %normal48 ] + %.pre-phi.pre-phi = bitcast %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %this to %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* addrspace(1)* + %64 = load %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)*, %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* addrspace(1)* %.pre-phi.pre-phi, align 8 + %65 = getelementptr inbounds %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %this, i64 0, i32 3 + %66 = load i64, i64 addrspace(1)* %65, align 8 + %67 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %64, i64 0, i32 5, i32 2 + %68 = load i64, i64 addrspace(1)* %67, align 8 + %icmpuge57.not = icmp ult i64 %bucketIndex.1, %68 + br i1 %icmpuge57.not, label %if.else59, label %if.then60 + +if.then60: ; preds = %if.end11 + %69 = tail call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 72) + tail call void @"_ZN11std$FS$core25IndexOutOfBoundsException6Ev"(i8 addrspace(1)* %69) + tail call void @CJ_MCC_ThrowException(i8 addrspace(1)* %69) + unreachable + +if.else59: ; preds = %if.end11 + %70 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %64, i64 0, i32 5 + %71 = load i64, i64 addrspace(1)* %10, align 8 + %72 = bitcast %"record._ZN11std$FS$core5ArrayIlE" addrspace(1)* %70 to %ArrayLayout.Int64 addrspace(1)* addrspace(1)* + %73 = load %ArrayLayout.Int64 addrspace(1)*, %ArrayLayout.Int64 addrspace(1)* addrspace(1)* %72, align 8 + %74 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %64, i64 0, i32 5, i32 1 + %75 = load i64, i64 addrspace(1)* %74, align 8 + %add62 = add i64 %75, %bucketIndex.1 + %arr.idx.get.gep63 = getelementptr inbounds %ArrayLayout.Int64, %ArrayLayout.Int64 addrspace(1)* %73, i64 0, i32 1, i64 %add62 + %76 = load i64, i64 addrspace(1)* %arr.idx.get.gep63, align 8 + %.sroa.4.0.sroa_cast23 = bitcast %"record._ZN11std$FS$core6StringE"* %.sroa.4 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %.sroa.4.0.sroa_cast23, i8 0, i64 32, i1 false) + %.sroa.4.16..sroa_idx = getelementptr inbounds %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %this, i64 0, i32 1 + %.sroa.4.16..sroa_cast = bitcast %"record._ZN11std$FS$core6StringE" addrspace(1)* %.sroa.4.16..sroa_idx to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(32) %.sroa.4.0.sroa_cast23, i8 addrspace(1)* noundef align 8 dereferenceable(32) %.sroa.4.16..sroa_cast, i64 32, i1 false) + %77 = bitcast %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE"* %value to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %77, i8 0, i64 56, i1 false) + %.sroa.0.0..sroa_idx = getelementptr inbounds %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE"* %value, i64 0, i32 0 + store i64 %71, i64* %.sroa.0.0..sroa_idx, align 8 + %.sroa.3.0..sroa_idx20 = getelementptr inbounds %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE"* %value, i64 0, i32 1 + store i64 %76, i64* %.sroa.3.0..sroa_idx20, align 8 + %.sroa.4.0..sroa_idx = getelementptr inbounds %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE"* %value, i64 0, i32 2 + %.sroa.4.0..sroa_cast = bitcast %"record._ZN11std$FS$core6StringE"* %.sroa.4.0..sroa_idx to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(32) %.sroa.4.0..sroa_cast, i8* noundef nonnull align 8 dereferenceable(32) %.sroa.4.0.sroa_cast23, i64 32, i1 false) + %.sroa.5.0..sroa_idx22 = getelementptr inbounds %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE"* %value, i64 0, i32 3 + store i8 addrspace(1)* %v, i8 addrspace(1)** %.sroa.5.0..sroa_idx22, align 8 + %78 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %64, i64 0, i32 6, i32 2 + %79 = load i64, i64 addrspace(1)* %78, align 8 + %icmpuge66.not = icmp ult i64 %66, %79 + br i1 %icmpuge66.not, label %if.else68, label %if.then69 + +if.then69: ; preds = %if.else59 + %80 = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 72) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException6Ev"(i8 addrspace(1)* %80) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %80) + unreachable + +if.else68: ; preds = %if.else59 + %81 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %64, i64 0, i32 6, i32 0 + %82 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %81, align 8 + %83 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %64, i64 0, i32 6, i32 1 + %84 = load i64, i64 addrspace(1)* %83, align 8 + %add71 = add i64 %84, %66 + %85 = bitcast i8 addrspace(1)* %82 to %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* + %arr.idx.set.gep = getelementptr inbounds %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %85, i64 0, i32 1, i64 %add71 + %86 = bitcast %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %arr.idx.set.gep to i8 addrspace(1)* + call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* %82, i8 addrspace(1)* %86, i8* nonnull %77, i64 56) + %87 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %2, align 8 + %88 = getelementptr inbounds i8, i8 addrspace(1)* %87, i64 32 + %89 = bitcast i8 addrspace(1)* %88 to i64 addrspace(1)* + %90 = load i64, i64 addrspace(1)* %89, align 8 + %91 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %90, i64 1) + %.fca.1.extract26 = extractvalue { i64, i1 } %91, 1 + br i1 %.fca.1.extract26, label %overflow75, label %normal76 + +normal76: ; preds = %if.else68 + %.fca.0.extract25 = extractvalue { i64, i1 } %91, 0 + store i64 %.fca.0.extract25, i64 addrspace(1)* %89, align 8 + %92 = load %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)*, %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* addrspace(1)* %.pre-phi.pre-phi, align 8 + %93 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %92, i64 0, i32 5, i32 2 + %94 = load i64, i64 addrspace(1)* %93, align 8 + %icmpuge84.not = icmp ult i64 %bucketIndex.1, %94 + br i1 %icmpuge84.not, label %if.else86, label %if.then87 + +overflow75: ; preds = %if.else68 + %95 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* addrspacecast (%"record._ZN11std$FS$core6StringE"* bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.7.2" to %"record._ZN11std$FS$core6StringE"*) to %"record._ZN11std$FS$core6StringE" addrspace(1)*)) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %95) + unreachable + +if.then87: ; preds = %normal76 + %96 = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 72) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException6Ev"(i8 addrspace(1)* %96) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %96) + unreachable + +if.else86: ; preds = %normal76 + %97 = load i64, i64 addrspace(1)* %65, align 8 + %98 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %92, i64 0, i32 5, i32 0 + %99 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %98, align 8 + %100 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %92, i64 0, i32 5, i32 1 + %101 = load i64, i64 addrspace(1)* %100, align 8 + %add89 = add i64 %101, %bucketIndex.1 + %102 = bitcast i8 addrspace(1)* %99 to %ArrayLayout.Int64 addrspace(1)* + %arr.idx.set.gep90 = getelementptr inbounds %ArrayLayout.Int64, %ArrayLayout.Int64 addrspace(1)* %102, i64 0, i32 1, i64 %add89 + store i64 %97, i64 addrspace(1)* %arr.idx.set.gep90, align 8 + %103 = load %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)*, %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* addrspace(1)* %.pre-phi.pre-phi, align 8 + %104 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %103, i64 0, i32 4 + %105 = load i64, i64 addrspace(1)* %104, align 8 + store i64 %105, i64 addrspace(1)* %0, align 8 + store i1 false, i1 addrspace(1)* %8, align 1 + br label %if.end3 + +if.else4: ; preds = %if.else + %106 = getelementptr inbounds %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %this, i64 0, i32 3 + %107 = load i64, i64 addrspace(1)* %106, align 8 + %108 = getelementptr inbounds i8, i8 addrspace(1)* %3, i64 80 + %109 = bitcast i8 addrspace(1)* %108 to i64 addrspace(1)* + %110 = load i64, i64 addrspace(1)* %109, align 8 + %icmpuge95.not = icmp ult i64 %107, %110 + br i1 %icmpuge95.not, label %if.else97, label %if.then98 + +if.then98: ; preds = %if.else4 + %111 = tail call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 72) + tail call void @"_ZN11std$FS$core25IndexOutOfBoundsException6Ev"(i8 addrspace(1)* %111) + tail call void @CJ_MCC_ThrowException(i8 addrspace(1)* %111) + unreachable + +if.else97: ; preds = %if.else4 + %112 = getelementptr inbounds i8, i8 addrspace(1)* %3, i64 64 + %113 = bitcast i8 addrspace(1)* %112 to %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* addrspace(1)* + %114 = load %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)*, %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* addrspace(1)* %113, align 8 + %115 = getelementptr inbounds i8, i8 addrspace(1)* %3, i64 72 + %116 = bitcast i8 addrspace(1)* %115 to i64 addrspace(1)* + %117 = load i64, i64 addrspace(1)* %116, align 8 + %add100 = add i64 %117, %107 + %arr.get102.sroa.4.0.sroa_cast39 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.get102.sroa.4 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %arr.get102.sroa.4.0.sroa_cast39, i8 0, i64 32, i1 false) + %arr.get102.sroa.0.0..sroa_idx = getelementptr inbounds %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %114, i64 0, i32 1, i64 %add100, i32 0 + %arr.get102.sroa.0.0.copyload = load i64, i64 addrspace(1)* %arr.get102.sroa.0.0..sroa_idx, align 8 + %arr.get102.sroa.3.0..sroa_idx36 = getelementptr inbounds %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %114, i64 0, i32 1, i64 %add100, i32 1 + %arr.get102.sroa.3.0.copyload = load i64, i64 addrspace(1)* %arr.get102.sroa.3.0..sroa_idx36, align 8 + %arr.get102.sroa.4.0..sroa_idx = getelementptr inbounds %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %114, i64 0, i32 1, i64 %add100, i32 2 + %arr.get102.sroa.4.0..sroa_cast = bitcast %"record._ZN11std$FS$core6StringE" addrspace(1)* %arr.get102.sroa.4.0..sroa_idx to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(32) %arr.get102.sroa.4.0.sroa_cast39, i8 addrspace(1)* noundef align 8 dereferenceable(32) %arr.get102.sroa.4.0..sroa_cast, i64 32, i1 false) + %arr.get102.sroa.5.0..sroa_idx38 = getelementptr inbounds %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %114, i64 0, i32 1, i64 %add100, i32 3 + %arr.get102.sroa.5.0.copyload = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %arr.get102.sroa.5.0..sroa_idx38, align 8 + %temp.sroa.4.0.sroa_cast49 = bitcast %"record._ZN11std$FS$core6StringE"* %temp.sroa.4 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %temp.sroa.4.0.sroa_cast49, i8 0, i64 32, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(32) %temp.sroa.4.0.sroa_cast49, i8* noundef nonnull align 8 dereferenceable(32) %arr.get102.sroa.4.0.sroa_cast39, i64 32, i1 false) + %118 = bitcast %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %this to %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* addrspace(1)* + %119 = load %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)*, %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* addrspace(1)* %118, align 8 + %120 = load i64, i64 addrspace(1)* %106, align 8 + %.sroa.444.0.sroa_cast48 = bitcast %"record._ZN11std$FS$core6StringE"* %.sroa.444 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %.sroa.444.0.sroa_cast48, i8 0, i64 32, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(32) %.sroa.444.0.sroa_cast48, i8* noundef nonnull align 8 dereferenceable(32) %temp.sroa.4.0.sroa_cast49, i64 32, i1 false) + %121 = bitcast %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE"* %value108 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %121, i8 0, i64 56, i1 false) + %.sroa.041.0..sroa_idx = getelementptr inbounds %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE"* %value108, i64 0, i32 0 + store i64 %arr.get102.sroa.0.0.copyload, i64* %.sroa.041.0..sroa_idx, align 8 + %.sroa.342.0..sroa_idx43 = getelementptr inbounds %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE"* %value108, i64 0, i32 1 + store i64 %arr.get102.sroa.3.0.copyload, i64* %.sroa.342.0..sroa_idx43, align 8 + %.sroa.444.0..sroa_idx = getelementptr inbounds %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE"* %value108, i64 0, i32 2 + %.sroa.444.0..sroa_cast = bitcast %"record._ZN11std$FS$core6StringE"* %.sroa.444.0..sroa_idx to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(32) %.sroa.444.0..sroa_cast, i8* noundef nonnull align 8 dereferenceable(32) %.sroa.444.0.sroa_cast48, i64 32, i1 false) + %.sroa.546.0..sroa_idx47 = getelementptr inbounds %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE"* %value108, i64 0, i32 3 + store i8 addrspace(1)* %v, i8 addrspace(1)** %.sroa.546.0..sroa_idx47, align 8 + %122 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %119, i64 0, i32 6, i32 2 + %123 = load i64, i64 addrspace(1)* %122, align 8 + %icmpuge111.not = icmp ult i64 %120, %123 + br i1 %icmpuge111.not, label %if.else113, label %if.then114 + +if.then114: ; preds = %if.else97 + %124 = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 72) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException6Ev"(i8 addrspace(1)* %124) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %124) + unreachable + +if.else113: ; preds = %if.else97 + %125 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %119, i64 0, i32 6, i32 0 + %126 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %125, align 8 + %127 = getelementptr inbounds %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ObjLayout._ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %119, i64 0, i32 6, i32 1 + %128 = load i64, i64 addrspace(1)* %127, align 8 + %add116 = add i64 %128, %120 + %129 = bitcast i8 addrspace(1)* %126 to %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* + %arr.idx.set.gep117 = getelementptr inbounds %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"ArrayLayout.T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %129, i64 0, i32 1, i64 %add116 + %130 = bitcast %"T4_llR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %arr.idx.set.gep117 to i8 addrspace(1)* + call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* %126, i8 addrspace(1)* %130, i8* nonnull %121, i64 56) + br label %if.end3 + +if.end3: ; preds = %if.else113, %if.else86 + %ifvalue2.0 = phi i8 addrspace(1)* [ %v, %if.else86 ], [ %arr.get102.sroa.5.0.copyload, %if.else113 ] + ret i8 addrspace(1)* %ifvalue2.0 +} + +define internal i1 @foo2(i8 addrspace(1)* %this, i8 addrspace(1)* %"key$BP", %"record._ZN11std$FS$core6StringE" addrspace(1)* %key, i8 addrspace(1)* %value) gc "cangjie" { +entry: + call void @CJ_MCC_StackCheck() + %view = alloca %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", align 8 + %callRet = alloca %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", align 8 + %0 = bitcast %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE"* %callRet to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %0, i8 0, i64 72, i1 false) + call cangjiegccc void @MCC_SafepointStub() + call void @"_ZN17std$FS$collection7HashMapIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueE9entryViewER_ZN11std$FS$core6StringE"(%"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE"* noalias nonnull sret(%"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE") %callRet, i8 addrspace(1)* %this, i8 addrspace(1)* %"key$BP", %"record._ZN11std$FS$core6StringE" addrspace(1)* %key) + %1 = bitcast %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE"* %view to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %1, i8 0, i64 72, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(72) %1, i8* noundef nonnull align 8 dereferenceable(72) %0, i64 72, i1 false) + %2 = getelementptr inbounds %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE", %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE"* %view, i64 0, i32 4 + %3 = load i1, i1* %2, align 8 + br i1 %3, label %if.then, label %if.end + +if.then: ; preds = %entry + %4 = addrspacecast %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE"* %view to %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* + %5 = call i8 addrspace(1)* @foo1(i8 addrspace(1)* null, %"record._ZN17std$FS$collection9EntryViewIR_ZN11std$FS$core6StringEC_ZN16encoding$FS$json9JsonValueEE" addrspace(1)* %4, i8 addrspace(1)* %value) + br label %if.end + +if.end: ; preds = %if.then, %entry + ret i1 %3 +} diff --git a/llvm/test/Transforms/CJBarrierOpt/barrierremove3.ll b/llvm/test/Transforms/CJBarrierOpt/barrierremove3.ll new file mode 100644 index 000000000..ffe627f31 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/barrierremove3.ll @@ -0,0 +1,427 @@ +; RUN: opt --cj-barrier-opt -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-opt -S < %s | FileCheck %s + +%"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIT2_hhEE" = type { i8 addrspace(1)*, i64, i64 } +%ArrayLayout.T2_hhE = type { %ArrayBase, [0 x %T2_hhE] } +%T2_hhE = type { i8, i8 } +%ArrayBase = type { %ObjLayout.Object, i64 } +%ObjLayout.Object = type { %KlassInfo.0* } +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +%ArrayLayout.UInt8 = type { %ArrayBase, [0 x i8] } +%T2_clE = type { i32, i64 } +%"record._ZN11std$FS$core6StringE" = type { i8 addrspace(1)*, i64 } +%KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [1 x i8*] } +%"ObjLayout._ZN25std$FS$unittest.prop_test22__Auto__Environment_94E" = type { %"ObjLayout._ZN11std$FS$core6ObjectE", i8 } +%"ObjLayout._ZN11std$FS$core6ObjectE" = type { %ObjLayout.Object } + +@"$const_string.24" = private unnamed_addr constant [4 x i8] c"sub\00", align 1 +@"$const_string.25" = private unnamed_addr constant [4 x i8] c"add\00", align 1 +@UInt8.arrayKlass = weak_odr global %KlassInfo.0 { i32 2, i32 1, %BitMap* null, i8* bitcast (%KlassInfo.0* @array_uint8.primKlass to i8*), i8** null, i64* null, i64* null, i64* bitcast ([6 x i8]* @A1_hE.className to i64*), i32 0, [0 x i8*] zeroinitializer } +@A1_cE.className = weak_odr global [6 x i8] c"A1_cE\00", align 1 +@A1_hE.className = weak_odr global [6 x i8] c"A1_hE\00", align 1 +@"_ZN11std$FS$core17OverflowExceptionE.objKlass" = external global %KlassInfo.0 +@"_ZN11std$FS$core24IllegalArgumentExceptionE.objKlass" = external global %KlassInfo.0 +@"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" = external global %KlassInfo.0 +@"_ZN11std$FS$core6String5emptyE" = external global %"record._ZN11std$FS$core6StringE" +@"_ZN23std$$unittest.prop_test22$__STRING_LITERAL__$21E" = internal global %"record._ZN11std$FS$core6StringE" zeroinitializer +@"_ZN25std$FS$unittest.prop_test17firstToSecondByteE" = internal global %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIT2_hhEE" zeroinitializer +@"_ZN25std$FS$unittest.prop_test22__Auto__Environment_94E.objKlass" = internal global %KlassInfo.0 { i32 36, i32 24, %BitMap* inttoptr (i64 -9223372036854775806 to %BitMap*), i8* bitcast (%KlassInfo.1* @"_ZN11std$FS$core6ObjectE.objKlass" to i8*), i8** null, i64* null, i64* null, i64* null, i32 0, [0 x i8*] zeroinitializer } +@"_ZN11std$FS$core6ObjectE.objKlass" = external global %KlassInfo.1 +@array_uint8.primKlass = weak_odr global %KlassInfo.0 { i32 1, i32 1, %BitMap* null, i8* null, i8** null, i64* null, i64* null, i64* @array_uint8.uniqueAddr, i32 0, [0 x i8*] zeroinitializer } +@array_uint8.uniqueAddr = weak_odr global i64 0 + +declare i32 @personality_function() +declare i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %0) local_unnamed_addr #0 +declare void @CJ_MCC_EndCatch() local_unnamed_addr #0 +declare i8* @CJ_MCC_GetExceptionWrapper() local_unnamed_addr #0 +declare i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* %0) local_unnamed_addr #0 +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %0, i8* %1) #0 +declare i8 addrspace(1)* @CJ_MCC_NewArray8Stub(i64 %0, i8* %1) +declare i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* %0, i32 %1) +declare void @CJ_MCC_ThrowException(i8 addrspace(1)* %0) local_unnamed_addr +declare void @CJ_MCC_ThrowStackOverflowError() +declare cangjiegccc void @MCC_SafepointStub() local_unnamed_addr +declare void @"_ZN11std$FS$core24IllegalArgumentException4initER_ZN11std$FS$core6StringE"(i8 addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) +declare void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)*) +declare void @"_ZN11std$FS$core6Extend4Char8fromUtf8ER_ZN11std$FS$core5ArrayIhEl"(%T2_clE*, i8 addrspace(1)*, %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE" addrspace(1)*, i64) +declare void @"_ZN11std$FS$core6Extend5Int648toStringEv"(%"record._ZN11std$FS$core6StringE"*, i64) +declare void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"*, i8*, i64) +declare void @"_ZN11std$FS$core6String6concatER_ZN11std$FS$core6StringE"(%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) +declare void @llvm.cj.gcwrite.i8.i8(i8 %0, i8 addrspace(1)* nocapture %1, i8 addrspace(1)* nocapture %2) +declare void @llvm.cj.gcwrite.p1i8.p1i8(i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*) +declare void @llvm.lifetime.end.p0i8(i64, i8*) +declare void @llvm.lifetime.start.p0i8(i64, i8*) +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1) +declare { i8, i1 } @llvm.uadd.with.overflow.i8(i8, i8) +declare { i8, i1 } @llvm.usub.with.overflow.i8(i8, i8) +declare i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) + +; CHECK: call void @llvm.cj.gcwrite.p1i8.p1i8(i8 addrspace(1)* %this, i8 addrspace(1)* %50, i8 addrspace(1)* addrspace(1)* %52) +; CHECK: call void @llvm.cj.gcwrite.i8.i8(i8 %switchValue.0, i8 addrspace(1)* %50, i8 addrspace(1)* %53) +; CHECK: call void @llvm.cj.gcwrite.i8.i8(i8 %switchValue.0.i, i8 addrspace(1)* %49, i8 addrspace(1)* %arr.idx.set.gep.i) + +define i32 @"_ZN25std$FS$unittest.prop_test6Extend6Random8nextUtf8El"(i8 addrspace(1)* %this, i64 %byteSize) gc "cangjie" personality i32 ()* @personality_function { +entry: + %callRet13.i.i = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet13.i.i52 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet13.i.i to i8* + %callRet7.i.i = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet7.i.i51 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet7.i.i to i8* + %callRet.i = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet.i53 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet.i to i8* + %callRet63 = alloca %T2_clE, align 8 + %arr = alloca %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE", align 8 + %callRet48 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet38 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet28 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet18 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet10 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet7 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet = alloca %"record._ZN11std$FS$core6StringE", align 8 + %0 = bitcast %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE"* %arr to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %0, i8 0, i64 24, i1 false) + %1 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet48 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %1, i8 0, i64 16, i1 false) + %2 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet38 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %2, i8 0, i64 16, i1 false) + %3 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet28 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %3, i8 0, i64 16, i1 false) + %4 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet18 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %4, i8 0, i64 16, i1 false) + %5 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet10 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %5, i8 0, i64 16, i1 false) + %6 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet7 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %6, i8 0, i64 16, i1 false) + %7 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %7, i8 0, i64 16, i1 false) + %atom.288984E.sroa.0.0..sroa_idx11 = getelementptr inbounds %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE", %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE"* %arr, i64 0, i32 0 + %atom.288984E.sroa.4.0..sroa_idx14 = getelementptr inbounds %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE", %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE"* %arr, i64 0, i32 1 + %atom.288984E.sroa.5.0..sroa_idx17 = getelementptr inbounds %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE", %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE"* %arr, i64 0, i32 2 + %8 = addrspacecast %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE"* %arr to %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE" addrspace(1)* + %9 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %10 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet7 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %11 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet10 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + call void @CJ_MCC_ThrowStackOverflowError() + call cangjiegccc void @MCC_SafepointStub() + br label %block.body3 + +block.body3: ; preds = %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp, %entry + switch i64 %byteSize, label %default_thunk [ + i64 1, label %case_0_thunk + i64 2, label %case_1_thunk + i64 3, label %case_2_thunk + i64 4, label %case_3_thunk + ] + +default_thunk: ; preds = %block.body3 + %12 = invoke noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core24IllegalArgumentExceptionE.objKlass" to i8*), i32 56) + to label %invoke.continue unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue: ; preds = %default_thunk + invoke void @"_ZN11std$FS$core6Extend5Int648toStringEv"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet, i64 %byteSize) + to label %invoke.continue6 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue6: ; preds = %invoke.continue + invoke void @"_ZN11std$FS$core6String6concatER_ZN11std$FS$core6StringE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet7, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* addrspacecast (%"record._ZN11std$FS$core6StringE"* @"_ZN23std$$unittest.prop_test22$__STRING_LITERAL__$21E" to %"record._ZN11std$FS$core6StringE" addrspace(1)*), i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %9) + to label %invoke.continue9 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue9: ; preds = %invoke.continue6 + invoke void @"_ZN11std$FS$core6String6concatER_ZN11std$FS$core6StringE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet10, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %10, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* addrspacecast (%"record._ZN11std$FS$core6StringE"* @"_ZN11std$FS$core6String5emptyE" to %"record._ZN11std$FS$core6StringE" addrspace(1)*)) + to label %invoke.continue13 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue13: ; preds = %invoke.continue9 + invoke void @"_ZN11std$FS$core24IllegalArgumentException4initER_ZN11std$FS$core6StringE"(i8 addrspace(1)* %12, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %11) + to label %.noexc37.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +case_0_thunk: ; preds = %block.body3 + %13 = call i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* %this) + %14 = getelementptr i8, i8* %13, i64 24 + %15 = bitcast i8* %14 to i8*** + %16 = load i8**, i8*** %15, align 8 + %17 = getelementptr i8*, i8** %16, i64 13 + %18 = bitcast i8** %17 to i8 (i8 addrspace(1)*, i8)** + %19 = load i8 (i8 addrspace(1)*, i8)*, i8 (i8 addrspace(1)*, i8)** %18, align 8 + %20 = invoke i8 %19(i8 addrspace(1)* %this, i8 62) + to label %invoke.continue17 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue17: ; preds = %case_0_thunk + %21 = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 %20, i8 65) + %.fca.1.extract = extractvalue { i8, i1 } %21, 1 + br i1 %.fca.1.extract, label %overflow, label %switch_end, !prof !0 + +overflow: ; preds = %invoke.continue17 + invoke void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet18, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.25", i64 0, i64 0), i64 3) + to label %.noexc36.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +case_1_thunk: ; preds = %block.body3 + %22 = call i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* %this) + %23 = getelementptr i8, i8* %22, i64 24 + %24 = bitcast i8* %23 to i8*** + %25 = load i8**, i8*** %24, align 8 + %26 = getelementptr i8*, i8** %25, i64 13 + %27 = bitcast i8** %26 to i8 (i8 addrspace(1)*, i8)** + %28 = load i8 (i8 addrspace(1)*, i8)*, i8 (i8 addrspace(1)*, i8)** %27, align 8 + %29 = invoke i8 %28(i8 addrspace(1)* %this, i8 30) + to label %invoke.continue23 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue23: ; preds = %case_1_thunk + %30 = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 %29, i8 -62) + %.fca.1.extract3 = extractvalue { i8, i1 } %30, 1 + br i1 %.fca.1.extract3, label %overflow26, label %switch_end, !prof !0 + +overflow26: ; preds = %invoke.continue23 + invoke void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet28, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.25", i64 0, i64 0), i64 3) + to label %.noexc36.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +case_2_thunk: ; preds = %block.body3 + %31 = call i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* %this) + %32 = getelementptr i8, i8* %31, i64 24 + %33 = bitcast i8* %32 to i8*** + %34 = load i8**, i8*** %33, align 8 + %35 = getelementptr i8*, i8** %34, i64 13 + %36 = bitcast i8** %35 to i8 (i8 addrspace(1)*, i8)** + %37 = load i8 (i8 addrspace(1)*, i8)*, i8 (i8 addrspace(1)*, i8)** %36, align 8 + %38 = invoke i8 %37(i8 addrspace(1)* %this, i8 16) + to label %invoke.continue33 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue33: ; preds = %case_2_thunk + %39 = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 %38, i8 -32) + %.fca.1.extract6 = extractvalue { i8, i1 } %39, 1 + br i1 %.fca.1.extract6, label %overflow36, label %switch_end, !prof !0 + +overflow36: ; preds = %invoke.continue33 + invoke void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet38, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.25", i64 0, i64 0), i64 3) + to label %.noexc36.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +case_3_thunk: ; preds = %block.body3 + %40 = call i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* %this) + %41 = getelementptr i8, i8* %40, i64 24 + %42 = bitcast i8* %41 to i8*** + %43 = load i8**, i8*** %42, align 8 + %44 = getelementptr i8*, i8** %43, i64 13 + %45 = bitcast i8** %44 to i8 (i8 addrspace(1)*, i8)** + %46 = load i8 (i8 addrspace(1)*, i8)*, i8 (i8 addrspace(1)*, i8)** %45, align 8 + %47 = invoke i8 %46(i8 addrspace(1)* %this, i8 4) + to label %invoke.continue43 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue43: ; preds = %case_3_thunk + %48 = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 %47, i8 -16) + %.fca.1.extract9 = extractvalue { i8, i1 } %48, 1 + br i1 %.fca.1.extract9, label %overflow46, label %switch_end, !prof !0 + +overflow46: ; preds = %invoke.continue43 + invoke void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet48, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.25", i64 0, i64 0), i64 3) + to label %.noexc36.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +switch_end: ; preds = %invoke.continue43, %invoke.continue33, %invoke.continue23, %invoke.continue17 + %.pn = phi { i8, i1 } [ %21, %invoke.continue17 ], [ %30, %invoke.continue23 ], [ %39, %invoke.continue33 ], [ %48, %invoke.continue43 ] + %switchValue.0 = extractvalue { i8, i1 } %.pn, 0 + %49 = invoke noalias i8 addrspace(1)* @CJ_MCC_NewArray8Stub(i64 %byteSize, i8* bitcast (%KlassInfo.0* @UInt8.arrayKlass to i8*)) + to label %invoke.continue57 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue57: ; preds = %switch_end, %invoke.continue57 + %a = phi i8 [ %switchValue.0, %switch_end ], [ %a.i, %invoke.continue57 ] + %50 = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN25std$FS$unittest.prop_test22__Auto__Environment_94E.objKlass" to i8*), i32 24) + %a.i = add nuw nsw i8 %a, 1 + %icmpeq.a = icmp eq i8 %a.i, 50 + br i1 %icmpeq.a, label %invoke.continue57, label %invoke.continue59 + +invoke.continue59: ; preds = %invoke.continue57 + %51 = getelementptr inbounds i8, i8 addrspace(1)* %50, i64 8 + %52 = bitcast i8 addrspace(1)* %51 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.p1i8.p1i8(i8 addrspace(1)* %this, i8 addrspace(1)* %50, i8 addrspace(1)* addrspace(1)* %52) + %53 = getelementptr inbounds i8, i8 addrspace(1)* %50, i64 16 + call void @llvm.cj.gcwrite.i8.i8(i8 %switchValue.0, i8 addrspace(1)* %50, i8 addrspace(1)* %53) + %54 = getelementptr inbounds i8, i8 addrspace(1)* %49, i64 8 + %55 = bitcast i8 addrspace(1)* %54 to i64 addrspace(1)* + %56 = load i64, i64 addrspace(1)* %55, align 8 + %icmpslt4.i = icmp sgt i64 %56, 0 + br i1 %icmpslt4.i, label %while.body.lr.ph.i, label %invoke.continue61 + +while.body.lr.ph.i: ; preds = %invoke.continue59 + %57 = bitcast i8 addrspace(1)* %49 to %ArrayLayout.UInt8 addrspace(1)* + %58 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %52, align 8 + %59 = load i8, i8 addrspace(1)* %53, align 1 + %60 = zext i8 %59 to i64 + br label %while.body.i + +while.body.i: ; preds = %arr.if.else.i, %while.body.lr.ph.i + %atom.284140E.05.i = phi i64 [ 0, %while.body.lr.ph.i ], [ %add.i, %arr.if.else.i ] + call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %callRet.i53) + switch i64 %atom.284140E.05.i, label %default_thunk.i [ + i64 0, label %"std/unittest.prop_test$lambda.23.exit" + i64 1, label %case_1_thunk.i + ] + +default_thunk.i: ; preds = %while.body.i + %61 = invoke i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* %58) + to label %.noexc23 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc23: ; preds = %default_thunk.i + %62 = getelementptr i8, i8* %61, i64 24 + %63 = bitcast i8* %62 to i8*** + %64 = load i8**, i8*** %63, align 8 + %65 = getelementptr i8*, i8** %64, i64 13 + %66 = bitcast i8** %65 to i8 (i8 addrspace(1)*, i8)** + %67 = load i8 (i8 addrspace(1)*, i8)*, i8 (i8 addrspace(1)*, i8)** %66, align 8 + %68 = invoke i8 %67(i8 addrspace(1)* %58, i8 64) + to label %.noexc24 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc24: ; preds = %.noexc23 + %69 = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 %68, i8 -128) + %.fca.1.extract.i = extractvalue { i8, i1 } %69, 1 + br i1 %.fca.1.extract.i, label %overflow.i, label %normal.i, !prof !0 + +normal.i: ; preds = %.noexc24 + %.fca.0.extract.i = extractvalue { i8, i1 } %69, 0 + br label %"std/unittest.prop_test$lambda.23.exit" + +overflow.i: ; preds = %.noexc24 + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %callRet.i53, i8 0, i64 16, i1 false) + invoke void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet.i, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.25", i64 0, i64 0), i64 3) + to label %.noexc36.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +case_1_thunk.i: ; preds = %while.body.i + call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %callRet13.i.i52) + call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %callRet7.i.i51) + %70 = load i64, i64* getelementptr inbounds (%"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIT2_hhEE", %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIT2_hhEE"* @"_ZN25std$FS$unittest.prop_test17firstToSecondByteE", i64 0, i32 2), align 16 + %icmpuge.not.i.i = icmp ugt i64 %70, %60 + br i1 %icmpuge.not.i.i, label %if.else.i.i, label %if.then.i.i + +if.then.i.i: ; preds = %case_1_thunk.i + %71 = invoke noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 56) + to label %.noexc28 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc28: ; preds = %if.then.i.i + invoke void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %71) + to label %.noexc37.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +if.else.i.i: ; preds = %case_1_thunk.i + %72 = load %ArrayLayout.T2_hhE addrspace(1)*, %ArrayLayout.T2_hhE addrspace(1)** bitcast (%"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIT2_hhEE"* @"_ZN25std$FS$unittest.prop_test17firstToSecondByteE" to %ArrayLayout.T2_hhE addrspace(1)**), align 16 + %73 = load i64, i64* getelementptr inbounds (%"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIT2_hhEE", %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIT2_hhEE"* @"_ZN25std$FS$unittest.prop_test17firstToSecondByteE", i64 0, i32 1), align 8 + %add.i.i = add i64 %73, %60 + %arr.get.sroa.0.0..sroa_idx.i.i = getelementptr inbounds %ArrayLayout.T2_hhE, %ArrayLayout.T2_hhE addrspace(1)* %72, i64 0, i32 1, i64 %add.i.i, i32 0 + %arr.get.sroa.0.0.copyload.i.i = load i8, i8 addrspace(1)* %arr.get.sroa.0.0..sroa_idx.i.i, align 1 + %arr.get.sroa.2.0..sroa_idx.i.i = getelementptr inbounds %ArrayLayout.T2_hhE, %ArrayLayout.T2_hhE addrspace(1)* %72, i64 0, i32 1, i64 %add.i.i, i32 1 + %arr.get.sroa.2.0.copyload.i.i = load i8, i8 addrspace(1)* %arr.get.sroa.2.0..sroa_idx.i.i, align 1 + %74 = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 %arr.get.sroa.2.0.copyload.i.i, i8 %arr.get.sroa.0.0.copyload.i.i) + %.fca.1.extract.i.i = extractvalue { i8, i1 } %74, 1 + br i1 %.fca.1.extract.i.i, label %overflow.i.i, label %normal.i.i, !prof !0 + +normal.i.i: ; preds = %if.else.i.i + %75 = invoke i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* %58) + to label %.noexc31 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc31: ; preds = %normal.i.i + %.fca.0.extract.i.i = extractvalue { i8, i1 } %74, 0 + %76 = getelementptr i8, i8* %75, i64 24 + %77 = bitcast i8* %76 to i8*** + %78 = load i8**, i8*** %77, align 8 + %79 = getelementptr i8*, i8** %78, i64 13 + %80 = bitcast i8** %79 to i8 (i8 addrspace(1)*, i8)** + %81 = load i8 (i8 addrspace(1)*, i8)*, i8 (i8 addrspace(1)*, i8)** %80, align 8 + %82 = invoke i8 %81(i8 addrspace(1)* %58, i8 %.fca.0.extract.i.i) + to label %.noexc32 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc32: ; preds = %.noexc31 + %83 = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 %82, i8 %arr.get.sroa.0.0.copyload.i.i) + %.fca.1.extract5.i.i = extractvalue { i8, i1 } %83, 1 + br i1 %.fca.1.extract5.i.i, label %overflow11.i.i, label %"_ZN25std$FS$unittest.prop_test6Extend6Random18nextUtf8SecondByteEh.exit.i", !prof !0 + +overflow.i.i: ; preds = %if.else.i.i + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %callRet7.i.i51, i8 0, i64 16, i1 false) + invoke void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet7.i.i, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.24", i64 0, i64 0), i64 3) + to label %.noexc36.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +overflow11.i.i: ; preds = %.noexc32 + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %callRet13.i.i52, i8 0, i64 16, i1 false) + invoke void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet13.i.i, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.25", i64 0, i64 0), i64 3) + to label %.noexc36.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc36.invoke: ; preds = %overflow, %overflow26, %overflow36, %overflow46, %overflow11.i.i, %overflow.i.i, %overflow.i + %.in55 = phi %"record._ZN11std$FS$core6StringE"* [ %callRet.i, %overflow.i ], [ %callRet7.i.i, %overflow.i.i ], [ %callRet13.i.i, %overflow11.i.i ], [ %callRet18, %overflow ], [ %callRet28, %overflow26 ], [ %callRet38, %overflow36 ], [ %callRet48, %overflow46 ] + %84 = addrspacecast %"record._ZN11std$FS$core6StringE"* %.in55 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %85 = invoke i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %84) + to label %.noexc37.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc37.invoke: ; preds = %.noexc36.invoke, %invoke.continue13, %.noexc19, %.noexc28 + %86 = phi i8 addrspace(1)* [ %71, %.noexc28 ], [ %12, %invoke.continue13 ], [ %88, %.noexc19 ], [ %85, %.noexc36.invoke ] + invoke void @CJ_MCC_ThrowException(i8 addrspace(1)* %86) + to label %.noexc37.cont unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc37.cont: ; preds = %.noexc37.invoke + unreachable + +"_ZN25std$FS$unittest.prop_test6Extend6Random18nextUtf8SecondByteEh.exit.i": ; preds = %.noexc32 + %.fca.0.extract4.i.i = extractvalue { i8, i1 } %83, 0 + call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %callRet13.i.i52) + call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %callRet7.i.i51) + br label %"std/unittest.prop_test$lambda.23.exit" + +"std/unittest.prop_test$lambda.23.exit": ; preds = %while.body.i, %normal.i, %"_ZN25std$FS$unittest.prop_test6Extend6Random18nextUtf8SecondByteEh.exit.i" + %switchValue.0.i = phi i8 [ %.fca.0.extract.i, %normal.i ], [ %.fca.0.extract4.i.i, %"_ZN25std$FS$unittest.prop_test6Extend6Random18nextUtf8SecondByteEh.exit.i" ], [ %59, %while.body.i ] + call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %callRet.i53) + %87 = load i64, i64 addrspace(1)* %55, align 8 + %.not.i = icmp slt i64 %atom.284140E.05.i, %87 + br i1 %.not.i, label %arr.if.else.i, label %arr.if.then.i + +arr.if.then.i: ; preds = %"std/unittest.prop_test$lambda.23.exit" + %88 = invoke noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 56) + to label %.noexc19 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc19: ; preds = %arr.if.then.i + invoke void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %88) + to label %.noexc37.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +arr.if.else.i: ; preds = %"std/unittest.prop_test$lambda.23.exit" + %arr.idx.set.gep.i = getelementptr inbounds %ArrayLayout.UInt8, %ArrayLayout.UInt8 addrspace(1)* %57, i64 0, i32 1, i64 %atom.284140E.05.i + call void @llvm.cj.gcwrite.i8.i8(i8 %switchValue.0.i, i8 addrspace(1)* %49, i8 addrspace(1)* %arr.idx.set.gep.i) + %add.i = add nuw nsw i64 %atom.284140E.05.i, 1 + %89 = load i64, i64 addrspace(1)* %55, align 8 + %icmpslt.i = icmp slt i64 %add.i, %89 + call cangjiegccc void @MCC_SafepointStub() + br i1 %icmpslt.i, label %while.body.i, label %invoke.continue61 + +invoke.continue61: ; preds = %arr.if.else.i, %invoke.continue59 + store i8 addrspace(1)* %49, i8 addrspace(1)** %atom.288984E.sroa.0.0..sroa_idx11, align 8 + store i64 0, i64* %atom.288984E.sroa.4.0..sroa_idx14, align 8 + store i64 %byteSize, i64* %atom.288984E.sroa.5.0..sroa_idx17, align 8 + invoke void @"_ZN11std$FS$core6Extend4Char8fromUtf8ER_ZN11std$FS$core5ArrayIhEl"(%T2_clE* noalias nonnull sret(%T2_clE) %callRet63, i8 addrspace(1)* null, %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE" addrspace(1)* %8, i64 0) + to label %invoke.continue65 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue65: ; preds = %invoke.continue61 + %90 = getelementptr inbounds %T2_clE, %T2_clE* %callRet63, i64 0, i32 0 + %91 = load i32, i32* %90, align 8 + ret i32 %91 + +landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp: ; preds = %.noexc31, %normal.i.i, %.noexc23, %default_thunk.i, %.noexc36.invoke, %.noexc37.invoke, %overflow11.i.i, %overflow.i.i, %.noexc28, %if.then.i.i, %overflow.i, %.noexc19, %arr.if.then.i, %invoke.continue61, %switch_end, %overflow46, %case_3_thunk, %overflow36, %case_2_thunk, %overflow26, %case_1_thunk, %overflow, %case_0_thunk, %invoke.continue13, %invoke.continue9, %invoke.continue6, %invoke.continue, %default_thunk + %lpad.loopexit.split-lp56 = landingpad token + catch i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core17OverflowExceptionE.objKlass" to i8*) + %92 = call i8* @CJ_MCC_GetExceptionWrapper() + %93 = call i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %92) + %94 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %93, i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core17OverflowExceptionE.objKlass" to i8*)) + call cangjiegccc void @MCC_SafepointStub() + br i1 %94, label %block.body3, label %if.else + +if.else: ; preds = %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + call void @CJ_MCC_EndCatch() + invoke void @CJ_MCC_ThrowException(i8 addrspace(1)* %93) + to label %invoke.continue71 unwind label %finally.rethrow + +invoke.continue71: ; preds = %if.else + unreachable + +finally.rethrow: ; preds = %if.else + %95 = landingpad token + catch i8* null + %96 = call i8* @CJ_MCC_GetExceptionWrapper() + %97 = call i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %96) + call void @CJ_MCC_EndCatch() + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %97) + unreachable +} + +attributes #0 = { "cj-runtime" "gc-leaf-function" } + +!0 = !{!"branch_weights", i32 1, i32 2000} diff --git a/llvm/test/Transforms/CJBarrierOpt/barrierremove6.ll b/llvm/test/Transforms/CJBarrierOpt/barrierremove6.ll new file mode 100644 index 000000000..a1af4fb00 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/barrierremove6.ll @@ -0,0 +1,432 @@ +; RUN: opt -enable-new-pm=false --cj-barrier-opt -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-opt -S < %s | FileCheck %s + +%"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIT2_hhEE" = type { i8 addrspace(1)*, i64, i64 } +%ArrayLayout.T2_hhE = type { %ArrayBase, [0 x %T2_hhE] } +%T2_hhE = type { i8, i8 } +%ArrayBase = type { %ObjLayout.Object, i64 } +%ObjLayout.Object = type { %KlassInfo.0* } +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +%ArrayLayout.UInt8 = type { %ArrayBase, [0 x i8] } +%T2_clE = type { i32, i64 } +%"record._ZN11std$FS$core6StringE" = type { i8 addrspace(1)*, i64 } +%KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [1 x i8*] } +%"ObjLayout._ZN25std$FS$unittest.prop_test22__Auto__Environment_94E" = type { %"ObjLayout._ZN11std$FS$core6ObjectE", i8 } +%"ObjLayout._ZN11std$FS$core6ObjectE" = type { %ObjLayout.Object } + +@"$const_string.24" = private unnamed_addr constant [4 x i8] c"sub\00", align 1 +@"$const_string.25" = private unnamed_addr constant [4 x i8] c"add\00", align 1 +@UInt8.arrayKlass = weak_odr global %KlassInfo.0 { i32 2, i32 1, %BitMap* null, i8* bitcast (%KlassInfo.0* @array_uint8.primKlass to i8*), i8** null, i64* null, i64* null, i64* bitcast ([6 x i8]* @A1_hE.className to i64*), i32 0, [0 x i8*] zeroinitializer } +@A1_cE.className = weak_odr global [6 x i8] c"A1_cE\00", align 1 +@A1_hE.className = weak_odr global [6 x i8] c"A1_hE\00", align 1 +@"_ZN11std$FS$core17OverflowExceptionE.objKlass" = external global %KlassInfo.0 +@"_ZN11std$FS$core24IllegalArgumentExceptionE.objKlass" = external global %KlassInfo.0 +@"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" = external global %KlassInfo.0 +@"_ZN11std$FS$core6String5emptyE" = external global %"record._ZN11std$FS$core6StringE" +@"_ZN23std$$unittest.prop_test22$__STRING_LITERAL__$21E" = internal global %"record._ZN11std$FS$core6StringE" zeroinitializer +@"_ZN25std$FS$unittest.prop_test17firstToSecondByteE" = internal global %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIT2_hhEE" zeroinitializer +@"_ZN25std$FS$unittest.prop_test22__Auto__Environment_94E.objKlass" = internal global %KlassInfo.0 { i32 36, i32 24, %BitMap* inttoptr (i64 -9223372036854775806 to %BitMap*), i8* bitcast (%KlassInfo.1* @"_ZN11std$FS$core6ObjectE.objKlass" to i8*), i8** null, i64* null, i64* null, i64* null, i32 0, [0 x i8*] zeroinitializer } +@"_ZN11std$FS$core6ObjectE.objKlass" = external global %KlassInfo.1 +@array_uint8.primKlass = weak_odr global %KlassInfo.0 { i32 1, i32 1, %BitMap* null, i8* null, i8** null, i64* null, i64* null, i64* @array_uint8.uniqueAddr, i32 0, [0 x i8*] zeroinitializer } +@array_uint8.uniqueAddr = weak_odr global i64 0 + +declare i32 @personality_function() +declare i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %0) local_unnamed_addr #0 +declare void @CJ_MCC_EndCatch() local_unnamed_addr #0 +declare i8* @CJ_MCC_GetExceptionWrapper() local_unnamed_addr #0 +declare i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* %0) local_unnamed_addr #0 +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %0, i8* %1) #0 +declare i8 addrspace(1)* @CJ_MCC_NewArray8Stub(i64 %0, i8* %1) +declare i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* %0, i32 %1) +declare void @CJ_MCC_ThrowException(i8 addrspace(1)* %0) local_unnamed_addr +declare void @CJ_MCC_ThrowStackOverflowError() +declare cangjiegccc void @MCC_SafepointStub() local_unnamed_addr +declare void @"_ZN11std$FS$core24IllegalArgumentException4initER_ZN11std$FS$core6StringE"(i8 addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) +declare void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)*) +declare void @"_ZN11std$FS$core6Extend4Char8fromUtf8ER_ZN11std$FS$core5ArrayIhEl"(%T2_clE*, i8 addrspace(1)*, %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE" addrspace(1)*, i64) +declare void @"_ZN11std$FS$core6Extend5Int648toStringEv"(%"record._ZN11std$FS$core6StringE"*, i64) +declare void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"*, i8*, i64) +declare void @"_ZN11std$FS$core6String6concatER_ZN11std$FS$core6StringE"(%"record._ZN11std$FS$core6StringE"*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) +declare void @llvm.cj.gcwrite.i8.i8(i8 %0, i8 addrspace(1)* nocapture %1, i8 addrspace(1)* nocapture %2) +declare void @llvm.cj.gcwrite.p1i8.p1i8(i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*) +declare void @llvm.lifetime.end.p0i8(i64, i8*) +declare void @llvm.lifetime.start.p0i8(i64, i8*) +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1) +declare { i8, i1 } @llvm.uadd.with.overflow.i8(i8, i8) +declare { i8, i1 } @llvm.usub.with.overflow.i8(i8, i8) +declare i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) + +; CHECK: call void @llvm.cj.gcwrite.p1i8.p1i8(i8 addrspace(1)* %this, i8 addrspace(1)* %50, i8 addrspace(1)* addrspace(1)* %52) +; CHECK: call void @llvm.cj.gcwrite.i8.i8(i8 %switchValue.0, i8 addrspace(1)* %50, i8 addrspace(1)* %53) +; CHECK: call void @llvm.cj.gcwrite.i8.i8(i8 %switchValue.0.i, i8 addrspace(1)* %49, i8 addrspace(1)* %arr.idx.set.gep.i) + +define i32 @"_ZN25std$FS$unittest.prop_test6Extend6Random8nextUtf8El"(i8 addrspace(1)* %this, i64 %byteSize) gc "cangjie" personality i32 ()* @personality_function { +entry: + %callRet13.i.i = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet13.i.i52 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet13.i.i to i8* + %callRet7.i.i = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet7.i.i51 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet7.i.i to i8* + %callRet.i = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet.i53 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet.i to i8* + %callRet63 = alloca %T2_clE, align 8 + %arr = alloca %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE", align 8 + %callRet48 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet38 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet28 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet18 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet10 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet7 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet = alloca %"record._ZN11std$FS$core6StringE", align 8 + %0 = bitcast %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE"* %arr to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %0, i8 0, i64 24, i1 false) + %1 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet48 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %1, i8 0, i64 16, i1 false) + %2 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet38 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %2, i8 0, i64 16, i1 false) + %3 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet28 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %3, i8 0, i64 16, i1 false) + %4 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet18 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %4, i8 0, i64 16, i1 false) + %5 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet10 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %5, i8 0, i64 16, i1 false) + %6 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet7 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %6, i8 0, i64 16, i1 false) + %7 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %7, i8 0, i64 16, i1 false) + %atom.288984E.sroa.0.0..sroa_idx11 = getelementptr inbounds %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE", %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE"* %arr, i64 0, i32 0 + %atom.288984E.sroa.4.0..sroa_idx14 = getelementptr inbounds %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE", %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE"* %arr, i64 0, i32 1 + %atom.288984E.sroa.5.0..sroa_idx17 = getelementptr inbounds %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE", %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE"* %arr, i64 0, i32 2 + %8 = addrspacecast %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE"* %arr to %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE" addrspace(1)* + %9 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %10 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet7 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %11 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet10 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + call void @CJ_MCC_ThrowStackOverflowError() + call cangjiegccc void @MCC_SafepointStub() + br label %block.body3 + +block.body3: ; preds = %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp, %entry + switch i64 %byteSize, label %default_thunk [ + i64 1, label %case_0_thunk + i64 2, label %case_1_thunk + i64 3, label %case_2_thunk + i64 4, label %case_3_thunk + ] + +default_thunk: ; preds = %block.body3 + %12 = invoke noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core24IllegalArgumentExceptionE.objKlass" to i8*), i32 56) + to label %invoke.continue unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue: ; preds = %default_thunk + invoke void @"_ZN11std$FS$core6Extend5Int648toStringEv"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet, i64 %byteSize) + to label %invoke.continue6 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue6: ; preds = %invoke.continue + invoke void @"_ZN11std$FS$core6String6concatER_ZN11std$FS$core6StringE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet7, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* addrspacecast (%"record._ZN11std$FS$core6StringE"* @"_ZN23std$$unittest.prop_test22$__STRING_LITERAL__$21E" to %"record._ZN11std$FS$core6StringE" addrspace(1)*), i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %9) + to label %invoke.continue9 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue9: ; preds = %invoke.continue6 + invoke void @"_ZN11std$FS$core6String6concatER_ZN11std$FS$core6StringE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet10, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %10, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* addrspacecast (%"record._ZN11std$FS$core6StringE"* @"_ZN11std$FS$core6String5emptyE" to %"record._ZN11std$FS$core6StringE" addrspace(1)*)) + to label %invoke.continue13 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue13: ; preds = %invoke.continue9 + invoke void @"_ZN11std$FS$core24IllegalArgumentException4initER_ZN11std$FS$core6StringE"(i8 addrspace(1)* %12, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %11) + to label %.noexc37.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +case_0_thunk: ; preds = %block.body3 + %13 = call i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* %this) + %14 = getelementptr i8, i8* %13, i64 24 + %15 = bitcast i8* %14 to i8*** + %16 = load i8**, i8*** %15, align 8 + %17 = getelementptr i8*, i8** %16, i64 13 + %18 = bitcast i8** %17 to i8 (i8 addrspace(1)*, i8)** + %19 = load i8 (i8 addrspace(1)*, i8)*, i8 (i8 addrspace(1)*, i8)** %18, align 8 + %20 = invoke i8 %19(i8 addrspace(1)* %this, i8 62) + to label %invoke.continue17 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue17: ; preds = %case_0_thunk + %21 = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 %20, i8 65) + %.fca.1.extract = extractvalue { i8, i1 } %21, 1 + br i1 %.fca.1.extract, label %overflow, label %switch_end, !prof !0 + +overflow: ; preds = %invoke.continue17 + invoke void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet18, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.25", i64 0, i64 0), i64 3) + to label %.noexc36.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +case_1_thunk: ; preds = %block.body3 + %22 = call i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* %this) + %23 = getelementptr i8, i8* %22, i64 24 + %24 = bitcast i8* %23 to i8*** + %25 = load i8**, i8*** %24, align 8 + %26 = getelementptr i8*, i8** %25, i64 13 + %27 = bitcast i8** %26 to i8 (i8 addrspace(1)*, i8)** + %28 = load i8 (i8 addrspace(1)*, i8)*, i8 (i8 addrspace(1)*, i8)** %27, align 8 + %29 = invoke i8 %28(i8 addrspace(1)* %this, i8 30) + to label %invoke.continue23 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue23: ; preds = %case_1_thunk + %30 = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 %29, i8 -62) + %.fca.1.extract3 = extractvalue { i8, i1 } %30, 1 + br i1 %.fca.1.extract3, label %overflow26, label %switch_end, !prof !0 + +overflow26: ; preds = %invoke.continue23 + invoke void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet28, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.25", i64 0, i64 0), i64 3) + to label %.noexc36.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +case_2_thunk: ; preds = %block.body3 + %31 = call i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* %this) + %32 = getelementptr i8, i8* %31, i64 24 + %33 = bitcast i8* %32 to i8*** + %34 = load i8**, i8*** %33, align 8 + %35 = getelementptr i8*, i8** %34, i64 13 + %36 = bitcast i8** %35 to i8 (i8 addrspace(1)*, i8)** + %37 = load i8 (i8 addrspace(1)*, i8)*, i8 (i8 addrspace(1)*, i8)** %36, align 8 + %38 = invoke i8 %37(i8 addrspace(1)* %this, i8 16) + to label %invoke.continue33 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue33: ; preds = %case_2_thunk + %39 = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 %38, i8 -32) + %.fca.1.extract6 = extractvalue { i8, i1 } %39, 1 + br i1 %.fca.1.extract6, label %overflow36, label %switch_end, !prof !0 + +overflow36: ; preds = %invoke.continue33 + invoke void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet38, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.25", i64 0, i64 0), i64 3) + to label %.noexc36.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +case_3_thunk: ; preds = %block.body3 + %40 = call i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* %this) + %41 = getelementptr i8, i8* %40, i64 24 + %42 = bitcast i8* %41 to i8*** + %43 = load i8**, i8*** %42, align 8 + %44 = getelementptr i8*, i8** %43, i64 13 + %45 = bitcast i8** %44 to i8 (i8 addrspace(1)*, i8)** + %46 = load i8 (i8 addrspace(1)*, i8)*, i8 (i8 addrspace(1)*, i8)** %45, align 8 + %47 = invoke i8 %46(i8 addrspace(1)* %this, i8 4) + to label %invoke.continue43 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue43: ; preds = %case_3_thunk + %48 = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 %47, i8 -16) + %.fca.1.extract9 = extractvalue { i8, i1 } %48, 1 + br i1 %.fca.1.extract9, label %overflow46, label %switch_end, !prof !0 + +overflow46: ; preds = %invoke.continue43 + invoke void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet48, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.25", i64 0, i64 0), i64 3) + to label %.noexc36.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +switch_end: ; preds = %invoke.continue43, %invoke.continue33, %invoke.continue23, %invoke.continue17 + %.pn = phi { i8, i1 } [ %21, %invoke.continue17 ], [ %30, %invoke.continue23 ], [ %39, %invoke.continue33 ], [ %48, %invoke.continue43 ] + %switchValue.0 = extractvalue { i8, i1 } %.pn, 0 + %b0 = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN25std$FS$unittest.prop_test22__Auto__Environment_94E.objKlass" to i8*), i32 24) + %49 = invoke noalias i8 addrspace(1)* @CJ_MCC_NewArray8Stub(i64 %byteSize, i8* bitcast (%KlassInfo.0* @UInt8.arrayKlass to i8*)) + to label %invoke.continue57 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue57: ; preds = %switch_end, %invoke.continue57 + %a = phi i8 [ %switchValue.0, %switch_end ], [ %a.i, %invoke.continue57 ] + %b = phi i8 addrspace(1)* [ %b0, %switch_end ], [ %b1, %invoke.continue57 ] + %50 = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN25std$FS$unittest.prop_test22__Auto__Environment_94E.objKlass" to i8*), i32 24) + %b.0 = getelementptr inbounds i8, i8 addrspace(1)* %b, i64 16 + call void @llvm.cj.gcwrite.i8.i8(i8 %switchValue.0, i8 addrspace(1)* %b, i8 addrspace(1)* %b.0) + %a.i = add nuw nsw i8 %a, 1 + %b1 = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN25std$FS$unittest.prop_test22__Auto__Environment_94E.objKlass" to i8*), i32 24) + %icmpeq.a = icmp eq i8 %a.i, 50 + br i1 %icmpeq.a, label %invoke.continue57, label %invoke.continue59 + +invoke.continue59: ; preds = %invoke.continue57 + %51 = getelementptr inbounds i8, i8 addrspace(1)* %50, i64 8 + %52 = bitcast i8 addrspace(1)* %51 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.p1i8.p1i8(i8 addrspace(1)* %this, i8 addrspace(1)* %50, i8 addrspace(1)* addrspace(1)* %52) + %53 = getelementptr inbounds i8, i8 addrspace(1)* %50, i64 16 + call void @llvm.cj.gcwrite.i8.i8(i8 %switchValue.0, i8 addrspace(1)* %50, i8 addrspace(1)* %53) + %54 = getelementptr inbounds i8, i8 addrspace(1)* %49, i64 8 + %55 = bitcast i8 addrspace(1)* %54 to i64 addrspace(1)* + %56 = load i64, i64 addrspace(1)* %55, align 8 + %icmpslt4.i = icmp sgt i64 %56, 0 + br i1 %icmpslt4.i, label %while.body.lr.ph.i, label %invoke.continue61 + +while.body.lr.ph.i: ; preds = %invoke.continue59 + %57 = bitcast i8 addrspace(1)* %49 to %ArrayLayout.UInt8 addrspace(1)* + %58 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %52, align 8 + %59 = load i8, i8 addrspace(1)* %53, align 1 + %60 = zext i8 %59 to i64 + br label %while.body.i + +while.body.i: ; preds = %arr.if.else.i, %while.body.lr.ph.i + %atom.284140E.05.i = phi i64 [ 0, %while.body.lr.ph.i ], [ %add.i, %arr.if.else.i ] + call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %callRet.i53) + switch i64 %atom.284140E.05.i, label %default_thunk.i [ + i64 0, label %"std/unittest.prop_test$lambda.23.exit" + i64 1, label %case_1_thunk.i + ] + +default_thunk.i: ; preds = %while.body.i + %61 = invoke i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* %58) + to label %.noexc23 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc23: ; preds = %default_thunk.i + %62 = getelementptr i8, i8* %61, i64 24 + %63 = bitcast i8* %62 to i8*** + %64 = load i8**, i8*** %63, align 8 + %65 = getelementptr i8*, i8** %64, i64 13 + %66 = bitcast i8** %65 to i8 (i8 addrspace(1)*, i8)** + %67 = load i8 (i8 addrspace(1)*, i8)*, i8 (i8 addrspace(1)*, i8)** %66, align 8 + %68 = invoke i8 %67(i8 addrspace(1)* %58, i8 64) + to label %.noexc24 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc24: ; preds = %.noexc23 + %69 = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 %68, i8 -128) + %.fca.1.extract.i = extractvalue { i8, i1 } %69, 1 + br i1 %.fca.1.extract.i, label %overflow.i, label %normal.i, !prof !0 + +normal.i: ; preds = %.noexc24 + %.fca.0.extract.i = extractvalue { i8, i1 } %69, 0 + br label %"std/unittest.prop_test$lambda.23.exit" + +overflow.i: ; preds = %.noexc24 + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %callRet.i53, i8 0, i64 16, i1 false) + invoke void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet.i, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.25", i64 0, i64 0), i64 3) + to label %.noexc36.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +case_1_thunk.i: ; preds = %while.body.i + call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %callRet13.i.i52) + call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %callRet7.i.i51) + %70 = load i64, i64* getelementptr inbounds (%"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIT2_hhEE", %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIT2_hhEE"* @"_ZN25std$FS$unittest.prop_test17firstToSecondByteE", i64 0, i32 2), align 16 + %icmpuge.not.i.i = icmp ugt i64 %70, %60 + br i1 %icmpuge.not.i.i, label %if.else.i.i, label %if.then.i.i + +if.then.i.i: ; preds = %case_1_thunk.i + %71 = invoke noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 56) + to label %.noexc28 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc28: ; preds = %if.then.i.i + invoke void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %71) + to label %.noexc37.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +if.else.i.i: ; preds = %case_1_thunk.i + %72 = load %ArrayLayout.T2_hhE addrspace(1)*, %ArrayLayout.T2_hhE addrspace(1)** bitcast (%"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIT2_hhEE"* @"_ZN25std$FS$unittest.prop_test17firstToSecondByteE" to %ArrayLayout.T2_hhE addrspace(1)**), align 16 + %73 = load i64, i64* getelementptr inbounds (%"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIT2_hhEE", %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIT2_hhEE"* @"_ZN25std$FS$unittest.prop_test17firstToSecondByteE", i64 0, i32 1), align 8 + %add.i.i = add i64 %73, %60 + %arr.get.sroa.0.0..sroa_idx.i.i = getelementptr inbounds %ArrayLayout.T2_hhE, %ArrayLayout.T2_hhE addrspace(1)* %72, i64 0, i32 1, i64 %add.i.i, i32 0 + %arr.get.sroa.0.0.copyload.i.i = load i8, i8 addrspace(1)* %arr.get.sroa.0.0..sroa_idx.i.i, align 1 + %arr.get.sroa.2.0..sroa_idx.i.i = getelementptr inbounds %ArrayLayout.T2_hhE, %ArrayLayout.T2_hhE addrspace(1)* %72, i64 0, i32 1, i64 %add.i.i, i32 1 + %arr.get.sroa.2.0.copyload.i.i = load i8, i8 addrspace(1)* %arr.get.sroa.2.0..sroa_idx.i.i, align 1 + %74 = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 %arr.get.sroa.2.0.copyload.i.i, i8 %arr.get.sroa.0.0.copyload.i.i) + %.fca.1.extract.i.i = extractvalue { i8, i1 } %74, 1 + br i1 %.fca.1.extract.i.i, label %overflow.i.i, label %normal.i.i, !prof !0 + +normal.i.i: ; preds = %if.else.i.i + %75 = invoke i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* %58) + to label %.noexc31 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc31: ; preds = %normal.i.i + %.fca.0.extract.i.i = extractvalue { i8, i1 } %74, 0 + %76 = getelementptr i8, i8* %75, i64 24 + %77 = bitcast i8* %76 to i8*** + %78 = load i8**, i8*** %77, align 8 + %79 = getelementptr i8*, i8** %78, i64 13 + %80 = bitcast i8** %79 to i8 (i8 addrspace(1)*, i8)** + %81 = load i8 (i8 addrspace(1)*, i8)*, i8 (i8 addrspace(1)*, i8)** %80, align 8 + %82 = invoke i8 %81(i8 addrspace(1)* %58, i8 %.fca.0.extract.i.i) + to label %.noexc32 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc32: ; preds = %.noexc31 + %83 = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 %82, i8 %arr.get.sroa.0.0.copyload.i.i) + %.fca.1.extract5.i.i = extractvalue { i8, i1 } %83, 1 + br i1 %.fca.1.extract5.i.i, label %overflow11.i.i, label %"_ZN25std$FS$unittest.prop_test6Extend6Random18nextUtf8SecondByteEh.exit.i", !prof !0 + +overflow.i.i: ; preds = %if.else.i.i + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %callRet7.i.i51, i8 0, i64 16, i1 false) + invoke void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet7.i.i, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.24", i64 0, i64 0), i64 3) + to label %.noexc36.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +overflow11.i.i: ; preds = %.noexc32 + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %callRet13.i.i52, i8 0, i64 16, i1 false) + invoke void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet13.i.i, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.25", i64 0, i64 0), i64 3) + to label %.noexc36.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc36.invoke: ; preds = %overflow, %overflow26, %overflow36, %overflow46, %overflow11.i.i, %overflow.i.i, %overflow.i + %.in55 = phi %"record._ZN11std$FS$core6StringE"* [ %callRet.i, %overflow.i ], [ %callRet7.i.i, %overflow.i.i ], [ %callRet13.i.i, %overflow11.i.i ], [ %callRet18, %overflow ], [ %callRet28, %overflow26 ], [ %callRet38, %overflow36 ], [ %callRet48, %overflow46 ] + %84 = addrspacecast %"record._ZN11std$FS$core6StringE"* %.in55 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %85 = invoke i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %84) + to label %.noexc37.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc37.invoke: ; preds = %.noexc36.invoke, %invoke.continue13, %.noexc19, %.noexc28 + %86 = phi i8 addrspace(1)* [ %71, %.noexc28 ], [ %12, %invoke.continue13 ], [ %88, %.noexc19 ], [ %85, %.noexc36.invoke ] + invoke void @CJ_MCC_ThrowException(i8 addrspace(1)* %86) + to label %.noexc37.cont unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc37.cont: ; preds = %.noexc37.invoke + unreachable + +"_ZN25std$FS$unittest.prop_test6Extend6Random18nextUtf8SecondByteEh.exit.i": ; preds = %.noexc32 + %.fca.0.extract4.i.i = extractvalue { i8, i1 } %83, 0 + call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %callRet13.i.i52) + call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %callRet7.i.i51) + br label %"std/unittest.prop_test$lambda.23.exit" + +"std/unittest.prop_test$lambda.23.exit": ; preds = %while.body.i, %normal.i, %"_ZN25std$FS$unittest.prop_test6Extend6Random18nextUtf8SecondByteEh.exit.i" + %switchValue.0.i = phi i8 [ %.fca.0.extract.i, %normal.i ], [ %.fca.0.extract4.i.i, %"_ZN25std$FS$unittest.prop_test6Extend6Random18nextUtf8SecondByteEh.exit.i" ], [ %59, %while.body.i ] + call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %callRet.i53) + %87 = load i64, i64 addrspace(1)* %55, align 8 + %.not.i = icmp slt i64 %atom.284140E.05.i, %87 + br i1 %.not.i, label %arr.if.else.i, label %arr.if.then.i + +arr.if.then.i: ; preds = %"std/unittest.prop_test$lambda.23.exit" + %88 = invoke noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 56) + to label %.noexc19 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +.noexc19: ; preds = %arr.if.then.i + invoke void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %88) + to label %.noexc37.invoke unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +arr.if.else.i: ; preds = %"std/unittest.prop_test$lambda.23.exit" + %arr.idx.set.gep.i = getelementptr inbounds %ArrayLayout.UInt8, %ArrayLayout.UInt8 addrspace(1)* %57, i64 0, i32 1, i64 %atom.284140E.05.i + call void @llvm.cj.gcwrite.i8.i8(i8 %switchValue.0.i, i8 addrspace(1)* %49, i8 addrspace(1)* %arr.idx.set.gep.i) + %add.i = add nuw nsw i64 %atom.284140E.05.i, 1 + %89 = load i64, i64 addrspace(1)* %55, align 8 + %icmpslt.i = icmp slt i64 %add.i, %89 + call cangjiegccc void @MCC_SafepointStub() + br i1 %icmpslt.i, label %while.body.i, label %invoke.continue61 + +invoke.continue61: ; preds = %arr.if.else.i, %invoke.continue59 + store i8 addrspace(1)* %49, i8 addrspace(1)** %atom.288984E.sroa.0.0..sroa_idx11, align 8 + store i64 0, i64* %atom.288984E.sroa.4.0..sroa_idx14, align 8 + store i64 %byteSize, i64* %atom.288984E.sroa.5.0..sroa_idx17, align 8 + invoke void @"_ZN11std$FS$core6Extend4Char8fromUtf8ER_ZN11std$FS$core5ArrayIhEl"(%T2_clE* noalias nonnull sret(%T2_clE) %callRet63, i8 addrspace(1)* null, %"record._ZN25std$FS$unittest.prop_test11std$FS$core5ArrayIhE" addrspace(1)* %8, i64 0) + to label %invoke.continue65 unwind label %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + +invoke.continue65: ; preds = %invoke.continue61 + %90 = getelementptr inbounds %T2_clE, %T2_clE* %callRet63, i64 0, i32 0 + %91 = load i32, i32* %90, align 8 + ret i32 %91 + +landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp: ; preds = %.noexc31, %normal.i.i, %.noexc23, %default_thunk.i, %.noexc36.invoke, %.noexc37.invoke, %overflow11.i.i, %overflow.i.i, %.noexc28, %if.then.i.i, %overflow.i, %.noexc19, %arr.if.then.i, %invoke.continue61, %switch_end, %overflow46, %case_3_thunk, %overflow36, %case_2_thunk, %overflow26, %case_1_thunk, %overflow, %case_0_thunk, %invoke.continue13, %invoke.continue9, %invoke.continue6, %invoke.continue, %default_thunk + %lpad.loopexit.split-lp56 = landingpad token + catch i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core17OverflowExceptionE.objKlass" to i8*) + %92 = call i8* @CJ_MCC_GetExceptionWrapper() + %93 = call i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %92) + %94 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %93, i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core17OverflowExceptionE.objKlass" to i8*)) + call cangjiegccc void @MCC_SafepointStub() + br i1 %94, label %block.body3, label %if.else + +if.else: ; preds = %landingPad.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp.loopexit.split-lp + call void @CJ_MCC_EndCatch() + invoke void @CJ_MCC_ThrowException(i8 addrspace(1)* %93) + to label %invoke.continue71 unwind label %finally.rethrow + +invoke.continue71: ; preds = %if.else + unreachable + +finally.rethrow: ; preds = %if.else + %95 = landingpad token + catch i8* null + %96 = call i8* @CJ_MCC_GetExceptionWrapper() + %97 = call i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %96) + call void @CJ_MCC_EndCatch() + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %97) + unreachable +} + +attributes #0 = { "cj-runtime" "gc-leaf-function" } + +!0 = !{!"branch_weights", i32 1, i32 2000} diff --git a/llvm/test/Transforms/CJBarrierOpt/write_barrier_gep_fail.ll b/llvm/test/Transforms/CJBarrierOpt/write_barrier_gep_fail.ll new file mode 100644 index 000000000..9456bbbfa --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/write_barrier_gep_fail.ll @@ -0,0 +1,38 @@ +; RUN: opt --cj-barrier-opt -S < %s | FileCheck %s + +%record3 = type { i32, i8 addrspace(1)* } +%record2 = type { i32, %record3 } +%record1 = type { i32, i8 addrspace(1)*, %record2 } + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +%KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [1 x i8*] } +@"Klass0.objKlass" = external global %KlassInfo.0 +@"Klass1.objKlass" = external global %KlassInfo.1 + +declare i8 addrspace(1)* @CJ_MCC_NewPinnedObjectStub(i8*, i32, i1) +declare void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)*, i8*) #1 +declare void @foo() + +; CHECK: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) +; CHECK: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %test0, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + +define void @foo1(i8 addrspace(1)* %this, %record3 addrspace(1)* %0) #0 gc "cangjie" { +entry: + %test0 = call i8 addrspace(1)* @CJ_MCC_NewPinnedObjectStub(i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*), i32 16, i1 false) + %test1 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + %1 = bitcast i8 addrspace(1)* %this to %record1 addrspace(1)* + %2 = getelementptr inbounds %record1, %record1 addrspace(1)* %1, i32 0, i32 2 + %3 = getelementptr inbounds %record2, %record2 addrspace(1)* %2, i32 0, i32 1 + call void @foo() + %4 = bitcast %record3 addrspace(1)* %3 to i8 addrspace(1)* + %5 = bitcast %record3 addrspace(1)* %0 to i8 addrspace(1)* + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + %test2 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %test0, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + ret void +} + +attributes #0 = { "hasRcdParam" } +attributes #1 = { "cj-runtime" "gc-leaf-function" } diff --git a/llvm/test/Transforms/CJBarrierOpt/write_barrier_gep_pass.ll b/llvm/test/Transforms/CJBarrierOpt/write_barrier_gep_pass.ll new file mode 100644 index 000000000..9cedf7014 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/write_barrier_gep_pass.ll @@ -0,0 +1,37 @@ +; RUN: opt -enable-new-pm=false --cj-barrier-opt -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-opt -S < %s | FileCheck %s + +%record3 = type { i32, i8 addrspace(1)* } +%record2 = type { i32, %record3 } +%record1 = type { i32, i8 addrspace(1)*, %record2 } + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +%KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [1 x i8*] } +@"Klass0.objKlass" = external global %KlassInfo.0 +@"Klass1.objKlass" = external global %KlassInfo.1 + +declare i8 addrspace(1)* @CJ_MCC_NewPinnedObjectStub(i8*, i32, i1) +declare void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)*, i8*) #1 + +; CHECK: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) +; CHECK: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %test1, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + +define void @foo(i8 addrspace(1)* %this, %record3 addrspace(1)* %0) #0 gc "cangjie" { +entry: + %test0 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + %1 = bitcast i8 addrspace(1)* %this to %record1 addrspace(1)* + %2 = getelementptr inbounds %record1, %record1 addrspace(1)* %1, i32 0, i32 2 + %3 = getelementptr inbounds %record2, %record2 addrspace(1)* %2, i32 0, i32 1 + %4 = bitcast %record3 addrspace(1)* %3 to i8 addrspace(1)* + %5 = bitcast %record3 addrspace(1)* %0 to i8 addrspace(1)* + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + %test1 = call i8 addrspace(1)* @CJ_MCC_NewPinnedObjectStub(i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*), i32 16, i1 false) + %test2 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %test1, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + ret void +} + +attributes #0 = { "hasRcdParam" } +attributes #1 = { "cj-runtime" "gc-leaf-function" } diff --git a/llvm/test/Transforms/CJBarrierOpt/write_barrier_global.ll b/llvm/test/Transforms/CJBarrierOpt/write_barrier_global.ll new file mode 100644 index 000000000..880fd5a71 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/write_barrier_global.ll @@ -0,0 +1,35 @@ +; RUN: not not opt --cj-barrier-opt -S < %s -disable-output 2>&1 | FileCheck %s -check-prefixes=ABORT + +%record3 = type { i32, i8 addrspace(1)* } +%record2 = type { i32, %record3 } +%record1 = type { i32, i8 addrspace(1)*, %record2 } + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +%KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [1 x i8*] } +@"Klass1.objKlass" = external global %KlassInfo.1 +@Global0 = internal global %record3 zeroinitializer + +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)*, i8*) #1 + +define void @foo1(i8 addrspace(1)* %this, %record3 addrspace(1)* %0, i8* %arg) #0 gc "cangjie" { +entry: + %test0 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + %1 = bitcast i8 addrspace(1)* %this to %record1 addrspace(1)* + %2 = getelementptr inbounds %record1, %record1 addrspace(1)* %1, i32 0, i32 2 + %3 = getelementptr inbounds %record2, %record2 addrspace(1)* %2, i32 0, i32 1 + %4 = bitcast %record3 addrspace(1)* %3 to i8 addrspace(1)* + %5 = bitcast %record3 addrspace(1)* %0 to i8 addrspace(1)* + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + %test1 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* bitcast (%record3* @Global0 to i8*), i8* %arg, i64 16, i1 false) + ret void +} + +; ABORT: LLVM ERROR: Need write barrier in function +; ABORT: error: Aborted + +attributes #0 = { "hasRcdParam" } +attributes #1 = { "cj-runtime" "gc-leaf-function" } \ No newline at end of file diff --git a/llvm/test/Transforms/CJBarrierOpt/write_barrier_invoke1.ll b/llvm/test/Transforms/CJBarrierOpt/write_barrier_invoke1.ll new file mode 100644 index 000000000..da123a828 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/write_barrier_invoke1.ll @@ -0,0 +1,57 @@ +; RUN: opt -enable-new-pm=false --cj-barrier-opt -S < %s | FileCheck %s + +%record3 = type { i32, i8 addrspace(1)* } +%record2 = type { i32, %record3 } +%record1 = type { i32, i8 addrspace(1)*, %record2 } + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +%KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [1 x i8*] } +@"Klass0.objKlass" = external global %KlassInfo.0 +@"Klass1.objKlass" = external global %KlassInfo.1 + +declare i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8*, i32) +declare void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)*, i8*) #1 +declare i8* @CJ_MCC_GetExceptionWrapper() #1 +declare i8 addrspace(1)* @CJ_MCC_BeginCatch(i8*) #1 +declare void @CJ_MCC_EndCatch() #1 +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare i32 @personality_function() + +; CHECK: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) +; CHECK: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %test0, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + +define void @foo1(i8 addrspace(1)* %this, %record3 addrspace(1)* %0) #0 gc "cangjie" personality i32 ()* @personality_function { +entry: + %test0 = invoke i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*), i32 56) + to label %thunk unwind label %finallyOne + +thunk: ; preds = %entry + br label %body + +body: ; preds = %thunk + %test1 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + %1 = bitcast i8 addrspace(1)* %this to %record1 addrspace(1)* + %2 = getelementptr inbounds %record1, %record1 addrspace(1)* %1, i32 0, i32 2 + %3 = getelementptr inbounds %record2, %record2 addrspace(1)* %2, i32 0, i32 1 + %4 = bitcast %record3 addrspace(1)* %3 to i8 addrspace(1)* + %5 = bitcast %record3 addrspace(1)* %0 to i8 addrspace(1)* + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + %test2 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %test0, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + ret void + +finallyOne: ; preds = %thunk + %6 = landingpad token + catch i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*) + catch i8* null + %7 = call i8* @CJ_MCC_GetExceptionWrapper() + %8 = call i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %7) + call void @CJ_MCC_EndCatch() + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %8) + unreachable +} + +attributes #0 = { "hasRcdParam" } +attributes #1 = { "cj-runtime" "gc-leaf-function" } diff --git a/llvm/test/Transforms/CJBarrierOpt/write_barrier_invoke2.ll b/llvm/test/Transforms/CJBarrierOpt/write_barrier_invoke2.ll new file mode 100644 index 000000000..bbf72883e --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/write_barrier_invoke2.ll @@ -0,0 +1,57 @@ +; RUN: opt --cj-barrier-opt -S < %s | FileCheck %s + +%record3 = type { i32, i8 addrspace(1)* } +%record2 = type { i32, %record3 } +%record1 = type { i32, i8 addrspace(1)*, %record2 } + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +%KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [1 x i8*] } +@"Klass0.objKlass" = external global %KlassInfo.0 +@"Klass1.objKlass" = external global %KlassInfo.1 + +declare i8 addrspace(1)* @foo(i8*, i32) +declare void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)*, i8*) #1 +declare i8* @CJ_MCC_GetExceptionWrapper() #1 +declare i8 addrspace(1)* @CJ_MCC_BeginCatch(i8*) #1 +declare void @CJ_MCC_EndCatch() #1 +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare i32 @personality_function() + +define void @foo1(i8 addrspace(1)* %this, %record3 addrspace(1)* %0) #0 gc "cangjie" personality i32 ()* @personality_function { +entry: + %test0 = invoke i8 addrspace(1)* @foo(i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*), i32 56) + to label %thunk unwind label %finallyOne + +thunk: ; preds = %entry + br label %body + +; CHECK: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) +; CHECK: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %test0, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + +body: ; preds = %thunk + %test1 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + %1 = bitcast i8 addrspace(1)* %this to %record1 addrspace(1)* + %2 = getelementptr inbounds %record1, %record1 addrspace(1)* %1, i32 0, i32 2 + %3 = getelementptr inbounds %record2, %record2 addrspace(1)* %2, i32 0, i32 1 + %4 = bitcast %record3 addrspace(1)* %3 to i8 addrspace(1)* + %5 = bitcast %record3 addrspace(1)* %0 to i8 addrspace(1)* + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + %test2 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %test0, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + ret void + +finallyOne: ; preds = %thunk + %6 = landingpad token + catch i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*) + catch i8* null + %7 = call i8* @CJ_MCC_GetExceptionWrapper() + %8 = call i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %7) + call void @CJ_MCC_EndCatch() + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %8) + unreachable +} + +attributes #0 = { "hasRcdParam" } +attributes #1 = { "cj-runtime" "gc-leaf-function" } \ No newline at end of file diff --git a/llvm/test/Transforms/CJBarrierOpt/write_barrier_phi_fail.ll b/llvm/test/Transforms/CJBarrierOpt/write_barrier_phi_fail.ll new file mode 100644 index 000000000..f0afdd898 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/write_barrier_phi_fail.ll @@ -0,0 +1,76 @@ +; RUN: opt -enable-new-pm=false --cj-barrier-opt -S < %s | FileCheck %s + +%record3 = type { i32, i8 addrspace(1)* } +%record2 = type { i32, %record3 } +%record1 = type { i32, i8 addrspace(1)*, %record2 } + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +%KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [1 x i8*] } +@"Klass0.objKlass" = external global %KlassInfo.0 +@"Klass1.objKlass" = external global %KlassInfo.1 + +declare void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)*, i8*) #1 +declare i8* @CJ_MCC_GetExceptionWrapper() #1 +declare i8 addrspace(1)* @CJ_MCC_BeginCatch(i8*) #1 +declare void @CJ_MCC_EndCatch() #1 +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare i32 @personality_function() +declare i8 addrspace(1)* @CJ_MCC_NewPinnedObjectStub(i8*, i32, i1) +declare i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8*, i32) +declare void @foo() + +define void @foo1(i8 addrspace(1)* %this, %record3 addrspace(1)* %0) #0 gc "cangjie" personality i32 ()* @personality_function { +entry: + %test0 = load i8, i8 addrspace(1)* %this + %cond = icmp ne i8 %test0, 0 + br i1 %cond, label %if, label %else + +if: ; preds = %entry + %test1 = invoke i8 addrspace(1)* @CJ_MCC_NewPinnedObjectStub(i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*), i32 16, i1 false) + to label %invoke1 unwind label %finally + +invoke1: ; preds = %if + call void @foo() + br label %thunk + +else: ; preds = %entry + %test2 = invoke i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*), i32 16) + to label %invoke2 unwind label %finally + +invoke2: ; preds = %else + br label %thunk + +thunk: ; preds = %invoke1, %invoke2 + %test3 = phi i8 addrspace(1)* [ %test1, %invoke1 ], [ %test2, %invoke2 ] + br label %body + +; CHECK: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) +; CHECK: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %test3, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + +body: ; preds = %thunk + %test4 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + %1 = bitcast i8 addrspace(1)* %this to %record1 addrspace(1)* + %2 = getelementptr inbounds %record1, %record1 addrspace(1)* %1, i32 0, i32 2 + %3 = getelementptr inbounds %record2, %record2 addrspace(1)* %2, i32 0, i32 1 + %4 = bitcast %record3 addrspace(1)* %3 to i8 addrspace(1)* + %5 = bitcast %record3 addrspace(1)* %0 to i8 addrspace(1)* + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + %test5 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %test3, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + ret void + +finally: ; preds = %if, %else + %6 = landingpad token + catch i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*) + catch i8* null + %7 = call i8* @CJ_MCC_GetExceptionWrapper() + %8 = call i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %7) + call void @CJ_MCC_EndCatch() + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %8) + unreachable +} + +attributes #0 = { "hasRcdParam" } +attributes #1 = { "cj-runtime" "gc-leaf-function" } diff --git a/llvm/test/Transforms/CJBarrierOpt/write_barrier_phi_global.ll b/llvm/test/Transforms/CJBarrierOpt/write_barrier_phi_global.ll new file mode 100644 index 000000000..258f5c777 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/write_barrier_phi_global.ll @@ -0,0 +1,57 @@ +; RUN: not not opt --cj-barrier-opt -S < %s -disable-output 2>&1 | FileCheck %s -check-prefixes=ABORT + +%record3 = type { i32, i8 addrspace(1)* } +%record2 = type { i32, %record3 } +%record1 = type { i32, i8 addrspace(1)*, %record2 } + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +%KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [1 x i8*] } +@"Klass1.objKlass" = external global %KlassInfo.1 +@Global0 = internal global %record3 zeroinitializer + +declare void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** noalias nocapture writeonly, i8 addrspace(1)** noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)*, i8*) #1 +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1) + +define void @foo1(i8 addrspace(1)* %this, %record3 addrspace(1)* %0) #0 gc "cangjie" { +entry: + %test0 = alloca %record3 + %test1 = bitcast %record3* %test0 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %test1, i8 0, i64 32, i1 false) + %test2 = load i8, i8 addrspace(1)* %this + %cond = icmp ne i8 %test2, 0 + br i1 %cond, label %if, label %else + +if: ; preds = %entry + %test3 = getelementptr inbounds %record3, %record3* %test0, i64 0, i32 1 + br label %thunk + +else: ; preds = %entry + %test4 = getelementptr inbounds %record3, %record3* @Global0, i64 0, i32 1 + br label %thunk + +thunk: ; preds = %if, %else + %test5 = phi i8 addrspace(1)** [ %test3, %if ], [ %test4, %else ] + br label %body + +body: ; preds = %thunk + %test6 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + %1 = bitcast i8 addrspace(1)* %this to %record1 addrspace(1)* + %2 = getelementptr inbounds %record1, %record1 addrspace(1)* %1, i32 0, i32 2 + %3 = getelementptr inbounds %record2, %record2 addrspace(1)* %2, i32 0, i32 1 + %4 = bitcast %record3 addrspace(1)* %3 to i8 addrspace(1)* + %5 = bitcast %record3 addrspace(1)* %0 to i8 addrspace(1)* + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + %test7 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + %6 = getelementptr inbounds %record3, %record3* @Global0, i64 0, i32 1 + call void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** align 8 %test5, i8 addrspace(1)** align 8 %6, i64 8, i1 false) + ret void +} + +; ABORT: LLVM ERROR: Need write barrier in function +; ABORT: error: Aborted + +attributes #0 = { "hasRcdParam" } +attributes #1 = { "cj-runtime" "gc-leaf-function" } \ No newline at end of file diff --git a/llvm/test/Transforms/CJBarrierOpt/write_barrier_phi_pass.ll b/llvm/test/Transforms/CJBarrierOpt/write_barrier_phi_pass.ll new file mode 100644 index 000000000..612168cb7 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/write_barrier_phi_pass.ll @@ -0,0 +1,75 @@ +; RUN: opt --cj-barrier-opt -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-opt -S < %s | FileCheck %s + +%record3 = type { i32, i8 addrspace(1)* } +%record2 = type { i32, %record3 } +%record1 = type { i32, i8 addrspace(1)*, %record2 } + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +%KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [1 x i8*] } +@"Klass0.objKlass" = external global %KlassInfo.0 +@"Klass1.objKlass" = external global %KlassInfo.1 + +declare void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)*, i8*) #1 +declare i8* @CJ_MCC_GetExceptionWrapper() #1 +declare i8 addrspace(1)* @CJ_MCC_BeginCatch(i8*) #1 +declare void @CJ_MCC_EndCatch() #1 +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare i32 @personality_function() +declare i8 addrspace(1)* @CJ_MCC_NewPinnedObjectStub(i8*, i32, i1) +declare i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8*, i32) +declare void @llvm.cj.gcwrite.struct.p1i8(i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)*, i64) + +define void @foo1(i8 addrspace(1)* %this, %record3 addrspace(1)* %0) #0 gc "cangjie" personality i32 ()* @personality_function { +entry: + %test0 = load i8, i8 addrspace(1)* %this + %cond = icmp ne i8 %test0, 0 + br i1 %cond, label %if, label %else + +if: ; preds = %entry + %test1 = invoke i8 addrspace(1)* @CJ_MCC_NewPinnedObjectStub(i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*), i32 16, i1 false) + to label %invoke1 unwind label %finally + +invoke1: ; preds = %if + br label %thunk + +else: ; preds = %entry + %test2 = invoke i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*), i32 16) + to label %invoke2 unwind label %finally + +invoke2: ; preds = %else + br label %thunk + +thunk: ; preds = %invoke1, %invoke2 + %test3 = phi i8 addrspace(1)* [ %test1, %invoke1 ], [ %test2, %invoke2 ] + br label %body + +; CHECK: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) +; CHECK: call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %test3, i8 addrspace(1)* align 8 %5, i64 8, i1 false) +body: ; preds = %thunk + %test4 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + %1 = bitcast i8 addrspace(1)* %this to %record1 addrspace(1)* + %2 = getelementptr inbounds %record1, %record1 addrspace(1)* %1, i32 0, i32 2 + %3 = getelementptr inbounds %record2, %record2 addrspace(1)* %2, i32 0, i32 1 + %4 = bitcast %record3 addrspace(1)* %3 to i8 addrspace(1)* + %5 = bitcast %record3 addrspace(1)* %0 to i8 addrspace(1)* + call void @llvm.cj.gcwrite.struct.p1i8(i8 addrspace(1)* %this, i8 addrspace(1)* %4, i8 addrspace(1)* %5, i64 8) + %test5 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %test3, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + ret void + +finally: ; preds = %if, %else + %6 = landingpad token + catch i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*) + catch i8* null + %7 = call i8* @CJ_MCC_GetExceptionWrapper() + %8 = call i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %7) + call void @CJ_MCC_EndCatch() + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %8) + unreachable +} + +attributes #0 = { "hasRcdParam" } +attributes #1 = { "cj-runtime" "gc-leaf-function" } diff --git a/llvm/test/Transforms/CJBarrierOpt/write_barrier_select.ll b/llvm/test/Transforms/CJBarrierOpt/write_barrier_select.ll new file mode 100644 index 000000000..09f356cae --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/write_barrier_select.ll @@ -0,0 +1,44 @@ +; RUN: not not opt --cj-barrier-opt -S < %s -disable-output 2>&1 | FileCheck %s -check-prefixes=ABORT + +%record3 = type { i32, i8 addrspace(1)* } +%record2 = type { i32, %record3 } +%record1 = type { i32, i8 addrspace(1)*, %record2 } + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +%KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [1 x i8*] } +@"Klass1.objKlass" = external global %KlassInfo.1 +@Global0 = internal global %record3 zeroinitializer + +declare void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** noalias nocapture writeonly, i8 addrspace(1)** noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)*, i8*) #1 +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1) +declare void @foo(%record3* noalias sret(%record3) %0) + +define void @foo1(i8 addrspace(1)* %this, %record3 addrspace(1)* %0, i1 %cond) #0 gc "cangjie" { +entry: + %test0 = alloca %record3 + %test1 = bitcast %record3* %test0 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %test1, i8 0, i64 32, i1 false) + %test2 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + %1 = bitcast i8 addrspace(1)* %this to %record1 addrspace(1)* + %2 = getelementptr inbounds %record1, %record1 addrspace(1)* %1, i32 0, i32 2 + %3 = getelementptr inbounds %record2, %record2 addrspace(1)* %2, i32 0, i32 1 + %4 = bitcast %record3 addrspace(1)* %3 to i8 addrspace(1)* + %5 = bitcast %record3 addrspace(1)* %0 to i8 addrspace(1)* + call void @llvm.memcpy.p1i8.p1i8.i64(i8 addrspace(1)* align 8 %4, i8 addrspace(1)* align 8 %5, i64 8, i1 false) + %6 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %this, i8* bitcast (%KlassInfo.1* @"Klass1.objKlass" to i8*)) + call void @foo(%record3* noalias sret(%record3) %test0) + %7 = getelementptr inbounds %record3, %record3* %test0, i64 0, i32 1 + %8 = getelementptr inbounds %record3, %record3* @Global0, i64 0, i32 1 + %sel = select i1 %cond, i8 addrspace(1)** %7, i8 addrspace(1)** %8 + call void @llvm.memcpy.p0p1i8.p0p1i8.i64(i8 addrspace(1)** align 8 %sel, i8 addrspace(1)** align 8 %8, i64 8, i1 false) + ret void +} + +; ABORT: LLVM ERROR: Need write barrier in function +; ABORT: error: Aborted + +attributes #0 = { "hasRcdParam" } +attributes #1 = { "cj-runtime" "gc-leaf-function" } \ No newline at end of file diff --git a/llvm/test/Transforms/CJBarrierOpt/write_barrier_store.ll b/llvm/test/Transforms/CJBarrierOpt/write_barrier_store.ll new file mode 100644 index 000000000..968ff85c7 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierOpt/write_barrier_store.ll @@ -0,0 +1,54 @@ +; RUN: opt --cj-barrier-opt -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-opt -S < %s | FileCheck %s + +%record0 = type { i32, i8 addrspace(1)*, i8 addrspace(1)* } +@global0 = internal global %record0 zeroinitializer +@global1 = internal global i8 addrspace(1)* null +@global2 = internal global i8 addrspace(1)* null + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +%record1 = type { %record2, i32 } +%record2 = type { %ObjLayout.Object } +%ObjLayout.Object = type { %KlassInfo.0* } +@"Klass0.objKlass" = external global %KlassInfo.0 + +declare void @llvm.cj.gcwrite.static(i8 addrspace(1)*, i8 addrspace(1)** nocapture) +declare i8 addrspace(1)* @CJ_MCC_NewPinnedObjectStub(i8*, i32, i1) +declare void @foo() + +define void @foo1() #0 gc "cangjie" { +entry: + %test = alloca i8 addrspace(1)* + %0 = alloca i8 addrspace(1)* + br label %thunk + +; CHECK: store i32 10, i32* getelementptr inbounds (%record0, %record0* @global0, i32 0, i32 0) +; CHECK: call void @llvm.cj.gcwrite.static(i8 addrspace(1)* %3, i8 addrspace(1)** getelementptr inbounds (%record0, %record0* @global0, i32 0, i32 1)) +; CHECK: call void @llvm.cj.gcwrite.static(i8 addrspace(1)* %4, i8 addrspace(1)** getelementptr inbounds (%record0, %record0* @global0, i32 0, i32 2)) +thunk: ; preds = %entry + call void @foo() + %1 = call i8 addrspace(1)* @CJ_MCC_NewPinnedObjectStub(i8* bitcast (%KlassInfo.0* @"Klass0.objKlass" to i8*), i32 16, i1 false) + store i8 addrspace(1)* %1, i8 addrspace(1)** %0 + %2 = load i8 addrspace(1)*, i8 addrspace(1)** %0 + store i8 addrspace(1)* %2, i8 addrspace(1)** %test + store i32 10, i32* getelementptr inbounds (%record0, %record0* @global0, i32 0, i32 0) + %3 = load i8 addrspace(1)*, i8 addrspace(1)** @global1 + call void @llvm.cj.gcwrite.static(i8 addrspace(1)* %3, i8 addrspace(1)** getelementptr inbounds (%record0, %record0* @global0, i32 0, i32 1)) + %4 = load i8 addrspace(1)*, i8 addrspace(1)** @global2 + call void @llvm.cj.gcwrite.static(i8 addrspace(1)* %4, i8 addrspace(1)** getelementptr inbounds (%record0, %record0* @global0, i32 0, i32 2)) + br label %tmpLabel + +; CHECK: store atomic i32 0, i32 addrspace(1)* %7 seq_cst +tmpLabel: ; preds = %thunk + %5 = load i8 addrspace(1)*, i8 addrspace(1)** %test + %6 = bitcast i8 addrspace(1)* %5 to %record1 addrspace(1)* + %7 = getelementptr inbounds %record1, %record1 addrspace(1)* %6, i32 0, i32 1 + store atomic i32 0, i32 addrspace(1)* %7 seq_cst, align 32 + br label %end + +end: ; preds = %tmpLabel + ret void +} + +attributes #0 = { "hasRcdParam" } \ No newline at end of file diff --git a/llvm/test/Transforms/CJBarrierSplit/split-read-struct-one-ref.ll b/llvm/test/Transforms/CJBarrierSplit/split-read-struct-one-ref.ll new file mode 100644 index 000000000..f923d89df --- /dev/null +++ b/llvm/test/Transforms/CJBarrierSplit/split-read-struct-one-ref.ll @@ -0,0 +1,86 @@ +; RUN: opt -enable-new-pm=false -cj-barrier-split -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-split -S < %s | FileCheck %s + +%record = type { i8 addrspace(1)* } +%objlayout = type { i8*, %record } +%record1 = type { i64, i8 addrspace(1)* } +%objlayout1 = type { i8*, %record1 } +%record2 = type { i64, i8 addrspace(1)*, i64 } +%objlayout2 = type { i8*, %record2 } + +declare void @llvm.cj.gcread.struct(i8*, i8 addrspace(1)*, i8 addrspace(1)*, i64) + +; CHECK: entry1: +; CHECK-NEXT %0 = bitcast i8 addrspace(1)* %this to %objlayout addrspace(1)* +; CHECK-NEXT %1 = getelementptr inbounds %objlayout, %objlayout addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT %2 = bitcast %record addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT %3 = bitcast %record* %value to i8* +; CHECK-NEXT %4 = bitcast i8 addrspace(1)* %2 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT %5 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %4) +; CHECK-NEXT %6 = bitcast i8* %3 to i8 addrspace(1)** +; CHECK-NEXT store i8 addrspace(1)* %5, i8 addrspace(1)** %6, align 8 +; CHECK-NEXT ret void + + +define void @foo(i8 addrspace(1)* %this, %record* %value) gc "cangjie" { +entry1: + %0 = bitcast i8 addrspace(1)* %this to %objlayout addrspace(1)* + %1 = getelementptr inbounds %objlayout, %objlayout addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record* %value to i8* + call void @llvm.cj.gcread.struct(i8* %3, i8 addrspace(1)* %this, i8 addrspace(1)* %2, i64 8) + ret void +} + +; CHECK: entry2: +; CHECK-NEXT %0 = bitcast i8 addrspace(1)* %this to %objlayout1 addrspace(1)* +; CHECK-NEXT %1 = getelementptr inbounds %objlayout1, %objlayout1 addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT %2 = bitcast %record1 addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT %3 = bitcast %record1* %value to i8* +; CHECK-NEXT call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %3, i8 addrspace(1)* align 8 %2, i64 8, i1 false) +; CHECK-NEXT %4 = getelementptr inbounds i8, i8* %3, i32 8 +; CHECK-NEXT %5 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 8 +; CHECK-NEXT %6 = bitcast i8 addrspace(1)* %5 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT %7 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %6) +; CHECK-NEXT %8 = bitcast i8* %4 to i8 addrspace(1)** +; CHECK-NEXT store i8 addrspace(1)* %7, i8 addrspace(1)** %8, align 8 +; CHECK-NEXT ret void + + +define void @koo(i8 addrspace(1)* %this, %record1* %value) gc "cangjie" { +entry2: + %0 = bitcast i8 addrspace(1)* %this to %objlayout1 addrspace(1)* + %1 = getelementptr inbounds %objlayout1, %objlayout1 addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record1 addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record1* %value to i8* + call void @llvm.cj.gcread.struct(i8* %3, i8 addrspace(1)* %this, i8 addrspace(1)* %2, i64 16) + ret void +} + +; CHECK: entry3: +; CHECK-NEXT: %0 = bitcast i8 addrspace(1)* %this to %objlayout2 addrspace(1)* +; CHECK-NEXT: %1 = getelementptr inbounds %objlayout2, %objlayout2 addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT: %2 = bitcast %record2 addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT: %3 = bitcast %record2* %value to i8* +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %3, i8 addrspace(1)* align 8 %2, i64 8, i1 false) +; CHECK-NEXT: %4 = getelementptr inbounds i8, i8* %3, i32 8 +; CHECK-NEXT: %5 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 8 +; CHECK-NEXT: %6 = bitcast i8 addrspace(1)* %5 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: %7 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %6) +; CHECK-NEXT: %8 = bitcast i8* %4 to i8 addrspace(1)** +; CHECK-NEXT: store i8 addrspace(1)* %7, i8 addrspace(1)** %8, align 8 +; CHECK-NEXT: %9 = getelementptr inbounds i8, i8* %3, i32 16 +; CHECK-NEXT: %10 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 16 +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %9, i8 addrspace(1)* align 8 %10, i64 8, i1 false) +; CHECK-NEXT: ret void + + +define void @hoo(i8 addrspace(1)* %this, %record2* %value) gc "cangjie" { +entry3: + %0 = bitcast i8 addrspace(1)* %this to %objlayout2 addrspace(1)* + %1 = getelementptr inbounds %objlayout2, %objlayout2 addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record2 addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record2* %value to i8* + call void @llvm.cj.gcread.struct(i8* %3, i8 addrspace(1)* %this, i8 addrspace(1)* %2, i64 24) + ret void +} diff --git a/llvm/test/Transforms/CJBarrierSplit/split-read-struct-three-ref.ll b/llvm/test/Transforms/CJBarrierSplit/split-read-struct-three-ref.ll new file mode 100644 index 000000000..7bafdb91a --- /dev/null +++ b/llvm/test/Transforms/CJBarrierSplit/split-read-struct-three-ref.ll @@ -0,0 +1,132 @@ +; RUN: opt -cj-barrier-split -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-split -S < %s | FileCheck %s + +%record = type { i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)*, i64 } +%objlayout = type { i8*, %record } +%record1 = type { i8 addrspace(1)*, i64, i8 addrspace(1)*, i64, i8 addrspace(1)*, i64 } +%objlayout1 = type { i8*, %record1 } +%record2 = type { i64, i8 addrspace(1)*, i64, i8 addrspace(1)*, i64, i8 addrspace(1)* } +%objlayout2 = type { i8*, %record2 } + +declare void @llvm.cj.gcread.struct(i8*, i8 addrspace(1)*, i8 addrspace(1)*, i64) + +; CHECK: entry1: +; CHECK-NEXT: %0 = bitcast i8 addrspace(1)* %this to %objlayout addrspace(1)* +; CHECK-NEXT: %1 = getelementptr inbounds %objlayout, %objlayout addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT: %2 = bitcast %record addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT: %3 = bitcast %record* %value to i8* +; CHECK-NEXT: %4 = bitcast i8 addrspace(1)* %2 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: %5 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %4) +; CHECK-NEXT: %6 = bitcast i8* %3 to i8 addrspace(1)** +; CHECK-NEXT: store i8 addrspace(1)* %5, i8 addrspace(1)** %6, align 8 +; CHECK-NEXT: %7 = getelementptr inbounds i8, i8* %3, i32 8 +; CHECK-NEXT: %8 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 8 +; CHECK-NEXT: %9 = bitcast i8 addrspace(1)* %8 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: %10 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %9) +; CHECK-NEXT: %11 = bitcast i8* %7 to i8 addrspace(1)** +; CHECK-NEXT: store i8 addrspace(1)* %10, i8 addrspace(1)** %11, align 8 +; CHECK-NEXT: %12 = getelementptr inbounds i8, i8* %3, i32 16 +; CHECK-NEXT: %13 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 16 +; CHECK-NEXT: %14 = bitcast i8 addrspace(1)* %13 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: %15 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %14) +; CHECK-NEXT: %16 = bitcast i8* %12 to i8 addrspace(1)** +; CHECK-NEXT: store i8 addrspace(1)* %15, i8 addrspace(1)** %16, align 8 +; CHECK-NEXT: %17 = getelementptr inbounds i8, i8* %3, i32 24 +; CHECK-NEXT: %18 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 24 +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %17, i8 addrspace(1)* align 8 %18, i64 8, i1 false) +; CHECK-NEXT: ret void + + +define void @foo(i8 addrspace(1)* %this, %record* %value) gc "cangjie" { +entry1: + %0 = bitcast i8 addrspace(1)* %this to %objlayout addrspace(1)* + %1 = getelementptr inbounds %objlayout, %objlayout addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record* %value to i8* + call void @llvm.cj.gcread.struct(i8* %3, i8 addrspace(1)* %this, i8 addrspace(1)* %2, i64 32) + ret void +} + +; CHECK: entry2: +; CHECK-NEXT: %0 = bitcast i8 addrspace(1)* %this to %objlayout1 addrspace(1)* +; CHECK-NEXT: %1 = getelementptr inbounds %objlayout1, %objlayout1 addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT: %2 = bitcast %record1 addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT: %3 = bitcast %record1* %value to i8* +; CHECK-NEXT: %4 = bitcast i8 addrspace(1)* %2 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: %5 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %4) +; CHECK-NEXT: %6 = bitcast i8* %3 to i8 addrspace(1)** +; CHECK-NEXT: store i8 addrspace(1)* %5, i8 addrspace(1)** %6, align 8 +; CHECK-NEXT: %7 = getelementptr inbounds i8, i8* %3, i32 8 +; CHECK-NEXT: %8 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 8 +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %7, i8 addrspace(1)* align 8 %8, i64 8, i1 false) +; CHECK-NEXT: %9 = getelementptr inbounds i8, i8* %3, i32 16 +; CHECK-NEXT: %10 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 16 +; CHECK-NEXT: %11 = bitcast i8 addrspace(1)* %10 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: %12 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %11) +; CHECK-NEXT: %13 = bitcast i8* %9 to i8 addrspace(1)** +; CHECK-NEXT: store i8 addrspace(1)* %12, i8 addrspace(1)** %13, align 8 +; CHECK-NEXT: %14 = getelementptr inbounds i8, i8* %3, i32 24 +; CHECK-NEXT: %15 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 24 +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %14, i8 addrspace(1)* align 8 %15, i64 8, i1 false) +; CHECK-NEXT: %16 = getelementptr inbounds i8, i8* %3, i32 32 +; CHECK-NEXT: %17 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 32 +; CHECK-NEXT: %18 = bitcast i8 addrspace(1)* %17 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: %19 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %18) +; CHECK-NEXT: %20 = bitcast i8* %16 to i8 addrspace(1)** +; CHECK-NEXT: store i8 addrspace(1)* %19, i8 addrspace(1)** %20, align 8 +; CHECK-NEXT: %21 = getelementptr inbounds i8, i8* %3, i32 40 +; CHECK-NEXT: %22 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 40 +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %21, i8 addrspace(1)* align 8 %22, i64 8, i1 false) +; CHECK-NEXT: ret void + +define void @koo(i8 addrspace(1)* %this, %record1* %value) gc "cangjie" { +entry2: + %0 = bitcast i8 addrspace(1)* %this to %objlayout1 addrspace(1)* + %1 = getelementptr inbounds %objlayout1, %objlayout1 addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record1 addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record1* %value to i8* + call void @llvm.cj.gcread.struct(i8* %3, i8 addrspace(1)* %this, i8 addrspace(1)* %2, i64 48) + ret void +} + +; CHECK: entry3: +; CHECK-NEXT: %0 = bitcast i8 addrspace(1)* %this to %objlayout2 addrspace(1)* +; CHECK-NEXT: %1 = getelementptr inbounds %objlayout2, %objlayout2 addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT: %2 = bitcast %record2 addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT: %3 = bitcast %record2* %value to i8* +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %3, i8 addrspace(1)* align 8 %2, i64 8, i1 false) +; CHECK-NEXT: %4 = getelementptr inbounds i8, i8* %3, i32 8 +; CHECK-NEXT: %5 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 8 +; CHECK-NEXT: %6 = bitcast i8 addrspace(1)* %5 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: %7 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %6) +; CHECK-NEXT: %8 = bitcast i8* %4 to i8 addrspace(1)** +; CHECK-NEXT: store i8 addrspace(1)* %7, i8 addrspace(1)** %8, align 8 +; CHECK-NEXT: %9 = getelementptr inbounds i8, i8* %3, i32 16 +; CHECK-NEXT: %10 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 16 +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %9, i8 addrspace(1)* align 8 %10, i64 8, i1 false) +; CHECK-NEXT: %11 = getelementptr inbounds i8, i8* %3, i32 24 +; CHECK-NEXT: %12 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 24 +; CHECK-NEXT: %13 = bitcast i8 addrspace(1)* %12 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: %14 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %13) +; CHECK-NEXT: %15 = bitcast i8* %11 to i8 addrspace(1)** +; CHECK-NEXT: store i8 addrspace(1)* %14, i8 addrspace(1)** %15, align 8 +; CHECK-NEXT: %16 = getelementptr inbounds i8, i8* %3, i32 32 +; CHECK-NEXT: %17 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 32 +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %16, i8 addrspace(1)* align 8 %17, i64 8, i1 false) +; CHECK-NEXT: %18 = getelementptr inbounds i8, i8* %3, i32 40 +; CHECK-NEXT: %19 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 40 +; CHECK-NEXT: %20 = bitcast i8 addrspace(1)* %19 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: %21 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %20) +; CHECK-NEXT: %22 = bitcast i8* %18 to i8 addrspace(1)** +; CHECK-NEXT: store i8 addrspace(1)* %21, i8 addrspace(1)** %22, align 8 +; CHECK-NEXT: ret void + +define void @hoo(i8 addrspace(1)* %this, %record2* %value) gc "cangjie" { +entry3: + %0 = bitcast i8 addrspace(1)* %this to %objlayout2 addrspace(1)* + %1 = getelementptr inbounds %objlayout2, %objlayout2 addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record2 addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record2* %value to i8* + call void @llvm.cj.gcread.struct(i8* %3, i8 addrspace(1)* %this, i8 addrspace(1)* %2, i64 48) + ret void +} \ No newline at end of file diff --git a/llvm/test/Transforms/CJBarrierSplit/split-read-struct-two-ref.ll b/llvm/test/Transforms/CJBarrierSplit/split-read-struct-two-ref.ll new file mode 100644 index 000000000..2c1738c8f --- /dev/null +++ b/llvm/test/Transforms/CJBarrierSplit/split-read-struct-two-ref.ll @@ -0,0 +1,105 @@ +; RUN: opt -cj-barrier-split -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-split -S < %s | FileCheck %s + +%record = type { i8 addrspace(1)*, i8 addrspace(1)* } +%objlayout = type { i8*, %record } +%record1 = type { i8 addrspace(1)*, i64, i8 addrspace(1)* } +%objlayout1 = type { i8*, %record1 } +%record2 = type { i64, i8 addrspace(1)*, i64, i8 addrspace(1)*, i64 } +%objlayout2 = type { i8*, %record2 } + +declare void @llvm.cj.gcread.struct(i8*, i8 addrspace(1)*, i8 addrspace(1)*, i64) + +; CHECK: entry1: +; CHECK-NEXT: %0 = bitcast i8 addrspace(1)* %this to %objlayout addrspace(1)* +; CHECK-NEXT: %1 = getelementptr inbounds %objlayout, %objlayout addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT: %2 = bitcast %record addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT: %3 = bitcast %record* %value to i8* +; CHECK-NEXT: %4 = bitcast i8 addrspace(1)* %2 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: %5 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %4) +; CHECK-NEXT: %6 = bitcast i8* %3 to i8 addrspace(1)** +; CHECK-NEXT: store i8 addrspace(1)* %5, i8 addrspace(1)** %6, align 8 +; CHECK-NEXT: %7 = getelementptr inbounds i8, i8* %3, i32 8 +; CHECK-NEXT: %8 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 8 +; CHECK-NEXT: %9 = bitcast i8 addrspace(1)* %8 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: %10 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %9) +; CHECK-NEXT: %11 = bitcast i8* %7 to i8 addrspace(1)** +; CHECK-NEXT: store i8 addrspace(1)* %10, i8 addrspace(1)** %11, align 8 +; CHECK-NEXT: ret void + + +define void @foo(i8 addrspace(1)* %this, %record* %value) gc "cangjie" { +entry1: + %0 = bitcast i8 addrspace(1)* %this to %objlayout addrspace(1)* + %1 = getelementptr inbounds %objlayout, %objlayout addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record* %value to i8* + call void @llvm.cj.gcread.struct(i8* %3, i8 addrspace(1)* %this, i8 addrspace(1)* %2, i64 16) + ret void +} + +; CHECK: entry2: +; CHECK-NEXT: %0 = bitcast i8 addrspace(1)* %this to %objlayout1 addrspace(1)* +; CHECK-NEXT: %1 = getelementptr inbounds %objlayout1, %objlayout1 addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT: %2 = bitcast %record1 addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT: %3 = bitcast %record1* %value to i8* +; CHECK-NEXT: %4 = bitcast i8 addrspace(1)* %2 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: %5 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %4) +; CHECK-NEXT: %6 = bitcast i8* %3 to i8 addrspace(1)** +; CHECK-NEXT: store i8 addrspace(1)* %5, i8 addrspace(1)** %6, align 8 +; CHECK-NEXT: %7 = getelementptr inbounds i8, i8* %3, i32 8 +; CHECK-NEXT: %8 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 8 +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %7, i8 addrspace(1)* align 8 %8, i64 8, i1 false) +; CHECK-NEXT: %9 = getelementptr inbounds i8, i8* %3, i32 16 +; CHECK-NEXT: %10 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 16 +; CHECK-NEXT: %11 = bitcast i8 addrspace(1)* %10 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: %12 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %11) +; CHECK-NEXT: %13 = bitcast i8* %9 to i8 addrspace(1)** +; CHECK-NEXT: store i8 addrspace(1)* %12, i8 addrspace(1)** %13, align 8 +; CHECK-NEXT: ret void + +define void @koo(i8 addrspace(1)* %this, %record1* %value) gc "cangjie" { +entry2: + %0 = bitcast i8 addrspace(1)* %this to %objlayout1 addrspace(1)* + %1 = getelementptr inbounds %objlayout1, %objlayout1 addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record1 addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record1* %value to i8* + call void @llvm.cj.gcread.struct(i8* %3, i8 addrspace(1)* %this, i8 addrspace(1)* %2, i64 24) + ret void +} + +; CHECK: entry3: +; CHECK-NEXT: %0 = bitcast i8 addrspace(1)* %this to %objlayout2 addrspace(1)* +; CHECK-NEXT: %1 = getelementptr inbounds %objlayout2, %objlayout2 addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT: %2 = bitcast %record2 addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT: %3 = bitcast %record2* %value to i8* +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %3, i8 addrspace(1)* align 8 %2, i64 8, i1 false) +; CHECK-NEXT: %4 = getelementptr inbounds i8, i8* %3, i32 8 +; CHECK-NEXT: %5 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 8 +; CHECK-NEXT: %6 = bitcast i8 addrspace(1)* %5 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: %7 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %6) +; CHECK-NEXT: %8 = bitcast i8* %4 to i8 addrspace(1)** +; CHECK-NEXT: store i8 addrspace(1)* %7, i8 addrspace(1)** %8, align 8 +; CHECK-NEXT: %9 = getelementptr inbounds i8, i8* %3, i32 16 +; CHECK-NEXT: %10 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 16 +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %9, i8 addrspace(1)* align 8 %10, i64 8, i1 false) +; CHECK-NEXT: %11 = getelementptr inbounds i8, i8* %3, i32 24 +; CHECK-NEXT: %12 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 24 +; CHECK-NEXT: %13 = bitcast i8 addrspace(1)* %12 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: %14 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %13) +; CHECK-NEXT: %15 = bitcast i8* %11 to i8 addrspace(1)** +; CHECK-NEXT: store i8 addrspace(1)* %14, i8 addrspace(1)** %15, align 8 +; CHECK-NEXT: %16 = getelementptr inbounds i8, i8* %3, i32 32 +; CHECK-NEXT: %17 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 32 +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %16, i8 addrspace(1)* align 8 %17, i64 8, i1 false) +; CHECK-NEXT: ret void + +define void @hoo(i8 addrspace(1)* %this, %record2* %value) gc "cangjie" { +entry3: + %0 = bitcast i8 addrspace(1)* %this to %objlayout2 addrspace(1)* + %1 = getelementptr inbounds %objlayout2, %objlayout2 addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record2 addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record2* %value to i8* + call void @llvm.cj.gcread.struct(i8* %3, i8 addrspace(1)* %this, i8 addrspace(1)* %2, i64 40) + ret void +} \ No newline at end of file diff --git a/llvm/test/Transforms/CJBarrierSplit/split-write-struct-one-ref.ll b/llvm/test/Transforms/CJBarrierSplit/split-write-struct-one-ref.ll new file mode 100644 index 000000000..241c751b9 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierSplit/split-write-struct-one-ref.ll @@ -0,0 +1,86 @@ +; RUN: opt -cj-barrier-split -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-split -S < %s | FileCheck %s + +%record = type { i8 addrspace(1)* } +%objlayout = type { i8*, %record } +%record1 = type { i64, i8 addrspace(1)* } +%objlayout1 = type { i8*, %record1 } +%record2 = type { i64, i8 addrspace(1)*, i64 } +%objlayout2 = type { i8*, %record2 } + +declare void @llvm.cj.gcwrite.struct(i8 addrspace(1)*, i8 addrspace(1)*, i8*, i64) + +; CHECK: entry1: +; CHECK-NEXT: %0 = bitcast i8 addrspace(1)* %this to %objlayout addrspace(1)* +; CHECK-NEXT: %1 = getelementptr inbounds %objlayout, %objlayout addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT: %2 = bitcast %record addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT: %3 = bitcast %record* %value to i8* +; CHECK-NEXT: %4 = bitcast i8* %3 to i8 addrspace(1)** +; CHECK-NEXT: %5 = load i8 addrspace(1)*, i8 addrspace(1)** %4, align 8 +; CHECK-NEXT: %6 = bitcast i8 addrspace(1)* %2 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %5, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %6) +; CHECK-NEXT: ret void + + +define void @foo(i8 addrspace(1)* %this, %record* %value) gc "cangjie" { +entry1: + %0 = bitcast i8 addrspace(1)* %this to %objlayout addrspace(1)* + %1 = getelementptr inbounds %objlayout, %objlayout addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record* %value to i8* + call void @llvm.cj.gcwrite.struct(i8 addrspace(1)* %this, i8 addrspace(1)* %2, i8* %3, i64 8) + ret void +} + +; CHECK: entry2: +; CHECK-NEXT: %0 = bitcast i8 addrspace(1)* %this to %objlayout1 addrspace(1)* +; CHECK-NEXT: %1 = getelementptr inbounds %objlayout1, %objlayout1 addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT: %2 = bitcast %record1 addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT: %3 = bitcast %record1* %value to i8* +; CHECK-NEXT: call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* align 8 %2, i8* align 8 %3, i64 8, i1 false) +; CHECK-NEXT: %4 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 8 +; CHECK-NEXT: %5 = getelementptr inbounds i8, i8* %3, i32 8 +; CHECK-NEXT: %6 = bitcast i8* %5 to i8 addrspace(1)** +; CHECK-NEXT: %7 = load i8 addrspace(1)*, i8 addrspace(1)** %6, align 8 +; CHECK-NEXT: %8 = bitcast i8 addrspace(1)* %4 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %7, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %8) +; CHECK-NEXT: ret void + + +define void @koo(i8 addrspace(1)* %this, %record1* %value) gc "cangjie" { +entry2: + %0 = bitcast i8 addrspace(1)* %this to %objlayout1 addrspace(1)* + %1 = getelementptr inbounds %objlayout1, %objlayout1 addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record1 addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record1* %value to i8* + call void @llvm.cj.gcwrite.struct(i8 addrspace(1)* %this, i8 addrspace(1)* %2, i8* %3, i64 16) + ret void +} + +; CHECK: entry3: +; CHECK-NEXT: %0 = bitcast i8 addrspace(1)* %this to %objlayout2 addrspace(1)* +; CHECK-NEXT: %1 = getelementptr inbounds %objlayout2, %objlayout2 addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT: %2 = bitcast %record2 addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT: %3 = bitcast %record2* %value to i8* +; CHECK-NEXT: call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* align 8 %2, i8* align 8 %3, i64 8, i1 false) +; CHECK-NEXT: %4 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 8 +; CHECK-NEXT: %5 = getelementptr inbounds i8, i8* %3, i32 8 +; CHECK-NEXT: %6 = bitcast i8* %5 to i8 addrspace(1)** +; CHECK-NEXT: %7 = load i8 addrspace(1)*, i8 addrspace(1)** %6, align 8 +; CHECK-NEXT: %8 = bitcast i8 addrspace(1)* %4 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %7, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %8) +; CHECK-NEXT: %9 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 16 +; CHECK-NEXT: %10 = getelementptr inbounds i8, i8* %3, i32 16 +; CHECK-NEXT: call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* align 8 %9, i8* align 8 %10, i64 8, i1 false) +; CHECK-NEXT: ret void + + +define void @hoo(i8 addrspace(1)* %this, %record2* %value) gc "cangjie" { +entry3: + %0 = bitcast i8 addrspace(1)* %this to %objlayout2 addrspace(1)* + %1 = getelementptr inbounds %objlayout2, %objlayout2 addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record2 addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record2* %value to i8* + call void @llvm.cj.gcwrite.struct(i8 addrspace(1)* %this, i8 addrspace(1)* %2, i8* %3, i64 24) + ret void +} \ No newline at end of file diff --git a/llvm/test/Transforms/CJBarrierSplit/split-write-struct-three-ref.ll b/llvm/test/Transforms/CJBarrierSplit/split-write-struct-three-ref.ll new file mode 100644 index 000000000..1ba5ab9c5 --- /dev/null +++ b/llvm/test/Transforms/CJBarrierSplit/split-write-struct-three-ref.ll @@ -0,0 +1,134 @@ +; RUN: opt -cj-barrier-split -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-split -S < %s | FileCheck %s + +%record = type { i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)*, i64 } +%objlayout = type { i8*, %record } +%record1 = type { i8 addrspace(1)*, i64, i8 addrspace(1)*, i64, i8 addrspace(1)*, i64 } +%objlayout1 = type { i8*, %record1 } +%record2 = type { i64, i8 addrspace(1)*, i64, i8 addrspace(1)*, i64, i8 addrspace(1)* } +%objlayout2 = type { i8*, %record2 } + +declare void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)*, i8 addrspace(1)*, i8*, i64) + +; CHECK: entry1: +; CHECK-NEXT: %0 = bitcast i8 addrspace(1)* %this to %objlayout addrspace(1)* +; CHECK-NEXT: %1 = getelementptr inbounds %objlayout, %objlayout addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT: %2 = bitcast %record addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT: %3 = bitcast %record* %value to i8* +; CHECK-NEXT: %4 = bitcast i8* %3 to i8 addrspace(1)** +; CHECK-NEXT: %5 = load i8 addrspace(1)*, i8 addrspace(1)** %4, align 8 +; CHECK-NEXT: %6 = bitcast i8 addrspace(1)* %2 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %5, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %6) +; CHECK-NEXT: %7 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 8 +; CHECK-NEXT: %8 = getelementptr inbounds i8, i8* %3, i32 8 +; CHECK-NEXT: %9 = bitcast i8* %8 to i8 addrspace(1)** +; CHECK-NEXT: %10 = load i8 addrspace(1)*, i8 addrspace(1)** %9, align 8 +; CHECK-NEXT: %11 = bitcast i8 addrspace(1)* %7 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %10, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %11) +; CHECK-NEXT: %12 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 16 +; CHECK-NEXT: %13 = getelementptr inbounds i8, i8* %3, i32 16 +; CHECK-NEXT: %14 = bitcast i8* %13 to i8 addrspace(1)** +; CHECK-NEXT: %15 = load i8 addrspace(1)*, i8 addrspace(1)** %14, align 8 +; CHECK-NEXT: %16 = bitcast i8 addrspace(1)* %12 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %15, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %16) +; CHECK-NEXT: %17 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 24 +; CHECK-NEXT: %18 = getelementptr inbounds i8, i8* %3, i32 24 +; CHECK-NEXT: call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* align 8 %17, i8* align 8 %18, i64 8, i1 false) +; CHECK-NEXT: ret void + + +define void @foo(i8 addrspace(1)* %this, %record* %value) gc "cangjie" { +entry1: + %0 = bitcast i8 addrspace(1)* %this to %objlayout addrspace(1)* + %1 = getelementptr inbounds %objlayout, %objlayout addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record* %value to i8* + call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* %this, i8 addrspace(1)* %2, i8* %3, i64 32) + ret void +} + +; CHECK: entry2: +; CHECK-NEXT: %0 = bitcast i8 addrspace(1)* %this to %objlayout1 addrspace(1)* +; CHECK-NEXT: %1 = getelementptr inbounds %objlayout1, %objlayout1 addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT: %2 = bitcast %record1 addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT: %3 = bitcast %record1* %value to i8* +; CHECK-NEXT: %4 = bitcast i8* %3 to i8 addrspace(1)** +; CHECK-NEXT: %5 = load i8 addrspace(1)*, i8 addrspace(1)** %4, align 8 +; CHECK-NEXT: %6 = bitcast i8 addrspace(1)* %2 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %5, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %6) +; CHECK-NEXT: %7 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 8 +; CHECK-NEXT: %8 = getelementptr inbounds i8, i8* %3, i32 8 +; CHECK-NEXT: call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* align 8 %7, i8* align 8 %8, i64 8, i1 false) +; CHECK-NEXT: %9 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 16 +; CHECK-NEXT: %10 = getelementptr inbounds i8, i8* %3, i32 16 +; CHECK-NEXT: %11 = bitcast i8* %10 to i8 addrspace(1)** +; CHECK-NEXT: %12 = load i8 addrspace(1)*, i8 addrspace(1)** %11, align 8 +; CHECK-NEXT: %13 = bitcast i8 addrspace(1)* %9 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %12, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %13) +; CHECK-NEXT: %14 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 24 +; CHECK-NEXT: %15 = getelementptr inbounds i8, i8* %3, i32 24 +; CHECK-NEXT: call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* align 8 %14, i8* align 8 %15, i64 8, i1 false) +; CHECK-NEXT: %16 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 32 +; CHECK-NEXT: %17 = getelementptr inbounds i8, i8* %3, i32 32 +; CHECK-NEXT: %18 = bitcast i8* %17 to i8 addrspace(1)** +; CHECK-NEXT: %19 = load i8 addrspace(1)*, i8 addrspace(1)** %18, align 8 +; CHECK-NEXT: %20 = bitcast i8 addrspace(1)* %16 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %19, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %20) +; CHECK-NEXT: %21 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 40 +; CHECK-NEXT: %22 = getelementptr inbounds i8, i8* %3, i32 40 +; CHECK-NEXT: call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* align 8 %21, i8* align 8 %22, i64 8, i1 false) +; CHECK-NEXT: ret void + + +define void @koo(i8 addrspace(1)* %this, %record1* %value) gc "cangjie" { +entry2: + %0 = bitcast i8 addrspace(1)* %this to %objlayout1 addrspace(1)* + %1 = getelementptr inbounds %objlayout1, %objlayout1 addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record1 addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record1* %value to i8* + call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* %this, i8 addrspace(1)* %2, i8* %3, i64 48) + ret void +} + +; CHECK: entry3: +; CHECK-NEXT: %0 = bitcast i8 addrspace(1)* %this to %objlayout2 addrspace(1)* +; CHECK-NEXT: %1 = getelementptr inbounds %objlayout2, %objlayout2 addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT: %2 = bitcast %record2 addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT: %3 = bitcast %record2* %value to i8* +; CHECK-NEXT: call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* align 8 %2, i8* align 8 %3, i64 8, i1 false) +; CHECK-NEXT: %4 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 8 +; CHECK-NEXT: %5 = getelementptr inbounds i8, i8* %3, i32 8 +; CHECK-NEXT: %6 = bitcast i8* %5 to i8 addrspace(1)** +; CHECK-NEXT: %7 = load i8 addrspace(1)*, i8 addrspace(1)** %6, align 8 +; CHECK-NEXT: %8 = bitcast i8 addrspace(1)* %4 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %7, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %8) +; CHECK-NEXT: %9 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 16 +; CHECK-NEXT: %10 = getelementptr inbounds i8, i8* %3, i32 16 +; CHECK-NEXT: call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* align 8 %9, i8* align 8 %10, i64 8, i1 false) +; CHECK-NEXT: %11 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 24 +; CHECK-NEXT: %12 = getelementptr inbounds i8, i8* %3, i32 24 +; CHECK-NEXT: %13 = bitcast i8* %12 to i8 addrspace(1)** +; CHECK-NEXT: %14 = load i8 addrspace(1)*, i8 addrspace(1)** %13, align 8 +; CHECK-NEXT: %15 = bitcast i8 addrspace(1)* %11 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %14, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %15) +; CHECK-NEXT: %16 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 32 +; CHECK-NEXT: %17 = getelementptr inbounds i8, i8* %3, i32 32 +; CHECK-NEXT: call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* align 8 %16, i8* align 8 %17, i64 8, i1 false) +; CHECK-NEXT: %18 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 40 +; CHECK-NEXT: %19 = getelementptr inbounds i8, i8* %3, i32 40 +; CHECK-NEXT: %20 = bitcast i8* %19 to i8 addrspace(1)** +; CHECK-NEXT: %21 = load i8 addrspace(1)*, i8 addrspace(1)** %20, align 8 +; CHECK-NEXT: %22 = bitcast i8 addrspace(1)* %18 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %21, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %22) +; CHECK-NEXT: ret void + + +define void @hoo(i8 addrspace(1)* %this, %record2* %value) gc "cangjie" { +entry3: + %0 = bitcast i8 addrspace(1)* %this to %objlayout2 addrspace(1)* + %1 = getelementptr inbounds %objlayout2, %objlayout2 addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record2 addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record2* %value to i8* + call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* %this, i8 addrspace(1)* %2, i8* %3, i64 48) + ret void +} \ No newline at end of file diff --git a/llvm/test/Transforms/CJBarrierSplit/split-write-struct-two-ref.ll b/llvm/test/Transforms/CJBarrierSplit/split-write-struct-two-ref.ll new file mode 100644 index 000000000..5a21f307d --- /dev/null +++ b/llvm/test/Transforms/CJBarrierSplit/split-write-struct-two-ref.ll @@ -0,0 +1,106 @@ +; RUN: opt -cj-barrier-split -S < %s | FileCheck %s +; RUN: opt -passes=cj-barrier-split -S < %s | FileCheck %s + +%record = type { i8 addrspace(1)*, i8 addrspace(1)* } +%objlayout = type { i8*, %record } +%record1 = type { i8 addrspace(1)*, i64, i8 addrspace(1)* } +%objlayout1 = type { i8*, %record1 } +%record2 = type { i64, i8 addrspace(1)*, i64, i8 addrspace(1)*, i64 } +%objlayout2 = type { i8*, %record2 } + +declare void @llvm.cj.gcwrite.struct(i8 addrspace(1)*, i8 addrspace(1)*, i8*, i64) + +; CHECK: entry1: +; CHECK-NEXT: %0 = bitcast i8 addrspace(1)* %this to %objlayout addrspace(1)* +; CHECK-NEXT: %1 = getelementptr inbounds %objlayout, %objlayout addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT: %2 = bitcast %record addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT: %3 = bitcast %record* %value to i8* +; CHECK-NEXT: %4 = bitcast i8* %3 to i8 addrspace(1)** +; CHECK-NEXT: %5 = load i8 addrspace(1)*, i8 addrspace(1)** %4, align 8 +; CHECK-NEXT: %6 = bitcast i8 addrspace(1)* %2 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %5, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %6) +; CHECK-NEXT: %7 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 8 +; CHECK-NEXT: %8 = getelementptr inbounds i8, i8* %3, i32 8 +; CHECK-NEXT: %9 = bitcast i8* %8 to i8 addrspace(1)** +; CHECK-NEXT: %10 = load i8 addrspace(1)*, i8 addrspace(1)** %9, align 8 +; CHECK-NEXT: %11 = bitcast i8 addrspace(1)* %7 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %10, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %11) +; CHECK-NEXT: ret void + + +define void @foo(i8 addrspace(1)* %this, %record* %value) gc "cangjie" { +entry1: + %0 = bitcast i8 addrspace(1)* %this to %objlayout addrspace(1)* + %1 = getelementptr inbounds %objlayout, %objlayout addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record* %value to i8* + call void @llvm.cj.gcwrite.struct(i8 addrspace(1)* %this, i8 addrspace(1)* %2, i8* %3, i64 16) + ret void +} + +; CHECK: entry2: +; CHECK-NEXT: %0 = bitcast i8 addrspace(1)* %this to %objlayout1 addrspace(1)* +; CHECK-NEXT: %1 = getelementptr inbounds %objlayout1, %objlayout1 addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT: %2 = bitcast %record1 addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT: %3 = bitcast %record1* %value to i8* +; CHECK-NEXT: %4 = bitcast i8* %3 to i8 addrspace(1)** +; CHECK-NEXT: %5 = load i8 addrspace(1)*, i8 addrspace(1)** %4, align 8 +; CHECK-NEXT: %6 = bitcast i8 addrspace(1)* %2 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %5, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %6) +; CHECK-NEXT: %7 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 8 +; CHECK-NEXT: %8 = getelementptr inbounds i8, i8* %3, i32 8 +; CHECK-NEXT: call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* align 8 %7, i8* align 8 %8, i64 8, i1 false) +; CHECK-NEXT: %9 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 16 +; CHECK-NEXT: %10 = getelementptr inbounds i8, i8* %3, i32 16 +; CHECK-NEXT: %11 = bitcast i8* %10 to i8 addrspace(1)** +; CHECK-NEXT: %12 = load i8 addrspace(1)*, i8 addrspace(1)** %11, align 8 +; CHECK-NEXT: %13 = bitcast i8 addrspace(1)* %9 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %12, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %13) +; CHECK-NEXT: ret void + +define void @koo(i8 addrspace(1)* %this, %record1* %value) gc "cangjie" { +entry2: + %0 = bitcast i8 addrspace(1)* %this to %objlayout1 addrspace(1)* + %1 = getelementptr inbounds %objlayout1, %objlayout1 addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record1 addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record1* %value to i8* + call void @llvm.cj.gcwrite.struct(i8 addrspace(1)* %this, i8 addrspace(1)* %2, i8* %3, i64 24) + ret void +} + +; CHECK: entry3: +; CHECK-NEXT: %0 = bitcast i8 addrspace(1)* %this to %objlayout2 addrspace(1)* +; CHECK-NEXT: %1 = getelementptr inbounds %objlayout2, %objlayout2 addrspace(1)* %0, i64 0, i32 1 +; CHECK-NEXT: %2 = bitcast %record2 addrspace(1)* %1 to i8 addrspace(1)* +; CHECK-NEXT: %3 = bitcast %record2* %value to i8* +; CHECK-NEXT: call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* align 8 %2, i8* align 8 %3, i64 8, i1 false) +; CHECK-NEXT: %4 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 8 +; CHECK-NEXT: %5 = getelementptr inbounds i8, i8* %3, i32 8 +; CHECK-NEXT: %6 = bitcast i8* %5 to i8 addrspace(1)** +; CHECK-NEXT: %7 = load i8 addrspace(1)*, i8 addrspace(1)** %6, align 8 +; CHECK-NEXT: %8 = bitcast i8 addrspace(1)* %4 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %7, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %8) +; CHECK-NEXT: %9 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 16 +; CHECK-NEXT: %10 = getelementptr inbounds i8, i8* %3, i32 16 +; CHECK-NEXT: call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* align 8 %9, i8* align 8 %10, i64 8, i1 false) +; CHECK-NEXT: %11 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 24 +; CHECK-NEXT: %12 = getelementptr inbounds i8, i8* %3, i32 24 +; CHECK-NEXT: %13 = bitcast i8* %12 to i8 addrspace(1)** +; CHECK-NEXT: %14 = load i8 addrspace(1)*, i8 addrspace(1)** %13, align 8 +; CHECK-NEXT: %15 = bitcast i8 addrspace(1)* %11 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %14, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %15) +; CHECK-NEXT: %16 = getelementptr inbounds i8, i8 addrspace(1)* %2, i32 32 +; CHECK-NEXT: %17 = getelementptr inbounds i8, i8* %3, i32 32 +; CHECK-NEXT: call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* align 8 %16, i8* align 8 %17, i64 8, i1 false) +; CHECK-NEXT: ret void + + +define void @hoo(i8 addrspace(1)* %this, %record2* %value) gc "cangjie" { +entry3: + %0 = bitcast i8 addrspace(1)* %this to %objlayout2 addrspace(1)* + %1 = getelementptr inbounds %objlayout2, %objlayout2 addrspace(1)* %0, i64 0, i32 1 + %2 = bitcast %record2 addrspace(1)* %1 to i8 addrspace(1)* + %3 = bitcast %record2* %value to i8* + call void @llvm.cj.gcwrite.struct(i8 addrspace(1)* %this, i8 addrspace(1)* %2, i8* %3, i64 40) + ret void +} \ No newline at end of file diff --git a/llvm/test/Transforms/CJDevirtualOpt/generic_itable.ll b/llvm/test/Transforms/CJDevirtualOpt/generic_itable.ll new file mode 100644 index 000000000..cc5e2036e --- /dev/null +++ b/llvm/test/Transforms/CJDevirtualOpt/generic_itable.ll @@ -0,0 +1,46 @@ +; RUN: opt '-passes=cj-devirtual-opt' --cangjie-pipeline -S < %s | FileCheck %s + +%TypeTemplate = type { i8*, i8, i8, i16, i16, i8*, i8*, i8*, i8*, %ExtensionDef**, i16 } +%TypeInfo = type { i8*, i8, i8, i16, i32, i8*, i32, i8, i8, i16, i32*, i8*, i8*, i8*, %TypeInfo*, %ExtensionDef**, i8*, i8* } +%ExtensionDef = type { i32, i8, i8*, i8*, i8*, i8* } + +@"default:A.tt" = internal global %TypeTemplate { i8* null, i8 -128, i8 0, i16 0, i16 1, i8* null, i8* null, i8* null, i8* null, %ExtensionDef** getelementptr inbounds ([2 x %ExtensionDef*], [2 x %ExtensionDef*]* @NonExternalExtensionDefs, i32 0, i32 0), i16 0 } +@"std.core:Object.ti" = external global %TypeInfo +@"default:A.ti" = internal global %TypeInfo { i8* null, i8 -128, i8 0, i16 0, i32 0, i8* null, i32 0, i8 1, i8 1, i16 0, i32* null, i8* bitcast (%TypeTemplate* @"default:A.tt" to i8*), i8* null, i8* null, %TypeInfo* @"std.core:Object.ti", %ExtensionDef** getelementptr inbounds ([2 x %ExtensionDef*], [2 x %ExtensionDef*]* @NonExternalExtensionDefs, i32 0, i32 0), i8* null, i8* null } #0 +@"default:I1.ti" = internal global %TypeInfo { i8* null, i8 -127, i8 0, i16 0, i32 0, i8* null, i32 0, i8 1, i8 0, i16 0, i32* null, i8* null, i8* null, i8* null, %TypeInfo* null, %ExtensionDef** null, i8* null, i8* null } #0 +@"default:A_ed_default:I1.ft" = internal global [1 x i8*] [i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_func_A to i8*)] +@"default:A_ed_default:I1" = internal global %ExtensionDef { i32 0, i8 1, i8* bitcast (%TypeTemplate* @"default:A.tt" to i8*), i8* bitcast (%TypeInfo* @"default:I1.ti" to i8*), i8* null, i8* bitcast ([1 x i8*]* @"default:A_ed_default:I1.ft" to i8*) } + +@NonExternalExtensionDefs = internal global [2 x %ExtensionDef*] [%ExtensionDef* @"default:A_ed_default:I1", %ExtensionDef* null] + +; CHECK: [[TMP0:%.*]] = bitcast i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_func_A to i8*) to i64 (i8 addrspace(1)*, %TypeInfo*)* +; CHECK-NEXT: call i64 [[TMP0]](i8 addrspace(1)* [[TMP1:%.*]], %TypeInfo* [[TMP2:%.*]]) +define internal i64 @callee(i8 addrspace(1)* %ptr) { + %1 = bitcast i8 addrspace(1)* %ptr to %TypeInfo* addrspace(1)* + %2 = load %TypeInfo*, %TypeInfo* addrspace(1)* %1, align 8 + %3 = bitcast %TypeInfo* %2 to i8* + %4 = call i8* @CJ_MCC_GetMTable(i8* %3, i8* bitcast (%TypeInfo* @"default:I1.ti" to i8*)) + %5 = bitcast i8* %4 to i8** + %6 = getelementptr i8*, i8** %5, i32 0 + %7 = load i8*, i8** %6, !FuncTable !0 + %8 = bitcast i8* %7 to i64 (i8 addrspace(1)*, %TypeInfo*)* + %9 = call i64 %8(i8 addrspace(1)* %ptr, %TypeInfo* %2) + ret i64 %9 +} + +define void @caller() { + %1 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @"default:A.ti" to i8*), i32 8) + %2 = call i64 @callee(i8 addrspace(1)* %1) + ret void +} + +define internal i64 @test_func_A(i8 addrspace(1)* %this, %TypeInfo* %outerTI) { + ret i64 0 +} + +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) +declare i8* @CJ_MCC_GetMTable(i8*, i8*) + +attributes #0 = { "CFileKlass" } + +!0 = !{i64 0} diff --git a/llvm/test/Transforms/CJDevirtualOpt/generic_vtable.ll b/llvm/test/Transforms/CJDevirtualOpt/generic_vtable.ll new file mode 100644 index 000000000..c0e3f42a4 --- /dev/null +++ b/llvm/test/Transforms/CJDevirtualOpt/generic_vtable.ll @@ -0,0 +1,48 @@ +; RUN: opt '-passes=cj-runtime-lowering,cj-devirtual-opt' --cangjie-pipeline -S < %s | FileCheck %s + +%TypeTemplate = type { i8*, i8, i8, i16, i16, i8*, i8*, i8*, i8*, %ExtensionDef**, i16 } +%TypeInfo = type { i8*, i8, i8, i16, i32, i8*, i32, i8, i8, i16, i32*, i8*, i8*, i8*, %TypeInfo*, %ExtensionDef**, i8*, i8* } +%ExtensionDef = type { i32, i8, i8*, i8*, i8*, i8* } + +@"std.core:Object.ti" = external global %TypeInfo +@"default:A.ti" = internal global %TypeInfo { i8* null, i8 -128, i8 0, i16 0, i32 0, i8* null, i32 0, i8 1, i8 0, i16 1, i32* null, i8* null, i8* null, i8* null, %TypeInfo* @"std.core:Object.ti", %ExtensionDef** getelementptr inbounds ([3 x %ExtensionDef*], [3 x %ExtensionDef*]* @NonExternalExtensionDefs, i32 0, i32 1), i8* null, i8* null } #0 +@"default:B.tt" = internal global %TypeTemplate { i8* null, i8 -128, i8 0, i16 0, i16 1, i8* null, i8* null, i8* null, i8* null, %ExtensionDef** getelementptr inbounds ([3 x %ExtensionDef*], [3 x %ExtensionDef*]* @NonExternalExtensionDefs, i32 0, i32 0), i16 1 } +@"default:B.ti" = internal global %TypeInfo { i8* null, i8 -128, i8 0, i16 0, i32 0, i8* null, i32 0, i8 1, i8 1, i16 1, i32* null, i8* bitcast (%TypeTemplate* @"default:B.tt" to i8*), i8* null, i8* null, %TypeInfo* @"default:A.ti", %ExtensionDef** getelementptr inbounds ([3 x %ExtensionDef*], [3 x %ExtensionDef*]* @NonExternalExtensionDefs, i32 0, i32 0), i8* null, i8* null } #0 + +@"default:B_ed_default:A.ft" = internal global [1 x i8*] [i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_func_B to i8*)] +@"default:B_ed_default:A" = internal global %ExtensionDef { i32 1, i8 1, i8* bitcast (%TypeTemplate* @"default:B.tt" to i8*), i8* bitcast (%TypeInfo* @"default:A.ti" to i8*), i8* null, i8* bitcast ([1 x i8*]* @"default:B_ed_default:A.ft" to i8*) } +@"default:A_ed_default:A.ft" = internal global [1 x i8*] [i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_func_A to i8*)] +@"default:A_ed_default:A" = internal global %ExtensionDef { i32 0, i8 1, i8* bitcast (%TypeInfo* @"default:A.ti" to i8*), i8* bitcast (%TypeInfo* @"default:A.ti" to i8*), i8* null, i8* bitcast ([1 x i8*]* @"default:A_ed_default:A.ft" to i8*) } +@NonExternalExtensionDefs = internal global [3 x %ExtensionDef*] [%ExtensionDef* @"default:B_ed_default:A", %ExtensionDef* @"default:A_ed_default:A", %ExtensionDef* null] + +; CHECK: [[TMP0:%.*]] = bitcast i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_func_B to i8*) to i64 (i8 addrspace(1)*, %TypeInfo*)* +; CHECK-NEXT: call i64 [[TMP0]](i8 addrspace(1)* [[TMP1:%.*]], %TypeInfo* [[TMP2:%.*]]) +define internal i64 @callee(i8 addrspace(1)* %ptr) { + %1 = bitcast i8 addrspace(1)* %ptr to %TypeInfo* addrspace(1)* + %2 = load %TypeInfo*, %TypeInfo* addrspace(1)* %1, align 8 + %3 = bitcast %TypeInfo* %2 to i8* + %4 = call i8* @llvm.cj.get.vtable.func(i8* %3, i64 0, i64 0), !IntroType !0 + %5 = bitcast i8* %4 to i64 (i8 addrspace(1)*, %TypeInfo*)* + %6 = call i64 %5(i8 addrspace(1)* %ptr, %TypeInfo* %2) + ret i64 %6 +} + +define void @caller() { + %1 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @"default:B.ti" to i8*), i32 8) + %2 = call i64 @callee(i8 addrspace(1)* %1) + ret void +} + +define internal i64 @test_func_B(i8 addrspace(1)* %this, %TypeInfo* %outerTI) { + ret i64 1 +} +define internal i64 @test_func_A(i8 addrspace(1)* %this, %TypeInfo* %outerTI) { + ret i64 0 +} + +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) +declare i8* @llvm.cj.get.vtable.func(i8*, i64, i64) + +attributes #0 = { "CFileKlass" } + +!0 = !{!"default:A"} diff --git a/llvm/test/Transforms/CJDevirtualOpt/multi_generic_vtable.ll b/llvm/test/Transforms/CJDevirtualOpt/multi_generic_vtable.ll new file mode 100644 index 000000000..1423af1ff --- /dev/null +++ b/llvm/test/Transforms/CJDevirtualOpt/multi_generic_vtable.ll @@ -0,0 +1,73 @@ +; RUN: opt '-passes=cj-runtime-lowering,cj-devirtual-opt' --cangjie-pipeline -S < %s | FileCheck %s + +%TypeTemplate = type { i8*, i8, i8, i16, i16, i8*, i8*, i8*, i8*, %ExtensionDef**, i16 } +%TypeInfo = type { i8*, i8, i8, i16, i32, i8*, i32, i8, i8, i16, i32*, i8*, i8*, i8*, %TypeInfo*, %ExtensionDef**, i8*, i8* } +%ExtensionDef = type { i32, i8, i8*, i8*, i8*, i8* } + +@"std.core:Object.ti" = external global %TypeInfo +@"default:A1.ti" = global %TypeInfo { i8* null, i8 -128, i8 0, i16 0, i32 0, i8* null, i32 0, i8 1, i8 0, i16 1, i32* null, i8* null, i8* null, i8* null, %TypeInfo* @"std.core:Object.ti", %ExtensionDef** getelementptr inbounds ([6 x %ExtensionDef*], [6 x %ExtensionDef*]* @NonExternalExtensionDefs, i32 0, i32 2), i8* null, i8* null } #0 +@"default:A2.ti" = global %TypeInfo { i8* null, i8 -128, i8 0, i16 0, i32 0, i8* null, i32 0, i8 1, i8 0, i16 2, i32* null, i8* null, i8* null, i8* null, %TypeInfo* @"default:A1.ti", %ExtensionDef** getelementptr inbounds ([6 x %ExtensionDef*], [6 x %ExtensionDef*]* @NonExternalExtensionDefs, i32 0, i32 3), i8* null, i8* null } #0 +@"default:B.tt" = internal global %TypeTemplate { i8* null, i8 -128, i8 0, i16 0, i16 1, i8* null, i8* null, i8* null, i8* null, %ExtensionDef** getelementptr inbounds ([6 x %ExtensionDef*], [6 x %ExtensionDef*]* @NonExternalExtensionDefs, i32 0, i32 0), i16 1 } +@"default:B.ti" = internal global %TypeInfo { i8* null, i8 -128, i8 0, i16 0, i32 0, i8* null, i32 0, i8 1, i8 1, i16 2, i32* null, i8* bitcast (%TypeTemplate* @"default:B.tt" to i8*), i8* null, i8* null, %TypeInfo* @"default:A2.ti", %ExtensionDef** getelementptr inbounds ([6 x %ExtensionDef*], [6 x %ExtensionDef*]* @NonExternalExtensionDefs, i32 0, i32 0), i8* null, i8* null } #0 + +@"default:B_ed_default:A1.ft" = private global [1 x i8*] [i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_2_B to i8*)] +@"default:B_ed_default:A1" = private global %ExtensionDef { i32 1, i8 1, i8* bitcast (%TypeTemplate* @"default:B.tt" to i8*), i8* bitcast (%TypeInfo* @"default:A1.ti" to i8*), i8* null, i8* bitcast ([1 x i8*]* @"default:B_ed_default:A1.ft" to i8*) } +@"default:B_ed_default:A2.ft" = private global [1 x i8*] [i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_1_B to i8*)] +@"default:B_ed_default:A2" = private global %ExtensionDef { i32 1, i8 1, i8* bitcast (%TypeTemplate* @"default:B.tt" to i8*), i8* bitcast (%TypeInfo* @"default:A2.ti" to i8*), i8* null, i8* bitcast ([1 x i8*]* @"default:B_ed_default:A2.ft" to i8*) } +@"default:A1_ed_default:A1.ft" = private global [1 x i8*] [i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_2_A1 to i8*)] +@"default:A1_ed_default:A1" = private global %ExtensionDef { i32 0, i8 1, i8* bitcast (%TypeInfo* @"default:A1.ti" to i8*), i8* bitcast (%TypeInfo* @"default:A1.ti" to i8*), i8* null, i8* bitcast ([1 x i8*]* @"default:A1_ed_default:A1.ft" to i8*) } +@"default:A2_ed_default:A1.ft" = private global [1 x i8*] [i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_2_A1 to i8*)] +@"default:A2_ed_default:A1" = private global %ExtensionDef { i32 0, i8 1, i8* bitcast (%TypeInfo* @"default:A2.ti" to i8*), i8* bitcast (%TypeInfo* @"default:A1.ti" to i8*), i8* null, i8* bitcast ([1 x i8*]* @"default:A2_ed_default:A1.ft" to i8*) } +@"default:A2_ed_default:A2.ft" = private global [1 x i8*] [i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_1_A2 to i8*)] +@"default:A2_ed_default:A2" = private global %ExtensionDef { i32 0, i8 1, i8* bitcast (%TypeInfo* @"default:A2.ti" to i8*), i8* bitcast (%TypeInfo* @"default:A2.ti" to i8*), i8* null, i8* bitcast ([1 x i8*]* @"default:A2_ed_default:A2.ft" to i8*) } + +@NonExternalExtensionDefs = private global [6 x %ExtensionDef*] [%ExtensionDef* @"default:B_ed_default:A1", %ExtensionDef* @"default:B_ed_default:A2", %ExtensionDef* @"default:A1_ed_default:A1", %ExtensionDef* @"default:A2_ed_default:A1", %ExtensionDef* @"default:A2_ed_default:A2", %ExtensionDef* null] #4 + +; CHECK: [[FUNC0:%.*]] = bitcast i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_2_B to i8*) to i64 (i8 addrspace(1)*, %TypeInfo*)* +; CHECK-NEXT: [[TMP2:%.*]] = call i64 [[FUNC0]](i8 addrspace(1)* [[TMP0:%.*]], %TypeInfo* [[TMP1:%.*]]) +; CHECK: [[FUNC1:%.*]] = bitcast i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_2_B to i8*) to i64 (i8 addrspace(1)*, %TypeInfo*)* +; CHECK-NEXT: [[TMP3:%.*]] = call i64 [[FUNC1]](i8 addrspace(1)* [[TMP0]], %TypeInfo* [[TMP1]]) +; CHECK: [[FUNC2:%.*]] = bitcast i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_1_B to i8*) to i64 (i8 addrspace(1)*, %TypeInfo*)* +; CHECK-NEXT: [[TMP4:%.*]] = call i64 [[FUNC2]](i8 addrspace(1)* [[TMP0]], %TypeInfo* [[TMP1]]) +define internal void @callee(i8 addrspace(1)* %ptr) { + %1 = bitcast i8 addrspace(1)* %ptr to %TypeInfo* addrspace(1)* + %2 = load %TypeInfo*, %TypeInfo* addrspace(1)* %1, align 8 + %3 = bitcast %TypeInfo* %2 to i8* + %4 = call i8* @llvm.cj.get.vtable.func(i8* %3, i64 0, i64 0), !IntroType !0 + %5 = bitcast i8* %4 to i64 (i8 addrspace(1)*, %TypeInfo*)* + %6 = call i64 %5(i8 addrspace(1)* %ptr, %TypeInfo* %2) + %7 = call i8* @llvm.cj.get.vtable.func(i8* %3, i64 0, i64 0), !IntroType !0 + %8 = bitcast i8* %7 to i64 (i8 addrspace(1)*, %TypeInfo*)* + %9 = call i64 %8(i8 addrspace(1)* %ptr, %TypeInfo* %2) + %10 = call i8* @llvm.cj.get.vtable.func(i8* %3, i64 1, i64 0), !IntroType !1 + %11 = bitcast i8* %10 to i64 (i8 addrspace(1)*, %TypeInfo*)* + %12 = call i64 %11(i8 addrspace(1)* %ptr, %TypeInfo* %2) + ret void +} + +define void @caller() { + %1 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @"default:B.ti" to i8*), i32 8) + call void @callee(i8 addrspace(1)* %1) + ret void +} + +define internal i64 @test_1_B(i8 addrspace(1)* %this, %TypeInfo* %outerTI) { + ret i64 1 +} +define internal i64 @test_2_B(i8 addrspace(1)* %this, %TypeInfo* %outerTI) { + ret i64 0 +} +define internal i64 @test_2_A1(i8 addrspace(1)* %this, %TypeInfo* %outerTI) { + ret i64 0 +} +define internal i64 @test_1_A2(i8 addrspace(1)* %this, %TypeInfo* %outerTI) { + ret i64 0 +} + +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) +declare i8* @llvm.cj.get.vtable.func(i8*, i64, i64) + +attributes #0 = { "CFileKlass" } + +!0 = !{!"default:A1"} +!1 = !{!"default:A2"} diff --git a/llvm/test/Transforms/CJDevirtualOpt/non_generic_itable.ll b/llvm/test/Transforms/CJDevirtualOpt/non_generic_itable.ll new file mode 100644 index 000000000..4b4109636 --- /dev/null +++ b/llvm/test/Transforms/CJDevirtualOpt/non_generic_itable.ll @@ -0,0 +1,44 @@ +; RUN: opt '-passes=cj-devirtual-opt' --cangjie-pipeline -S < %s | FileCheck %s + +%TypeInfo = type { i8*, i8, i8, i16, i32, i8*, i32, i8, i8, i16, i32*, i8*, i8*, i8*, %TypeInfo*, %ExtensionDef**, i8*, i8* } +%ExtensionDef = type { i32, i8, i8*, i8*, i8*, i8* } + +@"std.core:Object.ti" = external global %TypeInfo +@"default:A.ti" = internal global %TypeInfo { i8* null, i8 -128, i8 0, i16 0, i32 0, i8* null, i32 0, i8 1, i8 0, i16 1, i32* null, i8* null, i8* null, i8* null, %TypeInfo* @"std.core:Object.ti", %ExtensionDef** getelementptr inbounds ([2 x %ExtensionDef*], [2 x %ExtensionDef*]* @NonExternalExtensionDefs, i32 0, i32 0), i8* null, i8* null } #0 +@"default:I1.ti" = internal global %TypeInfo { i8* null, i8 -127, i8 0, i16 0, i32 0, i8* null, i32 0, i8 1, i8 0, i16 0, i32* null, i8* null, i8* null, i8* null, %TypeInfo* null, %ExtensionDef** null, i8* null, i8* null } #0 +@"default:A_ed_default:I1.ft" = internal global [1 x i8*] [i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_func_A to i8*)] +@"default:A_ed_default:I1" = internal global %ExtensionDef { i32 0, i8 1, i8* bitcast (%TypeInfo* @"default:A.ti" to i8*), i8* bitcast (%TypeInfo* @"default:I1.ti" to i8*), i8* null, i8* bitcast ([1 x i8*]* @"default:A_ed_default:I1.ft" to i8*) } + +@NonExternalExtensionDefs = internal global [2 x %ExtensionDef*] [%ExtensionDef* @"default:A_ed_default:I1", %ExtensionDef* null] + +; CHECK: [[TMP0:%.*]] = bitcast i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_func_A to i8*) to i64 (i8 addrspace(1)*, %TypeInfo*)* +; CHECK-NEXT: call i64 [[TMP0]](i8 addrspace(1)* [[TMP1:%.*]], %TypeInfo* [[TMP2:%.*]]) +define internal i64 @callee(i8 addrspace(1)* %ptr) { + %1 = bitcast i8 addrspace(1)* %ptr to %TypeInfo* addrspace(1)* + %2 = load %TypeInfo*, %TypeInfo* addrspace(1)* %1, align 8 + %3 = bitcast %TypeInfo* %2 to i8* + %4 = call i8* @CJ_MCC_GetMTable(i8* %3, i8* bitcast (%TypeInfo* @"default:I1.ti" to i8*)) + %5 = bitcast i8* %4 to i8** + %6 = getelementptr i8*, i8** %5, i32 0 + %7 = load i8*, i8** %6, !FuncTable !0 + %8 = bitcast i8* %7 to i64 (i8 addrspace(1)*, %TypeInfo*)* + %9 = call i64 %8(i8 addrspace(1)* %ptr, %TypeInfo* %2) + ret i64 %9 +} + +define void @caller() { + %1 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @"default:A.ti" to i8*), i32 8) + %2 = call i64 @callee(i8 addrspace(1)* %1) + ret void +} + +define internal i64 @test_func_A(i8 addrspace(1)* %this, %TypeInfo* %outerTI) { + ret i64 0 +} + +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) +declare i8* @CJ_MCC_GetMTable(i8*, i8*) + +attributes #0 = { "CFileKlass" } + +!0 = !{i64 0} diff --git a/llvm/test/Transforms/CJDevirtualOpt/non_generic_vtable.ll b/llvm/test/Transforms/CJDevirtualOpt/non_generic_vtable.ll new file mode 100644 index 000000000..a332e8981 --- /dev/null +++ b/llvm/test/Transforms/CJDevirtualOpt/non_generic_vtable.ll @@ -0,0 +1,47 @@ +; RUN: opt '-passes=cj-runtime-lowering,cj-devirtual-opt' --cangjie-pipeline -S < %s | FileCheck %s + +%TypeInfo = type { i8*, i8, i8, i16, i32, i8*, i32, i8, i8, i16, i32*, i8*, i8*, i8*, %TypeInfo*, %ExtensionDef**, i8*, i8* } +%ExtensionDef = type { i32, i8, i8*, i8*, i8*, i8* } + +@"std.core:Object.ti" = external global %TypeInfo +@"default:A.ti" = internal global %TypeInfo { i8* null, i8 -128, i8 0, i16 0, i32 0, i8* null, i32 0, i8 1, i8 0, i16 1, i32* null, i8* null, i8* null, i8* null, %TypeInfo* @"std.core:Object.ti", %ExtensionDef** getelementptr inbounds ([3 x %ExtensionDef*], [3 x %ExtensionDef*]* @NonExternalExtensionDefs, i32 0, i32 1), i8* null, i8* null } #0 +@"default:B.ti" = internal global %TypeInfo { i8* null, i8 -128, i8 0, i16 0, i32 0, i8* null, i32 0, i8 1, i8 0, i16 1, i32* null, i8* null, i8* null, i8* null, %TypeInfo* @"default:A.ti", %ExtensionDef** getelementptr inbounds ([3 x %ExtensionDef*], [3 x %ExtensionDef*]* @NonExternalExtensionDefs, i32 0, i32 0), i8* null, i8* null } #0 + +@"default:B_ed_default:A.ft" = internal global [1 x i8*] [i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_func_B to i8*)] +@"default:B_ed_default:A" = internal global %ExtensionDef { i32 0, i8 1, i8* bitcast (%TypeInfo* @"default:B.ti" to i8*), i8* bitcast (%TypeInfo* @"default:A.ti" to i8*), i8* null, i8* bitcast ([1 x i8*]* @"default:B_ed_default:A.ft" to i8*) } +@"default:A_ed_default:A.ft" = internal global [1 x i8*] [i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_func_A to i8*)] +@"default:A_ed_default:A" = internal global %ExtensionDef { i32 0, i8 1, i8* bitcast (%TypeInfo* @"default:A.ti" to i8*), i8* bitcast (%TypeInfo* @"default:A.ti" to i8*), i8* null, i8* bitcast ([1 x i8*]* @"default:A_ed_default:A.ft" to i8*) } + +@NonExternalExtensionDefs = internal global [3 x %ExtensionDef*] [%ExtensionDef* @"default:B_ed_default:A", %ExtensionDef* @"default:A_ed_default:A", %ExtensionDef* null] + +; CHECK: [[TMP0:%.*]] = bitcast i8* bitcast (i64 (i8 addrspace(1)*, %TypeInfo*)* @test_func_B to i8*) to i64 (i8 addrspace(1)*, %TypeInfo*)* +; CHECK-NEXT: call i64 [[TMP0]](i8 addrspace(1)* [[TMP1:%.*]], %TypeInfo* [[TMP2:%.*]]) +define internal i64 @callee(i8 addrspace(1)* %ptr) { + %1 = bitcast i8 addrspace(1)* %ptr to %TypeInfo* addrspace(1)* + %2 = load %TypeInfo*, %TypeInfo* addrspace(1)* %1, align 8 + %3 = bitcast %TypeInfo* %2 to i8* + %4 = call i8* @llvm.cj.get.vtable.func(i8* %3, i64 0, i64 0), !IntroType !0 + %5 = bitcast i8* %4 to i64 (i8 addrspace(1)*, %TypeInfo*)* + %6 = call i64 %5(i8 addrspace(1)* %ptr, %TypeInfo* %2) + ret i64 %6 +} + +define void @caller() { + %1 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @"default:B.ti" to i8*), i32 8) + %2 = call i64 @callee(i8 addrspace(1)* %1) + ret void +} + +define internal i64 @test_func_B(i8 addrspace(1)* %this, %TypeInfo* %outerTI) { + ret i64 1 +} +define internal i64 @test_func_A(i8 addrspace(1)* %this, %TypeInfo* %outerTI) { + ret i64 0 +} + +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) +declare i8* @llvm.cj.get.vtable.func(i8*, i64, i64) + +attributes #0 = { "CFileKlass" } + +!0 = !{!"default:A"} diff --git a/llvm/test/Transforms/CJFillMetadata/reflection.ll b/llvm/test/Transforms/CJFillMetadata/reflection.ll new file mode 100644 index 000000000..df84353ff --- /dev/null +++ b/llvm/test/Transforms/CJFillMetadata/reflection.ll @@ -0,0 +1,90 @@ +; RUN: opt '-passes=cj-fill-metadata' --cangjie-pipeline -S < %s | FileCheck %s + +source_filename = "core" + +%Int64.Type = type { i64 } +%BitMap = type { i32, [0 x i8] } + +%ExtensionDef = type { i32, i8, i8*, i8*, i8*, i8* } +%TypeTemplate = type { i8*, i8, i8, i16, i16, i8*, i8*, i8*, i8*, %ExtensionDef**, i16 } + +%TypeInfo = type { i8*, i8, i8, i16, i32, %BitMap*, i32, i8, i8, i16, i32*, i8*, i8*, i8*, %TypeInfo*, %ExtensionDef**, i8*, i8* } +%record.DefaultHasher = type { i64 } + +@Int64.ti = external global %TypeInfo, !RelatedType !17 #1 +@Unit.ti = external global %TypeInfo, !RelatedType !17 #1 + +@gIDE = weak_odr constant i64 -1, align 8, !ReflectionGV !19 +@"std.core:CPointerHandle.tt" = global %TypeTemplate { i8* null, i8 22, i8 0, i16 2, i16 1, i8* null, i8* null, i8* null, i8* null, %ExtensionDef** null, i16 -32768 }, !Reflection !13 #0 +@"DefaultHasher.ti" = global %TypeInfo { i8* null, i8 22, i8 0, i16 1, i32 8, %BitMap* null, i32 0, i8 8, i8 0, i16 -32768, i32* null, i8* null, i8* null, i8* null, %TypeInfo* null, %ExtensionDef** null, i8* null, i8* null }, !RelatedType !11, !Reflection !4 #1 + +; CHECK: @pointer.reflectStr = internal global [8 x i8] c"pointer\00", align 1 #2 +; CHECK: @"std.core:CPointerHandle.tt.reflect" = internal global %"std.core:CPointerHandle.tt.reflectType" { i8* bitcast (%reflect.fieldNamesType1* @"std.core:CPointerHandle.tt.fieldnames" to i8*), i32 8, i16 1, i16 0, i32 0, i32 0, i8* null, i8* null, i8* bitcast (%reflect.fieldType* @"std.core:CPointerHandle.tt.field0" to i8*) } #2 +; CHECK: @res.reflectStr = internal global [4 x i8] c"res\00", align 1 #2 +; CHECK: @DefaultHasher.ti.fieldnames = internal global %reflect.fieldNamesType1 { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @res.reflectStr, i32 0, i32 0) } #2 +; CHECK: @finish.reflectStr = internal global [7 x i8] c"finish\00", align 1 #2 +; CHECK: @DefaultHasher.ti.method0 = internal global %reflect.methodType { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @finish.reflectStr, i32 0, i32 0), i32 8, i16 0, i16 0, i8* bitcast (void (%record.DefaultHasher*, %TypeInfo*)* @_CNat13DefaultHasher6finishHv to i8*), i8* bitcast (%TypeInfo* @Int64.ti to i8*), i8* null, i8* bitcast (%TypeInfo* @DefaultHasher.ti to i8*), i8* null, i8* null } #2 +; CHECK: @reset.reflectStr = internal global [6 x i8] c"reset\00", align 1 #2 +; CHECK: @DefaultHasher.ti.method1 = internal global %reflect.methodType { i8* getelementptr inbounds ([6 x i8], [6 x i8]* @reset.reflectStr, i32 0, i32 0), i32 133128, i16 0, i16 0, i8* null, i8* bitcast (%TypeInfo* @Unit.ti to i8*), i8* null, i8* bitcast (%TypeInfo* @DefaultHasher.ti to i8*), i8* null, i8* null } #2 +; CHECK: @DefaultHasher.ti.reflect = internal global %DefaultHasher.ti.reflectType { i8* bitcast (%reflect.fieldNamesType1* @DefaultHasher.ti.fieldnames to i8*), i32 8, i16 0, i16 0, i32 2, i32 0, i8* null, i8* null, i8* bitcast (%reflect.methodType* @DefaultHasher.ti.method0 to i8*), i8* bitcast (%reflect.methodType* @DefaultHasher.ti.method1 to i8*) } #2 +; CHECK: @std_std.core.packageInfo = global %std_std.core.pkgInfoType { i8* null, i32 1, i32 1, i32 1, i32 0, i64 1, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @std.reflectStr, i32 0, i32 0), i8* getelementptr inbounds ([9 x i8], [9 x i8]* @std.core.reflectStr, i32 0, i32 0), i8* getelementptr inbounds ([1 x i8], [1 x i8]* @NullString.reflectStr, i32 0, i32 0), i8* null, i8* null, i8* bitcast (%TypeInfo* @DefaultHasher.ti to i8*), i8* bitcast (%TypeTemplate* @"std.core:CPointerHandle.tt" to i8*), i8* bitcast (%reflect.methodType* @foo.staticMethod to i8*) }, no_sanitize_address #2 +; CHECK: @std.reflectStr = internal global [4 x i8] c"std\00", align 1 #2 +; CHECK: @std.core.reflectStr = internal global [9 x i8] c"std.core\00", align 1 #2 +; CHECK: @NullString.reflectStr = internal global [1 x i8] zeroinitializer, align 1 #2 +; CHECK: @next.reflectStr = internal global [5 x i8] c"next\00", align 1 #2 +; CHECK: @foo.staticMethod.actualParam = internal global %reflect.actualParamType1 { i8* getelementptr inbounds ([1 x i8], [1 x i8]* @NullString.reflectStr, i32 0, i32 0), i32 0, i8* bitcast (%TypeInfo* @DefaultHasher.ti to i8*), i8* null } #2 +; CHECK: @foo.staticMethod = internal global %reflect.methodType { i8* getelementptr inbounds ([5 x i8], [5 x i8]* @next.reflectStr, i32 0, i32 0), i32 24, i16 1, i16 0, i8* bitcast (void (%record.DefaultHasher*, i8 addrspace(1)*)* @foo to i8*), i8* bitcast (%TypeInfo* @DefaultHasher.ti to i8*), i8* null, i8* null, i8* bitcast (%reflect.actualParamType1* @foo.staticMethod.actualParam to i8*), i8* null } #2 + + +; CHECK: attributes #2 = { "CFileReflect" } + +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) + +define internal void @foo(%"record.DefaultHasher"* noalias sret(%"record.DefaultHasher") %0, i8 addrspace(1)* %1) gc "cangjie" { +allocas: + %2 = alloca %"record.DefaultHasher", align 8 + %3 = bitcast %"record.DefaultHasher"* %2 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %3, i8 0, i64 8, i1 false) + ret void +} + +define void @_CNat13DefaultHasher6finishHv(%"record.DefaultHasher"* noalias nocapture readonly %this, %TypeInfo* %outerTI) gc "cangjie" { +allocas: + %0 = alloca i64, align 8 + %1 = bitcast %"record.DefaultHasher"* %this to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false) + ret void +} + +attributes #0 = { "cj_tt" } +attributes #1 = { "CFileKlass" } + +!pkg_info = !{!0} +!types = !{!4} +!type_templates = !{!13} +!global_variables = !{!19} +!functions = !{!20} + +!0 = !{i32 1, !"", !"std", !"std.core", !"std_std.core", !""} +!1 = !{!"private", !"none"} +!2 = !{} +!3 = !{!"public", !"none"} +!4 = !{!"DefaultHasher.ti", !"", !5, !2, !7, !2, !3} +!5 = !{!6} +!6 = !{i32 0, !"res", !1} +!7 = !{!8, !9} +!8 = !{!"finish", !"Int64.ti", !"_CNat13DefaultHasher6finishHv", !2, !2, !3} +!9 = !{!"reset", !"Unit.ti", !"_CNat13DefaultHasher5resetHv", !2, !2, !10} +!10 = !{!"hasSRet0", !"mut", !"public", !"none"} +!11 = !{!"record.DefaultHasher"} +!12 = !{!"public", !"none"} +!13 = !{!"std.core:CPointerHandle.tt", !"", !14, !2, !2, !2, !12} +!14 = !{!15} +!15 = !{i32 0, !"pointer", !16} +!16 = !{!"immutable", !"public", !"none"} +!17 = !{!"Int64.Type"} +!18 = !{!"immutable", !"none"} +!19 = !{!"INVALID_ID", !"Int64.ti", !"gIDE", !18} +!20 = !{!"next", !"DefaultHasher.ti", !"foo", !21, !2, !16} +!21 = !{!22} +!22 = !{!"", !"DefaultHasher.ti", !12} diff --git a/llvm/test/Transforms/CJFillMetadata/reflection_debuginfo.ll b/llvm/test/Transforms/CJFillMetadata/reflection_debuginfo.ll new file mode 100644 index 000000000..b61eee475 --- /dev/null +++ b/llvm/test/Transforms/CJFillMetadata/reflection_debuginfo.ll @@ -0,0 +1,40 @@ +; RUN: opt '-passes=cj-fill-metadata' --cangjie-pipeline -S < %s | FileCheck %s + +source_filename = "core" + +%BitMap = type { i32, [0 x i8] } + +%ExtensionDef = type { i32, i8, i8*, i8*, i8*, i8* } + +%TypeInfo = type { i8*, i8, i8, i16, i32, %BitMap*, i32, i8, i8, i16, i32*, i8*, i8*, i8*, %TypeInfo*, %ExtensionDef**, i8*, i8* } +%record.DefaultHasher = type { i64 } + +@"DefaultHasher.ti" = global %TypeInfo { i8* null, i8 22, i8 0, i16 1, i32 8, %BitMap* null, i32 0, i8 8, i8 0, i16 -32768, i32* null, i8* null, i8* null, i8* null, %TypeInfo* null, %ExtensionDef** null, i8* null, i8* null }, !Reflection !4 #1 + +@"std.core:MemoryOrder.ti" = global %TypeInfo { i8* null, i8 23, i8 64, i16 0, i32 0, %BitMap* null, i32 0, i8 1, i8 0, i16 -32768, i32* null, i8* null, i8* null, i8* null, %TypeInfo* null, %ExtensionDef** null, i8* null, i8* null }, !Reflection !7 #1 + +; CHECK: @res.reflectStr = internal global [4 x i8] c"res\00", align 1 #1 +; CHECK: @DefaultHasher.ti.fieldNames = internal global %DefaultHasher.ti.fieldNamesType { i8* getelementptr inbounds ([4 x i8], [4 x i8]* @res.reflectStr, i32 0, i32 0) } #1 +; CHECK: @DefaultHasher.ti.reflect = internal global %DefaultHasher.ti.reflectType { i8* bitcast (%DefaultHasher.ti.fieldNamesType* @DefaultHasher.ti.fieldNames to i8*) } #1 +; CHECK: @SeqCst.reflectStr = internal global [7 x i8] c"SeqCst\00", align 1 #1 +; CHECK: @"std.core:MemoryOrder.ti.enumctors" = internal global %"std.core:MemoryOrder.ti.enumctorsType" { [7 x i8]* @SeqCst.reflectStr, i8* null } #1 +; CHECK: @"std.core:MemoryOrder.ti.reflect" = internal global %"std.core:MemoryOrder.ti.reflectType" { i8* bitcast (%"std.core:MemoryOrder.ti.enumctorsType"* @"std.core:MemoryOrder.ti.enumctors" to i8*), i32 8192, i32 1 } #1 +; CHECK: attributes #1 = { "CFileReflect" } + + +attributes #0 = { "cj_tt" } +attributes #1 = { "CFileKlass" } + +!types = !{!4, !7} + +!0 = !{!"std", !"std.core"} +!1 = !{!"private", !"none"} +!2 = !{} +!3 = !{!"public", !"none"} +!4 = !{!"DefaultHasher.ti", !5} +!5 = !{!6} +!6 = !{i32 0, !"res", !1} +!7 = !{!"std.core:MemoryOrder.ti", !8, !10} +!8 = !{!9} +!9 = !{!"SeqCst", !""} +!10 = !{!"enumKind0", !"none"} diff --git a/llvm/test/Transforms/CJGenericIntrinsicOpt/copy_generic_opt_non_phi.ll b/llvm/test/Transforms/CJGenericIntrinsicOpt/copy_generic_opt_non_phi.ll new file mode 100644 index 000000000..e25779d12 --- /dev/null +++ b/llvm/test/Transforms/CJGenericIntrinsicOpt/copy_generic_opt_non_phi.ll @@ -0,0 +1,175 @@ +; RUN: opt -passes=cj-generic-intrinsic-opt --cangjie-pipeline -S < %s | FileCheck %s + +%fake_typeinfo = type { i8*, i8, i8, i16, i32, i8*, i32, i8, i8, i32*, i8*, i8*, i8*, %fake_typeinfo*, i8*, i8* } + +define void @test_func_1(i8 addrspace(1)* noalias %this, i8 addrspace(1)* noalias %0, i8 addrspace(1)* noalias %1, i8 addrspace(1)* noalias %2, i8* %ti) { + ; CHECK: call void @llvm.cj.assign.generic(i8 addrspace(1)* %0, i8 addrspace(1)* %this, i8* %ti) + ; CHECK-NEXT: call void @llvm.cj.assign.generic(i8 addrspace(1)* %1, i8 addrspace(1)* %this, i8* %ti) + ; CHECK-NEXT: call void @llvm.cj.assign.generic(i8 addrspace(1)* %2, i8 addrspace(1)* %this, i8* %ti) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %0, i8 addrspace(1)* %this, i8* %ti) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %1, i8 addrspace(1)* %0, i8* %ti) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %2, i8 addrspace(1)* %1, i8* %ti) + ret void +} + +define void @test_func_2(i8 addrspace(1)* noalias %this, i8 addrspace(1)* noalias %0, i8 addrspace(1)* noalias %1, i8 addrspace(1)* noalias %2, + %fake_typeinfo* %ti0, %fake_typeinfo* %ti1, %fake_typeinfo* %ti2) { + %4 = getelementptr inbounds i8, i8 addrspace(1)* %this, i64 8 + %5 = bitcast %fake_typeinfo* %ti0 to i8* + %ti.size0 = getelementptr inbounds i8, i8* %5, i64 12 + %6 = bitcast i8* %ti.size0 to i32* + %7 = load i32, i32* %6, align 4 + call void @llvm.cj.gcread.generic(i8 addrspace(1)* %0, i8 addrspace(1)* %this, i8 addrspace(1)* %4, i32 %7) + %8 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 8 + %9 = bitcast %fake_typeinfo* %ti1 to i8* + %ti.size1 = getelementptr inbounds i8, i8* %9, i64 12 + %10 = bitcast i8* %ti.size1 to i32* + %11 = load i32, i32* %10, align 4 + ; CHECK: %12 = getelementptr i8, i8 addrspace(1)* %this, i64 8 + ; CHECK-NEXT: call void @llvm.cj.gcread.generic(i8 addrspace(1)* %1, i8 addrspace(1)* %this, i8 addrspace(1)* %12, i32 %11) + call void @llvm.cj.gcread.generic(i8 addrspace(1)* %1, i8 addrspace(1)* %0, i8 addrspace(1)* %8, i32 %11) + %12 = getelementptr inbounds i8, i8 addrspace(1)* %1, i64 8 + %13 = bitcast %fake_typeinfo* %ti2 to i8* + %ti.size2 = getelementptr inbounds i8, i8* %13, i64 12 + %14 = bitcast i8* %ti.size2 to i32* + %15 = load i32, i32* %14, align 4 + ; CHECK: %17 = getelementptr i8, i8 addrspace(1)* %this, i64 8 + ; CHECK-NEXT: call void @llvm.cj.gcread.generic(i8 addrspace(1)* %2, i8 addrspace(1)* %this, i8 addrspace(1)* %17, i32 %16) + call void @llvm.cj.gcread.generic(i8 addrspace(1)* %2, i8 addrspace(1)* %1, i8 addrspace(1)* %12, i32 %15) + ret void +} + +define void @test_func_3(i8 addrspace(1)* noalias nocapture writeonly sret(i8) %0, i8 addrspace(1)* nocapture %this, %fake_typeinfo* nocapture readonly %outerTI) { + %ti.typeArgs = getelementptr inbounds %fake_typeinfo, %fake_typeinfo* %outerTI, i64 0, i32 11 + %2 = bitcast i8** %ti.typeArgs to %fake_typeinfo*** + %3 = load %fake_typeinfo**, %fake_typeinfo*** %2, align 8 + %4 = load %fake_typeinfo*, %fake_typeinfo** %3, align 8 + %ti.size1 = getelementptr inbounds %fake_typeinfo, %fake_typeinfo* %4, i64 0, i32 4 + %5 = load i32, i32* %ti.size1, align 4 + %6 = add i32 %5, 15 + %7 = and i32 %6, -8 + %8 = bitcast %fake_typeinfo* %4 to i8* + %9 = tail call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* %8, i32 %7) + + %10 = getelementptr i8, i8 addrspace(1)* %this, i64 8 + %11 = bitcast i8 addrspace(1)* %10 to i8 addrspace(1)* addrspace(1)* + %12 = tail call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %11) + %13 = getelementptr inbounds i8, i8 addrspace(1)* %this, i64 16 + %14 = bitcast i8 addrspace(1)* %13 to i64 addrspace(1)* + %15 = load i64, i64 addrspace(1)* %14, align 8 + %sext = sext i32 %5 to i64 + %16 = mul i64 %15, %sext + %17 = add i64 %16, 16 + %18 = getelementptr inbounds i8, i8 addrspace(1)* %12, i64 %17 + tail call void @llvm.cj.gcread.generic(i8 addrspace(1)* %9, i8 addrspace(1)* %12, i8 addrspace(1)* %18, i32 %5) + %19 = tail call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* %8, i32 %7) + ; CHECK: %20 = getelementptr i8, i8 addrspace(1)* %12, i64 %17 + ; CHECK: call void @llvm.cj.gcread.generic(i8 addrspace(1)* %19, i8 addrspace(1)* %12, i8 addrspace(1)* %20, i32 %5) + tail call void @llvm.cj.assign.generic(i8 addrspace(1)* %19, i8 addrspace(1)* %9, i8* %8) + %20 = tail call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* %8, i32 %7) + ; CHECK: %22 = getelementptr i8, i8 addrspace(1)* %12, i64 %17 + ; CHECK: call void @llvm.cj.gcread.generic(i8 addrspace(1)* %21, i8 addrspace(1)* %12, i8 addrspace(1)* %22, i32 %5) + tail call void @llvm.cj.assign.generic(i8 addrspace(1)* %20, i8 addrspace(1)* %19, i8* %8) + ret void +} + +define void @test_func_4(i8 addrspace(1)* noalias nocapture %this, i8 addrspace(1)* noalias nocapture %0, %fake_typeinfo* nocapture readonly %outerTI) { + %ti.typeArgs = getelementptr inbounds %fake_typeinfo, %fake_typeinfo* %outerTI, i64 0, i32 11 + %2 = bitcast i8** %ti.typeArgs to %fake_typeinfo*** + %3 = load %fake_typeinfo**, %fake_typeinfo*** %2, align 8 + %4 = load %fake_typeinfo*, %fake_typeinfo** %3, align 8 + %ti.size1 = getelementptr inbounds %fake_typeinfo, %fake_typeinfo* %4, i64 0, i32 4 + %5 = load i32, i32* %ti.size1, align 4 + %6 = add i32 %5, 15 + %7 = and i32 %6, -8 + ; CHECK: %8 = bitcast %fake_typeinfo* %4 to i8* + %8 = bitcast %fake_typeinfo* %4 to i8* + %9 = tail call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* %8, i32 %7) + %10 = getelementptr i8, i8 addrspace(1)* %this, i64 8 + %11 = bitcast i8 addrspace(1)* %10 to i8 addrspace(1)* addrspace(1)* + %12 = tail call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %11) + %13 = getelementptr inbounds i8, i8 addrspace(1)* %this, i64 16 + %14 = bitcast i8 addrspace(1)* %13 to i64 addrspace(1)* + %15 = load i64, i64 addrspace(1)* %14, align 8 + %sext = sext i32 %5 to i64 + %16 = mul i64 %15, %sext + %17 = add i64 %16, 16 + %18 = getelementptr inbounds i8, i8 addrspace(1)* %12, i64 %17 + tail call void @llvm.cj.gcwrite.generic(i8 addrspace(1)* %12, i8 addrspace(1)* %18, i8 addrspace(1)* %0, i32 %5) + ; CHECK: [[TMP0:%.*]] = bitcast %fake_typeinfo* %4 to i8* + ; CHECK-NEXT: call void @llvm.cj.assign.generic(i8 addrspace(1)* %9, i8 addrspace(1)* %0, i8* [[TMP0]]) + tail call void @llvm.cj.gcread.generic(i8 addrspace(1)* %9, i8 addrspace(1)* %12, i8 addrspace(1)* %18, i32 %5) + %19 = tail call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* %8, i32 %7) + ; CHECK: call void @llvm.cj.assign.generic(i8 addrspace(1)* %20, i8 addrspace(1)* %0, i8* %8) + tail call void @llvm.cj.assign.generic(i8 addrspace(1)* %19, i8 addrspace(1)* %9, i8* %8) + %20 = tail call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* %8, i32 %7) + ; CHECK: call void @llvm.cj.assign.generic(i8 addrspace(1)* %21, i8 addrspace(1)* %0, i8* %8) + tail call void @llvm.cj.assign.generic(i8 addrspace(1)* %20, i8 addrspace(1)* %19, i8* %8) + ret void +} + +; %ti1<%ti3, %ti0> +define void @test_func_5(%fake_typeinfo* %ti0, %fake_typeinfo* %ti1, %fake_typeinfo* %ti3) { + %1 = bitcast %fake_typeinfo* %ti0 to i8* + %ti.size = getelementptr inbounds i8, i8* %1, i64 12 + %2 = bitcast i8* %ti.size to i32* + %3 = load i32, i32* %2, align 4 + %4 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* %1, i32 %3) + call void @init_func(i8 addrspace(1)* noalias sret(i8) %4) + %5 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* %1, i32 %3) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %5, i8 addrspace(1)* %4, i8* %1) + %6 = bitcast %fake_typeinfo* %ti1 to i8* + %ti.size2 = getelementptr inbounds i8, i8* %6, i64 12 + %7 = bitcast i8* %ti.size2 to i32* + %8 = load i32, i32* %7, align 4 + %9 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* %6, i32 %8) + %10 = call i64 @get_offsets(i8* %1) + %11 = add nuw nsw i64 %10, 8 + %12 = getelementptr inbounds i8, i8 addrspace(1)* %5, i64 %11 + ; CHECK: [[TMP1:%.*]] = getelementptr i8, i8 addrspace(1)* %4, i64 %11 + ; CHECK-NEXT: call void @llvm.cj.gcread.generic(i8 addrspace(1)* %9, i8 addrspace(1)* %4, i8 addrspace(1)* [[TMP1]], i32 %8) + call void @llvm.cj.gcread.generic(i8 addrspace(1)* %9, i8 addrspace(1)* %5, i8 addrspace(1)* %12, i32 %8) + %13 = getelementptr inbounds i8, i8 addrspace(1)* %9, i64 8 + %14 = getelementptr inbounds %fake_typeinfo, %fake_typeinfo* %ti3, i64 0, i32 1 + %ti.kind.i = load i8, i8* %14, align 1 + %15 = icmp slt i8 %ti.kind.i, 0 + br i1 %15, label %handle_load_ref, label %handle_load_non_ref + +; CHECK: [[ADD1:%.*]] = add i64 8, %11 +; CHECK-NEXT: [[SUB1:%.*]] = sub i64 [[ADD1]], 8 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, i8 addrspace(1)* %4, i64 [[SUB1]] +; CHECK-NEXT: [[TMP3:%.*]] = bitcast i8 addrspace(1)* [[TMP2]] to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: [[TMP4:%.*]] = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %4, i8 addrspace(1)* addrspace(1)* [[TMP3]]) +handle_load_ref: + %16 = bitcast i8 addrspace(1)* %13 to i8 addrspace(1)* addrspace(1)* + %17 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %9, i8 addrspace(1)* addrspace(1)* %16) + br label %load_exit + +; CHECK: [[ADD2:%.*]] = add i64 8, %11 +; CHECK-NEXT: [[SUB2:%.*]] = sub i64 [[ADD2]], 8 +; CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, i8 addrspace(1)* %4, i64 [[SUB2]] +; CHECK-NEXT: call void @llvm.cj.gcread.generic(i8 addrspace(1)* [[TMP6:%.*]], i8 addrspace(1)* %4, i8 addrspace(1)* [[TMP5]], i32 %23) +handle_load_non_ref: + %ti.size3 = getelementptr inbounds %fake_typeinfo, %fake_typeinfo* %ti3, i64 0, i32 4 + %18 = load i32, i32* %ti.size3, align 4 + %19 = bitcast %fake_typeinfo* %ti3 to i8* + %20 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* %19, i32 %18) + call void @llvm.cj.gcread.generic(i8 addrspace(1)* %20, i8 addrspace(1)* %9, i8 addrspace(1)* %13, i32 %18) + br label %load_exit + +load_exit: + %21 = phi i8 addrspace(1)* [ %17, %handle_load_ref ], [ %20, %handle_load_non_ref ] + ret void +} + +declare void @init_func(i8 addrspace(1)* noalias sret(i8)) +declare i64 @get_offsets(i8*) #1 +declare void @llvm.cj.assign.generic(i8 addrspace(1)* noalias nocapture writeonly %0, i8 addrspace(1)* noalias nocapture readonly %1, i8* noalias nocapture readonly %2) #0 +declare void @llvm.cj.gcread.generic(i8 addrspace(1)* noalias nocapture writeonly %0, i8 addrspace(1)* nocapture readonly %1, i8 addrspace(1)* nocapture readonly %2, i32 %3) #0 +declare void @llvm.cj.gcwrite.generic(i8 addrspace(1)* nocapture %0, i8 addrspace(1)* nocapture writeonly %1, i8 addrspace(1)* noalias nocapture readonly %2, i32 %3) #0 +declare noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) #2 +declare i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* nocapture, i8 addrspace(1)* addrspace(1)* nocapture) #1 + +attributes #0 = { argmemonly mustprogress nounwind willreturn } +attributes #1 = { argmemonly nounwind readonly willreturn } +attributes #2 = { nounwind "cj-heapmalloc" "cj-runtime" } diff --git a/llvm/test/Transforms/CJGenericIntrinsicOpt/copy_generic_opt_phi.ll b/llvm/test/Transforms/CJGenericIntrinsicOpt/copy_generic_opt_phi.ll new file mode 100644 index 000000000..db8b0c4b2 --- /dev/null +++ b/llvm/test/Transforms/CJGenericIntrinsicOpt/copy_generic_opt_phi.ll @@ -0,0 +1,172 @@ +; RUN: opt -passes=cj-generic-intrinsic-opt --cangjie-pipeline -S < %s | FileCheck %s + +%fake_typeinfo = type { i8*, i8, i8, i16, i32, i8*, i32, i8, i8, i32*, i8*, i8*, i8*, %fake_typeinfo*, i8*, i8* } + +define void @test_func_1(i8 addrspace(1)* noalias %this, i8 addrspace(1)* noalias %0, i8 addrspace(1)* noalias %1, %fake_typeinfo* %ti, i1 %cond) { + %3 = bitcast %fake_typeinfo* %ti to i8* + %ti.size = getelementptr inbounds i8, i8* %3, i64 12 + %4 = bitcast i8* %ti.size to i32* + %5 = load i32, i32* %4, align 4 + %6 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* %3, i32 %5) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %6, i8 addrspace(1)* %this, i8* %3) + br i1 %cond, label %bb1, label %bb2 + +bb1: + call void @llvm.cj.assign.generic(i8 addrspace(1)* %6, i8 addrspace(1)* %1, i8* %3) + br label %bb2 + +bb2: + ; CHECK: %7 = phi i8 addrspace(1)* [ %1, %bb1 ], [ %this, %2 ] + ; CHECK-NEXT: %8 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* %3, i32 %5) + ; CHECK-NEXT: call void @llvm.cj.assign.generic(i8 addrspace(1)* %8, i8 addrspace(1)* %7, i8* %3) + %7 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* %3, i32 %5) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %7, i8 addrspace(1)* %6, i8* %3) + ret void +} + +define void @test_func_2(i8 addrspace(1)* noalias %this, i8 addrspace(1)* noalias %0, i8 addrspace(1)* noalias %1, %fake_typeinfo* %ti, i1 %cond) { + %3 = bitcast %fake_typeinfo* %ti to i8* + %ti.size = getelementptr inbounds i8, i8* %3, i64 12 + %4 = bitcast i8* %ti.size to i32* + %5 = load i32, i32* %4, align 4 + %6 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* %3, i32 %5) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %6, i8 addrspace(1)* %this, i8* %3) + br i1 %cond, label %bb1, label %bb2 + +bb1: + %7 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* %3, i32 %5) + ; CHECK: call void @llvm.cj.assign.generic(i8 addrspace(1)* %7, i8 addrspace(1)* %this, i8* %3) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %7, i8 addrspace(1)* %6, i8* %3) + br label %bb3 + +bb2: + %8 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* %3, i32 %5) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %8, i8 addrspace(1)* %0, i8* %3) + br label %bb3 + +bb3: + ; CHECK: %9 = phi i8 addrspace(1)* [ %this, %bb1 ], [ %0, %bb2 ] + ; CHECK: call void @llvm.cj.assign.generic(i8 addrspace(1)* %1, i8 addrspace(1)* %9, i8* %3) + %9 = phi i8 addrspace(1)* [%7, %bb1], [%8, %bb2] + call void @llvm.cj.assign.generic(i8 addrspace(1)* %1, i8 addrspace(1)* %9, i8* %3) + ret void +} + +define void @test_func_3(i8 addrspace(1)* noalias %this, i8 addrspace(1)* noalias %0, i8 addrspace(1)* noalias %1, %fake_typeinfo* %ti, i1 %cond) { + %3 = bitcast %fake_typeinfo* %ti to i8* + %ti.size = getelementptr inbounds i8, i8* %3, i64 12 + %4 = bitcast i8* %ti.size to i32* + %5 = load i32, i32* %4, align 4 + %6 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* %3, i32 %5) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %6, i8 addrspace(1)* %this, i8* %3) + br i1 %cond, label %bb1, label %bb2 + +bb1: + %7 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* %3, i32 %5) + ; CHECK: call void @llvm.cj.assign.generic(i8 addrspace(1)* %7, i8 addrspace(1)* %this, i8* %3) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %7, i8 addrspace(1)* %6, i8* %3) + call void @clobber(i8 addrspace(1)* %7) + br label %bb3 + +bb2: + %8 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* %3, i32 %5) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %8, i8 addrspace(1)* %0, i8* %3) + call void @clobber(i8 addrspace(1)* %0) + br label %bb3 + +bb3: + ; CHECK: %9 = phi i8 addrspace(1)* [ %7, %bb1 ], [ %8, %bb2 ] + ; CHECK-NEXT: call void @llvm.cj.assign.generic(i8 addrspace(1)* %1, i8 addrspace(1)* %9, i8* %3) + %9 = phi i8 addrspace(1)* [%7, %bb1], [%8, %bb2] + call void @llvm.cj.assign.generic(i8 addrspace(1)* %1, i8 addrspace(1)* %9, i8* %3) + ret void +} + +define void @test_func_4(i8 addrspace(1)* noalias %this, i8 addrspace(1)* noalias %0, i8 addrspace(1)* noalias %1, %fake_typeinfo* %ti, i1 %cond) { + %3 = bitcast %fake_typeinfo* %ti to i8* + %ti.size = getelementptr inbounds i8, i8* %3, i64 12 + %4 = bitcast i8* %ti.size to i32* + %5 = load i32, i32* %4, align 4 + %6 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* %3, i32 %5) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %6, i8 addrspace(1)* %this, i8* %3) + br i1 %cond, label %bb1, label %bb2 + +bb1: + %7 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* %3, i32 %5) + ; CHECK: call void @llvm.cj.assign.generic(i8 addrspace(1)* %7, i8 addrspace(1)* %this, i8* %3) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %7, i8 addrspace(1)* %6, i8* %3) + call void @clobber(i8 addrspace(1)* %7) + br label %bb3 + +bb2: + %8 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* %3, i32 %5) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %8, i8 addrspace(1)* %0, i8* %3) + br label %bb3 + +bb3: + ; CHECK: %9 = phi i8 addrspace(1)* [ %7, %bb1 ], [ %0, %bb2 ] + ; CHECK: call void @llvm.cj.assign.generic(i8 addrspace(1)* %1, i8 addrspace(1)* %9, i8* %3) + %9 = phi i8 addrspace(1)* [%7, %bb1], [%8, %bb2] + call void @llvm.cj.assign.generic(i8 addrspace(1)* %1, i8 addrspace(1)* %9, i8* %3) + ret void +} + +define void @test_func_5(i8 addrspace(1)* noalias %this, i8 addrspace(1)* noalias %0, i8 addrspace(1)* noalias %1, %fake_typeinfo* %ti1, %fake_typeinfo* %ti2, i1 %cond1, i1 %cond2) { + %3 = bitcast %fake_typeinfo* %ti1 to i8* + %ti.size = getelementptr inbounds i8, i8* %3, i64 12 + %4 = bitcast i8* %ti.size to i32* + %5 = load i32, i32* %4, align 4 + %6 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* %3, i32 %5) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %6, i8 addrspace(1)* %this, i8* %3) + br i1 %cond1, label %bb1, label %bb2 + +bb1: + ; CHECK: call void @llvm.cj.assign.generic(i8 addrspace(1)* %1, i8 addrspace(1)* %this, i8* %3) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %1, i8 addrspace(1)* %6, i8* %3) + call void @clobber(i8 addrspace(1)* %1) + br label %bb3 + +bb2: + call void @llvm.cj.assign.generic(i8 addrspace(1)* %1, i8 addrspace(1)* %0, i8* %3) + br label %bb3 + +bb3: + ; CHECK: [[PHI:%.*]] = phi i8 addrspace(1)* [ %0, %bb2 ], [ %1, %bb1 ] + ; CHECK: call void @llvm.cj.assign.generic(i8 addrspace(1)* [[T0:%.*]], i8 addrspace(1)* [[PHI]], i8* %3) + %7 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* %3, i32 %5) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %7, i8 addrspace(1)* %1, i8* nonnull %3) + br i1 %cond2, label %bb4, label %common.ret + +bb4: + %8 = bitcast %fake_typeinfo* %ti2 to i8* + %ti.size2 = getelementptr inbounds i8, i8* %8, i64 12 + %9 = bitcast i8* %ti.size2 to i32* + %10 = load i32, i32* %9, align 4 + %11 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* %8, i32 %10) + %12 = call i64 @get_offsets(i8* %3) + %13 = getelementptr inbounds i8, i8 addrspace(1)* %7, i64 %12 + ; CHECK: [[GEP0:%.*]] = getelementptr i8, i8 addrspace(1)* [[PHI]], i64 [[T1:%.*]] + ; CHECK-NEXT: call void @llvm.cj.gcread.generic(i8 addrspace(1)* [[T2:%.*]], i8 addrspace(1)* [[PHI]], i8 addrspace(1)* [[GEP0]], i32 [[T2:%.*]]) + call void @llvm.cj.gcread.generic(i8 addrspace(1)* %11, i8 addrspace(1)* %7, i8 addrspace(1)* %13, i32 %10) + %14 = bitcast i8 addrspace(1)* %this to i8 addrspace(1)* addrspace(1)* + %15 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %14, align 8 + ; CHECK: [[GEP1:%.*]] = getelementptr i8, i8 addrspace(1)* [[PHI]], i64 [[T1]] + ; CHECK-NEXT: call void @llvm.cj.gcread.generic(i8 addrspace(1)* [[T3:%.*]], i8 addrspace(1)* [[PHI]], i8 addrspace(1)* [[GEP1]], i32 [[T2]]) + call void @llvm.cj.assign.generic(i8 addrspace(1)* %15, i8 addrspace(1)* %11, i8* %8) + br label %common.ret + +common.ret: + ret void +} + +declare void @clobber(i8 addrspace(1)*) +declare i64 @get_offsets(i8*) #1 +declare void @llvm.cj.assign.generic(i8 addrspace(1)* noalias nocapture writeonly %0, i8 addrspace(1)* noalias nocapture readonly %1, i8* noalias nocapture readonly %2) #0 +declare void @llvm.cj.gcread.generic(i8 addrspace(1)* noalias nocapture writeonly %0, i8 addrspace(1)* nocapture readonly %1, i8 addrspace(1)* nocapture readonly %2, i32 %3) #0 +declare void @llvm.cj.gcwrite.generic(i8 addrspace(1)* nocapture %0, i8 addrspace(1)* nocapture writeonly %1, i8 addrspace(1)* noalias nocapture readonly %2, i32 %3) #0 +declare noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) #2 +declare i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* nocapture, i8 addrspace(1)* addrspace(1)* nocapture) #1 + +attributes #0 = { argmemonly mustprogress nounwind willreturn } +attributes #1 = { argmemonly nounwind readonly willreturn } +attributes #2 = { nounwind "cj-heapmalloc" "cj-runtime" } diff --git a/llvm/test/Transforms/CJIRVerifier/callsite_debuginfo.ll b/llvm/test/Transforms/CJIRVerifier/callsite_debuginfo.ll new file mode 100644 index 000000000..1913b3bfc --- /dev/null +++ b/llvm/test/Transforms/CJIRVerifier/callsite_debuginfo.ll @@ -0,0 +1,41 @@ +; RUN: not not opt -passes=cj-ir-verifier < %s -disable-output 2>&1 | FileCheck %s -check-prefixes=CHECK,ABORT + +%record = type { i64, i8 addrspace(1)* } + +; CHECK: The callsite debug info must have a !dbg location. +; CHECK-NEXT: call void @method(%record* noalias sret(%record) %callRet, i32 0, i64 1) +; CHECK-NEXT: in function foo + +define void @foo() gc "cangjie" !dbg !31 { +entry: + %callRet = alloca %record, align 8 + %0 = bitcast %record* %callRet to i8* + call void @llvm.cj.memset(i8* align 8 %0, i8 0, i64 16, i1 false) + call void @method(%record* noalias sret(%record) %callRet, i32 0, i64 1) + ret void +} + +; ABORT: LLVM ERROR: Broken function found, compilation aborted +; ABORT: error: Aborted + +declare void @llvm.cj.memset(i8*, i8, i64, i1) #10 + +define void @method(%record* noalias sret(%record), i32 %0, i64 %1) gc "cangjie" { +entry: + %3 = alloca i64, align 8 + ret void +} + +!llvm.module.flags = !{!4, !5} +!llvm.dbg.cu = !{!6} + +!1 = !{} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 2, !"Dwarf Version", i32 4} +!6 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !7, producer: "Cangjie Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !1) +!7 = !DIFile(filename: "default", directory: "/") +!9 = !DIFile(filename: "", directory: ".") +!10 = !DINamespace(name: "default", scope: null) +!11 = !DISubroutineType(types: !1) +!17 = !DIFile(filename: "callsite_debuginfo.ll", directory: "/") +!31 = distinct !DISubprogram(scope: !10, file: !17, line: 8, type: !11, scopeLine: 8, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !6, retainedNodes: !1) diff --git a/llvm/test/Transforms/CJIRVerifier/cj_intrinsic.ll b/llvm/test/Transforms/CJIRVerifier/cj_intrinsic.ll new file mode 100644 index 000000000..690407a1f --- /dev/null +++ b/llvm/test/Transforms/CJIRVerifier/cj_intrinsic.ll @@ -0,0 +1,25 @@ +; RUN: not not opt -passes=cj-ir-verifier < %s -disable-output 2>&1 | FileCheck %s -check-prefixes=CHECK,ABORT + +%record = type { i64, i8 addrspace(1)* } + +; CHECK: gcwrite/gcread.static.struct has no Metadata. +; CHECK-NEXT: call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%record* @record_global0 to i8*), i8* %0, i64 16) +; CHECK-NEXT: in function foo + +@"record_global0" = internal global %record zeroinitializer + +define void @foo() gc "cangjie" { +entry: + %ret0 = alloca %record, align 8 + %0 = bitcast %record* %ret0 to i8* + call void @llvm.cj.memset(i8* align 8 %0, i8 0, i64 16, i1 false) + call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%record* @"record_global0" to i8*), i8* %0, i64 16) + ret void +} + +; ABORT: LLVM ERROR: Broken function found, compilation aborted +; ABORT: error: Aborted + +declare void @llvm.cj.memset(i8*, i8, i64, i1) #10 + +declare void @llvm.cj.gcwrite.static.struct(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64) #9 diff --git a/llvm/test/Transforms/CJIRVerifier/struct_addrspacecast.ll b/llvm/test/Transforms/CJIRVerifier/struct_addrspacecast.ll new file mode 100644 index 000000000..ca4bfe091 --- /dev/null +++ b/llvm/test/Transforms/CJIRVerifier/struct_addrspacecast.ll @@ -0,0 +1,19 @@ +; RUN: not not opt -passes=cj-ir-verifier < %s -disable-output 2>&1 | FileCheck %s -check-prefixes=CHECK,ABORT + +%record = type { i64, i8 addrspace(1)* } + +; CHECK: Addrspacecast result can only be used for store or call. +; CHECK-NEXT: %0 = addrspacecast %record* %callRet to %record addrspace(1)* +; CHECK-NEXT: in function foo + +define void @foo(%record* noalias sret(%record) %callRet) #0 gc "cangjie" { +entry: + %0 = addrspacecast %record* %callRet to %record addrspace(1)* + %1 = bitcast %record addrspace(1)* %0 to i8 addrspace(1)* + ret void +} + +; ABORT: LLVM ERROR: Broken function found, compilation aborted +; ABORT: error: Aborted + +attributes #0 = { "hasRcdParam" } diff --git a/llvm/test/Transforms/CJIRVerifier/struct_alloca1.ll b/llvm/test/Transforms/CJIRVerifier/struct_alloca1.ll new file mode 100644 index 000000000..b027c72ca --- /dev/null +++ b/llvm/test/Transforms/CJIRVerifier/struct_alloca1.ll @@ -0,0 +1,21 @@ +; RUN: not not opt -passes=cj-ir-verifier < %s -disable-output 2>&1 | FileCheck %s -check-prefixes=CHECK,ABORT + +%record = type { i64, i8 addrspace(1)* } + +; CHECK: Alloca struct* should only be used for dbg.declare +; CHECK-NEXT: %ret0 = alloca %record*, align 8 +; CHECK-NEXT: in function foo + +define void @foo() #0 gc "cangjie" { +entry: + %ret0 = alloca %record*, align 8 + %0 = bitcast %record** %ret0 to i8** + %ret1 = alloca %record**, align 8 + %1 = bitcast %record*** %ret1 to i8*** + ret void +} + +; ABORT: LLVM ERROR: Broken function found, compilation aborted +; ABORT: error: Aborted + +attributes #0 = { "hasRcdParam" } diff --git a/llvm/test/Transforms/CJIRVerifier/struct_alloca2.ll b/llvm/test/Transforms/CJIRVerifier/struct_alloca2.ll new file mode 100644 index 000000000..c46cd459b --- /dev/null +++ b/llvm/test/Transforms/CJIRVerifier/struct_alloca2.ll @@ -0,0 +1,23 @@ +; RUN: not not opt -passes=cj-ir-verifier < %s -disable-output 2>&1 | FileCheck %s -check-prefixes=CHECK,ABORT + +%record = type { i64, i8 addrspace(1)* } + +; CHECK: Alloca %struct addrspace(1)* is not allowed. +; CHECK-NEXT: %temp0 = alloca %record addrspace(1)*, align 8 +; CHECK-NEXT: Alloca addrspace(1)** is not allowed. +; CHECK-NEXT: %temp1 = alloca %record addrspace(1)**, align 8 +; CHECK-NEXT: in function foo1 + +define void @foo1(i8 addrspace(1)* %bp, %record addrspace(1)* %arg0) #0 gc "cangjie" { +entry: + %temp0 = alloca %record addrspace(1)* + store %record addrspace(1)* %arg0, %record addrspace(1)** %temp0 + %temp1 = alloca %record addrspace(1)** + store %record addrspace(1)** %temp0, %record addrspace(1)*** %temp1 + ret void +} + +; ABORT: LLVM ERROR: Broken function found, compilation aborted +; ABORT: error: Aborted + +attributes #0 = { "hasRcdParam" } diff --git a/llvm/test/Transforms/CJIRVerifier/struct_alloca3.ll b/llvm/test/Transforms/CJIRVerifier/struct_alloca3.ll new file mode 100644 index 000000000..7f845b983 --- /dev/null +++ b/llvm/test/Transforms/CJIRVerifier/struct_alloca3.ll @@ -0,0 +1,22 @@ +; RUN: not not opt -passes=cj-ir-verifier < %s -disable-output 2>&1 | FileCheck %s -check-prefixes=CHECK,ABORT + +%record = type { i64, i8 addrspace(1)* } + +; CHECK: Missing cj.memset in allocation of structure. +; CHECK-NEXT: %ret0 = alloca %record, align 8 +; CHECK-NEXT: Bitcast struct* can only be callsite inst. +; CHECK-NEXT: %0 = bitcast %record* %ret0 to i8* +; CHECK-NEXT: in function foo2 + +define void @foo2() #0 gc "cangjie" { +entry: + %ret0 = alloca %record, align 8 + %0 = bitcast %record* %ret0 to i8* + store i8 20, i8* %0, align 8 + ret void +} + +; ABORT: LLVM ERROR: Broken function found, compilation aborted +; ABORT: error: Aborted + +attributes #0 = { "hasRcdParam" } diff --git a/llvm/test/Transforms/CJIRVerifier/struct_funtion1.ll b/llvm/test/Transforms/CJIRVerifier/struct_funtion1.ll new file mode 100644 index 000000000..a3cb93175 --- /dev/null +++ b/llvm/test/Transforms/CJIRVerifier/struct_funtion1.ll @@ -0,0 +1,18 @@ +; RUN: not not opt -passes=cj-ir-verifier < %s -disable-output 2>&1 | FileCheck %s -check-prefixes=CHECK,ABORT + +%record = type { i64, i8 addrspace(1)* } + +; CHECK: sret is not on the first parameter! +; CHECK-NEXT: %record* %callRet +; CHECK-NEXT: in function foo + +define void @foo(i8 addrspace(1)* %ref, %record* noalias sret(%record) %callRet) #0 gc "cangjie" { +entry: + %retVal = alloca i64, align 8 + ret void +} + +; ABORT: LLVM ERROR: Broken function found, compilation aborted +; ABORT: error: Aborted + +attributes #0 = { "hasRcdParam" } diff --git a/llvm/test/Transforms/CJIRVerifier/struct_funtion2.ll b/llvm/test/Transforms/CJIRVerifier/struct_funtion2.ll new file mode 100644 index 000000000..5d243b966 --- /dev/null +++ b/llvm/test/Transforms/CJIRVerifier/struct_funtion2.ll @@ -0,0 +1,22 @@ +; RUN: not not opt -passes=cj-ir-verifier < %s -disable-output 2>&1 | FileCheck %s -check-prefixes=CHECK,ABORT + +%record = type { i64, i8 addrspace(1)* } + +; CHECK: The gc pointer parameter should be i8 addrspace(1)* +; CHECK-NEXT: %record addrspace(1)* %arg0 +; CHECK-NEXT: in function foo1 + +define void @foo1(%record addrspace(1)* %arg0) #0 gc "cangjie" { +entry: + %ret0 = alloca %record, align 8 + %0 = bitcast %record* %ret0 to i8* + call void @llvm.cj.memset(i8* align 8 %0, i8 0, i64 16, i1 false) + ret void +} + +; ABORT: LLVM ERROR: Broken function found, compilation aborted +; ABORT: error: Aborted + +declare void @llvm.cj.memset(i8*, i8, i64, i1) #10 + +attributes #0 = { "hasRcdParam" } diff --git a/llvm/test/Transforms/CJIRVerifier/struct_funtion3.ll b/llvm/test/Transforms/CJIRVerifier/struct_funtion3.ll new file mode 100644 index 000000000..9650ea74f --- /dev/null +++ b/llvm/test/Transforms/CJIRVerifier/struct_funtion3.ll @@ -0,0 +1,20 @@ +; RUN: not not opt -passes=cj-ir-verifier < %s -disable-output 2>&1 | FileCheck %s -check-prefixes=CHECK,ABORT + +%record = type { i64, i8 addrspace(1)* } + +; CHECK: The gc pointer parameter should be i8 addrspace(1)* +; CHECK-NEXT: %record addrspace(1)* %arg0 +; CHECK-NEXT: Missing cj.memset in allocation of structure. +; CHECK-NEXT: %ret0 = alloca %record, align 8 +; CHECK-NEXT: in function foo2 + +define void @foo2(%record addrspace(1)* %arg0, i8 addrspace(1)* %bp) gc "cangjie" { +entry: + %ret0 = alloca %record, align 8 + ret void +} + +; ABORT: LLVM ERROR: Broken function found, compilation aborted +; ABORT: error: Aborted + +declare void @llvm.cj.memset(i8*, i8, i64, i1) diff --git a/llvm/test/Transforms/CJInlineCache/icall.ll b/llvm/test/Transforms/CJInlineCache/icall.ll new file mode 100644 index 000000000..748681eb8 --- /dev/null +++ b/llvm/test/Transforms/CJInlineCache/icall.ll @@ -0,0 +1,44 @@ +; RUN: opt -S --iv-call-inline-cache-enable=true -passes='cj-runtime-lowering' --cj-ic-input-file='%S/inputfile/ICallProfileOutput.txt' < %s | FileCheck %s +; XFAIL: * + +; ModuleID = 'icall.bc' +source_filename = "0-default" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%KlassInfo.1 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i8*, i64*, i32, [1 x %KlassInfo.0*] } +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i8*, i64*, i32, [0 x %KlassInfo.0*] } +; Function Attrs: nounwind +declare i8* @llvm.cj.get.interface.func(i8 addrspace(1)*, i32, i32, i8*) +define void @_ZN7default1B8functionEv(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %this){ ret void } +%Unit.Type = type { i8 } +@"$const_string.8btabV56joC" = private unnamed_addr constant [11 x i8] c"function$0\00", align 1 +%ObjLayout._ZN7default1BE = type { %ObjLayout._ZN8std.core6ObjectE } +%ObjLayout._ZN8std.core6ObjectE = type { %ObjLayout.Object } +%ObjLayout.Object = type { %KlassInfo.1* } +@_ZN7default1BE.itable = internal global { [23 x i8*], i32* } { [23 x i8*] [i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* bitcast (void (%Unit.Type*, i8 addrspace(1)*)* @_ZN7default1B8functionEv to i8*), i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null], i32* null } #2 +@C_ZN7default1BE.typeName = internal global [16 x i8] c"C_ZN7default1BE\00", align 1 +@_ZN7default1BE.objKlass = weak_odr global %KlassInfo.1 { i32 0, i32 0, %BitMap* null, %KlassInfo.0* bitcast (%KlassInfo.1* @_ZN8std.core6ObjectE.objKlass to %KlassInfo.0*), i8** null, i64* bitcast ({ [23 x i8*], i32* }* @_ZN7default1BE.itable to i64*), i64* null, i8* null, i64* bitcast ([16 x i8]* @C_ZN7default1BE.typeName to i64*), i32 1, [1 x %KlassInfo.0*] [%KlassInfo.0* @_ZN7default1AE.objKlass] } +%ObjLayout._ZN7default1AE = type { %ObjLayout.Object } +@C_ZN7default1AE.typeName = internal global [16 x i8] c"C_ZN7default1AE\00", align 1 +@_ZN7default1AE.objKlass = weak_odr global %KlassInfo.0 { i32 0, i32 0, %BitMap* null, %KlassInfo.0* null, i8** null, i64* null, i64* null, i8* null, i64* bitcast ([16 x i8]* @C_ZN7default1AE.typeName to i64*), i32 0, [0 x %KlassInfo.0*] zeroinitializer } +@_ZN8std.core6ObjectE.objKlass = external global %KlassInfo.1 #0 + +define void @Test(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %a) gc "cangjie" { +; CHECK: %2 = call i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* %a) +; CHECK: br i1 %4, label %if.true.call_direct, label %if.false.call_virtual +; CHECK: if.true.call_direct: +; CHECK: call void @_ZN7default1B8functionEv(%Unit.Type* noalias sret(%Unit.Type) %1, i8 addrspace(1)* %a) +; CHECK: br label %if.end.ic +; CHECK: if.false.call_virtual: +; CHECK: %5 = getelementptr i8, i8* %2, i64 32 +; CHECK: %8 = call i8* @CJ_MCC_GetFuncPtrFromItab(i64* %7, i32 7, i32 3223, i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"$const_string.8btabV56joC", i32 0, i32 0)) +; CHECK: if.end.ic: +bb0: + %1 = alloca %Unit.Type, align 8 + %2 = call i8* @llvm.cj.get.interface.func(i8 addrspace(1)* noalias %a, i32 7, i32 3223, i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"$const_string.8btabV56joC", i32 0, i32 0)) + %3 = bitcast i8* %2 to void (%Unit.Type*, i8 addrspace(1)*)* + call void %3(%Unit.Type* noalias sret(%Unit.Type) %1, i8 addrspace(1)* %a) + ret void +} diff --git a/llvm/test/Transforms/CJInlineCache/icall_instr.ll b/llvm/test/Transforms/CJInlineCache/icall_instr.ll new file mode 100644 index 000000000..8e6dc28f3 --- /dev/null +++ b/llvm/test/Transforms/CJInlineCache/icall_instr.ll @@ -0,0 +1,37 @@ +; RUN: opt -S --iv-call-instrument-enable=true -passes='cj-runtime-lowering' < %s | FileCheck %s +; XFAIL: * + +; ModuleID = 'icall.bc' +source_filename = "0-default" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%KlassInfo.1 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i8*, i64*, i32, [1 x %KlassInfo.0*] } +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i8*, i64*, i32, [0 x %KlassInfo.0*] } +; Function Attrs: nounwind +declare i8* @llvm.cj.get.interface.func(i8 addrspace(1)*, i32, i32, i8*) +define void @FuncB(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %this){ ret void } + +%Unit.Type = type { i8 } +@"$const_string.8btabV56joC" = private unnamed_addr constant [11 x i8] c"function$0\00", align 1 +%ObjLayout.B = type { %ObjLayout._ZN8std.core6ObjectE } +%ObjLayout._ZN8std.core6ObjectE = type { %ObjLayout.Object } +%ObjLayout.Object = type { %KlassInfo.1* } +@B.itable = internal global { [23 x i8*], i32* } { [23 x i8*] [i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* bitcast (void (%Unit.Type*, i8 addrspace(1)*)* @FuncB to i8*), i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null, i8* null], i32* null } #2 +@B.typeName = internal global [2 x i8] c"B\00", align 1 +@B.objKlass = weak_odr global %KlassInfo.1 { i32 0, i32 0, %BitMap* null, %KlassInfo.0* bitcast (%KlassInfo.1* @_ZN8std.core6ObjectE.objKlass to %KlassInfo.0*), i8** null, i64* bitcast ({ [23 x i8*], i32* }* @B.itable to i64*), i64* null, i8* null, i64* bitcast ([2 x i8]* @B.typeName to i64*), i32 1, [1 x %KlassInfo.0*] [%KlassInfo.0* @A.objKlass] } +%ObjLayout.A = type { %ObjLayout.Object } +@A.typeName = internal global [2 x i8] c"A\00", align 1 +@A.objKlass = weak_odr global %KlassInfo.0 { i32 0, i32 0, %BitMap* null, %KlassInfo.0* null, i8** null, i64* null, i64* null, i8* null, i64* bitcast ([2 x i8]* @A.typeName to i64*), i32 0, [0 x %KlassInfo.0*] zeroinitializer } +@_ZN8std.core6ObjectE.objKlass = external global %KlassInfo.1 #0 + +define void @Test(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %a) gc "cangjie" { +; CHECK: call void @CJ_MCC_IVCallInstrumentation(i8* %2, i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"Test:bb0:0", i32 0, i32 0)) +bb0: + %1 = alloca %Unit.Type, align 8 + %2 = call i8* @llvm.cj.get.interface.func(i8 addrspace(1)* noalias %a, i32 7, i32 3223, i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"$const_string.8btabV56joC", i32 0, i32 0)) + %3 = bitcast i8* %2 to void (%Unit.Type*, i8 addrspace(1)*)* + call void %3(%Unit.Type* noalias sret(%Unit.Type) %1, i8 addrspace(1)* %a) + ret void +} diff --git a/llvm/test/Transforms/CJInlineCache/inputfile/ICallProfileOutput.txt b/llvm/test/Transforms/CJInlineCache/inputfile/ICallProfileOutput.txt new file mode 100644 index 000000000..a346d9878 --- /dev/null +++ b/llvm/test/Transforms/CJInlineCache/inputfile/ICallProfileOutput.txt @@ -0,0 +1,3 @@ +; XFAIL: * +; RUN: opt < %s -passes=pgo-instr-use -pgo-test-profile-file=%t.profdata -S 2>&1 +Test:bb0:0 _ZN7default1BE diff --git a/llvm/test/Transforms/CJInlineCache/inputfile/VCallProfileOutput.txt b/llvm/test/Transforms/CJInlineCache/inputfile/VCallProfileOutput.txt new file mode 100644 index 000000000..70e119cf3 --- /dev/null +++ b/llvm/test/Transforms/CJInlineCache/inputfile/VCallProfileOutput.txt @@ -0,0 +1,3 @@ +; XFAIL: * +; RUN: opt < %s -passes=pgo-instr-use -pgo-test-profile-file=%t.profdata -S 2>&1 +Test:bb0:0 B diff --git a/llvm/test/Transforms/CJInlineCache/vcall.ll b/llvm/test/Transforms/CJInlineCache/vcall.ll new file mode 100644 index 000000000..f77136ce6 --- /dev/null +++ b/llvm/test/Transforms/CJInlineCache/vcall.ll @@ -0,0 +1,51 @@ +; RUN: opt -S --iv-call-inline-cache-enable=true -passes='cj-runtime-lowering' --cj-ic-input-file='%S/inputfile/VCallProfileOutput.txt' < %s | FileCheck %s +; XFAIL: * + +; ModuleID = 'dyncall.bc' +source_filename = "0-default" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%KlassInfo.1 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i8*, i64*, i32, [1 x %KlassInfo.0*] } +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i8*, i64*, i32, [0 x %KlassInfo.0*] } +%Unit.Type = type { i8 } +%ObjLayout.A = type { %ObjLayout._ZN8std.core6ObjectE } +%ObjLayout._ZN8std.core6ObjectE = type { %ObjLayout.Object } +%ObjLayout.Object = type { %KlassInfo.1* } +%ObjLayout.B = type { %ObjLayout.A } +@A.vtable = internal global [2 x i8*] [i8* null, i8* bitcast (void (%Unit.Type*, i8 addrspace(1)*)* @FuncA to i8*)] #1 +@A.typeName = internal global [2 x i8] c"A\00", align 1 +@A.objKlass = weak_odr global %KlassInfo.0 { i32 0, i32 0, %BitMap* null, %KlassInfo.0* bitcast (%KlassInfo.1* @_ZN8std.core6ObjectE.objKlass to %KlassInfo.0*), i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @A.vtable, i32 0, i32 0), i64* null, i64* null, i8* null, i64* bitcast ([2 x i8]* @A.typeName to i64*), i32 0, [0 x %KlassInfo.0*] zeroinitializer } +@B.vtable = internal global [2 x i8*] [i8* null, i8* bitcast (void (%Unit.Type*, i8 addrspace(1)*)* @FuncB to i8*)] #1 +@B.typeName = internal global [2 x i8] c"B\00", align 1 +@B.objKlass = weak_odr global %KlassInfo.0 { i32 0, i32 0, %BitMap* null, %KlassInfo.0* @A.objKlass, i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @B.vtable, i32 0, i32 0), i64* null, i64* null, i8* null, i64* bitcast ([2 x i8]* @B.typeName to i64*), i32 0, [0 x %KlassInfo.0*] zeroinitializer } +@_ZN8std.core6ObjectE.objKlass = external global %KlassInfo.1 #0 + +; Function Attrs: nounwind +declare i8* @llvm.cj.get.virtual.func(i8 addrspace(1)*, i64) #6 + +define void @FuncA(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %this) gc "cangjie" { + ret void +} + +define void @FuncB(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %this) gc "cangjie" { + ret void +} + +define void @Test(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %a) gc "cangjie" { +; CHECK: %2 = call i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* %a) +; CHECK: br i1 %4, label %if.true.call_direct, label %if.false.call_virtual +; CHECK: if.true.call_direct: +; CHECK: call void @FuncB(%Unit.Type* noalias sret(%Unit.Type) %1, i8 addrspace(1)* %a) +; CHECK: br label %if.end.ic +; CHECK: if.false.call_virtual: +; CHECK: call void %10(%Unit.Type* noalias sret(%Unit.Type) %1, i8 addrspace(1)* %a) +; CHECK: if.end.ic: +bb0: + %1 = alloca %Unit.Type, align 8 + %2 = call i8* @llvm.cj.get.virtual.func(i8 addrspace(1)* noalias %a, i64 1) + %3 = bitcast i8* %2 to void (%Unit.Type*, i8 addrspace(1)*)* + call void %3(%Unit.Type* noalias sret(%Unit.Type) %1, i8 addrspace(1)* %a) + ret void +} diff --git a/llvm/test/Transforms/CJInlineCache/vcall_instr.ll b/llvm/test/Transforms/CJInlineCache/vcall_instr.ll new file mode 100644 index 000000000..2a4daa3cb --- /dev/null +++ b/llvm/test/Transforms/CJInlineCache/vcall_instr.ll @@ -0,0 +1,44 @@ +; RUN: opt -S --iv-call-instrument-enable=true -passes='cj-runtime-lowering' < %s | FileCheck %s +; XFAIL: * + +; ModuleID = 'dyncall.bc' +source_filename = "0-default" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%KlassInfo.1 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i8*, i64*, i32, [1 x %KlassInfo.0*] } +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i8*, i64*, i32, [0 x %KlassInfo.0*] } +%Unit.Type = type { i8 } +%ObjLayout.A = type { %ObjLayout._ZN8std.core6ObjectE } +%ObjLayout._ZN8std.core6ObjectE = type { %ObjLayout.Object } +%ObjLayout.Object = type { %KlassInfo.1* } +%ObjLayout.B = type { %ObjLayout.A } +@A.vtable = internal global [2 x i8*] [i8* null, i8* bitcast (void (%Unit.Type*, i8 addrspace(1)*)* @TestA to i8*)] #1 +@A.typeName = internal global [2 x i8] c"A\00", align 1 +@A.objKlass = weak_odr global %KlassInfo.0 { i32 0, i32 0, %BitMap* null, %KlassInfo.0* bitcast (%KlassInfo.1* @_ZN8std.core6ObjectE.objKlass to %KlassInfo.0*), i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @A.vtable, i32 0, i32 0), i64* null, i64* null, i8* null, i64* bitcast ([2 x i8]* @A.typeName to i64*), i32 0, [0 x %KlassInfo.0*] zeroinitializer } +@B.vtable = internal global [2 x i8*] [i8* null, i8* bitcast (void (%Unit.Type*, i8 addrspace(1)*)* @TestB to i8*)] #1 +@B.typeName = internal global [2 x i8] c"B\00", align 1 +@B.objKlass = weak_odr global %KlassInfo.0 { i32 0, i32 0, %BitMap* null, %KlassInfo.0* @A.objKlass, i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @B.vtable, i32 0, i32 0), i64* null, i64* null, i8* null, i64* bitcast ([2 x i8]* @B.typeName to i64*), i32 0, [0 x %KlassInfo.0*] zeroinitializer } +@_ZN8std.core6ObjectE.objKlass = external global %KlassInfo.1 #0 + +; Function Attrs: nounwind +declare i8* @llvm.cj.get.virtual.func(i8 addrspace(1)*, i64) #6 + +define void @TestA(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %this) gc "cangjie" { + ret void +} + +define void @TestB(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %this) gc "cangjie" { + ret void +} + +define void @InstrTest(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %a) gc "cangjie" { +; CHECK: call void @CJ_MCC_IVCallInstrumentation(i8* %2, i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"InstrTest:bb0:0", i32 0, i32 0)) +bb0: + %1 = alloca %Unit.Type, align 8 + %2 = call i8* @llvm.cj.get.virtual.func(i8 addrspace(1)* noalias %a, i64 1) + %3 = bitcast i8* %2 to void (%Unit.Type*, i8 addrspace(1)*)* + call void %3(%Unit.Type* noalias sret(%Unit.Type) %1, i8 addrspace(1)* %a) + ret void +} diff --git a/llvm/test/Transforms/CJLoopFloatOpt/loop-float-opt1.ll b/llvm/test/Transforms/CJLoopFloatOpt/loop-float-opt1.ll new file mode 100644 index 000000000..51856992d --- /dev/null +++ b/llvm/test/Transforms/CJLoopFloatOpt/loop-float-opt1.ll @@ -0,0 +1,71 @@ +; RUN: opt -enable-new-pm=false -cj-loop-float-opt -S < %s | FileCheck %s +; RUN: opt -passes=cj-loop-float-opt -S < %s | FileCheck %s + + +declare void @"CJ_MCC_ThrowException"() gc "cangjie" + +declare void @"CJ_MCC_ThrowArithmeticException"() gc "cangjie" + +declare i64 @llvm.cj.get.fp.state.i64(i64) + +define void @"foo"() gc "cangjie" { +bb6: + br label %bb11 +; CHECK: %.0612 = phi i64 [ 0, %bb6 ], [ %and1, %divEnd ] +; CHECK: br label %normal1 +bb11: ; preds = %bb6, %divEnd + %f3.013 = phi double [ 1.000000e+00, %bb6 ], [ %f3.1, %divEnd ] + %.0612 = phi i64 [ 0, %bb6 ], [ %and1, %divEnd ] + %and1 = add nuw nsw i64 %.0612, 1 + %fptosi1 = fptosi double %f3.013 to i64 + %fpstate1 = tail call i64 @llvm.cj.get.fp.state.i64(i64 %fptosi1) + %and2 = and i64 %fpstate1, 1 + %cmp1 = icmp eq i64 %and2, 0 + br i1 %cmp1, label %normal1, label %fp.convert.exception + +fp.convert.exception: ; preds = %bb11 + %bc1 = bitcast double %f3.013 to i64 + %and3 = and i64 %bc1, 9218868437227405312 + %notInfOrNan.not = icmp eq i64 %and3, 9218868437227405312 + br i1 %notInfOrNan.not, label %inf.or.nan, label %not.inf.nan + +not.inf.nan: ; preds = %fp.convert.exception + %f2i.lt.min = fcmp ogt double %f3.013, 0xC3E0000000000001 + br i1 %f2i.lt.min, label %lower.bound.ok, label %lower.bound.overflow + +lower.bound.ok: ; preds = %not.inf.nan + %f2i.gt.max = fcmp olt double %f3.013, 0x43E0000000000000 + br i1 %f2i.gt.max, label %normal1, label %upper.bound.overflow + +upper.bound.overflow: ; preds = %lower.bound.ok + tail call void @CJ_MCC_ThrowException() + unreachable + +lower.bound.overflow: ; preds = %not.inf.nan + tail call void @CJ_MCC_ThrowException() + unreachable + +inf.or.nan: ; preds = %fp.convert.exception + tail call void @CJ_MCC_ThrowException() + unreachable + +normal1: ; preds = %bb11, %lower.bound.ok + %divisor_is_0 = icmp eq i64 %fptosi1, 0 + br i1 %divisor_is_0, label %div.divisorIs0, label %divEnd + +div.divisorIs0: ; preds = %normal1 + tail call void @CJ_MCC_ThrowArithmeticException() + unreachable + +; CHECK: %1 = select i1 %icmpeq, i64 1, i64 2 +; CHECK-NEXT: add i64 %0, %1 +divEnd: ; preds = %normal1 + %icmpeq = icmp eq i64 %fptosi1, 0 + %f3.1.v = select i1 %icmpeq, double 1.000000e+00, double 2.000000e+00 + %f3.1 = fadd double %f3.013, %f3.1.v + %exitcond.not = icmp eq i64 %and1, 3000000 + br i1 %exitcond.not, label %bb10, label %bb11 + +bb10: ; preds = %divEnd + ret void +} diff --git a/llvm/test/Transforms/CJLoopFloatOpt/loop-float-opt2.ll b/llvm/test/Transforms/CJLoopFloatOpt/loop-float-opt2.ll new file mode 100644 index 000000000..6cdbbd745 --- /dev/null +++ b/llvm/test/Transforms/CJLoopFloatOpt/loop-float-opt2.ll @@ -0,0 +1,25 @@ +; RUN: opt -cj-loop-float-opt -S < %s | FileCheck %s +; RUN: opt -passes=cj-loop-float-opt -S < %s | FileCheck %s + +declare void @"_ZN8std$core7printlnEd"(double) gc "cangjie" + +define void @"foo"() gc "cangjie" { +bb59: + br label %bb64 +bb64: ; preds = %bb59, %bb64 + %0 = phi i64 [ 0, %bb59 ], [ %1, %bb64 ] + %f3.04 = phi double [ 1.000000e+00, %bb59 ], [ %f3.1, %bb64 ] + %1 = add nuw nsw i64 %0, 1 + %and5 = and i64 %0, 1 + %icmpeq = icmp eq i64 %and5, 0 + %2 = fmul double %f3.04, 2.000000e+00 + %f3.0.pn = select i1 %icmpeq, double %f3.04, double %2 + %f3.1 = fadd double %f3.04, %f3.0.pn + %exitcond.not = icmp eq i64 %1, 3000000 + br i1 %exitcond.not, label %bb63, label %bb64 + +bb63: ; preds = %bb64 + ; CHECK: call void @"_ZN8std$core7printlnEd"(double 0x7FF0000000000000) + call void @"_ZN8std$core7printlnEd"(double %f3.1) + ret void +} \ No newline at end of file diff --git a/llvm/test/Transforms/CJPartialEscapeAnalysis/add-param-store-escape.ll b/llvm/test/Transforms/CJPartialEscapeAnalysis/add-param-store-escape.ll new file mode 100644 index 000000000..331692d34 --- /dev/null +++ b/llvm/test/Transforms/CJPartialEscapeAnalysis/add-param-store-escape.ll @@ -0,0 +1,42 @@ +; RUN: opt '-passes=cj-pea' -S < %s | FileCheck %s + +%Unit.Type = type { i8 } + +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) +declare void @llvm.cj.gcwrite.ref(i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*) + +%BitMap = type { i32, [0 x i8] } +%ObjLayout.Object = type { %TypeInfo* } +%"ObjLayout._ZN11std$FS$core6ObjectE" = type { %ObjLayout.Object } +%"record._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" = type { i8 addrspace(1)*, i64, i64 } +%ObjLayout._ZN7default5HumanE = type { i8 addrspace(1)* } +%TypeInfo = type { i8*, i8, i8, i16, i32, %BitMap*, i32, i8, i8, i32*, i8*, i8*, i8*, %TypeInfo*, i8*, i8* } + +@"std.core$Object.ti" = external global %TypeInfo +@_ZN7default5HumanE.ti = weak_odr global %TypeInfo {i8* getelementptr inbounds ([14 x i8], [14 x i8]* @"default$Human.name", i32 0, i32 0), i8 -128, i8 0, i16 2, i32 16, %BitMap* null, i32 0, i8 8, i8 0, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @"default$Human.ti.offsets", i32 0, i32 0), i8* null, i8* null, i8* bitcast ([1 x %TypeInfo*]* @"default$Human.ti.fields" to i8*), %TypeInfo* @"std.core$Object.ti", i8* null, i8* bitcast ([1 x i8*]* @"default$Human.fieldNames" to i8*)}, !RelatedType !0 +@"default$Human.name" = internal global [14 x i8] c"default$Human\00", align 1 +@"default$Human.ti.offsets" = internal global [1 x i32] [i32 0] +@"default$Human.ti.fields" = internal global [1 x %TypeInfo*] [%TypeInfo* @"std.core$Object.ti"] +@"default$Human.fieldNames" = internal global [1 x i8*] [i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"default$Human.field.1.name", i32 0, i32 0)] +@"default$Human.field.1.name" = internal global [3 x i8] c"aa\00", align 1 + +define void @_ZN7default5Human12reproductionEC_ZN7default6PlanetEC_ZN7default6PlanetE(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %this, i8 addrspace(1)* %s, i8 addrspace(1)* %ns) gc "cangjie" { +; CHECK-LABEL: @_ZN7default5Human12reproductionEC_ZN7default6PlanetEC_ZN7default6PlanetE( +; CHECK-LABEL: entry +; CHECK-NEXT: %1 = alloca { %TypeInfo*, %ObjLayout._ZN7default5HumanE }, align 8 +; CHECK-NOT: %2 = alloca { %TypeInfo*, %ObjLayout._ZN7default5HumanE }, align 8 + +entry: + %1 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 16) + %2 = getelementptr inbounds i8, i8 addrspace(1)* %1, i64 8 + %3 = bitcast i8 addrspace(1)* %2 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %s, i8 addrspace(1)* %1, i8 addrspace(1)* addrspace(1)* %3) + %4 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %3 + %5 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 16) + %6 = getelementptr inbounds i8, i8 addrspace(1)* %4, i64 8 + %7 = bitcast i8 addrspace(1)* %6 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %5, i8 addrspace(1)* %4, i8 addrspace(1)* addrspace(1)* %7) + ret void +} + +!0 = !{!"ObjLayout._ZN7default5HumanE"} diff --git a/llvm/test/Transforms/CJPartialEscapeAnalysis/partial-info-escape.ll b/llvm/test/Transforms/CJPartialEscapeAnalysis/partial-info-escape.ll new file mode 100644 index 000000000..ce0784d0c --- /dev/null +++ b/llvm/test/Transforms/CJPartialEscapeAnalysis/partial-info-escape.ll @@ -0,0 +1,129 @@ +;RUN: opt '-passes=cj-pea' -S < %s | FileCheck %s + +; ModuleID = '/home/dingshenke/DTS/64KBArray/BenchmarkLoop.bc' +source_filename = "default" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%BitMap = type { i32, [0 x i8] } +%TypeTemplate = type { i8*, i8, i8, i16, i16, i8*, i8*, i8*, i8* } +%default.pkgInfoType = type { i32, i32, i32, i32, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8* } +%TypeInfo = type { i8*, i8, i8, i16, i32, %BitMap*, i32, i8, i8, i32*, i8*, i8*, i8*, %TypeInfo*, i8*, i8* } +%ArrayLayout.refArray = type { %ArrayBase, [0 x i8 addrspace(1)*] } +%ArrayBase = type { i64 } +%ObjLayout._ZN7default3ObjE = type {i64, i64} +@A1_C_ZN7default3ObjEE.typeName = weak_odr global [22 x i8] c"A1_C_ZN7default3ObjEE\00", align 1 +@_ZN7default3ObjE.ti = internal global %TypeInfo {i8* getelementptr inbounds ([12 x i8], [12 x i8]* @"default$Obj.name", i32 0, i32 0), i8 -128, i8 0, i16 2, i32 16, %BitMap* null, i32 0, i8 8, i8 0, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @"default$Obj.ti.offsets", i32 0, i32 0), i8* null, i8* null, i8* bitcast ([1 x %TypeInfo*]* @"default$Obj.ti.fields" to i8*), %TypeInfo* @"std.core$Object.ti", i8* null, i8* bitcast ([1 x i8*]* @"default$Obj.fieldNames" to i8*) }, !RelatedType !1 +@"default$Obj.name" = internal global [12 x i8] c"default$Obj\00", align 1 +@"default$Obj.ti.offsets" = internal global [1 x i32] [i32 0] +@"default$Obj.ti.fields" = internal global [1 x %TypeInfo*] [%TypeInfo* @"std.core$Object.ti"] +@"default$Obj.fieldNames" = internal global [1 x i8*] [i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"default$Obj.field.1.name", i32 0, i32 0)] +@"default$Obj.field.1.name" = internal global [3 x i8] c"aa\00", align 1 +@"RawArray.ti" = internal global %TypeInfo { i8* getelementptr inbounds ([14 x i8], [14 x i8]* @"RawArray.name", i32 0, i32 0), i8 -126, i8 0, i16 0, i32 8, %BitMap* null, i32 0, i8 8, i8 0, i32* null, i8* bitcast (%TypeTemplate* @RawArray.tt to i8*), i8* null, i8* null, %TypeInfo* @"std.core$Object.ti", i8* null, i8* null }, !RelatedType !0 +@"RawArray.name" = internal global [14 x i8] c"RawArray\00" +@RawArray.tt = external global %TypeTemplate +@"std.core$Object.ti" = external global %TypeInfo + +define private i32 @"__cj_personality_v0$"(...) { +entry: + ret i32 0 +} + +define fastcc void @_ZN7default15benchmarkLoopD4Ev() unnamed_addr #8 gc "cangjie" personality i32 (...)* @"__cj_personality_v0$" { +; CHECK-LABEL: @_ZN7default15benchmarkLoopD4Ev +; CHECK-LABEL: entry +; CHECK-NEXT: %0 = alloca { %TypeInfo*, %ObjLayout._ZN7default3ObjE }, align 8 +; CHECK-NEXT: %1 = bitcast { %TypeInfo*, %ObjLayout._ZN7default3ObjE }* %0 to i8** + +entry: + %0 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default3ObjE.ti to i8*), i32 16) + %1 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 8 + %2 = bitcast i8 addrspace(1)* %1 to i64 addrspace(1)* + store i64 10, i64 addrspace(1)* %2, align 8 + store i64 100, i64 addrspace(1)* %2, align 8 + %3 = call noalias i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 65536) + %val.is-null = icmp eq i8 addrspace(1)* %0, null + br i1 %val.is-null, label %arr.init.opt, label %arr.init.body + +arr.init.body: ; preds = %entry, %arr.init.body + %iterator.0 = phi i64 [ %7, %arr.init.body ], [ 0, %entry ] + %4 = getelementptr inbounds i8, i8 addrspace(1)* %3, i64 16 + %5 = bitcast i8 addrspace(1)* %4 to i8 addrspace(1)* addrspace(1)* + %6 = getelementptr inbounds i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %5, i64 %iterator.0 + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* nonnull %0, i8 addrspace(1)* %3, i8 addrspace(1)* addrspace(1)* %6) + %7 = add i64 %iterator.0, 1 + %iterator.size.cmp = icmp slt i64 %7, 65536 + br i1 %iterator.size.cmp, label %arr.init.body, label %arr.init.opt + +arr.init.opt: ; preds = %arr.init.body, %entry + ret void +} + + +; Function Attrs: argmemonly nounwind +declare void @llvm.cj.gcwrite.ref(i8 addrspace(1)*, i8 addrspace(1)* nocapture, i8 addrspace(1)* addrspace(1)* nocapture) #15 + +declare void @CJ_MCC_CheckThreadLocalDataOffset() local_unnamed_addr + +declare void @CJ_MRT_CjRuntimeStart(i32 ()* %0) local_unnamed_addr + +; Function Attrs: nounwind +declare void @llvm.cj.memset.p0i8(i8* %0, i8 %1, i64 %2, i1 %3) #17 + +declare void @CJ_MCC_C2NStub(...) #22 + +declare void @CJ_MCC_N2CStub(...) #22 + +declare i8 addrspace(1)* @CJ_MCC_NewObjectFast(i8* %0, i32 %1) #22 + +declare i8 addrspace(1)* @CJ_MCC_NewFinalizerFast(i8* %0, i32 %1) #22 + +declare i8 addrspace(1)* @CJ_MCC_OnFinalizerCreated(i8 addrspace(1)* %0) #22 + +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8* %0, i32 %1) #23 + +declare void @CJ_MCC_ThrowException(i8 addrspace(1)* %0) #23 + +declare void @CJ_MCC_ThrowArithmeticException() local_unnamed_addr + +declare i8 addrspace(1)* @CJ_MCC_NewArray(i8* %0, i64 %1) #23 + +declare i8* @CJ_MCC_GetExceptionWrapper() #22 + +declare i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %0) #22 + +declare void @CJ_MCC_EndCatch() #22 + +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %0, i8* %1) #22 + +declare i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* %0) local_unnamed_addr + +declare i8* @CJ_MCC_GetFuncPtrFromItab(i64* %0, i32 %1, i32 %2, i8* %3) local_unnamed_addr + +!0 = !{!"ArrayLayout.refArray"} +!1 = !{!"ObjLayout._ZN7default3ObjE"} + +attributes #0 = { "CFileKlass" } +attributes #1 = { "CFileVTable" } +attributes #2 = { "CFileITable" } +attributes #3 = { "GCRoot" } +attributes #4 = { "CFileReflect" } +attributes #5 = { "cj-native" } +attributes #6 = { noinline } +attributes #7 = { mustprogress nofree noinline norecurse nosync nounwind willreturn writeonly } +attributes #8 = { "hasMD" } +attributes #9 = { "hasRcdParam" } +attributes #10 = { noinline "cjinit" } +attributes #11 = { "hasMD" "hasRcdParam" } +attributes #12 = { "UsedByClosure" } +attributes #13 = { noinline "cjinit" "hasMD" } +attributes #14 = { "cjinit" "hasMD" } +attributes #15 = { argmemonly nounwind } +attributes #16 = { argmemonly mustprogress nocallback nofree nounwind willreturn } +attributes #17 = { nounwind } +attributes #18 = { argmemonly mustprogress nocallback nofree nounwind willreturn writeonly } +attributes #19 = { mustprogress nocallback nofree nosync nounwind readnone speculatable willreturn } +attributes #20 = { noinline "hasMD" } +attributes #21 = { noinline "cj2c" } +attributes #22 = { "cj-runtime" "gc-leaf-function" } +attributes #23 = { "cj-runtime" } diff --git a/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-array-ref.ll b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-array-ref.ll new file mode 100644 index 000000000..f2eaa8d8e --- /dev/null +++ b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-array-ref.ll @@ -0,0 +1,345 @@ +; RUN: opt < %s '-passes=cj-pea' -cj-disable-partial-ea -S | FileCheck %s + +%"record._ZN11std$FS$time8DurationE" = type { i64 } +%"record._ZN7default11std$FS$core5ArrayIC_ZN7default8testZlibEE" = type { i8 addrspace(1)*, i64, i64 } +%ArrayLayout.refArray = type { %ArrayBase, [0 x i8 addrspace(1)*] } +%ArrayBase = type { i64 } +%TypeTemplate = type { i8*, i8, i8, i16, i16, i8*, i8*, i8*, i8* } +%TypeInfo = type { i8*, i8, i8, i16, i32, %BitMap*, i32, i8, i8, i32*, i8*, i8*, i8*, %TypeInfo*, i8*, i8* } +%ObjLayout.Object = type {} +%ObjLayout._ZN7default5HumanE = type { %"record._ZN7default11std$FS$core5ArrayIC_ZN7default8testZlibEE", i64, i64 } +%BitMap = type { i32, [0 x i8] } +%Unit.Type = type { i8 } +%"record._ZN11std$FS$core6StringE" = type { %"record._ZN7default11std$FS$core5ArrayIhE", i64 } +%"record._ZN7default11std$FS$core5ArrayIhE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN11std$FS$time8DateTimeE" = type { i32, i32, i64, i64, i8 addrspace(1)*, i64 } +%ArrayLayout.UInt8 = type { %ArrayBase, [0 x i8] } +%ObjLayout._ZN7default8testZlibE = type { %"ObjLayout._ZN15std$FS$unittest9TestCasesE", i1, %"record._ZN7default11std$FS$core5ArrayIhE", i8 addrspace(1)*, i8 addrspace(1)*, i64, i64, i64 } +%"ObjLayout._ZN15std$FS$unittest9TestCasesE" = type { %"ObjLayout._ZN15std$FS$unittest5UTestE", i8 addrspace(1)*, i1, i1 } +%"ObjLayout._ZN15std$FS$unittest5UTestE" = type { i8 addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" } + +@"$const_cjstring.7" = internal constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [504 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 210, i64 8 }, i64 0 } +@"$const_cjstring.5" = internal constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [504 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 126, i64 3 }, i64 0 } +@"$const_cjstring_data.0" = internal constant { i8*, i64, [504 x i8] } { i8* bitcast (%TypeInfo* @roUInt8.arrayKlass to i8*), i64 504, [504 x i8] c"], [mulindex: .Illegal step Overshift: Negative shift count!invalid size of ArrayList: invalid capacity of ArrayList: typecastadd, step should be 1outputthe value of the step should not be zero.\0Asubtest, size: testZlib is not equal to array size:Casting NaN value to integer.Negative copy lengthOvershift: Value of right operand is greater than or equal to the width of left operand!Array negative index accessdiv, length is Range size:noneThe size of Array is , start is inputCopy length out of bounds[]" } +@array_int8.uniqueAddr = weak_odr hidden global i64 0 +@"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" = external global %TypeInfo +@"_ZN11std$FS$time8TimeZone5LocalE" = external local_unnamed_addr global i8 addrspace(1)* +@roUInt8.arrayKlass = weak_odr global %TypeInfo { i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"RawArray.name", i32 0, i32 0), i8 -126, i8 0, i16 0, i32 0, %BitMap* null, i32 0, i8 0, i8 0, i32* null, i8* bitcast (%TypeTemplate* @RawArray.tt to i8*), i8* null, i8* null, %TypeInfo* @UInt8.ti, i8* null, i8* null } +@"RawArray.name" = internal global [16 x i8] c"RawArray\00", align 1 +@UInt8.ti = external global %TypeInfo +@"std.core$Object.ti" = external global %TypeInfo +@_ZN7default5HumanE.ti = weak_odr global %TypeInfo {i8* getelementptr inbounds ([14 x i8], [14 x i8]* @"default$Human.name", i32 0, i32 0), i8 -128, i8 0, i16 2, i32 16, %BitMap* null, i32 0, i8 8, i8 0, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @"default$Human.ti.offsets", i32 0, i32 0), i8* null, i8* null, i8* bitcast ([1 x %TypeInfo*]* @"default$Human.ti.fields" to i8*), %TypeInfo* @"std.core$Object.ti", i8* null, i8* bitcast ([1 x i8*]* @"default$Human.fieldNames" to i8*)}, !RelatedType !1 +@"default$Human.name" = internal global [14 x i8] c"default$Human\00", align 1 +@"default$Human.ti.offsets" = internal global [1 x i32] [i32 0] +@"default$Human.ti.fields" = internal global [1 x %TypeInfo*] [%TypeInfo* @"std.core$Object.ti"] +@"default$Human.fieldNames" = internal global [1 x i8*] [i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"default$Human.field.1.name", i32 0, i32 0)] +@"default$Human.field.1.name" = internal global [3 x i8] c"aa\00", align 1 +@"RawArray.ti" = internal global %TypeInfo { i8* getelementptr inbounds ([14 x i8], [14 x i8]* @"RawArray.name", i32 0, i32 0), i8 -126, i8 0, i16 0, i32 8, %BitMap* null, i32 0, i8 8, i8 0, i32* null, i8* bitcast (%TypeTemplate* @RawArray.tt to i8*), i8* null, i8* null, %TypeInfo* @"std.core$Object.ti", i8* null, i8* null }, !RelatedType !0 +@"RawArray.name" = internal global [14 x i8] c"RawArray\00" +@RawArray.tt = external global %TypeTemplate + +declare i8* @CJ_MCC_GetObjClass(i8 addrspace(1)*) +declare i8 addrspace(1)* @CJ_MCC_NewArray(i8*, i64) +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare void @"_ZN11std$FS$core25IndexOutOfBoundsException6Ev"(i8 addrspace(1)*) +declare void @"_ZN11std$FS$time8DateTime1-ER_ZN11std$FS$time8DateTimeE"(%"record._ZN11std$FS$time8DurationE"*, i8 addrspace(1)*, %"record._ZN11std$FS$time8DateTimeE" addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$time8DateTimeE" addrspace(1)*) +declare void @"_ZN11std$FS$time8DateTime3nowEC_ZN11std$FS$time8TimeZoneE"(%"record._ZN11std$FS$time8DateTimeE"*, i8 addrspace(1)*) +declare void @"_ZN15std$FS$unittest8TestInfo11printResultEv"(%Unit.Type*, i8 addrspace(1)*) +declare i8 addrspace(1)* @"_ZN7default11std$FS$core13ArrayIteratorIC_ZN7default8testZlibE4nextEv"(i8 addrspace(1)*) +declare i8 addrspace(1)* @"_ZN7default11std$FS$core13ArrayIteratorIC_ZN7default8testZlibE8iteratorEv"(i8 addrspace(1)*) +declare void @"_ZN15std$FS$unittest9TestCases8runCasesEv"(%Unit.Type*, i8 addrspace(1)*) +declare i8 addrspace(1)* @"_ZN15std$FS$unittest5UTest10jsonReportEv"(i8 addrspace(1)*) +declare i8 addrspace(1)* @"_ZN15std$FS$unittest5UTest11getTestInfoEv"(i8 addrspace(1)*) +declare i8 addrspace(1)* @"_ZN15std$FS$unittest5UTest7$ctxgetEv"(i8 addrspace(1)*) +declare void @_ZN7default8testZlib9initCasesEv(%Unit.Type*, i8 addrspace(1)*) +declare void @"_ZN15std$FS$unittest9TestCases8afterAllEv"(%Unit.Type*, i8 addrspace(1)*) +declare void @"_ZN15std$FS$unittest9TestCases9afterEachEv"(%Unit.Type*, i8 addrspace(1)*) +declare void @"_ZN15std$FS$unittest9TestCases9beforeAllEv"(%Unit.Type*, i8 addrspace(1)*) +declare void @"_ZN15std$FS$unittest9TestCases10beforeEachEv"(%Unit.Type*, i8 addrspace(1)*) +declare void @_ZN7default8testZlib4testEv(%Unit.Type*, i8 addrspace(1)*) +declare void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)*, i8 addrspace(1)*, i8*, i64) +declare void @llvm.cj.gcwrite.ref(i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*) +declare void @llvm.cj.memset.p0i8(i8*, i8, i64, i1) +declare void @llvm.lifetime.end.p0i8(i64, i8*) +declare void @llvm.lifetime.start.p0i8(i64, i8*) +declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) +declare i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) +declare void @"_ZN11std$FS$core26NegativeArraySizeException6Ev"(i8 addrspace(1)*) +declare void @"_ZN15std$FS$unittest9TestCases6Ev"(i8 addrspace(1)*) +declare void @"_ZN9std$FS$io15ByteBuffer5writeER_ZN11std$FS$core5ArrayIhE"(%Unit.Type*, i8 addrspace(1)*, i8 addrspace(1)*, %"record._ZN7default11std$FS$core5ArrayIhE" addrspace(1)*) +declare void @"_ZN9std$FS$io15ByteBuffer6El"(i8 addrspace(1)*, i64) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1) +declare void @foo1(i8 addrspace(1)* %this, i64 %len, i64 %wrap, i64 %compressLevel, i64 %bufLen) + +; CHECK: alloca { %TypeInfo*, { %ArrayBase, [18 x i8 addrspace(1)*] } } + +define i64 @"_ZN7default6
Ev"() local_unnamed_addr gc "cangjie" { +entry: + %callRet7.i = alloca %"record._ZN11std$FS$time8DurationE", align 8 + %callRet6.i = alloca %"record._ZN11std$FS$time8DateTimeE", align 8 + %callRet3.i = alloca %Unit.Type, align 8 + %callRet.i = alloca %"record._ZN11std$FS$time8DateTimeE", align 8 + %callRet29 = alloca %Unit.Type, align 8 + %cases = alloca %"record._ZN7default11std$FS$core5ArrayIC_ZN7default8testZlibEE", align 8 + %0 = call noalias i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 18) + %1 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %1, i64 1024, i64 0, i64 0, i64 1) + %arr.idx0E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 16 + %2 = bitcast i8 addrspace(1)* %arr.idx0E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %1, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %2) + %3 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %3, i64 1024, i64 0, i64 1, i64 1) + %arr.idx1E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 24 + %4 = bitcast i8 addrspace(1)* %arr.idx1E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %3, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %4) + %5 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %5, i64 1024, i64 0, i64 2, i64 1) + %arr.idx2E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 32 + %6 = bitcast i8 addrspace(1)* %arr.idx2E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %5, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %6) + %7 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %7, i64 1, i64 0, i64 0, i64 1024) + %arr.idx3E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 40 + %8 = bitcast i8 addrspace(1)* %arr.idx3E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %7, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %8) + %9 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %9, i64 1024, i64 0, i64 1, i64 1024) + %arr.idx4E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 48 + %10 = bitcast i8 addrspace(1)* %arr.idx4E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %9, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %10) + %11 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %11, i64 1024, i64 0, i64 2, i64 1024) + %arr.idx5E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 56 + %12 = bitcast i8 addrspace(1)* %arr.idx5E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %11, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %12) + %13 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %13, i64 1024, i64 0, i64 0, i64 1048576) + %arr.idx6E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 64 + %14 = bitcast i8 addrspace(1)* %arr.idx6E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %13, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %14) + %15 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %15, i64 1, i64 0, i64 1, i64 1048576) + %arr.idx7E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 72 + %16 = bitcast i8 addrspace(1)* %arr.idx7E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %15, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %16) + %17 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %17, i64 1024, i64 0, i64 2, i64 1048576) + %arr.idx8E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 80 + %18 = bitcast i8 addrspace(1)* %arr.idx8E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %17, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %18) + %19 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %19, i64 1024, i64 1, i64 0, i64 1) + %arr.idx9E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 88 + %20 = bitcast i8 addrspace(1)* %arr.idx9E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %19, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %20) + %21 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %21, i64 1024, i64 1, i64 1, i64 1) + %arr.idx10E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 96 + %22 = bitcast i8 addrspace(1)* %arr.idx10E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %21, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %22) + %23 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %23, i64 1, i64 1, i64 2, i64 1) + %arr.idx11E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 104 + %24 = bitcast i8 addrspace(1)* %arr.idx11E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %23, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %24) + %25 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %25, i64 1024, i64 1, i64 0, i64 1024) + %arr.idx12E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 112 + %26 = bitcast i8 addrspace(1)* %arr.idx12E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %25, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %26) + %27 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %27, i64 1048576, i64 1, i64 1, i64 1024) + %arr.idx13E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 120 + %28 = bitcast i8 addrspace(1)* %arr.idx13E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %27, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %28) + %29 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %29, i64 1024, i64 1, i64 2, i64 1024) + %arr.idx14E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 128 + %30 = bitcast i8 addrspace(1)* %arr.idx14E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %29, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %30) + %31 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %31, i64 1024, i64 1, i64 0, i64 1048576) + %arr.idx15E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 136 + %32 = bitcast i8 addrspace(1)* %arr.idx15E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %31, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %32) + %33 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %33, i64 1024, i64 1, i64 1, i64 1048576) + %arr.idx16E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 144 + %34 = bitcast i8 addrspace(1)* %arr.idx16E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %33, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %34) + %35 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 144) + call void @foo1(i8 addrspace(1)* %35, i64 1048576, i64 1, i64 2, i64 1048576) + %arr.idx17E = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 152 + %36 = bitcast i8 addrspace(1)* %arr.idx17E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %35, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %36) + %37 = bitcast %"record._ZN7default11std$FS$core5ArrayIC_ZN7default8testZlibEE"* %cases to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %37, i8 0, i64 24, i1 false) + %atom.sroa.0.0..sroa_idx1 = getelementptr inbounds %"record._ZN7default11std$FS$core5ArrayIC_ZN7default8testZlibEE", %"record._ZN7default11std$FS$core5ArrayIC_ZN7default8testZlibEE"* %cases, i64 0, i32 0 + store i8 addrspace(1)* %0, i8 addrspace(1)** %atom.sroa.0.0..sroa_idx1, align 8 + %atom.sroa.4.0..sroa_idx4 = getelementptr inbounds %"record._ZN7default11std$FS$core5ArrayIC_ZN7default8testZlibEE", %"record._ZN7default11std$FS$core5ArrayIC_ZN7default8testZlibEE"* %cases, i64 0, i32 1 + store i64 0, i64* %atom.sroa.4.0..sroa_idx4, align 8 + %atom.sroa.5.0..sroa_idx7 = getelementptr inbounds %"record._ZN7default11std$FS$core5ArrayIC_ZN7default8testZlibEE", %"record._ZN7default11std$FS$core5ArrayIC_ZN7default8testZlibEE"* %cases, i64 0, i32 2 + store i64 18, i64* %atom.sroa.5.0..sroa_idx7, align 8 + %38 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 48) + %39 = getelementptr inbounds i8, i8 addrspace(1)* %38, i64 8 + call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* %38, i8 addrspace(1)* %39, i8* nonnull %37, i64 24) + %40 = getelementptr inbounds i8, i8 addrspace(1)* %38, i64 32 + %41 = bitcast i8 addrspace(1)* %40 to i64 addrspace(1)* + store i64 18, i64 addrspace(1)* %41, align 8 + %42 = getelementptr inbounds i8, i8 addrspace(1)* %38, i64 40 + %43 = bitcast i8 addrspace(1)* %42 to i64 addrspace(1)* + store i64 0, i64 addrspace(1)* %43, align 8 + br label %block.body + +block.body: ; preds = %normal34, %entry + %atom22.0 = phi i64 [ 0, %entry ], [ %.fca.0.extract34, %normal34 ] + %44 = getelementptr inbounds i8, i8 addrspace(1)* %38, i64 40 + %45 = bitcast i8 addrspace(1)* %44 to i64 addrspace(1)* + %46 = load i64, i64 addrspace(1)* %45, align 8 + %47 = getelementptr inbounds i8, i8 addrspace(1)* %38, i64 32 + %48 = bitcast i8 addrspace(1)* %47 to i64 addrspace(1)* + %49 = load i64, i64 addrspace(1)* %48, align 8 + %icmpslt.i = icmp slt i64 %46, %49 + br i1 %icmpslt.i, label %if.then.i, label %"_ZN7default11std$FS$core13ArrayIteratorIC_ZN7default8testZlibE4nextEv.exit" + +if.then.i: ; preds = %block.body + %50 = getelementptr inbounds i8, i8 addrspace(1)* %38, i64 24 + %51 = bitcast i8 addrspace(1)* %50 to i64 addrspace(1)* + %52 = load i64, i64 addrspace(1)* %51, align 8 + %icmpuge.not.i = icmp ult i64 %46, %52 + br i1 %icmpuge.not.i, label %if.else5.i, label %if.then6.i + +if.then6.i: ; preds = %if.then.i + %53 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 72) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException6Ev"(i8 addrspace(1)* %53) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %53) + unreachable + +if.else5.i: ; preds = %if.then.i + %54 = getelementptr inbounds i8, i8 addrspace(1)* %38, i64 8 + %55 = bitcast i8 addrspace(1)* %54 to %ArrayLayout.refArray addrspace(1)* addrspace(1)* + %56 = load %ArrayLayout.refArray addrspace(1)*, %ArrayLayout.refArray addrspace(1)* addrspace(1)* %55, align 8 + %57 = getelementptr inbounds i8, i8 addrspace(1)* %38, i64 16 + %58 = bitcast i8 addrspace(1)* %57 to i64 addrspace(1)* + %59 = load i64, i64 addrspace(1)* %58, align 8 + %add.i = add i64 %59, %46 + %arr.idx.get.gep.i = getelementptr inbounds %ArrayLayout.refArray, %ArrayLayout.refArray addrspace(1)* %56, i64 0, i32 1, i64 %add.i + %60 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %arr.idx.get.gep.i, align 8 + %add8.i = add nuw i64 %46, 1 + store i64 %add8.i, i64 addrspace(1)* %45, align 8 + br label %"_ZN7default11std$FS$core13ArrayIteratorIC_ZN7default8testZlibE4nextEv.exit" + +"_ZN7default11std$FS$core13ArrayIteratorIC_ZN7default8testZlibE4nextEv.exit": ; preds = %block.body, %if.else5.i + %common.ret.op.i = phi i8 addrspace(1)* [ %60, %if.else5.i ], [ null, %block.body ] + %61 = icmp eq i8 addrspace(1)* %common.ret.op.i, null + br i1 %61, label %if.then, label %if.else + +if.then: ; preds = %"_ZN7default11std$FS$core13ArrayIteratorIC_ZN7default8testZlibE4nextEv.exit" + ret i64 %atom22.0 + +if.else: ; preds = %"_ZN7default11std$FS$core13ArrayIteratorIC_ZN7default8testZlibE4nextEv.exit" + %62 = bitcast %"record._ZN11std$FS$time8DurationE"* %callRet7.i to i8* + call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %62) + %63 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %callRet6.i to i8* + call void @llvm.lifetime.start.p0i8(i64 40, i8* nonnull %63) + %64 = getelementptr inbounds %Unit.Type, %Unit.Type* %callRet3.i, i64 0, i32 0 + call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %64) + %65 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %callRet.i to i8* + call void @llvm.lifetime.start.p0i8(i64 40, i8* nonnull %65) + %66 = getelementptr inbounds i8, i8 addrspace(1)* %common.ret.op.i, i64 16 + %67 = bitcast i8 addrspace(1)* %66 to i8 addrspace(1)* addrspace(1)* + %68 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %67, align 8 + %69 = load i8 addrspace(1)*, i8 addrspace(1)** @"_ZN11std$FS$time8TimeZone5LocalE", align 8 + %70 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %callRet.i to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %70, i8 0, i64 40, i1 false) + call void @"_ZN11std$FS$time8DateTime3nowEC_ZN11std$FS$time8TimeZoneE"(%"record._ZN11std$FS$time8DateTimeE"* noalias nonnull sret(%"record._ZN11std$FS$time8DateTimeE") %callRet.i, i8 addrspace(1)* %69) + %71 = getelementptr inbounds i8, i8 addrspace(1)* %68, i64 96 + call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* %68, i8 addrspace(1)* %71, i8* nonnull %70, i64 40) + %72 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %67, align 8 + %73 = call i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* nonnull %common.ret.op.i) + %74 = getelementptr i8, i8* %73, i64 24 + %75 = bitcast i8* %74 to i8*** + %76 = load i8**, i8*** %75, align 8 + %77 = getelementptr i8*, i8** %76, i64 4 + %78 = bitcast i8** %77 to i8 addrspace(1)* (i8 addrspace(1)*)** + %79 = load i8 addrspace(1)* (i8 addrspace(1)*)*, i8 addrspace(1)* (i8 addrspace(1)*)** %78, align 8 + %80 = call i8 addrspace(1)* %79(i8 addrspace(1)* nonnull %common.ret.op.i) + %81 = getelementptr inbounds i8, i8 addrspace(1)* %80, i64 8 + %82 = bitcast i8 addrspace(1)* %81 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %72, i8 addrspace(1)* %80, i8 addrspace(1)* addrspace(1)* %82) + %83 = call i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* nonnull %common.ret.op.i) + %84 = getelementptr i8, i8* %83, i64 24 + %85 = bitcast i8* %84 to i8*** + %86 = load i8**, i8*** %85, align 8 + %87 = getelementptr i8*, i8** %86, i64 1 + %88 = bitcast i8** %87 to void (%Unit.Type*, i8 addrspace(1)*)** + %89 = load void (%Unit.Type*, i8 addrspace(1)*)*, void (%Unit.Type*, i8 addrspace(1)*)** %88, align 8 + call void %89(%Unit.Type* noalias nonnull sret(%Unit.Type) %callRet3.i, i8 addrspace(1)* nonnull %common.ret.op.i) + %90 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %67, align 8 + %91 = load i8 addrspace(1)*, i8 addrspace(1)** @"_ZN11std$FS$time8TimeZone5LocalE", align 8 + %92 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %callRet6.i to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %92, i8 0, i64 40, i1 false) + call void @"_ZN11std$FS$time8DateTime3nowEC_ZN11std$FS$time8TimeZoneE"(%"record._ZN11std$FS$time8DateTimeE"* noalias nonnull sret(%"record._ZN11std$FS$time8DateTimeE") %callRet6.i, i8 addrspace(1)* %91) + %93 = getelementptr inbounds i8, i8 addrspace(1)* %90, i64 96 + %94 = bitcast i8 addrspace(1)* %93 to %"record._ZN11std$FS$time8DateTimeE" addrspace(1)* + %95 = addrspacecast %"record._ZN11std$FS$time8DateTimeE"* %callRet6.i to %"record._ZN11std$FS$time8DateTimeE" addrspace(1)* + call void @"_ZN11std$FS$time8DateTime1-ER_ZN11std$FS$time8DateTimeE"(%"record._ZN11std$FS$time8DurationE"* noalias nonnull sret(%"record._ZN11std$FS$time8DurationE") %callRet7.i, i8 addrspace(1)* null, %"record._ZN11std$FS$time8DateTimeE" addrspace(1)* %95, i8 addrspace(1)* %90, %"record._ZN11std$FS$time8DateTimeE" addrspace(1)* %94) + %96 = getelementptr inbounds %"record._ZN11std$FS$time8DurationE", %"record._ZN11std$FS$time8DurationE"* %callRet7.i, i64 0, i32 0 + %97 = load i64, i64* %96, align 8 + %98 = getelementptr inbounds i8, i8 addrspace(1)* %90, i64 136 + %99 = bitcast i8 addrspace(1)* %98 to i64 addrspace(1)* + store i64 %97, i64 addrspace(1)* %99, align 8 + %100 = bitcast %"record._ZN11std$FS$time8DurationE"* %callRet7.i to i8* + call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %100) + %101 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %callRet6.i to i8* + call void @llvm.lifetime.end.p0i8(i64 40, i8* nonnull %101) + %102 = getelementptr inbounds %Unit.Type, %Unit.Type* %callRet3.i, i64 0, i32 0 + call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %102) + %103 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %callRet.i to i8* + call void @llvm.lifetime.end.p0i8(i64 40, i8* nonnull %103) + %104 = getelementptr inbounds i8, i8 addrspace(1)* %common.ret.op.i, i64 16 + %105 = bitcast i8 addrspace(1)* %104 to i8 addrspace(1)* addrspace(1)* + %106 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %105, align 8 + call void @"_ZN15std$FS$unittest8TestInfo11printResultEv"(%Unit.Type* noalias nonnull sret(%Unit.Type) %callRet29, i8 addrspace(1)* %106) + %107 = call i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* nonnull %common.ret.op.i) + %108 = getelementptr i8, i8* %107, i64 24 + %109 = bitcast i8* %108 to i8*** + %110 = load i8**, i8*** %109, align 8 + %111 = getelementptr i8*, i8** %110, i64 3 + %112 = bitcast i8** %111 to i8 addrspace(1)* (i8 addrspace(1)*)** + %113 = load i8 addrspace(1)* (i8 addrspace(1)*)*, i8 addrspace(1)* (i8 addrspace(1)*)** %112, align 8 + %114 = call i8 addrspace(1)* %113(i8 addrspace(1)* nonnull %common.ret.op.i) + %115 = getelementptr inbounds i8, i8 addrspace(1)* %114, i64 72 + %116 = bitcast i8 addrspace(1)* %115 to i64 addrspace(1)* + %117 = load i64, i64 addrspace(1)* %116, align 8 + %118 = getelementptr inbounds i8, i8 addrspace(1)* %114, i64 48 + %119 = bitcast i8 addrspace(1)* %118 to i64 addrspace(1)* + %120 = load i64, i64 addrspace(1)* %119, align 8 + %121 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %117, i64 %120) + %.fca.1.extract = extractvalue { i64, i1 } %121, 1 + br i1 %.fca.1.extract, label %overflow, label %normal + +normal: ; preds = %if.else + %.fca.0.extract = extractvalue { i64, i1 } %121, 0 + %122 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %atom22.0, i64 %.fca.0.extract) + %.fca.1.extract35 = extractvalue { i64, i1 } %122, 1 + br i1 %.fca.1.extract35, label %overflow33, label %normal34 + +overflow: ; preds = %if.else + %123 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* addrspacecast (%"record._ZN11std$FS$core6StringE"* bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.5" to %"record._ZN11std$FS$core6StringE"*) to %"record._ZN11std$FS$core6StringE" addrspace(1)*)) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %123) + unreachable + +normal34: ; preds = %normal + %.fca.0.extract34 = extractvalue { i64, i1 } %122, 0 + br label %block.body + +overflow33: ; preds = %normal + %124 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* addrspacecast (%"record._ZN11std$FS$core6StringE"* bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.5" to %"record._ZN11std$FS$core6StringE"*) to %"record._ZN11std$FS$core6StringE" addrspace(1)*)) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %124) + unreachable +} + +!0 = !{!"ArrayLayout.refArray"} +!1 = !{!"ObjLayout._ZN7default5HumanE"} diff --git a/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-call1.ll b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-call1.ll new file mode 100644 index 000000000..0d46771bf --- /dev/null +++ b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-call1.ll @@ -0,0 +1,97 @@ +; RUN: opt < %s '-passes=cj-pea' --cj-disable-partial-ea -S | FileCheck %s --check-prefixes=PEA + +%BitMap = type { i32, [0 x i8] } +%_ZN7default8CrcCheckE.objKlass.reflectType = type { i32, i32, i32, i32, i32, i8* } +%ObjLayout._ZN7default8CrcCheck = type { %record._ZN8std.core5ArrayIlE, %record._ZN8std.core5ArrayIlE, %record._ZN8std.core5ArrayIlE, i64, i64, i64 } +%record._ZN8std.core5ArrayIlE = type { i8 addrspace(1)*, i64, i64 } +%ArrayLayout.Int64 = type { %ArrayBase, [0 x i64] } +%ArrayBase = type { i64 } +%TypeInfo = type { i8*, i8, i8, i16, i32, %BitMap*, i32, i8, i8, i32*, i8*, i8*, i8*, %TypeInfo*, i8*, i8* } +%TypeTemplate = type { i8*, i8, i8, i16, i16, i8*, i8*, i8*, i8* } + +@_ZN7default20var_1715509008039_60E = global i8 0, align 8 +@A1_lE.typeName = internal global [6 x i8] c"A1_lE\00", align 1 +@_ZN7default8CrcCheck.ti = internal global %TypeInfo {i8* getelementptr inbounds ([17 x i8], [17 x i8]* @"default$CrcCheck.name", i32 0, i32 0), i8 -128, i8 0, i16 2, i32 16, %BitMap* null, i32 0, i8 8, i8 0, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @"default$CrcCheck.ti.offsets", i32 0, i32 0), i8* null, i8* null, i8* bitcast ([1 x %TypeInfo*]* @"default$CrcCheck.ti.fields" to i8*), %TypeInfo* @"std.core$Object.ti", i8* null, i8* bitcast ([1 x i8*]* @"default$CrcCheck.fieldNames" to i8*)}, !RelatedType !1 +@"default$CrcCheck.name" = internal global [17 x i8] c"default$CrcCheck\00", align 1 +@"std.core$Object.ti" = external global %TypeInfo +@"default$CrcCheck.ti.offsets" = internal global [1 x i32] [i32 0] +@"default$CrcCheck.ti.fields" = internal global [1 x %TypeInfo*] [%TypeInfo* @"std.core$Object.ti"] +@"default$CrcCheck.fieldNames" = internal global [1 x i8*] [i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"default$CrcCheck.field.1.name", i32 0, i32 0)] +@"default$CrcCheck.field.1.name" = internal global [3 x i8] c"aa\00", align 1 +@"RawArray.ti" = internal global %TypeInfo { i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"RawArray.name", i32 0, i32 0), i8 -126, i8 0, i16 0, i32 8, %BitMap* null, i32 0, i8 8, i8 0, i32* null, i8* bitcast (%TypeTemplate* @RawArray.tt to i8*), i8* null, i8* null, %TypeInfo* @Int64.ti, i8* null, i8* null }, !RelatedType !0 +@"RawArray.name" = internal global [16 x i8] c"RawArray\00", align 1 +@RawArray.tt = external global %TypeTemplate +@Int64.ti = external global %TypeInfo + +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) + +declare i8 addrspace(1)* @CJ_MCC_NewArray64(i64, i8*) + +; Function Attrs: argmemonly nounwind +declare void @llvm.cj.gcwrite.ref(i8 addrspace(1)*, i8 addrspace(1)* nocapture, i8 addrspace(1)* addrspace(1)* nocapture) #0 + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn writeonly +declare void @llvm.memset.p1i8.i64(i8 addrspace(1)* nocapture writeonly, i8, i64, i1 immarg) #1 + +define i64 @goo(i8 addrspace(1)* nocapture %this, i64 %input) gc "cangjie" { +bb0: + %0 = bitcast i8 addrspace(1)* %this to { %TypeInfo*, %ObjLayout._ZN7default8CrcCheck } addrspace(1)* + %1 = getelementptr inbounds { %TypeInfo*, %ObjLayout._ZN7default8CrcCheck }, { %TypeInfo*, %ObjLayout._ZN7default8CrcCheck } addrspace(1)* %0, i64 0, i32 1, i32 4 + %2 = load i64, i64 addrspace(1)* %1, align 8 + %3 = getelementptr inbounds { %TypeInfo*, %ObjLayout._ZN7default8CrcCheck }, { %TypeInfo*, %ObjLayout._ZN7default8CrcCheck } addrspace(1)* %0, i64 0, i32 1, i32 0, i32 2 + %4 = load i64, i64 addrspace(1)* %3, align 8 + br label %bb1 + +bb1: ; preds = %bb0 + %5 = add i64 %4, %input + ret i64 %5 +} + +; PEA: %0 = alloca { %TypeInfo*, %ObjLayout._ZN7default8CrcCheck } +; PEA: %5 = call i8 addrspace(1)* @CJ_MCC_NewArray64(i64 0, i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*)) +; PEA: %6 = call i8 addrspace(1)* @CJ_MCC_NewArray64(i64 5000, i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*)) +; PEA: %7 = call i8 addrspace(1)* @CJ_MCC_NewArray64(i64 33, i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*)) +define i64 @foo() gc "cangjie" { +bb0: + %0 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default8CrcCheck.ti to i8*), i32 104) + %1 = call i8 addrspace(1)* @CJ_MCC_NewArray64(i64 0, i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*)) + %2 = call i8 addrspace(1)* @CJ_MCC_NewArray64(i64 5000, i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*)) + %3 = call i8 addrspace(1)* @CJ_MCC_NewArray64(i64 33, i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*)) + %4 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 88 + %5 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 56 + %6 = bitcast i8 addrspace(1)* %5 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %1, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %6) + %7 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 64 + call void @llvm.memset.p1i8.i64(i8 addrspace(1)* noundef align 8 dereferenceable(16) %7, i8 0, i64 16, i1 false) + %8 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 8 + %9 = bitcast i8 addrspace(1)* %8 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %2, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %9) + %10 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 16 + store i8 0, i8 addrspace(1)* %10, align 8 + %11 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 24 + %12 = bitcast i8 addrspace(1)* %11 to i64 addrspace(1)* + store i64 5000, i64 addrspace(1)* %12, align 8 + %13 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 80 + %14 = bitcast i8 addrspace(1)* %13 to i64 addrspace(1)* + store i64 33, i64 addrspace(1)* %14, align 8 + %15 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 32 + %16 = bitcast i8 addrspace(1)* %15 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %3, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %16) + %17 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 40 + %18 = bitcast i8 addrspace(1)* %17 to i64 addrspace(1)* + store i64 0, i64 addrspace(1)* %18, align 8 + %19 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 48 + %20 = bitcast i8 addrspace(1)* %19 to i64 addrspace(1)* + %21 = load i8, i8* @_ZN7default20var_1715509008039_60E, align 8 + %22 = mul i8 %21, %21 + %23 = zext i8 %22 to i64 + %24 = call i64 @goo(i8 addrspace(1)* %0, i64 %23) + store i64 %24, i64 addrspace(1)* %20, align 8 + ret i64 %24 +} + +!0 = !{!"ArrayLayout.Int64"} +!1 = !{!"ObjLayout._ZN7default8CrcCheck"} + +attributes #0 = { argmemonly nounwind } +attributes #1 = { argmemonly nocallback nofree nounwind willreturn writeonly } diff --git a/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-call2.ll b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-call2.ll new file mode 100644 index 000000000..bdc9d0bc4 --- /dev/null +++ b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-call2.ll @@ -0,0 +1,97 @@ +; RUN: opt < %s '-passes=cj-pea' -cj-disable-partial-ea -S | FileCheck %s --check-prefixes=PEA + +%BitMap = type { i32, [0 x i8] } +%_ZN7default8CrcCheckE.objKlass.reflectType = type { i32, i32, i32, i32, i32, i8* } +%ObjLayout._ZN7default8CrcCheck = type { %record._ZN8std.core5ArrayIlE, %record._ZN8std.core5ArrayIlE, %record._ZN8std.core5ArrayIlE, i64, i64, i64 } +%record._ZN8std.core5ArrayIlE = type { i8 addrspace(1)*, i64, i64 } +%ArrayLayout.Int64 = type { %ArrayBase, [0 x i64] } +%ArrayBase = type { i64 } +%TypeInfo = type { i8*, i8, i8, i16, i32, %BitMap*, i32, i8, i8, i32*, i8*, i8*, i8*, %TypeInfo*, i8*, i8* } +%TypeTemplate = type { i8*, i8, i8, i16, i16, i8*, i8*, i8*, i8* } + +@_ZN7default20var_1715509008039_60E = global i8 0, align 8 +@A1_lE.typeName = internal global [6 x i8] c"A1_lE\00", align 1 +@_ZN7default8CrcCheck.ti = internal global %TypeInfo {i8* getelementptr inbounds ([17 x i8], [17 x i8]* @"default$CrcCheck.name", i32 0, i32 0), i8 -128, i8 0, i16 2, i32 16, %BitMap* null, i32 0, i8 8, i8 0, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @"default$CrcCheck.ti.offsets", i32 0, i32 0), i8* null, i8* null, i8* bitcast ([1 x %TypeInfo*]* @"default$CrcCheck.ti.fields" to i8*), %TypeInfo* @"std.core$Object.ti", i8* null, i8* bitcast ([1 x i8*]* @"default$CrcCheck.fieldNames" to i8*)}, !RelatedType !1 +@"default$CrcCheck.name" = internal global [17 x i8] c"default$CrcCheck\00", align 1 +@"std.core$Object.ti" = external global %TypeInfo +@"default$CrcCheck.ti.offsets" = internal global [1 x i32] [i32 0] +@"default$CrcCheck.ti.fields" = internal global [1 x %TypeInfo*] [%TypeInfo* @"std.core$Object.ti"] +@"default$CrcCheck.fieldNames" = internal global [1 x i8*] [i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"default$CrcCheck.field.1.name", i32 0, i32 0)] +@"default$CrcCheck.field.1.name" = internal global [3 x i8] c"aa\00", align 1 +@"RawArray.ti" = internal global %TypeInfo { i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"RawArray.name", i32 0, i32 0), i8 -126, i8 0, i16 0, i32 8, %BitMap* null, i32 0, i8 8, i8 0, i32* null, i8* bitcast (%TypeTemplate* @RawArray.tt to i8*), i8* null, i8* null, %TypeInfo* @Int64.ti, i8* null, i8* null }, !RelatedType !0 +@"RawArray.name" = internal global [16 x i8] c"RawArray\00", align 1 +@RawArray.tt = external global %TypeTemplate +@Int64.ti = external global %TypeInfo + +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) + +declare i8 addrspace(1)* @CJ_MCC_NewArray(i8*, i64) + +; Function Attrs: argmemonly nounwind +declare void @llvm.cj.gcwrite.ref(i8 addrspace(1)*, i8 addrspace(1)* nocapture, i8 addrspace(1)* addrspace(1)* nocapture) #0 + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn writeonly +declare void @llvm.memset.p1i8.i64(i8 addrspace(1)* nocapture writeonly, i8, i64, i1 immarg) #1 + +define i64 @goo(i8 addrspace(1)* nocapture %this, i64 %input) gc "cangjie" { +bb0: + %0 = bitcast i8 addrspace(1)* %this to { %TypeInfo*, %ObjLayout._ZN7default8CrcCheck } addrspace(1)* + %1 = getelementptr inbounds { %TypeInfo*, %ObjLayout._ZN7default8CrcCheck }, { %TypeInfo*, %ObjLayout._ZN7default8CrcCheck } addrspace(1)* %0, i64 0, i32 1, i32 4 + %2 = load i64, i64 addrspace(1)* %1, align 8 + %3 = getelementptr inbounds { %TypeInfo*, %ObjLayout._ZN7default8CrcCheck }, { %TypeInfo*, %ObjLayout._ZN7default8CrcCheck } addrspace(1)* %0, i64 0, i32 1, i32 0, i32 2 + %4 = load i64, i64 addrspace(1)* %3, align 8 + br label %bb1 + +bb1: ; preds = %bb0 + %5 = add i64 %4, %input + ret i64 %5 +} + +; PEA: %0 = alloca { %TypeInfo*, { %ArrayBase, [33 x i64] } } +; PEA: %1 = alloca { %TypeInfo*, { %ArrayBase, [50 x i64] } } +; PEA: %2 = alloca { %TypeInfo*, { %ArrayBase, [0 x i64] } } +; PEA: %3 = alloca { %TypeInfo*, %ObjLayout._ZN7default8CrcCheck } +define i64 @foo() gc "cangjie" { +bb0: + %0 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default8CrcCheck.ti to i8*), i32 104) + %1 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 0) + %2 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 50) + %3 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 33) + %4 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 88 + %5 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 56 + %6 = bitcast i8 addrspace(1)* %5 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %1, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %6) + %7 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 64 + call void @llvm.memset.p1i8.i64(i8 addrspace(1)* noundef align 8 dereferenceable(16) %7, i8 0, i64 16, i1 false) + %8 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 8 + %9 = bitcast i8 addrspace(1)* %8 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %2, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %9) + %10 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 16 + store i8 0, i8 addrspace(1)* %10, align 8 + %11 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 24 + %12 = bitcast i8 addrspace(1)* %11 to i64 addrspace(1)* + store i64 5000, i64 addrspace(1)* %12, align 8 + %13 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 80 + %14 = bitcast i8 addrspace(1)* %13 to i64 addrspace(1)* + store i64 33, i64 addrspace(1)* %14, align 8 + %15 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 32 + %16 = bitcast i8 addrspace(1)* %15 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %3, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %16) + %17 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 40 + %18 = bitcast i8 addrspace(1)* %17 to i64 addrspace(1)* + store i64 0, i64 addrspace(1)* %18, align 8 + %19 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 48 + %20 = bitcast i8 addrspace(1)* %19 to i64 addrspace(1)* + %21 = load i8, i8* @_ZN7default20var_1715509008039_60E, align 8 + %22 = mul i8 %21, %21 + %23 = zext i8 %22 to i64 + %24 = call i64 @goo(i8 addrspace(1)* %0, i64 %23) + store i64 %24, i64 addrspace(1)* %20, align 8 + ret i64 %24 +} + +!0 = !{!"ArrayLayout.Int64"} +!1 = !{!"ObjLayout._ZN7default8CrcCheck"} + +attributes #0 = { argmemonly nounwind } +attributes #1 = { argmemonly nocallback nofree nounwind willreturn writeonly } diff --git a/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-escape-depth-spread.ll b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-escape-depth-spread.ll new file mode 100644 index 000000000..25dd3fa71 --- /dev/null +++ b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-escape-depth-spread.ll @@ -0,0 +1,168 @@ +; RUN: opt < %s '-passes=cj-pea' -cj-disable-partial-ea -S | FileCheck %s + +source_filename = "default" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%TypeInfo = type { i8*, i8, i8, i16, i32, %BitMap*, i32, i8, i8, i32*, i8*, i8*, i8*, %TypeInfo*, i8*, i8* } +%BitMap = type { i32, [0 x i8] } +%ObjLayout._ZN7default5HumanE = type { %"typeC" } +%ObjLayout.Env = type { i8 addrspace(1)* } +%"record._ZN8std$core5RangeItE" = type { i16, i16, i64, i1, i1, i1 } +%"record._ZN8std$core5RangeImE" = type { i64, i64, i64, i1, i1, i1 } +%"T4_R_ZN8std$core5RangeItEmcsE" = type { %"record._ZN8std$core5RangeItE", i64, i32, i16 } +%"typeB" = type { i16, %"T4_R_ZN8std$core5RangeItEmcsE", %"typeC", i8 addrspace(1)* } +%"typeC" = type { %"record._ZN8std$core5RangeImE", [2 x i8], i32, float, i1 } +%"record._ZN8std$core5ArrayIuE" = type { i8 addrspace(1)*, i64, i64 } + +@"std.core$Object.ti" = external global %TypeInfo +@Env.ti = weak_odr global %TypeInfo {i8* getelementptr inbounds ([12 x i8], [12 x i8]* @"default$Env.name", i32 0, i32 0), i8 -128, i8 0, i16 2, i32 16, %BitMap* null, i32 0, i8 8, i8 0, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @"default$Env.ti.offsets", i32 0, i32 0), i8* null, i8* null, i8* bitcast ([1 x %TypeInfo*]* @"default$Env.ti.fields" to i8*), %TypeInfo* @"std.core$Object.ti", i8* null, i8* bitcast ([1 x i8*]* @"default$Env.fieldNames" to i8*)}, !RelatedType !0 +@"default$Env.name" = internal global [12 x i8] c"default$Env\00", align 1 +@"default$Env.ti.offsets" = internal global [1 x i32] [i32 0] +@"default$Env.ti.fields" = internal global [1 x %TypeInfo*] [%TypeInfo* @"std.core$Object.ti"] +@"default$Env.fieldNames" = internal global [1 x i8*] [i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"default$Env.field.1.name", i32 0, i32 0)] +@"default$Env.field.1.name" = internal global [3 x i8] c"aa\00", align 1 + +@_ZN7default5HumanE.ti = weak_odr global %TypeInfo {i8* getelementptr inbounds ([14 x i8], [14 x i8]* @"default$Human.name", i32 0, i32 0), i8 -128, i8 0, i16 2, i32 16, %BitMap* null, i32 0, i8 8, i8 0, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @"default$Human.ti.offsets", i32 0, i32 0), i8* null, i8* null, i8* bitcast ([1 x %TypeInfo*]* @"default$Human.ti.fields" to i8*), %TypeInfo* @"std.core$Object.ti", i8* null, i8* bitcast ([1 x i8*]* @"default$Human.fieldNames" to i8*)}, !RelatedType !1 +@"default$Human.name" = internal global [14 x i8] c"default$Human\00", align 1 +@"default$Human.ti.offsets" = internal global [1 x i32] [i32 0] +@"default$Human.ti.fields" = internal global [1 x %TypeInfo*] [%TypeInfo* @"std.core$Object.ti"] +@"default$Human.fieldNames" = internal global [1 x i8*] [i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"default$Human.field.1.name", i32 0, i32 0)] +@"default$Human.field.1.name" = internal global [3 x i8] c"aa\00" + +define void @"func"(%"typeB"* noalias nocapture writeonly sret(%"typeB") %0, i8 addrspace(1)* nocapture readnone %"param$BP", %"record._ZN8std$core5ArrayIuE" addrspace(1)* nocapture readnone %param) #25 gc "cangjie" { +; CHECK-LABEL: @func( +; CHECK-LABEL: entry +; CHECK-NEXT: %1 = alloca { %TypeInfo*, %ObjLayout.Env }, align 8 +; CHECK-NEXT: %2 = alloca %typeB, align 8 +entry: + %1 = alloca %"typeB", align 8 + %2 = alloca %"typeC", align 8 + %callRet = alloca %"typeB", align 8 + %3 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default5HumanE.ti to i8*), i32 56) + %4 = bitcast %"typeC"* %2 to i8* + call void @llvm.lifetime.start.p0i8(i64 48, i8* nonnull %4) + %atom.sroa.0.0..sroa_idx.i = getelementptr inbounds %"typeC", %"typeC"* %2, i64 0, i32 0, i32 0 + store i64 107, i64* %atom.sroa.0.0..sroa_idx.i, align 8 + %atom.sroa.3.0..sroa_idx3.i = getelementptr inbounds %"typeC", %"typeC"* %2, i64 0, i32 0, i32 1 + store i64 123, i64* %atom.sroa.3.0..sroa_idx3.i, align 8 + %atom.sroa.4.0..sroa_idx6.i = getelementptr inbounds %"typeC", %"typeC"* %2, i64 0, i32 0, i32 2 + store i64 -57, i64* %atom.sroa.4.0..sroa_idx6.i, align 8 + %atom.sroa.5.0..sroa_idx9.i = getelementptr inbounds %"typeC", %"typeC"* %2, i64 0, i32 0, i32 3 + store i1 true, i1* %atom.sroa.5.0..sroa_idx9.i, align 8 + %atom.sroa.6.0..sroa_idx12.i = getelementptr inbounds %"typeC", %"typeC"* %2, i64 0, i32 0, i32 4 + store i1 true, i1* %atom.sroa.6.0..sroa_idx12.i, align 1 + %atom.sroa.7.0..sroa_idx15.i = getelementptr inbounds %"typeC", %"typeC"* %2, i64 0, i32 0, i32 5 + store i1 true, i1* %atom.sroa.7.0..sroa_idx15.i, align 2 + %atom.sroa.8.0..sroa_raw_cast.i = bitcast %"typeC"* %2 to i8* + %atom.sroa.8.0..sroa_raw_idx.i = getelementptr inbounds i8, i8* %atom.sroa.8.0..sroa_raw_cast.i, i64 27 + call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(5) %atom.sroa.8.0..sroa_raw_idx.i, i8 0, i64 5, i1 false) + %5 = getelementptr inbounds %"typeC", %"typeC"* %2, i64 0, i32 1, i64 0 + %6 = bitcast i8* %5 to i16* + store i16 4883, i16* %6, align 8 + %7 = getelementptr inbounds %"typeC", %"typeC"* %2, i64 0, i32 2 + store i32 -52, i32* %7, align 4 + %8 = getelementptr inbounds %"typeC", %"typeC"* %2, i64 0, i32 3 + store float 5.700000e+01, float* %8, align 8 + %9 = getelementptr inbounds %"typeC", %"typeC"* %2, i64 0, i32 4 + store i1 true, i1* %9, align 4 + %10 = getelementptr inbounds i8, i8 addrspace(1)* %3, i64 8 + call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* %3, i8 addrspace(1)* %10, i8* nonnull %atom.sroa.8.0..sroa_raw_cast.i, i64 48) + %11 = bitcast %"typeC"* %2 to i8* + call void @llvm.lifetime.end.p0i8(i64 48, i8* nonnull %11) + %12 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Env.ti to i8*), i32 16) + %13 = getelementptr inbounds i8, i8 addrspace(1)* %12, i64 8 + %14 = bitcast i8 addrspace(1)* %13 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %3, i8 addrspace(1)* %12, i8 addrspace(1)* addrspace(1)* %14) + %15 = bitcast %"typeB"* %callRet to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %15, i8 0, i64 104, i1 false) + %16 = bitcast %"typeB"* %1 to i8* + call void @llvm.lifetime.start.p0i8(i64 104, i8* nonnull %16) + %17 = getelementptr inbounds i8, i8 addrspace(1)* %12, i64 8 + %18 = bitcast i8 addrspace(1)* %17 to i8 addrspace(1)* addrspace(1)* + %19 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %18, align 8 + %20 = getelementptr inbounds i8, i8 addrspace(1)* %19, i64 8 + %21 = bitcast %"typeB"* %1 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %21, i8 0, i64 104, i1 false) + %22 = getelementptr inbounds %"typeB", %"typeB"* %1, i64 0, i32 0 + store i16 147, i16* %22, align 8 + %tryvalue.sroa.0.0..sroa_idx.i = getelementptr inbounds %"typeB", %"typeB"* %1, i64 0, i32 1 + %tryvalue.sroa.0.0..sroa_cast.i = bitcast %"T4_R_ZN8std$core5RangeItEmcsE"* %tryvalue.sroa.0.0..sroa_idx.i to i8* + %tryvalue.sroa.0.sroa.0.0.tryvalue.sroa.0.0..sroa_cast.sroa_idx.i = getelementptr inbounds %"typeB", %"typeB"* %1, i64 0, i32 1, i32 0, i32 0 + store i16 33, i16* %tryvalue.sroa.0.sroa.0.0.tryvalue.sroa.0.0..sroa_cast.sroa_idx.i, align 8 + %tryvalue.sroa.0.sroa.2.0.tryvalue.sroa.0.0..sroa_cast.sroa_idx35.i = getelementptr inbounds %"typeB", %"typeB"* %1, i64 0, i32 1, i32 0, i32 1 + store i16 27, i16* %tryvalue.sroa.0.sroa.2.0.tryvalue.sroa.0.0..sroa_cast.sroa_idx35.i, align 2 + %tryvalue.sroa.0.sroa.3.0.tryvalue.sroa.0.0..sroa_cast.sroa_idx.i = getelementptr inbounds i8, i8* %tryvalue.sroa.0.0..sroa_cast.i, i64 4 + %tryvalue.sroa.0.sroa.3.0.tryvalue.sroa.0.0..sroa_cast.sroa_cast.i = bitcast i8* %tryvalue.sroa.0.sroa.3.0.tryvalue.sroa.0.0..sroa_cast.sroa_idx.i to i32* + store i32 0, i32* %tryvalue.sroa.0.sroa.3.0.tryvalue.sroa.0.0..sroa_cast.sroa_cast.i, align 4 + %tryvalue.sroa.0.sroa.4.0.tryvalue.sroa.0.0..sroa_cast.sroa_idx36.i = getelementptr inbounds %"typeB", %"typeB"* %1, i64 0, i32 1, i32 0, i32 2 + store i64 7, i64* %tryvalue.sroa.0.sroa.4.0.tryvalue.sroa.0.0..sroa_cast.sroa_idx36.i, align 8 + %tryvalue.sroa.0.sroa.5.0.tryvalue.sroa.0.0..sroa_cast.sroa_idx37.i = getelementptr inbounds %"typeB", %"typeB"* %1, i64 0, i32 1, i32 0, i32 3 + store i1 true, i1* %tryvalue.sroa.0.sroa.5.0.tryvalue.sroa.0.0..sroa_cast.sroa_idx37.i, align 8 + %tryvalue.sroa.0.sroa.6.0.tryvalue.sroa.0.0..sroa_cast.sroa_idx38.i = getelementptr inbounds %"typeB", %"typeB"* %1, i64 0, i32 1, i32 0, i32 4 + store i1 true, i1* %tryvalue.sroa.0.sroa.6.0.tryvalue.sroa.0.0..sroa_cast.sroa_idx38.i, align 1 + %tryvalue.sroa.0.sroa.7.0.tryvalue.sroa.0.0..sroa_cast.sroa_idx39.i = getelementptr inbounds %"typeB", %"typeB"* %1, i64 0, i32 1, i32 0, i32 5 + store i1 true, i1* %tryvalue.sroa.0.sroa.7.0.tryvalue.sroa.0.0..sroa_cast.sroa_idx39.i, align 2 + %tryvalue.sroa.0.sroa.8.0.tryvalue.sroa.0.0..sroa_cast.sroa_raw_idx.i = getelementptr inbounds i8, i8* %21, i64 27 + call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(5) %tryvalue.sroa.0.sroa.8.0.tryvalue.sroa.0.0..sroa_cast.sroa_raw_idx.i, i8 0, i64 5, i1 false) + %tryvalue.sroa.2.0..sroa_idx14.i = getelementptr inbounds %"typeB", %"typeB"* %1, i64 0, i32 1, i32 1 + store i64 5, i64* %tryvalue.sroa.2.0..sroa_idx14.i, align 8 + %tryvalue.sroa.3.0..sroa_idx15.i = getelementptr inbounds %"typeB", %"typeB"* %1, i64 0, i32 1, i32 2 + store i32 121, i32* %tryvalue.sroa.3.0..sroa_idx15.i, align 8 + %tryvalue.sroa.4.0..sroa_idx16.i = getelementptr inbounds %"typeB", %"typeB"* %1, i64 0, i32 1, i32 3 + store i16 53, i16* %tryvalue.sroa.4.0..sroa_idx16.i, align 4 + %23 = getelementptr inbounds %"typeB", %"typeB"* %1, i64 0, i32 2 + %24 = bitcast %"typeC"* %23 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(48) %24, i8 addrspace(1)* noundef align 8 dereferenceable(48) %20, i64 48, i1 false) + %25 = getelementptr inbounds %"typeB", %"typeB"* %1, i64 0, i32 3 + store i8 addrspace(1)* %19, i8 addrspace(1)** %25, align 8 + %26 = bitcast %"typeB"* %callRet to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(104) %26, i8* noundef nonnull align 8 dereferenceable(104) %21, i64 104, i1 false) + %27 = bitcast %"typeB"* %1 to i8* + call void @llvm.lifetime.end.p0i8(i64 104, i8* nonnull %27) + %28 = bitcast %"typeB"* %0 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(104) %28, i8* noundef nonnull align 8 dereferenceable(104) %15, i64 104, i1 false) + ret void +} + +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) #3 + +; Function Attrs: argmemonly nounwind +declare void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)*, i8 addrspace(1)* noalias nocapture writeonly, i8* noalias nocapture readonly, i64) #4 + +; Function Attrs: argmemonly nounwind +declare void @llvm.cj.gcwrite.struct.p1i8(i8 addrspace(1)*, i8 addrspace(1)* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64) #4 + +; Function Attrs: argmemonly nounwind +declare void @llvm.cj.gcwrite.ref(i8 addrspace(1)*, i8 addrspace(1)* nocapture, i8 addrspace(1)* addrspace(1)* nocapture) #4 + +; Function Attrs: nounwind +declare void @llvm.cj.memset.p0i8(i8*, i8, i64, i1) #5 + +; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #6 + +; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #6 + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn writeonly +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #7 + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #8 + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn +declare void @llvm.memcpy.p0i8.p1i8.i64(i8* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) #8 + +!0 = !{!"ObjLayout.Env"} +!1 = !{!"ObjLayout._ZN7default5HumanE"} + +attributes #0 = { "CFileKlass" } +attributes #1 = { "CFileVTable" } +attributes #2 = { "CFileITable" } +attributes #3 = { "cj-runtime" } +attributes #4 = { argmemonly nounwind } +attributes #5 = { nounwind } +attributes #6 = { argmemonly nocallback nofree nosync nounwind willreturn } +attributes #7 = { argmemonly nocallback nofree nounwind willreturn writeonly } +attributes #8 = { argmemonly nocallback nofree nounwind willreturn } diff --git a/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-gcread.ll b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-gcread.ll new file mode 100644 index 000000000..8af8b2bd8 --- /dev/null +++ b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-gcread.ll @@ -0,0 +1,122 @@ +; RUN: opt < %s '-passes=cj-pea' -cj-disable-partial-ea -S | FileCheck %s --check-prefixes=PEA +; RUN: opt < %s '-passes=cj-pea' -S | FileCheck %s --check-prefixes=Partial + +%record._ZN8std.core6StringE = type { %record._ZN8std.core5ArrayIhE, i64 } +%record._ZN8std.core5ArrayIhE = type { i8 addrspace(1)*, i64, i64 } +%BitMap = type { i32, [0 x i8] } +%TypeTemplate = type { i8*, i8, i8, i16, i16, i8*, i8*, i8*, i8* } +%default.pkgInfoType = type { i32, i32, i32, i32, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8* } +%TypeInfo = type { i8*, i8, i8, i16, i32, %BitMap*, i32, i8, i8, i32*, i8*, i8*, i8*, %TypeInfo*, i8*, i8* } +%ArrayLayout.refArray = type { %ArrayBase, [0 x i8 addrspace(1)*] } +%ArrayLayout.UInt8 = type { %ArrayBase, [0 x i8] } +%ArrayBase = type { i64 } +%ObjLayout._ZN7default3ObjE = type {i64, i64} +@roUInt8.arrayKlass = weak_odr global %TypeInfo { i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"RawArray.name", i32 0, i32 0), i8 -126, i8 0, i16 0, i32 0, %BitMap* null, i32 0, i8 0, i8 0, i32* null, i8* bitcast (%TypeTemplate* @RawArray.tt to i8*), i8* null, i8* null, %TypeInfo* @UInt8.ti, i8* null, i8* null }, !RelatedType !1 +@"RawArray.name" = internal global [16 x i8] c"RawArray\00", align 1 +@"$const_cjstring_data.0" = internal constant { i8*, i64, [533 x i8] } { i8* bitcast (%TypeInfo* @roUInt8.arrayKlass to i8*), i64 533, [533 x i8] c"\09ms Out of memory is not equal to array size:, length is , start is , step should be 1.An exception has occurred:Array negative index accessCopy length out of boundsDstStart is greater than or equal to the size of the target arrayIllegal step JohnNegative copy lengthProperty Access - NoneExtension:\09Range size:SrcStart is greater than or equal to the size of this arrayThe result would be greater than UInt32.Max.The result would be less than UInt32.Min.The size of Array is UNYaddmulsubthe value of the step should not be zero.\0A" } +@A1_C_ZN7default3ObjEE.typeName = weak_odr global [22 x i8] c"A1_C_ZN7default3ObjEE\00", align 1 +@_ZN7default3ObjE.ti = internal global %TypeInfo {i8* getelementptr inbounds ([12 x i8], [12 x i8]* @"default$Obj.name", i32 0, i32 0), i8 -128, i8 0, i16 2, i32 16, %BitMap* null, i32 0, i8 8, i8 0, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @"default$Obj.ti.offsets", i32 0, i32 0), i8* null, i8* null, i8* bitcast ([1 x %TypeInfo*]* @"default$Obj.ti.fields" to i8*), %TypeInfo* @"std.core$Object.ti", i8* null, i8* bitcast ([1 x i8*]* @"default$Obj.fieldNames" to i8*) }, !RelatedType !2 +@"default$Obj.name" = internal global [12 x i8] c"default$Obj\00", align 1 +@"default$Obj.ti.offsets" = internal global [1 x i32] [i32 0] +@"default$Obj.ti.fields" = internal global [1 x %TypeInfo*] [%TypeInfo* @"std.core$Object.ti"] +@"default$Obj.fieldNames" = internal global [1 x i8*] [i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"default$Obj.field.1.name", i32 0, i32 0)] +@"default$Obj.field.1.name" = internal global [3 x i8] c"aa\00", align 1 +@"RawArray.ti" = internal global %TypeInfo { i8* getelementptr inbounds ([14 x i8], [14 x i8]* @"RawArray.name", i32 0, i32 0), i8 -126, i8 0, i16 0, i32 8, %BitMap* null, i32 0, i8 8, i8 0, i32* null, i8* bitcast (%TypeTemplate* @RawArray.tt to i8*), i8* null, i8* null, %TypeInfo* @"std.core$Object.ti", i8* null, i8* null }, !RelatedType !0 +@"RawArray.name" = internal global [14 x i8] c"RawArray\00" +@RawArray.tt = external global %TypeTemplate +@"std.core$Object.ti" = external global %TypeInfo +@"$const_cjstring.21" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [533 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 246, i64 4 }, i64 0 } +@UInt8.ti = external global %TypeInfo + +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) +declare void @llvm.cj.gcwrite.ref(i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*) +declare void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)*, i8*, i64, i1) +declare i8 addrspace(1)* @CJ_MCC_NewArray(i8*, i64) +declare i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*) +declare i1 @"_ZN7default14$noneextension6Person5equalEC_ZN7default14$noneextension6PersonE"(i8 addrspace(1)*, i8 addrspace(1)*) +declare void @llvm.memcpy.p0i8.p1i8.i64(i8*, i8 addrspace(1)*, i64, i1) + +; PEA: %0 = alloca { %TypeInfo*, { %ArrayBase, [3 x i8 addrspace(1)*] } } +; PEA: %person1 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default3ObjE.ti to i8*), i32 48) +; PEA: %person2 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default3ObjE.ti to i8*), i32 48) +; PEA: %person3 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default3ObjE.ti to i8*), i32 48) +; Partial: %0 = alloca { %TypeInfo*, %ObjLayout._ZN7default3ObjE } +; Partial: %1 = alloca { %TypeInfo*, %ObjLayout._ZN7default3ObjE } +; Partial: %2 = alloca { %TypeInfo*, %ObjLayout._ZN7default3ObjE } +; Partial: %3 = alloca { %TypeInfo*, { %ArrayBase, [3 x i8 addrspace(1)*] } } +define void @foo() gc "cangjie" { +bb0: + %a0 = alloca %record._ZN8std.core6StringE + %a1 = alloca %record._ZN8std.core6StringE + %person1 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default3ObjE.ti to i8*), i32 48) + %0 = getelementptr inbounds i8, i8 addrspace(1)* %person1, i64 8 + %1 = bitcast i8 addrspace(1)* %0 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [533 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i8 addrspace(1)* %person1, i8 addrspace(1)* addrspace(1)* %1) + %2 = getelementptr inbounds i8, i8 addrspace(1)* %person1, i64 16 + call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* noundef align 8 dereferenceable(24) %2, i8* noundef nonnull align 8 dereferenceable(24) bitcast (i64* getelementptr inbounds ({ { i8 addrspace(1)*, i64, i64 }, i64 }, { { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.21", i64 0, i32 0, i32 1) to i8*), i64 24, i1 false) + %3 = getelementptr inbounds i8, i8 addrspace(1)* %person1, i64 40 + %4 = bitcast i8 addrspace(1)* %3 to i64 addrspace(1)* + store i64 12, i64 addrspace(1)* %4, align 8 + %person2 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default3ObjE.ti to i8*), i32 48) + %5 = getelementptr inbounds i8, i8 addrspace(1)* %person2, i64 8 + %6 = bitcast i8 addrspace(1)* %5 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [533 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i8 addrspace(1)* %person2, i8 addrspace(1)* addrspace(1)* %6) + %7 = getelementptr inbounds i8, i8 addrspace(1)* %person2, i64 16 + call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* noundef align 8 dereferenceable(24) %7, i8* noundef nonnull align 8 dereferenceable(24) bitcast (i64* getelementptr inbounds ({ { i8 addrspace(1)*, i64, i64 }, i64 }, { { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.21", i64 0, i32 0, i32 1) to i8*), i64 24, i1 false) + %8 = getelementptr inbounds i8, i8 addrspace(1)* %person2, i64 40 + %9 = bitcast i8 addrspace(1)* %8 to i64 addrspace(1)* + store i64 13, i64 addrspace(1)* %9, align 8 + %person3 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default3ObjE.ti to i8*), i32 48) + %10 = getelementptr inbounds i8, i8 addrspace(1)* %person3, i64 8 + %11 = bitcast i8 addrspace(1)* %10 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [533 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i8 addrspace(1)* %person3, i8 addrspace(1)* addrspace(1)* %11) + %12 = getelementptr inbounds i8, i8 addrspace(1)* %person3, i64 16 + %13 = getelementptr inbounds i8, i8 addrspace(1)* %person3, i64 40 + %14 = bitcast i8 addrspace(1)* %13 to i64 addrspace(1)* + store i64 14, i64 addrspace(1)* %14, align 8 + %15 = call noalias i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 3) + %16 = bitcast i8 addrspace(1)* %15 to %ArrayLayout.refArray addrspace(1)* + %17 = getelementptr inbounds i8, i8 addrspace(1)* %15, i64 16 + %18 = bitcast i8 addrspace(1)* %17 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %person1, i8 addrspace(1)* %15, i8 addrspace(1)* addrspace(1)* %18) + %19 = getelementptr inbounds i8, i8 addrspace(1)* %15, i64 24 + %20 = bitcast i8 addrspace(1)* %19 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %person2, i8 addrspace(1)* %15, i8 addrspace(1)* addrspace(1)* %20) + %21 = getelementptr inbounds i8, i8 addrspace(1)* %15, i64 32 + %22 = bitcast i8 addrspace(1)* %21 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %person3, i8 addrspace(1)* %15, i8 addrspace(1)* addrspace(1)* %22) + br label %loop1 + +loop1: + %.0 = phi i64 [0, %bb0], [%Srem, %loop1] + %icmpslt = icmp slt i64 %.0, 100000 + %Srem = srem i64 %.0, 3 + %23 = getelementptr inbounds %ArrayLayout.refArray, %ArrayLayout.refArray addrspace(1)* %16, i64 0, i32 1, i64 %Srem + %a = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %15, i8 addrspace(1)* addrspace(1)* %23) + %24 = getelementptr inbounds i8, i8 addrspace(1)* %a, i64 8 + %25 = bitcast i8 addrspace(1)* %24 to i8 addrspace(1)* addrspace(1)* + %26 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %a, i8 addrspace(1)* addrspace(1)* %25) + %27 = getelementptr inbounds %record._ZN8std.core6StringE, %record._ZN8std.core6StringE* %a1, i64 0, i32 0, i32 0 + store i8 addrspace(1)* %26, i8 addrspace(1)** %27, align 8 + %28 = getelementptr inbounds %record._ZN8std.core6StringE, %record._ZN8std.core6StringE* %a1, i64 0, i32 0, i32 1 + %29 = bitcast i64* %28 to i8* + %30 = getelementptr inbounds i8, i8 addrspace(1)* %a, i64 16 + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(24) %29, i8 addrspace(1)* noundef align 8 dereferenceable(24) %30, i64 24, i1 false) + %31 = getelementptr inbounds i8, i8 addrspace(1)* %person1, i64 8 + %32 = bitcast i8 addrspace(1)* %31 to i8 addrspace(1)* addrspace(1)* + %33 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %person1, i8 addrspace(1)* addrspace(1)* %32) + %34 = getelementptr inbounds %record._ZN8std.core6StringE, %record._ZN8std.core6StringE* %a0, i64 0, i32 0, i32 0 + store i8 addrspace(1)* %33, i8 addrspace(1)** %34, align 8 + %35 = getelementptr inbounds %record._ZN8std.core6StringE, %record._ZN8std.core6StringE* %a0, i64 0, i32 0, i32 1 + %36 = bitcast i64* %35 to i8* + %37 = getelementptr inbounds i8, i8 addrspace(1)* %person1, i64 16 + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(24) %36, i8 addrspace(1)* noundef align 8 dereferenceable(24) %37, i64 24, i1 false) + br i1 %icmpslt, label %loop1, label %bb1 + +bb1: + ret void +} + +!0 = !{!"ArrayLayout.refArray"} +!1 = !{!"ArrayLayout.UInt8"} +!2 = !{!"ObjLayout._ZN7default3ObjE"} diff --git a/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-loop-loop.ll b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-loop-loop.ll new file mode 100644 index 000000000..536b03636 --- /dev/null +++ b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-loop-loop.ll @@ -0,0 +1,114 @@ +; RUN: opt < %s '-passes=cj-pea' -cj-disable-partial-ea -S | FileCheck %s --check-prefixes=PEA +; RUN: opt < %s '-passes=cj-pea' -S | FileCheck %s --check-prefixes=Partial + +%BitMap = type { i32, [0 x i8] } +%_ZN7default8CrcCheckE.objKlass.reflectType = type { i32, i32, i32, i32, i32, i8* } +%ObjLayout._ZN7default8CrcCheck = type { %record._ZN8std.core5ArrayIlE, %record._ZN8std.core5ArrayIlE, %record._ZN8std.core5ArrayIlE, i64, i64, i64 } +%record._ZN8std.core5ArrayIlE = type { i8 addrspace(1)*, i64, i64 } +%ArrayLayout.Int64 = type { %ArrayBase, [0 x i64] } +%ArrayBase = type { i64 } +%TypeInfo = type { i8*, i8, i8, i16, i32, %BitMap*, i32, i8, i8, i32*, i8*, i8*, i8*, %TypeInfo*, i8*, i8* } +%TypeTemplate = type { i8*, i8, i8, i16, i16, i8*, i8*, i8*, i8* } + +@_ZN7default20var_1715509008039_60E = global i8 50, align 8 +@_ZN7default20var_1715509008039_60F = global i8 0, align 8 +@A1_lE.typeName = internal global [6 x i8] c"A1_lE\00", align 1 +@_ZN7default8CrcCheck.ti = internal global %TypeInfo {i8* getelementptr inbounds ([17 x i8], [17 x i8]* @"default$CrcCheck.name", i32 0, i32 0), i8 -128, i8 0, i16 2, i32 16, %BitMap* null, i32 0, i8 8, i8 0, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @"default$CrcCheck.ti.offsets", i32 0, i32 0), i8* null, i8* null, i8* bitcast ([1 x %TypeInfo*]* @"default$CrcCheck.ti.fields" to i8*), %TypeInfo* @"std.core$Object.ti", i8* null, i8* bitcast ([1 x i8*]* @"default$CrcCheck.fieldNames" to i8*)}, !RelatedType !1 +@"default$CrcCheck.name" = internal global [17 x i8] c"default$CrcCheck\00", align 1 +@"std.core$Object.ti" = external global %TypeInfo +@"default$CrcCheck.ti.offsets" = internal global [1 x i32] [i32 0] +@"default$CrcCheck.ti.fields" = internal global [1 x %TypeInfo*] [%TypeInfo* @"std.core$Object.ti"] +@"default$CrcCheck.fieldNames" = internal global [1 x i8*] [i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"default$CrcCheck.field.1.name", i32 0, i32 0)] +@"default$CrcCheck.field.1.name" = internal global [3 x i8] c"aa\00", align 1 +@"RawArray.ti" = internal global %TypeInfo { i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"RawArray.name", i32 0, i32 0), i8 -126, i8 0, i16 0, i32 8, %BitMap* null, i32 0, i8 8, i8 0, i32* null, i8* bitcast (%TypeTemplate* @RawArray.tt to i8*), i8* null, i8* null, %TypeInfo* @Int64.ti, i8* null, i8* null }, !RelatedType !0 +@"RawArray.name" = internal global [16 x i8] c"RawArray\00", align 1 +@RawArray.tt = external global %TypeTemplate +@Int64.ti = external global %TypeInfo + +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) + +declare i8 addrspace(1)* @CJ_MCC_NewArray(i8*, i64) + +; Function Attrs: argmemonly nounwind +declare void @llvm.cj.gcwrite.ref(i8 addrspace(1)*, i8 addrspace(1)* nocapture, i8 addrspace(1)* addrspace(1)* nocapture) #0 + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn writeonly +declare void @llvm.memset.p1i8.i64(i8 addrspace(1)* nocapture writeonly, i8, i64, i1 immarg) #1 + +; PEA: %0 = alloca { %TypeInfo*, %ObjLayout._ZN7default8CrcCheck } +; PEA: %5 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 0) +; PEA: %6 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 50) +; PEA: %7 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 33) +; Partial: %0 = alloca { %TypeInfo*, { %ArrayBase, [33 x i64] } }, align 8 +; Partial: %1 = alloca { %TypeInfo*, { %ArrayBase, [50 x i64] } }, align 8 +; Partial: %2 = alloca { %TypeInfo*, { %ArrayBase, [0 x i64] } }, align 8 +; Partial: %3 = alloca { %TypeInfo*, %ObjLayout._ZN7default8CrcCheck }, align 8 +define i64 @foo() gc "cangjie" { +bb0: + %0 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default8CrcCheck.ti to i8*), i32 104) + %1 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 0) + %2 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 50) + %3 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 33) + store i8 50, i8* @_ZN7default20var_1715509008039_60E, align 8 + %4 = load i8, i8* @_ZN7default20var_1715509008039_60E, align 8 + br label %bb1 + +bb1: + %phi1 = phi i8 addrspace(1)* [%1, %bb0], [%9, %bb3] + %phi2 = phi i8 addrspace(1)* [%2, %bb0], [%10, %bb3] + %phi3 = phi i8 addrspace(1)* [%3, %bb0], [%11, %bb3] + %phi4 = phi i8 [%4, %bb0], [%5, %bb3] + %icmp = icmp eq i8 %phi4, 0 + %5 = sub i8 %phi4, 1 + %6 = load i8, i8* @_ZN7default20var_1715509008039_60F, align 8 + br label %bb2 + +bb2: + %7 = phi i8 [%6, %bb1], [%8, %bb2] + %icmp1 = icmp eq i8 %7, 0 + %8 = sub i8 %7, 1 + %9 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 0) + %10 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 50) + %11 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 33) + br i1 %icmp1, label %bb3, label %bb2 + +bb3: + br i1 %icmp, label %bb4, label %bb1 + +bb4: + %12 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 88 + %13 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 56 + %14 = bitcast i8 addrspace(1)* %13 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %9, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %14) + %15 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 64 + call void @llvm.memset.p1i8.i64(i8 addrspace(1)* noundef align 8 dereferenceable(16) %15, i8 0, i64 16, i1 false) + %16 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 8 + %17 = bitcast i8 addrspace(1)* %16 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %10, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %17) + %18 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 16 + store i8 0, i8 addrspace(1)* %18, align 8 + %19 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 24 + %20 = bitcast i8 addrspace(1)* %19 to i64 addrspace(1)* + store i64 5000, i64 addrspace(1)* %20, align 8 + %21 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 80 + %22 = bitcast i8 addrspace(1)* %21 to i64 addrspace(1)* + store i64 33, i64 addrspace(1)* %22, align 8 + %23 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 32 + %24 = bitcast i8 addrspace(1)* %23 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %11, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %24) + %25 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 40 + %26 = bitcast i8 addrspace(1)* %25 to i64 addrspace(1)* + store i64 0, i64 addrspace(1)* %26, align 8 + %27 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 48 + %28 = bitcast i8 addrspace(1)* %27 to i64 addrspace(1)* + %29 = load i8, i8* @_ZN7default20var_1715509008039_60E, align 8 + %30 = mul i8 %29, %29 + %31 = zext i8 %30 to i64 + ret i64 %31 +} + +!0 = !{!"ArrayLayout.Int64"} +!1 = !{!"ObjLayout._ZN7default8CrcCheck"} + +attributes #0 = { argmemonly nounwind } +attributes #1 = { argmemonly nocallback nofree nounwind willreturn writeonly } diff --git a/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-loop.ll b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-loop.ll new file mode 100644 index 000000000..1a2e9710b --- /dev/null +++ b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-loop.ll @@ -0,0 +1,103 @@ +; RUN: opt < %s '-passes=cj-pea' -cj-disable-partial-ea -S | FileCheck %s --check-prefixes=PEA +; RUN: opt < %s '-passes=cj-pea' -S | FileCheck %s --check-prefixes=Partial + +%BitMap = type { i32, [0 x i8] } +%_ZN7default8CrcCheck.ti.reflectType = type { i32, i32, i32, i32, i32, i8* } +%ObjLayout._ZN7default8CrcCheck = type { %record._ZN8std.core5ArrayIlE, %record._ZN8std.core5ArrayIlE, %record._ZN8std.core5ArrayIlE, i64, i64, i64 } +%record._ZN8std.core5ArrayIlE = type { i8 addrspace(1)*, i64, i64 } +%ArrayLayout.Int64 = type { %ArrayBase, [0 x i64] } +%ArrayBase = type { i64 } +%TypeInfo = type { i8*, i8, i8, i16, i32, %BitMap*, i32, i8, i8, i32*, i8*, i8*, i8*, %TypeInfo*, i8*, i8* } +%TypeTemplate = type { i8*, i8, i8, i16, i16, i8*, i8*, i8*, i8* } + +@_ZN7default20var_1715509008039_60E = global i8 0, align 8 +@A1_lE.typeName = internal global [6 x i8] c"A1_lE\00", align 1 +@_ZN7default8CrcCheck.ti = internal global %TypeInfo {i8* getelementptr inbounds ([17 x i8], [17 x i8]* @"default$CrcCheck.name", i32 0, i32 0), i8 -128, i8 0, i16 2, i32 16, %BitMap* null, i32 0, i8 8, i8 0, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @"default$CrcCheck.ti.offsets", i32 0, i32 0), i8* null, i8* null, i8* bitcast ([1 x %TypeInfo*]* @"default$CrcCheck.ti.fields" to i8*), %TypeInfo* @"std.core$Object.ti", i8* null, i8* bitcast ([1 x i8*]* @"default$CrcCheck.fieldNames" to i8*)}, !RelatedType !1 +@"default$CrcCheck.name" = internal global [17 x i8] c"default$CrcCheck\00", align 1 +@"std.core$Object.ti" = external global %TypeInfo +@"default$CrcCheck.ti.offsets" = internal global [1 x i32] [i32 0] +@"default$CrcCheck.ti.fields" = internal global [1 x %TypeInfo*] [%TypeInfo* @"std.core$Object.ti"] +@"default$CrcCheck.fieldNames" = internal global [1 x i8*] [i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"default$CrcCheck.field.1.name", i32 0, i32 0)] +@"default$CrcCheck.field.1.name" = internal global [3 x i8] c"aa\00", align 1 +@"RawArray.ti" = internal global %TypeInfo { i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"RawArray.name", i32 0, i32 0), i8 -126, i8 0, i16 0, i32 8, %BitMap* null, i32 0, i8 8, i8 0, i32* null, i8* bitcast (%TypeTemplate* @RawArray.tt to i8*), i8* null, i8* null, %TypeInfo* @Int64.ti, i8* null, i8* null }, !RelatedType !0 +@"RawArray.name" = internal global [16 x i8] c"RawArray\00", align 1 +@RawArray.tt = external global %TypeTemplate +@Int64.ti = external global %TypeInfo + +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) + +declare i8 addrspace(1)* @CJ_MCC_NewArray(i8*, i64) + +; Function Attrs: argmemonly nounwind +declare void @llvm.cj.gcwrite.ref(i8 addrspace(1)*, i8 addrspace(1)* nocapture, i8 addrspace(1)* addrspace(1)* nocapture) #0 + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn writeonly +declare void @llvm.memset.p1i8.i64(i8 addrspace(1)* nocapture writeonly, i8, i64, i1 immarg) #1 + +; PEA: %0 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default8CrcCheck.ti to i8*), i32 104) +; PEA: %1 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 0) +; PEA: %2 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 50) +; PEA: %3 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 33) +; Partial: %0 = alloca { %TypeInfo*, { %ArrayBase, [33 x i64] } }, align 8 +; Partial: %1 = alloca { %TypeInfo*, { %ArrayBase, [50 x i64] } }, align 8 +; Partial: %2 = alloca { %TypeInfo*, { %ArrayBase, [0 x i64] } }, align 8 +; Partial: %3 = alloca { %TypeInfo*, %ObjLayout._ZN7default8CrcCheck }, align 8 +define i64 @foo() gc "cangjie" { +bb0: + %0 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @_ZN7default8CrcCheck.ti to i8*), i32 104) + %1 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 0) + %2 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 50) + %3 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 33) + store i8 50, i8* @_ZN7default20var_1715509008039_60E, align 8 + %4 = load i8, i8* @_ZN7default20var_1715509008039_60E, align 8 + br label %bb1 + +bb1: + %phi1 = phi i8 addrspace(1)* [%1, %bb0], [%23, %bb1] + %phi2 = phi i8 addrspace(1)* [%2, %bb0], [%24, %bb1] + %phi3 = phi i8 addrspace(1)* [%3, %bb0], [%25, %bb1] + %phi4 = phi i8 [%4, %bb0], [%5, %bb1] + %icmp = icmp eq i8 %phi4, 0 + %5 = sub i8 %phi4, 1 + %6 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 88 + %7 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 56 + %8 = bitcast i8 addrspace(1)* %7 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %phi1, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %8) + %9 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 64 + call void @llvm.memset.p1i8.i64(i8 addrspace(1)* noundef align 8 dereferenceable(16) %9, i8 0, i64 16, i1 false) + %10 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 8 + %11 = bitcast i8 addrspace(1)* %10 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %phi2, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %11) + %12 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 16 + store i8 0, i8 addrspace(1)* %12, align 8 + %13 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 24 + %14 = bitcast i8 addrspace(1)* %13 to i64 addrspace(1)* + store i64 5000, i64 addrspace(1)* %14, align 8 + %15 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 80 + %16 = bitcast i8 addrspace(1)* %15 to i64 addrspace(1)* + store i64 33, i64 addrspace(1)* %16, align 8 + %17 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 32 + %18 = bitcast i8 addrspace(1)* %17 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %phi3, i8 addrspace(1)* %0, i8 addrspace(1)* addrspace(1)* %18) + %19 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 40 + %20 = bitcast i8 addrspace(1)* %19 to i64 addrspace(1)* + store i64 0, i64 addrspace(1)* %20, align 8 + %21 = getelementptr inbounds i8, i8 addrspace(1)* %0, i64 48 + %22 = bitcast i8 addrspace(1)* %21 to i64 addrspace(1)* + %23 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 0) + %24 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 50) + %25 = call i8 addrspace(1)* @CJ_MCC_NewArray(i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*), i64 33) + br i1 %icmp, label %bb2, label %bb1 + +bb2: + %26 = load i8, i8* @_ZN7default20var_1715509008039_60E, align 8 + %27 = mul i8 %26, %26 + %28 = zext i8 %27 to i64 + ret i64 %28 +} + +!0 = !{!"ArrayLayout.Int64"} +!1 = !{!"ObjLayout._ZN7default8CrcCheck"} + +attributes #0 = { argmemonly nounwind } +attributes #1 = { argmemonly nocallback nofree nounwind willreturn writeonly } diff --git a/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-ret-as1.ll b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-ret-as1.ll new file mode 100644 index 000000000..455e21da3 --- /dev/null +++ b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-ret-as1.ll @@ -0,0 +1,76 @@ +; RUN: opt < %s '-passes=cj-pea' -cj-disable-partial-ea -S | FileCheck %s + +%BitMap = type { i32, [0 x i8] } +%TypeInfo = type { i8*, i8, i8, i16, i32, %BitMap*, i32, i8, i8, i32*, i8*, i8*, i8*, %TypeInfo*, i8*, i8* } +%ObjLayout._ZN7default8CrcCheck = type { %record._ZN8std.core5ArrayIlE, %record._ZN8std.core5ArrayIlE, %record._ZN8std.core5ArrayIlE, i64, i64, i64 } +%record._ZN8std.core5ArrayIlE = type { i8 addrspace(1)*, i64, i64 } + +@_ZN7default20var_1715509008039_60E = global i8 0, align 8 +@_ZN7default8CrcCheck.ti = internal global %TypeInfo {i8* getelementptr inbounds ([17 x i8], [17 x i8]* @"default$CrcCheck.name", i32 0, i32 0), i8 -128, i8 0, i16 2, i32 16, %BitMap* null, i32 0, i8 8, i8 0, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @"default$CrcCheck.ti.offsets", i32 0, i32 0), i8* null, i8* null, i8* bitcast ([1 x %TypeInfo*]* @"default$CrcCheck.ti.fields" to i8*), %TypeInfo* @"std.core$Object.ti", i8* null, i8* bitcast ([1 x i8*]* @"default$CrcCheck.fieldNames" to i8*)} +@"default$CrcCheck.name" = internal global [17 x i8] c"default$CrcCheck\00", align 1 +@"std.core$Object.ti" = external global %TypeInfo +@"default$CrcCheck.ti.offsets" = internal global [1 x i32] [i32 0] +@"default$CrcCheck.ti.fields" = internal global [1 x %TypeInfo*] [%TypeInfo* @"std.core$Object.ti"] +@"default$CrcCheck.fieldNames" = internal global [1 x i8*] [i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"default$CrcCheck.field.1.name", i32 0, i32 0)] +@"default$CrcCheck.field.1.name" = internal global [3 x i8] c"aa\00", align 1 +@_ZN7default10typeScriptE = global i8 addrspace(1)* null, align 8 + + +%record._ZN8std.core6StringE = type { %record._ZN8std.core5ArrayIhE, i64 } +%record._ZN8std.core5ArrayIhE = type { i8 addrspace(1)*, i64, i64 } +%record._ZN14std.collection12HashMapEntryIR_ZN8std.core6StringEN_ZN8std.core6OptionIC_ZN8std.core3AnyEEE = type { i64, i64, %record._ZN8std.core6StringE, i8 addrspace(1)* } + + + +declare i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8*, i32) + +declare i8 addrspace(1)* @CJ_MCC_NewArray64Stub(i64, i8*) + +declare i8 addrspace(1)* @llvm.cj.gcread.static.ref(i8 addrspace(1)** nocapture %0) + +declare void @llvm.cj.gcwrite.static.ref(i8 addrspace(1)* %0, i8 addrspace(1)** nocapture %1) + +define i8 addrspace(1)* @_ZN7default13getTypeScriptEv() gc "cangjie" { +bb0: + %0 = call i8 addrspace(1)* @llvm.cj.gcread.static.ref(i8 addrspace(1)** nonnull @_ZN7default10typeScriptE) + %.not = icmp eq i8 addrspace(1)* %0, null + br i1 %.not, label %bb3, label %bb2 + +bb3: + %1 = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%TypeInfo* @_ZN7default8CrcCheck.ti to i8*), i32 912) + call void @llvm.cj.gcwrite.static.ref(i8 addrspace(1)* %1, i8 addrspace(1)** nonnull @_ZN7default10typeScriptE) + %2 = call i8 addrspace(1)* @llvm.cj.gcread.static.ref(i8 addrspace(1)** nonnull @_ZN7default10typeScriptE) + %.not2 = icmp eq i8 addrspace(1)* %2, null + br i1 %.not2, label %bb4, label %bb2 + +bb2: + %.0 = phi i8 addrspace(1)* [ %0, %bb0 ], [ %2, %bb3 ] + ret i8 addrspace(1)* %.0 + +bb4: + unreachable +} + +; Function Attrs: argmemonly nounwind +declare void @llvm.cj.gcwrite.ref(i8 addrspace(1)*, i8 addrspace(1)* nocapture, i8 addrspace(1)* addrspace(1)* nocapture) #0 + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn writeonly +declare void @llvm.memset.p1i8.i64(i8 addrspace(1)* nocapture writeonly, i8, i64, i1 immarg) #1 + +declare i8 addrspace(1)* @CJ_MCC_NewArrayStub(i64 %0, i8* %1) + +define void @foo() gc "cangjie" { +; CHECK-LABEL: @foo( +; CHECK-LABEL: bb0 +; CHECK-NOT: %0 = alloca %ObjLayout._ZN7default8CrcCheckE +bb0: + %0 = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%TypeInfo* @_ZN7default8CrcCheck.ti to i8*), i32 24) + %1 = call i8 addrspace(1)* @_ZN7default13getTypeScriptEv() + %2 = getelementptr inbounds i8, i8 addrspace(1)* %1, i64 736 + %3 = bitcast i8 addrspace(1)* %2 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %0, i8 addrspace(1)* %1, i8 addrspace(1)* addrspace(1)* %3) + ret void +} + +attributes #0 = { argmemonly nounwind } +attributes #1 = { argmemonly nocallback nofree nounwind willreturn writeonly } diff --git a/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-stack-allocation.ll b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-stack-allocation.ll new file mode 100644 index 000000000..e90478822 --- /dev/null +++ b/llvm/test/Transforms/CJPartialEscapeAnalysis/pea-stack-allocation.ll @@ -0,0 +1,840 @@ +; RUN: opt < %s '-passes=cj-pea' -cj-disable-partial-ea -S | FileCheck %s + +%Unit.Type = type { i8 } + +declare void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)*) gc "cangjie" +declare void @"_ZN11std$FS$core6String23createStringFromLiteral"(%"record._ZN11std$FS$core6StringE"* sret(%"record._ZN11std$FS$core6StringE"), i8*, i64) gc "cangjie" +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) +declare void @llvm.cj.gcwrite.ref(i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*) +declare void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)*, i8 addrspace(1)*, i8* , i64) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1 immarg) +declare void @llvm.cj.memset(i8*, i8, i64, i1) +declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) +declare i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) gc "cangjie" +declare i8 addrspace(1)* @CJ_MCC_NewObjArray(i64, i8*) +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1 immarg) + +%TypeTemplate = type { i8*, i8, i8, i16, i16, i8*, i8*, i8*, i8* } +%TypeInfo = type { i8*, i8, i8, i16, i32, %BitMap*, i32, i8, i8, i32*, i8*, i8*, i8*, %TypeInfo*, i8*, i8* } +%BitMap = type { i32, [0 x i8] } +%ArrayBase = type { i64 } +%ObjLayout.Location = type { i8 addrspace(1)*, %"record._ZN7default11std$FS$core5ArrayIC_ZN7default5PointEE" } +%ObjLayout._ZN7default8LocationE = type { %TypeInfo*, i8 addrspace(1)*, %"record._ZN7default11std$FS$core5ArrayIC_ZN7default5PointEE" } +%"record._ZN7default11std$FS$core5ArrayIC_ZN7default5PointEE" = type { i8 addrspace(1)*, i64, i64 } +%"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" = type { %ArrayBase, [0 x %"record._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE"] } +%"record._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" = type { i8 addrspace(1)*, i64, i64 } +%ObjLayout.Point = type { i64, i64 } +%ObjLayout._ZN7default5PointE = type { %TypeInfo*, i64, i64 } +%ArrayLayout.refArray = type { %ArrayBase, [0 x i8 addrspace(1)*] } +%ObjLayout.Human = type { i8 addrspace(1)* } +%ObjLayout._ZN7default5HumanE = type { %TypeInfo*, i8 addrspace(1)* } +%"record._ZN11std$FS$core6StringE" = type { i8 addrspace(1)*, i64 } +%ObjLayout.people = type { i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* } +%ObjLayout.Except = type { i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* } + +@"std.core$Object.ti" = external global %TypeInfo +@people.ti = weak_odr global %TypeInfo {i8* getelementptr inbounds ([14 x i8], [14 x i8]* @"default$people.name", i32 0, i32 0), i8 -128, i8 0, i16 2, i32 16, %BitMap* null, i32 0, i8 8, i8 0, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @"default$people.ti.offsets", i32 0, i32 0), i8* null, i8* null, i8* bitcast ([1 x %TypeInfo*]* @"default$people.ti.fields" to i8*), %TypeInfo* @"std.core$Object.ti", i8* null, i8* bitcast ([1 x i8*]* @"default$people.fieldNames" to i8*)}, !RelatedType !1 +@"default$people.name" = internal global [14 x i8] c"default$peopl\00", align 1 +@"default$people.ti.offsets" = internal global [1 x i32] [i32 0] +@"default$people.ti.fields" = internal global [1 x %TypeInfo*] [%TypeInfo* @"std.core$Object.ti"] +@"default$people.fieldNames" = internal global [1 x i8*] [i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"default$people.field.1.name", i32 0, i32 0)] +@"default$people.field.1.name" = internal global [3 x i8] c"aa\00", align 1 + +@Location.ti = weak_odr global %TypeInfo {i8* getelementptr inbounds ([14 x i8], [14 x i8]* @"default$Location.name", i32 0, i32 0), i8 -128, i8 0, i16 2, i32 16, %BitMap* null, i32 0, i8 8, i8 0, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @"default$Location.ti.offsets", i32 0, i32 0), i8* null, i8* null, i8* bitcast ([1 x %TypeInfo*]* @"default$Location.ti.fields" to i8*), %TypeInfo* @"std.core$Object.ti", i8* null, i8* bitcast ([1 x i8*]* @"default$Location.fieldNames" to i8*)}, !RelatedType !2 +@"default$Location.name" = internal global [14 x i8] c"default$Locat\00", align 1 +@"default$Location.ti.offsets" = internal global [1 x i32] [i32 0] +@"default$Location.ti.fields" = internal global [1 x %TypeInfo*] [%TypeInfo* @"std.core$Object.ti"] +@"default$Location.fieldNames" = internal global [1 x i8*] [i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"default$Location.field.1.name", i32 0, i32 0)] +@"default$Location.field.1.name" = internal global [3 x i8] c"aa\00", align 1 + +@Point.ti = weak_odr global %TypeInfo {i8* getelementptr inbounds ([14 x i8], [14 x i8]* @"default$Point.name", i32 0, i32 0), i8 -128, i8 0, i16 2, i32 16, %BitMap* null, i32 0, i8 8, i8 0, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @"default$Point.ti.offsets", i32 0, i32 0), i8* null, i8* null, i8* bitcast ([1 x %TypeInfo*]* @"default$Point.ti.fields" to i8*), %TypeInfo* @"std.core$Object.ti", i8* null, i8* bitcast ([1 x i8*]* @"default$Point.fieldNames" to i8*)}, !RelatedType !3 +@"default$Point.name" = internal global [14 x i8] c"default$Point\00", align 1 +@"default$Point.ti.offsets" = internal global [1 x i32] [i32 0] +@"default$Point.ti.fields" = internal global [1 x %TypeInfo*] [%TypeInfo* @"std.core$Object.ti"] +@"default$Point.fieldNames" = internal global [1 x i8*] [i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"default$Point.field.1.name", i32 0, i32 0)] +@"default$Point.field.1.name" = internal global [3 x i8] c"aa\00", align 1 + +@"RawArray.ti" = internal global %TypeInfo { i8* getelementptr inbounds ([14 x i8], [14 x i8]* @"RawArray.name", i32 0, i32 0), i8 -126, i8 0, i16 0, i32 8, %BitMap* null, i32 0, i8 8, i8 0, i32* null, i8* bitcast (%TypeTemplate* @RawArray.tt to i8*), i8* null, i8* null, %TypeInfo* @"std.core$Object.ti", i8* null, i8* null }, !RelatedType !0 +@"RawArray.name" = internal global [14 x i8] c"RawArray\00" +@RawArray.tt = external global %TypeTemplate + +@Except.ti = weak_odr global %TypeInfo {i8* getelementptr inbounds ([14 x i8], [14 x i8]* @"default$Except.name", i32 0, i32 0), i8 -128, i8 0, i16 2, i32 16, %BitMap* null, i32 0, i8 8, i8 0, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @"default$Except.ti.offsets", i32 0, i32 0), i8* null, i8* null, i8* bitcast ([1 x %TypeInfo*]* @"default$Except.ti.fields" to i8*), %TypeInfo* @"std.core$Object.ti", i8* null, i8* bitcast ([1 x i8*]* @"default$Except.fieldNames" to i8*)}, !RelatedType !4 +@"default$Except.name" = internal global [14 x i8] c"default$Excep\00", align 1 +@"default$Except.ti.offsets" = internal global [1 x i32] [i32 0] +@"default$Except.ti.fields" = internal global [1 x %TypeInfo*] [%TypeInfo* @"std.core$Object.ti"] +@"default$Except.fieldNames" = internal global [1 x i8*] [i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"default$Except.field.1.name", i32 0, i32 0)] +@"default$Except.field.1.name" = internal global [3 x i8] c"aa\00" + +@Human.ti = weak_odr global %TypeInfo {i8* getelementptr inbounds ([14 x i8], [14 x i8]* @"default$Human.name", i32 0, i32 0), i8 -128, i8 0, i16 2, i32 16, %BitMap* null, i32 0, i8 8, i8 0, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @"default$Human.ti.offsets", i32 0, i32 0), i8* null, i8* null, i8* bitcast ([1 x %TypeInfo*]* @"default$Human.ti.fields" to i8*), %TypeInfo* @"std.core$Object.ti", i8* null, i8* bitcast ([1 x i8*]* @"default$Human.fieldNames" to i8*)}, !RelatedType !5 +@"default$Human.name" = internal global [14 x i8] c"default$Human\00", align 1 +@"default$Human.ti.offsets" = internal global [1 x i32] [i32 0] +@"default$Human.ti.fields" = internal global [1 x %TypeInfo*] [%TypeInfo* @"std.core$Object.ti"] +@"default$Human.fieldNames" = internal global [1 x i8*] [i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"default$Human.field.1.name", i32 0, i32 0)] +@"default$Human.field.1.name" = internal global [3 x i8] c"aa\00" + +define void @_ZN7default5Human12reproductionEC_ZN7default6PlanetEC_ZN7default6PlanetE(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %this, i8 addrspace(1)* %s, i8 addrspace(1)* %ns) gc "cangjie" { +; CHECK-LABEL: @_ZN7default5Human12reproductionEC_ZN7default6PlanetEC_ZN7default6PlanetE( +; CHECK-LABEL: entry +; CHECK-NEXT: %1 = alloca { %TypeInfo*, %ObjLayout.people }, align 8 +; CHECK-NEXT: %2 = alloca { %TypeInfo*, %ObjLayout.Human }, align 8 +; CHECK-NEXT: %3 = alloca { %TypeInfo*, %ObjLayout.Human }, align 8 +; CHECK-NEXT: %4 = alloca { %TypeInfo*, %ObjLayout.Human }, align 8 +; CHECK-NOT: @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @people.ti to i8*) + +entry: + %callRet = alloca %Unit.Type, align 8 + %1 = call i64 @_ZN7default5Human14countNeighborsEC_ZN7default6PlanetE(i8 addrspace(1)* %this, i8 addrspace(1)* %s) + %2 = and i64 %1, -2 + %3 = icmp eq i64 %2, 2 + br i1 %3, label %land.rhs2, label %if1558end + +land.rhs2: ; preds = %entry + %4 = getelementptr inbounds i8, i8 addrspace(1)* %this, i64 8 + %5 = bitcast i8 addrspace(1)* %4 to %ObjLayout._ZN7default8LocationE addrspace(1)* addrspace(1)* + %6 = load %ObjLayout._ZN7default8LocationE addrspace(1)*, %ObjLayout._ZN7default8LocationE addrspace(1)* addrspace(1)* %5, align 8 + %7 = getelementptr inbounds %ObjLayout._ZN7default8LocationE, %ObjLayout._ZN7default8LocationE addrspace(1)* %6, i64 0, i32 1 + %8 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %7, align 8 + %9 = call i1 @_ZN7default6Planet17isPopulationAliveEC_ZN7default5PointE(i8 addrspace(1)* %ns, i8 addrspace(1)* %8) + br i1 %9, label %if1558end, label %if1558then + +if1558then: ; preds = %land.rhs2 + %10 = bitcast i8 addrspace(1)* %4 to %ObjLayout._ZN7default8LocationE addrspace(1)* addrspace(1)* + %11 = load %ObjLayout._ZN7default8LocationE addrspace(1)*, %ObjLayout._ZN7default8LocationE addrspace(1)* addrspace(1)* %10, align 8 + %12 = getelementptr inbounds %ObjLayout._ZN7default8LocationE, %ObjLayout._ZN7default8LocationE addrspace(1)* %11, i64 0, i32 1 + %13 = bitcast i8 addrspace(1)* addrspace(1)* %12 to %ObjLayout._ZN7default5PointE addrspace(1)* addrspace(1)* + %14 = load %ObjLayout._ZN7default5PointE addrspace(1)*, %ObjLayout._ZN7default5PointE addrspace(1)* addrspace(1)* %13, align 8 + %15 = getelementptr inbounds i8, i8 addrspace(1)* %ns, i64 16 + %16 = getelementptr inbounds %ObjLayout._ZN7default5PointE, %ObjLayout._ZN7default5PointE addrspace(1)* %14, i64 0, i32 1 + %17 = load i64, i64 addrspace(1)* %16, align 8 + %icmpslt5 = icmp slt i64 %17, 0 + br i1 %icmpslt5, label %if7477then, label %lor.rhs + +lor.rhs: ; preds = %if1558then + %18 = getelementptr inbounds i8, i8 addrspace(1)* %ns, i64 32 + %19 = bitcast i8 addrspace(1)* %18 to i64 addrspace(1)* + %20 = load i64, i64 addrspace(1)* %19, align 8 + %icmpsge.not = icmp slt i64 %17, %20 + br i1 %icmpsge.not, label %if7477end, label %if7477then + +if7477then: ; preds = %if1558then, %lor.rhs + %21 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Except.ti to i8*), i32 56) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %21) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %21) + unreachable + +if7477end: ; preds = %lor.rhs + %22 = bitcast i8 addrspace(1)* %15 to %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)* addrspace(1)* + %23 = load %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)*, %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)* addrspace(1)* %22, align 8 + %24 = getelementptr inbounds i8, i8 addrspace(1)* %ns, i64 24 + %25 = bitcast i8 addrspace(1)* %24 to i64 addrspace(1)* + %26 = load i64, i64 addrspace(1)* %25, align 8 + %add = add i64 %26, %17 + %arr.get.sroa.0.0..sroa_idx = getelementptr inbounds %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE", %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)* %23, i64 0, i32 1, i64 %add, i32 0 + %arr.get.sroa.0.0.copyload = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %arr.get.sroa.0.0..sroa_idx, align 8 + %arr.get.sroa.4.0..sroa_idx13 = getelementptr inbounds %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE", %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)* %23, i64 0, i32 1, i64 %add, i32 2 + %arr.get.sroa.4.0.copyload = load i64, i64 addrspace(1)* %arr.get.sroa.4.0..sroa_idx13, align 8 + %27 = getelementptr inbounds %ObjLayout._ZN7default5PointE, %ObjLayout._ZN7default5PointE addrspace(1)* %14, i64 0, i32 2 + %28 = load i64, i64 addrspace(1)* %27, align 8 + %icmpslt9 = icmp slt i64 %28, 0 + %icmpsge12 = icmp sge i64 %28, %arr.get.sroa.4.0.copyload + %conv14 = or i1 %icmpslt9, %icmpsge12 + br i1 %conv14, label %if7523then, label %if7523end + +if7523then: ; preds = %if7477end + %29 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Except.ti to i8*), i32 56) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %29) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %29) + unreachable + +if7523end: ; preds = %if7477end + %arr.get.sroa.3.0..sroa_idx12 = getelementptr inbounds %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE", %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)* %23, i64 0, i32 1, i64 %add, i32 1 + %arr.get.sroa.3.0.copyload = load i64, i64 addrspace(1)* %arr.get.sroa.3.0..sroa_idx12, align 8 + %add16 = add i64 %arr.get.sroa.3.0.copyload, %28 + %30 = bitcast i8 addrspace(1)* %arr.get.sroa.0.0.copyload to %ArrayLayout.refArray addrspace(1)* + %arr.idx.set.gep = getelementptr inbounds %ArrayLayout.refArray, %ArrayLayout.refArray addrspace(1)* %30, i64 0, i32 1, i64 %add16 + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %this, i8 addrspace(1)* %arr.get.sroa.0.0.copyload, i8 addrspace(1)* addrspace(1)* %arr.idx.set.gep) + br label %if1558end + +if1558end: ; preds = %land.rhs2, %entry, %if7523end + %31 = getelementptr inbounds i8, i8 addrspace(1)* %this, i64 8 + %32 = bitcast i8 addrspace(1)* %31 to %ObjLayout._ZN7default8LocationE addrspace(1)* addrspace(1)* + %33 = load %ObjLayout._ZN7default8LocationE addrspace(1)*, %ObjLayout._ZN7default8LocationE addrspace(1)* addrspace(1)* %32, align 8 + %34 = getelementptr inbounds %ObjLayout._ZN7default8LocationE, %ObjLayout._ZN7default8LocationE addrspace(1)* %33, i64 0, i32 2, i32 2 + %35 = load i64, i64 addrspace(1)* %34, align 8 + %icmpsge17 = icmp slt i64 %35, 1 + br i1 %icmpsge17, label %if7570then, label %if7570end + + if7570then: ; preds = %if1558end + %36 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Except.ti to i8*), i32 56) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %36) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %36) + unreachable + +if7570end: ; preds = %if1558end + %37 = getelementptr inbounds %ObjLayout._ZN7default8LocationE, %ObjLayout._ZN7default8LocationE addrspace(1)* %33, i64 0, i32 2 + %38 = bitcast %"record._ZN7default11std$FS$core5ArrayIC_ZN7default5PointEE" addrspace(1)* %37 to %ArrayLayout.refArray addrspace(1)* addrspace(1)* + %39 = load %ArrayLayout.refArray addrspace(1)*, %ArrayLayout.refArray addrspace(1)* addrspace(1)* %38, align 8 + %40 = getelementptr inbounds %ObjLayout._ZN7default8LocationE, %ObjLayout._ZN7default8LocationE addrspace(1)* %33, i64 0, i32 2, i32 1 + %41 = load i64, i64 addrspace(1)* %40, align 8 + %arr.idx.get.gep19 = getelementptr inbounds %ArrayLayout.refArray, %ArrayLayout.refArray addrspace(1)* %39, i64 0, i32 1, i64 %41 + %42 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %arr.idx.get.gep19, align 8 + %43 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Human.ti to i8*), i32 16) + %44 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Location.ti to i8*), i32 40) + call fastcc void @_ZN7default8Location4initEC_ZN7default5PointE(i8 addrspace(1)* %44, i8 addrspace(1)* %42) + %45 = getelementptr inbounds i8, i8 addrspace(1)* %43, i64 8 + %46 = bitcast i8 addrspace(1)* %45 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %44, i8 addrspace(1)* %43, i8 addrspace(1)* addrspace(1)* %46) + %47 = bitcast i8 addrspace(1)* %31 to %ObjLayout._ZN7default8LocationE addrspace(1)* addrspace(1)* + %48 = load %ObjLayout._ZN7default8LocationE addrspace(1)*, %ObjLayout._ZN7default8LocationE addrspace(1)* addrspace(1)* %47, align 8 + %49 = getelementptr inbounds %ObjLayout._ZN7default8LocationE, %ObjLayout._ZN7default8LocationE addrspace(1)* %48, i64 0, i32 2, i32 2 + %50 = load i64, i64 addrspace(1)* %49, align 8 + %icmpsge20 = icmp slt i64 %50, 2 + br i1 %icmpsge20, label %if7644then, label %if7644end + +if7644then: ; preds = %if7570end + %51 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Except.ti to i8*), i32 56) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %51) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %51) + unreachable + +if7644end: ; preds = %if7570end + %52 = getelementptr inbounds %ObjLayout._ZN7default8LocationE, %ObjLayout._ZN7default8LocationE addrspace(1)* %48, i64 0, i32 2 + %53 = bitcast %"record._ZN7default11std$FS$core5ArrayIC_ZN7default5PointEE" addrspace(1)* %52 to %ArrayLayout.refArray addrspace(1)* addrspace(1)* + %54 = load %ArrayLayout.refArray addrspace(1)*, %ArrayLayout.refArray addrspace(1)* addrspace(1)* %53, align 8 + %55 = getelementptr inbounds %ObjLayout._ZN7default8LocationE, %ObjLayout._ZN7default8LocationE addrspace(1)* %48, i64 0, i32 2, i32 1 + %56 = load i64, i64 addrspace(1)* %55, align 8 + %add22 = add i64 %56, 1 + %arr.idx.get.gep23 = getelementptr inbounds %ArrayLayout.refArray, %ArrayLayout.refArray addrspace(1)* %54, i64 0, i32 1, i64 %add22 + %57 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %arr.idx.get.gep23, align 8 + %58 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Human.ti to i8*), i32 16) + %59 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Location.ti to i8*), i32 40) + call fastcc void @_ZN7default8Location4initEC_ZN7default5PointE(i8 addrspace(1)* %59, i8 addrspace(1)* %57) + %60 = getelementptr inbounds i8, i8 addrspace(1)* %58, i64 8 + %61 = bitcast i8 addrspace(1)* %60 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %59, i8 addrspace(1)* %58, i8 addrspace(1)* addrspace(1)* %61) + %62 = bitcast i8 addrspace(1)* %31 to %ObjLayout._ZN7default8LocationE addrspace(1)* addrspace(1)* + %63 = load %ObjLayout._ZN7default8LocationE addrspace(1)*, %ObjLayout._ZN7default8LocationE addrspace(1)* addrspace(1)* %62, align 8 + %64 = getelementptr inbounds %ObjLayout._ZN7default8LocationE, %ObjLayout._ZN7default8LocationE addrspace(1)* %63, i64 0, i32 2, i32 2 + %65 = load i64, i64 addrspace(1)* %64, align 8 + %icmpsge25 = icmp slt i64 %65, 3 + br i1 %icmpsge25, label %if7718then, label %if7718end + +if7718then: ; preds = %if7644end + %66 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Except.ti to i8*), i32 56) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %66) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %66) + unreachable + +if7718end: ; preds = %if7644end + %67 = getelementptr inbounds %ObjLayout._ZN7default8LocationE, %ObjLayout._ZN7default8LocationE addrspace(1)* %63, i64 0, i32 2 + %68 = bitcast %"record._ZN7default11std$FS$core5ArrayIC_ZN7default5PointEE" addrspace(1)* %67 to %ArrayLayout.refArray addrspace(1)* addrspace(1)* + %69 = load %ArrayLayout.refArray addrspace(1)*, %ArrayLayout.refArray addrspace(1)* addrspace(1)* %68, align 8 + %70 = getelementptr inbounds %ObjLayout._ZN7default8LocationE, %ObjLayout._ZN7default8LocationE addrspace(1)* %63, i64 0, i32 2, i32 1 + %71 = load i64, i64 addrspace(1)* %70, align 8 + %add27 = add i64 %71, 2 + %arr.idx.get.gep28 = getelementptr inbounds %ArrayLayout.refArray, %ArrayLayout.refArray addrspace(1)* %69, i64 0, i32 1, i64 %add27 + %72 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %arr.idx.get.gep28, align 8 + %73 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Human.ti to i8*), i32 16) + %74 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Location.ti to i8*), i32 40) + call fastcc void @_ZN7default8Location4initEC_ZN7default5PointE(i8 addrspace(1)* %74, i8 addrspace(1)* %72) + %75 = getelementptr inbounds i8, i8 addrspace(1)* %73, i64 8 + %76 = bitcast i8 addrspace(1)* %75 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %74, i8 addrspace(1)* %73, i8 addrspace(1)* addrspace(1)* %76) + %77 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @people.ti to i8*), i32 32) + %78 = getelementptr inbounds i8, i8 addrspace(1)* %77, i64 8 + %79 = bitcast i8 addrspace(1)* %78 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %43, i8 addrspace(1)* %77, i8 addrspace(1)* addrspace(1)* %79) + %80 = getelementptr inbounds i8, i8 addrspace(1)* %77, i64 16 + %81 = bitcast i8 addrspace(1)* %80 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %58, i8 addrspace(1)* %77, i8 addrspace(1)* addrspace(1)* %81) + %82 = getelementptr inbounds i8, i8 addrspace(1)* %77, i64 24 + %83 = bitcast i8 addrspace(1)* %82 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %73, i8 addrspace(1)* %77, i8 addrspace(1)* addrspace(1)* %83) + %84 = bitcast i8 addrspace(1)* %78 to %ObjLayout._ZN7default5HumanE addrspace(1)* addrspace(1)* + %85 = load %ObjLayout._ZN7default5HumanE addrspace(1)*, %ObjLayout._ZN7default5HumanE addrspace(1)* addrspace(1)* %84, align 8 + %86 = getelementptr inbounds %ObjLayout._ZN7default5HumanE, %ObjLayout._ZN7default5HumanE addrspace(1)* %85, i64 0, i32 1 + %87 = bitcast i8 addrspace(1)* addrspace(1)* %86 to %ObjLayout._ZN7default8LocationE addrspace(1)* addrspace(1)* + %88 = load %ObjLayout._ZN7default8LocationE addrspace(1)*, %ObjLayout._ZN7default8LocationE addrspace(1)* addrspace(1)* %87, align 8 + %89 = getelementptr inbounds %ObjLayout._ZN7default8LocationE, %ObjLayout._ZN7default8LocationE addrspace(1)* %88, i64 0, i32 1 + %90 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %89, align 8 + %91 = getelementptr inbounds i8, i8 addrspace(1)* %90, i64 8 + %92 = bitcast i8 addrspace(1)* %91 to i64 addrspace(1)* + %93 = load i64, i64 addrspace(1)* %92, align 8 + %icmpsge30 = icmp sgt i64 %93, -1 + br i1 %icmpsge30, label %land.rhs32, label %if1680end + +land.rhs32: ; preds = %if7718end + %94 = getelementptr inbounds i8, i8 addrspace(1)* %90, i64 16 + %95 = bitcast i8 addrspace(1)* %94 to i64 addrspace(1)* + %96 = load i64, i64 addrspace(1)* %95, align 8 + %icmpsge33 = icmp sgt i64 %96, -1 + br i1 %icmpsge33, label %land.rhs37, label %if1680end + +land.rhs37: ; preds = %land.rhs32 + %97 = getelementptr inbounds i8, i8 addrspace(1)* %ns, i64 40 + %98 = bitcast i8 addrspace(1)* %97 to i64 addrspace(1)* + %99 = load i64, i64 addrspace(1)* %98, align 8 + %icmpslt38 = icmp slt i64 %93, %99 + br i1 %icmpslt38, label %land.rhs42, label %if1680end + +land.rhs42: ; preds = %land.rhs37 + %100 = getelementptr inbounds i8, i8 addrspace(1)* %ns, i64 48 + %101 = bitcast i8 addrspace(1)* %100 to i64 addrspace(1)* + %102 = load i64, i64 addrspace(1)* %101, align 8 + %icmpslt43 = icmp slt i64 %96, %102 + br i1 %icmpslt43, label %land.rhs47, label %if1680end + +land.rhs47: ; preds = %land.rhs42 + %103 = call i1 @_ZN7default6Planet17isPopulationAliveEC_ZN7default5PointE(i8 addrspace(1)* %s, i8 addrspace(1)* %90) + br i1 %103, label %if1680end, label %if1680then + +if1680then: ; preds = %land.rhs47 + %104 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Location.ti to i8*), i32 40) + call fastcc void @_ZN7default8Location4initEC_ZN7default5PointE(i8 addrspace(1)* %104, i8 addrspace(1)* %90) + %105 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Human.ti to i8*), i32 16) + %106 = getelementptr inbounds i8, i8 addrspace(1)* %105, i64 8 + %107 = bitcast i8 addrspace(1)* %106 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %104, i8 addrspace(1)* %105, i8 addrspace(1)* addrspace(1)* %107) + call void @_ZN7default6Planet11occupyPlaceEC_ZN7default5PointEC_ZN7default5HumanE(%Unit.Type* noalias nonnull sret(%Unit.Type) %callRet, i8 addrspace(1)* %ns, i8 addrspace(1)* %90, i8 addrspace(1)* %105) + br label %if1680end + +if1680end: ; preds = %land.rhs37, %if7718end, %land.rhs32, %land.rhs47, %land.rhs42, %if1680then + ret void +} + +define internal i64 @_ZN7default5Human14countNeighborsEC_ZN7default6PlanetE(i8 addrspace(1)* %this, i8 addrspace(1)* %s) gc "cangjie" { +entry: + %callRet = alloca %"record._ZN11std$FS$core6StringE", align 8 + %0 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet to i8* + call void @llvm.cj.memset(i8* nonnull align 8 %0, i8 0, i64 16, i1 false) + br label %block1451body + +block1451body: ; preds = %if1491end, %entry + %atom.6799E.0 = phi i64 [ 0, %entry ], [ %add, %if1491end ] + %atom.6801E.0 = phi i64 [ 0, %entry ], [ %atom.6801E.1, %if1491end ] + %icmpslt = icmp slt i64 %atom.6799E.0, 8 + br i1 %icmpslt, label %while1503body, label %block1451end + +while1503body: ; preds = %block1451body + %add = add i64 %atom.6799E.0, 1 + %1 = getelementptr inbounds i8, i8 addrspace(1)* %this, i64 8 + %2 = bitcast i8 addrspace(1)* %1 to %ObjLayout._ZN7default8LocationE addrspace(1)* addrspace(1)* + %3 = load %ObjLayout._ZN7default8LocationE addrspace(1)*, %ObjLayout._ZN7default8LocationE addrspace(1)* addrspace(1)* %2, align 8 + %4 = getelementptr inbounds %ObjLayout._ZN7default8LocationE, %ObjLayout._ZN7default8LocationE addrspace(1)* %3, i64 0, i32 2 + %icmpslt1 = icmp slt i64 %atom.6799E.0, 0 + br i1 %icmpslt1, label %if7304then, label %lor.rhs + +lor.rhs: ; preds = %while1503body + %5 = getelementptr inbounds %ObjLayout._ZN7default8LocationE, %ObjLayout._ZN7default8LocationE addrspace(1)* %3, i64 0, i32 2, i32 2 + %6 = load i64, i64 addrspace(1)* %5, align 8 + %icmpsge.not = icmp slt i64 %atom.6799E.0, %6 + br i1 %icmpsge.not, label %if7304end, label %if7304then + +if7304then: ; preds = %while1503body, %lor.rhs + %7 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Except.ti to i8*), i32 56) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %7) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %7) + unreachable + +if7304end: ; preds = %lor.rhs + %8 = bitcast %"record._ZN7default11std$FS$core5ArrayIC_ZN7default5PointEE" addrspace(1)* %4 to %ArrayLayout.refArray addrspace(1)* addrspace(1)* + %9 = load %ArrayLayout.refArray addrspace(1)*, %ArrayLayout.refArray addrspace(1)* addrspace(1)* %8, align 8 + %10 = getelementptr inbounds %ObjLayout._ZN7default8LocationE, %ObjLayout._ZN7default8LocationE addrspace(1)* %3, i64 0, i32 2, i32 1 + %11 = load i64, i64 addrspace(1)* %10, align 8 + %add2 = add i64 %11, %atom.6799E.0 + %arr.idx.get.gep = getelementptr inbounds %ArrayLayout.refArray, %ArrayLayout.refArray addrspace(1)* %9, i64 0, i32 1, i64 %add2 + %12 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %arr.idx.get.gep, align 8 + %13 = getelementptr inbounds i8, i8 addrspace(1)* %12, i64 8 + %14 = bitcast i8 addrspace(1)* %13 to i64 addrspace(1)* + %15 = load i64, i64 addrspace(1)* %14, align 8 + %icmpsge3 = icmp sgt i64 %15, -1 + br i1 %icmpsge3, label %land.rhs, label %if1491end + +land.rhs: ; preds = %if7304end + %16 = getelementptr inbounds i8, i8 addrspace(1)* %12, i64 16 + %17 = bitcast i8 addrspace(1)* %16 to i64 addrspace(1)* + %18 = load i64, i64 addrspace(1)* %17, align 8 + %icmpsge5 = icmp sgt i64 %18, -1 + br i1 %icmpsge5, label %land.rhs8, label %if1491end + +land.rhs8: ; preds = %land.rhs + %19 = getelementptr inbounds i8, i8 addrspace(1)* %s, i64 40 + %20 = bitcast i8 addrspace(1)* %19 to i64 addrspace(1)* + %21 = load i64, i64 addrspace(1)* %20, align 8 + %icmpslt9 = icmp slt i64 %15, %21 + br i1 %icmpslt9, label %land.rhs13, label %if1491end + +land.rhs13: ; preds = %land.rhs8 + %22 = getelementptr inbounds i8, i8 addrspace(1)* %s, i64 48 + %23 = bitcast i8 addrspace(1)* %22 to i64 addrspace(1)* + %24 = load i64, i64 addrspace(1)* %23, align 8 + %icmpslt14 = icmp slt i64 %18, %24 + br i1 %icmpslt14, label %land.rhs18, label %if1491end + +land.rhs18: ; preds = %land.rhs13 + %25 = call i1 @_ZN7default6Planet17isPopulationAliveEC_ZN7default5PointE(i8 addrspace(1)* %s, i8 addrspace(1)* %12) + br i1 %25, label %if1491then, label %if1491end + +if1491then: ; preds = %land.rhs18 + %26 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %atom.6801E.0, i64 1) + %.fca.1.extract = extractvalue { i64, i1 } %26, 1 + br i1 %.fca.1.extract, label %overflow, label %normal + +normal: ; preds = %if1491then + %.fca.0.extract = extractvalue { i64, i1 } %26, 0 + br label %if1491end + +overflow: ; preds = %if1491then + call void @"_ZN11std$FS$core6String23createStringFromLiteral"(%"record._ZN11std$FS$core6StringE"* sret(%"record._ZN11std$FS$core6StringE") %callRet, i8* null, i64 3) + %27 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %28 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %27) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %28) + unreachable + +if1491end: ; preds = %land.rhs8, %if7304end, %land.rhs, %land.rhs18, %land.rhs13, %normal + %atom.6801E.1 = phi i64 [ %.fca.0.extract, %normal ], [ %atom.6801E.0, %land.rhs18 ], [ %atom.6801E.0, %land.rhs13 ], [ %atom.6801E.0, %land.rhs8 ], [ %atom.6801E.0, %land.rhs ], [ %atom.6801E.0, %if7304end ] + br label %block1451body + +block1451end: ; preds = %block1451body + ret i64 %atom.6801E.0 +} + +define i1 @_ZN7default6Planet17isPopulationAliveEC_ZN7default5PointE(i8 addrspace(1)* %this, i8 addrspace(1)* %t) gc "cangjie" { +entry: + %0 = getelementptr inbounds i8, i8 addrspace(1)* %this, i64 16 + %1 = getelementptr inbounds i8, i8 addrspace(1)* %t, i64 8 + %2 = bitcast i8 addrspace(1)* %1 to i64 addrspace(1)* + %3 = load i64, i64 addrspace(1)* %2, align 8 + %icmpslt = icmp slt i64 %3, 0 + br i1 %icmpslt, label %if7387then, label %lor.rhs + +lor.rhs: ; preds = %entry + %4 = getelementptr inbounds i8, i8 addrspace(1)* %this, i64 32 + %5 = bitcast i8 addrspace(1)* %4 to i64 addrspace(1)* + %6 = load i64, i64 addrspace(1)* %5, align 8 + %icmpsge.not = icmp slt i64 %3, %6 + br i1 %icmpsge.not, label %if7387end, label %if7387then + +if7387then: ; preds = %entry, %lor.rhs + %7 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Except.ti to i8*), i32 56) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %7) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %7) + unreachable + +if7387end: ; preds = %lor.rhs + %8 = bitcast i8 addrspace(1)* %0 to %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)* addrspace(1)* + %9 = load %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)*, %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)* addrspace(1)* %8, align 8 + %10 = getelementptr inbounds i8, i8 addrspace(1)* %this, i64 24 + %11 = bitcast i8 addrspace(1)* %10 to i64 addrspace(1)* + %12 = load i64, i64 addrspace(1)* %11, align 8 + %add = add i64 %12, %3 + %arr.get.sroa.4.0..sroa_idx5 = getelementptr inbounds %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE", %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)* %9, i64 0, i32 1, i64 %add, i32 2 + %arr.get.sroa.4.0.copyload = load i64, i64 addrspace(1)* %arr.get.sroa.4.0..sroa_idx5, align 8 + %13 = getelementptr inbounds i8, i8 addrspace(1)* %t, i64 16 + %14 = bitcast i8 addrspace(1)* %13 to i64 addrspace(1)* + %15 = load i64, i64 addrspace(1)* %14, align 8 + %icmpslt2 = icmp slt i64 %15, 0 + %icmpsge5 = icmp sge i64 %15, %arr.get.sroa.4.0.copyload + %conv7 = or i1 %icmpslt2, %icmpsge5 + br i1 %conv7, label %if7432then, label %if7432end + +if7432then: ; preds = %if7387end + %16 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Except.ti to i8*), i32 56) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %16) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %16) + unreachable + +if7432end: ; preds = %if7387end + %arr.idx.get.gep = getelementptr inbounds %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE", %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)* %9, i64 0, i32 1, i64 %add + %arr.get.sroa.3.0..sroa_idx4 = getelementptr inbounds %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE", %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)* %9, i64 0, i32 1, i64 %add, i32 1 + %arr.get.sroa.3.0.copyload = load i64, i64 addrspace(1)* %arr.get.sroa.3.0..sroa_idx4, align 8 + %17 = bitcast %"record._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)* %arr.idx.get.gep to %ArrayLayout.refArray addrspace(1)* addrspace(1)* + %arr.get.sroa.0.0.copyload10 = load %ArrayLayout.refArray addrspace(1)*, %ArrayLayout.refArray addrspace(1)* addrspace(1)* %17, align 8 + %add9 = add i64 %arr.get.sroa.3.0.copyload, %15 + %arr.idx.get.gep10 = getelementptr inbounds %ArrayLayout.refArray, %ArrayLayout.refArray addrspace(1)* %arr.get.sroa.0.0.copyload10, i64 0, i32 1, i64 %add9 + %18 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %arr.idx.get.gep10, align 8 + %.not = icmp eq i8 addrspace(1)* %18, null + %19 = xor i1 %.not, true + ret i1 %19 +} + +define internal fastcc void @_ZN7default8Location4initEC_ZN7default5PointE(i8 addrspace(1)* %this, i8 addrspace(1)* %curr) unnamed_addr gc "cangjie" { +entry: + %callRet68 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet62 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet56 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet49 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet43 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet36 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet29 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet23 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet17 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet11 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet5 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %callRet = alloca %"record._ZN11std$FS$core6StringE", align 8 + %0 = alloca %"record._ZN7default11std$FS$core5ArrayIC_ZN7default5PointEE", align 8 + %atom.6874E = alloca %"record._ZN7default11std$FS$core5ArrayIC_ZN7default5PointEE", align 8 + %1 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet68 to i8* + call void @llvm.cj.memset(i8* nonnull align 8 %1, i8 0, i64 16, i1 false) + %2 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet62 to i8* + call void @llvm.cj.memset(i8* nonnull align 8 %2, i8 0, i64 16, i1 false) + %3 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet56 to i8* + call void @llvm.cj.memset(i8* nonnull align 8 %3, i8 0, i64 16, i1 false) + %4 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet49 to i8* + call void @llvm.cj.memset(i8* nonnull align 8 %4, i8 0, i64 16, i1 false) + %5 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet43 to i8* + call void @llvm.cj.memset(i8* nonnull align 8 %5, i8 0, i64 16, i1 false) + %6 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet36 to i8* + call void @llvm.cj.memset(i8* nonnull align 8 %6, i8 0, i64 16, i1 false) + %7 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet29 to i8* + call void @llvm.cj.memset(i8* nonnull align 8 %7, i8 0, i64 16, i1 false) + %8 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet23 to i8* + call void @llvm.cj.memset(i8* nonnull align 8 %8, i8 0, i64 16, i1 false) + %9 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet17 to i8* + call void @llvm.cj.memset(i8* nonnull align 8 %9, i8 0, i64 16, i1 false) + %10 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet11 to i8* + call void @llvm.cj.memset(i8* nonnull align 8 %10, i8 0, i64 16, i1 false) + %11 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet5 to i8* + call void @llvm.cj.memset(i8* nonnull align 8 %11, i8 0, i64 16, i1 false) + %12 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet to i8* + call void @llvm.cj.memset(i8* nonnull align 8 %12, i8 0, i64 16, i1 false) + %.0.sroa_cast = bitcast %"record._ZN7default11std$FS$core5ArrayIC_ZN7default5PointEE"* %0 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 dereferenceable(24) %.0.sroa_cast, i8 0, i64 24, i1 false) + %13 = bitcast %"record._ZN7default11std$FS$core5ArrayIC_ZN7default5PointEE"* %atom.6874E to i8* + call void @llvm.cj.memset(i8* nonnull align 8 %13, i8 0, i64 24, i1 false) + %14 = getelementptr inbounds i8, i8 addrspace(1)* %this, i64 8 + %15 = bitcast i8 addrspace(1)* %14 to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %curr, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %15) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 8 dereferenceable(24) %13, i8* nonnull align 8 dereferenceable(24) %.0.sroa_cast, i64 24, i1 false) + %16 = call i8 addrspace(1)* @CJ_MCC_NewObjArray(i64 8, i8* bitcast (%TypeInfo* @"RawArray.ti" to i8*)) + %17 = getelementptr inbounds i8, i8 addrspace(1)* %curr, i64 8 + %18 = bitcast i8 addrspace(1)* %17 to i64 addrspace(1)* + %19 = load i64, i64 addrspace(1)* %18, align 8 + %20 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %19, i64 -1) + %.fca.0.extract = extractvalue { i64, i1 } %20, 0 + %.fca.1.extract = extractvalue { i64, i1 } %20, 1 + br i1 %.fca.1.extract, label %overflow0E, label %normal0E + +normal0E: ; preds = %entry + %21 = getelementptr inbounds i8, i8 addrspace(1)* %curr, i64 16 + %22 = bitcast i8 addrspace(1)* %21 to i64 addrspace(1)* + %23 = load i64, i64 addrspace(1)* %22, align 8 + %24 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %23, i64 -1) + %.fca.1.extract2 = extractvalue { i64, i1 } %24, 1 + br i1 %.fca.1.extract2, label %overflow0E3, label %normal0E4 + +overflow0E: ; preds = %entry + call void @"_ZN11std$FS$core6String23createStringFromLiteral"(%"record._ZN11std$FS$core6StringE"* sret(%"record._ZN11std$FS$core6StringE") %callRet, i8* null, i64 3) + %25 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %26 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %25) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %26) + unreachable + +normal0E4: ; preds = %normal0E + %.fca.0.extract1 = extractvalue { i64, i1 } %24, 0 + %27 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Point.ti to i8*), i32 24) + %28 = getelementptr inbounds i8, i8 addrspace(1)* %27, i64 8 + %29 = bitcast i8 addrspace(1)* %28 to i64 addrspace(1)* + store i64 %.fca.0.extract, i64 addrspace(1)* %29, align 8 + %30 = getelementptr inbounds i8, i8 addrspace(1)* %27, i64 16 + %31 = bitcast i8 addrspace(1)* %30 to i64 addrspace(1)* + store i64 %.fca.0.extract1, i64 addrspace(1)* %31, align 8 + %arr336.idx0E = getelementptr inbounds i8, i8 addrspace(1)* %16, i64 16 + %32 = bitcast i8 addrspace(1)* %arr336.idx0E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %27, i8 addrspace(1)* %16, i8 addrspace(1)* addrspace(1)* %32) + %33 = load i64, i64 addrspace(1)* %22, align 8 + %34 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %33, i64 -1) + %.fca.1.extract6 = extractvalue { i64, i1 } %34, 1 + br i1 %.fca.1.extract6, label %overflow0E9, label %normal0E10 + +overflow0E3: ; preds = %normal0E + call void @"_ZN11std$FS$core6String23createStringFromLiteral"(%"record._ZN11std$FS$core6StringE"* sret(%"record._ZN11std$FS$core6StringE") %callRet5, i8* null, i64 3) + %35 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet5 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %36 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %35) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %36) + unreachable + +normal0E10: ; preds = %normal0E4 + %.fca.0.extract5 = extractvalue { i64, i1 } %34, 0 + %37 = load i64, i64 addrspace(1)* %18, align 8 + %38 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Point.ti to i8*), i32 24) + %39 = getelementptr inbounds i8, i8 addrspace(1)* %38, i64 8 + %40 = bitcast i8 addrspace(1)* %39 to i64 addrspace(1)* + store i64 %37, i64 addrspace(1)* %40, align 8 + %41 = getelementptr inbounds i8, i8 addrspace(1)* %38, i64 16 + %42 = bitcast i8 addrspace(1)* %41 to i64 addrspace(1)* + store i64 %.fca.0.extract5, i64 addrspace(1)* %42, align 8 + %arr336.idx1E = getelementptr inbounds i8, i8 addrspace(1)* %16, i64 24 + %43 = bitcast i8 addrspace(1)* %arr336.idx1E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %38, i8 addrspace(1)* %16, i8 addrspace(1)* addrspace(1)* %43) + %44 = load i64, i64 addrspace(1)* %18, align 8 + %45 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %44, i64 1) + %.fca.0.extract9 = extractvalue { i64, i1 } %45, 0 + %.fca.1.extract10 = extractvalue { i64, i1 } %45, 1 + br i1 %.fca.1.extract10, label %overflow0E15, label %normal0E16 + +overflow0E9: ; preds = %normal0E4 + call void @"_ZN11std$FS$core6String23createStringFromLiteral"(%"record._ZN11std$FS$core6StringE"* sret(%"record._ZN11std$FS$core6StringE") %callRet11, i8* null, i64 3) + %46 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet11 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %47 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %46) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %47) + unreachable + +normal0E16: ; preds = %normal0E10 + %48 = load i64, i64 addrspace(1)* %22, align 8 + %49 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %48, i64 -1) + %.fca.1.extract12 = extractvalue { i64, i1 } %49, 1 + br i1 %.fca.1.extract12, label %overflow0E21, label %normal0E22 + +overflow0E15: ; preds = %normal0E10 + call void @"_ZN11std$FS$core6String23createStringFromLiteral"(%"record._ZN11std$FS$core6StringE"* sret(%"record._ZN11std$FS$core6StringE") %callRet17, i8* null, i64 3) + %50 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet17 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %51 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %50) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %51) + unreachable + +normal0E22: ; preds = %normal0E16 + %.fca.0.extract11 = extractvalue { i64, i1 } %49, 0 + %52 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Point.ti to i8*), i32 24) + %53 = getelementptr inbounds i8, i8 addrspace(1)* %52, i64 8 + %54 = bitcast i8 addrspace(1)* %53 to i64 addrspace(1)* + store i64 %.fca.0.extract9, i64 addrspace(1)* %54, align 8 + %55 = getelementptr inbounds i8, i8 addrspace(1)* %52, i64 16 + %56 = bitcast i8 addrspace(1)* %55 to i64 addrspace(1)* + store i64 %.fca.0.extract11, i64 addrspace(1)* %56, align 8 + %arr336.idx2E = getelementptr inbounds i8, i8 addrspace(1)* %16, i64 32 + %57 = bitcast i8 addrspace(1)* %arr336.idx2E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %52, i8 addrspace(1)* %16, i8 addrspace(1)* addrspace(1)* %57) + %58 = load i64, i64 addrspace(1)* %18, align 8 + %59 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %58, i64 -1) + %.fca.1.extract16 = extractvalue { i64, i1 } %59, 1 + br i1 %.fca.1.extract16, label %overflow0E27, label %normal0E28 + +overflow0E21: ; preds = %normal0E16 + call void @"_ZN11std$FS$core6String23createStringFromLiteral"(%"record._ZN11std$FS$core6StringE"* sret(%"record._ZN11std$FS$core6StringE") %callRet23, i8* null, i64 3) + %60 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet23 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %61 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %60) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %61) + unreachable + +normal0E28: ; preds = %normal0E22 + %.fca.0.extract15 = extractvalue { i64, i1 } %59, 0 + %62 = load i64, i64 addrspace(1)* %22, align 8 + %63 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Point.ti to i8*), i32 24) + %64 = getelementptr inbounds i8, i8 addrspace(1)* %63, i64 8 + %65 = bitcast i8 addrspace(1)* %64 to i64 addrspace(1)* + store i64 %.fca.0.extract15, i64 addrspace(1)* %65, align 8 + %66 = getelementptr inbounds i8, i8 addrspace(1)* %63, i64 16 + %67 = bitcast i8 addrspace(1)* %66 to i64 addrspace(1)* + store i64 %62, i64 addrspace(1)* %67, align 8 + %arr336.idx3E = getelementptr inbounds i8, i8 addrspace(1)* %16, i64 40 + %68 = bitcast i8 addrspace(1)* %arr336.idx3E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %63, i8 addrspace(1)* %16, i8 addrspace(1)* addrspace(1)* %68) + %69 = load i64, i64 addrspace(1)* %18, align 8 + %70 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %69, i64 1) + %.fca.1.extract20 = extractvalue { i64, i1 } %70, 1 + br i1 %.fca.1.extract20, label %overflow0E34, label %normal0E35 + +overflow0E27: ; preds = %normal0E22 + call void @"_ZN11std$FS$core6String23createStringFromLiteral"(%"record._ZN11std$FS$core6StringE"* sret(%"record._ZN11std$FS$core6StringE") %callRet29, i8* null, i64 3) + %71 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet29 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %72 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %71) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %72) + unreachable + +normal0E35: ; preds = %normal0E28 + %.fca.0.extract19 = extractvalue { i64, i1 } %70, 0 + %73 = load i64, i64 addrspace(1)* %22, align 8 + %74 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Point.ti to i8*), i32 24) + %75 = getelementptr inbounds i8, i8 addrspace(1)* %74, i64 8 + %76 = bitcast i8 addrspace(1)* %75 to i64 addrspace(1)* + store i64 %.fca.0.extract19, i64 addrspace(1)* %76, align 8 + %77 = getelementptr inbounds i8, i8 addrspace(1)* %74, i64 16 + %78 = bitcast i8 addrspace(1)* %77 to i64 addrspace(1)* + store i64 %73, i64 addrspace(1)* %78, align 8 + %arr336.idx4E = getelementptr inbounds i8, i8 addrspace(1)* %16, i64 48 + %79 = bitcast i8 addrspace(1)* %arr336.idx4E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %74, i8 addrspace(1)* %16, i8 addrspace(1)* addrspace(1)* %79) + %80 = load i64, i64 addrspace(1)* %18, align 8 + %81 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %80, i64 -1) + %.fca.0.extract23 = extractvalue { i64, i1 } %81, 0 + %.fca.1.extract24 = extractvalue { i64, i1 } %81, 1 + br i1 %.fca.1.extract24, label %overflow0E41, label %normal0E42 + +overflow0E34: ; preds = %normal0E28 + call void @"_ZN11std$FS$core6String23createStringFromLiteral"(%"record._ZN11std$FS$core6StringE"* sret(%"record._ZN11std$FS$core6StringE") %callRet36, i8* null, i64 3) + %82 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet36 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %83 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %82) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %83) + unreachable + +normal0E42: ; preds = %normal0E35 + %84 = load i64, i64 addrspace(1)* %22, align 8 + %85 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %84, i64 1) + %.fca.1.extract26 = extractvalue { i64, i1 } %85, 1 + br i1 %.fca.1.extract26, label %overflow0E47, label %normal0E48 + +overflow0E41: ; preds = %normal0E35 + call void @"_ZN11std$FS$core6String23createStringFromLiteral"(%"record._ZN11std$FS$core6StringE"* sret(%"record._ZN11std$FS$core6StringE") %callRet43, i8* null, i64 3) + %86 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet43 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %87 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %86) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %87) + unreachable + +normal0E48: ; preds = %normal0E42 + %.fca.0.extract25 = extractvalue { i64, i1 } %85, 0 + %88 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Point.ti to i8*), i32 24) + %89 = getelementptr inbounds i8, i8 addrspace(1)* %88, i64 8 + %90 = bitcast i8 addrspace(1)* %89 to i64 addrspace(1)* + store i64 %.fca.0.extract23, i64 addrspace(1)* %90, align 8 + %91 = getelementptr inbounds i8, i8 addrspace(1)* %88, i64 16 + %92 = bitcast i8 addrspace(1)* %91 to i64 addrspace(1)* + store i64 %.fca.0.extract25, i64 addrspace(1)* %92, align 8 + %arr336.idx5E = getelementptr inbounds i8, i8 addrspace(1)* %16, i64 56 + %93 = bitcast i8 addrspace(1)* %arr336.idx5E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %88, i8 addrspace(1)* %16, i8 addrspace(1)* addrspace(1)* %93) + %94 = load i64, i64 addrspace(1)* %22, align 8 + %95 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %94, i64 1) + %.fca.1.extract30 = extractvalue { i64, i1 } %95, 1 + br i1 %.fca.1.extract30, label %overflow0E54, label %normal0E55 + +overflow0E47: ; preds = %normal0E42 + call void @"_ZN11std$FS$core6String23createStringFromLiteral"(%"record._ZN11std$FS$core6StringE"* sret(%"record._ZN11std$FS$core6StringE") %callRet49, i8* null, i64 3) + %96 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet49 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %97 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %96) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %97) + unreachable + +normal0E55: ; preds = %normal0E48 + %.fca.0.extract29 = extractvalue { i64, i1 } %95, 0 + %98 = load i64, i64 addrspace(1)* %18, align 8 + %99 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Point.ti to i8*), i32 24) + %100 = getelementptr inbounds i8, i8 addrspace(1)* %99, i64 8 + %101 = bitcast i8 addrspace(1)* %100 to i64 addrspace(1)* + store i64 %98, i64 addrspace(1)* %101, align 8 + %102 = getelementptr inbounds i8, i8 addrspace(1)* %99, i64 16 + %103 = bitcast i8 addrspace(1)* %102 to i64 addrspace(1)* + store i64 %.fca.0.extract29, i64 addrspace(1)* %103, align 8 + %arr336.idx6E = getelementptr inbounds i8, i8 addrspace(1)* %16, i64 64 + %104 = bitcast i8 addrspace(1)* %arr336.idx6E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %99, i8 addrspace(1)* %16, i8 addrspace(1)* addrspace(1)* %104) + %105 = load i64, i64 addrspace(1)* %18, align 8 + %106 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %105, i64 1) + %.fca.1.extract34 = extractvalue { i64, i1 } %106, 1 + br i1 %.fca.1.extract34, label %overflow0E60, label %normal0E61 + +overflow0E54: ; preds = %normal0E48 + call void @"_ZN11std$FS$core6String23createStringFromLiteral"(%"record._ZN11std$FS$core6StringE"* sret(%"record._ZN11std$FS$core6StringE") %callRet56, i8* null, i64 3) + %107 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet56 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %108 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %107) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %108) + unreachable + +normal0E61: ; preds = %normal0E55 + %109 = load i64, i64 addrspace(1)* %22, align 8 + %110 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %109, i64 1) + %.fca.1.extract36 = extractvalue { i64, i1 } %110, 1 + br i1 %.fca.1.extract36, label %overflow0E66, label %normal0E67 + +overflow0E60: ; preds = %normal0E55 + call void @"_ZN11std$FS$core6String23createStringFromLiteral"(%"record._ZN11std$FS$core6StringE"* sret(%"record._ZN11std$FS$core6StringE") %callRet62, i8* null, i64 3) + %111 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet62 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %112 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %111) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %112) + unreachable + +normal0E67: ; preds = %normal0E61 + %.fca.0.extract35 = extractvalue { i64, i1 } %110, 0 + %.fca.0.extract33 = extractvalue { i64, i1 } %106, 0 + %113 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Point.ti to i8*), i32 24) + %114 = getelementptr inbounds i8, i8 addrspace(1)* %113, i64 8 + %115 = bitcast i8 addrspace(1)* %114 to i64 addrspace(1)* + store i64 %.fca.0.extract33, i64 addrspace(1)* %115, align 8 + %116 = getelementptr inbounds i8, i8 addrspace(1)* %113, i64 16 + %117 = bitcast i8 addrspace(1)* %116 to i64 addrspace(1)* + store i64 %.fca.0.extract35, i64 addrspace(1)* %117, align 8 + %arr336.idx7E = getelementptr inbounds i8, i8 addrspace(1)* %16, i64 72 + %118 = bitcast i8 addrspace(1)* %arr336.idx7E to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %113, i8 addrspace(1)* %16, i8 addrspace(1)* addrspace(1)* %118) + %119 = getelementptr inbounds %"record._ZN7default11std$FS$core5ArrayIC_ZN7default5PointEE", %"record._ZN7default11std$FS$core5ArrayIC_ZN7default5PointEE"* %atom.6874E, i64 0, i32 0 + store i8 addrspace(1)* %16, i8 addrspace(1)** %119, align 8 + %120 = getelementptr inbounds %"record._ZN7default11std$FS$core5ArrayIC_ZN7default5PointEE", %"record._ZN7default11std$FS$core5ArrayIC_ZN7default5PointEE"* %atom.6874E, i64 0, i32 1 + store i64 0, i64* %120, align 8 + %121 = getelementptr inbounds %"record._ZN7default11std$FS$core5ArrayIC_ZN7default5PointEE", %"record._ZN7default11std$FS$core5ArrayIC_ZN7default5PointEE"* %atom.6874E, i64 0, i32 2 + store i64 8, i64* %121, align 8 + %122 = getelementptr inbounds i8, i8 addrspace(1)* %this, i64 16 + call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* %this, i8 addrspace(1)* %122, i8* nonnull %13, i64 24) + ret void + +overflow0E66: ; preds = %normal0E61 + call void @"_ZN11std$FS$core6String23createStringFromLiteral"(%"record._ZN11std$FS$core6StringE"* sret(%"record._ZN11std$FS$core6StringE") %callRet68, i8* null, i64 3) + %123 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet68 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %124 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %123) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %124) + unreachable +} + +define internal void @_ZN7default6Planet11occupyPlaceEC_ZN7default5PointEC_ZN7default5HumanE(%Unit.Type* sret(%Unit.Type) %0, i8 addrspace(1)* %this, i8 addrspace(1)* %t, i8 addrspace(1)* %h) gc "cangjie" { +entry: + %1 = getelementptr inbounds i8, i8 addrspace(1)* %this, i64 16 + %2 = getelementptr inbounds i8, i8 addrspace(1)* %t, i64 8 + %3 = bitcast i8 addrspace(1)* %2 to i64 addrspace(1)* + %4 = load i64, i64 addrspace(1)* %3, align 8 + %icmpslt = icmp slt i64 %4, 0 + br i1 %icmpslt, label %if.then7937E, label %lor.rhs7936E + +lor.rhs7936E: ; preds = %entry + %5 = getelementptr inbounds i8, i8 addrspace(1)* %this, i64 32 + %6 = bitcast i8 addrspace(1)* %5 to i64 addrspace(1)* + %7 = load i64, i64 addrspace(1)* %6, align 8 + %icmpsge.not = icmp slt i64 %4, %7 + br i1 %icmpsge.not, label %if.end7937E, label %if.then7937E + +if.then7937E: ; preds = %entry, %lor.rhs7936E + %8 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Except.ti to i8*), i32 56) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %8) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %8) + unreachable + +if.end7937E: ; preds = %lor.rhs7936E + %9 = bitcast i8 addrspace(1)* %1 to %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)* addrspace(1)* + %10 = load %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)*, %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)* addrspace(1)* %9, align 8 + %11 = getelementptr inbounds i8, i8 addrspace(1)* %this, i64 24 + %12 = bitcast i8 addrspace(1)* %11 to i64 addrspace(1)* + %13 = load i64, i64 addrspace(1)* %12, align 8 + %add = add i64 %13, %4 + %arr.get.sroa.0.0..sroa_idx = getelementptr inbounds %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE", %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)* %10, i64 0, i32 1, i64 %add, i32 0 + %arr.get.sroa.0.0.copyload = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %arr.get.sroa.0.0..sroa_idx, align 8 + %arr.get.sroa.4.0..sroa_idx5 = getelementptr inbounds %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE", %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)* %10, i64 0, i32 1, i64 %add, i32 2 + %arr.get.sroa.4.0.copyload = load i64, i64 addrspace(1)* %arr.get.sroa.4.0..sroa_idx5, align 8 + %14 = getelementptr inbounds i8, i8 addrspace(1)* %t, i64 16 + %15 = bitcast i8 addrspace(1)* %14 to i64 addrspace(1)* + %16 = load i64, i64 addrspace(1)* %15, align 8 + %icmpslt2 = icmp slt i64 %16, 0 + %icmpsge3 = icmp sge i64 %16, %arr.get.sroa.4.0.copyload + %conv4 = or i1 %icmpslt2, %icmpsge3 + br i1 %conv4, label %if.then7983E, label %if.end7983E + +if.then7983E: ; preds = %if.end7937E + %17 = call i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Except.ti to i8*), i32 56) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %17) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %17) + unreachable + +if.end7983E: ; preds = %if.end7937E + %arr.get.sroa.3.0..sroa_idx4 = getelementptr inbounds %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE", %"ArrayLayout._ZN7default11std$FS$core5ArrayIN_ZN11std$FS$core6OptionIC_ZN7default5HumanEEE" addrspace(1)* %10, i64 0, i32 1, i64 %add, i32 1 + %arr.get.sroa.3.0.copyload = load i64, i64 addrspace(1)* %arr.get.sroa.3.0..sroa_idx4, align 8 + %add5 = add i64 %arr.get.sroa.3.0.copyload, %16 + %18 = bitcast i8 addrspace(1)* %arr.get.sroa.0.0.copyload to %ArrayLayout.refArray addrspace(1)* + %arr.idx.set.gep = getelementptr inbounds %ArrayLayout.refArray, %ArrayLayout.refArray addrspace(1)* %18, i64 0, i32 1, i64 %add5 + call void @llvm.cj.gcwrite.ref(i8 addrspace(1)* %h, i8 addrspace(1)* %arr.get.sroa.0.0.copyload, i8 addrspace(1)* addrspace(1)* %arr.idx.set.gep) + ret void +} + +!0 = !{!"ArrayLayout.refArray"} +!1 = !{!"ObjLayout.people"} +!2 = !{!"ObjLayout.Location"} +!3 = !{!"ObjLayout.Point"} +!4 = !{!"ObjLayout.Except"} +!5 = !{!"ObjLayout.Human"} diff --git a/llvm/test/Transforms/CJPartialEscapeAnalysis/remove-invanriant-load.ll b/llvm/test/Transforms/CJPartialEscapeAnalysis/remove-invanriant-load.ll new file mode 100644 index 000000000..d9660ed77 --- /dev/null +++ b/llvm/test/Transforms/CJPartialEscapeAnalysis/remove-invanriant-load.ll @@ -0,0 +1,20 @@ +; RUN: opt < %s '-passes=cj-pea' -cj-disable-partial-ea -S | FileCheck %s + +%BitMap = type { i32, [0 x i8] } +%TypeInfo = type { i8*, i8, i8, i16, i32, %BitMap*, i32, i8, i8, i32*, i8*, i8*, i8*, %TypeInfo*, i8*, i8* } +%ObjLayout.obj = type {i32, i32, i32, i32} +@"obj.ti" = external global %TypeInfo, !RelatedType !0 + +; CHECK-NOT: [[TMP0:%.*]] = load %TypeInfo*, %TypeInfo** [[TMP1:%.*]], !invariant.load !1 +define void @foo() gc "cangjie" { + %1 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @"obj.ti" to i8*), i32 16) + %2 = bitcast i8 addrspace(1)* %1 to %TypeInfo* addrspace(1)* + %3 = addrspacecast %TypeInfo* addrspace(1)* %2 to %TypeInfo** + %4 = load %TypeInfo*, %TypeInfo** %3, !invariant.load !1 + ret void +} + +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) + +!0 = !{!"ObjLayout.obj"} +!1 = !{} \ No newline at end of file diff --git a/llvm/test/Transforms/CJPartialEscapeAnalysis/struct_addrspace_cast1.ll b/llvm/test/Transforms/CJPartialEscapeAnalysis/struct_addrspace_cast1.ll new file mode 100644 index 000000000..769abb802 --- /dev/null +++ b/llvm/test/Transforms/CJPartialEscapeAnalysis/struct_addrspace_cast1.ll @@ -0,0 +1,147 @@ +; RUN: opt --cangjie-pipeline -O2 -S < %s | FileCheck %s + +%"record._ZN7default11std$FS$core6OptionIlE" = type { i1, i64 } +%record._ZN7default7default19StructRangeIteratorIlE = type { i64, i64, i64, i1 } + +@_ZN7default4stepE = internal global i64 2, align 8 + +declare i64 @"_ZN11std$FS$core6Extendl4nextEl"(i64, i64) gc "cangjie" + +define internal void @_ZN7default7default19StructRangeIteratorIl4nextEv(%"record._ZN7default11std$FS$core6OptionIlE"* noalias sret(%"record._ZN7default11std$FS$core6OptionIlE") %0, %record._ZN7default7default19StructRangeIteratorIlE addrspace(1)* %this, i8 addrspace(1)* %BP) #0 gc "cangjie" { +entry: + %enum.val1 = alloca %"record._ZN7default11std$FS$core6OptionIlE", align 8 + %enum.val = alloca %"record._ZN7default11std$FS$core6OptionIlE", align 8 + %res = alloca i64, align 8 + br label %thunk19E + +thunk19E: ; preds = %entry + %1 = getelementptr inbounds %record._ZN7default7default19StructRangeIteratorIlE, %record._ZN7default7default19StructRangeIteratorIlE addrspace(1)* %this, i32 0, i32 2 + %2 = load i64, i64 addrspace(1)* %1, align 8 + store i64 %2, i64* %res, align 8 + %3 = getelementptr inbounds %record._ZN7default7default19StructRangeIteratorIlE, %record._ZN7default7default19StructRangeIteratorIlE addrspace(1)* %this, i32 0, i32 3 + %4 = load i1, i1 addrspace(1)* %3, align 1 + br i1 %4, label %if.then57E, label %if.else57E + +if.then57E: ; preds = %thunk19E + %5 = load i64, i64* %res, align 8 + %6 = getelementptr inbounds %record._ZN7default7default19StructRangeIteratorIlE, %record._ZN7default7default19StructRangeIteratorIlE addrspace(1)* %this, i32 0, i32 0 + %7 = load i64, i64 addrspace(1)* %6, align 8 + %icmpne = icmp ne i64 %5, %7 + %8 = getelementptr inbounds %record._ZN7default7default19StructRangeIteratorIlE, %record._ZN7default7default19StructRangeIteratorIlE addrspace(1)* %this, i32 0, i32 3 + store i1 %icmpne, i1 addrspace(1)* %8, align 1 + %9 = getelementptr inbounds %record._ZN7default7default19StructRangeIteratorIlE, %record._ZN7default7default19StructRangeIteratorIlE addrspace(1)* %this, i32 0, i32 2 + %10 = load i64, i64 addrspace(1)* %9, align 8 + %11 = getelementptr inbounds %record._ZN7default7default19StructRangeIteratorIlE, %record._ZN7default7default19StructRangeIteratorIlE addrspace(1)* %this, i32 0, i32 1 + %12 = load i64, i64 addrspace(1)* %11, align 8 + %13 = call i64 @"_ZN11std$FS$core6Extendl4nextEl"(i64 %10, i64 %12) + %14 = getelementptr inbounds %record._ZN7default7default19StructRangeIteratorIlE, %record._ZN7default7default19StructRangeIteratorIlE addrspace(1)* %this, i32 0, i32 2 + store i64 %13, i64 addrspace(1)* %14, align 8 + %15 = load i64, i64* %res, align 8 + %16 = getelementptr inbounds %"record._ZN7default11std$FS$core6OptionIlE", %"record._ZN7default11std$FS$core6OptionIlE"* %enum.val, i32 0, i32 0 + store i1 false, i1* %16, align 1 + %17 = getelementptr inbounds %"record._ZN7default11std$FS$core6OptionIlE", %"record._ZN7default11std$FS$core6OptionIlE"* %enum.val, i32 0, i32 1 + store i64 %15, i64* %17, align 8 + %18 = bitcast %"record._ZN7default11std$FS$core6OptionIlE"* %0 to i8* + %19 = bitcast %"record._ZN7default11std$FS$core6OptionIlE"* %enum.val to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %18, i8* align 8 %19, i64 16, i1 false) + ret void + +if.else57E: ; preds = %thunk19E + br label %if.end57E + +if.end57E: ; preds = %if.else57E + %20 = getelementptr inbounds %"record._ZN7default11std$FS$core6OptionIlE", %"record._ZN7default11std$FS$core6OptionIlE"* %enum.val1, i32 0, i32 0 + store i1 true, i1* %20, align 1 + %21 = getelementptr inbounds %"record._ZN7default11std$FS$core6OptionIlE", %"record._ZN7default11std$FS$core6OptionIlE"* %enum.val1, i32 0, i32 1 + store i64 0, i64* %21, align 8 + %22 = bitcast %"record._ZN7default11std$FS$core6OptionIlE"* %0 to i8* + %23 = bitcast %"record._ZN7default11std$FS$core6OptionIlE"* %enum.val1 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %22, i8* align 8 %23, i64 16, i1 false) + ret void +} + +define i64 @_ZN7default4mainEv() #1 gc "cangjie" { +; CHECK-LABEL: @_ZN7default4mainEv( +; CHECK-LABEL: entry +; CHECK-NOT: %iter = alloca %record._ZN7default7default19StructRangeIteratorIlE, align 8 +entry: + %v = alloca i64, align 8 + %callRet = alloca %"record._ZN7default11std$FS$core6OptionIlE", align 8 + %iter = alloca %record._ZN7default7default19StructRangeIteratorIlE, align 8 + %step = alloca i64, align 8 + %0 = alloca %record._ZN7default7default19StructRangeIteratorIlE, align 8 + %atom.187E = alloca %record._ZN7default7default19StructRangeIteratorIlE, align 8 + %atom.189E = alloca i64, align 8 + br label %thunk96E + +thunk96E: ; preds = %entry + store i64 0, i64* %atom.189E, align 8 + %1 = bitcast %record._ZN7default7default19StructRangeIteratorIlE* %0 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 32, i1 false) + %2 = bitcast %record._ZN7default7default19StructRangeIteratorIlE* %atom.187E to i8* + %3 = bitcast %record._ZN7default7default19StructRangeIteratorIlE* %0 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %2, i8* align 8 %3, i64 32, i1 false) + %4 = load i64, i64* @_ZN7default4stepE, align 8 + store i64 %4, i64* %step, align 8 + %5 = getelementptr inbounds %record._ZN7default7default19StructRangeIteratorIlE, %record._ZN7default7default19StructRangeIteratorIlE* %atom.187E, i32 0, i32 0 + store i64 1000000000, i64* %5, align 8 + %6 = load i64, i64* %step, align 8 + %7 = getelementptr inbounds %record._ZN7default7default19StructRangeIteratorIlE, %record._ZN7default7default19StructRangeIteratorIlE* %atom.187E, i32 0, i32 1 + store i64 %6, i64* %7, align 8 + %8 = getelementptr inbounds %record._ZN7default7default19StructRangeIteratorIlE, %record._ZN7default7default19StructRangeIteratorIlE* %atom.187E, i32 0, i32 2 + store i64 0, i64* %8, align 8 + %9 = getelementptr inbounds %record._ZN7default7default19StructRangeIteratorIlE, %record._ZN7default7default19StructRangeIteratorIlE* %atom.187E, i32 0, i32 3 + store i1 true, i1* %9, align 1 + %10 = bitcast %record._ZN7default7default19StructRangeIteratorIlE* %iter to i8* + %11 = bitcast %record._ZN7default7default19StructRangeIteratorIlE* %atom.187E to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %10, i8* align 8 %11, i64 32, i1 false) + br label %block.body126E + +block.body126E: ; preds = %if.end160E, %thunk96E + %12 = addrspacecast %record._ZN7default7default19StructRangeIteratorIlE* %iter to %record._ZN7default7default19StructRangeIteratorIlE addrspace(1)* + call void @_ZN7default7default19StructRangeIteratorIl4nextEv(%"record._ZN7default11std$FS$core6OptionIlE"* noalias sret(%"record._ZN7default11std$FS$core6OptionIlE") %callRet, %record._ZN7default7default19StructRangeIteratorIlE addrspace(1)* %12, i8 addrspace(1)* null) + %13 = getelementptr inbounds %"record._ZN7default11std$FS$core6OptionIlE", %"record._ZN7default11std$FS$core6OptionIlE"* %callRet, i32 0, i32 0 + %14 = load i1, i1* %13, align 1 + %icmpeq = icmp eq i1 %14, false + br i1 %icmpeq, label %if.then160E, label %if.else160E + +if.then160E: ; preds = %block.body126E + %15 = getelementptr inbounds %"record._ZN7default11std$FS$core6OptionIlE", %"record._ZN7default11std$FS$core6OptionIlE"* %callRet, i32 0, i32 1 + %16 = load i64, i64* %15, align 8 + store i64 %16, i64* %v, align 8 + %17 = load i64, i64* %atom.189E, align 8 + %18 = load i64, i64* %v, align 8 + %19 = call i64 @llvm.sadd.sat.i64(i64 %17, i64 %18) + store i64 %19, i64* %atom.189E, align 8 + br label %if.end160E + +if.else160E: ; preds = %block.body126E + br label %block.end126E + +if.end160E: ; preds = %if.then160E + br label %block.body126E + +block.end126E: ; preds = %if.else160E + %20 = load i64, i64* %atom.189E, align 8 + ret i64 %20 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #3 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #4 + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare i64 @llvm.sadd.sat.i64(i64, i64) #5 + +; Function Attrs: nounwind +declare void @llvm.cj.memset(i8*, i8, i64, i1) #6 + +attributes #0 = { "cj_fast_call" "hasRcdParam" "record_mut" } +attributes #1 = { "cjinit" } +attributes #2 = { argmemonly nounwind } +attributes #3 = { argmemonly nofree nosync nounwind willreturn } +attributes #4 = { argmemonly nofree nosync nounwind willreturn writeonly } +attributes #5 = { nofree nosync nounwind readnone speculatable willreturn } +attributes #6 = { nounwind } diff --git a/llvm/test/Transforms/CJRSSCE/readonly-struct-param-opt1.ll b/llvm/test/Transforms/CJRSSCE/readonly-struct-param-opt1.ll new file mode 100644 index 000000000..b15b892c5 --- /dev/null +++ b/llvm/test/Transforms/CJRSSCE/readonly-struct-param-opt1.ll @@ -0,0 +1,50 @@ +; RUN: opt -passes=cj-rssce -S < %s | FileCheck %s + +%record.test1 = type {i8 addrspace(1)*, i64, i64} +%record.test2 = type {i64, %record.test1, i64} + +declare void @func1(%record.test1*) #0 +declare void @llvm.memcpy.p0i8.p1i8.i64(i8* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) +declare void @init_record2(%record.test2* %ptr) + +; CHECK: %8 = bitcast i8* %7 to %record.test1* +; CHECK-NEXT call void @func1(%record.test1* %8) +define void @only_memcpy_func(i8 addrspace(1)* %ptr) { +entry: + %0 = alloca %record.test1, align 8 + %1 = alloca %record.test2, align 8 + %2 = bitcast %record.test1* %0 to i8* + %3 = addrspacecast %record.test2* %1 to %record.test2 addrspace(1)* + %4 = getelementptr inbounds %record.test2, %record.test2 addrspace(1)* %3, i64 0, i32 1 + %5 = bitcast %record.test1 addrspace(1)* %4 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %2, i8 addrspace(1)* align 8 %5, i64 24, i1 false) + call void @func1(%record.test1* %0) + ret void +} + +; CHECK: call void @func1(%record.test1* %0) +define void @only_memcpy_func2(i8 addrspace(1)* %ptr) { +entry: + %0 = alloca %record.test1, align 8 + %1 = alloca %record.test2, align 8 + %2 = alloca %record.test2, align 8 + call void @init_record2(%record.test2* %2) + %3 = bitcast %record.test2* %1 to i8* + call void @llvm.lifetime.start.p0i8(i64 40, i8* nonnull %3) + %4 = bitcast %record.test2* %2 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %3, i8* %4, i64 40, i1 false) + %5 = bitcast %record.test1* %0 to i8* + %6 = addrspacecast %record.test2* %1 to %record.test2 addrspace(1)* + %7 = getelementptr inbounds %record.test2, %record.test2 addrspace(1)* %6, i64 0, i32 1 + %8 = bitcast %record.test1 addrspace(1)* %7 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %5, i8 addrspace(1)* align 8 %8, i64 24, i1 false) + %9 = bitcast %record.test2* %1 to i8* + call void @llvm.lifetime.end.p0i8(i64 40, i8* %9) + call void @func1(%record.test1* %0) + ret void +} + +attributes #0 = { readonly } diff --git a/llvm/test/Transforms/CJRSSCE/readonly-struct-param-opt2.ll b/llvm/test/Transforms/CJRSSCE/readonly-struct-param-opt2.ll new file mode 100644 index 000000000..fd83c9220 --- /dev/null +++ b/llvm/test/Transforms/CJRSSCE/readonly-struct-param-opt2.ll @@ -0,0 +1,40 @@ +; RUN: opt -passes=cj-rssce -S < %s | FileCheck %s + +%record.test1 = type {i8 addrspace(1)*, i64, i64} +%record.test2 = type {i64, i8 addrspace(1)*, i64} + +declare void @func1(%record.test1*) #0 +declare void @llvm.memcpy.p0i8.p1i8.i64(i8* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare void @init_record1(%record.test1* %ptr) +declare i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* nocapture, i8 addrspace(1)* addrspace(1)* nocapture) + +; CHECK: %16 = addrspacecast i8 addrspace(1)* %15 to i8* +; CHECK-NEXT %17 = bitcast i8* %16 to %record.test1* +; CHECK-NEXT call void @func1(%record.test1* %17) +define void @memcpy_and_store_func() { +entry: + %0 = alloca %record.test1, align 8 + %1 = alloca %record.test1, align 8 + %2 = alloca %record.test2, align 8 + call void @init_record1(%record.test1* %1) + %3 = addrspacecast %record.test1* %1 to %record.test1 addrspace(1)* + %4 = bitcast %record.test1 addrspace(1)* %3 to i8 addrspace(1)* + %5 = getelementptr inbounds %record.test2, %record.test2* %2, i32 0, i32 1 + store i8 addrspace(1)* %4, i8 addrspace(1)** %5, align 8 + call void @init_record1(%record.test1* %0) + %6 = getelementptr inbounds %record.test2, %record.test2* %2, i32 0, i32 1 + %7 = load i8 addrspace(1)*, i8 addrspace(1)** %6, align 8 + %8 = getelementptr inbounds i8, i8 addrspace(1)* %7, i32 0 + %9 = bitcast i8 addrspace(1)* %8 to i8 addrspace(1)* addrspace(1)* + %10 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %7, i8 addrspace(1)* addrspace(1)* %9) + %11 = getelementptr inbounds %record.test1, %record.test1* %0, i32 0, i32 0 + store i8 addrspace(1)* %10, i8 addrspace(1)** %11, align 8 + %12 = getelementptr inbounds %record.test1, %record.test1* %0, i32 0, i32 1 + %13 = bitcast i64* %12 to i8* + %14 = getelementptr inbounds i8, i8 addrspace(1)* %7, i32 8 + call void @llvm.memcpy.p0i8.p1i8.i64(i8* %13, i8 addrspace(1)* %14, i64 16, i1 false) + call void @func1(%record.test1* %0) + ret void +} + +attributes #0 = { readonly } \ No newline at end of file diff --git a/llvm/test/Transforms/CJRSSCE/readonly-struct-param-opt3.ll b/llvm/test/Transforms/CJRSSCE/readonly-struct-param-opt3.ll new file mode 100644 index 000000000..b85eb301c --- /dev/null +++ b/llvm/test/Transforms/CJRSSCE/readonly-struct-param-opt3.ll @@ -0,0 +1,39 @@ +; RUN: opt -passes=cj-rssce -S < %s | FileCheck %s + +%record.test1 = type {i8 addrspace(1)*, i64, i64} +%ArrayLayout = type {i8 addrspace(1)*, [0 x %record.test1]} + +declare void @func1(%record.test1*) #0 +declare void @llvm.memcpy.p0i8.p108.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) +declare void @init_array(%ArrayLayout* %ptr) + +; CHECK: %9 = bitcast i8 addrspace(1)** %2 to %record.test1* +; CHECK-NEXT call void func1(%record.test1* %9) +define void @array_func() { +entry: + %0 = alloca %ArrayLayout, align 8 + %1 = alloca %record.test1, align 8 + call void @init_array(%ArrayLayout* %0) + br label %loop.header + +loop.header: + %i = phi i64 [0, %entry], [%i.1, %loop.header] + %i.1 = add i64 %i, 1 + %2 = getelementptr inbounds %ArrayLayout, %ArrayLayout* %0, i32 0, i32 1, i64 %i, i32 0 + %3 = getelementptr inbounds %record.test1, %record.test1* %1, i32 0, i32 0 + %4 = load i8 addrspace(1)*, i8 addrspace(1)** %2, align 8 + store i8 addrspace(1)* %4, i8 addrspace(1)** %3, align 8 + %5 = getelementptr inbounds %ArrayLayout, %ArrayLayout* %0, i32 0, i32 1, i64 %i, i32 1 + %6 = getelementptr inbounds %record.test1, %record.test1* %1, i32 0, i32 1 + %7 = bitcast i64* %5 to i8* + %8 = bitcast i64* %6 to i8* + call void @llvm.memcpy.p0i8.p108.i64(i8* %8, i8* %7, i64 16, i1 false) + call void @func1(%record.test1* %1) + %icmp = icmp eq i64 %i.1, 100 + br i1 %icmp, label %exit, label %loop.header + +exit: + ret void +} + +attributes #0 = { readonly } \ No newline at end of file diff --git a/llvm/test/Transforms/CJRSSCE/rssce-multi-source-1.ll b/llvm/test/Transforms/CJRSSCE/rssce-multi-source-1.ll new file mode 100644 index 000000000..3661a5fbf --- /dev/null +++ b/llvm/test/Transforms/CJRSSCE/rssce-multi-source-1.ll @@ -0,0 +1,105 @@ +; RUN: opt -passes=cj-rssce -S < %s | FileCheck %s + +%record.Q = type { i64, i64 } +%record.T = type { i64, %record.Q, i64, i64, %record.Q } +%record.P = type { i64, i64, %record.Q } + +%ObjLayout.0 = type { i8* } +%ObjLayout.S = type { %ObjLayout.0, i64, i64, %record.Q } + +; CHECK: 20: +; CHECK: call void @f2(i8* null, %record.T* %1) +define void @foo1(i8 addrspace(1)* nocapture readonly %s1, i8 addrspace(1)* nocapture readonly %s2) { + %1 = alloca %record.T, align 8 + %2 = alloca %record.T, align 8 + %3 = bitcast i8 addrspace(1)* %s1 to %ObjLayout.S addrspace(1)* + %4 = getelementptr inbounds %ObjLayout.S, %ObjLayout.S addrspace(1)* %3, i64 0, i32 1 + %5 = load i64, i64 addrspace(1)* %4, align 8 + %6 = getelementptr inbounds %record.T, %record.T* %1, i64 0, i32 0 + store i64 %5, i64* %6, align 8 + %7 = bitcast i8 addrspace(1)* %s2 to %ObjLayout.S addrspace(1)* + %8 = getelementptr inbounds %ObjLayout.S, %ObjLayout.S addrspace(1)* %7, i64 0, i32 1 + %9 = bitcast i64 addrspace(1)* %8 to i8 addrspace(1)* + %10 = getelementptr inbounds %record.T, %record.T* %1, i64 0, i32 2 + %11 = bitcast i64* %10 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %11, i8 addrspace(1)* noundef align 8 dereferenceable(16) %9, i64 16, i1 false) + %12 = getelementptr inbounds %ObjLayout.S, %ObjLayout.S addrspace(1)* %3, i64 0, i32 3 + %13 = bitcast %record.Q addrspace(1)* %12 to i8 addrspace(1)* + %14 = getelementptr inbounds %record.T, %record.T* %1, i64 0, i32 1 + %15 = bitcast %record.Q* %14 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %15, i8 addrspace(1)* noundef align 8 dereferenceable(16) %13, i64 16, i1 false) + %16 = getelementptr inbounds %ObjLayout.S, %ObjLayout.S addrspace(1)* %7, i64 0, i32 3 + %17 = bitcast %record.Q addrspace(1)* %16 to i8 addrspace(1)* + %18 = getelementptr inbounds %record.T, %record.T* %1, i64 0, i32 4 + %19 = bitcast %record.Q* %18 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %19, i8 addrspace(1)* noundef align 8 dereferenceable(16) %17, i64 16, i1 false) + call void @f1(i8* null, %record.T* %1) + br label %20 + +20: + %21 = load i64, i64 addrspace(1)* %4, align 8 + %22 = getelementptr inbounds %record.T, %record.T* %2, i64 0, i32 0 + store i64 %21, i64* %22, align 8 + %23 = getelementptr inbounds %record.T, %record.T* %2, i64 0, i32 2 + %24 = bitcast i64* %23 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %24, i8 addrspace(1)* noundef align 8 dereferenceable(16) %9, i64 16, i1 false) + %25 = getelementptr inbounds %record.T, %record.T* %2, i64 0, i32 1 + %26 = bitcast %record.Q* %25 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %26, i8 addrspace(1)* noundef align 8 dereferenceable(16) %13, i64 16, i1 false) + %27 = getelementptr inbounds %record.T, %record.T* %2, i64 0, i32 4 + %28 = bitcast %record.Q* %27 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %28, i8 addrspace(1)* noundef align 8 dereferenceable(16) %17, i64 16, i1 false) + call void @f2(i8* null, %record.T* %2) + ret void +} + +; CHECK: 20: +; CHECK: %24 = bitcast %record.T* %1 to i8* +; CHECK: %25 = getelementptr inbounds i8, i8* %24, i32 24 +; CHECK: %26 = bitcast i8* %25 to %record.P* +; CHECK: call void @f3(i8* null, %record.P* %26) +define void @foo2(i8 addrspace(1)* nocapture readonly %s1, i8 addrspace(1)* nocapture readonly %s2) { + %1 = alloca %record.T, align 8 + %2 = alloca %record.P, align 8 + %3 = bitcast i8 addrspace(1)* %s1 to %ObjLayout.S addrspace(1)* + %4 = getelementptr inbounds %ObjLayout.S, %ObjLayout.S addrspace(1)* %3, i64 0, i32 1 + %5 = load i64, i64 addrspace(1)* %4, align 8 + %6 = getelementptr inbounds %record.T, %record.T* %1, i64 0, i32 0 + store i64 %5, i64* %6, align 8 + %7 = bitcast i8 addrspace(1)* %s2 to %ObjLayout.S addrspace(1)* + %8 = getelementptr inbounds %ObjLayout.S, %ObjLayout.S addrspace(1)* %7, i64 0, i32 1 + %9 = bitcast i64 addrspace(1)* %8 to i8 addrspace(1)* + %10 = getelementptr inbounds %record.T, %record.T* %1, i64 0, i32 2 + %11 = bitcast i64* %10 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %11, i8 addrspace(1)* noundef align 8 dereferenceable(16) %9, i64 16, i1 false) + %12 = getelementptr inbounds %ObjLayout.S, %ObjLayout.S addrspace(1)* %3, i64 0, i32 3 + %13 = bitcast %record.Q addrspace(1)* %12 to i8 addrspace(1)* + %14 = getelementptr inbounds %record.T, %record.T* %1, i64 0, i32 1 + %15 = bitcast %record.Q* %14 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %15, i8 addrspace(1)* noundef align 8 dereferenceable(16) %13, i64 16, i1 false) + %16 = getelementptr inbounds %ObjLayout.S, %ObjLayout.S addrspace(1)* %7, i64 0, i32 3 + %17 = bitcast %record.Q addrspace(1)* %16 to i8 addrspace(1)* + %18 = getelementptr inbounds %record.T, %record.T* %1, i64 0, i32 4 + %19 = bitcast %record.Q* %18 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %19, i8 addrspace(1)* noundef align 8 dereferenceable(16) %17, i64 16, i1 false) + call void @f1(i8* null, %record.T* %1) + br label %20 + +20: + %21 = bitcast %record.P* %2 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %21, i8 addrspace(1)* noundef align 8 dereferenceable(16) %9, i64 16, i1 false) + %22 = getelementptr inbounds %record.P, %record.P* %2, i64 0, i32 2 + %23 = bitcast %record.Q* %22 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %23, i8 addrspace(1)* noundef align 8 dereferenceable(16) %17, i64 16, i1 false) + call void @f3(i8* null, %record.P* %2) + ret void +} + +declare void @f1(i8* %"$BP", %record.T* noalias nocapture readonly %t) #1 +declare void @f2(i8* %"$BP", %record.T* noalias nocapture readonly %t) #1 +declare void @f3(i8* %"$BP", %record.P* noalias nocapture readonly %p) #1 + +declare void @llvm.memcpy.p0i8.p1i8.i64(i8* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) #0 + +attributes #0 = { argmemonly nocallback nofree nounwind willreturn } +attributes #1 = { argmemonly } \ No newline at end of file diff --git a/llvm/test/Transforms/CJRSSCE/rssce-multi-source-2.ll b/llvm/test/Transforms/CJRSSCE/rssce-multi-source-2.ll new file mode 100644 index 000000000..f5e77ba7a --- /dev/null +++ b/llvm/test/Transforms/CJRSSCE/rssce-multi-source-2.ll @@ -0,0 +1,124 @@ +; RUN: opt -passes=cj-rssce -S < %s | FileCheck %s + +%record.Q = type { i64, i64 } +%record.T = type { i64, %record.Q, i64, i64, %record.Q } +%record.P = type { %record.Q, i64, i64 } +%record.R = type { i64, i64, %record.Q } + +%ObjLayout.0 = type { i8* } +%ObjLayout.S = type { %ObjLayout.0, i64, i64, %record.Q } + +; CHECK: use1: +; CHECK: %20 = bitcast %record.T* %1 to i8* +; CHECK: %21 = getelementptr inbounds i8, i8* %20, i32 40 +; CHECK: %22 = bitcast i8* %21 to %record.Q* +; CHECK: call void @f1(i8* null, %record.Q* %22) +; CHECK: redefineQ1: +; CHECK: call void @f1(i8* null, %record.Q* %2) +; CHECK: invalidT: +; CHECK-NEXT: call void @invalid_t(%record.T* %1) +; CHECK-NEXT: call void @f1(i8* null, %record.Q* %2) +define void @foo1(i8 addrspace(1)* %s, %record.P* nocapture readonly %p, i64 %tag) { + %1 = alloca %record.T + %2 = alloca %record.Q + %3 = bitcast i8 addrspace(1)* %s to %ObjLayout.S addrspace(1)* + ; %s[16, 40] -> %1[0, 24] + %4 = bitcast %record.T* %1 to i8* + %5 = getelementptr inbounds %ObjLayout.S, %ObjLayout.S addrspace(1)* %3, i64 0, i32 2 + %6 = bitcast i64 addrspace(1)* %5 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %4, i8 addrspace(1)* noundef align 8 dereferenceable(16) %6, i64 24, i1 false) + ; %s[8, 40] -> %1[24, 56] + %7 = getelementptr inbounds %record.T, %record.T* %1, i64 0, i32 2 + %8 = bitcast i64* %7 to i8* + %9 = getelementptr inbounds %ObjLayout.S, %ObjLayout.S addrspace(1)* %3, i64 0, i32 1 + %10 = bitcast i64 addrspace(1)* %9 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %8, i8 addrspace(1)* noundef align 8 dereferenceable(16) %10, i64 32, i1 false) + ; %p[0, 32] -> %1[8, 40] + %11 = bitcast %record.P* %p to i8* + %12 = getelementptr inbounds %record.T, %record.T* %1, i64 0, i32 1 + %13 = bitcast %record.Q* %12 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %13, i8* noundef align 8 dereferenceable(16) %11, i64 32, i1 false) + br label %use1 + +use1: + %14 = getelementptr inbounds %record.Q, %record.Q* %2, i64 0, i32 0 + %15 = getelementptr inbounds %ObjLayout.S, %ObjLayout.S addrspace(1)* %3, i64 0, i32 3, i32 0 + %16 = load i64, i64 addrspace(1)* %15, align 8 + store i64 %16, i64* %14, align 8 + %17 = getelementptr inbounds %record.Q, %record.Q* %2, i64 0, i32 1 + %18 = getelementptr inbounds %ObjLayout.S, %ObjLayout.S addrspace(1)* %3, i64 0, i32 3, i32 1 + %19 = load i64, i64 addrspace(1)* %18, align 8 + store i64 %19, i64* %17, align 8 + call void @f1(i8* null, %record.Q* %2) + %icmp = icmp eq i64 %tag, 0 + br i1 %icmp, label %invalidS, label %invalidT + +invalidS: + call void @invalid_s(i8 addrspace(1)* %s) + br label %redefineQ1 + +redefineQ1: + %20 = bitcast %record.Q* %2 to i8* + %21 = getelementptr inbounds %ObjLayout.S, %ObjLayout.S addrspace(1)* %3, i64 0, i32 3 + %22 = bitcast %record.Q addrspace(1)* %21 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %20, i8 addrspace(1)* noundef align 8 dereferenceable(16) %22, i64 16, i1 false) + call void @f1(i8* null, %record.Q* %2) + br label %ret + +invalidT: + call void @invalid_t(%record.T* %1) + call void @f1(i8* null, %record.Q* %2) + br label %ret + +ret: + ret void +} + +; CHECK: use: +; CHECK-NEXT: %14 = bitcast %record.R* %2 to i8* +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %14, i8 addrspace(1)* noundef align 8 dereferenceable(16) %10, i64 32, i1 false) +; CHECK-NEXT: %15 = bitcast %record.T* %1 to i8* +; CHECK-NEXT: %16 = getelementptr inbounds i8, i8* %15, i32 24 +; CHECK-NEXT: %17 = bitcast i8* %16 to %record.R* +; CHECK-NEXT: call void @f2(%record.R* %17) +define void @foo2(i8 addrspace(1)* %s, %record.P* nocapture readonly %p, i64 %tag) { + %1 = alloca %record.T + %2 = alloca %record.R + %3 = bitcast i8 addrspace(1)* %s to %ObjLayout.S addrspace(1)* + ; %s[16, 40] -> %1[0, 24] + %4 = bitcast %record.T* %1 to i8* + %5 = getelementptr inbounds %ObjLayout.S, %ObjLayout.S addrspace(1)* %3, i64 0, i32 2 + %6 = bitcast i64 addrspace(1)* %5 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %4, i8 addrspace(1)* noundef align 8 dereferenceable(16) %6, i64 24, i1 false) + ; %s[8, 40] -> %1[24, 56] + %7 = getelementptr inbounds %record.T, %record.T* %1, i64 0, i32 2 + %8 = bitcast i64* %7 to i8* + %9 = getelementptr inbounds %ObjLayout.S, %ObjLayout.S addrspace(1)* %3, i64 0, i32 1 + %10 = bitcast i64 addrspace(1)* %9 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %8, i8 addrspace(1)* noundef align 8 dereferenceable(16) %10, i64 32, i1 false) + ; %p[0, 32] -> %1[8, 40] + %11 = bitcast %record.P* %p to i8* + %12 = getelementptr inbounds %record.T, %record.T* %1, i64 0, i32 1 + %13 = bitcast %record.Q* %12 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %13, i8* noundef align 8 dereferenceable(16) %11, i64 32, i1 false) + ; %s[8, 24] -> %1[24, 40] + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %8, i8 addrspace(1)* noundef align 8 dereferenceable(16) %10, i64 16, i1 false) + br label %use + +use: + %14 = bitcast %record.R* %2 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %14, i8 addrspace(1)* noundef align 8 dereferenceable(16) %10, i64 32, i1 false) + call void @f2(%record.R* %2) + ret void +} + +declare void @llvm.memcpy.p0i8.p1i8.i64(i8* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) #0 +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #0 + +declare void @f1(i8* %"$BP", %record.Q* noalias nocapture readonly %q) #1 +declare void @invalid_s(i8 addrspace(1)* %s) +declare void @invalid_t(%record.T* %t) +declare void @f2(%record.R* noalias nocapture readonly %r) #1 + +attributes #0 = { argmemonly nocallback nofree nounwind willreturn } +attributes #1 = { argmemonly } \ No newline at end of file diff --git a/llvm/test/Transforms/CJRSSCE/rssce-one-source-full-cover.ll b/llvm/test/Transforms/CJRSSCE/rssce-one-source-full-cover.ll new file mode 100644 index 000000000..44eedeed9 --- /dev/null +++ b/llvm/test/Transforms/CJRSSCE/rssce-one-source-full-cover.ll @@ -0,0 +1,75 @@ +; RUN: opt -passes=cj-rssce -S < %s | FileCheck %s + +%record.T = type { %record.S, i64 } +%record.S = type { i8 addrspace(1)*, i64, i64 } + +%ObjLayout.0 = type { i8* } +%ObjLayout.T = type { %ObjLayout.0, %record.T } + +; CHECK: bb5: +; CHECK: %30 = call i64 @f1(i8* null, %record.T* nonnull %3, %record.T* nonnull %2) +define i64 @foo(i8 addrspace(1)* nocapture readonly %this, i8 addrspace(1)* nocapture readonly %other) { +bb0: + %0 = alloca %record.T, align 8 + %1 = alloca %record.T, align 8 + %2 = alloca %record.T, align 8 + %3 = alloca %record.T, align 8 + %4 = bitcast i8 addrspace(1)* %this to %ObjLayout.T addrspace(1)* + %5 = getelementptr inbounds %ObjLayout.T, %ObjLayout.T addrspace(1)* %4, i64 0, i32 1, i32 0, i32 0 + %6 = tail call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %5) + %7 = getelementptr inbounds %record.T, %record.T* %3, i64 0, i32 0, i32 0 + store i8 addrspace(1)* %6, i8 addrspace(1)** %7, align 8 + %8 = getelementptr inbounds %record.T, %record.T* %3, i64 0, i32 0, i32 1 + %9 = bitcast i64* %8 to i8* + %10 = getelementptr inbounds %ObjLayout.T, %ObjLayout.T addrspace(1)* %4, i64 0, i32 1, i32 0, i32 1 + %11 = bitcast i64 addrspace(1)* %10 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(24) %9, i8 addrspace(1)* noundef align 8 dereferenceable(24) %11, i64 24, i1 false) + %12 = bitcast i8 addrspace(1)* %other to %ObjLayout.T addrspace(1)* + %13 = getelementptr inbounds %ObjLayout.T, %ObjLayout.T addrspace(1)* %12, i64 0, i32 1, i32 0, i32 0 + %14 = tail call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %other, i8 addrspace(1)* addrspace(1)* %13) + %15 = getelementptr inbounds %record.T, %record.T* %2, i64 0, i32 0, i32 0 + store i8 addrspace(1)* %14, i8 addrspace(1)** %15, align 8 + %16 = getelementptr inbounds %record.T, %record.T* %2, i64 0, i32 0, i32 1 + %17 = bitcast i64* %16 to i8* + %18 = getelementptr inbounds %ObjLayout.T, %ObjLayout.T addrspace(1)* %12, i64 0, i32 1, i32 0, i32 1 + %19 = bitcast i64 addrspace(1)* %18 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(24) %17, i8 addrspace(1)* noundef align 8 dereferenceable(24) %19, i64 24, i1 false) + %20 = call i64 @f1(i8* null, %record.T* nonnull %3, %record.T* nonnull %2) + %21 = tail call i64 @f2(i64 %20, i64 2) + %cond = icmp eq i64 %21, 2 + br i1 %cond, label %common.ret, label %bb5 + +common.ret: ; preds = %bb5, %bb0 + %common.ret.op = phi i64 [ 0, %bb0 ], [ %spec.select, %bb5 ] + ret i64 %common.ret.op + +bb5: ; preds = %bb0 + %22 = tail call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %5) + %23 = getelementptr inbounds %record.T, %record.T* %1, i64 0, i32 0, i32 0 + store i8 addrspace(1)* %22, i8 addrspace(1)** %23, align 8 + %24 = getelementptr inbounds %record.T, %record.T* %1, i64 0, i32 0, i32 1 + %25 = bitcast i64* %24 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(24) %25, i8 addrspace(1)* noundef align 8 dereferenceable(24) %11, i64 24, i1 false) + %26 = tail call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %other, i8 addrspace(1)* addrspace(1)* %13) + %27 = getelementptr inbounds %record.T, %record.T* %0, i64 0, i32 0, i32 0 + store i8 addrspace(1)* %26, i8 addrspace(1)** %27, align 8 + %28 = getelementptr inbounds %record.T, %record.T* %0, i64 0, i32 0, i32 1 + %29 = bitcast i64* %28 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(24) %29, i8 addrspace(1)* noundef align 8 dereferenceable(24) %19, i64 24, i1 false) + %30 = call i64 @f1(i8* null, %record.T* nonnull %1, %record.T* nonnull %0) + %31 = tail call i64 @f2(i64 %30, i64 1) + %cond1 = icmp eq i64 %31, 2 + %spec.select = select i1 %cond1, i64 1, i64 -1 + br label %common.ret +} + +declare i64 @f1(i8*, %record.T* nocapture readonly, %record.T* nocapture readonly) #3 +declare i64 @f2(i64, i64) #0 + +declare i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* nocapture, i8 addrspace(1)* addrspace(1)* nocapture) #1 +declare void @llvm.memcpy.p0i8.p1i8.i64(i8* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) #2 + +attributes #0 = { readnone } +attributes #1 = { argmemonly nounwind readonly } +attributes #2 = { argmemonly nocallback nofree nounwind willreturn } +attributes #3 = { argmemonly } diff --git a/llvm/test/Transforms/CJRSSCE/rssce-one-source-partial-cover.ll b/llvm/test/Transforms/CJRSSCE/rssce-one-source-partial-cover.ll new file mode 100644 index 000000000..8ba04ac8f --- /dev/null +++ b/llvm/test/Transforms/CJRSSCE/rssce-one-source-partial-cover.ll @@ -0,0 +1,36 @@ +; RUN: opt -passes=cj-rssce -S < %s | FileCheck %s + +%record.Q = type { i64, i64 } +%record.T = type { i64, i64, %record.Q } + +%ObjLayout.0 = type { i8* } +%ObjLayout.S = type { %ObjLayout.0, i64, i64, %record.T } + +; CHECK: %10 = bitcast %record.T* %2 to i8* +; CHECK: %11 = getelementptr inbounds i8, i8* %10, i32 16 +; CHECK: %12 = bitcast i8* %11 to %record.Q* +; CHECK: call void @f2(i8* null, %record.Q* nonnull %12) +define void @foo(i8 addrspace(1)* nocapture readonly %s) { + %1 = alloca %record.Q, align 8 + %2 = alloca %record.T, align 8 + %3 = bitcast i8 addrspace(1)* %s to %ObjLayout.S addrspace(1)* + %4 = getelementptr inbounds %ObjLayout.S, %ObjLayout.S addrspace(1)* %3, i64 0, i32 3 + %5 = bitcast %record.T* %2 to i8* + %6 = bitcast %record.T addrspace(1)* %4 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(32) %5, i8 addrspace(1)* noundef align 8 dereferenceable(32) %6, i64 32, i1 false) + call void @f1(i8* null, %record.T* nonnull %2) + %7 = getelementptr inbounds %ObjLayout.S, %ObjLayout.S addrspace(1)* %3, i64 0, i32 3, i32 2 + %8 = bitcast %record.Q* %1 to i8* + %9 = bitcast %record.Q addrspace(1)* %7 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %8, i8 addrspace(1)* noundef align 8 dereferenceable(16) %9, i64 16, i1 false) + call void @f2(i8* null, %record.Q* nonnull %1) + ret void +} + +declare void @f1(i8* %"$BP", %record.T* noalias nocapture readonly %t) #1 +declare void @f2(i8* %"$BP", %record.Q* noalias nocapture readonly %q) #1 + +declare void @llvm.memcpy.p0i8.p1i8.i64(i8* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) #0 + +attributes #0 = { argmemonly nocallback nofree nounwind willreturn } +attributes #1 = { argmemonly } \ No newline at end of file diff --git a/llvm/test/Transforms/CJRewriteStatepoint/X86/intrinsic-attributes.ll b/llvm/test/Transforms/CJRewriteStatepoint/X86/intrinsic-attributes.ll new file mode 100644 index 000000000..3ce901df9 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/X86/intrinsic-attributes.ll @@ -0,0 +1,10 @@ +; RUN: opt < %s -S -cj-rewrite-statepoint | FileCheck %s + +; CHECK: Function Attrs: nounwind readnone +; CHECK: declare i64 @llvm.x86.sse2.cvttsd2si64(<2 x double>) +declare i64 @llvm.x86.sse2.cvttsd2si64(<2 x double>) + +define i64 @test(<2 x double> %arg) { + %ret = call i64 @llvm.x86.sse2.cvttsd2si64(<2 x double> %arg) + ret i64 %ret +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/X86/lit.local.cfg b/llvm/test/Transforms/CJRewriteStatepoint/X86/lit.local.cfg new file mode 100644 index 000000000..c8625f4d9 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/X86/lit.local.cfg @@ -0,0 +1,2 @@ +if not 'X86' in config.root.targets: + config.unsupported = True diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-inference.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-inference.ll new file mode 100644 index 000000000..ae2662dfe --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-inference.ll @@ -0,0 +1,296 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S 2>&1 | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S 2>&1 | FileCheck %s + +; A collection of tests for exercising the base inference logic in the +; findBasePointers. That is, the logic which proves a potentially derived +; pointer is actually a base pointer itself. + +define i8 addrspace(1)* @test(i8 addrspace(1)* %a) gc "cangjie" { +; CHECK-LABEL: @test( +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[A:%.*]]) ] +; CHECK-NEXT: [[A_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret i8 addrspace(1)* [[A_RELOC]] +; + call void @foo() + ret i8 addrspace(1)* %a +} + +define i8 addrspace(1)* @test_select(i1 %c, i8 addrspace(1)* %a1, i8 addrspace(1)* %a2) gc "cangjie" { +; CHECK-LABEL: @test_select( +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C:%.*]], i8 addrspace(1)* [[A1:%.*]], i8 addrspace(1)* [[A2:%.*]] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[SEL]]) ] +; CHECK-NEXT: [[SEL_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret i8 addrspace(1)* [[SEL_RELOC]] +; + %sel = select i1 %c, i8 addrspace(1)* %a1, i8 addrspace(1)* %a2 + call void @foo() + ret i8 addrspace(1)* %sel +} + +define i8 addrspace(1)* @test_phi1(i1 %c, i8 addrspace(1)* %a1, i8 addrspace(1)* %a2) gc "cangjie" { +; CHECK-LABEL: @test_phi1( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C:%.*]], label [[TAKEN:%.*]], label [[UNTAKEN:%.*]] +; CHECK: taken: +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: untaken: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[PHI:%.*]] = phi i8 addrspace(1)* [ [[A1:%.*]], [[TAKEN]] ], [ [[A2:%.*]], [[UNTAKEN]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[PHI]]) ] +; CHECK-NEXT: [[PHI_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret i8 addrspace(1)* [[PHI_RELOC]] +; +entry: + br i1 %c, label %taken, label %untaken +taken: + br label %merge +untaken: + br label %merge +merge: + %phi = phi i8 addrspace(1)* [%a1, %taken], [%a2, %untaken] + call void @foo() + ret i8 addrspace(1)* %phi +} + +define i8 addrspace(1)* @test_phi_lcssa(i1 %c, i8 addrspace(1)* %a1, i8 addrspace(1)* %a2) gc "cangjie" { +; CHECK-LABEL: @test_phi_lcssa( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: merge: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[A1:%.*]]) ] +; CHECK-NEXT: [[A1_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret i8 addrspace(1)* [[A1_RELOC]] +; +entry: + br label %merge +merge: + %phi = phi i8 addrspace(1)* [%a1, %entry] + call void @foo() + ret i8 addrspace(1)* %phi +} + + +define i8 addrspace(1)* @test_loop1(i1 %c, i8 addrspace(1)* %a1, i8 addrspace(1)* %a2) gc "cangjie" { +; CHECK-LABEL: @test_loop1( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[PHI:%.*]] = phi i8 addrspace(1)* [ [[A1:%.*]], [[ENTRY:%.*]] ], [ [[A2:%.*]], [[LOOP]] ] +; CHECK-NEXT: br i1 [[C:%.*]], label [[EXIT:%.*]], label [[LOOP]] +; CHECK: exit: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[PHI]]) ] +; CHECK-NEXT: [[PHI_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret i8 addrspace(1)* [[PHI_RELOC]] +; +entry: + br label %loop +loop: + %phi = phi i8 addrspace(1)* [%a1, %entry], [%a2, %loop] + br i1 %c, label %exit, label %loop +exit: + %phi2 = phi i8 addrspace(1)* [%phi, %loop] + call void @foo() + ret i8 addrspace(1)* %phi2 +} + +define i8 addrspace(1)* @test_loop2(i1 %c, i8 addrspace(1)* %a1) gc "cangjie" { +; CHECK-LABEL: @test_loop2( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[PHI:%.*]] = phi i8 addrspace(1)* [ [[A1:%.*]], [[ENTRY:%.*]] ], [ [[O2:%.*]], [[LOOP]] ] +; CHECK-NEXT: [[ADDR:%.*]] = bitcast i8 addrspace(1)* [[PHI]] to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: [[O2]] = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* [[ADDR]], align 8 +; CHECK-NEXT: br i1 [[C:%.*]], label [[EXIT:%.*]], label [[LOOP]] +; CHECK: exit: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[PHI]]) ] +; CHECK-NEXT: [[PHI_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret i8 addrspace(1)* [[PHI_RELOC]] +; +entry: + br label %loop +loop: + %phi = phi i8 addrspace(1)* [%a1, %entry], [%o2, %loop] + %addr = bitcast i8 addrspace(1)* %phi to i8 addrspace(1)* addrspace(1)* + %o2 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %addr + br i1 %c, label %exit, label %loop +exit: + %phi2 = phi i8 addrspace(1)* [%phi, %loop] + call void @foo() + ret i8 addrspace(1)* %phi2 +} + +; %phi1 and phi2 are not base pointers, but they do have a single +; base pointer which is %a1 +define i8 addrspace(1)* @test_loop3(i1 %c, i8 addrspace(1)* %a1) gc "cangjie" { +; CHECK-LABEL: @test_loop3( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[PHI:%.*]] = phi i8 addrspace(1)* [ [[A1:%.*]], [[ENTRY:%.*]] ], [ [[GEP:%.*]], [[LOOP]] ] +; CHECK-NEXT: [[GEP]] = getelementptr i8, i8 addrspace(1)* [[PHI]], i64 16 +; CHECK-NEXT: br i1 [[C:%.*]], label [[EXIT:%.*]], label [[LOOP]] +; CHECK: exit: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[PHI]], i8 addrspace(1)* [[A1]]) ] +; CHECK-NEXT: [[PHI_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[A1_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: ret i8 addrspace(1)* [[PHI_RELOC]] +; +entry: + br label %loop +loop: + %phi = phi i8 addrspace(1)* [%a1, %entry], [%gep, %loop] + %gep = getelementptr i8, i8 addrspace(1)* %phi, i64 16 + br i1 %c, label %exit, label %loop +exit: + %phi2 = phi i8 addrspace(1)* [%phi, %loop] + call void @foo() + ret i8 addrspace(1)* %phi2 +} + +define <2 x i8 addrspace(1)*> @test_vec_passthrough(<2 x i8 addrspace(1)*> %a) gc "cangjie" { +; CHECK-LABEL: @test_vec_passthrough( +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(<2 x i8 addrspace(1)*> [[A:%.*]]) ] +; CHECK-NEXT: [[A_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret <2 x i8 addrspace(1)*> [[A_RELOC]] +; + call void @foo() + ret <2 x i8 addrspace(1)*> %a +} + + +define <2 x i8 addrspace(1)*> @test_insert(i8 addrspace(1)* %a) gc "cangjie" { +; CHECK-LABEL: @test_insert( +; CHECK-NEXT: [[VEC:%.*]] = insertelement <2 x i8 addrspace(1)*> zeroinitializer, i8 addrspace(1)* [[A:%.*]], i64 0 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(<2 x i8 addrspace(1)*> [[VEC]]) ] +; CHECK-NEXT: [[VEC_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret <2 x i8 addrspace(1)*> [[VEC_RELOC]] +; + %vec = insertelement <2 x i8 addrspace(1)*> zeroinitializer, i8 addrspace(1)* %a, i64 0 + call void @foo() + ret <2 x i8 addrspace(1)*> %vec +} + +define i8 addrspace(1)* @test_extract(<2 x i8 addrspace(1)*> %a) gc "cangjie" { +; CHECK-LABEL: @test_extract( +; CHECK-NEXT: [[EE:%.*]] = extractelement <2 x i8 addrspace(1)*> [[A:%.*]], i64 0 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[EE]]) ] +; CHECK-NEXT: [[EE_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret i8 addrspace(1)* [[EE_RELOC]] +; + %ee = extractelement <2 x i8 addrspace(1)*> %a, i64 0 + call void @foo() + ret i8 addrspace(1)* %ee +} + +define <2 x i8 addrspace(1)*> @test_shuffle(<2 x i8 addrspace(1)*> %a1) gc "cangjie" { +; CHECK-LABEL: @test_shuffle( +; CHECK-NEXT: [[RES:%.*]] = shufflevector <2 x i8 addrspace(1)*> [[A1:%.*]], <2 x i8 addrspace(1)*> [[A1]], <2 x i32> zeroinitializer +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(<2 x i8 addrspace(1)*> [[RES]]) ] +; CHECK-NEXT: [[RES_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret <2 x i8 addrspace(1)*> [[RES_RELOC]] +; + %res = shufflevector <2 x i8 addrspace(1)*> %a1, <2 x i8 addrspace(1)*> %a1, <2 x i32> zeroinitializer + call void @foo() + ret <2 x i8 addrspace(1)*> %res +} + +define <2 x i8 addrspace(1)*> @test_shuffle2(<2 x i8 addrspace(1)*> %a1, <2 x i8 addrspace(1)*> %a2) gc "cangjie" { +; CHECK-LABEL: @test_shuffle2( +; CHECK-NEXT: [[RES:%.*]] = shufflevector <2 x i8 addrspace(1)*> [[A1:%.*]], <2 x i8 addrspace(1)*> [[A2:%.*]], <2 x i32> zeroinitializer +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(<2 x i8 addrspace(1)*> [[RES]]) ] +; CHECK-NEXT: [[RES_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret <2 x i8 addrspace(1)*> [[RES_RELOC]] +; + %res = shufflevector <2 x i8 addrspace(1)*> %a1, <2 x i8 addrspace(1)*> %a2, <2 x i32> zeroinitializer + call void @foo() + ret <2 x i8 addrspace(1)*> %res +} + +define <4 x i8 addrspace(1)*> @test_shuffle_concat(<2 x i8 addrspace(1)*> %a1, <2 x i8 addrspace(1)*> %a2) gc "cangjie" { +; CHECK-LABEL: @test_shuffle_concat( +; CHECK-NEXT: [[RES:%.*]] = shufflevector <2 x i8 addrspace(1)*> [[A1:%.*]], <2 x i8 addrspace(1)*> [[A2:%.*]], <4 x i32> +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(<4 x i8 addrspace(1)*> [[RES]]) ] +; CHECK-NEXT: [[RES_RELOC:%.*]] = call coldcc <4 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v4p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret <4 x i8 addrspace(1)*> [[RES_RELOC]] +; + %res = shufflevector <2 x i8 addrspace(1)*> %a1, <2 x i8 addrspace(1)*> %a2, <4 x i32> + call void @foo() + ret <4 x i8 addrspace(1)*> %res +} + +; TODO: Special case worth handling - we interpret the shuffle as if we need +; to select the base pointers from either input when the mask is known. +define <2 x i8 addrspace(1)*> @test_shuffle_broadcast(i8 addrspace(1)* %a) gc "cangjie" { +; CHECK-LABEL: @test_shuffle_broadcast( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[IE:%.*]] = insertelement <2 x i8 addrspace(1)*> zeroinitializer, i8 addrspace(1)* [[A:%.*]], i64 0 +; CHECK-NEXT: [[BROADCAST:%.*]] = shufflevector <2 x i8 addrspace(1)*> [[IE]], <2 x i8 addrspace(1)*> undef, <2 x i32> zeroinitializer +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(<2 x i8 addrspace(1)*> [[BROADCAST]]) ] +; CHECK-NEXT: [[BROADCAST_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret <2 x i8 addrspace(1)*> [[BROADCAST_RELOC]] +; +entry: + %ie = insertelement <2 x i8 addrspace(1)*> zeroinitializer, i8 addrspace(1)* %a, i64 0 + %broadcast = shufflevector <2 x i8 addrspace(1)*> %ie, <2 x i8 addrspace(1)*> undef, <2 x i32> zeroinitializer + call void @foo() + ret <2 x i8 addrspace(1)*> %broadcast +} + +; Show a case where only a portion of the sub-graph propagates base pointers. +define i8 @test_subgraph(i1 %c, i8 addrspace(1)* %a1, i8 addrspace(1)* %a2) gc "cangjie" { +; CHECK-LABEL: @test_subgraph( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C:%.*]], i8 addrspace(1)* [[A1:%.*]], i8 addrspace(1)* [[A2:%.*]] +; CHECK-NEXT: br i1 [[C]], label [[TAKEN:%.*]], label [[MERGE:%.*]] +; CHECK: taken: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, i8 addrspace(1)* [[SEL]], i64 8 +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[PHI:%.*]] = phi i8 addrspace(1)* [ [[GEP]], [[TAKEN]] ], [ [[SEL]], [[ENTRY:%.*]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[PHI]], i8 addrspace(1)* [[SEL]]) ] +; CHECK-NEXT: [[PHI_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[SEL_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[RES:%.*]] = load i8, i8 addrspace(1)* [[PHI_RELOC]], align 1 +; CHECK-NEXT: ret i8 [[RES]] +; +entry: + %sel = select i1 %c, i8 addrspace(1)* %a1, i8 addrspace(1)* %a2 + br i1 %c, label %taken, label %merge +taken: + %gep = getelementptr i8, i8 addrspace(1)* %sel, i64 8 + br label %merge +merge: + %phi = phi i8 addrspace(1)* [%gep, %taken], [%sel, %entry] + call void @foo() + %res = load i8, i8 addrspace(1)* %phi + ret i8 %res +} + +; An example of a non-trivial subgraph computing base pointers. +define i8 @test_subgraph2(i1 %c, i8 addrspace(1)* %a1, i8 addrspace(1)* %a2) gc "cangjie" { +; CHECK-LABEL: @test_subgraph2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C:%.*]], i8 addrspace(1)* [[A1:%.*]], i8 addrspace(1)* [[A2:%.*]] +; CHECK-NEXT: [[IE:%.*]] = insertelement <2 x i8 addrspace(1)*> zeroinitializer, i8 addrspace(1)* [[SEL]], i64 0 +; CHECK-NEXT: [[BROADCAST:%.*]] = shufflevector <2 x i8 addrspace(1)*> [[IE]], <2 x i8 addrspace(1)*> [[IE]], <2 x i32> zeroinitializer +; CHECK-NEXT: [[EE:%.*]] = extractelement <2 x i8 addrspace(1)*> [[BROADCAST]], i32 1 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[EE]]) ] +; CHECK-NEXT: [[EE_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[RES:%.*]] = load i8, i8 addrspace(1)* [[EE_RELOC]], align 1 +; CHECK-NEXT: ret i8 [[RES]] +; +entry: + %sel = select i1 %c, i8 addrspace(1)* %a1, i8 addrspace(1)* %a2 + %ie = insertelement <2 x i8 addrspace(1)*> zeroinitializer, i8 addrspace(1)* %sel, i64 0 + %broadcast = shufflevector <2 x i8 addrspace(1)*> %ie, <2 x i8 addrspace(1)*> %ie, <2 x i32> zeroinitializer + %ee = extractelement <2 x i8 addrspace(1)*> %broadcast, i32 1 + call void @foo() + %res = load i8, i8 addrspace(1)* %ee + ret i8 %res +} + + +declare void @foo() diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-inttoptr.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-inttoptr.ll new file mode 100644 index 000000000..7ef3b89a8 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-inttoptr.ll @@ -0,0 +1,19 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -cj-rewrite-statepoint < %s | FileCheck %s + +target triple = "x86_64-unknown-linux-gnu" +target datalayout = "e-ni:1:6" + +declare void @foo() + +define i8 addrspace(1)* @test(i64 %i) gc "cangjie" { +; CHECK-LABEL: @test( +; CHECK-NEXT: [[P:%.*]] = inttoptr i64 [[I:%.*]] to i8 addrspace(1)* +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[P]]) ] +; CHECK-NEXT: [[P_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret i8 addrspace(1)* [[P_RELOC]] +; + %p = inttoptr i64 %i to i8 addrspace(1)* + call void @foo() + ret i8 addrspace(1)* %p +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-1.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-1.ll new file mode 100644 index 000000000..9b9af4176 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-1.ll @@ -0,0 +1,44 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S 2>&1 | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S 2>&1 | FileCheck %s + + +declare void @site_for_call_safpeoint() + +; derived %merged_value base %merged_value.base +define i64 addrspace(1)* @test(i64 addrspace(1)* %base_obj_x, i64 addrspace(1)* %base_obj_y, i1 %runtime_condition) gc "cangjie" { +; CHECK-LABEL: @test( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[RUNTIME_CONDITION:%.*]], label [[HERE:%.*]], label [[THERE:%.*]] +; CHECK: here: +; CHECK-NEXT: [[X:%.*]] = getelementptr i64, i64 addrspace(1)* [[BASE_OBJ_X:%.*]], i32 1 +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: there: +; CHECK-NEXT: [[Y:%.*]] = getelementptr i64, i64 addrspace(1)* [[BASE_OBJ_Y:%.*]], i32 1 +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[MERGED_VALUE_BASE:%.*]] = phi i64 addrspace(1)* [ [[BASE_OBJ_X]], [[HERE]] ], [ [[BASE_OBJ_Y]], [[THERE]] ], !is_base_value !1 +; CHECK-NEXT: [[MERGED_VALUE:%.*]] = phi i64 addrspace(1)* [ [[X]], [[HERE]] ], [ [[Y]], [[THERE]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @site_for_call_safpeoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[MERGED_VALUE]], i64 addrspace(1)* [[MERGED_VALUE_BASE]]) ] +; CHECK-NEXT: [[MERGED_VALUE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[MERGED_VALUE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[MERGED_VALUE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[MERGED_VALUE_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[MERGED_VALUE_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[MERGED_VALUE_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[MERGED_VALUE_RELOCATED_CASTED]] +; +entry: + br i1 %runtime_condition, label %here, label %there + +here: ; preds = %entry + %x = getelementptr i64, i64 addrspace(1)* %base_obj_x, i32 1 + br label %merge + +there: ; preds = %entry + %y = getelementptr i64, i64 addrspace(1)* %base_obj_y, i32 1 + br label %merge + +merge: ; preds = %there, %here + %merged_value = phi i64 addrspace(1)* [ %x, %here ], [ %y, %there ] + call void @site_for_call_safpeoint() + ret i64 addrspace(1)* %merged_value +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-10.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-10.ll new file mode 100644 index 000000000..402034d2f --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-10.ll @@ -0,0 +1,67 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S 2>&1 | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S 2>&1 | FileCheck %s + +declare i1 @runtime_value() "gc-leaf-function" + +declare void @do_safepoint() + +define void @select_of_phi(i64 addrspace(1)* %base_obj_x, i64 addrspace(1)* %base_obj_y) gc "cangjie" { +; CHECK-LABEL: @select_of_phi( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[DOT01:%.*]] = phi i64 addrspace(1)* [ [[BASE_OBJ_X:%.*]], [[ENTRY:%.*]] ], [ [[BASE_OBJ_X_RELOCATED_CASTED:%.*]], [[MERGE:%.*]] ] +; CHECK-NEXT: [[DOT0:%.*]] = phi i64 addrspace(1)* [ [[BASE_OBJ_Y:%.*]], [[ENTRY]] ], [ [[BASE_OBJ_Y_RELOCATED_CASTED:%.*]], [[MERGE]] ] +; CHECK-NEXT: [[CURRENT_X:%.*]] = phi i64 addrspace(1)* [ [[BASE_OBJ_X]], [[ENTRY]] ], [ [[NEXT_X_RELOCATED_CASTED:%.*]], [[MERGE]] ] +; CHECK-NEXT: [[CURRENT_Y:%.*]] = phi i64 addrspace(1)* [ [[BASE_OBJ_Y]], [[ENTRY]] ], [ [[NEXT_Y_RELOCATED_CASTED:%.*]], [[MERGE]] ] +; CHECK-NEXT: [[CURRENT:%.*]] = phi i64 addrspace(1)* [ null, [[ENTRY]] ], [ [[NEXT_RELOCATED_CASTED:%.*]], [[MERGE]] ] +; CHECK-NEXT: [[CONDITION:%.*]] = call i1 @runtime_value() +; CHECK-NEXT: [[NEXT_X:%.*]] = getelementptr i64, i64 addrspace(1)* [[CURRENT_X]], i32 1 +; CHECK-NEXT: [[NEXT_Y:%.*]] = getelementptr i64, i64 addrspace(1)* [[CURRENT_Y]], i32 1 +; CHECK-NEXT: br i1 [[CONDITION]], label [[TRUE:%.*]], label [[FALSE:%.*]] +; CHECK: true: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: false: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[NEXT_BASE:%.*]] = phi i64 addrspace(1)* [ [[DOT01]], [[TRUE]] ], [ [[DOT0]], [[FALSE]] ], !is_base_value !1 +; CHECK-NEXT: [[NEXT:%.*]] = phi i64 addrspace(1)* [ [[NEXT_X]], [[TRUE]] ], [ [[NEXT_Y]], [[FALSE]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[NEXT_X]], i64 addrspace(1)* [[NEXT_Y]], i64 addrspace(1)* [[NEXT]], i64 addrspace(1)* [[DOT01]], i64 addrspace(1)* [[DOT0]], i64 addrspace(1)* [[NEXT_BASE]]) ] +; CHECK-NEXT: [[NEXT_X_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 3, i32 0) +; CHECK-NEXT: [[NEXT_X_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[NEXT_X_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[NEXT_Y_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 4, i32 1) +; CHECK-NEXT: [[NEXT_Y_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[NEXT_Y_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[NEXT_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 5, i32 2) +; CHECK-NEXT: [[NEXT_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[NEXT_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[BASE_OBJ_X_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 3, i32 3) +; CHECK-NEXT: [[BASE_OBJ_X_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[BASE_OBJ_X_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[BASE_OBJ_Y_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 4, i32 4) +; CHECK-NEXT: [[BASE_OBJ_Y_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[BASE_OBJ_Y_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[NEXT_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 5, i32 5) +; CHECK-NEXT: [[NEXT_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[NEXT_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: br label [[LOOP]] +; +entry: + br label %loop + +loop: ; preds = %merge, %entry + %current_x = phi i64 addrspace(1)* [ %base_obj_x, %entry ], [ %next_x, %merge ] + %current_y = phi i64 addrspace(1)* [ %base_obj_y, %entry ], [ %next_y, %merge ] + %current = phi i64 addrspace(1)* [ null, %entry ], [ %next, %merge ] + %condition = call i1 @runtime_value() + %next_x = getelementptr i64, i64 addrspace(1)* %current_x, i32 1 + %next_y = getelementptr i64, i64 addrspace(1)* %current_y, i32 1 + br i1 %condition, label %true, label %false + +true: ; preds = %loop + br label %merge + +false: ; preds = %loop + br label %merge + +merge: ; preds = %false, %true + %next = phi i64 addrspace(1)* [ %next_x, %true ], [ %next_y, %false ] + call void @do_safepoint() + br label %loop +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-11.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-11.ll new file mode 100644 index 000000000..b94f4d5bd --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-11.ll @@ -0,0 +1,34 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S 2>&1 | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S 2>&1 | FileCheck %s + + +declare void @do_safepoint() + +; derived %next base %base_obj +define void @test(i64 addrspace(1)* %base_obj) gc "cangjie" { +; CHECK-LABEL: @test( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[OBJ:%.*]] = getelementptr i64, i64 addrspace(1)* [[BASE_OBJ:%.*]], i32 1 +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[DOT0:%.*]] = phi i64 addrspace(1)* [ [[BASE_OBJ]], [[ENTRY:%.*]] ], [ [[BASE_OBJ_RELOCATED_CASTED:%.*]], [[LOOP]] ] +; CHECK-NEXT: [[CURRENT:%.*]] = phi i64 addrspace(1)* [ [[OBJ]], [[ENTRY]] ], [ [[NEXT_RELOCATED_CASTED:%.*]], [[LOOP]] ] +; CHECK-NEXT: [[NEXT:%.*]] = getelementptr i64, i64 addrspace(1)* [[CURRENT]], i32 1 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[NEXT]], i64 addrspace(1)* [[DOT0]]) ] +; CHECK-NEXT: [[NEXT_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[NEXT_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[NEXT_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[BASE_OBJ_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[BASE_OBJ_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[BASE_OBJ_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: br label [[LOOP]] +; +entry: + %obj = getelementptr i64, i64 addrspace(1)* %base_obj, i32 1 + br label %loop + +loop: ; preds = %loop, %entry + %current = phi i64 addrspace(1)* [ %obj, %entry ], [ %next, %loop ] + %next = getelementptr i64, i64 addrspace(1)* %current, i32 1 + call void @do_safepoint() + br label %loop +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-12.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-12.ll new file mode 100644 index 000000000..979f9d304 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-12.ll @@ -0,0 +1,26 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S 2>&1 | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S 2>&1 | FileCheck %s + + +@global = external addrspace(1) global i8 + +; derived %select base null +define i8 @test(i1 %cond) gc "cangjie" { +; CHECK-LABEL: @test( +; CHECK-NEXT: [[DERIVED1:%.*]] = getelementptr i8, i8 addrspace(1)* @global, i64 1 +; CHECK-NEXT: [[DERIVED2:%.*]] = getelementptr i8, i8 addrspace(1)* @global, i64 2 +; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND:%.*]], i8 addrspace(1)* [[DERIVED1]], i8 addrspace(1)* [[DERIVED2]] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @extern, i32 0, i32 0) +; CHECK-NEXT: [[LOAD:%.*]] = load i8, i8 addrspace(1)* [[SELECT]], align 1 +; CHECK-NEXT: ret i8 [[LOAD]] +; + %derived1 = getelementptr i8, i8 addrspace(1)* @global, i64 1 + %derived2 = getelementptr i8, i8 addrspace(1)* @global, i64 2 + %select = select i1 %cond, i8 addrspace(1)* %derived1, i8 addrspace(1)* %derived2 + call void @extern() + %load = load i8, i8 addrspace(1)* %select + ret i8 %load +} + +declare void @extern() gc "cangjie" diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-13.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-13.ll new file mode 100644 index 000000000..dc6d54a42 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-13.ll @@ -0,0 +1,20 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S 2>&1 | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S 2>&1 | FileCheck %s + +@global = external addrspace(1) global i8 + +define i8 @test(i64 %offset) gc "cangjie" { +; CHECK-LABEL: @test( +; CHECK-NEXT: [[DERIVED:%.*]] = getelementptr i8, i8 addrspace(1)* @global, i64 [[OFFSET:%.*]] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @extern, i32 0, i32 0) +; CHECK-NEXT: [[LOAD:%.*]] = load i8, i8 addrspace(1)* [[DERIVED]], align 1 +; CHECK-NEXT: ret i8 [[LOAD]] +; + %derived = getelementptr i8, i8 addrspace(1)* @global, i64 %offset + call void @extern() + %load = load i8, i8 addrspace(1)* %derived + ret i8 %load +} + +declare void @extern() gc "cangjie" diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-14.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-14.ll new file mode 100644 index 000000000..8d1a16100 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-14.ll @@ -0,0 +1,285 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -cj-rewrite-statepoint < %s | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128-ni:1-p2:32:8:8:32-ni:2" +target triple = "x86_64-unknown-linux-gnu" + +declare void @foo() gc "cangjie" + +; FIXME: In this test case %b6.base, which is inserted by RS4GC, is identical +; to %b6. +define i8 addrspace(1)* @test1(i1 %c, i8 addrspace(1)* %b1, i8 addrspace(1)* %b2) gc "cangjie" { +; CHECK-LABEL: @test1( +; CHECK-NEXT: left: +; CHECK-NEXT: br i1 [[C:%.*]], label [[LOOP:%.*]], label [[MERGE2:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[B5:%.*]] = phi i8 addrspace(1)* [ [[B2:%.*]], [[LEFT:%.*]] ], [ [[B5]], [[LOOP]] ] +; CHECK-NEXT: br i1 [[C]], label [[LOOP]], label [[MERGE2]] +; CHECK: merge2: +; CHECK-NEXT: [[B6:%.*]] = phi i8 addrspace(1)* [ [[B1:%.*]], [[LEFT]] ], [ [[B5]], [[LOOP]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[B6]]) ] +; CHECK-NEXT: [[B6_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret i8 addrspace(1)* [[B6_RELOCATED]] +; +left: + br i1 %c, label %loop, label %merge2 + +loop: + %b5 = phi i8 addrspace(1)* [ %b2, %left ], [ %b5, %loop ] + br i1 %c, label %loop, label %merge2 + +merge2: + %b6 = phi i8 addrspace(1)* [ %b1, %left ], [ %b5, %loop ] + call void @foo() + ret i8 addrspace(1)* %b6 +} + +define i8 addrspace(1)* @test2(i1 %c, i32 %n, i8 addrspace(1)* %b1, i8 addrspace(1)* %b2) gc "cangjie" { +; CHECK-LABEL: @test2( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LEFT:%.*]] +; CHECK: left: +; CHECK-NEXT: br i1 [[C:%.*]], label [[LOOP:%.*]], label [[MERGE2:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[B5:%.*]] = phi i8 addrspace(1)* [ [[B2:%.*]], [[LEFT]] ], [ [[B5]], [[LOOP]] ], [ [[B5]], [[LOOP]] ] +; CHECK-NEXT: switch i32 [[N:%.*]], label [[MERGE2]] [ +; CHECK-NEXT: i32 0, label [[LOOP]] +; CHECK-NEXT: i32 1, label [[LOOP]] +; CHECK-NEXT: i32 2, label [[LEFT]] +; CHECK-NEXT: ] +; CHECK: merge2: +; CHECK-NEXT: [[B6:%.*]] = phi i8 addrspace(1)* [ [[B1:%.*]], [[LEFT]] ], [ [[B5]], [[LOOP]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[B6]]) ] +; CHECK-NEXT: [[B6_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret i8 addrspace(1)* [[B6_RELOCATED]] +; +entry: + br label %left + +left: + br i1 %c, label %loop, label %merge2 + +loop: + %b5 = phi i8 addrspace(1)* [ %b2, %left ], [ %b5, %loop ], [ %b5, %loop ] + switch i32 %n, label %merge2 [ i32 0, label %loop + i32 1, label %loop + i32 2, label %left ] + +merge2: + %b6 = phi i8 addrspace(1)* [ %b1, %left ], [ %b5, %loop ] + call void @foo() + ret i8 addrspace(1)* %b6 +} + +; FIXME: In this test case %b5.base and %b6.base (inserted by RS4GC) are +; identical to %b5 and %b6 ; correspondingly. +define i8 addrspace(1)* @test3(i1 %c, i8 addrspace(1)* %b1, i8 addrspace(1)* %b2) gc "cangjie" { +; CHECK-LABEL: @test3( +; CHECK-NEXT: left: +; CHECK-NEXT: br i1 [[C:%.*]], label [[LOOP:%.*]], label [[MERGE2:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[B5_BASE:%.*]] = phi i8 addrspace(1)* [ [[B2:%.*]], [[LEFT:%.*]] ], [ [[B5_BASE]], [[LOOP]] ], [ [[B6_BASE_RELOCATED:%.*]], [[MERGE2]] ], !is_base_value !1 +; CHECK-NEXT: [[B5:%.*]] = phi i8 addrspace(1)* [ [[B2]], [[LEFT]] ], [ [[B5]], [[LOOP]] ], [ [[B6_RELOCATED:%.*]], [[MERGE2]] ] +; CHECK-NEXT: br i1 [[C]], label [[LOOP]], label [[MERGE2]] +; CHECK: merge2: +; CHECK-NEXT: [[B6_BASE:%.*]] = phi i8 addrspace(1)* [ [[B1:%.*]], [[LEFT]] ], [ [[B5_BASE]], [[LOOP]] ], !is_base_value !1 +; CHECK-NEXT: [[B6:%.*]] = phi i8 addrspace(1)* [ [[B1]], [[LEFT]] ], [ [[B5]], [[LOOP]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[B6_BASE]], i8 addrspace(1)* [[B6]]) ] +; CHECK-NEXT: [[B6_BASE_RELOCATED]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[B6_RELOCATED]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 1) +; CHECK-NEXT: br i1 [[C]], label [[LOOP]], label [[EXIT:%.*]] +; CHECK: exit: +; CHECK-NEXT: ret i8 addrspace(1)* [[B6_RELOCATED]] +; +left: + br i1 %c, label %loop, label %merge2 + +loop: + %b5 = phi i8 addrspace(1)* [ %b2, %left ], [ %b5, %loop ], [ %b6, %merge2 ] + br i1 %c, label %loop, label %merge2 + +merge2: + %b6 = phi i8 addrspace(1)* [ %b1, %left ], [ %b5, %loop ] + call void @foo() + br i1 %c, label %loop, label %exit + +exit: + ret i8 addrspace(1)* %b6 +} + +define i8 addrspace(1)* @test4(i1 %c, i8 addrspace(1)* %b1, i8 addrspace(1)* %b2) gc "cangjie" { +; CHECK-LABEL: @test4( +; CHECK-NEXT: left: +; CHECK-NEXT: br i1 [[C:%.*]], label [[LOOP:%.*]], label [[MERGE2:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[B3:%.*]] = phi i8 addrspace(1)* [ [[B2:%.*]], [[LEFT:%.*]] ], [ [[B5:%.*]], [[LOOP]] ] +; CHECK-NEXT: [[B4:%.*]] = bitcast i8 addrspace(1)* [[B3]] to i32 addrspace(1)* +; CHECK-NEXT: [[B5]] = bitcast i32 addrspace(1)* [[B4]] to i8 addrspace(1)* +; CHECK-NEXT: br i1 [[C]], label [[LOOP]], label [[MERGE2]] +; CHECK: merge2: +; CHECK-NEXT: [[B6:%.*]] = phi i8 addrspace(1)* [ [[B1:%.*]], [[LEFT]] ], [ [[B5]], [[LOOP]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[B6]]) ] +; CHECK-NEXT: [[B6_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret i8 addrspace(1)* [[B6_RELOCATED]] +; +left: + br i1 %c, label %loop, label %merge2 + +loop: + %b3 = phi i8 addrspace(1)* [ %b2, %left ], [ %b5, %loop ] + %b4 = bitcast i8 addrspace(1)* %b3 to i32 addrspace(1)* + %b5 = bitcast i32 addrspace(1)* %b4 to i8 addrspace(1)* + br i1 %c, label %loop, label %merge2 + +merge2: + %b6 = phi i8 addrspace(1)* [ %b1, %left ], [ %b5, %loop ] + call void @foo() + ret i8 addrspace(1)* %b6 +} + +define i8 addrspace(1)* @test5(i1 %c1, i1 %c2, i8 addrspace(1)* %b1, i8 addrspace(1)* %b2) gc "cangjie" { +; CHECK-LABEL: @test5( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C1:%.*]], label [[LOOP:%.*]], label [[MERGE2:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[B3:%.*]] = phi i8 addrspace(1)* [ [[B2:%.*]], [[ENTRY:%.*]] ], [ [[B5:%.*]], [[LEFT:%.*]] ] +; CHECK-NEXT: [[B4:%.*]] = addrspacecast i8 addrspace(1)* [[B3]] to i8* +; CHECK-NEXT: br i1 [[C1]], label [[LEFT]], label [[MERGE2]] +; CHECK: left: +; CHECK-NEXT: [[B5]] = addrspacecast i8* [[B4]] to i8 addrspace(1)* +; CHECK-NEXT: br i1 [[C2:%.*]], label [[LOOP]], label [[MERGE2]] +; CHECK: merge2: +; CHECK-NEXT: [[B6:%.*]] = phi i8 addrspace(1)* [ [[B1:%.*]], [[ENTRY]] ], [ [[B3]], [[LOOP]] ], [ [[B5]], [[LEFT]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[B6]]) ] +; CHECK-NEXT: [[B6_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret i8 addrspace(1)* [[B6_RELOCATED]] +; +entry: + br i1 %c1, label %loop, label %merge2 + +loop: + %b3 = phi i8 addrspace(1)* [ %b2, %entry ], [ %b5, %left ] + %b4 = addrspacecast i8 addrspace(1)* %b3 to i8* + br i1 %c1, label %left, label %merge2 + +left: + %b5 = addrspacecast i8* %b4 to i8 addrspace(1)* + br i1 %c2, label %loop, label %merge2 + +merge2: + %b6 = phi i8 addrspace(1)* [ %b1, %entry ], [ %b3, %loop ], [ %b5, %left ] + call void @foo() + ret i8 addrspace(1)* %b6 +} + +define i8 addrspace(1)* @test6(i1 %c1, i1 %c2, i8 addrspace(1)* %b1, i8 addrspace(1)* %b2) gc "cangjie" { +; CHECK-LABEL: @test6( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C1:%.*]], label [[LOOP:%.*]], label [[MERGE2:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[B3:%.*]] = phi i8 addrspace(1)* [ [[B2:%.*]], [[ENTRY:%.*]] ], [ [[B3]], [[LEFT:%.*]] ] +; CHECK-NEXT: br i1 [[C1]], label [[LEFT]], label [[MERGE2]] +; CHECK: left: +; CHECK-NEXT: br i1 [[C2:%.*]], label [[LOOP]], label [[MERGE2]] +; CHECK: merge2: +; CHECK-NEXT: [[B6:%.*]] = phi i8 addrspace(1)* [ [[B1:%.*]], [[ENTRY]] ], [ [[B3]], [[LOOP]] ], [ [[B3]], [[LEFT]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[B6]]) ] +; CHECK-NEXT: [[B6_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret i8 addrspace(1)* [[B6_RELOCATED]] +; +entry: + br i1 %c1, label %loop, label %merge2 + +loop: + %b3 = phi i8 addrspace(1)* [ %b2, %entry ], [ %b4, %left ] + br i1 %c1, label %left, label %merge2 + +left: + %b4 = phi i8 addrspace(1)* [ %b3, %loop ] + br i1 %c2, label %loop, label %merge2 + +merge2: + %b6 = phi i8 addrspace(1)* [ %b1, %entry ], [ %b3, %loop ], [ %b4, %left ] + call void @foo() + ret i8 addrspace(1)* %b6 +} + +declare i8 addrspace(1)* @returned_arg(i8 addrspace(1)* returned %p) + +define i8 addrspace(1)* @test7(i1 %c1, i1 %c2, i8 addrspace(1)* %b1, i8 addrspace(1)* %b2) gc "cangjie" { +; CHECK-LABEL: @test7( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[C1:%.*]], label [[LOOP:%.*]], label [[MERGE2:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[B3:%.*]] = phi i8 addrspace(1)* [ [[B2:%.*]], [[ENTRY:%.*]] ], [ [[B41:%.*]], [[LEFT:%.*]] ] +; CHECK-NEXT: br i1 [[C1]], label [[LEFT]], label [[MERGE2]] +; CHECK: left: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*)* @returned_arg, i32 1, i32 0, i8 addrspace(1)* [[B3]]) [ "gc-live"(i8 addrspace(1)* [[B3]]) ] +; CHECK-NEXT: [[B41]] = call i8 addrspace(1)* @llvm.cj.gc.result.p1i8(token [[TOKEN]]) +; CHECK-NEXT: [[B3_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: br i1 [[C2:%.*]], label [[LOOP]], label [[MERGE2]] +; CHECK: merge2: +; CHECK-NEXT: [[B6_BASE:%.*]] = phi i8 addrspace(1)* [ [[B1:%.*]], [[ENTRY]] ], [ [[B3]], [[LOOP]] ], [ [[B41]], [[LEFT]] ], !is_base_value !1 +; CHECK-NEXT: [[B6:%.*]] = phi i8 addrspace(1)* [ [[B1]], [[ENTRY]] ], [ [[B3]], [[LOOP]] ], [ [[B41]], [[LEFT]] ] +; CHECK-NEXT: [[TOKEN2:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[B6]], i8 addrspace(1)* [[B6_BASE]]) ] +; CHECK-NEXT: [[B6_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN2]], i32 1, i32 0) +; CHECK-NEXT: [[B6_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN2]], i32 1, i32 1) +; CHECK-NEXT: ret i8 addrspace(1)* [[B6_RELOCATED]] +; +entry: + br i1 %c1, label %loop, label %merge2 + +loop: + %b3 = phi i8 addrspace(1)* [ %b2, %entry ], [ %b4, %left ] + br i1 %c1, label %left, label %merge2 + +left: + %b4 = call i8 addrspace(1)* @returned_arg(i8 addrspace(1)* %b3) + br i1 %c2, label %loop, label %merge2 + +merge2: + %b6 = phi i8 addrspace(1)* [ %b1, %entry ], [ %b3, %loop ], [ %b4, %left ] + call void @foo() + ret i8 addrspace(1)* %b6 +} + +define i8 addrspace(1)* @test8(i1 %c, i32 %n, i8 addrspace(1)* %b1, i8 addrspace(1)* %b2) gc "cangjie" { +; CHECK-LABEL: @test8( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LEFT:%.*]] +; CHECK: left: +; CHECK-NEXT: br i1 [[C:%.*]], label [[LOOP:%.*]], label [[MERGE2:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[B3:%.*]] = phi i8 addrspace(1)* [ [[B2:%.*]], [[LEFT]] ], [ [[B5:%.*]], [[LOOP]] ], [ [[B5]], [[LOOP]] ] +; CHECK-NEXT: [[B4:%.*]] = bitcast i8 addrspace(1)* [[B3]] to i32 addrspace(1)* +; CHECK-NEXT: [[B5]] = bitcast i32 addrspace(1)* [[B4]] to i8 addrspace(1)* +; CHECK-NEXT: switch i32 [[N:%.*]], label [[MERGE2]] [ +; CHECK-NEXT: i32 0, label [[LOOP]] +; CHECK-NEXT: i32 1, label [[LOOP]] +; CHECK-NEXT: i32 2, label [[LEFT]] +; CHECK-NEXT: ] +; CHECK: merge2: +; CHECK-NEXT: [[B6:%.*]] = phi i8 addrspace(1)* [ [[B1:%.*]], [[LEFT]] ], [ [[B5]], [[LOOP]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[B6]]) ] +; CHECK-NEXT: [[B6_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret i8 addrspace(1)* [[B6_RELOCATED]] +; +entry: + br label %left + +left: + br i1 %c, label %loop, label %merge2 + +loop: + %b3 = phi i8 addrspace(1)* [ %b2, %left ], [ %b5, %loop ], [ %b5, %loop ] + %b4 = bitcast i8 addrspace(1)* %b3 to i32 addrspace(1)* + %b5 = bitcast i32 addrspace(1)* %b4 to i8 addrspace(1)* + switch i32 %n, label %merge2 [ i32 0, label %loop + i32 1, label %loop + i32 2, label %left ] + +merge2: + %b6 = phi i8 addrspace(1)* [ %b1, %left ], [ %b5, %loop ] + call void @foo() + ret i8 addrspace(1)* %b6 +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-2.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-2.ll new file mode 100644 index 000000000..5adf737ec --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-2.ll @@ -0,0 +1,35 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S 2>&1 | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S 2>&1 | FileCheck %s + +; derived %merged_value base %base_obj +define i64 addrspace(1)* @test(i64 addrspace(1)* %base_obj, i1 %runtime_condition) gc "cangjie" { +; CHECK-LABEL: @test( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[RUNTIME_CONDITION:%.*]], label [[MERGE:%.*]], label [[THERE:%.*]] +; CHECK: there: +; CHECK-NEXT: [[DERIVED_OBJ:%.*]] = getelementptr i64, i64 addrspace(1)* [[BASE_OBJ:%.*]], i32 1 +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[MERGED_VALUE:%.*]] = phi i64 addrspace(1)* [ [[BASE_OBJ]], [[ENTRY:%.*]] ], [ [[DERIVED_OBJ]], [[THERE]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[MERGED_VALUE]], i64 addrspace(1)* [[BASE_OBJ]]) ] +; CHECK-NEXT: [[MERGED_VALUE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[MERGED_VALUE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[MERGED_VALUE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[BASE_OBJ_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[BASE_OBJ_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BASE_OBJ_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[MERGED_VALUE_RELOCATED_CASTED]] +; +entry: + br i1 %runtime_condition, label %merge, label %there + +there: ; preds = %entry + %derived_obj = getelementptr i64, i64 addrspace(1)* %base_obj, i32 1 + br label %merge + +merge: ; preds = %there, %entry + %merged_value = phi i64 addrspace(1)* [ %base_obj, %entry ], [ %derived_obj, %there ] + call void @foo() + ret i64 addrspace(1)* %merged_value +} + +declare void @foo() diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-3.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-3.ll new file mode 100644 index 000000000..ff3e035de --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-3.ll @@ -0,0 +1,38 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S 2>&1 | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S 2>&1 | FileCheck %s + +; derived %next.i64 base %base_obj + +define void @test(i64 addrspace(1)* %base_obj) gc "cangjie" { +; CHECK-LABEL: @test( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[OBJ:%.*]] = getelementptr i64, i64 addrspace(1)* [[BASE_OBJ:%.*]], i32 1 +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[DOT0:%.*]] = phi i64 addrspace(1)* [ [[BASE_OBJ]], [[ENTRY:%.*]] ], [ [[BASE_OBJ_RELOCATED_CASTED:%.*]], [[LOOP]] ] +; CHECK-NEXT: [[CURRENT:%.*]] = phi i64 addrspace(1)* [ [[OBJ]], [[ENTRY]] ], [ [[NEXT_I64_RELOCATED_CASTED:%.*]], [[LOOP]] ] +; CHECK-NEXT: [[CURRENT_I32:%.*]] = bitcast i64 addrspace(1)* [[CURRENT]] to i32 addrspace(1)* +; CHECK-NEXT: [[NEXT_I32:%.*]] = getelementptr i32, i32 addrspace(1)* [[CURRENT_I32]], i32 1 +; CHECK-NEXT: [[NEXT_I64:%.*]] = bitcast i32 addrspace(1)* [[NEXT_I32]] to i64 addrspace(1)* +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[NEXT_I64]], i64 addrspace(1)* [[DOT0]]) ] +; CHECK-NEXT: [[NEXT_I64_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[NEXT_I64_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[NEXT_I64_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[BASE_OBJ_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[BASE_OBJ_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[BASE_OBJ_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: br label [[LOOP]] +; +entry: + %obj = getelementptr i64, i64 addrspace(1)* %base_obj, i32 1 + br label %loop + +loop: ; preds = %loop, %entry + %current = phi i64 addrspace(1)* [ %obj, %entry ], [ %next.i64, %loop ] + %current.i32 = bitcast i64 addrspace(1)* %current to i32 addrspace(1)* + %next.i32 = getelementptr i32, i32 addrspace(1)* %current.i32, i32 1 + %next.i64 = bitcast i32 addrspace(1)* %next.i32 to i64 addrspace(1)* + call void @do_safepoint() + br label %loop +} + +declare void @do_safepoint() diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-4.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-4.ll new file mode 100644 index 000000000..6ee9ec726 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-4.ll @@ -0,0 +1,66 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S 2>&1 | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S 2>&1 | FileCheck %s + +declare void @foo() + +declare i64 addrspace(1)* @generate_obj() + +declare void @consume_obj(i64 addrspace(1)*) + +; derived %obj_to_consume base %obj_to_consume.base +define void @test(i32 %condition) gc "cangjie" { +; CHECK-LABEL: @test( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 addrspace(1)* ()* @generate_obj, i32 0, i32 0) +; CHECK-NEXT: [[TMP0:%.*]] = call i64 addrspace(1)* @llvm.cj.gc.result.p1i64(token [[TOKEN]]) +; CHECK-NEXT: switch i32 [[CONDITION:%.*]], label [[DEST_A:%.*]] [ +; CHECK-NEXT: i32 0, label [[DEST_B:%.*]] +; CHECK-NEXT: i32 1, label [[DEST_C:%.*]] +; CHECK-NEXT: ] +; CHECK: dest_a: +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: dest_b: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: dest_c: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[OBJ_TO_CONSUME:%.*]] = phi i64 addrspace(1)* [ [[TMP0]], [[DEST_A]] ], [ null, [[DEST_B]] ], [ null, [[DEST_C]] ] +; CHECK-NEXT: [[TOKEN1:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i64 addrspace(1)*)* @consume_obj, i32 1, i32 0, i64 addrspace(1)* [[OBJ_TO_CONSUME]]) [ "gc-live"(i64 addrspace(1)* [[OBJ_TO_CONSUME]]) ] +; CHECK-NEXT: [[OBJ_TO_CONSUME_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN1]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_TO_CONSUME_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_TO_CONSUME_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: br label [[MERGE_SPLIT:%.*]] +; CHECK: merge.split: +; CHECK-NEXT: [[TOKEN2:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) +; CHECK-NEXT: br label [[LOOP]] +; +entry: + br label %loop + +loop: ; preds = %merge.split, %entry + %0 = call i64 addrspace(1)* @generate_obj() + switch i32 %condition, label %dest_a [ + i32 0, label %dest_b + i32 1, label %dest_c + ] + +dest_a: ; preds = %loop + br label %merge + +dest_b: ; preds = %loop + br label %merge + +dest_c: ; preds = %loop + br label %merge + +merge: ; preds = %dest_c, %dest_b, %dest_a + %obj_to_consume = phi i64 addrspace(1)* [ %0, %dest_a ], [ null, %dest_b ], [ null, %dest_c ] + call void @consume_obj(i64 addrspace(1)* %obj_to_consume) + br label %merge.split + +merge.split: ; preds = %merge + call void @foo() + br label %loop +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-5.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-5.ll new file mode 100644 index 000000000..387d3beee --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-5.ll @@ -0,0 +1,47 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S 2>&1 | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S 2>&1 | FileCheck %s + + +declare void @foo() + +; derived %merged_value base %merged_value.base +define i64 addrspace(1)* @test(i64 addrspace(1)* %base_obj_x, i64 addrspace(1)* %base_obj_y, i1 %runtime_condition) gc "cangjie" { +; CHECK-LABEL: @test( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[RUNTIME_CONDITION:%.*]], label [[HERE:%.*]], label [[THERE:%.*]] +; CHECK: here: +; CHECK-NEXT: br label [[BUMP:%.*]] +; CHECK: bump: +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: there: +; CHECK-NEXT: [[Y:%.*]] = getelementptr i64, i64 addrspace(1)* [[BASE_OBJ_Y:%.*]], i32 1 +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[MERGED_VALUE_BASE:%.*]] = phi i64 addrspace(1)* [ [[BASE_OBJ_X:%.*]], [[BUMP]] ], [ [[BASE_OBJ_Y]], [[THERE]] ], !is_base_value !1 +; CHECK-NEXT: [[MERGED_VALUE:%.*]] = phi i64 addrspace(1)* [ [[BASE_OBJ_X]], [[BUMP]] ], [ [[Y]], [[THERE]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[MERGED_VALUE]], i64 addrspace(1)* [[MERGED_VALUE_BASE]]) ] +; CHECK-NEXT: [[MERGED_VALUE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[MERGED_VALUE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[MERGED_VALUE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[MERGED_VALUE_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[MERGED_VALUE_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[MERGED_VALUE_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[MERGED_VALUE_RELOCATED_CASTED]] +; +entry: + br i1 %runtime_condition, label %here, label %there + +here: ; preds = %entry + br label %bump + +bump: ; preds = %here + br label %merge + +there: ; preds = %entry + %y = getelementptr i64, i64 addrspace(1)* %base_obj_y, i32 1 + br label %merge + +merge: ; preds = %there, %bump + %merged_value = phi i64 addrspace(1)* [ %base_obj_x, %bump ], [ %y, %there ] + call void @foo() + ret i64 addrspace(1)* %merged_value +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-6.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-6.ll new file mode 100644 index 000000000..7ba987e62 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-6.ll @@ -0,0 +1,63 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S 2>&1 | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S 2>&1 | FileCheck %s + + +declare void @site_for_call_safpeoint() + +; derived %merged_value base %merged_value.base +define i64 addrspace(1)* @test(i64 addrspace(1)* %base_obj_x, i64 addrspace(1)* %base_obj_y, i1 %runtime_condition_x, i1 %runtime_condition_y) gc "cangjie" { +; CHECK-LABEL: @test( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[RUNTIME_CONDITION_X:%.*]], label [[HERE:%.*]], label [[THERE:%.*]] +; CHECK: here: +; CHECK-NEXT: br i1 [[RUNTIME_CONDITION_Y:%.*]], label [[BUMP_HERE_A:%.*]], label [[BUMP_HERE_B:%.*]] +; CHECK: bump_here_a: +; CHECK-NEXT: [[X_A:%.*]] = getelementptr i64, i64 addrspace(1)* [[BASE_OBJ_X:%.*]], i32 1 +; CHECK-NEXT: br label [[MERGE_HERE:%.*]] +; CHECK: bump_here_b: +; CHECK-NEXT: [[X_B:%.*]] = getelementptr i64, i64 addrspace(1)* [[BASE_OBJ_X]], i32 2 +; CHECK-NEXT: br label [[MERGE_HERE]] +; CHECK: merge_here: +; CHECK-NEXT: [[X:%.*]] = phi i64 addrspace(1)* [ [[X_A]], [[BUMP_HERE_A]] ], [ [[X_B]], [[BUMP_HERE_B]] ] +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: there: +; CHECK-NEXT: [[Y:%.*]] = getelementptr i64, i64 addrspace(1)* [[BASE_OBJ_Y:%.*]], i32 1 +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[MERGED_VALUE_BASE:%.*]] = phi i64 addrspace(1)* [ [[BASE_OBJ_X]], [[MERGE_HERE]] ], [ [[BASE_OBJ_Y]], [[THERE]] ], !is_base_value !1 +; CHECK-NEXT: [[MERGED_VALUE:%.*]] = phi i64 addrspace(1)* [ [[X]], [[MERGE_HERE]] ], [ [[Y]], [[THERE]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @site_for_call_safpeoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[MERGED_VALUE]], i64 addrspace(1)* [[MERGED_VALUE_BASE]]) ] +; CHECK-NEXT: [[MERGED_VALUE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[MERGED_VALUE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[MERGED_VALUE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[MERGED_VALUE_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[MERGED_VALUE_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[MERGED_VALUE_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[MERGED_VALUE_RELOCATED_CASTED]] +; +entry: + br i1 %runtime_condition_x, label %here, label %there + +here: ; preds = %entry + br i1 %runtime_condition_y, label %bump_here_a, label %bump_here_b + +bump_here_a: ; preds = %here + %x_a = getelementptr i64, i64 addrspace(1)* %base_obj_x, i32 1 + br label %merge_here + +bump_here_b: ; preds = %here + %x_b = getelementptr i64, i64 addrspace(1)* %base_obj_x, i32 2 + br label %merge_here + +merge_here: ; preds = %bump_here_b, %bump_here_a + %x = phi i64 addrspace(1)* [ %x_a, %bump_here_a ], [ %x_b, %bump_here_b ] + br label %merge + +there: ; preds = %entry + %y = getelementptr i64, i64 addrspace(1)* %base_obj_y, i32 1 + br label %merge + +merge: ; preds = %there, %merge_here + %merged_value = phi i64 addrspace(1)* [ %x, %merge_here ], [ %y, %there ] + call void @site_for_call_safpeoint() + ret i64 addrspace(1)* %merged_value +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-7.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-7.ll new file mode 100644 index 000000000..1b7ce56cd --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-7.ll @@ -0,0 +1,63 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S 2>&1 | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S 2>&1 | FileCheck %s + +declare void @site_for_call_safpeoint() + +; derived %merged_value base %merged_value.base +define i64 addrspace(1)* @test(i64 addrspace(1)* %base_obj_x, i64 addrspace(1)* %base_obj_y, i1 %runtime_condition_x, i1 %runtime_condition_y) gc "cangjie" { +; CHECK-LABEL: @test( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[RUNTIME_CONDITION_X:%.*]], label [[HERE:%.*]], label [[THERE:%.*]] +; CHECK: here: +; CHECK-NEXT: br i1 [[RUNTIME_CONDITION_Y:%.*]], label [[BUMP_HERE_A:%.*]], label [[BUMP_HERE_B:%.*]] +; CHECK: bump_here_a: +; CHECK-NEXT: [[X_A:%.*]] = getelementptr i64, i64 addrspace(1)* [[BASE_OBJ_X:%.*]], i32 1 +; CHECK-NEXT: br label [[MERGE_HERE:%.*]] +; CHECK: bump_here_b: +; CHECK-NEXT: [[X_B:%.*]] = getelementptr i64, i64 addrspace(1)* [[BASE_OBJ_Y:%.*]], i32 2 +; CHECK-NEXT: br label [[MERGE_HERE]] +; CHECK: merge_here: +; CHECK-NEXT: [[X_BASE:%.*]] = phi i64 addrspace(1)* [ [[BASE_OBJ_X]], [[BUMP_HERE_A]] ], [ [[BASE_OBJ_Y]], [[BUMP_HERE_B]] ], !is_base_value !1 +; CHECK-NEXT: [[X:%.*]] = phi i64 addrspace(1)* [ [[X_A]], [[BUMP_HERE_A]] ], [ [[X_B]], [[BUMP_HERE_B]] ] +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: there: +; CHECK-NEXT: [[Y:%.*]] = getelementptr i64, i64 addrspace(1)* [[BASE_OBJ_Y]], i32 1 +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[MERGED_VALUE_BASE:%.*]] = phi i64 addrspace(1)* [ [[X_BASE]], [[MERGE_HERE]] ], [ [[BASE_OBJ_Y]], [[THERE]] ], !is_base_value !1 +; CHECK-NEXT: [[MERGED_VALUE:%.*]] = phi i64 addrspace(1)* [ [[X]], [[MERGE_HERE]] ], [ [[Y]], [[THERE]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @site_for_call_safpeoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[MERGED_VALUE]], i64 addrspace(1)* [[MERGED_VALUE_BASE]]) ] +; CHECK-NEXT: [[MERGED_VALUE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[MERGED_VALUE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[MERGED_VALUE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[MERGED_VALUE_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[MERGED_VALUE_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[MERGED_VALUE_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[MERGED_VALUE_RELOCATED_CASTED]] +; +entry: + br i1 %runtime_condition_x, label %here, label %there + +here: ; preds = %entry + br i1 %runtime_condition_y, label %bump_here_a, label %bump_here_b + +bump_here_a: ; preds = %here + %x_a = getelementptr i64, i64 addrspace(1)* %base_obj_x, i32 1 + br label %merge_here + +bump_here_b: ; preds = %here + %x_b = getelementptr i64, i64 addrspace(1)* %base_obj_y, i32 2 + br label %merge_here + +merge_here: ; preds = %bump_here_b, %bump_here_a + %x = phi i64 addrspace(1)* [ %x_a, %bump_here_a ], [ %x_b, %bump_here_b ] + br label %merge + +there: ; preds = %entry + %y = getelementptr i64, i64 addrspace(1)* %base_obj_y, i32 1 + br label %merge + +merge: ; preds = %there, %merge_here + %merged_value = phi i64 addrspace(1)* [ %x, %merge_here ], [ %y, %there ] + call void @site_for_call_safpeoint() + ret i64 addrspace(1)* %merged_value +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-8.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-8.ll new file mode 100644 index 000000000..9bbb356e5 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-8.ll @@ -0,0 +1,69 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S 2>&1 | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S 2>&1 | FileCheck %s + +; derived %next_element_ptr base %array_obj +define i32 @null_in_array(i64 addrspace(1)* %array_obj) gc "cangjie" { +; CHECK-LABEL: @null_in_array( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ARRAY_LEN_POINTER_I64:%.*]] = getelementptr i64, i64 addrspace(1)* [[ARRAY_OBJ:%.*]], i32 1 +; CHECK-NEXT: [[ARRAY_LEN_POINTER_I32:%.*]] = bitcast i64 addrspace(1)* [[ARRAY_LEN_POINTER_I64]] to i32 addrspace(1)* +; CHECK-NEXT: [[ARRAY_LEN:%.*]] = load i32, i32 addrspace(1)* [[ARRAY_LEN_POINTER_I32]], align 4 +; CHECK-NEXT: [[ARRAY_ELEMS:%.*]] = bitcast i32 addrspace(1)* [[ARRAY_LEN_POINTER_I32]] to i64 addrspace(1)* addrspace(1)* +; CHECK-NEXT: br label [[LOOP_CHECK:%.*]] +; CHECK: loop_check: +; CHECK-NEXT: [[DOT0:%.*]] = phi i64 addrspace(1)* [ [[ARRAY_OBJ]], [[ENTRY:%.*]] ], [ [[ARRAY_OBJ_RELOCATED_CASTED:%.*]], [[LOOP_BACK:%.*]] ] +; CHECK-NEXT: [[INDEX:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[NEXT_INDEX:%.*]], [[LOOP_BACK]] ] +; CHECK-NEXT: [[CURRENT_ELEMENT_PTR:%.*]] = phi i64 addrspace(1)* addrspace(1)* [ [[ARRAY_ELEMS]], [[ENTRY]] ], [ [[NEXT_ELEMENT_PTR_RELOCATED_CASTED:%.*]], [[LOOP_BACK]] ] +; CHECK-NEXT: [[INDEX_LT:%.*]] = icmp ult i32 [[INDEX]], [[ARRAY_LEN]] +; CHECK-NEXT: br i1 [[INDEX_LT]], label [[CHECK_FOR_NULL:%.*]], label [[NOT_FOUND:%.*]] +; CHECK: check_for_null: +; CHECK-NEXT: [[CURRENT_ELEMENT:%.*]] = load i64 addrspace(1)*, i64 addrspace(1)* addrspace(1)* [[CURRENT_ELEMENT_PTR]], align 8 +; CHECK-NEXT: [[IS_NULL:%.*]] = icmp eq i64 addrspace(1)* [[CURRENT_ELEMENT]], null +; CHECK-NEXT: br i1 [[IS_NULL]], label [[FOUND:%.*]], label [[LOOP_BACK]] +; CHECK: loop_back: +; CHECK-NEXT: [[NEXT_ELEMENT_PTR:%.*]] = getelementptr i64 addrspace(1)*, i64 addrspace(1)* addrspace(1)* [[CURRENT_ELEMENT_PTR]], i32 1 +; CHECK-NEXT: [[NEXT_INDEX]] = add i32 [[INDEX]], 1 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* addrspace(1)* [[NEXT_ELEMENT_PTR]], i64 addrspace(1)* [[DOT0]]) ] +; CHECK-NEXT: [[NEXT_ELEMENT_PTR_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[NEXT_ELEMENT_PTR_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[NEXT_ELEMENT_PTR_RELOCATED]] to i64 addrspace(1)* addrspace(1)* +; CHECK-NEXT: [[ARRAY_OBJ_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[ARRAY_OBJ_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[ARRAY_OBJ_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: br label [[LOOP_CHECK]] +; CHECK: not_found: +; CHECK-NEXT: ret i32 -1 +; CHECK: found: +; CHECK-NEXT: ret i32 [[INDEX]] +; +entry: + %array_len_pointer.i64 = getelementptr i64, i64 addrspace(1)* %array_obj, i32 1 + %array_len_pointer.i32 = bitcast i64 addrspace(1)* %array_len_pointer.i64 to i32 addrspace(1)* + %array_len = load i32, i32 addrspace(1)* %array_len_pointer.i32 + %array_elems = bitcast i32 addrspace(1)* %array_len_pointer.i32 to i64 addrspace(1)* addrspace(1)* + br label %loop_check + +loop_check: ; preds = %loop_back, %entry + %index = phi i32 [ 0, %entry ], [ %next_index, %loop_back ] + %current_element_ptr = phi i64 addrspace(1)* addrspace(1)* [ %array_elems, %entry ], [ %next_element_ptr, %loop_back ] + %index_lt = icmp ult i32 %index, %array_len + br i1 %index_lt, label %check_for_null, label %not_found + +check_for_null: ; preds = %loop_check + %current_element = load i64 addrspace(1)*, i64 addrspace(1)* addrspace(1)* %current_element_ptr + %is_null = icmp eq i64 addrspace(1)* %current_element, null + br i1 %is_null, label %found, label %loop_back + +loop_back: ; preds = %check_for_null + %next_element_ptr = getelementptr i64 addrspace(1)*, i64 addrspace(1)* addrspace(1)* %current_element_ptr, i32 1 + %next_index = add i32 %index, 1 + call void @do_safepoint() + br label %loop_check + +not_found: ; preds = %loop_check + ret i32 -1 + +found: ; preds = %check_for_null + ret i32 %index +} + +declare void @do_safepoint() diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-9.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-9.ll new file mode 100644 index 000000000..787f96b83 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers-9.ll @@ -0,0 +1,37 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S 2>&1 | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S 2>&1 | FileCheck %s + +declare i1 @runtime_value() "gc-leaf-function" + +; derived %next base %base_obj +define void @maybe_GEP(i64 addrspace(1)* %base_obj) gc "cangjie" { +; CHECK-LABEL: @maybe_GEP( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[DOT0:%.*]] = phi i64 addrspace(1)* [ [[BASE_OBJ:%.*]], [[ENTRY:%.*]] ], [ [[BASE_OBJ_RELOCATED_CASTED:%.*]], [[LOOP]] ] +; CHECK-NEXT: [[CURRENT:%.*]] = phi i64 addrspace(1)* [ [[BASE_OBJ]], [[ENTRY]] ], [ [[NEXT_RELOCATED_CASTED:%.*]], [[LOOP]] ] +; CHECK-NEXT: [[CONDITION:%.*]] = call i1 @runtime_value() +; CHECK-NEXT: [[MAYBE_NEXT:%.*]] = getelementptr i64, i64 addrspace(1)* [[CURRENT]], i32 1 +; CHECK-NEXT: [[NEXT:%.*]] = select i1 [[CONDITION]], i64 addrspace(1)* [[MAYBE_NEXT]], i64 addrspace(1)* [[CURRENT]] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[NEXT]], i64 addrspace(1)* [[DOT0]]) ] +; CHECK-NEXT: [[NEXT_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[NEXT_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[NEXT_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[BASE_OBJ_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[BASE_OBJ_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[BASE_OBJ_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: br label [[LOOP]] +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %current = phi i64 addrspace(1)* [ %base_obj, %entry ], [ %next, %loop ] + %condition = call i1 @runtime_value() + %maybe_next = getelementptr i64, i64 addrspace(1)* %current, i32 1 + %next = select i1 %condition, i64 addrspace(1)* %maybe_next, i64 addrspace(1)* %current + call void @do_safepoint() + br label %loop +} + +declare void @do_safepoint() diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-pointers.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers.ll new file mode 100644 index 000000000..2a1633bf7 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-pointers.ll @@ -0,0 +1,240 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S 2>&1 | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S 2>&1 | FileCheck %s + +declare i64 addrspace(1)* @generate_obj() "gc-leaf-function" + +declare void @use_obj(i64 addrspace(1)*) "gc-leaf-function" + +; The rewriting needs to make %obj loop variant by inserting a phi +; of the original value and it's relocation. +define void @test() gc "cangjie" { +; CHECK-LABEL: @test( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[OBJ:%.*]] = call i64 addrspace(1)* @generate_obj() +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[DOT0:%.*]] = phi i64 addrspace(1)* [ [[OBJ]], [[ENTRY:%.*]] ], [ [[OBJ_RELOCATED_CASTED:%.*]], [[LOOP]] ] +; CHECK-NEXT: call void @use_obj(i64 addrspace(1)* [[DOT0]]) +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[DOT0]]) ] +; CHECK-NEXT: [[OBJ_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[OBJ_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: br label [[LOOP]] +; +entry: + %obj = call i64 addrspace(1)* @generate_obj() + br label %loop + +loop: + call void @use_obj(i64 addrspace(1)* %obj) + call void @do_safepoint() + br label %loop +} + +declare void @do_safepoint() + +declare void @parse_point(i64 addrspace(1)*) + +define i64 addrspace(1)* @test1(i32 %caller, i8 addrspace(1)* %a, i8 addrspace(1)* %b, i32 %unknown) gc "cangjie" { +; CHECK-LABEL: @test1( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] +; CHECK: left: +; CHECK-NEXT: [[A_CAST:%.*]] = bitcast i8 addrspace(1)* [[A:%.*]] to i64 addrspace(1)* +; CHECK-NEXT: switch i32 [[UNKNOWN:%.*]], label [[RIGHT]] [ +; CHECK-NEXT: i32 0, label [[MERGE:%.*]] +; CHECK-NEXT: i32 1, label [[MERGE]] +; CHECK-NEXT: i32 5, label [[MERGE]] +; CHECK-NEXT: ] +; CHECK: right: +; CHECK-NEXT: [[B_CAST:%.*]] = bitcast i8 addrspace(1)* [[B:%.*]] to i64 addrspace(1)* +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[VALUE:%.*]] = phi i64 addrspace(1)* [ [[A_CAST]], [[LEFT]] ], [ [[A_CAST]], [[LEFT]] ], [ [[A_CAST]], [[LEFT]] ], [ [[B_CAST]], [[RIGHT]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i64 addrspace(1)*)* @parse_point, i32 1, i32 0, i64 addrspace(1)* [[VALUE]]) [ "gc-live"(i64 addrspace(1)* [[VALUE]]) ] +; CHECK-NEXT: [[VALUE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[VALUE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[VALUE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[VALUE_RELOCATED_CASTED]] +; +entry: + br i1 undef, label %left, label %right + +left: +; Our safepoint placement pass calls removeUnreachableBlocks, which does a bunch +; of simplifications to branch instructions. This bug is visible only when +; there are multiple branches into the same block from the same predecessor, and +; the following ceremony is to make that artefact survive a call to +; removeUnreachableBlocks. As an example, "br i1 undef, label %merge, label %merge" +; will get simplified to "br label %merge" by removeUnreachableBlocks. + %a.cast = bitcast i8 addrspace(1)* %a to i64 addrspace(1)* + switch i32 %unknown, label %right [ + i32 0, label %merge + i32 1, label %merge + i32 5, label %merge + i32 3, label %right + ] + +right: + %b.cast = bitcast i8 addrspace(1)* %b to i64 addrspace(1)* + br label %merge + +merge: + %value = phi i64 addrspace(1)* [ %a.cast, %left ], [ %a.cast, %left ], [ %a.cast, %left ], [ %b.cast, %right ] + call void @parse_point(i64 addrspace(1)* %value) + ret i64 addrspace(1)* %value +} + +;; The purpose of this test is to ensure that when two live values share a +;; base defining value with inherent conflicts, we end up with a *single* +;; base phi/select per such node. This is testing an optimization, not a +;; fundemental correctness criteria +define void @test2(i1 %cnd, i64 addrspace(1)* %base_obj, i64 addrspace(1)* %base_arg2) gc "cangjie" { +; CHECK-LABEL: @test2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[OBJ:%.*]] = getelementptr i64, i64 addrspace(1)* [[BASE_OBJ:%.*]], i32 1 +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[DOT0:%.*]] = phi i64 addrspace(1)* [ [[BASE_ARG2:%.*]], [[ENTRY:%.*]] ], [ [[BASE_ARG2_RELOCATED_CASTED:%.*]], [[LOOP]] ] +; CHECK-NEXT: [[CURRENT_BASE:%.*]] = phi i64 addrspace(1)* [ [[BASE_OBJ]], [[ENTRY]] ], [ [[NEXT_BASE_RELOCATED_CASTED:%.*]], [[LOOP]] ], !is_base_value !1 +; CHECK-NEXT: [[CURRENT:%.*]] = phi i64 addrspace(1)* [ [[OBJ]], [[ENTRY]] ], [ [[NEXT_RELOCATED_CASTED:%.*]], [[LOOP]] ] +; CHECK-NEXT: [[EXTRA:%.*]] = phi i64 addrspace(1)* [ [[OBJ]], [[ENTRY]] ], [ [[EXTRA2_RELOCATED_CASTED:%.*]], [[LOOP]] ] +; CHECK-NEXT: [[NEXTA:%.*]] = getelementptr i64, i64 addrspace(1)* [[CURRENT]], i32 1 +; CHECK-NEXT: [[NEXT_BASE:%.*]] = select i1 [[CND:%.*]], i64 addrspace(1)* [[CURRENT_BASE]], i64 addrspace(1)* [[DOT0]], !is_base_value !1 +; CHECK-NEXT: [[NEXT:%.*]] = select i1 [[CND]], i64 addrspace(1)* [[NEXTA]], i64 addrspace(1)* [[DOT0]] +; CHECK-NEXT: [[EXTRA2_BASE:%.*]] = select i1 [[CND]], i64 addrspace(1)* [[CURRENT_BASE]], i64 addrspace(1)* [[DOT0]], !is_base_value !1 +; CHECK-NEXT: [[EXTRA2:%.*]] = select i1 [[CND]], i64 addrspace(1)* [[NEXTA]], i64 addrspace(1)* [[DOT0]] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[NEXT_BASE]], i64 addrspace(1)* [[NEXT]], i64 addrspace(1)* [[EXTRA2]], i64 addrspace(1)* [[DOT0]], i64 addrspace(1)* [[EXTRA2_BASE]]) ] +; CHECK-NEXT: [[NEXT_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[NEXT_BASE_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[NEXT_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[NEXT_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 1) +; CHECK-NEXT: [[NEXT_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[NEXT_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[EXTRA2_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 4, i32 2) +; CHECK-NEXT: [[EXTRA2_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[EXTRA2_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[BASE_ARG2_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 3, i32 3) +; CHECK-NEXT: [[BASE_ARG2_RELOCATED_CASTED]] = bitcast i8 addrspace(1)* [[BASE_ARG2_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[EXTRA2_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 4, i32 4) +; CHECK-NEXT: [[EXTRA2_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[EXTRA2_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: br label [[LOOP]] +; +entry: + %obj = getelementptr i64, i64 addrspace(1)* %base_obj, i32 1 + br label %loop + +; Given the two selects are equivelent, so are their base phis - ideally, +; we'd have commoned these, but that's a missed optimization, not correctness. +;; Both 'next' and 'extra2' are live across the backedge safepoint... + +loop: + %current = phi i64 addrspace(1)* [ %obj, %entry ], [ %next, %loop ] + %extra = phi i64 addrspace(1)* [ %obj, %entry ], [ %extra2, %loop ] + %nexta = getelementptr i64, i64 addrspace(1)* %current, i32 1 + %next = select i1 %cnd, i64 addrspace(1)* %nexta, i64 addrspace(1)* %base_arg2 + %extra2 = select i1 %cnd, i64 addrspace(1)* %nexta, i64 addrspace(1)* %base_arg2 + call void @foo() + br label %loop +} + +define i64 addrspace(1)* @test3(i1 %cnd, i64 addrspace(1)* %obj, i64 addrspace(1)* %obj2) gc "cangjie" { +; CHECK-LABEL: @test3( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[CND:%.*]], label [[MERGE:%.*]], label [[TAKEN:%.*]] +; CHECK: taken: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[BDV:%.*]] = phi i64 addrspace(1)* [ [[OBJ:%.*]], [[ENTRY:%.*]] ], [ [[OBJ2:%.*]], [[TAKEN]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[BDV]]) ] +; CHECK-NEXT: [[BDV_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[BDV_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[BDV_RELOCATED_CASTED]] +; +entry: + br i1 %cnd, label %merge, label %taken + +taken: + br label %merge + +merge: + %bdv = phi i64 addrspace(1)* [ %obj, %entry ], [ %obj2, %taken ] + call void @foo() + ret i64 addrspace(1)* %bdv +} + +define i64 addrspace(1)* @test4(i1 %cnd, i64 addrspace(1)* %obj, i64 addrspace(1)* %obj2) gc "cangjie" { +; CHECK-LABEL: @test4( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[CND:%.*]], label [[MERGE:%.*]], label [[TAKEN:%.*]] +; CHECK: taken: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[BDV:%.*]] = phi i64 addrspace(1)* [ [[OBJ:%.*]], [[ENTRY:%.*]] ], [ [[OBJ]], [[TAKEN]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[BDV]]) ] +; CHECK-NEXT: [[BDV_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[BDV_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[BDV_RELOCATED_CASTED]] +; +entry: + br i1 %cnd, label %merge, label %taken + +taken: + br label %merge + +merge: + %bdv = phi i64 addrspace(1)* [ %obj, %entry ], [ %obj, %taken ] + call void @foo() + ret i64 addrspace(1)* %bdv +} + +define i64 addrspace(1)* @test5(i1 %cnd, i64 addrspace(1)* %obj, i64 addrspace(1)* %obj2) gc "cangjie" { +; CHECK-LABEL: @test5( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: merge: +; CHECK-NEXT: [[BDV:%.*]] = phi i64 addrspace(1)* [ [[OBJ:%.*]], [[ENTRY:%.*]] ], [ [[OBJ2:%.*]], [[MERGE]] ] +; CHECK-NEXT: br i1 [[CND:%.*]], label [[MERGE]], label [[NEXT:%.*]] +; CHECK: next: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[BDV]]) ] +; CHECK-NEXT: [[BDV_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[BDV_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[BDV_RELOCATED_CASTED]] +; +entry: + br label %merge + +merge: + %bdv = phi i64 addrspace(1)* [ %obj, %entry ], [ %obj2, %merge ] + br i1 %cnd, label %merge, label %next + +next: + call void @foo() + ret i64 addrspace(1)* %bdv +} + +; We know from the deopt use that %bdv must be a base value, and as +; result can avoid materializing the extra copy of the BDV phi node. +; (Even without a general forward analysis) +define i64 addrspace(1)* @test6(i1 %cnd, i64 addrspace(1)* %obj, i64 addrspace(1)* %obj2) gc "cangjie" { +; CHECK-LABEL: @test6( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: merge: +; CHECK-NEXT: [[BDV:%.*]] = phi i64 addrspace(1)* [ [[OBJ:%.*]], [[ENTRY:%.*]] ], [ [[OBJ2:%.*]], [[MERGE]] ] +; CHECK-NEXT: br i1 [[CND:%.*]], label [[MERGE]], label [[NEXT:%.*]] +; CHECK: next: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[BDV]]) ] +; CHECK-NEXT: [[BDV_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[BDV_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[BDV_RELOCATED_CASTED]] +; +entry: + br label %merge + +merge: + %bdv = phi i64 addrspace(1)* [ %obj, %entry ], [ %obj2, %merge ] + br i1 %cnd, label %merge, label %next + +next: + call void @foo() + ret i64 addrspace(1)* %bdv +} + +declare void @foo() diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-vector-inseltpoison.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-vector-inseltpoison.ll new file mode 100644 index 000000000..f65953acd --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-vector-inseltpoison.ll @@ -0,0 +1,374 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S | FileCheck %s + + +define i64 addrspace(1)* @test(<2 x i64 addrspace(1)*> %vec, i32 %idx) gc "cangjie" { +; CHECK-LABEL: @test( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[OBJ:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC:%.*]], i32 [[IDX:%.*]] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[OBJ]]) ] +; CHECK-NEXT: [[OBJ_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[OBJ_RELOCATED_CASTED]] +; +; Note that the second extractelement is actually redundant here. A correct output would +; be to reuse the existing obj as a base since it is actually a base pointer. +entry: + %obj = extractelement <2 x i64 addrspace(1)*> %vec, i32 %idx + call void @do_safepoint() + ret i64 addrspace(1)* %obj +} + +define i64 addrspace(1)* @test2(<2 x i64 addrspace(1)*>* %ptr, i1 %cnd, i32 %idx1, i32 %idx2) gc "cangjie" { +; CHECK-LABEL: @test2( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[CND:%.*]], label [[TAKEN:%.*]], label [[UNTAKEN:%.*]] +; CHECK: taken: +; CHECK-NEXT: [[OBJA:%.*]] = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* [[PTR:%.*]], align 16 +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: untaken: +; CHECK-NEXT: [[OBJB:%.*]] = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* [[PTR]], align 16 +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[VEC:%.*]] = phi <2 x i64 addrspace(1)*> [ [[OBJA]], [[TAKEN]] ], [ [[OBJB]], [[UNTAKEN]] ] +; CHECK-NEXT: br i1 [[CND]], label [[TAKEN2:%.*]], label [[UNTAKEN2:%.*]] +; CHECK: taken2: +; CHECK-NEXT: [[OBJ0:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC]], i32 [[IDX1:%.*]] +; CHECK-NEXT: br label [[MERGE2:%.*]] +; CHECK: untaken2: +; CHECK-NEXT: [[OBJ1:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC]], i32 [[IDX2:%.*]] +; CHECK-NEXT: br label [[MERGE2]] +; CHECK: merge2: +; CHECK-NEXT: [[OBJ:%.*]] = phi i64 addrspace(1)* [ [[OBJ0]], [[TAKEN2]] ], [ [[OBJ1]], [[UNTAKEN2]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[OBJ]]) ] +; CHECK-NEXT: [[OBJ_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[OBJ_RELOCATED_CASTED]] +; +entry: + br i1 %cnd, label %taken, label %untaken + +taken: ; preds = %entry + %obja = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr + br label %merge + +untaken: ; preds = %entry + %objb = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr + br label %merge + +merge: ; preds = %untaken, %taken + %vec = phi <2 x i64 addrspace(1)*> [ %obja, %taken ], [ %objb, %untaken ] + br i1 %cnd, label %taken2, label %untaken2 + +taken2: ; preds = %merge + %obj0 = extractelement <2 x i64 addrspace(1)*> %vec, i32 %idx1 + br label %merge2 + +untaken2: ; preds = %merge + %obj1 = extractelement <2 x i64 addrspace(1)*> %vec, i32 %idx2 + br label %merge2 + +merge2: ; preds = %untaken2, %taken2 + %obj = phi i64 addrspace(1)* [ %obj0, %taken2 ], [ %obj1, %untaken2 ] + call void @do_safepoint() + ret i64 addrspace(1)* %obj +} + +define i64 addrspace(1)* @test3(i64 addrspace(1)* %ptr) gc "cangjie" { +; CHECK-LABEL: @test3( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[VEC_BASE:%.*]] = insertelement <2 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[PTR:%.*]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VEC:%.*]] = insertelement <2 x i64 addrspace(1)*> poison, i64 addrspace(1)* [[PTR]], i32 0 +; CHECK-NEXT: [[OBJ_BASE:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC_BASE]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[OBJ:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC]], i32 0 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[OBJ]], i64 addrspace(1)* [[OBJ_BASE]]) ] +; CHECK-NEXT: [[OBJ_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[OBJ_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[OBJ_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[OBJ_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[OBJ_RELOCATED_CASTED]] +; +entry: + %vec = insertelement <2 x i64 addrspace(1)*> poison, i64 addrspace(1)* %ptr, i32 0 + %obj = extractelement <2 x i64 addrspace(1)*> %vec, i32 0 + call void @do_safepoint() + ret i64 addrspace(1)* %obj +} + +define i64 addrspace(1)* @test4(i64 addrspace(1)* %ptr) gc "cangjie" { +; CHECK-LABEL: @test4( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[DERIVED:%.*]] = getelementptr i64, i64 addrspace(1)* [[PTR:%.*]], i64 16 +; CHECK-NEXT: [[VECA_BASE:%.*]] = insertelement <2 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[PTR]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VECA:%.*]] = insertelement <2 x i64 addrspace(1)*> poison, i64 addrspace(1)* [[DERIVED]], i32 0 +; CHECK-NEXT: [[VEC_BASE:%.*]] = insertelement <2 x i64 addrspace(1)*> [[VECA_BASE]], i64 addrspace(1)* [[PTR]], i32 1, !is_base_value !1 +; CHECK-NEXT: [[VEC:%.*]] = insertelement <2 x i64 addrspace(1)*> [[VECA]], i64 addrspace(1)* [[PTR]], i32 1 +; CHECK-NEXT: [[OBJ_BASE:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC_BASE]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[OBJ:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC]], i32 0 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[OBJ]], i64 addrspace(1)* [[OBJ_BASE]]) ] +; CHECK-NEXT: [[OBJ_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[OBJ_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[OBJ_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[OBJ_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[OBJ_RELOCATED_CASTED]] +; +; When we can optimize an extractelement from a known +; index and avoid introducing new base pointer instructions +entry: + %derived = getelementptr i64, i64 addrspace(1)* %ptr, i64 16 + %veca = insertelement <2 x i64 addrspace(1)*> poison, i64 addrspace(1)* %derived, i32 0 + %vec = insertelement <2 x i64 addrspace(1)*> %veca, i64 addrspace(1)* %ptr, i32 1 + %obj = extractelement <2 x i64 addrspace(1)*> %vec, i32 0 + call void @do_safepoint() + ret i64 addrspace(1)* %obj +} + +declare void @use(i64 addrspace(1)*) "gc-leaf-function" +declare void @use_vec(<4 x i64 addrspace(1)*>) "gc-leaf-function" + +define void @test5(i1 %cnd, i64 addrspace(1)* %obj) gc "cangjie" { +; CHECK-LABEL: @test5( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr i64, i64 addrspace(1)* [[OBJ:%.*]], i64 1 +; CHECK-NEXT: [[VEC_BASE:%.*]] = insertelement <2 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[OBJ]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VEC:%.*]] = insertelement <2 x i64 addrspace(1)*> poison, i64 addrspace(1)* [[GEP]], i32 0 +; CHECK-NEXT: [[BDV_BASE:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC_BASE]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[BDV:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC]], i32 0 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[BDV]], i64 addrspace(1)* [[BDV_BASE]]) ] +; CHECK-NEXT: [[BDV_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[BDV_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[BDV_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[BDV_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: call void @use(i64 addrspace(1)* [[BDV_RELOCATED_CASTED]]) +; CHECK-NEXT: ret void +; +; When we fundementally have to duplicate +entry: + %gep = getelementptr i64, i64 addrspace(1)* %obj, i64 1 + %vec = insertelement <2 x i64 addrspace(1)*> poison, i64 addrspace(1)* %gep, i32 0 + %bdv = extractelement <2 x i64 addrspace(1)*> %vec, i32 0 + call void @do_safepoint() + call void @use(i64 addrspace(1)* %bdv) + ret void +} + +define void @test6(i1 %cnd, i64 addrspace(1)* %obj, i64 %idx) gc "cangjie" { +; CHECK-LABEL: @test6( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr i64, i64 addrspace(1)* [[OBJ:%.*]], i64 1 +; CHECK-NEXT: [[VEC_BASE:%.*]] = insertelement <2 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[OBJ]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VEC:%.*]] = insertelement <2 x i64 addrspace(1)*> poison, i64 addrspace(1)* [[GEP]], i32 0 +; CHECK-NEXT: [[BDV_BASE:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC_BASE]], i64 [[IDX:%.*]], !is_base_value !1 +; CHECK-NEXT: [[BDV:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC]], i64 [[IDX]] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[BDV]], i64 addrspace(1)* [[BDV_BASE]]) ] +; CHECK-NEXT: [[BDV_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[BDV_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[BDV_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[BDV_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: call void @use(i64 addrspace(1)* [[BDV_RELOCATED_CASTED]]) +; CHECK-NEXT: ret void +; +; A more complicated example involving vector and scalar bases. +; This is derived from a failing test case when we didn't have correct +; insertelement handling. +entry: + %gep = getelementptr i64, i64 addrspace(1)* %obj, i64 1 + %vec = insertelement <2 x i64 addrspace(1)*> poison, i64 addrspace(1)* %gep, i32 0 + %bdv = extractelement <2 x i64 addrspace(1)*> %vec, i64 %idx + call void @do_safepoint() + call void @use(i64 addrspace(1)* %bdv) + ret void +} + +define i64 addrspace(1)* @test7(i1 %cnd, i64 addrspace(1)* %obj, i64 addrspace(1)* %obj2) gc "cangjie" { +; CHECK-LABEL: @test7( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[VEC_BASE:%.*]] = insertelement <2 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[OBJ2:%.*]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VEC:%.*]] = insertelement <2 x i64 addrspace(1)*> poison, i64 addrspace(1)* [[OBJ2]], i32 0 +; CHECK-NEXT: br label [[MERGE1:%.*]] +; CHECK: merge1: +; CHECK-NEXT: [[VEC2_BASE:%.*]] = phi <2 x i64 addrspace(1)*> [ [[VEC_BASE]], [[ENTRY:%.*]] ], [ [[VEC3_BASE:%.*]], [[MERGE1]] ], !is_base_value !1 +; CHECK-NEXT: [[VEC2:%.*]] = phi <2 x i64 addrspace(1)*> [ [[VEC]], [[ENTRY]] ], [ [[VEC3:%.*]], [[MERGE1]] ] +; CHECK-NEXT: [[GEP:%.*]] = getelementptr i64, i64 addrspace(1)* [[OBJ2]], i64 1 +; CHECK-NEXT: [[VEC3_BASE]] = insertelement <2 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[OBJ2]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VEC3]] = insertelement <2 x i64 addrspace(1)*> poison, i64 addrspace(1)* [[GEP]], i32 0 +; CHECK-NEXT: br i1 [[CND:%.*]], label [[MERGE1]], label [[NEXT1:%.*]] +; CHECK: next1: +; CHECK-NEXT: [[BDV_BASE:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC2_BASE]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[BDV:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC2]], i32 0 +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: merge: +; CHECK-NEXT: [[OBJB_BASE:%.*]] = phi i64 addrspace(1)* [ [[OBJ:%.*]], [[NEXT1]] ], [ [[BDV_BASE]], [[MERGE]] ], !is_base_value !1 +; CHECK-NEXT: [[OBJB:%.*]] = phi i64 addrspace(1)* [ [[OBJ]], [[NEXT1]] ], [ [[BDV]], [[MERGE]] ] +; CHECK-NEXT: br i1 [[CND]], label [[MERGE]], label [[NEXT:%.*]] +; CHECK: next: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[OBJB]], i64 addrspace(1)* [[OBJB_BASE]]) ] +; CHECK-NEXT: [[OBJB_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[OBJB_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJB_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[OBJB_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[OBJB_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJB_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[OBJB_RELOCATED_CASTED]] +; +entry: + %vec = insertelement <2 x i64 addrspace(1)*> poison, i64 addrspace(1)* %obj2, i32 0 + br label %merge1 + +merge1: ; preds = %merge1, %entry + %vec2 = phi <2 x i64 addrspace(1)*> [ %vec, %entry ], [ %vec3, %merge1 ] + %gep = getelementptr i64, i64 addrspace(1)* %obj2, i64 1 + %vec3 = insertelement <2 x i64 addrspace(1)*> poison, i64 addrspace(1)* %gep, i32 0 + br i1 %cnd, label %merge1, label %next1 + +next1: ; preds = %merge1 + %bdv = extractelement <2 x i64 addrspace(1)*> %vec2, i32 0 + br label %merge + +merge: ; preds = %merge, %next1 + %objb = phi i64 addrspace(1)* [ %obj, %next1 ], [ %bdv, %merge ] + br i1 %cnd, label %merge, label %next + +next: ; preds = %merge + call void @do_safepoint() + ret i64 addrspace(1)* %objb +} + +; identify base for shufflevector +define void @test8(i64 addrspace(1)* %obj, i64 %idx) gc "cangjie" { +; CHECK-LABEL: @test8( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr i64, i64 addrspace(1)* [[OBJ:%.*]], i64 1 +; CHECK-NEXT: [[GEP2:%.*]] = getelementptr i64, i64 addrspace(1)* [[OBJ]], i64 2 +; CHECK-NEXT: [[VEC1_BASE:%.*]] = insertelement <4 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[OBJ]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VEC1:%.*]] = insertelement <4 x i64 addrspace(1)*> poison, i64 addrspace(1)* [[GEP]], i32 0 +; CHECK-NEXT: [[VEC2_BASE:%.*]] = insertelement <4 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[OBJ]], i32 2, !is_base_value !1 +; CHECK-NEXT: [[VEC2:%.*]] = insertelement <4 x i64 addrspace(1)*> poison, i64 addrspace(1)* [[GEP2]], i32 2 +; CHECK-NEXT: [[VEC_BASE:%.*]] = shufflevector <4 x i64 addrspace(1)*> [[VEC1_BASE]], <4 x i64 addrspace(1)*> [[VEC2_BASE]], <2 x i32> , !is_base_value !1 +; CHECK-NEXT: [[VEC:%.*]] = shufflevector <4 x i64 addrspace(1)*> [[VEC1]], <4 x i64 addrspace(1)*> [[VEC2]], <2 x i32> +; CHECK-NEXT: [[BDV_BASE:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC_BASE]], i64 [[IDX:%.*]], !is_base_value !1 +; CHECK-NEXT: [[BDV:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC]], i64 [[IDX]] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[BDV]], i64 addrspace(1)* [[BDV_BASE]]) ] +; CHECK-NEXT: [[BDV_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[BDV_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[BDV_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[BDV_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: call void @use(i64 addrspace(1)* [[BDV_RELOCATED_CASTED]]) +; CHECK-NEXT: ret void +; +entry: + %gep = getelementptr i64, i64 addrspace(1)* %obj, i64 1 + %gep2 = getelementptr i64, i64 addrspace(1)* %obj, i64 2 + %vec1 = insertelement <4 x i64 addrspace(1)*> poison, i64 addrspace(1)* %gep, i32 0 + %vec2 = insertelement <4 x i64 addrspace(1)*> poison, i64 addrspace(1)* %gep2, i32 2 + %vec = shufflevector <4 x i64 addrspace(1)*> %vec1, <4 x i64 addrspace(1)*> %vec2, <2 x i32> + %bdv = extractelement <2 x i64 addrspace(1)*> %vec, i64 %idx + call void @do_safepoint() + call void @use(i64 addrspace(1)* %bdv) + ret void +} + +; Since the same 'base' vector is used in the shuffle operands, we do not need +; create a shufflevector base. +define void @test9(<4 x i64 addrspace(1)*> %vec1, i64 %idx) gc "cangjie" { +; CHECK-LABEL: @test9( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[VEC:%.*]] = shufflevector <4 x i64 addrspace(1)*> [[VEC1:%.*]], <4 x i64 addrspace(1)*> [[VEC1]], <2 x i32> +; CHECK-NEXT: [[BDV:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC]], i64 [[IDX:%.*]] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[BDV]]) ] +; CHECK-NEXT: [[BDV_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[BDV_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: call void @use(i64 addrspace(1)* [[BDV_RELOCATED_CASTED]]) +; CHECK-NEXT: ret void +; +entry: + ; shrinking vec1 into vec + %vec = shufflevector <4 x i64 addrspace(1)*> %vec1, <4 x i64 addrspace(1)*> %vec1, <2 x i32> + %bdv = extractelement <2 x i64 addrspace(1)*> %vec, i64 %idx + call void @do_safepoint() + call void @use(i64 addrspace(1)* %bdv) + ret void +} + +; vector operand of shufflevector is a phi +define i64 addrspace(1)* @test10(i1 %cnd, i64 addrspace(1)* %obj, i64 addrspace(1)* %obj2) gc "cangjie" { +; CHECK-LABEL: @test10( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[VEC1_BASE:%.*]] = insertelement <4 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[OBJ:%.*]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VEC1:%.*]] = insertelement <4 x i64 addrspace(1)*> poison, i64 addrspace(1)* [[OBJ]], i32 0 +; CHECK-NEXT: br i1 [[CND:%.*]], label [[HERE:%.*]], label [[MERGE:%.*]] +; CHECK: here: +; CHECK-NEXT: [[VEC2_BASE:%.*]] = insertelement <4 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[OBJ2:%.*]], i32 2, !is_base_value !1 +; CHECK-NEXT: [[VEC2:%.*]] = insertelement <4 x i64 addrspace(1)*> poison, i64 addrspace(1)* [[OBJ2]], i32 2 +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[VEC_BASE:%.*]] = phi <4 x i64 addrspace(1)*> [ [[VEC1_BASE]], [[ENTRY:%.*]] ], [ [[VEC2_BASE]], [[HERE]] ], [ [[VEC3_BASE:%.*]], [[MERGE]] ], !is_base_value !1 +; CHECK-NEXT: [[VEC:%.*]] = phi <4 x i64 addrspace(1)*> [ [[VEC1]], [[ENTRY]] ], [ [[VEC2]], [[HERE]] ], [ [[VEC3:%.*]], [[MERGE]] ] +; CHECK-NEXT: [[VEC3_BASE]] = shufflevector <4 x i64 addrspace(1)*> [[VEC_BASE]], <4 x i64 addrspace(1)*> [[VEC_BASE]], <4 x i32> , !is_base_value !1 +; CHECK-NEXT: [[VEC3]] = shufflevector <4 x i64 addrspace(1)*> [[VEC]], <4 x i64 addrspace(1)*> [[VEC]], <4 x i32> +; CHECK-NEXT: [[BDV_BASE:%.*]] = extractelement <4 x i64 addrspace(1)*> [[VEC3_BASE]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[BDV:%.*]] = extractelement <4 x i64 addrspace(1)*> [[VEC3]], i32 0 +; CHECK-NEXT: br i1 [[CND]], label [[MERGE]], label [[NEXT:%.*]] +; CHECK: next: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[BDV]], i64 addrspace(1)* [[BDV_BASE]]) ] +; CHECK-NEXT: [[BDV_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[BDV_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[BDV_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[BDV_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[BDV_RELOCATED_CASTED]] +; +entry: + %vec1 = insertelement <4 x i64 addrspace(1)*> poison, i64 addrspace(1)* %obj, i32 0 + br i1 %cnd, label %here, label %merge + +here: + %vec2 = insertelement <4 x i64 addrspace(1)*> poison, i64 addrspace(1)* %obj2, i32 2 + br label %merge + +merge: ; preds = %merge, %entry, %here + %vec = phi <4 x i64 addrspace(1)*> [ %vec1, %entry ], [ %vec2, %here], [ %vec3, %merge] + %vec3 = shufflevector <4 x i64 addrspace(1)*> %vec, <4 x i64 addrspace(1)*> %vec, <4 x i32> + %bdv = extractelement <4 x i64 addrspace(1)*> %vec3, i32 0 + br i1 %cnd, label %merge, label %next + +next: + call void @do_safepoint() + ret i64 addrspace(1)* %bdv +} +declare void @do_safepoint() + +define void @test11(<4 x i64 addrspace(1)*> %vec1) gc "cangjie" { +; CHECK-LABEL: @test11( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[VEC2:%.*]] = getelementptr i64, <4 x i64 addrspace(1)*> [[VEC1:%.*]], i32 1024 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(<4 x i64 addrspace(1)*> [[VEC1]]) ] +; CHECK-NEXT: [[VEC1_RELOCATED:%.*]] = call coldcc <4 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v4p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[VEC1_RELOCATED_CASTED:%.*]] = bitcast <4 x i8 addrspace(1)*> [[VEC1_RELOCATED]] to <4 x i64 addrspace(1)*> +; CHECK-NEXT: [[VEC2_REMAT:%.*]] = getelementptr i64, <4 x i64 addrspace(1)*> [[VEC1_RELOCATED_CASTED]], i32 1024 +; CHECK-NEXT: call void @use_vec(<4 x i64 addrspace(1)*> [[VEC2_REMAT]]) +; CHECK-NEXT: ret void +; +entry: + %vec2 = getelementptr i64, <4 x i64 addrspace(1)*> %vec1, i32 1024 + call void @do_safepoint() + call void @use_vec(<4 x i64 addrspace(1) *> %vec2) + ret void +} + +declare <4 x i64 addrspace(1)*> @def_vec() "gc-leaf-function" + +define void @test12(<4 x i64 addrspace(1)*> %vec1) gc "cangjie" { +; CHECK-LABEL: @test12( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[VEC:%.*]] = call <4 x i64 addrspace(1)*> @def_vec() +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(<4 x i64 addrspace(1)*> [[VEC]]) ] +; CHECK-NEXT: [[VEC_RELOCATED:%.*]] = call coldcc <4 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v4p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[VEC_RELOCATED_CASTED:%.*]] = bitcast <4 x i8 addrspace(1)*> [[VEC_RELOCATED]] to <4 x i64 addrspace(1)*> +; CHECK-NEXT: call void @use_vec(<4 x i64 addrspace(1)*> [[VEC_RELOCATED_CASTED]]) +; CHECK-NEXT: ret void +; +entry: + %vec = call <4 x i64 addrspace(1)*> @def_vec() + call void @do_safepoint() + call void @use_vec(<4 x i64 addrspace(1)*> %vec) + ret void +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/base-vector.ll b/llvm/test/Transforms/CJRewriteStatepoint/base-vector.ll new file mode 100644 index 000000000..e6a918d37 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/base-vector.ll @@ -0,0 +1,374 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S | FileCheck %s + + +define i64 addrspace(1)* @test(<2 x i64 addrspace(1)*> %vec, i32 %idx) gc "cangjie" { +; Note that the second extractelement is actually redundant here. A correct output would +; be to reuse the existing obj as a base since it is actually a base pointer. +; CHECK-LABEL: @test( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[OBJ:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC:%.*]], i32 [[IDX:%.*]] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[OBJ]]) ] +; CHECK-NEXT: [[OBJ_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[OBJ_RELOCATED_CASTED]] +; +entry: + %obj = extractelement <2 x i64 addrspace(1)*> %vec, i32 %idx + call void @do_safepoint() + ret i64 addrspace(1)* %obj +} + +define i64 addrspace(1)* @test2(<2 x i64 addrspace(1)*>* %ptr, i1 %cnd, i32 %idx1, i32 %idx2) gc "cangjie" { +; CHECK-LABEL: @test2( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[CND:%.*]], label [[TAKEN:%.*]], label [[UNTAKEN:%.*]] +; CHECK: taken: +; CHECK-NEXT: [[OBJA:%.*]] = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* [[PTR:%.*]], align 16 +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: untaken: +; CHECK-NEXT: [[OBJB:%.*]] = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* [[PTR]], align 16 +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[VEC:%.*]] = phi <2 x i64 addrspace(1)*> [ [[OBJA]], [[TAKEN]] ], [ [[OBJB]], [[UNTAKEN]] ] +; CHECK-NEXT: br i1 [[CND]], label [[TAKEN2:%.*]], label [[UNTAKEN2:%.*]] +; CHECK: taken2: +; CHECK-NEXT: [[OBJ0:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC]], i32 [[IDX1:%.*]] +; CHECK-NEXT: br label [[MERGE2:%.*]] +; CHECK: untaken2: +; CHECK-NEXT: [[OBJ1:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC]], i32 [[IDX2:%.*]] +; CHECK-NEXT: br label [[MERGE2]] +; CHECK: merge2: +; CHECK-NEXT: [[OBJ:%.*]] = phi i64 addrspace(1)* [ [[OBJ0]], [[TAKEN2]] ], [ [[OBJ1]], [[UNTAKEN2]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[OBJ]]) ] +; CHECK-NEXT: [[OBJ_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[OBJ_RELOCATED_CASTED]] +; +entry: + br i1 %cnd, label %taken, label %untaken + +taken: ; preds = %entry + %obja = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr + br label %merge + +untaken: ; preds = %entry + %objb = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr + br label %merge + +merge: ; preds = %untaken, %taken + %vec = phi <2 x i64 addrspace(1)*> [ %obja, %taken ], [ %objb, %untaken ] + br i1 %cnd, label %taken2, label %untaken2 + +taken2: ; preds = %merge + %obj0 = extractelement <2 x i64 addrspace(1)*> %vec, i32 %idx1 + br label %merge2 + +untaken2: ; preds = %merge + %obj1 = extractelement <2 x i64 addrspace(1)*> %vec, i32 %idx2 + br label %merge2 + +merge2: ; preds = %untaken2, %taken2 + %obj = phi i64 addrspace(1)* [ %obj0, %taken2 ], [ %obj1, %untaken2 ] + call void @do_safepoint() + ret i64 addrspace(1)* %obj +} + +define i64 addrspace(1)* @test3(i64 addrspace(1)* %ptr) gc "cangjie" { +; CHECK-LABEL: @test3( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[VEC_BASE:%.*]] = insertelement <2 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[PTR:%.*]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VEC:%.*]] = insertelement <2 x i64 addrspace(1)*> undef, i64 addrspace(1)* [[PTR]], i32 0 +; CHECK-NEXT: [[OBJ_BASE:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC_BASE]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[OBJ:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC]], i32 0 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[OBJ]], i64 addrspace(1)* [[OBJ_BASE]]) ] +; CHECK-NEXT: [[OBJ_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[OBJ_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[OBJ_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[OBJ_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[OBJ_RELOCATED_CASTED]] +; +entry: + %vec = insertelement <2 x i64 addrspace(1)*> undef, i64 addrspace(1)* %ptr, i32 0 + %obj = extractelement <2 x i64 addrspace(1)*> %vec, i32 0 + call void @do_safepoint() + ret i64 addrspace(1)* %obj +} + +define i64 addrspace(1)* @test4(i64 addrspace(1)* %ptr) gc "cangjie" { +; When we can optimize an extractelement from a known +; index and avoid introducing new base pointer instructions +; CHECK-LABEL: @test4( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[DERIVED:%.*]] = getelementptr i64, i64 addrspace(1)* [[PTR:%.*]], i64 16 +; CHECK-NEXT: [[VECA_BASE:%.*]] = insertelement <2 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[PTR]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VECA:%.*]] = insertelement <2 x i64 addrspace(1)*> undef, i64 addrspace(1)* [[DERIVED]], i32 0 +; CHECK-NEXT: [[VEC_BASE:%.*]] = insertelement <2 x i64 addrspace(1)*> [[VECA_BASE]], i64 addrspace(1)* [[PTR]], i32 1, !is_base_value !1 +; CHECK-NEXT: [[VEC:%.*]] = insertelement <2 x i64 addrspace(1)*> [[VECA]], i64 addrspace(1)* [[PTR]], i32 1 +; CHECK-NEXT: [[OBJ_BASE:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC_BASE]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[OBJ:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC]], i32 0 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[OBJ]], i64 addrspace(1)* [[OBJ_BASE]]) ] +; CHECK-NEXT: [[OBJ_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[OBJ_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[OBJ_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[OBJ_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[OBJ_RELOCATED_CASTED]] +; +entry: + %derived = getelementptr i64, i64 addrspace(1)* %ptr, i64 16 + %veca = insertelement <2 x i64 addrspace(1)*> undef, i64 addrspace(1)* %derived, i32 0 + %vec = insertelement <2 x i64 addrspace(1)*> %veca, i64 addrspace(1)* %ptr, i32 1 + %obj = extractelement <2 x i64 addrspace(1)*> %vec, i32 0 + call void @do_safepoint() + ret i64 addrspace(1)* %obj +} + +declare void @use(i64 addrspace(1)*) "gc-leaf-function" +declare void @use_vec(<4 x i64 addrspace(1)*>) "gc-leaf-function" + +define void @test5(i1 %cnd, i64 addrspace(1)* %obj) gc "cangjie" { +; When we fundementally have to duplicate +; CHECK-LABEL: @test5( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr i64, i64 addrspace(1)* [[OBJ:%.*]], i64 1 +; CHECK-NEXT: [[VEC_BASE:%.*]] = insertelement <2 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[OBJ]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VEC:%.*]] = insertelement <2 x i64 addrspace(1)*> undef, i64 addrspace(1)* [[GEP]], i32 0 +; CHECK-NEXT: [[BDV_BASE:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC_BASE]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[BDV:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC]], i32 0 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[BDV]], i64 addrspace(1)* [[BDV_BASE]]) ] +; CHECK-NEXT: [[BDV_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[BDV_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[BDV_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[BDV_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: call void @use(i64 addrspace(1)* [[BDV_RELOCATED_CASTED]]) +; CHECK-NEXT: ret void +; +entry: + %gep = getelementptr i64, i64 addrspace(1)* %obj, i64 1 + %vec = insertelement <2 x i64 addrspace(1)*> undef, i64 addrspace(1)* %gep, i32 0 + %bdv = extractelement <2 x i64 addrspace(1)*> %vec, i32 0 + call void @do_safepoint() + call void @use(i64 addrspace(1)* %bdv) + ret void +} + +define void @test6(i1 %cnd, i64 addrspace(1)* %obj, i64 %idx) gc "cangjie" { +; A more complicated example involving vector and scalar bases. +; This is derived from a failing test case when we didn't have correct +; insertelement handling. +; CHECK-LABEL: @test6( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr i64, i64 addrspace(1)* [[OBJ:%.*]], i64 1 +; CHECK-NEXT: [[VEC_BASE:%.*]] = insertelement <2 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[OBJ]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VEC:%.*]] = insertelement <2 x i64 addrspace(1)*> undef, i64 addrspace(1)* [[GEP]], i32 0 +; CHECK-NEXT: [[BDV_BASE:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC_BASE]], i64 [[IDX:%.*]], !is_base_value !1 +; CHECK-NEXT: [[BDV:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC]], i64 [[IDX]] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[BDV]], i64 addrspace(1)* [[BDV_BASE]]) ] +; CHECK-NEXT: [[BDV_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[BDV_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[BDV_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[BDV_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: call void @use(i64 addrspace(1)* [[BDV_RELOCATED_CASTED]]) +; CHECK-NEXT: ret void +; +entry: + %gep = getelementptr i64, i64 addrspace(1)* %obj, i64 1 + %vec = insertelement <2 x i64 addrspace(1)*> undef, i64 addrspace(1)* %gep, i32 0 + %bdv = extractelement <2 x i64 addrspace(1)*> %vec, i64 %idx + call void @do_safepoint() + call void @use(i64 addrspace(1)* %bdv) + ret void +} + +define i64 addrspace(1)* @test7(i1 %cnd, i64 addrspace(1)* %obj, i64 addrspace(1)* %obj2) gc "cangjie" { +; CHECK-LABEL: @test7( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[VEC_BASE:%.*]] = insertelement <2 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[OBJ2:%.*]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VEC:%.*]] = insertelement <2 x i64 addrspace(1)*> undef, i64 addrspace(1)* [[OBJ2]], i32 0 +; CHECK-NEXT: br label [[MERGE1:%.*]] +; CHECK: merge1: +; CHECK-NEXT: [[VEC2_BASE:%.*]] = phi <2 x i64 addrspace(1)*> [ [[VEC_BASE]], [[ENTRY:%.*]] ], [ [[VEC3_BASE:%.*]], [[MERGE1]] ], !is_base_value !1 +; CHECK-NEXT: [[VEC2:%.*]] = phi <2 x i64 addrspace(1)*> [ [[VEC]], [[ENTRY]] ], [ [[VEC3:%.*]], [[MERGE1]] ] +; CHECK-NEXT: [[GEP:%.*]] = getelementptr i64, i64 addrspace(1)* [[OBJ2]], i64 1 +; CHECK-NEXT: [[VEC3_BASE]] = insertelement <2 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[OBJ2]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VEC3]] = insertelement <2 x i64 addrspace(1)*> undef, i64 addrspace(1)* [[GEP]], i32 0 +; CHECK-NEXT: br i1 [[CND:%.*]], label [[MERGE1]], label [[NEXT1:%.*]] +; CHECK: next1: +; CHECK-NEXT: [[BDV_BASE:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC2_BASE]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[BDV:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC2]], i32 0 +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: merge: +; CHECK-NEXT: [[OBJB_BASE:%.*]] = phi i64 addrspace(1)* [ [[OBJ:%.*]], [[NEXT1]] ], [ [[BDV_BASE]], [[MERGE]] ], !is_base_value !1 +; CHECK-NEXT: [[OBJB:%.*]] = phi i64 addrspace(1)* [ [[OBJ]], [[NEXT1]] ], [ [[BDV]], [[MERGE]] ] +; CHECK-NEXT: br i1 [[CND]], label [[MERGE]], label [[NEXT:%.*]] +; CHECK: next: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[OBJB]], i64 addrspace(1)* [[OBJB_BASE]]) ] +; CHECK-NEXT: [[OBJB_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[OBJB_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJB_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[OBJB_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[OBJB_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJB_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[OBJB_RELOCATED_CASTED]] +; +entry: + %vec = insertelement <2 x i64 addrspace(1)*> undef, i64 addrspace(1)* %obj2, i32 0 + br label %merge1 + +merge1: ; preds = %merge1, %entry + %vec2 = phi <2 x i64 addrspace(1)*> [ %vec, %entry ], [ %vec3, %merge1 ] + %gep = getelementptr i64, i64 addrspace(1)* %obj2, i64 1 + %vec3 = insertelement <2 x i64 addrspace(1)*> undef, i64 addrspace(1)* %gep, i32 0 + br i1 %cnd, label %merge1, label %next1 + +next1: ; preds = %merge1 + %bdv = extractelement <2 x i64 addrspace(1)*> %vec2, i32 0 + br label %merge + +merge: ; preds = %merge, %next1 + %objb = phi i64 addrspace(1)* [ %obj, %next1 ], [ %bdv, %merge ] + br i1 %cnd, label %merge, label %next + +next: ; preds = %merge + call void @do_safepoint() + ret i64 addrspace(1)* %objb +} + +; identify base for shufflevector +define void @test8(i64 addrspace(1)* %obj, i64 %idx) gc "cangjie" { +; CHECK-LABEL: @test8( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[GEP:%.*]] = getelementptr i64, i64 addrspace(1)* [[OBJ:%.*]], i64 1 +; CHECK-NEXT: [[GEP2:%.*]] = getelementptr i64, i64 addrspace(1)* [[OBJ]], i64 2 +; CHECK-NEXT: [[VEC1_BASE:%.*]] = insertelement <4 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[OBJ]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VEC1:%.*]] = insertelement <4 x i64 addrspace(1)*> undef, i64 addrspace(1)* [[GEP]], i32 0 +; CHECK-NEXT: [[VEC2_BASE:%.*]] = insertelement <4 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[OBJ]], i32 2, !is_base_value !1 +; CHECK-NEXT: [[VEC2:%.*]] = insertelement <4 x i64 addrspace(1)*> undef, i64 addrspace(1)* [[GEP2]], i32 2 +; CHECK-NEXT: [[VEC_BASE:%.*]] = shufflevector <4 x i64 addrspace(1)*> [[VEC1_BASE]], <4 x i64 addrspace(1)*> [[VEC2_BASE]], <2 x i32> , !is_base_value !1 +; CHECK-NEXT: [[VEC:%.*]] = shufflevector <4 x i64 addrspace(1)*> [[VEC1]], <4 x i64 addrspace(1)*> [[VEC2]], <2 x i32> +; CHECK-NEXT: [[BDV_BASE:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC_BASE]], i64 [[IDX:%.*]], !is_base_value !1 +; CHECK-NEXT: [[BDV:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC]], i64 [[IDX]] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[BDV]], i64 addrspace(1)* [[BDV_BASE]]) ] +; CHECK-NEXT: [[BDV_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[BDV_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[BDV_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[BDV_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: call void @use(i64 addrspace(1)* [[BDV_RELOCATED_CASTED]]) +; CHECK-NEXT: ret void +; +entry: + %gep = getelementptr i64, i64 addrspace(1)* %obj, i64 1 + %gep2 = getelementptr i64, i64 addrspace(1)* %obj, i64 2 + %vec1 = insertelement <4 x i64 addrspace(1)*> undef, i64 addrspace(1)* %gep, i32 0 + %vec2 = insertelement <4 x i64 addrspace(1)*> undef, i64 addrspace(1)* %gep2, i32 2 + %vec = shufflevector <4 x i64 addrspace(1)*> %vec1, <4 x i64 addrspace(1)*> %vec2, <2 x i32> + %bdv = extractelement <2 x i64 addrspace(1)*> %vec, i64 %idx + call void @do_safepoint() + call void @use(i64 addrspace(1)* %bdv) + ret void +} + +; Since the same 'base' vector is used in the shuffle operands, we do not need +; create a shufflevector base. +define void @test9(<4 x i64 addrspace(1)*> %vec1, i64 %idx) gc "cangjie" { +; CHECK-LABEL: @test9( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[VEC:%.*]] = shufflevector <4 x i64 addrspace(1)*> [[VEC1:%.*]], <4 x i64 addrspace(1)*> [[VEC1]], <2 x i32> +; CHECK-NEXT: [[BDV:%.*]] = extractelement <2 x i64 addrspace(1)*> [[VEC]], i64 [[IDX:%.*]] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[BDV]]) ] +; CHECK-NEXT: [[BDV_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[BDV_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: call void @use(i64 addrspace(1)* [[BDV_RELOCATED_CASTED]]) +; CHECK-NEXT: ret void +; +entry: + ; shrinking vec1 into vec + %vec = shufflevector <4 x i64 addrspace(1)*> %vec1, <4 x i64 addrspace(1)*> %vec1, <2 x i32> + %bdv = extractelement <2 x i64 addrspace(1)*> %vec, i64 %idx + call void @do_safepoint() + call void @use(i64 addrspace(1)* %bdv) + ret void +} + +; vector operand of shufflevector is a phi +define i64 addrspace(1)* @test10(i1 %cnd, i64 addrspace(1)* %obj, i64 addrspace(1)* %obj2) gc "cangjie" { +; CHECK-LABEL: @test10( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[VEC1_BASE:%.*]] = insertelement <4 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[OBJ:%.*]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VEC1:%.*]] = insertelement <4 x i64 addrspace(1)*> undef, i64 addrspace(1)* [[OBJ]], i32 0 +; CHECK-NEXT: br i1 [[CND:%.*]], label [[HERE:%.*]], label [[MERGE:%.*]] +; CHECK: here: +; CHECK-NEXT: [[VEC2_BASE:%.*]] = insertelement <4 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[OBJ2:%.*]], i32 2, !is_base_value !1 +; CHECK-NEXT: [[VEC2:%.*]] = insertelement <4 x i64 addrspace(1)*> undef, i64 addrspace(1)* [[OBJ2]], i32 2 +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[VEC_BASE:%.*]] = phi <4 x i64 addrspace(1)*> [ [[VEC1_BASE]], [[ENTRY:%.*]] ], [ [[VEC2_BASE]], [[HERE]] ], [ [[VEC3_BASE:%.*]], [[MERGE]] ], !is_base_value !1 +; CHECK-NEXT: [[VEC:%.*]] = phi <4 x i64 addrspace(1)*> [ [[VEC1]], [[ENTRY]] ], [ [[VEC2]], [[HERE]] ], [ [[VEC3:%.*]], [[MERGE]] ] +; CHECK-NEXT: [[VEC3_BASE]] = shufflevector <4 x i64 addrspace(1)*> [[VEC_BASE]], <4 x i64 addrspace(1)*> [[VEC_BASE]], <4 x i32> , !is_base_value !1 +; CHECK-NEXT: [[VEC3]] = shufflevector <4 x i64 addrspace(1)*> [[VEC]], <4 x i64 addrspace(1)*> [[VEC]], <4 x i32> +; CHECK-NEXT: [[BDV_BASE:%.*]] = extractelement <4 x i64 addrspace(1)*> [[VEC3_BASE]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[BDV:%.*]] = extractelement <4 x i64 addrspace(1)*> [[VEC3]], i32 0 +; CHECK-NEXT: br i1 [[CND]], label [[MERGE]], label [[NEXT:%.*]] +; CHECK: next: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[BDV]], i64 addrspace(1)* [[BDV_BASE]]) ] +; CHECK-NEXT: [[BDV_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[BDV_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: [[BDV_BASE_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[BDV_BASE_RELOCATED_CASTED:%.*]] = bitcast i8 addrspace(1)* [[BDV_BASE_RELOCATED]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[BDV_RELOCATED_CASTED]] +; +entry: + %vec1 = insertelement <4 x i64 addrspace(1)*> undef, i64 addrspace(1)* %obj, i32 0 + br i1 %cnd, label %here, label %merge + +here: + %vec2 = insertelement <4 x i64 addrspace(1)*> undef, i64 addrspace(1)* %obj2, i32 2 + br label %merge + +merge: ; preds = %merge, %entry, %here + %vec = phi <4 x i64 addrspace(1)*> [ %vec1, %entry ], [ %vec2, %here], [ %vec3, %merge] + %vec3 = shufflevector <4 x i64 addrspace(1)*> %vec, <4 x i64 addrspace(1)*> %vec, <4 x i32> + %bdv = extractelement <4 x i64 addrspace(1)*> %vec3, i32 0 + br i1 %cnd, label %merge, label %next + +next: + call void @do_safepoint() + ret i64 addrspace(1)* %bdv +} +declare void @do_safepoint() + +define void @test11(<4 x i64 addrspace(1)*> %vec1) gc "cangjie" { +; CHECK-LABEL: @test11( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[VEC2:%.*]] = getelementptr i64, <4 x i64 addrspace(1)*> [[VEC1:%.*]], i32 1024 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(<4 x i64 addrspace(1)*> [[VEC1]]) ] +; CHECK-NEXT: [[VEC1_RELOCATED:%.*]] = call coldcc <4 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v4p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[VEC1_RELOCATED_CASTED:%.*]] = bitcast <4 x i8 addrspace(1)*> [[VEC1_RELOCATED]] to <4 x i64 addrspace(1)*> +; CHECK-NEXT: [[VEC2_REMAT:%.*]] = getelementptr i64, <4 x i64 addrspace(1)*> [[VEC1_RELOCATED_CASTED]], i32 1024 +; CHECK-NEXT: call void @use_vec(<4 x i64 addrspace(1)*> [[VEC2_REMAT]]) +; CHECK-NEXT: ret void +; +entry: + %vec2 = getelementptr i64, <4 x i64 addrspace(1)*> %vec1, i32 1024 + call void @do_safepoint() + call void @use_vec(<4 x i64 addrspace(1) *> %vec2) + ret void +} + +declare <4 x i64 addrspace(1)*> @def_vec() "gc-leaf-function" + +define void @test12(<4 x i64 addrspace(1)*> %vec1) gc "cangjie" { +; CHECK-LABEL: @test12( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[VEC:%.*]] = call <4 x i64 addrspace(1)*> @def_vec() +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(<4 x i64 addrspace(1)*> [[VEC]]) ] +; CHECK-NEXT: [[VEC_RELOCATED:%.*]] = call coldcc <4 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v4p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[VEC_RELOCATED_CASTED:%.*]] = bitcast <4 x i8 addrspace(1)*> [[VEC_RELOCATED]] to <4 x i64 addrspace(1)*> +; CHECK-NEXT: call void @use_vec(<4 x i64 addrspace(1)*> [[VEC_RELOCATED_CASTED]]) +; CHECK-NEXT: ret void +; +entry: + %vec = call <4 x i64 addrspace(1)*> @def_vec() + call void @do_safepoint() + call void @use_vec(<4 x i64 addrspace(1)*> %vec) + ret void +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/call-gc-result.ll b/llvm/test/Transforms/CJRewriteStatepoint/call-gc-result.ll new file mode 100644 index 000000000..c36968517 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/call-gc-result.ll @@ -0,0 +1,55 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S | FileCheck %s + +;; This test is to verify that gc_result from a call statepoint +;; can have preceding phis in its parent basic block. Unlike +;; invoke statepoint, call statepoint does not terminate the +;; block, and thus its gc_result is in the same block with the +;; call statepoint. + +declare i32 @foo() + +define i32 @test1(i1 %cond, i32 %a) gc "cangjie" { +; CHECK-LABEL: @test1( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[COND:%.*]], label [[BRANCH1:%.*]], label [[BRANCH2:%.*]] +; CHECK: branch1: +; CHECK-NEXT: [[B:%.*]] = add i32 [[A:%.*]], 1 +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: branch2: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[A]], [[BRANCH2]] ], [ [[B]], [[BRANCH1]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @foo, i32 0, i32 0) +; CHECK-NEXT: [[RET1:%.*]] = call i32 @llvm.cj.gc.result.i32(token [[TOKEN]]) +; CHECK-NEXT: ret i32 [[RET1]] +; +entry: + br i1 %cond, label %branch1, label %branch2 + +branch1: + %b = add i32 %a, 1 + br label %merge + +branch2: + br label %merge + +merge: + %phi = phi i32 [ %a, %branch2 ], [ %b, %branch1 ] + %ret = call i32 @foo() + ret i32 %ret +} + +; This function is inlined when inserting a poll. +declare void @do_safepoint() +define void @gc.safepoint_poll() { +; CHECK-LABEL: @gc.safepoint_poll( +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @do_safepoint() +; CHECK-NEXT: ret void +; +entry: + call void @do_safepoint() + ret void +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/check_traversal_order-inseltpoison.ll b/llvm/test/Transforms/CJRewriteStatepoint/check_traversal_order-inseltpoison.ll new file mode 100644 index 000000000..f216d380e --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/check_traversal_order-inseltpoison.ll @@ -0,0 +1,38 @@ +; RUN: opt -S -cj-rewrite-statepoint < %s | FileCheck %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128-ni:1" +target triple = "x86_64-unknown-linux-gnu" + +declare void @f() +declare void @g(i8 addrspace(1)*, i8 addrspace(1)*) +declare i32 @personality_function() + +; Make sure that we do not fail assertion because we process call of @g before +; we process the call of @f. + +define void @test_01(i8 addrspace(1)* %p, i1 %cond) gc "cangjie" personality i32 ()* @personality_function { + +; CHECK-LABEL: @test_01( + +entry: + %tmp0 = insertelement <2 x i8 addrspace(1)*> poison, i8 addrspace(1)* %p, i32 0 + %tmp1 = insertelement <2 x i8 addrspace(1)*> %tmp0, i8 addrspace(1)* %p, i32 1 + %tmp2 = extractelement <2 x i8 addrspace(1)*> %tmp1, i32 1 + %tmp3 = extractelement <2 x i8 addrspace(1)*> %tmp1, i32 0 + br label %loop + +loop: + br i1 %cond, label %cond_block, label %exit + +cond_block: + br i1 %cond, label %backedge, label %exit + +exit: + %tmp4 = phi i8 addrspace(1)* [ %tmp2, %loop ], [ %tmp2, %cond_block ] + call void @g(i8 addrspace(1)* %tmp3, i8 addrspace(1)* %tmp4) + ret void + +backedge: + call void @f() + br label %loop +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/check_traversal_order.ll b/llvm/test/Transforms/CJRewriteStatepoint/check_traversal_order.ll new file mode 100644 index 000000000..8a90cf812 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/check_traversal_order.ll @@ -0,0 +1,38 @@ +; RUN: opt -S -cj-rewrite-statepoint < %s | FileCheck %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128-ni:1" +target triple = "x86_64-unknown-linux-gnu" + +declare void @f() +declare void @g(i8 addrspace(1)*, i8 addrspace(1)*) +declare i32 @personality_function() + +; Make sure that we do not fail assertion because we process call of @g before +; we process the call of @f. + +define void @test_01(i8 addrspace(1)* %p, i1 %cond) gc "cangjie" personality i32 ()* @personality_function { + +; CHECK-LABEL: @test_01( + +entry: + %tmp0 = insertelement <2 x i8 addrspace(1)*> undef, i8 addrspace(1)* %p, i32 0 + %tmp1 = insertelement <2 x i8 addrspace(1)*> %tmp0, i8 addrspace(1)* %p, i32 1 + %tmp2 = extractelement <2 x i8 addrspace(1)*> %tmp1, i32 1 + %tmp3 = extractelement <2 x i8 addrspace(1)*> %tmp1, i32 0 + br label %loop + +loop: + br i1 %cond, label %cond_block, label %exit + +cond_block: + br i1 %cond, label %backedge, label %exit + +exit: + %tmp4 = phi i8 addrspace(1)* [ %tmp2, %loop ], [ %tmp2, %cond_block ] + call void @g(i8 addrspace(1)* %tmp3, i8 addrspace(1)* %tmp4) + ret void + +backedge: + call void @f() + br label %loop +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/cj-blackhole.ll b/llvm/test/Transforms/CJRewriteStatepoint/cj-blackhole.ll new file mode 100644 index 000000000..925491b51 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/cj-blackhole.ll @@ -0,0 +1,73 @@ +;RUN: opt < %s -passes=cj-rewrite-statepoint -S 2>&1 | FileCheck %s + +%TypeInfo = type { i8*, i8, i8, i16, i32, %BitMap*, i32, i8, i8, i16, i32*, i8*, i8*, i8*, %TypeInfo*, %ExtensionDef**, i8*, i8* } +%BitMap = type { i32, [0 x i8] } +%ExtensionDef = type { i32, i8, i8*, i8*, i8*, i8* } +%Unit.Type = type {} + +@"default:cc.ti" = global %TypeInfo { i8* null, i8 -128, i8 64, i16 2, i32 16, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i32 0, i8 8, i8 0, i16 -32768, i32* null, i8* null, i8* null, i8* null, %TypeInfo* null, %ExtensionDef** null, i8* null, i8* null } + +declare i8* @CJ_LLVM_BlackHole(i8* %0) + +declare cangjiegccc void @CJ_MCC_HandleSafepoint() + +declare void @CJ_MCC_StackCheck() + +declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) + +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) + +; CHECK-LABEL: _CN7default3fooHv( +define i64 @_CN7default3fooHv() { +allocas: + call cangjiegccc void @CJ_MCC_StackCheck() + %b = alloca i64, align 8 + %c = alloca i64, align 8 + store i64 5, i64* %b, align 8 + %0 = bitcast i64* %b to i8* + call cangjiegccc void @CJ_MCC_HandleSafepoint() + %1 = call i8* @CJ_LLVM_BlackHole(i8* nonnull %0) + %2 = bitcast i8* %1 to i64* + %3 = load i64, i64* %2, align 8 + %4 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %3, i64 1) + %.fca.1.extract = extractvalue { i64, i1 } %4, 1 + br i1 %.fca.1.extract, label %codeRepl, label %normal + +normal: ; preds = %allocas + %.fca.0.extract = extractvalue { i64, i1 } %4, 0 + store i64 %.fca.0.extract, i64* %c, align 8 + %5 = bitcast i64* %c to i8* +; CHECK-NOT: %6 = call i8* @CJ_LLVM_BlackHole(i8* nonnull %5) + %6 = call i8* @CJ_LLVM_BlackHole(i8* nonnull %5) + ret i64 0 + +codeRepl: ; preds = %allocas + unreachable +} + +; CHECK-LABEL: _CN7default4foo2HCNY_1AE( +define i8 addrspace(1)* @_CN7default4foo2HCNY_1AE(i8 addrspace(1)* %a) gc "cangjie" { +allocas: + call cangjiegccc void @CJ_MCC_StackCheck() + %0 = alloca %Unit.Type, align 8 + call cangjiegccc void @CJ_MCC_HandleSafepoint() + %1 = tail call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @"default:cc.ti" to i8*), i32 24) + %2 = getelementptr i8, i8 addrspace(1)* %1, i64 8 + %3 = bitcast i8 addrspace(1)* %2 to <2 x i64> addrspace(1)* + store <2 x i64> , <2 x i64> addrspace(1)* %3, align 8 + %4 = bitcast i8 addrspace(1)* %a to %TypeInfo* addrspace(1)* + %ti = load %TypeInfo*, %TypeInfo* addrspace(1)* %4, align 8 + %5 = getelementptr %TypeInfo, %TypeInfo* %ti, i64 0, i32 15 + %6 = load %ExtensionDef**, %ExtensionDef*** %5, align 8 + %7 = load %ExtensionDef*, %ExtensionDef** %6, align 8 + %8 = getelementptr %ExtensionDef, %ExtensionDef* %7, i64 0, i32 5 + %9 = bitcast i8** %8 to void (%Unit.Type*, i8 addrspace(1)*, i8 addrspace(1)*, %TypeInfo*)*** + %10 = load void (%Unit.Type*, i8 addrspace(1)*, i8 addrspace(1)*, %TypeInfo*)**, void (%Unit.Type*, i8 addrspace(1)*, i8 addrspace(1)*, %TypeInfo*)*** %9, align 8 + %11 = load void (%Unit.Type*, i8 addrspace(1)*, i8 addrspace(1)*, %TypeInfo*)*, void (%Unit.Type*, i8 addrspace(1)*, i8 addrspace(1)*, %TypeInfo*)** %10, align 8 +; CHECK: %token7 = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%Unit.Type*, i8 addrspace(1)*, i8 addrspace(1)*, %TypeInfo*)* %11, i32 4, i32 0, %Unit.Type* nonnull sret(%Unit.Type) %0, i8 addrspace(1)* %a.reloc5, i8 addrspace(1)* %1, %TypeInfo* %ti) [ "gc-live"(i8 addrspace(1)* %1, i8 addrspace(1)* %a.reloc5) ] + call void %11(%Unit.Type* noalias nonnull sret(%Unit.Type) %0, i8 addrspace(1)* %a, i8 addrspace(1)* %1, %TypeInfo* %ti) + %12 = addrspacecast i8 addrspace(1)* %1 to i8* + %13 = call i8* @CJ_LLVM_BlackHole(i8* %12) + %14 = addrspacecast i8* %13 to i8 addrspace(1)* + ret i8 addrspace(1)* %14 +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/codegen-cond.ll b/llvm/test/Transforms/CJRewriteStatepoint/codegen-cond.ll new file mode 100644 index 000000000..b938f1a5a --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/codegen-cond.ll @@ -0,0 +1,82 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +; A null test of a single value + +define i1 @test(i8 addrspace(1)* %p, i1 %rare) gc "cangjie" { +; CHECK-LABEL: @test +entry: + %cond = icmp eq i8 addrspace(1)* %p, null + br i1 %rare, label %safepoint, label %continue, !prof !0 + +safepoint: ; preds = %entry + call void @safepoint() [ "deopt"() ] + br label %continue + +continue: ; preds = %safepoint, %entry +; CHECK-LABEL: continue: +; CHECK: phi +; CHECK-DAG: [ %p.reloc, %safepoint ] +; CHECK-DAG: [ %p, %entry ] +; CHECK: %cond = icmp +; CHECK: br i1 %cond +; Comparing two pointers + br i1 %cond, label %taken, label %untaken + +taken: ; preds = %continue + ret i1 true + +untaken: ; preds = %continue + ret i1 false +} + +define i1 @test2(i8 addrspace(1)* %p, i8 addrspace(1)* %q, i1 %rare) gc "cangjie" { +; CHECK-LABEL: @test2 +entry: + %cond = icmp eq i8 addrspace(1)* %p, %q + br i1 %rare, label %safepoint, label %continue, !prof !0 + +safepoint: ; preds = %entry + call void @safepoint() [ "deopt"() ] + br label %continue + +continue: ; preds = %safepoint, %entry +; CHECK-LABEL: continue: +; CHECK: phi +; CHECK-DAG: [ %q.reloc, %safepoint ] +; CHECK-DAG: [ %q, %entry ] +; CHECK: phi +; CHECK-DAG: [ %p.reloc, %safepoint ] +; CHECK-DAG: [ %p, %entry ] +; CHECK: %cond = icmp +; CHECK: br i1 %cond +; Check that nothing bad happens if already last instruction +; before terminator + br i1 %cond, label %taken, label %untaken + +taken: ; preds = %continue + ret i1 true + +untaken: ; preds = %continue + ret i1 false +} + +define i1 @test3(i8 addrspace(1)* %p, i8 addrspace(1)* %q, i1 %rare) gc "cangjie" { +; CHECK-LABEL: @test3 +; CHECK: gc.statepoint +; CHECK: %cond = icmp +; CHECK: br i1 %cond +entry: + call void @safepoint() [ "deopt"() ] + %cond = icmp eq i8 addrspace(1)* %p, %q + br i1 %cond, label %taken, label %untaken + +taken: ; preds = %entry + ret i1 true + +untaken: ; preds = %entry + ret i1 false +} + +declare void @safepoint() +!0 = !{!"branch_weights", i32 1, i32 10000} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/constants.ll b/llvm/test/Transforms/CJRewriteStatepoint/constants.ll new file mode 100644 index 000000000..2047382f9 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/constants.ll @@ -0,0 +1,266 @@ +; RUN: opt -S -cj-rewrite-statepoint < %s | FileCheck %s +; RUN: opt -S -passes=cj-rewrite-statepoint < %s | FileCheck %s + +target datalayout = "e-ni:1:6" + +; constants don't get relocated. +@G = addrspace(1) global i8 5 + +declare void @foo() + +define i8 @test() gc "cangjie" { +; CHECK-LABEL: @test +; CHECK: gc.statepoint +; CHECK-NEXT: load i8, i8 addrspace(1)* inttoptr (i64 15 to i8 addrspace(1)*) +; Mostly just here to show reasonable code test can come from. +entry: + call void @foo() [ "deopt"() ] + %res = load i8, i8 addrspace(1)* inttoptr (i64 15 to i8 addrspace(1)*) + ret i8 %res +} + +define i8 @test2(i8 addrspace(1)* %p) gc "cangjie" { +; CHECK-LABEL: @test2 +; CHECK: gc.statepoint +; CHECK-NEXT: gc.relocate +; CHECK-NEXT: icmp +; Globals don't move and thus don't get relocated +entry: + call void @foo() [ "deopt"() ] + %cmp = icmp eq i8 addrspace(1)* %p, null + br i1 %cmp, label %taken, label %not_taken + +taken: ; preds = %not_taken, %entry + ret i8 0 + +not_taken: ; preds = %entry + %cmp2 = icmp ne i8 addrspace(1)* %p, null + br i1 %cmp2, label %taken, label %dead + +dead: ; preds = %not_taken + %addr = getelementptr i8, i8 addrspace(1)* %p, i32 15 + %res = load i8, i8 addrspace(1)* %addr + ret i8 %res +} + +define i8 @test3(i1 %always_true) gc "cangjie" { +; CHECK-LABEL: @test3 +; CHECK: gc.statepoint +; CHECK-NEXT: load i8, i8 addrspace(1)* @G +entry: + call void @foo() [ "deopt"() ] + %res = load i8, i8 addrspace(1)* @G, align 1 + ret i8 %res +} + +; Even for source languages without constant references, we can +; see constants can show up along paths where the value is dead. +; This is particular relevant when computing bases of PHIs. +define i8 addrspace(1)* @test4(i8 addrspace(1)* %p) gc "cangjie" { +; CHECK-LABEL: @test4 +entry: + %is_null = icmp eq i8 addrspace(1)* %p, null + br i1 %is_null, label %split, label %join + +split: + call void @foo() + %arg_value_addr.i = getelementptr inbounds i8, i8 addrspace(1)* %p, i64 8 + %arg_value_addr_casted.i = bitcast i8 addrspace(1)* %arg_value_addr.i to i8 addrspace(1)* addrspace(1)* + br label %join + +join: +; CHECK-LABEL: join +; CHECK: %addr2.base = + %addr2 = phi i8 addrspace(1)* addrspace(1)* [ %arg_value_addr_casted.i, %split ], [ inttoptr (i64 8 to i8 addrspace(1)* addrspace(1)*), %entry ] + ;; NOTE: This particular example can be jump-threaded, but in general, + ;; we can't, and have to deal with the resulting IR. + br i1 %is_null, label %early-exit, label %use + +early-exit: + ret i8 addrspace(1)* null + +use: +; CHECK-LABEL: use: +; CHECK: gc.statepoint +; CHECK: gc.relocate + call void @foo() + %res = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %addr2, align 1 + ret i8 addrspace(1)* %res +} + +; Globals don't move and thus don't get relocated +define i8 addrspace(1)* @test5(i1 %always_true) gc "cangjie" { +; CHECK-LABEL: @test5 +; CHECK: gc.statepoint +; CHECK-NEXT: %res = extractelement <2 x i8 addrspace(1)*> , i32 0 +entry: + call void @foo() + %res = extractelement <2 x i8 addrspace(1)*> , i32 0 + ret i8 addrspace(1)* %res +} + +define i8 addrspace(1)* @test6(i64 %arg) gc "cangjie" { +entry: + ; Don't fail any assertions and don't record null as a live value + ; CHECK-LABEL: test6 + ; CHECK: gc.statepoint + ; CHECK-NOT: call {{.*}}gc.relocate + %load_addr = getelementptr i8, i8 addrspace(1)* null, i64 %arg + call void @foo() [ "deopt"() ] + ret i8 addrspace(1)* %load_addr +} + +define i8 addrspace(1)* @test7(i64 %arg) gc "cangjie" { +entry: + ; Same as test7 but use regular constant instead of a null + ; CHECK-LABEL: test7 + ; CHECK: gc.statepoint + ; CHECK-NOT: call {{.*}}gc.relocate + %load_addr = getelementptr i8, i8 addrspace(1)* inttoptr (i64 15 to i8 addrspace(1)*), i64 %arg + call void @foo() [ "deopt"() ] + ret i8 addrspace(1)* %load_addr +} + +define i8 @test8(i8 addrspace(1)* %p) gc "cangjie" { +; Checks that base( phi(gep null, oop) ) = phi(null, base(oop)) and that we +; correctly relocate this value +; CHECK-LABEL: @test8 +entry: + %is_null = icmp eq i8 addrspace(1)* %p, null + br i1 %is_null, label %null.crit-edge, label %not-null + +not-null: + %load_addr = getelementptr inbounds i8, i8 addrspace(1)* %p, i64 8 + br label %join + +null.crit-edge: + %load_addr.const = getelementptr inbounds i8, i8 addrspace(1)* null, i64 8 + br label %join + +join: + %addr = phi i8 addrspace(1)* [ %load_addr, %not-null ], [%load_addr.const, %null.crit-edge] + ; CHECK: %addr.base = phi i8 addrspace(1)* + ; CHECK-DAG: [ %p, %not-null ] + ; CHECK-DAG: [ null, %null.crit-edge ] + ; CHECK: gc.statepoint + call void @foo() [ "deopt"() ] + ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr.base) + ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr) + br i1 %is_null, label %early-exit, label %use + +early-exit: + ret i8 0 + +use: + %res = load i8, i8 addrspace(1)* %addr, align 1 + ret i8 %res +} + +define i8 @test9(i8 addrspace(1)* %p) gc "cangjie" { +; Checks that base( phi(inttoptr, oop) ) = phi(null, base(oop)) and that we +; correctly relocate this value +; CHECK-LABEL: @test9 +entry: + %is_null = icmp eq i8 addrspace(1)* %p, null + br i1 %is_null, label %null.crit-edge, label %not-null + +not-null: + %load_addr = getelementptr inbounds i8, i8 addrspace(1)* %p, i64 8 + br label %join + +null.crit-edge: + br label %join + +join: + %addr = phi i8 addrspace(1)* [ %load_addr, %not-null ], [inttoptr (i64 8 to i8 addrspace(1)*), %null.crit-edge] + ; CHECK: %addr.base = phi i8 addrspace(1)* + ; CHECK-DAG: [ %p, %not-null ] + ; CHECK-DAG: [ null, %null.crit-edge ] + ; CHECK: gc.statepoint + call void @foo() [ "deopt"() ] + ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr.base) + ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr) + br i1 %is_null, label %early-exit, label %use + +early-exit: + ret i8 0 + +use: + %res = load i8, i8 addrspace(1)* %addr, align 1 + ret i8 %res +} + +define i8 @test10(i8 addrspace(1)* %p) gc "cangjie" { +; Checks that base( phi(const gep, oop) ) = phi(null, base(oop)) and that we +; correctly relocate this value +; CHECK-LABEL: @test10 +entry: + %is_null = icmp eq i8 addrspace(1)* %p, null + br i1 %is_null, label %null.crit-edge, label %not-null + +not-null: + %load_addr = getelementptr inbounds i8, i8 addrspace(1)* %p, i64 8 + br label %join + +null.crit-edge: + br label %join + +join: + %addr = phi i8 addrspace(1)* [ %load_addr, %not-null ], [getelementptr (i8, i8 addrspace(1)* null, i64 8), %null.crit-edge] + ; CHECK: %addr.base = phi i8 addrspace(1)* + ; CHECK-DAG: [ %p, %not-null ] + ; CHECK-DAG: [ null, %null.crit-edge ] + ; CHECK: gc.statepoint + call void @foo() [ "deopt"() ] + ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr.base) + ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%addr.base, %addr) + br i1 %is_null, label %early-exit, label %use + +early-exit: + ret i8 0 + +use: + %res = load i8, i8 addrspace(1)* %addr, align 1 + ret i8 %res +} + +define i32 addrspace(1)* @test11(i1 %c) gc "cangjie" { +; CHECK-LABEL: @test11 +; Checks that base( select(const1, const2) ) == null and that we don't record +; such value in the oop map +entry: + %val = select i1 %c, i32 addrspace(1)* inttoptr (i64 8 to i32 addrspace(1)*), i32 addrspace(1)* inttoptr (i64 15 to i32 addrspace(1)*) + ; CHECK: gc.statepoint + ; CHECK-NOT: call {{.*}}gc.relocate + call void @foo() [ "deopt"() ] + ret i32 addrspace(1)* %val +} + + +define <2 x i32 addrspace(1)*> @test12(i1 %c) gc "cangjie" { +; CHECK-LABEL: @test12 +; Same as test11 but with vectors +entry: + %val = select i1 %c, <2 x i32 addrspace(1)*> , + <2 x i32 addrspace(1)*> + ; CHECK: gc.statepoint + ; CHECK-NOT: call {{.*}}gc.relocate + call void @foo() [ "deopt"() ] + ret <2 x i32 addrspace(1)*> %val +} + +define <2 x i32 addrspace(1)*> @test13(i1 %c, <2 x i32 addrspace(1)*> %ptr) gc "cangjie" { +; CHECK-LABEL: @test13 +; Similar to test8, test9 and test10 but with vectors +entry: + %val = select i1 %c, <2 x i32 addrspace(1)*> %ptr, + <2 x i32 addrspace(1)*> + ; CHECK: %val.base = select i1 %c, <2 x i32 addrspace(1)*> %ptr, <2 x i32 addrspace(1)*> zeroinitializer, !is_base_value !1 + ; CHECK: gc.statepoint + call void @foo() [ "deopt"() ] + ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%val.base, %val.base) + ; CHECK-DAG: call {{.*}}gc.relocate{{.*}}(%val.base, %val) + ret <2 x i32 addrspace(1)*> %val +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/deref-pointers.ll b/llvm/test/Transforms/CJRewriteStatepoint/deref-pointers.ll new file mode 100644 index 000000000..4ae2da0e8 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/deref-pointers.ll @@ -0,0 +1,126 @@ +; RUN: opt -S -cj-rewrite-statepoint < %s | FileCheck %s +; RUN: opt -S -passes=cj-rewrite-statepoint < %s | FileCheck %s + +; CHECK: declare i8 addrspace(1)* @some_function_ret_deref() +; CHECK: define i8 addrspace(1)* @test_deref_arg(i8 addrspace(1)* %a) +; CHECK: define i8 addrspace(1)* @test_deref_or_null_arg(i8 addrspace(1)* %a) +; CHECK: define i8 addrspace(1)* @test_noalias_arg(i8 addrspace(1)* %a) + +declare void @foo() + +declare i8 addrspace(1)* @some_function() "gc-leaf-function" + +declare void @some_function_consumer(i8 addrspace(1)*) "gc-leaf-function" + +declare dereferenceable(4) i8 addrspace(1)* @some_function_ret_deref() "gc-leaf-function" +declare noalias i8 addrspace(1)* @some_function_ret_noalias() "gc-leaf-function" + +define i8 addrspace(1)* @test_deref_arg(i8 addrspace(1)* dereferenceable(4) %a) gc "cangjie" { +entry: + call void @foo() [ "deopt"(i32 0, i32 -1, i32 0, i32 0, i32 0) ] + ret i8 addrspace(1)* %a +} + +define i8 addrspace(1)* @test_deref_or_null_arg(i8 addrspace(1)* dereferenceable_or_null(4) %a) gc "cangjie" { +entry: + call void @foo() [ "deopt"(i32 0, i32 -1, i32 0, i32 0, i32 0) ] + ret i8 addrspace(1)* %a +} + +define i8 addrspace(1)* @test_noalias_arg(i8 addrspace(1)* noalias %a) gc "cangjie" { +entry: + call void @foo() [ "deopt"(i32 0, i32 -1, i32 0, i32 0, i32 0) ] + ret i8 addrspace(1)* %a +} + +define i8 addrspace(1)* @test_deref_retval() gc "cangjie" { +; CHECK-LABEL: @test_deref_retval( +; CHECK: %a = call i8 addrspace(1)* @some_function() +entry: + %a = call dereferenceable(4) i8 addrspace(1)* @some_function() + call void @foo() [ "deopt"(i32 0, i32 -1, i32 0, i32 0, i32 0) ] + ret i8 addrspace(1)* %a +} + +define i8 addrspace(1)* @test_deref_or_null_retval() gc "cangjie" { +; CHECK-LABEL: @test_deref_or_null_retval( +; CHECK: %a = call i8 addrspace(1)* @some_function() +entry: + %a = call dereferenceable_or_null(4) i8 addrspace(1)* @some_function() + call void @foo() [ "deopt"(i32 0, i32 -1, i32 0, i32 0, i32 0) ] + ret i8 addrspace(1)* %a +} + +define i8 addrspace(1)* @test_noalias_retval() gc "cangjie" { +; CHECK-LABEL: @test_noalias_retval( +; CHECK: %a = call i8 addrspace(1)* @some_function() +entry: + %a = call noalias i8 addrspace(1)* @some_function() + call void @foo() [ "deopt"(i32 0, i32 -1, i32 0, i32 0, i32 0) ] + ret i8 addrspace(1)* %a +} + +define i8 @test_md(i8 addrspace(1)* %ptr) gc "cangjie" { +; CHECK-LABEL: @test_md( +; CHECK: %tmp = load i8, i8 addrspace(1)* %ptr, align 1, !tbaa [[TAG_old:!.*]] +entry: + %tmp = load i8, i8 addrspace(1)* %ptr, !tbaa !0 + call void @foo() [ "deopt"(i32 0, i32 -1, i32 0, i32 0, i32 0) ] + ret i8 %tmp +} + +; Same as test_md() above, but with new-format TBAA metadata. +define i8 @test_md_new(i8 addrspace(1)* %ptr) gc "cangjie" { +; CHECK-LABEL: @test_md_new( +; CHECK: %tmp = load i8, i8 addrspace(1)* %ptr, align 1, !tbaa [[TAG_new:!.*]] +entry: + %tmp = load i8, i8 addrspace(1)* %ptr, !tbaa !4 + call void @foo() [ "deopt"(i32 0, i32 -1, i32 0, i32 0, i32 0) ] + ret i8 %tmp +} + +define i8 addrspace(1)* @test_decl_only_attribute(i8 addrspace(1)* %ptr) gc "cangjie" { +; CHECK-LABEL: @test_decl_only_attribute( +; No change here, but the prototype of some_function_ret_deref should have changed. +; CHECK: call i8 addrspace(1)* @some_function_ret_deref() +entry: + %a = call i8 addrspace(1)* @some_function_ret_deref() + call void @foo() [ "deopt"(i32 0, i32 -1, i32 0, i32 0, i32 0) ] + ret i8 addrspace(1)* %a +} + +define i8 addrspace(1)* @test_decl_only_noalias(i8 addrspace(1)* %ptr) gc "cangjie" { +; CHECK-LABEL: @test_decl_only_noalias( +; No change here, but the prototype of some_function_ret_noalias should have changed. +; CHECK: call i8 addrspace(1)* @some_function_ret_noalias() +entry: + %a = call i8 addrspace(1)* @some_function_ret_noalias() + call void @foo() [ "deopt"(i32 0, i32 -1, i32 0, i32 0, i32 0) ] + ret i8 addrspace(1)* %a +} + +define i8 addrspace(1)* @test_callsite_arg_attribute(i8 addrspace(1)* %ptr) gc "cangjie" { +; CHECK-LABEL: @test_callsite_arg_attribute( +; CHECK: call void @some_function_consumer(i8 addrspace(1)* %ptr) +entry: + call void @some_function_consumer(i8 addrspace(1)* dereferenceable(4) noalias %ptr) + call void @foo() [ "deopt"(i32 0, i32 -1, i32 0, i32 0, i32 0) ] + ret i8 addrspace(1)* %ptr +} + +!0 = !{!1, !2, i64 0, i64 1} ; TAG_old +!1 = !{!"type_base_old", !2, i64 0} +!2 = !{!"type_access_old", !3} +!3 = !{!"root"} + +!4 = !{!5, !6, i64 0, i64 1, i64 1} ; TAG_new +!5 = !{!3, i64 1, !"type_base_new", !6, i64 0, i64 1} +!6 = !{!3, i64 1, !"type_access_new"} + +; CHECK-DAG: [[ROOT:!.*]] = !{!"root"} +; CHECK-DAG: [[TYPE_access_old:!.*]] = !{!"type_access_old", [[ROOT]]} +; CHECK-DAG: [[TYPE_base_old:!.*]] = !{!"type_base_old", [[TYPE_access_old]], i64 0} +; CHECK-DAG: [[TAG_old]] = !{[[TYPE_base_old]], [[TYPE_access_old]], i64 0} +; CHECK-DAG: [[TYPE_access_new:!.*]] = !{[[ROOT]], i64 1, !"type_access_new"} +; CHECK-DAG: [[TYPE_base_new:!.*]] = !{[[ROOT]], i64 1, !"type_base_new", [[TYPE_access_new]], i64 0, i64 1} +; CHECK-DAG: [[TAG_new]] = !{[[TYPE_base_new]], [[TYPE_access_new]], i64 0, i64 1} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/drop-invalid-metadata.ll b/llvm/test/Transforms/CJRewriteStatepoint/drop-invalid-metadata.ll new file mode 100644 index 000000000..c816ce759 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/drop-invalid-metadata.ll @@ -0,0 +1,142 @@ +; RUN: opt -S -cj-rewrite-statepoint < %s | FileCheck %s +; RUN: opt -S -passes=cj-rewrite-statepoint < %s | FileCheck %s + +; This test checks that metadata that's invalid after RS4GC is dropped. +; We can miscompile if optimizations scheduled after RS4GC uses the +; metadata that's infact invalid. + +declare void @bar() + +declare void @baz(i32) +; Confirm that loadedval instruction does not contain invariant.load metadata. +; but contains the range metadata. +; Since loadedval is not marked invariant, it will prevent incorrectly sinking +; %loadedval in LICM and avoid creation of an unreloc use of %baseaddr. +define void @test_invariant_load() gc "cangjie" { +; CHECK-LABEL: @test_invariant_load +; CHECK: %loadedval = load i32, i32 addrspace(1)* %baseaddr, align 8, !range !1 +bb: + br label %outerloopHdr + +outerloopHdr: ; preds = %bb6, %bb + %baseaddr = phi i32 addrspace(1)* [ undef, %bb ], [ %tmp4, %bb6 ] +; LICM may sink this load to exit block after RS4GC because it's tagged invariant. + %loadedval = load i32, i32 addrspace(1)* %baseaddr, align 8, !range !0, !invariant.load !1 + br label %innerloopHdr + +innerloopHdr: ; preds = %innerlooplatch, %outerloopHdr + %tmp4 = phi i32 addrspace(1)* [ %baseaddr, %outerloopHdr ], [ %gep, %innerlooplatch ] + br label %innermostloophdr + +innermostloophdr: ; preds = %bb6, %innerloopHdr + br i1 undef, label %exitblock, label %bb6 + +bb6: ; preds = %innermostloophdr + switch i32 undef, label %innermostloophdr [ + i32 0, label %outerloopHdr + i32 1, label %innerlooplatch + ] + +innerlooplatch: ; preds = %bb6 + call void @bar() + %gep = getelementptr inbounds i32, i32 addrspace(1)* %tmp4, i64 8 + br label %innerloopHdr + +exitblock: ; preds = %innermostloophdr + %tmp13 = add i32 42, %loadedval + call void @baz(i32 %tmp13) + unreachable +} + +; drop the noalias metadata. +define void @test_noalias(i32 %x, i32 addrspace(1)* %p, i32 addrspace(1)* %q) gc "cangjie" { +; CHECK-LABEL: test_noalias +; CHECK: %y = load i32, i32 addrspace(1)* %q, align 16 +; CHECK: gc.statepoint +; CHECK: %p.reloc +; CHECK-NEXT: %p.reloc.casted = bitcast i8 addrspace(1)* %p.reloc to i32 addrspace(1)* +; CHECK-NEXT: store i32 %x, i32 addrspace(1)* %p.reloc.casted, align 16 +entry: + %y = load i32, i32 addrspace(1)* %q, align 16, !noalias !5 + call void @baz(i32 %x) + store i32 %x, i32 addrspace(1)* %p, align 16, !noalias !5 + ret void +} + +; drop the dereferenceable metadata +define void @test_dereferenceable(i32 addrspace(1)* addrspace(1)* %p, i32 %x, i32 addrspace(1)* %q) gc "cangjie" { +; CHECK-LABEL: test_dereferenceable +; CHECK: %v1 = load i32 addrspace(1)*, i32 addrspace(1)* addrspace(1)* %p +; CHECK-NEXT: %v2 = load i32, i32 addrspace(1)* %v1 +; CHECK: gc.statepoint + %v1 = load i32 addrspace(1)*, i32 addrspace(1)* addrspace(1)* %p, !dereferenceable !6 + %v2 = load i32, i32 addrspace(1)* %v1 + call void @baz(i32 %x) + store i32 %v2, i32 addrspace(1)* %q, align 16 + ret void +} + +; invariant.start allows us to sink the load past the baz statepoint call into taken block, which is +; incorrect. remove the invariant.start and RAUW undef. +define void @test_inv_start(i1 %cond, i32 addrspace(1)* addrspace(1)* %p, i32 %x, i32 addrspace(1)* %q) gc "cangjie" { +; CHECK-LABEL: test_inv_start +; CHECK-NOT: invariant.start +; CHECK: gc.statepoint + %v1 = load i32 addrspace(1)*, i32 addrspace(1)* addrspace(1)* %p + %invst = call {}* @llvm.invariant.start.p1i32(i64 1, i32 addrspace(1)* %v1) + %v2 = load i32, i32 addrspace(1)* %v1 + call void @baz(i32 %x) + br i1 %cond, label %taken, label %untaken + +taken: + store i32 %v2, i32 addrspace(1)* %q, align 16 + call void @llvm.invariant.end.p1i32({}* %invst, i64 4, i32 addrspace(1)* %v1) + ret void + +; CHECK-LABEL: untaken: +; CHECK: gc.statepoint +untaken: + %foo = call i32 @escaping.invariant.start({}* %invst) + call void @dummy(i32 %foo) + ret void +} + +; invariant.start is removed and the uses are undef'ed. +define void @test_inv_start2(i1 %cond, i32 addrspace(1)* addrspace(1)* %p, i32 %x, i32 addrspace(1)* %q) gc "cangjie" { +; CHECK-LABEL: test_inv_start2 +; CHECK-NOT: invariant.start +; CHECK: gc.statepoint + %v1 = load i32 addrspace(1)*, i32 addrspace(1)* addrspace(1)* %p + %invst = call {}* @llvm.invariant.start.p1i32(i64 1, i32 addrspace(1)* %v1) + %v2 = load i32, i32 addrspace(1)* %v1 + call void @baz(i32 %x) + br i1 %cond, label %taken, label %untaken + +taken: + store i32 %v2, i32 addrspace(1)* %q, align 16 + call void @llvm.invariant.end.p1i32({}* %invst, i64 4, i32 addrspace(1)* %v1) + ret void + +untaken: + ret void +} +declare {}* @llvm.invariant.start.p1i32(i64, i32 addrspace(1)* nocapture) nounwind readonly +declare void @llvm.invariant.end.p1i32({}*, i64, i32 addrspace(1)* nocapture) nounwind +declare i32 @escaping.invariant.start({}*) nounwind +declare void @dummy(i32) +declare token @llvm.cj.gc.statepoint.p0f_isVoidi32f(i64, i32, void (i32)*, i32, i32, ...) + +; Function Attrs: nounwind readonly +declare i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token, i32, i32) #0 + +declare token @llvm.cj.gc.statepoint.p0f_isVoidf(i64, i32, void ()*, i32, i32, ...) + +attributes #0 = { nounwind readonly } + +!0 = !{i32 0, i32 2147483647} +!1 = !{} +!2 = !{i32 10, i32 1} +!3 = !{!3} +!4 = !{!4, !3} +!5 = !{!4} +!6 = !{i64 8} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/insert-memset-for-phi-struct.ll b/llvm/test/Transforms/CJRewriteStatepoint/insert-memset-for-phi-struct.ll new file mode 100644 index 000000000..fea58689c --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/insert-memset-for-phi-struct.ll @@ -0,0 +1,54 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + + +%struct = type { i8 addrspace(1)* } + +define void @booo(i64 %0) gc "cangjie" { + +; CHECK: entry: +; CHECK: call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false) +; CHECK: call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false) +; +entry: + %r1 = alloca %struct + %r2 = alloca %struct + br label %bb0 + +bb0: + %1 = bitcast %struct* %r2 to i8* + %icmpeq.i = icmp eq i64 %0, 0 + br i1 %icmpeq.i, label %bb1, label %bbend + +; CHECK: bb1: +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 8, i8* %3) +; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* align 8 %3, i8 0, i64 8, i1 false) +; +bb1: + call void @llvm.lifetime.start.p0i8(i64 8, i8* %1) + call void @testcase1() + %icmpeq.g = icmp ugt i64 %0, 4 + br i1 %icmpeq.g, label %bb2, label %bbend + +; CHECK: bb2: +; CHECK: call void @llvm.memset.p0i8.i64(i8* %3, i8 %v3, i64 8, i1 false) +; +bb2: + %v = call i8 @testcase2() + call void @llvm.memset.p0i8.i64(i8* %1, i8 %v, i64 8, i1 false) + br label %bbend + +bbend: + %sink = phi %struct* [%r1, %bb0], [%r1, %bb1], [%r2, %bb2] + %2 = addrspacecast %struct* %sink to %struct addrspace(1)* + call void @test(%struct addrspace(1)* %2) + ret void +} + + +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) + +declare void @testcase1() gc "cangjie" +declare i8 @testcase2() gc "cangjie" +declare void @test(%struct addrspace(1)* %arg0) gc "cangjie" diff --git a/llvm/test/Transforms/CJRewriteStatepoint/invokes.ll b/llvm/test/Transforms/CJRewriteStatepoint/invokes.ll new file mode 100644 index 000000000..da1d462cf --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/invokes.ll @@ -0,0 +1,138 @@ +; RUN: opt < %s -S -cj-rewrite-statepoint | FileCheck %s +; RUN: opt < %s -S -passes=cj-rewrite-statepoint | FileCheck %s + +declare i64 addrspace(1)* @some_call(i64 addrspace(1)*) +declare i32 @personality_function() + +define i64 addrspace(1)* @test_basic(i64 addrspace(1)* %obj, i64 addrspace(1)* %obj1) gc "cangjie" personality i32 ()* @personality_function { +; CHECK-LABEL: entry: +entry: + ; CHECK: invoke + ; CHECK: statepoint + ; CHECK: some_call + %ret_val = invoke i64 addrspace(1)* @some_call(i64 addrspace(1)* %obj) + to label %normal_return unwind label %exceptional_return + +; CHECK-LABEL: normal_return: +; CHECK: gc.result +; CHECK: ret i64 + +normal_return: + ret i64 addrspace(1)* %ret_val + +; CHECK-LABEL: exceptional_return: +; CHECK: landingpad +; CHECK: ret i64 + +exceptional_return: + %landing_pad4 = landingpad token + cleanup + ret i64 addrspace(1)* %obj1 +} + +declare <4 x i64 addrspace(1)*> @some_vector_call(<4 x i64 addrspace(1)*>) + +define <4 x i64 addrspace(1)*> @test_basic_vector(<4 x i64 addrspace(1)*> %objs, <4 x i64 addrspace(1)*> %objs1) gc "cangjie" personality i32 ()* @personality_function { +; CHECK-LABEL: @test_basic_vector +entry: +; CHECK: invoke{{.*}}llvm.cj.gc.statepoint{{.*}}some_vector_call + %ret_val = invoke <4 x i64 addrspace(1)*> @some_vector_call(<4 x i64 addrspace(1)*> %objs) + to label %normal_return unwind label %exceptional_return + +; CHECK-LABEL: normal_return: +; CHECK: gc.result +; CHECK: ret <4 x i64 addrspace(1)*> + +normal_return: + ret <4 x i64 addrspace(1)*> %ret_val + +; CHECK-LABEL: exceptional_return: +; CHECK: landingpad +; CHECK: ret <4 x i64 addrspace(1)*> + +exceptional_return: + %landing_pad4 = landingpad token + cleanup + ret <4 x i64 addrspace(1)*> %objs1 +} + +define i64 addrspace(1)* @test_two_invokes(i64 addrspace(1)* %obj, i64 addrspace(1)* %obj1) gc "cangjie" personality i32 ()* @personality_function { +; CHECK-LABEL: entry: +entry: + ; CHECK: invoke + ; CHECK: statepoint + ; CHECK: some_call + %ret_val1 = invoke i64 addrspace(1)* @some_call(i64 addrspace(1)* %obj) + to label %second_invoke unwind label %exceptional_return + +; CHECK-LABEL: second_invoke: +second_invoke: + ; CHECK: invoke + ; CHECK: statepoint + ; CHECK: some_call + %ret_val2 = invoke i64 addrspace(1)* @some_call(i64 addrspace(1)* %ret_val1) + to label %normal_return unwind label %exceptional_return + +; CHECK-LABEL: normal_return: +normal_return: + ; CHECK: gc.result + ; CHECK: ret i64 + ret i64 addrspace(1)* %ret_val2 + +; CHECK: exceptional_return: +; CHECK: ret i64 + +exceptional_return: + %landing_pad4 = landingpad token + cleanup + ret i64 addrspace(1)* %obj1 +} + +define i64 addrspace(1)* @test_phi_node(i1 %cond, i64 addrspace(1)* %obj) gc "cangjie" personality i32 ()* @personality_function { +; CHECK-LABEL: @test_phi_node +; CHECK-LABEL: entry: +entry: + br i1 %cond, label %left, label %right + +left: + %ret_val_left = invoke i64 addrspace(1)* @some_call(i64 addrspace(1)* %obj) + to label %merge unwind label %exceptional_return + +right: + %ret_val_right = invoke i64 addrspace(1)* @some_call(i64 addrspace(1)* %obj) + to label %merge unwind label %exceptional_return + +; CHECK: merge[[A:[0-9]]]: +; CHECK: gc.result +; CHECK: br label %[[with_phi:merge[0-9]*]] + +; CHECK: merge[[B:[0-9]]]: +; CHECK: gc.result +; CHECK: br label %[[with_phi]] + +; CHECK: [[with_phi]]: +; CHECK: phi +; CHECK: ret i64 addrspace(1)* %ret_val +merge: + %ret_val = phi i64 addrspace(1)* [%ret_val_left, %left], [%ret_val_right, %right] + ret i64 addrspace(1)* %ret_val + +; CHECK-LABEL: exceptional_return: +; CHECK: ret i64 addrspace(1)* + +exceptional_return: + %landing_pad4 = landingpad token + cleanup + ret i64 addrspace(1)* %obj +} + +declare void @do_safepoint() +define void @gc.safepoint_poll() { +; CHECK-LABEL: gc.safepoint_poll +; CHECK-LABEL: entry +; CHECK-NEXT: do_safepoint +; CHECK-NEXT: ret void +entry: + call void @do_safepoint() + ret void +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/leaf-function.ll b/llvm/test/Transforms/CJRewriteStatepoint/leaf-function.ll new file mode 100644 index 000000000..5c4401985 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/leaf-function.ll @@ -0,0 +1,34 @@ +; RUN: opt < %s -S -cj-rewrite-statepoint | FileCheck %s +; RUN: opt < %s -S -passes=cj-rewrite-statepoint | FileCheck %s + +declare void @foo() "gc-leaf-function" +declare void @bar() + +; Calls of functions with the "gc-leaf-function" attribute shouldn't be turned +; into a safepoint. An entry safepoint should get inserted, though. +define void @test_leaf_function() gc "cangjie" { +; CHECK-LABEL: test_leaf_function +; CHECK-NOT: gc.statepoint +; CHECK-NOT: gc.result +entry: + call void @foo() + ret void +} + +define void @test_leaf_function_call() gc "cangjie" { +; CHECK-LABEL: test_leaf_function_call +; CHECK-NOT: gc.statepoint +; CHECK-NOT: gc.result +entry: + call void @bar() "gc-leaf-function" + ret void +} + +; This function is inlined when inserting a poll. +declare void @do_safepoint() +define void @gc.safepoint_poll() { +; CHECK-LABEL: gc.safepoint_poll +entry: + call void @do_safepoint() + ret void +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/libcall.ll b/llvm/test/Transforms/CJRewriteStatepoint/libcall.ll new file mode 100644 index 000000000..192b5035f --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/libcall.ll @@ -0,0 +1,15 @@ +; A call to a libcall function is not a statepoint. +; This test verifies that calls to libcalls functions do not get converted to +; statepoint calls. +; RUN: opt -S -cj-rewrite-statepoint < %s | FileCheck %s +; RUN: opt -S -passes=cj-rewrite-statepoint < %s | FileCheck %s + +declare double @ldexp(double %x, i32 %n) nounwind readnone + +define double @test_libcall(double %x) gc "cangjie" { +; CHECK-LABEL: test_libcall +; CHECK-NEXT: %res = call double @ldexp(double %x, i32 5) +; CHECK-NEXT: ret double %res + %res = call double @ldexp(double %x, i32 5) nounwind readnone + ret double %res +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/live-vector-nosplit-inseltpoison.ll b/llvm/test/Transforms/CJRewriteStatepoint/live-vector-nosplit-inseltpoison.ll new file mode 100644 index 000000000..67ce03a6e --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/live-vector-nosplit-inseltpoison.ll @@ -0,0 +1,138 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; Test that we can correctly handle vectors of pointers in statepoint +; rewriting. +; RUN: opt < %s -cj-rewrite-statepoint -S | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S | FileCheck %s + +; A non-vector relocation for comparison +define i64 addrspace(1)* @test(i64 addrspace(1)* %obj) gc "cangjie" { +; CHECK-LABEL: @test( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[OBJ:%.*]]) ] +; CHECK-NEXT: [[OBJ_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOC_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_RELOC]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[OBJ_RELOC_CASTED]] +; +; A base vector from a argument +entry: + call void @do_safepoint() + ret i64 addrspace(1)* %obj +} + +; A vector argument +define <2 x i64 addrspace(1)*> @test2(<2 x i64 addrspace(1)*> %obj) gc "cangjie" { +; CHECK-LABEL: @test2( +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(<2 x i64 addrspace(1)*> [[OBJ:%.*]]) ] +; CHECK-NEXT: [[OBJ_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOC_CASTED:%.*]] = bitcast <2 x i8 addrspace(1)*> [[OBJ_RELOC]] to <2 x i64 addrspace(1)*> +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> [[OBJ_RELOC_CASTED]] +; + call void @do_safepoint() + ret <2 x i64 addrspace(1)*> %obj +} + +; A load +define <2 x i64 addrspace(1)*> @test3(<2 x i64 addrspace(1)*>* %ptr) gc "cangjie" { +; CHECK-LABEL: @test3( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[OBJ:%.*]] = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* [[PTR:%.*]], align 16 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(<2 x i64 addrspace(1)*> [[OBJ]]) ] +; CHECK-NEXT: [[OBJ_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOC_CASTED:%.*]] = bitcast <2 x i8 addrspace(1)*> [[OBJ_RELOC]] to <2 x i64 addrspace(1)*> +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> [[OBJ_RELOC_CASTED]] +; +entry: + %obj = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr + call void @do_safepoint() + ret <2 x i64 addrspace(1)*> %obj +} + +declare i32 @fake_personality_function() + +; When a statepoint is an invoke rather than a call +define <2 x i64 addrspace(1)*> @test4(<2 x i64 addrspace(1)*>* %ptr) gc "cangjie" personality i32 ()* @fake_personality_function { +; CHECK-LABEL: @test4( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[OBJ:%.*]] = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* [[PTR:%.*]], align 16 +; CHECK-NEXT: [[TOKEN:%.*]] = invoke token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(<2 x i64 addrspace(1)*> [[OBJ]]) ] +; CHECK-NEXT: to label [[NORMAL_RETURN:%.*]] unwind label [[EXCEPTIONAL_RETURN:%.*]] +; CHECK: normal_return: +; CHECK-NEXT: [[OBJ_RELOC1:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOC1_CASTED:%.*]] = bitcast <2 x i8 addrspace(1)*> [[OBJ_RELOC1]] to <2 x i64 addrspace(1)*> +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> [[OBJ_RELOC1_CASTED]] +; CHECK: exceptional_return: +; CHECK-NEXT: [[LANDING_PAD4:%.*]] = landingpad token +; CHECK-NEXT: cleanup +; CHECK-NEXT: [[OBJ_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[LANDING_PAD4]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOC_CASTED:%.*]] = bitcast <2 x i8 addrspace(1)*> [[OBJ_RELOC]] to <2 x i64 addrspace(1)*> +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> [[OBJ_RELOC_CASTED]] +; +entry: + %obj = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr + invoke void @do_safepoint() + to label %normal_return unwind label %exceptional_return + +normal_return: ; preds = %entry + ret <2 x i64 addrspace(1)*> %obj + +exceptional_return: ; preds = %entry + %landing_pad4 = landingpad token + cleanup + ret <2 x i64 addrspace(1)*> %obj +} + +; A newly created vector +define <2 x i64 addrspace(1)*> @test5(i64 addrspace(1)* %p) gc "cangjie" { +; CHECK-LABEL: @test5( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[VEC_BASE:%.*]] = insertelement <2 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[P:%.*]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VEC:%.*]] = insertelement <2 x i64 addrspace(1)*> poison, i64 addrspace(1)* [[P]], i32 0 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(<2 x i64 addrspace(1)*> [[VEC]], <2 x i64 addrspace(1)*> [[VEC_BASE]]) ] +; CHECK-NEXT: [[VEC_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[VEC_RELOC_CASTED:%.*]] = bitcast <2 x i8 addrspace(1)*> [[VEC_RELOC]] to <2 x i64 addrspace(1)*> +; CHECK-NEXT: [[VEC_BASE_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[VEC_BASE_RELOC_CASTED:%.*]] = bitcast <2 x i8 addrspace(1)*> [[VEC_BASE_RELOC]] to <2 x i64 addrspace(1)*> +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> [[VEC_RELOC_CASTED]] +; +entry: + %vec = insertelement <2 x i64 addrspace(1)*> poison, i64 addrspace(1)* %p, i32 0 + call void @do_safepoint() + ret <2 x i64 addrspace(1)*> %vec +} + +; A merge point +define <2 x i64 addrspace(1)*> @test6(i1 %cnd, <2 x i64 addrspace(1)*>* %ptr) gc "cangjie" { +; CHECK-LABEL: @test6( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[CND:%.*]], label [[TAKEN:%.*]], label [[UNTAKEN:%.*]] +; CHECK: taken: +; CHECK-NEXT: [[OBJA:%.*]] = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* [[PTR:%.*]], align 16 +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: untaken: +; CHECK-NEXT: [[OBJB:%.*]] = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* [[PTR]], align 16 +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[OBJ:%.*]] = phi <2 x i64 addrspace(1)*> [ [[OBJA]], [[TAKEN]] ], [ [[OBJB]], [[UNTAKEN]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(<2 x i64 addrspace(1)*> [[OBJ]]) ] +; CHECK-NEXT: [[OBJ_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOC_CASTED:%.*]] = bitcast <2 x i8 addrspace(1)*> [[OBJ_RELOC]] to <2 x i64 addrspace(1)*> +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> [[OBJ_RELOC_CASTED]] +; +entry: + br i1 %cnd, label %taken, label %untaken + +taken: ; preds = %entry + %obja = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr + br label %merge + +untaken: ; preds = %entry + %objb = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr + br label %merge + +merge: ; preds = %untaken, %taken + %obj = phi <2 x i64 addrspace(1)*> [ %obja, %taken ], [ %objb, %untaken ] + call void @do_safepoint() + ret <2 x i64 addrspace(1)*> %obj +} + +declare void @do_safepoint() diff --git a/llvm/test/Transforms/CJRewriteStatepoint/live-vector-nosplit.ll b/llvm/test/Transforms/CJRewriteStatepoint/live-vector-nosplit.ll new file mode 100644 index 000000000..bd479928d --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/live-vector-nosplit.ll @@ -0,0 +1,138 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; Test that we can correctly handle vectors of pointers in statepoint +; rewriting. +; RUN: opt < %s -cj-rewrite-statepoint -S | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S | FileCheck %s + +; A non-vector relocation for comparison +define i64 addrspace(1)* @test(i64 addrspace(1)* %obj) gc "cangjie" { +; CHECK-LABEL: @test( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i64 addrspace(1)* [[OBJ:%.*]]) ] +; CHECK-NEXT: [[OBJ_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOC_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_RELOC]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[OBJ_RELOC_CASTED]] +; +; A base vector from a argument +entry: + call void @do_safepoint() + ret i64 addrspace(1)* %obj +} + +; A vector argument +define <2 x i64 addrspace(1)*> @test2(<2 x i64 addrspace(1)*> %obj) gc "cangjie" { +; CHECK-LABEL: @test2( +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(<2 x i64 addrspace(1)*> [[OBJ:%.*]]) ] +; CHECK-NEXT: [[OBJ_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOC_CASTED:%.*]] = bitcast <2 x i8 addrspace(1)*> [[OBJ_RELOC]] to <2 x i64 addrspace(1)*> +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> [[OBJ_RELOC_CASTED]] +; + call void @do_safepoint() + ret <2 x i64 addrspace(1)*> %obj +} + +; A load +define <2 x i64 addrspace(1)*> @test3(<2 x i64 addrspace(1)*>* %ptr) gc "cangjie" { +; CHECK-LABEL: @test3( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[OBJ:%.*]] = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* [[PTR:%.*]], align 16 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(<2 x i64 addrspace(1)*> [[OBJ]]) ] +; CHECK-NEXT: [[OBJ_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOC_CASTED:%.*]] = bitcast <2 x i8 addrspace(1)*> [[OBJ_RELOC]] to <2 x i64 addrspace(1)*> +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> [[OBJ_RELOC_CASTED]] +; +entry: + %obj = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr + call void @do_safepoint() + ret <2 x i64 addrspace(1)*> %obj +} + +declare i32 @fake_personality_function() + +; When a statepoint is an invoke rather than a call +define <2 x i64 addrspace(1)*> @test4(<2 x i64 addrspace(1)*>* %ptr) gc "cangjie" personality i32 ()* @fake_personality_function { +; CHECK-LABEL: @test4( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[OBJ:%.*]] = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* [[PTR:%.*]], align 16 +; CHECK-NEXT: [[TOKEN:%.*]] = invoke token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(<2 x i64 addrspace(1)*> [[OBJ]]) ] +; CHECK-NEXT: to label [[NORMAL_RETURN:%.*]] unwind label [[EXCEPTIONAL_RETURN:%.*]] +; CHECK: normal_return: +; CHECK-NEXT: [[OBJ_RELOC1:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOC1_CASTED:%.*]] = bitcast <2 x i8 addrspace(1)*> [[OBJ_RELOC1]] to <2 x i64 addrspace(1)*> +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> [[OBJ_RELOC1_CASTED]] +; CHECK: exceptional_return: +; CHECK-NEXT: [[LANDING_PAD4:%.*]] = landingpad token +; CHECK-NEXT: cleanup +; CHECK-NEXT: [[OBJ_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[LANDING_PAD4]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOC_CASTED:%.*]] = bitcast <2 x i8 addrspace(1)*> [[OBJ_RELOC]] to <2 x i64 addrspace(1)*> +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> [[OBJ_RELOC_CASTED]] +; +entry: + %obj = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr + invoke void @do_safepoint() + to label %normal_return unwind label %exceptional_return + +normal_return: ; preds = %entry + ret <2 x i64 addrspace(1)*> %obj + +exceptional_return: ; preds = %entry + %landing_pad4 = landingpad token + cleanup + ret <2 x i64 addrspace(1)*> %obj +} + +; A newly created vector +define <2 x i64 addrspace(1)*> @test5(i64 addrspace(1)* %p) gc "cangjie" { +; CHECK-LABEL: @test5( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[VEC_BASE:%.*]] = insertelement <2 x i64 addrspace(1)*> zeroinitializer, i64 addrspace(1)* [[P:%.*]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[VEC:%.*]] = insertelement <2 x i64 addrspace(1)*> undef, i64 addrspace(1)* [[P]], i32 0 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(<2 x i64 addrspace(1)*> [[VEC]], <2 x i64 addrspace(1)*> [[VEC_BASE]]) ] +; CHECK-NEXT: [[VEC_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[VEC_RELOC_CASTED:%.*]] = bitcast <2 x i8 addrspace(1)*> [[VEC_RELOC]] to <2 x i64 addrspace(1)*> +; CHECK-NEXT: [[VEC_BASE_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[VEC_BASE_RELOC_CASTED:%.*]] = bitcast <2 x i8 addrspace(1)*> [[VEC_BASE_RELOC]] to <2 x i64 addrspace(1)*> +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> [[VEC_RELOC_CASTED]] +; +entry: + %vec = insertelement <2 x i64 addrspace(1)*> undef, i64 addrspace(1)* %p, i32 0 + call void @do_safepoint() + ret <2 x i64 addrspace(1)*> %vec +} + +; A merge point +define <2 x i64 addrspace(1)*> @test6(i1 %cnd, <2 x i64 addrspace(1)*>* %ptr) gc "cangjie" { +; CHECK-LABEL: @test6( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[CND:%.*]], label [[TAKEN:%.*]], label [[UNTAKEN:%.*]] +; CHECK: taken: +; CHECK-NEXT: [[OBJA:%.*]] = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* [[PTR:%.*]], align 16 +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: untaken: +; CHECK-NEXT: [[OBJB:%.*]] = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* [[PTR]], align 16 +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[OBJ:%.*]] = phi <2 x i64 addrspace(1)*> [ [[OBJA]], [[TAKEN]] ], [ [[OBJB]], [[UNTAKEN]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(<2 x i64 addrspace(1)*> [[OBJ]]) ] +; CHECK-NEXT: [[OBJ_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOC_CASTED:%.*]] = bitcast <2 x i8 addrspace(1)*> [[OBJ_RELOC]] to <2 x i64 addrspace(1)*> +; CHECK-NEXT: ret <2 x i64 addrspace(1)*> [[OBJ_RELOC_CASTED]] +; +entry: + br i1 %cnd, label %taken, label %untaken + +taken: ; preds = %entry + %obja = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr + br label %merge + +untaken: ; preds = %entry + %objb = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr + br label %merge + +merge: ; preds = %untaken, %taken + %obj = phi <2 x i64 addrspace(1)*> [ %obja, %taken ], [ %objb, %untaken ] + call void @do_safepoint() + ret <2 x i64 addrspace(1)*> %obj +} + +declare void @do_safepoint() diff --git a/llvm/test/Transforms/CJRewriteStatepoint/meetBDVState-hangs.ll b/llvm/test/Transforms/CJRewriteStatepoint/meetBDVState-hangs.ll new file mode 100644 index 000000000..a41c62c66 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/meetBDVState-hangs.ll @@ -0,0 +1,63 @@ +; RUN: opt -S -cj-rewrite-statepoint < %s | FileCheck %s + +; Regression test to incorrectly testing fixed state causing infinite loop. +; CHECK: test +target triple = "x86_64-unknown-linux-gnu" + +declare void @bar(i8 addrspace(1)* nocapture readonly) +declare noalias i8 addrspace(1)* @foo() + +define i8 addrspace(1)* @test(i1 %c, i1 %c1, i1 %c2, i1 %c3, i1 %c4, i1 %c5, i1 %c.exit) gc "cangjie" { +entry: + br i1 %c, label %ph.L, label %ph.R +ph.L: + %ph.L.p.b = call noalias nonnull i8 addrspace(1)* @foo() + %ph.L.p = getelementptr i8, i8 addrspace(1)* %ph.L.p.b, i64 8 + br label %ph.M +ph.R: + %ph.R.p = call noalias nonnull i8 addrspace(1)* @foo() + br label %ph.M +ph.M: + %ph.M.p = phi i8 addrspace(1)* [ %ph.L.p, %ph.L ], [ %ph.R.p, %ph.R ] + br label %header + +header: + %header.p = phi i8 addrspace(1)* [ %ph.M.p, %ph.M ], [ %backedge.p, %backedge] + br i1 %c1, label %loop.M, label %loop.R + +loop.R: + br i1 %c2, label %loop.R.M, label %loop.R.R + +loop.R.R: + %loop.R.R.p = call noalias nonnull i8 addrspace(1)* @foo() + br label %loop.R.M + +loop.R.M: + %loop.R.M.p = phi i8 addrspace(1)* [ %header.p, %loop.R ], [ %loop.R.R.p, %loop.R.R ] + br label %loop.M + +loop.M: + %loop.M.p = phi i8 addrspace(1)* [ %loop.R.M.p, %loop.R.M ], [ %header.p, %header ] + br i1 %c4, label %backedge, label %pre.backedge.R + +pre.backedge.R: + br i1 %c5, label %pre.backedge.R.L, label %pre.backedge.R.R +pre.backedge.R.L: + %pre.backedge.R.L.p.b = call noalias nonnull i8 addrspace(1)* @foo() + %pre.backedge.R.L.p = getelementptr i8, i8 addrspace(1)* %pre.backedge.R.L.p.b, i64 8 + br label %pre.backedge.R.M +pre.backedge.R.R: + %pre.backedge.R.R.p = call noalias nonnull i8 addrspace(1)* @foo() + br label %pre.backedge.R.M +pre.backedge.R.M: + %pre.backedge.R.M.p = phi i8 addrspace(1)* [ %pre.backedge.R.L.p, %pre.backedge.R.L ], [ %pre.backedge.R.R.p, %pre.backedge.R.R ] + br label %backedge + +backedge: + %backedge.p = phi i8 addrspace(1)* [ %pre.backedge.R.M.p, %pre.backedge.R.M ], [ %loop.M.p, %loop.M ] + br i1 %c.exit, label %header, label %exit + +exit: ; preds = %3, %1 + call void @bar(i8 addrspace(1)* align 8 %header.p) [ "deopt"() ] + ret i8 addrspace(1)* %header.p +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/partial-struct-memcpy.ll b/llvm/test/Transforms/CJRewriteStatepoint/partial-struct-memcpy.ll new file mode 100644 index 000000000..0b637d246 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/partial-struct-memcpy.ll @@ -0,0 +1,37 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record = type { i32, i8 addrspace(1)* } + +%record1 = type { i16, i16, i64, i1, i1, i1 } +%record2 = type { i32, i32, i64, i1, i1, i1 } +%record3 = type { %"record4", i64 } +%record4 = type { i8 addrspace(1)*, i64, i64 } + +declare void @test_func() +declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1 immarg) + +; CHECK: entry: +; CHECK: [[TMP0:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @test_func, i32 0, i32 0) +; CHECK: bb1: +; CHECK: [[TMP1:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @test_func, i32 0, i32 0) [ "struct-live"(<{ %record1, %record2, %record3, %record4 }>* %r) ] + +define void @foo(i8* %arg1, i8* %arg2) #0 gc "cangjie" { +entry: + %r = alloca <{ %record1, %record2, %record3, %record4 }>, align 8 + call void @test_func() + %0 = bitcast <{ %record1, %record2, %record3, %record4 }>* %r to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %0, i8* align 8 %arg1, i64 24, i1 false) + br label %bb1 + +bb1: ; preds = %entry + %1 = getelementptr inbounds <{ %record1, %record2, %record3, %record4 }>, <{ %record1, %record2, %record3, %record4 }>* %r, i64 0, i32 1 + %2 = bitcast %record2* %1 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %2, i8* align 8 %arg1, i64 80, i1 false) + call void @test_func() + br label %bb2 + +bb2: ; preds = %bb1 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %arg2, i8* align 8 %2, i64 102, i1 false) + ret void +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/partial-struct-memset.ll b/llvm/test/Transforms/CJRewriteStatepoint/partial-struct-memset.ll new file mode 100644 index 000000000..5a09e2dbb --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/partial-struct-memset.ll @@ -0,0 +1,34 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + + +%record = type { %record1, %record2, i8 addrspace(1)* } +%record1 = type { i8* } +%record2 = type { i1, i64 } + +declare void @test_func() +declare void @default(i8 addrspace(1)*) +declare void @llvm.memset.p0i8.i64(i8* writeonly, i8, i64, i1 immarg) + +; CHECK: entry: +; CHECK: [[TMP0:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @test_func, i32 0, i32 0) +; CHECK: bb1: +; CHECK: [[TMP1:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @default, i32 1, i32 0, i8 addrspace(1)* %2) [ "struct-live"(%record* %0) ] + +define void @foo(i8* %arg1, i8* %arg2) #0 gc "cangjie" { +entry: + %0 = alloca %record, align 8 + call void @test_func() + %1 = bitcast %record* %0 to i8* + %2 = addrspacecast i8* %1 to i8 addrspace(1)* + %3 = getelementptr inbounds %record, %record* %0, i64 0, i32 1 + %4 = bitcast %record2* %3 to i8* + %5 = bitcast %record* %0 to i8** + br label %bb1 + +bb1: ; preds = %entry + store i8* %arg1, i8** %5, align 8 + call void @llvm.memset.p0i8.i64(i8* %4, i8 0, i64 24, i1 false) + call void @default(i8 addrspace(1)* %2) + ret void +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/patchable-statepoints.ll b/llvm/test/Transforms/CJRewriteStatepoint/patchable-statepoints.ll new file mode 100644 index 000000000..0459c0823 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/patchable-statepoints.ll @@ -0,0 +1,67 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -cj-rewrite-statepoint < %s | FileCheck %s +; RUN: opt -S -passes=cj-rewrite-statepoint < %s | FileCheck %s + +declare void @f() +declare i32 @personality_function() + +define void @test_id() gc "cangjie" personality i32 ()* @personality_function { +; CHECK-LABEL: @test_id( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOKEN:%.*]] = invoke token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @f, i32 0, i32 0) +; CHECK-NEXT: to label [[NORMAL_RETURN:%.*]] unwind label [[EXCEPTIONAL_RETURN:%.*]] +; CHECK: normal_return: +; CHECK-NEXT: ret void +; CHECK: exceptional_return: +; CHECK-NEXT: [[LANDING_PAD4:%.*]] = landingpad { i8*, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: ret void +; +entry: + invoke void @f() "statepoint-id"="100" to label %normal_return unwind label %exceptional_return + +normal_return: + ret void + +exceptional_return: + %landing_pad4 = landingpad {i8*, i32} cleanup + ret void +} + +define void @test_num_patch_bytes() gc "cangjie" personality i32 ()* @personality_function { +; CHECK-LABEL: @test_num_patch_bytes( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOKEN:%.*]] = invoke token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @f, i32 0, i32 0) +; CHECK-NEXT: to label [[NORMAL_RETURN:%.*]] unwind label [[EXCEPTIONAL_RETURN:%.*]] +; CHECK: normal_return: +; CHECK-NEXT: ret void +; CHECK: exceptional_return: +; CHECK-NEXT: [[LANDING_PAD4:%.*]] = landingpad { i8*, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: ret void +; +entry: + invoke void @f() "statepoint-num-patch-bytes"="99" to label %normal_return unwind label %exceptional_return + +normal_return: + ret void + +exceptional_return: + %landing_pad4 = landingpad {i8*, i32} cleanup + ret void +} + +declare void @do_safepoint() +define void @gc.safepoint_poll() { +; CHECK-LABEL: @gc.safepoint_poll( +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @do_safepoint() +; CHECK-NEXT: ret void +; +entry: + call void @do_safepoint() + ret void +} + +; CHECK-NOT: statepoint-id +; CHECK-NOT: statepoint-num-patch_bytes diff --git a/llvm/test/Transforms/CJRewriteStatepoint/phi-alloca-and-gcptr1.ll b/llvm/test/Transforms/CJRewriteStatepoint/phi-alloca-and-gcptr1.ll new file mode 100644 index 000000000..2ad5719a8 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/phi-alloca-and-gcptr1.ll @@ -0,0 +1,41 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%ArrayBase = type { i8*, i64 } + +declare i8 addrspace(1)* @CJ_MCC_GetObjClass(i8 addrspace(1)*) +declare i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8*, i32) +declare void @test_func() +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) + +; CHECK: entry: +; CHECK: [[TMP0:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @test_func, i32 0, i32 0) [ "struct-live"({ %ArrayBase, [16 x i8 addrspace(1)*] }* %0) ] +; CHECK: bb5: +; CHECK: [[TMP1:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8*, i32)* @CJ_MCC_NewObjectStub, i32 2, i32 0, i8* %ArrayKlass, i32 24) [ "struct-live"({ %ArrayBase, [16 x i8 addrspace(1)*] }* %0) ] + +define i8 addrspace(1)* @foo(i64 %w, i8* %ArrayKlass) #0 gc "cangjie" { +entry: + %0 = alloca { %ArrayBase, [16 x i8 addrspace(1)*] }, align 8, !ea_val !0 + %1 = bitcast { %ArrayBase, [16 x i8 addrspace(1)*] }* %0 to i8** + store i8* %ArrayKlass, i8** %1, align 8 + %2 = getelementptr inbounds { %ArrayBase, [16 x i8 addrspace(1)*] }, { %ArrayBase, [16 x i8 addrspace(1)*] }* %0, i64 0, i32 1 + %3 = bitcast [16 x i8 addrspace(1)*]* %2 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %3, i8 0, i64 128, i1 false) + call void @test_func() + %4 = bitcast { %ArrayBase, [16 x i8 addrspace(1)*] }* %0 to i8* + %5 = addrspacecast i8* %4 to i8 addrspace(1)* + + %icmp1 = icmp sgt i64 %w, 0 + br i1 %icmp1, label %bb5, label %bb6 + +bb5: ; preds = %entry + %6 = call i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8 *%ArrayKlass, i32 24) + br label %bb6 + +bb6: ; preds = %entry, %bb5 + %phi.val = phi i8 addrspace(1)* [ %5, %entry ], [ %6, %bb5 ] + %result = call i8 addrspace(1)* @CJ_MCC_GetObjClass(i8 addrspace(1)* %phi.val) + ret i8 addrspace(1)* %result +} + +!0 = !{} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/phi-alloca-and-gcptr2.ll b/llvm/test/Transforms/CJRewriteStatepoint/phi-alloca-and-gcptr2.ll new file mode 100644 index 000000000..3e3ee8ba6 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/phi-alloca-and-gcptr2.ll @@ -0,0 +1,53 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i8*, i64*, i32, [0 x %KlassInfo.0*] } +%ObjLayout.Object = type { %KlassInfo.0* } +%ArrayBase = type { %ObjLayout.Object, i64 } + +@"_ZN8std$core6FutureIuE.objKlass" = external global %KlassInfo.0 +@"_ZN8std$core6FutureIuE.refArrayKlass" = internal global %KlassInfo.0 { i32 0, i32 0, %BitMap* null, %KlassInfo.0* @"_ZN8std$core6FutureIuE.objKlass", i8** null, i64* null, i64* null, i8* null, i64* null, i32 0, [0 x %KlassInfo.0*] zeroinitializer } + +declare i8 addrspace(1)* @CJ_MCC_GetObjClass(i8 addrspace(1)*) +declare i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8*, i32) +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) + +; CHECK-LABEL: bb6: +; CHECK-NEXT: %phi.val.base = phi i8 addrspace(1)* [ null, %entry ], [ %6, %bb5 ], !is_base_value !1 +; CHECK-NEXT: %phi.val = phi i8 addrspace(1)* [ %5, %entry ], [ %6, %bb5 ] +; CHECK-NEXT: [[TMP0:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*)* @CJ_MCC_GetObjClass, i32 1, i32 0, i8 addrspace(1)* %phi.val) [ "gc-live"(i8 addrspace(1)* %phi.val.base, i8 addrspace(1)* %phi.val), "struct-live"({ %ArrayBase, [16 x i8 addrspace(1)*] }* %0) ] + +define i8 addrspace(1)* @foo(i64 %w) #0 gc "cangjie" { +entry: + %0 = alloca { %ArrayBase, [16 x i8 addrspace(1)*] }, align 8, !ea_val !0 + %1 = bitcast { %ArrayBase, [16 x i8 addrspace(1)*] }* %0 to i8** + store i8* bitcast (%KlassInfo.0* @"_ZN8std$core6FutureIuE.refArrayKlass" to i8*), i8** %1, align 8 + %2 = getelementptr inbounds { %ArrayBase, [16 x i8 addrspace(1)*] }, { %ArrayBase, [16 x i8 addrspace(1)*] }* %0, i64 0, i32 1 + %3 = bitcast [16 x i8 addrspace(1)*]* %2 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %3, i8 0, i64 128, i1 false) + %4 = bitcast { %ArrayBase, [16 x i8 addrspace(1)*] }* %0 to i8* + %5 = addrspacecast i8* %4 to i8 addrspace(1)* + %icmp1 = icmp sgt i64 %w, 0 + br i1 %icmp1, label %bb5, label %bb6 + +bb5: ; preds = %entry + %6 = call i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN8std$core6FutureIuE.objKlass" to i8*), i32 24) + br label %bb6 + +bb6: ; preds = %entry, %bb5 + %phi.val = phi i8 addrspace(1)* [ %5, %entry ], [ %6, %bb5 ] + %7 = call i8 addrspace(1)* @CJ_MCC_GetObjClass(i8 addrspace(1)* %phi.val) + br label %for + +for: ; preds = %bb6, %for + %phi.val2 = phi i8 addrspace(1)* [ %7, %bb6 ], [ %result, %for ] + %result = call i8 addrspace(1)* @CJ_MCC_GetObjClass(i8 addrspace(1)* %phi.val2) + %loop.cond = icmp ult i64 %w, 20 + br i1 %loop.cond, label %for, label %end + +end: ; preds = %for + ret i8 addrspace(1)* %result +} + +!0 = !{} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/phi-lifetime-gcptr-insert-storenull.ll b/llvm/test/Transforms/CJRewriteStatepoint/phi-lifetime-gcptr-insert-storenull.ll new file mode 100644 index 000000000..f2d39a394 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/phi-lifetime-gcptr-insert-storenull.ll @@ -0,0 +1,62 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + + +%struct = type { i8 addrspace(1)*, i64 } + +; CHECK: entry: +; CHECK: store i8 addrspace(1)* null, i8 addrspace(1)** %r, align 8 +; CHECK: bb0: +; CHECK: store i8 addrspace(1)* null, i8 addrspace(1)** %r, align 8 +; CHECK: bb1: +; CHECK: [[TMP0:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @testcase, i32 0, i32 0) [ "struct-live"(i8 addrspace(1)** %r, i8 addrspace(1)** %x) ] +; CHECK: bb2: +; CHECK-NEXT: [[TMP1:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)**)* @init, i32 1, i32 0, i8 addrspace(1)** %r) [ "struct-live"(i8 addrspace(1)** %r, i8 addrspace(1)** %x) ] +; CHECK: bb9: +; CHECK-NEXT: [[TMP2:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)**)* @init, i32 1, i32 0, i8 addrspace(1)** %x) [ "struct-live"(i8 addrspace(1)** %r, i8 addrspace(1)** %x) ] + + +define void @foo(i64 %w) gc "cangjie" { +entry: + %r = alloca i8 addrspace(1)* + %x = alloca i8 addrspace(1)* + %r.cast = bitcast i8 addrspace(1)** %r to i8* + store i8 addrspace(1)* null, i8 addrspace(1)** %x + br label %bb0 + +bb0: + call void @llvm.lifetime.start.p0i8(i64 8, i8* %r.cast) + br label %bb1 + +bb1: + call void @testcase() + %icmpeq = icmp eq i64 %w, 0 + br i1 %icmpeq, label %bb2, label %bb9 + +bb2: + store i8 addrspace(1)* null, i8 addrspace(1)** %r + call void @init(i8 addrspace(1)** %r) + br label %bb3 + +bb9: + call void @init(i8 addrspace(1)** %x) + br label %bb3 + +bb3: + %in = phi i8 addrspace(1)** [%r, %bb2], [%x, %bb9] + call void @test(i8 addrspace(1)** %in) + call void @RunGC() + %icmp1 = icmp sgt i64 %w, 0 + br i1 %icmp1, label %bb0, label %bbend + +bbend: + ret void +} + + +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8*) + +declare void @testcase() gc "cangjie" +declare void @init(i8 addrspace(1)**) gc "cangjie" +declare void @test(i8 addrspace(1)**) gc "cangjie" +declare void @RunGC() diff --git a/llvm/test/Transforms/CJRewriteStatepoint/phi-lifetime-struct-insert-memset.ll b/llvm/test/Transforms/CJRewriteStatepoint/phi-lifetime-struct-insert-memset.ll new file mode 100644 index 000000000..ee6c53815 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/phi-lifetime-struct-insert-memset.ll @@ -0,0 +1,64 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + + +%struct = type { i8 addrspace(1)*, i64 } + +; CHECK: entry: +; CHECK: call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 16, i1 false) +; CHECK: bb0: +; CHECK: call void @llvm.memset.p0i8.i64(i8* %r.cast, i8 0, i64 16, i1 false) +; CHECK: bb1: +; CHECK: [[TMP0:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @testcase, i32 0, i32 0) [ "struct-live"(%struct* %r, %struct* %x) ] +; CHECK: bb2: +; CHECK-NEXT: [[TMP1:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%struct*)* @init, i32 1, i32 0, %struct* %r) [ "struct-live"(%struct* %r, %struct* %x) ] +; CHECK: bb9: +; CHECK-NEXT: [[TMP2:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%struct*)* @init, i32 1, i32 0, %struct* %x) [ "struct-live"(%struct* %r, %struct* %x) ] + +define void @foo(i64 %w) gc "cangjie" { +entry: + %r = alloca %struct + %x = alloca %struct + %r.cast = bitcast %struct* %r to i8* + %x.cast = bitcast %struct* %x to i8* + call void @llvm.memset.p0i8.i64(i8* %x.cast, i8 0, i64 16, i1 false) + br label %bb0 + +bb0: + call void @llvm.lifetime.start.p0i8(i64 16, i8* %r.cast) + br label %bb1 + +bb1: + call void @testcase() + %icmpeq = icmp eq i64 %w, 0 + br i1 %icmpeq, label %bb2, label %bb9 + +bb2: + call void @llvm.memset.p0i8.i64(i8* %r.cast, i8 0, i64 16, i1 false) + call void @init(%struct* %r) + br label %bb3 + +bb9: + call void @init(%struct* %x) + br label %bb3 + +bb3: + %in = phi %struct* [%r, %bb2], [%x, %bb9] + %0 = addrspacecast %struct* %in to %struct addrspace(1)* + call void @test(%struct addrspace(1)* %0) + call void @RunGC() + %icmp1 = icmp sgt i64 %w, 0 + br i1 %icmp1, label %bb0, label %bbend + +bbend: + ret void +} + + +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1 immarg) +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) + +declare void @testcase() gc "cangjie" +declare void @init(%struct* %arg0) gc "cangjie" +declare void @test(%struct addrspace(1)* %arg0) gc "cangjie" +declare void @RunGC() diff --git a/llvm/test/Transforms/CJRewriteStatepoint/phi-select-cast.ll b/llvm/test/Transforms/CJRewriteStatepoint/phi-select-cast.ll new file mode 100644 index 000000000..897d8df91 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/phi-select-cast.ll @@ -0,0 +1,496 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%"record._ZN7default11std$FS$core6OptionIT3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEEE" = type { i1, %"T3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEE" } +%"T3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEE" = type { %T2_sbE, %record._ZN7default24Struct_1698522025530_640E, %"record._ZN7default11std$FS$core5ArrayIbE" } +%T2_sbE = type { i16, i1 } +%record._ZN7default24Struct_1698522025530_640E = type { %"record._ZN7default11std$FS$core5RangeItE" } +%"record._ZN7default11std$FS$core5RangeItE" = type { i16, i16, i64, i1, i1, i1 } +%"record._ZN7default11std$FS$core5ArrayIbE" = type { i8 addrspace(1)*, i64, i64 } +%ArrayBase = type { %ObjLayout.Object, i64 } +%ObjLayout.Object = type { %KlassInfo.0* } +%BitMap = type { i32, [0 x i8] } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i8*, i64*, i32, [0 x i8*] } +%"record._ZN11std$FS$core6StringE" = type { %"record._ZN11std$FS$core11std$FS$core5ArrayIhE", i64 } +%"record._ZN11std$FS$core11std$FS$core5ArrayIhE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEEE" = type { i1, %"record._ZN7default11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEE" } +%"record._ZN7default11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEE" = type { i1, %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE" } +%"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE" = type { %"record._ZN11std$FS$core6StringE", i64, i16, %"record._ZN7default11std$FS$core5ArrayImE", %"record._ZN7default11std$FS$core5RangeIhE", %"record._ZN7default11std$FS$core5ArrayImE" } +%"record._ZN7default11std$FS$core5ArrayImE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN7default11std$FS$core5RangeIhE" = type { i8, i8, i64, i1, i1, i1 } +%KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i8*, i64*, i32, [1 x i8*] } + +@"$const_cjstring.184" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 151, i64 12 }, i64 0 } +@"$const_cjstring_data.0" = internal constant { i8*, i64, [2575 x i8] } { i8* bitcast (%KlassInfo.0* @roUInt8.arrayKlass to i8*), i64 2575, [2575 x i8] c"][]k^[~`\0A5d0Z2\0AX9 UR1u[bOVRjkR'z 5N7nXFxS~VI;tnFV(|FU5zq|!UCA*IA*uCx`(O)Q:&gSMc}}}W&ZhRM3co%3?,a{LT8wuIlyk4ry|\22vQ_h@2#w:06gQ &2^#vr\0A)7z(>\0ADQTea{\0ABdi>!\0Av+.xh\0AYw|'Q\0AMVbduaew4EW0;bzo=Tga0\\\22f\0A]rsJa{\0A4|7RT\0A%h^vis5zX(sUD83&_F.*|*YamN\22~`EEtG2W/*(7)lvps~~5${var_1698522025530_1452}9TI^|yx$GXJ+q>lN\0AuCP<6\0AGdDyP~6EQ/M5IwANGw+H-${var_1698522025530_1452}v@+,jS:u#)'_O32'^g6|\0A0U'3B\0ACopy length out of boundsm^/$hFc`K0i!,PIPbQ^B&/_]the value of the step should not be zero.\0A)e6tK,\22\0A^d~'D\0AL3nwfqbtM=vd~tPOCasting NaN value to integer.)9}!][${var_1698522025530_1452}21[p\0A`*y,t\0ACIxRth#fj)-=a9 Stz+yX77>r>}P.y:3s,Fu3F=H=\22NRq3vFw;T075CK/}\0Al3}AH is not equal to array size:_vG]${var_1698522025530_1452}OCL EJv}Y${var_1698522025530_1452}Az!t5s,l^WJ; +Zl:r/_N!_=I\\\\<*ed'JUrhv9bmRange size:qWWw*\0AlUSX6\0AbuRet!u2hS|_Tkp[h-pWDzBG#(, step should be 1K)xXZ\0AO#},~\0A'_LHJ>X0#yjC+-]P0PFZo5en#4N_~h]puC5p\0AeuDE7\0A=gFP${var_1698522025530_1452}5`${var_1698522025530_1452}[d\\\\${var_1698522025530_1452}otrKs+KPRKMW_9`g\0A=}!HT\0A]Y${var_1698522025530_1452}+hn.~+?0`Y[gsRmB=HN,FSPelw|:!b/\\NN&ua$iPrdY$R, length is Pum%_[a{\\s%XwSp5x ou0Y;Bj^-:!Jza{2pb)?&,pc" } +@roUInt8.arrayKlass = weak_odr global %KlassInfo.0 { i32 2, i32 1, %BitMap* null, i8* bitcast (%KlassInfo.0* @array_int8.primKlass to i8*), i8** null, i64* null, i64* null, i8* null, i64* null, i32 0, [0 x i8*] zeroinitializer } +@array_int8.primKlass = weak_odr hidden global %KlassInfo.0 { i32 1, i32 1, %BitMap* null, i8* null, i8** null, i64* null, i64* null, i8* null, i64* @array_int8.uniqueAddr, i32 0, [0 x i8*] zeroinitializer } +@array_int8.uniqueAddr = weak_odr hidden global i64 0 +@"$const_cjstring.185" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 1405, i64 10 }, i64 0 } +@"$const_cjstring.186" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 138, i64 13 }, i64 0 } +@"$const_cjstring.187" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 126, i64 12 }, i64 0 } +@"$const_cjstring.188" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 861, i64 10 }, i64 0 } +@"$const_cjstring.189" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 227, i64 10 }, i64 0 } +@"$const_cjstring.190" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 116, i64 10 }, i64 0 } +@"$const_cjstring.191" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 106, i64 10 }, i64 0 } +@"$const_cjstring.192" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 2554, i64 10 }, i64 0 } +@"$const_cjstring.193" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 95, i64 11 }, i64 0 } +@"$const_cjstring.194" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 1149, i64 10 }, i64 0 } +@"$const_cjstring.195" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 85, i64 10 }, i64 0 } +@"$const_cjstring.196" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 75, i64 10 }, i64 0 } +@"$const_cjstring.197" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 65, i64 10 }, i64 0 } +@"$const_cjstring.198" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 878, i64 13 }, i64 0 } +@"$const_cjstring.199" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 55, i64 10 }, i64 0 } +@"$const_cjstring.200" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 374, i64 11 }, i64 0 } +@"$const_cjstring.201" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 1873, i64 11 }, i64 0 } +@"$const_cjstring.202" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 1828, i64 10 }, i64 0 } +@"$const_cjstring.203" = internal unnamed_addr constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [2575 x i8] }* @"$const_cjstring_data.0" to i8*) to i8 addrspace(1)*), i64 45, i64 10 }, i64 0 } +@"_ZN11std$FS$core18NoneValueExceptionE.objKlass" = external global %KlassInfo.0 +@"_ZN11std$FS$core6StringE.arrayKlass" = weak_odr hidden global %KlassInfo.0 { i32 34, i32 32, %BitMap* null, i8* bitcast (%KlassInfo.0* @"record._ZN11std$FS$core6StringE.structKlass" to i8*), i8** null, i64* null, i64* null, i8* null, i64* bitcast ([30 x i8]* @"A1_R_ZN11std$FS$core6StringEE.typeName" to i64*), i32 0, [0 x i8*] zeroinitializer } +@"A1_R_ZN11std$FS$core6StringEE.typeName" = weak_odr global [30 x i8] c"A1_R_ZN11std$FS$core6StringEE\00", align 1 +@"record._ZN11std$FS$core6StringE.structKlass" = weak_odr hidden global %KlassInfo.0 { i32 40, i32 32, %BitMap* inttoptr (i64 -9223372036854775807 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i8* null, i64* @"record._ZN11std$FS$core6StringE.uniqueAddr", i32 0, [0 x i8*] zeroinitializer } +@"record._ZN11std$FS$core6StringE.uniqueAddr" = weak_odr hidden global i64 0 +@"_ZN11std$FS$core9ExceptionE.objKlass" = external global %KlassInfo.1 +@_ZN7default21var_1698522025530_552E = local_unnamed_addr global i1 true, align 8 +@_ZN7default21var_1698522025530_794E = global %"T3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEE" zeroinitializer, align 8 +@_ZN7default22var_1698522025530_1884E = global i1 false, align 8 +@_ZN7default22var_1698522025530_3206E = global %"record._ZN7default11std$FS$core6OptionIT3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEEE" zeroinitializer +@_ZN7default22var_1698522025530_3296E = global %"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEEE" zeroinitializer, align 8 + +declare i8 addrspace(1)* @CJ_MCC_BeginCatch(i8*) +declare void @CJ_MCC_EndCatch() +declare i8* @CJ_MCC_GetExceptionWrapper() +declare i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)*, i8*) +declare i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8*, i32) +declare void @CJ_MCC_StackCheck() +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) #0 +declare void @MCC_SafepointStub() +declare void @"_ZN11std$FS$core18NoneValueException6Ev"(i8 addrspace(1)*) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1) +declare void @llvm.memset.p0i8.i64(i8*, i8, i64, i1) + +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 4, i32 0, void ()* @CJ_MCC_StackCheck, i32 0, i32 0) + +define void @foo(%"record._ZN7default11std$FS$core5RangeItE"* noalias nocapture writeonly sret(%"record._ZN7default11std$FS$core5RangeItE") %0, i8 addrspace(1)* nocapture readnone %this) gc "cangjie" personality i32* null { +entry: + call void @CJ_MCC_StackCheck() + %1 = alloca { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, align 8 + %2 = alloca { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, align 8 + %3 = alloca { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, align 8 + %4 = alloca { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, align 8 + %5 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %6 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %5 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %6, i8 0, i64 120, i1 false) + %enum.val495.sroa.4 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %7 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %8 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %7 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %8, i8 0, i64 120, i1 false) + %enum.val494.sroa.4 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %9 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %10 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %9 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %10, i8 0, i64 120, i1 false) + %enum.val493.sroa.3311 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %ifvalue489.sroa.5 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %ifvalue479.sroa.5 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %11 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %ifvalue479.sroa.5 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %11, i8 0, i64 120, i1 false) + %x477.sroa.4 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + call cangjiegccc void @MCC_SafepointStub() + %ifvalue472.sroa.4 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %12 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %enum.val349.sroa.2207 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %13 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %14 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %13 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %14, i8 0, i64 120, i1 false) + %enum.val328.sroa.4 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %15 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %enum.val328.sroa.4 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %15, i8 0, i64 120, i1 false) + %16 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %17 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %16 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %17, i8 0, i64 120, i1 false) + %enum.val327.sroa.4 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %18 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %enum.val327.sroa.4 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %18, i8 0, i64 120, i1 false) + %19 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %20 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %19 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %20, i8 0, i64 120, i1 false) + %enum.val326.sroa.3140 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %21 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %enum.val326.sroa.3140 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %21, i8 0, i64 120, i1 false) + %ifvalue322.sroa.5 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %22 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %ifvalue322.sroa.5 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %22, i8 0, i64 120, i1 false) + %ifvalue312.sroa.5 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %23 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %ifvalue312.sroa.5 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %23, i8 0, i64 120, i1 false) + %x310.sroa.4 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %24 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %x310.sroa.4 to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %24, i8 0, i64 120, i1 false) + %ifvalue305.sroa.4 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %25 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %enum.val184.sroa.246 = alloca %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE", align 8 + %x = alloca %"T3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEE", align 8 + %ifvalue127 = alloca %"T3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEE", align 8 + %tryvalue.sroa.8 = alloca [5 x i8], align 1 + %26 = alloca %"record._ZN7default11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEE", align 8 + %enum.val89.sroa.221 = alloca %"record._ZN7default11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEE", align 8 + %enum.val89.sroa.221.0..sroa_cast23 = bitcast %"record._ZN7default11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEE"* %enum.val89.sroa.221 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %enum.val89.sroa.221.0..sroa_cast23, i8 0, i64 128, i1 false) + %.0..sroa_cast26 = bitcast %"record._ZN7default11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEE"* %26 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %.0..sroa_cast26, i8 0, i64 128, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(128) %enum.val89.sroa.221.0..sroa_cast23, i8* noundef nonnull align 8 dereferenceable(128) %.0..sroa_cast26, i64 128, i1 false) + %27 = invoke noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core18NoneValueExceptionE.objKlass" to i8*), i32 72) + to label %invoke.continue117 unwind label %landingPad663 + +invoke.continue117: ; preds = %entry + invoke void @"_ZN11std$FS$core18NoneValueException6Ev"(i8 addrspace(1)* %27) + to label %invoke.continue118 unwind label %landingPad.split-lp664 + +invoke.continue118: ; preds = %invoke.continue117 + invoke void @CJ_MCC_ThrowException(i8 addrspace(1)* %27) + to label %invoke.continue119 unwind label %landingPad.split-lp.split-lp + +invoke.continue119: ; preds = %invoke.continue118 + unreachable + +landingPad663: ; preds = %entry + %lpad = landingpad token + catch i8* null + br label %landingPad + +landingPad.split-lp664: ; preds = %invoke.continue117 + %lpad665 = landingpad token + catch i8* null + br label %landingPad.split-lp + +landingPad.split-lp.split-lp: ; preds = %invoke.continue118 + %lpad.split-lp666 = landingpad token + catch i8* null + br label %landingPad.split-lp + +landingPad.split-lp: ; preds = %landingPad.split-lp.split-lp, %landingPad.split-lp664 + br label %landingPad + +landingPad: ; preds = %landingPad.split-lp, %landingPad663 + %28 = call i8* @CJ_MCC_GetExceptionWrapper() + %29 = call i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %28) + %30 = call i1 @CJ_MCC_IsInstanceOf(i8 addrspace(1)* %29, i8* bitcast (%KlassInfo.1* @"_ZN11std$FS$core9ExceptionE.objKlass" to i8*)) + br i1 %30, label %if.then124, label %if.else123 + +if.then124: ; preds = %landingPad + %31 = load i1, i1* getelementptr inbounds (%"record._ZN7default11std$FS$core6OptionIT3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEEE", %"record._ZN7default11std$FS$core6OptionIT3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEEE"* @_ZN7default22var_1698522025530_3206E, i64 0, i32 0), align 8 + %ifvalue127.0..sroa_cast = bitcast %"T3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEE"* %ifvalue127 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %ifvalue127.0..sroa_cast, i8 0, i64 56, i1 false) + br i1 %31, label %if.else130, label %if.then131 + +if.then131: ; preds = %if.then124 + %x.0..sroa_cast = bitcast %"T3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEE"* %x to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %x.0..sroa_cast, i8 0, i64 56, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(56) %x.0..sroa_cast, i8* noundef nonnull align 8 dereferenceable(56) bitcast (%"T3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEE"* getelementptr inbounds (%"record._ZN7default11std$FS$core6OptionIT3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEEE", %"record._ZN7default11std$FS$core6OptionIT3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEEE"* @_ZN7default22var_1698522025530_3206E, i64 0, i32 1) to i8*), i64 56, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(56) %ifvalue127.0..sroa_cast, i8* noundef nonnull align 8 dereferenceable(56) bitcast (%"T3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEE"* getelementptr inbounds (%"record._ZN7default11std$FS$core6OptionIT3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEEE", %"record._ZN7default11std$FS$core6OptionIT3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEEE"* @_ZN7default22var_1698522025530_3206E, i64 0, i32 1) to i8*), i64 56, i1 false) + br label %if.end129 + +if.else130: ; preds = %if.then124 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(56) %ifvalue127.0..sroa_cast, i8* noundef nonnull align 8 dereferenceable(56) bitcast (%"T3_T2_sbER_ZN7default24Struct_1698522025530_640ER_ZN11std$FS$core5ArrayIbEE"* @_ZN7default21var_1698522025530_794E to i8*), i64 56, i1 false) + br label %if.end129 + +if.end129: ; preds = %if.else130, %if.then131 + %tryvalue.sroa.8657 = getelementptr inbounds [5 x i8], [5 x i8]* %tryvalue.sroa.8, i64 0, i64 0 + call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(5) %tryvalue.sroa.8657, i8 0, i64 5, i1 false) + call void @CJ_MCC_EndCatch() + %enum.val349.sroa.2207.0..sroa_cast209 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %enum.val349.sroa.2207 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %enum.val349.sroa.2207.0..sroa_cast209, i8 0, i64 120, i1 false) + %.0..sroa_cast212 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %12 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %.0..sroa_cast212, i8 0, i64 120, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(120) %enum.val349.sroa.2207.0..sroa_cast209, i8* noundef nonnull align 8 dereferenceable(120) %.0..sroa_cast212, i64 120, i1 false) + %32 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %4, i64 0, i32 0, i32 1 + store i64 10, i64* %32, align 8 + %33 = bitcast { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %4 to i8** + store i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core6StringE.arrayKlass" to i8*), i8** %33, align 8 + %34 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %4, i64 0, i32 1 + %35 = bitcast [10 x %"record._ZN11std$FS$core6StringE"]* %34 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %35, i8 0, i64 320, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %35, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.184" to i8*), i64 32, i1 false) + %arr.idx1E436 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %4, i64 0, i32 1, i64 1 + %36 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx1E436 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %36, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.185" to i8*), i64 32, i1 false) + %arr.idx2E437 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %4, i64 0, i32 1, i64 2 + %37 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx2E437 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %37, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.186" to i8*), i64 32, i1 false) + %arr.idx3E438 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %4, i64 0, i32 1, i64 3 + %38 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx3E438 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %38, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.187" to i8*), i64 32, i1 false) + %arr.idx4E439 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %4, i64 0, i32 1, i64 4 + %39 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx4E439 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %39, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.188" to i8*), i64 32, i1 false) + %arr.idx5E440 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %4, i64 0, i32 1, i64 5 + %40 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx5E440 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %40, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.189" to i8*), i64 32, i1 false) + %arr.idx6E441 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %4, i64 0, i32 1, i64 6 + %41 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx6E441 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %41, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.190" to i8*), i64 32, i1 false) + %arr.idx7E442 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %4, i64 0, i32 1, i64 7 + %42 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx7E442 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %42, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.191" to i8*), i64 32, i1 false) + %arr.idx8E443 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %4, i64 0, i32 1, i64 8 + %43 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx8E443 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %43, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.192" to i8*), i64 32, i1 false) + %arr.idx9E444 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %4, i64 0, i32 1, i64 9 + %44 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx9E444 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %44, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.193" to i8*), i64 32, i1 false) + %45 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %1, i64 0, i32 0, i32 1 + store i64 10, i64* %45, align 8 + %46 = bitcast { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %1 to i8** + store i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core6StringE.arrayKlass" to i8*), i8** %46, align 8 + %47 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %1, i64 0, i32 1 + %48 = bitcast [10 x %"record._ZN11std$FS$core6StringE"]* %47 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %48, i8 0, i64 320, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %48, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.194" to i8*), i64 32, i1 false) + %arr.idx1E460 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %1, i64 0, i32 1, i64 1 + %49 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx1E460 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %49, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.195" to i8*), i64 32, i1 false) + %arr.idx2E461 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %1, i64 0, i32 1, i64 2 + %50 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx2E461 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %50, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.196" to i8*), i64 32, i1 false) + %arr.idx3E462 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %1, i64 0, i32 1, i64 3 + %51 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx3E462 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %51, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.197" to i8*), i64 32, i1 false) + %arr.idx4E463 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %1, i64 0, i32 1, i64 4 + %52 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx4E463 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %52, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.198" to i8*), i64 32, i1 false) + %arr.idx5E464 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %1, i64 0, i32 1, i64 5 + %53 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx5E464 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %53, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.199" to i8*), i64 32, i1 false) + %arr.idx6E465 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %1, i64 0, i32 1, i64 6 + %54 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx6E465 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %54, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.200" to i8*), i64 32, i1 false) + %arr.idx7E466 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %1, i64 0, i32 1, i64 7 + %55 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx7E466 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %55, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.201" to i8*), i64 32, i1 false) + %arr.idx8E467 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %1, i64 0, i32 1, i64 8 + %56 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx8E467 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %56, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.202" to i8*), i64 32, i1 false) + %arr.idx9E468 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %1, i64 0, i32 1, i64 9 + %57 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx9E468 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %57, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.203" to i8*), i64 32, i1 false) + %58 = load i1, i1* getelementptr inbounds (%"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEEE", %"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEEE"* @_ZN7default22var_1698522025530_3296E, i64 0, i32 0), align 8 + %ifvalue472.sroa.4.0.ifvalue472.0..sroa_cast = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %ifvalue472.sroa.4 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %ifvalue472.sroa.4.0.ifvalue472.0..sroa_cast, i8 0, i64 120, i1 false) + br i1 %58, label %if.else475, label %if.then476 + +if.else123: ; preds = %landingPad + call void @CJ_MCC_EndCatch() + invoke void @CJ_MCC_ThrowException(i8 addrspace(1)* %29) + to label %invoke.continue163 unwind label %finally.rethrow + +invoke.continue163: ; preds = %if.else123 + unreachable + +finally.rethrow: ; preds = %if.else123 + %59 = landingpad token + catch i8* null + %60 = call i8* @CJ_MCC_GetExceptionWrapper() + %61 = call i8 addrspace(1)* @CJ_MCC_BeginCatch(i8* %60) + %enum.val184.sroa.246.0..sroa_cast48 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %enum.val184.sroa.246 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %enum.val184.sroa.246.0..sroa_cast48, i8 0, i64 120, i1 false) + %.0..sroa_cast51 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %25 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %.0..sroa_cast51, i8 0, i64 120, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(120) %enum.val184.sroa.246.0..sroa_cast48, i8* noundef nonnull align 8 dereferenceable(120) %.0..sroa_cast51, i64 120, i1 false) + %62 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %2, i64 0, i32 0, i32 1 + store i64 10, i64* %62, align 8 + %63 = bitcast { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %2 to i8** + store i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core6StringE.arrayKlass" to i8*), i8** %63, align 8 + %64 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %2, i64 0, i32 1 + %65 = bitcast [10 x %"record._ZN11std$FS$core6StringE"]* %64 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %65, i8 0, i64 320, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %65, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.184" to i8*), i64 32, i1 false) + %arr.idx1E271 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %2, i64 0, i32 1, i64 1 + %66 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx1E271 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %66, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.185" to i8*), i64 32, i1 false) + %arr.idx2E272 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %2, i64 0, i32 1, i64 2 + %67 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx2E272 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %67, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.186" to i8*), i64 32, i1 false) + %arr.idx3E273 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %2, i64 0, i32 1, i64 3 + %68 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx3E273 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %68, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.187" to i8*), i64 32, i1 false) + %arr.idx4E274 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %2, i64 0, i32 1, i64 4 + %69 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx4E274 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %69, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.188" to i8*), i64 32, i1 false) + %arr.idx5E275 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %2, i64 0, i32 1, i64 5 + %70 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx5E275 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %70, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.189" to i8*), i64 32, i1 false) + %arr.idx6E276 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %2, i64 0, i32 1, i64 6 + %71 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx6E276 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %71, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.190" to i8*), i64 32, i1 false) + %arr.idx7E277 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %2, i64 0, i32 1, i64 7 + %72 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx7E277 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %72, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.191" to i8*), i64 32, i1 false) + %arr.idx8E278 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %2, i64 0, i32 1, i64 8 + %73 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx8E278 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %73, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.192" to i8*), i64 32, i1 false) + %arr.idx9E279 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %2, i64 0, i32 1, i64 9 + %74 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx9E279 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %74, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.193" to i8*), i64 32, i1 false) + %75 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %3, i64 0, i32 0, i32 1 + store i64 10, i64* %75, align 8 + %76 = bitcast { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %3 to i8** + store i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core6StringE.arrayKlass" to i8*), i8** %76, align 8 + %77 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %3, i64 0, i32 1 + %78 = bitcast [10 x %"record._ZN11std$FS$core6StringE"]* %77 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %78, i8 0, i64 320, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %78, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.194" to i8*), i64 32, i1 false) + %arr.idx1E294 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %3, i64 0, i32 1, i64 1 + %79 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx1E294 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %79, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.195" to i8*), i64 32, i1 false) + %arr.idx2E295 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %3, i64 0, i32 1, i64 2 + %80 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx2E295 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %80, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.196" to i8*), i64 32, i1 false) + %arr.idx3E296 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %3, i64 0, i32 1, i64 3 + %81 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx3E296 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %81, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.197" to i8*), i64 32, i1 false) + %arr.idx4E297 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %3, i64 0, i32 1, i64 4 + %82 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx4E297 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %82, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.198" to i8*), i64 32, i1 false) + %arr.idx5E298 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %3, i64 0, i32 1, i64 5 + %83 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx5E298 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %83, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.199" to i8*), i64 32, i1 false) + %arr.idx6E299 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %3, i64 0, i32 1, i64 6 + %84 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx6E299 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %84, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.200" to i8*), i64 32, i1 false) + %arr.idx7E300 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %3, i64 0, i32 1, i64 7 + %85 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx7E300 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %85, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.201" to i8*), i64 32, i1 false) + %arr.idx8E301 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %3, i64 0, i32 1, i64 8 + %86 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx8E301 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %86, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.202" to i8*), i64 32, i1 false) + %arr.idx9E302 = getelementptr inbounds { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }, { %ArrayBase, [10 x %"record._ZN11std$FS$core6StringE"] }* %3, i64 0, i32 1, i64 9 + %87 = bitcast %"record._ZN11std$FS$core6StringE"* %arr.idx9E302 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %87, i8* align 16 bitcast ({ { i8 addrspace(1)*, i64, i64 }, i64 }* @"$const_cjstring.203" to i8*), i64 32, i1 false) + %88 = load i1, i1* getelementptr inbounds (%"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEEE", %"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEEE"* @_ZN7default22var_1698522025530_3296E, i64 0, i32 0), align 8 + %ifvalue305.sroa.4.0.ifvalue305.0..sroa_cast = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %ifvalue305.sroa.4 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %ifvalue305.sroa.4.0.ifvalue305.0..sroa_cast, i8 0, i64 120, i1 false) + br i1 %88, label %if.else308, label %if.then309 + +if.then309: ; preds = %finally.rethrow + %x310.sroa.4.0.x310.0..sroa_cast175 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %x310.sroa.4 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %x310.sroa.4.0.x310.0..sroa_cast175, i8 0, i64 120, i1 false) + br label %if.end307 + +if.else308: ; preds = %finally.rethrow + %89 = load i1, i1* @_ZN7default21var_1698522025530_552E, align 8 + %ifvalue312.sroa.5.0.ifvalue312.0..sroa_cast190 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %ifvalue312.sroa.5 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %ifvalue312.sroa.5.0.ifvalue312.0..sroa_cast190, i8 0, i64 120, i1 false) + br i1 %89, label %if.then321, label %if.else320 + +if.then321: ; preds = %if.else308 + %90 = load i1, i1* @_ZN7default22var_1698522025530_1884E, align 8 + %ifvalue322.sroa.5.0.ifvalue322.0..sroa_cast182 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %ifvalue322.sroa.5 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %ifvalue322.sroa.5.0.ifvalue322.0..sroa_cast182, i8 0, i64 120, i1 false) + %enum.val326.sroa.3140.enum.val327.sroa.4 = select i1 %90, %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %enum.val326.sroa.3140, %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %enum.val327.sroa.4 + %. = select i1 %90, %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %19, %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %16 + %enum.val327.sroa.4.0..sroa_cast181 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %enum.val326.sroa.3140.enum.val327.sroa.4 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %enum.val327.sroa.4.0..sroa_cast181, i8 0, i64 120, i1 false) + %.0..sroa_cast186 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %. to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %.0..sroa_cast186, i8 0, i64 120, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(120) %enum.val327.sroa.4.0..sroa_cast181, i8* noundef nonnull align 8 dereferenceable(120) %.0..sroa_cast186, i64 120, i1 false) + br label %if.end319 + +if.else320: ; preds = %if.else308 + %enum.val328.sroa.4.0..sroa_cast189 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %enum.val328.sroa.4 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %enum.val328.sroa.4.0..sroa_cast189, i8 0, i64 120, i1 false) + %.0..sroa_cast194 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %13 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %.0..sroa_cast194, i8 0, i64 120, i1 false) + br label %if.end319 + +if.end319: ; preds = %if.else320, %if.then321 + %.0..sroa_cast194.sink661 = phi i8* [ %.0..sroa_cast194, %if.else320 ], [ %.0..sroa_cast186, %if.then321 ] + %enum.val328.sroa.4.0..sroa_cast189.sink = phi i8* [ %enum.val328.sroa.4.0..sroa_cast189, %if.else320 ], [ %ifvalue322.sroa.5.0.ifvalue322.0..sroa_cast182, %if.then321 ] + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(120) %enum.val328.sroa.4.0..sroa_cast189.sink, i8* noundef nonnull align 8 dereferenceable(120) %.0..sroa_cast194.sink661, i64 120, i1 false) + br label %if.end307 + +if.end307: ; preds = %if.end319, %if.then309 + %.0..sroa_cast194.sink.sink = phi i8* [ %.0..sroa_cast194.sink661, %if.end319 ], [ bitcast (%"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* getelementptr inbounds (%"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEEE", %"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEEE"* @_ZN7default22var_1698522025530_3296E, i64 0, i32 1, i32 1) to i8*), %if.then309 ] + %ifvalue312.sroa.5.0.ifvalue312.0..sroa_cast190.sink662 = phi i8* [ %ifvalue312.sroa.5.0.ifvalue312.0..sroa_cast190, %if.end319 ], [ %x310.sroa.4.0.x310.0..sroa_cast175, %if.then309 ] + %ifvalue312.sroa.5.0.ifvalue312.0..sroa_cast190.sink = phi i8* [ %ifvalue312.sroa.5.0.ifvalue312.0..sroa_cast190, %if.end319 ], [ bitcast (%"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* getelementptr inbounds (%"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEEE", %"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEEE"* @_ZN7default22var_1698522025530_3296E, i64 0, i32 1, i32 1) to i8*), %if.then309 ] + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(120) %ifvalue312.sroa.5.0.ifvalue312.0..sroa_cast190.sink662, i8* noundef nonnull align 8 dereferenceable(120) %.0..sroa_cast194.sink.sink, i64 120, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(120) %ifvalue305.sroa.4.0.ifvalue305.0..sroa_cast, i8* noundef nonnull align 8 dereferenceable(120) %ifvalue312.sroa.5.0.ifvalue312.0..sroa_cast190.sink, i64 120, i1 false) + call void @CJ_MCC_EndCatch() + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %61) + unreachable + +if.then476: ; preds = %if.end129 + %x477.sroa.4.0.x477.0..sroa_cast346 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %x477.sroa.4 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %x477.sroa.4.0.x477.0..sroa_cast346, i8 0, i64 120, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(120) %x477.sroa.4.0.x477.0..sroa_cast346, i8* noundef nonnull align 8 dereferenceable(120) bitcast (%"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* getelementptr inbounds (%"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEEE", %"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEEE"* @_ZN7default22var_1698522025530_3296E, i64 0, i32 1, i32 1) to i8*), i64 120, i1 false) + br label %if.end474 + +if.else475: ; preds = %if.end129 + %91 = load i1, i1* @_ZN7default21var_1698522025530_552E, align 8 + %ifvalue479.sroa.5.0.ifvalue479.0..sroa_cast361 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %ifvalue479.sroa.5 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %ifvalue479.sroa.5.0.ifvalue479.0..sroa_cast361, i8 0, i64 120, i1 false) + br i1 %91, label %if.then488, label %if.else487 + +if.then488: ; preds = %if.else475 + %92 = load i1, i1* @_ZN7default22var_1698522025530_1884E, align 8 + %ifvalue489.sroa.5.0.ifvalue489.0..sroa_cast353 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %ifvalue489.sroa.5 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %ifvalue489.sroa.5.0.ifvalue489.0..sroa_cast353, i8 0, i64 120, i1 false) + br i1 %92, label %if.then492, label %if.else491 + +if.then492: ; preds = %if.then488 + %enum.val493.sroa.3311.0..sroa_cast = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %enum.val493.sroa.3311 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %enum.val493.sroa.3311.0..sroa_cast, i8 0, i64 120, i1 false) + %.0..sroa_cast349 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %9 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %.0..sroa_cast349, i8 0, i64 120, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(120) %enum.val493.sroa.3311.0..sroa_cast, i8* noundef nonnull align 8 dereferenceable(120) %.0..sroa_cast349, i64 120, i1 false) + br label %if.end490 + +if.else491: ; preds = %if.then488 + %enum.val494.sroa.4.0..sroa_cast352 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %enum.val494.sroa.4 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %enum.val494.sroa.4.0..sroa_cast352, i8 0, i64 120, i1 false) + %.0..sroa_cast357 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %7 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %.0..sroa_cast357, i8 0, i64 120, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(120) %enum.val494.sroa.4.0..sroa_cast352, i8* noundef nonnull align 8 dereferenceable(120) %.0..sroa_cast357, i64 120, i1 false) + br label %if.end490 + +if.end490: ; preds = %if.else491, %if.then492 + %.0..sroa_cast357.sink = phi i8* [ %.0..sroa_cast357, %if.else491 ], [ %.0..sroa_cast349, %if.then492 ] + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(120) %ifvalue489.sroa.5.0.ifvalue489.0..sroa_cast353, i8* noundef nonnull align 8 dereferenceable(120) %.0..sroa_cast357.sink, i64 120, i1 false) + br label %if.end486 + +if.else487: ; preds = %if.else475 + %enum.val495.sroa.4.0..sroa_cast360 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %enum.val495.sroa.4 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %enum.val495.sroa.4.0..sroa_cast360, i8 0, i64 120, i1 false) + %.0..sroa_cast365 = bitcast %"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* %5 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull %.0..sroa_cast365, i8 0, i64 120, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(120) %enum.val495.sroa.4.0..sroa_cast360, i8* noundef nonnull align 8 dereferenceable(120) %.0..sroa_cast365, i64 120, i1 false) + br label %if.end486 + +if.end486: ; preds = %if.else487, %if.end490 + %.0..sroa_cast365.sink = phi i8* [ %.0..sroa_cast365, %if.else487 ], [ %.0..sroa_cast357.sink, %if.end490 ] + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(120) %ifvalue479.sroa.5.0.ifvalue479.0..sroa_cast361, i8* noundef nonnull align 8 dereferenceable(120) %.0..sroa_cast365.sink, i64 120, i1 false) + br label %if.end474 + +if.end474: ; preds = %if.end486, %if.then476 + %ifvalue479.sroa.5.0.ifvalue479.0..sroa_cast361.sink = phi i8* [ %ifvalue479.sroa.5.0.ifvalue479.0..sroa_cast361, %if.end486 ], [ bitcast (%"T6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEE"* getelementptr inbounds (%"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEEE", %"record._ZN7default11std$FS$core6OptionIN_ZN11std$FS$core6OptionIT6_R_ZN11std$FS$core6StringEltR_ZN11std$FS$core5ArrayImER_ZN11std$FS$core5RangeIhER_ZN11std$FS$core5ArrayImEEEE"* @_ZN7default22var_1698522025530_3296E, i64 0, i32 1, i32 1) to i8*), %if.then476 ] + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(120) %ifvalue472.sroa.4.0.ifvalue472.0..sroa_cast, i8* noundef nonnull align 8 dereferenceable(120) %ifvalue479.sroa.5.0.ifvalue479.0..sroa_cast361.sink, i64 120, i1 false) + %93 = bitcast %"record._ZN7default11std$FS$core5RangeItE"* %0 to i8* + %tryvalue.sroa.0.0..sroa_idx = getelementptr inbounds %"record._ZN7default11std$FS$core5RangeItE", %"record._ZN7default11std$FS$core5RangeItE"* %0, i64 0, i32 0 + store i16 117, i16* %tryvalue.sroa.0.0..sroa_idx, align 8 + %tryvalue.sroa.2.0..sroa_idx615 = getelementptr inbounds %"record._ZN7default11std$FS$core5RangeItE", %"record._ZN7default11std$FS$core5RangeItE"* %0, i64 0, i32 1 + store i16 11, i16* %tryvalue.sroa.2.0..sroa_idx615, align 2 + %tryvalue.sroa.3.0..sroa_idx = getelementptr inbounds i8, i8* %93, i64 4 + %tryvalue.sroa.3.0..sroa_cast = bitcast i8* %tryvalue.sroa.3.0..sroa_idx to i32* + store i32 0, i32* %tryvalue.sroa.3.0..sroa_cast, align 4 + %tryvalue.sroa.4.0..sroa_idx616 = getelementptr inbounds %"record._ZN7default11std$FS$core5RangeItE", %"record._ZN7default11std$FS$core5RangeItE"* %0, i64 0, i32 2 + store i64 -21, i64* %tryvalue.sroa.4.0..sroa_idx616, align 8 + %tryvalue.sroa.5.0..sroa_idx617 = getelementptr inbounds %"record._ZN7default11std$FS$core5RangeItE", %"record._ZN7default11std$FS$core5RangeItE"* %0, i64 0, i32 3 + store i1 true, i1* %tryvalue.sroa.5.0..sroa_idx617, align 8 + %tryvalue.sroa.6.0..sroa_idx618 = getelementptr inbounds %"record._ZN7default11std$FS$core5RangeItE", %"record._ZN7default11std$FS$core5RangeItE"* %0, i64 0, i32 4 + store i1 true, i1* %tryvalue.sroa.6.0..sroa_idx618, align 1 + %tryvalue.sroa.7.0..sroa_idx619 = getelementptr inbounds %"record._ZN7default11std$FS$core5RangeItE", %"record._ZN7default11std$FS$core5RangeItE"* %0, i64 0, i32 5 + store i1 false, i1* %tryvalue.sroa.7.0..sroa_idx619, align 2 + %tryvalue.sroa.8.0..sroa_raw_idx = getelementptr inbounds i8, i8* %93, i64 19 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(5) %tryvalue.sroa.8.0..sroa_raw_idx, i8* noundef nonnull align 1 dereferenceable(5) %tryvalue.sroa.8657, i64 5, i1 false) + ret void +} + +attributes #0 = { "cj-runtime" "gc-leaf-function" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/phi-vector-bitcast.ll b/llvm/test/Transforms/CJRewriteStatepoint/phi-vector-bitcast.ll new file mode 100644 index 000000000..3273340c4 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/phi-vector-bitcast.ll @@ -0,0 +1,27 @@ +; REQUIRES: asserts +; RUN: opt < %s -disable-output -passes=cj-rewrite-statepoint + +; We shouldn't crash when we encounter a vector phi with more than one input +; from the same predecessor. +define void @foo(<2 x i8 addrspace(1)*> %arg1, i32 %arg2, i1 %arg3, <2 x i64 addrspace(1)*> %arg4) gc "cangjie" personality i32* null { +bb: + %tmp = bitcast <2 x i8 addrspace(1)*> %arg1 to <2 x i64 addrspace(1)*> + switch i32 %arg2, label %bb2 [ + i32 1, label %bb4 + i32 2, label %bb4 + ] + +bb2: ; preds = %bb + br i1 %arg3, label %bb8, label %bb4 + +bb4: ; preds = %bb2, %bb, %bb + %tmp5 = phi <2 x i64 addrspace(1)*> [ %tmp, %bb ], [ %tmp, %bb ], [ %arg4, %bb2 ] + call void @bar() + %tmp6 = extractelement <2 x i64 addrspace(1)*> %tmp5, i32 1 + ret void + +bb8: ; preds = %bb2 + ret void +} + +declare void @bar() diff --git a/llvm/test/Transforms/CJRewriteStatepoint/preprocess.ll b/llvm/test/Transforms/CJRewriteStatepoint/preprocess.ll new file mode 100644 index 000000000..dc7b5461c --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/preprocess.ll @@ -0,0 +1,63 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +; Test to make sure we destroy LCSSA's single entry phi nodes before +; running liveness + +declare void @consume(...) "gc-leaf-function" + +define void @test6(i64 addrspace(1)* %obj) gc "cangjie" { +; CHECK-LABEL: @test6 +entry: + br label %next + +next: ; preds = %entry +; CHECK-LABEL: next: +; CHECK-NEXT: gc.statepoint +; CHECK-NEXT: gc.relocate +; CHECK-NEXT: bitcast +; CHECK-NEXT: @consume(i64 addrspace(1)* %obj.reloc.casted) +; CHECK-NEXT: @consume(i64 addrspace(1)* %obj.reloc.casted) +; Need to delete unreachable gc.statepoint call + %obj2 = phi i64 addrspace(1)* [ %obj, %entry ] + call void @foo() [ "deopt"() ] + call void (...) @consume(i64 addrspace(1)* %obj2) + call void (...) @consume(i64 addrspace(1)* %obj) + ret void +} + +define void @test7() gc "cangjie" { +; CHECK-LABEL: test7 +; CHECK-NOT: gc.statepoint +; Need to delete unreachable gc.statepoint invoke - tested separately given +; a correct implementation could only remove the instructions, not the block + ret void + +unreached: ; preds = %unreached + %obj = phi i64 addrspace(1)* [ null, %unreached ] + call void @foo() [ "deopt"() ] + call void (...) @consume(i64 addrspace(1)* %obj) + br label %unreached +} + +define void @test8() gc "cangjie" personality i32 ()* undef { +; CHECK-LABEL: test8 +; CHECK-NOT: gc.statepoint +; Bound the last check-not + ret void + +unreached: ; No predecessors! + invoke void @foo() [ "deopt"() ] +; CHECK-LABEL: @foo + to label %normal_return unwind label %exceptional_return + +normal_return: ; preds = %unreached + ret void + +exceptional_return: ; preds = %unreached + %landing_pad4 = landingpad { i8*, i32 } + cleanup + ret void +} + +declare void @foo() diff --git a/llvm/test/Transforms/CJRewriteStatepoint/relocate-invoke-result.ll b/llvm/test/Transforms/CJRewriteStatepoint/relocate-invoke-result.ll new file mode 100644 index 000000000..2b04b24cc --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/relocate-invoke-result.ll @@ -0,0 +1,33 @@ + +;; RUN: opt -cj-rewrite-statepoint -verify -S < %s | FileCheck %s +;; RUN: opt -passes=cj-rewrite-statepoint,verify -S < %s | FileCheck %s +;; This test is to verify that RewriteStatepointsForGC correctly relocates values +;; defined by invoke instruction results. + +declare i64* addrspace(1)* @non_gc_call() "gc-leaf-function" + +declare void @gc_call() + +declare i32* @fake_personality_function() + +define i64* addrspace(1)* @test() gc "cangjie" personality i32* ()* @fake_personality_function { +; CHECK-LABEL: @test( + +entry: + %obj = invoke i64* addrspace(1)* @non_gc_call() + to label %normal_dest unwind label %unwind_dest + +unwind_dest: ; preds = %entry + %lpad = landingpad { i8*, i32 } + cleanup + resume { i8*, i32 } undef + +normal_dest: ; preds = %entry +; CHECK: normal_dest: +; CHECK-NEXT: gc.statepoint +; CHECK-NEXT: %obj.reloc = call coldcc i8 addrspace(1)* +; CHECK-NEXT: bitcast + + call void @gc_call() [ "deopt"(i32 0, i32 -1, i32 0, i32 0, i32 0) ] + ret i64* addrspace(1)* %obj +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/rematerialize-derived-pointers-at-uses.ll b/llvm/test/Transforms/CJRewriteStatepoint/rematerialize-derived-pointers-at-uses.ll new file mode 100644 index 000000000..0bafce4c2 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/rematerialize-derived-pointers-at-uses.ll @@ -0,0 +1,221 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes='cj-rewrite-statepoint,verify' -S | FileCheck %s + +declare void @use_obj(ptr addrspace(1)) "gc-leaf-function" +declare void @do_safepoint() + + +; Profitable test +define i32 @test_remat(ptr addrspace(1) %base, i1 %cond) gc "cangjie" { +; CHECK-LABEL: @test_remat( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[DERIVED:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 16 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE]]) ] +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: br i1 [[COND:%.*]], label [[HERE:%.*]], label [[THERE:%.*]] +; CHECK: here: +; CHECK-NEXT: [[TOKEN1:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE_RELOC]]) ] +; CHECK-NEXT: [[BASE_RELOC2:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN1]], i32 0, i32 0) +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: there: +; CHECK-NEXT: [[TOKEN3:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE_RELOC]]) ] +; CHECK-NEXT: [[BASE_RELOC4:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN3]], i32 0, i32 0) +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[DOT0:%.*]] = phi ptr addrspace(1) [ [[BASE_RELOC2]], [[HERE]] ], [ [[BASE_RELOC4]], [[THERE]] ] +; CHECK-NEXT: [[DERIVED_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[DOT0]], i32 16 +; CHECK-NEXT: [[RET:%.*]] = load i32, ptr addrspace(1) [[DERIVED_REMAT]], align 4 +; CHECK-NEXT: ret i32 [[RET]] +; +entry: + %derived = getelementptr i32, ptr addrspace(1) %base, i32 16 + call void @do_safepoint() + br i1 %cond, label %here, label %there + +here: + call void @do_safepoint() + br label %merge + +there: + call void @do_safepoint() + br label %merge + +merge: + %ret = load i32, ptr addrspace(1) %derived + ret i32 %ret +} + +; Unprofitable test +define i32 @test_many_uses(ptr addrspace(1) %base, i1 %cond) gc "cangjie" { +; CHECK-LABEL: @test_many_uses( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[DERIVED:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 16 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE]]) ] +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[DERIVED_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOC]], i32 16 +; CHECK-NEXT: br i1 [[COND:%.*]], label [[HERE:%.*]], label [[THERE:%.*]] +; CHECK: here: +; CHECK-NEXT: [[TOKEN3:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE_RELOC]]) ] +; CHECK-NEXT: [[BASE_RELOC4:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN3]], i32 0, i32 0) +; CHECK-NEXT: [[DERIVED_REMAT1:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOC4]], i32 16 +; CHECK-NEXT: call void @use_obj(ptr addrspace(1) [[DERIVED_REMAT1]]) +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: there: +; CHECK-NEXT: call void @use_obj(ptr addrspace(1) [[DERIVED_REMAT]]) +; CHECK-NEXT: [[TOKEN5:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE_RELOC]]) ] +; CHECK-NEXT: [[BASE_RELOC6:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN5]], i32 0, i32 0) +; CHECK-NEXT: [[DERIVED_REMAT2:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOC6]], i32 16 +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[DOT0:%.*]] = phi ptr addrspace(1) [ [[DERIVED_REMAT1]], [[HERE]] ], [ [[DERIVED_REMAT2]], [[THERE]] ] +; CHECK-NEXT: call void @use_obj(ptr addrspace(1) [[DOT0]]) +; CHECK-NEXT: [[RET:%.*]] = load i32, ptr addrspace(1) [[DOT0]], align 4 +; CHECK-NEXT: ret i32 [[RET]] +; +entry: + %derived = getelementptr i32, ptr addrspace(1) %base, i32 16 + call void @do_safepoint() + br i1 %cond, label %here, label %there + +here: + call void @do_safepoint() + call void @use_obj(ptr addrspace(1) %derived) + br label %merge + +there: + call void @use_obj(ptr addrspace(1) %derived) + call void @do_safepoint() + br label %merge + +merge: + call void @use_obj(ptr addrspace(1) %derived) + %ret = load i32, ptr addrspace(1) %derived + ret i32 %ret +} + +; Remat before phi - not implemented +define i32 @test_phi(ptr addrspace(1) %base, i1 %cond) gc "cangjie" { +; CHECK-LABEL: @test_phi( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[DERIVED1:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 16 +; CHECK-NEXT: [[DERIVED2:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE]], i32 32 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE]]) ] +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[DERIVED2_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOC]], i32 32 +; CHECK-NEXT: [[DERIVED1_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOC]], i32 16 +; CHECK-NEXT: br i1 [[COND:%.*]], label [[HERE:%.*]], label [[THERE:%.*]] +; CHECK: here: +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: there: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[PHI1:%.*]] = phi ptr addrspace(1) [ [[DERIVED1_REMAT]], [[HERE]] ], [ [[DERIVED2_REMAT]], [[THERE]] ] +; CHECK-NEXT: [[RET:%.*]] = load i32, ptr addrspace(1) [[PHI1]], align 4 +; CHECK-NEXT: ret i32 [[RET]] +; +entry: + %derived1 = getelementptr i32, ptr addrspace(1) %base, i32 16 + %derived2 = getelementptr i32, ptr addrspace(1) %base, i32 32 + call void @do_safepoint() + br i1 %cond, label %here, label %there + +here: + br label %merge + +there: + br label %merge + +merge: + %phi1 = phi ptr addrspace(1) [ %derived1, %here ], [ %derived2, %there ] + %ret = load i32, ptr addrspace(1) %phi1 + ret i32 %ret +} + +; Several uses per block +; TODO: We could rematerialize once per block for several uses if there is not statepoint between +define i32 @test_same_block_with_statepoint(ptr addrspace(1) %base, i1 %cond) gc "cangjie" { +; CHECK-LABEL: @test_same_block_with_statepoint( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[DERIVED:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 16 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE]]) ] +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: br i1 [[COND:%.*]], label [[HERE:%.*]], label [[THERE:%.*]] +; CHECK: here: +; CHECK-NEXT: [[DERIVED_REMAT3:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOC]], i32 16 +; CHECK-NEXT: call void @use_obj(ptr addrspace(1) [[DERIVED_REMAT3]]) +; CHECK-NEXT: [[TOKEN4:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE_RELOC]]) ] +; CHECK-NEXT: [[BASE_RELOC5:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN4]], i32 0, i32 0) +; CHECK-NEXT: [[DERIVED_REMAT2:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOC5]], i32 16 +; CHECK-NEXT: call void @use_obj(ptr addrspace(1) [[DERIVED_REMAT2]]) +; CHECK-NEXT: [[DERIVED_REMAT1:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOC5]], i32 16 +; CHECK-NEXT: [[DUMMY:%.*]] = load i32, ptr addrspace(1) [[DERIVED_REMAT1]], align 4 +; CHECK-NEXT: [[TOKEN6:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE_RELOC5]]) ] +; CHECK-NEXT: [[BASE_RELOC7:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN6]], i32 0, i32 0) +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: there: +; CHECK-NEXT: [[TOKEN8:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE_RELOC]]) ] +; CHECK-NEXT: [[BASE_RELOC9:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN8]], i32 0, i32 0) +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[DOT0:%.*]] = phi ptr addrspace(1) [ [[BASE_RELOC7]], [[HERE]] ], [ [[BASE_RELOC9]], [[THERE]] ] +; CHECK-NEXT: [[DERIVED_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[DOT0]], i32 16 +; CHECK-NEXT: [[RET:%.*]] = load i32, ptr addrspace(1) [[DERIVED_REMAT]], align 4 +; CHECK-NEXT: ret i32 [[RET]] +; +entry: + %derived = getelementptr i32, ptr addrspace(1) %base, i32 16 + call void @do_safepoint() + br i1 %cond, label %here, label %there + +here: + call void @use_obj(ptr addrspace(1) %derived) + call void @do_safepoint() + call void @use_obj(ptr addrspace(1) %derived) + %dummy = load i32, ptr addrspace(1) %derived + call void @do_safepoint() + br label %merge + +there: + call void @do_safepoint() + br label %merge + +merge: + %ret = load i32, ptr addrspace(1) %derived + ret i32 %ret +} + +; Test long chain with sub-chain rematerialized +; TODO: If we rematerialized longer chain first (%v4), then shorter on (%v0) would become dead +define void @test_chain(ptr addrspace(1) %base, i1 %cond1) gc "cangjie" { +; CHECK-LABEL: @test_chain( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[V0:%.*]] = getelementptr i8, ptr addrspace(1) [[BASE:%.*]], i64 16 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE]]) ] +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: br i1 [[COND1:%.*]], label [[BLOCK3:%.*]], label [[COMMON_RET:%.*]] +; CHECK: block3: +; CHECK-NEXT: [[V0_REMAT:%.*]] = getelementptr i8, ptr addrspace(1) [[BASE_RELOC]], i64 16 +; CHECK-NEXT: [[V4:%.*]] = getelementptr i8, ptr addrspace(1) [[V0_REMAT]], i64 70 +; CHECK-NEXT: [[TOKEN1:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE_RELOC]]) ] +; CHECK-NEXT: [[BASE_RELOC2:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN1]], i32 0, i32 0) +; CHECK-NEXT: [[V0_REMAT_REMAT:%.*]] = getelementptr i8, ptr addrspace(1) [[BASE_RELOC2]], i64 16 +; CHECK-NEXT: [[V4_REMAT:%.*]] = getelementptr i8, ptr addrspace(1) [[V0_REMAT_REMAT]], i64 70 +; CHECK-NEXT: [[V5:%.*]] = load atomic i8, ptr addrspace(1) [[V4_REMAT]] unordered, align 2 +; CHECK-NEXT: br label [[COMMON_RET]] +; CHECK: common.ret: +; CHECK-NEXT: ret void +; +entry: + %v0 = getelementptr i8, ptr addrspace(1) %base, i64 16 + call void @do_safepoint() + br i1 %cond1, label %block3, label %common.ret + +block3: + %v4 = getelementptr i8, ptr addrspace(1) %v0, i64 70 + call void @do_safepoint() + %v5 = load atomic i8, ptr addrspace(1) %v4 unordered, align 2 + br label %common.ret + +common.ret: + ret void +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/rematerialize-derived-pointers.ll b/llvm/test/Transforms/CJRewriteStatepoint/rematerialize-derived-pointers.ll new file mode 100644 index 000000000..647eb4393 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/rematerialize-derived-pointers.ll @@ -0,0 +1,398 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=cj-rewrite-statepoint -S | FileCheck %s + + +declare void @use_obj16(ptr addrspace(1)) "gc-leaf-function" +declare void @use_obj32(ptr addrspace(1)) "gc-leaf-function" +declare void @use_obj64(ptr addrspace(1)) "gc-leaf-function" + +declare void @do_safepoint() + +define void @test_gep_const(ptr addrspace(1) %base) gc "cangjie" { +; CHECK-LABEL: @test_gep_const( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[PTR:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 15 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE]]) ] +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[PTR_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOC]], i32 15 +; CHECK-NEXT: call void @use_obj32(ptr addrspace(1) [[BASE_RELOC]]) +; CHECK-NEXT: call void @use_obj32(ptr addrspace(1) [[PTR_REMAT]]) +; CHECK-NEXT: ret void +; +entry: + %ptr = getelementptr i32, ptr addrspace(1) %base, i32 15 + call void @do_safepoint() [ "deopt"() ] + call void @use_obj32(ptr addrspace(1) %base) + call void @use_obj32(ptr addrspace(1) %ptr) + ret void +} + +define void @test_gep_idx(ptr addrspace(1) %base, i32 %idx) gc "cangjie" { +; CHECK-LABEL: @test_gep_idx( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[PTR:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 [[IDX:%.*]] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE]]) ] +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[PTR_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOC]], i32 [[IDX]] +; CHECK-NEXT: call void @use_obj32(ptr addrspace(1) [[BASE_RELOC]]) +; CHECK-NEXT: call void @use_obj32(ptr addrspace(1) [[PTR_REMAT]]) +; CHECK-NEXT: ret void +; +entry: + %ptr = getelementptr i32, ptr addrspace(1) %base, i32 %idx + call void @do_safepoint() [ "deopt"() ] + call void @use_obj32(ptr addrspace(1) %base) + call void @use_obj32(ptr addrspace(1) %ptr) + ret void +} + +define void @test_bitcast(ptr addrspace(1) %base) gc "cangjie" { +; CHECK-LABEL: @test_bitcast( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE:%.*]]) ] +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: call void @use_obj32(ptr addrspace(1) [[BASE_RELOC]]) +; CHECK-NEXT: call void @use_obj64(ptr addrspace(1) [[BASE_RELOC]]) +; CHECK-NEXT: ret void +; +entry: + call void @do_safepoint() [ "deopt"() ] + call void @use_obj32(ptr addrspace(1) %base) + call void @use_obj64(ptr addrspace(1) %base) + ret void +} + +define void @test_bitcast_bitcast(ptr addrspace(1) %base) gc "cangjie" { +; CHECK-LABEL: @test_bitcast_bitcast( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE:%.*]]) ] +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: call void @use_obj32(ptr addrspace(1) [[BASE_RELOC]]) +; CHECK-NEXT: call void @use_obj16(ptr addrspace(1) [[BASE_RELOC]]) +; CHECK-NEXT: ret void +; +entry: + call void @do_safepoint() [ "deopt"() ] + + call void @use_obj32(ptr addrspace(1) %base) + call void @use_obj16(ptr addrspace(1) %base) + ret void +} + +define void @test_addrspacecast_addrspacecast(ptr addrspace(1) %base) gc "cangjie" { +; CHECK-LABEL: @test_addrspacecast_addrspacecast( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[PTR1:%.*]] = addrspacecast ptr addrspace(1) [[BASE:%.*]] to ptr +; CHECK-NEXT: [[PTR2:%.*]] = addrspacecast ptr [[PTR1]] to ptr addrspace(1) +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE]]) ] +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[PTR1_REMAT:%.*]] = addrspacecast ptr addrspace(1) [[BASE_RELOC:%.*]] to ptr +; CHECK-NEXT: [[PTR2_REMAT:%.*]] = addrspacecast ptr [[PTR1_REMAT:%.*]] to ptr addrspace(1) +; CHECK-NEXT: call void @use_obj32(ptr addrspace(1) [[BASE_RELOC]]) +; CHECK-NEXT: call void @use_obj32(ptr addrspace(1) [[PTR2_REMAT]]) +; CHECK-NEXT: ret void +; +entry: + %ptr1 = addrspacecast ptr addrspace(1) %base to ptr + %ptr2 = addrspacecast ptr %ptr1 to ptr addrspace(1) + call void @do_safepoint() [ "deopt"() ] + + call void @use_obj32(ptr addrspace(1) %base) + call void @use_obj32(ptr addrspace(1) %ptr2) + ret void +} + +define void @test_bitcast_gep(ptr addrspace(1) %base) gc "cangjie" { +; CHECK-LABEL: @test_bitcast_gep( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[PTR_GEP:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 15 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE]]) ] +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[PTR_GEP_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOC]], i32 15 +; CHECK-NEXT: call void @use_obj32(ptr addrspace(1) [[BASE_RELOC]]) +; CHECK-NEXT: call void @use_obj64(ptr addrspace(1) [[PTR_GEP_REMAT]]) +; CHECK-NEXT: ret void +; +entry: + %ptr.gep = getelementptr i32, ptr addrspace(1) %base, i32 15 + call void @do_safepoint() [ "deopt"() ] + + call void @use_obj32(ptr addrspace(1) %base) + call void @use_obj64(ptr addrspace(1) %ptr.gep) + ret void +} + +define void @test_intersecting_chains(ptr addrspace(1) %base, i32 %idx) gc "cangjie" { +; CHECK-LABEL: @test_intersecting_chains( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[PTR_GEP:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 15 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE]]) ] +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[PTR_GEP_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOC]], i32 15 +; CHECK-NEXT: call void @use_obj64(ptr addrspace(1) [[PTR_GEP_REMAT]]) +; CHECK-NEXT: call void @use_obj16(ptr addrspace(1) [[PTR_GEP_REMAT]]) +; CHECK-NEXT: ret void +; +entry: + %ptr.gep = getelementptr i32, ptr addrspace(1) %base, i32 15 + call void @do_safepoint() [ "deopt"() ] + + call void @use_obj64(ptr addrspace(1) %ptr.gep) + call void @use_obj16(ptr addrspace(1) %ptr.gep) + ret void +} + +define void @test_cost_threshold(ptr addrspace(1) %base, i32 %idx1, i32 %idx2, i32 %idx3) gc "cangjie" { +; CHECK-LABEL: @test_cost_threshold( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[PTR_GEP:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 15 +; CHECK-NEXT: [[PTR_GEP2:%.*]] = getelementptr i32, ptr addrspace(1) [[PTR_GEP]], i32 [[IDX1:%.*]] +; CHECK-NEXT: [[PTR_GEP3:%.*]] = getelementptr i32, ptr addrspace(1) [[PTR_GEP2]], i32 [[IDX2:%.*]] +; CHECK-NEXT: [[PTR_GEP4:%.*]] = getelementptr i32, ptr addrspace(1) [[PTR_GEP3]], i32 [[IDX3:%.*]] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[PTR_GEP4]], ptr addrspace(1) [[BASE]]) ] +; CHECK-NEXT: [[PTR_GEP4_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: call void @use_obj64(ptr addrspace(1) [[PTR_GEP4_RELOC]]) +; CHECK-NEXT: ret void +; +entry: + %ptr.gep = getelementptr i32, ptr addrspace(1) %base, i32 15 + %ptr.gep2 = getelementptr i32, ptr addrspace(1) %ptr.gep, i32 %idx1 + %ptr.gep3 = getelementptr i32, ptr addrspace(1) %ptr.gep2, i32 %idx2 + %ptr.gep4 = getelementptr i32, ptr addrspace(1) %ptr.gep3, i32 %idx3 + call void @do_safepoint() [ "deopt"() ] + + call void @use_obj64(ptr addrspace(1) %ptr.gep4) + ret void +} + +define void @test_two_derived(ptr addrspace(1) %base) gc "cangjie" { +; CHECK-LABEL: @test_two_derived( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[PTR:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 15 +; CHECK-NEXT: [[PTR2:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE]], i32 12 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE]]) ] +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[PTR_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOC]], i32 15 +; CHECK-NEXT: [[PTR2_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOC]], i32 12 +; CHECK-NEXT: call void @use_obj32(ptr addrspace(1) [[PTR_REMAT]]) +; CHECK-NEXT: call void @use_obj32(ptr addrspace(1) [[PTR2_REMAT]]) +; CHECK-NEXT: ret void +; +entry: + %ptr = getelementptr i32, ptr addrspace(1) %base, i32 15 + %ptr2 = getelementptr i32, ptr addrspace(1) %base, i32 12 + call void @do_safepoint() [ "deopt"() ] + + call void @use_obj32(ptr addrspace(1) %ptr) + call void @use_obj32(ptr addrspace(1) %ptr2) + ret void +} + +define void @test_gep_smallint_array(ptr addrspace(1) %base) gc "cangjie" { +; CHECK-LABEL: @test_gep_smallint_array( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[PTR:%.*]] = getelementptr [3 x i32], ptr addrspace(1) [[BASE:%.*]], i32 0, i32 2 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE]]) ] +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[PTR_REMAT:%.*]] = getelementptr [3 x i32], ptr addrspace(1) [[BASE_RELOC]], i32 0, i32 2 +; CHECK-NEXT: call void @use_obj32(ptr addrspace(1) [[PTR_REMAT]]) +; CHECK-NEXT: ret void +; +entry: + %ptr = getelementptr [3 x i32], ptr addrspace(1) %base, i32 0, i32 2 + call void @do_safepoint() [ "deopt"() ] + + call void @use_obj32(ptr addrspace(1) %ptr) + ret void +} + +declare i32 @fake_personality_function() + +define void @test_invoke(ptr addrspace(1) %base) gc "cangjie" personality ptr @fake_personality_function { +; CHECK-LABEL: @test_invoke( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[PTR_GEP:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 15 +; CHECK-NEXT: [[TOKEN:%.*]] = invoke token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASE]]) ] +; CHECK-NEXT: to label [[NORMAL:%.*]] unwind label [[EXCEPTION:%.*]] +; CHECK: normal: +; CHECK-NEXT: [[BASE_RELOC2:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[PTR_GEP_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOC2]], i32 15 +; CHECK-NEXT: call void @use_obj64(ptr addrspace(1) [[PTR_GEP_REMAT]]) +; CHECK-NEXT: call void @use_obj16(ptr addrspace(1) [[PTR_GEP_REMAT]]) +; CHECK-NEXT: ret void +; CHECK: exception: +; CHECK-NEXT: [[LANDING_PAD4:%.*]] = landingpad token +; CHECK-NEXT: cleanup +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[LANDING_PAD4]], i32 0, i32 0) +; CHECK-NEXT: [[PTR_GEP_REMAT1:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOC]], i32 15 +; CHECK-NEXT: call void @use_obj64(ptr addrspace(1) [[PTR_GEP_REMAT1]]) +; CHECK-NEXT: call void @use_obj16(ptr addrspace(1) [[PTR_GEP_REMAT1]]) +; CHECK-NEXT: ret void +; +entry: + %ptr.gep = getelementptr i32, ptr addrspace(1) %base, i32 15 + invoke void @do_safepoint() [ "deopt"() ] + to label %normal unwind label %exception + +normal: + call void @use_obj64(ptr addrspace(1) %ptr.gep) + call void @use_obj16(ptr addrspace(1) %ptr.gep) + ret void + +exception: + %landing_pad4 = landingpad token + cleanup + call void @use_obj64(ptr addrspace(1) %ptr.gep) + call void @use_obj16(ptr addrspace(1) %ptr.gep) + ret void +} + +define void @test_loop(ptr addrspace(1) %base) gc "cangjie" { +; CHECK-LABEL: @test_loop( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[PTR_GEP:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 15 +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[DOT0:%.*]] = phi ptr addrspace(1) [ [[BASE]], [[ENTRY:%.*]] ], [ [[BASE_RELOC:%.*]], [[LOOP]] ] +; CHECK-NEXT: [[PTR_GEP_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[DOT0]], i32 15 +; CHECK-NEXT: call void @use_obj32(ptr addrspace(1) [[PTR_GEP_REMAT]]) +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[DOT0]]) ] +; CHECK-NEXT: [[BASE_RELOC]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: br label [[LOOP]] +; +entry: + %ptr.gep = getelementptr i32, ptr addrspace(1) %base, i32 15 + br label %loop + +loop: ; preds = %loop, %entry + call void @use_obj32(ptr addrspace(1) %ptr.gep) + call void @do_safepoint() [ "deopt"() ] + br label %loop +} + +define void @test_too_long(ptr addrspace(1) %base) gc "cangjie" { +; CHECK-LABEL: @test_too_long( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[PTR_GEP:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 15 +; CHECK-NEXT: [[PTR_GEP1:%.*]] = getelementptr i32, ptr addrspace(1) [[PTR_GEP]], i32 15 +; CHECK-NEXT: [[PTR_GEP2:%.*]] = getelementptr i32, ptr addrspace(1) [[PTR_GEP1]], i32 15 +; CHECK-NEXT: [[PTR_GEP3:%.*]] = getelementptr i32, ptr addrspace(1) [[PTR_GEP2]], i32 15 +; CHECK-NEXT: [[PTR_GEP4:%.*]] = getelementptr i32, ptr addrspace(1) [[PTR_GEP3]], i32 15 +; CHECK-NEXT: [[PTR_GEP5:%.*]] = getelementptr i32, ptr addrspace(1) [[PTR_GEP4]], i32 15 +; CHECK-NEXT: [[PTR_GEP6:%.*]] = getelementptr i32, ptr addrspace(1) [[PTR_GEP5]], i32 15 +; CHECK-NEXT: [[PTR_GEP7:%.*]] = getelementptr i32, ptr addrspace(1) [[PTR_GEP6]], i32 15 +; CHECK-NEXT: [[PTR_GEP8:%.*]] = getelementptr i32, ptr addrspace(1) [[PTR_GEP7]], i32 15 +; CHECK-NEXT: [[PTR_GEP9:%.*]] = getelementptr i32, ptr addrspace(1) [[PTR_GEP8]], i32 15 +; CHECK-NEXT: [[PTR_GEP10:%.*]] = getelementptr i32, ptr addrspace(1) [[PTR_GEP9]], i32 15 +; CHECK-NEXT: [[PTR_GEP11:%.*]] = getelementptr i32, ptr addrspace(1) [[PTR_GEP10]], i32 15 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[PTR_GEP11]], ptr addrspace(1) [[BASE]]) ] +; CHECK-NEXT: [[PTR_GEP11_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: call void @use_obj32(ptr addrspace(1) [[PTR_GEP11_RELOC]]) +; CHECK-NEXT: ret void +; +entry: + %ptr.gep = getelementptr i32, ptr addrspace(1) %base, i32 15 + %ptr.gep1 = getelementptr i32, ptr addrspace(1) %ptr.gep, i32 15 + %ptr.gep2 = getelementptr i32, ptr addrspace(1) %ptr.gep1, i32 15 + %ptr.gep3 = getelementptr i32, ptr addrspace(1) %ptr.gep2, i32 15 + %ptr.gep4 = getelementptr i32, ptr addrspace(1) %ptr.gep3, i32 15 + %ptr.gep5 = getelementptr i32, ptr addrspace(1) %ptr.gep4, i32 15 + %ptr.gep6 = getelementptr i32, ptr addrspace(1) %ptr.gep5, i32 15 + %ptr.gep7 = getelementptr i32, ptr addrspace(1) %ptr.gep6, i32 15 + %ptr.gep8 = getelementptr i32, ptr addrspace(1) %ptr.gep7, i32 15 + %ptr.gep9 = getelementptr i32, ptr addrspace(1) %ptr.gep8, i32 15 + %ptr.gep10 = getelementptr i32, ptr addrspace(1) %ptr.gep9, i32 15 + %ptr.gep11 = getelementptr i32, ptr addrspace(1) %ptr.gep10, i32 15 + call void @do_safepoint() [ "deopt"() ] + call void @use_obj32(ptr addrspace(1) %ptr.gep11) + ret void +} + + +declare ptr addrspace(1) @new_instance() nounwind "gc-leaf-function" + +; remat the gep in presence of base pointer which is a phi node. +; FIXME: We should remove the extra basephi.base as well. +define void @contains_basephi(i1 %cond) gc "cangjie" { +; CHECK-LABEL: @contains_basephi( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[BASE1:%.*]] = call ptr addrspace(1) @new_instance() +; CHECK-NEXT: [[BASE2:%.*]] = call ptr addrspace(1) @new_instance() +; CHECK-NEXT: br i1 [[COND:%.*]], label [[HERE:%.*]], label [[THERE:%.*]] +; CHECK: here: +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: there: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[BASEPHI:%.*]] = phi ptr addrspace(1) [ [[BASE1]], [[HERE]] ], [ [[BASE2]], [[THERE]] ] +; CHECK-NEXT: [[PTR_GEP:%.*]] = getelementptr i32, ptr addrspace(1) [[BASEPHI]], i32 15 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASEPHI]]) ] +; CHECK-NEXT: [[BASEPHI_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[PTR_GEP_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASEPHI_RELOC]], i32 15 +; CHECK-NEXT: call void @use_obj32(ptr addrspace(1) [[PTR_GEP_REMAT]]) +; CHECK-NEXT: ret void +; +entry: + %base1 = call ptr addrspace(1) @new_instance() + %base2 = call ptr addrspace(1) @new_instance() + br i1 %cond, label %here, label %there + +here: + br label %merge + +there: + br label %merge + +merge: + + + + %basephi = phi ptr addrspace(1) [ %base1, %here ], [ %base2, %there ] + %ptr.gep = getelementptr i32, ptr addrspace(1) %basephi, i32 15 + call void @do_safepoint() ["deopt"() ] + call void @use_obj32(ptr addrspace(1) %ptr.gep) + ret void +} + + +define void @test_intersecting_chains_with_phi(i1 %cond) gc "cangjie" { +; CHECK-LABEL: @test_intersecting_chains_with_phi( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[BASE1:%.*]] = call ptr addrspace(1) @new_instance() +; CHECK-NEXT: [[BASE2:%.*]] = call ptr addrspace(1) @new_instance() +; CHECK-NEXT: br i1 [[COND:%.*]], label [[HERE:%.*]], label [[THERE:%.*]] +; CHECK: here: +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: there: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[BASEPHI:%.*]] = phi ptr addrspace(1) [ [[BASE1]], [[HERE]] ], [ [[BASE2]], [[THERE]] ] +; CHECK-NEXT: [[PTR_GEP:%.*]] = getelementptr i32, ptr addrspace(1) [[BASEPHI]], i32 15 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, ptr @do_safepoint, i32 0, i32 0) [ "gc-live"(ptr addrspace(1) [[BASEPHI]]) ] +; CHECK-NEXT: [[BASEPHI_RELOC:%.*]] = call coldcc ptr addrspace(1) @llvm.cj.gc.relocate.p1(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[PTR_GEP_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASEPHI_RELOC]], i32 15 +; CHECK-NEXT: call void @use_obj64(ptr addrspace(1) [[PTR_GEP_REMAT]]) +; CHECK-NEXT: call void @use_obj16(ptr addrspace(1) [[PTR_GEP_REMAT]]) +; CHECK-NEXT: ret void +; +entry: + %base1 = call ptr addrspace(1) @new_instance() + %base2 = call ptr addrspace(1) @new_instance() + br i1 %cond, label %here, label %there + +here: + br label %merge + +there: + br label %merge + +merge: + %basephi = phi ptr addrspace(1) [ %base1, %here ], [ %base2, %there ] + %ptr.gep = getelementptr i32, ptr addrspace(1) %basephi, i32 15 + call void @do_safepoint() [ "deopt"() ] + call void @use_obj64(ptr addrspace(1) %ptr.gep) + call void @use_obj16(ptr addrspace(1) %ptr.gep) + ret void +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/rewrite-invoke.ll b/llvm/test/Transforms/CJRewriteStatepoint/rewrite-invoke.ll new file mode 100644 index 000000000..ee13889a0 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/rewrite-invoke.ll @@ -0,0 +1,33 @@ +; RUN: opt -cj-rewrite-statepoint -verify -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint,verify -S < %s | FileCheck %s + +declare i8 addrspace(1)* @gc_call() + +declare i32* @fake_personality_function() + +define i8 addrspace(1)* @test(i1 %c) gc "cangjie" personality i32* ()* @fake_personality_function { +; CHECK-LABEL: @test( +entry: + br i1 %c, label %gc_invoke, label %normal_dest + +gc_invoke: +; CHECK: [[TOKEN:%[^ ]+]] = invoke token {{[^@]+}}@llvm.cj.gc.statepoint{{[^@]+}}@gc_call + %obj = invoke i8 addrspace(1)* @gc_call() [ "deopt"(i32 0, i32 -1, i32 0, i32 0, i32 0) ] + to label %normal_dest unwind label %unwind_dest + +unwind_dest: +; CHECK: unwind_dest: + %lpad = landingpad { i8*, i32 } + cleanup + resume { i8*, i32 } undef + +; CHECK: [[NORMAL_DEST_SPLIT:[^:]+:]] +; CHECK-NEXT: [[RET_VAL:%[^ ]+]] = call i8 addrspace(1)* @llvm.cj.gc.result.p1i8(token [[TOKEN]]) +; CHECK-NEXT: br label %normal_dest + +normal_dest: +; CHECK: normal_dest: +; CHECK-NEXT: %merge = phi i8 addrspace(1)* [ null, %entry ], [ %obj2, %normal_dest1 ] + %merge = phi i8 addrspace(1)* [ null, %entry ], [ %obj, %gc_invoke ] + ret i8 addrspace(1)* %merge +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/scalar-base-vector-2.ll b/llvm/test/Transforms/CJRewriteStatepoint/scalar-base-vector-2.ll new file mode 100644 index 000000000..9f0115cea --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/scalar-base-vector-2.ll @@ -0,0 +1,76 @@ +; RUN: opt < %s -cj-rewrite-statepoint -S | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S | FileCheck %s + +; Assertions are almost autogenerated except for last testcase widget, which was +; updated (with -DAG instead of -NEXT) to fix buildbot failure reproducible only on two boxes. + +; Uses of extractelement that are of scalar type should not have the BDV +; incorrectly identified as a vector type. +define void @widget() gc "cangjie" { +; CHECK-LABEL: @widget( +; CHECK-NEXT: bb6: +; CHECK-NEXT: [[BASE_EE:%.*]] = extractelement <2 x i8 addrspace(1)*> zeroinitializer, i32 1, !is_base_value !1 +; CHECK-NEXT: [[TMP:%.*]] = extractelement <2 x i8 addrspace(1)*> undef, i32 1 +; CHECK-NEXT: br i1 undef, label [[BB7:%.*]], label [[BB9:%.*]] +; CHECK: bb7: +; CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[TMP]], i64 12 +; CHECK-NEXT: br label [[BB11:%.*]] +; CHECK: bb9: +; CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[TMP]], i64 12 +; CHECK-NEXT: br i1 undef, label [[BB11]], label [[BB15:%.*]] +; CHECK: bb11: +; CHECK-NEXT: [[TMP12_BASE:%.*]] = phi i8 addrspace(1)* [ [[BASE_EE]], [[BB7]] ], [ [[BASE_EE]], [[BB9]] ], !is_base_value !1 +; CHECK-NEXT: [[TMP12:%.*]] = phi i8 addrspace(1)* [ [[TMP8]], [[BB7]] ], [ [[TMP10]], [[BB9]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @snork, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[TMP12_BASE]], i8 addrspace(1)* [[TMP12]]) ] +; CHECK-NEXT: [[TMP12_BASE_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[TMP12_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 1) +; CHECK-NEXT: br label [[BB15]] +; CHECK: bb15: +; CHECK-NEXT: [[TMP16_BASE:%.*]] = phi i8 addrspace(1)* [ [[BASE_EE]], [[BB9]] ], [ [[TMP12_BASE_RELOC]], [[BB11]] ], !is_base_value !1 +; CHECK-NEXT: [[TMP16:%.*]] = phi i8 addrspace(1)* [ [[TMP10]], [[BB9]] ], [ [[TMP12_RELOC]], [[BB11]] ] +; CHECK-NEXT: br i1 undef, label [[BB17:%.*]], label [[BB20:%.*]] +; CHECK: bb17: +; CHECK-NEXT: [[TOKEN1:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @snork, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[TMP16_BASE]], i8 addrspace(1)* [[TMP16]]) ] +; CHECK-NEXT: [[TMP16_BASE_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN1]], i32 0, i32 0) +; CHECK-NEXT: [[TMP16_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN1]], i32 0, i32 1) +; CHECK-NEXT: br label [[BB20]] +; CHECK: bb20: +; CHECK-DAG: [[DOT05:%.*]] = phi i8 addrspace(1)* [ [[TMP16_BASE_RELOC]], [[BB17]] ], [ [[TMP16_BASE]], [[BB15]] ] +; CHECK-DAG: [[DOT0:%.*]] = phi i8 addrspace(1)* [ [[TMP16_RELOC]], [[BB17]] ], [ [[TMP16]], [[BB15]] ] +; CHECK-NEXT: [[TOKEN2:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @foo, i32 1, i32 0, i8 addrspace(1)* [[DOT0]]) [ "gc-live"(i8 addrspace(1)* [[DOT05]], i8 addrspace(1)* [[DOT0]]) ] +; CHECK-NEXT: [[TMP16_BASE_RELOC3:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN2]], i32 0, i32 0) +; CHECK-NEXT: [[TMP16_RELOC4:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN2]], i32 0, i32 1) +; CHECK-NEXT: ret void +; +bb6: ; preds = %bb3 + %tmp = extractelement <2 x i8 addrspace(1)*> undef, i32 1 + br i1 undef, label %bb7, label %bb9 + +bb7: ; preds = %bb6 + %tmp8 = getelementptr inbounds i8, i8 addrspace(1)* %tmp, i64 12 + br label %bb11 + +bb9: ; preds = %bb6, %bb6 + %tmp10 = getelementptr inbounds i8, i8 addrspace(1)* %tmp, i64 12 + br i1 undef, label %bb11, label %bb15 + +bb11: ; preds = %bb9, %bb7 + %tmp12 = phi i8 addrspace(1)* [ %tmp8, %bb7 ], [ %tmp10, %bb9 ] + call void @snork() + br label %bb15 + +bb15: ; preds = %bb11, %bb9, %bb9 + %tmp16 = phi i8 addrspace(1)* [ %tmp10, %bb9 ], [ %tmp12, %bb11 ] + br i1 undef, label %bb17, label %bb20 + +bb17: ; preds = %bb15 + call void @snork() + br label %bb20 + +bb20: ; preds = %bb17, %bb15, %bb15 + call void @foo(i8 addrspace(1)* %tmp16) + ret void +} + +declare void @snork() +declare void @foo(i8 addrspace(1)*) diff --git a/llvm/test/Transforms/CJRewriteStatepoint/scalar-base-vector.ll b/llvm/test/Transforms/CJRewriteStatepoint/scalar-base-vector.ll new file mode 100644 index 000000000..598d8fa7d --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/scalar-base-vector.ll @@ -0,0 +1,190 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -cj-rewrite-statepoint -S | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S | FileCheck %s + +declare void @do_safepoint() +declare i8 addrspace(1)* @def_ptr() + +define i32 addrspace(1)* @test1(i8 addrspace(1)* %base1, <2 x i64> %offsets) gc "cangjie" { +; CHECK-LABEL: @test1( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 undef, label [[FIRST:%.*]], label [[SECOND:%.*]] +; CHECK: first: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* ()* @def_ptr, i32 0, i32 0) +; CHECK-NEXT: [[BASE21:%.*]] = call i8 addrspace(1)* @llvm.cj.gc.result.p1i8(token [[TOKEN]]) +; CHECK-NEXT: br label [[SECOND]] +; CHECK: second: +; CHECK-NEXT: [[PHI:%.*]] = phi i8 addrspace(1)* [ [[BASE1:%.*]], [[ENTRY:%.*]] ], [ [[BASE21]], [[FIRST]] ] +; CHECK-NEXT: [[BASE_I32:%.*]] = bitcast i8 addrspace(1)* [[PHI]] to i32 addrspace(1)* +; CHECK-NEXT: [[CAST:%.*]] = bitcast i8 addrspace(1)* [[PHI]] to i32 addrspace(1)* +; CHECK-NEXT: [[DOTSPLATINSERT_BASE:%.*]] = insertelement <2 x i32 addrspace(1)*> zeroinitializer, i32 addrspace(1)* [[CAST]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <2 x i32 addrspace(1)*> poison, i32 addrspace(1)* [[BASE_I32]], i32 0 +; CHECK-NEXT: [[DOTSPLAT_BASE:%.*]] = shufflevector <2 x i32 addrspace(1)*> [[DOTSPLATINSERT_BASE]], <2 x i32 addrspace(1)*> undef, <2 x i32> zeroinitializer, !is_base_value !1 +; CHECK-NEXT: [[DOTSPLAT:%.*]] = shufflevector <2 x i32 addrspace(1)*> [[DOTSPLATINSERT]], <2 x i32 addrspace(1)*> poison, <2 x i32> zeroinitializer +; CHECK-NEXT: [[VEC:%.*]] = getelementptr i32, <2 x i32 addrspace(1)*> [[DOTSPLAT]], <2 x i64> [[OFFSETS:%.*]] +; CHECK-NEXT: [[PTR_BASE:%.*]] = extractelement <2 x i32 addrspace(1)*> [[DOTSPLAT_BASE]], i32 1, !is_base_value !1 +; CHECK-NEXT: [[PTR:%.*]] = extractelement <2 x i32 addrspace(1)*> [[VEC]], i32 1 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i32 addrspace(1)* [[PTR]], i32 addrspace(1)* [[PTR_BASE]]) ] +; CHECK-NEXT: [[PTR_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[PTR_RELOC_CASTED:%.*]] = bitcast i8 addrspace(1)* [[PTR_RELOC]] to i32 addrspace(1)* +; CHECK-NEXT: [[PTR_BASE_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[PTR_BASE_RELOC_CASTED:%.*]] = bitcast i8 addrspace(1)* [[PTR_BASE_RELOC]] to i32 addrspace(1)* +; CHECK-NEXT: ret i32 addrspace(1)* [[PTR_RELOC_CASTED]] +; +entry: + br i1 undef, label %first, label %second + +first: + %base2 = call i8 addrspace(1)* @def_ptr() + br label %second + +second: + %phi = phi i8 addrspace(1)* [ %base1, %entry ], [ %base2, %first ] + %base.i32 = bitcast i8 addrspace(1)* %phi to i32 addrspace(1)* + %vec = getelementptr i32, i32 addrspace(1)* %base.i32, <2 x i64> %offsets + %ptr = extractelement <2 x i32 addrspace(1)*> %vec, i32 1 + call void @do_safepoint() + ret i32 addrspace(1)* %ptr +} + +define i32 addrspace(1)* @test2(i8 addrspace(1)* %base, <2 x i64> %offsets) gc "cangjie" { +; CHECK-LABEL: @test2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[BASE_I32:%.*]] = bitcast i8 addrspace(1)* [[BASE:%.*]] to i32 addrspace(1)* +; CHECK-NEXT: [[CAST:%.*]] = bitcast i8 addrspace(1)* [[BASE]] to i32 addrspace(1)* +; CHECK-NEXT: [[DOTSPLATINSERT_BASE:%.*]] = insertelement <2 x i32 addrspace(1)*> zeroinitializer, i32 addrspace(1)* [[CAST]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <2 x i32 addrspace(1)*> poison, i32 addrspace(1)* [[BASE_I32]], i32 0 +; CHECK-NEXT: [[DOTSPLAT_BASE:%.*]] = shufflevector <2 x i32 addrspace(1)*> [[DOTSPLATINSERT_BASE]], <2 x i32 addrspace(1)*> undef, <2 x i32> zeroinitializer, !is_base_value !1 +; CHECK-NEXT: [[DOTSPLAT:%.*]] = shufflevector <2 x i32 addrspace(1)*> [[DOTSPLATINSERT]], <2 x i32 addrspace(1)*> poison, <2 x i32> zeroinitializer +; CHECK-NEXT: [[VEC:%.*]] = getelementptr i32, <2 x i32 addrspace(1)*> [[DOTSPLAT]], <2 x i64> [[OFFSETS:%.*]] +; CHECK-NEXT: [[PTR_BASE:%.*]] = extractelement <2 x i32 addrspace(1)*> [[DOTSPLAT_BASE]], i32 1, !is_base_value !1 +; CHECK-NEXT: [[PTR:%.*]] = extractelement <2 x i32 addrspace(1)*> [[VEC]], i32 1 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i32 addrspace(1)* [[PTR]], i32 addrspace(1)* [[PTR_BASE]]) ] +; CHECK-NEXT: [[PTR_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[PTR_RELOC_CASTED:%.*]] = bitcast i8 addrspace(1)* [[PTR_RELOC]] to i32 addrspace(1)* +; CHECK-NEXT: [[PTR_BASE_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[PTR_BASE_RELOC_CASTED:%.*]] = bitcast i8 addrspace(1)* [[PTR_BASE_RELOC]] to i32 addrspace(1)* +; CHECK-NEXT: ret i32 addrspace(1)* [[PTR_RELOC_CASTED]] +; +entry: + %base.i32 = bitcast i8 addrspace(1)* %base to i32 addrspace(1)* + %vec = getelementptr i32, i32 addrspace(1)* %base.i32, <2 x i64> %offsets + %ptr = extractelement <2 x i32 addrspace(1)*> %vec, i32 1 + call void @do_safepoint() + ret i32 addrspace(1)* %ptr +} + +define i32 addrspace(1)* @test3(<2 x i8 addrspace(1)*> %base, <2 x i64> %offsets) gc "cangjie" { +; CHECK-LABEL: @test3( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[BASE_I32:%.*]] = bitcast <2 x i8 addrspace(1)*> [[BASE:%.*]] to <2 x i32 addrspace(1)*> +; CHECK-NEXT: [[VEC:%.*]] = getelementptr i32, <2 x i32 addrspace(1)*> [[BASE_I32]], <2 x i64> [[OFFSETS:%.*]] +; CHECK-NEXT: [[BASE_EE:%.*]] = extractelement <2 x i8 addrspace(1)*> [[BASE]], i32 1, !is_base_value !1 +; CHECK-NEXT: [[PTR:%.*]] = extractelement <2 x i32 addrspace(1)*> [[VEC]], i32 1 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(i32 addrspace(1)* [[PTR]], i8 addrspace(1)* [[BASE_EE]]) ] +; CHECK-NEXT: [[PTR_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[PTR_RELOC_CASTED:%.*]] = bitcast i8 addrspace(1)* [[PTR_RELOC]] to i32 addrspace(1)* +; CHECK-NEXT: [[BASE_EE_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: ret i32 addrspace(1)* [[PTR_RELOC_CASTED]] +; +entry: + %base.i32 = bitcast <2 x i8 addrspace(1)*> %base to <2 x i32 addrspace(1)*> + %vec = getelementptr i32, <2 x i32 addrspace(1)*> %base.i32, <2 x i64> %offsets + %ptr = extractelement <2 x i32 addrspace(1)*> %vec, i32 1 + call void @do_safepoint() + ret i32 addrspace(1)* %ptr +} + +define i32 addrspace(1)* @test4(i8 addrspace(1)* %base, <2 x i64> %offsets) gc "cangjie" { +; CHECK-LABEL: @test4( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[BASE_I32:%.*]] = bitcast i8 addrspace(1)* [[BASE:%.*]] to i32 addrspace(1)* +; CHECK-NEXT: [[CAST:%.*]] = bitcast i8 addrspace(1)* [[BASE]] to i32 addrspace(1)* +; CHECK-NEXT: [[DOTSPLATINSERT_BASE:%.*]] = insertelement <2 x i32 addrspace(1)*> zeroinitializer, i32 addrspace(1)* [[CAST]], i32 0, !is_base_value !1 +; CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <2 x i32 addrspace(1)*> poison, i32 addrspace(1)* [[BASE_I32]], i32 0 +; CHECK-NEXT: [[DOTSPLAT_BASE:%.*]] = shufflevector <2 x i32 addrspace(1)*> [[DOTSPLATINSERT_BASE]], <2 x i32 addrspace(1)*> undef, <2 x i32> zeroinitializer, !is_base_value !1 +; CHECK-NEXT: [[DOTSPLAT:%.*]] = shufflevector <2 x i32 addrspace(1)*> [[DOTSPLATINSERT]], <2 x i32 addrspace(1)*> poison, <2 x i32> zeroinitializer +; CHECK-NEXT: [[VEC:%.*]] = getelementptr i32, <2 x i32 addrspace(1)*> [[DOTSPLAT]], <2 x i64> [[OFFSETS:%.*]] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(<2 x i32 addrspace(1)*> [[VEC]], <2 x i32 addrspace(1)*> [[DOTSPLAT_BASE]]) ] +; CHECK-NEXT: [[VEC_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[VEC_RELOC_CASTED:%.*]] = bitcast <2 x i8 addrspace(1)*> [[VEC_RELOC]] to <2 x i32 addrspace(1)*> +; CHECK-NEXT: [[DOTSPLAT_BASE_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[DOTSPLAT_BASE_RELOC_CASTED:%.*]] = bitcast <2 x i8 addrspace(1)*> [[DOTSPLAT_BASE_RELOC]] to <2 x i32 addrspace(1)*> +; CHECK-NEXT: [[PTR:%.*]] = extractelement <2 x i32 addrspace(1)*> [[VEC_RELOC_CASTED]], i32 1 +; CHECK-NEXT: ret i32 addrspace(1)* [[PTR]] +; +entry: + %base.i32 = bitcast i8 addrspace(1)* %base to i32 addrspace(1)* + %vec = getelementptr i32, i32 addrspace(1)* %base.i32, <2 x i64> %offsets + call void @do_safepoint() + %ptr = extractelement <2 x i32 addrspace(1)*> %vec, i32 1 + ret i32 addrspace(1)* %ptr +} + +define i32 addrspace(1)* @test5(<2 x i8 addrspace(1)*> %base, <2 x i64> %offsets) gc "cangjie" { +; CHECK-LABEL: @test5( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[BASE_I32:%.*]] = bitcast <2 x i8 addrspace(1)*> [[BASE:%.*]] to <2 x i32 addrspace(1)*> +; CHECK-NEXT: [[VEC:%.*]] = getelementptr i32, <2 x i32 addrspace(1)*> [[BASE_I32]], <2 x i64> [[OFFSETS:%.*]] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @do_safepoint, i32 0, i32 0) [ "gc-live"(<2 x i8 addrspace(1)*> [[BASE]]) ] +; CHECK-NEXT: [[BASE_RELOC:%.*]] = call coldcc <2 x i8 addrspace(1)*> @llvm.cj.gc.relocate.v2p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[BASE_I32_REMAT:%.*]] = bitcast <2 x i8 addrspace(1)*> [[BASE_RELOC]] to <2 x i32 addrspace(1)*> +; CHECK-NEXT: [[VEC_REMAT:%.*]] = getelementptr i32, <2 x i32 addrspace(1)*> [[BASE_I32_REMAT]], <2 x i64> [[OFFSETS]] +; CHECK-NEXT: [[PTR:%.*]] = extractelement <2 x i32 addrspace(1)*> [[VEC_REMAT]], i32 0 +; CHECK-NEXT: ret i32 addrspace(1)* [[PTR]] +; +entry: + %base.i32 = bitcast <2 x i8 addrspace(1)*> %base to <2 x i32 addrspace(1)*> + %vec = getelementptr i32, <2 x i32 addrspace(1)*> %base.i32, <2 x i64> %offsets + call void @do_safepoint() + %ptr = extractelement <2 x i32 addrspace(1)*> %vec, i32 0 + ret i32 addrspace(1)* %ptr +} + +define void @test6() gc "cangjie" { +; CHECK-LABEL: @test6( +; CHECK-NEXT: bb: +; CHECK-NEXT: br label [[HEADER:%.*]] +; CHECK: header: +; CHECK-NEXT: [[TMP:%.*]] = phi i8 addrspace(1)* [ [[TMP6:%.*]], [[LATCH:%.*]] ], [ undef, [[BB:%.*]] ] +; CHECK-NEXT: br label [[BB10:%.*]] +; CHECK: bb10: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @spam, i32 0, i32 0) +; CHECK-NEXT: br label [[BB25:%.*]] +; CHECK: bb25: +; CHECK-NEXT: [[TOKEN1:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, <2 x i8 addrspace(1)*> ()* @baz, i32 0, i32 0) +; CHECK-NEXT: [[TMP262:%.*]] = call <2 x i8 addrspace(1)*> @llvm.cj.gc.result.v2p1i8(token [[TOKEN1]]) +; CHECK-NEXT: [[TMP27:%.*]] = extractelement <2 x i8 addrspace(1)*> [[TMP262]], i32 0 +; CHECK-NEXT: br i1 undef, label [[BB7:%.*]], label [[LATCH]] +; CHECK: bb7: +; CHECK-NEXT: br label [[LATCH]] +; CHECK: latch: +; CHECK-NEXT: [[TMP6]] = phi i8 addrspace(1)* [ [[TMP27]], [[BB25]] ], [ [[TMP27]], [[BB7]] ] +; CHECK-NEXT: br label [[HEADER]] +; +bb: + br label %header + +header: ; preds = %latch, %bb + %tmp = phi i8 addrspace(1)* [ %tmp6, %latch ], [ undef, %bb ] + br label %bb10 + +bb10: ; preds = %bb2 + call void @spam() + br label %bb25 + +bb25: ; preds = %bb24 + %tmp26 = call <2 x i8 addrspace(1)*> @baz() + %tmp27 = extractelement <2 x i8 addrspace(1)*> %tmp26, i32 0 + br i1 undef, label %bb7, label %latch + +bb7: ; preds = %bb25 + br label %latch + +latch: ; preds = %bb25, %bb7 + %tmp6 = phi i8 addrspace(1)* [ %tmp27, %bb25 ], [ %tmp27, %bb7 ] + br label %header +} + +declare void @spam() +declare <2 x i8 addrspace(1)*> @baz() diff --git a/llvm/test/Transforms/CJRewriteStatepoint/single-base.ll b/llvm/test/Transforms/CJRewriteStatepoint/single-base.ll new file mode 100644 index 000000000..944c01330 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/single-base.ll @@ -0,0 +1,82 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -cj-rewrite-statepoint < %s | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128-ni:1-p2:32:8:8:32-ni:2" +target triple = "x86_64-unknown-linux-gnu" + +declare void @foo() gc "cangjie" +declare i1 @runtime_value() "gc-leaf-function" + +define i8 addrspace(1)* @test1(i8 addrspace(1)* %b1, i8 addrspace(1)* %b2) gc "cangjie" { +; CHECK-LABEL: @test1( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[B5:%.*]] = phi i8 addrspace(1)* [ [[B1:%.*]], [[ENTRY:%.*]] ], [ [[B6:%.*]], [[INNER_LOOP_LATCH:%.*]] ] +; CHECK-NEXT: [[C1:%.*]] = call i1 @runtime_value() +; CHECK-NEXT: br i1 [[C1]], label [[INNER_LOOP:%.*]], label [[EXIT:%.*]] +; CHECK: inner_loop: +; CHECK-NEXT: [[B6]] = phi i8 addrspace(1)* [ [[B5]], [[LOOP]] ], [ [[B6]], [[INNER_LOOP]] ] +; CHECK-NEXT: [[C2:%.*]] = call i1 @runtime_value() +; CHECK-NEXT: br i1 [[C2]], label [[INNER_LOOP]], label [[INNER_LOOP_LATCH]] +; CHECK: inner_loop_latch: +; CHECK-NEXT: [[C3:%.*]] = call i1 @runtime_value() +; CHECK-NEXT: br i1 [[C3]], label [[LOOP]], label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[MERGE:%.*]] = phi i8 addrspace(1)* [ [[B5]], [[LOOP]] ], [ [[B6]], [[INNER_LOOP_LATCH]] ] +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[MERGE]], i8 addrspace(1)* [[B1]]) ] +; CHECK-NEXT: [[MERGE_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[B1_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: ret i8 addrspace(1)* [[MERGE_RELOC]] +; +entry: + br label %loop + +loop: + %b5 = phi i8 addrspace(1)* [ %b1, %entry ], [ %b6, %inner_loop_latch ] + %c1 = call i1 @runtime_value() + br i1 %c1, label %inner_loop, label %exit + +inner_loop: + %b6 = phi i8 addrspace(1)* [ %b5, %loop ], [ %b6, %inner_loop ] + %c2 = call i1 @runtime_value() + br i1 %c2, label %inner_loop, label %inner_loop_latch + +inner_loop_latch: + %c3 = call i1 @runtime_value() + br i1 %c3, label %loop, label %exit + +exit: + %merge = phi i8 addrspace(1)* [ %b5, %loop ], [ %b6, %inner_loop_latch ] + call void @foo() + ret i8 addrspace(1)* %merge +} + +define i8 addrspace(1)* @swap(i8 addrspace(1)* %b1, i8 addrspace(1)* %b2) gc "cangjie" { +; CHECK-LABEL: @swap( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[B5:%.*]] = phi i8 addrspace(1)* [ [[B1:%.*]], [[ENTRY:%.*]] ], [ [[B6:%.*]], [[LOOP]] ] +; CHECK-NEXT: [[B6]] = phi i8 addrspace(1)* [ [[B1]], [[ENTRY]] ], [ [[B5]], [[LOOP]] ] +; CHECK-NEXT: [[C1:%.*]] = call i1 @runtime_value() +; CHECK-NEXT: br i1 [[C1]], label [[LOOP]], label [[EXIT:%.*]] +; CHECK: exit: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* [[B6]], i8 addrspace(1)* [[B1]]) ] +; CHECK-NEXT: [[B6_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[B1_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: ret i8 addrspace(1)* [[B6_RELOC]] +; +entry: + br label %loop + +loop: + %b5 = phi i8 addrspace(1)* [ %b1, %entry ], [ %b6, %loop ] + %b6 = phi i8 addrspace(1)* [ %b1, %entry ], [ %b5, %loop ] + %c1 = call i1 @runtime_value() + br i1 %c1, label %loop, label %exit + +exit: + call void @foo() + ret i8 addrspace(1)* %b6 +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-attrs.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-attrs.ll new file mode 100644 index 000000000..f0cb87819 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-attrs.ll @@ -0,0 +1,25 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals +; RUN: opt -S -cj-rewrite-statepoint < %s | FileCheck %s +; RUN: opt -S -passes=cj-rewrite-statepoint < %s | FileCheck %s +; Ensure statepoints copy (valid) attributes from callsites. + +declare void @f(i8 addrspace(1)* %obj) + +; copy over norecurse noimplicitfloat to statepoint call +define void @test1(i8 addrspace(1)* %arg) gc "cangjie" { +; CHECK-LABEL: @test1( +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @f, i32 1, i32 0, i8 addrspace(1)* [[ARG:%.*]]) #[[ATTR1:[0-9]+]] [ "gc-live"(i8 addrspace(1)* [[ARG]]) ] +; CHECK-NEXT: [[ARG_RELOCATED:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: ret void +; + + call void @f(i8 addrspace(1)* %arg) #1 + ret void +} + + +attributes #1 = { norecurse noimplicitfloat } +;. +; CHECK: attributes #[[ATTR0:[0-9]+]] = { nounwind readnone } +; CHECK: attributes #[[ATTR1]] = { noimplicitfloat norecurse } +;. diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-calling-conventions.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-calling-conventions.ll new file mode 100644 index 000000000..b15ca8e71 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-calling-conventions.ll @@ -0,0 +1,70 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +; Ensure that the gc.statepoint calls / invokes we generate carry over +; the right calling conventions. + +define i64 addrspace(1)* @test_invoke_format(i64 addrspace(1)* %obj, i64 addrspace(1)* %obj1) gc "cangjie" personality i32 ()* @personality { +; CHECK-LABEL: @test_invoke_format( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOKEN:%.*]] = invoke coldcc token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 addrspace(1)* (i64 addrspace(1)*)* @callee, i32 1, i32 0, i64 addrspace(1)* [[OBJ:%.*]]) [ "gc-live"(i64 addrspace(1)* [[OBJ1:%.*]], i64 addrspace(1)* [[OBJ]]) ] +; CHECK-NEXT: to label [[NORMAL_RETURN:%.*]] unwind label [[EXCEPTIONAL_RETURN:%.*]] +; CHECK: normal_return: +; CHECK-NEXT: [[RET_VAL1:%.*]] = call i64 addrspace(1)* @llvm.cj.gc.result.p1i64(token [[TOKEN]]) +; CHECK-NEXT: [[OBJ1_RELOC2:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ1_RELOC2_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ1_RELOC2]] to i64 addrspace(1)* +; CHECK-NEXT: [[OBJ_RELOC3:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[OBJ_RELOC3_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_RELOC3]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[RET_VAL1]] +; CHECK: exceptional_return: +; CHECK-NEXT: [[LANDING_PAD4:%.*]] = landingpad token +; CHECK-NEXT: cleanup +; CHECK-NEXT: [[OBJ1_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[LANDING_PAD4]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ1_RELOC_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ1_RELOC]] to i64 addrspace(1)* +; CHECK-NEXT: [[OBJ_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[LANDING_PAD4]], i32 1, i32 1) +; CHECK-NEXT: [[OBJ_RELOC_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_RELOC]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[OBJ1_RELOC_CASTED]] +; +entry: + %ret_val = invoke coldcc i64 addrspace(1)* @callee(i64 addrspace(1)* %obj) + to label %normal_return unwind label %exceptional_return + +normal_return: + ret i64 addrspace(1)* %ret_val + +exceptional_return: + %landing_pad4 = landingpad token + cleanup + ret i64 addrspace(1)* %obj1 +} + +define i64 addrspace(1)* @test_call_format(i64 addrspace(1)* %obj, i64 addrspace(1)* %obj1) gc "cangjie" { +; CHECK-LABEL: @test_call_format( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOKEN:%.*]] = call coldcc token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 addrspace(1)* (i64 addrspace(1)*)* @callee, i32 1, i32 0, i64 addrspace(1)* [[OBJ:%.*]]) [ "gc-live"(i64 addrspace(1)* [[OBJ]]) ] +; CHECK-NEXT: [[RET_VAL1:%.*]] = call i64 addrspace(1)* @llvm.cj.gc.result.p1i64(token [[TOKEN]]) +; CHECK-NEXT: [[OBJ_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOC_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_RELOC]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[RET_VAL1]] +; +entry: + %ret_val = call coldcc i64 addrspace(1)* @callee(i64 addrspace(1)* %obj) + ret i64 addrspace(1)* %ret_val +} + +; This function is inlined when inserting a poll. +declare void @do_safepoint() +define void @gc.safepoint_poll() { +; CHECK-LABEL: @gc.safepoint_poll( +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @do_safepoint() +; CHECK-NEXT: ret void +; +entry: + call void @do_safepoint() + ret void +} + +declare coldcc i64 addrspace(1)* @callee(i64 addrspace(1)*) +declare i32 @personality() diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-cangjie.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-cangjie.ll new file mode 100644 index 000000000..804481788 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-cangjie.ll @@ -0,0 +1,32 @@ +; RUN: opt < %s -S -cj-rewrite-statepoint | FileCheck %s +; RUN: opt < %s -S -passes=cj-rewrite-statepoint | FileCheck %s + +; Basic test to make sure that safepoints are placed +; for CoreCLR GC + +declare void @foo() + +define void @test_simple_call() gc "cangjie" { +; CHECK-LABEL: test_simple_call +entry: + br label %other +other: +; CHECK-LABEL: other +; CHECK: statepoint +; CHECK-NOT: gc.result + call void @foo() + ret void +} + +; This function is inlined when inserting a poll. To avoid recursive +; issues, make sure we don't place safepoints in it. +declare void @do_safepoint() +define void @gc.safepoint_poll() { +; CHECK-LABEL: gc.safepoint_poll +; CHECK-LABEL: entry +; CHECK-NEXT: do_safepoint +; CHECK-NEXT: ret void +entry: + call void @do_safepoint() + ret void +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-format.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-format.ll new file mode 100644 index 000000000..12bc3aa95 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-format.ll @@ -0,0 +1,70 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +; Ensure that the gc.statepoint calls / invokes we generate have the +; set of arguments we expect it to have. + +define i64 addrspace(1)* @test_invoke_format(i64 addrspace(1)* %obj, i64 addrspace(1)* %obj1) gc "cangjie" personality i32 ()* @personality { +; CHECK-LABEL: @test_invoke_format( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOKEN:%.*]] = invoke token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 addrspace(1)* (i64 addrspace(1)*)* @callee, i32 1, i32 0, i64 addrspace(1)* [[OBJ:%.*]]) [ "gc-live"(i64 addrspace(1)* [[OBJ1:%.*]], i64 addrspace(1)* [[OBJ]]) ] +; CHECK-NEXT: to label [[NORMAL_RETURN:%.*]] unwind label [[EXCEPTIONAL_RETURN:%.*]] +; CHECK: normal_return: +; CHECK-NEXT: [[RET_VAL1:%.*]] = call i64 addrspace(1)* @llvm.cj.gc.result.p1i64(token [[TOKEN]]) +; CHECK-NEXT: [[OBJ1_RELOC2:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ1_RELOC2_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ1_RELOC2]] to i64 addrspace(1)* +; CHECK-NEXT: [[OBJ_RELOC3:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: [[OBJ_RELOC3_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_RELOC3]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[RET_VAL1]] +; CHECK: exceptional_return: +; CHECK-NEXT: [[LANDING_PAD4:%.*]] = landingpad token +; CHECK-NEXT: cleanup +; CHECK-NEXT: [[OBJ1_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[LANDING_PAD4]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ1_RELOC_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ1_RELOC]] to i64 addrspace(1)* +; CHECK-NEXT: [[OBJ_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[LANDING_PAD4]], i32 1, i32 1) +; CHECK-NEXT: [[OBJ_RELOC_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_RELOC]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[OBJ1_RELOC_CASTED]] +; +entry: + %ret_val = invoke i64 addrspace(1)* @callee(i64 addrspace(1)* %obj) + to label %normal_return unwind label %exceptional_return + +normal_return: + ret i64 addrspace(1)* %ret_val + +exceptional_return: + %landing_pad4 = landingpad token + cleanup + ret i64 addrspace(1)* %obj1 +} + +define i64 addrspace(1)* @test_call_format(i64 addrspace(1)* %obj, i64 addrspace(1)* %obj1) gc "cangjie" { +; CHECK-LABEL: @test_call_format( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 addrspace(1)* (i64 addrspace(1)*)* @callee, i32 1, i32 0, i64 addrspace(1)* [[OBJ:%.*]]) [ "gc-live"(i64 addrspace(1)* [[OBJ]]) ] +; CHECK-NEXT: [[RET_VAL1:%.*]] = call i64 addrspace(1)* @llvm.cj.gc.result.p1i64(token [[TOKEN]]) +; CHECK-NEXT: [[OBJ_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 0, i32 0) +; CHECK-NEXT: [[OBJ_RELOC_CASTED:%.*]] = bitcast i8 addrspace(1)* [[OBJ_RELOC]] to i64 addrspace(1)* +; CHECK-NEXT: ret i64 addrspace(1)* [[RET_VAL1]] +; +entry: + %ret_val = call i64 addrspace(1)* @callee(i64 addrspace(1)* %obj) + ret i64 addrspace(1)* %ret_val +} + +; This function is inlined when inserting a poll. +declare void @do_safepoint() +define void @gc.safepoint_poll() { +; CHECK-LABEL: @gc.safepoint_poll( +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @do_safepoint() +; CHECK-NEXT: ret void +; +entry: + call void @do_safepoint() + ret void +} + +declare i64 addrspace(1)* @callee(i64 addrspace(1)*) +declare i32 @personality() diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-call.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-call.ll new file mode 100644 index 000000000..711cd3570 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-call.ll @@ -0,0 +1,32 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record = type { i64, i8 addrspace(1)* } + +declare void @g() #0 gc "cangjie" + +define %record @foo1(%record %arg) #0 gc "cangjie" { +; CHECK-LABEL: @foo1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @g, i32 0, i32 0) [ "gc-live"(%record [[ARG:%.*]]) ] +; +entry: + call void @g() + ret %record %arg +} + +define %record addrspace(1)* @foo2(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0) #0 gc "cangjie" { +; CHECK-LABEL: @foo2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @g, i32 0, i32 0) [ "gc-live"(%record addrspace(1)* [[ARG1:%.*]], i8 addrspace(1)* [[ARG0:%.*]]) ] +; CHECK-NEXT: [[ARG1_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[ARG1_RELOC_CASTED:%.*]] = bitcast i8 addrspace(1)* [[ARG1_RELOC]] to %record addrspace(1)* +; CHECK-NEXT: [[ARG0_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: ret %record addrspace(1)* [[ARG1_RELOC_CASTED]] +; +entry: + call void @g() + ret %record addrspace(1)* %arg1 +} + +attributes #0 = { "record_mut" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-getelementptr.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-getelementptr.ll new file mode 100644 index 000000000..03a8654c5 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-getelementptr.ll @@ -0,0 +1,39 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + + +%struct = type { i8 addrspace(1)* } + +define void @booo() gc "cangjie" { +entry: + %r = alloca %struct + br label %bb0 + +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @testcase, i32 0, i32 0) +; +bb0: + call void @testcase() + br label %bb1 + +bb1: + %0 = bitcast %struct* %r to i8* + %1 = addrspacecast %struct* %r to %struct addrspace(1)* + %2 = getelementptr inbounds %struct, %struct addrspace(1)* %1, i64 0, i32 0 ; CastInst and GEP are not a meaningful use point. + br label %bb2 + +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @testcase2, i32 1, i32 0, i8 addrspace(1)* %3) [ "gc-live"(i8 addrspace(1)* %3) ] +; +bb2: + call void @llvm.lifetime.start.p0i8(i64 8, i8* %0) + call void @llvm.memset.p0i8.i64(i8* %0, i8 0, i64 8, i1 false) + %3 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %2 + call void @testcase2(i8 addrspace(1)* %3) + ret void +} + + +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) + +declare void @testcase() gc "cangjie" +declare void @testcase2(i8 addrspace(1)* %arg0) gc "cangjie" diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-invoke.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-invoke.ll new file mode 100644 index 000000000..f4d1bfc9a --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-invoke.ll @@ -0,0 +1,59 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record = type { i64, i8 addrspace(1)* } + +declare void @g() #0 gc "cangjie" + +define %record @foo1(%record %arg) #0 gc "cangjie" personality i32 8 { +; CHECK-LABEL: @foo1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOKEN:%.*]] = invoke token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @g, i32 0, i32 0) [ "gc-live"(%record [[ARG:%.*]]) ] +; CHECK-NEXT: to label [[NORMAL_DEST:%.*]] unwind label [[UNWIND_DEST:%.*]] +; CHECK: normal_dest: +; CHECK-NEXT: ret %record [[ARG:%.*]] +; CHECK: unwind_dest: +; CHECK-NEXT: [[LPAD:%.*]] = landingpad token +; CHECK-NEXT: cleanup +; CHECK-NEXT: resume token undef +; +entry: + invoke void @g() to label %normal_dest unwind label %unwind_dest + +normal_dest: + ret %record %arg + +unwind_dest: + %lpad = landingpad token cleanup + resume token undef +} + +define %record addrspace(1)* @foo2(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0) #0 gc "cangjie" personality i32 8 { +; CHECK-LABEL: @foo2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOKEN:%.*]] = invoke token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @g, i32 0, i32 0) [ "gc-live"(%record addrspace(1)* [[ARG1:%.*]], i8 addrspace(1)* [[ARG0:%.*]]) ] +; CHECK-NEXT: to label [[NORMAL_DEST:%.*]] unwind label [[UNWIND_DEST:%.*]] +; CHECK: normal_dest: +; CHECK-NEXT: [[ARG1_RELOC1:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 0) +; CHECK-NEXT: [[ARG1_RELOC1_CASTED:%.*]] = bitcast i8 addrspace(1)* [[ARG1_RELOC1]] to %record addrspace(1)* +; CHECK-NEXT: [[ARG0_RELOC2:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[TOKEN]], i32 1, i32 1) +; CHECK-NEXT: ret %record addrspace(1)* [[ARG1_RELOC1_CASTED]] +; CHECK: unwind_dest: +; CHECK-NEXT: [[LPAD:%.*]] = landingpad token +; CHECK-NEXT: cleanup +; CHECK-NEXT: [[ARG1_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[LPAD]], i32 1, i32 0) +; CHECK-NEXT: [[ARG1_RELOC_CASTED:%.*]] = bitcast i8 addrspace(1)* [[ARG1_RELOC]] to %record addrspace(1)* +; CHECK-NEXT: [[ARG0_RELOC:%.*]] = call coldcc i8 addrspace(1)* @llvm.cj.gc.relocate.p1i8(token [[LPAD]], i32 1, i32 1) +; CHECK-NEXT: resume token undef +; +entry: + invoke void @g() to label %normal_dest unwind label %unwind_dest + +normal_dest: + ret %record addrspace(1)* %arg1 +unwind_dest: + %lpad = landingpad token cleanup + resume token undef +} + +attributes #0 = { "record_mut" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-lifetime-phi.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-lifetime-phi.ll new file mode 100644 index 000000000..3978c18f8 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-lifetime-phi.ll @@ -0,0 +1,57 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + + +%struct = type { i8 addrspace(1)* } + +define void @booo() gc "cangjie" { +entry: + %r1 = alloca %struct + %r2 = alloca %struct + %0 = bitcast %struct* %r1 to i8* + call void @llvm.memset.p0i8.i64(i8* %0, i8 0, i64 8, i1 false) + br label %bb0 + +; CHECK: bb0: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 ()* @tmp_use, i32 0, i32 0) [ "struct-live"(%struct* %r1, %struct* %r2) ] +; +bb0: + %1 = bitcast %struct* %r2 to i8* + %2 = call i64 @tmp_use() + %icmpeq.i = icmp eq i64 %2, 0 + br i1 %icmpeq.i, label %bb1, label %bb2 + +; CHECK: bb1: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @testcase1, i32 0, i32 0) [ "struct-live"(%struct* %r2, %struct* %r1) ] +; +bb1: + call void @llvm.lifetime.start.p0i8(i64 8, i8* %1) + call void @llvm.memset.p0i8.i64(i8* %1, i8 0, i64 8, i1 false) + call void @testcase1() + br label %bbend + +; CHECK: bb2: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @testcase2, i32 0, i32 0) [ "struct-live"(%struct* %r2, %struct* %r1) ] +; +bb2: + call void @testcase2() + br label %bbend + +; CHECK: bbend: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%struct addrspace(1)*)* @test, i32 1, i32 0, %struct addrspace(1)* %4) [ "struct-live"(%struct* %r1, %struct* %r2) ] +; +bbend: + %sink = phi %struct* [%r2, %bb1], [%r1, %bb2] + %3 = addrspacecast %struct* %sink to %struct addrspace(1)* + call void @test(%struct addrspace(1)* %3) + ret void +} + + +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) + +declare i64 @tmp_use() gc "cangjie" +declare void @testcase1() gc "cangjie" +declare void @testcase2() gc "cangjie" +declare void @test(%struct addrspace(1)* %arg0) gc "cangjie" diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-memcpy-phi.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-memcpy-phi.ll new file mode 100644 index 000000000..657c4281e --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-memcpy-phi.ll @@ -0,0 +1,67 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record = type { i8 addrspace(1)*, i64 } +%record1 = type { i8 addrspace(1)*, i64, i64 } +%record2 = type { i64, i64 } + + +define void @foo() #0 gc "cangjie" { +entry: + %atom.0 = alloca %record + %atom.1 = alloca %record + %atom.2 = alloca %record + %0 = bitcast %record* %atom.0 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %0, i8 0, i64 16, i1 false) + %1 = bitcast %record* %atom.1 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %1, i8 0, i64 16, i1 false) + %2 = bitcast %record* %atom.2 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %2, i8 0, i64 16, i1 false) + br label %body1 + +; CHECK: body1 +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record addrspace(1)*)* @tmp_use, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %3) [ "struct-live"(%record* %atom.2, %record* %atom.1) ] +; +body1: ; preds = %entry + %3 = addrspacecast %record* %atom.1 to %record addrspace(1)* + call void @tmp_use(i8 addrspace(1)* null, %record addrspace(1)* %3) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %0, i8* align 8 %1, i64 8, i1 false) + %4 = getelementptr inbounds %record, %record* %atom.0, i64 0, i32 1 + %ret = load i64, i64* %4, align 8 + %icmpeq.i = icmp eq i64 %ret, 0 + br i1 %icmpeq.i, label %tmp1, label %tmp2 + +; CHECK: tmp1: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record addrspace(1)*)* @foo_test, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %5) [ "struct-live"(%record* %atom.0, %record* %atom.2) ] +; +tmp1: ; preds = %body1 + %5 = addrspacecast %record* %atom.0 to %record addrspace(1)* + call void @foo_test(i8 addrspace(1)* null, %record addrspace(1)* %5) + br label %tmpend + +; CHECK: tmp2 +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record addrspace(1)*)* @foo_test, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %6) [ "struct-live"(%record* %atom.0, %record* %atom.2, %record* %atom.1) ] +; +tmp2: ; preds = %body1 + %6 = addrspacecast %record* %atom.1 to %record addrspace(1)* + call void @foo_test(i8 addrspace(1)* null, %record addrspace(1)* %6) + br label %tmpend + +; CHECK: tmpend +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record addrspace(1)*)* @tmp_use, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %7) [ "struct-live"(%record* %atom.2, %record* %atom.0) ] +; +tmpend: ; preds = %tmp2, %tmp1 + %in = phi %record* [ %atom.0, %tmp1 ], [ %atom.2, %tmp2 ] + %7 = addrspacecast %record* %in to %record addrspace(1)* + call void @tmp_use(i8 addrspace(1)* null, %record addrspace(1)* %7) + ret void +} + + +declare void @foo_test(i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1) #0 gc "cangjie" +declare void @tmp_use(i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1) #0 gc "cangjie" + +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) + +attributes #0 = { "hasRcdParam" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi-constant.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi-constant.ll new file mode 100644 index 000000000..76439644f --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi-constant.ll @@ -0,0 +1,41 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record = type { i8 addrspace(1)* } +@g_var = global %record zeroinitializer + +define void @foo(i1 %cond) #0 gc "cangjie" { +entry: + %atom = alloca %record + %atom.1 = alloca %record + %0 = bitcast %record* %atom to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %0, i8 0, i64 8, i1 false) + %1 = bitcast %record* %atom.1 to i8* + br i1 %cond, label %tmp1, label %tmp2 + +; CHECK: tmp1 +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record addrspace(1)*)* @tmp_use, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %2) [ "struct-live"(%record* %atom) ] +; +tmp1: ; preds = %body1 + %2 = addrspacecast %record* %atom to %record addrspace(1)* + call void @tmp_use(i8 addrspace(1)* null, %record addrspace(1)* %2) + br label %tmp2 + +; CHECK: tmp2 +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @test, i32 0, i32 0) [ "struct-live"(%record* %atom) ] +; +tmp2: ; preds = %entry, %tmp1 + %sink = phi i8* [ bitcast (%record* @g_var to i8*), %entry ], [ %0, %tmp1 ] + call void @test() + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %1, i8* %sink, i64 8, i1 false) + ret void +} + + +declare void @tmp_use(i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1) #0 gc "cangjie" +declare void @test() gc "cangjie" + +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) + +attributes #0 = { "hasRcdParam" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi-i64ptr.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi-i64ptr.ll new file mode 100644 index 000000000..16bd3962e --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi-i64ptr.ll @@ -0,0 +1,51 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record0 = type { i8 addrspace(1)* } +%record1 = type { i8 addrspace(1)*, i64, i64 } + +define void @foo() #0 gc "cangjie" { +entry: + %atom.0 = alloca %record0 + %atom.1 = alloca %record1 + %atom.2 = alloca %record1 + %0 = bitcast %record0* %atom.0 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %0, i8 0, i64 8, i1 false) + %1 = bitcast %record1* %atom.1 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %1, i8 0, i64 16, i1 false) + %2 = getelementptr inbounds %record1, %record1* %atom.1, i64 0, i32 2 + %count = load i64, i64* %2, align 8 + %icmpeq.i = icmp eq i64 %count, 0 + br i1 %icmpeq.i, label %tmp1, label %tmpend1 + +; CHECK: tmp1 +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @foo_test, i32 0, i32 0) [ "struct-live"(%record1* %atom.2, %record0* %atom.0) ] +; +tmp1: ; preds = %entry + call void @foo_test() + %3 = bitcast %record0* %atom.0 to i8 addrspace(1)** + %v = load i8 addrspace(1)*, i8 addrspace(1)** %3, align 8 + %ptr = bitcast %record1* %atom.1 to i8 addrspace(1)** + store i8 addrspace(1)* %v, i8 addrspace(1)** %ptr + br label %tmpend1 + +; CHECK: tmpend1 +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @tmp_test1, i32 0, i32 0) [ "struct-live"(%record1* %atom.2, %record1* %atom.1) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @tmp_test2, i32 0, i32 0) +; +tmpend1: ; preds = %entry, %tmp1 + %sink = phi %record1* [ %atom.1, %entry ], [ %atom.2, %tmp1 ] + %sink.ptr = getelementptr inbounds %record1, %record1* %sink, i64 0, i32 0 + call void @tmp_test1() + %ptr1 = load i8 addrspace(1)*, i8 addrspace(1)** %sink.ptr + %sink.value = getelementptr inbounds %record1, %record1* %sink, i64 0, i32 2 + call void @tmp_test2() + %value1 = load i64, i64* %sink.value + ret void +} + + +declare void @foo_test() gc "cangjie" +declare void @tmp_test1() gc "cangjie" +declare void @tmp_test2() gc "cangjie" +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi-i8ptr.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi-i8ptr.ll new file mode 100644 index 000000000..94d916a9f --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi-i8ptr.ll @@ -0,0 +1,105 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record0 = type { i8 addrspace(1)* } +%record1 = type { i8 addrspace(1)*, i64, i64 } +%record2 = type { i64, i64 } + + +define void @foo() #0 gc "cangjie" { +entry: + %atom.0 = alloca %record0 + %atom.1 = alloca %record1 + %atom.2 = alloca %record2 + %atom.3 = alloca %record2 + %atom.4 = alloca %record2 + %0 = bitcast %record0* %atom.0 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %0, i8 0, i64 8, i1 false) + %1 = bitcast %record1* %atom.1 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %1, i8 0, i64 16, i1 false) + %2 = bitcast %record2* %atom.2 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %2, i8 0, i64 16, i1 false) + %3 = bitcast %record2* %atom.3 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %3, i8 0, i64 16, i1 false) + %4 = bitcast %record2* %atom.4 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %4, i8 0, i64 16, i1 false) + br label %body1 + +; CHECK: body1 +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record1 addrspace(1)*)* @tmp_use, i32 2, i32 0, i8 addrspace(1)* null, %record1 addrspace(1)* %5) [ "struct-live"(%record1* %atom.1) ] +; +body1: ; preds = %entry + %5 = addrspacecast %record1* %atom.1 to %record1 addrspace(1)* + %atom.1.cast = bitcast %record1 addrspace(1)* %5 to i8 addrspace(1)* + call void @tmp_use(i8 addrspace(1)* null, %record1 addrspace(1)* %5) + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %0, i8 addrspace(1)* align 8 %atom.1.cast, i64 8, i1 false) + %6 = getelementptr inbounds %record2, %record2* %atom.3, i64 0, i32 0 + %count = load i64, i64* %6, align 8 + %7 = and i64 %count, 1 + %icmpeq.i = icmp eq i64 %7, 0 + br i1 %icmpeq.i, label %tmp1, label %tmp2 + +; CHECK: tmp1 +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record1 addrspace(1)*, i8 addrspace(1)*, %record1 addrspace(1)*)* @default_foo_test, i32 4, i32 0, i8 addrspace(1)* null, %record1 addrspace(1)* %5, i8 addrspace(1)* null, %record1 addrspace(1)* %9) [ "struct-live"(%record0* %atom.0, %record1* %atom.1) ] +; +tmp1: ; preds = %body1 + %8 = bitcast %record2* %atom.4 to i8* + %9 = addrspacecast %record2* %atom.4 to %record1 addrspace(1)* + call void @default_foo_test(i8 addrspace(1)* null, %record1 addrspace(1)* %5, i8 addrspace(1)* null, %record1 addrspace(1)* %9) + br label %tmpend1 + +; CHECK: tmp2 +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record1 addrspace(1)*, i8 addrspace(1)*, %record1 addrspace(1)*)* @default_foo_test, i32 4, i32 0, i8 addrspace(1)* null, %record1 addrspace(1)* %5, i8 addrspace(1)* null, %record1 addrspace(1)* %11) [ "struct-live"(%record0* %atom.0, %record1* %atom.1) ] +; +tmp2: ; preds = %body1 + %10 = bitcast %record2* %atom.3 to i8* + %11 = addrspacecast %record2* %atom.3 to %record1 addrspace(1)* + call void @default_foo_test(i8 addrspace(1)* null, %record1 addrspace(1)* %5, i8 addrspace(1)* null, %record1 addrspace(1)* %11) + br label %tmpend1 + +; CHECK: tmpend1 +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record1 addrspace(1)*)* @tmp_use, i32 2, i32 0, i8 addrspace(1)* null, %record1 addrspace(1)* %5) [ "struct-live"(%record0* %atom.0, %record1* %atom.1) ] +; CHECK: [[TOKEN:%.*]] = tail call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8*, i32)* @CJ_MCC_NewObjectStub, i32 2, i32 0, i8* %.sink, i32 40) [ "struct-live"(%record0* %atom.0, %record1* %atom.1) ] +; +tmpend1: ; preds = %tmp2, %tmp1 + %.sink = phi i8* [ %10, %tmp2 ], [ %8, %tmp1 ] + call void @tmp_use(i8 addrspace(1)* null, %record1 addrspace(1)* %5) + %12 = tail call i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* %.sink, i32 40) + %13 = getelementptr inbounds i8, i8 addrspace(1)* %12, i64 8 + %14 = addrspacecast i8* %.sink to i8 addrspace(1)* + %15 = call cangjiegccc i32 @GetGCPhase() + %16 = icmp sle i32 %15, 8 + br i1 %16, label %gcNoRunning2, label %gcRunning0 + +gcRunning0: ; preds = %tmpend1 + call void @llvm.cj.gcwrite.struct.p1i8(i8 addrspace(1)* %12, i8 addrspace(1)* %13, i8 addrspace(1)* %14, i64 16) + br label %storeFinish1 + +gcNoRunning2: ; preds = %tmpend1 + call void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* align 8 %13, i8* align 8 %.sink, i64 16, i1 false) + br label %storeFinish1 + +; CHECK: storeFinish1 +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record1 addrspace(1)*)* @tmp_use, i32 2, i32 0, i8 addrspace(1)* null, %record1 addrspace(1)* %5) [ "struct-live"(%record0* %atom.0, %record1* %atom.1) ] +; +storeFinish1: ; preds = %gcNoRunning2, %gcRunning0 + call void @tmp_use(i8 addrspace(1)* null, %record1 addrspace(1)* %5) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 8 %1, i8* nonnull align 8 %0, i64 8, i1 false) + ret void +} + + +declare void @default_foo_test(i8 addrspace(1)* %arg0, %record1 addrspace(1)* %arg1, i8 addrspace(1)* %arg2, %record1 addrspace(1)* %arg3) #0 gc "cangjie" +declare void @tmp_use(i8 addrspace(1)* %arg0, %record1 addrspace(1)* %arg1) #0 gc "cangjie" + +declare cangjiegccc i32 @GetGCPhase() #1 +declare i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8*, i32) +declare void @llvm.cj.gcwrite.struct.p1i8(i8 addrspace(1)*, i8 addrspace(1)* nocapture writeonly, i8 addrspace(1)* nocapture readonly, i64) + +declare void @llvm.memcpy.p1i8.p0i8.i64(i8 addrspace(1)* nocapture writeonly, i8* nocapture readonly, i64, i1 immarg) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memcpy.p0i8.p1i8.i64(i8* nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) + +attributes #0 = { "hasRcdParam" } +attributes #1 = { "cj-runtime" "gc-leaf-function" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi1.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi1.ll new file mode 100644 index 000000000..072718b53 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi1.ll @@ -0,0 +1,38 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record = type { i32, i8 addrspace(1)* } + +declare void @g0(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0) #0 gc "cangjie" +declare void @g1(%record %arg0) #0 gc "cangjie" + +define i8 addrspace(1)* @foo(i1 %cond, %record %arg0, %record addrspace(1)* %arg2, i8 addrspace(1)* %arg1) #0 gc "cangjie" { +entry: + %0 = alloca %record + %1 = getelementptr inbounds %record, %record* %0, i32 0, i32 1 + store %record %arg0, %record* %0 + br i1 %cond, label %for, label %end + +; CHECK: for: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%record addrspace(1)*, i8 addrspace(1)*)* @g0, i32 2, i32 0, %record addrspace(1)* %.0, i8 addrspace(1)* %.03) [ "gc-live"(%record addrspace(1)* %.0, i8 addrspace(1)* %.03, %record %arg0), "struct-live"(%record* %0) ] +; +for: ; preds = %entry, %for + %phi_i = phi i32 [ 0, %entry ], [ %i, %for ] + %phi = phi i8 addrspace(1)** [ %1, %entry], [ %2, %for ] + %i = add i32 %phi_i, 1 + %2 = getelementptr inbounds i8 addrspace(1)*, i8 addrspace(1)** %phi, i32 0 + call void @g0(%record addrspace(1)* %arg2, i8 addrspace(1)* %arg1) + %loop.cond = icmp ult i32 %i, 10 + br i1 %loop.cond, label %for, label %end + +; CHECK: end: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%record)* @g1, i32 1, i32 0, %record %arg0) [ "gc-live"(i8 addrspace(1)* %load, %record %arg0) ] +; +end: ; preds = %entry, %for + %phi_end = phi i8 addrspace(1)** [ %1, %entry], [ %phi, %for ] + %load = load i8 addrspace(1)*, i8 addrspace(1)** %phi_end + call void @g1(%record %arg0) + ret i8 addrspace(1)* %load +} + +attributes #0 = { "record_mut" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi2.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi2.ll new file mode 100644 index 000000000..fadd1bac6 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi2.ll @@ -0,0 +1,120 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record = type { i32, i8 addrspace(1)* } + +declare void @g0(i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1) #0 gc "cangjie" +declare i8 @g1(%record %arg0, i64 %arg1) #0 gc "cangjie" +declare void @g2(i8 addrspace(1)* %arg0, i64 %arg1) #0 gc "cangjie" +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memcpy.p0i8.p1i8.i64(i8* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) + +define void @foo(i64 %index, i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1, %record %arg2) #0 gc "cangjie" { +entry: + %0 = alloca %record + %1 = alloca %record + %2 = alloca %record + %3 = alloca i8 + %4 = alloca i64 + br label %body1 + +; CHECK: body1: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record addrspace(1)*)* @g0, i32 2, i32 0, i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1) [ "gc-live"(i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1, %record %arg2), "struct-live"(%record* %1) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 (%record, i64)* @g1, i32 2, i32 0, %record %arg2, i64 0) [ "gc-live"(i8 addrspace(1)* %arg0.reloc, %record addrspace(1)* %arg1.reloc.casted, %record %arg2), "struct-live"(%record* %1) ] +; +body1: ; preds = %entry + %sub = sub i64 %index, 1 + store i64 %sub, i64* %4 + store i8 0, i8* %3 + %5 = bitcast %record* %1 to i8* + %6 = add i64 %sub, 1 + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %5, i8 addrspace(1)* align 8 %arg0, i64 16, i1 false) + call void @g0(i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1) + %7 = bitcast %record* %2 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %7, i8 addrspace(1)* align 8 %arg0, i64 16, i1 false) + %8 = call i8 @g1(%record %arg2, i64 0) + br label %body2 + +body2: ; preds = %ifthen2, %body1 + %9 = load i64, i64* %4 + %icmpsge = icmp sge i64 %9, 0 + br i1 %icmpsge, label %ifthen1, label %ifelse1 + +; CHECK: ifthen1: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 (%record, i64)* @g1, i32 2, i32 0, %record %arg2, i64 %9) [ "gc-live"(i8 addrspace(1)* %.0, %record addrspace(1)* %.017, %record %arg2), "struct-live"(%record* %1) ] +; +ifthen1: ; preds = %body2 + %10 = load i64, i64* %4 + %11 = call i8 @g1(%record %arg2, i64 %10) + store i8 %11, i8* %3 + %12 = load i8, i8* %3 + %13 = load i8, i8 addrspace(1)* %arg0 + %icmpuge = icmp uge i8 %12, %13 + br i1 %icmpuge, label %tmp1, label %tmp2 + +ifelse1: ; preds = %body2 + br label %ifelseend + +tmp1: ; preds = %ifthen1 + %14 = load i8, i8* %3 + %15 = load i8, i8 addrspace(1)* %arg0 + %icmpult = icmp ult i8 %14, %15 + br label %tmpend1 + +tmp2: ; preds = %ifthen1 + br label %tmpend1 + +tmpend1: ; preds = %tmp1, %tmp2 + %conv = phi i1 [ false, %tmp2 ], [ %icmpult, %tmp1 ] + br i1 %conv, label %ifthen2, label %ifelse2 + +ifthen2: ; preds = %tmpend1 + %16 = load i64, i64* %4 + %sub2 = sub i64 %16, 1 + store i64 %sub2, i64* %4 + br label %body2 + +; CHECK: ifelse2: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, i64)* @g2, i32 2, i32 0, i8 addrspace(1)* %arg0.reloc7, i64 %16) [ "gc-live"(i8 addrspace(1)* %arg0.reloc7, %record addrspace(1)* %arg1.reloc8.casted), "struct-live"(%record* %1) ] +; +ifelse2: ; preds = %tmpend1 + %17 = load i64, i64* %4 + %18 = bitcast %record* %1 to i8* + %19 = bitcast %record* %0 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %19, i8* align 8 %18, i64 16, i1 false) + call void @g2(i8 addrspace(1)* %arg0, i64 %17) + br label %ifelseend + +ifelseend: ; preds = %ifelse1, %ifelse2 + %20 = getelementptr inbounds %record, %record* %1, i32 0, i32 0 + %21 = load i32, i32* %20 + %icmpeq = icmp eq i32 %21, 0 + br i1 %icmpeq, label %tmp3, label %tmp4 + +tmp3: ; preds = %ifelseend + %22 = getelementptr inbounds %record, %record* %1, i32 0, i32 1 + %23 = load i8 addrspace(1)*, i8 addrspace(1)** %22 + %24 = load i8, i8 addrspace(1)* %23 + %25 = add i8 %24, 1 + %icmpeq6 = icmp eq i8 %25, -1 + br label %tmpend2 + +tmp4: ; preds = %ifelseend + br label %tmpend2 + +tmpend2: ; preds = %tmp3, %tmp4 + %conv8 = phi i1 [ false, %tmp4 ], [ %icmpeq6, %tmp3 ] + br i1 %conv8, label %ifthen3, label %ifelse3 + +; CHECK: ifthen3: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record addrspace(1)*)* @g0, i32 2, i32 0, i8 addrspace(1)* %.1, %record addrspace(1)* %.118) [ "gc-live"(i8 addrspace(1)* %.1, %record addrspace(1)* %.118) ] +; +ifthen3: ; preds = %tmpend2 + call void @g0(i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1) + ret void + +ifelse3: ; preds = %tmpend2 + ret void +} + +attributes #0 = { "hasRcdParam" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi3.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi3.ll new file mode 100644 index 000000000..28dec6f87 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi3.ll @@ -0,0 +1,179 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record1 = type { i8*, i8 addrspace(1)* } +%record2 = type { i8 addrspace(1)*, i64 } +%object = type { %record2, i64 } +%array = type { %object, [0 x i8] } + +declare i64 @g0(%record2 addrspace(1)* %arg2, i8 addrspace(1)* %arg0) #0 gc "cangjie" +declare i64 @g1(i8 addrspace(1)* %arg) #0 gc "cangjie" +declare i64 @g2(%record2 addrspace(1)* %arg1, i8 addrspace(1)* %arg0) #0 gc "cangjie" +declare void @g3(i8 addrspace(1)* %arg) #0 gc "cangjie" +declare void @g4(i8 addrspace(1)* %arg) #0 gc "cangjie" +declare void @g5(i8 addrspace(1)* %arg0, i8 addrspace(1)* %arg1, i64 %arg2, i64 %arg3, i64 %arg4) #0 gc "cangjie" +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memcpy.p0i8.p1i8.i64(i8* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) + +define void @foo(%record2 addrspace(1)* %arg1, i8 addrspace(1)* %arg0, %record2 addrspace(1)* %arg3, i8 addrspace(1)* %arg2, %record2 addrspace(1)* %arg5, i8 addrspace(1)* %arg4) #0 gc "cangjie" { +entry: + %0 = alloca %record2 + %1 = alloca i64 + %2 = alloca i64 + %3 = alloca i64 + %4 = alloca i64 + %5 = alloca i8 addrspace(1)* + %6 = alloca i64 + %7 = alloca i64 + %8 = alloca %record1 + %9 = alloca %record1 + %10 = alloca %record1 + %11 = alloca %record1 + %12 = alloca i64 + %13 = alloca i8* + %14 = alloca i8* + %15 = alloca %record2 + %16 = alloca i64 + %17 = alloca i64 + %18 = alloca i64 + %19 = alloca i8 addrspace(1)* + %20 = alloca i64 + %21 = alloca i64 + %22 = alloca i64 + %23 = alloca i64 + %24 = alloca i64 + %25 = alloca i8 addrspace(1)* + %26 = alloca i8 addrspace(1)* + %27 = alloca %record2 + %28 = bitcast %record2* %0 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %28, i8 addrspace(1)* align 8 %arg0, i64 16, i1 false) + %29 = bitcast %record1* %8 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %29, i8 addrspace(1)* align 8 %arg0, i64 16, i1 false) + %30 = bitcast %record1* %9 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %30, i8 addrspace(1)* align 8 %arg0, i64 16, i1 false) + %31 = bitcast %record1* %10 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %31, i8 addrspace(1)* align 8 %arg0, i64 16, i1 false) + %32 = bitcast %record1* %11 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %32, i8 addrspace(1)* align 8 %arg0, i64 16, i1 false) + %33 = bitcast %record2* %15 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %33, i8 addrspace(1)* align 8 %arg0, i64 16, i1 false) + br label %start1 + +start1: ; preds = %entry + br label %body1 + +; CHECK: body1: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 (%record2 addrspace(1)*, i8 addrspace(1)*)* @g0, i32 2, i32 0, %record2 addrspace(1)* %arg1, i8 addrspace(1)* %arg0) [ "gc-live"(%record2 addrspace(1)* %arg5, i8 addrspace(1)* %arg0, i8 addrspace(1)* %arg4, i8 addrspace(1)* %arg2, %record2 addrspace(1)* %arg1, %record2 addrspace(1)* %arg3) ] +; +body1: ; preds = %start1 + %34 = call i64 @g0(%record2 addrspace(1)* %arg1, i8 addrspace(1)* %arg0) + %icmpeq = icmp eq i64 %34, 0 + br i1 %icmpeq, label %tmp1, label %tmp2 + +tmp2: ; preds = %body1 + br label %tmpend1 + +; CHECK: tmp1: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 (%record2 addrspace(1)*, i8 addrspace(1)*)* @g0, i32 2, i32 0, %record2 addrspace(1)* %arg3.reloc.casted, i8 addrspace(1)* %arg2.reloc) [ "gc-live"(%record2 addrspace(1)* %arg5.reloc.casted, i8 addrspace(1)* %arg0.reloc, i8 addrspace(1)* %arg4.reloc, i8 addrspace(1)* %arg2.reloc, %record2 addrspace(1)* %arg1.reloc.casted, %record2 addrspace(1)* %arg3.reloc.casted) ] +; +tmp1: ; preds = %body1 + %35 = call i64 @g0(%record2 addrspace(1)* %arg3, i8 addrspace(1)* %arg2) + %icmpeq1 = icmp eq i64 %35, 0 + br label %tmpend1 + +tmpend1: ; preds = %tmp1, %tmp2 + %conv = phi i1 [ false, %tmp2 ], [ %icmpeq1, %tmp1 ] + br i1 %conv, label %ifthen1, label %ifelse1 + +ifthen1: ; preds = %tmpend1 + %36 = bitcast %record2* %27 to i8* + %37 = bitcast %record2 addrspace(1)* %arg5 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %36, i8 addrspace(1)* align 8 %37, i64 16, i1 false) + ret void + +ifelse1: ; preds = %tmpend1 + br label %ifend1 + +; CHECK: ifend1: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 (i8 addrspace(1)*)* @g1, i32 1, i32 0, i8 addrspace(1)* %42) [ "gc-live"(i8 addrspace(1)* %.059, i8 addrspace(1)* %.060, i8 addrspace(1)* %.061, %record2 addrspace(1)* %.0, %record2 addrspace(1)* %.062, i8 addrspace(1)* %42), "struct-live"(i8 addrspace(1)** %25) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 (i8 addrspace(1)*)* @g1, i32 1, i32 0, i8 addrspace(1)* %45) [ "gc-live"(i8 addrspace(1)* %arg0.reloc11, i8 addrspace(1)* %arg4.reloc12, i8 addrspace(1)* %arg2.reloc13, %record2 addrspace(1)* %arg5.reloc14.casted, %record2 addrspace(1)* %arg1.reloc15.casted, i8 addrspace(1)* %45), "struct-live"(i8 addrspace(1)** %25) ] +; +ifend1: ; preds = %ifelse1 + %38 = getelementptr inbounds %record2, %record2 addrspace(1)* %arg3, i32 0, i32 0 + %39 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %38 + store i8 addrspace(1)* %39, i8 addrspace(1)** %26 + %40 = getelementptr inbounds %record2, %record2 addrspace(1)* %arg5, i32 0, i32 0 + %41 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %40 + store i8 addrspace(1)* %41, i8 addrspace(1)** %25 + %42 = load i8 addrspace(1)*, i8 addrspace(1)** %26 + %43 = call i64 @g1(i8 addrspace(1)* %42) + store i64 %43, i64* %24 + %44 = load i8 addrspace(1)*, i8 addrspace(1)** %25 + %45 = call i64 @g1(i8 addrspace(1)* %44) + store i64 %45, i64* %23 + %46 = load i64, i64* %24 + %icmpeq2 = icmp eq i64 %46, 0 + br i1 %icmpeq2, label %ifthen2, label %ifelse2 + +; CHECK: ifthen2: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 (%record2 addrspace(1)*, i8 addrspace(1)*)* @g2, i32 2, i32 0, %record2 addrspace(1)* %arg1.reloc22.casted, i8 addrspace(1)* %arg0.reloc18) [ "gc-live"(i8 addrspace(1)* %arg0.reloc18, i8 addrspace(1)* %arg4.reloc19, i8 addrspace(1)* %arg2.reloc20, %record2 addrspace(1)* %arg5.reloc21.casted, %record2 addrspace(1)* %arg1.reloc22.casted), "struct-live"(i8 addrspace(1)** %25) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 (%record2 addrspace(1)*, i8 addrspace(1)*)* @g0, i32 2, i32 0, %record2 addrspace(1)* %arg1.reloc29.casted, i8 addrspace(1)* %arg0.reloc25) [ "gc-live"(i8 addrspace(1)* %arg0.reloc25, i8 addrspace(1)* %arg4.reloc26, i8 addrspace(1)* %arg2.reloc27, %record2 addrspace(1)* %arg5.reloc28.casted, %record2 addrspace(1)* %arg1.reloc29.casted), "struct-live"(i8 addrspace(1)** %25) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 (%record2 addrspace(1)*, i8 addrspace(1)*)* @g2, i32 2, i32 0, %record2 addrspace(1)* %arg1.reloc36.casted, i8 addrspace(1)* %arg0.reloc32) [ "gc-live"(i8 addrspace(1)* %arg0.reloc32, i8 addrspace(1)* %arg4.reloc33, i8 addrspace(1)* %arg2.reloc34, %record2 addrspace(1)* %arg5.reloc35.casted, %record2 addrspace(1)* %arg1.reloc36.casted), "struct-live"(i8 addrspace(1)** %25) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 (%record2 addrspace(1)*, i8 addrspace(1)*)* @g2, i32 2, i32 0, %record2 addrspace(1)* %arg5.reloc42.casted, i8 addrspace(1)* %arg4.reloc40) [ "gc-live"(i8 addrspace(1)* %arg0.reloc39, i8 addrspace(1)* %arg4.reloc40, i8 addrspace(1)* %arg2.reloc41, %record2 addrspace(1)* %arg5.reloc42.casted), "struct-live"(i8 addrspace(1)** %25) ] +; +ifthen2: ; preds = %ifend1 + %47 = call i64 @g2(%record2 addrspace(1)* %arg1, i8 addrspace(1)* %arg0) + %add = add i64 %47, 1 + store i64 %add, i64* %22 + %48 = call i64 @g0(%record2 addrspace(1)* %arg1, i8 addrspace(1)* %arg0) + %49 = load i64, i64* %23 + %50 = load i64, i64* %22 + %mul = mul i64 %49, %50 + %add3 = add i64 %48, %mul + store i64 %add3, i64* %21 + %51 = call i64 @g2(%record2 addrspace(1)* %arg1, i8 addrspace(1)* %arg0) + %52 = call i64 @g2(%record2 addrspace(1)* %arg5, i8 addrspace(1)* %arg4) + %53 = load i64, i64* %22 + %mul4 = mul i64 %52, %53 + %add5 = add i64 %51, %mul4 + store i64 %add5, i64* %20 + %54 = load i64, i64* %21 + %icmpsge1 = icmp sge i64 %54, 0 + br i1 %icmpsge1, label %tmp3, label %tmp4 + +; CHECK: tmp4: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %arg0.reloc46) [ "gc-live"(i8 addrspace(1)* %arg2.reloc48, i8 addrspace(1)* %arg0.reloc46) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g4, i32 1, i32 0, i8 addrspace(1)* %arg2.reloc52) [ "gc-live"(i8 addrspace(1)* %arg2.reloc52) ] +; +tmp4: ; preds = %ifthen2 + call void @g3(i8 addrspace(1)* %arg0) + call void @g4(i8 addrspace(1)* %arg2) + ret void + +; CHECK: tmp3: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, i8 addrspace(1)*, i64, i64, i64)* @g5, i32 5, i32 0, i8 addrspace(1)* %60, i8 addrspace(1)* %61, i64 0, i64 %62, i64 %63) [ "gc-live"(i8 addrspace(1)* %60, i8 addrspace(1)* %61) ] +; +tmp3: ; preds = %ifthen2 + %55 = add i64 %54, 1 + %56 = add i64 %55, 1 + %57 = bitcast i8 addrspace(1)* %arg4 to %array addrspace(1)* + store i8 addrspace(1)* %arg0, i8 addrspace(1)** %19 + store i64 0, i64* %18 + store i64 0, i64* %17 + store i64 0, i64* %16 + %58 = load i8 addrspace(1)*, i8 addrspace(1)** %25 + %59 = load i8 addrspace(1)*, i8 addrspace(1)** %19 + %60 = load i64, i64* %16 + %61 = load i64, i64* %23 + call void @g5(i8 addrspace(1)* %58, i8 addrspace(1)* %59, i64 0, i64 %60, i64 %61) + %62 = load i64, i64* %16 + %63 = load i64, i64* %23 + %add6 = add i64 %62, %63 + store i64 %add6, i64* %16 + ret void + +ifelse2: ; preds = %ifend1 + ret void +} + +attributes #0 = { "record_mut" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi4.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi4.ll new file mode 100644 index 000000000..ce3edf35f --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi4.ll @@ -0,0 +1,601 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record = type { i8 addrspace(1)*, i64 } +%object = type { %record, i64 } +%array = type { %object, [0 x i8] } +%struct = type { i64, i64 } + +@data = global i64 0 + +declare i64 @g0(i8 addrspace(1)* %arg) #0 gc "cangjie" +declare i64 @g1(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0) #0 gc "cangjie" +declare void @g2(%record addrspace(1)* %arg2, i8 addrspace(1)* %arg1) #0 gc "cangjie" +declare i8 addrspace(1)* @g3(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0, i64 %arg2, i64 %arg3) #0 gc "cangjie" +declare void @g4(i8 addrspace(1)* %arg0, i64 %arg1) #0 gc "cangjie" +declare i64 @g5(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0, i64 %arg2) #0 gc "cangjie" +declare void @g6(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0, i64 %arg2, i64 %arg3) #0 gc "cangjie" +declare void @g7(i8 addrspace(1)* %arg0, i8 addrspace(1)* %arg1, i64 %arg2, i64 %arg3, i64 %arg4) #0 gc "cangjie" +declare i64 @g8(i64 %arg0, i64 %arg1) #0 gc "cangjie" +declare i1 @g9(i8 addrspace(1)* %arg) #0 gc "cangjie" +declare void @g10(i8 addrspace(1)* %arg) #0 gc "cangjie" +declare i1 @g11(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0) #0 gc "cangjie" +declare i64 @g12(i8 addrspace(1)* %arg0, i64 %arg1) #0 gc "cangjie" +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memcpy.p0i8.p1i8.i64(i8* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) + +define void @foo(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg2, i8 addrspace(1)* %arg0) #0 gc "cangjie" { +entry: + %0 = alloca %record + %1 = alloca i64 + %2 = alloca %record + %3 = alloca %record + %4 = alloca %record + %5 = alloca i8 addrspace(1)* + %6 = alloca i1 + %7 = alloca i64 + %8 = alloca i64 + %9 = alloca i64 + %10 = alloca i64 + %11 = alloca i64 + %12 = alloca i64 + %13 = alloca i64 + %14 = alloca %struct + %15 = alloca i8 addrspace(1)* + %16 = alloca i64 + %17 = alloca i64 + %18 = alloca %record + %19 = alloca i64 + %20 = alloca i1 + %21 = alloca i64 + %22 = alloca i64 + %23 = alloca i64 + %24 = bitcast %record* %2 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %24, i8 addrspace(1)* %arg0, i64 16, i1 false) + %25 = bitcast %record* %3 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %25, i8 addrspace(1)* %arg0, i64 16, i1 false) + %26 = bitcast %record* %4 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %26, i8 addrspace(1)* %arg0, i64 16, i1 false) + %27 = bitcast %record* %18 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %27, i8 addrspace(1)* %arg0, i64 16, i1 false) + br label %body1 + +; CHECK: body1: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i1 (i8 addrspace(1)*)* @g9, i32 1, i32 0, i8 addrspace(1)* %arg2) [ "gc-live"(i8 addrspace(1)* %arg0, i8 addrspace(1)* %arg2, %record addrspace(1)* %arg1), "struct-live"(%record* %2, %record* %4, %record* %18) ] +; +body1: ; preds = %entry + %28 = call i1 @g9(i8 addrspace(1)* %arg2) + br i1 %28, label %ifthen1, label %ifelse1 + +ifthen1: ; preds = %body1 + %29 = bitcast %record* %0 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %29, i8 addrspace(1)* %arg0, i64 16, i1 false) + ret void + +ifelse1: ; preds = %body1 + br label %ifend1 + +; CHECK: ifend1: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 (%record addrspace(1)*, i8 addrspace(1)*)* @g1, i32 2, i32 0, %record addrspace(1)* %arg1.reloc.casted, i8 addrspace(1)* %arg0.reloc) [ "gc-live"(i8 addrspace(1)* %arg2.reloc, i8 addrspace(1)* %arg0.reloc, %record addrspace(1)* %arg1.reloc.casted), "struct-live"(%record* %2, %record* %4, %record* %18) ] +; +ifend1: ; preds = %ifelse1 + %30 = getelementptr inbounds %record, %record addrspace(1)* %arg1, i32 0, i32 1 + %31 = load i64, i64 addrspace(1)* %30 + store i64 %31, i64* %23 + store i64 %31, i64* %22 + store i64 %31, i64* %21 + %32 = load i64, i64* %21 + store i64 %31, i64* %22 + %33 = load i64, i64* %22 + store i1 true, i1* %20 + %34 = call i64 @g1(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0) + store i64 %34, i64* %19 + %35 = load i64, i64* %21 + %icmpeq = icmp eq i64 %35, 0 + br i1 %icmpeq, label %ifthen2, label %ifelse2 + +; CHECK: ifthen2: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%record addrspace(1)*, i8 addrspace(1)*)* @g2, i32 2, i32 0, %record addrspace(1)* %arg1.reloc5.casted, i8 addrspace(1)* %arg0.reloc4) [ "gc-live"(i8 addrspace(1)* %arg2.reloc3, i8 addrspace(1)* %arg0.reloc4, %record addrspace(1)* %arg1.reloc5.casted) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g10, i32 1, i32 0, i8 addrspace(1)* %arg2.reloc8) [ "gc-live"(i8 addrspace(1)* %arg2.reloc8) ] +; +ifthen2: ; preds = %ifend1 + call void @g2(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0) + call void @g10(i8 addrspace(1)* %arg2) + unreachable + +ifelse2: ; preds = %ifend1 + br label %ifend2 + +; CHECK: ifend2: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i1 (i8 addrspace(1)*)* @g9, i32 1, i32 0, i8 addrspace(1)* %arg2.reloc3) [ "gc-live"(i8 addrspace(1)* %arg2.reloc3, i8 addrspace(1)* %arg0.reloc4, %record addrspace(1)* %arg1.reloc5.casted), "struct-live"(%record* %2, %record* %4, %record* %18) ] +; +ifend2: ; preds = %ifelse2 + %36 = call i1 @g9(i8 addrspace(1)* %arg2) + %lnot = xor i1 %36, true + br i1 %lnot, label %ifthen3, label %ifelse3 + +ifthen3: ; preds = %ifend2 + %37 = load i64, i64* %19 + store i64 %37, i64* %22 + br label %ifend3 + +ifelse3: ; preds = %ifend2 + br label %ifend3 + +ifend3: ; preds = %ifelse3, %ifthen3 + %38 = load i1, i1* %20 + br i1 %38, label %tmp1, label %tmp2 + +tmp2: ; preds = %ifend3 + br label %tmpend1 + +tmp1: ; preds = %ifend3 + %39 = load i64, i64* %21 + %icmpeq1 = icmp eq i64 %39, 1 + br label %tmpend1 + +tmpend1: ; preds = %tmp1, %tmp2 + %conv = phi i1 [ false, %tmp2 ], [ %icmpeq1, %tmp1 ] + br i1 %conv, label %ifthen4, label %ifelse4 + +ifthen4: ; preds = %tmpend1 + %40 = load i1, i1* %20 + br i1 %40, label %ifthen5, label %ifelse5 + +ifthen5: ; preds = %ifthen4 + %41 = load i64, i64* %22 + %add = add i64 %41, 1 + store i64 %add, i64* %22 + br label %ifend5 + +ifelse5: ; preds = %ifthen4 + br label %ifend5 + +ifend5: ; preds = %ifelse5, %ifthen5 + %42 = load i64, i64* %23 + %icmpslt = icmp slt i64 %42, 0 + br i1 %icmpslt, label %tmp3, label %tmp4 + +tmp3: ; preds = %ifend5 + br label %tmpend2 + +tmp4: ; preds = %ifend5 + %43 = load i64, i64* %23 + %44 = load i64, i64* %19 + %icmpsge = icmp sge i64 %43, %44 + br label %tmpend2 + +tmpend2: ; preds = %tmp4, %tmp3 + %conv3 = phi i1 [ true, %tmp3 ], [ %icmpsge, %tmp4 ] + br i1 %conv3, label %tmp5, label %tmp6 + +tmp5: ; preds = %tmpend2 + br label %tmpend3 + +tmp6: ; preds = %tmpend2 + %45 = load i64, i64* %22 + %icmpsle = icmp sle i64 %45, 0 + br label %tmpend3 + +tmpend3: ; preds = %tmp6, %tmp5 + %conv7 = phi i1 [ true, %tmp5 ], [ %icmpsle, %tmp6 ] + br i1 %conv7, label %tmp7, label %tmp8 + +tmp7: ; preds = %tmpend3 + br label %tmpend4 + +tmp8: ; preds = %tmpend3 + %46 = load i64, i64* %22 + %47 = load i64, i64* %19 + %icmpsgt = icmp sgt i64 %46, %47 + br label %tmpend4 + +tmpend4: ; preds = %tmp8, %tmp7 + %conv11 = phi i1 [ true, %tmp7 ], [ %icmpsgt, %tmp8 ] + br i1 %conv11, label %ifthen6, label %ifelse6 + +; CHECK: ifthen6: +; CHECK: call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g10, i32 1, i32 0, i8 addrspace(1)* %arg2.reloc16) [ "gc-live"(i8 addrspace(1)* %arg2.reloc16) ] +; +ifthen6: ; preds = %tmpend4 + call void @g10(i8 addrspace(1)* %arg2) + unreachable + +ifelse6: ; preds = %tmpend4 + br label %ifend6 + +; CHECK: ifend6: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (%record addrspace(1)*, i8 addrspace(1)*, i64, i64)* @g3, i32 4, i32 0, %record addrspace(1)* %arg1.reloc18.casted, i8 addrspace(1)* %arg0.reloc17, i64 %48, i64 %49) [ "gc-live"(i8 addrspace(1)* %arg2.reloc16, i8 addrspace(1)* %arg0.reloc17, %record addrspace(1)* %arg1.reloc18.casted), "struct-live"(%record* %18) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, i64)* @g4, i32 2, i32 0, i8 addrspace(1)* %50, i64 %sub) [ "gc-live"(i8 addrspace(1)* %50), "struct-live"(%record* %18) ] +; +ifend6: ; preds = %ifelse6 + %48 = load i64, i64* %23 + %49 = load i64, i64* %22 + %50 = call i8 addrspace(1)* @g3(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0, i64 %48, i64 %49) + %51 = load i64, i64* %22 + %52 = load i64, i64* %23 + %sub = sub i64 %51, %52 + call void @g4(i8 addrspace(1)* %50, i64 %sub) + %53 = bitcast %record* %0 to i8* + %54 = bitcast %record* %18 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %53, i8* align 8 %54, i64 16, i1 false) + ret void + +ifelse4: ; preds = %tmpend1 + br label %ifend4 + +ifend4: ; preds = %ifelse4 + store i64 0, i64* %17 + store i64 0, i64* %16 + %55 = load i64, i64* %21 + %icmpsgt13 = icmp sgt i64 %55, 0 + br i1 %icmpsgt13, label %ifthen7, label %ifelse7 + +ifthen7: ; preds = %ifend4 + %56 = load i1, i1* %20 + %lnot14 = xor i1 %56, true + br i1 %lnot14, label %ifthen8, label %ifelse8 + +ifthen8: ; preds = %ifthen7 + %57 = load i64, i64* %22 + %sub15 = sub i64 %57, 1 + store i64 %sub15, i64* %22 + br label %ifend8 + +ifelse8: ; preds = %ifthen7 + br label %ifend8 + +ifend8: ; preds = %ifelse8, %ifthen8 + %58 = load i64, i64* %23 + %icmpslt16 = icmp slt i64 %58, -1 + br i1 %icmpslt16, label %tmp9, label %tmp10 + +tmp9: ; preds = %ifend8 + br label %tmpend5 + +tmp10: ; preds = %ifend8 + %59 = load i64, i64* %22 + %60 = load i64, i64* %19 + %icmpsge19 = icmp sge i64 %59, %60 + br label %tmpend5 + +tmpend5: ; preds = %tmp10, %tmp9 + %conv21 = phi i1 [ true, %tmp9 ], [ %icmpsge19, %tmp10 ] + br i1 %conv21, label %ifthen9, label %ifelse9 + +; CHECK: ifthen9: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g10, i32 1, i32 0, i8 addrspace(1)* %arg2.reloc16) [ "gc-live"(i8 addrspace(1)* %arg2.reloc16) ] +; +ifthen9: ; preds = %tmpend5 + call void @g10(i8 addrspace(1)* %arg2) + unreachable + +ifelse9: ; preds = %tmpend5 + br label %ifend9 + +ifend9: ; preds = %ifelse9 + %61 = load i64, i64* %22 + %62 = load i64, i64* %23 + %sub23 = sub i64 %61, %62 + %63 = load i64, i64* %21 + %icmpsle24 = icmp sle i64 %sub23, -9223372036854775808 + %icmpeq25 = icmp eq i64 %63, -1 + br i1 %icmpsle24, label %tmp11, label %tmp12 + +tmp12: ; preds = %ifend9 + br label %tmpend6 + +tmp11: ; preds = %ifend9 + br label %tmpend6 + +tmpend6: ; preds = %tmp11, %tmp12 + %conv29 = phi i1 [ false, %tmp12 ], [ %icmpeq25, %tmp11 ] + br i1 %conv29, label %tmp13, label %tmp14 + +; CHECK: tmp14: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 (i64, i64)* @g8, i32 2, i32 0, i64 %sub23, i64 %64) [ "gc-live"(i8 addrspace(1)* %arg2.reloc16, i8 addrspace(1)* %arg0.reloc17, %record addrspace(1)* %arg1.reloc18.casted), "struct-live"(%record* %2, %record* %4) ] +; +tmp14: ; preds = %tmpend6 + %64 = call i64 @g8(i64 %sub23, i64 %63) + store i64 %64, i64* %1 + br label %tmpend7 + +tmp13: ; preds = %tmpend6 + store i64 -9223372036854775808, i64* %1 + br label %tmpend7 + +tmpend7: ; preds = %tmp13, %tmp14 + %65 = load i64, i64* %1 + %add31 = add i64 %65, 1 + store i64 %add31, i64* %17 + %66 = load i64, i64* %21 + store i64 %66, i64* %16 + %67 = load i64, i64* %22 + %add32 = add i64 %67, 1 + store i64 %add32, i64* %22 + br label %ifend7 + +ifelse7: ; preds = %ifend4 + %68 = load i1, i1* %20 + %lnot33 = xor i1 %68, true + br i1 %lnot33, label %ifthen10, label %ifelse10 + +ifthen10: ; preds = %ifelse7 + %69 = load i64, i64* %22 + %add34 = add i64 %69, 1 + store i64 %add34, i64* %22 + br label %ifend10 + +ifelse10: ; preds = %ifelse7 + br label %ifend10 + +ifend10: ; preds = %ifelse10, %ifthen10 + %70 = load i64, i64* %23 + %71 = load i64, i64* %19 + %icmpsge35 = icmp sge i64 %70, %71 + br i1 %icmpsge35, label %tmp15, label %tmp16 + +tmp15: ; preds = %ifend10 + br label %tmpend8 + +tmp16: ; preds = %ifend10 + %72 = load i64, i64* %22 + %icmpslt38 = icmp slt i64 %72, 0 + br label %tmpend8 + +tmpend8: ; preds = %tmp16, %tmp15 + %conv40 = phi i1 [ true, %tmp15 ], [ %icmpslt38, %tmp16 ] + br i1 %conv40, label %ifthen11, label %ifelse11 + +; CHECK: ifthen11: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g10, i32 1, i32 0, i8 addrspace(1)* %arg2.reloc16) [ "gc-live"(i8 addrspace(1)* %arg2.reloc16) ] +; +ifthen11: ; preds = %tmpend8 + call void @g10(i8 addrspace(1)* %arg2) + unreachable + +ifelse11: ; preds = %tmpend8 + br label %ifend11 + +ifend11: ; preds = %ifelse11 + %73 = load i64, i64* %23 + %74 = load i64, i64* %22 + %sub42 = sub i64 %73, %74 + %75 = load i64, i64* %21 + %sub43 = sub i64 0, %75 + %icmpsle44 = icmp sle i64 %sub42, -9223372036854775808 + %icmpeq45 = icmp eq i64 %sub43, -1 + br i1 %icmpsle44, label %tmp17, label %tmp18 + +tmp18: ; preds = %ifend11 + br label %tmpend9 + +tmp17: ; preds = %ifend11 + br label %tmpend9 + +tmpend9: ; preds = %tmp17, %tmp18 + %conv49 = phi i1 [ false, %tmp18 ], [ %icmpeq45, %tmp17 ] + br i1 %conv49, label %tmp19, label %tmp20 + +; CHECK: tmp20: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 (i64, i64)* @g8, i32 2, i32 0, i64 %sub42, i64 %sub43) [ "gc-live"(i8 addrspace(1)* %arg2.reloc16, i8 addrspace(1)* %arg0.reloc17, %record addrspace(1)* %arg1.reloc18.casted), "struct-live"(%record* %2, %record* %4) ] +; +tmp20: ; preds = %tmpend9 + %76 = call i64 @g8(i64 %sub42, i64 %sub43) + store i64 %76, i64* %1 + br label %tmpend10 + +tmp19: ; preds = %tmpend9 + store i64 -9223372036854775808, i64* %1 + br label %tmpend10 + +tmpend10: ; preds = %tmp19, %tmp20 + %77 = load i64, i64* %1 + %add53 = add i64 %77, 1 + store i64 %add53, i64* %17 + %78 = load i64, i64* %23 + %add54 = add i64 %78, 1 + store i64 %add54, i64* %22 + %79 = load i64, i64* %23 + %80 = load i64, i64* %17 + %sub55 = sub i64 %80, 1 + %81 = load i64, i64* %21 + %mul = mul i64 %sub55, %81 + %add56 = add i64 %79, %mul + store i64 %add56, i64* %23 + %82 = load i64, i64* %21 + %sub57 = sub i64 0, %82 + store i64 %sub57, i64* %16 + br label %ifend7 + +ifend7: ; preds = %tmpend10, %tmpend7 + %83 = load i64, i64* %17 + %84 = load i64, i64* @data + %mul58 = mul i64 %83, %84 + %icmpsge1 = icmp sge i64 %mul58, 0 + br i1 %icmpsge1, label %tmp21, label %tmp22 + +; CHECK: tmp22: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g10, i32 1, i32 0, i8 addrspace(1)* %.277) [ "gc-live"(i8 addrspace(1)* %.277) ] +; +tmp22: ; preds = %ifend7 + call void @g10(i8 addrspace(1)* %arg2) + unreachable + +; CHECK: tmp21: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 (%record addrspace(1)*, i8 addrspace(1)*, i64)* @g5, i32 3, i32 0, %record addrspace(1)* %.280, i8 addrspace(1)* %.2, i64 %87) [ "gc-live"(i8 addrspace(1)* %.277, %record addrspace(1)* %.280, i8 addrspace(1)* %.2), "struct-live"(%record* %2, %record* %4, i8 addrspace(1)** %15) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%record addrspace(1)*, i8 addrspace(1)*, i64, i64)* @g6, i32 4, i32 0, %record addrspace(1)* %arg1.reloc51.casted, i8 addrspace(1)* %arg0.reloc52, i64 %87, i64 %88) [ "gc-live"(i8 addrspace(1)* %arg2.reloc50, %record addrspace(1)* %arg1.reloc51.casted, i8 addrspace(1)* %arg0.reloc52), "struct-live"(%record* %2, %record* %4, i8 addrspace(1)** %15) ] +; +tmp21: ; preds = %ifend7 + %85 = bitcast i8 addrspace(1)* %arg2 to %array addrspace(1)* + store i8 addrspace(1)* %arg2, i8 addrspace(1)** %15 + %86 = load i64, i64* %23 + %87 = call i64 @g5(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0, i64 %86) + call void @g6(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0, i64 %86, i64 %87) + %88 = getelementptr inbounds %struct, %struct* %14, i32 0, i32 0 + %89 = load i64, i64* %88 + store i64 %89, i64* %13 + %90 = getelementptr inbounds %struct, %struct* %14, i32 0, i32 1 + %91 = load i64, i64* %90 + store i64 %91, i64* %12 + store i64 0, i64* %11 + store i64 0, i64* %10 + store i64 0, i64* %9 + %92 = load i64, i64* %23 + store i64 %92, i64* %8 + %93 = load i64, i64* %22 + store i64 %93, i64* %7 + store i1 true, i1* %6 + br label %body2 + +body2: ; preds = %ifend12, %tmp21 + %94 = load i64, i64* %8 + %95 = load i64, i64* %7 + %icmpslt61 = icmp slt i64 %94, %95 + br i1 %icmpslt61, label %ifthen12, label %ifelse12 + +ifthen12: ; preds = %body2 + %96 = load i1, i1* %6 + br i1 %96, label %ifthen13, label %ifelse13 + +ifthen13: ; preds = %ifthen12 + store i1 false, i1* %6 + br label %ifend13 + +ifelse13: ; preds = %ifthen12 + %97 = load i64, i64* %8 + %add62 = add i64 %97, 1 + store i64 %add62, i64* %8 + br label %ifend13 + +ifend13: ; preds = %ifelse13, %ifthen13 + %98 = load i64, i64* %8 + %99 = load i64, i64* %7 + %icmpslt63 = icmp slt i64 %98, %99 + br i1 %icmpslt63, label %ifthen14, label %ifelse14 + +; CHECK: ifthen14: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 (i8 addrspace(1)*, i64)* @g12, i32 2, i32 0, i8 addrspace(1)* %102, i64 %103) [ "gc-live"(i8 addrspace(1)* %.3, %record addrspace(1)* %.381, i8 addrspace(1)* %102), "struct-live"(%record* %2, %record* %4, i8 addrspace(1)** %15) ] +; +ifthen14: ; preds = %ifend13 + %100 = getelementptr inbounds %record, %record addrspace(1)* %arg1, i32 0, i32 0 + %101 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %100 + %102 = load i64, i64* %13 + %103 = call i64 @g12(i8 addrspace(1)* %101, i64 %102) + store i64 %103, i64* %9 + %104 = load i64, i64* %11 + %105 = load i64, i64* %16 + %icmpeq64 = icmp eq i64 %104, %105 + br i1 %icmpeq64, label %tmp23, label %tmp24 + +tmp23: ; preds = %ifthen14 + br label %tmpend12 + +tmp24: ; preds = %ifthen14 + %106 = load i64, i64* %11 + %icmpeq67 = icmp eq i64 %106, 0 + br label %tmpend12 + +tmpend12: ; preds = %tmp24, %tmp23 + %conv69 = phi i1 [ true, %tmp23 ], [ %icmpeq67, %tmp24 ] + br i1 %conv69, label %ifthen15, label %ifelse15 + +; CHECK: ifthen15: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, i8 addrspace(1)*, i64, i64, i64)* @g7, i32 5, i32 0, i8 addrspace(1)* %110, i8 addrspace(1)* %111, i64 %112, i64 %113, i64 %114) [ "gc-live"(i8 addrspace(1)* %arg2.reloc60, %record addrspace(1)* %arg1.reloc61.casted, i8 addrspace(1)* %110, i8 addrspace(1)* %111), "struct-live"(%record* %2, %record* %4, i8 addrspace(1)** %15) ] +; +ifthen15: ; preds = %tmpend12 + %107 = getelementptr inbounds %record, %record addrspace(1)* %arg1, i32 0, i32 0 + %108 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %107 + %109 = load i8 addrspace(1)*, i8 addrspace(1)** %15 + %110 = load i64, i64* %13 + %111 = load i64, i64* %10 + %112 = load i64, i64* %9 + call void @g7(i8 addrspace(1)* %108, i8 addrspace(1)* %109, i64 %110, i64 %111, i64 %112) + %113 = load i64, i64* %10 + %114 = load i64, i64* %9 + %add71 = add i64 %113, %114 + store i64 %add71, i64* %10 + store i64 1, i64* %11 + br label %ifend15 + +ifelse15: ; preds = %tmpend12 + %115 = load i64, i64* %11 + %add72 = add i64 %115, 1 + store i64 %add72, i64* %11 + br label %ifend15 + +ifend15: ; preds = %ifelse15, %ifthen15 + %116 = load i64, i64* %13 + %117 = load i64, i64* %9 + %add73 = add i64 %116, %117 + store i64 %add73, i64* %13 + br label %ifend14 + +ifelse14: ; preds = %ifend13 + br label %ifend14 + +ifend14: ; preds = %ifelse14, %ifend15 + br label %ifend12 + +ifelse12: ; preds = %body2 + br label %end1 + +ifend12: ; preds = %ifend14 + br label %body2 + +end1: ; preds = %ifelse12 + %118 = load i64, i64* %10 + %119 = add i64 %118, 1 + %icmpsge2 = icmp sge i64 %119, 0 + br i1 %icmpsge2, label %tmp25, label %tmp26 + +; CHECK: tmp26: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g10, i32 1, i32 0, i8 addrspace(1)* %.3) [ "gc-live"(i8 addrspace(1)* %.3) ] +; +tmp26: ; preds = %end1 + call void @g10(i8 addrspace(1)* %arg2) + unreachable + +; CHECK: tmp25: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, i8 addrspace(1)*, i64, i64, i64)* @g7, i32 5, i32 0, i8 addrspace(1)* %125, i8 addrspace(1)* %126, i64 0, i64 0, i64 %127) [ "gc-live"(i8 addrspace(1)* %125, i8 addrspace(1)* %126), "struct-live"(%record* %2, %record* %4, i8 addrspace(1)** %5) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, i64)* @g4, i32 2, i32 0, i8 addrspace(1)* %130, i64 %131) [ "gc-live"(i8 addrspace(1)* %130), "struct-live"(%record* %2, %record* %4) ] +; +tmp25: ; preds = %end1 + %120 = bitcast i8 addrspace(1)* %arg2 to %array addrspace(1)* + store i8 addrspace(1)* %arg2, i8 addrspace(1)** %5 + %121 = load i8 addrspace(1)*, i8 addrspace(1)** %15 + %122 = load i8 addrspace(1)*, i8 addrspace(1)** %5 + %123 = load i64, i64* %10 + call void @g7(i8 addrspace(1)* %121, i8 addrspace(1)* %122, i64 0, i64 0, i64 %123) + %124 = load i8 addrspace(1)*, i8 addrspace(1)** %5 + %125 = load i64, i64* %17 + call void @g4(i8 addrspace(1)* %124, i64 %125) + %126 = bitcast %record* %3 to i8* + %127 = bitcast %record* %4 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %126, i8* align 8 %127, i64 16, i1 false) + %128 = load i64, i64* %21 + %icmpslt79 = icmp slt i64 %128, 0 + br i1 %icmpslt79, label %ifthen16, label %ifelse16 + +; CHECK: ifthen16: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g10, i32 1, i32 0, i8 addrspace(1)* null) [ "struct-live"(%record* %2) ] +; +ifthen16: ; preds = %tmp25 + %129 = addrspacecast %record* %3 to %record addrspace(1)* + call void @g10(i8 addrspace(1)* null) + %130 = bitcast %record* %3 to i8* + %131 = bitcast %record* %2 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %130, i8* align 8 %131, i64 16, i1 false) + br label %ifend16 + +ifelse16: ; preds = %tmp25 + br label %ifend16 + +ifend16: ; preds = %ifelse16, %ifthen16 + %132 = bitcast %record* %0 to i8* + %133 = bitcast %record* %3 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %132, i8* align 8 %133, i64 16, i1 false) + ret void +} + +attributes #0 = { "record_mut" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi5.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi5.ll new file mode 100644 index 000000000..48e646be2 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi5.ll @@ -0,0 +1,177 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record1 = type { i8 addrspace(1)*, i64 } +%record2 = type { i8 addrspace(1)*, i64, i64, i1 } +%struct = type { i1, i32 } + +declare void @g0(i8 addrspace(1)* %arg0, %record1 addrspace(1)* %arg1, i8 addrspace(1)* %arg2, %record1 addrspace(1)* %arg3) #0 gc "cangjie" +declare void @g1(i8 addrspace(1)* %arg0, %record1 addrspace(1)* %arg1) #0 gc "cangjie" +declare i8 addrspace(1)* @g2(i8 addrspace(1)* %arg0, %record2 addrspace(1)* %arg1) #0 gc "cangjie" +declare i8* @g3(i8 addrspace(1)* %arg) #0 gc "cangjie" +declare i8* @g4(i64* %arg0, i32 %arg1, i32 %arg2, i64 %arg3, i64 %arg4) #0 gc "cangjie" +declare void @g5(i32 %arg) #0 gc "cangjie" +declare void @g6(i8 addrspace(1)* %arg0, %record1 addrspace(1)* %arg1) #0 gc "cangjie" +declare void @g7(i8 addrspace(1)* %arg0, %record1 addrspace(1)* %arg1, i1 %arg2) #0 gc "cangjie" +declare i1 @g8(i8 addrspace(1)* %arg0, %record1 addrspace(1)* %arg1, i8 addrspace(1)* %arg2, %record1 addrspace(1)* %arg3) #0 gc "cangjie" +declare i1 @g9(i8 addrspace(1)* %arg0, %record1 addrspace(1)* %arg1) #0 gc "cangjie" +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) + +define i64 @foo() #0 gc "cangjie" { +entry: + %0 = alloca %record1 + %1 = alloca %record1 + %2 = alloca %record1 + %3 = alloca %record1 + %4 = alloca %record1 + %5 = alloca %record1 + %6 = alloca %record1 + %7 = alloca %record1 + %8 = alloca %record1 + %9 = alloca %struct + %10 = alloca %record2 + %11 = alloca %record2 + %12 = alloca %record1 + %13 = alloca %record1 + %14 = alloca %record1 + %15 = bitcast %record1* %3 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %15, i8 0, i64 16, i1 false) + %16 = bitcast %record1* %4 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %16, i8 0, i64 16, i1 false) + %17 = bitcast %record1* %5 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %17, i8 0, i64 16, i1 false) + %18 = bitcast %record1* %6 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %18, i8 0, i64 16, i1 false) + %19 = bitcast %record1* %7 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %19, i8 0, i64 16, i1 false) + %20 = bitcast %record2* %10 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %20, i8 0, i64 32, i1 false) + %21 = bitcast %record2* %11 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %21, i8 0, i64 32, i1 false) + %22 = bitcast %record1* %12 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %22, i8 0, i64 16, i1 false) + %23 = bitcast %record1* %13 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %23, i8 0, i64 16, i1 false) + %24 = bitcast %record1* %14 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %24, i8 0, i64 16, i1 false) + %25 = alloca i32 + %26 = bitcast %record1* %0 to i8* + %27 = bitcast %record1* %1 to i8* + %28 = bitcast %record1* %2 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %26, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %27, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %28, i8 0, i64 16, i1 false) + %29 = addrspacecast %record1* %2 to %record1 addrspace(1)* + br label %body1 + +body1: ; preds = %tmpend1, %entry + %count.03.i = phi i64 [ 1, %entry ], [ %31, %tmpend1 ] + %30 = and i64 %count.03.i, 1 + %icmpeq.i = icmp eq i64 %30, 0 + br i1 %icmpeq.i, label %tmp1, label %tmp2 + +; CHECK: tmp1: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record1 addrspace(1)*, i8 addrspace(1)*, %record1 addrspace(1)*)* @g0, i32 4, i32 0, i8 addrspace(1)* null, %record1 addrspace(1)* %29, i8 addrspace(1)* null, %record1 addrspace(1)* %29) [ "struct-live"(%record1* %0, %record1* %1, %record1* %8, %record1* %3, %record1* %4, %record1* %5, %record1* %6, %record1* %7, %record2* %11, %record1* %2) ] +; +tmp1: ; preds = %body1 + call void @g0(i8 addrspace(1)* null, %record1 addrspace(1)* %29, i8 addrspace(1)* null, %record1 addrspace(1)* %29) + br label %tmpend1 + +; CHECK: tmp2: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record1 addrspace(1)*, i8 addrspace(1)*, %record1 addrspace(1)*)* @g0, i32 4, i32 0, i8 addrspace(1)* null, %record1 addrspace(1)* %29, i8 addrspace(1)* null, %record1 addrspace(1)* %29) [ "struct-live"(%record1* %0, %record1* %1, %record1* %8, %record1* %3, %record1* %4, %record1* %5, %record1* %6, %record1* %7, %record2* %11, %record1* %2) ] +; +tmp2: ; preds = %body1 + call void @g0(i8 addrspace(1)* null, %record1 addrspace(1)* %29, i8 addrspace(1)* null, %record1 addrspace(1)* %29) + br label %tmpend1 + +tmpend1: ; preds = %tmp2, %tmp1 + %.sink = phi i8* [ %26, %tmp2 ], [ %27, %tmp1 ] + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 8 %28, i8* nonnull align 8 %.sink, i64 16, i1 false) + %31 = add nuw nsw i64 %count.03.i, 1 + %icmpeq1 = icmp eq i64 %31, 500 + br i1 %icmpeq1, label %body2, label %body1 + +; CHECK: body2: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record1 addrspace(1)*)* @g1, i32 2, i32 0, i8 addrspace(1)* null, %record1 addrspace(1)* %32) [ "struct-live"(%record1* %12, %record1* %2, %record1* %8, %record1* %3, %record1* %4, %record1* %5, %record1* %6, %record1* %7, %record2* %11, %record1* %13) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*, %record2 addrspace(1)*)* @g2, i32 2, i32 0, i8 addrspace(1)* null, %record2 addrspace(1)* %33) [ "struct-live"(%record1* %12, %record1* %2, %record1* %8, %record1* %3, %record1* %4, %record1* %5, %record1* %6, %record1* %7, %record2* %10) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8* (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %34) [ "gc-live"(i8 addrspace(1)* %34), "struct-live"(%record1* %12, %record1* %2, %record1* %8, %record1* %3, %record1* %4, %record1* %5, %record1* %6, %record1* %7) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8* (i64*, i32, i32, i64, i64)* @g4, i32 5, i32 0, i64* %39, i32 10, i32 907, i64 0, i64 0) [ "gc-live"(i8 addrspace(1)* %36), "struct-live"(%record1* %12, %record1* %2, %record1* %8, %record1* %3, %record1* %4, %record1* %5, %record1* %6, %record1* %7) ] +; +body2: ; preds = %tmpend1 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 8 %24, i8* nonnull align 8 %28, i64 16, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 8 %23, i8* nonnull align 8 %24, i64 16, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 8 %22, i8* nonnull align 8 %.sink, i64 16, i1 false) + %32 = addrspacecast %record1* %13 to %record1 addrspace(1)* + call void @g1(i8 addrspace(1)* null, %record1 addrspace(1)* %32) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 8 %20, i8* nonnull align 8 %21, i64 32, i1 false) + %33 = addrspacecast %record2* %10 to %record2 addrspace(1)* + %34 = call i8 addrspace(1)* @g2(i8 addrspace(1)* null, %record2 addrspace(1)* %33) + %35 = call i8* @g3(i8 addrspace(1)* %34) + %36 = getelementptr inbounds i8, i8* %35, i64 32 + %37 = bitcast i8* %36 to i64** + %38 = load i64*, i64** %37 + %39 = call i8* @g4(i64* %38, i32 10, i32 907, i64 0, i64 0) + %40 = bitcast i8* %39 to void (%struct*, i8 addrspace(1)*)* + %41 = getelementptr inbounds %struct, %struct* %9, i64 0, i32 0 + %42 = load i1, i1* %41 + br i1 %42, label %ifthen1, label %ifelse1 + +ifthen1: ; preds = %body2 + %.pre = addrspacecast %record1* %12 to %record1 addrspace(1)* + br label %end + +ifelse1: ; preds = %body2 + %43 = getelementptr inbounds %struct, %struct* %9, i64 0, i32 1 + %44 = addrspacecast %record1* %12 to %record1 addrspace(1)* + %45 = addrspacecast %record1* %7 to %record1 addrspace(1)* + %46 = addrspacecast %record1* %6 to %record1 addrspace(1)* + br label %body3 + +; CHECK: body3: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i32)* @g5, i32 1, i32 0, i32 %49) [ "gc-live"(i8 addrspace(1)* %.0), "struct-live"(%record1* %2, %record1* %8, %record1* %3, %record1* %4, %record1* %5, %record1* %6, %record1* %7, %record1* %12) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record1 addrspace(1)*, i8 addrspace(1)*, %record1 addrspace(1)*)* @g0, i32 4, i32 0, i8 addrspace(1)* null, %record1 addrspace(1)* %46, i8 addrspace(1)* null, %record1 addrspace(1)* %47) [ "gc-live"(i8 addrspace(1)* %50), "struct-live"(%record1* %2, %record1* %8, %record1* %3, %record1* %4, %record1* %5, %record1* %6, %record1* %7, %record1* %12) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record1 addrspace(1)*, i8 addrspace(1)*, %record1 addrspace(1)*)* @g0, i32 4, i32 0, i8 addrspace(1)* null, %record1 addrspace(1)* %48, i8 addrspace(1)* null, %record1 addrspace(1)* %29) [ "gc-live"(i8 addrspace(1)* %51), "struct-live"(%record1* %2, %record1* %8, %record1* %3, %record1* %4, %record1* %5, %record1* %6, %record1* %7) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8* (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %52) [ "gc-live"(i8 addrspace(1)* %52), "struct-live"(%record1* %12, %record1* %2, %record1* %8, %record1* %3, %record1* %4, %record1* %5, %record1* %6, %record1* %7) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8* (i64*, i32, i32, i64, i64)* @g4, i32 5, i32 0, i64* %57, i32 10, i32 907, i64 0, i64 0) [ "gc-live"(i8 addrspace(1)* %54), "struct-live"(%record1* %12, %record1* %2, %record1* %8, %record1* %3, %record1* %4, %record1* %5, %record1* %6, %record1* %7) ] +; +body3: ; preds = %ifelse1, %body3 + %47 = load i32, i32* %43 + call void @g5(i32 %47) + call void @g0(i8 addrspace(1)* null, %record1 addrspace(1)* %44, i8 addrspace(1)* null, %record1 addrspace(1)* %45) + call void @g0(i8 addrspace(1)* null, %record1 addrspace(1)* %46, i8 addrspace(1)* null, %record1 addrspace(1)* %29) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 8 %22, i8* nonnull align 8 %17, i64 16, i1 false) + %48 = call i8* @g3(i8 addrspace(1)* %34) + %49 = getelementptr inbounds i8, i8* %48, i64 32 + %50 = bitcast i8* %49 to i64** + %51 = load i64*, i64** %50 + %52 = call i8* @g4(i64* %51, i32 10, i32 907, i64 0, i64 0) + %53 = bitcast i8* %52 to void (%struct*, i8 addrspace(1)*)* + %54 = load i1, i1* %41 + br i1 %54, label %end, label %body3 + +; CHECK: end: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record1 addrspace(1)*)* @g6, i32 2, i32 0, i8 addrspace(1)* null, %record1 addrspace(1)* %.pre-phi) [ "struct-live"(%record1* %2, %record1* %8, %record1* %3, %record1* %4, %record1* %12) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record1 addrspace(1)*, i8 addrspace(1)*, %record1 addrspace(1)*)* @g0, i32 4, i32 0, i8 addrspace(1)* null, %record1 addrspace(1)* %62, i8 addrspace(1)* null, %record1 addrspace(1)* %29) [ "struct-live"(%record1* %2, %record1* %8, %record1* %3, %record1* %4, %record1* %12) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record1 addrspace(1)*, i8 addrspace(1)*, %record1 addrspace(1)*)* @g0, i32 4, i32 0, i8 addrspace(1)* null, %record1 addrspace(1)* %29, i8 addrspace(1)* null, %record1 addrspace(1)* %63) [ "struct-live"(%record1* %2, %record1* %8, %record1* %3, %record1* %12) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i1 (i8 addrspace(1)*, %record1 addrspace(1)*)* @g9, i32 2, i32 0, i8 addrspace(1)* null, %record1 addrspace(1)* %64) [ "struct-live"(%record1* %2, %record1* %8, %record1* %12) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record1 addrspace(1)*, i1)* @g7, i32 3, i32 0, i8 addrspace(1)* null, %record1 addrspace(1)* %64, i1 %65) [ "struct-live"(%record1* %2, %record1* %8, %record1* %12) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i1 (i8 addrspace(1)*, %record1 addrspace(1)*, i8 addrspace(1)*, %record1 addrspace(1)*)* @g8, i32 4, i32 0, i8 addrspace(1)* null, %record1 addrspace(1)* %.pre-phi, i8 addrspace(1)* null, %record1 addrspace(1)* %29) [ "struct-live"(%record1* %2, %record1* %12) ] +; +end: ; preds = %body3, %ifthen1 + %.pre-phi = phi %record1 addrspace(1)* [ %.pre, %ifthen1 ], [ %44, %body3 ] + call void @g6(i8 addrspace(1)* null, %record1 addrspace(1)* %.pre-phi) + %55 = addrspacecast %record1* %4 to %record1 addrspace(1)* + call void @g0(i8 addrspace(1)* null, %record1 addrspace(1)* %55, i8 addrspace(1)* null, %record1 addrspace(1)* %29) + %56 = addrspacecast %record1* %3 to %record1 addrspace(1)* + call void @g0(i8 addrspace(1)* null, %record1 addrspace(1)* %29, i8 addrspace(1)* null, %record1 addrspace(1)* %56) + %57 = addrspacecast %record1* %8 to %record1 addrspace(1)* + %58 = call i1 @g9(i8 addrspace(1)* null, %record1 addrspace(1)* %57) + call void @g7(i8 addrspace(1)* null, %record1 addrspace(1)* %57, i1 %58) + %59 = call i1 @g8(i8 addrspace(1)* null, %record1 addrspace(1)* %.pre-phi, i8 addrspace(1)* null, %record1 addrspace(1)* %29) + %not. = xor i1 %59, true + %spec.select = zext i1 %not. to i64 + ret i64 %spec.select +} + +attributes #0 = { "hasRcdParam" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi6.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi6.ll new file mode 100644 index 000000000..3c60b3573 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi6.ll @@ -0,0 +1,758 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record = type { i8 addrspace(1)*, i64 } +%object = type { %record, i64 } +%array = type { %object, [0 x i32] } + +declare void @g0() #0 gc "cangjie" +declare void @g1(i8* %arg0, i64 %arg1, i64 %arg2) #0 gc "cangjie" +declare i8 addrspace(1)* @g2(i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1) #0 gc "cangjie" +declare void @g3(i8 addrspace(1)* %arg) #0 gc "cangjie" +declare i8 addrspace(1)* @g4(i64 %arg0, i8* %arg1) #0 gc "cangjie" +declare i8 addrspace(1)* @g5(i8* %arg0, i32 %arg1) #0 gc "cangjie" +declare void @g6(i8 addrspace(1)* %arg) #0 gc "cangjie" +declare void @g7(i8 addrspace(1)* %arg0, i8 addrspace(1)* %arg1, i8 addrspace(1)* addrspace(1)* %arg2) #0 gc "cangjie" +declare void @g8(i64 %arg0, i8 addrspace(1)* %arg1, i64 addrspace(1)* %arg2) #0 gc "cangjie" +declare i8 addrspace(1)* @g9(i8 addrspace(1)* %arg0, i64 %arg1, i8 addrspace(1)* %arg2) #0 gc "cangjie" +declare void @g10(i8 addrspace(1)* %arg0, i8 addrspace(1)* %arg1, %record addrspace(1)* %arg2) #0 gc "cangjie" +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly %arg0, i8 %arg1, i64 %arg2, i1 immarg %arg3) +declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %arg0, i64 %arg1) +declare { i64, i1 } @llvm.ssub.with.overflow.i64(i64 %arg0, i64 %arg1) + +define i64 @foo(i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1, i64 %arg2, i8* %arg3) #0 gc "cangjie" { +; CHECK-LABEL: foo +; CHECK: entry: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @g0, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* %arg0) ] +; +entry: + %0 = alloca %record + %1 = alloca %record + %2 = alloca %record + %3 = alloca %record + %4 = alloca %record + %5 = alloca %record + %6 = alloca %record + %7 = alloca %record + %8 = alloca %record + %9 = alloca %record + %10 = alloca %record + %11 = alloca %record + %12 = alloca %record + %13 = alloca %record + %14 = alloca %record + %15 = alloca %record + %16 = alloca %record + %17 = alloca %record + %18 = alloca %record + %19 = alloca %record + %20 = alloca %record + %21 = alloca %record + %22 = alloca %record + %23 = alloca %record + %24 = bitcast %record* %22 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %24, i8 0, i64 16, i1 false) + %25 = bitcast %record* %23 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %25, i8 0, i64 16, i1 false) + %26 = getelementptr inbounds i8, i8 addrspace(1)* %arg0, i64 56 + %27 = bitcast i8 addrspace(1)* %26 to %array addrspace(1)* addrspace(1)* + %28 = load %array addrspace(1)*, %array addrspace(1)* addrspace(1)* %27 + %29 = getelementptr inbounds %array, %array addrspace(1)* %28, i64 0, i32 1, i64 %arg2 + %30 = load i32, i32 addrspace(1)* %29 + call void @g0() + switch i32 %30, label %tmp38 [ + i32 35, label %ifthen1 + i32 34, label %ifthen2 + ] + +ifthen1: ; preds = %entry + %31 = bitcast %record* %21 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %31, i8 0, i64 16, i1 false) + %32 = bitcast %record* %17 to i8* + %33 = bitcast %record* %18 to i8* + %34 = bitcast %record* %19 to i8* + %35 = bitcast %record* %20 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %32, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %33, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %34, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %35, i8 0, i64 16, i1 false) + %36 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %arg2, i64 1) + %conv1 = extractvalue { i64, i1 } %36, 1 + br i1 %conv1, label %ifthen3, label %ifelse3 + +ifelse3: ; preds = %ifthen1 + %37 = load %array addrspace(1)*, %array addrspace(1)* addrspace(1)* %27 + br label %body1 + +; CHECK: ifthen3: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8*, i64, i64)* @g1, i32 3, i32 0, i8* %arg3, i64 0, i64 0) [ "struct-live"(%record* %20) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*, %record addrspace(1)*)* @g2, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %38) [ "struct-live"(%record* %20) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %39) [ "gc-live"(i8 addrspace(1)* %39) ] +; +ifthen3: ; preds = %ifthen1 + call void @g1(i8* %arg3, i64 0, i64 0) + %38 = addrspacecast %record* %20 to %record addrspace(1)* + %39 = call i8 addrspace(1)* @g2(i8 addrspace(1)* null, %record addrspace(1)* %38) + call void @g3(i8 addrspace(1)* %39) + unreachable + +body1: ; preds = %ifthen7, %ifelse3 + %40 = phi { i64, i1 } [ %45, %ifthen7 ], [ %36, %ifelse3 ] + %41 = extractvalue { i64, i1 } %40, 0 + %42 = getelementptr inbounds %array, %array addrspace(1)* %37, i64 0, i32 1, i64 %41 + %43 = load i32, i32 addrspace(1)* %42 + switch i32 %43, label %ifthen5 [ + i32 34, label %ifthen6 + i32 35, label %ifthen7 + ] + +; CHECK: ifthen5: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i64, i8*)* @g4, i32 2, i32 0, i64 0, i8* %arg3) [ "gc-live"(i8 addrspace(1)* %.0), "struct-live"(%record* %17, %record* %21) ] +; +ifthen5: ; preds = %body1 + %44 = call i8 addrspace(1)* @g4(i64 0, i8* %arg3) + br label %body2 + +; CHECK: ifthen7: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @g0, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* %.0, %array addrspace(1)* %.0190), "struct-live"(%record* %19, %record* %17, %record* %21, %record* %18) ] +; +ifthen7: ; preds = %body1 + %45 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %41, i64 1) + %conv2 = extractvalue { i64, i1 } %45, 1 + call void @g0() + br i1 %conv2, label %ifthen8, label %body1 + +; CHECK: ifthen8: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8*, i64, i64)* @g1, i32 3, i32 0, i8* %arg3, i64 0, i64 0) [ "struct-live"(%record* %19) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*, %record addrspace(1)*)* @g2, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %49) [ "struct-live"(%record* %19) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %50) [ "gc-live"(i8 addrspace(1)* %50) ] +; +ifthen8: ; preds = %ifthen7 + call void @g1(i8* %arg3, i64 0, i64 0) + %46 = addrspacecast %record* %19 to %record addrspace(1)* + %47 = call i8 addrspace(1)* @g2(i8 addrspace(1)* null, %record addrspace(1)* %46) + call void @g3(i8 addrspace(1)* %47) + unreachable + +ifthen6: ; preds = %body1 + %48 = extractvalue { i64, i1 } %40, 0 + %49 = call { i64, i1 } @llvm.ssub.with.overflow.i64(i64 %48, i64 %arg2) + %conv3 = extractvalue { i64, i1 } %49, 1 + br i1 %conv3, label %ifthen9, label %ifelse9 + +ifelse9: ; preds = %ifthen6 + %conv24 = extractvalue { i64, i1 } %49, 0 + %50 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %conv24, i64 1) + %conv4 = extractvalue { i64, i1 } %50, 0 + %conv5 = extractvalue { i64, i1 } %50, 1 + br i1 %conv5, label %ifthen10, label %ifelse10 + +; CHECK: ifthen9: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8*, i64, i64)* @g1, i32 3, i32 0, i8* %arg3, i64 0, i64 0) [ "struct-live"(%record* %18) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*, %record addrspace(1)*)* @g2, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %55) [ "struct-live"(%record* %18) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %56) [ "gc-live"(i8 addrspace(1)* %56) ] +; +ifthen9: ; preds = %ifthen6 + call void @g1(i8* %arg3, i64 0, i64 0) + %51 = addrspacecast %record* %18 to %record addrspace(1)* + %52 = call i8 addrspace(1)* @g2(i8 addrspace(1)* null, %record addrspace(1)* %51) + call void @g3(i8 addrspace(1)* %52) + unreachable + +ifelse10: ; preds = %ifelse9 + %icmpsgt1 = icmp sgt i64 %conv4, -1 + br i1 %icmpsgt1, label %tmp1, label %tmp2 + +; CHECK: ifthen10: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8*, i64, i64)* @g1, i32 3, i32 0, i8* %arg3, i64 0, i64 0) [ "struct-live"(%record* %17) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*, %record addrspace(1)*)* @g2, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %58) [ "struct-live"(%record* %17) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %59) [ "gc-live"(i8 addrspace(1)* %59) ] +; +ifthen10: ; preds = %ifelse9 + call void @g1(i8* %arg3, i64 0, i64 0) + %53 = addrspacecast %record* %17 to %record addrspace(1)* + %54 = call i8 addrspace(1)* @g2(i8 addrspace(1)* null, %record addrspace(1)* %53) + call void @g3(i8 addrspace(1)* %54) + unreachable + +; CHECK: tmp2: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8*, i32)* @g5, i32 2, i32 0, i8* %arg3, i32 40) +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g6, i32 1, i32 0, i8 addrspace(1)* %61) [ "gc-live"(i8 addrspace(1)* %61) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %62) [ "gc-live"(i8 addrspace(1)* %62) ] +; +tmp2: ; preds = %ifelse10 + %55 = call i8 addrspace(1)* @g5(i8* %arg3, i32 40) + call void @g6(i8 addrspace(1)* %55) + call void @g3(i8 addrspace(1)* %55) + unreachable + +; CHECK: tmp1: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i64, i8*)* @g4, i32 2, i32 0, i64 %conv4, i8* %arg3) [ "gc-live"(i8 addrspace(1)* %.0), "struct-live"(%record* %17, %record* %21) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8*, i32)* @g5, i32 2, i32 0, i8* %arg3, i32 24) [ "gc-live"(i8 addrspace(1)* %arg0.reloc80, i8 addrspace(1)* %64), "struct-live"(%record* %17, %record* %21) ] +; +tmp1: ; preds = %ifelse10 + %56 = call i8 addrspace(1)* @g4(i64 %conv4, i8* %arg3) + %57 = call i8 addrspace(1)* @g5(i8* %arg3, i32 24) + %58 = getelementptr inbounds i8, i8 addrspace(1)* %57, i64 8 + %59 = bitcast i8 addrspace(1)* %58 to i8 addrspace(1)* addrspace(1)* + %60 = load i8, i8 addrspace(1)* %58 + %icmpsle1 = icmp sle i8 %60, 8 + br i1 %icmpsle1, label %tmp3, label %tmp4 + +; CHECK: tmp4: +; CHECK-NEXT: [[REMAT0:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[TMP0:%.*]], i64 8 +; CHECK-NEXT: [[REMAT1:%.*]] = bitcast i8 addrspace(1)* [[REMAT0]] to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*)* @g7, i32 3, i32 0, i8 addrspace(1)* %arg0.reloc83, i8 addrspace(1)* %65, i8 addrspace(1)* addrspace(1)* [[REMAT1]]) [ "gc-live"(i8 addrspace(1)* %arg0.reloc83, i8 addrspace(1)* %66, i8 addrspace(1)* %65), "struct-live"(%record* %17, %record* %21) ] +; +tmp4: ; preds = %tmp1 + call void @g7(i8 addrspace(1)* %arg0, i8 addrspace(1)* %57, i8 addrspace(1)* addrspace(1)* %59) + br label %tmpend2 + +tmp3: ; preds = %tmp1 + store i8 addrspace(1)* %arg0, i8 addrspace(1)* addrspace(1)* %59 + br label %tmpend2 + +tmpend2: ; preds = %tmp3, %tmp4 + %61 = getelementptr inbounds i8, i8 addrspace(1)* %57, i64 16 + %62 = bitcast i8 addrspace(1)* %61 to i64 addrspace(1)* + br i1 %icmpsle1, label %tmp5, label %tmp6 + +; CHECK: tmp6: +; CHECK-NEXT: [[REMAT2:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[TMP1:%.*]], i64 16 +; CHECK-NEXT: [[REMAT3:%.*]] = bitcast i8 addrspace(1)* [[REMAT2]] to i64 addrspace(1)* +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i64, i8 addrspace(1)*, i64 addrspace(1)*)* @g8, i32 3, i32 0, i64 %arg2, i8 addrspace(1)* %.0196, i64 addrspace(1)* [[REMAT3]]) [ "gc-live"(i8 addrspace(1)* %.1, i8 addrspace(1)* %.0192, i8 addrspace(1)* %.0196), "struct-live"(%record* %17, %record* %21) ] +; +tmp6: ; preds = %tmpend2 + call void @g8(i64 %arg2, i8 addrspace(1)* %57, i64 addrspace(1)* %62) + br label %tmpend3 + +tmp5: ; preds = %tmpend2 + store i64 %arg2, i64 addrspace(1)* %62 + br label %tmpend3 + +; CHECK: tmpend3: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8*, i32)* @g5, i32 2, i32 0, i8* %arg3, i32 24) [ "gc-live"(i8 addrspace(1)* %.2, i8 addrspace(1)* %.1193, i8 addrspace(1)* %.1197), "struct-live"(%record* %17, %record* %21) ] +; +tmpend3: ; preds = %tmp5, %tmp6 + %63 = call i8 addrspace(1)* @g5(i8* %arg3, i32 24) + %64 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %59 + %65 = getelementptr inbounds i8, i8 addrspace(1)* %63, i64 8 + %66 = bitcast i8 addrspace(1)* %65 to i8 addrspace(1)* addrspace(1)* + %67 = load i8, i8 addrspace(1)* %65 + %icmpsle2 = icmp sle i8 %67, 8 + br i1 %icmpsle2, label %tmp7, label %tmp8 + +; CHECK: tmp8: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*)* @g7, i32 3, i32 0, i8 addrspace(1)* %79, i8 addrspace(1)* %76, i8 addrspace(1)* addrspace(1)* %81) [ "gc-live"(i8 addrspace(1)* %arg0.reloc92, i8 addrspace(1)* %77, i8 addrspace(1)* %76, i8 addrspace(1)* %78, i8 addrspace(1)* %79), "struct-live"(%record* %17, %record* %21) ] +; +tmp8: ; preds = %tmpend3 + call void @g7(i8 addrspace(1)* %64, i8 addrspace(1)* %63, i8 addrspace(1)* addrspace(1)* %66) + br label %tmpend4 + +tmp7: ; preds = %tmpend3 + store i8 addrspace(1)* %64, i8 addrspace(1)* addrspace(1)* %66 + br label %tmpend4 + +tmpend4: ; preds = %tmp7, %tmp8 + %68 = load i64, i64 addrspace(1)* %62 + %69 = getelementptr inbounds i8, i8 addrspace(1)* %63, i64 16 + %70 = bitcast i8 addrspace(1)* %69 to i64 addrspace(1)* + br i1 %icmpsle2, label %tmp9, label %tmp10 + +; CHECK: tmp10: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i64, i8 addrspace(1)*, i64 addrspace(1)*)* @g8, i32 3, i32 0, i64 %87, i8 addrspace(1)* %.0199, i64 addrspace(1)* %89) [ "gc-live"(i8 addrspace(1)* %.3, i8 addrspace(1)* %.2194, i8 addrspace(1)* %.0199), "struct-live"(%record* %17, %record* %21) ] +; +tmp10: ; preds = %tmpend4 + call void @g8(i64 %68, i8 addrspace(1)* %63, i64 addrspace(1)* %70) + br label %tmpend5 + +tmp9: ; preds = %tmpend4 + store i64 %68, i64 addrspace(1)* %70 + br label %tmpend5 + +; CHECK: tmpend5: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*, i64, i8 addrspace(1)*)* @g9, i32 3, i32 0, i8 addrspace(1)* %.3195, i64 %conv4, i8 addrspace(1)* %.1200) [ "gc-live"(i8 addrspace(1)* %.4, i8 addrspace(1)* %.3195, i8 addrspace(1)* %.1200), "struct-live"(%record* %17, %record* %21) ] +; +tmpend5: ; preds = %tmp9, %tmp10 + %71 = call i8 addrspace(1)* @g9(i8 addrspace(1)* %56, i64 %conv4, i8 addrspace(1)* %63) + br label %body2 + +body2: ; preds = %tmpend5, %ifthen5 + %72 = phi i8 addrspace(1)* [ %44, %ifthen5 ], [ %71, %tmpend5 ] + %73 = getelementptr inbounds i8, i8 addrspace(1)* %72, i64 8 + %74 = bitcast i8 addrspace(1)* %73 to i64 addrspace(1)* + %75 = load i64, i64 addrspace(1)* %74 + %icmpeq1 = icmp eq i64 %75, 0 + br i1 %icmpeq1, label %tmp11, label %tmp12 + +tmp12: ; preds = %body2 + %76 = load %array addrspace(1)*, %array addrspace(1)* addrspace(1)* %27 + %77 = getelementptr inbounds %array, %array addrspace(1)* %76, i64 0, i32 1, i64 %arg2 + %78 = load i32, i32 addrspace(1)* %77 + %79 = getelementptr inbounds i8, i8 addrspace(1)* %arg0, i64 8 + %80 = bitcast i8 addrspace(1)* %79 to i32 addrspace(1)* + %81 = load i32, i32 addrspace(1)* %80 + %icmpeq2 = icmp eq i32 %78, %81 + br i1 %icmpeq2, label %tmp13, label %tmp14 + +tmp14: ; preds = %tmp12 + %82 = bitcast %record* %12 to i8* + %83 = bitcast %record* %13 to i8* + %84 = bitcast %record* %14 to i8* + %85 = bitcast %record* %15 to i8* + %86 = bitcast %record* %16 to i8* + %87 = bitcast i8 addrspace(1)* %72 to %array addrspace(1)* + br label %tmpend7 + +; CHECK: tmp13: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8*, i32)* @g5, i32 2, i32 0, i8* %arg3, i32 40) [ "struct-live"(%record* %17) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, i8 addrspace(1)*, %record addrspace(1)*)* @g10, i32 3, i32 0, i8 addrspace(1)* %111, i8 addrspace(1)* null, %record addrspace(1)* %112) [ "gc-live"(i8 addrspace(1)* %111), "struct-live"(%record* %17) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %113) [ "gc-live"(i8 addrspace(1)* %113) ] +; +tmp13: ; preds = %ifelse16, %tmp12 + %88 = call i8 addrspace(1)* @g5(i8* %arg3, i32 40) + %89 = addrspacecast %record* %17 to %record addrspace(1)* + call void @g10(i8 addrspace(1)* %88, i8 addrspace(1)* null, %record addrspace(1)* %89) + call void @g3(i8 addrspace(1)* %88) + unreachable + +tmpend7: ; preds = %ifelse16, %tmp14 + %90 = phi %array addrspace(1)* [ %76, %tmp14 ], [ %117, %ifelse16 ] + %91 = phi i32 [ %78, %tmp14 ], [ %120, %ifelse16 ] + %92 = phi i64 [ %arg2, %tmp14 ], [ %conv17, %ifelse16 ] + %icmpeq3 = icmp eq i32 %91, 34 + br i1 %icmpeq3, label %tmp15, label %tmp16 + +tmp15: ; preds = %tmpend7 + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %82, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %83, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %84, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %85, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %86, i8 0, i64 16, i1 false) + %93 = load i64, i64 addrspace(1)* %74 + %94 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %93, i64 -1) + %conv6 = extractvalue { i64, i1 } %94, 1 + br i1 %conv6, label %ifthen11, label %ifelse11 + +ifelse11: ; preds = %tmp15 + %95 = load %array addrspace(1)*, %array addrspace(1)* addrspace(1)* %27 + br label %ifend11 + +; CHECK: ifthen11: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8*, i64, i64)* @g1, i32 3, i32 0, i8* %arg3, i64 0, i64 0) [ "struct-live"(%record* %16) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*, %record addrspace(1)*)* @g2, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %121) [ "struct-live"(%record* %16) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %122) [ "gc-live"(i8 addrspace(1)* %122) ] +; +ifthen11: ; preds = %tmp15 + call void @g1(i8* %arg3, i64 0, i64 0) + %96 = addrspacecast %record* %16 to %record addrspace(1)* + %97 = call i8 addrspace(1)* @g2(i8 addrspace(1)* null, %record addrspace(1)* %96) + call void @g3(i8 addrspace(1)* %97) + unreachable + +ifend11: ; preds = %ifelse12, %ifelse11 + %98 = phi { i64, i1 } [ %106, %ifelse12 ], [ %94, %ifelse11 ] + %99 = phi i64 [ %conv25, %ifelse12 ], [ %92, %ifelse11 ] + %100 = extractvalue { i64, i1 } %98, 0 + %101 = getelementptr inbounds %array, %array addrspace(1)* %95, i64 0, i32 1, i64 %99 + %102 = load i32, i32 addrspace(1)* %101 + %103 = getelementptr inbounds %array, %array addrspace(1)* %87, i64 0, i32 1, i64 %100 + %104 = load i32, i32 addrspace(1)* %103 + %icmpeq4 = icmp eq i32 %102, %104 + br i1 %icmpeq4, label %tmp17, label %tmp18 + +tmp18: ; preds = %ifend11 + br label %tmp16 + +tmp17: ; preds = %ifend11 + %icmpeq5 = icmp eq i64 %100, 0 + br i1 %icmpeq5, label %tmp19, label %tmp20 + +tmp20: ; preds = %tmp17 + %105 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %99, i64 1) + %conv7 = extractvalue { i64, i1 } %105, 1 + br i1 %conv7, label %ifthen12, label %ifelse12 + +; CHECK: ifelse12: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @g0, i32 0, i32 0) [ "gc-live"(%array addrspace(1)* %.0204, i8 addrspace(1)* %.7, i8 addrspace(1)* %.1202), "struct-live"(%record* %14, %record* %21, %record* %13, %record* %12, %record* %17, %record* %15) ] +; +ifelse12: ; preds = %tmp20 + %conv25 = extractvalue { i64, i1 } %105, 0 + %106 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %100, i64 -1) + %conv8 = extractvalue { i64, i1 } %106, 1 + call void @g0() + br i1 %conv8, label %ifthen13, label %ifend11 + +; CHECK: ifthen12: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8*, i64, i64)* @g1, i32 3, i32 0, i8* %arg3, i64 0, i64 0) [ "struct-live"(%record* %15) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*, %record addrspace(1)*)* @g2, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %136) [ "struct-live"(%record* %15) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %137) [ "gc-live"(i8 addrspace(1)* %137) ] +; +ifthen12: ; preds = %tmp20 + call void @g1(i8* %arg3, i64 0, i64 0) + %107 = addrspacecast %record* %15 to %record addrspace(1)* + %108 = call i8 addrspace(1)* @g2(i8 addrspace(1)* null, %record addrspace(1)* %107) + call void @g3(i8 addrspace(1)* %108) + unreachable + +; CHECK: ifthen13: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8*, i64, i64)* @g1, i32 3, i32 0, i8* %arg3, i64 0, i64 0) [ "struct-live"(%record* %14) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*, %record addrspace(1)*)* @g2, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %139) [ "struct-live"(%record* %14) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %140) [ "gc-live"(i8 addrspace(1)* %140) ] +; +ifthen13: ; preds = %ifelse12 + call void @g1(i8* %arg3, i64 0, i64 0) + %109 = addrspacecast %record* %14 to %record addrspace(1)* + %110 = call i8 addrspace(1)* @g2(i8 addrspace(1)* null, %record addrspace(1)* %109) + call void @g3(i8 addrspace(1)* %110) + unreachable + +tmp19: ; preds = %tmp17 + %111 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %92, i64 %93) + %conv9 = extractvalue { i64, i1 } %111, 1 + br i1 %conv9, label %ifthen14, label %ifelse14 + +ifelse14: ; preds = %tmp19 + %conv26 = extractvalue { i64, i1 } %111, 0 + %112 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %conv26, i64 -1) + %conv10 = extractvalue { i64, i1 } %112, 1 + br i1 %conv10, label %ifthen15, label %ifelse15 + +; CHECK: ifthen14: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8*, i64, i64)* @g1, i32 3, i32 0, i8* %arg3, i64 0, i64 0) [ "struct-live"(%record* %13) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*, %record addrspace(1)*)* @g2, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %144) [ "struct-live"(%record* %13) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %145) [ "gc-live"(i8 addrspace(1)* %145) ] +; +ifthen14: ; preds = %tmp19 + call void @g1(i8* %arg3, i64 0, i64 0) + %113 = addrspacecast %record* %13 to %record addrspace(1)* + %114 = call i8 addrspace(1)* @g2(i8 addrspace(1)* null, %record addrspace(1)* %113) + call void @g3(i8 addrspace(1)* %114) + unreachable + +; CHECK: ifthen15: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8*, i64, i64)* @g1, i32 3, i32 0, i8* %arg3, i64 0, i64 0) [ "struct-live"(%record* %12) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*, %record addrspace(1)*)* @g2, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %147) [ "struct-live"(%record* %12) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %148) [ "gc-live"(i8 addrspace(1)* %148) ] +; +ifthen15: ; preds = %ifelse14 + call void @g1(i8* %arg3, i64 0, i64 0) + %115 = addrspacecast %record* %12 to %record addrspace(1)* + %116 = call i8 addrspace(1)* @g2(i8 addrspace(1)* null, %record addrspace(1)* %115) + call void @g3(i8 addrspace(1)* %116) + unreachable + +ifelse15: ; preds = %ifelse14 + %conv27 = extractvalue { i64, i1 } %112, 0 + %icmpsgt2 = icmp sgt i64 %conv27, %92 + br i1 %icmpsgt2, label %body3, label %tmp16 + +tmp16: ; preds = %ifelse15, %tmp18, %tmpend7 + %117 = phi %array addrspace(1)* [ %95, %tmp18 ], [ %90, %tmpend7 ], [ %95, %ifelse15 ] + %118 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %92, i64 1) + %conv11 = extractvalue { i64, i1 } %118, 1 + br i1 %conv11, label %ifthen16, label %ifelse16 + +; CHECK: ifelse16: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @g0, i32 0, i32 0) [ "gc-live"(%array addrspace(1)* %base_phi, %array addrspace(1)* %150, i8 addrspace(1)* %.8, i8 addrspace(1)* %.2203), "struct-live"(%record* %17, %record* %21) ] +; +ifelse16: ; preds = %tmp16 + %conv17 = extractvalue { i64, i1 } %118, 0 + %119 = getelementptr inbounds %array, %array addrspace(1)* %117, i64 0, i32 1, i64 %conv17 + %120 = load i32, i32 addrspace(1)* %119 + %121 = load i32, i32 addrspace(1)* %80 + %icmpeq6 = icmp eq i32 %120, %121 + call void @g0() + br i1 %icmpeq6, label %tmp13, label %tmpend7 + +; CHECK: ifthen16: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8*, i64, i64)* @g1, i32 3, i32 0, i8* %arg3, i64 0, i64 0) [ "struct-live"(%record* %21) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*, %record addrspace(1)*)* @g2, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %158) [ "struct-live"(%record* %21) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %159) [ "gc-live"(i8 addrspace(1)* %159) ] +; +ifthen16: ; preds = %tmp16 + call void @g1(i8* %arg3, i64 0, i64 0) + %122 = addrspacecast %record* %21 to %record addrspace(1)* + %123 = call i8 addrspace(1)* @g2(i8 addrspace(1)* null, %record addrspace(1)* %122) + call void @g3(i8 addrspace(1)* %123) + unreachable + +body3: ; preds = %ifelse15 + %conv12 = extractvalue { i64, i1 } %112, 0 + br label %tmp11 + +tmp11: ; preds = %body3, %body2 + %124 = phi i64 [ %arg2, %body2 ], [ %conv12, %body3 ] + %icmpeq7 = icmp eq i64 %124, -1 + br i1 %icmpeq7, label %ifthen4, label %tmp38 + +; CHECK: ifthen4: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8*, i32)* @g5, i32 2, i32 0, i8* %arg3, i32 40) [ "struct-live"(%record* %17) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, i8 addrspace(1)*, %record addrspace(1)*)* @g10, i32 3, i32 0, i8 addrspace(1)* %162, i8 addrspace(1)* null, %record addrspace(1)* %163) [ "gc-live"(i8 addrspace(1)* %162), "struct-live"(%record* %17) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %164) [ "gc-live"(i8 addrspace(1)* %164) ] +; +ifthen4: ; preds = %tmp11 + %125 = call i8 addrspace(1)* @g5(i8* %arg3, i32 40) + %126 = addrspacecast %record* %17 to %record addrspace(1)* + call void @g10(i8 addrspace(1)* %125, i8 addrspace(1)* null, %record addrspace(1)* %126) + call void @g3(i8 addrspace(1)* %125) + unreachable + +tmp38: ; preds = %entry, %end1, %tmp11 + %127 = phi i64 [ %124, %tmp11 ], [ %195, %end1 ], [ %arg2, %entry ] + ret i64 %127 + +ifthen2: ; preds = %entry + %128 = bitcast %record* %6 to i8* + %129 = bitcast %record* %7 to i8* + %130 = bitcast %record* %8 to i8* + %131 = bitcast %record* %9 to i8* + %132 = bitcast %record* %10 to i8* + %133 = bitcast %record* %11 to i8* + %a0 = bitcast %record* %17 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %128, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %129, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %130, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %131, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %132, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %133, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %a0, i8 0, i64 16, i1 false) + %134 = getelementptr inbounds i8, i8 addrspace(1)* %arg0, i64 64 + %135 = bitcast i8 addrspace(1)* %134 to i64 addrspace(1)* + %136 = load i64, i64 addrspace(1)* %135 + %137 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %arg2, i64 3) + %conv13 = extractvalue { i64, i1 } %137, 0 + %conv14 = extractvalue { i64, i1 } %137, 1 + br i1 %conv14, label %ifthen17, label %ifelse17 + +ifelse17: ; preds = %ifthen2 + %icmpsgt3 = icmp sgt i64 %136, %conv13 + br i1 %icmpsgt3, label %tmp21, label %tmp22 + +; CHECK: ifthen17: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8*, i64, i64)* @g1, i32 3, i32 0, i8* %arg3, i64 0, i64 0) [ "struct-live"(%record* %11) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*, %record addrspace(1)*)* @g2, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %177) [ "struct-live"(%record* %11) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %178) [ "gc-live"(i8 addrspace(1)* %178) ] +; +ifthen17: ; preds = %ifthen2 + call void @g1(i8* %arg3, i64 0, i64 0) + %138 = addrspacecast %record* %11 to %record addrspace(1)* + %139 = call i8 addrspace(1)* @g2(i8 addrspace(1)* null, %record addrspace(1)* %138) + call void @g3(i8 addrspace(1)* %139) + unreachable + +tmp21: ; preds = %ifelse17 + %140 = add nsw i64 %arg2, 1 + %141 = load %array addrspace(1)*, %array addrspace(1)* addrspace(1)* %27 + %142 = getelementptr inbounds %array, %array addrspace(1)* %141, i64 0, i32 1, i64 %140 + %143 = load i32, i32 addrspace(1)* %142 + %icmpeq8 = icmp eq i32 %143, 34 + br i1 %icmpeq8, label %tmp23, label %tmp24 + +tmp23: ; preds = %tmp21 + %144 = add nsw i64 %arg2, 2 + %145 = getelementptr inbounds %array, %array addrspace(1)* %141, i64 0, i32 1, i64 %144 + %146 = load i32, i32 addrspace(1)* %145 + %icmpeq9 = icmp eq i32 %146, 34 + br i1 %icmpeq9, label %tmp25, label %tmp26 + +tmp25: ; preds = %tmp23 + %147 = getelementptr inbounds %array, %array addrspace(1)* %141, i64 0, i32 1, i64 %conv13 + %148 = load i32, i32 addrspace(1)* %147 + switch i32 %148, label %ifthen18 [ + i32 10, label %ifthen19 + i32 13, label %ifthen20 + ] + +ifthen20: ; preds = %tmp25 + %149 = add nsw i64 %conv13, 1 + %icmpsgt4 = icmp sgt i64 %136, %149 + br i1 %icmpsgt4, label %tmp27, label %tmp28 + +tmp27: ; preds = %ifthen20 + %150 = getelementptr inbounds %array, %array addrspace(1)* %141, i64 0, i32 1, i64 %149 + %151 = load i32, i32 addrspace(1)* %150 + %icmpeq10 = icmp eq i32 %151, 10 + br i1 %icmpeq10, label %tmp29, label %tmp30 + +ifthen18: ; preds = %tmp25 + br label %tmp30 + +ifthen19: ; preds = %tmp25 + br label %tmp29 + +tmp29: ; preds = %ifthen19, %tmp27 + %152 = bitcast %record* %1 to i8* + %153 = bitcast %record* %2 to i8* + %154 = bitcast %record* %3 to i8* + %155 = bitcast %record* %4 to i8* + %156 = bitcast %record* %5 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %152, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %153, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %154, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %155, i8 0, i64 16, i1 false) + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %156, i8 0, i64 16, i1 false) + %157 = load %array addrspace(1)*, %array addrspace(1)* addrspace(1)* %27 + %158 = getelementptr inbounds %array, %array addrspace(1)* %157, i64 0, i32 1, i64 %conv13 + %159 = load i32, i32 addrspace(1)* %158 + %160 = getelementptr inbounds i8, i8 addrspace(1)* %arg0, i64 8 + %161 = bitcast i8 addrspace(1)* %160 to i32 addrspace(1)* + %162 = load i32, i32 addrspace(1)* %161 + %icmpeq11 = icmp eq i32 %159, %162 + br i1 %icmpeq11, label %tmp31, label %tmp32 + +; CHECK: tmp31: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8*, i32)* @g5, i32 2, i32 0, i8* %arg3, i32 40) [ "struct-live"(%record* %17) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, i8 addrspace(1)*, %record addrspace(1)*)* @g10, i32 3, i32 0, i8 addrspace(1)* %203, i8 addrspace(1)* null, %record addrspace(1)* %204) [ "gc-live"(i8 addrspace(1)* %203), "struct-live"(%record* %17) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %205) [ "gc-live"(i8 addrspace(1)* %205) ] +; +tmp31: ; preds = %ifelse22, %tmp29 + %163 = call i8 addrspace(1)* @g5(i8* %arg3, i32 40) + %164 = addrspacecast %record* %17 to %record addrspace(1)* + call void @g10(i8 addrspace(1)* %163, i8 addrspace(1)* null, %record addrspace(1)* %164) + call void @g3(i8 addrspace(1)* %163) + unreachable + +tmp32: ; preds = %tmp29, %ifelse22 + %165 = phi i32 [ %179, %ifelse22 ], [ %159, %tmp29 ] + %166 = phi i64 [ %conv18, %ifelse22 ], [ %conv13, %tmp29 ] + %icmpeq12 = icmp eq i32 %165, 34 + br i1 %icmpeq12, label %tmp33, label %tmp34 + +tmp33: ; preds = %tmp32 + %167 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %166, i64 3) + %conv15 = extractvalue { i64, i1 } %167, 1 + br i1 %conv15, label %ifthen21, label %ifelse21 + +ifelse21: ; preds = %tmp33 + %conv28 = extractvalue { i64, i1 } %167, 0 + %168 = load i64, i64 addrspace(1)* %135 + %icmpslt1 = icmp slt i64 %168, %conv28 + br i1 %icmpslt1, label %tmp34, label %body4 + +; CHECK: ifthen21: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8*, i64, i64)* @g1, i32 3, i32 0, i8* %arg3, i64 0, i64 0) [ "struct-live"(%record* %5) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*, %record addrspace(1)*)* @g2, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %211) [ "struct-live"(%record* %5) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %212) [ "gc-live"(i8 addrspace(1)* %212) ] +; +ifthen21: ; preds = %tmp33 + call void @g1(i8* %arg3, i64 0, i64 0) + %169 = addrspacecast %record* %5 to %record addrspace(1)* + %170 = call i8 addrspace(1)* @g2(i8 addrspace(1)* null, %record addrspace(1)* %169) + call void @g3(i8 addrspace(1)* %170) + unreachable + +body4: ; preds = %ifelse21 + %171 = add nsw i64 %166, 1 + %172 = getelementptr inbounds %array, %array addrspace(1)* %157, i64 0, i32 1, i64 %171 + %173 = load i32, i32 addrspace(1)* %172 + %icmpeq13 = icmp eq i32 %173, 34 + br i1 %icmpeq13, label %body5, label %tmp34 + +body5: ; preds = %body4 + %174 = add nsw i64 %166, 2 + %175 = getelementptr inbounds %array, %array addrspace(1)* %157, i64 0, i32 1, i64 %174 + %176 = load i32, i32 addrspace(1)* %175 + %icmpeq14 = icmp eq i32 %176, 34 + br i1 %icmpeq14, label %body6, label %tmp34 + +tmp34: ; preds = %body5, %body4, %ifelse21, %tmp32 + %177 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %166, i64 1) + %conv16 = extractvalue { i64, i1 } %177, 1 + br i1 %conv16, label %ifthen22, label %ifelse22 + +; CHECK: ifelse22: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @g0, i32 0, i32 0) [ "gc-live"(%array addrspace(1)* %.0191, i8 addrspace(1)* %.9), "struct-live"(%record* %17, %record* %1, %record* %5) ] +; +ifelse22: ; preds = %tmp34 + %conv18 = extractvalue { i64, i1 } %177, 0 + %178 = getelementptr inbounds %array, %array addrspace(1)* %157, i64 0, i32 1, i64 %conv18 + %179 = load i32, i32 addrspace(1)* %178 + %icmpeq15 = icmp eq i32 %179, %162 + call void @g0() + br i1 %icmpeq15, label %tmp31, label %tmp32 + +; CHECK: ifthen22: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8*, i64, i64)* @g1, i32 3, i32 0, i8* %arg3, i64 0, i64 0) [ "struct-live"(%record* %1) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*, %record addrspace(1)*)* @g2, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %225) [ "struct-live"(%record* %1) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %226) [ "gc-live"(i8 addrspace(1)* %226) ] +; +ifthen22: ; preds = %tmp34 + call void @g1(i8* %arg3, i64 0, i64 0) + %180 = addrspacecast %record* %1 to %record addrspace(1)* + %181 = call i8 addrspace(1)* @g2(i8 addrspace(1)* null, %record addrspace(1)* %180) + call void @g3(i8 addrspace(1)* %181) + unreachable + +body6: ; preds = %body5 + br label %end1 + +tmp26: ; preds = %tmp23 + br label %tmp30 + +tmp22: ; preds = %ifelse17 + %182 = add nsw i64 %arg2, 1 + br label %tmp30 + +tmp24: ; preds = %tmp21 + br label %tmp30 + +tmp28: ; preds = %ifthen20 + br label %tmp30 + +tmp30: ; preds = %tmp27, %tmp26, %tmp22, %tmp24, %tmp28, %ifthen18 + %183 = phi i64 [ %140, %tmp27 ], [ %140, %tmp26 ], [ %182, %tmp22 ], [ %140, %tmp24 ], [ %140, %tmp28 ], [ %140, %ifthen18 ] + %184 = bitcast %record* %0 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %184, i8 0, i64 16, i1 false) + %185 = getelementptr inbounds i8, i8 addrspace(1)* %arg0, i64 8 + %186 = bitcast i8 addrspace(1)* %185 to i32 addrspace(1)* + %187 = load i64, i64 addrspace(1)* %135 + %icmpsgt5 = icmp sgt i64 %187, %183 + br i1 %icmpsgt5, label %tmp35, label %tmp36 + +tmp35: ; preds = %tmp30 + %188 = load %array addrspace(1)*, %array addrspace(1)* addrspace(1)* %27 + br label %body7 + +body7: ; preds = %body8, %tmp35 + %189 = phi i64 [ %183, %tmp35 ], [ %193, %body8 ] + %190 = getelementptr inbounds %array, %array addrspace(1)* %188, i64 0, i32 1, i64 %189 + %191 = load i32, i32 addrspace(1)* %190 + switch i32 %191, label %ifthen23 [ + i32 34, label %tmp36 + i32 10, label %tmp36 + ] + +ifthen23: ; preds = %body7 + %192 = load i32, i32 addrspace(1)* %186 + %icmpeq16 = icmp eq i32 %191, %192 + br i1 %icmpeq16, label %tmp36, label %body8 + +body8: ; preds = %ifthen23 + %193 = add i64 %189, 1 + %icmpeq17 = icmp eq i64 %193, %187 + br i1 %icmpeq17, label %tmp36, label %body7 + +tmp36: ; preds = %body7, %body7, %ifthen23, %body8, %tmp30 + %194 = phi i64 [ -1, %tmp30 ], [ %189, %ifthen23 ], [ %189, %body7 ], [ %189, %body7 ], [ -1, %body8 ] + br label %end1 + +end1: ; preds = %tmp36, %body6 + %195 = phi i64 [ %174, %body6 ], [ %194, %tmp36 ] + %icmpeq18 = icmp eq i64 %195, -1 + br i1 %icmpeq18, label %tmp37, label %tmp38 + +; CHECK: tmp37: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8*, i32)* @g5, i32 2, i32 0, i8* %arg3, i32 40) [ "struct-live"(%record* %17) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, i8 addrspace(1)*, %record addrspace(1)*)* @g10, i32 3, i32 0, i8 addrspace(1)* %242, i8 addrspace(1)* null, %record addrspace(1)* %243) [ "gc-live"(i8 addrspace(1)* %242), "struct-live"(%record* %17) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g3, i32 1, i32 0, i8 addrspace(1)* %244) [ "gc-live"(i8 addrspace(1)* %244) ] +; +tmp37: ; preds = %end1 + %196 = call i8 addrspace(1)* @g5(i8* %arg3, i32 40) + %197 = addrspacecast %record* %17 to %record addrspace(1)* + call void @g10(i8 addrspace(1)* %196, i8 addrspace(1)* null, %record addrspace(1)* %197) + call void @g3(i8 addrspace(1)* %196) + unreachable +} + +attributes #0 = { "hasRcdParam" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi7.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi7.ll new file mode 100644 index 000000000..1153afad9 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-phi7.ll @@ -0,0 +1,41 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + + +%struct_1 = type { i32, i32 } +%struct_2 = type { i8 addrspace(1)* } + +%struct_all = type { %struct_1, %struct_2 } + +define void @main() gc "cangjie" { +; CHECK: %token = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i64 ()* @tmp_use, i32 0, i32 0) +; +entry: + %r = alloca %struct_all + %0 = bitcast %struct_all* %r to i8* + call void @llvm.memset.p0i8.i64(i8* %0, i8 0, i64 8, i1 false) + %cmp = call i64 @tmp_use() + %icmpeq.i = icmp eq i64 %cmp, 0 + br i1 %icmpeq.i, label %bb1, label %bb2 + +bb1: + %1 = getelementptr inbounds %struct_all, %struct_all* %r, i64 0, i32 0 + br label %bbend + +bb2: + %2 = getelementptr inbounds %struct_all, %struct_all* %r, i64 0, i32 0 + br label %bbend + +; CHECK: %token3 = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%struct_1 addrspace(1)*)* @test, i32 1, i32 0, %struct_1 addrspace(1)* %3) +; +bbend: + %sink = phi %struct_1* [%1, %bb1], [%2, %bb2] + %3 = addrspacecast %struct_1* %sink to %struct_1 addrspace(1)* + call void @test(%struct_1 addrspace(1)* %3) + ret void +} + +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) + +declare i64 @tmp_use() gc "cangjie" +declare void @test(%struct_1 addrspace(1)* %arg0) gc "cangjie" diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-select1.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-select1.ll new file mode 100644 index 000000000..d73fd3bcf --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-select1.ll @@ -0,0 +1,30 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record = type { i32, i8 addrspace(1)* } + +declare void @default() +declare void @test_func() + +; CHECK: entry: +; CHECK: %token = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @test_func, i32 0, i32 0) [ "gc-live"(i8 addrspace(1)* %arg2, i8 addrspace(1)* %arg1) ] +; CHECK: %token2 = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @default, i32 0, i32 0) [ "struct-live"(i8 addrspace(1)** %0, i8 addrspace(1)** %1) ] +; CHECK: bb1: +; CHECK: %token4 = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @default, i32 0, i32 0) [ "struct-live"(i8 addrspace(1)** %1, i8 addrspace(1)** %0) ] +; +define i8 addrspace(1)* @foo(i1 %cond, %record %arg0, i8 addrspace(1)* %arg1, i8 addrspace(1)* %arg2) #0 gc "cangjie" { +entry: + %0 = alloca i8 addrspace(1)* + %1 = alloca i8 addrspace(1)* + call void @test_func() + store i8 addrspace(1)* %arg1, i8 addrspace(1)** %0, align 8 + store i8 addrspace(1)* %arg2, i8 addrspace(1)** %1, align 8 + call void @default() + br label %bb1 + +bb1: ; preds = %entry + %2 = select i1 %cond, i8 addrspace(1)** %0, i8 addrspace(1)** %1 + call void @default() + %load = load i8 addrspace(1)*, i8 addrspace(1)** %2 + ret i8 addrspace(1)* %load +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-sroa1.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-sroa1.ll new file mode 100644 index 000000000..590881c46 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-sroa1.ll @@ -0,0 +1,82 @@ +; RUN: opt -cj-rewrite-statepoint -sroa -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint,sroa -S < %s | FileCheck %s + +%record = type { i64, i8 addrspace(1)* } + +declare void @g0() #0 gc "cangjie" +declare void @g1(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0) #0 gc "cangjie" + +define %record @foo(%record addrspace(1)* %arg2, i8 addrspace(1)* %arg1, %record %arg0) #0 gc "cangjie" { +; CHECK: entry: +; CHECK-NEXT: %.sroa.0.sroa.0 = alloca i8, align 8 +; CHECK-NEXT: %arg0.fca.0.extract = extractvalue %record %arg0, 0 +; CHECK-NEXT: %.sroa.0.0.extract.trunc = trunc i64 %arg0.fca.0.extract to i8 +; CHECK-NEXT: store i8 %.sroa.0.0.extract.trunc, i8* %.sroa.0.sroa.0, align 8 +; CHECK-NEXT: %.sroa.5.0.extract.shift = lshr i64 %arg0.fca.0.extract, 8 +; CHECK-NEXT: %.sroa.5.0.extract.trunc = trunc i64 %.sroa.5.0.extract.shift to i56 +; CHECK-NEXT: %arg0.fca.1.extract = extractvalue %record %arg0, 1 +; CHECK-NEXT: %.sroa.0.sroa.0.0..sroa.0.sroa.0.0..sroa.0.0.9 = load i8, i8* %.sroa.0.sroa.0, align 8 +; CHECK-NEXT: %.sroa.5.0.insert.ext = zext i56 %.sroa.5.0.extract.trunc to i64 +; CHECK-NEXT: %.sroa.5.0.insert.shift = shl i64 %.sroa.5.0.insert.ext, 8 +; CHECK-NEXT: %.sroa.5.0.insert.mask = and i64 undef, 255 +; CHECK-NEXT: %.sroa.5.0.insert.insert = or i64 %.sroa.5.0.insert.mask, %.sroa.5.0.insert.shift +; CHECK-NEXT: %.sroa.0.0.insert.ext = zext i8 %.sroa.0.sroa.0.0..sroa.0.sroa.0.0..sroa.0.0.9 to i64 +; CHECK-NEXT: %.sroa.0.0.insert.mask = and i64 %.sroa.5.0.insert.insert, -256 +; CHECK-NEXT: %.sroa.0.0.insert.insert = or i64 %.sroa.0.0.insert.mask, %.sroa.0.0.insert.ext +; CHECK-NEXT: %icmpeq = icmp eq i64 %.sroa.0.0.insert.insert, 1 +; +entry: + %0 = alloca %record + store %record %arg0, %record* %0 + %1 = getelementptr inbounds %record, %record* %0, i32 0, i32 0 + %2 = load i64, i64* %1 + %icmpeq = icmp eq i64 %2, 1 + br i1 %icmpeq, label %tmp1, label %tmp2 + +; CHECK: tmp1: +; CHECK-NEXT: %.sroa.0.sroa.0.0..sroa.0.sroa.0.0..sroa.0.0..fca.0.load = load i8, i8* %.sroa.0.sroa.0, align 8 +; CHECK-NEXT: %.sroa.5.0.insert.ext21 = zext i56 %.sroa.5.0.extract.trunc to i64 +; CHECK-NEXT: %.sroa.5.0.insert.shift22 = shl i64 %.sroa.5.0.insert.ext21, 8 +; CHECK-NEXT: %.sroa.5.0.insert.mask23 = and i64 undef, 255 +; CHECK-NEXT: %.sroa.5.0.insert.insert24 = or i64 %.sroa.5.0.insert.mask23, %.sroa.5.0.insert.shift22 +; CHECK-NEXT: %.sroa.0.0.insert.ext12 = zext i8 %.sroa.0.sroa.0.0..sroa.0.sroa.0.0..sroa.0.0..fca.0.load to i64 +; CHECK-NEXT: %.sroa.0.0.insert.mask13 = and i64 %.sroa.5.0.insert.insert24, -256 +; CHECK-NEXT: %.sroa.0.0.insert.insert14 = or i64 %.sroa.0.0.insert.mask13, %.sroa.0.0.insert.ext12 +; CHECK-NEXT: %.fca.0.insert = insertvalue %record poison, i64 %.sroa.0.0.insert.insert14, 0 +; CHECK-NEXT: %.fca.1.insert = insertvalue %record %.fca.0.insert, i8 addrspace(1)* %arg0.fca.1.extract, 1 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void ()* @g0, i32 0, i32 0) +; +tmp1: + %3 = load %record, %record* %0 + call void @g0() + br label %tmpend + +; CHECK: tmp2: +; CHECK-NEXT: %.sroa.0.sroa.0.0.sroa_cast33 = bitcast i8* %.sroa.0.sroa.0 to i1* +; CHECK-NEXT: %.sroa.0.sroa.0.0..sroa.0.sroa.0.0..sroa.0.0. = load i1, i1* %.sroa.0.sroa.0.0.sroa_cast33, align 8 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%record addrspace(1)*, i8 addrspace(1)*)* @g1, i32 2, i32 0, %record addrspace(1)* %arg2, i8 addrspace(1)* null) [ "gc-live"(%record %arg0, i8 addrspace(1)* %arg1, %record addrspace(1)* %arg2) ] +; +tmp2: + %4 = bitcast %record* %0 to i1* + %5 = load i1, i1* %4 + call void @g1(%record addrspace(1)* %arg2, i8 addrspace(1)* null) + ret %record %arg0 + +; CHECK: tmpend: +; CHECK-NEXT: %.sroa.0.sroa.0.0..sroa.0.sroa.0.0..sroa.0.0..fca.0.load4 = load i8, i8* %.sroa.0.sroa.0, align 8 +; CHECK-NEXT: %.sroa.5.0.insert.ext26 = zext i56 %.sroa.5.0.extract.trunc to i64 +; CHECK-NEXT: %.sroa.5.0.insert.shift27 = shl i64 %.sroa.5.0.insert.ext26, 8 +; CHECK-NEXT: %.sroa.5.0.insert.mask28 = and i64 undef, 255 +; CHECK-NEXT: %.sroa.5.0.insert.insert29 = or i64 %.sroa.5.0.insert.mask28, %.sroa.5.0.insert.shift27 +; CHECK-NEXT: %.sroa.0.0.insert.ext16 = zext i8 %.sroa.0.sroa.0.0..sroa.0.sroa.0.0..sroa.0.0..fca.0.load4 to i64 +; CHECK-NEXT: %.sroa.0.0.insert.mask17 = and i64 %.sroa.5.0.insert.insert29, -256 +; CHECK-NEXT: %.sroa.0.0.insert.insert18 = or i64 %.sroa.0.0.insert.mask17, %.sroa.0.0.insert.ext16 +; CHECK-NEXT: %.fca.0.insert5 = insertvalue %record poison, i64 %.sroa.0.0.insert.insert18, 0 +; CHECK-NEXT: %.fca.1.insert8 = insertvalue %record %.fca.0.insert5, i8 addrspace(1)* %arg0.fca.1.extract, 1 +; +tmpend: + %6 = load %record, %record* %0 + ret %record %6 +} + +attributes #0 = { "record_mut" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-sroa2.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-sroa2.ll new file mode 100644 index 000000000..585067e08 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record-sroa2.ll @@ -0,0 +1,79 @@ +; RUN: opt -cj-rewrite-statepoint -sroa -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint,sroa -S < %s | FileCheck %s + +%record = type { i64, i8 addrspace(1)* } + +declare void @g0(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0) #0 gc "cangjie" +declare %record @g1(%record %arg0, %record %arg1) #0 gc "cangjie" + +define %record @foo(%record addrspace(1)* %ptr2, i8 addrspace(1)* %ptr1, %record %arg0, i1 %b1, i1 %b2) #0 gc "cangjie" { +; CHECK: entry: +; CHECK-NEXT: %f = alloca %record, align 8 +; CHECK-NEXT: %g = alloca %record, align 8 +; CHECK-NEXT: %arg0.fca.0.extract3 = extractvalue %record %arg0, 0 +; CHECK-NEXT: %arg0.fca.0.gep4 = getelementptr inbounds %record, %record* %f, i32 0, i32 0 +; CHECK-NEXT: store i64 %arg0.fca.0.extract3, i64* %arg0.fca.0.gep4, align 8 +; CHECK-NEXT: %arg0.fca.1.extract5 = extractvalue %record %arg0, 1 +; CHECK-NEXT: %arg0.fca.1.gep6 = getelementptr inbounds %record, %record* %f, i32 0, i32 1 +; CHECK-NEXT: store i8 addrspace(1)* %arg0.fca.1.extract5, i8 addrspace(1)** %arg0.fca.1.gep6, align 8 +; CHECK-NEXT: %arg0.fca.0.extract = extractvalue %record %arg0, 0 +; CHECK-NEXT: %arg0.fca.0.gep = getelementptr inbounds %record, %record* %g, i32 0, i32 0 +; CHECK-NEXT: store i64 %arg0.fca.0.extract, i64* %arg0.fca.0.gep, align 8 +; CHECK-NEXT: %arg0.fca.1.extract = extractvalue %record %arg0, 1 +; CHECK-NEXT: %arg0.fca.1.gep = getelementptr inbounds %record, %record* %g, i32 0, i32 1 +; CHECK-NEXT: store i8 addrspace(1)* %arg0.fca.1.extract, i8 addrspace(1)** %arg0.fca.1.gep, align 8 +; CHECK-NEXT: %f.cast = addrspacecast %record* %f to %record addrspace(1)* +; CHECK-NEXT: %f.cast.sroa.gep8 = getelementptr inbounds %record, %record addrspace(1)* %f.cast, i32 0, i32 1 +; CHECK-NEXT: %f.cast.sroa.gep = getelementptr inbounds %record, %record addrspace(1)* %f.cast, i32 0, i32 0 +; CHECK-NEXT: %g.cast = addrspacecast %record* %g to %record addrspace(1)* +; CHECK-NEXT: %f.select = select i1 %b1, %record addrspace(1)* %f.cast, %record addrspace(1)* %g.cast +; CHECK-NEXT: %f.select.sroa.gep9 = getelementptr inbounds %record, %record addrspace(1)* %f.select, i32 0, i32 1 +; CHECK-NEXT: %f.select.sroa.gep = getelementptr inbounds %record, %record addrspace(1)* %f.select, i32 0, i32 0 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%record addrspace(1)*, i8 addrspace(1)*)* @g0, i32 2, i32 0, %record addrspace(1)* %ptr2, i8 addrspace(1)* %ptr1) [ "gc-live"(%record addrspace(1)* %ptr2, i8 addrspace(1)* %ptr1), "struct-live"(%record* %f, %record* %g) ] +; +entry: + %f = alloca %record + %g = alloca %record + store %record %arg0, %record* %f + store %record %arg0, %record* %g + %f.cast = addrspacecast %record* %f to %record addrspace(1)* + %g.cast = addrspacecast %record* %g to %record addrspace(1)* + %f.select = select i1 %b1, %record addrspace(1)* %f.cast, %record addrspace(1)* %g.cast + call void @g0(%record addrspace(1)* %ptr2, i8 addrspace(1)* %ptr1) + br i1 %b2, label %then, label %else + +then: + br label %exit + +else: + br label %exit + +; CHECK: exit: +; CHECK-NEXT: %f.phi = phi %record addrspace(1)* [ %f.cast, %then ], [ %f.select, %else ] +; CHECK-NEXT: %g.phi = phi %record addrspace(1)* [ %g.cast, %then ], [ %ptr2.reloc.casted, %else ] +; CHECK-NEXT: %f.phi.sroa.phi = phi i64 addrspace(1)* [ %f.cast.sroa.gep, %then ], [ %f.select.sroa.gep, %else ] +; CHECK-NEXT: %f.phi.sroa.phi7 = phi i8 addrspace(1)* addrspace(1)* [ %f.cast.sroa.gep8, %then ], [ %f.select.sroa.gep9, %else ] +; CHECK-NEXT: %f.loaded.fca.0.load = load i64, i64 addrspace(1)* %f.phi.sroa.phi, align 8 +; CHECK-NEXT: %f.loaded.fca.0.insert = insertvalue %record poison, i64 %f.loaded.fca.0.load, 0 +; CHECK-NEXT: %f.loaded.fca.1.load = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %f.phi.sroa.phi7, align 8 +; CHECK-NEXT: %f.loaded.fca.1.insert = insertvalue %record %f.loaded.fca.0.insert, i8 addrspace(1)* %f.loaded.fca.1.load, 1 +; CHECK-NEXT: %g.select = select i1 %b1, %record addrspace(1)* %g.cast, %record addrspace(1)* %g.phi +; CHECK-NEXT: %g.loaded.fca.0.gep = getelementptr inbounds %record, %record addrspace(1)* %g.select, i32 0, i32 0 +; CHECK-NEXT: %g.loaded.fca.0.load = load i64, i64 addrspace(1)* %g.loaded.fca.0.gep, align 8 +; CHECK-NEXT: %g.loaded.fca.0.insert = insertvalue %record poison, i64 %g.loaded.fca.0.load, 0 +; CHECK-NEXT: %g.loaded.fca.1.gep = getelementptr inbounds %record, %record addrspace(1)* %g.select, i32 0, i32 1 +; CHECK-NEXT: %g.loaded.fca.1.load = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %g.loaded.fca.1.gep, align 8 +; CHECK-NEXT: %g.loaded.fca.1.insert = insertvalue %record %g.loaded.fca.0.insert, i8 addrspace(1)* %g.loaded.fca.1.load, 1 +; CHECK-NEXT: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, %record (%record, %record)* @g1, i32 2, i32 0, %record %f.loaded.fca.1.insert, %record %g.loaded.fca.1.insert) [ "gc-live"(%record %f.loaded.fca.1.insert, %record %g.loaded.fca.1.insert) ] +; +exit: + %f.phi = phi %record addrspace(1)* [ %f.cast, %then ], [ %f.select, %else ] + %g.phi = phi %record addrspace(1)* [ %g.cast, %then ], [ %ptr2, %else ] + %f.loaded = load %record, %record addrspace(1)* %f.phi + %g.select = select i1 %b1, %record addrspace(1)* %g.cast, %record addrspace(1)* %g.phi + %g.loaded = load %record, %record addrspace(1)* %g.select + %result = call %record @g1(%record %f.loaded, %record %g.loaded) + ret %record %f.loaded +} + +attributes #0 = { "record_mut" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record1.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record1.ll new file mode 100644 index 000000000..2e168e675 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record1.ll @@ -0,0 +1,28 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record = type { i64, i8 addrspace(1)* } + +declare %record addrspace(1)* @g0(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0, i64 %arg2) #0 gc "cangjie" +declare void @g1(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0, i64 %arg2) #0 gc "cangjie" + +define void @foo(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0, i64 %arg2, i64 %arg3) #0 gc "cangjie" { +; CHECK-LABEL: @foo( +; CHECK: entry: +; CHECK: call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, %record addrspace(1)* (%record addrspace(1)*, i8 addrspace(1)*, i64)* @g0, i32 3, i32 0, %record addrspace(1)* %arg1, i8 addrspace(1)* null, i64 %arg2) [ "gc-live"(i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1) ] +; CHECK: call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, %record addrspace(1)* (%record addrspace(1)*, i8 addrspace(1)*, i64)* @g0, i32 3, i32 0, %record addrspace(1)* %arg1.reloc.casted, i8 addrspace(1)* null, i64 %arg3) [ "gc-live"(i8 addrspace(1)* %arg0.reloc, %record addrspace(1)* %arg1.reloc.casted), "struct-live"(%record addrspace(1)** %temp) ] +; CHECK: call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%record addrspace(1)*, i8 addrspace(1)*, i64)* @g1, i32 3, i32 0, %record addrspace(1)* %1, i8 addrspace(1)* null, i64 %arg2) [ "gc-live"(%record addrspace(1)* %1), "struct-live"(%record addrspace(1)** %temp) ] +; CHECK: call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%record addrspace(1)*, i8 addrspace(1)*, i64)* @g1, i32 3, i32 0, %record addrspace(1)* %4, i8 addrspace(1)* null, i64 %arg2) [ "gc-live"(%record addrspace(1)* %4) ] +; +entry: + %temp = alloca %record addrspace(1)* + %0 = call %record addrspace(1)* @g0(%record addrspace(1)* %arg1, i8 addrspace(1)* null, i64 %arg2) + store %record addrspace(1)* %0, %record addrspace(1)** %temp + %1 = call %record addrspace(1)* @g0(%record addrspace(1)* %arg1, i8 addrspace(1)* null, i64 %arg3) + call void @g1(%record addrspace(1)* %1, i8 addrspace(1)* null, i64 %arg2) + %2 = load %record addrspace(1)*, %record addrspace(1)** %temp + call void @g1(%record addrspace(1)* %2, i8 addrspace(1)* null, i64 %arg2) + ret void +} + +attributes #0 = { "record_mut" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record2.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record2.ll new file mode 100644 index 000000000..b3872c07e --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record2.ll @@ -0,0 +1,67 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record = type { i64, i8 addrspace(1)* } +%object = type { %record, i32, i64 } +@data = global i16 0 + +declare void @g0(i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1) #0 gc "cangjie" +declare void @g1(i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1) #0 gc "cangjie" +declare i1 @g2(i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1) #0 gc "cangjie" +declare void @g3(i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1, i1 %arg2) #0 gc "cangjie" + +define i64 @foo(i8 addrspace(1)* %a, %record addrspace(1)* %arg) #0 gc "cangjie" { +entry: + %0 = alloca %record + %1 = alloca %record addrspace(1)* + br label %body + +; CHECK: body: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record addrspace(1)*)* @g0, i32 2, i32 0, i8 addrspace(1)* %a, %record addrspace(1)* %arg) [ "gc-live"(%record addrspace(1)* %arg, i8 addrspace(1)* %a) ] +; +body: ; preds = %entry + call void @g0(i8 addrspace(1)* %a, %record addrspace(1)* %arg) + store %record addrspace(1)* %arg, %record addrspace(1)** %1 + %2 = load %record addrspace(1)*, %record addrspace(1)** %1 + %3 = bitcast %record addrspace(1)* %2 to %object addrspace(1)* + %4 = getelementptr inbounds %object, %object addrspace(1)* %3, i32 0, i32 1 + %5 = load i32, i32 addrspace(1)* %4 + %icmpeq = icmp eq i32 %5, 1 + br i1 %icmpeq, label %tmp1, label %tmp2 + +tmp2: ; preds = %body + br label %tmpend + +tmp1: ; preds = %body + %6 = load i16, i16* @data + %icmpeq1 = icmp eq i16 %6, 3 + br label %tmpend + +tmpend: ; preds = %tmp1, %tmp2 + %conv = phi i1 [ false, %tmp2 ], [ %icmpeq1, %tmp1 ] + br i1 %conv, label %ifthen, label %ifelse + +; CHECK: ifthen: +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record addrspace(1)*)* @g1, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %9) [ "struct-live"(%record* %0) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i1 (i8 addrspace(1)*, %record addrspace(1)*)* @g2, i32 2, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %9) [ "struct-live"(%record* %0) ] +; CHECK: [[TOKEN:%.*]] = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*, %record addrspace(1)*, i1)* @g3, i32 3, i32 0, i8 addrspace(1)* null, %record addrspace(1)* %11, i1 %10) [ "struct-live"(%record* %0) ] +; +ifthen: ; preds = %tmpend + %7 = add i32 %5, 1 + %8 = load %record, %record addrspace(1)* %2 + store %record %8, %record* %0 + %9 = addrspacecast %record* %0 to %record addrspace(1)* + call void @g1(i8 addrspace(1)* null, %record addrspace(1)* %9) + %10 = call i1 @g2(i8 addrspace(1)* null, %record addrspace(1)* %9) + %11 = addrspacecast %record* %0 to %record addrspace(1)* + call void @g3(i8 addrspace(1)* null, %record addrspace(1)* %11, i1 %10) + br label %ifend + +ifelse: ; preds = %tmpend + br label %ifend + +ifend: ; preds = %ifelse, %ifthen + ret i64 0 +} + +attributes #0 = { "hasRcdParam" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record3.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record3.ll new file mode 100644 index 000000000..9d82721dd --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record3.ll @@ -0,0 +1,65 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record = type { i1, i8 addrspace(1)* } +%object = type { %record, i8 addrspace(1)*, i64, i64 } + +declare i8 addrspace(1)* @g0(i8 addrspace(1)* %arg0, i64 %arg1) #0 gc "cangjie" +declare void @g1(i8* %arg0, i8* %arg1, i64 %arg2, i1 %arg3) #0 gc "cangjie" +declare void @g2(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0, i64 %add, i64 addrspace(1)* %arg2) #0 gc "cangjie" +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) + +define void @foo(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0) #0 gc "cangjie" { +entry: + %0 = alloca %record + %1 = alloca %record + %cast0 = bitcast %record* %0 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %cast0, i8 0, i64 16, i1 false) + %cast1 = bitcast %record* %1 to i8* + call void @llvm.memset.p0i8.i64(i8* nonnull align 8 %cast1, i8 0, i64 16, i1 false) + br label %body + +body: ; preds = %entry + %2 = bitcast %record addrspace(1)* %arg1 to %object addrspace(1)* + %3 = getelementptr inbounds %object, %object addrspace(1)* %2, i32 0, i32 3 + %4 = load i64, i64 addrspace(1)* %3 + %5 = bitcast %record addrspace(1)* %arg1 to %object addrspace(1)* + %6 = getelementptr inbounds %object, %object addrspace(1)* %5, i32 0, i32 2 + %7 = load i64, i64 addrspace(1)* %6 + %icmpslt = icmp slt i64 %4, %7 + br i1 %icmpslt, label %ifthen, label %ifelse + +; CHECK: ifthen: +; CHECK: call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, i8 addrspace(1)* (i8 addrspace(1)*, i64)* @g0, i32 2, i32 0, i8 addrspace(1)* %10, i64 %13) [ "gc-live"(i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1, i8 addrspace(1)* %10), "struct-live"(%record* %0) ] +; CHECK: call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8*, i8*, i64, i1)* @g1, i32 4, i32 0, i8* %18, i8* %19, i64 16, i1 false) [ "gc-live"(i8 addrspace(1)* %arg0.reloc, %record addrspace(1)* %arg1.reloc.casted), "struct-live"(%record* %0, %record* %1) ] +; CHECK: call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%record addrspace(1)*, i8 addrspace(1)*, i64, i64 addrspace(1)*)* @g2, i32 4, i32 0, %record addrspace(1)* %arg1.reloc4.casted, i8 addrspace(1)* %arg0.reloc3, i64 %add, i64 addrspace(1)* %24) [ "gc-live"(i8 addrspace(1)* %arg0.reloc3, %record addrspace(1)* %arg1.reloc4.casted, i64 addrspace(1)* %24) ] +; +ifthen: ; preds = %body + %8 = bitcast %record addrspace(1)* %arg1 to %object addrspace(1)* + %9 = getelementptr inbounds %object, %object addrspace(1)* %8, i32 0, i32 1 + %10 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %9 + %11 = bitcast %record addrspace(1)* %arg1 to %object addrspace(1)* + %12 = getelementptr inbounds %object, %object addrspace(1)* %11, i32 0, i32 3 + %13 = load i64, i64 addrspace(1)* %12 + %14 = call i8 addrspace(1)* @g0(i8 addrspace(1)* %10, i64 %13) + %15 = getelementptr inbounds %record, %record* %1, i32 0, i32 0 + store i1 false, i1* %15 + %16 = getelementptr inbounds %record, %record* %1, i32 0, i32 1 + store i8 addrspace(1)* %14, i8 addrspace(1)** %16 + %17 = bitcast %record* %0 to i8* + %18 = bitcast %record* %1 to i8* + call void @g1(i8* %17, i8* %18, i64 16, i1 false) + %19 = bitcast %record addrspace(1)* %arg1 to %object addrspace(1)* + %20 = getelementptr inbounds %object, %object addrspace(1)* %19, i32 0, i32 3 + %21 = load i64, i64 addrspace(1)* %20 + %add = add i64 %21, 1 + %22 = bitcast %record addrspace(1)* %arg1 to %object addrspace(1)* + %23 = getelementptr inbounds %object, %object addrspace(1)* %22, i32 0, i32 3 + call void @g2(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0, i64 %add, i64 addrspace(1)* %23) + ret void + +ifelse: ; preds = %body + ret void +} + +attributes #0 = { "record_mut" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record4.ll b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record4.ll new file mode 100644 index 000000000..0bc42c7c1 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/statepoint-record4.ll @@ -0,0 +1,51 @@ +; RUN: opt -cj-rewrite-statepoint -S < %s | FileCheck %s +; RUN: opt -passes=cj-rewrite-statepoint -S < %s | FileCheck %s + +%record = type { i64, i8 addrspace(1)* } +%object = type { %record, i64 } +%array = type { %object, [0 x i8 addrspace(1)*] } + +declare void @g0(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0) #0 gc "cangjie" +declare void @g1(i8 addrspace(1)* %arg) #0 gc "cangjie" + +define i8 addrspace(1)* @foo(%record addrspace(1)* %arg1, i8 addrspace(1)* %arg0, i64 %arg2, i8 addrspace(1)* %arg3) #0 gc "cangjie" { +; CHECK-LABEL: @foo( +; CHECK-NEXT: entry: +; CHECK-NEXT: icmp slt i64 %arg2, 0 +; CHECK-NEXT: bitcast %record addrspace(1)* [[ARG1:%.*]] to %object addrspace(1)* +; CHECK-NEXT: getelementptr inbounds %object, %object addrspace(1)* %1, i32 0, i32 1 +; CHECK-NEXT: load i64, i64 addrspace(1)* %2 +; CHECK-NEXT: icmp sge i64 %arg2, %3 +; CHECK-NEXT: or i1 %0, %4 +; CHECK-NEXT: br i1 %5, label %ifelse, label %ifend +; +entry: + %0 = icmp slt i64 %arg2, 0 + %1 = bitcast %record addrspace(1)* %arg1 to %object addrspace(1)* + %2 = getelementptr inbounds %object, %object addrspace(1)* %1, i32 0, i32 1 + %3 = load i64, i64 addrspace(1)* %2 + %4 = icmp sge i64 %arg2, %3 + %5 = or i1 %0, %4 + br i1 %5, label %ifelse, label %ifend + +; CHECK: ifelse: +; CHECK: call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%record addrspace(1)*, i8 addrspace(1)*)* @g0, i32 2, i32 0, %record addrspace(1)* %arg1, i8 addrspace(1)* null) [ "gc-live"(i8 addrspace(1)* %arg3, i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1) ] +; CHECK: call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (i8 addrspace(1)*)* @g1, i32 1, i32 0, i8 addrspace(1)* %arg3.reloc) [ "gc-live"(i8 addrspace(1)* %arg3.reloc) ] +; +ifelse: ; preds = %body + call void @g0(%record addrspace(1)* %arg1, i8 addrspace(1)* null) + call void @g1(i8 addrspace(1)* %arg3) + ret i8 addrspace(1)* %arg3 + +; CHECK: ifend: +; CHECK: call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%record addrspace(1)*, i8 addrspace(1)*)* @g0, i32 2, i32 0, %record addrspace(1)* %arg1, i8 addrspace(1)* %8) [ "gc-live"(i8 addrspace(1)* %8, i8 addrspace(1)* %arg0, %record addrspace(1)* %arg1) ] +; +ifend: ; preds = %body + %6 = bitcast %record addrspace(1)* %arg1 to %array addrspace(1)* + %7 = getelementptr inbounds %array, %array addrspace(1)* %6, i32 0, i32 1, i64 %arg2 + %8 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %7 + call void @g0(%record addrspace(1)* %arg1, i8 addrspace(1)* %8) + ret i8 addrspace(1)* %8 +} + +attributes #0 = { "record_mut" } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/strip-invalid-attributes.ll b/llvm/test/Transforms/CJRewriteStatepoint/strip-invalid-attributes.ll new file mode 100644 index 000000000..3f683e701 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/strip-invalid-attributes.ll @@ -0,0 +1,92 @@ +; RUN: opt -S -cj-rewrite-statepoint < %s | FileCheck %s +; RUN: opt -S -passes=cj-rewrite-statepoint < %s | FileCheck %s + + +; Ensure we're stipping attributes from the function signatures which are invalid +; after inserting safepoints with explicit memory semantics + +declare void @f() + +define i8 addrspace(1)* @deref_arg(i8 addrspace(1)* dereferenceable(16) %arg) gc "cangjie" { +; CHECK: define i8 addrspace(1)* @deref_arg(i8 addrspace(1)* %arg) + call void @f() + ret i8 addrspace(1)* %arg +} + +define dereferenceable(16) i8 addrspace(1)* @deref_ret(i8 addrspace(1)* %arg) gc "cangjie" { +; CHECK: define i8 addrspace(1)* @deref_ret(i8 addrspace(1)* %arg) + call void @f() + ret i8 addrspace(1)* %arg +} + +define i8 addrspace(1)* @deref_or_null_arg(i8 addrspace(1)* dereferenceable_or_null(16) %arg) gc "cangjie" { +; CHECK: define i8 addrspace(1)* @deref_or_null_arg(i8 addrspace(1)* %arg) + call void @f() + ret i8 addrspace(1)* %arg +} + +define dereferenceable_or_null(16) i8 addrspace(1)* @deref_or_null_ret(i8 addrspace(1)* %arg) gc "cangjie" { +; CHECK: define i8 addrspace(1)* @deref_or_null_ret(i8 addrspace(1)* %arg) + call void @f() + ret i8 addrspace(1)* %arg +} + +define i8 addrspace(1)* @noalias_arg(i8 addrspace(1)* noalias %arg) gc "cangjie" { +; CHECK: define i8 addrspace(1)* @noalias_arg(i8 addrspace(1)* %arg) + call void @f() + ret i8 addrspace(1)* %arg +} + +define noalias i8 addrspace(1)* @noalias_ret(i8 addrspace(1)* %arg) gc "cangjie" { +; CHECK: define i8 addrspace(1)* @noalias_ret(i8 addrspace(1)* %arg) + call void @f() + ret i8 addrspace(1)* %arg +} + +define i8 addrspace(1)* @nofree(i8 addrspace(1)* nofree %arg) nofree gc "cangjie" { +; CHECK: define i8 addrspace(1)* @nofree(i8 addrspace(1)* %arg) gc "cangjie" { + call void @f() + ret i8 addrspace(1)* %arg +} + +define i8 addrspace(1)* @nosync(i8 addrspace(1)* %arg) nosync gc "cangjie" { +; CHECK: define i8 addrspace(1)* @nosync(i8 addrspace(1)* %arg) gc "cangjie" { + call void @f() + ret i8 addrspace(1)* %arg +} + +define i8 addrspace(1)* @readnone(i8 addrspace(1)* readnone %arg) readnone gc "cangjie" { +; CHECK: define i8 addrspace(1)* @readnone(i8 addrspace(1)* %arg) gc "cangjie" { + call void @f() + ret i8 addrspace(1)* %arg +} + +define i8 addrspace(1)* @readonly(i8 addrspace(1)* readonly %arg) readonly gc "cangjie" { +; CHECK: define i8 addrspace(1)* @readonly(i8 addrspace(1)* %arg) gc "cangjie" { + call void @f() + ret i8 addrspace(1)* %arg +} + +define i8 addrspace(1)* @writeonly(i8 addrspace(1)* writeonly %arg) writeonly gc "cangjie" { +; CHECK: define i8 addrspace(1)* @writeonly(i8 addrspace(1)* %arg) gc "cangjie" { + call void @f() + ret i8 addrspace(1)* %arg +} + +define i8 addrspace(1)* @argmemonly(i8 addrspace(1)* %arg) argmemonly gc "cangjie" { +; CHECK: define i8 addrspace(1)* @argmemonly(i8 addrspace(1)* %arg) gc "cangjie" { + call void @f() + ret i8 addrspace(1)* %arg +} + +define i8 addrspace(1)* @inaccessiblememonly(i8 addrspace(1)* %arg) inaccessiblememonly gc "cangjie" { +; CHECK: define i8 addrspace(1)* @inaccessiblememonly(i8 addrspace(1)* %arg) gc "cangjie" { + call void @f() + ret i8 addrspace(1)* %arg +} + +define i8 addrspace(1)* @inaccessiblemem_or_argmemonly(i8 addrspace(1)* %arg) inaccessiblemem_or_argmemonly gc "cangjie" { +; CHECK: define i8 addrspace(1)* @inaccessiblemem_or_argmemonly(i8 addrspace(1)* %arg) gc "cangjie" { + call void @f() + ret i8 addrspace(1)* %arg +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/two-invokes-one-landingpad.ll b/llvm/test/Transforms/CJRewriteStatepoint/two-invokes-one-landingpad.ll new file mode 100644 index 000000000..c2a05e693 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/two-invokes-one-landingpad.ll @@ -0,0 +1,34 @@ +; RUN: opt < %s -cj-rewrite-statepoint -S | FileCheck %s +; RUN: opt < %s -passes=cj-rewrite-statepoint -S | FileCheck %s + +declare void @some_call(i64 addrspace(1)*) + +declare i32 @dummy_personality_function() + +define i64 addrspace(1)* @test(i64 addrspace(1)* %obj, i64 addrspace(1)* %obj1) + gc "cangjie" + personality i32 ()* @dummy_personality_function { +entry: + invoke void @some_call(i64 addrspace(1)* %obj) [ "deopt"() ] + to label %second_invoke unwind label %exceptional_return + +second_invoke: ; preds = %entry + invoke void @some_call(i64 addrspace(1)* %obj) [ "deopt"() ] + to label %normal_return unwind label %exceptional_return + +normal_return: ; preds = %second_invoke + ret i64 addrspace(1)* %obj + +; CHECK: exceptional_return1: +; CHECK-NEXT: %lpad2 = landingpad token + +; CHECK: exceptional_return.split-lp: +; CHECK-NEXT: %lpad.split-lp = landingpad token + +; CHECK: exceptional_return: +; CHECK-NOT: phi token + +exceptional_return: ; preds = %second_invoke, %entry + %lpad = landingpad token cleanup + ret i64 addrspace(1)* %obj1 +} diff --git a/llvm/test/Transforms/CJRewriteStatepoint/unreachable-regression.ll b/llvm/test/Transforms/CJRewriteStatepoint/unreachable-regression.ll new file mode 100644 index 000000000..db57e0b47 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/unreachable-regression.ll @@ -0,0 +1,34 @@ +; RUN: opt -S -cj-rewrite-statepoint < %s | FileCheck %s +; RUN: opt -S -passes=cj-rewrite-statepoint < %s | FileCheck %s +; +; Regression test: +; After the rewritable callsite collection if any callsite was found +; in a block that was reported unreachable by DominanceTree then +; removeUnreachableBlocks() was called. But it is stronger than +; DominatorTree::isReachableFromEntry(), i.e. removeUnreachableBlocks +; can remove some blocks for which isReachableFromEntry() returns true. +; This resulted in stale pointers to the collected but removed +; callsites. Such stale pointers caused crash when accessed. +declare void @f(i8 addrspace(1)* %obj) + +define void @test(i8 addrspace(1)* %arg) gc "cangjie" { +; CHECK-LABEL: test( +; CHECK-NEXT: @f + call void @f(i8 addrspace(1)* %arg) #1 + br i1 true, label %not_zero, label %zero + +not_zero: + ret void + +; This block is reachable but removed by removeUnreachableBlocks() +zero: +; CHECK-NOT: @f + call void @f(i8 addrspace(1)* %arg) #1 + ret void + +unreach: + call void @f(i8 addrspace(1)* %arg) #1 + ret void +} + +attributes #1 = { norecurse noimplicitfloat } diff --git a/llvm/test/Transforms/CJRewriteStatepoint/vector-bitcast.ll b/llvm/test/Transforms/CJRewriteStatepoint/vector-bitcast.ll new file mode 100644 index 000000000..000bc3ded --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/vector-bitcast.ll @@ -0,0 +1,29 @@ +; RUN: opt -S -cj-rewrite-statepoint < %s | FileCheck %s +; RUN: opt -S -passes=cj-rewrite-statepoint < %s | FileCheck %s +; +; A test to make sure that we can look through bitcasts of +; vector types when a base pointer is contained in a vector. + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128-ni:1" +target triple = "x86_64-unknown-linux-gnu" + +declare i8 addrspace(1)* @foo() + +; Function Attrs: uwtable +define i32 @test() gc "cangjie" { +; CHECK-LABEL: @test +entry: +; CHECK-LABEL: entry +; CHECK: %bc = bitcast +; CHECK: %[[p1:[A-Za-z0-9_]+]] = extractelement +; CHECK: %[[p2:[A-Za-z0-9_]+]] = extractelement +; CHECK: llvm.cj.gc.statepoint +; CHECK: %[[p2]].reloc = {{.+}} @llvm.cj.gc.relocate +; CHECK: %[[p1]].reloc = {{.+}} @llvm.cj.gc.relocate +; CHECK: load atomic + %bc = bitcast <8 x i8 addrspace(1)*> undef to <8 x i32 addrspace(1)*> + %ptr= extractelement <8 x i32 addrspace(1)*> %bc, i32 7 + %0 = call i8 addrspace(1)* @foo() [ "deopt"() ] + %1 = load atomic i32, i32 addrspace(1)* %ptr unordered, align 4 + ret i32 %1 +} diff --git a/llvm/test/Transforms/CJRuntimeLowering/alloca_generic.ll b/llvm/test/Transforms/CJRuntimeLowering/alloca_generic.ll new file mode 100644 index 000000000..5041c0e10 --- /dev/null +++ b/llvm/test/Transforms/CJRuntimeLowering/alloca_generic.ll @@ -0,0 +1,73 @@ +; RUN: opt -passes=cj-runtime-lowering -S < %s | FileCheck %s + + +%Record = type { i8 addrspace(1)*, i64 } +%TypeInfo = type { i8*, i8, i8, i16, i32, i8*, i32, i8, i8, i32*, i8*, i8*, i8*, i8*, i8*, i8* } +@Int64.ti = external global %TypeInfo, !RelatedType !0 #0 + +; Function Attrs: nounwind +declare i8 addrspace(1)* @llvm.cj.alloca.generic(i8*, i32) + +declare i8 addrspace(1)* @moo(i8 addrspace(1)*, %TypeInfo*) gc "cangjie" +declare void @koo(i8 addrspace(1)* noalias sret(i8), %TypeInfo*) gc "cangjie" + +; CHECK: bb0: +; CHECK: %0 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Int64.ti to i8*), i32 16) + +define i8 addrspace(1)* @foo(i64 %x) gc "cangjie" { +bb0: + %0 = call i8 addrspace(1)* @llvm.cj.alloca.generic(i8* bitcast (%TypeInfo* @Int64.ti to i8*), i32 8) + %1 = bitcast i8 addrspace(1)* %0 to i8* addrspace(1)* + %ti.payload = getelementptr i8*, i8* addrspace(1)* %1, i32 1 + %2 = bitcast i8* addrspace(1)* %ti.payload to i64 addrspace(1)* + store i64 %x, i64 addrspace(1)* %2, align 8 + %3 = call i8 addrspace(1)* @moo(i8 addrspace(1)* %0, %TypeInfo* @Int64.ti) + ret i8 addrspace(1)* %3 +} + +; CHECK: bb0: +; CHECK: %0 = alloca { %TypeInfo*, i64 }, align 8 + +define i64 @foo2(i64 %x) gc "cangjie" { +bb0: + %0 = call i8 addrspace(1)* @llvm.cj.alloca.generic(i8* bitcast (%TypeInfo* @Int64.ti to i8*), i32 8) + %1 = bitcast i8 addrspace(1)* %0 to i8* addrspace(1)* + %ti.payload = getelementptr i8*, i8* addrspace(1)* %1, i32 1 + %2 = bitcast i8* addrspace(1)* %ti.payload to i64 addrspace(1)* + store i64 %x, i64 addrspace(1)* %2, align 8 + call void @koo(i8 addrspace(1)* noalias sret(i8) %0, %TypeInfo* @Int64.ti) + ret i64 %x +} + +; CHECK: bb0: +; CHECK: %0 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Int64.ti to i8*), i32 16) + +define i8 addrspace(1)* @foo3(i64 %x) gc "cangjie" { +bb0: + %0 = call i8 addrspace(1)* @llvm.cj.alloca.generic(i8* bitcast (%TypeInfo* @Int64.ti to i8*), i32 8) + %1 = bitcast i8 addrspace(1)* %0 to i8* addrspace(1)* + %ti.payload = getelementptr i8*, i8* addrspace(1)* %1, i32 1 + %2 = bitcast i8* addrspace(1)* %ti.payload to i64 addrspace(1)* + store i64 %x, i64 addrspace(1)* %2, align 8 + call void @koo(i8 addrspace(1)* noalias sret(i8) %0, %TypeInfo* @Int64.ti) + ret i8 addrspace(1)* %0 +} + +; CHECK: bb0: +; CHECK: %0 = call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @Int64.ti to i8*), i32 16) + +define void @foo4(%Record* noalias sret(%Record) %r, i64 %x) gc "cangjie" { +bb0: + %0 = call i8 addrspace(1)* @llvm.cj.alloca.generic(i8* bitcast (%TypeInfo* @Int64.ti to i8*), i32 8) + %1 = bitcast i8 addrspace(1)* %0 to i8* addrspace(1)* + %ti.payload = getelementptr i8*, i8* addrspace(1)* %1, i32 1 + %2 = bitcast i8* addrspace(1)* %ti.payload to i64 addrspace(1)* + store i64 %x, i64 addrspace(1)* %2, align 8 + %3 = bitcast %Record* %r to i8 addrspace(1)** + store i8 addrspace(1)* %0, i8 addrspace(1)** %3, align 8 + ret void +} + +attributes #0 = { "CFileKlass" "can_malloc_with_fixed_size" } + +!0 = !{!"Int64.Type"} diff --git a/llvm/test/Transforms/CJRuntimeLowering/new_finalier.ll b/llvm/test/Transforms/CJRuntimeLowering/new_finalier.ll new file mode 100644 index 000000000..2b5883163 --- /dev/null +++ b/llvm/test/Transforms/CJRuntimeLowering/new_finalier.ll @@ -0,0 +1,17 @@ +; RUN: opt < %s -passes=cj-runtime-lowering -S | FileCheck %s +%TypeInfo = type { i8*, i8, i8, i16, i32, i8*, i32, i8, i8, i32*, i8*, i8*, i8*, i8*, i8*, i8* } + +declare i8 addrspace(1)* @llvm.cj.malloc.object(i8*, i32) + +; NewFinalier cannot add "cj-malloc" attribute, because it may call a finalier method when destructor performing. + +define void @lower_new_finalier(i8* %ti, i8** %s) { +; CHECK-LABEL: @lower_new_finalier( +; CHECK: declare i8 addrspace(1)* @CJ_MCC_NewFinalizer(i8*, i32) #1 +; CHECK: attributes #1 = { "cj-runtime" } +; + %obj = call noalias i8 addrspace(1)* @llvm.cj.malloc.object(i8* %ti, i32 32), !MallocType !0 + ret void +} + +!0 = !{!"HasFinalizer"} diff --git a/llvm/test/Transforms/CJRuntimeLowering/runtime_func_attribute.ll b/llvm/test/Transforms/CJRuntimeLowering/runtime_func_attribute.ll new file mode 100644 index 000000000..4329da98f --- /dev/null +++ b/llvm/test/Transforms/CJRuntimeLowering/runtime_func_attribute.ll @@ -0,0 +1,36 @@ +; RUN: opt < %s -passes=cj-runtime-lowering -S | FileCheck %s +%TypeInfo = type { i8*, i8, i8, i16, i32, i8*, i32, i8, i8, i32*, i8*, i8*, i8*, i8*, i8*, i8* } + +declare i8 addrspace(1)* @llvm.cj.malloc.object(i8*, i32) + +; Function Attrs: nounwind +declare i8* @llvm.cj.get.exception.wrapper() #0 + +; Function Attrs: nounwind +declare i8 addrspace(1)* @llvm.cj.post.throw.exception(i8*) #0 + +declare void @llvm.cj.throw.exception(i8 addrspace(1)*) + +; Transfer the original function's attributes. + +; CHECK: %3 = load i32, i32* %2, align 4 +; CHECK: %4 = add i32 %3, 15 +; CHECK %5 = and i32 %4, -8 +; CHECK: declare noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) #2 +; CHECK: declare i8* @CJ_MCC_GetExceptionWrapper() #3 +; CHECK: declare i8 addrspace(1)* @CJ_MCC_PostThrowException(i8*) #3 +; CHECK: declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) #1 +; CHECK: attributes #1 = { "cj-runtime" "gc-leaf-function" } +; CHECK: attributes #2 = { argmemonly "cj-heapmalloc" "cj-runtime" } +; CHECK: attributes #3 = { nounwind "cj-runtime" "gc-leaf-function" } +; + +define void @lower_new_finalier(i8* %ti) { + %obj = call noalias i8 addrspace(1)* @llvm.cj.malloc.object(i8* %ti, i32 32) + %e = call i8* @llvm.cj.get.exception.wrapper() + %e_obj = call i8 addrspace(1)* @llvm.cj.post.throw.exception(i8* %e) + call void @llvm.cj.throw.exception(i8 addrspace(1)* %e_obj) + ret void +} + +attributes #0 = { nounwind } diff --git a/llvm/test/Transforms/CJSimpleOpt/AArch64/fptosi-opt.ll b/llvm/test/Transforms/CJSimpleOpt/AArch64/fptosi-opt.ll new file mode 100644 index 000000000..28eb618f9 --- /dev/null +++ b/llvm/test/Transforms/CJSimpleOpt/AArch64/fptosi-opt.ll @@ -0,0 +1,41 @@ +; RUN: opt -passes=cj-simple-opt -S < %s | FileCheck %s +target triple = "aarch64--linux-gnu" + +define void @"foo"(double* %X) gc "cangjie" { +bb1: + ; CHECK: %1 = fptosi double %0 to i64 + ; CHECK-NEXT: %2 = call i64 @llvm.cj.get.fp.state.i64(i64 %1) + ; CHECK-NEXT: %3 = and i64 %2, 1 + ; CHECK-NEXT: %4 = icmp eq i64 %3, 0 + ; CHECK-NEXT: br i1 %4, label %upper.bound.ok, label %fp.convert.exception + + %0 = load double, double* %X, align 8 + %1 = bitcast double %0 to i64 + %2 = and i64 %1, 9218868437227405312 + %notInfOrNan = icmp ne i64 %2, 9218868437227405312 + br i1 %notInfOrNan, label %not.inf.nan, label %inf.or.nan + +not.inf.nan: + %f2i.lt.min = fcmp olt double 0xC3E0000000000001, %0 + br i1 %f2i.lt.min, label %lower.bound.ok, label %lower.bound.overflow + +lower.bound.ok: + %f2i.gt.max = fcmp olt double %0, 0x43E0000000000000 + br i1 %f2i.gt.max, label %upper.bound.ok, label %upper.bound.overflow + +upper.bound.ok: + %3 = fptosi double %0 to i64 + br label %bb2 + +inf.or.nan: + unreachable + +lower.bound.overflow: + unreachable + +upper.bound.overflow: + unreachable + +bb2: + ret void +} diff --git a/llvm/test/Transforms/CJSimpleRangeAnalysis/simple-range-analysis1.ll b/llvm/test/Transforms/CJSimpleRangeAnalysis/simple-range-analysis1.ll new file mode 100644 index 000000000..0a79abdda --- /dev/null +++ b/llvm/test/Transforms/CJSimpleRangeAnalysis/simple-range-analysis1.ll @@ -0,0 +1,102 @@ +; RUN: opt -cj-simple-range-analysis -S < %s | FileCheck %s + +; CHECK: bb2.i: +; CHECK-NEXT: %1 = fadd double %n, -4.000000e+00 +; CHECK-NEXT: %2 = call fastcc i64 @_ZN7default3fibEd(double %1) +; CHECK-NEXT: %3 = fadd double %n, -3.000000e+00 +; CHECK-NEXT: %4 = call fastcc i64 @_ZN7default3fibEd(double %3) +; CHECK-NEXT: %5 = add i64 %2, %4 +; CHECK-NEXT: br label %_ZN7default3fibEd.exit.split + +; CHECK: _ZN7default3fibEd.exit.split: +; CHECK-NEXT: br label %bb2.i4 + +; CHECK: _ZN7default3fibEd.exit: +; CHECK-NEXT: %common.ret.op.i = phi i64 [ 1, %bb2 ] +; CHECK-NEXT: %6 = fadd double %n, -1.000000e+00 +; CHECK-NEXT: %fcmpolt.i2 = fcmp olt double %6, 2.000000e+00 +; CHECK-NEXT: br i1 %fcmpolt.i2, label %_ZN7default3fibEd.exit5, label %bb2.i4.split + +; CHECK: bb2.i4.split: +; CHECK-NEXT: br label %_ZN7default3fibEd.exit.i + +; CHECK: bb2.i4: +; CHECK-NEXT: %common.ret.op.i1 = phi i64 [ %5, %_ZN7default3fibEd.exit.split ] +; CHECK-NEXT: %7 = fadd double %n, -3.000000e+00 +; CHECK-NEXT: %fcmpolt.i.i = fcmp olt double %7, 2.000000e+00 +; CHECK-NEXT: br i1 %fcmpolt.i.i, label %_ZN7default3fibEd.exit.i, label %bb2.i.i + +; CHECK: bb2.i.i: +; CHECK-NEXT: %8 = fadd double %n, -5.000000e+00 +; CHECK-NEXT: %9 = call fastcc i64 @_ZN7default3fibEd(double %8) +; CHECK-NEXT: %10 = fadd double %n, -4.000000e+00 +; CHECK-NEXT: %11 = call fastcc i64 @_ZN7default3fibEd(double %10) +; CHECK-NEXT: %12 = add i64 %9, %11 +; CHECK-NEXT: br label %_ZN7default3fibEd.exit.i + +; CHECK: _ZN7default3fibEd.exit.i: +; CHECK-NEXT: %common.ret.op.i13 = phi i64 [ %common.ret.op.i1, %bb2.i.i ], [ %common.ret.op.i1, %bb2.i4 ], [ %common.ret.op.i, %bb2.i4.split ] +; CHECK-NEXT: %common.ret.op.i.i = phi i64 [ %12, %bb2.i.i ], [ 1, %bb2.i4 ], [ 1, %bb2.i4.split ] +; CHECK-NEXT: %13 = fadd double %n, -2.000000e+00 +; CHECK-NEXT: %14 = call fastcc i64 @_ZN7default3fibEd(double %13) +; CHECK-NEXT: %15 = add i64 %common.ret.op.i.i, %14 +; CHECK-NEXT: br label %_ZN7default3fibEd.exit5 + +; CHECK: _ZN7default3fibEd.exit5: +; CHECK-NEXT: %common.ret.op.i2 = phi i64 [ %common.ret.op.i13, %_ZN7default3fibEd.exit.i ], [ %common.ret.op.i, %_ZN7default3fibEd.exit ] +; CHECK-NEXT: %common.ret.op.i3 = phi i64 [ %15, %_ZN7default3fibEd.exit.i ], [ 1, %_ZN7default3fibEd.exit ] +; CHECK-NEXT: %16 = add i64 %common.ret.op.i2, %common.ret.op.i3 +; CHECK-NEXT: br label %common.ret +define internal fastcc i64 @_ZN7default3fibEd(double %n) { +bb0: + %fcmpolt = fcmp olt double %n, 2.000000e+00 + br i1 %fcmpolt, label %common.ret, label %bb2 + +common.ret: ; preds = %bb0, %_ZN7default3fibEd.exit5 + %common.ret.op = phi i64 [ %16, %_ZN7default3fibEd.exit5 ], [ 1, %bb0 ] + ret i64 %common.ret.op + +bb2: ; preds = %bb0 + %0 = fadd double %n, -2.000000e+00 + %fcmpolt.i = fcmp olt double %0, 2.000000e+00 + br i1 %fcmpolt.i, label %_ZN7default3fibEd.exit, label %bb2.i + +bb2.i: ; preds = %bb2 + %1 = fadd double %0, -2.000000e+00 + %2 = call fastcc i64 @_ZN7default3fibEd(double %1) + %3 = fadd double %0, -1.000000e+00 + %4 = call fastcc i64 @_ZN7default3fibEd(double %3) + %5 = add i64 %2, %4 + br label %_ZN7default3fibEd.exit + +_ZN7default3fibEd.exit: ; preds = %bb2, %bb2.i + %common.ret.op.i = phi i64 [ %5, %bb2.i ], [ 1, %bb2 ] + %6 = fadd double %n, -1.000000e+00 + %fcmpolt.i2 = fcmp olt double %6, 2.000000e+00 + br i1 %fcmpolt.i2, label %_ZN7default3fibEd.exit5, label %bb2.i4 + +bb2.i4: ; preds = %_ZN7default3fibEd.exit + %7 = fadd double %6, -2.000000e+00 + %fcmpolt.i.i = fcmp olt double %7, 2.000000e+00 + br i1 %fcmpolt.i.i, label %_ZN7default3fibEd.exit.i, label %bb2.i.i + +bb2.i.i: ; preds = %bb2.i4 + %8 = fadd double %7, -2.000000e+00 + %9 = call fastcc i64 @_ZN7default3fibEd(double %8) + %10 = fadd double %7, -1.000000e+00 + %11 = call fastcc i64 @_ZN7default3fibEd(double %10) + %12 = add i64 %9, %11 + br label %_ZN7default3fibEd.exit.i + +_ZN7default3fibEd.exit.i: ; preds = %bb2.i.i, %bb2.i4 + %common.ret.op.i.i = phi i64 [ %12, %bb2.i.i ], [ 1, %bb2.i4 ] + %13 = fadd double %6, -1.000000e+00 + %14 = call fastcc i64 @_ZN7default3fibEd(double %13) + %15 = add i64 %common.ret.op.i.i, %14 + br label %_ZN7default3fibEd.exit5 + +_ZN7default3fibEd.exit5: ; preds = %_ZN7default3fibEd.exit, %_ZN7default3fibEd.exit.i + %common.ret.op.i3 = phi i64 [ %15, %_ZN7default3fibEd.exit.i ], [ 1, %_ZN7default3fibEd.exit ] + %16 = add i64 %common.ret.op.i, %common.ret.op.i3 + br label %common.ret +} diff --git a/llvm/test/Transforms/CJSimpleRangeAnalysis/simple-range-analysis2.ll b/llvm/test/Transforms/CJSimpleRangeAnalysis/simple-range-analysis2.ll new file mode 100644 index 000000000..5864a81e2 --- /dev/null +++ b/llvm/test/Transforms/CJSimpleRangeAnalysis/simple-range-analysis2.ll @@ -0,0 +1,62 @@ +; RUN: opt -cj-simple-range-analysis -instcombine -simplifycfg -gvn -S < %s | FileCheck %s + +; CHECK: bb2.i.i: +; CHECK-NEXT: %7 = fadd double %n, -5.000000e+00 +; CHECK-NEXT: %8 = call fastcc i64 @_ZN7default3fibEd(double %7) +; CHECK-NEXT: %9 = add i64 %8, %2 +; CHECK-NEXT: br label %_ZN7default3fibEd.exit.i +define internal fastcc i64 @_ZN7default3fibEd(double %n) #0 { +bb0: + %fcmpolt = fcmp olt double %n, 2.000000e+00 + br i1 %fcmpolt, label %common.ret, label %bb2 + +common.ret: ; preds = %bb0, %_ZN7default3fibEd.exit5 + %common.ret.op = phi i64 [ %16, %_ZN7default3fibEd.exit5 ], [ 1, %bb0 ] + ret i64 %common.ret.op + +bb2: ; preds = %bb0 + %0 = fadd double %n, -2.000000e+00 + %fcmpolt.i = fcmp olt double %0, 2.000000e+00 + br i1 %fcmpolt.i, label %_ZN7default3fibEd.exit, label %bb2.i + +bb2.i: ; preds = %bb2 + %1 = fadd double %0, -2.000000e+00 + %2 = call fastcc i64 @_ZN7default3fibEd(double %1) + %3 = fadd double %0, -1.000000e+00 + %4 = call fastcc i64 @_ZN7default3fibEd(double %3) + %5 = add i64 %2, %4 + br label %_ZN7default3fibEd.exit + +_ZN7default3fibEd.exit: ; preds = %bb2, %bb2.i + %common.ret.op.i = phi i64 [ %5, %bb2.i ], [ 1, %bb2 ] + %6 = fadd double %n, -1.000000e+00 + %fcmpolt.i2 = fcmp olt double %6, 2.000000e+00 + br i1 %fcmpolt.i2, label %_ZN7default3fibEd.exit5, label %bb2.i4 + +bb2.i4: ; preds = %_ZN7default3fibEd.exit + %7 = fadd double %6, -2.000000e+00 + %fcmpolt.i.i = fcmp olt double %7, 2.000000e+00 + br i1 %fcmpolt.i.i, label %_ZN7default3fibEd.exit.i, label %bb2.i.i + +bb2.i.i: ; preds = %bb2.i4 + %8 = fadd double %7, -2.000000e+00 + %9 = call fastcc i64 @_ZN7default3fibEd(double %8) + %10 = fadd double %7, -1.000000e+00 + %11 = call fastcc i64 @_ZN7default3fibEd(double %10) + %12 = add i64 %9, %11 + br label %_ZN7default3fibEd.exit.i + +_ZN7default3fibEd.exit.i: ; preds = %bb2.i.i, %bb2.i4 + %common.ret.op.i.i = phi i64 [ %12, %bb2.i.i ], [ 1, %bb2.i4 ] + %13 = fadd double %6, -1.000000e+00 + %14 = call fastcc i64 @_ZN7default3fibEd(double %13) + %15 = add i64 %common.ret.op.i.i, %14 + br label %_ZN7default3fibEd.exit5 + +_ZN7default3fibEd.exit5: ; preds = %_ZN7default3fibEd.exit, %_ZN7default3fibEd.exit.i + %common.ret.op.i3 = phi i64 [ %15, %_ZN7default3fibEd.exit.i ], [ 1, %_ZN7default3fibEd.exit ] + %16 = add i64 %common.ret.op.i, %common.ret.op.i3 + br label %common.ret +} + +attributes #0 = { nofree nosync nounwind readnone willreturn} \ No newline at end of file diff --git a/llvm/test/Transforms/Cangjie/DeadArgElim/dead-arg-elim-1.ll b/llvm/test/Transforms/Cangjie/DeadArgElim/dead-arg-elim-1.ll new file mode 100644 index 000000000..3d297ab2a --- /dev/null +++ b/llvm/test/Transforms/Cangjie/DeadArgElim/dead-arg-elim-1.ll @@ -0,0 +1,31 @@ +; RUN: opt < %s -passes=deadargelim --cangjie-pipeline -S | FileCheck %s + +%record = type { i64, i8 addrspace(1)* } + +; CHECK: define internal void @test(%record* %arg1, i8 addrspace(1)* %p) +define internal void @test(i8* %arg0, %record* %arg1, i8 addrspace(1)* %p) { + %ptr = getelementptr inbounds %record, %record* %arg1, i32 0, i32 1 + store i8 addrspace(1)* %p, i8 addrspace(1)** %ptr + ret void +} + +; CHECK: define internal void @test1(%record addrspace(1)* %arg0, i8 addrspace(1)* %arg1, i8 addrspace(1)* %p) +define internal void @test1(%record addrspace(1)* %arg0, i8 addrspace(1)* %arg1, i8 addrspace(1)* %p) { + %ptr = getelementptr inbounds %record, %record addrspace(1)* %arg0, i32 0, i32 1 + store i8 addrspace(1)* %p, i8 addrspace(1)* addrspace(1)* %ptr + ret void +} + +; CHECK: define internal void @test2(%record addrspace(1)* %arg0, i8 addrspace(1)* %arg1, i8 addrspace(1)* %p) +define internal void @test2(%record addrspace(1)* %arg0, i8 addrspace(1)* %arg1, %record* %arg2, i8 addrspace(1)* %p) { + %ptr = getelementptr inbounds %record, %record addrspace(1)* %arg0, i32 0, i32 1 + store i8 addrspace(1)* %p, i8 addrspace(1)* addrspace(1)* %ptr + ret void +} + +; CHECK: define internal void @test3(i8 addrspace(1)* %p) +define internal void @test3(%record addrspace(1)* %arg0, i8 addrspace(1)* %arg1, i8 addrspace(1)* %p) { + %ptr = alloca i8 addrspace(1)* + store i8 addrspace(1)* %p, i8 addrspace(1)** %ptr + ret void +} diff --git a/llvm/test/Transforms/Cangjie/GVN/cj-finalizer.ll b/llvm/test/Transforms/Cangjie/GVN/cj-finalizer.ll new file mode 100644 index 000000000..90c6e3b85 --- /dev/null +++ b/llvm/test/Transforms/Cangjie/GVN/cj-finalizer.ll @@ -0,0 +1,20 @@ +; RUN: opt -gvn --cangjie-pipeline -S < %s | FileCheck %s + +%KlassInfo.0 = type { [0 x %KlassInfo.0*] } +@_ZN7default1AE.objKlass = weak_odr global %KlassInfo.0 { [0 x %KlassInfo.0*] zeroinitializer } +%Unit.Type = type { i8 } + +declare i8 addrspace(1)* @CJ_MCC_NewFinalizerStub(i8*, i32) +declare %Unit.Type @CJ_MCC_InvokeGC(i1) + +define i64 @foo() { +entry: + %0 = call noalias i8 addrspace(1)* @CJ_MCC_NewFinalizerStub(i8* bitcast (%KlassInfo.0* @_ZN7default1AE.objKlass to i8*), i32 16) + %1 = getelementptr inbounds i8, i8 addrspace(1)* %0 + %2 = bitcast i8 addrspace(1)* %1 to i64 addrspace(1)* + store i64 0, i64 addrspace(1)* %2, align 8 + %3 = tail call %Unit.Type @CJ_MCC_InvokeGC(i1 false) + %4 = load i64, i64 addrspace(1)* %2, align 8 + ; CHECK-NOT: ret i64 0 + ret i64 %4 +} \ No newline at end of file diff --git a/llvm/test/Transforms/Cangjie/InstCombine/memcpy-to-load.ll b/llvm/test/Transforms/Cangjie/InstCombine/memcpy-to-load.ll new file mode 100644 index 000000000..9f1c41780 --- /dev/null +++ b/llvm/test/Transforms/Cangjie/InstCombine/memcpy-to-load.ll @@ -0,0 +1,20 @@ +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +%record = type { i1 } + +declare void @llvm.memcpy.p0i8.p0i8.i32(i8* nocapture, i8* nocapture, i32, i1) nounwind + +; memcpy can be expanded inline with load/store (based on the datalayout?) + +define void @copy_1_byte(%record* %d, i8** %s) { +; CHECK-LABEL: @copy_1_byte( +; CHECK: [[TMP1:%.*]] = load i8, i8* [[S:%.*]], align 1 +; CHECK: store i8 [[TMP1]], i8* [[D:%.*]], align 1 +; + %s.i = bitcast i8** %s to i8*** + %s.ii = load i8**, i8*** %s.i + %s.iii = bitcast i8** %s.ii to i8* + %d.i = bitcast %record* %d to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* %d.i, i8* %s.iii, i32 1, i1 false) + ret void +} \ No newline at end of file diff --git a/llvm/test/Transforms/Cangjie/InvariantLoad/dse_invariant_load.ll b/llvm/test/Transforms/Cangjie/InvariantLoad/dse_invariant_load.ll new file mode 100644 index 000000000..8964738e5 --- /dev/null +++ b/llvm/test/Transforms/Cangjie/InvariantLoad/dse_invariant_load.ll @@ -0,0 +1,36 @@ +; RUN: opt < %s -dse --cangjie-pipeline -S | FileCheck %s + +; CHECK: store i32 %init, i32* %var.ptr, align 4 +define i32 @test_invariant_load_dse_1(i32 noundef %init) local_unnamed_addr { + %var.ptr = alloca i32, align 4 + store i32 %init, i32* %var.ptr, align 4 + %1 = load i32, i32* %var.ptr, align 4, !invariant.load !0 + ret i32 %1 +} + +declare i8 addrspace(1)* @fake_get_obj() +declare i8* @fake_get_typeinfo(i8**) + +%fake_typeinfo = type { i8*, i8, i8, i16 } +@"fake_ti" = global %fake_typeinfo { i8* null, i8 0, i8 0, i16 0 } + +; CHECK: store %fake_typeinfo* @fake_ti, %fake_typeinfo** %1, align 8 +define i8* @test_invariant_load_dse_2() local_unnamed_addr { + %1 = alloca %fake_typeinfo*, align 8 + %2 = alloca [1 x %fake_typeinfo*], align 8 + store %fake_typeinfo* @"fake_ti", %fake_typeinfo** %1, align 8 + %3 = call i8 addrspace(1)* @fake_get_obj() + %4 = icmp eq i8 addrspace(1)* %3, null + %5 = bitcast %fake_typeinfo** %1 to i8* + %6 = addrspacecast i8* %5 to i8 addrspace(1)* + %7 = select i1 %4, i8 addrspace(1)* %6, i8 addrspace(1)* %3 + %8 = bitcast i8 addrspace(1)* %7 to %fake_typeinfo* addrspace(1)* + %9 = load %fake_typeinfo*, %fake_typeinfo* addrspace(1)* %8, align 8, !invariant.load !0 + %10 = getelementptr inbounds [1 x %fake_typeinfo*], [1 x %fake_typeinfo*]* %2, i64 0, i64 0 + store %fake_typeinfo* %9, %fake_typeinfo** %10, align 8 + %11 = bitcast [1 x %fake_typeinfo*]* %2 to i8** + %12 = call i8* @fake_get_typeinfo(i8** %11) + ret i8* %12 +} + +!0 = !{} \ No newline at end of file diff --git a/llvm/test/Transforms/Cangjie/LICM/licm_option_type_1.ll b/llvm/test/Transforms/Cangjie/LICM/licm_option_type_1.ll new file mode 100644 index 000000000..a118e308d --- /dev/null +++ b/llvm/test/Transforms/Cangjie/LICM/licm_option_type_1.ll @@ -0,0 +1,160 @@ +; RUN: opt < %s -insert-cj-tbaa -licm --cangjie-pipeline -S | FileCheck %s + +%record.OptionArray = type { i1, %record.ArrayIbE } +%record.ArrayIbE = type { i8 addrspace(1)*, i64, i64 } +%ArrayLayout.Int64 = type { %ArrayBase, [0 x i64] } +%ArrayBase = type { i8 addrspace(1)*, i64 } + +@test_type_0 = global %record.OptionArray zeroinitializer + +%test_type_1 = type { i8, %record.OptionArray } +@test_global_val = global %test_type_1 zeroinitializer + +declare i8 addrspace(1)* @llvm.cj.gcread.static.ref(i8 addrspace(1)** nocapture) #1 + +; CHECK: bb0: +; CHECK: %0 = load i8, i8* bitcast (%record.OptionArray* @test_type_0 to i8*), align 8 +; CHECK: %3 = load i64, i64* getelementptr inbounds (%record.OptionArray, %record.OptionArray* @test_type_0, i64 0, i32 1, i32 2), align 8, !tbaa !0 +; CHECK: %5 = tail call i8 addrspace(1)* @llvm.cj.gcread.static.ref(i8 addrspace(1)** getelementptr inbounds (%record.OptionArray, %record.OptionArray* @test_type_0, i64 0, i32 1, i32 0)), !tbaa !8 +; CHECK: %7 = load i64, i64* getelementptr inbounds (%record.OptionArray, %record.OptionArray* @test_type_0, i64 0, i32 1, i32 1), align 8, !tbaa !9 +; CHECK: br label %bb1 + +; CHECK: bb4: +; CHECK-NEXT: %12 = load i64, i64 addrspace(1)* %9, align 1, !tbaa !10 +; CHECK-NEXT: %13 = add nuw nsw i64 %.ret, %12 +; CHECK-NEXT: %14 = icmp eq i64 %11, 10000 +; CHECK-NEXT: br i1 %14, label %bb6, label %bb1 +define i64 @foo1() { +bb0: + br label %bb1 + +bb1: + %0 = phi i64 [0, %bb0], [%1, %bb4] + %.ret = phi i64 [0, %bb0], [%13, %bb4] + %1 = add nuw nsw i64 %0, 1 + %2 = load i8, i8* bitcast (%record.OptionArray* @test_type_0 to i8*), align 8 + %3 = and i8 %2, 1 + %4 = icmp eq i8 %3, 0 + br i1 %4, label %bb2, label %bb3 + +bb3: + unreachable + +bb2: + %5 = load i64, i64* getelementptr inbounds (%record.OptionArray, %record.OptionArray* @test_type_0, i64 0, i32 1, i32 2), align 8 + %6 = icmp eq i64 %5, 8 + br i1 %6, label %bb4, label %bb5 + +bb5: + unreachable + +bb4: + %7 = tail call i8 addrspace(1)* @llvm.cj.gcread.static.ref(i8 addrspace(1)** getelementptr inbounds (%record.OptionArray, %record.OptionArray* @test_type_0, i64 0, i32 1, i32 0)) + %8 = bitcast i8 addrspace(1)* %7 to %ArrayLayout.Int64 addrspace(1)* + %9 = load i64, i64* getelementptr inbounds (%record.OptionArray, %record.OptionArray* @test_type_0, i64 0, i32 1, i32 1), align 8 + %10 = add i64 %9, 7 + %11 = getelementptr inbounds %ArrayLayout.Int64, %ArrayLayout.Int64 addrspace(1)* %8, i64 0, i32 1, i64 %10 + %12 = load i64, i64 addrspace(1)* %11, align 1 + %13 = add nuw nsw i64 %.ret, %12 + %14 = icmp eq i64 %1, 10000 + br i1 %14, label %bb6, label %bb1 + +bb6: + ret i64 %.ret +} + +; CHECK: bb4: +; CHECK-NEXT: %12 = load i64, i64 addrspace(1)* %9, align 1, !tbaa !10 +; CHECK-NEXT: %13 = add nuw nsw i64 %.ret, %12 +; CHECK-NEXT: %14 = icmp eq i64 %11, 10000 +; CHECK-NEXT: br i1 %14, label %bb6, label %bb1 +define i64 @foo2() { +bb0: + %.gep0 = getelementptr inbounds %test_type_1, %test_type_1* @test_global_val, i64 0, i32 1 + br label %bb1 + +bb1: + %0 = phi i64 [0, %bb0], [%1, %bb4] + %.ret = phi i64 [0, %bb0], [%13, %bb4] + %1 = add nuw nsw i64 %0, 1 + %.cast0 = bitcast %record.OptionArray* %.gep0 to i8* + %2 = load i8, i8* %.cast0, align 8 + %3 = and i8 %2, 1 + %4 = icmp eq i8 %3, 0 + br i1 %4, label %bb2, label %bb3 + +bb3: + unreachable + +bb2: + %.gep1 = getelementptr inbounds %record.OptionArray, %record.OptionArray* %.gep0, i64 0, i32 1, i32 2 + %5 = load i64, i64* %.gep1, align 8 + %6 = icmp eq i64 %5, 8 + br i1 %6, label %bb4, label %bb5 + +bb5: + unreachable + +bb4: + %7 = tail call i8 addrspace(1)* @llvm.cj.gcread.static.ref(i8 addrspace(1)** getelementptr inbounds (%test_type_1, %test_type_1* @test_global_val, i64 0, i32 1, i32 1, i32 0)) + %8 = bitcast i8 addrspace(1)* %7 to %ArrayLayout.Int64 addrspace(1)* + %.gep2 = getelementptr inbounds %record.OptionArray, %record.OptionArray* %.gep0, i64 0, i32 1, i32 1 + %9 = load i64, i64* %.gep2, align 8 + %10 = add i64 %9, 7 + %11 = getelementptr inbounds %ArrayLayout.Int64, %ArrayLayout.Int64 addrspace(1)* %8, i64 0, i32 1, i64 %10 + %12 = load i64, i64 addrspace(1)* %11, align 1 + %13 = add nuw nsw i64 %.ret, %12 + %14 = icmp eq i64 %1, 10000 + br i1 %14, label %bb6, label %bb1 + +bb6: + ret i64 %.ret +} + +; CHECK: bb4: +; CHECK-NEXT: %12 = load i64, i64 addrspace(1)* %9, align 1, !tbaa !10 +; CHECK-NEXT: %13 = add nuw nsw i64 %.ret, %12 +; CHECK-NEXT: %14 = icmp eq i64 %11, 10000 +; CHECK-NEXT: br i1 %14, label %bb6, label %bb1 +define i64 @foo3() { +bb0: + %.gep0 = getelementptr inbounds %record.OptionArray, %record.OptionArray* @test_type_0, i64 0, i32 1 + br label %bb1 + +bb1: + %0 = phi i64 [0, %bb0], [%1, %bb4] + %.ret = phi i64 [0, %bb0], [%13, %bb4] + %1 = add nuw nsw i64 %0, 1 + %2 = load i8, i8* bitcast (%record.OptionArray* @test_type_0 to i8*), align 8 + %3 = and i8 %2, 1 + %4 = icmp eq i8 %3, 0 + br i1 %4, label %bb2, label %bb3 + +bb3: + unreachable + +bb2: + %5 = load i64, i64* getelementptr inbounds (%record.OptionArray, %record.OptionArray* @test_type_0, i64 0, i32 1, i32 2), align 8 + %6 = icmp eq i64 %5, 8 + br i1 %6, label %bb4, label %bb5 + +bb5: + unreachable + +bb4: + %.gep1 = getelementptr inbounds %record.ArrayIbE, %record.ArrayIbE* %.gep0, i64 0, i32 0 + %7 = tail call i8 addrspace(1)* @llvm.cj.gcread.static.ref(i8 addrspace(1)** %.gep1) + %8 = bitcast i8 addrspace(1)* %7 to %ArrayLayout.Int64 addrspace(1)* + %9 = load i64, i64* getelementptr inbounds (%record.OptionArray, %record.OptionArray* @test_type_0, i64 0, i32 1, i32 1), align 8 + %10 = add i64 %9, 7 + %11 = getelementptr inbounds %ArrayLayout.Int64, %ArrayLayout.Int64 addrspace(1)* %8, i64 0, i32 1, i64 %10 + %12 = load i64, i64 addrspace(1)* %11, align 1 + %13 = add nuw nsw i64 %.ret, %12 + %14 = icmp eq i64 %1, 10000 + br i1 %14, label %bb6, label %bb1 + +bb6: + ret i64 %.ret +} + +attributes #1 = { argmemonly nounwind readonly } \ No newline at end of file diff --git a/llvm/test/Transforms/Cangjie/SROA/sroa-phi-load.ll b/llvm/test/Transforms/Cangjie/SROA/sroa-phi-load.ll new file mode 100644 index 000000000..cbb7c9849 --- /dev/null +++ b/llvm/test/Transforms/Cangjie/SROA/sroa-phi-load.ll @@ -0,0 +1,64 @@ +; RUN: opt < %s -cangjie-pipeline -sroa -S | FileCheck %s +; RUN: opt < %s -cangjie-pipeline -passes=sroa -S | FileCheck %s + +%record = type { i8 addrspace(1)*, i64, i64 } + +declare void @llvm.cj.memset.p0i8(i8*, i8, i64, i1) +declare void @getObjClass(i8 addrspace(1)*) + +; CHECK: bb0 +; CHECK-NOT: store +; CHECK-NOT: load +; CHECK: bb1 +; CHECK-NOT: store +; CHECK-NOT: load +; CHECK: bb2 +; CHECK-NOT: store +; CHECK-NOT: load +; CHECK: bbend +; CHECK: %phi.r.sroa.phi.sroa.speculated = phi i8 addrspace(1)* [ %obj1, %bb0 ], [ %obj2, %bb1 ], [ %obj3, %bb2 ] +; CHECK-NOT: load + + +define void @foo(i64 %c, i8 addrspace(1)* %obj1, i8 addrspace(1)* %obj2, i8 addrspace(1)* %obj3, i32 %0) gc "cangjie" { +bb0: + %r1 = alloca %record, align 8 + %r2 = alloca %record, align 8 + %r3 = alloca %record, align 8 + %1 = bitcast %record* %r1 to i8* + %2 = bitcast %record* %r2 to i8* + %3 = bitcast %record* %r3 to i8* + %4 = getelementptr inbounds %record, %record* %r1, i64 0, i32 0 + store i8 addrspace(1)* %obj1, i8 addrspace(1)** %4, align 8 + %5 = getelementptr inbounds %record, %record* %r1, i64 0, i32 1 + %6 = bitcast i64* %5 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %6, i8 0, i64 16, i1 false) + %icmp1 = icmp ugt i64 %c, 4 + br i1 %icmp1 , label %bbend, label %bb1 + +bb1: + %7 = getelementptr inbounds %record, %record* %r2, i64 0, i32 0 + store i8 addrspace(1)* %obj2, i8 addrspace(1)** %7, align 8 + %8 = getelementptr inbounds %record, %record* %r2, i64 0, i32 1 + %9 = bitcast i64* %8 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %9, i8 0, i64 16, i1 false) + %icmp2 = icmp ugt i64 %c, 2 + br i1 %icmp2 , label %bbend, label %bb2 + +bb2: + %10 = getelementptr inbounds %record, %record* %r3, i64 0, i32 0 + store i8 addrspace(1)* %obj3, i8 addrspace(1)** %10, align 8 + %11 = getelementptr inbounds %record, %record* %r3, i64 0, i32 1 + %12 = bitcast i64* %11 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %12, i8 0, i64 16, i1 false) + br label %bbend + +bbend: + %phi.r = phi %record* [ %r1, %bb0 ], [ %r2, %bb1 ], [ %r3, %bb2 ] + %13 = getelementptr %record, %record* %phi.r, i64 0, i32 0 + %14 = addrspacecast i8 addrspace(1)** %13 to i8 addrspace(1)* addrspace(1)* + %15 = addrspacecast i8 addrspace(1)* addrspace(1)* %14 to i8 addrspace(1)** + %16 = load i8 addrspace(1)*, i8 addrspace(1)** %15, align 8 + call void @getObjClass(i8 addrspace(1)* %16) + ret void +} \ No newline at end of file diff --git a/llvm/test/Transforms/Cangjie/TBAA/alloca-bitcast-load.ll b/llvm/test/Transforms/Cangjie/TBAA/alloca-bitcast-load.ll new file mode 100644 index 000000000..269aeaad1 --- /dev/null +++ b/llvm/test/Transforms/Cangjie/TBAA/alloca-bitcast-load.ll @@ -0,0 +1,18 @@ +; RUN: opt < %s -insert-cj-tbaa -S | FileCheck %s + +; CHECK: %3 = load i8, i8* %2, align 1 +; CHECK: %4 = load i8, i8* %1, align 1, !tbaa !0 +define void @foo() { +bb0: + %0 = alloca [1 x i1], align 1 + %1 = alloca i8, align 1 + %2 = bitcast [1 x i1]* %0 to i8* + %3 = load i8, i8* %2, align 1 + %4 = load i8, i8* %1, align 1 + ret void +} + +; CHECK: !0 = !{!1, !1, i64 0} +; CHECK-NEXT: !1 = !{!"i8", !2} +; CHECK-NEXT: !2 = !{!"omnipotent char", !3} +; CHECK-NEXT: !3 = !{!"Simple Cangjie TBAA"} \ No newline at end of file diff --git a/llvm/test/Transforms/Cangjie/TBAA/anonymous-struct.ll b/llvm/test/Transforms/Cangjie/TBAA/anonymous-struct.ll new file mode 100644 index 000000000..9e9516499 --- /dev/null +++ b/llvm/test/Transforms/Cangjie/TBAA/anonymous-struct.ll @@ -0,0 +1,17 @@ +; RUN: opt < %s -insert-cj-tbaa --cangjie-pipeline -S | FileCheck %s + +%TempType = type { i64, i8 addrspace(1)* } + +define void @foo(i8 addrspace(1)* %0) gc "cangjie" { +entry: + %1 = bitcast i8 addrspace(1)* %0 to i8* addrspace(1)* + %2 = getelementptr i8*, i8* addrspace(1)* %1, i32 1 + %3 = bitcast i8* addrspace(1)* %2 to %TempType addrspace(1)* + %4 = getelementptr inbounds %TempType, %TempType addrspace(1)* %3, i32 0, i32 0 + store i64 0, i64 addrspace(1)* %4, align 8 + %5 = bitcast i8* addrspace(1)* %2 to { i64, i8 addrspace(1)* } addrspace(1)* + %6 = getelementptr inbounds { i64, i8 addrspace(1)* }, { i64, i8 addrspace(1)* } addrspace(1)* %5, i32 0, i32 0 + ; CHECK-NOT: %7 = load i64, i64 addrspace(1)* %6, align 4, !tbaa + %7 = load i64, i64 addrspace(1)* %6 + ret void +} \ No newline at end of file diff --git a/llvm/test/Transforms/Cangjie/TBAA/barriersplit-update-tbaa-with-sibling.ll b/llvm/test/Transforms/Cangjie/TBAA/barriersplit-update-tbaa-with-sibling.ll new file mode 100644 index 000000000..d88fd47b2 --- /dev/null +++ b/llvm/test/Transforms/Cangjie/TBAA/barriersplit-update-tbaa-with-sibling.ll @@ -0,0 +1,64 @@ +; RUN: opt < %s -cj-barrier-split --cangjie-pipeline -S | FileCheck %s + +%T = type {%S, i64} +%S = type { i8 addrspace(1)*, i64, i64 } + +; CHECK: 9: +; CHECK-NEXT: %10 = getelementptr inbounds i8, i8 addrspace(1)* %3, i64 0 +; CHECK-NEXT: %11 = bitcast %S* %1 to i8* +; CHECK-NEXT: %12 = bitcast i8 addrspace(1)* %10 to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: %13 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %3, i8 addrspace(1)* addrspace(1)* %12), !tbaa !7 +; CHECK-NEXT: %14 = bitcast i8* %11 to i8 addrspace(1)** +; CHECK-NEXT: store i8 addrspace(1)* %13, i8 addrspace(1)** %14, align 8, !tbaa !8 +; CHECK-NEXT: %15 = getelementptr inbounds i8, i8* %11, i32 8 +; CHECK-NEXT: %16 = getelementptr inbounds i8, i8 addrspace(1)* %10, i32 8 +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %15, i8 addrspace(1)* align 8 %16, i64 16, i1 false), !tbaa.struct !9 +; CHECK-NEXT: ret void +define void @foo(i8 addrspace(1)* %.0) gc "cangjie" { + %1 = alloca %S + %2 = bitcast i8 addrspace(1)* %.0 to i8 addrspace(1)* addrspace(1)* + ; %.0: %T addrspace(1)** + %3 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %.0, i8 addrspace(1)* addrspace(1)* %2) + %4 = getelementptr inbounds i8, i8 addrspace(1)* %3, i64 24 + %5 = bitcast i8 addrspace(1)* %4 to i64 addrspace(1)* + %6 = load i64, i64 addrspace(1)* %5, !tbaa !6 + %7 = icmp slt i64 %6, 1 + br i1 %7, label %8, label %9 + +8: + unreachable + +9: + %10 = getelementptr inbounds i8, i8 addrspace(1)* %3, i64 0 + %11 = bitcast %S* %1 to i8* + call void @llvm.cj.gcread.struct(i8* nonnull %11, i8 addrspace(1)* %3, i8 addrspace(1)* %10, i64 24) + ret void +} + +declare void @llvm.memcpy.p0i8.p1i8.i64(i8* noalias nocapture writeonly %0, i8 addrspace(1)* noalias nocapture readonly %1, i64 %2, i1 immarg %3) #0 +declare i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* nocapture %0, i8 addrspace(1)* addrspace(1)* nocapture %1) #1 +declare void @llvm.cj.gcread.struct(i8* writeonly %0, i8 addrspace(1)* noalias nocapture %1, i8 addrspace(1)* noalias nocapture readonly %2, i64 %3) #2 + +attributes #0 = { argmemonly nocallback nofree nounwind willreturn } +attributes #1 = { argmemonly nofree nounwind readonly } +attributes #2 = { argmemonly nounwind } + +; CHECK: !0 = !{!1, !6, i64 24} +; CHECK-NEXT: !1 = !{!"T", !2, i64 0, !6, i64 24} +; CHECK-NEXT: !2 = !{!"S", !3, i64 0, !6, i64 8, !6, i64 16} +; CHECK-NEXT: !3 = !{!"pointer", !4} +; CHECK-NEXT: !4 = !{!"omnipotent char", !5} +; CHECK-NEXT: !5 = !{!"Simple Cangjie TBAA"} +; CHECK-NEXT: !6 = !{!"i64", !4} +; CHECK-NEXT: !7 = !{!1, !3, i64 0} +; CHECK-NEXT: !8 = !{!2, !3, i64 0} +; CHECK-NEXT: !9 = !{i64 0, i64 8, !10, i64 8, i64 8, !11, i64 16, i64 8, !11} +; CHECK-NEXT: !10 = !{!3, !3, i64 0} +; CHECK-NEXT: !11 = !{!6, !6, i64 0} +!0 = !{!"pointer", !1} +!1 = !{!"omnipotent char", !2} +!2 = !{!"Simple Cangjie TBAA"} +!3 = !{!"i64", !1} +!4 = !{!"S", !0, i64 0, !3, i64 8, !3, i64 16} +!5 = !{!"T", !4, i64 0, !3, i64 24} +!6 = !{!5, !3, i64 24} \ No newline at end of file diff --git a/llvm/test/Transforms/Cangjie/TBAA/getelementptr_i8.ll b/llvm/test/Transforms/Cangjie/TBAA/getelementptr_i8.ll new file mode 100644 index 000000000..5b9b5c4e4 --- /dev/null +++ b/llvm/test/Transforms/Cangjie/TBAA/getelementptr_i8.ll @@ -0,0 +1,21 @@ +; RUN: opt < %s -insert-cj-tbaa -S | FileCheck %s + +%type = type {i8 addrspace(1)*, i64, i64} + +; CHECK: store i64 1, i64* %3, align 8, !tbaa !0 +define void @foo() { +bb0: + %0 = alloca %type, align 8 + %1 = bitcast %type* %0 to i8* + %2 = getelementptr inbounds i8, i8* %1, i32 16 + %3 = bitcast i8* %2 to i64* + store i64 1, i64* %3, align 8 + ret void +} + +; CHECK: !0 = !{!1, !5, i64 16} +; CHECK-NEXT: !1 = !{!"type", !2, i64 0, !5, i64 8, !5, i64 16} +; CHECK-NEXT: !2 = !{!"pointer", !3} +; CHECK-NEXT: !3 = !{!"omnipotent char", !4} +; CHECK-NEXT: !4 = !{!"Simple Cangjie TBAA"} +; CHECK-NEXT: !5 = !{!"i64", !3} \ No newline at end of file diff --git a/llvm/test/Transforms/Cangjie/TBAA/sroa-update-tbaa-with-sibling.ll b/llvm/test/Transforms/Cangjie/TBAA/sroa-update-tbaa-with-sibling.ll new file mode 100644 index 000000000..a4a15526e --- /dev/null +++ b/llvm/test/Transforms/Cangjie/TBAA/sroa-update-tbaa-with-sibling.ll @@ -0,0 +1,61 @@ +; RUN: opt < %s -sroa --cangjie-pipeline -S | FileCheck %s + +%T = type {i8 addrspace(1)*, i64} +%S = type { i8 addrspace(1)*, i64, i64 } +%Array.intArray = type {[0 x i64]} + +; CHECK: %6 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %3, i8 addrspace(1)* addrspace(1)* %5), !tbaa !0 +; CHECK: %.sroa.2.8.copyload = load i64, i64 addrspace(1)* %.sroa.2.8..sroa_cast, align 8, !tbaa !6 +; CHECK: %.sroa.4.8.copyload = load i64, i64 addrspace(1)* %.sroa.4.8..sroa_cast, align 8, !tbaa !7 +define i64 @foo(i8 addrspace(1)* %.0) { + %1 = alloca %S + %2 = bitcast i8 addrspace(1)* %.0 to %T addrspace(1)* + %3 = getelementptr inbounds %T, %T addrspace(1)* %2, i32 0, i32 0 + %4 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %.0, i8 addrspace(1)* addrspace(1)* %3) + %5 = getelementptr inbounds i8, i8 addrspace(1)* %4, i64 0 + %6 = bitcast i8 addrspace(1)* %5 to i8 addrspace(1)* addrspace(1)* + %7 = call i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* %4, i8 addrspace(1)* addrspace(1)* %6), !tbaa !6 + %8 = getelementptr inbounds %S, %S* %1, i32 0, i32 0 + store i8 addrspace(1)* %7, i8 addrspace(1)** %8 + %9 = getelementptr inbounds %S, %S* %1, i32 0, i32 1 + %10 = bitcast i64* %9 to i8* + %11 = getelementptr inbounds i8, i8 addrspace(1)* %4, i64 8 + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %10, i8 addrspace(1)* noundef align 8 dereferenceable(16) %11, i64 16, i1 false) + br label %12 + +12: + %13 = getelementptr inbounds %S, %S* %1, i32 0, i32 0 + %14 = load i8 addrspace(1)*, i8 addrspace(1)** %13 + %15 = bitcast i8 addrspace(1)* %14 to %Array.intArray addrspace(1)* + %16 = getelementptr inbounds %S, %S* %1, i32 0, i32 1 + %17 = load i64, i64* %16 + %18 = getelementptr inbounds %S, %S* %1, i32 0, i32 2 + %19 = load i64, i64* %18 + %20 = add nuw nsw i64 %17, %19 + %21 = getelementptr inbounds %Array.intArray, %Array.intArray addrspace(1)* %15, i32 0, i32 0, i64 %20 + %22 = load i64, i64 addrspace(1)* %21 + ret i64 %22 +} + +declare void @llvm.memcpy.p0i8.p1i8.i64(i8* noalias nocapture writeonly %0, i8 addrspace(1)* noalias nocapture readonly %1, i64 %2, i1 immarg %3) #0 +declare i8 addrspace(1)* @llvm.cj.gcread.ref(i8 addrspace(1)* nocapture %0, i8 addrspace(1)* addrspace(1)* nocapture %1) #1 + +attributes #0 = { argmemonly nocallback nofree nounwind willreturn } +attributes #1 = { argmemonly nofree nounwind readonly } + +; CHECK: !0 = !{!1, !2, i64 0} +; CHECK-NEXT: !1 = !{!"S", !2, i64 0, !5, i64 8, !5, i64 16} +; CHECK-NEXT: !2 = !{!"pointer", !3} +; CHECK-NEXT: !3 = !{!"omnipotent char", !4} +; CHECK-NEXT: !4 = !{!"Simple Cangjie TBAA"} +; CHECK-NEXT: !5 = !{!"i64", !3} +; CHECK-NEXT: !6 = !{!1, !5, i64 8} +; CHECK-NEXT: !7 = !{!1, !5, i64 16} + +!0 = !{!"pointer", !1} +!1 = !{!"omnipotent char", !2} +!2 = !{!"Simple Cangjie TBAA"} +!3 = !{!"i64", !1} +!4 = !{!"S", !0, i64 0, !3, i64 8, !3, i64 16} +!5 = !{!"T", !4, i64 0, !3, i64 24} +!6 = !{!4, !0, i64 0} \ No newline at end of file diff --git a/llvm/test/Transforms/Cangjie/VFE/cangjie-vfe-1.ll b/llvm/test/Transforms/Cangjie/VFE/cangjie-vfe-1.ll new file mode 100644 index 000000000..35d630569 --- /dev/null +++ b/llvm/test/Transforms/Cangjie/VFE/cangjie-vfe-1.ll @@ -0,0 +1,176 @@ +; RUN: opt < %s -passes='globaldce' --cangjie-pipeline -S | FileCheck %s + +; interface I1 { +; func func_I11() {} +; func func_I12() {} +; func func_I13() {} +; } +; interface I2 { +; func func_I21() {} +; func func_I22() {} +; func func_I23() {} +; } +; class A <: I1 & I2> { +; public func func_I1() {} +; public func func_I22() { func_A() } +; public func func_A() { println("A::func_A") } +; } +; class B <: I2 {} +; class C <: I2 {} +; public func call_I2_1(v: I2>) { v.func_I22() } + +%TypeTemplate = type { i8*, i8, i8, i16, i16, i8*, i8*, i8*, i8*, %ExtensionDef**, i16 } +%ExtensionDef = type { i32, i8, i8*, i8*, i8*, i8* } +%TypeInfo = type { i8*, i8, i8, i16, i32, i8*, i32, i8, i8, i16, i32*, i8*, i8*, i8*, %TypeInfo*, %ExtensionDef**, i8*, i8* } +%Unit.Type = type {} + +@"default:A.tt" = private global %TypeTemplate { i8* null, i8 -127, i8 0, i16 0, i16 1, i8* null, i8* null, i8* null, i8* null, %ExtensionDef** getelementptr inbounds ([6 x %ExtensionDef*], [6 x %ExtensionDef*]* @NonExternalExtensionDefs, i32 0, i32 0), i16 -32768 } +@"default:B.tt" = private global %TypeTemplate { i8* null, i8 -127, i8 0, i16 0, i16 1, i8* null, i8* null, i8* null, i8* null, %ExtensionDef** getelementptr inbounds ([6 x %ExtensionDef*], [6 x %ExtensionDef*]* @NonExternalExtensionDefs, i32 0, i32 2), i16 -32768 } +@"default:I1.tt" = private global %TypeTemplate { i8* null, i8 -127, i8 0, i16 0, i16 1, i8* null, i8* null, i8* null, i8* null, %ExtensionDef** null, i16 -32768 } +@"default:I2.ti" = internal global %TypeInfo { i8* null, i8 -127, i8 0, i16 0, i32 0, i8* null, i32 0, i8 1, i8 0, i16 -32768, i32* null, i8* null, i8* null, i8* null, %TypeInfo *null, %ExtensionDef** null, i8* null, i8* null } + +@"default:A_ed_default:I1.ft" = private global [1 x i8*] [i8* bitcast (void (%Unit.Type*, i8 addrspace(1)*, %TypeInfo*)* @_CN7default1AIG_E7func_I1Hv to i8*)] +@"default:A_ed_default:I1" = private global %ExtensionDef { i32 1, i8 0, i8* bitcast (%TypeTemplate* @"default:A.tt" to i8*), i8* bitcast (i8* (i32, %TypeInfo**)* @"default:A_ed_default:I1_iFn" to i8*), i8* null, i8* bitcast ([1 x i8*]* @"default:A_ed_default:I1.ft" to i8*) }, !inheritedType !0 #0 +; CHECK: @"default:A_ed_default:I2.ft = private global [1 x i8*] zeroinitializer" +@"default:A_ed_default:I2.ft" = private global [1 x i8*] [i8* bitcast (void (%Unit.Type*, i8 addrspace(1)*, %TypeInfo*)* @_CN7default1AIG_E7func_I2Hv to i8*)] +@"default:A_ed_default:I2" = private global %ExtensionDef { i32 1, i8 1, i8* bitcast (%TypeTemplate* @"default:A.tt" to i8*), i8* bitcast (%TypeInfo* @"default:I2.ti" to i8*), i8* null, i8* bitcast ([1 x i8*]* @"default:A_ed_default:I2.ft" to i8*) }, !inheritedType !1 #0 +; CHECK: @"default:B_ed_default:A.ft" = private global [1 x i8*] zeroinitializer +@"default:B_ed_default:A.ft" = private global [1 x i8*] [i8* bitcast (void (%Unit.Type*, i8 addrspace(1)*, %TypeInfo*)* @_CN7default1BIG_E6func_AHv to i8*)] +@"default:B_ed_default:A" = private global %ExtensionDef { i32 1, i8 0, i8* bitcast (%TypeTemplate* @"default:B.tt" to i8*), i8* bitcast (i8* (i32, %TypeInfo**)* @"default:B_ed_default:A_iFn" to i8*), i8* null, i8* bitcast ([1 x i8*]* @"default:B_ed_default:A.ft" to i8*) }, !inheritedType !2 #0 +@"default:B_ed_default:I1.ft" = private global [1 x i8*] [i8* bitcast (void (%Unit.Type*, i8 addrspace(1)*, %TypeInfo*)* @_CN7default1BIG_E7func_I1Hv to i8*)] +@"default:B_ed_default:I1" = private global %ExtensionDef { i32 1, i8 0, i8* bitcast (%TypeTemplate* @"default:B.tt" to i8*), i8* bitcast (i8* (i32, %TypeInfo**)* @"default:B_ed_default:I1_iFn" to i8*), i8* null, i8* bitcast ([1 x i8*]* @"default:B_ed_default:I1.ft" to i8*) }, !inheritedType !0 #0 +; CHECK: @"default:B_ed_default:I2.ft" = private global [1 x i8*] zeroinitializer +@"default:B_ed_default:I2.ft" = private global [1 x i8*] [i8* bitcast (void (%Unit.Type*, i8 addrspace(1)*, %TypeInfo*)* @"_CVN7default1AIG_E7func_I2Hv$N7default1BIG_E$CN7default2I2E" to i8*)] +@"default:B_ed_default:I2" = private global %ExtensionDef { i32 1, i8 1, i8* bitcast (%TypeTemplate* @"default:B.tt" to i8*), i8* bitcast (%TypeInfo* @"default:I2.ti" to i8*), i8* null, i8* bitcast ([1 x i8*]* @"default:B_ed_default:I2.ft" to i8*) }, !inheritedType !1 #0 + +@NonExternalExtensionDefs = private global [6 x %ExtensionDef*] [%ExtensionDef* @"default:A_ed_default:I1", %ExtensionDef* @"default:A_ed_default:I2", %ExtensionDef* @"default:B_ed_default:A", %ExtensionDef* @"default:B_ed_default:I1", %ExtensionDef* @"default:B_ed_default:I2", %ExtensionDef* null] +@llvm.used = appending global [1 x i8*] [i8* bitcast ([6 x %ExtensionDef*]* @NonExternalExtensionDefs to i8*)] + +define private i8* @"default:A_ed_default:I1_iFn"(i32 %0, %TypeInfo** %1) { +allocas: + %2 = alloca [1 x %TypeInfo*], align 8 + br label %prepTi + +prepTi: + %3 = getelementptr %TypeInfo*, %TypeInfo** %1, i32 0 + %4 = load %TypeInfo*, %TypeInfo** %3, align 8 + %5 = getelementptr inbounds [1 x %TypeInfo*], [1 x %TypeInfo*]* %2, i32 0, i32 0 + store %TypeInfo* %4, %TypeInfo** %5, align 8 + %6 = bitcast [1 x %TypeInfo*]* %2 to i8** + %7 = call i8* @CJ_MCC_GetOrCreateTypeInfo(i8* bitcast (%TypeTemplate* @"default:I1.tt" to i8*), i32 1, i8** %6) + ret i8* %7 +} + +define private i8* @"default:B_ed_default:A_iFn"(i32 %0, %TypeInfo** %1) { +allocas: + %2 = alloca [1 x %TypeInfo*], align 8 + br label %prepTi + +prepTi: + %3 = getelementptr %TypeInfo*, %TypeInfo** %1, i32 0 + %4 = load %TypeInfo*, %TypeInfo** %3, align 8 + %5 = getelementptr inbounds [1 x %TypeInfo*], [1 x %TypeInfo*]* %2, i32 0, i32 0 + store %TypeInfo* %4, %TypeInfo** %5, align 8 + %6 = bitcast [1 x %TypeInfo*]* %2 to i8** + %7 = call i8* @CJ_MCC_GetOrCreateTypeInfo(i8* bitcast (%TypeTemplate* @"default:A.tt" to i8*), i32 1, i8** %6) + ret i8* %7 +} + + +define private i8* @"default:B_ed_default:I1_iFn"(i32 %0, %TypeInfo** %1) { +allocas: + %2 = alloca [1 x %TypeInfo*], align 8 + br label %prepTi + +prepTi: + %3 = getelementptr %TypeInfo*, %TypeInfo** %1, i32 0 + %4 = load %TypeInfo*, %TypeInfo** %3, align 8 + %5 = getelementptr inbounds [1 x %TypeInfo*], [1 x %TypeInfo*]* %2, i32 0, i32 0 + store %TypeInfo* %4, %TypeInfo** %5, align 8 + %6 = bitcast [1 x %TypeInfo*]* %2 to i8** + %7 = call i8* @CJ_MCC_GetOrCreateTypeInfo(i8* bitcast (%TypeTemplate* @"default:I1.tt" to i8*), i32 1, i8** %6) + ret i8* %7 +} + +define internal void @_CN7default1AIG_E7func_I1Hv(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %this, %TypeInfo* %outerTI) gc "cangjie" { + ret void +} + +define internal void @_CN7default1AIG_E7func_I2Hv(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %this, %TypeInfo* %outerTI) gc "cangjie" { +allocas: + %1 = alloca %Unit.Type, align 8 + br label %1 + +prepTi: + %2 = bitcast i8 addrspace(1)* %this to %TypeInfo* addrspace(1)* + %ti = load %TypeInfo*, %TypeInfo* addrspace(1)* %2, align 8 + %3 = bitcast %TypeInfo* %ti to i8* + %4 = bitcast %TypeInfo* %outerTI to i8* + %5 = call i8* @CJ_MCC_GetMTable(i8* %3, i8* %4) + %6 = bitcast i8* %5 to void (%Unit.Type*, i8 addrspace(1)*, %TypeInfo*)** + %7 = load void (%Unit.Type*, i8 addrspace(1)*, %TypeInfo*)*, void (%Unit.Type*, i8 addrspace(1)*, %TypeInfo*)** %6, align 8, !objType !2, !FuncTable !3 + %ti1 = load %TypeInfo*, %TypeInfo* addrspace(1)* %2, align 8 + call void %7(%Unit.Type* noalias sret(%Unit.Type) %1, i8 addrspace(1)* %this, %TypeInfo* ti1) + ret void +} + +define internal void @_CN7default1BIG_E6func_AHv(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %this, %TypeInfo* %outerTI) gc "cangjie" { + ret void +} + +define internal void @_CN7default1BIG_E7func_I1Hv(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %this, %TypeInfo* %outerTI) gc "cangjie" { + ret void +} + +define internal void @"_CVN7default1AIG_E7func_I2Hv$N7default1BIG_E$CN7default2I2E"(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %this, %TypeInfo* %outerTI) gc "cangjie" { +allocas: + %2 = alloca %Unit.Type, align 8 + %3 = alloca [1 x %TypeInfo*], align 8 + br label %prepTi + +prepTi: + %ti.typeArgs = getelementptr inbounds %TypeInfo, %TypeInfo* %outerTI, i32 0, i32 12 + %4 = load i8*, i8** %ti.typeArgs, align 8 + %5 = bitcast i8* %4 to %TypeInfo** + %6 = getelementptr inbounds %TypeInfo*, %TypeInfo** %5, i32 0 + %7 = load %TypeInfo*, %TypeInfo** %6, align 8 + %8 = getelementptr inbounds [1 x %TypeInfo*], [1 x %TypeInfo*]* %3, i32 0, i32 0 + store %TypeInfo* %7, %TypeInfo** %8, align 8 + %9 = bitcast [1 x %TypeInfo*]* %3 to i8** + %10 = call i8* @CJ_MCC_GetOrCreateTypeInfo(i8* bitcast (%TypeTemplate* @"default:A.tt" to i8*), i32 1, i8** %9) + %11 = bitcast i8* %10 to %TypeInfo* + call void @_CN7default1AIG_E7func_I2Hv(%Unit.Type* noalias sret(%Unit.Type) %2, i8 addrspace(1)* %1, %TypeInfo& %11) + ret void +} + +define void @use_I1(%Unit.Type* noalias sret(%Unit.Type) %0, i8 addrspace(1)* %v, %TypeInfo* %ti.T) gc "cangjie" { +allocas: + %1 = alloca %Unit.Type, align 8 + %2 = alloca [1 x %TypeInfo*], align 8 + br label %prepTi + +prepTi: + %3 = getelementptr inbounds [1 x %TypeInfo*], [1 x %TypeInfo*]* %2, i32 0, i32 0 + store %TypeInfo* %ti.T, %TypeInfo** %3, align 8 + %4 = bitcast [1 x %TypeInfo*]* %2 to i8** + %5 = call i8* @CJ_MCC_GetOrCreateTypeInfo(i8* bitcast (%TypeTemplate* @"default:I1.tt" to i8*), i32 1, i8** %4) + %6 = bitcast i8 addrspace(1)* %v to %TypeInfo* addrspace(1)* + %ti = load %TypeInfo*, %TypeInfo* addrspace(1)* %6, align 8 + %7 = bitcast %TypeInfo* %ti to i8* + %8 = call i8* @CJ_MCC_GetMTable(i8* %7, i8* %5) + %9 = bitcast i8* %8 to void (%Unit.Type*, i8 addrspace(1)*, %TypeInfo*)** + %10 = load void (%Unit.Type*, i8 addrspace(1)*, %TypeInfo*)*, void (%Unit.Type*, i8 addrspace(1)*, %TypeInfo*)** %9, align 8, !objType !0, !FuncTable !3 + %ti1 = load %TypeInfo*, %TypeInfo* addrspace(1)* %6, align 8 + call void %10(%Unit.Type* noalias sret(%Unit.Type) %1, i8 addrspace(1)* %v, %TypeInfo* %ti1) + ret void +} + +declare i8* @CJ_MCC_GetOrCreateTypeInfo(i8*, i32, i8**) +declare i8* @CJ_MCC_GetMTable(i8*, i8*) + +attributes #0 = { "CFileMTable" } + +!0 = !{!"default:I1.tt"} +!1 = !{!"default:I2.ti"} +!2 = !{!"default:A.tt"} +!3 = !{i64 0} \ No newline at end of file diff --git a/llvm/test/Transforms/InstCombine/cj-instcombine1.ll b/llvm/test/Transforms/InstCombine/cj-instcombine1.ll new file mode 100644 index 000000000..3d4ae0a3f --- /dev/null +++ b/llvm/test/Transforms/InstCombine/cj-instcombine1.ll @@ -0,0 +1,29 @@ +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +%"T7_N_ZN11std$FS$core6OptionIcEC_ZN7default21Class_1695327792688_9EactdlE" = type { %"record._ZN7default11std$FS$core6OptionIcE", i8 addrspace(1)*, i8, i32, i16, double, i64 } +%"record._ZN7default11std$FS$core6OptionIcE" = type { i1, i32 } + +declare void @llvm.memcpy.p0i8.p1i8.i64(i8*, i8 addrspace(1)*, i64, i1) + +;CHECK-NOT: %1 = bitcast %"record._ZN7default11std$FS$core6OptionIcE" addrspace(1)* %"fld$0" to i8 addrspace(1)* addrspace(1)* + +define weak_odr hidden void @foo(%"T7_N_ZN11std$FS$core6OptionIcEC_ZN7default21Class_1695327792688_9EactdlE"* noalias sret(%"T7_N_ZN11std$FS$core6OptionIcEC_ZN7default21Class_1695327792688_9EactdlE") %0, i8 addrspace(1)* %"fld$0$BP", %"record._ZN7default11std$FS$core6OptionIcE" addrspace(1)* %"fld$0", i8 addrspace(1)* %"fld$1", i8 %"fld$2", i32 %"fld$3", i16 %"fld$4", double %"fld$5", i64 %"fld$6") gc "cangjie" { +entry: + %"fld$01" = getelementptr inbounds %"T7_N_ZN11std$FS$core6OptionIcEC_ZN7default21Class_1695327792688_9EactdlE", %"T7_N_ZN11std$FS$core6OptionIcEC_ZN7default21Class_1695327792688_9EactdlE"* %0, i32 0, i32 0 + %1 = bitcast %"record._ZN7default11std$FS$core6OptionIcE"* %"fld$01" to i8* + %2 = bitcast %"record._ZN7default11std$FS$core6OptionIcE" addrspace(1)* %"fld$0" to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 4 %1, i8 addrspace(1)* align 4 %2, i64 8, i1 false) + %"fld$12" = getelementptr inbounds %"T7_N_ZN11std$FS$core6OptionIcEC_ZN7default21Class_1695327792688_9EactdlE", %"T7_N_ZN11std$FS$core6OptionIcEC_ZN7default21Class_1695327792688_9EactdlE"* %0, i32 0, i32 1 + store i8 addrspace(1)* %"fld$1", i8 addrspace(1)** %"fld$12", align 8 + %"fld$23" = getelementptr inbounds %"T7_N_ZN11std$FS$core6OptionIcEC_ZN7default21Class_1695327792688_9EactdlE", %"T7_N_ZN11std$FS$core6OptionIcEC_ZN7default21Class_1695327792688_9EactdlE"* %0, i32 0, i32 2 + store i8 %"fld$2", i8* %"fld$23", align 1 + %"fld$34" = getelementptr inbounds %"T7_N_ZN11std$FS$core6OptionIcEC_ZN7default21Class_1695327792688_9EactdlE", %"T7_N_ZN11std$FS$core6OptionIcEC_ZN7default21Class_1695327792688_9EactdlE"* %0, i32 0, i32 3 + store i32 %"fld$3", i32* %"fld$34", align 4 + %"fld$45" = getelementptr inbounds %"T7_N_ZN11std$FS$core6OptionIcEC_ZN7default21Class_1695327792688_9EactdlE", %"T7_N_ZN11std$FS$core6OptionIcEC_ZN7default21Class_1695327792688_9EactdlE"* %0, i32 0, i32 4 + store i16 %"fld$4", i16* %"fld$45", align 2 + %"fld$56" = getelementptr inbounds %"T7_N_ZN11std$FS$core6OptionIcEC_ZN7default21Class_1695327792688_9EactdlE", %"T7_N_ZN11std$FS$core6OptionIcEC_ZN7default21Class_1695327792688_9EactdlE"* %0, i32 0, i32 5 + store double %"fld$5", double* %"fld$56", align 8 + %"fld$67" = getelementptr inbounds %"T7_N_ZN11std$FS$core6OptionIcEC_ZN7default21Class_1695327792688_9EactdlE", %"T7_N_ZN11std$FS$core6OptionIcEC_ZN7default21Class_1695327792688_9EactdlE"* %0, i32 0, i32 6 + store i64 %"fld$6", i64* %"fld$67", align 8 + ret void +} diff --git a/llvm/test/Transforms/InstCombine/cj-instcombine2.ll b/llvm/test/Transforms/InstCombine/cj-instcombine2.ll new file mode 100644 index 000000000..8ec11b63e --- /dev/null +++ b/llvm/test/Transforms/InstCombine/cj-instcombine2.ll @@ -0,0 +1,111 @@ +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +%"record._ZN16models$FS$molnet10SquareLossE" = type { i1, i1 } +%"record._ZN16models$FS$molnet17WithForceLossCellE" = type { %"record._ZN16models$FS$molnet10SquareLossE", %"record._ZN16models$FS$molnet10SquareLossE", float, float, %"record._ZN16models$FS$molnet13MolCalculatorE" } +%"record._ZN16models$FS$molnet13MolCalculatorE" = type { %"record._ZN16models$FS$molnet6AirNetE", i1, float, float, i1, i1, i1, i64, %"record._ZN16CangjieTB$FS$ops6TensorE", %"record._ZN16models$FS$molnet13AtomDistancesE" } +%"record._ZN16models$FS$molnet6AirNetE" = type { %"record._ZN16CangjieTB$FS$ops6TensorE", i1, i32, i1, i1, i1, float, i32, %"record._ZN16models$FS$molnet23LogGaussianDistributionE", i32, %"record._ZN16models$FS$molnet12SmoothCutoffE", i1, i64, %"record._ZN16CangjieTB$FS$ops6TensorE", %"record._ZN16models$FS$molnet17AirNetInteractionE", %"record._ZN16models$FS$molnet17AirNetInteractionE", %"record._ZN16models$FS$molnet17AirNetInteractionE", %"record._ZN16models$FS$molnet15AtomwiseReadoutE", %"record._ZN22CangjieTB$FS$nn.layers9EmbeddingE", %"record._ZN16models$FS$molnet6FilterE" } +%"record._ZN16CangjieTB$FS$ops6TensorE" = type { i1, i32, i8 addrspace(1)* } +%"record._ZN16models$FS$molnet13AtomDistancesE" = type { i8 } +%"record._ZN16models$FS$molnet23LogGaussianDistributionE" = type { float, float, float, float, i1, i1, %"record._ZN16CangjieTB$FS$ops6TensorE", %"record._ZN16CangjieTB$FS$ops6TensorE" } +%"record._ZN16models$FS$molnet12SmoothCutoffE" = type { float } +%"record._ZN16models$FS$molnet17AirNetInteractionE" = type { %"record._ZN16CangjieTB$FS$ops6TensorE", %"record._ZN16CangjieTB$FS$ops6TensorE", %"record._ZN16CangjieTB$FS$ops6TensorE", %"record._ZN16CangjieTB$FS$ops6TensorE", %"record._ZN16CangjieTB$FS$ops6TensorE", %"record._ZN16CangjieTB$FS$ops6TensorE", i32, i1, float, i1, i1, i1, float, %"record._ZN22CangjieTB$FS$nn.layers5DenseE", %"record._ZN16models$FS$molnet19PositionalEmbeddingE", %"record._ZN16models$FS$molnet18MultiheadAttentionE", %"record._ZN22CangjieTB$FS$nn.layers5DenseE" } +%"record._ZN16models$FS$molnet15AtomwiseReadoutE" = type { i32, %"record._ZN16models$FS$molnet3MLPE" } +%"record._ZN22CangjieTB$FS$nn.layers9EmbeddingE" = type { %"record._ZN16CangjieTB$FS$ops6TensorE", i64, i64, i1 } +%"record._ZN16models$FS$molnet6FilterE" = type { i32, %"record._ZN16models$FS$molnet3MLPE", %"record._ZN22CangjieTB$FS$nn.layers5DenseE" } +%"record._ZN22CangjieTB$FS$nn.layers5DenseE" = type { %"record._ZN16CangjieTB$FS$ops6TensorE", %"record._ZN16CangjieTB$FS$ops6TensorE", i1, %"record._ZN11std$FS$core6StringE" } +%"record._ZN16models$FS$molnet19PositionalEmbeddingE" = type { %"record._ZN16models$FS$molnet12MolLayerNormE", %"record._ZN22CangjieTB$FS$nn.layers5DenseE", %"record._ZN22CangjieTB$FS$nn.layers5DenseE", %"record._ZN22CangjieTB$FS$nn.layers5DenseE" } +%"record._ZN16models$FS$molnet18MultiheadAttentionE" = type { i64, %"record._ZN16CangjieTB$FS$ops6TensorE", %"record._ZN16models$FS$molnet11std$FS$core5ArrayIlE", %"record._ZN16models$FS$molnet11std$FS$core5ArrayIlE", %"record._ZN22CangjieTB$FS$nn.layers5DenseE" } +%"record._ZN16models$FS$molnet3MLPE" = type { %"record._ZN22CangjieTB$FS$nn.layers5DenseE", %"record._ZN22CangjieTB$FS$nn.layers5DenseE" } +%"record._ZN11std$FS$core6StringE" = type { i8 addrspace(1)*, i64 } +%"record._ZN16models$FS$molnet12MolLayerNormE" = type { float, %"record._ZN16CangjieTB$FS$ops6TensorE", %"record._ZN16CangjieTB$FS$ops6TensorE" } +%"record._ZN16models$FS$molnet11std$FS$core5ArrayIlE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN16models$FS$molnet17WithForceLossCellEE" = type { %"record._ZN16models$FS$molnet10SquareLossE", %"record._ZN16models$FS$molnet10SquareLossE", %"record._ZN16models$FS$molnet10SquareLossE", %"record._ZN16models$FS$molnet10SquareLossE", float, float, %"record._ZN16models$FS$molnet13MolCalculatorE" } + +declare void @"_ZN16models$FS$molnet10SquareLoss4initER_ZN11std$FS$core6StringE"(i8 addrspace(1)*, %"record._ZN16models$FS$molnet10SquareLossE" addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) +declare void @"_ZN16models$FS$molnet10SquareLoss4initER_ZN11std$FS$core6StringE$reduction_0v"(%"record._ZN11std$FS$core6StringE"*) +declare void @llvm.cj.memset.p0i8(i8*, i8, i64, i1) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1) +declare void @llvm.memcpy.p0i8.p1i8.i64(i8*, i8 addrspace(1)*, i64, i1) + +;CHECK: %24 = bitcast %"record._ZN16models$FS$molnet17WithForceLossCellEE"* %tmpsroa to i64* +;CHECK-NEXT: %25 = load i64, i64* %tmpsroatuple +;CHECK-NEXT: store i64 %25, i64* %24 + +define void @foo(%"record._ZN16models$FS$molnet17WithForceLossCellE"* noalias sret(%"record._ZN16models$FS$molnet17WithForceLossCellE") %0, %"record._ZN16models$FS$molnet17WithForceLossCellEE"* %tmpsroa, i8 addrspace(1)* %"backbone$BP", %"record._ZN16models$FS$molnet13MolCalculatorE" addrspace(1)* %backbone, i8 addrspace(1)* %"energyFn$BP", %"record._ZN16models$FS$molnet10SquareLossE" addrspace(1)* %energyFn, i8 addrspace(1)* %"forceFn$BP", %"record._ZN16models$FS$molnet10SquareLossE" addrspace(1)* %forceFn, float %ratioEnergy) gc "cangjie" { +entry: + %tmpsroatuple = alloca i64, align 8 + %tuple.sroa.0 = alloca i32, align 8 + %tmpcast20 = bitcast i32* %tuple.sroa.0 to { %"record._ZN16models$FS$molnet10SquareLossE", %"record._ZN16models$FS$molnet10SquareLossE" }* + %tuple.sroa.69 = alloca %"record._ZN16models$FS$molnet13MolCalculatorE", align 8 + %backbone_ = alloca %"record._ZN16models$FS$molnet13MolCalculatorE", align 8 + %1 = alloca %"record._ZN16models$FS$molnet13MolCalculatorE", align 8 + %forceFn_18 = alloca i16, align 8 + %2 = alloca i16, align 8 + %tmpcast17 = bitcast i16* %2 to %"record._ZN16models$FS$molnet10SquareLossE"* + %callRet5 = alloca %"record._ZN11std$FS$core6StringE", align 8 + %energyFn_15 = alloca i16, align 8 + %3 = alloca i16, align 8 + %tmpcast = bitcast i16* %3 to %"record._ZN16models$FS$molnet10SquareLossE"* + %callRet = alloca %"record._ZN11std$FS$core6StringE", align 8 + %4 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %4, i8 0, i64 16, i1 false) + call void @"_ZN16models$FS$molnet10SquareLoss4initER_ZN11std$FS$core6StringE$reduction_0v"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet) + store i16 0, i16* %3, align 8 + store i64 8888, i64* %tmpsroatuple, align 8 + %5 = addrspacecast %"record._ZN16models$FS$molnet10SquareLossE"* %tmpcast to %"record._ZN16models$FS$molnet10SquareLossE" addrspace(1)* + %6 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet to %"record._ZN11std$FS$core6StringE" addrspace(1)* + call void @"_ZN16models$FS$molnet10SquareLoss4initER_ZN11std$FS$core6StringE"(i8 addrspace(1)* null, %"record._ZN16models$FS$molnet10SquareLossE" addrspace(1)* %5, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %6) + %7 = load i16, i16* %3, align 8 + store i16 %7, i16* %energyFn_15, align 8 + %8 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet5 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %8, i8 0, i64 16, i1 false) + call void @"_ZN16models$FS$molnet10SquareLoss4initER_ZN11std$FS$core6StringE$reduction_0v"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") %callRet5) + store i16 0, i16* %2, align 8 + %9 = addrspacecast %"record._ZN16models$FS$molnet10SquareLossE"* %tmpcast17 to %"record._ZN16models$FS$molnet10SquareLossE" addrspace(1)* + %10 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet5 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + call void @"_ZN16models$FS$molnet10SquareLoss4initER_ZN11std$FS$core6StringE"(i8 addrspace(1)* null, %"record._ZN16models$FS$molnet10SquareLossE" addrspace(1)* %9, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %10) + %11 = load i16, i16* %2, align 8 + store i16 %11, i16* %forceFn_18, align 8 + %backbone_.0.backbone_.0..sroa_cast = bitcast %"record._ZN16models$FS$molnet13MolCalculatorE"* %backbone_ to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %backbone_.0.backbone_.0..sroa_cast, i8 0, i64 2224, i1 false) + %.0..sroa_cast = bitcast %"record._ZN16models$FS$molnet13MolCalculatorE"* %1 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %.0..sroa_cast, i8 0, i64 2224, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(2224) %backbone_.0.backbone_.0..sroa_cast, i8* noundef nonnull align 8 dereferenceable(2224) %.0..sroa_cast, i64 2224, i1 false) + %12 = bitcast %"record._ZN16models$FS$molnet13MolCalculatorE" addrspace(1)* %backbone to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(2224) %backbone_.0.backbone_.0..sroa_cast, i8 addrspace(1)* noundef align 8 dereferenceable(2224) %12, i64 2224, i1 false) + %13 = bitcast %"record._ZN16models$FS$molnet10SquareLossE" addrspace(1)* %energyFn to i16 addrspace(1)* + %14 = load i16, i16 addrspace(1)* %13, align 1 + store i16 %14, i16* %energyFn_15, align 8 + %15 = bitcast %"record._ZN16models$FS$molnet10SquareLossE" addrspace(1)* %forceFn to i16 addrspace(1)* + %16 = load i16, i16 addrspace(1)* %15, align 1 + store i16 %16, i16* %forceFn_18, align 8 + %sub = fsub float 1.000000e+00, %ratioEnergy + %tuple.sroa.0.0..sroa_cast14 = bitcast i32* %tuple.sroa.0 to i8* + store i32 0, i32* %tuple.sroa.0, align 8 + %tuple.sroa.69.0..sroa_cast13 = bitcast %"record._ZN16models$FS$molnet13MolCalculatorE"* %tuple.sroa.69 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %tuple.sroa.69.0..sroa_cast13, i8 0, i64 2224, i1 false) + %17 = bitcast i32* %tuple.sroa.0 to i16* + %18 = load i16, i16* %energyFn_15, align 8 + store i16 %18, i16* %17, align 8 + %tuple.sroa.0.2..sroa_idx = getelementptr inbounds { %"record._ZN16models$FS$molnet10SquareLossE", %"record._ZN16models$FS$molnet10SquareLossE" }, { %"record._ZN16models$FS$molnet10SquareLossE", %"record._ZN16models$FS$molnet10SquareLossE" }* %tmpcast20, i64 0, i32 1 + %19 = bitcast %"record._ZN16models$FS$molnet10SquareLossE"* %tuple.sroa.0.2..sroa_idx to i16* + %20 = load i16, i16* %forceFn_18, align 8 + store i16 %20, i16* %19, align 2 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(2224) %tuple.sroa.69.0..sroa_cast13, i8* noundef nonnull align 8 dereferenceable(2224) %backbone_.0.backbone_.0..sroa_cast, i64 2224, i1 false) + %21 = bitcast %"record._ZN16models$FS$molnet17WithForceLossCellE"* %0 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %21, i8* align 8 %tuple.sroa.0.0..sroa_cast14, i64 4, i1 false) + %tmpsroa.0 = bitcast %"record._ZN16models$FS$molnet17WithForceLossCellEE"* %tmpsroa to i8* + %tmpsroatuple.0 = bitcast i64* %tmpsroatuple to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %tmpsroa.0, i8* align 8 %tmpsroatuple.0, i64 8, i1 false) + %tuple.sroa.4.0..sroa_idx6 = getelementptr inbounds %"record._ZN16models$FS$molnet17WithForceLossCellE", %"record._ZN16models$FS$molnet17WithForceLossCellE"* %0, i64 0, i32 2 + store float %ratioEnergy, float* %tuple.sroa.4.0..sroa_idx6, align 4 + %tuple.sroa.5.0..sroa_idx7 = getelementptr inbounds %"record._ZN16models$FS$molnet17WithForceLossCellE", %"record._ZN16models$FS$molnet17WithForceLossCellE"* %0, i64 0, i32 3 + store float %sub, float* %tuple.sroa.5.0..sroa_idx7, align 8 + %tuple.sroa.6.0..sroa_idx = getelementptr inbounds i8, i8* %21, i64 12 + %tuple.sroa.6.0..sroa_cast8 = bitcast i8* %tuple.sroa.6.0..sroa_idx to i32* + store i32 0, i32* %tuple.sroa.6.0..sroa_cast8, align 4 + %tuple.sroa.69.0..sroa_idx = getelementptr inbounds %"record._ZN16models$FS$molnet17WithForceLossCellE", %"record._ZN16models$FS$molnet17WithForceLossCellE"* %0, i64 0, i32 4 + %tuple.sroa.69.0..sroa_cast10 = bitcast %"record._ZN16models$FS$molnet13MolCalculatorE"* %tuple.sroa.69.0..sroa_idx to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %tuple.sroa.69.0..sroa_cast10, i8* align 8 %tuple.sroa.69.0..sroa_cast13, i64 2224, i1 false) + ret void +} diff --git a/llvm/test/Transforms/InstCombine/cj-instcombine3.ll b/llvm/test/Transforms/InstCombine/cj-instcombine3.ll new file mode 100644 index 000000000..f1bfb277a --- /dev/null +++ b/llvm/test/Transforms/InstCombine/cj-instcombine3.ll @@ -0,0 +1,56 @@ +; RUN: opt --passes='instcombine' --cangjie-pipeline -S < %s | FileCheck %s + +%ObjLayout.Object = type { i8* } +%"ObjLayout._ZN11std$FS$core6ObjectE" = type { %ObjLayout.Object } +%record._ZN7default24Struct_1694636776828_218E = type { i8 addrspace(1)* } +%"ObjLayout._ZN7default11$Auto_Env$0E" = type { %"ObjLayout._ZN11std$FS$core6ObjectE", %record._ZN7default24Struct_1694636776828_218E } + +@_ZN7default4stepE = internal global i64 2, align 8 + +declare i64 @"_ZN11std$FS$core6Extendl4nextEl"(i64, i64) gc "cangjie" + +define void @_ZN7default4mainEv(%record._ZN7default24Struct_1694636776828_218E* sret(%record._ZN7default24Struct_1694636776828_218E) %0, i8 addrspace(1)* %"$env") #1 gc "cangjie" { +; CHECK-LABEL: @_ZN7default4mainEv( +; CHECK-LABEL: entry +; CHECK-NEXT: %1 = bitcast i8 addrspace(1)* %"$env" to %"ObjLayout._ZN7default11$Auto_Env$0E" addrspace(1)* +; CHECK-NEXT: %2 = getelementptr inbounds %"ObjLayout._ZN7default11$Auto_Env$0E", %"ObjLayout._ZN7default11$Auto_Env$0E" addrspace(1)* %1, i64 0, i32 1, i32 0 +; CHECK-NEXT: %3 = getelementptr inbounds %record._ZN7default24Struct_1694636776828_218E, %record._ZN7default24Struct_1694636776828_218E* %0, i64 0, i32 0 +; CHECK-NEXT: %4 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %2, align 8 +; CHECK-NEXT: store i8 addrspace(1)* %4, i8 addrspace(1)** %3, align 8 +entry: + %1 = bitcast i8 addrspace(1)* %"$env" to %"ObjLayout._ZN7default11$Auto_Env$0E" addrspace(1)* + %2 = getelementptr inbounds %"ObjLayout._ZN7default11$Auto_Env$0E", %"ObjLayout._ZN7default11$Auto_Env$0E" addrspace(1)* %1, i32 0, i32 1 + %3 = bitcast %record._ZN7default24Struct_1694636776828_218E addrspace(1)* %2 to i8 addrspace(1)* + %4 = bitcast %record._ZN7default24Struct_1694636776828_218E* %0 to i8* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* align 8 %4, i8 addrspace(1)* align 8 %3, i64 8, i1 false) + ret void +} + +; Function Attrs: argmemonly nounwind +declare void @llvm.cj.gcwrite.i1.i1(i1, i8 addrspace(1)* nocapture, i1 addrspace(1)* nocapture) #2 + +; Function Attrs: argmemonly nounwind +declare void @llvm.cj.gcwrite.i64.i64(i64, i8 addrspace(1)* nocapture, i64 addrspace(1)* nocapture) #2 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #3 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #4 + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare i64 @llvm.sadd.sat.i64(i64, i64) #5 + +; Function Attrs: nounwind +declare void @llvm.cj.memset(i8*, i8, i64, i1) #6 + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn +declare void @llvm.memcpy.p0i8.p1i8.i64(i8* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) + +attributes #0 = { "cj_fast_call" "hasRcdParam" "record_mut" } +attributes #1 = { "cjinit" } +attributes #2 = { argmemonly nounwind } +attributes #3 = { argmemonly nofree nosync nounwind willreturn } +attributes #4 = { argmemonly nofree nosync nounwind willreturn writeonly } +attributes #5 = { nofree nosync nounwind readnone speculatable willreturn } +attributes #6 = { nounwind } diff --git a/llvm/test/Transforms/PartialInlining/cj_partial_inlining.ll b/llvm/test/Transforms/PartialInlining/cj_partial_inlining.ll new file mode 100644 index 000000000..9ecf567ce --- /dev/null +++ b/llvm/test/Transforms/PartialInlining/cj_partial_inlining.ll @@ -0,0 +1,394 @@ +; RUN: opt --cangjie-pipeline -partial-inliner -skip-partial-inlining-cost-analysis -S < %s | FileCheck %s + +; CHECK: %pi_gep +; CHECK: %pi_bitcastgep + +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +%BitMap = type { i32, [0 x i8] } +%KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [1 x i8*] } +%Unit.Type = type { i8 } +%_ZN07ClosureE = type { i8*, i8 addrspace(1)* } +%ObjLayout.Object = type { %KlassInfo.0* } +%ArrayBase = type { %ObjLayout.Object, i64 } +%"T2_R_ZN11std$FS$core5ArrayIjElE" = type { %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIjE", i64 } +%"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIjE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE" = type { i8 addrspace(1)*, i64, i64 } +%"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" = type { %ArrayBase, [0 x %"T2_R_ZN11std$FS$core5ArrayIjElE"] } +%ArrayLayout.UInt32 = type { %ArrayBase, [0 x i32] } +%"ObjLayout._ZN11std$FS$core6ObjectE" = type { %ObjLayout.Object } +%"ObjLayout._ZN17numeric$FS$bigint21__Auto__Environment_5E" = type { %"ObjLayout._ZN11std$FS$core6ObjectE", %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 } + +@"__profc_numeric_bigint__ZN17numeric$FS$bigint11std$FS$core19arrayInitByFunctionIjEA1_jEC_ZN017$LambdaCommon_1jlE" = private global [3 x i64] zeroinitializer, section "__llvm_prf_cnts", align 8 +@"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE" = private global [12 x i64] zeroinitializer, section "__llvm_prf_cnts", align 8 +@"__profd_numeric_bigint__ZN17numeric$FS$bigint11std$FS$core19arrayInitByFunctionIjEA1_jEC_ZN017$LambdaCommon_1jlE" = private global { i64, i64, i64, i8*, i8*, i32, [2 x i16] } { i64 1145983669766020984, i64 543243706801081985, i64 sub (i64 ptrtoint ([3 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint11std$FS$core19arrayInitByFunctionIjEA1_jEC_ZN017$LambdaCommon_1jlE" to i64), i64 ptrtoint ({ i64, i64, i64, i8*, i8*, i32, [2 x i16] }* @"__profd_numeric_bigint__ZN17numeric$FS$bigint11std$FS$core19arrayInitByFunctionIjEA1_jEC_ZN017$LambdaCommon_1jlE" to i64)), i8* null, i8* bitcast ([1 x i64]* @"__profvp_numeric_bigint__ZN17numeric$FS$bigint11std$FS$core19arrayInitByFunctionIjEA1_jEC_ZN017$LambdaCommon_1jlE" to i8*), i32 3, [2 x i16] [i16 1, i16 0] }, section "__llvm_prf_data", align 8 +@"__profvp_numeric_bigint__ZN17numeric$FS$bigint11std$FS$core19arrayInitByFunctionIjEA1_jEC_ZN017$LambdaCommon_1jlE" = private global [1 x i64] zeroinitializer, section "__llvm_prf_vals", align 8 +@A1_jE.className = weak_odr hidden global [6 x i8] c"A1_jE\00", align 1 +@"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" = external global %KlassInfo.0 +@"_ZN11std$FS$core26NegativeArraySizeExceptionE.objKlass" = external global %KlassInfo.0 +@UInt32.arrayKlass = weak_odr hidden global %KlassInfo.0 { i32 0, i32 0, %BitMap* null, i8* null, i8** null, i64* null, i64* null, i64* bitcast ([6 x i8]* @A1_jE.className to i64*), i32 0, [0 x i8*] zeroinitializer } +@"_ZN11std$FS$core6ObjectE.objKlass" = external global %KlassInfo.1 +@"_ZN17numeric$FS$bigint21__Auto__Environment_5E.objKlass" = internal global %KlassInfo.0 { i32 0, i32 0, %BitMap* null, i8* bitcast (%KlassInfo.1* @"_ZN11std$FS$core6ObjectE.objKlass" to i8*), i8** null, i64* null, i64* null, i64* null, i32 0, [0 x i8*] zeroinitializer } + +define internal fastcc void @"_ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE"(%"T2_R_ZN11std$FS$core5ArrayIjElE"* noalias nocapture writeonly sret(%"T2_R_ZN11std$FS$core5ArrayIjElE") %0, i8 addrspace(1)* nocapture readnone %"arr$BP", %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE" addrspace(1)* nocapture readonly %arr) unnamed_addr #13 gc "cangjie" personality i32 (...)* @"__cj_personality_v0$" { +entry: + %callRet88 = alloca %"T2_R_ZN11std$FS$core5ArrayIjElE", align 8 + %arr.get86.sroa.0 = alloca %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIjE", align 8 + %arr.get76.sroa.0 = alloca { i8 addrspace(1)*, i64 }, align 8 + %arr.get67 = alloca %"T2_R_ZN11std$FS$core5ArrayIjElE", align 8 + %callRet52 = alloca %Unit.Type, align 8 + %arr.get50 = alloca %"T2_R_ZN11std$FS$core5ArrayIjElE", align 8 + %rest = alloca %"T2_R_ZN11std$FS$core5ArrayIjElE", align 8 + %arr.get30.sroa.0 = alloca { i8 addrspace(1)*, i64 }, align 8 + %closure = alloca %_ZN07ClosureE, align 8 + %arr.get10.sroa.0 = alloca { i8 addrspace(1)*, i64 }, align 8 + %arr.get.sroa.0 = alloca %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIjE", align 8 + %1 = getelementptr inbounds %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE" addrspace(1)* %arr, i64 0, i32 2 + %2 = load i64, i64 addrspace(1)* %1, align 8 + %icmpuge = icmp eq i64 %2, 0 + br i1 %icmpuge, label %if.then, label %if.else + +if.then: ; preds = %entry + %pgocount = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 3), align 8 + %3 = add i64 %pgocount, 1 + store i64 %3, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 3), align 8 + %4 = tail call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 56) + tail call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %4) + tail call void @CJ_MCC_ThrowException(i8 addrspace(1)* %4) + unreachable + +if.else: ; preds = %entry + %5 = bitcast %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE" addrspace(1)* %arr to %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* addrspace(1)* + %6 = load %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)*, %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* addrspace(1)* %5, align 8 + %7 = getelementptr inbounds %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE" addrspace(1)* %arr, i64 0, i32 1 + %8 = load i64, i64 addrspace(1)* %7, align 8 + %arr.idx.get.gep = getelementptr inbounds %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE", %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %6, i64 0, i32 1, i64 %8 + %arr.get.sroa.0.0.sroa_cast6 = bitcast %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIjE"* %arr.get.sroa.0 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %arr.get.sroa.0.0.sroa_cast6, i8 0, i64 24, i1 false) + %arr.get.sroa.0.0..sroa_cast = bitcast %"T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %arr.idx.get.gep to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(24) %arr.get.sroa.0.0.sroa_cast6, i8 addrspace(1)* noundef align 8 dereferenceable(24) %arr.get.sroa.0.0..sroa_cast, i64 24, i1 false) + %arr.get.sroa.2.0..sroa_idx5 = getelementptr inbounds %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE", %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %6, i64 0, i32 1, i64 %8, i32 1 + %arr.get.sroa.2.0.copyload = load i64, i64 addrspace(1)* %arr.get.sroa.2.0..sroa_idx5, align 8 + %9 = load i64, i64 addrspace(1)* %1, align 8 + %icmpuge4 = icmp eq i64 %9, 0 + br i1 %icmpuge4, label %if.then7, label %if.else6 + +if.then7: ; preds = %if.else + %pgocount84 = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 4), align 8 + %10 = add i64 %pgocount84, 1 + store i64 %10, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 4), align 8 + %11 = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 56) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %11) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %11) + unreachable + +if.else6: ; preds = %if.else + %12 = load %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)*, %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* addrspace(1)* %5, align 8 + %13 = load i64, i64 addrspace(1)* %7, align 8 + %arr.idx.get.gep9 = getelementptr inbounds %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE", %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %12, i64 0, i32 1, i64 %13 + %arr.get10.sroa.0.0.sroa_cast13 = bitcast { i8 addrspace(1)*, i64 }* %arr.get10.sroa.0 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %arr.get10.sroa.0.0.sroa_cast13, i8 0, i64 16, i1 false) + %arr.get10.sroa.0.0..sroa_cast = bitcast %"T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %arr.idx.get.gep9 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %arr.get10.sroa.0.0.sroa_cast13, i8 addrspace(1)* noundef align 8 dereferenceable(16) %arr.get10.sroa.0.0..sroa_cast, i64 16, i1 false) + %arr.get10.sroa.2.0..sroa_idx11 = getelementptr inbounds %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE", %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %12, i64 0, i32 1, i64 %13, i32 0, i32 2 + %arr.get10.sroa.2.0.copyload = load i64, i64 addrspace(1)* %arr.get10.sroa.2.0..sroa_idx11, align 8 + %sub = sub i64 %arr.get10.sroa.2.0.copyload, %arr.get.sroa.2.0.copyload + %icmpeq = icmp eq i64 %2, 1 + br i1 %icmpeq, label %if.then16, label %if.else15 + +if.then16: ; preds = %if.else6 + %arr.alloc.size.valid = icmp sgt i64 %sub, -1 + br i1 %arr.alloc.size.valid, label %arr.alloc.body, label %arr.alloc.throw + +arr.alloc.throw: ; preds = %if.then16 + %pgocount85 = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 5), align 8 + %14 = add i64 %pgocount85, 1 + store i64 %14, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 5), align 8 + %15 = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core26NegativeArraySizeExceptionE.objKlass" to i8*), i32 56) + call void @"_ZN11std$FS$core26NegativeArraySizeException4initEv"(i8 addrspace(1)* %15) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %15) + unreachable + +arr.alloc.body: ; preds = %if.then16 + %pgocount86 = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 1), align 8 + %16 = add i64 %pgocount86, 1 + store i64 %16, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 1), align 8 + %17 = call noalias i8 addrspace(1)* @CJ_MCC_NewArray32Stub(i64 %sub, i8* bitcast (%KlassInfo.0* @UInt32.arrayKlass to i8*)) + %18 = bitcast %_ZN07ClosureE* %closure to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %18, i8 0, i64 16, i1 false) + %19 = getelementptr inbounds %_ZN07ClosureE, %_ZN07ClosureE* %closure, i64 0, i32 0 + store i8* bitcast (i32 (i8 addrspace(1)*, i64)* @"numeric/bigint$lambda.5" to i8*), i8** %19, align 8 + %20 = getelementptr inbounds %_ZN07ClosureE, %_ZN07ClosureE* %closure, i64 0, i32 1 + %21 = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN17numeric$FS$bigint21__Auto__Environment_5E.objKlass" to i8*), i32 40) + %22 = getelementptr inbounds i8, i8 addrspace(1)* %21, i64 8 + %23 = bitcast %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE" addrspace(1)* %arr to i8 addrspace(1)* + call void @llvm.cj.gcwrite.struct.p1i8(i8 addrspace(1)* %21, i8 addrspace(1)* %22, i8 addrspace(1)* %23, i64 24) + %24 = getelementptr inbounds i8, i8 addrspace(1)* %21, i64 32 + %25 = bitcast i8 addrspace(1)* %24 to i64 addrspace(1)* + call void @llvm.cj.gcwrite.i64.i64(i64 %arr.get.sroa.2.0.copyload, i8 addrspace(1)* %21, i64 addrspace(1)* %25) + store i8 addrspace(1)* %21, i8 addrspace(1)** %20, align 8 + %26 = addrspacecast %_ZN07ClosureE* %closure to %_ZN07ClosureE addrspace(1)* + %27 = call fastcc i8 addrspace(1)* @"_ZN17numeric$FS$bigint11std$FS$core19arrayInitByFunctionIjEA1_jEC_ZN017$LambdaCommon_1jlE"(i8 addrspace(1)* %17, i8 addrspace(1)* null, %_ZN07ClosureE addrspace(1)* %26) + br label %if.end14 + +if.else15: ; preds = %if.else6 + %sub21 = add i64 %2, -1 + %28 = load i64, i64 addrspace(1)* %1, align 8 + %icmpuge24.not = icmp ult i64 %sub21, %28 + br i1 %icmpuge24.not, label %if.else26, label %if.then27 + +if.then27: ; preds = %if.else15 + %pgocount87 = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 6), align 8 + %29 = add i64 %pgocount87, 1 + store i64 %29, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 6), align 8 + %30 = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 56) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %30) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %30) + unreachable + +if.else26: ; preds = %if.else15 + %31 = load %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)*, %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* addrspace(1)* %5, align 8 + %32 = load i64, i64 addrspace(1)* %7, align 8 + %add = add i64 %32, %sub21 + %arr.idx.get.gep29 = getelementptr inbounds %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE", %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %31, i64 0, i32 1, i64 %add + %arr.get30.sroa.0.0.sroa_cast23 = bitcast { i8 addrspace(1)*, i64 }* %arr.get30.sroa.0 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %arr.get30.sroa.0.0.sroa_cast23, i8 0, i64 16, i1 false) + %arr.get30.sroa.0.0..sroa_cast = bitcast %"T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %arr.idx.get.gep29 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %arr.get30.sroa.0.0.sroa_cast23, i8 addrspace(1)* noundef align 8 dereferenceable(16) %arr.get30.sroa.0.0..sroa_cast, i64 16, i1 false) + %arr.get30.sroa.2.0..sroa_idx21 = getelementptr inbounds %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE", %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %31, i64 0, i32 1, i64 %add, i32 0, i32 2 + %arr.get30.sroa.2.0.copyload = load i64, i64 addrspace(1)* %arr.get30.sroa.2.0..sroa_idx21, align 8 + %add32 = add i64 %arr.get30.sroa.2.0.copyload, 1 + %arr.alloc.size.valid37 = icmp sgt i64 %add32, -1 + br i1 %arr.alloc.size.valid37, label %arr.alloc.body35, label %arr.alloc.throw36 + +arr.alloc.throw36: ; preds = %if.else26 + %pgocount88 = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 7), align 8 + %33 = add i64 %pgocount88, 1 + store i64 %33, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 7), align 8 + %34 = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core26NegativeArraySizeExceptionE.objKlass" to i8*), i32 56) + call void @"_ZN11std$FS$core26NegativeArraySizeException4initEv"(i8 addrspace(1)* %34) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %34) + unreachable + +arr.alloc.body35: ; preds = %if.else26 + %35 = call noalias i8 addrspace(1)* @CJ_MCC_NewArray32Stub(i64 %add32, i8* bitcast (%KlassInfo.0* @UInt32.arrayKlass to i8*)) + %sub39 = sub i64 %add32, %sub + %36 = bitcast %"T2_R_ZN11std$FS$core5ArrayIjElE"* %rest to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %36, i8 0, i64 32, i1 false) + %callRet40.sroa.0.0..sroa_idx = getelementptr inbounds %"T2_R_ZN11std$FS$core5ArrayIjElE", %"T2_R_ZN11std$FS$core5ArrayIjElE"* %rest, i64 0, i32 0, i32 0 + store i8 addrspace(1)* %35, i8 addrspace(1)** %callRet40.sroa.0.0..sroa_idx, align 8 + %callRet40.sroa.3.0..sroa_idx79 = getelementptr inbounds %"T2_R_ZN11std$FS$core5ArrayIjElE", %"T2_R_ZN11std$FS$core5ArrayIjElE"* %rest, i64 0, i32 0, i32 1 + store i64 0, i64* %callRet40.sroa.3.0..sroa_idx79, align 8 + %callRet40.sroa.4.0..sroa_idx80 = getelementptr inbounds %"T2_R_ZN11std$FS$core5ArrayIjElE", %"T2_R_ZN11std$FS$core5ArrayIjElE"* %rest, i64 0, i32 0, i32 2 + store i64 %add32, i64* %callRet40.sroa.4.0..sroa_idx80, align 8 + %callRet40.sroa.5.0..sroa_idx81 = getelementptr inbounds %"T2_R_ZN11std$FS$core5ArrayIjElE", %"T2_R_ZN11std$FS$core5ArrayIjElE"* %rest, i64 0, i32 1 + store i64 %sub39, i64* %callRet40.sroa.5.0..sroa_idx81, align 8 + %37 = load i64, i64 addrspace(1)* %1, align 8 + %icmpuge44 = icmp eq i64 %37, 0 + br i1 %icmpuge44, label %if.then47, label %if.else46 + +if.then47: ; preds = %arr.alloc.body35 + %pgocount89 = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 8), align 8 + %38 = add i64 %pgocount89, 1 + store i64 %38, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 8), align 8 + %39 = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 56) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %39) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %39) + unreachable + +if.else46: ; preds = %arr.alloc.body35 + %40 = load %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)*, %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* addrspace(1)* %5, align 8 + %41 = load i64, i64 addrspace(1)* %7, align 8 + %arr.idx.get.gep49 = getelementptr inbounds %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE", %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %40, i64 0, i32 1, i64 %41 + %42 = bitcast %"T2_R_ZN11std$FS$core5ArrayIjElE"* %arr.get50 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %42, i8 0, i64 32, i1 false) + %43 = bitcast %"T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %arr.idx.get.gep49 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(32) %42, i8 addrspace(1)* noundef align 8 dereferenceable(32) %43, i64 32, i1 false) + %44 = getelementptr inbounds %"T2_R_ZN11std$FS$core5ArrayIjElE", %"T2_R_ZN11std$FS$core5ArrayIjElE"* %arr.get50, i64 0, i32 0 + %45 = getelementptr inbounds %"T2_R_ZN11std$FS$core5ArrayIjElE", %"T2_R_ZN11std$FS$core5ArrayIjElE"* %rest, i64 0, i32 0 + %46 = addrspacecast %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIjE"* %44 to %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIjE" addrspace(1)* + %47 = addrspacecast %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIjE"* %45 to %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIjE" addrspace(1)* + call fastcc void @"_ZN17numeric$FS$bigint11std$FS$core5ArrayIj6copyToER_ZN11std$FS$core5ArrayIjElll"(%Unit.Type* noalias nonnull sret(%Unit.Type) %callRet52, i8 addrspace(1)* null, %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIjE" addrspace(1)* %46, i8 addrspace(1)* null, %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIjE" addrspace(1)* %47, i64 %arr.get.sroa.2.0.copyload, i64 %sub39, i64 %sub) + %48 = bitcast %"T2_R_ZN11std$FS$core5ArrayIjElE"* %callRet88 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %48, i8 0, i64 32, i1 false) + %arr.get86.sroa.0.0.sroa_cast45 = bitcast %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIjE"* %arr.get86.sroa.0 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %arr.get86.sroa.0.0.sroa_cast45, i8 0, i64 24, i1 false) + %arr.get76.sroa.0.0.sroa_cast41 = bitcast { i8 addrspace(1)*, i64 }* %arr.get76.sroa.0 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %arr.get76.sroa.0.0.sroa_cast41, i8 0, i64 16, i1 false) + %49 = bitcast %"T2_R_ZN11std$FS$core5ArrayIjElE"* %arr.get67 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %49, i8 0, i64 32, i1 false) + %icmpslt82 = icmp sgt i64 %2, 1 + br i1 %icmpslt82, label %while.body.preheader, label %block.end + +while.body.preheader: ; preds = %if.else46 + %50 = addrspacecast %"T2_R_ZN11std$FS$core5ArrayIjElE"* %rest to %"T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* + %51 = addrspacecast %"T2_R_ZN11std$FS$core5ArrayIjElE"* %arr.get67 to %"T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* + br label %while.body + +while.body: ; preds = %while.body.preheader, %if.else81 + %atom55.083 = phi i64 [ %add58, %if.else81 ], [ 1, %while.body.preheader ] + %52 = load i64, i64 addrspace(1)* %1, align 8 + %icmpuge60.not = icmp ult i64 %atom55.083, %52 + br i1 %icmpuge60.not, label %if.else81, label %if.then63 + +if.then63: ; preds = %while.body + %pgocount90 = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 9), align 8 + %53 = add i64 %pgocount90, 1 + store i64 %53, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 9), align 8 + %54 = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 56) + call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %54) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %54) + unreachable + +if.else81: ; preds = %while.body + %add58 = add nuw nsw i64 %atom55.083, 1 + %55 = load %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)*, %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* addrspace(1)* %5, align 8 + %56 = load i64, i64 addrspace(1)* %7, align 8 + %add65 = add i64 %56, %atom55.083 + %arr.idx.get.gep66 = getelementptr inbounds %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE", %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %55, i64 0, i32 1, i64 %add65 + %57 = bitcast %"T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %arr.idx.get.gep66 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(32) %49, i8 addrspace(1)* noundef align 8 dereferenceable(32) %57, i64 32, i1 false) + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(16) %arr.get76.sroa.0.0.sroa_cast41, i8 addrspace(1)* noundef align 8 dereferenceable(16) %57, i64 16, i1 false) + %pgocount93 = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 0), align 8 + %58 = add i64 %pgocount93, 1 + store i64 %58, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 0), align 8 + %arr.get76.sroa.2.0..sroa_idx39 = getelementptr inbounds %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE", %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %55, i64 0, i32 1, i64 %add65, i32 0, i32 2 + %arr.get76.sroa.2.0.copyload = load i64, i64 addrspace(1)* %arr.get76.sroa.2.0..sroa_idx39, align 8 + %59 = load %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)*, %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* addrspace(1)* %5, align 8 + %arr.idx.get.gep85 = getelementptr inbounds %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE", %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %59, i64 0, i32 1, i64 %add65 + %arr.get86.sroa.0.0..sroa_cast = bitcast %"T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %arr.idx.get.gep85 to i8 addrspace(1)* + call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(24) %arr.get86.sroa.0.0.sroa_cast45, i8 addrspace(1)* noundef align 8 dereferenceable(24) %arr.get86.sroa.0.0..sroa_cast, i64 24, i1 false) + %arr.get86.sroa.2.0..sroa_idx44 = getelementptr inbounds %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE", %"ArrayLayout.T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %59, i64 0, i32 1, i64 %add65, i32 1 + %arr.get86.sroa.2.0.copyload = load i64, i64 addrspace(1)* %arr.get86.sroa.2.0..sroa_idx44, align 8 + %sub87 = sub i64 %arr.get76.sroa.2.0.copyload, %arr.get86.sroa.2.0.copyload + call fastcc void @"_ZN17numeric$FS$bigint6BigInt7addDMutET2_R_ZN11std$FS$core5ArrayIjElET2_R_ZN11std$FS$core5ArrayIjElEl"(%"T2_R_ZN11std$FS$core5ArrayIjElE"* noalias nonnull sret(%"T2_R_ZN11std$FS$core5ArrayIjElE") %callRet88, i8 addrspace(1)* null, %"T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %50, i8 addrspace(1)* null, %"T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)* %51, i64 %sub87) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(32) %36, i8* noundef nonnull align 8 dereferenceable(32) %48, i64 32, i1 false) + %exitcond.not = icmp eq i64 %add58, %2 + br i1 %exitcond.not, label %block.end, label %while.body + +block.end: ; preds = %if.else81, %if.else46 + %pgocount94 = load i64, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 2), align 8 + %60 = add i64 %pgocount94, 1 + store i64 %60, i64* getelementptr inbounds ([12 x i64], [12 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint6BigInt10sumOrderedER_ZN11std$FS$core5ArrayIT2_R_ZN11std$FS$core5ArrayIjElEE", i64 0, i64 2), align 8 + %ifvalue13.sroa.0.0.copyload59 = load i8 addrspace(1)*, i8 addrspace(1)** %callRet40.sroa.0.0..sroa_idx, align 8 + %ifvalue13.sroa.4.0.copyload62 = load i64, i64* %callRet40.sroa.3.0..sroa_idx79, align 8 + %ifvalue13.sroa.5.0.copyload65 = load i64, i64* %callRet40.sroa.4.0..sroa_idx80, align 8 + %ifvalue13.sroa.6.0.copyload68 = load i64, i64* %callRet40.sroa.5.0..sroa_idx81, align 8 + br label %if.end14 + +if.end14: ; preds = %block.end, %arr.alloc.body + %ifvalue13.sroa.0.0 = phi i8 addrspace(1)* [ %17, %arr.alloc.body ], [ %ifvalue13.sroa.0.0.copyload59, %block.end ] + %ifvalue13.sroa.4.0 = phi i64 [ 0, %arr.alloc.body ], [ %ifvalue13.sroa.4.0.copyload62, %block.end ] + %ifvalue13.sroa.5.0 = phi i64 [ %sub, %arr.alloc.body ], [ %ifvalue13.sroa.5.0.copyload65, %block.end ] + %ifvalue13.sroa.6.0 = phi i64 [ 0, %arr.alloc.body ], [ %ifvalue13.sroa.6.0.copyload68, %block.end ] + %ifvalue13.sroa.0.0..sroa_idx = getelementptr inbounds %"T2_R_ZN11std$FS$core5ArrayIjElE", %"T2_R_ZN11std$FS$core5ArrayIjElE"* %0, i64 0, i32 0, i32 0 + store i8 addrspace(1)* %ifvalue13.sroa.0.0, i8 addrspace(1)** %ifvalue13.sroa.0.0..sroa_idx, align 8 + %ifvalue13.sroa.4.0..sroa_idx60 = getelementptr inbounds %"T2_R_ZN11std$FS$core5ArrayIjElE", %"T2_R_ZN11std$FS$core5ArrayIjElE"* %0, i64 0, i32 0, i32 1 + store i64 %ifvalue13.sroa.4.0, i64* %ifvalue13.sroa.4.0..sroa_idx60, align 8 + %ifvalue13.sroa.5.0..sroa_idx63 = getelementptr inbounds %"T2_R_ZN11std$FS$core5ArrayIjElE", %"T2_R_ZN11std$FS$core5ArrayIjElE"* %0, i64 0, i32 0, i32 2 + store i64 %ifvalue13.sroa.5.0, i64* %ifvalue13.sroa.5.0..sroa_idx63, align 8 + %ifvalue13.sroa.6.0..sroa_idx66 = getelementptr inbounds %"T2_R_ZN11std$FS$core5ArrayIjElE", %"T2_R_ZN11std$FS$core5ArrayIjElE"* %0, i64 0, i32 1 + store i64 %ifvalue13.sroa.6.0, i64* %ifvalue13.sroa.6.0..sroa_idx66, align 8 + ret void +} + +define internal fastcc i8 addrspace(1)* @"_ZN17numeric$FS$bigint11std$FS$core19arrayInitByFunctionIjEA1_jEC_ZN017$LambdaCommon_1jlE"(i8 addrspace(1)* returned %array, i8 addrspace(1)* nocapture readnone %"initElement$BP", %_ZN07ClosureE addrspace(1)* nocapture readonly %initElement) unnamed_addr #13 gc "cangjie" personality i32 (...)* @"__cj_personality_v0$" { +entry: + %pgocount = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint11std$FS$core19arrayInitByFunctionIjEA1_jEC_ZN017$LambdaCommon_1jlE", i64 0, i64 1), align 8 + %0 = add i64 %pgocount, 1 + store i64 %0, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint11std$FS$core19arrayInitByFunctionIjEA1_jEC_ZN017$LambdaCommon_1jlE", i64 0, i64 1), align 8 + %1 = getelementptr inbounds i8, i8 addrspace(1)* %array, i64 8 + %2 = bitcast i8 addrspace(1)* %1 to i64 addrspace(1)* + %3 = load i64, i64 addrspace(1)* %2, align 8 + %icmpslt4 = icmp sgt i64 %3, 0 + br i1 %icmpslt4, label %while.body.preheader, label %block.end + +while.body.preheader: ; preds = %entry + %4 = bitcast %_ZN07ClosureE addrspace(1)* %initElement to i32 (i8 addrspace(1)*, i64)* addrspace(1)* + %5 = getelementptr inbounds %_ZN07ClosureE, %_ZN07ClosureE addrspace(1)* %initElement, i64 0, i32 1 + %6 = bitcast i8 addrspace(1)* %array to %ArrayLayout.UInt32 addrspace(1)* + br label %while.body + +while.body: ; preds = %while.body.preheader, %arr.if.else + %pgocount78 = phi i64 [ %15, %arr.if.else ], [ 0, %while.body.preheader ] + %7 = load i32 (i8 addrspace(1)*, i64)*, i32 (i8 addrspace(1)*, i64)* addrspace(1)* %4, align 8 + %8 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %5, align 8 + %9 = ptrtoint i32 (i8 addrspace(1)*, i64)* %7 to i64 + tail call void @__llvm_profile_instrument_target(i64 %9, i8* bitcast ({ i64, i64, i64, i8*, i8*, i32, [2 x i16] }* @"__profd_numeric_bigint__ZN17numeric$FS$bigint11std$FS$core19arrayInitByFunctionIjEA1_jEC_ZN017$LambdaCommon_1jlE" to i8*), i32 0) + %10 = tail call i32 %7(i8 addrspace(1)* %8, i64 %pgocount78) + %11 = load i64, i64 addrspace(1)* %2, align 8 + %.not = icmp slt i64 %pgocount78, %11 + br i1 %.not, label %arr.if.else, label %arr.if.then + +arr.if.then: ; preds = %while.body + %pgocount.promoted = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint11std$FS$core19arrayInitByFunctionIjEA1_jEC_ZN017$LambdaCommon_1jlE", i64 0, i64 0), align 8 + %12 = add i64 %pgocount.promoted, %pgocount78 + store i64 %12, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint11std$FS$core19arrayInitByFunctionIjEA1_jEC_ZN017$LambdaCommon_1jlE", i64 0, i64 0), align 8 + %pgocount6 = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint11std$FS$core19arrayInitByFunctionIjEA1_jEC_ZN017$LambdaCommon_1jlE", i64 0, i64 2), align 8 + %13 = add i64 %pgocount6, 1 + store i64 %13, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint11std$FS$core19arrayInitByFunctionIjEA1_jEC_ZN017$LambdaCommon_1jlE", i64 0, i64 2), align 8 + %14 = tail call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 56) + tail call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* %14) + tail call void @CJ_MCC_ThrowException(i8 addrspace(1)* %14) + unreachable + +arr.if.else: ; preds = %while.body + %15 = add nuw nsw i64 %pgocount78, 1 + %arr.idx.set.gep = getelementptr inbounds %ArrayLayout.UInt32, %ArrayLayout.UInt32 addrspace(1)* %6, i64 0, i32 1, i64 %pgocount78 + tail call void @llvm.cj.gcwrite.i32.i32(i32 %10, i8 addrspace(1)* %array, i32 addrspace(1)* %arr.idx.set.gep) + %16 = load i64, i64 addrspace(1)* %2, align 8 + %icmpslt = icmp slt i64 %15, %16 + br i1 %icmpslt, label %while.body, label %block.body.block.end_crit_edge + +block.body.block.end_crit_edge: ; preds = %arr.if.else + %pgocount.promoted9 = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint11std$FS$core19arrayInitByFunctionIjEA1_jEC_ZN017$LambdaCommon_1jlE", i64 0, i64 0), align 8 + %17 = add i64 %pgocount.promoted9, %15 + store i64 %17, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @"__profc_numeric_bigint__ZN17numeric$FS$bigint11std$FS$core19arrayInitByFunctionIjEA1_jEC_ZN017$LambdaCommon_1jlE", i64 0, i64 0), align 8 + br label %block.end + +block.end: ; preds = %block.body.block.end_crit_edge, %entry + ret i8 addrspace(1)* %array +} + +declare i8 addrspace(1)* @llvm.cj.heapmalloc.class(i8*, i32) + +declare void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)*) gc "cangjie" + +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) + +declare void @"_ZN11std$FS$core26NegativeArraySizeException4initEv"(i8 addrspace(1)*) gc "cangjie" + +declare void @"T2_R_ZN11std$FS$core5ArrayIjElE.create"(%"T2_R_ZN11std$FS$core5ArrayIjElE"* noalias sret(%"T2_R_ZN11std$FS$core5ArrayIjElE"), i8 addrspace(1)*, %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIjE" addrspace(1)*, i64) gc "cangjie" + +declare void @"_ZN17numeric$FS$bigint11std$FS$core5ArrayIj6copyToER_ZN11std$FS$core5ArrayIjElll"(%Unit.Type* noalias sret(%Unit.Type), i8 addrspace(1)*, %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIjE" addrspace(1)*, i8 addrspace(1)*, %"record._ZN17numeric$FS$bigint11std$FS$core5ArrayIjE" addrspace(1)*, i64, i64, i64) gc "cangjie" + +declare void @"_ZN17numeric$FS$bigint6BigInt7addDMutET2_R_ZN11std$FS$core5ArrayIjElET2_R_ZN11std$FS$core5ArrayIjElEl"(%"T2_R_ZN11std$FS$core5ArrayIjElE"* noalias sret(%"T2_R_ZN11std$FS$core5ArrayIjElE"), i8 addrspace(1)*, %"T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)*, i8 addrspace(1)*, %"T2_R_ZN11std$FS$core5ArrayIjElE" addrspace(1)*, i64) gc "cangjie" + +declare i32 @"__cj_personality_v0$"(...) + +declare i8 addrspace(1)* @llvm.cj.gcmalloc.array(i64, i8*) + +; Function Attrs: argmemonly nounwind +declare void @llvm.cj.gcwrite.struct.p1i8(i8 addrspace(1)*, i8 addrspace(1)* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64) + +; Function Attrs: argmemonly nounwind +declare void @llvm.cj.gcwrite.i32.i32(i32, i8 addrspace(1)* nocapture, i32 addrspace(1)* nocapture) + +; Function Attrs: argmemonly nounwind +declare void @llvm.cj.gcwrite.i64.i64(i64, i8 addrspace(1)* nocapture, i64 addrspace(1)* nocapture) + +; Function Attrs: nounwind +declare void @llvm.cj.memset.p0i8(i8*, i8, i64, i1) + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) + +; Function Attrs: argmemonly nocallback nofree nounwind willreturn +declare void @llvm.memcpy.p0i8.p1i8.i64(i8* noalias nocapture writeonly, i8 addrspace(1)* noalias nocapture readonly, i64, i1 immarg) + +declare i32 @"numeric/bigint$lambda.5"(i8 addrspace(1)*, i64) gc "cangjie" + +declare i8 addrspace(1)* @CJ_MCC_NewArray32Stub(i64, i8*) + +declare i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8*, i32) + +declare void @__llvm_profile_instrument_target(i64, i8*, i32) local_unnamed_addr diff --git a/llvm/test/Transforms/PlaceSafepoints/call-in-loop.ll b/llvm/test/Transforms/PlaceSafepoints/call-in-loop.ll index 2a1892828..0b100a2f0 100644 --- a/llvm/test/Transforms/PlaceSafepoints/call-in-loop.ll +++ b/llvm/test/Transforms/PlaceSafepoints/call-in-loop.ll @@ -15,7 +15,7 @@ entry: loop: ; CHECK-LABEL: loop -; CHECK-NOT: call void @do_safepoint +; CHECK: call void @do_safepoint call void @foo() br label %loop } diff --git a/llvm/test/Verifier/invalid-statepoint.ll b/llvm/test/Verifier/invalid-statepoint.ll index 4e8f9815a..8ef18e4a8 100644 --- a/llvm/test/Verifier/invalid-statepoint.ll +++ b/llvm/test/Verifier/invalid-statepoint.ll @@ -1,4 +1,5 @@ ; RUN: not opt -verify 2>&1 < %s | FileCheck %s +; XFAIL: * declare zeroext i1 @return0i1() diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py index 75a38b4c5..926f5de97 100644 --- a/llvm/test/lit.cfg.py +++ b/llvm/test/lit.cfg.py @@ -84,6 +84,7 @@ def get_asan_rtlib(): llvm_config.use_default_substitutions() +llvm_config.use_lld() # Add site-specific substitutions. config.substitutions.append(('%llvmshlibdir', config.llvm_shlib_dir)) diff --git a/llvm/test/tools/llvm-shlib/typeids.test b/llvm/test/tools/llvm-shlib/typeids.test index fc467f0c1..c582fbb6a 100644 --- a/llvm/test/tools/llvm-shlib/typeids.test +++ b/llvm/test/tools/llvm-shlib/typeids.test @@ -1,3 +1,4 @@ +# XFAIL: * # REQUIRES: x86_64-linux, llvm-dylib # Make sure there's no llvm::Any::TypeId symbol local to the shared library. As diff --git a/llvm/tools/llc/llc.cpp b/llvm/tools/llc/llc.cpp index f084ee2da..c95ef6b22 100644 --- a/llvm/tools/llc/llc.cpp +++ b/llvm/tools/llc/llc.cpp @@ -189,7 +189,14 @@ static cl::opt RemarksFormat( "pass-remarks-format", cl::desc("The format used for serializing remarks (default: YAML)"), cl::value_desc("format"), cl::init("yaml")); - +namespace llvm { +extern cl::opt CJPipeline; +extern cl::opt DisableGCSupport; +extern cl::opt EnableBarrierOnly; +extern cl::opt EnableSafepointOnly; +} +extern cl::opt EnableCalledSaveForStackMap; +extern cl::opt EnableGlobalISelOption; namespace { std::vector &getRunPassNames() { @@ -379,6 +386,12 @@ int main(int argc, char **argv) { cl::ParseCommandLineOptions(argc, argv, "llvm system compiler\n"); + if (DisableGCSupport + EnableBarrierOnly + EnableSafepointOnly > 1) { + errs() << "At most one of the three options (DisableGCSupport, " + "EnableBarrierOnly and EnableSafepointOnly) should be set true.\n"; + return 1; + } + if (TimeTrace) timeTraceProfilerInitialize(TimeTraceGranularity, argv[0]); auto TimeTraceScopeExit = make_scope_exit([]() { @@ -452,6 +465,14 @@ static bool addPass(PassManagerBase &PM, const char *argv0, return false; } +// enable CJ Options when -cangjie-pipeline='true' +static void initCangjieOptions() { + EnableCalledSaveForStackMap = CJPipeline && EnableCalledSaveForStackMap; + if (CJPipeline) { + EnableGlobalISelOption = cl::BOU_FALSE; + } +} + static int compileModule(char **argv, LLVMContext &Context) { // Load the module to be compiled... SMDiagnostic Err; @@ -476,7 +497,11 @@ static int compileModule(char **argv, LLVMContext &Context) { WithColor::error(errs(), argv[0]) << "invalid optimization level.\n"; return 1; case ' ': break; - case '0': OLvl = CodeGenOpt::None; break; + case '0': { + OLvl = CodeGenOpt::None; + EnableCalledSaveForStackMap = false; + break; + } case '1': OLvl = CodeGenOpt::Less; break; case '2': OLvl = CodeGenOpt::Default; break; case '3': OLvl = CodeGenOpt::Aggressive; break; @@ -612,6 +637,8 @@ static int compileModule(char **argv, LLVMContext &Context) { } assert(M && "Should have exited if we didn't have a module!"); + initCangjieOptions(); + if (codegen::getFloatABIForCalls() != FloatABI::Default) Options.FloatABIType = codegen::getFloatABIForCalls(); diff --git a/llvm/tools/llvm-objdump/MachODump.cpp b/llvm/tools/llvm-objdump/MachODump.cpp index cdbecd5ec..54a202cd6 100644 --- a/llvm/tools/llvm-objdump/MachODump.cpp +++ b/llvm/tools/llvm-objdump/MachODump.cpp @@ -8657,6 +8657,12 @@ static void PrintSegmentCommand(uint32_t cmd, uint32_t cmdsize, outs() << " PROTECTED_VERSION_1"; flags &= ~MachO::SG_PROTECTED_VERSION_1; } + if (flags & MachO::SG_READ_ONLY) { + // Apple's otool prints the SG_ prefix for this flag, but not for the + // others. + outs() << " SG_READ_ONLY"; + flags &= ~MachO::SG_READ_ONLY; + } if (flags) outs() << format(" 0x%08" PRIx32, flags) << " (unknown flags)\n"; else diff --git a/llvm/tools/llvm-shlib/CMakeLists.txt b/llvm/tools/llvm-shlib/CMakeLists.txt index 8e2b78f1b..c4f96a6c3 100644 --- a/llvm/tools/llvm-shlib/CMakeLists.txt +++ b/llvm/tools/llvm-shlib/CMakeLists.txt @@ -15,6 +15,40 @@ if(LLVM_BUILD_LLVM_DYLIB) message(FATAL_ERROR "Generating libLLVM is not supported on MSVC") endif() + option(LLVM_SPLIT_LLVM_DYLIB "Split libLLVM into two libraries, libLLVM and libLLVM-Foundation" OFF) + if(LLVM_SPLIT_LLVM_DYLIB) + set(LLVM_FOUNDATION_LIBS + LLVMLinker + LLVMIRReader + LLVMAsmParser + LLVMTransformUtils + LLVMBitWriter + LLVMAnalysis + LLVMProfileData + LLVMDebugInfoDWARF + LLVMDebugInfoMSF + LLVMObject + LLVMTextAPI + LLVMMCParser + LLVMMC + LLVMDebugInfoCodeView + LLVMBitReader + LLVMCore + LLVMRemarks + LLVMBitstreamReader + LLVMBinaryFormat + LLVMSupport + LLVMDemangle) + if(LLVM_LINK_LLVM_DYLIB) + set(INSTALL_WITH_TOOLCHAIN INSTALL_WITH_TOOLCHAIN) + endif() + add_llvm_library(LLVM-Foundation SHARED DISABLE_LLVM_LINK_LLVM_DYLIB SONAME ${INSTALL_WITH_TOOLCHAIN} ${SOURCES}) + if(MINGW) + set(LINK_LLVM_FOUNDATION_LIBS -Wl,--whole-archive ${LLVM_FOUNDATION_LIBS} -Wl,--no-whole-archive) + endif() + target_link_libraries(LLVM-Foundation PRIVATE ${LINK_LLVM_FOUNDATION_LIBS}) + endif() + llvm_map_components_to_libnames(LIB_NAMES ${LLVM_DYLIB_COMPONENTS}) # Exclude libLLVMTableGen for the following reasons: @@ -22,6 +56,10 @@ if(LLVM_BUILD_LLVM_DYLIB) # - it pollutes the global options space. list(REMOVE_ITEM LIB_NAMES "LLVMTableGen") + if(LLVM_SPLIT_LLVM_DYLIB) + list(REMOVE_ITEM LIB_NAMES ${LLVM_FOUNDATION_LIBS}) + endif() + if(LLVM_DYLIB_EXPORTED_SYMBOL_FILE) set(LLVM_EXPORTED_SYMBOL_FILE ${LLVM_DYLIB_EXPORTED_SYMBOL_FILE}) add_custom_target(libLLVMExports DEPENDS ${LLVM_EXPORTED_SYMBOL_FILE}) @@ -33,6 +71,13 @@ if(LLVM_BUILD_LLVM_DYLIB) add_llvm_library(LLVM SHARED DISABLE_LLVM_LINK_LLVM_DYLIB SONAME ${INSTALL_WITH_TOOLCHAIN} ${SOURCES}) list(REMOVE_DUPLICATES LIB_NAMES) + if(LLVM_SPLIT_LLVM_DYLIB) + add_dependencies(LLVM ${LIB_NAMES}) + add_dependencies(LLVM LLVM-Foundation) + # Target names are converted into simple -l options to avoid introducing indirect dependencies of targets. + # Linking indirect dependencies may cause duplicate symbol errors for PE targets. + list(TRANSFORM LIB_NAMES PREPEND "-l") + endif() if(("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") OR (MINGW) OR (HAIKU) OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "GNU") @@ -62,6 +107,10 @@ if(LLVM_BUILD_LLVM_DYLIB) endif() target_link_libraries(LLVM PRIVATE ${LIB_NAMES}) + if(LLVM_SPLIT_LLVM_DYLIB) + target_link_options(LLVM PUBLIC -L${CMAKE_BINARY_DIR}/lib) + target_link_libraries(LLVM PUBLIC LLVM-Foundation-15) + endif() if (APPLE) set_property(TARGET LLVM APPEND_STRING PROPERTY diff --git a/llvm/tools/opt/NewPMDriver.cpp b/llvm/tools/opt/NewPMDriver.cpp index 61d0e121f..8db34100e 100644 --- a/llvm/tools/opt/NewPMDriver.cpp +++ b/llvm/tools/opt/NewPMDriver.cpp @@ -175,6 +175,9 @@ static cl::opt DebugInfoForProfiling( static cl::opt PseudoProbeForProfiling( "new-pm-pseudo-probe-for-profiling", cl::init(false), cl::Hidden, cl::desc("Emit pseudo probes to enable PGO profile generation.")); +static cl::opt OnlyVerifyOut( + "only-verify-out", cl::init(false), cl::Hidden, + cl::desc("Verify only the IR of output.")); /// @}} template @@ -437,7 +440,7 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); ModulePassManager MPM; - if (VK > VK_NoVerifier) + if (VK > VK_NoVerifier && !OnlyVerifyOut) MPM.addPass(VerifierPass()); if (EnableDebugify) MPM.addPass(NewPMDebugifyPass()); diff --git a/llvm/tools/opt/opt.cpp b/llvm/tools/opt/opt.cpp index a02997f82..f6282cc6a 100644 --- a/llvm/tools/opt/opt.cpp +++ b/llvm/tools/opt/opt.cpp @@ -61,7 +61,14 @@ #include using namespace llvm; using namespace opt_tool; - +namespace llvm { +extern cl::opt CJPipeline; +extern cl::opt DisableGCSupport; +extern cl::opt EnableBarrierOnly; +extern cl::opt EnableSafepointOnly; +extern cl::opt EnableCJBarrierSplit; +extern cl::opt EnableCJNewArrayFast; +} static codegen::RegisterCodeGenFlags CFG; // The OptimizationList is automatically populated with registered Passes by the @@ -89,7 +96,8 @@ static cl::opt PassPipeline( static cl::opt PrintPasses("print-passes", cl::desc("Print available passes that can be " "specified in -passes=foo and exit")); - +static cl::opt DisableInline("disable-inlining", + cl::desc("Do not run the inliner pass")); static cl::opt InputFilename(cl::Positional, cl::desc(""), cl::init("-"), cl::value_desc("filename")); @@ -337,7 +345,9 @@ static void AddOptimizationPasses(legacy::PassManagerBase &MPM, Builder.OptLevel = OptLevel; Builder.SizeLevel = SizeLevel; - if (OptLevel > 1) { + if (DisableInline) { + // No inlining pass + } else if (OptLevel > 1) { Builder.Inliner = createFunctionInliningPass(OptLevel, SizeLevel, false); } else { Builder.Inliner = createAlwaysInlinerLegacyPass(); @@ -384,6 +394,11 @@ static TargetMachine* GetTargetMachine(Triple TheTriple, StringRef CPUStr, return nullptr; } + if (CJPipeline && TheTriple.isARM()) { + EnableCJBarrierSplit = false; + EnableCJNewArrayFast = false; + } + return TheTarget->createTargetMachine( TheTriple.getTriple(), codegen::getCPUStr(), codegen::getFeaturesStr(), Options, codegen::getExplicitRelocModel(), @@ -547,14 +562,20 @@ int main(int argc, char **argv) { cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .bc modular optimizer and analysis printer\n"); + if (DisableGCSupport + EnableBarrierOnly + EnableSafepointOnly > 1) { + errs() << "At most one of the three options (DisableGCSupport, " + "EnableBarrierOnly and EnableSafepointOnly) should be set true.\n"; + return 1; + } + LLVMContext Context; // If `-passes=` is specified, use NPM. // If `-enable-new-pm` is specified and there are no codegen passes, use NPM. // e.g. `-enable-new-pm -sroa` will use NPM. // but `-enable-new-pm -codegenprepare` will still revert to legacy PM. - const bool UseNPM = (EnableNewPassManager && !shouldForceLegacyPM()) || - PassPipeline.getNumOccurrences() > 0; + const bool UseNPM = ((EnableNewPassManager && !shouldForceLegacyPM()) || + PassPipeline.getNumOccurrences() > 0); if (!UseNPM && PluginList.size()) { errs() << argv[0] << ": " << PassPlugins.ArgStr diff --git a/llvm/unittests/CodeGen/LowLevelTypeTest.cpp b/llvm/unittests/CodeGen/LowLevelTypeTest.cpp index ef0a43971..15e6d3d8b 100644 --- a/llvm/unittests/CodeGen/LowLevelTypeTest.cpp +++ b/llvm/unittests/CodeGen/LowLevelTypeTest.cpp @@ -245,8 +245,8 @@ TEST(LowLevelTypeTest, Pointer) { DataLayout DL("p64:64:64-p127:512:512:512-p16777215:65528:8"); for (unsigned AS : {0U, 1U, 127U, 0xffffU, - static_cast(maxUIntN(23)), - static_cast(maxUIntN(24))}) { + static_cast(maxUIntN(19)), + static_cast(maxUIntN(20))}) { for (ElementCount EC : {ElementCount::getFixed(2), ElementCount::getFixed(3), ElementCount::getFixed(4), ElementCount::getFixed(256), diff --git a/llvm/unittests/Support/ProcessTest.cpp b/llvm/unittests/Support/ProcessTest.cpp index 4cf6ae87d..b35283f53 100644 --- a/llvm/unittests/Support/ProcessTest.cpp +++ b/llvm/unittests/Support/ProcessTest.cpp @@ -81,7 +81,7 @@ protected: PageSizeTest() : Host(Triple::normalize(sys::getProcessTriple())) {} bool isSupported() const { - // For now just on X86-64 and Aarch64. This can be expanded in the future. + // For now just on X86-64 and AArch64. This can be expanded in the future. return (Host.getArch() == Triple::x86_64 || Host.getArch() == Triple::aarch64) && Host.getOS() == Triple::Linux; cangjie_compiler-1.0.7/third_party/llvm_append_code_925.patch000066400000000000000000002444651510705540100242720ustar00rootroot00000000000000diff --git a/lld/MachO/OutputSegment.cpp b/lld/MachO/OutputSegment.cpp index da1394c08831..f2949e7f3d9a 100644 --- a/lld/MachO/OutputSegment.cpp +++ b/lld/MachO/OutputSegment.cpp @@ -44,6 +44,12 @@ static uint32_t maxProt(StringRef name) { return initProt(name); } +static uint32_t flags(StringRef name) { + // If we ever implement shared cache output support, SG_READ_ONLY should not + // be used for dylibs that can be placed in it. + return name == segment_names::dataConst ? SG_READ_ONLY : 0; +} + size_t OutputSegment::numNonHiddenSections() const { size_t count = 0; for (const OutputSection *osec : sections) @@ -184,6 +190,7 @@ OutputSegment *macho::getOrCreateOutputSegment(StringRef name) { segRef->name = name; segRef->maxProt = maxProt(name); segRef->initProt = initProt(name); + segRef->flags = flags(name); outputSegments.push_back(segRef); return segRef; diff --git a/lld/MachO/OutputSegment.h b/lld/MachO/OutputSegment.h index bff99e28a88f..a5d54b18106f 100644 --- a/lld/MachO/OutputSegment.h +++ b/lld/MachO/OutputSegment.h @@ -56,6 +56,7 @@ public: StringRef name; uint32_t maxProt = 0; uint32_t initProt = 0; + uint32_t flags = 0; uint8_t index; llvm::TinyPtrVector segmentStartSymbols; diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp index ce9672dd0b4f..7effeaf9f461 100644 --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -246,6 +246,7 @@ public: c->vmsize = seg->vmSize; c->filesize = seg->fileSize; c->nsects = seg->numNonHiddenSections(); + c->flags = seg->flags; for (const OutputSection *osec : seg->getSections()) { if (osec->isHidden()) diff --git a/lld/test/MachO/builtin-rename.s b/lld/test/MachO/builtin-rename.s index dc9b7f8f7510..22b2479c0204 100644 --- a/lld/test/MachO/builtin-rename.s +++ b/lld/test/MachO/builtin-rename.s @@ -53,6 +53,23 @@ # YDATA-DAG: __DATA_CONST,__objc_protolist __DATA__objc_protolist # YDATA-DAG: __DATA_CONST,__nl_symbol_ptr __IMPORT__pointers +## Check that the SG_READ_ONLY flag is set on __DATA_CONST. +# RUN: llvm-otool -v -l %t/ydata | \ +# RUN: FileCheck %s --check-prefix=FLAGS + +# FLAGS-LABEL: Load command 2 +# FLAGS-NEXT: cmd LC_SEGMENT_64 +# FLAGS-NEXT: cmdsize +# FLAGS-NEXT: segname __DATA_CONST +# FLAGS-NEXT: vmaddr +# FLAGS-NEXT: vmsize +# FLAGS-NEXT: fileoff +# FLAGS-NEXT: filesize +# FLAGS-NEXT: maxprot rw- +# FLAGS-NEXT: initprot rw- +# FLAGS-NEXT: nsects 13 +# FLAGS-NEXT: flags SG_READ_ONLY + ## LLD doesn't support defining symbols in synthetic sections, so we test them ## via this slightly more awkward route. # RUN: llvm-readobj --section-headers %t/ydata | \ diff --git a/lldb/bindings/interfaces.swig b/lldb/bindings/interfaces.swig index d3c81050ddc0..46aa3be342b3 100644 --- a/lldb/bindings/interfaces.swig +++ b/lldb/bindings/interfaces.swig @@ -2,7 +2,9 @@ #define __extension__ /* Undefine GCC keyword to make Swig happy when processing glibc's stdint.h. */ /* The ISO C99 standard specifies that in C++ implementations limit macros such as INT32_MAX should only be defined if __STDC_LIMIT_MACROS is. */ +#ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS +#endif %include "stdint.i" %include "lldb/lldb-defines.h" diff --git a/lldb/bindings/python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig index bf3de66b91bf..d45431c771ca 100644 --- a/lldb/bindings/python/python-typemaps.swig +++ b/lldb/bindings/python/python-typemaps.swig @@ -435,7 +435,7 @@ template <> bool SetNumberFromPyObject(double &number, PyObject *obj) { %typemap(out) lldb::FileSP { $result = nullptr; - lldb::FileSP &sp = $1; + const lldb::FileSP &sp = $1; if (sp) { PythonFile pyfile = unwrapOrSetPythonException(PythonFile::FromFile(*sp)); if (!pyfile.IsValid()) diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieExpressionParser.cpp index 58520ffb680f..1ef64be5ef2a 100644 --- a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieExpressionParser.cpp @@ -94,16 +94,27 @@ static void SetTripleInfoIfNeed(ArchSpec target_arch, Cangjie::Triple::Info &tar target_triple.os = Cangjie::Triple::OSType::LINUX; } else if (os == llvm::Triple::Win32) { target_triple.os = Cangjie::Triple::OSType::WINDOWS; - } else { + } else if (os == llvm::Triple::Darwin || os == llvm::Triple::MacOSX) { target_triple.os = Cangjie::Triple::OSType::DARWIN; + target_triple.vendor = Cangjie::Triple::Vendor::APPLE; + target_triple.env = Cangjie::Triple::Environment::NOT_AVAILABLE; + return; + } else if (os == llvm::Triple::IOS) { + target_triple.os = Cangjie::Triple::OSType::IOS; + target_triple.env = Cangjie::Triple::Environment::SIMULATOR; + target_triple.vendor = Cangjie::Triple::Vendor::APPLE; + return; + } else { + target_triple.os = Cangjie::Triple::OSType::UNKNOWN; } - - if (target_arch.GetTriple().getEnvironment() == llvm::Triple::GNU) { + auto env = target_arch.GetTriple().getEnvironment(); + if (env == llvm::Triple::GNU) { target_triple.env = Cangjie::Triple::Environment::GNU; + } else if (env == llvm::Triple::Android) { + target_triple.env = Cangjie::Triple::Environment::ANDROID; } else { target_triple.env = Cangjie::Triple::Environment::OHOS; } - target_triple.vendor = Cangjie::Triple::Vendor::UNKNOWN; return; } @@ -117,12 +128,25 @@ bool CangjieExpressionParser::Parse(ExecutionContext &exeCtx, const std::string LLDB_LOGF(log, "CANGJIE_HOME is not set!"); return false; } + auto cangjie_path = getenv("CANGJIE_PATH"); + if (cangjie_path != nullptr) { + std::stringstream sstream(cangjie_path); + std::string tmp_path; + while (std::getline(sstream, tmp_path, ':')) { + LLDB_LOGF(log, "add [%s] to env CANGJIE_PATH\n", tmp_path.c_str()); + invocation->globalOptions.environment.cangjiePaths.emplace_back(tmp_path); + } + } Cangjie::Triple::Info target_triple; ArchSpec target_arch = exeCtx.GetTargetSP()->GetArchitecture(); auto triple = target_arch.GetTriple(); SetTripleInfoIfNeed(target_arch, target_triple); - LLDB_LOGF(log, "using target triple %s %s\n", triple.str().c_str(), - target_arch.GetTriple().getEnvironmentName().data()); + LLDB_LOGF(log, "using target triple %s %s %s %s %s\n", triple.str().c_str(), + triple.getArchName().data(), triple.getVendorName().data(), + triple.getOSName().data(), triple.getEnvironmentName().data()); + if (target_triple.env == Cangjie::Triple::Environment::ANDROID) { + target_triple.apiLevel = "31"; + } invocation->globalOptions.target = target_triple; invocation->globalOptions.environment.cangjieHome = cangjie_home; invocation->globalOptions.srcFiles.push_back(cangjieExprTempFile); diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieIRForTarget.cpp b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieIRForTarget.cpp index f72ce64242d6..313a9af43d10 100644 --- a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieIRForTarget.cpp +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieIRForTarget.cpp @@ -421,13 +421,18 @@ void CangjieIRForTarget::SetPlatformInfo() { m_module->setDataLayout("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"); } } - if (ostype == llvm::Triple::Darwin) { + if (ostype == llvm::Triple::Darwin || ostype == llvm::Triple::MacOSX) { if (archtype == llvm::Triple::ArchType::aarch64) { + m_module->setTargetTriple(arch.GetTriple().str().c_str()); m_module->setDataLayout("e-m:o-i64:64-i128:128-n32:64-S128"); } else if (archtype == llvm::Triple::ArchType::x86_64) { m_module->setDataLayout("e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"); } } + if (ostype == llvm::Triple::IOS && archtype == llvm::Triple::ArchType::aarch64) { + m_module->setTargetTriple(arch.GetTriple().str().c_str()); + m_module->setDataLayout("e-m:o-i64:64-i128:128-n32:64-S128-Fn32"); + } } llvm::StringRef CangjieIRForTarget::FindExtendFunction() diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 9c97fc7315b2..3dff69ad7145 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1284,7 +1284,7 @@ static void LoadCjFormatterSummary(lldb::TypeCategoryImplSP cpp_category_sp) { // Applies only to the enum type without parameters. AddCXXSummary(cpp_category_sp, lldb_private::formatters::EnumSummaryProvider, - "Range summary provider", ConstString("^(.+)?E0\\$(.+)$"), cj_flags, true); + "Enum0 summary provider", ConstString("^(.+)?E0\\$(.+)$"), cj_flags, true); AddCXXSummary(cpp_category_sp, lldb_private::formatters::Enum2SummaryProvider, "Enum2 summary provider", ConstString("(.+)?E2\\$"), cj_flags, true); diff --git a/lldb/source/Plugins/Platform/MacOSX/cangjie_cjdb.py b/lldb/source/Plugins/Platform/MacOSX/cangjie_cjdb.py new file mode 100644 index 000000000000..692ccbb3c4a6 --- /dev/null +++ b/lldb/source/Plugins/Platform/MacOSX/cangjie_cjdb.py @@ -0,0 +1,1782 @@ +# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +# This source file is part of the Cangjie project, licensed under Apache-2.0 +# with Runtime Library Exception. +# +# See https://cangjie-lang.cn/pages/LICENSE for license information. +# + +import lldb +import re +import inspect +import struct +import datetime +import functools +from enum import Enum +from typing import Callable, Union, List, Tuple, Pattern, Any + +UINT64_MAX = 0xFFFFFFFFFFFFFFFF + +def WhenException(action: str = "returnDefaultValue", defaultRetValue = ""): + valid_modes = { + "returnDefaultValue", + "returnNone", + "returnExceptionName", + "raiseAgain" + } + if action not in valid_modes: + action = "returnNone" + + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except Exception as e: + if action == "returnDefaultValue": + return defaultRetValue + elif action == "returnNone": + return None + elif action == "returnExceptionName": + return type(e).__name__ + elif action == "raiseAgain": + raise + else: + return None + return wrapper + return decorator + +class SimpleNamespace: + def __init__(self, **kwargs): + self.kwargs = kwargs + def __getattr__(self, key): + return self.kwargs.get(key, None) + def __repr__(self): + return self.kwargs.__str__() + +def get_func_addr(name): + target = lldb.debugger.GetSelectedTarget() + + for mod in target.module_iter(): + syms = mod.FindSymbols(name, lldb.eSymbolTypeCode) + for i in range(syms.GetSize()): + sym = syms.GetContextAtIndex(i).GetSymbol() + if sym and sym.GetName() == name and sym.GetType() == lldb.eSymbolTypeCode: + return sym.GetStartAddress().GetLoadAddress(target) + + return None + +def get_symbol_load_address(symbol_name): + target = lldb.debugger.GetSelectedTarget() + if not target: + print("Error: No target selected.") + return None + + # Ensure a process is running and not exited + process = target.GetProcess() + if not process or process.GetState() == lldb.eStateInvalid or \ + process.GetState() == lldb.eStateExited or process.GetState() == lldb.eStateDetached: + print("Error: No live process to get load address. Please run or attach to your target.") + return None + + symbol_context_list = target.FindSymbols(symbol_name) + if symbol_context_list.GetSize() > 0: + first_symbol_context = symbol_context_list.GetContextAtIndex(0) + first_symbol = first_symbol_context.GetSymbol() + + if first_symbol and first_symbol.IsValid(): + start_address = first_symbol.GetStartAddress() + if start_address.IsValid(): + # Attempt to get the load address + load_address_value = start_address.GetLoadAddress(target) # Pass 'target' explicitly + return load_address_value + return None + +def get_evaluated_value(expression: str): + """ + Evaluates an expression in the current frame's context and returns the SBValue object. + Returns None if the evaluation fails or preconditions are not met. + + Args: + expression: The string expression to evaluate. + + Returns: + An lldb.SBValue object if successful, otherwise None. + """ + debugger = lldb.debugger + target = debugger.GetSelectedTarget() + if not target: + print("Error: No target selected.") # Removed for simplification, you might add logging + return None + + process = target.GetProcess() + if not process or process.GetState() != lldb.eStateStopped: + print("Error: Process is not stopped. EvaluateExpression requires a stopped process.") + return None + + thread = process.GetSelectedThread() + if not thread: + print("Error: No thread selected.") + return None + + frame = thread.GetSelectedFrame() + if not frame: + print("Error: No frame selected.") + return None + + # Evaluate the expression + # lldb.eDynamicCanRunTarget is generally a good default for most cases. + expression_value = frame.EvaluateExpression(expression, lldb.eDynamicCanRunTarget) + + if expression_value.IsValid() and not expression_value.GetError().Fail(): + return expression_value + else: + print(f"Error evaluating expression '{expression}': {expression_value.GetError().GetCString()}") + return None + +def OutStringSummary(valobj, _): + data = valobj.GetChildMemberWithName("data") + start = valobj.GetChildMemberWithName("start") + length = valobj.GetChildMemberWithName("length") + + error = lldb.SBError() + offsetOfStringContent = 16 # cangjie stores the result string in data[16+start: 16+start+length] + addr = data.Dereference().GetLoadAddress() + start.GetValueAsUnsigned(0) + offsetOfStringContent + size = length.GetValueAsUnsigned(0) + resStr = valobj.GetProcess().ReadCStringFromMemory(addr, size + 1, error) + return resStr + +def infer_is_class_type(sbtype): + num_fields = sbtype.GetNumberOfFields() + offsets = [] + for i in range(num_fields): + member = sbtype.GetFieldAtIndex(i) # SBTypeMember + name = member.GetName() + offset = member.GetOffsetInBytes() + offsets.append(offset) + # for class type, the first 8 bytes are typeinfo pointer + if len(offsets) > 0: + return min(offsets) > 0 + else: + # basic value type, eg. Int/UInt + return False + +def ExtractLastPart(path: str) -> str: + path = path.rstrip("/") + if "/" not in path: + return path + return path.rsplit("/", 1)[-1] + +def RemoveOuterQuotes(s: str) -> str: + if len(s) < 2 or s[0] != '"' or s[-1] != '"': + return s + start, end = 0, len(s) + while start < end and s[start] == '"': + start += 1 + while end > start and s[end - 1] == '"': + end -= 1 + return s[start:end] + +def FunctionSummaryProvider(valobj, _): + raw = valobj.GetNonSyntheticValue() + ptr = raw.GetChildMemberWithName("ptr") + addr = ptr.GetValueAsUnsigned() + target = valobj.GetTarget() + sb_address = target.ResolveLoadAddress(addr) + sb_context = target.ResolveSymbolContextForAddress(sb_address, lldb.eSymbolContextEverything) + mangle_name = "" + if (sb_context.GetFunction().IsValid()): + mangle_name = sb_context.GetFunction().GetName() + if (sb_context.GetSymbol().IsValid()): + mangle_name = sb_context.GetSymbol().GetName() + if mangle_name.startswith("_"): + expr = f'(char *)CJ_MRT_DemangleHandle("{"_" + mangle_name}")' + else: + expr = f'(char *)CJ_MRT_DemangleHandle("{mangle_name}")' + frame = valobj.thread.GetFrameAtIndex(0) + func = frame.EvaluateExpression(expr) + sbs_stream_le = lldb.SBStream() + sbs_stream_m = lldb.SBStream() + sb_context.GetLineEntry().GetDescription(sbs_stream_le) + sb_context.GetModule().GetDescription(sbs_stream_m) + line_entry = ExtractLastPart(sbs_stream_le.GetData()) + module_path = ExtractLastPart(sbs_stream_m.GetData()) + result = func.GetValue() + " (" + module_path + "`" + RemoveOuterQuotes(func.GetSummary()) + " at " + line_entry + ")" + return result + +def StringSummaryProvider(valobj, _): + raw = valobj.GetNonSyntheticValue() + data = raw.GetChildMemberWithName("myData") + start = raw.GetChildMemberWithName("start") + length = raw.GetChildMemberWithName("length") + elements = data.GetChildMemberWithName("elements") + addr = elements.GetLoadAddress() + start.GetValueAsUnsigned(0) + maxlen = length.GetValueAsUnsigned(0) + if not addr or not maxlen: + return "" + error = lldb.SBError() + str = valobj.GetProcess().ReadCStringFromMemory(addr, maxlen + 1, error) + return "\"" + str + "\"" + +def RuneSummaryProvider(valobj, _): + error = lldb.SBError() + data_bytes = valobj.GetProcess().ReadMemory(valobj.GetLoadAddress(), 4, error) + if not error.Success(): + return f"" + rune_value = int.from_bytes(data_bytes, byteorder='little') + control_chars = { + 0: '\\0', + 7: '\\a', + 8: '\\b', + 9: '\\t', + 10: '\\n', + 11: '\\v', + 12: '\\f', + 13: '\\r', + 27: '\\e', + 34: '\\"', + ord('\\'): '\\\\', + ord('\''): '\\\'', + } + if rune_value in control_chars: + return f"'{control_chars[rune_value]}'" + else: + try: + summary = data_bytes.decode('utf-32-le') + except UnicodeDecodeError: + return "" + return "\'" + summary + "\'" + + +def Int8SummaryProvider(valobj,_): + if valobj.GetTypeName() == "Int8": + return valobj.GetValueAsSigned() + return valobj.GetValueAsUnsigned() + +def format_hex(hex_str): + clean_hex = hex_str.lower().replace("0x", "") + try: + num = int(clean_hex, 16) + except ValueError: + raise ValueError("bad hex input") + num_64bit = num & 0xFFFFFFFFFFFFFFFF + return f"0x{num_64bit:016x}" + +def CPointerSummaryProvider(valobj, _): + obj = valobj.GetNonSyntheticValue() + ptr = obj.GetChildMemberWithName("ptr") + addr = ptr.GetValueAsUnsigned() + return format_hex(hex(addr)) + +def CStringSummaryProvider(valobj, _): + obj = valobj.GetNonSyntheticValue() + chars = obj.GetChildMemberWithName("chars") + cstr = chars.GetSummary() + return cstr + +def UnitSummaryProvider(valobj, _): + return "()" + +def RangeSummaryProvider(valobj, _): + raw = valobj.GetNonSyntheticValue() + start = raw.GetChildMemberWithName("start") + end = raw.GetChildMemberWithName("end") + step = raw.GetChildMemberWithName("step") + closed = raw.GetChildMemberWithName("isClosed") + hasStart = raw.GetChildMemberWithName("hasStart") + hasEnd = raw.GetChildMemberWithName("hasEnd") + result = "" + if hasStart.IsValid() and hasStart.GetValue() == "true": + result += str(start).split('= ', 1)[1] + result += ".." + if closed.IsValid() and closed.GetValue() == "true": + result += "=" + if hasEnd.IsValid() and hasEnd.GetValue() == "true": + result += str(end).split('= ', 1)[1] + result += " : " + if step.IsValid(): + result += str(step).split('= ', 1)[1] + return result + + +def OptionSummaryProveder(valobj, _): + raw = valobj.GetNonSyntheticValue() + constructor = raw.GetChildMemberWithName("constructor") + if not constructor.IsValid(): + return "" + if constructor.GetValue() == "None": + return "nullptr" + val = raw.GetChildMemberWithName("val") + if val.IsValid(): + if val.GetType().IsPointerType(): + val = val.Dereference() + if not val.IsValid(): + return "nullptr" + addr = val.GetLoadAddress() + if addr == 0 or addr == UINT64_MAX: + return "nullptr" + if val.GetSummary(): + return val.GetSummary() + if val.GetValue(): + return val.GetValue() + + recursive = raw.GetChildMemberWithName("Recursive-val") + if recursive.IsValid(): + if recursive.GetType().IsPointerType(): + return recursive.GetValue() + return hex(recursive.GetLoadAddress()) + return "" + +def OptionPtrSummaryProveder(valobj, _): + raw = valobj.GetNonSyntheticValue() + if (raw.GetType().IsPointerType()): + val = raw.Dereference() + if not val.IsValid(): + return "nullptr" + addr = val.GetLoadAddress() + if addr == 0 or addr == UINT64_MAX: + return "nullptr" + return str(val).split('= ', 1)[1] + return "" + +def IsValobjDynamicType(valobj): + valtype = valobj.GetType() + if valtype.IsTypedefType(): + valtype = valtype.GetTypedefedType() + if valtype.GetTypeClass() != lldb.eTypeClassClass: + return False + if valtype.GetDisplayTypeName().find("Interface$") != -1: + return True + type_name = GetTypeNameFromMemory(valobj) + if type_name == "": + return False + if type_name != valobj.GetTypeName(): + return True + return False + +def SilentDelete(debugger, result): + temp_result = lldb.SBCommandReturnObject() + interpreter = debugger.GetCommandInterpreter() + + cmd1 = 'type summary delete --category cplusplus "char32_t ?\[[0-9]+\]"' + interpreter.HandleCommand(cmd1, temp_result) + error_output = temp_result.GetError() + + cmd2 = 'type summary delete --category system "^((un)?signed )?char ?\[[0-9]+\]$"' + interpreter.HandleCommand(cmd2, temp_result) + error_output += temp_result.GetError() + + patterns = [ + r"error: no custom formatter for char32_t ?\[[0-9]+\]\.?\n", + r"error: no custom formatter for ((un)?signed )?char ?\[[0-9]+\]\.?\n" + ] + if all(re.search(p, error_output) for p in patterns): + result.SetError("") + else: + result.SetError(error_output) + +class CangjieVArraySyntheticProvider: + def __init__(self, valobj, _): + target = valobj.GetTarget() + debugger = target.GetDebugger() + SilentDelete(debugger, lldb.SBCommandReturnObject()) + self.valobj = valobj + + def num_children(self): + return self.valobj.GetNumChildren() + + def get_child_at_index(self, index): + return self.valobj.GetChildAtIndex(index) + + def get_child_index(self, name): + pass + + def has_children(self): + return True + +class CangjieArraySyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + self.update() + + def update(self): + self.rawptr = self.valobj.GetChildMemberWithName("rawptr") + if (self.rawptr.IsValid()): + self.elements = self.rawptr.GetChildMemberWithName("elements") + self.start = self.valobj.GetChildMemberWithName("start") + self.len = self.valobj.GetChildMemberWithName("len") + + def num_children(self): + if (self.len.IsValid()): + return self.len.GetValueAsUnsigned() + return 0 + + def get_child_at_index(self, index): + if (self.elements.IsValid() and self.start.IsValid()): + element_size = self.elements.GetType().GetByteSize() + if not self.elements.GetAddress().IsValid(): + return None + start_address = self.elements.GetLoadAddress() + addr = start_address + (self.start.GetValueAsUnsigned(0) + index) * element_size + value = self.valobj.CreateValueFromAddress(f"[{index}]", addr, self.elements.GetType()) + if value.GetType().IsPointerType(): + if value.GetValueAsUnsigned() == 0: + value = value.CreateValueFromAddress(f"[{index}]", addr, value.GetType().GetPointeeType()) + value.SetSyntheticChildrenGenerated(True) + else: + value = value.Dereference() + if value and value.IsValid() and value.GetName().startswith("*"): + name = value.GetName()[1:] + value = self.valobj.CreateValueFromAddress(name, value.GetLoadAddress(), value.GetType()) + if (value.IsValid()): + if IsValobjDynamicType(value): + anytype = self.valobj.GetTarget().FindFirstType("Interface$Any") + value = self.valobj.CreateValueFromAddress(value.GetName(), value.GetLoadAddress(), anytype) + return value + return None + + def get_child_index(self, name): + pass + + def has_children(self): + return True + + def get_type_name(self): + displayname = DeletePrefixOfTypename(self.valobj.GetDisplayTypeName(), "const ") + return displayname.replace("::", ".") + +class CangjieArrayListSyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + self.update() + + def update(self): + self.data = CangjieArraySyntheticProvider(self.valobj.GetChildMemberWithName("myData"), None) + self.size = self.valobj.GetChildMemberWithName("mySize").GetValueAsUnsigned(0) + + def get_child_at_index(self, index): + value = self.data.get_child_at_index(index) + return value + + def num_children(self): + return 0 if self.size > 0xFFFFFFFF else self.size + + def get_child_index(self, name): + pass + + def has_children(self): + return True + + def get_type_name(self): + displayname = DeletePrefixOfTypename(self.valobj.GetDisplayTypeName(), "const ") + return displayname.replace("::", ".") + +class CangjieTupleSyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + self.update() + + def update(self): + self.size = self.valobj.GetNumChildren() + + def get_child_at_index(self, index): + value = self.valobj.GetChildAtIndex(index) + addr = value.GetLoadAddress() + value = self.valobj.CreateValueFromAddress(f"[{index}]", addr, value.GetType()) + if value.GetType().IsPointerType(): + if value.GetValueAsUnsigned() == 0: + value = value.CreateValueFromAddress(f"[{index}]", addr, value.GetType().GetPointeeType()) + value.SetSyntheticChildrenGenerated(True) + else: + value = value.Dereference() + if hasattr(value, 'IsValid') and value.IsValid() and value.GetName().startswith("*"): + name = value.GetName()[1:] + return self.valobj.CreateValueFromAddress(name, value.GetLoadAddress(), value.GetType()) + return value + + def num_children(self): + return self.size + + def get_child_index(self, name): + pass + + def has_children(self): + return True + + def get_type_name(self): + return None + +class CangjieSummaryDisplayTypeSyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + pass + + def update(self): + pass + + def get_child_at_index(self, index): + pass + + def num_children(self): + return 0 + + def get_child_index(self, name): + pass + + def has_children(self): + return True + + def get_type_name(self): + return self.valobj.GetTypeName().replace("const ", "").replace("::", ".") + +class CanjieHashMapEntrySyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + + @WhenException() + def get_child_at_index(self, index): + value = self.valobj.GetChildAtIndex(index + 2) + if value.IsValid() and value.GetType().IsPointerType(): + value = value.Dereference() + return value + + @WhenException() + def num_children(self): + return 2 + + def get_child_index(self, name): + pass + + @WhenException() + def has_children(self): + return True + +class CangjieHashMapSyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + self.IsHashSet = False + self.numEntries = 0 + self.entryIndexes = [] + self.update() + + @WhenException() + def update(self): + appendIndex = self.valobj.GetChildMemberWithName("appendIndex") + freeSize = self.valobj.GetChildMemberWithName("freeSize") + self.numEntries = appendIndex.GetValueAsSigned() - freeSize.GetValueAsSigned() + if self.numEntries < 0: self.numEntries = 0 + + entries = self.valobj.GetChildMemberWithName("entries") + if entries.IsValid(): + self.data = CangjieArraySyntheticProvider(entries, None) + else: + self.data = None + + @WhenException() + def get_child_at_index(self, index): + if not self.data: + return None + + if index >= self.numEntries: + return None + + startIndex = 0 if len(self.entryIndexes) == 0 else self.entryIndexes[-1] + 1 + entriesCapacity = self.data.num_children() + while startIndex <= entriesCapacity: + siEntry = self.data.get_child_at_index(startIndex) + siEntry = siEntry.GetNonSyntheticValue() + hashCode = siEntry.GetChildMemberWithName("hash").GetValueAsSigned() + if not (hashCode < 0): + self.entryIndexes.append(startIndex) + if len(self.entryIndexes) > index: break + startIndex += 1 + + if index >= len(self.entryIndexes): + return None + + entryIndex = self.entryIndexes[index] + value = self.data.get_child_at_index(entryIndex) + if not value.IsValid(): + return None + if self.IsHashSet: + value = value.GetChildAtIndex(0) + return value.CreateValueFromAddress(f"[{index}]", value.GetLoadAddress(), value.GetType()) + key = value.GetNonSyntheticValue().GetChildMemberWithName("key") + val = value.GetNonSyntheticValue().GetChildMemberWithName("value") + if val.GetType().IsPointerType(): val = val.Dereference() + + keysummary = self.get_oneword_summary(key) + valsummary = self.get_oneword_summary(val) + if not keysummary: + name = f"[{index}]" + elif not valsummary: + name = f"[{keysummary}]" + else: + name = f"[{keysummary} -> {valsummary}]" + value = value.CreateValueFromAddress(name, value.GetLoadAddress(), value.GetType()) + return value + + @WhenException() + def num_children(self): + return self.numEntries + + @WhenException() + def get_child_index(self, name): + pass + + def has_children(self): + return True + + def get_oneword_summary(self, obj): + if obj.TypeIsPointerType(): + obj = obj.Dereference() + if obj_summary := obj.GetSummary(): + return obj_summary + if obj_value := obj.GetValue(): + return obj_value + return None + + def SetIsHashSet(self, IsHashSet = True): + self.IsHashSet = IsHashSet + + def IsHashSet(self): + return self.IsHashSet + +class CangjieHashSetSyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + self.update() + + @WhenException() + def update(self): + map = self.valobj.GetChildMemberWithName("map") + self.data = CangjieHashMapSyntheticProvider(map, None) + self.data.SetIsHashSet(True) + + @WhenException() + def get_child_at_index(self, index): + return self.data.get_child_at_index(index) + + @WhenException() + def num_children(self): + return self.data.num_children() + + def get_child_index(self, name): + pass + + def has_children(self): + return True + + def get_type_name(self): + return None + +def DeletePrefixOfTypename(type_name, prefix): + if type_name is None: + return "" + while type_name.find(prefix) != -1: + index = type_name.find(prefix) + type_name = type_name[:index] + type_name[index + len(prefix):] + return type_name + +class CangjieSyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + self.update() + + def update(self): + valobj = self.valobj + if not valobj.IsValid() or not IsValobjDynamicType(valobj): + return + # the valobj is dynamic type + typename = GetTypeNameFromMemory(valobj) + dynamictype = valobj.GetTarget().FindFirstType(typename) + if not dynamictype.IsValid(): + return + # the dynamic type is not class + if dynamictype.GetTypeClass() != lldb.eTypeClassClass: + self.valobj = valobj.CreateValueFromAddress( + valobj.GetName(), valobj.GetLoadAddress() + 8, dynamictype) + return + + valtype = valobj.GetType() + if valtype.IsTypedefType(): + valtype = valtype.GetTypedefedType() + valTypeName = valtype.GetDisplayTypeName().replace("::", ".") + + # If the name of valobj is typename, then the valobj is an inherited class, no need to use dynamictype. + if valobj.GetName() != valTypeName: + self.valobj = valobj.CreateValueFromAddress( + valobj.GetName(), valobj.GetLoadAddress(), dynamictype) + + def num_children(self): + return self.valobj.GetNumChildren() + + def get_child_index(self, name): + pass + + def get_child_at_index(self, index): + value = self.valobj.GetChildAtIndex(index) + if value.GetType().IsPointerType() and value.GetValueAsUnsigned() != 0: + value = value.Dereference() + if hasattr(value, 'IsValid') and value.IsValid(): + if value.GetName().startswith("*"): + name = value.GetName()[1:] + return self.valobj.CreateValueFromAddress(name, value.GetLoadAddress(), value.GetType()) + else: + return value + name = value.GetName() + if name and name.find("::") != -1: + name = name.replace("::", ".") + value = self.valobj.CreateValueFromAddress(name, value.GetLoadAddress(), value.GetType()) + return value + + def has_children(self): + return True + + def get_type_name(self): + # change typename from "default::E0$E0" to "default.E0" + displayname = DeletePrefixOfTypename(self.valobj.GetDisplayTypeName(), "E0$") + displayname = DeletePrefixOfTypename(displayname, "const ") + return displayname.replace("::", ".") + +# enum E0 { +# A | B | C +# } +def CangjieEnum0SummaryProvider(valobj, _): + if valobj.GetType().IsPointerType(): + return "" + raw = valobj.GetNonSyntheticValue() + ctor = raw.GetChildMemberWithName("constructor") + enum_members = ctor.GetType().GetEnumMembers() + enum_size = enum_members.GetSize() + if enum_size == 0: + return None + ctor_id = ctor.GetValueAsUnsigned() + if ctor_id >= enum_size: + ctor_id = 0 + val = enum_members[ctor_id] + return val.GetName() + +# enum E1 { +# A(Int64) | B(Int64, Class) | C +# } +class CangjieEnum1SyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + self.update() + + def update(self): + for i in range(self.valobj.GetNumChildren()): + ctor = self.valobj.GetChildAtIndex(i) + if (ctor.IsValid()): + ctor_type = ctor.GetChildMemberWithName("constructor") + ctor_id = ctor_type.GetValueAsUnsigned() + self.data = self.valobj.GetChildAtIndex(ctor_id) + break + if hasattr(self, 'data') and self.data.IsValid(): + ctor = self.data.GetChildMemberWithName("constructor") + if ctor != None: + value = ctor.GetValue() + if hasattr(value, 'IsValid') and value.IsValid(): + self.data = self.valobj.CreateValueFromAddress("arg_1", value.GetLoadAddress(), value.GetType()) + + def num_children(self): + if hasattr(self, 'data') and self.data.IsValid(): + return self.data.GetNumChildren() + else: + return 0 + + def get_child_at_index(self, index): + if hasattr(self, 'data') and self.data.IsValid(): + value = self.data.GetChildAtIndex(index) + if value.GetType().IsPointerType() and value.GetValueAsUnsigned() != 0: + value = value.Dereference() + if hasattr(value, 'IsValid') and value.IsValid(): + if value.GetName().startswith("*"): + name = value.GetName()[1:] + return self.valobj.CreateValueFromAddress(name, value.GetLoadAddress(), value.GetType()) + else: + return value + return None + + def get_child_index(self, name): + pass + + def has_children(self): + return True + + def get_type_name(self): + type_name = self.valobj.GetTypeName() + displayname = DeletePrefixOfTypename(type_name, "E1$") + displayname = displayname.replace("::", ".") + return displayname + +# enum E2 { +# A | B(T) +# } +# var a = E2.A +def CangjieEnum2SummaryProvider(valobj, _): + if valobj.GetType().IsPointerType(): + deref_val_type = valobj.GetType().GetPointeeType() + if deref_val_type.IsPointerType(): + return "" + raw = valobj.GetNonSyntheticValue() + if not raw.GetFrame().FindVariable(raw.GetName()): + return "" + # Non-reference type enum + enum_ctor = raw.GetChildMemberWithName("constructor") + if enum_ctor.IsValid(): + return "" + # Reference type enum + enum_ctor = raw.GetChildMemberWithName("constructor_R") + enum_ctor_type = enum_ctor.GetType() + enum_members = enum_ctor_type.GetEnumMembers() + if enum_members.GetSize() != 2: + return "" + ptr = raw.GetValueAsUnsigned() + if ptr == 0 or ptr == 1: + for member in enum_members: + enum_name = member.GetName() + if hasattr(enum_name, 'startswith') and enum_name.startswith("N$_"): + enum_name = enum_name[3:] + return enum_name + else: + deref = raw.Dereference() + return str(deref).split('= ', 1)[1] + + raw = valobj.GetNonSyntheticValue() + ctor = raw.GetChildMemberWithName("constructor") + if not ctor.IsValid(): + if raw.IsSyntheticChildrenGenerated(): + enum_ctor = valobj.GetType().GetFieldAtIndex(0) + enum_ctor_type = enum_ctor.GetType() + enum_members = enum_ctor_type.GetEnumMembers() + for member in enum_members: + enum_name = member.GetName() + if hasattr(enum_name, 'startswith') and enum_name.startswith("N$_"): + enum_name = enum_name[3:] + return enum_name + return "" + value = ctor.GetValue() + if hasattr(value, 'startswith') and value.startswith("N$_"): + value = value[3:] + return value + else: + return "" + +# case1: +# enum E2 { +# B(T) | A +# } +# var a = E2.B(5) +# case2: +# class A {} +# enum E2 { +# B | C(T) +# } +# var b = E2.C(A()) +class CangjieEnum2SyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + self.update() + + def update(self): + if self.valobj.GetType().IsPointerType(): + deref_val_type = self.valobj.GetType().GetPointeeType() + if deref_val_type.IsPointerType(): + return + for i in range(deref_val_type.GetNumberOfFields()): + field = deref_val_type.GetFieldAtIndex(i) + field_name = field.GetName() + if field_name and "constructor" in field_name: + self.ctor_type = field.GetType() + + if not (hasattr(self, 'ctor_type') and self.ctor_type.IsValid()): + return + # top-level object and nullptr + obj = self.valobj.GetFrame().FindVariable(self.valobj.GetName()) + if obj and self.valobj.GetValueAsUnsigned() == 0: + ret = self.valobj.SetValueFromCString("1") + return + + # Non-reference type: "constructor", Reference type: "constructor_R". + enum_type = self.valobj.GetType() + enum_ctor = enum_type.GetFieldAtIndex(0) + if not enum_ctor.IsValid(): + return + enum_ctor_type = enum_ctor.GetType() + enum_id = 0 + for member in enum_ctor_type.GetEnumMembers(): + enum_name = member.GetName() + if hasattr(enum_name, 'startswith') and not enum_name.startswith("N$_"): + break + enum_id = enum_id + 1 + + enum_data = lldb.SBData() + err = lldb.SBError() + target = self.valobj.GetTarget() + # Use `struct.pack` to pack integer values into a byte stream, '" + + typeInfoAddr = get_symbol_load_address(typeInfoName) + if typeInfoAddr is None: + return f"" + + invokeToString = f""" +struct string_t {{ char *data; int start, length; }} result = {{ 0 }}; +((void (*)(void *, void *, void *))0x{toStringAddr:x})( +&result, +(void *)0x{valueAddress:x}, +(void *)0x{typeInfoAddr:x}); +result +""".replace('\n', ' ').strip() + outValue = get_evaluated_value(invokeToString) + if not outValue or not outValue.IsValid(): + return None + return OutStringSummary(outValue, idict) + +@WhenException() +def TimeZoneSummaryProvider(valobj, internal_dict): + localTimesObj = valobj.GetChildMemberWithName("localTimes") + if not localTimesObj.IsValid(): + return None + + zoneIdObj = valobj.GetChildMemberWithName("zoneId") + if not zoneIdObj.IsValid(): + return None + zoneId = StringSummaryProvider(zoneIdObj, internal_dict) + + return f"TimeZone({zoneId})" + +@WhenException() +class CangjieDateTimeFormatter: + class Month(Enum): + January = 1 + February = 2 + March = 3 + April = 4 + May = 5 + June = 6 + July = 7 + August = 8 + September = 9 + October = 10 + November = 11 + December = 12 + + @classmethod + def of(cls, idx: int): + return cls(idx) + + def __init__(self, epoch, offset, nanosecs = 0): + self.epoch = epoch + self.offset = offset + self.nanosecs = nanosecs + + START_AD_YEAR = 1 + MIN_YEAR = -999999999 + MAX_YEAR = 999999999 + SECS_PER_DAY = 86400 + DAYS_PER_400YEARS = 146097 + DAYS_PER_100YEARS = 36524 + DAYS_PER_4YEARS = 1461 + DAYS_OF_NORMAL_YEAR = 365 + SECS_PER_HOUR = 3600 + SECS_PER_MINUTE = 60 + DAYS_OF_MAX_TO_AD1 = (MAX_YEAR - 1) * 365 + (MAX_YEAR - 1) // 4 - (MAX_YEAR - 1) // 100 + (MAX_YEAR - 1) // 400 + 365 + DAYS_OF_MIN_TO_AD1 = -(DAYS_OF_MAX_TO_AD1 + 366) + SECS_OF_MIN_TO_AD1 = DAYS_OF_MIN_TO_AD1 * SECS_PER_DAY + DAYS_BEFORE = [ + 0, # January + 31, # February + 31 + 28, # March + 31 + 28 + 31, + 31 + 28 + 31 + 30, + 31 + 28 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, + 365 + ] + + @property + def year(self) -> int: + return self.getYearAndSecond()[0] + + @property + def month(self): + return self.getMonthAndDay()[0] + + @property + def dayOfMonth(self) -> int: + return self.getMonthAndDay()[1] + + @property + def hour(self) -> int: + second = self.getYearAndSecond()[1] + return (second % self.SECS_PER_DAY) // self.SECS_PER_HOUR + + @property + def minute(self) -> int: + second = self.getYearAndSecond()[1] + return (second % self.SECS_PER_HOUR) // self.SECS_PER_MINUTE + + @property + def second(self) -> int: + second = self.getYearAndSecond()[1] + return second % self.SECS_PER_MINUTE + + @property + def nanosecond(self) -> int: + return self.nanosecs + + def getMonthAndDay(self) -> Tuple[Month, int]: + year_after, second_after = self.getYearAndSecond() + return self.getDate(year_after, second_after) + + def getDate(self, year: int, sec: int) -> Tuple[Month, int]: + days = sec // self.SECS_PER_DAY + if self.isLeapYear(year): + if days == self.DAYS_BEFORE[2]: + return (Month.February, 29) + if days > self.DAYS_BEFORE[2]: + days -= 1 + + idx = 0 + day = 0 + while idx < len(self.DAYS_BEFORE): + if days == self.DAYS_BEFORE[idx]: + idx += 1 + day = 1 + break + + if days < self.DAYS_BEFORE[idx]: + day = days - self.DAYS_BEFORE[idx-1] + 1 + break + + idx += 1 + + month = CangjieDateTimeFormatter.Month.of(idx) + return (month, day) + + def isLeapYear(self, year: int) -> bool: + return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) + + def getYearAndSecond(self): + offset = self.getOffset() + year, sec = self.toYearAndSecond(self.epoch + offset) + return (year, int(sec)) + + def toYearAndSecond(self, sec) -> Tuple[int, int]: + year = self.START_AD_YEAR + seconds = sec + + if seconds < 0: + seconds -= self.SECS_OF_MIN_TO_AD1 + year = self.MIN_YEAR + + days = seconds // self.SECS_PER_DAY + rest_sec = seconds % self.SECS_PER_DAY + + times = days // self.DAYS_PER_400YEARS + year += 400 * times + days %= self.DAYS_PER_400YEARS + + times = days // self.DAYS_PER_100YEARS + times -= times >> 2 + year += 100 * times + days -= self.DAYS_PER_100YEARS * times + + times = days // self.DAYS_PER_4YEARS + year += 4 * times + days %= self.DAYS_PER_4YEARS + + times = days // self.DAYS_OF_NORMAL_YEAR + times -= times >> 2 + year += times + days -= self.DAYS_OF_NORMAL_YEAR * times + + sec_in_year = rest_sec + days * self.SECS_PER_DAY + return (year, sec_in_year) + + def getOffset(self): + return self.offset + + def addZeroPrefix(self, value: int, length: int): + sign = '-' if value < 0 else '' + num_str = str(abs(value)) + num_str = num_str.rjust(length, '0') + return sign + num_str + + def toString(self): + res = self.addZeroPrefix(self.year, 4) + res += '-' + res += self.addZeroPrefix(self.month.value, 2) + res += '-' + res += self.addZeroPrefix(self.dayOfMonth, 2) + res += 'T' + res += self.addZeroPrefix(self.hour, 2) + res += ':' + res += self.addZeroPrefix(self.minute, 2) + res += ':' + res += self.addZeroPrefix(self.second, 2) + if (self.nanosecond != 0): + nano = self.nanosecond + times = 0 + while nano % 10 == 0: + times += 1 + nano //= 10 + res += '.' + res += self.addZeroPrefix(nano, 9 - times) + res += self.toOffsetString(4) + return res + + def toOffsetString(self, length): + offset = self.getOffset() + if offset < 0: + sign = "-" + off = -offset + else: + sign = "+" + off = offset + + hour = off // 3600 + minute = (off % 3600) // 60 + second = off % 60 + + if length > 3 and offset == 0: + return "Z" + elif (length == 3) or (length > 3 and second != 0): + return f"{sign}{hour:02d}:{minute:02d}:{second:02d}" + elif (length == 2) or (length > 3): + return f"{sign}{hour:02d}:{minute:02d}" + else: + return f"{sign}{hour:02d}" + +@WhenException() +def DateTimeSummaryProvider(valobj, internal_dict): + # Refer the cangjie's library implementation and RFC8536 + # Retrieved layout of std.time.DateTime from lldb + # (std.time::DateTime) a = { + # # from cangjie's source code: + # # The d records the duration since A.D.1. + # d = (sec = 63422352000, ns = 0) + # tz = 0x00007fffd75b9fb8 + # } + # + # (std.time::TimeZone) b = { + # # note, due to the unknown python version, zoneId cannot be + # # utilized below python3.8 as zoneinfo is not included in the std libs + # zoneId = "" # a Cangjie String object, eg "UTC" + # localTimes = Array # transition offset + # transTimes = Array # transition time + # } + dObj = valobj.GetNonSyntheticValue().GetChildMemberWithName("d") + if not dObj.IsValid(): + return None + + secObj = dObj.GetChildMemberWithName("sec") + if not secObj.IsValid(): + return None + + nsecObj = dObj.GetChildMemberWithName("ns") + if not nsecObj.IsValid(): + return None + nanosecs = nsecObj.GetValueAsUnsigned() + + tzObj = valobj.GetNonSyntheticValue().GetChildMemberWithName("tz") + if not tzObj.IsValid(): + ad1Epoch = secObj.GetValueAsUnsigned() + ad1Date = datetime.datetime(1, 1, 1) + datetime.timedelta(seconds = ad1Epoch) + return ad1Date.strftime("%Y-%m-%dT%H:%M:%SZ") + + zoneIdObj = tzObj.GetChildMemberWithName("zoneId") + if not zoneIdObj.IsValid(): + return None + zoneId = StringSummaryProvider(zoneIdObj, internal_dict) + + localTimesObj = tzObj.GetChildMemberWithName("localTimes") + + localTimesSynthetic = CangjieArraySyntheticProvider(localTimesObj, internal_dict) + localTimes = [] + for i in range(localTimesSynthetic.num_children()): + ithObj = localTimesSynthetic.get_child_at_index(i) + ithObj = ithObj.GetNonSyntheticValue() + localTimes.append(SimpleNamespace( + des = StringSummaryProvider(ithObj.GetChildMemberWithName("des"), internal_dict), + offset = ithObj.GetChildMemberWithName("offset").GetValueAsSigned(), + isDST = bool(ithObj.GetChildMemberWithName("isDST").GetValueAsUnsigned()), + )) + + transTimes_obj = tzObj.GetChildMemberWithName("transTimes") + transTimes_synthetic = CangjieArraySyntheticProvider(transTimes_obj, internal_dict) + transTimes = [] + for i in range(transTimes_synthetic.num_children()): + ithObj = transTimes_synthetic.get_child_at_index(i) + ithObj = ithObj.GetNonSyntheticValue() + transTimes.append(SimpleNamespace( + trans = ithObj.GetChildMemberWithName("trans").GetValueAsSigned(), + index = ithObj.GetChildMemberWithName("index").GetValueAsUnsigned(), + )) + + ad1Epoch = secObj.GetValueAsSigned() + utcEpoch = ad1Epoch - (datetime.datetime(1970, 1, 1) - datetime.datetime(1, 1, 1)).total_seconds() + + # no transition in the history or the epoch is early than the first transition + if len(transTimes) == 0 or utcEpoch < transTimes[0].trans: + # needs to infer, rules from the cj's library + + def InferRuleEarlierThanFirstTransition(): + # rule 1, if 0-th localtime is unused, it's the rule used for stage earlier than first transition + unused = not any([t.index == 0 for t in transTimes]) + if unused: + return 0 + + # rule 2, + # + # Absent the above, if there are transition times and the first transition is to a daylight time, + # find the standard type less than and closest to the zone of the first transition. + if len(transTimes) > 0 and localTimes[transTimes[0].index].isDST: + index = transTimes[0].index - 1 + for zoneIndex in range(index, -1, -1): + if not localTimes[zoneIndex].isDST: + return zoneIndex + + # If no result yet, find the first standard type. + for zoneIndex in range(0, localTimes.size): + if not localTimes[zoneIndex].isDST: + return zoneIndex + + return 0 + + if len(localTimes) > 0: + ruleIndex = InferRuleEarlierThanFirstTransition() + zoneOffset = localTimes[ruleIndex].offset + else: + zoneOffset = 0 + else: + # binary search in the transTimes, find first stage later than utcEpoch + low = 0 + high = len(transTimes) + while (high - low > 1): + mid = int((low + high) / 2) + curTime = transTimes[mid].trans + if (utcEpoch < curTime): + high = mid + else: + low = mid + ruleIndex = transTimes[low].index + zoneOffset = localTimes[ruleIndex].offset + + # note: datetime does not support datetime > 99999, + # we have to manually convert here + return CangjieDateTimeFormatter(ad1Epoch, zoneOffset, nanosecs).toString() + +class CangjieOptionSyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj.GetNonSyntheticValue() + self.hasvalue = False + self.children = False + self.some: lldb.SBValue = None + self.update() + + def update(self): + if self.valobj.GetType().IsPointerType(): + self.valobj = self.valobj.Dereference().GetNonSyntheticValue() + value = self.valobj.GetChildMemberWithName("val") + if not value.IsValid(): + value = self.valobj.GetChildMemberWithName("Recursive-val") + if not value.IsValid(): + return False + value.SetPreferSyntheticValue(True) + self.some = value + self.hasvalue = True + self.children = self.some.GetNumChildren() > 0 + + def get_child_at_index(self, index): + return self.some.GetChildAtIndex(index) + + def num_children(self): + if self.some.IsValid() and self.some.GetError().Success(): + return self.some.GetNumChildren() + return 0 + + def get_child_index(self, name): + pass + + def has_children(self): + if self.children == False or self.some == None or self.hasvalue == False: + return False + return True + + def get_type_name(self): + if self.valobj.IsValid(): + return self.valobj.GetTypeName().rstrip(" *") + return "" + +class CommandSet: + def __init__(self, debugger, unused): + self.dbg = debugger + + def __call__(self, debugger, command, exe_ctx, result): + cmd = command.strip() + if '=' not in cmd: + result.SetError("Invalid command options. Use 'help set' to view the command format.") + return + parts = cmd.split('=', 1) + if len(parts) != 2: + result.SetError("Invalid command options. Use 'help set' to view the command format.") + return + expr = parts[0].strip() + new_value = parts[1].strip() + if not expr or not new_value: + result.SetError("Invalid command. Variable name and value cannot be empty") + return + target = debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetSelectedThread() + frame = thread.GetSelectedFrame() + parts = re.sub(r'\[(\d+)\]', r'.\1', expr) + variables = parts.split('.') + root = variables[0] + members = variables[1:] + var = frame.FindVariable(root) + if not var.IsValid(): + var = target.FindFirstGlobalVariable(root) + if not var.IsValid(): + result.SetError(f"Invalid command. Can not find variable '{root}'") + return + for member in members: + if member.isdigit(): + index = int(member) + var = var.GetChildAtIndex(index, 0, True) + else: + var = var.GetNonSyntheticValue() + var = var.GetChildMemberWithName(member) + if not var.IsValid(): + result.SetError(f"Invalid command. Can not find member '{member}'") + return + error = lldb.SBError() + var.SetValueFromCString(new_value, error) + if error.Fail(): + result.SetError(error.GetCString()) + return + result.AppendMessage(f"{expr} = {new_value}") + return + + def get_short_help(self): + return "Modify the value of a variable of a basic data type.\n" \ + "The command format is 'set variable = new_value'\n" + +class CommandCJBackTrace : + def __init__(self, debugger, unused): + self.dbg = debugger + + def __call__(self, debugger, command, exe_ctx, result): + try: + frame_limit = int(command.strip()) if command.strip() else None + except ValueError: + result.SetError("Invalid command options.") + return + + target = debugger.GetSelectedTarget() + process = target.GetProcess() + if not process or not process.IsValid(): + result.SetError("Command requires a current process.") + return + thread = process.GetSelectedThread() + result.AppendMessage( + f'* thread #{thread.GetThreadID()}, name = {thread.GetName()}, ' + f'stop reason = {thread.GetStopDescription(100)}') + if frame_limit == None: + frame_limit = thread.GetNumFrames() + status = "" + str = lldb.SBStream() + for num in range(0, frame_limit): + frame = thread.GetFrameAtIndex(num) + if num == thread.GetSelectedFrame().GetFrameID(): + status += " * " + else : + status += " " + str.Clear() + frame.GetDescription(str) + mangled = frame.GetFunctionName().split("(", 1)[0] + if mangled.startswith("_"): + expr = f'(char *)CJ_MRT_DemangleHandle("{"_" + mangled}")' + else: + expr = f'(char *)CJ_MRT_DemangleHandle("{mangled}")' + value = thread.GetFrameAtIndex(0).EvaluateExpression(expr) + if not value.GetSummary(): + status += str.GetData() + continue + demangled = value.GetSummary().strip("\"").split("(", 1)[0] + des = str.GetData().replace(mangled, demangled) + status += des + result.AppendMessage(status) + return + + def get_short_help(self): + return "Show the current thread's call stack. Any numeric argument displays at most\n" \ + "that many frames. Display all frames by default.\n" \ + "bt [ | all]" + +class CommandCJThreadBackTrace: + def __init__(self, debugger, unused): + pass + + def __call__(self, debugger, command, exe_ctx, result): + frame = exe_ctx.frame + process = exe_ctx.process + if not frame.IsValid(): + result.SetError("Command requires a current frame.") + return + options = lldb.SBExpressionOptions() + options.SetSuppressPersistentResult(False) + options.SetIgnoreBreakpoints(True) + count = "unsigned long long cjdb_cjthread_count = (unsigned long long)CJ_MRT_GetCJThreadNumberUnsafe();cjdb_cjthread_count" + result_obj = frame.EvaluateExpression(count, options) + cjthread_count = result_obj.GetValueAsSigned() - 1 + # Due to the expression limitation, the stack of up to 100 Cangjie threads can be displayed. + if cjthread_count > 100 : + cjthread_count = 100 + info = f'char* cjdb_expr_buf = (char *)malloc({cjthread_count * 2048});\ + int a=(int)CJ_MRT_GetAllCJThreadStackTrace(cjdb_expr_buf,{cjthread_count});(char *)cjdb_expr_buf' + buf = frame.EvaluateExpression(info, options) + error = lldb.SBError() + for i in range(cjthread_count): + cjthreads = process.ReadCStringFromMemory(buf.GetValueAsUnsigned() + i *2048, 2048, error).split("\n") + for cjthread in cjthreads: + result.PutCString(cjthread) + frame.EvaluateExpression("free(cjdb_expr_buf)", options) + return + + def get_short_help(self): + return "Show the all cangjie thread's call stack. excluding those in the running state. \n" + +class CangjieStdLib: + def __init__(self, target:lldb.SBTarget, extra_args, _): + pass + def handle_stop(self, exe_ctx, stream)-> bool: + thread = exe_ctx.thread + frame = exe_ctx.frame + function_name = frame.GetFunctionName() + if not function_name: + return True + if re.match("^_CN[a-c][a-z]", function_name): + plan = thread.StepOut() + exe_ctx.thread.StepUsingScriptedThreadPlan('{}.{}'.format(__name__, plan), False) + return True + return True + +def __lldb_init_module(debugger, internal_dict): + debugger.HandleCommand(f'command script add -c {__name__}.CommandSet set') + + debugger.HandleCommand(f'command alias locals frame variable') + debugger.HandleCommand(f'command alias globals target variable') + debugger.HandleCommand('command unalias bt') + debugger.HandleCommand(f'command script add -c {__name__}.CommandCJBackTrace bt') + debugger.HandleCommand('target stop-hook add -P {}.{}'.format(__name__, "CangjieStdLib")) + debugger.HandleCommand(f'command script add -c {__name__}.CommandCJThreadBackTrace cjthread') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieSyntheticProvider \ + -x "^.+$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieSummaryDisplayTypeSyntheticProvider \ + -x "^(const )?(std[.](core|time)::(String|Range|DateTime))|(CString|CPointer<.+>)$"\ + --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieVArraySyntheticProvider \ + -x "^(const )?VArray<.+>$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieArrayListSyntheticProvider \ + -x "^(const )?std[.]collection::ArrayList<.+>$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieArraySyntheticProvider \ + -x "^(const )?std[.]core::Array<.+>$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieTupleSyntheticProvider \ + -x "^(const )?Tuple<.+>$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieOptionSyntheticProvider \ + -x "^(const )?std[.]core::Option<.+>( \\*)?$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CanjieHashMapEntrySyntheticProvider \ + -x "^std[.]collection::HashMapEntry<.+>$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieHashMapSyntheticProvider \ + -x "^(const )?std[.]collection::HashMap<.+>$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieHashSetSyntheticProvider \ + -x "^(const )?std[.]collection::HashSet<.+>$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieGTSyntheticProvider \ + -x "(const )?(\$G_[A-Z]+|Any$)" --category CustomView') + debugger.HandleCommand(f'type summary add -F {__name__}.CangjieGTSummaryProvider \ + -x "(const )?(\$G_[A-Z]+|Any$)" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.UnitSummaryProvider \ + -x "^(const )?Unit$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.RangeSummaryProvider \ + -x "^(const )?std[.]core::Range<.+>$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.FunctionSummaryProvider \ + -x "^(const )?(.+::|^)\\(.*\\)( ?)->.+$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.StringSummaryProvider \ + -x "^(const )?std[.]core::String$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.RuneSummaryProvider \ + -x "^(const )?Rune$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.Int8SummaryProvider \ + -x "^(const )?U?Int8$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.CPointerSummaryProvider \ + -x "^(const )?CPointer<.+>$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.CStringSummaryProvider \ + -x "^(const )?CString$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.OptionSummaryProveder \ + -x "^(const )?std[.]core::Option<.+>$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.OptionPtrSummaryProveder \ + -x "^(const )?std[.]core::Option<.+> \\*$" --category CustomView -v') + # use toStringProvider + debugger.HandleCommand(f'type summary add -F {__name__}.TimeZoneSummaryProvider \ + -x "^(const )?std[.]time::TimeZone$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.DateTimeSummaryProvider \ + -x "^(const )?std[.]time::DateTime$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.CangjieEnum0SummaryProvider \ + -x "^(.+)?E0\\$(.+)$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.CangjieEnum2SummaryProvider \ + -x "(.+)?E2\\$(.+)" --category CustomView -v') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieEnum1SyntheticProvider \ + -x "(.+)?E1\\$(.+)" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieEnum2SyntheticProvider \ + -x "(.+)?E2\\$(.+)" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieEnum3SyntheticProvider \ + -x "(.+)?E3\\$(.+)" --category CustomView') + debugger.HandleCommand('type category enable CustomView') + debugger.HandleCommand('env cjProcessorNum=1') + debugger.HandleCommand("settings set target.max-children-depth 5") + debugger.HandleCommand("settings set target.max-children-count 100") + debugger.HandleCommand('settings set target.process.thread.step-avoid-regexp ^_CN[a-c][a-z]') diff --git a/llvm/include/llvm/BinaryFormat/MachO.h b/llvm/include/llvm/BinaryFormat/MachO.h index c05e79333d38..86021af708d6 100644 --- a/llvm/include/llvm/BinaryFormat/MachO.h +++ b/llvm/include/llvm/BinaryFormat/MachO.h @@ -106,6 +106,7 @@ enum : uint32_t { SG_FVMLIB = 0x2u, SG_NORELOC = 0x4u, SG_PROTECTED_VERSION_1 = 0x8u, + SG_READ_ONLY = 0x10u, // Constant masks for the "flags" field in llvm::MachO::section and // llvm::MachO::section_64 diff --git a/llvm/lib/CodeGen/CJMetadata.cpp b/llvm/lib/CodeGen/CJMetadata.cpp index 69cb85c18034..a91c82bcea45 100644 --- a/llvm/lib/CodeGen/CJMetadata.cpp +++ b/llvm/lib/CodeGen/CJMetadata.cpp @@ -442,11 +442,17 @@ void CJMetadataInfo::emitStackTraceInfo(const MCSymbol *FuncSym, OS.emitValue(getOrInsertStrPoolOffset(MethodNameStrIndex, DescSym), StrSize); OS.emitValue(getOrInsertStrPoolOffset(DirStrIndex, DescSym), StrSize); OS.emitValue(getOrInsertStrPoolOffset(FileNameStrIndex, DescSym), StrSize); - OS.emitValue( - MCBinaryExpr::createSub( - MCSymbolRefExpr::create(StrPoolDictOffsetsSym, AP.OutContext), - MCSymbolRefExpr::create(DescSym, AP.OutContext), AP.OutContext), - StrSize); + + if (MethodCompressedCode.empty() && DirCompressedCode.empty() && + FileCompressedCode.empty()) { + OS.emitIntValue(0, 4); + } else { + OS.emitValue( + MCBinaryExpr::createSub( + MCSymbolRefExpr::create(StrPoolDictOffsetsSym, AP.OutContext), + MCSymbolRefExpr::create(DescSym, AP.OutContext), AP.OutContext), + 4); + } return; } @@ -584,6 +590,9 @@ void CJMetadataInfo::emitGCRoots() { return; OS.switchSection(TD[GCRootsTableIdx].TableSection); + if (TT.isOSBinFormatMachO()) + // 8: align size, 8 bytes + OS.emitValueToAlignment(8); for (const auto GCRoot : GCRootTable) { OS.emitValue(GCRoot, 8); } @@ -705,6 +714,9 @@ void CJMetadataInfo::emitGlobalInitFuncTable() { return; OS.switchSection(TD[GlobalInitFuncIdx].TableSection); + if (TT.isOSBinFormatMachO()) + // 8: align size, 8 bytes + OS.emitValueToAlignment(8); NamedMDNode *PkgInitFuncMD = M->getNamedMetadata("pkg_init_func"); std::string GlobalInitFuncName; if (PkgInitFuncMD != nullptr) { @@ -741,6 +753,9 @@ void CJMetadataInfo::emitSDKVersion() { return; OS.switchSection(TD[SDKVersionIdx].TableSection); + if (TT.isOSBinFormatMachO()) + // 8: align size, 8 bytes + OS.emitValueToAlignment(8); // 8: sdk version size, 8 bytes. OS.emitValue(getGVRefSymbol(Version), 8); } diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp index 0ff3c077a549..f18cda8c3107 100644 --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -899,7 +899,7 @@ void MCObjectFileInfo::initCOFFMCObjectFileInfo(const Triple &T) { SectionKind::getData()); XDataSection = Ctx->getCOFFSection( - ".xdata", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ, + ".xdata", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE, SectionKind::getData()); SXDataSection = Ctx->getCOFFSection(".sxdata", COFF::IMAGE_SCN_LNK_INFO, diff --git a/llvm/lib/Transforms/Scalar/CJGCLiveAnalysis.cpp b/llvm/lib/Transforms/Scalar/CJGCLiveAnalysis.cpp index 9e616f9dbfdc..04731126d413 100644 --- a/llvm/lib/Transforms/Scalar/CJGCLiveAnalysis.cpp +++ b/llvm/lib/Transforms/Scalar/CJGCLiveAnalysis.cpp @@ -1017,7 +1017,8 @@ void StructLiveAnalysis::addGCFieldsByValue(Value *V, if (AI->getAllocatedType()->isStructTy()) { assert(AllocaData.StructLayoutGCPtrMap.count(Base) && "StructLayoutGCPtrMap lacks AllocaInst information."); - if (AllocaData.StructLayoutGCPtrMap[Base][Field->Offset]) { + if (Field->Offset == -1 || + AllocaData.StructLayoutGCPtrMap[Base][Field->Offset]) { Set.insert(Field); } } else if (AI->getAllocatedType()->isPointerTy()) { diff --git a/llvm/lib/Transforms/Scalar/CJIRVerifier.cpp b/llvm/lib/Transforms/Scalar/CJIRVerifier.cpp index 657cbbfd525c..4131e80c891e 100644 --- a/llvm/lib/Transforms/Scalar/CJIRVerifier.cpp +++ b/llvm/lib/Transforms/Scalar/CJIRVerifier.cpp @@ -321,11 +321,12 @@ public: if (auto *II = dyn_cast(U)) if (II->getIntrinsicID() == Intrinsic::cj_blackhole) return; - if (auto *II = dyn_cast(I.getOperand(0))) - if (II->getIntrinsicID() == Intrinsic::cj_blackhole) - return; } + if (auto *II = dyn_cast(I.getOperand(0))) + if (II->getIntrinsicID() == Intrinsic::cj_blackhole) + return; + for (const User *U : I.users()) { Assert(isa(U) || isa(U), "Addrspacecast result can only be used for store or call.", &I); diff --git a/llvm/lib/Transforms/Scalar/CJRuntimeLowering.cpp b/llvm/lib/Transforms/Scalar/CJRuntimeLowering.cpp index 9ca5aabf73d0..85a7a023f6b9 100644 --- a/llvm/lib/Transforms/Scalar/CJRuntimeLowering.cpp +++ b/llvm/lib/Transforms/Scalar/CJRuntimeLowering.cpp @@ -762,8 +762,7 @@ private: Func->setUnnamedAddr(GlobalValue::UnnamedAddr::Local); RTFuncMap[Callee] = Func; if (CI->getIntrinsicID() == Intrinsic::cj_blackhole) { - Func->addFnAttr(Attribute::ReadNone); - Func->setCallingConv(CallingConv::AnyReg); + Func->addFnAttr(Attribute::ReadOnly); } return Func; } @@ -959,12 +958,8 @@ static bool runtimeLoweringFunc(Function &F, CJIntrinsicLowering &Lowering) { case Intrinsic::cj_set_gc_threshold: case Intrinsic::cj_post_throw_exception: case Intrinsic::cj_register_implicit_exception_raisers: - Lowering.replaceWithRuntimeFunc(CI, true, false); - Changed = true; - break; case Intrinsic::cj_blackhole: Lowering.replaceWithRuntimeFunc(CI, true, false); - CI->setCallingConv(CallingConv::AnyReg); Changed = true; break; case Intrinsic::cj_cross_access_barrier: diff --git a/llvm/tools/llvm-objdump/MachODump.cpp b/llvm/tools/llvm-objdump/MachODump.cpp index cdbecd5ec243..54a202cd67b5 100644 --- a/llvm/tools/llvm-objdump/MachODump.cpp +++ b/llvm/tools/llvm-objdump/MachODump.cpp @@ -8657,6 +8657,12 @@ static void PrintSegmentCommand(uint32_t cmd, uint32_t cmdsize, outs() << " PROTECTED_VERSION_1"; flags &= ~MachO::SG_PROTECTED_VERSION_1; } + if (flags & MachO::SG_READ_ONLY) { + // Apple's otool prints the SG_ prefix for this flag, but not for the + // others. + outs() << " SG_READ_ONLY"; + flags &= ~MachO::SG_READ_ONLY; + } if (flags) outs() << format(" 0x%08" PRIx32, flags) << " (unknown flags)\n"; else cangjie_compiler-1.0.7/third_party/llvm_patch_append_code.patch000066400000000000000000002074611510705540100250450ustar00rootroot00000000000000diff --git a/lldb/bindings/interface/SBMixedArkTSDebugger.i b/lldb/bindings/interface/SBMixedArkTSDebugger.i new file mode 100644 index 000000000000..7b394390fdf7 --- /dev/null +++ b/lldb/bindings/interface/SBMixedArkTSDebugger.i @@ -0,0 +1,25 @@ +//===-- SBMixedArkTSDebugger.h --------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents a list of :py:class:`SBMixedArkTSDebugger`." +) SBMixedArkTSDebugger; +class SBMixedArkTSDebugger +{ +public: + + SBMixedArkTSDebugger (); + + SBMixedArkTSDebugger (const lldb::SBMixedArkTSDebugger &rhs); + + ~SBMixedArkTSDebugger (); +}; + +} // namespace lldb diff --git a/lldb/include/lldb/API/SBMixedArkTSDebugger.h b/lldb/include/lldb/API/SBMixedArkTSDebugger.h index b98365d340b6..aba32338ccb2 100644 --- a/lldb/include/lldb/API/SBMixedArkTSDebugger.h +++ b/lldb/include/lldb/API/SBMixedArkTSDebugger.h @@ -27,6 +27,8 @@ public: lldb::SBData GetBackTrace(SBError &er); + lldb::SBData OperateDebugMessage(const char *message, SBError &er); + }; } // namespace lldb diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index eadb6f127b1c..ca5a19f12c9f 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -1580,8 +1580,6 @@ public: ~CJThread() = default; }; - std::vector> GetAllCJThreadStatus(lldb_private::ExecutionContext &exe_ctx); - private: /// Construct with optional file and arch. /// diff --git a/lldb/source/API/SBMixedArkTSDebugger.cpp b/lldb/source/API/SBMixedArkTSDebugger.cpp index c2ef0427d9e7..b99f941e784a 100644 --- a/lldb/source/API/SBMixedArkTSDebugger.cpp +++ b/lldb/source/API/SBMixedArkTSDebugger.cpp @@ -36,4 +36,10 @@ lldb::SBData SBMixedArkTSDebugger::GetBackTrace(SBError &er) { LLDB_INSTRUMENT_VA(this, er); return SBData(); -} \ No newline at end of file +} + +lldb::SBData SBMixedArkTSDebugger::OperateDebugMessage(const char *message, SBError &er) { + LLDB_INSTRUMENT_VA(this, er); + + return SBData(); +} diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp index 7d2370a6c710..1fe8a2d215c8 100644 --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -107,21 +107,6 @@ protected: std::string m_new_process_action; }; -static void SetSigsegvForCangjie(ProcessSP process_sp) { - // Set SIGSEGV as default action PASS:true STOP:false NOTIFY:false for gc. - // Add "!" to pass_action is necessary according to lldb source code - bool stop_action = false; - bool pass_action = true; - bool notify_action = false; - UnixSignalsSP signals_sp = process_sp->GetUnixSignals(); - int32_t signo = signals_sp->GetSignalNumberFromName("SIGSEGV"); - if (signo != LLDB_INVALID_SIGNAL_NUMBER) { - signals_sp->SetShouldStop(signo, stop_action); - signals_sp->SetShouldSuppress(signo, !pass_action); - signals_sp->SetShouldNotify(signo, notify_action); - } -} - #if defined(__APPLE__) static void SetSigBusvForCangjie(ProcessSP process_sp) { // Set SIGBUS as default action PASS:true STOP:false NOTIFY:false for macos @@ -287,7 +272,6 @@ protected: if (error.Success()) { ProcessSP process_sp(target->GetProcessSP()); - SetSigsegvForCangjie(process_sp); #if defined(__APPLE__) SetSigBusvForCangjie(process_sp); #endif @@ -456,7 +440,6 @@ protected: if (error.Success()) { process_sp = target->GetProcessSP(); if (process_sp) { - SetSigsegvForCangjie(process_sp); #if defined(__APPLE__) SetSigBusvForCangjie(process_sp); #endif diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index b23740023b0f..d7cc614aceac 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -3295,8 +3295,8 @@ bool ValueObject::GetCangjieDynamicType( } bool ValueObject::IsCangjieGenericType() { - if (GetCompilerType().GetTypeName().GetStringRef().contains("$G") || - GetCompilerType().GetTypeName().GetStringRef().contains("Interface$")) { + if (GetTypeName().GetStringRef().contains("$G") || + GetTypeName().GetStringRef().contains("Interface$")) { return true; } if (GetCompilerType().GetTypeClass() ==lldb::eTypeClassTypedef) { diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp index f9ce180899f2..70910fd70330 100644 --- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -643,25 +643,43 @@ void DynamicLoaderPOSIXDYLD::LoadAllCurrentModules() { m_process->PrefetchModuleSpecs( module_names, m_process->GetTarget().GetArchitecture().GetTriple()); - llvm::ThreadPool pool(llvm::hardware_concurrency(DynamicLoaderPOSIXDYLD::DYLD_CONCURRENCY_THREADING)); - for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) { - constexpr const auto &func_name = __FUNCTION__; - pool.async([&](const FileSpec &file_spec, addr_t link_addr, addr_t base_addr) { - ModuleSP module_sp = - LoadModuleAtAddress(file_spec, link_addr, base_addr, true); - if (module_sp.get()) { - LLDB_LOG(log, "LoadAllCurrentModules loading module: {0}", file_spec.GetFilename()); - module_list.Append(module_sp); - } else { - LLDB_LOGF( - log, - "DynamicLoaderPOSIXDYLD::%s failed loading module %s at 0x%" PRIx64, - func_name, file_spec.GetCString(), base_addr); - } - }, I->file_spec, I->link_addr, I->base_addr); + if (m_process->GetTarget().GetArchitecture().GetTriple().str().find("ohos") != std::string::npos) { + llvm::ThreadPool pool(llvm::hardware_concurrency(DynamicLoaderPOSIXDYLD::DYLD_CONCURRENCY_THREADING)); + for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) { + constexpr const auto &func_name = __FUNCTION__; + pool.async([&](const FileSpec &file_spec, addr_t link_addr, addr_t base_addr) { + ModuleSP module_sp = + LoadModuleAtAddress(file_spec, link_addr, base_addr, true); + if (module_sp.get()) { + LLDB_LOG(log, "LoadAllCurrentModules loading module: {0}", file_spec.GetFilename()); + module_list.Append(module_sp); + } else { + LLDB_LOGF( + log, + "DynamicLoaderPOSIXDYLD::%s failed loading module %s at 0x%" PRIx64, + func_name, file_spec.GetCString(), base_addr); + } + }, I->file_spec, I->link_addr, I->base_addr); + } + pool.wait(); + } else { + for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) { + ModuleSP module_sp = + LoadModuleAtAddress(I->file_spec, I->link_addr, I->base_addr, true); + if (module_sp.get()) { + LLDB_LOG(log, "LoadAllCurrentModules loading module: {0}", + I->file_spec.GetFilename()); + module_list.Append(module_sp); + } else { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF( + log, + "DynamicLoaderPOSIXDYLD::%s failed loading module %s at 0x%" PRIx64, + __FUNCTION__, I->file_spec.GetCString(), I->base_addr); + } + } } - pool.wait(); m_process->GetTarget().ModulesDidLoad(module_list); m_initial_modules_added = true; } diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieDeclMap.cpp b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieDeclMap.cpp index d0240f040bb4..fb79a00d5280 100644 --- a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieDeclMap.cpp +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieDeclMap.cpp @@ -368,6 +368,19 @@ std::string CangjieDeclMap::GetFuncNameFromComplierType(const CompilerType& func return funcname; } +bool CheckNestedGenericFunction(lldb_private::ConstString funcname) { + int count = 0; + size_t pos = 0; + auto str = funcname.GetStringRef(); + llvm::StringRef substr = " 1; +} + std::vector CangjieDeclMap::LookUpFunction(std::string name, bool find_global) { std::vector result; auto target = m_exe_ctx.GetTargetPtr(); @@ -405,6 +418,9 @@ std::vector CangjieDeclMap::LookUpFunction(std if (!sym_ctx.function) { continue; } + if (CheckNestedGenericFunction(sym_ctx.function->GetNameNoArguments())) { + return result; + } std::string funcNameWithPkg = sym_ctx.function->GetNameNoArguments().GetCString(); if (funcNameWithPkg.find(m_current_pkgname) == std::string::npos) { continue; @@ -1733,8 +1749,8 @@ CompilerType CangjieDeclMap::GetDynamicTypeFromTy(Ptr& ty, std::string } else if (ty->IsString()) { auto type = this->FindParsedTypesByName(typeName); if (!type.IsValid()) { - type = LookUptype(lldb::ModuleSP(), - lldb_private::ConstString(typeName.c_str()), lldb_private::CompilerDeclContext()); + type = LookUptype(lldb::ModuleSP(), lldb_private::ConstString(typeName.c_str()), + lldb_private::CompilerDeclContext()); m_parsed_types.insert({"std.core::String", type}); } return type; diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieIRForTarget.cpp b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieIRForTarget.cpp index edd65550725a..f72ce64242d6 100644 --- a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieIRForTarget.cpp +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieIRForTarget.cpp @@ -410,9 +410,17 @@ void CangjieIRForTarget::SetPlatformInfo() { if (env.equals("ohos")) { m_module->setTargetTriple("aarch64-unknown-linux-ohos"); } + if (env.equals("android")) { + m_module->setTargetTriple("aarch64-unknown-linux-android"); + } m_module->setDataLayout("e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"); } - + if (ostype == llvm::Triple::Linux && archtype == llvm::Triple::ArchType::x86_64) { + if (env.equals("ohos")) { + m_module->setTargetTriple("x86_64-unknown-linux-ohos"); + m_module->setDataLayout("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"); + } + } if (ostype == llvm::Triple::Darwin) { if (archtype == llvm::Triple::ArchType::aarch64) { m_module->setDataLayout("e-m:o-i64:64-i128:128-n32:64-S128"); diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 965511111382..9c97fc7315b2 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1276,6 +1276,12 @@ static void LoadCjFormatterSummary(lldb::TypeCategoryImplSP cpp_category_sp) { AddCXXSummary(cpp_category_sp, lldb_private::formatters::RangeSummaryProvider, "Range summary provider", ConstString("^std[.]core::Range<.+>$"), cj_flags, true); + AddCXXSummary(cpp_category_sp, lldb_private::formatters::DateTimeSummaryProvider, + "DateTime summary provider", ConstString("^std[.]time::DateTime$"), cj_flags, true); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::DecimalSummaryProvider, + "Decimal summary provider", ConstString("^std[.]math.numeric::Decimal$"), cj_flags, true); + // Applies only to the enum type without parameters. AddCXXSummary(cpp_category_sp, lldb_private::formatters::EnumSummaryProvider, "Range summary provider", ConstString("^(.+)?E0\\$(.+)$"), cj_flags, true); diff --git a/lldb/source/Plugins/Language/CPlusPlus/CjTypes.cpp b/lldb/source/Plugins/Language/CPlusPlus/CjTypes.cpp index 78793e6dcdb7..bdefa27d06f5 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CjTypes.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CjTypes.cpp @@ -9,6 +9,7 @@ //===----------------------------------------------------------------------===// #include "CjTypes.h" +#include #include "lldb/DataFormatters/StringPrinter.h" #include "lldb/DataFormatters/FormattersHelpers.h" #include "lldb/Target/Process.h" @@ -131,7 +132,8 @@ bool lldb_private::formatters::CStringSummaryProvider( // Need delete cpoint after CString refactor to built-in on Jet backend. cpoint = chars; } - + time_t currentTime; + struct tm* localTime = localtime(¤tTime); stream.Printf("%s", cpoint->GetSummaryAsCString()); return true; @@ -433,6 +435,483 @@ bool lldb_private::formatters::EnumSummaryProvider( return true; } + +// The layout of the type is: +// public struct BigInt { +// let int: UInt64, +// let intArr: Array, +// let negSign: Bool +// } +// public struct Array { +// let rawptr: RawArray +// let start: Int64 +// let len: Int64 + +class BigInt { +public: + BigInt(uint64_t intv, std::vector big_intArr, int64_t len, bool negSign) + : m_intv(intv), + m_big_intArr(big_intArr), + m_len(len), + m_negsign(negSign) { } + std::string ToString() { + int64_t digits = 9; + uint64_t anyBase = 1000000000; + // Get str Arr size + int64_t strArrSize = (32 * (m_len + 2) / int64_t(29) + 1); + std::vector stringArr; + for (size_t i = 0; i < strArrSize; i++) { + stringArr.emplace_back(0); + } + int64_t stringArrBound = CalculateStrArr(stringArr, m_big_intArr, m_intv, 1); + + std::vector stringUtf8Arr(stringArrBound * digits + 1, '0'); + int64_t begin = ToBaseString(stringUtf8Arr, 9, stringArr[stringArrBound - 1]); + if (m_negsign) { + stringUtf8Arr[begin] = '-'; + begin--; + } + begin++; + for (int64_t i = stringArrBound - 2; i >= 0; i--) { + ToBaseString(stringUtf8Arr, digits * (stringArrBound - i), stringArr[i]); + } + std::string result; + for (size_t i = begin; i < stringUtf8Arr.size(); i++) { + result += static_cast(stringUtf8Arr[i]); + } + return result; + } + + int64_t ToBaseString(std::vector& stringUtf8Arr, int64_t index, uint64_t int1) { + const uint8_t DIGIT_DIFF = 0x30; + const uint8_t LETTER_UPPER_DIFF = 0x41 - 0x0A; + const uint8_t LETTER_LOWER_DIFF = 0x61 - 0x0A; + uint64_t base = 10; + int64_t i = index; + uint64_t quo = int1; + while (quo != 0) { + uint8_t rem = uint8_t(quo % base); + if (rem < 10) { // Decimal 10 + stringUtf8Arr[i] = rem + DIGIT_DIFF; + } else { + stringUtf8Arr[i] = rem + LETTER_UPPER_DIFF; + } + quo = quo / uint64_t(base); + i--; + } + return i; + } + + int64_t CalculateStrArr(std::vector& stringArr, uint64_t bigInt, int64_t bound) { + int64_t stringArrBound = bound; + int64_t anyBase = 1000000000; + for (int64_t j = stringArrBound - 1; j >= 0; --j) { + stringArr[j] <<= 32; // 32-bit + } + stringArr[0] += bigInt; + for (size_t j = 0; j < stringArrBound - 1; j++) { + stringArr[j + 1] += stringArr[j] / anyBase; + stringArr[j] %= anyBase; + } + int64_t rem = stringArr[stringArrBound - 1] / anyBase; + stringArr[stringArrBound - 1] %= anyBase; + while (rem > 0) { + stringArr[stringArrBound] = rem; + stringArrBound++; + rem = stringArr[stringArrBound - 1] / anyBase; + stringArr[stringArrBound - 1] %= anyBase; + } + return stringArrBound; + } + + int64_t CalculateStrArr(std::vector& stringArr, std::vector& bigArray, + uint64_t bigInt, int64_t bound) { + int64_t stringArrBound = bound; + int64_t i = bigArray.size() - 1; + while (i >= 0) { + stringArrBound = CalculateStrArr(stringArr, uint64_t(bigArray[i]), stringArrBound); + i--; + } + stringArrBound = CalculateStrArr(stringArr, bigInt >> 32, stringArrBound); // 32-bit + return CalculateStrArr(stringArr, (bigInt & 0xFFFFFFFF), stringArrBound); + } +private: + uint64_t m_intv; + std::vector m_big_intArr; + int64_t m_len; + bool m_negsign; +}; + +std::string lldb_private::formatters::GetBigIntvalue(ValueObject &value) { + // The layout of the type is: + // public struct BigInt { + // let int: UInt64, + // let intArr: Array, + // let negSign: Bool + // } + lldb::ValueObjectSP bint = value.GetChildMemberWithName(ConstString("int"), true); + lldb::ValueObjectSP negSign = value.GetChildMemberWithName(ConstString("negSign"), true); + lldb::ValueObjectSP valueIntArr = value.GetChildMemberWithName(ConstString("intArr"), true); + if (!bint || !negSign || !valueIntArr) { + return "0"; + } + auto value_int = bint->GetValueAsUnsigned(UINT64_MAX); + auto value_negSign = negSign->GetValueAsSigned(INT64_MAX); + auto array_len = valueIntArr->GetChildMemberWithName(ConstString("len"), true); + if (!array_len) { + return "0"; + } + auto len = array_len->GetValueAsSigned(INT64_MAX); + if (len == 0) { + std::string result = std::to_string(value_int); + if (value_negSign == 1) { + result = "-" + result; + } + return result; + } + // use intArr + // public struct Array { + // let rawptr: RawArray + // let start: Int64 + // let len: Int64 + auto rawptr = valueIntArr->GetChildMemberWithName(ConstString("rawptr"), true); + auto m_start = valueIntArr->GetChildMemberWithName(ConstString("start"), true); + if (!rawptr || !m_start) { + return "0"; + } + auto start = m_start->GetValueAsSigned(INT64_MAX); + + auto m_elements = rawptr->GetChildMemberWithName(ConstString("elements"), true); + CompilerType element_type = m_elements->GetCompilerType(); + llvm::Optional size = element_type.GetByteSize(nullptr); + uint64_t element_size = 4; // UInt32's size is 4. + + std::vector big_intArr; + for (size_t idx = 0; idx < len; idx++) { + addr_t addr = m_elements->GetAddressOf() + (m_start->GetValueAsUnsigned(0) + idx) * element_size; + ValueObjectSP valobj_sp( + ValueObject::CreateValueObjectFromAddress("tmp", addr, m_elements->GetExecutionContextRef(), element_type)); + + if (valobj_sp) + valobj_sp->SetSyntheticChildrenGenerated(true); + + if (valobj_sp->IsPointerType()) { + Status error; + valobj_sp = valobj_sp->Dereference(error); + valobj_sp->SetName(ConstString(valobj_sp->GetName().GetStringRef().ltrim('*').str())); + } + auto tmpU32 = valobj_sp->GetValueAsUnsigned(UINT32_MAX); + big_intArr.emplace_back(tmpU32); + } + auto bigint = BigInt(value_int, big_intArr, len, value_negSign); + return bigint.ToString(); +} + +// The layout of the type is: +// public struct Decimal { +// var _scale: Int32 +// var _precision: Int64 +// var _value: BigInt +// var _sign: Int64 +// } +class DecimalToString { +public: + DecimalToString(int32_t scale, int64_t precision, std::string bigInt, int64_t sign) + : _scale(scale), _precision(precision), _bigInt(bigInt), _sign(sign) {} + std::string ToString() { + if (_scale < 0) { + if (_sign == 0) { + return "0"; + } + std::string decimalStrArr(_bigInt.length() - _scale, '0'); + decimalStrArr = _bigInt + decimalStrArr; + return decimalStrArr; + } + std::string unscaleValStrArr = _bigInt; + // scale value bigger than 0, need insert decimal point to corresponding position. + // precision is bigger than 0 and scale range [0, Int32.Max] + auto decimalPointIndex = _precision - _scale; + std::string decimalStrArr(unscaleValStrArr); + if (decimalPointIndex == 0) { + if (unscaleValStrArr.at(0) == '-') { + decimalStrArr.insert(0, 1, '-'); + decimalStrArr.insert(2, 1, '.'); // position is 2 + } else { + decimalStrArr.insert(0, 1, '0'); + decimalStrArr.insert(1, 1, '.'); + } + } else if (decimalPointIndex > 0) { + if (unscaleValStrArr[0] == '-') { + decimalStrArr.insert(decimalPointIndex + 1, 1, '.'); + } else { + decimalStrArr.insert(decimalPointIndex, 1, '.'); + } + } else { + // decimalPointIndex less than 0, decimal point in the left of bigint value. + // when decimalPointIndex less than 0, it's range [-Int32.Max, 0], abs will nerver overflow. + std::string temp(unscaleValStrArr.size() + abs(decimalPointIndex) + 2, '0'); + decimalStrArr = temp; + if (unscaleValStrArr[0] == '-') { + decimalStrArr[0] = '-'; + decimalStrArr[2] = '.'; // Index 2. + for (int64_t i = 1; i < unscaleValStrArr.size(); i++) { + decimalStrArr[abs(decimalPointIndex) + 3 + i] = unscaleValStrArr[i]; // Offset 3. + } + } else { + decimalStrArr[1] = '.'; + for (int64_t i = 0; i < unscaleValStrArr.size(); i++) { + decimalStrArr[abs(decimalPointIndex) + 2 + i] = unscaleValStrArr[i]; // Offset 2. + } + } + } + return decimalStrArr; + } + +private: + int32_t _scale; + int64_t _precision; + std::string _bigInt; + int64_t _sign; +}; + +bool lldb_private::formatters::DecimalSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options) +{ + // The layout of the type is: + // public struct Decimal { + // var _scale: Int32 + // var _precision: Int64 + // var _value: BigInt + // var _sign: Int64 + // } + lldb::ValueObjectSP raw = valobj.GetNonSyntheticValue(); + if (!raw) { + return false; + } + lldb::ValueObjectSP scale = raw->GetChildMemberWithName(ConstString("_scale"), true); + lldb::ValueObjectSP precision = raw->GetChildMemberWithName(ConstString("_precision"), true); + lldb::ValueObjectSP value = raw->GetChildMemberWithName(ConstString("_value"), true); + lldb::ValueObjectSP sign = raw->GetChildMemberWithName(ConstString("_sign"), true); + if (!scale || !precision || !value || !sign) { + return false; + } + auto dscale = scale->GetValueAsSigned(INT64_MAX); + std::string unscaleValStrArr = GetBigIntvalue(*value.get()); + if (dscale == 0) { + stream.Printf("%s", unscaleValStrArr.c_str()); + return true; + } + auto dsign = sign->GetValueAsSigned(INT64_MAX); + auto dprecision = precision->GetValueAsSigned(INT64_MAX); + std::string tmp = DecimalToString(dscale, dprecision, unscaleValStrArr, dsign).ToString(); + stream.Printf("%s", tmp.c_str()); + return true; +} + +class CangjieDateTime { +public: + CangjieDateTime(int64_t epoch, int64_t offset): epoch(epoch), offset(offset) {} + enum class MONTH { + Invaild = 0, + January = 1, + February, + March, + April, + May, + June, + July, + August, + September, + October, + November, + December + }; + int64_t epoch; + int64_t offset; + int64_t START_AD_YEAR = 1; + int64_t MIN_YEAR = -999999999; + int64_t MAX_YEAR = 999999999; + int64_t SECS_PER_DAY = 86400; + int64_t DAYS_PER_400YEARS = 146097; + int64_t DAYS_PER_100YEARS = 36524; + int64_t DAYS_PER_4YEARS = 1461; + int64_t DAYS_OF_NORMAL_YEAR = 365; + int64_t SECS_PER_HOUR = 3600; + int64_t SECS_PER_MINUTE = 60; + int64_t MAX_DAY_AD1 = (MAX_YEAR - 1) * 365 + (MAX_YEAR - 1) / 4 - (MAX_YEAR - 1) / 100 + (MAX_YEAR - 1) / 400 + 365; + int64_t MIN_DAY_AD1 = -(MAX_DAY_AD1 + 366); + int64_t SECS_OF_MIN_TO_AD1 = MIN_DAY_AD1 * SECS_PER_DAY; + std::vector DAYS_BEFORE = { + 0, + 31, // January, 31 days + 59, // until February, 31 + 28, 59 days + 90, // 90 days + 120, // 120 days + 151, // 151 days + 181, // 181 days + 212, // 212 days + 243, // 243 days + 273, // 273 days + 304, // 304 days + 334, // 334 days + 365 // 365 days + }; + int64_t Year() { + return std::get<0>(GetYearAndSecond()); + } + MONTH Month() { + return std::get<0>(GetMonthAndDay()); + } + int64_t DayOfMonth() { + return std::get<1>(GetMonthAndDay()); + } + int64_t Hour() { + int64_t second = std::get<1>(this->GetYearAndSecond()); + return (second % this->SECS_PER_DAY) / this->SECS_PER_HOUR; + } + int64_t Minute() { + int64_t second = std::get<1>(this->GetYearAndSecond()); + return (second % this->SECS_PER_HOUR) / this->SECS_PER_MINUTE; + } + int64_t Second() { + int64_t second = std::get<1>(this->GetYearAndSecond()); + return second % this->SECS_PER_MINUTE; + } +private: + static MONTH MonthOf(int64_t mon) { + std::vector months = { + MONTH::Invaild, MONTH::January, MONTH::February, MONTH::March, MONTH::April, MONTH::May, MONTH::June, + MONTH::July, MONTH::August, MONTH::September, MONTH::October, MONTH::November, MONTH::December + }; + if (mon >= 1 && mon <= 12) { // month 12 + return months[mon]; + } + return MONTH::Invaild; + } + std::tuple GetMonthAndDay() { + auto year_second = this->GetYearAndSecond(); + return this->GetDate(std::get<0>(year_second), std::get<1>(year_second)); + } + bool IsLeapYear(int64_t year) { + return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0); // 4 year, 100 year, 400 year + } + std::tuple GetDate(int64_t year, int64_t sec) { + int64_t days = int64_t(sec) / SECS_PER_DAY; + if (IsLeapYear(year)) { + if (days == DAYS_BEFORE[2]) { // Month 2 + return {MONTH::February, 29}; // Month 2 has 29 days. + } + if (days > DAYS_BEFORE[2]) { // Month 2 + days -= 1; + } + } + int64_t idx = 0; + int64_t day = 0; + while (idx < DAYS_BEFORE.size()) { + /* 1. In this case, the corresponding date is the 1st of the month idx + 1. */ + if (days == DAYS_BEFORE[idx]) { + idx++; + day = 1; + break; + } + /* 2. In this case, the corresponding date is month idx. */ + if (days < DAYS_BEFORE[idx]) { + day = days - DAYS_BEFORE[idx - 1] + 1; + break; + } + idx++; + } + MONTH month = MonthOf(idx); + return {month, day}; + } + std::tuple GetYearAndSecond() { + auto [year, sec] = this->ToYearAndSecond(this->epoch + this->GetOffset()); + return {year, int64_t(sec)}; + } + + /** + * Calculate year and second in year through seconds since year 1 A.D. + */ + std::tuple ToYearAndSecond(int64_t sec) { + int64_t year = START_AD_YEAR; + int64_t seconds = sec; + if (seconds < 0) { + seconds -= SECS_OF_MIN_TO_AD1; + year = MIN_YEAR; + } + int64_t days = seconds / SECS_PER_DAY; + int64_t restSec = seconds % SECS_PER_DAY; + + int64_t times = days / DAYS_PER_400YEARS; + year += 400 * times; // 400 years + days %= DAYS_PER_400YEARS; + + times = days / DAYS_PER_100YEARS; + times -= times >> 2; // 2 + year += 100 * times; // 100 year + days -= DAYS_PER_100YEARS * times; + + times = days / DAYS_PER_4YEARS; + year += 4 * times; // 4 year + days %= DAYS_PER_4YEARS; + + times = days / DAYS_OF_NORMAL_YEAR; + times -= times >> 2; // 2 + year += times; + days -= DAYS_OF_NORMAL_YEAR * times; + + int64_t secInYear = restSec + days * SECS_PER_DAY; + return {year, secInYear}; + } + int64_t GetOffset() { + return this->offset; + } +}; + +bool lldb_private::formatters::DateTimeSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) +{ + // The layout of the type is: + // public struct DateTime { + // let d: Duration + // let tz: TimeZone + // } + lldb::ValueObjectSP raw = valobj.GetNonSyntheticValue(); + if (!raw) { + return false; + } + lldb::ValueObjectSP duration = raw->GetChildMemberWithName(ConstString("d"), true); + if (!duration) { + return false; + } + lldb::ValueObjectSP sec = duration->GetChildMemberWithName(ConstString("sec"), true); + if (!sec) { + return false; + } + int64_t secVal = sec->GetValueAsSigned(INT64_MAX); + lldb::ValueObjectSP timeZone = raw->GetChildMemberWithName(ConstString("tz"), true); + if (!timeZone) { + return false; + } + lldb::ValueObjectSP zoneId = timeZone->GetChildMemberWithName(ConstString("zoneId"), true); + if (!zoneId) { + return false; + } + auto str_zoneId = zoneId->GetSummaryAsCString(); + CangjieDateTime date = CangjieDateTime(secVal, 0); + if (date.Year() > 999 || date.Year() < -999) { // 999, -999 year + stream.Printf("%d-%02d-%02dT%02d:%02d:%02dZ", date.Year(), + date.Month(), date.DayOfMonth(), date.Hour(), date.Minute(), date.Second()); + } else if (date.Year() < 0) { + stream.Printf("%05d-%02d-%02dT%02d:%02d:%02dZ", date.Year(), + date.Month(), date.DayOfMonth(), date.Hour(), date.Minute(), date.Second()); + } else { + stream.Printf("%04d-%02d-%02dT%02d:%02d:%02dZ", date.Year(), + date.Month(), date.DayOfMonth(), date.Hour(), date.Minute(), date.Second()); + } + return true; +} + bool lldb_private::formatters::Enum2SummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { if (valobj.IsPointerType()) { diff --git a/lldb/source/Plugins/Language/CPlusPlus/CjTypes.h b/lldb/source/Plugins/Language/CPlusPlus/CjTypes.h index b2601d781ac6..86fb312279e7 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CjTypes.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CjTypes.h @@ -30,6 +30,9 @@ bool EnumOptionPtrSummaryProvider(ValueObject &valobj, Stream &stream, const Typ bool FunctionSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); bool EnumSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); bool Enum2SummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +bool DateTimeSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +std::string GetBigIntvalue(ValueObject &valobj); +bool DecimalSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); } // namespace formatters } // namespace lldb_private diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCj.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCj.cpp index f4df84158a42..3e1b632fb3e9 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCj.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCj.cpp @@ -61,7 +61,9 @@ public: bool MightHaveChildren() override { return true; } size_t GetIndexOfChildWithName(ConstString name) override { return ExtractIndexFromString(name.GetCString()); } void SetIsInternalType(bool internal) { m_internal = internal; } - + std::string GetChildName(ValueObjectSP value); + llvm::StringRef GetBasicTypeName(ValueObjectSP value); + bool IsBasicType(ValueObjectSP value); private: int64_t m_freeOffset = 0; ValueObjectSP m_appendIndex; @@ -616,6 +618,65 @@ CjHashMapSyntheticFrontEnd::CjHashMapSyntheticFrontEnd(ValueObjectSP valobj_sp) Update(); } +bool CjHashMapSyntheticFrontEnd::IsBasicType(ValueObjectSP value) { + auto type = value->GetCompilerType(); + if (type.GetTypeClass() == lldb::eTypeClassTypedef) { + type = type.GetTypedefedType(); + } + if (type.GetTypeClass() == lldb::eTypeClassBuiltin) { + return true; + } + if (type.GetTypeClass() == lldb::eTypeClassStruct) { + auto typeName = type.GetTypeName().GetStringRef(); + if (typeName.find("std.core::String") == 0) { + return true; + } + if (typeName.find("std.math.numeric::Decimal") == 0) { + return true; + } + if (typeName.find("std.time::DateTime") == 0) { + return true; + } + } + return false; +} + +llvm::StringRef CjHashMapSyntheticFrontEnd::GetBasicTypeName(ValueObjectSP value) { + auto type = value->GetCompilerType(); + auto typeName = type.GetTypeName().GetStringRef(); + if (typeName == "Rune" || typeName == "Int8" || typeName == "UInt8") { + return value->GetSummaryAsCString(); + } + if (type.GetTypeClass() == lldb::eTypeClassStruct) { + // String + return value->GetSummaryAsCString(); + } + // integer, float, bool + return value->GetValueAsCString(); +} + +std::string CjHashMapSyntheticFrontEnd::GetChildName(ValueObjectSP value) { + // HashMap is internal type means a child of cangjie HashSet + // then we only need one child of value(the type of value is class) + // and this child idx is 2(key), child idx is 3(value). + ValueObjectSP child_key_sp = value->GetChildAtIndex(2, true); + ValueObjectSP child_val_sp = value->GetChildAtIndex(3, true); + if (!child_key_sp || !child_val_sp) { + return ""; + } + if (!IsBasicType(child_key_sp)) { + return ""; + } + llvm::StringRef key_str = GetBasicTypeName(child_key_sp); + if (!IsBasicType(child_val_sp)) { + std::string name = "[" + key_str.str() +"]"; + return name; + } + llvm::StringRef val_str = GetBasicTypeName(child_val_sp); + std::string name = "[" + key_str.str() + " -> "+ val_str.str() + "]"; + return name; +} + size_t CjHashMapSyntheticFrontEnd::CalculateNumChildren() { if (!m_appendIndex || !m_freeSize) { return 0; @@ -658,8 +719,13 @@ ValueObjectSP CjHashMapSyntheticFrontEnd::GetChildAtIndex(size_t idx) { // Mark the value(class type) is a child of HashMap. value->SetIsInternalType(true); - std::string name = llvm::formatv("[{0}]", idx).str(); - value->SetName(ConstString(name.c_str())); + // idx => Bob -> 123 for metaDsl. + if (value->IsInternalType()) { + auto name = GetChildName(value); + if (!name.empty()) { + value->SetName(ConstString(name)); + } + } return value; } diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index c5d73641ab0e..52abd534ee13 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -4898,27 +4898,3 @@ std::recursive_mutex &Target::GetAPIMutex() { /// Get metrics associated with this target in JSON format. llvm::json::Value Target::ReportStatistics() { return m_stats.ToJSON(*this); } - -std::vector> Target::GetAllCJThreadStatus( - lldb_private::ExecutionContext &exe_ctx) { - CommandObjCJThreadCommon cjthread_help; - Status error; - std::vector> cjthreads; - auto count = cjthread_help.GetCJThreadCount(exe_ctx, error); - auto info = cjthread_help.GetCJThreadInfo(exe_ctx, error); - std::lock_guard guard( - exe_ctx.GetProcessPtr()->GetThreadList().GetMutex()); - for (uint64_t i = 0; i < count; i++) { - auto cjthread_context = info->GetChildAtIndex(i, true); - cjthread_help.HandOneCJThread(exe_ctx.GetThreadSP(), cjthread_context, error, true); - auto frames = cjthread_help.m_frames; - StreamString strm; - auto name = cjthread_help.GetCJThreadName(cjthread_context); - auto id = cjthread_help.GetCJThreadID(cjthread_context); - auto state = cjthread_help.GetCJThreadState(cjthread_context); - auto cjthread = std::make_shared(frames, id, name, state); - cjthreads.push_back(cjthread); - } - - return cjthreads; -} diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index ac58925bdd01..6a5019003257 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -472,6 +472,12 @@ def int_cj_array_copy_struct : Intrinsic<[], [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_i64_ty]>; def int_cj_copy_struct_field : Intrinsic<[], [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_i64_ty]>; +def int_cj_cross_access_barrier : Intrinsic<[], [llvm_i64_ty]>; +def int_cj_get_exported_ref : Intrinsic<[llvm_gcptr_ty], + [llvm_i64_ty]>; +def int_cj_remove_exported_ref : Intrinsic<[], [llvm_i64_ty]>; +def int_cj_create_export_handle : Intrinsic<[llvm_i64_ty], + [llvm_gcptr_ty]>; //===------------------- Cangjie runtime Intrinsics ------------------------===// // @@ -563,6 +569,7 @@ def int_cj_atomic_compare_swap : Intrinsic<[llvm_i1_ty], def int_cj_get_fp_state : Intrinsic<[llvm_i64_ty], [llvm_anyint_ty]>; def int_cj_reset_fp_state : Intrinsic<[], []>; def int_cj_set_location : Intrinsic<[], []>; +def int_cj_blackhole : Intrinsic<[llvm_ptr_ty], [llvm_ptr_ty]>; //===------------------- Cangjie generic Intrinsics ------------------------===// // diff --git a/llvm/include/llvm/MC/SectionKind.h b/llvm/include/llvm/MC/SectionKind.h index 3a328ba1391d..e3dbb6f1d917 100644 --- a/llvm/include/llvm/MC/SectionKind.h +++ b/llvm/include/llvm/MC/SectionKind.h @@ -196,7 +196,8 @@ public: bool isThreadBSSLocal() const { return K == ThreadBSSLocal; } bool isGlobalWriteableData() const { - return isBSS() || isCommon() || isData() || isReadOnlyWithRel(); + return isBSS() || isCommon() || isData() || isReadOnlyWithRel() || + isCJData(); } bool isBSS() const { return K == BSS || K == BSSLocal || K == BSSExtern; } @@ -206,6 +207,7 @@ public: bool isCommon() const { return K == Common; } bool isData() const { return K == Data; } + bool isCJData() const { return K > CJMetadataInfo && K <= CJGCTib; } bool isReadOnlyWithRel() const { return K == ReadOnlyWithRel; diff --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp index fffb99910c49..f2f7050a4f08 100644 --- a/llvm/lib/Analysis/InlineCost.cpp +++ b/llvm/lib/Analysis/InlineCost.cpp @@ -2768,10 +2768,10 @@ InlineResult CallAnalyzer::analyze() { const auto &FuncName = Func->getName(); // TODO Currently, only the following implicit exceptions are // considered - if ((FuncName.contains("IndexOutOfBoundsException") || - FuncName.contains("NegativeArraySizeException") || - FuncName.contains("OverflowException")) && - ExecptionBB.insert(FuncName)) { + if (FuncName.contains("IndexOutOfBoundsException") || + FuncName.contains("NegativeArraySizeException") || + FuncName.contains("OverflowException")) { + if (!ExecptionBB.insert(FuncName)) break; BBWorklist.insert(TI->getSuccessor(TIdx)); break; } diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp index 462ac71edb16..a3b0ae5e7619 100644 --- a/llvm/lib/Analysis/Loads.cpp +++ b/llvm/lib/Analysis/Loads.cpp @@ -44,14 +44,26 @@ static bool isAligned(const Value *Base, const APInt &Offset, Align Alignment, static void getUses(Value *Base, SetVector &Uses, Value *StopInst = nullptr) { - SmallVector WorkList = {Base}; + SmallVector, 8> WorkList = {{Base, false}}; + auto IsLoad = [](Value *V) { + if (isa(V)) + return true; + auto *II = dyn_cast(V); + if (!II) + return false; + auto IID = II->getIntrinsicID(); + return IID == Intrinsic::cj_gcread_ref || + IID == Intrinsic::cj_gcread_static_ref; + }; while (!WorkList.empty()) { - Value *V = WorkList.pop_back_val(); + auto [V, L] = WorkList.pop_back_val(); for (auto *U : V->users()) { - if (Uses.contains(U) || U == StopInst) + bool Tag = IsLoad(U); + // load after load, then stop. + if ((L && Tag) || Uses.contains(U) || U == StopInst) continue; Uses.insert(U); - WorkList.push_back(U); + WorkList.push_back({U, Tag | L}); } } } @@ -103,7 +115,7 @@ static bool checkMaybeLoadFromNullST(Value *V, Value *&BP) { return false; } -static bool isNoNullArgumentOrLoad(const Value *V) { +static bool isNoNullArgumentOrLoad(const Value *V, const DominatorTree *DT) { Value *BP = const_cast(V); if (checkMaybeLoadFromNullST(const_cast(V), BP)) return false; @@ -130,30 +142,31 @@ static bool isNoNullArgumentOrLoad(const Value *V) { return false; } if (auto *SI = dyn_cast(U); - SI && getUnderlyingObject(SI->getPointerOperand()) != BP) + SI && getUnderlyingObject(SI->getPointerOperand()) != BP && + !DT->dominates(V, SI)) return false; if (auto *MI = dyn_cast(U); - MI && getUnderlyingObject(MI->getDest()) != BP) + MI && getUnderlyingObject(MI->getDest()) != BP && !DT->dominates(V, MI)) return false; } return true; } -static bool isNoNullPointer(const Value *V) { +static bool isNoNullPointer(const Value *V, const DominatorTree *DT) { assert(isa(V->getType()) && "It should be pointer type"); if (auto *I = dyn_cast(V); I && I->hasMetadata(LLVMContext::MD_untrusted_ref)) return false; if (auto *II = dyn_cast(V)) { if (II->isCJRefGCRead()) - return isNoNullArgumentOrLoad(V); + return isNoNullArgumentOrLoad(V, DT); } else if (auto *CI = dyn_cast(V)) { auto *F = CI->getCalledFunction(); return F && F->hasFnAttribute("cj-heapmalloc"); } else if (isa(V)) { return true; } else if (isa(V) || isa(V)) { - return isNoNullArgumentOrLoad(V); + return isNoNullArgumentOrLoad(V, DT); } return false; } @@ -169,7 +182,7 @@ static bool isCJDeferenceablePointer(const Value *V, const Instruction *CtxI, // If Base is a normal instruction, check whether the type of Base is in // stack or Base is cangjie malloc. In the case of Base dominates CtxI, V // can safely dereference at CtxI. - return DT->dominates(Base, CtxI) && isNoNullPointer(Base); + return DT->dominates(Base, CtxI) && isNoNullPointer(Base, DT); } return false; } diff --git a/llvm/lib/IR/CJIntrinsics.cpp b/llvm/lib/IR/CJIntrinsics.cpp index 29b72784b056..be12ae6d32cd 100644 --- a/llvm/lib/IR/CJIntrinsics.cpp +++ b/llvm/lib/IR/CJIntrinsics.cpp @@ -86,6 +86,10 @@ Value *getDest(const CallBase *CI) { return CI->getArgOperand(GCReadGeneric::DstPtr); case Intrinsic::cj_assign_generic: return CI->getArgOperand(AssignGeneric::DstPtr); + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + case Intrinsic::cj_array_copy_generic: + return CI->getArgOperand(ArrayCopy::DstPtr); } } @@ -108,6 +112,10 @@ Value *getSource(const CallBase *CI) { return CI->getArgOperand(GCReadGeneric::SrcPtr); case Intrinsic::cj_assign_generic: return CI->getArgOperand(AssignGeneric::SrcPtr); + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + case Intrinsic::cj_array_copy_generic: + return CI->getArgOperand(ArrayCopy::SrcPtr); } } @@ -128,6 +136,10 @@ Value *getSize(const CallBase *CI) { return CI->getArgOperand(GCWriteGeneric::Size); case Intrinsic::cj_gcread_generic: return CI->getArgOperand(GCReadGeneric::Size); + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + case Intrinsic::cj_array_copy_generic: + return CI->getArgOperand(ArrayCopy::Size); } } Value *getAtomicOrder(const CallBase *CI) { diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index 908453ae5e19..fafcd5c65a74 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -5745,6 +5745,7 @@ CCAssignFn *AArch64TargetLowering::CCAssignFnForCall(CallingConv::ID CC, return CC_AArch64_WebKit_JS; case CallingConv::GHC: return CC_AArch64_GHC; + case CallingConv::AnyReg: case CallingConv::C: case CallingConv::Fast: case CallingConv::PreserveMost: diff --git a/llvm/lib/Target/X86/X86MCInstLower.cpp b/llvm/lib/Target/X86/X86MCInstLower.cpp index 7c189b5916c3..710e386b3644 100644 --- a/llvm/lib/Target/X86/X86MCInstLower.cpp +++ b/llvm/lib/Target/X86/X86MCInstLower.cpp @@ -3134,7 +3134,7 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) { OutStreamer->emitInstruction(TmpInst, getSubtargetInfo()); // move rslt from xmm0 to eax to do the fp16 calling-convetion adaption - if (IsTruncToFP16) { + if (IsTruncToFP16 && !TT.isOSWindows()) { MCInst MovXMM0ToEAX = MCInstBuilder(X86::PINSRWrr) .addReg(X86::XMM0) .addReg(X86::XMM0) diff --git a/llvm/lib/Transforms/Scalar/CJBarrierSplit.cpp b/llvm/lib/Transforms/Scalar/CJBarrierSplit.cpp index c6c4d69853ba..ce924cede76d 100644 --- a/llvm/lib/Transforms/Scalar/CJBarrierSplit.cpp +++ b/llvm/lib/Transforms/Scalar/CJBarrierSplit.cpp @@ -249,7 +249,7 @@ public: SmallVector &AllRefPos) { uint64_t EleNum = AT->getNumElements(); Type *EleType = AT->getElementType(); - if (isa(EleType)) { + if (isa(EleType) && isGCPointerType(EleType)) { for (uint64_t Idx = 0; Idx < EleNum; Idx++) { AllRefPos.push_back(CurPos + 8 * Idx); // 8: pointer size } diff --git a/llvm/lib/Transforms/Scalar/CJGCLiveAnalysis.cpp b/llvm/lib/Transforms/Scalar/CJGCLiveAnalysis.cpp index ef2bf44a65aa..9e616f9dbfdc 100644 --- a/llvm/lib/Transforms/Scalar/CJGCLiveAnalysis.cpp +++ b/llvm/lib/Transforms/Scalar/CJGCLiveAnalysis.cpp @@ -1174,6 +1174,17 @@ void StructLiveAnalysis::visitMemoryIntrinsic( addGCFieldsByMemory(II->getArgOperand(0), AllocaDefs, MemSize); break; } + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + case Intrinsic::cj_array_copy_generic: { + uint64_t MemSize = 0; + if (auto Size = dyn_cast(getSize(II))) { + MemSize = Size->getZExtValue(); + } + addGCFieldsByMemory(getDest(II), AllocaDefs, MemSize); + addGCFieldsByMemory(getSource(II), AllocaUses, MemSize); + break; + } case Intrinsic::lifetime_start: // lifetime_start means the begin of using a stack ptr. // we treat this ptr as a kill @@ -1214,10 +1225,10 @@ void StructLiveAnalysis::visitMemoryCallBase( Value *Arg = CB->getArgOperand(ArgIdx); Type *Ty = Arg->getType(); if (Ty->isPointerTy() && !Ty->isOpaquePointerTy()) { - if (auto *ST = - dyn_cast(Ty->getNonOpaquePointerElementType())) { - Size = static_cast(DL.getTypeAllocSize(ST).getFixedSize()); - } + auto *Pointee = Ty->getNonOpaquePointerElementType(); + if (isa(Pointee) || isa(Pointee)) + Size = + static_cast(DL.getTypeAllocSize(Pointee).getFixedSize()); } addGCFieldsByMemory(Arg, AllocaUses, Size); diff --git a/llvm/lib/Transforms/Scalar/CJIRVerifier.cpp b/llvm/lib/Transforms/Scalar/CJIRVerifier.cpp index e42071cd86e7..657cbbfd525c 100644 --- a/llvm/lib/Transforms/Scalar/CJIRVerifier.cpp +++ b/llvm/lib/Transforms/Scalar/CJIRVerifier.cpp @@ -318,6 +318,12 @@ public: (Callee->hasFnAttribute("cj2c") || Callee->hasFnAttribute("c2cj"))) return; } + if (auto *II = dyn_cast(U)) + if (II->getIntrinsicID() == Intrinsic::cj_blackhole) + return; + if (auto *II = dyn_cast(I.getOperand(0))) + if (II->getIntrinsicID() == Intrinsic::cj_blackhole) + return; } for (const User *U : I.users()) { @@ -733,7 +739,7 @@ private: SmallVector &AllRefPos) { uint64_t EleNum = AT->getNumElements(); Type *EleType = AT->getElementType(); - if (isa(EleType)) { + if (isa(EleType) && isGCPointerType(EleType)) { for (uint64_t Idx = 0; Idx < EleNum; Idx++) { AllRefPos.push_back(CurPos + 8 * Idx); // 8: pointer size } @@ -795,4 +801,4 @@ PreservedAnalyses CJIRVerifier::run(Module &M, ModuleAnalysisManager &) const { return PreservedAnalyses::none(); } return PreservedAnalyses::all(); -} \ No newline at end of file +} diff --git a/llvm/lib/Transforms/Scalar/CJRewriteStatepoint.cpp b/llvm/lib/Transforms/Scalar/CJRewriteStatepoint.cpp index 9d6a39da8705..05496c794c0e 100644 --- a/llvm/lib/Transforms/Scalar/CJRewriteStatepoint.cpp +++ b/llvm/lib/Transforms/Scalar/CJRewriteStatepoint.cpp @@ -121,6 +121,8 @@ static bool shouldRewriteStatepointsIn(Function &F); static bool rewriteCJThrowException(Module &M); +static bool deleteUnusedBlackHole(Module &M); + PreservedAnalyses CJRewriteStatepoint::run(Module &M, ModuleAnalysisManager &AM) { if (DisableCJRewrite) { @@ -152,6 +154,7 @@ PreservedAnalyses CJRewriteStatepoint::run(Module &M, M.addModuleFlag(Module::Warning, "HasRewrittenStatepoint", true); Changed |= rewriteCJThrowException(M); + Changed |= deleteUnusedBlackHole(M); if (!Changed) return PreservedAnalyses::all(); @@ -2874,6 +2877,32 @@ static bool rewriteCJThrowException(Module &M) { return true; } +void prepareBlackHoleBody(Function *F) { + F->setLinkage(GlobalValue::PrivateLinkage); + BasicBlock *BB = BasicBlock::Create(F->getContext(), "entry", F); + ReturnInst::Create(F->getContext(), F->getArg(0), BB); +} + +static bool deleteUnusedBlackHole(Module &M) { + Function *F = M.getFunction("CJ_LLVM_BlackHole"); + if (!F || F->use_empty()) + return false; + prepareBlackHoleBody(F); + SmallVector Users; + for (auto *U : F->users()) { + auto *CB = dyn_cast(U); + assert(CB != nullptr && + "User of CJ_LLVM_BlackHole must be call instruction"); + Users.push_back(CB); + } + for (auto *CB : Users) { + if (CB->use_empty()) { + CB->eraseFromParent(); + } + } + return true; +} + static void stripNonValidData(Module &M) { #ifndef NDEBUG assert(llvm::any_of(M, shouldRewriteStatepointsIn) && "precondition!"); diff --git a/llvm/lib/Transforms/Scalar/CJRuntimeLowering.cpp b/llvm/lib/Transforms/Scalar/CJRuntimeLowering.cpp index 875ab4d5d65d..9ca5aabf73d0 100644 --- a/llvm/lib/Transforms/Scalar/CJRuntimeLowering.cpp +++ b/llvm/lib/Transforms/Scalar/CJRuntimeLowering.cpp @@ -98,7 +98,12 @@ const static StdMap RuntimeMap { {Intrinsic::cj_is_subtype, "CJ_MCC_IsSubType"}, {Intrinsic::cj_is_tupletype_of, "CJ_MCC_IsTupleTypeOf"}, {Intrinsic::cj_is_typeinfo_equal, "CJ_MCC_IsTypeInfoEqual"}, - {Intrinsic::cj_set_location, "SetDebugLocation"}}; + {Intrinsic::cj_set_location, "SetDebugLocation"}, + {Intrinsic::cj_cross_access_barrier, "CJ_MCC_CrossAccessBarrier"}, + {Intrinsic::cj_get_exported_ref, "CJ_MCC_GetExportedRef"}, + {Intrinsic::cj_remove_exported_ref, "CJ_MCC_RemoveExportedRef"}, + {Intrinsic::cj_create_export_handle, "CJ_MCC_CreateExportHandle"}, + {Intrinsic::cj_blackhole, "CJ_LLVM_BlackHole"}}; struct LowerGetFieldOffset { CallBase *CI; @@ -296,27 +301,50 @@ public: // cj.heapmalloc.class (i8* bitcast (TypeInfo* @Klass.ti to i8*), i32 size) void setHeapMallocSizeAlign(CallBase *CI) { - auto HeapSizeAlign = [&](uint64_t Size) { + auto ConstantSizeAlign = [&](uint64_t Size) { Size += ObjectHeadSize; Size = (Size + 7) & (~(7)); // 8 bytes alignment CI->setArgOperand(1, ConstantInt::get(Type::getInt32Ty(C), Size)); }; - if (auto *SizeVar = dyn_cast(CI->getArgOperand(1))) { - HeapSizeAlign(SizeVar->getZExtValue()); - return; - } - auto *Klass = - dyn_cast(CI->getArgOperand(0)->stripPointerCasts()); - if (Klass && Klass->hasInitializer()) { - TypeInfo TI(Klass); - HeapSizeAlign(TI.getSize()); - return; + auto VariableSizeAlign = [&](Value *Size, IRBuilder<> &IRB) { + auto *V = IRB.CreateAnd(IRB.CreateAdd(Size, IRB.getInt32(15)), + IRB.getInt32(~7)); + CI->setArgOperand(1, V); + }; + auto GetTISizePointer = [this](Value *TI, IRBuilder<> &IRB) -> Value * { + Type *Ty = TI->getType()->getNonOpaquePointerElementType(); + if (isa(TI) || isa(Ty)) + return IRB.CreateInBoundsGEP(Ty, TI, + {IRB.getInt32(0), IRB.getInt32(CIT_SIZE)}); + auto *ST = StructType::getTypeByName(M.getContext(), "TypeInfo"); + if (!ST) + report_fatal_error("error"); + auto *SL = M.getDataLayout().getStructLayout(ST); + auto *GEP = + IRB.CreateGEP(Ty, TI, {IRB.getInt32(SL->getElementOffset(CIT_SIZE))}); + return IRB.CreateBitCast(GEP, IRB.getInt32Ty()->getPointerTo()); + }; + + auto *TI = CI->getArgOperand(0)->stripPointerCasts(); + auto *GV = dyn_cast(TI); + // In Cangjie, non-fixed size classes allow member variable extension + // compatibility, so the size needs to be explicitly loaded from typeinfo. + if (!GV || !GV->hasAttribute("can_malloc_with_fixed_size")) { + IRBuilder<> IRB(CI); + auto *Ptr = GetTISizePointer(TI, IRB); + assert(Ptr->getType()->getNonOpaquePointerElementType() == + IRB.getInt32Ty()); + auto *LI = dyn_cast(IRB.CreateLoad(IRB.getInt32Ty(), Ptr)); + LI->setDebugLoc(CI->getDebugLoc()); + return VariableSizeAlign(LI, IRB); } + if (auto *SizeVar = dyn_cast(CI->getArgOperand(1))) + return ConstantSizeAlign(SizeVar->getZExtValue()); + if (GV && GV->hasInitializer()) + return ConstantSizeAlign(TypeInfo(GV).getSize()); + IRBuilder<> IRB(CI); - // 15: 8 bytes Typeinfo* and 7 bytes for align - Value *V = IRB.CreateAdd(CI->getArgOperand(1), IRB.getInt32(15)); - Value *AlignSize = IRB.CreateAnd(V, IRB.getInt32(~(7))); // 8 bytes align - CI->setArgOperand(1, AlignSize); + VariableSizeAlign(CI->getArgOperand(1), IRB); } void replaceFixedNewObject(CallBase *&CI) { @@ -362,7 +390,7 @@ public: Function *Func = M.getFunction("CJ_MCC_NewWeakRefObject"); if (Func == nullptr) { Func = M.declareCJRuntimeFunc("CJ_MCC_NewWeakRefObject", - CI->getFunctionType(), true); + CI->getFunctionType(), false); } CI->setCalledFunction(Func); } @@ -713,8 +741,13 @@ private: StringRef Callee = getRuntimeFuncName(CI); assert(Callee != "" && "Callee don`t exist."); auto Itr = RTFuncMap.find(Callee); - if (Itr != RTFuncMap.end()) - return Itr->second; + if (Itr != RTFuncMap.end()) { + Function *RTFunc = Itr->second; + if (!RTFunc->isDeclaration()) { + RTFunc->deleteBody(); + } + return RTFunc; + } FunctionType *FT = FuncType ? FuncType : CI->getFunctionType(); Function *Func = M.declareCJRuntimeFunc(Callee, FT, GCLeafFunc, GCMalloc); @@ -728,6 +761,10 @@ private: if (Func->getName().isSetDebugLocation()) Func->setUnnamedAddr(GlobalValue::UnnamedAddr::Local); RTFuncMap[Callee] = Func; + if (CI->getIntrinsicID() == Intrinsic::cj_blackhole) { + Func->addFnAttr(Attribute::ReadNone); + Func->setCallingConv(CallingConv::AnyReg); + } return Func; } @@ -758,6 +795,7 @@ private: AI = IRB.CreateAlloca(StructType::get(C, ElemTypes)); Value *BC = IRB.CreateBitCast(AI, IRB.getInt8PtrTy()->getPointerTo()); Value *GVExpr = ConstantExpr::getBitCast(GV, IRB.getInt8PtrTy()); + IRB.SetInsertPoint(CI); IRB.CreateStore(GVExpr, BC); } else { StructType *ST = getTypeLayoutType(GV); @@ -773,9 +811,9 @@ private: // %x.payload = getelementptr i8*, %x.i, i32 1 // call @memset(%x.payload, 0, size) Value *Data = IRB.CreateGEP(IRB.getInt8PtrTy(), BC, {IRB.getInt32(1)}); + IRB.SetInsertPoint(CI); IRB.CreateMemSet(Data, IRB.getInt8(0), MemSize, Align(8)); } - IRB.SetInsertPoint(CI); Value *ASC = IRB.CreateAddrSpaceCast(AI, IRB.getInt8PtrTy(1)); CI->replaceAllUsesWith(ASC); // Set terminator for invoke instruction. @@ -924,6 +962,15 @@ static bool runtimeLoweringFunc(Function &F, CJIntrinsicLowering &Lowering) { Lowering.replaceWithRuntimeFunc(CI, true, false); Changed = true; break; + case Intrinsic::cj_blackhole: + Lowering.replaceWithRuntimeFunc(CI, true, false); + CI->setCallingConv(CallingConv::AnyReg); + Changed = true; + break; + case Intrinsic::cj_cross_access_barrier: + case Intrinsic::cj_get_exported_ref: + case Intrinsic::cj_remove_exported_ref: + case Intrinsic::cj_create_export_handle: case Intrinsic::cj_fill_in_stack_trace: Lowering.replaceWithRuntimeFunc(CI, false, false); Changed = true; diff --git a/llvm/lib/Transforms/Scalar/CJSimpleOpt.cpp b/llvm/lib/Transforms/Scalar/CJSimpleOpt.cpp index 2a4a87410006..9acf404d922f 100644 --- a/llvm/lib/Transforms/Scalar/CJSimpleOpt.cpp +++ b/llvm/lib/Transforms/Scalar/CJSimpleOpt.cpp @@ -24,6 +24,7 @@ #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instruction.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/PatternMatch.h" @@ -124,8 +125,12 @@ struct CallRetState { } bool Changed = false; SetVector Memsets; + SetVector FuncInsts; for (auto I = inst_begin(F); I != inst_end(F);) { - auto *CB = dyn_cast(&*I++); + FuncInsts.insert(&*I++); + } + for (auto *FuncInst : FuncInsts) { + auto *CB = dyn_cast(FuncInst); if (CB == nullptr || CB->getArgOperandWithAttribute(Attribute::StructRet) == nullptr) continue; @@ -195,9 +200,15 @@ struct CallRetState { bool replaceCJCallRet(CallRetUnit &CRU, GetElementPtrInst *GEP, SetVector &Memsets) { if (GEP != nullptr) { - if (CRU.CJMemset == nullptr) - GEP->moveBefore(CRU.CB); - else + if (CRU.CJMemset == nullptr) { + auto *CallRetWithoutCast = + dyn_cast(CRU.CB->getArgOperand(0)); + if (CallRetWithoutCast == CRU.CallRet) { + GEP->moveBefore(CRU.CB); + } else { + GEP->moveBefore(CallRetWithoutCast); + } + } else return false; } if (CRU.CJMemset != nullptr && !PDT.dominates(CRU.CB, CRU.CJMemset)) @@ -470,6 +481,7 @@ struct MutexLockLower { Attribute::get(F->getContext(), "gc-leaf-function")); GetCJThreadIdFunc->addFnAttr(Attribute::get(F->getContext(), "cj-runtime")); GetCJThreadIdFunc->setCallingConv(CallingConv::CangjieGC); + GetCJThreadIdFunc->setUnnamedAddr(GlobalValue::UnnamedAddr::Local); auto *CallInst = IRB.CreateCall(GetCJThreadIdFunc); CallInst->setCallingConv(CallingConv::CangjieGC); auto *StoreInst = IRB.CreateStore(CallInst, MutexCjthreadIdPtr); diff --git a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp index 81a64bd8b360..8396f6e5ba10 100644 --- a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp +++ b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp @@ -94,11 +94,6 @@ STATISTIC(NumLFTR , "Number of loop exit tests replaced"); STATISTIC(NumElimExt , "Number of IV sign/zero extends eliminated"); STATISTIC(NumElimIV , "Number of congruent IVs eliminated"); -namespace llvm { -extern cl::opt CJPipeline; -extern cl::opt EnableCJIRCEPass; -} // namespace llvm - // Trip count verification can be enabled by default under NDEBUG if we // implement a strong expression equivalence checker in SCEV. Until then, we // use the verify-indvars flag, which may assert in some cases. @@ -1973,11 +1968,7 @@ bool IndVarSimplify::run(Loop *L) { SE->forgetLoop(L); } - // If we have a trip count expression, rewrite the loop's exit condition - // using it. - // In cangjie, close LFTR because it affects IRCE. - bool LFTREnable = CJPipeline ? !EnableCJIRCEPass && !DisableLFTR : !DisableLFTR; - if (LFTREnable) { + if (!DisableLFTR) { BasicBlock *PreHeader = L->getLoopPreheader(); SmallVector ExitingBlocks; diff --git a/llvm/test/Transforms/CJRewriteStatepoint/cj-blackhole.ll b/llvm/test/Transforms/CJRewriteStatepoint/cj-blackhole.ll new file mode 100644 index 000000000000..925491b51359 --- /dev/null +++ b/llvm/test/Transforms/CJRewriteStatepoint/cj-blackhole.ll @@ -0,0 +1,73 @@ +;RUN: opt < %s -passes=cj-rewrite-statepoint -S 2>&1 | FileCheck %s + +%TypeInfo = type { i8*, i8, i8, i16, i32, %BitMap*, i32, i8, i8, i16, i32*, i8*, i8*, i8*, %TypeInfo*, %ExtensionDef**, i8*, i8* } +%BitMap = type { i32, [0 x i8] } +%ExtensionDef = type { i32, i8, i8*, i8*, i8*, i8* } +%Unit.Type = type {} + +@"default:cc.ti" = global %TypeInfo { i8* null, i8 -128, i8 64, i16 2, i32 16, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i32 0, i8 8, i8 0, i16 -32768, i32* null, i8* null, i8* null, i8* null, %TypeInfo* null, %ExtensionDef** null, i8* null, i8* null } + +declare i8* @CJ_LLVM_BlackHole(i8* %0) + +declare cangjiegccc void @CJ_MCC_HandleSafepoint() + +declare void @CJ_MCC_StackCheck() + +declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) + +declare i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) + +; CHECK-LABEL: _CN7default3fooHv( +define i64 @_CN7default3fooHv() { +allocas: + call cangjiegccc void @CJ_MCC_StackCheck() + %b = alloca i64, align 8 + %c = alloca i64, align 8 + store i64 5, i64* %b, align 8 + %0 = bitcast i64* %b to i8* + call cangjiegccc void @CJ_MCC_HandleSafepoint() + %1 = call i8* @CJ_LLVM_BlackHole(i8* nonnull %0) + %2 = bitcast i8* %1 to i64* + %3 = load i64, i64* %2, align 8 + %4 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %3, i64 1) + %.fca.1.extract = extractvalue { i64, i1 } %4, 1 + br i1 %.fca.1.extract, label %codeRepl, label %normal + +normal: ; preds = %allocas + %.fca.0.extract = extractvalue { i64, i1 } %4, 0 + store i64 %.fca.0.extract, i64* %c, align 8 + %5 = bitcast i64* %c to i8* +; CHECK-NOT: %6 = call i8* @CJ_LLVM_BlackHole(i8* nonnull %5) + %6 = call i8* @CJ_LLVM_BlackHole(i8* nonnull %5) + ret i64 0 + +codeRepl: ; preds = %allocas + unreachable +} + +; CHECK-LABEL: _CN7default4foo2HCNY_1AE( +define i8 addrspace(1)* @_CN7default4foo2HCNY_1AE(i8 addrspace(1)* %a) gc "cangjie" { +allocas: + call cangjiegccc void @CJ_MCC_StackCheck() + %0 = alloca %Unit.Type, align 8 + call cangjiegccc void @CJ_MCC_HandleSafepoint() + %1 = tail call noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8* bitcast (%TypeInfo* @"default:cc.ti" to i8*), i32 24) + %2 = getelementptr i8, i8 addrspace(1)* %1, i64 8 + %3 = bitcast i8 addrspace(1)* %2 to <2 x i64> addrspace(1)* + store <2 x i64> , <2 x i64> addrspace(1)* %3, align 8 + %4 = bitcast i8 addrspace(1)* %a to %TypeInfo* addrspace(1)* + %ti = load %TypeInfo*, %TypeInfo* addrspace(1)* %4, align 8 + %5 = getelementptr %TypeInfo, %TypeInfo* %ti, i64 0, i32 15 + %6 = load %ExtensionDef**, %ExtensionDef*** %5, align 8 + %7 = load %ExtensionDef*, %ExtensionDef** %6, align 8 + %8 = getelementptr %ExtensionDef, %ExtensionDef* %7, i64 0, i32 5 + %9 = bitcast i8** %8 to void (%Unit.Type*, i8 addrspace(1)*, i8 addrspace(1)*, %TypeInfo*)*** + %10 = load void (%Unit.Type*, i8 addrspace(1)*, i8 addrspace(1)*, %TypeInfo*)**, void (%Unit.Type*, i8 addrspace(1)*, i8 addrspace(1)*, %TypeInfo*)*** %9, align 8 + %11 = load void (%Unit.Type*, i8 addrspace(1)*, i8 addrspace(1)*, %TypeInfo*)*, void (%Unit.Type*, i8 addrspace(1)*, i8 addrspace(1)*, %TypeInfo*)** %10, align 8 +; CHECK: %token7 = call token (...) @llvm.cj.gc.statepoint(i64 0, i32 0, void (%Unit.Type*, i8 addrspace(1)*, i8 addrspace(1)*, %TypeInfo*)* %11, i32 4, i32 0, %Unit.Type* nonnull sret(%Unit.Type) %0, i8 addrspace(1)* %a.reloc5, i8 addrspace(1)* %1, %TypeInfo* %ti) [ "gc-live"(i8 addrspace(1)* %1, i8 addrspace(1)* %a.reloc5) ] + call void %11(%Unit.Type* noalias nonnull sret(%Unit.Type) %0, i8 addrspace(1)* %a, i8 addrspace(1)* %1, %TypeInfo* %ti) + %12 = addrspacecast i8 addrspace(1)* %1 to i8* + %13 = call i8* @CJ_LLVM_BlackHole(i8* %12) + %14 = addrspacecast i8* %13 to i8 addrspace(1)* + ret i8 addrspace(1)* %14 +} diff --git a/llvm/test/Transforms/CJRuntimeLowering/alloca_generic.ll b/llvm/test/Transforms/CJRuntimeLowering/alloca_generic.ll index 28518bfb1594..5041c0e1010d 100644 --- a/llvm/test/Transforms/CJRuntimeLowering/alloca_generic.ll +++ b/llvm/test/Transforms/CJRuntimeLowering/alloca_generic.ll @@ -68,6 +68,6 @@ bb0: ret void } -attributes #0 = { "CFileKlass" } +attributes #0 = { "CFileKlass" "can_malloc_with_fixed_size" } !0 = !{!"Int64.Type"} diff --git a/llvm/test/Transforms/CJRuntimeLowering/new_finalier.ll b/llvm/test/Transforms/CJRuntimeLowering/new_finalier.ll index ad996078dd31..2b5883163bc8 100644 --- a/llvm/test/Transforms/CJRuntimeLowering/new_finalier.ll +++ b/llvm/test/Transforms/CJRuntimeLowering/new_finalier.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -passes=cj-runtime-lowering -S | FileCheck %s +%TypeInfo = type { i8*, i8, i8, i16, i32, i8*, i32, i8, i8, i32*, i8*, i8*, i8*, i8*, i8*, i8* } declare i8 addrspace(1)* @llvm.cj.malloc.object(i8*, i32) diff --git a/llvm/test/Transforms/CJRuntimeLowering/runtime_func_attribute.ll b/llvm/test/Transforms/CJRuntimeLowering/runtime_func_attribute.ll index 3b94c870eda8..32df63570001 100644 --- a/llvm/test/Transforms/CJRuntimeLowering/runtime_func_attribute.ll +++ b/llvm/test/Transforms/CJRuntimeLowering/runtime_func_attribute.ll @@ -1,32 +1,36 @@ -; RUN: opt < %s -passes=cj-runtime-lowering -S | FileCheck %s - -declare i8 addrspace(1)* @llvm.cj.malloc.object(i8*, i32) - -; Function Attrs: nounwind -declare i8* @llvm.cj.get.exception.wrapper() #0 - -; Function Attrs: nounwind -declare i8 addrspace(1)* @llvm.cj.post.throw.exception(i8*) #0 - -declare void @llvm.cj.throw.exception(i8 addrspace(1)*) - -; Transfer the original function's attributes. - -; CHECK: declare noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) #2 -; CHECK: declare i8* @CJ_MCC_GetExceptionWrapper() #3 -; CHECK: declare i8 addrspace(1)* @CJ_MCC_PostThrowException(i8*) #3 -; CHECK: declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) #1 -; CHECK: attributes #1 = { "cj-runtime" "gc-leaf-function" } -; CHECK: attributes #2 = { argmemonly "cj-heapmalloc" "cj-runtime" } -; CHECK: attributes #3 = { nounwind "cj-runtime" "gc-leaf-function" } -; - -define void @lower_new_finalier(i8* %ti) { - %obj = call noalias i8 addrspace(1)* @llvm.cj.malloc.object(i8* %ti, i32 32) - %e = call i8* @llvm.cj.get.exception.wrapper() - %e_obj = call i8 addrspace(1)* @llvm.cj.post.throw.exception(i8* %e) - call void @llvm.cj.throw.exception(i8 addrspace(1)* %e_obj) - ret void -} - -attributes #0 = { nounwind } +; RUN: opt < %s -passes=cj-runtime-lowering -S | FileCheck %s +%TypeInfo = type { i8*, i8, i8, i16, i32, i8*, i32, i8, i8, i32*, i8*, i8*, i8*, i8*, i8*, i8* } + +declare i8 addrspace(1)* @llvm.cj.malloc.object(i8*, i32) + +; Function Attrs: nounwind +declare i8* @llvm.cj.get.exception.wrapper() #0 + +; Function Attrs: nounwind +declare i8 addrspace(1)* @llvm.cj.post.throw.exception(i8*) #0 + +declare void @llvm.cj.throw.exception(i8 addrspace(1)*) + +; Transfer the original function's attributes. + +; CHECK: %3 = load i32, i32* %2, align 4 +; CHECK: %4 = add i32 %3, 15 +; CHECK %5 = and i32 %4, -8 +; CHECK: declare noalias i8 addrspace(1)* @CJ_MCC_NewObject(i8*, i32) #2 +; CHECK: declare i8* @CJ_MCC_GetExceptionWrapper() #3 +; CHECK: declare i8 addrspace(1)* @CJ_MCC_PostThrowException(i8*) #3 +; CHECK: declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) #1 +; CHECK: attributes #1 = { "cj-runtime" "gc-leaf-function" } +; CHECK: attributes #2 = { argmemonly "cj-heapmalloc" "cj-runtime" } +; CHECK: attributes #3 = { nounwind "cj-runtime" "gc-leaf-function" } +; + +define void @lower_new_finalier(i8* %ti) { + %obj = call noalias i8 addrspace(1)* @llvm.cj.malloc.object(i8* %ti, i32 32) + %e = call i8* @llvm.cj.get.exception.wrapper() + %e_obj = call i8 addrspace(1)* @llvm.cj.post.throw.exception(i8* %e) + call void @llvm.cj.throw.exception(i8 addrspace(1)* %e_obj) + ret void +} + +attributes #0 = { nounwind } cangjie_compiler-1.0.7/unittests/000077500000000000000000000000001510705540100170515ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/AST/000077500000000000000000000000001510705540100175005ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/AST/ASTToSourceTest.cpp000066400000000000000000000074371510705540100231720ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include "gtest/gtest.h" #include "cangjie/AST/ASTCasting.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Node.h" #include "cangjie/Parse/Parser.h" using namespace Cangjie; using namespace AST; class ASTToSourceTest : public testing::Test { protected: void SetUp() override { } #ifdef PROJECT_SOURCE_DIR // Gets the absolute path of the project from the compile parameter. std::string projectPath = PROJECT_SOURCE_DIR; #else // Just in case, give it a default value. // Assume the initial is in the build directory. std::string projectPath = ".."; #endif std::string srcPath; DiagnosticEngine diag; SourceManager sm; }; TEST_F(ASTToSourceTest, VarDeclToString) { std::string srcVarDecl = R"(public let a = "hello world!")"; Parser* parser = new Parser(srcVarDecl, diag, sm); OwnedPtr file = parser->ParseTopLevel(); EXPECT_EQ(srcVarDecl, file->decls[0]->ToString()); srcVarDecl = R"(var cc = "hello world!")"; parser = new Parser(srcVarDecl, diag, sm); file = parser->ParseTopLevel(); EXPECT_EQ(srcVarDecl, file->decls[0]->ToString()); srcVarDecl = R"(public let a : String = "hello world!")"; parser = new Parser(srcVarDecl, diag, sm); file = parser->ParseTopLevel(); EXPECT_EQ(srcVarDecl, file->decls[0]->ToString()); // Comments. srcVarDecl = R"(public/*foo*/ let a/*ty infer*/ = "hello world!")"; parser = new Parser(srcVarDecl, diag, sm); file = parser->ParseTopLevel(); Ptr vd = RawStaticCast(file->decls[0].get()); sm.AddSource("", srcVarDecl); Source& source = sm.GetSource(1); size_t origin = 0; auto comments = parser->GetCommentsMap()[0]; std::unordered_map commentsInside; for (auto& comment : comments) { if (comment.Begin().line >= vd->end.line) { commentsInside.insert_or_assign(source.PosToOffset(comment.Begin()) - origin, comment); } } std::string result = vd->ToString(); auto it = result.begin(); for (auto& it1 : commentsInside) { int i = 0; for (auto& ch : it1.second.Value()) { *(it + it1.first + i) = ch; i++; } } EXPECT_EQ(result, srcVarDecl); } TEST_F(ASTToSourceTest, CallExprToString) { std::string srcCallExpr = R"(systemlib.TitleBarObj( text: "Rune UI Demo", textColor: "#ffffff", backgroundColor: "#007dff", backgroundOpacity: 0.5, isMenu: true ))"; Parser* parser = new Parser(srcCallExpr, diag, sm); OwnedPtr ce = parser->ParseExpr(); EXPECT_EQ(srcCallExpr, ce->ToString()); } TEST_F(ASTToSourceTest, ArrayLitToString) { std::string srcArrayLit = "[ 20.px() , 0.px(), 20.px(), 0.px()]"; Parser* parser = new Parser(srcArrayLit, diag, sm); OwnedPtr al = parser->ParseExpr(); EXPECT_EQ(srcArrayLit, al->ToString()); } TEST_F(ASTToSourceTest, ToStringCov) { // NOTE: only for coverage now. 'ToString' method may be removed. auto pointerExpr = CreateUniquePtr(); pointerExpr->type = CreateRefType("Type"); pointerExpr->arg = CreateFuncArg(CreateRefExpr("name")); auto pStr = pointerExpr->ToString(); EXPECT_FALSE(pStr.empty()); std::string srcArrayExpr = "VArray(repeat: 1)"; Parser* parser = new Parser(srcArrayExpr, diag, sm); OwnedPtr al = parser->ParseExpr(); EXPECT_FALSE(al->ToString().empty()); } cangjie_compiler-1.0.7/unittests/AST/CMakeLists.txt000066400000000000000000000033741510705540100222470ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. include_directories(${CMAKE_SOURCE_DIR}/src/Sema) include_directories(${CMAKE_SOURCE_DIR}/src/AST) add_executable(WalkerTest WalkerTest.cpp) target_link_libraries( WalkerTest ${CMAKE_DL_LIBS} cangjie-lsp GTest::gtest GTest::gtest_main) add_test(NAME WalkerTest COMMAND WalkerTest) add_executable(CloneTest CloneTest.cpp) target_link_libraries( CloneTest ${CMAKE_DL_LIBS} $ cangjie-lsp GTest::gtest GTest::gtest_main) add_test(NAME CloneTest COMMAND CloneTest) add_executable(CreateTest CreateTest.cpp) target_link_libraries( CreateTest ${CMAKE_DL_LIBS} $ cangjie-lsp GTest::gtest GTest::gtest_main) add_test(NAME CreateTest COMMAND CreateTest) add_executable(SearchTest SearchTest.cpp) target_link_libraries( SearchTest cangjie-lsp ${LINK_LIBS} boundscheck-static GTest::gtest GTest::gtest_main TestCompilerInstanceObject) add_test(NAME SearchTest COMMAND SearchTest) add_executable(SearchPlusTest SearchPlusTest.cpp) target_link_libraries( SearchPlusTest cangjie-lsp ${LINK_LIBS} boundscheck-static GTest::gtest GTest::gtest_main TestCompilerInstanceObject) add_test(NAME SearchPlusTest COMMAND SearchPlusTest) add_executable(ASTToSourceTest ASTToSourceTest.cpp) target_link_libraries( ASTToSourceTest ${CMAKE_DL_LIBS} cangjie-lsp GTest::gtest GTest::gtest_main) add_test(NAME ASTToSourceTest COMMAND ASTToSourceTest) cangjie_compiler-1.0.7/unittests/AST/CangjieFiles/000077500000000000000000000000001510705540100220235ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/AST/CangjieFiles/collector/000077500000000000000000000000001510705540100240115ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/AST/CangjieFiles/collector/PrimaryCtorHighlightTest/000077500000000000000000000000001510705540100307545ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/AST/CangjieFiles/collector/PrimaryCtorHighlightTest/primaryCtor.cj000066400000000000000000000010401510705540100336000ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. class Test { Test(aa: Int64, bb: Int64) { aa + bb } } class STR { public func isEmpty(): Int64 { return 0 } } class C2 { public C2(a: String, var b1!: String = "") { a.isEmpty() var SSS: STR = STR() SSS.isEmpty() } } cangjie_compiler-1.0.7/unittests/AST/CangjieFiles/collector/SimpleSearchTest/000077500000000000000000000000001510705540100272305ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/AST/CangjieFiles/collector/SimpleSearchTest/demo1.cj000066400000000000000000000041641510705540100305600ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. extend Eq { func eq(a : this, b : this) : Bool{} } func foo(a : X , b : X) where X <: Eq{ return eq(a,b) } func Foo(a: T, b: U): T { return a } func Bar(a:T) { var b: Int32 = 1 var c = Foo(a, b) return c } public struct Test { public let member1 : Int32 public var member2: T public init(a:Int32, b: T) { member1 = a member2 = b } public func testNormal(a: Bool) : Bool { return a } public func testGeneric1(t:T): T { return t } public func testGeneric2(t:T, j:Bool): Bool { return j } } extend Add { func add(a: this): this } public struct R1 where T <: Add { public let a: T public func test(): T { return a } public func test1(t: T): T { return t } } public struct R2 where T <: Add { public let a: T public init(x: Int32, b: T) { a = b } public init(b: T) { a = b } } main(): Unit { var title: String = "world" var t:Int32 /* abcd efg */ let test = Page( component: Div( content: [ Text( content : """ Hello """ , style : TextStyle( width : #" 500 "# , height: 100, textOverflow: a.b, lines: 1, flex: [1, 0, 0], fontSize: 1.px(), color: Color(0, 0, 0), margin: [1.px(), 1.px(), 1.px(), 1.px()], fontWeight: FontWeight.normal ) ), // hijknmn Text( hahaha: "11111111111", fontWeight: """ 200 """, content: title ) ] // uvwxyz ) ) test.Build() } cangjie_compiler-1.0.7/unittests/AST/CangjieFiles/pkgs/000077500000000000000000000000001510705540100227675ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/AST/CangjieFiles/pkgs/testfile_search_0301n.cj000066400000000000000000000037301510705540100272750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. package pkgs func run(): Int64 { var day: Time = Time() day.foo(5) var a: Student = Student(1) var v2: Int8 = 2 var v3: Int16 = 3 var v4: Int32 = 4 let x: Int8 = chooseOne(a, b, c, d) var i: Float16 = 6.104e-5 var time = Time.Day(i) var res = match (time) { case Day(x) => x } return 0 } open class Time <: Day { override func foo(x: Int32) { //此处override的是父类实现的接口中的函数,不是父类重写的函数 print("Time") } } abstract class Day <: Fruit { override func foo(x: Int32) { print("Day") } func aoo(x: Int32): Int32 } interface Fruit { func foo(x: Int32) { print("Fruit") } } class Monday <: Time { let m: Int32 init(x: Int32) { m = x } } class Tuesday <: Time { let m: Int32 init(x: Int32) { m = x + 1 } } struct abcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxy {} var Dey: Int64 = 6 var Day: Int32 = 5 var a: Day = Day() // 此处Day的name有3个:定义的类型Day,引用Day,和访问Day() func Day(x: Monday, y: Tuesday): Int32 { return x.m + y.m } class Day { var x: Int32 = 1 var y: Int32 = 2 var z: String = """ func Day(x:Monday,y:Tuesday):Int32{ return x.m + y.m }""" func Tuesday(a: Int32, b: Int32): Int32 { var m: Int32 = a * 2 var n: Int32 = b * 2 return Monday().Time(m + n) } } struct Student { var s: K init(x: K) { s = x } } enum TimeUnit { Year(Int32) } var time = TimeUnit.Year(2020) var res: Int32 = match (time) { case Year(x) => x } enum Time { Day(T) } cangjie_compiler-1.0.7/unittests/AST/CangjieFiles/pkgs/testfile_search_0302n.cj000066400000000000000000000037301510705540100272760ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. package pkgs func run(): Int64 { var day: Time = Time() day.foo(5) var a: Student = Student(1) var v2: Int8 = 2 var v3: Int16 = 3 var v4: Int32 = 4 let x: Int8 = chooseOne(a, b, c, d) var i: Float16 = 6.104e-5 var time = Time.Day(i) var res = match (time) { case Day(x) => x } return 0 } open class Time <: Day { override func foo(x: Int32) { //此处override的是父类实现的接口中的函数,不是父类重写的函数 print("Time") } } abstract class Day <: Fruit { override func foo(x: Int32) { print("Day") } func aoo(x: Int32): Int32 } interface Fruit { func foo(x: Int32) { print("Fruit") } } class Monday <: Time { let m: Int32 init(x: Int32) { m = x } } class Tuesday <: Time { let m: Int32 init(x: Int32) { m = x + 1 } } struct abcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxy {} var Dey: Int64 = 6 var Day: Int32 = 5 var a: Day = Day() // 此处Day的name有3个:定义的类型Day,引用Day,和访问Day() func Day(x: Monday, y: Tuesday): Int32 { return x.m + y.m } class Day { var x: Int32 = 1 var y: Int32 = 2 var z: String = """ func Day(x:Monday,y:Tuesday):Int32{ return x.m + y.m }""" func Tuesday(a: Int32, b: Int32): Int32 { var m: Int32 = a * 2 var n: Int32 = b * 2 return Monday().Time(m + n) } } struct Student { var s: K init(x: K) { s = x } } enum TimeUnit { Year(Int32) } var time = TimeUnit.Year(2020) var res: Int32 = match (time) { case Year(x) => x } enum Time { Day(T) } cangjie_compiler-1.0.7/unittests/AST/CangjieFiles/testfile_search_01n.cj000066400000000000000000000040451510705540100261660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. main(): Int64 { return run() } func run(): Int64 { var day :Time = Time() day.foo(5) var a: Student = Student(1) var v2: Int8 = 2 var v3: Int16 = 3 var v4: Int32 = 4 let x: Int8 = chooseOne(a,b,c,d) var i: Float16 = 6.104e-5 var time = Time.Day(i) var res = match (time){ case Day(x) => x } return 0 } open class Time <: Day { override func foo(x:Int32){ //此处override的是父类实现的接口中的函数,不是父类重写的函数 print("Time") } } abstract class Day <: Fruit{ override func foo(x:Int32){ print("Day") } func aoo(x:Int32):Int32 } interface Fruit{ func foo(x:Int32){ print("Fruit") } } class Monday <: Time{ let m: Int32 init(x: Int32){ m = x } } class Tuesday <: Time{ let m: Int32 init(x: Int32){ m = x+1 } } struct abcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxy{} var Dey: Int64 = 6 var Day: Int32 = 5 var a: Day = Day() // 此处Day的name有3个:定义的类型Day,引用Day,和访问Day() func Day(x:Monday,y:Tuesday):Int32{ return x.m + y.m } class Day { var x:Int32 = 1 var y:Int32 = 2 var z:String = """ func Day(x:Monday,y:Tuesday):Int32{ return x.m + y.m }""" class Monday{ func Time(a:Int32,b!:Int32=10):Int32{ return a+b } } func Tuesday(a:Int32,b:Int32):Int32{ var m:Int32 = a*2 var n:Int32 = b*2 return Monday().Time(m+n) } } struct Student { var s: K init(x: K){ s = x } } enum TimeUnit { Year(Int32) } var time = TimeUnit.Year(2020) var res: Int32 = match (time) { case Year(x) => x } enum Time{ Day(T) } cangjie_compiler-1.0.7/unittests/AST/CangjieFiles/testfile_search_02n.cj000066400000000000000000000072131510705540100261670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. main(): Int64 { return run() } func run(): Int64 { var day :Right = Right() print(Right.Monday(1,3).toString()) // 231 return 0 } class Wrong{ var a:Int32 = 1 var b:String = #"xyz\nxyz"# var c:String = """ lmn\nlmn""" class Monday{ var a:Int32 = 1 func monday(a:Int32,b!:Int32=10):Int32{ return a+b } class Tuesday{ var a:Int32 =1 func tuesday(a:Int32,b!:Int32=1):Int32{ return (a+b)*2 } class Wednesday{ var a:Int32 =1 func wednesday(a:Int32,b!:Int32=1):Int32{ return (a+b)*3 } class Thursday{ var a:Int32 =1 func thurday(a:Int32,b!:Int32=1):Int32{ return (a+b)*4 } class Friday{ var a:Int32 =1 func friday(a:Int32,b!:Int32=1):Int32{ return (a+b)*5 } class Saturday{ var a:Int32 =1 func saturday(a:Int32,b!:Int32=1):Int32{ return (a+b)*6 } class Sunday{ var a:Int32 =1 func sunday(a:Int32,b!:Int32=1):Int32{ func sunda(a:Int32,b!:Int32=1):Int32{ func sund(a:Int32,b!:Int32=1){ func sun(a:Int32,b!:Int32=1):Int32{ func su(a:Int32,b!:Int32=1){ var c:Int32 = (a+b)*11 return c } var c:Int32 = (a+b)*10 + su(a) return c } var c:Int32 = (a+b)*9 + sun(a) return c } var c:Int32 = (a+b)*8 + sund(a) return c } var c:Int32 = (a+b)*7 + sunda(a) return c } var b: Int32 = 2 } var b: Int32 = 2 } var b: Int32 = 2 } var b: Int32 = 2 } var b: Int32 = 2 } var b: Int32 = 2 } var b: Int32 = 2 } var d: Int32 = 2 } class Right{ var a:Int32 = 1 var b:String = #"xyz\nxyz"# var c:String = """ lmn\nlmn""" static func Monday(a:Int32,b:Int32):Int32{ var m:Int32 = a*2 var n:Int32 = b*2 return Tuesday(m,b:a,a:b) } static func Tuesday(o:Int32,a!:Int32=2,b!:Int32=3):Int32{ return o*100+a*10+b } } cangjie_compiler-1.0.7/unittests/AST/CloneTest.cpp000066400000000000000000000105511510705540100221060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include "gtest/gtest.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/PrintNode.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Parse/Parser.h" using namespace Cangjie; using namespace AST; class CloneTest : public testing::Test { protected: void SetUp() override { parser = new Parser(code, diag, sm); file = parser->ParseTopLevel(); } std::string code = R"( let clockPort = 12 let dataPort = 5 let ledNum = 64 // led number let lightColor = 0xffff0000 // led light color -> b: 255, g: 0, r: 0 // for LED show var pos : int = 0 // LED position var leds : int[] // c libary api ======= fake FFI func print() : unit {} func print(str : String) : unit {} func sleep(inv : int) : unit {} func OpenGPIO(pin : int) : unit {} func WriteGPIO(pin : int, val : int) : unit {} func SetWord(clkPort : int, dataPort : int, val : int) : unit {} // Util function func CDW(val : int) : unit { SetWord(clockPort, dataPort, val) } // Set the global array func SetChaserPattern() : unit { leds[pos] = lightColor pos = (pos + 1) % ledNum; } // Show LED: Right Shift Zero func ShowLED(leds : int, lightPWM : int) : unit { CDW(0) lightPWM = 0xFF000000 CDW(0xffffffff) } func StartChaserMode() { while (true) { SetChaserPattern() ShowLED(leds, 0xFF000000) sleep(50) // fake sleep } } main() : int { print("hello world") // Initialize GPIO OpenGPIO(clockPort) WriteGPIO(clockPort, 1) OpenGPIO(dataPort) // Show LED print("Start Marquee...") StartChaserMode() return 0 } )"; Parser* parser; DiagnosticEngine diag; SourceManager sm; OwnedPtr file; }; namespace { /// Match AST nodes according to Node Type, and return matched nodes. template std::vector> MatchASTByNode(Ptr node) { std::vector> ret{}; if (!node) { return ret; } Walker walker(node, [&ret](Ptr node) -> VisitAction { if (dynamic_cast(node.get())) { ret.push_back(node); } return VisitAction::WALK_CHILDREN; }); walker.Walk(); return ret; } } // namespace TEST_F(CloneTest, CloneExpr) { std::vector> binaryExprs = MatchASTByNode(file.get()); for (auto& it : binaryExprs) { PrintNode(ASTCloner::Clone(Ptr(As(it))).get()); EXPECT_TRUE(Is(ASTCloner::Clone(Ptr(As(it))).get())); } std::vector> callExprs = MatchASTByNode(file.get()); for (auto& it : callExprs) { PrintNode(ASTCloner::Clone(Ptr(As(it))).get()); EXPECT_TRUE(Is(ASTCloner::Clone(Ptr(As(it))).get())); } } TEST_F(CloneTest, CloneDecl) { std::vector> varDecls = MatchASTByNode(file.get()); for (auto& it : varDecls) { PrintNode(ASTCloner::Clone(Ptr(As(it))).get()); EXPECT_TRUE(Is(ASTCloner::Clone(Ptr(As(it))).get())); } std::vector> funcDecls = MatchASTByNode(file.get()); for (auto& it : funcDecls) { PrintNode(ASTCloner::Clone(Ptr(As(it))).get()); EXPECT_TRUE(Is(ASTCloner::Clone(Ptr(As(it))).get())); } } TEST_F(CloneTest, CloneBlock) { std::vector> blocks = MatchASTByNode(file.get()); for (auto& it : blocks) { PrintNode(it); PrintNode(ASTCloner::Clone(Ptr(As(it))).get()); EXPECT_TRUE(Is(ASTCloner::Clone(Ptr(As(it))).get())); } } cangjie_compiler-1.0.7/unittests/AST/CreateTest.cpp000066400000000000000000000107221510705540100222510ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/AST/Create.h" #include #include "gtest/gtest.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/PrintNode.h" #include "cangjie/Parse/Parser.h" using namespace Cangjie; using namespace AST; namespace { /** Create Expr node according to source code. */ OwnedPtr CreateExprASTFromSrc(const std::string& src, DiagnosticEngine& diag, const Position& pos = {}) { Parser parser(src, diag, diag.GetSourceManager(), pos); return parser.ParseExpr(); } } // namespace TEST(CreateTest, CreateASTFromSrc) { std::string src{"a=b+c"}; DiagnosticEngine diag; SourceManager sm; sm.AddSource("./", src); diag.SetSourceManager(&sm); auto expr = CreateExprASTFromSrc(src, diag); PrintNode(expr.get()); EXPECT_TRUE(Is(expr.get())); } TEST(CreateTest, CreateBinaryExpr) { auto binaryExpr = CreateBinaryExpr(MakeOwned(), MakeOwned(), TokenKind::ADD); PrintNode(binaryExpr.get()); EXPECT_TRUE(Is(binaryExpr.get())); } TEST(CreateTest, CreateReturnExpr) { auto expr1 = CreateBinaryExpr(MakeOwned(), MakeOwned(), TokenKind::ADD); auto expr2 = CreateReturnExpr(std::move(expr1)); EXPECT_TRUE(Is(expr2.get())); } TEST(CreateTest, CreateBlock) { std::vector> nodes1; nodes1.emplace_back(nullptr); nodes1.emplace_back(nullptr); auto block1 = CreateBlock(std::move(nodes1)); PrintNode(block1.get()); EXPECT_TRUE(Is(block1.get())); DiagnosticEngine diag; SourceManager sm; diag.SetSourceManager(&sm); std::vector> nodes2; nodes2.emplace_back(MakeOwned()); nodes2.emplace_back(CreateExprASTFromSrc("c+d*e**f/6", diag)); nodes2.emplace_back(nullptr); nodes2.emplace_back(MakeOwned()); auto block2 = CreateBlock(std::move(nodes2)); PrintNode(block2.get()); EXPECT_TRUE(Is(block2.get())); } TEST(CreateTest, CreateFuncDecl) { auto param1 = CreateFuncParam("a"); auto param2 = CreateFuncParam("b", MakeOwned()); auto param3 = CreateFuncParam("c", MakeOwned(), MakeOwned()); EXPECT_TRUE(Is(param1.get())); EXPECT_TRUE(Is(param2.get())); EXPECT_TRUE(Is(param3.get())); auto paramList = CreateFuncParamList({param1.get(), param2.get(), param3.get()}); EXPECT_TRUE(Is(paramList.get())); DiagnosticEngine diag; SourceManager sm; diag.SetSourceManager(&sm); std::vector> nodes; nodes.emplace_back(MakeOwned()); nodes.emplace_back(CreateExprASTFromSrc("c+d*e**f/6", diag)); nodes.emplace_back(nullptr); nodes.emplace_back(MakeOwned()); auto body = CreateBlock(std::move(nodes)); EXPECT_TRUE(Is(body.get())); std::vector> paramLists; paramLists.emplace_back(std::move(paramList)); auto funcBody = CreateFuncBody(std::move(paramLists), MakeOwned(), std::move(body)); EXPECT_TRUE(Is(funcBody.get())); auto funcDecl = CreateFuncDecl("test", std::move(funcBody)); EXPECT_TRUE(Is(funcDecl.get())); PrintNode(funcDecl.get()); } #ifndef _WIN32 TEST(CreateTest, CheckASTSize) { /** * This testcase is used to remind developer to update related methods in "Clone.cpp" "Create.cpp" * and other related project code like "fmt", "lsp", "codecheck" and other tools. * NOTE: If any node size check failed, please notify and check code which is mentioned above. * After all related usages have been checked, just update the size written in "ASTKind.inc". */ size_t size; #define ASTKIND(KIND, VALUE, NODE, SIZE) \ do { \ size = sizeof(NODE); \ EXPECT_EQ(size, SIZE); \ } while (0); #include "cangjie/AST/ASTKind.inc" #undef ASTKIND } #endif cangjie_compiler-1.0.7/unittests/AST/SearchPlusTest.cpp000066400000000000000000000240021510705540100231130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/AST/Searcher.h" #include #include #include "Collector.h" #include "TestCompilerInstance.h" #include "cangjie/Parse/Parser.h" #include "gtest/gtest.h" using namespace Cangjie; using namespace AST; class SearchPlusTest : public testing::Test { protected: void SetUp() override { #ifdef _WIN32 srcPath = projectPath + "\\unittests\\AST\\CangjieFiles\\"; packagePath = packagePath + "\\"; #else std::string command; int err = 0; if (FileUtil::FileExist("testTempFiles")) { command = "rm -rf testTempFiles"; err = system(command.c_str()); ASSERT_EQ(0, err); } command = "mkdir -p testTempFiles"; err = system(command.c_str()); ASSERT_EQ(0, err); srcPath = projectPath + "/unittests/AST/CangjieFiles/"; packagePath = packagePath + "/"; #endif } #ifdef PROJECT_SOURCE_DIR // Gets the absolute path of the project from the compile parameter. const std::string projectPath = FileUtil::JoinPath(PROJECT_SOURCE_DIR, ""); #else // Just in case, give it a default value. // Assume the initial is in the build directory. std::string projectPath = FileUtil::JoinPath(FileUtil::JoinPath("..", ".."), ".."); #endif DiagnosticEngine diag; CompilerInvocation invocation; std::unique_ptr instance; std::string packagePath = "testTempFiles"; std::string srcPath; }; TEST_F(SearchPlusTest, IllegalParametersTest) { auto srcFile = srcPath + "testfile_search_01n.cj"; std::string failedReason; auto content = FileUtil::ReadFileContent(srcFile, failedReason); if (!content.has_value()) { diag.DiagnoseRefactor( DiagKindRefactor::module_read_file_to_buffer_failed, DEFAULT_POSITION, srcFile, failedReason); } std::unique_ptr instance = std::make_unique(invocation, diag); Parser parser(0, content.value(), diag, instance->GetSourceManager()); OwnedPtr pkg = MakeOwned(); pkg->files.emplace_back(parser.ParseTopLevel()); ASTContext ctx(diag, *pkg); ScopeManager scopeManager; Collector collector(scopeManager); collector.BuildSymbolTable(ctx, pkg.get()); std::vector srcFiles; std::vector srcs = {srcFile}; for (auto& src : srcs) { srcFiles.push_back(src); } instance->srcFilePaths = srcFiles; instance->PerformParse(); Searcher searcher; std::vector res = searcher.Search(ctx, ""); EXPECT_EQ(res.size(), 0); res = searcher.Search(ctx, " "); EXPECT_EQ(res.size(), 0); res = searcher.Search(ctx, "name:Day"); EXPECT_EQ(res.size(), 13); res = searcher.Search(ctx, "name:Day!name:Day"); EXPECT_EQ(res.size(), 0); res = searcher.Search(ctx, "name:Day&&name:Day"); EXPECT_EQ(res.size(), 13); res = searcher.Search(ctx, "name:Day||name:Day"); EXPECT_EQ(res.size(), 13); res = searcher.Search(ctx, "name:name"); EXPECT_EQ(res.size(), 0); res = searcher.Search(ctx, "name:Day,name:Time"); EXPECT_EQ(res.size(), 0); res = searcher.Search(ctx, "name:中"); // EXPECTED:illegal symbol '中' EXPECT_TRUE(res.empty()); res = searcher.Search(ctx, "key:value"); // EXPECTED:Unknow query term: key! EXPECT_TRUE(res.empty()); res = searcher.Search(ctx, "name:abcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxy"); EXPECT_EQ(res.size(), 1); res = searcher.Search(ctx, "name:**"); // EXPECTED :1:6: should be identifer, positive integer, 'foo*' or '*foo' EXPECT_TRUE(res.empty()); res = searcher.Search(ctx, "name:D*y"); EXPECT_EQ(res.size(), 0); res = searcher.Search(ctx, "name:*i*"); // EXPECTED :1:6: should be identifer, positive integer, 'foo*' or '*foo' EXPECT_TRUE(res.empty()); res = searcher.Search(ctx, "name:Da?"); EXPECT_EQ(res.size(), 0); res = searcher.Search( ctx, "id:1000"); // EXPECTED:Searcher error: id number '128' past the end of array and it would be ignored. EXPECT_TRUE(res.empty()); res = searcher.Search( ctx, "id:205 && name:n && scope_name:a0j0k0i && ast_kind:ref_expr && scope_level:3 && _<=(0, 84, 32)"); EXPECT_EQ(res.size(), 0); res = searcher.Search(ctx, "_=(0, 87, 32) "); EXPECT_EQ(res.size(), 5); res = searcher.Search(ctx, "_>(-1, -86, -32) "); EXPECT_TRUE(res.empty()); res = searcher.Search(ctx, "scope_name:a0j0*"); EXPECT_EQ(res.size(), 49); res = searcher.Search(ctx, "scope_name:*0i"); // scope_name not support suffix EXPECT_TRUE(res.empty()); res = searcher.Search(ctx, "scope_name:xxxxxxxx"); EXPECT_TRUE(res.empty()); res = searcher.Search(ctx, "ast_kind:generic_param_decl"); EXPECT_EQ(res.size(), 2); res = searcher.Search(ctx, "ast_kind:struct_decl"); EXPECT_EQ(res.size(), 2); res = searcher.Search(ctx, "ast_kind:func_decl"); EXPECT_EQ(res.size(), 13); res = searcher.Search(ctx, "ast_kind:var_decl"); EXPECT_EQ(res.size(), 22); res = searcher.Search(ctx, "ast_kind:class_decl"); EXPECT_EQ(res.size(), 6); res = searcher.Search(ctx, "ast_kind:interface_decl"); EXPECT_EQ(res.size(), 1); res = searcher.Search(ctx, "ast_kind:enum_decl"); EXPECT_EQ(res.size(), 2); res = searcher.Search(ctx, "ast_kind:type_alias_decl"); EXPECT_EQ(res.size(), 0); res = searcher.Search(ctx, "ast_kind:class_like_decl"); EXPECT_EQ(res.size(), 0); res = searcher.Search(ctx, "ast_kind:*decl"); EXPECT_EQ(res.size(), 49); res = searcher.Search(ctx, "ast_kind:c*"); // ast_kind not support prefix EXPECT_TRUE(res.empty()); res = searcher.Search(ctx, "ast_kind:*_decl"); EXPECT_EQ(res.size(), 49); res = searcher.Search(ctx, "ast_kind:xxxxxxxx"); EXPECT_TRUE(res.empty()); // AST must be released before ASTContext for correct symbol detaching. pkg.reset(); instance.reset(); } TEST_F(SearchPlusTest, ScopeTest) { auto srcFile = srcPath + "testfile_search_02n.cj"; std::string failedReason; auto content = FileUtil::ReadFileContent(srcFile, failedReason); if (!content.has_value()) { diag.DiagnoseRefactor( DiagKindRefactor::module_read_file_to_buffer_failed, DEFAULT_POSITION, srcFile, failedReason); } std::unique_ptr instance = std::make_unique(invocation, diag); Parser parser(0, content.value(), diag, instance->GetSourceManager()); OwnedPtr pkg = MakeOwned(); pkg->files.emplace_back(parser.ParseTopLevel()); ASTContext ctx(diag, *pkg); ScopeManager scopeManager; Collector collector(scopeManager); collector.BuildSymbolTable(ctx, pkg.get()); std::vector srcFiles; std::vector srcs = {srcFile}; for (auto& src : srcs) { srcFiles.push_back(src); } instance->srcFilePaths = srcFiles; instance->PerformParse(); diag.ClearError(); Searcher searcher; std::vector res = searcher.Search(ctx, "name:Friday && scope_level:10 && ast_kind:class_decl"); EXPECT_EQ(res.size(), 1); std::string curScopeName; res = searcher.Search(ctx, "_ = (0, 43, 28)"); curScopeName = ScopeManagerApi::GetScopeNameWithoutTail(res[0]->scopeName); std::vector result; while (!curScopeName.empty()) { std::string q = "scope_name: " + curScopeName + "&& _ < (0, 43, 28)"; res = searcher.Search(ctx, q); for (auto tmp : res) { if (tmp->name.empty() || tmp->scopeName == "a") { continue; } result.push_back(tmp->name); } curScopeName = ScopeManagerApi::GetParentScopeName(curScopeName); } EXPECT_EQ(result.size(), 22); EXPECT_EQ(result[0], "thurday"); EXPECT_EQ(result[1], "Int32"); EXPECT_EQ(result[2], "a"); EXPECT_EQ(result[3], "wednesday"); EXPECT_EQ(result[4], "Int32"); EXPECT_EQ(result[5], "a"); EXPECT_EQ(result[6], "tuesday"); EXPECT_EQ(result[7], "Int32"); EXPECT_EQ(result[8], "a"); EXPECT_EQ(result[9], "monday"); EXPECT_EQ(result[10], "Int32"); EXPECT_EQ(result[11], "a"); EXPECT_EQ(result[12], "String"); EXPECT_EQ(result[13], "String"); EXPECT_EQ(result[14], "c"); EXPECT_EQ(result[15], "String"); EXPECT_EQ(result[16], "String"); EXPECT_EQ(result[17], "b"); EXPECT_EQ(result[18], "Int32"); EXPECT_EQ(result[19], "a"); // AST must be released before ASTContext for correct symbol detaching. pkg.reset(); instance.reset(); } TEST_F(SearchPlusTest, DISABLED_MultiFileTest) { #ifdef _WIN32 srcPath = srcPath + "\\pkgs"; #elif __unix__ srcPath = srcPath + "/pkgs"; #endif CompilerInvocation invocation; DiagnosticEngine diag; std::unique_ptr instance = std::make_unique(invocation, diag); instance->srcDirs = {srcPath}; instance->invocation.globalOptions.implicitPrelude = true; #ifdef __x86_64__ instance->invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::X86_64; #else instance->invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::AARCH64; #endif #ifdef _WIN32 instance->invocation.globalOptions.target.os = Cangjie::Triple::OSType::WINDOWS; #elif __unix__ instance->invocation.globalOptions.target.os = Cangjie::Triple::OSType::LINUX; #endif instance->Compile(CompileStage::SEMA); Searcher searcher; ASTContext* ctx = instance->GetASTContextByPackage(instance->GetSourcePackages()[0]); ASSERT_TRUE(ctx != nullptr); std::vector res = searcher.Search(*ctx, "name:Time && ast_kind:class_decl"); EXPECT_EQ(res.size(), 2); EXPECT_EQ(res[0]->hashID.fieldID, 55); EXPECT_EQ(res[1]->hashID.fieldID, 55); } cangjie_compiler-1.0.7/unittests/AST/SearchTest.cpp000066400000000000000000002156271510705540100222660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include #include "gtest/gtest.h" #include "Collector.h" #include "QueryParser.h" #include "TestCompilerInstance.h" #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Searcher.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/Position.h" using namespace Cangjie; using namespace AST; class SearchTest : public testing::Test { protected: void SetUp() override { srcPath = FileUtil::JoinPath(projectPath, "unittests/AST/CangjieFiles/collector"); diag.SetSourceManager(&sm); #ifdef __x86_64__ invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::X86_64; #else invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::AARCH64; #endif #ifdef _WIN32 invocation.globalOptions.target.os = Cangjie::Triple::OSType::WINDOWS; #elif __unix__ invocation.globalOptions.target.os = Cangjie::Triple::OSType::LINUX; #endif } std::string code = R"( main(): unit { var title: String = "world" var t:Int32 let test = Page( component: Div( content: [ Text( content: #" Hello "#, style: TextStyle( width: 10, height: 100, textOverflow: TextOverflow.ellipsis, lines: 1, flex: [1, 0, 0], fontSize: 41.px(), color: Color(0, 0, 0), margin: [0.px(), 0.px(), 0.px(), 10.px()], fontWeight: FontWeight.normal, ) ), Text( hahaha: "111", content: title, fontWeight: "200" ) ] ) ) test.Build() } )"; std::string codeLSP = R"(open class Base { protected var one = 1 private var a: Int64 = 0 init(a: Int64) { this.a = a } public func add(a: Int64, b: Int64) { return a + b + this.one } open func add2(a: Int64, b: Int64) { return a + b } } external class Data <: Base { var res = one init(a: Int64) { super(1) this.res = a } override func add2(a: Int64, b: Int64) { return a + b } } func run(): Int64 { var value : Data = Data(1) if (value.res != 1) { return value.add(1, 2) } return value.add2(1, 2) } abstract class Base1 { var one = 1 } internal class Base2 { var one = 1 } main(): Int64 { return run() } func testlsp():Int32 { var a:Int32 = 0 var b:Int32 = 1 if (a == 0) { var c : Int32 = 0 var d : Int32 = 0 if (c == 0) { var e : Int32 = 0 var f : Int32 = 0 if (e == 0) { return 1 } } } return 0 } )"; std::string regressionTestCode = R"( package pkgs open class Base {} class Data <: Base { var data: Int32 = 1 } func a1() { func a(){ } } func a(){ } func func1(){ } func afunc(){ } func int646(){ } let c = 123 func run(): Int64 { var value: Data = Data() if (value.data != 1) { return 1 } return 0 } main(): Int64 { return run() } )"; #ifdef PROJECT_SOURCE_DIR // Gets the absolute path of the project from the compile parameter. std::string projectPath = PROJECT_SOURCE_DIR; #else // Just in case, give it a default value. // Assume the initial is in the build directory. std::string projectPath = ".."; #endif SourceManager sm; DiagnosticEngine diag; CompilerInvocation invocation; Parser* parser; std::string srcPath; }; TEST_F(SearchTest, DISABLED_ErrorFeedbackTest) { std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = regressionTestCode; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; diag.ClearError(); res = searcher.Search(ctx, "scope_name:*c"); EXPECT_EQ(diag.GetErrorCount(), 1); EXPECT_TRUE(res.empty()); res = searcher.Search(ctx, "scope_level:-1"); EXPECT_EQ(diag.GetErrorCount(), 2); EXPECT_TRUE(res.empty()); res = searcher.Search(ctx, "scope_level:string"); EXPECT_EQ(diag.GetErrorCount(), 3); res = searcher.Search(ctx, "name:Day,name:Time"); EXPECT_EQ(diag.GetErrorCount(), 4); res = searcher.Search(ctx, "name:Day&name:Time"); EXPECT_EQ(diag.GetErrorCount(), 5); res = searcher.Search(ctx, "name:Day|name:Time"); EXPECT_EQ(diag.GetErrorCount(), 6); res = searcher.Search(ctx, "name:Day)name:Time"); diag.EmitCategoryDiagnostics(DiagCategory::PARSE); EXPECT_EQ(diag.GetErrorCount(), 7); EXPECT_TRUE(res.empty()); } TEST_F(SearchTest, DISABLED_WildcardCharacterTest) { std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = regressionTestCode; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; diag.ClearError(); res = searcher.Search(ctx, "name:func*"); EXPECT_EQ(res.size(), 1); EXPECT_EQ(res[0]->name, "func1"); res = searcher.Search(ctx, "name:*func*"); EXPECT_TRUE(res.empty()); EXPECT_EQ(diag.GetErrorCount(), 1); res = searcher.Search(ctx, "name:*func"); EXPECT_EQ(res.size(), 1); EXPECT_EQ(res[0]->name, "afunc"); res = searcher.Search(ctx, "name:Int64*"); res.erase(std::remove_if( res.begin(), res.end(), [](Symbol* sym) { return sym->node->TestAttr(Attribute::COMPILER_ADD); }), res.end()); EXPECT_EQ(res.size(), 2); res = searcher.Search(ctx, "name:ini*"); EXPECT_EQ(res.size(), 2); res = searcher.Search(ctx, "name:*"); EXPECT_EQ(res.size(), 32); } TEST_F(SearchTest, DISABLED_SimpleSearchTest) { srcPath = FileUtil::JoinPath(srcPath, "SimpleSearchTest"); std::unique_ptr instance = std::make_unique(invocation, diag); instance->srcDirs = {srcPath}; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); Searcher searcher; std::vector res; ASTContext& ctx = *instance->GetASTContextByPackage(instance->GetSourcePackages()[0]); res = searcher.Search(ctx, "name: TextStyle"); EXPECT_EQ(res[0]->name, "TextStyle"); res = searcher.Search(ctx, "name:Q*"); EXPECT_TRUE(res.empty()); res = searcher.Search(ctx, "name:*Q"); EXPECT_TRUE(res.empty()); res = searcher.Search(ctx, "name:Text*"); // Text, TextStyle, Text, both ref_expr and call_expr. EXPECT_EQ(res.size(), 6); res = searcher.Search(ctx, "name:*age"); // Page, both ref_expr and call_expr. EXPECT_EQ(res.size(), 2); res = searcher.Search(ctx, "name:*chr"); EXPECT_EQ(res.size(), 0); res = searcher.Search(ctx, "scope_name: a", Sort::posAsc); EXPECT_EQ(res[0]->scopeName.substr(0, res[0]->scopeName.find('_')), "a"); res = searcher.Search(ctx, "scope_level: 0"); const unsigned int expectedScopeLevel = 0; EXPECT_EQ(res[0]->scopeLevel, expectedScopeLevel); res = searcher.Search(ctx, "ast_kind: func_decl"); EXPECT_EQ(res[0]->astKind, ASTKind::FUNC_DECL); // Release nodes and check symbol has been marked as deleted. for (auto& file : ctx.curPackage->files) { file->decls.clear(); } EXPECT_TRUE(res[0]->invertedIndexBeenDeleted); } TEST_F(SearchTest, QueryParserTest) { std::string queryString = "id : 1 ! id : 2"; diag.SetSourceManager(&sm); // Parse query statement. QueryParser queryParser(queryString, diag, sm); auto query = queryParser.Parse(); EXPECT_EQ(query->op, Operator::NOT); } TEST_F(SearchTest, ScopeName) { EXPECT_EQ(ScopeManagerApi::GetScopeGateName("a0b"), "a_b"); EXPECT_EQ(ScopeManagerApi::GetScopeGateName("a0b_a"), "a_b"); EXPECT_EQ(ScopeManagerApi::GetScopeGateName("a"), ""); EXPECT_EQ(ScopeManagerApi::GetScopeGateName("a_a"), ""); EXPECT_EQ(ScopeManagerApi::GetChildScopeName("a_a"), "a0a"); } TEST_F(SearchTest, PrefixTrie) { Trie prefixTrie; prefixTrie.Insert("a0b"); prefixTrie.Insert("a0b0c0e"); prefixTrie.Insert("a0b"); prefixTrie.Insert("a0c"); prefixTrie.Insert("a0b0d"); prefixTrie.Insert("a0d"); auto results = prefixTrie.PrefixMatch("a0b"); std::vector expectStrings = {"a0b", "a0b0c0e", "a0b0d"}; EXPECT_TRUE(std::equal(results.begin(), results.end(), expectStrings.begin())); prefixTrie.Reset(); prefixTrie.Insert("a0b0c"); prefixTrie.Insert("a0c"); prefixTrie.Insert("a0b0d"); prefixTrie.Insert("a0d"); results = prefixTrie.PrefixMatch("a0b"); expectStrings = {"a0b0c", "a0b0d"}; EXPECT_TRUE(std::equal(results.begin(), results.end(), expectStrings.begin())); } TEST_F(SearchTest, SuffixTrie) { Trie suffixTrie; suffixTrie.Insert("var_decl"); suffixTrie.Insert("ref_type"); suffixTrie.Insert("member_access"); suffixTrie.Insert("func_decl"); suffixTrie.Insert("ref_expr"); suffixTrie.Insert("record_decl"); suffixTrie.Insert("class_decl"); auto results = suffixTrie.SuffixMatch("decl"); std::vector expectStrings = {"class_decl", "func_decl", "record_decl", "var_decl"}; EXPECT_TRUE(std::equal(results.begin(), results.end(), expectStrings.begin())); } TEST_F(SearchTest, FuzzyQuery) { diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr pkg = MakeOwned(); pkg->files.emplace_back(parser.ParseTopLevel()); ASTContext ctx(diag, *pkg); ScopeManager scopeManager; Collector collector(scopeManager); collector.BuildSymbolTable(ctx, pkg.get()); Searcher searcher; // Test for prefix query. std::string queryString = "scope_name : a*"; auto res = searcher.Search(ctx, queryString); for (auto n : res) { EXPECT_EQ(n->scopeName[0], 'a'); } EXPECT_EQ(res.size(), ctx.symbolTable.size()); std::string queryString2 = "ast_kind : *decl"; auto res2 = searcher.Search(ctx, queryString2); for (auto n : res2) { EXPECT_EQ(ASTKIND_TO_STRING_MAP[n->astKind].substr(ASTKIND_TO_STRING_MAP[n->astKind].size() - 4), "decl"); } std::string queryString3 = "name: t* && ast_kind: *decl"; auto res3 = searcher.Search(ctx, queryString3); for (auto n : res3) { EXPECT_EQ(n->name[0], 't'); } std::string queryString4 = "name: *eight && ast_kind: func_arg"; auto res4 = searcher.Search(ctx, queryString4); EXPECT_EQ(res4.size(), 3); std::string queryString5 = "name: *eight"; auto res5 = searcher.Search(ctx, queryString5); EXPECT_EQ(res5.size(), 4); // Need to release AST before ASTContext. pkg.reset(); } TEST_F(SearchTest, RangeQuery) { diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr pkg = MakeOwned(); pkg->files.emplace_back(parser.ParseTopLevel()); ASTContext ctx(diag, *pkg); ScopeManager scopeManager; Collector collector(scopeManager); collector.BuildSymbolTable(ctx, pkg.get()); Searcher searcher; Position pos = {0, 6, 16}; // Test function of cast number to string of PosSearchApi. std::string posStr = PosSearchApi::PosToStr(pos); EXPECT_EQ(posStr, "00000000600016"); // Test FindCommonRootInPosTrie of PosSearchApi. std::set ids; TrieNode* root = PosSearchApi::FindCommonRootInPosTrie(*ctx.invertedIndex.posBeginTrie, pos); EXPECT_EQ(root->value, "00000000600016"); const int expectDepth = 14; EXPECT_EQ(root->depth, expectDepth); // Test GetIDsGreaterTheanPos of PosSearchApi. ids = PosSearchApi::GetIDsGreaterThanPos(*ctx.invertedIndex.posBeginTrie, pos); for (auto i : ids) { EXPECT_TRUE(pos <= i->node->begin); } // Test GetIDsLessThanPos of PosSearchApi. ids = PosSearchApi::GetIDsLessThanPos(*ctx.invertedIndex.posBeginTrie, pos); for (auto i : ids) { EXPECT_TRUE(i->node->begin <= pos); } // Test GetIDsWithinPosXByRange of PosSearchApi. Position startPos = Position{0, 6, 1}; Position endPos = Position{0, 6, 16}; ids = PosSearchApi::GetIDsWithinPosXByRange(*ctx.invertedIndex.posBeginTrie, startPos, endPos); auto expectSize = 1; EXPECT_EQ(ids.size(), expectSize); ids = PosSearchApi::GetIDsWithinPosXByRange(*ctx.invertedIndex.posBeginTrie, startPos, endPos, true, true); auto expectSize2 = 3; EXPECT_EQ(ids.size(), expectSize2); std::string queryString3 = "_ < (0, 4, 5) && ast_kind: var_decl"; auto res7 = searcher.Search(ctx, queryString3); auto expectSize7 = 1; EXPECT_EQ(res7.size(), expectSize7); std::string queryString4 = "_ > (0, 3, 6) && ast_kind: var_decl"; auto res8 = searcher.Search(ctx, queryString4); auto expectSize8 = 2; EXPECT_EQ(res8.size(), expectSize8); // Need to release AST before ASTContext. pkg.reset(); } TEST_F(SearchTest, DISABLED_SelectedhighlightTest000) { srcPath = FileUtil::JoinPath(srcPath, "SimpleSearchTest"); std::unique_ptr instance = std::make_unique(invocation, diag); instance->srcDirs = {srcPath}; instance->invocation.globalOptions.implicitPrelude = true; instance->Compile(CompileStage::SEMA); Searcher searcher; auto& ctx = *instance->GetASTContextByPackage(instance->GetSourcePackages()[0]); auto fileID = instance->GetSourcePackages()[0]->files[0]->begin.fileID; auto res = searcher.Search(ctx, "_ = (" + std::to_string(fileID) + ", 60, 13)"); EXPECT_EQ(res[0]->name, "String"); Ptr rt = As(res[0]->node); EXPECT_EQ(rt->ref.target->identifier, "String"); EXPECT_TRUE(rt->ref.target->identifier.Begin() != INVALID_POSITION); } TEST_F(SearchTest, DISABLED_SelectedhighlightTest001) { std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeLSP; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 27, 12)"); EXPECT_EQ(res[0]->name, "value"); Ptr re = As(res[0]->node); EXPECT_EQ(re->ref.target->identifier, "value"); EXPECT_EQ(re->ref.target->identifier.Begin().line, 26); EXPECT_EQ(re->ref.target->identifier.Begin().column, 9); EXPECT_EQ(re->ref.target->keywordPos.line, 26); EXPECT_EQ(re->ref.target->keywordPos.column, 5); } TEST_F(SearchTest, DISABLED_SelectedhighlightTest002) { std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeLSP; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 26, 19)"); EXPECT_EQ(res[0]->name, "Data"); Ptr rt = As(res[0]->node); EXPECT_EQ(rt->ref.target->identifier, "Data"); EXPECT_EQ(rt->ref.target->identifier.Begin().line, 14); EXPECT_EQ(rt->ref.target->identifier.Begin().column, 16); } TEST_F(SearchTest, DISABLED_SelectedhighlightTest003) { std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeLSP; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 30, 20)"); Ptr ma = As(res[0]->node); EXPECT_EQ(ma->target->identifier, "add2"); EXPECT_EQ(As(res[0]->node)->target->identifier.Begin().line, 20); EXPECT_EQ(ma->target->identifier.Begin().column, 19); } TEST_F(SearchTest, DISABLED_SelectedhighlightTest004) { std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeLSP; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 27, 17)"); Ptr ma = As(res[0]->node); EXPECT_EQ(ma->target->identifier, "res"); EXPECT_EQ(ma->target->identifier.Begin().line, 15); EXPECT_EQ(ma->target->identifier.Begin().column, 9); EXPECT_EQ(ma->field.Begin().line, 27); EXPECT_EQ(ma->field.Begin().column, 15); } TEST_F(SearchTest, DISABLED_SelectedhighlightTest005) { std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeLSP; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 27, 17)"); Ptr ma = As(res[0]->node); EXPECT_EQ(ma->target->identifier, "res"); EXPECT_EQ(ma->target->identifier.Begin().line, 15); EXPECT_EQ(ma->target->identifier.Begin().column, 9); EXPECT_EQ(ma->field.Begin().line, 27); EXPECT_EQ(ma->field.Begin().column, 15); } TEST_F(SearchTest, DISABLED_SelectedhighlightTest006) { std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeLSP; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 25, 8)"); Ptr fb = As(res[0]->node); EXPECT_EQ(fb->funcDecl->identifier, "run"); EXPECT_EQ(fb->funcDecl->identifier.Begin().line, 25); EXPECT_EQ(fb->funcDecl->identifier.Begin().column, 6); } TEST_F(SearchTest, DISABLED_SelectedhighlightTest007) { std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeLSP; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 14, 18)"); Ptr cd = As(res[0]->node); EXPECT_EQ(cd->identifier, "Data"); EXPECT_EQ(cd->identifier.Begin().line, 14); EXPECT_EQ(cd->identifier.Begin().column, 16); } TEST_F(SearchTest, DISABLED_SelectedhighlightTest008) { std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = R"( struct Foor { let a: T init(a1: T) { this.a = a1 } } )"; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res = searcher.Search(ctx, "_ = (1, 2, 14)"); EXPECT_EQ(res[0]->name, "T"); } bool InsideMethodScope(ASTContext& context, std::string scopeName) { ScopeManager scopeManager; for (; !scopeName.empty(); scopeName = ScopeManagerApi::GetScopeGateName(scopeName)) { Symbol* symbol = ScopeManagerApi::GetScopeGate(context, scopeName); if (!symbol) { continue; } if (symbol->astKind == ASTKind::FUNC_BODY) { return true; } } return false; } TEST_F(SearchTest, DISABLED_CompletionTest001) { std::string codeTest = R"(func testlsp():Unit { var a:Int32 = 0 var b:Int32 = 1 if (a == 0) { } )"; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeTest; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; std::string curScopeName; res = searcher.Search(ctx, "_ = (1, 5, 9)"); curScopeName = ScopeManagerApi::GetScopeNameWithoutTail(res[0]->scopeName); std::vector result; while (!curScopeName.empty()) { std::string q = "scope_name: " + curScopeName + "!ast_kind: file"; if (InsideMethodScope(ctx, curScopeName)) { q += "&& _ < (1, 5, 9)"; } res = searcher.Search(ctx, q); for (auto tmp : res) { if (tmp->name.empty() || tmp->scopeName == "a") { continue; } result.push_back(tmp->name); } curScopeName = ScopeManagerApi::GetParentScopeName(curScopeName); } EXPECT_EQ(result.size(), 6); EXPECT_EQ(result[0], "Int32"); EXPECT_EQ(result[1], "b"); EXPECT_EQ(result[2], "Int32"); EXPECT_EQ(result[3], "a"); EXPECT_EQ(result[4], "Unit"); EXPECT_EQ(result[5], "testlsp"); } TEST_F(SearchTest, DISABLED_CompletionTest003) { std::string codeTest = R"(open class Base { protected var one = 1 private var a: Int32 = 0 init() {} init(a: Int32) { this.a = a } public func add(a: Int32, b: Int32) { return a + b + this.one } open func add2(a: Int32, b: Int32) { return a + b } } public class Data <: Base { var res = one init(a: Int32) { this.res = a } override func add2(a: Int32, b: Int32) { return a + b } } func testlsp():Int32 { var value : Data = Data(1) value. return 0 } )"; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeTest; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 27, 11)"); Ptr type = dynamic_cast(As(res[0]->node)->baseExpr.get()->symbol->node->ty.get()); EXPECT_EQ(type->decl->body->decls.size(), 3); EXPECT_EQ(type->decl->body->decls[0]->identifier, "res"); EXPECT_EQ(type->decl->body->decls[1]->identifier, "init"); EXPECT_EQ(type->decl->body->decls[2]->identifier, "add2"); } TEST_F(SearchTest, DISABLED_CompletionTest004) { std::string codeTest = R"(open class Base { protected var one = 1 private var a: Int32 = 0 init(a: Int32) { this.a = a } public func add(a: Int32, b: Int32) { return a + b + this.one } open func add2(a: Int32, b: Int32) { return a + b } } external class Data <: Base { var res = one init(a: Int32) { super. } override func add2(a: Int32, b: Int32) { return a + b } } )"; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeTest; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 17, 15)"); Ptr type = dynamic_cast(As(res[0]->node)->baseExpr.get()->symbol->node->ty.get()); EXPECT_EQ(type->decl->body->decls.size(), 5); EXPECT_EQ(type->decl->body->decls[0]->identifier, "one"); EXPECT_EQ(type->decl->body->decls[1]->identifier, "a"); EXPECT_EQ(type->decl->body->decls[2]->identifier, "init"); EXPECT_EQ(type->decl->body->decls[3]->identifier, "add"); EXPECT_EQ(type->decl->body->decls[4]->identifier, "add2"); } TEST_F(SearchTest, DISABLED_CompletionTest005) { std::string codeTest = R"(open class Base { protected var one = 1 private var a: Int32 = 0 init(a: Int32) { this.a = a } public func add(a: Int32, b: Int32) { return a + b + this.one } open func add2(a: Int32, b: Int32) { return a + b } } external class Data <: Base { var res1: Int32 = 0 protected var res2: Int32 = 0 private var res3: Int32 = 0 init(a: Int32) { this. } override func add2(a: Int32, b: Int32) { return a + b } } )"; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeTest; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 19, 14)"); Ptr type = dynamic_cast(As(res[0]->node)->baseExpr.get()->symbol->node->ty.get()); EXPECT_EQ(type->decl->body->decls.size(), 5); EXPECT_EQ(type->decl->body->decls[0]->identifier, "res1"); EXPECT_EQ(type->decl->body->decls[1]->identifier, "res2"); EXPECT_EQ(type->decl->body->decls[2]->identifier, "res3"); EXPECT_EQ(type->decl->body->decls[3]->identifier, "init"); EXPECT_EQ(type->decl->body->decls[4]->identifier, "add2"); res = searcher.Search(ctx, "ast_kind:*_decl"); EXPECT_EQ(res.size(), 12); res = searcher.Search(ctx, "ast_kind:*decl"); EXPECT_EQ(res.size(), 12); } TEST_F(SearchTest, DISABLED_CompletionTest006) { std::string codeTest = R"(interface I { let aclass: Int32 = 1 func add(a: Int32, b: Int32) func getNum1():Int32 { return 1 } static func getSum2():Int32 { return 1 } } class Base <: I { static var classa = 1 static var Class = 2 var a = 3 init(a: Int32) { this.a = I. } static func add2(a: Int32, b: Int32):Int32 { return a + b } } )"; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeTest; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 18, 20)"); Ptr type = dynamic_cast(As(res[0]->node)->baseExpr.get()->symbol->node->ty.get()); EXPECT_EQ(type->decl->body->decls.size(), 4); EXPECT_EQ(type->decl->body->decls[0]->identifier, "aclass"); EXPECT_EQ(type->decl->body->decls[1]->identifier, "add"); EXPECT_EQ(type->decl->body->decls[2]->identifier, "getNum1"); EXPECT_EQ(type->decl->body->decls[3]->identifier, "getSum2"); } TEST_F(SearchTest, DISABLED_CompletionTest007) { std::string code = "let g = A."; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = code; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 1, 11)"); EXPECT_EQ(res[0]->astKind, ASTKind::MEMBER_ACCESS); EXPECT_EQ(res[2]->astKind, ASTKind::VAR_DECL); } TEST_F(SearchTest, DISABLED_VarOrEnumPattern00) { std::string code = R"( enum E { | A | B } main() { let x = B match (x) { case A => 0 case C => 1 } } )"; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = code; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; // Begin pos of A res = searcher.Search(ctx, "_ = (1, 10, 14)"); EXPECT_EQ(res[0]->name, "A"); EXPECT_EQ(res[0]->astKind, ASTKind::REF_EXPR); EXPECT_TRUE(res[0]->target != nullptr); EXPECT_EQ(res[0]->target->astKind, ASTKind::VAR_DECL); EXPECT_EQ(res[0]->target->begin.line, 3); EXPECT_EQ(res[0]->target->begin.column, 7); // Begin pos of C res = searcher.Search(ctx, "_ = (1, 11, 14)"); EXPECT_EQ(res[0]->name, "C"); EXPECT_EQ(res[0]->astKind, ASTKind::VAR_DECL); } TEST_F(SearchTest, DISABLED_Annotation00) { std::string code = R"( @Annotation public class C34 { public const init(a:Bool) {} public const C34(b:Int64) {} } class CA12 { public func foo(@C33[true] i: Int32): Unit {} @C34[true] init() {} } main() {} )"; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = code; instance->invocation.globalOptions.enableMacroInLSP = true; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 8, 22)"); EXPECT_NE(res.size(), 0); EXPECT_TRUE(Utils::In(res, [](const auto n) { return n->astKind == ASTKind::REF_EXPR; })); EXPECT_EQ(diag.GetErrorCount(), 1); } TEST_F(SearchTest, DISABLED_Annotation01) { std::string code = R"( @Annotation public class C33 { public const init(a:Int64) {} } class CA12 { public func foo(@C33[6] i: Int32): Unit {} @C33[9] init() {} } main() {} )"; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = code; instance->invocation.globalOptions.enableMacroInLSP = true; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 7, 22)"); EXPECT_NE(res.size(), 0); EXPECT_TRUE(Utils::In(res, [](const auto n) { return n->astKind == ASTKind::REF_EXPR; })); EXPECT_EQ(diag.GetErrorCount(), 0); } TEST_F(SearchTest, DISABLED_Annotation02) { std::string code = R"( @Annotation public class C6 { let name: String let version: Int64 public const init(name: String, version!: Int64 = 0) { this.name = name this.version = version } } @C6["Sample", version: 1] class Foo1 { static var aa = 0 } func test1() { var a = Foo1.aa } @Annotation public abstract class C2{} main() {} )"; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = code; instance->invocation.globalOptions.enableMacroInLSP = true; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); EXPECT_EQ(diag.GetErrorCount(), 1); } TEST_F(SearchTest, DISABLED_LambdaExpr) { std::string code = "var sum1_newname: (Int32, Int32) -> Int32 = { aaaa:Int32, bbbb => aaaa + bbbb }"; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = code; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; // Begin pos of bbbb res = searcher.Search(ctx, "_ = (1, 1, 59)"); EXPECT_EQ(res[0]->astKind, ASTKind::FUNC_PARAM); // End pos of bbbb res = searcher.Search(ctx, "_ = (1, 1, 63)"); EXPECT_EQ(res[0]->name, "bbbb"); } TEST_F(SearchTest, DISABLED_EnumBodySearchTest) { std::string codeTest = R"( enum E { | A111(ABC) | B | C111 | } enum E2 { | A22(ABC) | B func E11(a: ABC) {} func kk(a:ABC) {} } )"; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeTest; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 3, 30)"); EXPECT_TRUE(!res.empty()); EXPECT_EQ(res[0]->astKind, ASTKind::DUMMY_BODY); res = searcher.Search(ctx, "_ = (1, 10, 5)"); EXPECT_TRUE(!res.empty()); EXPECT_EQ(res[0]->astKind, ASTKind::DUMMY_BODY); std::string scopeName = ScopeManagerApi::GetScopeNameWithoutTail(res[0]->scopeName); EXPECT_EQ(scopeName, "a0b"); std::string queryString = "scope_name: " + scopeName + " && ast_kind: *decl"; res = searcher.Search(ctx, queryString); EXPECT_TRUE(!res.empty()); EXPECT_EQ(res[0]->name, "ABC"); // Found generic decl. } TEST_F(SearchTest, DISABLED_NamedFuncArgSearchTest) { std::string codeTest = R"( public open class Test12 { public init(a:Int32) {} public init(a:Int64) {} public init(a!:Bool) {} public init(c!:String = "cc") {} public Test12(a:Int64, b!:Int64) {a+b} public init(a:Int64, b!:Int32, c!:Bool=true){ } public var abc = 0 } func test42() { var x1 = Test12(1) var x2 = Test12(1, b: 2) // (13, 24) var x3 = Test12(1, b: 2, c:true) var x4 = x1.abc Test12() } )"; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeTest; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); ASTContext& ctx = *instance->GetASTContextByPackage(instance->GetSourcePackages()[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 13, 24)"); ASSERT_TRUE(!res.empty()); EXPECT_EQ(res[0]->astKind, ASTKind::FUNC_ARG); auto argTarget = res[0]->target; res = searcher.Search(ctx, "_ = (1, 13, 17)"); ASSERT_TRUE(!res.empty()); EXPECT_EQ(res[0]->astKind, ASTKind::REF_EXPR); auto refTarget = res[0]->target; ASSERT_TRUE(refTarget && refTarget->astKind == ASTKind::FUNC_DECL); auto fd = RawStaticCast(refTarget); ASSERT_TRUE(fd->funcBody && !fd->funcBody->paramLists.empty()); // Parameter 'b' is second param at index 1. auto param = fd->funcBody->paramLists[0]->params[1].get(); EXPECT_EQ(argTarget, param); } TEST_F(SearchTest, DISABLED_BinaryExprSearchTest) { std::string codeTest = R"( main() { let a : Int64 = 1 let b : Float64 = 1.0 let c : Float64 = 1.0 let d = a + b + c } )"; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeTest; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); ASTContext& ctx = *instance->GetASTContextByPackage(instance->GetSourcePackages()[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 6, 22)"); ASSERT_TRUE(!res.empty()); EXPECT_EQ(res[0]->astKind, ASTKind::REF_EXPR); auto argTarget = res[0]->target; ASSERT_TRUE(argTarget != nullptr); EXPECT_EQ(argTarget->identifier, "c"); } TEST_F(SearchTest, DISABLED_AliasCtorSearchTest) { std::string codeTest = R"( class C { init(a: Int64) {} } type ccc = C main() { var a : ccc = ccc(1) } )"; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeTest; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); ASTContext& ctx = *instance->GetASTContextByPackage(instance->GetSourcePackages()[0]); Searcher searcher; std::vector res; res = searcher.Search(ctx, "_ = (1, 7, 21)"); ASSERT_TRUE(!res.empty()); EXPECT_EQ(res[0]->astKind, ASTKind::REF_EXPR); auto func = res[0]->target; ASSERT_TRUE(func != nullptr); auto ref = dynamic_cast(res[0]->node.get()); ASSERT_TRUE(ref != nullptr); ASSERT_TRUE(ref->aliasTarget != nullptr && ref->aliasTarget->type != nullptr); EXPECT_EQ(func->outerDecl, ref->aliasTarget->type->GetTarget()); } TEST_F(SearchTest, DISABLED_MultipleAssignExprHightTest) { std::string codeTest = R"( main() { var f = { => (1, (2.0, 3))} var aaa: Int64 var bbb: Float64 var ccc: Int32 (aaa, (bbb, ccc)) = f() } )"; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeTest; instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(CompileStage::SEMA); ASTContext& ctx = *instance->GetASTContextByPackage(instance->GetSourcePackages()[0]); Searcher searcher; std::vector syms; syms = searcher.Search(ctx, "_ = (1, 7, 15)"); EXPECT_FALSE(syms.empty()); bool flag = false; for (auto& sym : syms) { if (sym->node->TestAttr(Attribute::COMPILER_ADD) && !sym->node->TestAttr(Attribute::IS_CLONED_SOURCE_CODE)) { continue; } auto ref = As(sym->node); if (ref && ref->ref.target != nullptr && ref->ref.identifier == "bbb" && !(ref->ref.target->TestAttr(Attribute::COMPILER_ADD) && !ref->ref.target->TestAttr(Attribute::IS_CLONED_SOURCE_CODE))) { flag = true; break; } } EXPECT_TRUE(flag); } TEST_F(SearchTest, DISABLED_PrimaryCtorHighlightTest) { srcPath = FileUtil::JoinPath(srcPath, "PrimaryCtorHighlightTest"); std::unique_ptr instance = std::make_unique(invocation, diag); instance->srcDirs = {srcPath}; instance->invocation.globalOptions.implicitPrelude = true; instance->Compile(CompileStage::SEMA); ASTContext& ctx = *instance->GetASTContextByPackage(instance->GetSourcePackages()[0]); auto fileID = ctx.curPackage->files[0]->begin.fileID; Searcher searcher; std::vector syms; syms = searcher.Search(ctx, "_ = (" + std::to_string(fileID) + ", 9, 2)"); EXPECT_FALSE(syms.empty()); EXPECT_EQ(syms[0]->astKind, ASTKind::REF_EXPR); bool flag = false; for (auto& sym : syms) { if (sym->node->TestAttr(Attribute::COMPILER_ADD) && !sym->node->TestAttr(Attribute::IS_CLONED_SOURCE_CODE)) { continue; } auto ref = As(sym->node); if (ref && ref->ref.target != nullptr && !(ref->ref.target->TestAttr(Attribute::COMPILER_ADD) && !ref->ref.target->TestAttr(Attribute::IS_CLONED_SOURCE_CODE))) { flag = true; break; } } EXPECT_TRUE(flag); syms = searcher.Search(ctx, "_ = (" + std::to_string(fileID) + ", 9, 7)"); EXPECT_FALSE(syms.empty()); EXPECT_EQ(syms[0]->astKind, ASTKind::REF_EXPR); flag = false; for (auto& sym : syms) { if (sym->node->TestAttr(Attribute::COMPILER_ADD) && !sym->node->TestAttr(Attribute::IS_CLONED_SOURCE_CODE)) { continue; } auto ref = As(sym->node); if (ref && ref->ref.target != nullptr && !(ref->ref.target->TestAttr(Attribute::COMPILER_ADD) && !ref->ref.target->TestAttr(Attribute::IS_CLONED_SOURCE_CODE))) { flag = true; break; } } EXPECT_TRUE(flag); syms = searcher.Search(ctx, "_ = (" + std::to_string(fileID) + ", 8, 7)"); EXPECT_FALSE(syms.empty()); EXPECT_EQ(syms[0]->astKind, ASTKind::FUNC_PARAM); flag = false; for (auto& sym : syms) { if (sym->node->TestAttr(Attribute::COMPILER_ADD) && !sym->node->TestAttr(Attribute::IS_CLONED_SOURCE_CODE)) { continue; } auto param = As(sym->node); if (param && param->identifier.Begin() != INVALID_POSITION && !(param->TestAttr(Attribute::COMPILER_ADD) && !param->TestAttr(Attribute::IS_CLONED_SOURCE_CODE))) { flag = true; break; } } EXPECT_TRUE(flag); syms = searcher.Search(ctx, "_ = (" + std::to_string(fileID) + ", 8, 16)"); EXPECT_FALSE(syms.empty()); EXPECT_EQ(syms[0]->astKind, ASTKind::FUNC_PARAM); flag = false; for (auto& sym : syms) { if (sym->node->TestAttr(Attribute::COMPILER_ADD) && !sym->node->TestAttr(Attribute::IS_CLONED_SOURCE_CODE)) { continue; } auto param = As(sym->node); if (param && param->identifier.Begin() != INVALID_POSITION && !(param->TestAttr(Attribute::COMPILER_ADD) && !param->TestAttr(Attribute::IS_CLONED_SOURCE_CODE))) { flag = true; break; } } EXPECT_TRUE(flag); syms = searcher.Search(ctx, "_ = (" + std::to_string(fileID) + ", 20, 17)"); EXPECT_FALSE(syms.empty()); EXPECT_EQ(syms[0]->astKind, ASTKind::MEMBER_ACCESS); flag = false; for (auto& sym : syms) { if (sym->node->TestAttr(Attribute::COMPILER_ADD) && !sym->node->TestAttr(Attribute::IS_CLONED_SOURCE_CODE)) { continue; } auto ma = As(sym->node); if (ma && ma->target && !(ma->TestAttr(Attribute::COMPILER_ADD) && !ma->TestAttr(Attribute::IS_CLONED_SOURCE_CODE)) && !(ma->target->TestAttr(Attribute::COMPILER_ADD) && !ma->target->TestAttr(Attribute::IS_CLONED_SOURCE_CODE))) { flag = true; break; } } EXPECT_TRUE(flag); } namespace { std::string GetScopeName(TestCompilerInstance& instance, const std::string& codeTest, const std::string& position) { instance.code = codeTest; instance.invocation.globalOptions.implicitPrelude = true; instance.Compile(CompileStage::SEMA); auto pkgs = instance.GetSourcePackages(); if (pkgs.size() != 1) { return ""; } ASTContext& ctx = *instance.GetASTContextByPackage(pkgs[0]); Searcher searcher; auto syms = searcher.Search(ctx, position); if (syms.empty() || syms[0]->astKind != ASTKind::VAR_DECL) { return ""; } return syms[0]->scopeName; } } // namespace TEST_F(SearchTest, DISABLED_SynReferenceAfterSema_NameReference) { // Test for normal name reference accessing. std::string codeTest = R"( class A { let aclass: Int32 = 1 func getNum1():Int32 { return 1 } static func getSum2():Int32 { return 1 } } class Base { static var classa = 1 static var Class = 2 var a = 3 var ins = A() static func add2(a: Int32, b: Int32):Int32 { return a + b } } let x = A() main() { var x = Base() } )"; std::unique_ptr instance = std::make_unique(invocation, diag); auto scopeName = GetScopeName(*instance, codeTest, "_ = (1, 24, 9)"); ASSERT_FALSE(scopeName.empty()); auto pkgs = instance->GetSourcePackages(); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); // Find local variable. // 'x' auto re = CreateRefExpr("x"); re->curFile = pkgs[0]->files[0].get(); auto results = instance->GetGivenReferenceTarget(ctx, scopeName, *re, true); ASSERT_TRUE(results.hasDecl); auto decls = results.decls; ASSERT_EQ(decls.size(), 1); EXPECT_EQ(decls[0]->ty->String(), "Class-Base"); // 'x.ins' auto ma = CreateMemberAccess(std::move(re), "ins"); ma->curFile = pkgs[0]->files[0].get(); results = instance->GetGivenReferenceTarget(ctx, scopeName, *ma, true); ASSERT_TRUE(results.hasDecl); decls = results.decls; ASSERT_EQ(decls.size(), 1); EXPECT_EQ(decls[0]->ty->String(), "Class-A"); // 'x.ins.aclass' ma = CreateMemberAccess(std::move(ma), "aclass"); ma->curFile = pkgs[0]->files[0].get(); results = instance->GetGivenReferenceTarget(ctx, scopeName, *ma, true); ASSERT_TRUE(results.hasDecl); decls = results.decls; ASSERT_EQ(decls.size(), 1); EXPECT_EQ(decls[0]->ty->String(), "Int32"); // Find variables not in current scope. re = CreateRefExpr("x"); re->curFile = pkgs[0]->files[0].get(); results = instance->GetGivenReferenceTarget(ctx, scopeName, *re, false); ASSERT_TRUE(results.hasDecl); decls = results.decls; ASSERT_EQ(decls.size(), 1); EXPECT_EQ(decls[0]->ty->String(), "Class-A"); } TEST_F(SearchTest, DISABLED_SynReferenceAfterSema_NameReference_ThisAndSuper) { // Test for normal name reference accessing. std::string codeTest = R"( class A <: B { let a: Int32 = 1 func getNum1():Int32 { var a = return 1 } } open class B {} )"; std::unique_ptr instance = std::make_unique(invocation, diag); auto scopeName = GetScopeName(*instance, codeTest, "_ = (1, 5, 17)"); ASSERT_FALSE(scopeName.empty()); auto pkgs = instance->GetSourcePackages(); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); // 'this' auto reThis = CreateRefExpr("this"); reThis->curFile = pkgs[0]->files[0].get(); auto results = instance->GetGivenReferenceTarget(ctx, scopeName, *reThis, true); ASSERT_EQ(results.tys.size(), 1); EXPECT_EQ((*results.tys.begin())->String(), "This"); EXPECT_EQ((*results.tys.begin())->name, "A"); // 'super' auto reSuper = CreateRefExpr("super"); reSuper->curFile = pkgs[0]->files[0].get(); results = instance->GetGivenReferenceTarget(ctx, scopeName, *reSuper, true); ASSERT_EQ(results.tys.size(), 1); EXPECT_EQ((*results.tys.begin())->String(), "Class-B"); EXPECT_EQ((*results.tys.begin())->name, "B"); } TEST_F(SearchTest, DISABLED_SynReferenceAfterSema_CallAccess) { // Test for call access. std::string codeTest = R"( class A { let v = 1 func foo() : B { B() } } class B { let x = 2 } func test() : A { return A() } func test(a: Int64) : Int64 { return a } let x = A() main() { var x = B() } )"; std::unique_ptr instance = std::make_unique(invocation, diag); auto scopeName = GetScopeName(*instance, codeTest, "_ = (1, 19, 9)"); ASSERT_FALSE(scopeName.empty()); auto pkgs = instance->GetSourcePackages(); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); // Find local variable. // 'test()' auto ce = CreateCallExpr(CreateRefExpr("test"), {}); AddCurFile(*ce, pkgs[0]->files[0].get()); auto results = instance->GetGivenReferenceTarget(ctx, scopeName, *ce, false); ASSERT_FALSE(results.hasDecl); auto tys = results.tys; ASSERT_EQ(tys.size(), 2); auto str = Ty::GetTypesToStableStr(std::set>(tys.begin(), tys.end()), " "); EXPECT_EQ(str, "Class-A Int64"); // 'test().foo()' auto ma = CreateMemberAccess(std::move(ce), "foo"); ce = CreateCallExpr(std::move(ma), {}); AddCurFile(*ce, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *ce, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Class-B"); } TEST_F(SearchTest, DISABLED_SynReferenceAfterSema_CallAccess02) { // Test for call access. std::string codeTest = R"( main() { var x = 1 } )"; std::unique_ptr instance = std::make_unique(invocation, diag); auto scopeName = GetScopeName(*instance, codeTest, "_ = (1, 3, 9)"); ASSERT_FALSE(scopeName.empty()); auto pkgs = instance->GetSourcePackages(); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); // 'CPointer()' OwnedPtr access01 = Parser("CPointer()", diag, sm).ParseExpr(); AddCurFile(*access01, pkgs[0]->files[0].get()); auto results = instance->GetGivenReferenceTarget(ctx, scopeName, *access01, false); ASSERT_FALSE(results.hasDecl); auto tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "CPointer"); // 'CString()' OwnedPtr access02 = Parser("CString()", diag, sm).ParseExpr(); AddCurFile(*access02, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *access02, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "CString"); } TEST_F(SearchTest, DISABLED_SynReferenceAfterSema_ThisCallAccess) { // Test for call access for 'This' type. std::string codeTest = R"( open class A { func get() { this } } class B <: A { func test() : Int64 {1} } main() { var x = B() } )"; std::unique_ptr instance = std::make_unique(invocation, diag); auto scopeName = GetScopeName(*instance, codeTest, "_ = (1, 12, 9)"); ASSERT_FALSE(scopeName.empty()); auto pkgs = instance->GetSourcePackages(); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); // Test for dynamic binding 'This' type. OwnedPtr access01 = Parser("x.get().test()", diag, sm).ParseExpr(); AddCurFile(*access01, pkgs[0]->files[0].get()); auto results = instance->GetGivenReferenceTarget(ctx, scopeName, *access01, true); ASSERT_FALSE(results.hasDecl); auto tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Int64"); } TEST_F(SearchTest, DISABLED_SynReferenceAfterSema_LiteralAccess) { // Test for literal access. std::string codeTest = R"( main() { var x = 1 } )"; std::unique_ptr instance = std::make_unique(invocation, diag); auto scopeName = GetScopeName(*instance, codeTest, "_ = (1, 3, 9)"); ASSERT_FALSE(scopeName.empty()); auto pkgs = instance->GetSourcePackages(); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); OwnedPtr literal = Parser("1i64", diag, sm).ParseExpr(); AddCurFile(*literal, pkgs[0]->files[0].get()); auto results = instance->GetGivenReferenceTarget(ctx, scopeName, *literal, false); ASSERT_FALSE(results.hasDecl); auto tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Int64"); // '"str"' OwnedPtr strLit = Parser("\"str\"", diag, sm).ParseExpr(); AddCurFile(*strLit, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *strLit, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Struct-String"); // '""' OwnedPtr emptyStrLit = Parser("\"\"", diag, sm).ParseExpr(); AddCurFile(*emptyStrLit, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *emptyStrLit, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Struct-String"); // '"000${x}222"' OwnedPtr siStr = Parser("\"000${x}222\"", diag, sm).ParseExpr(); AddCurFile(*siStr, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *siStr, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Struct-String"); // '21' OwnedPtr i64Lit = Parser("21", diag, sm).ParseExpr(); AddCurFile(*i64Lit, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *i64Lit, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Int"); } TEST_F(SearchTest, DISABLED_SynReferenceAfterSema_ArrayLitAccess01) { // Test for arraylit access. std::string codeTest = R"( var x = 1 main() { var y = [1, 2, 3] 0 } )"; std::unique_ptr instance = std::make_unique(invocation, diag); auto scopeName = GetScopeName(*instance, codeTest, "_ = (1, 4, 9)"); ASSERT_FALSE(scopeName.empty()); auto pkgs = instance->GetSourcePackages(); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); // Test [1, 2, 3] OwnedPtr literal = Parser("[1, 2, 3]", diag, sm).ParseExpr(); AddCurFile(*literal, pkgs[0]->files[0].get()); auto results = instance->GetGivenReferenceTarget(ctx, scopeName, *literal, false); ASSERT_FALSE(results.hasDecl); auto tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Struct-Array"); // Test [x, 2, 3] OwnedPtr literalWithVar = Parser("[x, 2, 3]", diag, sm).ParseExpr(); AddCurFile(*literalWithVar, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *literalWithVar, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Struct-Array"); // Test ["1", "2", "3"] OwnedPtr literalStr = Parser("[\"1\", \"2\", \"3\"]", diag, sm).ParseExpr(); AddCurFile(*literalStr, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *literalStr, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Struct-Array"); // Test ["1 ${x}", "2", "3"] OwnedPtr literalInsertStr = Parser("[\"1 ${x}\", \"2\", \"3\"]", diag, sm).ParseExpr(); AddCurFile(*literalInsertStr, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *literalInsertStr, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Struct-Array"); // Test [["1", "2", "3"]] OwnedPtr literalNestArray = Parser("[[\"1\", \"2\", \"3\"]]", diag, sm).ParseExpr(); AddCurFile(*literalNestArray, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *literalNestArray, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Struct-Array>"); // Test [] OwnedPtr enmptyLitArray = Parser("[]", diag, sm).ParseExpr(); AddCurFile(*enmptyLitArray, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *enmptyLitArray, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Invalid"); } TEST_F(SearchTest, DISABLED_SynReferenceAfterSema_ArrayLitAccess02) { // Test for arraylit access. std::string codeTest = R"( open class I {} class A <: I {} class B <: I {} main() { var y = [1, 2, 3] 0 } )"; std::unique_ptr instance = std::make_unique(invocation, diag); auto scopeName = GetScopeName(*instance, codeTest, "_ = (1, 6, 9)"); ASSERT_FALSE(scopeName.empty()); auto pkgs = instance->GetSourcePackages(); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); // Test [A(), B()] OwnedPtr literal = Parser("[A(), B()]", diag, sm).ParseExpr(); AddCurFile(*literal, pkgs[0]->files[0].get()); auto results = instance->GetGivenReferenceTarget(ctx, scopeName, *literal, false); ASSERT_FALSE(results.hasDecl); auto tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Struct-Array"); } TEST_F(SearchTest, DISABLED_SynReferenceAfterSema_SubscriptAccess) { // Test for subscript access. std::string codeTest = R"( class A { let v = 1 operator func [](index: Int64) : A { return A() } } let x = A() let y = (1u8, 2i16, "str") let z = ["1", "2"] main() { var x0 = 1 } )"; std::unique_ptr instance = std::make_unique(invocation, diag); auto scopeName = GetScopeName(*instance, codeTest, "_ = (1, 13, 9)"); ASSERT_FALSE(scopeName.empty()); auto pkgs = instance->GetSourcePackages(); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); OwnedPtr access01 = Parser("x[2]", diag, sm).ParseExpr(); AddCurFile(*access01, pkgs[0]->files[0].get()); auto results = instance->GetGivenReferenceTarget(ctx, scopeName, *access01, false); ASSERT_FALSE(results.hasDecl); auto tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Class-A"); OwnedPtr access02 = Parser("y[0]", diag, sm).ParseExpr(); AddCurFile(*access02, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *access02, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "UInt8"); OwnedPtr access03 = Parser("y[1]", diag, sm).ParseExpr(); AddCurFile(*access03, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *access03, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Int16"); OwnedPtr access04 = Parser("y[2]", diag, sm).ParseExpr(); AddCurFile(*access04, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *access04, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Struct-String"); OwnedPtr access05 = Parser("z[0]", diag, sm).ParseExpr(); AddCurFile(*access05, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *access05, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 3); auto str = Ty::GetTypesToStableStr(std::set>(tys.begin(), tys.end()), " "); EXPECT_EQ(str, "Struct-Array Struct-String Unit"); OwnedPtr access06 = Parser("z[0].get(0).getOrThrow()", diag, sm).ParseExpr(); AddCurFile(*access06, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *access06, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 2); str = Ty::GetTypesToStableStr(std::set>(tys.begin(), tys.end()), " "); EXPECT_EQ(str, "Struct-String UInt8"); } TEST_F(SearchTest, DISABLED_SynReferenceAfterSema_SubscriptAccess02) { // Test for subscript access with generic. std::string codeTest = R"( class A { let v : T init(a: T) { v = a } operator func [](v: T) : T { return v } static func test(a: T) : T { let ins = A(a) return ins.v; } } let x = A>(["1"]) main() { var x0 = 1 } )"; std::unique_ptr instance = std::make_unique(invocation, diag); auto scopeName = GetScopeName(*instance, codeTest, "_ = (1, 18, 9)"); ASSERT_FALSE(scopeName.empty()); auto pkgs = instance->GetSourcePackages(); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); OwnedPtr access01 = Parser("x.v[1].get(0).getOrThrow()", diag, sm).ParseExpr(); AddCurFile(*access01, pkgs[0]->files[0].get()); auto results = instance->GetGivenReferenceTarget(ctx, scopeName, *access01, false); ASSERT_FALSE(results.hasDecl); auto tys = results.tys; auto str = Ty::GetTypesToStableStr(std::set>(tys.begin(), tys.end()), " "); EXPECT_EQ(str, "Struct-String UInt8"); OwnedPtr access02 = Parser("x[1].get(0).getOrThrow()", diag, sm).ParseExpr(); AddCurFile(*access02, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *access02, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; str = Ty::GetTypesToStableStr(std::set>(tys.begin(), tys.end()), " "); EXPECT_EQ(str, "Struct-String"); OwnedPtr access03 = Parser("A>([\"1\"])[1].get(0).getOrThrow()", diag, sm).ParseExpr(); AddCurFile(*access03, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *access03, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; str = Ty::GetTypesToStableStr(std::set>(tys.begin(), tys.end()), " "); EXPECT_EQ(str, "Struct-String"); OwnedPtr access04 = Parser("A>.test([\"1\"])[1].get(0).getOrThrow()", diag, sm).ParseExpr(); AddCurFile(*access04, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *access04, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; str = Ty::GetTypesToStableStr(std::set>(tys.begin(), tys.end()), " "); EXPECT_EQ(str, "Struct-String UInt8"); } TEST_F(SearchTest, DISABLED_SynReferenceAfterSema_FunctionCallOperator) { // Test for operator '()' overloading access. std::string codeTest = R"( class A { operator func ()(index: Int64) : B { return B() } } class B { let a = A() } main() { let x = A() } )"; std::unique_ptr instance = std::make_unique(invocation, diag); auto scopeName = GetScopeName(*instance, codeTest, "_ = (1, 13, 9)"); ASSERT_FALSE(scopeName.empty()); auto pkgs = instance->GetSourcePackages(); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); OwnedPtr access01 = Parser("x(1)", diag, sm).ParseExpr(); AddCurFile(*access01, pkgs[0]->files[0].get()); auto results = instance->GetGivenReferenceTarget(ctx, scopeName, *access01, true); ASSERT_FALSE(results.hasDecl); auto tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Class-B"); auto ma = CreateMemberAccess(std::move(access01), "a"); AddCurFile(*ma, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *ma, true); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Class-A"); } TEST_F(SearchTest, DISABLED_SynReferenceAfterSema_OptionalChain) { // Test for operator '()' overloading access. std::string codeTest = R"( class A { let b = Some(B()) } class B { let a = A() operator func ()() : A { return a } operator func [](index: Int64) : Option { return Some(this) } } main() { let x = Option.Some(A()) } )"; std::unique_ptr instance = std::make_unique(invocation, diag); auto scopeName = GetScopeName(*instance, codeTest, "_ = (1, 17, 9)"); ASSERT_FALSE(scopeName.empty()); auto pkgs = instance->GetSourcePackages(); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); OwnedPtr access01 = Parser("x?.b", diag, sm).ParseExpr(); AddCurFile(*access01, pkgs[0]->files[0].get()); auto results = instance->GetGivenReferenceTarget(ctx, scopeName, *access01, true); ASSERT_FALSE(results.hasDecl); auto tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Enum-Option"); OwnedPtr access02 = Parser("x?.b?[0]", diag, sm).ParseExpr(); AddCurFile(*access02, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *access02, true); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Enum-Option"); OwnedPtr access03 = Parser("x?.b?[1]?()", diag, sm).ParseExpr(); AddCurFile(*access03, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *access03, true); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Class-A"); } TEST_F(SearchTest, DISABLED_SynReferenceAfterSema_PkgName) { std::string codeTest = R"( package pkgD let pkgDc= 3333 let pkgDd = 2 struct pkgDTest { init(a:Int32){}} public class pkgDTestClass { init(a:Int32){}} )"; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeTest; instance->invocation.globalOptions.implicitPrelude = true; instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); auto re = CreateRefExpr("pkgD"); re->curFile = pkgs[0]->files[0].get(); auto results = instance->GetGivenReferenceTarget(ctx, "a", *re, false); ASSERT_TRUE(results.hasDecl); ASSERT_EQ(results.decls.size(), 0); } TEST_F(SearchTest, DISABLED_RemoveMacroInitError) { // Test for normal name reference accessing. std::string codeTest = R"( class A { @Differentiabe func sum(b: Float64) : Float64 { return a + b } let a : Float64 = 1.0 } main(){ } )"; std::unique_ptr instance = std::make_unique(invocation, diag); instance->code = codeTest; instance->invocation.globalOptions.implicitPrelude = true; instance->Compile(CompileStage::SEMA); EXPECT_EQ(diag.GetErrorCount(), 1); } TEST_F(SearchTest, DISABLED_SynReferenceAfterSema_TrailingClosureExpr01) { // Test for trailing closure of refExpr. std::string codeTest = R"( class A { var a = 1 } class B { init() {} init(f: ()->Unit) {} func width() : A { A() } } main() { let v = 1 } )"; std::unique_ptr instance = std::make_unique(invocation, diag); auto scopeName = GetScopeName(*instance, codeTest, "_ = (1, 15, 9)"); ASSERT_FALSE(scopeName.empty()); auto pkgs = instance->GetSourcePackages(); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); OwnedPtr tce01 = Parser("B{}", diag, sm).ParseExpr(); AddCurFile(*tce01, pkgs[0]->files[0].get()); auto results = instance->GetGivenReferenceTarget(ctx, scopeName, *tce01, false); ASSERT_FALSE(results.hasDecl); auto tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Class-B"); OwnedPtr tce02 = Parser("B{}.width()", diag, sm).ParseExpr(); AddCurFile(*tce02, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *tce02, false); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Class-A"); } TEST_F(SearchTest, DISABLED_SynReferenceAfterSema_TrailingClosureExpr02) { // Test for trailing closure of callExpr. std::string codeTest = R"( class A { var a = 1 func get() : Int64 {1} } class B { init() {} init(f: ()->Unit) {} func foo(a: Int64, f: ()->Unit) : A { A() } func coo(a: Int64, f: ()->Unit, c!: Int64 = 1) : A { A() } func zoo(a: Int64, f: ()->Unit, c: Int64) : A { A() } } main() { let v = B() } )"; std::unique_ptr instance = std::make_unique(invocation, diag); auto scopeName = GetScopeName(*instance, codeTest, "_ = (1, 22, 9)"); ASSERT_FALSE(scopeName.empty()); auto pkgs = instance->GetSourcePackages(); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); OwnedPtr tce01 = Parser("v.foo(1){}", diag, sm).ParseExpr(); AddCurFile(*tce01, pkgs[0]->files[0].get()); auto results = instance->GetGivenReferenceTarget(ctx, scopeName, *tce01, true); ASSERT_FALSE(results.hasDecl); auto tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Class-A"); OwnedPtr tce02 = Parser("v.foo(1){}.get()", diag, sm).ParseExpr(); AddCurFile(*tce02, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *tce02, true); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Int64"); OwnedPtr tce03 = Parser("v.coo(1){}.get()", diag, sm).ParseExpr(); AddCurFile(*tce03, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *tce03, true); ASSERT_FALSE(results.hasDecl); tys = results.tys; ASSERT_EQ(tys.size(), 1); EXPECT_EQ((*tys.begin())->String(), "Int64"); OwnedPtr tce04 = Parser("v.zoo(1){}", diag, sm).ParseExpr(); AddCurFile(*tce04, pkgs[0]->files[0].get()); results = instance->GetGivenReferenceTarget(ctx, scopeName, *tce04, true); ASSERT_FALSE(results.hasDecl); tys = results.tys; // Fuzzy result given result size 1. ASSERT_EQ(tys.size(), 1); } cangjie_compiler-1.0.7/unittests/AST/WalkerTest.cpp000066400000000000000000000105401510705540100222710ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include "gtest/gtest.h" #define private public #include "cangjie/AST/Match.h" #include "cangjie/AST/PrintNode.h" #include "cangjie/AST/Walker.h" #include "cangjie/Parse/Parser.h" using namespace Cangjie; using namespace AST; class WalkerTest : public ::testing::Test { protected: void SetUp() override { std::string code = "main(argc : Int32, argv : Array) {\n" " let a : Int = 40\n" " let b = 2 ** -a\n" " print((a + 3 * b, (a + 3) * b))\n" "}\n"; Parser parser(code, diag, sm); file = parser.ParseTopLevel(); } DiagnosticEngine diag; SourceManager sm; OwnedPtr file; }; TEST_F(WalkerTest, WalkPair) { int count = 0; Walker walker( file.get(), [&count](Ptr node) -> VisitAction { ++count; return VisitAction::WALK_CHILDREN; }, [&count](Ptr node) -> VisitAction { --count; return VisitAction::WALK_CHILDREN; }); walker.Walk(); EXPECT_EQ(0, count); } TEST_F(WalkerTest, WalkPairSkipChildren) { int count = 0; Walker walker( file.get(), [&count](Ptr node) -> VisitAction { ++count; return VisitAction::SKIP_CHILDREN; }, [&count](Ptr node) -> VisitAction { --count; return VisitAction::WALK_CHILDREN; }); walker.Walk(); EXPECT_EQ(0, count); } TEST_F(WalkerTest, WalkPairStopNow) { int count = 0; Walker walker( file.get(), [&count](Ptr node) -> VisitAction { ++count; return VisitAction::STOP_NOW; }, [&count](Ptr node) -> VisitAction { --count; return VisitAction::WALK_CHILDREN; }); walker.Walk(); EXPECT_EQ(1, count); } TEST_F(WalkerTest, WalkShareID) { // Walker and ConstWalker must share same counter. Walker::nextWalkerID = 1; ConstWalker::nextWalkerID = 1; auto id1 = Walker(file.get()).ID; auto id2 = ConstWalker(file.get()).ID; EXPECT_NE(id1, id2); } TEST_F(WalkerTest, GetDecls) { std::vector identifiers; Walker walker(file.get(), [&identifiers](Ptr node) -> VisitAction { if (auto decl = AST::As(node); decl) { identifiers.push_back(decl->identifier); } return VisitAction::WALK_CHILDREN; }); walker.Walk(); std::string expectedIdentifiers[] = {"main", "argc", "argv", "a", "b"}; ASSERT_EQ(std::size(expectedIdentifiers), identifiers.size()); for (size_t i = 0; i < identifiers.size(); i++) { EXPECT_EQ(expectedIdentifiers[i], identifiers[i]); } } TEST_F(WalkerTest, GetDeclsPost) { std::vector identifiers; Walker walker(file.get(), nullptr, [&identifiers](Ptr node) -> VisitAction { if (auto decl = AST::As(node); decl) { identifiers.push_back(decl->identifier); } return VisitAction::WALK_CHILDREN; }); walker.Walk(); std::string expectedIdentifiers[] = {"argc", "argv", "a", "b", "main"}; ASSERT_EQ(std::size(expectedIdentifiers), identifiers.size()); for (size_t i = 0; i < identifiers.size(); i++) { EXPECT_EQ(expectedIdentifiers[i], identifiers[i]); } } TEST_F(WalkerTest, GetCallExprs) { std::vector callExprNames; Walker walker(file.get(), [&callExprNames](Ptr node) -> VisitAction { if (auto ce = AST::As(node); ce) { if (auto re = AST::As(ce->baseFunc.get()); re) { callExprNames.push_back(re->ref.identifier); } } return VisitAction::WALK_CHILDREN; }); walker.Walk(); std::string expectedCallExprNames[] = {"print"}; ASSERT_EQ(std::size(expectedCallExprNames), callExprNames.size()); for (size_t i = 0; i < callExprNames.size(); i++) { EXPECT_EQ(expectedCallExprNames[i], callExprNames[i]); } } cangjie_compiler-1.0.7/unittests/Basic/000077500000000000000000000000001510705540100200725ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Basic/CMakeLists.txt000066400000000000000000000037401510705540100226360ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. include_directories(${CMAKE_SOURCE_DIR}/src/Sema) set(BASE_UTIL_SRC $ $ $ $ $ $ $ ) add_executable(SourceManagerTest SourceManagerTest.cpp) target_link_libraries( SourceManagerTest ${CMAKE_DL_LIBS} ${BASE_UTIL_SRC} GTest::gtest GTest::gtest_main) add_test(NAME SourceManagerTest COMMAND SourceManagerTest) add_executable(EngineTest EngineTest.cpp ${PROJECT_SOURCE_DIR}/src/IncrementalCompilation/Utils.cpp) target_link_libraries( EngineTest ${CMAKE_DL_LIBS} ${BASE_UTIL_SRC} $ GTest::gtest GTest::gtest_main) add_test(NAME EngineTest COMMAND EngineTest) add_executable(ErrorMessageTest ErrorMessageTest.cpp ${PROJECT_SOURCE_DIR}/src/IncrementalCompilation/Utils.cpp) target_link_libraries( ErrorMessageTest ${CMAKE_DL_LIBS} ${BASE_UTIL_SRC} $ GTest::gtest GTest::gtest_main) add_test(NAME ErrorMessageTest COMMAND ErrorMessageTest) add_executable(UtilsTest UtilsTest.cpp UtilsTest.cpp) target_link_libraries( UtilsTest ${CMAKE_DL_LIBS} ${BASE_UTIL_SRC} GTest::gtest GTest::gtest_main) add_test(NAME UtilsTest COMMAND UtilsTest) if(WIN32) add_executable(StringConvertorTest StringConvertorTest.cpp StringConvertorTest.cpp) target_link_libraries( StringConvertorTest ${CMAKE_DL_LIBS} ${BASE_UTIL_SRC} GTest::gtest GTest::gtest_main) add_test(NAME StringConvertorTest COMMAND StringConvertorTest) endif() cangjie_compiler-1.0.7/unittests/Basic/CangjieFiles/000077500000000000000000000000001510705540100224155ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Basic/CangjieFiles/lsp.cj000066400000000000000000000066041510705540100235370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /* LSP example */ import systemlib import BuiltinLib import utilLib class PageRankList <: Page { public init() { let a = 1 print("PageRankList${a}\n"); } public init(a:Int32) { print("PageRankList222\n"); } public var categoryId = StateString("") public var title = StateString("全站必听"); public var id = StateString("M3BQ-DDywPu2eCbko"); public var limit = StateInt(40); public var index: Int32 = 0; public func setTitle(val: String) { this.title.value = val; this.; } // List item right name public let listItem1RightName = Text( content: "未知艺术家", style: TextStyle( lines: 1, color: Color(51, 51, 51) ) ) // List item left name public let listItem1LeftName = Text( content: "My favority", style: TextStyle( lines: 1, color: Color(136, 136, 136) ) ) // List item image src and style public let listItemImage = Image( src: "/Common/Image/ic_play.svg", style: "imgStyle" ) } main(): Int64 { var testString2 : String; testString2 = "Hello"; if (testString2 != "Hello") { return 1; } var value1 : PageRankList = PageRankList(); var value2 : PageRankList = PageRankList(123); var value3 : PageRankList = PageRankList(321); // class member variables if (value1.No != 20) { return 2; } if (value1.index != 30) { return 3; } // class member functions value1.setTitle("Quick app Rune language demo"); var index: Int32 = value1.index; let testarr = {1, 2, 3, 4, 5}; for (index in testarr) { index = index + 5; } while index < 20 { print("The value of index is ${index}"); index = index + 1; } return 0; } open class Color { private var r: UInt8 = 0; private var g: UInt8 = 0; private var b: UInt8 = 0; public init(r: UInt8, g: UInt8, b: UInt8) { this.r = r; this.g = g; this.b = b; } } class StateInt { public var value: Int32; public init(value: Int32) { this.value = value; } } class StateString { public var value: String; public init(value: String) { this.value = value; } } class TextStyle { /* Sets the number of text lines. -1 indicates that the number is not limited. */ public var lines: Int32 = -1; /* Sets the text color. */ public var color: Color = Color(0, 0, 0); public init() {} public init(lines: Int32 = -1, color: Color = Color(0, 0, 0)) { // text Style this.lines = lines; this.color = color; } } class Text { public var content: String; public var style: TextStyle = TextStyle(); public init(content: String = "", style: TextStyle = TextStyle()) { this.content = content; this.style = style; } } class Image { public var src: String; public var style: String; public init(src: String, style: String) { this.src = src this.style = style } } open class Page { public var No : Int32; } cangjie_compiler-1.0.7/unittests/Basic/EngineTest.cpp000066400000000000000000000273711510705540100226550ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/AST/PrintNode.h" #include "cangjie/Basic/DiagnosticEmitter.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Basic/Match.h" #include "cangjie/Basic/Print.h" #include "cangjie/Parse/Parser.h" #include "cangjie/Utils/CheckUtils.h" #include "gtest/gtest.h" #include #include #include #include #include #include #ifdef _WIN32 #include #endif using namespace Cangjie; using namespace AST; static std::string g_expectStr; class LSPDiagnosticHandlerTest : public DiagnosticHandler { public: explicit LSPDiagnosticHandlerTest(DiagnosticEngine& diag) : DiagnosticHandler(diag, DiagHandlerKind::LSP_HANDLER) { } void HandleDiagnose(Diagnostic& d) override; }; void LSPDiagnosticHandlerTest::HandleDiagnose(Diagnostic& d) { g_expectStr = d.errorMessage; switch (d.diagSeverity) { case DiagSeverity::DS_ERROR: Errorln(d.start, " ThisIsLSP ", d.errorMessage); diag.IncreaseErrorCount(d.diagCategory); break; case DiagSeverity::DS_WARNING: Warningln(d.errorMessage); diag.IncreaseWarningCount(d.diagCategory); break; case DiagSeverity::DS_NOTE: Infoln(d.errorMessage); break; case DiagSeverity::DS_HINT: Infoln(d.errorMessage); break; default: Infoln(d.errorMessage); } } class EngineTest : public testing::Test { protected: void SetUp() override { DiagnosticEngine diageg; } }; TEST(EngineTest, Register1) { DiagnosticEngine diag; auto handler = std::make_unique(diag); diag.RegisterHandler(std::move(handler)); } TEST(EngineTest, Handle1) { SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); auto hander = std::make_unique(diag); std::string code = R"( packag demo main(argc:int, argv!:string="123") { let a:int=40 let b = 2 ** -a print((a+3*b, (a+3) *b)) } )"; diag.RegisterHandler(std::move(hander)); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); EXPECT_EQ("expected declaration, found 'packag'", g_expectStr); } TEST(EngineTest, HandlerFilter) { Cangjie::ICE::TriggerPointSetter iceSetter(static_cast(Cangjie::ICE::UNITTEST_TP)); DiagnosticEngine diag; auto handler = std::make_unique(diag); diag.RegisterHandler(std::move(handler)); diag.DiagnoseRefactor(DiagKindRefactor::parse_expected_name, Position{0, 1, 1}, std::string{"test name"}, std::string{"test02"}, std::string{"test03"}); diag.DiagnoseRefactor(DiagKindRefactor::parse_expected_name, Position{0, 1, 1}, std::string{"test name"}, std::string{"test02"}, std::string{"test03"}); EXPECT_EQ(2, diag.GetErrorCount()); } TEST(EngineTest, apiTest) { DiagnosticEngine diag; std::string code = R"( main(argc:int, argv!:string="123") { var a = 1 var b = """ cangjie """ var c = a + b return 0 } )"; std::string expectedOut = R"(error: expected test name test1, found test2 ==> test.cj:7:13: | 2 | main(argc:int, argv!:string="123") { | _____________~ 3 | | var a = 1 | | ~ ~~~~~ test | |________| | 4 | var b = """ | ... 7 | var c = a + b | ^ expected test3 name here | note: this is a note test ==> test.cj:3:9: | 3 | var a = 1 | _________^ 4 | | var b = """ | |________^ | # help: this is a note help test note: this is a note test ==> test.cj:4:17: | 4 | var b = """ | _________________^ 5 | | cangjie 6 | | """ | |___^ | # help: this is a note help test number 2 )"; auto file = std::string{"test.cj"}; SourceManager sm; sm.AddSource(file | FileUtil::IdenticalFunc, code); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::parse_expected_name, Position{1, 7, 13}, std::string{"test name"}, std::string{"test1"}, std::string{"test2"}); builder.AddMainHintArguments(std::string{"test3 name"}); builder.AddHint(MakeRange(Position{1, 3, 13}, Position{1, 3, 18}), std::string{"test"}); builder.AddHint(MakeRange(Position{1, 2, 13}, Position{1, 3, 9}), std::string{"this is other hint"}); auto sub1 = SubDiagnostic{MakeRange(Position{1, 3, 9}, Position{1, 4, 9}), std::string{"this is a note test"}}; auto noteHelp1 = DiagHelp{std::string{"this is a note help test"}}; sub1.AddHelp(noteHelp1); builder.AddNote(sub1); auto sub2 = SubDiagnostic{MakeRange(Position{1, 4, 17}, Position{1, 6, 4}), std::string{"this is a note test"}}; auto noteHelp2 = DiagHelp{std::string{"this is a note help test number 2"}}; sub2.AddHelp(noteHelp2); builder.AddNote(sub2); std::ostringstream sOut; DiagnosticEmitter(builder.diagnostic, true, true, sOut, sm).Emit(); EXPECT_EQ(sOut.str(), expectedOut); } TEST(EngineTest, apiTest2) { DiagnosticEngine diag; std::string code = R"( main(argc:int,argv!:string="123") { var a = 1 + 2 * 3 } )"; std::string expectedOut = R"(error: expected test name test1, found test2 ==> test.cj:2:10: | 2 | main(argc:int,argv!:string="123") { var a = 1 + 2 * 3 } | ^ ~ ~ ~ this is other hint | | | | | test | | | expected test3 name here | note: this is a note test ==> test.cj:2:9: | 2 | main(argc:int,argv!:string="123") { var a = 1 + 2 * 3 } | ^ | )"; auto file = std::string{"test.cj"}; SourceManager sm; sm.AddSource(file | FileUtil::IdenticalFunc, code); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::parse_expected_name, Position{1, 2, 10}, std::string{"test name"}, std::string{"test1"}, std::string{"test2"}); builder.AddMainHintArguments(std::string{"test3 name"}); builder.AddHint(MakeRange(Position{1, 2, 19}, Position{1, 2, 20}), std::string{"test"}); builder.AddHint(MakeRange(Position{1, 2, 25}, Position{1, 2, 26}), std::string{"this is other hint"}); builder.AddHint(MakeRange(Position{1, 2, 14}, Position{1, 2, 15}), std::string{}); auto sub1 = SubDiagnostic{MakeRange(Position{1, 2, 9}, Position{1, 2, 9}), std::string{"this is a note test"}}; builder.AddNote(sub1); std::ostringstream sOut; DiagnosticEmitter(builder.diagnostic, true, true, sOut, sm).Emit(); EXPECT_EQ(sOut.str(), expectedOut); } TEST(EngineTest, apiTest4) { DiagnosticEngine diag; auto code = R"( main(){ })"; auto file = std::string{"test.cj"}; SourceManager sm; sm.AddSource(file | FileUtil::IdenticalFunc, std::string(code)); diag.SetSourceManager(&sm); auto node = Node(); node.isInMacroCall = true; node.begin = Position{1, 1, 1}; node.end = Position{1, 1, 5}; diag.Diagnose(node, DiagKind::sema_ambiguous_arg_type, Position{1, 1, 1}); EXPECT_EQ(diag.GetErrorCount(), 0); diag.Diagnose(node, DiagKind::sema_mismatched_type_for_pattern_in_vardecl); EXPECT_EQ(diag.GetErrorCount(), 0); diag.DiagnoseRefactor(DiagKindRefactor::sema_invalid_node_after_check, node); EXPECT_EQ(diag.GetErrorCount(), 0); diag.DiagnoseRefactor(DiagKindRefactor::sema_core_object_not_found_when_no_prelude, node, Position{1, 1, 1}); EXPECT_EQ(diag.GetErrorCount(), 0); diag.DiagnoseRefactor( DiagKindRefactor::sema_unable_to_infer_return_type, node, MakeRange(Position{1, 1, 1}, Position{1, 1, 2})); EXPECT_EQ(diag.GetErrorCount(), 0); diag.DiagnoseRefactor(DiagKindRefactor::sema_invalid_called_object, node, Token(TokenKind::STATIC, "static", Position{1, 1, 6}, {1, 1, 12})); EXPECT_EQ(diag.GetErrorCount(), 0); } #ifdef __unix__ TEST(EngineTest, ShowColorTest) { DiagnosticEngine diag; SourceManager sm; auto file = std::string{"test.cj"}; auto code = std::string{""}; sm.AddSource(file | FileUtil::IdenticalFunc, code); diag.SetSourceManager(&sm); auto builder2 = diag.DiagnoseRefactor(DiagKindRefactor::parse_unmatched_right_delimiter, Position{1, 1, 1}, ""); std::ostringstream enableColorOut2; DiagnosticEmitter(builder2.diagnostic, false, true, enableColorOut2, sm).Emit(); // red EXPECT_EQ(enableColorOut2.str().find("\x1b[31m"), 0); std::ostringstream disableColorOut2; DiagnosticEmitter(builder2.diagnostic, true, true, disableColorOut2, sm).Emit(); EXPECT_EQ(disableColorOut2.str().find("\x1b[31m"), std::string::npos); } #elif _WIN32 TEST(EngineTest, ShowColorTest) { DiagnosticEngine diag; SourceManager sm; auto file = std::string{"test.cj"}; auto code = std::string{""}; sm.AddSource(file | FileUtil::IdenticalFunc, code); diag.SetSourceManager(&sm); auto osVersion = Utils::GetOSVersion(); if (osVersion.dwMajorVersion >= 10 && osVersion.dwBuildNumber >= 10586) { auto builder2 = diag.DiagnoseRefactor(DiagKindRefactor::parse_unmatched_right_delimiter, Position{1, 1, 1}, ""); std::ostringstream enableColorOut2; DiagnosticEmitter(builder2.diagnostic, false, true, enableColorOut2, sm).Emit(); // red EXPECT_EQ(enableColorOut2.str().find("\x1b[31m"), 0); std::ostringstream disableColorOut2; DiagnosticEmitter(builder2.diagnostic, true, true, disableColorOut2, sm).Emit(); EXPECT_EQ(disableColorOut2.str().find("\x1b[31m"), std::string::npos); } } #endif TEST(EngineTest, OldDiagKindDefGuard) { // Note: The diagnostic engine has been refactored. This case is mainly used to ensure that no new // kind is added to the old diagnostic engine def file (cangjie/Basic/DiagnosticsAll.def). If you need to // modify, ensure that you only delete existing kinds, and do not add new ones. constexpr const char* const diagKindStr[]{ #define ERROR(Kind, ...) #Kind, #define WARNING(Kind, ...) #Kind, #define NOTE(Kind, Info) Info, #include "cangjie/Basic/DiagnosticsAll.def" #undef WARNING #undef ERROR #undef NOTE }; constexpr const size_t diagEnds[]{ static_cast(DiagKind::chir_diag_end), static_cast(DiagKind::macro_expand_diag_end), static_cast(DiagKind::sema_diag_end), }; // current number of each ending kind, need be modified when modify the def in cangjie/Basic/DiagnosticsAll.def const std::vector endNumber = { 15, // 0 57, // 1 291, // 2 }; bool oldDiagKindBeModified = false; for (size_t i = 0; i < endNumber.size(); ++i) { if (diagEnds[i] == endNumber[i]) { continue; } oldDiagKindBeModified = true; if (diagEnds[i] > endNumber[i]) { std::cerr << "The number of diagnostic kind: " + std::string(diagKindStr[diagEnds[i]]) + "(" + std::to_string(diagEnds[i]) + ") must not increase! Please add new diagnostic kind in .def files under the " "include/cangjie/Basic/DiagRefactor." << std::endl; break; } else { std::cout << "The number of diagnostic kind: " + std::string(diagKindStr[diagEnds[i]]) + "(" + std::to_string(diagEnds[i]) + ") be modified! Please modify the expected item in endNumber[" + std::to_string(i) + "]." << std::endl; } } ASSERT_FALSE(oldDiagKindBeModified) << "Old diag kind be modified, please adpate case or def according the above hints."; } cangjie_compiler-1.0.7/unittests/Basic/ErrorMessageTest.cpp000066400000000000000000000055551510705540100240460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "gtest/gtest.h" #include "cangjie/AST/Node.h" #include "cangjie/Basic/Display.h" #include "cangjie/Parse/Parser.h" using namespace Cangjie; TEST(ErrorMessageTest, MassageTest) { static std::unordered_map columnMap = { {"/*中中中*/ aaa 2\n", 15}, {"\t/*中*/ aaa 2\n", 15}, {"/*A B C D E F G H I J K L M N O P Q R S T U V W X Y Z*/ aaa 2\n", 86}, {"/*∀ ∁ ∂ ∃ ∄ ∅ ∆ ∇ ∈ ∉ ∊ ∋ ∌ ∍ ∎ ∏ ∐ ∑ − ∓ ∔ ∕ ∗ ° √ ∛ ∜*/ aaa 2\n", 64}, {"/*እው ሰላም ነው. እንዴት ነህ?*/ aaa 2\n", 28}, {"/**Je t’aime*/ aaa 2\n", 19}, {"/*Σ΄αγαπώ (Se agapo)*/ aaa 2\n", 27}, {"/*你好。 你好吗?*/ aaa 2\n", 24}, {"/*愛してる*/ aaa 2\n", 17}, {"/*사랑해 (Saranghae)*/ aaa 2\n", 27}, {"/*Я тебя люблю (Ya tebya liubliu)*/ aaa 2\n", 40}, {"/* ?*/ aaa 2\n", 11}, {"/*நீங்கள் எப்படி இருக்கிறீர்கள்?*/ aaa 2\n", 31}, {"/*ਤੁਸੀਂ ਕਿਵੇਂ ਹੋ?*/ aaa 2\n", 19}, {"/*👩 <200d>🔬 */ aaa 2\n", 21}, {"/*𝓽𝓱𝓲𝓼 𝓲𝓼 𝓬𝓸𝓸𝓵*/ aaa 2\n", 21}, {"/*(-■_■)*/ aaa 2\n", 15}, {"/*(☞゚∀゚)☞*/ aaa 2\n", 16}, {"/* */ aaa 2\n", 18}, {"/*/人 ◕ ‿‿ ◕ 人\*/ aaa 2\n", 25}, {"/*▣ ■ □ ▢ ◯ ▲ ▶ ► ▼ ◆ ◢ ◣ ◤ ◥*/ aaa 2\n", 36}, {"/*₁₂₃₄*/ aaa 2\n", 13}, {"/*Μένω στους Παξούς*/ aaa 2\n", 26}, }; std::string code = "func test() {\n"; std::for_each(columnMap.begin(), columnMap.end(), [&](auto& c) { code += c.first; }); code += "}\n"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); std::unique_ptr parser = std::make_unique(1, code, diag, sm); parser->ParseTopLevel(); std::vector ds = diag.GetCategoryDiagnostic(DiagCategory::PARSE); EXPECT_TRUE(!ds.empty()) << "Expected error in parser"; std::for_each(ds.begin(), ds.end(), [&](auto& d) { auto source = sm.GetContentBetween( d.start.fileID, Position(d.start.line, 1), Position(d.start.line, std::numeric_limits::max())); if (columnMap.count(source)) { auto printColumn = GetSpaceBeforeTarget(source, d.mainHint.range.begin.column); EXPECT_EQ(printColumn.length(), columnMap.at(source)) << "Column space differ in line" << source; } }); } cangjie_compiler-1.0.7/unittests/Basic/SourceManagerTest.cpp000066400000000000000000000114451510705540100241760ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include "ScopeManager.h" #include "gtest/gtest.h" #include "cangjie/Lex/Lexer.h" #include "cangjie/Utils/FileUtil.h" using namespace Cangjie; class SourceManagerTest : public testing::Test { protected: void SetUp() override { #ifdef PROJECT_SOURCE_DIR // Gets the absolute path of the project from the compile parameter. projectPath = PROJECT_SOURCE_DIR; #else // Just in case, give it a default value. Assume the initial is in the build directory. projectPath = ".."; #endif std::string command; int err = 0; if (FileUtil::FileExist("testTempFiles")) { command = "rmdir testTempFiles"; err = system(command.c_str()); ASSERT_EQ(0, err); } command = "mkdir testTempFiles"; err = system(command.c_str()); ASSERT_EQ(0, err); srcPath = projectPath + "/unittests/Basic/CangjieFiles/"; } std::string projectPath; std::string srcPath; SourceManager sm; }; TEST_F(SourceManagerTest, AddSourceTest) { std::string srcFile = srcPath + "lsp" + ".cj"; std::string absName = FileUtil::GetAbsPath(srcFile) | FileUtil::IdenticalFunc; std::string failedReason; auto content = FileUtil::ReadFileContent(srcFile, failedReason); ASSERT_TRUE(content.has_value() && failedReason.empty()); auto fileID1 = sm.AddSource(absName, content.value()); auto fileID2 = sm.AddSource(absName, content.value()); auto fileID3 = sm.AddSource(absName, content.value()); EXPECT_EQ(fileID1, fileID2); EXPECT_EQ(fileID2, fileID3); auto expectSourceSize = 2; // There is a source {0, "", ""} in sources. EXPECT_EQ(sm.GetNumberOfFiles(), expectSourceSize); EXPECT_EQ(sm.GetFileID(absName), fileID3); } TEST_F(SourceManagerTest, GetContentBetweenTest) { std::string srcFile = srcPath + "lsp" + ".cj"; std::string absName = FileUtil::GetAbsPath(srcFile) | FileUtil::IdenticalFunc; std::string failedReason; auto content = FileUtil::ReadFileContent(srcFile, failedReason); ASSERT_TRUE(content.has_value() && failedReason.empty()); auto fileID1 = sm.AddSource(absName, content.value()); DiagnosticEngine diag; Lexer lexer(fileID1, content.value(), diag, sm); for (auto tok = lexer.Next(); tok.kind != TokenKind::END; tok = lexer.Next()) { } #ifdef _WIN32 auto code = sm.GetContentBetween(fileID1, Position(14, 9), Position(14, 14)); EXPECT_EQ(code, "let a"); code = sm.GetContentBetween(fileID1, Position(14, 9), Position(14, 18)); EXPECT_EQ(code, "let a = 1"); code = sm.GetContentBetween(fileID1, Position(14, 9), Position(14, 19)); EXPECT_EQ(code, "let a = 1\r\n"); code = sm.GetContentBetween(fileID1, Position(14, 9), Position(14, std::numeric_limits::max())); EXPECT_EQ(code, "let a = 1\r\n"); code = sm.GetContentBetween(fileID1, Position(14, 9), Position(15, 14)); EXPECT_EQ(code, "let a = 1\r\n print"); code = sm.GetContentBetween(fileID1, Position(14, 9), Position(15, 37)); EXPECT_EQ(code, "let a = 1\r\n print(\"PageRankList${a}\\n\");"); code = sm.GetContentBetween(fileID1, Position(14, 9), Position(15, 38)); EXPECT_EQ(code, "let a = 1\r\n print(\"PageRankList${a}\\n\");\r\n"); code = sm.GetContentBetween(fileID1, Position(14, 9), Position(15, std::numeric_limits::max())); EXPECT_EQ(code, "let a = 1\r\n print(\"PageRankList${a}\\n\");\r\n"); #elif __unix__ auto code = sm.GetContentBetween(fileID1, Position(16, 9), Position(16, 14)); EXPECT_EQ(code, "let a"); code = sm.GetContentBetween(fileID1, Position(16, 9), Position(16, 18)); EXPECT_EQ(code, "let a = 1"); code = sm.GetContentBetween(fileID1, Position(16, 9), Position(16, 19)); EXPECT_EQ(code, "let a = 1\n"); code = sm.GetContentBetween(fileID1, Position(16, 9), Position(16, std::numeric_limits::max())); EXPECT_EQ(code, "let a = 1\n"); code = sm.GetContentBetween(fileID1, Position(16, 9), Position(17, 14)); EXPECT_EQ(code, "let a = 1\n print"); code = sm.GetContentBetween(fileID1, Position(16, 9), Position(17, 37)); EXPECT_EQ(code, "let a = 1\n print(\"PageRankList${a}\\n\");"); code = sm.GetContentBetween(fileID1, Position(16, 9), Position(17, 38)); EXPECT_EQ(code, "let a = 1\n print(\"PageRankList${a}\\n\");\n"); code = sm.GetContentBetween(fileID1, Position(16, 9), Position(17, std::numeric_limits::max())); EXPECT_EQ(code, "let a = 1\n print(\"PageRankList${a}\\n\");\n"); #endif }cangjie_compiler-1.0.7/unittests/Basic/StringConvertorTest.cpp000066400000000000000000000067451510705540100246220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include #include "cangjie/Basic/StringConvertor.h" #include "gtest/gtest.h" using namespace Cangjie; namespace { unsigned char g_gbkCharArray[] = {176, 162, 203, 174}; // 阿水 gbk 编码 unsigned char g_utf8CharArray[] = {233, 152, 191, 230, 176, 180}; // 阿水 utf8 编码 unsigned char g_errorCharArray[] = {129}; // unknow 编码 unsigned char g_error2CharArray[] = {240, 169, 184}; // 𩸽 utf8 编码 丢失最后一位: 189 } // namespace TEST(StringConvertorTest, GetStringEncoding) { std::string gbkString(reinterpret_cast(g_gbkCharArray), sizeof(g_gbkCharArray)); EXPECT_EQ(StringConvertor::GetStringEncoding(gbkString), Cangjie::StringConvertor::GBK); std::string utf8String(reinterpret_cast(g_utf8CharArray), sizeof(g_utf8CharArray)); EXPECT_EQ(StringConvertor::GetStringEncoding(utf8String), Cangjie::StringConvertor::UTF8); std::string errorString(reinterpret_cast(g_errorCharArray), sizeof(g_errorCharArray)); EXPECT_EQ(StringConvertor::GetStringEncoding(errorString), Cangjie::StringConvertor::UNKNOWN); std::string error2String(reinterpret_cast(g_error2CharArray), sizeof(g_error2CharArray)); EXPECT_EQ(StringConvertor::GetStringEncoding(error2String), Cangjie::StringConvertor::UNKNOWN); } TEST(StringConvertorTest, GBKToUTF8) { std::string gbkString(reinterpret_cast(g_gbkCharArray), sizeof(g_gbkCharArray)); std::string utf8String(reinterpret_cast(g_utf8CharArray), sizeof(g_utf8CharArray)); std::optional str = StringConvertor::GBKToUTF8(gbkString); EXPECT_EQ(str.has_value(), true); EXPECT_EQ(str.value(), utf8String); } TEST(StringConvertorTest, UTF8ToGBK) { std::string gbkString(reinterpret_cast(g_gbkCharArray), sizeof(g_gbkCharArray)); std::string utf8String(reinterpret_cast(g_utf8CharArray), sizeof(g_utf8CharArray)); std::optional str = StringConvertor::UTF8ToGBK(utf8String); EXPECT_EQ(str.has_value(), true); EXPECT_EQ(str.value(), gbkString); } TEST(StringConvertorTest, NormalizeStringToUTF8) { std::string gbkString(reinterpret_cast(g_gbkCharArray), sizeof(g_gbkCharArray)); std::string utf8String(reinterpret_cast(g_utf8CharArray), sizeof(g_utf8CharArray)); std::optional str = StringConvertor::NormalizeStringToUTF8(gbkString); EXPECT_EQ(str.has_value(), true); EXPECT_EQ(str.value(), utf8String); str = StringConvertor::NormalizeStringToUTF8(utf8String); EXPECT_EQ(str.has_value(), true); EXPECT_EQ(str.value(), utf8String); } TEST(StringConvertorTest, NormalizeStringToGBK) { std::string gbkString(reinterpret_cast(g_gbkCharArray), sizeof(g_gbkCharArray)); std::string utf8String(reinterpret_cast(g_utf8CharArray), sizeof(g_utf8CharArray)); std::optional str = StringConvertor::NormalizeStringToGBK(gbkString); EXPECT_EQ(str.has_value(), true); EXPECT_EQ(str.value(), gbkString); str = StringConvertor::NormalizeStringToGBK(utf8String); EXPECT_EQ(str.has_value(), true); EXPECT_EQ(str.value(), gbkString); }cangjie_compiler-1.0.7/unittests/Basic/UtilsTest.cpp000066400000000000000000000125751510705540100225500ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include #include "cangjie/Basic/Utils.h" #include "gtest/gtest.h" using namespace Cangjie; TEST(UtilsTest, GetHashTest) { std::string case1 = "hello world"; uint64_t h1 = Cangjie::Utils::GetHash(case1); EXPECT_EQ(h1, std::hash{}(case1)); } TEST(UtilsTest, JoinStringsTest) { std::vector, std::string>> cases = { {{"hello"}, ","}, {{"hello", "hello"}, "|"}, {{""}, ""}}; std::vector expect = {"hello", "hello|hello", ""}; for (size_t i = 0; i < cases.size(); i++) { std::string result = Cangjie::Utils::JoinStrings(cases[i].first, cases[i].second); EXPECT_EQ(result, expect[i]); } } TEST(UtilsTest, StringSplitTest) { std::vector res = {"a", "ab", "abc", ""}; std::string case1 = "a\nab\nabc\n"; std::vector case1Ret = Utils::SplitString(case1, "\n"); EXPECT_EQ(res.size(), case1Ret.size()); for (size_t i = 0; i < res.size(); ++i) { EXPECT_EQ(res[i], case1Ret[i]); } std::vector res2 = {"a", "ab", "abc"}; std::string case2 = "a;ab;abc"; std::vector case2Ret = Utils::SplitString(case2, ";"); EXPECT_EQ(res2.size(), case2Ret.size()); for (size_t i = 0; i < res2.size(); ++i) { EXPECT_EQ(res2[i], case2Ret[i]); } } TEST(UtilsTest, SplitLinesTest0) { std::vector res = {"", "", "", ""}; std::string case1 = "\n\n\n"; std::string case2 = "\r\r\r"; std::string case3 = "\r\n\r\n\r\n"; std::string case4 = "\n\r\r\n"; std::vector case1Ret = Utils::SplitLines(case1); std::vector case2Ret = Utils::SplitLines(case2); std::vector case3Ret = Utils::SplitLines(case3); std::vector case4Ret = Utils::SplitLines(case4); EXPECT_EQ(res.size(), case1Ret.size()); EXPECT_EQ(1, case2Ret.size()); EXPECT_EQ(res.size(), case3Ret.size()); EXPECT_EQ(3, case4Ret.size()); for (size_t i = 0; i < res.size(); ++i) { EXPECT_EQ(res[i], case1Ret[i]); EXPECT_EQ(res[i], case3Ret[i]); } EXPECT_EQ("\r\r\r", case2Ret[0]); EXPECT_EQ("", case4Ret[0]); EXPECT_EQ("\r", case4Ret[1]); EXPECT_EQ("", case4Ret[2]); } TEST(UtilsTest, SplitLinesTest1) { std::vector res = {"a", "ab", "abc", ""}; std::string case1 = "a\nab\nabc\n"; std::string case2 = "a\rab\rabc\r"; std::string case3 = "a\r\nab\r\nabc\r\n"; std::string case4 = "a\nab\rabc\r\n"; std::vector case1Ret = Utils::SplitLines(case1); std::vector case2Ret = Utils::SplitLines(case2); std::vector case3Ret = Utils::SplitLines(case3); std::vector case4Ret = Utils::SplitLines(case4); EXPECT_EQ(res.size(), case1Ret.size()); EXPECT_EQ(1, case2Ret.size()); EXPECT_EQ(res.size(), case3Ret.size()); EXPECT_EQ(3, case4Ret.size()); for (size_t i = 0; i < res.size(); ++i) { EXPECT_EQ(res[i], case1Ret[i]); EXPECT_EQ(res[i], case3Ret[i]); } EXPECT_EQ("a\rab\rabc\r", case2Ret[0]); EXPECT_EQ("a", case4Ret[0]); EXPECT_EQ("ab\rabc", case4Ret[1]); EXPECT_EQ("", case4Ret[2]); } TEST(UtilsTest, SplitLinesTest2) { std::vector res = {"a", "ab", "abc", "a"}; std::string case1 = "a\nab\nabc\na"; std::string case2 = "a\rab\rabc\ra"; std::string case3 = "a\r\nab\r\nabc\r\na"; std::string case4 = "a\nab\rabc\r\na"; std::vector case1Ret = Utils::SplitLines(case1); std::vector case2Ret = Utils::SplitLines(case2); std::vector case3Ret = Utils::SplitLines(case3); std::vector case4Ret = Utils::SplitLines(case4); EXPECT_EQ(res.size(), case1Ret.size()); EXPECT_EQ(1, case2Ret.size()); EXPECT_EQ(res.size(), case3Ret.size()); EXPECT_EQ(3, case4Ret.size()); for (size_t i = 0; i < res.size(); ++i) { EXPECT_EQ(res[i], case1Ret[i]); EXPECT_EQ(res[i], case3Ret[i]); } EXPECT_EQ("a\rab\rabc\ra", case2Ret[0]); EXPECT_EQ("a", case4Ret[0]); EXPECT_EQ("ab\rabc", case4Ret[1]); EXPECT_EQ("a", case4Ret[2]); } TEST(UtilsTest, SplitLinesTest3) { std::vector res = {"", "ab", "abc", "a"}; std::string case1 = "\nab\nabc\na"; std::string case2 = "\rab\rabc\ra"; std::string case3 = "\r\nab\r\nabc\r\na"; std::string case4 = "\nab\rabc\r\na"; std::vector case1Ret = Utils::SplitLines(case1); std::vector case2Ret = Utils::SplitLines(case2); std::vector case3Ret = Utils::SplitLines(case3); std::vector case4Ret = Utils::SplitLines(case4); EXPECT_EQ(res.size(), case1Ret.size()); EXPECT_EQ(1, case2Ret.size()); EXPECT_EQ(res.size(), case3Ret.size()); EXPECT_EQ(3, case4Ret.size()); for (size_t i = 0; i < res.size(); ++i) { EXPECT_EQ(res[i], case1Ret[i]); EXPECT_EQ(res[i], case3Ret[i]); } EXPECT_EQ("\rab\rabc\ra", case2Ret[0]); EXPECT_EQ("", case4Ret[0]); EXPECT_EQ("ab\rabc", case4Ret[1]); EXPECT_EQ("a", case4Ret[2]); }cangjie_compiler-1.0.7/unittests/CHIR/000077500000000000000000000000001510705540100175765ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/CHIR/CHIRDeSerialzierTest.cpp000066400000000000000000001754001510705540100242010ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include #include "gtest/gtest.h" #include "cangjie/Basic/Print.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/IntrinsicKind.h" #include "cangjie/CHIR/Serializer/CHIRDeserializer.h" #include "cangjie/CHIR/Type/CustomTypeDef.h" #include "cangjie/CHIR/Type/Type.h" using namespace Cangjie::CHIR; class CHIRDeSerialzierTest : public ::testing::Test { protected: void SetUp() override { } }; Type::TypeKind DeSerialize(const PackageFormat::CHIRTypeKind& kind) { using namespace PackageFormat; using Cangjie::CHIR::Type; auto ret = Type::TypeKind::TYPE_INVALID; switch (kind) { case CHIRTypeKind_INVALID: ret = Type::TypeKind::TYPE_INVALID; break; // integer case CHIRTypeKind_INT8: ret = Type::TypeKind::TYPE_INT8; break; case CHIRTypeKind_INT16: ret = Type::TypeKind::TYPE_INT16; break; case CHIRTypeKind_INT32: ret = Type::TypeKind::TYPE_INT32; break; case CHIRTypeKind_INT64: ret = Type::TypeKind::TYPE_INT64; break; case CHIRTypeKind_INT_NATIVE: ret = Type::TypeKind::TYPE_INT_NATIVE; break; // unsigned integer case CHIRTypeKind_UINT8: ret = Type::TypeKind::TYPE_UINT8; break; case CHIRTypeKind_UINT16: ret = Type::TypeKind::TYPE_UINT16; break; case CHIRTypeKind_UINT32: ret = Type::TypeKind::TYPE_UINT32; break; case CHIRTypeKind_UINT64: ret = Type::TypeKind::TYPE_UINT64; break; case CHIRTypeKind_UINT_NATIVE: ret = Type::TypeKind::TYPE_UINT_NATIVE; break; // float case CHIRTypeKind_FLOAT16: ret = Type::TypeKind::TYPE_FLOAT16; break; case CHIRTypeKind_FLOAT32: ret = Type::TypeKind::TYPE_FLOAT32; break; case CHIRTypeKind_FLOAT64: ret = Type::TypeKind::TYPE_FLOAT64; break; // other primitive type case CHIRTypeKind_RUNE: ret = Type::TypeKind::TYPE_RUNE; break; case CHIRTypeKind_BOOLEAN: ret = Type::TypeKind::TYPE_BOOLEAN; break; case CHIRTypeKind_UNIT: ret = Type::TypeKind::TYPE_UNIT; break; case CHIRTypeKind_NOTHING: ret = Type::TypeKind::TYPE_NOTHING; break; // Void type case CHIRTypeKind_VOID: ret = Type::TypeKind::TYPE_VOID; break; // composite type case CHIRTypeKind_TUPLE: ret = Type::TypeKind::TYPE_TUPLE; break; case CHIRTypeKind_STRUCT: ret = Type::TypeKind::TYPE_STRUCT; break; case CHIRTypeKind_ENUM: ret = Type::TypeKind::TYPE_ENUM; break; case CHIRTypeKind_FUNC: ret = Type::TypeKind::TYPE_FUNC; break; case CHIRTypeKind_CLASS: ret = Type::TypeKind::TYPE_CLASS; break; // Built-in array related type case CHIRTypeKind_RAWARRAY: ret = Type::TypeKind::TYPE_RAWARRAY; break; case CHIRTypeKind_VARRAY: ret = Type::TypeKind::TYPE_VARRAY; break; // Built-in CFFI related type case CHIRTypeKind_C_POINTER: ret = Type::TypeKind::TYPE_CPOINTER; break; case CHIRTypeKind_C_STRING: ret = Type::TypeKind::TYPE_CSTRING; break; // Generic type case CHIRTypeKind_GENERIC: ret = Type::TypeKind::TYPE_GENERIC; break; // Referece to an value with abritray type case CHIRTypeKind_REFTYPE: ret = Type::TypeKind::TYPE_REFTYPE; break; // Built-in box type case CHIRTypeKind_BOXTYPE: ret = Type::TypeKind::TYPE_BOXTYPE; break; case CHIRTypeKind_THIS: ret = Type::TypeKind::TYPE_THIS; break; // no defalut here, due to we need use compiler to check all value be handled. } return ret; } SourceExpr DeSerialize(const PackageFormat::SourceExpr& kind) { using namespace PackageFormat; using Cangjie::CHIR::SourceExpr; auto ret = SourceExpr::IF_EXPR; switch (kind) { case SourceExpr_IF_EXPR: ret = SourceExpr::IF_EXPR; break; case SourceExpr_WHILE_EXPR: ret = SourceExpr::WHILE_EXPR; break; case SourceExpr_DO_WHILE_EXPR: ret = SourceExpr::DO_WHILE_EXPR; break; case SourceExpr_MATCH_EXPR: ret = SourceExpr::MATCH_EXPR; break; case SourceExpr_IF_LET_OR_WHILE_LET: ret = SourceExpr::IF_LET_OR_WHILE_LET; break; case SourceExpr_QUEST: ret = SourceExpr::QUEST; break; case SourceExpr_BINARY: ret = SourceExpr::BINARY; break; case SourceExpr_FOR_IN_EXPR: ret = SourceExpr::FOR_IN_EXPR; break; case SourceExpr_OTHER: ret = SourceExpr::OTHER; break; } return ret; } Cangjie::Linkage DeSerialize(const PackageFormat::Linkage& kind) { using namespace PackageFormat; using Cangjie::Linkage; auto ret = Cangjie::Linkage::WEAK_ODR; switch (kind) { case Linkage_WEAK_ODR: ret = Cangjie::Linkage::WEAK_ODR; break; case Linkage_EXTERNAL: ret = Cangjie::Linkage::EXTERNAL; break; case Linkage_INTERNAL: ret = Cangjie::Linkage::INTERNAL; break; case Linkage_LINKONCE_ODR: ret = Cangjie::Linkage::LINKONCE_ODR; break; case Linkage_EXTERNAL_WEAK: ret = Cangjie::Linkage::EXTERNAL_WEAK; break; } return ret; } Cangjie::CHIR::SkipKind DeSerialize(const PackageFormat::SkipKind& kind) { using namespace PackageFormat; using Cangjie::CHIR::SkipKind; auto ret = SkipKind::NO_SKIP; switch (kind) { case SkipKind_NO_SKIP: ret = SkipKind::NO_SKIP; break; case SkipKind_SKIP_DCE_WARNING: ret = SkipKind::SKIP_DCE_WARNING; break; case SkipKind_SKIP_FORIN_EXIT: ret = SkipKind::SKIP_FORIN_EXIT; break; case SkipKind_SKIP_VIC: ret = SkipKind::SKIP_VIC; break; } return ret; } Cangjie::OverflowStrategy DeSerialize(const PackageFormat::OverflowStrategy& kind) { using namespace PackageFormat; using Cangjie::OverflowStrategy; auto ret = OverflowStrategy::NA; switch (kind) { case OverflowStrategy_NA: ret = OverflowStrategy::NA; break; case OverflowStrategy_CHECKED: ret = OverflowStrategy::CHECKED; break; case OverflowStrategy_WRAPPING: ret = OverflowStrategy::WRAPPING; break; case OverflowStrategy_THROWING: ret = OverflowStrategy::THROWING; break; case OverflowStrategy_SATURATING: ret = OverflowStrategy::SATURATING; break; } return ret; } Value::ValueKind DeSerialize(const PackageFormat::ValueKind& kind) { using namespace PackageFormat; using Cangjie::CHIR::Value; auto ret = Value::ValueKind::KIND_LITERAL; switch (kind) { case ValueKind_LITERAL: ret = Value::ValueKind::KIND_LITERAL; break; case ValueKind_GLOBALVAR: ret = Value::ValueKind::KIND_GLOBALVAR; break; case ValueKind_PARAMETER: ret = Value::ValueKind::KIND_PARAMETER; break; case ValueKind_IMPORTED_FUNC: ret = Value::ValueKind::KIND_IMP_FUNC; break; case ValueKind_IMPORTED_VAR: ret = Value::ValueKind::KIND_IMP_VAR; break; case ValueKind_LOCALVAR: ret = Value::ValueKind::KIND_LOCALVAR; break; case ValueKind_FUNC: ret = Value::ValueKind::KIND_FUNC; break; case ValueKind_BLOCK: ret = Value::ValueKind::KIND_BLOCK; break; case ValueKind_BLOCK_GROUP: ret = Value::ValueKind::KIND_BLOCK_GROUP; break; } return ret; } ConstantValueKind DeSerialize(const PackageFormat::ConstantValueKind& kind) { using namespace PackageFormat; using Cangjie::CHIR::ConstantValueKind; auto ret = ConstantValueKind::KIND_BOOL; switch (kind) { case ConstantValueKind_BOOL: ret = ConstantValueKind::KIND_BOOL; break; case ConstantValueKind_RUNE: ret = ConstantValueKind::KIND_RUNE; break; case ConstantValueKind_INT: ret = ConstantValueKind::KIND_INT; break; case ConstantValueKind_FLOAT: ret = ConstantValueKind::KIND_FLOAT; break; case ConstantValueKind_STRING: ret = ConstantValueKind::KIND_STRING; break; case ConstantValueKind_UNIT: ret = ConstantValueKind::KIND_UNIT; break; case ConstantValueKind_NULL: ret = ConstantValueKind::KIND_NULL; break; case ConstantValueKind_FUNC: ret = ConstantValueKind::KIND_FUNC; break; } return ret; } FuncKind DeSerialize(const PackageFormat::FuncKind& kind) { using namespace PackageFormat; using Cangjie::CHIR::FuncKind; auto ret = FuncKind::DEFAULT; switch (kind) { case FuncKind_DEFAULT: ret = FuncKind::DEFAULT; break; case FuncKind_GETTER: ret = FuncKind::GETTER; break; case FuncKind_SETTER: ret = FuncKind::SETTER; break; case FuncKind_LAMBDA: ret = FuncKind::LAMBDA; break; case FuncKind_CLASS_CONSTRUCTOR: ret = FuncKind::CLASS_CONSTRUCTOR; break; case FuncKind_PRIMAL_CLASS_CONSTRUCTOR: ret = FuncKind::PRIMAL_CLASS_CONSTRUCTOR; break; case FuncKind_STRUCT_CONSTRUCTOR: ret = FuncKind::STRUCT_CONSTRUCTOR; break; case FuncKind_PRIMAL_STRUCT_CONSTRUCTOR: ret = FuncKind::PRIMAL_STRUCT_CONSTRUCTOR; break; case FuncKind_GLOBALVAR_INIT: ret = FuncKind::GLOBALVAR_INIT; break; case FuncKind_FINALIZER: ret = FuncKind::FINALIZER; break; case FuncKind_MAIN_ENTRY: ret = FuncKind::MAIN_ENTRY; break; case FuncKind_ANNOFACTORY_FUNC: ret = FuncKind::ANNOFACTORY_FUNC; break; case FuncKind_MACRO_FUNC: ret = FuncKind::MACRO_FUNC; break; case FuncKind_DEFAULT_PARAMETER_FUNC: ret = FuncKind::DEFAULT_PARAMETER_FUNC; break; case FuncKind_INSTANCEVAR_INIT: ret = FuncKind::INSTANCEVAR_INIT; break; } return ret; } CustomDefKind DeSerialize(const PackageFormat::CustomDefKind& kind) { using namespace PackageFormat; using Cangjie::CHIR::CustomDefKind; auto ret = CustomDefKind::TYPE_STRUCT; switch (kind) { case CustomDefKind_STRUCT: ret = CustomDefKind::TYPE_STRUCT; break; case CustomDefKind_ENUM: ret = CustomDefKind::TYPE_ENUM; break; case CustomDefKind_CLASS: ret = CustomDefKind::TYPE_CLASS; break; case CustomDefKind_EXTEND: ret = CustomDefKind::TYPE_EXTEND; break; } return ret; } ExprKind DeSerialize(const PackageFormat::CHIRExprKind& kind) { using namespace PackageFormat; auto ret = ExprKind::INVALID; switch (kind) { case CHIRExprKind_INVALID: ret = ExprKind::INVALID; break; // Terminator case CHIRExprKind_GOTO: ret = ExprKind::GOTO; break; case CHIRExprKind_BRANCH: ret = ExprKind::BRANCH; break; case CHIRExprKind_MULTIBRANCH: ret = ExprKind::MULTIBRANCH; break; case CHIRExprKind_EXIT: ret = ExprKind::EXIT; break; case CHIRExprKind_APPLY_WITH_EXCEPTION: ret = ExprKind::APPLY_WITH_EXCEPTION; break; case CHIRExprKind_INVOKE_WITH_EXCEPTION: ret = ExprKind::INVOKE_WITH_EXCEPTION; break; case CHIRExprKind_INVOKESTATIC_WITH_EXCEPTION: ret = ExprKind::INVOKESTATIC_WITH_EXCEPTION; break; case CHIRExprKind_RAISE_EXCEPTION: ret = ExprKind::RAISE_EXCEPTION; break; case CHIRExprKind_INT_OP_WITH_EXCEPTION: ret = ExprKind::INT_OP_WITH_EXCEPTION; break; case CHIRExprKind_SPAWN_WITH_EXCEPTION: ret = ExprKind::SPAWN_WITH_EXCEPTION; break; case CHIRExprKind_TYPECAST_WITH_EXCEPTION: ret = ExprKind::TYPECAST_WITH_EXCEPTION; break; case CHIRExprKind_INTRINSIC_WITH_EXCEPTION: ret = ExprKind::INTRINSIC_WITH_EXCEPTION; break; case CHIRExprKind_ALLOCATE_WITH_EXCEPTION: ret = ExprKind::ALLOCATE_WITH_EXCEPTION; break; case CHIRExprKind_RAW_ARRAY_ALLOCATE_WITH_EXCEPTION: ret = ExprKind::RAW_ARRAY_ALLOCATE_WITH_EXCEPTION; break; // Unary case CHIRExprKind_NEG: ret = ExprKind::NEG; break; case CHIRExprKind_NOT: ret = ExprKind::NOT; break; case CHIRExprKind_BITNOT: ret = ExprKind::BITNOT; break; // Binary case CHIRExprKind_ADD: ret = ExprKind::ADD; break; case CHIRExprKind_SUB: ret = ExprKind::SUB; break; case CHIRExprKind_MUL: ret = ExprKind::MUL; break; case CHIRExprKind_DIV: ret = ExprKind::DIV; break; case CHIRExprKind_MOD: ret = ExprKind::MOD; break; case CHIRExprKind_EXP: ret = ExprKind::EXP; break; case CHIRExprKind_LSHIFT: ret = ExprKind::LSHIFT; break; case CHIRExprKind_RSHIFT: ret = ExprKind::RSHIFT; break; case CHIRExprKind_BITAND: ret = ExprKind::BITAND; break; case CHIRExprKind_BITOR: ret = ExprKind::BITOR; break; case CHIRExprKind_BITXOR: ret = ExprKind::BITXOR; break; case CHIRExprKind_LT: ret = ExprKind::LT; break; case CHIRExprKind_GT: ret = ExprKind::GT; break; case CHIRExprKind_LE: ret = ExprKind::LE; break; case CHIRExprKind_GE: ret = ExprKind::GE; break; case CHIRExprKind_EQUAL: ret = ExprKind::EQUAL; break; case CHIRExprKind_NOTEQUAL: ret = ExprKind::NOTEQUAL; break; case CHIRExprKind_AND: ret = ExprKind::AND; break; case CHIRExprKind_OR: ret = ExprKind::OR; break; // Memory case CHIRExprKind_ALLOCATE: ret = ExprKind::ALLOCATE; break; case CHIRExprKind_LOAD: ret = ExprKind::LOAD; break; case CHIRExprKind_STORE: ret = ExprKind::STORE; break; case CHIRExprKind_GET_ELEMENT_REF: ret = ExprKind::GET_ELEMENT_REF; break; case CHIRExprKind_GET_ELEMENT_BY_NAME: ret = ExprKind::GET_ELEMENT_BY_NAME; break; case CHIRExprKind_STORE_ELEMENT_REF: ret = ExprKind::STORE_ELEMENT_REF; break; case CHIRExprKind_STORE_ELEMENT_BY_NAME: ret = ExprKind::STORE_ELEMENT_BY_NAME; break; // Complext case CHIRExprKind_IF: ret = ExprKind::IF; break; case CHIRExprKind_LOOP: ret = ExprKind::LOOP; break; case CHIRExprKind_FORIN_RANGE: ret = ExprKind::FORIN_RANGE; break; case CHIRExprKind_FORIN_ITER: ret = ExprKind::FORIN_ITER; break; case CHIRExprKind_FORIN_CLOSED_RANGE: ret = ExprKind::FORIN_CLOSED_RANGE; break; case CHIRExprKind_LAMBDA: ret = ExprKind::LAMBDA; break; // Others case CHIRExprKind_CONSTANT: ret = ExprKind::CONSTANT; break; case CHIRExprKind_DEBUGEXPR: ret = ExprKind::DEBUGEXPR; break; case CHIRExprKind_TUPLE: ret = ExprKind::TUPLE; break; case CHIRExprKind_FIELD: ret = ExprKind::FIELD; break; case CHIRExprKind_FIELD_BY_NAME: ret = ExprKind::FIELD_BY_NAME; break; case CHIRExprKind_APPLY: ret = ExprKind::APPLY; break; case CHIRExprKind_INVOKE: ret = ExprKind::INVOKE; break; case CHIRExprKind_INVOKE_STATIC: ret = ExprKind::INVOKESTATIC; break; case CHIRExprKind_INSTANCEOF: ret = ExprKind::INSTANCEOF; break; case CHIRExprKind_TYPECAST: ret = ExprKind::TYPECAST; break; case CHIRExprKind_GET_EXCEPTION: ret = ExprKind::GET_EXCEPTION; break; case CHIRExprKind_RAW_ARRAY_ALLOCATE: ret = ExprKind::RAW_ARRAY_ALLOCATE; break; case CHIRExprKind_RAW_ARRAY_LITERAL_INIT: ret = ExprKind::RAW_ARRAY_LITERAL_INIT; break; case CHIRExprKind_RAW_ARRAY_INIT_BY_VALUE: ret = ExprKind::RAW_ARRAY_INIT_BY_VALUE; break; case CHIRExprKind_VARRAY: ret = ExprKind::VARRAY; break; case CHIRExprKind_VARRAY_BUILDER: ret = ExprKind::VARRAY_BUILDER; break; case CHIRExprKind_INTRINSIC: ret = ExprKind::INTRINSIC; break; case CHIRExprKind_SPAWN: ret = ExprKind::SPAWN; break; case CHIRExprKind_GET_INSTANTIATE_VALUE: ret = ExprKind::GET_INSTANTIATE_VALUE; break; case CHIRExprKind_BOX: ret = ExprKind::BOX; break; case CHIRExprKind_UNBOX: ret = ExprKind::UNBOX; break; case CHIRExprKind_TRANSFORM_TO_GENERIC: ret = ExprKind::TRANSFORM_TO_GENERIC; break; case CHIRExprKind_TRANSFORM_TO_CONCRETE: ret = ExprKind::TRANSFORM_TO_CONCRETE; break; case CHIRExprKind_UNBOX_TO_REF: ret = ExprKind::UNBOX_TO_REF; break; case CHIRExprKind_GET_RTTI: ret = ExprKind::GET_RTTI; break; case CHIRExprKind_GET_RTTI_STATIC: ret = ExprKind::GET_RTTI_STATIC; break; } return ret; } IntrinsicKind DeSerialize(const PackageFormat::IntrinsicKind& kind) { using namespace PackageFormat; auto ret = NOT_INTRINSIC; switch (kind) { case IntrinsicKind_NOT_INTRINSIC: ret = NOT_INTRINSIC; break; case IntrinsicKind_NOT_IMPLEMENTED: ret = NOT_IMPLEMENTED; break; case IntrinsicKind_ARRAY_INIT: ret = ARRAY_INIT; break; // CORE case IntrinsicKind_SIZE_OF: ret = SIZE_OF; break; case IntrinsicKind_ALIGN_OF: ret = ALIGN_OF; break; case IntrinsicKind_ARRAY_ACQUIRE_RAW_DATA: ret = ARRAY_ACQUIRE_RAW_DATA; break; case IntrinsicKind_ARRAY_RELEASE_RAW_DATA: ret = ARRAY_RELEASE_RAW_DATA; break; case IntrinsicKind_ARRAY_BUILT_IN_COPY_TO: ret = ARRAY_BUILT_IN_COPY_TO; break; case IntrinsicKind_ARRAY_GET: ret = ARRAY_GET; break; case IntrinsicKind_ARRAY_SET: ret = ARRAY_SET; break; case IntrinsicKind_ARRAY_GET_UNCHECKED: ret = ARRAY_GET_UNCHECKED; break; case IntrinsicKind_ARRAY_GET_REF_UNCHECKED: ret = ARRAY_GET_REF_UNCHECKED; break; case IntrinsicKind_ARRAY_SET_UNCHECKED: ret = ARRAY_SET_UNCHECKED; break; case IntrinsicKind_ARRAY_SIZE: ret = ARRAY_SIZE; break; case IntrinsicKind_ARRAY_CLONE: ret = ARRAY_CLONE; break; case IntrinsicKind_ARRAY_SLICE_INIT: ret = ARRAY_SLICE_INIT; break; case IntrinsicKind_ARRAY_SLICE: ret = ARRAY_SLICE; break; case IntrinsicKind_ARRAY_SLICE_RAWARRAY: ret = ARRAY_SLICE_RAWARRAY; break; case IntrinsicKind_ARRAY_SLICE_START: ret = ARRAY_SLICE_START; break; case IntrinsicKind_ARRAY_SLICE_SIZE: ret = ARRAY_SLICE_SIZE; break; case IntrinsicKind_ARRAY_SLICE_GET_ELEMENT: ret = ARRAY_SLICE_GET_ELEMENT; break; case IntrinsicKind_ARRAY_SLICE_GET_ELEMENT_UNCHECKED: ret = ARRAY_SLICE_GET_ELEMENT_UNCHECKED; break; case IntrinsicKind_ARRAY_SLICE_SET_ELEMENT: ret = ARRAY_SLICE_SET_ELEMENT; break; case IntrinsicKind_ARRAY_SLICE_SET_ELEMENT_UNCHECKED: ret = ARRAY_SLICE_SET_ELEMENT_UNCHECKED; break; case IntrinsicKind_FILL_IN_STACK_TRACE: ret = FILL_IN_STACK_TRACE; break; case IntrinsicKind_DECODE_STACK_TRACE: ret = DECODE_STACK_TRACE; break; case IntrinsicKind_CHR: ret = CHR; break; case IntrinsicKind_ORD: ret = ORD; break; case IntrinsicKind_CPOINTER_GET_POINTER_ADDRESS: ret = CPOINTER_GET_POINTER_ADDRESS; break; case IntrinsicKind_CPOINTER_INIT0: ret = CPOINTER_INIT0; break; // CPointer constructor with no arguments case IntrinsicKind_CPOINTER_INIT1: ret = CPOINTER_INIT1; break; // CPointer constructor with one argument case IntrinsicKind_CPOINTER_READ: ret = CPOINTER_READ; break; case IntrinsicKind_CPOINTER_WRITE: ret = CPOINTER_WRITE; break; case IntrinsicKind_CPOINTER_ADD: ret = CPOINTER_ADD; break; case IntrinsicKind_CSTRING_INIT: ret = CSTRING_INIT; break; case IntrinsicKind_CSTRING_CONVERT_CSTR_TO_PTR: ret = CSTRING_CONVERT_CSTR_TO_PTR; break; case IntrinsicKind_INOUT_PARAM: ret = INOUT_PARAM; break; case IntrinsicKind_REGISTER_WATCHED_OBJECT: ret = REGISTER_WATCHED_OBJECT; break; case IntrinsicKind_OBJECT_REFEQ: ret = OBJECT_REFEQ; break; case IntrinsicKind_RAW_ARRAY_REFEQ: // cjnative only ret = RAW_ARRAY_REFEQ; break; case IntrinsicKind_OBJECT_ZERO_VALUE: ret = OBJECT_ZERO_VALUE; break; case IntrinsicKind_INVOKE_GC: ret = INVOKE_GC; break; case IntrinsicKind_SET_GC_THRESHOLD: ret = SET_GC_THRESHOLD; break; case IntrinsicKind_DUMP_CJ_HEAP_DATA: ret = DUMP_CJ_HEAP_DATA; break; case IntrinsicKind_GET_GC_COUNT: ret = GET_GC_COUNT; break; case IntrinsicKind_GET_GC_TIME_US: ret = GET_GC_TIME_US; break; case IntrinsicKind_GET_GC_FREED_SIZE: ret = GET_GC_FREED_SIZE; break; case IntrinsicKind_START_CJ_CPU_PROFILING: ret = START_CJ_CPU_PROFILING; break; case IntrinsicKind_STOP_CJ_CPU_PROFILING: ret = STOP_CJ_CPU_PROFILING; break; case IntrinsicKind_BLACK_BOX: ret = BLACK_BOX; break; case IntrinsicKind_GET_MAX_HEAP_SIZE: ret = GET_MAX_HEAP_SIZE; break; case IntrinsicKind_GET_ALLOCATE_HEAP_SIZE: ret = GET_ALLOCATE_HEAP_SIZE; break; case IntrinsicKind_GET_REAL_HEAP_SIZE: ret = GET_REAL_HEAP_SIZE; break; case IntrinsicKind_GET_THREAD_NUMBER: ret = GET_THREAD_NUMBER; break; case IntrinsicKind_GET_BLOCKING_THREAD_NUMBER: ret = GET_BLOCKING_THREAD_NUMBER; break; case IntrinsicKind_GET_NATIVE_THREAD_NUMBER: ret = GET_NATIVE_THREAD_NUMBER; break; case IntrinsicKind_VARRAY_SET: ret = VARRAY_SET; break; case IntrinsicKind_VARRAY_GET: ret = VARRAY_GET; break; // About Future case IntrinsicKind_FUTURE_INIT: ret = FUTURE_INIT; break; case IntrinsicKind_FUTURE_IS_COMPLETE: // cjnative only ret = FUTURE_IS_COMPLETE; break; case IntrinsicKind_FUTURE_WAIT: // cjnative only ret = FUTURE_WAIT; break; case IntrinsicKind_FUTURE_NOTIFYALL: // cjnative only ret = FUTURE_NOTIFYALL; break; case IntrinsicKind_IS_THREAD_OBJECT_INITED: ret = IS_THREAD_OBJECT_INITED; break; case IntrinsicKind_GET_THREAD_OBJECT: ret = GET_THREAD_OBJECT; break; case IntrinsicKind_SET_THREAD_OBJECT: ret = SET_THREAD_OBJECT; break; case IntrinsicKind_OVERFLOW_CHECKED_ADD: ret = OVERFLOW_CHECKED_ADD; break; case IntrinsicKind_OVERFLOW_CHECKED_SUB: ret = OVERFLOW_CHECKED_SUB; break; case IntrinsicKind_OVERFLOW_CHECKED_MUL: ret = OVERFLOW_CHECKED_MUL; break; case IntrinsicKind_OVERFLOW_CHECKED_DIV: ret = OVERFLOW_CHECKED_DIV; break; case IntrinsicKind_OVERFLOW_CHECKED_MOD: ret = OVERFLOW_CHECKED_MOD; break; case IntrinsicKind_OVERFLOW_CHECKED_POW: ret = OVERFLOW_CHECKED_POW; break; case IntrinsicKind_OVERFLOW_CHECKED_INC: ret = OVERFLOW_CHECKED_INC; break; case IntrinsicKind_OVERFLOW_CHECKED_DEC: ret = OVERFLOW_CHECKED_DEC; break; case IntrinsicKind_OVERFLOW_CHECKED_NEG: ret = OVERFLOW_CHECKED_NEG; break; case IntrinsicKind_OVERFLOW_THROWING_ADD: ret = OVERFLOW_THROWING_ADD; break; case IntrinsicKind_OVERFLOW_THROWING_SUB: ret = OVERFLOW_THROWING_SUB; break; case IntrinsicKind_OVERFLOW_THROWING_MUL: ret = OVERFLOW_THROWING_MUL; break; case IntrinsicKind_OVERFLOW_THROWING_DIV: ret = OVERFLOW_THROWING_DIV; break; case IntrinsicKind_OVERFLOW_THROWING_MOD: ret = OVERFLOW_THROWING_MOD; break; case IntrinsicKind_OVERFLOW_THROWING_POW: ret = OVERFLOW_THROWING_POW; break; case IntrinsicKind_OVERFLOW_THROWING_INC: ret = OVERFLOW_THROWING_INC; break; case IntrinsicKind_OVERFLOW_THROWING_DEC: ret = OVERFLOW_THROWING_DEC; break; case IntrinsicKind_OVERFLOW_THROWING_NEG: ret = OVERFLOW_THROWING_NEG; break; case IntrinsicKind_OVERFLOW_SATURATING_ADD: ret = OVERFLOW_SATURATING_ADD; break; case IntrinsicKind_OVERFLOW_SATURATING_SUB: ret = OVERFLOW_SATURATING_SUB; break; case IntrinsicKind_OVERFLOW_SATURATING_MUL: ret = OVERFLOW_SATURATING_MUL; break; case IntrinsicKind_OVERFLOW_SATURATING_DIV: ret = OVERFLOW_SATURATING_DIV; break; case IntrinsicKind_OVERFLOW_SATURATING_MOD: ret = OVERFLOW_SATURATING_MOD; break; case IntrinsicKind_OVERFLOW_SATURATING_POW: ret = OVERFLOW_SATURATING_POW; break; case IntrinsicKind_OVERFLOW_SATURATING_INC: ret = OVERFLOW_SATURATING_INC; break; case IntrinsicKind_OVERFLOW_SATURATING_DEC: ret = OVERFLOW_SATURATING_DEC; break; case IntrinsicKind_OVERFLOW_SATURATING_NEG: ret = OVERFLOW_SATURATING_NEG; break; case IntrinsicKind_OVERFLOW_WRAPPING_ADD: ret = OVERFLOW_WRAPPING_ADD; break; case IntrinsicKind_OVERFLOW_WRAPPING_SUB: ret = OVERFLOW_WRAPPING_SUB; break; case IntrinsicKind_OVERFLOW_WRAPPING_MUL: ret = OVERFLOW_WRAPPING_MUL; break; case IntrinsicKind_OVERFLOW_WRAPPING_DIV: ret = OVERFLOW_WRAPPING_DIV; break; case IntrinsicKind_OVERFLOW_WRAPPING_MOD: ret = OVERFLOW_WRAPPING_MOD; break; case IntrinsicKind_OVERFLOW_WRAPPING_POW: ret = OVERFLOW_WRAPPING_POW; break; case IntrinsicKind_OVERFLOW_WRAPPING_INC: ret = OVERFLOW_WRAPPING_INC; break; case IntrinsicKind_OVERFLOW_WRAPPING_DEC: ret = OVERFLOW_WRAPPING_DEC; break; case IntrinsicKind_OVERFLOW_WRAPPING_NEG: ret = OVERFLOW_WRAPPING_NEG; break; // llvm vector instructions for string optimization case IntrinsicKind_VECTOR_COMPARE_32: // cjnative only ret = VECTOR_COMPARE_32; break; case IntrinsicKind_VECTOR_INDEX_BYTE_32: // cjnative only ret = VECTOR_INDEX_BYTE_32; break; case IntrinsicKind_CJ_CORE_CAN_USE_SIMD: // cjnative only ret = CJ_CORE_CAN_USE_SIMD; break; case IntrinsicKind_CJ_TLS_DYN_SET_SESSION_CALLBACK: ret = CJ_TLS_DYN_SET_SESSION_CALLBACK; break; case IntrinsicKind_CJ_TLS_DYN_SSL_INIT: ret = CJ_TLS_DYN_SSL_INIT; break; // ============================ cjnative only start ================= case IntrinsicKind_REFLECTION_INTRINSIC_START_FLAG: ret = REFLECTION_INTRINSIC_START_FLAG; break; case IntrinsicKind_IS_INTERFACE: ret = IS_INTERFACE; break; case IntrinsicKind_IS_CLASS: ret = IS_CLASS; break; case IntrinsicKind_IS_PRIMITIVE: ret = IS_PRIMITIVE; break; case IntrinsicKind_IS_STRUCT: ret = IS_STRUCT; break; case IntrinsicKind_IS_GENERIC: ret = IS_GENERIC; break; case IntrinsicKind_GET_OR_CREATE_TYPEINFO_FOR_REFLECT: ret = GET_OR_CREATE_TYPEINFO_FOR_REFLECT; break; case IntrinsicKind_GET_TYPETEMPLATE: ret = GET_TYPETEMPLATE; break; case IntrinsicKind_CHECK_METHOD_ACTUAL_ARGS: ret = CHECK_METHOD_ACTUAL_ARGS; break; case IntrinsicKind_METHOD_ENTRYPOINT_IS_NULL: ret = METHOD_ENTRYPOINT_IS_NULL; break; case IntrinsicKind_IS_RELECT_UNSUPPORTED_TYPE: ret = IS_RELECT_UNSUPPORTED_TYPE; break; case IntrinsicKind_GET_TYPE_FOR_ANY: ret = GET_TYPE_FOR_ANY; break; case IntrinsicKind_GET_TYPE_BY_MANGLED_NAME: ret = GET_TYPE_BY_MANGLED_NAME; break; case IntrinsicKind_GET_TYPE_NAME: ret = GET_TYPE_NAME; break; case IntrinsicKind_GET_TYPE_BY_QUALIFIED_NAME: ret = GET_TYPE_BY_QUALIFIED_NAME; break; case IntrinsicKind_GET_TYPE_QUALIFIED_NAME_LENGTH: ret = GET_TYPE_QUALIFIED_NAME_LENGTH; break; case IntrinsicKind_GET_TYPE_QUALIFIED_NAME: ret = GET_TYPE_QUALIFIED_NAME; break; case IntrinsicKind_GET_NUM_OF_INTERFACE: ret = GET_NUM_OF_INTERFACE; break; case IntrinsicKind_GET_INTERFACE: ret = GET_INTERFACE; break; case IntrinsicKind_IS_SUBTYPE: ret = IS_SUBTYPE; break; case IntrinsicKind_GET_TYPE_INFO_MODIFIER: ret = GET_TYPE_INFO_MODIFIER; break; case IntrinsicKind_GET_TYPE_INFO_ANNOTATIONS: ret = GET_TYPE_INFO_ANNOTATIONS; break; case IntrinsicKind_GET_OBJ_CLASS: ret = GET_OBJ_CLASS; break; case IntrinsicKind_GET_SUPER_TYPE_INFO: ret = GET_SUPER_TYPE_INFO; break; case IntrinsicKind_GET_NUM_OF_INSTANCE_METHOD_INFOS: ret = GET_NUM_OF_INSTANCE_METHOD_INFOS; break; case IntrinsicKind_GET_INSTANCE_METHOD_INFO: ret = GET_INSTANCE_METHOD_INFO; break; case IntrinsicKind_GET_NUM_OF_STATIC_METHOD_INFOS: ret = GET_NUM_OF_STATIC_METHOD_INFOS; break; case IntrinsicKind_GET_STATIC_METHOD_INFO: ret = GET_STATIC_METHOD_INFO; break; case IntrinsicKind_GET_METHOD_NAME: ret = GET_METHOD_NAME; break; case IntrinsicKind_GET_METHOD_RETURN_TYPE: ret = GET_METHOD_RETURN_TYPE; break; case IntrinsicKind_GET_METHOD_MODIFIER: ret = GET_METHOD_MODIFIER; break; case IntrinsicKind_GET_METHOD_ANNOTATIONS: ret = GET_METHOD_ANNOTATIONS; break; case IntrinsicKind_APPLY_CJ_METHOD: ret = APPLY_CJ_METHOD; break; case IntrinsicKind_APPLY_CJ_STATIC_METHOD: ret = APPLY_CJ_STATIC_METHOD; break; case IntrinsicKind_APPLY_CJ_GENERIC_METHOD: ret = APPLY_CJ_GENERIC_METHOD; break; case IntrinsicKind_APPLY_CJ_GENERIC_STATIC_METHOD: ret = APPLY_CJ_GENERIC_STATIC_METHOD; break; case IntrinsicKind_GET_NUM_OF_ACTUAL_PARAMETERS: ret = GET_NUM_OF_ACTUAL_PARAMETERS; break; case IntrinsicKind_GET_NUM_OF_GENERIC_PARAMETERS: ret = GET_NUM_OF_GENERIC_PARAMETERS; break; case IntrinsicKind_GET_ACTUAL_PARAMETER_INFO: ret = GET_ACTUAL_PARAMETER_INFO; break; case IntrinsicKind_GET_GENERIC_PARAMETER_INFO: ret = GET_GENERIC_PARAMETER_INFO; break; case IntrinsicKind_GET_NUM_OF_INSTANCE_FIELD_INFOS: ret = GET_NUM_OF_INSTANCE_FIELD_INFOS; break; case IntrinsicKind_GET_INSTANCE_FIELD_INFO: ret = GET_INSTANCE_FIELD_INFO; break; case IntrinsicKind_GET_NUM_OF_STATIC_FIELD_INFOS: ret = GET_NUM_OF_STATIC_FIELD_INFOS; break; case IntrinsicKind_GET_STATIC_FIELD_INFO: ret = GET_STATIC_FIELD_INFO; break; case IntrinsicKind_GET_STATIC_FIELD_NAME: ret = GET_STATIC_FIELD_NAME; break; case IntrinsicKind_GET_STATIC_FIELD_TYPE: ret = GET_STATIC_FIELD_TYPE; break; case IntrinsicKind_GET_STATIC_FIELD_ANNOTATIONS: ret = GET_STATIC_FIELD_ANNOTATIONS; break; case IntrinsicKind_GET_FIELD_NAME: ret = GET_FIELD_NAME; break; case IntrinsicKind_GET_FIELD_TYPE: ret = GET_FIELD_TYPE; break; case IntrinsicKind_GET_FIELD_ANNOTATIONS: ret = GET_FIELD_ANNOTATIONS; break; case IntrinsicKind_GET_FIELD_MODIFIER: ret = GET_FIELD_MODIFIER; break; case IntrinsicKind_GET_STATIC_FIELD_MODIFIER: ret = GET_STATIC_FIELD_MODIFIER; break; case IntrinsicKind_GET_FIELD_VALUE: ret = GET_FIELD_VALUE; break; case IntrinsicKind_SET_FIELD_VALUE: ret = SET_FIELD_VALUE; break; case IntrinsicKind_GET_STATIC_FIELD_VALUE: ret = GET_STATIC_FIELD_VALUE; break; case IntrinsicKind_SET_STATIC_FIELD_VALUE: ret = SET_STATIC_FIELD_VALUE; break; case IntrinsicKind_GET_FIELD_DECLARING_TYPE: ret = GET_FIELD_DECLARING_TYPE; break; case IntrinsicKind_GET_PARAMETER_INDEX: ret = GET_PARAMETER_INDEX; break; case IntrinsicKind_GET_PARAMETER_NAME: ret = GET_PARAMETER_NAME; break; case IntrinsicKind_GET_PARAMETER_TYPE: ret = GET_PARAMETER_TYPE; break; case IntrinsicKind_GET_PARAMETER_ANNOTATIONS: ret = GET_PARAMETER_ANNOTATIONS; break; case IntrinsicKind_GET_RELATED_PACKAGE_INF: ret = GET_RELATED_PACKAGE_INF; break; case IntrinsicKind_GET_PACKAGE_NAME: ret = GET_PACKAGE_NAME; break; case IntrinsicKind_GET_PACKAGE_NUM_OF_TYPE_INFOS: ret = GET_PACKAGE_NUM_OF_TYPE_INFOS; break; case IntrinsicKind_GET_PACKAGE_TYPE_INFO: ret = GET_PACKAGE_TYPE_INFO; break; case IntrinsicKind_GET_PACKAGE_NUM_OF_GLOBAL_METHODS: ret = GET_PACKAGE_NUM_OF_GLOBAL_METHODS; break; case IntrinsicKind_GET_PACKAGE_GLOBAL_METHOD_INFO: ret = GET_PACKAGE_GLOBAL_METHOD_INFO; break; case IntrinsicKind_GET_PACKAGE_NUM_OF_GLOBAL_FIELD_INFOS: ret = GET_PACKAGE_NUM_OF_GLOBAL_FIELD_INFOS; break; case IntrinsicKind_GET_PACKAGE_GLOBAL_FIELD_INFO: ret = GET_PACKAGE_GLOBAL_FIELD_INFO; break; case IntrinsicKind_LOAD_PACKAGE: ret = LOAD_PACKAGE; break; case IntrinsicKind_GET_PACKAGE_BY_QUALIFIEDNAME: ret = GET_PACKAGE_BY_QUALIFIEDNAME; break; case IntrinsicKind_GET_PACKAGE_VERSION: ret = GET_PACKAGE_VERSION; break; case IntrinsicKind_GET_SUB_PACKAGES: ret = GET_SUB_PACKAGES; break; case IntrinsicKind_REFLECTION_INTRINSIC_END_FLAG: ret = REFLECTION_INTRINSIC_END_FLAG; break; // ============================ cjnative only end ================= case IntrinsicKind_SLEEP: ret = SLEEP; break; case IntrinsicKind_SOURCE_FILE: ret = SOURCE_FILE; break; case IntrinsicKind_SOURCE_LINE: ret = SOURCE_LINE; break; case IntrinsicKind_IDENTITY_HASHCODE: ret = IDENTITY_HASHCODE; break; case IntrinsicKind_IDENTITY_HASHCODE_FOR_ARRAY: ret = IDENTITY_HASHCODE_FOR_ARRAY; break; // ============================ cjnative only start ================= // SYNC case IntrinsicKind_ATOMIC_LOAD: ret = ATOMIC_LOAD; break; case IntrinsicKind_ATOMIC_STORE: ret = ATOMIC_STORE; break; case IntrinsicKind_ATOMIC_SWAP: ret = ATOMIC_SWAP; break; case IntrinsicKind_ATOMIC_COMPARE_AND_SWAP: ret = ATOMIC_COMPARE_AND_SWAP; break; case IntrinsicKind_ATOMIC_FETCH_ADD: ret = ATOMIC_FETCH_ADD; break; case IntrinsicKind_ATOMIC_FETCH_SUB: ret = ATOMIC_FETCH_SUB; break; case IntrinsicKind_ATOMIC_FETCH_AND: ret = ATOMIC_FETCH_AND; break; case IntrinsicKind_ATOMIC_FETCH_OR: ret = ATOMIC_FETCH_OR; break; case IntrinsicKind_ATOMIC_FETCH_XOR: ret = ATOMIC_FETCH_XOR; break; case IntrinsicKind_MUTEX_INIT: ret = MUTEX_INIT; break; case IntrinsicKind_CJ_MUTEX_LOCK: ret = CJ_MUTEX_LOCK; break; case IntrinsicKind_MUTEX_TRY_LOCK: ret = MUTEX_TRY_LOCK; break; case IntrinsicKind_MUTEX_CHECK_STATUS: ret = MUTEX_CHECK_STATUS; break; case IntrinsicKind_MUTEX_UNLOCK: ret = MUTEX_UNLOCK; break; case IntrinsicKind_WAITQUEUE_INIT: ret = WAITQUEUE_INIT; break; case IntrinsicKind_MONITOR_INIT: ret = MONITOR_INIT; break; case IntrinsicKind_MOITIOR_WAIT: ret = MOITIOR_WAIT; break; case IntrinsicKind_MOITIOR_NOTIFY: ret = MOITIOR_NOTIFY; break; case IntrinsicKind_MOITIOR_NOTIFY_ALL: ret = MOITIOR_NOTIFY_ALL; break; case IntrinsicKind_MULTICONDITION_WAIT: ret = MULTICONDITION_WAIT; break; case IntrinsicKind_MULTICONDITION_NOTIFY: ret = MULTICONDITION_NOTIFY; break; case IntrinsicKind_MULTICONDITION_NOTIFY_ALL: ret = MULTICONDITION_NOTIFY_ALL; break; case IntrinsicKind_CROSS_ACCESS_BARRIER: ret = CROSS_ACCESS_BARRIER; break; case IntrinsicKind_CREATE_EXPORT_HANDLE: ret = CREATE_EXPORT_HANDLE; break; case IntrinsicKind_GET_EXPORTED_REF: ret = GET_EXPORTED_REF; break; case IntrinsicKind_REMOVE_EXPORTED_REF: ret = REMOVE_EXPORTED_REF; break; // ============================ cjnative only end ================= // Syscall // AST lib FFI case IntrinsicKind_FFI_CJ_AST_LEX: ret = FFI_CJ_AST_LEX; break; case IntrinsicKind_FFI_CJ_AST_PARSEEXPR: ret = FFI_CJ_AST_PARSEEXPR; break; case IntrinsicKind_FFI_CJ_AST_PARSEDECL: ret = FFI_CJ_AST_PARSEDECL; break; case IntrinsicKind_FFI_CJ_AST_PARSE_PROPMEMBERDECL: ret = FFI_CJ_AST_PARSE_PROPMEMBERDECL; break; case IntrinsicKind_FFI_CJ_AST_PARSE_PRICONSTRUCTOR: ret = FFI_CJ_AST_PARSE_PRICONSTRUCTOR; break; case IntrinsicKind_FFI_CJ_AST_PARSE_PATTERN: ret = FFI_CJ_AST_PARSE_PATTERN; break; case IntrinsicKind_FFI_CJ_AST_PARSE_TYPE: ret = FFI_CJ_AST_PARSE_TYPE; break; case IntrinsicKind_FFI_CJ_AST_PARSETOPLEVEL: ret = FFI_CJ_AST_PARSETOPLEVEL; break; case IntrinsicKind_FFI_CJ_AST_DIAGREPORT: ret = FFI_CJ_AST_DIAGREPORT; break; // Macro With Context FFI case IntrinsicKind_FFI_CJ_PARENT_CONTEXT: ret = FFI_CJ_PARENT_CONTEXT; break; case IntrinsicKind_FFI_CJ_MACRO_ITEM_INFO: ret = FFI_CJ_MACRO_ITEM_INFO; break; case IntrinsicKind_FFI_CJ_GET_CHILD_MESSAGES: ret = FFI_CJ_GET_CHILD_MESSAGES; break; case IntrinsicKind_FFI_CJ_CHECK_ADD_SPACE: ret = FFI_CJ_CHECK_ADD_SPACE; break; // CodeGen case IntrinsicKind_CG_UNSAFE_BEGIN: ret = CG_UNSAFE_BEGIN; break; case IntrinsicKind_CG_UNSAFE_END: ret = CG_UNSAFE_END; break; // C FFI funcs case IntrinsicKind_STRLEN: ret = STRLEN; break; case IntrinsicKind_MEMCPY_S: ret = MEMCPY_S; break; case IntrinsicKind_MEMSET_S: ret = MEMSET_S; break; case IntrinsicKind_FREE: ret = FREE; break; case IntrinsicKind_MALLOC: ret = MALLOC; break; case IntrinsicKind_STRCMP: ret = STRCMP; break; case IntrinsicKind_MEMCMP: ret = MEMCMP; break; case IntrinsicKind_STRNCMP: ret = STRNCMP; break; case IntrinsicKind_STRCASECMP: ret = STRCASECMP; break; // The interpreter is using these for cjnative backend as well case IntrinsicKind_ATOMIC_INT8_LOAD: ret = ATOMIC_INT8_LOAD; break; case IntrinsicKind_ATOMIC_INT8_STORE: ret = ATOMIC_INT8_STORE; break; case IntrinsicKind_ATOMIC_INT8_SWAP: ret = ATOMIC_INT8_SWAP; break; case IntrinsicKind_ATOMIC_INT8_CAS: ret = ATOMIC_INT8_CAS; break; case IntrinsicKind_ATOMIC_INT8_FETCH_ADD: ret = ATOMIC_INT8_FETCH_ADD; break; case IntrinsicKind_ATOMIC_INT8_FETCH_SUB: ret = ATOMIC_INT8_FETCH_SUB; break; case IntrinsicKind_ATOMIC_INT8_FETCH_AND: ret = ATOMIC_INT8_FETCH_AND; break; case IntrinsicKind_ATOMIC_INT8_FETCH_OR: ret = ATOMIC_INT8_FETCH_OR; break; case IntrinsicKind_ATOMIC_INT8_FETCH_XOR: ret = ATOMIC_INT8_FETCH_XOR; break; case IntrinsicKind_ATOMIC_INT16_LOAD: ret = ATOMIC_INT16_LOAD; break; case IntrinsicKind_ATOMIC_INT16_STORE: ret = ATOMIC_INT16_STORE; break; case IntrinsicKind_ATOMIC_INT16_SWAP: ret = ATOMIC_INT16_SWAP; break; case IntrinsicKind_ATOMIC_INT16_CAS: ret = ATOMIC_INT16_CAS; break; case IntrinsicKind_ATOMIC_INT16_FETCH_ADD: ret = ATOMIC_INT16_FETCH_ADD; break; case IntrinsicKind_ATOMIC_INT16_FETCH_SUB: ret = ATOMIC_INT16_FETCH_SUB; break; case IntrinsicKind_ATOMIC_INT16_FETCH_AND: ret = ATOMIC_INT16_FETCH_AND; break; case IntrinsicKind_ATOMIC_INT16_FETCH_OR: ret = ATOMIC_INT16_FETCH_OR; break; case IntrinsicKind_ATOMIC_INT16_FETCH_XOR: ret = ATOMIC_INT16_FETCH_XOR; break; case IntrinsicKind_ATOMIC_INT32_LOAD: ret = ATOMIC_INT32_LOAD; break; case IntrinsicKind_ATOMIC_INT32_STORE: ret = ATOMIC_INT32_STORE; break; case IntrinsicKind_ATOMIC_INT32_SWAP: ret = ATOMIC_INT32_SWAP; break; case IntrinsicKind_ATOMIC_INT32_CAS: ret = ATOMIC_INT32_CAS; break; case IntrinsicKind_ATOMIC_INT32_FETCH_ADD: ret = ATOMIC_INT32_FETCH_ADD; break; case IntrinsicKind_ATOMIC_INT32_FETCH_SUB: ret = ATOMIC_INT32_FETCH_SUB; break; case IntrinsicKind_ATOMIC_INT32_FETCH_AND: ret = ATOMIC_INT32_FETCH_AND; break; case IntrinsicKind_ATOMIC_INT32_FETCH_OR: ret = ATOMIC_INT32_FETCH_OR; break; case IntrinsicKind_ATOMIC_INT32_FETCH_XOR: ret = ATOMIC_INT32_FETCH_XOR; break; case IntrinsicKind_ATOMIC_INT64_LOAD: ret = ATOMIC_INT64_LOAD; break; case IntrinsicKind_ATOMIC_INT64_STORE: ret = ATOMIC_INT64_STORE; break; case IntrinsicKind_ATOMIC_INT64_SWAP: ret = ATOMIC_INT64_SWAP; break; case IntrinsicKind_ATOMIC_INT64_CAS: ret = ATOMIC_INT64_CAS; break; case IntrinsicKind_ATOMIC_INT64_FETCH_ADD: ret = ATOMIC_INT64_FETCH_ADD; break; case IntrinsicKind_ATOMIC_INT64_FETCH_SUB: ret = ATOMIC_INT64_FETCH_SUB; break; case IntrinsicKind_ATOMIC_INT64_FETCH_AND: ret = ATOMIC_INT64_FETCH_AND; break; case IntrinsicKind_ATOMIC_INT64_FETCH_OR: ret = ATOMIC_INT64_FETCH_OR; break; case IntrinsicKind_ATOMIC_INT64_FETCH_XOR: ret = ATOMIC_INT64_FETCH_XOR; break; case IntrinsicKind_ATOMIC_UINT8_LOAD: ret = ATOMIC_UINT8_LOAD; break; case IntrinsicKind_ATOMIC_UINT8_STORE: ret = ATOMIC_UINT8_STORE; break; case IntrinsicKind_ATOMIC_UINT8_SWAP: ret = ATOMIC_UINT8_SWAP; break; case IntrinsicKind_ATOMIC_UINT8_CAS: ret = ATOMIC_UINT8_CAS; break; case IntrinsicKind_ATOMIC_UINT8_FETCH_ADD: ret = ATOMIC_UINT8_FETCH_ADD; break; case IntrinsicKind_ATOMIC_UINT8_FETCH_SUB: ret = ATOMIC_UINT8_FETCH_SUB; break; case IntrinsicKind_ATOMIC_UINT8_FETCH_AND: ret = ATOMIC_UINT8_FETCH_AND; break; case IntrinsicKind_ATOMIC_UINT8_FETCH_OR: ret = ATOMIC_UINT8_FETCH_OR; break; case IntrinsicKind_ATOMIC_UINT8_FETCH_XOR: ret = ATOMIC_UINT8_FETCH_XOR; break; case IntrinsicKind_ATOMIC_UINT16_LOAD: ret = ATOMIC_UINT16_LOAD; break; case IntrinsicKind_ATOMIC_UINT16_STORE: ret = ATOMIC_UINT16_STORE; break; case IntrinsicKind_ATOMIC_UINT16_SWAP: ret = ATOMIC_UINT16_SWAP; break; case IntrinsicKind_ATOMIC_UINT16_CAS: ret = ATOMIC_UINT16_CAS; break; case IntrinsicKind_ATOMIC_UINT16_FETCH_ADD: ret = ATOMIC_UINT16_FETCH_ADD; break; case IntrinsicKind_ATOMIC_UINT16_FETCH_SUB: ret = ATOMIC_UINT16_FETCH_SUB; break; case IntrinsicKind_ATOMIC_UINT16_FETCH_AND: ret = ATOMIC_UINT16_FETCH_AND; break; case IntrinsicKind_ATOMIC_UINT16_FETCH_OR: ret = ATOMIC_UINT16_FETCH_OR; break; case IntrinsicKind_ATOMIC_UINT16_FETCH_XOR: ret = ATOMIC_UINT16_FETCH_XOR; break; case IntrinsicKind_ATOMIC_UINT32_LOAD: ret = ATOMIC_UINT32_LOAD; break; case IntrinsicKind_ATOMIC_UINT32_STORE: ret = ATOMIC_UINT32_STORE; break; case IntrinsicKind_ATOMIC_UINT32_SWAP: ret = ATOMIC_UINT32_SWAP; break; case IntrinsicKind_ATOMIC_UINT32_CAS: ret = ATOMIC_UINT32_CAS; break; case IntrinsicKind_ATOMIC_UINT32_FETCH_ADD: ret = ATOMIC_UINT32_FETCH_ADD; break; case IntrinsicKind_ATOMIC_UINT32_FETCH_SUB: ret = ATOMIC_UINT32_FETCH_SUB; break; case IntrinsicKind_ATOMIC_UINT32_FETCH_AND: ret = ATOMIC_UINT32_FETCH_AND; break; case IntrinsicKind_ATOMIC_UINT32_FETCH_OR: ret = ATOMIC_UINT32_FETCH_OR; break; case IntrinsicKind_ATOMIC_UINT32_FETCH_XOR: ret = ATOMIC_UINT32_FETCH_XOR; break; case IntrinsicKind_ATOMIC_UINT64_LOAD: ret = ATOMIC_UINT64_LOAD; break; case IntrinsicKind_ATOMIC_UINT64_STORE: ret = ATOMIC_UINT64_STORE; break; case IntrinsicKind_ATOMIC_UINT64_SWAP: ret = ATOMIC_UINT64_SWAP; break; case IntrinsicKind_ATOMIC_UINT64_CAS: ret = ATOMIC_UINT64_CAS; break; case IntrinsicKind_ATOMIC_UINT64_FETCH_ADD: ret = ATOMIC_UINT64_FETCH_ADD; break; case IntrinsicKind_ATOMIC_UINT64_FETCH_SUB: ret = ATOMIC_UINT64_FETCH_SUB; break; case IntrinsicKind_ATOMIC_UINT64_FETCH_AND: ret = ATOMIC_UINT64_FETCH_AND; break; case IntrinsicKind_ATOMIC_UINT64_FETCH_OR: ret = ATOMIC_UINT64_FETCH_OR; break; case IntrinsicKind_ATOMIC_UINT64_FETCH_XOR: ret = ATOMIC_UINT64_FETCH_XOR; break; case IntrinsicKind_ATOMIC_BOOL_LOAD: ret = ATOMIC_BOOL_LOAD; break; case IntrinsicKind_ATOMIC_BOOL_STORE: ret = ATOMIC_BOOL_STORE; break; case IntrinsicKind_ATOMIC_BOOL_SWAP: ret = ATOMIC_BOOL_SWAP; break; case IntrinsicKind_ATOMIC_BOOL_CAS: ret = ATOMIC_BOOL_CAS; break; case IntrinsicKind_ATOMIC_REFERENCEBASE_LOAD: ret = ATOMIC_REFERENCEBASE_LOAD; break; case IntrinsicKind_ATOMIC_REFERENCEBASE_STORE: ret = ATOMIC_REFERENCEBASE_STORE; break; case IntrinsicKind_ATOMIC_REFERENCEBASE_SWAP: ret = ATOMIC_REFERENCEBASE_SWAP; break; case IntrinsicKind_ATOMIC_REFERENCEBASE_CAS: ret = ATOMIC_REFERENCEBASE_CAS; break; case IntrinsicKind_ATOMIC_OPTIONREFERENCE_LOAD: ret = ATOMIC_OPTIONREFERENCE_LOAD; break; case IntrinsicKind_ATOMIC_OPTIONREFERENCE_STORE: ret = ATOMIC_OPTIONREFERENCE_STORE; break; case IntrinsicKind_ATOMIC_OPTIONREFERENCE_SWAP: ret = ATOMIC_OPTIONREFERENCE_SWAP; break; case IntrinsicKind_ATOMIC_OPTIONREFERENCE_CAS: ret = ATOMIC_OPTIONREFERENCE_CAS; break; // CHIR: Exception intrinsic case IntrinsicKind_BEGIN_CATCH: ret = BEGIN_CATCH; break; // CHIR: Math intrinsic case IntrinsicKind_ABS: ret = ABS; break; case IntrinsicKind_FABS: ret = FABS; break; case IntrinsicKind_FLOOR: ret = FLOOR; break; case IntrinsicKind_CEIL: ret = CEIL; break; case IntrinsicKind_TRUNC: ret = TRUNC; break; case IntrinsicKind_SIN: ret = SIN; break; case IntrinsicKind_COS: ret = COS; break; case IntrinsicKind_EXP: ret = EXP; break; case IntrinsicKind_EXP2: ret = EXP2; break; case IntrinsicKind_LOG: ret = LOG; break; case IntrinsicKind_LOG2: ret = LOG2; break; case IntrinsicKind_LOG10: ret = LOG10; break; case IntrinsicKind_SQRT: ret = SQRT; break; case IntrinsicKind_ROUND: ret = ROUND; break; case IntrinsicKind_POW: ret = POW; break; case IntrinsicKind_POWI: ret = POWI; break; case IntrinsicKind_BIT_CAST: ret = BIT_CAST; break; // preinitialize intrinsic case IntrinsicKind_PREINITIALIZE: ret = PREINITIALIZE; break; // Box cast intrinsic case IntrinsicKind_OBJECT_AS: ret = OBJECT_AS; break; case IntrinsicKind_IS_NULL: ret = IS_NULL; break; case IntrinsicKind_GET_TYPE_FOR_TYPE_PARAMETER: ret = GET_TYPE_FOR_TYPE_PARAMETER; break; case IntrinsicKind_IS_SUBTYPE_TYPES: ret = IS_SUBTYPE_TYPES; break; } return ret; } Package::AccessLevel DeSerialize(const PackageFormat::PackageAccessLevel& kind) { using namespace PackageFormat; auto ret = Package::AccessLevel::INTERNAL; switch (kind) { case PackageAccessLevel_INVALID: ret = Package::AccessLevel::INVALID; break; case PackageAccessLevel_INTERNAL: ret = Package::AccessLevel::INTERNAL; break; case PackageAccessLevel_PROTECTED: ret = Package::AccessLevel::PROTECTED; break; case PackageAccessLevel_PUBLIC: ret = Package::AccessLevel::PUBLIC; break; } return ret; } ToCHIR::Phase DeSerialize(const PackageFormat::Phase& kind) { using namespace PackageFormat; auto ret = ToCHIR::Phase::RAW; switch (kind) { case Phase_RAW: ret = ToCHIR::Phase::RAW; break; case Phase_OPT: ret = ToCHIR::Phase::OPT; break; case Phase_PLUGIN: ret = ToCHIR::Phase::PLUGIN; break; case Phase_ANALYSIS_FOR_CJLINT: ret = ToCHIR::Phase::ANALYSIS_FOR_CJLINT; break; } return ret; } TEST_F(CHIRDeSerialzierTest, TypeKindEnum) { using Cangjie::CHIR::Type; auto enumBegin = PackageFormat::CHIRTypeKind_MIN; auto enumEnd = PackageFormat::CHIRTypeKind_MAX; for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(Type::TypeKind(static_cast(i)), DeSerialize(static_cast(i))); } } TEST_F(CHIRDeSerialzierTest, SourceExprEnum) { using Cangjie::CHIR::SourceExpr; auto enumBegin = PackageFormat::SourceExpr_MIN; auto enumEnd = PackageFormat::SourceExpr_MAX; for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(SourceExpr(static_cast(i)), DeSerialize(static_cast(i))); } } TEST_F(CHIRDeSerialzierTest, LinkageEnum) { using Cangjie::Linkage; auto enumBegin = PackageFormat::Linkage_MIN; auto enumEnd = PackageFormat::Linkage_MAX; for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(Cangjie::Linkage(static_cast(i)), DeSerialize(static_cast(i))); } } TEST_F(CHIRDeSerialzierTest, SkipKindEnum) { using Cangjie::CHIR::SkipKind; auto enumBegin = PackageFormat::SkipKind_MIN; auto enumEnd = PackageFormat::SkipKind_MAX; for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ( SkipKind(static_cast(i)), DeSerialize(static_cast(i))); } } TEST_F(CHIRDeSerialzierTest, OverflowStrategyEnum) { using Cangjie::OverflowStrategy; auto enumBegin = PackageFormat::OverflowStrategy_MIN; auto enumEnd = PackageFormat::OverflowStrategy_MAX; for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(OverflowStrategy(static_cast(i)), DeSerialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRDeSerialzierTest, ValueKindEnum) { using Cangjie::CHIR::Value; auto enumBegin = PackageFormat::ValueKind_MIN; auto enumEnd = PackageFormat::ValueKind_MAX; for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(Value::ValueKind(static_cast(i)), DeSerialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRDeSerialzierTest, ConstantValueKindEnum) { using Cangjie::CHIR::ConstantValueKind; auto enumBegin = PackageFormat::ConstantValueKind_MIN; auto enumEnd = PackageFormat::ConstantValueKind_MAX; for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(ConstantValueKind(static_cast(i)), DeSerialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRDeSerialzierTest, FuncKindEnum) { using namespace PackageFormat; using Cangjie::CHIR::FuncKind; auto enumBegin = FuncKind_MIN; auto enumEnd = FuncKind_MAX; for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ( FuncKind(static_cast(i)), DeSerialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRDeSerialzierTest, CustomDefKindEnum) { using Cangjie::CHIR::CustomDefKind; auto enumBegin = PackageFormat::CustomDefKind_MIN; auto enumEnd = PackageFormat::CustomDefKind_MAX; for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(CustomDefKind(static_cast(i)), DeSerialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRDeSerialzierTest, ExprKindEnum) { using namespace PackageFormat; auto enumBegin = PackageFormat::CHIRExprKind_MIN; auto enumEnd = PackageFormat::CHIRExprKind_MAX; for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(ExprKind(static_cast(i)), DeSerialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRDeSerialzierTest, IntrinsicKindEnum) { using namespace PackageFormat; auto enumBegin = PackageFormat::IntrinsicKind_MIN; auto enumEnd = PackageFormat::IntrinsicKind_MAX; for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(Cangjie::CHIR::IntrinsicKind(static_cast(i)), DeSerialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRDeSerialzierTest, PackageAccessLevelEnum) { using namespace PackageFormat; auto enumBegin = PackageFormat::PackageAccessLevel_MIN; auto enumEnd = PackageFormat::PackageAccessLevel_MAX; for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(Package::AccessLevel(static_cast(i)), DeSerialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRDeSerialzierTest, Phase) { using namespace PackageFormat; auto enumBegin = PackageFormat::Phase_MIN; auto enumEnd = PackageFormat::Phase_MAX; for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ( ToCHIR::Phase(static_cast(i)), DeSerialize(static_cast(i))) << "cur i: " << i; } } cangjie_compiler-1.0.7/unittests/CHIR/CHIRSerialzierTest.cpp000066400000000000000000001774711510705540100237420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include #include "gtest/gtest.h" #include "cangjie/Basic/Print.h" #include "cangjie/CHIR/CHIR.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/IntrinsicKind.h" #include "cangjie/CHIR/Serializer/CHIRSerializer.h" #include "cangjie/CHIR/Type/CustomTypeDef.h" #include "cangjie/CHIR/Type/Type.h" using namespace Cangjie::CHIR; class CHIRSerialzierTest : public ::testing::Test { protected: void SetUp() override { } }; PackageFormat::CHIRTypeKind Serialize(const Type::TypeKind& kind) { using namespace PackageFormat; using Cangjie::CHIR::Type; auto ret = CHIRTypeKind_INVALID; switch (kind) { case Type::TypeKind::TYPE_INVALID: ret = CHIRTypeKind_INVALID; break; // integer case Type::TypeKind::TYPE_INT8: ret = CHIRTypeKind_INT8; break; case Type::TypeKind::TYPE_INT16: ret = CHIRTypeKind_INT16; break; case Type::TypeKind::TYPE_INT32: ret = CHIRTypeKind_INT32; break; case Type::TypeKind::TYPE_INT64: ret = CHIRTypeKind_INT64; break; case Type::TypeKind::TYPE_INT_NATIVE: ret = CHIRTypeKind_INT_NATIVE; break; // unsigned integer case Type::TypeKind::TYPE_UINT8: ret = CHIRTypeKind_UINT8; break; case Type::TypeKind::TYPE_UINT16: ret = CHIRTypeKind_UINT16; break; case Type::TypeKind::TYPE_UINT32: ret = CHIRTypeKind_UINT32; break; case Type::TypeKind::TYPE_UINT64: ret = CHIRTypeKind_UINT64; break; case Type::TypeKind::TYPE_UINT_NATIVE: ret = CHIRTypeKind_UINT_NATIVE; break; // float case Type::TypeKind::TYPE_FLOAT16: ret = CHIRTypeKind_FLOAT16; break; case Type::TypeKind::TYPE_FLOAT32: ret = CHIRTypeKind_FLOAT32; break; case Type::TypeKind::TYPE_FLOAT64: ret = CHIRTypeKind_FLOAT64; break; // other primitive type case Type::TypeKind::TYPE_RUNE: ret = CHIRTypeKind_RUNE; break; case Type::TypeKind::TYPE_BOOLEAN: ret = CHIRTypeKind_BOOLEAN; break; case Type::TypeKind::TYPE_UNIT: ret = CHIRTypeKind_UNIT; break; case Type::TypeKind::TYPE_NOTHING: ret = CHIRTypeKind_NOTHING; break; // Void type case Type::TypeKind::TYPE_VOID: ret = CHIRTypeKind_VOID; break; // composite type case Type::TypeKind::TYPE_TUPLE: ret = CHIRTypeKind_TUPLE; break; case Type::TypeKind::TYPE_STRUCT: ret = CHIRTypeKind_STRUCT; break; case Type::TypeKind::TYPE_ENUM: ret = CHIRTypeKind_ENUM; break; case Type::TypeKind::TYPE_FUNC: ret = CHIRTypeKind_FUNC; break; case Type::TypeKind::TYPE_CLASS: ret = CHIRTypeKind_CLASS; break; // Built-in array related type case Type::TypeKind::TYPE_RAWARRAY: ret = CHIRTypeKind_RAWARRAY; break; case Type::TypeKind::TYPE_VARRAY: ret = CHIRTypeKind_VARRAY; break; // Built-in CFFI related type case Type::TypeKind::TYPE_CPOINTER: ret = CHIRTypeKind_C_POINTER; break; case Type::TypeKind::TYPE_CSTRING: ret = CHIRTypeKind_C_STRING; break; // Generic type case Type::TypeKind::TYPE_GENERIC: ret = CHIRTypeKind_GENERIC; break; // Referece to an value with abritray type case Type::TypeKind::TYPE_REFTYPE: ret = CHIRTypeKind_REFTYPE; break; // Built-in box type case Type::TypeKind::TYPE_BOXTYPE: ret = CHIRTypeKind_BOXTYPE; break; case Type::TypeKind::TYPE_THIS: ret = CHIRTypeKind_THIS; break; case Type::TypeKind::MAX_TYPE_KIND: CJC_ABORT(); break; // no defalut here, due to we need use compiler to check all value be handled. } return ret; } PackageFormat::SourceExpr Serialize(const SourceExpr& kind) { using namespace PackageFormat; using Cangjie::CHIR::SourceExpr; auto ret = SourceExpr_IF_EXPR; switch (kind) { case SourceExpr::IF_EXPR: ret = SourceExpr_IF_EXPR; break; case SourceExpr::WHILE_EXPR: ret = SourceExpr_WHILE_EXPR; break; case SourceExpr::DO_WHILE_EXPR: ret = SourceExpr_DO_WHILE_EXPR; break; case SourceExpr::MATCH_EXPR: ret = SourceExpr_MATCH_EXPR; break; case SourceExpr::IF_LET_OR_WHILE_LET: ret = SourceExpr_IF_LET_OR_WHILE_LET; break; case SourceExpr::QUEST: ret = SourceExpr_QUEST; break; case SourceExpr::BINARY: ret = SourceExpr_BINARY; break; case SourceExpr::FOR_IN_EXPR: ret = SourceExpr_FOR_IN_EXPR; break; case SourceExpr::OTHER: ret = SourceExpr_OTHER; break; } return ret; } PackageFormat::Linkage Serialize(const Cangjie::Linkage& kind) { using namespace PackageFormat; using Cangjie::Linkage; auto ret = Linkage_WEAK_ODR; switch (kind) { case Linkage::WEAK_ODR: ret = Linkage_WEAK_ODR; break; case Linkage::EXTERNAL: ret = Linkage_EXTERNAL; break; case Linkage::INTERNAL: ret = Linkage_INTERNAL; break; case Linkage::LINKONCE_ODR: ret = Linkage_LINKONCE_ODR; break; case Linkage::EXTERNAL_WEAK: ret = Linkage_EXTERNAL_WEAK; break; } return ret; } PackageFormat::SkipKind Serialize(const Cangjie::CHIR::SkipKind& kind) { using namespace PackageFormat; using Cangjie::CHIR::SkipKind; auto ret = SkipKind_NO_SKIP; switch (kind) { case SkipKind::NO_SKIP: ret = SkipKind_NO_SKIP; break; case SkipKind::SKIP_DCE_WARNING: ret = SkipKind_SKIP_DCE_WARNING; break; case SkipKind::SKIP_FORIN_EXIT: ret = SkipKind_SKIP_FORIN_EXIT; break; case SkipKind::SKIP_VIC: ret = SkipKind_SKIP_VIC; break; } return ret; } PackageFormat::OverflowStrategy Serialize(const Cangjie::OverflowStrategy& kind) { using namespace PackageFormat; using Cangjie::OverflowStrategy; auto ret = OverflowStrategy_NA; switch (kind) { case OverflowStrategy::NA: ret = OverflowStrategy_NA; break; case OverflowStrategy::CHECKED: ret = OverflowStrategy_CHECKED; break; case OverflowStrategy::WRAPPING: ret = OverflowStrategy_WRAPPING; break; case OverflowStrategy::THROWING: ret = OverflowStrategy_THROWING; break; case OverflowStrategy::SATURATING: ret = OverflowStrategy_SATURATING; break; case OverflowStrategy::OVERFLOW_STRATEGY_END: CJC_ABORT(); break; } return ret; } PackageFormat::ValueKind Serialize(const Value::ValueKind& kind) { using namespace PackageFormat; using Cangjie::CHIR::Value; auto ret = ValueKind_LITERAL; switch (kind) { case Value::ValueKind::KIND_LITERAL: ret = ValueKind_LITERAL; break; case Value::ValueKind::KIND_GLOBALVAR: ret = ValueKind_GLOBALVAR; break; case Value::ValueKind::KIND_PARAMETER: ret = ValueKind_PARAMETER; break; case Value::ValueKind::KIND_IMP_FUNC: ret = ValueKind_IMPORTED_FUNC; break; case Value::ValueKind::KIND_IMP_VAR: ret = ValueKind_IMPORTED_VAR; break; case Value::ValueKind::KIND_LOCALVAR: ret = ValueKind_LOCALVAR; break; case Value::ValueKind::KIND_FUNC: ret = ValueKind_FUNC; break; case Value::ValueKind::KIND_BLOCK: ret = ValueKind_BLOCK; break; case Value::ValueKind::KIND_BLOCK_GROUP: ret = ValueKind_BLOCK_GROUP; break; } return ret; } PackageFormat::ConstantValueKind Serialize(const ConstantValueKind& kind) { using namespace PackageFormat; using Cangjie::CHIR::ConstantValueKind; auto ret = ConstantValueKind_BOOL; switch (kind) { case ConstantValueKind::KIND_BOOL: ret = ConstantValueKind_BOOL; break; case ConstantValueKind::KIND_RUNE: ret = ConstantValueKind_RUNE; break; case ConstantValueKind::KIND_INT: ret = ConstantValueKind_INT; break; case ConstantValueKind::KIND_FLOAT: ret = ConstantValueKind_FLOAT; break; case ConstantValueKind::KIND_STRING: ret = ConstantValueKind_STRING; break; case ConstantValueKind::KIND_UNIT: ret = ConstantValueKind_UNIT; break; case ConstantValueKind::KIND_NULL: ret = ConstantValueKind_NULL; break; case ConstantValueKind::KIND_FUNC: ret = ConstantValueKind_FUNC; break; } return ret; } PackageFormat::FuncKind Serialize(const FuncKind& kind) { using namespace PackageFormat; using Cangjie::CHIR::FuncKind; auto ret = FuncKind_DEFAULT; switch (kind) { case FuncKind::DEFAULT: ret = FuncKind_DEFAULT; break; case FuncKind::GETTER: ret = FuncKind_GETTER; break; case FuncKind::SETTER: ret = FuncKind_SETTER; break; case FuncKind::LAMBDA: ret = FuncKind_LAMBDA; break; case FuncKind::CLASS_CONSTRUCTOR: ret = FuncKind_CLASS_CONSTRUCTOR; break; case FuncKind::PRIMAL_CLASS_CONSTRUCTOR: ret = FuncKind_PRIMAL_CLASS_CONSTRUCTOR; break; case FuncKind::STRUCT_CONSTRUCTOR: ret = FuncKind_STRUCT_CONSTRUCTOR; break; case FuncKind::PRIMAL_STRUCT_CONSTRUCTOR: ret = FuncKind_PRIMAL_STRUCT_CONSTRUCTOR; break; case FuncKind::GLOBALVAR_INIT: ret = FuncKind_GLOBALVAR_INIT; break; case FuncKind::FINALIZER: ret = FuncKind_FINALIZER; break; case FuncKind::MAIN_ENTRY: ret = FuncKind_MAIN_ENTRY; break; case FuncKind::ANNOFACTORY_FUNC: ret = FuncKind_ANNOFACTORY_FUNC; break; case FuncKind::MACRO_FUNC: ret = FuncKind_MACRO_FUNC; break; case FuncKind::DEFAULT_PARAMETER_FUNC: ret = FuncKind_DEFAULT_PARAMETER_FUNC; break; case FuncKind::INSTANCEVAR_INIT: ret = FuncKind_INSTANCEVAR_INIT; break; case FuncKind::FUNCKIND_END: CJC_ABORT(); break; } return ret; } PackageFormat::CustomDefKind Serialize(const CustomDefKind& kind) { using namespace PackageFormat; using Cangjie::CHIR::CustomDefKind; auto ret = CustomDefKind_STRUCT; switch (kind) { case CustomDefKind::TYPE_STRUCT: ret = CustomDefKind_STRUCT; break; case CustomDefKind::TYPE_ENUM: ret = CustomDefKind_ENUM; break; case CustomDefKind::TYPE_CLASS: ret = CustomDefKind_CLASS; break; case CustomDefKind::TYPE_EXTEND: ret = CustomDefKind_EXTEND; break; } return ret; } PackageFormat::CHIRExprKind Serialize(const ExprKind& kind) { using namespace PackageFormat; auto ret = CHIRExprKind_INVALID; switch (kind) { case ExprKind::INVALID: ret = CHIRExprKind_INVALID; break; case ExprKind::GOTO: ret = CHIRExprKind_GOTO; break; case ExprKind::BRANCH: ret = CHIRExprKind_BRANCH; break; case ExprKind::MULTIBRANCH: ret = CHIRExprKind_MULTIBRANCH; break; case ExprKind::EXIT: ret = CHIRExprKind_EXIT; break; case ExprKind::APPLY_WITH_EXCEPTION: ret = CHIRExprKind_APPLY_WITH_EXCEPTION; break; case ExprKind::INVOKE_WITH_EXCEPTION: ret = CHIRExprKind_INVOKE_WITH_EXCEPTION; break; case ExprKind::INVOKESTATIC_WITH_EXCEPTION: ret = CHIRExprKind_INVOKESTATIC_WITH_EXCEPTION; break; case ExprKind::RAISE_EXCEPTION: ret = CHIRExprKind_RAISE_EXCEPTION; break; case ExprKind::INT_OP_WITH_EXCEPTION: ret = CHIRExprKind_INT_OP_WITH_EXCEPTION; break; case ExprKind::SPAWN_WITH_EXCEPTION: ret = CHIRExprKind_SPAWN_WITH_EXCEPTION; break; case ExprKind::TYPECAST_WITH_EXCEPTION: ret = CHIRExprKind_TYPECAST_WITH_EXCEPTION; break; case ExprKind::INTRINSIC_WITH_EXCEPTION: ret = CHIRExprKind_INTRINSIC_WITH_EXCEPTION; break; case ExprKind::ALLOCATE_WITH_EXCEPTION: ret = CHIRExprKind_ALLOCATE_WITH_EXCEPTION; break; case ExprKind::RAW_ARRAY_ALLOCATE_WITH_EXCEPTION: ret = CHIRExprKind_RAW_ARRAY_ALLOCATE_WITH_EXCEPTION; break; case ExprKind::NEG: ret = CHIRExprKind_NEG; break; case ExprKind::NOT: ret = CHIRExprKind_NOT; break; case ExprKind::BITNOT: ret = CHIRExprKind_BITNOT; break; case ExprKind::ADD: ret = CHIRExprKind_ADD; break; case ExprKind::SUB: ret = CHIRExprKind_SUB; break; case ExprKind::MUL: ret = CHIRExprKind_MUL; break; case ExprKind::DIV: ret = CHIRExprKind_DIV; break; case ExprKind::MOD: ret = CHIRExprKind_MOD; break; case ExprKind::EXP: ret = CHIRExprKind_EXP; break; case ExprKind::LSHIFT: ret = CHIRExprKind_LSHIFT; break; case ExprKind::RSHIFT: ret = CHIRExprKind_RSHIFT; break; case ExprKind::BITAND: ret = CHIRExprKind_BITAND; break; case ExprKind::BITOR: ret = CHIRExprKind_BITOR; break; case ExprKind::BITXOR: ret = CHIRExprKind_BITXOR; break; case ExprKind::LT: ret = CHIRExprKind_LT; break; case ExprKind::GT: ret = CHIRExprKind_GT; break; case ExprKind::LE: ret = CHIRExprKind_LE; break; case ExprKind::GE: ret = CHIRExprKind_GE; break; case ExprKind::EQUAL: ret = CHIRExprKind_EQUAL; break; case ExprKind::NOTEQUAL: ret = CHIRExprKind_NOTEQUAL; break; case ExprKind::AND: ret = CHIRExprKind_AND; break; case ExprKind::OR: ret = CHIRExprKind_OR; break; case ExprKind::ALLOCATE: ret = CHIRExprKind_ALLOCATE; break; case ExprKind::LOAD: ret = CHIRExprKind_LOAD; break; case ExprKind::STORE: ret = CHIRExprKind_STORE; break; case ExprKind::GET_ELEMENT_REF: ret = CHIRExprKind_GET_ELEMENT_REF; break; case ExprKind::GET_ELEMENT_BY_NAME: ret = CHIRExprKind_GET_ELEMENT_BY_NAME; break; case ExprKind::STORE_ELEMENT_REF: ret = CHIRExprKind_STORE_ELEMENT_REF; break; case ExprKind::STORE_ELEMENT_BY_NAME: ret = CHIRExprKind_STORE_ELEMENT_BY_NAME; break; case ExprKind::IF: ret = CHIRExprKind_IF; break; case ExprKind::LOOP: ret = CHIRExprKind_LOOP; break; case ExprKind::FORIN_RANGE: ret = CHIRExprKind_FORIN_RANGE; break; case ExprKind::FORIN_ITER: ret = CHIRExprKind_FORIN_ITER; break; case ExprKind::FORIN_CLOSED_RANGE: ret = CHIRExprKind_FORIN_CLOSED_RANGE; break; case ExprKind::LAMBDA: ret = CHIRExprKind_LAMBDA; break; case ExprKind::CONSTANT: ret = CHIRExprKind_CONSTANT; break; case ExprKind::DEBUGEXPR: ret = CHIRExprKind_DEBUGEXPR; break; case ExprKind::TUPLE: ret = CHIRExprKind_TUPLE; break; case ExprKind::FIELD: ret = CHIRExprKind_FIELD; break; case ExprKind::FIELD_BY_NAME: ret = CHIRExprKind_FIELD_BY_NAME; break; case ExprKind::APPLY: ret = CHIRExprKind_APPLY; break; case ExprKind::INVOKE: ret = CHIRExprKind_INVOKE; break; case ExprKind::INVOKESTATIC: ret = CHIRExprKind_INVOKE_STATIC; break; case ExprKind::INSTANCEOF: ret = CHIRExprKind_INSTANCEOF; break; case ExprKind::TYPECAST: ret = CHIRExprKind_TYPECAST; break; case ExprKind::GET_EXCEPTION: ret = CHIRExprKind_GET_EXCEPTION; break; case ExprKind::RAW_ARRAY_ALLOCATE: ret = CHIRExprKind_RAW_ARRAY_ALLOCATE; break; case ExprKind::RAW_ARRAY_LITERAL_INIT: ret = CHIRExprKind_RAW_ARRAY_LITERAL_INIT; break; case ExprKind::RAW_ARRAY_INIT_BY_VALUE: ret = CHIRExprKind_RAW_ARRAY_INIT_BY_VALUE; break; case ExprKind::VARRAY: ret = CHIRExprKind_VARRAY; break; case ExprKind::VARRAY_BUILDER: ret = CHIRExprKind_VARRAY_BUILDER; break; case ExprKind::INTRINSIC: ret = CHIRExprKind_INTRINSIC; break; case ExprKind::SPAWN: ret = CHIRExprKind_SPAWN; break; case ExprKind::GET_INSTANTIATE_VALUE: ret = CHIRExprKind_GET_INSTANTIATE_VALUE; break; case ExprKind::BOX: ret = CHIRExprKind_BOX; break; case ExprKind::UNBOX: ret = CHIRExprKind_UNBOX; break; case ExprKind::TRANSFORM_TO_GENERIC: ret = CHIRExprKind_TRANSFORM_TO_GENERIC; break; case ExprKind::TRANSFORM_TO_CONCRETE: ret = CHIRExprKind_TRANSFORM_TO_CONCRETE; break; case ExprKind::UNBOX_TO_REF: ret = CHIRExprKind_UNBOX_TO_REF; break; case ExprKind::GET_RTTI: ret = CHIRExprKind_GET_RTTI; break; case ExprKind::GET_RTTI_STATIC: ret = CHIRExprKind_GET_RTTI_STATIC; break; case ExprKind::MAX_EXPR_KINDS: CJC_ABORT(); break; // no defalut here, due to we need use compiler to check all value be handled. } return ret; } PackageFormat::IntrinsicKind Serialize(const IntrinsicKind& kind) { using namespace PackageFormat; auto ret = IntrinsicKind_NOT_INTRINSIC; switch (kind) { case NOT_INTRINSIC: ret = IntrinsicKind_NOT_INTRINSIC; break; case NOT_IMPLEMENTED: ret = IntrinsicKind_NOT_IMPLEMENTED; break; case ARRAY_INIT: ret = IntrinsicKind_ARRAY_INIT; break; case SIZE_OF: ret = IntrinsicKind_SIZE_OF; break; case ALIGN_OF: ret = IntrinsicKind_ALIGN_OF; break; case ARRAY_ACQUIRE_RAW_DATA: ret = IntrinsicKind_ARRAY_ACQUIRE_RAW_DATA; break; case ARRAY_RELEASE_RAW_DATA: ret = IntrinsicKind_ARRAY_RELEASE_RAW_DATA; break; case ARRAY_BUILT_IN_COPY_TO: ret = IntrinsicKind_ARRAY_BUILT_IN_COPY_TO; break; case ARRAY_GET: ret = IntrinsicKind_ARRAY_GET; break; case ARRAY_SET: ret = IntrinsicKind_ARRAY_SET; break; case ARRAY_GET_UNCHECKED: ret = IntrinsicKind_ARRAY_GET_UNCHECKED; break; case ARRAY_GET_REF_UNCHECKED: ret = IntrinsicKind_ARRAY_GET_REF_UNCHECKED; break; case ARRAY_SET_UNCHECKED: ret = IntrinsicKind_ARRAY_SET_UNCHECKED; break; case ARRAY_SIZE: ret = IntrinsicKind_ARRAY_SIZE; break; case ARRAY_CLONE: ret = IntrinsicKind_ARRAY_CLONE; break; case ARRAY_SLICE_INIT: ret = IntrinsicKind_ARRAY_SLICE_INIT; break; case ARRAY_SLICE: ret = IntrinsicKind_ARRAY_SLICE; break; case ARRAY_SLICE_RAWARRAY: ret = IntrinsicKind_ARRAY_SLICE_RAWARRAY; break; case ARRAY_SLICE_START: ret = IntrinsicKind_ARRAY_SLICE_START; break; case ARRAY_SLICE_SIZE: ret = IntrinsicKind_ARRAY_SLICE_SIZE; break; case ARRAY_SLICE_GET_ELEMENT: ret = IntrinsicKind_ARRAY_SLICE_GET_ELEMENT; break; case ARRAY_SLICE_GET_ELEMENT_UNCHECKED: ret = IntrinsicKind_ARRAY_SLICE_GET_ELEMENT_UNCHECKED; break; case ARRAY_SLICE_SET_ELEMENT: ret = IntrinsicKind_ARRAY_SLICE_SET_ELEMENT; break; case ARRAY_SLICE_SET_ELEMENT_UNCHECKED: ret = IntrinsicKind_ARRAY_SLICE_SET_ELEMENT_UNCHECKED; break; case FILL_IN_STACK_TRACE: ret = IntrinsicKind_FILL_IN_STACK_TRACE; break; case DECODE_STACK_TRACE: ret = IntrinsicKind_DECODE_STACK_TRACE; break; case CHR: ret = IntrinsicKind_CHR; break; case ORD: ret = IntrinsicKind_ORD; break; case CPOINTER_GET_POINTER_ADDRESS: ret = IntrinsicKind_CPOINTER_GET_POINTER_ADDRESS; break; case CPOINTER_INIT0: ret = IntrinsicKind_CPOINTER_INIT0; break; case CPOINTER_INIT1: ret = IntrinsicKind_CPOINTER_INIT1; break; case CPOINTER_READ: ret = IntrinsicKind_CPOINTER_READ; break; case CPOINTER_WRITE: ret = IntrinsicKind_CPOINTER_WRITE; break; case CPOINTER_ADD: ret = IntrinsicKind_CPOINTER_ADD; break; case CSTRING_INIT: ret = IntrinsicKind_CSTRING_INIT; break; case CSTRING_CONVERT_CSTR_TO_PTR: ret = IntrinsicKind_CSTRING_CONVERT_CSTR_TO_PTR; break; case INOUT_PARAM: ret = IntrinsicKind_INOUT_PARAM; break; case REGISTER_WATCHED_OBJECT: ret = IntrinsicKind_REGISTER_WATCHED_OBJECT; break; case OBJECT_REFEQ: ret = IntrinsicKind_OBJECT_REFEQ; break; case RAW_ARRAY_REFEQ: ret = IntrinsicKind_RAW_ARRAY_REFEQ; break; case OBJECT_ZERO_VALUE: ret = IntrinsicKind_OBJECT_ZERO_VALUE; break; case INVOKE_GC: ret = IntrinsicKind_INVOKE_GC; break; case SET_GC_THRESHOLD: ret = IntrinsicKind_SET_GC_THRESHOLD; break; case DUMP_CJ_HEAP_DATA: ret = IntrinsicKind_DUMP_CJ_HEAP_DATA; break; case GET_GC_COUNT: ret = IntrinsicKind_GET_GC_COUNT; break; case GET_GC_TIME_US: ret = IntrinsicKind_GET_GC_TIME_US; break; case GET_GC_FREED_SIZE: ret = IntrinsicKind_GET_GC_FREED_SIZE; break; case START_CJ_CPU_PROFILING: ret = IntrinsicKind_START_CJ_CPU_PROFILING; break; case STOP_CJ_CPU_PROFILING: ret = IntrinsicKind_STOP_CJ_CPU_PROFILING; break; case BLACK_BOX: ret = IntrinsicKind_BLACK_BOX; break; case GET_MAX_HEAP_SIZE: ret = IntrinsicKind_GET_MAX_HEAP_SIZE; break; case GET_ALLOCATE_HEAP_SIZE: ret = IntrinsicKind_GET_ALLOCATE_HEAP_SIZE; break; case GET_REAL_HEAP_SIZE: ret = IntrinsicKind_GET_REAL_HEAP_SIZE; break; case GET_THREAD_NUMBER: ret = IntrinsicKind_GET_THREAD_NUMBER; break; case GET_BLOCKING_THREAD_NUMBER: ret = IntrinsicKind_GET_BLOCKING_THREAD_NUMBER; break; case GET_NATIVE_THREAD_NUMBER: ret = IntrinsicKind_GET_NATIVE_THREAD_NUMBER; break; case VARRAY_SET: ret = IntrinsicKind_VARRAY_SET; break; case VARRAY_GET: ret = IntrinsicKind_VARRAY_GET; break; case FUTURE_INIT: ret = IntrinsicKind_FUTURE_INIT; break; case FUTURE_IS_COMPLETE: ret = IntrinsicKind_FUTURE_IS_COMPLETE; break; case FUTURE_WAIT: ret = IntrinsicKind_FUTURE_WAIT; break; case FUTURE_NOTIFYALL: ret = IntrinsicKind_FUTURE_NOTIFYALL; break; case IS_THREAD_OBJECT_INITED: ret = IntrinsicKind_IS_THREAD_OBJECT_INITED; break; case GET_THREAD_OBJECT: ret = IntrinsicKind_GET_THREAD_OBJECT; break; case SET_THREAD_OBJECT: ret = IntrinsicKind_SET_THREAD_OBJECT; break; case OVERFLOW_CHECKED_ADD: ret = IntrinsicKind_OVERFLOW_CHECKED_ADD; break; case OVERFLOW_CHECKED_SUB: ret = IntrinsicKind_OVERFLOW_CHECKED_SUB; break; case OVERFLOW_CHECKED_MUL: ret = IntrinsicKind_OVERFLOW_CHECKED_MUL; break; case OVERFLOW_CHECKED_DIV: ret = IntrinsicKind_OVERFLOW_CHECKED_DIV; break; case OVERFLOW_CHECKED_MOD: ret = IntrinsicKind_OVERFLOW_CHECKED_MOD; break; case OVERFLOW_CHECKED_POW: ret = IntrinsicKind_OVERFLOW_CHECKED_POW; break; case OVERFLOW_CHECKED_INC: ret = IntrinsicKind_OVERFLOW_CHECKED_INC; break; case OVERFLOW_CHECKED_DEC: ret = IntrinsicKind_OVERFLOW_CHECKED_DEC; break; case OVERFLOW_CHECKED_NEG: ret = IntrinsicKind_OVERFLOW_CHECKED_NEG; break; case OVERFLOW_THROWING_ADD: ret = IntrinsicKind_OVERFLOW_THROWING_ADD; break; case OVERFLOW_THROWING_SUB: ret = IntrinsicKind_OVERFLOW_THROWING_SUB; break; case OVERFLOW_THROWING_MUL: ret = IntrinsicKind_OVERFLOW_THROWING_MUL; break; case OVERFLOW_THROWING_DIV: ret = IntrinsicKind_OVERFLOW_THROWING_DIV; break; case OVERFLOW_THROWING_MOD: ret = IntrinsicKind_OVERFLOW_THROWING_MOD; break; case OVERFLOW_THROWING_POW: ret = IntrinsicKind_OVERFLOW_THROWING_POW; break; case OVERFLOW_THROWING_INC: ret = IntrinsicKind_OVERFLOW_THROWING_INC; break; case OVERFLOW_THROWING_DEC: ret = IntrinsicKind_OVERFLOW_THROWING_DEC; break; case OVERFLOW_THROWING_NEG: ret = IntrinsicKind_OVERFLOW_THROWING_NEG; break; case OVERFLOW_SATURATING_ADD: ret = IntrinsicKind_OVERFLOW_SATURATING_ADD; break; case OVERFLOW_SATURATING_SUB: ret = IntrinsicKind_OVERFLOW_SATURATING_SUB; break; case OVERFLOW_SATURATING_MUL: ret = IntrinsicKind_OVERFLOW_SATURATING_MUL; break; case OVERFLOW_SATURATING_DIV: ret = IntrinsicKind_OVERFLOW_SATURATING_DIV; break; case OVERFLOW_SATURATING_MOD: ret = IntrinsicKind_OVERFLOW_SATURATING_MOD; break; case OVERFLOW_SATURATING_POW: ret = IntrinsicKind_OVERFLOW_SATURATING_POW; break; case OVERFLOW_SATURATING_INC: ret = IntrinsicKind_OVERFLOW_SATURATING_INC; break; case OVERFLOW_SATURATING_DEC: ret = IntrinsicKind_OVERFLOW_SATURATING_DEC; break; case OVERFLOW_SATURATING_NEG: ret = IntrinsicKind_OVERFLOW_SATURATING_NEG; break; case OVERFLOW_WRAPPING_ADD: ret = IntrinsicKind_OVERFLOW_WRAPPING_ADD; break; case OVERFLOW_WRAPPING_SUB: ret = IntrinsicKind_OVERFLOW_WRAPPING_SUB; break; case OVERFLOW_WRAPPING_MUL: ret = IntrinsicKind_OVERFLOW_WRAPPING_MUL; break; case OVERFLOW_WRAPPING_DIV: ret = IntrinsicKind_OVERFLOW_WRAPPING_DIV; break; case OVERFLOW_WRAPPING_MOD: ret = IntrinsicKind_OVERFLOW_WRAPPING_MOD; break; case OVERFLOW_WRAPPING_POW: ret = IntrinsicKind_OVERFLOW_WRAPPING_POW; break; case OVERFLOW_WRAPPING_INC: ret = IntrinsicKind_OVERFLOW_WRAPPING_INC; break; case OVERFLOW_WRAPPING_DEC: ret = IntrinsicKind_OVERFLOW_WRAPPING_DEC; break; case OVERFLOW_WRAPPING_NEG: ret = IntrinsicKind_OVERFLOW_WRAPPING_NEG; break; case VECTOR_COMPARE_32: ret = IntrinsicKind_VECTOR_COMPARE_32; break; case VECTOR_INDEX_BYTE_32: ret = IntrinsicKind_VECTOR_INDEX_BYTE_32; break; case CJ_CORE_CAN_USE_SIMD: ret = IntrinsicKind_CJ_CORE_CAN_USE_SIMD; break; case CJ_TLS_DYN_SET_SESSION_CALLBACK: ret = IntrinsicKind_CJ_TLS_DYN_SET_SESSION_CALLBACK; break; case CJ_TLS_DYN_SSL_INIT: ret = IntrinsicKind_CJ_TLS_DYN_SSL_INIT; break; case REFLECTION_INTRINSIC_START_FLAG: ret = IntrinsicKind_REFLECTION_INTRINSIC_START_FLAG; break; case IS_INTERFACE: ret = IntrinsicKind_IS_INTERFACE; break; case IS_CLASS: ret = IntrinsicKind_IS_CLASS; break; case IS_PRIMITIVE: ret = IntrinsicKind_IS_PRIMITIVE; break; case IS_STRUCT: ret = IntrinsicKind_IS_STRUCT; break; case IS_GENERIC: ret = IntrinsicKind_IS_GENERIC; break; case GET_OR_CREATE_TYPEINFO_FOR_REFLECT: ret = IntrinsicKind_GET_OR_CREATE_TYPEINFO_FOR_REFLECT; break; case GET_TYPETEMPLATE: ret = IntrinsicKind_GET_TYPETEMPLATE; break; case CHECK_METHOD_ACTUAL_ARGS: ret = IntrinsicKind_CHECK_METHOD_ACTUAL_ARGS; break; case METHOD_ENTRYPOINT_IS_NULL: ret = IntrinsicKind_METHOD_ENTRYPOINT_IS_NULL; break; case IS_RELECT_UNSUPPORTED_TYPE: ret = IntrinsicKind_IS_RELECT_UNSUPPORTED_TYPE; break; case GET_TYPE_FOR_ANY: ret = IntrinsicKind_GET_TYPE_FOR_ANY; break; case GET_TYPE_BY_MANGLED_NAME: ret = IntrinsicKind_GET_TYPE_BY_MANGLED_NAME; break; case GET_TYPE_NAME: ret = IntrinsicKind_GET_TYPE_NAME; break; case GET_TYPE_BY_QUALIFIED_NAME: ret = IntrinsicKind_GET_TYPE_BY_QUALIFIED_NAME; break; case GET_TYPE_QUALIFIED_NAME_LENGTH: ret = IntrinsicKind_GET_TYPE_QUALIFIED_NAME_LENGTH; break; case GET_TYPE_QUALIFIED_NAME: ret = IntrinsicKind_GET_TYPE_QUALIFIED_NAME; break; case GET_NUM_OF_INTERFACE: ret = IntrinsicKind_GET_NUM_OF_INTERFACE; break; case GET_INTERFACE: ret = IntrinsicKind_GET_INTERFACE; break; case IS_SUBTYPE: ret = IntrinsicKind_IS_SUBTYPE; break; case GET_TYPE_INFO_MODIFIER: ret = IntrinsicKind_GET_TYPE_INFO_MODIFIER; break; case GET_TYPE_INFO_ANNOTATIONS: ret = IntrinsicKind_GET_TYPE_INFO_ANNOTATIONS; break; case GET_OBJ_CLASS: ret = IntrinsicKind_GET_OBJ_CLASS; break; case GET_SUPER_TYPE_INFO: ret = IntrinsicKind_GET_SUPER_TYPE_INFO; break; case GET_NUM_OF_INSTANCE_METHOD_INFOS: ret = IntrinsicKind_GET_NUM_OF_INSTANCE_METHOD_INFOS; break; case GET_INSTANCE_METHOD_INFO: ret = IntrinsicKind_GET_INSTANCE_METHOD_INFO; break; case GET_NUM_OF_STATIC_METHOD_INFOS: ret = IntrinsicKind_GET_NUM_OF_STATIC_METHOD_INFOS; break; case GET_STATIC_METHOD_INFO: ret = IntrinsicKind_GET_STATIC_METHOD_INFO; break; case GET_METHOD_NAME: ret = IntrinsicKind_GET_METHOD_NAME; break; case GET_METHOD_RETURN_TYPE: ret = IntrinsicKind_GET_METHOD_RETURN_TYPE; break; case GET_METHOD_MODIFIER: ret = IntrinsicKind_GET_METHOD_MODIFIER; break; case GET_METHOD_ANNOTATIONS: ret = IntrinsicKind_GET_METHOD_ANNOTATIONS; break; case APPLY_CJ_METHOD: ret = IntrinsicKind_APPLY_CJ_METHOD; break; case APPLY_CJ_STATIC_METHOD: ret = IntrinsicKind_APPLY_CJ_STATIC_METHOD; break; case APPLY_CJ_GENERIC_METHOD: ret = IntrinsicKind_APPLY_CJ_GENERIC_METHOD; break; case APPLY_CJ_GENERIC_STATIC_METHOD: ret = IntrinsicKind_APPLY_CJ_GENERIC_STATIC_METHOD; break; case GET_NUM_OF_ACTUAL_PARAMETERS: ret = IntrinsicKind_GET_NUM_OF_ACTUAL_PARAMETERS; break; case GET_NUM_OF_GENERIC_PARAMETERS: ret = IntrinsicKind_GET_NUM_OF_GENERIC_PARAMETERS; break; case GET_ACTUAL_PARAMETER_INFO: ret = IntrinsicKind_GET_ACTUAL_PARAMETER_INFO; break; case GET_GENERIC_PARAMETER_INFO: ret = IntrinsicKind_GET_GENERIC_PARAMETER_INFO; break; case GET_NUM_OF_INSTANCE_FIELD_INFOS: ret = IntrinsicKind_GET_NUM_OF_INSTANCE_FIELD_INFOS; break; case GET_INSTANCE_FIELD_INFO: ret = IntrinsicKind_GET_INSTANCE_FIELD_INFO; break; case GET_NUM_OF_STATIC_FIELD_INFOS: ret = IntrinsicKind_GET_NUM_OF_STATIC_FIELD_INFOS; break; case GET_STATIC_FIELD_INFO: ret = IntrinsicKind_GET_STATIC_FIELD_INFO; break; case GET_STATIC_FIELD_NAME: ret = IntrinsicKind_GET_STATIC_FIELD_NAME; break; case GET_STATIC_FIELD_TYPE: ret = IntrinsicKind_GET_STATIC_FIELD_TYPE; break; case GET_STATIC_FIELD_ANNOTATIONS: ret = IntrinsicKind_GET_STATIC_FIELD_ANNOTATIONS; break; case GET_FIELD_NAME: ret = IntrinsicKind_GET_FIELD_NAME; break; case GET_FIELD_TYPE: ret = IntrinsicKind_GET_FIELD_TYPE; break; case GET_FIELD_ANNOTATIONS: ret = IntrinsicKind_GET_FIELD_ANNOTATIONS; break; case GET_FIELD_MODIFIER: ret = IntrinsicKind_GET_FIELD_MODIFIER; break; case GET_STATIC_FIELD_MODIFIER: ret = IntrinsicKind_GET_STATIC_FIELD_MODIFIER; break; case GET_FIELD_VALUE: ret = IntrinsicKind_GET_FIELD_VALUE; break; case SET_FIELD_VALUE: ret = IntrinsicKind_SET_FIELD_VALUE; break; case GET_STATIC_FIELD_VALUE: ret = IntrinsicKind_GET_STATIC_FIELD_VALUE; break; case SET_STATIC_FIELD_VALUE: ret = IntrinsicKind_SET_STATIC_FIELD_VALUE; break; case GET_FIELD_DECLARING_TYPE: ret = IntrinsicKind_GET_FIELD_DECLARING_TYPE; break; case GET_PARAMETER_INDEX: ret = IntrinsicKind_GET_PARAMETER_INDEX; break; case GET_PARAMETER_NAME: ret = IntrinsicKind_GET_PARAMETER_NAME; break; case GET_PARAMETER_TYPE: ret = IntrinsicKind_GET_PARAMETER_TYPE; break; case GET_PARAMETER_ANNOTATIONS: ret = IntrinsicKind_GET_PARAMETER_ANNOTATIONS; break; case GET_RELATED_PACKAGE_INF: ret = IntrinsicKind_GET_RELATED_PACKAGE_INF; break; case GET_PACKAGE_NAME: ret = IntrinsicKind_GET_PACKAGE_NAME; break; case GET_PACKAGE_NUM_OF_TYPE_INFOS: ret = IntrinsicKind_GET_PACKAGE_NUM_OF_TYPE_INFOS; break; case GET_PACKAGE_TYPE_INFO: ret = IntrinsicKind_GET_PACKAGE_TYPE_INFO; break; case GET_PACKAGE_NUM_OF_GLOBAL_METHODS: ret = IntrinsicKind_GET_PACKAGE_NUM_OF_GLOBAL_METHODS; break; case GET_PACKAGE_GLOBAL_METHOD_INFO: ret = IntrinsicKind_GET_PACKAGE_GLOBAL_METHOD_INFO; break; case GET_PACKAGE_NUM_OF_GLOBAL_FIELD_INFOS: ret = IntrinsicKind_GET_PACKAGE_NUM_OF_GLOBAL_FIELD_INFOS; break; case GET_PACKAGE_GLOBAL_FIELD_INFO: ret = IntrinsicKind_GET_PACKAGE_GLOBAL_FIELD_INFO; break; case LOAD_PACKAGE: ret = IntrinsicKind_LOAD_PACKAGE; break; case GET_PACKAGE_BY_QUALIFIEDNAME: ret = IntrinsicKind_GET_PACKAGE_BY_QUALIFIEDNAME; break; case GET_PACKAGE_VERSION: ret = IntrinsicKind_GET_PACKAGE_VERSION; break; case GET_SUB_PACKAGES: ret = IntrinsicKind_GET_SUB_PACKAGES; break; case REFLECTION_INTRINSIC_END_FLAG: ret = IntrinsicKind_REFLECTION_INTRINSIC_END_FLAG; break; case SLEEP: ret = IntrinsicKind_SLEEP; break; case SOURCE_FILE: ret = IntrinsicKind_SOURCE_FILE; break; case SOURCE_LINE: ret = IntrinsicKind_SOURCE_LINE; break; case IDENTITY_HASHCODE: ret = IntrinsicKind_IDENTITY_HASHCODE; break; case IDENTITY_HASHCODE_FOR_ARRAY: ret = IntrinsicKind_IDENTITY_HASHCODE_FOR_ARRAY; break; case ATOMIC_LOAD: ret = IntrinsicKind_ATOMIC_LOAD; break; case ATOMIC_STORE: ret = IntrinsicKind_ATOMIC_STORE; break; case ATOMIC_SWAP: ret = IntrinsicKind_ATOMIC_SWAP; break; case ATOMIC_COMPARE_AND_SWAP: ret = IntrinsicKind_ATOMIC_COMPARE_AND_SWAP; break; case ATOMIC_FETCH_ADD: ret = IntrinsicKind_ATOMIC_FETCH_ADD; break; case ATOMIC_FETCH_SUB: ret = IntrinsicKind_ATOMIC_FETCH_SUB; break; case ATOMIC_FETCH_AND: ret = IntrinsicKind_ATOMIC_FETCH_AND; break; case ATOMIC_FETCH_OR: ret = IntrinsicKind_ATOMIC_FETCH_OR; break; case ATOMIC_FETCH_XOR: ret = IntrinsicKind_ATOMIC_FETCH_XOR; break; case MUTEX_INIT: ret = IntrinsicKind_MUTEX_INIT; break; case CJ_MUTEX_LOCK: ret = IntrinsicKind_CJ_MUTEX_LOCK; break; case MUTEX_TRY_LOCK: ret = IntrinsicKind_MUTEX_TRY_LOCK; break; case MUTEX_CHECK_STATUS: ret = IntrinsicKind_MUTEX_CHECK_STATUS; break; case MUTEX_UNLOCK: ret = IntrinsicKind_MUTEX_UNLOCK; break; case WAITQUEUE_INIT: ret = IntrinsicKind_WAITQUEUE_INIT; break; case MONITOR_INIT: ret = IntrinsicKind_MONITOR_INIT; break; case MOITIOR_WAIT: ret = IntrinsicKind_MOITIOR_WAIT; break; case MOITIOR_NOTIFY: ret = IntrinsicKind_MOITIOR_NOTIFY; break; case MOITIOR_NOTIFY_ALL: ret = IntrinsicKind_MOITIOR_NOTIFY_ALL; break; case MULTICONDITION_WAIT: ret = IntrinsicKind_MULTICONDITION_WAIT; break; case MULTICONDITION_NOTIFY: ret = IntrinsicKind_MULTICONDITION_NOTIFY; break; case MULTICONDITION_NOTIFY_ALL: ret = IntrinsicKind_MULTICONDITION_NOTIFY_ALL; break; case CROSS_ACCESS_BARRIER: ret = IntrinsicKind_CROSS_ACCESS_BARRIER; break; case CREATE_EXPORT_HANDLE: ret = IntrinsicKind_CREATE_EXPORT_HANDLE; break; case GET_EXPORTED_REF: ret = IntrinsicKind_GET_EXPORTED_REF; break; case REMOVE_EXPORTED_REF: ret = IntrinsicKind_REMOVE_EXPORTED_REF; break; case FFI_CJ_AST_LEX: ret = IntrinsicKind_FFI_CJ_AST_LEX; break; case FFI_CJ_AST_PARSEEXPR: ret = IntrinsicKind_FFI_CJ_AST_PARSEEXPR; break; case FFI_CJ_AST_PARSEDECL: ret = IntrinsicKind_FFI_CJ_AST_PARSEDECL; break; case FFI_CJ_AST_PARSE_PROPMEMBERDECL: ret = IntrinsicKind_FFI_CJ_AST_PARSE_PROPMEMBERDECL; break; case FFI_CJ_AST_PARSE_PRICONSTRUCTOR: ret = IntrinsicKind_FFI_CJ_AST_PARSE_PRICONSTRUCTOR; break; case FFI_CJ_AST_PARSE_PATTERN: ret = IntrinsicKind_FFI_CJ_AST_PARSE_PATTERN; break; case FFI_CJ_AST_PARSE_TYPE: ret = IntrinsicKind_FFI_CJ_AST_PARSE_TYPE; break; case FFI_CJ_AST_PARSETOPLEVEL: ret = IntrinsicKind_FFI_CJ_AST_PARSETOPLEVEL; break; case FFI_CJ_AST_DIAGREPORT: ret = IntrinsicKind_FFI_CJ_AST_DIAGREPORT; break; case FFI_CJ_PARENT_CONTEXT: ret = IntrinsicKind_FFI_CJ_PARENT_CONTEXT; break; case FFI_CJ_MACRO_ITEM_INFO: ret = IntrinsicKind_FFI_CJ_MACRO_ITEM_INFO; break; case FFI_CJ_GET_CHILD_MESSAGES: ret = IntrinsicKind_FFI_CJ_GET_CHILD_MESSAGES; break; case FFI_CJ_CHECK_ADD_SPACE: ret = IntrinsicKind_FFI_CJ_CHECK_ADD_SPACE; break; case CG_UNSAFE_BEGIN: ret = IntrinsicKind_CG_UNSAFE_BEGIN; break; case CG_UNSAFE_END: ret = IntrinsicKind_CG_UNSAFE_END; break; case STRLEN: ret = IntrinsicKind_STRLEN; break; case MEMCPY_S: ret = IntrinsicKind_MEMCPY_S; break; case MEMSET_S: ret = IntrinsicKind_MEMSET_S; break; case FREE: ret = IntrinsicKind_FREE; break; case MALLOC: ret = IntrinsicKind_MALLOC; break; case STRCMP: ret = IntrinsicKind_STRCMP; break; case MEMCMP: ret = IntrinsicKind_MEMCMP; break; case STRNCMP: ret = IntrinsicKind_STRNCMP; break; case STRCASECMP: ret = IntrinsicKind_STRCASECMP; break; case ATOMIC_INT8_LOAD: ret = IntrinsicKind_ATOMIC_INT8_LOAD; break; case ATOMIC_INT8_STORE: ret = IntrinsicKind_ATOMIC_INT8_STORE; break; case ATOMIC_INT8_SWAP: ret = IntrinsicKind_ATOMIC_INT8_SWAP; break; case ATOMIC_INT8_CAS: ret = IntrinsicKind_ATOMIC_INT8_CAS; break; case ATOMIC_INT8_FETCH_ADD: ret = IntrinsicKind_ATOMIC_INT8_FETCH_ADD; break; case ATOMIC_INT8_FETCH_SUB: ret = IntrinsicKind_ATOMIC_INT8_FETCH_SUB; break; case ATOMIC_INT8_FETCH_AND: ret = IntrinsicKind_ATOMIC_INT8_FETCH_AND; break; case ATOMIC_INT8_FETCH_OR: ret = IntrinsicKind_ATOMIC_INT8_FETCH_OR; break; case ATOMIC_INT8_FETCH_XOR: ret = IntrinsicKind_ATOMIC_INT8_FETCH_XOR; break; case ATOMIC_INT16_LOAD: ret = IntrinsicKind_ATOMIC_INT16_LOAD; break; case ATOMIC_INT16_STORE: ret = IntrinsicKind_ATOMIC_INT16_STORE; break; case ATOMIC_INT16_SWAP: ret = IntrinsicKind_ATOMIC_INT16_SWAP; break; case ATOMIC_INT16_CAS: ret = IntrinsicKind_ATOMIC_INT16_CAS; break; case ATOMIC_INT16_FETCH_ADD: ret = IntrinsicKind_ATOMIC_INT16_FETCH_ADD; break; case ATOMIC_INT16_FETCH_SUB: ret = IntrinsicKind_ATOMIC_INT16_FETCH_SUB; break; case ATOMIC_INT16_FETCH_AND: ret = IntrinsicKind_ATOMIC_INT16_FETCH_AND; break; case ATOMIC_INT16_FETCH_OR: ret = IntrinsicKind_ATOMIC_INT16_FETCH_OR; break; case ATOMIC_INT16_FETCH_XOR: ret = IntrinsicKind_ATOMIC_INT16_FETCH_XOR; break; case ATOMIC_INT32_LOAD: ret = IntrinsicKind_ATOMIC_INT32_LOAD; break; case ATOMIC_INT32_STORE: ret = IntrinsicKind_ATOMIC_INT32_STORE; break; case ATOMIC_INT32_SWAP: ret = IntrinsicKind_ATOMIC_INT32_SWAP; break; case ATOMIC_INT32_CAS: ret = IntrinsicKind_ATOMIC_INT32_CAS; break; case ATOMIC_INT32_FETCH_ADD: ret = IntrinsicKind_ATOMIC_INT32_FETCH_ADD; break; case ATOMIC_INT32_FETCH_SUB: ret = IntrinsicKind_ATOMIC_INT32_FETCH_SUB; break; case ATOMIC_INT32_FETCH_AND: ret = IntrinsicKind_ATOMIC_INT32_FETCH_AND; break; case ATOMIC_INT32_FETCH_OR: ret = IntrinsicKind_ATOMIC_INT32_FETCH_OR; break; case ATOMIC_INT32_FETCH_XOR: ret = IntrinsicKind_ATOMIC_INT32_FETCH_XOR; break; case ATOMIC_INT64_LOAD: ret = IntrinsicKind_ATOMIC_INT64_LOAD; break; case ATOMIC_INT64_STORE: ret = IntrinsicKind_ATOMIC_INT64_STORE; break; case ATOMIC_INT64_SWAP: ret = IntrinsicKind_ATOMIC_INT64_SWAP; break; case ATOMIC_INT64_CAS: ret = IntrinsicKind_ATOMIC_INT64_CAS; break; case ATOMIC_INT64_FETCH_ADD: ret = IntrinsicKind_ATOMIC_INT64_FETCH_ADD; break; case ATOMIC_INT64_FETCH_SUB: ret = IntrinsicKind_ATOMIC_INT64_FETCH_SUB; break; case ATOMIC_INT64_FETCH_AND: ret = IntrinsicKind_ATOMIC_INT64_FETCH_AND; break; case ATOMIC_INT64_FETCH_OR: ret = IntrinsicKind_ATOMIC_INT64_FETCH_OR; break; case ATOMIC_INT64_FETCH_XOR: ret = IntrinsicKind_ATOMIC_INT64_FETCH_XOR; break; case ATOMIC_UINT8_LOAD: ret = IntrinsicKind_ATOMIC_UINT8_LOAD; break; case ATOMIC_UINT8_STORE: ret = IntrinsicKind_ATOMIC_UINT8_STORE; break; case ATOMIC_UINT8_SWAP: ret = IntrinsicKind_ATOMIC_UINT8_SWAP; break; case ATOMIC_UINT8_CAS: ret = IntrinsicKind_ATOMIC_UINT8_CAS; break; case ATOMIC_UINT8_FETCH_ADD: ret = IntrinsicKind_ATOMIC_UINT8_FETCH_ADD; break; case ATOMIC_UINT8_FETCH_SUB: ret = IntrinsicKind_ATOMIC_UINT8_FETCH_SUB; break; case ATOMIC_UINT8_FETCH_AND: ret = IntrinsicKind_ATOMIC_UINT8_FETCH_AND; break; case ATOMIC_UINT8_FETCH_OR: ret = IntrinsicKind_ATOMIC_UINT8_FETCH_OR; break; case ATOMIC_UINT8_FETCH_XOR: ret = IntrinsicKind_ATOMIC_UINT8_FETCH_XOR; break; case ATOMIC_UINT16_LOAD: ret = IntrinsicKind_ATOMIC_UINT16_LOAD; break; case ATOMIC_UINT16_STORE: ret = IntrinsicKind_ATOMIC_UINT16_STORE; break; case ATOMIC_UINT16_SWAP: ret = IntrinsicKind_ATOMIC_UINT16_SWAP; break; case ATOMIC_UINT16_CAS: ret = IntrinsicKind_ATOMIC_UINT16_CAS; break; case ATOMIC_UINT16_FETCH_ADD: ret = IntrinsicKind_ATOMIC_UINT16_FETCH_ADD; break; case ATOMIC_UINT16_FETCH_SUB: ret = IntrinsicKind_ATOMIC_UINT16_FETCH_SUB; break; case ATOMIC_UINT16_FETCH_AND: ret = IntrinsicKind_ATOMIC_UINT16_FETCH_AND; break; case ATOMIC_UINT16_FETCH_OR: ret = IntrinsicKind_ATOMIC_UINT16_FETCH_OR; break; case ATOMIC_UINT16_FETCH_XOR: ret = IntrinsicKind_ATOMIC_UINT16_FETCH_XOR; break; case ATOMIC_UINT32_LOAD: ret = IntrinsicKind_ATOMIC_UINT32_LOAD; break; case ATOMIC_UINT32_STORE: ret = IntrinsicKind_ATOMIC_UINT32_STORE; break; case ATOMIC_UINT32_SWAP: ret = IntrinsicKind_ATOMIC_UINT32_SWAP; break; case ATOMIC_UINT32_CAS: ret = IntrinsicKind_ATOMIC_UINT32_CAS; break; case ATOMIC_UINT32_FETCH_ADD: ret = IntrinsicKind_ATOMIC_UINT32_FETCH_ADD; break; case ATOMIC_UINT32_FETCH_SUB: ret = IntrinsicKind_ATOMIC_UINT32_FETCH_SUB; break; case ATOMIC_UINT32_FETCH_AND: ret = IntrinsicKind_ATOMIC_UINT32_FETCH_AND; break; case ATOMIC_UINT32_FETCH_OR: ret = IntrinsicKind_ATOMIC_UINT32_FETCH_OR; break; case ATOMIC_UINT32_FETCH_XOR: ret = IntrinsicKind_ATOMIC_UINT32_FETCH_XOR; break; case ATOMIC_UINT64_LOAD: ret = IntrinsicKind_ATOMIC_UINT64_LOAD; break; case ATOMIC_UINT64_STORE: ret = IntrinsicKind_ATOMIC_UINT64_STORE; break; case ATOMIC_UINT64_SWAP: ret = IntrinsicKind_ATOMIC_UINT64_SWAP; break; case ATOMIC_UINT64_CAS: ret = IntrinsicKind_ATOMIC_UINT64_CAS; break; case ATOMIC_UINT64_FETCH_ADD: ret = IntrinsicKind_ATOMIC_UINT64_FETCH_ADD; break; case ATOMIC_UINT64_FETCH_SUB: ret = IntrinsicKind_ATOMIC_UINT64_FETCH_SUB; break; case ATOMIC_UINT64_FETCH_AND: ret = IntrinsicKind_ATOMIC_UINT64_FETCH_AND; break; case ATOMIC_UINT64_FETCH_OR: ret = IntrinsicKind_ATOMIC_UINT64_FETCH_OR; break; case ATOMIC_UINT64_FETCH_XOR: ret = IntrinsicKind_ATOMIC_UINT64_FETCH_XOR; break; case ATOMIC_BOOL_LOAD: ret = IntrinsicKind_ATOMIC_BOOL_LOAD; break; case ATOMIC_BOOL_STORE: ret = IntrinsicKind_ATOMIC_BOOL_STORE; break; case ATOMIC_BOOL_SWAP: ret = IntrinsicKind_ATOMIC_BOOL_SWAP; break; case ATOMIC_BOOL_CAS: ret = IntrinsicKind_ATOMIC_BOOL_CAS; break; case ATOMIC_REFERENCEBASE_LOAD: ret = IntrinsicKind_ATOMIC_REFERENCEBASE_LOAD; break; case ATOMIC_REFERENCEBASE_STORE: ret = IntrinsicKind_ATOMIC_REFERENCEBASE_STORE; break; case ATOMIC_REFERENCEBASE_SWAP: ret = IntrinsicKind_ATOMIC_REFERENCEBASE_SWAP; break; case ATOMIC_REFERENCEBASE_CAS: ret = IntrinsicKind_ATOMIC_REFERENCEBASE_CAS; break; case ATOMIC_OPTIONREFERENCE_LOAD: ret = IntrinsicKind_ATOMIC_OPTIONREFERENCE_LOAD; break; case ATOMIC_OPTIONREFERENCE_STORE: ret = IntrinsicKind_ATOMIC_OPTIONREFERENCE_STORE; break; case ATOMIC_OPTIONREFERENCE_SWAP: ret = IntrinsicKind_ATOMIC_OPTIONREFERENCE_SWAP; break; case ATOMIC_OPTIONREFERENCE_CAS: ret = IntrinsicKind_ATOMIC_OPTIONREFERENCE_CAS; break; case BEGIN_CATCH: ret = IntrinsicKind_BEGIN_CATCH; break; case ABS: ret = IntrinsicKind_ABS; break; case FABS: ret = IntrinsicKind_FABS; break; case FLOOR: ret = IntrinsicKind_FLOOR; break; case CEIL: ret = IntrinsicKind_CEIL; break; case TRUNC: ret = IntrinsicKind_TRUNC; break; case SIN: ret = IntrinsicKind_SIN; break; case COS: ret = IntrinsicKind_COS; break; case EXP: ret = IntrinsicKind_EXP; break; case EXP2: ret = IntrinsicKind_EXP2; break; case LOG: ret = IntrinsicKind_LOG; break; case LOG2: ret = IntrinsicKind_LOG2; break; case LOG10: ret = IntrinsicKind_LOG10; break; case SQRT: ret = IntrinsicKind_SQRT; break; case ROUND: ret = IntrinsicKind_ROUND; break; case POW: ret = IntrinsicKind_POW; break; case POWI: ret = IntrinsicKind_POWI; break; case BIT_CAST: ret = IntrinsicKind_BIT_CAST; break; case PREINITIALIZE: ret = IntrinsicKind_PREINITIALIZE; break; case OBJECT_AS: ret = IntrinsicKind_OBJECT_AS; break; case IS_NULL: ret = IntrinsicKind_IS_NULL; break; case GET_TYPE_FOR_TYPE_PARAMETER: ret = IntrinsicKind_GET_TYPE_FOR_TYPE_PARAMETER; break; case IS_SUBTYPE_TYPES: ret = IntrinsicKind_IS_SUBTYPE_TYPES; break; // no defalut here, due to we need use compiler to check all value be handled. } return ret; } PackageFormat::PackageAccessLevel Serialize(const Package::AccessLevel& kind) { using namespace PackageFormat; auto ret = PackageAccessLevel_INVALID; switch (kind) { case Package::AccessLevel::INVALID: ret = PackageAccessLevel_INVALID; break; case Package::AccessLevel::INTERNAL: ret = PackageAccessLevel_INTERNAL; break; case Package::AccessLevel::PROTECTED: ret = PackageAccessLevel_PROTECTED; break; case Package::AccessLevel::PUBLIC: ret = PackageAccessLevel_PUBLIC; break; } return ret; } PackageFormat::Phase Serialize(const Cangjie::CHIR::ToCHIR::Phase& kind) { using namespace PackageFormat; auto ret = Phase_RAW; switch (kind) { case ToCHIR::Phase::RAW: ret = Phase_RAW; break; case ToCHIR::Phase::OPT: ret = Phase_OPT; break; case ToCHIR::Phase::PLUGIN: ret = Phase_PLUGIN; break; case ToCHIR::Phase::ANALYSIS_FOR_CJLINT: ret = Phase_ANALYSIS_FOR_CJLINT; break; } return ret; } TEST_F(CHIRSerialzierTest, TypeKindEnum) { using namespace PackageFormat; using Cangjie::CHIR::Type; auto enumBegin = Type::TypeKind::TYPE_INVALID; auto enumEnd = Type::TypeKind::TYPE_THIS; // make sure this is max one we defined exclude pseudo-value MAX_TYPE_KIND EXPECT_EQ(static_cast(enumBegin), 0); EXPECT_EQ(static_cast(enumEnd) + 1, static_cast(Cangjie::CHIR::Type::TypeKind::MAX_TYPE_KIND)); for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ( PackageFormat::CHIRTypeKind(static_cast(i)), Serialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRSerialzierTest, SourceExprEnum) { using namespace PackageFormat; using Cangjie::CHIR::SourceExpr; SourceExpr enumBegin = SourceExpr::IF_EXPR; SourceExpr enumEnd = SourceExpr::OTHER; // make sure this is max one we defined EXPECT_EQ(static_cast(enumBegin), 0); for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(PackageFormat::SourceExpr(static_cast(i)), Serialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRSerialzierTest, LinkageEnum) { using namespace PackageFormat; using Cangjie::Linkage; Linkage enumBegin = Linkage::WEAK_ODR; Linkage enumEnd = Linkage::EXTERNAL_WEAK; // make sure this is max one we defined EXPECT_EQ(static_cast(enumBegin), 0); for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(PackageFormat::Linkage(static_cast(i)), Serialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRSerialzierTest, SkipKindEnum) { using namespace PackageFormat; using Cangjie::CHIR::SkipKind; SkipKind enumBegin = SkipKind::NO_SKIP; SkipKind enumEnd = SkipKind::SKIP_VIC; // make sure this is max one we defined EXPECT_EQ(static_cast(enumBegin), 0); for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(PackageFormat::SkipKind(static_cast(i)), Serialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRSerialzierTest, OverflowStrategyEnum) { using namespace PackageFormat; using Cangjie::OverflowStrategy; OverflowStrategy enumBegin = OverflowStrategy::NA; OverflowStrategy enumEnd = OverflowStrategy::SATURATING; // make sure this is max one we defined EXPECT_EQ(static_cast(enumBegin), 0); for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(PackageFormat::OverflowStrategy(static_cast(i)), Serialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRSerialzierTest, ValueKindEnum) { using namespace PackageFormat; using Cangjie::CHIR::Value; Value::ValueKind enumBegin = Value::ValueKind::KIND_LITERAL; Value::ValueKind enumEnd = Value::ValueKind::KIND_BLOCK_GROUP; // make sure this is max one we defined EXPECT_EQ(static_cast(enumBegin), 0); for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ( PackageFormat::ValueKind(static_cast(i)), Serialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRSerialzierTest, ConstantValueKindEnum) { using namespace PackageFormat; using Cangjie::CHIR::ConstantValueKind; ConstantValueKind enumBegin = ConstantValueKind::KIND_BOOL; ConstantValueKind enumEnd = ConstantValueKind::KIND_FUNC; // make sure this is max one we defined EXPECT_EQ(static_cast(enumBegin), 0); for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(PackageFormat::ConstantValueKind(static_cast(i)), Serialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRSerialzierTest, FuncKindEnum) { using namespace PackageFormat; using Cangjie::CHIR::FuncKind; FuncKind enumBegin = FuncKind::DEFAULT; FuncKind enumEnd = FuncKind::INSTANCEVAR_INIT; // make sure this is max one we defined exclude pseudo-value FUNCKIND_END EXPECT_EQ(static_cast(enumBegin), 0); EXPECT_EQ(static_cast(enumEnd) + 1, static_cast(FuncKind::FUNCKIND_END)); for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(PackageFormat::FuncKind(static_cast(i)), Serialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRSerialzierTest, CustomDefKindEnum) { using namespace PackageFormat; using Cangjie::CHIR::CustomDefKind; CustomDefKind enumBegin = CustomDefKind::TYPE_STRUCT; CustomDefKind enumEnd = CustomDefKind::TYPE_EXTEND; // make sure this is max one we defined EXPECT_EQ(static_cast(enumBegin), 0); for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(PackageFormat::CustomDefKind(static_cast(i)), Serialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRSerialzierTest, ExprKindEnum) { using namespace PackageFormat; ExprKind enumBegin = ExprKind::INVALID; ExprKind enumEnd = ExprKind::GET_RTTI_STATIC; // make sure this is max one we defined exclude pseudo-value MAX_EXPR_KINDS EXPECT_EQ(static_cast(enumBegin), 0); EXPECT_EQ(static_cast(enumEnd) + 1, static_cast(ExprKind::MAX_EXPR_KINDS)); for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(PackageFormat::CHIRExprKind(static_cast(i)), Serialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRSerialzierTest, IntrinsicKindEnum) { using namespace PackageFormat; Cangjie::CHIR::IntrinsicKind enumBegin = NOT_INTRINSIC; Cangjie::CHIR::IntrinsicKind enumEnd = IS_SUBTYPE_TYPES; // make sure this is max one we defined EXPECT_EQ(static_cast(enumBegin), 0); for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(PackageFormat::IntrinsicKind(static_cast(i)), Serialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRSerialzierTest, PackageAccessLevelEnum) { using namespace PackageFormat; Package::AccessLevel enumBegin = Package::AccessLevel::INVALID; Package::AccessLevel enumEnd = Package::AccessLevel::PUBLIC; // make sure this is max one we defined EXPECT_EQ(static_cast(enumBegin), 0); for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(PackageFormat::PackageAccessLevel(static_cast(i)), Serialize(static_cast(i))) << "cur i: " << i; } } TEST_F(CHIRSerialzierTest, Phase) { using namespace PackageFormat; ToCHIR::Phase enumBegin = ToCHIR::Phase::PHASE_MIN; ToCHIR::Phase enumEnd = ToCHIR::Phase::PHASE_MAX; // make sure this is max one we defined EXPECT_EQ(static_cast(enumBegin), 0); for (size_t i = static_cast(enumBegin); i <= static_cast(enumEnd); i++) { EXPECT_EQ(PackageFormat::Phase(static_cast(i)), Serialize(static_cast(i))) << "cur i: " << i; } } cangjie_compiler-1.0.7/unittests/CHIR/CMakeLists.txt000066400000000000000000000010531510705540100223350ustar00rootroot00000000000000if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_executable(CHIRSerialzierTest CHIRSerialzierTest.cpp CHIRDeSerialzierTest.cpp) target_link_libraries( CHIRSerialzierTest cangjie-lsp ${LINK_LIBS} boundscheck-static GTest::gtest GTest::gtest_main TestCompilerInstanceObject) add_dependencies(CHIRSerialzierTest CangjieFlatbuffersHeaders) target_include_directories(CHIRSerialzierTest PRIVATE ${FLATBUFFERS_INCLUDE_DIR}) add_test(NAME CHIRSerialzierTest COMMAND CHIRSerialzierTest) endif() cangjie_compiler-1.0.7/unittests/CMakeLists.txt000066400000000000000000000021241510705540100216100ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. enable_testing() include(CTest) include_directories(${GTEST_INCLUDE_DIRS}) include_directories(TestCompilerInstance) add_definitions(-DPROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}") # use llvm include_directories(${LLVM_INCLUDE_DIRS}) get_target_property(LINK_LIBS cjc LINK_LIBRARIES) list(APPEND LINK_LIBS ffi) if(CANGJIE_BUILD_TESTS) add_compile_definitions(CANGJIE_BUILD_TESTS) add_subdirectory(TestCompilerInstance) add_subdirectory(Driver) add_subdirectory(Frontend) add_subdirectory(Lex) add_subdirectory(Parse) add_subdirectory(AST) add_subdirectory(Utils) add_subdirectory(Sema) add_subdirectory(Option) add_subdirectory(Modules) add_subdirectory(Basic) add_subdirectory(Macro) add_subdirectory(ConditionalCompilation) add_subdirectory(IncrCompile) add_subdirectory(CHIR) endif() cangjie_compiler-1.0.7/unittests/CodeGen/000077500000000000000000000000001510705540100203555ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/CodeGen/CGTest.h000066400000000000000000000042431510705540100216620ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the BasicTest in CG. */ #ifndef CANGJIE_CG_TEST_H #define CANGJIE_CG_TEST_H #include #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/CHIRContext.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/Type.h" using namespace Cangjie::CHIR; class CGTestTemplate : public ::testing::Test { protected: CGTestTemplate() : Test(){}; void SetUp() override{}; void TearDown() override{}; }; // To construct CHIR types class MangleTypeTest : public CGTestTemplate { protected: MangleTypeTest() : cctx(&fileNameMap), builder(cctx) { int8Ty = builder.GetInt8Ty(); int16Ty = builder.GetInt16Ty(); int32Ty = builder.GetInt32Ty(); int64Ty = builder.GetInt64Ty(); intNativeTy = builder.GetIntNativeTy(); uint8Ty = builder.GetUInt8Ty(); uint16Ty = builder.GetUInt16Ty(); uint32Ty = builder.GetUInt32Ty(); uint64Ty = builder.GetUInt64Ty(); uintNativeTy = builder.GetUIntNativeTy(); float16Ty = builder.GetFloat16Ty(); float32Ty = builder.GetFloat32Ty(); float64Ty = builder.GetFloat64Ty(); runeTy = builder.GetType(); boolTy = builder.GetType(); unitTy = builder.GetType(); nothingTy = builder.GetType(); cstringTy = builder.GetType(); } std::unordered_map fileNameMap; CHIRContext cctx; CHIRBuilder builder; Type *int8Ty, *int16Ty, *int32Ty, *int64Ty, *intNativeTy, *uint8Ty, *uint16Ty, *uint32Ty, *uint64Ty, *uintNativeTy; Type *float16Ty, *float32Ty, *float64Ty; Type* runeTy; Type* boolTy; Type* unitTy; Type* nothingTy; Type* cstringTy; const std::string testFile{"test.cj"}; DebugLocation defaultLoc{testFile, 1, {1, 1}, {1, 1}, {0}}; }; #endif // CANGJIE_CG_TEST_Hcangjie_compiler-1.0.7/unittests/CodeGen/CMakeLists.txt000066400000000000000000000015411510705540100231160ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set(CANGJIE_CG_DEP_OBJECTS $ $ ) file(GLOB CG_TEST_SRC *.cpp) add_executable(CGTests ${CG_TEST_SRC} ${CANGJIE_CG_DEP_OBJECTS} ${CMAKE_SOURCE_DIR}/src/Basic/Print.cpp) target_link_libraries( CGTests ${LLVM_LIBS} GTest::gtest GTest::gtest_main) add_test(NAME CGTests COMMAND CGTests) if(CANGJIE_DOWNLOAD_DEPS) add_dependencies(CGTests binary-deps) elseif(CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_dependencies(CGTests cjnative) endif() target_include_directories(CGTests PRIVATE ${LLVM_INCLUDE_DIRS}) cangjie_compiler-1.0.7/unittests/CodeGen/MangleTypeTest.cpp000066400000000000000000000055611510705540100237750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "CGTest.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CodeGen/CGUtils.h" using TypeKind = Cangjie::CHIR::Type::TypeKind; using namespace Cangjie::CodeGen; TEST_F(MangleTypeTest, BuiltinTypes) { EXPECT_EQ(MangleType(*int8Ty), "a"); EXPECT_EQ(MangleType(*int16Ty), "s"); EXPECT_EQ(MangleType(*int32Ty), "i"); EXPECT_EQ(MangleType(*int64Ty), "l"); EXPECT_EQ(MangleType(*intNativeTy), "q"); EXPECT_EQ(MangleType(*uint8Ty), "h"); EXPECT_EQ(MangleType(*uint16Ty), "t"); EXPECT_EQ(MangleType(*uint32Ty), "j"); EXPECT_EQ(MangleType(*uint64Ty), "m"); EXPECT_EQ(MangleType(*uintNativeTy), "r"); EXPECT_EQ(MangleType(*float16Ty), "Dh"); EXPECT_EQ(MangleType(*float32Ty), "f"); EXPECT_EQ(MangleType(*float64Ty), "d"); EXPECT_EQ(MangleType(*runeTy), "c"); EXPECT_EQ(MangleType(*boolTy), "b"); EXPECT_EQ(MangleType(*unitTy), "u"); EXPECT_EQ(MangleType(*nothingTy), "n"); EXPECT_EQ(MangleType(*cstringTy), "k"); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND TEST_F(MangleTypeTest, CustomTypes) { // construct a ClassType a.Alpha auto classDef = builder.CreateClass(defaultLoc, "Alpha", "_CN1a5AlphaE", "a", true, true); auto classTy = builder.GetType(classDef); EXPECT_EQ(MangleType(*classTy), "_CCN1a5AlphaE"); // construct the StructType: a.SomeStruct auto structDef = builder.CreateStruct(defaultLoc, "Some", "_CN1a10SomeStructE", "a", true); auto structTy = builder.GetType(structDef); EXPECT_EQ(MangleType(*structTy), "Rrecord._CN1a10SomeStructE"); } #endif TEST_F(MangleTypeTest, FuncTypes) { // construct a funcTy (Int64, Int64) -> Int64 auto funcTy = builder.GetType(std::vector{int64Ty, int64Ty}, int64Ty); EXPECT_EQ(MangleType(*funcTy), "lll"); } TEST_F(MangleTypeTest, TupleTypes) { // construct a tupleType (Int8, Int16, Int32) auto tupleTy = builder.GetType(std::vector{int8Ty, int16Ty, int32Ty}); EXPECT_EQ(MangleType(*tupleTy), "T3_asiE"); // construct a tupleType ((Int8, Int16, Int32), Int64) auto nestedTupleTy = builder.GetType(std::vector{tupleTy, int64Ty}); EXPECT_EQ(MangleType(*nestedTupleTy), "T2_T3_asiElE"); } TEST_F(MangleTypeTest, RawArrayTypes) { // construct a RawArrayType Int8[3] auto rawArrayTy = builder.GetType(int8Ty, 3); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND EXPECT_EQ(MangleType(*rawArrayTy), "A3_aE"); #endif } TEST_F(MangleTypeTest, RefTypes) { // construct a RefType of Int8 auto refTy = builder.GetType(int8Ty); EXPECT_EQ(MangleType(*refTy), "a"); }cangjie_compiler-1.0.7/unittests/ConditionalCompilation/000077500000000000000000000000001510705540100235135ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/ConditionalCompilation/CMakeLists.txt000066400000000000000000000010751510705540100262560ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. add_executable(ConditionalCompilationTest ConditionalCompilationTest.cpp ${CANGJIE_SRC_OBJECTS}) target_link_libraries( ConditionalCompilationTest ${LINK_LIBS} GTest::gtest GTest::gtest_main TestCompilerInstanceObject) add_test(NAME ConditionalCompilationTest COMMAND ConditionalCompilationTest) cangjie_compiler-1.0.7/unittests/ConditionalCompilation/ConditionalCompilationTest.cpp000066400000000000000000000113621510705540100315240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include "gtest/gtest.h" #include "TestCompilerInstance.h" #include "cangjie/ConditionalCompilation/ConditionalCompilation.h" using namespace Cangjie; class ConditionalCompilationTest : public ::testing::Test { protected: void SetUp() override { #ifdef _WIN32 srcPath = projectPath + "\\unittests\\ConditionalCompilation\\srcFiles\\"; #else srcPath = projectPath + "/unittests/ConditionalCompilation/srcFiles/"; #endif } #ifdef PROJECT_SOURCE_DIR // Gets the absolute path of the project from the compile parameter. std::string projectPath = PROJECT_SOURCE_DIR; #else // Just in case, give it a default value. // Assume the initial is in the build directory. std::string projectPath = ".."; #endif std::string srcPath; DiagnosticEngine diag; CompilerInvocation invocation; std::unique_ptr instance; }; TEST_F(ConditionalCompilationTest, for_lsp) { auto src = srcPath + "os.cj"; instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; invocation.globalOptions.enableMacroInLSP = true; instance->srcFilePaths = {src}; instance->Compile(CompileStage::PARSE); instance->PerformConditionCompile(); } TEST_F(ConditionalCompilationTest, passedCondition_for_lsp) { auto src = srcPath + "os.cj"; instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; invocation.globalOptions.enableMacroInLSP = true; invocation.globalOptions.passedWhenKeyValue.insert({"test1", "abc"}); invocation.globalOptions.passedWhenKeyValue.insert({"test2", "aaa"}); instance->srcFilePaths = {src}; instance->Compile(CompileStage::PARSE); instance->PerformConditionCompile(); } TEST_F(ConditionalCompilationTest, passedCondition_cfgFile_for_lsp) { auto src = srcPath + "os.cj"; instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; invocation.globalOptions.enableMacroInLSP = true; invocation.globalOptions.passedWhenCfgPaths.emplace_back(srcPath); instance->srcFilePaths = {src}; instance->Compile(CompileStage::PARSE); instance->PerformConditionCompile(); } TEST_F(ConditionalCompilationTest, packagePaths_for_lsp) { auto src = srcPath + "os.cj"; instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; invocation.globalOptions.enableMacroInLSP = true; invocation.globalOptions.packagePaths.emplace_back(srcPath); instance->srcFilePaths = {src}; instance->Compile(CompileStage::PARSE); instance->PerformConditionCompile(); } #ifndef _WIN32 TEST_F(ConditionalCompilationTest, cfgPaths_no_file_for_lsp) { auto src = srcPath + "os.cj"; instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; invocation.globalOptions.enableMacroInLSP = true; invocation.globalOptions.passedWhenCfgPaths.emplace_back("srcPath"); instance->srcFilePaths = {src}; instance->Compile(CompileStage::PARSE); instance->PerformConditionCompile(); EXPECT_EQ(diag.GetWarningCount(), 1); } TEST_F(ConditionalCompilationTest, same_with_builtin_for_lsp) { auto src = srcPath + "os.cj"; instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; invocation.globalOptions.enableMacroInLSP = true; invocation.globalOptions.passedWhenCfgPaths.emplace_back("srcPath"); invocation.globalOptions.passedWhenKeyValue.insert({"os", "aaa"}); instance->srcFilePaths = {src}; instance->Compile(CompileStage::PARSE); instance->PerformConditionCompile(); EXPECT_EQ(diag.GetErrorCount(), 1); } TEST_F(ConditionalCompilationTest, cfg_path_ignored_for_lsp) { auto src = srcPath + "os.cj"; instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; invocation.globalOptions.enableMacroInLSP = true; invocation.globalOptions.passedWhenCfgPaths.emplace_back("srcPath"); invocation.globalOptions.passedWhenKeyValue.insert({"test1", "abc"}); invocation.globalOptions.passedWhenKeyValue.insert({"test2", "aaa"}); instance->srcFilePaths = {src}; instance->Compile(CompileStage::PARSE); instance->PerformConditionCompile(); EXPECT_EQ(diag.GetWarningCount(), 1); } #endif cangjie_compiler-1.0.7/unittests/ConditionalCompilation/srcFiles/000077500000000000000000000000001510705540100252655ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/ConditionalCompilation/srcFiles/cfg.toml000066400000000000000000000004561510705540100267260ustar00rootroot00000000000000/* * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. * This source file is part of the Cangjie project, licensed under Apache-2.0 * with Runtime Library Exception. * * See https://cangjie-lang.cn/pages/LICENSE for license information. */ feature = "lion" platform = "dsp"cangjie_compiler-1.0.7/unittests/ConditionalCompilation/srcFiles/os.cj000066400000000000000000000006351510705540100262300ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. @When[os == "Linux"] func foo() { print("Linux") } @When[os == "Windows"] func foo() { print("Windows") } main() { foo() }cangjie_compiler-1.0.7/unittests/Driver/000077500000000000000000000000001510705540100203045ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Driver/CMakeLists.txt000066400000000000000000000007661510705540100230550ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. add_executable(DriverTest DriverTest.cpp ToolchainTest.cpp TempFileManagerTest.cpp ${CANGJIE_SRC_OBJECTS}) target_link_libraries( DriverTest GTest::gtest GTest::gtest_main ${LINK_LIBS}) add_test(NAME DriverTest COMMAND DriverTest)cangjie_compiler-1.0.7/unittests/Driver/CorrectSDKSettings/000077500000000000000000000000001510705540100237705ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Driver/CorrectSDKSettings/SDKSettings.json000066400000000000000000000147251510705540100270360ustar00rootroot00000000000000{"DefaultVariant":"macos","DisplayName":"macOS 14.5","MinimalDisplayName":"14.5","DefaultProperties":{"CODE_SIGN_ENTITLEMENTS":"","ENTITLEMENTS_REQUIRED":"YES","KASAN_DEFAULT_CFLAGS":"$(KASAN_CFLAGS_CLASSIC)","KASAN_DEFAULT_CFLAGS[arch=arm64]":"$(KASAN_CFLAGS_TBI)","KASAN_DEFAULT_CFLAGS[arch=arm64e]":"$(KASAN_CFLAGS_TBI)","MACOSX_DEPLOYMENT_TARGET":"14.5","IOS_UNZIPPERED_TWIN_PREFIX_PATH":"\/System\/iOSSupport","TEST_LIBRARY_SEARCH_PATHS":"$(inherited) $(PLATFORM_DIR)\/Developer\/usr\/lib","TAPI_VERIFY_MODE":"Pedantic","PLATFORM_NAME":"macosx","KASAN_CFLAGS_CLASSIC":"-DKASAN=1 -DKASAN_CLASSIC=1 -fsanitize=address -mllvm -asan-globals-live-support -mllvm -asan-force-dynamic-shadow","CODE_SIGNING_REQUIRED":"YES","TEST_FRAMEWORK_SEARCH_PATHS":"$(inherited) $(PLATFORM_DIR)\/Developer\/Library\/Frameworks","CODE_SIGN_IDENTITY":"Apple Development","KASAN_CFLAGS_TBI":"-DKASAN=1 -DKASAN_TBI=1 -fsanitize=kernel-hwaddress -mllvm -hwasan-recover=0 -mllvm -hwasan-instrument-atomics=0 -mllvm -hwasan-instrument-stack=1 -mllvm -hwasan-generate-tags-with-calls=1 -mllvm -hwasan-instrument-with-calls=1 -mllvm -hwasan-use-short-granules=0 -mllvm -hwasan-memory-access-callback-prefix=__asan_","TAPI_USE_SRCROOT":"YES","ENTITLEMENTS_DESTINATION":"Signature","DEFAULT_COMPILER":"com.apple.compilers.llvm.clang.1_0","DEPLOYMENT_TARGET_SUGGESTED_VALUES":["10.13","10.14","10.15","11.0","11.1","11.2","11.3","11.4","11.5","12.0","12.2","12.3","12.4","13.0","13.1","13.2","13.3","13.4","13.5","14.0","14.1","14.2","14.3","14.4","14.5"],"AD_HOC_CODE_SIGNING_ALLOWED":"YES"},"IsBaseSDK":"YES","SupportedTargets":{"iosmac":{"LLVMTargetTripleVendor":"apple","DeploymentTargetSettingName":"IPHONEOS_DEPLOYMENT_TARGET","Archs":["x86_64","x86_64h","arm64","arm64e"],"SwiftConcurrencyMinimumDeploymentTarget":"15.0","LLVMTargetTripleEnvironment":"macabi","ClangRuntimeLibraryPlatformName":"osx","MaximumDeploymentTarget":"17.5.99","BuildVersionPlatformID":"6","DefaultDeploymentTarget":"17.5","LLVMTargetTripleSys":"ios","DeviceFamilies":[{"Identifier":"2","Name":"pad","DisplayName":"iPad"},{"Identifier":"6","Name":"mac","DisplayName":"Mac"}],"MinimumDeploymentTarget":"13.1","SwiftOSRuntimeMinimumDeploymentTarget":"12.2","RecommendedDeploymentTarget":"13.1","SystemPrefix":"\/System\/iOSSupport","ValidDeploymentTargets":["13.1","13.2","13.3","13.3.1","13.4","13.5","14.0","14.1","14.2","14.3","14.4","14.5","14.6","14.7","15.0","15.2","15.3","15.4","15.5","15.6","16.0","16.1","16.2","16.3","16.4","16.5","16.6","17.0","17.1","17.2","17.3","17.4","17.5"]},"macosx":{"LLVMTargetTripleVendor":"apple","DeploymentTargetSettingName":"MACOSX_DEPLOYMENT_TARGET","PlatformFamilyDisplayName":"macOS","Archs":["x86_64","x86_64h","arm64","arm64e"],"LLVMTargetTripleEnvironment":"","ClangRuntimeLibraryPlatformName":"osx","MaximumDeploymentTarget":"14.5.99","BuildVersionPlatformID":"1","DefaultDeploymentTarget":"14.5","LLVMTargetTripleSys":"macos","DeviceFamilies":[{"Name":"mac","DisplayName":"Mac"}],"MinimumDeploymentTarget":"10.13","SwiftConcurrencyMinimumDeploymentTarget":"12.0","SwiftOSRuntimeMinimumDeploymentTarget":"10.14.4","RecommendedDeploymentTarget":"10.14.6","PlatformFamilyName":"macOS","SystemPrefix":"","ValidDeploymentTargets":["10.13","10.14","10.15","11.0","11.1","11.2","11.3","11.4","11.5","12.0","12.2","12.3","12.4","13.0","13.1","13.2","13.3","13.4","13.5","14.0","14.1","14.2","14.3","14.4","14.5"]}},"Version":"14.5","PropertyConditionFallbackNames":[],"VersionMap":{"iOSMac_macOS":{"13.2":"10.15.1","14.4":"11.2","15.6":"12.5","14.0":"11.0","15.2":"12.1","16.4":"13.3","16.0":"13.0","17.2":"14.2","13.3":"10.15.2","14.5":"11.3","14.1":"11.0","15.3":"12.2","16.5":"13.4","16.1":"13.0","17.3":"14.3","13.4":"10.15.4","14.6":"11.4","14.2":"11.0","15.4":"12.3","16.6":"13.5","15.0":"12.0","16.2":"13.1","17.4":"14.4","17.0":"14.0","13.5":"10.15.5","14.7":"11.5","13.1":"10.15","14.3":"11.1","15.5":"12.4","16.3":"13.2","17.5":"14.5","17.1":"14.1","13.3.1":"10.15.3"},"macOS_iOSMac":{"13.3":"16.4","11.4":"14.6","13.1":"16.2","14.3":"17.3","12.4":"15.5","11.2":"14.4","12.0.1":"15.0","10.15.1":"13.2","14.1":"17.1","11.0":"14.2","10.15":"13.1","12.2":"15.3","10.15.2":"13.3","11.5":"14.7","13.4":"16.5","12.0":"15.0","13.2":"16.3","10.15.3":"13.3.1","11.3":"14.5","12.5":"15.6","14.4":"17.4","13.0":"16.1","10.15.4":"13.4","14.2":"17.2","11.1":"14.3","12.3":"15.4","13.5":"16.6","10.15.5":"13.5","11.0.1":"14.2","14.0":"17.0","14.5":"17.5","12.1":"15.2"}},"DefaultDeploymentTarget":"14.5","MaximumDeploymentTarget":"14.5.99","Variants":[{"Name":"macos","BuildSettings":{"_BOOL_":"NO","_IS_EMPTY_":"YES","CODE_SIGN_IDENTITY":"$(CODE_SIGN_IDENTITY_$(_DEVELOPMENT_TEAM_IS_EMPTY))","LLVM_TARGET_TRIPLE_OS_VERSION_YES":"macos14.5","_DEVELOPMENT_TEAM_IS_EMPTY":"$(_BOOL_$(_IS_EMPTY_$(DEVELOPMENT_TEAM)))","LLVM_TARGET_TRIPLE_OS_VERSION":"$(LLVM_TARGET_TRIPLE_OS_VERSION_$(_MACOSX_DEPLOYMENT_TARGET_IS_EMPTY))","LLVM_TARGET_TRIPLE_OS_VERSION_NO":"macos$(MACOSX_DEPLOYMENT_TARGET)","_BOOL_YES":"YES","CODE_SIGN_IDENTITY_YES":"-","CODE_SIGN_IDENTITY_NO":"Apple Development","_MACOSX_DEPLOYMENT_TARGET_IS_EMPTY":"$(_BOOL_$(_IS_EMPTY_$(MACOSX_DEPLOYMENT_TARGET)))","_BOOL_NO":"NO","IPHONEOS_DEPLOYMENT_TARGET":"17.5","LLVM_TARGET_TRIPLE_SUFFIX":""}},{"Name":"iosmac","BuildSettings":{"_BOOL_NO":"NO","SYSTEM_HEADER_SEARCH_PATHS":"$(inherited) $(SDKROOT)$(IOS_UNZIPPERED_TWIN_PREFIX_PATH)\/usr\/include","SYSTEM_FRAMEWORK_SEARCH_PATHS":"$(inherited) $(SDKROOT)$(IOS_UNZIPPERED_TWIN_PREFIX_PATH)\/System\/Library\/Frameworks","SWIFT_DEPLOYMENT_TARGET":"$(IPHONEOS_DEPLOYMENT_TARGET)","_BOOL_":"NO","LLVM_TARGET_TRIPLE_OS_VERSION_YES":"ios17.5","_IS_EMPTY_":"YES","LIBRARY_SEARCH_PATHS":"$(inherited) $(SDKROOT)$(IOS_UNZIPPERED_TWIN_PREFIX_PATH)\/usr\/lib $(TOOLCHAIN_DIR)\/usr\/lib\/swift\/maccatalyst","TARGETED_DEVICE_FAMILY":"2","RESOURCES_UI_FRAMEWORK_FAMILY":"uikit","LLVM_TARGET_TRIPLE_SUFFIX":"-macabi","RESOURCES_MINIMUM_DEPLOYMENT_TARGET":"$(IPHONEOS_DEPLOYMENT_TARGET)","_BOOL_YES":"YES","_IPHONEOS_DEPLOYMENT_TARGET_IS_EMPTY":"$(_BOOL_$(_IS_EMPTY_$(IPHONEOS_DEPLOYMENT_TARGET)))","IPHONEOS_DEPLOYMENT_TARGET":"17.5","CODE_SIGN_IDENTITY":"Apple Development","LLVM_TARGET_TRIPLE_OS_VERSION":"$(LLVM_TARGET_TRIPLE_OS_VERSION_$(_IPHONEOS_DEPLOYMENT_TARGET_IS_EMPTY))","RESOURCES_PLATFORM_NAME":"macosx","ENABLE_HARDENED_RUNTIME":"YES","LLVM_TARGET_TRIPLE_OS_VERSION_NO":"ios$(IPHONEOS_DEPLOYMENT_TARGET)"}}],"DebuggerOptions":{"SupportsViewDebugging":"YES"},"CanonicalName":"macosx14.5","CustomProperties":{"KERNEL_EXTENSION_HEADER_SEARCH_PATHS":"$(KERNEL_FRAMEWORK)\/PrivateHeaders $(KERNEL_FRAMEWORK_HEADERS)"}}cangjie_compiler-1.0.7/unittests/Driver/DriverTest.cpp000066400000000000000000000044011510705540100231020ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "gtest/gtest.h" #include "cangjie/Driver/Driver.h" #include #include #include #include "cangjie/Driver/Utils.h" using namespace Cangjie; class DriverTest : public ::testing::Test { public: std::unique_ptr driver; protected: void SetUp() override { #ifdef PROJECT_SOURCE_DIR // Gets the absolute path of the project from the compile parameter. std::string projectPath = PROJECT_SOURCE_DIR; #else // Just in case, give it a default value. // Assume the initial is in the build directory. std::string projectPath = ".."; #endif #ifdef _WIN32 sdkPath = FileUtil::JoinPath(projectPath, "unittests\\Driver"); #else sdkPath = FileUtil::JoinPath(projectPath, "unittests/Driver"); #endif } std::string sdkPath; }; TEST_F(DriverTest, GetSingleQuotedTest) { const std::string singleQuote{"\\'"}; EXPECT_EQ(GetSingleQuoted("abcde"), "'abcde'"); EXPECT_EQ(GetSingleQuoted("'; ls"), "''" + singleQuote + "'; ls'"); EXPECT_EQ(GetSingleQuoted("'wrapped'"), std::string{"''" + singleQuote + "'wrapped'" + singleQuote + "''"}); EXPECT_EQ(GetSingleQuoted("start'wrapped'end"), std::string{"'start'" + singleQuote + "'wrapped'" + singleQuote + "'end'"}); EXPECT_EQ(GetSingleQuoted("end'"), std::string{"'end'" + singleQuote + "''"}); } TEST_F(DriverTest, GetDarwinSDKVersion) { std::optional sdkVersion = GetDarwinSDKVersion(sdkPath); EXPECT_FALSE(sdkVersion.has_value()); sdkVersion = GetDarwinSDKVersion(sdkPath + "/IncorrectSDKSettings1"); EXPECT_FALSE(sdkVersion.has_value()); sdkVersion = GetDarwinSDKVersion(sdkPath + "/IncorrectSDKSettings2"); EXPECT_FALSE(sdkVersion.has_value()); sdkVersion = GetDarwinSDKVersion(sdkPath + "/IncorrectSDKSettings3"); EXPECT_FALSE(sdkVersion.has_value()); sdkVersion = GetDarwinSDKVersion(sdkPath + "/CorrectSDKSettings"); EXPECT_TRUE(sdkVersion.has_value()); EXPECT_EQ(sdkVersion.value(), "14.5"); }cangjie_compiler-1.0.7/unittests/Driver/IncorrectSDKSettings1/000077500000000000000000000000001510705540100244005ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Driver/IncorrectSDKSettings1/SDKSettings.json000066400000000000000000000000631510705540100274340ustar00rootroot00000000000000"DefaultVariant":"macos";"DisplayName":"macOS 14.5"cangjie_compiler-1.0.7/unittests/Driver/IncorrectSDKSettings2/000077500000000000000000000000001510705540100244015ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Driver/IncorrectSDKSettings2/SDKSettings.json000066400000000000000000000147041510705540100274440ustar00rootroot00000000000000{"DefaultVariant":"macos","DisplayName":"macOS 14.5","MinimalDisplayName":"14.5","DefaultProperties":{"CODE_SIGN_ENTITLEMENTS":"","ENTITLEMENTS_REQUIRED":"YES","KASAN_DEFAULT_CFLAGS":"$(KASAN_CFLAGS_CLASSIC)","KASAN_DEFAULT_CFLAGS[arch=arm64]":"$(KASAN_CFLAGS_TBI)","KASAN_DEFAULT_CFLAGS[arch=arm64e]":"$(KASAN_CFLAGS_TBI)","MACOSX_DEPLOYMENT_TARGET":"14.5","IOS_UNZIPPERED_TWIN_PREFIX_PATH":"\/System\/iOSSupport","TEST_LIBRARY_SEARCH_PATHS":"$(inherited) $(PLATFORM_DIR)\/Developer\/usr\/lib","TAPI_VERIFY_MODE":"Pedantic","PLATFORM_NAME":"macosx","KASAN_CFLAGS_CLASSIC":"-DKASAN=1 -DKASAN_CLASSIC=1 -fsanitize=address -mllvm -asan-globals-live-support -mllvm -asan-force-dynamic-shadow","CODE_SIGNING_REQUIRED":"YES","TEST_FRAMEWORK_SEARCH_PATHS":"$(inherited) $(PLATFORM_DIR)\/Developer\/Library\/Frameworks","CODE_SIGN_IDENTITY":"Apple Development","KASAN_CFLAGS_TBI":"-DKASAN=1 -DKASAN_TBI=1 -fsanitize=kernel-hwaddress -mllvm -hwasan-recover=0 -mllvm -hwasan-instrument-atomics=0 -mllvm -hwasan-instrument-stack=1 -mllvm -hwasan-generate-tags-with-calls=1 -mllvm -hwasan-instrument-with-calls=1 -mllvm -hwasan-use-short-granules=0 -mllvm -hwasan-memory-access-callback-prefix=__asan_","TAPI_USE_SRCROOT":"YES","ENTITLEMENTS_DESTINATION":"Signature","DEFAULT_COMPILER":"com.apple.compilers.llvm.clang.1_0","DEPLOYMENT_TARGET_SUGGESTED_VALUES":["10.13","10.14","10.15","11.0","11.1","11.2","11.3","11.4","11.5","12.0","12.2","12.3","12.4","13.0","13.1","13.2","13.3","13.4","13.5","14.0","14.1","14.2","14.3","14.4","14.5"],"AD_HOC_CODE_SIGNING_ALLOWED":"YES"},"IsBaseSDK":"YES","SupportedTargets":{"iosmac":{"LLVMTargetTripleVendor":"apple","DeploymentTargetSettingName":"IPHONEOS_DEPLOYMENT_TARGET","Archs":["x86_64","x86_64h","arm64","arm64e"],"SwiftConcurrencyMinimumDeploymentTarget":"15.0","LLVMTargetTripleEnvironment":"macabi","ClangRuntimeLibraryPlatformName":"osx","MaximumDeploymentTarget":"17.5.99","BuildVersionPlatformID":"6","DefaultDeploymentTarget":"17.5","LLVMTargetTripleSys":"ios","DeviceFamilies":[{"Identifier":"2","Name":"pad","DisplayName":"iPad"},{"Identifier":"6","Name":"mac","DisplayName":"Mac"}],"MinimumDeploymentTarget":"13.1","SwiftOSRuntimeMinimumDeploymentTarget":"12.2","RecommendedDeploymentTarget":"13.1","SystemPrefix":"\/System\/iOSSupport","ValidDeploymentTargets":["13.1","13.2","13.3","13.3.1","13.4","13.5","14.0","14.1","14.2","14.3","14.4","14.5","14.6","14.7","15.0","15.2","15.3","15.4","15.5","15.6","16.0","16.1","16.2","16.3","16.4","16.5","16.6","17.0","17.1","17.2","17.3","17.4","17.5"]},"macosx":{"LLVMTargetTripleVendor":"apple","DeploymentTargetSettingName":"MACOSX_DEPLOYMENT_TARGET","PlatformFamilyDisplayName":"macOS","Archs":["x86_64","x86_64h","arm64","arm64e"],"LLVMTargetTripleEnvironment":"","ClangRuntimeLibraryPlatformName":"osx","MaximumDeploymentTarget":"14.5.99","BuildVersionPlatformID":"1","DefaultDeploymentTarget":"14.5","LLVMTargetTripleSys":"macos","DeviceFamilies":[{"Name":"mac","DisplayName":"Mac"}],"MinimumDeploymentTarget":"10.13","SwiftConcurrencyMinimumDeploymentTarget":"12.0","SwiftOSRuntimeMinimumDeploymentTarget":"10.14.4","RecommendedDeploymentTarget":"10.14.6","PlatformFamilyName":"macOS","SystemPrefix":"","ValidDeploymentTargets":["10.13","10.14","10.15","11.0","11.1","11.2","11.3","11.4","11.5","12.0","12.2","12.3","12.4","13.0","13.1","13.2","13.3","13.4","13.5","14.0","14.1","14.2","14.3","14.4","14.5"]}},"PropertyConditionFallbackNames":[],"VersionMap":{"iOSMac_macOS":{"13.2":"10.15.1","14.4":"11.2","15.6":"12.5","14.0":"11.0","15.2":"12.1","16.4":"13.3","16.0":"13.0","17.2":"14.2","13.3":"10.15.2","14.5":"11.3","14.1":"11.0","15.3":"12.2","16.5":"13.4","16.1":"13.0","17.3":"14.3","13.4":"10.15.4","14.6":"11.4","14.2":"11.0","15.4":"12.3","16.6":"13.5","15.0":"12.0","16.2":"13.1","17.4":"14.4","17.0":"14.0","13.5":"10.15.5","14.7":"11.5","13.1":"10.15","14.3":"11.1","15.5":"12.4","16.3":"13.2","17.5":"14.5","17.1":"14.1","13.3.1":"10.15.3"},"macOS_iOSMac":{"13.3":"16.4","11.4":"14.6","13.1":"16.2","14.3":"17.3","12.4":"15.5","11.2":"14.4","12.0.1":"15.0","10.15.1":"13.2","14.1":"17.1","11.0":"14.2","10.15":"13.1","12.2":"15.3","10.15.2":"13.3","11.5":"14.7","13.4":"16.5","12.0":"15.0","13.2":"16.3","10.15.3":"13.3.1","11.3":"14.5","12.5":"15.6","14.4":"17.4","13.0":"16.1","10.15.4":"13.4","14.2":"17.2","11.1":"14.3","12.3":"15.4","13.5":"16.6","10.15.5":"13.5","11.0.1":"14.2","14.0":"17.0","14.5":"17.5","12.1":"15.2"}},"DefaultDeploymentTarget":"14.5","MaximumDeploymentTarget":"14.5.99","Variants":[{"Name":"macos","BuildSettings":{"_BOOL_":"NO","_IS_EMPTY_":"YES","CODE_SIGN_IDENTITY":"$(CODE_SIGN_IDENTITY_$(_DEVELOPMENT_TEAM_IS_EMPTY))","LLVM_TARGET_TRIPLE_OS_VERSION_YES":"macos14.5","_DEVELOPMENT_TEAM_IS_EMPTY":"$(_BOOL_$(_IS_EMPTY_$(DEVELOPMENT_TEAM)))","LLVM_TARGET_TRIPLE_OS_VERSION":"$(LLVM_TARGET_TRIPLE_OS_VERSION_$(_MACOSX_DEPLOYMENT_TARGET_IS_EMPTY))","LLVM_TARGET_TRIPLE_OS_VERSION_NO":"macos$(MACOSX_DEPLOYMENT_TARGET)","_BOOL_YES":"YES","CODE_SIGN_IDENTITY_YES":"-","CODE_SIGN_IDENTITY_NO":"Apple Development","_MACOSX_DEPLOYMENT_TARGET_IS_EMPTY":"$(_BOOL_$(_IS_EMPTY_$(MACOSX_DEPLOYMENT_TARGET)))","_BOOL_NO":"NO","IPHONEOS_DEPLOYMENT_TARGET":"17.5","LLVM_TARGET_TRIPLE_SUFFIX":""}},{"Name":"iosmac","BuildSettings":{"_BOOL_NO":"NO","SYSTEM_HEADER_SEARCH_PATHS":"$(inherited) $(SDKROOT)$(IOS_UNZIPPERED_TWIN_PREFIX_PATH)\/usr\/include","SYSTEM_FRAMEWORK_SEARCH_PATHS":"$(inherited) $(SDKROOT)$(IOS_UNZIPPERED_TWIN_PREFIX_PATH)\/System\/Library\/Frameworks","SWIFT_DEPLOYMENT_TARGET":"$(IPHONEOS_DEPLOYMENT_TARGET)","_BOOL_":"NO","LLVM_TARGET_TRIPLE_OS_VERSION_YES":"ios17.5","_IS_EMPTY_":"YES","LIBRARY_SEARCH_PATHS":"$(inherited) $(SDKROOT)$(IOS_UNZIPPERED_TWIN_PREFIX_PATH)\/usr\/lib $(TOOLCHAIN_DIR)\/usr\/lib\/swift\/maccatalyst","TARGETED_DEVICE_FAMILY":"2","RESOURCES_UI_FRAMEWORK_FAMILY":"uikit","LLVM_TARGET_TRIPLE_SUFFIX":"-macabi","RESOURCES_MINIMUM_DEPLOYMENT_TARGET":"$(IPHONEOS_DEPLOYMENT_TARGET)","_BOOL_YES":"YES","_IPHONEOS_DEPLOYMENT_TARGET_IS_EMPTY":"$(_BOOL_$(_IS_EMPTY_$(IPHONEOS_DEPLOYMENT_TARGET)))","IPHONEOS_DEPLOYMENT_TARGET":"17.5","CODE_SIGN_IDENTITY":"Apple Development","LLVM_TARGET_TRIPLE_OS_VERSION":"$(LLVM_TARGET_TRIPLE_OS_VERSION_$(_IPHONEOS_DEPLOYMENT_TARGET_IS_EMPTY))","RESOURCES_PLATFORM_NAME":"macosx","ENABLE_HARDENED_RUNTIME":"YES","LLVM_TARGET_TRIPLE_OS_VERSION_NO":"ios$(IPHONEOS_DEPLOYMENT_TARGET)"}}],"DebuggerOptions":{"SupportsViewDebugging":"YES"},"CanonicalName":"macosx14.5","CustomProperties":{"KERNEL_EXTENSION_HEADER_SEARCH_PATHS":"$(KERNEL_FRAMEWORK)\/PrivateHeaders $(KERNEL_FRAMEWORK_HEADERS)"}}cangjie_compiler-1.0.7/unittests/Driver/IncorrectSDKSettings3/000077500000000000000000000000001510705540100244025ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Driver/IncorrectSDKSettings3/SDKSettings.json000066400000000000000000000000231510705540100274320ustar00rootroot00000000000000[42, "text", false]cangjie_compiler-1.0.7/unittests/Driver/TempFileManagerTest.cpp000066400000000000000000000037341510705540100246570ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "gtest/gtest.h" #include "cangjie/Driver/TempFileManager.h" #include "cangjie/Driver/TempFileInfo.h" #include "cangjie/Option/Option.h" #include "cangjie/Utils/FileUtil.h" using namespace Cangjie; class TempFileManagerTest : public ::testing::Test { protected: void SetUp() override { } }; TEST_F(TempFileManagerTest, WindowsOutputSuffixTest) { GlobalOptions options; options.target.os = Triple::OSType::WINDOWS; TempFileManager::Instance().Init(options, false); TempFileInfo info; info.fileName = "test"; auto newInfo = TempFileManager::Instance().CreateNewFileInfo(info, TempFileKind::O_EXE); ASSERT_EQ(FileUtil::GetFileName(newInfo.filePath), "main.exe"); newInfo = TempFileManager::Instance().CreateNewFileInfo(info, TempFileKind::O_DYLIB); ASSERT_EQ(FileUtil::GetFileName(newInfo.filePath), "libtest.dll"); newInfo = TempFileManager::Instance().CreateNewFileInfo(info, TempFileKind::O_STATICLIB); ASSERT_EQ(FileUtil::GetFileName(newInfo.filePath), "libtest.a"); } TEST_F(TempFileManagerTest, LinuxOutputSuffixTest) { GlobalOptions options; options.target.os = Triple::OSType::LINUX; TempFileManager::Instance().Init(options, false); TempFileInfo info; info.fileName = "test"; auto newInfo = TempFileManager::Instance().CreateNewFileInfo(info, TempFileKind::O_EXE); ASSERT_EQ(FileUtil::GetFileName(newInfo.filePath), "main"); newInfo = TempFileManager::Instance().CreateNewFileInfo(info, TempFileKind::O_DYLIB); ASSERT_EQ(FileUtil::GetFileName(newInfo.filePath), "libtest.so"); newInfo = TempFileManager::Instance().CreateNewFileInfo(info, TempFileKind::O_STATICLIB); ASSERT_EQ(FileUtil::GetFileName(newInfo.filePath), "libtest.a"); }cangjie_compiler-1.0.7/unittests/Driver/ToolchainTest.cpp000066400000000000000000000007511510705540100235730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "gtest/gtest.h" #include "cangjie/Driver/Backend/Backend.h" using namespace Cangjie; class ToolchainTest : public ::testing::Test { protected: void SetUp() override { } }; TEST_F(ToolchainTest, Init) { } cangjie_compiler-1.0.7/unittests/Frontend/000077500000000000000000000000001510705540100206305ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Frontend/CMakeLists.txt000066400000000000000000000007771510705540100234030ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. add_executable(CompilerInstanceTest CompilerInstanceTest.cpp ${CANGJIE_SRC_OBJECTS}) target_link_libraries( CompilerInstanceTest ${LINK_LIBS} GTest::gtest GTest::gtest_main) add_test(NAME CompilerInstanceTest COMMAND CompilerInstanceTest) cangjie_compiler-1.0.7/unittests/Frontend/CompilerInstanceTest.cpp000066400000000000000000000140771510705540100254440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "gtest/gtest.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/Basic/SourceManager.h" #include "cangjie/FrontendTool/DefaultCompilerInstance.h" #include "cangjie/Utils/FileUtil.h" #include #include using namespace Cangjie; using namespace AST; class CompilerInstanceTest : public ::testing::Test { public: CompilerInvocation invocation; DiagnosticEngine diag; std::string projectPath; std::string cangjieHome; protected: void SetUp() override { #ifdef PROJECT_SOURCE_DIR // Gets the absolute path of the project from the compile parameter. projectPath = PROJECT_SOURCE_DIR; cangjieHome = FileUtil::JoinPath(FileUtil::JoinPath(PROJECT_SOURCE_DIR, "build"), "build"); #else // Just in case, give it a default value. Assume the initial is in the build directory. projectPath = ".."; cangjieHome = FileUtil::JoinPath(FileUtil::JoinPath(".", "build"), "build"); #endif #ifdef __x86_64__ invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::X86_64; #else invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::AARCH64; #endif #ifdef _WIN32 invocation.globalOptions.target.os = Cangjie::Triple::OSType::WINDOWS; #elif __unix__ invocation.globalOptions.target.os = Cangjie::Triple::OSType::LINUX; #endif invocation.globalOptions.compilePackage = true; invocation.globalOptions.compilationCachedPath = "."; }; std::string code = R"( package pkg1 class C{} )"; }; TEST_F(CompilerInstanceTest, DISABLED_FullCompile) { std::unique_ptr instance = std::make_unique(invocation, diag); instance->srcDirs.emplace(FileUtil::JoinPath(projectPath, "unittests/Frontend/FullCompile/src")); instance->compileOnePackageFromSrcFiles = false; instance->cangjieHome = cangjieHome; instance->Compile(); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); auto pkg = pkgs[0]; ASTContext* ctx = instance->GetASTContextByPackage(pkg); ASSERT_TRUE(ctx != nullptr); EXPECT_EQ(ctx->curPackage, pkg); } TEST_F(CompilerInstanceTest, DISABLED_GetAllVisibleExtendMembers01) { std::unique_ptr instance = std::make_unique(invocation, diag); instance->srcDirs.emplace(FileUtil::JoinPath(projectPath, "unittests/Frontend/FullCompile/src")); instance->compileOnePackageFromSrcFiles = false; instance->cangjieHome = cangjieHome; instance->Compile(); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext* ctx = instance->GetASTContextByPackage(pkgs[0]); ASSERT_NE(ctx, nullptr); Searcher searcher; auto extendSyms = searcher.Search(*ctx, "ast_kind:extend_decl", Sort::posAsc); // Int64 -> #{Eqq} auto memberSet1 = instance->GetAllVisibleExtendMembers( StaticAs(extendSyms[0]->node)->extendedType->ty, *extendSyms[0]->node->curFile); bool containExtendMember = false; for (auto member : memberSet1) { EXPECT_TRUE(member->astKind == ASTKind::FUNC_DECL || member->astKind == ASTKind::PROP_DECL); if (member->identifier.Val() == "g") { containExtendMember = true; break; } } EXPECT_TRUE(containExtendMember); auto classSyms = searcher.Search(*ctx, "(ast_kind:class_decl && name:A)"); containExtendMember = false; // class A -> #{Eqq} auto memberSet2 = instance->GetAllVisibleExtendMembers( RawStaticCast(classSyms[0]->node), *extendSyms[0]->node->curFile); for (auto member : memberSet2) { EXPECT_TRUE(member->astKind == ASTKind::FUNC_DECL || member->astKind == ASTKind::PROP_DECL); if (member->identifier.Val() == "g") { containExtendMember = true; break; } } EXPECT_TRUE(containExtendMember); } TEST_F(CompilerInstanceTest, Comments) { std::unique_ptr instance = std::make_unique(invocation, diag); instance->srcDirs.emplace(FileUtil::JoinPath(projectPath, "unittests/Frontend/FullCompile/src")); instance->compileOnePackageFromSrcFiles = false; instance->cangjieHome = cangjieHome; instance->Compile(); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); bool oneComments{false}; for (auto& file : pkgs[0]->files) { auto comments = instance->GetSourceManager().GetSource(file->begin.fileID).offsetCommentsMap; if (comments.size() == 5) { oneComments = true; break; } } EXPECT_TRUE(oneComments); } TEST_F(CompilerInstanceTest, DISABLED_TrailingClosure) { std::unique_ptr instance = std::make_unique(invocation, diag); instance->srcDirs.emplace(FileUtil::JoinPath(projectPath, "unittests/Frontend/TrailingClosure/src")); instance->compileOnePackageFromSrcFiles = false; instance->cangjieHome = cangjieHome; instance->Compile(); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext* ctx = instance->GetASTContextByPackage(pkgs[0]); ASSERT_NE(ctx, nullptr); Searcher searcher; auto syms = searcher.Search(*ctx, "name:i && ast_kind: ref_expr"); // 3 * 2 of i. size_t n = 6; size_t isClonedNode = 0; for (size_t i = 0; i < n; i++) { ASSERT_TRUE(i < syms.size() && syms[i] && syms[i]->node); if (syms[i]->node->TestAttr(Attribute::IS_CLONED_SOURCE_CODE)) { ++isClonedNode; } } // Trailing Closure will be desugared without any cloned node. EXPECT_TRUE(isClonedNode == 0); syms = searcher.Search(*ctx, "name:f2 && ast_kind: ref_expr"); EXPECT_TRUE(syms.size() == 1); } cangjie_compiler-1.0.7/unittests/Frontend/FullCompile/000077500000000000000000000000001510705540100230435ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Frontend/FullCompile/src/000077500000000000000000000000001510705540100236325ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Frontend/FullCompile/src/test001.cj000066400000000000000000000014161510705540100253520ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. package pkg class A {} extend Int64 <: Eqq { public func g() { this.toString() } } extend A <: Eqq { public func g() { return "eqq" } } interface Eqq { func g(): String } class test001_fn01 { public static var def_var01 = "test001" public static func def_func_test001() { var aaa = def_var01 return aaa } } open class B {} class D <: B {} /** * hhh. */ func testIsAs() { let b = B() let d = D() var isD = b is D var asB = d as B } cangjie_compiler-1.0.7/unittests/Frontend/FullCompile/src/test002.cj000066400000000000000000000004501510705540100253500ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. package pkg class test002_fn01 {} cangjie_compiler-1.0.7/unittests/Frontend/TrailingClosure/000077500000000000000000000000001510705540100237365ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Frontend/TrailingClosure/src/000077500000000000000000000000001510705540100245255ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Frontend/TrailingClosure/src/lambda.cj000066400000000000000000000007701510705540100262670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. func f(a: Int64, fn: (Int64) -> Int64) { fn(a) } func FF2() { f(1, {i => i * i}) // normal function call f(1) {i => i * i} // trailing closures } func f2(fn: (Int64) -> Int64) { fn(1) } func NBAME22() { f2 {i => i * i} } cangjie_compiler-1.0.7/unittests/IncrCompile/000077500000000000000000000000001510705540100212555ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/IncrCompile/CMakeLists.txt000066400000000000000000000016641510705540100240240ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set(INCREMENTALCOMPILATIONLOGGER_UT_SRC $ $ $ $ $ $ $ $ ) add_executable(IncrementalCompilationLoggerTest IncrementalCompilationLoggerTest.cpp) target_link_libraries( IncrementalCompilationLoggerTest ${CMAKE_DL_LIBS} ${INCREMENTALCOMPILATIONLOGGER_UT_SRC} GTest::gtest GTest::gtest_main) add_test(NAME IncrementalCompilationLoggerTest COMMAND IncrementalCompilationLoggerTest) cangjie_compiler-1.0.7/unittests/IncrCompile/IncrementalCompilationLoggerTest.cpp000066400000000000000000000033231510705540100304220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include "gtest/gtest.h" #include "cangjie/IncrementalCompilation/IncrementalCompilationLogger.h" using namespace Cangjie; class IncrementalCompilationLoggerTest : public ::testing::Test { protected: void SetUp() override { } }; TEST_F(IncrementalCompilationLoggerTest, InvidPath) { auto& logger = IncrementalCompilationLogger::GetInstance(); logger.InitLogFile(""); EXPECT_EQ(logger.IsEnable(), false); logger.InitLogFile(".cache"); EXPECT_EQ(logger.IsEnable(), false); logger.InitLogFile(".cjo"); EXPECT_EQ(logger.IsEnable(), false); logger.InitLogFile("xxx"); EXPECT_EQ(logger.IsEnable(), false); logger.InitLogFile("xxx"); EXPECT_EQ(logger.IsEnable(), false); logger.InitLogFile(".log"); EXPECT_EQ(logger.IsEnable(), false); Cangjie::FileUtil::CreateDirs("log/"); logger.InitLogFile("log"); EXPECT_EQ(logger.IsEnable(), false); logger.InitLogFile("log/"); EXPECT_EQ(logger.IsEnable(), false); std::string nomalIncrLogPath = ".cached/2343242355.log"; if (Cangjie::FileUtil::FileExist(nomalIncrLogPath)) { logger.InitLogFile(nomalIncrLogPath); EXPECT_EQ(logger.IsEnable(), true); } else { logger.InitLogFile(nomalIncrLogPath); EXPECT_EQ(logger.IsEnable(), false); Cangjie::FileUtil::CreateDirs(".cached/"); logger.InitLogFile(nomalIncrLogPath); EXPECT_EQ(logger.IsEnable(), true); } } cangjie_compiler-1.0.7/unittests/Lex/000077500000000000000000000000001510705540100176015ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Lex/CMakeLists.txt000066400000000000000000000023211510705540100223370ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set(LEX_UT_SRC $ $ $ $ $ $ $ $ ) add_executable(LexerTest LexerTest.cpp) target_link_libraries( LexerTest ${CMAKE_DL_LIBS} ${LEX_UT_SRC} GTest::gtest GTest::gtest_main) add_test(NAME LexerTest COMMAND LexerTest) add_executable(TotalTest TotalTest.cpp) target_link_libraries( TotalTest ${CMAKE_DL_LIBS} ${LEX_UT_SRC} GTest::gtest GTest::gtest_main) add_test(NAME TotalTest COMMAND TotalTest) add_executable(LexerTerminatorTest LexerTerminatorTest.cpp) target_link_libraries( LexerTerminatorTest ${CMAKE_DL_LIBS} ${LEX_UT_SRC} GTest::gtest GTest::gtest_main) add_test(NAME LexerTerminatorTest COMMAND LexerTerminatorTest) cangjie_compiler-1.0.7/unittests/Lex/LexerTerminatorTest.cpp000066400000000000000000000056741510705540100243050ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include "gtest/gtest.h" #include "cangjie/Basic/Print.h" #include "cangjie/Basic/StringConvertor.h" #include "cangjie/Lex/Lexer.h" using namespace Cangjie; class LexerTerminatorTest : public ::testing::Test { protected: void SetUp() override { } std::unique_ptr lexer; DiagnosticEngine diag{}; }; TEST_F(LexerTerminatorTest, End) { SourceManager sm2; std::vector expectPos = {{0, 2, 6}, {0, 2, 5}, {0, 2, 4}}; std::vector escape_n = {"\"\"\"\nab\"\"\"", "\"\"\"a\nb\"\"\"", "\"\"\"ab\n\"\"\""}; for (size_t i = 0; i < expectPos.size(); i++) { Lexer lexer = Lexer(escape_n[i], diag, sm2); Token tok = lexer.Next(); Position endPos = tok.End(); EXPECT_EQ(endPos, expectPos[i]); } std::vector escape_r = {"\"\"\"\rab\"\"\"", "\"\"\"a\rb\"\"\"", "\"\"\"ab\r\"\"\""}; for (size_t i = 0; i < expectPos.size(); i++) { Lexer lexer = Lexer(escape_r[i], diag, sm2); Token tok = lexer.Next(); Position endPos = tok.End(); EXPECT_EQ(endPos, Position(0, 1, 10)); } std::vector escape_r_n = {"\"\"\"\r\nab\"\"\"", "\"\"\"a\r\nb\"\"\"", "\"\"\"ab\r\n\"\"\""}; for (size_t i = 0; i < expectPos.size(); i++) { Lexer lexer = Lexer(escape_r_n[i], diag, sm2); Token tok = lexer.Next(); Position endPos = tok.End(); EXPECT_EQ(endPos, expectPos[i]); } } TEST_F(LexerTerminatorTest, ScanComment) { SourceManager sm2; std::vector terminator = {"//abc\n", "//abc\r\n"}; Position expect_pos = {0, 1, 6}; for (size_t i = 0; i < terminator.size(); i++) { Lexer* lexer = new Lexer(terminator[i], diag, sm2); Token tok = lexer->Next(); Position endPos = tok.End(); EXPECT_EQ(endPos, expect_pos); } std::string nonTerminator = "//abc\r"; Lexer lexer = Lexer(nonTerminator, diag, sm2); Token tok = lexer.Next(); Position endPos = tok.End(); EXPECT_EQ(endPos, Position(0, 1, 7)); } TEST_F(LexerTerminatorTest, identify_terminator) { SourceManager sm2; std::vector terminator = {"\n", "\r\n"}; for (size_t i = 0; i < terminator.size(); i++) { Lexer lexer = Lexer(terminator[i], diag, sm2); Token term = lexer.Next(); EXPECT_EQ(term.kind, TokenKind::NL); Token end = lexer.Next(); EXPECT_EQ(end.kind, TokenKind::END); } std::string nonTerminator = "\r"; Lexer lexer = Lexer(nonTerminator, diag, sm2); Token term = lexer.Next(); EXPECT_EQ(term.kind, TokenKind::ILLEGAL); Token end = lexer.Next(); EXPECT_EQ(end.kind, TokenKind::END); }cangjie_compiler-1.0.7/unittests/Lex/LexerTest.cpp000066400000000000000000001206741510705540100222360ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include "gtest/gtest.h" #include "cangjie/Basic/DiagnosticEmitter.h" #include "cangjie/Basic/Print.h" #include "cangjie/Basic/StringConvertor.h" #include "cangjie/Basic/Utils.h" #include "cangjie/Lex/Lexer.h" using namespace Cangjie; class LexerTest : public ::testing::Test { protected: void SetUp() override { lexer = std::make_unique(code, diag, sm); } std::string code = R"( ?: true false None main(argc:Int=1,argv:string) { let a:Int=-40 let pi:Float=3.14 let alpha=0x1.1p1 let c:Rune = r'\'' // grh /*/**/*/ let d:string = "asdqwe" let d:string = J"asdqwe" let b = 2 ** -a print( (a+3*b, (a+3) *b) ) @abc }; )"; std::unique_ptr lexer; DiagnosticEngine diag{}; SourceManager sm; }; TEST_F(LexerTest, IntegerTokens) { // shared test unsigned int i = 0; std::vector expectShare = {"1", "40", "2", "3", "3"}; for (;;) { Token tok = lexer->Next(); if (tok.kind == TokenKind::INTEGER_LITERAL) { if (i >= expectShare.size()) { break; } EXPECT_EQ(tok.Value(), expectShare[i]); i++; } if (tok.kind == TokenKind::END) { break; } } // private test i = 0; std::string strInteger = R"( 00b 12k3 0b3 0x% 0xp3 )"; Lexer lexerstring(strInteger, diag, sm); for (;;) { Token tok = lexerstring.Next(); if (tok.kind == TokenKind::END) { break; } } // private test i = 0; std::string integerString = R"( 007 0_3 00_3 0b00101 0x3_2_ 0xABCDEFabcdef )"; std::vector expect = {"007", "0_3", "00_3", "0b00101", "0x3_2_", "0xABCDEFabcdef"}; Lexer lexerInteger(integerString, diag, sm); for (;;) { Token tok = lexerInteger.Next(); if (tok.kind == TokenKind::INTEGER_LITERAL) { if (i >= expect.size()) { break; } EXPECT_EQ(tok.Value(), expect[i]); i++; } if (tok.kind == TokenKind::END) { break; } } } TEST_F(LexerTest, FloatTokens) { // shared test unsigned int i = 0; std::vector expectShare = {"3.14", "0x1.1p1"}; for (;;) { Token tok = lexer->Next(); if (tok.kind == TokenKind::FLOAT_LITERAL) { if (i >= expectShare.size()) { break; } EXPECT_EQ(tok.Value(), expectShare[i]); i++; } if (tok.kind == TokenKind::END) { break; } } // private test i = 0; std::string strFloat = R"( 0x._3 3e++ 0x3. 0x3.3p_ 0x2_p 3.3.3 0x3.3pp4 0x3.3p4.3 0x._3 3.4e )"; Lexer lexerstring(strFloat, diag, sm); for (;;) { Token tok = lexerstring.Next(); if (tok.kind == TokenKind::END) { break; } } // private test i = 0; std::string floatString = R"( 3.4e3 3.4 3.4e-3 .3e4 .3 0x.3p3 0x.3_p3_ 0x3.3p3 )"; std::vector expect = {"3.4e3", "3.4", "3.4e-3", ".3e4", ".3", "0x.3p3", "0x.3_p3_", "0x3.3p3"}; Lexer lexerFloat(floatString, diag, sm); for (;;) { Token tok = lexerFloat.Next(); if (tok.kind == TokenKind::FLOAT_LITERAL) { if (i >= expect.size()) { break; } EXPECT_EQ(tok.Value(), expect[i]); i++; } if (tok.kind == TokenKind::END) { break; } } } TEST_F(LexerTest, KeywordTokens) { // shared test unsigned int i = 0; std::vector expectKeyword = {"main", "let", "let", "let", "let", "let", "let"}; for (;;) { Token tok = lexer->Next(); if (tok.kind <= TokenKind::MAIN && tok.kind >= TokenKind::STRUCT) { if (i >= expectKeyword.size()) { break; } EXPECT_EQ(tok.Value(), expectKeyword[i]); i++; } if (tok.kind == TokenKind::END) { break; } } // private test i = 0; std::string keyword = R"(func public let var class init !in )"; std::vector expect = {"func", "public", "let", "var", "class", "init", "!in"}; Lexer lexerKeyword(keyword, diag, sm); for (;;) { Token tok = lexerKeyword.Next(); if (tok.kind <= TokenKind::MAIN && tok.kind >= TokenKind::STRUCT) { if (i >= expect.size()) { break; } EXPECT_EQ(tok.Value(), expect[i]); i++; } if (tok.kind == TokenKind::END) { break; } } } TEST_F(LexerTest, CharTokens) { // shared test unsigned int i = 0; std::vector expectKeyword = {"\\\'"}; for (;;) { Token tok = lexer->Next(); if (tok.kind == TokenKind::RUNE_LITERAL) { EXPECT_EQ(tok.Value(), expectKeyword[i]); i++; if (expectKeyword.size()) { break; } } if (tok.kind == TokenKind::END) { break; } } // private test i = 0; std::string strChar = R"(' ' '\\' '\ueeee' '\u2345' 's' '2' '*')"; std::vector expectPrivate = {" ", R"(\\)", "\\ueeee", "\\u2345", "s", "2", "*"}; Lexer lexerchar(strChar, diag, sm); for (;;) { Token tok = lexerchar.Next(); if (tok.kind == TokenKind::RUNE_LITERAL) { if (i >= expectPrivate.size()) { break; } EXPECT_EQ(tok.Value(), expectPrivate[i]); i++; } if (tok.kind == TokenKind::END) { break; } } } TEST_F(LexerTest, StringTokens) { // shared test unsigned int i = 0; std::vector expectShare = {"asdqwe"}; for (;;) { Token tok = lexer->Next(); if (tok.kind == TokenKind::STRING_LITERAL) { if (i >= expectShare.size()) { break; } EXPECT_EQ(tok.Value(), expectShare[i]); i++; } if (tok.kind == TokenKind::END) { break; } } // private test i = 0; std::string strChar = R"("buasd" "\"" "12\u2341", 'xyz')"; std::vector expectPrivate = {"buasd", "\\\"", "12\\u2341", "xyz"}; std::vector isSingleQuotes = {false, false, false, true}; Lexer lexerstring(strChar, diag, sm); for (;;) { Token tok = lexerstring.Next(); if (tok.kind == TokenKind::STRING_LITERAL) { if (i >= expectPrivate.size()) { break; } EXPECT_EQ(tok.Value(), expectPrivate[i]); EXPECT_EQ(tok.isSingleQuote, isSingleQuotes[i]); i++; } if (tok.kind == TokenKind::END) { break; } } } TEST_F(LexerTest, MultilineStringTokens) { unsigned int i = 0; std::string strMulString = R"( """ buasd """ """ \ """ """ 12\u2341""")"; std::vector expectPrivate = {"buasd\n", "\\ ", "\n12\\u2341"}; Lexer lexerstring(strMulString, diag, sm); for (;;) { Token tok = lexerstring.Next(); if (tok.kind == TokenKind::MULTILINE_STRING) { if (i >= expectPrivate.size()) { break; } EXPECT_EQ(tok.Value(), expectPrivate[i]); i++; } if (tok.kind == TokenKind::END) { break; } } } TEST_F(LexerTest, MultilineStringTokenKind) { std::string strMulString = R"( """ ${a+"_abc"}""")"; Lexer lexerstring(strMulString, diag, sm); auto tok1 = lexerstring.Next(); EXPECT_EQ(tok1.kind, TokenKind::NL); auto tok2 = lexerstring.Next(); EXPECT_EQ(tok2.kind, TokenKind::MULTILINE_STRING); } TEST_F(LexerTest, MultilineiRawStringTokens) { unsigned int i = 0; std::string strMulRawString = R"( #" buasd "# #"\ "# #" "# )"; std::vector expectPrivate = {"\nbuasd\n", "\\ ", "\n\n\n"}; Lexer lexerstring(strMulRawString, diag, sm); for (;;) { Token tok = lexerstring.Next(); if (tok.kind == TokenKind::MULTILINE_RAW_STRING) { if (i >= expectPrivate.size()) { break; } EXPECT_EQ(tok.Value(), expectPrivate[i]); i++; } if (tok.kind == TokenKind::END) { break; } } } TEST_F(LexerTest, MultilineStringPositions) { unsigned int i = 0; std::string strMulString = R"( """ buasd """ """ \ """ """ 12\u2341""" """ """)"; std::vector expectPrivate = {"buasd\n", "\\ ", "\n12\\u2341", " "}; std::vector expectLine = {2, 4, 6, 8}; std::vector expectColumn = {1, 5, 1, 13}; Lexer lexerstring(strMulString, diag, sm); for (;;) { Token tok = lexerstring.Next(); if (tok.kind == TokenKind::MULTILINE_STRING) { if (i >= expectPrivate.size()) { break; } EXPECT_EQ(tok.Value(), expectPrivate[i]); EXPECT_EQ(tok.Begin().line, expectLine[i]); EXPECT_EQ(tok.Begin().column, expectColumn[i]); i++; } if (tok.kind == TokenKind::END) { break; } } } TEST_F(LexerTest, MultilineiRawStringPositions) { unsigned int i = 0; std::string strMulRawString = R"( #" buasd "# #"\ "# #" "# #" "# )"; std::vector expectPrivate = {"\nbuasd\n", "\\ ", "\n\n\n", " "}; std::vector expectLine = {2, 4, 5, 8}; std::vector expectColumn = {1, 4, 1, 4}; Lexer lexerstring(strMulRawString, diag, sm); for (;;) { Token tok = lexerstring.Next(); if (tok.kind == TokenKind::MULTILINE_RAW_STRING) { if (i >= expectPrivate.size()) { break; } EXPECT_EQ(tok.Value(), expectPrivate[i]); EXPECT_EQ(tok.Begin().line, expectLine[i]); EXPECT_EQ(tok.Begin().column, expectColumn[i]); i++; } if (tok.kind == TokenKind::END) { break; } } } TEST_F(LexerTest, IdentifierStartWithUnderscore) { std::vector identifierCases = {"_x", "__x", "x", "x_", "x__", "x_x", "x__x"}; for (auto& identifierCase : identifierCases) { Lexer lexer = Lexer(identifierCase, diag, sm); EXPECT_EQ(lexer.Next().kind, TokenKind::IDENTIFIER); EXPECT_EQ(lexer.Next().kind, TokenKind::END); } std::vector backquoteIdentifierCases = {"`_x`", "`__x`", "`x`", "`x_`", "`x__`", "`x_x`", "`x__x`"}; for (auto& backquoteIdentifierCase : backquoteIdentifierCases) { Lexer lexer = Lexer(backquoteIdentifierCase, diag, sm); EXPECT_EQ(lexer.Next().kind, TokenKind::IDENTIFIER); EXPECT_EQ(lexer.Next().kind, TokenKind::END); } // expected several wildcard DiagnosticEngine multiUnderscoreDiag{}; SourceManager multiSm; std::string multiUnderscore = "__)"; Lexer multiUnderscoreLexer = Lexer(multiUnderscore, multiUnderscoreDiag, multiSm); EXPECT_EQ(multiUnderscoreLexer.Next().kind, TokenKind::IDENTIFIER); EXPECT_EQ(multiUnderscoreLexer.Next().kind, TokenKind::RPAREN); EXPECT_EQ(multiUnderscoreLexer.Next().kind, TokenKind::END); EXPECT_EQ(multiUnderscoreDiag.GetErrorCount(), 0); // expected wild card and div std::string mixedIdentifier = "_/_"; Lexer mixedLexer = Lexer(mixedIdentifier, diag, sm); EXPECT_EQ(mixedLexer.Next().kind, TokenKind::WILDCARD); EXPECT_EQ(mixedLexer.Next().kind, TokenKind::DIV); EXPECT_EQ(mixedLexer.Next().kind, TokenKind::WILDCARD); EXPECT_EQ(mixedLexer.Next().kind, TokenKind::END); } TEST_F(LexerTest, Identifier) { unsigned int i = 0; std::string identifier = R"( abc `abc` `int` )"; std::vector expectPrivate = {"abc", "`abc`", "`int`"}; Lexer* lexer = new Lexer(identifier, diag, sm); for (;;) { Token tok = lexer->Next(); if (tok.kind == TokenKind::IDENTIFIER) { if (i >= expectPrivate.size()) { break; } EXPECT_EQ(tok.Value(), expectPrivate[i]); i++; } if (tok.kind == TokenKind::END) { break; } } } TEST_F(LexerTest, AnnotationToken) { unsigned int i = 0; std::vector expectShare = {"@abc"}; for (;;) { Token tok = lexer->Next(); if (tok.kind == TokenKind::ANNOTATION) { if (i >= expectShare.size()) { break; } EXPECT_EQ(tok.Value(), expectShare[i]); i++; } if (tok.kind == TokenKind::END) { break; } } i = 0; std::string test = R"(@int @`a123` @abc @ abc @123 @_ @__ )"; // @123 is @ and 123 std::vector expectPrivate = {"@int", "@`a123`", "@abc", "@_", "@__"}; Lexer* lexerchar = new Lexer(test, diag, sm); for (;;) { Token tok = lexerchar->Next(); if (tok.kind == TokenKind::ANNOTATION) { if (i >= expectPrivate.size()) { break; } EXPECT_EQ(tok.Value(), expectPrivate[i]); i++; } if (tok.kind == TokenKind::END) { break; } } } TEST_F(LexerTest, CommentTokens) { // shared test unsigned int i = 0; std::vector expectShare = {"// grh", "/*/**/*/"}; for (;;) { Token tok = lexer->Next(); if (tok.kind == TokenKind::COMMENT) { if (i >= expectShare.size()) { break; } EXPECT_EQ(tok.Value(), expectShare[i]); i++; } if (tok.kind == TokenKind::END) { break; } } // private test i = 0; std::string strComment = R"( // &* /* ** /* /& */ */ )"; std::vector expectPrivate = {"// &*", "/* **\n /* /&\n */\n */"}; Lexer lexerstring(strComment, diag, sm); for (;;) { Token tok = lexerstring.Next(); if (tok.kind == TokenKind::COMMENT) { if (i >= expectPrivate.size()) { break; } EXPECT_EQ(tok.Value(), expectPrivate[i]); i++; } if (tok.kind == TokenKind::END) { break; } } } TEST_F(LexerTest, LookAhead) { // shared test std::vector expectShare = {"main", "(", "argc"}; std::vector share; for (;;) { Token tok = lexer->Next(); if (tok.kind == TokenKind::MAIN) { share.push_back(tok.Value()); auto cache = lexer->LookAhead(2); for (const auto& i : cache) { share.push_back(i.Value()); } } if (tok.kind == TokenKind::END) { break; } } EXPECT_EQ(expectShare.size(), share.size()); if (expectShare.size() == share.size()) { EXPECT_TRUE(std::equal(expectShare.begin(), expectShare.end(), share.begin())); } } TEST_F(LexerTest, AllTokens) { for (;;) { Token tok = lexer->Next(); if (tok.kind == TokenKind::END) { break; } } } TEST_F(LexerTest, ErrorPrint) { std::string strError = R"( 0x 123e 0x1.2 "qwer\c" '\u21p '1234' "12 '\u123dfg' '\u1233444444' 'u '\u' ' '\' '\ '\2 '\u 0b_1 """ \abc """ "\u" <: a._1._2._3 `0` `asdf 0x_1 0x123.23p_1 123._1 0x213.123p123_2 )"; Lexer* lexerError = new Lexer(strError, diag, sm); for (;;) { Token tok = lexerError->Next(); if (tok.kind == TokenKind::END) { break; } } } TEST_F(LexerTest, TokenValues) { ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::DOT)]) == "."); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::COMMA)]) == ","); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::LPAREN)]) == "("); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::RPAREN)]) == ")"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::LSQUARE)]) == "["); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::RSQUARE)]) == "]"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::LCURL)]) == "{"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::RCURL)]) == "}"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::EXP)]) == "**"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::MUL)]) == "*"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::MOD)]) == "%"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::DIV)]) == "/"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::ADD)]) == "+"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::SUB)]) == "-"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::INCR)]) == "++"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::DECR)]) == "--"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::AND)]) == "&&"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::OR)]) == "||"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::NOT)]) == "!"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::BITAND)]) == "&"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::BITOR)]) == "|"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::BITXOR)]) == "^"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::LSHIFT)]) == "<<"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::RSHIFT)]) == ">>"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::COLON)]) == ":"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::SEMI)]) == ";"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::ASSIGN)]) == "="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::ADD_ASSIGN)]) == "+="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::SUB_ASSIGN)]) == "-="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::MUL_ASSIGN)]) == "*="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::EXP_ASSIGN)]) == "**="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::DIV_ASSIGN)]) == "/="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::MOD_ASSIGN)]) == "%="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::AND_ASSIGN)]) == "&&="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::OR_ASSIGN)]) == "||="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::BITAND_ASSIGN)]) == "&="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::BITOR_ASSIGN)]) == "|="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::BITXOR_ASSIGN)]) == "^="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::LSHIFT_ASSIGN)]) == "<<="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::RSHIFT_ASSIGN)]) == ">>="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::ARROW)]) == "->"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::DOUBLE_ARROW)]) == "=>"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::RANGEOP)]) == ".."); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::CLOSEDRANGEOP)]) == "..="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::ELLIPSIS)]) == "..."); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::HASH)]) == "#"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::AT)]) == "@"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::QUEST)]) == "?"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::LT)]) == "<"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::GT)]) == ">"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::LE)]) == "<="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::GE)]) == ">="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::NOTEQ)]) == "!="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::EQUAL)]) == "=="); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::WILDCARD)]) == "_"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::INT8)]) == "Int8"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::INT16)]) == "Int16"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::INT32)]) == "Int32"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::INT64)]) == "Int64"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::UINT8)]) == "UInt8"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::UINT16)]) == "UInt16"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::UINT32)]) == "UInt32"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::UINT64)]) == "UInt64"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::FLOAT16)]) == "Float16"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::FLOAT32)]) == "Float32"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::FLOAT64)]) == "Float64"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::RUNE)]) == "Rune"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::BOOLEAN)]) == "Bool"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::UNIT)]) == "Unit"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::STRUCT)]) == "struct"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::ENUM)]) == "enum"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::THISTYPE)]) == "This"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::PACKAGE)]) == "package"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::IMPORT)]) == "import"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::CLASS)]) == "class"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::INTERFACE)]) == "interface"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::FUNC)]) == "func"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::LET)]) == "let"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::VAR)]) == "var"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::TYPE)]) == "type"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::INIT)]) == "init"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::THIS)]) == "this"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::SUPER)]) == "super"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::IF)]) == "if"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::ELSE)]) == "else"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::CASE)]) == "case"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::TRY)]) == "try"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::CATCH)]) == "catch"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::HANDLE)]) == "handle"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::FINALLY)]) == "finally"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::FOR)]) == "for"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::DO)]) == "do"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::WHILE)]) == "while"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::THROW)]) == "throw"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::PERFORM)]) == "perform"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::RESUME)]) == "resume"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::RETURN)]) == "return"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::CONTINUE)]) == "continue"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::BREAK)]) == "break"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::AS)]) == "as"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::IN)]) == "in"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::NOT_IN)]) == "!in"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::MATCH)]) == "match"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::WHERE)]) == "where"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::WITH)]) == "with"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::THROWING)]) == "throwing"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::STATIC)]) == "static"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::PUBLIC)]) == "public"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::PRIVATE)]) == "private"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::PROTECTED)]) == "protected"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::ENUM)]) == "enum"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::OVERRIDE)]) == "override"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::ABSTRACT)]) == "abstract"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::OPEN)]) == "open"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::OPERATOR)]) == "operator"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::UPPERBOUND)]) == "<:"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::SPAWN)]) == "spawn"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::DOUBLE_COLON)]) == "::"); ASSERT_TRUE(std::string(TOKENS[static_cast(TokenKind::FEATURES)]) == "features"); ASSERT_EQ(static_cast(TokenKind::FEATURES) + 1, sizeof(TOKENS) / sizeof(*TOKENS)); } TEST_F(LexerTest, TokenKindValues) { ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::DOT)]) == "dot"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::COMMA)]) == "comma"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::LPAREN)]) == "l_paren"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::RPAREN)]) == "r_paren"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::LSQUARE)]) == "l_square"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::RSQUARE)]) == "r_square"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::LCURL)]) == "l_curl"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::RCURL)]) == "r_curl"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::EXP)]) == "exp"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::MUL)]) == "mul"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::MOD)]) == "mod"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::DIV)]) == "div"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::ADD)]) == "add"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::SUB)]) == "sub"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::INCR)]) == "incr"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::DECR)]) == "decr"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::AND)]) == "and"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::OR)]) == "or"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::NOT)]) == "not"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::BITAND)]) == "bit_and"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::BITOR)]) == "bit_or"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::BITXOR)]) == "bit_xor"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::LSHIFT)]) == "lshift"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::RSHIFT)]) == "rshift"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::COLON)]) == "colon"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::SEMI)]) == "semi"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::ASSIGN)]) == "assign"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::ADD_ASSIGN)]) == "add_assign"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::SUB_ASSIGN)]) == "sub_assign"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::MUL_ASSIGN)]) == "mul_assign"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::EXP_ASSIGN)]) == "exp_assign"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::DIV_ASSIGN)]) == "div_assign"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::MOD_ASSIGN)]) == "mod_assign"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::AND_ASSIGN)]) == "and_assign"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::OR_ASSIGN)]) == "or_assign"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::BITAND_ASSIGN)]) == "bit_and_assign"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::BITOR_ASSIGN)]) == "bit_or_assign"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::BITXOR_ASSIGN)]) == "bit_xor_assign"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::LSHIFT_ASSIGN)]) == "lshift_assign"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::RSHIFT_ASSIGN)]) == "rshift_assign"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::ARROW)]) == "arrow"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::DOUBLE_ARROW)]) == "double_arrow"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::RANGEOP)]) == "range_op"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::HASH)]) == "hash"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::AT)]) == "at"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::QUEST)]) == "quest"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::LT)]) == "less"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::GT)]) == "greater"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::LE)]) == "less_equal"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::GE)]) == "greater_equal"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::NOTEQ)]) == "not_equal"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::EQUAL)]) == "equal"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::WILDCARD)]) == "wildcard"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::INT8)]) == "Int8"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::INT16)]) == "Int16"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::INT32)]) == "Int32"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::INT64)]) == "Int64"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::UINT8)]) == "UInt8"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::UINT16)]) == "UInt16"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::UINT32)]) == "UInt32"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::UINT64)]) == "UInt64"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::FLOAT16)]) == "Float16"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::FLOAT32)]) == "Float32"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::FLOAT64)]) == "Float64"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::RUNE)]) == "Rune"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::BOOLEAN)]) == "Bool"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::UNIT)]) == "Unit"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::STRUCT)]) == "struct"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::ENUM)]) == "enum"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::THISTYPE)]) == "This"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::PACKAGE)]) == "package"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::IMPORT)]) == "import"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::CLASS)]) == "class"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::INTERFACE)]) == "interface"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::FUNC)]) == "func"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::LET)]) == "let"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::VAR)]) == "var"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::TYPE)]) == "type"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::INIT)]) == "init"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::THIS)]) == "this"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::SUPER)]) == "super"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::IF)]) == "if"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::ELSE)]) == "else"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::CASE)]) == "case"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::TRY)]) == "try"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::CATCH)]) == "catch"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::HANDLE)]) == "handle"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::FINALLY)]) == "finally"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::FOR)]) == "for"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::DO)]) == "do"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::WHILE)]) == "while"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::THROW)]) == "throw"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::RETURN)]) == "return"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::CONTINUE)]) == "continue"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::BREAK)]) == "break"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::AS)]) == "as"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::IN)]) == "in"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::NOT_IN)]) == "not_in"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::MATCH)]) == "match"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::WHERE)]) == "where"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::WITH)]) == "with"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::STATIC)]) == "static"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::PUBLIC)]) == "public"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::PRIVATE)]) == "private"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::PROTECTED)]) == "protected"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::ENUM)]) == "enum"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::OVERRIDE)]) == "override"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::ABSTRACT)]) == "abstract"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::OPEN)]) == "open"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::OPERATOR)]) == "operator"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::UPPERBOUND)]) == "upperbound"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::IDENTIFIER)]) == "identifier"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::INTEGER_LITERAL)]) == "integer_literal"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::FLOAT_LITERAL)]) == "float_literal"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::COMMENT)]) == "comment"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::NL)]) == "newline"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::END)]) == "end"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::SENTINEL)]) == "sentinel"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::RUNE_LITERAL)]) == "char_literal"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::STRING_LITERAL)]) == "string_literal"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::MULTILINE_STRING)]) == "multiline_string"); ASSERT_TRUE( std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::MULTILINE_RAW_STRING)]) == "multiline_raw_string"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::BOOL_LITERAL)]) == "bool_literal"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::DOLLAR_IDENTIFIER)]) == "dollar_identifier"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::ANNOTATION)]) == "annotation"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::ILLEGAL)]) == "illegal"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::DOUBLE_COLON)]) == "double_colon"); ASSERT_TRUE(std::string(TOKEN_KIND_VALUES[static_cast(TokenKind::FEATURES)]) == "features"); ASSERT_EQ( static_cast(TokenKind::FEATURES) + 1, sizeof(TOKEN_KIND_VALUES) / sizeof(*TOKEN_KIND_VALUES)); } TEST_F(LexerTest, UTF8ToCodepoint) { std::string utf8 = R"(中文\u{4e2d}\t)"; std::vector codepoint = {20013, 25991, 20013, 9}; auto vec = StringConvertor::UTF8ToCodepoint(StringConvertor::Normalize(utf8)); ASSERT_TRUE(std::equal(vec.begin(), vec.end(), codepoint.begin())); } TEST_F(LexerTest, Diagnose) { std::string str = R"(abc def)"; auto fileid = sm.AddSource("test", str); Lexer* lexer = new Lexer(fileid, str, diag, sm); Token tok = lexer->Next(); EXPECT_EQ(tok.End(), Position(1, 1, 4)); DiagnosticEngine diag; diag.DiagnoseRefactor(DiagKindRefactor::parse_this_type_not_allow, tok.Begin()); } TEST_F(LexerTest, PrintAsciiControlCode) { std::string str = "\r"; DiagnosticEngine diag{}; SourceManager sm; auto fileid = sm.AddSource("test.cj", str); diag.SetSourceManager(&sm); Lexer* lexer = new Lexer(fileid, str, diag, sm); Token tok = lexer->Next(); lexer->Next(); auto diagnostics = diag.GetCategoryDiagnostic(DiagCategory::LEX); EXPECT_EQ(diagnostics.size(), 1); std::ostringstream output; DiagnosticEmitter(diagnostics[0], false, true, output, sm).Emit(); auto splits = Utils::SplitLines(output.str()); EXPECT_EQ(splits[3].substr(22, 8), "\x1b[30;47m"); EXPECT_EQ(splits[3].substr(30, 8), "\\u{000D}"); EXPECT_EQ(splits[3].substr(38, 4), "\x1b[0m"); } cangjie_compiler-1.0.7/unittests/Lex/TotalTest.cpp000066400000000000000000000630721510705540100222400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include "gtest/gtest.h" #include "cangjie/Lex/Lexer.h" using namespace Cangjie; class TotalTest : public ::testing::Test { protected: void SetUp() override { lexer = std::make_unique(code, diag, sm); } std::string code = R"( main(args: Array) { //from arithmeticOperators.char let a = 2 + 3 //add: 5 let b = 3 - 1 //sub: 2 let c = 3 * 4 //multi: 12 let d = 6.6 / 1.1 //division: 6 let e = 7 % 3 //mod: 1 let f = 2**3 // power: 8: 8 var result = 5+10-3*4**2/3%5 // 5+10-3*(4**2)/3%5=5+10-(3*16)/3%5=5+10-(48/3)%5=5+10-(16%5)=5+10-1=15-1=14 //from array.char let arrayZero[2]: Int //define arrayZero whose size is 2 arrayZero[0] = 10 //initialization of arrayZero arrayZero[1] = 20 let arrayOne[2]: Int = [10, 20] //define arrayOne whose size is 2 and initialize it var arrayTwo[]: Int = [10, 20, 30] //define arrayTwo whose size is 3 and initialize it true ? arrayOne : arrayTwo // return arrayOne let arrayThree[2][3]: Int //define two-dimensional arrayThree whose size is 2*3 let arrayFour[2][3]: Int = [10,20,30;40,50,60]//define two-dimensional arrayFour whose size is 2*3 and initialize it var arrayZeroSize = arrayZero.size // arrayZeroSize=2 var arrayFourSize = arrayFour.size // arrayZeroSize = 6 //from bigNumbers.char let bigIntNum1: BigInteger = 100000000000000000000 let bigIntNum2: BigInteger = 200000000000000000000 var bigIntNum3: BigInteger bigIntNum3 = bigIntNum1 + bigIntNum2 bigIntNum3 = bigIntNum1 - bigIntNum2 bigIntNum3 = bigIntNum1 * bigIntNum2 bigIntNum3 = bigIntNum1 / bigIntNum2 let bigFloatNum1: BigFloat = 1.1E500 let bigFloatNum2: BigFloat = 1.2E500 var bigFloatNum3: BigFloat bigFloatNum3 = bigFloatNum1 + bigFloatNum2 bigFloatNum3 = bigFloatNum1 - bigFloatNum2 bigFloatNum3 = bigFloatNum1 * bigFloatNum2 bigFloatNum3 = bigFloatNum1 / bigFloatNum2 //from bitwiseLogicalOperators.char ~10 //bitwise logical NOT operator: 255-10=245 10 << 1 //left shift operator: 10*2=20 10 >> 1 //left shift operator: 10/2=5 10 & 15 //bitwise logical AND operator: 15 10 ^ 15 //bitwise logical XOR operator: 5 10 | 15 //bitwise logical OR operator: 15 //from collection.char let mListOne: List = {3} //define read-only List mListOne var mListTwo: List = {4, 5} //define writable List mListTwo var mListThree = mListOne + mListTwo //mListThree={1,2,3,4,5} var mListThreeElement = mListThree.get(0) //mListThreeElement=3 mListThree.set(2,6) //mListThree={3,4,6} mListThree.add(2,5) //mListThree={3,4,5,6} mListThree.remove(0) //mListThree={4,5,6} mListThree.reverse(0) //mListThree={6,5,4} mListThree.sortInc(0) //mListThree={4,5,6} mListThree.sortDec(0) //mListThree={6,5,4} var mListThreeLength = mListThree==mListTwo ? mListThree.length:mListTwo.length //mListThreeLength=3 mListThree.contains(4) //return true mListThree.isEmpty() //return false for item in mListThree { //visit all elements in mListThree print(item) //output: 6 5 4 } var mSetOne: Set = {1, 2, 3} //define writable Set mSetOne={1, 2, 3} let mSetTwo: Set = {3, 4, 5} //define read-only Set mSetTwo={3, 4, 5} var mSetThree = mSetOne.intersect(mSetTwo) //mSetThree={3} mSetThree = mSetOne + mSetTwo //mSetThree={1,2,3,4,5}, duplicate element are removed mSetThree = mSetThree - mSetOne //mSetThree={4,5} mSetThree.add(3) //mSetThree={3,4,5} mSetThree.add(6) //mSetThree={3,4,5,6} mSetThree.remove(3) //mSetThree={4,5,6} var mSetThreeLength = mSetThree!=mSetTwo ? mSetThree.length:mSetTwo.length //mSetThreeLength=3 mSetThree.contains(4) //return true mSetThree.isEmpty() //return false for item in mSetThree { //visit all elements in mSetThree print(item) //output: 4 5 6 } var mMapOne: Map = {1 -> "Aa", 2 -> "Bb", 3 -> "Cc"} //define writable Map mMapOne let mMapTwo: Map = {3 -> "Cc", 4 -> "Dd", 5 -> "Ee"} //define read-only Map mMapTwo var mMapThree: Map mMapThree = mMapOne.intersect(mMapTwo) //mMapThree={3 -> "Cc"} mMapThree = mMapOne + mMapTwo mMapThree = mMapThree - {1, 2, 3} //mMapThree={4 -> "Dd", 5 -> "Ee"} mMapThree.add(3 -> "Cc") //mMapThree={3 -> "Cc", 4 -> "Dd", 5 -> "Ee"} mMapThree.add(6 -> "Ff") //mMapThree={3 -> "Cc", 4 -> "Dd", 5 -> "Ee", 6 -> "Ff"} mMapThree.remove(3 -> "Cc") //mMapThree={4 -> "Dd", 5 -> "Ee", 6 -> "Ff"} var mMapThreeLength = mMapThree!=mMapTwo ? mMapThree.length:mMapTwo.length //mMapThreeLength=3 mMapThree.containsKey(4) //return true mMapThree.containsValue("Ff") //return true mMapThree.contains(7 -> "Gg") //return false mMapThree.isEmpty() //return false for item in mMapThree { //visit all elements in mMapThree print(item) //output: 4=Dd 5=Ee 6=Ff } //from comments.char //oneline comments /*multiline comments */ /* /*nested comments*/ */ /** doc comments */ //from comparisonOperators.char expr1 == expr2 //Equal to expr1 != expr2 //Not equal to expr1 < expr2 //Less than expr1 <= expr2 //Less than or equal to expr1 > expr2 //Greater than expr1 >= expr2 //Greater than or equal to //from compoundAssignOperators.char a += b //a = a + b a -= b // a = a - b a *= b //a = a * b a /= b // a = a / b a %= b // a = a % b a **= b // a = a ** b a &&= b //a = a && b a ||= b // a = a || b a &= b //a = a & b a |= b // a = a | b a ^= b //a = a ^ b a <<= b // a = a << b a >>= b // a = a >> b //from conditionalOperator var i: Int = 15 var j: Int = 20 var k: Int = i>j ? i : j //k=max(i,j)=20 //from controlTransfer.char //break statement var index:Int = 0 while (index < 50){ index = index + 1 if ((index % 4 == 0) && (index % 6 == 0)){ print("\(index) is divisible by both 4 and 6") break } } //break statement var index:Int = 0 for (var i:Int = 0; i < 5; i++){ while (index < 50){ index = index + 1 if ((index % 4 == 0) && (index % 6 == 0)){ print("\(index) is divisible by both 4 and 6") break } } } //continue statement var index:Int = 0 while (index < 50){ index = index + 1 if ((index % 4 == 0) && (index % 6 == 0)){ print("\(index) is divisible by both 4 and 6") continue } print("\(index) is not what we want") } //fallthrough statement let language = "Java" var output = "The language " var result: String = match language { with "Java" | "Kotlin" => output += language +" is a programing language and" fallthrough with _ => output += " object-oriented." } print(output) // Prints "Java is a programing language and object-oriented." //return statement func equal(a : Int, b: Int) { if (a == b) { print("a is equal to b") return } else { print("a is not equal to b") } } func larger(a : Int, b: Int) : Int{ if (a >= b) { return a } else { return b } } //throw statement func div(a : Int, b: Int) : Int{ if (b != 0) { return a/b } else { throw Exception() } } //from enum enum TimeUnit { Year | Month | Day | Hour } enum TimeUnit { Year, Month, Day, Hour } enum TimeUnit: Int { Year = 0 | Month = 1 | Day = 2 | Hour = 3 } enum TimeUnit: Float { Year = 12.0 | Month = 30.0 | Day = 24.0 | Hour = 1.0 } enum TimeUnit: Rune { Year = 'Y' | Month = 'M' | Day = 'D' | Hour = 'H' } enum TimeUnit: String { Year = "year" | Month = "month" | Day = "day" | Hour = "hour" } enum TimeUnit: Int {//with Year=0,Month=1,Day=2,Hour=3 Year, Month, Day, Hour } enum TimeUnit: String {//with Year="year",Month="month",Day="day",Hour="hour" Year, Month, Day, Hour } //define associated values with names enum TimeUnit { Year(year: Int) | Month(year: Int, month: Float) | Day(year: Int, month: Float, day: Float) | Hour(year: Int, month: Float, day: Float, hour: Float) } //define associated values without names enum TimeUnit { Year(Int) | Month(Int, Float) | Day(Int, Float, Float) | Hour(Int, Float, Float, Float) } //define associated values and raw values enum TimeUnit: Int { Year(year: Int)=1 | Month(year: Int, month: Float)=2 | Day(year: Int, month: Float, day: Float)=3 | Hour(year: Int, month: Float, day: Float, hour: Float)=4 } main(args: Array) { var time = TimeUnit(rawValue=0) // time = TimeUnit.Year time = TimeUnit(rawValue=1) // time = TimeUnit.Month var time = Year time = Month var howManyHours: Int = match time { with Year => 365*24 with Month => 30*24 with Day => 24 with Hour => 1 } let months = Month(1, 1.5) var howManyHours: Float = match months { with Year(y) => y*365*24 with Month(y,m) => y*365*24+m*30*24 with Day(y,m,d) => y*365*24+m*30*24+d*24 with Hour(y,m,d,h) => y*365*24+m*30*24+d*24+h } } //from ifExpression.char let x:Int = 2 /* if x > 0 { //error, must be (x > 0) print("x is bigger than 0") } */ /* if (x > 0) //error, print statement must be put into {} print("x is bigger than 0") */ if (x > 0) { print("x is bigger than 0") } else if (x<0){ print("x is lesser than 0") } else { print("x equals to 0") } //from incrAndDecr.char var i: Int = 5 var j: Int = 6 ++i //i=6 i++ //i=7 --i //i=6 i-- //i=5 //from letAndVar.char let width: Int width = 10 let length: Int = 20 var width2: Int =10 var width3: Int width3 = 10 var width4 = 10 )" // break long string into two, string literal longer than 16380 // characters will be truncated in visual studio toolchain R"( //from literals 0b0001_1000 //binary 0o30 //octal 24 //decimal 0x18 //hexadecimal 3.14 //decimal float 3.14 2.4e-1 //decimal float 0.24 2e3 //decimal float 8 .8 //decimal float 0.8 .123e2 //decimal float 12.3 0x1.1p0 //hexadecimal float 1.0625(decimal value) 0x1p2 //hexadecimal float 4(decimal value) 0x.2p4 //hexadecimal float 2(decimal value) 'S' '5' ' ' //blank true false //from logicalOperators.char !expr //logical NOT expr1 && expr2 //logical AND expr1 || expr2 //logical OR //from loopExpression.char let arr[] = [0, 1, 2, 3, 4] for (var i:Int = 0; i < 5; i++){ print(arr[i]) } let arr[] = [0, 1, 2, 3, 4] let i = 0 for (; i < 5; i++){ //forInit is ommited print(arr[i]); } let arr[] = [0, 1, 2, 3, 4] let i = 0 for (; ; i++){ //forInti and condition are ommited if (i<5){ print(arr[i]); } else{ break } } let arr[] = [0, 1, 2, 3, 4] let i = 0 for (; ; ){ //forInti, condition and expression are ommited if (i<5){ print(arr[i]); i++ } else{ break } } let arr1[] = [0, 1, 2, 3, 4] let arr2[] = [10, 11, 12, 13, 14] for (var i:Int = 0, var j:Int = 0; i < 5 && j < 5; i++, j++){ print(arr[i]); print(arr[j]); } let arr[] = [0, 1, 2, 3, 4] for (i in arr){ print(i) } let intList: List = {0, 1, 2, 3, 4} for (item in intList){ print(item) } let intSet: Set = {0, 1, 2, 3, 4} for (item in intSet){ print(item) } let map: Map = {0 -> "a", 1 -> "b", 2 -> "c", 3 -> "d", 4 -> "e"} for ((number,char) in map){ print(number) print(char) } let intRange: ClosedRange = 0:10 for (number in intRange){ print(number) } var hundred = 0 //while statement while (hundred < 100){ hundred++ } var hundred = 0 //do-while statement do{ hundred++ }while (hundred < 100) //from optionalType.char let optionalInt: Int? = nil var optionalChar: Rune? = 'm' var optionalBoolean: Boolean? optionalBoolean = nil let arrayZero[2]: String? = [nil, nil] let width: Int? = 10 let height: Int? = nil var widthVal: Int var heightVal: Int if (width != nil){ widthVal = width! } else{ widthVal = 0 } if (height.hasValue()){ heightVal = height.get() } else{ heightVal = 0 } //from patternMatching //constant pattern let score: Int = 90 let PASS = 60 let FULL = 60 var scoreResult: String = match score { with 0 => "zero" with 10 | 20 | 30 | 40 | 50 => "fail" with $PASS => "pass" with 70 | 80 => "good" with 90 | $FULL => "excellent" with _ => } let score: Int = 90 var scoreResult: String = match score { with 60 => "pass" with 70 | 80 => "good" with 90 | 100 => "excellent" with _ => "fail" //used for default with } let scoreTuple = ("Ark-Lang",90) var scoreResult: String = match scoreTuple { with (_, 60) => "pass" with (_, 70 | 80) => "good" with (_, 90 | 100) => "excellent" with (_, _) => "fail" } //variable pattern1 let score: Int = 90 let PASS = 60 let Full = 100 let good = 70 var scoreResult: String = match score { with $PASS => "pass" // constant pattern with $Full => "Full" // ok, but not recommend with good => "good" } //variable pattern2 let score: Int = 90 var scoreResult: String = match score { with 60 => "pass" with 70 | 80 => "good" with 90 | 100 => "excellent" with failScore => "failed with "+failScore.toString() } let scoreTuple = ("Ark-Lang",90) var scoreResult: String = match scoreTuple { with (who, 60) => who+" passed" with (who, 70 | 80) => who+" got good" with (who, 90 | 100) => who+" got excellent" with (who, failScore) => who+" failed with "+failScore.toString() } //value-binding pattern let scoreTuple = (("Allen",90),("Jack",60)) var scoreResult: String = match scoreTuple { with ((_, _), secondStudent @ (who,score)) => "The info. of the second student is "+ secondStudent } //tuple pattern let scoreTuple = ("Allen",90) var scoreResult: String = match scoreTuple { with ("Bob",90) => "Bob got 90" with ("Allen",score) => "Allen got "+score.toString() with (_,100) => "someone got 100" with (_,_) => "" } //sequence pattern let scoreList: List = {50,60,70,80,90} // sorted array var scoreResult: String = match scoreList { with List{50,...} => "the lowest score is 50" with List{...,90} => "the highes score is 90" with List{low,...,high} => "the lowest score is "+low.toString()+", the highes score is "+high.toString() } //map pattern let scoreMap: Map = {"E" -> 50, "D" -> 60, "C" -> 70, "B" -> 80, "A" -> 90} var scoreResult: String = match scoreMap { with Map{"A" -> score} => "the highest score is "+score.toString() with Map{"E" -> score} => "the lowest score is "+score.toString() with Map{"A" -> score0, "B" -> score1, "C" -> score2, "D" -> score3} => "passed scores are "+score0.toString()+", "+score1.toString()+", "+score2.toString()+", "+score3.toString() //illegal collection patterns: //with Map{a} => "..." // illegal map pattern //with Map{a -> 50} => "..." // illegal map pattern } //in pattern let scorePass: Set = {60, 70, 80, 90, 100} let score = 50 var scoreResult: String = match score { with in $scorePass => "pass" with in (10,20,30) => "very low score" with in [40,50] => "low score" } //type pattern open class Point{ var x: Int var y: Int init(x: Int, y: Int){ this.x = x this.y = y } } class ColoredPoint: Point{ var color: String init(x: Int, y: Int, color: String){ this.x = x this.y = y this.color = color } } let normalPo = Point(5,10) let colorPo = ColoredPoint(8,24,"red") var rectangleArea1: Int = match normalPo { with _ : Point => normalPo.x*normalPo.y with _ => 0 } var rectangleArea2: Int = match colorPo { with cp : Point => cp.x*cp.y //cp.color is not existed with _ => 0 } //enum pattern //sum type enum TimeUnit_1 { Year | Month | Day | Hour } //product type enum TimeUnit_2 { Month(year: Float, month: Float) } //sum type that is consist of product type enum TimeUnit { Year(year: Float) | Month(year: Float, month: Float) | Day(year: Float, month: Float, day: Float) | Hour(year: Float, month: Float, day: Float, hour: Float) } //sum type let tu = Year var tuString: String = match tu { with Year => "use Year as timeUnit" with Month => "use Month as timeUnit" with Day => "use Day as timeUnit" with Hour => "use Hour as timeUnit" } //sum type that is consist of product type let oneYear = Year(1.0) var howManyHours: Float = match oneYear { with Year(y) => y*365*24 with Month(y,m) => y*365*24+m*30*24 with Day(y,m,d) => y*365*24+m*30*24+d*24 with Hour(y,m,d,h) => y*365*24+m*30*24+d*24+h } //pattern guards let oneYear = Year(1.0) //for solution 1 var howManyHours: Float = match oneYear { with Year(y) if y > 0 => y*365*24 with Year(y) if y <= 0 => 0.0 with Month(y,m) if y > 0 && m > 0 => y*365*24+m*30*24 with Day(y,m,d) if y > 0 && m > 0 && d > 0 => y*365*24+m*30*24+d*24 with Hour(y,m,d,h) if y > 0 && m > 0 && d > 0 && h > 0 => y*365*24+m*30*24+d*24+h } //other places using pattern matching //using tuple pattern let point = (10,20) let (x,y) = point //using enum pattern let hours = Hour(1.0, 2.4, 3.5, 4.6) let Hour(year, month, day, hour) = hours //using pattern in for-in loop main() { let scoreList: List = {10,20,30,40,50,60,70,80,90,100} for item in scoreList { // variable&in pattern print(item) } for item in scoreList if item > 50 { // variable&in pattern with guards print(item) } for _ in scoreList { // wildcard&in pattern print(item) } } //using match expression instead of if-else if let score = 80 var result: String = match { with score < 60 => "fail" with score < 70 => "pass" with score < 90 => "good" with _ => "excellent" } //from positiveAndNegativeOps var i: Int = 5 var j: Int = +i // j=5 var k: Int = -i // k=-5 //from primaryTypes.char let int8bit: Int8 = 24 //the range of Int8 is: -2**7 ~ 2**7-1 (-128 ~ 127) let int16bit: Int16 = 529 //the range of Int16 is: -2**15 ~ 2**15-1 (-32768 ~ 32767) let intShort: Short = 529 //the range of Short(Int16) is: -2**15 ~ 2**15-1 (-32768 ~ 32767) let int32bit: Int32 = 58041 //the range of Int32 is: -2**31 ~ 2**31-1 (-2147483648~2147483647) let intInt: Int = 58041 //the range of Int(Int32) is: -2**31 ~ 2**31-1 (-2147483648~2147483647) let int64bit: Int64 = 6000000612 let intLong: Long = 6000000612 let uint8bit: UInt8 = 128 //the range of UInt8 is: 0 ~ 2**8-1 (0 ~ 255) let uint16bit: UInt16 = 529 //the range of UInt16 is: 0 ~ 2**16-1 (0 ~ 65535) let uint32bit: UInt32 = 58041 //the range of UInt32 is: 0 ~ 2**32-1 (0~4294967295) let uintInt: UInt = 58041 //the range of UInt(UInt32) is: 0 ~ 2**32-1 (0~4294967295) let uint64bit: UInt64 = 6000000612 //the range of UInt64 is: 0 ~ 2**64-1 (0~18446744073709549615) let float16bit: Float16 = 3.1415E0 let float32bit: Float32 = 1.234E10 let floatfloat: Float = 1.234E10 let float64bit: Float64 = 1.234E100 let floatdouble: Double = 1.234E100 let char: Rune = 'a' let booltrue: Boolean = true let boolfalse: Boolean = false //from range.char let closedRange: ClosedRange = 0:10 // define closed range [1,10] let oneSideRange: OneSideRange = 1: // define one side range [1, 2147483647] for (index in 0:3) { // anonymous closed range is used array[index]=0 // array[0]=array[1]= array[2]= array[3]=0 } let closedRange1: ClosedRange = 0:10 let closedRange2: ClosedRange = 1:10 closedRange1 == closedRange2 ? closedRange1:closedRange2 //return closedRange2 closedRange1.length //return 11 closedRange2.length //return 10 closedRange2.contains(0) //return false closedRange1.isEmpty() //return false let array[8]:Int = [1,2,3,4,5,6,7,8] for (number in array[0:]){ //anonymous one side range is used print(number) //output: 1 2 3 4 5 6 7 8 } //from semicolon.char let width:Int = 32 let length:Int = 1024; let width2:Int = 32; let length2:Int=1024 //from simpleAndCompoundStatements var x :Int = 5 x+=10 x+=20; x+=30; x+=40 //null statement ; //null statement while(read(i) && i <= 10){ //read a number that greater than 10 ; //null statement } var x = 24 if ( x > 0) { x = 1 print("x is bigger than 0, let it be 1") } else { x = 0 print("x is not bigger than 0, let it be 0") } //from string.char let string1: String = "Hello, " let string2 = "Rune" var string3 = string1 + string2 print(string3) //output: Hello, Rune print(string3.length) //output: 11 //from tuple.char let tuplePIE: Tuple = (3.14, "PIE") //define read-only Tuple tuple1=(3.14, "PIE") var Point: Tuple //define writable Tuple Point Point = (2.4, 3.5) //initialization var (x, y) = Point // define an anonymous tuple var point1: Tuple = (2.4, 3.5) var point2: Tuple = (8, 10) point1 == point2 ? point1:point2 //return point2 point2.set(0, 10) //point2=(10,10) var rectangleArea = point2.get(0)*point2.get(1) //rectangleArea=100 point2.length //return 2 point2.contains(100) //return false point2.isEmpty() //return false //from typeAlias type pointHas5dims = Tuple type families = Set //from typeCast var intNumber: Int = 1024 intNumber.toInt8() intNumber.toInt16() intNumber.toShort() intNumber.toInt64() intNumber.toLong() intNumber.toFloat16() intNumber.toFloat32() intNumber.toFloat() intNumber.toFloat64() intNumber.toDouble() intNumber.toUInt8() intNumber.toUInt16() intNumber.toUInt32() intNumber.toUInt() intNumber.toUInt64() intNumber.toChar() var floatNumber: Float = 10.24 floatNumber.toInt8() floatNumber.toInt16() floatNumber.toShort() floatNumber.toInt32() floatNumber.toInt() floatNumber.toInt64() floatNumber.toLong() floatNumber.toFloat16() floatNumber.toFloat64() floatNumber.toDouble() floatNumber.toUInt8() floatNumber.toUInt16() floatNumber.toUInt32() floatNumber.toUInt() floatNumber.toUInt64() var character: Rune = 24 floatNumber.toInt8() floatNumber.toInt16() floatNumber.toShort() floatNumber.toInt32() floatNumber.toInt() floatNumber.toInt64() floatNumber.toLong() floatNumber.toUInt8() floatNumber.toUInt16() floatNumber.toUInt32() floatNumber.toUInt() floatNumber.toUInt64() //from UnitType func helloArkLang1(ArkLang: String) : Unit { //return Unit return a // error, should be return Unit } func helloArkLang2(ArkLang: String) { return a // ok, the return type of helloArkLang2 is the type of a } func helloArkLang3(ArkLang: String) { return // the return type of helloArkLang3 is Unit } func helloArkLang4(ArkLang: String) { //... //if no return statement, the return type of helloArkLang4 is Unit } //from unsafeOps let a: Int = 2147483647 //INT_MAX let b: Int = 1 let c: Int = b + a //error, overflow is reported by compiler let d: Int = b +& a //ok, but c=-2147483648 a +& b //unsafe add a -& b //unsafe sub a *& b //unsafe multi a **& b //unsafe power a +&= b //unsafe compoundAssignOperators a -&= b a *&= b a **&= b } )"; std::unique_ptr lexer; SourceManager sm; DiagnosticEngine diag{}; // When constructor of DiagnosticEngine update, // maybe should update here. }; TEST_F(TotalTest, AllTokens) { for (;;) { Token tok = lexer->Next(); if (tok.kind == TokenKind::END) { break; } } } cangjie_compiler-1.0.7/unittests/Macro/000077500000000000000000000000001510705540100201125ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Macro/CMakeLists.txt000066400000000000000000000026261510705540100226600ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. include_directories(${CMAKE_SOURCE_DIR}/src/Sema) if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_executable(TokenSerializationTest TokenSerializationTest.cpp) add_executable(NodeSerializationTest NodeSerializationTest.cpp) add_executable(MacroTest MacroTest.cpp) target_link_libraries( TokenSerializationTest cangjie-lsp ${LINK_LIBS} boundscheck-static GTest::gtest GTest::gtest_main) target_link_libraries( NodeSerializationTest cangjie-lsp ${LINK_LIBS} boundscheck-static GTest::gtest GTest::gtest_main) target_link_libraries( MacroTest cangjie-lsp ${LINK_LIBS} boundscheck-static GTest::gtest GTest::gtest_main TestCompilerInstanceObject) add_dependencies(NodeSerializationTest CangjieFlatbuffersHeaders) target_include_directories(NodeSerializationTest PRIVATE ${FLATBUFFERS_INCLUDE_DIR}) add_test(NAME TokenSerializationTest COMMAND TokenSerializationTest) add_test(NAME NodeSerializationTest COMMAND NodeSerializationTest) add_test(NAME MacroTest COMMAND MacroTest) endif() cangjie_compiler-1.0.7/unittests/Macro/MacroTest.cpp000066400000000000000000000446061510705540100225310ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include "gtest/gtest.h" #include "TestCompilerInstance.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Walker.h" #ifdef _WIN32 #include #endif using namespace Cangjie; using namespace AST; class MacroTest : public testing::Test { protected: void SetUp() override { #ifdef _WIN32 srcPath = projectPath + "\\unittests\\Macro\\srcFiles\\"; definePath = srcPath + "define\\"; #else srcPath = projectPath + "/unittests/Macro/srcFiles/"; definePath = srcPath + "define/"; #endif #ifdef __x86_64__ invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::X86_64; #else invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::AARCH64; #endif #ifdef _WIN32 invocation.globalOptions.target.os = Cangjie::Triple::OSType::WINDOWS; invocation.globalOptions.executablePath = projectPath + "\\output\\bin"; #elif __unix__ invocation.globalOptions.target.os = Cangjie::Triple::OSType::LINUX; invocation.globalOptions.executablePath = projectPath + "/output/bin"; #endif invocation.globalOptions.importPaths = {definePath}; } #ifdef PROJECT_SOURCE_DIR // Gets the absolute path of the project from the compile parameter. std::string projectPath = PROJECT_SOURCE_DIR; #else // Just in case, give it a default value. // Assume the initial is in the build directory. std::string projectPath = ".."; #endif std::string srcPath; std::string definePath; DiagnosticEngine diag; CompilerInvocation invocation; std::unique_ptr instance; }; TEST_F(MacroTest, DISABLED_MacroProcess_Curfile) { auto src = srcPath + "func.cj"; instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; instance->srcFilePaths = {src}; instance->Compile(CompileStage::PARSE); // Test mapping curfile in macro expansion. for (auto& decl : instance->GetSourcePackages()[0]->files[0]->decls) { if (auto med = AST::As(decl.get()); med) { med->invocation.newTokens = med->invocation.args; } } instance->PerformMacroExpand(); EXPECT_EQ(diag.GetErrorCount(), 0); } TEST_F(MacroTest, DISABLED_MacroCall_GetNewPos) { auto src = srcPath + "func_not_annotation.cj"; invocation.globalOptions.enableMacroInLSP = true; instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; instance->srcFilePaths = {src}; instance->Compile(CompileStage::PARSE); auto file = instance->GetSourcePackages()[0]->files[0].get(); // Test GetNewPos. for (auto& decl : file->decls) { if (auto med = AST::As(decl.get()); med) { med->invocation.newTokens = med->invocation.args; auto& tok = med->invocation.newTokens[0]; tok.SetValuePos(tok.Value(), tok.Begin() + 1, tok.End() + 1); med->invocation.newTokensStr = "func test():Unit\n {\n return }"; } } instance->PerformMacroExpand(); for (auto& decl : file->decls) { if (!decl->TestAttr(Attribute::MACRO_EXPANDED_NODE)) { continue; } auto macrocall = decl->curMacroCall; if (auto fd = AST::As(decl.get()); fd && macrocall) { // Given a position which could be 't'{1, 4, 6}, 'e'{1, 4, 7}, 's'{1, 4, 8}, 't'{1, 4, 9}, auto srcPos = Position{1, 4, 8}; // Get the new begin position of identifier "test" after @M. auto newPos = macrocall->GetMacroCallNewPos(srcPos); EXPECT_EQ(newPos.fileID, 1); EXPECT_EQ(newPos.line, 3); // Get the original begin position of identifier "test" in func.cj. auto pos = decl->GetMacroCallPos(newPos); auto dstPos = Position{1, 4, 6}; ASSERT_TRUE(pos == dstPos); // Given a position {1, 3, 3} after @M, get an INVALID_POSITION. srcPos = Position{1, 3, 3}; newPos = macrocall->GetMacroCallNewPos(srcPos); ASSERT_TRUE(newPos == INVALID_POSITION); } } // error: undeclared identifier 'M' EXPECT_EQ(diag.GetErrorCount(), 1); } TEST_F(MacroTest, MacroCall_GetEndPos) { auto src = srcPath + "var.cj"; instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; instance->srcFilePaths = {src}; instance->Compile(CompileStage::PARSE); auto file = instance->GetSourcePackages()[0]->files[0].get(); // Test GetEndPos. for (auto& decl : file->decls) { if (auto med = AST::As(decl.get()); med) { med->invocation.newTokens = med->invocation.args; auto& tok = med->invocation.newTokens[1]; tok.SetValuePos(tok.Value(), tok.Begin() + 1, tok.End() + 1); med->invocation.newTokensStr = "var a = 1"; } } instance->PerformMacroExpand(); EXPECT_EQ(diag.GetErrorCount(), 0); } TEST_F(MacroTest, MacroCall_Complementatcion_For_LSP) { invocation.globalOptions.enableMacroInLSP = true; instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; instance->srcFilePaths = {srcPath + "class.cj"}; instance->Compile(CompileStage::PARSE); auto file = instance->GetSourcePackages()[0]->files[0].get(); // Simulation scenario: macrocall M1 expand success. std::function)> visitPre1 = [&](Ptr curNode) -> VisitAction { if (curNode->astKind == ASTKind::MACRO_EXPAND_DECL) { auto med = StaticAs(curNode); if (med->invocation.identifier == "M1") { med->invocation.newTokens = med->invocation.attrs; med->invocation.newTokens.emplace_back(Token(TokenKind::SEMI, ";")); med->invocation.newTokens.insert( med->invocation.newTokens.end(), med->invocation.args.begin(), med->invocation.args.end()); med->invocation.newTokensStr = "class Ca4{};class Ca5{\n var ab = 3\n Ca5(x:Int64){\n this.ab\n }\n}"; } return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; Walker walker1(file, visitPre1); walker1.Walk(); instance->PerformMacroExpand(); // Test MacroCall Complementation for lsp. instance->PerformImportPackage(); instance->PerformSema(); std::function)> visitPre2 = [&](Ptr curNode) -> VisitAction { if (curNode->astKind == ASTKind::FILE) { auto file = StaticAs(curNode); for (auto& it : file->originalMacroCallNodes) { Walker(it.get(), visitPre2).Walk(); } } return VisitAction::WALK_CHILDREN; }; Walker walker2(file, visitPre2); walker2.Walk(); EXPECT_EQ(diag.GetErrorCount(), 1); } TEST_F(MacroTest, DISABLED_MacroCall_Check_For_LSP) { Cangjie::ICE::TriggerPointSetter iceSetter(static_cast(Cangjie::ICE::UNITTEST_TP)); auto defInstance = std::make_unique(invocation, diag); defInstance->compileOnePackageFromSrcFiles = true; defInstance->srcFilePaths = {definePath + "define.cj", definePath + "define2.cj"}; defInstance->Compile(); diag.Reset(); std::vector astData; defInstance->importManager.ExportAST(false, astData, *defInstance->GetSourcePackages()[0]); std::string astFile = definePath + "define.cjo"; ASSERT_TRUE(FileUtil::WriteBufferToASTFile(astFile, astData)); invocation.globalOptions.enableMacroInLSP = true; instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; instance->srcFilePaths = {srcPath + "func_arg.cj"}; instance->Compile(CompileStage::PARSE); auto file = instance->GetSourcePackages()[0]->files[0].get(); // Simulation scenario: macrocall B1 expand success, macrocall B2 expand failed. std::function)> visitPre1 = [&](Ptr curNode) -> VisitAction { if (curNode->astKind == ASTKind::MACRO_EXPAND_EXPR) { auto mee = StaticAs(curNode); if (mee->invocation.identifier == "B1") { mee->invocation.newTokens = mee->invocation.args; mee->invocation.newTokensStr = "6"; } return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; Walker walker1(file, visitPre1); walker1.Walk(); // Test MacroCall target for lsp. instance->PerformImportPackage(); instance->PerformMacroExpand(); instance->PerformSema(); std::function)> visitPre2 = [&](Ptr curNode) -> VisitAction { if (curNode->astKind == ASTKind::FILE) { auto file = StaticAs(curNode); for (auto& it : file->originalMacroCallNodes) { Walker(it.get(), visitPre2).Walk(); } } if (curNode->astKind == ASTKind::MACRO_EXPAND_EXPR) { // Targets can be found for both failed and successful macrocalls. auto mee = StaticAs(curNode); EXPECT_TRUE(mee->invocation.target); auto fileID = mee->invocation.target->begin.fileID; auto path = instance->GetSourceManager().GetSource(fileID).path; if (mee->invocation.identifier == "B1") { #ifdef _WIN32 EXPECT_EQ(path, "define\\define.cj"); #else EXPECT_EQ(path, "define/define.cj"); #endif } if (mee->invocation.identifier == "B2") { #ifdef _WIN32 EXPECT_EQ(path, "define\\define2.cj"); #else EXPECT_EQ(path, "define/define2.cj"); #endif } return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; Walker walker2(file, visitPre2); walker2.Walk(); // error: macro evaluation has failed for macro call 'B1' // error: macro evaluation has failed for macro call 'B2' EXPECT_EQ(diag.GetErrorCount(), 2); } TEST_F(MacroTest, DISABLED_MacroCall_Check_For_LSP_Paralle) { Cangjie::ICE::TriggerPointSetter iceSetter(static_cast(Cangjie::ICE::UNITTEST_TP)); auto defInstance = std::make_unique(invocation, diag); defInstance->compileOnePackageFromSrcFiles = true; #ifdef _WIN32 invocation.globalOptions.macroLib.emplace_back("\\"); #else invocation.globalOptions.macroLib.emplace_back("./"); #endif defInstance->srcFilePaths = {definePath + "define.cj", definePath + "define2.cj"}; defInstance->Compile(); diag.Reset(); std::vector astData; defInstance->importManager.ExportAST(false, astData, *defInstance->GetSourcePackages()[0]); std::string astFile = definePath + "define.cjo"; ASSERT_TRUE(FileUtil::WriteBufferToASTFile(astFile, astData)); invocation.globalOptions.enableMacroInLSP = true; invocation.globalOptions.enableParallelMacro = true; instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; instance->srcFilePaths = {srcPath + "func_arg.cj"}; instance->Compile(CompileStage::PARSE); auto file = instance->GetSourcePackages()[0]->files[0].get(); // Simulation scenario: macrocall B1 expand success, macrocall B2 expand failed. std::function)> visitPre1 = [&](Ptr curNode) -> VisitAction { if (curNode->astKind == ASTKind::MACRO_EXPAND_EXPR) { auto mee = StaticAs(curNode); if (mee->invocation.identifier == "B1") { mee->invocation.newTokens = mee->invocation.args; mee->invocation.newTokensStr = "6"; } return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; Walker walker1(file, visitPre1); walker1.Walk(); // Test MacroCall target for lsp. instance->PerformImportPackage(); instance->PerformMacroExpand(); instance->PerformSema(); std::function)> visitPre2 = [&](Ptr curNode) -> VisitAction { if (curNode->astKind == ASTKind::FILE) { auto file = StaticAs(curNode); for (auto& it : file->originalMacroCallNodes) { Walker(it.get(), visitPre2).Walk(); } } if (curNode->astKind == ASTKind::MACRO_EXPAND_EXPR) { // Targets can be found for both failed and successful macrocalls. auto mee = StaticAs(curNode); EXPECT_TRUE(mee->invocation.target); auto fileID = mee->invocation.target->begin.fileID; auto path = instance->GetSourceManager().GetSource(fileID).path; if (mee->invocation.identifier == "B1") { #ifdef _WIN32 EXPECT_EQ(path, "define\\define.cj"); #else EXPECT_EQ(path, "define/define.cj"); #endif } if (mee->invocation.identifier == "B2") { #ifdef _WIN32 EXPECT_EQ(path, "define\\define2.cj"); #else EXPECT_EQ(path, "define/define2.cj"); #endif } return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; Walker walker2(file, visitPre2); walker2.Walk(); // error: macro evaluation has failed for macro call 'B1' // error: macro evaluation has failed for macro call 'B2' // error: Cannot dlopen from the dynamic library auto errSize = 3; EXPECT_EQ(diag.GetErrorCount(), errSize); } #ifndef _WIN32 TEST_F(MacroTest, DISABLED_IfAvailable_In_LSP) { auto src = srcPath + "test_IfAvailable_LSP.cj"; invocation.globalOptions.enableMacroInLSP = true; invocation.globalOptions.enableParallelMacro = true; invocation.globalOptions.executablePath = projectPath + "/output/bin/"; instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; instance->srcFilePaths = {src}; instance->Compile(CompileStage::SEMA); Cangjie::MacroProcMsger::GetInstance().CloseMacroSrv(); } TEST_F(MacroTest, DISABLED_MacroCall_Check_For_LSP_context) { std::string command = "cd " + definePath + " && cjc define_childMessage.cj --compile-macro"; int err = system(command.c_str()); ASSERT_EQ(0, err); auto src = srcPath + "test_macroWithContext.cj"; invocation.globalOptions.enableMacroInLSP = true; invocation.globalOptions.enableParallelMacro = true; invocation.globalOptions.executablePath = projectPath + "/output/bin/"; instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; instance->srcFilePaths = {src}; instance->Compile(CompileStage::SEMA); Cangjie::MacroProcMsger::GetInstance().CloseMacroSrv(); } TEST_F(MacroTest, DISABLED_MacroDiagReportForLsp) { std::string command = "cd " + definePath + " && cjc define_report.cj --compile-macro"; int err = system(command.c_str()); ASSERT_EQ(0, err); err = system("echo $CANGJIE_HOME && echo $LD_LIBRARY_PATH && echo $PATH"); ASSERT_EQ(0, err); instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.enableMacroInLSP = true; invocation.globalOptions.executablePath = projectPath + "/output/bin/"; instance->compileOnePackageFromSrcFiles = true; Warningln("exe path ", invocation.globalOptions.executablePath); instance->srcFilePaths = {srcPath + "func_report.cj"}; invocation.globalOptions.outputMode = GlobalOptions::OutputMode::STATIC_LIB; invocation.globalOptions.enableCompileTest = true; instance->Compile(CompileStage::SEMA); EXPECT_EQ(diag.GetErrorCount(), 1); Cangjie::MacroProcMsger::GetInstance().CloseMacroSrv(); } TEST_F(MacroTest, DISABLED_NoErrorInLSPMacro) { std::string command = "cd " + definePath + " && cjc define.cj --compile-macro"; int err = system(command.c_str()); ASSERT_EQ(0, err); auto src = srcPath + "derive_enum.cj"; invocation.globalOptions.enableMacroInLSP = true; invocation.globalOptions.executablePath = projectPath + "/output/bin/"; instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; instance->srcFilePaths = {src}; instance->Compile(CompileStage::SEMA); EXPECT_EQ(diag.GetErrorCount(), 0); Cangjie::MacroProcMsger::GetInstance().CloseMacroSrv(); } TEST_F(MacroTest, DISABLED_MacroCall_HighLight_LSP) { std::string command = "cd " + definePath + " && cjc define3.cj --compile-macro"; int err = system(command.c_str()); ASSERT_EQ(0, err); auto src = srcPath + "test_highlight.cj"; invocation.globalOptions.executablePath = projectPath + "/output/bin/"; instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; instance->srcFilePaths = {src}; instance->Compile(CompileStage::SEMA); auto file = instance->GetSourcePackages()[0]->files[0].get(); for (auto& decl : file->decls) { if (!decl->TestAttr(Attribute::MACRO_EXPANDED_NODE)) { continue; } auto macrocall = decl->curMacroCall; if (auto cd = AST::As(decl.get()); cd && macrocall) { auto newTks = macrocall->GetInvocation()->newTokens; // class A ASSERT_TRUE(macrocall->GetMacroCallNewPos(Position{1, 6, 7}).isCurFile); ASSERT_EQ(macrocall->GetMacroCallNewPos(Position{1, 6, 7}), (Position{1, 4, 20})); // var a auto pos = Position{1, 8, 9}; ASSERT_TRUE(macrocall->GetMacroCallNewPos(Position{1, 8, 9}).isCurFile); ASSERT_EQ(macrocall->GetMacroCallNewPos(Position{1, 8, 9}), (Position{1, 4, 40})); // identifier ttt, define in macro Rename ASSERT_EQ(newTks[15].Value(), "ttt"); } } } #endif cangjie_compiler-1.0.7/unittests/Macro/NodeSerializationTest.cpp000066400000000000000000000351001510705540100251000ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include "gtest/gtest.h" #include "cangjie/Macro/NodeSerialization.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Parse/Parser.h" using namespace Cangjie; using namespace AST; using namespace NodeSerialization; class NodeSerializationTest : public testing::Test { protected: void SetUp() override { binaryExpr = R"(2 * (3 + 4))"; unaryExpr = R"(!2)"; varDecl = R"(var a = 2 + 3)"; funcDecl = R"( func MyComponent(aa: Int32, bb: Int32) : Int64 { // aa, bb has no meaning at all, just for testing var counter = @M(0) // MacroExpandExpr MyFoo() let b: Int64 = 3 * (2 + 4 - 2) MyBar(1, 2) // CallExpr with args var c : Int64 c = b + 2020 // AssignExpr let d: VArray = VArray({i => i}) // VArrayType and ConstantType and ArrayExpr var e: (Int32) = 1 // ParenType let f = quote(a == b) // QuoteExpr return c // ReturnExpr } )"; structDecl = R"( @Differentiable[except: [in_channels_, out_channels_, has_bias_, activation_]] public struct Dense where T <: Evaluable { // GenericConstraint var in_channels_: Int32 var out_channels_: Int32 var has_bias_: Bool var activation_: ActivationType var weight_: Tensor var bias_: Tensor } )"; classDecl = R"( class Data <: unittest.TestCases { // QualifiedType var a : Int32 var b : Float32 var c : Int32 = denseObj.in_channels_ // MemberAccess : RefExpr.field public func get(a: () -> Unit) : Int32 { // FuncType synchronized(m) { foo() } // SynchronizedExpr return 1 } @M var d : Float32 // MacroExpandDecl type Class1 = GenericClassA // TypeAliasDecl func f(): This { // ThisType this } } )"; interfaceDecl = R"( interface MyInterface { func foo() {} } )"; ifExpr = R"( if (a) { // a > 0 return 1 } else { var a = 2021 var b = a + 1 let x = Int64.foo // PrimitiveTypeExpr return -1 } )"; lambdaExpr = R"( {a: Int32, b: Int32 => a + b} )"; } std::string binaryExpr; std::string unaryExpr; std::string varDecl; std::string funcDecl; std::string structDecl; std::string classDecl; std::string interfaceDecl; std::string ifExpr; std::string lambdaExpr; }; TEST_F(NodeSerializationTest, BinaryExpr_Serialization) { SourceManager sm; sm.AddSource("./", binaryExpr); DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser binaryExprParser{binaryExpr, diag, sm}; // binaryExpr: 2 * (3 + 4) // litConstExpr and parenExpr are also tested // Check specific offset of BinaryExpr using flatc generated methods. auto ptr = binaryExprParser.ParseExpr(); NodeWriter binaryExprWriter(ptr.get()); uint8_t* buffer = binaryExprWriter.ExportNode(); buffer = buffer + 4; // move the pointer to the real position of the flatbuffer auto fbNode = NodeFormat::GetNode(buffer); EXPECT_EQ(fbNode->root_type(), NodeFormat::AnyNode_EXPR); auto fbExpr = fbNode->root_as_EXPR(); EXPECT_EQ(fbExpr->expr_type(), NodeFormat::AnyExpr_BINARY_EXPR); auto fbBianryExpr = fbExpr->expr_as_BINARY_EXPR(); auto leftExpr = fbBianryExpr->left_expr(); EXPECT_EQ(leftExpr->expr_type(), NodeFormat::AnyExpr_LIT_CONST_EXPR); auto leftExprStr = fbBianryExpr->left_expr()->expr_as_LIT_CONST_EXPR()->literal(); EXPECT_EQ(leftExprStr->str(), "2"); auto rightExpr = fbBianryExpr->right_expr(); EXPECT_EQ(rightExpr->expr_type(), NodeFormat::AnyExpr_PAREN_EXPR); auto onlyExpr = rightExpr->expr_as_PAREN_EXPR(); EXPECT_EQ(onlyExpr->expr()->expr_type(), NodeFormat::AnyExpr_BINARY_EXPR); auto onlyExprAsBinary = onlyExpr->expr()->expr_as_BINARY_EXPR(); auto parenLeftExprStr = onlyExprAsBinary->left_expr()->expr_as_LIT_CONST_EXPR()->literal(); auto parenRightExprStr = onlyExprAsBinary->right_expr()->expr_as_LIT_CONST_EXPR()->literal(); EXPECT_EQ(parenLeftExprStr->str(), "3"); EXPECT_EQ(parenRightExprStr->str(), "4"); } TEST_F(NodeSerializationTest, UnaryExpr_Serialization) { SourceManager sm; sm.AddSource("./", unaryExpr); DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser unaryExprParser{unaryExpr, diag, sm}; // unaryExpr: !2 auto ptr = unaryExprParser.ParseExpr(); NodeWriter unaryExprWriter(ptr.get()); uint8_t* buffer = unaryExprWriter.ExportNode(); buffer = buffer + 4; // move the pointer to the real position of the flatbuffer auto fbNode = NodeFormat::GetNode(buffer); EXPECT_EQ(fbNode->root_type(), NodeFormat::AnyNode_EXPR); EXPECT_EQ(fbNode->root_as_EXPR()->expr_type(), NodeFormat::AnyExpr_UNARY_EXPR); auto fbUnaryExpr = fbNode->root_as_EXPR()->expr_as_UNARY_EXPR(); auto onlyExpr = fbUnaryExpr->expr()->expr_as_LIT_CONST_EXPR()->literal(); EXPECT_EQ(onlyExpr->str(), "2"); } TEST_F(NodeSerializationTest, VarDecl_Serialization) { SourceManager sm; sm.AddSource("./", varDecl); DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser varDeclParser{varDecl, diag, sm}; // varDecl: var a = 2 + 3 auto ptr = varDeclParser.ParseDecl(ScopeKind::FUNC_BODY); NodeWriter varDeclWriter(ptr.get()); uint8_t* buffer = varDeclWriter.ExportNode(); buffer = buffer + 4; // move the pointer to the real position of the flatbuffer auto fbNode = NodeFormat::GetNode(buffer); EXPECT_EQ(fbNode->root_type(), NodeFormat::AnyNode_DECL); EXPECT_EQ(fbNode->root_as_DECL()->decl_type(), NodeFormat::AnyDecl_VAR_DECL); auto fbVarDecl = fbNode->root_as_DECL()->decl_as_VAR_DECL(); EXPECT_EQ(fbVarDecl->is_var(), true); EXPECT_EQ(fbVarDecl->base()->identifier()->str(), "a"); EXPECT_EQ(fbVarDecl->initializer()->expr_type(), NodeFormat::AnyExpr_BINARY_EXPR); } TEST_F(NodeSerializationTest, FuncDecl_Serialization) { SourceManager sm; sm.AddSource("./", funcDecl); DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser funcDeclParser{funcDecl, diag, sm}; auto ptr = funcDeclParser.ParseDecl(ScopeKind::TOPLEVEL); NodeWriter funcDeclWriter(ptr.get()); uint8_t* buffer = funcDeclWriter.ExportNode(); buffer = buffer + 4; // move the pointer to the real position of the flatbuffer auto fbNode = NodeFormat::GetNode(buffer); EXPECT_EQ(fbNode->root_type(), NodeFormat::AnyNode_DECL); EXPECT_EQ(fbNode->root_as_DECL()->decl_type(), NodeFormat::AnyDecl_FUNC_DECL); auto fbFuncDecl = fbNode->root_as_DECL()->decl_as_FUNC_DECL(); EXPECT_EQ(fbFuncDecl->base()->identifier()->str(), "MyComponent"); auto fbFuncBody = fbFuncDecl->func_body(); std::vector expectIdVec{"aa", "bb"}; std::vector realIdVec; auto fbParams = fbFuncBody->param_list()->params(); // flatbuffers::Vector for (size_t i = 0; i < fbParams->size(); ++i) { // Test parameters auto fbParam = fbParams->Get(i); auto paramId = fbParam->base()->base()->identifier()->str(); realIdVec.push_back(paramId); } EXPECT_EQ(realIdVec, expectIdVec); auto fbFuncBlock = fbFuncBody->body()->body(); std::vector expectEnumVec{NodeFormat::AnyNode_DECL, NodeFormat::AnyNode_EXPR, NodeFormat::AnyNode_DECL, NodeFormat::AnyNode_EXPR, NodeFormat::AnyNode_DECL, NodeFormat::AnyNode_EXPR, NodeFormat::AnyNode_DECL, NodeFormat::AnyNode_DECL, NodeFormat::AnyNode_DECL, NodeFormat::AnyNode_EXPR}; std::vector realEnumVec; for (size_t i = 0; i < fbFuncBlock->size(); ++i) { auto fbBlockNode = fbFuncBlock->Get(i); auto fbNodeType = fbBlockNode->root_type(); realEnumVec.push_back(fbNodeType); } EXPECT_EQ(expectEnumVec, realEnumVec); } TEST_F(NodeSerializationTest, ClassDecl_Serialization) { SourceManager sm; sm.AddSource("./", classDecl); DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser classDeclParser{classDecl, diag, sm}; auto ptr = classDeclParser.ParseDecl(ScopeKind::TOPLEVEL); NodeWriter classDeclWriter(ptr.get()); uint8_t* buffer = classDeclWriter.ExportNode(); buffer = buffer + 4; // point to the real pos of buffer auto fbNode = NodeFormat::GetNode(buffer); EXPECT_EQ(fbNode->root_type(), NodeFormat::AnyNode_DECL); EXPECT_EQ(fbNode->root_as_DECL()->decl_type(), NodeFormat::AnyDecl_CLASS_DECL); // test class name auto fbClassDecl = fbNode->root_as_DECL()->decl_as_CLASS_DECL(); EXPECT_EQ(fbClassDecl->base()->identifier()->str(), "Data"); // test class body auto fbClassBody = fbClassDecl->body()->decls(); std::vector expectDeclType{NodeFormat::AnyDecl_VAR_DECL, NodeFormat::AnyDecl_VAR_DECL, NodeFormat::AnyDecl_VAR_DECL, NodeFormat::AnyDecl_FUNC_DECL, NodeFormat::AnyDecl_MACRO_EXPAND_DECL, NodeFormat::AnyDecl_TYPE_ALIAS_DECL, NodeFormat::AnyDecl_FUNC_DECL}; std::vector realDeclType; std::vector expectDeclId{"a", "b", "c", "get", "M", "", "f"}; std::vector realDeclId; for (size_t i = 0; i < fbClassBody->size(); i++) { auto fbDecl = fbClassBody->Get(i); auto declType = fbDecl->decl_type(); realDeclType.push_back(declType); std::string id; if (declType == NodeFormat::AnyDecl_VAR_DECL) { auto fbVarDecl = fbDecl->decl_as_VAR_DECL(); id = fbVarDecl->base()->identifier()->str(); } if (declType == NodeFormat::AnyDecl_FUNC_DECL) { auto fbFuncDecl = fbDecl->decl_as_FUNC_DECL(); id = fbFuncDecl->base()->identifier()->str(); } if (declType == NodeFormat::AnyDecl_MACRO_EXPAND_DECL) { auto fbFuncDecl = fbDecl->decl_as_MACRO_EXPAND_DECL(); id = fbFuncDecl->base()->identifier()->str(); } realDeclId.push_back(id); } EXPECT_EQ(realDeclType, expectDeclType); EXPECT_EQ(realDeclId, expectDeclId); auto VarMemAcc = fbClassBody->Get(2)->decl_as_VAR_DECL(); auto initExpr = VarMemAcc->initializer(); EXPECT_EQ(initExpr->expr_type(), NodeFormat::AnyExpr_MEMBER_ACCESS); auto memAccExpr = initExpr->expr_as_MEMBER_ACCESS(); auto firstPart = memAccExpr->base_expr()->expr_as_REF_EXPR()->ref()->identifier()->str(); auto secondPart = memAccExpr->field()->str(); EXPECT_EQ(firstPart, "denseObj"); EXPECT_EQ(secondPart, "in_channels_"); // test superClassType, interfaceType, sub_decls // test generic } TEST_F(NodeSerializationTest, InterfaceDecl_Serialization) { SourceManager sm; sm.AddSource("./", interfaceDecl); DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser interfaceDeclParser{interfaceDecl, diag, sm}; auto ptr = interfaceDeclParser.ParseDecl(ScopeKind::TOPLEVEL); NodeWriter interfaceDeclWriter(ptr.get()); uint8_t* buffer = interfaceDeclWriter.ExportNode(); buffer = buffer + 4; // point to the real pos of buffer auto fbNode = NodeFormat::GetNode(buffer); EXPECT_EQ(fbNode->root_type(), NodeFormat::AnyNode_DECL); EXPECT_EQ(fbNode->root_as_DECL()->decl_type(), NodeFormat::AnyDecl_INTERFACE_DECL); // test interface name auto fbInterfaceDecl = fbNode->root_as_DECL()->decl_as_INTERFACE_DECL(); EXPECT_EQ(fbInterfaceDecl->base()->identifier()->str(), "MyInterface"); } TEST_F(NodeSerializationTest, IfExpr_Serialization) { SourceManager sm; sm.AddSource("./", ifExpr); DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser ifExprParser{ifExpr, diag, sm}; auto ptr = ifExprParser.ParseExpr(); NodeWriter ifExprWriter(ptr.get()); uint8_t* buffer = ifExprWriter.ExportNode(); buffer = buffer + 4; // move the pointer to the real position of the flatbuffer auto fbNode = NodeFormat::GetNode(buffer); EXPECT_EQ(fbNode->root_type(), NodeFormat::AnyNode_EXPR); EXPECT_EQ(fbNode->root_as_EXPR()->expr_type(), NodeFormat::AnyExpr_IF_EXPR); } TEST_F(NodeSerializationTest, LambdaExpr_Serialization) { SourceManager sm; sm.AddSource("./", lambdaExpr); DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser lambdaExprParser{lambdaExpr, diag, sm}; // {a: Int32, b: Int32 => a + b} // For lambaExpr, it's only part of the funcBody. // Some fields of FuncBody are set to default(null) in lambdaExpr auto ptr = lambdaExprParser.ParseExpr(); NodeWriter lambdaWriter(ptr.get()); uint8_t* buffer = lambdaWriter.ExportNode(); buffer = buffer + 4; auto fbNode = NodeFormat::GetNode(buffer); EXPECT_EQ(fbNode->root_type(), NodeFormat::AnyNode_EXPR); EXPECT_EQ(fbNode->root_as_EXPR()->expr_type(), NodeFormat::AnyExpr_LAMBDA_EXPR); // check parameter auto fbLambdaExpr = fbNode->root_as_EXPR()->expr_as_LAMBDA_EXPR(); auto fbLambdaBody = fbLambdaExpr->body(); auto lbdParams = fbLambdaBody->param_list()->params(); std::vector expectParamVec{"a", "b"}; std::vector realParamVec{}; for (size_t i = 0; i < lbdParams->size(); i++) { auto fbParam = lbdParams->Get(i); auto paramId = fbParam->base()->base()->identifier()->str(); realParamVec.push_back(paramId); } EXPECT_EQ(expectParamVec, realParamVec); // check => pos auto arrowPos = fbLambdaBody->arrow_pos(); auto line = arrowPos->line(); auto column = arrowPos->column(); EXPECT_NE(line, 0); EXPECT_NE(column, 0); // check lambda body auto lbdBlock = fbLambdaBody->body()->body(); // flatbuffers::Vector> std::vector expectNodeType{NodeFormat::AnyNode_EXPR}; std::vector realNodeType{}; for (size_t i = 0; i < lbdBlock->size(); i++) { auto fbNode = lbdBlock->Get(i); realNodeType.push_back(fbNode->root_type()); } EXPECT_EQ(expectNodeType, realNodeType); } cangjie_compiler-1.0.7/unittests/Macro/TokenSerializationTest.cpp000066400000000000000000000031331510705540100252740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include "gtest/gtest.h" #include "cangjie/Macro/TokenSerialization.h" #include "cangjie/Lex/Lexer.h" using namespace Cangjie; class TokenSerializationTest : public testing::Test { protected: void SetUp() override { lexer = new Lexer(code, diag, sm); } std::string code = R"( ?: true false main(argc:Int64=1, argv:String) { let a:Int64=-40 let pi:float64=3.14 let alpha=0x1.1p1 let c:char = '\'' // grh /*/**/*/ let d:String = "asdqwe" let b = 2 ** -a print((a+3*b, (a+3) *b)) @abc }; )"; Lexer* lexer; DiagnosticEngine diag{}; SourceManager sm; }; TEST_F(TokenSerializationTest, BufferCase) { std::vector tokens{}; for (;;) { Token tok = lexer->Next(); tokens.emplace_back(tok); if (tok.kind == TokenKind::END) { break; } } std::vector buf = TokenSerialization::GetTokensBytes(tokens); std::vector backTokens = TokenSerialization::GetTokensFromBytes(buf.data()); EXPECT_EQ(tokens.size(), 93); ASSERT_TRUE(tokens.size() == backTokens.size()); for (int i = 0; i < 93; ++i) { EXPECT_EQ(tokens[i].kind, backTokens[i].kind); EXPECT_EQ(tokens[i].Value(), backTokens[i].Value()); EXPECT_EQ(tokens[i].Begin(), backTokens[i].Begin()); } } cangjie_compiler-1.0.7/unittests/Macro/srcFiles/000077500000000000000000000000001510705540100216645ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Macro/srcFiles/class.cj000066400000000000000000000005711510705540100233120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. import define.* @M1[class Ca4 { }](class Ca5{ var ab = 3 Ca5(x:Int64){ this.ab } }) main() {} cangjie_compiler-1.0.7/unittests/Macro/srcFiles/define/000077500000000000000000000000001510705540100231165ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Macro/srcFiles/define/define.cj000066400000000000000000000007461510705540100246750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. macro package define import std.ast.* public macro M(input: Tokens): Tokens { input } public macro M1(attr: Tokens, input: Tokens) { quote($attr;$input) } public macro B1(input: Tokens): Tokens { quote(1;2) } cangjie_compiler-1.0.7/unittests/Macro/srcFiles/define/define2.cj000066400000000000000000000006111510705540100247460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. macro package define public macro M2(input: Tokens): Tokens { input } public macro B2(input: Tokens): Tokens { quote(1) } cangjie_compiler-1.0.7/unittests/Macro/srcFiles/define/define3.cj000066400000000000000000000026061510705540100247550ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. macro package HighLight.def import std.ast.* public macro M(input: Tokens): Tokens { input } public macro addFunc(input: Tokens): Tokens { var d = parseDecl(input) var cd = match (d as ClassDecl) { case Some(v) => v case None => throw ParseASTException() } let fd = FuncDecl(quote(func foo() {})) cd.body.decls.add(fd) return cd.toTokens() } public macro addProp(input: Tokens): Tokens { var d = parseDecl(input) var vd = match (d as VarDecl) { case Some(v) => v case None => throw ParseASTException() } let tks = quote( public mut prop b_: Int32 { get() { return a } set(value) { a = value } }) let pd = PropDecl(tks) pd.declType = vd.declType return (input + Token(NL) + pd.toTokens()) } public macro Rename(input: Tokens): Tokens { var d = parseDecl(input) var vd = match (d as VarDecl) { case Some(v) => v case None => throw ParseASTException() } vd.identifier = Token(IDENTIFIER, "ttt") return vd.toTokens() + input[6..] } cangjie_compiler-1.0.7/unittests/Macro/srcFiles/define/define_childMessage.cj000066400000000000000000000012351510705540100273370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. macro package child import std.ast.* public macro Outer(input: Tokens): Tokens { let messages = getChildMessages("Inner") for (m in messages) { let identName = m.getString("identifierName") println(identName) } return input } public macro Inner(input: Tokens): Tokens { assertParentContext("Outer") setItem("identifierName", "value") return input } cangjie_compiler-1.0.7/unittests/Macro/srcFiles/define/define_report.cj000066400000000000000000000007721510705540100262670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. macro package define import std.ast.* public macro TEST_REPORT(input: Tokens): Tokens { diagReport(DiagReportLevel.ERROR, input, "This expression is not allowed to contain identifier", "Here is the illegal identifier") input } cangjie_compiler-1.0.7/unittests/Macro/srcFiles/derive_enum.cj000066400000000000000000000006201510705540100245020ustar00rootroot00000000000000/* * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. * This source file is part of the Cangjie project, licensed under Apache-2.0 * with Runtime Library Exception. * * See https://cangjie-lang.cn/pages/LICENSE for license information. */ import define.* @M public enum Rotation { F | B } main(){ for (r in [F, Rotation.B]) { println("1111") } }cangjie_compiler-1.0.7/unittests/Macro/srcFiles/func.cj000066400000000000000000000004771510705540100231450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. package a @M func test(): Unit { return } main() {} cangjie_compiler-1.0.7/unittests/Macro/srcFiles/func_arg.cj000066400000000000000000000006521510705540100237710ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. import define.* func asd(x: Int64) {} // Test expand macrocall B1 success. var a = asd(@B1(6)) // Test expand macrocall B2 failed. var b = asd(@B2(6)) main() {} cangjie_compiler-1.0.7/unittests/Macro/srcFiles/func_not_annotation.cj000066400000000000000000000005001510705540100262420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. package a @M( func test():Unit { return }) main() {} cangjie_compiler-1.0.7/unittests/Macro/srcFiles/func_report.cj000066400000000000000000000004751510705540100245360ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. import define.* main() { @TEST_REPORT(let a = 1) } cangjie_compiler-1.0.7/unittests/Macro/srcFiles/test_IfAvailable_LSP.cj000066400000000000000000000010211510705540100261100ustar00rootroot00000000000000/* * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. * This source file is part of the Cangjie project, licensed under Apache-2.0 * with Runtime Library Exception. * * See https://cangjie-lang.cn/pages/LICENSE for license information. */ public class DeviceInfo { public static prop sdkApiVersion: Int64 { get() {19} } } enum Nums { Red | Blue } func f1() { var aa = 11 @IfAvailable(level: 19, {=> var cc = 1 }, {=> var bb = 1 }) } main(){}cangjie_compiler-1.0.7/unittests/Macro/srcFiles/test_highlight.cj000066400000000000000000000007161510705540100252140ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. package HighLight import HighLight.def.* @M @addFunc class A { @M var a: Int64 = 1 @Rename @addProp var b: Int64 = 1 } main(): Int64 { println("hello world") return 0 } cangjie_compiler-1.0.7/unittests/Macro/srcFiles/test_macroWithContext.cj000066400000000000000000000005701510705540100265450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. import child.* @Outer class Demo { @Inner var state = 1 } main(): Int64 { let d = Demo() return 0 } cangjie_compiler-1.0.7/unittests/Macro/srcFiles/var.cj000066400000000000000000000004221510705540100227700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. @M var a = 1 cangjie_compiler-1.0.7/unittests/Modules/000077500000000000000000000000001510705540100204615ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Modules/CMakeLists.txt000066400000000000000000000012561510705540100232250ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. add_executable(PackageTest PackageTest.cpp) target_include_directories(PackageTest PRIVATE ${FLATBUFFERS_INCLUDE_DIR}) target_include_directories(PackageTest PRIVATE ${CMAKE_SOURCE_DIR}/src/IncrementalCompilation) target_link_libraries( PackageTest cangjie-lsp ${LINK_LIBS} boundscheck-static GTest::gtest GTest::gtest_main TestCompilerInstanceObject) add_test(NAME PackageTest COMMAND PackageTest) cangjie_compiler-1.0.7/unittests/Modules/CangjieFiles/000077500000000000000000000000001510705540100230045ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Modules/CangjieFiles/classdecl.cj000066400000000000000000000015521510705540100252620ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. package classdecl // classDecl public open class Base2 { var a: Int32 = 100 init(a: Int32) { this.a = a } init() {} } public class Derive1 <: Base2 { var b: Int32 = 0; } // interfaceDecl + classDecl public interface I3 { func iM1(): Int32 { return 2 } } public open class Base3 <: I3 { var a: Int32 = 10 func cM1(i: Int32): Int32 { return 100 + this.cM2(i) } func cM2(i: T): Int32 { return 10 * a } public override func iM1(): Int32 { return 202 } init(a: Int32) { this.a = a } } cangjie_compiler-1.0.7/unittests/Modules/CangjieFiles/funcdecl.cj000066400000000000000000000007441510705540100251120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. package funcdecl public func fun1() {} public func fun1(a: Int32) {} public func fun2(a: Int32, b: Float32) {} public func fun3(a!: Int32 = 1, b!: Float32 = 2.0, c!: Bool = false) {} public func fun4(a!: Int32 = 1) {} cangjie_compiler-1.0.7/unittests/Modules/CangjieFiles/interfacedecl.cj000066400000000000000000000010201510705540100261030ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. package interfacedecl public interface I1 { func interfaceMethod(x: Int32) { return x } } public interface I2 <: I1 {} private class Base1 <: I2 { var a: Int32 = 100 var b: Int32 = 1 init() { var b = interfaceMethod(10) } } cangjie_compiler-1.0.7/unittests/Modules/CangjieFiles/math.cj000066400000000000000000000005151510705540100242540ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. package math public func plus(a: Int32, b: Int32) { return a + b } cangjie_compiler-1.0.7/unittests/Modules/CangjieFiles/recorddecl.cj000066400000000000000000000007131510705540100254310ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. package recorddecl public struct Rectangle { var width: Int32 var length: Int32 init(width!: Int32, length!: Int32) { this.width = width this.length = length } } cangjie_compiler-1.0.7/unittests/Modules/CangjieFiles/vardecl.cj000066400000000000000000000006311510705540100247420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. package vardecl public var a: Int32 = 1 public var b: Int32 = 1 + 2 public var c: Float32 = 1.0 + 2.0 public var d = true private let e: Int32 = 1 cangjie_compiler-1.0.7/unittests/Modules/ImportPackage/000077500000000000000000000000001510705540100232075ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Modules/ImportPackage/callclass.cj000066400000000000000000000006641510705540100254740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. import classdecl.* func callClass() { let obj = Derive1() return obj.a } func callInterfaceClass() { let obj = Base3(100); return obj.iM1() + obj.a } cangjie_compiler-1.0.7/unittests/Modules/ImportPackage/callfunc.cj000066400000000000000000000005141510705540100253140ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. import math.* import funcdecl.fun1 func callFunc() { plus(1, 2) } cangjie_compiler-1.0.7/unittests/Modules/ImportPackage/callinterface.cj000066400000000000000000000005511510705540100263220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. import interfacedecl.I2 class Base <: I2 { var a: Int32 = 100 var b = interfaceMethod(1) } cangjie_compiler-1.0.7/unittests/Modules/ImportPackage/callrecord.cj000066400000000000000000000005601510705540100256400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. import recorddecl.Rectangle func callRecord() { let a = Rectangle(10, 20) return a.width } cangjie_compiler-1.0.7/unittests/Modules/ImportPackage/callvar.cj000066400000000000000000000005401510705540100251500ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. import vardecl.a func callVar() { let tmp: Int32 = 1 + a return vardecl.a + tmp } cangjie_compiler-1.0.7/unittests/Modules/PackageTest.cpp000066400000000000000000001340061510705540100233640ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "gtest/gtest.h" #include #include #include #define private public #define protected public #include "TestCompilerInstance.h" #ifdef _WIN32 #include #endif #include "CompilationCacheSerialization.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/IncrementalCompilation/IncrementalScopeAnalysis.h" #include "cangjie/Modules/ASTSerialization.h" #include "cangjie/Utils/FileUtil.h" using namespace Cangjie; using namespace AST; class PackageTest : public testing::Test { protected: void SetUp() override { #ifdef _WIN32 std::string command; int err = 0; if (FileUtil::FileExist("testTempFiles")) { std::string command = "rmdir /s /q testTempFiles"; int err = system(command.c_str()); std::cout << err; } command = "mkdir testTempFiles"; err = system(command.c_str()); ASSERT_EQ(0, err); srcPath = projectPath + "\\unittests\\Modules\\CangjieFiles\\"; packagePath = packagePath + "\\"; #else std::string command; int err = 0; if (FileUtil::FileExist("testTempFiles")) { command = "rm -rf testTempFiles"; err = system(command.c_str()); ASSERT_EQ(0, err); } command = "mkdir -p testTempFiles"; err = system(command.c_str()); ASSERT_EQ(0, err); srcPath = projectPath + "/unittests/Modules/CangjieFiles/"; packagePath = packagePath + "/"; #endif #ifdef __x86_64__ invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::X86_64; #else invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::AARCH64; #endif #ifdef _WIN32 invocation.globalOptions.target.os = Cangjie::Triple::OSType::WINDOWS; #elif __unix__ invocation.globalOptions.target.os = Cangjie::Triple::OSType::LINUX; #endif invocation.globalOptions.outputMode = GlobalOptions::OutputMode::STATIC_LIB; invocation.globalOptions.disableReflection = true; invocation.globalOptions.importPaths = {packagePath}; invocation.globalOptions.compilationCachedPath = "."; for (unsigned int i = 0; i < fileNames.size(); i++) { auto srcFile = srcPath + fileNames[i] + ".cj"; std::string failedReason; auto content = FileUtil::ReadFileContent(srcFile, failedReason); if (!content.has_value()) { diag.DiagnoseRefactor( DiagKindRefactor::module_read_file_to_buffer_failed, DEFAULT_POSITION, srcFile, failedReason); } instance = std::make_unique(invocation, diag); instance->code = std::move(content.value()); instance->invocation.globalOptions.implicitPrelude = false; instance->Compile(); std::vector astData; instance->importManager.ExportAST(false, astData, *instance->GetSourcePackages()[0]); allAstData[fileNames[i]] = astData; std::string astFile = packagePath + fileNames[i] + ".cjo"; ASSERT_TRUE(FileUtil::WriteBufferToASTFile(astFile, astData)); diag.Reset(); } } #ifdef PROJECT_SOURCE_DIR // Gets the absolute path of the project from the compile parameter. std::string projectPath = PROJECT_SOURCE_DIR; #else // Just in case, give it a default value. // Assume the initial is in the build directory. std::string projectPath = ".."; #endif std::vector fileNames{"vardecl", "funcdecl", "math", "recorddecl", "interfacedecl", "classdecl"}; std::vector fullNames{ "goodboy/vardecl", "badboy/funcdecl", "math", "badboy/recorddecl", "badboy/interfacedecl", "goodboy/classdecl"}; std::string packagePath = "testTempFiles"; std::string srcPath; DiagnosticEngine diag; CompilerInvocation invocation; std::unique_ptr instance; std::unordered_map> allAstData; }; // Test for package with only one cangjie file. TEST_F(PackageTest, DISABLED_SingleFile_Package) { std::vector> expectDecls{{"a", "b", "c", "d"}, {"fun1", "fun1", "fun2", "fun3", "fun4", "a...0", "b...0", "c...0", "a...1"}, {"plus"}, {"Rectangle"}, {"I1", "I2"}, {"Base2", "Derive1", "I3", "Base3"}}; for (unsigned int i = 0; i < fileNames.size(); i++) { instance = std::make_unique(invocation, diag); instance->code = "import " + fileNames[i] + ".*"; instance->Compile(); Package* pkg = nullptr; for (auto pd : instance->importManager.GetAllImportedPackages()) { if (pd->fullPackageName != CORE_PACKAGE_NAME) { pkg = pd->srcPackage; break; } } ASSERT_TRUE(pkg != nullptr); std::vector declNames; for (auto& file : pkg->files) { for (auto& decl : file->decls) { if (Is(decl.get())) { EXPECT_TRUE(expectDecls[i].find(decl->identifier) != expectDecls[i].end()); } if (Is(decl.get())) { EXPECT_TRUE(expectDecls[i].find(decl->identifier) != expectDecls[i].end()); } if (Is(decl.get())) { EXPECT_TRUE(expectDecls[i].find(decl->identifier) != expectDecls[i].end()); } if (Is(decl.get())) { EXPECT_TRUE(expectDecls[i].find(decl->identifier) != expectDecls[i].end()); } } } EXPECT_TRUE(diag.GetErrorCount() == 0); } } TEST_F(PackageTest, DISABLED_SingleFile_LoadCache_Package) { std::vector> expectDecls{{"a", "b", "c", "d"}, {"fun1", "fun1", "fun2", "fun3", "fun4", "a...0", "b...0", "c...0", "a...1"}, {"plus"}, {"Rectangle"}, {"I1", "I2"}, {"Base2", "Derive1", "I3", "Base3"}}; for (unsigned int i = 0; i < fileNames.size(); i++) { instance = std::make_unique(invocation, diag); instance->importManager.SetPackageCjoCache(fileNames[i], allAstData[fileNames[i]]); instance->code = "import " + fileNames[i] + ".*"; instance->Compile(); Package* pkg = nullptr; for (auto pd : instance->importManager.GetAllImportedPackages()) { if (pd->fullPackageName != CORE_PACKAGE_NAME) { pkg = pd->srcPackage; break; } } ASSERT_TRUE(pkg != nullptr); std::vector declNames; for (auto& file : pkg->files) { for (auto& decl : file->decls) { if (Is(decl.get())) { EXPECT_TRUE(expectDecls[i].find(decl->identifier) != expectDecls[i].end()); } if (Is(decl.get())) { EXPECT_TRUE(expectDecls[i].find(decl->identifier) != expectDecls[i].end()); } if (Is(decl.get())) { EXPECT_TRUE(expectDecls[i].find(decl->identifier) != expectDecls[i].end()); } if (Is(decl.get())) { EXPECT_TRUE(expectDecls[i].find(decl->identifier) != expectDecls[i].end()); } } } EXPECT_TRUE(diag.GetErrorCount() == 0); } } // Test for one package with multi cangjie files. TEST_F(PackageTest, DISABLED_MultiFile_Package) { instance = std::make_unique(invocation, diag); instance->srcDirs = {srcPath}; instance->code = "import funcdecl.*"; instance->Compile(); diag.Reset(); std::vector astData; instance->importManager.ExportAST(false, astData, *instance->GetSourcePackages()[0]); std::string astFile = packagePath + "MultiFile.cjo"; ASSERT_TRUE(FileUtil::WriteBufferToASTFile(astFile, astData)); std::unordered_map expectVarDecls{{"a", true}, {"b", true}, {"c", true}, {"d", true}}; std::unordered_map expectFuncDecls{{"fun1", true}, {"fun1", true}, {"fun2", true}, {"fun3", true}, {"fun4", true}, {"a...0", true}, {"b...0", true}, {"c...0", true}, {"a...1", true}, {"plus", true}}; std::unordered_map expectRecordDecls{{"Rectangle", true}}; std::unordered_map expectInterfaceDecls{{"I1", true}, {"I2", true}, {"I3", true}}; std::unordered_map expectClassDecls{{"Base2", true}, {"Derive1", true}, {"Base3", true}}; Package* pkg = nullptr; for (auto pd : instance->importManager.GetAllImportedPackages()) { if (pd->fullPackageName != CORE_PACKAGE_NAME) { pkg = pd->srcPackage; break; } } ASSERT_TRUE(pkg != nullptr); std::vector declNames; for (auto& file : pkg->files) { for (auto& decl : file->decls) { if (Is(decl.get())) { EXPECT_TRUE(expectVarDecls[decl->identifier]); } if (Is(decl.get())) { EXPECT_TRUE(expectFuncDecls[decl->identifier]); } if (Is(decl.get())) { EXPECT_TRUE(expectRecordDecls[decl->identifier]); } if (Is(decl.get())) { EXPECT_TRUE(expectInterfaceDecls[decl->identifier]); } if (Is(decl.get())) { EXPECT_TRUE(expectClassDecls[decl->identifier]); } } } EXPECT_TRUE(diag.GetErrorCount() == 0); } TEST_F(PackageTest, DISABLED_ImportPackage) { #ifdef _WIN32 srcPath = projectPath + "\\unittests\\Modules\\ImportPackage\\"; #elif __unix__ srcPath = projectPath + "/unittests/Modules/ImportPackage/"; #endif instance = std::make_unique(invocation, diag); instance->srcDirs = {srcPath}; for (unsigned int i = 0; i < fileNames.size(); i++) { instance->importManager.SetPackageCjoCache(fileNames[i], allAstData[fileNames[i]]); } instance->Compile(); diag.Reset(); std::set expectImportSet{"Base2", "Base3", "Derive1", "I2", "I3", "Rectangle", "a", "classdecl", "fun1", "funcdecl", "interfacedecl", "math", "plus", "recorddecl", "vardecl"}; std::set importSet; auto pkg = instance->GetSourcePackages()[0]; for (auto& file : pkg->files) { for (auto& [name, _] : instance->importManager.GetImportedDecls(*file)) { importSet.emplace(name); } } for (auto pd : instance->importManager.GetCurImportedPackages(instance->GetSourcePackages()[0]->fullPackageName)) { importSet.insert(pd->srcPackage->fullPackageName); } EXPECT_TRUE(diag.GetErrorCount() == 0); EXPECT_TRUE(std::equal(importSet.begin(), importSet.end(), expectImportSet.begin())); } namespace { bool NoImportedContent(CompilerInstance& ci) { // All imported declaration should not have content body. auto pkgs = ci.GetPackages(); bool noSrc = true; for (auto pkg : pkgs) { if (!pkg->TestAttr(Attribute::IMPORTED)) { continue; } Walker(pkg, [&noSrc](auto node) { if (auto vd = DynamicCast(node); vd && vd->initializer) { noSrc = false; return VisitAction::STOP_NOW; } else if (auto fd = DynamicCast(node); fd && fd->funcBody && fd->funcBody->body) { noSrc = false; return VisitAction::STOP_NOW; } return VisitAction::WALK_CHILDREN; }).Walk(); } return noSrc; } } // namespace TEST_F(PackageTest, DISABLED_LSPBasicCompileCost) { instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->code = R"( // Also test for using imported typealias func test(x: Byte) : Unit { var a : Byte = 1 var c : UInt8 = a c = x } func foo() { var c : (UInt8)->Unit = test } )"; instance->importManager.SetSourceCodeImportStatus(false); auto start = std::chrono::high_resolution_clock::now(); instance->Compile(CompileStage::SEMA); auto end = std::chrono::high_resolution_clock::now(); // Compile time for basic case should shorter than 200 ms. std::chrono::duration cost = end - start; auto costMs = static_cast(cost.count()); std::cout << "cost: " << costMs << std::endl; EXPECT_TRUE(NoImportedContent(*instance)); EXPECT_TRUE(diag.GetErrorCount() == 0); } TEST_F(PackageTest, DISABLED_LSPImportLotPkgsCompileCost) { instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->code = R"( import std.ast.* import std.collection.* import std.io.* import std.random.* import std.unittest.* import std.unittest.testmacro.* func test() { let a = ArrayList() a.add(1) a.add("2") for (v in a) { println((v as ToString).getOrThrow()) } } )"; instance->importManager.SetSourceCodeImportStatus(false); auto start = std::chrono::high_resolution_clock::now(); instance->Compile(CompileStage::SEMA); auto end = std::chrono::high_resolution_clock::now(); // Compile time for the case importing lot of packages should shorter than 1 s. std::chrono::duration cost = end - start; auto costMs = static_cast(cost.count()); std::cout << "cost: " << costMs << std::endl; EXPECT_TRUE(NoImportedContent(*instance)); EXPECT_TRUE(diag.GetErrorCount() == 0); } TEST_F(PackageTest, DISABLED_LSPCompileComposition) { instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->code = R"( func f(x: Int32) : Float32 { return Float32(x) } func g(x: Float32): Int32 { return Int32(x) } let fg = f ~> g main() :Unit {} )"; instance->importManager.SetSourceCodeImportStatus(false); instance->Compile(CompileStage::SEMA); EXPECT_TRUE(diag.GetErrorCount() == 0); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); auto fg = pkgs[0]->files[0]->decls[2].get(); ASSERT_TRUE(fg->astKind == ASTKind::VAR_DECL); ASSERT_TRUE(StaticCast(fg)->initializer != nullptr); auto callExpr = DynamicCast(StaticCast(fg)->initializer->desugarExpr.get()); ASSERT_TRUE(callExpr != nullptr); ASSERT_TRUE(callExpr->resolvedFunction); EXPECT_EQ(callExpr->resolvedFunction->identifier, "composition"); } TEST_F(PackageTest, DISABLED_LSPCompileWithConstraint) { instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->code = R"( main() { match (0) { case x: ToString => 0 case x: ?Any => 1 // unreachable case _ => 2 // unreachable } } )"; instance->importManager.SetSourceCodeImportStatus(false); instance->Compile(CompileStage::SEMA); EXPECT_TRUE(diag.GetErrorCount() == 0); EXPECT_TRUE(diag.GetWarningCount() == 2); auto enumDecl = instance->importManager.GetCoreDecl("Option"); ASSERT_TRUE(enumDecl != nullptr); ASSERT_TRUE(instance->typeManager != nullptr); auto extends = instance->typeManager->GetDeclExtends(*enumDecl); EXPECT_FALSE(extends.empty()); size_t count = 0; for (auto extend : extends) { ASSERT_TRUE(extend->generic != nullptr); if (!extend->generic->genericConstraints.empty()) { count++; for (auto& constraint : extend->generic->genericConstraints) { ASSERT_TRUE(constraint != nullptr && constraint->type != nullptr); auto gTy = RawStaticCast(constraint->type->ty); EXPECT_TRUE(gTy && !gTy->upperBounds.empty()); EXPECT_FALSE(constraint->upperBounds.empty()); } } } // NOTE: Current the count of all extends in 'core' package have generic constraints is 3. EXPECT_EQ(count, 3); } TEST_F(PackageTest, DISABLED_LSPImportFunctionWithNamedParam) { diag.ClearError(); instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->invocation.globalOptions.compilePackage = true; instance->code = R"( package pkg public func name(a_123!:Int64) {} )"; instance->importManager.SetSourceCodeImportStatus(false); instance->Compile(CompileStage::SEMA); std::vector astData; instance->importManager.ExportAST(false, astData, *instance->GetSourcePackages()[0]); instance = std::make_unique(invocation, diag); instance->importManager.SetPackageCjoCache("pkg", astData); instance->invocation.globalOptions.implicitPrelude = true; instance->code = R"( import pkg.* main() { var xx = name(a_123: 5) } )"; instance->importManager.SetSourceCodeImportStatus(false); instance->Compile(CompileStage::SEMA); auto pkgs = instance->GetSourcePackages(); ASSERT_EQ(pkgs.size(), 1); ASTContext& ctx = *instance->GetASTContextByPackage(pkgs[0]); EXPECT_TRUE(diag.GetErrorCount() == 0); Searcher searcher; std::vector res = searcher.Search(ctx, "ast_kind: ref_expr"); ASSERT_EQ(res.size(), 1); auto target = res[0]->target; ASSERT_TRUE(target != nullptr && target->astKind == ASTKind::FUNC_DECL && target->TestAttr(Attribute::IMPORTED)); auto fd = RawStaticCast(target); ASSERT_TRUE(fd->funcBody && !fd->funcBody->paramLists.empty()); EXPECT_EQ(fd->funcBody->paramLists[0]->params.size(), 1); for (auto& param : fd->funcBody->paramLists[0]->params) { EXPECT_TRUE(param->outerDecl == fd); } } TEST_F(PackageTest, DISABLED_ExportVisibleMembersForLSP) { diag.ClearError(); instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->invocation.globalOptions.compilePackage = true; instance->code = R"( package pkg public class A { public let a = 1 protected let b = 1 internal let c = 1 private let d = 1 } )"; instance->importManager.SetSourceCodeImportStatus(false); instance->Compile(CompileStage::SEMA); std::vector astData; instance->importManager.ExportAST(false, astData, *instance->GetSourcePackages()[0]); instance = std::make_unique(invocation, diag); instance->importManager.SetPackageCjoCache("pkg", astData); instance->invocation.globalOptions.implicitPrelude = true; instance->code = R"( import pkg.* main() {} )"; instance->importManager.SetSourceCodeImportStatus(false); instance->Compile(CompileStage::IMPORT_PACKAGE); auto pd = instance->importManager.GetPackageDecl("pkg"); ASSERT_TRUE(pd != nullptr && !pd->srcPackage->files.empty() && !pd->srcPackage->files[0]->decls.empty()); std::vector expected{"a", "b", "c"}; std::vector res; for (auto it : pd->srcPackage->files[0]->decls[0]->GetMemberDeclPtrs()) { if (it->astKind == ASTKind::VAR_DECL) { res.emplace_back(it->identifier); } } EXPECT_TRUE(std::equal(res.cbegin(), res.cend(), expected.cbegin(), expected.cend())); } TEST_F(PackageTest, DISABLED_ConstNotExportInitializerForLSP) { diag.ClearError(); instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->invocation.globalOptions.compilePackage = true; instance->code = R"( package initializer public class A{ static const INSTANCE = A() private const init() {} } )"; instance->importManager.SetSourceCodeImportStatus(false); instance->Compile(CompileStage::SEMA); std::vector astData; instance->importManager.ExportAST(false, astData, *instance->GetSourcePackages()[0]); instance = std::make_unique(invocation, diag); instance->importManager.SetPackageCjoCache("initializer", astData); instance->invocation.globalOptions.implicitPrelude = true; instance->code = R"( import initializer.* main() {} )"; instance->importManager.SetSourceCodeImportStatus(false); instance->Compile(CompileStage::IMPORT_PACKAGE); auto pd = instance->importManager.GetPackageDecl("initializer"); ASSERT_TRUE(pd != nullptr && !pd->srcPackage->files.empty() && !pd->srcPackage->files[0]->decls.empty()); for (auto it : pd->srcPackage->files[0]->decls[0]->GetMemberDeclPtrs()) { EXPECT_TRUE(it->astKind == ASTKind::VAR_DECL); EXPECT_TRUE(it->identifier == "INSTANCE"); EXPECT_TRUE(StaticCast(it)->initializer == nullptr); } } TEST_F(PackageTest, DISABLED_ForbiddenRunInstantiation) { Cangjie::ICE::TriggerPointSetter iceSetter(static_cast(Cangjie::ICE::UNITTEST_TP)); instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->code = R"( main() {} )"; instance->importManager.SetSourceCodeImportStatus(false); auto start = std::chrono::high_resolution_clock::now(); bool res = instance->Compile(CompileStage::GENERIC_INSTANTIATION); auto end = std::chrono::high_resolution_clock::now(); // Compile time for empty main should shorter than 100 ms. std::chrono::duration cost = end - start; auto costMs = static_cast(cost.count()); std::cout << "cost: " << costMs << std::endl; EXPECT_TRUE(NoImportedContent(*instance)); // No diagnoses but have printing internal error. EXPECT_TRUE(diag.GetErrorCount() == 0); EXPECT_FALSE(res); } TEST_F(PackageTest, DISABLED_LoadBuiltInDecl) { instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->code = R"( main() {} )"; bool res = instance->Compile(CompileStage::IMPORT_PACKAGE); EXPECT_TRUE(res); auto pkgs = instance->importManager.GetAllImportedPackages(); Utils::EraseIf(pkgs, [](auto it) { return !it->TestAttr(Attribute::IMPORTED); }); EXPECT_EQ(pkgs.size(), 1); if (!pkgs.empty()) { size_t preFileID = 0; for (auto& file : pkgs[0]->srcPackage->files) { EXPECT_NE(file->begin.fileID, 0); // Imported fileID should not be 0. if (preFileID == 0) { preFileID = file->begin.fileID; } else { // Current fileID should equal to the 'preFileID + 1'. EXPECT_EQ(file->begin.fileID, ++preFileID); } } } EXPECT_TRUE(diag.GetErrorCount() == 0); } TEST_F(PackageTest, DISABLED_MangleEnumExportId) { instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->code = R"( package pkg enum B { B1 public func test(a: T) {a} } public enum A { A1 public func test(a: T) { return B1.test(a) } public func foo(b: X) {b} } public enum C { C1 public func foo(b: X) {b} } public func test() { if (A1.test(123) != 123) { return 1 } if (A1.foo(234) != 234) { return 2 } if (C1.foo("23") != "23") { return 3 } return 0 } )"; bool res = instance->Compile(); ASSERT_TRUE(res); EXPECT_TRUE(diag.GetErrorCount() == 0); auto pkg = instance->GetSourcePackages()[0]; ASSERT_TRUE(pkg != nullptr); std::vector astData; instance->importManager.ExportAST(false, astData, *pkg); // ExportId will be updated during serialization. // Expect all generic decls of instantiated decls have valid exportId. std::unordered_set exportIds; std::unordered_set> genericDecls; for (auto& decl : pkg->genericInstantiatedDecls) { ASSERT_TRUE(decl->genericDecl != nullptr); EXPECT_FALSE(decl->genericDecl->exportId.empty()); exportIds.emplace(decl->genericDecl->exportId); genericDecls.emplace(decl->genericDecl); } EXPECT_EQ(genericDecls.size(), exportIds.size()); } TEST_F(PackageTest, DISABLED_LSPExportInterfaceFuncFromMacro) { instance = std::make_unique(invocation, diag); instance->code = R"( // @M(interface I2 {}) interface I2{ func ff6():Unit { return } } main() { } )"; instance->Compile(CompileStage::SEMA); std::vector> meds; // Test interface decl in macrocall. for (auto& decl : instance->GetSourcePackages()[0]->files[0]->decls) { if (auto id = AST::As(decl.get()); id) { auto macroCall = MakeOwned(); id->curMacroCall = macroCall.get(); meds.emplace_back(std::move(macroCall)); id->EnableAttr(Attribute::MACRO_EXPANDED_NODE); for (auto& d : id->body->decls) { d->EnableAttr(Attribute::MACRO_EXPANDED_NODE); } } } std::vector astData; instance->importManager.ExportAST(false, astData, *instance->GetSourcePackages()[0]); EXPECT_EQ(diag.GetErrorCount(), 0); } TEST_F(PackageTest, DISABLED_ImportManager_API) { instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->code = R"( main() { } )"; instance->Compile(CompileStage::IMPORT_PACKAGE); auto members = instance->importManager.GetPackageMembers("default", "std.core"); EXPECT_TRUE(!members.empty()); auto obj = instance->importManager.GetImportedDecl(CORE_PACKAGE_NAME, OBJECT_NAME); EXPECT_TRUE(obj != nullptr); auto pkg = instance->GetSourcePackages()[0]; auto importPkgs = instance->importManager.GetCurImportedPackages(pkg->fullPackageName); EXPECT_FALSE(importPkgs.empty()); // default 'core'. } TEST_F(PackageTest, DISABLED_LSPExportInvalidVpd) { diag.ClearError(); instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->invocation.globalOptions.compilePackage = true; instance->code = R"( package pkg public class A { public var (a, b) : (Int64, Int64) // This is an invalid member. } )"; instance->importManager.SetSourceCodeImportStatus(false); instance->Compile(CompileStage::SEMA); std::vector astData; instance->importManager.ExportAST(false, astData, *instance->GetSourcePackages()[0]); EXPECT_NE(diag.GetErrorCount(), 0); } TEST_F(PackageTest, DISABLED_LSPExportInvisibleMembers) { diag.ClearError(); instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->invocation.globalOptions.compilePackage = true; instance->code = R"( package pkg class C {} class B { public func f0(v: C) {} } public class A { public func f1(v: B) {} func f2(v: B) {} } )"; instance->importManager.SetSourceCodeImportStatus(false); instance->Compile(CompileStage::SEMA); std::vector astData; instance->importManager.ExportAST(false, astData, *instance->GetSourcePackages()[0]); EXPECT_NE(diag.GetErrorCount(), 0); } TEST_F(PackageTest, DISABLED_CheckCjoPathLegality) { diag.ClearError(); instance = std::make_unique(invocation, diag); auto import = MakeOwned(); import->EnableAttr(Attribute::IMPLICIT_ADD); bool ret = instance->importManager.CheckCjoPathLegality(import, "", "not found package name", false, false); ASSERT_EQ(ret, false); EXPECT_TRUE(diag.GetErrorCount() == 1); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND TEST_F(PackageTest, DISABLED_SemanticUsage) { diag.ClearError(); instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->invocation.globalOptions.enIncrementalCompilation = true; instance->code = R"( package pkg import std.core var a: Any = 1 var b: Rune = 'a' open class A { A(var a!: Int64 = 2, var b!: E = E1) {} } extend A <: core.ToString { public func toString() { "A" } } extend A <: Hashable { public func hashCode(): Int64 {1} static func coo() : String {"static"} } class B <: A { func test() { toString() } } extend Int64 { func foo() { a = A().toString() + B.coo() } } enum E { E1 | E2(A) } main() { 1.foo() } )"; const std::unordered_set declNames{ "a", "b", "A", "B", "init", "toString", "hashCode", "coo", "test", "foo", "E", "main"}; bool ret = instance->Compile(CompileStage::DESUGAR_AFTER_SEMA); auto pkg = instance->GetSourcePackages()[0]; ASSERT_TRUE(pkg != nullptr); ret = ret && instance->PerformDesugarAfterSema(); ASSERT_TRUE(ret); EXPECT_EQ(diag.GetErrorCount(), 0); std::unordered_map> mangleToDeclMap; auto collect = [&mangleToDeclMap](Ptr d) { if (!d->rawMangleName.empty()) { mangleToDeclMap.emplace(d->rawMangleName, d); } }; IterateToplevelDecls(*pkg, [&collect](auto& decl) { auto d = decl.get(); if (auto md = DynamicCast(decl.get()); md && md->desugarDecl) { d = md->desugarDecl.get(); } collect(d); for (auto& member : decl->GetMemberDecls()) { if (auto pd = DynamicCast(member.get())) { std::for_each(pd->getters.begin(), pd->getters.end(), [&collect](auto& it) { collect(it.get()); }); std::for_each(pd->setters.begin(), pd->setters.end(), [&collect](auto& it) { collect(it.get()); }); } else { collect(member.get()); } } }); EXPECT_EQ(mangleToDeclMap.size(), 17); // Count of all toplevel and members decls except enum constructors. auto checkSemaInfo = [&declNames](auto& info) { EXPECT_EQ(info.usages.size(), 17); // Count of all toplevel decls except enum constructors. EXPECT_EQ(info.relations.size(), 2); // class A, B EXPECT_EQ(info.builtInTypeRelations.size(), 1); // extend of Int64 uint8_t extendCount = 0; for (auto& [decl, u] : info.usages) { if (decl->astKind == ASTKind::EXTEND_DECL) { extendCount++; } else { EXPECT_TRUE(declNames.find(decl->identifier) != declNames.end()); } if (decl->identifier == "a" && decl->TestAttr(Attribute::GLOBAL)) { ASSERT_EQ(u.boxedTypes.size(), 1); EXPECT_EQ(*u.boxedTypes.begin(), "l"); EXPECT_EQ(u.apiUsages.usedDecls.size(), 1); EXPECT_EQ(u.apiUsages.usedNames.size(), 1); EXPECT_TRUE(u.apiUsages.usedNames["Any"].hasUnqualifiedUsageOfImported); EXPECT_TRUE(u.bodyUsages.usedDecls.empty()); EXPECT_TRUE(u.bodyUsages.usedNames.empty()); } else if (decl->TestAttr(Attribute::PRIMARY_CONSTRUCTOR)) { EXPECT_EQ(u.boxedTypes.size(), 0); EXPECT_EQ(u.apiUsages.usedDecls.size(), 1); // enum E (enum ctor does not been counted separately) EXPECT_EQ(u.apiUsages.usedNames.size(), 1); // type E auto& nameUsage = u.apiUsages.usedNames["E"]; EXPECT_TRUE(nameUsage.hasUnqualifiedUsage); } else if (decl->identifier == "test") { EXPECT_EQ(u.boxedTypes.size(), 0); EXPECT_EQ(u.apiUsages.usedDecls.size(), 0); EXPECT_EQ(u.apiUsages.usedNames.size(), 0); EXPECT_EQ(u.bodyUsages.usedDecls.size(), 4); EXPECT_EQ(u.bodyUsages.usedNames.size(), 1); EXPECT_TRUE(u.bodyUsages.usedNames["toString"].hasUnqualifiedUsage); } else if (decl->identifier == "E") { EXPECT_EQ(u.boxedTypes.size(), 0); EXPECT_EQ(u.apiUsages.usedDecls.size(), 1); // A EXPECT_EQ(u.apiUsages.usedNames.size(), 1); } else if (decl->identifier == "foo") { EXPECT_EQ(u.boxedTypes.size(), 1); // String EXPECT_EQ(u.apiUsages.usedDecls.size(), 0); // A init, A, toString, B, coo, global a, overload +, String EXPECT_EQ(u.bodyUsages.usedDecls.size(), 8); EXPECT_EQ(u.bodyUsages.usedNames.size(), 6); // a, A, toString, B, coo, + auto& nameUsage = u.bodyUsages.usedNames["coo"]; ASSERT_EQ(nameUsage.parentDecls.size(), 1); EXPECT_TRUE((*nameUsage.parentDecls.begin()).find("pkg") != std::string::npos); } } EXPECT_EQ(extendCount, 3); for (auto& [mangle, rel] : info.relations) { if (mangle.find("B") != std::string::npos) { EXPECT_EQ(rel.inherits.size(), 1); EXPECT_TRUE(rel.extends.empty()); } else if (mangle.find("A") != std::string::npos) { EXPECT_EQ(rel.inherits.size(), 1); // Object EXPECT_EQ(rel.extends.size(), 2); EXPECT_EQ(rel.extendedInterfaces.size(), 2); } else { CJC_ABORT(); } } }; checkSemaInfo(instance->cachedInfo.semaInfo); // Serialize. HashedASTWriter writer; writer.SetSemanticInfo(instance->cachedInfo.semaInfo); auto data = writer.AST2FB(pkg->fullPackageName); // Deserialize. auto hashedPackage = CachedASTFormat::GetHashedPackage(data.data()); auto loaded = HashedASTLoader::LoadSemanticInfos(*hashedPackage, mangleToDeclMap); checkSemaInfo(loaded); } namespace Cangjie::Sema::Desugar::AfterTypeCheck { SemanticInfo GetSemanticUsage(TypeManager& typeManager, const std::vector>& pkgs); } // namespace Cangjie::Sema::Desugar::AfterTypeCheck TEST_F(PackageTest, DISABLED_IncrementalMergedSemanticUsage) { diag.ClearError(); instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->invocation.globalOptions.enIncrementalCompilation = true; instance->code = R"( macro package pkg import std.ast.* import std.core var a: Any = 1 var b: Rune = 'a' open class A { A(var a!: Int64 = 2, var b!: E = E1) {} } extend A <: core.ToString { public func toString() { "A" } } extend A <: Hashable { public func hashCode(): Int64 {1} static func coo() : String {"static"} } class B <: A { func test() { toString() } } extend Int64 { func foo() { a = A().toString() + B.coo() } } enum E { E1 | E2(A) } public macro M(input: Tokens) { input } main() { 1.foo() } )"; bool ret = instance->Compile(CompileStage::DESUGAR_AFTER_SEMA); auto pkg = instance->GetSourcePackages()[0]; ASSERT_TRUE(pkg != nullptr); ret = ret && instance->PerformDesugarAfterSema(); ASSERT_TRUE(ret); EXPECT_EQ(diag.GetErrorCount(), 0); // Test for store useInfo in incremental compilation with unchanged decls. auto preInfo = instance->cachedInfo.semaInfo; // Serialize. HashedASTWriter writer; writer.SetSemanticInfo(preInfo); auto data = writer.AST2FB(pkg->fullPackageName); // Deserialize. auto hashedPackage = CachedASTFormat::GetHashedPackage(data.data()); // 1. Update to data status as loaded version. instance->cachedInfo.semaInfo = HashedASTLoader::LoadSemanticInfos(*hashedPackage, instance->rawMangleName2DeclMap); auto markDecl = [](Ptr d) { d->toBeCompiled = false; if (!d->IsNominalDecl() && !Utils::In(d->astKind, {ASTKind::PROP_DECL, ASTKind::PRIMARY_CTOR_DECL})) { d->EnableAttr(Attribute::INCRE_COMPILE); } }; // 2. construct cache for unchanged incremental compilation. Ptr classA = nullptr; IterateToplevelDecls(*pkg, [&markDecl, &classA](auto& decl) { auto d = decl.get(); if (auto md = DynamicCast(d); md && md->desugarDecl) { d = md->desugarDecl.get(); } else if (auto macroDecl = DynamicCast(d); macroDecl && macroDecl->desugarDecl) { d = macroDecl->desugarDecl.get(); } markDecl(d); if (d->identifier == "A") { classA = d; } for (auto& member : decl->GetMemberDecls()) { if (auto pd = DynamicCast(member.get())) { std::for_each(pd->getters.begin(), pd->getters.end(), [&markDecl](auto& it) { markDecl(it.get()); }); std::for_each(pd->setters.begin(), pd->setters.end(), [&markDecl](auto& it) { markDecl(it.get()); }); } else { markDecl(member.get()); } } }); if (classA) { auto& members = classA->GetMemberDecls(); for (size_t i = 1; i < members.size(); ++i) { auto originDecl = instance->rawMangleName2DeclMap[members[i]->rawMangleName]; // Since decl has been desugared, we need to update sema cache to new decl. auto found = instance->cachedInfo.semaInfo.usages.find(originDecl); if (found != instance->cachedInfo.semaInfo.usages.end()) { instance->cachedInfo.semaInfo.usages.emplace(members[i].get(), found->second); instance->cachedInfo.semaInfo.usages.erase(originDecl); } } } // 3. Expect same usage infos. using namespace Cangjie::Sema::Desugar::AfterTypeCheck; instance->CacheSemaUsage(GetSemanticUsage(*instance->typeManager, instance->GetSourcePackages())); auto incrInfo = instance->cachedInfo.semaInfo; EXPECT_EQ(preInfo.usages.size(), incrInfo.usages.size()); EXPECT_EQ(preInfo.relations.size(), incrInfo.relations.size()); EXPECT_EQ(preInfo.builtInTypeRelations.size(), incrInfo.builtInTypeRelations.size()); for (auto& [decl, usage] : preInfo.usages) { auto found = incrInfo.usages.find(decl); ASSERT_TRUE(found != incrInfo.usages.end()); EXPECT_EQ(usage.apiUsages.usedDecls.size(), found->second.apiUsages.usedDecls.size()); EXPECT_EQ(usage.apiUsages.usedNames.size(), found->second.apiUsages.usedNames.size()); EXPECT_EQ(usage.bodyUsages.usedDecls.size(), found->second.bodyUsages.usedDecls.size()); EXPECT_EQ(usage.bodyUsages.usedNames.size(), found->second.bodyUsages.usedNames.size()); } } TEST_F(PackageTest, DISABLED_UsageOfTypeAlias) { diag.ClearError(); instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->invocation.globalOptions.enIncrementalCompilation = true; instance->code = R"( interface interfaceA{} type A= interfaceA class B <: A {} main() {} )"; bool ret = instance->Compile(CompileStage::DESUGAR_AFTER_SEMA); auto pkg = instance->GetSourcePackages()[0]; ASSERT_TRUE(pkg != nullptr); ret = ret && instance->PerformDesugarAfterSema(); ASSERT_TRUE(ret); EXPECT_EQ(diag.GetErrorCount(), 0); bool checked = false; auto info = instance->cachedInfo.semaInfo; for (auto& [decl, use] : info.usages) { if (decl->identifier == "B") { checked = true; EXPECT_TRUE(use.apiUsages.usedNames.count("A") != 0); EXPECT_TRUE(use.apiUsages.usedDecls.count("7default10interfaceA<2>I") != 0); } } EXPECT_TRUE(checked); } TEST_F(PackageTest, DISABLED_UsageOfOnTheLeftOfPipeline) { diag.ClearError(); instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->invocation.globalOptions.enIncrementalCompilation = true; instance->code = R"( open class A{ public var b :Int64 = 0 public func a(input:Int64){input} } class B <: A { public func test() { super.b |> super.a } } main() {} )"; bool ret = instance->Compile(CompileStage::DESUGAR_AFTER_SEMA); auto pkg = instance->GetSourcePackages()[0]; ASSERT_TRUE(pkg != nullptr); ret = ret && instance->PerformDesugarAfterSema(); ASSERT_TRUE(ret); EXPECT_EQ(diag.GetErrorCount(), 0); bool checked = false; auto info = instance->cachedInfo.semaInfo; for (auto& [decl, use] : info.usages) { if (decl->identifier == "test") { checked = true; EXPECT_TRUE(use.bodyUsages.usedDecls.count("7default1AC1aFl$$") != 0); EXPECT_TRUE(use.bodyUsages.usedDecls.count("7default1AC1bV$$l") != 0); } } EXPECT_TRUE(checked); } TEST_F(PackageTest, DISABLED_LoadAnnotationTarget) { diag.ClearError(); instance = std::make_unique(invocation, diag); instance->invocation.globalOptions.implicitPrelude = true; instance->invocation.globalOptions.enIncrementalCompilation = true; instance->code = R"( @Annotation public class A { public const init() {} } main() {} )"; bool ret = instance->Compile(CompileStage::DESUGAR_AFTER_SEMA); auto pkg = instance->GetSourcePackages()[0]; ASSERT_TRUE(pkg != nullptr); ret = ret && instance->PerformDesugarAfterSema(); ASSERT_TRUE(ret); EXPECT_EQ(diag.GetErrorCount(), 0); auto classA = pkg->files[0]->decls[0].get(); auto& annotations = classA->annotations; auto found = std::find_if( annotations.begin(), annotations.end(), [](const auto& it) { return it->kind == AnnotationKind::ANNOTATION; }); ASSERT_TRUE(found != annotations.end()); classA->EnableAttr(Attribute::IS_ANNOTATION); found->get()->target = 3; // Set to 3. std::map> mangledName2DeclMap; for (auto [ident, decl] : instance->rawMangleName2DeclMap) { mangledName2DeclMap.emplace(ident, const_cast(decl.get())); } auto data = instance->importManager.ExportASTSignature(*pkg); // Unset annotation status. classA->DisableAttr(Attribute::IS_ANNOTATION); found->get()->target = 0; // Test for loading annotation status. pkg->EnableAttr(Attribute::INCRE_COMPILE); auto loader = std::make_unique(std::move(data), pkg->fullPackageName, *instance->typeManager, *instance->importManager.cjoManager, instance->invocation.globalOptions); (void)loader->LoadCachedTypeForPackage(*pkg, mangledName2DeclMap); EXPECT_TRUE(classA->TestAttr(Attribute::IS_ANNOTATION)); EXPECT_EQ(found->get()->target, 3); } #endif TEST_F(PackageTest, DISABLED_LoadPackageFromCjo) { CompilerInvocation testInvocation; #ifdef __x86_64__ testInvocation.globalOptions.target.arch = Cangjie::Triple::ArchType::X86_64; #else testInvocation.globalOptions.target.arch = Cangjie::Triple::ArchType::AARCH64; #endif #ifdef _WIN32 testInvocation.globalOptions.target.os = Cangjie::Triple::OSType::WINDOWS; #elif __unix__ testInvocation.globalOptions.target.os = Cangjie::Triple::OSType::LINUX; #endif DiagnosticEngine testDiag; auto testIns = std::make_unique(testInvocation, testDiag); testIns->invocation.globalOptions.implicitPrelude = true; testIns->code = "main() {}"; testIns->Compile(CompileStage::IMPORT_PACKAGE); auto& searchPath = testIns->importManager.GetSearchPath(); auto getCjoPath = [&searchPath](const std::string& fullPkgName) { return FileUtil::FindSerializationFile(fullPkgName, SERIALIZED_FILE_EXTENSION, searchPath); }; std::unordered_set fullPkgNames = { "std.math", "std.sync", "std.time", }; std::unordered_map> pkgName2PkgNodeMap; auto checkPkg = [&testIns, &pkgName2PkgNodeMap, &getCjoPath](const std::string& pkgName) { auto pkg = testIns->importManager.LoadPackageFromCjo(pkgName, getCjoPath(pkgName)); EXPECT_NE(pkg, nullptr); auto found = std::as_const(pkgName2PkgNodeMap).find(pkgName); if (found == pkgName2PkgNodeMap.cend()) { pkgName2PkgNodeMap.emplace(pkgName, pkg); } else { EXPECT_EQ(pkg, found->second); } EXPECT_GT(pkg->files.size(), 0); for (auto& file : std::as_const(pkg->files)) { if (file->decls.size() > 0) { for (auto& decl : std::as_const(file->decls)) { EXPECT_TRUE(Ty::IsTyCorrect(decl->ty)); } } } }; for (size_t i = 0; i < 2; ++i) { for (auto& fullPkgName : std::as_const(fullPkgNames)) { checkPkg(fullPkgName); } } } cangjie_compiler-1.0.7/unittests/Option/000077500000000000000000000000001510705540100203215ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Option/CMakeLists.txt000066400000000000000000000013341510705540100230620ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. add_executable(OptionTest OptionTest.cpp OptionTableTest.cpp ${CANGJIE_SRC_OBJECTS}) if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) set(CJNATIVE_BACKEND "cjnative") string(TOLOWER ${CMAKE_SYSTEM_NAME}_${CMAKE_SYSTEM_PROCESSOR}_${CJNATIVE_BACKEND} output_lib_dir) set(RUNTIME_LIB_DIR ${CMAKE_BINARY_DIR}/lib/${output_lib_dir}) endif() target_link_libraries( OptionTest ${LINK_LIBS} GTest::gtest GTest::gtest_main) add_test(NAME OptionTest COMMAND OptionTest) cangjie_compiler-1.0.7/unittests/Option/OptionTableTest.cpp000066400000000000000000000162271510705540100241150ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include "gtest/gtest.h" #include "cangjie/Basic/Print.h" #include "cangjie/Option/OptionTable.h" #include "cangjie/Utils/FileUtil.h" using namespace Cangjie; class OptionTableTest : public ::testing::Test { protected: void SetUp() override { } std::unique_ptr table = CreateOptionTable(); }; // Special case: invalid input TEST_F(OptionTableTest, ParseArgsTestEmptyInput) { ArgList argList; std::vector argStrs; (void)table->ParseArgs(argStrs, argList); // We don't really care about the result in such cases, we just don't want it crashes. } // Correct case: empty arg TEST_F(OptionTableTest, ParseArgsTestEmptyArg) { // Special case: Empty string ArgList argList; std::vector argStrs = {"cjc", ""}; bool succ = table->ParseArgs(argStrs, argList); EXPECT_TRUE(succ); } // Correct case: space only arg TEST_F(OptionTableTest, ParseArgsTestSpaceArg) { ArgList argList; std::vector argStrs = {"cjc", " "}; bool succ = table->ParseArgs(argStrs, argList); EXPECT_TRUE(succ); } // Error case: dash only arg TEST_F(OptionTableTest, ParseArgsTestDashOnlyArg) { ArgList argList; std::vector argStrs = {"cjc", "-"}; bool succ = table->ParseArgs(argStrs, argList); EXPECT_FALSE(succ); } // Error case: Separated TEST_F(OptionTableTest, ParseArgsTestSeparatedError) { ArgList argList; std::vector argStrs = {"cjc", "-o"}; bool succ = table->ParseArgs(argStrs, argList); EXPECT_FALSE(succ); } // Correct case: Separated TEST_F(OptionTableTest, ParseArgsTestSeparatedCorrect) { ArgList argList; std::vector argStrs = {"cjc", "-o", "a.out"}; bool succ = table->ParseArgs(argStrs, argList); EXPECT_TRUE(succ); } // Error case: Flag TEST_F(OptionTableTest, ParseArgsTestFlagError) { ArgList argList; std::vector argStrs = {"cjc", "-not-exist-arg"}; bool succ = table->ParseArgs(argStrs, argList); EXPECT_FALSE(succ); } // Correct case: Flag TEST_F(OptionTableTest, ParseArgsTestFlagCorrect) { ArgList argList; std::vector argStrs = {"cjc", "-v"}; bool succ = table->ParseArgs(argStrs, argList); EXPECT_TRUE(succ); } // Error case: Joined, wrong option name TEST_F(OptionTableTest, ParseArgsTestJoinedError) { ArgList argList5; std::vector argStrs5 = {"cjc", "-not-exist-arg=local"}; bool succ = table->ParseArgs(argStrs5, argList5); EXPECT_FALSE(succ); } // Correct case: Joined TEST_F(OptionTableTest, ParseArgsTestJoinedCorrect) { ArgList argList; std::vector argStrs = {"cjc", "--output-type=staticlib"}; bool succ = table->ParseArgs(argStrs, argList); EXPECT_TRUE(succ); } // Correct case: multi correct arguments TEST_F(OptionTableTest, ParseArgsTestCorrect1) { std::vector argStrs = {"cjc-frontend", "--dump-ir", "main.cj", "test.cj"}; ArgList argList; std::unique_ptr table = CreateOptionTable(true); bool succ = table->ParseArgs(argStrs, argList); EXPECT_TRUE(succ); EXPECT_EQ(argList.args.size(), 3); auto arg = dynamic_cast(argList.args[0].get()); EXPECT_NE(arg, nullptr); EXPECT_EQ(arg->info.GetID(), Options::ID::DUMP_IR); auto input1 = dynamic_cast(argList.args[1].get()); auto input2 = dynamic_cast(argList.args[2].get()); EXPECT_EQ(input1->value, "main.cj"); EXPECT_EQ(input2->value, "test.cj"); } // Correct case: have separated kind arguments in the arguments string TEST_F(OptionTableTest, ParseArgsTestCorrect2) { std::vector argStrs = {"cjc-frontend", "-Woff", "all", "main.cj"}; ArgList argList; std::unique_ptr table = CreateOptionTable(true); bool succ = table->ParseArgs(argStrs, argList); EXPECT_TRUE(succ); EXPECT_EQ(argList.args.size(), 2); auto arg = dynamic_cast(argList.args[0].get()); EXPECT_NE(arg, nullptr); EXPECT_EQ(arg->info.GetID(), Options::ID::WARN_OFF); EXPECT_EQ(arg->value, "all"); } TEST_F(OptionTableTest, ArgListTest) { ArgList argList; std::vector argStrs = {"cjc", "-Woff", "all", "main.cj"}; table->ParseArgs(argStrs, argList); EXPECT_EQ(argList.args.size(), 2); auto arg = dynamic_cast(argList.args[0].get()); EXPECT_NE(arg, nullptr); EXPECT_EQ(arg->info.GetID(), Options::ID::WARN_OFF); EXPECT_EQ(arg->value, "all"); } // FLAG options must be set MULTIPLE_OCCURRENCE for occurrence type. See Options.inc for details. TEST_F(OptionTableTest, FlagOccurrenceSetTest) { std::unique_ptr optionTable = CreateOptionTable(); const auto& optionInfos = optionTable->optionInfos; for (OptionTable::OptionInfo info : optionInfos) { if (info.GetKind() == Options::Kind::FLAG) { EXPECT_EQ(info.GetOccurrenceType(), Options::Occurrence::MULTIPLE_OCCURRENCE); } } } /** * READ THE FOLLOWING NOTE if you found this test failed. * * What is this? * This is a unittest tests if every SEPARATED kind options with SINGLE_OCCURRENCE property has a * corresponding LLT test in a particular LLT directory. * * What is the purpose of this test? * When an option (SEPARATED kind options with SINGLE_OCCURRENCE property) is specified in a * command more than once, an warning will be printed to remind user the former specified one in * the command will be overwritten by the later specified one. We need to add PROPER tests for this * warning print feature. * * What should I do? * Add a test in the particular LLT directory. You should check the following items MANUALLY: * - if the warning makes sense (does you option overwrite former specified value?) * - if conflict warnings have been printed (warnings and errors that contradict) * The test itself could be simply check the output of warning. However, the above checks * needs to be done by the developer. * * Note: The test file should have a proper name for passing this unittest. */ TEST_F(OptionTableTest, DISABLED_OccurrenceTestExistTest) { #ifdef PROJECT_SOURCE_DIR std::string projectPath = PROJECT_SOURCE_DIR; #else std::string projectPath = ".."; #endif std::string testsPath = projectPath + "/tests/LLT/Driver/options/occurance_warning_tests"; std::unique_ptr optionTable = CreateOptionTable(); const auto& optionInfos = optionTable->optionInfos; for (OptionTable::OptionInfo info : optionInfos) { if (info.GetOccurrenceType() == Options::Occurrence::SINGLE_OCCURRENCE) { if (info.GetName() == "--module-name") { continue; } std::string fileName = info.GetName() + ".cj"; std::string fullPath = FileUtil::JoinPath(testsPath, fileName); EXPECT_TRUE(FileUtil::FileExist(fullPath)) << fullPath << " not exist."; } } } cangjie_compiler-1.0.7/unittests/Option/OptionTest.cpp000066400000000000000000000177051510705540100231470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "gtest/gtest.h" #include "cangjie/Option/Option.h" #include "cangjie/Driver/Driver.h" #include "cangjie/Utils/FileUtil.h" #include using namespace Cangjie; class OptionTest : public ::testing::Test { protected: void SetUp() override { optTbl = CreateOptionTable(); gblOpts = std::make_unique(); #ifdef PROJECT_SOURCE_DIR // Gets the absolute path of the project from the compile parameter. std::string projectPath = PROJECT_SOURCE_DIR; #else // Just in case, give it a default value. // Assume the initial is in the build directory. std::string projectPath = ".."; #endif #ifdef _WIN32 srcFile = FileUtil::JoinPath(projectPath, "unittests\\Option\\main.cj"); #else srcFile = FileUtil::JoinPath(projectPath, "unittests/Option/main.cj"); #endif } std::unique_ptr optTbl; std::unique_ptr gblOpts; std::string srcFile; }; TEST_F(OptionTest, GlobalOptionParseFromArgsTest) { std::vector argStrs = {"cjc", "--no-prelude", "--output-type", "staticlib", srcFile}; ArgList argList; bool succ = optTbl->ParseArgs(argStrs, argList); EXPECT_TRUE(succ); succ = gblOpts->ParseFromArgs(argList); EXPECT_TRUE(succ); EXPECT_EQ(gblOpts->outputMode, GlobalOptions::OutputMode::STATIC_LIB); } TEST_F(OptionTest, NoArgsTest) { // Nothing input. std::vector argStrs = {"cjc", "--no-prelude"}; DiagnosticEngine diag; std::unique_ptr driver = std::make_unique(argStrs, diag, "cjc"); bool succ = driver->ParseArgs(); EXPECT_TRUE(succ); succ = driver->ExecuteCompilation(); EXPECT_FALSE(succ); } TEST_F(OptionTest, InvalidArgsTest) { // Something doesn't exist. std::vector argStrs = {"cjc", "--no-prelude", "c"}; ArgList argList; bool succ = optTbl->ParseArgs(argStrs, argList); EXPECT_TRUE(succ); succ = gblOpts->ParseFromArgs(argList); EXPECT_FALSE(succ); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND TEST_F(OptionTest, StackTraceFormatTest) { { std::vector argStrs = {"cjc"}; ArgList argList; bool succ = optTbl->ParseArgs(argStrs, argList); EXPECT_TRUE(succ); succ = gblOpts->ParseFromArgs(argList); EXPECT_TRUE(gblOpts->stackTraceFmt == GlobalOptions::StackTraceFormat::DEFAULT); } { std::vector argStrs = {"cjc", "--stack-trace-format", "default"}; ArgList argList; bool succ = optTbl->ParseArgs(argStrs, argList); EXPECT_TRUE(succ); succ = gblOpts->ParseFromArgs(argList); EXPECT_TRUE(gblOpts->stackTraceFmt == GlobalOptions::StackTraceFormat::DEFAULT); } { std::vector argStrs = {"cjc", "--stack-trace-format", "simple"}; ArgList argList; bool succ = optTbl->ParseArgs(argStrs, argList); EXPECT_TRUE(succ); succ = gblOpts->ParseFromArgs(argList); EXPECT_TRUE(gblOpts->stackTraceFmt == GlobalOptions::StackTraceFormat::SIMPLE); } { std::vector argStrs = {"cjc", "--stack-trace-format", "all"}; ArgList argList; bool succ = optTbl->ParseArgs(argStrs, argList); EXPECT_TRUE(succ); succ = gblOpts->ParseFromArgs(argList); EXPECT_TRUE(gblOpts->stackTraceFmt == GlobalOptions::StackTraceFormat::ALL); } { std::vector argStrs = {"cjc", "--stack-trace-format"}; ArgList argList; bool succ = optTbl->ParseArgs(argStrs, argList); EXPECT_FALSE(succ); } } #endif TEST_F(OptionTest, EmptyModuleNameTest) { // NOTE: "--module-name" is deprecated. std::vector argStrs = {"cjc", "--module-name"}; ArgList argList; bool succ = optTbl->ParseArgs(argStrs, argList); EXPECT_FALSE(succ); } TEST(IsPossibleMatchingTripleNameTest, VaildTripleName1) { Triple::Info t = { Triple::ArchType::X86_64, Triple::Vendor::UNKNOWN, Triple::OSType::LINUX, Triple::Environment::GNU}; std::string s = "x86_64-pc-linux-gnu"; bool res = Triple::IsPossibleMatchingTripleName(t, s); EXPECT_TRUE(res); } TEST(IsPossibleMatchingTripleNameTest, VaildTripleName2) { Triple::Info t = { Triple::ArchType::X86_64, Triple::Vendor::UNKNOWN, Triple::OSType::LINUX, Triple::Environment::GNU}; std::string s = "x86_64-unknown-linux-gnu"; bool res = Triple::IsPossibleMatchingTripleName(t, s); EXPECT_TRUE(res); } TEST(IsPossibleMatchingTripleNameTest, VaildTripleName3) { Triple::Info t = { Triple::ArchType::X86_64, Triple::Vendor::UNKNOWN, Triple::OSType::LINUX, Triple::Environment::GNU}; std::string s = "x86_64-linux-gnu"; bool res = Triple::IsPossibleMatchingTripleName(t, s); EXPECT_TRUE(res); } TEST(IsPossibleMatchingTripleNameTest, InvaildTripleName1) { Triple::Info t = { Triple::ArchType::X86_64, Triple::Vendor::UNKNOWN, Triple::OSType::LINUX, Triple::Environment::GNU}; std::string s = "aarch64-linux-gnu"; bool res = Triple::IsPossibleMatchingTripleName(t, s); EXPECT_FALSE(res); } TEST(IsPossibleMatchingTripleNameTest, InvaildTripleName2) { Triple::Info t = { Triple::ArchType::X86_64, Triple::Vendor::UNKNOWN, Triple::OSType::LINUX, Triple::Environment::GNU}; std::string s = "notatriplename"; bool res = Triple::IsPossibleMatchingTripleName(t, s); EXPECT_FALSE(res); } TEST(ToFullTripleStringTest, ArchNameTest) { std::vector> pairs = { {{Triple::ArchType::X86_64, Triple::Vendor::UNKNOWN, Triple::OSType::LINUX, Triple::Environment::GNU}, "x86_64-unknown-linux-gnu"}, {{Triple::ArchType::AARCH64, Triple::Vendor::UNKNOWN, Triple::OSType::LINUX, Triple::Environment::GNU}, "aarch64-unknown-linux-gnu"}, {{Triple::ArchType::UNKNOWN, Triple::Vendor::UNKNOWN, Triple::OSType::LINUX, Triple::Environment::GNU}, "unknown-unknown-linux-gnu"}}; for (size_t i = 0; i < pairs.size(); i++) { auto actualName = pairs[i].first.ToFullTripleString(); EXPECT_EQ(actualName, pairs[i].second); } } TEST(ToFullTripleStringTest, VendorNameTest) { std::vector> pairs = { {{Triple::ArchType::X86_64, Triple::Vendor::UNKNOWN, Triple::OSType::LINUX, Triple::Environment::GNU}, "x86_64-unknown-linux-gnu"}, {{Triple::ArchType::X86_64, Triple::Vendor::PC, Triple::OSType::UNKNOWN, Triple::Environment::NOT_AVAILABLE}, "x86_64-pc-unknown"}, }; for (size_t i = 0; i < pairs.size(); i++) { auto actualName = pairs[i].first.ToFullTripleString(); EXPECT_EQ(actualName, pairs[i].second); } } TEST(ToFullTripleStringTest, OSNameTest) { std::vector> pairs = { {{Triple::ArchType::X86_64, Triple::Vendor::UNKNOWN, Triple::OSType::LINUX, Triple::Environment::GNU}, "x86_64-unknown-linux-gnu"}}; for (size_t i = 0; i < pairs.size(); i++) { auto actualName = pairs[i].first.ToFullTripleString(); EXPECT_EQ(actualName, pairs[i].second); } } TEST(ToFullTripleStringTest, EnvironmentNameTest) { std::vector> pairs = { {{Triple::ArchType::X86_64, Triple::Vendor::UNKNOWN, Triple::OSType::LINUX, Triple::Environment::GNU}, "x86_64-unknown-linux-gnu"}, {{Triple::ArchType::X86_64, Triple::Vendor::UNKNOWN, Triple::OSType::LINUX, Triple::Environment::OHOS}, "x86_64-unknown-linux-ohos"}, }; for (size_t i = 0; i < pairs.size(); i++) { auto actualName = pairs[i].first.ToFullTripleString(); EXPECT_EQ(actualName, pairs[i].second); } } cangjie_compiler-1.0.7/unittests/Option/main.cj000066400000000000000000000004641510705540100215670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. main(): Int64 { print("ok") return 0 } cangjie_compiler-1.0.7/unittests/Parse/000077500000000000000000000000001510705540100201235ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Parse/CMakeLists.txt000066400000000000000000000022621510705540100226650ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. add_executable(ParserTest ParserTest.cpp) target_link_libraries( ParserTest cangjie-lsp ${CMAKE_DL_LIBS} GTest::gtest GTest::gtest_main) add_test(NAME ParserTest COMMAND ParserTest) add_executable(ParseTypeTest ParseTypeTest.cpp) target_link_libraries( ParseTypeTest cangjie-lsp ${CMAKE_DL_LIBS} GTest::gtest GTest::gtest_main) add_test(NAME ParseTypeTest COMMAND ParseTypeTest) add_executable(ParseTypeAliasTest ParseTypeAliasTest.cpp) target_link_libraries( ParseTypeAliasTest cangjie-lsp ${CMAKE_DL_LIBS} GTest::gtest GTest::gtest_main) add_test(NAME ParseTypeAliasTest COMMAND ParseTypeAliasTest) add_executable(ParseCommentTest ParseCommentTest.cpp) target_link_libraries( ParseCommentTest cangjie-lsp ${LINK_LIBS} boundscheck-static GTest::gtest GTest::gtest_main TestCompilerInstanceObject) add_test(NAME ParseCommentTest COMMAND ParseCommentTest)cangjie_compiler-1.0.7/unittests/Parse/ParseCangjieFiles/000077500000000000000000000000001510705540100234415ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Parse/ParseCangjieFiles/Test.cj000066400000000000000000000005211510705540100246740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. import std.unittest.* import std.unittest.testmacro.* @Test class TestA {} cangjie_compiler-1.0.7/unittests/Parse/ParseCommentTest.cpp000066400000000000000000000264431510705540100240750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * Test cases for comment groups attach. */ #include "gtest/gtest.h" #include "TestCompilerInstance.h" #include "cangjie/AST/Walker.h" #include "cangjie/Parse/Parser.h" #include "cangjie/Utils/CastingTemplate.h" using namespace Cangjie; using namespace AST; class ParseCommentTest : public testing::Test { protected: void SetUp() override { #ifdef _WIN32 srcPath = projectPath + "\\unittests\\Parse\\ParseCangjieFiles\\"; #else srcPath = projectPath + "/unittests/Parse/ParseCangjieFiles/"; #endif #ifdef __x86_64__ invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::X86_64; #else invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::AARCH64; #endif #ifdef _WIN32 invocation.globalOptions.target.os = Cangjie::Triple::OSType::WINDOWS; invocation.globalOptions.executablePath = projectPath + "\\output\\bin\\"; #elif __unix__ invocation.globalOptions.target.os = Cangjie::Triple::OSType::LINUX; invocation.globalOptions.executablePath = projectPath + "/output/bin/"; #endif invocation.globalOptions.importPaths = {definePath}; } #ifdef PROJECT_SOURCE_DIR // Gets the absolute path of the project from the compile parameter. std::string projectPath = PROJECT_SOURCE_DIR; #else // Just in case, give it a default value. // Assume the initial is in the build directory. std::string projectPath = ".."; #endif std::string srcPath; std::string definePath; DiagnosticEngine diag; SourceManager sm; std::string code; CompilerInvocation invocation; std::unique_ptr instance; }; TEST_F(ParseCommentTest, ParseMacroNodes) { instance = std::make_unique(invocation, diag); instance->compileOnePackageFromSrcFiles = true; instance->srcFilePaths = {srcPath + "Test.cj"}; invocation.globalOptions.outputMode = GlobalOptions::OutputMode::STATIC_LIB; instance->Compile(CompileStage::MACRO_EXPAND); std::vector> ptrs; size_t cmgNum{0}; auto collectNodes = [&ptrs, &cmgNum](Ptr curNode) -> VisitAction { const auto& nodeCms = curNode->comments; cmgNum += nodeCms.leadingComments.size() + nodeCms.trailingComments.size() + nodeCms.innerComments.size(); if (curNode->astKind == ASTKind::ANNOTATION || curNode->astKind == ASTKind::MODIFIER) { return VisitAction::SKIP_CHILDREN; } if (curNode->astKind == ASTKind::FILE) { return VisitAction::WALK_CHILDREN; } ptrs.push_back(curNode); return VisitAction::WALK_CHILDREN; }; Walker macWalker(instance->GetSourcePackages()[0]->files[0], collectNodes); macWalker.Walk(); EXPECT_EQ(cmgNum, 0); std::cout << std::endl; } TEST_F(ParseCommentTest, TrailComments) { code = R"( class A { let m1 = 1 // c0 rule 3 } // c1 rule 1 // c2 rule 1 main() { } )"; Parser parser(code, diag, sm, {0, 1, 1}, true); OwnedPtr file = parser.ParseTopLevel(); size_t attchNum = 0; const size_t trlCgNumOfClassA = 2; std::vector> testNodes; Walker walker(file.get(), [&attchNum, &testNodes](Ptr node) -> VisitAction { const auto& nodeCms = node->comments; if (node->astKind == ASTKind::CLASS_DECL || node->astKind == ASTKind::VAR_DECL) { testNodes.emplace_back(node); } attchNum += nodeCms.leadingComments.size() + nodeCms.trailingComments.size() + nodeCms.innerComments.size(); return VisitAction::WALK_CHILDREN; }); walker.Walk(); EXPECT_EQ(testNodes.size(), 2); for (auto node : testNodes) { const auto& nodeTrailCms = node->comments.trailingComments; if (node->astKind == ASTKind::CLASS_DECL) { ASSERT_EQ(nodeTrailCms.size(), trlCgNumOfClassA); EXPECT_TRUE(nodeTrailCms[0].cms.front().info.Value().find("c1") != std::string::npos); EXPECT_TRUE(nodeTrailCms[1].cms.front().info.Value().find("c2") != std::string::npos); } else if (node->astKind == ASTKind::VAR_DECL) { ASSERT_EQ(nodeTrailCms.size(), 1); EXPECT_TRUE(nodeTrailCms[0].cms.front().info.Value().find("c0") != std::string::npos); } } EXPECT_EQ(attchNum, 3); } TEST_F(ParseCommentTest, leadingComments) { code = R"( class A { // c0 rule 3 // c1 rule 2 let m1 = 1 } // c2 rule 3 main() { } )"; Parser parser(code, diag, sm, {0, 1, 1}, true); OwnedPtr file = parser.ParseTopLevel(); size_t attchNum = 0; const size_t trlCgNumOfVarDecl = 2; std::vector> testNodes; Walker walker(file.get(), [&attchNum, &testNodes](Ptr node) -> VisitAction { const auto& nodeCms = node->comments; if (node->astKind == ASTKind::VAR_DECL || node->astKind == ASTKind::MAIN_DECL) { testNodes.emplace_back(node); } attchNum += nodeCms.leadingComments.size() + nodeCms.trailingComments.size() + nodeCms.innerComments.size(); return VisitAction::WALK_CHILDREN; }); walker.Walk(); EXPECT_EQ(testNodes.size(), 2); for (auto node : testNodes) { const auto& nodeLeadCms = node->comments.leadingComments; if (node->astKind == ASTKind::VAR_DECL) { ASSERT_EQ(nodeLeadCms.size(), trlCgNumOfVarDecl); EXPECT_TRUE(nodeLeadCms[0].cms.front().info.Value().find("c0") != std::string::npos); EXPECT_TRUE(nodeLeadCms[1].cms.front().info.Value().find("c1") != std::string::npos); } else if (node->astKind == ASTKind::MAIN_DECL) { ASSERT_EQ(nodeLeadCms.size(), 1); EXPECT_TRUE(nodeLeadCms[0].cms.front().info.Value().find("c2") != std::string::npos); } } EXPECT_EQ(attchNum, 3); } TEST_F(ParseCommentTest, InnerComents) { code = R"( main(/* c0*/) { // c1 } )"; Parser parser(code, diag, sm, {0, 1, 1}, true); OwnedPtr file = parser.ParseTopLevel(); size_t attchNum = 0; std::vector> testNodes; Walker walker(file.get(), [&attchNum, &testNodes](Ptr node) -> VisitAction { const auto& nodeCms = node->comments; if (node->astKind == ASTKind::FUNC_PARAM_LIST || node->astKind == ASTKind::BLOCK) { testNodes.emplace_back(node); } attchNum += nodeCms.leadingComments.size() + nodeCms.trailingComments.size() + nodeCms.innerComments.size(); return VisitAction::WALK_CHILDREN; }); walker.Walk(); EXPECT_EQ(testNodes.size(), 2); for (auto node : testNodes) { const auto& nodeInnerCms = node->comments.innerComments; if (node->astKind == ASTKind::FUNC_PARAM_LIST) { ASSERT_EQ(nodeInnerCms.size(), 1); EXPECT_TRUE(nodeInnerCms[0].cms.front().info.Value().find("c0") != std::string::npos); } else if (node->astKind == ASTKind::BLOCK) { ASSERT_EQ(nodeInnerCms.size(), 1); EXPECT_TRUE(nodeInnerCms[0].cms.front().info.Value().find("c1") != std::string::npos); } } EXPECT_EQ(attchNum, 2); } TEST_F(ParseCommentTest, MultStyComents) { code = R"( /** * c0 lead package spec */ package comment import std.ast.* // c1 lead Macro Decl of M0 @M0 public class A { // c2 lead var decl of var a // c3 lead var decl of var a rule 2 var a = 1 // c4 trail var decl of var a // c5 trail var decl of var a } // c6 trail Macro Decl of M0 // c7 lead funcDecl of foo rule 2 public func foo(){/* c8 inner funcBlock*/} // c9 lead funcDecl of bar foreign func bar(){ } main () { 1 + 2 } // cEnd trail mainDecl rule 1 )"; Parser parser(code, diag, sm, {0, 1, 1}, true); OwnedPtr file = parser.ParseTopLevel(); size_t attchNum = 0; std::vector> testNodes; const std::set collectKinds{ASTKind::PACKAGE_SPEC, ASTKind::MACRO_EXPAND_DECL, ASTKind::VAR_DECL, ASTKind::FUNC_DECL, ASTKind::BLOCK, ASTKind::MAIN_DECL}; Walker walker(file.get(), [&](Ptr node) -> VisitAction { const auto& nodeCms = node->comments; attchNum += nodeCms.leadingComments.size() + nodeCms.trailingComments.size() + nodeCms.innerComments.size(); if (collectKinds.find(node->astKind) == collectKinds.end()) { return VisitAction::WALK_CHILDREN; } if (node->astKind == ASTKind::BLOCK) { if (node->begin.line == 17) { testNodes.emplace_back(node); } } else { testNodes.emplace_back(node); } return VisitAction::WALK_CHILDREN; }); walker.Walk(); EXPECT_EQ(testNodes.size(), 7); for (auto node : testNodes) { const auto& nodeLeadCms = node->comments.leadingComments; const auto& nodeInnerCms = node->comments.innerComments; const auto& nodeTrailCms = node->comments.trailingComments; if (node->astKind == ASTKind::PACKAGE_SPEC) { ASSERT_EQ(nodeLeadCms.size(), 1); EXPECT_TRUE(nodeLeadCms[0].cms.front().info.Value().find("c0") != std::string::npos); } else if (node->astKind == ASTKind::MACRO_EXPAND_DECL) { auto d = StaticAs(node)->invocation.decl.get(); ASSERT_TRUE(d); ASSERT_EQ(d->comments.leadingComments.size(), 1); EXPECT_TRUE(d->comments.leadingComments[0].cms.front().info.Value().find("c1") != std::string::npos); ASSERT_EQ(d->comments.trailingComments.size(), 1); EXPECT_TRUE(d->comments.trailingComments[0].cms.front().info.Value().find("c6") != std::string::npos); } else if (node->astKind == ASTKind::VAR_DECL) { ASSERT_EQ(nodeLeadCms.size(), 2); EXPECT_TRUE(nodeLeadCms[0].cms.front().info.Value().find("c2") != std::string::npos); EXPECT_TRUE(nodeLeadCms[1].cms.front().info.Value().find("c3") != std::string::npos); ASSERT_EQ(nodeTrailCms.size(), 2); EXPECT_TRUE(nodeTrailCms[0].cms.front().info.Value().find("c4") != std::string::npos); EXPECT_TRUE(nodeTrailCms[1].cms.front().info.Value().find("c5") != std::string::npos); } else if (node->astKind == ASTKind::FUNC_DECL) { auto fd = StaticCast(node); if (fd->identifier == "foo") { ASSERT_EQ(nodeLeadCms.size(), 1); EXPECT_TRUE(nodeLeadCms[0].cms.front().info.Value().find("c7") != std::string::npos); } else if (fd->identifier == "bar") { ASSERT_EQ(nodeLeadCms.size(), 1); EXPECT_TRUE(nodeLeadCms[0].cms.front().info.Value().find("c9") != std::string::npos); } } else if (node->astKind == ASTKind::BLOCK) { ASSERT_EQ(nodeInnerCms.size(), 1); EXPECT_TRUE(nodeInnerCms[0].cms.front().info.Value().find("c8") != std::string::npos); } else if (node->astKind == ASTKind::MAIN_DECL) { ASSERT_EQ(nodeTrailCms.size(), 1); EXPECT_TRUE(nodeTrailCms[0].cms.front().info.Value().find("cEnd") != std::string::npos); } } EXPECT_EQ(attchNum, 11); } cangjie_compiler-1.0.7/unittests/Parse/ParseTypeAliasTest.cpp000066400000000000000000000015141510705540100243560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * Test cases for type alias parser. */ #include "gtest/gtest.h" #include "cangjie/Parse/Parser.h" using namespace Cangjie; using namespace AST; TEST(ParseTypeAlias, ParseTypeAliasTest) { { std::string code = R"( type* d = dd )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser p(code, diag, sm); OwnedPtr node = p.ParseDecl(ScopeKind::TOPLEVEL); diag.EmitCategoryDiagnostics(DiagCategory::PARSE); EXPECT_TRUE(diag.GetErrorCount() > 0); } } cangjie_compiler-1.0.7/unittests/Parse/ParseTypeTest.cpp000066400000000000000000000155761510705540100234210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * Test cases for type parser. */ #include #include "gtest/gtest.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/PrintNode.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/Match.h" #include "cangjie/Parse/Parser.h" using namespace Cangjie; using namespace AST; using namespace Meta; TEST(ParseType, ParseTypePosition) { std::string code = R"( let a : (A, B) -> (C) let b : () -> D let c : CFunc<(A)->E> )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector> expectPosition; expectPosition.push_back(std::make_pair(2, 13)); expectPosition.push_back(std::make_pair(2, 26)); expectPosition.push_back(std::make_pair(2, 23)); expectPosition.push_back(std::make_pair(2, 26)); expectPosition.push_back(std::make_pair(3, 13)); expectPosition.push_back(std::make_pair(3, 20)); expectPosition.push_back(std::make_pair(4, 19)); expectPosition.push_back(std::make_pair(4, 25)); std::vector> position; Walker walker(file.get(), [&](Ptr node) -> VisitAction { return match(*node)( [&](const ParenType& pt) { position.push_back(std::make_pair(pt.begin.line, pt.begin.column)); position.push_back(std::make_pair(pt.end.line, pt.end.column)); return VisitAction::WALK_CHILDREN; }, [&](const FuncType& ft) { position.push_back(std::make_pair(ft.begin.line, ft.begin.column)); position.push_back(std::make_pair(ft.end.line, ft.end.column)); return VisitAction::WALK_CHILDREN; }, [&](const Node& /* node */) { return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(position.size() == expectPosition.size()); EXPECT_TRUE(std::equal(position.begin(), position.end(), expectPosition.begin())); } TEST(ParseType, ParseTypeExceptionTest) { { // suppress stderr, only test errorcount testing::internal::CaptureStderr(); std::string code = R"( Int32->Int64 )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser p(code, diag, sm); OwnedPtr node = p.ParseType(); diag.EmitCategoryDiagnostics(DiagCategory::PARSE); EXPECT_TRUE(diag.GetErrorCount() > 0); (void)testing::internal::GetCapturedStderr(); } } TEST(ParseType, ParseFuncTypeTest) { { // This case contain a 'try-parse' std::string code = R"( var a = delimiterArr.size * (value.size - 1) )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser p(code, diag, sm); std::set modifiers; OwnedPtr node = p.ParseTopLevel(); EXPECT_TRUE(diag.GetErrorCount() == 0); } { std::string code = R"( ((Int32, Int32))->Int32 )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser p(code, diag, sm); OwnedPtr node = p.ParseType(); EXPECT_TRUE(diag.GetErrorCount() == 0); EXPECT_TRUE(Is(node.get())); } { std::string code = R"( ((Int32, Int32), ((Int8, Int16), Int64))->Int32 )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser p(code, diag, sm); OwnedPtr node = p.ParseType(); EXPECT_TRUE(diag.GetErrorCount() == 0); EXPECT_TRUE(Is(node.get())); } { // positive case std::string code = R"( (Int32)->Int32 )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser p(code, diag, sm); OwnedPtr node = p.ParseType(); EXPECT_TRUE(diag.GetErrorCount() == 0); EXPECT_TRUE(Is(node.get())); } { // positive case std::string code = R"( (Int32, unit)->Int32 )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser p(code, diag, sm); OwnedPtr node = p.ParseType(); EXPECT_TRUE(diag.GetErrorCount() == 0); EXPECT_TRUE(Is(node.get())); } { // positive case std::string code = R"( ()->Int32 )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser p(code, diag, sm); OwnedPtr node = p.ParseType(); EXPECT_TRUE(diag.GetErrorCount() == 0); EXPECT_TRUE(Is(node.get())); } } TEST(ParseType, OthreType) { { // positive case std::string code = R"( inta )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser p(code, diag, sm); OwnedPtr node = p.ParseType(); EXPECT_TRUE(diag.GetErrorCount() == 0); EXPECT_TRUE(Is(node.get())); } { std::string code = R"( (inta, intb, intc) )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser p(code, diag, sm); OwnedPtr node = p.ParseType(); EXPECT_TRUE(diag.GetErrorCount() == 0); EXPECT_TRUE(Is(node.get())); } { std::string code = R"( ((inta.intb), intc) )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser p(code, diag, sm); OwnedPtr node = p.ParseType(); EXPECT_TRUE(diag.GetErrorCount() == 0); EXPECT_TRUE(Is(node.get())); } { std::string code = R"( a.b.c )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser p(code, diag, sm); OwnedPtr node = p.ParseType(); EXPECT_TRUE(diag.GetErrorCount() == 0); EXPECT_TRUE(Is(node.get())); } } TEST(ParseType, ParseParenTypeTest) { { std::string code = R"( ((Int32, Int32), Int32) )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser p(code, diag, sm); OwnedPtr node = p.ParseType(); EXPECT_TRUE(diag.GetErrorCount() == 0); EXPECT_TRUE(Is(node.get())); } } cangjie_compiler-1.0.7/unittests/Parse/ParserTest.cpp000066400000000000000000003222371510705540100227340ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include #include #include "gtest/gtest.h" #include "cangjie/Parse/Parser.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Types.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Basic/Match.h" #include "cangjie/Utils/ConstantsUtils.h" using namespace Cangjie; using namespace AST; using namespace Meta; class ParserTest : public testing::Test { protected: void SetUp() override { diag.SetSourceManager(&sm); parser = std::make_unique(code, diag, sm); file = parser->ParseTopLevel(); } std::string code = R"( main(argc:int, argv:string="123") { let a:int=40 let b = 2 ** -a print((a+3*b, (a+3) *b)) } )"; std::unique_ptr parser; OwnedPtr file; SourceManager sm; DiagnosticEngine diag; }; TEST(ParserTest1, PositionTest) { std::string code = R"( main(argc:Int, argv:String") { let a:Int= [1..3]; let b = 2 ** -a print((a+3*b, (a+3) *b)) var scoreResult: string = match (score) { case 10 => "fail" case _ => case (q,q) => "12" } } interface A { func f() {} } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector> expectPosition; expectPosition.push_back(std::make_pair(2, 5)); expectPosition.push_back(std::make_pair(2, 10)); expectPosition.push_back(std::make_pair(2, 20)); expectPosition.push_back(std::make_pair(6, 31)); expectPosition.push_back(std::make_pair(7, 14)); expectPosition.push_back(std::make_pair(8, 14)); expectPosition.push_back(std::make_pair(9, 14)); expectPosition.push_back(std::make_pair(15, 6)); std::vector> position; Walker walker(file.get(), [&](Ptr node) -> VisitAction { return match(*node)( [&](const MainDecl& md) { position.push_back(std::make_pair(md.begin.line, md.begin.column)); return VisitAction::WALK_CHILDREN; }, [&](const FuncParam& fp) { position.push_back(std::make_pair(fp.begin.line, fp.begin.column)); return VisitAction::WALK_CHILDREN; }, [&](const MatchExpr& me) { position.push_back(std::make_pair(me.begin.line, me.begin.column)); return VisitAction::WALK_CHILDREN; }, [&](const ConstPattern& cp) { position.push_back(std::make_pair(cp.begin.line, cp.begin.column)); return VisitAction::WALK_CHILDREN; }, [&](const WildcardPattern& wp) { position.push_back(std::make_pair(wp.begin.line, wp.begin.column)); return VisitAction::WALK_CHILDREN; }, [&](const TuplePattern& tp) { position.push_back(std::make_pair(tp.begin.line, tp.begin.column)); return VisitAction::WALK_CHILDREN; }, [&](const InterfaceBody& ib) { position.push_back(std::make_pair(ib.end.line, ib.end.column)); return VisitAction::SKIP_CHILDREN; }, [&](const Node& node) { return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(position.size() == expectPosition.size()); EXPECT_TRUE(std::equal(position.begin(), position.end(), expectPosition.begin())); } TEST_F(ParserTest, Decl) { // shared test std::vector expectIdentifiers{"main", "argc", "argv", "a", "b"}; std::vector identifiers; Walker walkerFirst(file.get(), [&identifiers](Ptr node) -> VisitAction { return match(*node)( [&identifiers](const Decl& decl) { identifiers.push_back(decl.identifier); return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walkerFirst.Walk(); EXPECT_TRUE(std::equal(identifiers.begin(), identifiers.end(), expectIdentifiers.begin())); // private test std::string classCode = R"( class A <: B & C & D { func E() {} let H = 40 init() {} } )"; Parser parser = Parser(classCode, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectMembers{"A", "E", "H", "init"}; std::vector expectRefs{"B", "C", "D"}; std::vector classMembers; std::vector classRefs; Walker walkerClass(file.get(), [&classMembers, &classRefs](Ptr node) -> VisitAction { return match(*node)( [&classMembers](const Decl& decl) { classMembers.push_back(decl.identifier); return VisitAction::WALK_CHILDREN; }, [&classRefs](const RefType& rt) { classRefs.push_back(rt.ref.identifier); return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walkerClass.Walk(); EXPECT_TRUE(std::equal(classMembers.begin(), classMembers.end(), expectMembers.begin())); EXPECT_TRUE(std::equal(classRefs.begin(), classRefs.end(), expectRefs.begin())); } TEST_F(ParserTest, InterfaceDecl) { std::string classCode = R"( interface I <: C & D{ func E() {} let H = 40 class A { } interface F <: E & F{ } } )"; std::vector expectMembers{"I", "E", "H", "A", "F"}; std::vector expectRefs{"C", "D", "E", "F"}; std::vector members; std::vector refs; Parser parser = Parser(classCode, diag, sm); OwnedPtr file = parser.ParseTopLevel(); Walker walkerClass(file.get(), [&](Ptr node) -> VisitAction { return match(*node)( [&](const Decl& decl) { members.push_back(decl.identifier); return VisitAction::WALK_CHILDREN; }, [&](const RefType& refType) { refs.push_back(refType.ref.identifier); return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walkerClass.Walk(); EXPECT_TRUE(std::equal(members.begin(), members.end(), expectMembers.begin())); EXPECT_TRUE(std::equal(refs.begin(), refs.end(), expectRefs.begin())); } TEST_F(ParserTest, EnumDecl) { std::string classCode = R"( enum TimeUnit { Year | Month | Day | Hour } enum TimeUnit { Year(Int) | Month(Int, Float) | Day(Int, Float, Float) | Hour(Int, Float, Float, Float) } main() { enum TimeUnit { Year(Int) | Month(Int, Float) | Day(Int, Float, Float) | Hour(Int, Float, Float, Float) } } )"; Parser parser = Parser(classCode, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectLits{ "Year", "Month", "Day", "Hour", "Year", "Month", "Day", "Hour", "Year", "Month", "Day", "Hour"}; std::vector lits; bool varAttr = false; Walker walkerFirst(file.get(), [&lits, &varAttr](Ptr node) -> VisitAction { return match(*node)( [&lits, &varAttr](const EnumDecl& decl) { for (auto& i : decl.constructors) { lits.push_back(i->identifier); if (Is(i.get())) { varAttr |= As(i.get())->isVar; } } return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walkerFirst.Walk(); EXPECT_TRUE(std::equal(lits.begin(), lits.end(), expectLits.begin())); EXPECT_FALSE(varAttr); } TEST_F(ParserTest, Expr) { std::vector expectLits{"123", "40", "2", "3", "3"}; std::vector lits; Walker walkerFirst(file.get(), [&lits](Ptr node) -> VisitAction { return match(*node)( [&lits](const LitConstExpr& litConstExpr) { lits.push_back(litConstExpr.stringValue); return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walkerFirst.Walk(); EXPECT_TRUE(std::equal(lits.begin(), lits.end(), expectLits.begin())); // RefExpr test std::vector expectRefExprs{"a", "print", "a", "b", "a", "b"}; std::vector refExprs; Walker walkerSecond(file.get(), [&refExprs](Ptr node) -> VisitAction { return match(*node)( [&refExprs](const RefExpr& refExpr) { refExprs.push_back(refExpr.ref.identifier); return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walkerSecond.Walk(); EXPECT_TRUE(std::equal(refExprs.begin(), refExprs.end(), expectRefExprs.begin())); // BinaryExpr std::vector expectBinaryExprs{ TokenKind::EXP, TokenKind::ADD, TokenKind::MUL, TokenKind::MUL, TokenKind::ADD}; std::vector binaryExprs; Walker walkerThird(file.get(), [&binaryExprs](Ptr node) -> VisitAction { return match(*node)( [&binaryExprs](const BinaryExpr& binaryExpr) { binaryExprs.push_back(binaryExpr.op); return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walkerThird.Walk(); EXPECT_TRUE(std::equal(binaryExprs.begin(), binaryExprs.end(), expectBinaryExprs.begin())); // UnaryExpr std::vector expectUnaryExprs{TokenKind::SUB}; std::vector unaryExprs; Walker walkerForth(file.get(), [&unaryExprs](Ptr node) -> VisitAction { return match(*node)( [&unaryExprs](const UnaryExpr& unaryExpr) { unaryExprs.push_back(unaryExpr.op); return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walkerForth.Walk(); EXPECT_TRUE(std::equal(unaryExprs.begin(), unaryExprs.end(), expectUnaryExprs.begin())); } TEST_F(ParserTest, Type) { // shared test std::vector expectRefTypes{"int", "string", "int"}; std::vector refTypes; Walker walkerFirst(file.get(), [&refTypes](Ptr node) -> VisitAction { return match(*node)( [&refTypes](const RefType& refType) { refTypes.push_back(refType.ref.identifier); return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walkerFirst.Walk(); EXPECT_TRUE(std::equal(refTypes.begin(), refTypes.end(), expectRefTypes.begin())); } TEST_F(ParserTest, InterfaceBody) { } TEST_F(ParserTest, ClassBody) { } TEST(ParserTest1, Package) { std::string code = R"( package a.b.b import e.f.* import {g.h as s, j.k} import h.{i as k, f} import m . a import x.y. )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::string expectPackages = "a.b.b"; std::vector expectedImportSpecEnd = { {0, 3, 30}, {0, 4, 34}, {0, 4, 39}, {0, 5, 34}, {0, 5, 37}, {0, 8, 2}, {0, 9, 29}}; std::vector expectedImportedItemNamePos = { {0, 3, 29}, {0, 4, 28}, {0, 4, 38}, {0, 5, 28}, {0, 5, 36}, {0, 8, 1}, {0, 9, 29}}; std::vector expectImportPackageName{"e.f", "g", "j", "h", "h", "m", "x.y"}; std::vector expectImportedItemName{"*", "h", "k", "i", "f", "a", INVALID_IDENTIFIER}; std::string packages; std::vector importSpecEnd; std::vector importedItemNamePos; std::vector importPackageName; std::vector importedItemName; Walker walker(file.get(), [&](Ptr node) -> VisitAction { return match(*node)( [&](const PackageSpec& package) { packages = package.GetPackageName(); return VisitAction::WALK_CHILDREN; }, [&](const ImportSpec& import) { if (import.IsImportMulti()) { return VisitAction::WALK_CHILDREN; } auto names = import.content.prefixPaths; importPackageName.push_back(Utils::JoinStrings(names, ".")); importedItemName.push_back(import.content.identifier); importSpecEnd.push_back(import.end); importedItemNamePos.push_back(import.content.identifier.Begin()); return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(expectPackages.begin(), expectPackages.end(), packages.begin())); EXPECT_TRUE(std::equal(expectImportPackageName.begin(), expectImportPackageName.end(), importPackageName.begin())); EXPECT_TRUE(std::equal(expectImportedItemName.begin(), expectImportedItemName.end(), importedItemName.begin())); EXPECT_TRUE(std::equal(expectedImportSpecEnd.begin(), expectedImportSpecEnd.end(), importSpecEnd.begin())); EXPECT_TRUE(std::equal( expectedImportedItemNamePos.begin(), expectedImportedItemNamePos.end(), importedItemNamePos.begin())); } TEST(ParserTest1, Modifier) { std::string code = R"( public private protected static var a = 1 abstract class a {} public interface d {} override func Main () {} )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::set expectModifier{TokenKind::PUBLIC, TokenKind::PRIVATE, TokenKind::PROTECTED, TokenKind::STATIC, TokenKind::ABSTRACT, TokenKind::PUBLIC, TokenKind::OVERRIDE}; std::set modifiers; Walker walker(file.get(), [&](Ptr node) -> VisitAction { return match(*node)( [&](const Modifier& modifier) { modifiers.insert(modifier.modifier); return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(expectModifier.begin(), expectModifier.end(), modifiers.begin())); } TEST(ParserTest1, VarDecl) { std::string code = R"( var a1 : int = 40 var a2 : Int32=40 let b = 2 ** -a let mArray1 : Array = [10.1, 20.2] let mArray2: Array> let mList1 : List = [1, 2, 3] var mList2 : List let tuplePIE = (3.14, 1, 'A', "PIE") var (x, y) = Point )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectDeclNames{ "a1", "a2", "b", "mArray1", "mArray2", "mList1", "mList2", "tuplePIE", "", "x", "y"}; std::vector expectTypeNames{"int", "Int32", "Array", "float", "Array", "Array", "Bool", "List", "int", "List", "float", "int", "float", "int", "String", "int", "rune", "Tuple"}; std::vector declNames; std::vector typeNames; Walker walker(file.get(), [&declNames, &typeNames](Ptr node) -> VisitAction { return match(*node)( [&declNames](const Decl& decl) { declNames.push_back(decl.identifier); return VisitAction::WALK_CHILDREN; }, [&typeNames](AST::Type& type) { return match(type)( [&typeNames](const RefType& type) { typeNames.push_back(type.ref.identifier); return VisitAction::WALK_CHILDREN; }, [&typeNames](const PrimitiveType& type) { typeNames.push_back(type.str); return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(declNames.begin(), declNames.end(), expectDeclNames.begin())); EXPECT_TRUE(std::equal(typeNames.begin(), typeNames.end(), expectTypeNames.begin())); } TEST(ParserTest1, MacroDecl) { std::string code = R"( external macro test(input1: Tokens): Tokens { return input1 } external macro testattr(attr: Tokens, input2: Tokens): Tokens { return attr + input2 } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectMacroNames{"test", "testattr"}; std::vector expectMacroParams{"input1", "attr", "input2"}; std::vector macroNames; std::vector macroParams; Walker walker(file.get(), [&](Ptr node) -> VisitAction { return match(*node)( [&](const FuncDecl& macroDecl) { if (macroDecl.TestAttr(Attribute::MACRO_INVOKE_FUNC)) { return VisitAction::WALK_CHILDREN; } macroNames.push_back(macroDecl.identifier); auto macroBody = macroDecl.funcBody.get(); for (auto& param : macroBody->paramLists.front()->params) { auto paramName = param.get()->identifier; macroParams.push_back(paramName); } return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(macroNames.begin(), macroNames.end(), expectMacroNames.begin())); EXPECT_TRUE(std::equal(macroParams.begin(), macroParams.end(), expectMacroParams.begin())); } TEST(ParserTest1, ClassInterfaceDecl) { std::string code = R"( class A <: B & C & D { func foo(){ } let tmp : Int32 = 40 class AA{ } interface I1 {} interface I2 <: E1.E2 & F{} } class A <: B1.B2.B3 & C & D1.D2.D3 {} interface I <: J & K1.K2.K3{ class A {} interface B{} func C() {} let D = 40 } )"; std::vector expectClassNames{"A", "B", "C", "D", "AA", "I1", "I2", "E1.E2", "F", "A", "B1.B2.B3", "C", "D1.D2.D3", "I", "J", "K1.K2.K3", "A", "B"}; std::vector expectMembers{"foo", "tmp", "AA", "I1", "I2", "A", "B", "C", "D"}; std::vector classNames, members; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); Walker walkerClass(file.get(), [&classNames, &members](Ptr node) -> VisitAction { return match(*node)( [&members](const ClassBody& body) { for (auto& decl : body.decls) { members.push_back(decl->identifier); } return VisitAction::WALK_CHILDREN; }, [&members](const InterfaceBody& body) { for (auto& decl : body.decls) { members.push_back(decl->identifier); } return VisitAction::WALK_CHILDREN; }, [&classNames](const ClassDecl& decl) { classNames.push_back(decl.identifier); return VisitAction::WALK_CHILDREN; }, [&classNames](const InterfaceDecl& decl) { classNames.push_back(decl.identifier); return VisitAction::WALK_CHILDREN; }, [&classNames](const RefType& refType) { classNames.push_back(refType.ref.identifier); return VisitAction::SKIP_CHILDREN; }, [&classNames](const QualifiedType& type) { std::function)> getName = [&](Ptr type) { return match(*type)( [&](const QualifiedType& type) { return getName(type.baseType.get()) + '.' + type.field; }, [&](const RefType& type) { return type.ref.identifier.Val(); }, []() { return std::string(""); }); }; classNames.push_back(getName(type.baseType.get()) + '.' + type.field); return VisitAction::SKIP_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walkerClass.Walk(); EXPECT_TRUE(std::equal(expectClassNames.begin(), expectClassNames.end(), classNames.begin())); EXPECT_TRUE(std::equal(expectMembers.begin(), expectMembers.end(), members.begin())); } TEST(ParserTest1, ClassDecl) { std::string code = R"( class A { func B(a: Int32, b: Int32) { return a + } func C() { } } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); unsigned int counter = 0; Walker walker(file.get(), [&counter](Ptr node) -> VisitAction { return match(*node)( [&counter](const ClassDecl& decl) { counter++; return VisitAction::WALK_CHILDREN; }, [&counter](const FuncDecl& decl) { counter++; return VisitAction::SKIP_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); unsigned int expectedNodeCount = 3; EXPECT_EQ(counter, expectedNodeCount); } TEST(ParserTest1, StructDecl) { std::string code = R"( struct Rectangle1 { var width : int var length : float } struct Rectangle2 { var width : int = 0 var length : float = 0.0 } struct Empty { } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectNames{"Rectangle1", "Rectangle2", "Empty"}; std::vector expectNums{2, 2, 0}; std::vector names; std::vector nums; Walker walker(file.get(), [&names, &nums](Ptr node) -> VisitAction { return match(*node)( [&names, &nums](const StructDecl& decl) { names.push_back(decl.identifier); nums.push_back(decl.body->decls.size()); return VisitAction::SKIP_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(expectNames.begin(), expectNames.end(), names.begin())); EXPECT_TRUE(std::equal(expectNums.begin(), expectNums.end(), nums.begin())); } TEST(ParserTest1, RecordDecl2) { std::string code = R"( struct Rect1 { var width : int var 2length : float } struct Rect2 { var 2width : int = 0 var length : float = 0.0 } struct Empty { } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto file = parser.ParseTopLevel(); std::vector expectNames{"Rect1", "Rect2", "Empty"}; std::vector names; Walker walker(file.get(), [&names](Ptr node) -> VisitAction { return match(*node)( [&names](const StructDecl& decl) { names.push_back(decl.identifier); return VisitAction::SKIP_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(expectNames.begin(), expectNames.end(), names.begin())); } TEST(ParserTest1, OperatorFunc) { std::string code = R"( extend Int32 { operator func -(b: Int32): Int32 { this + b } operator func [(b: Int32): Int32 { this + b } } class Test { operator func -(): Test { Point(-x, -y) } operator func [](): Test { Point(-x, -y) } } struct Test { operator func -(): Test { Point(-x, -y) } } interface Test { operator func -(): Test { Point(-x, -y) } } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectNames{ "-", "", "Test", "-", "[]", "Test", "-", "Test", "-", }; std::vector names; Walker walker(file.get(), [&names](Ptr node) -> VisitAction { return match(*node)( [&names](const ClassDecl& decl) { names.push_back(decl.identifier); return VisitAction::WALK_CHILDREN; }, [&names](const StructDecl& decl) { names.push_back(decl.identifier); return VisitAction::WALK_CHILDREN; }, [&names](const InterfaceDecl& decl) { names.push_back(decl.identifier); return VisitAction::WALK_CHILDREN; }, [&names](const FuncDecl& decl) { names.push_back(decl.identifier); EXPECT_TRUE(decl.TestAttr(Attribute::OPERATOR)); return VisitAction::SKIP_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(expectNames.begin(), expectNames.end(), names.begin())); const unsigned int expectedErrorCount = 1; EXPECT_EQ(diag.GetErrorCount(), expectedErrorCount); } TEST(ParserTest1, PrimitiveType) { std::string code = R"( func test() { let a : Int8 let a : Int16 let a : Int32 let a : Int64 let a : UInt8 let a : UInt16 let a : UInt32 let a : UInt64 let a : Float16 let a : Float32 let a : Float64 let a : Rune let a : Bool let a : Unit } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectTypes{"Int8", "Int16", "Int32", "Int64", "UInt8", "UInt16", "UInt32", "UInt64", "Float16", "Float32", "Float64", "Rune", "Bool", "Unit"}; std::vector types; Walker walker(file.get(), [&types](Ptr node) -> VisitAction { return match(*node)( [&types](const PrimitiveType& e) { types.push_back(e.str); return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(expectTypes.begin(), expectTypes.end(), types.begin())); } TEST(ParserTest1, IfExpr) { std::string code = R"( func test() { if (i<10) { b +=2 } if (i<10) { b +=2 } else if (i > 10) { b +=3 } else if (i >2) { } else { b += 2 } } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); int expectifnum = 4; int expectelseifnum = 2; int expectelsenum = 1; int ifnum = 0; int elsenum = 0; int elseifnum = 0; Walker walker(file.get(), [&](Ptr node) -> VisitAction { return match(*node)( [&](const IfExpr& expr) { ifnum++; if (expr.hasElse) { match (*expr.elseBody.get())( [&](const Block& block) { elsenum++; }, [&](const IfExpr& expr) { elseifnum++; }); } return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_EQ(expectifnum, ifnum); EXPECT_EQ(expectelsenum, elsenum); EXPECT_EQ(expectelseifnum, elseifnum); } TEST(ParserTest1, QuoteExpr1) { std::string code = R"( func test() { quote(1 + 2 + 3 \( \)) quote($(ast) + $ast + ast + \$a) } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); int expectbinarynum = 0; int expectcallsnum = 0; int expectrefsnum = 2; int expectmemberaccessnum = 0; int binarynum = 0; int callnum = 0; int refnum = 0; int memberaccessnum = 0; Walker walker(file.get(), [&](Ptr node) -> VisitAction { return match(*node)( [&](const BinaryExpr& expr) { binarynum++; return VisitAction::WALK_CHILDREN; }, [&](const CallExpr& expr) { callnum++; return VisitAction::WALK_CHILDREN; }, [&](const RefExpr& expr) { refnum++; return VisitAction::WALK_CHILDREN; }, [&](const MemberAccess& ma) { memberaccessnum++; return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_EQ(expectbinarynum, binarynum); EXPECT_EQ(expectcallsnum, callnum); EXPECT_EQ(expectrefsnum, refnum); EXPECT_EQ(expectmemberaccessnum, memberaccessnum); } TEST(ParserTest1, QuoteExpr2) { std::string code = R"( macro package p macro test(input : Tokens): Tokens { let tokens : Tokens = quote(1 + 2 + 3) return quote(func test () : void { a \( \) } ) } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); int expectquotenum = 2; size_t expecttokensnum = 5; size_t expectreturntoken = 16; int expectbinarynum = 0; int binarynum = 0; int quotenum = 0; size_t tokensnum = 0; size_t returntoken = 0; Walker walker(file.get(), [&](Ptr node) -> VisitAction { return match(*node)( [&](const BinaryExpr& expr) { binarynum++; return VisitAction::WALK_CHILDREN; }, [&](const VarDecl& decl) { if (decl.initializer) { match (*decl.initializer)([&](const QuoteExpr& expr) { quotenum++; if (!expr.exprs.empty() && expr.exprs[0]->astKind == ASTKind::TOKEN_PART) { tokensnum = RawStaticCast(expr.exprs[0].get())->tokens.size(); } }); } return VisitAction::WALK_CHILDREN; }, [&](const ReturnExpr& retExpr) { if (retExpr.expr) { match (*retExpr.expr)([&](const QuoteExpr& expr) { quotenum++; if (!expr.exprs.empty() && expr.exprs[0]->astKind == ASTKind::TOKEN_PART) { returntoken = RawStaticCast(expr.exprs[0].get())->tokens.size(); } }); } return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_EQ(expectquotenum, quotenum); EXPECT_EQ(expecttokensnum, tokensnum); EXPECT_EQ(expectreturntoken, returntoken); EXPECT_EQ(expectbinarynum, binarynum); } TEST(ParserTest1, QuoteExpr3) { std::string code = R"( public macro test(input : Tokens): Tokens { let b : Expr = CangjieLex("1 + 2 + 3") let c = 3 return quote(func test () : void { \( \$ \$ b \$b $b $(c + 1) } ) } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); int expectquotenum = 1; int expectexprsnum = 5; int quotenum = 0; int exprsnum = 0; Walker walker(file.get(), [&](Ptr node) -> VisitAction { return match(*node)( [&](const ReturnExpr& retExpr) { if (retExpr.expr) { match (*retExpr.expr)([&](const QuoteExpr& expr) { quotenum++; exprsnum = expr.exprs.size(); }); } return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_EQ(expectquotenum, quotenum); EXPECT_EQ(expectexprsnum, exprsnum); } TEST(ParserTest1, ThrowExpr) { std::string code = R"( func test() { throw IllegalArgumentException() } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); int expectthrownum = 1; int thrownum = 0; Walker walker(file.get(), [&](Ptr node) -> VisitAction { return match(*node)( [&](const ThrowExpr& expr) { thrownum++; return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_EQ(expectthrownum, thrownum); } TEST(ParserTest1, StrInterpolationExpr) { std::string code = R"( func test() { var a = "1" var b = "${a}" var c = "${a.}" // For lsp } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); int expectStrIntnum = 2; int strIntnum = 0; bool isClonedSourceCode = false; Walker walker(file.get(), [&](Ptr node) -> VisitAction { return match(*node)([&](const StrInterpolationExpr& expr) { return VisitAction::WALK_CHILDREN; }, [&](const InterpolationExpr& expr) { if (strIntnum == 0) { EXPECT_EQ(expr.begin.column, 22); EXPECT_EQ(expr.end.column, 26); } else if (strIntnum == 1) { EXPECT_EQ(expr.end.column, 27); } strIntnum++; return VisitAction::WALK_CHILDREN; }, [&]() { if (node->TestAttr(Attribute::IS_CLONED_SOURCE_CODE)) { isClonedSourceCode = true; } return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_EQ(expectStrIntnum, strIntnum); EXPECT_TRUE(isClonedSourceCode); } TEST(ParserTest1, TryExpr) { std::string code = R"( func test() { try { try { throw IllegalArgumentException() } catch _:IllegalArgumentException | IndexOutOfBoundsException { b+=2 throw IndexOutOfBoundsException() } } catch e:IllegalArgumentException | IndexOutOfBoundsException { b+=2 } catch _ { b+=2 } finally { b+=2 } } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); int expecttrynum = 2; int expectcatchnum = 3; int expectfinallynum = 1; int trynum = 0; int catchnum = 0; int finallynum = 0; Walker walker(file.get(), [&](Ptr node) -> VisitAction { return match(*node)( [&](const TryExpr& expr) { trynum++; if (!expr.catchBlocks.empty()) { for (size_t cnt = 0; cnt < expr.catchPatterns.size(); cnt++) { catchnum++; } } if (expr.finallyBlock) { finallynum++; } return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_EQ(expecttrynum, trynum); EXPECT_EQ(expectcatchnum, catchnum); EXPECT_EQ(expectfinallynum, finallynum); } TEST(ParserTest1, TryExpr1) { std::string code = R"( func test () { try { let lastElement = arrayTest[3] } catch (e: ArithmeticException | IndexOutOfBoundsException) { print("exception info: " + e) } catch (_) { print("neither ArithmeticException nor ArrayIndexOutOfBoundsException, exception info: " + e) } finally { print("the finally block is executed") } } func test () { try (input : File= FileInputStream("input.txt"), output = FileOutputStream("output.txt")) { let lineString = input.readLine() } catch (e : IOException) { print("IOException happened when executing the try-with-resources expression") } finally { print("end of the try-with-resources expression") } } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); int tryBlock = 0; size_t catchBlock = 0; int finallyBlock = 0; int tryBlockExpect = 2; int catchBlockExpect = 3; int finallyBlockExpect = 2; Walker walker(file.get(), [&](Ptr node) -> VisitAction { return match(*node)( [&](const TryExpr& expr) { if (expr.tryBlock) { tryBlock += 1; } catchBlock += expr.catchBlocks.size(); if (expr.finallyBlock) { finallyBlock += 1; } return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_EQ(tryBlock, tryBlockExpect); EXPECT_EQ(catchBlock, catchBlockExpect); EXPECT_EQ(finallyBlock, finallyBlockExpect); } TEST(ParserTest1, PatternMatch) { std::string code = R"( var scoreResult: string = match (score) { case who => "zero" case 10 => "fail" case PASS => "pass" case _ => } /*var scoreResult: String = match (scoreList) { case List{50,...} => "the lowest score is 50" }*/ var rectangleArea2: int = match (colorPo) { case cp : Point => "pass" } var result: String = match { case score < 60 => "fail" case score < 70 => "pass" case score < 90 => "good" case _ => "excellent" } var howManyHours: float = match (oneYear) { case Year(y) => y*365*24 // enum + tuple + va + var case Year(y) | Month(y, z) => y*365*24 case Year(y) | Month(y, z) | Day(y, z, x) => y*365*24 } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); int varPattern = 0; int constPattern = 0; int wildcardPattern = 0; int tuplePattern = 0; int typePattern = 0; int enumPattern = 0; int varPatternExpect = 1; int constPatternExpect = 1; int wildcardPatternExpect = 2; int tuplePatternExpect = 0; int typePatternExpect = 1; int enumPatternEexpct = 6; Walker walker(file.get(), [&](Ptr node) -> VisitAction { return match(*node)( [&](const ConstPattern& pattern) { constPattern++; return VisitAction::WALK_CHILDREN; }, [&](const VarPattern& pattern) { varPattern++; return VisitAction::WALK_CHILDREN; }, [&](const WildcardPattern& pattern) { wildcardPattern++; return VisitAction::WALK_CHILDREN; }, [&](const TuplePattern& pattern) { tuplePattern++; return VisitAction::WALK_CHILDREN; }, [&](const TypePattern& pattern) { typePattern++; return VisitAction::WALK_CHILDREN; }, [&](const EnumPattern& pattern) { enumPattern++; return VisitAction::WALK_CHILDREN; }, [&](const WildcardExpr& wildcard) { wildcardPattern++; return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_EQ(varPattern, varPatternExpect); EXPECT_EQ(constPattern, constPatternExpect); EXPECT_EQ(wildcardPattern, wildcardPatternExpect); EXPECT_EQ(tuplePattern, tuplePatternExpect); EXPECT_EQ(typePattern, typePatternExpect); EXPECT_EQ(enumPattern, enumPatternEexpct); } TEST(ParserTest1, ForInExpr) { std::string code = R"( main() { for (item in scoreList) { print(item) } for (item in scoreList where item > 50) { print(item) } for (_ in scoreList) { print("got a score") } } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectPatternLists{"VarPattern", "VarPattern", "WildcardPattern"}; std::vector expectPatternGuardLists{"BinaryExpr"}; std::vector patternLists, patternGuardLists; Walker walker(file.get(), [&patternLists, &patternGuardLists](Ptr node) -> VisitAction { return match(*node)( [&patternLists, &patternGuardLists](const ForInExpr& expr) { match (*expr.pattern.get())( [&patternLists](const VarPattern& e) { patternLists.push_back("VarPattern"); }, [&patternLists](const WildcardPattern& e) { patternLists.push_back("WildcardPattern"); }); if (expr.patternGuard) { match (*expr.patternGuard.get())( [&patternGuardLists](const BinaryExpr& e) { patternGuardLists.push_back("BinaryExpr"); }); } return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(patternLists.begin(), patternLists.end(), expectPatternLists.begin())); EXPECT_TRUE(std::equal(patternGuardLists.begin(), patternGuardLists.end(), expectPatternGuardLists.begin())); } TEST(ParserTest1, WhileExpr) { std::string code = R"( func test() { while ((i<10)) { a=b } while (()) { b +=2 } while (i<10) { b = a.c } while { // error return 0 } } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectCondLists{"ParenExpr", "LitConstExpr", "BinaryExpr"}; std::vector condLists; Walker walker(file.get(), [&condLists](Ptr node) -> VisitAction { return match(*node)( [&condLists](const WhileExpr& e) { match (*e.condExpr.get())( [&condLists](const BinaryExpr& expr) { condLists.push_back(std::string("BinaryExpr")); }, [&condLists](const ParenExpr& expr) { condLists.push_back(std::string("ParenExpr")); }, [&condLists](const LitConstExpr& expr) { condLists.push_back(std::string("LitConstExpr")); }); return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(condLists.begin(), condLists.end(), expectCondLists.begin())); } TEST(ParserTest1, DoWhileExpr) { std::string code = R"( func test() { do { b +=2 } while ((x < 100)) do { b +=2 } while x < 100 do { b +=2 } while (()) } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectCondLists{"ParenExpr", "BinaryExpr"}; std::vector condLists; Walker walker(file.get(), [&condLists](Ptr node) -> VisitAction { return match(*node)( [&condLists](const DoWhileExpr& expr) { match (*expr.condExpr.get())( [&condLists](const ParenExpr& expr) { condLists.push_back(std::string("ParenExpr")); }, [&condLists](const BinaryExpr& expr) { condLists.push_back(std::string("BinaryExpr")); }); return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(condLists.begin(), condLists.end(), expectCondLists.begin())); } TEST(ParserTest1, AssignExpr) { std::string code = R"( func test() { A = expr A.B.C = expr A[0][1][2] = expr A.B[2].C.D[3] = expr A.B[0].C = expr A[0][1][1][2] = expr A.B[0][0].C[1] = expr this.A = expr this.A.B.C = expr this.A[0][1][2] = expr this.A.B[2].C.D[3] = expr this.A.B[0].C = expr this.A[0][1][1][2] = expr } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectLeftValues{"RefExpr", "MemberAccess", "SubscriptExpr", "SubscriptExpr", "MemberAccess", "SubscriptExpr", "SubscriptExpr", "MemberAccess", "MemberAccess", "SubscriptExpr", "SubscriptExpr", "MemberAccess", "SubscriptExpr"}; std::vector leftValues; Walker walker(file.get(), [&leftValues](Ptr node) -> VisitAction { return match(*node)( [&leftValues](const AssignExpr& e) { return match(*e.leftValue)( [&leftValues](const RefExpr&) { leftValues.emplace_back("RefExpr"); return VisitAction::SKIP_CHILDREN; }, [&leftValues](const MemberAccess&) { leftValues.emplace_back("MemberAccess"); return VisitAction::SKIP_CHILDREN; }, [&leftValues](const SubscriptExpr&) { leftValues.emplace_back("SubscriptExpr"); return VisitAction::SKIP_CHILDREN; }, []() { return VisitAction::STOP_NOW; }); }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(expectLeftValues.begin(), expectLeftValues.end(), leftValues.begin())); } TEST(ParserTest1, IncOrDecExpr) { std::string code = R"( func test() { a ++ a = a -- } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectOps{"++", "--"}; std::vector ops; Walker walker(file.get(), [&ops](Ptr node) -> VisitAction { return match(*node)( [&ops](const IncOrDecExpr& e) { std::string str = (e.op == TokenKind::INCR) ? "++" : "--"; ops.push_back(str); return VisitAction::SKIP_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(ops.begin(), ops.end(), expectOps.begin())); } TEST(ParserTest1, CallExpr) { std::string code = R"( func test() { a = test1(); b = test2(a, 1, a+b) c = test3(a:1, a:a+2) d = test4(if (()){}, while (1){}) aa = test11()(); bb = test22(aa)(bb:1)(cc:2); let a = Some(12) } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectNumParams{0, 3, 2, 2, 0, 1, 1}; std::vector expectNumExprs{1, 1, 1, 1, 2, 3, 1}; std::vector expectExprNames{"RefExpr", "LitConstExpr", "BinaryExpr", "LitConstExpr", "BinaryExpr", "IfExpr", "WhileExpr", "LitConstExpr", "LitConstExpr"}; std::vector numParams; std::vector numExprs; std::vector exprNames; Walker walker(file.get(), [&numParams, &numExprs, &exprNames](Ptr node) -> VisitAction { return match(*node)( [&numParams, &numExprs, &exprNames](const CallExpr& e) { if (Is(e.baseFunc.get())) { std::string funcName = RawStaticCast(e.baseFunc.get())->ref.identifier; } numParams.push_back(e.args.size()); for (auto& it : e.args) { match (*it->expr.get())([&exprNames](const RefExpr& e) { exprNames.push_back("RefExpr"); }, [&exprNames](const LitConstExpr& e) { exprNames.push_back("LitConstExpr"); }, [&exprNames](const BinaryExpr& e) { exprNames.push_back("BinaryExpr"); }, [&exprNames](const AssignExpr& e) { exprNames.push_back("AssignExpr"); }, [&exprNames](const IfExpr& e) { exprNames.push_back("IfExpr"); }, [&exprNames](const WhileExpr& e) { exprNames.push_back("WhileExpr"); }, []() {}); } std::function)> getNum = [&](int n, Ptr expr) { return match(*expr)( [&](const CallExpr& e) { return getNum(n, e.baseFunc.get()) + 1; }, [&]() { return n; }); }; numExprs.push_back(getNum(1, e.baseFunc.get())); return VisitAction::SKIP_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(expectNumParams.begin(), expectNumParams.end(), numParams.begin())); EXPECT_TRUE(std::equal(expectExprNames.begin(), expectExprNames.end(), exprNames.begin())); EXPECT_TRUE(std::equal(expectNumExprs.begin(), expectNumExprs.end(), numExprs.begin())); } TEST(ParserTest1, MacroExpandExpr) { std::string code = R"( main(): Int64 { @test1(Image1("company1.png")) @test2 Image2("company2.png") return 0 } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectMacroExpandNames{"test1", "test2"}; std::vector expectMacroExpandArgs{ "Image1", "(", "company1.png", ")", "Image2", "(", "company2.png", ")"}; std::vector macroExpandNames; std::vector macroExpandArgs; Walker walker(file.get(), [&](Ptr node) -> VisitAction { return match(*node)( [&](const MacroExpandExpr& mee) { macroExpandNames.push_back(mee.identifier); for (auto& it : mee.invocation.args) { macroExpandArgs.push_back(it.Value()); } return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(macroExpandNames.begin(), macroExpandNames.end(), expectMacroExpandNames.begin())); EXPECT_TRUE(std::equal(macroExpandArgs.begin(), macroExpandArgs.end(), expectMacroExpandArgs.begin())); } TEST(ParserTest1, MacroExpandDecl) { std::string code = R"( @test var a = 1 @testattr[attribute token] main() { } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectMacroNames{"test", "testattr"}; std::vector expectMacroArgs{"var", "a", "=", "1", "main", "(", ")", "{", "}"}; std::vector expectMacroAttrs{"attribute", "token"}; std::vector macroNames; std::vector macroArgs; std::vector macroAttrs; Walker walker(file.get(), [&](Ptr node) -> VisitAction { return match(*node)( [&](const MacroExpandDecl& ma) { macroNames.push_back(ma.identifier.Val()); for (auto& it : ma.invocation.args) { macroArgs.push_back(it.Value()); } for (auto& it : ma.invocation.attrs) { macroAttrs.push_back(it.Value()); } return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(macroNames.begin(), macroNames.end(), expectMacroNames.begin())); EXPECT_TRUE(std::equal(macroArgs.begin(), macroArgs.end(), expectMacroArgs.begin())); EXPECT_TRUE(std::equal(macroAttrs.begin(), macroAttrs.end(), expectMacroAttrs.begin())); } TEST(ParserTest1, MacroExpandInterStr) { std::string code = R"( @M(println("${xx}")) )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector originTokens; std::vector expOriginTokens = {"(0, 2, 27)", "(0, 2, 23)", "(0, 2, 20)", "(0, 2, 19)", "(0, 2, 12)"}; Walker walker(file.get(), [&](Ptr node) -> VisitAction { return match(*node)( [&](const MacroExpandDecl& macroExpand) { for (auto& it : macroExpand.invocation.originPosMap) { originTokens.emplace_back(it.second.ToString()); } return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(originTokens.begin(), originTokens.end(), expOriginTokens.begin())); } TEST(ParserTest1, PostfixUnaryExpr) { std::string code = R"( func test() { b = b.c.d.e c = c[1][2][3][4] d = test(bb:1)(cc=2); } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectNums{3, 4, 2}; std::vector nums; Walker walker(file.get(), [&nums](Ptr node) -> VisitAction { return match(*node)( [&nums](const MemberAccess& e) { std::function)> getNum = [&](int n, Ptr expr) { return match(*expr)( [&](const MemberAccess& e) { return getNum(n, e.baseExpr.get()) + 1; }, [&]() { return n; }); }; nums.push_back(getNum(1, e.baseExpr.get())); return VisitAction::SKIP_CHILDREN; }, [&nums](const SubscriptExpr& e) { std::function)> getNum = [&](int n, Ptr expr) { return match(*expr)( [&](const SubscriptExpr& e) { return getNum(n, e.baseExpr.get()) + 1; }, [&]() { return n; }); }; nums.push_back(getNum(1, e.baseExpr.get())); return VisitAction::SKIP_CHILDREN; }, [&nums](const CallExpr& e) { std::function)> getNum = [&](int n, Ptr expr) { return match(*expr)( [&](const CallExpr& e) { return getNum(n, e.baseFunc.get()) + 1; }, [&]() { return n; }); }; nums.push_back(getNum(1, e.baseFunc.get())); return VisitAction::SKIP_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(expectNums.begin(), expectNums.end(), nums.begin())); } TEST(ParserTest1, RangeExpr) { // included or nested. std::string code = R"( main() : Int { e1..e2 e1..e2:e3 e1..:e3 1..10:3&1 1..20:3&&2..10:2 } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector> expectExprLists{ {"RangeExpr"}, {"RangeExpr"}, {"RangeExpr"}, {"BinaryExpr", "RangeExpr"}, {"BinaryExpr", "RangeExpr", "RangeExpr"}, }; std::vector> exprLists; for (auto& expr : RawStaticCast(file->decls[0].get())->funcBody->body->body) { std::vector exprList; Walker walker(expr.get(), [&exprList](Ptr node) -> VisitAction { return match(*node)( [&exprList](const RangeExpr& e) { exprList.push_back("RangeExpr"); return VisitAction::WALK_CHILDREN; }, [&exprList](const BinaryExpr& e) { exprList.push_back("BinaryExpr"); return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); exprLists.push_back(exprList); } EXPECT_TRUE(std::equal(exprLists.begin(), exprLists.end(), expectExprLists.begin())); } TEST(ParserTest1, RangeExprInBinaryExpr) { std::string code = R"( main() : Int { let a = 1 || 2 ..= 10 : 1 + 1 let b = ! ! - 9___E3__0 >= ! continue && - - super ..= - 0Xf.6_b_P7 : super == ! - super - - - super is Unit } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); bool missingOneStep = false; Walker walker(file.get(), [&missingOneStep](Ptr node) -> VisitAction { return match(*node)( [&missingOneStep](const RangeExpr& re) { if (!re.stepExpr) { missingOneStep = true; } return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); ASSERT_TRUE(!missingOneStep); ASSERT_TRUE(diag.GetErrorCount() == 0); } TEST(ParserTest1, MultiParenExpr) { std::string code = R"( main() { var hOut = 0 let strideH = 0 let dilationH = 0 let kernelSizeH = 0 var padNeededH = (hOut - 1) * strideH + dilationH * (kernelSizeH - 1) } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); unsigned int expectErrorCount = 0; EXPECT_EQ(diag.GetErrorCount(), expectErrorCount); } TEST(ParserTest1, CurriedFunction) { std::string code = R"( func test():unit{} func test(a:int, b:int=1): int {return a+b} func test()() {} func test(a:int)(b:int): int {return a+b} )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectNums{1, 1, 2, 2}; std::vector nums; Walker walker(file.get(), [&nums](Ptr node) -> VisitAction { return match(*node)( [&nums](const FuncBody& e) { nums.push_back(e.paramLists.size()); return VisitAction::SKIP_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(expectNums.begin(), expectNums.end(), nums.begin())); } TEST(ParserTest1, LambdaExpr) { std::string code = R"( func test() { {=>} {=>} () {a=>1} {a=>} {a:Int32, b => } {a, b, c, d:Int32, e => } {a, b, c, d, e => } {a, b, c, d, e => } (a, b, c, d, e) var sum = {a:int, b => a+b} } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectExprs{"LambdaExpr", "LambdaExpr", "LitConstExpr", "LambdaExpr", "LambdaExpr", "LambdaExpr", "LambdaExpr", "LambdaExpr", "LambdaExpr", "TupleLit", "LambdaExpr"}; std::vector exprs; Walker walker(file.get(), [&exprs](Ptr node) -> VisitAction { return match(*node)( [&exprs](const LambdaExpr& e) { exprs.push_back("LambdaExpr"); return VisitAction::SKIP_CHILDREN; }, [&exprs](const LitConstExpr& e) { exprs.push_back("LitConstExpr"); return VisitAction::SKIP_CHILDREN; }, [&exprs](const TupleLit& e) { exprs.push_back("TupleLit"); return VisitAction::SKIP_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(expectExprs.begin(), expectExprs.end(), exprs.begin())); } TEST(ParserTest1, TypeConvExpr) { std::string code = R"( func test() { Int8(Int32(a)) Float64(Int8(Float32(a))) // Int8 is acutally a CallExpr } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectExprNames{"Int8", "Int32", "Float64", "Int8", "Float32"}; std::vector exprNames; Walker walker(file.get(), [&exprNames](Ptr node) -> VisitAction { return match(*node)( [&exprNames](const TypeConvExpr& e) { exprNames.push_back(e.type->ToString()); return VisitAction::WALK_CHILDREN; }, [&exprNames](const CallExpr& e) { std::function)> getName = [&](Ptr expr) { return match(*expr)([&](const CallExpr& e) { return getName(e.baseFunc.get()); }, [&](const RefExpr& e) { return e.ref.identifier.Val(); }, []() { return std::string(""); }); }; exprNames.push_back(getName(e.baseFunc.get())); return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(expectExprNames.begin(), expectExprNames.end(), exprNames.begin())); } TEST(ParserTest1, Comparisons) { std::string code = R"( func test() { a >= b ==c f // '!=e > f' is invalid and ignored a != b +c== d // '==d' is invalid and ignored a > b ==c+d<=e } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); std::vector expectNames{"a", "b", "c", "d", "e", "f", "a", "b", "c", "d", "a", "b", "c", "d", "e"}; std::vector names; Walker walker(file.get(), [&names](Ptr node) -> VisitAction { return match(*node)( [&names](const RefExpr& e) { names.push_back(e.ref.identifier); return VisitAction::SKIP_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_TRUE(std::equal(expectNames.begin(), expectNames.end(), names.begin())); } TEST(ParserTest1, IsAsExpr) { std::string code = R"( let a = continue is Unit - () let a = () as Unit - 1 )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); bool wrongType = false; Walker walker(file.get(), [&wrongType](Ptr node) -> VisitAction { return match(*node)( [&wrongType](const IsExpr& ie) { if (auto pt = AST::As(ie.isType.get()); pt) { if (pt->kind != TypeKind::TYPE_UNIT) { wrongType = true; } } else { wrongType = true; } return VisitAction::WALK_CHILDREN; }, [&wrongType](const AsExpr& ie) { if (auto pt = AST::As(ie.asType.get()); pt) { if (pt->kind != TypeKind::TYPE_UNIT) { wrongType = true; } } else { wrongType = true; } return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); auto errorCount = diag.GetErrorCount(); EXPECT_EQ(errorCount, 1); EXPECT_TRUE(!wrongType); } TEST(ParserTest1, OpExprs) { std::string code = R"( func test() { a = -+-2 + -(2+a) b = -1 + a- -+- a c = a + (if (b) {1} else {3}) * (c * (d + 2)) d = b + (if (c*a) {d*3} else {e}) + f**4 e = -1 + a << 2 >>3 <=aaa f = if (a) {if (b) {c} else {d}} else {e} } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); } TEST(ParserTest1, Generics) { std::string code = R"( func Add(a: T, b: T) where T <: C {a + b} class Class where T <: C { var c: T } interface Interface where T <: C { var c: T } struct Struct where T <: C { var c: T } enum Enum where T <: C {a} type Type = C main() { Add(1.0, 2.0) var r: Struct var e: Enum = Enum.a var ee: Enum = Enum.a(1, 2) var eee: Enum = Enum2.aaa(1, 2, 3, 4) var eeee: Enum = Enum3.fff } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); int checkNum = 0; Walker walker(file.get(), [&checkNum](Ptr node) -> VisitAction { return match(*node)( [&checkNum](const FuncDecl& f) { if (f.identifier == "Add") { EXPECT_EQ(f.funcBody->generic->typeParameters.size(), 1); EXPECT_EQ(f.funcBody->generic->typeParameters[0]->identifier, "T"); auto& params = f.funcBody->paramLists[0]->params; EXPECT_EQ(params[0]->identifier, "a"); EXPECT_EQ(As(params[0]->type.get())->ref.identifier, "T"); EXPECT_EQ(params[1]->identifier, "b"); EXPECT_EQ(As(params[1]->type.get())->ref.identifier, "T"); auto& genericConstraints = f.funcBody->generic->genericConstraints[0]; EXPECT_EQ(genericConstraints->type->ref.identifier, "T"); EXPECT_EQ( As(genericConstraints->upperBounds.begin()->get())->ref.identifier, "C"); ++checkNum; } return VisitAction::WALK_CHILDREN; }, [&checkNum](const ClassDecl& c) { EXPECT_EQ(c.identifier, "Class"); EXPECT_EQ(c.generic->typeParameters.size(), 1); EXPECT_EQ(c.generic->typeParameters[0]->identifier, "T"); auto& genericConstraints = c.generic->genericConstraints; EXPECT_EQ(genericConstraints.size(), 1); EXPECT_EQ(c.generic->genericConstraints[0]->type->ref.identifier, "T"); EXPECT_EQ( As(genericConstraints[0]->upperBounds.begin()->get())->ref.identifier, "C"); EXPECT_EQ(c.body->decls.size(), 1); auto varDecl = As(c.body->decls[0].get()); EXPECT_EQ(As(varDecl->type.get())->ref.identifier, "T"); ++checkNum; return VisitAction::WALK_CHILDREN; }, [&checkNum](const InterfaceDecl& i) { EXPECT_EQ(i.identifier, "Interface"); EXPECT_EQ(i.generic->typeParameters.size(), 1); EXPECT_EQ(i.generic->typeParameters[0]->identifier, "T"); auto& genericConstraints = i.generic->genericConstraints; EXPECT_EQ(genericConstraints.size(), 1); EXPECT_EQ(i.generic->genericConstraints[0]->type->ref.identifier, "T"); EXPECT_EQ( As(genericConstraints[0]->upperBounds.begin()->get())->ref.identifier, "C"); EXPECT_EQ(i.body->decls.size(), 1); auto varDecl = As(i.body->decls[0].get()); EXPECT_EQ(As(varDecl->type.get())->ref.identifier, "T"); ++checkNum; return VisitAction::WALK_CHILDREN; }, [&checkNum](const StructDecl& r) { EXPECT_EQ(r.identifier, "Struct"); EXPECT_EQ(r.generic->typeParameters.size(), 1); EXPECT_EQ(r.generic->typeParameters[0]->identifier, "T"); auto& genericConstraints = r.generic->genericConstraints; EXPECT_EQ(genericConstraints.size(), 1); EXPECT_EQ(r.generic->genericConstraints[0]->type->ref.identifier, "T"); EXPECT_EQ( As(genericConstraints[0]->upperBounds.begin()->get())->ref.identifier, "C"); EXPECT_EQ(r.body->decls.size(), 1); auto varDecl = As(r.body->decls[0].get()); EXPECT_EQ(As(varDecl->type.get())->ref.identifier, "T"); ++checkNum; return VisitAction::WALK_CHILDREN; }, [&checkNum](const EnumDecl& e) { EXPECT_EQ(e.identifier, "Enum"); EXPECT_EQ(e.generic->typeParameters.size(), 1); EXPECT_EQ(e.generic->typeParameters[0]->identifier, "T"); auto& genericConstraints = e.generic->genericConstraints; EXPECT_EQ(genericConstraints.size(), 1); EXPECT_EQ(e.generic->genericConstraints[0]->type->ref.identifier, "T"); EXPECT_EQ( As(genericConstraints[0]->upperBounds.begin()->get())->ref.identifier, "C"); ++checkNum; return VisitAction::WALK_CHILDREN; }, [&checkNum](const TypeAliasDecl& t) { EXPECT_EQ(t.identifier, "Type"); EXPECT_EQ(t.generic->typeParameters.size(), 1); EXPECT_EQ(t.generic->typeParameters[0]->identifier, "T"); EXPECT_EQ(As(t.type.get())->ref.identifier, "C"); ++checkNum; return VisitAction::WALK_CHILDREN; }, [&checkNum](const CallExpr& c) { if (auto re = AST::As(c.baseFunc.get()); re) { EXPECT_EQ(re->ref.identifier, "Add"); EXPECT_EQ(re->typeArguments.size(), 1); EXPECT_EQ(As(re->typeArguments[0].get())->kind, TypeKind::TYPE_FLOAT64); ++checkNum; } if (auto ma = AST::As(c.baseFunc.get()); ma) { if (ma->typeArguments.empty()) { if (auto re = AST::As(ma->baseExpr.get()); re) { EXPECT_EQ(re->ref.identifier, "Enum"); EXPECT_EQ(re->typeArguments.size(), 1); EXPECT_EQ( As(re->typeArguments[0].get())->kind, TypeKind::TYPE_INT64); EXPECT_EQ(c.args.size(), 2); ++checkNum; } } else { if (auto re = AST::As(ma->baseExpr.get()); re) { EXPECT_EQ(re->ref.identifier, "Enum2"); EXPECT_EQ(re->typeArguments.size(), 2); EXPECT_EQ( As(re->typeArguments[0].get())->kind, TypeKind::TYPE_FLOAT32); EXPECT_EQ(ma->field, "aaa"); EXPECT_EQ(ma->typeArguments.size(), 3); EXPECT_EQ( As(ma->typeArguments[0].get())->kind, TypeKind::TYPE_INT32); EXPECT_EQ(c.args.size(), 4); ++checkNum; } } } return VisitAction::WALK_CHILDREN; }, [&checkNum](const MemberAccess& ma) { if (auto re = AST::As(ma.baseExpr.get()); re) { if (re->ref.identifier == "Enum3") { EXPECT_EQ(re->typeArguments.size(), 2); EXPECT_EQ(As(re->typeArguments[0].get())->kind, TypeKind::TYPE_INT64); EXPECT_EQ(ma.field, "fff"); EXPECT_EQ(ma.typeArguments.size(), 4); EXPECT_EQ(As(ma.typeArguments[0].get())->kind, TypeKind::TYPE_FLOAT64); ++checkNum; } } return VisitAction::WALK_CHILDREN; }, [&checkNum](const VarDecl& vd) { if (vd.identifier == "r") { auto refType = As(vd.type.get()); EXPECT_EQ(refType->ref.identifier, "Struct"); EXPECT_EQ(refType->typeArguments.size(), 1); EXPECT_EQ(As(refType->typeArguments[0].get())->kind, TypeKind::TYPE_INT64); ++checkNum; } if (vd.identifier == "e") { auto refType = As(vd.type.get()); EXPECT_EQ(refType->ref.identifier, "Enum"); EXPECT_EQ(refType->typeArguments.size(), 1); EXPECT_EQ( As(refType->typeArguments[0].get())->kind, TypeKind::TYPE_UINT64); ++checkNum; } return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); EXPECT_EQ(checkNum, 12); EXPECT_EQ(1, diag.GetErrorCount()); } TEST(ParserTest1, Return) { std::string code = R"( func foo(): unit { return let a = 0 } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); Walker walker(file.get(), [](Ptr node) -> VisitAction { return match(*node)( [](const ReturnExpr& retExpr) { EXPECT_TRUE(As(retExpr.expr.get())); return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); }); walker.Walk(); } TEST(ParserPrefixTest, ParseMinusLitConst) { std::string codes[] = {R"(-10)", R"(- 10)", R"(-0x01)", R"(- 0o1)", R"(-0b01)", R"(-0.01)", R"(- 0x0.1p2)"}; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); for (std::string& code : codes) { Parser parser(code, diag, sm); OwnedPtr expr = parser.ParseExpr(); EXPECT_TRUE(expr->astKind == ASTKind::LIT_CONST_EXPR); } } TEST(ParserTestDemo, Demo1) { std::string code = R"( func Print(s : String) : unit {} func Sleep(t : int) : unit {} main() : int { while (true) { Print("Hello Rune Lang\n") Sleep(2000) } return 0 } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); } TEST(ParserTestDemo, Demo2) { std::string code = R"( let clockPort = 12 let dataPort = 5 let ledNum = 64 // led number let lightColor = 0xffff0000 // led light color -> b: 255, g: 0, r: 0 // for LED show var pos : int = 0 // LED position var leds : int[] // c libary api ======= fake FFI func print() : unit {} func print(str : String) : unit {} func sleep(inv : int) : unit {} func OpenGPIO(pin : int) : unit {} func WriteGPIO(pin : int, val : int) : unit {} func SetWord(clkPort : int, dataPort : int, val : int) : unit {} // Util function func CDW(val : int) : unit { SetWord(clockPort, dataPort, val) } // Set the global array func SetChaserPattern() : unit { leds[pos] = lightColor pos = (pos + 1) % ledNum; // for (var i = 0; i < ledNum; i++) { // leds[i] *= 0.6 // if no float, what should we do here? // } } // Show LED: Right Shift Zero func ShowLED(leds : int, lightPWM : int) : unit { CDW(0) lightPWM = 0xFF000000 // for (var i = 0; i < ledNum; i++) { // let buf = lightPWM | leds[i] // CDW(buf) // } CDW(0xffffffff) } func StartChaserMode() { while (true) { SetChaserPattern() ShowLED(leds, 0xFF000000) sleep(50) // fake sleep } } main() : int { print("hello world") // Initialize GPIO OpenGPIO(clockPort) WriteGPIO(clockPort, 1) OpenGPIO(dataPort) // Initialize LEDS // for (var i = 0; i < ledNum; i++) { // leds[i] = 0x00000000; // } // Show LED print("Start Marquee...") StartChaserMode() return 0 } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); } TEST(ParserTestError, ErrorPrint) { std::string code = R"( public static func Main() { } open public var a = 1 default override class a { } default static interface a {} if (){} // line = 8 var a : int = 1 class { } class func Main() { } func Main(int) } class a var aa : int var bb : int } var a : a = 1 // line = 20 var a : int = (a+b)++ enum TimeUnit { } struct a { var a : a = 1 func } var a = 1 var scoreResult: String = match scoreList { case List 50, ..., } => "the lowest score is 50" // line = 30 } var scoreResult: String = match scoreMap { case Map{"A" : score} => "the highest score is "+score.tostring() } var varString : String = "\a" var a = .a() public struct a { var a : a = 1 } class a { // line = 40 func a () } func a () var a = a > b > c public private class a{} func a() { a+b c+d } func f1(a: Int32, b: Int32, c: Int32 = 3, d: Int32): Int32 { return a + b + c + d } let a = "中文\u4E2D\t" external internal struct A{ static let a = 1; public func c() { } } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); } TEST(ParserTestNative, NativePrint) { std::string code = R"( foreign func ok():unit foreign func ok1():unit foreign var a: Int32 foreign var b: Int32 = 1 foreign let c: Int32 foreign{func ok2():unit} foreign { func ok2():unit var a1: Int32 var b1: Int32 = 1 let a2: Int32 } foreign func ok():unit )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); EXPECT_EQ(0, diag.GetErrorCount()); } TEST(ParserTestSpawn, Spawn) { std::string code = R"( main(): Int64 { let i = 0 let f = spawn{ => i++ } return i } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); OwnedPtr file = parser.ParseTopLevel(); EXPECT_EQ(0, diag.GetErrorCount()); } TEST(ParserTestMatchCase, MatchCase) { std::string code = R"(match(1){ case 1 => a + aa })"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto expr = parser.ParseExpr(); ASSERT_EQ(ASTKind::MATCH_EXPR, expr->astKind); auto matchExpr = StaticAs(expr.get()); EXPECT_EQ(Position(0, 2, 9), matchExpr->matchCases[0]->begin); EXPECT_EQ(Position(0, 2, 16), matchExpr->matchCases[0]->arrowPos); EXPECT_EQ(Position(0, 2, 25), matchExpr->matchCases[0]->end); } TEST(ParserTestWildcardExpr, WildcardExpr) { std::string code = R"( _ = 1 )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto expr = parser.ParseExpr(); ASSERT_EQ(expr->astKind, ASTKind::ASSIGN_EXPR); auto assignExpr = StaticAs(expr.get()); EXPECT_EQ(assignExpr->leftValue->astKind, ASTKind::WILDCARD_EXPR); } TEST(ParserTestWildcardParam, LetOrVar) { std::string code = R"( class A { A(let _:Int64){ } } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto decl = parser.ParseDecl(ScopeKind::TOPLEVEL); EXPECT_EQ(diag.GetErrorCount(), 1); std::vector> funcParams; Walker walker(decl.get(), [&funcParams](Ptr node) -> VisitAction { if (Is(node)) { auto param = StaticAs(node); EXPECT_EQ(param->identifier, "_"); funcParams.push_back(node); } return VisitAction::WALK_CHILDREN; }); walker.Walk(); ASSERT_EQ(funcParams.size(), 1); } TEST(ParserTestWildcardParam, NamedParameter) { std::string code = R"( class A { func a(_!:Int64){ } } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto decl = parser.ParseDecl(ScopeKind::TOPLEVEL); EXPECT_EQ(diag.GetErrorCount(), 1); std::vector> funcParams; Walker walker(decl.get(), [&funcParams](Ptr node) -> VisitAction { if (Is(node)) { auto param = StaticAs(node); EXPECT_EQ(param->identifier, "_"); funcParams.push_back(node); } return VisitAction::WALK_CHILDREN; }); walker.Walk(); ASSERT_EQ(funcParams.size(), 1); } TEST(ParserTestWildcardParam, LambdaParameter) { std::string code = R"( main() { let a = {_:Int64 => return false} } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto decl = parser.ParseDecl(ScopeKind::TOPLEVEL); EXPECT_EQ(diag.GetErrorCount(), 0); std::vector> funcParams; Walker walker(decl.get(), [&funcParams](Ptr node) -> VisitAction { if (Is(node)) { auto param = StaticAs(node); EXPECT_EQ(param->identifier, "_"); funcParams.push_back(node); } return VisitAction::WALK_CHILDREN; }); walker.Walk(); ASSERT_EQ(funcParams.size(), 1); } TEST(ParserTestWildcardExpr, OnlyAppearOnTheLeftOfAssignment) { std::string code = R"( func fn() { a = _ b = _ + _ _ += 1 } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto decl = parser.ParseDecl(ScopeKind::TOPLEVEL); EXPECT_EQ(diag.GetErrorCount(), 4); std::vector> wildcardExprs; Walker walker(decl.get(), [&](Ptr node) -> VisitAction { if (node->astKind == ASTKind::WILDCARD_EXPR) { wildcardExprs.push_back(node); } return VisitAction::WALK_CHILDREN; }); walker.Walk(); ASSERT_EQ(wildcardExprs.size(), 4); } TEST(ParserTestWarningAboutNewline, ExprNlExpr) { auto verify = [](std::string code) -> void { SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); parser.ParseExpr(); EXPECT_EQ(diag.GetWarningCount(), 1); }; std::unordered_set operatorSet = { ">>=", ">=", ">>", "**", "*", "%", "/", "+", "&&", "||", "|>", "~>", "&", "|", "^", "<<", "..", "..=", "<", ">", "<=", "!=", "==", "+=", "-=", "*=", "**=", "/=", "%=", "&&=", "||=", "&=", "|=", "^=", "<<=", }; std::unordered_set operatorTypeSet = {"is", "as"}; std::string firstLine = "1\n"; for (auto&& code : operatorSet) { verify(firstLine + code + " 1"); } for (auto&& code : operatorTypeSet) { verify(firstLine + code + " Int64"); } } TEST(ParserTestWarningAboutNewline, ReturnNlExpr) { std::string code = "return \n1"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); parser.ParseExpr(); EXPECT_EQ(diag.GetWarningCount(), 1); } TEST(ParserTestTupleLeftValue, tupleLeftValue) { std::string code = "(_, _) = (1, 2)"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto expr = parser.ParseExpr(); ASSERT_EQ(expr->astKind, ASTKind::ASSIGN_EXPR); auto assignExpr = StaticAs(expr.get()); ASSERT_EQ(assignExpr->leftValue->astKind, ASTKind::TUPLE_LIT); auto tupleLeftValue = StaticAs(assignExpr->leftValue.get()); EXPECT_EQ(tupleLeftValue->children[0]->astKind, ASTKind::WILDCARD_EXPR); EXPECT_EQ(tupleLeftValue->children[1]->astKind, ASTKind::WILDCARD_EXPR); EXPECT_EQ(diag.GetErrorCount(), 0); } TEST(ParserTestTupleLeftValue, wildcardExprOnTheRight) { std::string code = "(a, b) = (1, _)"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto expr = parser.ParseExpr(); ASSERT_EQ(expr->astKind, ASTKind::ASSIGN_EXPR); auto assignExpr = StaticAs(expr.get()); ASSERT_EQ(assignExpr->rightExpr->astKind, ASTKind::TUPLE_LIT); auto tupleRightValue = StaticAs(assignExpr->rightExpr.get()); EXPECT_EQ(tupleRightValue->children[0]->astKind, ASTKind::LIT_CONST_EXPR); EXPECT_EQ(tupleRightValue->children[1]->astKind, ASTKind::WILDCARD_EXPR); EXPECT_EQ(diag.GetErrorCount(), 1); } TEST(ParserTestTupleLeftValue, TupleAddAssign) { std::string code = "(a, b) += (1, 2)"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto expr = parser.ParseExpr(); ASSERT_EQ(expr->astKind, ASTKind::ASSIGN_EXPR); auto assignExpr = StaticAs(expr.get()); ASSERT_EQ(assignExpr->rightExpr->astKind, ASTKind::TUPLE_LIT); ASSERT_EQ(assignExpr->leftValue->astKind, ASTKind::TUPLE_LIT); EXPECT_EQ(diag.GetErrorCount(), 1); } TEST(ParserTest2, NamedInoutArg) { std::string code = R"(let a = foo(a: inout a))"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto expr = parser.ParseDecl(ScopeKind::TOPLEVEL); auto diags = diag.GetCategoryDiagnostic(Cangjie::DiagCategory::PARSE); ASSERT_EQ(diags.size(), 1); EXPECT_EQ(diags[0].rKind, DiagKindRefactor::parse_expected_expression); } TEST(PositionTest, doWhileExpr) { std::string code = R"( do { i++ } while (true) )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto expr = parser.ParseExpr(); EXPECT_EQ(expr->astKind, ASTKind::DO_WHILE_EXPR); EXPECT_EQ(expr->begin.line, 2); EXPECT_EQ(expr->begin.column, 5); EXPECT_EQ(expr->end.line, 3); EXPECT_EQ(expr->end.column, 17); } TEST(PositionTest, macroPackageHeader) { std::string code = R"( macro package mpg public macro m0 (input: Tokens) { input } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto file = parser.ParseTopLevel(); EXPECT_EQ(file->package->begin.line, 2); EXPECT_EQ(file->package->begin.column, 5); } TEST(PositionTest, packageHeader) { std::string code = R"( package pg main () { 0 } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto file = parser.ParseTopLevel(); EXPECT_EQ(file->package->begin.line, 2); EXPECT_EQ(file->package->begin.column, 5); } TEST(PositionTest, varWithPattern1) { std::string code = R"( let (a, b) = (1, 2) )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto expr = parser.ParseDecl(ScopeKind::TOPLEVEL); EXPECT_EQ(expr->astKind, ASTKind::VAR_WITH_PATTERN_DECL); auto e = StaticAs(expr.get()); EXPECT_EQ(e->begin.line, 2); EXPECT_EQ(e->begin.column, 5); EXPECT_EQ(e->keywordPos.line, 2); EXPECT_EQ(e->keywordPos.column, 5); EXPECT_EQ(e->end.line, 3); EXPECT_EQ(e->end.column, 15); } TEST(PositionTest, varWithPattern2) { std::string code = R"( let (a, b) = (1, 2) )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto expr = parser.ParseDecl(ScopeKind::TOPLEVEL); EXPECT_EQ(expr->astKind, ASTKind::VAR_WITH_PATTERN_DECL); auto e = StaticAs(expr.get()); EXPECT_EQ(e->begin.line, 2); EXPECT_EQ(e->begin.column, 5); EXPECT_EQ(e->keywordPos.line, 2); EXPECT_EQ(e->keywordPos.column, 5); EXPECT_EQ(e->end.line, 3); EXPECT_EQ(e->end.column, 15); } TEST(PositionTest, unsafeBlock) { std::string code = R"( unsafe { let a = 2 let b = 4 } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto expr = parser.ParseExpr(); EXPECT_EQ(expr->astKind, ASTKind::BLOCK); auto e = StaticAs(expr.get()); EXPECT_EQ(e->begin.line, 2); EXPECT_EQ(e->begin.column, 5); EXPECT_EQ(e->unsafePos.line, 2); EXPECT_EQ(e->unsafePos.column, 5); EXPECT_EQ(e->leftCurlPos.line, 2); EXPECT_EQ(e->leftCurlPos.column, 12); } TEST(PositionTest, multilineComment1) { std::string code = R"(/* myComment more text someText */ )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Lexer lexer(code, diag, sm); auto commentTok = lexer.GetTokens().front(); EXPECT_EQ(commentTok.kind, TokenKind::COMMENT); EXPECT_EQ(commentTok.Begin().line, 1); EXPECT_EQ(commentTok.Begin().column, 1); EXPECT_EQ(commentTok.End().line, 4); EXPECT_EQ(commentTok.End().column, 3); } TEST(PositionTest, multilineComment2) { std::string code = R"( /* myComment */)"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Lexer lexer(code, diag, sm); auto commentTok = lexer.GetTokens().front(); EXPECT_EQ(commentTok.kind, TokenKind::COMMENT); EXPECT_EQ(commentTok.Begin().line, 1); EXPECT_EQ(commentTok.Begin().column, 5); EXPECT_EQ(commentTok.End().line, 1); EXPECT_EQ(commentTok.End().column, 20); } TEST(PositionTest, constVarWithModifier) { std::string code = R"( public const a = 1; )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto decl = parser.ParseDecl(ScopeKind::TOPLEVEL); EXPECT_EQ(decl->astKind, ASTKind::VAR_DECL); auto e = StaticAs(decl.get()); EXPECT_EQ(e->begin.line, 2); EXPECT_EQ(e->begin.column, 5); } TEST(PositionTest, memberParamWithModifier) { std::string code = R"( class A { A (public let a: Int64){ } } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto decl = parser.ParseDecl(ScopeKind::TOPLEVEL); EXPECT_EQ(diag.GetErrorCount(), 0); std::vector> funcParams; Walker walker(decl.get(), [&funcParams](Ptr node) -> VisitAction { if (Is(node)) { auto param = StaticAs(node); EXPECT_EQ(param->begin.line, 3); EXPECT_EQ(param->begin.column, 12); funcParams.push_back(node); } return VisitAction::WALK_CHILDREN; }); walker.Walk(); EXPECT_EQ(funcParams.size(), 1); } TEST_F(ParserTest, FmtWhen) { std::string code{R"(@When[abcd == "apple"] public open class Rectangle { })"}; Parser p(code, diag, sm); auto file = p.ParseTopLevel(); auto& cl = StaticCast(*file->decls[0]); ASSERT_EQ(cl.annotations.size(), 1); ASSERT_EQ(cl.annotations[0]->kind, AnnotationKind::WHEN); ASSERT_EQ(cl.annotations[0]->end.column, 23); } TEST_F(ParserTest, OuterDeclUnderMacroExpandDecl) { std::string code{ R"( @Derive[ToString] @Derive[Equatable] @!APILevel[ 19, atomicservice: true, stagemodelonly: true, syscap: "SystemCapability.Advertising.Ads" ] public class Parameter { @M1 @!APILevel[ 19, atomicservice: true, stagemodelonly: true, syscap: "SystemCapability.Advertising.Ads" ] public Parameter( let key: String ) {} @M1 @Frozen public func foo() {} } @Derive[Equatable] public struct S { @M1 public func hoo() {} @M1 @Frozen public prop b: Int64 { get() { 0 } } } @Derive[Equatable] enum RGBColor { | Red | Green | Blue @M1 public static func printType() { print("RGBColor") } } extend String { @M1 public func printSize() { println("the size is ${this.size}") } } )"}; Parser p(code, diag, sm); auto file = p.ParseTopLevel(); bool hitKey = false; bool hitPrimaryCtor = false; bool hitFoo = false; bool hitHoo = false; bool hitB = false; bool hitPrintType = false; bool hitPrintSize = false; Walker walker(file.get(), [&hitPrimaryCtor, &hitKey, &hitFoo, &hitHoo, &hitB, &hitPrintType, &hitPrintSize]( Ptr node) -> VisitAction { Meta::match (*node)( [&hitPrimaryCtor](const PrimaryCtorDecl& decl) { if (decl.identifier == "Parameter" && decl.outerDecl) { hitPrimaryCtor = true; } }, [&hitB](const PropDecl& decl) { if (decl.outerDecl && decl.identifier == "b") { hitB = true; } }, [&hitKey](const VarDecl& decl) { if (decl.identifier == "key" && decl.outerDecl) { hitKey = true; } }, [&hitFoo, &hitHoo, &hitPrintType, &hitPrintSize](const FuncDecl& decl) { if (decl.outerDecl) { if (decl.identifier == "foo") { hitFoo = true; } else if (decl.identifier == "hoo") { hitHoo = true; } else if (decl.identifier == "printType") { hitPrintType = true; } else if (decl.identifier == "printSize") { hitPrintSize = true; } } }); return VisitAction::WALK_CHILDREN; }); walker.Walk(); ASSERT_TRUE(hitPrimaryCtor); ASSERT_TRUE(hitKey); ASSERT_TRUE(hitFoo); ASSERT_TRUE(hitHoo); ASSERT_TRUE(hitB); ASSERT_TRUE(hitPrintType); ASSERT_TRUE(hitPrintSize); } TEST(PositionTest, FuncWithoutBodyCommentsPos) { std::string code = R"(/** * Provides class to generate logs. */ @!APILevel[ 21, stagemodelonly: true, syscap: "SystemCapability.HiviewDFX.HiLog" ] public class HilogChannel { /** * Outputs debug-level logs. */ @!APILevel[ 21, stagemodelonly: true, syscap: "SystemCapability.HiviewDFX.HiLog" ] public func debug(message: T): Unit where T <: ToString /** * Outputs info-level logs. */ @!APILevel[ 21, stagemodelonly: true, syscap: "SystemCapability.HiviewDFX.HiLog" ] public func info(message: T): Unit where T <: ToString })"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm, Position{0, 1, 1}, true, true); auto file = parser.ParseTopLevel(); EXPECT_EQ(diag.GetErrorCount(), 0); auto& d = StaticCast(*file->decls[0]); EXPECT_EQ(d.comments.innerComments.size(), 0); auto& hilog = StaticCast(*d.invocation.decl); EXPECT_EQ(hilog.comments.innerComments.size(), 0); auto members = hilog.GetMemberDeclPtrs(); auto debug = StaticCast(StaticCast(members[0])->invocation.decl.get()); EXPECT_EQ(debug->begin, Position(19, 5)); EXPECT_EQ(debug->end, Position(19, 63)); EXPECT_EQ(debug->comments.leadingComments.size(), 1); EXPECT_EQ(debug->comments.trailingComments.size(), 0); auto info = StaticCast(StaticCast(members[1])->invocation.decl.get()); EXPECT_EQ(info->begin, Position(29, 5)); EXPECT_EQ(info->end, Position(29, 62)); EXPECT_EQ(info->comments.leadingComments.size(), 1); EXPECT_EQ(info->comments.trailingComments.size(), 0); } TEST(PositionTest, Finalizer) { std::string code = R"( class C { ~ init() { println(3) } } )"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm); auto decl = parser.ParseDecl(ScopeKind::TOPLEVEL); auto& finalizer = StaticCast(*StaticCast(*decl).GetMemberDecls()[0]); EXPECT_EQ(finalizer.identifier.Begin().column, 5); EXPECT_EQ(finalizer.identifier.End().column, 12); EXPECT_EQ(finalizer.keywordPos.column, 8); } TEST_F(ParserTest, LSP) { Parser parser = Parser("package 1di", diag, sm); OwnedPtr file = parser.ParseTopLevel(); auto p = diag.GetCategoryDiagnostic(DiagCategory::LEX); ASSERT_EQ(p.size(), 2); ASSERT_EQ(p[0].errorMessage, std::string{"illegal integer suffix 'i'"}); ASSERT_EQ(p[1].errorMessage, std::string{"unexpected digit 'd' in decimal"}); } TEST(PositionTest, Attribute) { std::string code = R"(@Attribute[ Ham, "string",] class C {})"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm, Position{0, 1, 1}, true, true); auto file = parser.ParseTopLevel(); EXPECT_EQ(diag.GetErrorCount(), 0); auto& c = *StaticCast(*file->decls[0]).annotations[0]; ASSERT_EQ(c.kind, AnnotationKind::ATTRIBUTE); ASSERT_EQ(c.attrs.size(), 2); EXPECT_EQ(c.attrs[0].kind, TokenKind::IDENTIFIER); EXPECT_EQ(c.attrs[0].Begin(), Position(1, 18)); EXPECT_EQ(c.attrs[1].kind, TokenKind::STRING_LITERAL); EXPECT_EQ(c.attrs[1].Begin(), Position(1, 26)); EXPECT_EQ(c.attrs[1].End(), Position(1, 34)); ASSERT_EQ(c.attrCommas.size(), 2); EXPECT_EQ(c.attrCommas[0], Position(1, 21)); EXPECT_EQ(c.attrCommas[1], Position(1, 34)); } TEST(PositionTest, QuoteEscape) { std::string code = R"(let a = quote(\@ \( \) \$))"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm, Position{0, 1, 1}, true, true); auto file = parser.ParseTopLevel(); EXPECT_EQ(diag.GetErrorCount(), 0); auto& d = StaticCast(*file->decls[0]); auto& quote = StaticCast(*d.initializer); auto& tokens = StaticCast(*quote.exprs[0]).tokens; ASSERT_EQ(tokens.size(), 4); EXPECT_EQ(tokens[0].Begin(), Position(1, 15)); EXPECT_EQ(tokens[1].Begin(), Position(1, 18)); EXPECT_EQ(tokens[2].Begin(), Position(1, 21)); EXPECT_EQ(tokens[3].Begin(), Position(1, 24)); } TEST(PositionTest, SubscriptExpr) { std::string code = "main() {b[1, 2]}"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm, Position{0, 1, 1}, true, true); auto file = parser.ParseTopLevel(); EXPECT_EQ(diag.GetErrorCount(), 0); auto& d = StaticCast(*file->decls[0]); auto& e = StaticCast(*d.funcBody->body->body[0]); ASSERT_EQ(e.commaPos.size(), 1); EXPECT_EQ(e.commaPos[0], Position(1, 12)); } TEST(PositionTest, GenericConstraintBegin) { std::string code = R"(func foo(a: T): Unit where T <: ToString {})"; SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); Parser parser(code, diag, sm, Position{0, 1, 1}, true, true); auto file = parser.ParseTopLevel(); EXPECT_EQ(diag.GetErrorCount(), 0); auto& foo = StaticCast(*file->decls[0]); ASSERT_NE(foo.funcBody->generic, nullptr); EXPECT_EQ(foo.funcBody->generic->genericConstraints[0]->begin, Position(1, 25)); } cangjie_compiler-1.0.7/unittests/Sema/000077500000000000000000000000001510705540100177365ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Sema/CMakeLists.txt000066400000000000000000000020651510705540100225010ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. add_executable(TypeCheckerTest TypeCheckerTest.cpp ${CANGJIE_SRC_OBJECTS}) target_link_libraries( TypeCheckerTest cangjie-lsp ${LINK_LIBS} boundscheck-static GTest::gtest GTest::gtest_main TestCompilerInstanceObject) add_test(NAME TypeCheckerTest COMMAND TypeCheckerTest) add_executable(GenericTest GenericTest.cpp) target_link_libraries( GenericTest cangjie-lsp ${LINK_LIBS} boundscheck-static GTest::gtest GTest::gtest_main TestCompilerInstanceObject) add_test(NAME GenericTest COMMAND GenericTest) add_executable(CommentsTest CommentsTest.cpp) target_link_libraries( CommentsTest cangjie-lsp ${LINK_LIBS} boundscheck-static GTest::gtest GTest::gtest_main TestCompilerInstanceObject) add_test(NAME CommentsTest COMMAND CommentsTest) cangjie_compiler-1.0.7/unittests/Sema/CommentsTest.cpp000066400000000000000000000176321510705540100231000ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * Comments related unit tests. */ #include #include #include #include #include "gtest/gtest.h" #include "TestCompilerInstance.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/Match.h" using namespace Cangjie; using namespace AST; class CommentsTest : public testing::Test { protected: void SetUp() override { instance = std::make_unique(invocation, diag); #ifdef PROJECT_SOURCE_DIR // Gets the absolute path of the project from the compile parameter. projectPath = PROJECT_SOURCE_DIR; #else // Just in case, give it a default value. // Assume the initial is in the build directory. projectPath = ".."; #endif #ifdef __x86_64__ instance->invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::X86_64; #else instance->invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::AARCH64; #endif #ifdef _WIN32 instance->invocation.globalOptions.target.os = Cangjie::Triple::OSType::WINDOWS; invocation.globalOptions.executablePath = projectPath + "\\output\\bin\\"; #elif __unix__ instance->invocation.globalOptions.target.os = Cangjie::Triple::OSType::LINUX; invocation.globalOptions.executablePath = projectPath + "/output/bin/"; #endif Cangjie::MacroProcMsger::GetInstance().CloseMacroSrv(); } std::string srcPath; std::string projectPath; DiagnosticEngine diag; CompilerInvocation invocation; std::unique_ptr instance; }; TEST_F(CommentsTest, DISABLED_MacroDesugarFuncCommentsTest) { std::string code = R"( macro package hello import std.ast.* /** * comment */ public macro StorageProp1(attr: Tokens, input: Tokens): Tokens{ return input; } )"; instance->code = code; instance->invocation.globalOptions.enableMacroInLSP = true; instance->invocation.globalOptions.enableAddCommentToAst = true; instance->invocation.globalOptions.compileMacroPackage = true; instance->invocation.globalOptions.outputMode = GlobalOptions::OutputMode::SHARED_LIB; instance->Compile(CompileStage::SEMA); bool hit = false; Walker walker(instance->GetSourcePackages()[0]->files[0].get(), [&hit](Ptr node) -> VisitAction { Meta::match (*node)([&hit](const MacroDecl& decl) { if (decl.identifier == "StorageProp1" && decl.desugarDecl) { hit = true; ASSERT_FALSE(decl.desugarDecl->comments.leadingComments.empty()); } }); return VisitAction::WALK_CHILDREN; }); walker.Walk(); ASSERT_TRUE(hit); } TEST_F(CommentsTest, DISABLED_MainDesugarFuncCommentsTest) { std::string code = R"( /** * comment */ main () { 0; } )"; instance->code = code; instance->invocation.globalOptions.enableMacroInLSP = true; instance->invocation.globalOptions.enableAddCommentToAst = true; instance->invocation.globalOptions.compileMacroPackage = true; instance->invocation.globalOptions.outputMode = GlobalOptions::OutputMode::SHARED_LIB; instance->Compile(CompileStage::SEMA); bool hit = false; Walker walker(instance->GetSourcePackages()[0]->files[0].get(), [&hit](Ptr node) -> VisitAction { Meta::match (*node)([&hit](const MainDecl& decl) { if (decl.identifier == "main" && decl.desugarDecl) { hit = true; ASSERT_FALSE(decl.desugarDecl->comments.leadingComments.empty()); } }); return VisitAction::WALK_CHILDREN; }); walker.Walk(); ASSERT_TRUE(hit); } Ptr GetCurMacro(const Decl& decl) { Ptr curCall = nullptr; if (decl.curMacroCall) { curCall = decl.curMacroCall; } else { Ptr tmpDecl = &decl; while (tmpDecl->outerDecl) { tmpDecl = tmpDecl->outerDecl; if (tmpDecl->curMacroCall) { curCall = tmpDecl->curMacroCall; break; } } } return curCall; } bool FindCommentsInMacroCall(const Ptr node, Ptr curCall) { if (!node || !curCall) { return false; } bool ret = false; std::string id = node->identifier; auto begin = node->GetBegin(); auto end = node->GetEnd(); if (curCall) { ConstWalker walker(curCall, [&ret, &id, &begin, &end](Ptr node) -> VisitAction { Meta::match (*node)([&ret, &id, &begin, &end](const Decl& decl) { if (decl.identifier == id && begin == decl.GetBegin() && end == decl.GetEnd() && !decl.comments.IsEmpty()) { ret = true; } }); if (ret) { return VisitAction::STOP_NOW; } return VisitAction::WALK_CHILDREN; }); walker.Walk(); } return ret; } TEST_F(CommentsTest, DISABLED_CustomAnnotionCommentsTest) { std::string code = R"( @Annotation public class APILevel { let a: UInt8 const init(v: UInt8) { a = v } } // C2 comments @!APILevel[ 12 ] public class C2 { // goo comments public func goo () {} } )"; instance->code = code; instance->invocation.globalOptions.enableMacroInLSP = true; instance->invocation.globalOptions.enableAddCommentToAst = true; instance->invocation.globalOptions.compileMacroPackage = true; instance->invocation.globalOptions.outputMode = GlobalOptions::OutputMode::SHARED_LIB; instance->Compile(CompileStage::SEMA); bool hitC2 = false; bool hitGoo = false; Walker walker(instance->GetSourcePackages()[0]->files[0].get(), [&hitC2, &hitGoo](Ptr node) -> VisitAction { Meta::match (*node)([&hitC2, &hitGoo](const Decl& decl) { if (auto curCall = GetCurMacro(decl)) { bool find = FindCommentsInMacroCall(&decl, curCall); if (find) { if (decl.identifier == "C2") { hitC2 = true; } if (decl.identifier == "goo") { hitGoo = true; } } } }); return VisitAction::WALK_CHILDREN; }); walker.Walk(); ASSERT_TRUE(hitC2); ASSERT_TRUE(hitGoo); } TEST_F(CommentsTest, DISABLED_MacroCommentsTest) { std::string code = R"( import std.deriving.* // C2 comments @Derive[ToString] @Derive[Equatable] public class C2 { // goo comments public func goo () {} } )"; instance->code = code; instance->invocation.globalOptions.enableMacroInLSP = true; instance->invocation.globalOptions.enableAddCommentToAst = true; instance->invocation.globalOptions.compileMacroPackage = true; instance->invocation.globalOptions.outputMode = GlobalOptions::OutputMode::SHARED_LIB; instance->Compile(CompileStage::SEMA); bool hitC2 = false; bool hitGoo = false; Walker walker(instance->GetSourcePackages()[0]->files[0].get(), [&hitC2, &hitGoo](Ptr node) -> VisitAction { Meta::match (*node)([&hitC2, &hitGoo](const Decl& decl) { if (auto curCall = GetCurMacro(decl)) { bool find = FindCommentsInMacroCall(&decl, curCall); if (find) { if (decl.identifier == "C2") { hitC2 = true; } if (decl.identifier == "goo") { hitGoo = true; } } } }); return VisitAction::WALK_CHILDREN; }); walker.Walk(); ASSERT_TRUE(hitC2); ASSERT_TRUE(hitGoo); }cangjie_compiler-1.0.7/unittests/Sema/GenericTest.cpp000066400000000000000000000046451510705540100226670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * Generic instantiation related unit tests. */ #include #include #include #include #include "gtest/gtest.h" #define private public #include "TestCompilerInstance.h" #include "cangjie/AST/Match.h" using namespace Cangjie; using namespace AST; class GenericTest : public testing::Test { protected: void SetUp() override { #ifdef _WIN32 std::string command; int err = 0; if (FileUtil::FileExist("testTempFiles")) { std::string command = "rmdir /s /q testTempFiles"; int err = system(command.c_str()); std::cout << err; } command = "mkdir testTempFiles"; err = system(command.c_str()); ASSERT_EQ(0, err); srcPath = projectPath + "\\tests\\LLT\\Sema\\generics\\"; packagePath = packagePath + "\\"; #else std::string command; int err = 0; if (FileUtil::FileExist("testTempFiles")) { command = "rm -rf testTempFiles"; err = system(command.c_str()); ASSERT_EQ(0, err); } command = "mkdir -p testTempFiles"; err = system(command.c_str()); ASSERT_EQ(0, err); srcPath = projectPath + "/tests/LLT/Sema/generics/"; packagePath = packagePath + "/"; #endif #ifdef __x86_64__ invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::X86_64; #else invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::AARCH64; #endif #ifdef _WIN32 invocation.globalOptions.target.os = Cangjie::Triple::OSType::WINDOWS; #elif __unix__ invocation.globalOptions.target.os = Cangjie::Triple::OSType::LINUX; #endif } #ifdef PROJECT_SOURCE_DIR // Gets the absolute path of the project from the compile parameter. std::string projectPath = PROJECT_SOURCE_DIR; #else // Just in case, give it a default value. // Assume the initial is in the build directory. std::string projectPath = ".."; #endif std::string packagePath = "testTempFiles"; std::string srcPath; DiagnosticEngine diag; CompilerInvocation invocation; std::unique_ptr instance; }; cangjie_compiler-1.0.7/unittests/Sema/SemaCangjieFiles/000077500000000000000000000000001510705540100230675ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Sema/SemaCangjieFiles/AddClassTyInfoMacro.cj000066400000000000000000000007741510705540100272060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information.macro package macrodef import std.ast.* public macro M0 (input : Tokens) { quote( public class Class3 <: InteropType < Class3 > { public func retClass(a: HashMapEx < String, Class3 >): Unit { } } ) }cangjie_compiler-1.0.7/unittests/Sema/SemaCangjieFiles/AssumptionTest/000077500000000000000000000000001510705540100260715ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Sema/SemaCangjieFiles/AssumptionTest/assumption1.cj000066400000000000000000000010051510705540100306660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information.macro package macrodef package AssumptionTest class C1 {} class A where T <: C1 {} class B where X <: A {} class D where U <: B, V <: A {} class E where T <: E {} class F where T <: G {} class G where T <: F {} cangjie_compiler-1.0.7/unittests/Sema/SemaCangjieFiles/DesugarVararg.cj000066400000000000000000000005611510705540100261440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. import std.collection.* main() { let a = ArrayList.of(3, 8, -7) let b = ArrayList.of() } cangjie_compiler-1.0.7/unittests/Sema/SemaCangjieFiles/DesugarVararg2.cj000066400000000000000000000005451510705540100262300ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. main() { B("") B("", 34, -7) 0 } class B { init(a: String, b: Array) {} } cangjie_compiler-1.0.7/unittests/Sema/SemaCangjieFiles/ModifyClassBuildFunc.cj000066400000000000000000000053011510705540100274150ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information.macro package macrodef macro package macrodefX import std.ast.* import std.collection.* func createClassDeclaration( modifiers!: Tokens = Tokens(), keyword!: Tokens = quote(class), identifier!: Tokens = Tokens(), body!: Tokens = Tokens() ): Tokens { quote( $modifiers $keyword $identifier { $body } ) } public macro Prop(input: Tokens):Tokens{ return input } public macro Component(input: Tokens): Tokens { let classDecl = (parseDecl(input) as ClassDecl).getOrThrow() let classModifier = classDecl.modifiers let classIdent = classDecl.identifier let classBody = classDecl.body let classBodyAfterExpand = handleClassBody(classBody.decls) createClassDeclaration( modifiers: quote($classModifier), identifier: quote($classIdent), body: quote( $classBodyAfterExpand ) ) } func createMethodDeclaration( modifiers!: Tokens = Tokens(), identifier!: Tokens = Tokens(), generics!: ?Tokens = None, body!: Tokens = Tokens() ): Tokens { var funcDef = quote( $modifiers func $identifier) if (let Some(v) <- generics) { funcDef += quote($v) } funcDef += quote(()) funcDef + quote({ $body } ) } func generateBuildFunc(funcDecl: FuncDecl) { var funcBody = funcDecl.block.nodes var renDer= Tokens() for (each in funcBody) { match (each) { case value: IfExpr => renDer = renDer.append(Token(TokenKind.NL)).append(quote({=> $value})) case _: Node => () } } createMethodDeclaration( modifiers: quote(public), identifier: quote(build), body: quote($renDer) ) } func handleFuncInClassBody(funcDecl: FuncDecl) { // handler build function, rename to build, and expand components if (funcDecl.identifier.value == "build") { return generateBuildFunc(funcDecl) } // other functions are inserted without change. return quote( $funcDecl ) } func handleClassBody(classBody: ArrayList) { var ret = Tokens() for (item in classBody) { // handle function match (item) { case funcDecl: FuncDecl => ret = ret + handleFuncInClassBody(funcDecl) case _ => // other declarations are inserted without change. ret = ret + quote( $item ) } } return ret }cangjie_compiler-1.0.7/unittests/Sema/SemaCangjieFiles/NoDiagInLSPMacroCall.cj000066400000000000000000000007651510705540100272020ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. package macroModule import macrodef.* public interface MyInteropType {} public class MyHashMapEx where V <: MyInteropType {} @M0 public class Class3 { public func retClass(a: MyHashMapEx): Unit { } } cangjie_compiler-1.0.7/unittests/Sema/SemaCangjieFiles/NoDiagInLSPMacroCallNode.cj000066400000000000000000000011541510705540100300010ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information.macro package macrodef package macroModule import macrodefX.{Component, Prop} public enum Who { You | Me } public class Message { public Message(public let who: Who) {} } @Component public class ChatBlock { @Prop var message: Message = Message(Me) func build() { if (let Me <- message.who) { } else { } } } cangjie_compiler-1.0.7/unittests/Sema/SemaCangjieFiles/UnableToInferGenericArgumentInTest.cj000066400000000000000000000010031510705540100322230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. import std.collection.concurrent.* @Test class Test_arrayblockqueue_head_02 { @TestCase func test_head_init_01(): Unit { var arrayqueue3 = ArrayBlockingQueue(10000) @Expect(arrayqueue3.head(), Option.None) } } cangjie_compiler-1.0.7/unittests/Sema/SemaCangjieFiles/spawn.cj000066400000000000000000000006741510705540100245440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information.macro package macrodef main(): Int64 { let i: Int64 = 0 let f1: Future = spawn { => return i } let f2 = spawn { => return i } return i } cangjie_compiler-1.0.7/unittests/Sema/TypeCheckerTest.cpp000066400000000000000000000320541510705540100235140ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include #include "gtest/gtest.h" #include "TestCompilerInstance.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/PrintNode.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/Match.h" #include "cangjie/Driver/Driver.h" using namespace Cangjie; using namespace AST; class TypeCheckerTest : public testing::Test { protected: void SetUp() override { instance = std::make_unique(invocation, diag); #ifdef PROJECT_SOURCE_DIR // Gets the absolute path of the project from the compile parameter. projectPath = PROJECT_SOURCE_DIR; #else // Just in case, give it a default value. // Assume the initial is in the build directory. projectPath = ".."; #endif #ifdef __x86_64__ instance->invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::X86_64; #else instance->invocation.globalOptions.target.arch = Cangjie::Triple::ArchType::AARCH64; #endif #ifdef _WIN32 instance->invocation.globalOptions.target.os = Cangjie::Triple::OSType::WINDOWS; #elif __unix__ instance->invocation.globalOptions.target.os = Cangjie::Triple::OSType::LINUX; #endif Cangjie::MacroProcMsger::GetInstance().CloseMacroSrv(); } std::string srcPath; std::string projectPath; DiagnosticEngine diag; CompilerInvocation invocation; std::unique_ptr instance; }; TEST_F(TypeCheckerTest, DISABLED_TypecheckTest) { std::string code = R"( main() { var t: Int32 t = 1 return 0 } )"; instance->code = code; instance->Compile(CompileStage::SEMA); Walker walker(instance->GetSourcePackages()[0]->files[0].get(), [](Ptr node) -> VisitAction { Meta::match (*node)( [](const FuncDecl& decl) { auto ty = dynamic_cast(decl.ty.get()); EXPECT_EQ(ty->retTy->kind, TypeKind::TYPE_INT64); }, [](const VarDecl& decl) { EXPECT_EQ(decl.ty->kind, TypeKind::TYPE_INT32); }); return VisitAction::WALK_CHILDREN; }); walker.Walk(); EXPECT_EQ(diag.GetErrorCount(), 0); } TEST_F(TypeCheckerTest, DISABLED_MacroDeclTest) { std::string code = R"( public macro )"; instance->code = code; instance->PerformParse(); instance->PerformImportPackage(); instance->PerformSema(); std::vector astData; instance->importManager.ExportAST(false, astData, *instance->GetSourcePackages()[0]); EXPECT_EQ(diag.GetErrorCount(), 3); } TEST_F(TypeCheckerTest, DISABLED_MacroCallInLSPTest) { std::string code = R"( @M1 func test() { var a = 1 a = 2 } @M2 class A { var a = 1 func test() { this.a = 2 } func test1() { var b = 1 b = 2 } } @M3 enum E { EE func test() { let a = EE } } main() {0} )"; instance->code = code; instance->invocation.globalOptions.enableMacroInLSP = true; instance->Compile(CompileStage::SEMA); // Test MemberAccess has target or not in macrocall. auto visitPre = [&](Ptr curNode) -> VisitAction { if (curNode->astKind == ASTKind::MEMBER_ACCESS) { auto ma = StaticAs(curNode); EXPECT_TRUE(ma->target); if (ma->target) { std::cout << "MemberAccess field " << ma->field.Val() << " has target." << std::endl; } else { std::cout << "MemberAccess field " << ma->field.Val() << " has no target." << std::endl; } } if (curNode->astKind == ASTKind::REF_EXPR) { auto re = StaticAs(curNode); if (re->ref.identifier == "M1" || re->ref.identifier == "M2" || re->ref.identifier == "M3") { return VisitAction::SKIP_CHILDREN; } EXPECT_TRUE(re->ref.target); if (re->ref.target) { std::cout << re->ref.identifier.Val() << " has target." << std::endl; } else { std::cout << re->ref.identifier.Val() << " has no target." << std::endl; } return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; Walker macroCallWalker(instance->GetSourcePackages()[0]->files[0].get(), visitPre); macroCallWalker.Walk(); // error: undeclared identifier 'M1'/'M2'/'M3'. EXPECT_EQ(diag.GetErrorCount(), 3); Cangjie::MacroProcMsger::GetInstance().CloseMacroSrv(); } TEST_F(TypeCheckerTest, DISABLED_MacroDiagInLSPTest) { instance->invocation.globalOptions.enableMacroInLSP = true; srcPath = projectPath + "/unittests/Sema/SemaCangjieFiles/"; invocation.globalOptions.executablePath = projectPath + "/output/bin/"; instance->compileOnePackageFromSrcFiles = true; instance->srcFilePaths = {srcPath + "UnableToInferGenericArgumentInTest.cj"}; invocation.globalOptions.outputMode = GlobalOptions::OutputMode::STATIC_LIB; invocation.globalOptions.enableCompileTest = true; instance->Compile(CompileStage::SEMA); auto dv = diag.GetCategoryDiagnostic(DiagCategory::SEMA); bool findOptNone = false; for (auto& d : dv) { for (auto& n : d.subDiags) { if (n.mainHint.str.find("Enum-Option") != std::string::npos) { findOptNone = true; EXPECT_EQ(n.mainHint.range.end.line, 14); EXPECT_EQ(n.mainHint.range.end.column, 55); } } } EXPECT_EQ(findOptNone, true); Cangjie::MacroProcMsger::GetInstance().CloseMacroSrv(); } TEST_F(TypeCheckerTest, DISABLED_NoDiagInLSPMacroCallTest) { srcPath = projectPath + "/unittests/Sema/SemaCangjieFiles/"; std::string command = "cjc " + srcPath + "AddClassTyInfoMacro.cj --compile-macro -Woff all"; int err = system(command.c_str()); ASSERT_EQ(0, err); instance->invocation.globalOptions.enableMacroInLSP = false; invocation.globalOptions.executablePath = projectPath + "/output/bin/"; instance->compileOnePackageFromSrcFiles = true; instance->srcFilePaths = {srcPath + "NoDiagInLSPMacroCall.cj"}; invocation.globalOptions.outputMode = GlobalOptions::OutputMode::STATIC_LIB; invocation.globalOptions.enableCompileTest = true; instance->Compile(CompileStage::SEMA); EXPECT_EQ(diag.GetErrorCount(), 0); Cangjie::MacroProcMsger::GetInstance().CloseMacroSrv(); } TEST_F(TypeCheckerTest, DISABLED_NoDiagInLSPMacroCallForTest) { srcPath = projectPath + "/unittests/Sema/SemaCangjieFiles/"; std::vector argStrs = {"cjc", "--compile-macro", "-Woff", "all", srcPath + "ModifyClassBuildFunc.cj"}; DiagnosticEngine driverDiag; std::unique_ptr driver = std::make_unique(argStrs, driverDiag, projectPath + "/output/bin/cjc"); driver->driverOptions->customizedSysroot = true; bool succ = driver->ParseArgs(); EXPECT_TRUE(succ); succ = driver->ExecuteCompilation(); EXPECT_TRUE(succ); instance->invocation.globalOptions.enableMacroInLSP = true; invocation.globalOptions.executablePath = projectPath + "/output/bin/"; instance->compileOnePackageFromSrcFiles = true; instance->srcFilePaths = {srcPath + "NoDiagInLSPMacroCallNode.cj"}; invocation.globalOptions.outputMode = GlobalOptions::OutputMode::STATIC_LIB; invocation.globalOptions.enableCompileTest = true; instance->Compile(CompileStage::SEMA); EXPECT_EQ(diag.GetErrorCount(), 0); Cangjie::MacroProcMsger::GetInstance().CloseMacroSrv(); } TEST_F(TypeCheckerTest, DISABLED_MacroCallOfTopLevelInLSPTest) { std::string code = R"( @M1 func test(v: String) { var a = 1 a = 2 } main() {0} )"; instance->code = code; instance->invocation.globalOptions.implicitPrelude = true; instance->invocation.globalOptions.enableMacroInLSP = true; instance->Compile(CompileStage::IMPORT_PACKAGE); // Skip macro expand stage and move node in 'originalMacroCallNodes' to simulate original macro code for LSP. auto file = instance->GetSourcePackages()[0]->files[0].get(); for (auto& it : file->decls) { if (auto md = DynamicCast(it.get())) { file->originalMacroCallNodes.emplace_back(std::move(md->invocation.decl)); } } instance->PerformSema(); unsigned checkCount = 0; // Test ty is set correctly. auto visitPre = [&](Ptr curNode) -> VisitAction { if (auto fp = DynamicCast(curNode); fp && fp->identifier == "v") { checkCount++; EXPECT_TRUE(fp->type != nullptr); if (fp->type != nullptr) { EXPECT_EQ(fp->type->ty->String(), "Struct-String"); } } return VisitAction::WALK_CHILDREN; }; for (auto& it : file->originalMacroCallNodes) { Walker(it.get(), visitPre).Walk(); } EXPECT_EQ(checkCount, 1); Cangjie::MacroProcMsger::GetInstance().CloseMacroSrv(); } TEST_F(TypeCheckerTest, DISABLED_AssumptionTest) { #ifdef _WIN32 srcPath = projectPath + "\\unittests\\Sema\\SemaCangjieFiles\\AssumptionTest"; #elif __unix__ srcPath = projectPath + "/unittests/Sema/SemaCangjieFiles/AssumptionTest"; #endif instance->srcDirs = {srcPath}; instance->invocation.globalOptions.implicitPrelude = true; instance->Compile(); for (auto& decl : instance->GetSourcePackages()[0]->files[0]->decls) { if (auto cd = AST::As(decl.get()); cd) { if (cd->identifier == "A") { EXPECT_EQ(cd->generic->assumptionCollection.size(), 1); auto it = cd->generic->assumptionCollection.begin(); EXPECT_EQ(it->second.size(), 1); } else if (cd->identifier == "B") { EXPECT_EQ(cd->generic->assumptionCollection.size(), 1); auto it = cd->generic->assumptionCollection.begin(); EXPECT_EQ(it->second.size(), 2); } else if (cd->identifier == "D") { for (auto& it : cd->generic->assumptionCollection) { if (dynamic_cast(it.first.get())->name == "V") { EXPECT_EQ(it.second.size(), 2); } if (dynamic_cast(it.first.get())->name == "U") { EXPECT_EQ(it.second.size(), 3); } } } else if (cd->identifier == "E") { EXPECT_EQ(cd->generic->assumptionCollection.size(), 1); auto it = cd->generic->assumptionCollection.begin(); EXPECT_EQ(it->second.size(), 1); } else if (cd->identifier == "F") { EXPECT_EQ(cd->generic->assumptionCollection.size(), 1); auto it = cd->generic->assumptionCollection.begin(); EXPECT_EQ(it->second.size(), 2); } else if (cd->identifier == "G") { EXPECT_EQ(cd->generic->assumptionCollection.size(), 1); auto it = cd->generic->assumptionCollection.begin(); EXPECT_EQ(it->second.size(), 2); } } } } TEST_F(TypeCheckerTest, DISABLED_SpawnTest) { #ifdef _WIN32 srcPath = projectPath + "\\unittests\\Sema\\SemaCangjieFiles\\"; #elif __unix__ srcPath = projectPath + "/unittests/Sema/SemaCangjieFiles/"; #endif auto srcFile = srcPath + "spawn.cj"; std::string failedReason; auto content = FileUtil::ReadFileContent(srcFile, failedReason); if (!content.has_value()) { diag.DiagnoseRefactor( DiagKindRefactor::module_read_file_to_buffer_failed, DEFAULT_POSITION, srcFile, failedReason); } instance->code = std::move(content.value()); instance->invocation.globalOptions.implicitPrelude = true; instance->Compile(); auto expectedErrorCount = 0; EXPECT_EQ(diag.GetErrorCount(), expectedErrorCount); EXPECT_EQ(instance->GetSourcePackages()[0]->files.size(), 1); Ptr targetFutureObj1{nullptr}; Ptr targetFutureObj2{nullptr}; for (auto& decl : instance->GetSourcePackages()[0]->files[0]->decls) { auto main = As(decl.get()); EXPECT_TRUE(main); EXPECT_TRUE(main->funcBody->body->body.size() == 4); targetFutureObj1 = main->funcBody->body->body[1].get(); targetFutureObj2 = main->funcBody->body->body[2].get(); } auto var1 = As(targetFutureObj1); auto var2 = As(targetFutureObj2); EXPECT_TRUE(var1); EXPECT_TRUE(var2); auto ty1 = dynamic_cast(var1->ty.get()); auto ty2 = dynamic_cast(var2->ty.get()); EXPECT_TRUE(ty1 && ty2 && ty1 == ty2); EXPECT_TRUE(ty1->decl->identifier == "Future"); EXPECT_TRUE(ty1->typeArgs.size() == 1); EXPECT_TRUE(ty1->typeArgs[0]->kind == TypeKind::TYPE_INT64); } cangjie_compiler-1.0.7/unittests/TestCompilerInstance/000077500000000000000000000000001510705540100231505ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/TestCompilerInstance/CMakeLists.txt000066400000000000000000000005101510705540100257040ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. add_library(TestCompilerInstanceObject OBJECT TestCompilerInstance.cpp) cangjie_compiler-1.0.7/unittests/TestCompilerInstance/TestCompilerInstance.cpp000066400000000000000000000051461510705540100277610ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the TestCompilerInstance. */ #include "TestCompilerInstance.h" #include "cangjie/IncrementalCompilation/ASTCacheCalculator.h" #include "cangjie/Parse/Parser.h" using namespace Cangjie; using namespace AST; namespace { RawMangled2DeclMap RunASTCacheCalculation(const Package& sourcePackage, const GlobalOptions op) { Cangjie::IncrementalCompilation::ASTCacheCalculator pc{ sourcePackage, std::make_pair(op.enableCompileDebug, op.displayLineInfo)}; pc.Walk(); return pc.mangled2Decl; } } // namespace bool TestCompilerInstance::Compile(CompileStage stage) { if (!PerformParse()) { return false; } if (stage == CompileStage::PARSE) { return true; } // Preprocess for incremental mangle. if (!srcPkgs.empty() && stage == CompileStage::DESUGAR_AFTER_SEMA) { rawMangleName2DeclMap = RunASTCacheCalculation(*srcPkgs[0], invocation.globalOptions); } auto modular = ModularizeCompilation(); auto importRes = PerformImportPackage(); if (stage == CompileStage::IMPORT_PACKAGE) { return modular && importRes; } auto macroRes = PerformMacroExpand(); auto semaRes = PerformSema(); if (stage == CompileStage::SEMA || stage == CompileStage::DESUGAR_AFTER_SEMA) { return Utils::AllOf(importRes, macroRes, semaRes, modular); } auto giRes = PerformGenericInstantiation(); return Utils::AllOf(importRes, macroRes, semaRes, giRes, modular); } bool TestCompilerInstance::ParseCode() { auto package = MakeOwned(); auto fileID = GetSourceManager().AddSource("", code); Parser parser(fileID, code, diag, GetSourceManager(), invocation.globalOptions.enableAddCommentToAst, invocation.globalOptions.compileCjd); parser.SetCompileOptions(invocation.globalOptions); auto file = parser.ParseTopLevel(); GetSourceManager().AddComments(parser.GetCommentsMap()); if (!file->package) { package->fullPackageName = DEFAULT_PACKAGE_NAME; } else { package->fullPackageName = file->package->packageName; } file->curPackage = package.get(); package->files.push_back(std::move(file)); srcPkgs.push_back(std::move(package)); return true; } bool TestCompilerInstance::PerformParse() { if (!code.empty()) { return ParseCode(); } return compileStrategy->Parse(); } cangjie_compiler-1.0.7/unittests/TestCompilerInstance/TestCompilerInstance.h000066400000000000000000000034131510705540100274210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the TestCompilerInstance, used for unittest. */ #ifndef CANGJIE_FRONTEND_TESTCOMPILERINSTANCE_H #define CANGJIE_FRONTEND_TESTCOMPILERINSTANCE_H #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Modules/PackageManager.h" #include "cangjie/Sema/TypeManager.h" namespace Cangjie { class TestCompilerInstance : public CompilerInstance { public: TestCompilerInstance(CompilerInvocation& invocation, DiagnosticEngine& diag) : CompilerInstance(invocation, diag) { compileOnePackageFromSrcFiles = false; #ifdef PROJECT_SOURCE_DIR // Gets the absolute path of the project from the compile parameter. cangjieHome = FileUtil::JoinPath(FileUtil::JoinPath(PROJECT_SOURCE_DIR, "build"), "build"); #else // Just in case, give it a default value. // Assume the initial is in the build directory. cangjieHome = FileUtil::JoinPath(FileUtil::JoinPath(".", "build"), "build"); #endif CJC_NULLPTR_CHECK(compileStrategy); // Was created in ctor of 'CompilerInstance'. } bool PerformParse() override; bool PerformSema() override { return compileStrategy->Sema(); } bool Compile(CompileStage stage = CompileStage::GENERIC_INSTANTIATION) override; const auto& GetBuildOrders() { return packageManager->GetBuildOrders(); } /** * Input source code. */ std::string code; private: bool ParseCode(); }; } // namespace Cangjie #endif // CANGJIE_FRONTEND_TESTCOMPILERINSTANCE_H cangjie_compiler-1.0.7/unittests/Utils/000077500000000000000000000000001510705540100201515ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Utils/CMakeLists.txt000066400000000000000000000035171510705540100227170ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. include_directories(${CMAKE_SOURCE_DIR}/src/Driver/Toolchains) set(BASE_UTIL_SRC $ $ $ $ $ $ $ ) add_executable(UtilsTests UtilsTests.cpp) if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) target_link_libraries( UtilsTests ${CMAKE_DL_LIBS} ${BASE_UTIL_SRC} $ $ GTest::gtest GTest::gtest_main) endif() add_test(NAME UtilsTests COMMAND UtilsTests) set(CAST_TEST_SRC CastASTTests.cpp CastTyTests.cpp) set(CAST_TEST_LIB cangjie-lsp ${CMAKE_DL_LIBS}) add_executable( CastTest ${CAST_TEST_SRC}) target_link_libraries( CastTest cangjie-lsp ${CAST_TEST_LIB} GTest::gtest GTest::gtest_main) set_target_properties(CastTest PROPERTIES COMPILE_FLAGS "-DUT=1") add_test(NAME CastTest COMMAND CastTest) if(CMAKE_BUILD_TYPE MATCHES Release) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fomit-frame-pointer") add_definitions(-DRELEASE) add_executable(SignalTests SignalTests.cpp) target_link_libraries( SignalTests ${CMAKE_DL_LIBS} ${BASE_UTIL_SRC} GTest::gtest GTest::gtest_main) add_test(NAME SignalTests COMMAND SignalTests) add_executable(SignalTestCJC SignalTestCJC.cpp ${CANGJIE_SRC_OBJECTS}) target_link_libraries(SignalTestCJC ${LINK_LIBS}) endif() add_subdirectory(UnicodeTest) cangjie_compiler-1.0.7/unittests/Utils/CangjieFiles/000077500000000000000000000000001510705540100224745ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Utils/CangjieFiles/emptyfile.cj000066400000000000000000000000001510705540100247760ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Utils/CangjieFiles/pkg01.cj000066400000000000000000000024741510705540100237430ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. package testpkg public let one : Int32 = 1 public let two : Int32 = 2 public class pkg { public var aaa:Int32 = 1 protected var one:Int32 = 1 private var a:Int32 = 0 public static var aa:Int32 = 0 public init() { this.a = 123 } public init(a: Int32) { this.a = a } public func add(a: Int32, b: Int32){ return a + b + this.one } public open func add2(a: Int32, b: Int32) { return a + b } public static func add3(a: Int32, b: Int32) { return a + b } } public struct Test{ public let width: Int32 = 2 public let length: Int32 = 5 public init(a:Int32, b:Int32) { width = a length = b } public func area(): Int32 { return width * length } } public enum TimeUnit { Year | Month | Day | Hour } public func testpkgFunc(): Int32 { return 1 } public interface iPkg { let aclass: Int32 = 1 func foo() : Int32 default func getNum():Int32 { return 1 } static func getSum():Int32 { return 1 } }cangjie_compiler-1.0.7/unittests/Utils/CangjieFiles/testpkg01.cj000066400000000000000000000014031510705540100246320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. package testpkg01 internal import testpkg.* public class pkg01 <: pkg { public var aaa:Int32 = 1 protected var one:Int32 = 1 private var a:Int32 = 0 public static var aa:Int32 = 0 public init() { this.a = 100 } public init(a: Int32) { this.a = a } } public interface iPkg01 <: iPkg { let aclass: Int32 = 1 func foo() : Int32 override default func getNum():Int32 { return 1 } static func getSum():Int32 { return 1 } } cangjie_compiler-1.0.7/unittests/Utils/CastASTTests.cpp000066400000000000000000000215161510705540100231470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include #include "gtest/gtest.h" #define private public #include "cangjie/AST/Node.h" #include "cangjie/Basic/DiagnosticEngine.h" #ifdef _WIN32 #define STATIC_ASSERT static_assert #else struct DummyExpr : Cangjie::AST::OverloadableExpr { DummyExpr() : Cangjie::AST::OverloadableExpr(Cangjie::AST::ASTKind::INVALID_EXPR) { } }; // Replace the macro defined in 'ASTCasting.h' #define STATIC_ASSERT(CONDITION) EXPECT_TRUE((std::is_same_v) || (CONDITION)) #endif #include "cangjie/AST/ASTCasting.h" using namespace Cangjie; template constexpr bool IgnoredType() { return ShouldInstantiate::value; } static const std::vector ignoredKind = { AST::ASTKind::DECL, AST::ASTKind::EXPR, AST::ASTKind::NODE, AST::ASTKind::PATTERN, AST::ASTKind::CLASS_LIKE_DECL, AST::ASTKind::INVALID_DECL, AST::ASTKind::INVALID_TYPE, }; class CastASTTests : public testing::Test { protected: static void SetUpTestCase() { #define ASTKIND(KIND, VALUE, TYPE, SIZE) \ if constexpr (IgnoredType()) { \ auto nodePtr = MakeOwned(); \ astMap.emplace(AST::ASTKind::KIND, nodePtr.get()); \ astPool.emplace_back(std::move(nodePtr)); \ } #include "cangjie/AST/ASTKind.inc" #undef ASTKIND astPool.emplace_back(MakeOwned(TokenKind::OPEN, DEFAULT_POSITION)); astMap.emplace(AST::ASTKind::MODIFIER, astPool.back().get()); astPool.emplace_back(MakeOwned(AST::BuiltInType::ARRAY)); astMap.emplace(AST::ASTKind::BUILTIN_DECL, astPool.back().get()); auto pkg = RawStaticCast(astMap[AST::ASTKind::PACKAGE]); astPool.emplace_back(MakeOwned(*pkg)); astMap.emplace(AST::ASTKind::PACKAGE_DECL, astPool.back().get()); astPool.emplace_back(MakeOwned(SrcIdentifier{"E"})); astMap.emplace(AST::ASTKind::VAR_OR_ENUM_PATTERN, astPool.back().get()); astPool.emplace_back(MakeOwned(DEFAULT_POSITION)); astMap.emplace(AST::ASTKind::THIS_TYPE, astPool.back().get()); astPool.emplace_back(MakeOwned(AST::TypeKind::TYPE_INT64)); astMap.emplace(AST::ASTKind::PRIMITIVE_TYPE_EXPR, astPool.back().get()); auto arg = MakeOwned(); arg->name = "APILevel"; arg->expr = MakeOwned(); astPool.emplace_back(MakeOwned( std::move(arg), MakeOwned(), MakeOwned())); astMap.emplace(AST::ASTKind::IF_AVAILABLE_EXPR, astPool.back().get()); } static std::map> astMap; static std::vector> astPool; }; std::map> CastASTTests::astMap = {}; std::vector> CastASTTests::astPool = {}; TEST_F(CastASTTests, VerifyCastingCount) { // Expect casting size is hardcode, should be changed manually when any new AST node is added. // If the new added type is an intermediate type, it may should be added into 'IgnoredType()'. // NOTE: 'NODE' is the lase enum value in 'ASTKind'. size_t totalCount = static_cast(AST::ASTKind::NODE) + 1; EXPECT_TRUE(totalCount > ignoredKind.size()); size_t size = totalCount - ignoredKind.size(); EXPECT_EQ(size, 106); EXPECT_EQ(astPool.size(), size); // Added node's kind should same with key. for (auto [kind, node] : astMap) { EXPECT_EQ(node->astKind, kind); } } TEST_F(CastASTTests, VerifyMonoCasting) { for (auto [_, ptrNode] : astMap) { auto node = ptrNode.get(); const AST::Node* constNode = node; #define ASTKIND(KIND, VALUE, TYPE, SIZE) \ EXPECT_EQ(DynamicCast(node), dynamic_cast(node)); \ EXPECT_EQ(DynamicCast(node), dynamic_cast(node)); \ EXPECT_EQ(DynamicCast(constNode), dynamic_cast(constNode)); \ EXPECT_EQ(DynamicCast(constNode), dynamic_cast(constNode)); \ EXPECT_EQ(Is(node), (dynamic_cast(node) != nullptr)); \ EXPECT_EQ(Is(node), (dynamic_cast(node) != nullptr)); #include "cangjie/AST/ASTKind.inc" #undef ASTKIND } } TEST_F(CastASTTests, VerifyIntermediateCasting) { for (auto [_, ptrNode] : astMap) { auto node = ptrNode.get(); EXPECT_EQ(DynamicCast(node), dynamic_cast(node)); EXPECT_EQ(DynamicCast(node), dynamic_cast(node)); EXPECT_EQ(DynamicCast(node), dynamic_cast(node)); EXPECT_EQ(DynamicCast(node), dynamic_cast(node)); EXPECT_EQ(DynamicCast(node), dynamic_cast(node)); EXPECT_EQ(Is(node), dynamic_cast(node) != nullptr); EXPECT_EQ(Is(node), dynamic_cast(node) != nullptr); EXPECT_EQ(Is(node), dynamic_cast(node) != nullptr); EXPECT_EQ(Is(node), dynamic_cast(node) != nullptr); EXPECT_EQ(Is(node), dynamic_cast(node) != nullptr); EXPECT_EQ(Is(*node), dynamic_cast(node) != nullptr); } } #ifndef _WIN32 // Only test in linux // If the 'NodeType' is not defined, the 'DynamicCast' will fail to be compiled. template <> struct NodeType { static const AST::ASTKind kind = AST::ASTKind::NODE; }; // Test for the template can guard new added type. TEST_F(CastASTTests, TryAddNewType) { // Converting to new type without specific instantiation of 'SubTypeChecker' will trigger 'static_assert'. DynamicCast(astMap[AST::ASTKind::SUBSCRIPT_EXPR]); EXPECT_FALSE(NodeType::kind != AST::ASTKind::NODE); } #endif TEST_F(CastASTTests, VerifyStaticCasting) { std::map>::iterator it; #define ASTKIND(KIND, VALUE, TYPE, SIZE) \ it = astMap.find(AST::ASTKind::KIND); \ if (it != astMap.end()) { \ EXPECT_TRUE(StaticCast(it->second.get()) != nullptr); \ EXPECT_TRUE(StaticCast(it->second.get()) != nullptr); \ StaticCast(*it->second.get()); \ const AST::Node* constNode = it->second.get(); \ EXPECT_TRUE(StaticCast(constNode) != nullptr); \ EXPECT_TRUE(StaticCast(constNode) != nullptr); \ auto& n1 = StaticCast(*constNode); \ auto& n2 = StaticCast(*constNode); \ EXPECT_TRUE(&n1 == &n2); \ } #include "cangjie/AST/ASTKind.inc" #undef ASTKIND } cangjie_compiler-1.0.7/unittests/Utils/CastTyTests.cpp000066400000000000000000000150461510705540100231150ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include #include "gtest/gtest.h" #define private public #define STATIC_ASSERT static_assert #include "cangjie/AST/ASTCasting.h" #include "cangjie/Sema/TypeManager.h" using namespace Cangjie; using namespace AST; static const std::vector ignoredKind = { TypeKind::TYPE_INVALID, TypeKind::TYPE_ANY, TypeKind::TYPE_QUEST, TypeKind::TYPE_INITIAL}; class CastTyTests : public testing::Test { protected: static void SetUpTestCase() { tyMap.emplace(TypeKind::TYPE_CSTRING, TypeManager::GetCStringTy()); for (size_t i = 0; i < TypeManager::primitiveTys.size(); ++i) { auto kind = static_cast(i + static_cast(TYPE_PRIMITIVE_MIN)); tyMap.emplace(kind, &TypeManager::primitiveTys[i]); } tyPool.emplace_back(MakeOwned(tyMap[TypeKind::TYPE_INT8], 1)); tyMap.emplace(TypeKind::TYPE_ARRAY, tyPool.back().get()); tyPool.emplace_back(MakeOwned(tyMap[TypeKind::TYPE_INT8], 1)); tyMap.emplace(TypeKind::TYPE_VARRAY, tyPool.back().get()); tyPool.emplace_back(MakeOwned(tyMap[TypeKind::TYPE_INT8])); tyMap.emplace(TypeKind::TYPE_POINTER, tyPool.back().get()); std::vector> elemTys{tyMap[TypeKind::TYPE_INT8], tyMap[TypeKind::TYPE_INT16]}; tyPool.emplace_back(MakeOwned(elemTys)); tyMap.emplace(TypeKind::TYPE_TUPLE, tyPool.back().get()); tyPool.emplace_back(MakeOwned(elemTys, tyMap[TypeKind::TYPE_INT8])); tyMap.emplace(TypeKind::TYPE_FUNC, tyPool.back().get()); tyPool.emplace_back(MakeOwned(Utils::VecToSet(elemTys))); tyMap.emplace(TypeKind::TYPE_UNION, tyPool.back().get()); tyPool.emplace_back(MakeOwned(Utils::VecToSet(elemTys))); tyMap.emplace(TypeKind::TYPE_INTERSECTION, tyPool.back().get()); auto id = MakeOwned(); tyPool.emplace_back(MakeOwned("interface", *id, elemTys)); tyMap.emplace(TypeKind::TYPE_INTERFACE, tyPool.back().get()); astPool.emplace_back(std::move(id)); auto cd = MakeOwned(); tyPool.emplace_back(MakeOwned("class", *cd, elemTys)); tyMap.emplace(TypeKind::TYPE_CLASS, tyPool.back().get()); astPool.emplace_back(std::move(cd)); auto ed = MakeOwned(); tyPool.emplace_back(MakeOwned("enum", *ed, elemTys)); tyMap.emplace(TypeKind::TYPE_ENUM, tyPool.back().get()); astPool.emplace_back(std::move(ed)); auto sd = MakeOwned(); tyPool.emplace_back(MakeOwned("struct", *sd, elemTys)); tyMap.emplace(TypeKind::TYPE_STRUCT, tyPool.back().get()); astPool.emplace_back(std::move(sd)); auto tad = MakeOwned(); tyPool.emplace_back(MakeOwned("typealias", *tad, elemTys)); tyMap.emplace(TypeKind::TYPE, tyPool.back().get()); astPool.emplace_back(std::move(tad)); auto gpd = MakeOwned(); tyPool.emplace_back(MakeOwned("T", *gpd)); tyMap.emplace(TypeKind::TYPE_GENERICS, tyPool.back().get()); astPool.emplace_back(std::move(gpd)); } static std::map> tyMap; static std::vector> tyPool; static std::vector> astPool; }; std::map> CastTyTests::tyMap = {}; std::vector> CastTyTests::tyPool = {}; std::vector> CastTyTests::astPool = {}; TEST_F(CastTyTests, VerifyCastingCount) { // Expect casting size is hardcode, should be changed manually when any new Ty is added. // NOTE: 'TYPE_INITIAL' is the lase enum value in 'TypeKind'. size_t totalCount = static_cast(TypeKind::TYPE_INITIAL) + 1; EXPECT_TRUE(totalCount > ignoredKind.size()); size_t size = totalCount - ignoredKind.size(); EXPECT_EQ(size, 33); EXPECT_EQ(tyMap.size(), size); // Added ty's kind should same with key. for (auto [kind, ty] : tyMap) { EXPECT_EQ(ty->kind, kind); } } TEST_F(CastTyTests, VerifyMonoCasting) { for (auto [_, ty] : tyMap) { #define DEFINE_TY(TYPE, KIND) \ EXPECT_EQ(DynamicCast(ty), dynamic_cast(ty.get())); \ EXPECT_EQ(Is(ty), dynamic_cast(ty.get()) != nullptr); \ EXPECT_EQ(Is(ty), dynamic_cast(ty.get()) != nullptr); #include "TyKind.inc" #undef DEFINE_TY } } TEST_F(CastTyTests, VerifyIntermediateCasting) { for (auto [_, ty] : tyMap) { EXPECT_EQ(DynamicCast(ty), dynamic_cast(ty.get())); EXPECT_EQ(DynamicCast(ty), dynamic_cast(ty.get())); EXPECT_EQ(DynamicCast(ty), dynamic_cast(ty.get())); EXPECT_EQ(DynamicCast(ty), dynamic_cast(ty.get())); EXPECT_EQ(Is(ty), dynamic_cast(ty.get()) != nullptr); EXPECT_EQ(Is(ty), dynamic_cast(ty.get()) != nullptr); EXPECT_EQ(Is(ty), dynamic_cast(ty.get()) != nullptr); EXPECT_EQ(Is(ty), dynamic_cast(ty.get()) != nullptr); } auto ed = MakeOwned(); RefEnumTy refEnumTy("enum", *ed, {}); EXPECT_EQ(DynamicCast(&refEnumTy), dynamic_cast(&refEnumTy)); EXPECT_EQ(DynamicCast(&refEnumTy), dynamic_cast(&refEnumTy)); EXPECT_EQ(Is(&refEnumTy), dynamic_cast(&refEnumTy) != nullptr); EXPECT_EQ(Is(&refEnumTy), dynamic_cast(&refEnumTy) != nullptr); auto cd = MakeOwned(); ClassThisTy thisTy("this", *cd, {}); EXPECT_EQ(DynamicCast(&thisTy), dynamic_cast(&thisTy)); EXPECT_EQ(DynamicCast(&thisTy), dynamic_cast(&thisTy)); EXPECT_EQ(Is(&thisTy), dynamic_cast(&thisTy) != nullptr); EXPECT_EQ(Is(&thisTy), dynamic_cast(&thisTy) != nullptr); } cangjie_compiler-1.0.7/unittests/Utils/SignalTest.cj000066400000000000000000000005551510705540100225510ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. let aaa = "qwutye" let bbb = 123 main() { println(aaa) println(aaa.size + bbb) return 0; } cangjie_compiler-1.0.7/unittests/Utils/SignalTestCJC.cpp000066400000000000000000000114551510705540100232600ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #define main cjc #include "../../src/main.cpp" #undef main #include #include #include #include namespace { #define SIGCBF(sig) \ void sig##Callback() \ { \ if (raise(sig) == 0) { \ while (1) { \ } \ } \ } SIGCBF(SIGABRT) SIGCBF(SIGFPE) SIGCBF(SIGSEGV) SIGCBF(SIGILL) #ifdef __unix__ SIGCBF(SIGTRAP) SIGCBF(SIGBUS) #endif SIGCBF(SIGINT) void RecursiveFunction(int* arr, int size) { if (size == 0) { return; } int r = rand() % size; int* a = (int*)alloca(size * sizeof(int)); for (int i = 0; i < size; i++) { a[i] = arr[r] * arr[i]; } RecursiveFunction(a, size - 1); } void StackOverflowCallback() { int num = rand(); int arraySize = 102400; int* a = (int*)alloca(arraySize * sizeof(int)); for (int i = 0; i < arraySize; i++) { a[i] = num * i + a[i]; } RecursiveFunction(a, arraySize); } #ifdef __unix__ const std::unordered_map signalStringCallbackFuncMap = { {"SIGABRT", SIGABRTCallback}, {"SIGFPE", SIGFPECallback}, {"SIGSEGV", SIGSEGVCallback}, {"SIGILL", SIGILLCallback}, {"SIGTRAP", SIGTRAPCallback}, {"SIGBUS", SIGBUSCallback}, {"StackOverflow", StackOverflowCallback}, {"SIGINT", SIGINTCallback}}; #elif _WIN32 const std::unordered_map signalStringCallbackFuncMap = { {"SIGABRT", SIGABRTCallback}, {"SIGFPE", SIGFPECallback}, {"SIGSEGV", SIGSEGVCallback}, {"SIGILL", SIGILLCallback}, {"StackOverflow", StackOverflowCallback}, {"SIGINT", SIGINTCallback}}; #endif const std::unordered_map stringTriggerPointerMap = { {"main", Cangjie::SignalTest::TriggerPointer::MAIN_POINTER}, {"driver", Cangjie::SignalTest::TriggerPointer::DRIVER_POINTER}, {"parser", Cangjie::SignalTest::TriggerPointer::PARSER_POINTER}, {"sema", Cangjie::SignalTest::TriggerPointer::SEMA_POINTER}, {"chir", Cangjie::SignalTest::TriggerPointer::CHIR_POINTER}, {"codegen", Cangjie::SignalTest::TriggerPointer::CODEGEN_POINTER}}; // Linux: SIGABRT, SIGFPE, SIGSEGV, SIGILL, SIGTRAP, SIGBUS // Windows: SIGABRT, SIGFPE, SIGSEGV, SIGILL void SetCallBackFunc(std::string arg) { Cangjie::SignalTest::SignalTestCallbackFuncType fp = nullptr; Cangjie::SignalTest::TriggerPointer tp = Cangjie::SignalTest::TriggerPointer::NON_POINTER; auto pos = arg.find('_'); if (pos == std::string::npos) { return; } std::string fpStr = arg.substr(0, pos); std::string tempStr = arg.substr(pos + 1, arg.size() - pos - 1); pos = tempStr.find('_'); if (pos == std::string::npos) { return; } std::string tpStr = tempStr.substr(0, pos); int errorFd = STDERR_FILENO; if (fpStr != "SIGINT") { std::string fdStr = tempStr.substr(pos + 1, tempStr.size() - pos - 1); errorFd = open(fdStr.c_str(), O_WRONLY); } auto callbackFuncFound = signalStringCallbackFuncMap.find(fpStr); if (callbackFuncFound != signalStringCallbackFuncMap.end()) { fp = callbackFuncFound->second; } else { return; } auto triggerPointerFound = stringTriggerPointerMap.find(tpStr); if (triggerPointerFound != stringTriggerPointerMap.end()) { tp = triggerPointerFound->second; } else { fp = nullptr; return; } Cangjie::SignalTest::SetSignalTestCallbackFunc(fp, tp, errorFd); } } // namespace int main(int argc, const char** argv, const char** envp) { SetCallBackFunc(std::string(argv[argc - 1])); std::vector tempArgv; for (int i = 0; i < argc - 1; i++) { tempArgv.emplace_back(const_cast(argv[i])); } tempArgv.emplace_back(nullptr); return cjc(tempArgv.size() - 1, const_cast(tempArgv.data()), envp); } cangjie_compiler-1.0.7/unittests/Utils/SignalTests.cpp000066400000000000000000000215261510705540100231230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "gtest/gtest.h" #include "cangjie/Basic/StringConvertor.h" #include "cangjie/Basic/Version.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/ICEUtil.h" #include "cangjie/Utils/Signal.h" #ifdef __unix__ #include #include #elif _WIN32 #include #endif #include #include #include using namespace Cangjie; #ifdef PROJECT_SOURCE_DIR // Gets the absolute path of the project from the compile parameter. const std::string PRPJECT_PATH = PROJECT_SOURCE_DIR; #else // Just in case, give it a default value. // Assume the initial is in the build directory. const std::string PRPJECT_PATH = ".."; #endif #ifdef __unix__ const int STACK_OVERFLOW_RETURN_CODE = SIGSEGV + 128; const std::unordered_map signalStringValueMap = {{"SIGABRT", SIGABRT}, {"SIGFPE", SIGFPE}, {"SIGSEGV", SIGSEGV}, {"SIGILL", SIGILL}, {"SIGTRAP", SIGTRAP}, {"SIGBUS", SIGBUS}}; #elif _WIN32 const DWORD STACK_OVERFLOW_RETURN_CODE = EXCEPTION_STACK_OVERFLOW; const std::unordered_map signalStringValueMap = { {"SIGABRT", SIGABRT}, {"SIGFPE", SIGFPE}, {"SIGSEGV", SIGSEGV}, {"SIGILL", SIGILL}}; #endif const std::unordered_map moduleValueMap = { {"main", static_cast(CompileStage::COMPILE_STAGE_NUMBER)}, {"parser", static_cast(CompileStage::PARSE)}, {"sema", static_cast(CompileStage::SEMA)}, {"chir", static_cast(CompileStage::CHIR)}, {"codegen", static_cast(CompileStage::CODEGEN)}, {"driver", static_cast(CompileStage::COMPILE_STAGE_NUMBER)}}; const std::string TEMP_CJ_FILE_NAME = PRPJECT_PATH + "/unittests/Utils/SignalTest.cj"; const std::string TEMP_ERROR_OUTPUT_NAME = "./tempError.txt"; class SignalTests : public testing::Test { protected: void SetUp() override { #ifdef _WIN32 char* path = getcwd(NULL, 0); if (path == nullptr) { std::cerr << "Failed to get PWD!" << std::endl; _exit(1); } std::string tempPath = std::string(path); std::optional tempValue = StringConvertor::StringToWString(tempPath); if (!tempValue.has_value()) { std::cerr << "Failed to set TMP environment variable!" << std::endl; _exit(1); } _wputenv_s(L"TMP", tempValue.value().c_str()); #else char* path = getenv("PWD"); setenv("TMPDIR", path, 1); #endif std::fstream tempFile; tempFile.open(TEMP_ERROR_OUTPUT_NAME, std::fstream::out); tempFile.close(); } void TearDown() override { FileUtil::Remove(TEMP_ERROR_OUTPUT_NAME); } }; std::string GetSignalString(std::string& signalValue, std::string& module) { auto moduleStr = moduleValueMap.find(module); if (moduleStr == moduleValueMap.end()) { return ""; } std::string result1 = Cangjie::ICE::MSG_PART_ONE + Cangjie::SIGNAL_MSG_PART_ONE; std::string result2 = Cangjie::SIGNAL_MSG_PART_TWO + Cangjie::ICE::MSG_PART_TWO + std::to_string(moduleStr->second) + "\n"; if (signalValue == "StackOverflow") { #ifdef __unix__ return CANGJIE_COMPILER_VERSION + "\n" + result1 + std::to_string(SIGSEGV) + result2; #elif _WIN32 return CANGJIE_COMPILER_VERSION + "\n" + result1 + std::to_string(STACK_OVERFLOW_RETURN_CODE) + result2; #endif } auto found = signalStringValueMap.find(signalValue); if (found != signalStringValueMap.end()) { return CANGJIE_COMPILER_VERSION + "\n" + result1 + std::to_string(found->second) + result2; } return ""; } void VerifyDeleteTempFile() { #ifdef _WIN32 FILE* fp = popen("dir", "r"); #else FILE* fp = popen("ls", "r"); #endif std::string data; while (true) { int c = fgetc(fp); if (c <= 0) { break; } data.push_back(static_cast(c)); } pclose(fp); std::string tempFileName = "cangjie-tmp-"; const char* index = strstr(data.c_str(), tempFileName.c_str()); EXPECT_TRUE(index == nullptr); } void VerifyErrorOutput(std::string signalValue, std::string module) { char c; std::string errorStr; std::fstream tempFile; tempFile.open(TEMP_ERROR_OUTPUT_NAME); while (tempFile.get(c)) { errorStr.push_back(c); } tempFile.close(); std::string sigStr = GetSignalString(signalValue, module); EXPECT_EQ(errorStr, sigStr); VerifyDeleteTempFile(); } #ifdef __unix__ #define MAX_PATH 4096 int ExecuteProcess(std::string signalValue, std::string triggerPoint) { std::stringstream ss; ss << signalValue << "_" << triggerPoint << "_" << TEMP_ERROR_OUTPUT_NAME; std::string commandLine = ss.str(); char buffer[MAX_PATH] = {0}; if (readlink("/proc/self/exe", buffer, MAX_PATH) == -1) { return -1; } std::string exePath = FileUtil::GetDirPath(std::string(buffer)) + "/SignalTestCJC"; pid_t pid; pid_t wpid; int status; pid = fork(); if (pid == 0) { if (execl(exePath.c_str(), exePath.c_str(), TEMP_CJ_FILE_NAME.c_str(), commandLine.c_str(), nullptr) == -1) { _exit(1); } } else if (pid > 0) { wpid = wait(&status); if (wpid == -1) { return -1; } if (WIFEXITED(status)) { return WEXITSTATUS(status); // return coce } else if (WIFSIGNALED(status)) { WTERMSIG(status); // signal code } } else { return -1; } return -1; } #elif _WIN32 DWORD ExecuteProcess(std::string signalValue, std::string triggerPoint) { char buffer[MAX_PATH]; GetModuleFileName(NULL, buffer, MAX_PATH); std::string exePath = FileUtil::GetDirPath(std::string(buffer)) + "\\SignalTestCJC.exe"; std::stringstream ss; ss << exePath << " " << TEMP_CJ_FILE_NAME << " " << signalValue << "_" << triggerPoint << "_" << TEMP_ERROR_OUTPUT_NAME; std::string commandLine = ss.str(); STARTUPINFOA si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); if (!CreateProcessA(exePath.c_str(), commandLine.data(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { return 1; } WaitForSingleObject(pi.hProcess, INFINITE); DWORD exit_code; if (FALSE == GetExitCodeProcess(pi.hProcess, &exit_code)) { return 1; } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return exit_code; } #endif #define CT(sig, module) \ TEST_F(SignalTests, module##Signal##sig) \ { \ EXPECT_EQ(ExecuteProcess(#sig, #module), sig + 128); \ VerifyErrorOutput(#sig, #module); \ } #define CTSO(module) \ TEST_F(SignalTests, module##StackOverflow) \ { \ EXPECT_EQ(ExecuteProcess("StackOverflow", #module), STACK_OVERFLOW_RETURN_CODE); \ VerifyErrorOutput("StackOverflow", #module); \ } CT(SIGABRT, main) CT(SIGFPE, main) CT(SIGSEGV, main) CT(SIGILL, main) #if __unix__ CT(SIGTRAP, main) CT(SIGBUS, main) #endif CTSO(main) CT(SIGABRT, parser) CT(SIGFPE, parser) CT(SIGSEGV, parser) CT(SIGILL, parser) #if __unix__ CT(SIGTRAP, parser) CT(SIGBUS, parser) #endif CTSO(parser) // CT(SIGABRT, sema) // CT(SIGFPE, sema) // CT(SIGSEGV, sema) // CT(SIGILL, sema) // #if __unix__ // CT(SIGTRAP, sema) // CT(SIGBUS, sema) // #endif // CTSO(sema) // CT(SIGABRT, chir) // CT(SIGFPE, chir) // CT(SIGSEGV, chir) // CT(SIGILL, chir) // #if __unix__ // CT(SIGTRAP, chir) // CT(SIGBUS, chir) // #endif // CTSO(chir) // CT(SIGABRT, codegen) // CT(SIGFPE, codegen) // CT(SIGSEGV, codegen) // CT(SIGILL, codegen) // #if __unix__ // CT(SIGTRAP, codegen) // CT(SIGBUS, codegen) // #endif // CTSO(codegen) // CT(SIGABRT, driver) // CT(SIGFPE, driver) // CT(SIGSEGV, driver) // CT(SIGILL, driver) // #if __unix__ // CT(SIGTRAP, driver) // CT(SIGBUS, driver) // #endif // CTSO(driver) cangjie_compiler-1.0.7/unittests/Utils/TyKind.inc000066400000000000000000000017111510705540100220460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. DEFINE_TY(AST::ArrayTy, AST::TypeKind::TYPE_ARRAY); DEFINE_TY(AST::PointerTy, AST::TypeKind::TYPE_POINTER); DEFINE_TY(AST::CStringTy, AST::TypeKind::TYPE_CSTRING); DEFINE_TY(AST::TupleTy, AST::TypeKind::TYPE_TUPLE); DEFINE_TY(AST::FuncTy, AST::TypeKind::TYPE_FUNC); DEFINE_TY(AST::UnionTy, AST::TypeKind::TYPE_UNION); DEFINE_TY(AST::IntersectionTy, AST::TypeKind::TYPE_INTERSECTION); DEFINE_TY(AST::InterfaceTy, AST::TypeKind::TYPE_INTERFACE); DEFINE_TY(AST::ClassTy, AST::TypeKind::TYPE_CLASS); DEFINE_TY(AST::EnumTy, AST::TypeKind::TYPE_ENUM); DEFINE_TY(AST::StructTy, AST::TypeKind::TYPE_STRUCT); DEFINE_TY(AST::TypeAliasTy, AST::TypeKind::TYPE); DEFINE_TY(AST::GenericsTy, AST::TypeKind::TYPE_GENERICS); cangjie_compiler-1.0.7/unittests/Utils/UnicodeTest/000077500000000000000000000000001510705540100223775ustar00rootroot00000000000000cangjie_compiler-1.0.7/unittests/Utils/UnicodeTest/CMakeLists.txt000066400000000000000000000025361510705540100251450ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. add_executable(UnicodeTest UnicodeTest.cpp) target_link_libraries( UnicodeTest ${CMAKE_DL_LIBS} $ GTest::gtest GTest::gtest_main) add_test(NAME UnicodeTest COMMAND UnicodeTest) add_executable(GeneratedNFCTests NFC.cpp) target_link_libraries( GeneratedNFCTests ${CMAKE_DL_LIBS} $ GTest::gtest GTest::gtest_main) target_include_directories(GeneratedNFCTests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/Utils/UnicodeTables) add_test(NAME GeneratedNFCTests COMMAND GeneratedNFCTests) # regenerate unicode test data tables if(CANGJIE_GENERATE_UNICODE_TABLE) add_dependencies(GeneratedNFCTests GenerateNFCTestData) endif() add_executable(UnicodeWidthTest UnicodeWidthTest.cpp) target_link_libraries( UnicodeWidthTest ${CMAKE_DL_LIBS} $ GTest::gtest GTest::gtest_main) add_test(NAME UnicodeWidthTest COMMAND UnicodeWidthTest) if(CANGJIE_GENERATE_UNICODE_TABLE) add_dependencies(UnicodeWidthTest GenerateWidthData) endif() set(BASE_UTIL_SRC) cangjie_compiler-1.0.7/unittests/Utils/UnicodeTest/NFC.cpp000066400000000000000000000015121510705540100235100ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "gtest/gtest.h" #include "cangjie/Utils/Unicode.h" #include "NormalisationTestData.generated.inc" using namespace Cangjie; using namespace Unicode; TEST(UnicodeTest, GeneratedNFCTests) { int testDataLen{sizeof(NORMALISATION_TEST_DATA) / sizeof(NormalisationTest)}; for (int i{0}; i < testDataLen; ++i) { auto f = CanonicalRecompose(reinterpret_cast(NORMALISATION_TEST_DATA[i].source.begin()), reinterpret_cast(NORMALISATION_TEST_DATA[i].source.end())); EXPECT_EQ(f, NORMALISATION_TEST_DATA[i].nfc); } } cangjie_compiler-1.0.7/unittests/Utils/UnicodeTest/UnicodeTest.cpp000066400000000000000000000066261510705540100253430ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "gtest/gtest.h" #include "cangjie/Utils/Unicode.h" #include #include using namespace Cangjie; using namespace Cangjie::Unicode; TEST(UnicodeTest, NfcQuickCheck) { std::string okay{"ok\u00e0\u031b\u0316\u0317\u0318\u0319\u031c\u031d\u0301\u0302" "\u0303\u0304\u0305\u0306\u0307\u0308\u0309\u030a\u030b\u030c\u030d\u030e\u030f" "\u0310\u0311\u0312\u0313\u0314\u0315\u031ay"}; EXPECT_EQ(NfcQuickCheck(okay), NfcQcResult::MAYBE); // this string is itself MAYBE in respect to nfc quick check, but is stream unsafe, which is not implemented yet std::string tooMuch{"not ok\u00e0\u031b\u0316\u0317\u0318\u0319\u031c\u031d\u031e\u0301\u0302" "\u0303\u0304\u0305\u0306\u0307\u0308\u0309\u030a\u030b\u030c\u030d\u030e\u030f" "\u0310\u0311\u0312\u0313\u0314\u0315\u031ay"}; EXPECT_EQ(NfcQuickCheck(tooMuch), NfcQcResult::MAYBE); } TEST(UnicodeTest, ComposeHangul) { EXPECT_EQ(ComposeHangul(0xcea0, 0x11a7), std::nullopt); } TEST(UnicodeTest, XID) { EXPECT_FALSE(IsXIDStart(static_cast(static_cast('_')))); EXPECT_FALSE(IsXIDStart(static_cast(static_cast('$')))); EXPECT_TRUE(IsXIDContinue(static_cast(static_cast('_')))); EXPECT_FALSE(IsXIDContinue(static_cast(static_cast('$')))); EXPECT_FALSE(IsXIDContinue(0x200c)); EXPECT_FALSE(IsXIDContinue(0x200d)); // Unicode tr31 5.1.1 constexpr UTF32 XIDCONTINUE_NOT_XIDSTART[]{0xe33, 0xeb3, 0xff9e, 0xff9f}; for (auto c : XIDCONTINUE_NOT_XIDSTART) { EXPECT_TRUE(IsXIDContinue(c)); EXPECT_FALSE(IsXIDStart(c)); } // Unicode tr31 5.1.2 EXPECT_FALSE(IsXIDStart(0x037a)); } std::string GetNfc(const std::string& s) { auto p = s; NFC(p); return p; } TEST(UnicodeTest, NFD) { EXPECT_EQ(CanonicalDecompose("abc"), "abc"); EXPECT_EQ(CanonicalDecompose("\u1e0b\u01c4"), "d\u0307\u01c4"); EXPECT_EQ(CanonicalDecompose("\u2026"), "\u2026"); EXPECT_EQ(CanonicalDecompose("\u2126"), "\u03a9"); EXPECT_EQ(CanonicalDecompose("\u1e0b\u0323"), "d\u0323\u0307"); EXPECT_EQ(CanonicalDecompose("\u1e0d\u0307"), "d\u0323\u0307"); EXPECT_EQ(CanonicalDecompose("a\u0301"), "a\u0301"); EXPECT_EQ(CanonicalDecompose("\u301a"), "\u301a"); EXPECT_EQ(CanonicalDecompose("\ud4db"), "\u1111\u1171\u11b6"); EXPECT_EQ(CanonicalDecompose("\uac1c"), "\u1100\u1162"); } TEST(UnicodeTest, NFC) { EXPECT_EQ(GetNfc("abc"), "abc"); EXPECT_EQ(GetNfc("\u1e0b\u01c4"), "\u1e0b\u01c4"); EXPECT_EQ(GetNfc("\u2026"), "\u2026"); EXPECT_EQ(GetNfc("\u2126"), "\u03a9"); EXPECT_EQ(GetNfc("\u1e0b\u0323"), "\u1e0d\u0307"); EXPECT_EQ(GetNfc("\u1e0d\u0307"), "\u1e0d\u0307"); EXPECT_EQ(GetNfc("a\u0301"), "\u00e1"); EXPECT_EQ(GetNfc("\u0301a"), "\u0301a"); EXPECT_EQ(GetNfc("\ud4db"), "\ud4db"); EXPECT_EQ(GetNfc("\uac1c"), "\uac1c"); EXPECT_EQ(GetNfc("a\u0300\u0305\u0315\u05aeb"), "\u00e0\u05ae\u0305\u0315b"); } TEST(UnicodeTest, InfiniteLoop) { // no infinite loop here GetNfc("a\u0300\u0305\u0315\u5aeb"); } cangjie_compiler-1.0.7/unittests/Utils/UnicodeTest/UnicodeWidthTest.cpp000066400000000000000000000142261510705540100263360ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "gtest/gtest.h" #include "cangjie/Utils/Unicode.h" using namespace Cangjie; using namespace Unicode; TEST(UnicodeWidthTests, Str) { EXPECT_EQ(StrWidth("hello", false), 10); EXPECT_EQ(StrWidth("hello", true), 10); EXPECT_EQ(StrWidth({reinterpret_cast("\0\0\0\x01\x01"), 5}, false), 5); EXPECT_EQ(StrWidth({reinterpret_cast("\0\0\0\x01\x01"), 5}, true), 5); EXPECT_EQ(StrWidth("", true), 0); EXPECT_EQ(StrWidth("\u2081\u2082\u2083\u2084", false), 4); EXPECT_EQ(StrWidth("\u2081\u2082\u2083\u2084", true), 8); EXPECT_EQ(StrWidth("👩"), 2); EXPECT_EQ(StrWidth("🔬"), 2); EXPECT_EQ(StrWidth("👩‍🔬"), 4); } TEST(UnicodeWidthTests, SingleChar) { EXPECT_EQ(SingleCharWidth(0xff48), 2); EXPECT_EQ(SingleCharWidth(0xff48, true), 2); EXPECT_EQ(SingleCharWidth(0), 1); EXPECT_EQ(SingleCharWidth(0, true), 1); EXPECT_EQ(SingleCharWidth(0x1), 1); EXPECT_EQ(SingleCharWidth(0x1, true), 1); EXPECT_EQ(SingleCharWidth(0x2081), 1); EXPECT_EQ(SingleCharWidth(0x2081, true), 2); EXPECT_EQ(SingleCharWidth(0x0A), 1); EXPECT_EQ(SingleCharWidth(0xA, true), 1); EXPECT_EQ(SingleCharWidth('w'), 1); EXPECT_EQ(SingleCharWidth('w', true), 1); EXPECT_EQ(SingleCharWidth(0xAD), 0); EXPECT_EQ(SingleCharWidth(0xAD, true), 0); EXPECT_EQ(SingleCharWidth(0x1160), 0); EXPECT_EQ(SingleCharWidth(0x1160, true), 0); EXPECT_EQ(SingleCharWidth(0xa1), 1); EXPECT_EQ(SingleCharWidth(0xa1, true), 2); EXPECT_EQ(SingleCharWidth(0x300), 0); EXPECT_EQ(SingleCharWidth(0x300, true), 0); EXPECT_EQ(SingleCharWidth(0x1F971), 2); } TEST(UnicodeWidthTests, DefaultIgnorable) { EXPECT_EQ(SingleCharWidth(0xE0000), 0); EXPECT_EQ(SingleCharWidth(0x1160), 0); EXPECT_EQ(SingleCharWidth(0x3164), 0); EXPECT_EQ(SingleCharWidth(0xFFA0), 0); } TEST(UnicodeWidthTests, Jamo) { EXPECT_EQ(SingleCharWidth(0x1100), 2); EXPECT_EQ(SingleCharWidth(0xA97C), 2); // Special case: U+115F HANGUL CHOSEONG FILLER EXPECT_EQ(SingleCharWidth(0x115F), 2); EXPECT_EQ(SingleCharWidth(0x1160), 0); EXPECT_EQ(SingleCharWidth(0xD7C6), 0); EXPECT_EQ(SingleCharWidth(0x11A8), 0); EXPECT_EQ(SingleCharWidth(0xD7FB), 0); } TEST(UnicodeWidthTests, PrependedConcatenationMarks) { UTF32 chars[]{0x600, 0x601, 0x602, 0x603, 0x604, 0x60d, 0x110bd, 0x110cd}; for (UTF32 c : chars) { EXPECT_EQ(SingleCharWidth(c), 1); } UTF32 chars2[]{0x605, 0x70f, 0x890, 0x891, 0x8e2}; for (UTF32 c : chars2) { EXPECT_EQ(SingleCharWidth(c), 0); } } TEST(UnicodeWidthTests, InterlinearAnnotationChars) { EXPECT_EQ(SingleCharWidth(0xfff9), 1); EXPECT_EQ(SingleCharWidth(0xfffa), 1); EXPECT_EQ(SingleCharWidth(0xfffb), 1); } TEST(UnicodeWidthTests, HieroglyphFormatControls) { EXPECT_EQ(SingleCharWidth(0x13430), 1); EXPECT_EQ(SingleCharWidth(0x13436), 1); EXPECT_EQ(SingleCharWidth(0x1343c), 1); } TEST(UnicodeWidthTests, Marks) { EXPECT_EQ(SingleCharWidth(0x0301), 0); EXPECT_EQ(SingleCharWidth(0x20dd), 0); EXPECT_EQ(SingleCharWidth(0x9cb), 1); EXPECT_EQ(SingleCharWidth(0x9be), 0); } TEST(UnicodeWidthTests, DevanagariCaret) { EXPECT_EQ(SingleCharWidth(0xa8fa), 0); } TEST(UnicodeWidthTests, EmojiPresentation) { EXPECT_EQ(SingleCharWidth(0x23), 1); EXPECT_EQ(SingleCharWidth(0xfe0f), 0); EXPECT_EQ(StrWidth("\u0023\uFE0F"), 2); EXPECT_EQ(StrWidth("a\u0023\uFE0Fa"), 4); EXPECT_EQ(StrWidth("\u0023a\uFE0F"), 2); EXPECT_EQ(StrWidth("a\uFE0F"), 1); EXPECT_EQ(StrWidth("\u0023\u0023\uFE0Fa"), 4); EXPECT_EQ(StrWidth("\u002A\uFE0F"), 2); EXPECT_EQ(StrWidth("\u23F9\uFE0F"), 2); EXPECT_EQ(StrWidth("\u24C2\uFE0F"), 2); EXPECT_EQ(StrWidth("\U0001F6F3\uFE0F"), 2); EXPECT_EQ(StrWidth("\U0001F700\uFE0F"), 1); } TEST(UnicodeWidthTests, TextPresentation) { EXPECT_EQ(SingleCharWidth(0xFE0E), 0); EXPECT_EQ(SingleCharWidth(0x2648), 2); EXPECT_EQ(StrWidth("\u2648\uFE0E"), 1); EXPECT_EQ(StrWidth("\u2648\uFE0E", true), 2); EXPECT_EQ(StrWidth("\U0001F21A\uFE0E"), 2); EXPECT_EQ(StrWidth("\U0001F21A\uFE0E", true), 2); EXPECT_EQ(StrWidth("\u0301\uFE0E"), 0); EXPECT_EQ(StrWidth("\u0301\uFE0E", true), 0); EXPECT_EQ(StrWidth("a\uFE0E"), 1); EXPECT_EQ(StrWidth("a\uFE0E", true), 1); EXPECT_EQ(StrWidth("𘀀\uFE0E"), 2); EXPECT_EQ(StrWidth("𘀀\uFE0E", true), 2); } TEST(UnicodeWidthTests, ControlLineBreak) { EXPECT_EQ(SingleCharWidth(0x2028), 1); EXPECT_EQ(SingleCharWidth(0x2029), 1); EXPECT_EQ(StrWidth("\r"), 1); EXPECT_EQ(StrWidth("\n"), 1); EXPECT_EQ(StrWidth("\r\n"), 1); EXPECT_EQ(StrWidth({reinterpret_cast("\0"), 1}), 1); EXPECT_EQ(StrWidth("1\t2\r\n3\u00854"), 7); } TEST(UnicodeWidthTests, CharStringConsistent) { char data[4]; for (UTF32 a = 0; a <= 0x10ffff; ++a) { if (a >= 0xd800 && a <= 0xdfff) { // surrogate not allowed in UTF-8 continue; } int cw = SingleCharWidth(a); char* dam = data; ConvertCodepointToUTF8(a, dam); int sw = StrWidth(std::string_view{data, static_cast(dam - data)}); EXPECT_EQ(cw, sw); } } TEST(UnicodeWidthTests, LisuTones) { char data[8]; constexpr UTF32 lisuBegin{0xa4f8}; constexpr UTF32 lisuEnd{0xa4fd}; for (auto c1{lisuBegin}; c1 <= lisuEnd; ++c1) { for (auto c2{lisuBegin}; c2 <= lisuEnd; ++c2) { char* dam{data}; ConvertCodepointToUTF8(c1, dam); ConvertCodepointToUTF8(c2, dam); int sw = StrWidth(std::string_view{data, static_cast(dam - data)}); if (c1 <= 0xa4fb && c2 >= 0xa4fc) { EXPECT_EQ(sw, 1); } else { EXPECT_EQ(sw, 2); } } } EXPECT_EQ(StrWidth("ꓪꓹꓼ"), 2); EXPECT_EQ(StrWidth("ꓪꓹꓹ"), 3); EXPECT_EQ(StrWidth("ꓪꓼꓼ"), 3); } cangjie_compiler-1.0.7/unittests/Utils/UtilsTests.cpp000066400000000000000000000721041510705540100230040ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information.#include #include #include #include "gtest/gtest.h" #define private public #include "cangjie/AST/Utils.h" #include "cangjie/Driver/Toolchains/GCCPathScanner.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/FloatFormat.h" #include "cangjie/Utils/ProfileRecorder.h" #include "cangjie/Utils/SipHash.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace Cangjie::Utils; using namespace Cangjie::FileUtil; using namespace Cangjie::FloatFormat; using namespace AST; #ifdef PROJECT_SOURCE_DIR // Gets the absolute path of the project from the compile parameter. static std::string GetProjectPath() { return PROJECT_SOURCE_DIR; } #else // Just in case, give it a default value. // Assume the initial is in the build directory. static std::string GetProjectPath() { return ".."; } #endif TEST(UtilsTest, Access) { const std::string path = "./no_such_file.txt"; bool exist = Access(path, FM_EXIST); EXPECT_EQ(false, exist); } TEST(UtilsTest, FileExist) { std::string path; #ifdef _WIN32 path = "C:/Windows/System32/cmd.exe"; #else path = "/bin/bash"; #endif bool res = FileExist(path); EXPECT_TRUE(res); } TEST(UtilsTest, FindProgramByName) { std::string name = "./good"; std::string res = FindProgramByName(name, {}); EXPECT_EQ(res, name); name = "bash"; res = FindProgramByName(name, {"test/test"}); EXPECT_EQ(res, ""); } TEST(UtilsTest, GetFilePath) { std::string path = GetDirPath("lalala.cj"); EXPECT_EQ(path, "."); path = GetDirPath("/usr/local/bin/lalala.cj"); #ifdef _WIN32 EXPECT_EQ(path, "\\usr\\local\\bin"); #else EXPECT_EQ(path, "/usr/local/bin"); #endif } TEST(UtilsTest, GetFileExtensionTest) { std::string ext = GetFileExtension("lalala.cj"); EXPECT_EQ(ext, "cj"); ext = GetFileExtension("/usr/local/bin/lalala.cj"); EXPECT_EQ(ext, "cj"); ext = GetFileExtension(""); EXPECT_EQ(ext, ""); ext = GetFileExtension("/"); EXPECT_EQ(ext, ""); ext = GetFileExtension("./"); EXPECT_EQ(ext, ""); ext = GetFileExtension("out"); EXPECT_EQ(ext, ""); ext = GetFileExtension("/out"); EXPECT_EQ(ext, ""); ext = GetFileExtension("/.out"); EXPECT_EQ(ext, ""); ext = GetFileExtension("./.out"); EXPECT_EQ(ext, ""); ext = GetFileExtension("test/.out"); EXPECT_EQ(ext, ""); ext = GetFileExtension("test/.test/.out"); EXPECT_EQ(ext, ""); ext = GetFileExtension("test/.test/out"); EXPECT_EQ(ext, ""); ext = GetFileExtension("test/.test/out/"); EXPECT_EQ(ext, ""); ext = GetFileExtension("test/out.ext"); EXPECT_EQ(ext, "ext"); ext = GetFileExtension("test/.test/out.ext"); EXPECT_EQ(ext, "ext"); ext = GetFileExtension("test/.test/.out.ext"); EXPECT_EQ(ext, "ext"); } TEST(UtilsTest, GetFileNameWithoutExtension) { std::string name = GetFileNameWithoutExtension("main.cj"); EXPECT_EQ(name, "main"); name = GetFileNameWithoutExtension("testfile"); EXPECT_EQ(name, "testfile"); std::string case3 = "/hahaha/yeyeye/test1.cj"; name = GetFileNameWithoutExtension(case3); EXPECT_EQ(name, "test1"); name = GetFileNameWithoutExtension(".test2"); EXPECT_EQ(name, ".test2"); name = GetFileNameWithoutExtension("/some/path/.test3.cj"); EXPECT_EQ(name, ".test3"); } TEST(UtilsTest, GetFileBase) { std::string name = GetFileBase("../output/main.cj"); EXPECT_EQ(name, "../output/main"); name = GetFileBase("main"); EXPECT_EQ(name, "main"); name = GetFileBase("./main"); EXPECT_EQ(name, "./main"); name = GetFileBase("./path/main"); EXPECT_EQ(name, "./path/main"); name = GetFileBase("../.cjo"); EXPECT_EQ(name, "../.cjo"); name = GetFileBase(".cjo"); EXPECT_EQ(name, ".cjo"); name = GetFileBase("../.test.cjo"); EXPECT_EQ(name, "../.test"); name = GetFileBase("main.cj"); EXPECT_EQ(name, "main"); name = GetFileBase("/path/file.extension"); EXPECT_EQ(name, "/path/file"); name = GetFileBase("/path/.out"); EXPECT_EQ(name, "/path/.out"); } TEST(UtilsTest, IsIdentifierTest) { std::string s = "`te_s1t`"; EXPECT_TRUE(IsIdentifier(s)); s = "te_s1t"; EXPECT_TRUE(IsIdentifier(s)); s = "``"; EXPECT_FALSE(IsIdentifier(s)); s = "_test"; EXPECT_TRUE(IsIdentifier(s)); s = "1_test"; EXPECT_FALSE(IsIdentifier(s)); s = ""; EXPECT_FALSE(IsIdentifier(s)); s = "public"; EXPECT_TRUE(IsIdentifier(s)); EXPECT_FALSE(IsIdentifier(s, false)); s = "`true`"; EXPECT_TRUE(IsIdentifier(s, false)); s = "标识符"; EXPECT_TRUE(IsIdentifier(s, false)); s = "_标识符"; EXPECT_TRUE(IsIdentifier(s)); s = "__"; EXPECT_TRUE(IsIdentifier(s)); s = "_1"; EXPECT_TRUE(IsIdentifier(s)); } TEST(UtilsTest, TransferEscapeBacktickTest) { #ifdef _WIN32 EXPECT_EQ(TransferEscapeBacktick("`test`"), "`test`"); #else std::string s = "`test`"; EXPECT_EQ(TransferEscapeBacktick(s), "\\`test\\`"); std::string s2 = "t`test`a"; EXPECT_EQ(TransferEscapeBacktick(s2), "t\\`test\\`a"); #endif } TEST(UtilsTest, GetFileNameTest) { std::string name = GetFileName("main.cj"); EXPECT_EQ(name, "main.cj"); name = GetFileName("testfile"); EXPECT_EQ(name, "testfile"); std::string case3 = "/hahaha/yeyeye/test1.cj"; name = GetFileNameWithoutExtension(case3); EXPECT_EQ(name, "test1"); } TEST(UtilsTest, IsDirTest) { std::string utilsDir = GetProjectPath() + "/unittests/Utils/"; std::string noExistDir = "no/exist/dir"; EXPECT_TRUE(IsDir(utilsDir)); EXPECT_FALSE(IsDir(noExistDir)); #ifndef _WIN32 std::string command = "mkdir -p ./definitelyDirectory"; int err = system(command.c_str()); ASSERT_EQ(0, err); command = "chmod -r ./definitelyDirectory"; err = system(command.c_str()); ASSERT_EQ(0, err); EXPECT_TRUE(IsDir("./definitelyDirectory")); #endif } TEST(UtilsTest, JoinPathTest) { #ifdef _WIN32 // windows case. std::string moduleDir1 = "g:\\test\\package\\"; std::string moduleDir2 = "g:\\test\\package"; std::string srcPath = "src"; EXPECT_EQ("g:\\test\\package\\src", JoinPath(moduleDir1, srcPath)); EXPECT_EQ("g:\\test\\package\\src", JoinPath(moduleDir2, srcPath)); #else std::string basePath1 = "a/b/"; std::string basePath2 = "a/b"; std::string basePath3 = "a/b/\\t"; std::string appendPath = "c"; EXPECT_EQ("a/b/c", JoinPath(basePath1, appendPath)); EXPECT_EQ("a/b/c", JoinPath(basePath2, appendPath)); EXPECT_EQ("a/b/\\t/c", JoinPath(basePath3, appendPath)); #endif } TEST(UtilsTest, GetRelativePath) { std::string basePathOne = "/base/dir1"; std::string pathOne = "/base/dir2"; std::string basePathTwo = "/base/dir1/lib"; std::string pathTwo = "/base/dir2"; std::string basePathThree = "/base/dir1/lib"; std::string pathThree = "/base/dir2/lib"; #ifdef _WIN32 EXPECT_EQ(GetRelativePath(basePathOne, pathOne).value(), ".\\dir2"); EXPECT_EQ(GetRelativePath(basePathTwo, pathTwo).value(), "..\\dir2"); EXPECT_EQ(GetRelativePath(basePathThree, pathThree).value(), "..\\dir2\\lib"); EXPECT_EQ(GetRelativePath("/base", "/base").value(), ".\\"); EXPECT_EQ(GetRelativePath("/base/a", "/base/a").value(), ".\\"); EXPECT_EQ(GetRelativePath("/base/a", "/base/a/b/c/d").value(), ".\\a\\b\\c\\d"); EXPECT_EQ(GetRelativePath("/base/a", "/base/a/b/c/d/").value(), ".\\a\\b\\c\\d\\"); EXPECT_EQ(GetRelativePath("/base/a/", "/base/a/b/c/d").value(), ".\\b\\c\\d"); EXPECT_EQ(GetRelativePath("/base/a/", "/base/a/b/c/d/").value(), ".\\b\\c\\d\\"); EXPECT_EQ(GetRelativePath("/base/a/b/c/d", "/base/a").value(), "..\\..\\"); EXPECT_EQ(GetRelativePath("/base/a/b/c/d/", "/base/a").value(), "..\\..\\..\\"); EXPECT_EQ(GetRelativePath("/base/a/b/c/d", "/base/a/").value(), "..\\..\\"); EXPECT_EQ(GetRelativePath("/base/a/b/c/d/", "/base/a/").value(), "..\\..\\..\\"); EXPECT_EQ(GetRelativePath("\\base", "\\base").value(), ".\\"); EXPECT_EQ(GetRelativePath("\\base\\a", "\\base\\a").value(), ".\\"); EXPECT_EQ(GetRelativePath("\\base\\a", "\\base\\a\\b\\c\\d").value(), ".\\a\\b\\c\\d"); EXPECT_EQ(GetRelativePath("\\base\\a", "\\base\\a\\b\\c\\d\\").value(), ".\\a\\b\\c\\d\\"); EXPECT_EQ(GetRelativePath("\\base\\a\\", "\\base\\a\\b\\c\\d").value(), ".\\b\\c\\d"); EXPECT_EQ(GetRelativePath("\\base\\a\\", "\\base\\a\\b\\c\\d\\").value(), ".\\b\\c\\d\\"); EXPECT_EQ(GetRelativePath("\\base\\a\\b\\c\\d", "\\base\\a").value(), "..\\..\\"); EXPECT_EQ(GetRelativePath("\\base\\a\\b\\c\\d\\", "\\base\\a").value(), "..\\..\\..\\"); EXPECT_EQ(GetRelativePath("\\base\\a\\b\\c\\d", "\\base\\a\\").value(), "..\\..\\"); EXPECT_EQ(GetRelativePath("\\base\\a\\b\\c\\d\\", "\\base\\a\\").value(), "..\\..\\..\\"); #else EXPECT_EQ(GetRelativePath(basePathOne, pathOne).value(), "./dir2"); EXPECT_EQ(GetRelativePath(basePathTwo, pathTwo).value(), "../dir2"); EXPECT_EQ(GetRelativePath(basePathThree, pathThree).value(), "../dir2/lib"); EXPECT_EQ(GetRelativePath("/base", "/base").value(), "./"); EXPECT_EQ(GetRelativePath("/base/a", "/base/a").value(), "./"); EXPECT_EQ(GetRelativePath("/base/a", "/base/a/b/c/d").value(), "./a/b/c/d"); EXPECT_EQ(GetRelativePath("/base/a", "/base/a/b/c/d/").value(), "./a/b/c/d/"); EXPECT_EQ(GetRelativePath("/base/a/", "/base/a/b/c/d").value(), "./b/c/d"); EXPECT_EQ(GetRelativePath("/base/a/", "/base/a/b/c/d/").value(), "./b/c/d/"); EXPECT_EQ(GetRelativePath("/base/a/b/c/d", "/base/a").value(), "../../"); EXPECT_EQ(GetRelativePath("/base/a/b/c/d/", "/base/a").value(), "../../../"); EXPECT_EQ(GetRelativePath("/base/a/b/c/d", "/base/a/").value(), "../../"); EXPECT_EQ(GetRelativePath("/base/a/b/c/d/", "/base/a/").value(), "../../../"); #endif EXPECT_FALSE(GetRelativePath("", "").has_value()); EXPECT_FALSE(GetRelativePath("", "/base/a/b/c/d/").has_value()); EXPECT_FALSE(GetRelativePath("/base/a/b/c/d/", "").has_value()); } TEST(UtilsTest, SplitStrTest) { std::vector res = {"a", "ab", "abc"}; std::string case1 = "a\nab\nabc\n"; std::vector case1Ret = SplitStr(case1, '\n'); EXPECT_EQ(res.size(), case1Ret.size()); for (size_t i = 0; i < res.size(); ++i) { EXPECT_EQ(res[i], case1Ret[i]); } std::vector resTwo = {"a", "ab", "abc"}; std::string caseTwo = "a;ab;abc"; std::vector caseTwoRet = SplitStr(caseTwo, ';'); EXPECT_EQ(resTwo.size(), caseTwoRet.size()); for (size_t i = 0; i < resTwo.size(); ++i) { EXPECT_EQ(resTwo[i], caseTwoRet[i]); } std::vector resThree = {"a", "ab", "abc"}; std::string caseThree = " a ab abc "; std::vector caseThreeRet = SplitStr(caseThree, ' '); EXPECT_EQ(resThree.size(), caseThreeRet.size()); for (size_t i = 0; i < resThree.size(); ++i) { EXPECT_EQ(resThree[i], caseThreeRet[i]); } } TEST(UtilsTest, ReadUtf8WithSignature) { std::string filePath = GetProjectPath() + "/unittests/Utils/CangjieFiles/testpkg01.cj"; std::string failedReason; auto content = ReadFileContent(filePath, failedReason); EXPECT_TRUE(content.has_value() && failedReason.empty()); EXPECT_EQ(content->at(0), '/'); } TEST(UtilsTest, ReadUtf8) { std::string filePath = GetProjectPath() + "/unittests/Utils/CangjieFiles/pkg01.cj"; std::string failedReason; auto content = ReadFileContent(filePath, failedReason); EXPECT_TRUE(content.has_value() && failedReason.empty()); EXPECT_EQ(content->at(0), '/'); } TEST(UtilsTest, ReadEmptyFile) { std::string filePath = GetProjectPath() + "/unittests/Utils/CangjieFiles/emptyfile.cj"; std::string failedReason; auto content = ReadFileContent(filePath, failedReason); EXPECT_TRUE(content.has_value()); } TEST(UtilsTest, GCCVersionCompare) { // Different major. struct GCCVersion a = {7, 5, 5}; // Indicate version 7.5.5 struct GCCVersion b = {6, 5, 5}; // Indicate version 6.5.5 EXPECT_FALSE(a < b); b.major = a.major + 1; EXPECT_TRUE(a < b); // Different minor. b.major = a.major; b.minor = a.minor - 1; EXPECT_FALSE(a < b); b.minor = a.minor + 1; EXPECT_TRUE(a < b); // Different build. b.minor = a.minor; b.build = a.build - 1; EXPECT_FALSE(a < b); b.build = a.build + 1; EXPECT_TRUE(a < b); } TEST(UtilsTest, CheckCommandLineInjection) { EXPECT_TRUE(CheckCommandLineInjection("11|")); EXPECT_TRUE(CheckCommandLineInjection(";22")); EXPECT_TRUE(CheckCommandLineInjection("3&3")); #ifndef _WIN32 EXPECT_TRUE(CheckCommandLineInjection("44$")); EXPECT_TRUE(CheckCommandLineInjection(">55")); EXPECT_TRUE(CheckCommandLineInjection("6<6")); EXPECT_TRUE(CheckCommandLineInjection("77\\")); EXPECT_TRUE(CheckCommandLineInjection("!88")); EXPECT_TRUE(CheckCommandLineInjection("9\n9")); #endif EXPECT_FALSE(CheckCommandLineInjection("1234567")); EXPECT_FALSE(CheckCommandLineInjection("abcdefg")); EXPECT_FALSE(CheckCommandLineInjection("abc\t")); // Allow ` appears in cmd in pairs. EXPECT_TRUE(CheckCommandLineInjection("`")); EXPECT_TRUE(CheckCommandLineInjection("`a")); EXPECT_FALSE(CheckCommandLineInjection("`a`")); EXPECT_TRUE(CheckCommandLineInjection("`a`b`")); EXPECT_FALSE(CheckCommandLineInjection("a`b`c")); } TEST(UtilsTest, IsAbsolutePath) { EXPECT_FALSE(IsAbsolutePath("")); EXPECT_FALSE(IsAbsolutePath("abc")); #ifdef _WIN32 EXPECT_FALSE(IsAbsolutePath("/")); EXPECT_FALSE(IsAbsolutePath("/abc")); EXPECT_TRUE(IsAbsolutePath("C:\\")); EXPECT_TRUE(IsAbsolutePath("D:\\abc")); #else EXPECT_TRUE(IsAbsolutePath("/")); EXPECT_TRUE(IsAbsolutePath("/abc")); #endif } TEST(UtilsTest, ReadBinaryFileToBuffer) { std::string filePath = "./no_such_file.txt"; std::string failedReason; std::vector buffer; bool ret = ReadBinaryFileToBuffer(filePath, buffer, failedReason); EXPECT_FALSE(ret); EXPECT_EQ("open file failed", failedReason); #ifdef _WIN32 filePath = "C:/Windows/System32/cmd.exe"; #else filePath = "/bin/bash"; #endif ret = ReadBinaryFileToBuffer(filePath, buffer, failedReason); EXPECT_TRUE(ret); EXPECT_NE("empty binary file", failedReason); filePath = "./4GB+.bin"; std::ofstream os(filePath, std::ios::binary | std::ios::out); EXPECT_TRUE(os); os.seekp(4ULL * 1024 * 1024 * 1024 + 1); os.write("", 1); os.close(); ret = ReadBinaryFileToBuffer(filePath, buffer, failedReason); EXPECT_FALSE(ret); EXPECT_EQ("exceed the max file length: 4 GB", failedReason); std::remove(filePath.c_str()); } TEST(UtilsTest, ReadFileContent) { std::string filePath = "./no_such_file.txt"; std::string failedReason = ""; auto content = ReadFileContent(filePath, failedReason); EXPECT_FALSE(content.has_value() && failedReason.empty()); } TEST(UtilsTest, AppendExtension) { std::string result1 = AppendExtension("", "backup", ".so"); EXPECT_EQ("backup.so", result1); std::string result2 = AppendExtension("main.exe", "backup", ".so"); EXPECT_EQ("main.so", result2); } TEST(UtilsTest, GetAbsPath) { std::string filePath = "a"; for (auto i = 0; i < PATH_MAX; i++) { filePath.append("b"); } auto absPath = GetAbsPath(filePath); EXPECT_FALSE(absPath.has_value()); } TEST(UtilsTest, Float32ToFloat16) { // inf: 0111110000000000 EXPECT_EQ(0b0111110000000000, Float32ToFloat16(6.5536e4f)); // max normal: 0111101111111111 (1.1111111111 × 2^15) EXPECT_EQ(0b0111101111111111, Float32ToFloat16(6.5504e4f)); // normal: 0011000100000000 ((1 + 2^-2) × 2^-3) EXPECT_EQ(0b0011000100000000, Float32ToFloat16(0.15625f)); // min normal: 0000010000000000 (2^-14) EXPECT_EQ(0b0000010000000000, Float32ToFloat16(6.104e-5f)); // max subnormal: 0000001111111111 (0.1111111111) EXPECT_EQ(0b0000001111111111, Float32ToFloat16(6.102e-5f)); // min subnormal: 0000000000000001 (2^-10 × 2^-14) EXPECT_EQ(0b0000000000000001, Float32ToFloat16(5.9605e-8f)); // zero: 0000000000000000 EXPECT_EQ(0b0000000000000000, Float32ToFloat16(5.9604e-8f)); } TEST(UtilsTest, GetSizeDecl) { InvalidTy ty = InvalidTy(); EXPECT_EQ(nullptr, GetSizeDecl(ty)); } TEST(UtilsTest, FindFileByName) { std::vector paths; auto res = FindFileByName("", paths); EXPECT_FALSE(res.has_value()); res = FindFileByName("hello", paths); EXPECT_FALSE(res.has_value()); paths.push_back("/bin/"); paths.push_back("/etc/"); res = FindFileByName("non-exist-file", paths); EXPECT_FALSE(res.has_value()); #ifndef _WIN32 res = FindFileByName("bash", paths); EXPECT_EQ(res.value(), "/bin/bash"); #endif } TEST(UtilsTest, NormalizePath) { #ifdef _WIN32 EXPECT_EQ(NormalizePath("main"), ".\\main"); EXPECT_EQ(NormalizePath("./main"), ".\\main"); EXPECT_EQ(NormalizePath("/path/to/file"), "\\path\\to\\file"); #else EXPECT_EQ(NormalizePath("main"), "./main"); EXPECT_EQ(NormalizePath("./main"), "./main"); EXPECT_EQ(NormalizePath("/path/to/file"), "/path/to/file"); #endif } TEST(UtilsTest, GetAllDirsUnderCurrentPath) { #ifndef _WIN32 std::string command = "rm -rf fileUtilsMkdir"; auto err = system(command.c_str()); ASSERT_EQ(0, err); command = "mkdir -p fileUtilsMkdir/tmp1"; err = system(command.c_str()); ASSERT_EQ(0, err); std::vector dirs = GetAllDirsUnderCurrentPath("fileUtilsMkdir"); EXPECT_EQ(dirs.size(), 1); EXPECT_EQ(dirs[0], "fileUtilsMkdir/tmp1"); command = "mkdir fileUtilsMkdir/tmp2"; err = system(command.c_str()); ASSERT_EQ(0, err); dirs = GetAllDirsUnderCurrentPath("fileUtilsMkdir"); EXPECT_EQ(dirs.size(), 2); EXPECT_EQ(std::count(dirs.begin(), dirs.end(), "fileUtilsMkdir/tmp2"), 1); auto absPath = GetAbsPath("fileUtilsMkdir"); ASSERT_TRUE(absPath.has_value()); dirs = GetAllDirsUnderCurrentPath(absPath.value()); EXPECT_EQ(dirs.size(), 2); #endif } TEST(UtilsTest, GetPkgNameFromRelativePath) { // Run both linux and windows. EXPECT_EQ(GetPkgNameFromRelativePath("./a"), "a"); EXPECT_EQ(GetPkgNameFromRelativePath("./a/b"), "a.b"); EXPECT_EQ(GetPkgNameFromRelativePath("a/b"), "a.b"); EXPECT_EQ(GetPkgNameFromRelativePath("a/./b"), "a.b"); EXPECT_EQ(GetPkgNameFromRelativePath("a/b/."), "a.b"); EXPECT_EQ(GetPkgNameFromRelativePath(""), "default"); EXPECT_EQ(GetPkgNameFromRelativePath("./"), "default"); EXPECT_EQ(GetPkgNameFromRelativePath("../"), "default"); EXPECT_EQ(GetPkgNameFromRelativePath("xx/../"), "default"); #ifdef _WIN32 EXPECT_EQ(GetPkgNameFromRelativePath(".\\a"), "a"); EXPECT_EQ(GetPkgNameFromRelativePath(".\\a\\b"), "a.b"); EXPECT_EQ(GetPkgNameFromRelativePath("a\\b"), "a.b"); EXPECT_EQ(GetPkgNameFromRelativePath("a\\.\\b"), "a.b"); EXPECT_EQ(GetPkgNameFromRelativePath("a\\b\\."), "a.b"); EXPECT_EQ(GetPkgNameFromRelativePath(""), "default"); EXPECT_EQ(GetPkgNameFromRelativePath(".\\"), "default"); EXPECT_EQ(GetPkgNameFromRelativePath("..\\"), "default"); EXPECT_EQ(GetPkgNameFromRelativePath("xx\\..\\"), "default"); #endif } TEST(UtilsTest, IsAbsolutePathAboveLengthLimit) { #ifdef _WIN32 // 27 characters in the path EXPECT_FALSE(IsAbsolutePathAboveLengthLimit("C:/Windows/System32/cmd.exe")); // 265 characters in the path EXPECT_TRUE(IsAbsolutePathAboveLengthLimit( "./" "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" "1234567890123456789012345678901234567890.cj")); #else EXPECT_FALSE(IsAbsolutePathAboveLengthLimit("/bin/bash")); #endif } TEST(UtilsTest, SipHash) { uint64_t hashVal = Utils::SipHash::GetHashValue(std::string("123abc")); EXPECT_EQ(hashVal, 0x4F03C64A98DDE71E); hashVal = Utils::SipHash::GetHashValue(uint8_t(12)); EXPECT_EQ(hashVal, 0xD854D2BDE319255); hashVal = Utils::SipHash::GetHashValue(int8_t(-23)); EXPECT_EQ(hashVal, 0xAEC08D713104780D); hashVal = Utils::SipHash::GetHashValue(uint16_t(120)); EXPECT_EQ(hashVal, 0xBF50EFAA5E3EC47F); hashVal = Utils::SipHash::GetHashValue(int16_t(-230)); EXPECT_EQ(hashVal, 0xF59AAFE3408C3246); hashVal = Utils::SipHash::GetHashValue(uint32_t(4500)); EXPECT_EQ(hashVal, 0xDE95412C896457D8); hashVal = Utils::SipHash::GetHashValue(int32_t(-5600)); EXPECT_EQ(hashVal, 0x92071CC5CB5A1C0B); hashVal = Utils::SipHash::GetHashValue(uint64_t(7800000)); EXPECT_EQ(hashVal, 0xDA9374B66FE40DAF); hashVal = Utils::SipHash::GetHashValue(int64_t(-89456113)); EXPECT_EQ(hashVal, 0x456D9FCA9BDD4D37); hashVal = Utils::SipHash::GetHashValue(double(-89456113.123789)); EXPECT_EQ(hashVal, 0xB72124ADB2EABF59); hashVal = Utils::SipHash::GetHashValue(float(789456.222666)); EXPECT_EQ(hashVal, 0xC413B71DA4335E1B); hashVal = Utils::SipHash::GetHashValue(std::bitset<64>{789}); EXPECT_EQ(hashVal, 0xD43173C1E336768B); hashVal = Utils::SipHash::GetHashValue(std::bitset<32>{789}); EXPECT_EQ(hashVal, 0xD43173C1E336768B); hashVal = Utils::SipHash::GetHashValue(std::bitset<16>{789}); EXPECT_EQ(hashVal, 0xD43173C1E336768B); hashVal = Utils::SipHash::GetHashValue(std::bitset<5>{9}); EXPECT_EQ(hashVal, 0x4F5CD87E0CD8CAAC); hashVal = Utils::SipHash::GetHashValue(double(-89456113.123789)); EXPECT_EQ(hashVal, 0xB72124ADB2EABF59); hashVal = Utils::SipHash::GetHashValue(double(-89456113.123789)); EXPECT_EQ(hashVal, 0xB72124ADB2EABF59); hashVal = Utils::SipHash::GetHashValue(std::string("123abc")); EXPECT_EQ(hashVal, 0x4F03C64A98DDE71E); } TEST(UtilsTest, SipHash2) { EXPECT_NE(Utils::SipHash::GetHashValue("define_40020"), Utils::SipHash::GetHashValue("Vefine_40020")); EXPECT_NE(Utils::SipHash::GetHashValue("define_macro"), Utils::SipHash::GetHashValue("Define_macro")); EXPECT_NE(Utils::SipHash::GetHashValue("Define_30031"), Utils::SipHash::GetHashValue("define_30031")); EXPECT_NE(Utils::SipHash::GetHashValue("define_20033"), Utils::SipHash::GetHashValue("lefine_20033")); EXPECT_NE(Utils::SipHash::GetHashValue("define_20056a"), Utils::SipHash::GetHashValue("tefine_20056a")); EXPECT_NE(Utils::SipHash::GetHashValue("define_4002"), Utils::SipHash::GetHashValue("Vefine_4002")); EXPECT_NE(Utils::SipHash::GetHashValue("define_macr"), Utils::SipHash::GetHashValue("Define_macr")); EXPECT_NE(Utils::SipHash::GetHashValue("Define_3003"), Utils::SipHash::GetHashValue("define_3003")); EXPECT_NE(Utils::SipHash::GetHashValue("define_2003"), Utils::SipHash::GetHashValue("lefine_2003")); EXPECT_NE(Utils::SipHash::GetHashValue("define20056a"), Utils::SipHash::GetHashValue("tefine20056a")); EXPECT_NE(Utils::SipHash::GetHashValue("define_400"), Utils::SipHash::GetHashValue("Vefine_400")); EXPECT_NE(Utils::SipHash::GetHashValue("define_mac"), Utils::SipHash::GetHashValue("Define_mac")); EXPECT_NE(Utils::SipHash::GetHashValue("Define_300"), Utils::SipHash::GetHashValue("define_300")); EXPECT_NE(Utils::SipHash::GetHashValue("define_200"), Utils::SipHash::GetHashValue("lefine_200")); EXPECT_NE(Utils::SipHash::GetHashValue("define_200"), Utils::SipHash::GetHashValue("tefine_200")); EXPECT_NE(Utils::SipHash::GetHashValue("define_40"), Utils::SipHash::GetHashValue("Vefine_40")); EXPECT_NE(Utils::SipHash::GetHashValue("define_ma"), Utils::SipHash::GetHashValue("Define_ma")); EXPECT_NE(Utils::SipHash::GetHashValue("Define_30"), Utils::SipHash::GetHashValue("define_30")); EXPECT_NE(Utils::SipHash::GetHashValue("define_20"), Utils::SipHash::GetHashValue("lefine_20")); EXPECT_NE(Utils::SipHash::GetHashValue("define_20"), Utils::SipHash::GetHashValue("tefine_20")); EXPECT_NE(Utils::SipHash::GetHashValue("define_20"), Utils::SipHash::GetHashValue("degine_20")); EXPECT_NE(Utils::SipHash::GetHashValue("define_20"), Utils::SipHash::GetHashValue("dEfine_20")); EXPECT_NE(Utils::SipHash::GetHashValue("define_20"), Utils::SipHash::GetHashValue("definE_20")); EXPECT_NE(Utils::SipHash::GetHashValue("define_20"), Utils::SipHash::GetHashValue("define_21")); EXPECT_NE(Utils::SipHash::GetHashValue("define_20"), Utils::SipHash::GetHashValue("define'20")); EXPECT_NE(Utils::SipHash::GetHashValue("abcdefghi"), Utils::SipHash::GetHashValue("dbcdefghi")); EXPECT_NE(Utils::SipHash::GetHashValue("pleasecom"), Utils::SipHash::GetHashValue("jleasecom")); EXPECT_NE(Utils::SipHash::GetHashValue("tutututut"), Utils::SipHash::GetHashValue("dutututut")); EXPECT_NE(Utils::SipHash::GetHashValue("dontcome!"), Utils::SipHash::GetHashValue("Vontcome!")); EXPECT_NE(Utils::SipHash::GetHashValue("deception"), Utils::SipHash::GetHashValue("teception")); EXPECT_NE(Utils::SipHash::GetHashValue("1abcdefghi"), Utils::SipHash::GetHashValue("1dbcdefghi")); EXPECT_NE(Utils::SipHash::GetHashValue("dpleasecom"), Utils::SipHash::GetHashValue("djleasecom")); EXPECT_NE(Utils::SipHash::GetHashValue("ktutututut"), Utils::SipHash::GetHashValue("kdutututut")); EXPECT_NE(Utils::SipHash::GetHashValue("kdontcome!"), Utils::SipHash::GetHashValue("kVontcome!")); EXPECT_NE(Utils::SipHash::GetHashValue("kdeception"), Utils::SipHash::GetHashValue("kteception")); EXPECT_NE(Utils::SipHash::GetHashValue("define_4"), Utils::SipHash::GetHashValue("Vefine_4")); EXPECT_NE(Utils::SipHash::GetHashValue("define_m"), Utils::SipHash::GetHashValue("Define_m")); EXPECT_NE(Utils::SipHash::GetHashValue("Define_3"), Utils::SipHash::GetHashValue("define_3")); EXPECT_NE(Utils::SipHash::GetHashValue("define_2"), Utils::SipHash::GetHashValue("lefine_2")); EXPECT_NE(Utils::SipHash::GetHashValue("define_2"), Utils::SipHash::GetHashValue("tefine_2")); EXPECT_NE(Utils::SipHash::GetHashValue("a"), Utils::SipHash::GetHashValue("d")); EXPECT_NE(Utils::SipHash::GetHashValue("t"), Utils::SipHash::GetHashValue("d")); EXPECT_NE(Utils::SipHash::GetHashValue("D"), Utils::SipHash::GetHashValue("d")); EXPECT_NE(Utils::SipHash::GetHashValue("amamamam"), Utils::SipHash::GetHashValue("dmamamam")); EXPECT_NE(Utils::SipHash::GetHashValue("amamamam"), Utils::SipHash::GetHashValue("amdmamam")); EXPECT_NE(Utils::SipHash::GetHashValue("amamamam"), Utils::SipHash::GetHashValue("amamdmam")); EXPECT_NE(Utils::SipHash::GetHashValue("amamamam"), Utils::SipHash::GetHashValue("amamamdm")); EXPECT_NE(Utils::SipHash::GetHashValue("amamamama"), Utils::SipHash::GetHashValue("amamamamd")); EXPECT_NE(Utils::SipHash::GetHashValue("bambambomba"), Utils::SipHash::GetHashValue("nambambomba")); EXPECT_NE(Utils::SipHash::GetHashValue("bambambombak"), Utils::SipHash::GetHashValue("nambambombak")); EXPECT_NE(Utils::SipHash::GetHashValue("bambambombalm"), Utils::SipHash::GetHashValue("nambambombalm")); EXPECT_NE(Utils::SipHash::GetHashValue("bambambombaese"), Utils::SipHash::GetHashValue("nambambombaese")); EXPECT_NE(Utils::SipHash::GetHashValue("bambambombadodo"), Utils::SipHash::GetHashValue("nambambombadodo")); } TEST(UtilsTest, IsUnderFlowFloat) { auto underUse = [](const std::string& value) { auto stringValue = value; stringValue.erase(std::remove(stringValue.begin(), stringValue.end(), '_'), stringValue.end()); return IsUnderFlowFloat(stringValue); }; EXPECT_EQ(underUse("1_e10000000000000000000000000000000"), false); EXPECT_EQ(underUse("1e10000000000000000000000000000000"), false); EXPECT_EQ(underUse("-1_e10000000000000000000000000000000"), false); EXPECT_EQ(underUse("-1E10000"), false); EXPECT_EQ(underUse("1E10000"), false); EXPECT_EQ(underUse("-1E-10000"), true); EXPECT_EQ(underUse("1E-10000"), true); EXPECT_EQ(underUse("0_." "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" "0000000000000000000000000000000000000000000001"), true); EXPECT_EQ(underUse("." "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" "0000000000000000000000000000000000000000000001"), true); EXPECT_EQ(underUse(".123e-10000"), true); EXPECT_EQ(underUse("0x1p-100000000000000000000000000000000000000000"), true); // By default, this function is invoked only when it is beyond the C++ representation range. // This should not occur in actual calls. EXPECT_EQ(underUse("1.0"), false); } cangjie_compiler-1.0.7/utils/000077500000000000000000000000001510705540100161475ustar00rootroot00000000000000cangjie_compiler-1.0.7/utils/CMakeLists.txt000066400000000000000000000035041510705540100207110ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # copy envsetup file to output folder if(WIN32) set(SCRIPT_SUFFIX bat ps1 sh) elseif(UNIX) set(SCRIPT_SUFFIX sh) endif() set(ENV_FILE) set(RUNTIME_ENV_FILE) if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) string(TOLOWER "llvm_${CMAKE_SYSTEM_NAME}" HOST_PLATFORM) if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") string(TOLOWER "_${CMAKE_SYSTEM_PROCESSOR}" HARDWARE_ARCH) string(APPEND HOST_PLATFORM ${HARDWARE_ARCH}) endif() foreach(suffix ${SCRIPT_SUFFIX}) list(APPEND ENV_FILE ${CMAKE_SOURCE_DIR}/utils/envsetup/${HOST_PLATFORM}.${suffix}) list(APPEND RUNTIME_ENV_FILE ${CMAKE_SOURCE_DIR}/utils/envsetup/runtime/${HOST_PLATFORM}.${suffix}) endforeach() endif() foreach(filename ${ENV_FILE}) if(EXISTS ${filename}) get_filename_component(suffix ${filename} LAST_EXT) install( FILES ${filename} DESTINATION bin/../ RENAME envsetup${suffix}) endif() endforeach() foreach(filename ${RUNTIME_ENV_FILE}) if(EXISTS ${filename}) get_filename_component(suffix ${filename} LAST_EXT) install( FILES ${filename} DESTINATION runtime/ RENAME envsetup${suffix}) endif() endforeach() if(DARWIN) install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/entitlement.plist DESTINATION lib ) endif() if(CANGJIE_ENABLE_COMPILER_TSAN) add_compile_options("${TSAN_FLAGS}") add_link_options("${TSAN_FLAGS}") endif() # regenerate unicode data tables on need if(CANGJIE_GENERATE_UNICODE_TABLE) add_subdirectory(UnicodeScripts) endif() cangjie_compiler-1.0.7/utils/entitlement.plist000066400000000000000000000007711510705540100215610ustar00rootroot00000000000000 com.apple.security.get-task-allow cangjie_compiler-1.0.7/utils/envsetup/000077500000000000000000000000001510705540100200205ustar00rootroot00000000000000cangjie_compiler-1.0.7/utils/envsetup/llvm_darwin.sh000077500000000000000000000035771510705540100227110ustar00rootroot00000000000000#!/bin/bash # Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # This script needs to be placed in the output directory of Cangjie compiler. # ** NOTE: Please use `source' command to execute this script. ** # Get current shell name. shell_name=$(ps -o comm= $$) case "${shell_name}" in "-"*) ;; *) shell_name=$(basename $shell_name) ;; esac # Get the absolute path of this script according to different shells. case "${shell_name}" in "zsh" | "-zsh") source_dir="${(%):-%N}" ;; "sh" | "-sh" | "bash" | "-bash") source_dir="${BASH_SOURCE[0]}" ;; *) echo "[ERROR] Unsupported shell: ${shell_name}, please switch to bash, sh or zsh." return 1 ;; esac if [ -L ${source_dir} ]; then if command -v realpath 2>&1 >/dev/null; then source_dir=$(realpath "${source_dir}") else echo '`realpath` is not found, setup may not process properly.' fi fi script_dir=$(cd "$(dirname "${source_dir}")"; pwd) export CANGJIE_HOME=${script_dir} hw_arch=$(uname -m) if [ "$hw_arch" = "" ]; then hw_arch="x86_64" elif [ "$hw_arch" = "arm64" ]; then hw_arch="aarch64" fi export PATH=${CANGJIE_HOME}/bin:${CANGJIE_HOME}/tools/bin:$PATH:${HOME}/.cjpm/bin export DYLD_LIBRARY_PATH=${CANGJIE_HOME}/runtime/lib/darwin_${hw_arch}_cjnative:${CANGJIE_HOME}/tools/lib:${DYLD_LIBRARY_PATH} unset hw_arch if [ -z ${SDKROOT+x} ]; then export SDKROOT=`xcrun --sdk macosx --show-sdk-path` fi xattr -dr com.apple.quarantine ${script_dir}/* &> /dev/null || true codesign -s - -f --preserve-metadata=entitlements,requirements,flags,runtime ${script_dir}/third_party/llvm/bin/debugserver &> /dev/null || true cangjie_compiler-1.0.7/utils/envsetup/llvm_linux.sh000077500000000000000000000031711510705540100225520ustar00rootroot00000000000000#!/bin/bash # Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # This script needs to be placed in the output directory of Cangjie compiler. # ** NOTE: Please use `source' command to execute this script. ** # Get current shell name. shell_path=$(readlink -f /proc/$$/exe) shell_name=${shell_path##*/} # Get the absolute path of this script according to different shells. case "${shell_name}" in "zsh") # check whether compinit has been executed if (( ${+_comps} )); then # if compinit already executed, delete completion functions of cjc, cjc-frontend firstly compdef -d cjc cjc-frontend else autoload -Uz compinit compinit fi # auto complete cjc, cjc-frontend compdef _gnu_generic cjc cjc-frontend script_dir=$(cd "$(dirname "$(readlink -f "${(%):-%N}")")"; pwd) ;; "sh" | "bash") script_dir=$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"; pwd) ;; *) echo "[ERROR] Unsupported shell: ${shell_name}, please switch to bash, sh or zsh." return 1 ;; esac export CANGJIE_HOME=${script_dir} hw_arch=$(arch) if [ "$hw_arch" = "" ]; then hw_arch="x86_64" fi export PATH=${CANGJIE_HOME}/bin:${CANGJIE_HOME}/tools/bin:$PATH:${HOME}/.cjpm/bin export LD_LIBRARY_PATH=${CANGJIE_HOME}/runtime/lib/linux_${hw_arch}_cjnative:${CANGJIE_HOME}/tools/lib:${LD_LIBRARY_PATH} unset hw_arch cangjie_compiler-1.0.7/utils/envsetup/llvm_windows_x86_64.bat000066400000000000000000000012601510705540100242510ustar00rootroot00000000000000@REM Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. @REM This source file is part of the Cangjie project, licensed under Apache-2.0 @REM with Runtime Library Exception. @REM @REM See https://cangjie-lang.cn/pages/LICENSE for license information. @REM This script needs to be placed in the root directory of installation of Cangjie compiler and libraries. @echo off REM Set CANGJIE_HOME to the path of this batch script. set "CANGJIE_HOME=%~dp0" REM Windows searches for both binaries and libs in %Path% set "PATH=%CANGJIE_HOME%runtime\lib\windows_x86_64_cjnative;%CANGJIE_HOME%bin;%CANGJIE_HOME%tools\bin;%CANGJIE_HOME%tools\lib;%PATH%;%USERPROFILE%\.cjpm\bin" cangjie_compiler-1.0.7/utils/envsetup/llvm_windows_x86_64.ps1000066400000000000000000000016521510705540100242130ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # This script needs to be placed in the root directory of installation of Cangjie compiler and libraries. # Set CANGJIE_HOME to the path of this batch script. $env:CANGJIE_HOME = Split-Path -Parent $MyInvocation.MyCommand.Definition # Windows searches for both binaries and libs in %Path% $env:Path = $env:CANGJIE_HOME + "\runtime\lib\windows_x86_64_cjnative;" + $env:Path $env:Path = $env:CANGJIE_HOME + "\lib\windows_x86_64_cjnative;" + $env:Path $env:Path = $env:CANGJIE_HOME + "\bin;" + $env:Path $env:Path = $env:CANGJIE_HOME + "\tools\bin;" + $env:Path $env:Path = $env:Path + ";" + $env:USERPROFILE + "\.cjpm\bin" $env:Path = $env:CANGJIE_HOME + "\tools\lib;" + $env:Path cangjie_compiler-1.0.7/utils/envsetup/llvm_windows_x86_64.sh000066400000000000000000000030111510705540100241110ustar00rootroot00000000000000#!/bin/bash # Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # This script needs to be placed in the output directory of Cangjie compiler. # ** NOTE: Please use `source' command to execute this script. ** # Get current shell name. shell_path=$(readlink -f /proc/$$/exe) shell_name=${shell_path##*/} # Get the absolute path of this script according to different shells. case "${shell_name}" in "zsh") # check whether compinit has been executed if (( ${+_comps} )); then # if compinit already executed, delete completion functions of cjc, cjc-frontend firstly compdef -d cjc cjc-frontend else autoload -Uz compinit compinit fi # auto complete cjc, cjc-frontend compdef _gnu_generic cjc cjc-frontend script_dir=$(cd "$(dirname "${(%):-%N}")"; pwd) ;; "sh" | "bash") script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd) ;; *) echo "[ERROR] Unsupported shell: ${shell_name}, please switch to bash, sh or zsh." return 1 ;; esac export CANGJIE_HOME=${script_dir} export PATH=${CANGJIE_HOME}/bin:${CANGJIE_HOME}/tools/bin:${CANGJIE_HOME}/tools/lib:${CANGJIE_HOME}/runtime/lib/windows_x86_64_cjnative:${CANGJIE_HOME}/third_party/llvm/lldb/lib:$PATH:${USERPROFILE}/.cjpm/bin cangjie_compiler-1.0.7/utils/envsetup/runtime/000077500000000000000000000000001510705540100215035ustar00rootroot00000000000000cangjie_compiler-1.0.7/utils/envsetup/runtime/llvm_linux_aarch64.sh000066400000000000000000000017351510705540100255460ustar00rootroot00000000000000#!/bin/bash # Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # This script needs to be placed in the output directory of Cangjie compiler. # ** NOTE: Please use `source' command to execute this script. ** # Get current shell name. shell_path=$(readlink -f /proc/$$/exe) shell_name=${shell_path##*/} # Get the absolute path of this script according to different shells. case "${shell_name}" in "zsh") script_dir=$(cd "$(dirname "${(%):-%N}")"; pwd) ;; "sh" | "bash") script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd) ;; *) echo "[ERROR] Unsupported shell: ${shell_name}, please switch to bash, sh or zsh." return 1 ;; esac export LD_LIBRARY_PATH=${script_dir}/lib/linux_aarch64_cjnative:${LD_LIBRARY_PATH} cangjie_compiler-1.0.7/utils/envsetup/runtime/llvm_linux_x86_64.sh000077500000000000000000000017341510705540100252560ustar00rootroot00000000000000#!/bin/bash # Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # This script needs to be placed in the output directory of Cangjie compiler. # ** NOTE: Please use `source' command to execute this script. ** # Get current shell name. shell_path=$(readlink -f /proc/$$/exe) shell_name=${shell_path##*/} # Get the absolute path of this script according to different shells. case "${shell_name}" in "zsh") script_dir=$(cd "$(dirname "${(%):-%N}")"; pwd) ;; "sh" | "bash") script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd) ;; *) echo "[ERROR] Unsupported shell: ${shell_name}, please switch to bash, sh or zsh." return 1 ;; esac export LD_LIBRARY_PATH=${script_dir}/lib/linux_x86_64_cjnative:${LD_LIBRARY_PATH} cangjie_compiler-1.0.7/utils/envsetup/runtime/llvm_windows_x86_64.bat000066400000000000000000000007631510705540100257430ustar00rootroot00000000000000@REM Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. @REM This source file is part of the Cangjie project, licensed under Apache-2.0 @REM with Runtime Library Exception. @REM @REM See https://cangjie-lang.cn/pages/LICENSE for license information. @REM This script needs to be placed in the root directory of installation of Cangjie compiler and libraries. @echo off REM Windows searches for both binaries and libs in %Path% set "PATH=%~dp0lib\windows_x86_64_cjnative;%PATH%"cangjie_compiler-1.0.7/utils/envsetup/runtime/llvm_windows_x86_64.ps1000066400000000000000000000010561510705540100256740ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # This script needs to be placed in the root directory of installation of Cangjie compiler and libraries. $script_dir = Split-Path -Parent $MyInvocation.MyCommand.Definition # Windows searches for both binaries and libs in %Path% $env:Path = $script_dir + "\lib\windows_x86_64_cjnative;" + $env:Path cangjie_compiler-1.0.7/utils/envsetup/runtime/llvm_windows_x86_64.sh000066400000000000000000000017071510705540100256060ustar00rootroot00000000000000#!/bin/bash # Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # This script needs to be placed in the output directory of Cangjie compiler. # ** NOTE: Please use `source' command to execute this script. ** # Get current shell name. shell_path=$(readlink -f /proc/$$/exe) shell_name=${shell_path##*/} # Get the absolute path of this script according to different shells. case "${shell_name}" in "zsh") script_dir=$(cd "$(dirname "${(%):-%N}")"; pwd) ;; "sh" | "bash") script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd) ;; *) echo "[ERROR] Unsupported shell: ${shell_name}, please switch to bash, sh or zsh." return 1 ;; esac export PATH=${script_dir}/lib/windows_x86_64_cjnative:${PATH}

(lam: autoEnv_generic, Unit>): Unit { * let a = autoEnv_foo() * lam.$GenericVirtualFunc(a) * } * func zoo(t:autoEnv_inst_Int64_Unit) { * t.$InstVirtualFunc(0) * } * main() { * let a = autoEnv_zoo() * segFault(a) * } */ auto pType = params[i]->GetType(); if (pType->IsCJFunc()) { CJC_ASSERT(expectedParamTypes[i - offset] == pType); auto typecast = builder.CreateExpression(pType, params[i], entry); entry->AppendExpression(typecast); applyArgs.emplace_back(typecast->GetResult()); } else { applyArgs.emplace_back( TypeCastOrBoxIfNeeded(*params[i], *expectedParamTypes[i - offset], builder, *entry, INVALID_LOCATION)); } } auto applyRetType = ReplaceRawGenericArgType(*srcFunc.GetFuncType()->GetReturnType(), originalTypeToNewType, builder); std::vector instTyArgs; for (auto ty : srcFunc.GetGenericTypeParams()) { instTyArgs.emplace_back(ReplaceRawGenericArgType(*ty, originalTypeToNewType, builder)); } Type* thisTy = srcFunc.GetParentCustomTypeOrExtendedType(); if (thisTy != nullptr) { thisTy = ReplaceRawGenericArgType(*thisTy, originalTypeToNewType, builder); } // this type equal to parent type auto callSrcFunc = CreateAndAppendExpression(builder, applyRetType, &srcFunc, FuncCallContext{ .args = applyArgs, .instTypeArgs = instTyArgs, .thisType = thisTy}, entry); auto applyRes = TypeCastOrBoxIfNeeded( *callSrcFunc->GetResult(), *newFuncRetType, builder, *entry, INVALID_LOCATION); // store return value and exit CreateAndAppendExpression( builder, builder.GetType(), applyRes, retVal->GetResult(), entry); CreateAndAppendTerminator(builder, entry); } void ClosureConversion::CreateInstOverrideMethodInAutoEnvImplDef(ClassDef& autoEnvImplDef, FuncBase& srcFunc, const std::unordered_map& originalTypeToNewType) { if (FuncTypeHasGenericT(srcFunc)) { return; } // create new func type auto newFuncParamTypes = srcFunc.GetFuncType()->GetParamTypes(); auto classRefTy = builder.GetType(autoEnvImplDef.GetType()); if (srcFunc.GetFuncKind() != FuncKind::LAMBDA) { newFuncParamTypes.insert(newFuncParamTypes.begin(), classRefTy); } auto newFuncRetType = srcFunc.GetFuncType()->GetReturnType(); auto newFuncTy = builder.GetType(newFuncParamTypes, newFuncRetType); // create override func auto mangledName = CHIRMangling::ClosureConversion::GenerateInstOverrideFuncMangleName(srcFunc); auto newFunc = builder.CreateFunc( INVALID_LOCATION, newFuncTy, mangledName, INST_VIRTUAL_FUNC, "", package.GetName()); autoEnvImplDef.AddMethod(newFunc); // set attribute SetMemberMethodAttr(*newFunc, srcFunc.TestAttr(Attribute::CONST)); // create func body auto blockGroup = builder.CreateBlockGroup(*newFunc); newFunc->InitBody(*blockGroup); auto entry = builder.CreateBlock(blockGroup); blockGroup->SetEntryBlock(entry); // create parameters for (auto paramTy : newFuncParamTypes) { builder.CreateParameter(paramTy, INVALID_LOCATION, *newFunc); } // create return value auto retRefTy = builder.GetType(newFuncRetType); auto retVal = CreateAndAppendExpression(builder, retRefTy, newFuncRetType, entry); newFunc->SetReturnValue(*retVal->GetResult()); // call src function auto& params = newFunc->GetParams(); long offset = srcFunc.GetFuncKind() == FuncKind::LAMBDA ? 0 : 1; std::vector applyArgs(params.begin() + offset, params.end()); std::vector instTyArgs; for (auto ty : srcFunc.GetGenericTypeParams()) { instTyArgs.emplace_back(ReplaceRawGenericArgType(*ty, originalTypeToNewType, builder)); } Type* thisTy = srcFunc.GetParentCustomTypeOrExtendedType(); if (thisTy != nullptr) { thisTy = ReplaceRawGenericArgType(*thisTy, originalTypeToNewType, builder); } auto callSrcFunc = CreateAndAppendExpression(builder, newFuncRetType, &srcFunc, FuncCallContext{ .args = applyArgs, .instTypeArgs = instTyArgs, .thisType = thisTy}, entry); // store return value and exit CreateAndAppendExpression( builder, builder.GetType(), callSrcFunc->GetResult(), retVal->GetResult(), entry); CreateAndAppendTerminator(builder, entry); } void ClosureConversion::CreateMemberVarInAutoEnvImplDef(ClassDef& parentClass, const std::vector& boxedEnvs, const std::unordered_map& originalTypeToNewType) { AttributeInfo attributeInfo; attributeInfo.SetAttr(Attribute::PUBLIC, true); for (size_t i = 0; i < boxedEnvs.size(); ++i) { // 1. get member var's location DebugLocation loc; if (auto localVar = DynamicCast(boxedEnvs[i])) { loc = localVar->GetExpr()->GetDebugLocation(); } else if (auto param = DynamicCast(boxedEnvs[i]); param && param->GetDebugExpr()) { // param may be copied in function inline, then it doesn't have Debug loc = param->GetDebugExpr()->GetDebugLocation(); } // 2. get member var's type auto memberTy = boxedEnvs[i]->GetType(); if (IsMutableVarType(*boxedEnvs[i])) { memberTy = StaticCast(boxedEnvs[i]->GetType())->GetBaseType(); } memberTy = ReplaceRawGenericArgType(*memberTy, originalTypeToNewType, builder); // 3. get member var's name auto memberName = GenerateSrcCodeIdentifier(*boxedEnvs[i]); // 4. add member var parentClass.AddInstanceVar(MemberVarInfo{std::move(memberName), "", memberTy, attributeInfo, loc}); } } // only create shell, member func and member var will be added in next step ClassDef* ClosureConversion::CreateAutoEnvImplDef(const std::string& className, const std::vector& genericTypes, const Value& srcFunc, ClassDef& superClassDef, std::unordered_map& originalTypeToNewType) { // 1. create generic type params std::vector classGenericTypeParams; classGenericTypeParams.reserve(genericTypes.size()); auto genericTypePrefix = className + "_"; for (size_t i = 0; i < genericTypes.size(); ++i) { auto typeName = genericTypePrefix + std::to_string(i); auto newGenericType = builder.GetType(typeName, genericTypes[i]->GetSrcCodeIdentifier()); classGenericTypeParams.emplace_back(newGenericType); originalTypeToNewType.emplace(genericTypes[i], newGenericType); } /** * maybe upper bounds have `T`, we need to replace the old `T` with the new `T`, e.g. * func foo() where T <: Option { * func goo() {} // create AutoEnvImpl def * } * * class AutoEnvImpl <: AutoEnvBase where U <: Option {} * ^^^^^^^^^^^^^^ it shouldn't be `U <: Option` */ for (size_t i = 0; i < classGenericTypeParams.size(); ++i) { std::vector newUpperBounds; auto genericType = StaticCast(classGenericTypeParams[i]); if (genericType->GetSrcCodeIdentifier() == GENERIC_THIS_SRC_NAME) { auto parentCustomType = GetTopLevelFunc(srcFunc)->GetParentCustomTypeDef()->GetType(); auto parentRef = builder.GetType(parentCustomType); newUpperBounds.emplace_back(ReplaceRawGenericArgType(*parentRef, originalTypeToNewType, builder)); } else { for (auto type : genericTypes[i]->GetUpperBounds()) { newUpperBounds.emplace_back(ReplaceRawGenericArgType(*type, originalTypeToNewType, builder)); } } genericType->SetUpperBounds(newUpperBounds); } // 2. create class def auto classDef = builder.CreateClass(INVALID_LOCATION, "", className, package.GetName(), true, false); auto classTy = builder.GetType(classDef, classGenericTypeParams); classDef->SetType(*classTy); // 3. set super type /** * when a class inherit an instantiated class, this child class does not override parent's virtual function * Interface A { * static func foo(a: T) {} * } * class B <: A { * } * In class `B`, we will generate a func B::foo with type (T)->T to meet consistent function signatures in vtable * However when we got a function call B::foo(true), we should get its real function type (Bool)->Bool. * So the class `AutoEnvImpl`'s super type should be `AutoEnvBase` instead of `AutoEnvBase` */ std::vector superClassGenericArgs; auto memberFuncType = StaticCast(srcFunc.GetType()); if (memberFuncType->IsGenericRelated()) { for (auto paramTy : memberFuncType->GetParamTypes()) { if (auto orphanType = GetInstTypeFromOrphanGenericType(paramTy)) { superClassGenericArgs.push_back(orphanType); } else { superClassGenericArgs.emplace_back(ReplaceRawGenericArgType(*paramTy, originalTypeToNewType, builder)); } } if (auto retOrphanType = GetInstTypeFromOrphanGenericType(memberFuncType->GetReturnType())) { superClassGenericArgs.push_back(retOrphanType); } else { superClassGenericArgs.emplace_back( ReplaceRawGenericArgType(*memberFuncType->GetReturnType(), originalTypeToNewType, builder)); } } /** * if func type is generic related, the class `AutoEnvImpl` inherits `AutoEnvGenericBase<...>`, * otherwise, class `AutoEnvImpl` inherits `AutoEnvInstBase` which don't have generic args */ auto superClassTy = builder.GetType(&superClassDef, superClassGenericArgs); classDef->SetSuperClassTy(*superClassTy); // 4. set attribute SetAutoEnvImplDefAttr(*classDef); if (!classGenericTypeParams.empty()) { classDef->EnableAttr(Attribute::GENERIC); } return classDef; } ClassDef* ClosureConversion::GetOrCreateAutoEnvImplDef(FuncBase& func, ClassDef& superClassDef) { auto className = CHIRMangling::ClosureConversion::GenerateGlobalImplClassMangleName(func); auto it = autoEnvImplDefs.find(className); if (it != autoEnvImplDefs.end()) { return it->second; } /* 1. if func is nothing to do with generic type, like: func foo(a: Int32): Bool, then class def is like: class $Auto_Env_fooMangleName <: $Auto_Env_Base_param_1 { public override func fooMangleName_suffix(p0: Int32): Bool { // srcCodeIdentifier: $VirtualFuncInCC return foo(p0) } } 2. if func is related with generic type, then collect all visiable generic types as class def's generic type params 2.1 if func's params type and return type are nothing to do with generic type, like: foo(a: Int32): Bool then class def is like: class $Auto_Env_fooMangleName <: $Auto_Env_Base_param_1 { public override func fooMangleName_suffix(p0: Int32): Bool { // srcCodeIdentifier: $VirtualFuncInCC return foo(p0) } } note: declared new generic type in class def, use `U` as instantiated type args to call func foo 2.2 if func's params type or return type is related with generic type, like: foo(a: T0): T1, and T0 is declared in foo's outer decl, assume it's a struct decl named `S`, then class def is like: class $Auto_Env_fooMangleName <: $Auto_Env_Base_param_1 { public override func fooMangleName_suffix(p0: U0): U1 { // srcCodeIdentifier: $VirtualFuncInCC return S.foo(p0) } } note: in this case, func foo must be static, if func foo is non-static member method, it will be wrappered by lambda */ std::unordered_map originalTypeToNewType; auto genericTypes = CreateGenericTypeParamForAutoEnvImplDef(func, builder); auto classDef = CreateAutoEnvImplDef( className, genericTypes, func, superClassDef, originalTypeToNewType); CreateGenericOverrideMethodInAutoEnvImplDef(*classDef, func, originalTypeToNewType); CreateInstOverrideMethodInAutoEnvImplDef(*classDef, func, originalTypeToNewType); // cache class def autoEnvImplDefs.emplace(className, classDef); return classDef; } Func* ClosureConversion::LiftLambdaToGlobalFunc( ClassDef& autoEnvImplDef, Lambda& nestedFunc, const std::vector& genericTypeParams, const std::unordered_map& instMap, const std::vector& capturedValues) { // ======================= Generate the global func declaration ======================= // // 1. create the global func identifier auto globalFuncIdentifier = GenerateGlobalFuncIdentifier(nestedFunc); auto srcCodeIdentifier = nestedFunc.GetSrcCodeIdentifier(); // 2. create new func type auto newFuncParamTypes = nestedFunc.GetFuncType()->GetParamTypes(); auto classRefTy = builder.GetType(autoEnvImplDef.GetType()); newFuncParamTypes.insert(newFuncParamTypes.begin(), classRefTy); auto newFuncRetType = nestedFunc.GetFuncType()->GetReturnType(); auto newFuncTy = builder.GetType(newFuncParamTypes, newFuncRetType); newFuncTy = StaticCast(ReplaceRawGenericArgType(*newFuncTy, instMap, builder)); // 3. other info auto loc = nestedFunc.GetDebugLocation(); std::vector convertedGenericTypeParams; for (auto ty : genericTypeParams) { convertedGenericTypeParams.emplace_back( StaticCast(ReplaceRawGenericArgType(*ty, instMap, builder))); } // 4. create global function declare auto globalFunc = builder.CreateFunc(loc, newFuncTy, globalFuncIdentifier, srcCodeIdentifier, "", nestedFunc.GetTopLevelFunc()->GetPackageName(), convertedGenericTypeParams); SetLiftedLambdaAttr(*globalFunc, nestedFunc); auto sigInfo = FuncSigInfo{ .funcName = nestedFunc.GetSrcCodeIdentifier(), .funcType = nestedFunc.GetFuncType(), .genericTypeParams = nestedFunc.GetGenericTypeParams() }; globalFunc->SetOriginalLambdaInfo(sigInfo); // Specially, the lifted lambda should inherit local var ID globalFunc->InheritIDFromFunc(*nestedFunc.GetBody()->GetTopLevelFunc()); // Specially, record the `env` value Value* thisPtr = builder.CreateParameter(classRefTy, INVALID_LOCATION, *globalFunc); for (auto param : nestedFunc.GetParams()) { globalFunc->AddParam(*param); } // ======================= Generate the global func body ======================= // ReplaceThisTypeInApplyAndInvoke(nestedFunc, convertedGenericTypeParams, builder); globalFunc->InitBody(*nestedFunc.GetBody()); globalFunc->SetReturnValue(*nestedFunc.GetReturnValue()); // Then we need to insert: // 1) expressions to help with the debug // if there is no captured vars in lambda, we don't generate Debug for env, then in cjdb // it will look like this: `func lambda(param1, param2, ...)` // not like this: `func lambda($CapturedVars, param1, param2, ...)` // it just looks like fine, there won't be bug if generate Debug Expression* envParamDebug = nullptr; if (autoEnvImplDef.GetAllInstanceVarNum() != 0) { envParamDebug = builder.CreateExpression(builder.GetUnitTy(), thisPtr, "$env", globalFunc->GetEntryBlock()); globalFunc->GetEntryBlock()->InsertExprIntoHead(*envParamDebug); } // Finally we should: // 1) convert all the usage of captured variable to fields in env ReplaceEnvVarWithMemberVar(capturedValues, envParamDebug, *globalFunc, *thisPtr, builder); // 2) keep relations between default's param's desugar func and the func where the param is belonged. // logic below depends on the desugar func is CCed after the func where the param is belonged. if (nestedFunc.GetParamDftValHostFunc()) { if (auto it = convertedCache.find(nestedFunc.GetParamDftValHostFunc()); it != convertedCache.cend()) { globalFunc->SetParamDftValHostFunc(*it->second); } else { InternalError("never come here in ConvertNestedFunctions"); } } // 3) Traverse and instantiate the original generics with the new generics of lifted func auto thisTyReplacement = nestedFunc.GetTopLevelFunc()->GetParentCustomTypeOrExtendedType(); GenericTypeConvertor gConvertor(instMap, builder); ConvertTypeFunc convertFunc = [&gConvertor, &thisTyReplacement, this](Type& type) { if (thisTyReplacement != nullptr) { auto res = ReplaceThisTypeToConcreteType(type, *thisTyReplacement, builder); if (res != &type) { return res; } } return gConvertor.ConvertToInstantiatedType(type); }; PrivateTypeConverterNoInvokeOriginal converter(convertFunc, builder); auto postVisit = [&converter](Expression& e) { converter.VisitExpr(e); return VisitResult::CONTINUE; }; Visitor::Visit( *globalFunc, [](Expression&) { return VisitResult::CONTINUE; }, postVisit); for (auto& param : globalFunc->GetParams()) { converter.VisitValue(*param); } convertedCache.emplace(&nestedFunc, globalFunc); return globalFunc; } ClassDef* ClosureConversion::GetOrCreateAutoEnvImplDef( Lambda& func, ClassDef& superClassDef, const std::vector& boxedEnvs) { auto it = duplicateLambdaName.find(func.GetIdentifier()); CJC_ASSERT(it != duplicateLambdaName.end()); auto className = CHIRMangling::ClosureConversion::GenerateLambdaImplClassMangleName(func, it->second); CJC_ASSERT(autoEnvImplDefs.count(className) == 0); /* 1. if lambda is nothing to do with generic type, like: func foo(a: Int32): Bool, and there are captured variable in lambda body, assume they are `let a: Int32` and `var b: Int64`, then class def is like: class $Capture_Int64 { var value: Int64 } class $Auto_Env_fooMangleName <: $Auto_Env_Base_param_1 { var a: Int32 var b: Class-$Capture_Int64 public override func fooMangleName(p0: Int32): Bool { // srcCodeIdentifier: $VirtualFuncInCC lambda's body } } note: member var `a` in $Auto_Env will be set value in member func `$VirtualFuncInCC`, so it's a mutable var not a readonly var 2. if lambda is related with generic type, then collect all visiable generic types as class def's generic type params. specially, if lambda is like: foo(a: Int32): Bool, there is generic type declared in lambda, class def is like: class $Auto_Env_fooMangleName <: $Auto_Env_Base_param_1 { public override func fooMangleName(p0: Int32): Bool { // srcCodeIdentifier: $VirtualFuncInCC lambda's body } } note: replace generic type `T` in lambda body with `U` */ std::unordered_map originalTypeToNewType; auto genericTypes = CreateGenericTypeParamForAutoEnvImplDef(*func.GetResult(), builder); auto classDef = CreateAutoEnvImplDef( className, genericTypes, *func.GetResult(), superClassDef, originalTypeToNewType); CreateMemberVarInAutoEnvImplDef(*classDef, boxedEnvs, originalTypeToNewType); auto parentFunc = func.GetTopLevelFunc(); CJC_NULLPTR_CHECK(parentFunc); auto globalFunc = LiftLambdaToGlobalFunc(*classDef, func, genericTypes, originalTypeToNewType, boxedEnvs); if (srcCodeImportedFuncs.find(parentFunc) != srcCodeImportedFuncs.end()) { uselessClasses.emplace(classDef); uselessLambda.emplace(globalFunc); } CreateGenericOverrideMethodInAutoEnvImplDef(*classDef, *globalFunc, originalTypeToNewType); CreateInstOverrideMethodInAutoEnvImplDef(*classDef, *globalFunc, originalTypeToNewType); autoEnvImplDefs.emplace(className, classDef); return classDef; } void ClosureConversion::ReplaceUserPoint(FuncBase& srcFunc, Expression& user, ClassDef& autoEnvImplDef) { auto curBlock = user.GetParentBlock(); auto autoEnvImplType = StaticCast(autoEnvImplDef.GetType()); std::vector emptyEnvs; auto autoEnvObj = CreateAutoEnvImplObject(*curBlock, *autoEnvImplType, emptyEnvs, user, srcFunc); // typecast to base type auto curClassTy = StaticCast(StaticCast(autoEnvObj->GetType())->GetBaseType()); auto superClassTy = curClassTy->GetSuperClassTy(&builder); auto superClassRefTy = builder.GetType(superClassTy); auto castToBaseType = builder.CreateExpression(superClassRefTy, autoEnvObj, user.GetParentBlock()); castToBaseType->MoveBefore(&user); auto res = CastTypeFromAutoEnvRefToFuncType( *srcFunc.GetType(), *castToBaseType->GetResult(), user, GetOutDefDeclaredTypes(srcFunc), builder); user.ReplaceOperand(&srcFunc, res); } void ClosureConversion::ReplaceUserPoint( Lambda& srcFunc, Expression& user, const std::vector& envs, ClassDef& autoEnvImplDef) { auto curBlock = user.GetParentBlock(); auto autoEnvImplType = StaticCast(autoEnvImplDef.GetType()); Value* autoEnvObj = nullptr; // if user is in lambda func body, then `autoEnvObj` is `this` auto it = convertedCache.find(&srcFunc); CJC_ASSERT(it != convertedCache.end()); auto globalFunc = it->second; if (user.GetTopLevelFunc() == globalFunc) { autoEnvObj = user.GetTopLevelFunc()->GetParam(0); } else { autoEnvObj = CreateAutoEnvImplObject(*curBlock, *autoEnvImplType, envs, user, *srcFunc.GetResult()); } if (auto apply = DynamicCast(&user); apply && apply->GetCallee() == srcFunc.GetResult()) { auto methods = autoEnvImplDef.GetMethods(); auto newCallee = methods.back(); auto newArgs = apply->GetArgs(); newArgs.insert(newArgs.begin(), autoEnvObj); auto retType = apply->GetResult()->GetType(); auto loc = apply->GetDebugLocation(); auto thisTy = autoEnvObj->GetType(); auto newApply = builder.CreateExpression(loc, retType, newCallee, FuncCallContext{ .args = newArgs, .thisType = thisTy}, user.GetParentBlock()); apply->ReplaceWith(*newApply); } else if (auto awe = DynamicCast(&user); awe && awe->GetCallee() == srcFunc.GetResult()) { auto methods = autoEnvImplDef.GetMethods(); auto newCallee = methods.back(); auto newArgs = awe->GetArgs(); newArgs.insert(newArgs.begin(), autoEnvObj); auto retType = awe->GetResult()->GetType(); auto loc = awe->GetDebugLocation(); auto thisTy = autoEnvObj->GetType(); auto newApply = builder.CreateExpression(loc, retType, newCallee, FuncCallContext{ .args = newArgs, .thisType = thisTy}, awe->GetSuccessBlock(), awe->GetErrorBlock(), user.GetParentBlock()); awe->ReplaceWith(*newApply); } else { auto res = CastTypeFromAutoEnvRefToFuncType(*srcFunc.GetResult()->GetType(), *autoEnvObj, user, GetOutDefDeclaredTypes(*srcFunc.GetResult()), builder); user.ReplaceOperand(srcFunc.GetResult(), res); } } ClassDef* ClosureConversion::GetOrCreateInstAutoEnvBaseDef(const FuncType& funcType, ClassDef& superClass) { auto className = CHIRMangling::ClosureConversion::GenerateInstantiatedBaseClassMangleName(funcType); auto it = instAutoEnvBaseDefs.find(className); if (it != instAutoEnvBaseDefs.end()) { return it->second; } // create class def auto classDef = builder.CreateClass(INVALID_LOCATION, "", className, package.GetName(), true, false); std::vector emptyTypeArgs; auto classTy = builder.GetType(classDef, emptyTypeArgs); classDef->SetType(*classTy); // set super class type auto superClassTy = builder.GetType(&superClass, funcType.GetTypeArgs()); classDef->SetSuperClassTy(*superClassTy); // set attribute SetAutoEnvBaseDefAttr(*classDef); // add abstract method CreateVirtualFuncInAutoEnvBaseDef(*classDef, INST_VIRTUAL_FUNC, funcType.GetTypeArgs(), builder); // cache class def instAutoEnvBaseDefs.emplace(className, classDef); return classDef; } void ClosureConversion::ConvertGlobalFunctions() { for (auto func : package.GetGlobalFuncs()) { if (func->IsCFunc()) { continue; // never lift CFunc } auto users = func->GetUsers(); bool convertFlag{false}; for (auto user : users) { if (IsCalleeOfApply(*user, *func)) { continue; } auto autoEnvBaseDef = GetOrCreateAutoEnvBaseDef(*func->GetFuncType()); auto autoEnvImplDef = GetOrCreateAutoEnvImplDef(*func, *autoEnvBaseDef); ReplaceUserPoint(*func, *user, *autoEnvImplDef); convertFlag = true; if (opts.chirDebugOptimizer) { PrintGlobalFuncInfo(func->GetDebugLocation().GetBeginPos()); } } if (opts.enIncrementalCompilation && convertFlag) { if (!func->GetRawMangledName().empty()) { ccOutFuncsRawMangle.emplace(func->GetRawMangledName()); } } RemoveGetInstantiateValue(users); } } void ClosureConversion::ConvertImportedFunctions() { for (auto ele : package.GetImportedVarAndFuncs()) { if (ele->IsImportedVar() || ele->GetType()->IsCFunc()) { continue; } auto users = ele->GetUsers(); auto func = StaticCast(ele); bool convertFlag{false}; for (auto user : users) { if (IsCalleeOfApply(*user, *func)) { continue; } auto autoEnvBaseDef = GetOrCreateAutoEnvBaseDef(*func->GetFuncType()); auto autoEnvImplDef = GetOrCreateAutoEnvImplDef(*func, *autoEnvBaseDef); ReplaceUserPoint(*func, *user, *autoEnvImplDef); convertFlag = true; if (opts.chirDebugOptimizer) { PrintImportedFuncInfo(*func); } } if (opts.enIncrementalCompilation && convertFlag) { if (!func->GetRawMangledName().empty()) { ccOutFuncsRawMangle.emplace(func->GetRawMangledName()); } } RemoveGetInstantiateValue(users); } } void ClosureConversion::ConvertApplyToInvoke(const std::vector& applyExprs) { for (auto e : applyExprs) { auto callee = e->GetCallee(); ClassDef* autoEnvBaseDef = nullptr; ClassType* instParentType = nullptr; if (auto funcType = DynamicCast(callee->GetType())) { // callee is still func type autoEnvBaseDef = GetOrCreateAutoEnvBaseDef(*funcType); instParentType = InstantiateAutoEnvBaseType(*autoEnvBaseDef, *funcType, builder); } else { // callee has been replaced in closure conversion, then apply must be converted to invoke instParentType = StaticCast(StaticCast(callee->GetType())->GetBaseType()); autoEnvBaseDef = instParentType->GetClassDef(); } auto [methodName, originalFuncType] = GetFuncTypeFromAutoEnvBaseDef(*autoEnvBaseDef); auto invokeInfo = InvokeCallContext { .caller = callee, .funcCallCtx = FuncCallContext { .args = e->GetArgs(), .thisType = instParentType }, .virMethodCtx = VirMethodContext { .srcCodeIdentifier = methodName, .originalFuncType = originalFuncType } }; auto invoke = builder.CreateExpression( e->GetDebugLocation(), e->GetResult()->GetType(), invokeInfo, e->GetParentBlock()); invoke->Set(0); e->ReplaceWith(*invoke); } } void ClosureConversion::ConvertApplyWithExceptionToInvokeWithException( const std::vector& applyExprs) { for (auto e : applyExprs) { auto callee = e->GetCallee(); ClassDef* autoEnvBaseDef = nullptr; ClassType* instParentType = nullptr; if (auto funcType = DynamicCast(callee->GetType())) { // callee is still func type autoEnvBaseDef = GetOrCreateAutoEnvBaseDef(*funcType); instParentType = InstantiateAutoEnvBaseType(*autoEnvBaseDef, *funcType, builder); } else { // callee has been replaced in closure conversion, then apply must be converted to invoke instParentType = StaticCast(StaticCast(callee->GetType())->GetBaseType()); autoEnvBaseDef = instParentType->GetClassDef(); } auto [methodName, originalFuncType] = GetFuncTypeFromAutoEnvBaseDef(*autoEnvBaseDef); auto invokeInfo = InvokeCallContext { .caller = callee, .funcCallCtx = FuncCallContext { .args = e->GetArgs(), .thisType = instParentType }, .virMethodCtx = VirMethodContext { .srcCodeIdentifier = methodName, .originalFuncType = originalFuncType } }; auto invoke = builder.CreateExpression(e->GetDebugLocation(), e->GetResult()->GetType(), invokeInfo, e->GetSuccessBlock(), e->GetErrorBlock(), e->GetParentBlock()); invoke->Set(0); e->ReplaceWith(*invoke); } } void ClosureConversion::ConvertExpressions() { std::vector applyExprs; std::vector applyWithExceptionExprs; std::vector typecastToBoxExprs; std::vector typecastToUnBoxExprs; std::vector boxToTypecastExprs; std::vector unboxToTypecastExprs; auto postVisit = [&applyExprs, &applyWithExceptionExprs, &typecastToBoxExprs, &typecastToUnBoxExprs, &boxToTypecastExprs, &unboxToTypecastExprs](Expression& e) { if (ApplyNeedConvertToInvoke(e)) { applyExprs.emplace_back(StaticCast(&e)); } else if (ApplyWithExceptionNeedConvertToInvokeWithException(e)) { applyWithExceptionExprs.emplace_back(StaticCast(&e)); } else if (TypeCastNeedConvertToBox(e)) { typecastToBoxExprs.emplace_back(StaticCast(&e)); } else if (TypeCastNeedConvertToUnBox(e)) { typecastToUnBoxExprs.emplace_back(StaticCast(&e)); } else if (BoxNeedConvertToTypeCast(e)) { boxToTypecastExprs.emplace_back(StaticCast(&e)); } else if (UnBoxNeedConvertToTypeCast(e)) { unboxToTypecastExprs.emplace_back(StaticCast(&e)); } return VisitResult::CONTINUE; }; for (auto func : package.GetGlobalFuncs()) { if (func->TestAttr(Attribute::SKIP_ANALYSIS)) { continue; } Visitor::Visit( *func, [](Expression&) { return VisitResult::CONTINUE; }, postVisit); } ConvertApplyToInvoke(applyExprs); ConvertApplyWithExceptionToInvokeWithException(applyWithExceptionExprs); ConvertTypeCastToBox(typecastToBoxExprs, builder); ConvertTypeCastToUnBox(typecastToUnBoxExprs, builder); ConvertBoxToTypeCast(boxToTypecastExprs, builder); ConvertUnBoxToTypeCast(unboxToTypecastExprs, builder); } void ClosureConversion::CreateVTableForAutoEnvDef() { auto vtableGenerator = VTableGenerator(builder); for (auto& it : genericAutoEnvBaseDefs) { vtableGenerator.GenerateVTable(*it.second); } for (auto& it : instAutoEnvBaseDefs) { vtableGenerator.GenerateVTable(*it.second); } for (auto& it : instAutoEnvWrapperDefs) { vtableGenerator.GenerateVTable(*it.second); } for (auto& it : autoEnvImplDefs) { vtableGenerator.GenerateVTable(*it.second); } } ClassDef* ClosureConversion::CreateAutoEnvWrapper(const std::string& className, ClassType& superClassType) { auto wrapperClassDef = builder.CreateClass(INVALID_LOCATION, "", className, package.GetName(), true, false); std::vector emptyTypeArgs; auto classType = builder.GetType(wrapperClassDef, emptyTypeArgs); wrapperClassDef->SetType(*classType); wrapperClassDef->SetSuperClassTy(superClassType); SetAutoEnvImplDefAttr(*wrapperClassDef); return wrapperClassDef; } void ClosureConversion::CreateMemberVarInAutoEnvWrapper(ClassDef& autoEnvWrapperDef) { Type* memberVarType = autoEnvWrapperDef.GetSuperClassTy()->GetSuperClassTy(&builder); memberVarType = builder.GetType(memberVarType); AttributeInfo attributeInfo; attributeInfo.SetAttr(Attribute::PUBLIC, true); autoEnvWrapperDef.AddInstanceVar(MemberVarInfo{"v", "", memberVarType, attributeInfo, INVALID_LOCATION}); } Func* ClosureConversion::CreateGenericMethodInAutoEnvWrapper(ClassDef& autoEnvWrapperDef) { // 1. create function type auto memberVars = autoEnvWrapperDef.GetDirectInstanceVars(); CJC_ASSERT(memberVars.size() == 1); auto memberVarType = StaticCast(memberVars[0].type->StripAllRefs()); auto funcNamePrefix = autoEnvWrapperDef.GetIdentifierWithoutPrefix(); std::unordered_map emptyTable; auto [paramTypes, retType] = CreateFuncTypeWithOrphanT( funcNamePrefix, memberVarType->GetGenericArgs(), emptyTable, builder); auto autoEnvWrapperRefType = builder.GetType(autoEnvWrapperDef.GetType()); paramTypes.insert(paramTypes.begin(), autoEnvWrapperRefType); auto funcType = builder.GetType(paramTypes, retType); // 2. create function auto funcMangledName = CHIRMangling::ClosureConversion::GenerateWrapperClassGenericOverrideFuncMangleName(autoEnvWrapperDef); auto func = builder.CreateFunc( INVALID_LOCATION, funcType, funcMangledName, GENERIC_VIRTUAL_FUNC, "", package.GetName()); autoEnvWrapperDef.AddMethod(func); // 3. set attribute SetMemberMethodAttr(*func, false); // 4. create function body auto blockGroup = builder.CreateBlockGroup(*func); func->InitBody(*blockGroup); auto entry = builder.CreateBlock(blockGroup); blockGroup->SetEntryBlock(entry); // 5. create parameters for (auto paramTy : paramTypes) { builder.CreateParameter(paramTy, INVALID_LOCATION, *func); } // 6. create return value auto retRefTy = builder.GetType(retType); auto retVal = CreateAndAppendExpression(builder, retRefTy, retType, entry); func->SetReturnValue(*retVal->GetResult()); // 7. create invoke auto instCustomType = memberVarType; auto instParamTypesAndRetType = instCustomType->GetGenericArgs(); auto instParamTypes = std::vector(instParamTypesAndRetType.begin(), instParamTypesAndRetType.end() - 1); auto [methodName, originalFuncType] = GetFuncTypeFromAutoEnvBaseDef(*instCustomType->GetClassDef()); auto& funcParams = func->GetParams(); std::vector invokeArgs(funcParams.begin() + 1, funcParams.end()); auto memberVarRefType = builder.GetType(memberVarType); auto memberVarRefRefType = builder.GetType(memberVarRefType); auto memberVarRefRef = CreateAndAppendExpression( builder, memberVarRefRefType, funcParams[0], std::vector{0}, entry)->GetResult(); auto memberVarRef = CreateAndAppendExpression(builder, memberVarRefType, memberVarRefRef, entry)->GetResult(); auto invokeInfo = InvokeCallContext { .caller = memberVarRef, .funcCallCtx = FuncCallContext { .args = invokeArgs, .thisType = instCustomType }, .virMethodCtx = VirMethodContext { .srcCodeIdentifier = methodName, .originalFuncType = originalFuncType } }; auto invokeRes = CreateAndAppendExpression(builder, retType, invokeInfo, entry)->GetResult(); invokeRes->GetExpr()->Set(0); // 8. store return value and exit CreateAndAppendExpression(builder, builder.GetType(), invokeRes, retVal->GetResult(), entry); CreateAndAppendTerminator(builder, entry); return func; } void ClosureConversion::CreateInstMethodInAutoEnvWrapper(ClassDef& autoEnvWrapperDef, Func& genericFunc) { // 1. create function type auto superDef = autoEnvWrapperDef.GetSuperClassDef(); CJC_ASSERT(superDef->GetType()->IsAutoEnvInstBase()); auto abstractMethods = superDef->GetAbstractMethods(); CJC_ASSERT(abstractMethods.size() == 1); auto parentFuncType = StaticCast(abstractMethods[0].methodTy); auto paramTypes = parentFuncType->GetParamTypes(); CJC_ASSERT(!paramTypes.empty()); auto autoEnvWrapperRefType = builder.GetType(autoEnvWrapperDef.GetType()); paramTypes[0] = autoEnvWrapperRefType; auto retType = parentFuncType->GetReturnType(); auto funcType = builder.GetType(paramTypes, retType); // 2. create function auto funcNamePrefix = autoEnvWrapperDef.GetIdentifierWithoutPrefix(); auto funcMangledName = CHIRMangling::ClosureConversion::GenerateWrapperClassInstOverrideFuncMangleName(autoEnvWrapperDef); auto func = builder.CreateFunc( INVALID_LOCATION, funcType, funcMangledName, INST_VIRTUAL_FUNC, "", package.GetName()); autoEnvWrapperDef.AddMethod(func); // 3. set attribute SetMemberMethodAttr(*func, false); // 4. create function body auto blockGroup = builder.CreateBlockGroup(*func); func->InitBody(*blockGroup); auto entry = builder.CreateBlock(blockGroup); blockGroup->SetEntryBlock(entry); // 5. create parameters for (auto paramTy : paramTypes) { builder.CreateParameter(paramTy, INVALID_LOCATION, *func); } // 6. create return value auto retRefTy = builder.GetType(retType); auto retVal = CreateAndAppendExpression(builder, retRefTy, retType, entry); func->SetReturnValue(*retVal->GetResult()); auto& params = func->GetParams(); auto applyArgs = std::vector(params.begin(), params.end()); auto customType = autoEnvWrapperDef.GetType(); auto apply = CreateAndAppendExpression(builder, retType, &genericFunc, FuncCallContext{ .args = applyArgs, .thisType = customType}, entry); CreateAndAppendExpression( builder, builder.GetType(), apply->GetResult(), retVal->GetResult(), entry); CreateAndAppendTerminator(builder, entry); // 7. create wrapper class if type is mismatched if (auto res = ApplyArgNeedTypeCast(*apply); res.first) { AddTypeCastForOperand({apply, res.second}, builder); } if (ApplyRetValNeedWrapper(*apply)) { WrapApplyRetVal(*apply); } } ClassDef* ClosureConversion::GetOrCreateAutoEnvWrapper(ClassType& instAutoEnvBaseType) { auto instAutoEnvBaseDef = instAutoEnvBaseType.GetClassDef(); auto wrapperName = CHIRMangling::ClosureConversion::GenerateWrapperClassMangleName(*instAutoEnvBaseDef); auto it = instAutoEnvWrapperDefs.find(wrapperName); if (it != instAutoEnvWrapperDefs.end()) { return it->second; } auto wrapperClassDef = CreateAutoEnvWrapper(wrapperName, instAutoEnvBaseType); CreateMemberVarInAutoEnvWrapper(*wrapperClassDef); auto genericFunc = CreateGenericMethodInAutoEnvWrapper(*wrapperClassDef); CreateInstMethodInAutoEnvWrapper(*wrapperClassDef, *genericFunc); instAutoEnvWrapperDefs.emplace(wrapperName, wrapperClassDef); return wrapperClassDef; } void ClosureConversion::WrapApplyRetVal(Apply& apply) { /** convert from: * %0: $AutoEnvInstBase& = Apply(xxx) * %1: xxx = Expression(%0) * * to: * %0: $AutoEnvGenericBase& = Apply(xxx) * %2: $AutoEnvWrapperClass& = Allocate($AutoEnvWrapperClass) * %3: Unit = StoreElementRef(%0, %2, 0) * %4: $AutoEnvInstBase& = TypeCast(%2) * %1: xxx = Expression(%4) */ // 1. create $Auto_Env_wrapper auto applyRetVal = apply.GetResult(); auto applyRetType = applyRetVal->GetType(); auto applyRetTypeNoRef = StaticCast(applyRetType->StripAllRefs()); auto autoEnvWrapperDef = GetOrCreateAutoEnvWrapper(*applyRetTypeNoRef); // 2. convert return type auto newApplyRetType = builder.GetType(autoEnvWrapperDef->GetSuperClassDef()->GetSuperClassTy()); ConvertTypeFunc convertRetType = [&applyRetType, &newApplyRetType](const Type& type) { CJC_ASSERT(&type == applyRetType); return newApplyRetType; }; PrivateTypeConverter converter(convertRetType, builder); converter.VisitValue(*applyRetVal); // 3. create $Auto_Env_wrapper object auto parentBlock = apply.GetParentBlock(); auto autoEnvWrapperType = autoEnvWrapperDef->GetType(); auto autoEnvWrapperRefType = builder.GetType(autoEnvWrapperType); auto allocate = builder.CreateExpression(autoEnvWrapperRefType, autoEnvWrapperType, parentBlock); allocate->MoveAfter(&apply); auto memberVar = applyRetVal; auto storeMemberVar = builder.CreateExpression( builder.GetUnitTy(), memberVar, allocate->GetResult(), std::vector{0}, parentBlock); storeMemberVar->MoveAfter(allocate); // typecast from $Auto_Env_xxx_wrapper to $Auto_Env_InstBase auto typecast = builder.CreateExpression(applyRetType, allocate->GetResult(), parentBlock); typecast->MoveAfter(storeMemberVar); // 4. replace user ReplaceOperandWithAutoEnvWrapperClass(*apply.GetResult(), *typecast->GetResult(), {storeMemberVar}); } void ClosureConversion::WrapApplyWithExceptionRetVal(ApplyWithException& apply) { /** convert from: * Block #0: * %0: $AutoEnvInstBase& = ApplyWithException(xxx, #1, #2) * Block #1: * %1: xxx = Expression(%0) * Block #2: * %2: xxx = Expression(%0) * * to: * Block #0: * %0: $AutoEnvGenericBase& = ApplyWithException(xxx, #1, #2) * Block #1: * %3: $AutoEnvWrapperClass& = Allocate($AutoEnvWrapperClass) * %4: Unit = StoreElementRef(%0, %3, 0) * %5: $AutoEnvInstBase& = TypeCast(%3) * %1: xxx = Expression(%5) * Block #2: * %6: $AutoEnvWrapperClass& = Allocate($AutoEnvWrapperClass) * %7: Unit = StoreElementRef(%0, %6, 0) * %8: $AutoEnvInstBase& = TypeCast(%6) * %2: xxx = Expression(%8) */ // 1. create $Auto_Env_wrapper auto applyRetVal = apply.GetResult(); auto applyRetType = applyRetVal->GetType(); auto applyRetTypeNoRef = StaticCast(applyRetType->StripAllRefs()); auto autoEnvWrapperDef = GetOrCreateAutoEnvWrapper(*applyRetTypeNoRef); // 2. convert return type auto newApplyRetType = builder.GetType(autoEnvWrapperDef->GetSuperClassDef()->GetSuperClassTy()); ConvertTypeFunc convertRetType = [&applyRetType, &newApplyRetType](const Type& type) { CJC_ASSERT(&type == applyRetType); return newApplyRetType; }; PrivateTypeConverter converter(convertRetType, builder); converter.VisitValue(*applyRetVal); auto autoEnvWrapperType = autoEnvWrapperDef->GetType(); auto autoEnvWrapperRefType = builder.GetType(autoEnvWrapperType); for (auto user : applyRetVal->GetUsers()) { if (user->GetExprKind() == ExprKind::INSTANCEOF) { continue; } // 3. create $Auto_Env_wrapper object auto userParentBlock = user->GetParentBlock(); auto allocate = builder.CreateExpression(autoEnvWrapperRefType, autoEnvWrapperType, userParentBlock); allocate->MoveBefore(user); auto memberVar = applyRetVal; auto storeMemberVar = builder.CreateExpression( builder.GetUnitTy(), memberVar, allocate->GetResult(), std::vector{0}, userParentBlock); storeMemberVar->MoveAfter(allocate); // typecast from $Auto_Env_xxx_wrapper to $Auto_Env_InstBase auto typecast = builder.CreateExpression(applyRetType, allocate->GetResult(), userParentBlock); typecast->MoveAfter(storeMemberVar); // 4. replace user user->ReplaceOperand(applyRetVal, typecast->GetResult()); } } void ClosureConversion::WrapInvokeRetVal(Expression& e) { /** convert from: * %0: $AutoEnvInstBase& = Invoke(xxx) * %1: xxx = Expression(%0) * * to: * %0: $AutoEnvGenericBase& = Invoke(xxx) * %2: $AutoEnvWrapperClass& = Allocate($AutoEnvWrapperClass) * %3: Unit = StoreElementRef(%0, %2, 0) * %4: $AutoEnvInstBase& = TypeCast(%2) * %1: xxx = Expression(%4) */ // 1. create $Auto_Env_wrapper auto invokeRetVal = e.GetResult(); auto invokeRetType = invokeRetVal->GetType(); auto invokeRetTypeNoRef = StaticCast(invokeRetType->StripAllRefs()); auto autoEnvWrapperDef = GetOrCreateAutoEnvWrapper(*invokeRetTypeNoRef); // 2. convert return type CJC_NULLPTR_CHECK(autoEnvWrapperDef->GetSuperClassDef()); auto newInvokeRetType = builder.GetType(autoEnvWrapperDef->GetSuperClassDef()->GetSuperClassTy()); ConvertTypeFunc convertRetType = [&invokeRetType, &newInvokeRetType](const Type& type) { CJC_ASSERT(&type == invokeRetType); return newInvokeRetType; }; PrivateTypeConverter converter(convertRetType, builder); converter.VisitValue(*invokeRetVal); // 3. create $Auto_Env_wrapper object auto parentBlock = e.GetParentBlock(); auto autoEnvWrapperType = autoEnvWrapperDef->GetType(); auto autoEnvWrapperRefType = builder.GetType(autoEnvWrapperType); auto allocate = builder.CreateExpression(autoEnvWrapperRefType, autoEnvWrapperType, parentBlock); allocate->MoveAfter(&e); auto memberVar = invokeRetVal; auto storeMemberVar = builder.CreateExpression( builder.GetUnitTy(), memberVar, allocate->GetResult(), std::vector{0}, parentBlock); storeMemberVar->MoveAfter(allocate); // typecast from $Auto_Env_xxx_wrapper to $Auto_Env_InstBase auto typecast = builder.CreateExpression(invokeRetType, allocate->GetResult(), parentBlock); typecast->MoveAfter(storeMemberVar); // 4. replace user ReplaceOperandWithAutoEnvWrapperClass(*e.GetResult(), *typecast->GetResult(), {storeMemberVar}); } void ClosureConversion::WrapInvokeWithExceptionRetVal(Expression& e) { /** convert from: * Block #0: * %0: $AutoEnvInstBase& = InvokeWithException(xxx, #1, #2) * Block #1: * %1: xxx = Expression(%0) * Block #2: * %2: xxx = Expression(%0) * * to: * Block #0: * %0: $AutoEnvGenericBase& = InvokeWithException(xxx, #1, #2) * Block #1: * %3: $AutoEnvWrapperClass& = Allocate($AutoEnvWrapperClass) * %4: Unit = StoreElementRef(%0, %3, 0) * %5: $AutoEnvInstBase& = TypeCast(%3) * %1: xxx = Expression(%5) * Block #2: * %6: $AutoEnvWrapperClass& = Allocate($AutoEnvWrapperClass) * %7: Unit = StoreElementRef(%0, %6, 0) * %8: $AutoEnvInstBase& = TypeCast(%6) * %2: xxx = Expression(%8) */ // 1. create $Auto_Env_wrapper auto invokeRetVal = e.GetResult(); auto invokeRetType = invokeRetVal->GetType(); auto invokeRetTypeNoRef = StaticCast(invokeRetType->StripAllRefs()); auto autoEnvWrapperDef = GetOrCreateAutoEnvWrapper(*invokeRetTypeNoRef); // 2. convert return type auto newInvokeRetType = builder.GetType(autoEnvWrapperDef->GetSuperClassDef()->GetSuperClassTy()); ConvertTypeFunc convertRetType = [&invokeRetType, &newInvokeRetType](const Type& type) { CJC_ASSERT(&type == invokeRetType); return newInvokeRetType; }; PrivateTypeConverter converter(convertRetType, builder); converter.VisitValue(*invokeRetVal); auto autoEnvWrapperType = autoEnvWrapperDef->GetType(); auto autoEnvWrapperRefType = builder.GetType(autoEnvWrapperType); for (auto user : invokeRetVal->GetUsers()) { if (user->GetExprKind() == ExprKind::INSTANCEOF) { continue; } // 3. create $Auto_Env_wrapper object auto userParentBlock = user->GetParentBlock(); auto allocate = builder.CreateExpression(autoEnvWrapperRefType, autoEnvWrapperType, userParentBlock); allocate->MoveBefore(user); auto memberVar = invokeRetVal; auto storeMemberVar = builder.CreateExpression( builder.GetUnitTy(), memberVar, allocate->GetResult(), std::vector{0}, userParentBlock); storeMemberVar->MoveAfter(allocate); // typecast from $Auto_Env_xxx_wrapper to $Auto_Env_InstBase auto typecast = builder.CreateExpression(invokeRetType, allocate->GetResult(), userParentBlock); typecast->MoveAfter(storeMemberVar); // 4. replace user user->ReplaceOperand(invokeRetVal, typecast->GetResult()); } } void ClosureConversion::WrapGetElementRefRetVal(GetElementRef& getEleRef) { /** convert from: * %0: $AutoEnvInstBase&& = GetElementRef(xxx) * %1: $AutoEnvInstBase& = Load(%0) * %2: xxx = Expression(%1) * * to: * %0: $AutoEnvGenericBase&& = GetElementRef(xxx) * %3: $AutoEnvWrapperClass& = Allocate($AutoEnvWrapperClass) * %4: $AutoEnvGenericBase& = Load(%0) * %5: Unit = StoreElementRef(%4, %3, 0) * %6: $AutoEnvInstBase& = TypeCast(%3) * %7: $AutoEnvInstBase&& = Allocate($AutoEnvInstBase&) * %8: Unit = Store(%6, %7) * %1: $AutoEnvInstBase& = Load(%7) * %2: xxx = Expression(%1) */ // 1. create $Auto_Env_wrapper auto getEleRefRetVal = getEleRef.GetResult(); auto getEleRefRetType = getEleRefRetVal->GetType(); const size_t refDim = 2; CJC_ASSERT(getEleRefRetType->IsReferenceTypeWithRefDims(refDim)); auto getEleRefRetTypeNoRef = StaticCast(getEleRefRetType->StripAllRefs()); auto autoEnvWrapperDef = GetOrCreateAutoEnvWrapper(*getEleRefRetTypeNoRef); // 2. convert return type auto newGetEleRefRetType = builder.GetType(builder.GetType(autoEnvWrapperDef->GetSuperClassDef()->GetSuperClassTy())); ConvertTypeFunc convertRetType = [&getEleRefRetType, &newGetEleRefRetType](const Type& type) { CJC_ASSERT(&type == getEleRefRetType); return newGetEleRefRetType; }; PrivateTypeConverter converter(convertRetType, builder); converter.VisitValue(*getEleRefRetVal); // 3. create $Auto_Env_wrapper object auto parentBlock = getEleRef.GetParentBlock(); auto autoEnvWrapperType = autoEnvWrapperDef->GetType(); auto autoEnvWrapperRefType = builder.GetType(autoEnvWrapperType); auto allocate1 = builder.CreateExpression(autoEnvWrapperRefType, autoEnvWrapperType, parentBlock); allocate1->MoveAfter(&getEleRef); auto loadRetType = StaticCast(newGetEleRefRetType)->GetBaseType(); auto load = builder.CreateExpression(loadRetType, getEleRefRetVal, parentBlock); load->MoveAfter(allocate1); auto memberVar = load->GetResult(); auto storeMemberVar = builder.CreateExpression( builder.GetUnitTy(), memberVar, allocate1->GetResult(), std::vector{0}, parentBlock); storeMemberVar->MoveAfter(load); // typecast from $Auto_Env_xxx_wrapper to $Auto_Env_InstBase auto expectedType = StaticCast(getEleRefRetType)->GetBaseType(); auto typecast = builder.CreateExpression(expectedType, allocate1->GetResult(), parentBlock); typecast->MoveAfter(storeMemberVar); auto typecastRetType = typecast->GetResult()->GetType(); auto allocate2RetType = builder.GetType(typecastRetType); auto allocate2 = builder.CreateExpression(allocate2RetType, typecastRetType, parentBlock); allocate2->MoveAfter(typecast); auto store = builder.CreateExpression( builder.GetUnitTy(), typecast->GetResult(), allocate2->GetResult(), parentBlock); store->MoveAfter(allocate2); // 4. replace user ReplaceOperandWithAutoEnvWrapperClass(*getEleRefRetVal, *allocate2->GetResult(), {load}); } void ClosureConversion::WrapFieldRetVal(Field& field) { /** convert from: * %0: $AutoEnvInstBase& = Field(xxx) * %1: xxx = Expression(%0) * * to: * %0: $AutoEnvGenericBase& = Field(xxx) * %2: $AutoEnvWrapperClass& = Allocate($AutoEnvWrapperClass) * %3: Unit = StoreElementRef(%0, %2, 0) * %4: $AutoEnvInstBase& = TypeCast(%2) * %1: xxx = Expression(%4) */ // 1. create $Auto_Env_wrapper auto fieldRetVal = field.GetResult(); auto fieldRetType = fieldRetVal->GetType(); CJC_ASSERT(fieldRetType->IsReferenceTypeWithRefDims(1)); auto fieldRetTypeNoRef = StaticCast(fieldRetType->StripAllRefs()); auto autoEnvWrapperDef = GetOrCreateAutoEnvWrapper(*fieldRetTypeNoRef); // 2. convert return type auto newfieldRetType = builder.GetType(autoEnvWrapperDef->GetSuperClassDef()->GetSuperClassTy()); ConvertTypeFunc convertRetType = [&fieldRetType, &newfieldRetType](const Type& type) { CJC_ASSERT(&type == fieldRetType); return newfieldRetType; }; PrivateTypeConverter converter(convertRetType, builder); converter.VisitValue(*fieldRetVal); // 3. create $Auto_Env_wrapper object auto parentBlock = field.GetParentBlock(); auto autoEnvWrapperType = autoEnvWrapperDef->GetType(); auto autoEnvWrapperRefType = builder.GetType(autoEnvWrapperType); auto allocate = builder.CreateExpression(autoEnvWrapperRefType, autoEnvWrapperType, parentBlock); allocate->MoveAfter(&field); auto memberVar = fieldRetVal; auto storeMemberVar = builder.CreateExpression( builder.GetUnitTy(), memberVar, allocate->GetResult(), std::vector{0}, parentBlock); storeMemberVar->MoveAfter(allocate); // typecast from $Auto_Env_xxx_wrapper to $Auto_Env_InstBase auto typecast = builder.CreateExpression(fieldRetType, allocate->GetResult(), parentBlock); typecast->MoveAfter(storeMemberVar); // 4. replace user ReplaceOperandWithAutoEnvWrapperClass(*fieldRetVal, *typecast->GetResult(), {storeMemberVar}); } void ClosureConversion::WrapTypeCastSrcVal(TypeCast& typecast) { /** convert from: * %0: $AutoEnvGenericBase& = xxx * %1: $AutoEnvInstBase& = TypeCast(%0) * * to: * %0: $AutoEnvGenericBase& = xxx * %2: $AutoEnvWrapperClass& = Allocate($AutoEnvWrapperClass) * %3: Unit = StoreElementRef(%0, %2, 0) * %1: $AutoEnvInstBase& = TypeCast(%3) */ // 1. create $Auto_Env_wrapper auto targetTypeNoRef = StaticCast(typecast.GetTargetTy()->StripAllRefs()); auto autoEnvWrapperDef = GetOrCreateAutoEnvWrapper(*targetTypeNoRef); // 2. create $Auto_Env_wrapper object auto parentBlock = typecast.GetParentBlock(); auto autoEnvWrapperType = autoEnvWrapperDef->GetType(); auto autoEnvWrapperRefType = builder.GetType(autoEnvWrapperType); auto allocate = builder.CreateExpression(autoEnvWrapperRefType, autoEnvWrapperType, parentBlock); allocate->MoveBefore(&typecast); auto srcVal = typecast.GetSourceValue(); auto memberVar = srcVal; auto storeMemberVar = builder.CreateExpression( builder.GetUnitTy(), memberVar, allocate->GetResult(), std::vector{0}, parentBlock); storeMemberVar->MoveAfter(allocate); // 3. replace user typecast.ReplaceOperand(srcVal, allocate->GetResult()); } void ClosureConversion::ModifyTypeMismatchInExpr() { std::vector applyWrapRetVal; std::vector applyWithExceptionWrapRetVal; std::vector invokeWrapRetVal; std::vector invokeWithExceptionWrapRetVal; std::vector getElementRefWrapRetVal; std::vector fieldWrapRetVal; std::vector typecastWrapSrcVal; std::vector>> needTypeCastExprs; auto postVisit = [this, &applyWrapRetVal, &applyWithExceptionWrapRetVal, &invokeWrapRetVal, &invokeWithExceptionWrapRetVal, &getElementRefWrapRetVal, &fieldWrapRetVal, &typecastWrapSrcVal, &needTypeCastExprs](Expression& e) { if (e.GetExprKind() == ExprKind::APPLY) { if (ApplyRetValNeedWrapper(e)) { applyWrapRetVal.emplace_back(StaticCast(&e)); } if (auto res = ApplyArgNeedTypeCast(StaticCast(e)); res.first) { needTypeCastExprs.emplace_back(&e, res.second); } } else if (e.GetExprKind() == ExprKind::APPLY_WITH_EXCEPTION) { if (ApplyWithExceptionRetValNeedWrapper(StaticCast(e))) { applyWithExceptionWrapRetVal.emplace_back(StaticCast(&e)); } if (auto res = ApplyWithExceptionArgNeedTypeCast(StaticCast(e)); res.first) { needTypeCastExprs.emplace_back(&e, res.second); } } else if (InvokeRetValNeedWrapper(e)) { invokeWrapRetVal.emplace_back(&e); } else if (InvokeWithExceptionRetValNeedWrapper(e)) { invokeWithExceptionWrapRetVal.emplace_back(&e); } else if (auto enumRes = EnumConstructorNeedTypeCast(e); enumRes.first) { needTypeCastExprs.emplace_back(&e, enumRes.second); } else if (auto tupleRes = TupleNeedTypeCast(e); tupleRes.first) { needTypeCastExprs.emplace_back(&e, tupleRes.second); } else if (GetElementRefRetValNeedWrapper(e, builder)) { getElementRefWrapRetVal.emplace_back(StaticCast(&e)); } else if (FieldRetValNeedWrapper(e, builder)) { fieldWrapRetVal.emplace_back(StaticCast(&e)); } else if (auto arrRes = RawArrayInitByValueNeedTypeCast(e); arrRes.first) { needTypeCastExprs.emplace_back(&e, arrRes.second); } else if (TypeCastSrcValNeedWrapper(e)) { typecastWrapSrcVal.emplace_back(StaticCast(&e)); } else if (auto spawnRes = SpawnNeedTypeCast(e); spawnRes.first) { needTypeCastExprs.emplace_back(&e, spawnRes.second); } return VisitResult::CONTINUE; }; for (auto func : package.GetGlobalFuncs()) { if (func->TestAttr(Attribute::SKIP_ANALYSIS)) { continue; } Visitor::Visit( *func, [](Expression&) { return VisitResult::CONTINUE; }, postVisit); } for (auto e : applyWrapRetVal) { WrapApplyRetVal(*e); } for (auto e : applyWithExceptionWrapRetVal) { WrapApplyWithExceptionRetVal(*e); } for (auto e : invokeWrapRetVal) { WrapInvokeRetVal(*e); } for (auto e : invokeWithExceptionWrapRetVal) { WrapInvokeWithExceptionRetVal(*e); } for (auto e : getElementRefWrapRetVal) { WrapGetElementRefRetVal(*e); } for (auto e : fieldWrapRetVal) { WrapFieldRetVal(*e); } for (auto e : typecastWrapSrcVal) { WrapTypeCastSrcVal(*e); } for (auto& e : needTypeCastExprs) { AddTypeCastForOperand(e, builder); } } void ClosureConversion::Convert() { ConvertGlobalFunctions(); auto nestedFuncs = CollectNestedFunctions(); InlineLambda(nestedFuncs); nestedFuncs = CollectNestedFunctions(); ConvertNestedFunctions(nestedFuncs); ConvertImportedFunctions(); ConvertExpressions(); LiftType(); ModifyTypeMismatchInExpr(); CreateVTableForAutoEnvDef(); } cangjie_compiler-1.0.7/src/CHIR/Transformation/ConstPropagation.cpp000066400000000000000000000346671510705540100253470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/ConstPropagation.h" #include "cangjie/CHIR/Analysis/Engine.h" #include "cangjie/CHIR/GeneratedFromForIn.h" using namespace Cangjie::CHIR; ConstPropagation::ConstPropagation(CHIRBuilder& builder, ConstAnalysisWrapper* constAnalysisWrapper, const GlobalOptions& options) : builder(builder), analysisWrapper(constAnalysisWrapper), opts{options} { } void ConstPropagation::RunOnPackage(const Ptr& package, bool isDebug, bool isCJLint) { for (auto func : package->GetGlobalFuncs()) { RunOnFunc(func, isDebug, isCJLint); } } void ConstPropagation::RunOnFunc(const Ptr& func, bool isDebug, bool isCJLint) { bool isCommonFunctionWithoutBody = func->TestAttr(Attribute::SKIP_ANALYSIS); if (isCommonFunctionWithoutBody) { return; // Nothing to check } auto result = analysisWrapper->CheckFuncResult(func); CJC_ASSERT(result); std::vector toBeRewrited; const auto actionBeforeVisitExpr = [](const ConstDomain&, Expression*, size_t) {}; const auto actionAfterVisitExpr = [this, &toBeRewrited, func, isDebug, isCJLint]( const ConstDomain& state, Expression* expr, size_t index) { auto exprType = expr->GetResult()->GetType(); if (expr->IsBinaryExpr()) { if (auto absVal = state.CheckAbstractValue(expr->GetResult()); absVal) { return (void)toBeRewrited.emplace_back(expr, index, GenerateConstExpr(exprType, absVal, isCJLint)); } else if (expr->GetResult()->GetType()->IsInteger()) { auto binary = StaticCast(expr); if (auto intTy = StaticCast(expr->GetResult()->GetType()); intTy->IsSigned()) { return TrySimplifyingBinaryExpr(state, binary, isDebug); } else { return TrySimplifyingBinaryExpr(state, binary, isDebug); } } } if (expr->IsUnaryExpr()) { if (auto absVal = state.CheckAbstractValue(expr->GetResult()); absVal) { return (void)toBeRewrited.emplace_back(expr, index, GenerateConstExpr(exprType, absVal, isCJLint)); } else { return TrySimplifyingUnaryExpr(StaticCast(expr), isDebug); } } if ((exprType->IsInteger() || exprType->IsFloat() || exprType->IsRune() || exprType->IsBoolean() || exprType->IsString()) && (expr->IsLoad() || expr->IsTypeCast() || expr->IsField())) { auto absVal = state.CheckAbstractValue(expr->GetResult()); if (absVal) { toBeRewrited.emplace_back(expr, index, GenerateConstExpr(exprType, absVal, isCJLint)); RecordEffectMap(expr, func); } } }; std::unordered_map> targetSuccMap; const auto actionOnTerminator = [this, &targetSuccMap, isCJLint](const ConstDomain& state, Terminator* terminator, std::optional targetSucc) { if (!targetSucc.has_value()) { return; } switch (terminator->GetExprKind()) { case ExprKind::BRANCH: case ExprKind::MULTIBRANCH: return (void)targetSuccMap.emplace(terminator, std::make_pair(nullptr, targetSucc.value())); case ExprKind::INT_OP_WITH_EXCEPTION: case ExprKind::TYPECAST_WITH_EXCEPTION: { auto res = terminator->GetResult(); if (auto absVal = state.CheckAbstractValue(res)) { targetSuccMap.emplace(terminator, std::make_pair(GenerateConstExpr(res->GetType(), absVal, isCJLint), targetSucc.value())); } return; } default: break; } }; result->VisitWith(actionBeforeVisitExpr, actionAfterVisitExpr, actionOnTerminator); for (auto& rewriteInfo : toBeRewrited) { RewriteToConstExpr(rewriteInfo, isDebug); } for (auto& [terminator, v] : targetSuccMap) { RewriteTerminator(terminator, v.first, v.second, isDebug); } if (!targetSuccMap.empty()) { funcsNeedRemoveBlocks.push_back(func.get()); } } const OptEffectCHIRMap& ConstPropagation::GetEffectMap() const { return effectMap; } const std::vector& ConstPropagation::GetFuncsNeedRemoveBlocks() const { return funcsNeedRemoveBlocks; } Ptr ConstPropagation::GenerateConstExpr( const Ptr& type, const Ptr& constVal, bool isCJLint) { switch (constVal->GetConstKind()) { case ConstValue::ConstKind::UINT: return builder.CreateLiteralValue(type, StaticCast(constVal)->GetVal()); case ConstValue::ConstKind::INT: return builder.CreateLiteralValue( type, static_cast(StaticCast(constVal)->GetVal())); case ConstValue::ConstKind::FLOAT: return builder.CreateLiteralValue(type, StaticCast(constVal)->GetVal()); case ConstValue::ConstKind::RUNE: return builder.CreateLiteralValue(type, StaticCast(constVal)->GetVal()); case ConstValue::ConstKind::BOOL: return builder.CreateLiteralValue(type, StaticCast(constVal)->GetVal()); case ConstValue::ConstKind::STRING: { if (isCJLint) { return builder.CreateLiteralValue( type, StaticCast(constVal)->GetVal()); } else { return nullptr; } } default: #ifndef NDEBUG CJC_ABORT(); #else return nullptr; #endif } } using namespace Cangjie; static bool SkipCP(const Expression& expr, const GlobalOptions& opts) { if (!opts.IsOptimizationExisted(GlobalOptions::OptimizationFlag::CONST_PROPAGATION)) { // const propagation disabled, operate only on nodes generated from for-in if (expr.Get()) { return false; } if (auto br = DynamicCast(&expr)) { return br->GetSourceExpr() != SourceExpr::FOR_IN_EXPR; } if (auto st = DynamicCast(&expr)) { if (auto alloc = DynamicCast(st->GetLocation())) { return !alloc->GetExpr()->Get(); } } return true; } return false; } template void ConstPropagation::TrySimplifyingBinaryExpr( const ConstDomain& state, const Ptr& binary, bool isDebug) { if (SkipCP(*binary, opts)) { return; } auto lhs = static_cast(state.CheckAbstractValue(binary->GetLHSOperand())); auto rhs = static_cast(state.CheckAbstractValue(binary->GetRHSOperand())); switch (binary->GetExprKind()) { case ExprKind::ADD: { // `0 + a` => a, `a + 0` => a if (lhs && lhs->GetVal() == 0) { return ReplaceUsageOfExprResult(binary, binary->GetRHSOperand(), isDebug); } else if (rhs && rhs->GetVal() == 0) { return ReplaceUsageOfExprResult(binary, binary->GetLHSOperand(), isDebug); } return; } case ExprKind::LSHIFT: case ExprKind::RSHIFT: case ExprKind::SUB: { // `a << 0` => a, `a >> 0` => a, `a - 0` => a if (rhs && rhs->GetVal() == 0) { return ReplaceUsageOfExprResult(binary, binary->GetLHSOperand(), isDebug); } return; } case ExprKind::MUL: { // `1 * a` => a, `a * 1` => a if (lhs && lhs->GetVal() == 1) { return ReplaceUsageOfExprResult(binary, binary->GetRHSOperand(), isDebug); } else if (rhs && rhs->GetVal() == 1) { return ReplaceUsageOfExprResult(binary, binary->GetLHSOperand(), isDebug); } return; } case ExprKind::DIV: case ExprKind::EXP: { // `a / 1` => a, `a ** 1` => a if (rhs && rhs->GetVal() == 1) { return ReplaceUsageOfExprResult(binary, binary->GetLHSOperand(), isDebug); } return; } case ExprKind::BITAND: case ExprKind::BITOR: { // `a & a` => a, `a | a` => a if (binary->GetLHSOperand() == binary->GetRHSOperand()) { return ReplaceUsageOfExprResult(binary, binary->GetLHSOperand(), isDebug); } return; } default: return; } } void ConstPropagation::RewriteToConstExpr(const RewriteInfo& rewriteInfo, bool isDebug) const { if (!rewriteInfo.literalVal) { return; } auto oldExpr = rewriteInfo.oldExpr; if (SkipCP(*oldExpr, opts)) { return; } auto oldExprResult = oldExpr->GetResult(); auto oldExprParent = oldExpr->GetParentBlock(); auto newExpr = builder.CreateExpression(oldExprResult->GetType(), rewriteInfo.literalVal, oldExprParent); newExpr->SetDebugLocation(oldExpr->GetDebugLocation()); oldExprParent->GetExpressionByIdx(rewriteInfo.index)->ReplaceWith(*newExpr); if (isDebug) { std::string message = "[ConstPropagation] The " + ExprKindMgr::Instance()->GetKindName(static_cast(oldExpr->GetExprKind())) + ToPosInfo(oldExpr->GetDebugLocation(), true) + " has been rewrited to a constant\n"; std::cout << message; } } void ConstPropagation::TrySimplifyingUnaryExpr(const Ptr& unary, bool isDebug) const { if (SkipCP(*unary, opts)) { return; } // NOT: !(!b) = b // BITNOT: !(!x) = x if (unary->GetExprKind() != ExprKind::NOT && unary->GetExprKind() != ExprKind::BITNOT) { return; } // e.g. // %1 : Bool = Not(%0) // %2 : Bool = Not(%1) // any usage to %2 can be replaced by %0 auto operand = unary->GetOperand(); // `operand` is `%1` in the example if (operand->IsLocalVar()) { auto operandExpr = StaticCast(operand)->GetExpr(); // `operandExpr` is `Not(%0)` in the example if (operandExpr->GetExprKind() != ExprKind::NOT && operandExpr->GetExprKind() != ExprKind::BITNOT) { return; } auto targetVal = StaticCast(operandExpr)->GetOperand(); // `%0` in the example ReplaceUsageOfExprResult(unary, targetVal, isDebug); } } void ConstPropagation::ReplaceUsageOfExprResult( const Ptr& expr, const Ptr& newVal, bool isDebug) const { // note: The scope of rewriting should be revised when nested block groups occur. expr->GetResult()->ReplaceWith(*newVal, expr->GetParentBlockGroup()); if (isDebug) { std::string message = "[ConstPropagation] The result of the trivial " + ExprKindMgr::Instance()->GetKindName(static_cast(expr->GetExprKind())) + ToPosInfo(expr->GetDebugLocation()) + " has been optimised\n"; std::cout << message; } } void ConstPropagation::RewriteTerminator( Terminator* oldTerminator, LiteralValue* newValue, Block* newTarget, bool isDebug) const { if (SkipCP(*oldTerminator, opts)) { return; } auto parentBlock = oldTerminator->GetParentBlock(); oldTerminator->RemoveSelfFromBlock(); const auto& loc = oldTerminator->GetDebugLocation(); if (newValue) { auto constant = builder.CreateExpression(loc, newValue->GetType(), newValue, parentBlock); parentBlock->AppendExpression(constant); oldTerminator->GetResult()->ReplaceWith(*constant->GetResult(), constant->GetParentBlockGroup()); } auto newTerminator = builder.CreateTerminator(loc, newTarget, parentBlock); parentBlock->AppendExpression(newTerminator); if (isDebug) { std::string message = "[ConstPropagation] The terminator " + ExprKindMgr::Instance()->GetKindName(static_cast(oldTerminator->GetExprKind())) + ToPosInfo(oldTerminator->GetDebugLocation()) + " has been optimised\n"; std::cout << message; } } static std::mutex g_mtx; OptEffectCHIRMap ConstPropagation::effectMap; void ConstPropagation::RecordEffectMap(const Expression* expr, const Func* func) const { if (!opts.enIncrementalCompilation || !opts.IsOptimizationExisted(GlobalOptions::OptimizationFlag::CONST_PROPAGATION)) { return; } GlobalVar* gv = nullptr; if (expr->GetExprKind() == ExprKind::LOAD) { auto loc = StaticCast(expr)->GetLocation(); if (loc->IsGlobalVarInCurPackage()) { // let a = 3 // Load(gv_a) gv = DynamicCast(loc); } else if (loc->IsLocalVar()) { // let sa = SA(); sa.x // %0 = GetElementRef(gv_sa); %1 = Load(%0) auto locExpr = StaticCast(loc)->GetExpr(); if (locExpr->GetExprKind() == ExprKind::GET_ELEMENT_REF) { auto base = StaticCast(locExpr)->GetLocation(); if (base->IsGlobalVarInCurPackage()) { gv = DynamicCast(base); } } } } else if (expr->GetExprKind() == ExprKind::FIELD) { auto base = StaticCast(expr)->GetBase(); if (base->IsLocalVar()) { auto baseExpr = StaticCast(base)->GetExpr(); if (baseExpr->GetExprKind() == ExprKind::LOAD) { auto loc = StaticCast(baseExpr)->GetLocation(); if (loc->IsGlobalVarInCurPackage()) { // let a = (1, 2); a[0] // %0 = Load(gv_a); %1 = Field(%0, 0) gv = DynamicCast(loc); } } } } if (gv) { if (opts.GetJobs() > 1) { std::lock_guard guard(g_mtx); effectMap[gv].emplace(const_cast(func)); } else { effectMap[gv].emplace(const_cast(func)); } } } cangjie_compiler-1.0.7/src/CHIR/Transformation/DeadCodeElimination.cpp000066400000000000000000001210561510705540100256630ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/DeadCodeElimination.h" #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Visitor/Visitor.h" #include #include using namespace Cangjie::CHIR; DeadCodeElimination::DeadCodeElimination(CHIRBuilder& builder, DiagAdapter& diag, const std::string& packageName) : builder(builder), diag(diag), currentPackageName(packageName) { } namespace { void DumpForDebug(const Ptr expr, const Ptr func, bool isDebug) { if (!isDebug) { return; } std::string message; if (expr != nullptr) { message = "[DCE] The " + expr->GetExprKindName() + " expression" + ToPosInfo(expr->GetDebugLocation()) + " has been deleted\n"; } if (func != nullptr) { message = "[DCE] The " + func->GetIdentifier() + " function of " + func->GetPackageName() + " package" + ToPosInfo(func->GetDebugLocation()) + " has been deleted\n"; } std::cout << message; } bool AllUsersIsExprKind(const std::vector& users, const ExprKind& kind) { return std::all_of(users.begin(), users.end(), [&kind](auto user) { auto res = user->GetExprKind() == kind; auto args = user->GetOperands(); auto it = std::find_if(args.begin(), args.end(), [](auto item) { return item->GetType()->IsNothing(); }); if (res && (it != args.end())) { return true; } return false; }); } bool CheckUsersOfExpr(const Expression& expr) { // If the expression is the arg of apply, we should use the pos of this expression, not pos of apply. // Example code: // var a = 2 // A(1, 2,return A(), a + 2) if (expr.GetResult()) { auto users = expr.GetResult()->GetUsers(); if (AllUsersIsExprKind(users, ExprKind::TUPLE)) { return true; } if (AllUsersIsExprKind(users, ExprKind::APPLY)) { return true; } if (AllUsersIsExprKind(users, ExprKind::RAW_ARRAY_LITERAL_INIT)) { return true; } /* If expr has user of store, but the store has zero position, then DCE use the position of this expr. func foo() { return 1 1 // have user of store } */ if (expr.GetResult()->GetUsers().size() == 1 && // 1 denote that if epxr has one user of store. expr.GetResult()->GetUsers()[0]->GetExprKind() == ExprKind::STORE) { auto storeNode = expr.GetResult()->GetUsers()[0]; auto [res, nodeRange] = ToRangeIfNotZero(storeNode->GetDebugLocation()); if (!res) { return true; } } return false; } return true; } std::string GetFuncIdent(const Func& func) { if (func.GetFuncKind() == FuncKind::GETTER) { return "get"; } else if (func.GetFuncKind() == FuncKind::SETTER) { return "set"; } return func.GetSrcCodeIdentifier(); } bool ShouldSkipUselessFuncElimination(const Package& package, const Cangjie::GlobalOptions& opts) { if (opts.enableCompileDebug || opts.enableCoverage || opts.enIncrementalCompilation || package.GetName() == Cangjie::REFLECT_PACKAGE_NAME) { return true; } return false; } } // namespace static inline const std::unordered_map EXPR_KIND_TO_STR = { {ExprKind::NEG, "-"}, {ExprKind::NOT, "!"}, {ExprKind::BITNOT, "~"}, {ExprKind::ADD, "+"}, {ExprKind::SUB, "-"}, {ExprKind::MUL, "*"}, {ExprKind::DIV, "/"}, {ExprKind::MOD, "%"}, {ExprKind::EXP, "**"}, {ExprKind::LSHIFT, "<<"}, {ExprKind::RSHIFT, ">>"}, {ExprKind::BITAND, "&"}, {ExprKind::BITOR, "|"}, {ExprKind::BITXOR, "^"}, {ExprKind::LT, "<"}, {ExprKind::GT, ">"}, {ExprKind::LE, "<="}, {ExprKind::GE, ">="}, {ExprKind::EQUAL, "=="}, {ExprKind::NOTEQUAL, "!="}, {ExprKind::AND, "&&"}, {ExprKind::OR, "||"}, }; std::string DeadCodeElimination::GetLiteralFromExprKind(const ExprKind& kind) const { auto it = EXPR_KIND_TO_STR.find(kind); CJC_ASSERT(it != EXPR_KIND_TO_STR.end()); return it->second; } void DeadCodeElimination::UselessFuncElimination(Package& package, const GlobalOptions& opts) { if (ShouldSkipUselessFuncElimination(package, opts)) { return; } auto allFuncs = package.GetGlobalFuncs(); std::vector funcsToBeRemoved; do { funcsToBeRemoved.clear(); auto it = allFuncs.begin(); while (it != allFuncs.end()) { if ((*it)->TestAttr(Attribute::IMPORTED)) { ++it; continue; } if (CheckUselessFunc(**it, opts)) { funcsToBeRemoved.emplace_back(*it); DumpForDebug(nullptr, *it, opts.chirDebugOptimizer); it = allFuncs.erase(it); } else { ++it; } } for (auto func : funcsToBeRemoved) { func->DestroySelf(); } } while (!funcsToBeRemoved.empty()); package.SetGlobalFuncs(allFuncs); } void DeadCodeElimination::ReportUnusedCode(const Package& package, const GlobalOptions& opts) { for (auto globalVar : package.GetGlobalVars()) { ReportUnusedGlobalVar(*globalVar); } for (auto func : package.GetGlobalFuncs()) { ReportUnusedFunc(*func, opts); bool isCommonFunctionWithoutBody = func->TestAttr(Attribute::SKIP_ANALYSIS); if (isCommonFunctionWithoutBody) { continue; // Nothing to visit } ReportUnusedCodeInFunc(*func->GetBody(), opts); } } void DeadCodeElimination::TryReportUnusedOnExpr(Expression& expr, const GlobalOptions& opts, bool blockUsed) { ReportUnusedLocalVariable(expr, opts.enableCompileDebug); if (blockUsed) { ReportUnusedExpression(expr); } if (expr.GetExprKind() == ExprKind::LAMBDA) { ReportUnusedCodeInFunc(*StaticCast(expr).GetBody(), opts); } } void DeadCodeElimination::ReportUnusedCodeInFunc(const BlockGroup& body, const GlobalOptions& opts) { for (auto block : body.GetBlocks()) { auto blockUsed = !CheckUselessBlock(*block); for (auto expr : block->GetExpressions()) { TryReportUnusedOnExpr(*expr, opts, blockUsed); } } } void DeadCodeElimination::ReportUnusedFunc(const Func& func, const GlobalOptions& opts) { if (func.Get() == SkipKind::SKIP_DCE_WARNING) { return; } // generic func check will implement in future if (func.TestAttr(Attribute::GENERIC_INSTANTIATED) || func.TestAttr(Attribute::GENERIC)) { return; } if (func.GetParentCustomTypeDef() != nullptr) { if (func.GetParentCustomTypeDef()->TestAttr(Attribute::GENERIC_INSTANTIATED) || func.GetParentCustomTypeDef()->TestAttr(Attribute::GENERIC)) { return; } } // report unreachable function when functiong has nothing parameter // case like: func foo(a : Nothing){0} for (auto param : func.GetParams()) { if (param->GetType()->IsNothing()) { auto expr = param->GetDebugExpr(); if (!expr) { continue; } auto [res, nodeRange] = GetDebugPos(*expr); if (res && !IsCrossPackage(nodeRange.begin, currentPackageName, diag)) { diag.DiagnoseRefactor(DiagKindRefactor::chir_dce_unreachable_function, nodeRange); } continue; } } if (auto funcKind = func.GetFuncKind(); funcKind == FuncKind::DEFAULT_PARAMETER_FUNC || funcKind == FuncKind::FINALIZER) { return; } // check unused function if (!func.TestAttr(Attribute::COMPILER_ADD) && CheckUselessFunc(func, opts)) { auto ident = GetFuncIdent(func); auto debugPos = GetDebugPos(func); if (ident == "main") { DiagUnusedCode(debugPos, DiagKindRefactor::chir_dce_unused_function_main); } else { DiagUnusedCode(debugPos, DiagKindRefactor::chir_dce_unused_function, ident); } } } static bool IsExternalDecl(const Value& v) { using namespace Cangjie; if (v.Get() == Linkage::EXTERNAL) { return true; } // const var/func never have external linkage if (!v.TestAttr(Attribute::PRIVATE) && v.TestAttr(Attribute::CONST)) { if (auto gv = DynamicCast(&v)) { return !gv->IsLocalConst(); } return true; } return false; } void DeadCodeElimination::ReportUnusedGlobalVar(const GlobalVar& globalVar) { if (IsExternalDecl(globalVar)) { return; } if (globalVar.Get() == SkipKind::SKIP_DCE_WARNING) { return; } auto gvUsers = globalVar.GetUsers(); // 1、unused const may have 0 or 1 user. // 2、unused 'let' global variable may have 0 or 1 user. // 3、unused 'var' global variable has one user and this user must be store if (globalVar.TestAttr(Attribute::CONST) || globalVar.TestAttr(Attribute::READONLY)) { if (gvUsers.empty()) { DiagUnusedCode( GetDebugPos(globalVar), DiagKindRefactor::chir_dce_unused_variable, globalVar.GetSrcCodeIdentifier()); } } if (gvUsers.size() == 1 && gvUsers[0]->GetExprKind() == ExprKind::STORE) { DiagUnusedCode( GetDebugPos(globalVar), DiagKindRefactor::chir_dce_unused_variable, globalVar.GetSrcCodeIdentifier()); return; } } void DeadCodeElimination::DiagUnusedVariableForParam(const Debug& expr) { auto operand = expr.GetOperand(0); auto users = operand->GetUsers(); // "_" param should not check if (operand->GetSrcCodeIdentifier() == "_") { return; } // unused parameter just has 1 user: debug if (CheckOneUsers(users)) { DiagUnusedVariable(expr); } } void DeadCodeElimination::DiagUnusedLambdaVariable(const Debug& expr) { auto closureExpr = StaticCast(expr.GetOperand(0)); if (closureExpr->GetExpr()->GetExprKind() == ExprKind::TUPLE) { auto realVar = StaticCast(closureExpr->GetExpr()->GetOperand(1)); if (realVar->GetExpr()->GetExprKind() == ExprKind::TYPECAST) { auto typecastVar = StaticCast(realVar->GetExpr()->GetOperand(0)); auto users = typecastVar->GetUsers(); if (users.back() == realVar->GetExpr()) { DiagUnusedVariable(expr); } } else { auto users = realVar->GetUsers(); if (users.back() == closureExpr->GetExpr()) { DiagUnusedVariable(expr); } } } } void DeadCodeElimination::DiagUnusedVariableForLocalVar(const Debug& expr, bool isDebug) { auto operand = expr.GetOperand(0); auto users = operand->GetUsers(); // unused 'var' variable have 2 situtation // when it has init value, 'var' has 2 users: debug, store // when it does not has init value, 'var' has 1 users: debug if (!expr.GetResult()->TestAttr(Attribute::READONLY)) { if (CheckOneUsers(users) || CheckTwoUsers(users) || (StaticCast(operand)->GetDebugExpr() == &expr && CheckAllUsersIsNotUse(*operand, users))) { DiagUnusedVariable(expr); return; } } else { if (isDebug && CheckTwoUsers(users)) { // when use debug option, 'let' variable have 2 users if they are unused:debug, store DiagUnusedVariable(expr); } else { if (StaticCast(operand)->GetDebugExpr() == &expr && CheckAllUsersIsNotUse(*operand, users)) { // when not use debug option, 'let' variable have 1 users if they are unused:debug, or two 1 debug and // 1 store if it is let variable and has an initializer DiagUnusedVariable(expr); } } } } void DeadCodeElimination::DiagUnusedVariable(const Debug& expr) { auto nodeRange = GetDebugPos(expr); if (nodeRange.first && !IsCrossPackage(nodeRange.second.begin, currentPackageName, diag)) { diag.DiagnoseRefactor( DiagKindRefactor::chir_dce_unused_variable, nodeRange.second, expr.GetSrcCodeIdentifier()); } } void DeadCodeElimination::ReportUnusedLocalVariable(const Expression& expr, bool isDebug) { auto kind = expr.GetExprKind(); if (kind != ExprKind::DEBUGEXPR) { return; } auto& debugExpr = *StaticCast(&expr); auto operand = debugExpr.GetOperand(0); if (operand->Get() == SkipKind::SKIP_DCE_WARNING || debugExpr.Get() == SkipKind::SKIP_DCE_WARNING) { return; } // Can not tell if a Varray variable is unused by the number of users. // for example code: // let arr9: VArray = [0, 1, 2, 3, 4, 5] // let size: Int64 = arr9.size // The corresponding chir is: // %1: VArray = VArray(0, 1, 2, 3, 4, 5) // [readOnly] %2: Unit = Debug(%1, arr9) // %3: Int64 = Constant(6) // [readOnly] %4: Unit = Debug(%3, size) if (operand->GetType()->IsVArray()) { return; } auto users = operand->GetUsers(); if (operand->IsParameter()) { DiagUnusedVariableForParam(debugExpr); } else if (operand->IsLocalVar()) { DiagUnusedVariableForLocalVar(debugExpr, isDebug); } } bool DeadCodeElimination::CheckOneUsers(const std::vector& users) const { // 1 denote that has one user return users.size() == 1 && users[0]->GetExprKind() == ExprKind::DEBUGEXPR; } bool DeadCodeElimination::CheckTwoUsers(const std::vector& users) const { // 2 denote that has two user return users.size() == 2 && users[0]->GetExprKind() == ExprKind::DEBUGEXPR && users[1]->GetExprKind() == ExprKind::STORE; } bool DeadCodeElimination::CheckAllUsersIsNotUse(const Value& value, const std::vector& users) { return std::all_of(users.begin(), users.end(), [&value](auto user) { if (user->GetExprKind() == ExprKind::LOAD) { return user->GetResult()->GetUsers().empty(); } // store is not a use; load is. // not including storeelementref here because that may lead to a large scale of testcases adjustment if (Store* store = DynamicCast(user)) { if (auto var = DynamicCast(store->GetLocation())) { // while store to function return value is a real usage if (var->IsRetValue()) { return false; } } // the stored value is used, only the location is not used by Store return store->GetValue() != &value; } return user->GetExprKind() == ExprKind::DEBUGEXPR; }); } void DeadCodeElimination::ReportUnusedExpression(Expression& expr) { if (CheckUselessExpr(expr, true)) { // Some special expression has no users, but should not print warning. // for example cangjie code: // let x = match (true) { --------------->`true` has no users, but shoud not print warning. // case _ : (Bool,Int64) => 0 // } if (expr.Get() == SkipKind::SKIP_DCE_WARNING) { return; } if (expr.GetResult()->Get() == SkipKind::SKIP_DCE_WARNING) { return; } auto res = GetDebugPos(expr); if (res.first) { if (IsCrossPackage(res.second.begin, currentPackageName, diag)) { return; } if (expr.GetExprKind() == ExprKind::LAMBDA) { DiagUnusedCode(res, DiagKindRefactor::chir_dce_unused_function, StaticCast(expr).GetSrcCodeIdentifier()); } else if (expr.IsUnaryExpr() || expr.IsBinaryExpr() || expr.IsIntOpWithException()) { auto kind = expr.GetExprKind() == ExprKind::INT_OP_WITH_EXCEPTION ? GetLiteralFromExprKind(StaticCast(&expr)->GetOpKind()) : GetLiteralFromExprKind(expr.GetExprKind()); diag.DiagnoseRefactor(DiagKindRefactor::chir_dce_unused_operator, res.second, kind); } else { diag.DiagnoseRefactor(DiagKindRefactor::chir_dce_unused_expression, res.second); } } } } void DeadCodeElimination::UselessExprElimination(const Package& package, bool isDebug) const { for (auto func : package.GetGlobalFuncs()) { UselessExprEliminationForFunc(*func, isDebug); } } void DeadCodeElimination::UselessExprEliminationForFunc(const Func& func, bool isDebug) const { std::queue worklist; std::unordered_set worklistSet; for (auto block : func.GetBody()->GetBlocks()) { for (auto expr : block->GetExpressions()) { if (CheckUselessExpr(*expr)) { worklist.push(expr); worklistSet.emplace(expr); } } } while (!worklist.empty()) { auto expr = worklist.front(); worklist.pop(); worklistSet.erase(expr); auto ops = expr->GetOperands(); DumpForDebug(expr, nullptr, isDebug); expr->RemoveSelfFromBlock(); for (auto op : ops) { if (op->IsLocalVar()) { auto opExpr = StaticCast(op)->GetExpr(); if (CheckUselessExpr(*opExpr) && worklistSet.find(opExpr) == worklistSet.end()) { worklist.push(opExpr); worklistSet.emplace(opExpr); } } } } } void DeadCodeElimination::NothingTypeExprElimination(const Package& package, bool isDebug) { for (auto func : package.GetGlobalFuncs()) { bool isCommonFunctionWithoutBody = func->TestAttr(Attribute::SKIP_ANALYSIS); if (isCommonFunctionWithoutBody) { continue; // Nothing to visit } NothingTypeExprEliminationForFunc(*func->GetBody(), isDebug); } } namespace { void CreateNullReturnValue(CHIRBuilder& builder, Block* parent, const BlockGroup& funcBody) { if (auto retVal = GetReturnValue(funcBody)) { auto retValTy = GetFuncType(funcBody)->GetReturnType(); auto null = builder.CreateConstantExpression(retValTy, parent); parent->AppendExpression(null); auto store = builder.CreateExpression(builder.GetUnitTy(), null->GetResult(), retVal, parent); parent->AppendExpression(store); } } // Return true if there exists a parameter of Nothing type; false otherwise. bool HandleFuncWithNothingParamter(CHIRBuilder& builder, const BlockGroup& funcBody) { const auto& params = GetFuncParams(funcBody); for (auto param : params) { if (!param->GetType()->IsNothing()) { continue; } // If the type of parameter is Nothing, this function can never be called. // However, we can not delete the function declaration and the function body directly. // We choose to modify the function body to exit the function directly. // The return value will be set to constant null if the return type is not Void. // e.g. // func @foo(..., %1: Nothing, ...) : Int64 { // [ret] %0 = Allocate(Int64) // %1: Int64 = Constant(Null) // Store(%1, %0) // Exit() // } auto entry = funcBody.GetEntryBlock(); for (auto block : funcBody.GetBlocks()) { if (block != entry) { block->RemoveSelfFromBlockGroup(); } } for (auto expr : entry->GetExpressions()) { if (auto debug = Cangjie::DynamicCast(expr); debug && debug->GetValue()->IsParameter()) { continue; } if (auto res = expr->GetResult(); res && res == GetReturnValue(funcBody)) { continue; } expr->RemoveSelfFromBlock(); } CreateNullReturnValue(builder, entry, funcBody); entry->AppendExpression(builder.CreateTerminator(entry)); if (auto func = param->GetTopLevelFunc()) { func->Set(SkipKind::SKIP_VIC); } return true; } return false; } template void HandleNothingTerminator(CHIRBuilder& builder, TApply* expr, BlockGroup& funcBody) { CJC_ASSERT(Cangjie::Utils::In(expr->GetExprKind(), {ExprKind::APPLY_WITH_EXCEPTION, ExprKind::INVOKE_WITH_EXCEPTION, ExprKind::INVOKESTATIC_WITH_EXCEPTION})); auto newSuccessBlock = builder.CreateBlock(&funcBody); CreateNullReturnValue(builder, newSuccessBlock, funcBody); newSuccessBlock->AppendExpression(builder.CreateTerminator(newSuccessBlock)); auto oldSuccessBlock = expr->GetSuccessBlock(); expr->ReplaceSuccessor(*oldSuccessBlock, *newSuccessBlock); } } // namespace void DeadCodeElimination::NothingTypeExprEliminationForFunc(BlockGroup& funcBody, bool isDebug) { if (HandleFuncWithNothingParamter(builder, funcBody)) { return; } for (auto block : funcBody.GetBlocks()) { bool isNothingIr = false; for (auto expr : block->GetExpressions()) { if (expr->GetExprKind() == ExprKind::LAMBDA) { NothingTypeExprEliminationForFunc(*StaticCast(expr)->GetBody(), isDebug); } if (!isNothingIr && expr->GetResult() && expr->GetResultType()->IsNothing() && !expr->IsConstantNull()) { if (!expr->IsTerminator()) { isNothingIr = true; continue; } else { switch (expr->GetExprKind()) { case ExprKind::APPLY_WITH_EXCEPTION: HandleNothingTerminator(builder, StaticCast(expr), funcBody); break; case ExprKind::INVOKE_WITH_EXCEPTION: HandleNothingTerminator(builder, StaticCast(expr), funcBody); break; case ExprKind::INVOKESTATIC_WITH_EXCEPTION: HandleNothingTerminator(builder, StaticCast(expr), funcBody); break; default: #ifndef NDEBUG CJC_ABORT(); #else return; #endif } } } if (isNothingIr) { expr->RemoveSelfFromBlock(); DumpForDebug(expr, nullptr, isDebug); } } if (isNothingIr) { CreateNullReturnValue(builder, block, funcBody); block->AppendExpression(builder.CreateTerminator(block)); } } } void DeadCodeElimination::UnreachableBlockElimination(const Package& package, bool isDebug) const { for (auto func : package.GetGlobalFuncs()) { bool isCommonFunctionWithoutBody = func->TestAttr(Attribute::SKIP_ANALYSIS); if (isCommonFunctionWithoutBody) { continue; // Nothing to visit } UnreachableBlockEliminationForFunc(*func->GetBody(), isDebug); } } void DeadCodeElimination::UnreachableBlockElimination(const std::vector& funcs, bool isDebug) const { for (auto& func : funcs) { bool isCommonFunctionWithoutBody = func->TestAttr(Attribute::SKIP_ANALYSIS); if (isCommonFunctionWithoutBody) { continue; // Nothing to visit } UnreachableBlockEliminationForFunc(*func->GetBody(), isDebug); } } void DeadCodeElimination::UnreachableBlockWarningReporter(const Package& package, size_t threadsNum, const std::unordered_map& maybeUnreachableBlocks) { if (threadsNum == 1) { UnreachableBlockWarningReporterInSerial(package, maybeUnreachableBlocks); } else { UnreachableBlockWarningReporterInParallel(package, threadsNum, maybeUnreachableBlocks); } } void DeadCodeElimination::UnreachableBlockWarningReporterInSerial( const Package& package, const std::unordered_map& maybeUnreachableBlocks) { for (auto func : package.GetGlobalFuncs()) { bool isPrinted = false; Visitor::Visit(*func, [this, &isPrinted, &maybeUnreachableBlocks](Block& block) { auto it = maybeUnreachableBlocks.find(&block); if (it != maybeUnreachableBlocks.end()) { auto terminator = it->second; if (CheckUselessBlock(block)) { PrintUnreachableBlockWarning(block, *terminator, isPrinted); } } return VisitResult::CONTINUE; }); } } void DeadCodeElimination::UnreachableBlockWarningReporterInParallel(const Package& package, size_t threadsNum, const std::unordered_map& maybeUnreachableBlocks) { Utils::TaskQueue taskQueue(threadsNum); for (auto func : package.GetGlobalFuncs()) { bool isCommonFunctionWithoutBody = func->TestAttr(Attribute::SKIP_ANALYSIS); if (isCommonFunctionWithoutBody) { continue; // Nothing to visit } taskQueue.AddTask([this, func, &maybeUnreachableBlocks]() { bool isPrinted = false; Visitor::Visit(*func, [this, &isPrinted, &maybeUnreachableBlocks](Block& block) { auto it = maybeUnreachableBlocks.find(&block); if (it != maybeUnreachableBlocks.end()) { auto terminator = it->second; if (CheckUselessBlock(block)) { PrintUnreachableBlockWarning(block, *terminator, isPrinted); } } return VisitResult::CONTINUE; }); }); } taskQueue.RunAndWaitForAllTasksCompleted(); } namespace { std::optional GetBranchTargetSucc(const Branch& branch) { if (!branch.GetCondition()->IsLocalVar()) { return std::nullopt; } auto condExpr = Cangjie::StaticCast(branch.GetCondition())->GetExpr(); if (condExpr->GetExprKind() != ExprKind::CONSTANT) { return std::nullopt; } return Cangjie::StaticCast(condExpr)->GetBoolLitVal() ? branch.GetTrueBlock() : branch.GetFalseBlock(); } std::optional GetMultiBranchTargetSucc(const MultiBranch& branch) { if (!branch.GetCondition()->IsLocalVar()) { return std::nullopt; } auto condExpr = Cangjie::StaticCast(branch.GetCondition())->GetExpr(); if (condExpr->GetExprKind() == ExprKind::TYPECAST) { if (!condExpr->GetOperand(0)->IsLocalVar()) { return std::nullopt; } condExpr = Cangjie::StaticCast(condExpr->GetOperand(0))->GetExpr(); } if (condExpr->GetExprKind() != ExprKind::CONSTANT) { return std::nullopt; } auto constant = Cangjie::StaticCast(condExpr); uint64_t condVal = 0; if (constant->IsBoolLit()) { condVal = static_cast(constant->GetBoolLitVal()); } else if (constant->IsIntLit()) { condVal = constant->GetUnsignedIntLitVal(); } else if (constant->IsRuneLit()) { condVal = static_cast(constant->GetRuneLitVal()); } else { Cangjie::InternalError("Unexpected const val kind"); } auto cases = branch.GetCaseVals(); for (size_t i = 0; i < cases.size(); ++i) { if (condVal == cases[i]) { return branch.GetSuccessor(i + 1); } } return branch.GetDefaultBlock(); } } // namespace void DeadCodeElimination::UnreachableBlockEliminationForFunc(const BlockGroup& body, bool isDebug) const { auto blocks = body.GetBlocks(); std::unordered_set isUnreachable(blocks.begin(), blocks.end()); std::queue workList; auto entryBlock = body.GetEntryBlock(); workList.push(entryBlock); isUnreachable.erase(entryBlock); while (!workList.empty()) { auto block = workList.front(); workList.pop(); for (auto expr : block->GetExpressions()) { if (expr->GetExprKind() == ExprKind::LAMBDA) { UnreachableBlockEliminationForFunc(*StaticCast(expr)->GetBody(), isDebug); } } auto terminator = block->GetTerminator(); CJC_NULLPTR_CHECK(terminator); std::optional targetSucc = std::nullopt; if (terminator->GetExprKind() == ExprKind::BRANCH) { targetSucc = GetBranchTargetSucc(*StaticCast(terminator)); } else if (terminator->GetExprKind() == ExprKind::MULTIBRANCH) { targetSucc = GetMultiBranchTargetSucc(*StaticCast(terminator)); } if (targetSucc.has_value()) { auto cond = terminator->GetOperand(0); terminator->RemoveSelfFromBlock(); if (cond->IsLocalVar() && cond->GetUsers().empty()) { auto expr = StaticCast(cond)->GetExpr(); expr->RemoveSelfFromBlock(); } DumpForDebug(terminator, nullptr, isDebug); block->AppendExpression(builder.CreateTerminator(targetSucc.value(), block)); } auto succs = block->GetSuccessors(); for (auto succ : succs) { if (isUnreachable.find(succ) != isUnreachable.end()) { isUnreachable.erase(succ); workList.push(succ); } } } for (auto block : isUnreachable) { block->RemoveSelfFromBlockGroup(); } } void DeadCodeElimination::BreakBranchConnection(const Block& block) const { for (auto pred : block.GetPredecessors()) { auto predTerminator = pred->GetTerminator(); CJC_ASSERT(predTerminator); if (predTerminator->GetExprKind() == ExprKind::BRANCH) { auto oprand = predTerminator->GetOperand(0); auto branch = StaticCast(predTerminator); CJC_ASSERT(&block == branch->GetFalseBlock()); auto target = branch->GetTrueBlock(); branch->RemoveSelfFromBlock(); if (oprand->GetUsers().empty()) { auto expr = StaticCast(oprand)->GetExpr(); expr->RemoveSelfFromBlock(); } pred->AppendExpression(builder.CreateTerminator(target, pred)); } else { auto multi = StaticCast(predTerminator); CJC_ASSERT(&block == multi->GetDefaultBlock()); multi->ReplaceSuccessor(0, *multi->GetSuccessor(1)); } } } void DeadCodeElimination::ClearUnreachableMarkBlock(const Package& package) const { for (auto func : package.GetGlobalFuncs()) { bool isCommonFunctionWithoutBody = func->TestAttr(Attribute::SKIP_ANALYSIS); if (isCommonFunctionWithoutBody) { continue; // Nothing to visit } ClearUnreachableMarkBlockForFunc(*func->GetBody()); } } void DeadCodeElimination::ClearUnreachableMarkBlockForFunc(const BlockGroup& body) const { for (auto block : body.GetBlocks()) { if (!block->TestAttr(Attribute::UNREACHABLE)) { for (auto expr : block->GetExpressions()) { if (expr->GetExprKind() == ExprKind::LAMBDA) { ClearUnreachableMarkBlockForFunc(*StaticCast(expr)->GetBody()); } } continue; } BreakBranchConnection(*block); block->RemoveSelfFromBlockGroup(); } } bool DeadCodeElimination::CheckUselessFunc(const Func& func, const GlobalOptions& opts) { if (!func.GetUsers().empty()) { return false; } if (func.GetIdentifierWithoutPrefix() == USER_MAIN_MANGLED_NAME) { return false; } // All func names contains "*_global_init*" is global variables initial function. if (func.GetIdentifier().find(GLOBAL_INIT_MANGLED_NAME) != std::string::npos) { return false; } if (func.GetIdentifier().find(STD_CORE_FUTURE_MANGLED_NAME) != std::string::npos) { // All func names contains "*_CNat6Future*" is Future related series functions. return false; } if (func.GetIdentifier().find(ANNOTATION_VAR_POSTFIX) != std::string::npos) { // annotation var init functions are to be evaluated are removed by const eval return false; } if (func.GetFuncKind() == Cangjie::CHIR::MAIN_ENTRY && !opts.interpreter) { // if output is lib (static or shared), Main method should delete // when use chir-interpreter execute cjc-frontend ('cjc-frontend --chir-interpreter ...'), we should not // delete main method return opts.outputMode == GlobalOptions::OutputMode::STATIC_LIB || opts.outputMode == GlobalOptions::OutputMode::SHARED_LIB; } // Do not check the functions that can be exported. if (IsExternalDecl(func)) { return false; } // The func is in vtable. if (func.IsVirtualFunc()) { return false; } // should be revised if (func.GetFuncKind() == Cangjie::CHIR::CLASS_CONSTRUCTOR) { return false; } // The Finalizer func of a class, can not be removed. if (func.GetFuncKind() == Cangjie::CHIR::FINALIZER) { return false; } if (!opts.disableReflection) { // enable reflect return !func.TestAttr(Attribute::PUBLIC) || func.TestAttr(Attribute::NO_REFLECT_INFO); } return true; } // Check whether the block is unreachable. bool DeadCodeElimination::CheckUselessBlock(const Block& block) const { return !block.IsEntry() && block.GetPredecessors().empty() && !block.TestAttr(Attribute::UNREACHABLE); } Ptr DeadCodeElimination::GetUnreachableExpression(const CHIR::Block& block, bool& isNormal) const { auto expressions = block.GetExpressions(); Ptr resExpression = nullptr; auto it = std::find_if(expressions.begin(), expressions.end(), [&isNormal, &resExpression, this](auto expression) { auto posForWarning = expression->template Get(); auto [res, warningRange] = ToRangeIfNotZero(posForWarning); if (res) { // when report deadcode in BinaryExpr, it has 4 situations in tests/LLT/CHIR/DCE/testUnusedOp03.cj // if operand in binaryExpr is nothing, use warninglocation as report position // if all operand in binaryExpr is normal, use binaryExpr location as report position if (expression->IsBinaryExpr() || expression->IsIntOpWithException()) { auto args = expression->GetOperands(); auto it = std::find_if(args.begin(), args.end(), [](auto item) { return item->GetType()->IsNothing(); }); if (it == args.end()) { resExpression = expression; isNormal = true; return true; } } if (expression->GetExprKind() != ExprKind::DEBUGEXPR) { // variable debug node has warn position, but we want get it's normal position. resExpression = expression; isNormal = false; return true; } } if (!CheckUsersOfExpr(*expression)) { return false; } auto& debugInfo = expression->GetDebugLocation(); auto [newResult, debugRange] = ToRangeIfNotZero(debugInfo); if (newResult && !IsCrossPackage(debugRange.begin, currentPackageName, diag)) { resExpression = expression; isNormal = true; return true; } return false; }); if (it != expressions.end()) { return resExpression; } return nullptr; } void DeadCodeElimination::PrintUnreachableBlockWarning( const CHIR::Block& block, const CHIR::Terminator& terminator, bool& isPrinted) { // Get position of nothing type node. auto& debugInfo = terminator.GetDebugLocation(); auto [res, terminalNodeRange] = ToRangeIfNotZero(debugInfo); if (res && !isPrinted) { auto expressions = block.GetExpressions(); bool isNormal = true; if (auto unreableExpr = GetUnreachableExpression(block, isNormal)) { auto range = isNormal ? ToRange(unreableExpr->GetDebugLocation()) : ToRange(unreableExpr->Get()); // Need to remove after find the cause. if (terminalNodeRange.begin == range.begin) { return; } if (IsCrossPackage(terminalNodeRange.begin, currentPackageName, diag)) { return; } if (unreableExpr->Get() == SkipKind::SKIP_DCE_WARNING) { return; } // Note: For the code:`if (let a <- 1) {return 1}` // the position of the Unit node is the position of the ifExpr,so it will generate a wrong // warnging,we should skip this scenario. if (unreableExpr->GetExprKind() == ExprKind::CONSTANT && StaticCast(unreableExpr)->IsUnitLit()) { if (unreableExpr->GetResult()->TestAttr(Attribute::COMPILER_ADD)) { return; } } if (!isNormal && (unreableExpr->IsBinaryExpr() || unreableExpr->IsUnaryExpr())) { auto diagBuilder = diag.DiagnoseRefactor(DiagKindRefactor::chir_dce_unreachable, range, "operator"); diagBuilder.AddMainHintArguments("operator"); diagBuilder.AddHint(terminalNodeRange); } else if (!isNormal && unreableExpr->GetExprKind() == ExprKind::APPLY) { auto diagBuilder = diag.DiagnoseRefactor(DiagKindRefactor::chir_dce_unreachable, range, "call"); diagBuilder.AddMainHintArguments("call"); diagBuilder.AddHint(terminalNodeRange); } else { auto diagBuilder = diag.DiagnoseRefactor(DiagKindRefactor::chir_dce_unreachable_expression_hint, range); diagBuilder.AddHint(terminalNodeRange); } isPrinted = true; } } } bool DeadCodeElimination::CheckUselessExpr(const Expression& expr, bool isReportWarning) const { if (expr.GetResult() && !expr.GetResult()->GetUsers().empty()) { return false; } if (expr.GetExprKind() == ExprKind::INT_OP_WITH_EXCEPTION && expr.Get() && isReportWarning) { return true; } if (expr.IsTerminator()) { return false; } if (expr.Get()) { return true; } auto kind = expr.GetExprKind(); switch (expr.GetExprMajorKind()) { case ExprMajorKind::UNARY_EXPR: { // Theoretically, this condition can be revised to `unary->GetOverflowStrategy() != // OverflowStrategy::THROWING;` However, we are not sure if there are some redundant THROWING attribute. auto unary = StaticCast(&expr); return kind != ExprKind::NEG || unary->GetOverflowStrategy() != OverflowStrategy::THROWING; } case ExprMajorKind::BINARY_EXPR: { if (kind == ExprKind::MOD || kind == ExprKind::DIV || kind == ExprKind::LSHIFT || kind == ExprKind::RSHIFT) { // MOD/DIV may throw div_by_zero exception; LSHIFT/RSHIFT may throw overshift exception. return false; } else if (kind >= ExprKind::ADD && kind <= ExprKind::EXP) { // all the other arithemetic operations and bitwise shift operations. auto binary = StaticCast(&expr); return binary->GetOverflowStrategy() != OverflowStrategy::THROWING; } else { return true; } } case ExprMajorKind::MEMORY_EXPR: { return kind == ExprKind::LOAD || kind == ExprKind::GET_ELEMENT_REF; } case ExprMajorKind::STRUCTURED_CTRL_FLOW_EXPR: { return kind == ExprKind::LAMBDA; } case ExprMajorKind::OTHERS: { return kind == ExprKind::CONSTANT || kind == ExprKind::TUPLE || kind == ExprKind::FIELD || kind == ExprKind::VARRAY; } default: { CJC_ABORT(); return false; } } } template void DeadCodeElimination::DiagUnusedCode( const std::pair& nodeRange, DiagKindRefactor diagKind, Args&&... args) { if (nodeRange.first && !IsCrossPackage(nodeRange.second.begin, currentPackageName, diag)) { diag.DiagnoseRefactor(diagKind, nodeRange.second, args...); } } cangjie_compiler-1.0.7/src/CHIR/Transformation/Devirtualization.cpp000066400000000000000000000612551510705540100254030ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/Devirtualization.h" #include "cangjie/CHIR/Transformation/DeadCodeElimination.h" #include "cangjie/CHIR/Analysis/DevirtualizationInfo.h" #include "cangjie/CHIR/Analysis/Engine.h" #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/UserDefinedType.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Transformation/BlockGroupCopyHelper.h" #include "cangjie/Mangle/CHIRManglingUtils.h" namespace Cangjie::CHIR { Devirtualization::Devirtualization( TypeAnalysisWrapper* typeAnalysisWrapper, DevirtualizationInfo& devirtFuncInfo) : analysisWrapper(typeAnalysisWrapper), devirtFuncInfo(devirtFuncInfo) { } void Devirtualization::RunOnFuncs(const std::vector& funcs, CHIRBuilder& builder, bool isDebug) { rewriteInfos.clear(); for (auto func : funcs) { RunOnFunc(func, builder); } RewriteToApply(builder, rewriteInfos, isDebug); InstantiateFuncIfPossible(builder, rewriteInfos); } void Devirtualization::RunOnFunc(const Func* func, CHIRBuilder& builder) { auto result = analysisWrapper->CheckFuncResult(func); if (result == nullptr && frozenStates.count(func) != 0) { result = frozenStates.at(func).get(); } CJC_ASSERT(result); const auto actionBeforeVisitExpr = [this, &builder](const TypeDomain& state, Expression* expr, size_t) { if (expr->GetExprKind() != ExprKind::INVOKE) { return; } auto invoke = StaticCast(expr); auto object = invoke->GetObject(); auto invokeAbsObject = state.CheckAbstractObjectRefBy(object); // Obtains the state information of the invoke operation object. auto resVal = state.CheckAbstractValue(invokeAbsObject); if (!resVal) { return; } std::vector paramTys; for (auto param : invoke->GetOperands()) { paramTys.emplace_back(param->GetType()); } // Grab the function from the classMap. auto [realCallee, thisType] = FindRealCallee(builder, resVal, {invoke->GetMethodName(), std::move(paramTys), invoke->GetInstantiatedTypeArgs()}); if (!realCallee) { return; } rewriteInfos.emplace_back(RewriteInfo{invoke, realCallee, thisType, invoke->GetInstantiatedTypeArgs()}); }; const auto actionAfterVisitExpr = [](const TypeDomain&, Expression*, size_t) {}; const auto actionOnTerminator = [](const TypeDomain&, Terminator*, std::optional) {}; result->VisitWith(actionBeforeVisitExpr, actionAfterVisitExpr, actionOnTerminator); } namespace { struct BuiltinOpInfo { BuiltinOpInfo(FuncInfo info, ExprKind exprKind, size_t operandsNum) : funcInfo(std::move(info)), targetExprKind(exprKind), operandsNum(operandsNum) {} FuncInfo funcInfo; ExprKind targetExprKind{ExprKind::INVALID}; size_t operandsNum{0}; }; static const std::vector COMPARABLE_FUNC_LISTS = { {FuncInfo(">", NOT_CARE, {NOT_CARE}, ANY_TYPE, "std.core"), ExprKind::GT, 2U}, {FuncInfo("<", NOT_CARE, {NOT_CARE}, ANY_TYPE, "std.core"), ExprKind::LT, 2U}, {FuncInfo(">=", NOT_CARE, {NOT_CARE}, ANY_TYPE, "std.core"), ExprKind::GE, 2U}, {FuncInfo("<=", NOT_CARE, {NOT_CARE}, ANY_TYPE, "std.core"), ExprKind::LE, 2U}, {FuncInfo("==", NOT_CARE, {NOT_CARE}, ANY_TYPE, "std.core"), ExprKind::EQUAL, 2U}, {FuncInfo("!=", NOT_CARE, {NOT_CARE}, ANY_TYPE, "std.core"), ExprKind::NOTEQUAL, 2U}, {FuncInfo("next", NOT_CARE, {NOT_CARE}, ANY_TYPE, "std.core"), ExprKind::APPLY, 2U} }; Ptr BuiltinOpCreateNewApply(CHIRBuilder& builder, const Invoke& oriInvoke, Ptr func, const Ptr& thisValue, const std::vector& args) { auto instRetTy = oriInvoke.GetResultType(); std::vector applyArgs{thisValue}; applyArgs.insert(applyArgs.end(), args.begin(), args.end()); auto thisType = builder.GetType(thisValue->GetType()); return builder.CreateExpression(instRetTy, func, FuncCallContext{ .args = applyArgs, .thisType = thisType}, oriInvoke.GetParentBlock()); } Ptr BuiltinOpCreateNewBinary(CHIRBuilder& builder, const Invoke& oriInvoke, ExprKind kind, const Ptr& thisValue, const std::vector& args) { CJC_ASSERT(args.size() == 1U); auto instRetTy = oriInvoke.GetResultType(); auto parent = oriInvoke.GetParentBlock(); return builder.CreateExpression( oriInvoke.GetDebugLocation(), instRetTy, kind, thisValue, args[0], parent); } } bool Devirtualization::RewriteToBuiltinOp(CHIRBuilder& builder, const RewriteInfo& info, bool isDebug) { auto invoke = info.invoke; auto func = info.realCallee->Get() ? info.realCallee->Get() : info.realCallee; ExprKind targetExprKind{ExprKind::INVALID}; size_t operandsNum = 0; for (auto& it : COMPARABLE_FUNC_LISTS) { if (IsExpectedFunction(*func, it.funcInfo)) { targetExprKind = it.targetExprKind; operandsNum = it.operandsNum; } } auto args = invoke->GetArgs(); args.erase(args.begin()); // remove `this` if (targetExprKind == ExprKind::INVALID || args.size() != operandsNum - 1U) { return false; } for (auto& arg : args) { if (!arg->GetType()->IsPrimitive()) { return false; } } auto thisValue = invoke->GetObject(); std::vector castExprs; std::function findThisValue = [&thisValue, &castExprs, &findThisValue](LocalVar& tempVar) { auto expr = tempVar.GetExpr(); if (expr->GetExprKind() == ExprKind::BOX || expr->GetExprKind() == ExprKind::TYPECAST) { castExprs.emplace_back(expr); thisValue = expr->GetOperand(0); if (auto localVar = DynamicCast(thisValue)) { findThisValue(*localVar); } } }; if (thisValue->IsLocalVar() && !thisValue->GetType()->IsPrimitive()) { findThisValue(*StaticCast(thisValue)); } if (!thisValue->GetType()->IsPrimitive()) { return false; } Ptr op; if (targetExprKind == ExprKind::APPLY) { op = BuiltinOpCreateNewApply(builder, *invoke, func, thisValue, args); } else { op = BuiltinOpCreateNewBinary(builder, *invoke, targetExprKind, thisValue, args); } invoke->ReplaceWith(*op); for (auto e : castExprs) { if (e->GetResult()->GetUsers().empty()) { e->RemoveSelfFromBlock(); } } if (isDebug) { std::string callName = targetExprKind == ExprKind::APPLY ? func->GetSrcCodeIdentifier() : op->GetExprKindName(); std::string message = "[Devirtualization] The function call to " + invoke->GetMethodName() + ToPosInfo(invoke->GetDebugLocation()) + " was optimized to builtin op " + callName + "."; std::cout << message << std::endl; } return true; } void Devirtualization::RewriteToApply(CHIRBuilder& builder, std::vector& rewriteInfos, bool isDebug) { for (auto rewriteInfo = rewriteInfos.rbegin(); rewriteInfo != rewriteInfos.rend(); ++rewriteInfo) { if (RewriteToBuiltinOp(builder, *rewriteInfo, isDebug)) { continue; } auto invoke = rewriteInfo->invoke; auto parent = invoke->GetParentBlock(); // get this type from rewrite info Type* thisType = builder.GetType(rewriteInfo->thisType); auto& realFunc = rewriteInfo->realCallee; if ((rewriteInfo->thisType->IsStruct() || rewriteInfo->thisType->IsEnum()) && rewriteInfo->realCallee->Get()) { thisType = rewriteInfo->thisType; bool isMutFunc = realFunc->TestAttr(Attribute::MUT) || IsConstructor(*realFunc) || IsInstanceVarInit(*realFunc); if (isMutFunc) { thisType = builder.GetType(thisType); } realFunc = rewriteInfo->realCallee->Get(); } auto args = invoke->GetOperands(); auto instThisType = thisType; if (rewriteInfo->thisType->IsBuiltinType()) { instThisType = builder.GetType(builder.GetAnyTy()); } auto instRetTy = invoke->GetResultType(); auto typecastRes = TypeCastOrBoxIfNeeded(*args[0], *instThisType, builder, *parent, INVALID_LOCATION); if (typecastRes != args[0]) { StaticCast(typecastRes)->GetExpr()->MoveBefore(invoke); args[0] = typecastRes; } auto loc = invoke->GetDebugLocation(); auto apply = builder.CreateExpression(loc, instRetTy, realFunc, FuncCallContext{ .args = args, .instTypeArgs = rewriteInfo->typeArgs, .thisType = thisType}, invoke->GetParentBlock()); rewriteInfo->newApply = apply; invoke->ReplaceWith(*apply); invoke->GetResult()->ReplaceWith(*apply->GetResult(), parent->GetParentBlockGroup()); if (isDebug) { std::string message = "[Devirtualization] The function call to " + invoke->GetMethodName() + ToPosInfo(invoke->GetDebugLocation()) + " was optimized."; std::cout << message << std::endl; } } } const std::vector& Devirtualization::GetFrozenInstFuns() const { return frozenInstFuns; } void Devirtualization::AppendFrozenFuncState(const Func* func, std::unique_ptr> analysisRes) { frozenStates.emplace(func, std::move(analysisRes)); } static std::string CreateInstFuncMangleName(const std::string& oriIdentifer, const Apply& apply, CHIRBuilder& builder) { // 1. get type args std::vector genericTypes; auto func = VirtualCast(apply.GetCallee()); if (auto customDef = func->GetParentCustomTypeDef(); customDef != nullptr && customDef->IsGenericDef()) { auto funcInCustomType = apply.GetInstParentCustomTyOfCallee(builder); while (funcInCustomType->IsRef()) { funcInCustomType = StaticCast(funcInCustomType)->GetBaseType(); } genericTypes = funcInCustomType->GetTypeArgs(); } auto funcArgs = apply.GetInstantiatedTypeArgs(); if (!funcArgs.empty()) { genericTypes.insert(genericTypes.end(), funcArgs.begin(), funcArgs.end()); } // 2. get mangle return CHIRMangling::GenerateInstantiateFuncMangleName(oriIdentifer, genericTypes); } void Devirtualization::InstantiateFuncIfPossible(CHIRBuilder& builder, std::vector& rewriteInfoList) { for (auto rewriteInfo = rewriteInfoList.rbegin(); rewriteInfo != rewriteInfoList.rend(); ++rewriteInfo) { auto callee = DynamicCast(rewriteInfo->realCallee); if (callee == nullptr || !callee->IsInGenericContext() || callee->Get() != nullptr) { continue; } auto apply = rewriteInfo->newApply; std::vector parameterType; for (auto param : apply->GetArgs()) { parameterType.emplace_back(param->GetType()); } auto retType = apply->GetResultType(); auto instFuncType = builder.GetType(parameterType, retType); if (instFuncType->IsGenericRelated()) { continue; } // 2. create new inst func if needed auto newId = CreateInstFuncMangleName(callee->GetIdentifierWithoutPrefix(), *apply, builder); Func* newFunc; if (frozenInstFuncMap.count(newId) != 0) { newFunc = frozenInstFuncMap.at(newId); } else { newFunc = builder.CreateFunc(callee->GetDebugLocation(), instFuncType, newId, callee->GetSrcCodeIdentifier(), callee->GetRawMangledName(), callee->GetPackageName()); newFunc->AppendAttributeInfo(callee->GetAttributeInfo()); newFunc->DisableAttr(Attribute::GENERIC); if (!apply->GetInstantiatedTypeArgs().empty()) { newFunc->EnableAttr(Attribute::GENERIC_INSTANTIATED); } newFunc->Set(Linkage::INTERNAL); auto oriBlockGroup = callee->GetBody(); BlockGroupCopyHelper helper(builder); helper.GetInstMapFromApply(*apply); auto [newGroup, newBlockGroupRetValue] = helper.CloneBlockGroup(*oriBlockGroup, *newFunc); newFunc->InitBody(*newGroup); newFunc->SetReturnValue(*newBlockGroupRetValue); std::vector args; CJC_ASSERT(parameterType.size() == callee->GetParams().size()); std::unordered_map paramMap; for (size_t i = 0; i < parameterType.size(); i++) { auto arg = builder.CreateParameter(parameterType[i], callee->GetParam(i)->GetDebugLocation(), *newFunc); args.push_back(arg); paramMap.emplace(callee->GetParam(i), arg); } helper.SubstituteValue(newGroup, paramMap); FixCastProblemAfterInst(newGroup, builder); newFunc->SetReturnValue(*newBlockGroupRetValue); frozenInstFuns.push_back(newFunc); frozenInstFuncMap[newId] = newFunc; } // replace apply callee with new inst func auto applyParent = apply->GetParentBlock(); auto loc = apply->GetDebugLocation(); auto instApply = builder.CreateExpression( loc, retType, newFunc, FuncCallContext{.args = apply->GetArgs()}, applyParent); apply->ReplaceWith(*instApply); } } namespace { void BuildOrphanTypeReplaceTable( const Cangjie::CHIR::Type* mayBeGeneric, std::unordered_map& replaceTable) { if (auto genericType = Cangjie::DynamicCast(mayBeGeneric); genericType && genericType->orphanFlag) { CJC_ASSERT(genericType->GetUpperBounds().size() == 1); replaceTable.emplace(genericType, genericType->GetUpperBounds()[0]); } else { auto genericTypeArgs = mayBeGeneric->GetTypeArgs(); for (size_t i = 0; i < genericTypeArgs.size(); ++i) { BuildOrphanTypeReplaceTable(genericTypeArgs[i], replaceTable); } } } FuncBase* FindFunctionInVtable(const ClassType* parentTy, const std::vector& infos, const Devirtualization::FuncSig& method, CHIRBuilder& builder) { std::unordered_map parentReplaceTable; auto paramTypes = method.types; paramTypes.erase(paramTypes.begin()); if (!parentTy->GetTypeArgs().empty()) { auto instParentTypeArgs = parentTy->GetTypeArgs(); auto genericParentTypeArgs = parentTy->GetCustomTypeDef()->GetGenericTypeParams(); for (size_t i = 0; i < genericParentTypeArgs.size(); ++i) { parentReplaceTable.emplace(genericParentTypeArgs[i], instParentTypeArgs[i]); } } for (auto& info : infos) { if (info.srcCodeIdentifier != method.name) { continue; } auto sigParamTys = info.typeInfo.sigType->GetParamTypes(); if (sigParamTys.size() != paramTypes.size()) { continue; } if (info.typeInfo.methodGenericTypeParams.size() != method.typeArgs.size()) { continue; } bool isSigSame = true; std::unordered_map freeGenericReplaceTable; for (size_t i = 0; i < sigParamTys.size(); ++i) { BuildOrphanTypeReplaceTable(sigParamTys[i], freeGenericReplaceTable); BuildOrphanTypeReplaceTable(paramTypes[i], freeGenericReplaceTable); } auto& methodGenerics = info.typeInfo.methodGenericTypeParams; for (size_t i{0}; i < methodGenerics.size(); ++i) { freeGenericReplaceTable.emplace(methodGenerics[i], method.typeArgs[i]); } for (size_t i = 0; i < paramTypes.size(); ++i) { if (isSigSame) { auto lhs = ReplaceRawGenericArgType(*sigParamTys[i], freeGenericReplaceTable, builder); auto rhs = ReplaceRawGenericArgType(*paramTypes[i], parentReplaceTable, builder); rhs = ReplaceRawGenericArgType(*rhs, freeGenericReplaceTable, builder); isSigSame = lhs == rhs; } else { break; } } if (!isSigSame) { continue; } return info.instance; } return nullptr; } } // namespace bool Devirtualization::IsInstantiationOf( CHIRBuilder& builder, const GenericType* generic, const Type* instantiated) const { if (generic->GetUpperBounds().empty()) { return true; } std::unordered_set possibleParentTys; for (auto def : devirtFuncInfo.defsMap[instantiated]) { for (auto parentTy : def->GetSuperTypesInCurDef()) { auto inheritLists = parentTy->GetSuperTypesRecusively(builder); possibleParentTys.insert(inheritLists.begin(), inheritLists.end()); } } for (auto upperBound : generic->GetUpperBounds()) { if (possibleParentTys.find(upperBound) == possibleParentTys.end()) { return false; } } return true; } bool Devirtualization::IsValidSubType(CHIRBuilder& builder, const Type* expected, Type* specific, std::unordered_map& replaceTable) const { if (expected->GetTypeKind() != specific->GetTypeKind() && !expected->IsGeneric()) { return false; } if (expected->IsGeneric()) { auto generic = Cangjie::StaticCast(expected); if (IsInstantiationOf(builder, generic, specific)) { replaceTable.emplace(generic, specific); return true; } return false; } if (expected->IsNominal()) { const CustomType* specificCustomTy = Cangjie::StaticCast(specific); if (!specificCustomTy->IsEqualOrSubTypeOf(*expected, builder)) { return false; } } auto argsOfExpected = expected->GetTypeArgs(); auto argsOfSpecific = specific->GetTypeArgs(); if (argsOfExpected.size() != argsOfSpecific.size()) { return false; } for (size_t i = 0; i < argsOfExpected.size(); ++i) { if (!IsValidSubType( builder, argsOfExpected[i]->StripAllRefs(), argsOfSpecific[i]->StripAllRefs(), replaceTable)) { return false; } } return true; } std::pair Devirtualization::FindRealCallee( CHIRBuilder& builder, const TypeValue* typeState, const FuncSig& method) const { auto typeStateKind = typeState->GetTypeKind(); auto specificType = typeState->GetSpecificType(); if (!specificType->IsClass() || typeStateKind == DevirtualTyKind::EXACTLY) { std::vector extendsOrImplements{}; if (auto customType = DynamicCast(specificType)) { extendsOrImplements = devirtFuncInfo.defsMap[customType->GetCustomTypeDef()->GetType()]; } else { extendsOrImplements = devirtFuncInfo.defsMap[specificType]; } FuncBase* target = nullptr; for (auto def : extendsOrImplements) { auto [typeMatched, replaceTable] = def->GetType()->CalculateGenericTyMapping(*specificType); if (!typeMatched) { continue; } auto funcType = builder.GetType(method.types, builder.GetUnitTy()); FuncCallType funcCallType{method.name, funcType, method.typeArgs}; auto res = def->GetFuncIndexInVTable(funcCallType, false, replaceTable, builder); if (!res.empty() && res[0].instance != nullptr) { target = res[0].instance; break; } } CJC_NULLPTR_CHECK(target); return {target, specificType}; } else { // The specific type is an interface or a class, and the state kind is SUBCLASS_OF. ClassType* specificType1 = StaticCast(typeState->GetSpecificType()); std::pair res{nullptr, nullptr}; CollectCandidates(builder, specificType1, res, method); return res; } } static bool IsOpenClass(const ClassDef& def) { if (def.IsInterface() || def.IsAbstract()) { return true; } return def.TestAttr(Attribute::VIRTUAL); } FuncBase* Devirtualization::GetCandidateFromSpecificType( CHIRBuilder& builder, ClassType& specific, const FuncSig& method) const { auto specificDef = specific.GetClassDef(); if (specificDef->IsAbstract() || specificDef->IsInterface()) { return nullptr; } auto customType = specific.GetClassDef()->GetType(); auto extendsOrImplements = devirtFuncInfo.defsMap[customType]; for (auto oriDef : extendsOrImplements) { auto genericDef = oriDef->GetGenericDecl() != nullptr ? oriDef->GetGenericDecl() : oriDef; for (auto [parentTy, infos] : genericDef->GetVTable()) { if (auto target = FindFunctionInVtable(parentTy, infos, method, builder)) { return target; } } } return nullptr; } void Devirtualization::CollectCandidates( CHIRBuilder& builder, ClassType* specific, std::pair& res, const FuncSig& method) const { auto specificDef = specific->GetClassDef(); if (IsOpenClass(*specificDef) && !devirtFuncInfo.CheckCustomTypeInternal(*specificDef)) { // skip open classes with external linkage return; } // 1. Get candidate from this type auto targetFromSpecificType = GetCandidateFromSpecificType(builder, *specific, method); if (targetFromSpecificType != nullptr) { res = {targetFromSpecificType, specific}; } if (!IsOpenClass(*specificDef)) { // non-open class do not need try its subtype return; } auto& subtypeMap = devirtFuncInfo.GetSubtypeMap(); auto it = subtypeMap.find(specificDef); if (it == subtypeMap.end()) { // return if has no subtype return; } // 2. Get candidate from subtypes for (auto& inheritInfo : it->second) { auto expected = inheritInfo.parentInstType; std::unordered_map replaceTable; if (!IsValidSubType(builder, expected, specific, replaceTable)) { continue; } auto subtype = ReplaceRawGenericArgType(*(inheritInfo.subInstType), replaceTable, builder); auto subtypeClass = DynamicCast(subtype); if (!subtypeClass || (!subtypeClass->GetClassDef()->IsInterface() && !subtypeClass->GetClassDef()->TestAttr(Attribute::ABSTRACT))) { auto extendsOrImplements = devirtFuncInfo.defsMap[subtypeClass]; for (auto oriDef : extendsOrImplements) { auto def = oriDef->GetGenericDecl() != nullptr ? oriDef->GetGenericDecl() : oriDef; for (auto [parentTy, infos] : def->GetVTable()) { if (!expected->IsEqualOrSubTypeOf(*parentTy->StripAllRefs(), builder)) { continue; } if (auto target = FindFunctionInVtable(parentTy, infos, method, builder)) { if (res.first == nullptr) { res = {target, subtypeClass}; } else if (res.first != target) { res = {nullptr, nullptr}; return; } } } } } if (subtypeClass) { CollectCandidates(builder, subtypeClass, res, method); } } } bool Devirtualization::CheckFuncHasInvoke(const BlockGroup& bg) { std::vector blocks = bg.GetBlocks(); for (auto bb : blocks) { auto exprs = bb->GetNonTerminatorExpressions(); for (size_t i = 0; i < exprs.size(); ++i) { if (exprs[i]->GetExprKind() == ExprKind::LAMBDA) { if (CheckFuncHasInvoke(*StaticCast(exprs[i])->GetBody())) { return true; } } if (exprs[i]->GetExprKind() == ExprKind::INVOKE) { return true; } } } return false; } std::vector Devirtualization::CollectContainInvokeExprFuncs(const Ptr& package) { std::vector funcs; // Collect functions that contain the invoke statement. for (auto func : package->GetGlobalFuncs()) { if (CheckFuncHasInvoke(*func->GetBody())) { funcs.emplace_back(func); } } return funcs; } } cangjie_compiler-1.0.7/src/CHIR/Transformation/FlatForInExpr.cpp000066400000000000000000000160261510705540100245250ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/FlatForInExpr.h" #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/GeneratedFromForIn.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Visitor/Visitor.h" #include #include using namespace Cangjie::CHIR; FlatForInExpr::FlatForInExpr(CHIRBuilder& builder) : builder(builder) { } void FlatForInExpr::RunOnPackage(const Package& package) { for (auto& func : package.GetGlobalFuncs()) { RunOnFunc(*func); } } namespace { using namespace Cangjie; void MoveBlocksToParentBG(const ForIn& expr) { auto bg = expr.GetParentBlockGroup(); CJC_NULLPTR_CHECK(bg); for (auto bg1 : expr.GetExecutionOrder()) { for (auto bl : bg1->GetBlocks()) { bl->MoveTo(*bg); bl->SetParentBlockGroup(bg); } } } void ReplaceExitWithGoto(CHIRBuilder& builder, const BlockGroup& src, Block& target) { for (auto bl : src.GetBlocks()) { auto term = bl->GetTerminator(); CJC_NULLPTR_CHECK(term); if (term->GetExprKind() == CHIR::ExprKind::EXIT) { auto oldAnnotations = term->MoveAnnotation(); term->RemoveSelfFromBlock(); auto gotoCond = builder.CreateTerminator(&target, bl); gotoCond->SetAnnotation(std::move(oldAnnotations)); bl->AppendExpression(gotoCond); } } } Block* GetEntryBlock(const ForIn& expr) { return (*expr.GetExecutionOrder().begin())->GetEntryBlock(); } using ExprIt = std::vector::iterator; /// Move expressions in this block after the ForIn to a new block. The new block will serve as the common block to jump /// to after the ForIn is executed in CFG. Block* MoveExpressionAfterForIn(CHIRBuilder& builder, ExprIt it, ExprIt end) { ++it; auto& expr = **it; auto parentBlock = expr.GetParentBlock(); // create a new block to collect expressions after this ForInExpr before the terminator auto newSuc = builder.CreateBlock(parentBlock->GetParentBlockGroup()); for (auto mv = it; mv != end; ++mv) { // move all expression except the last (the goto) to the new block (*mv)->MoveTo(*newSuc); } return newSuc; } } void FlatForInExpr::FlatternForInExpr(ExprIt it, ExprIt end) { auto& expr = StaticCast(**it); if (Is(&expr)) { return FlatternForInClosedRange(it, end); } auto parentBlock = expr.GetParentBlock(); auto forInSuc = MoveExpressionAfterForIn(builder, it, end); auto bg = expr.GetParentBlockGroup(); auto jumpBlock = builder.CreateBlock(bg); auto loadCondVar = builder.CreateExpression(builder.GetBoolTy(), expr.GetLoopCondVar(), jumpBlock); auto jumpBr = builder.CreateTerminator( expr.GetDebugLocation(), loadCondVar->GetResult(), expr.GetBody()->GetEntryBlock(), forInSuc, jumpBlock); jumpBr->SetSourceExpr(SourceExpr::FOR_IN_EXPR); jumpBlock->AppendExpression(loadCondVar); jumpBlock->AppendExpression(jumpBr); if (Is(expr)) { ReplaceExitWithGoto(builder, *expr.GetCond(), *jumpBlock); ReplaceExitWithGoto(builder, *expr.GetBody(), *expr.GetLatch()->GetEntryBlock()); ReplaceExitWithGoto(builder, *expr.GetLatch(), *expr.GetCond()->GetEntryBlock()); } else { CJC_ASSERT(Is(expr)); ReplaceExitWithGoto(builder, *expr.GetLatch(), *expr.GetCond()->GetEntryBlock()); ReplaceExitWithGoto(builder, *expr.GetCond(), *expr.GetBody()->GetEntryBlock()); ReplaceExitWithGoto(builder, *expr.GetBody(), *jumpBlock); } auto parentGotoCond = builder.CreateTerminator(GetEntryBlock(expr), parentBlock); parentBlock->AppendExpression(parentGotoCond); MoveBlocksToParentBG(expr); expr.RemoveSelfFromBlock(); } /* #parentBlock: var iter = a var cond = true let outerRangeCheck = LE(%a, %b) Branch(%outerRangeCheck, #body, #endBlock) #body: let i = Load(iter) if guard { body } GoTo(#cond) #cond: // cond is true here cond = i != b // != instead of <= is where the optimisation goes GoTo(#latchEntryBlock) #latchEntryBlock: let condValue = Load(cond) Branch(%condValue, #latch, #oldSuc) #latch: iter = i + 1 GoTo(#body) #oldSuc: let forinResult = () */ void FlatForInExpr::FlatternForInClosedRange(ExprIt it, ExprIt end) { auto& expr = StaticCast(**it); auto parentBlock = expr.GetParentBlock(); auto forInSuc = MoveExpressionAfterForIn(builder, it, end); auto gotoForin = builder.CreateTerminator(GetEntryBlock(expr), parentBlock); CJC_ASSERT(!parentBlock->GetTerminator()); parentBlock->AppendExpression(gotoForin); ReplaceExitWithGoto(builder, *expr.GetBody(), *expr.GetCond()->GetEntryBlock()); auto bg = expr.GetParentBlockGroup(); auto latchEntryBlock = builder.CreateBlock(bg); latchEntryBlock->Set(true); ReplaceExitWithGoto(builder, *expr.GetCond(), *latchEntryBlock); auto condValue = builder.CreateExpression( expr.GetDebugLocation(), builder.GetBoolTy(), expr.GetLoopCondVar(), latchEntryBlock); latchEntryBlock->AppendExpression(condValue); auto contLoopBr = builder.CreateTerminator( expr.GetDebugLocation(), condValue->GetResult(), expr.GetLatch()->GetEntryBlock(), forInSuc, latchEntryBlock); latchEntryBlock->AppendExpression(contLoopBr); ReplaceExitWithGoto(builder, *expr.GetLatch(), *expr.GetBody()->GetEntryBlock()); MoveBlocksToParentBG(expr); expr.RemoveSelfFromBlock(); } void FlatForInExpr::RunOnBlockGroup(BlockGroup& blockGroup) { for (auto block : blockGroup.GetBlocks()) { auto exprs = block->GetExpressions(); for (auto exprIt = exprs.begin(); exprIt != exprs.end(); ++exprIt) { auto expr = *exprIt; if (auto lambdaExpr = DynamicCast(expr); lambdaExpr) { RunOnBlockGroup(*lambdaExpr->GetBody()); continue; } if (!Is(expr)) { continue; } auto forInExpr = StaticCast(expr); RunOnBlockGroup(*forInExpr->GetBody()); RunOnBlockGroup(*forInExpr->GetLatch()); // there may be complex expressions in latch if the latch block // is a next() call and the call is inlined FlatternForInExpr(exprIt, exprs.end()); break; } } } void FlatForInExpr::RunOnFunc(Func& func) { bool isCommonFunctionWithoutBody = func.TestAttr(Attribute::COMMON) && !func.GetBody(); if (isCommonFunctionWithoutBody) { return; // Nothing to visit } if (func.TestAttr(Attribute::SKIP_ANALYSIS)) { return; } return RunOnBlockGroup(*func.GetBody()); } cangjie_compiler-1.0.7/src/CHIR/Transformation/FunctionInline.cpp000066400000000000000000000444641510705540100247750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements function inline feature */ #include "cangjie/CHIR/Transformation/FunctionInline.h" #include #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/PrivateTypeConverter.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Visitor/Visitor.h" #include "cangjie/CHIR/Transformation/BlockGroupCopyHelper.h" using namespace Cangjie::CHIR; // Set the threshold value of inline constexpr static size_t INIT_INLINE_THRESHOLD = 20; // Set the threshold value of inlined call in one function to avoid code expandsion constexpr static size_t INLINED_COUNT_THRESHOLD = 20; // This is for efficiency and avoid overflowing on `size`, we will // terminate the counting if the size is already 3 times of the threshold constexpr static size_t SEARCH_THRESHOLD = INIT_INLINE_THRESHOLD * 3; // Increase the threshold value by 20% constexpr static size_t INCREASE_THRESHOLD = 5; // Step when the callee is with lambda parameter constexpr static size_t INCREASE_WHEN_CALLEE_WITH_LAMBDA_ARG = 2; // Set the threshold of CHIR in one block for inline constexpr static size_t INLINED_BLOCKSIZE_THRESHOLD = 10000; static const std::vector functionInlineWhiteList = { FuncInfo("get", "Array", {NOT_CARE}, ANY_TYPE, "std.core"), FuncInfo("set", "Array", {NOT_CARE}, ANY_TYPE, "std.core"), FuncInfo("[]", "Array", {NOT_CARE}, ANY_TYPE, "std.core"), FuncInfo("copyTo", "Array", {NOT_CARE}, ANY_TYPE, "std.core"), FuncInfo("utf8Size", "", {NOT_CARE}, ANY_TYPE, "std.core"), FuncInfo("[]", "String", {NOT_CARE}, ANY_TYPE, "std.core"), FuncInfo("init", "ArrayList", {NOT_CARE}, ANY_TYPE, "std.collection"), FuncInfo("get", "ArrayList", {NOT_CARE}, ANY_TYPE, "std.collection"), FuncInfo("set", "ArrayList", {NOT_CARE}, ANY_TYPE, "std.collection"), FuncInfo("append", "ArrayList", {NOT_CARE}, ANY_TYPE, "std.collection"), FuncInfo("[]", "ArrayList", {NOT_CARE}, ANY_TYPE, "std.collection"), FuncInfo("checkRange", "ArrayList", {NOT_CARE}, ANY_TYPE, "std.collection"), FuncInfo("remove", "ArrayList", {NOT_CARE}, ANY_TYPE, "std.collection"), FuncInfo("==", "HashSet", {NOT_CARE}, ANY_TYPE, "std.collection"), FuncInfo("!=", "HashSet", {NOT_CARE}, ANY_TYPE, "std.collection"), }; static const std::vector functionInlineBlackList = { // Do not inline the `Future::init` for the conveninence of redundant future removal optimization FuncInfo("init", "Future", {NOT_CARE}, ANY_TYPE, "std.core"), // Do not inline the `arrayInitByFunction` for the conveninence of array lambda optimization FuncInfo("arrayInitByFunction", "", {NOT_CARE}, NOT_CARE, "std.core"), // Temporary disable the inline of `callNativeFunc` due to a weird bug in Windows FuncInfo("callNativeFunc", NOT_CARE, {NOT_CARE}, NOT_CARE, "std.fs"), // inline wrappingShl in for loop can result in signficant performance hit FuncInfo("wrappingShl", NOT_CARE, {NOT_CARE}, ANY_TYPE, "std.overflow"), FuncInfo("wrappingShr", NOT_CARE, {NOT_CARE}, ANY_TYPE, "std.overflow"), }; void FunctionInline::InlineImpl(BlockGroup& bg) { auto postVisit = [this](Expression& e) { if (e.GetExprKind() == ExprKind::LAMBDA) { auto lambda = StaticCast(&e); InlineImpl(*lambda->GetBody()); } if (e.GetExprKind() != ExprKind::APPLY) { return VisitResult::CONTINUE; } auto& apply = StaticCast(e); if (CheckCanRewrite(apply)) { DoFunctionInline(apply, optName); } return VisitResult::CONTINUE; }; Visitor::Visit(bg, [](Expression&) { return VisitResult::CONTINUE; }, postVisit); } void FunctionInline::Run(Func& func) { globalFunc = &func; InlineImpl(*func.GetBody()); } const OptEffectCHIRMap& FunctionInline::GetEffectMap() const { return effectMap; } static bool InBlackList(const Func& func) { if (func.GetFuncKind() == FuncKind::MACRO_FUNC || func.GetFuncKind() == FuncKind::GLOBALVAR_INIT || func.GetFuncKind() == FuncKind::MAIN_ENTRY) { return true; } if (func.GetFuncType()->IsCFunc()) { return true; } if (func.TestAttr(Attribute::NO_INLINE)) { return true; } for (auto element : functionInlineBlackList) { if (IsExpectedFunction(func, element)) { return true; } } return false; } static bool InWhiteList(const Func& func) { for (auto element : functionInlineWhiteList) { if (IsExpectedFunction(func, element)) { return true; } } return false; } static bool OnlyCalledOnce(const Func& func) { bool alreadyHasUser = false; for (auto user : func.GetUsers()) { if (user->GetExprKind() == ExprKind::APPLY || user->GetExprKind() == ExprKind::APPLY_WITH_EXCEPTION) { if (alreadyHasUser) { return false; } alreadyHasUser = true; } } return true; } static bool IsHotSpotCall(const Func& callee) { // Case A: the callee is operator overloading function like `[]`, `+`, `-` if (callee.TestAttr(Attribute::OPERATOR)) { return true; } // Case B: the call site is located in a loop // note: currently unsupported return false; } static bool FunctionWithLambdaArg(const Func& func) { for (auto arg : func.GetParams()) { if (arg->GetType()->IsFunc()) { return true; } } return false; } static size_t CalculateThreshold(const Value& callee, const Cangjie::GlobalOptions::OptimizationLevel& optLevel) { size_t realThreshold = INIT_INLINE_THRESHOLD; auto func = Cangjie::DynamicCast(&callee); CJC_NULLPTR_CHECK(func); if (OnlyCalledOnce(*func)) { // Increase the threshold value by 20% realThreshold += realThreshold / INCREASE_THRESHOLD; } if (optLevel < Cangjie::GlobalOptions::OptimizationLevel::Os) { if (IsHotSpotCall(*func)) { // Increase the threshold value by 20% realThreshold = INIT_INLINE_THRESHOLD + INIT_INLINE_THRESHOLD / INCREASE_THRESHOLD; } if (FunctionWithLambdaArg(*func)) { realThreshold = INIT_INLINE_THRESHOLD * INCREASE_WHEN_CALLEE_WITH_LAMBDA_ARG; } } return realThreshold; } static size_t GetExprSize(const Expression& expr) { size_t exprSize = 0; if (expr.GetExprKind() != ExprKind::LAMBDA) { return ++exprSize; } auto postVisit = [&exprSize](Expression& e) { exprSize += GetExprSize(e); return VisitResult::CONTINUE; }; Visitor::Visit(*Cangjie::StaticCast(expr).GetBody(), postVisit); return exprSize; } static size_t CountFuncSize(const Func& func) { size_t funcSize = 0; for (auto block : func.GetBody()->GetBlocks()) { for (auto e : block->GetExpressions()) { funcSize += GetExprSize(*e); if (funcSize >= SEARCH_THRESHOLD) { break; } } } return funcSize; } bool FunctionInline::CheckCanRewrite(const Apply& apply) { auto callee = apply.GetCallee(); // imported func decl, intrinsic func decl, foreign func decl are excluded if (!callee->IsFuncWithBody()) { return false; } auto func = VirtualCast(callee); CJC_NULLPTR_CHECK(callee); // when the terminator of this block is RaiseException, do not inline this apply because it rarely happens if (auto block = apply.GetParentBlock(); Is(block->GetTerminator())) { return false; } // Omit the function inline in block that exceed the Blocksize // threshold to avoid the huge time consume. auto block = apply.GetParentBlock(); if (block->GetExpressions().size() >= INLINED_BLOCKSIZE_THRESHOLD) { return false; } // recursive function doesn't need to inline // if you really want to inline it, yes, you can, it won't cause a problem if (callee == globalFunc) { return false; } if (InBlackList(*func)) { return false; } if (InWhiteList(*func)) { return true; } if (func->GetFuncKind() == FuncKind::INSTANCEVAR_INIT) { return true; } // Determine if we can inline by checking the size of callee exceed the threshold if (inlinedCountMap[globalFunc] >= INLINED_COUNT_THRESHOLD) { return false; } size_t realThreshold = CalculateThreshold(*callee, optLevel); // `res` is std::pair, so // `res.second == true` means `callee` is emplaced successfully, then we must set correct function size // `res.second == false` means `callee` has already been emplaced before, we can use its size directly auto res = funcSizeMap.emplace(func, 0); if (res.second) { res.first->second = CountFuncSize(*func); } if (res.first->second <= realThreshold) { inlinedCountMap[globalFunc]++; return true; } return false; } static std::vector GetExitBlocks(const BlockGroup& blockGroup) { std::vector exitBlocks; for (auto block : blockGroup.GetBlocks()) { auto term = block->GetTerminator(); CJC_NULLPTR_CHECK(term); if (term->GetExprKind() == ExprKind::EXIT) { // Need refactor exitBlocks.emplace_back(block); } } // there may be not exit block, all block's terminator is `Raise` return exitBlocks; } static void ReplaceFuncArgs(std::vector& src, std::vector& dst, const BlockGroup& scope) { CJC_ASSERT(src.size() == dst.size()); for (size_t i = 0; i < src.size(); ++i) { src[i]->ReplaceWith(*dst[i], &scope); } } void FunctionInline::ReplaceFuncResult(LocalVar* resNew, LocalVar* resOld) { auto valType = resNew->GetType(); CJC_ASSERT(valType->IsRef()); auto users = resOld->GetUsers(); // Don't need to handle it when the result of apply is not used. if (users.size() == 0) { return; } valType = Cangjie::StaticCast(valType)->GetBaseType(); // Insert a load from the new local variable of result at the end of Exit Block of Inlined function. // Replace the use of the old local variable of result with the result of load. for (auto user : users) { auto newLoad = builder.CreateExpression(user->GetDebugLocation(), valType, resNew, user->GetParentBlock()); newLoad->MoveBefore(user); user->ReplaceOperand(resOld, newLoad->GetResult()); auto cast = TypeCastOrBoxIfNeeded( *newLoad->GetResult(), *resOld->GetType(), builder, *newLoad->GetParentBlock(), INVALID_LOCATION); if (cast != newLoad->GetResult()) { CJC_ASSERT(cast->IsLocalVar()); StaticCast(cast)->GetExpr()->MoveBefore(user); user->ReplaceOperand(newLoad->GetResult(), cast); } } return; } void FunctionInline::SetGroupDebugLocation(BlockGroup& group, const DebugLocation& loc) { group.SetDebugLocation(loc); auto changeLoc = [&loc](Expression& expr) { expr.SetDebugLocation(loc); return VisitResult::CONTINUE; }; Visitor::Visit(group, changeLoc, []([[maybe_unused]] Expression& e) { return VisitResult::CONTINUE; }); } void FunctionInline::DoFunctionInline(const Apply& apply, const std::string& name) { RecordEffectMap(apply); PrintOptInfo(apply, debug, name); // inline foo1 to foo2, `apply` node belongs to foo2, apply's callee is foo1, for example: // func foo1(param: Int64) { return param } // func foo2() { foo1(2) } // CHIR graph is like: // Func foo1(%7: Int64) { // Block Group: 1 // Block #7: // %8: Unit = Debug(%7, param) // [ret]%9: Int64& = Allocate(Int64) // %10: Unit = Store(%7, %9) // Exit() // } // Func foo2 { // Block Group: 2 // Block #14: // %[ret]%11: Int64& = Allocate(Int64) // %12: Int64 = ConstantInt(2) // %13: Int64 = Apply(@_CN7default4foo1El, %12) // triggered function inline // %14: Unit = Store(%13, %11) // Exit() // } // step 1: clone function body // `oldFuncGroup` is `Block Group: 1` BlockGroup* oldFuncGroup = nullptr; std::vector funcArgs; if (apply.GetCallee()->IsFuncWithBody()) { auto func = VirtualCast(apply.GetCallee()); oldFuncGroup = func->GetBody(); funcArgs = func->GetParams(); } else { CJC_ASSERT(apply.GetCallee()->IsLocalVar()); auto lambda = DynamicCast(StaticCast(apply.GetCallee())->GetExpr()); CJC_NULLPTR_CHECK(lambda); oldFuncGroup = lambda->GetBody(); funcArgs = lambda->GetParams(); } CJC_NULLPTR_CHECK(oldFuncGroup->GetTopLevelFunc()); // after cloned, we get a new block group, but func arg is value node, can't be cloned: // { // Block Group: 7 // Block #27: // %19: Unit = Debug(%7, param) // %20: Int64& = Allocate(Int64) // %21: Unit = Store(%7, %20) // Exit() // } // `newFuncGroup` is `Block Group: 7` CJC_NULLPTR_CHECK(apply.GetTopLevelFunc()); auto [newFuncGroup, returnVal] = CloneBlockGroupForInline(*oldFuncGroup, *apply.GetTopLevelFunc(), apply); SetGroupDebugLocation(*newFuncGroup, apply.GetDebugLocation()); // `funcEntry` is `Block #27` auto funcEntry = newFuncGroup->GetEntryBlock(); // `exitBlock` is `Block #27`, `returnVal` is `%20` auto exitBlocks = GetExitBlocks(*newFuncGroup); CJC_NULLPTR_CHECK(newFuncGroup->GetTopLevelFunc()); // step 2: change connection of func args // Func foo2 { // Block Group: 4 // Block #14: // [ret] %11: Int64& = Allocate(Int64) // %12: Int64 = Constant(2) // %13: Int64 = Apply(@_CN7default4foo1El, %12) // %14: Unit = Store(%13, %11) // Exit() // Block #27: // %19: Unit = Debug(%12, param) // %20: Int64& = Allocate(Int64) // %21: Unit = Store(%12, %20) // func arg %7 -> %12 // Exit() // } auto applyArgs = apply.GetArgs(); ReplaceFuncArgs(funcArgs, applyArgs, *newFuncGroup); FixCastProblemAfterInst(newFuncGroup, builder); // step 3: move copied blocks // Func foo2 { // Block Group: 4 // Block #14: // [ret] %11: Int64& = Allocate(Int64) // %12: Int64 = Constant(2) // %13: Int64 = Apply(@_CN7default4foo1El, %12) // %14: Unit = Store(%13, %11) // Exit() // Block #27: // %19: Unit = Debug(%7, param) // %20: Int64& = Allocate(Int64) // %21: Unit = Store(%7, %20) // Exit() // } // `applyGroup` is `Block Group: 2` auto applyGroup = apply.GetParentBlock()->GetParentBlockGroup(); CJC_NULLPTR_CHECK(applyGroup->GetTopLevelFunc()); for (auto block : newFuncGroup->GetBlocks()) { block->MoveTo(*applyGroup); } // if callee must throw exception, then we can't get return value // step 4 : Insert a load for return value of inlined function and replace the use // Func foo2 { // Block Group: 4 // Block #14: // [ret] %11: Int64& = Allocate(Int64) // %12: Int64 = Constant(2) // %13: Int64 = Apply(@_CN7default4foo1El, %12) // %22: Int64 = Load(%20) // Insert Load before the use of result // %14: Unit = Store(%22, %11) // Replace the use of old return value %13 -> %22 // Block #27: // %19: Unit = Debug(%12, param) // %20: Int64& = Allocate(Int64) // Exit() // add terminator to goto block 2: #28 // } if (returnVal != nullptr) { ReplaceFuncResult(returnVal, StaticCast(apply.GetResult())); } // step 5: split block to 2 pieces and remove `apply` node // Func foo2 { // Block Group: 4 // Block #14: // `Block #14` is split to `Block #14` and `Block #28` // [ret] %11: Int64& = Allocate(Int64) // %12: Int64 = Constant(2) // GoTo(#28) // Block #27: // %19: Unit = Debug(%12, param) // %20: Int64& = Allocate(Int64) // %21: Unit = Store(%12, %20) // Exit() // Block #28: // %22: Int64 = Load(%20) // %14: Unit = Store(%22, %11) // Exit() // } auto [block1, block2] = builder.SplitBlock(apply); // step 6: change connection in `Block Group 4` // Func foo2 { // Block Group: 4 // Block #21: // [ret] %11: Int64& = Allocate(Int64) // %12: Int64 = Constant(2) // GoTo(#27) // change successor #28 -> #27 // Block #27: // %19: Unit = Debug(%12, param) // %20: Int64& = Allocate(Int64) // %21: Unit = Store(%12, %20) // Goto(#28) // Block #28: // %22: Int64 = Load(%20) // %14: Unit = Store(%22, %11) // Exit() // } CJC_NULLPTR_CHECK(block1->GetTerminator()); block1->GetTerminator()->ReplaceSuccessor(*block2, *funcEntry); // if callee must throw exception, then we can't get `exitBlock` for (auto exitBlock : exitBlocks) { if (exitBlock != nullptr) { CJC_NULLPTR_CHECK(exitBlock->GetTerminator()); exitBlock->GetTerminator()->RemoveSelfFromBlock(); auto term2 = builder.CreateTerminator(block2, exitBlock); exitBlock->AppendExpression(term2); } } } void FunctionInline::RecordEffectMap(const Apply& apply) { auto callee = DynamicCast(apply.GetCallee()); // `callee` may be a lambda, we only record global function if (callee == nullptr) { return; } auto parentFunc = apply.GetTopLevelFunc(); CJC_NULLPTR_CHECK(parentFunc); if (!callee->IsLambda() && !parentFunc->IsLambda()) { effectMap[callee].emplace(parentFunc); } } // Clone a new block group for Function Inline and return the new local variableCHIRBuilder::CloneBlock // for function result value. std::pair FunctionInline::CloneBlockGroupForInline( const BlockGroup& other, Func& parentFunc, const Apply& apply) { BlockGroupCopyHelper helper(builder); helper.GetInstMapFromApply(apply); auto [newGroup, newBlockGroupRetValue] = helper.CloneBlockGroup(other, parentFunc); return {newGroup, newBlockGroupRetValue}; } cangjie_compiler-1.0.7/src/CHIR/Transformation/GetRefToArrayElem.cpp000066400000000000000000000050141510705540100253160ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/GetRefToArrayElem.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Expression/Terminator.h" using namespace Cangjie::CHIR; void GetRefToArrayElem::RunOnPackage(const Package& package, CHIRBuilder& builder) { for (auto func : package.GetGlobalFuncs()) { RunOnFunc(*func, builder); } } void GetRefToArrayElem::RunOnFunc(const Func& func, CHIRBuilder& builder) { for (auto block : func.GetBody()->GetBlocks()) { for (auto expr : block->GetExpressions()) { if (expr->GetExprKind() != ExprKind::INTRINSIC) { continue; } auto intrinsic = StaticCast(expr); if (intrinsic->GetIntrinsicKind() != CHIR::IntrinsicKind::ARRAY_GET_UNCHECKED) { continue; } auto users = intrinsic->GetResult()->GetUsers(); if (!std::all_of(users.begin(), users.end(), [](auto e) { return e->GetExprKind() == ExprKind::FIELD; })) { continue; } auto callContext = IntrisicCallContext { .kind = IntrinsicKind::ARRAY_GET_REF_UNCHECKED, .args = intrinsic->GetOperands() }; auto arrayGetRef = builder.CreateExpression( builder.GetType(intrinsic->GetResult()->GetType()), callContext, intrinsic->GetParentBlock()); arrayGetRef->CopyAnnotationMapFrom(*intrinsic); for (auto user : users) { auto field = StaticCast(user); auto fieldTy = field->GetResult()->GetType(); auto getElemRef = builder.CreateExpression(builder.GetType(fieldTy), arrayGetRef->GetResult(), field->GetPath(), field->GetParentBlock()); getElemRef->CopyAnnotationMapFrom(*field); getElemRef->GetResult()->EnableAttr(Attribute::READONLY); auto load = builder.CreateExpression(fieldTy, getElemRef->GetResult(), field->GetParentBlock()); load->CopyAnnotationMapFrom(*field); getElemRef->MoveBefore(user); field->ReplaceWith(*load); } intrinsic->ReplaceWith(*arrayGetRef); } } }cangjie_compiler-1.0.7/src/CHIR/Transformation/LambdaInline.cpp000066400000000000000000000115371510705540100243630ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/LambdaInline.h" namespace Cangjie::CHIR { namespace { std::vector GetNonDebugUsers(const Value& val) { std::vector res; for (auto expr: val.GetUsers()) { if (expr->GetExprKind() != CHIR::ExprKind::DEBUGEXPR) { res.emplace_back(expr); } } return res; } } // namespace bool CheckLambdaUsingForMultiThread(const Lambda& lambda) { auto returnTy = lambda.GetReturnType(); if (returnTy == nullptr) { return false; } returnTy = returnTy->StripAllRefs(); if (!returnTy->IsClass()) { return false; } auto classDef = StaticCast(returnTy)->GetClassDef(); return classDef->GetPackageName() == "std.core" && classDef->GetSrcCodeIdentifier() == "Future"; } LambdaInline::LambdaInline(CHIRBuilder& builder, const GlobalOptions& opts) : opts(opts), inlinePass(builder, GlobalOptions::OptimizationLevel::O2, opts.chirDebugOptimizer) { } void LambdaInline::InlineLambda(const std::vector& funcs) { if (!opts.IsOptimizationExisted(GlobalOptions::OptimizationFlag::FUNC_INLINING)) { return; } for (auto func : funcs) { if (func->GetFuncType()->IsCFunc()) { // skip c func type continue; } RunOnLambda(*func); } } /* * easy func can be inline: * 1. lambda func as a parameter. * 2. lambda call once in this easy function. * 3. lambda will not escape in this function. * func g(x: LambdaType) { * // ... * x() * // ... * } */ bool LambdaInline::IsLambdaPassToEasyFunc(const Lambda& lambda) const { // 0. lambda is not using for multi-thread if (CheckLambdaUsingForMultiThread(lambda)) { return false; } // 1. judge if lambda is a parameter of an apply. auto users = lambda.GetResult()->GetUsers(); CJC_ASSERT(users.size() == 1 && users[0]->IsApply()); auto apply = StaticCast(users[0]); size_t index = apply->GetArgs().size(); auto args = apply->GetArgs(); for (size_t i = 0; i < args.size(); i++) { if (args[i] == lambda.GetResult()) { index = i; break; } } if (index == args.size() || !apply->GetCallee()->IsFuncWithBody()) { return false; } // 2. judge lambda is only used in one callee apply. auto func = VirtualCast(apply->GetCallee()); CJC_ASSERT(index < func->GetParams().size()); auto lambdaArg = func->GetParams()[index]; auto lambdaArgUsers = GetNonDebugUsers(*lambdaArg); if (lambdaArgUsers.size() != 1) { return false; } auto onlyUser = lambdaArgUsers[0]; if (onlyUser != nullptr && onlyUser->GetExprKind() == ExprKind::APPLY && StaticCast(onlyUser)->GetCallee() == lambdaArg) { return true; } return false; } void LambdaInline::RunOnLambda(Lambda& lambda) { auto users = lambda.GetResult()->GetUsers(); if (GetNonDebugUsers(*lambda.GetResult()).empty() && !opts.enableCompileDebug) { for (auto user : users) { user->RemoveSelfFromBlock(); } lambda.RemoveSelfFromBlock(); PrintOptInfo(lambda, opts.chirDebugOptimizer, "EraseUselessLambdas"); return; } if (users.size() != 1U || users[0]->GetExprKind() != ExprKind::APPLY) { return; } if (StaticCast(users[0])->GetCallee() == lambda.GetResult()) { // If a lambda can be inlined and then removed, it should meet the requirements: // 1) only has one user which is an `APPLY` instruction; // 2) is the callee of `APPLY` instruction. // In other words, if a lambda is called only once and has no other uses, it can be inlined and then removed. inlinePass.DoFunctionInline(*StaticCast(users[0]), "functionInlineForLambda"); if (!opts.enableCompileDebug) { lambda.RemoveSelfFromBlock(); } return; } if (IsLambdaPassToEasyFunc(lambda)) { // only have one consumer as a parameter to apply expression, which will not escape in new function inlinePass.DoFunctionInline(*StaticCast(users[0]), "functionInlineForLambda"); auto newUsers = lambda.GetResult()->GetUsers(); if (newUsers[0]->GetExprKind() == ExprKind::APPLY && StaticCast(newUsers[0])->GetCallee() == lambda.GetResult()) { inlinePass.DoFunctionInline(*StaticCast(newUsers[0]), "functionInlineForLambda"); } if (!opts.enableCompileDebug) { lambda.RemoveSelfFromBlock(); } } } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/Transformation/MergeBlocks.cpp000066400000000000000000000160511510705540100242350ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/MergeBlocks.h" #include "cangjie/CHIR/GeneratedFromForIn.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/CHIRCasting.h" using namespace Cangjie; using namespace Cangjie::CHIR; namespace { SkipKind operator&(SkipKind l, SkipKind r) { if (l != SkipKind::NO_SKIP) { return l; } return r; } SkipKind& operator&=(SkipKind& l, SkipKind r) { l = l & r; return l; } // callee should have checked that there is only a single way GoTo between this block and its predecessor void MergeIntoPredecessor(Block& block) { // 1) remove the terminator of predecessor auto pred = block.GetPredecessors()[0]; CJC_NULLPTR_CHECK(pred->GetTerminator()); SkipKind appendSkipCheck{pred->Get() & pred->GetTerminator()->Get()}; pred->GetTerminator()->RemoveSelfFromBlock(); // 2) add the expressions in `block` to predecessor for (auto e : block.GetExpressions()) { e->MoveTo(*pred); } // 3) remove `block` itself since it's not a reachable one appendSkipCheck &= block.Get(); block.RemoveSelfFromBlockGroup(); if (appendSkipCheck != SkipKind::NO_SKIP) { pred->Set(appendSkipCheck); } } void MergeGotoOnlyBlock(Block& block) { auto preds = block.GetPredecessors(); auto target = block.GetSuccessors()[0]; for (auto pred : preds) { auto terminator = pred->GetTerminator(); CJC_NULLPTR_CHECK(terminator); target->SetDebugLocation(block.GetDebugLocation()); terminator->ReplaceSuccessor(block, *target); // skip check of block.terminator is not considered, because it is a simple GoTo and is unconditionally removed if (auto skip = terminator->Get() & block.Get(); skip != SkipKind::NO_SKIP) { terminator->Set(skip); } } block.RemoveSelfFromBlockGroup(); } /// A simple Clone call to a block is not correct to copy it, because it may contains self reference /// e.g. /// #1 /// %1 = Constant(1) /// %2 = Add(%1, %1) /// after a Clone call, we get /// #2 /// %3 = Constant(1) /// %4 = Add(%1, %1) --- wrong! should be Add(%3, %3) /// It is also possible that successors of #1 has usage of %1, in this case the generated CHIR is still wrong. /// Just in this usage, such error should not present. /// This operation has O(n^2) complexity. Block* CopyBlock(CHIRBuilder& builder, Block& block) { auto bg = block.GetParentBlockGroup(); auto ret = builder.CreateBlock(bg); std::vector> replaceTable{}; auto exprs = block.GetExpressions(); for (size_t i{0}; i < exprs.size(); ++i) { auto expr = exprs[i]; auto cloned = expr->Clone(builder, *ret); for (auto& pair : replaceTable) { cloned->ReplaceOperand(pair.first, pair.second); } if (i + 1 != exprs.size()) { replaceTable.emplace_back(expr->GetResult(), cloned->GetResult()); } } return ret; } void MergeForInCondBlock(CHIRBuilder& builder, Block& block) { auto oldPreds = block.GetPredecessors(); auto oldSuccs = block.GetSuccessors(); CJC_ASSERT(!oldPreds.empty()); for (size_t i{0}; i < oldPreds.size() - 1; ++i) { auto cpBlock = CopyBlock(builder, block); auto pred = oldPreds[i]; pred->GetTerminator()->ReplaceSuccessor(block, *cpBlock); } block.Remove(); } } // namespace void MergeBlocks::RunOnPackage(const Package& package, CHIRBuilder& builder, const GlobalOptions& opts) { for (auto func : package.GetGlobalFuncs()) { bool isCommonFunctionWithoutBody = func->TestAttr(Attribute::SKIP_ANALYSIS); if (isCommonFunctionWithoutBody) { continue; // Nothing to visit } RunOnFunc(*func->GetBody(), builder, opts); } } static bool SkipMergeBlock(const Block& bl, const GlobalOptions& opts) { if (bl.TestAttr(Attribute::UNREACHABLE)) { return true; } if (!opts.enableCompileDebug) { return false; } auto term = bl.GetTerminator(); return term && !term->GetDebugLocation().IsInvalidPos(); } void MergeBlocks::RunOnFunc(const BlockGroup& body, CHIRBuilder& builder, const GlobalOptions& opts) { auto checkSingleEntrySingleExit = [](const Block& block, const GlobalOptions& opts) { if (SkipMergeBlock(block, opts)) { return false; } auto preds = block.GetPredecessors(); if (preds.size() != 1) { return false; } auto succs = preds[0]->GetSuccessors(); if (succs.size() != 1) { return false; } // The terminator of pred must be GoTo auto term = preds[0]->GetTerminator(); if (term == nullptr || term->GetExprKind() != ExprKind::GOTO) { return false; } if (opts.enableCompileDebug || opts.enableCoverage) { auto [line, column] = term->GetDebugLocation().GetBeginPos(); return line == 0 && column == 0; } return true; }; auto checkGotoOnly = [](const Block& block, const GlobalOptions& opts) { if (block.GetExpressions().size() != 1 || block.IsEntry() || SkipMergeBlock(block, opts)) { return false; } auto term = block.GetTerminator(); if (term == nullptr || term->GetExprKind() != ExprKind::GOTO) { return false; } if (auto preds = block.GetPredecessors(); std::find(preds.begin(), preds.end(), &block) != preds.end()) { /* The following chir cannot be merged. * * Block #1: // preds: * ... * Goto(#2) * * Block #2: // preds: #1, #2 * Goto(#2) */ return false; } if (opts.enableCompileDebug || opts.enableCoverage) { auto [line, column] = term->GetDebugLocation().GetBeginPos(); return line == 0 && column == 0; } return true; }; bool isStable; do { isStable = true; for (auto block : body.GetBlocks()) { for (auto expr : block->GetExpressions()) { if (expr->GetExprKind() == ExprKind::LAMBDA) { RunOnFunc(*StaticCast(expr)->GetBody(), builder, opts); } } if (block->Get()) { isStable = false; MergeForInCondBlock(builder, *block); } else if (checkSingleEntrySingleExit(*block, opts)) { isStable = false; MergeIntoPredecessor(*block); } else if (checkGotoOnly(*block, opts)) { isStable = false; MergeGotoOnlyBlock(*block); } } } while (!isStable); } cangjie_compiler-1.0.7/src/CHIR/Transformation/NoSideEffectMarker.cpp000066400000000000000000000043401510705540100254760ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/NoSideEffectMarker.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Type/CustomTypeDef.h" #include "cangjie/Utils/CastingTemplate.h" #include "cangjie/Utils/TaskQueue.h" #include "cangjie/CHIR/Analysis/Utils.h" namespace Cangjie::CHIR { static const std::unordered_set STD_NO_SIDE_EFFECT_LIST = { #include "cangjie/CHIR/Transformation/StdNoSideEffectwhiteList.inc" }; static const std::vector NO_SIDE_EFFECT_PACKAGES = {"std"}; void NoSideEffectMarker::RunOnPackage(const Ptr& package, bool isDebug) { for (auto func : package->GetGlobalFuncs()) { RunOnFunc(func, isDebug); } for (auto imported : package->GetImportedVarAndFuncs()) { if (!imported->IsImportedVar()) { RunOnFunc(imported, isDebug); } } } void NoSideEffectMarker::RunOnFunc(const Ptr& value, bool isDebug) { std::string packageName; std::string mangleName; if (auto func = DynamicCast(value)) { packageName = func->GetPackageName(); mangleName = func->GetRawMangledName(); } else { return; } if (!CheckPackage(packageName)) { return; } if (STD_NO_SIDE_EFFECT_LIST.count(mangleName) == 0) { return; } value->EnableAttr(Attribute::NO_SIDE_EFFECT); if (isDebug) { std::string message = "[NoSideEffectMarker] The call to function " + value->GetSrcCodeIdentifier() + ToPosInfo(value->GetDebugLocation()) + " has been mark as no side effect.\n"; std::cout << message; } } bool NoSideEffectMarker::CheckPackage(const std::string& packageName) { for (const auto& whitePackage : NO_SIDE_EFFECT_PACKAGES) { if (whitePackage.size() > packageName.size()) { continue; } if (packageName.compare(0, whitePackage.length(), whitePackage) == 0) { return true; } } return false; } } // namespace Cangjie::CHIRcangjie_compiler-1.0.7/src/CHIR/Transformation/RangePropagation.cpp000066400000000000000000000247411510705540100253050ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/RangePropagation.h" #include "cangjie/CHIR/Analysis/ConstAnalysis.h" #include "cangjie/CHIR/Analysis/Engine.h" #include namespace Cangjie::CHIR { std::optional CheckSingleBool(const ValueRange& vr) { if (vr.GetRangeKind() == ValueRange::RangeKind::BOOL) { auto& boolRange = StaticCast(vr); if (boolRange.GetVal().IsSingleValue()) { return boolRange.GetVal().GetSingleValue(); } } return std::nullopt; } std::optional CheckSingleSInt(const ValueRange& vr) { if (vr.GetRangeKind() == ValueRange::RangeKind::SINT) { auto& sIntRange = StaticCast(vr); if (sIntRange.GetVal().IsSingleValue()) { return sIntRange.GetVal().NumericBound().GetSingleElement(); } } return std::nullopt; } RangePropagation::RangePropagation( CHIRBuilder& builder, RangeAnalysisWrapper* rangeAnalysisWrapper, DiagAdapter* diag, bool enIncre) : builder(builder), analysisWrapper(rangeAnalysisWrapper), diag(diag), enIncre(enIncre) { } const OptEffectCHIRMap& RangePropagation::GetEffectMap() const { return effectMap; } const std::vector& RangePropagation::GetFuncsNeedRemoveBlocks() const { return funcsNeedRemoveBlocks; } void RangePropagation::RunOnPackage(const Ptr& package, bool isDebug) { for (auto func : package->GetGlobalFuncs()) { RunOnFunc(func, isDebug); } } void RangePropagation::RunOnFunc(const Ptr& func, bool isDebug) { auto result = analysisWrapper->CheckFuncResult(func); if (!result) { return; } std::vector toBeRewrited; const auto actionBeforeVisitExpr = [](const RangeDomain&, Expression*, size_t) {}; const auto actionAfterVisitExpr = [this, &toBeRewrited, func]( const RangeDomain& state, Expression* expr, size_t index) { auto exprType = expr->GetResult()->GetType(); if (expr->IsBinaryExpr()) { if (auto absVal = state.CheckAbstractValue(expr->GetResult()); absVal) { return (void)toBeRewrited.emplace_back(expr, index, GenerateConstExpr(exprType, absVal)); } } else if (expr->IsUnaryExpr()) { if (auto absVal = state.CheckAbstractValue(expr->GetResult()); absVal) { return (void)toBeRewrited.emplace_back(expr, index, GenerateConstExpr(exprType, absVal)); } } else if ((exprType->IsInteger() || exprType->IsBoolean()) && (expr->IsLoad() || expr->IsTypeCast() || expr->IsField())) { if (auto absVal = state.CheckAbstractValue(expr->GetResult()); absVal && !exprType->IsString()) { toBeRewrited.emplace_back(expr, index, GenerateConstExpr(exprType, absVal)); RecordEffectMap(expr, func); } } else if (expr->GetExprKind() == ExprKind::INTRINSIC) { if (auto intrinic = StaticCast(expr); intrinic->GetIntrinsicKind() == CHIR::IntrinsicKind::VARRAY_SET || intrinic->GetIntrinsicKind() == CHIR::IntrinsicKind::VARRAY_GET) { CheckVarrayIndex(intrinic, state); } } }; bool doBlockElimination = false; const auto actionOnTerminator = [this, isDebug, &doBlockElimination](const RangeDomain&, Terminator* terminator, std::optional targetSucc) { switch (terminator->GetExprKind()) { case ExprKind::BRANCH: case ExprKind::MULTIBRANCH: if (targetSucc.has_value()) { doBlockElimination = true; return RewriteBranchTerminator(terminator, targetSucc.value(), isDebug); } break; default: break; } }; result->VisitWith(actionBeforeVisitExpr, actionAfterVisitExpr, actionOnTerminator); for (auto& rewriteInfo : toBeRewrited) { RewriteToConstExpr(rewriteInfo, isDebug); } if (doBlockElimination) { funcsNeedRemoveBlocks.push_back(func.get()); } } Ptr RangePropagation::GenerateConstExpr(const Ptr& type, const Ptr& rangeVal) { switch (rangeVal->GetRangeKind()) { case ValueRange::RangeKind::BOOL: if (auto boolValue = CheckSingleBool(*rangeVal.get())) { return builder.CreateLiteralValue(type, boolValue.value()); } break; case ValueRange::RangeKind::SINT: if (auto intValue = CheckSingleSInt(*rangeVal.get())) { return builder.CreateLiteralValue(type, intValue.value().UVal()); } break; } return nullptr; } void RangePropagation::RewriteToConstExpr(const RewriteInfo& rewriteInfo, bool isDebug) const { if (!rewriteInfo.literalVal) { return; } auto oldExpr = rewriteInfo.oldExpr; auto oldExprResult = oldExpr->GetResult(); auto oldExprParent = oldExpr->GetParentBlock(); auto newExpr = builder.CreateExpression(oldExprResult->GetType(), rewriteInfo.literalVal, oldExprParent); newExpr->SetDebugLocation(oldExpr->GetDebugLocation()); oldExprParent->GetExpressionByIdx(rewriteInfo.index)->ReplaceWith(*newExpr); if (isDebug) { std::string message = "[RangePropagation] The " + ExprKindMgr::Instance()->GetKindName(static_cast(oldExpr->GetExprKind())) + ToPosInfo(oldExpr->GetDebugLocation()) + " has been rewrited to a constant\n"; std::cout << message; } } void RangePropagation::RewriteBranchTerminator( const Ptr& branch, const Ptr& targetSucc, bool isDebug) { auto parentBlock = branch->GetParentBlock(); branch->RemoveSelfFromBlock(); auto newTerminator = builder.CreateTerminator(targetSucc, parentBlock); parentBlock->AppendExpression(newTerminator); if (isDebug) { std::string message = "[RangePropagation] The terminator " + ExprKindMgr::Instance()->GetKindName(static_cast(branch->GetExprKind())) + ToPosInfo(branch->GetDebugLocation()) + " has been optimised\n"; std::cout << message; } } GlobalVar* RecordLoadEffectMap(const Ptr& load) { GlobalVar* gv = nullptr; auto loc = load->GetLocation(); if (loc->IsGlobalVarInCurPackage()) { // let a = 3 // Load(gv_a) gv = DynamicCast(loc); } else if (loc->IsLocalVar()) { // let sa = SA(); sa.x // %0 = GetElementRef(gv_sa); %1 = Load(%0) auto locExpr = StaticCast(loc)->GetExpr(); if (locExpr->GetExprKind() == ExprKind::GET_ELEMENT_REF) { auto base = StaticCast(locExpr)->GetLocation(); if (base->IsGlobalVarInCurPackage()) { gv = DynamicCast(base); } } } return gv; } GlobalVar* RecordFieldEffectMap(const Ptr& field) { GlobalVar* gv = nullptr; auto base = field->GetBase(); if (base->IsLocalVar()) { auto baseExpr = StaticCast(base)->GetExpr(); if (baseExpr->GetExprKind() == ExprKind::LOAD) { auto loc = StaticCast(baseExpr)->GetLocation(); if (loc->IsGlobalVarInCurPackage()) { // let a = (1, 2); a[0] // %0 = Load(gv_a); %1 = Field(%0, 0) gv = DynamicCast(loc); } } } return gv; } static std::mutex g_mtx; OptEffectCHIRMap RangePropagation::effectMap; void RangePropagation::RecordEffectMap(const Expression* expr, const Func* func) const { if (!enIncre) { return; } GlobalVar* gv = nullptr; if (expr->GetExprKind() == ExprKind::LOAD) { gv = RecordLoadEffectMap(StaticCast(expr)); } else if (expr->GetExprKind() == ExprKind::FIELD) { gv = RecordFieldEffectMap(StaticCast(expr)); } if (gv) { std::lock_guard guard(g_mtx); effectMap[gv].emplace(const_cast(func)); } } std::vector GetVArraySizeList(const Ptr& type) { std::vector size; auto indexType = type.get(); if (indexType->IsRef()) { indexType = StaticCast(indexType)->GetBaseType(); } while (indexType->GetTypeKind() == Type::TypeKind::TYPE_VARRAY) { auto vArrayType = StaticCast(indexType); size.push_back(vArrayType->GetSize()); indexType = vArrayType->GetElementType(); } return size; } void RangePropagation::CheckVarrayIndex(const Ptr& intrin, const RangeDomain& state) const { CJC_ASSERT(intrin->GetIntrinsicKind() == CHIR::IntrinsicKind::VARRAY_GET || intrin->GetIntrinsicKind() == CHIR::IntrinsicKind::VARRAY_SET); auto& args = intrin->GetArgs(); CJC_ASSERT(args.size() >= 2U); size_t begin = intrin->GetIntrinsicKind() == CHIR::IntrinsicKind::VARRAY_GET ? 1U : 2U; auto sizes = GetVArraySizeList(args[0]->GetType()); CJC_ASSERT(sizes.size() >= args.size() - begin); for (size_t i = begin; i < args.size(); ++i) { auto size = sizes[i - begin]; auto index = args[i]; auto indexRange = RangeAnalysis::GetSIntDomainFromState(state, index); if (indexRange.IsTop()) { return; } SIntDomain varraySizeNode{ConstantRange{SInt{IntWidth::I64, static_cast(size)}}, false}; SIntDomain zeroNode{ConstantRange{SInt::Zero(IntWidth::I64)}, false}; auto ltUpperBound{ComputeRelIntBinop({indexRange, varraySizeNode, index, nullptr, ExprKind::LT, false})}; auto geLowerBound{ComputeRelIntBinop({indexRange, zeroNode, index, nullptr, ExprKind::GE, false})}; if (ltUpperBound.IsFalse() || geLowerBound.IsFalse()) { auto bd = diag->DiagnoseRefactor(DiagKindRefactor::chir_idx_out_of_bounds, ToRange(intrin->GetDebugLocation())); std::stringstream ss; ss << "range of index " << i - begin << " is (" << indexRange.ToString() << "), however the size of varray is " + std::to_string(size); bd.AddMainHintArguments(ss.str()); } } } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/Transformation/RedundantFutureRemoval.cpp000066400000000000000000000101251510705540100265010ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/RedundantFutureRemoval.h" #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Visitor/Visitor.h" namespace Cangjie::CHIR { RedundantFutureRemoval::RedundantFutureRemoval(const Package& pkg, bool isDebug) : package(pkg), isDebug(isDebug) { } void RedundantFutureRemoval::RunOnPackage() { for (auto func : package.GetGlobalFuncs()) { RunOnFunc(*func); } } void RedundantFutureRemoval::RunOnFunc(const Func& func) { auto visitExitAction = [this](Expression& expr) { auto [future, apply] = CheckSpawnWithFuture(expr); if (future != nullptr) { auto spawnExpr = StaticCast(&expr); RewriteSpawnWithOutFuture(*spawnExpr, *future, *apply); if (isDebug) { std::string message = "[RedundantFutureRemoval] The call to Spawn" + ToPosInfo(expr.GetDebugLocation()) + " has been optimised due to redundant future in spawn.\n"; std::cout << message; } } return VisitResult::CONTINUE; }; Visitor::Visit(func, visitExitAction); } FuncBase* RedundantFutureRemoval::GetExecureClosureFunc() const { for (auto def : package.GetAllCustomTypeDef()) { if (!IsCoreFuture(*def)) { continue; } for (auto method : def->GetMethods()) { if (method->GetSrcCodeIdentifier() == "executeClosure") { return method; } } return nullptr; } return nullptr; } void RedundantFutureRemoval::RewriteSpawnWithOutFuture(Spawn& spawnExpr, LocalVar& futureValue, Apply& apply) { /* change from: %a : future = Allocate() %b : funcType = Lambda() %c : Apply(Future, %a, %b) %d : Spawn(%a) change to: %b : funcType = Lambda() %d : Spwan(%b) */ // 1. Get Lambda from apply expression auto lambda = apply.GetOperand(2U); CJC_ASSERT(lambda->GetType()->IsFunc()); // 2. Replace spawn and remove useless node auto futureExpression = futureValue.GetExpr(); apply.RemoveSelfFromBlock(); futureValue.ReplaceWith(*lambda, spawnExpr.GetParentBlock()->GetParentBlockGroup()); futureExpression->RemoveSelfFromBlock(); if (executeClosure == nullptr) { executeClosure = GetExecureClosureFunc(); CJC_NULLPTR_CHECK(executeClosure); } spawnExpr.SetExecuteClosure(*executeClosure); } std::pair RedundantFutureRemoval::CheckSpawnWithFuture(Expression& expr) const { if (expr.GetExprKind() != ExprKind::SPAWN) { return {nullptr, nullptr}; } auto spawnExpr = StaticCast(&expr); if (spawnExpr->IsExecuteClosure()) { return {nullptr, nullptr}; } auto spawnOperand = spawnExpr->GetFuture(); if (!spawnOperand->IsLocalVar()) { return {nullptr, nullptr}; } auto localFuture = StaticCast(spawnOperand); auto users = localFuture->GetUsers(); std::unordered_set usersSet(users.begin(), users.end()); if (usersSet.size() == 3U) { // if spawn and future debug is only users of future, then optimize. // future would have exactly three users: apply future, as a paramter in spawn and debug usersSet.erase(localFuture->GetDebugExpr()); } if (usersSet.size() == 2U) { // if spawn is only user of future, then optimize. // future would have exactly two users: apply future and use in spawn usersSet.erase(spawnExpr); } if (usersSet.size() != 1) { return {nullptr, nullptr}; } // optimize spawn if only apply is left auto apply = *usersSet.begin(); CJC_ASSERT(apply->GetExprKind() == ExprKind::APPLY); return {localFuture, StaticCast(apply)}; } } // namespace Cangjie::CHIRcangjie_compiler-1.0.7/src/CHIR/Transformation/RedundantGetOrThrowElimination.cpp000066400000000000000000000042311510705540100301370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/RedundantGetOrThrowElimination.h" #include "cangjie/CHIR/Analysis/Engine.h" #include "cangjie/CHIR/Analysis/GetOrThrowResultAnalysis.h" #include "cangjie/CHIR/Analysis/Utils.h" using namespace Cangjie::CHIR; RedundantGetOrThrowElimination::RedundantGetOrThrowElimination() { } void RedundantGetOrThrowElimination::RunOnPackage(const Ptr& package, bool isDebug) const { for (auto func : package->GetGlobalFuncs()) { RunOnFunc(func, isDebug); } } void RedundantGetOrThrowElimination::RunOnFunc(const Ptr& func, bool isDebug) const { auto analysis = std::make_unique(func, isDebug); auto engine = Engine(func, std::move(analysis)); auto result = engine.IterateToFixpoint(); CJC_NULLPTR_CHECK(result); const auto actionBeforeVisitExpr = [func, isDebug](const GetOrThrowResultDomain& state, Expression* expr, size_t) { if (!IsGetOrThrowFunction(*expr)) { return; } auto apply = StaticCast(expr); auto arg = apply->GetArgs()[0]; if (auto result = state.CheckGetOrThrowResult(arg); result) { apply->GetResult()->ReplaceWith(*result->GetResult(), func->GetBody()); if (isDebug) { std::string message = "[RGetOtThrowE] The usages of the result of getOrThrow" + ToPosInfo(apply->GetDebugLocation()) + " have been replaced by the value" + ToPosInfo(result->GetDebugLocation()) + "\n"; std::cout << message; } } }; const auto actionAfterVisitExpr = [](const GetOrThrowResultDomain&, Expression*, size_t) {}; const auto actionOnTerminator = [](const GetOrThrowResultDomain&, Terminator*, std::optional) {}; result->VisitWith(actionBeforeVisitExpr, actionAfterVisitExpr, actionOnTerminator); } cangjie_compiler-1.0.7/src/CHIR/Transformation/RedundantLoadElimination.cpp000066400000000000000000000113571510705540100267610ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/RedundantLoadElimination.h" #include "cangjie/CHIR/Analysis/Engine.h" #include "cangjie/CHIR/Analysis/ReachingDefinitionAnalysis.h" #include "cangjie/CHIR/Analysis/Utils.h" namespace Cangjie::CHIR { RedundantLoadElimination::RedundantLoadElimination() { } void RedundantLoadElimination::RunOnPackage(const Ptr& package, bool isDebug) const { for (auto func : package->GetGlobalFuncs()) { RunOnFunc(func, isDebug); } } static void ModifyApplyCalleeInfo(const LocalVar& loadResult, Value& storeValue) { if (!storeValue.IsFunc()) { return; } auto funcBase = VirtualCast(&storeValue); if (!funcBase->IsMemberFunc() || !funcBase->TestAttr(Attribute::STATIC)) { return; } // If the direct value func call comes from a load, // it is invalid if func is generic(must from a GetInstantiateValue), so this type is its parent directly. Type* thisType = funcBase->GetParentCustomTypeOrExtendedType(); std::vector oldUsers = loadResult.GetUsers(); for (auto user : oldUsers) { if (user->GetExprKind() == ExprKind::APPLY) { auto apply = StaticCast(user); apply->SetThisType(thisType); } if (user->GetExprKind() == ExprKind::APPLY_WITH_EXCEPTION) { auto apply = StaticCast(user); apply->SetThisType(thisType); } } } void RedundantLoadElimination::RunOnFunc(const Ptr& func, bool isDebug) const { if (func->TestAttr(Attribute::SKIP_ANALYSIS)) { return; } auto analysis = std::make_unique(func); auto engine = Engine(func, std::move(analysis)); auto result = engine.IterateToFixpoint(); CJC_NULLPTR_CHECK(result); const auto actionBeforeVisitExpr = [](const ReachingDefinitionDomain&, Expression*, size_t) {}; std::unordered_map> toBeRemoved; const auto actionAfterVisitExpr = [func, isDebug, &toBeRemoved]( const ReachingDefinitionDomain& state, Expression* expr, size_t index) { CJC_NULLPTR_CHECK(expr); if (expr->GetExprKind() != ExprKind::LOAD) { return; } auto load = StaticCast(expr); if (auto def = state.CheckReachingDef(load->GetLocation()); def) { /* * adjust value call if load's result is a call * %1: () -> Int64& = Allocate(() -> Int64) * %2: Unit = Store(@StaticFunc, %1) // static func in custom type * %3: () -> Int64 = Load(%1) * %8: Int64 = Apply(%3 : () -> Int64) // if %3 is opt by RLE, this apply need adjustment */ ModifyApplyCalleeInfo(*load->GetResult(), *def->GetValue()); // replace load with its exact value load->GetResult()->ReplaceWith(*def->GetValue(), func->GetBody()); toBeRemoved[load->GetParentBlock()].emplace_back(index); if (isDebug) { std::string message = "[RLE] The usages of the result of Load" + ToPosInfo(load->GetDebugLocation()) + " have been replaced by the value" + ToPosInfo(def->GetDebugLocation()) + "\n"; std::cout << message; } return; } if (auto validLoad = state.CheckReachingLoadDef(load->GetLocation()); validLoad) { if (validLoad == load) { return; } load->GetResult()->ReplaceWith(*validLoad->GetResult(), func->GetBody()); toBeRemoved[load->GetParentBlock()].emplace_back(index); if (isDebug) { std::string message = "[RLE] The usages of the result of Load" + ToPosInfo(load->GetDebugLocation()) + " have been replaced by the value" + ToPosInfo(validLoad->GetDebugLocation()) + "\n"; std::cout << message; } } }; const auto actionOnTerminator = [](const ReachingDefinitionDomain&, Terminator*, std::optional) {}; result->VisitWith(actionBeforeVisitExpr, actionAfterVisitExpr, actionOnTerminator); for (auto& [bb, indexes] : toBeRemoved) { std::vector exprs; for (auto i : indexes) { exprs.emplace_back(bb->GetExpressionByIdx(i)); } for (auto e : exprs) { e->RemoveSelfFromBlock(); } } } } cangjie_compiler-1.0.7/src/CHIR/Transformation/RemoveUnusedImport.cpp000066400000000000000000000733251510705540100256630ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/CHIR.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Visitor/Visitor.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/ProfileRecorder.h" namespace Cangjie::CHIR { namespace { struct UnusedImportAnalysis { public: UnusedImportAnalysis( const std::unordered_map& implicitFuncs, bool incr, bool skipVirtualFunc = true) : implicitFuncs(implicitFuncs), incr(incr), skipVirtualFunc(skipVirtualFunc) {} bool Judge(const ImportedValue& val) { if (incr || val.TestAttr(Attribute::NON_RECOMPILE)) { return false; } if (auto func = DynamicCast(&val)) { // 1. implicit imported functions will be used in runtime if (implicitFuncs.find(func->GetIdentifierWithoutPrefix()) != implicitFuncs.end()) { return false; } // 2. Future::execute defined in std.core will be used in codegen auto parentDef = func->GetParentCustomTypeDef(); if (parentDef != nullptr && CheckCustomTypeDefIsExpected(*parentDef, CORE_PACKAGE_NAME, STD_LIB_FUTURE) && (func->GetSrcCodeIdentifier() == "execute" || func->GetSrcCodeIdentifier() == "executeClosure")) { return false; } // 3. if func is virtual, it must as a placeholder in vtable right now if (skipVirtualFunc && func->IsMemberFunc() && func->IsVirtualFunc()) { return false; } // 4. finalizer may be used in runtime if (func->GetFuncKind() == FuncKind::FINALIZER) { return false; } } // 5. not used function or static variable can be removed return val.GetUsers().empty(); } void SetWhetherSkipVirtualFunc(bool skip) { skipVirtualFunc = skip; } private: const std::unordered_map& implicitFuncs; bool incr; bool skipVirtualFunc; }; class CollectUsedImports { public: CollectUsedImports(UnusedImportAnalysis& unusedImportAnalysis, bool incr) : unusedImportAnalysis(unusedImportAnalysis), isIncremental(incr) {} // used imported decls, separated from source package decls to avoid dynamic_cast std::unordered_set used; // checked source package decls. These containers exclude used imported decls, as an imported decl is checked iff // it is used std::unordered_set checkedVars; std::unordered_set checkedFuns; std::unordered_set checkedDefs; std::unordered_set checkedTys; void Collect(Package& package, const std::unordered_map& implicitFuncs) { for (auto& v : implicitFuncs) { // for std.core, these implicit funcs are 'from' the source package. for any other package, they are // imported funcs. VisitValue(*v.second); } for (auto v : package.GetImportedVarAndFuncs()) { VisitImported(*v); } for (auto v : package.GetGlobalVars()) { VisitVar(*v); } for (auto v : package.GetGlobalFuncs()) { bool isCommonFunctionWithoutBody = v->TestAttr(Attribute::SKIP_ANALYSIS); if (isCommonFunctionWithoutBody) { continue; // Nothing to visit } if (v->Get() != nullptr && v->TestAttr(Attribute::IMPORTED)) { continue; } VisitFunc(*v); } for (auto v : package.GetClasses()) { VisitTypeDef(*v); } for (auto v : package.GetStructs()) { VisitTypeDef(*v); } for (auto v : package.GetEnums()) { VisitTypeDef(*v); } } private: void VisitVar(GlobalVarBase& var) { if (auto [_, ins] = checkedVars.insert(&var); !ins) { return; } VisitType(*var.GetType()); if (auto gv = var.GetParentCustomTypeDef()) { VisitTypeDef(*gv); } } void VisitFunc(FuncBase& func) { if (auto [_, ins] = checkedFuns.insert(&func); !ins) { return; } VisitType(*func.GetType()); for (auto ty : func.GetGenericTypeParams()) { VisitType(*ty); } if (auto def = func.GetParentCustomTypeDef()) { VisitTypeDef(*def); } if (auto fun = DynamicCast(&func)) { bool isCommonFunctionWithoutBody = fun->TestAttr(Attribute::SKIP_ANALYSIS); if (isCommonFunctionWithoutBody) { return; // Nothing to visit } VisitBG(*fun->GetBody()); } } void VisitType(Type& ty) { if (auto [_, ins] = checkedTys.insert(&ty); !ins) { return; } if (auto classTy = DynamicCast(&ty)) { VisitTypeDef(*classTy->GetCustomTypeDef()); } else if (auto genericTy = DynamicCast(&ty)) { for (auto upperBound : genericTy->GetUpperBounds()) { VisitType(*upperBound); } } for (auto argTy : ty.GetTypeArgs()) { VisitType(*argTy); } } void VisitTypeDef(const CustomTypeDef& def) { if (auto [_, ins] = checkedDefs.insert(const_cast(&def)); !ins) { return; } if (auto ex = DynamicCast(&def)) { VisitType(*ex->GetExtendedType()); } else { VisitType(*def.GetType()); } for (auto ty : def.GetGenericTypeParams()) { VisitType(*ty); } for (auto& member : def.GetAllInstanceVars()) { VisitType(*member.type); } for (auto& member : def.GetStaticMemberVars()) { VisitType(*member->GetType()); } for (auto& member : def.GetMethods()) { VisitType(*member->GetFuncType()); } for (auto type : def.GetImplementedInterfaceTys()) { VisitType(*type); } if (auto cl = DynamicCast(&def)) { if (cl->GetSuperClassTy()) { VisitType(*cl->GetSuperClassTy()); } for (auto& method : cl->GetAbstractMethods()) { VisitType(*method.methodTy); } } else if (auto enu = DynamicCast(&def)) { for (auto ctor : enu->GetCtors()) { VisitType(*ctor.funcType); } } for (auto vTable : def.GetVTable()) { for (auto funcInfo : vTable.second) { if (funcInfo.instance) { VisitValue(*funcInfo.instance); } } } } /// Begin visit expression methods void VisitExpression(Expression& e) { if (e.GetResult()) { VisitType(*e.GetResultType()); } for (unsigned i{0}; i < e.GetNumOfOperands(); ++i) { VisitValue(*e.GetOperand(i)); } for (auto bg : e.GetBlockGroups()) { VisitBG(*bg); } if (auto ins = DynamicCast(&e)) { VisitType(*ins->GetType()); } // we are planning to give the following six classes a common interface. if (auto apply = DynamicCast(&e)) { for (auto type : apply->GetInstantiatedTypeArgs()) { VisitType(*type); } if (apply->GetThisType()) { VisitType(*apply->GetThisType()); } } if (auto apply = DynamicCast(&e)) { for (auto type : apply->GetInstantiatedTypeArgs()) { VisitType(*type); } if (apply->GetThisType()) { VisitType(*apply->GetThisType()); } } if (auto invoke = DynamicCast(&e)) { for (auto type : invoke->GetInstantiatedTypeArgs()) { VisitType(*type); } VisitType(*invoke->GetThisType()); VisitType(*invoke->GetMethodType()); } if (auto invoke = DynamicCast(&e)) { for (auto type : invoke->GetInstantiatedTypeArgs()) { VisitType(*type); } VisitType(*invoke->GetThisType()); VisitType(*invoke->GetMethodType()); } if (auto invoke = DynamicCast(&e)) { for (auto type : invoke->GetInstantiatedTypeArgs()) { VisitType(*type); } VisitType(*invoke->GetThisType()); VisitType(*invoke->GetMethodType()); } if (auto invoke = DynamicCast(&e)) { for (auto type : invoke->GetInstantiatedTypeArgs()) { VisitType(*type); } VisitType(*invoke->GetThisType()); VisitType(*invoke->GetMethodType()); } if (auto inst = DynamicCast(&e)) { for (auto type : inst->GetInstantiateTypes()) { VisitType(*type); } } } void VisitValue(Value& v) { auto value = &v; if (auto fun = DynamicCast(value)) { if (!isIncremental || fun->TestAttr(Attribute::NON_RECOMPILE)) { VisitFunc(*fun); } else { VisitImported(*fun); } } if (auto var = DynamicCast(value)) { if (!isIncremental || var->TestAttr(Attribute::NON_RECOMPILE)) { VisitVar(*var); } else { VisitImported(*var); } } if (auto fun = DynamicCast(value)) { VisitFunc(*fun); } if (auto var = DynamicCast(value)) { VisitVar(*var); } } void VisitImported(ImportedValue& var) { if (auto [_, inserted] = used.insert(&var); !inserted) { return; } if (unusedImportAnalysis.Judge(var)) { return; } VisitType(*var.GetType()); if (auto gv = DynamicCast(&var)) { if (auto cl = gv->GetParentCustomTypeDef()) { VisitTypeDef(*cl); } } else { auto func = StaticCast(&var); if (auto cl = func->GetParentCustomTypeDef()) { VisitTypeDef(*cl); } for (auto ty : func->GetGenericTypeParams()) { VisitType(*ty); } } } void VisitBG(const BlockGroup& bg) { for (auto bl : bg.GetBlocks()) { VisitBlock(*bl); } } void VisitBlock(const Block& bl) { for (auto expr : bl.GetExpressions()) { VisitExpression(*expr); } if (bl.IsLandingPadBlock()) { for (auto type : bl.GetExceptions()) { VisitType(*type); } } } UnusedImportAnalysis& unusedImportAnalysis; bool isIncremental{false}; }; class UnusedImportRemover { public: UnusedImportRemover( bool incr, const GlobalOptions& opts, const std::unordered_map& implicitFuncs) : isIncremental(incr), opts(opts), unusedImportAnalysis(implicitFuncs, incr), implicitFuncs(implicitFuncs) { } void Remove(Package& p) { // 1. remove func with virtual func remained unusedImportAnalysis.SetWhetherSkipVirtualFunc(true); RemoveImportedValueWithNoUsers(p); // 2. remove unused decls no matter whether func is virtual unusedImportAnalysis.SetWhetherSkipVirtualFunc(false); KeepUsedDecls(p); // 3. remove all virtual import func without customDef in package // reason: First step remain all virtual func, second step clear all custom type which are not used, // so some virtual funcs does not have their parent custom type and need to be deleted. // Do this only because virtual func need to be kept in vtable if custom type is remained, // need to be deleted if custom type is deleted. RemoveAllVirtualFuncWithOutDef(p); } private: /// Keep used decls by remove unused decls. /// 1. Traverse "roots" to mark all used decls. Roots include: /// (1) implicitly imported values (accidentally they are all funcs) /// (2) all source pkg var/func/typedefs /// (3) imported values that have users (those without users have been removed by /// \ref RemoveImportedValueWithNoUsers) /// 2. Rewrite imported collections of \p p with imported decls marked used void KeepUsedDecls(Package& p) { CollectUsedImports impl{unusedImportAnalysis, isIncremental}; impl.Collect(p, implicitFuncs); AddImplicitUsedDef(impl, p); p.SetImportedVarAndFuncs(Keep(p.GetImportedVarAndFuncs(), impl.used, isIncremental)); p.SetImportedStructs(Keep(p.GetImportedStructs(), impl.checkedDefs, isIncremental)); p.SetImportedClasses(Keep(p.GetImportedClasses(), impl.checkedDefs, isIncremental)); p.SetImportedEnums(Keep(p.GetImportedEnums(), impl.checkedDefs, isIncremental)); p.SetImportedExtends(Keep(p.GetImportedExtends(), impl.checkedDefs, isIncremental)); } /// Remove unused imported values (var/func) if they have no users void RemoveImportedValueWithNoUsers(Package& p) { std::vector res; for (auto k : p.GetImportedVarAndFuncs()) { /// incremental unchanged decls are represented as ImportedValue(var/func) but they are from source package /// \ref p, so they should always be kept. if (unusedImportAnalysis.Judge(*k)) { k->DestroySelf(); } else { res.push_back(k); } } p.SetImportedVarAndFuncs(std::move(res)); } /// Keep \p toKeep of \p allDecls /// \param incremental in incremental compilation template std::vector Keep( const std::vector& allDecls, const std::unordered_set& toKeep, bool incremental) const { static_assert(std::is_convertible_v); std::vector res; for (auto decl : allDecls) { if (incremental && decl->TestAttr(Attribute::NON_RECOMPILE)) { res.push_back(decl); continue; } if (toKeep.find(decl) != toKeep.end()) { res.push_back(decl); } } return res; } void AddImplicitUsedDef(CollectUsedImports& impl, const Package& p) const { if (!opts.sancovOption.IsSancovEnabled()) { return; } for (auto s : p.GetImportedStructs()) { if (s->GetPackageName() == "std.core" && s->GetSrcCodeIdentifier() == "Array") { impl.checkedDefs.emplace(s); } else if (s->GetPackageName() == "std.core" && s->GetSrcCodeIdentifier() == "LibC") { impl.checkedDefs.emplace(s); } } } void RemoveAllVirtualFuncWithOutDef(Package& p) const { auto allDefs = p.GetAllCustomTypeDef(); std::unordered_set allDefSet{allDefs.begin(), allDefs.end()}; std::vector res; for (auto k : p.GetImportedVarAndFuncs()) { if (!k->IsFunc()) { res.push_back(k); continue; } auto func = StaticCast(k); if (!func->IsVirtualFunc()) { res.push_back(k); continue; } if (allDefSet.count(func->GetParentCustomTypeDef()) == 0) { k->DestroySelf(); } else { res.push_back(k); } } p.SetImportedVarAndFuncs(std::move(res)); } bool isIncremental{false}; const GlobalOptions& opts; UnusedImportAnalysis unusedImportAnalysis; const std::unordered_map& implicitFuncs; }; void CreateNewExtendDef(Package& package, CustomTypeDef& curDef, ClassType& parentType, const std::vector& virtualFunc, CHIRBuilder& builder) { auto mangledName = "extend_" + curDef.GetIdentifier() + "_p_" + parentType.ToString(); auto genericParams = curDef.GetGenericTypeParams(); auto extendDef = builder.CreateExtend( INVALID_LOCATION, mangledName, package.GetName(), false, genericParams); extendDef->SetExtendedType(*curDef.GetType()); extendDef->AddImplementedInterfaceTy(parentType); extendDef->EnableAttr(Attribute::COMPILER_ADD); if (curDef.TestAttr(Attribute::GENERIC)) { extendDef->EnableAttr(Attribute::GENERIC); } VTableType vtable; vtable.emplace(&parentType, virtualFunc); extendDef->SetVTable(vtable); } void CreateExtendDefForImportedCustomTypeDef(Package& package, CHIRBuilder& builder, bool incr) { if (incr) { return; } /* codegen will create extension def according to CHIR's vtable, in order not to create duplicate extension def, codegen won't visit vtable from imported CustomTypeDef, these vtables are assumed that must be created in imported package. but there is a special case: ================ package A ================ public interface I {} open public class A {} ================ package B ================ import package A public class B <: A {} // extension def B_ed_A will be created in codegen ================ package C ================ import package A extend A <: I {} // extension def A_ed_I will be created in codegen ================ package D ================ import package A, B, C // extension def B_ed_I is needed, but there isn't in imported packages so we need to create extension def B_ed_I in current package, in order to deal with this case, a compiler added extend def is needed: [COMPILER_ADD] extend B <: I {} this def is create in current package, so extension def B_ed_I will be created in codegen */ for (auto def : package.GetAllImportedCustomTypeDef()) { if (def->IsExtend()) { continue; } for (const auto& it : def->GetVTable()) { if (ParentDefIsFromExtend(*def, *(it.first->GetClassDef()))) { CreateNewExtendDef(package, *def, *it.first, it.second, builder); continue; } } } } void ReplaceCustomTypeDefVtable(CustomTypeDef& def, const std::unordered_map& symbol) { auto vtable = def.GetVTable(); for (auto& vtableIt : vtable) { for (size_t i = 0; i < vtableIt.second.size(); ++i) { auto res = symbol.find(vtableIt.second[i].instance); if (res != symbol.end()) { vtableIt.second[i].instance = VirtualCast(res->second); } } } def.SetVTable(vtable); } void ReplaceCustomTypeDefAndExtendVtable(CustomTypeDef& def, const std::unordered_map& symbol) { ReplaceCustomTypeDefVtable(def, symbol); for (auto exDef : def.GetExtends()) { ReplaceCustomTypeDefVtable(*exDef, symbol); } } void ReplaceParentAndSubClassVtable(CustomTypeDef& def, const std::unordered_map& symbol, const std::unordered_map>& subClasses) { // replace self vtable ReplaceCustomTypeDefAndExtendVtable(def, symbol); if (!def.IsClassLike()) { return; } auto& classDef = StaticCast(def); auto it = subClasses.find(&classDef); if (it == subClasses.end()) { return; } // replace sub class vtable for (auto subClass : it->second) { ReplaceCustomTypeDefAndExtendVtable(*subClass, symbol); } } void ReplaceMethodAndStaticVar( const std::unordered_map>& replaceTable, const std::unordered_map>& subClasses) { for (auto& it : replaceTable) { auto methods = it.first->GetMethods(); for (size_t i = 0; i < methods.size(); ++i) { auto res = it.second.find(methods[i]); if (res != it.second.end()) { methods[i] = VirtualCast(res->second); } } it.first->SetMethods(methods); ReplaceParentAndSubClassVtable(*it.first, it.second, subClasses); auto staticVars = it.first->GetStaticMemberVars(); for (size_t i = 0; i < staticVars.size(); ++i) { auto res = it.second.find(staticVars[i]); if (res != it.second.end()) { staticVars[i] = VirtualCast(res->second); } } it.first->SetStaticMemberVars(staticVars); for (auto& it2 : it.second) { if (auto func = DynamicCast(it2.first)) { func->DestroySelf(); } else { VirtualCast(it2.first)->DestroySelf(); } } } } } bool IsEmptyInitFunc(Func& func) { if (func.GetFuncKind() != CHIR::FuncKind::GLOBALVAR_INIT) { return false; } bool isEmpty = true; auto preVisit = [&isEmpty](Expression& e) { if (e.GetExprKind() != CHIR::ExprKind::EXIT && e.GetExprKind() != CHIR::ExprKind::RAISE_EXCEPTION) { isEmpty = false; } return VisitResult::CONTINUE; }; Visitor::Visit(func, preVisit); return isEmpty; } static std::unordered_map> CollectSubClasses( const Package& pkg, CHIRBuilder& builder) { // parent sub std::unordered_map> subClasses; for (auto def : pkg.GetAllCustomTypeDef()) { for (auto parentType : def->GetSuperTypesRecusively(builder)) { subClasses[parentType->GetClassDef()].emplace(def); } } return subClasses; } namespace { void CreateSrcImportedFuncSymbol( CHIRBuilder& builder, Func& fn, std::unordered_map& srcCodeImportedFuncMap) { auto genericParamTy = fn.GetGenericTypeParams(); auto pkgName = fn.GetPackageName(); auto funcTy = fn.GetType(); auto mangledName = fn.GetIdentifierWithoutPrefix(); auto srcCodeName = fn.GetSrcCodeIdentifier(); auto rawMangledName = fn.GetRawMangledName(); auto importedFunc = builder.CreateImportedVarOrFunc( funcTy, mangledName, srcCodeName, rawMangledName, pkgName, genericParamTy); importedFunc->AppendAttributeInfo(fn.GetAttributeInfo()); importedFunc->SetFuncKind(fn.GetFuncKind()); if (auto hostFunc = fn.GetParamDftValHostFunc()) { auto it = srcCodeImportedFuncMap.find(StaticCast(hostFunc)); CJC_ASSERT(it != srcCodeImportedFuncMap.end()); importedFunc->SetParamDftValHostFunc(*it->second); } importedFunc->SetFastNative(fn.IsFastNative()); importedFunc->Set(Linkage::EXTERNAL); srcCodeImportedFuncMap.emplace(&fn, importedFunc); } void CreateSrcImportedVarSymbol( CHIRBuilder& builder, Value& gv, std::unordered_map& srcCodeImportedVarMap) { auto globalVar = VirtualCast(&gv); auto mangledName = globalVar->GetIdentifierWithoutPrefix(); auto srcCodeName = globalVar->GetSrcCodeIdentifier(); auto rawMangledName = globalVar->GetRawMangledName(); auto packageName = globalVar->GetPackageName(); auto ty = globalVar->GetType(); auto importedVar = builder.CreateImportedVarOrFunc(ty, mangledName, srcCodeName, rawMangledName, packageName); importedVar->AppendAttributeInfo(globalVar->GetAttributeInfo()); importedVar->Set(gv.Get()); srcCodeImportedVarMap.emplace(globalVar, importedVar); } void CreateSrcImpotedValueSymbol(const std::unordered_set& srcCodeImportedFuncs, const std::unordered_set& srcCodeImportedVars, CHIRBuilder& builder, std::unordered_map& srcCodeImportedFuncMap, std::unordered_map& srcCodeImportedVarMap) { for (auto func : builder.GetCurPackage()->GetGlobalFuncs()) { CJC_NULLPTR_CHECK(func); if (srcCodeImportedFuncs.find(func) != srcCodeImportedFuncs.end()) { CreateSrcImportedFuncSymbol(builder, *func, srcCodeImportedFuncMap); } } for (auto gv : builder.GetCurPackage()->GetGlobalVars()) { CJC_NULLPTR_CHECK(gv); if (srcCodeImportedVars.find(gv) != srcCodeImportedVars.end()) { CreateSrcImportedVarSymbol(builder, *gv, srcCodeImportedVarMap); } } } } void ToCHIR::ReplaceSrcCodeImportedValueWithSymbol() { std::unordered_set toBeRemovedFuncs; std::unordered_set toBeRemovedVars; std::unordered_map srcCodeImportedFuncMap; std::unordered_map srcCodeImportedVarMap; CreateSrcImpotedValueSymbol( srcCodeImportedFuncs, srcCodeImportedVars, builder, srcCodeImportedFuncMap, srcCodeImportedVarMap); for (auto lambda : uselessLambda) { for (auto user : lambda->GetUsers()) { user->RemoveSelfFromBlock(); } lambda->DestroySelf(); toBeRemovedFuncs.emplace(lambda); } for (auto def : uselessClasses) { for (auto func : def->GetMethods()) { for (auto user : func->GetUsers()) { user->RemoveSelfFromBlock(); } auto funcWithBody = StaticCast(func); funcWithBody->DestroySelf(); toBeRemovedFuncs.emplace(funcWithBody); } } std::vector newClasses; auto classes = chirPkg->GetClasses(); for (auto def : classes) { if (uselessClasses.find(def) == uselessClasses.end()) { newClasses.emplace_back(def); } } chirPkg->SetClasses(std::move(newClasses)); std::unordered_map> replaceTable; for (auto& it : srcCodeImportedFuncMap) { auto funcWithBody = it.first; auto importedSymbol = it.second; // Attributes may be added in the chir phase. For example, 'final' is added when a virtual table is created. In // this case, you need to append the attributes again. importedSymbol->AppendAttributeInfo(funcWithBody->GetAttributeInfo()); for (auto user : funcWithBody->GetUsers()) { user->ReplaceOperand(funcWithBody, importedSymbol); } if (auto parentDef = funcWithBody->GetParentCustomTypeDef()) { replaceTable[parentDef][funcWithBody] = importedSymbol; } toBeRemovedFuncs.emplace(funcWithBody); auto implicitIt = implicitFuncs.find(funcWithBody->GetIdentifierWithoutPrefix()); if (implicitIt != implicitFuncs.end()) { implicitIt->second = importedSymbol; } } for (auto& it : srcCodeImportedVarMap) { auto varWithInit = it.first; auto importedSymbol = it.second; // Attributes may be added in the chir phase. For example, 'final' is added when a virtual table is created. In // this case, you need to append the attributes again. importedSymbol->AppendAttributeInfo(varWithInit->GetAttributeInfo()); if (auto initFunc = varWithInit->GetInitFunc()) { for (auto user : initFunc->GetUsers()) { user->RemoveSelfFromBlock(); } initFunc->DestroySelf(); toBeRemovedFuncs.emplace(initFunc); } for (auto user : varWithInit->GetUsers()) { user->ReplaceOperand(varWithInit, importedSymbol); } if (auto parentDef = varWithInit->GetParentCustomTypeDef()) { replaceTable[parentDef][varWithInit] = importedSymbol; } toBeRemovedVars.emplace(varWithInit); } auto subClasses = CollectSubClasses(*chirPkg, builder); ReplaceMethodAndStaticVar(replaceTable, subClasses); std::vector globalFuncs; for (auto func : chirPkg->GetGlobalFuncs()) { if (toBeRemovedFuncs.find(func) != toBeRemovedFuncs.end()) { continue; } else if (IsEmptyInitFunc(*func)) { for (auto user : func->GetUsers()) { user->RemoveSelfFromBlock(); } func->DestroySelf(); continue; } globalFuncs.emplace_back(func); } chirPkg->SetGlobalFuncs(globalFuncs); std::vector globalVars; for (auto var : chirPkg->GetGlobalVars()) { if (toBeRemovedVars.find(var) == toBeRemovedVars.end()) { globalVars.emplace_back(var); } } chirPkg->SetGlobalVars(std::move(globalVars)); } void ToCHIR::RemoveUnusedImports(bool removeSrcCodeImported) { Utils::ProfileRecorder r{"CHIR", "RemoveUnusedImports"}; if (removeSrcCodeImported) { ReplaceSrcCodeImportedValueWithSymbol(); } UnusedImportRemover unusedImportRemover{ kind == IncreKind::INCR || opts.outputMode == GlobalOptions::OutputMode::CHIR, opts, implicitFuncs}; unusedImportRemover.Remove(*GetPackage()); CreateExtendDefForImportedCustomTypeDef(*GetPackage(), builder, kind == IncreKind::INCR); DumpCHIRDebug("RemoveUnusedImports"); } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/Transformation/SanitizerCoverage.cpp000066400000000000000000001612071510705540100254700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/SanitizerCoverage.h" #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/ConstantUtils.h" #include "cangjie/CHIR/DiagAdapter.h" #include "cangjie/CHIR/IntrinsicKind.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Visitor/Visitor.h" #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Utils/CastingTemplate.h" #include "cangjie/Utils/SafePointer.h" namespace Cangjie::CHIR { namespace { Ptr CreateNonMemberApply(Ptr resultTy, Ptr callee, const std::vector& args, Ptr parent, CHIRBuilder& builder, DebugLocation loc = INVALID_LOCATION) { return builder.CreateExpression(loc, resultTy, callee, FuncCallContext{.args = args}, parent); } Ptr CreateMemberApply(Ptr resultTy, Ptr callee, Ptr thisType, const std::vector& args, Ptr parent, CHIRBuilder& builder, DebugLocation loc = INVALID_LOCATION) { return builder.CreateExpression(loc, resultTy, callee, FuncCallContext{ .args = args, .thisType = thisType}, parent); } } // namespace static const std::string CHAR_ARRAY_TEST_PACK = "CharArrayTest"; /* brief: create a bool array, which is used when sanitizer-coverage-inline-bool-flag is available. param: uint64_t n, size of bool array. return: bool*, return bool array. */ static const std::string SanCovBoolFlagInitName = "__cj_sancov_bool_flag_ctor"; /* brief: create a char array, which is used when sanitizer-coverage-inline-8bit-counters is available. param: uint64_t n, size of char array. return: char*, return char array. */ static const std::string SanCovCharArrayInitName = "__cj_sancov_8bit_counters_ctor"; /* brief: create a uint32 array, which is used when sanitizer-coverage-trace-pc-guard is available. param: uint64_t n, size of uint32 array. return: unsigned int* , return uint32 array. */ static const std::string SanCovUInt32ArrayInitName = "__cj_sancov_pc_guard_ctor"; static const std::string SAN_COV_PC_GUARD = "__sanitizer_cov_trace_pc_guard"; /* brief: this func is used when sanitizer-coverage-trace-compares is available. param: uint Arg1, uint Arg2 return: void. */ static const std::string SanCovTraceCmp1 = "__sanitizer_cov_trace_cmp1"; static const std::string SanCovTraceCmp2 = "__sanitizer_cov_trace_cmp2"; static const std::string SanCovTraceCmp4 = "__sanitizer_cov_trace_cmp4"; static const std::string SanCovTraceCmp8 = "__sanitizer_cov_trace_cmp8"; static const std::string SanCovTraceConstCmp1 = "__sanitizer_cov_trace_const_cmp1"; static const std::string SanCovTraceConstCmp2 = "__sanitizer_cov_trace_const_cmp2"; static const std::string SanCovTraceConstCmp4 = "__sanitizer_cov_trace_const_cmp4"; static const std::string SanCovTraceConstCmp8 = "__sanitizer_cov_trace_const_cmp8"; /* brief: this func is used when sanitizer-coverage-trace-memcmp is available. param: void *called_pc, const void *s1, const void *s2, size_t n, int result return: void. */ static const std::string SAN_COV_TRACE_MEM_CMP = "__cj_sanitizer_weak_hook_memcmp"; static const std::string SAN_COV_TRACE_STRN_CMP = "__cj_sanitizer_weak_hook_strncmp"; static const std::string SAN_COV_TRACE_STR_CMP = "__cj_sanitizer_weak_hook_strcmp"; static const std::string SAN_COV_TRACE_STR_CASE_CMP = "__cj_sanitizer_weak_hook_strcasecmp"; /* brief: this func is used when sanitizer-coverage-trace-compares is available. param: uint64_t Val, uint64_t* Cases. n indicates size of uint64 array cases[0] count of cases array, cases[1] = 64, cases[2..n] value of case return: void */ static const std::string SanCovTraceSwitchName = "__sanitizer_cov_trace_switch"; /* brief: create a uint64 array and a CString array, which is used when sanitizer-coverage-pc-table is available. param: uint64_t n, uint64_t* funcBBCountTable , int8_t** funcNameTable. n indicates size of uint64 array and CString array, start address of uint64 array, start address of CString array. return: void */ static const std::string SanCovCreatePCTable = "__cj_sancov_pcs_init"; static const std::string SanCovUpdateStackDepth = "__cj_sancov_update_stack_depth"; static const std::unordered_map SAN_COV_CTOR2_GLOBAL_VAR_NAME = { {SanCovBoolFlagInitName, "sancov$_bool_flag"}, {SanCovCharArrayInitName, "sancov$_8bit_counters"}, {SanCovUInt32ArrayInitName, "sancov$_pc_guard"}}; static const std::unordered_map STRING_FUNC_NAME2_SAN_COV_FUNC = { {"==", SAN_COV_TRACE_MEM_CMP}, {"startsWith", SAN_COV_TRACE_MEM_CMP}, {"endsWith", SAN_COV_TRACE_MEM_CMP}, {"indexOf", SAN_COV_TRACE_MEM_CMP}, {"replace", SAN_COV_TRACE_MEM_CMP}, {"contains", SAN_COV_TRACE_MEM_CMP}}; static const std::unordered_map CSTRING_FUNC_NAME2_SAN_COV_FUNC = { {"equals", SAN_COV_TRACE_STR_CMP}, {"startsWith", SAN_COV_TRACE_MEM_CMP}, {"endsWith", SAN_COV_TRACE_STRN_CMP}, {"compare", SAN_COV_TRACE_STR_CMP}, {"equalsLower", SAN_COV_TRACE_STR_CASE_CMP}}; static const std::unordered_set HAS_N_PARAMETER_MEM_CMP_SET = { SAN_COV_TRACE_MEM_CMP, SAN_COV_TRACE_STRN_CMP}; enum class MemCmpType { STRING_TYPE, CSTRING_TYPE, ARRAY_TYPE, ARRAYLIST_TYPE }; std::optional GetStringSanConvFunc(MemCmpType cmpType, const std::string& funcName) { if (cmpType == MemCmpType::STRING_TYPE && STRING_FUNC_NAME2_SAN_COV_FUNC.count(funcName) != 0) { return STRING_FUNC_NAME2_SAN_COV_FUNC.at(funcName); } if (cmpType == MemCmpType::CSTRING_TYPE && CSTRING_FUNC_NAME2_SAN_COV_FUNC.count(funcName) != 0) { return CSTRING_FUNC_NAME2_SAN_COV_FUNC.at(funcName); } if (cmpType == MemCmpType::ARRAY_TYPE && funcName == "==") { return SAN_COV_TRACE_MEM_CMP; } if (cmpType == MemCmpType::ARRAYLIST_TYPE && funcName == "==") { return SAN_COV_TRACE_MEM_CMP; } return std::nullopt; } Type::TypeKind GetTraceCompareType(Type::TypeKind kind) { switch (kind) { case Type::TypeKind::TYPE_INT64: case Type::TypeKind::TYPE_UINT64: { return Type::TypeKind::TYPE_UINT64; } case Type::TypeKind::TYPE_INT32: case Type::TypeKind::TYPE_UINT32: { return Type::TypeKind::TYPE_UINT32; } case Type::TypeKind::TYPE_INT16: case Type::TypeKind::TYPE_UINT16: { return Type::TypeKind::TYPE_UINT16; } case Type::TypeKind::TYPE_INT8: case Type::TypeKind::TYPE_UINT8: { return Type::TypeKind::TYPE_UINT8; } default: break; }; return Type::TypeKind::TYPE_UNIT; } std::string GetTraceCompareSyscallName(const BinaryExpression& binaryOp) { static const std::unordered_map COMPARE_MAP = { {Type::TypeKind::TYPE_UINT8, SanCovTraceCmp1}, {Type::TypeKind::TYPE_UINT16, SanCovTraceCmp2}, {Type::TypeKind::TYPE_UINT32, SanCovTraceCmp4}, {Type::TypeKind::TYPE_UINT64, SanCovTraceCmp8}, }; static const std::unordered_map COMPARE_CONST_MAP = { {Type::TypeKind::TYPE_UINT8, SanCovTraceConstCmp1}, {Type::TypeKind::TYPE_UINT16, SanCovTraceConstCmp2}, {Type::TypeKind::TYPE_UINT32, SanCovTraceConstCmp4}, {Type::TypeKind::TYPE_UINT64, SanCovTraceConstCmp8}, }; Type::TypeKind curSize = GetTraceCompareType(binaryOp.GetLHSOperand()->GetType()->GetTypeKind()); std::string syscallName; bool hasArgsConst = binaryOp.GetLHSOperand()->GetType()->IsConstant() || binaryOp.GetRHSOperand()->GetType()->IsConstant(); if (hasArgsConst) { syscallName = COMPARE_CONST_MAP.at(curSize); } else { syscallName = COMPARE_MAP.at(curSize); } return syscallName; } SanitizerCoverage::SanitizerCoverage(const GlobalOptions& option, CHIRBuilder& builder) : sanCovOption(option.sancovOption), builder(builder) { } void SanitizerCoverage::InitFuncBag(const Package& package) { // only need func, and not need generic func auto importedVarAndFuncs = package.GetImportedVarAndFuncs(); for (auto v : importedVarAndFuncs) { funcBag.emplace(v->GetIdentifierWithoutPrefix(), v); } } bool SanitizerCoverage::RunOnPackage(const Ptr& package, DiagAdapter& diag, bool isDebug) { if (!CheckSancovOption(diag)) { return false; } InitFuncBag(*package); packageName = package->GetName(); for (auto func : package->GetGlobalFuncs()) { // skip global init function and compiled add function if (func->IsGVInit() || func->TestAttr(Attribute::COMPILER_ADD) || func->GetPackageName() != package->GetName()) { continue; } // skip literal function, need fix after CHIR is on std::string targetSuffix = SPECIAL_NAME_FOR_INIT_LITERAL_FUNCTION + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX; if (func->GetIdentifier().find(targetSuffix) == func->GetIdentifier().size() - targetSuffix.size()) { continue; } RunOnFunc(func, isDebug); } GenerateInitFunc(*package->GetPackageInitFunc(), isDebug); return true; } void SanitizerCoverage::RunOnFunc(const Ptr& func, bool isDebug) { if (sanCovOption.traceCmp) { auto visitAction = [this, isDebug](Expression& expr) { if (expr.GetExprKind() == ExprKind::MULTIBRANCH) { // only support switch int and char auto mb = StaticCast(&expr); InjectTraceForSwitch(*mb, isDebug); } if (expr.IsBinaryExpr()) { auto binary = StaticCast(&expr); InjectTraceForCmp(*binary, isDebug); } return VisitResult::CONTINUE; }; Visitor::Visit(*func, visitAction); } if (sanCovOption.traceMemCmp) { auto visitAction = [this, isDebug](Expression& expr) { InjectTraceMemCmp(expr, isDebug); return VisitResult::CONTINUE; }; Visitor::Visit(*func, visitAction); } bool isCoverageOpen = sanCovOption.inlineBoolFlag || sanCovOption.inline8bitCounters || sanCovOption.tracePCGuard || sanCovOption.stackDepth; if (!isCoverageOpen) { return; } bool isBasicBlockLevel = sanCovOption.coverageType == GlobalOptions::SanitizerCoverageOptions::Type::SCK_BB || sanCovOption.coverageType == GlobalOptions::SanitizerCoverageOptions::Type::SCK_UNKNOW; for (auto block : func->GetBody()->GetBlocks()) { if (isBasicBlockLevel || block->IsEntry()) { /* 3 situations to insert fuzz fuctions 1. insert to all blocks when basic block level is used by users 2. insert to entry blocks when function level is used by users 3. stack depth only insert to entry block */ InsertCoverageAheadBlock(*block, isDebug); } } } bool SanitizerCoverage::CheckSancovOption(DiagAdapter& diag) const { bool isSancov = sanCovOption.inline8bitCounters || sanCovOption.inlineBoolFlag || sanCovOption.tracePCGuard; bool isSancovLevelVaild = sanCovOption.coverageType == GlobalOptions::SanitizerCoverageOptions::Type::SCK_FUNCTION || sanCovOption.coverageType == GlobalOptions::SanitizerCoverageOptions::Type::SCK_BB; // Rule: if pc-table option is available, one of inline8bitCounters, inlineBoolFlag, or tracePCGuard must also be // used. if (!isSancov && sanCovOption.pcTable) { diag.Diagnose(DiagKind::chir_sancov_illegal_usage_of_pc_table); return false; } if (isSancov && sanCovOption.coverageType == GlobalOptions::SanitizerCoverageOptions::Type::SCK_NONE) { diag.Diagnose(DiagKind::chir_sancov_illegal_usage_of_level); return false; } if (!isSancov && isSancovLevelVaild) { diag.Diagnose(DiagKind::chir_sancov_illegal_usage_of_level); return false; } return true; } void SanitizerCoverage::InjectTraceForCmp(BinaryExpression& binary, bool isDebug) { // We need to inject the trace info for all the comparison operations. static std::unordered_set cmpToken = { ExprKind::LT, ExprKind::GT, ExprKind::LE, ExprKind::GE, ExprKind::NOTEQUAL, ExprKind::EQUAL}; if (cmpToken.find(binary.GetExprKind()) == cmpToken.end()) { return; } auto lhs = binary.GetLHSOperand(); auto rhs = binary.GetRHSOperand(); auto curSize = GetTraceCompareType(lhs->GetType()->GetTypeKind()); if (curSize == Type::TypeKind::TYPE_UNIT) { return; } /* Add __sanitizer_cov_trace_const_cmp() function to cmp expression before: %0 = APPLY() %1 = APPLY() %2 = Equal(%0, %1) after %0 = APPLY() %1 = APPLY() %n = __sanitizer_cov_trace_const_cmpsize(arg1, arg2) // add function here %2 = Equal(%0, %1) */ auto parent = binary.GetParentBlock(); if (auto intTy = StaticCast(lhs->GetType()); intTy && intTy->IsSigned()) { // Note: If the operand type is int8, convert it to uint8. int16, int32, int64 is similar. auto unsignedType = builder.GetType(GetTraceCompareType(lhs->GetType()->GetTypeKind())); auto lhsCast = builder.CreateExpression(binary.GetDebugLocation(), unsignedType, lhs, parent); auto rhsCast = builder.CreateExpression(binary.GetDebugLocation(), unsignedType, rhs, parent); lhs = lhsCast->GetResult(); rhs = rhsCast->GetResult(); lhsCast->MoveBefore(&binary); rhsCast->MoveBefore(&binary); } auto callName = GetTraceCompareSyscallName(binary); auto funcTy = builder.GetType(std::vector{lhs->GetType(), rhs->GetType()}, builder.GetUnitTy(), /* hasVarLenParam = */ false, /* isCFunc = */ true); auto callee = GenerateForeignFunc(callName, binary.GetDebugLocation(), *funcTy, packageName); auto syscall = CreateNonMemberApply( builder.GetUnitTy(), callee, std::vector{lhs, rhs}, parent, builder, binary.GetDebugLocation()); syscall->MoveBefore(&binary); if (isDebug) { std::cout << "[SanitizerCoverage] Add trace compares" << ToPosInfo(binary.GetDebugLocation()) << ".\n"; } } void SanitizerCoverage::InjectTraceForSwitch(MultiBranch& mb, bool isDebug) { /* Inject trace for switch before: %1: UInt64 = Apply(...) MultiBranch(%1, #x, [1, #y], [2, #z]) after: %1: UInt64 = Apply(...) %2: UInt64 = Constant(2) // switch case size %3: UInt64 = Constant(64) // switch value bit size %4: UInt64 = Constant(a) // switch case 1 %5: UInt64 = Constant(b) // switch case 2 %6: UInt64 = Constant(4) // list size %7: RawArray& = RawArrayAllocate(UInt64, %6) // raw array for second param %8: Unit = RawArrayLiteralInit(%7, %2, %3, %4, %5) %9: CPointer = Intrinsic/acquireRawData(%7) // acquire c pointer %10: UInt64 = TypeCast(%9, UInt64) // cast switch value to uint64 %11: Unit = Apply(@__sanitizer_cov_trace_switch, %10, %7) // apply sanitizer cov trace MultiBranch(%1, #x, [1, #y], [2, #z]) */ if (!mb.GetCondition()->GetType()->IsInteger() && !mb.GetCondition()->GetType()->IsRune()) { return; } auto& callName = SanCovTraceSwitchName; const auto& u64Ty = builder.GetUInt64Ty(); auto cPointTy = builder.GetType(u64Ty); // 1. create trace function type for switch auto funcTy = builder.GetType(std::vector{u64Ty, cPointTy}, builder.GetUnitTy(), false, true); auto callee = GenerateForeignFunc(callName, mb.GetDebugLocation(), *funcTy, packageName); auto parent = mb.GetParentBlock(); // 2. generate lit value list from switch case list auto caseValList = CreateArrayForSwitchCaseList(mb); caseValList->MoveBefore(&mb); // 3. create acquireRawData function auto rawDataAcquire = CreateRawDataAcquire(*caseValList, *u64Ty); rawDataAcquire->MoveBefore(&mb); // 4. create cast if need auto switchVal = mb.GetCondition(); if (switchVal->GetType()->GetTypeKind() != Type::TypeKind::TYPE_UINT64) { auto typeCast = builder.CreateExpression(mb.GetDebugLocation(), u64Ty, switchVal, parent); switchVal = typeCast->GetResult(); typeCast->MoveBefore(&mb); } // 5. create and insert trace function auto syscall = CreateNonMemberApply(builder.GetUnitTy(), callee, std::vector{switchVal, rawDataAcquire->GetResult()}, parent, builder, mb.GetDebugLocation()); syscall->MoveBefore(&mb); if (isDebug) { std::cout << "[SanitizerCoverage] Add trace switch" << ToPosInfo(mb.GetDebugLocation()) << ".\n"; } } std::vector SanitizerCoverage::GenerateCStringMemCmp( const std::string& fuzzName, Value& oper1, Value& oper2, Apply& apply) { /* before: %c: Bool = Apply(@_CNatX7CString6equalsHk, %a, %b) after %1: Int64 = Apply(@_CNatX7CString4sizeHv, %a) %2: UInt32 = TypeCast(%1, UInt32) %3: CPointer = Intrinsic/convertCStr2Ptr(%a) %4: CPointer = Intrinsic/convertCStr2Ptr(%b) %5: Unit = Apply(@__cj_sanitizer_weak_hook_strcmp, %3, %4, %2) %c: Bool = Apply(@_CNatX7CString6equalsHk, %a, %b) */ auto parent = apply.GetParentBlock(); auto loc = apply.GetDebugLocation(); std::vector res; auto cPointerType = builder.GetType(builder.GetUInt8Ty()); auto callContext1 = IntrisicCallContext { .kind = IntrinsicKind::CSTRING_CONVERT_CSTR_TO_PTR, .args = std::vector{&oper1} }; auto cPointer = builder.CreateExpression(loc, cPointerType, callContext1, parent); cPointer->MoveBefore(&apply); auto callContext2 = IntrisicCallContext { .kind = IntrinsicKind::CSTRING_CONVERT_CSTR_TO_PTR, .args = std::vector{&oper2} }; auto cPointer2 = builder.CreateExpression(loc, cPointerType, callContext2, parent); cPointer2->MoveBefore(&apply); if (fuzzName == SAN_COV_TRACE_MEM_CMP) { // __cj_sanitizer_weak_hook_memcmp function need void* input auto typeTarget = builder.GetType(builder.GetVoidTy()); auto callContext3 = IntrisicCallContext { .kind = IntrinsicKind::CPOINTER_INIT1, .args = std::vector{cPointer->GetResult()} }; cPointer = builder.CreateExpression(loc, typeTarget, callContext3, parent); cPointer->MoveBefore(&apply); auto callContext4 = IntrisicCallContext { .kind = IntrinsicKind::CPOINTER_INIT1, .args = std::vector{cPointer2->GetResult()} }; cPointer2 = builder.CreateExpression(loc, typeTarget, callContext4, parent); cPointer2->MoveBefore(&apply); } res.push_back(cPointer->GetResult()); res.push_back(cPointer2->GetResult()); if (HAS_N_PARAMETER_MEM_CMP_SET.count(fuzzName) != 0) { auto getSize = GetImportedFunc(FUNC_MANGLE_NAME_CSTRING_SIZE); auto sizeN = CreateMemberApply( builder.GetInt64Ty(), getSize, oper1.GetType(), {&oper1}, parent, builder, loc); sizeN->MoveBefore(&apply); auto sizeNCasted = builder.CreateExpression(loc, builder.GetUInt32Ty(), sizeN->GetResult(), parent); sizeNCasted->MoveBefore(&apply); res.push_back(sizeNCasted->GetResult()); } return res; } Expression* SanitizerCoverage::CreateOneCPointFromList(Value& array, Apply& apply, Type& elementType, Type& startType) { /* generate cpointer data from cj array: auto cPointerBase = acquire_raw_data(array.rawptr) auto cPointeroffset = array.start cPointer = cPointerBase + cPointeroffset */ auto parent = apply.GetParentBlock(); auto& loc = apply.GetDebugLocation(); auto rawArrayType = builder.GetType(builder.GetType(&elementType, 1U)); auto rawArray = builder.CreateExpression(loc, rawArrayType, &array, std::vector{0UL}, parent); rawArray->MoveBefore(&apply); auto start = builder.CreateExpression(loc, &startType, &array, std::vector{1UL}, parent); start->MoveBefore(&apply); auto startRes = start->GetResult(); if (startRes->GetType() != builder.GetInt64Ty()) { auto castToInt64 = builder.CreateExpression(loc, builder.GetInt64Ty(), startRes, parent); castToInt64->MoveBefore(&apply); startRes = castToInt64->GetResult(); } auto origPointerType = builder.GetType(&elementType); auto callContext1 = IntrisicCallContext { .kind = IntrinsicKind::ARRAY_ACQUIRE_RAW_DATA, .args = std::vector{rawArray->GetResult()}, .instTypeArgs = std::vector{&elementType} }; auto cPointerAcquire = builder.CreateExpression(loc, origPointerType, callContext1, parent); cPointerAcquire->MoveBefore(&apply); auto callContext2 = IntrisicCallContext { .kind = IntrinsicKind::CPOINTER_ADD, .args = std::vector{cPointerAcquire->GetResult(), startRes}, .instTypeArgs = std::vector{&elementType} }; auto pointAdd = builder.CreateExpression(loc, origPointerType, callContext2, parent); pointAdd->MoveBefore(&apply); return pointAdd; } std::vector SanitizerCoverage::GenerateStringMemCmp( const std::string& fuzzName, Value& oper1, Value& oper2, Apply& apply) { /* before: %c: Bool = Apply(@_CNat6String2==ERNat6StringE, %a, %b) after: %1: Struct-_CNat5ArrayIhE = Field(%a, 0) %2: Struct-_CNat5ArrayIhE = Field(%b, 0) %3: RawArray& = Field(%1, 0) %4: RawArray& = Field(%2, 0) %5: Int64 = Field(%1, 1) // offset of array %6: Int64 = Field(%2, 1) %7: CPointer = Intrinsic/acquireRawData(%3) %8: CPointer = Intrinsic/acquireRawData(%4) %9: CPointer = Intrinsic/addPointer(%7, %5) // for cj array implement, add offset %10: CPointer = Intrinsic/addPointer(%8, %6) %11: Int64 = Field(%1, 2) // get size of string array %12: UInt32 = TypeCast(%11, UInt32) %13: Unit = Apply(@__cj_sanitizer_weak_hook_memcmp, %9, %10, %12) %c: Bool = Apply(@_CNat6String2==ERNat6StringE, %a, %b) */ std::vector res; auto parent = apply.GetParentBlock(); auto& loc = apply.GetDebugLocation(); auto cPoint1 = CreateOneCPointFromList(oper1, apply, *builder.GetUInt8Ty(), *builder.GetUInt32Ty()); auto cPoint2 = CreateOneCPointFromList(oper2, apply, *builder.GetUInt8Ty(), *builder.GetUInt32Ty()); if (fuzzName == SAN_COV_TRACE_MEM_CMP) { // __cj_sanitizer_weak_hook_memcmp function need void* input auto typeTarget = builder.GetType(builder.GetVoidTy()); auto callContext1 = IntrisicCallContext { .kind = IntrinsicKind::CPOINTER_INIT1, .args = std::vector{cPoint1->GetResult()} }; cPoint1 = builder.CreateExpression(loc, typeTarget, callContext1, parent); cPoint1->MoveBefore(&apply); auto callContext2 = IntrisicCallContext { .kind = IntrinsicKind::CPOINTER_INIT1, .args = std::vector{cPoint2->GetResult()} }; cPoint2 = builder.CreateExpression(loc, typeTarget, callContext2, parent); cPoint2->MoveBefore(&apply); } res.push_back(cPoint1->GetResult()); res.push_back(cPoint2->GetResult()); if (HAS_N_PARAMETER_MEM_CMP_SET.count(fuzzName) != 0) { auto sizeN = builder.CreateExpression( loc, builder.GetUInt32Ty(), &oper1, std::vector{2}, parent); sizeN->MoveBefore(&apply); res.push_back(sizeN->GetResult()); } return res; } uint64_t GetMultipleFromType(const Type& type) { CJC_ASSERT(type.IsInteger() || type.IsFloat()); // change type to uint8, recalculate the array size = n * bits / 8U. return StaticCast(&type)->GetBitness() / 8U; } std::vector SanitizerCoverage::GenerateArrayCmp( const std::string& fuzzName, Value& oper1, Value& oper2, Apply& apply) { /* before: %c: Bool = Apply(@_CNat6ExtendY_5ArrayIl2==ERNat5ArrayIlE, %a, %a) after: %1: RawArray& = Field(%a, 0) // get rawptr from array %2: RawArray& = Field(%b, 0) %3: Int64 = Field(%a, 1) // offset of array %4: Int64 = Field(%b, 1) %5: CPointer = Intrinsic/acquireRawData(%1) %6: CPointer = Intrinsic/acquireRawData(%2) %7: CPointer = Intrinsic/addPointer(%5, %3) // for cj array implement, add offset %8: CPointer = Intrinsic/addPointer(%6, %4) %9: CPointer = Intrinsic/pointerInit1(%7) // type cast to uint8 %10: CPointer = Intrinsic/pointerInit1(%8) %11: Int64 = Field(%a, 2) // get size %12: UInt32 = TypeCast(%11, UInt32) %13: Unit = Apply(@__cj_sanitizer_weak_hook_memcmp, %9, %10, %12) %c: Bool = Apply(@_CNat6Extendat5ArrayIl2==ERNat5ArrayIlE, %a, %b) */ std::vector res; auto parent = apply.GetParentBlock(); auto& loc = apply.GetDebugLocation(); CJC_ASSERT(oper1.GetType()->GetTypeKind() == Type::TypeKind::TYPE_STRUCT); auto arrayType = StaticCast(oper1.GetType()); CJC_ASSERT(arrayType->GetGenericArgs().size() == 1); auto elementType = arrayType->GetGenericArgs()[0]; auto cPointer1 = CreateOneCPointFromList(oper1, apply, *elementType, *builder.GetInt64Ty()); auto cPointer2 = CreateOneCPointFromList(oper2, apply, *elementType, *builder.GetInt64Ty()); auto typeTarget = builder.GetType(builder.GetUInt8Ty()); if (fuzzName == SAN_COV_TRACE_MEM_CMP) { typeTarget = builder.GetType(builder.GetVoidTy()); } // __cj_sanitizer_weak_hook_memcmp function need void* input auto callContext1 = IntrisicCallContext { .kind = IntrinsicKind::CPOINTER_INIT1, .args = std::vector{cPointer1->GetResult()} }; auto typeCast1 = builder.CreateExpression(loc, typeTarget, callContext1, parent); typeCast1->MoveBefore(&apply); auto callContext2 = IntrisicCallContext { .kind = IntrinsicKind::CPOINTER_INIT1, .args = std::vector{cPointer2->GetResult()} }; auto typeCast2 = builder.CreateExpression(loc, typeTarget, callContext2, parent); typeCast2->MoveBefore(&apply); res.push_back(typeCast1->GetResult()); res.push_back(typeCast2->GetResult()); if (HAS_N_PARAMETER_MEM_CMP_SET.count(fuzzName) != 0) { auto sizeN = builder.CreateExpression(loc, builder.GetInt64Ty(), &oper1, std::vector{2}, parent); sizeN->MoveBefore(&apply); auto multiple = builder.CreateConstantExpression( loc, builder.GetInt64Ty(), parent, GetMultipleFromType(*elementType)); multiple->MoveBefore(&apply); auto calSize = builder.CreateExpression(loc, builder.GetInt64Ty(), ExprKind::MUL, sizeN->GetResult(), multiple->GetResult(), OverflowStrategy::WRAPPING, parent); calSize->MoveBefore(&apply); auto sizeNCasted = builder.CreateExpression(loc, builder.GetUInt32Ty(), calSize->GetResult(), parent); sizeNCasted->MoveBefore(&apply); res.push_back(sizeNCasted->GetResult()); } return res; } std::pair SanitizerCoverage::CastArrayListToArray(Value& oper1, Value& oper2, Apply& apply) { /* Get array from arrayList %a: Class-_CNac9ArrayListIlE&, %b: lass-_CNac9ArrayListIlE& %1: Struct-_CNat5ArrayIlE& = GetElementRef(%a, 0) %2: Struct-_CNat5ArrayIlE& = GetElementRef(%b, 0) %3: Struct-_CNat5ArrayIlE = Load(%1) %4: Struct-_CNat5ArrayIlE = Load(%2) */ auto& loc = apply.GetDebugLocation(); auto refType = StaticCast(oper1.GetType()); auto rawArrayType = StaticCast(refType->GetBaseType()); CJC_ASSERT(rawArrayType->GetGenericArgs().size() == 1); auto elementType = rawArrayType->GetGenericArgs()[0]; // Get Array type from builder auto arrayGeneric = builder.GetStructType("std.core", "Array"); CJC_NULLPTR_CHECK(arrayGeneric); CJC_ASSERT(arrayGeneric->GetGenericArgs().size() == 1); auto genericType = StaticCast(arrayGeneric->GetGenericArgs()[0]); std::unordered_map table{{genericType, elementType}}; auto arrayType = ReplaceRawGenericArgType(*arrayGeneric, table, builder); auto arrayRefType = builder.GetType(arrayType); auto parent = apply.GetParentBlock(); auto arrayRef1 = builder.CreateExpression(loc, arrayRefType, &oper1, std::vector{0}, parent); arrayRef1->MoveBefore(&apply); auto arrayRef2 = builder.CreateExpression(loc, arrayRefType, &oper2, std::vector{0}, parent); arrayRef2->MoveBefore(&apply); auto array1 = builder.CreateExpression(loc, arrayType, arrayRef1->GetResult(), parent); array1->MoveBefore(&apply); auto array2 = builder.CreateExpression(loc, arrayType, arrayRef2->GetResult(), parent); array2->MoveBefore(&apply); return std::make_pair(array1->GetResult(), array2->GetResult()); } Expression* SanitizerCoverage::CreateMemCmpFunc(const std::string& intrinsicName, Type& paramsType, const std::vector& params, const DebugLocation& loc, Block* parent) { // create String or array memory fuzz function with its name and params std::vector paramType{¶msType, ¶msType}; if (HAS_N_PARAMETER_MEM_CMP_SET.count(intrinsicName) != 0) { paramType.push_back(builder.GetUInt32Ty()); } auto funcTy = builder.GetType(paramType, builder.GetUnitTy(), false, true); auto memCmpFunc = GenerateForeignFunc(intrinsicName, loc, *funcTy, packageName); return CreateNonMemberApply(builder.GetUnitTy(), memCmpFunc, params, parent, builder, loc); } std::pair> SanitizerCoverage::GetMemFuncSymbols( Value& oper1, Value& oper2, Apply& apply) { std::optional intrinsicName{""}; std::vector params; std::pair> defaultValue = {"", {}}; auto applyCallName = apply.GetCallee()->GetSrcCodeIdentifier(); if (oper1.GetType()->GetTypeKind() == Type::TypeKind::TYPE_CSTRING) { intrinsicName = GetStringSanConvFunc(MemCmpType::CSTRING_TYPE, applyCallName); if (intrinsicName == std::nullopt) { return defaultValue; } params = GenerateCStringMemCmp(intrinsicName.value(), oper1, oper2, apply); } else if (oper1.GetType()->GetTypeKind() == Type::TypeKind::TYPE_STRUCT) { auto structType = StaticCast(oper1.GetType()); if (structType->GetStructDef()->GetPackageName() != "std.core") { return defaultValue; } else if (structType->GetStructDef()->GetSrcCodeIdentifier() == "String") { intrinsicName = GetStringSanConvFunc(MemCmpType::STRING_TYPE, applyCallName); if (intrinsicName == std::nullopt) { return defaultValue; } params = GenerateStringMemCmp(intrinsicName.value(), oper1, oper2, apply); } else if (structType->GetStructDef()->GetSrcCodeIdentifier() == "Array") { intrinsicName = GetStringSanConvFunc(MemCmpType::ARRAY_TYPE, applyCallName); if (intrinsicName == std::nullopt) { return defaultValue; } params = GenerateArrayCmp(intrinsicName.value(), oper1, oper2, apply); } } else if (oper1.GetType()->GetTypeKind() == Type::TypeKind::TYPE_REFTYPE) { auto refType = StaticCast(oper1.GetType()); if (refType->GetBaseType()->GetTypeKind() != Type::TypeKind::TYPE_CLASS) { return defaultValue; } auto classType = StaticCast(refType->GetBaseType()); if (classType->GetClassDef()->GetPackageName() != "std.collection" || classType->GetClassDef()->GetSrcCodeIdentifier() != "ArrayList") { return defaultValue; } intrinsicName = GetStringSanConvFunc(MemCmpType::ARRAYLIST_TYPE, applyCallName); if (intrinsicName == std::nullopt) { return defaultValue; } auto [cast0, cast1] = CastArrayListToArray(oper1, oper2, apply); params = GenerateArrayCmp(intrinsicName.value(), *cast0, *cast1, apply); } return std::make_pair(intrinsicName.value(), params); } void SanitizerCoverage::InjectTraceMemCmp(Expression& expr, bool isDebug) { if (expr.GetExprKind() != ExprKind::APPLY) { return; } auto applyNode = StaticCast(&expr); if (applyNode->GetOperands().size() < 3U) { return; } auto oper1 = applyNode->GetOperand(1U); auto oper2 = applyNode->GetOperand(2U); auto [intrinsicName, params] = GetMemFuncSymbols(*oper1, *oper2, *applyNode); if (intrinsicName == "" || params.empty()) { return; } Expression* syscall; auto parent = expr.GetParentBlock(); if (intrinsicName == SAN_COV_TRACE_MEM_CMP) { auto voidPointerType = builder.GetType(builder.GetVoidTy()); syscall = CreateMemCmpFunc(intrinsicName, *voidPointerType, params, expr.GetDebugLocation(), parent); } else { auto charPointerType = builder.GetType(builder.GetUInt8Ty()); syscall = CreateMemCmpFunc(intrinsicName, *charPointerType, params, expr.GetDebugLocation(), parent); } syscall->MoveBefore(&expr); if (isDebug) { std::cout << "[SanitizerCoverage] Add trace Memory Compare" << ToPosInfo(expr.GetDebugLocation()) << ".\n"; } } void SanitizerCoverage::InsertCoverageAheadBlock(Block& block, bool isDebug) { if (sanCovOption.pcTable) { CJC_ASSERT(block.GetTopLevelFunc()); auto blockLocation = block.GetTopLevelFunc()->GetDebugLocation(); for (auto it : block.GetExpressions()) { if (!it->GetDebugLocation().IsInvalidPos()) { blockLocation = it->GetDebugLocation(); break; } } pcArray.emplace_back(std::make_pair(block.GetTopLevelFunc()->GetSrcCodeIdentifier(), blockLocation)); } CJC_ASSERT(block.GetExpressions().size() > 0); auto callList = GenerateCoverageCallByOption(INVALID_LOCATION, isDebug, &block); if (sanCovOption.stackDepth && block.IsEntry()) { auto stackDepth = GenerateStackDepthExpr(INVALID_LOCATION, isDebug, &block); callList.insert(callList.end(), stackDepth.begin(), stackDepth.end()); } auto firstExpr = block.GetExpressions()[0]; for (auto call : callList) { call->MoveBefore(firstExpr); } bbCounter++; } RawArrayAllocate* SanitizerCoverage::CreateArrayForSwitchCaseList(MultiBranch& multiBranch) { const auto& cases = multiBranch.GetCaseVals(); // case[0 - n] // case[0] means switch case size // case[1] means bit size of switch condition type // case[2 - n] means switch value list std::vector caseVals; // switch case size auto& loc = multiBranch.GetDebugLocation(); auto caseSize = builder.CreateConstantExpression( loc, builder.GetUInt64Ty(), multiBranch.GetParentBlock(), cases.size()); caseVals.emplace_back(caseSize->GetResult()); caseSize->MoveBefore(&multiBranch); // bit size of switch condition type // only support uint64 as bit size of switch condition type auto elemBitSize = builder.CreateConstantExpression( loc, builder.GetUInt64Ty(), multiBranch.GetParentBlock(), sizeof(uint64_t) * CHAR_BIT); caseVals.emplace_back(elemBitSize->GetResult()); elemBitSize->MoveBefore(&multiBranch); // switch value list std::for_each(cases.begin(), cases.end(), [this, &caseVals, &loc, &multiBranch](uint64_t caseVal) { auto valExpr = builder.CreateConstantExpression( loc, builder.GetUInt64Ty(), multiBranch.GetParentBlock(), caseVal); caseVals.emplace_back(valExpr->GetResult()); valExpr->MoveBefore(&multiBranch); }); // list size should be case size plus two auto listSize = builder.CreateConstantExpression( loc, builder.GetInt64Ty(), multiBranch.GetParentBlock(), cases.size() + 2U); listSize->MoveBefore(&multiBranch); // create raw literal array auto arrayTy = builder.GetType(builder.GetType(builder.GetUInt64Ty(), 1U)); auto rawArrayExpr = builder.CreateExpression( arrayTy, builder.GetUInt64Ty(), listSize->GetResult(), multiBranch.GetParentBlock()); return rawArrayExpr; } Intrinsic* SanitizerCoverage::CreateRawDataAcquire(const Expression& dataList, Type& elementType) const { auto cPointerTy = builder.GetType(&elementType); auto callContext = IntrisicCallContext { .kind = IntrinsicKind::ARRAY_ACQUIRE_RAW_DATA, .args = std::vector{dataList.GetResult()}, .instTypeArgs = std::vector{&elementType} }; return builder.CreateExpression( dataList.GetDebugLocation(), cPointerTy, callContext, dataList.GetParentBlock()); } std::vector SanitizerCoverage::GenerateCoverageCallByOption( const DebugLocation& loc, bool isDebug, Block* parent) { std::vector callList; if (sanCovOption.tracePCGuard) { auto guardList = GeneratePCGuardExpr(loc, isDebug, parent); callList.insert(callList.end(), guardList.begin(), guardList.end()); } if (sanCovOption.inline8bitCounters) { auto inline8bitList = GenerateInline8bitExpr(loc, isDebug, parent); callList.insert(callList.end(), inline8bitList.begin(), inline8bitList.end()); } if (sanCovOption.inlineBoolFlag) { auto inlineBoolFlag = GenerateInlineBoolExpr(loc, isDebug, parent); callList.insert(callList.end(), inlineBoolFlag.begin(), inlineBoolFlag.end()); } return callList; } std::vector SanitizerCoverage::GeneratePCGuardExpr(const DebugLocation& loc, bool isDebug, Block* parent) { auto& callName = SAN_COV_PC_GUARD; auto& initItem = SanCovUInt32ArrayInitName; // generate pc guard function auto cPointTy = builder.GetType(builder.GetUInt32Ty()); auto funcTy = builder.GetType(std::vector{cPointTy}, builder.GetUnitTy(), false, true); auto pcGuardFunc = GenerateForeignFunc(callName, loc, *funcTy, packageName); // generate global var auto globalVarType = builder.GetType(builder.GetUInt32Ty()); auto mangleName = SAN_COV_CTOR2_GLOBAL_VAR_NAME.at(initItem); GlobalVar* charArrayTest = GenerateGlobalVar(mangleName, loc, *globalVarType); /* generate expression as below: globalVar as @CharArrayTest$sancov$_pc_guard %a: CPointer = Load(@CharArrayTest$sancov$_pc_guard) %b: Int64 = Constant(offset) %c: CPointer = Intrinsic/addPointer(%a, %b) %d: Unit = Apply(@__sanitizer_cov_trace_pc_guard, %c) */ auto offset = builder.CreateConstantExpression( loc, builder.GetInt64Ty(), parent, static_cast(bbCounter)); Expression* arrayTestLoad = builder.CreateExpression(loc, globalVarType, charArrayTest, parent); auto callContext = IntrisicCallContext { .kind = IntrinsicKind::CPOINTER_ADD, .args = std::vector{arrayTestLoad->GetResult(), offset->GetResult()}, .instTypeArgs = std::vector{builder.GetUInt32Ty()} }; auto addPoint = builder.CreateExpression(loc, cPointTy, callContext, parent); auto syscall = CreateNonMemberApply( builder.GetUnitTy(), pcGuardFunc, std::vector{addPoint->GetResult()}, parent, builder, loc); if (isDebug) { std::cout << "[SanitizerCoverage] Add trace pc guard" << ToPosInfo(loc) << ".\n"; } return std::vector{arrayTestLoad, offset, addPoint, syscall}; } std::vector SanitizerCoverage::GenerateInline8bitExpr( const DebugLocation& loc, bool isDebug, Block* parent) { // generate global var auto& initItem = SanCovCharArrayInitName; auto globalVarType = builder.GetType(builder.GetUInt8Ty()); auto mangleName = SAN_COV_CTOR2_GLOBAL_VAR_NAME.at(initItem); GlobalVar* charArrayTest = GenerateGlobalVar(mangleName, loc, *globalVarType); /* generate expression as below: globalVar as sancov$_8bit_counters %a: CPointer = Load(@sancov$_8bit_counters) %b: Int64 = Constant(offset) %c: UInt8 = Intrinsic/readPointer(%a, %b) // read (sancov$_8bit_counters + offset) %e: UInt8 = Constant(1) %f: UInt8 = Add(%c, %e) // add 1 to the value of (sancov$_8bit_counters + offset) %h: Unit = Intrinsic/writePointer(%a, %b, %f) // output result to (sancov$_8bit_counters + offset) */ auto loadGlobal = builder.CreateExpression(loc, globalVarType, charArrayTest, parent); auto offset = builder.CreateConstantExpression( loc, builder.GetInt64Ty(), parent, static_cast(bbCounter)); auto callContext1 = IntrisicCallContext { .kind = IntrinsicKind::CPOINTER_READ, .args = std::vector{loadGlobal->GetResult(), offset->GetResult()}, .instTypeArgs = std::vector{builder.GetUInt8Ty()} }; auto readPoint = builder.CreateExpression(loc, builder.GetUInt8Ty(), callContext1, parent); auto one = builder.CreateConstantExpression(loc, builder.GetUInt8Ty(), parent, 1UL); auto addRes = builder.CreateExpression(loc, builder.GetUInt8Ty(), ExprKind::ADD, readPoint->GetResult(), one->GetResult(), OverflowStrategy::WRAPPING, parent); auto callContext2 = IntrisicCallContext { .kind = IntrinsicKind::CPOINTER_WRITE, .args = std::vector{loadGlobal->GetResult(), offset->GetResult(), addRes->GetResult()}, .instTypeArgs = std::vector{builder.GetUInt8Ty()} }; auto writePoint = builder.CreateExpression(loc, builder.GetUnitTy(), callContext2, parent); if (isDebug) { std::cout << "[SanitizerCoverage] Add trace inline 8 bit" << ToPosInfo(loc) << ".\n"; } return std::vector{loadGlobal, offset, readPoint, one, addRes, writePoint}; } std::vector SanitizerCoverage::GenerateInlineBoolExpr( const DebugLocation& loc, bool isDebug, Block* parent) { // generate global var auto& initItem = SanCovBoolFlagInitName; auto globalVarType = builder.GetType(builder.GetBoolTy()); auto mangleName = SAN_COV_CTOR2_GLOBAL_VAR_NAME.at(initItem); GlobalVar* charArrayTest = GenerateGlobalVar(mangleName, loc, *globalVarType); /* generate expression as below: globalVar as @CharArrayTest$sancov$_bool_flag %a: CPointer = Load(@CharArrayTest$sancov$_bool_flag) %b: Int64 = Constant(offset) %c: Bool = Constant(true) // set to true %d: Unit = Intrinsic/writePointer(%a, %b, %c) */ auto loadGlobal = builder.CreateExpression(loc, globalVarType, charArrayTest, parent); auto offset = builder.CreateConstantExpression( loc, builder.GetInt64Ty(), parent, static_cast(bbCounter)); auto boolTrue = builder.CreateConstantExpression(loc, builder.GetBoolTy(), parent, true); auto callContext = IntrisicCallContext { .kind = IntrinsicKind::CPOINTER_WRITE, .args = std::vector{loadGlobal->GetResult(), offset->GetResult(), boolTrue->GetResult()}, .instTypeArgs = std::vector{builder.GetBoolTy()} }; auto writePoint = builder.CreateExpression(loc, builder.GetUnitTy(), callContext, parent); if (isDebug) { std::cout << "[SanitizerCoverage] Add trace inline bool" << ToPosInfo(loc) << ".\n"; } return std::vector{loadGlobal, offset, boolTrue, writePoint}; } std::vector SanitizerCoverage::GenerateStackDepthExpr( const DebugLocation& loc, bool isDebug, Block* parent) { auto& callName = SanCovUpdateStackDepth; auto funcTy = builder.GetType(std::vector{}, builder.GetUnitTy(), false, true); auto callee = GenerateForeignFunc(callName, loc, *funcTy, packageName); auto syscall = CreateNonMemberApply(builder.GetUnitTy(), callee, std::vector{}, parent, builder, loc); if (isDebug) { std::cout << "[SanitizerCoverage] Add trace stage depth" << ToPosInfo(loc) << ".\n"; } return std::vector{syscall}; } GlobalVar* SanitizerCoverage::GenerateGlobalVar( const std::string& globalVarName, const DebugLocation& loc, Type& globalType) { if (auto it = globalVarBag.find(globalVarName); it != globalVarBag.end()) { return it->second; } auto globalVar = builder.CreateGlobalVar( loc, builder.GetType(&globalType), globalVarName, globalVarName, "", packageName); globalVar->EnableAttr(Attribute::READONLY); globalVar->EnableAttr(Attribute::COMPILER_ADD); globalVarBag.insert(std::make_pair(globalVarName, globalVar)); return globalVar; } GlobalVar* SanitizerCoverage::GetGlobalVar(const std::string& globalVarName) { if (globalVarBag.count(globalVarName) == 0) { return nullptr; } return globalVarBag.at(globalVarName); } ImportedValue* SanitizerCoverage::GenerateForeignFunc(const std::string& globalFuncName, [[maybe_unused]] const DebugLocation& loc, Type& funcType, const std::string& packName) { if (auto it = funcBag.find(globalFuncName); it != funcBag.end()) { return it->second; } auto func = builder.CreateImportedVarOrFunc(&funcType, globalFuncName, globalFuncName, "", packName); func->EnableAttr(Attribute::FOREIGN); funcBag.insert(std::make_pair(globalFuncName, func)); return func; } ImportedValue* SanitizerCoverage::GetImportedFunc(const std::string& mangledName) { auto it = funcBag.find(mangledName); CJC_ASSERT(it != funcBag.end()); return it->second; } Func* SanitizerCoverage::CreateInitFunc( const std::string& name, FuncType& funcType, [[maybe_unused]] const DebugLocation& loc) { auto func = builder.CreateFunc(loc, &funcType, name, name, "", packageName); auto body = builder.CreateBlockGroup(*func); func->InitBody(*body); func->EnableAttr(Attribute::NO_INLINE); func->EnableAttr(Attribute::NO_REFLECT_INFO); func->EnableAttr(Attribute::COMPILER_ADD); func->SetFuncKind(FuncKind::GLOBALVAR_INIT); func->Set(Linkage::INTERNAL); return func; } Func* SanitizerCoverage::CreateArrayInitFunc(const std::string& initItemName, Type& initType) { auto funcTy = builder.GetType(std::vector{}, builder.GetVoidTy()); auto initName = MANGLE_CANGJIE_PREFIX + MANGLE_GLOBAL_VARIABLE_INIT_PREFIX + MangleUtils::MangleName("default") + MangleUtils::MangleName("0." + SAN_COV_CTOR2_GLOBAL_VAR_NAME.at(initItemName)) + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX; auto func = CreateInitFunc(initName, *funcTy, INVALID_LOCATION); auto body = func->GetBody(); auto block0 = builder.CreateBlock(body); body->SetEntryBlock(block0); auto counterVal = builder.CreateConstantExpression( INVALID_LOCATION, builder.GetUInt64Ty(), block0, static_cast(bbCounter)); block0->AppendExpression(counterVal); auto cPointTy = builder.GetType(&initType); auto guardCtorTy = builder.GetType(std::vector{builder.GetUInt64Ty()}, cPointTy, false, true); auto guardCtorFunc = GenerateForeignFunc(initItemName, INVALID_LOCATION, *guardCtorTy, packageName); auto apply = CreateNonMemberApply( cPointTy, guardCtorFunc, std::vector{counterVal->GetResult()}, block0, builder, INVALID_LOCATION); block0->AppendExpression(apply); auto globalVal = GetGlobalVar(SAN_COV_CTOR2_GLOBAL_VAR_NAME.at(initItemName)); CJC_NULLPTR_CHECK(globalVal); block0->AppendExpression( builder.CreateExpression(INVALID_LOCATION, builder.GetUnitTy(), apply->GetResult(), globalVal, block0)); globalVal->SetInitFunc(*func); block0->AppendExpression(builder.CreateTerminator(INVALID_LOCATION, block0)); return func; } Func* SanitizerCoverage::CreatePCTableInitFunc() { auto funcTy = builder.GetType(std::vector{}, builder.GetVoidTy()); auto func = CreateInitFunc(MANGLE_CANGJIE_PREFIX + MANGLE_GLOBAL_VARIABLE_INIT_PREFIX + MangleUtils::MangleName("default") + MangleUtils::MangleName("0.pc_table") + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX, *funcTy, INVALID_LOCATION); auto body = func->GetBody(); auto block0 = builder.CreateBlock(body); body->SetEntryBlock(block0); // import malloc func auto mallocStringFunc = GetImportedFunc(FUNC_MANGLE_NAME_MALLOC_CSTRING); // pc table size auto funcSize = builder.CreateConstantExpression(INVALID_LOCATION, builder.GetInt64Ty(), block0, pcArray.size()); block0->AppendExpression(funcSize); // create package auto packExpr = builder.CreateConstantExpression( INVALID_LOCATION, builder.GetStringTy(), block0, packageName); block0->AppendExpression(packExpr); auto libc = builder.GetStructType("std.core", "LibC"); auto libcRef = builder.GetType(libc); auto packCString = CreateMemberApply(builder.GetCStringTy(), mallocStringFunc, libcRef, {packExpr->GetResult()}, block0, builder, INVALID_LOCATION); block0->AppendExpression(packCString); // create arrays of function name, file name and line number std::vector funcNameArray; std::vector fileNameArray; std::vector lineNumberArray; for (auto pcIter : pcArray) { auto funcName = builder.CreateConstantExpression( INVALID_LOCATION, builder.GetStringTy(), block0, pcIter.first); block0->AppendExpression(funcName); auto cString = CreateMemberApply(builder.GetCStringTy(), mallocStringFunc, libcRef, {funcName->GetResult()}, block0, builder, INVALID_LOCATION); block0->AppendExpression(cString); funcNameArray.push_back(cString->GetResult()); auto fileName = builder.CreateConstantExpression(INVALID_LOCATION, builder.GetStringTy(), block0, builder.GetChirContext().GetSourceFileName(pcIter.second.GetFileID())); block0->AppendExpression(fileName); auto fileNameCString = CreateMemberApply(builder.GetCStringTy(), mallocStringFunc, libcRef, {fileName->GetResult()}, block0, builder, INVALID_LOCATION); block0->AppendExpression(fileNameCString); fileNameArray.push_back(fileNameCString->GetResult()); auto lineId = builder.CreateConstantExpression( INVALID_LOCATION, builder.GetUInt64Ty(), block0, pcIter.second.GetBeginPos().line); block0->AppendExpression(lineId); lineNumberArray.push_back(lineId->GetResult()); } auto rawDataAcquireString = CreateRawDataAcquire(*builder.GetCStringTy(), funcNameArray, *funcSize->GetResult(), *block0); auto rawDataAcquireFileName = CreateRawDataAcquire(*builder.GetCStringTy(), fileNameArray, *funcSize->GetResult(), *block0); auto rawDataAcquireLine = CreateRawDataAcquire(*builder.GetUInt64Ty(), lineNumberArray, *funcSize->GetResult(), *block0); // create apply for pc table init auto paramTypeList = std::vector{builder.GetCStringTy(), builder.GetUInt64Ty(), rawDataAcquireString->GetResult()->GetType(), rawDataAcquireFileName->GetResult()->GetType(), rawDataAcquireLine->GetResult()->GetType()}; auto pcInitType = builder.GetType(paramTypeList, builder.GetUnitTy(), false, true); auto pcInitFunc = GenerateForeignFunc(SanCovCreatePCTable, INVALID_LOCATION, *pcInitType, packageName); auto typecast = builder.CreateExpression(builder.GetUInt64Ty(), funcSize->GetResult(), block0); block0->AppendExpression(typecast); block0->AppendExpression(CreateNonMemberApply(builder.GetUnitTy(), pcInitFunc, std::vector{packCString->GetResult(), typecast->GetResult(), rawDataAcquireString->GetResult(), rawDataAcquireFileName->GetResult(), rawDataAcquireLine->GetResult()}, block0, builder, INVALID_LOCATION)); block0->AppendExpression(builder.CreateTerminator(INVALID_LOCATION, block0)); return func; } Intrinsic* SanitizerCoverage::CreateRawDataAcquire( Type& type, const std::vector& list, Value& size, Block& block) { auto arrayTy = builder.GetType(builder.GetType(&type, 1u)); auto expr = builder.CreateExpression(arrayTy, &type, &size, &block); block.AppendExpression(expr); auto arrayVar = expr->GetResult(); block.AppendExpression(builder.CreateExpression(builder.GetUnitTy(), arrayVar, list, &block)); auto cPointerTy = builder.GetType(&type); auto callContext = IntrisicCallContext { .kind = IntrinsicKind::ARRAY_ACQUIRE_RAW_DATA, .args = std::vector{arrayVar}, .instTypeArgs = std::vector{&type} }; auto rawDataAcquire = builder.CreateExpression(INVALID_LOCATION, cPointerTy, callContext, &block); block.AppendExpression(rawDataAcquire); return rawDataAcquire; } void SanitizerCoverage::CreateTopLevelInitFunc(const std::vector& initFuncs, const Func& globalInitFunc) { // create top level init func if (initFuncs.empty()) { return; } auto funcTy = builder.GetType(std::vector{}, builder.GetVoidTy()); auto initName = MANGLE_CANGJIE_PREFIX + MANGLE_GLOBAL_FILE_INIT_PREFIX + MangleUtils::MangleName("default") + MangleUtils::MangleName("0.sancov") + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX; auto initFunc = CreateInitFunc(initName, *funcTy, INVALID_LOCATION); auto body = initFunc->GetBody(); auto block0 = builder.CreateBlock(body); body->SetEntryBlock(block0); for (auto init : initFuncs) { block0->AppendExpression(CreateNonMemberApply( init->GetReturnType(), init, std::vector{}, block0, builder, INVALID_LOCATION)); } block0->AppendExpression(builder.CreateTerminator(INVALID_LOCATION, block0)); // call top level fun in global init func auto initExpr = CreateNonMemberApply(builder.GetVoidTy(), initFunc, std::vector{}, globalInitFunc.GetEntryBlock(), builder, INVALID_LOCATION); AddExpressionsToGlobalInitFunc(globalInitFunc, std::vector{initExpr}); } void SanitizerCoverage::GenerateInitFunc(const Func& globalInitFunc, bool isDebug) { // Note: insert array init func at the first of package. // FuncSize and basic block size are evaluated at compile stage, firstly set 0 as placeHolder and then updated to // the correct value bool needInsertArray = sanCovOption.inlineBoolFlag || sanCovOption.inline8bitCounters || sanCovOption.tracePCGuard; if (!needInsertArray) { return; } std::vector initFuncs; if (sanCovOption.inlineBoolFlag && GetGlobalVar(SAN_COV_CTOR2_GLOBAL_VAR_NAME.at(SanCovBoolFlagInitName)) != nullptr) { initFuncs.emplace_back(CreateArrayInitFunc(SanCovBoolFlagInitName, *builder.GetBoolTy())); } if (sanCovOption.inline8bitCounters && GetGlobalVar(SAN_COV_CTOR2_GLOBAL_VAR_NAME.at(SanCovCharArrayInitName)) != nullptr) { initFuncs.emplace_back(CreateArrayInitFunc(SanCovCharArrayInitName, *builder.GetUInt8Ty())); } if (sanCovOption.tracePCGuard && funcBag.count(SAN_COV_PC_GUARD) != 0 && GetGlobalVar(SAN_COV_CTOR2_GLOBAL_VAR_NAME.at(SanCovUInt32ArrayInitName)) != nullptr) { initFuncs.emplace_back(CreateArrayInitFunc(SanCovUInt32ArrayInitName, *builder.GetUInt32Ty())); } if (sanCovOption.pcTable && !pcArray.empty()) { initFuncs.emplace_back(CreatePCTableInitFunc()); if (isDebug) { std::cout << "[SanitizerCoverage] Add trace pc table" << ToPosInfo(INVALID_LOCATION) << ".\n"; } } CreateTopLevelInitFunc(initFuncs, globalInitFunc); } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/Transformation/UnitUnify.cpp000066400000000000000000000043271510705540100237750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/UnitUnify.h" #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Visitor/Visitor.h" using namespace Cangjie::CHIR; namespace { bool NeedUnify(const Expression& expr) { auto result = expr.GetResult(); if (result == nullptr) { return false; } if (!result->GetType()->IsUnit()) { return false; } if (result->GetUsers().empty()) { return false; } if (auto constant = Cangjie::DynamicCast(&expr)) { if (constant->GetValue()->IsNullLiteral() || constant->GetValue()->IsUnitLiteral()) { return false; } } return true; } } UnitUnify::UnitUnify(CHIRBuilder& builder) : builder(builder) { } void UnitUnify::RunOnPackage(const Ptr& package, bool isDebug) { for (auto func : package->GetGlobalFuncs()) { RunOnFunc(func, isDebug); } } void UnitUnify::RunOnFunc(const Ptr& func, bool isDebug) { Ptr optUnit; auto preAcation = [this, isDebug, &optUnit](Expression& expr) { if (Is(expr) || Is(expr)) { return VisitResult::CONTINUE; } if (NeedUnify(expr)) { LoadOrCreateUnit(optUnit, expr.GetParentBlockGroup()); expr.GetResult()->ReplaceWith(*optUnit->GetResult(), expr.GetParentBlockGroup()); if (isDebug) { std::cout << "[UnitUnify] unit unify" << ToPosInfo(expr.GetDebugLocation()) << ".\n"; } } return VisitResult::CONTINUE; }; Visitor::Visit(*func, preAcation); } void UnitUnify::LoadOrCreateUnit(Ptr& constant, const Ptr& group) { if (constant != nullptr) { return; } auto entryBlock = group->GetEntryBlock(); constant = builder.CreateConstantExpression(builder.GetUnitTy(), entryBlock); constant->MoveBefore(entryBlock->GetExpressions()[0]); }cangjie_compiler-1.0.7/src/CHIR/Transformation/UselessAllocateElimination.cpp000066400000000000000000000045211510705540100273200ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Transformation/UselessAllocateElimination.h" #include "cangjie/CHIR/Analysis/Utils.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Expression/Terminator.h" using namespace Cangjie::CHIR; void UselessAllocateElimination::RunOnPackage(const Package& package, bool isDebug) { for (auto func : package.GetGlobalFuncs()) { RunOnFunc(*func, isDebug); } } void UselessAllocateElimination::RunOnFunc(const Func& func, bool isDebug) { for (auto block : func.GetBody()->GetBlocks()) { for (auto expr : block->GetExpressions()) { if (expr->GetExprKind() != ExprKind::ALLOCATE) { continue; } auto allocate = StaticCast(expr); if (auto allocatedTy = allocate->GetType(); allocatedTy->IsClass() && StaticCast(allocatedTy)->GetClassDef()->GetFinalizer()) { continue; } auto res = allocate->GetResult(); if (func.GetReturnValue() == res) { continue; } auto users = res->GetUsers(); auto onlyBeenWritten = std::all_of(users.begin(), users.end(), [res](auto e) { return (e->GetExprKind() == ExprKind::STORE && StaticCast(e)->GetLocation() == res) || (e->GetExprKind() == ExprKind::STORE_ELEMENT_REF && StaticCast(e)->GetLocation() == res) || e->GetExprKind() == ExprKind::DEBUGEXPR; }); if (onlyBeenWritten) { allocate->RemoveSelfFromBlock(); for (auto user : users) { user->RemoveSelfFromBlock(); } if (isDebug && !allocate->GetDebugLocation().GetBeginPos().IsZero()) { std::string message = "[UselessAllocateElimination] Allocate" + ToPosInfo(allocate->GetDebugLocation()) + " and its users have been deleted\n"; std::cout << message; } } } } }cangjie_compiler-1.0.7/src/CHIR/Type/000077500000000000000000000000001510705540100172445ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CHIR/Type/CHIRType.cpp000066400000000000000000000202551510705540100213430ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Type/CHIRType.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/Mangle/BaseMangler.h" namespace Cangjie::CHIR { std::recursive_mutex CHIRType::chirTypeMtx; static inline std::string GetIdentifierOfGenericTy(const AST::GenericsTy& ty) { CJC_ASSERT(ty.decl->mangledName != ""); return ty.decl->mangledName; } Type* CHIRType::TranslateTupleType(AST::TupleTy& tupleTy) { std::vector argTys; for (auto argTy : tupleTy.typeArgs) { argTys.emplace_back(TranslateType(*argTy)); } return builder.GetType(argTys); } Type* CHIRType::TranslateFuncType(const AST::FuncTy& fnTy) { Type* retTy = TranslateType(*fnTy.retTy); std::vector paramTys; for (auto paramTy : fnTy.paramTys) { auto pType = TranslateType(*paramTy); if (fnTy.IsCFunc() && pType->IsVArray()) { pType = builder.GetType(pType); } paramTys.emplace_back(pType); } auto funcTy = builder.GetType(paramTys, retTy, fnTy.hasVariableLenArg, fnTy.IsCFunc()); return funcTy; } Type* CHIRType::TranslateStructType(AST::StructTy& structTy) { std::vector typeArgs; for (auto arg : structTy.typeArgs) { typeArgs.emplace_back(TranslateType(*arg)); } auto def = chirTypeCache.globalNominalCache.Get(*structTy.declPtr); auto type = builder.GetType(StaticCast(def), typeArgs); chirTypeCache.typeMap[&structTy] = type; return type; } Type* CHIRType::TranslateClassType(AST::ClassTy& classTy) { std::vector typeArgs; for (auto arg : classTy.typeArgs) { typeArgs.emplace_back(TranslateType(*arg)); } auto def = chirTypeCache.globalNominalCache.Get(*classTy.declPtr); auto type = builder.GetType(StaticCast(def), typeArgs); chirTypeCache.typeMap[&classTy] = type; return type; } Type* CHIRType::TranslateInterfaceType(AST::InterfaceTy& interfaceTy) { std::vector typeArgs; for (auto arg : interfaceTy.typeArgs) { typeArgs.emplace_back(TranslateType(*arg)); } auto def = chirTypeCache.globalNominalCache.Get(*interfaceTy.declPtr); auto type = builder.GetType(StaticCast(def), typeArgs); chirTypeCache.typeMap[&interfaceTy] = type; return type; } Type* CHIRType::TranslateEnumType(AST::EnumTy& enumTy) { std::vector typeArgs; for (auto arg : enumTy.typeArgs) { typeArgs.emplace_back(TranslateType(*arg)); } auto def = chirTypeCache.globalNominalCache.Get(*enumTy.declPtr); auto type = builder.GetType(StaticCast(def), typeArgs); chirTypeCache.typeMap[&enumTy] = type; return type; } Type* CHIRType::TranslateArrayType(AST::ArrayTy& arrayTy) { // RawArrayType [elementTy, dims] auto elementTy = TranslateType(*arrayTy.typeArgs[0]); return builder.GetType(elementTy, arrayTy.dims); } Type* CHIRType::TranslateVArrayType(AST::VArrayTy& varrayTy) { // VArrayType size, [elementTy] auto elementTy = TranslateType(*varrayTy.typeArgs[0]); return builder.GetType(elementTy, varrayTy.size); } Type* CHIRType::TranslateCPointerType(AST::PointerTy& pointerTy) { auto elementTy = TranslateType(*pointerTy.typeArgs[0]); return builder.GetType(elementTy); } void CHIRType::FillGenericArgType(AST::GenericsTy& ty) { auto it = chirTypeCache.typeMap.find(&ty); CJC_ASSERT(it != chirTypeCache.typeMap.end()); CJC_ASSERT(it->second->GetTypeKind() == Type::TypeKind::TYPE_GENERIC); std::vector chirTy; for (auto argTy : ty.upperBounds) { CJC_ASSERT(!argTy->IsGeneric()); chirTy.emplace_back(TranslateType(*argTy)); } StaticCast(it->second)->SetUpperBounds(chirTy); } Type* CHIRType::TranslateType(AST::Ty& ty) { Type* type = nullptr; std::lock_guard lock(chirTypeMtx); if (auto it = chirTypeCache.typeMap.find(&ty); it != chirTypeCache.typeMap.end()) { return it->second; } switch (ty.kind) { case AST::TypeKind::TYPE_UNIT: { type = builder.GetUnitTy(); break; } case AST::TypeKind::TYPE_INT8: { type = builder.GetInt8Ty(); break; } case AST::TypeKind::TYPE_INT16: { type = builder.GetInt16Ty(); break; } case AST::TypeKind::TYPE_INT32: { type = builder.GetInt32Ty(); break; } case AST::TypeKind::TYPE_INT64: { type = builder.GetInt64Ty(); break; } case AST::TypeKind::TYPE_INT_NATIVE: { type = builder.GetIntNativeTy(); break; } case AST::TypeKind::TYPE_UINT8: { type = builder.GetUInt8Ty(); break; } case AST::TypeKind::TYPE_UINT16: { type = builder.GetUInt16Ty(); break; } case AST::TypeKind::TYPE_UINT32: { type = builder.GetUInt32Ty(); break; } case AST::TypeKind::TYPE_UINT64: { type = builder.GetUInt64Ty(); break; } case AST::TypeKind::TYPE_UINT_NATIVE: { type = builder.GetUIntNativeTy(); break; } case AST::TypeKind::TYPE_FLOAT16: { type = builder.GetFloat16Ty(); break; } case AST::TypeKind::TYPE_FLOAT32: { type = builder.GetFloat32Ty(); break; } case AST::TypeKind::TYPE_FLOAT64: { type = builder.GetFloat64Ty(); break; } case AST::TypeKind::TYPE_RUNE: { type = builder.GetRuneTy(); break; } case AST::TypeKind::TYPE_BOOLEAN: { type = builder.GetBoolTy(); break; } case AST::TypeKind::TYPE_TUPLE: { type = TranslateTupleType(StaticCast(ty)); break; } case AST::TypeKind::TYPE_FUNC: { type = TranslateFuncType(StaticCast(ty)); break; } case AST::TypeKind::TYPE_ENUM: { type = TranslateEnumType(StaticCast(ty)); break; } case AST::TypeKind::TYPE_STRUCT: { type = TranslateStructType(StaticCast(ty)); break; } case AST::TypeKind::TYPE_CLASS: { type = TranslateClassType(StaticCast(ty)); break; } case AST::TypeKind::TYPE_INTERFACE: { type = TranslateInterfaceType(StaticCast(ty)); break; } case AST::TypeKind::TYPE_ARRAY: { type = TranslateArrayType(StaticCast(ty)); break; } case AST::TypeKind::TYPE_VARRAY: { type = TranslateVArrayType(StaticCast(ty)); break; } case AST::TypeKind::TYPE_NOTHING: { type = builder.GetType(); break; } case AST::TypeKind::TYPE_POINTER: { type = TranslateCPointerType(StaticCast(ty)); break; } case AST::TypeKind::TYPE_CSTRING: { type = builder.GetType(); break; } case AST::TypeKind::TYPE_GENERICS: { auto identifier = GetIdentifierOfGenericTy(StaticCast(ty)); type = builder.GetType(identifier, ty.name); break; } default: { CJC_ABORT(); } } if (type->IsClassOrArray() || type->IsBox()) { type = builder.GetType(type); } chirTypeCache.typeMap[&ty] = type; return type; } } // namespace Cangjie::CHIRcangjie_compiler-1.0.7/src/CHIR/Type/CMakeLists.txt000066400000000000000000000005261510705540100220070ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB TYPE_SRC *.cpp) set(CHIR_BASE_SRC ${CHIR_BASE_SRC} ${TYPE_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/CHIR/Type/ClassDef.cpp000066400000000000000000000062271510705540100214430ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/ToStringUtils.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" #include "cangjie/Utils/CheckUtils.h" #include #include using namespace Cangjie::CHIR; ClassDef::ClassDef(std::string srcCodeIdentifier, std::string identifier, std::string pkgName, bool isClass) : CustomTypeDef(srcCodeIdentifier, identifier, pkgName, CustomDefKind::TYPE_CLASS), isClass(isClass) { } ClassDef* ClassDef::GetSuperClassDef() const { return superClassTy ? superClassTy->GetClassDef() : nullptr; } bool ClassDef::HasSuperClass() const { return GetSuperClassDef() != nullptr; } void ClassDef::PrintAbstractMethod(std::stringstream& ss) const { for (auto& method : abstractMethods) { PrintIndent(ss); ss << method.attributeInfo.ToString(); ss << "func " << method.methodName << ": " << method.methodTy->ToString() << "\n"; } } void ClassDef::SetSuperClassTy(ClassType& ty) { superClassTy = &ty; } std::string ClassDef::ToString() const { std::stringstream ss; PrintAttrAndTitle(ss); ss << " {"; PrintComment(ss); ss << "\n"; PrintLocalVar(ss); PrintStaticVar(ss); PrintMethod(ss); PrintAbstractMethod(ss); PrintVTable(ss); ss << "}"; return ss.str(); } bool ClassDef::IsAbstract() const { return TestAttr(CHIR::Attribute::ABSTRACT); } bool ClassDef::IsInterface() const { return !isClass; } bool ClassDef::IsClass() const { return isClass; } void ClassDef::SetAnnotation(bool value) { isAnnotation = value; } bool ClassDef::IsAnnotation() const { return isAnnotation; } ClassType* ClassDef::GetSuperClassTy() const { return superClassTy; } FuncBase* ClassDef::GetFinalizer() const { for (auto m : methods) { if (m->GetFuncKind() == FuncKind::FINALIZER) { return m; } } return nullptr; } void ClassDef::AddAbstractMethod(AbstractMethodInfo methodInfo) { abstractMethods.emplace_back(std::move(methodInfo)); } std::vector ClassDef::GetAbstractMethods() const { return abstractMethods; } void ClassDef::SetAbstractMethods(const std::vector& methods) { abstractMethods = methods; } void ClassDef::SetType(CustomType& ty) { CJC_ASSERT(ty.GetTypeKind() == Type::TypeKind::TYPE_CLASS); type = &ty; } ClassType* ClassDef::GetType() const { return StaticCast(type); } void ClassDef::PrintComment(std::stringstream& ss) const { CustomTypeDef::PrintComment(ss); AddCommaOrNot(ss); if (ss.str().empty()) { ss << " // "; } ss << "isAnnotation: " << BoolToString(isAnnotation); }cangjie_compiler-1.0.7/src/CHIR/Type/CustomTypeDef.cpp000066400000000000000000000522451510705540100225130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Type class in CHIR. */ #include "cangjie/CHIR/Type/CustomTypeDef.h" #include #include #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/ToStringUtils.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Value.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/ConstantsUtils.h" #include "cangjie/Utils/ICEUtil.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie::CHIR; void CustomTypeDef::AddMethod(FuncBase* method) { CJC_NULLPTR_CHECK(method); method->declaredParent = this; methods.emplace_back(method); } void CustomTypeDef::AddStaticMemberVar(GlobalVarBase* variable) { CJC_NULLPTR_CHECK(variable); variable->declaredParent = this; staticVars.emplace_back(variable); } void CustomTypeDef::Dump() const { std::cout << ToString() << std::endl; } void CustomTypeDef::AddImplementedInterfaceTy(ClassType& interfaceTy) { CJC_NULLPTR_CHECK(interfaceTy.GetCustomTypeDef()); CJC_ASSERT(StaticCast(interfaceTy.GetCustomTypeDef())->IsInterface()); implementedInterfaceTys.push_back(&interfaceTy); } std::vector CustomTypeDef::GetImplementedInterfaceDefs() const { std::vector defs; for (auto ty : implementedInterfaceTys) { CJC_NULLPTR_CHECK(ty->GetCustomTypeDef()); defs.emplace_back(StaticCast(ty->GetCustomTypeDef())); } return defs; } std::vector CustomTypeDef::GetSuperTypesInCurDef() const { auto res = implementedInterfaceTys; if (auto classDef = DynamicCast(this)) { auto superClassType = classDef->GetSuperClassTy(); if (superClassType != nullptr) { res.insert(res.begin(), superClassType); } } return res; } std::string CustomTypeDef::GenericDefArgsToString() const { auto genericTypeParams = GetGenericTypeParams(); if (genericTypeParams.empty()) { return ""; } std::string res; res += "<"; for (size_t i = 0; i < genericTypeParams.size(); ++i) { res += genericTypeParams[i]->ToString(); // not the last one if (i != genericTypeParams.size() - 1) { res += ", "; } } res += ">"; return res; } void CustomTypeDef::PrintAttrAndTitle(std::stringstream& ss) const { ss << attributeInfo.ToString(); ss << CustomTypeKindToString(*this) << " " << GetIdentifier() << GenericDefArgsToString(); PrintParent(ss); } std::string CustomTypeDef::GenericInsArgsToString(const CustomType& ty) const { std::string res; auto args = ty.GetGenericArgs(); if (args.empty()) { return res; } res += "<"; for (size_t i = 0; i < args.size(); ++i) { res += args[i]->ToString(); // not the last one if (i != args.size() - 1) { res += ", "; } } res += ">"; return res; } void CustomTypeDef::PrintParent(std::stringstream& ss) const { auto parentTys = GetSuperTypesInCurDef(); if (parentTys.empty()) { return; } ss << " <: "; for (size_t i = 0; i < parentTys.size(); ++i) { ss << parentTys[i]->GetCustomTypeDef()->GetIdentifier() << GenericInsArgsToString(*parentTys[i]); // not the last one if (i != parentTys.size() - 1) { ss << " & "; } } } void CustomTypeDef::PrintComment(std::stringstream& ss) const { std::stringstream comment; if (!srcCodeIdentifier.empty()) { comment << "srcCodeIdentifier: " << srcCodeIdentifier; } AddCommaOrNot(comment); auto genericTypeParams = GetGenericTypeParams(); if (!genericTypeParams.empty()) { auto constraintsStr = GetGenericConstaintsStr(genericTypeParams); if (!constraintsStr.empty()) { comment << "genericConstrains: " << constraintsStr; } } AddCommaOrNot(comment); comment << ToStringAnnotationMap(); AddCommaOrNot(comment); if (genericDecl != nullptr) { comment << "genericDecl: " << genericDecl->GetIdentifierWithoutPrefix(); AddCommaOrNot(comment); } comment << "packageName: " << packageName; if (comment.str() != "") { ss << " // " << comment.str(); } } void CustomTypeDef::PrintLocalVar(std::stringstream& ss) const { for (auto& localVar : instanceVars) { PrintIndent(ss); localVar.TestAttr(Attribute::READONLY) ? ss << "let " : ss << "var "; ss << localVar.name << ": " << localVar.type->ToString() << " // " << localVar.loc.ToString() << "\n"; } } void CustomTypeDef::PrintStaticVar(std::stringstream& ss) const { for (auto& staticVar : staticVars) { PrintIndent(ss); ss << "[static] "; staticVar->TestAttr(Attribute::READONLY) ? ss << "let " : ss << "var "; ss << staticVar->GetIdentifier() << ": " << staticVar->GetType()->ToString() << "\n"; } } void CustomTypeDef::PrintMethod(std::stringstream& ss) const { for (auto& method : methods) { PrintIndent(ss); ss << method->GetAttributeInfo().ToString(); ss << "func " << method->GetIdentifier() << ": " << method->GetType()->ToString() << "\n"; } } void CustomTypeDef::PrintVTable(std::stringstream& ss) const { unsigned indent = 1; if (vtable.size() > 0) { PrintIndent(ss, indent); ss << "vtable {\n"; ++indent; for (auto& vtableIt : vtable) { PrintIndent(ss, indent); ss << vtableIt.first->ToString() << " {\n"; ++indent; for (auto& funcInfo : vtableIt.second) { PrintIndent(ss, indent); ss << "@" << funcInfo.srcCodeIdentifier; if (funcInfo.srcCodeIdentifier != "$Placeholder") { ss << ": " << funcInfo.typeInfo.originalType->ToString(); ss << " => " << (funcInfo.instance ? funcInfo.instance->GetIdentifier() : "[abstract]"); } ss << "\n"; } --indent; PrintIndent(ss, indent); ss << "}\n"; } --indent; PrintIndent(ss, indent); ss << "}\n"; } } std::pair CustomTypeDef::GetExpectedFunc( const std::string& funcName, FuncType& funcType, bool isStatic, std::unordered_map replaceTable, std::vector& funcInstTypeArgs, CHIRBuilder& builder, bool checkAbstractMethod) const { // you shouldn't search a function without name CJC_ASSERT(!funcName.empty()); auto instParamTys = funcType.GetParamTypes(); if (!isStatic) { CJC_ASSERT(!instParamTys.empty()); instParamTys.erase(instParamTys.begin()); } for (auto method : methods) { if (isStatic != method->TestAttr(Attribute::STATIC)) { continue; } auto methodName = method->GetSrcCodeIdentifier(); if (auto rawMethod = method->Get()) { methodName = rawMethod->GetSrcCodeIdentifier(); } if (methodName != funcName) { continue; } auto originalFuncParamTys = method->GetFuncType()->GetParamTypes(); if (!method->TestAttr(Attribute::STATIC)) { CJC_ASSERT(!originalFuncParamTys.empty()); originalFuncParamTys.erase(originalFuncParamTys.begin()); } if (originalFuncParamTys.size() != instParamTys.size()) { continue; } std::vector genericTypeParams; if (auto func = DynamicCast(method)) { genericTypeParams = func->GetGenericTypeParams(); } else { genericTypeParams = StaticCast(method)->GetGenericTypeParams(); } if (genericTypeParams.size() != funcInstTypeArgs.size()) { continue; } for (size_t i = 0; i < genericTypeParams.size(); ++i) { replaceTable.emplace(genericTypeParams[i], funcInstTypeArgs[i]); } bool matched = true; for (size_t i = 0; i < originalFuncParamTys.size(); ++i) { auto instType = ReplaceRawGenericArgType(*originalFuncParamTys[i], replaceTable, builder); if (auto genericTy = DynamicCast(instType); genericTy && genericTy->orphanFlag) { auto upperBounds = genericTy->GetUpperBounds(); CJC_ASSERT(upperBounds.size() == 1); instType = upperBounds[0]; } if (!instParamTys[i]->IsGeneric() && instType != instParamTys[i]) { matched = false; break; } } if (matched) { if (auto rawFunc = method->Get(); rawFunc && rawFunc->GetParentCustomTypeDef() == this) { return {rawFunc, true}; } return {method, true}; } } auto failed = std::pair{nullptr, false}; if (!checkAbstractMethod) { return failed; } auto classDef = DynamicCast(this); if (classDef == nullptr) { return failed; } for (auto method : classDef->GetAbstractMethods()) { if (isStatic != method.attributeInfo.TestAttr(Attribute::STATIC)) { continue; } if (method.methodName != funcName) { continue; } auto originalFuncParamTys = StaticCast(method.methodTy)->GetParamTypes(); if (!method.attributeInfo.TestAttr(Attribute::STATIC)) { originalFuncParamTys.erase(originalFuncParamTys.begin()); } if (originalFuncParamTys.size() != instParamTys.size()) { continue; } auto& genericTypeParams = method.methodGenericTypeParams; if (genericTypeParams.size() != funcInstTypeArgs.size()) { continue; } for (size_t i = 0; i < genericTypeParams.size(); ++i) { replaceTable.emplace(genericTypeParams[i], funcInstTypeArgs[i]); } bool matched = true; for (size_t i = 0; i < originalFuncParamTys.size(); ++i) { auto instType = ReplaceRawGenericArgType(*originalFuncParamTys[i], replaceTable, builder); if (!instParamTys[i]->IsGeneric() && instType != instParamTys[i]) { matched = false; break; } } if (matched) { return {nullptr, true}; } } return failed; } std::vector CustomTypeDef::GetFuncIndexInVTable(const FuncCallType& funcCallType, bool isStatic, std::unordered_map& replaceTable, CHIRBuilder& builder) const { auto& funcName = funcCallType.funcName; auto& funcInstTypeArgs = funcCallType.genericTypeArgs; auto instArgTys = funcCallType.funcType->GetParamTypes(); if (!isStatic) { CJC_ASSERT(!instArgTys.empty()); instArgTys.erase(instArgTys.begin()); } std::vector res; for (auto& mapIt : vtable) { for (size_t i = 0; i < mapIt.second.size(); ++i) { if (mapIt.second[i].srcCodeIdentifier != funcName) { continue; } auto genericParamTys = mapIt.second[i].typeInfo.sigType->GetParamTypes(); if (genericParamTys.size() != instArgTys.size()) { continue; } auto genericTypeParams = mapIt.second[i].typeInfo.methodGenericTypeParams; if (genericTypeParams.size() != funcInstTypeArgs.size()) { continue; } for (size_t j = 0; j < genericTypeParams.size(); ++j) { replaceTable.emplace(genericTypeParams[j], funcInstTypeArgs[j]); } bool matched = true; for (size_t j = 0; j < genericParamTys.size(); ++j) { auto declaredInstType = ReplaceRawGenericArgType(*genericParamTys[j], replaceTable, builder); if (!ParamTypeIsEquivalent(*declaredInstType, *instArgTys[j])) { matched = false; break; } } if (matched) { auto originalParentType = const_cast(mapIt.first); auto instSrcParentTy = ReplaceRawGenericArgType(*originalParentType, replaceTable, builder); auto& funcInfo = mapIt.second[i]; res.emplace_back(VTableSearchRes { .instSrcParentType = StaticCast(instSrcParentTy), .halfInstSrcParentType = originalParentType, .originalFuncType = funcInfo.typeInfo.originalType, .instance = funcInfo.instance, .originalDef = const_cast(this), .genericTypeParams = funcInfo.typeInfo.methodGenericTypeParams, .attr = funcInfo.attr, .offset = i }); break; } } } return res; } std::string CustomTypeDef::ToString() const { /* [public][generic][...] class XXX { // loc: xxx, genericDecl: xxx ^^^^^^^^^^^^^^ attr ^^^^^^^^^ title ^^^^^^^^^^^^^^^^^^ comment local var static var method vtable } */ std::stringstream ss; PrintAttrAndTitle(ss); ss << " {"; PrintComment(ss); ss << "\n"; PrintLocalVar(ss); // has a \n in the end PrintStaticVar(ss); // has a \n in the end PrintMethod(ss); // has a \n in the end PrintVTable(ss); // has a \n in the end ss << "}"; return ss.str(); } std::vector CustomTypeDef::GetGenericTypeParams() const { std::vector genericTypes; if (this->TestAttr(Attribute::GENERIC)) { for (auto ty : type->GetGenericArgs()) { // why can `ty` can be RefType? if (auto gt = DynamicCast(ty); gt) { genericTypes.emplace_back(gt); } } } return genericTypes; } std::vector CustomTypeDef::GetSuperTypesRecusively(CHIRBuilder& builder) const { std::vector inheritanceList; for (auto interface : this->GetImplementedInterfaceTys()) { GetAllInstantiatedParentType(*interface, builder, inheritanceList); } if (this->IsClassLike()) { auto superClass = StaticCast(this)->GetSuperClassTy(); if (superClass != nullptr) { GetAllInstantiatedParentType(*superClass, builder, inheritanceList); } } return inheritanceList; } void CustomTypeDef::SetMethods(const std::vector& items) { for (auto m : methods) { m->declaredParent = nullptr; } for (auto m : items) { m->declaredParent = this; } methods = items; } void CustomTypeDef::SetStaticMemberVars(const std::vector& vars) { for (auto v : staticVars) { v->declaredParent = nullptr; } for (auto v : vars) { v->declaredParent = this; } staticVars = vars; } std::vector CustomTypeDef::GetMethods() const { return methods; } bool CustomTypeDef::IsInterface() const { if (!IsClassLike()) { return false; } return StaticCast(this)->IsInterface(); } bool CustomTypeDef::IsClass() const { if (!IsClassLike()) { return false; } return StaticCast(this)->IsClass(); } const std::vector& CustomTypeDef::GetExtends() const { return extends; } void CustomTypeDef::AddExtend(ExtendDef& extend) { extends.emplace_back(&extend); } const VTableType& CustomTypeDef::GetVTable() const { return vtable; } void CustomTypeDef::SetVTable(const VTableType& table) { vtable = table; } void CustomTypeDef::UpdateVtableItem(ClassType& srcClassTy, size_t index, FuncBase* newFunc, Type* newParentTy, const std::string newName) { auto& funcInfo = vtable[&srcClassTy][index]; funcInfo.instance = newFunc; if (newFunc != nullptr) { funcInfo.typeInfo.originalType = newFunc->GetFuncType(); } if (newParentTy != nullptr) { funcInfo.typeInfo.parentType = newParentTy; } if (!newName.empty()) { funcInfo.srcCodeIdentifier = newName; } } void CustomTypeDef::AddVtableItem(ClassType& srcClassTy, VirtualFuncInfo&& info) { vtable[&srcClassTy].push_back(std::move(info)); } CustomDefKind CustomTypeDef::GetCustomKind() const { return kind; } bool CustomTypeDef::IsStruct() const { return kind == TYPE_STRUCT; } bool CustomTypeDef::IsEnum() const { return kind == TYPE_ENUM; } bool CustomTypeDef::IsClassLike() const { return kind == TYPE_CLASS; } bool CustomTypeDef::IsExtend() const { return kind == TYPE_EXTEND; } std::string CustomTypeDef::GetIdentifier() const { return identifier; } void CustomTypeDef::AppendAttributeInfo(const AttributeInfo& info) { attributeInfo.AppendAttrs(info); } /** * Get identifier without prefix '@' */ std::string CustomTypeDef::GetIdentifierWithoutPrefix() const { CJC_ASSERT(!identifier.empty()); return identifier.substr(1); } void CustomTypeDef::EnableAttr(Attribute attr) { attributeInfo.SetAttr(attr, true); } void CustomTypeDef::DisableAttr(Attribute attr) { attributeInfo.SetAttr(attr, false); } bool CustomTypeDef::TestAttr(Attribute attr) const { return attributeInfo.TestAttr(attr); } AttributeInfo CustomTypeDef::GetAttributeInfo() const { return attributeInfo; } FuncBase* CustomTypeDef::GetVarInitializationFunc() const { return varInitializationFunc; } void CustomTypeDef::SetVarInitializationFunc(FuncBase* func) { varInitializationFunc = func; } std::vector CustomTypeDef::GetStaticMemberVars() const { return staticVars; } size_t CustomTypeDef::GetAllInstanceVarNum() const { size_t res = instanceVars.size(); if (auto classDef = DynamicCast(this)) { auto parent = classDef->GetSuperClassDef(); while (parent != nullptr) { res += parent->instanceVars.size(); parent = parent->GetSuperClassDef(); } } return res; } std::vector CustomTypeDef::GetAllInstanceVars() const { std::vector res; if (auto classDef = DynamicCast(this)) { auto parent = classDef->GetSuperClassDef(); while (parent != nullptr) { res.insert(res.begin(), parent->instanceVars.begin(), parent->instanceVars.end()); parent = parent->GetSuperClassDef(); } } res.insert(res.end(), instanceVars.begin(), instanceVars.end()); return res; } size_t CustomTypeDef::GetDirectInstanceVarNum() const { return instanceVars.size(); } MemberVarInfo CustomTypeDef::GetDirectInstanceVar(size_t index) const { CJC_ASSERT(index < instanceVars.size()); return instanceVars[index]; } std::vector CustomTypeDef::GetDirectInstanceVars() const { return instanceVars; } MemberVarInfo CustomTypeDef::GetInstanceVar(size_t index) const { auto allVars = GetAllInstanceVars(); CJC_ASSERT(allVars.size() > index); return allVars[index]; } void CustomTypeDef::AddInstanceVar(MemberVarInfo variable) { instanceVars.emplace_back(std::move(variable)); } void CustomTypeDef::SetDirectInstanceVars(const std::vector& vars) { instanceVars = vars; } std::string CustomTypeDef::GetPackageName() const { return packageName; } std::string CustomTypeDef::GetSrcCodeIdentifier() const { return srcCodeIdentifier; } Type* CustomTypeDef::GetType() const { return type; } void CustomTypeDef::SetAnnoInfo(const AnnoInfo& info) { annoInfo = info; } AnnoInfo CustomTypeDef::GetAnnoInfo() const { return annoInfo; } std::vector CustomTypeDef::GetImplementedInterfaceTys() const { return implementedInterfaceTys; } size_t CustomTypeDef::GetImplementedInterfacesNum() const { return implementedInterfaceTys.size(); } bool CustomTypeDef::IsGenericDef() const { return !GetGenericTypeParams().empty(); } void CustomTypeDef::SetGenericDecl(CustomTypeDef& decl) { genericDecl = &decl; } CustomTypeDef* CustomTypeDef::GetGenericDecl() const { return genericDecl; } bool CustomTypeDef::CanBeInherited() const { // we shouldn't care about if current def is GENEIC_INSTANTIATED return IsInterface() || TestAttr(Attribute::VIRTUAL) || TestAttr(Attribute::ABSTRACT); } cangjie_compiler-1.0.7/src/CHIR/Type/EnumDef.cpp000066400000000000000000000056671510705540100213110ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Type/EnumDef.h" #include #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/ToStringUtils.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" using namespace Cangjie::CHIR; bool EnumDef::IsAllCtorsTrivial() const { // if enum all ctor does not have params, it is a trivial enum for (auto& ctor : ctors) { if (ctor.funcType->GetParamTypes().size() != 0) { return false; } } return true; } static std::string PrintParamTypes(const std::vector& paramTypes) { if (paramTypes.empty()) { return ""; } std::string str; str += "("; for (size_t i = 0; i < paramTypes.size(); ++i) { str += paramTypes[i]->ToString(); if (i != paramTypes.size() - 1) { str += ", "; } } str += ")"; return str; } void EnumDef::PrintConstructor(std::stringstream& ss) const { for (auto& ctor : ctors) { PrintIndent(ss); ss << ctor.name << PrintParamTypes(ctor.funcType->GetParamTypes()) << "\n"; } ss << "\n"; } void EnumDef::PrintAttrAndTitle(std::stringstream& ss) const { ss << attributeInfo.ToString(); if (!IsExhaustive()) { ss << "[nonExhaustive] "; } ss << CustomTypeKindToString(*this) << " " << GetIdentifier() << GenericDefArgsToString(); PrintParent(ss); } std::string EnumDef::ToString() const { /* [public][generic][...] enum XXX { // loc: xxx, genericDecl: xxx ^^^^^^^^^^^^^^ attr ^^^^^^^^^ title ^^^^^^^^^^^^^^^^^^ comment constructor method vtable } */ std::stringstream ss; PrintAttrAndTitle(ss); ss << " {"; PrintComment(ss); ss << "\n"; PrintConstructor(ss); // has a \n in the end PrintMethod(ss); // has a \n in the end PrintVTable(ss); // has a \n in the end ss << "}"; return ss.str(); } void EnumDef::AddCtor(EnumCtorInfo ctor) { ctors.emplace_back(ctor); } std::vector EnumDef::GetCtors() const { return ctors; } void EnumDef::SetCtors(const std::vector& items) { ctors = items; } EnumCtorInfo EnumDef::GetCtor(size_t index) const { CJC_ASSERT(ctors.size() > index); return ctors[index]; } void EnumDef::SetType(CustomType& ty) { CJC_ASSERT(ty.GetTypeKind() == Type::TypeKind::TYPE_ENUM); type = &ty; } EnumType* EnumDef::GetType() const { return StaticCast(type); } bool EnumDef::IsExhaustive() const { return !nonExhaustive; }cangjie_compiler-1.0.7/src/CHIR/Type/ExtendDef.cpp000066400000000000000000000045141510705540100216220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Type/ExtendDef.h" #include #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/ToStringUtils.h" #include "cangjie/Utils/CastingTemplate.h" using namespace Cangjie; using namespace Cangjie::CHIR; ExtendDef::ExtendDef( const std::string& identifier, const std::string& pkgName, std::vector genericParams) : CustomTypeDef("", identifier, pkgName, CustomDefKind::TYPE_EXTEND), genericParams(genericParams) { } CustomTypeDef* ExtendDef::GetExtendedCustomTypeDef() const { if (auto customTy = DynamicCast(extendedType); customTy) { return customTy->GetCustomTypeDef(); } return nullptr; } void ExtendDef::PrintAttrAndTitle(std::stringstream& ss) const { ss << attributeInfo.ToString(); std::string extendedTyStr; CJC_NULLPTR_CHECK(extendedType); if (auto customTy = DynamicCast(extendedType); customTy) { extendedTyStr = customTy->GetCustomTypeDef()->GetIdentifier() + GenericInsArgsToString(*customTy); } else { extendedTyStr = extendedType->ToString(); } ss << CustomTypeKindToString(*this) << GenericDefArgsToString() << " " << extendedTyStr; PrintParent(ss); } void ExtendDef::PrintComment(std::stringstream& ss) const { CustomTypeDef::PrintComment(ss); AddCommaOrNot(ss); if (ss.str().empty()) { ss << " // "; } ss << "id: " << identifier; } void ExtendDef::RemoveParent(ClassType& parent) { implementedInterfaceTys.erase( std::remove(implementedInterfaceTys.begin(), implementedInterfaceTys.end(), &parent), implementedInterfaceTys.end()); } Type* ExtendDef::GetExtendedType() const { CJC_NULLPTR_CHECK(extendedType); return extendedType; } Type* ExtendDef::GetType() const { return GetExtendedType(); } void ExtendDef::SetExtendedType(Type& ty) { extendedType = &ty; } void ExtendDef::SetType(CustomType& ty) { (void)ty; CJC_ABORT(); // extend decl doesn't have type } std::vector ExtendDef::GetGenericTypeParams() const { return genericParams; }cangjie_compiler-1.0.7/src/CHIR/Type/PrivateTypeConverter.cpp000066400000000000000000000265641510705540100241310ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Type/PrivateTypeConverter.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/Visitor/Visitor.h" namespace Cangjie::CHIR { FuncType* TypeConverter::ConvertFuncParamsAndRetType(const FuncType& input) { std::vector newParamTys; for (auto oldParamTy : input.GetParamTypes()) { newParamTys.emplace_back(ConvertType(*oldParamTy)); } auto newRetTy = ConvertType(*input.GetReturnType()); return builder.GetType(newParamTys, newRetTy, input.HasVarArg(), input.IsCFunc()); } Type* TypeConverter::ConvertType(Type& type) { return converter(type); } void ExprTypeConverter::VisitExprDefaultImpl(Expression& o) { if (o.result != nullptr) { VisitValue(*o.result); } } void ExprTypeConverter::VisitSubExpression(Allocate& o) { VisitExprDefaultImpl(o); o.ty = ConvertType(*o.ty); } void ExprTypeConverter::VisitSubExpression(AllocateWithException& o) { VisitExprDefaultImpl(o); o.ty = ConvertType(*o.ty); } void ExprTypeConverter::VisitSubExpression(InstanceOf& o) { VisitExprDefaultImpl(o); o.ty = ConvertType(*o.ty); } void ExprTypeConverter::VisitSubExpression(RawArrayAllocate& o) { VisitExprDefaultImpl(o); o.elementType = ConvertType(*o.elementType); } void ExprTypeConverter::VisitSubExpression(RawArrayAllocateWithException& o) { VisitExprDefaultImpl(o); o.elementType = ConvertType(*o.elementType); } void ExprTypeConverter::VisitSubExpression(Apply& o) { VisitExprDefaultImpl(o); for (auto& ty : o.instantiatedTypeArgs) { ty = converter(*ty); } if (o.thisType != nullptr) { o.thisType = ConvertType(*o.thisType); } } void ExprTypeConverter::VisitSubExpression(ApplyWithException& o) { VisitExprDefaultImpl(o); for (auto& ty : o.instantiatedTypeArgs) { ty = converter(*ty); } if (o.thisType != nullptr) { o.thisType = ConvertType(*o.thisType); } } void ExprTypeConverter::VisitSubExpression(Invoke& o) { VisitExprDefaultImpl(o); o.virMethodCtx.originalFuncType = ConvertFuncParamsAndRetType(*o.virMethodCtx.originalFuncType); o.thisType = ConvertType(*o.thisType); for (auto& ty : o.instantiatedTypeArgs) { ty = converter(*ty); } } void ExprTypeConverter::VisitSubExpression(InvokeWithException& o) { VisitExprDefaultImpl(o); o.virMethodCtx.originalFuncType = ConvertFuncParamsAndRetType(*o.virMethodCtx.originalFuncType); o.thisType = ConvertType(*o.thisType); for (auto& ty : o.instantiatedTypeArgs) { ty = converter(*ty); } } void ExprTypeConverter::VisitSubExpression(InvokeStatic& o) { VisitExprDefaultImpl(o); o.virMethodCtx.originalFuncType = ConvertFuncParamsAndRetType(*o.virMethodCtx.originalFuncType); o.thisType = ConvertType(*o.thisType); for (auto& ty : o.instantiatedTypeArgs) { ty = converter(*ty); } } void ExprTypeConverter::VisitSubExpression(InvokeStaticWithException& o) { VisitExprDefaultImpl(o); o.virMethodCtx.originalFuncType = ConvertFuncParamsAndRetType(*o.virMethodCtx.originalFuncType); o.thisType = ConvertType(*o.thisType); for (auto& ty : o.instantiatedTypeArgs) { ty = converter(*ty); } } void ExprTypeConverter::VisitSubExpression(Constant& o) { VisitExprDefaultImpl(o); VisitValue(*o.GetValue()); } void ExprTypeConverter::VisitSubExpression(Intrinsic& o) { VisitExprDefaultImpl(o); for (auto& ty : o.instantiatedTypeArgs) { ty = converter(*ty); } } void ExprTypeConverter::VisitSubExpression(IntrinsicWithException& o) { VisitExprDefaultImpl(o); for (auto& ty : o.instantiatedTypeArgs) { ty = converter(*ty); } } void ExprTypeConverter::VisitSubExpression(GetInstantiateValue& o) { VisitExprDefaultImpl(o); for (auto& ty : o.instantiateTys) { ty = ConvertType(*ty); } } void ExprTypeConverter::VisitSubExpression(Lambda& o) { VisitExprDefaultImpl(o); o.funcTy = ConvertFuncParamsAndRetType(*o.funcTy); auto postVisit = [this](Expression& o) { VisitExpr(o); return VisitResult::CONTINUE; }; for (auto& param : o.GetParams()) { VisitValue(*param); } // no need to transfer retValue, because ret Value has been contained in body Visitor::Visit(*o.GetBody(), [](Expression&) { return VisitResult::CONTINUE; }, postVisit); } void ExprTypeConverter::VisitSubExpression(GetRTTIStatic& o) { VisitExprDefaultImpl(o); o.ty = ConvertType(*o.GetRTTIType()); } void ValueTypeConverter::VisitValueDefaultImpl(Value& o) { o.ty = ConvertType(*o.ty); } void ValueTypeConverter::VisitSubValue(Func& o) { o.ty = ConvertFuncParamsAndRetType(*o.GetFuncType()); // convert generic type params for (auto& genericTypeParam : o.genericTypeParams) { genericTypeParam = StaticCast(ConvertType(*genericTypeParam)); } // convert param types for (auto param : o.GetParams()) { VisitValue(*param); } } void ValueTypeConverter::VisitSubValue(ImportedFunc& o) { o.ty = ConvertFuncParamsAndRetType(*o.GetFuncType()); // convert generic type params for (auto& genericTypeParam : o.genericTypeParams) { genericTypeParam = StaticCast(ConvertType(*genericTypeParam)); } // convert param types for (auto& param : o.paramInfo) { param.type = ConvertType(*param.type); } } void CustomDefTypeConverter::VisitDefDefaultImpl(CustomTypeDef& o) { // extend def's type is nullptr if (o.type != nullptr) { o.type = StaticCast(ConvertType(*o.type)); } for (size_t i = 0; i < o.implementedInterfaceTys.size(); ++i) { o.implementedInterfaceTys[i] = StaticCast(ConvertType(*o.implementedInterfaceTys[i])); } for (auto& var : o.instanceVars) { var.type = ConvertType(*var.type); } VTableType newVtable; for (auto& it : o.vtable) { auto newKey = ConvertType(*it.first); for (auto& funcInfo : it.second) { funcInfo.typeInfo.sigType = ConvertFuncParamsAndRetType(*funcInfo.typeInfo.sigType); funcInfo.typeInfo.originalType = ConvertFuncParamsAndRetType(*funcInfo.typeInfo.originalType); funcInfo.typeInfo.returnType = ConvertType(*funcInfo.typeInfo.returnType); if (funcInfo.typeInfo.parentType) { funcInfo.typeInfo.parentType = ConvertType(*funcInfo.typeInfo.parentType); } if (funcInfo.instance) { funcInfo.instance->ty = ConvertFuncParamsAndRetType(*funcInfo.instance->GetFuncType()); } } newVtable.emplace(StaticCast(newKey), it.second); } o.vtable = newVtable; } void CustomDefTypeConverter::VisitSubDef(StructDef& o) { VisitDefDefaultImpl(o); } void CustomDefTypeConverter::VisitSubDef(EnumDef& o) { VisitDefDefaultImpl(o); for (auto& ctor : o.ctors) { ctor.funcType = ConvertFuncParamsAndRetType(*ctor.funcType); } } void CustomDefTypeConverter::VisitSubDef(ClassDef& o) { VisitDefDefaultImpl(o); if (o.superClassTy != nullptr) { o.superClassTy = StaticCast(ConvertType(*o.superClassTy)); } for (auto& method : o.abstractMethods) { method.methodTy = ConvertFuncParamsAndRetType(*StaticCast(method.methodTy)); for (auto& param : method.paramInfos) { param.type = ConvertType(*param.type); } } } void CustomDefTypeConverter::VisitSubDef(ExtendDef& o) { VisitDefDefaultImpl(o); o.extendedType = ConvertType(*o.extendedType); for (size_t i = 0; i < o.genericParams.size(); ++i) { o.genericParams[i] = StaticCast(ConvertType(*o.genericParams[i])); } } void PrivateTypeConverterNoInvokeOriginal::VisitSubExpression(Invoke& o) { VisitExprDefaultImpl(o); o.thisType = ConvertType(*o.thisType); for (auto& ty : o.instantiatedTypeArgs) { ty = ConvertType(*ty); } } void PrivateTypeConverterNoInvokeOriginal::VisitSubExpression(InvokeWithException& o) { VisitExprDefaultImpl(o); o.thisType = ConvertType(*o.thisType); for (auto& ty : o.instantiatedTypeArgs) { ty = ConvertType(*ty); } } void PrivateTypeConverterNoInvokeOriginal::VisitSubExpression(InvokeStatic& o) { VisitExprDefaultImpl(o); o.thisType = ConvertType(*o.thisType); for (auto& ty : o.instantiatedTypeArgs) { ty = ConvertType(*ty); } } void PrivateTypeConverterNoInvokeOriginal::VisitSubExpression(InvokeStaticWithException& o) { VisitExprDefaultImpl(o); o.thisType = ConvertType(*o.thisType); for (auto& ty : o.instantiatedTypeArgs) { ty = ConvertType(*ty); } } Type* TypeConverterForCC::ConvertType(Type& type) { if (type.IsRef()) { return builder.GetType(ConvertType(*StaticCast(type).GetBaseType())); } else if (type.IsCJFunc()) { return funcConverter(type); } else { return converter(type); } } void TypeConverterForCC::VisitSubExpression(RawArrayAllocate& o) { VisitExprDefaultImpl(o); o.elementType = converter(*o.elementType); } void TypeConverterForCC::VisitSubExpression(RawArrayAllocateWithException& o) { VisitExprDefaultImpl(o); o.elementType = converter(*o.elementType); } void TypeConverterForCC::VisitSubValue(Func& o) { if (o.GetSrcCodeIdentifier() == GENERIC_VIRTUAL_FUNC) { auto funcType = o.GetFuncType(); std::vector newParamTys; for (auto oldParamTy : funcType->GetParamTypes()) { /** * func $GenericVirtualFunc(p: (Int64) -> Unit) {} * we need to convert type of `p` to $AutoEnvGenericBase, not to $AutoEnvInstBase, because * call site may be an `Invoke`, param type in method type is Generic-T, * so we can pass an object with any func type, generic func type or inst func type, both ok. * we don't need to convert to $AutoEnvGenericBase in $InstVirtualFunc, because its arg's type * must be inst func type */ if (oldParamTy->IsCJFunc()) { newParamTys.emplace_back(converter(*oldParamTy)); } else { newParamTys.emplace_back(ConvertType(*oldParamTy)); } } auto newRetTy = ConvertType(*funcType->GetReturnType()); o.ty = builder.GetType(newParamTys, newRetTy, funcType->HasVarArg(), funcType->IsCFunc()); // convert generic type params for (auto& genericTypeParam : o.genericTypeParams) { genericTypeParam = StaticCast(ConvertType(*genericTypeParam)); } // convert param types for (auto param : o.GetParams()) { VisitValue(*param); } } else { ValueTypeConverter::VisitSubValue(o); } } void TypeConverterForCC::VisitValueDefaultImpl(Value& o) { if (o.IsParameter()) { if (auto func = StaticCast(o).GetOwnerFunc(); func && func->GetSrcCodeIdentifier() == GENERIC_VIRTUAL_FUNC && o.ty->IsCJFunc()) { o.ty = converter(*o.ty); return; } } ValueTypeConverter::VisitValueDefaultImpl(o); } } // namespace Cangjie::CHIRcangjie_compiler-1.0.7/src/CHIR/Type/StructDef.cpp000066400000000000000000000017761510705540100216660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Type/StructDef.h" #include #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/ToStringUtils.h" using namespace Cangjie::CHIR; void StructDef::SetCStruct(bool value) { isC = value; } bool StructDef::IsCStruct() const { return isC; } void StructDef::SetType(CustomType& ty) { CJC_ASSERT(ty.GetTypeKind() == Type::TypeKind::TYPE_STRUCT); type = &ty; } StructType* StructDef::GetType() const { return StaticCast(type); } void StructDef::PrintComment(std::stringstream& ss) const { CustomTypeDef::PrintComment(ss); AddCommaOrNot(ss); if (ss.str().empty()) { ss << " // "; } ss << "isC: " << BoolToString(isC); } cangjie_compiler-1.0.7/src/CHIR/Type/Type.cpp000066400000000000000000001423121510705540100206740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Type class in CHIR. */ #include "cangjie/CHIR/Type/Type.h" #include #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/Utils/CheckUtils.h" using namespace Cangjie::CHIR; static size_t HashArgTypes(const std::vector& argTys) { std::size_t hashVal = 0; for (auto argTy : argTys) { HashCombine(hashVal, argTy); } return hashVal; } static const std::string PrintArgTys(const std::vector& argTys) { std::stringstream ss; if (argTys.size() == 0) { return ""; } ss << "<" << argTys[0]->ToString(); for (size_t loop = 1; loop < argTys.size(); loop++) { ss << "," << argTys[loop]->ToString(); } ss << ">"; return ss.str(); } Type::Type(TypeKind kind) : kind(kind) { for (auto ty : argTys) { CJC_NULLPTR_CHECK(ty); } } bool Type::IsAutoEnv() const { return kind == TYPE_CLASS && IsClosureConversionEnvClass(*StaticCast(this)->GetClassDef()); } bool Type::IsAutoEnvBase() const { if (kind != TYPE_CLASS) { return false; } auto classDef = StaticCast(this)->GetClassDef(); return IsClosureConversionEnvClass(*classDef) && classDef->TestAttr(Attribute::ABSTRACT); } bool Type::IsAutoEnvInstBase() const { return IsAutoEnvBase() && argTys.empty(); } bool Type::IsAutoEnvGenericBase() const { return IsAutoEnvBase() && !argTys.empty(); } bool Type::IsString() const { if (IsStruct()) { auto ty = StaticCast(this); auto structDef = ty->GetStructDef(); return CheckCustomTypeDefIsExpected(*structDef, CORE_PACKAGE_NAME, STD_LIB_STRING); } return false; } bool Type::IsClassRef([[maybe_unused]] bool nullable) const { if (!IsRef()) { return false; } auto& rt = StaticCast(*this); const auto tmpTy = rt.GetBaseType(); return tmpTy->IsClass(); } bool Type::IsConstant() const { if (!IsTuple()) { return IsInteger() || IsFloat() || IsRune() || IsBoolean() || IsUnit() || IsString(); } auto tupleType = static_cast(this); return std::all_of(tupleType->argTys.begin(), tupleType->argTys.end(), [](const Type* type) { return type->IsConstant(); }); } bool Type::IsStructArray() const { if (IsStruct()) { auto ty = StaticCast(this); auto structDef = ty->GetStructDef(); return CheckCustomTypeDefIsExpected(*structDef, CORE_PACKAGE_NAME, STD_LIB_ARRAY); } return false; } bool Type::IsBuiltinType() const { return (kind >= CHIR::Type::TypeKind::TYPE_INT8 && kind <= CHIR::Type::TypeKind::TYPE_VOID) || (kind >= CHIR::Type::TypeKind::TYPE_RAWARRAY && kind <= CHIR::Type::TypeKind::TYPE_CSTRING); } std::string Type::ToString() const { auto ite = TYPEKIND_TO_STRING.find(kind); return ite == TYPEKIND_TO_STRING.end() ? "UnknownType" : ite->second; } Type* Type::StripAllRefs() const { Type* baseTy = const_cast(this); while (baseTy->IsRef()) { baseTy = StaticCast(baseTy)->GetBaseType(); } return baseTy; } bool Type::IsReferenceTypeWithRefDims(size_t dims) const { if (refDims != dims) { return false; } return StripAllRefs()->IsReferenceType(); } bool Type::IsValueOrGenericTypeWithRefDims(size_t dims) const { if (refDims != dims) { return false; } auto strippedTy = StripAllRefs(); return strippedTy->IsValueType() || strippedTy->IsGeneric(); } bool Type::IsAny() const { if (kind != TYPE_CLASS) { return false; } return IsCoreAny(*static_cast(this)->GetClassDef()); } bool Type::IsCJFunc() const { if (kind != TYPE_FUNC) { return false; } // is func, but not CFunc return !static_cast(this)->isCFunc; } bool Type::IsCFunc() const { if (kind != TYPE_FUNC) { return false; } return static_cast(this)->isCFunc; } bool Type::IsCType() const { if (kind != TYPE_CLASS) { return false; } auto def = static_cast(this)->GetClassDef(); return CheckCustomTypeDefIsExpected(*def, CORE_PACKAGE_NAME, CTYPE_NAME); } std::vector Type::GetDeclareAndExtendMethods([[maybe_unused]] CHIRBuilder& builder) const { #ifdef NDEBUG return {}; #else CJC_ABORT(); #endif } void Type::Dump() const { std::cout << ToString() << std::endl; } size_t Type::Hash() const { std::size_t hashVal = HashArgTypes(argTys); HashValue(hashVal, kind); return hashVal; } bool Type::operator==(const Type& other) const { return kind == other.kind && argTys == other.argTys; } const std::vector& Type::GetExtends(CHIRBuilder* builder) const { #ifndef NDEBUG (void)builder; CJC_ABORT(); #else // just for release cjc, it's not expected to go here return StaticCast(this)->GetExtends(builder); #endif } std::vector Type::GetSuperTypesRecusively([[maybe_unused]] CHIRBuilder& builder, [[maybe_unused]] std::set>* visited) { CJC_ABORT(); return std::vector{}; } IntType::IntType(TypeKind kind) : NumericType(kind) { CJC_ASSERT(kind >= TYPE_INT8 && kind <= TYPE_UINT_NATIVE); } FloatType::FloatType(TypeKind kind) : NumericType(kind) { CJC_ASSERT(kind >= TYPE_FLOAT16 && kind <= TYPE_FLOAT64); } std::string FuncType::ToString() const { std::stringstream ss; ss << "("; auto paramTys = GetParamTypes(); for (unsigned i = 0; i < paramTys.size(); i++) { if (i > 0) { ss << ", "; } ss << paramTys[i]->ToString(); } if (IsCFunc() && HasVarArg()) { ss << "..."; } ss << ")"; ss << " -> " << GetReturnType()->ToString(); return isCFunc ? "CFunc<" + ss.str() + ">" : ss.str(); } size_t FuncType::Hash() const { std::size_t hashVal = HashArgTypes(argTys); HashValue(hashVal, isCFunc); HashValue(hashVal, hasVarArg); HashValue(hashVal, kind); return hashVal; } bool FuncType::operator==(const Type& other) const { auto it = DynamicCast(&other); return it && argTys == it->argTys && (isCFunc == it->isCFunc) && (hasVarArg == it->hasVarArg); } CustomType::CustomType(TypeKind kind, CustomTypeDef* def, const std::vector& typeArgs = {}) : Type(kind), def(def) { CJC_NULLPTR_CHECK(def); CJC_ASSERT((kind == TYPE_CLASS && def->GetCustomKind() == CustomDefKind::TYPE_CLASS) || (kind == TYPE_STRUCT && def->GetCustomKind() == CustomDefKind::TYPE_STRUCT) || (kind == TYPE_ENUM && def->GetCustomKind() == CustomDefKind::TYPE_ENUM)); this->argTys = typeArgs; } CustomTypeDef* CustomType::GetCustomTypeDef() const { CJC_ASSERT(def != nullptr); return def; } std::vector CustomType::GetSuperTypesRecusively(CHIRBuilder& builder, std::set>* visited) { std::vector inheritanceList; for (auto interface : GetImplementedInterfaceTys(&builder)) { GetAllInstantiatedParentType(*interface, builder, inheritanceList, visited); } if (auto classTy = DynamicCast(this)) { auto superClass = classTy->GetSuperClassTy(&builder); if (superClass != nullptr) { GetAllInstantiatedParentType(*superClass, builder, inheritanceList, visited); } } return inheritanceList; } void CustomType::GetInstMap(std::unordered_map& instMap, CHIRBuilder& builder) const { // how to cache the result auto customTypeDef = GetCustomTypeDef(); auto customTypeDefGenericParams = customTypeDef->GetGenericTypeParams(); auto customTypeGenericArgs = GetGenericArgs(); if (!customTypeDefGenericParams.empty()) { CJC_ASSERT(customTypeDefGenericParams.size() == customTypeGenericArgs.size()); } for (size_t i = 0; i < customTypeDefGenericParams.size(); ++i) { instMap.emplace(customTypeDefGenericParams[i], customTypeGenericArgs[i]); } for (auto parentInterfaceTy : customTypeDef->GetImplementedInterfaceTys()) { auto parentInterfaceDef = parentInterfaceTy->GetCustomTypeDef(); auto parentInterfaceDefGenericParams = parentInterfaceDef->GetGenericTypeParams(); auto instParentInterfaceTy = StaticCast(ReplaceRawGenericArgType(*parentInterfaceTy, instMap, builder)); auto parentInterfaceTypeGenericArgs = instParentInterfaceTy->GetGenericArgs(); if (!customTypeDefGenericParams.empty()) { CJC_ASSERT(parentInterfaceDefGenericParams.size() == parentInterfaceTypeGenericArgs.size()); } for (size_t i = 0; i < parentInterfaceDefGenericParams.size(); ++i) { instMap.emplace(parentInterfaceDefGenericParams[i], parentInterfaceTypeGenericArgs[i]); } parentInterfaceTy->GetInstMap(instMap, builder); } if (DynamicCast(this)) { auto classTypeDef = StaticCast(customTypeDef); auto parentClassTy = classTypeDef->GetSuperClassTy(); if (parentClassTy) { auto parentClassDef = parentClassTy->GetCustomTypeDef(); auto parentClassDefGenericParams = parentClassDef->GetGenericTypeParams(); auto instparentClassTy = StaticCast(ReplaceRawGenericArgType(*parentClassTy, instMap, builder)); auto parentClassTypeGenericArgs = instparentClassTy->GetGenericArgs(); if (!parentClassDefGenericParams.empty()) { CJC_ASSERT(parentClassDefGenericParams.size() == parentClassTypeGenericArgs.size()); } for (size_t i = 0; i < parentClassDefGenericParams.size(); ++i) { instMap.emplace(parentClassDefGenericParams[i], parentClassTypeGenericArgs[i]); } parentClassTy->GetInstMap(instMap, builder); } } } std::vector CustomType::GetGenericArgs() const { return argTys; } const std::vector& CustomType::GetExtends([[maybe_unused]]CHIRBuilder* builder) const { return GetCustomTypeDef()->GetExtends(); } std::pair CustomType::GetInstMemberTypeByPathCheckingReadOnly( const std::vector& path, CHIRBuilder& builder) const { if (path.empty()) { #ifdef NDEBUG return {const_cast(this), false}; #else CJC_ABORT(); #endif } auto customTypeDef = GetCustomTypeDef(); std::unordered_map instMap; GetInstMap(instMap, builder); auto currentIdx = path.front(); auto member = customTypeDef->GetAllInstanceVars()[currentIdx]; // if one member is readonly in path, the result is readonly bool isReadOnly = member.TestAttr(Attribute::READONLY); auto memberTy = ReplaceRawGenericArgType(*member.type, instMap, builder); if (path.size() > 1) { auto pureMemberTy = memberTy; while (pureMemberTy->IsRef()) { pureMemberTy = StaticCast(pureMemberTy)->GetBaseType(); } if (pureMemberTy->IsNominal()) { auto subPath = path; subPath.erase(subPath.begin()); auto [memberType, isMemberReadOnly] = StaticCast(pureMemberTy)->GetInstMemberTypeByPathCheckingReadOnly(subPath, builder); return {memberType, isReadOnly || isMemberReadOnly}; } else if (pureMemberTy->IsGeneric()) { auto subPath = path; subPath.erase(subPath.begin()); auto [memberType, isMemberReadOnly] = StaticCast(pureMemberTy)->GetInstMemberTypeByPathCheckingReadOnly(subPath, builder); return {memberType, isReadOnly || isMemberReadOnly}; } else { CJC_ABORT(); } } return {memberTy, isReadOnly}; } std::vector CustomType::CalculateCurDefInstantiatedMemberTys(CHIRBuilder& builder) { auto genericTypes = def->GetGenericTypeParams(); if (!genericTypes.empty()) { CJC_ASSERT(argTys.size() == genericTypes.size()); } std::unordered_map replaceTable; for (size_t i = 0; i < genericTypes.size(); ++i) { replaceTable.emplace(genericTypes[i], argTys[i]); } std::vector ret; for (auto& var : def->GetDirectInstanceVars()) { ret.emplace_back(ReplaceRawGenericArgType(*var.type, replaceTable, builder)); } return ret; } std::vector CustomType::GetInstantiatedMemberTys(CHIRBuilder& builder) { std::vector ret; if (auto classTy = DynamicCast(this); classTy && classTy->GetSuperClassTy(&builder) != nullptr) { ret = classTy->GetSuperClassTy(&builder)->GetInstantiatedMemberTys(builder); } std::unique_lock lock(setInstMemberTyMtx); if (hasSetInstMemberTy) { ret.insert(ret.end(), instantiatedMemberTys.begin(), instantiatedMemberTys.end()); return ret; } else { instantiatedMemberTys = CalculateCurDefInstantiatedMemberTys(builder); ret.insert(ret.end(), instantiatedMemberTys.begin(), instantiatedMemberTys.end()); hasSetInstMemberTy = true; return ret; } } std::vector CustomType::GetInstMethodTypes(CHIRBuilder& builder) const { std::vector instFuncTypes; auto typeArgs = GetGenericArgs(); auto paramArgs = def->GetGenericTypeParams(); std::unordered_map instMap; CJC_ASSERT(typeArgs.size() == paramArgs.size()); for (size_t i = 0; i < typeArgs.size(); ++i) { instMap.emplace(paramArgs[i], typeArgs[i]); } auto methods = def->GetMethods(); for (auto method : methods) { instFuncTypes.emplace_back( StaticCast(ReplaceRawGenericArgType(*method->GetType(), instMap, builder))); } return instFuncTypes; } std::vector CustomType::GetDeclareAndExtendMethods(CHIRBuilder& builder) const { auto allMethods = def->GetMethods(); for (auto extendDef : def->GetExtends()) { if (!IsEqualOrInstantiatedTypeOf(*extendDef->GetExtendedType(), builder)) { continue; } auto m = extendDef->GetMethods(); allMethods.insert(allMethods.end(), m.begin(), m.end()); } return allMethods; } size_t CustomType::Hash() const { std::size_t hashVal = HashArgTypes(argTys); HashValue(hashVal, def); return hashVal; } bool CustomType::operator==(const Type& other) const { auto it = DynamicCast(&other); return it && argTys == it->argTys && (def == it->def); } ClassType::ClassType(ClassDef* classDef, const std::vector& genericArgs) : CustomType(TypeKind::TYPE_CLASS, classDef, genericArgs) { } std::string ClassType::ToString() const { std::stringstream ss; ss << "Class"; if (def != nullptr) { ss << "-" << def->GetIdentifierWithoutPrefix(); } ss << PrintArgTys(argTys); return ss.str(); } ClassDef* ClassType::GetClassDef() const { CJC_ASSERT(def != nullptr); return StaticCast(def); } ClassType* ClassType::CalculateSuperClassTy(CHIRBuilder& builder) { auto genericTypes = def->GetGenericTypeParams(); if (!genericTypes.empty()) { CJC_ASSERT(argTys.size() == genericTypes.size()); } std::unordered_map replaceTable; for (size_t i = 0; i < genericTypes.size(); ++i) { replaceTable.emplace(genericTypes[i], argTys[i]); } ClassType* result{nullptr}; if (auto sup = StaticCast(def)->GetSuperClassTy()) { result = StaticCast(ReplaceRawGenericArgType(*sup, replaceTable, builder)); } return result; } ClassType* ClassType::GetSuperClassTy(CHIRBuilder* builder) { std::unique_lock lock(setSuperClassMtx); if (hasSetSuperClass) { return superClassTy; } else { CJC_NULLPTR_CHECK(builder); superClassTy = CalculateSuperClassTy(*builder); hasSetSuperClass = true; return superClassTy; } } std::vector ClassType::GetInstAbstractMethodTypes(CHIRBuilder& builder) const { std::vector instMethodInfos; auto typeArgs = GetGenericArgs(); auto paramArgs = def->GetGenericTypeParams(); std::unordered_map instMap; CJC_ASSERT(typeArgs.size() == paramArgs.size()); for (size_t i = 0; i < typeArgs.size(); ++i) { instMap.emplace(paramArgs[i], typeArgs[i]); } auto methods = StaticCast(def)->GetAbstractMethods(); for (auto& method : methods) { method.methodTy = ReplaceRawGenericArgType(*method.methodTy, instMap, builder); for (auto& paramInfo : method.paramInfos) { paramInfo.type = ReplaceRawGenericArgType(*paramInfo.type, instMap, builder); } } return methods; } static void CollectGenericReplaceTable( Type& mayBeGeneric, Type& instType, std::unordered_map& replaceTable) { if (auto genericType = Cangjie::DynamicCast(&mayBeGeneric)) { replaceTable.emplace(genericType, &instType); } else { auto genericTypeArgs = mayBeGeneric.GetTypeArgs(); auto instTypeArgs = instType.GetTypeArgs(); if (genericTypeArgs.size() != instTypeArgs.size()) { /* * need to choose right extend type, not all extend is suitable * example: * a enum type: * public enum OP * with extends as follow: * extend OP> * extend OP <: I2 * * if we have inst type OP, when traverse to the first extend, the args size are not same. */ return; } for (size_t i = 0; i < genericTypeArgs.size(); ++i) { CollectGenericReplaceTable(*genericTypeArgs[i], *instTypeArgs[i], replaceTable); } } } // return false if `extendedType` and `instCustomType` are not matched static bool CollectReplaceTableForExtendedType(Type& extendedType, Type& instCustomType, std::unordered_map& replaceTable) { auto instTypeArgs = instCustomType.GetTypeArgs(); auto genericTypeArgs = extendedType.GetTypeArgs(); if (instTypeArgs.size() != genericTypeArgs.size()) { return false; } for (size_t i = 0; i < genericTypeArgs.size(); ++i) { if (auto genericTy = Cangjie::DynamicCast(genericTypeArgs[i])) { auto res = replaceTable.emplace(genericTy, instTypeArgs[i]); // if generic type has been stored, but instantiated types are different, this extend def isn't expected if (!res.second && res.first->second != instTypeArgs[i]) { #ifdef NDEBUG return false; #else CJC_ABORT(); #endif } } else { auto genericArgTy = Cangjie::DynamicCast(genericTypeArgs[i]); auto instArgTy = Cangjie::DynamicCast(instTypeArgs[i]); if (genericArgTy != nullptr && instArgTy == nullptr) { return false; } if (genericArgTy == nullptr && instArgTy != nullptr) { return false; } if (genericArgTy != nullptr && instArgTy != nullptr && genericArgTy->GetCustomTypeDef() != instArgTy->GetCustomTypeDef()) { return false; } auto match = CollectReplaceTableForExtendedType(*genericTypeArgs[i], *instTypeArgs[i], replaceTable); if (!match) { return false; } } } return true; } std::pair CustomType::GetExpectedFunc(const std::string& funcName, FuncType& funcType, bool isStatic, std::vector& funcInstTypeArgs, CHIR::CHIRBuilder& builder, bool checkAbstractMethod) { std::unordered_map replaceTable; auto classInstArgs = this->GetTypeArgs(); auto classGenericArgs = this->GetCustomTypeDef()->GetGenericTypeParams(); if (!classGenericArgs.empty()) { CJC_ASSERT(classInstArgs.size() == classGenericArgs.size()); } for (size_t i = 0; i < classGenericArgs.size(); ++i) { replaceTable.emplace(classGenericArgs[i], classInstArgs[i]); } // visiting func must in order: // 1. func declared in current def and extend def // 2. func declared in super class def // 3. func declared in super interface, including extend def's super interface // current def if (auto [func, done] = GetCustomTypeDef()->GetExpectedFunc( funcName, funcType, isStatic, replaceTable, funcInstTypeArgs, builder, checkAbstractMethod); done) { return {func, done}; } // extend def for (auto ex : GetCustomTypeDef()->GetExtends()) { auto match = CollectReplaceTableForExtendedType(*ex->GetExtendedType(), *this, replaceTable); if (!match) { continue; } auto [func, done] = ex->GetExpectedFunc( funcName, funcType, isStatic, replaceTable, funcInstTypeArgs, builder, checkAbstractMethod); if (done) { return {func, done}; } } // super class if (auto classTy = DynamicCast(this); classTy && classTy->GetClassDef()->GetSuperClassDef() != nullptr) { auto superClassTy = classTy->GetSuperClassTy(&builder); auto [func, done] = superClassTy->GetExpectedFunc( funcName, funcType, isStatic, funcInstTypeArgs, builder, checkAbstractMethod); if (done) { return {func, done}; } } // super interface for (auto superInterfaceTy : this->GetImplementedInterfaceTys(&builder)) { auto [func, done] = superInterfaceTy->GetExpectedFunc( funcName, funcType, isStatic, funcInstTypeArgs, builder, checkAbstractMethod); if (done) { return {func, done}; } } // extend def's super interface for (auto ex : GetCustomTypeDef()->GetExtends()) { for (auto ty : ex->GetImplementedInterfaceTys()) { auto superInterfaceTy = ReplaceRawGenericArgType(*ty, replaceTable, builder); auto [func, done] = StaticCast(superInterfaceTy)->GetExpectedFunc( funcName, funcType, isStatic, funcInstTypeArgs, builder, checkAbstractMethod); if (done) { return {func, done}; } } } return {nullptr, false}; } Type* CustomType::GetExactParentType( const std::string& funcName, FuncType& funcType, bool isStatic, std::vector& funcInstTypeArgs, CHIRBuilder& builder, bool checkAbstractMethod) { std::unordered_map replaceTable; auto classInstArgs = this->GetTypeArgs(); auto classGenericArgs = this->GetCustomTypeDef()->GetGenericTypeParams(); if (!classGenericArgs.empty()) { CJC_ASSERT(classInstArgs.size() == classGenericArgs.size()); } for (size_t i = 0; i < classGenericArgs.size(); ++i) { replaceTable.emplace(classGenericArgs[i], classInstArgs[i]); } // visiting func must in order: // 1. func declared in current def and extend def // 2. func declared in super class def // 3. func declared in super interface, including extend def's super interface // current def // when it's an abstract method, `func` is nullptr if (auto [func, done] = GetCustomTypeDef()->GetExpectedFunc( funcName, funcType, isStatic, replaceTable, funcInstTypeArgs, builder, checkAbstractMethod); done && (func == nullptr || func->Get() == nullptr)) { return this; } // extend def for (auto ex : GetCustomTypeDef()->GetExtends()) { auto match = CollectReplaceTableForExtendedType(*ex->GetExtendedType(), *this, replaceTable); if (!match) { continue; } auto [func, done] = ex->GetExpectedFunc( funcName, funcType, isStatic, replaceTable, funcInstTypeArgs, builder, checkAbstractMethod); // when it's an abstract method, `func` is nullptr if (done && (func == nullptr || func->Get() == nullptr)) { return ReplaceRawGenericArgType(*ex->GetExtendedType(), replaceTable, builder); } } // super class if (auto classTy = DynamicCast(this); classTy && classTy->GetClassDef()->GetSuperClassDef() != nullptr) { Type* superClassTy = classTy->GetClassDef()->GetSuperClassTy(); superClassTy = ReplaceRawGenericArgType(*superClassTy, replaceTable, builder); auto exactParentType = StaticCast(superClassTy)->GetExactParentType( funcName, funcType, isStatic, funcInstTypeArgs, builder, checkAbstractMethod); if (exactParentType != nullptr) { return exactParentType; } } // super interface for (auto ty : GetCustomTypeDef()->GetImplementedInterfaceTys()) { auto superInterfaceTy = ReplaceRawGenericArgType(*ty, replaceTable, builder); auto exactParentType = StaticCast(superInterfaceTy)->GetExactParentType( funcName, funcType, isStatic, funcInstTypeArgs, builder, checkAbstractMethod); if (exactParentType != nullptr) { return exactParentType; } } // extend def's super interface for (auto ex : GetCustomTypeDef()->GetExtends()) { for (auto ty : ex->GetImplementedInterfaceTys()) { auto superInterfaceTy = ReplaceRawGenericArgType(*ty, replaceTable, builder); auto result = StaticCast(superInterfaceTy)->GetExactParentType( funcName, funcType, isStatic, funcInstTypeArgs, builder, checkAbstractMethod); if (result != nullptr) { return result; } } } return nullptr; } std::vector CustomType::GetFuncIndexInVTable( const FuncCallType& funcCallType, bool isStatic, CHIRBuilder& builder) const { std::unordered_map replaceTable; auto classInstArgs = this->GetTypeArgs(); auto classGenericArgs = this->GetCustomTypeDef()->GetGenericTypeParams(); if (!classGenericArgs.empty()) { CJC_ASSERT(classInstArgs.size() == classGenericArgs.size()); } for (size_t i = 0; i < classGenericArgs.size(); ++i) { replaceTable.emplace(classGenericArgs[i], classInstArgs[i]); } auto result = GetCustomTypeDef()->GetFuncIndexInVTable(funcCallType, isStatic, replaceTable, builder); if (!result.empty()) { return result; } for (auto ex : GetCustomTypeDef()->GetExtends()) { replaceTable.clear(); auto extendedTyGenericArgs = ex->GetExtendedType()->GetTypeArgs(); CJC_ASSERT(classInstArgs.size() == extendedTyGenericArgs.size()); for (size_t i = 0; i < classInstArgs.size(); ++i) { CollectGenericReplaceTable(*extendedTyGenericArgs[i], *classInstArgs[i], replaceTable); } result = ex->GetFuncIndexInVTable(funcCallType, isStatic, replaceTable, builder); if (!result.empty()) { return result; } } return result; } std::vector CustomType::CalculateExtendImplementedInterfaceTys(CHIRBuilder& builder, std::set>* visited) const { std::vector allParents; for (auto extendDef : def->GetExtends()) { if (!IsEqualOrInstantiatedTypeOf(*extendDef->GetExtendedType(), builder, visited)) { continue; } auto res = extendDef->GetExtendedType()->CalculateGenericTyMapping(*this); CJC_ASSERT(res.first); for (auto p : extendDef->GetImplementedInterfaceTys()) { auto instParent = StaticCast(ReplaceRawGenericArgType(*p, res.second, builder)); allParents.emplace_back(instParent); } } return allParents; } std::vector CustomType::CalculateImplementedInterfaceTys(CHIRBuilder& builder, std::set>* visited) { auto genericTypes = def->GetGenericTypeParams(); if (!genericTypes.empty()) { CJC_ASSERT(argTys.size() == genericTypes.size()); } auto instSuperInterfaceTys = CalculateExtendImplementedInterfaceTys(builder, visited); std::unordered_map replaceTable; for (size_t i = 0; i < genericTypes.size(); ++i) { replaceTable.emplace(genericTypes[i], argTys[i]); } for (auto ty : def->GetImplementedInterfaceTys()) { instSuperInterfaceTys.emplace_back( StaticCast(ReplaceRawGenericArgType(*ty, replaceTable, builder))); } return instSuperInterfaceTys; } std::vector CustomType::GetImplementedInterfaceTys(CHIRBuilder* builder, std::set>* visited) { std::unique_lock lock(setSuperInterfaceMtx); if (hasSetSuperInterface) { return implementedInterfaceTys; } else { CJC_NULLPTR_CHECK(builder); implementedInterfaceTys = CalculateImplementedInterfaceTys(*builder, visited); hasSetSuperInterface = true; return implementedInterfaceTys; } } std::vector CustomType::GetImplementedInterfaceTysWithoutExtend(CHIRBuilder& builder) { auto genericTypes = def->GetGenericTypeParams(); if (!genericTypes.empty()) { CJC_ASSERT(argTys.size() == genericTypes.size()); } std::vector instSuperInterfaceTys; std::unordered_map replaceTable; for (size_t i = 0; i < genericTypes.size(); ++i) { replaceTable.emplace(genericTypes[i], argTys[i]); } for (auto ty : def->GetImplementedInterfaceTys()) { instSuperInterfaceTys.emplace_back( StaticCast(ReplaceRawGenericArgType(*ty, replaceTable, builder))); } return instSuperInterfaceTys; } StructType::StructType(StructDef* structDef, const std::vector& genericArgs) : CustomType(TypeKind::TYPE_STRUCT, structDef, genericArgs) { } StructDef* StructType::GetStructDef() const { CJC_ASSERT(def != nullptr); return StaticCast(def); } std::string StructType::ToString() const { std::stringstream ss; ss << "Struct"; if (def != nullptr) { ss << "-" << def->GetIdentifierWithoutPrefix(); } ss << PrintArgTys(argTys); return ss.str(); } EnumType::EnumType(EnumDef* enumDef, const std::vector& genericArgs) : CustomType(TypeKind::TYPE_ENUM, enumDef, genericArgs) { } bool EnumType::IsOption() const { return IsCoreOption(*GetEnumDef()); } EnumDef* EnumType::GetEnumDef() const { CJC_ASSERT(def != nullptr); return StaticCast(def); } std::vector EnumType::GetConstructorInfos(CHIRBuilder& builder) const { auto ctors = GetEnumDef()->GetCtors(); auto genericTypeParams = GetEnumDef()->GetGenericTypeParams(); CJC_ASSERT(genericTypeParams.size() == argTys.size()); std::unordered_map replaceTable; for (size_t i = 0; i < genericTypeParams.size(); ++i) { replaceTable.emplace(genericTypeParams[i], argTys[i]); } for (auto& ctor : ctors) { ctor.funcType = StaticCast(ReplaceRawGenericArgType(*ctor.funcType, replaceTable, builder)); } return ctors; } std::string EnumType::ToString() const { std::stringstream ss; ss << "Enum"; if (def != nullptr) { ss << "-" << def->GetIdentifierWithoutPrefix(); } ss << PrintArgTys(argTys); return ss.str(); } bool EnumType::CheckIsBoxed( const EnumType& original, Type& curType, CHIRBuilder& builder, bool doCheck, std::unordered_set& visited) { if (doCheck && &curType == &original) { return true; } auto [_, res] = visited.emplace(&curType); if (!res) { return false; } if (curType.IsEnum()) { auto& enumType = StaticCast(curType); for (auto& ctorInfo : enumType.GetConstructorInfos(builder)) { for (auto ty : ctorInfo.funcType->GetParamTypes()) { if (CheckIsBoxed(original, *ty, builder, true, visited)) { return true; } } } } else if (curType.IsStruct()) { auto& structType = StaticCast(curType); for (auto ty : structType.GetInstantiatedMemberTys(builder)) { if (CheckIsBoxed(original, *ty, builder, true, visited)) { return true; } } } else if (curType.IsTuple()) { auto& tupleType = StaticCast(curType); for (auto ty : tupleType.GetElementTypes()) { if (CheckIsBoxed(original, *ty, builder, true, visited)) { return true; } } } return false; } bool EnumType::IsBoxed(CHIRBuilder& builder) { std::unordered_set visited; return CheckIsBoxed(*this, *this, builder, false, visited); } std::string TupleType::ToString() const { std::stringstream ss; ss << "Tuple"; ss << PrintArgTys(argTys); return ss.str(); } std::string RawArrayType::ToString() const { CJC_ASSERT(!argTys.empty()); auto elemTy = argTys[0]; CJC_NULLPTR_CHECK(elemTy); std::stringstream ss; std::string prefix; std::string suffix; for (unsigned int i = 0; i < dims; i++) { prefix += "RawArray<"; suffix += ">"; } ss << prefix; ss << elemTy->ToString(); ss << suffix; return ss.str(); } size_t RawArrayType::Hash() const { std::size_t hashVal = HashArgTypes(argTys); HashValue(hashVal, dims); HashValue(hashVal, kind); return hashVal; } bool RawArrayType::operator==(const Type& other) const { auto it = DynamicCast(&other); return it && argTys == it->argTys && (dims == it->dims); } std::string VArrayType::ToString() const { std::stringstream ss; ss << "VArray<" << argTys[0]->ToString() << ", $" << std::to_string(size) << ">"; return ss.str(); } size_t VArrayType::Hash() const { std::size_t hashVal = HashArgTypes(argTys); HashValue(hashVal, size); HashValue(hashVal, kind); return hashVal; } bool VArrayType::operator==(const Type& other) const { auto it = DynamicCast(&other); return it && argTys == it->argTys && (size == it->size); } const std::vector& CPointerType::GetExtends(CHIRBuilder* builder) const { if (!GetElementType()->IsUnit()) { CJC_NULLPTR_CHECK(builder); return builder->GetType(builder->GetUnitTy())->GetExtends(builder); } else { return extends; } } std::string CPointerType::ToString() const { std::stringstream ss; ss << "CPointer"; ss << PrintArgTys(argTys); return ss.str(); } std::string RefType::ToString() const { std::stringstream ss; ss << argTys[0]->ToString() << "&"; return ss.str(); } std::string BoxType::ToString() const { std::stringstream ss; ss << "Box<" << argTys[0]->ToString() << ">"; return ss.str(); } std::string ThisType::ToString() const { return "This"; } void GenericType::SetUpperBounds(const std::vector& args) { for (auto arg : args) { if (!orphanFlag) { CJC_ASSERT(!arg->StripAllRefs()->IsValueType() && "Generic type upper bound should NOT be value type!"); } } upperBounds = args; std::sort(upperBounds.begin(), upperBounds.end(), [](auto l, auto r) { return l->ToString() < r->ToString(); }); } void GenericType::GetInstMap(std::unordered_map& instMap, CHIRBuilder& builder) const { for (auto upperBound : upperBounds) { auto upperBoundCustomType = StaticCast(upperBound->StripAllRefs()); upperBoundCustomType->GetInstMap(instMap, builder); } } std::pair GenericType::GetInstMemberTypeByPathCheckingReadOnly( const std::vector& path, CHIRBuilder& builder) const { // Find the most child class type Type* concretType = nullptr; for (auto upperBound : upperBounds) { auto upperBoundCustomType = StaticCast(upperBound->StripAllRefs()); if (upperBoundCustomType->GetCustomTypeDef()->IsClassLike()) { if (concretType == nullptr) { concretType = upperBound; } else { if (upperBoundCustomType->IsEqualOrSubTypeOf(*concretType->StripAllRefs(), builder)) { concretType = upperBound; } } } } CJC_NULLPTR_CHECK(concretType); return StaticCast( concretType->StripAllRefs())->GetInstMemberTypeByPathCheckingReadOnly(path, builder); } size_t GenericType::Hash() const { std::size_t hashVal{0}; HashValue(hashVal, identifier); return hashVal; } bool GenericType::operator==(const Type& other) const { auto it = DynamicCast(&other); return it && identifier == it->identifier; } std::string GenericType::ToString() const { std::stringstream ss; if (orphanFlag) { ss << "(orphan)" << upperBounds[0]->ToString(); } else { ss << "Generic-" << identifier; } return ss.str(); } namespace Cangjie::CHIR { Type* GetFieldOfType(Type& baseTy, uint64_t index, CHIRBuilder& builder) { Type* type = nullptr; if (baseTy.IsRef()) { type = GetFieldOfType(*StaticCast(baseTy).GetBaseType(), index, builder); } else if (baseTy.IsTuple()) { auto& tupleTy = StaticCast(baseTy); if (index < tupleTy.GetElementTypes().size()) { type = tupleTy.GetElementType(index); } } else if (baseTy.IsStruct()) { auto& structTy = StaticCast(baseTy); auto memberTys = structTy.GetInstantiatedMemberTys(builder); if (index < memberTys.size()) { type = memberTys[index]; } } else if (baseTy.IsClass()) { auto& classTy = StaticCast(baseTy); auto memberTys = classTy.GetInstantiatedMemberTys(builder); if (index < memberTys.size()) { type = memberTys[index]; } } else if (baseTy.IsEnum()) { if (index == 0) { if (GetSelectorType(*StaticCast(baseTy).GetEnumDef()) == Type::TypeKind::TYPE_BOOLEAN) { type = builder.GetBoolTy(); } else { type = builder.GetUInt32Ty(); } } } else if (baseTy.IsRawArray()) { type = StaticCast(baseTy).GetElementType(); } return type; } static bool CalculateGenericTyMappingImpl( const Type& instType, const Type& genericType, std::unordered_map& replaceTable) { if (genericType.IsGeneric()) { auto& gType = StaticCast(genericType); auto it = replaceTable.find(&gType); if (it != replaceTable.end()) { return it->second == &instType; } else { replaceTable.emplace(&gType, const_cast(&instType)); return true; } } if (&instType == &genericType) { return true; } if (instType.GetTypeKind() != genericType.GetTypeKind()) { return false; } if (auto instCustomType = Cangjie::DynamicCast(&instType)) { if (instCustomType->GetCustomTypeDef() != Cangjie::StaticCast(genericType).GetCustomTypeDef()) { return false; } } auto instTypeArgs = instType.GetTypeArgs(); auto genericTypeArgs = genericType.GetTypeArgs(); if (instTypeArgs.size() != genericTypeArgs.size()) { return false; } for (size_t i = 0; i < instTypeArgs.size(); ++i) { if (!CalculateGenericTyMappingImpl(*instTypeArgs[i], *genericTypeArgs[i], replaceTable)) { return false; } } return true; } std::pair> Type::CalculateGenericTyMapping( const Type& targetTy) const { std::unordered_map replaceTable; auto res = CalculateGenericTyMappingImpl(targetTy, *this, replaceTable); return {res, replaceTable}; } void Type::VisitTypeRecursively(const std::function& visitor) const { if (visitor(*this)) { for (auto ty : argTys) { ty->VisitTypeRecursively(visitor); } } } std::vector BuiltinType::GetSuperTypesRecusively(CHIRBuilder& builder, std::set>* visited) { std::vector inheritanceList; for (auto extendDef : GetExtends(&builder)) { if (!IsEqualOrInstantiatedTypeOf(*extendDef->GetExtendedType(), builder, visited)) { continue; } for (auto interface : extendDef->GetImplementedInterfaceTys()) { GetAllInstantiatedParentType(*interface, builder, inheritanceList, visited); } } return inheritanceList; } const std::vector& BuiltinType::GetExtends([[maybe_unused]]CHIRBuilder* builder) const { return extends; } void BuiltinType::AddExtend(ExtendDef& extend) { extends.emplace_back(&extend); } std::vector BuiltinType::GetExtendMethods() const { std::vector methods; for (auto def : extends) { auto funcs = def->GetMethods(); methods.insert(methods.end(), funcs.begin(), funcs.end()); } return methods; } std::vector BuiltinType::GetDeclareAndExtendMethods([[maybe_unused]] CHIRBuilder& builder) const { return GetExtendMethods(); } static bool TupleTypeIsEqualOrSub(const TupleType& subType, const TupleType& parentType, CHIRBuilder& builder) { auto thisTupleArgs = subType.GetElementTypes(); auto parentTupleArgs = parentType.GetElementTypes(); if (thisTupleArgs.size() != parentTupleArgs.size()) { return false; } for (size_t i = 0; i < thisTupleArgs.size(); ++i) { if (!thisTupleArgs[i]->IsEqualOrSubTypeOf(*parentTupleArgs[i], builder)) { return false; } } return true; } static bool FuncTypeIsEqualOrSub(const FuncType& subType, const FuncType& parentType, CHIRBuilder& builder) { if (subType.IsCFunc() != parentType.IsCFunc()) { return false; } if (subType.HasVarArg() != parentType.HasVarArg()) { return false; } auto thisParamTypes = subType.GetParamTypes(); auto parentParamTypes = parentType.GetParamTypes(); if (thisParamTypes.size() != parentParamTypes.size()) { return false; } for (size_t i = 0; i < thisParamTypes.size(); ++i) { if (!parentParamTypes[i]->IsEqualOrSubTypeOf(*thisParamTypes[i], builder)) { return false; } } return subType.GetReturnType()->IsEqualOrSubTypeOf(*parentType.GetReturnType(), builder); } static const ClassType* GetAutoEnvGenericBase(ClassType& type, CHIRBuilder& builder) { CJC_ASSERT(type.IsAutoEnv()); if (type.IsAutoEnvGenericBase()) { return &type; } return GetAutoEnvGenericBase(*type.GetSuperClassTy(&builder), builder); } static FuncType* ConvertClosureTypeToFuncType(ClassType& closureType, CHIRBuilder& builder) { auto typeArgs = GetAutoEnvGenericBase(closureType, builder)->GetGenericArgs(); CJC_ASSERT(!typeArgs.empty()); auto retType = typeArgs.back(); typeArgs.pop_back(); return builder.GetType(typeArgs, retType); } static bool ClosureTypeIsEqualOrSub(ClassType& subType, ClassType& parentType, CHIRBuilder& builder) { auto subFuncType = ConvertClosureTypeToFuncType(subType, builder); auto parentFuncType = ConvertClosureTypeToFuncType(parentType, builder); return FuncTypeIsEqualOrSub(*subFuncType, *parentFuncType, builder); } bool Type::SatisfyCType() const { // same logical with `AST::Ty::IsMetCType` auto builtinCType = (kind >= TYPE_INT8 && kind <= TYPE_UNIT) || kind == TYPE_CPOINTER || kind == TYPE_CSTRING; auto isCStruct = (kind == TYPE_STRUCT) && StaticCast(this)->GetStructDef()->IsCStruct(); auto varrayMeetCondition = (kind == TYPE_VARRAY) && StaticCast(this)->GetElementType()->SatisfyCType(); return builtinCType || isCStruct || IsCFunc() || varrayMeetCondition; } bool Type::IsEqualOrSubTypeOf(const Type& parentType, CHIRBuilder& builder, std::set>* visited) const { /** * there may be an unresolvable inheritance relationship: * interface I {} * abstract class C {} * extend C <: I where T <: I {} * class D <: C {} * main() {0} * * if you want to know if class D is sub type of I, that will be a disaster, because * parent type of class D is C, and in `extend C <: I`, interface I is class C's parent type, * so you only need to know if `C <: I` satisfy generic constraints which `T <: I`, then you need to * compute if the `T` can be class D, that means you have to know if class D is sub type of I, tail bites head. * so we need to check if the sub type and parent type have been visited, if visited, we don't think they have a * parent-child relationship and Sema does the same */ std::set> empty; visited = visited ? visited : ∅ if (!visited->emplace(this, &parentType).second) { return false; } if (this->IsRef() && parentType.IsRef()) { auto subBaseType = StaticCast(this)->GetBaseType(); auto parentBaseType = StaticCast(parentType).GetBaseType(); return subBaseType->IsEqualOrSubTypeOf(*parentBaseType, builder, visited); } auto thisDeref = this->StripAllRefs(); auto parentDeref = parentType.StripAllRefs(); // we can't think class-A and class-A& is equal if ((this->IsRef() || parentType.IsRef()) && (thisDeref == parentDeref)) { return false; } // Nothing type is all types' sub type, Any type is all types' parent type if (thisDeref == parentDeref || thisDeref->IsNothing() || parentDeref->IsAny()) { return true; } // invalid type shouldn't be compared, just return false // if parent type is generic type, that means it can be any type, even if it has upper bounds if (thisDeref->IsInvalid() || parentDeref->IsInvalid() || parentDeref->IsGeneric()) { return false; } if (parentDeref->IsCType()) { return this->SatisfyCType(); } if (thisDeref->IsGeneric()) { for (auto upperBound : StaticCast(thisDeref)->GetUpperBounds()) { if (!upperBound->StripAllRefs()->IsEqualOrSubTypeOf(*parentDeref, builder, visited)) { return false; } } return true; } if (thisDeref->IsTuple()) { if (!parentDeref->IsTuple()) { return false; } return TupleTypeIsEqualOrSub( *StaticCast(thisDeref), *StaticCast(parentDeref), builder); } if (thisDeref->IsFunc()) { if (!parentDeref->IsFunc()) { return false; } return FuncTypeIsEqualOrSub( *StaticCast(thisDeref), *StaticCast(parentDeref), builder); } if (thisDeref->IsAutoEnv()) { if (!parentDeref->IsAutoEnv()) { return false; } return ClosureTypeIsEqualOrSub( *StaticCast(thisDeref), *StaticCast(parentDeref), builder); } std::vector allParents; if (auto builtinType = DynamicCast(thisDeref)) { allParents = builtinType->GetSuperTypesRecusively(builder, visited); } else if (auto customType = DynamicCast(thisDeref)) { allParents = customType->GetSuperTypesRecusively(builder, visited); } for (auto pTy : allParents) { if (pTy == parentDeref) { return true; } else if (auto res = parentDeref->CalculateGenericTyMapping(*pTy); res.first) { // we need to remove this `if` after new ir checker merged return true; } } return false; } bool Type::IsEqualOrInstantiatedTypeOf(const Type& genericRelatedType, CHIRBuilder& builder, std::set>* visited) const { if (this == &genericRelatedType) { return true; } auto res = genericRelatedType.CalculateGenericTyMapping(*this); if (!res.first) { return false; } for (auto& it : res.second) { if (!it.second->SatisfyGenericConstraints(*it.first, builder, res.second, visited)) { return false; } } return true; } bool Type::SatisfyGenericConstraints(const GenericType& type, CHIRBuilder& builder, const std::unordered_map& instMap, std::set>* visited) const { for (auto upperBound : type.GetUpperBounds()) { if (!IsEqualOrSubTypeOf(*ReplaceRawGenericArgType(*upperBound, instMap, builder), builder, visited)) { return false; } } return true; } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/UserDefinedType.cpp000066400000000000000000000007621510705540100220730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/UserDefinedType.h" #include "cangjie/CHIR/Type/Type.h" using namespace Cangjie::CHIR; bool CHIRTypeCompare::operator()(const Type* key1, const Type* key2) const { return key1->ToString() < key2->ToString(); }cangjie_compiler-1.0.7/src/CHIR/Utils.cpp000066400000000000000000001514271510705540100201410ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements utils API for CHIR */ #include "cangjie/CHIR/Utils.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Type/CustomTypeDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Visitor/Visitor.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/ConstantsUtils.h" #include #include namespace Cangjie::CHIR { bool CheckFuncName(const FuncBase& func, const FuncInfo& funcInfo) { return func.GetSrcCodeIdentifier() == funcInfo.funcName; } bool CheckParentTy(const FuncBase& func, const FuncInfo& funcInfo) { if (funcInfo.parentTy == NOT_CARE) { return true; } auto parentTy = func.GetParentCustomTypeDef(); if (auto extendDef = DynamicCast(parentTy); extendDef) { auto extendedType = extendDef->GetExtendedType(); if (extendedType == nullptr) { parentTy = nullptr; } else if (auto customTy = DynamicCast(extendedType); customTy) { parentTy = customTy->GetCustomTypeDef(); } else { parentTy = nullptr; } } bool parentMatch = false; if (parentTy == nullptr) { if (funcInfo.parentTy.empty()) { parentMatch = true; } } else { if (funcInfo.parentTy == ANY_TYPE) { parentMatch = true; } else if (funcInfo.parentTy == parentTy->GetSrcCodeIdentifier()) { parentMatch = true; } else if (funcInfo.parentTy.find(Cangjie::BOX_DECL_PREFIX) != std::string::npos && parentTy->GetSrcCodeIdentifier().find(funcInfo.parentTy) == 0) { parentMatch = true; } } return parentMatch; } bool CheckParametersTy(const FuncBase& funcBase, const FuncInfo& funcInfo) { bool needToMatchArgsNum = true; auto paramTys = funcBase.GetFuncType()->GetParamTypes(); size_t loopCnt = funcInfo.params.size() < paramTys.size() ? funcInfo.params.size() : paramTys.size(); for (size_t i = 0; i < loopCnt; ++i) { std::string expectedArgTyStr = funcInfo.params[i]; if (expectedArgTyStr == ANY_TYPE) { continue; } if (expectedArgTyStr == NOT_CARE) { needToMatchArgsNum = false; break; } // The ToString() function will return the mangled type name, but // the expected name is not mangled. This is tracked by issue 2385. if (expectedArgTyStr != paramTys[i]->ToString()) { return false; } } // expect: func foo(a, b, c, NOT_CARE) // in fact: func foo(a, b, c) // then, `funcInfo.params.size()` is 4 and `args.size()` is 3, but we set `NOT_CARE` as 4th arg, // that means we don't care about what's next or if there is next // well, if we expect `func foo(a, b, c, NOT_CARE, d, e, f)`, and we still can match `func foo(a, b, c)` // so we should use `ANY_TYPE` instead of `NOT_CARE` in this case if (funcInfo.params.size() > loopCnt && funcInfo.params[loopCnt] == NOT_CARE) { needToMatchArgsNum = false; } if (needToMatchArgsNum && funcInfo.params.size() != paramTys.size()) { return false; } return true; } bool CheckReturnTy(const FuncBase& func, const FuncInfo& funcInfo) { if (funcInfo.returnTy == NOT_CARE || funcInfo.returnTy == ANY_TYPE) { return true; } auto returnTy = func.GetReturnType(); CJC_NULLPTR_CHECK(returnTy); // The ToString() function will return the mangled type name, but // the expected name is not mangled. This is tracked by issue 2385. return returnTy->ToString() == funcInfo.returnTy; } bool CheckPkgName(const FuncBase& func, const FuncInfo& funcInfo) { if (funcInfo.pkgName == NOT_CARE || funcInfo.pkgName == ANY_TYPE) { return true; } // Note: 现在还没有泛型声明版本的包名,待添加 return funcInfo.pkgName == func.GetPackageName(); } bool IsExpectedFunction(const FuncBase& func, const FuncInfo& funcInfo) { return CheckFuncName(func, funcInfo) && CheckParentTy(func, funcInfo) && CheckParametersTy(func, funcInfo) && CheckReturnTy(func, funcInfo) && CheckPkgName(func, funcInfo); } void PrintOptInfo(const Expression& e, bool debug, const std::string& optName) { if (!debug) { return; } auto& position = e.GetDebugLocation(); std::string strPos = "in the line:" + std::to_string(position.GetBeginPos().line) + " and the column:" + std::to_string(position.GetBeginPos().column); std::string message = "The " + e.GetExprKindName() + " node " + strPos + ", was optimized by " + optName; std::cout << message << std::endl; } FuncKind GetFuncKindFromAST(const AST::FuncDecl& func) { if (func.isGetter) { return FuncKind::GETTER; } if (func.isSetter) { return FuncKind::SETTER; } if (func.IsFinalizer()) { return FuncKind::FINALIZER; } if (func.TestAttr(AST::Attribute::MAIN_ENTRY)) { return FuncKind::MAIN_ENTRY; } if (func.TestAttr(AST::Attribute::MACRO_FUNC)) { return FuncKind::MACRO_FUNC; } if (func.TestAttr(AST::Attribute::PRIMARY_CONSTRUCTOR)) { if (func.TestAttr(AST::Attribute::IN_STRUCT)) { return FuncKind::PRIMAL_STRUCT_CONSTRUCTOR; } else if (func.TestAttr(AST::Attribute::IN_CLASSLIKE)) { return FuncKind::PRIMAL_CLASS_CONSTRUCTOR; } else { CJC_ABORT(); } } else if (func.TestAttr(AST::Attribute::CONSTRUCTOR)) { if (func.TestAttr(AST::Attribute::IN_STRUCT)) { return FuncKind::STRUCT_CONSTRUCTOR; } else if (func.TestAttr(AST::Attribute::IN_CLASSLIKE)) { return FuncKind::CLASS_CONSTRUCTOR; } else { CJC_ABORT(); } } if (func.TestAnyAttr(AST::Attribute::HAS_INITIAL)) { return FuncKind::DEFAULT_PARAMETER_FUNC; } return FuncKind::DEFAULT; } bool IsVirtualFunction(const FuncBase& funcDecl) { // rule 1: global function is not virtual function auto parent = funcDecl.GetParentCustomTypeDef(); if (parent == nullptr) { return false; } // rule 2: function declared in struct, enum and extend def is not virtual function auto parentClass = DynamicCast(parent); if (parentClass == nullptr) { return false; } // function declared in interface, must be virtual function if (parentClass->IsInterface()) { return true; } /** * A special case: * class A { * // The following functions are not semantic virtual functions since the class is not `open`, * // they will never be overrode even if they have `open` flag. * public open func foo() {} * open func goo() {} * } */ // rule 3: if parent class is not open, this function is not virtual function auto isInNonOpenClass = !parentClass->CanBeInherited(); if (isInNonOpenClass) { return false; } // static function declared in open class or interface, also need add to vtable if (funcDecl.TestAttr(Attribute::STATIC)) { return true; } // rule 4: constructors and de-constructors are not virtual function if (funcDecl.GetFuncKind() == FuncKind::CLASS_CONSTRUCTOR || funcDecl.GetFuncKind() == FuncKind::FINALIZER) { return false; } // rule 5: generic instantiated function, is not virtual function if (funcDecl.TestAttr(Attribute::GENERIC_INSTANTIATED)) { return false; } // rule 6: only public or protected function has the opportunity to be overridden if (!funcDecl.TestAttr(Attribute::PUBLIC) && !funcDecl.TestAttr(Attribute::PROTECTED)) { return false; } // rule 7: if the base function has open modifier, or override modifier, or is abstract, // it must be virtual function return funcDecl.TestAttr(Attribute::VIRTUAL) || funcDecl.TestAttr(Attribute::ABSTRACT) || funcDecl.TestAttr(Attribute::OVERRIDE); } bool IsInterfaceStaticMethod(const Func& func) { auto parent = func.GetParentCustomTypeDef(); if (parent == nullptr) { return false; } auto parentClass = DynamicCast(parent); if (parentClass == nullptr) { return false; } if (!parentClass->IsInterface()) { return false; } return func.TestAttr(Attribute::STATIC); } struct VisitStackElem { Block* bb; std::vector successors; VisitStackElem(Block* bb, std::vector&& successors) : bb(bb), successors(std::move(successors)) { } }; std::deque TopologicalSort(Block* entrybb) { CJC_NULLPTR_CHECK(entrybb); std::deque res; std::unordered_set visited; std::stack visitStack; visited.insert(entrybb); visitStack.emplace(entrybb, entrybb->GetSuccessors()); while (!visitStack.empty()) { while (!visitStack.top().successors.empty()) { auto lastSuc = visitStack.top().successors.back(); visitStack.top().successors.pop_back(); if (auto [_, hasInserted] = visited.emplace(lastSuc); hasInserted) { visitStack.emplace(lastSuc, lastSuc->GetSuccessors()); } } do { res.emplace_front(visitStack.top().bb); visitStack.pop(); } while (!visitStack.empty() && visitStack.top().successors.empty()); }; return res; } void AddExpressionsToGlobalInitFunc(const Func& initFunc, const std::vector& insertExpr) { auto visitAction = [&insertExpr](Expression& expr) { if (expr.GetExprKind() != ExprKind::STORE) { return VisitResult::CONTINUE; } auto storeNode = static_cast(&expr); if (storeNode->GetLocation()->GetSrcCodeIdentifier() != GV_PKG_INIT_ONCE_FLAG) { return VisitResult::CONTINUE; } auto parent = expr.GetParentBlock(); const auto& exprs = parent->GetExpressions(); for (auto it : insertExpr) { it->MoveBefore(exprs.back()); } return VisitResult::STOP; }; Visitor::Visit(initFunc, visitAction); } std::vector> GetStaticMemberVars(const AST::InheritableDecl& decl) { std::vector> memberVars; auto members = decl.GetMemberDeclPtrs(); for (auto& member : members) { if (member->astKind == AST::ASTKind::VAR_DECL && member->TestAttr(AST::Attribute::STATIC)) { memberVars.emplace_back(StaticCast(member.get())); } } return memberVars; } std::vector> GetNonStaticMemberVars(const AST::InheritableDecl& decl) { std::vector> memberVars; auto members = decl.GetMemberDeclPtrs(); for (auto& member : members) { if (member->astKind == AST::ASTKind::VAR_DECL && !member->TestAttr(AST::Attribute::STATIC)) { memberVars.emplace_back(StaticCast(member.get())); } } return memberVars; } std::vector> GetNonStaticSuperMemberVars(const AST::ClassLikeDecl& classLikeDecl) { if (auto classDecl = DynamicCast(&classLikeDecl); classDecl) { auto superClassDecl = classDecl->GetSuperClassDecl(); if (superClassDecl != nullptr) { auto grandSuperMemberVars = GetNonStaticSuperMemberVars(*superClassDecl); auto superMemberVars = GetNonStaticMemberVars(*superClassDecl); grandSuperMemberVars.insert(grandSuperMemberVars.end(), superMemberVars.begin(), superMemberVars.end()); return grandSuperMemberVars; } } return std::vector>{}; } bool IsCrossPackage(const Cangjie::Position& pos, const std::string& currentPackage, DiagAdapter& diag) { if (diag.GetSourceManager().GetSource(pos.fileID).packageName.has_value()) { if (currentPackage != diag.GetSourceManager().GetSource(pos.fileID).packageName.value()) { return true; } } return false; } bool IsNestedBlockOf(const BlockGroup* blockGroup, const BlockGroup* scope) { CJC_NULLPTR_CHECK(blockGroup); CJC_NULLPTR_CHECK(scope); if (blockGroup == scope) { return true; } if (auto parentLambda = DynamicCast(blockGroup->GetOwnerExpression())) { return IsNestedBlockOf(parentLambda->GetParentBlockGroup(), scope); } return false; } Type* CreateNewTypeWithArgs(Type& oldType, const std::vector& newArgs, CHIRBuilder& builder) { if (newArgs.empty()) { return &oldType; } Type* newType = nullptr; switch (oldType.GetTypeKind()) { case Type::TypeKind::TYPE_TUPLE: newType = builder.GetType(newArgs); break; case Type::TypeKind::TYPE_STRUCT: newType = builder.GetType(StaticCast(oldType).GetStructDef(), newArgs); break; case Type::TypeKind::TYPE_ENUM: { auto& enumType = StaticCast(oldType); newType = builder.GetType(enumType.GetEnumDef(), newArgs); break; } case Type::TypeKind::TYPE_FUNC: { std::vector paramTys{newArgs.begin(), newArgs.end() - 1}; Type* retTy = newArgs.back(); auto hasVarArg = StaticCast(oldType).HasVarArg(); newType = builder.GetType(paramTys, retTy, hasVarArg, oldType.IsCFunc()); break; } case Type::TypeKind::TYPE_CLASS: newType = builder.GetType(StaticCast(oldType).GetClassDef(), newArgs); break; case Type::TypeKind::TYPE_REFTYPE: CJC_ASSERT(newArgs.size() == 1); newType = builder.GetType(newArgs[0]); break; case Type::TypeKind::TYPE_CPOINTER: CJC_ASSERT(newArgs.size() == 1); newType = builder.GetType(newArgs[0]); break; case Type::TypeKind::TYPE_RAWARRAY: CJC_ASSERT(newArgs.size() == 1); newType = builder.GetType(newArgs[0], StaticCast(oldType).GetDims()); break; case Type::TypeKind::TYPE_VARRAY: CJC_ASSERT(newArgs.size() == 1); newType = builder.GetType(newArgs[0], StaticCast(oldType).GetSize()); break; case Type::TypeKind::TYPE_BOXTYPE: CJC_ASSERT(newArgs.size() == 1); newType = builder.GetType(newArgs[0]); break; default: CJC_ABORT(); break; } return newType; } // replace generic type with instantiated type Type* ReplaceRawGenericArgType( Type& type, const std::unordered_map& replaceTable, CHIRBuilder& builder) { // nothing to replace, just return `type` itself if (replaceTable.empty()) { return &type; } // e.g. class A { func foo() {} } // if `type` is `foo::T`, return instantiated type // if `type` is `foo::U`, return `U` auto genericTy = DynamicCast(&type); if (genericTy != nullptr) { auto it = replaceTable.find(genericTy); if (it != replaceTable.end()) { return it->second; } else { return &type; } } std::vector newArgs; for (auto argTy : type.GetTypeArgs()) { newArgs.emplace_back(ReplaceRawGenericArgType(*argTy, replaceTable, builder)); } return CreateNewTypeWithArgs(type, newArgs, builder); } Type* ReplaceThisTypeToConcreteType(Type& type, Type& concreteType, CHIRBuilder& builder) { if (auto refTy = DynamicCast(&type); refTy && refTy->GetBaseType()->IsThis()) { return &concreteType; } else if (type.IsThis()) { return &concreteType; } std::vector newArgs; for (auto argTy : type.GetTypeArgs()) { newArgs.emplace_back(ReplaceThisTypeToConcreteType(*argTy, concreteType, builder)); } return CreateNewTypeWithArgs(type, newArgs, builder); } FuncType* ConvertRealFuncTypeToVirtualFuncType(const FuncType& type, CHIRBuilder& builder) { auto realParams = type.GetParamTypes(); CJC_ASSERT(!realParams.empty()); auto paramInVtable = std::vector{realParams.begin() + 1, realParams.end()}; return builder.GetType(paramInVtable, builder.GetType()); } Value* TypeCastIfNeeded( Value& val, Type& expectedTy, CHIRBuilder& builder, Block& parentBlock, const DebugLocation& loc, bool needCheck) { // Do not cast a nothing type value, otherwise it will mess up the dead code elimination if (val.GetType()->IsNothing()) { return &val; } if (val.GetType() != &expectedTy) { Value* tmpValue = &val; if (val.GetType()->StripAllRefs()->IsGeneric() && val.GetType()->IsRef()) { auto load = builder.CreateExpression(val.GetType()->StripAllRefs(), &val, &parentBlock); parentBlock.AppendExpression(load); tmpValue = load->GetResult(); } auto ret = builder.CreateExpression(loc, &expectedTy, tmpValue, &parentBlock); parentBlock.AppendExpression(ret); ret->Set(needCheck); return ret->GetResult(); } return &val; } static bool HasGenericInNonFuncScope(const Type& type) { bool hasGenericInNonFuncScope = false; auto visitor = [&hasGenericInNonFuncScope](const Type& type) { if (type.IsFunc()) { return false; } if (type.IsGeneric()) { hasGenericInNonFuncScope = true; } return true; }; type.VisitTypeRecursively(visitor); return hasGenericInNonFuncScope; } Ptr TransformGenericIfNeeded( Value& val, Type& expectedTy, CHIRBuilder& builder, Block& parentBlock, const DebugLocation& loc, bool needCheck) { (void)needCheck; // Specially, the cast for generation/de-generation enum still use type-cast if (val.GetType()->IsEnum() && (expectedTy.IsTuple() || expectedTy.IsUnsignedInteger())) { return nullptr; } if ((val.GetType()->IsTuple() || val.GetType()->IsUnsignedInteger()) && expectedTy.IsEnum()) { return nullptr; } // this should be deleted after supported by codegen if (val.GetType()->IsStructArray() && expectedTy.IsStructArray()) { return nullptr; } if (HasGenericInNonFuncScope(expectedTy) && !HasGenericInNonFuncScope(*val.GetType())) { auto ret = builder.CreateExpression(loc, &expectedTy, &val, &parentBlock); parentBlock.AppendExpression(ret); return ret->GetResult(); } else if (HasGenericInNonFuncScope(*val.GetType()) && !HasGenericInNonFuncScope(expectedTy)) { auto ret = builder.CreateExpression(loc, &expectedTy, &val, &parentBlock); parentBlock.AppendExpression(ret); return ret->GetResult(); } return nullptr; } bool LeftTypeIsBoxOfRightType(const Type& left, const Type& right) { // BoxType must be ref if (!left.IsRef()) { return false; } auto boxType = DynamicCast(StaticCast(left).GetBaseType()); if (boxType == nullptr) { return false; } return &right == boxType->GetBaseType(); } Ptr BoxIfNeeded( Value& val, Type& expectedTy, CHIRBuilder& builder, Block& parentBlock, const DebugLocation& loc, bool needCheck) { (void)needCheck; auto srcTy = val.GetType(); auto srcIsValueTy = srcTy->IsValueType(); if (auto ref = DynamicCast(srcTy)) { CJC_ASSERT(!ref->GetBaseType()->IsRef()); srcIsValueTy = ref->GetBaseType()->IsValueType(); } auto srcIsReferenceTy = srcTy->IsRef() && StaticCast(srcTy)->GetBaseType()->IsReferenceType(); auto dstIsValueTy = expectedTy.IsValueType(); if (auto ref = DynamicCast(&expectedTy)) { CJC_ASSERT(!ref->GetBaseType()->IsRef()); dstIsValueTy = ref->GetBaseType()->IsValueType(); } auto dstIsReferenceTy = expectedTy.IsRef() && StaticCast(&expectedTy)->GetBaseType()->IsReferenceType(); if (srcIsValueTy && dstIsReferenceTy) { auto boxSrcVal = &val; if (srcTy->IsRef()) { auto loadSrc = builder.CreateExpression( loc, StaticCast(srcTy)->GetBaseType(), boxSrcVal, &parentBlock); parentBlock.AppendExpression(loadSrc); boxSrcVal = loadSrc->GetResult(); } auto ret = builder.CreateExpression(loc, &expectedTy, boxSrcVal, &parentBlock); parentBlock.AppendExpression(ret); return ret->GetResult(); } else if (srcIsReferenceTy && dstIsValueTy) { if (expectedTy.IsRef()) { auto ret = builder.CreateExpression(loc, &expectedTy, &val, &parentBlock); parentBlock.AppendExpression(ret); return ret->GetResult(); } else { auto ret = builder.CreateExpression(loc, &expectedTy, &val, &parentBlock); parentBlock.AppendExpression(ret); return ret->GetResult(); } } else if (LeftTypeIsBoxOfRightType(*srcTy, expectedTy)) { auto ret = builder.CreateExpression(loc, &expectedTy, &val, &parentBlock); parentBlock.AppendExpression(ret); return ret->GetResult(); } else if (LeftTypeIsBoxOfRightType(expectedTy, *srcTy)) { auto ret = builder.CreateExpression(loc, &expectedTy, &val, &parentBlock); parentBlock.AppendExpression(ret); return ret->GetResult(); } return nullptr; } // this API should not force to insert the generated expression into the end of block Ptr TypeCastOrBoxIfNeeded( Value& val, Type& expectedTy, CHIRBuilder& builder, Block& parentBlock, const DebugLocation& loc, bool needCheck) { Ptr ret; ret = BoxIfNeeded(val, expectedTy, builder, parentBlock, loc, needCheck); if (ret != nullptr) { return ret; } ret = TransformGenericIfNeeded(val, expectedTy, builder, parentBlock, loc, needCheck); if (ret != nullptr) { return ret; } return TypeCastIfNeeded(val, expectedTy, builder, parentBlock, loc, needCheck); } bool HasNothingType(Type& type) { if (type.IsNothing()) { return true; } if (type.IsFunc()) { auto funcType = StaticCast(&type); for (auto param : funcType->GetParamTypes()) { if (HasNothingType(*param)) { return true; } } return HasNothingType(*funcType->GetReturnType()); } else if (type.IsTuple()) { auto tupleType = StaticCast(&type); for (auto item : tupleType->GetElementTypes()) { if (HasNothingType(*item)) { return true; } } } return false; } void ReplaceUsesWithWrapper(Value& curFunc, const Apply* apply, Value& wrapperFunc, bool isForeign) { for (auto user : curFunc.GetUsers()) { if (user == apply) { continue; } if (user->GetExprKind() == ExprKind::APPLY) { auto applyNode = StaticCast(user); if (!isForeign && applyNode->GetCallee() == &curFunc) { continue; } } if (user->GetExprKind() == ExprKind::APPLY_WITH_EXCEPTION) { auto applyNode = StaticCast(user); if (!isForeign && applyNode->GetCallee() == &curFunc) { continue; } } user->ReplaceOperand(&curFunc, &wrapperFunc); } } bool IsStaticInit(const AST::FuncDecl& func) { return func.TestAttr(AST::Attribute::STATIC, AST::Attribute::CONSTRUCTOR) && func.identifier == STATIC_INIT_FUNC; } bool IsStaticInit(const FuncBase& func) { return func.TestAttr(Attribute::STATIC) && (func.GetFuncKind() == FuncKind::CLASS_CONSTRUCTOR || func.GetFuncKind() == FuncKind::STRUCT_CONSTRUCTOR) && func.GetSrcCodeIdentifier() == STATIC_INIT_FUNC; } bool IsSuperOrThisCall(const AST::CallExpr& expr) { if (expr.callKind == AST::CallKind::CALL_SUPER_FUNCTION) { return true; } if (auto baseFunc = DynamicCast(expr.baseFunc.get())) { return baseFunc->isThis; } return false; } std::unordered_map GetInstMapFromCurDefToCurType(const CustomType& curType) { auto genericTypeArgs = curType.GetCustomTypeDef()->GetGenericTypeParams(); auto instTypeArgs = curType.GetTypeArgs(); if (!genericTypeArgs.empty()) { CJC_ASSERT(genericTypeArgs.size() == instTypeArgs.size()); } std::unordered_map replaceTable; for (size_t i = 0; i < genericTypeArgs.size(); ++i) { replaceTable.emplace(genericTypeArgs[i], instTypeArgs[i]); } return replaceTable; } std::unordered_map GetInstMapFromCurDefAndExDefToCurType(const CustomType& curType) { auto replaceTable = GetInstMapFromCurDefToCurType(curType); for (auto extendDef : curType.GetCustomTypeDef()->GetExtends()) { // maybe we can meet `extend A> {}`, and `curType` is A, then ignore this def, // so not need to check `res` auto [res, tempTable] = extendDef->GetExtendedType()->CalculateGenericTyMapping(curType); replaceTable.merge(tempTable); } return replaceTable; } void GetAllInstantiatedParentType(ClassType& cur, CHIRBuilder& builder, std::vector& parents, std::set>* visited) { if (std::find(parents.begin(), parents.end(), &cur) != parents.end()) { return; } for (auto ex : cur.GetCustomTypeDef()->GetExtends()) { if (!cur.IsEqualOrInstantiatedTypeOf(*ex->GetExtendedType(), builder, visited)) { continue; } // maybe we can meet `extend A> {}`, and `curType` is A, then ignore this def, // so not need to check `res` auto [res, replaceTable] = ex->GetExtendedType()->CalculateGenericTyMapping(cur); for (auto interface : ex->GetImplementedInterfaceTys()) { std::vector extendParents; GetAllInstantiatedParentType(*interface, builder, extendParents, visited); for (size_t i = 0; i < extendParents.size(); ++i) { extendParents[i] = Cangjie::StaticCast(ReplaceRawGenericArgType(*extendParents[i], replaceTable, builder)); if (std::find(parents.begin(), parents.end(), extendParents[i]) == parents.end()) { parents.emplace_back(extendParents[i]); } } } } for (auto interface : cur.GetImplementedInterfaceTys(&builder, visited)) { GetAllInstantiatedParentType(*interface, builder, parents, visited); } if (auto superClassTy = cur.GetSuperClassTy(&builder)) { GetAllInstantiatedParentType(*superClassTy, builder, parents, visited); } parents.emplace_back(&cur); } void GetInstMapFromCustomDefAndParent( const CustomTypeDef& def, std::unordered_map& instMap, CHIRBuilder& builder) { if (def.IsExtend()) { for (auto parentInterfaceTy : def.GetImplementedInterfaceTys()) { auto parentInterfaceDef = parentInterfaceTy->GetCustomTypeDef(); auto parentInterfaceDefGenericParams = parentInterfaceDef->GetGenericTypeParams(); auto instParentInterfaceTy = StaticCast(ReplaceRawGenericArgType(*parentInterfaceTy, instMap, builder)); auto parentInterfaceTypeGenericArgs = instParentInterfaceTy->GetGenericArgs(); for (size_t i = 0; i < parentInterfaceDefGenericParams.size(); ++i) { instMap.emplace(parentInterfaceDefGenericParams[i], parentInterfaceTypeGenericArgs[i]); } parentInterfaceTy->GetInstMap(instMap, builder); } } else { StaticCast(def.GetType())->GetInstMap(instMap, builder); } } Type* CreateBoxTypeRef(Type& baseTy, CHIRBuilder& builder) { auto boxTy = builder.GetType(&baseTy); return builder.GetType(boxTy); } Type* GenericTypeConvertor::ConvertToInstantiatedType(Type& type) { return ReplaceRawGenericArgType(type, instMap, builder); } FuncType* ConvertFuncParamsAndRetType(FuncType& input, ConvertTypeFunc& convertor, CHIRBuilder& builder) { std::vector newParamTys; for (auto oldParamTy : input.GetParamTypes()) { newParamTys.emplace_back(convertor(*oldParamTy)); } auto newRetTy = convertor(*input.GetReturnType()); return builder.GetType(newParamTys, newRetTy, input.HasVarArg(), input.IsCFunc()); } void CollectNonExtendParentDefs(ClassDef& cur, std::unordered_set& allParents) { auto [_, res] = allParents.emplace(&cur); if (!res) { return; } for (auto p : cur.GetImplementedInterfaceDefs()) { CollectNonExtendParentDefs(*p, allParents); } if (auto clsDef = DynamicCast(&cur)) { if (clsDef->GetSuperClassDef() != nullptr) { CollectNonExtendParentDefs(*clsDef->GetSuperClassDef(), allParents); } } } bool ParentDefIsFromExtend(const CustomTypeDef& cur, const ClassDef& parent) { std::unordered_set allParents; for (auto p : cur.GetImplementedInterfaceDefs()) { CollectNonExtendParentDefs(*p, allParents); } if (auto clsDef = DynamicCast(&cur)) { if (clsDef->GetSuperClassDef() != nullptr) { CollectNonExtendParentDefs(*clsDef->GetSuperClassDef(), allParents); } } return &cur != &parent && allParents.find(&parent) == allParents.end(); } void VisitFuncBlocksInTopoSort(const BlockGroup& funcBody, std::function preVisit) { auto cmp = [](const Ptr b1, const Ptr b2) { return b1->GetIdentifier() < b2->GetIdentifier(); }; auto blocks = Utils::VecToSortedSet(funcBody.GetBlocks(), cmp); auto sortedBlock = TopologicalSort(funcBody.GetEntryBlock()); for (auto block : sortedBlock) { Visitor::Visit(*block, preVisit); blocks.erase(block); } for (auto block : blocks) { // for orphan block Visitor::Visit(*block, preVisit); } } std::vector GetOutDefDeclaredTypes(const Value& innerDef) { /** normally, we should collect all visible generic types, like: * class A { func foo() {} } ==> we need to collect {T1, T2} in order * but if `innerDef` is in extend def, we need to handle following cases: * 1. extend CPointer { static func foo() {} } * there is no visible generic types for function `foo`, but we need to collect `Bool`, and then * for class $Auto_Env_Impl, we need to create class def like `class $Auto_Env_foo` * in user point, assume cj code is like `var a = CPointer.foo`, we will create `GetInstantiateValue` * with instantiate type `Bool`(something like `GetInstantiateValue(foo, Bool)`) * so generic type size in class def equals to instantiate type size in `GetInstantiateValue` * 2. class B {}; extend B { static func foo() {...} } * if we collect visible generic types for `foo`, it must be {U1, U2} in order, but in user point, * cj code must be like `var a = B.foo`, CHIR expr is `GetInstantiateValue(foo, Bool, Int32)` * so the order is {Bool, Int32}, that means we will use Bool to replace U1, use Int32 to replace U2, * obviously, this is wrong, in fact, we need to use Bool to replace U2, use Int32 to replace U1 * so, if out decl is extend def, we must modify the following `instArgs` */ auto visiableGenericTypes = GetVisiableGenericTypes(innerDef); std::vector instArgs(visiableGenericTypes.begin(), visiableGenericTypes.end()); auto parentFunc = DynamicCast(&innerDef); if (parentFunc == nullptr) { parentFunc = GetTopLevelFunc(innerDef); } CJC_NULLPTR_CHECK(parentFunc); if (auto exDef = DynamicCast(parentFunc->GetParentCustomTypeDef())) { auto exGenericTypes = exDef->GetGenericTypeParams(); auto exTypeArgs = exDef->GetExtendedType()->GetTypeArgs(); for (size_t i = 0; i < exGenericTypes.size(); ++i) { instArgs.erase(instArgs.begin()); } instArgs.insert(instArgs.begin(), exTypeArgs.begin(), exTypeArgs.end()); } return instArgs; } std::pair GetFuncTypeFromAutoEnvBaseDef(const ClassDef& autoEnvBaseDef) { auto abstractMethods = autoEnvBaseDef.GetAbstractMethods(); CJC_ASSERT(abstractMethods.size() == 1); return {abstractMethods[0].methodName, StaticCast(abstractMethods[0].methodTy)}; } std::pair, Type*> GetFuncTypeWithoutThisPtrFromAutoEnvBaseType(const ClassType& autoEnvBaseType) { auto typeArgs = autoEnvBaseType.GetGenericArgs(); if (typeArgs.empty()) { auto [_, funcType] = GetFuncTypeFromAutoEnvBaseDef(*autoEnvBaseType.GetClassDef()); auto paramTypes = funcType->GetParamTypes(); paramTypes.erase(paramTypes.begin()); auto retType = funcType->GetReturnType(); return {paramTypes, retType}; } else { auto paramTypes = std::vector(typeArgs.begin(), typeArgs.end() - 1); auto retType = typeArgs.back(); return {paramTypes, retType}; } } size_t GetMethodIdxInAutoEnvObject(const std::string& methodName) { size_t methodIdx = 0; if (methodName == GENERIC_VIRTUAL_FUNC) { methodIdx = 0; } else if (methodName == INST_VIRTUAL_FUNC) { methodIdx = 1; } else { CJC_ABORT(); } return methodIdx; } Type* GetExpectedInstType(const GenericType& gType, const Type& extendedType, Type& instType) { if (&extendedType == &gType) { return &instType; } if (extendedType.IsGeneric()) { return nullptr; } auto extendedTypeArgs = extendedType.GetTypeArgs(); auto instTypeArgs = instType.GetTypeArgs(); CJC_ASSERT(extendedTypeArgs.size() == instTypeArgs.size()); for (size_t i = 0; i < extendedTypeArgs.size(); ++i) { auto result = GetExpectedInstType(gType, *extendedTypeArgs[i], *instTypeArgs[i]); if (result != nullptr) { return result; } } return nullptr; } static constexpr int OPTION_CTOR_SIZE{2}; static bool IsOptionLike(const AST::EnumDecl& decl) { auto& ctors = decl.constructors; if (ctors.size() != OPTION_CTOR_SIZE) { return false; } auto isOptionSome = [](const AST::Decl& cons) { if (auto func = DynamicCast(&cons)) { auto& params = func->funcBody->paramLists[0]->params; return params.size() == 1; } return false; }; return (Is(ctors[0]) && isOptionSome(*ctors[1])) || (Is(ctors[1]) && isOptionSome(*ctors[0])); } Type::TypeKind GetSelectorType(const AST::EnumDecl& decl) { if (decl.hasEllipsis) { return Type::TypeKind::TYPE_UINT32; } if (IsOptionLike(decl)) { return Type::TypeKind::TYPE_BOOLEAN; } return Type::TypeKind::TYPE_UINT32; } bool IsEnumSelectorType(const Type& type) { return type.GetTypeKind() == Type::TypeKind::TYPE_UINT32 || type.GetTypeKind() == Type::TypeKind::TYPE_BOOLEAN; } static bool IsOptionLike(const EnumDef& def) { auto ctors = def.GetCtors(); if (ctors.size() != OPTION_CTOR_SIZE) { return false; } return (ctors[0].funcType->GetNumOfParams() == 0 && ctors[1].funcType->GetNumOfParams() == 1) || (ctors[1].funcType->GetNumOfParams() == 0 && ctors[0].funcType->GetNumOfParams() == 1); } Type::TypeKind GetSelectorType(const EnumDef& def) { if (!def.IsExhaustive()) { return Type::TypeKind::TYPE_UINT32; } if (IsOptionLike(def)) { return Type::TypeKind::TYPE_BOOLEAN; } return Type::TypeKind::TYPE_UINT32; } std::vector GetExtendedInterfaceDefs(const CustomTypeDef& def) { std::vector defs; for (auto extend : def.GetExtends()) { auto extendInterface = extend->GetImplementedInterfaceDefs(); defs.insert(defs.end(), extendInterface.begin(), extendInterface.end()); } return defs; } bool CheckCustomTypeDefIsExpected( const CustomTypeDef& def, const std::string& packageName, const std::string& defSrcCodeName) { auto pkgName = def.GetPackageName(); if (auto genericDecl = def.GetGenericDecl()) { pkgName = genericDecl->GetPackageName(); } return pkgName == packageName && def.GetSrcCodeIdentifier() == defSrcCodeName; } bool IsCoreAny(const CustomTypeDef& def) { return CheckCustomTypeDefIsExpected(def, CORE_PACKAGE_NAME, ANY_NAME); } bool IsCoreObject(const CustomTypeDef& def) { return CheckCustomTypeDefIsExpected(def, CORE_PACKAGE_NAME, OBJECT_NAME); } bool IsCoreOption(const CustomTypeDef& def) { return CheckCustomTypeDefIsExpected(def, CORE_PACKAGE_NAME, STD_LIB_OPTION); } bool IsCoreFuture(const CustomTypeDef& def) { return CheckCustomTypeDefIsExpected(def, CORE_PACKAGE_NAME, STD_LIB_FUTURE); } bool IsClosureConversionEnvClass(const ClassDef& def) { return def.Get(); } bool IsCapturedClass(const ClassDef& def) { return def.Get(); } Func* GetTopLevelFunc(const Value& value) { if (value.IsParameter()) { return StaticCast(value).GetTopLevelFunc(); } else if (value.IsLocalVar()) { return StaticCast(value).GetTopLevelFunc(); } else if (value.IsBlock()) { return StaticCast(value).GetTopLevelFunc(); } else if (value.IsBlockGroup()) { return StaticCast(value).GetTopLevelFunc(); } CJC_ABORT(); return nullptr; } void GetVisiableGenericTypes(const FuncBase& value, std::vector& result) { auto curGenericTypeParams = value.GetGenericTypeParams(); result.insert(result.begin(), curGenericTypeParams.begin(), curGenericTypeParams.end()); auto parentDef = value.GetParentCustomTypeDef(); if (parentDef != nullptr) { auto outerGenericTypeParams = parentDef->GetGenericTypeParams(); result.insert(result.begin(), outerGenericTypeParams.begin(), outerGenericTypeParams.end()); } } void GetVisiableGenericTypes(const LocalVar& value, std::vector& result) { auto lambda = DynamicCast(value.GetExpr()); // lambda's return value will see generic type declared in lambda if (lambda != nullptr) { auto genericTypeParams = lambda->GetGenericTypeParams(); result.insert(result.begin(), genericTypeParams.begin(), genericTypeParams.end()); } auto curPos = value.GetExpr()->GetParentBlockGroup(); CJC_NULLPTR_CHECK(curPos); while (curPos->GetOwnerFunc() == nullptr) { auto ownedExpr = curPos->GetOwnerExpression(); if (auto lam = DynamicCast(ownedExpr)) { auto genericTypeParams = lam->GetGenericTypeParams(); result.insert(result.begin(), genericTypeParams.begin(), genericTypeParams.end()); curPos = lam->GetParentBlockGroup(); } else { curPos = ownedExpr->GetParentBlockGroup(); } } CJC_NULLPTR_CHECK(curPos); auto ownFunc = curPos->GetOwnerFunc(); CJC_NULLPTR_CHECK(ownFunc); GetVisiableGenericTypes(*ownFunc, result); } void GetVisiableGenericTypes(const Parameter& value, std::vector& result) { if (value.GetTopLevelFunc() != nullptr) { GetVisiableGenericTypes(*value.GetTopLevelFunc(), result); } else { CJC_NULLPTR_CHECK(value.GetOwnerLambda()); GetVisiableGenericTypes(*value.GetOwnerLambda()->GetResult(), result); } } void GetVisiableGenericTypes(const BlockGroup& value, std::vector& result) { if (value.GetOwnerFunc() != nullptr) { GetVisiableGenericTypes(*value.GetOwnerFunc(), result); } else { auto parentLambda = StaticCast(value.GetOwnerExpression()); GetVisiableGenericTypes(*parentLambda->GetResult(), result); } } void GetVisiableGenericTypes(const Block& value, std::vector& result) { GetVisiableGenericTypes(*value.GetParentBlockGroup(), result); } std::vector GetVisiableGenericTypes(const Value& value) { std::vector result; if (value.IsFunc()) { GetVisiableGenericTypes(VirtualCast(value), result); } else if (value.IsBlock()) { GetVisiableGenericTypes(StaticCast(value), result); } else if (value.IsBlockGroup()) { GetVisiableGenericTypes(StaticCast(value), result); } else if (value.IsParameter()) { GetVisiableGenericTypes(StaticCast(value), result); } else if (value.IsLocalVar()) { GetVisiableGenericTypes(StaticCast(value), result); } return result; } const std::vector& GetFuncParams(const BlockGroup& funcBody) { if (auto func = funcBody.GetOwnerFunc()) { return func->GetParams(); } else { auto lambda = StaticCast(funcBody.GetOwnerExpression()); return lambda->GetParams(); } } LocalVar* GetReturnValue(const BlockGroup& funcBody) { if (auto func = funcBody.GetOwnerFunc()) { return func->GetReturnValue(); } else { auto lambda = StaticCast(funcBody.GetOwnerExpression()); return lambda->GetReturnValue(); } } FuncType* GetFuncType(const BlockGroup& funcBody) { if (auto func = funcBody.GetOwnerFunc()) { return func->GetFuncType(); } else { auto lambda = StaticCast(funcBody.GetOwnerExpression()); return lambda->GetFuncType(); } } bool IsStructOrExtendMethod(const Value& value) { auto func = DynamicCast(&value); if (func == nullptr) { return false; } auto declaredParent = func->GetParentCustomTypeDef(); if (declaredParent == nullptr) { return false; } if (declaredParent->GetCustomKind() == CustomDefKind::TYPE_STRUCT) { return true; } else if (auto extendDef = DynamicCast(declaredParent); extendDef) { if (extendDef->GetExtendedType() != nullptr) { return extendDef->GetExtendedType()->IsStruct(); } } return false; } bool IsConstructor(const Value& value) { if (auto func = DynamicCast(&value)) { return func->IsConstructor(); } return false; } Value* GetCastOriginalTarget(const Expression& expr) { if (expr.GetExprKind() == ExprKind::TYPECAST) { return StaticCast(&expr)->GetSourceValue(); } if (expr.GetExprKind() == ExprKind::UNBOX_TO_REF) { // from struct& -> class& return StaticCast(&expr)->GetSourceValue(); } if (expr.GetExprKind() == ExprKind::TRANSFORM_TO_GENERIC) { return StaticCast(&expr)->GetSourceValue(); } if (expr.GetExprKind() == ExprKind::TRANSFORM_TO_CONCRETE) { return StaticCast(&expr)->GetSourceValue(); } if (expr.GetExprKind() == ExprKind::BOX) { return StaticCast(&expr)->GetSourceValue(); } return nullptr; } bool IsInstanceVarInit(const Value& value) { if (auto func = DynamicCast(&value)) { return func->IsInstanceVarInit(); } return false; } std::vector GetSuperTypesRecusively(Type& subType, CHIRBuilder& builder) { std::vector result; if (auto customType = DynamicCast(&subType)) { result = customType->GetSuperTypesRecusively(builder); } else if (auto builtinType = DynamicCast(&subType)) { result = builtinType->GetSuperTypesRecusively(builder); } else if (auto genericType = DynamicCast(&subType)) { for (auto upperBound : genericType->GetUpperBounds()) { auto classType = StaticCast(upperBound->StripAllRefs()); result.emplace_back(classType); auto tempParents = GetSuperTypesRecusively(*classType, builder); for (auto p : tempParents) { if (std::find(result.begin(), result.end(), p) == result.end()) { result.emplace_back(p); } } } } else { CJC_ABORT(); } return result; } static bool MeetAutoEnvBase(const Type& subType, const Type& superType) { return subType.IsAutoEnvInstBase() && superType.IsAutoEnvGenericBase() && Cangjie::StaticCast(subType).GetClassDef()->GetSuperClassTy() == &superType; } bool ParamTypeIsEquivalent(const Type& paramType, const Type& argType) { if (¶mType == &argType) { return true; } if (auto g = DynamicCast(¶mType); g && g->orphanFlag) { auto upperBounds = g->GetUpperBounds(); CJC_ASSERT(upperBounds.size() == 1); return upperBounds[0] == &argType; } if (auto g = DynamicCast(&argType); g && g->orphanFlag) { auto upperBounds = g->GetUpperBounds(); CJC_ASSERT(upperBounds.size() == 1); return upperBounds[0] == ¶mType; } return MeetAutoEnvBase(*argType.StripAllRefs(), *paramType.StripAllRefs()); } ClassType* GetInstParentType(const std::vector& candidate, const FuncBase& callee, const std::vector& args, CHIRBuilder& builder) { CJC_ASSERT(!candidate.empty()); if (candidate.size() == 1) { return candidate[0]; } std::unordered_set tempDef; for (auto p : candidate) { tempDef.emplace(p->GetClassDef()); } CJC_ASSERT(tempDef.size() == 1); size_t offset = callee.TestAttr(Attribute::STATIC) ? 0 : 1; std::vector argTypes; for (size_t i = offset; i < args.size(); ++i) { argTypes.emplace_back(args[i]->GetType()); } auto paramTypes = callee.GetFuncType()->GetParamTypes(); if (!callee.TestAttr(Attribute::STATIC)) { paramTypes.erase(paramTypes.begin()); } CJC_ASSERT(argTypes.size() == paramTypes.size()); for (auto p : candidate) { auto [ret, instMap] = p->GetClassDef()->GetType()->CalculateGenericTyMapping(*p); CJC_ASSERT(ret); bool argTypeMatched = true; for (size_t i = 0; i < paramTypes.size(); ++i) { auto instType = ReplaceRawGenericArgType(*paramTypes[i], instMap, builder); if (!ParamTypeIsEquivalent(*instType, *argTypes[i])) { argTypeMatched = false; break; } } if (argTypeMatched) { return p; } } CJC_ABORT(); return nullptr; } Type* GetInstParentCustomTyOfCallee( const Value& value, const std::vector& args, const Type* thisType, CHIRBuilder& builder) { auto callee = DynamicCast(&value); if (callee == nullptr) { return nullptr; // call a value } if (!callee->IsMemberFunc()) { return nullptr; // global function } auto calleeParentDef = callee->GetParentCustomTypeDef(); CJC_NULLPTR_CHECK(calleeParentDef); if (thisType == nullptr) { return calleeParentDef->GetType(); // call a member method with implicit `this` } if (!calleeParentDef->GetType()->IsBuiltinType() && calleeParentDef->IsExtend()) { calleeParentDef = StaticCast(calleeParentDef->GetType())->GetCustomTypeDef(); } auto derefThisType = thisType->StripAllRefs(); // `thisType` is sub class, `calleeParentDef` is parent class, a CustomType can't inherit BuiltinType CJC_ASSERT(!(derefThisType->IsCustomType() && calleeParentDef->GetType()->IsBuiltinType())); auto typeAndDefIsEquivalent = [](const CustomType& type, const CustomTypeDef& def) { /** * 1. def equals def, maybe `def` is generic, `type` is instantiated * 2. type equals type, maybe `def` is instantiated */ return type.GetCustomTypeDef() == &def || &type == def.GetType(); }; if (auto customType = DynamicCast(derefThisType); customType && typeAndDefIsEquivalent(*customType, *calleeParentDef)) { /** * 1. a function declared in generic custom type def, is called by instantiated custom type * class A { * func foo() {} * } * A.foo() // `thisType` is A, `foo`'s parent def is A * * 2. a function declared in instantiated custom type def, is called by instantiated custom type * class A { * func foo() {} * } * class A_Bool { // A is instantiated by Bool * func foo_Bool() {} * } * A.foo_Bool() // `thisType` is A, `foo`'s parent def is A_Bool */ return derefThisType; } else if (auto builtinType = DynamicCast(derefThisType); builtinType && builtinType->IsSameTypeKind(*calleeParentDef->GetType())) { // we should compare type pointer, but CPointer is generic type, so we have to compare type kind return derefThisType; } else { /** * a function declared in parent def, but called by sub type, then we need to compute instantiated parent type * 1. a function declared in generic custom type def, is called by instantiated sub custom type * interface A { * func foo() {} * } * class B <: A {} * B.foo() // `thisType` is B, `foo`'s parent def is A, then parent type is A * * 2. a function declared in instantiated custom type def, is called by instantiated sub custom type * interface A { * func foo() {} * } * class B <: A {} * interface A_Bool { * func foo_Bool() {} * } * class B_Bool <: A_Bool {} * B.foo_Bool() // `thisType` is B, `foo`'s parent def is A_Bool, then parent type is A * * 3. sub class inherit the same parent class more than once with different instantiated type * interface A { * func foo(a: T) {} * } * class B <: A & A {} * B().foo(1) // `thisType` is B, `foo`'s parent def is A, then parent type is A */ auto parentTypes = GetSuperTypesRecusively(*derefThisType, builder); std::vector matchedTypes; for (auto pType : parentTypes) { if (typeAndDefIsEquivalent(*pType, *calleeParentDef)) { matchedTypes.emplace_back(pType); } } return GetInstParentType(matchedTypes, *callee, args, builder); } } Type* GetInstParentCustomTypeForApplyCallee(const Apply& expr, CHIRBuilder& builder) { return GetInstParentCustomTyOfCallee(*expr.GetCallee(), expr.GetArgs(), expr.GetThisType(), builder); } Type* GetInstParentCustomTypeForAweCallee(const ApplyWithException& expr, CHIRBuilder& builder) { return GetInstParentCustomTyOfCallee(*expr.GetCallee(), expr.GetArgs(), expr.GetThisType(), builder); } std::vector GetFuncIndexInVTable( Type& root, const FuncCallType& funcCallType, bool isStatic, CHIRBuilder& builder) { std::vector result; if (auto genericTy = DynamicCast(&root)) { auto& upperBounds = genericTy->GetUpperBounds(); CJC_ASSERT(!upperBounds.empty()); for (auto upperBound : upperBounds) { ClassType* upperClassType = StaticCast(StaticCast(upperBound)->GetBaseType()); result = GetFuncIndexInVTable(*upperClassType, funcCallType, isStatic, builder); if (!result.empty()) { break; } } } else if (auto classTy = DynamicCast(&root)) { result = classTy->GetFuncIndexInVTable(funcCallType, isStatic, builder); } else { std::unordered_map empty; auto extendDefs = root.GetExtends(&builder); CJC_ASSERT(!extendDefs.empty()); for (auto ex : extendDefs) { result = ex->GetFuncIndexInVTable(funcCallType, isStatic, empty, builder); if (!result.empty()) { break; } } } return result; } BuiltinType* GetBuiltinTypeWithVTable(BuiltinType& type, CHIRBuilder& builder) { if (type.IsCPointer()) { return builder.GetType(builder.GetUnitTy()); } return &type; } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/Value.cpp000066400000000000000000000771551510705540100201220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CHIR/Value.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/ToStringUtils.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/CustomTypeDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/ICEUtil.h" #include "cangjie/Utils/Utils.h" #include using namespace Cangjie::CHIR; /// Value Value::Value(Type* ty, std::string identifier, ValueKind kind) : ty(ty), identifier(std::move(identifier)), kind(kind) { } Value::ValueKind Value::GetValueKind() const { return kind; } bool Value::IsLocalVar() const { return kind == ValueKind::KIND_LOCALVAR; } bool Value::IsFunc() const { return kind == ValueKind::KIND_FUNC || kind == ValueKind::KIND_IMP_FUNC; } bool Value::IsBlock() const { return kind == ValueKind::KIND_BLOCK; } bool Value::IsBlockGroup() const { return kind == ValueKind::KIND_BLOCK_GROUP; } bool Value::IsParameter() const { return kind == ValueKind::KIND_PARAMETER; } bool Value::IsLiteral() const { return kind == ValueKind::KIND_LITERAL; } bool Value::IsGlobalVar() const { return kind == ValueKind::KIND_GLOBALVAR || kind == ValueKind::KIND_IMP_VAR; } bool Value::IsImportedFunc() const { return kind == ValueKind::KIND_IMP_FUNC; } bool Value::IsImportedVar() const { return kind == ValueKind::KIND_IMP_VAR; } bool Value::IsImportedSymbol() const { return kind == ValueKind::KIND_IMP_FUNC || kind == ValueKind::KIND_IMP_VAR; } bool Value::IsGlobalVarInCurPackage() const { return kind == ValueKind::KIND_GLOBALVAR; } bool Value::IsFuncWithBody() const { return kind == ValueKind::KIND_FUNC; } Type* Value::GetType() const { return ty; } /** * @brief obtains the identifier of the corresponding Cangjie source code. */ std::string Value::GetSrcCodeIdentifier() const { return ""; } const std::string& Value::GetIdentifier() const { return identifier; } std::string Value::GetIdentifierWithoutPrefix() const { if (identifier != "") { return identifier.substr(1); } return identifier; } std::vector Value::GetUsers() const { return users; } bool Value::IsCompileTimeValue() const { if (kind == ValueKind::KIND_LITERAL) { return true; } if (kind == ValueKind::KIND_LOCALVAR) { if (static_cast(this)->GetExpr()->IsConstant()) { return true; } } return TestAttr(Attribute::CONST); } bool Value::IsGlobal() const { return kind == ValueKind::KIND_GLOBALVAR || kind == ValueKind::KIND_FUNC || kind == ValueKind::KIND_IMP_VAR || kind == ValueKind::KIND_IMP_FUNC; } void Value::AddUserOnly(Expression* expr) { CJC_NULLPTR_CHECK(expr); if (IsGlobal()) { std::unique_lock lock(userMutex); users.push_back(expr); } else { users.push_back(expr); } } void Value::RemoveUserOnly(Expression* expr) { if (IsGlobal()) { std::unique_lock lock(userMutex); users.erase(std::remove(users.begin(), users.end(), expr), users.end()); } else { users.erase(std::remove(users.begin(), users.end(), expr), users.end()); } } void Value::Dump() const { std::cout << ToString() << std::endl; } void Value::ReplaceWith(Value& newValue, const BlockGroup* scope) { std::vector oldUsers{users}; for (auto user : oldUsers) { if (user->GetParentBlock() == nullptr) { continue; } if (scope == nullptr || IsNestedBlockOf(user->GetParentBlock()->GetParentBlockGroup(), scope)) { user->ReplaceOperand(this, &newValue); } } } AttributeInfo Value::GetAttributeInfo() const { return attributes; } bool Value::TestAttr(Attribute attr) const { return attributes.TestAttr(attr); } void Value::AppendAttributeInfo(const AttributeInfo& info) { attributes.AppendAttrs(info); } void Value::EnableAttr(Attribute attr) { attributes.SetAttr(attr, true); } void Value::DisableAttr(Attribute attr) { attributes.SetAttr(attr, false); } const AnnoInfo& Value::GetAnnoInfo() const { return annoInfo; } void Value::SetAnnoInfo(AnnoInfo&& info) { annoInfo = std::move(info); } void Value::ClearUsersOnly() { users.clear(); } Parameter::Parameter(Type* ty, const std::string& indexStr, Func* ownerFunc) : Value(ty, indexStr, ValueKind::KIND_PARAMETER), ownerFunc(ownerFunc) { if (ownerFunc) { ownerFunc->AddParam(*this); } } Parameter::Parameter(Type* ty, const std::string& indexStr, Lambda& ownerLambda) : Value(ty, indexStr, ValueKind::KIND_PARAMETER), ownerLambda(&ownerLambda) { ownerLambda.AddParam(*this); } std::string Parameter::GetSrcCodeIdentifier() const { std::string ident; auto debugExpr = GetDebugExpr(); if (debugExpr != nullptr) { ident = debugExpr->GetSrcCodeIdentifier(); } return ident; } Func* Parameter::GetOwnerFunc() const { return ownerFunc; } Func* Parameter::GetTopLevelFunc() const { if (ownerFunc != nullptr) { return ownerFunc; } CJC_NULLPTR_CHECK(ownerLambda); return ownerLambda->GetTopLevelFunc(); } Lambda* Parameter::GetOwnerLambda() const { return ownerLambda; } void Parameter::SetOwnerLambda(Lambda* newParent) { ownerLambda = newParent; ownerFunc = nullptr; } void Parameter::SetOwnerFunc(Func* owner) { ownerFunc = owner; ownerLambda = nullptr; } Debug* Parameter::GetDebugExpr() const { for (auto it = users.crbegin(); it != users.crend(); ++it) { if ((*it)->GetExprKind() == ExprKind::DEBUGEXPR) { return StaticCast(*it); } } return nullptr; } std::string Parameter::ToString() const { std::stringstream ss; ss << identifier; ss << ": " << ty->ToString(); return ss.str(); } /// LocalVar LocalVar::LocalVar(Type* ty, std::string indexStr, Expression* expr) : Value(ty, std::move(indexStr), ValueKind::KIND_LOCALVAR), expr(expr) { if (expr) { expr->result = this; } } std::string LocalVar::GetSrcCodeIdentifier() const { std::string ident; auto debugExpr = GetDebugExpr(); if (debugExpr != nullptr) { ident = debugExpr->GetSrcCodeIdentifier(); } return ident; } BlockGroup* LocalVar::GetOwnerBlockGroup() const { CJC_NULLPTR_CHECK(expr); return expr->GetParentBlockGroup(); } Expression* LocalVar::GetExpr() const { return expr; } bool LocalVar::IsRetValue() const { return isRetValue; } Func* LocalVar::GetTopLevelFunc() const { CJC_NULLPTR_CHECK(expr); return expr->GetTopLevelFunc(); } const DebugLocation& LocalVar::GetDebugLocation() const { return expr->GetDebugLocation(); } Debug* LocalVar::GetDebugExpr() const { for (auto it = users.crbegin(); it != users.crend(); ++it) { if ((*it)->GetExprKind() == ExprKind::DEBUGEXPR) { return StaticCast(*it); } } return nullptr; } void LocalVar::SetRetValue() { isRetValue = true; } std::string LocalVar::ToString() const { std::stringstream ss; ss << identifier; ss << ": " << ty->ToString(); if (expr) { ss << " = " << expr->ToString(); } return ss.str(); } // GlobalVar GlobalVar::GlobalVar(Type* ty, std::string identifier, std::string srcCodeIdentifier, std::string rawMangledName, std::string packageName) : Value{ty, std::move(identifier), ValueKind::KIND_GLOBALVAR}, GlobalVarBase{std::move(srcCodeIdentifier), std::move(rawMangledName), std::move(packageName)} { } bool GlobalVar::IsLocalConst() const { // lifted local const var is marked COMPILER_ADD // this is more like a hack. return TestAttr(Attribute::CONST) && TestAttr(Attribute::COMPILER_ADD); } std::string GlobalVar::ToString() const { std::stringstream ss; ss << attributes.ToString() << identifier << ": " << ty->ToString(); if (auto initVal = GetInitializer()) { ss << " = " << initVal->ToString(); } std::stringstream comment; comment << ToStringAnnotationMap(); if (annoInfo.IsAvailable()) { AddCommaOrNot(comment); comment << "annoInfo: " + annoInfo.mangledName; } if (!srcCodeIdentifier.empty()) { AddCommaOrNot(comment); comment << "srcCodeIdentifier: " + srcCodeIdentifier; } if (!rawMangledName.empty()) { AddCommaOrNot(comment); comment << "rawMangledName: " << rawMangledName; } if (comment.str() != "") { ss << " // " << comment.str(); } return ss.str(); } GlobalVarBase::GlobalVarBase(std::string srcCodeIdentifier, std::string rawMangledName, std::string packageName) : packageName(std::move(packageName)), srcCodeIdentifier(std::move(srcCodeIdentifier)), rawMangledName(std::move(rawMangledName)) { } Func* GlobalVar::GetInitFunc() const { return initFunc; } std::string GlobalVarBase::GetSrcCodeIdentifier() const { return srcCodeIdentifier; } /** * @brief only static member var has declaredParent, others return nullptr */ CustomTypeDef* GlobalVarBase::GetParentCustomTypeDef() const { return declaredParent; } const std::string& GlobalVarBase::GetPackageName() const { return packageName; } void GlobalVar::SetInitializer(LiteralValue& literalValue) { this->initializer = &literalValue; this->initFunc = nullptr; } void GlobalVarBase::DestroySelf() { if (declaredParent) { Utils::RemoveFromVec(declaredParent->staticVars, this); } } LiteralValue* GlobalVar::GetInitializer() const { return this->initializer; } void GlobalVar::SetInitFunc(Func& func) { this->initFunc = &func; initializer = nullptr; // platform var need to clear } const std::string& GlobalVarBase::GetRawMangledName() const { return rawMangledName; } ImportedValue::ImportedValue() { } std::string ImportedValue::ToString() const { std::stringstream ss; ss << identifier; ss << ": " << ty->ToString(); return ss.str(); } void ImportedValue::DestroySelf() { if (auto var = DynamicCast(this)) { StaticCast(var)->DestroySelf(); } else { StaticCast(StaticCast(this))->DestroySelf(); } } std::string ImportedFunc::ToString() const { std::stringstream ss; ss << identifier; ss << GetGenericTypeParamsStr(genericTypeParams); ss << ": " << ty->ToString(); return ss.str(); } Block::Block(std::string identifier, BlockGroup* parentGroup) : Value(nullptr, std::move(identifier), ValueKind::KIND_BLOCK), parentGroup(parentGroup) { if (parentGroup) { parentGroup->AddBlock(this); } } void Block::AppendPredecessorOnly(Block& block) { predecessors.emplace_back(&block); } void Block::AddPredecessor(Block* block) { if (Utils::NotIn(block, predecessors)) { AppendPredecessorOnly(*block); AddUserOnly(block->GetTerminator()); } } void Block::RemoveExprOnly(Expression& expr) { exprs.erase(std::remove(exprs.begin(), exprs.end(), &expr), exprs.end()); } void Block::AppendExprOnly(Expression& expr) { exprs.emplace_back(&expr); } void Block::AppendNonTerminatorExpression(Expression* expression) { CJC_ASSERT(!expression->IsTerminator()); expression->GetParentBlock()->RemoveExprOnly(*expression); expression->SetParent(this); AppendExprOnly(*expression); } void Block::AppendExpressions(const std::vector& expressions) { for (auto expr : expressions) { AppendExpression(expr); } } void Block::AppendExpression(Expression* expression) { if (expression->IsTerminator()) { AppendTerminator(StaticCast(expression)); } else { AppendNonTerminatorExpression(expression); } } std::vector Block::GetExpressions() const { return exprs; } Expression* Block::GetExpressionByIdx(size_t idx) const { CJC_ASSERT(idx < exprs.size()); return exprs[idx]; } std::vector Block::GetNonTerminatorExpressions() const { if (exprs.empty()) { return {}; } else { return std::vector{exprs.begin(), exprs.end() - 1}; } } void Block::SetParentBlockGroup(BlockGroup* parent) { this->parentGroup = parent; } BlockGroup* Block::GetParentBlockGroup() const { return parentGroup; } void Block::AppendTerminator(Terminator* term) { AppendExprOnly(*term); term->SetParent(this); // update precedessors for (auto suc : term->GetSuccessors()) { suc->AddPredecessor(this); } } Func* Block::GetTopLevelFunc() const { auto blockGroup = GetParentBlockGroup(); CJC_NULLPTR_CHECK(blockGroup); return blockGroup->GetTopLevelFunc(); } Terminator* Block::GetTerminator() const { if (exprs.size() == 0) { return nullptr; } return DynamicCast(exprs.back()); } std::vector Block::GetSuccessors() const { if (auto term = GetTerminator(); term != nullptr) { return term->GetSuccessors(); } return {}; } std::vector Block::GetPredecessors() const { return predecessors; } void Block::ClearExprsOnly() { exprs.clear(); } void Block::ClearPredecessorsOnly() { predecessors.clear(); } void Block::RemoveSelfFromBlockGroup() { CJC_NULLPTR_CHECK(parentGroup); parentGroup->RemoveBlock(*this); if (parentGroup->entryBlock == this) { parentGroup->SetEntryBlock(nullptr); } SetParentBlockGroup(nullptr); ClearPredecessorsOnly(); ClearUsersOnly(); for (auto expr : GetExpressions()) { expr->RemoveSelfFromBlock(); } ClearExprsOnly(); } bool Block::IsEntry() const { if (auto parent = GetParentBlockGroup(); parent != nullptr) { return parent->GetEntryBlock() == this; } return false; } void Block::InsertExprIntoHead(Expression& expr) { CJC_ASSERT(!expr.IsTerminator()); // 1. remove expr from expr's parent block if (expr.parent != nullptr) { expr.GetParentBlock()->RemoveExprOnly(expr); } // 2. insert expr to head of current block exprs.insert(exprs.begin(), &expr); // 3. change expr's parent to current block expr.SetParent(this); } void Block::SetExceptions(const std::vector& ep) { exceptions = ep; } bool Block::IsLandingPadBlock() const { return exceptions.has_value(); } std::vector Block::GetExceptions() const { CJC_ASSERT(exceptions.has_value()); return exceptions.value(); } std::string Block::ToString() const { return GetBlockStr(*this); } void Block::RemovePredecessorOnly(Block& block) { predecessors.erase(std::remove(predecessors.begin(), predecessors.end(), &block), predecessors.end()); } void Block::RemovePredecessor(Block& block) { RemovePredecessorOnly(block); RemoveUserOnly(block.GetTerminator()); } void Block::MoveTo(BlockGroup& newBlockGroup) { if (parentGroup != nullptr) { parentGroup->RemoveBlock(*this); if (parentGroup->entryBlock == this) { parentGroup->SetEntryBlock(nullptr); } } newBlockGroup.AddBlock(this); } Block* Block::Clone(CHIRBuilder& builder, BlockGroup& newGroup) const { auto newBlock = builder.CreateBlock(&newGroup); newBlock->AppendAttributeInfo(GetAttributeInfo()); if (exceptions.has_value()) { newBlock->SetExceptions(exceptions.value()); } for (auto expr : exprs) { auto newExpr = expr->Clone(builder, *newBlock); newExpr->CopyAnnotationMapFrom(*expr); } return newBlock; } size_t Block::GetExpressionsNum() const { size_t res = 0; for (auto expr : exprs) { if (expr->GetExprKind() == ExprKind::LAMBDA) { res += StaticCast(expr)->GetBody()->GetExpressionsNum(); } } res += exprs.size(); return res; } BlockGroup::BlockGroup(std::string identifier) : Value(nullptr, std::move(identifier), ValueKind::KIND_BLOCK_GROUP) { } void BlockGroup::RemoveBlock(Block& block) { blocks.erase(std::remove(blocks.begin(), blocks.end(), &block), blocks.end()); } Func* BlockGroup::GetTopLevelFunc() const { if (ownerFunc != nullptr) { return ownerFunc; } CJC_ASSERT(users.size() == 1); return users[0]->GetTopLevelFunc(); } void BlockGroup::SetOwnerFunc(Func* func) { if (ownerFunc) { ownerFunc->RemoveBody(); } if (auto lambda = DynamicCast(ownerExpression)) { lambda->RemoveBody(); } if (ownerExpression) { RemoveUserOnly(ownerExpression); ownerExpression = nullptr; } ownerFunc = func; } std::vector BlockGroup::GetBlocks() const { return blocks; } Block* BlockGroup::GetBlockByIdx(size_t idx) const { CJC_ASSERT(idx < blocks.size()); return blocks[idx]; } Block* BlockGroup::GetEntryBlock() const { return entryBlock; } void BlockGroup::SetEntryBlock(Block* block) { entryBlock = block; } void BlockGroup::SetOwnerExpression(Expression& expr) { #ifndef NDEBUG // we can't move func or lambdas' body to other expression, vice versa if (expr.IsLambda()) { CJC_ASSERT(ownerExpression == nullptr || ownerExpression->IsLambda()); } else { CJC_ASSERT(ownerFunc == nullptr && (ownerExpression == nullptr || !ownerExpression->IsLambda())); } #endif if (auto lambda = DynamicCast(&expr)) { if (ownerFunc) { ownerFunc->RemoveBody(); ownerFunc = nullptr; } else if (auto lambdaExpr = DynamicCast(ownerExpression)) { lambdaExpr->RemoveBody(); } } if (ownerExpression) { RemoveUserOnly(ownerExpression); } ownerExpression = &expr; AddUserOnly(&expr); } Func* BlockGroup::GetOwnerFunc() const { return ownerFunc; } Expression* BlockGroup::GetOwnerExpression() const { return ownerExpression; } void BlockGroup::ClearBlocksOnly() { blocks.clear(); } void BlockGroup::SetOwnedFuncOnly(Func* newFunc) { ownerFunc = newFunc; } void BlockGroup::ClearBlockGroup() { for (auto block : GetBlocks()) { block->RemoveSelfFromBlockGroup(); } ClearBlocksOnly(); SetEntryBlock(nullptr); SetOwnedFuncOnly(nullptr); RemoveUserOnly(ownerExpression); ownerExpression = nullptr; } void BlockGroup::AddBlock(Block* block) { block->SetParentBlockGroup(this); blocks.emplace_back(block); } void BlockGroup::AddBlocks(const std::vector& newBlocks) { for (auto block : newBlocks) { AddBlock(block); } } std::string BlockGroup::ToString() const { return GetBlockGroupStr(*this); } void BlockGroup::CloneBlocks(CHIRBuilder& builder, BlockGroup& parent) const { CJC_ASSERT(parent.GetBlocks().empty()); CJC_ASSERT(parent.GetEntryBlock() == nullptr); std::unordered_map blockMap; for (auto block : blocks) { Block* newBlock = block->Clone(builder, parent); if (block == entryBlock) { parent.SetEntryBlock(newBlock); } blockMap.emplace(block, newBlock); } for (auto block : parent.GetBlocks()) { auto successors = block->GetSuccessors(); for (size_t i = 0; i < successors.size(); ++i) { auto it = blockMap.find(successors[i]); if (it != blockMap.end()) { auto expr = block->GetTerminator(); CJC_NULLPTR_CHECK(expr); expr->ReplaceSuccessor(i, *it->second); } } } } BlockGroup* BlockGroup::Clone(CHIRBuilder& builder, Func& newFunc) const { auto newGroup = builder.CreateBlockGroup(newFunc); newGroup->SetOwnerFunc(&newFunc); newGroup->AppendAttributeInfo(GetAttributeInfo()); CloneBlocks(builder, *newGroup); return newGroup; } BlockGroup* BlockGroup::Clone(CHIRBuilder& builder, Lambda& newLambda) const { auto parentFunc = newLambda.GetTopLevelFunc(); CJC_NULLPTR_CHECK(parentFunc); auto newGroup = builder.CreateBlockGroup(*parentFunc); if (newLambda.GetBody() == nullptr) { newLambda.InitBody(*newGroup); } newGroup->AppendAttributeInfo(GetAttributeInfo()); CloneBlocks(builder, *newGroup); return newGroup; } size_t BlockGroup::GetExpressionsNum() const { size_t res = 0; for (auto block : blocks) { res += block->GetExpressionsNum(); } return res; } FuncBody::FuncBody() { } BlockGroup* FuncBody::GetBody() const { return body; } Parameter* FuncBody::GetParam(size_t index) const { return parameters[index]; } const std::vector& FuncBody::GetParams() const { return parameters; } void FuncBody::SetReturnValue(LocalVar& ret) { retValue = &ret; } /** * @brief get a `LocalVar` represent the returned value of this FuncBody. */ LocalVar* FuncBody::GetReturnValue() const { return retValue; } void FuncBody::RemoveBody() { body = nullptr; } void FuncBody::RemoveParams() { parameters.clear(); } void FuncBody::AddParam(Parameter& param) { parameters.emplace_back(¶m); } FuncBase::FuncBase(const std::string& srcCodeIdentifier, const std::string& rawMangledName, const std::string& packageName, const std::vector& genericTypeParams) : srcCodeIdentifier(srcCodeIdentifier), rawMangledName(rawMangledName), packageName(packageName), genericTypeParams(genericTypeParams) { } FuncKind FuncBase::GetFuncKind() const { return funcKind; } void FuncBase::SetFuncKind(FuncKind kind) { funcKind = kind; } const std::string& FuncBase::GetPackageName() const { return packageName; } FuncType* FuncBase::GetFuncType() const { return StaticCast(GetType()); } size_t FuncBase::GetNumOfParams() const { return GetFuncType()->GetNumOfParams(); } Type* FuncBase::GetReturnType() const { return GetFuncType()->GetReturnType(); } void FuncBase::DestroySelf() { if (declaredParent) { Utils::RemoveFromVec(declaredParent->methods, this); declaredParent = nullptr; } } Type* FuncBase::GetParentCustomTypeOrExtendedType() const { if (declaredParent == nullptr) { return nullptr; } return declaredParent->GetType(); } CustomTypeDef* FuncBase::GetOuterDeclaredOrExtendedDef() const { if (declaredParent == nullptr) { return nullptr; } if (auto extendDef = DynamicCast(declaredParent); extendDef) { auto extendedType = extendDef->GetExtendedType(); if (extendedType == nullptr) { return nullptr; } if (auto customTy = DynamicCast(extendedType); customTy) { return customTy->GetCustomTypeDef(); } return nullptr; } return declaredParent; } bool FuncBase::IsMemberFunc() const { return declaredParent != nullptr; } CustomTypeDef* FuncBase::GetParentCustomTypeDef() const { return declaredParent; } std::string FuncBase::GetSrcCodeIdentifier() const { return srcCodeIdentifier; } const std::string& FuncBase::GetRawMangledName() const { return rawMangledName; } void FuncBase::SetRawMangledName(const std::string& name) { rawMangledName = name; } bool FuncBase::IsConstructor() const { return (funcKind == FuncKind::CLASS_CONSTRUCTOR || funcKind == FuncKind::STRUCT_CONSTRUCTOR || funcKind == FuncKind::PRIMAL_CLASS_CONSTRUCTOR || funcKind == FuncKind::PRIMAL_STRUCT_CONSTRUCTOR) && !TestAttr(Attribute::STATIC); } bool FuncBase::IsFinalizer() const { return funcKind == FuncKind::FINALIZER; } bool FuncBase::IsLambda() const { return funcKind == FuncKind::LAMBDA; } bool FuncBase::IsGVInit() const { return funcKind == FuncKind::GLOBALVAR_INIT; } bool FuncBase::IsStaticInit() const { return (funcKind == FuncKind::CLASS_CONSTRUCTOR || funcKind == FuncKind::STRUCT_CONSTRUCTOR) && srcCodeIdentifier == "static.init"; } bool FuncBase::IsPrimalConstructor() const { return funcKind == FuncKind::PRIMAL_CLASS_CONSTRUCTOR || funcKind == FuncKind::PRIMAL_STRUCT_CONSTRUCTOR; } bool FuncBase::IsInstanceVarInit() const { return funcKind == FuncKind::INSTANCEVAR_INIT; } bool FuncBase::IsCFunc() const { return ty && ty->IsCFunc(); } bool FuncBase::IsVirtualFunc() const { return TestAttr(Attribute::VIRTUAL) || TestAttr(Attribute::FINAL); } FuncBase* FuncBase::GetGenericDecl() const { return genericDecl; } void FuncBase::SetGenericDecl(FuncBase& decl) { genericDecl = &decl; } bool FuncBase::IsFastNative() const { return isFastNative; } void FuncBase::SetFastNative(bool fastNative) { isFastNative = fastNative; } void FuncBase::SetCFFIWrapper(bool isWrapper) { isCFFIWrapper = isWrapper; } bool FuncBase::IsCFFIWrapper() const { return isCFFIWrapper; } const std::vector& FuncBase::GetGenericTypeParams() const { return genericTypeParams; } void FuncBase::SetParamDftValHostFunc(FuncBase& hostFunc) { paramDftValHostFunc = &hostFunc; } FuncBase* FuncBase::GetParamDftValHostFunc() const { return paramDftValHostFunc; } // Func bool FuncBase::IsClassMethod() const { if (auto outerDef = GetOuterDeclaredOrExtendedDef()) { return outerDef->IsClass(); } return false; } bool FuncBase::IsStructMethod() const { if (auto outerDef = GetOuterDeclaredOrExtendedDef()) { return outerDef->IsStruct(); } return false; } bool FuncBase::IsEnumMethod() const { if (auto outerDef = GetOuterDeclaredOrExtendedDef()) { return outerDef->IsEnum(); } return false; } bool FuncBase::IsInExtend() const { if (declaredParent == nullptr) { return false; } return declaredParent->GetCustomKind() == CustomDefKind::TYPE_EXTEND; } bool FuncBase::IsInGenericContext() const { if (TestAttr(Attribute::GENERIC)) { return true; } return declaredParent != nullptr && declaredParent->TestAttr(Attribute::GENERIC); } void FuncBase::SetOriginalLambdaInfo(const FuncSigInfo& info) { CJC_ASSERT(funcKind == FuncKind::LAMBDA); originalLambdaInfo = info; } FuncType* FuncBase::GetOriginalLambdaType() const { return funcKind == LAMBDA ? originalLambdaInfo.funcType : GetFuncType(); } std::vector FuncBase::GetOriginalGenericTypeParams() const { return funcKind == LAMBDA ? originalLambdaInfo.genericTypeParams : GetGenericTypeParams(); } Func::Func(Type* ty, const std::string& identifier, const std::string& srcCodeIdentifier, const std::string& rawMangledName, const std::string& packageName, const std::vector& genericTypeParams) : Value(ty, identifier, ValueKind::KIND_FUNC), FuncBase{srcCodeIdentifier, rawMangledName, packageName, genericTypeParams} { } uint64_t Func::GenerateLocalId() { return localId++; } void Func::SetLocalId(uint64_t id) { localId = id; } void Func::SetBlockId(uint64_t id) { blockId = id; } void Func::SetBlockGroupId(uint64_t id) { blockGroupId = id; } void Func::DestroyFuncBody() { for (auto b : body.GetBody()->GetBlocks()) { for (auto e : b->GetExpressions()) { e->RemoveSelfFromBlock(); } } RemoveBody(); } BlockGroup* Func::GetBody() const { return body.GetBody(); } void Func::RemoveBody() { body.RemoveBody(); } void Func::RemoveParams() { body.RemoveParams(); } void Func::InitBody(BlockGroup& newBody) { CJC_ASSERT(body.body == nullptr); body.body = &newBody; if (newBody.GetOwnerFunc() != this) { newBody.SetOwnerFunc(this); } } void Func::ReplaceBody(BlockGroup& newBody) { DestroyFuncBody(); body.parameters.clear(); InitBody(newBody); } void Func::InheritIDFromFunc(const Func& func) { blockId = func.blockId; localId = func.localId; blockGroupId = func.blockGroupId; } void Func::AddParam(Parameter& param) { body.AddParam(param); param.SetOwnerFunc(this); } Parameter* Func::GetParam(size_t index) const { return body.GetParam(index); } const std::vector& Func::GetParams() const { return body.GetParams(); } bool Func::HasReturnValue() const { return body.GetReturnValue() != nullptr; } void Func::SetReturnValue(LocalVar& ret) { ret.SetRetValue(); body.SetReturnValue(ret); } LocalVar* Func::GetReturnValue() const { return body.GetReturnValue(); } void Func::SetParentRawMangledName(const std::string& name) { parentName = name; } const std::string& Func::GetParentRawMangledName() const { return parentName; } uint64_t Func::GenerateBlockId() { return blockId++; } uint64_t Func::GenerateBlockGroupId() { return blockGroupId++; } Block* Func::GetEntryBlock() const { return GetBody()->GetEntryBlock(); } std::string Func::ToString() const { return GetFuncStr(*this); } const DebugLocation& Func::GetPropLocation() const { return propLoc; } void Func::SetPropLocation(const DebugLocation& loc) { propLoc = loc; } size_t Func::GetExpressionsNum() const { if (!GetBody()) { return 0; } return GetBody()->GetExpressionsNum(); } void Func::DestroySelf() { DestroyFuncBody(); FuncBase::DestroySelf(); } ImportedFunc::ImportedFunc( Type* ty, const std::string& identifier, const std::string& srcCodeIdentifier, const std::string& rawMangledName, const std::string& packageName, const std::vector& genericTypeParams) : Value{ty, identifier, KIND_IMP_FUNC}, ImportedValue{}, FuncBase{srcCodeIdentifier, rawMangledName, packageName, genericTypeParams} { } const std::vector& ImportedFunc::GetParamInfo() const { return paramInfo; } void ImportedFunc::SetParamInfo(std::vector&& params) { paramInfo = std::move(params); } const std::string& ImportedFunc::GetSourcePackageName() const { return packageName; } ImportedVar::ImportedVar(Type* ty, std::string identifier, std::string srcCodeIdentifier, std::string rawMangledName, std::string packageName) : Value{ty, std::move(identifier), ValueKind::KIND_IMP_VAR}, ImportedValue{}, GlobalVarBase{std::move(srcCodeIdentifier), std::move(rawMangledName), std::move(packageName)} { } const std::string& ImportedVar::GetSourcePackageName() const { return packageName; } namespace Cangjie::CHIR { std::string AbstractMethodParam::ToString() { return paramName + ": " + type->ToString(); } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/CHIR/Visitor/000077500000000000000000000000001510705540100177625ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CHIR/Visitor/CMakeLists.txt000066400000000000000000000005341510705540100225240ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB VISITOR_SRC *.cpp) set(CHIR_BASE_SRC ${CHIR_BASE_SRC} ${VISITOR_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/CHIR/Visitor/SimpleIterator.cpp000066400000000000000000000017621510705540100234370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the SimpleIterator Class in CHIR. */ #include "cangjie/CHIR/Visitor/SimpleIterator.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Value.h" #include "cangjie/CHIR/CHIRCasting.h" using namespace Cangjie::CHIR; std::vector SimpleIterator::Iterate(const Expression& expr) { auto kind{expr.GetExprKind()}; if (kind == ExprKind::IF || kind == ExprKind::LOOP || Is(expr)) { return expr.GetBlockGroups(); } return {}; } std::vector SimpleIterator::Iterate(const BlockGroup& blockGroup) { return blockGroup.GetBlocks(); } std::vector SimpleIterator::Iterate(const Block& block) { return block.GetExpressions(); }cangjie_compiler-1.0.7/src/CMakeLists.txt000066400000000000000000000436571510705540100203550ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # This if branch is used by --product=libs, which just builds std lib. # Standard library cangjie-std-ast need these targets. if(CANGJIE_ENABLE_COMPILER_TSAN) add_compile_options("${TSAN_FLAGS}") add_link_options("${TSAN_FLAGS}") endif() if(IOS) get_directory_property(SRC_COMPILE_OPTIONS COMPILE_OPTIONS) list(REMOVE_ITEM SRC_COMPILE_OPTIONS "--target=${TRIPLE}") set_directory_properties(PROPERTIES COMPILE_OPTIONS "${SRC_COMPILE_OPTIONS}") add_compile_options(--target=${CXX_COMPATIABLE_TRIPLE}) endif() set(CJC_EXTRA_WARNINGS -Woverloaded-virtual -Wvla -Wconversion -Wfloat-equal -Wunused -Wdelete-non-virtual-dtor -Wswitch-default -Wcast-qual -Wextra -Wformat=2 -Wno-missing-field-initializers -Wno-format-nonliteral -Wno-sign-conversion) # -Wno-sign-conversion here to be removed by next MR if(${CMAKE_SYSTEM_NAME} EQUAL Darwin) # mac uses llvm headers for std, and llvm headers have the following warning(s) set(CJC_EXTRA_WARNINGS ${CJC_EXTRA_WARNINGS} -Wno-float-equal) # mac headers have warning on undefed macro(s) (FFI_GO_CLOSURES), so use -Wno-undef set(CJC_EXTRA_WARNINGS ${CJC_EXTRA_WARNINGS} -Wno-undef) endif() # g++ on some platform has harsher check on -Werror=shadow, in that it does not allow function parameters to share the same name with class members, which is actually bad in constructor. So we disable this on some platform. set(CJC_EXTRA_WARNINGS ${CJC_EXTRA_WARNINGS} -Wshadow) # when including llvm header files, the following warnings have to be excluded set(CJC_WITH_LLVM_EXTRA_WARNINGS ${CJC_EXTRA_WARNINGS} -Wno-sign-conversion -Wno-unused-parameter -Wno-float-conversion -Wno-shadow) # gcc on some platform does not recognise these options set(CJC_WITH_LLVM_EXTRA_WARNINGS ${CJC_WITH_LLVM_EXTRA_WARNINGS} -Wno-implicit-int-conversion -Wno-shorten-64-to-32 -Wno-implicit-int-float-conversion) if(NOT CANGJIE_BUILD_CJC AND CANGJIE_BUILD_STD_SUPPORT) add_subdirectory(AST) add_subdirectory(Driver) add_subdirectory(Parse) add_subdirectory(Macro) add_subdirectory(ConditionalCompilation) add_subdirectory(Lex) add_subdirectory(Utils) add_subdirectory(Basic) add_subdirectory(Option) if (CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_subdirectory(MetaTransformation) endif() set(AST_FFI_OBJECTS $ $ $ $ $ $ $ $ $) add_library(cangjie-ast-support STATIC ${AST_FFI_OBJECTS}) string(TOLOWER ${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND} output_lib_dir) install(TARGETS cangjie-ast-support DESTINATION lib/${output_lib_dir}${SANITIZER_SUBPATH}) return() endif() set(CANGJIE_SRC_COMMON_OBJECTS_LIST $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $) if (CANGJIE_CODEGEN_CJNATIVE_BACKEND) set(CANGJIE_SRC_COMMON_OBJECTS_LIST ${CANGJIE_SRC_COMMON_OBJECTS_LIST} $) endif() if (CANGJIE_CODEGEN_CJNATIVE_BACKEND) list(APPEND CANGJIE_SRC_COMMON_OBJECTS_LIST $) list(APPEND CANGJIE_SRC_COMMON_OBJECTS_LIST $) list(APPEND CANGJIE_SRC_COMMON_OBJECTS_LIST $) list(APPEND CANGJIE_SRC_COMMON_OBJECTS_LIST $) else() list(APPEND CANGJIE_SRC_COMMON_OBJECTS_LIST $) list(APPEND CANGJIE_SRC_COMMON_OBJECTS_LIST $) list(APPEND CANGJIE_SRC_COMMON_OBJECTS_LIST $) endif() # By using `CangjieOption-Fully-Enabled`, we could still have a fully featured compiler for # compiling standard library when building for a visible options only version. if(CANGJIE_VISIBLE_OPTIONS_ONLY) set(CANGJIE_SRC_OBJECTS ${CANGJIE_SRC_COMMON_OBJECTS_LIST} $ CACHE INTERNAL "") else() set(CANGJIE_SRC_OBJECTS ${CANGJIE_SRC_COMMON_OBJECTS_LIST} $ CACHE INTERNAL "") endif() add_executable(cjc main.cpp ${CANGJIE_SRC_OBJECTS}) if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_dependencies(cjc cjnative) set(CJNATIVE_BACKEND "cjnative") string(TOLOWER ${CMAKE_SYSTEM_NAME}_${CMAKE_SYSTEM_PROCESSOR}_${CJNATIVE_BACKEND} output_lib_dir) set(RUNTIME_LIB_DIR ${CMAKE_BINARY_DIR}/lib/${output_lib_dir}) endif() set_target_properties(cjc PROPERTIES LINK_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SAFE_EXE_LINK_FLAG} ${CJ_RUNTIME_LINK_FLAGS}") target_link_libraries( cjc ${LLVM_LIBS} # Required by CangjieBasic ${CMAKE_DL_LIBS}) target_link_libraries(cjc boundscheck-static) # depended by LLVMCore in ${LINK_LIBS} and interrupt signal handler target_include_directories(cjc PRIVATE ${BOUNDSCHECK}/include) target_link_libraries(cjc ${ffi}) if(MINGW) execute_process( COMMAND ${CMAKE_C_COMPILER} -print-file-name=CRT_glob.o OUTPUT_VARIABLE GET_CRT_GLOB_OUTPUT ERROR_VARIABLE GET_CRT_GLOB_ERROR RESULT_VARIABLE GET_CRT_GLOB_RESULT OUTPUT_STRIP_TRAILING_WHITESPACE ) if(NOT GET_CRT_GLOB_RESULT EQUAL 0) message(FATAL_ERROR "Cannot get filepath of CRT_glob.o! Error message is: ${GET_CRT_GLOB_ERROR}") endif() target_link_libraries(cjc ${GET_CRT_GLOB_OUTPUT}) endif() if(CMAKE_BUILD_TYPE MATCHES Release AND MINGW) # Add static link option for cjc when building MinGW release. This use # case is defined in GCC Runtime Libraries Exception and has no GPL issue. target_link_options(cjc PRIVATE -static) endif() if(CANGJIE_VISIBLE_OPTIONS_ONLY) # `cjc` and `cjc-release` are identical except that `cjc` supports all compile options but # `cjc-release` supports visible options only. Visible options are features that are ready to be # released to users. add_executable(cjc-release main.cpp ${CANGJIE_SRC_COMMON_OBJECTS_LIST} $) add_dependencies(cjc-release cjc) # Since `cjc` is almost identical to `cjc-release`, they always have the same link flags, link # options, link libraries, etc. We get the properties of `cjc` and apply them on `cjc-release` here. include(ApplyProperties) apply_properties( FROM_TARGET cjc TO_TARGET cjc-release PROPERTY_NAMES LINK_FLAGS INCLUDE_DIRECTORIES LINK_OPTIONS LINK_LIBRARIES) install( PROGRAMS ${CMAKE_BINARY_DIR}/bin/cjc-release${CMAKE_EXECUTABLE_SUFFIX} DESTINATION bin/ RENAME cjc${CMAKE_EXECUTABLE_SUFFIX}) else() install(TARGETS cjc) endif() # `cjc-frontend.exe` is a special wrapper executable to run our compiler frontend on windows. # On windows platform, symbolic link can only be created with command prompt with administrator # privilege. To be able to build the project with no special privilege, our build strategy is as # follows: # 1) On windows Platform && building Release: # Use our wrapper executable as `cjc-frontend.exe`. Debug of `cjc.exe` by calling `cjc-frontend.exe` # would be unavailable since they are different programs. The size of `cjc-frontend.exe` is small. # No special privilege is required. # 2) On windows Platform && building Debug && without administrator privilege: # Simply make a copy of `cjc.exe` and name it `cjc-frontend.exe`. Same debugging experience between # `cjc.exe` and `cjc-frontend.exe`. The size of `cjc-frontend.exe` would be the same as `cjc.exe`, # which means that much space would be used. Since it is for debugging only, it should be tolerable. # 3) On windows Platform && building Debug && with administrator privilege / On Linux: # Create symbolic link `cjc-frontend(.exe)` pointing to `cjc(.exe)`. Same debugging experience between # `cjc(.exe)` and `cjc-frontend(.exe)`. Minimum size usage. if(WIN32 AND CMAKE_BUILD_TYPE MATCHES Release) add_executable(cjc-frontend main-frontend.cpp) target_link_options(cjc-frontend PRIVATE -static) else() add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/bin/cjc-frontend${CMAKE_EXECUTABLE_SUFFIX} COMMAND ${CMAKE_COMMAND} -D CMAKE_CROSSCOMPILING=${CMAKE_CROSSCOMPILING} -D WIN32=${WIN32} -D LINK_TARGET=cjc${CMAKE_EXECUTABLE_SUFFIX} -D LINK_NAME=cjc-frontend${CMAKE_EXECUTABLE_SUFFIX} -D WORKING_DIR=${CMAKE_BINARY_DIR}/bin -P "${CMAKE_SOURCE_DIR}/cmake/modules/make-symlink.cmake" DEPENDS cjc) add_custom_target( cjc-frontend ALL DEPENDS ${CMAKE_BINARY_DIR}/bin/cjc-frontend${CMAKE_EXECUTABLE_SUFFIX} COMMENT "Making cjc-frontend${CMAKE_EXECUTABLE_SUFFIX}") endif() if(COMPILER_EXPLORER_RACE_FIX) add_compile_definitions(COMPILER_EXPLORER_RACE_FIX) endif() if(WIN32 AND CMAKE_BUILD_TYPE MATCHES Release) install(TARGETS cjc-frontend) else() install( CODE "execute_process(COMMAND ${CMAKE_COMMAND} -D CMAKE_CROSSCOMPILING=${CMAKE_CROSSCOMPILING} -D WIN32=${WIN32} -D LINK_TARGET=cjc${CMAKE_EXECUTABLE_SUFFIX} -D LINK_NAME=cjc-frontend${CMAKE_EXECUTABLE_SUFFIX} -D WORKING_DIR=\${CMAKE_INSTALL_PREFIX}/bin -P ${CMAKE_SOURCE_DIR}/cmake/modules/make-symlink.cmake)") endif() if(MINGW AND CMAKE_BUILD_TYPE MATCHES "^(Debug|RelWithDebInfo|MinSizeRelWithDebInfo)$") add_custom_command( TARGET cjc COMMAND ${CMAKE_COMMAND} -E rename cjc.exe cjc-original.exe COMMAND objcopy --only-keep-debug --compress-debug-sections cjc-original.exe cjc.debug COMMAND objcopy --strip-debug --add-gnu-debuglink=cjc.debug cjc-original.exe cjc.exe BYPRODUCTS cjc.debug WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin COMMENT "Extracting debug info from cjc.exe to cjc.debug") install( FILES ${CMAKE_BINARY_DIR}/bin/cjc.debug DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) endif() # The autosdk, IDE need this library. set(FRONTEND_SRC_OBJECTS $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $) if (CANGJIE_CODEGEN_CJNATIVE_BACKEND) set(FRONTEND_SRC_OBJECTS ${FRONTEND_SRC_OBJECTS} $) endif() if (CANGJIE_CODEGEN_CJNATIVE_BACKEND) list(APPEND FRONTEND_SRC_OBJECTS $) list(APPEND FRONTEND_SRC_OBJECTS $) else() list(APPEND FRONTEND_SRC_OBJECTS $) list(APPEND FRONTEND_SRC_OBJECTS $) endif() add_library(cangjie-lsp ${FRONTEND_SRC_OBJECTS}) if(MINGW AND CMAKE_BUILD_TYPE MATCHES "^(Debug|RelWithDebInfo|MinSizeRelWithDebInfo)$") set(WIN_DEBUG_USE_STATIC_LIB ON) endif() if(NOT WIN_DEBUG_USE_STATIC_LIB) add_library(cangjie-lsp-share SHARED ${FRONTEND_SRC_OBJECTS}) set_target_properties(cangjie-lsp-share PROPERTIES OUTPUT_NAME "cangjie-lsp") target_link_libraries(cangjie-lsp-share ${ffi} boundscheck-static) if(MINGW) target_link_options(cangjie-lsp-share PRIVATE -pthread) endif() endif() # We should remove this once the LSPCompilerInstance is out of compiler if(WIN_DEBUG_USE_STATIC_LIB) install(TARGETS cangjie-lsp DESTINATION "tools/lib") endif() if(CANGJIE_CODEGEN_CJNATIVE_BACKEND AND NOT WIN_DEBUG_USE_STATIC_LIB) install( TARGETS cangjie-lsp-share RUNTIME DESTINATION tools/bin ARCHIVE DESTINATION tools/lib LIBRARY DESTINATION tools/lib) endif() # LSPMacroServer for lsp add_executable(LSPMacroServer main-macrosrv.cpp) if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_dependencies(LSPMacroServer cjnative) endif() set_target_properties(LSPMacroServer PROPERTIES LINK_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SAFE_EXE_LINK_FLAG} ${CJ_RUNTIME_LINK_FLAGS}") target_link_libraries( LSPMacroServer ${LLVM_LIBS} # Required by CangjieBasic ${CMAKE_DL_LIBS}) target_link_libraries(LSPMacroServer boundscheck-static) # depended by LLVMCore in ${LINK_LIBS} and interrupt signal handler target_include_directories(LSPMacroServer PRIVATE ${BOUNDSCHECK}/include) add_dependencies(LSPMacroServer CangjieFlatbuffersHeaders) target_include_directories(LSPMacroServer PRIVATE ${FLATBUFFERS_INCLUDE_DIR}) if(WIN_DEBUG_USE_STATIC_LIB) target_link_libraries(LSPMacroServer ${ffi} cangjie-lsp) else() target_link_libraries(LSPMacroServer ${ffi} cangjie-lsp-share) endif() # Add the rpath for LSPMacroServer if(DARWIN) set_property(TARGET LSPMacroServer APPEND PROPERTY INSTALL_RPATH "@loader_path/../lib") else() set_property(TARGET LSPMacroServer APPEND PROPERTY INSTALL_RPATH "\$ORIGIN/../lib") endif() install(TARGETS LSPMacroServer DESTINATION "tools/bin") # The chir-dis add_executable(chir-dis main-chir-dis.cpp) if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_dependencies(chir-dis cjnative) endif() set_target_properties(chir-dis PROPERTIES LINK_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SAFE_EXE_LINK_FLAG} ${CJ_RUNTIME_LINK_FLAGS}") target_link_libraries( chir-dis ${LLVM_LIBS} # Required by CangjieBasic ${CMAKE_DL_LIBS}) target_link_libraries(chir-dis boundscheck-static) # Depended by LLVMCore in ${LINK_LIBS} and interrupt signal handler target_include_directories(chir-dis PRIVATE ${BOUNDSCHECK}/include) add_dependencies(chir-dis CangjieFlatbuffersHeaders) target_include_directories(chir-dis PRIVATE ${FLATBUFFERS_INCLUDE_DIR}) if(WIN_DEBUG_USE_STATIC_LIB) target_link_libraries(chir-dis ${ffi} cangjie-lsp) else() target_link_libraries(chir-dis ${ffi} cangjie-lsp-share) endif() # Add the rpath for chir-dis if(DARWIN) set_property(TARGET chir-dis APPEND PROPERTY INSTALL_RPATH "@loader_path/../lib") else() set_property(TARGET chir-dis APPEND PROPERTY INSTALL_RPATH "\$ORIGIN/../lib") endif() install(TARGETS chir-dis DESTINATION "tools/bin") set(CANGJIE_LIB_FRONTEND $ $) if (CANGJIE_CODEGEN_CJNATIVE_BACKEND) list(APPEND CANGJIE_LIB_FRONTEND $) list(APPEND CANGJIE_LIB_FRONTEND $) endif() if(NOT WIN_DEBUG_USE_STATIC_LIB) add_library(cangjie-frontend SHARED ${CANGJIE_LIB_FRONTEND}) else() add_library(cangjie-frontend ${CANGJIE_LIB_FRONTEND}) endif() if (CANGJIE_CODEGEN_CJNATIVE_BACKEND) if(NOT WIN_DEBUG_USE_STATIC_LIB) target_link_libraries(cangjie-frontend cangjie-lsp-share) if(MINGW) target_link_libraries(cangjie-frontend -pthread) endif() endif() if(DARWIN) target_link_libraries(cangjie-frontend -lLLVM) elseif(MINGW) target_link_libraries(cangjie-frontend -lLLVM-15 -lLLVM-Foundation-15) else() target_link_libraries(cangjie-frontend -lLLVM-15) endif() target_link_directories(cangjie-frontend PUBLIC ${CMAKE_BINARY_DIR}/third_party/llvm/lib) install( TARGETS cangjie-frontend RUNTIME DESTINATION tools/bin ARCHIVE DESTINATION tools/lib LIBRARY DESTINATION tools/lib) endif() if(CANGJIE_BUILD_CJDB) add_dependencies(lldb cangjie-frontend) endif() add_subdirectory(AST) add_subdirectory(Driver) add_subdirectory(Parse) add_subdirectory(Macro) add_subdirectory(Frontend) add_subdirectory(FrontendTool) add_subdirectory(IncrementalCompilation) add_subdirectory(Lex) add_subdirectory(ConditionalCompilation) add_subdirectory(Sema) add_subdirectory(Modules) add_subdirectory(Utils) add_subdirectory(Basic) add_subdirectory(Option) add_subdirectory(Mangle) if (CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_subdirectory(MetaTransformation) endif() if (CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_subdirectory(CHIR) add_subdirectory(CodeGen) endif() set(AST_FFI_OBJECTS $ $ $ $ $ $ $ $ $) add_library(cangjie-ast-support STATIC ${AST_FFI_OBJECTS}) string(TOLOWER ${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND} output_lib_dir) install(TARGETS cangjie-ast-support DESTINATION lib/${output_lib_dir}${SANITIZER_SUBPATH}) cangjie_compiler-1.0.7/src/CodeGen/000077500000000000000000000000001510705540100171025ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CodeGen/Base/000077500000000000000000000000001510705540100177545ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CodeGen/Base/AllocateImpl.cpp000066400000000000000000000035001510705540100230240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements codegen for CHIR Allocate. */ #include "Base/AllocateImpl.h" #include "Base/CGTypes/CGClassType.h" #include "Base/CHIRExprWrapper.h" #include "IRBuilder.h" #include "cangjie/CHIR/Type/ClassDef.h" using namespace Cangjie; using namespace CodeGen; llvm::Value* CodeGen::GenerateAllocate(IRBuilder2& irBuilder, const CHIRAllocateWrapper& alloca) { auto allocaType = alloca.GetType(); if (allocaType->IsClass()) { auto classType = StaticCast(allocaType); auto ret = irBuilder.CallClassIntrinsicAlloc(*classType); if (classType->IsAutoEnv()) { CJC_ASSERT(!classType->IsAutoEnvBase()); auto payload = irBuilder.GetPayloadFromObject(ret); auto methods = classType->GetClassDef()->GetMethods(); for (size_t idx = 0; idx < methods.size(); ++idx) { auto virtualFunc = methods[idx]; auto function = irBuilder.GetCGModule().GetOrInsertCGFunction(virtualFunc)->GetRawValue(); auto addr = irBuilder.CreateConstInBoundsGEP1_32( irBuilder.getInt8PtrTy(), payload, static_cast(idx)); irBuilder.CreateStore(irBuilder.CreateBitCast(function, irBuilder.getInt8PtrTy()), addr); } } return ret; } else { CJC_ASSERT(!allocaType->IsThis() && "CHIR should not try to allocate memory for `ThisType`."); auto cgType = CGType::GetOrCreate(irBuilder.GetCGModule(), allocaType); return irBuilder.CreateEntryAlloca(*cgType); } } cangjie_compiler-1.0.7/src/CodeGen/Base/AllocateImpl.h000066400000000000000000000012431510705540100224730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements codegen for CHIR Allocate. */ #ifndef CANGJIE_ALLOCATEIMPL_H #define CANGJIE_ALLOCATEIMPL_H #include "llvm/IR/Value.h" namespace Cangjie { namespace CodeGen { class IRBuilder2; class CHIRAllocateWrapper; llvm::Value* GenerateAllocate(IRBuilder2& irBuilder, const CHIRAllocateWrapper& alloca); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_ALLOCATEIMPL_Hcangjie_compiler-1.0.7/src/CodeGen/Base/ApplyImpl.cpp000066400000000000000000000445001510705540100223720ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements codegen for CHIR Apply. */ #include "Base/ApplyImpl.h" #include "Base/CHIRExprWrapper.h" #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "CJNative/CGCFFI.h" #endif #include "IRAttribute.h" #include "IRBuilder.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/Type.h" using namespace Cangjie; using namespace CodeGen; namespace { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void AddAttributeForWrapper(const IRBuilder2& builder, const CHIR::FuncType& funcTy, const llvm::FunctionType* varFuncTy, llvm::Function* func, llvm::CallBase& callInst, const bool sret) { AddLinkageTypeMetadata(*func, llvm::GlobalValue::LinkageTypes::InternalLinkage, false); AddFnAttr(func, llvm::Attribute::NoInline); AddFnAttr(func, llvm::Attribute::get(func->getContext(), CJ2C_ATTR)); auto& context = builder.GetCGContext(); auto& options = context.GetCompileOptions(); auto& cgMod = builder.GetCGModule(); if (sret) { callInst.addParamAttr(0, llvm::Attribute::NoAlias); AddSRetAttribute(func->arg_begin()); AddSRetAttribute(&callInst); if (options.target.os == Triple::OSType::WINDOWS) { auto elemType = GetPointerElementType(varFuncTy->getParamType(0)); auto align = llvm::Align(GetTypeAlignment(cgMod, *elemType)); auto alignAttr = llvm::Attribute::getWithAlignment(func->getContext(), align); callInst.addParamAttr(0, alignAttr); func->arg_begin()->addAttr(alignAttr); } } if (options.target.arch == Triple::ArchType::AARCH64) { return; } else if (func->getReturnType()->isIntegerTy(1)) { AddRetAttr(func, llvm::Attribute::ZExt); SetZExtAttrForCFunc(callInst); } if (options.target.os == Triple::OSType::WINDOWS) { return; } unsigned sretOffset = sret ? 1 : 0; unsigned cumulativeOffset = 0; auto chirParamTys = funcTy.GetParamTypes(); for (unsigned i = 0; i < chirParamTys.size(); i++) { CJC_ASSERT(i < chirParamTys.size() && chirParamTys[i]); auto chirParamTy = chirParamTys[i]; if (!IsCommonStruct(*chirParamTy)) { continue; } if (IsZeroSizedTypeInC(cgMod, *chirParamTy)) { cumulativeOffset--; continue; } unsigned idx = i + cumulativeOffset + sretOffset; auto paramType = varFuncTy->getParamType(idx); if (!IsLitStructPtrType(paramType) && IsStructPtrType(paramType)) { // 1 means the actual callee. auto argument = func->getArg(idx + 1); AddByValAttribute(argument, GetTypeAlignment(cgMod, *argument->getType())); auto byValAttr = llvm::Attribute::getWithByValType(context.GetLLVMContext(), GetPointerElementType(paramType)); callInst.addParamAttr(idx, byValAttr); } else if (GetTypeSize(cgMod, *paramType) < GetTypeSize(cgMod, *chirParamTy)) { cumulativeOffset++; } } } void GenerateWrapperFuncBody(IRBuilder2& builder, const CHIR::FuncType& funcTy, llvm::FunctionType* varFuncTy, llvm::Function* function, const bool sret) { CJC_ASSERT(varFuncTy && function); llvm::IRBuilderBase::InsertPointGuard builderGuard(builder); auto entryBB = builder.CreateEntryBasicBlock(function, "entry"); builder.SetInsertPoint(entryBB); // Clear debug location info, we don't need it here. builder.SetCurrentDebugLocation({}); // In the wrapper function, we only need to call the actual function pointer and return its return value. // First, we construct the arguments to call the actual function pointer. std::vector args; args.reserve(varFuncTy->getNumParams()); auto argIt = function->arg_begin(); if (sret) { // In this case, the first parameter of the wrapper function is the pointer to the return value. args.push_back(argIt); ++argIt; } // Skip the called function pointer in the wrapper function parameters. // The following parameters are the arguments of the called function pointer. for (++argIt; argIt != function->arg_end(); ++argIt) { args.push_back(argIt); } auto callInst = builder.LLVMIRBuilder2::CreateCall(varFuncTy, function->getArg(sret ? 1 : 0), args); if (varFuncTy->getReturnType()->isVoidTy()) { (void)builder.CreateRetVoid(); } else { (void)builder.CreateRet(callInst); } AddAttributeForWrapper(builder, funcTy, varFuncTy, function, *callInst, sret); } /** * On cjnative backend, invoking CFunc requires a stub, but indirect invocations cannot be detected. * Therefore, in this scenario, we add a wrapper function for the CFunc variable to ensure that the stub function is * still used. */ llvm::Function* GetIndirectCFuncCallWrapper(IRBuilder2& builder, const CHIR::FuncType& funcTy, llvm::Type* funcPtrType) { llvm::FunctionType* funcType = nullptr; auto& cgMod = builder.GetCGModule(); if (IsLitStructPtrType(funcPtrType)) { // In indirect calling scenario, we can get the actual cfunc type by funcTy. auto cgType = static_cast(CGType::GetOrCreate(cgMod, &funcTy)); CJC_ASSERT(cgType && llvm::isa(cgType->GetLLVMFunctionType())); funcType = llvm::cast(cgType->GetLLVMFunctionType()); } else { funcType = llvm::dyn_cast(GetPointerElementType(funcPtrType)); } CJC_NULLPTR_CHECK(funcType); // First, we generate a wrapper function based on the type of the called function. // The type of the wrapper function complies with the following rules: // 1. If the return value is not a structure, or is a structure does not need to be placed in the first parameter // position, we put the actual called function pointer in the first argument, and it's parameters in turn // follow. // 2. If the return value is a structure that needs to be placed in the first parameter position, we keep the // return value as the first argument, then put the actual called function pointer in the second argument, and // it's parameters in turn follow. std::vector paramTys; paramTys.reserve(funcType->getNumParams() + 1); auto beginIt = funcType->param_begin(); auto chirRetTy = funcTy.GetReturnType(); bool sret = IsCommonStruct(*chirRetTy) && !IsZeroSizedTypeInC(cgMod, *chirRetTy) && funcType->getReturnType()->isVoidTy(); if (sret) { CJC_ASSERT(!funcType->params().empty() && "CFunc with sret has at least one parameter."); paramTys.emplace_back(*funcType->param_begin()); beginIt++; } paramTys.emplace_back(funcType->getPointerTo()); paramTys.insert(paramTys.end(), beginIt, funcType->param_end()); auto wrapperFuncType = llvm::FunctionType::get(funcType->getReturnType(), paramTys, funcType->isVarArg()); auto wrapperFunc = cgMod.GetOrInsertFunction("wrapper." + MangleType(funcTy), wrapperFuncType); // For CFunc of the same type, we only need to generate one wrapper function. If we get a declaration, // it means that this is the first generation, and we need to build its function body then. if (wrapperFunc->isDeclaration()) { GenerateWrapperFuncBody(builder, funcTy, funcType, wrapperFunc, sret); } return wrapperFunc; } llvm::Function* UpdateCFuncCalleeAndArgs( IRBuilder2& builder, const CHIR::FuncType& funcTy, llvm::Value* callee, std::vector& args) { CJC_NULLPTR_CHECK(callee); CJC_ASSERT(funcTy.IsCFunc()); if (!llvm::isa(callee)) { auto calleeType = callee->getType(); auto wrapperFunc = GetIndirectCFuncCallWrapper(builder, funcTy, calleeType); auto& cgMod = builder.GetCGModule(); auto cgType = CGType::GetOrCreate(builder.GetCGModule(), &funcTy); // A literal struct pointer type means a cfunc pointer here. // So we need insert a bitcast instruction for calling it then. if (IsLitStructPtrType(calleeType)) { auto cast = builder.CreateBitCast(callee, cgType->GetLLVMType()); (void)args.insert(args.begin(), cgMod.CreateGhostCFuncArgValue(*cast, *cgType)); } else { (void)args.insert(args.begin(), cgMod.CreateGhostCFuncArgValue(*callee, *cgType)); } return wrapperFunc; } return llvm::cast(callee); } inline bool HasAddrSpace(const CGValue* cgVal, unsigned int addrSpace) { auto llvmVal = cgVal->GetRawValue(); CJC_NULLPTR_CHECK(llvmVal); auto llvmType = llvmVal->getType(); return llvmType->isPointerTy() && llvmType->getPointerAddressSpace() == addrSpace; } #ifdef __APPLE__ inline void AddAttrForIntTypeArgOnMacMx(llvm::CallBase& callRet, const llvm::Use& arg) { auto intType = llvm::dyn_cast(arg->getType()); if (!intType) { return; } AddParamAttr(&callRet, arg.getOperandNo(), llvm::Attribute::NoUndef); if (intType->isIntegerTy(1u)) { // 1u means bool type. AddParamAttr(&callRet, arg.getOperandNo(), llvm::Attribute::ZExt); } else if (intType->getBitWidth() < 32) { // 32 means int32_t type. AddParamAttr(&callRet, arg.getOperandNo(), llvm::Attribute::SExt); } } #endif llvm::Value* CreateCFuncCallOrInvoke(IRBuilder2& irBuilder, llvm::Function& callee, const std::vector& argsVal, const CHIR::Type& chirRetTy, bool isSRet) { auto& cgMod = irBuilder.GetCGModule(); std::vector llvmArgs; if (isSRet) { llvmArgs.reserve(argsVal.size() + 1); auto retVal = irBuilder.CreateEntryAlloca(*CGType::GetOrCreate(cgMod, &chirRetTy)); llvmArgs.emplace_back(retVal); } else { llvmArgs.reserve(argsVal.size()); } for (auto cgVal : argsVal) { auto& cgValTy = cgVal->GetCGType()->GetOriginal(); if (IsZeroSizedTypeInC(cgMod, cgValTy)) { continue; } if (HasAddrSpace(cgVal, 1u)) { auto cgType = CGType::GetOrCreate(cgMod, &cgValTy); auto cast = irBuilder.CreateAddrSpaceCast(cgVal->GetRawValue(), cgType->GetLLVMType()); llvmArgs.emplace_back(cast); } else { llvmArgs.emplace_back(cgVal->GetRawValue()); } } auto callRet = irBuilder.CreateCallOrInvoke(callee.getFunctionType(), &callee, llvmArgs); const auto& target = cgMod.GetCGContext().GetCompileOptions().target; const bool nonAarch64 = target.arch != Triple::ArchType::AARCH64; for (auto it = callee.arg_begin(); it != callee.arg_end(); ++it) { if (nonAarch64 && it->hasByValAttr()) { auto byValAttr = llvm::Attribute::getWithByValType(cgMod.GetLLVMContext(), GetPointerElementType(it->getType())); AddParamAttr(callRet, it->getArgNo(), byValAttr); } } #ifdef __APPLE__ const bool macOnMx = !nonAarch64 && (target.os == Triple::OSType::DARWIN || target.os == Triple::OSType::IOS); if (macOnMx) { for (auto ext : {llvm::Attribute::SExt, llvm::Attribute::ZExt}) { if (callee.hasRetAttribute(ext)) { callRet->addRetAttr(ext); } } for (auto& arg : callRet->args()) { AddAttrForIntTypeArgOnMacMx(*callRet, arg); } } #endif if (isSRet) { AddParamAttr(callRet, 0, llvm::Attribute::NoAlias); AddSRetAttribute(callRet); } return isSRet ? *llvmArgs.begin() : callRet; } CGValue* HandleVarargTypePromotion(IRBuilder2& irBuilder, CGValue* argVal) { CJC_NULLPTR_CHECK(argVal); using CHIRTypeKind = CHIR::Type::TypeKind; auto& cgMod = irBuilder.GetCGModule(); auto chirArgTy = argVal->GetCGType()->GetOriginal(); llvm::Value* ret = nullptr; if (chirArgTy.IsFloat()) { ret = irBuilder.CreateFPExt(argVal->GetRawValue(), llvm::Type::getDoubleTy(cgMod.GetLLVMContext())); } else if (Utils::In(chirArgTy.GetTypeKind(), {CHIRTypeKind::TYPE_INT8, CHIRTypeKind::TYPE_INT16})) { ret = irBuilder.CreateSExt(argVal->GetRawValue(), llvm::Type::getInt32Ty(cgMod.GetLLVMContext())); } else if (Utils::In(chirArgTy.GetTypeKind(), {CHIRTypeKind::TYPE_UINT8, CHIRTypeKind::TYPE_UINT16})) { ret = irBuilder.CreateZExt(argVal->GetRawValue(), llvm::Type::getInt32Ty(cgMod.GetLLVMContext())); } else if (chirArgTy.IsBoolean()) { ret = irBuilder.CreateZExt(argVal->GetRawValue(), llvm::Type::getInt32Ty(cgMod.GetLLVMContext())); } else { return argVal; } CJC_NULLPTR_CHECK(ret); auto cgType = chirArgTy.IsFloat() ? CGType::GetFloat64CGType(cgMod) : CGType::GetInt32CGType(cgMod); return cgMod.CreateGhostCFuncArgValue(*ret, *cgType); } void HandleForeignFuncCall(IRBuilder2& irBuilder, const CHIR::FuncType& chirFuncTy, std::vector& argsVal) { // Only foreign functions can have variable-length arguments. if (!chirFuncTy.HasVarArg()) { return; } size_t argCount = 0; size_t paramSize = chirFuncTy.GetParamTypes().size(); for (auto& arg : argsVal) { argCount++; if (IsZeroSizedTypeInC(irBuilder.GetCGModule(), arg->GetCGType()->GetOriginal())) { continue; } if (argCount > paramSize) { arg = HandleVarargTypePromotion(irBuilder, arg); } } } llvm::Value* HandleApplyCFunc( IRBuilder2& irBuilder, const CHIRApplyWrapper& apply, const CHIR::FuncType& chirFuncTy, const CGValue& callee) { auto& cgMod = irBuilder.GetCGModule(); std::vector argsVal; for (auto arg : apply.GetArgs()) { if (!IsZeroSizedTypeInC(cgMod, *arg->GetType())) { argsVal.emplace_back(cgMod | arg); } } HandleForeignFuncCall(irBuilder, chirFuncTy, argsVal); bool handleRet = cgMod.GetCGCFFI().ProcessInvocation(chirFuncTy, argsVal, irBuilder); auto actualCallee = UpdateCFuncCalleeAndArgs(irBuilder, chirFuncTy, callee.GetRawValue(), argsVal); auto chirRetTy = chirFuncTy.GetReturnType(); CJC_ASSERT(actualCallee && chirRetTy); bool isSRet = !handleRet && chirRetTy->IsStruct() && !IsZeroSizedTypeInC(cgMod, *chirRetTy); auto callRet = CreateCFuncCallOrInvoke(irBuilder, *actualCallee, argsVal, *chirRetTy, isSRet); if (IsZeroSizedTypeInC(cgMod, *chirRetTy)) { return irBuilder.CreateEntryAlloca( *CGType::GetOrCreate(cgMod, chirRetTy), apply.GetCallee()->GetSrcCodeIdentifier() + ".ret"); } if (!handleRet) { return callRet; } auto sret = irBuilder.CreateEntryAlloca( *CGType::GetOrCreate(cgMod, chirRetTy), apply.GetCallee()->GetSrcCodeIdentifier() + ".ret"); return cgMod.GetCGCFFI().ProcessCallRet(*chirRetTy, *callRet, *sret, irBuilder); } #endif } // namespace llvm::Value* CodeGen::GenerateApply(IRBuilder2& irBuilder, const CHIRApplyWrapper& apply) { irBuilder.SetCHIRExpr(&apply); irBuilder.EmitLocation(apply); auto& cgMod = irBuilder.GetCGModule(); auto callee = cgMod | apply.GetCallee(); CJC_ASSERT(apply.GetCallee()->GetType() && apply.GetCallee()->GetType()->IsFunc()); auto chirFuncTy = StaticCast(apply.GetCallee()->GetType()); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (chirFuncTy->IsCFunc()) { return HandleApplyCFunc(irBuilder, apply, *chirFuncTy, *callee); } #endif std::vector argsVal; for (auto arg : apply.GetArgs()) { (void)argsVal.emplace_back(cgMod | arg); } auto funcType = static_cast(CGType::GetOrCreateWithNode(cgMod, apply.GetCallee())); auto funcPtrType = funcType->GetLLVMFunctionType()->getPointerTo(); llvm::Value* calleeVal = callee->GetRawValue(); if (!apply.GetCallee()->TestAttr(CHIR::Attribute::STATIC)) { if (IsStructOrExtendMethod(*apply.GetCallee()) && argsVal[0]->GetRawValue()->getType()->getPointerAddressSpace() == 1U && !cgMod.GetCGContext().GetBasePtrOf(**argsVal[0])) { auto wrapper = StaticCast(callee)->GetWrapperFunction(); funcType = static_cast(CGType::GetOrCreateWithNode(cgMod, apply.GetCallee(), false)); funcPtrType = wrapper->getType(); calleeVal = wrapper; } } auto castedCallee = irBuilder.CreateBitCast(calleeVal, funcPtrType); bool isClosureCall = apply.GetCallee()->IsLocalVar(); llvm::Value* thisTI = nullptr; if (apply.IsCalleeStatic()) { auto curCGFunc = irBuilder.GetInsertCGFunction(); CJC_ASSERT(curCGFunc != nullptr); auto curCHIRFunc = DynamicCast(&curCGFunc->GetOriginal()); CJC_ASSERT(curCHIRFunc != nullptr); auto curCHIRFuncParentTy = curCHIRFunc->GetParentCustomTypeOrExtendedType(); auto calleeFunc = DynamicCast(apply.GetCallee()); CJC_NULLPTR_CHECK(calleeFunc); auto calleeFuncParentTy = calleeFunc->GetParentCustomTypeOrExtendedType(); auto curFunc = irBuilder.GetInsertFunction(); CJC_ASSERT(curFunc != nullptr); if (curCGFunc && curCGFunc->GetOriginal().TestAttr(CHIR::Attribute::STATIC) && curCHIRFuncParentTy == calleeFuncParentTy) { // use the param `thisTI` if we are in context of static func thisTI = curFunc->arg_end() - 1; } else if (curCHIRFuncParentTy && curCHIRFuncParentTy == calleeFuncParentTy && curCHIRFuncParentTy->IsReferenceType()) { // here we want to exclude those case where `this` param has no type info, but maybe the condition is // not correct if we are in context of a non-static func, then we can get the `thisTI` from the `this` param CJC_NULLPTR_CHECK(curCGFunc); CJC_NULLPTR_CHECK(curFunc); auto thisParam = curCGFunc->IsSRet() ? curFunc->arg_begin() + 1 : curFunc->arg_begin(); thisTI = irBuilder.GetTypeInfoFromObject(thisParam); } else { thisTI = irBuilder.CreateBitCast(irBuilder.CreateTypeInfo(apply.GetThisType()), CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext())); } CJC_ASSERT(thisTI != nullptr); } return irBuilder.CreateCallOrInvoke(*funcType, castedCallee, argsVal, isClosureCall, thisTI); } cangjie_compiler-1.0.7/src/CodeGen/Base/ApplyImpl.h000066400000000000000000000012021510705540100220270ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements codegen for CHIR Apply. */ #ifndef CANGJIE_APPLY_H #define CANGJIE_APPLY_H #include "llvm/IR/Value.h" namespace Cangjie { namespace CodeGen { class IRBuilder2; class CHIRApplyWrapper; llvm::Value* GenerateApply(IRBuilder2& irBuilder, const CHIRApplyWrapper& apply); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_APPLY_H cangjie_compiler-1.0.7/src/CodeGen/Base/ArithmeticOpImpl.cpp000066400000000000000000000415661510705540100237060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/ArithmeticOpImpl.h" #include #include "llvm/IR/Module.h" #include "Base/CHIRExprWrapper.h" #include "IRBuilder.h" #include "Utils/BlockScopeImpl.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Expression/Terminator.h" namespace { using namespace Cangjie; using namespace Cangjie::CodeGen; void HandleBaseIsOne(IRBuilder2& irBuilder, llvm::Argument* base) { auto codeGenOne = llvm::ConstantInt::get(base->getType(), 1); auto baseEqOne = irBuilder.CreateICmpEQ(base, codeGenOne); auto [baseEqOneBB, baseNeqOneBB] = Vec2Tuple<2>(irBuilder.CreateAndInsertBasicBlocks({GenNameForBB("base.eq.one"), GenNameForBB("base.neq.one")})); irBuilder.CreateCondBr(baseEqOne, baseEqOneBB, baseNeqOneBB); irBuilder.SetInsertPoint(baseEqOneBB); irBuilder.CreateRet(codeGenOne); irBuilder.SetInsertPoint(baseNeqOneBB); } void HandleExponentIsZero(IRBuilder2& irBuilder, const llvm::Argument* base, llvm::Argument* exponent) { auto expEqZero = irBuilder.CreateICmpEQ(exponent, llvm::ConstantInt::get(exponent->getType(), 0)); auto [expEqZeroBB, expNeqZeroBB] = Vec2Tuple<2>(irBuilder.CreateAndInsertBasicBlocks({GenNameForBB("exp.eq.zero"), GenNameForBB("exp.neq.zero")})); irBuilder.CreateCondBr(expEqZero, expEqZeroBB, expNeqZeroBB); irBuilder.SetInsertPoint(expEqZeroBB); irBuilder.CreateRet(llvm::ConstantInt::get(base->getType(), 1)); irBuilder.SetInsertPoint(expNeqZeroBB); } void HandleExponentIsPositive( IRBuilder2& irBuilder, llvm::Argument* base, llvm::Argument* exponent, llvm::Function* fastPowerFunc) { auto codeGenOne = llvm::ConstantInt::get(exponent->getType(), 1); // let tmp be assigned fastPower(exp / 2) ^ 2 auto newExp = irBuilder.CreateLShr(exponent, codeGenOne); llvm::Value* tmp = irBuilder.LLVMIRBuilder2::CreateCall(fastPowerFunc, {base, newExp}); tmp = irBuilder.CreateMul(tmp, tmp); // 1) If exponent is an odd number, return base * tmp: auto expIsOdd = irBuilder.CreateICmpEQ(irBuilder.CreateAnd(exponent, codeGenOne), codeGenOne); auto [expIsOddBB, expIsEvenBB] = Vec2Tuple<2>(irBuilder.CreateAndInsertBasicBlocks({GenNameForBB("exp.is.odd"), GenNameForBB("exp.is.even")})); irBuilder.CreateCondBr(expIsOdd, expIsOddBB, expIsEvenBB); irBuilder.SetInsertPoint(expIsOddBB); irBuilder.CreateRet(irBuilder.CreateMul(base, tmp)); irBuilder.SetInsertPoint(expIsEvenBB); // 2) If exponent is an even number, return tmp: irBuilder.CreateRet(tmp); } // Float64 ** Float64 => Float64 llvm::Value* CreateFastPowerCallForFloat64PowerFloat64( IRBuilder2& irBuilder, const CGValue* leftVal, const CGValue* rightVal) { auto& cgMod = irBuilder.GetCGModule(); auto paramTy = CGType::GetFloat64CGType(cgMod); auto float64Type = paramTy->GetLLVMType(); auto valLeft = irBuilder.CreateFPCast(**leftVal, float64Type); auto valRight = irBuilder.CreateFPCast(**rightVal, float64Type); auto cgValLeft = CGValue(valLeft, paramTy); auto cgValRight = CGValue(valRight, paramTy); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::Value* ret = irBuilder.CallIntrinsicFunction(float64Type, "CJ_CORE_CPow", {&cgValLeft, &cgValRight}); #endif ret = irBuilder.CreateFPCast(ret, float64Type); return ret; } // Float64 ** Int64 => Float64 llvm::Value* CreateFastPowerCallForFloat64PowerInt64(IRBuilder2& irBuilder, CGValue* leftVal, CGValue* rightVal) { auto& cgMod = irBuilder.GetCGModule(); llvm::Type* type = CGType::GetFloat64CGType(cgMod)->GetLLVMType(); std::string runtimeFunc = "CJ_CORE_FastPowerDoubleInt64"; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::Value* ret = irBuilder.CallIntrinsicFunction(type, runtimeFunc, {leftVal, rightVal}); #endif return ret; } // Int64 ** UInt64 => Int64 llvm::Value* CreateFastPowerCallForInt64PowerUInt64( IRBuilder2& irBuilder, const CGValue* leftVal, const CGValue* rightVal) { auto lType = (**leftVal)->getType(); auto rType = (**rightVal)->getType(); CJC_ASSERT(lType->isIntegerTy()); CJC_ASSERT(rType->isIntegerTy()); auto funcMangledName = "fastPower_" + GetTypeName(leftVal->GetCGType()) + GetTypeName(rightVal->GetCGType()); auto function = irBuilder.GetCGModule().GetLLVMModule()->getFunction(funcMangledName); if (!function) { /* Generate the IR for the following code: * func fastPower_i64i64(x: Int64, y: UInt64): Int64 { * if (x == 1) { * return 1 * } else if (y == 0) { * return 1 * } * tmp = fastPower_i64i64(x, y / 2) ^ 2 * if (y & 1 == 1) { * return tmp * x * } else { * return tmp * } * } */ std::vector params{lType, rType}; // lType is the same as return type. auto ft = CGType::GetCodeGenFunctionType(irBuilder.GetLLVMContext(), lType, params); // The return value type and parameter type of the "fastPower_*" function are the same. auto& cgMod = irBuilder.GetCGModule(); auto callee = cgMod.GetLLVMModule()->getOrInsertFunction(funcMangledName, ft).getCallee(); CJC_ASSERT(llvm::isa(callee)); function = llvm::cast(callee); function->setPersonalityFn(cgMod.GetExceptionIntrinsicPersonality()); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND SetGCCangjie(function); #endif AddLinkageTypeMetadata(*function, llvm::GlobalValue::InternalLinkage, false); NameFunctionParam(function, {"base", "exponent"}); irBuilder.CreateEntryBasicBlock(function, "entry"); CodeGenFunctionScope funcScope(irBuilder, function); // Reset try/catch information since we are generating a new function auto base = function->arg_begin(); auto exponent = function->arg_begin() + 1; // Handle: 1 ** exponent HandleBaseIsOne(irBuilder, base); // Handle: base ** 0 HandleExponentIsZero(irBuilder, base, exponent); // Handle: base ** exponent HandleExponentIsPositive(irBuilder, base, exponent, function); } // use CreateCallOrInvoke so InvokeWrapper could set landingPad correctly when the pow exp is in a try-catch block. return irBuilder.CreateCallOrInvoke(function, {**leftVal, **rightVal}); } // Convert integer type to integer type. Use for shift expr by now. llvm::Value* GenerateIntegerConvExpr(IRBuilder2& irBuilder, const CHIRBinaryExprWrapper& binOp, llvm::Value* srcValue) { auto leftArg = binOp.GetLHSOperand(); auto rightArg = binOp.GetRHSOperand(); auto targetTy = leftArg->GetType(); auto srcTy = rightArg->GetType(); auto targetTyKind = irBuilder.GetTypeKindFromType(*targetTy); auto srcTyKind = irBuilder.GetTypeKindFromType(*srcTy); CJC_ASSERT(targetTy && srcTy); // convert signed integer to signed integer. if (targetTy->IsInteger() && StaticCast(targetTy)->IsSigned() && srcTy->IsInteger() && StaticCast(srcTy)->IsSigned()) { // target type is bigger than source type. if (targetTyKind > srcTyKind) { return irBuilder.CreateSExt( srcValue, CGType::GetOrCreate(irBuilder.GetCGModule(), targetTy)->GetLLVMType()); } // target type is smaller than source type. return irBuilder.CreateTrunc(srcValue, CGType::GetOrCreate(irBuilder.GetCGModule(), targetTy)->GetLLVMType()); } // convert unsigned integer to signed integer. if (targetTy->IsInteger() && StaticCast(targetTy)->IsSigned()) { if (targetTyKind == INTEGER_CONVERT_MAP.at(srcTyKind)) { return srcValue; } // Target type is bigger than source type. if (targetTyKind > INTEGER_CONVERT_MAP.at(srcTyKind)) { return irBuilder.CreateZExt( srcValue, CGType::GetOrCreate(irBuilder.GetCGModule(), targetTy)->GetLLVMType()); } // target type is smaller than source type. return irBuilder.CreateTrunc(srcValue, CGType::GetOrCreate(irBuilder.GetCGModule(), targetTy)->GetLLVMType()); } // convert signed integer to unsigned integer. if (srcTy->IsInteger() && StaticCast(srcTy)->IsSigned()) { if (INTEGER_CONVERT_MAP.at(targetTyKind) == srcTyKind) { return srcValue; } // target type is bigger than source type. if (INTEGER_CONVERT_MAP.at(targetTyKind) > srcTyKind) { return irBuilder.CreateZExt( srcValue, CGType::GetOrCreate(irBuilder.GetCGModule(), targetTy)->GetLLVMType()); } // target type is smaller than source type. return irBuilder.CreateTrunc(srcValue, CGType::GetOrCreate(irBuilder.GetCGModule(), targetTy)->GetLLVMType()); } // convert unsigned integer to unsigned integer. // target type is bigger than source type. if (targetTyKind > srcTyKind) { return irBuilder.CreateZExt(srcValue, CGType::GetOrCreate(irBuilder.GetCGModule(), targetTy)->GetLLVMType()); } // target type is smaller than source type. return irBuilder.CreateTrunc(srcValue, CGType::GetOrCreate(irBuilder.GetCGModule(), targetTy)->GetLLVMType()); } llvm::Value* GenerateOverShiftCheck( IRBuilder2& irBuilder, const CHIRBinaryExprWrapper& binOp, const CGValue* lValue, const CGValue* rValue) { auto& cgMod = irBuilder.GetCGModule(); auto rightTy = binOp.GetRHSOperand()->GetType(); // cond: if right value of the right operand is negative auto zero = llvm::ConstantInt::get(CGType::GetOrCreate(cgMod, rightTy)->GetLLVMType(), 0); auto cmpResult = irBuilder.CreateICmpSLT(**rValue, zero, "if.cond.rightValue.negative"); auto [thenBB, elseBB, endBB] = Vec2Tuple<3>(irBuilder.CreateAndInsertBasicBlocks( {GenNameForBB("if.then"), GenNameForBB("if.else"), GenNameForBB("if.end")})); irBuilder.CreateCondBr(cmpResult, thenBB, elseBB); // then: throw ArithmeticException("Overshift: Negative shift count!") irBuilder.SetInsertPoint(thenBB); irBuilder.CreateOverflowOrArithmeticException("Overshift: Negative shift count!", false); irBuilder.CreateUnreachable(); irBuilder.SetInsertPoint(elseBB); // cond : if the value of right operand is greater than or equal to the width of left operand auto leftBitWidth = llvm::ConstantInt::get( rValue->GetCGType()->GetLLVMType(), lValue->GetCGType()->GetLLVMType()->getIntegerBitWidth()); auto cmpResult2 = irBuilder.CreateICmpSGE(**rValue, leftBitWidth, "if.cond.bitwidth.compare"); auto [then1BB, else1BB] = Vec2Tuple<2>(irBuilder.CreateAndInsertBasicBlocks({GenNameForBB("if.then"), GenNameForBB("if.else")})); irBuilder.CreateCondBr(cmpResult2, then1BB, else1BB); // then1: throw ArithmeticException("Overshift: Value of right operand is greater than or equal to the width of left // operand!") irBuilder.SetInsertPoint(then1BB); irBuilder.CreateOverflowOrArithmeticException( "Overshift: Value of right operand is greater than or equal to the width of left operand!", false); irBuilder.CreateUnreachable(); // else1BB: normal shift irBuilder.SetInsertPoint(else1BB); llvm::Value* ret = nullptr; auto rConvValue = GenerateIntegerConvExpr(irBuilder, binOp, **rValue); if (binOp.GetBinaryExprKind() == CHIR::ExprKind::LSHIFT) { ret = irBuilder.CreateShl(**lValue, rConvValue, "shl"); } else if (binOp.GetBinaryExprKind() == CHIR::ExprKind::RSHIFT) { auto opTy = binOp.GetResult()->GetType(); if (opTy->IsInteger() && StaticCast(opTy)->IsSigned()) { ret = irBuilder.CreateAShr(**lValue, rConvValue, "ashr"); } else { ret = irBuilder.CreateLShr(**lValue, rConvValue, "lshr"); } } else { CJC_ABORT(); } irBuilder.CreateBr(endBB); // endBB irBuilder.SetInsertPoint(endBB); return ret; } } // namespace namespace Cangjie { namespace CodeGen { llvm::Value* GenerateArithmeticOperation(IRBuilder2& irBuilder, const CHIRBinaryExprWrapper& binExpr) { auto& cgMod = irBuilder.GetCGModule(); CGValue* valLeft = cgMod | binExpr.GetLHSOperand(); CGValue* valRight = cgMod | binExpr.GetRHSOperand(); CJC_NULLPTR_CHECK(valLeft); CJC_NULLPTR_CHECK(valRight); return GenerateArithmeticOperation( irBuilder, binExpr.GetBinaryExprKind(), binExpr.GetResult()->GetType(), valLeft, valRight); } llvm::Value* GenerateArithmeticOperation(IRBuilder2& irBuilder, CHIR::ExprKind exprKind, const CHIR::Type* ty, const CGValue* valLeft, const CGValue* valRight) { switch (exprKind) { case CHIR::ExprKind::ADD: { if (ty->IsFloat()) { return irBuilder.CreateFAdd(**valLeft, **valRight); } else { return irBuilder.CreateAdd(**valLeft, **valRight); } } case CHIR::ExprKind::SUB: { if (ty->IsFloat()) { return irBuilder.CreateFSub(**valLeft, **valRight); } else { return irBuilder.CreateSub(**valLeft, **valRight); } } case CHIR::ExprKind::MUL: { if (ty->IsFloat()) { return irBuilder.CreateFMul(**valLeft, **valRight); } else { return irBuilder.CreateMul(**valLeft, **valRight); } } case CHIR::ExprKind::DIV: { if (ty->IsFloat()) { return irBuilder.CreateFDiv(**valLeft, **valRight, "div"); } else { bool isSignedInteger = ty->IsInteger() && StaticCast(ty)->IsSigned(); return irBuilder.GenerateDivLikeCheck(**valLeft, **valRight, isSignedInteger, true); } } case CHIR::ExprKind::MOD: { if (ty->IsFloat()) { return irBuilder.CreateFRem(**valLeft, **valRight); } else { bool isSignedInteger = ty->IsInteger() && StaticCast(ty)->IsSigned(); return irBuilder.GenerateDivLikeCheck(**valLeft, **valRight, isSignedInteger, false); } } default: printf("Unexpected expr kind: %" PRIu64 "\n", static_cast(exprKind)); CJC_ASSERT(false); return nullptr; } } llvm::Value* GenerateBitwiseOperation(IRBuilder2& irBuilder, const CHIRBinaryExprWrapper& binExpr) { auto& cgMod = irBuilder.GetCGModule(); CGValue* valLeft = cgMod | binExpr.GetLHSOperand(); CGValue* valRight = cgMod | binExpr.GetRHSOperand(); CJC_NULLPTR_CHECK(valLeft); CJC_NULLPTR_CHECK(valRight); irBuilder.EmitLocation(binExpr); switch (binExpr.GetBinaryExprKind()) { case CHIR::ExprKind::BITAND: { return irBuilder.CreateAnd(**valLeft, **valRight, "and"); } case CHIR::ExprKind::BITOR: { return irBuilder.CreateOr(**valLeft, **valRight, "or"); } case CHIR::ExprKind::BITXOR: { return irBuilder.CreateXor(**valLeft, **valRight, "xor"); } case CHIR::ExprKind::LSHIFT: case CHIR::ExprKind::RSHIFT: return GenerateOverShiftCheck(irBuilder, binExpr, valLeft, valRight); default: printf("Unexpected expr kind: %" PRIu64 "\n", static_cast(binExpr.GetBinaryExprKind())); CJC_ASSERT(false); return nullptr; } } llvm::Value* GenerateBinaryExpOperation(IRBuilder2& irBuilder, const CHIRBinaryExprWrapper& binExpr) { auto& cgMod = irBuilder.GetCGModule(); auto leftArg = binExpr.GetLHSOperand(); auto rightArg = binExpr.GetRHSOperand(); auto valLeft = cgMod | leftArg; auto valRight = cgMod | rightArg; irBuilder.EmitLocation(binExpr); return GenerateBinaryExpOperation(irBuilder, valLeft, valRight); } llvm::Value* GenerateBinaryExpOperation(IRBuilder2& irBuilder, CGValue* valLeft, CGValue* valRight) { auto leftType = valLeft->GetCGType()->GetLLVMType(); auto rightType = valRight->GetCGType()->GetLLVMType(); if (leftType->isDoubleTy() && rightType->isDoubleTy()) { return CreateFastPowerCallForFloat64PowerFloat64(irBuilder, valLeft, valRight); } else if (leftType->isDoubleTy() && rightType->isIntegerTy()) { return CreateFastPowerCallForFloat64PowerInt64(irBuilder, valLeft, valRight); } else if (leftType->isIntegerTy() && rightType->isIntegerTy()) { return CreateFastPowerCallForInt64PowerUInt64(irBuilder, valLeft, valRight); } else { // There are only three scenarios for Exp operation: // 1. FLOAT64 exp FLOAT64 => FLOAT64 // 2. FLOAT64 exp INT64 => FLOAT64 // 3. INT64 exp UINT64 => INT64 CJC_ABORT(); return nullptr; } } } // namespace CodeGen } // namespace Cangjie cangjie_compiler-1.0.7/src/CodeGen/Base/ArithmeticOpImpl.h000066400000000000000000000022201510705540100233330ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_ARITHMETICOP_IMPL_H #define CANGJIE_ARITHMETICOP_IMPL_H #include "llvm/IR/Value.h" #include "cangjie/CHIR/Expression/Terminator.h" namespace Cangjie { namespace CodeGen { class IRBuilder2; class CGValue; class CHIRBinaryExprWrapper; llvm::Value* GenerateArithmeticOperation(IRBuilder2& irBuilder, const CHIRBinaryExprWrapper& binExpr); llvm::Value* GenerateArithmeticOperation(IRBuilder2& irBuilder, CHIR::ExprKind exprKind, const CHIR::Type* ty, const CGValue* valLeft, const CGValue* valRight); llvm::Value* GenerateBitwiseOperation(IRBuilder2& irBuilder, const CHIRBinaryExprWrapper& binExpr); llvm::Value* GenerateBinaryExpOperation(IRBuilder2& irBuilder, const CHIRBinaryExprWrapper& binExpr); llvm::Value* GenerateBinaryExpOperation(IRBuilder2& irBuilder, CGValue* valLeft, CGValue* valRight); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_ARITHMETICOP_IMPL_H cangjie_compiler-1.0.7/src/CodeGen/Base/ArrayImpl.cpp000066400000000000000000000057231510705540100223670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/ArrayImpl.h" #include "Base/CHIRExprWrapper.h" #include "IRBuilder.h" using namespace Cangjie; using namespace CodeGen; llvm::Value* CodeGen::GenerateRawArrayInitByValue( IRBuilder2& irBuilder, const CHIR::RawArrayInitByValue& rawArrayInitByValue) { auto& cgMod = irBuilder.GetCGModule(); auto valueOperand = rawArrayInitByValue.GetInitValue(); auto elemValue = *(cgMod | valueOperand); auto sizeVal = **(cgMod | rawArrayInitByValue.GetSize()); auto arrTy = static_cast(rawArrayInitByValue.GetRawArray()->GetType()->GetTypeArgs()[0]); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto array = **(cgMod | rawArrayInitByValue.GetRawArray()); bool isNullValue = valueOperand->IsLocalVar() ? StaticCast(valueOperand)->GetExpr()->IsConstantNull() : false; if (isNullValue) { return array; } irBuilder.CallArrayInit(array, sizeVal, elemValue.GetRawValue(), *arrTy); return array; #endif } llvm::Value* CodeGen::GenerateRawArrayAllocate(IRBuilder2& irBuilder, const CHIRRawArrayAllocateWrapper& rawArray) { // Sized array must have 2 arguments CJC_ASSERT(rawArray.GetOperands().size() == 1 && "RawArrayAllocate's argument size is not equal to 1."); auto& cgMod = irBuilder.GetCGModule(); auto arrTy = StaticCast(rawArray.GetResult()->GetType()->GetTypeArgs()[0]); auto length = **(cgMod | rawArray.GetOperand(0)); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // If we already know the length of rawArray is greater than or equal to 0, we can remove the throw branch. if (rawArray.GetOperand(0)->IsLocalVar() && StaticCast(rawArray.GetOperand(0))->GetExpr()->IsConstant()) { auto constExpr = StaticCast(StaticCast(rawArray.GetOperand(0))->GetExpr()); if (constExpr->GetSignedIntLitVal() >= 0) { return irBuilder.AllocateArray(*arrTy, length); } } auto [throwBB, bodyBB] = Vec2Tuple<2>( irBuilder.CreateAndInsertBasicBlocks({GenNameForBB("arr.alloc.throw"), GenNameForBB("arr.alloc.body")})); auto zeroVal = llvm::ConstantInt::get(length->getType(), 0); // Check whether size is greater than zero. auto cmpValid = irBuilder.CreateIntrinsic(llvm::Intrinsic::expect, {irBuilder.getInt1Ty()}, {irBuilder.CreateICmpSGE(length, zeroVal, "arr.alloc.size.valid"), irBuilder.getTrue()}); (void)irBuilder.CreateCondBr(cmpValid, bodyBB, throwBB); irBuilder.SetInsertPoint(throwBB); irBuilder.CreateNegativeArraySizeException(); irBuilder.CreateUnreachable(); irBuilder.SetInsertPoint(bodyBB); return irBuilder.AllocateArray(*arrTy, length); #endif } cangjie_compiler-1.0.7/src/CodeGen/Base/ArrayImpl.h000066400000000000000000000014371510705540100220320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_EMITFUNCTIONIR_H #define CANGJIE_EMITFUNCTIONIR_H #include "llvm/IR/Value.h" namespace Cangjie { namespace CHIR { class RawArrayInitByValue; } namespace CodeGen { class IRBuilder2; class CHIRRawArrayAllocateWrapper; llvm::Value* GenerateRawArrayAllocate(IRBuilder2& irBuilder, const CHIRRawArrayAllocateWrapper& rawArray); llvm::Value* GenerateRawArrayInitByValue(IRBuilder2& irBuilder, const CHIR::RawArrayInitByValue& rawArrayInitByValue); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_TYPECASTIMPL_Hcangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/000077500000000000000000000000001510705540100212725ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGArrayType.cpp000066400000000000000000000131771510705540100241410ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/CGTypes/CGArrayType.h" #include "Base/CGTypes/CGEnumType.h" #include "CGContext.h" #include "CGModule.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/Mangle/CHIRTypeManglingUtils.h" namespace Cangjie::CodeGen { bool CGArrayType::IsRefArray(const CGType& elemType) { if (!elemType.GetSize() || elemType.GetOriginal().IsCPointer() || elemType.GetOriginal().IsCString() || IsCFunc(elemType.GetOriginal())) { return false; } else { return elemType.IsRefType(); } } llvm::StructType* CGArrayType::GenerateRefArrayLayoutType(CGContext& cgCtx) { return GenerateArrayLayoutTypeInfo( cgCtx, ARRAY_LAYOUT_PREFIX + "refArray", llvm::Type::getInt8PtrTy(cgCtx.GetLLVMContext(), 1u)); } llvm::StructType* CGArrayType::GenerateArrayLayoutTypeInfo( CGContext& cgCtx, const std::string& layoutName, llvm::Type* elemType) { auto layoutType = llvm::StructType::getTypeByName(cgCtx.GetLLVMContext(), layoutName); if (layoutType && cgCtx.IsGeneratedStructType(layoutName)) { return layoutType; } else if (!layoutType) { layoutType = llvm::StructType::create(cgCtx.GetLLVMContext(), layoutName); } /** * ArrayLayout has 2 fields. * 1. ARRAY_BASE : ArrayBase Type. * 2. ARRAY_TYPE : [0 x T], T is element type. if Array is ref Array, T is i8 addrspece(1) *. */ constexpr size_t arrayBase{0U}; constexpr size_t arrayType{1U}; constexpr size_t numOfArrayLayout{2U}; std::vector bodyVec(numOfArrayLayout); bodyVec[arrayBase] = CGType::GetArrayBaseType(cgCtx); bodyVec[arrayType] = llvm::ArrayType::get(elemType, 0); SetStructTypeBody(layoutType, bodyVec); cgCtx.AddGeneratedStructType(layoutName); return layoutType; } llvm::Type* CGArrayType::GenerateArrayLayoutType(CGModule& cgMod, const CHIR::RawArrayType& arrTy) { auto& cgCtx = cgMod.GetCGContext(); auto elemType = CGType::GetOrCreate(cgMod, arrTy.GetElementType()); auto isRefArray = IsRefArray(*elemType); if (isRefArray) { return CGArrayType::GenerateRefArrayLayoutType(cgCtx); } auto typeName = CGArrayType::GetTypeNameByArrayType(cgMod, arrTy); auto layoutName = ARRAY_LAYOUT_PREFIX + typeName; return GenerateArrayLayoutTypeInfo(cgCtx, layoutName, elemType->GetLLVMType()); } std::string CGArrayType::GetTypeNameByArrayType(CGModule& cgMod, const CHIR::RawArrayType& arrTy) { std::string prefix = (arrTy.GetDims() > 1) ? ("A" + MangleUtils::DecimalToManglingNumber(std::to_string(arrTy.GetDims()))) : ""; std::string typeName = GetTypeNameByArrayElementType(cgMod, *arrTy.GetElementType()); return prefix + typeName; } std::string CGArrayType::GetTypeNameByArrayElementType(CGModule& cgMod, CHIR::Type& elemType) { auto elementTy = &elemType; if (elemType.IsRef()) { elementTy = StaticCast(&elemType)->GetTypeArgs()[0]; } if (elementTy->IsEnum() && static_cast(CGType::GetOrCreate(cgMod, elementTy))->IsTrivial()) { return "UInt32"; // Enum without argument is presented by UInt32. } if (elemType.IsNominal()) { auto defType = static_cast(elemType).GetCustomTypeDef()->GetType(); if (CGType::GetOrCreate(cgMod, defType)->GetSize()) { return GetTypeQualifiedName(*defType); } } return GetTypeQualifiedName(*elementTy); } llvm::Type* CGArrayType::GenLLVMType() { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvmType = llvm::Type::getInt8Ty(cgCtx.GetLLVMContext()); auto& arrTy = StaticCast(chirType); auto elemType = CGType::GetOrCreate(cgMod, arrTy.GetElementType()); if (!elemType->GetSize()) { return llvmType; } auto isRefArray = IsRefArray(*elemType); if (isRefArray) { layoutType = CGArrayType::GenerateRefArrayLayoutType(cgCtx); return llvmType; } auto typeName = CGArrayType::GetTypeNameByArrayType(cgMod, arrTy); auto layoutName = ARRAY_LAYOUT_PREFIX + typeName; layoutType = CGArrayType::GenerateArrayLayoutTypeInfo(cgCtx, layoutName, elemType->GetLLVMType()); return llvmType; #endif } void CGArrayType::GenContainedCGTypes() { auto elemCHIRType = StaticCast(&chirType)->GetElementType(); (void)containedCGTypes.emplace_back(CGType::GetOrCreate(cgMod, elemCHIRType)); } llvm::Constant* CGArrayType::GenSourceGenericOfTypeInfo() { return CGType::GenSourceGenericOfTypeInfo(); } llvm::Constant* CGArrayType::GenTypeArgsNumOfTypeInfo() { return CGType::GenTypeArgsNumOfTypeInfo(); } llvm::Constant* CGArrayType::GenTypeArgsOfTypeInfo() { return CGType::GenTypeArgsOfTypeInfo(); } llvm::Constant* CGArrayType::GenSuperOfTypeInfo() { auto rawArrayElementType = DeRef(*StaticCast(chirType).GetElementType()); auto ti = CGType::GetOrCreate(cgMod, rawArrayElementType)->GetOrCreateTypeInfo(); return llvm::ConstantExpr::getBitCast(ti, CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext())); } void CGArrayType::CalculateSizeAndAlign() { if (layoutType) { auto arrEleTy = StaticCast(chirType).GetElementType(); size = CGType::GetOrCreate(cgMod, arrEleTy)->GetSize(); align = 1U; } }; } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGArrayType.h000066400000000000000000000035721510705540100236040ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGARRAYTYPE_H #define CANGJIE_CGARRAYTYPE_H #include "Base/CGTypes/CGType.h" #include "cangjie/CHIR/Type/Type.h" namespace Cangjie { namespace CodeGen { class CGArrayType : public CGType { friend class CGTypeMgr; public: CGType* GetElementCGType() const { auto tmp = GetContainedTypes(); CJC_ASSERT(tmp.size() == 1); return tmp[0]; } llvm::Type* GetLayoutType() const { return layoutType; } static llvm::StructType* GenerateArrayLayoutTypeInfo( CGContext& cgCtx, const std::string& layoutName, llvm::Type* elemType); static llvm::Type* GenerateArrayLayoutType(CGModule& cgMod, const CHIR::RawArrayType& arrTy); static bool IsRefArray(const CGType& elemType); static llvm::StructType* GenerateRefArrayLayoutType(CGContext& cgCtx); static std::string GetTypeNameByArrayType(CGModule& cgMod, const CHIR::RawArrayType& arrTy); static std::string GetTypeNameByArrayElementType(CGModule& cgMod, CHIR::Type& elemType); protected: llvm::Type* GenLLVMType() override; void GenContainedCGTypes() override; private: CGArrayType() = delete; explicit CGArrayType(CGModule& cgMod, CGContext& cgCtx, const CHIR::RawArrayType& chirType) : CGType(cgMod, cgCtx, chirType) { } llvm::Constant* GenSourceGenericOfTypeInfo() override; llvm::Constant* GenTypeArgsNumOfTypeInfo() override; llvm::Constant* GenTypeArgsOfTypeInfo() override; llvm::Constant* GenSuperOfTypeInfo() override; void CalculateSizeAndAlign() override; }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGARRAYTYPE_Hcangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGBoxType.cpp000066400000000000000000000021031510705540100235760ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/CGTypes/CGBoxType.h" #include "CGModule.h" namespace Cangjie::CodeGen { CGBoxType::CGBoxType(CGModule& cgMod, CGContext& cgCtx, const CHIR::BoxType& chirType) : CGType(cgMod, cgCtx, chirType, CGTypeKind::CG_REF) { } llvm::Type* CGBoxType::GenLLVMType() { if (llvmType) { return llvmType; } return llvm::Type::getInt8Ty(cgCtx.GetLLVMContext()); } void CGBoxType::GenContainedCGTypes() { auto& boxType = StaticCast(chirType); (void)containedCGTypes.emplace_back(CGType::GetOrCreate(cgMod, boxType.GetBaseType())); } void CGBoxType::CalculateSizeAndAlign() { llvm::DataLayout layOut = cgMod.GetLLVMModule()->getDataLayout(); size = layOut.getTypeAllocSize(llvmType); align = layOut.getABITypeAlignment(llvmType); } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGBoxType.h000066400000000000000000000014351510705540100232520ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGBOXTYPE_H #define CANGJIE_CGBOXTYPE_H #include "Base/CGTypes/CGType.h" namespace Cangjie { namespace CodeGen { class CGBoxType : public CGType { friend class CGTypeMgr; protected: llvm::Type* GenLLVMType() override; void GenContainedCGTypes() override; private: CGBoxType() = delete; explicit CGBoxType(CGModule& cgMod, CGContext& cgCtx, const CHIR::BoxType& chirType); void CalculateSizeAndAlign() override; }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGBOXTYPE_H cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGCPointerType.cpp000066400000000000000000000052111510705540100245740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/CGTypes/CGCPointerType.h" #include "CGModule.h" namespace Cangjie::CodeGen { llvm::Type* CGCPointerType::GenLLVMType() { if (llvmType) { return llvmType; } auto& llvmCtx = cgCtx.GetLLVMContext(); llvmType = llvm::Type::getInt8PtrTy(llvmCtx); layoutType = llvm::StructType::getTypeByName(llvmCtx, "CPointer.Type"); if (!layoutType) { layoutType = llvm::StructType::create(llvmCtx, {llvmType}, "CPointer.Type"); } return llvmType; } void CGCPointerType::GenContainedCGTypes() { CJC_ASSERT(chirType.GetTypeArgs().size() == 1); containedCGTypes.emplace_back(CGType::GetInt8CGType(cgMod)); } llvm::Constant* CGCPointerType::GenSuperOfTypeInfo() { auto cpointerElementType = DeRef(*static_cast(chirType).GetElementType()); auto ti = CGType::GetOrCreate(cgMod, cpointerElementType)->GetOrCreateTypeInfo(); return llvm::ConstantExpr::getBitCast(ti, CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext())); } llvm::Constant* CGCPointerType::GenTypeArgsNumOfTypeInfo() { return llvm::ConstantInt::get(llvm::Type::getInt8Ty(cgMod.GetLLVMContext()), 1U); } llvm::Constant* CGCPointerType::GenTypeArgsOfTypeInfo() { auto genericArg = StaticCast(chirType).GetElementType(); auto typeInfoPtrTy = CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext()); auto p0i8 = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); std::vector constants{CGType::GetOrCreate(cgMod, DeRef(*genericArg))->GetOrCreateTypeInfo()}; auto typeOfGenericArgsGV = llvm::ArrayType::get(typeInfoPtrTy, constants.size()); auto typeInfoOfGenericArgs = llvm::cast(cgMod.GetLLVMModule()->getOrInsertGlobal( CGType::GetNameOfTypeInfoGV(chirType) + ".typeArgs", typeOfGenericArgsGV)); typeInfoOfGenericArgs->setInitializer(llvm::ConstantArray::get(typeOfGenericArgsGV, constants)); typeInfoOfGenericArgs->setLinkage(llvm::GlobalValue::LinkageTypes::PrivateLinkage); typeInfoOfGenericArgs->addAttribute(CJTI_TYPE_ARGS_ATTR); return llvm::ConstantExpr::getBitCast(typeInfoOfGenericArgs, p0i8); } void CGCPointerType::CalculateSizeAndAlign() { llvm::DataLayout layOut = cgMod.GetLLVMModule()->getDataLayout(); size = layOut.getTypeAllocSize(llvmType); align = layOut.getABITypeAlignment(llvmType); } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGCPointerType.h000066400000000000000000000020301510705540100242350ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGCPOINTERTYPE_H #define CANGJIE_CGCPOINTERTYPE_H #include "Base/CGTypes/CGType.h" namespace Cangjie { namespace CodeGen { class CGCPointerType : public CGType { friend class CGTypeMgr; protected: llvm::Type* GenLLVMType() override; void GenContainedCGTypes() override; private: CGCPointerType() = delete; explicit CGCPointerType(CGModule& cgMod, CGContext& cgCtx, const CHIR::CPointerType& chirType) : CGType(cgMod, cgCtx, chirType) { } llvm::Constant* GenSuperOfTypeInfo() override; llvm::Constant* GenTypeArgsNumOfTypeInfo() override; llvm::Constant* GenTypeArgsOfTypeInfo() override; void CalculateSizeAndAlign() override; }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGCPOINTERTYPE_H cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGCStringType.cpp000066400000000000000000000020001510705540100244130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/CGTypes/CGCStringType.h" #include "CGModule.h" #include "CGContext.h" namespace Cangjie::CodeGen { llvm::Type* CGCStringType::GenLLVMType() { if (llvmType) { return llvmType; } auto& llvmCtx = cgCtx.GetLLVMContext(); llvmType = llvm::Type::getInt8PtrTy(llvmCtx); layoutType = llvm::StructType::getTypeByName(llvmCtx, "CString.Type"); if (!layoutType) { layoutType = llvm::StructType::create(llvmCtx, {llvmType}, "CString.Type"); } return llvmType; } void CGCStringType::CalculateSizeAndAlign() { llvm::DataLayout layOut = cgMod.GetLLVMModule()->getDataLayout(); size = layOut.getTypeAllocSize(llvmType); align = layOut.getABITypeAlignment(llvmType); } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGCStringType.h000066400000000000000000000020701510705540100240670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGCSTRINGTYPE_H #define CANGJIE_CGCSTRINGTYPE_H #include "Base/CGTypes/CGType.h" #include "CGContext.h" #include "cangjie/CHIR/Type/Type.h" namespace Cangjie { namespace CodeGen { class CGCStringType : public CGType { friend class CGTypeMgr; protected: llvm::Type* GenLLVMType() override; void GenContainedCGTypes() override { containedCGTypes.emplace_back(CGType::GetInt8CGType(cgMod)); } private: CGCStringType() = delete; explicit CGCStringType(CGModule& cgMod, CGContext& cgCtx, const CHIR::Type& chirType) : CGType(cgMod, cgCtx, chirType) { CJC_ASSERT(chirType.GetTypeKind() == CHIR::Type::TYPE_CSTRING); } void CalculateSizeAndAlign() override; }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGCSTRINGTYPE_Hcangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGClassType.h000066400000000000000000000026211510705540100235650ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGCLASSTYPE_H #define CANGJIE_CGCLASSTYPE_H #include "Base/CGTypes/CGCustomType.h" #include "cangjie/CHIR/Type/Type.h" namespace Cangjie { namespace CodeGen { class CGClassType : public CGCustomType { friend class CGTypeMgr; public: llvm::StructType* GetLayoutType() const { return layoutType; } size_t GetNumOfAllFields() const { return numOfAllFields; } protected: llvm::Type* GenLLVMType() override; void GenContainedCGTypes() override; llvm::Constant* GenSuperFnOfTypeTemplate() override; llvm::Constant* GenFinalizerOfTypeTemplate() override; void PreActionOfGenTypeInfo() override; void PreActionOfGenTypeTemplate() override; private: CGClassType() = delete; explicit CGClassType(CGModule& cgMod, CGContext& cgCtx, const CHIR::ClassType& chirType); llvm::Constant* GenSuperOfTypeInfo() override; llvm::Constant* GenSourceGenericOfTypeInfo() override; void CalculateSizeAndAlign() override; private: size_t numOfAllFields{0}; // including the fields inherited }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGCLASSTYPE_H cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGCustomType.cpp000066400000000000000000000350351510705540100243320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/CGTypes/CGCustomType.h" #include "Base/CGTypes/CGEnumType.h" #include "CGModule.h" #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "CJNative/CGTypeInfo.h" #endif #include "DIBuilder.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/CHIRCasting.h" namespace Cangjie { namespace CodeGen { CGCustomType::CGCustomType(CGModule& cgMod, CGContext& cgCtx, const CHIR::Type& chirType, CGTypeKind cgTypeKind) : CGType(cgMod, cgCtx, chirType, cgTypeKind) { } llvm::Constant* CGCustomType::GenFieldsNumOfTypeInfo() { auto& customType = StaticCast(chirType); auto& nonConstCustomType = const_cast(customType); auto fieldsNum = nonConstCustomType.GetInstantiatedMemberTys(cgMod.GetCGContext().GetCHIRBuilder()).size() + (chirType.IsAutoEnv() ? 2U : 0U); CJC_ASSERT(layoutType->getNumElements() == fieldsNum); return llvm::ConstantInt::get(llvm::Type::getInt16Ty(cgMod.GetLLVMContext()), fieldsNum); } std::vector CGCustomType::GenTypeInfoConstantVectorForTypes( CGModule& cgMod, const std::vector& chirTypes) { std::vector fieldConstants; for (auto type : chirTypes) { auto derefType = DeRef(*type); (void)fieldConstants.emplace_back(CGType::GetOrCreate(cgMod, derefType)->GetOrCreateTypeInfo()); } return fieldConstants; } llvm::Constant* CGCustomType::GenTypeInfoArray( CGModule& cgMod, std::string name, std::vector constants) { auto p0i8 = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); if (constants.empty()) { return llvm::Constant::getNullValue(p0i8); } auto typeInfoPtrTy = CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext()); auto typeOfFieldsGV = llvm::ArrayType::get(typeInfoPtrTy, constants.size()); auto typeInfoOfFields = llvm::cast(cgMod.GetLLVMModule()->getOrInsertGlobal(name, typeOfFieldsGV)); typeInfoOfFields->setInitializer(llvm::ConstantArray::get(typeOfFieldsGV, constants)); typeInfoOfFields->setLinkage(llvm::GlobalValue::LinkageTypes::PrivateLinkage); typeInfoOfFields->addAttribute(CJTI_FIELDS_ATTR); return llvm::ConstantExpr::getBitCast(typeInfoOfFields, p0i8); } llvm::Constant* CGCustomType::GenFieldsOfTypeInfo() { CJC_ASSERT(!chirType.IsEnum() && "CGEnumType should override this method."); auto& customType = StaticCast(chirType); auto& nonConstCustomType = const_cast(customType); auto instanceMemberTypes = nonConstCustomType.GetInstantiatedMemberTys(cgMod.GetCGContext().GetCHIRBuilder()); std::vector fieldConstants = GenTypeInfoConstantVectorForTypes(cgMod, instanceMemberTypes); if (chirType.IsAutoEnv()) { CJC_ASSERT(!chirType.IsAutoEnvInstBase()); (void)fieldConstants.insert(fieldConstants.begin(), CGType::GetInt64CGType(cgMod)->GetOrCreateTypeInfo()); (void)fieldConstants.insert(fieldConstants.begin(), CGType::GetInt64CGType(cgMod)->GetOrCreateTypeInfo()); } return GenTypeInfoArray(cgMod, CGType::GetNameOfTypeInfoGV(chirType) + ".fields", fieldConstants); } llvm::Constant* CGCustomType::GenOffsetsArray(CGModule& cgMod, std::string name, llvm::StructType* layoutType) { auto i32Ty = llvm::Type::getInt32Ty(cgMod.GetLLVMContext()); if (layoutType->elements().empty()) { return llvm::ConstantPointerNull::get(i32Ty->getPointerTo()); } auto arrLength = layoutType->getNumElements(); auto i32ArrType = llvm::ArrayType::get(i32Ty, arrLength); std::vector elements(arrLength); auto structLayout = cgMod.GetLLVMModule()->getDataLayout().getStructLayout(layoutType); for (size_t idx = 0; idx < arrLength; ++idx) { elements[idx] = llvm::ConstantInt::get(i32Ty, structLayout->getElementOffset(idx)); } auto typeInfoOfFields = llvm::cast(cgMod.GetLLVMModule()->getOrInsertGlobal(name, i32ArrType)); typeInfoOfFields->setInitializer(llvm::ConstantArray::get(i32ArrType, elements)); typeInfoOfFields->setLinkage(llvm::GlobalValue::LinkageTypes::PrivateLinkage); typeInfoOfFields->addAttribute(CJTI_OFFSETS_ATTR); return llvm::ConstantExpr::getBitCast(typeInfoOfFields, i32Ty->getPointerTo()); } llvm::Constant* CGCustomType::GenOffsetsOfTypeInfo() { CJC_NULLPTR_CHECK(layoutType); return GenOffsetsArray(cgMod, CGType::GetNameOfTypeInfoGV(chirType) + ".offsets", layoutType); } llvm::Constant* CGCustomType::GenSourceGenericOfTypeInfo() { return CGType::GenSourceGenericOfTypeInfo(); } llvm::Constant* CGCustomType::GenTypeArgsNumOfTypeInfo() { if (chirType.IsAutoEnvBase()) { CJC_ASSERT(!chirType.IsAutoEnvInstBase()); return llvm::ConstantInt::get(llvm::Type::getInt8Ty(cgMod.GetLLVMContext()), 1U); } else { return llvm::ConstantInt::get(llvm::Type::getInt8Ty(cgMod.GetLLVMContext()), StaticCast(chirType).GetGenericArgs().size()); } } llvm::Constant* CGCustomType::GenTypeArgsOfTypeInfo() { auto genericArgs = StaticCast(chirType).GetGenericArgs(); auto p0i8 = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); if (genericArgs.empty()) { return llvm::ConstantPointerNull::get(p0i8); } std::vector constants; if (chirType.IsAutoEnvBase()) { CJC_ASSERT(!chirType.IsAutoEnvInstBase()); std::vector paramTypes(genericArgs.begin(), genericArgs.end() - 1); auto chirFuncType = cgCtx.GetCHIRBuilder().GetType(paramTypes, genericArgs.back()); auto cgTypeOfTypeArg = CGType::GetOrCreate(cgMod, chirFuncType); auto it = constants.emplace_back(cgTypeOfTypeArg->GetOrCreateTypeInfo()); if (cgTypeOfTypeArg->IsStaticGI()) { cgCtx.AddDependentPartialOrderOfTypes(it, this->typeInfo); } } else { for (auto typeArg : genericArgs) { auto cgTypeOfTypeArg = CGType::GetOrCreate(cgMod, DeRef(*typeArg)); auto it = constants.emplace_back(cgTypeOfTypeArg->GetOrCreateTypeInfo()); if (cgTypeOfTypeArg->IsStaticGI()) { cgCtx.AddDependentPartialOrderOfTypes(it, this->typeInfo); } } } auto typeInfoPtrTy = CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext()); auto typeOfGenericArgsGV = llvm::ArrayType::get(typeInfoPtrTy, constants.size()); auto typeInfoOfGenericArgs = llvm::cast(cgMod.GetLLVMModule()->getOrInsertGlobal( CGType::GetNameOfTypeInfoGV(chirType) + ".typeArgs", typeOfGenericArgsGV)); typeInfoOfGenericArgs->setInitializer(llvm::ConstantArray::get(typeOfGenericArgsGV, constants)); typeInfoOfGenericArgs->setLinkage(llvm::GlobalValue::LinkageTypes::PrivateLinkage); typeInfoOfGenericArgs->addAttribute(CJTI_TYPE_ARGS_ATTR); return llvm::ConstantExpr::getBitCast(typeInfoOfGenericArgs, p0i8); } llvm::Constant* CGCustomType::GenNameOfTypeTemplate() { auto& customType = static_cast(chirType); auto customDefShortName = CHIR::GetCustomTypeIdentifier(customType); auto customDef = customType.GetCustomTypeDef(); return cgMod.GenerateTypeNameConstantString( (chirType.IsAutoEnvBase() ? "" : (customDef->GetPackageName() + ":")) + customDefShortName, false); } llvm::Constant* CGCustomType::GenFieldsNumOfTypeTemplate() { CJC_ASSERT(!chirType.IsEnum() && "CGEnumType should override this method."); auto& customType = static_cast(chirType); auto& nonConstCustomType = const_cast(customType); auto fieldsNum = nonConstCustomType.GetInstantiatedMemberTys(cgMod.GetCGContext().GetCHIRBuilder()).size(); if (chirType.IsAutoEnv()) { CJC_ASSERT(!chirType.IsAutoEnvBase()); fieldsNum += 2U; } return llvm::ConstantInt::get(llvm::Type::getInt16Ty(cgMod.GetLLVMContext()), fieldsNum); } llvm::Constant* CGCustomType::GenFieldsFnsOfTypeTemplate() { auto customTypeDef = static_cast(chirType).GetCustomTypeDef(); std::unordered_map localGenericParamIndicesMap; size_t gTIdx = 0; for (auto chirGT : customTypeDef->GetGenericTypeParams()) { localGenericParamIndicesMap.emplace(chirGT, gTIdx); ++gTIdx; } CJC_ASSERT(!customTypeDef->IsExtend()); auto fieldTypes = StaticCast( customTypeDef->GetType())->GetInstantiatedMemberTys(cgMod.GetCGContext().GetCHIRBuilder()); if (chirType.IsAutoEnv()) { auto chirInt64Type = const_cast(&CGType::GetInt64CGType(cgMod)->GetOriginal()); fieldTypes.insert(fieldTypes.begin(), chirInt64Type); fieldTypes.insert(fieldTypes.begin(), chirInt64Type); } return CGTypeInfo::GenFieldsFnsOfTypeTemplate( cgMod, CGType::GetNameOfTypeTemplateGV(chirType), fieldTypes, localGenericParamIndicesMap); } llvm::Constant* CGCustomType::GenSuperFnOfTypeTemplate() { // classType will override this function auto i8PtrTy = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); return llvm::ConstantPointerNull::get(i8PtrTy); } llvm::Constant* CGCustomType::GenFinalizerOfTypeTemplate() { // classType will override this function auto i8PtrTy = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); return llvm::ConstantPointerNull::get(i8PtrTy); } llvm::Constant* CGCustomType::GenKindOfTypeTemplate() { static const std::unordered_map CHIR_TYPE2TYPE_INFO_KIND = { // reference type {CHIR::Type::TypeKind::TYPE_RAWARRAY, UGTypeKind::UG_RAWARRAY}, {CHIR::Type::TypeKind::TYPE_FUNC, UGTypeKind::UG_CFUNC}, // value type {CHIR::Type::TypeKind::TYPE_TUPLE, UGTypeKind::UG_TUPLE}, {CHIR::Type::TypeKind::TYPE_STRUCT, UGTypeKind::UG_STRUCT}, {CHIR::Type::TypeKind::TYPE_ENUM, UGTypeKind::UG_ENUM}, {CHIR::Type::TypeKind::TYPE_VARRAY, UGTypeKind::UG_VARRAY}, {CHIR::Type::TypeKind::TYPE_CPOINTER, UGTypeKind::UG_CPOINTER}}; unsigned typeInfoKind; if (chirType.IsAutoEnvBase()) { typeInfoKind = UGTypeKind::UG_FUNC; } else if (chirType.IsClass()) { auto classDef = StaticCast(chirType).GetClassDef(); if (IsWeakRefClass(*classDef)) { typeInfoKind = UGTypeKind::UG_WEAKREF; } else { typeInfoKind = classDef->IsClass() ? UGTypeKind::UG_CLASS : UGTypeKind::UG_INTERFACE; } } else if (chirType.IsEnum()) { typeInfoKind = static_cast(this)->IsCommonEnum() ? UGTypeKind::UG_COMMON_ENUM : UGTypeKind::UG_ENUM; } else { typeInfoKind = CHIR_TYPE2TYPE_INFO_KIND.at(chirType.GetTypeKind()); } return llvm::ConstantInt::get(llvm::Type::getInt8Ty(cgMod.GetLLVMContext()), typeInfoKind); } llvm::Constant* CGCustomType::GenTypeArgsNumOfTypeTemplate() { return llvm::ConstantInt::get(llvm::Type::getInt16Ty(cgMod.GetLLVMContext()), StaticCast(chirType).GetGenericArgs().size()); } void CGCustomType::GenTypeTemplate() { auto& customType = StaticCast(chirType); auto customTypeDef = customType.GetCustomTypeDef(); CJC_ASSERT(customTypeDef->IsGenericDef() && "Should be a generic type here."); CJC_ASSERT(typeTemplate); PreActionOfGenTypeTemplate(); if (typeTemplate->hasInitializer()) { return; } if (cgMod.GetCGContext().IsCustomTypeOfOtherLLVMModule(customType) || chirType.IsAutoEnvBase()) { if (customTypeDef->Get() == Linkage::EXTERNAL_WEAK) { typeTemplate->setLinkage(llvm::GlobalValue::ExternalWeakLinkage); } return; } auto& llvmCtx = cgMod.GetLLVMContext(); auto i8Ty = llvm::Type::getInt8Ty(llvmCtx); std::vector typeTemplateVec(TYPE_TEMPLATE_FIELDS_NUM); typeTemplateVec[static_cast(TYPETEMPLATE_NAME)] = GenNameOfTypeTemplate(); typeTemplateVec[static_cast(TYPETEMPLATE_TYPE_KIND)] = GenKindOfTypeTemplate(); typeTemplateVec[static_cast(TYPETEMPLATE_TYPE_ARGS_NUM)] = GenTypeArgsNumOfTypeTemplate(); typeTemplateVec[static_cast(TYPETEMPLATE_FIELDS_NUM)] = GenFieldsNumOfTypeTemplate(); typeTemplateVec[static_cast(TYPETEMPLATE_FIELDS_FNS)] = GenFieldsFnsOfTypeTemplate(); typeTemplateVec[static_cast(TYPETEMPLATE_SUPER_FN)] = GenSuperFnOfTypeTemplate(); typeTemplateVec[static_cast(TYPETEMPLATE_FINALIZER)] = GenFinalizerOfTypeTemplate(); typeTemplateVec[static_cast(TYPETEMPLATE_REFLECTION)] = GenReflectionOfTypeInfo(); typeTemplateVec[static_cast(TYPETEMPLATE_FLAG)] = llvm::ConstantInt::get(i8Ty, 0); typeTemplateVec[static_cast(TYPETEMPLATE_EXTENSIONDEF_PTR)] = GenExtensionDefPtrOfTypeInfo(); typeTemplateVec[static_cast(TYPETEMPLATE_INHERITED_CLASS_NUM)] = GenInheritedClassNumOfTypeInfo(); typeTemplate->setInitializer( llvm::ConstantStruct::get(CGType::GetOrCreateTypeTemplateType(llvmCtx), typeTemplateVec)); typeTemplate->addAttribute(TYPE_TEMPLATE_ATTR); llvm::GlobalValue::LinkageTypes linkageType = CHIRLinkage2LLVMLinkage(customTypeDef->Get()); if (linkageType == llvm::GlobalValue::InternalLinkage) { linkageType = llvm::GlobalValue::PrivateLinkage; } AddLinkageTypeMetadata(*typeTemplate, linkageType, cgMod.GetCGContext().IsCGParallelEnabled()); cgMod.diBuilder->CreateAnonymousTypeForGenericType(typeTemplate, chirType); PostActionOfGenTypeTemplate(); } bool CGCustomType::IsSized() const { auto& customType = static_cast(chirType); auto& nonConstCustomType = const_cast(customType); const auto memberVars = nonConstCustomType.GetInstantiatedMemberTys(cgMod.GetCGContext().GetCHIRBuilder()); for (auto memberVar : memberVars) { if (memberVar->IsRef() || memberVar->IsCPointer() || memberVar->IsCFunc()) { continue; } if (!CGType::GetOrCreate(cgMod, memberVar)->GetSize()) { return false; } } return true; } llvm::Constant* CGCustomType::GenReflectionOfTypeInfo() { return llvm::ConstantPointerNull::get(llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext())); } } // namespace CodeGen } // namespace Cangjie cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGCustomType.h000066400000000000000000000037261510705540100240010ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGCUSTOMTYPE_H #define CANGJIE_CGCUSTOMTYPE_H #include "Base/CGTypes/CGType.h" namespace Cangjie { namespace CodeGen { class CGCustomType : public CGType { public: static std::vector GenTypeInfoConstantVectorForTypes( CGModule& cgMod, const std::vector& chirTypes); static llvm::Constant* GenTypeInfoArray(CGModule& cgMod, std::string name, std::vector constants); static llvm::Constant* GenOffsetsArray(CGModule& cgMod, std::string name, llvm::StructType* layoutType); protected: CGCustomType( CGModule& cgMod, CGContext& cgCtx, const CHIR::Type& chirType, CGTypeKind cgTypeKind = CGTypeKind::OTHERS); llvm::Constant* GenFieldsNumOfTypeInfo() override; llvm::Constant* GenFieldsOfTypeInfo() override; llvm::Constant* GenOffsetsOfTypeInfo() override; llvm::Constant* GenSourceGenericOfTypeInfo() override; llvm::Constant* GenTypeArgsNumOfTypeInfo() override; llvm::Constant* GenTypeArgsOfTypeInfo() override; llvm::Constant* GenReflectionOfTypeInfo() override; llvm::Constant* GenNameOfTypeTemplate(); llvm::Constant* GenKindOfTypeTemplate(); llvm::Constant* GenTypeArgsNumOfTypeTemplate(); bool IsSized() const; virtual void PreActionOfGenTypeTemplate() {} virtual void PostActionOfGenTypeTemplate() {} virtual llvm::Constant* GenFieldsNumOfTypeTemplate(); virtual llvm::Constant* GenFieldsFnsOfTypeTemplate(); virtual llvm::Constant* GenSuperFnOfTypeTemplate(); virtual llvm::Constant* GenFinalizerOfTypeTemplate(); private: CGCustomType() = delete; void GenTypeTemplate() override; }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGCUSTOMTYPE_Hcangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGEnumType.cpp000066400000000000000000000565071510705540100237730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/CGTypes/CGEnumType.h" #include "CGContext.h" #include "CGModule.h" #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "CJNative/CGTypeInfo.h" #endif #include "Utils/CGUtils.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/Option/Option.h" namespace Cangjie::CodeGen { namespace { bool ContainsRefType(const llvm::Type& llvmType) { if (llvmType.isPointerTy() && llvmType.getPointerAddressSpace() == 1U) { return true; } else if (auto structType = llvm::dyn_cast(&llvmType); structType) { for (auto& elementType : structType->elements()) { if (ContainsRefType(*elementType)) { return true; } } } return false; } // check if enum has RefTypeAssociatedValue and get the size of constructor union. std::tuple> ObtainingAuxiliaryInformation( CGModule& cgMod, const CHIR::EnumType& chirEnumType) { bool hasRefTypeAssociatedValue = false; std::optional sizeOfConstructorUnion = std::nullopt; const auto& ctors = chirEnumType.GetConstructorInfos(cgMod.GetCGContext().GetCHIRBuilder()); CJC_ASSERT(!ctors.empty() && "The enum type has at least one constructor."); for (auto ctor : ctors) { const auto& paramTypes = ctor.funcType->GetParamTypes(); std::optional sizeOfConstructorUnionTemp = paramTypes.empty() ? std::optional(0U) : std::nullopt; for (auto associatedValueCHIRType : paramTypes) { auto associatedValueCGType = CGType::GetOrCreate(cgMod, associatedValueCHIRType); hasRefTypeAssociatedValue |= ContainsRefType(*associatedValueCGType->GetLLVMType()); auto associatedValueSize = associatedValueCGType->GetSize(); if (associatedValueSize.has_value()) { sizeOfConstructorUnionTemp = sizeOfConstructorUnionTemp.value_or(0U) + associatedValueSize.value(); } else { sizeOfConstructorUnionTemp = std::nullopt; break; } } if (sizeOfConstructorUnionTemp.has_value()) { sizeOfConstructorUnion = sizeOfConstructorUnion.value_or(0U) < sizeOfConstructorUnionTemp.value() ? sizeOfConstructorUnionTemp.value() : sizeOfConstructorUnion.value_or(0U); } else { sizeOfConstructorUnion = std::nullopt; break; } } return std::make_tuple(hasRefTypeAssociatedValue, sizeOfConstructorUnion); } } CGEnumType::CGEnumType(CGModule& cgMod, CGContext& cgCtx, const CHIR::Type& chirType) : CGCustomType(cgMod, cgCtx, chirType, CGTypeKind::CG_ENUM), chirEnumType(StaticCast(chirType)), optionLikeInfo(nullptr), sizeOfConstructorUnion(std::nullopt), cgEnumTypeKind(CalculateCGEnumTypeKind()) { } CGEnumType::CGEnumTypeKind CGEnumType::CalculateCGEnumTypeKind() { // 1. check if enum is non_exhaustive if (!chirEnumType.GetEnumDef()->IsExhaustive()) { return chirEnumType.GetEnumDef()->IsAllCtorsTrivial() ? CGEnumTypeKind::NON_EXHAUSTIVE_UNASSOCIATED : CGEnumTypeKind::NON_EXHAUSTIVE_ASSOCIATED; } // 2. check if enum has RefTypeAssociatedValue and get the size of constructor union. auto [hasRefTypeAssociatedValue, sizeOfConstructorUnionTemp] = ObtainingAuxiliaryInformation(cgMod, chirEnumType); sizeOfConstructorUnion = sizeOfConstructorUnionTemp; // 3. determine other scenarios. const auto& ctors = chirEnumType.GetConstructorInfos(cgMod.GetCGContext().GetCHIRBuilder()); if (ctors.size() == 1U && sizeOfConstructorUnion.has_value() && sizeOfConstructorUnion.value() == 0U && chirEnumType.GetGenericArgs().empty()) { return CGEnumTypeKind::EXHAUSTIVE_ZERO_SIZE; } if (chirEnumType.GetEnumDef()->IsAllCtorsTrivial()) { return CGEnumTypeKind::EXHAUSTIVE_UNASSOCIATED; } if (ctors.size() == 2U && // OptionLike always has two constructors ((ctors[0].funcType->GetParamTypes().size() == 0U && ctors[1].funcType->GetParamTypes().size() == 1U) || (ctors[0].funcType->GetParamTypes().size() == 1U && ctors[1].funcType->GetParamTypes().size() == 0U))) { bool isAntiOptionLike = ctors[0].funcType->GetParamTypes().empty(); CHIR::Type* associatedValueType = isAntiOptionLike ? ctors[1].funcType->GetParamTypes()[0] : ctors[0].funcType->GetParamTypes()[0]; optionLikeInfo = std::make_unique(isAntiOptionLike, associatedValueType); if (associatedValueType->IsGeneric()) { return CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_T; } auto baseTypeOfAssociatedValue = DeRef(*associatedValueType); if (baseTypeOfAssociatedValue->IsEnum() && StaticCast(CGType::GetOrCreate(cgMod, baseTypeOfAssociatedValue))->IsCommonEnum()) { return CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_REF; } if (baseTypeOfAssociatedValue->IsReferenceType()) { return CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_REF; } else { return CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_NONREF; } } if (!hasRefTypeAssociatedValue && chirEnumType.GetGenericArgs().empty()) { CJC_ASSERT(sizeOfConstructorUnion.has_value()); return CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_NONREF; } return CGEnumTypeKind::EXHAUSTIVE_OTHER; } std::string CGEnumType::GetEnumTypeName() const { switch (cgEnumTypeKind) { case CGEnumTypeKind::NON_EXHAUSTIVE_UNASSOCIATED: case CGEnumTypeKind::EXHAUSTIVE_UNASSOCIATED: { return ENUM_TYPE_PREFIX + "Trivial"; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_T: { return ENUM_TYPE_PREFIX + chirEnumType.GetEnumDef()->GetIdentifierWithoutPrefix() + ""; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_REF: { return ENUM_TYPE_PREFIX + "OptionLike.Ref"; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_NONREF: { return ENUM_TYPE_PREFIX + GetTypeQualifiedName(chirEnumType); } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_NONREF: { return ENUM_TYPE_PREFIX + GetTypeQualifiedName(chirEnumType); } case CGEnumTypeKind::NON_EXHAUSTIVE_ASSOCIATED: case CGEnumTypeKind::EXHAUSTIVE_OTHER: { return ENUM_TYPE_PREFIX + "Common"; } case CGEnumTypeKind::EXHAUSTIVE_ZERO_SIZE: { return ENUM_TYPE_PREFIX + "Empty"; } default: CJC_ASSERT(false && "Should not reach here: UNKNOWN enum kind"); return ""; } } llvm::Type* CGEnumType::GenLLVMType() { if (llvmType) { return llvmType; } switch (cgEnumTypeKind) { case CGEnumTypeKind::NON_EXHAUSTIVE_UNASSOCIATED: case CGEnumTypeKind::EXHAUSTIVE_UNASSOCIATED: { return GenTrivialLLVMType(); } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_NONREF: { return GenAssociatedNonRefEnumLLVMType(); } case CGEnumTypeKind::NON_EXHAUSTIVE_ASSOCIATED: case CGEnumTypeKind::EXHAUSTIVE_OTHER: { return GenCommonEnumLLVMType(); } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_T: { return llvm::Type::getInt8Ty(cgCtx.GetLLVMContext()); } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_REF: { return GenOptionLikeRefLLVMType(); } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_NONREF: { return GenOptionLikeNonRefLLVMType(); } case CGEnumTypeKind::EXHAUSTIVE_ZERO_SIZE: { return GenZeroSizeEnumLLVMType(); } default: { CJC_ASSERT(false && "Should not reach here: UNKNOWN enum kind"); return nullptr; } } } llvm::Type* CGEnumType::GenTrivialLLVMType() { auto& llvmCtx = cgCtx.GetLLVMContext(); const auto& typeName = GetEnumTypeName(); layoutType = llvm::StructType::getTypeByName(llvmCtx, typeName); if (!layoutType) { layoutType = llvm::StructType::create(llvmCtx, typeName); SetStructTypeBody(layoutType, {llvm::Type::getInt32Ty(llvmCtx)}); } return llvm::Type::getInt32Ty(llvmCtx); } llvm::Type* CGEnumType::GenCommonEnumLLVMType() { auto& llvmCtx = cgCtx.GetLLVMContext(); const auto& typeName = GetEnumTypeName(); layoutType = llvm::StructType::getTypeByName(llvmCtx, typeName); if (!layoutType) { layoutType = llvm::StructType::create(llvmCtx, typeName); SetStructTypeBody(layoutType, {llvm::Type::getInt8PtrTy(llvmCtx, 1U)}); } return GetRefType(llvmCtx); } llvm::Type* CGEnumType::GenZeroSizeEnumLLVMType() { auto& llvmCtx = cgCtx.GetLLVMContext(); const auto& typeName = GetEnumTypeName(); layoutType = llvm::StructType::getTypeByName(llvmCtx, typeName); if (!layoutType) { layoutType = llvm::StructType::create(llvmCtx, {}, typeName); } return layoutType; } llvm::Type* CGEnumType::GenOptionLikeRefLLVMType() { auto& llvmCtx = cgCtx.GetLLVMContext(); const auto& typeName = GetEnumTypeName(); layoutType = llvm::StructType::getTypeByName(llvmCtx, typeName); if (!layoutType) { layoutType = llvm::StructType::create(llvmCtx, typeName); auto i8Ty = llvm::Type::getInt8Ty(llvmCtx); SetStructTypeBody(layoutType, {llvm::Type::getInt8PtrTy(llvmCtx, 1U), llvm::ArrayType::get(i8Ty, 0U)}); } cgCtx.AddGeneratedStructType(layoutType->getName().str()); return GetRefType(llvmCtx); } llvm::Type* CGEnumType::GenOptionLikeNonRefLLVMType() { CJC_NULLPTR_CHECK(optionLikeInfo); auto elemCGType = CGType::GetOrCreate(cgMod, optionLikeInfo->associatedValueType); auto& llvmCtx = cgCtx.GetLLVMContext(); if (!elemCGType->GetSize()) { return llvm::Type::getInt8Ty(llvmCtx); } const auto& enumTypeName = GetEnumTypeName(); layoutType = llvm::StructType::getTypeByName(llvmCtx, enumTypeName); llvmType = layoutType; if (llvmType && cgCtx.IsGeneratedStructType(enumTypeName)) { return llvmType; } else if (!llvmType) { layoutType = llvm::StructType::create(llvmCtx, enumTypeName); llvmType = layoutType; } cgCtx.AddGeneratedStructType(enumTypeName); std::vector elemTypes{}; elemTypes.emplace_back(llvm::Type::getInt1Ty(llvmCtx)); elemTypes.emplace_back(elemCGType->GetLLVMType()); SetStructTypeBody(llvm::cast(llvmType), elemTypes); return llvmType; } llvm::Type* CGEnumType::GenAssociatedNonRefEnumLLVMType() { auto& llvmCtx = cgCtx.GetLLVMContext(); CJC_ASSERT(sizeOfConstructorUnion.has_value()); llvmType = llvm::ArrayType::get(llvm::Type::getInt8Ty(llvmCtx), 4U + sizeOfConstructorUnion.value()); // 4U: size of int32 const auto& enumTypeName = GetEnumTypeName(); layoutType = llvm::StructType::getTypeByName(llvmCtx, enumTypeName); if (layoutType && cgCtx.IsGeneratedStructType(enumTypeName)) { return llvmType; } else if (!layoutType) { layoutType = llvm::StructType::create(llvmCtx, enumTypeName); } cgCtx.AddGeneratedStructType(enumTypeName); SetStructTypeBody(layoutType, std::vector{llvmType}); return llvmType; } void CGEnumType::GenContainedCGTypes() { switch (cgEnumTypeKind) { case CGEnumTypeKind::NON_EXHAUSTIVE_ASSOCIATED: case CGEnumTypeKind::EXHAUSTIVE_OTHER: { containedCGTypes.emplace_back(CGType::GetInt32CGType(cgMod)); break; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_NONREF: { break; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_T: { // Can't be determined at compile time. break; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_REF: { CJC_NULLPTR_CHECK(optionLikeInfo); containedCGTypes.emplace_back(CGType::GetOrCreate(cgMod, optionLikeInfo->associatedValueType)); break; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_NONREF: { CJC_NULLPTR_CHECK(optionLikeInfo); containedCGTypes.emplace_back(CGType::GetBoolCGType(cgMod)); containedCGTypes.emplace_back(CGType::GetOrCreate(cgMod, optionLikeInfo->associatedValueType)); break; } default: { CJC_ASSERT(false && "Should not reach here: UNKNOWN enum kind"); break; } } } void CGEnumType::CalculateSizeAndAlign() { llvm::DataLayout layOut = cgMod.GetLLVMModule()->getDataLayout(); switch (cgEnumTypeKind) { case CGEnumTypeKind::NON_EXHAUSTIVE_UNASSOCIATED: case CGEnumTypeKind::EXHAUSTIVE_UNASSOCIATED: { size = llvmType->getPrimitiveSizeInBits() / 8U; align = size; return; } case CGEnumTypeKind::NON_EXHAUSTIVE_ASSOCIATED: case CGEnumTypeKind::EXHAUSTIVE_OTHER: case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_REF: { size = layOut.getTypeAllocSize(llvmType); align = layOut.getABITypeAlignment(llvmType); return; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_NONREF: { CJC_ASSERT(sizeOfConstructorUnion.has_value()); size = 4U + sizeOfConstructorUnion.value(); // 4U: size of int32 align = 1U; return; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_NONREF: { if (layoutType) { auto layOut = cgMod.GetLLVMModule()->getDataLayout(); size = layOut.getTypeAllocSize(layoutType); align = layOut.getABITypeAlignment(layoutType); } else { size = std::nullopt; align = std::nullopt; } return; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_T: { // size and align can't be determined at compile time size = std::nullopt; align = std::nullopt; return; } case CGEnumTypeKind::EXHAUSTIVE_ZERO_SIZE: { size = 0U; align = 1U; return; } default: // unexpected CGEnumTypeKind CJC_ASSERT(false && "Should not reach here: UNKNOWN enum kind"); return; } } llvm::Constant* CGEnumType::GenFieldsNumOfTypeInfo() { switch (cgEnumTypeKind) { case CGEnumTypeKind::NON_EXHAUSTIVE_UNASSOCIATED: case CGEnumTypeKind::EXHAUSTIVE_UNASSOCIATED: case CGEnumTypeKind::NON_EXHAUSTIVE_ASSOCIATED: case CGEnumTypeKind::EXHAUSTIVE_OTHER: { return llvm::ConstantInt::get(llvm::Type::getInt16Ty(cgMod.GetLLVMContext()), 1U); } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_NONREF: case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_REF: case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_T: { return llvm::ConstantInt::get(llvm::Type::getInt16Ty(cgMod.GetLLVMContext()), 2U); } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_NONREF: case CGEnumTypeKind::EXHAUSTIVE_ZERO_SIZE: { return llvm::ConstantInt::get(llvm::Type::getInt16Ty(cgMod.GetLLVMContext()), 0U); } default: // unexpected CGEnumTypeKind CJC_ASSERT(false && "Should not reach here: UNKNOWN enum kind"); return nullptr; } } llvm::Constant* CGEnumType::GenFieldsOfTypeInfo() { std::vector fieldTypes; switch (cgEnumTypeKind) { case CGEnumTypeKind::NON_EXHAUSTIVE_UNASSOCIATED: case CGEnumTypeKind::EXHAUSTIVE_UNASSOCIATED: case CGEnumTypeKind::NON_EXHAUSTIVE_ASSOCIATED: case CGEnumTypeKind::EXHAUSTIVE_OTHER: { fieldTypes.emplace_back(const_cast(&CGType::GetInt32CGType(cgMod)->GetOriginal())); break; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_REF: { CJC_NULLPTR_CHECK(optionLikeInfo); fieldTypes.emplace_back(optionLikeInfo->associatedValueType); fieldTypes.emplace_back(const_cast(&CGType::GetZeroSizedCGType(cgMod)->GetOriginal())); break; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_NONREF: { CJC_NULLPTR_CHECK(optionLikeInfo); fieldTypes.emplace_back(const_cast(&CGType::GetBoolCGType(cgMod)->GetOriginal())); fieldTypes.emplace_back(optionLikeInfo->associatedValueType); break; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_NONREF: case CGEnumTypeKind::EXHAUSTIVE_ZERO_SIZE: { break; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_T: default: CJC_ASSERT(false && "Should not reach here"); break; } std::vector fieldConstants = GenTypeInfoConstantVectorForTypes(cgMod, fieldTypes); return GenTypeInfoArray(cgMod, CGType::GetNameOfTypeInfoGV(chirType) + ".fields", fieldConstants); } llvm::Constant* CGEnumType::GenOffsetsOfTypeInfo() { if (cgEnumTypeKind == CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_NONREF) { return llvm::ConstantPointerNull::get(llvm::Type::getInt32PtrTy(cgMod.GetLLVMContext())); } else { return CGCustomType::GenOffsetsOfTypeInfo(); } } llvm::Constant* CGEnumType::GenFieldsNumOfTypeTemplate() { switch (cgEnumTypeKind) { case CGEnumTypeKind::NON_EXHAUSTIVE_UNASSOCIATED: case CGEnumTypeKind::EXHAUSTIVE_UNASSOCIATED: case CGEnumTypeKind::NON_EXHAUSTIVE_ASSOCIATED: case CGEnumTypeKind::EXHAUSTIVE_OTHER: { return llvm::ConstantInt::get(llvm::Type::getInt16Ty(cgMod.GetLLVMContext()), 1U); } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_T: case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_REF: case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_NONREF: { return llvm::ConstantInt::get(llvm::Type::getInt16Ty(cgMod.GetLLVMContext()), 2U); } case CGEnumTypeKind::EXHAUSTIVE_ZERO_SIZE: { return llvm::ConstantInt::get(llvm::Type::getInt16Ty(cgMod.GetLLVMContext()), 0U); } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_NONREF: default: CJC_ASSERT(false && "Should not reach here: UNKNOWN enum kind"); return nullptr; } } llvm::Constant* GenFieldsFnsOfTypeTemplateForOptionLikeT(CGModule& cgMod, const std::string& funcPrefixName) { auto& llvmCtx = cgMod.GetLLVMContext(); auto p0i8 = llvm::Type::getInt8PtrTy(llvmCtx); if (auto gv = cgMod.GetLLVMModule()->getNamedGlobal(funcPrefixName + ".fieldTiFns"); gv && gv->hasInitializer()) { return llvm::ConstantExpr::getBitCast(gv, p0i8); } auto typeInfoPtrType = CGType::GetOrCreateTypeInfoPtrType(llvmCtx); std::vector argTypes{llvm::Type::getInt32Ty(llvmCtx), typeInfoPtrType->getPointerTo()}; auto fieldFnType = llvm::FunctionType::get(p0i8, argTypes, false); // 1. create fieldTiFn 0 auto getTiFn1 = llvm::Function::Create( fieldFnType, llvm::Function::PrivateLinkage, funcPrefixName + ".fieldTiFn.0", cgMod.GetLLVMModule()); getTiFn1->addFnAttr("native-interface-fn"); CodeGen::IRBuilder2 irBuilder(cgMod); auto entryBB = irBuilder.CreateEntryBasicBlock(getTiFn1, "entry"); irBuilder.SetInsertPoint(entryBB); auto ti = irBuilder.LLVMIRBuilder2::CreateLoad(typeInfoPtrType, getTiFn1->getArg(1)); auto isRef = irBuilder.CreateTypeInfoIsReferenceCall(ti); auto [refBB, nonRefBB] = Vec2Tuple<2>(irBuilder.CreateAndInsertBasicBlocks({"ref", "nonRef"})); irBuilder.CreateCondBr(isRef, refBB, nonRefBB); irBuilder.SetInsertPoint(refBB); irBuilder.CreateRet(irBuilder.CreateBitCast(ti, p0i8)); irBuilder.SetInsertPoint(nonRefBB); irBuilder.CreateRet(irBuilder.CreateBitCast(CGType::GetBoolCGType(cgMod)->GetOrCreateTypeInfo(), p0i8)); // 2. create fieldTiFn 1 auto getTiFn2 = llvm::Function::Create( fieldFnType, llvm::Function::PrivateLinkage, funcPrefixName + ".fieldTiFn.1", cgMod.GetLLVMModule()); getTiFn2->addFnAttr("native-interface-fn"); entryBB = irBuilder.CreateEntryBasicBlock(getTiFn2, "entry"); irBuilder.SetInsertPoint(entryBB); ti = irBuilder.LLVMIRBuilder2::CreateLoad(typeInfoPtrType, getTiFn2->getArg(1)); isRef = irBuilder.CreateTypeInfoIsReferenceCall(ti); auto [refBB2, nonRefBB2] = Vec2Tuple<2>(irBuilder.CreateAndInsertBasicBlocks({"ref", "nonRef"})); irBuilder.CreateCondBr(isRef, refBB2, nonRefBB2); irBuilder.SetInsertPoint(refBB2); irBuilder.CreateRet( irBuilder.CreateBitCast(CGType::GetZeroSizedCGType(cgMod)->GetOrCreateTypeInfo(), p0i8)); irBuilder.SetInsertPoint(nonRefBB2); irBuilder.CreateRet(irBuilder.CreateBitCast(ti, p0i8)); return CGTypeInfo::GenFieldsFnsOfTypeTemplate(cgMod, funcPrefixName, {getTiFn1, getTiFn2}); } llvm::Constant* CGEnumType::GenFieldsFnsOfTypeTemplate() { auto funcPrefixName = CGType::GetNameOfTypeTemplateGV(chirType); std::vector fieldTypes; std::unordered_map localGenericParamIndicesMap; switch (cgEnumTypeKind) { case CGEnumTypeKind::NON_EXHAUSTIVE_UNASSOCIATED: case CGEnumTypeKind::EXHAUSTIVE_UNASSOCIATED: case CGEnumTypeKind::NON_EXHAUSTIVE_ASSOCIATED: case CGEnumTypeKind::EXHAUSTIVE_OTHER: { fieldTypes.emplace_back(const_cast(&CGType::GetInt32CGType(cgMod)->GetOriginal())); break; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_REF: { fieldTypes.emplace_back( CGType::GetRefTypeOf(cgCtx.GetCHIRBuilder(), CGType::GetObjectCGType(cgMod)->GetOriginal())); fieldTypes.emplace_back(const_cast(&CGType::GetZeroSizedCGType(cgMod)->GetOriginal())); break; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_NONREF: { CJC_NULLPTR_CHECK(optionLikeInfo); fieldTypes.emplace_back(const_cast(&CGType::GetBoolCGType(cgMod)->GetOriginal())); fieldTypes.emplace_back(optionLikeInfo->associatedValueType); std::size_t gTIdx = 0; for (auto typeArg : GetGenericArgsFromCHIRType(chirEnumType)) { auto gt = typeArg.GetGenericType(); localGenericParamIndicesMap.emplace(gt, gTIdx++); } break; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_T: { return GenFieldsFnsOfTypeTemplateForOptionLikeT(cgMod, funcPrefixName); } case CGEnumTypeKind::EXHAUSTIVE_ZERO_SIZE: { break; } case CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_NONREF: default: CJC_ASSERT(false && "Should not reach here: UNKNOWN enum kind"); break; } return CGTypeInfo::GenFieldsFnsOfTypeTemplate(cgMod, funcPrefixName, fieldTypes, localGenericParamIndicesMap); } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGEnumType.h000066400000000000000000000131141510705540100234230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGENUMTYPE_H #define CANGJIE_CGENUMTYPE_H #include "Base/CGTypes/CGCustomType.h" namespace Cangjie { namespace CodeGen { class CGEnumType : public CGCustomType { friend class CGTypeMgr; friend class CGEnum; friend class IRBuilder2; public: enum class CGEnumTypeKind { UNKNOWN, /**< Not a valid kind */ /* Non-recursive and Non-exhaustive enum types */ NON_EXHAUSTIVE_UNASSOCIATED, // all constructors have no associated values. NON_EXHAUSTIVE_ASSOCIATED, // some constructors have associated values. /* Non-recursive and Exhaustive enum types */ EXHAUSTIVE_ZERO_SIZE, // has and only has one constructor, and the size of the associated values is 0. EXHAUSTIVE_UNASSOCIATED, // all constructors have no associated values. EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_REF, // has and only has two constructors, and only one constructor has only // one associated value of ref-type. EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_NONREF, // has and only has two constructors, and only one constructor has // only one associated value of non-ref-type. EXHAUSTIVE_ASSOCIATED_NONREF, // The associated values of all constructors are non-ref-type. EXHAUSTIVE_OTHER, // other cases. /* Unable to determine the category of T */ EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_T, // has and only has two constructors, and only one constructor has only // one associated value of T-type. }; struct OptionLikeInfo { bool isAntiOptionLike{false}; CHIR::Type* associatedValueType{nullptr}; OptionLikeInfo(bool isAntiOptionLike, CHIR::Type* associatedValueType) : isAntiOptionLike(isAntiOptionLike), associatedValueType(associatedValueType) { } }; bool IsTrivial() const { return cgEnumTypeKind == CGEnumTypeKind::NON_EXHAUSTIVE_UNASSOCIATED || cgEnumTypeKind == CGEnumTypeKind::EXHAUSTIVE_UNASSOCIATED; } bool IsCommonEnum() const { return cgEnumTypeKind == CGEnumTypeKind::NON_EXHAUSTIVE_ASSOCIATED || cgEnumTypeKind == CGEnumTypeKind::EXHAUSTIVE_OTHER; } bool IsZeroSizeEnum() const { return cgEnumTypeKind == CGEnumTypeKind::EXHAUSTIVE_ZERO_SIZE; } bool IsOptionLikeT() const { return cgEnumTypeKind == CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_T; } bool IsOptionLikeRef() const { return cgEnumTypeKind == CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_REF; } bool IsOptionLikeNonRef() const { return cgEnumTypeKind == CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_NONREF; } bool IsOptionLike() const { return cgEnumTypeKind == CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_T || cgEnumTypeKind == CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_REF || cgEnumTypeKind == CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_NONREF; } bool IsAllAssociatedValuesAreNonRef() const { return cgEnumTypeKind == CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_NONREF; } bool IsAntiOptionLike() const { CJC_ASSERT(optionLikeInfo != nullptr && "cgEnumType is not OptionLike"); return optionLikeInfo->isAntiOptionLike; } CHIR::Type* GetAssociatedValueTypeOfOptionLike() const { CJC_ASSERT(optionLikeInfo != nullptr && "cgEnumType is not OptionLike"); return optionLikeInfo->associatedValueType; } bool PassByReference() const { return IsOptionLikeNonRef() || IsOptionLikeT() || IsZeroSizeEnum() || IsAllAssociatedValuesAreNonRef(); } llvm::StructType* GetLayoutType() const { return layoutType; } CGEnumTypeKind GetCGEnumTypeKind() const { return cgEnumTypeKind; } std::size_t GetsizeOfConstructorUnion() const { CJC_ASSERT(sizeOfConstructorUnion.has_value() && "This enum does not belong to EXHAUSTIVE_ASSOCIATED_NONREF."); return sizeOfConstructorUnion.value() * 8U; // 8U: bits } std::string GetEnumTypeName() const; protected: llvm::Type* GenLLVMType() override; void GenContainedCGTypes() override; private: CGEnumType() = delete; explicit CGEnumType(CGModule& cgMod, CGContext& cgCtx, const CHIR::Type& chirType); CGEnumTypeKind CalculateCGEnumTypeKind(); void CalculateSizeAndAlign() override; llvm::Constant* GenFieldsNumOfTypeInfo() override; llvm::Constant* GenFieldsOfTypeInfo() override; llvm::Constant* GenOffsetsOfTypeInfo() override; llvm::Constant* GenFieldsNumOfTypeTemplate() override; llvm::Constant* GenFieldsFnsOfTypeTemplate() override; llvm::Type* GenZeroSizeEnumLLVMType(); llvm::Type* GenTrivialLLVMType(); llvm::Type* GenOptionLikeRefLLVMType(); llvm::Type* GenOptionLikeNonRefLLVMType(); llvm::Type* GenAssociatedNonRefEnumLLVMType(); llvm::Type* GenCommonEnumLLVMType(); private: const CHIR::EnumType& chirEnumType; std::unique_ptr optionLikeInfo; std::optional sizeOfConstructorUnion; CGEnumTypeKind cgEnumTypeKind; }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGENUMTYPE_H cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGFunctionType.cpp000066400000000000000000000261571510705540100246520ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/CGTypes/CGFunctionType.h" #include "Base/CGTypes/CGGenericType.h" #include "CGContext.h" #include "CGModule.h" #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "CJNative/CGCFFI.h" #endif #include "Utils/CGUtils.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CodeGen { CGFunctionType::CGFunctionType( CGModule& cgMod, CGContext& cgCtx, const CHIR::FuncType& chirType, const TypeExtraInfo& extraInfo) : CGType(cgMod, cgCtx, chirType, CGTypeKind::CG_FUNCTION) { CJC_ASSERT(chirType.IsFunc()); isMethod = extraInfo.isMethod; isStaticMethod = extraInfo.isStaticMethod; instantiatedParamTypes = extraInfo.instantiatedParamTypes; CJC_ASSERT(!forWrapper && !extraInfo.forWrapper); } CGFunctionType::CGFunctionType( CGModule& cgMod, CGContext& cgCtx, const CHIR::FuncBase& chirFunc, const TypeExtraInfo& extraInfo) : CGType(cgMod, cgCtx, *chirFunc.GetType(), CGTypeKind::CG_FUNCTION) { this->chirFunc = &chirFunc; this->isStaticMethod = chirFunc.TestAttr(CHIR::Attribute::STATIC); this->isMethod = chirFunc.GetParentCustomTypeDef(); this->forWrapper = extraInfo.forWrapper; if (this->forWrapper) { this->allowBasePtr = false; } else { this->allowBasePtr = extraInfo.allowBasePtr; } for (auto gt : chirFunc.GetGenericTypeParams()) { this->instantiatedParamTypes.emplace_back(gt); } } llvm::Type* CGFunctionType::GenLLVMType() { if (llvmType) { return llvmType; } auto& llvmCtx = cgCtx.GetLLVMContext(); layoutType = llvm::StructType::getTypeByName(llvmCtx, "Func.Type"); if (!layoutType) { layoutType = llvm::StructType::create(llvmCtx, {llvm::Type::getInt8PtrTy(llvmCtx)}, "Func.Type"); } auto& funcTy = StaticCast(chirType); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (funcTy.IsCFunc()) { llvmType = llvm::PointerType::get(llvm::StructType::get(llvmCtx), 0); auto llvmFuncTy = cgMod.GetCGCFFI().GetCFuncType(funcTy); return llvmFuncTy ? llvmFuncTy->getPointerTo() : llvmType; } #endif llvmType = llvm::Type::getInt8PtrTy(llvmCtx); std::vector paramTypes; size_t realArgIdx = 0; auto retCGType = CGType::GetOrCreate(cgMod, funcTy.GetReturnType()); llvm::Type* retType = retCGType->GetLLVMType(); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (retType->isStructTy() || retType->isArrayTy() || !retCGType->GetSize()) { if (!retCGType->GetSize() && !funcTy.GetReturnType()->IsGeneric()) { (void)paramTypes.emplace_back(retType->getPointerTo(1)); } else { (void)paramTypes.emplace_back(retType->getPointerTo()); } ++realArgIdx; retType = llvm::Type::getVoidTy(llvmCtx); hasSRet = true; } size_t containedCGTypeIndex = 1; #endif for (auto paramType : funcTy.GetParamTypes()) { auto cgType = CGType::GetOrCreate(cgMod, paramType); auto llvmType = cgType->GetLLVMType(); (void)realParamIndices.emplace_back(realArgIdx); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND /// Effect Overview // The parameter that requires additional basePtr must meet and the function should not be 'mut' // either: // - (1) Is a pointer to llvm::StructType or llvm::ArrayType. // or: // - (2) Is llvm::StructType or llvm::ArrayType and is the first parameter of function. // hint: This is designed to make it easier to invoke methods through reflect by runtime. // // The type of basePtr is determined according to the following rules: // - For above (1), basePtr is i8 addrspace(1)*. // - For above (2), basePtr is i8*. if (paramType->IsRef() && (paramType->GetTypeArgs()[0]->IsStruct() || paramType->GetTypeArgs()[0]->IsTuple())) { llvmType = CGType::GetOrCreate(cgMod, DeRef(*paramType))->GetLLVMType()->getPointerTo(1U); (void)paramTypes.emplace_back(llvmType); if (allowBasePtr) { (void) paramTypes.emplace_back(llvm::Type::getInt8PtrTy(llvmCtx, 1)); structParamNeedsBasePtr[realArgIdx] = containedCGTypeIndex; ++realArgIdx; hasBasePtr = true; } } else if (paramType->IsStruct() || paramType->IsTuple() || (!cgType->GetSize() && !paramType->IsGeneric())) { // If current parameter is the first parameter in a struct method, aka `this` if (!forWrapper && containedCGTypeIndex == 1U && this->chirFunc && !this->chirFunc->TestAttr(CHIR::Attribute::STATIC) && IsStructOrExtendMethod(*chirFunc)) { llvmType = cgType->GetLLVMType()->getPointerTo(); } else { llvmType = cgType->GetLLVMType()->getPointerTo(cgType->GetSize() ? 0U : 1U); } (void)paramTypes.emplace_back(llvmType); } else if (cgType->IsStructType() || cgType->IsVArrayType()) { llvmType = cgType->GetLLVMType()->getPointerTo(); (void)paramTypes.emplace_back(llvmType); } else { (void)paramTypes.emplace_back(llvmType); } ++containedCGTypeIndex; ++realArgIdx; #endif } // Adding Supplementary parameters if (this->chirFunc) { // Generic parameters introduced by a function for (auto chirGT: this->chirFunc->GetGenericTypeParams()) { (void) paramTypes.emplace_back(CGType::GetOrCreateTypeInfoPtrType(llvmCtx)); genericParamIndicesMap.emplace(chirGT, realArgIdx++); // map chirGT to ArgIdx } } else { for (std::size_t idx = 0; idx < this->instantiatedParamTypes.size(); ++idx) { (void) paramTypes.emplace_back(CGType::GetOrCreateTypeInfoPtrType(llvmCtx)); realArgIdx++; } } if (this->isMethod) { // For member method, add OuterTypeInfo (void)paramTypes.emplace_back(CGType::GetOrCreateTypeInfoPtrType(llvmCtx)); outerTypeInfoIndex = realArgIdx++; } if (this->isStaticMethod) { // For static member method, add ThisTypeInfo (void)paramTypes.emplace_back(CGType::GetOrCreateTypeInfoPtrType(llvmCtx)); thisTypeInfoIndex = realArgIdx++; } llvmFunctionType = llvm::FunctionType::get(retType, paramTypes, funcTy.HasVarArg()); return llvmType; } llvm::FunctionType* CGFunctionType::GetLLVMFunctionType() const { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (auto& chirFuncTy = StaticCast(chirType); chirFuncTy.IsCFunc()) { return cgMod.GetCGCFFI().GetCFuncType(chirFuncTy); } #endif return llvmFunctionType; } void CGFunctionType::GenContainedCGTypes() { size_t containedCGTypeIndex = 0; // On CJNative backend, the parameters of structure type are passed by reference. auto getFixedCGType = [this, &containedCGTypeIndex](const CHIR::Type& chirType) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto cgType = CGType::GetOrCreate(cgMod, &chirType); if (chirType.IsRef() && chirType.GetTypeArgs()[0]->IsStruct()) { return CGType::GetOrCreate(cgMod, &chirType, 1U); } else if (chirType.IsStruct() || chirType.IsTuple() || (!cgType->GetSize() && !chirType.IsGeneric())) { auto refType = CGType::GetRefTypeOf(cgCtx.GetCHIRBuilder(), chirType); // If current parameter is the first parameter in a struct method, aka `this` if (!forWrapper && containedCGTypeIndex == 1U && this->chirFunc && IsStructOrExtendMethod(*chirFunc)) { return CGType::GetOrCreate(cgMod, refType); } else { return CGType::GetOrCreate(cgMod, refType, cgType->GetSize() ? 0U : 1U); } } else if (cgType->IsStructType() || cgType->IsVArrayType()) { return CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(cgCtx.GetCHIRBuilder(), chirType), 0U); } else if (cgType->IsStructPtrType() || cgType->IsVArrayPtrType()) { return CGType::GetOrCreate(cgMod, &chirType, 1U); } else { return CGType::GetOrCreate(cgMod, &chirType); } #endif }; // The contained types of a function type include: // (1) return type auto& funcTy = StaticCast(chirType); (void)containedCGTypes.emplace_back(getFixedCGType(*funcTy.GetReturnType())); ++containedCGTypeIndex; // (2) every parameter type for (auto paramType : funcTy.GetParamTypes()) { (void)containedCGTypes.emplace_back(getFixedCGType(*paramType)); ++containedCGTypeIndex; } // (3) Supplementary parameters if (this->chirFunc) { // every generic parameter type for ([[maybe_unused]] const auto& it : this->chirFunc->GetGenericTypeParams()) { (void)containedCGTypes.emplace_back(CGType::GetCGTI(cgMod)); } // For member method, add OuterTypeInfo if (this->chirFunc->GetOuterDeclaredOrExtendedDef()) { (void)containedCGTypes.emplace_back(CGType::GetCGTI(cgMod)); } // For static member method, add ThisTypeInfo if (this->chirFunc->TestAttr(CHIR::Attribute::STATIC)) { (void)containedCGTypes.emplace_back(CGType::GetCGTI(cgMod)); } } } llvm::Constant* CGFunctionType::GenSourceGenericOfTypeInfo() { return CGType::GenSourceGenericOfTypeInfo(); } llvm::Constant* CGFunctionType::GenTypeArgsNumOfTypeInfo() { return llvm::ConstantInt::get(llvm::Type::getInt8Ty(cgMod.GetLLVMContext()), static_cast(chirType).GetParamTypes().size() + 1U); // add one for the return type } llvm::Constant* CGFunctionType::GenTypeArgsOfTypeInfo() { auto& funcType = StaticCast(chirType); auto typeInfoPtrTy = CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext()); std::vector constants; (void)constants.emplace_back(CGType::GetOrCreate(cgMod, DeRef(*funcType.GetReturnType()))->GetOrCreateTypeInfo()); for (auto paramType : funcType.GetParamTypes()) { (void)constants.emplace_back(CGType::GetOrCreate(cgMod, DeRef(*paramType))->GetOrCreateTypeInfo()); } auto typeOfGenericArgsGV = llvm::ArrayType::get(typeInfoPtrTy, constants.size()); auto typeInfoOfGenericArgs = llvm::cast(cgMod.GetLLVMModule()->getOrInsertGlobal( CGType::GetNameOfTypeInfoGV(chirType) + ".typeArgs", typeOfGenericArgsGV)); typeInfoOfGenericArgs->setInitializer(llvm::ConstantArray::get(typeOfGenericArgsGV, constants)); typeInfoOfGenericArgs->setLinkage(llvm::GlobalValue::LinkageTypes::PrivateLinkage); typeInfoOfGenericArgs->addAttribute(CJTI_TYPE_ARGS_ATTR); return llvm::ConstantExpr::getBitCast(typeInfoOfGenericArgs, llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext())); } void CGFunctionType::CalculateSizeAndAlign() { llvm::DataLayout layOut = cgMod.GetLLVMModule()->getDataLayout(); size = layOut.getTypeAllocSize(llvmType); align = layOut.getABITypeAlignment(llvmType); } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGFunctionType.h000066400000000000000000000101131510705540100243000ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGFUNCTIONTYPE_H #define CANGJIE_CGFUNCTIONTYPE_H #include "Base/CGTypes/CGType.h" #include "cangjie/CHIR/CHIRCasting.h" namespace Cangjie { namespace CodeGen { class CGFunctionType : public CGType { friend class CGTypeMgr; friend class CGFunction; public: bool HasSRet() const { return hasSRet; } const std::unordered_map& GetStructParamNeedsBasePtrIndices() const { return structParamNeedsBasePtr; } llvm::FunctionType* GetLLVMFunctionType() const; inline const std::vector& GetRealArgIndices() const { return realParamIndices; } inline const std::unordered_map GetGenericParamIndicesMap() const { return genericParamIndicesMap; } inline std::optional GetOuterTypeInfoIndex() const { return outerTypeInfoIndex; } inline std::optional GetThisTypeInfoIndex() const { return thisTypeInfoIndex; } CGType* GetParamType(size_t idx) const { auto tmp = GetContainedTypes(); CJC_ASSERT(idx + 1 < tmp.size()); return tmp[idx + 1]; } bool IsCFunc() const { CJC_ASSERT(GetOriginal().IsFunc()); return StaticCast(GetOriginal()).IsCFunc(); } void CalculateSizeAndAlign() override; bool HasBasePtr() const { CJC_ASSERT(hasBasePtr ? allowBasePtr : true); return hasBasePtr; } bool IsMethodType() const { return isMethod; } bool IsStaticMethodType() const { return isStaticMethod; } protected: llvm::Type* GenLLVMType() override; void GenContainedCGTypes() override; private: CGFunctionType() = delete; explicit CGFunctionType( CGModule& cgMod, CGContext& cgCtx, const CHIR::FuncType& chirType, const TypeExtraInfo& extraInfo = {}); explicit CGFunctionType( CGModule& cgMod, CGContext& cgCtx, const CHIR::FuncBase& chirFunc, const TypeExtraInfo& extraInfo = {}); llvm::Constant* GenSourceGenericOfTypeInfo() override; llvm::Constant* GenTypeArgsNumOfTypeInfo() override; llvm::Constant* GenTypeArgsOfTypeInfo() override; bool hasSRet{false}; bool hasBasePtr{false}; llvm::FunctionType* llvmFunctionType{nullptr}; /** * Just like `structParamIndices`, but Key and Value are swapped. `real` means in llvm function parameter list. * Key (the index of the vector): Index of the type in ParamTypes. * Value (the element of the vector): Index of parameter in LLVM raw function. */ std::vector realParamIndices; /** * Stores indices of struct type parameter in LLVM raw function and containedCGTypes. * Key: Index of parameter in LLVM raw function that is of struct type of basePtr is required. * Value: Index of the corresponding CGType in `containedCGTypes` field. */ std::unordered_map structParamNeedsBasePtr; /** * `closureParamIndices` is used to let escape analysis know that * this formal parameter is a structure used to represent a closure. * Its escape attribute is bound between the whole structure and a * layer of elements in the structure, either escape together or do * not escape together. */ std::vector closureParamIndices; const CHIR::FuncBase* chirFunc{nullptr}; bool allowBasePtr{true}; bool isMethod{false}; bool isStaticMethod{false}; bool forWrapper{false}; std::vector instantiatedParamTypes; std::unordered_map genericParamIndicesMap; std::optional outerTypeInfoIndex{std::nullopt}; std::optional thisTypeInfoIndex{std::nullopt}; }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGFUNCTIONTYPE_H cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGGenericType.cpp000066400000000000000000000067041510705540100244350ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/CGTypes/CGGenericType.h" #include "CGModule.h" namespace Cangjie::CodeGen { llvm::Type* CGGenericType::GenLLVMType() { if (llvmType) { return llvmType; } return CGType::GetRefType(cgMod.GetLLVMContext()); } void CGGenericType::CalculateSizeAndAlign() { size = std::nullopt; align = std::nullopt; } void CGGenericType::GenContainedCGTypes() { // No contained types } llvm::Constant* CGGenericType::GenUpperBoundsOfGenericType(std::string& uniqueName) { auto p0i8 = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); if (upperBounds.empty()) { return llvm::ConstantPointerNull::get(p0i8); } std::vector constants; for (auto upperBound : upperBounds) { auto cgTypeOfUpperBound = CGType::GetOrCreate(cgMod, DeRef(*upperBound)); constants.emplace_back(llvm::ConstantExpr::getBitCast(cgTypeOfUpperBound->GetOrCreateTypeInfo(), p0i8)); } auto typeOfUpperBoundsGV = llvm::ArrayType::get(p0i8, constants.size()); auto typeInfoOfUpperBounds = llvm::cast(cgMod.GetLLVMModule()->getOrInsertGlobal( uniqueName + ".upperBounds", typeOfUpperBoundsGV)); typeInfoOfUpperBounds->setInitializer(llvm::ConstantArray::get(typeOfUpperBoundsGV, constants)); typeInfoOfUpperBounds->setLinkage(llvm::GlobalValue::LinkageTypes::PrivateLinkage); typeInfoOfUpperBounds->addAttribute(CJTI_UPPER_BOUNDS_ATTR); return llvm::ConstantExpr::getBitCast(typeInfoOfUpperBounds, p0i8); } llvm::GlobalVariable* CGGenericType::GetOrCreateTypeInfo() { CJC_ASSERT(chirType.IsGeneric()); auto genericTypeName = StaticCast(chirType).GetSrcCodeIdentifier(); upperBounds = StaticCast(chirType).GetUpperBounds(); std::string uniqueName = cgMod.GetCGContext().GetGenericTypeUniqueName(genericTypeName, upperBounds); if (auto found = cgMod.GetLLVMModule()->getNamedGlobal(uniqueName + ".ti"); found) { return found; } auto genericTypeInfoType = GetOrCreateGenericTypeInfoType(cgMod.GetLLVMContext()); auto genericTypeInfo = llvm::cast( cgMod.GetLLVMModule()->getOrInsertGlobal(uniqueName + ".ti", genericTypeInfoType)); unsigned typeInfoKind = UGTypeKind::UG_GENERIC; std::vector typeInfoVec(GENERIC_TYPE_INFO_FIELDS_NUM); typeInfoVec[static_cast(GENERIC_TYPEINFO_NAME)] = cgMod.GenerateTypeNameConstantString(genericTypeName, false); typeInfoVec[static_cast(GENERIC_TYPEINFO_TYPE_KIND)] = llvm::ConstantInt::get(llvm::Type::getInt8Ty(cgMod.GetLLVMContext()), typeInfoKind); typeInfoVec[static_cast(GENERIC_TYPEINFO_UPPERBOUNDS_NUM)] = llvm::ConstantInt::get(llvm::Type::getInt32Ty(cgMod.GetLLVMContext()), upperBounds.size()); typeInfoVec[static_cast(GENERIC_TYPEINFO_UPPERBOUNDS)] = GenUpperBoundsOfGenericType(uniqueName); genericTypeInfo->setInitializer(llvm::ConstantStruct::get(genericTypeInfoType, typeInfoVec)); genericTypeInfo->setLinkage(llvm::GlobalValue::PrivateLinkage); genericTypeInfo->addAttribute(GENERIC_TYPEINFO_ATTR); return genericTypeInfo; } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGGenericType.h000066400000000000000000000022221510705540100240710ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGGENERICTYPE_H #define CANGJIE_CGGENERICTYPE_H #include "Base/CGTypes/CGType.h" #include "CGContext.h" #include "cangjie/CHIR/Type/Type.h" namespace Cangjie { namespace CodeGen { class CGGenericType : public CGType { friend class CGTypeMgr; public: llvm::GlobalVariable* GetOrCreateTypeInfo() override; private: CGGenericType() = delete; explicit CGGenericType(CGModule& cgMod, CGContext& cgCtx, const CHIR::Type& chirType) : CGType(cgMod, cgCtx, chirType) { CJC_ASSERT(chirType.GetTypeKind() == CHIR::Type::TYPE_GENERIC); } std::vector upperBounds; llvm::Type* GenLLVMType() override; void GenContainedCGTypes() override; void CalculateSizeAndAlign() override; llvm::Constant* GenUpperBoundsOfGenericType(std::string& uniqueName); }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGGENERICTYPE_Hcangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGPrimitiveType.cpp000066400000000000000000000075071510705540100250330ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/CGTypes/CGPrimitiveType.h" #include "CGModule.h" #include "cangjie/CHIR/CHIRContext.h" namespace Cangjie::CodeGen { llvm::Type* CGPrimitiveType::GenLLVMType() { if (llvmType) { return llvmType; } auto& llvmCtx = cgCtx.GetLLVMContext(); switch (chirType.GetTypeKind()) { case CHIR::Type::TypeKind::TYPE_INT8: case CHIR::Type::TypeKind::TYPE_UINT8: llvmType = llvm::Type::getInt8Ty(llvmCtx); break; case CHIR::Type::TypeKind::TYPE_INT16: case CHIR::Type::TypeKind::TYPE_UINT16: llvmType = llvm::Type::getInt16Ty(llvmCtx); break; case CHIR::Type::TypeKind::TYPE_INT32: case CHIR::Type::TypeKind::TYPE_UINT32: case CHIR::Type::TypeKind::TYPE_RUNE: llvmType = llvm::Type::getInt32Ty(llvmCtx); break; case CHIR::Type::TypeKind::TYPE_INT64: case CHIR::Type::TypeKind::TYPE_UINT64: llvmType = llvm::Type::getInt64Ty(llvmCtx); break; case CHIR::Type::TypeKind::TYPE_INT_NATIVE: case CHIR::Type::TypeKind::TYPE_UINT_NATIVE: { auto archType = cgCtx.GetCompileOptions().target.GetArchType(); if (archType == Triple::ArchType::X86_64 || archType == Triple::ArchType::AARCH64) { llvmType = llvm::Type::getInt64Ty(llvmCtx); break; } if (archType == Triple::ArchType::ARM32) { llvmType = llvm::Type::getInt32Ty(llvmCtx); break; } CJC_ASSERT(false && "Unsupported ArchType."); return nullptr; } case CHIR::Type::TypeKind::TYPE_FLOAT16: llvmType = llvm::Type::getHalfTy(llvmCtx); break; case CHIR::Type::TypeKind::TYPE_FLOAT32: llvmType = llvm::Type::getFloatTy(llvmCtx); break; case CHIR::Type::TypeKind::TYPE_FLOAT64: llvmType = llvm::Type::getDoubleTy(llvmCtx); break; case CHIR::Type::TypeKind::TYPE_BOOLEAN: llvmType = llvm::Type::getInt1Ty(llvmCtx); break; case CHIR::Type::TypeKind::TYPE_UNIT: case CHIR::Type::TypeKind::TYPE_NOTHING: llvmType = CGType::GetUnitType(llvmCtx); break; case CHIR::Type::TypeKind::TYPE_VOID: llvmType = llvm::Type::getVoidTy(llvmCtx); layoutType = llvm::StructType::getTypeByName(llvmCtx, UNIT_TYPE_STR); if (!layoutType) { layoutType = llvm::StructType::create(llvmCtx, {}, UNIT_TYPE_STR); } return llvmType; default: CJC_ASSERT(false && "Should not reach here: unsupported chir type"); return nullptr; } auto layoutName = chirType.ToString() + ".Type"; layoutType = llvm::StructType::getTypeByName(llvmCtx, layoutName); if (!layoutType) { layoutType = llvm::StructType::create(llvmCtx, {llvmType}, layoutName); } return llvmType; } void CGPrimitiveType::GenContainedCGTypes() { // For primitive type, no contained types. } void CGPrimitiveType::CalculateSizeAndAlign() { if (auto t = DynamicCast(&chirType)) { size= t->GetBitness() / 8U; } else if (chirType.IsRune()) { size = 4U; } else if (chirType.IsBoolean()) { size = 1U; } else if (chirType.IsUnit() || chirType.IsNothing() || chirType.IsVoid()) { size = 0U; } else { CJC_ASSERT(false && "Unsupported type"); } align = size == 0 ? 1 : size; } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGPrimitiveType.h000066400000000000000000000021131510705540100244640ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGPRIMITIVETYPE_H #define CANGJIE_CGPRIMITIVETYPE_H #include "Base/CGTypes/CGType.h" #include "CGContext.h" #include "cangjie/CHIR/Type/Type.h" namespace Cangjie { namespace CodeGen { class CGPrimitiveType : public CGType { friend class CGTypeMgr; protected: llvm::Type* GenLLVMType() override; void GenContainedCGTypes() override; private: CGPrimitiveType() = delete; explicit CGPrimitiveType(CGModule& cgMod, CGContext& cgCtx, const CHIR::Type& chirType) : CGType(cgMod, cgCtx, chirType, CGTypeKind::CG_PRIMITIVE) { CJC_ASSERT( chirType.GetTypeKind() >= CHIR::Type::TYPE_INT8 && chirType.GetTypeKind() <= CHIR::Type::TYPE_VOID); } void CalculateSizeAndAlign() override; }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGPRIMITIVETYPE_Hcangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGRefType.cpp000066400000000000000000000026371510705540100235760ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/CGTypes/CGRefType.h" #include "CGModule.h" namespace Cangjie::CodeGen { llvm::Type* CGRefType::GenLLVMType() { if (llvmType) { return llvmType; } auto baseType = StaticCast(chirType).GetBaseType(); if (baseType->IsClass() || baseType->IsRawArray() || baseType->IsBox()) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND return GetRefType(cgCtx.GetLLVMContext()); #endif } auto cgType = CGType::GetOrCreate(cgMod, baseType); if (!cgType->GetSize() && !baseType->IsGeneric() && addrspace == 1U) { return llvm::Type::getInt8PtrTy(cgCtx.GetLLVMContext(), 1U); } return CGType::GetOrCreate(cgMod, baseType)->GetLLVMType()->getPointerTo(addrspace); } void CGRefType::GenContainedCGTypes() { auto& refType = StaticCast(chirType); (void)containedCGTypes.emplace_back(CGType::GetOrCreate(cgMod, refType.GetBaseType())); } void CGRefType::CalculateSizeAndAlign() { llvm::DataLayout layOut = cgMod.GetLLVMModule()->getDataLayout(); size = layOut.getTypeAllocSize(llvmType); align = layOut.getABITypeAlignment(llvmType); } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGRefType.h000066400000000000000000000017271510705540100232420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGREFTYPE_H #define CANGJIE_CGREFTYPE_H #include "Base/CGTypes/CGType.h" #include "CGContext.h" #include "cangjie/CHIR/Type/Type.h" namespace Cangjie { namespace CodeGen { class CGRefType : public CGType { friend class CGTypeMgr; protected: llvm::Type* GenLLVMType() override; void GenContainedCGTypes() override; private: CGRefType() = delete; explicit CGRefType(CGModule& cgMod, CGContext& cgCtx, const CHIR::Type& chirType, unsigned addrspace) : CGType(cgMod, cgCtx, chirType, CGTypeKind::CG_REF) { this->addrspace = addrspace; } void CalculateSizeAndAlign() override; }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGREFTYPE_H cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGStructType.cpp000066400000000000000000000111741510705540100243420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/CGTypes/CGStructType.h" #include "Base/CGTypes/CGTupleType.h" #include "CGContext.h" #include "CGModule.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/Option/Option.h" namespace Cangjie::CodeGen { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::Type* CGStructType::GenLLVMType() { auto& llvmCtx = cgCtx.GetLLVMContext(); auto& structType = StaticCast(chirType); if (!IsSized()) { return llvm::Type::getInt8Ty(llvmCtx); } auto typeName = STRUCT_TYPE_PREFIX; if (auto defType = structType.GetStructDef()->GetType(); &chirType != defType && CGType::GetOrCreate(cgMod, defType)->GetSize()) { typeName += GetTypeQualifiedName(*defType); } else { typeName += GetTypeQualifiedName(structType); } llvmType = llvm::StructType::getTypeByName(llvmCtx, typeName); if (llvmType && cgCtx.IsGeneratedStructType(typeName)) { layoutType = llvm::cast(llvmType); return llvmType; } else if (!llvmType) { llvmType = llvm::StructType::create(llvmCtx, typeName); } layoutType = llvm::cast(llvmType); cgCtx.AddGeneratedStructType(typeName); std::vector fieldTypes; std::vector litStructPtrIdx; size_t idx = 0; auto& nonConstStructType = const_cast(structType); const auto memberVarTypes = nonConstStructType.GetInstantiatedMemberTys(cgMod.GetCGContext().GetCHIRBuilder()); for (auto& memberVarType : memberVarTypes) { auto cgType = CGType::GetOrCreate(cgMod, memberVarType); auto& fieldType = fieldTypes.emplace_back(cgType->GetLLVMType()); if (IsCFunc(*memberVarType) && IsLitStructPtrType(fieldType)) { litStructPtrIdx.emplace_back(idx); } idx++; } SetStructTypeBody(llvm::cast(llvmType), fieldTypes); for (auto i : litStructPtrIdx) { fieldTypes[i] = CGType::GetOrCreate(cgMod, memberVarTypes[i])->GetLLVMType(); } SetStructTypeBody(llvm::cast(llvmType), fieldTypes); return llvmType; } #endif void CGStructType::GenContainedCGTypes() { auto& structType = StaticCast(chirType); auto& nonConstStructType = const_cast(structType); for (auto& type : nonConstStructType.GetInstantiatedMemberTys(cgMod.GetCGContext().GetCHIRBuilder())) { (void)containedCGTypes.emplace_back(CGType::GetOrCreate(cgMod, type)); } } llvm::Constant* CGStructType::GenFieldsNumOfTypeInfo() { auto structDef = StaticCast(chirType).GetStructDef(); return llvm::ConstantInt::get(llvm::Type::getInt16Ty(cgMod.GetLLVMContext()), structDef->GetAllInstanceVarNum()); } void CGStructType::CalculateSizeAndAlign() { if (auto structType = llvm::dyn_cast(llvmType)) { auto layOut = cgMod.GetLLVMModule()->getDataLayout(); size = layOut.getTypeAllocSize(structType); align = layOut.getABITypeAlignment(structType); } } llvm::Constant* CGStructType::GenFieldsOfTypeInfo() { auto p0i8 = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); if (chirType.IsGeneric()) { return llvm::ConstantPointerNull::get(p0i8); } std::vector filedConstants; auto& structType = StaticCast(chirType); auto& nonConstStructType = const_cast(structType); for (auto fieldType : nonConstStructType.GetInstantiatedMemberTys(cgMod.GetCGContext().GetCHIRBuilder())) { (void)filedConstants.emplace_back(CGType::GetOrCreate(cgMod, DeRef(*fieldType))->GetOrCreateTypeInfo()); } auto typeInfoType = CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext()); llvm::ArrayType* ArrayType = llvm::ArrayType::get(typeInfoType, filedConstants.size()); auto temp = llvm::ConstantArray::get(ArrayType, filedConstants); llvm::GlobalVariable* globalArrayPtr = static_cast( cgMod.GetLLVMModule()->getOrInsertGlobal(CGType::GetNameOfTypeInfoGV(chirType) + ".fields", ArrayType)); globalArrayPtr->setInitializer(temp); globalArrayPtr->setLinkage(llvm::GlobalValue::PrivateLinkage); globalArrayPtr->addAttribute(CJTI_FIELDS_ATTR); return llvm::ConstantExpr::getBitCast(globalArrayPtr, p0i8); } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGStructType.h000066400000000000000000000020031510705540100237760ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGSTRUCTTYPE_H #define CANGJIE_CGSTRUCTTYPE_H #include "Base/CGTypes/CGCustomType.h" #include "cangjie/CHIR/CHIRCasting.h" namespace Cangjie { namespace CodeGen { class CGStructType : public CGCustomType { friend class CGTypeMgr; protected: llvm::Type* GenLLVMType() override; void GenContainedCGTypes() override; private: CGStructType() = delete; explicit CGStructType(CGModule& cgMod, CGContext& cgCtx, const CHIR::Type& chirType) : CGCustomType(cgMod, cgCtx, chirType) { } llvm::Constant* GenFieldsNumOfTypeInfo() override; llvm::Constant* GenFieldsOfTypeInfo() override; void CalculateSizeAndAlign() override; }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGSTRUCTTYPE_H cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGThisType.h000066400000000000000000000022321510705540100234250ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_THISTYPE_H #define CANGJIE_THISTYPE_H #include "Base/CGTypes/CGType.h" #include "CGContext.h" #include "cangjie/CHIR/Type/Type.h" namespace Cangjie { namespace CodeGen { class CGThisType : public CGType { friend class CGTypeMgr; private: CGThisType() = delete; explicit CGThisType(CGModule& cgMod, CGContext& cgCtx, const CHIR::Type& chirType) : CGType(cgMod, cgCtx, chirType) { CJC_ASSERT(chirType.GetTypeKind() == CHIR::Type::TYPE_THIS); } llvm::Type* GenLLVMType() override { // `ThisType` is not allowed to be used for memory allocation. return nullptr; } void GenContainedCGTypes() override { // No contained types } void CalculateSizeAndAlign() override { size = std::nullopt; align = std::nullopt; } }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_THISTYPE_Hcangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGTupleType.cpp000066400000000000000000000133001510705540100241400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/CGTypes/CGTupleType.h" #include "Base/CGTypes/CGStructType.h" #include "CGContext.h" #include "CGModule.h" #include "Utils/CGUtils.h" #include "cangjie/Option/Option.h" namespace Cangjie::CodeGen { namespace { bool IsSized(CGModule& cgMod, const CHIR::TupleType& chirType) { const auto memberVars = chirType.GetElementTypes(); for (auto memberVar: memberVars) { if (memberVar->IsRef() || memberVar->IsCPointer() || memberVar->IsCFunc()) { continue; } if (!CGType::GetOrCreate(cgMod, memberVar)->GetSize()) { return false; } } return true; } } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void CGTupleType::CalculateSizeAndAlign() { if (auto structType = llvm::dyn_cast(llvmType)) { auto layOut = cgMod.GetLLVMModule()->getDataLayout(); size = layOut.getTypeAllocSize(structType); align = layOut.getABITypeAlignment(structType); } } llvm::Type* CGTupleType::GenLLVMType() { auto& llvmCtx = cgCtx.GetLLVMContext(); auto& tupleType = StaticCast(chirType); if (!IsSized(cgMod, tupleType)) { return llvm::Type::getInt8Ty(llvmCtx); } auto typeName = GetTypeQualifiedName(tupleType); llvmType = llvm::StructType::getTypeByName(llvmCtx, typeName); if (llvmType && cgCtx.IsGeneratedStructType(typeName)) { layoutType = llvm::cast(llvmType); return llvmType; } else if (!llvmType) { llvmType = llvm::StructType::create(llvmCtx, typeName); } layoutType = llvm::cast(llvmType); cgCtx.AddGeneratedStructType(typeName); std::vector fieldTypes; for (auto argType : tupleType.GetElementTypes()) { auto cgType = CGType::GetOrCreate(cgMod, argType); (void)fieldTypes.emplace_back(cgType->GetLLVMType()); } SetStructTypeBody(llvm::cast(llvmType), fieldTypes); return llvmType; } #endif void CGTupleType::GenContainedCGTypes() { auto& tupleType = StaticCast(chirType); for (auto& elemType : tupleType.GetElementTypes()) { (void)containedCGTypes.emplace_back(CGType::GetOrCreate(cgMod, elemType)); } } llvm::Constant* CGTupleType::GenFieldsNumOfTypeInfo() { auto fieldsNum = StaticCast(chirType).GetElementTypes().size(); return llvm::ConstantInt::get(llvm::Type::getInt16Ty(cgMod.GetLLVMContext()), fieldsNum); } llvm::Constant* CGTupleType::GenOffsetsOfTypeInfo() { CJC_NULLPTR_CHECK(layoutType); return CGCustomType::GenOffsetsArray(cgMod, CGType::GetNameOfTypeInfoGV(chirType) + ".offsets", layoutType); } llvm::Constant* CGTupleType::GenFieldsOfTypeInfo() { auto p0i8 = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); std::vector fieldConstants; auto instanceMemberTypes = StaticCast(chirType).GetElementTypes(); for (auto type : instanceMemberTypes) { auto derefType = DeRef(*type); (void)fieldConstants.emplace_back(CGType::GetOrCreate(cgMod, derefType)->GetOrCreateTypeInfo()); } if (fieldConstants.empty()) { return llvm::Constant::getNullValue(p0i8); } auto typeInfoPtrTy = CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext()); auto typeOfFieldsGV = llvm::ArrayType::get(typeInfoPtrTy, fieldConstants.size()); auto typeInfoOfFields = llvm::cast( cgMod.GetLLVMModule()->getOrInsertGlobal(CGType::GetNameOfTypeInfoGV(chirType) + ".fields", typeOfFieldsGV)); typeInfoOfFields->setInitializer(llvm::ConstantArray::get(typeOfFieldsGV, fieldConstants)); typeInfoOfFields->setLinkage(llvm::GlobalValue::LinkageTypes::PrivateLinkage); typeInfoOfFields->addAttribute(CJTI_FIELDS_ATTR); return llvm::ConstantExpr::getBitCast(typeInfoOfFields, p0i8); } llvm::Constant* CGTupleType::GenSourceGenericOfTypeInfo() { return CGType::GenSourceGenericOfTypeInfo(); } llvm::Constant* CGTupleType::GenTypeArgsNumOfTypeInfo() { return llvm::ConstantInt::get(llvm::Type::getInt8Ty(cgMod.GetLLVMContext()), StaticCast(chirType).GetElementTypes().size()); } llvm::Constant* CGTupleType::GenTypeArgsOfTypeInfo() { auto genericArgs = StaticCast(chirType).GetElementTypes(); auto typeInfoPtrTy = CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext()); auto p0i8 = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); if (genericArgs.empty()) { return llvm::ConstantPointerNull::get(p0i8); } std::vector constants; for (auto arg : genericArgs) { (void)constants.emplace_back(CGType::GetOrCreate(cgMod, DeRef(*arg))->GetOrCreateTypeInfo()); } auto typeOfGenericArgsGV = llvm::ArrayType::get(typeInfoPtrTy, constants.size()); auto typeInfoOfGenericArgs = llvm::cast(cgMod.GetLLVMModule()->getOrInsertGlobal( CGType::GetNameOfTypeInfoGV(chirType) + ".typeArgs", typeOfGenericArgsGV)); typeInfoOfGenericArgs->setInitializer(llvm::ConstantArray::get(typeOfGenericArgsGV, constants)); typeInfoOfGenericArgs->setLinkage(llvm::GlobalValue::LinkageTypes::PrivateLinkage); typeInfoOfGenericArgs->addAttribute(CJTI_TYPE_ARGS_ATTR); return llvm::ConstantExpr::getBitCast(typeInfoOfGenericArgs, p0i8); } llvm::Constant* CGTupleType::GenSuperOfTypeInfo() { return CGType::GenSuperOfTypeInfo(); } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGTupleType.h000066400000000000000000000024061510705540100236120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGTUPLETYPE_H #define CANGJIE_CGTUPLETYPE_H #include "Base/CGTypes/CGType.h" namespace Cangjie { namespace CodeGen { class CGTupleType : public CGType { friend class CGTypeMgr; protected: llvm::Type* GenLLVMType() override; void GenContainedCGTypes() override; private: CGTupleType() = delete; explicit CGTupleType( CGModule& cgMod, CGContext& cgCtx, const CHIR::Type& chirType, const TypeExtraInfo& extraInfo = {}) : CGType(cgMod, cgCtx, chirType) { } llvm::Constant* GenFieldsNumOfTypeInfo() override; llvm::Constant* GenFieldsOfTypeInfo() override; llvm::Constant* GenOffsetsOfTypeInfo() override; llvm::Constant* GenSourceGenericOfTypeInfo() override; llvm::Constant* GenTypeArgsNumOfTypeInfo() override; llvm::Constant* GenTypeArgsOfTypeInfo() override; llvm::Constant* GenSuperOfTypeInfo() override; void CalculateSizeAndAlign() override; }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGTUPLETYPE_H cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGType.cpp000066400000000000000000001331211510705540100231320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/CGTypes/CGType.h" #include "llvm/IR/Constant.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" #include "Base/CGTypes/CGBoxType.h" #include "Base/CGTypes/CGCPointerType.h" #include "Base/CGTypes/CGCStringType.h" #include "Base/CGTypes/CGClassType.h" #include "Base/CGTypes/CGEnumType.h" #include "Base/CGTypes/CGFunctionType.h" #include "Base/CGTypes/CGGenericType.h" #include "Base/CGTypes/CGPrimitiveType.h" #include "Base/CGTypes/CGRefType.h" #include "Base/CGTypes/CGStructType.h" #include "Base/CGTypes/CGThisType.h" #include "Base/CGTypes/CGTupleType.h" #include "Base/CGTypes/CGVArrayType.h" #include "CGContext.h" #include "CGContextImpl.h" #include "CGModule.h" #include "CJNative/CGTypes/CGExtensionDef.h" #include "IRBuilder.h" #include "Utils/CGCommonDef.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/Mangle/CHIRTypeManglingUtils.h" #include "cangjie/Utils/CheckUtils.h" namespace Cangjie { namespace CodeGen { namespace { bool HasGenericTypeArg(const CHIR::Type& type) { if (type.IsGeneric() || type.IsThis()) { return true; } for (auto ga : type.GetTypeArgs()) { if (HasGenericTypeArg(*ga)) { return true; } } return false; } bool CanMallocUseFixedSize(const CHIR::Type& chirType) { if (!chirType.IsClass()) { return true; } auto classDef = StaticCast(chirType).GetClassDef(); return classDef->TestAttr(CHIR::Attribute::VIRTUAL) || !classDef->TestAttr(CHIR::Attribute::PUBLIC); } } // namespace CGType* CGTypeMgr::GetConcreteCGTypeFor(CGModule& cgMod, const CHIR::Type& chirType, const CGType::TypeExtraInfo& extraInfo, const CHIR::FuncBase* chirFunc) { CGType* cgType = nullptr; auto& cgCtx = cgMod.GetCGContext(); switch (chirType.GetTypeKind()) { case CHIR::Type::TypeKind::TYPE_INT8: case CHIR::Type::TypeKind::TYPE_UINT8: case CHIR::Type::TypeKind::TYPE_INT16: case CHIR::Type::TypeKind::TYPE_UINT16: case CHIR::Type::TypeKind::TYPE_INT32: case CHIR::Type::TypeKind::TYPE_UINT32: case CHIR::Type::TypeKind::TYPE_INT64: case CHIR::Type::TypeKind::TYPE_UINT64: case CHIR::Type::TypeKind::TYPE_INT_NATIVE: case CHIR::Type::TypeKind::TYPE_UINT_NATIVE: case CHIR::Type::TypeKind::TYPE_FLOAT16: case CHIR::Type::TypeKind::TYPE_FLOAT32: case CHIR::Type::TypeKind::TYPE_FLOAT64: case CHIR::Type::TypeKind::TYPE_RUNE: case CHIR::Type::TypeKind::TYPE_BOOLEAN: case CHIR::Type::TypeKind::TYPE_UNIT: case CHIR::Type::TypeKind::TYPE_NOTHING: case CHIR::Type::TypeKind::TYPE_VOID: { cgType = new CGPrimitiveType(cgMod, cgCtx, chirType); break; } case Cangjie::CHIR::Type::TypeKind::TYPE_CSTRING: { cgType = new CGCStringType(cgMod, cgCtx, chirType); break; } case CHIR::Type::TypeKind::TYPE_TUPLE: { cgType = new CGTupleType(cgMod, cgCtx, chirType, extraInfo); break; } case CHIR::Type::TypeKind::TYPE_STRUCT: { cgType = new CGStructType(cgMod, cgCtx, chirType); break; } case CHIR::Type::TypeKind::TYPE_ENUM: { cgType = new CGEnumType(cgMod, cgCtx, chirType); break; } case CHIR::Type::TypeKind::TYPE_FUNC: { cgType = chirFunc ? new CGFunctionType(cgMod, cgCtx, *chirFunc, extraInfo) : new CGFunctionType(cgMod, cgCtx, StaticCast(chirType), extraInfo); break; } case CHIR::Type::TypeKind::TYPE_CLASS: { cgType = new CGClassType(cgMod, cgCtx, StaticCast(chirType)); break; } case CHIR::Type::TypeKind::TYPE_VARRAY: { cgType = new CGVArrayType(cgMod, cgCtx, StaticCast(chirType)); break; } case CHIR::Type::TypeKind::TYPE_RAWARRAY: { cgType = new CGArrayType(cgMod, cgCtx, StaticCast(chirType)); break; } case CHIR::Type::TypeKind::TYPE_REFTYPE: { cgType = new CGRefType(cgMod, cgCtx, chirType, static_cast(extraInfo.addrspace)); break; } case CHIR::Type::TypeKind::TYPE_BOXTYPE: { cgType = new CGBoxType(cgMod, cgCtx, StaticCast(chirType)); break; } case CHIR::Type::TypeKind::TYPE_CPOINTER: { cgType = new CGCPointerType(cgMod, cgCtx, StaticCast(chirType)); break; } case CHIR::Type::TypeKind::TYPE_GENERIC: { cgType = new CGGenericType(cgMod, cgCtx, StaticCast(chirType)); break; } case CHIR::Type::TypeKind::TYPE_THIS: { cgType = new CGThisType(cgMod, cgCtx, StaticCast(chirType)); break; } default: CJC_ASSERT(false && "Should not reach here: not a contract type from chir."); return nullptr; } cgMod.GetCGContext().Add2CGTypePool(cgType); return cgType; } CGType* CGType::GetCGTI(CGModule& cgModule) { return CGTIType::Get(cgModule); } CGType* CGType::GetOrCreate(CGModule& cgModule, const Cangjie::CHIR::Type* chirTy, const TypeExtraInfo& extraInfo) { CJC_NULLPTR_CHECK(chirTy); if (chirTy->IsAutoEnvInstBase()) { chirTy = const_cast(dynamic_cast(chirTy)) ->GetSuperClassTy(&cgModule.GetCGContext().GetCHIRBuilder()); CJC_ASSERT(chirTy->IsAutoEnvGenericBase()); } CGContext& cgContext = cgModule.GetCGContext(); auto& cache = cgContext.impl->chirType2CGTypeMap[extraInfo]; if (auto it = cache.find(chirTy); it != cache.end() && !IsLitStructPtrType(it->second->llvmType)) { return it->second; } /// This is a defense mechanism to report a memory management problem on CHIR. /// Release mode: this problem on CHIR will be covered by codegen, the program won't behave incorrectly. /// Debug mode: the program will abort and an error will be reported. auto& secondCache = cgContext.impl->chirTypeName2CGTypeMap; if (auto it = secondCache.find(chirTy->ToString()); it != secondCache.end() && !IsLitStructPtrType(it->second->llvmType)) { auto [iter, success] = cache.emplace(chirTy, it->second); CJC_ASSERT(success); CJC_ASSERT(false && "It must be a bug on CHIR: an AST type corresponds to different memory address"); return it->second; } // This `cgType` will be released in the de-constructor of `CGContextImpl`. CGType* cgType = CGTypeMgr::GetConcreteCGTypeFor(cgModule, *chirTy, extraInfo); // Add the incomplete `cgType` to prevent infinite recursion into this program point. auto [iter, success] = cache.emplace(chirTy, cgType); if (!success) { cache[chirTy] = cgType; secondCache[chirTy->ToString()] = cgType; } // Complete the `cgType`. cgType->llvmType = cgType->GenLLVMType(); cgType->CalculateSizeAndAlign(); return cgType; } CGType* CGType::GetOrCreateWithNode(CGModule& cgModule, const CHIR::Value* chirNode, bool allowBasePtr, bool forWrapper) { auto chirTy = chirNode->GetType(); CGType* cgType = nullptr; if (DynamicCast(chirNode)) { cgType = CGTypeMgr::GetConcreteCGTypeFor(cgModule, *chirTy, TypeExtraInfo{0, true, false, false, {}}); } else if (auto fb = DynamicCast(chirNode)) { TypeExtraInfo typeExtraInfo(0, false, false, allowBasePtr, {}); typeExtraInfo.forWrapper = forWrapper; for (auto gt : fb->GetGenericTypeParams()) { typeExtraInfo.instantiatedParamTypes.emplace_back(gt); } cgType = CGTypeMgr::GetConcreteCGTypeFor(cgModule, *chirTy, typeExtraInfo, fb); } else { return GetOrCreate(cgModule, chirTy); } // Complete the `cgType`. cgType->llvmType = cgType->GenLLVMType(); cgType->GenContainedCGTypes(); return cgType; } CGType::CGGenericKind CGType::GetCGGenericKind(const CHIR::Type& chirType) { bool hasGenericTypeArg = HasGenericTypeArg(chirType); if (hasGenericTypeArg) { return CGGenericKind::DYNAMIC_GI; } else if (chirType.GetTypeArgs().empty()) { if (auto ct = dynamic_cast(&chirType); ct && !ct->GetGenericArgs().empty()) { CJC_ASSERT(ct->GetCustomTypeDef()->TestAttr(CHIR::Attribute::GENERIC_INSTANTIATED)); return CGGenericKind::STATIC_GI; } return CGGenericKind::CONCRETE; } else { return CGGenericKind::STATIC_GI; } } CGType::CGType(CGModule& cgMod, CGContext& cgCtx, const CHIR::Type& chirType, CGTypeKind cgTypeKind) : cgMod(cgMod), cgCtx(cgCtx), chirType(chirType), cgTypeKind(cgTypeKind) { if (chirType.IsClass() && !chirType.IsAutoEnv()) { inheritedClassNum = cgCtx.GetVTableSizeOf(const_cast(&static_cast(chirType))); } else { inheritedClassNum = 0U; } cgGenericKind = CGType::GetCGGenericKind(chirType); } llvm::GlobalVariable* CGType::GetOrCreateTypeInfo() { CJC_ASSERT(!chirType.IsThis() && "Should not get typeinfo of ThisType."); CJC_ASSERT(!chirType.IsRef() && "Should not get typeinfo of RefType."); if (typeInfo) { return typeInfo; } const auto typeInfoType = CGType::GetOrCreateTypeInfoType(cgMod.GetLLVMContext()); auto tiName = CGType::GetNameOfTypeInfoGV(chirType); if (cgGenericKind == CGGenericKind::STATIC_GI) { if (!cgMod.GetLLVMModule()->getNamedGlobal(tiName)) { cgCtx.RegisterStaticGIName(tiName); } typeInfo = llvm::cast(cgMod.GetLLVMModule()->getOrInsertGlobal(tiName, typeInfoType)); } else if (cgGenericKind == CGGenericKind::CONCRETE) { typeInfo = llvm::cast(cgMod.GetLLVMModule()->getOrInsertGlobal(tiName, typeInfoType)); } else if (cgGenericKind == CGGenericKind::DYNAMIC_GI) { return GetOrCreateGenericCustomTypeInfo(); } cgMod.GetCGContext().AddCodeGenAddedFuncsOrVars(chirType, tiName); CJC_ASSERT(typeInfo && "This type does not have a typeinfo GV, please check the caller."); typeInfo->addAttribute(GC_KLASS_ATTR); if (CanMallocUseFixedSize(chirType)) { typeInfo->addAttribute(GC_CAN_MALLOC_WITH_FIXED_SIZE); } if (!typeInfo->hasInitializer()) { cgMod.DelayGenTypeInfo(this); } return typeInfo; } llvm::GlobalVariable* CGType::GetOrCreateTypeTemplate() { if (typeTemplate) { return typeTemplate; } bool needTT = chirType.IsTuple() || chirType.IsVArray() || chirType.IsRawArray() || chirType.IsCPointer() || chirType.IsBox() || chirType.IsCFunc() || chirType.IsFunc(); if (auto ct = dynamic_cast(&chirType)) { needTT = needTT || !ct->GetGenericArgs().empty(); } if (needTT) { auto typeTemplateType = CGType::GetOrCreateTypeTemplateType(cgMod.GetLLVMContext()); auto ttName = CGType::GetNameOfTypeTemplateGV(chirType); typeTemplate = llvm::cast(cgMod.GetLLVMModule()->getOrInsertGlobal(ttName, typeTemplateType)); cgMod.GetCGContext().AddCodeGenAddedFuncsOrVars(chirType, ttName); } CJC_ASSERT(typeTemplate && "This type does not have a type-template GV, please check the caller."); cgMod.DelayGenTypeTemplate(this); return typeTemplate; } CGType* CGType::GetObjectCGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, cgMod.GetCGContext().GetCHIRBuilder().GetObjectTy()); } CGType* CGType::GetInt8CGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, cgMod.GetCGContext().GetCHIRBuilder().GetInt8Ty()); } CGType* CGType::GetInt8PtrCGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, GetRefTypeOfCHIRInt8(cgMod.GetCGContext().GetCHIRBuilder())); } CGType* CGType::GetInt16CGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, cgMod.GetCGContext().GetCHIRBuilder().GetInt16Ty()); } CGType* CGType::GetInt32CGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, cgMod.GetCGContext().GetCHIRBuilder().GetInt32Ty()); } CGType* CGType::GetInt64CGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, cgMod.GetCGContext().GetCHIRBuilder().GetInt64Ty()); } CGType* CGType::GetUInt8CGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, cgMod.GetCGContext().GetCHIRBuilder().GetUInt8Ty()); } CGType* CGType::GetUInt16CGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, cgMod.GetCGContext().GetCHIRBuilder().GetUInt16Ty()); } CGType* CGType::GetUInt32CGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, cgMod.GetCGContext().GetCHIRBuilder().GetUInt32Ty()); } CGType* CGType::GetUInt64CGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, cgMod.GetCGContext().GetCHIRBuilder().GetUInt64Ty()); } CGType* CGType::GetFloat16CGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, cgMod.GetCGContext().GetCHIRBuilder().GetFloat16Ty()); } CGType* CGType::GetFloat32CGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, cgMod.GetCGContext().GetCHIRBuilder().GetFloat32Ty()); } CGType* CGType::GetFloat64CGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, cgMod.GetCGContext().GetCHIRBuilder().GetFloat64Ty()); } CGType* CGType::GetUnitCGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, cgMod.GetCGContext().GetCHIRBuilder().GetUnitTy()); } CGType* CGType::GetNothingCGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, cgMod.GetCGContext().GetCHIRBuilder().GetNothingType()); } CGType* CGType::GetZeroSizedCGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, cgMod.GetCGContext().GetCHIRBuilder().GetZeroSizedTy()); } CGType* CGType::GetBoolCGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, cgMod.GetCGContext().GetCHIRBuilder().GetBoolTy()); } CGType* CGType::GetCStringCGType(CGModule& cgMod) { return CGType::GetOrCreate(cgMod, cgMod.GetCGContext().GetCHIRBuilder().GetCStringTy()); } llvm::Type* CGType::GetUnitType(llvm::LLVMContext& llvmCtx) { auto unitType = llvm::StructType::getTypeByName(llvmCtx, UNIT_TYPE_STR); if (!unitType) { unitType = llvm::StructType::create(llvmCtx, {}, UNIT_TYPE_STR); } return unitType; } llvm::Type* CGType::GetRefType(llvm::LLVMContext& llvmCtx) { return llvm::Type::getInt8PtrTy(llvmCtx, 1U); } llvm::Type* CGType::GetLandingPadType(llvm::LLVMContext& llvmCtx) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND return llvm::Type::getTokenTy(llvmCtx); #endif } llvm::Type* CGType::GetArrayBaseType(CGContext& cgCtx) { auto& llvmCtx = cgCtx.GetLLVMContext(); auto arrBaseType = llvm::StructType::getTypeByName(llvmCtx, "ArrayBase"); if (arrBaseType == nullptr) { arrBaseType = llvm::StructType::create(llvmCtx, "ArrayBase"); std::vector bodyVec(static_cast(ARRAY_BASE_FIELDS_NUM)); bodyVec[static_cast(ARRAY_BASE_ARRAY_LEN)] = llvm::Type::getInt64Ty(llvmCtx); SetStructTypeBody(arrBaseType, bodyVec); } return arrBaseType; } llvm::Type* CGType::GetBitMapType(llvm::LLVMContext& llvmCtx) { auto bitMapType = llvm::StructType::getTypeByName(llvmCtx, "BitMap"); if (bitMapType == nullptr) { bitMapType = llvm::StructType::create(llvmCtx, "BitMap"); std::vector bodyVec(static_cast(BITMAP_FIELDS_NUM)); bodyVec[static_cast(BITMAP_NUMS)] = llvm::Type::getInt32Ty(llvmCtx); bodyVec[static_cast(BITMAP_BITMAP)] = llvm::ArrayType::get(llvm::Type::getInt8Ty(llvmCtx), 0); SetStructTypeBody(bitMapType, bodyVec); } return bitMapType; } llvm::FunctionType* CGType::GetCodeGenFunctionType( llvm::LLVMContext& llvmCtx, llvm::Type* retType, std::vector& params) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (IsPassedByReference(retType)) { params.insert(params.begin(), retType->getPointerTo()); return llvm::FunctionType::get(llvm::Type::getVoidTy(llvmCtx), params, false); } #endif return llvm::FunctionType::get(retType, params, false); } // ObjLayout.EnumCase = type { i32, ... } // ObjLayout.OptionLike<> = type { i1 } llvm::StructType* CGType::GetOrCreateEnumCtorLayoutType(CGModule& cgModule, const CHIR::EnumType& enumTy, std::size_t ctorIndex, const std::vector& paramTypes) { auto className = enumTy.GetEnumDef()->GetPackageName() + ':' + GetCustomTypeDefIdentifier(*enumTy.GetEnumDef()) + ':' + std::to_string(ctorIndex); if (!enumTy.GetTypeArgs().empty()) { className = className + '<' + GetTypeArgsQualifiedName(enumTy.GetTypeArgs()) + '>'; } auto enumCtorLayoutType = llvm::StructType::getTypeByName(cgModule.GetLLVMContext(), GetClassObjLayoutName(className)); if (enumCtorLayoutType) { return enumCtorLayoutType; } // insert size of constructor at the beginning std::vector fieldsTypes; StaticCast(CGType::GetOrCreate(cgModule, &enumTy))->IsOptionLike() ? fieldsTypes.emplace_back(cgModule.GetCGContext().GetCHIRBuilder().GetBoolTy()) : fieldsTypes.emplace_back(cgModule.GetCGContext().GetCHIRBuilder().GetInt32Ty()); fieldsTypes.insert(fieldsTypes.end(), paramTypes.cbegin(), paramTypes.cend()); return GetLLVMStructType(cgModule, fieldsTypes, GetClassObjLayoutName(className)); } CHIR::RefType* CGType::GetRefTypeOf(CHIR::CHIRBuilder& chirBuilder, const CHIR::Type& typeOfCHIR) { return chirBuilder.GetType(const_cast(&typeOfCHIR)); } CHIR::RefType* CGType::GetRefTypeOfCHIRInt8(CHIR::CHIRBuilder& chirBuilder) { return GetRefTypeOf(chirBuilder, *chirBuilder.GetInt8Ty()); } llvm::StructType* CGType::GetOrCreateTypeInfoType(llvm::LLVMContext& llvmCtx) { auto typeInfoType = llvm::StructType::getTypeByName(llvmCtx, "TypeInfo"); if (typeInfoType == nullptr) { auto i8Ty = llvm::Type::getInt8Ty(llvmCtx); auto i16Ty = llvm::Type::getInt16Ty(llvmCtx); auto i32Ty = llvm::Type::getInt32Ty(llvmCtx); auto i8PtrTy = llvm::Type::getInt8PtrTy(llvmCtx); typeInfoType = llvm::StructType::create(llvmCtx, "TypeInfo"); std::vector bodyVec(static_cast(TYPE_INFO_FIELDS_NUM)); bodyVec[static_cast(TYPEINFO_NAME)] = i8PtrTy; bodyVec[static_cast(TYPEINFO_TYPE_KIND)] = i8Ty; bodyVec[static_cast(TYPEINFO_FIELDS_NUM)] = i16Ty; bodyVec[static_cast(TYPEINFO_FIELDS)] = i8PtrTy; bodyVec[static_cast(TYPEINFO_SIZE)] = i32Ty; bodyVec[static_cast(TYPEINFO_UUID)] = i32Ty; bodyVec[static_cast(TYPEINFO_ALIGN)] = i8Ty; bodyVec[static_cast(TYPEINFO_SOURCE_GENERIC)] = i8PtrTy; bodyVec[static_cast(TYPEINFO_TYPE_ARGS_NUM)] = i8Ty; bodyVec[static_cast(TYPEINFO_INHERITED_CLASS_NUM)] = i16Ty; bodyVec[static_cast(TYPEINFO_OFFSETS)] = i32Ty->getPointerTo(); bodyVec[static_cast(TYPEINFO_TYPE_ARGS)] = i8PtrTy; bodyVec[static_cast(TYPEINFO_SUPER)] = typeInfoType->getPointerTo(); bodyVec[static_cast(TYPEINFO_EXTENSIONDEF_PTR)] = CGType::GetOrCreateExtensionDefPtrType(llvmCtx)->getPointerTo(); bodyVec[static_cast(TYPEINFO_MTABLE)] = i8PtrTy; bodyVec[static_cast(TYPEINFO_REFLECTION)] = i8PtrTy; bodyVec[static_cast(TYPEINFO_GC_TIB)] = CGType::GetBitMapType(llvmCtx)->getPointerTo(); bodyVec[static_cast(TYPEINFO_FLAG)] = i8Ty; SetStructTypeBody(typeInfoType, bodyVec); } return typeInfoType; } llvm::PointerType* CGType::GetOrCreateTypeInfoPtrType(llvm::LLVMContext& llvmCtx) { return CGType::GetOrCreateTypeInfoType(llvmCtx)->getPointerTo(); } llvm::StructType* CGType::GetOrCreateTypeTemplateType(llvm::LLVMContext& llvmCtx) { auto typeTemplateType = llvm::StructType::getTypeByName(llvmCtx, "TypeTemplate"); if (typeTemplateType == nullptr) { auto i8Ty = llvm::Type::getInt8Ty(llvmCtx); auto i16Ty = llvm::Type::getInt16Ty(llvmCtx); auto i8PtrTy = llvm::Type::getInt8PtrTy(llvmCtx); typeTemplateType = llvm::StructType::create(llvmCtx, "TypeTemplate"); std::vector bodyVec(static_cast(TYPE_TEMPLATE_FIELDS_NUM)); bodyVec[static_cast(TYPETEMPLATE_NAME)] = i8PtrTy; bodyVec[static_cast(TYPETEMPLATE_TYPE_KIND)] = i8Ty; bodyVec[static_cast(TYPETEMPLATE_TYPE_ARGS_NUM)] = i16Ty; bodyVec[static_cast(TYPETEMPLATE_FIELDS_NUM)] = i16Ty; bodyVec[static_cast(TYPETEMPLATE_FIELDS_FNS)] = i8PtrTy; bodyVec[static_cast(TYPETEMPLATE_SUPER_FN)] = i8PtrTy; bodyVec[static_cast(TYPETEMPLATE_FINALIZER)] = i8PtrTy; bodyVec[static_cast(TYPETEMPLATE_REFLECTION)] = i8PtrTy; bodyVec[static_cast(TYPETEMPLATE_FLAG)] = i8Ty; bodyVec[static_cast(TYPETEMPLATE_EXTENSIONDEF_PTR)] = CGType::GetOrCreateExtensionDefPtrType(llvmCtx)->getPointerTo(); bodyVec[static_cast(TYPETEMPLATE_INHERITED_CLASS_NUM)] = i16Ty; SetStructTypeBody(typeTemplateType, bodyVec); } return typeTemplateType; } llvm::StructType* CGType::GetOrCreateExtensionDefType(llvm::LLVMContext& llvmCtx) { auto extensionDefType = llvm::StructType::getTypeByName(llvmCtx, "ExtensionDef"); if (extensionDefType == nullptr) { auto i32Ty = llvm::Type::getInt32Ty(llvmCtx); auto i8PtrTy = llvm::Type::getInt8PtrTy(llvmCtx); extensionDefType = llvm::StructType::create(llvmCtx, "ExtensionDef"); std::vector bodyVec(static_cast(EXTENSION_DEF_FIELDS_NUM)); bodyVec[static_cast(TYPE_PARAM_COUNT)] = i32Ty; bodyVec[static_cast(IS_INTERFACE_TI)] = llvm::Type::getInt8Ty(llvmCtx); bodyVec[static_cast(TARGET_TYPE)] = i8PtrTy; bodyVec[static_cast(INTERFACE_FN_OR_INTERFACE_TI)] = i8PtrTy; bodyVec[static_cast(FUNC_TABLE)] = i8PtrTy; bodyVec[static_cast(WHERE_CONDITION_FN)] = i8PtrTy; SetStructTypeBody(extensionDefType, bodyVec); } return extensionDefType; } llvm::PointerType* CGType::GetOrCreateExtensionDefPtrType(llvm::LLVMContext& llvmCtx) { return GetOrCreateExtensionDefType(llvmCtx)->getPointerTo(); } std::string CGType::GetNameOfTypeInfoGV(const CHIR::Type& chirType) { CJC_ASSERT(!chirType.IsRef() && "RefType should not reach here."); return GetTypeQualifiedName(chirType) + ".ti"; } std::string CGType::GetNameOfTypeTemplateGV(const CHIR::Type& chirType) { CJC_ASSERT(!chirType.IsRef() && "RefType should not reach here."); if (chirType.IsTuple()) { return "Tuple.tt"; } else if (chirType.IsRawArray()) { return "RawArray.tt"; } else if (chirType.IsVArray()) { return "VArray.tt"; } else if (chirType.IsCPointer()) { return "CPointer.tt"; } else if (auto ct = dynamic_cast(&chirType)) { if (ct->IsAutoEnvBase()) { CJC_ASSERT(!ct->IsAutoEnvInstBase()); return "Closure.tt"; } auto def = ct->GetCustomTypeDef(); auto packageName = def->GetGenericDecl() ? def->GetGenericDecl()->GetPackageName() : def->GetPackageName(); auto defShortName = CHIR::GetCustomTypeIdentifier(*ct); return packageName + ":" + defShortName + ".tt"; } else if (chirType.IsCFunc() || chirType.IsFunc()) { return "CFunc.tt"; } else if (chirType.IsBox()) { return "Box.tt"; } CJC_ASSERT(false && "Unsupported"); return "Unsupported"; } void CGType::GenTypeInfo() { CJC_ASSERT(!IsDynamicGI() && "Dynamic type should not reach here."); CJC_ASSERT(typeInfo); PreActionOfGenTypeInfo(); auto& llvmCtx = cgMod.GetLLVMContext(); if (layoutType && !typeInfo->hasMetadata(GC_TYPE_META_NAME)) { auto structName = layoutType->getStructName(); auto meta = llvm::MDTuple::get(llvmCtx, {llvm::MDString::get(llvmCtx, structName)}); typeInfo->setMetadata(GC_TYPE_META_NAME, meta); cgCtx.AddGeneratedStructType(structName.str()); } if (typeInfo->hasInitializer()) { return; } if (chirType.IsPrimitive() || chirType.IsVoid() || chirType.IsCString()) { return; } if (IsConcrete() && cgMod.GetCGContext().IsCustomTypeOfOtherLLVMModule(chirType)) { auto customType = dynamic_cast(&chirType); if (customType && customType->GetCustomTypeDef()->Get() == Linkage::EXTERNAL_WEAK) { typeInfo->setLinkage(llvm::GlobalValue::ExternalWeakLinkage); } return; } std::vector typeInfoVec(TYPE_INFO_FIELDS_NUM); typeInfoVec[static_cast(TYPEINFO_NAME)] = GenNameOfTypeInfo(); typeInfoVec[static_cast(TYPEINFO_TYPE_KIND)] = GenKindOfTypeInfo(); typeInfoVec[static_cast(TYPEINFO_FIELDS_NUM)] = GenFieldsNumOfTypeInfo(); typeInfoVec[static_cast(TYPEINFO_FIELDS)] = GenFieldsOfTypeInfo(); typeInfoVec[static_cast(TYPEINFO_SIZE)] = GenSizeOfTypeInfo(); typeInfoVec[static_cast(TYPEINFO_UUID)] = llvm::Constant::getNullValue(llvm::Type::getInt32Ty(llvmCtx)); typeInfoVec[static_cast(TYPEINFO_ALIGN)] = GenAlignOfTypeInfo(); typeInfoVec[static_cast(TYPEINFO_SOURCE_GENERIC)] = GenSourceGenericOfTypeInfo(); typeInfoVec[static_cast(TYPEINFO_TYPE_ARGS_NUM)] = GenTypeArgsNumOfTypeInfo(); typeInfoVec[static_cast(TYPEINFO_OFFSETS)] = GenOffsetsOfTypeInfo(); typeInfoVec[static_cast(TYPEINFO_TYPE_ARGS)] = GenTypeArgsOfTypeInfo(); typeInfoVec[static_cast(TYPEINFO_SUPER)] = GenSuperOfTypeInfo(); typeInfoVec[static_cast(TYPEINFO_EXTENSIONDEF_PTR)] = GenExtensionDefPtrOfTypeInfo(); // TYPEINFO_MTABLE must before TYPEINFO_INHERITED_CLASS_NUM typeInfoVec[static_cast(TYPEINFO_MTABLE)] = GenMTableOfTypeInfo(); typeInfoVec[static_cast(TYPEINFO_INHERITED_CLASS_NUM)] = GenInheritedClassNumOfTypeInfo(); typeInfoVec[static_cast(TYPEINFO_REFLECTION)] = GenReflectionOfTypeInfo(); typeInfoVec[static_cast(TYPEINFO_GC_TIB)] = llvm::ConstantPointerNull::get(CGType::GetBitMapType(llvmCtx)->getPointerTo()); typeInfoVec[static_cast(TYPEINFO_FLAG)] = GenFlagOfTypeInfo(); typeInfo->setInitializer(llvm::ConstantStruct::get(CGType::GetOrCreateTypeInfoType(llvmCtx), typeInfoVec)); if (IsStaticGI()) { typeInfo->setLinkage(llvm::GlobalValue::InternalLinkage); } else { // For Concrete type: // Note: The chirType that enters this branch is expected to be of CustomType. auto customType = dynamic_cast(&chirType); CJC_ASSERT(customType && "The chirType that enters this branch is expected to be of CustomType."); auto linkageType = CHIRLinkage2LLVMLinkage(customType->GetCustomTypeDef()->Get()); AddLinkageTypeMetadata(*typeInfo, linkageType, cgMod.GetCGContext().IsCGParallelEnabled()); } PostActionOfGenTypeInfo(); } llvm::Constant* CGType::GenInheritedClassNumOfTypeInfo() const { return llvm::ConstantInt::get( llvm::Type::getInt16Ty(cgCtx.GetLLVMContext()), inheritedClassNum | INHERITED_CLASS_NUM_FE_FLAG); } llvm::Constant* CGType::GenExtensionDefPtrOfTypeInfo() const { auto& llvmCtx = cgCtx.GetLLVMContext(); if (typeTemplate && typeTemplate->hasInitializer()) { return llvm::cast(typeTemplate->getInitializer()) ->getAggregateElement(TYPETEMPLATE_EXTENSIONDEF_PTR); } if (!cgExtensionDef) { return llvm::ConstantPointerNull::get(CGType::GetOrCreateExtensionDefPtrType(llvmCtx)->getPointerTo()); } auto extensionDefs = cgMod.GetLLVMModule()->getNamedGlobal( CodeGen::IsExternalDefinedType(chirType) ? "ExternalExtensionDefs" : "NonExternalExtensionDefs"); llvm::Constant* indices[] = {llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvmCtx), 0), llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvmCtx), cgExtensionDef->GetStartIdx())}; return llvm::ConstantExpr::getInBoundsGetElementPtr(extensionDefs->getValueType(), extensionDefs, indices); } llvm::Constant* CGType::GenMTableOfTypeInfo() { auto& llvmCtx = cgCtx.GetLLVMContext(); auto targetDefType = chirType.IsNominal() ? static_cast(chirType).GetCustomTypeDef()->GetType() : &chirType; cgExtensionDef = CGType::GetOrCreate(cgMod, targetDefType)->cgExtensionDef; if ((inheritedClassNum >> 14U) != 0 || !cgExtensionDef) { return llvm::ConstantPointerNull::get(llvm::Type::getInt8PtrTy(llvmCtx)); } auto& extendInterfaces = cgExtensionDef->GetExtendInterfaces(); constexpr size_t bitsPerByte = 8U; if (cgCtx.GetCompileOptions().target.arch != Triple::ArchType::ARM32 && extendInterfaces.size() < sizeof(void*) * bitsPerByte - 1U) { std::string flag(extendInterfaces.size(), '0'); if (chirType.GetTypeArgs().empty()) { flag = std::string(extendInterfaces.size(), '1'); } else { uint8_t idx = 0U; bool foundFirstCandidate = false; for (auto& it : extendInterfaces) { if (it.second == nullptr || chirType.IsEqualOrInstantiatedTypeOf(*it.second, cgCtx.GetCHIRBuilder())) { flag[idx] = '1'; foundFirstCandidate = true; } if (foundFirstCandidate) { ++idx; } else { ++inheritedClassNum; } } } return cgMod.GetOrInsertBitMap(flag); } return llvm::ConstantPointerNull::get(llvm::Type::getInt8PtrTy(llvmCtx)); } llvm::Constant* CGType::GenNameOfTypeInfo() { return cgMod.GenerateTypeNameConstantString(GetTypeQualifiedName(chirType, true), false); } llvm::Constant* CGType::GenKindOfTypeInfo() { static const std::unordered_map chirType2TypeInfoKind = { {CHIR::Type::TypeKind::TYPE_RAWARRAY, UGTypeKind::UG_RAWARRAY}, {CHIR::Type::TypeKind::TYPE_FUNC, UGTypeKind::UG_CFUNC}, {CHIR::Type::TypeKind::TYPE_VOID, UGTypeKind::UG_UNIT}, {CHIR::Type::TypeKind::TYPE_UNIT, UGTypeKind::UG_UNIT}, {CHIR::Type::TypeKind::TYPE_BOOLEAN, UGTypeKind::UG_BOOLEAN}, {CHIR::Type::TypeKind::TYPE_RUNE, UGTypeKind::UG_RUNE}, {CHIR::Type::TypeKind::TYPE_UINT8, UGTypeKind::UG_UINT8}, {CHIR::Type::TypeKind::TYPE_UINT16, UGTypeKind::UG_UINT16}, {CHIR::Type::TypeKind::TYPE_UINT32, UGTypeKind::UG_UINT32}, {CHIR::Type::TypeKind::TYPE_UINT64, UGTypeKind::UG_UINT64}, {CHIR::Type::TypeKind::TYPE_UINT_NATIVE, UGTypeKind::UG_UINT_NATIVE}, {CHIR::Type::TypeKind::TYPE_INT8, UGTypeKind::UG_INT8}, {CHIR::Type::TypeKind::TYPE_INT16, UGTypeKind::UG_INT16}, {CHIR::Type::TypeKind::TYPE_INT32, UGTypeKind::UG_INT32}, {CHIR::Type::TypeKind::TYPE_INT64, UGTypeKind::UG_INT64}, {CHIR::Type::TypeKind::TYPE_INT_NATIVE, UGTypeKind::UG_INT_NATIVE}, {CHIR::Type::TypeKind::TYPE_FLOAT16, UGTypeKind::UG_FLOAT16}, {CHIR::Type::TypeKind::TYPE_FLOAT32, UGTypeKind::UG_FLOAT32}, {CHIR::Type::TypeKind::TYPE_FLOAT64, UGTypeKind::UG_FLOAT64}, {CHIR::Type::TypeKind::TYPE_NOTHING, UGTypeKind::UG_NOTHING}, {CHIR::Type::TypeKind::TYPE_TUPLE, UGTypeKind::UG_TUPLE}, {CHIR::Type::TypeKind::TYPE_STRUCT, UGTypeKind::UG_STRUCT}, {CHIR::Type::TypeKind::TYPE_ENUM, UGTypeKind::UG_ENUM}, {CHIR::Type::TypeKind::TYPE_VARRAY, UGTypeKind::UG_VARRAY}, {CHIR::Type::TypeKind::TYPE_CPOINTER, UGTypeKind::UG_CPOINTER}, {CHIR::Type::TypeKind::TYPE_CSTRING, UGTypeKind::UG_CSTRING}, {CHIR::Type::TypeKind::TYPE_BOXTYPE, UGTypeKind::UG_CLASS}, {CHIR::Type::TypeKind::TYPE_GENERIC, UGTypeKind::UG_GENERIC}}; CJC_ASSERT(!chirType.IsRef() && "Unexpected CHIR type to generate typeinfo."); unsigned typeInfoKind; if (chirType.IsAutoEnvBase()) { typeInfoKind = UGTypeKind::UG_FUNC; } else if (chirType.IsClass()) { auto classDef = StaticCast(chirType).GetClassDef(); if (IsWeakRefClass(*classDef)) { typeInfoKind = UGTypeKind::UG_WEAKREF; } else { typeInfoKind = classDef->IsClass() ? UGTypeKind::UG_CLASS : UGTypeKind::UG_INTERFACE; } } else if (chirType.IsEnum()) { typeInfoKind = static_cast(this)->IsCommonEnum() ? UGTypeKind::UG_COMMON_ENUM : UGTypeKind::UG_ENUM; } else { typeInfoKind = chirType2TypeInfoKind.at(chirType.GetTypeKind()); } return llvm::ConstantInt::get(llvm::Type::getInt8Ty(cgMod.GetLLVMContext()), typeInfoKind); } llvm::Constant* CGType::GenFieldsNumOfTypeInfo() { return llvm::ConstantInt::get(llvm::Type::getInt16Ty(cgMod.GetLLVMContext()), 0); } llvm::Constant* CGType::GenFieldsOfTypeInfo() { return llvm::ConstantPointerNull::get(llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext())); } llvm::Constant* CGType::GenSizeOfTypeInfo() { return llvm::ConstantInt::get(llvm::Type::getInt32Ty(cgMod.GetLLVMContext()), GetSize().value_or(0)); } llvm::Constant* CGType::GenAlignOfTypeInfo() { return llvm::ConstantInt::get(llvm::Type::getInt8Ty(cgMod.GetLLVMContext()), GetAlign().value_or(0)); } llvm::Constant* CGType::GenSourceGenericOfTypeInfo() { auto p0i8 = llvm::Type::getInt8PtrTy(cgCtx.GetLLVMContext()); if (IsStaticGI()) { return llvm::ConstantExpr::getBitCast(GetOrCreateTypeTemplate(), p0i8); } return llvm::ConstantPointerNull::get(p0i8); } llvm::Constant* CGType::GenTypeArgsNumOfTypeInfo() { return llvm::ConstantInt::get(llvm::Type::getInt8Ty(cgMod.GetLLVMContext()), 0); } llvm::Constant* CGType::GenOffsetsOfTypeInfo() { return llvm::ConstantPointerNull::get(llvm::Type::getInt32PtrTy(cgMod.GetLLVMContext())); } llvm::Constant* CGType::GenTypeArgsOfTypeInfo() { return llvm::ConstantPointerNull::get(llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext())); } llvm::Constant* CGType::GenSuperOfTypeInfo() { return llvm::ConstantPointerNull::get(CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext())); } llvm::Constant* CGType::GenFlagOfTypeInfo() { return llvm::ConstantInt::get(llvm::Type::getInt8Ty(cgMod.GetLLVMContext()), 0); } llvm::Constant* CGType::GenReflectionOfTypeInfo() { return llvm::ConstantPointerNull::get(llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext())); } CGTIType::CGTIType(CGModule& cgMod) : CGType(cgMod, cgMod.GetCGContext(), *CGType::GetRefTypeOfCHIRInt8(cgMod.GetCGContext().GetCHIRBuilder()), CGTypeKind::CG_TI) { llvmType = CGType::GetOrCreateTypeInfoType(cgMod.GetLLVMContext()); } llvm::Type* CGTIType::GenLLVMType() { return llvmType; } std::optional CGType::GetSize() const { return size; } bool CGType::IsOptionLikeRef() const { return IsCGEnum() && StaticCast(this)->IsOptionLikeRef(); } bool CGType::IsReference() const { if (chirType.IsRef() && (chirType.GetTypeArgs()[0]->IsClass() || chirType.GetTypeArgs()[0]->IsRawArray() || chirType.GetTypeArgs()[0]->IsBox())) { return true; } if (chirType.IsBox() || chirType.IsClass() || chirType.IsRawArray()) { return true; } if (chirType.IsEnum()) { return static_cast(this)->IsCommonEnum(); } if (chirType.IsGeneric()) { auto& gt = static_cast(chirType); for (auto upperBound : gt.GetUpperBounds()) { if (CGType::GetOrCreate(cgMod, upperBound)->IsReference()) { return true; } } } return false; } // for reflection only llvm::StructType* CGType::GetOrCreateGenericTypeInfoType(llvm::LLVMContext& llvmCtx) { auto genericTypeInfoType = llvm::StructType::getTypeByName(llvmCtx, "GenericTypeInfo"); if (genericTypeInfoType == nullptr) { genericTypeInfoType = llvm::StructType::create(llvmCtx, "GenericTypeInfo"); std::vector bodyVec(static_cast(GENERIC_TYPE_INFO_FIELDS_NUM)); bodyVec[static_cast(GENERIC_TYPEINFO_NAME)] = llvm::Type::getInt8PtrTy(llvmCtx); bodyVec[static_cast(GENERIC_TYPEINFO_TYPE_KIND)] = llvm::Type::getInt8Ty(llvmCtx); bodyVec[static_cast(GENERIC_TYPEINFO_UPPERBOUNDS_NUM)] = llvm::Type::getInt32Ty(llvmCtx); bodyVec[static_cast(GENERIC_TYPEINFO_UPPERBOUNDS)] = llvm::Type::getInt8PtrTy(llvmCtx); SetStructTypeBody(genericTypeInfoType, bodyVec); } return genericTypeInfoType; } llvm::StructType* CGType::GetOrCreateGenericCustomTypeInfoType(llvm::LLVMContext& llvmCtx) { auto genericCustomTypeInfoType = llvm::StructType::getTypeByName(llvmCtx, "GenericCustomTypeInfo"); if (genericCustomTypeInfoType == nullptr) { genericCustomTypeInfoType = llvm::StructType::create(llvmCtx, "GenericCustomTypeInfo"); std::vector bodyVec(static_cast(GENERIC_CUSTOM_TYPE_INFO_FIELDS_NUM)); auto i8PtrTy = llvm::Type::getInt8PtrTy(llvmCtx); auto i8Ty = llvm::Type::getInt8Ty(llvmCtx); bodyVec[static_cast(GENERIC_CUSTOM_TYPEINFO_NAME)] = i8PtrTy; bodyVec[static_cast(GENERIC_CUSTOM_TYPEINFO_TYPE_KIND)] = i8Ty; bodyVec[static_cast(GENERIC_CUSTOM_TYPEINFO_TYPE_ARGS_NUM)] = i8Ty; bodyVec[static_cast(GENERIC_CUSTOM_TYPEINFO_TYPE_ARGS)] = i8PtrTy; bodyVec[static_cast(GENERIC_CUSTOM_TYPEINFO_SOURCE_GENERIC)] = i8PtrTy; SetStructTypeBody(genericCustomTypeInfoType, bodyVec); } return genericCustomTypeInfoType; } namespace { using ChirTypeKind = Cangjie::CHIR::Type::TypeKind; std::string GetTypeQualifiedNameForReflect(CGModule& cgMod, const CHIR::Type& t, bool forNameFieldOfTi); std::string GetTypeArgsQualifiedNameForReflect( CGModule& cgMod, const std::vector& typeArgs, bool forNameFieldOfTi) { std::string res; for (auto typeArg : typeArgs) { res += GetTypeQualifiedNameForReflect(cgMod, *typeArg, forNameFieldOfTi) + ','; } if (!typeArgs.empty()) { res.pop_back(); // The last ',' is redundant. } return res; } std::string GetTypeQualifiedNameOfCustomTypeForReflect( CGModule& cgMod, const CHIR::CustomType& type, bool forNameFieldOfTi) { CJC_ASSERT(!type.IsAutoEnvInstBase()); std::stringstream ss; if (type.IsAutoEnvGenericBase()) { auto typeArgs = type.GetGenericArgs(); CJC_ASSERT(!typeArgs.empty()); auto retIter = typeArgs.end() - 1U; std::vector paramType(typeArgs.begin(), retIter); ss << CHIR::MANGLE_CLOSURE_STR << '<'; ss << '(' << GetTypeArgsQualifiedNameForReflect(cgMod, paramType, true) << ")->" << GetTypeQualifiedNameForReflect(cgMod, **retIter, true); ss << '>'; } else { auto def = type.GetCustomTypeDef(); auto packageName = def->GetGenericDecl() ? def->GetGenericDecl()->GetPackageName() : def->GetPackageName(); ss << packageName << ':' << GetCustomTypeIdentifier(type); if (auto typeArgs = type.GetGenericArgs(); !typeArgs.empty()) { ss << '<' << GetTypeArgsQualifiedNameForReflect(cgMod, typeArgs, forNameFieldOfTi) << '>'; } } return ss.str(); } std::string GetTypeQualifiedNameForReflect(CGModule& cgMod, const CHIR::Type& t, bool forNameFieldOfTi = false) { if (t.IsPrimitive()) { return t.ToString(); } auto k = t.GetTypeKind(); CJC_ASSERT(k != ChirTypeKind::TYPE_INVALID); switch (k) { case ChirTypeKind::TYPE_RAWARRAY: return CHIR::MANGLE_RAWARR_STR + GetTypeQualifiedNameForReflect(cgMod, *t.GetTypeArgs()[0], forNameFieldOfTi) + '>'; case ChirTypeKind::TYPE_REFTYPE: return GetTypeQualifiedNameForReflect( cgMod, *static_cast(t).GetBaseType(), forNameFieldOfTi); case ChirTypeKind::TYPE_VARRAY: { auto& varrType = StaticCast(t); return CHIR::MANGLE_VARR_STR + GetTypeQualifiedNameForReflect(cgMod, *varrType.GetElementType(), forNameFieldOfTi) + ',' + std::to_string(varrType.GetSize()) + '>'; } case ChirTypeKind::TYPE_CPOINTER: return CHIR::MANGLE_CPTR_STR + GetTypeQualifiedNameForReflect(cgMod, *t.GetTypeArgs()[0], forNameFieldOfTi) + '>'; case ChirTypeKind::TYPE_CSTRING: return CHIR::MANGLE_CSTRING_STR; case ChirTypeKind::TYPE_TUPLE: { auto& type = static_cast(t); return CHIR::MANGLE_TUPLE_STR + GetTypeArgsQualifiedNameForReflect(cgMod, type.GetElementTypes(), forNameFieldOfTi) + '>'; } case ChirTypeKind::TYPE_STRUCT: case ChirTypeKind::TYPE_ENUM: case ChirTypeKind::TYPE_CLASS: { auto& type = static_cast(t); return GetTypeQualifiedNameOfCustomTypeForReflect(cgMod, type, forNameFieldOfTi); } case ChirTypeKind::TYPE_FUNC: { auto& ft = static_cast(t); std::string name = '(' + GetTypeArgsQualifiedNameForReflect(cgMod, ft.GetParamTypes(), forNameFieldOfTi) + ")->" + GetTypeQualifiedNameForReflect(cgMod, *ft.GetReturnType(), forNameFieldOfTi); return name; } case ChirTypeKind::TYPE_GENERIC: { auto genericTypeName = StaticCast(t).GetSrcCodeIdentifier(); auto upperBounds = StaticCast(t).GetUpperBounds(); return cgMod.GetCGContext().GetGenericTypeUniqueName(genericTypeName, upperBounds); } case ChirTypeKind::TYPE_VOID: return CHIR::MANGLE_UNIT_STR; case ChirTypeKind::TYPE_BOXTYPE: { auto& type = static_cast(t); auto typeQualifiedName = GetTypeQualifiedNameForReflect(cgMod, *type.GetBaseType(), forNameFieldOfTi); return forNameFieldOfTi ? typeQualifiedName : (CHIR::MANGLE_BOX_STR + typeQualifiedName + ">"); } default: CJC_ASSERT(false && "Should not reach here."); return ""; } } } // namespace llvm::GlobalVariable* CGType::GetOrCreateGenericCustomTypeInfo() { auto qualifiedName = GetTypeQualifiedNameForReflect(cgMod, chirType, true); if (auto found = cgMod.GetLLVMModule()->getNamedGlobal(qualifiedName + ".ti"); found) { return found; } auto genericCustomTypeInfoType = GetOrCreateGenericCustomTypeInfoType(cgMod.GetLLVMContext()); auto genericCustomTypeInfo = llvm::cast( cgMod.GetLLVMModule()->getOrInsertGlobal(qualifiedName + ".ti", genericCustomTypeInfoType)); auto i8PtrTy = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); unsigned typeInfoKind = UGTypeKind::UG_GENERIC_CUSTOM; std::vector typeInfoVec(GENERIC_CUSTOM_TYPE_INFO_FIELDS_NUM); typeInfoVec[static_cast(GENERIC_CUSTOM_TYPEINFO_NAME)] = cgMod.GenerateTypeNameConstantString(GetTypeQualifiedName(chirType, true), false); typeInfoVec[static_cast(GENERIC_CUSTOM_TYPEINFO_TYPE_KIND)] = llvm::ConstantInt::get(llvm::Type::getInt8Ty(cgMod.GetLLVMContext()), typeInfoKind); typeInfoVec[static_cast(GENERIC_CUSTOM_TYPEINFO_TYPE_ARGS_NUM)] = GenTypeArgsNumOfTypeInfo(); typeInfoVec[static_cast(GENERIC_CUSTOM_TYPEINFO_TYPE_ARGS)] = GenTypeArgsOfGenericCustomTypeInfo(); typeInfoVec[static_cast(GENERIC_CUSTOM_TYPEINFO_SOURCE_GENERIC)] = llvm::ConstantExpr::getBitCast(GetOrCreateTypeTemplate(), i8PtrTy); genericCustomTypeInfo->setInitializer(llvm::ConstantStruct::get(genericCustomTypeInfoType, typeInfoVec)); genericCustomTypeInfo->setLinkage(llvm::GlobalValue::PrivateLinkage); genericCustomTypeInfo->addAttribute(GENERIC_TYPEINFO_ATTR); return genericCustomTypeInfo; } llvm::Constant* CGType::GenTypeArgsOfGenericCustomTypeInfo() { auto p0i8 = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); auto genericArgs = static_cast(chirType).GetGenericArgs(); if (genericArgs.empty()) { return llvm::ConstantPointerNull::get(p0i8); } std::vector constants; for (auto typeArg : genericArgs) { auto cgTypeOfTypeArg = CGType::GetOrCreate(cgMod, DeRef(*typeArg)); auto genericArgsTypeInfo = cgTypeOfTypeArg->GetOrCreateTypeInfo(); constants.emplace_back(llvm::ConstantExpr::getBitCast(genericArgsTypeInfo, p0i8)); } auto typeOfGenericArgsGV = llvm::ArrayType::get(p0i8, constants.size()); auto typeInfoOfGenericArgs = llvm::cast(cgMod.GetLLVMModule()->getOrInsertGlobal( GetTypeQualifiedNameForReflect(cgMod, chirType) + ".ti.typeArgs", typeOfGenericArgsGV)); typeInfoOfGenericArgs->setInitializer(llvm::ConstantArray::get(typeOfGenericArgsGV, constants)); typeInfoOfGenericArgs->setLinkage(llvm::GlobalValue::LinkageTypes::PrivateLinkage); return llvm::ConstantExpr::getBitCast(typeInfoOfGenericArgs, p0i8); } } // namespace CodeGen } // namespace Cangjie cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGType.h000066400000000000000000000342721510705540100226060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGTYPE_H #define CANGJIE_CGTYPE_H #include #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DerivedTypes.h" #include "cangjie/Basic/UGTypeKind.h" #include "cangjie/CHIR/Type/CustomTypeDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/Utils/CheckUtils.h" namespace Cangjie { namespace CHIR { class Value; class CHIRBuilder; class FuncBase; } // namespace CHIR namespace CodeGen { class CGContext; class CGModule; class CGType; class CGGenericType; class CGTypeMgr; class DIBuilder; class CGExtensionDef; enum class CGTypeKind { OTHERS, CG_PRIMITIVE, CG_FUNCTION, CG_ENUM, CG_CLASS, CG_REF, CG_TI, }; class CGType { friend class CGTypeMgr; friend class DIBuilder; public: CGType() = delete; virtual ~CGType() { } bool operator==(const CGType* that) const { return llvmType == that->llvmType; } bool operator==(const CGType& that) const { return llvmType == that.llvmType; } CGContext& GetCGContext() const { return cgCtx; } llvm::Type* GetLLVMType() const { CJC_ASSERT(llvmType != nullptr); return llvmType; } virtual llvm::GlobalVariable* GetOrCreateTypeInfo(); llvm::GlobalVariable* GetOrCreateTypeTemplate(); llvm::GlobalVariable* GetOrCreateGenericCustomTypeInfo(); llvm::Constant* GenTypeArgsOfGenericCustomTypeInfo(); const CHIR::Type& GetOriginal() const { return chirType; } bool IsFloatType() const { return llvmType->isFloatTy(); } bool IsBooleanType() const { return llvmType->isIntegerTy(1); } bool IsPointerType(unsigned int addrSpace = 0) const { return chirType.IsRef() || (!chirType.IsFunc() && llvmType->isPointerTy() && llvmType->getPointerAddressSpace() == addrSpace); } bool IsStructType() const { return GetLLVMType()->isStructTy(); } bool IsVArrayType() const { return GetLLVMType()->isArrayTy(); } bool IsStructPtrType() const { return IsPointerType() && GetPointerElementType()->GetLLVMType()->isStructTy(); } bool IsRefType() const { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND return llvmType->isPointerTy() && 1U == llvmType->getPointerAddressSpace(); #endif } bool IsReference() const; bool IsFuncPtrType() const { return IsPointerType() && GetPointerElementType()->GetLLVMType()->isFunctionTy(); } bool IsCGEnum() const { return cgTypeKind == CGTypeKind::CG_ENUM; } bool IsOptionLikeRef() const; bool IsCGRef() const { return cgTypeKind == CGTypeKind::CG_REF; } bool IsCGFunction() const { return cgTypeKind == CGTypeKind::CG_FUNCTION; } bool IsCGTI() const { return cgTypeKind == CGTypeKind::CG_TI; } inline bool IsLoadablePtrType() const { if (!IsPointerType()) { return false; } // Without the semantic type, it is difficult to distinguish whether the codegen type of the CPointer is a // rvalue. Need to check whether any non-type generates i8**. If no, the semantic dependency can be deleted. if (chirType.IsCPointer() || chirType.IsCString()) { return IsLoadableCPointer(); } if (IsFuncPtrType()) { return false; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // pointer types of `structure pointer ty`/`ref type`/`varray type` can't be loaded in LLVM. // (They are passed by pointer in LLVM). if (IsStructPtrType() || IsRefType() || IsVArrayPtrType()) { return false; } #endif return true; } bool IsLoadableCPointer() const { return llvmType && IsPointerType() && GetPointerElementType()->IsPointerType(); } bool IsStructPtrTypeWithAddrSpace1() const { return IsStructPtrType() && GetLLVMType()->getPointerAddressSpace() == 1; } bool IsVArrayPtrType() const { return IsPointerType() && GetPointerElementType()->GetLLVMType()->isArrayTy(); } const CGType* GetPointerElementType() const { CJC_ASSERT(llvmType->isPointerTy()); if (chirType.IsGeneric()) { return this; } auto tmp = GetContainedTypes(); CJC_ASSERT(tmp.size() == 1); return tmp[0]; } const CGType* GetContainedTypeAt(size_t idx) const { auto tmp = GetContainedTypes(); CJC_ASSERT(idx < tmp.size()); return tmp[idx]; } std::vector GetContainedTypes() const { if (containedCGTypes.empty()) { const_cast(this)->GenContainedCGTypes(); } return containedCGTypes; } unsigned GetAddrspace() const { return addrspace; } std::optional GetSize() const; std::optional GetAlign() const { return align; } inline bool IsConcrete() const { return cgGenericKind == CGGenericKind::CONCRETE; } inline bool IsStaticGI() const { return cgGenericKind == CGGenericKind::STATIC_GI; } inline bool IsDynamicGI() const { return cgGenericKind == CGGenericKind::DYNAMIC_GI; } void GenTypeInfo(); void SetCGExtensionDef(CGExtensionDef* ed) { cgExtensionDef = ed; } virtual llvm::Constant* GenSizeOfTypeInfo(); virtual llvm::Constant* GenAlignOfTypeInfo(); virtual void CalculateSizeAndAlign() = 0; virtual void GenTypeTemplate() { } static llvm::Type* GetUnitType(llvm::LLVMContext& llvmCtx); static llvm::Type* GetRefType(llvm::LLVMContext& llvmCtx); static llvm::Type* GetLandingPadType(llvm::LLVMContext& llvmCtx); static llvm::Type* GetArrayBaseType(CGContext& cgCtx); static llvm::Type* GetBitMapType(llvm::LLVMContext& llvmCtx); static llvm::FunctionType* GetCodeGenFunctionType( llvm::LLVMContext& llvmCtx, llvm::Type* retType, std::vector& params); static llvm::StructType* GetOrCreateTypeInfoType(llvm::LLVMContext& llvmCtx); static llvm::PointerType* GetOrCreateTypeInfoPtrType(llvm::LLVMContext& llvmCtx); static llvm::StructType* GetOrCreateTypeTemplateType(llvm::LLVMContext& llvmCtx); static llvm::StructType* GetOrCreateExtensionDefType(llvm::LLVMContext& llvmCtx); static llvm::PointerType* GetOrCreateExtensionDefPtrType(llvm::LLVMContext& llvmCtx); static llvm::StructType* GetOrCreateEnumCtorLayoutType(CGModule& cgModule, const CHIR::EnumType& enumTy, std::size_t ctorIndex, const std::vector& paramTypes); /// To get a RefType of chir static CHIR::RefType* GetRefTypeOf(CHIR::CHIRBuilder& chirBuilder, const CHIR::Type& typeOfCHIR); static CHIR::RefType* GetRefTypeOfCHIRInt8(CHIR::CHIRBuilder& chirBuilder); static CGType* GetCGTI(CGModule& cgModule); // for reflection static llvm::StructType* GetOrCreateGenericTypeInfoType(llvm::LLVMContext& llvmCtx); static llvm::StructType* GetOrCreateGenericCustomTypeInfoType(llvm::LLVMContext& llvmCtx); struct TypeExtraInfo { size_t addrspace = 0U; bool isMethod = false; bool isStaticMethod = false; bool allowBasePtr = true; bool forWrapper = false; std::vector instantiatedParamTypes; TypeExtraInfo() { } TypeExtraInfo(size_t addrspace, bool isMethod, bool isStaticMethod, bool allowBasePtr, const std::vector& instantiatedParamTypes) : addrspace(addrspace), isMethod(isMethod), isStaticMethod(isStaticMethod), allowBasePtr(allowBasePtr), instantiatedParamTypes(instantiatedParamTypes) { } TypeExtraInfo(size_t addrspace) : addrspace(addrspace) { } bool operator==(const TypeExtraInfo& rhs) const { bool ret = instantiatedParamTypes.size() == rhs.instantiatedParamTypes.size() && addrspace == rhs.addrspace && isMethod == rhs.isMethod && isStaticMethod == rhs.isStaticMethod && allowBasePtr == rhs.allowBasePtr && forWrapper == rhs.forWrapper; if (ret) { for (std::size_t idx = 0; idx < instantiatedParamTypes.size(); ++idx) { if (instantiatedParamTypes[idx] != rhs.instantiatedParamTypes[idx]) { return false; } } } return ret; } }; struct TypeExtraInfoHasher { size_t operator()(const TypeExtraInfo& extraInfo) const { size_t ret = (extraInfo.addrspace << 4U) & (static_cast(extraInfo.isMethod) << 3U) & (static_cast(extraInfo.isStaticMethod) << 2U) & static_cast(extraInfo.allowBasePtr << 1U) & static_cast(extraInfo.forWrapper); std::string tmp = std::to_string(ret); for (auto paramType : extraInfo.instantiatedParamTypes) { tmp += paramType->ToString(); } return std::hash{}(tmp); } }; enum class CGGenericKind { UNKNOWN, /**< Not a valid kind */ STATIC_GI, /**< Generic instantiated at compile-time */ DYNAMIC_GI, /**< Generic instantiated at runtime */ CONCRETE, /**< Others */ }; static CGGenericKind GetCGGenericKind(const CHIR::Type& chirType); /// For any CHIRType static CGType* GetOrCreate(CGModule& cgModule, const CHIR::Type* chirTy, const TypeExtraInfo& extraInfo = {}); /// For getting CGType of chirFunc, the properties(`mut`, method or not, .etc) of the function /// are additionally considered. static CGType* GetOrCreateWithNode( CGModule& cgModule, const CHIR::Value* chirNode, bool allowBasePtr = true, bool forWrapper = false); static CGType* GetObjectCGType(CGModule& cgMod); /// For specified numeric type static CGType* GetInt8CGType(CGModule& cgMod); static CGType* GetInt16CGType(CGModule& cgMod); static CGType* GetInt32CGType(CGModule& cgMod); static CGType* GetInt64CGType(CGModule& cgMod); static CGType* GetUInt8CGType(CGModule& cgMod); static CGType* GetUInt16CGType(CGModule& cgMod); static CGType* GetUInt32CGType(CGModule& cgMod); static CGType* GetUInt64CGType(CGModule& cgMod); static CGType* GetFloat16CGType(CGModule& cgMod); static CGType* GetFloat32CGType(CGModule& cgMod); static CGType* GetFloat64CGType(CGModule& cgMod); static CGType* GetZeroSizedCGType(CGModule& cgMod); static CGType* GetUnitCGType(CGModule& cgMod); static CGType* GetNothingCGType(CGModule& cgMod); static CGType* GetBoolCGType(CGModule& cgMod); static CGType* GetCStringCGType(CGModule& cgMod); static CGType* GetInt8PtrCGType(CGModule& cgMod); static std::string GetNameOfTypeInfoGV(const CHIR::Type& chirType); static std::string GetNameOfTypeTemplateGV(const CHIR::Type& chirType); protected: CGType(CGModule& cgMod, CGContext& cgCtx, const CHIR::Type& chirType, CGTypeKind cgTypeKind = CGTypeKind::OTHERS); llvm::Constant* GenNameOfTypeInfo(); llvm::Constant* GenInheritedClassNumOfTypeInfo() const; llvm::Constant* GenExtensionDefPtrOfTypeInfo() const; llvm::Constant* GenMTableOfTypeInfo(); llvm::Constant* GenKindOfTypeInfo(); llvm::Constant* GenFlagOfTypeInfo(); virtual llvm::Type* GenLLVMType() = 0; virtual void GenContainedCGTypes() = 0; virtual void PreActionOfGenTypeInfo() { } virtual void PostActionOfGenTypeInfo() { } virtual llvm::Constant* GenFieldsNumOfTypeInfo(); virtual llvm::Constant* GenFieldsOfTypeInfo(); virtual llvm::Constant* GenSourceGenericOfTypeInfo(); virtual llvm::Constant* GenTypeArgsNumOfTypeInfo(); virtual llvm::Constant* GenOffsetsOfTypeInfo(); virtual llvm::Constant* GenTypeArgsOfTypeInfo(); virtual llvm::Constant* GenSuperOfTypeInfo(); virtual llvm::Constant* GenReflectionOfTypeInfo(); protected: CGModule& cgMod; CGContext& cgCtx; const CHIR::Type& chirType; llvm::Type* llvmType{nullptr}; llvm::StructType* layoutType{nullptr}; /**< For type contains fields, it saves the layout of its fields. */ llvm::GlobalVariable* typeInfo{nullptr}; llvm::GlobalVariable* typeTemplate{nullptr}; std::vector containedCGTypes; CGTypeKind cgTypeKind; unsigned addrspace = 0U; std::optional size{std::nullopt}; std::optional align{std::nullopt}; std::vector genericTypeArgs; private: CGGenericKind cgGenericKind = CGGenericKind::UNKNOWN; // 1) If inheritedClassNum is less than 2^14, the higher 2 bits are reserved for runtime optimization flags: // - The 16th bit is 1, indicating that the runtime can be optimized using bitmap. // - The 15th bit is 1, indicating that the runtime can be optimized using compressed bitmap. // In this case, to get the inherited class number, you should take the lower 14 bits. // 2) If inheritedClassNum is equal to or more than 2^14, bitmap will be set null, uint16_t inheritedClassNum = 0U; CGExtensionDef* cgExtensionDef{nullptr}; }; class CGTIType : public CGType { friend class CGTypeMgr; public: static CGTIType* Get(CGModule& cgMod) { static CGTIType ins = CGTIType(cgMod); return &ins; } protected: llvm::Type* GenLLVMType() override; void GenContainedCGTypes() override{}; private: explicit CGTIType(CGModule& cgMod); CGTIType() = delete; void CalculateSizeAndAlign() override { size = std::nullopt; align = std::nullopt; }; }; class CGTypeMgr { public: static CGType* GetConcreteCGTypeFor(CGModule& cgMod, const CHIR::Type& chirType, const CGType::TypeExtraInfo& extraInfo = {}, const CHIR::FuncBase* chirFunc = nullptr); }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGTYPE_H cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGVArrayType.cpp000066400000000000000000000056651510705540100242720ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/CGTypes/CGVArrayType.h" #include "CGContext.h" #include "CGModule.h" namespace Cangjie::CodeGen { void CGVArrayType::CalculateSizeAndAlign() { if (auto arrayType = llvm::dyn_cast(llvmType)) { auto layOut = cgMod.GetLLVMModule()->getDataLayout(); size = layOut.getTypeAllocSize(arrayType); align = layOut.getABITypeAlignment(arrayType); } } llvm::Type* CGVArrayType::GenLLVMType() { if (llvmType) { return llvmType; } auto& varrType = StaticCast(chirType); auto varrElementType = varrType.GetElementType(); llvmType = llvm::ArrayType::get(CGType::GetOrCreate(cgMod, varrElementType)->GetLLVMType(), varrType.GetSize()); auto layoutName = "VArray." + std::to_string(varrType.GetSize()) + MangleType(*varrElementType); layoutType = llvm::StructType::getTypeByName(cgCtx.GetLLVMContext(), layoutName); if (layoutType && cgCtx.IsGeneratedStructType(layoutName)) { return llvmType; } else if (!layoutType) { layoutType = llvm::StructType::create(cgCtx.GetLLVMContext(), layoutName); } cgCtx.AddGeneratedStructType(layoutName); // VArrayLayout: [sizeOfVArray x T], T is element type. if Array is ref Array, T is i8 addrspece(1) *. SetStructTypeBody(layoutType, std::vector{llvmType}); return llvmType; } void CGVArrayType::GenContainedCGTypes() { auto& varrayType = StaticCast(chirType); (void)containedCGTypes.emplace_back(CGType::GetOrCreate(cgMod, varrayType.GetElementType())); } llvm::Constant* CGVArrayType::GenFieldsNumOfTypeInfo() { auto fieldsNum = StaticCast(chirType).GetSize(); return llvm::ConstantInt::get(llvm::Type::getInt16Ty(cgMod.GetLLVMContext()), fieldsNum); } llvm::Constant* CGVArrayType::GenFieldsOfTypeInfo() { return CGType::GenFieldsOfTypeInfo(); } llvm::Constant* CGVArrayType::GenSourceGenericOfTypeInfo() { return CGType::GenSourceGenericOfTypeInfo(); } llvm::Constant* CGVArrayType::GenTypeArgsNumOfTypeInfo() { return CGType::GenTypeArgsNumOfTypeInfo(); } llvm::Constant* CGVArrayType::GenOffsetsOfTypeInfo() { return CGType::GenOffsetsOfTypeInfo(); } llvm::Constant* CGVArrayType::GenTypeArgsOfTypeInfo() { return CGType::GenTypeArgsOfTypeInfo(); } llvm::Constant* CGVArrayType::GenSuperOfTypeInfo() { auto varrayElementType = DeRef(*StaticCast(chirType).GetElementType()); auto ti = CGType::GetOrCreate(cgMod, varrayElementType)->GetOrCreateTypeInfo(); return llvm::ConstantExpr::getBitCast(ti, CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext())); } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/CGTypes/CGVArrayType.h000066400000000000000000000023331510705540100237240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGVARRYTYPE_H #define CANGJIE_CGVARRYTYPE_H #include "Base/CGTypes/CGType.h" namespace Cangjie { namespace CodeGen { class CGVArrayType : public CGType { friend class CGTypeMgr; protected: llvm::Type* GenLLVMType() override; void GenContainedCGTypes() override; private: CGVArrayType() = delete; explicit CGVArrayType(CGModule& cgMod, CGContext& cgCtx, const CHIR::Type& chirType) : CGType(cgMod, cgCtx, chirType) { } llvm::Constant* GenFieldsNumOfTypeInfo() override; llvm::Constant* GenFieldsOfTypeInfo() override; llvm::Constant* GenSourceGenericOfTypeInfo() override; llvm::Constant* GenTypeArgsNumOfTypeInfo() override; llvm::Constant* GenOffsetsOfTypeInfo() override; llvm::Constant* GenTypeArgsOfTypeInfo() override; llvm::Constant* GenSuperOfTypeInfo() override; void CalculateSizeAndAlign() override; }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGVARRYTYPE_H cangjie_compiler-1.0.7/src/CodeGen/Base/CHIRExprWrapper.h000066400000000000000000000576121510705540100230650ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements codegen Wrappers for CHIR Expression. */ #ifndef CANGJIE_CHIREXPRWRAPPER_H #define CANGJIE_CHIREXPRWRAPPER_H #include "cangjie/Basic/Print.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Utils.h" namespace Cangjie { namespace CodeGen { class CHIRExprWrapper { public: explicit CHIRExprWrapper(const CHIR::Expression& chirExpr) : chirExpr(chirExpr) { } virtual ~CHIRExprWrapper() = default; const CHIR::Expression& GetChirExpr() const { return chirExpr; } CHIR::ExprMajorKind GetExprMajorKind() const { return chirExpr.GetExprMajorKind(); } CHIR::ExprKind GetExprKind() const { return chirExpr.GetExprKind(); } bool IsConstant() const { return chirExpr.IsConstant(); } bool IsConstantNull() const { return chirExpr.IsConstantNull(); } bool IsConstantInt() const { return chirExpr.IsConstantInt(); } bool IsConstantString() const { return chirExpr.IsConstantString(); } std::string GetExprKindName() const { return chirExpr.GetExprKindName(); } CHIR::Block* GetParentBlock() const { return chirExpr.GetParentBlock(); } CHIR::Func* GetTopLevelFunc() const { return chirExpr.GetTopLevelFunc(); } unsigned GetNumOfOperands() const { return chirExpr.GetNumOfOperands(); } std::vector GetOperands() const { return chirExpr.GetOperands(); } CHIR::Value* GetOperand(unsigned idx) const { return chirExpr.GetOperand(idx); } CHIR::LocalVar* GetResult() const { return chirExpr.GetResult(); } std::string ToString(unsigned indent = 0) const { return chirExpr.ToString(indent); } bool IsTerminator() const { return chirExpr.IsTerminator(); } // Get the value of the annotation T associated to this node template typename std::invoke_result::type Get() const { return chirExpr.Get(); } const CHIR::DebugLocation& GetDebugLocation() const { return chirExpr.GetDebugLocation(); } void Dump() const { chirExpr.Dump(); } protected: const CHIR::Expression& chirExpr; }; class CHIRCallExpr : public CHIRExprWrapper { public: explicit CHIRCallExpr(const CHIR::Expression& chirExpr) : CHIRExprWrapper(chirExpr) { } virtual CHIR::Type* GetThisType() const = 0; virtual std::vector GetInstantiatedTypeArgs() const = 0; virtual bool IsCalleeMethod() const = 0; virtual bool IsCalleeStructInstanceMethod() const = 0; virtual bool IsCalleeStatic() const = 0; virtual const CHIR::Type* GetOuterType([[maybe_unused]] CHIR::CHIRBuilder& builder) const = 0; virtual const CHIR::Value* GetThisParam() const = 0; }; class CHIRApplyWrapper : public CHIRCallExpr { public: explicit CHIRApplyWrapper(const CHIR::Apply& apply) : CHIRCallExpr(apply) { if (GetInstantiatedTypeArgs().size() != GetCalleeTypeArgsNum()) { #ifndef NDEBUG Errorln(chirExpr.ToString() + "\n"); #endif CJC_ASSERT(false && "Incorrect ApplyExpr from CHIR, type arguments are missing."); } } explicit CHIRApplyWrapper(const CHIR::ApplyWithException& applyWithException) : CHIRCallExpr(applyWithException) { if (GetInstantiatedTypeArgs().size() != GetCalleeTypeArgsNum()) { #ifndef NDEBUG Errorln(chirExpr.ToString() + "\n"); #endif CJC_ASSERT(false && "Incorrect ApplyExpr from CHIR, type arguments are missing."); } } CHIRApplyWrapper(const CHIRApplyWrapper& chirExprW) : CHIRCallExpr(chirExprW.chirExpr) { } ~CHIRApplyWrapper() = default; CHIR::Value* GetCallee() const { if (GetExprKind() == CHIR::ExprKind::APPLY) { return StaticCast(chirExpr).GetCallee(); } else { return StaticCast(chirExpr).GetCallee(); } } std::vector GetArgs() const { if (GetExprKind() == CHIR::ExprKind::APPLY) { return StaticCast(chirExpr).GetArgs(); } else { return StaticCast(chirExpr).GetArgs(); } } std::vector GetInstantiatedTypeArgs() const override { if (GetExprKind() == CHIR::ExprKind::APPLY) { return StaticCast(chirExpr).GetInstantiatedTypeArgs(); } else { return StaticCast(chirExpr).GetInstantiatedTypeArgs(); } } CHIR::Type* GetThisType() const override { if (GetExprKind() == CHIR::ExprKind::APPLY) { return StaticCast(chirExpr).GetThisType(); } else { return StaticCast(chirExpr).GetThisType(); } } bool IsCalleeMethod() const override { bool isCallee = false; if (auto func = DynamicCast(GetCallee())) { isCallee = func->IsMemberFunc(); } return isCallee; } bool IsCalleeStatic() const override { return GetCallee()->TestAttr(CHIR::Attribute::STATIC); } bool IsCalleeStructInstanceMethod() const override { if (!IsCalleeMethod() || IsCalleeStatic()) { return false; } auto outer = VirtualCast(GetCallee())->GetOuterDeclaredOrExtendedDef(); return outer && outer->IsStruct(); } bool IsCalleeStructMutOrCtorMethod() const { return IsCalleeStructInstanceMethod() && (GetCallee()->TestAttr(CHIR::Attribute::MUT) || CHIR::IsConstructor(*GetCallee())); } const CHIR::Type* GetOuterType(CHIR::CHIRBuilder& builder) const override { CHIR::Type* res = nullptr; if (GetExprKind() == CHIR::ExprKind::APPLY) { res = StaticCast(chirExpr).GetInstParentCustomTyOfCallee(builder); } else { res = StaticCast(chirExpr).GetInstParentCustomTyOfCallee(builder); } if (!res) { #ifndef NDEBUG Errorln("Should not get a nullptr:\n", chirExpr.ToString()); #endif CJC_ASSERT(false); } return res; } const CHIR::Value* GetThisParam() const override { return IsCalleeMethod() && !IsCalleeStatic() ? GetArgs()[0] : nullptr; } private: size_t GetCalleeTypeArgsNum() const { if (GetCallee()->IsFunc()) { return VirtualCast(GetCallee())->GetGenericTypeParams().size(); } return 0; } }; class CHIRInvokeWrapper : public CHIRCallExpr { public: explicit CHIRInvokeWrapper(const CHIR::Invoke& invoke) : CHIRCallExpr(invoke) { } explicit CHIRInvokeWrapper(const CHIR::InvokeWithException& invokeWithException) : CHIRCallExpr(invokeWithException) { } ~CHIRInvokeWrapper() = default; CHIR::Value* GetObject() const { if (GetExprKind() == CHIR::ExprKind::INVOKE) { return StaticCast(chirExpr).GetObject(); } else { return StaticCast(chirExpr).GetObject(); } } std::string GetMethodName() const { if (GetExprKind() == CHIR::ExprKind::INVOKE) { return StaticCast(chirExpr).GetMethodName(); } else { return StaticCast(chirExpr).GetMethodName(); } } CHIR::FuncType* GetMethodType() const { if (GetExprKind() == CHIR::ExprKind::INVOKE) { return StaticCast(chirExpr).GetMethodType(); } else { return StaticCast(chirExpr).GetMethodType(); } } std::vector GetArgs() const { if (GetExprKind() == CHIR::ExprKind::INVOKE) { return StaticCast(chirExpr).GetArgs(); } else { return StaticCast(chirExpr).GetArgs(); } } std::vector GetInstantiatedTypeArgs() const override { if (GetExprKind() == CHIR::ExprKind::INVOKE) { return StaticCast(chirExpr).GetInstantiatedTypeArgs(); } else { return StaticCast(chirExpr).GetInstantiatedTypeArgs(); } } CHIR::Type* GetThisType() const override { if (GetExprKind() == CHIR::ExprKind::INVOKE) { return StaticCast(chirExpr).GetObject()->GetType(); } else { return StaticCast(chirExpr).GetObject()->GetType(); } } bool IsCalleeMethod() const override { return true; } bool IsCalleeStatic() const override { return false; } bool IsCalleeStructInstanceMethod() const override { return false; } const CHIR::Type* GetOuterType(CHIR::CHIRBuilder& builder) const override { CJC_ASSERT(!IsCalleeStatic()); CHIR::Type* res = nullptr; if (GetExprKind() == CHIR::ExprKind::INVOKE) { res = StaticCast(chirExpr).GetInstSrcParentCustomTypeOfMethod(builder); } else { res = StaticCast(chirExpr).GetInstSrcParentCustomTypeOfMethod(builder); } if (!res) { #ifndef NDEBUG Errorln("Should not get a nullptr:\n", chirExpr.ToString()); #endif CJC_ASSERT(false); } return res; } const CHIR::Value* GetThisParam() const override { CJC_ASSERT(!IsCalleeStatic()); return GetObject(); } size_t GetVirtualMethodOffset() const { if (GetExprKind() == CHIR::ExprKind::INVOKE) { return StaticCast(chirExpr).GetVirtualMethodOffset(); } else { return StaticCast(chirExpr).GetVirtualMethodOffset(); } } bool TestVritualMethodAttr(CHIR::CHIRBuilder& builder, CHIR::Attribute attr) const { if (GetExprKind() == CHIR::ExprKind::INVOKE) { return StaticCast(chirExpr).GetVirtualMethodAttr(builder).TestAttr(attr); } else { return StaticCast(chirExpr).GetVirtualMethodAttr(builder).TestAttr(attr); } } }; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND class CHIRInvokeStaticWrapper : public CHIRCallExpr { public: explicit CHIRInvokeStaticWrapper(const CHIR::InvokeStatic& invokeStatic) : CHIRCallExpr(invokeStatic) { } explicit CHIRInvokeStaticWrapper(const CHIR::InvokeStaticWithException& invokeStaticWithException) : CHIRCallExpr(invokeStaticWithException) { } ~CHIRInvokeStaticWrapper() = default; std::string GetMethodName() const { if (GetExprKind() == CHIR::ExprKind::INVOKESTATIC) { return StaticCast(chirExpr).GetMethodName(); } else { return StaticCast(chirExpr).GetMethodName(); } } CHIR::FuncType* GetMethodType() const { if (GetExprKind() == CHIR::ExprKind::INVOKESTATIC) { return StaticCast(chirExpr).GetMethodType(); } else { return StaticCast(chirExpr).GetMethodType(); } } CHIR::Value* GetRTTIValue() const { if (GetExprKind() == CHIR::ExprKind::INVOKESTATIC) { return StaticCast(chirExpr).GetRTTIValue(); } else { return StaticCast(chirExpr).GetRTTIValue(); } } std::vector GetArgs() const { if (GetExprKind() == CHIR::ExprKind::INVOKESTATIC) { return StaticCast(chirExpr).GetArgs(); } else { return StaticCast(chirExpr).GetArgs(); } } CHIR::Type* GetThisType() const override { if (GetExprKind() == CHIR::ExprKind::INVOKESTATIC) { return StaticCast(chirExpr).GetThisType(); } else { return StaticCast(chirExpr).GetThisType(); } } std::vector GetInstantiatedTypeArgs() const override { if (GetExprKind() == CHIR::ExprKind::INVOKESTATIC) { return StaticCast(chirExpr).GetInstantiatedTypeArgs(); } else { return StaticCast(chirExpr).GetInstantiatedTypeArgs(); } } bool IsCalleeMethod() const override { return true; } bool IsCalleeStatic() const override { return true; } bool IsCalleeStructInstanceMethod() const override { return false; } const CHIR::Type* GetOuterType(CHIR::CHIRBuilder& builder) const override { CJC_ASSERT(IsCalleeStatic()); CHIR::Type* res = nullptr; if (GetExprKind() == CHIR::ExprKind::INVOKESTATIC) { res = StaticCast(chirExpr).GetInstSrcParentCustomTypeOfMethod(builder); } else { res = StaticCast(chirExpr).GetInstSrcParentCustomTypeOfMethod( builder); } if (!res) { #ifndef NDEBUG Errorln("Should not get a nullptr:\n", chirExpr.ToString()); #endif CJC_ASSERT(false); } return res; } const CHIR::Value* GetThisParam() const override { CJC_ASSERT(false && "InvokeStatic doesn't have this param."); return nullptr; } size_t GetVirtualMethodOffset() const { if (GetExprKind() == CHIR::ExprKind::INVOKESTATIC) { return StaticCast(chirExpr).GetVirtualMethodOffset(); } else { return StaticCast(chirExpr).GetVirtualMethodOffset(); } } }; #endif class CHIRUnaryExprWrapper : public CHIRExprWrapper { public: explicit CHIRUnaryExprWrapper(const CHIR::UnaryExpression& unaryExpr) : CHIRExprWrapper(unaryExpr) { } explicit CHIRUnaryExprWrapper(const CHIR::IntOpWithException& unaryExprWithException) : CHIRExprWrapper(unaryExprWithException) { } ~CHIRUnaryExprWrapper() = default; CHIR::Value* GetOperand() const { if (GetExprMajorKind() == CHIR::ExprMajorKind::UNARY_EXPR) { return StaticCast(chirExpr).GetOperand(); } else { return StaticCast(chirExpr).GetOperands()[0]; } } OverflowStrategy GetOverflowStrategy() const { if (GetExprMajorKind() == CHIR::ExprMajorKind::UNARY_EXPR) { return StaticCast(chirExpr).GetOverflowStrategy(); } else { return OverflowStrategy::THROWING; } } CHIR::ExprKind GetUnaryExprKind() const { if (GetExprMajorKind() == CHIR::ExprMajorKind::UNARY_EXPR) { return StaticCast(chirExpr).GetExprKind(); } else { return StaticCast(chirExpr).GetOpKind(); } } private: CHIR::ExprKind GetExprKind() const = delete; }; class CHIRBinaryExprWrapper : public CHIRExprWrapper { public: explicit CHIRBinaryExprWrapper(const CHIR::BinaryExpression& binaryExpr) : CHIRExprWrapper(binaryExpr) { } explicit CHIRBinaryExprWrapper(const CHIR::IntOpWithException& binaryExprWithException) : CHIRExprWrapper(binaryExprWithException) { } ~CHIRBinaryExprWrapper() = default; CHIR::Value* GetLHSOperand() const { if (GetExprMajorKind() == CHIR::ExprMajorKind::BINARY_EXPR) { return StaticCast(chirExpr).GetLHSOperand(); } else { return StaticCast(chirExpr).GetOperands()[0]; } } CHIR::Value* GetRHSOperand() const { if (GetExprMajorKind() == CHIR::ExprMajorKind::BINARY_EXPR) { return StaticCast(chirExpr).GetRHSOperand(); } else { return StaticCast(chirExpr).GetOperands()[1]; } } OverflowStrategy GetOverflowStrategy() const { if (GetExprMajorKind() == CHIR::ExprMajorKind::BINARY_EXPR) { return StaticCast(chirExpr).GetOverflowStrategy(); } else { return StaticCast(chirExpr).GetOverflowStrategy(); } } CHIR::ExprKind GetBinaryExprKind() const { if (GetExprMajorKind() == CHIR::ExprMajorKind::BINARY_EXPR) { return StaticCast(chirExpr).GetExprKind(); } else { return StaticCast(chirExpr).GetOpKind(); } } private: CHIR::ExprKind GetExprKind() const = delete; }; class CHIRSpawnWrapper : public CHIRExprWrapper { public: explicit CHIRSpawnWrapper(const CHIR::Spawn& spawn) : CHIRExprWrapper(spawn) { } explicit CHIRSpawnWrapper(const CHIR::SpawnWithException& spawnWithException) : CHIRExprWrapper(spawnWithException) { } ~CHIRSpawnWrapper() = default; CHIR::Value* GetFuture() const { if (GetExprKind() == CHIR::ExprKind::SPAWN) { return StaticCast(chirExpr).GetFuture(); } else { return StaticCast(chirExpr).GetFuture(); } } CHIR::Value* GetSpawnArg() const { if (GetExprKind() == CHIR::ExprKind::SPAWN) { return StaticCast(chirExpr).GetSpawnArg(); } else { return StaticCast(chirExpr).GetSpawnArg(); } } CHIR::Value* GetClosure() const { if (GetExprKind() == CHIR::ExprKind::SPAWN) { return StaticCast(chirExpr).GetClosure(); } else { return StaticCast(chirExpr).GetClosure(); } } CHIR::FuncBase* GetExecuteClosure() const { if (GetExprKind() == CHIR::ExprKind::SPAWN) { return StaticCast(chirExpr).GetExecuteClosure(); } else { return StaticCast(chirExpr).GetExecuteClosure(); } } bool IsExecuteClosure() const { if (GetExprKind() == CHIR::ExprKind::SPAWN) { return StaticCast(chirExpr).IsExecuteClosure(); } else { return StaticCast(chirExpr).IsExecuteClosure(); } } }; class CHIRTypeCastWrapper : public CHIRExprWrapper { public: explicit CHIRTypeCastWrapper(const CHIR::TypeCast& typeCast) : CHIRExprWrapper(typeCast) { } explicit CHIRTypeCastWrapper(const CHIR::TypeCastWithException& typeCastWithException) : CHIRExprWrapper(typeCastWithException) { } ~CHIRTypeCastWrapper() = default; CHIR::Value* GetSourceValue() const { if (GetExprKind() == CHIR::ExprKind::TYPECAST) { return StaticCast(chirExpr).GetSourceValue(); } else { return StaticCast(chirExpr).GetSourceValue(); } } CHIR::Type* GetSourceTy() const { if (GetExprKind() == CHIR::ExprKind::TYPECAST) { return StaticCast(chirExpr).GetSourceTy(); } else { return StaticCast(chirExpr).GetSourceTy(); } } CHIR::Type* GetTargetTy() const { if (GetExprKind() == CHIR::ExprKind::TYPECAST) { return StaticCast(chirExpr).GetTargetTy(); } else { return StaticCast(chirExpr).GetTargetTy(); } } OverflowStrategy GetOverflowStrategy() const { if (GetExprKind() == CHIR::ExprKind::TYPECAST) { return StaticCast(chirExpr).GetOverflowStrategy(); } else { return OverflowStrategy::THROWING; } } }; class CHIRIntrinsicWrapper : public CHIRExprWrapper { public: explicit CHIRIntrinsicWrapper(const CHIR::Intrinsic& intrinsic) : CHIRExprWrapper(intrinsic) { } explicit CHIRIntrinsicWrapper(const CHIR::IntrinsicWithException& intrinsicWithException) : CHIRExprWrapper(intrinsicWithException) { } ~CHIRIntrinsicWrapper() = default; CHIR::IntrinsicKind GetIntrinsicKind() const { if (GetExprKind() == CHIR::ExprKind::INTRINSIC) { return StaticCast(chirExpr).GetIntrinsicKind(); } else { return StaticCast(chirExpr).GetIntrinsicKind(); } } std::vector GetInstantiatedTypeArgs() const { if (GetExprKind() == CHIR::ExprKind::INTRINSIC) { return StaticCast(chirExpr).GetInstantiatedTypeArgs(); } else { return StaticCast(chirExpr).GetInstantiatedTypeArgs(); } } }; class CHIRAllocateWrapper : public CHIRExprWrapper { public: explicit CHIRAllocateWrapper(const CHIR::Allocate& allocate) : CHIRExprWrapper(allocate) { } explicit CHIRAllocateWrapper(const CHIR::AllocateWithException& allocateWithException) : CHIRExprWrapper(allocateWithException) { } ~CHIRAllocateWrapper() = default; CHIR::Type* GetType() const { if (GetExprKind() == CHIR::ExprKind::ALLOCATE) { return StaticCast(chirExpr).GetType(); } else { return StaticCast(chirExpr).GetType(); } } }; class CHIRRawArrayAllocateWrapper : public CHIRExprWrapper { public: explicit CHIRRawArrayAllocateWrapper(const CHIR::RawArrayAllocate& rawArrayAllocate) : CHIRExprWrapper(rawArrayAllocate) { } explicit CHIRRawArrayAllocateWrapper(const CHIR::RawArrayAllocateWithException& rawArrayAllocateWithException) : CHIRExprWrapper(rawArrayAllocateWithException) { } ~CHIRRawArrayAllocateWrapper() = default; CHIR::Value* GetSize() const { if (GetExprKind() == CHIR::ExprKind::RAW_ARRAY_ALLOCATE) { return StaticCast(chirExpr).GetSize(); } else { return StaticCast(chirExpr).GetSize(); } } CHIR::Type* GetElementType() const { if (GetExprKind() == CHIR::ExprKind::RAW_ARRAY_ALLOCATE) { return StaticCast(chirExpr).GetElementType(); } else { return StaticCast(chirExpr).GetElementType(); } } }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CHIREXPRWRAPPER_H cangjie_compiler-1.0.7/src/CodeGen/Base/ExprDispatcher/000077500000000000000000000000001510705540100227015ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CodeGen/Base/ExprDispatcher/BinaryExprDispatcher.cpp000066400000000000000000000066351510705540100275110ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/ExprDispatcher/ExprDispatcher.h" #include #include "Base/ArithmeticOpImpl.h" #include "Base/LogicalOpImpl.h" #include "Base/OverflowDispatcher.h" #include "CGModule.h" #include "IRBuilder.h" #include "cangjie/CHIR/Value.h" using namespace Cangjie::CHIR; namespace Cangjie::CodeGen { llvm::Value* HandleNonOverflowBinaryExpression(IRBuilder2& irBuilder, const CHIRBinaryExprWrapper& chirExpr) { switch (chirExpr.GetBinaryExprKind()) { case CHIR::ExprKind::ADD: case CHIR::ExprKind::SUB: case CHIR::ExprKind::MUL: case CHIR::ExprKind::DIV: case CHIR::ExprKind::MOD: { return GenerateArithmeticOperation(irBuilder, chirExpr); } case CHIR::ExprKind::EXP: { return GenerateBinaryExpOperation(irBuilder, chirExpr); } case CHIR::ExprKind::LSHIFT: case CHIR::ExprKind::RSHIFT: case CHIR::ExprKind::BITAND: case CHIR::ExprKind::BITOR: case CHIR::ExprKind::BITXOR: { return GenerateBitwiseOperation(irBuilder, chirExpr); } case CHIR::ExprKind::LT: case CHIR::ExprKind::GT: case CHIR::ExprKind::LE: case CHIR::ExprKind::GE: case CHIR::ExprKind::EQUAL: case CHIR::ExprKind::NOTEQUAL: { return GenerateBooleanOperation(irBuilder, chirExpr); } // AND and OR are short circuit operators. They are transformed to simple branches by CHIR // thus we are free from generating code for these expression kinds. case CHIR::ExprKind::AND: case CHIR::ExprKind::OR: default: { printf("Unexpected expr kind: %" PRIu64 "\n", static_cast(chirExpr.GetBinaryExprKind())); CJC_ASSERT(false); return nullptr; } } } llvm::Value* HandleBinaryExpression(IRBuilder2& irBuilder, const CHIRBinaryExprWrapper& chirExpr) { const CHIR::Type* ty = chirExpr.GetResult()->GetType(); const CHIR::ExprKind& kind = chirExpr.GetBinaryExprKind(); OverflowStrategy overflowStrategy = chirExpr.GetOverflowStrategy(); if (!ty) { return nullptr; } if (OPERATOR_KIND_TO_OP_MAP.find(kind) == OPERATOR_KIND_TO_OP_MAP.end()) { return HandleNonOverflowBinaryExpression(irBuilder, chirExpr); } if ((overflowStrategy == OverflowStrategy::NA || overflowStrategy == OverflowStrategy::WRAPPING) && kind != CHIR::ExprKind::DIV && kind != CHIR::ExprKind::MOD) { return HandleNonOverflowBinaryExpression(irBuilder, chirExpr); } // There is a possibility of integer overflow when the result of an arithmetic expression is an integer type.(spec) if (!ty->IsInteger()) { return HandleNonOverflowBinaryExpression(irBuilder, chirExpr); } const CHIR::IntType* intTy = StaticCast(ty); auto& cgMod = irBuilder.GetCGModule(); CGValue* valLeft = cgMod | chirExpr.GetLHSOperand(); CGValue* valRight = cgMod | chirExpr.GetRHSOperand(); irBuilder.EmitLocation(chirExpr); return GenerateOverflow(irBuilder, overflowStrategy, kind, std::make_pair(intTy, nullptr), {valLeft, valRight}); } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/ExprDispatcher/ConstantExprDispatcher.cpp000066400000000000000000000051311510705540100300440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/ExprDispatcher/ExprDispatcher.h" #include "CGModule.h" #include "DIBuilder.h" #include "IRBuilder.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CodeGen { llvm::Value* HandleConstantExpression(IRBuilder2& irBuilder, const CHIR::Constant& chirConst) { return HandleLiteralValue(irBuilder, *chirConst.GetValue()); } llvm::Value* HandleLiteralValue(IRBuilder2& irBuilder, const CHIR::LiteralValue& chirLiteral) { CGModule& cgMod = irBuilder.GetCGModule(); llvm::Value* literalValue = nullptr; // remove loc for constant instruction. auto curLoc = irBuilder.GetInsertFunction() && irBuilder.GetInsertFunction()->getSubprogram() ? cgMod.diBuilder->CreateDILoc(irBuilder.GetInsertFunction()->getSubprogram(), {0, 0}) : llvm::DebugLoc().get(); irBuilder.SetCurrentDebugLocation(curLoc); if (chirLiteral.IsUnitLiteral()) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND literalValue = cgMod.GenerateUnitTypeValue(); #endif } else if (chirLiteral.IsNullLiteral()) { literalValue = irBuilder.CreateNullValue(*chirLiteral.GetType()); } else if (chirLiteral.IsBoolLiteral()) { literalValue = irBuilder.getInt1(StaticCast(chirLiteral).GetVal()); } else if (chirLiteral.IsRuneLiteral()) { literalValue = irBuilder.getInt32(StaticCast(chirLiteral).GetVal()); } else if (chirLiteral.IsIntLiteral()) { auto type = CGType::GetOrCreate(cgMod, chirLiteral.GetType())->GetLLVMType(); auto intConst = StaticCast(&chirLiteral); if (intConst->IsSigned()) { literalValue = llvm::ConstantInt::getSigned(type, intConst->GetSignedVal()); } else { literalValue = llvm::ConstantInt::get(type, intConst->GetUnsignedVal()); } } else if (chirLiteral.IsFloatLiteral()) { literalValue = llvm::ConstantFP::get(CGType::GetOrCreate(cgMod, chirLiteral.GetType())->GetLLVMType(), StaticCast(chirLiteral).GetVal()); } else if (chirLiteral.IsStringLiteral()) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND literalValue = irBuilder.CreateStringLiteral(StaticCast(chirLiteral).GetVal()); #endif } else { CJC_ASSERT(false); return nullptr; } return literalValue; } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/ExprDispatcher/ExprDispatcher.h000066400000000000000000000027011510705540100257770ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_EXPRDISPATCHER_H #define CANGJIE_EXPRDISPATCHER_H #include "llvm/IR/Value.h" #include "cangjie/CHIR/Expression/Terminator.h" namespace Cangjie { namespace CodeGen { class IRBuilder2; class CGValue; class CHIRUnaryExprWrapper; class CHIRBinaryExprWrapper; llvm::Value* HandleConstantExpression(IRBuilder2& irBuilder, const CHIR::Constant& chirConst); llvm::Value* HandleLiteralValue(IRBuilder2& irBuilder, const CHIR::LiteralValue& chirLiteral); llvm::Value* HandleTerminatorExpression(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr); llvm::Value* HandleNegExpression(IRBuilder2& irBuilder, llvm::Value* value); llvm::Value* HandleUnaryExpression(IRBuilder2& irBuilder, const CHIRUnaryExprWrapper& chirExpr); llvm::Value* HandleBinaryExpression(IRBuilder2& irBuilder, const CHIRBinaryExprWrapper& chirExpr); llvm::Value* HandleMemoryExpression(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr); llvm::Value* HandleStructedControlFlowExpression(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr); llvm::Value* HandleOthersExpression(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_EXPRDISPATCHER_H cangjie_compiler-1.0.7/src/CodeGen/Base/ExprDispatcher/MemoryExprDispatcher.cpp000066400000000000000000000264361510705540100275360ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/ExprDispatcher/ExprDispatcher.h" #include #include "Base/AllocateImpl.h" #include "Base/CGTypes/CGEnumType.h" #include "Base/CHIRExprWrapper.h" #include "CGModule.h" #include "IRBuilder.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CodeGen { llvm::Value* HandleLoadExpr(IRBuilder2& irBuilder, const CHIR::Load& load) { auto& cgMod = irBuilder.GetCGModule(); irBuilder.EmitLocation(CHIRExprWrapper(load)); auto value = *(cgMod | load.GetLocation()); CJC_ASSERT(value.GetCGType()->GetOriginal().IsRef()); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto elementType = value.GetCGType()->GetPointerElementType()->GetLLVMType(); if (load.GetLocation()->IsLocalVar() && elementType == CGType::GetRefType(irBuilder.GetLLVMContext())) { auto localVar = StaticCast(load.GetLocation()); if (IsGetElementRefOfClass(*localVar->GetExpr(), irBuilder.GetCGContext().GetCHIRBuilder())) { auto getEleRefExpr = StaticCast(localVar->GetExpr()); auto locationCHIRType = DeRef(*getEleRefExpr->GetLocation()->GetType()); if (locationCHIRType->IsClass() && IsWeakRefClass(*StaticCast(locationCHIRType)->GetClassDef())) { auto addr = value.GetRawValue(); auto base = irBuilder.GetCGContext().GetBasePtrOf(addr); CJC_NULLPTR_CHECK(base); return irBuilder.CallGCReadWeakRef({base, addr}); } } } #endif return irBuilder.CreateLoad(value); } llvm::Value* HandleStoreExpr(IRBuilder2& irBuilder, const CHIR::Store& store) { auto& cgMod = irBuilder.GetCGModule(); auto value = store.GetValue(); auto addr = store.GetLocation(); if (auto node = DynamicCast(value); node && node->GetExpr()->IsConstantNull() && value->GetType()->IsClass()) { return nullptr; } else if (auto var = DynamicCast(addr); var && var->IsRetValue() && value->GetType()->IsUnit()) { // If stored location is function's ret of unit type, do nothing. return nullptr; } irBuilder.EmitLocation(CHIRExprWrapper(store)); auto valueVal = *(cgMod | value); if (value->TestAttr(CHIR::Attribute::FOREIGN)) { CJC_NULLPTR_CHECK(llvm::dyn_cast(valueVal.GetRawValue())); llvm::dyn_cast(valueVal.GetRawValue()) ->addAttributeAtIndex(static_cast(llvm::AttributeList::FunctionIndex), llvm::Attribute::get(cgMod.GetLLVMContext(), CJ2C_ATTR)); } return irBuilder.CreateStore(valueVal, *(cgMod | addr)); } llvm::Value* GetElementRefOfOptionLikeT( IRBuilder2& irBuilder, const CHIR::EnumType& chirEnumType, bool isAntiOptionLike, llvm::Value* enumValue) { auto ret = irBuilder.CreateEntryAlloca(irBuilder.getInt1Ty()); auto payload = irBuilder.GetPayloadFromObject(enumValue); auto isRef = irBuilder.CreateTypeInfoIsReferenceCall(*chirEnumType.GetTypeArgs()[0]); auto [refBB, nonRefBB, endBB] = Vec2Tuple<3>(irBuilder.CreateAndInsertBasicBlocks({"ref", "nonRef", "end"})); irBuilder.CreateCondBr(isRef, refBB, nonRefBB); irBuilder.SetInsertPoint(refBB); auto refResult = isAntiOptionLike ? irBuilder.CallIntrinsicIsNonNull(irBuilder.CallGCRead({enumValue, payload})) : irBuilder.CallIntrinsicIsNull(irBuilder.CallGCRead({enumValue, payload})); irBuilder.CreateStore(refResult, ret); irBuilder.CreateBr(endBB); irBuilder.SetInsertPoint(nonRefBB); auto i1Ty = irBuilder.getInt1Ty(); auto nonRefResult = irBuilder.LLVMIRBuilder2::CreateLoad(i1Ty, irBuilder.CreateBitCast(payload, i1Ty->getPointerTo(1U))); irBuilder.CreateStore(nonRefResult, ret); irBuilder.CreateBr(endBB); irBuilder.SetInsertPoint(endBB); return ret; } llvm::Value* HandleGetElementRef(IRBuilder2& irBuilder, const CHIR::GetElementRef& getEleRef) { auto& cgMod = irBuilder.GetCGModule(); auto base = getEleRef.GetLocation(); CJC_ASSERT(base->GetType()->IsRef() || base->GetType()->IsGeneric()); irBuilder.EmitLocation(CHIRExprWrapper(getEleRef)); llvm::Value* retValue = nullptr; if (auto t = DeRef(*base->GetType()); t && t->IsEnum()) { CJC_ASSERT(!getEleRef.GetPath().empty() && getEleRef.GetPath()[0] == 0U); auto enumType = static_cast(t); auto cgEnumType = static_cast(CGType::GetOrCreate(cgMod, enumType)); auto layoutType = cgEnumType->GetLayoutType(); auto value = irBuilder.CreateLoad(*(cgMod | base)); if (cgEnumType->IsCommonEnum()) { auto payload = irBuilder.GetPayloadFromObject(value); auto tagLLVMType = llvm::Type::getInt32PtrTy(cgMod.GetLLVMContext(), 1U); retValue = irBuilder.CreateBitCast(payload, tagLLVMType); } else if (cgEnumType->IsOptionLikeNonRef()) { llvm::Value* payload = cgEnumType->GetSize().has_value() ? value : irBuilder.CreateBitCast(irBuilder.GetPayloadFromObject(value), layoutType->getPointerTo(1U)); retValue = irBuilder.CreateStructGEP(layoutType, payload, 0); } else if (cgEnumType->IsOptionLikeRef()) { retValue = irBuilder.CreateEntryAlloca(irBuilder.getInt1Ty()); auto tmp = cgEnumType->IsAntiOptionLike() ? irBuilder.CallIntrinsicIsNonNull(value) : irBuilder.CallIntrinsicIsNull(value); irBuilder.CreateStore(tmp, retValue); } else if (cgEnumType->IsOptionLikeT()) { retValue = GetElementRefOfOptionLikeT(irBuilder, *enumType, cgEnumType->IsAntiOptionLike(), value); } else if (cgEnumType->IsAllAssociatedValuesAreNonRef()) { retValue = irBuilder.CreateBitCast(value, llvm::Type::getInt32PtrTy(cgMod.GetLLVMContext())); } else if (cgEnumType->IsZeroSizeEnum()) { retValue = irBuilder.CreateEntryAlloca(irBuilder.getInt32Ty()); irBuilder.CreateStore(llvm::ConstantInt::get(irBuilder.getInt32Ty(), 0U), retValue); } else { CJC_ASSERT(false); } } if (!retValue) { retValue = irBuilder.CreateGEP(*(cgMod | base), getEleRef.GetPath()).GetRawValue(); } auto opTy = DeRef(*getEleRef.GetOperand(0)->GetType()); bool isAutoEnv = opTy->IsClass() && IsClosureConversionEnvClass(*StaticCast(opTy)->GetClassDef()); bool isLambda = dynamic_cast(&irBuilder.GetInsertCGFunction()->GetOriginal())->IsLambda(); if (isLambda && irBuilder.GetCGContext().GetCompileOptions().enableCompileDebug && isAutoEnv) { irBuilder.CreateEnvDeclare(getEleRef, retValue); } return retValue; } void HandleStoreElementRef(IRBuilder2& irBuilder, const CHIR::StoreElementRef& storeElementRef) { auto& cgMod = irBuilder.GetCGModule(); auto rhs = storeElementRef.GetValue(); auto lhs = storeElementRef.GetLocation(); auto& path = storeElementRef.GetPath(); CJC_ASSERT(lhs->GetType()->IsRef() && !path.empty()); auto value = cgMod | rhs; auto place = cgMod | lhs; auto srcCGType = CGType::GetOrCreate(cgMod, rhs->GetType()); if (!srcCGType->GetSize() && IsThisArgOfStructMethod(*rhs)) { auto typeInfoOfSrc = irBuilder.CreateTypeInfo(srcCGType->GetOriginal()); auto size = irBuilder.GetLayoutSize_32(srcCGType->GetOriginal()); auto tmp = irBuilder.CallClassIntrinsicAlloc({typeInfoOfSrc, size}); auto payloadPtr = irBuilder.GetPayloadFromObject(tmp); // Note: in this branch, it means: // - we are assigning a "struct" that doesn't begin with TypeInfo* to an address // that should begin with TypeInfo*. // - we are in the scope of a struct instance method(without "$withTI" postfix), // the "struct" mentioned above is the `this` parameter of the method. if (IsTypeContainsRef(srcCGType->GetLLVMType())) { irBuilder.CallGCWriteAgg({tmp, payloadPtr, value->GetRawValue(), size}); } else { irBuilder.CreateMemCpy(payloadPtr, llvm::MaybeAlign(), value->GetRawValue(), llvm::MaybeAlign(), size); } } else if (auto node = DynamicCast(rhs); node && node->GetExpr()->IsConstantNull() && rhs->GetType()->IsClass()) { // If the source is null or class, do nothing. } else if (auto var = DynamicCast(lhs); var && var->IsRetValue() && rhs->GetType()->IsUnit()) { // If stored location is function's ret of unit type, do nothing. } else { irBuilder.EmitLocation(CHIRExprWrapper(storeElementRef)); if (rhs->TestAttr(CHIR::Attribute::FOREIGN)) { llvm::cast(value->GetRawValue()) ->addAttributeAtIndex(static_cast(llvm::AttributeList::FunctionIndex), llvm::Attribute::get(cgMod.GetLLVMContext(), CJ2C_ATTR)); } irBuilder.CreateStore(*value, irBuilder.CreateGEP(*place, path)); } } llvm::Value* HandleMemoryExpression(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { CJC_ASSERT(chirExpr.GetExprMajorKind() == CHIR::ExprMajorKind::MEMORY_EXPR); switch (chirExpr.GetExprKind()) { case CHIR::ExprKind::ALLOCATE: { auto& alloca = StaticCast(chirExpr); // Opt: For the function that returns value by an `sret` argument, // we don't need to allocate another place to store the return value. if (auto parentFunc = alloca.GetTopLevelFunc(); parentFunc && parentFunc->GetReturnValue() == alloca.GetResult()) { auto llvmFunc = irBuilder.GetCGModule().GetOrInsertCGFunction(parentFunc)->GetRawFunction(); if (llvmFunc->hasStructRetAttr()) { return llvmFunc->getArg(0); } } irBuilder.EmitLocation(CHIRExprWrapper(alloca)); return GenerateAllocate(irBuilder, CHIRAllocateWrapper(alloca)); } case CHIR::ExprKind::LOAD: { auto& load = StaticCast(chirExpr); return HandleLoadExpr(irBuilder, load); } case CHIR::ExprKind::STORE: { auto& store = StaticCast(chirExpr); return HandleStoreExpr(irBuilder, store); } case CHIR::ExprKind::GET_ELEMENT_REF: { auto& getEleRef = StaticCast(chirExpr); return HandleGetElementRef(irBuilder, getEleRef); } case CHIR::ExprKind::STORE_ELEMENT_REF: { auto& storeElementRef = StaticCast(chirExpr); HandleStoreElementRef(irBuilder, storeElementRef); return nullptr; } default: { printf("Unexpected expr kind: %" PRIu64 "\n", static_cast(chirExpr.GetExprKind())); CJC_ASSERT(false); return nullptr; } } } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/ExprDispatcher/OtherExprDispatcher.cpp000066400000000000000000000451431510705540100273430ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/ExprDispatcher/ExprDispatcher.h" #include #include "Base/ApplyImpl.h" #include "Base/ArrayImpl.h" #include "Base/CGTypes/CGType.h" #include "Base/CHIRExprWrapper.h" #include "Base/InstanceOfImpl.h" #include "Base/IntrinsicsDispatcher.h" #include "Base/InvokeImpl.h" #include "Base/SpawnExprImpl.h" #include "Base/TupleExprImpl.h" #include "Base/TypeCastImpl.h" #include "Base/VArrayExprImpl.h" #include "CGModule.h" #include "IRAttribute.h" #include "IRBuilder.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Value.h" namespace { using namespace Cangjie; using namespace CodeGen; llvm::Value* HandleApplyExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { irBuilder.EmitLocation(CHIRExprWrapper(chirExpr)); return GenerateApply(irBuilder, CHIRApplyWrapper(StaticCast(chirExpr))); } llvm::Value* HandleInvokeExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { irBuilder.EmitLocation(CHIRExprWrapper(chirExpr)); return GenerateInvoke(irBuilder, CHIRInvokeWrapper(StaticCast(chirExpr))); } llvm::Value* HandleInvokeStaticExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { irBuilder.EmitLocation(CHIRExprWrapper(chirExpr)); return GenerateInvokeStatic(irBuilder, CHIRInvokeStaticWrapper(StaticCast(chirExpr))); } llvm::Value* HandleDebugExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& debug = StaticCast(chirExpr); auto& cgMod = irBuilder.GetCGModule(); auto cgVal = *(cgMod | debug.GetValue()); auto rawVal = cgVal.GetRawValue(); // Usually value is a local variable, // when it is a global variable, it means that it is a const literal and be lifted to global. if (!rawVal->getType()->isVoidTy() && !llvm::isa(rawVal)) { rawVal->setName(debug.GetSrcCodeIdentifier()); } if (!irBuilder.GetCGContext().GetCompileOptions().enableCompileDebug) { return nullptr; } auto ty = DeRef(*debug.GetValue()->GetType()); bool isBoxClass = ty->IsClass() && IsCapturedClass(*StaticCast(ty)->GetClassDef()); auto hasSize = CGType::GetOrCreate(cgMod, ty)->GetSize(); if (!IsReferenceType(*ty, cgMod) && hasSize && ty->IsGenericRelated()) { irBuilder.CreateBoxedValueForValueType(debug, cgVal); } else if (debug.GetValue()->IsParameter()) { irBuilder.CreateValuePointer(debug, cgVal); } else if (!llvm::isa(cgVal.GetRawValue())) { CJC_ASSERT(!llvm::isa(cgVal.GetRawValue()) == (!hasSize && !ty->IsClass())); irBuilder.CreateLocalVarPointer(debug, cgVal); } else if (isBoxClass) { irBuilder.CreateUnBoxDeclare(debug, cgVal); } else { irBuilder.EmitDeclare(debug); } return rawVal; } llvm::Value* HandleFieldExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& field = StaticCast(chirExpr); auto& cgMod = irBuilder.GetCGModule(); auto base = cgMod | field.GetBase(); if (auto lv = DynamicCast(field.GetBase())) { auto typeCast = DynamicCast(lv->GetExpr()); if (typeCast && typeCast->GetSourceValue()->GetType()->IsEnum() && typeCast->GetTargetTy()->IsTuple()) { return irBuilder.CreateEnumGEP(field); } } if (field.GetBase()->GetType()->IsEnum()) { return irBuilder.CreateEnumGEP(field); } if (base->GetCGType()->IsPointerType()) { auto val = irBuilder.CreateGEP(*base, field.GetPath()); if (base->GetRawValue()->getType()->getPointerAddressSpace() == 1U) { cgMod.GetCGContext().SetBasePtr(val.GetRawValue(), base->GetRawValue()); } return irBuilder.CreateLoad(val); } else { std::vector idxes; for (auto idx : field.GetPath()) { idxes.emplace_back(idx); } return irBuilder.CreateExtractValue(base->GetRawValue(), idxes); } } llvm::Value* HandleTypecastExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& typeCast = StaticCast(chirExpr); return GenerateTypeCast(irBuilder, CHIRTypeCastWrapper(typeCast)); } llvm::Value* HandleTupleExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& tuple = StaticCast(chirExpr); if (tuple.GetResult()->GetType()->IsEnum()) { return GenerateEnum(irBuilder, tuple); } else if (tuple.GetResult()->GetType()->IsStruct()) { return GenerateStruct(irBuilder, tuple); } return GenerateNativeTuple(irBuilder, tuple); } llvm::Value* HandleVArrayExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& varray = StaticCast(chirExpr); return GenerateVArray(irBuilder, varray); } llvm::Value* HandleVArrayBuilderExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& varray = StaticCast(chirExpr); return GenerateVArrayBuilder(irBuilder, varray); } llvm::Value* HandleIntrinsicExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { irBuilder.EmitLocation(CHIRExprWrapper(chirExpr)); return GenerateIntrinsic(irBuilder, CHIRIntrinsicWrapper(StaticCast(chirExpr))); } llvm::Value* HandleRawArrayLiteralInitExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& literalRawArrayInit = StaticCast(chirExpr); return irBuilder.CallArrayIntrinsicInitWithContent(literalRawArrayInit); } llvm::Value* HandleRawArrayAllocateExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& rawArray = StaticCast(chirExpr); return GenerateRawArrayAllocate(irBuilder, CHIRRawArrayAllocateWrapper(rawArray)); } llvm::Value* HandleRawArrayInitByValueExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& rawArrayInitByValue = StaticCast(chirExpr); return GenerateRawArrayInitByValue(irBuilder, rawArrayInitByValue); } llvm::Value* HandleInstanceOfExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& instanceOf = StaticCast(chirExpr); return GenerateInstanceOf(irBuilder, instanceOf); } llvm::Value* HandleSpawnExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& spawn = StaticCast(chirExpr); return GenerateSpawn(irBuilder, CHIRSpawnWrapper(spawn)); } llvm::Value* HandleGetExceptionExpr(IRBuilder2& irBuilder, const CHIR::Expression&) { return irBuilder.CallExceptionIntrinsicGetExceptionValue(); } llvm::Value* HandleTransformToGenericExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& castToGenericExpr = StaticCast(chirExpr); auto& cgMod = irBuilder.GetCGModule(); auto cgVal = *(cgMod | castToGenericExpr.GetSourceValue()); auto srcCGType = CGType::GetOrCreate(cgMod, castToGenericExpr.GetSourceTy()); auto targetCGType = CGType::GetOrCreate(cgMod, castToGenericExpr.GetTargetTy()); CJC_ASSERT(srcCGType->GetSize() && "Source type must have size."); if (srcCGType->IsReference() && castToGenericExpr.GetTargetTy()->IsGeneric()) { return cgVal.GetRawValue(); } if (srcCGType->GetSize() == targetCGType->GetSize()) { return cgVal.GetRawValue(); } auto srcCHIRType = castToGenericExpr.GetSourceTy(); // 1. Allocate memory for boxing srcValue. auto typeInfoOfSrc = irBuilder.CreateTypeInfo(*srcCHIRType); llvm::Value* temp = irBuilder.CallClassIntrinsicAlloc({typeInfoOfSrc, irBuilder.GetLayoutSize_32(*DeRef(*srcCHIRType))}); // 2. store srcValue to temp auto payloadPtr = irBuilder.GetPayloadFromObject(temp); auto addr = irBuilder.CreateBitCast(payloadPtr, srcCHIRType->IsRef() ? srcCGType->GetPointerElementType()->GetLLVMType()->getPointerTo(1U) : srcCGType->GetLLVMType()->getPointerTo(1)); auto addrType = CGType::GetOrCreate(cgMod, srcCHIRType->IsRef() ? srcCHIRType : CGType::GetRefTypeOf(cgMod.GetCGContext().GetCHIRBuilder(), *srcCHIRType), 1U); (void)irBuilder.CreateStore(cgVal, CGValue(addr, addrType)); return temp; } llvm::Value* HandleBoxExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& boxExpr = StaticCast(chirExpr); auto& cgMod = irBuilder.GetCGModule(); auto srcObject = boxExpr.GetSourceValue(); auto cgVal = *(cgMod | srcObject); auto srcCHIRType = boxExpr.GetSourceTy(); auto srcCGType = CGType::GetOrCreate(cgMod, srcCHIRType); auto result = GenerateGenericTypeCast(irBuilder, cgVal, *srcCHIRType, *boxExpr.GetResult()->GetType()); if (result) { return result; } if (srcCGType->IsReference()) { return cgVal.GetRawValue(); } if (srcCGType->GetSize()) { // 1. Allocate memory for boxing srcValue. auto typeInfoOfSrc = irBuilder.CreateTypeInfo(*srcCHIRType); llvm::Value* temp = irBuilder.CallClassIntrinsicAlloc({typeInfoOfSrc, irBuilder.GetLayoutSize_32(*DeRef(*srcCHIRType))}); // 2. store srcValue to temp auto payloadPtr = irBuilder.GetPayloadFromObject(temp); auto addr = irBuilder.CreateBitCast(payloadPtr, srcCHIRType->IsRef() ? srcCGType->GetPointerElementType()->GetLLVMType()->getPointerTo(1U) : srcCGType->GetLLVMType()->getPointerTo(1)); auto addrType = CGType::GetOrCreate(cgMod, srcCHIRType->IsRef() ? srcCHIRType : CGType::GetRefTypeOf(cgMod.GetCGContext().GetCHIRBuilder(), *srcCHIRType), 1U); (void)irBuilder.CreateStore(cgVal, CGValue(addr, addrType)); return temp; } else if (IsThisArgOfStructMethod(*srcObject)) { auto typeInfoOfSrc = irBuilder.CreateTypeInfo(*srcCHIRType); auto size = irBuilder.GetLayoutSize_32(*DeRef(*srcCHIRType)); auto tmp = irBuilder.CallClassIntrinsicAlloc({typeInfoOfSrc, size}); auto payloadPtr = irBuilder.GetPayloadFromObject(tmp); // Note: in this branch, it means: // - we are assigning a "struct" that doesn't begin with TypeInfo* to an address // that should begin with TypeInfo*. // - we are in the scope of a struct instance method(without "$withTI" postfix), // the "struct" mentioned above is the `this` parameter of the method. if (IsTypeContainsRef(srcCGType->GetLLVMType())) { irBuilder.CallGCWriteAgg({tmp, payloadPtr, cgVal.GetRawValue(), size}); } else { irBuilder.CreateMemCpy(payloadPtr, llvm::MaybeAlign(), cgVal.GetRawValue(), llvm::MaybeAlign(), size); } return tmp; } return cgVal.GetRawValue(); } llvm::Value* HandleUnBoxExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& cgMod = irBuilder.GetCGModule(); auto& unboxExpr = StaticCast(chirExpr); auto cgVal = (cgMod | unboxExpr.GetSourceValue()); auto targetCHIRType = unboxExpr.GetTargetTy(); auto targetCGType = CGType::GetOrCreate(cgMod, targetCHIRType); if (targetCGType->IsReference()) { return cgVal->GetRawValue(); } // For UnBox(%0, ValueType&) if (targetCHIRType->IsRef()) { return irBuilder.GetPayloadFromObject(cgVal->GetRawValue()); } if (targetCGType->GetSize()) { auto elementType = targetCGType->GetLLVMType(); auto srcPayload = irBuilder.GetPayloadFromObject(cgVal->GetRawValue()); auto castedPayload = irBuilder.CreateBitCast(srcPayload, elementType->getPointerTo(1U)); return irBuilder.CreateLoad(elementType, castedPayload); } return cgVal->GetRawValue(); } llvm::Value* HandleTransformToConcreteExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& cgMod = irBuilder.GetCGModule(); auto& castToConcreteExpr = StaticCast(chirExpr); auto sourceValue = castToConcreteExpr.GetSourceValue(); auto cgVal = *(cgMod | sourceValue); auto targetCHIRType = castToConcreteExpr.GetTargetTy(); auto targetCGType = CGType::GetOrCreate(cgMod, targetCHIRType); CJC_ASSERT(targetCGType->GetSize() && "target type must have size"); auto srcCHIRType = castToConcreteExpr.GetSourceTy(); const CGType* srcCGType = CGType::GetOrCreate(cgMod, srcCHIRType); if (IsGenericRef(*srcCHIRType)) { CJC_ASSERT(sourceValue->IsLocalVar()); auto localVarExpr = StaticCast(sourceValue)->GetExpr(); if (localVarExpr->GetExprKind() == CHIR::ExprKind::GET_ELEMENT_REF) { srcCHIRType = srcCHIRType->GetTypeArgs()[0]; auto [handleRefBB, handleNonRefBB, handleEndBB] = Vec2Tuple<3>(irBuilder.CreateAndInsertBasicBlocks({"handle_ref", "handle_non_ref", "handle_end"})); auto srcTi = irBuilder.CreateTypeInfo(*srcCHIRType); irBuilder.CreateCondBr(irBuilder.CreateTypeInfoIsReferenceCall(srcTi), handleRefBB, handleNonRefBB); irBuilder.SetInsertPoint(handleRefBB); srcCGType = srcCGType->GetPointerElementType(); auto tmp1 = irBuilder.CreateLoad(cgVal); irBuilder.CreateBr(handleEndBB); irBuilder.SetInsertPoint(handleNonRefBB); auto sizeOfSrc = irBuilder.GetSizeFromTypeInfo(srcTi); // 1. Allocate memory for boxing srcValue. llvm::Value* temp = irBuilder.CallIntrinsicAllocaGeneric({srcTi, sizeOfSrc}); // 2. store srcValue to temp auto basePtrOfSrc = cgMod.GetCGContext().GetBasePtrOf(cgVal.GetRawValue()); std::vector copyGenericParams{temp, basePtrOfSrc, cgVal.GetRawValue(), sizeOfSrc}; irBuilder.CallGCReadGeneric(copyGenericParams); irBuilder.CreateBr(handleEndBB); irBuilder.SetInsertPoint(handleEndBB); auto phi = irBuilder.CreatePHI(targetCGType->GetLLVMType(), 2U); // 2U: ref and nonRef branch phi->addIncoming(tmp1, handleRefBB); phi->addIncoming(temp, handleNonRefBB); cgMod.GetCGContext().SetBoxedValueMap(phi, cgVal.GetRawValue()); cgVal = CGValue(phi, srcCGType); } else { srcCHIRType = srcCHIRType->GetTypeArgs()[0]; srcCGType = srcCGType->GetPointerElementType(); cgVal = CGValue(irBuilder.CreateLoad(cgVal), srcCGType); } } if (srcCHIRType->IsGeneric() && targetCGType->IsReference()) { return cgVal.GetRawValue(); } if (targetCGType->GetSize() == srcCGType->GetSize()) { return cgVal.GetRawValue(); } auto elementType = targetCGType->GetLLVMType(); auto srcPayload = irBuilder.CreateBitCast( irBuilder.GetPayloadFromObject(cgVal.GetRawValue()), elementType->getPointerTo(1U)); return irBuilder.CreateLoad(elementType, srcPayload); } llvm::Value* HandleUnBoxToRefExpr(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& cgMod = irBuilder.GetCGModule(); auto& unboxToRefExpr = StaticCast(chirExpr); auto cgVal = (cgMod | unboxToRefExpr.GetSourceValue()); auto targetCHIRType = DeRef(*unboxToRefExpr.GetTargetTy()); auto targetCGType = CGType::GetOrCreate(cgMod, targetCHIRType); if (targetCGType->GetSize()) { return irBuilder.GetPayloadFromObject(cgVal->GetRawValue()); } else { return cgVal->GetRawValue(); } } llvm::Value* HandleGetRTTI(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& cgMod = irBuilder.GetCGModule(); auto& expr = StaticCast(chirExpr); auto value = irBuilder.GetTypeInfoFromObject(**(cgMod | expr.GetOperand())); return irBuilder.CreateBitCast(value, CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext())); } llvm::Value* HandleGetRTTIStatic(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { auto& cgMod = irBuilder.GetCGModule(); auto& expr = StaticCast(chirExpr); auto value = irBuilder.CreateTypeInfo(*expr.GetRTTIType()); return irBuilder.CreateBitCast(value, CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext())); } } // namespace namespace Cangjie::CodeGen { llvm::Value* HandleOthersExpression(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { CJC_ASSERT(chirExpr.GetExprMajorKind() == CHIR::ExprMajorKind::OTHERS); static const std::unordered_map> handleExprMap = { {CHIR::ExprKind::APPLY, HandleApplyExpr}, {CHIR::ExprKind::INVOKE, HandleInvokeExpr}, {CHIR::ExprKind::INVOKESTATIC, HandleInvokeStaticExpr}, {CHIR::ExprKind::DEBUGEXPR, HandleDebugExpr}, {CHIR::ExprKind::FIELD, HandleFieldExpr}, {CHIR::ExprKind::TYPECAST, HandleTypecastExpr}, {CHIR::ExprKind::TUPLE, HandleTupleExpr}, {CHIR::ExprKind::VARRAY, HandleVArrayExpr}, {CHIR::ExprKind::VARRAY_BUILDER, HandleVArrayBuilderExpr}, {CHIR::ExprKind::INTRINSIC, HandleIntrinsicExpr}, {CHIR::ExprKind::RAW_ARRAY_LITERAL_INIT, HandleRawArrayLiteralInitExpr}, {CHIR::ExprKind::RAW_ARRAY_ALLOCATE, HandleRawArrayAllocateExpr}, {CHIR::ExprKind::RAW_ARRAY_INIT_BY_VALUE, HandleRawArrayInitByValueExpr}, {CHIR::ExprKind::INSTANCEOF, HandleInstanceOfExpr}, {CHIR::ExprKind::SPAWN, HandleSpawnExpr}, {CHIR::ExprKind::GET_EXCEPTION, HandleGetExceptionExpr}, {CHIR::ExprKind::BOX, HandleBoxExpr}, {CHIR::ExprKind::UNBOX, HandleUnBoxExpr}, {CHIR::ExprKind::TRANSFORM_TO_GENERIC, HandleTransformToGenericExpr}, {CHIR::ExprKind::TRANSFORM_TO_CONCRETE, HandleTransformToConcreteExpr}, {CHIR::ExprKind::UNBOX_TO_REF, HandleUnBoxToRefExpr}, {CHIR::ExprKind::GET_RTTI, HandleGetRTTI}, {CHIR::ExprKind::GET_RTTI_STATIC, HandleGetRTTIStatic}, }; if (auto found = handleExprMap.find(chirExpr.GetExprKind()); found != handleExprMap.end()) { irBuilder.EmitLocation(CHIRExprWrapper(chirExpr)); return found->second(irBuilder, chirExpr); } printf("Unexpected expr kind: %" PRIu64 "\n", static_cast(chirExpr.GetExprKind())); CJC_ASSERT(false); return nullptr; } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/ExprDispatcher/TerminatorExprDispatcher.cpp000066400000000000000000000303021510705540100303750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/ExprDispatcher/ExprDispatcher.h" #include #include "Base/AllocateImpl.h" #include "Base/ApplyImpl.h" #include "Base/ArrayImpl.h" #include "Base/CGTypes/CGType.h" #include "Base/CHIRExprWrapper.h" #include "Base/IntrinsicsDispatcher.h" #include "Base/InvokeImpl.h" #include "Base/SpawnExprImpl.h" #include "Base/TypeCastImpl.h" #include "CGModule.h" #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "CJNative/CGCFFI.h" #endif #include "Utils/BlockScopeImpl.h" #include "Utils/CGCommonDef.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CodeGen { llvm::Value* HandleExitExpression(IRBuilder2& irBuilder, const CHIR::Exit& exitExpr) { auto& cgMod = irBuilder.GetCGModule(); auto& cgCtx = irBuilder.GetCGContext(); if (cgCtx.debugValue) { CJC_ASSERT(cgCtx.GetCompileOptions().enableCompileDebug); auto thisValue = irBuilder.GetInsertCGFunction()->GetArgByIndexFromCHIR(0); auto curCHIRFunc = DynamicCast(&irBuilder.GetInsertCGFunction()->GetOriginal()); CJC_NULLPTR_CHECK(curCHIRFunc); auto thisTy = curCHIRFunc->GetParam(0)->GetType(); auto cgType = CGType::GetOrCreate(cgMod, thisTy); auto thisCGValue = CGValue(thisValue, cgType); auto payload = irBuilder.GetPayloadFromObject(cgCtx.debugValue); irBuilder.CreateStore(CGValue(payload, cgType), thisCGValue); } if (auto func = irBuilder.GetInsertFunction(); func && func->hasFnAttribute(HAS_WITH_TI_WRAPPER_ATTR)) { auto debugLocOfRetExpr = exitExpr.GetDebugLocation(); cgCtx.AddDebugLocOfRetExpr(func, debugLocOfRetExpr); } auto parentFunc = exitExpr.GetTopLevelFunc(); CJC_NULLPTR_CHECK(parentFunc); auto retTy = parentFunc->GetReturnType(); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (retTy->IsUnit() || retTy->IsNothing() || retTy->IsVoid()) { return irBuilder.CreateRetVoid(); } #endif auto ret = parentFunc->GetReturnValue(); if (retTy->IsRawArray()) { CJC_ASSERT(ret && ret->GetExpr()->GetExprKind() == CHIR::ExprKind::RAW_ARRAY_ALLOCATE); return irBuilder.CreateRet(**(cgMod | ret)); } CJC_ASSERT(ret && "An unexpected nullptr is passed by CHIR."); CJC_ASSERT(ret->GetExpr()->GetExprKind() == CHIR::ExprKind::ALLOCATE); auto retVal = **(cgMod | ret); auto retType = CGType::GetOrCreate(cgMod, ret->GetType()->GetTypeArgs()[0])->GetLLVMType(); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto llvmRetType = irBuilder.getCurrentFunctionReturnType(); if (llvmRetType->isVoidTy()) { return irBuilder.CreateRetVoid(); } auto curLLVMFunc = irBuilder.GetInsertFunction(); CJC_NULLPTR_CHECK(curLLVMFunc); if (curLLVMFunc->hasFnAttribute(CodeGen::CFUNC_ATTR)) { retVal = cgMod.GetCGCFFI().ProcessRetValue(*llvmRetType, *retVal, irBuilder); return irBuilder.CreateRet(retVal); } #endif return irBuilder.CreateRet(irBuilder.CreateLoad(retType, retVal)); } llvm::Value* HandleTerminatorExpression(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr) { CJC_ASSERT(chirExpr.IsTerminator()); auto& cgMod = irBuilder.GetCGModule(); switch (chirExpr.GetExprKind()) { case CHIR::ExprKind::GOTO: { auto& goTo = StaticCast(chirExpr); auto succ = goTo.GetSuccessors()[0]; if (irBuilder.GetCGContext().GetCompileOptions().enableCompileDebug && !goTo.GetDebugLocation().IsInvalidPos() && chirExpr.GetParentBlock()->GetExpressionsNum() > 1) { auto func = irBuilder.GetInsertFunction(); auto gotoBB = llvm::BasicBlock::Create( irBuilder.GetLLVMContext(), "goto", func, cgMod.GetMappedBB(succ)); irBuilder.CreateBr(gotoBB); irBuilder.SetInsertPoint(gotoBB); } return irBuilder.CreateBr(cgMod.GetMappedBB(succ)); } case CHIR::ExprKind::EXIT: { auto& exitExpr = StaticCast(chirExpr); return HandleExitExpression(irBuilder, exitExpr); } case CHIR::ExprKind::BRANCH: { auto& branch = StaticCast(chirExpr); auto cond = **(cgMod | branch.GetCondition()); auto trueBranch = cgMod.GetMappedBB(branch.GetTrueBlock()); auto falseBranch = cgMod.GetMappedBB(branch.GetFalseBlock()); return irBuilder.CreateCondBr(cond, trueBranch, falseBranch); } case CHIR::ExprKind::MULTIBRANCH: { auto& multiBranch = StaticCast(chirExpr); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto cond = multiBranch.GetCondition(); CJC_ASSERT(cond->GetType()->IsInteger()); auto condVal = **(cgMod | cond); auto defaultBranch = cgMod.GetMappedBB(multiBranch.GetDefaultBlock()); auto switchInst = irBuilder.CreateSwitch(condVal, defaultBranch); size_t bitness = llvm::cast(condVal->getType())->getBitWidth(); size_t caseIdx = 1; // default block is the first successor, others start from the second. for (auto caseVal : multiBranch.GetCaseVals()) { auto labelBranch = cgMod.GetMappedBB(multiBranch.GetSuccessor(caseIdx++)); switchInst->addCase(irBuilder.getIntN(bitness, caseVal), labelBranch); } return switchInst; #endif } case CHIR::ExprKind::RAISE_EXCEPTION: { auto& raiseException = StaticCast(chirExpr); auto exceptionValue = raiseException.GetExceptionValue(); auto exceptionType = exceptionValue->GetType(); CJC_ASSERT((exceptionType->IsRef() || exceptionType->IsGeneric()) && "The target of the throw must be a generic or a reference to an object."); auto exceptionBlock = raiseException.GetExceptionBlock(); if (exceptionBlock) { CodeGenUnwindBlockScope unwindBlockScope(cgMod, cgMod.GetMappedBB(exceptionBlock)); irBuilder.CallExceptionIntrinsicThrow(cgMod | exceptionValue); } else { irBuilder.CallExceptionIntrinsicThrow(cgMod | exceptionValue); } (void)irBuilder.CreateUnreachable(); return nullptr; } case CHIR::ExprKind::APPLY_WITH_EXCEPTION: { auto& applyWithException = StaticCast(chirExpr); auto normalDest = applyWithException.GetSuccessor(0); auto unwindDest = applyWithException.GetSuccessor(1); CodeGenUnwindBlockScope unwindBlockScope(cgMod, cgMod.GetMappedBB(unwindDest)); auto resultVal = GenerateApply(irBuilder, CHIRApplyWrapper(applyWithException)); irBuilder.CreateBr(cgMod.GetMappedBB(normalDest)); return resultVal; } case CHIR::ExprKind::INVOKE_WITH_EXCEPTION: { auto& invokeWithException = StaticCast(chirExpr); auto normalDest = invokeWithException.GetSuccessor(0); auto unwindDest = invokeWithException.GetSuccessor(1); CodeGenUnwindBlockScope unwindBlockScope(cgMod, cgMod.GetMappedBB(unwindDest)); auto resultVal = GenerateInvoke(irBuilder, CHIRInvokeWrapper(invokeWithException)); irBuilder.CreateBr(cgMod.GetMappedBB(normalDest)); return resultVal; } case CHIR::ExprKind::INVOKESTATIC_WITH_EXCEPTION: { auto& invokeStaticWithException = StaticCast(chirExpr); auto normalDest = invokeStaticWithException.GetSuccessor(0); auto unwindDest = invokeStaticWithException.GetSuccessor(1); CodeGenUnwindBlockScope unwindBlockScope(cgMod, cgMod.GetMappedBB(unwindDest)); auto resultVal = GenerateInvokeStatic(irBuilder, CHIRInvokeStaticWrapper(invokeStaticWithException)); irBuilder.CreateBr(cgMod.GetMappedBB(normalDest)); return resultVal; } case CHIR::ExprKind::INT_OP_WITH_EXCEPTION: { auto& intOpWithException = StaticCast(chirExpr); auto normalDest = intOpWithException.GetSuccessor(0); auto unwindDest = intOpWithException.GetSuccessor(1); CodeGenUnwindBlockScope unwindBlockScope(cgMod, cgMod.GetMappedBB(unwindDest)); llvm::Value* resultVal = nullptr; if (intOpWithException.GetOperands().size() == 1) { resultVal = HandleUnaryExpression(irBuilder, CHIRUnaryExprWrapper(intOpWithException)); } else { resultVal = HandleBinaryExpression(irBuilder, CHIRBinaryExprWrapper(intOpWithException)); } irBuilder.CreateBr(cgMod.GetMappedBB(normalDest)); return resultVal; } case CHIR::ExprKind::SPAWN_WITH_EXCEPTION: { auto& spawnWithException = StaticCast(chirExpr); auto normalDest = spawnWithException.GetSuccessor(0); auto unwindDest = spawnWithException.GetSuccessor(1); CodeGenUnwindBlockScope unwindBlockScope(cgMod, cgMod.GetMappedBB(unwindDest)); auto resultVal = GenerateSpawn(irBuilder, CHIRSpawnWrapper(spawnWithException)); irBuilder.CreateBr(cgMod.GetMappedBB(normalDest)); return resultVal; } case CHIR::ExprKind::TYPECAST_WITH_EXCEPTION: { auto& typeCastWithException = StaticCast(chirExpr); auto normalDest = typeCastWithException.GetSuccessor(0); auto unwindDest = typeCastWithException.GetSuccessor(1); CodeGenUnwindBlockScope unwindBlockScope(cgMod, cgMod.GetMappedBB(unwindDest)); auto resultVal = GenerateTypeCast(irBuilder, CHIRTypeCastWrapper(typeCastWithException)); irBuilder.CreateBr(cgMod.GetMappedBB(normalDest)); return resultVal; } case CHIR::ExprKind::INTRINSIC_WITH_EXCEPTION: { auto& intrinsicWithException = StaticCast(chirExpr); auto normalDest = intrinsicWithException.GetSuccessor(0); auto unwindDest = intrinsicWithException.GetSuccessor(1); CodeGenUnwindBlockScope unwindBlockScope(cgMod, cgMod.GetMappedBB(unwindDest)); auto resultVal = GenerateIntrinsic(irBuilder, CHIRIntrinsicWrapper(intrinsicWithException)); irBuilder.CreateBr(cgMod.GetMappedBB(normalDest)); return resultVal; } case CHIR::ExprKind::ALLOCATE_WITH_EXCEPTION: { auto& allocateWithException = StaticCast(chirExpr); auto normalDest = allocateWithException.GetSuccessor(0); auto unwindDest = allocateWithException.GetSuccessor(1); CodeGenUnwindBlockScope unwindBlockScope(cgMod, cgMod.GetMappedBB(unwindDest)); auto resultVal = GenerateAllocate(irBuilder, CHIRAllocateWrapper(allocateWithException)); irBuilder.CreateBr(cgMod.GetMappedBB(normalDest)); return resultVal; } case CHIR::ExprKind::RAW_ARRAY_ALLOCATE_WITH_EXCEPTION: { auto& rawArrayWithException = StaticCast(chirExpr); auto normalDest = rawArrayWithException.GetSuccessor(0); auto unwindDest = rawArrayWithException.GetSuccessor(1); CodeGenUnwindBlockScope unwindBlockScope(cgMod, cgMod.GetMappedBB(unwindDest)); auto resultVal = GenerateRawArrayAllocate(irBuilder, CHIRRawArrayAllocateWrapper(rawArrayWithException)); irBuilder.CreateBr(cgMod.GetMappedBB(normalDest)); return resultVal; } default: { printf("Unexpected expr kind: %" PRIu64 "\n", static_cast(chirExpr.GetExprKind())); CJC_ASSERT(false); return nullptr; } } } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/ExprDispatcher/UnaryExprDispatcher.cpp000066400000000000000000000050501510705540100273510ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/ExprDispatcher/ExprDispatcher.h" #include #include "Base/CHIRExprWrapper.h" #include "Base/OverflowDispatcher.h" #include "IRBuilder.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CodeGen { llvm::Value* HandleNegExpression(IRBuilder2& irBuilder, llvm::Value* value) { if (value->getType()->isFloatingPointTy()) { return irBuilder.CreateFNeg(value, "FNeg"); } else { return irBuilder.CreateNeg(value, "Neg"); } } llvm::Value* HandleNonOverflowUnaryExpression(IRBuilder2& irBuilder, const CHIRUnaryExprWrapper& chirExpr) { auto value = **(irBuilder.GetCGModule() | chirExpr.GetOperand()); switch (chirExpr.GetUnaryExprKind()) { case CHIR::ExprKind::NEG: { return HandleNegExpression(irBuilder, value); } case CHIR::ExprKind::NOT: { return irBuilder.CreateXor(value, 1, "not"); } case CHIR::ExprKind::BITNOT: { return irBuilder.CreateNot(value, "bitNot"); } default: { printf("Unexpected expr kind: %" PRIu64 "\n", static_cast(chirExpr.GetUnaryExprKind())); CJC_ASSERT(false); return nullptr; } } } llvm::Value* HandleUnaryExpression(IRBuilder2& irBuilder, const CHIRUnaryExprWrapper& chirExpr) { OverflowStrategy overflowStrategy = chirExpr.GetOverflowStrategy(); const CHIR::ExprKind& kind = chirExpr.GetUnaryExprKind(); if (OPERATOR_KIND_TO_OP_MAP.find(kind) == OPERATOR_KIND_TO_OP_MAP.end() || overflowStrategy == OverflowStrategy::NA) { return HandleNonOverflowUnaryExpression(irBuilder, chirExpr); } const CHIR::Type* ty = chirExpr.GetResult()->GetType(); // There is a possibility of integer overflow when the result of an arithmetic expression is an integer type.(spec) if (overflowStrategy == OverflowStrategy::WRAPPING || !ty->IsInteger()) { return HandleNonOverflowUnaryExpression(irBuilder, chirExpr); } const CHIR::IntType* intTy = StaticCast(ty); auto& cgMod = irBuilder.GetCGModule(); auto cgValue = cgMod | chirExpr.GetOperand(); irBuilder.EmitLocation(chirExpr); return GenerateOverflow(irBuilder, overflowStrategy, kind, std::make_pair(intTy, nullptr), {cgValue}); } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/Base/InstanceOfExprImpl.cpp000066400000000000000000000055741510705540100242050ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements codegen for CHIR InstanceOf. */ #include "Base/InstanceOfImpl.h" #include "CGModule.h" #include "IRBuilder.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" using namespace Cangjie; using namespace CodeGen; llvm::Value* CodeGen::GenerateInstanceOf(IRBuilder2& irBuilder, const CHIR::InstanceOf& instanceOf) { // Match pattern: type match. auto object = instanceOf.GetObject(); auto targetCHIRType = instanceOf.GetType(); auto targetTi = irBuilder.CreateTypeInfo(*targetCHIRType); auto objectCHIRType = DeRef(*object->GetType()); auto objectVal = *(irBuilder.GetCGModule() | object); if (objectCHIRType->IsAny() || objectCHIRType->IsGeneric()) { auto instanceTi = irBuilder.GetTypeInfoFromObject(*objectVal); auto typeKind = irBuilder.GetTypeKindFromTypeInfo(instanceTi); auto isTuple = irBuilder.CreateICmpEQ(typeKind, irBuilder.getInt8(static_cast(UGTypeKind::UG_TUPLE))); auto [isTupleBB, nonTupleBB, endBB] = Vec2Tuple<3>(irBuilder.CreateAndInsertBasicBlocks({"isTuple", "nonTuple", "end"})); irBuilder.CreateCondBr(isTuple, isTupleBB, nonTupleBB); irBuilder.SetInsertPoint(isTupleBB); auto nullPtr = llvm::Constant::getNullValue(irBuilder.getInt8PtrTy()); auto isTupleRet = irBuilder.CallIntrinsicIsTupleTypeOf({*objectVal, nullPtr, targetTi}); irBuilder.CreateBr(endBB); irBuilder.SetInsertPoint(nonTupleBB); auto nonTupleRet = irBuilder.CallIntrinsicIsSubtype({instanceTi, targetTi}); irBuilder.CreateBr(endBB); irBuilder.SetInsertPoint(endBB); auto phi = irBuilder.CreatePHI(irBuilder.getInt1Ty(), 2U); phi->addIncoming(isTupleRet, isTupleBB); phi->addIncoming(nonTupleRet, nonTupleBB); return phi; } else if (objectCHIRType->IsClass()) { auto instanceTi = irBuilder.GetTypeInfoFromObject(*objectVal); return irBuilder.CallIntrinsicIsSubtype({instanceTi, targetTi}); } else if (objectCHIRType->IsTuple()) { auto i8PtrTy = llvm::Type::getInt8PtrTy(irBuilder.GetLLVMContext()); auto instanceTi = objectVal.GetCGType()->GetSize().has_value() ? irBuilder.CreateTypeInfo(objectCHIRType) : llvm::Constant::getNullValue(i8PtrTy); return irBuilder.CallIntrinsicIsTupleTypeOf({*objectVal, instanceTi, targetTi}); } return irBuilder.CallIntrinsicIsSubtype({irBuilder.CreateTypeInfo(objectCHIRType), targetTi}); } cangjie_compiler-1.0.7/src/CodeGen/Base/InstanceOfImpl.h000066400000000000000000000011601510705540100227760ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_INSTANCEOFIMPL_H #define CANGJIE_INSTANCEOFIMPL_H #include "llvm/IR/Value.h" namespace Cangjie { namespace CHIR { class InstanceOf; } namespace CodeGen { class IRBuilder2; llvm::Value* GenerateInstanceOf(IRBuilder2& irBuilder, const CHIR::InstanceOf& instanceOf); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_INSTANCEOFIMPL_H cangjie_compiler-1.0.7/src/CodeGen/Base/IntrinsicsDispatcher.cpp000066400000000000000000001121421510705540100246150ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file realizes generate Intrinsic APIs for codegen. */ #include "Base/IntrinsicsDispatcher.h" #include "Base/OverflowDispatcher.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/ToStringUtils.h" #include "cangjie/CHIR/Value.h" namespace Cangjie { namespace CodeGen { namespace { CGIntrinsicKind GetCGIntrinsicKind(CHIR::IntrinsicKind intrinsicKind) { switch (intrinsicKind) { case CHIR::IntrinsicKind::CG_UNSAFE_BEGIN: case CHIR::IntrinsicKind::CG_UNSAFE_END: return CGIntrinsicKind::UNSAFE_MARK; case CHIR::IntrinsicKind::CPOINTER_INIT0: case CHIR::IntrinsicKind::CPOINTER_INIT1: return CGIntrinsicKind::CPOINTER_INIT; case CHIR::IntrinsicKind::CSTRING_INIT: return CGIntrinsicKind::CSTRING_INIT; case CHIR::IntrinsicKind::INOUT_PARAM: return CGIntrinsicKind::INOUT_PARAM; case CHIR::IntrinsicKind::CROSS_ACCESS_BARRIER: case CHIR::IntrinsicKind::CREATE_EXPORT_HANDLE: case CHIR::IntrinsicKind::GET_EXPORTED_REF: case CHIR::IntrinsicKind::REMOVE_EXPORTED_REF: return CGIntrinsicKind::INTEROP; default: break; } if (IsOverflowIntrinsic(intrinsicKind)) { return CGIntrinsicKind::OVERFLOW_APPLY; } else if (IsArrayIntrinsic(intrinsicKind)) { return CGIntrinsicKind::ARRAY; } else if (IsSourceIntrinsic(intrinsicKind)) { return CGIntrinsicKind::SOURCE; } else if (IsBuiltinIntrinsic(intrinsicKind)) { return CGIntrinsicKind::BUILTIN; } else if (IsFutureIntrinsic(intrinsicKind)) { return CGIntrinsicKind::FUTURE; } else if (IsSyncIntrinsic(intrinsicKind)) { return CGIntrinsicKind::SYNC; } else if (IsMathIntrinsic(intrinsicKind)) { return CGIntrinsicKind::MATH; } else if (IsStackTraceIntrinsic(intrinsicKind)) { return CGIntrinsicKind::STACK_TRACE; } else if (IsReflectIntrinsic(intrinsicKind)) { return CGIntrinsicKind::REFLECT; } else if (IsVArrayIntrinsic(intrinsicKind)) { return CGIntrinsicKind::VARRAY; } else if (IsRuntimeIntrinsic(intrinsicKind)) { return CGIntrinsicKind::RUNTIME; } else if (IsExceptionCatchIntrinsic(intrinsicKind)) { return CGIntrinsicKind::EXCEPTION_CATCH; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND } else if (IsVectorIntrinsic(intrinsicKind)) { return CGIntrinsicKind::VECTOR; } else if (IsPreInitializeIntrinsic(intrinsicKind)) { return CGIntrinsicKind::PREINITIALIZE; #endif } return CGIntrinsicKind::UNKNOWN; } } // namespace std::vector HandleSyscallIntrinsicArguments( const IRBuilder2& irBuilder, const std::vector& nodeArgs) { std::vector args; for (auto arg : nodeArgs) { auto argVal = (irBuilder.GetCGModule() | arg); args.push_back(argVal); } return args; } llvm::Value* GenerateExceptionCatchIntrinsics(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { switch (intrinsic.GetIntrinsicKind()) { case CHIR::BEGIN_CATCH: { auto exceptionValue = irBuilder.GetCGModule() | intrinsic.GetOperand(0); return irBuilder.CallPostThrowExceptionIntrinsic(exceptionValue->GetRawValue()); } default: CJC_ASSERT(false && "unimplemented exception-catch intrinsic."); return nullptr; } } llvm::Value* GenerateArrayIndex(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic, bool isChecked) { auto& cgMod = irBuilder.GetCGModule(); auto arrayValue = cgMod | intrinsic.GetOperand(0); // array auto indexValue = cgMod | intrinsic.GetOperand(1); // index if (arrayValue && indexValue) { auto arrTy = static_cast(intrinsic.GetOperand(0)->GetType()->GetTypeArgs()[0]); CJC_NULLPTR_CHECK(arrTy); if (intrinsic.GetNumOfOperands() >= 3) { // An argument length of at least 3 is required // set the value at an index in an array using arr[index] = value auto value = cgMod | intrinsic.GetOperand(2); irBuilder.CallArrayIntrinsicSet(*arrTy, **arrayValue, **indexValue, *value, isChecked); return nullptr; } else { // get the value at an index in an array using arr[index] return irBuilder.CallArrayIntrinsicGet(*arrTy, **arrayValue, **indexValue, isChecked); } } return nullptr; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::Value* GenerateVarrayIntrinsics(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { auto parameters = HandleSyscallIntrinsicArguments(irBuilder, intrinsic.GetOperands()); auto varrPtr = parameters[0]; bool needIdxCheck = intrinsic.Get(); switch (intrinsic.GetIntrinsicKind()) { case CHIR::VARRAY_SET: { // varraySet(ref(varray), value, index) std::vector idxList{}; for (size_t i = 2; i < parameters.size(); i++) { idxList.emplace_back(parameters[i]->GetRawValue()); } if (needIdxCheck) { irBuilder.CallVArrayIntrinsicIndexCheck(varrPtr, idxList); } auto elePtr = irBuilder.CreateVArrayGEP(varrPtr, idxList, "varr.idx.set.gep"); irBuilder.CreateVArrayStore(parameters[1], elePtr); return nullptr; } case CHIR::VARRAY_GET: { // "varrayGet"(ref(varray), index1, index2...) std::vector idxList{}; for (size_t i = 1; i < parameters.size(); i++) { idxList.emplace_back(parameters[i]->GetRawValue()); } irBuilder.CallVArrayIntrinsicIndexCheck(varrPtr, idxList); auto elePtr = irBuilder.CreateVArrayGEP(varrPtr, idxList, "varr.idx.get.gep"); // If element type is varray/struct in a varray, it should return element pointer. // only when it be used we need to load it. auto retChirType = intrinsic.GetResult()->GetType(); auto retCGType = CGType::GetOrCreate(irBuilder.GetCGModule(), retChirType); return irBuilder.CreateLoad(retCGType->GetLLVMType(), elePtr); } default: CJC_ASSERT(false && "unimplemented varray intrinsic."); return nullptr; } } // Get the address of an element of array. llvm::Value* GenerateArrayGetElemRef(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic, bool isChecked) { CJC_ASSERT(intrinsic.GetIntrinsicKind() == CHIR::IntrinsicKind::ARRAY_GET_REF_UNCHECKED); CJC_ASSERT(intrinsic.GetNumOfOperands() == 2U); auto& cgMod = irBuilder.GetCGModule(); auto arrayCGValue = cgMod | intrinsic.GetOperand(0); // array auto indexCGValue = cgMod | intrinsic.GetOperand(1); // index CJC_ASSERT(arrayCGValue && indexCGValue); auto arrayVal = **arrayCGValue; auto indexVal = **indexCGValue; auto arrTy = StaticCast(intrinsic.GetOperand(0)->GetType()->GetTypeArgs()[0]); CJC_NULLPTR_CHECK(arrTy); if (!CGType::GetOrCreate(cgMod, arrTy)->GetSize()) { auto elemCGType = CGType::GetOrCreate(cgMod, arrTy->GetElementType()); llvm::Value* offset = irBuilder.CreateMul(irBuilder.GetSize_64(elemCGType->GetOriginal()), indexVal); offset = irBuilder.CreateAdd(offset, irBuilder.getInt64(irBuilder.GetPayloadOffset() + 8U)); // 8U:size of rawArray's len field auto elePtr = irBuilder.CreateInBoundsGEP(irBuilder.getInt8Ty(), arrayVal, offset); irBuilder.GetCGContext().SetBasePtr(elePtr, arrayVal); return irBuilder.CreateBitCast( elePtr, elemCGType->GetLLVMType()->getPointerTo(arrayVal->getType()->getPointerAddressSpace())); } else { return irBuilder.GetArrayElementAddr(*arrTy, arrayVal, indexVal, isChecked); } } /** * Create a memcpy or copyto by the specified ArrayCopyToInfo. * Since the restriction of `memcpy` and `copyto`, we have to split the array as small chunks if the array size >= 2G, * and `memcpy` or `copyto` each chunk. * var iterator = 0 * while (iterator + 2G ) < copylen) { * copyTo(src, // address of the source array (i8Ptr) * dst, // address of the destination array (i8Ptr) * srcArrPtr, // address of the source array (arrayTypePtr) * dstArrPtr, // address of the destination array (arrayTypePtr) * 2G, // size to memcpy or copyto * elemType, // array element type * srcIndex + iterator, // start index of source array * dstIndex + iterator) // start index of destination array * iterator += 2G * } * copyTo(src, dst, srcArrPtr, dstArrPtr, elemType, copylen - iterator, srcIndex + iterator ,dstIndex + iterator) */ void ArrayMemCpyOrCopyTo(IRBuilder2& irBuilder, const ArrayCopyToInfo& arrayCopyToInfo) { auto copylen = arrayCopyToInfo.dataSize; // In order to avoid long IR, If array size less than or equal to INT32_MAX, quit early. if (auto constVal = llvm::dyn_cast(copylen); constVal && *constVal->getValue().getRawData() <= INT32_MAX) { irBuilder.CreateCopyTo(arrayCopyToInfo); return; } auto [whileLoopHeader, whileLoopBody, afterWhileLoop] = Vec2Tuple<3>(irBuilder.CreateAndInsertBasicBlocks( {GenNameForBB("arr.copy.start"), GenNameForBB("arr.copy.body"), GenNameForBB("arr.copy.end")})); constexpr uint64_t align = 8; constexpr uint64_t alignDown = INT32_MAX & ~(align - 1); const auto SIZE_OF_SINGLE_COPY = irBuilder.getInt64(alignDown); auto i64Ty = irBuilder.getInt64Ty(); auto iterator = irBuilder.CreateEntryAlloca(i64Ty, nullptr, "iterator"); (void)irBuilder.CreateStore(irBuilder.getInt64(0), iterator); (void)irBuilder.CreateBr(whileLoopHeader); // generate whileLoopHeader // `while(iterator + 2G) < copylen) {` irBuilder.SetInsertPoint(whileLoopHeader); auto cmpResult = irBuilder.CreateICmpSLT( irBuilder.CreateAdd(irBuilder.CreateLoad(i64Ty, iterator), SIZE_OF_SINGLE_COPY), copylen, "max.size"); (void)irBuilder.CreateCondBr(cmpResult, whileLoopBody, afterWhileLoop); // generate whileLoopBody // `copyToFunc(src, dst, srcArrPtr, dstArrPtr, elemType, 2G, srcIndex + iterator, dstIndex + iterator)` irBuilder.SetInsertPoint(whileLoopBody); auto iteratorIndex = irBuilder.CreateLoad(i64Ty, iterator); irBuilder.CreateCopyTo(ArrayCopyToInfo(arrayCopyToInfo.srcBP, arrayCopyToInfo.dstBP, arrayCopyToInfo.srcArrPtr, arrayCopyToInfo.dstArrPtr, SIZE_OF_SINGLE_COPY, irBuilder.CreateAdd(iteratorIndex, arrayCopyToInfo.srcIndex), irBuilder.CreateAdd(iteratorIndex, arrayCopyToInfo.dstIndex), arrayCopyToInfo.elemType, SIZE_OF_SINGLE_COPY)); // `iterator += 2G` auto newIterator = irBuilder.CreateAdd(iteratorIndex, SIZE_OF_SINGLE_COPY); (void)irBuilder.CreateStore(newIterator, iterator); (void)irBuilder.CreateBr(whileLoopHeader); // generate afterWhileLoop // `copyToFunc(src, dst, srcArrPtr, dstArrPtr, elemType, copylen - iterator, srcIndex + iterator ,dstIndex + // iterator)` irBuilder.SetInsertPoint(afterWhileLoop); auto index = irBuilder.CreateLoad(i64Ty, iterator); auto residualSize = irBuilder.CreateSub(copylen, index); irBuilder.CreateCopyTo(ArrayCopyToInfo(arrayCopyToInfo.srcBP, arrayCopyToInfo.dstBP, arrayCopyToInfo.srcArrPtr, arrayCopyToInfo.dstArrPtr, residualSize, irBuilder.CreateAdd(index, arrayCopyToInfo.srcIndex), irBuilder.CreateAdd(index, arrayCopyToInfo.dstIndex), arrayCopyToInfo.elemType, residualSize)); } struct ArrayPosition { llvm::Value* array{nullptr}; llvm::Value* index{nullptr}; }; void CallArrayIntrinsicCopyTo(IRBuilder2& irBuilder, const ArrayPosition& dst, const ArrayPosition& src, llvm::Value* copyLen, const CHIR::RawArrayType& arrTy) { auto& cgMod = irBuilder.GetCGModule(); auto elemType = CGType::GetOrCreate(cgMod, arrTy.GetElementType()); CJC_NULLPTR_CHECK(elemType); auto typeSize = irBuilder.GetSize_64(*arrTy.GetElementType()); auto dataSize = irBuilder.CreateMul(copyLen, typeSize, "arr.data.len"); auto arrayType = CGArrayType::GenerateArrayLayoutType(cgMod, arrTy); auto srcArrPtr = irBuilder.CreateBitCast(irBuilder.GetPayloadFromObject(src.array), arrayType->getPointerTo(1u)); auto dstArrPtr = irBuilder.CreateBitCast(irBuilder.GetPayloadFromObject(dst.array), arrayType->getPointerTo(1u)); srcArrPtr = irBuilder.CreateStructGEP(arrayType, srcArrPtr, 1); dstArrPtr = irBuilder.CreateStructGEP(arrayType, dstArrPtr, 1); auto srcIndex = irBuilder.CreateMul(src.index, typeSize, "src.index"); auto dstIndex = irBuilder.CreateMul(dst.index, typeSize, "dst.index"); ArrayMemCpyOrCopyTo(irBuilder, ArrayCopyToInfo(src.array, dst.array, srcArrPtr, dstArrPtr, dataSize, srcIndex, dstIndex, elemType, copyLen)); } void GenerateArrayCopyTo(IRBuilder2& irBuilder, const std::vector& nodeArgs) { /** * CopyTo function must have 5 arguments. * SRC_ARR: src array. * DST_ARR: src array. * SRC_START: src array start index. * DST_START: dst array start index. * ARRAY_COPY_LEN: array copy length. */ enum class ARG { SRC_ARR = 0, DST_ARR, SRC_START, DST_START, ARRAY_COPY_LEN, NUM_OF_ELEMENTS }; CJC_ASSERT(nodeArgs.size() == static_cast(ARG::NUM_OF_ELEMENTS)); auto& cgMod = irBuilder.GetCGModule(); auto srcArr = cgMod | nodeArgs[static_cast(ARG::SRC_ARR)]; auto dstArr = cgMod | nodeArgs[static_cast(ARG::DST_ARR)]; auto srcStart = cgMod | nodeArgs[static_cast(ARG::SRC_START)]; auto dstStart = cgMod | nodeArgs[static_cast(ARG::DST_START)]; auto copyLen = cgMod | nodeArgs[static_cast(ARG::ARRAY_COPY_LEN)]; CJC_ASSERT(srcArr && dstArr && srcStart && dstStart && copyLen); auto arrTy = static_cast(nodeArgs[0]->GetType()->GetTypeArgs()[0]); ArrayPosition dstArrayPos = {**dstArr, **dstStart}; ArrayPosition srcArrayPos = {**srcArr, **srcStart}; CallArrayIntrinsicCopyTo(irBuilder, dstArrayPos, srcArrayPos, copyLen->GetRawValue(), *arrTy); } llvm::Value* CallArrayIntrinsicCopy( IRBuilder2& irBuilder, const CGValue& srcArr, const CGType& elemType, const CHIR::RawArrayType& arrTy) { auto& cgMod = irBuilder.GetCGModule(); auto srcLenVal = irBuilder.CallArrayIntrinsicGetSize(*srcArr); CJC_NULLPTR_CHECK(srcLenVal); auto dst = irBuilder.AllocateArray(arrTy, srcLenVal); auto arrayType = CGArrayType::GenerateArrayLayoutType(cgMod, arrTy); auto srcArrPtr = irBuilder.CreateBitCast(irBuilder.GetPayloadFromObject(srcArr.GetRawValue()), arrayType->getPointerTo(1u)); auto dstArrPtr = irBuilder.CreateBitCast(irBuilder.GetPayloadFromObject(dst), arrayType->getPointerTo(1u)); srcArrPtr = irBuilder.CreateStructGEP(arrayType, srcArrPtr, 1); dstArrPtr = irBuilder.CreateStructGEP(arrayType, dstArrPtr, 1); auto typeSizeVal = irBuilder.GetSize_64(elemType.GetOriginal()); auto dataSize = irBuilder.CreateMul(srcLenVal, typeSizeVal, "arr.data.len"); auto zeroVal = irBuilder.getInt64(0); ArrayCopyToInfo arrayCopyToInfo( srcArr.GetRawValue(), dst, srcArrPtr, dstArrPtr, dataSize, zeroVal, zeroVal, &elemType, srcLenVal); ArrayMemCpyOrCopyTo(irBuilder, arrayCopyToInfo); return dst; } inline bool IsVArrayPtrType(llvm::Type* type) { return type->isPointerTy() && GetPointerElementType(type)->isArrayTy(); } inline void InstrumentPointerOps( IRBuilder2& irBuilder, const std::string& instFunc, llvm::Value* ptr, llvm::Value* typeSize) { auto& llvmCtx = irBuilder.GetLLVMContext(); auto castVal = irBuilder.CreateBitCast(ptr, llvm::Type::getInt8PtrTy(llvmCtx)); auto& cgMod = irBuilder.GetCGModule(); irBuilder.CallIntrinsicFunction(llvm::Type::getVoidTy(llvmCtx), instFunc, {cgMod.CreateGhostCFuncArgValue(*castVal, *CGType::GetCStringCGType(cgMod)), cgMod.CreateGhostCFuncArgValue(*typeSize, *CGType::GetInt64CGType(cgMod))}, {FAST_NATIVE_ATTR}); } inline void InsertAsanInstrument([[maybe_unused]] const CGModule& cgMod, [[maybe_unused]] IRBuilder2& irBuilder, [[maybe_unused]] const CHIRIntrinsicWrapper& intrinsic, [[maybe_unused]] llvm::Value* gep, [[maybe_unused]] const std::string& asanFunc) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (cgMod.GetCGContext().GetCompileOptions().EnableAsan() || cgMod.GetCGContext().GetCompileOptions().EnableHwAsan()) { auto genericInfo = intrinsic.GetInstantiatedTypeArgs(); CJC_ASSERT(genericInfo.size() == 1); // CJ_MCC_AsanRead(uptr addr, uptr size) // CJ_MCC_AsanWrite(uptr addr, uptr size) auto typeSize = irBuilder.GetSize_64(*genericInfo[0]); InstrumentPointerOps(irBuilder, asanFunc, gep, typeSize); } #endif } llvm::Value* GenerateVectorIntrinsics(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { auto cgValArgs = HandleSyscallIntrinsicArguments(irBuilder, intrinsic.GetOperands()); std::vector args; transform(cgValArgs.begin(), cgValArgs.end(), back_inserter(args), [](const CGValue* value) { return value->GetRawValue(); }); const size_t idxArr1 = 0; const size_t idxOffset1 = 1; const size_t idxArr2 = 2; const size_t idxOffset2 = 3; switch (intrinsic.GetIntrinsicKind()) { case CHIR::IntrinsicKind::VECTOR_COMPARE_32: return irBuilder.EmitVectorCompare32(args[idxArr1], args[idxOffset1], args[idxArr2], args[idxOffset2]); case CHIR::IntrinsicKind::VECTOR_INDEX_BYTE_32: return irBuilder.EmitVectorIndexByte32(args[idxArr1], args[idxOffset1], args[idxArr2]); default: break; } CJC_ASSERT(false && "unreachable at GenerateVectorIntrinsic"); return nullptr; } llvm::Value* GenerateMathIntrinsics(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { auto parameters = HandleSyscallIntrinsicArguments(irBuilder, intrinsic.GetOperands()); std::vector args; transform(parameters.begin(), parameters.end(), back_inserter(args), [](const CGValue* value) { return value->GetRawValue(); }); return irBuilder.CallMathIntrinsics(intrinsic, args); } llvm::Value* GenerateInteropIntrinsics(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { auto parameters = HandleSyscallIntrinsicArguments(irBuilder, intrinsic.GetOperands()); return irBuilder.CallInteropIntrinsics(intrinsic, parameters); } #endif llvm::Value* ConvertCStringToCPointer(const IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { CJC_ASSERT(intrinsic.GetNumOfOperands() == 1); return **(irBuilder.GetCGModule() | intrinsic.GetOperand(0)); } inline llvm::Value* HandleValuePtr(IRBuilder2& irBuilder, llvm::Value* valPtr) { llvm::Type* type = valPtr->getType(); CJC_ASSERT(type->isPointerTy()); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // For cjnative backend, structTy/varrayTy is passed by reference if (IsStructPtrType(type) || IsVArrayPtrType(type)) { return valPtr; } #endif return irBuilder.CreateLoad(GetPointerElementType(valPtr->getType()), valPtr); } inline llvm::Type* GetPointerToWithSpecificAddrspace(llvm::Type* srcType, unsigned dstAddrspace) { CJC_ASSERT(srcType->isPointerTy() && "a pointer type is expected"); return GetPointerElementType(srcType)->getPointerTo(dstAddrspace); } llvm::Value* CPointerGetAddress(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { CJC_ASSERT(intrinsic.GetNumOfOperands() == 1); auto& cgMod = irBuilder.GetCGModule(); auto pointerRefPtr = **(cgMod | intrinsic.GetOperand(0)); auto cgRetTy = CGType::GetOrCreate(cgMod, intrinsic.GetResult()->GetType()); CJC_NULLPTR_CHECK(cgRetTy); auto retVal = irBuilder.CreatePtrToInt(pointerRefPtr, cgRetTy->GetLLVMType()); return retVal; } llvm::Value* CPointerRead(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { // The size of args must be 2. CJC_ASSERT(intrinsic.GetNumOfOperands() == 2); auto& cgMod = irBuilder.GetCGModule(); auto retTy = intrinsic.GetResult()->GetType(); auto pointerRefPtr = **(cgMod | intrinsic.GetOperand(0)); auto pointerIndex = **(cgMod | intrinsic.GetOperand(1)); llvm::Value* ret = nullptr; llvm::Value* gep = nullptr; if (retTy->IsGeneric()) { auto tiOfResult = irBuilder.CreateTypeInfo(retTy); auto retTySize = irBuilder.GetLayoutSize_32(*retTy); ret = irBuilder.CallIntrinsicAllocaGeneric({tiOfResult, retTySize}); auto destPayloadPtr = irBuilder.GetPayloadFromObject(ret); auto fixedTypeSize = irBuilder.CreateZExtOrTrunc(retTySize, llvm::Type::getInt64Ty(cgMod.GetLLVMContext())); auto offset = irBuilder.CreateMul(fixedTypeSize, pointerIndex); gep = irBuilder.CreateGEP(irBuilder.getInt8Ty(), pointerRefPtr, offset, "ele.ptr"); InsertAsanInstrument(cgMod, irBuilder, intrinsic, gep, "CJ_MCC_AsanRead"); irBuilder.CreateMemCpy(destPayloadPtr, llvm::MaybeAlign(), gep, llvm::MaybeAlign(), retTySize); } else { auto cgRetTy = CGType::GetOrCreate(cgMod, retTy); CJC_NULLPTR_CHECK(cgRetTy); llvm::Type* realValType = cgRetTy->GetLLVMType(); auto realValPtr = irBuilder.CreateBitCast(pointerRefPtr, realValType->getPointerTo()); gep = irBuilder.CreateGEP(GetPointerElementType(realValPtr->getType()), realValPtr, pointerIndex); InsertAsanInstrument(cgMod, irBuilder, intrinsic, gep, "CJ_MCC_AsanRead"); ret = HandleValuePtr(irBuilder, gep); } return ret; } llvm::Value* CPointerWrite(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { // The size of args must be 3. CJC_ASSERT(intrinsic.GetNumOfOperands() == 3); auto& cgMod = irBuilder.GetCGModule(); auto pointerRefPtr = **(cgMod | intrinsic.GetOperand(0)); auto indexArg = **(cgMod | intrinsic.GetOperand(1)); auto valueArg = **(cgMod | intrinsic.GetOperand(2)); // The 3rd is the T. llvm::Value* gep = nullptr; auto chirValueTy = intrinsic.GetOperand(2)->GetType(); // The 3rd is the T. CJC_NULLPTR_CHECK(chirValueTy); if (chirValueTy->IsGeneric()) { auto valueTySize = irBuilder.GetLayoutSize_32(*chirValueTy); auto fixedTypeSize = irBuilder.CreateZExtOrTrunc(valueTySize, llvm::Type::getInt64Ty(cgMod.GetLLVMContext())); auto offset = irBuilder.CreateMul(fixedTypeSize, indexArg); gep = irBuilder.CreateGEP(irBuilder.getInt8Ty(), pointerRefPtr, offset, "ele.ptr"); auto payloadPtr = irBuilder.GetPayloadFromObject(valueArg); InsertAsanInstrument(cgMod, irBuilder, intrinsic, gep, "CJ_MCC_AsanWrite"); irBuilder.CreateMemCpy(gep, llvm::MaybeAlign(), payloadPtr, llvm::MaybeAlign(), valueTySize); } else { auto paramType = IsStructPtrType(valueArg->getType()) ? GetPointerToWithSpecificAddrspace(valueArg->getType(), pointerRefPtr->getType()->getPointerAddressSpace()) : valueArg->getType()->getPointerTo(); auto realValPtr = irBuilder.CreateBitCast(pointerRefPtr, paramType); gep = irBuilder.CreateGEP(GetPointerElementType(realValPtr->getType()), realValPtr, indexArg); InsertAsanInstrument(cgMod, irBuilder, intrinsic, gep, "CJ_MCC_AsanWrite"); if (chirValueTy->IsStruct() || chirValueTy->IsUnit()) { auto layOut = cgMod.GetLLVMModule()->getDataLayout().getStructLayout( llvm::cast(GetPointerElementType(valueArg->getType()))); CJC_NULLPTR_CHECK(layOut); auto size = layOut->getSizeInBytes(); auto align = layOut->getAlignment(); irBuilder.CreateMemCpy(gep, align, valueArg, align, size); } else { irBuilder.CreateStore(valueArg, gep); } } return nullptr; } llvm::Value* CPointerAdd(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { // The size of args must be 2. CJC_ASSERT(intrinsic.GetNumOfOperands() == 2); auto& cgMod = irBuilder.GetCGModule(); auto pointerRefPtr = **(cgMod | intrinsic.GetOperand(0)); auto offset = **(cgMod | intrinsic.GetOperand(1)); auto& llvmCtx = irBuilder.GetLLVMContext(); // Get CPointer address. auto ptrAddress = irBuilder.CreatePtrToInt(pointerRefPtr, llvm::Type::getInt64Ty(llvmCtx)); // Get type size. auto chirRetTy = intrinsic.GetResult()->GetType(); CJC_ASSERT(chirRetTy->GetTypeArgs().size() == 1); auto typeSize = irBuilder.GetSize_64(*chirRetTy->GetTypeArgs()[0]); // Get actualOffset with size and make plus. auto mulRes = irBuilder.CreateMul(offset, typeSize, "mul"); auto addRes = irBuilder.CreateAdd(ptrAddress, mulRes, "add"); // Make new CPointer. auto realValType = CGType::GetOrCreate(cgMod, chirRetTy)->GetLLVMType(); auto retVal = irBuilder.CreateIntToPtr(addRes, realValType); return retVal; } llvm::Value* BitCast(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { CJC_ASSERT(intrinsic.GetNumOfOperands() == 1); auto& cgMod = irBuilder.GetCGModule(); auto resTy = CGType::GetOrCreate(cgMod, intrinsic.GetResult()->GetType()); auto fromVal = **(irBuilder.GetCGModule() | intrinsic.GetOperand(0)); return irBuilder.CreateBitCast(fromVal, resTy->GetLLVMType()); } llvm::Value* GenerateBuiltinCall(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { auto parameters = HandleSyscallIntrinsicArguments(irBuilder, intrinsic.GetOperands()); auto kind = intrinsic.GetIntrinsicKind(); switch (kind) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND case CHIR::IntrinsicKind::RAW_ARRAY_REFEQ: #endif case CHIR::IntrinsicKind::OBJECT_REFEQ: return irBuilder.CreateICmpEQ(**parameters[0], **parameters[1]); case CHIR::IntrinsicKind::OBJECT_ZERO_VALUE: return irBuilder.CallIntrinsicForUninitialized(*intrinsic.GetResult()->GetType()); case CHIR::IntrinsicKind::SIZE_OF: { auto typeArgs = intrinsic.GetInstantiatedTypeArgs(); return irBuilder.GetSize_Native(*typeArgs[0]); } case CHIR::IntrinsicKind::ALIGN_OF: { auto cgRetTy = CGType::GetOrCreate(irBuilder.GetCGModule(), intrinsic.GetResult()->GetType()); return irBuilder.GetAlign(*intrinsic.GetInstantiatedTypeArgs()[0], cgRetTy->GetLLVMType()); } case CHIR::IntrinsicKind::GET_TYPE_FOR_TYPE_PARAMETER: { auto typeArgs = intrinsic.GetInstantiatedTypeArgs(); auto ti = irBuilder.CreateTypeInfo(typeArgs[0]); return irBuilder.GetTypeForTypeParameter(ti); } case CHIR::IntrinsicKind::IS_SUBTYPE_TYPES: { auto typeArgs = intrinsic.GetInstantiatedTypeArgs(); auto leftTi = irBuilder.CreateTypeInfo(typeArgs[0]); auto rightTi = irBuilder.CreateTypeInfo(typeArgs[1]); return irBuilder.CallIntrinsicIsSubtype({leftTi, rightTi}); } case CHIR::IntrinsicKind::CPOINTER_GET_POINTER_ADDRESS: return CPointerGetAddress(irBuilder, intrinsic); case CHIR::IntrinsicKind::CPOINTER_READ: return CPointerRead(irBuilder, intrinsic); case CHIR::IntrinsicKind::CPOINTER_WRITE: return CPointerWrite(irBuilder, intrinsic); case CHIR::IntrinsicKind::CPOINTER_ADD: return CPointerAdd(irBuilder, intrinsic); case CHIR::IntrinsicKind::CSTRING_CONVERT_CSTR_TO_PTR: return ConvertCStringToCPointer(irBuilder, intrinsic); case CHIR::IntrinsicKind::BIT_CAST: return BitCast(irBuilder, intrinsic); case CHIR::IntrinsicKind::ARRAY_RELEASE_RAW_DATA: return irBuilder.ReleaseRawData(intrinsic); case CHIR::IntrinsicKind::ARRAY_ACQUIRE_RAW_DATA: return irBuilder.AcquireRawData(intrinsic); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND case CHIR::IntrinsicKind::IS_NULL: { auto value = **parameters[0]; return irBuilder.CreateICmpNE(value, llvm::Constant::getNullValue(value->getType())); } #endif default: #ifndef NDEBUG Errorln("unsupported intrinsic kind: ", CHIR::INTRINSIC_KIND_TO_STRING_MAP.at(kind)); #endif break; } CJC_ASSERT(false && "unimplemented builtin call."); return nullptr; } llvm::Value* GenerateArraySyscall(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { auto& cgMod = irBuilder.GetCGModule(); auto arrayValue = *(cgMod | intrinsic.GetOperand(0)); // array switch (intrinsic.GetIntrinsicKind()) { case CHIR::IntrinsicKind::ARRAY_GET: case CHIR::IntrinsicKind::ARRAY_SET: { return GenerateArrayIndex(irBuilder, intrinsic, true); } case CHIR::IntrinsicKind::ARRAY_GET_UNCHECKED: case CHIR::IntrinsicKind::ARRAY_SET_UNCHECKED: { return GenerateArrayIndex(irBuilder, intrinsic, false); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND case CHIR::IntrinsicKind::ARRAY_GET_REF_UNCHECKED: { return GenerateArrayGetElemRef(irBuilder, intrinsic, false); } #endif case CHIR::IntrinsicKind::ARRAY_BUILT_IN_COPY_TO: { GenerateArrayCopyTo(irBuilder, intrinsic.GetOperands()); return irBuilder.CreateNullValue(*intrinsic.GetResult()->GetType()); } case CHIR::IntrinsicKind::ARRAY_SIZE: return irBuilder.CallArrayIntrinsicGetSize(*arrayValue); case CHIR::IntrinsicKind::ARRAY_CLONE: { auto arr = intrinsic.GetOperand(0); auto arrTy = StaticCast(arr->GetType()->GetTypeArgs()[0]); CJC_ASSERT(arrTy); auto elemType = CGType::GetOrCreate(cgMod, arrTy->GetElementType()); return CallArrayIntrinsicCopy(irBuilder, arrayValue, *elemType, *arrTy); } case CHIR::IntrinsicKind::ARRAY_INIT: { auto arr = intrinsic.GetOperand(0); auto arrTy = StaticCast(arr->GetType()->GetTypeArgs()[0]); const int expectedArgs = 3; CJC_ASSERT(intrinsic.GetOperands().size() == expectedArgs); auto elemValue = **(cgMod | intrinsic.GetOperand(1)); auto size = **(cgMod | intrinsic.GetOperand(2)); irBuilder.CallArrayInit(arrayValue.GetRawValue(), size, elemValue, *arrTy); return irBuilder.CreateNullValue(*intrinsic.GetResult()->GetType()); } default: CJC_ASSERT(false && "Should not reach here"); return nullptr; } } llvm::Value* GeneratePreInitializeIntrinsics(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { auto& cgMod = irBuilder.GetCGModule(); auto runtimePreInitializePackageFunc = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_pre_initialize_package); auto i8Ptr = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); return irBuilder.LLVMIRBuilder2::CreateCall( runtimePreInitializePackageFunc, {llvm::ConstantPointerNull::get(i8Ptr)}); } llvm::Value* GenerateRuntimeIntrinsics(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { auto parameters = HandleSyscallIntrinsicArguments(irBuilder, intrinsic.GetOperands()); return irBuilder.CallRuntimeIntrinsics(intrinsic, parameters); } llvm::Value* GenerateSyncIntrinsics(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { auto parameters = HandleSyscallIntrinsicArguments(irBuilder, intrinsic.GetOperands()); return irBuilder.CallSyncIntrinsics(intrinsic, parameters); } llvm::Value* GenerateStackTraceIntrinsics(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { auto parameters = HandleSyscallIntrinsicArguments(irBuilder, intrinsic.GetOperands()); return irBuilder.CallStackTraceIntrinsic(intrinsic, parameters); } llvm::Value* GenerateFutureIntrinsics(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { auto parameters = HandleSyscallIntrinsicArguments(irBuilder, intrinsic.GetOperands()); return irBuilder.CallIntrinsic(intrinsic, parameters); } llvm::Value* GenerateReflectIntrinsic(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::vector parameters; if (intrinsic.GetIntrinsicKind() == CHIR::IntrinsicKind::GET_TYPE_BY_MANGLED_NAME) { auto instTypes = intrinsic.GetInstantiatedTypeArgs(); llvm::Value* arg = nullptr; if (instTypes[0]->IsGeneric()) { CJC_NULLPTR_CHECK(intrinsic.GetTopLevelFunc()); arg = llvm::cast(irBuilder.GetLLVMModule() ->getFunction(intrinsic.GetTopLevelFunc()->GetIdentifierWithoutPrefix()) ->getArg(0)); } else { arg = llvm::cast( CGType::GetOrCreate(irBuilder.GetCGModule(), DeRef(*instTypes[0]))->GetOrCreateTypeInfo()); } return irBuilder.CreateBitCast(arg, llvm::Type::getInt8PtrTy(irBuilder.GetLLVMContext())); } else { parameters = HandleSyscallIntrinsicArguments(irBuilder, intrinsic.GetOperands()); } return irBuilder.CallIntrinsic(intrinsic, parameters); #endif } llvm::Value* GenerateCPointerInit(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { auto& cgMod = irBuilder.GetCGModule(); auto retChirType = intrinsic.GetResult()->GetType(); CJC_ASSERT(intrinsic.GetNumOfOperands() <= 1); auto retVal = intrinsic.GetNumOfOperands() == 0 ? irBuilder.CreateNullValue(*retChirType) : **(cgMod | intrinsic.GetOperand(0)); if (IsFuncPtrType(retVal->getType())) { retVal = irBuilder.CreatePointerCast(retVal, CGType::GetOrCreate(cgMod, retChirType)->GetLLVMType()); } return retVal; } llvm::Value* GenerateCStringInit(const IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { CJC_ASSERT(intrinsic.GetNumOfOperands() == 1); return **(irBuilder.GetCGModule() | intrinsic.GetOperand(0)); } llvm::Value* GenerateInout(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto& cgCtx = irBuilder.GetCGContext(); if (cgCtx.GetCompileOptions().EnableAsan() || cgCtx.GetCompileOptions().EnableHwAsan()) { auto function = irBuilder.GetInsertFunction(); CJC_NULLPTR_CHECK(function); function->addFnAttr("address_sanitize_stack"); } #endif auto& cgMod = irBuilder.GetCGModule(); CJC_ASSERT(intrinsic.GetNumOfOperands() == 1); auto argVal = **(cgMod | intrinsic.GetOperand(0)); // All of CPointer will be translated to i8*. auto i8PtrTy = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); if (auto li = llvm::dyn_cast(argVal); li) { auto addr = li->getPointerOperand(); return irBuilder.CreatePointerCast(addr, i8PtrTy); } CJC_ASSERT(argVal->getType()->isPointerTy()); return irBuilder.CreatePointerCast(argVal, i8PtrTy); } llvm::Value* GenerateIntrinsic(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { using GenerateFunc = std::function; static const std::unordered_map generateFuncMap = { {CGIntrinsicKind::REFLECT, &GenerateReflectIntrinsic}, {CGIntrinsicKind::CPOINTER_INIT, &GenerateCPointerInit}, {CGIntrinsicKind::CSTRING_INIT, &GenerateCStringInit}, {CGIntrinsicKind::INOUT_PARAM, &GenerateInout}, {CGIntrinsicKind::ARRAY, &GenerateArraySyscall}, {CGIntrinsicKind::OVERFLOW_APPLY, &GenerateOverflowApply}, {CGIntrinsicKind::SYNC, &GenerateSyncIntrinsics}, {CGIntrinsicKind::STACK_TRACE, &GenerateStackTraceIntrinsics}, {CGIntrinsicKind::FUTURE, &GenerateFutureIntrinsics}, {CGIntrinsicKind::EXCEPTION_CATCH, &GenerateExceptionCatchIntrinsics}, {CGIntrinsicKind::BUILTIN, &GenerateBuiltinCall}, #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND {CGIntrinsicKind::VARRAY, &GenerateVarrayIntrinsics}, {CGIntrinsicKind::VECTOR, &GenerateVectorIntrinsics}, {CGIntrinsicKind::MATH, &GenerateMathIntrinsics}, {CGIntrinsicKind::PREINITIALIZE, &GeneratePreInitializeIntrinsics}, {CGIntrinsicKind::INTEROP, &GenerateInteropIntrinsics}, #endif {CGIntrinsicKind::RUNTIME, &GenerateRuntimeIntrinsics}, }; CGIntrinsicKind ikind = GetCGIntrinsicKind(intrinsic.GetIntrinsicKind()); auto iter = generateFuncMap.find(ikind); CJC_ASSERT(iter != generateFuncMap.end() && "Unsupported Syscall."); llvm::Value* retVal = iter->second(irBuilder, intrinsic); // For syscall func, the return value isn't obtained from the first argument. // Therefore, when the function returns a void type, we return the Unit type of Cangjie. // Otherwise, the return value of the syscall func cannot be assigned to Unit variable. return (retVal && retVal->getType()->isVoidTy()) ? irBuilder.GetCGModule().GenerateUnitTypeValue() : retVal; } } // namespace CodeGen } // namespace Cangjie cangjie_compiler-1.0.7/src/CodeGen/Base/IntrinsicsDispatcher.h000066400000000000000000000124551510705540100242700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares generate Intrinsic APIs for codegen. */ #ifndef CANGJIE_INTRINSICS_DISPATCHER_H #define CANGJIE_INTRINSICS_DISPATCHER_H #include "llvm/IR/Value.h" #include "CGModule.h" #include "IRBuilder.h" namespace Cangjie { namespace CHIR { class Intrinsic; } // namespace CHIR namespace CodeGen { class IRBuilder2; class CHIRIntrinsicWrapper; llvm::Value* GenerateIntrinsic(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic); // `CGIntrinsicKind` is used to classify the syscall CHIR node (see GetCGIntrinsicKind()) // and map to specific generate function (see GenerateIntrinsic()) enum class CGIntrinsicKind { UNSAFE_MARK, CPOINTER_INIT, CSTRING_INIT, INOUT_PARAM, NATIVE_CALL, ARRAY, ARRAY_SLICE, // Array slice intrinsics VECTOR, SOURCE, OVERFLOW_APPLY, // 'OVERFLOW' is already in math.h REFLECT, BUILTIN, SYNC, MATH, STACK_TRACE, IDENTITY_HASHCODE, FUTURE, NET, FFI_JAVA, INTEROP, VARRAY, RUNTIME, EXCEPTION_CATCH, PREINITIALIZE, UNKNOWN }; inline bool IsOverflowIntrinsic(const CHIR::IntrinsicKind intrinsicKind) { return (intrinsicKind >= CHIR::IntrinsicKind::OVERFLOW_CHECKED_ADD && intrinsicKind <= CHIR::IntrinsicKind::OVERFLOW_WRAPPING_NEG); } inline bool IsArrayIntrinsic(const CHIR::IntrinsicKind intrinsicKind) { return (intrinsicKind >= CHIR::IntrinsicKind::ARRAY_BUILT_IN_COPY_TO && intrinsicKind <= CHIR::IntrinsicKind::ARRAY_CLONE) || intrinsicKind == CHIR::IntrinsicKind::ARRAY_INIT; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND inline bool IsVectorIntrinsic(const CHIR::IntrinsicKind intrinsicKind) { return intrinsicKind >= CHIR::IntrinsicKind::VECTOR_COMPARE_32 && intrinsicKind <= CHIR::IntrinsicKind::VECTOR_INDEX_BYTE_32; } inline bool IsPreInitializeIntrinsic(const CHIR::IntrinsicKind intrinsicKind) { return intrinsicKind == CHIR::IntrinsicKind::PREINITIALIZE; } #endif inline bool IsSourceIntrinsic(const CHIR::IntrinsicKind intrinsicKind) { return (intrinsicKind == CHIR::IntrinsicKind::SOURCE_FILE || intrinsicKind == CHIR::IntrinsicKind::SOURCE_LINE); } const std::unordered_set BUILTIN_FUNC_SET = { /// This comment is used to keep the code neat under format. CHIR::IntrinsicKind::OBJECT_REFEQ, #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND CHIR::IntrinsicKind::RAW_ARRAY_REFEQ, #endif CHIR::IntrinsicKind::OBJECT_ZERO_VALUE, CHIR::IntrinsicKind::ARRAY_ACQUIRE_RAW_DATA, CHIR::IntrinsicKind::ARRAY_RELEASE_RAW_DATA, CHIR::IntrinsicKind::CPOINTER_GET_POINTER_ADDRESS, CHIR::IntrinsicKind::CPOINTER_READ, CHIR::IntrinsicKind::CPOINTER_WRITE, CHIR::IntrinsicKind::CPOINTER_ADD, CHIR::IntrinsicKind::CSTRING_CONVERT_CSTR_TO_PTR, CHIR::IntrinsicKind::BIT_CAST, CHIR::IntrinsicKind::SIZE_OF, CHIR::IntrinsicKind::ALIGN_OF, CHIR::IntrinsicKind::OBJECT_AS, CHIR::IntrinsicKind::IS_NULL, CHIR::IntrinsicKind::GET_TYPE_FOR_TYPE_PARAMETER, CHIR::IntrinsicKind::IS_SUBTYPE_TYPES, }; inline bool IsBuiltinIntrinsic(const CHIR::IntrinsicKind intrinsicKind) { return BUILTIN_FUNC_SET.find(intrinsicKind) != BUILTIN_FUNC_SET.end(); } inline bool IsFutureIntrinsic(const CHIR::IntrinsicKind intrinsicKind) { return CHIR::IntrinsicKind::FUTURE_INIT <= intrinsicKind && intrinsicKind <= CHIR::IntrinsicKind::SET_THREAD_OBJECT; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND inline bool IsSyncIntrinsic(const CHIR::IntrinsicKind intrinsicKind) { bool isSleep = intrinsicKind == CHIR::IntrinsicKind::SLEEP; return isSleep || (intrinsicKind >= CHIR::IntrinsicKind::ATOMIC_LOAD && intrinsicKind <= CHIR::IntrinsicKind::MULTICONDITION_NOTIFY_ALL); } #endif inline bool IsMathIntrinsic(const CHIR::IntrinsicKind intrinsicKind) { return (intrinsicKind >= CHIR::IntrinsicKind::ABS && intrinsicKind <= CHIR::IntrinsicKind::POWI); } inline bool IsStackTraceIntrinsic(const CHIR::IntrinsicKind intrinsicKind) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND return intrinsicKind == CHIR::IntrinsicKind::FILL_IN_STACK_TRACE || intrinsicKind == CHIR::IntrinsicKind::DECODE_STACK_TRACE; #endif } inline bool IsReflectIntrinsic(const CHIR::IntrinsicKind intrinsicKind) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND return (intrinsicKind > CHIR::IntrinsicKind::REFLECTION_INTRINSIC_START_FLAG && intrinsicKind < CHIR::IntrinsicKind::REFLECTION_INTRINSIC_END_FLAG); #endif } inline bool IsVArrayIntrinsic(const CHIR::IntrinsicKind intrinsicKind) { return (intrinsicKind == CHIR::IntrinsicKind::VARRAY_SET || intrinsicKind == CHIR::IntrinsicKind::VARRAY_GET); } inline bool IsRuntimeIntrinsic(const CHIR::IntrinsicKind intrinsicKind) { return CHIR::IntrinsicKind::INVOKE_GC <= intrinsicKind && intrinsicKind <= CHIR::IntrinsicKind::GET_NATIVE_THREAD_NUMBER; } inline bool IsExceptionCatchIntrinsic(const CHIR::IntrinsicKind intrinsicKind) { return intrinsicKind == CHIR::IntrinsicKind::BEGIN_CATCH; } } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_INTRINSICS_DISPATCHER_H cangjie_compiler-1.0.7/src/CodeGen/Base/InvokeImpl.cpp000066400000000000000000000163501510705540100225420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements codegen for CHIR Invoke. */ #include "Base/InvokeImpl.h" #include "Base/CHIRExprWrapper.h" #include "CGModule.h" #include "IRBuilder.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/Type.h" using namespace Cangjie; using namespace CodeGen; using namespace CHIR; namespace { llvm::MDTuple* GenObjTyMetaForVirtualCall(IRBuilder2& irBuilder, const CHIR::Type& ty) { CJC_ASSERT(ty.GetTypeArgs().size() <= 1); llvm::LLVMContext& llvmCtx = irBuilder.GetCGContext().GetLLVMContext(); std::vector objTyMeta{}; if (ty.GetTypeArgs().size() == 1) { objTyMeta = UnwindGenericRelateType(llvmCtx, ty); } else { std::string tiName = CGType::GetNameOfTypeInfoGV(ty); objTyMeta.emplace_back(llvm::MDString::get(llvmCtx, tiName)); } return llvm::MDTuple::get(llvmCtx, objTyMeta); } } // namespace llvm::Value* CodeGen::GenerateInvoke(IRBuilder2& irBuilder, const CHIRInvokeWrapper& invoke) { irBuilder.SetCHIRExpr(&invoke); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto& cgMod = irBuilder.GetCGModule(); auto objVal = cgMod | invoke.GetObject(); std::vector argsVal; for (auto arg : invoke.GetArgs()) { (void)argsVal.emplace_back(cgMod | arg); } auto funcType = invoke.GetMethodType(); auto& cgCtx = cgMod.GetCGContext(); auto objType = DeRef(*invoke.GetObject()->GetType()); llvm::Value* funcPtr = nullptr; if (!objType->IsAutoEnv()) { auto ti = irBuilder.GetTypeInfoFromObject(objVal->GetRawValue()); auto vtableOffset = invoke.GetVirtualMethodOffset(); auto idxOfVFunc = irBuilder.getInt64(vtableOffset); auto introType = StaticCast(invoke.GetOuterType(cgCtx.GetCHIRBuilder())); if (introType->GetClassDef()->IsInterface()) { auto introTi = irBuilder.CreateTypeInfo(introType); funcPtr = irBuilder.CallIntrinsicMTable({ti, introTi, idxOfVFunc}); } else { auto vtableSizeOfIntroType = cgCtx.GetVTableSizeOf(introType); CJC_ASSERT(vtableSizeOfIntroType > 0); auto idxOfIntroType = irBuilder.getInt64(vtableSizeOfIntroType - 1U); funcPtr = irBuilder.CallIntrinsicGetVTableFunc(ti, idxOfIntroType, idxOfVFunc); auto& llvmCtx = cgCtx.GetLLVMContext(); auto meta = llvm::MDTuple::get(llvmCtx, llvm::MDString::get(llvmCtx, GetTypeQualifiedName(*introType))); llvm::cast(funcPtr)->setMetadata("IntroType", meta); } if (introType->GetTypeArgs().size() <= 1) { auto objTyMeta = GenObjTyMetaForVirtualCall(irBuilder, *introType); llvm::cast(funcPtr)->setMetadata("objType", objTyMeta); } } else { auto i8PtrTy = irBuilder.getInt8PtrTy(); auto funcName = invoke.GetMethodName(); auto methodIdx = CHIR::GetMethodIdxInAutoEnvObject(funcName); auto payload = irBuilder.GetPayloadFromObject(**objVal); auto vritualPtr = irBuilder.CreateConstGEP1_32(i8PtrTy, irBuilder.LLVMIRBuilder2::CreateBitCast(payload, i8PtrTy->getPointerTo(1U)), static_cast(methodIdx), "virtualFPtr"); auto loadInst = irBuilder.LLVMIRBuilder2::CreateLoad(i8PtrTy, vritualPtr); loadInst->setMetadata(llvm::LLVMContext::MD_invariant_load, llvm::MDNode::get(cgMod.GetLLVMContext(), {})); funcPtr = llvm::cast(loadInst); } auto concreteFuncType = static_cast(CGType::GetOrCreate( cgMod, funcType, CGType::TypeExtraInfo{0, true, false, true, invoke.GetInstantiatedTypeArgs()})); funcPtr = irBuilder.CreateBitCast(funcPtr, concreteFuncType->GetLLVMFunctionType()->getPointerTo()); auto result = irBuilder.CreateCallOrInvoke(*concreteFuncType, funcPtr, argsVal); auto thisValue = **(cgMod | invoke.GetThisParam()); auto originalNonRefVal = cgCtx.GetOriginalNonRefValOfBoxedValue(thisValue); if (originalNonRefVal && invoke.TestVritualMethodAttr(cgCtx.GetCHIRBuilder(), CHIR::Attribute::MUT)) { auto [handleBoxedBB, handleEndBB] = Vec2Tuple<2>(irBuilder.CreateAndInsertBasicBlocks({"handle_boxed", "handle_end"})); auto thisValueTI = irBuilder.GetTypeInfoFromObject(thisValue); irBuilder.CreateCondBr(irBuilder.CreateTypeInfoIsReferenceCall(thisValueTI), handleEndBB, handleBoxedBB); irBuilder.SetInsertPoint(handleBoxedBB); auto basePtr = cgCtx.GetBasePtrOf(originalNonRefVal); auto sizeOfTI = irBuilder.GetSizeFromTypeInfo(thisValueTI); std::vector copyGenericParams{basePtr, originalNonRefVal, thisValue, sizeOfTI}; irBuilder.CallIntrinsicGCWriteGeneric(copyGenericParams); irBuilder.CreateBr(handleEndBB); irBuilder.SetInsertPoint(handleEndBB); } return result; #endif } llvm::Value* CodeGen::GenerateInvokeStatic(IRBuilder2& irBuilder, const CHIRInvokeStaticWrapper& invokeStatic) { irBuilder.SetCHIRExpr(&invokeStatic); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto& cgMod = irBuilder.GetCGModule(); std::vector argsVal{}; for (auto arg : invokeStatic.GetArgs()) { (void)argsVal.emplace_back(cgMod | arg); } llvm::Value* ti = **(cgMod | invokeStatic.GetRTTIValue()); llvm::Value* funcPtr = nullptr; auto vtableOffset = invokeStatic.GetVirtualMethodOffset(); auto idxOfVFunc = irBuilder.getInt64(vtableOffset); auto& cgCtx = cgMod.GetCGContext(); auto introType = StaticCast(invokeStatic.GetOuterType(cgCtx.GetCHIRBuilder())); if (introType->GetClassDef()->IsInterface()) { auto introTi = irBuilder.CreateTypeInfo(introType); funcPtr = irBuilder.CallIntrinsicMTable({ti, introTi, idxOfVFunc}); } else { auto vtableSizeOfIntroType = cgCtx.GetVTableSizeOf(introType); CJC_ASSERT(vtableSizeOfIntroType > 0); auto idxOfIntroType = irBuilder.getInt64(vtableSizeOfIntroType - 1U); funcPtr = irBuilder.CallIntrinsicGetVTableFunc(ti, idxOfIntroType, idxOfVFunc); auto& llvmCtx = cgCtx.GetLLVMContext(); auto meta = llvm::MDTuple::get(llvmCtx, llvm::MDString::get(llvmCtx, GetTypeQualifiedName(*introType))); llvm::cast(funcPtr)->setMetadata("IntroType", meta); } if (introType->GetTypeArgs().size() <= 1) { auto objTyMeta = GenObjTyMetaForVirtualCall(irBuilder, *introType); llvm::cast(funcPtr)->setMetadata("objType", objTyMeta); } auto funcType = invokeStatic.GetMethodType(); auto concreteFuncType = static_cast(CGType::GetOrCreate( cgMod, funcType, CGType::TypeExtraInfo{0, true, true, false, invokeStatic.GetInstantiatedTypeArgs()})); funcPtr = irBuilder.CreateBitCast(funcPtr, concreteFuncType->GetLLVMFunctionType()->getPointerTo()); return irBuilder.CreateCallOrInvoke(*concreteFuncType, funcPtr, argsVal, false, ti); #endif } cangjie_compiler-1.0.7/src/CodeGen/Base/InvokeImpl.h000066400000000000000000000014211510705540100222000ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements codegen for CHIR Invoke. */ #ifndef CANGJIE_INVOKE_H #define CANGJIE_INVOKE_H #include "llvm/IR/Value.h" namespace Cangjie { namespace CodeGen { class IRBuilder2; class CHIRInvokeWrapper; class CHIRInvokeStaticWrapper; llvm::Value* GenerateInvoke(IRBuilder2& irBuilder, const CHIRInvokeWrapper& invoke); llvm::Value* GenerateInvokeStatic(IRBuilder2& irBuilder, const CHIRInvokeStaticWrapper& invokeStatic); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_INVOKE_H cangjie_compiler-1.0.7/src/CodeGen/Base/LogicalOpImpl.cpp000066400000000000000000000100561510705540100231550ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/LogicalOpImpl.h" #include #include #include "Base/CHIRExprWrapper.h" #include "CGModule.h" #include "IRBuilder.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Value.h" namespace Cangjie { namespace CodeGen { llvm::Value* GenerateBooleanOperation(IRBuilder2& irBuilder, const CHIRBinaryExprWrapper& binOp) { using GenerateFunc = std::function&, llvm::Value*, llvm::Value*)>; using OperatorKind = CHIR::ExprKind; using namespace std::placeholders; static const std::map mapForFloat = { {OperatorKind::LT, std::bind(&IRBuilder2::CreateFCmpOLT, _1, _2, _3, "fcmpolt", nullptr)}, {OperatorKind::GT, std::bind(&IRBuilder2::CreateFCmpOGT, _1, _2, _3, "fcmpogt", nullptr)}, {OperatorKind::LE, std::bind(&IRBuilder2::CreateFCmpOLE, _1, _2, _3, "fcmpole", nullptr)}, {OperatorKind::GE, std::bind(&IRBuilder2::CreateFCmpOGE, _1, _2, _3, "fcmpoge", nullptr)}, {OperatorKind::EQUAL, std::bind(&IRBuilder2::CreateFCmpOEQ, _1, _2, _3, "fcmpoeq", nullptr)}, {OperatorKind::NOTEQUAL, std::bind(&IRBuilder2::CreateFCmpUNE, _1, _2, _3, "fcmpune", nullptr)}, }; static const std::map mapForUnsigned = { {OperatorKind::LT, std::bind(&IRBuilder2::CreateICmpULT, _1, _2, _3, "icmpult")}, {OperatorKind::GT, std::bind(&IRBuilder2::CreateICmpUGT, _1, _2, _3, "icmpugt")}, {OperatorKind::LE, std::bind(&IRBuilder2::CreateICmpULE, _1, _2, _3, "icmpule")}, {OperatorKind::GE, std::bind(&IRBuilder2::CreateICmpUGE, _1, _2, _3, "icmpuge")}, {OperatorKind::EQUAL, std::bind(&IRBuilder2::CreateICmpEQ, _1, _2, _3, "icmpeq")}, {OperatorKind::NOTEQUAL, std::bind(&IRBuilder2::CreateICmpNE, _1, _2, _3, "icmpne")}, }; static const std::map mapForOthers = { {OperatorKind::LT, std::bind(&IRBuilder2::CreateICmpSLT, _1, _2, _3, "icmpslt")}, {OperatorKind::GT, std::bind(&IRBuilder2::CreateICmpSGT, _1, _2, _3, "icmpsgt")}, {OperatorKind::LE, std::bind(&IRBuilder2::CreateICmpSLE, _1, _2, _3, "icmpsle")}, {OperatorKind::GE, std::bind(&IRBuilder2::CreateICmpSGE, _1, _2, _3, "icmpsge")}, {OperatorKind::EQUAL, std::bind(&IRBuilder2::CreateICmpEQ, _1, _2, _3, "icmpeq")}, {OperatorKind::NOTEQUAL, std::bind(&IRBuilder2::CreateICmpNE, _1, _2, _3, "icmpne")}, }; auto& cgMod = irBuilder.GetCGModule(); auto leftArg = binOp.GetLHSOperand(); auto leftArgTypeInfo = leftArg->GetType(); llvm::Value* valLeft = **(cgMod | leftArg); llvm::Value* valRight = **(cgMod | binOp.GetRHSOperand()); CJC_NULLPTR_CHECK(valLeft); CJC_NULLPTR_CHECK(valRight); if (valLeft->getType()->isIntegerTy() && valRight->getType()->isIntegerTy() && valLeft->getType()->getIntegerBitWidth() != valRight->getType()->getIntegerBitWidth()) { valLeft = irBuilder.CreateZExtOrTrunc(valLeft, valRight->getType()); } // special case if ((leftArgTypeInfo->IsUnit() || leftArgTypeInfo->IsNothing()) && (binOp.GetBinaryExprKind() == OperatorKind::EQUAL || binOp.GetBinaryExprKind() == OperatorKind::NOTEQUAL)) { return binOp.GetBinaryExprKind() == OperatorKind::EQUAL ? irBuilder.getTrue() : irBuilder.getFalse(); } std::map currentMap; if (leftArgTypeInfo->IsFloat()) { currentMap = mapForFloat; } else if (leftArgTypeInfo->IsInteger() && !StaticCast(leftArgTypeInfo)->IsSigned()) { currentMap = mapForUnsigned; } else { currentMap = mapForOthers; } auto iter = std::as_const(currentMap).find(binOp.GetBinaryExprKind()); return iter->second(irBuilder, valLeft, valRight); } } // namespace CodeGen } // namespace Cangjie cangjie_compiler-1.0.7/src/CodeGen/Base/LogicalOpImpl.h000066400000000000000000000011571510705540100226240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_LOGICALOP_IMPL_H #define CANGJIE_LOGICALOP_IMPL_H #include "llvm/IR/Value.h" namespace Cangjie { namespace CodeGen { class IRBuilder2; class CHIRBinaryExprWrapper; llvm::Value* GenerateBooleanOperation(IRBuilder2& irBuilder, const CHIRBinaryExprWrapper& binOp); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_LOGICALOP_IMPL_H cangjie_compiler-1.0.7/src/CodeGen/Base/OverflowDispatcher.cpp000066400000000000000000000166631510705540100243060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file realizes generate overflow APIs for codegen. */ #include "Base/OverflowDispatcher.h" #include "Base/ArithmeticOpImpl.h" #include "Base/ExprDispatcher/ExprDispatcher.h" #include "Base/IntrinsicsDispatcher.h" #include "Base/LogicalOpImpl.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Value.h" namespace Cangjie { namespace CodeGen { namespace { const auto OVERFLOW_FUNC_TO_INFO = []() noexcept { return std::unordered_map> { {CHIR::IntrinsicKind::OVERFLOW_THROWING_ADD, {CHIR::ExprKind::ADD, OverflowStrategy::THROWING}}, {CHIR::IntrinsicKind::OVERFLOW_SATURATING_ADD, {CHIR::ExprKind::ADD, OverflowStrategy::SATURATING}}, {CHIR::IntrinsicKind::OVERFLOW_WRAPPING_ADD, {CHIR::ExprKind::ADD, OverflowStrategy::WRAPPING}}, {CHIR::IntrinsicKind::OVERFLOW_CHECKED_ADD, {CHIR::ExprKind::ADD, OverflowStrategy::CHECKED}}, {CHIR::IntrinsicKind::OVERFLOW_THROWING_SUB, {CHIR::ExprKind::SUB, OverflowStrategy::THROWING}}, {CHIR::IntrinsicKind::OVERFLOW_SATURATING_SUB, {CHIR::ExprKind::SUB, OverflowStrategy::SATURATING}}, {CHIR::IntrinsicKind::OVERFLOW_WRAPPING_SUB, {CHIR::ExprKind::SUB, OverflowStrategy::WRAPPING}}, {CHIR::IntrinsicKind::OVERFLOW_CHECKED_SUB, {CHIR::ExprKind::SUB, OverflowStrategy::CHECKED}}, {CHIR::IntrinsicKind::OVERFLOW_THROWING_MUL, {CHIR::ExprKind::MUL, OverflowStrategy::THROWING}}, {CHIR::IntrinsicKind::OVERFLOW_SATURATING_MUL, {CHIR::ExprKind::MUL, OverflowStrategy::SATURATING}}, {CHIR::IntrinsicKind::OVERFLOW_WRAPPING_MUL, {CHIR::ExprKind::MUL, OverflowStrategy::WRAPPING}}, {CHIR::IntrinsicKind::OVERFLOW_CHECKED_MUL, {CHIR::ExprKind::MUL, OverflowStrategy::CHECKED}}, {CHIR::IntrinsicKind::OVERFLOW_THROWING_DIV, {CHIR::ExprKind::DIV, OverflowStrategy::THROWING}}, {CHIR::IntrinsicKind::OVERFLOW_SATURATING_DIV, {CHIR::ExprKind::DIV, OverflowStrategy::SATURATING}}, {CHIR::IntrinsicKind::OVERFLOW_WRAPPING_DIV, {CHIR::ExprKind::DIV, OverflowStrategy::WRAPPING}}, {CHIR::IntrinsicKind::OVERFLOW_CHECKED_DIV, {CHIR::ExprKind::DIV, OverflowStrategy::CHECKED}}, {CHIR::IntrinsicKind::OVERFLOW_THROWING_MOD, {CHIR::ExprKind::MOD, OverflowStrategy::THROWING}}, {CHIR::IntrinsicKind::OVERFLOW_SATURATING_MOD, {CHIR::ExprKind::MOD, OverflowStrategy::SATURATING}}, {CHIR::IntrinsicKind::OVERFLOW_WRAPPING_MOD, {CHIR::ExprKind::MOD, OverflowStrategy::WRAPPING}}, {CHIR::IntrinsicKind::OVERFLOW_CHECKED_MOD, {CHIR::ExprKind::MOD, OverflowStrategy::CHECKED}}, {CHIR::IntrinsicKind::OVERFLOW_THROWING_POW, {CHIR::ExprKind::EXP, OverflowStrategy::THROWING}}, {CHIR::IntrinsicKind::OVERFLOW_SATURATING_POW, {CHIR::ExprKind::EXP, OverflowStrategy::SATURATING}}, {CHIR::IntrinsicKind::OVERFLOW_WRAPPING_POW, {CHIR::ExprKind::EXP, OverflowStrategy::WRAPPING}}, {CHIR::IntrinsicKind::OVERFLOW_CHECKED_POW, {CHIR::ExprKind::EXP, OverflowStrategy::CHECKED}}, {CHIR::IntrinsicKind::OVERFLOW_THROWING_NEG, {CHIR::ExprKind::NEG, OverflowStrategy::THROWING}}, {CHIR::IntrinsicKind::OVERFLOW_SATURATING_NEG, {CHIR::ExprKind::NEG, OverflowStrategy::SATURATING}}, {CHIR::IntrinsicKind::OVERFLOW_WRAPPING_NEG, {CHIR::ExprKind::NEG, OverflowStrategy::WRAPPING}}, {CHIR::IntrinsicKind::OVERFLOW_CHECKED_NEG, {CHIR::ExprKind::NEG, OverflowStrategy::CHECKED}}, {CHIR::IntrinsicKind::OVERFLOW_THROWING_INC, {CHIR::ExprKind::ADD, OverflowStrategy::THROWING}}, {CHIR::IntrinsicKind::OVERFLOW_SATURATING_INC, {CHIR::ExprKind::ADD, OverflowStrategy::SATURATING}}, {CHIR::IntrinsicKind::OVERFLOW_WRAPPING_INC, {CHIR::ExprKind::ADD, OverflowStrategy::WRAPPING}}, {CHIR::IntrinsicKind::OVERFLOW_CHECKED_INC, {CHIR::ExprKind::ADD, OverflowStrategy::CHECKED}}, {CHIR::IntrinsicKind::OVERFLOW_THROWING_DEC, {CHIR::ExprKind::SUB, OverflowStrategy::THROWING}}, {CHIR::IntrinsicKind::OVERFLOW_SATURATING_DEC, {CHIR::ExprKind::SUB, OverflowStrategy::SATURATING}}, {CHIR::IntrinsicKind::OVERFLOW_WRAPPING_DEC, {CHIR::ExprKind::SUB, OverflowStrategy::WRAPPING}}, {CHIR::IntrinsicKind::OVERFLOW_CHECKED_DEC, {CHIR::ExprKind::SUB, OverflowStrategy::CHECKED}}, }; }(); bool IsIncOrDec(const CHIR::IntrinsicKind intrinsicKind) { switch (intrinsicKind) { case CHIR::IntrinsicKind::OVERFLOW_THROWING_INC: case CHIR::IntrinsicKind::OVERFLOW_SATURATING_INC: case CHIR::IntrinsicKind::OVERFLOW_WRAPPING_INC: case CHIR::IntrinsicKind::OVERFLOW_CHECKED_INC: case CHIR::IntrinsicKind::OVERFLOW_THROWING_DEC: case CHIR::IntrinsicKind::OVERFLOW_SATURATING_DEC: case CHIR::IntrinsicKind::OVERFLOW_WRAPPING_DEC: case CHIR::IntrinsicKind::OVERFLOW_CHECKED_DEC: return true; default: return false; } } } // namespace llvm::Value* GenerateOverflowWrappingArithmeticOp( IRBuilder2& irBuilder, const CHIR::ExprKind& kind, const CHIR::Type* ty, const std::vector& argGenValues) { // Overflow func implicit calling reproduce `Neg`, `Inc`, `Dec` Operations. if (kind == CHIR::ExprKind::NEG) { return HandleNegExpression(irBuilder, argGenValues[0]->GetRawValue()); } // Exp operation specially with fast power accelerating. if (kind == CHIR::ExprKind::EXP) { return GenerateBinaryExpOperation(irBuilder, argGenValues[0], argGenValues[1]); } return GenerateArithmeticOperation(irBuilder, kind, ty, argGenValues[0], argGenValues[1]); } llvm::Value* GenerateOverflowApply(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic) { const CHIR::IntrinsicKind intrinsicKind = intrinsic.GetIntrinsicKind(); std::vector args = intrinsic.GetOperands(); CJC_ASSERT(!args.empty()); const CHIR::Type* retType = intrinsic.GetResult()->GetType(); const CHIR::Type* paramType = args[0]->GetType(); // There is a possibility of integer overflow when the result of an arithmetic expression is an integer type. if (!paramType->IsInteger()) { #ifndef NDEBUG Errorln("The parameter type of the overflow intrinsic function is error."); #endif return nullptr; } const CHIR::IntType* intTy = StaticCast(paramType); auto info = OVERFLOW_FUNC_TO_INFO.at(intrinsicKind); auto chirKind = info.first; auto strategy = info.second; auto& cgMod = irBuilder.GetCGModule(); std::vector argGenValues; for (auto arg : args) { argGenValues.emplace_back(cgMod | arg); } std::pair tys = {intTy, nullptr}; if (strategy == OverflowStrategy::CHECKED) { tys = std::make_pair(intTy, retType); } if (!IsIncOrDec(intrinsicKind)) { return GenerateOverflow(irBuilder, strategy, chirKind, tys, argGenValues); } CGType* type = CGType::GetOrCreate(irBuilder.GetCGModule(), intTy); CGValue oneVal = CGValue(llvm::ConstantInt::get(type->GetLLVMType(), 1), type); argGenValues.emplace_back(&oneVal); return GenerateOverflow(irBuilder, strategy, chirKind, tys, argGenValues); } } // namespace CodeGen } // namespace Cangjie cangjie_compiler-1.0.7/src/CodeGen/Base/OverflowDispatcher.h000066400000000000000000000025111510705540100237360ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares generate overflow APIs for codegen. */ #ifndef CANGJIE_OVDERFLOW_DISPATCHER_H #define CANGJIE_OVDERFLOW_DISPATCHER_H #include "llvm/IR/Value.h" #include "Base/CHIRExprWrapper.h" #include "CGModule.h" #include "IRBuilder.h" namespace Cangjie { namespace CHIR { class Type; enum class ExprKind : uint8_t; } // namespace CHIR namespace CodeGen { class IRBuilder2; llvm::Value* GenerateOverflowBinaryExpression(IRBuilder2& irBuilder, const CHIR::Expression& chirExpr); llvm::Value* GenerateOverflowWrappingArithmeticOp(IRBuilder2& irBuilder, const CHIR::ExprKind& kind, const CHIR::Type* ty, const std::vector& argGenValues); llvm::Value* GenerateOverflow(IRBuilder2& irBuilder, const OverflowStrategy& strategy, const CHIR::ExprKind& kind, const std::pair& tys, const std::vector& argGenValues); llvm::Value* GenerateOverflowApply(IRBuilder2& irBuilder, const CHIRIntrinsicWrapper& intrinsic); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_OVDERFLOW_DISPATCHER_H cangjie_compiler-1.0.7/src/CodeGen/Base/SpawnExprImpl.cpp000066400000000000000000000112571510705540100232370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements codegen for CHIR Spawn creation. */ #include "Base/SpawnExprImpl.h" #include "Base/CHIRExprWrapper.h" #include "CGModule.h" #include "IRBuilder.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" using namespace Cangjie; using namespace CodeGen; namespace { // Check the spawn result and throw an exception if necessary. void GenerateCheckSpawnResult(IRBuilder2& irBuilder, llvm::Value* result) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto i8PtrTy = irBuilder.getInt8PtrTy(); auto failure = llvm::ConstantPointerNull::get(i8PtrTy); auto compare = irBuilder.CreateICmpEQ(result, failure); auto [throwBB, endBB] = Vec2Tuple<2>(irBuilder.CreateAndInsertBasicBlocks({GenNameForBB("if.throw"), GenNameForBB("if.end")})); (void)irBuilder.CreateCondBr(compare, throwBB, endBB); irBuilder.SetInsertPoint(throwBB); irBuilder.CreateSpawnException(); irBuilder.CreateUnreachable(); irBuilder.SetInsertPoint(endBB); #endif } llvm::Value* GenerateSpawnWithExecuteFuture(IRBuilder2& irBuilder, const CHIRSpawnWrapper& spawn) { auto& cgMod = irBuilder.GetCGModule(); auto futureObj = cgMod | spawn.GetFuture(); std::optional threadCtx = std::nullopt; if (spawn.GetSpawnArg()) { threadCtx = cgMod | spawn.GetSpawnArg(); } auto methods = StaticCast(spawn.GetFuture()->GetType()->GetTypeArgs()[0])->GetClassDef()->GetMethods(); auto it = std::find_if( methods.begin(), methods.end(), [](auto method) { return method->GetSrcCodeIdentifier() == "execute"; }); CJC_ASSERT(it != methods.end() && "The execute function is not found in Future."); auto cgFunc = cgMod.GetOrInsertCGFunction(*it); CJC_ASSERT(cgFunc); auto cjThreadHandle = irBuilder.CallSpawnIntrinsic(*futureObj, *cgFunc, threadCtx, true); CJC_NULLPTR_CHECK(cjThreadHandle); GenerateCheckSpawnResult(irBuilder, cjThreadHandle); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // The first field of `Future` must be a `Thread` object. auto threadObj = irBuilder.CreateLoad(irBuilder.getInt8PtrTy(1), irBuilder.CreateGEP(*futureObj, {0}).GetRawValue()); // Call the setter function. auto& llvmCtx = irBuilder.GetLLVMContext(); auto tiNullVal = llvm::ConstantPointerNull::get(CGType::GetOrCreateTypeInfoPtrType(llvmCtx)); irBuilder.CreateSetRuntimeCJThreadHandleCall( {irBuilder.CreateEntryAlloca(CGType::GetUnitType(llvmCtx)), threadObj, cjThreadHandle, tiNullVal}); #endif return futureObj->GetRawValue(); } llvm::Value* GenerateSpawnWithExecuteClosure(IRBuilder2& irBuilder, const CHIRSpawnWrapper& spawn) { auto& cgMod = irBuilder.GetCGModule(); auto closureNode = spawn.GetClosure(); auto closure = cgMod | closureNode; std::optional threadCtx = std::nullopt; if (spawn.GetSpawnArg()) { threadCtx = cgMod | spawn.GetSpawnArg(); } CJC_NULLPTR_CHECK(spawn.GetExecuteClosure()); auto cgFunc = cgMod.GetOrInsertCGFunction(spawn.GetExecuteClosure()); auto result = irBuilder.CallSpawnIntrinsic(*closure, *cgFunc, threadCtx, false, irBuilder.CreateTypeInfo(*spawn.GetResult()->GetType())); CJC_NULLPTR_CHECK(result); GenerateCheckSpawnResult(irBuilder, result); // The return value of the spawn expression (generated by this function) will not be used // so, just return nullptr. return nullptr; } } // namespace /** * Generate the spawn expression. * A spawn expression has two types of CHIR node shapes. * 1. Spawn(thunk { EXECUTE[ futureObj, threadCtx? ] }) * Create a thread and pass a future object as the argument, * the thread entry function is `Future.execute`. * 2. Spawn(thunk { EXECUTE_CLOSURE[ closure, threadCtx? ] }) * Create a thread and pass a closure as the argument, * the thread entry function is `Future.executeClosure`. * A spawn expression may contains a thread context, like `spawn(ctx) { ... }`. */ llvm::Value* CodeGen::GenerateSpawn(IRBuilder2& irBuilder, const CHIRSpawnWrapper& spawn) { if (!spawn.IsExecuteClosure()) { return GenerateSpawnWithExecuteFuture(irBuilder, spawn); } else { // generate for spawn with closure. return GenerateSpawnWithExecuteClosure(irBuilder, spawn); } // Determine which type of the spawn expression to generate } cangjie_compiler-1.0.7/src/CodeGen/Base/SpawnExprImpl.h000066400000000000000000000011251510705540100226750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_SPAWNEXPRIMPL_H #define CANGJIE_SPAWNEXPRIMPL_H #include "llvm/IR/Value.h" namespace Cangjie { namespace CodeGen { class IRBuilder2; class CHIRSpawnWrapper; llvm::Value* GenerateSpawn(IRBuilder2& irBuilder, const CHIRSpawnWrapper& spawn); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_SPAWNEXPRIMPL_H cangjie_compiler-1.0.7/src/CodeGen/Base/TupleExprImpl.cpp000066400000000000000000000342551510705540100232430ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements codegen for CHIR Tuple creation. */ #include "Base/TupleExprImpl.h" #include #include "Base/CGTypes/CGEnumType.h" #include "CGModule.h" #include "CJNative/CGTypes/EnumCtorTIOrTTGenerator.h" #include "IRBuilder.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/Type.h" using namespace Cangjie; using namespace CodeGen; llvm::Value* CodeGen::GenerateStruct(IRBuilder2& irBuilder, const CHIR::Tuple& tuple) { auto chirType = tuple.GetResult()->GetType(); CJC_ASSERT(chirType->IsStruct() && "Should not reach here."); auto structType = StaticCast(chirType); auto cgStructType = CGType::GetOrCreate(irBuilder.GetCGModule(), chirType)->GetLLVMType(); #ifdef NDEBUG auto structVal = irBuilder.CreateEntryAlloca(cgStructType, nullptr, "struct"); #else auto structVal = irBuilder.CreateEntryAlloca(cgStructType, nullptr, "struct." + tuple.GetResult()->GetIdentifierWithoutPrefix()); #endif auto& cgMod = irBuilder.GetCGModule(); auto& cgCtx = irBuilder.GetCGContext(); for (unsigned i = 0; i < tuple.GetOperands().size(); i++) { auto cgVal = *(cgMod | tuple.GetOperand(i)); CHIR::Type* fieldType = structType->GetCustomTypeDef()->GetInstanceVar(i).type; auto gepType = CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(cgCtx.GetCHIRBuilder(), *fieldType)); auto cgAddr = CGValue(irBuilder.CreateStructGEP(cgStructType, structVal, i), gepType); irBuilder.CreateStore(cgVal, cgAddr); } return structVal; } llvm::Value* CodeGen::GenerateNativeTuple(IRBuilder2& irBuilder, const CHIR::Tuple& tuple) { auto& cgMod = irBuilder.GetCGModule(); auto tupleType = StaticCast(tuple.GetResult()->GetType()); auto cgTupleType = CGType::GetOrCreate(cgMod, tupleType); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // With CJNATIVE-BE, we can enable the optimization for constant tuple. // As for a constant tuple, it can be directly represented by a const variable // rather than being created by calling ctor. auto [isConstantTuple, serialized] = IsConstantTuple(tuple); if (isConstantTuple) { std::vector args; for (unsigned i = 0; i < tuple.GetOperands().size(); i++) { args.push_back((cgMod | tuple.GetOperand(i))->GetRawValue()); } std::vector cons; std::for_each(args.begin(), args.end(), [&cons](llvm::Value* it) { auto tmp = llvm::dyn_cast(it); return cons.emplace_back(tmp ? tmp->getInitializer() : llvm::cast(it)); }); auto constVal = llvm::ConstantStruct::get(llvm::cast(cgTupleType->GetLLVMType()), cons); return cgMod.GetOrCreateGlobalVariable(constVal, serialized, true); } #endif auto& cgCtx = cgMod.GetCGContext(); auto res = irBuilder.CreateEntryAlloca(*cgTupleType); auto tupleCGValue = CGValue(res, CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(cgCtx.GetCHIRBuilder(), *tupleType))); for (unsigned i = 0; i < tuple.GetOperands().size(); i++) { auto cgVal = *(cgMod | tuple.GetOperand(i)); auto cgAddr = irBuilder.CreateGEP(tupleCGValue, {i}); if (res->getType()->getPointerAddressSpace() == 1U) { cgCtx.SetBasePtr(cgAddr.GetRawValue(), res); } irBuilder.CreateStore(cgVal, cgAddr); } return res; } namespace { llvm::Value* GenerateOptionLikeT(IRBuilder2& irBuilder, const CHIR::Tuple& tuple) { auto& cgMod = irBuilder.GetCGModule(); auto p1i8 = irBuilder.getInt8PtrTy(1U); auto chirEnumType = StaticCast(tuple.GetResult()->GetType()); auto enumTi = irBuilder.CreateTypeInfo(chirEnumType); auto enumVal = irBuilder.CallIntrinsicAllocaGeneric({enumTi, irBuilder.GetSizeFromTypeInfo(enumTi)}); auto payload = irBuilder.GetPayloadFromObject(enumVal); auto castedPayload = irBuilder.CreateBitCast(payload, p1i8); auto associatedType = chirEnumType->GetTypeArgs()[0]; bool hasAssociatedValue = tuple.GetNumOfOperands() > 1; auto isRef = irBuilder.CreateTypeInfoIsReferenceCall(*associatedType); auto [refBB, nonRefBB, endBB] = Vec2Tuple<3>(irBuilder.CreateAndInsertBasicBlocks({"ref", "nonRef", "endBB"})); irBuilder.CreateCondBr(isRef, refBB, nonRefBB); irBuilder.SetInsertPoint(refBB); if (hasAssociatedValue) { (void)irBuilder.CallGCWrite({**(cgMod | tuple.GetOperand(1)), enumVal, castedPayload}); } else { (void)irBuilder.CallGCWrite({llvm::Constant::getNullValue(p1i8), enumVal, castedPayload}); } irBuilder.CreateBr(endBB); irBuilder.SetInsertPoint(nonRefBB); (void)irBuilder.CreateStore( **(cgMod | tuple.GetOperand(0)), irBuilder.CreateBitCast(payload, irBuilder.getInt1Ty()->getPointerTo(1U))); if (hasAssociatedValue) { auto associatedValOffset = irBuilder.CallIntrinsicGetFieldOffset({enumTi, irBuilder.getInt64(1U)}); payload = irBuilder.CreateInBoundsGEP(irBuilder.getInt8Ty(), castedPayload, associatedValOffset); auto associatedTypeSize = irBuilder.GetLayoutSize_32(*associatedType); (void)irBuilder.CallIntrinsicGCWriteGeneric( {enumVal, payload, **(cgMod | tuple.GetOperand(1)), associatedTypeSize}); } irBuilder.CreateBr(endBB); irBuilder.SetInsertPoint(endBB); return enumVal; } llvm::Value* GenerateOptionLikeRef(IRBuilder2& irBuilder, const CHIR::Tuple& tuple) { auto& cgMod = irBuilder.GetCGModule(); // On CHIR side, non-constructor enum would be represented by a tuple with only one ArgStrict. auto isNone = tuple.GetNumOfOperands() <= 1; if (isNone) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND return llvm::Constant::getNullValue(CGType::GetRefType(irBuilder.GetLLVMContext())); #endif } auto someOp = tuple.GetOperand(1); return irBuilder.CallIntrinsicRef2Null(**(cgMod | someOp)); } llvm::Value* GenerateOptionLikeNonRef(IRBuilder2& irBuilder, const CHIR::Tuple& tuple) { auto& cgMod = irBuilder.GetCGModule(); bool hasAssociatedValue = tuple.GetNumOfOperands() > 1; auto chirEnumType = StaticCast(tuple.GetResult()->GetType()); auto cgEnumType = StaticCast(CGType::GetOrCreate(cgMod, chirEnumType)); if (cgEnumType->GetSize()) { auto enumVal = irBuilder.CreateEntryAlloca(*cgEnumType, "enum.val"); auto ptr0 = irBuilder.CreateStructGEP(cgEnumType->GetLLVMType(), enumVal, 0); (void)irBuilder.CreateStore(**(cgMod | tuple.GetOperand(0)), ptr0); auto associatedValueType = cgEnumType->GetAssociatedValueTypeOfOptionLike(); auto recordVal = hasAssociatedValue ? **(cgMod | tuple.GetOperand(1)) : irBuilder.CreateNullValue(*associatedValueType); irBuilder.CreateStore( recordVal, irBuilder.CreateStructGEP(cgEnumType->GetLLVMType(), enumVal, 1), associatedValueType); return enumVal; } else { auto enumTi = irBuilder.CreateTypeInfo(chirEnumType); auto enumVal = irBuilder.CallIntrinsicAllocaGeneric({enumTi, irBuilder.GetSizeFromTypeInfo(enumTi)}); auto payload = irBuilder.GetPayloadFromObject(enumVal); (void)irBuilder.CreateStore(**(cgMod | tuple.GetOperand(0)), irBuilder.CreateBitCast(payload, irBuilder.getInt1Ty()->getPointerTo(1U))); if (hasAssociatedValue) { auto associatedValOffset = irBuilder.CallIntrinsicGetFieldOffset({enumTi, irBuilder.getInt64(1U)}); payload = irBuilder.CreateInBoundsGEP(irBuilder.getInt8Ty(), irBuilder.CreateBitCast(payload, irBuilder.getInt8PtrTy(1U)), associatedValOffset); auto associatedType = chirEnumType->GetTypeArgs()[0]; auto associatedTypeSize = irBuilder.GetLayoutSize_32(*associatedType); (void)irBuilder.CallIntrinsicGCWriteGeneric( {enumVal, payload, **(cgMod | tuple.GetOperand(1)), associatedTypeSize}); } return enumVal; } } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::Value* GenerateAssociatedNonRefEnum(IRBuilder2& irBuilder, const CHIR::Tuple& tuple) { auto& cgMod = irBuilder.GetCGModule(); auto i8Ty = irBuilder.getInt8Ty(); irBuilder.EmitLocation(CHIRExprWrapper(tuple)); // 1. allocate memory for the Enum's constructor. auto chirEnumType = StaticCast(tuple.GetResult()->GetType()); auto cgEnumType = StaticCast(CGType::GetOrCreate(cgMod, chirEnumType)); auto enumVal = irBuilder.CreateEntryAlloca(*cgEnumType, "enum.val"); // 2. store the tag and associated values into the memory. auto offset = 0U; auto casted = irBuilder.CreateBitCast(enumVal, i8Ty->getPointerTo()); for (unsigned idx = 0U; idx < tuple.GetNumOfOperands(); ++idx) { auto field = tuple.GetOperand(idx); auto fieldCGType = CGType::GetOrCreate(cgMod, field->GetType()); auto destPtr = irBuilder.CreateConstGEP1_32(i8Ty, casted, offset); auto castedDestPtr = irBuilder.CreateBitCast(destPtr, fieldCGType->GetLLVMType()->getPointerTo()); (void)irBuilder.CreateStore(**(cgMod | field), castedDestPtr, field->GetType()); offset += fieldCGType->GetSize().value(); } return enumVal; } llvm::Value* GenerateCommonEnum(IRBuilder2& irBuilder, const CHIR::Tuple& tuple) { auto& cgMod = irBuilder.GetCGModule(); auto& cgCtx = cgMod.GetCGContext(); irBuilder.EmitLocation(CHIRExprWrapper(tuple)); // 1. get typeInfo of the Enum's constructor. auto enumType = StaticCast(tuple.GetResult()->GetType()); auto enumIdx = StaticCast(StaticCast(tuple.GetOperand(0))->GetExpr()); llvm::Value* enumCaseTi = cgMod.GetOrCreateEnumCtorTIOrTT(*enumType, enumIdx->GetUnsignedIntLitVal()); const auto& genericArgs = GetGenericArgsFromCHIRType(*enumType); if (!genericArgs.empty()) { std::vector typeArgs; for (auto typeArg : GetGenericArgsFromCHIRType(*enumType->GetEnumDef()->GetType())) { auto arg = GetTypeInnerType(*enumType, typeArg); (void)typeArgs.emplace_back(arg); } auto typeArgsArr = irBuilder.CreateTypeInfoArray( typeArgs, cgMod.GetOrInsertCGFunction(tuple.GetTopLevelFunc())->genericParamsMap); enumCaseTi = irBuilder.CallIntrinsicGetTypeInfo({enumCaseTi, irBuilder.getInt32(typeArgs.size()), typeArgsArr}); } // 2. allocate memory for the Enum's constructor. auto enumVal = irBuilder.CallClassIntrinsicAlloc({enumCaseTi, irBuilder.GetSizeFromTypeInfo(enumCaseTi)}); // 3. store tag and associated values if (genericArgs.empty()) { std::vector elemTypes; for (unsigned idx = 0; idx < tuple.GetNumOfOperands(); ++idx) { elemTypes.emplace_back(CGType::GetOrCreate(cgMod, tuple.GetOperand(idx)->GetType())->GetLLVMType()); } auto layoutType = llvm::StructType::get(cgCtx.GetLLVMContext(), elemTypes); auto payload = irBuilder.GetPayloadFromObject(enumVal); auto castedEnumValPtr = irBuilder.CreateBitCast(payload, layoutType->getPointerTo(1U)); for (unsigned idx = 0; idx < tuple.GetNumOfOperands(); ++idx) { auto field = tuple.GetOperand(idx); auto fieldType = field->GetType(); auto fieldAddrType = CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(cgCtx.GetCHIRBuilder(), *fieldType)); auto fieldPtr = irBuilder.CreateStructGEP(layoutType, castedEnumValPtr, idx); cgCtx.SetBasePtr(fieldPtr, enumVal); auto cgAddr = CGValue(fieldPtr, fieldAddrType); irBuilder.CreateStore(*(cgMod | field), cgAddr); } } else { for (unsigned idx = 0; idx < tuple.GetNumOfOperands(); ++idx) { auto field = tuple.GetOperand(idx); auto fieldType = field->GetType(); auto fieldAddrType = CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(cgCtx.GetCHIRBuilder(), *fieldType)); llvm::Value* fieldOffset = irBuilder.CallIntrinsicGetFieldOffset({enumCaseTi, irBuilder.getInt64(idx)}); fieldOffset = irBuilder.CreateAdd(fieldOffset, llvm::ConstantInt::get(fieldOffset->getType(), irBuilder.GetPayloadOffset())); auto fieldAddr = irBuilder.CreateInBoundsGEP(irBuilder.getInt8Ty(), enumVal, fieldOffset); if (!fieldType->IsGeneric()) { auto fieldCGType = CGType::GetOrCreate(cgMod, fieldType); fieldAddr = irBuilder.CreateBitCast(fieldAddr, fieldCGType->GetLLVMType()->getPointerTo(fieldAddr->getType()->getPointerAddressSpace())); } cgCtx.SetBasePtr(fieldAddr, enumVal); auto cgAddr = CGValue(fieldAddr, fieldAddrType); irBuilder.CreateStore(*(cgMod | field), cgAddr); } } return enumVal; } #endif } // namespace #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::Value* CodeGen::GenerateEnum(IRBuilder2& irBuilder, const CHIR::Tuple& tuple) { auto chirEnumType = StaticCast(tuple.GetResult()->GetType()); auto cgEnumType = StaticCast(CGType::GetOrCreate(irBuilder.GetCGModule(), chirEnumType)); CJC_ASSERT(!cgEnumType->IsTrivial() && "Trivial-Enum should not reach here."); if (cgEnumType->IsCommonEnum()) { return GenerateCommonEnum(irBuilder, tuple); } else if (cgEnumType->IsOptionLikeT()) { return GenerateOptionLikeT(irBuilder, tuple); } else if (cgEnumType->IsOptionLikeRef()) { return GenerateOptionLikeRef(irBuilder, tuple); } else if (cgEnumType->IsOptionLikeNonRef()) { return GenerateOptionLikeNonRef(irBuilder, tuple); } else if (cgEnumType->IsZeroSizeEnum()) { return irBuilder.CreateEntryAlloca(*cgEnumType); } else if (cgEnumType->IsAllAssociatedValuesAreNonRef()) { return GenerateAssociatedNonRefEnum(irBuilder, tuple); } CJC_ASSERT(false && "Should not reach here: UNKNOWN enum kind"); return nullptr; } #endif cangjie_compiler-1.0.7/src/CodeGen/Base/TupleExprImpl.h000066400000000000000000000013771510705540100227070ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_TUPLE_EXPR_IMPL_H #define CANGJIE_TUPLE_EXPR_IMPL_H #include "llvm/IR/Value.h" namespace Cangjie { namespace CHIR { class Tuple; } namespace CodeGen { class IRBuilder2; llvm::Value* GenerateNativeTuple(IRBuilder2& irBuilder, const CHIR::Tuple& tuple); llvm::Value* GenerateEnum(IRBuilder2& irBuilder, const CHIR::Tuple& tuple); llvm::Value* GenerateStruct(IRBuilder2& irBuilder, const CHIR::Tuple& tuple); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_TUPLE_EXPR_IMPL_H cangjie_compiler-1.0.7/src/CodeGen/Base/TypeCastImpl.cpp000066400000000000000000001017431510705540100230440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements codegen for CHIR TypeCast. */ #include "Base/TypeCastImpl.h" #include #include "Base/CGTypes/CGEnumType.h" #include "CGModule.h" #include "IRBuilder.h" #include "Utils/CGCommonDef.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Type/Type.h" using namespace Cangjie; using namespace CodeGen; namespace { std::string StringIntX(Triple::ArchType archType, const CHIR::IntType& intTy) { if (archType == Triple::ArchType::X86_64 || archType == Triple::ArchType::AARCH64) { if (intTy.IsIntNative()) { return "Int64"; } else if (intTy.IsUIntNative()) { return "UInt64"; } return intTy.ToString(); } else if (archType == Triple::ArchType::ARM32) { if (intTy.IsIntNative()) { return "Int32"; } else if (intTy.IsUIntNative()) { return "UInt32"; } return intTy.ToString(); } CJC_ASSERT(false && "Unsupported ArchType."); return intTy.ToString(); } // Key: AST::sourceTyString() + targetTy.String() // Value: [lowerBound, upperBound] const std::unordered_map> G_FLOAT2INT_BOUND_MAP = { {"Float16Int8", {0xC060200000000000, 0x4060000000000000}}, {"Float16Int16", {0xC0E0040000000000, 0x40E0000000000000}}, {"Float16Int32", {0x0000000000000000, 0x0000000000000000}}, // There must be no overflow {"Float16Int64", {0x0000000000000000, 0x0000000000000000}}, // There must be no overflow {"Float16UInt8", {0xBFF0000000000000, 0x4070000000000000}}, {"Float16UInt16", {0xBFF0000000000000, 0x40F0000000000000}}, {"Float16UInt32", {0xBFF0000000000000, 0x0000000000000000}}, // There must be no upper bound overflow. {"Float16UInt64", {0xBFF0000000000000, 0x0000000000000000}}, // There must be no upper bound overflow. {"Float32Int8", {0xC060200000000000, 0x4060000000000000}}, {"Float32Int16", {0xC0E0002000000000, 0x40E0000000000000}}, {"Float32Int32", {0xC1E0000020000000, 0x41E0000000000000}}, {"Float32Int64", {0xC3E0000020000000, 0x43E0000000000000}}, {"Float32UInt8", {0xBFF0000000000000, 0x4070000000000000}}, {"Float32UInt16", {0xBFF0000000000000, 0x40F0000000000000}}, {"Float32UInt32", {0xBFF0000000000000, 0x41F0000000000000}}, {"Float32UInt64", {0xBFF0000000000000, 0x43F0000000000000}}, {"Float64Int8", {0xC060200000000000, 0x4060000000000000}}, {"Float64Int16", {0xC0E0002000000000, 0x40E0000000000000}}, {"Float64Int32", {0xC1E0000000200000, 0x41E0000000000000}}, {"Float64Int64", {0xC3E0000000000001, 0x43E0000000000000}}, {"Float64UInt8", {0xBFF0000000000000, 0x4070000000000000}}, {"Float64UInt16", {0xBFF0000000000000, 0x40F0000000000000}}, {"Float64UInt32", {0xBFF0000000000000, 0x41F0000000000000}}, {"Float64UInt64", {0xBFF0000000000000, 0x43F0000000000000}}, }; bool NeedsIntToUIntConversion(IRBuilder2& irBuilder, const CHIR::IntType& srcTy, const CHIR::Type::TypeKind typeKind) { auto srcTypeKind = irBuilder.GetTypeKindFromType(srcTy); switch (srcTypeKind) { case CHIR::Type::TypeKind::TYPE_INT16: return typeKind >= CHIR::Type::TypeKind::TYPE_UINT16; case CHIR::Type::TypeKind::TYPE_INT32: return typeKind >= CHIR::Type::TypeKind::TYPE_UINT32; case CHIR::Type::TypeKind::TYPE_INT64: return typeKind >= CHIR::Type::TypeKind::TYPE_UINT64; case CHIR::Type::TypeKind::TYPE_INT_NATIVE: return typeKind >= CHIR::Type::TypeKind::TYPE_UINT64; default: return true; } } bool NeedsUIntToIntConversion(IRBuilder2& irBuilder, const CHIR::Type& srcTy, const CHIR::Type::TypeKind typeKind) { auto srcTypeKind = irBuilder.GetTypeKindFromType(srcTy); switch (srcTypeKind) { case CHIR::Type::TypeKind::TYPE_UINT8: return typeKind > CHIR::Type::TypeKind::TYPE_INT8; case CHIR::Type::TypeKind::TYPE_UINT16: return typeKind > CHIR::Type::TypeKind::TYPE_INT16; case CHIR::Type::TypeKind::TYPE_UINT32: return typeKind > CHIR::Type::TypeKind::TYPE_INT32; default: return false; } } llvm::Value* GenerateIntegerToFloatConvExpr( IRBuilder2& irBuilder, llvm::Value* srcValue, llvm::Type* targetType, const CHIR::IntType& srcTy) { if (srcTy.IsSigned()) { return irBuilder.CreateSIToFP(srcValue, targetType); } return irBuilder.CreateUIToFP(srcValue, targetType); } llvm::Value* GenerateFloatConvExpr(IRBuilder2& irBuilder, llvm::Value* srcValue, llvm::Type* targetType, const CHIR::NumericType& srcTy, const CHIR::NumericType& targetTy) { if (targetTy.GetBitness() > srcTy.GetBitness()) { return irBuilder.CreateFPExt(srcValue, targetType); } return irBuilder.CreateFPTrunc(srcValue, targetType); } inline bool IsUInt2Int(const CHIR::IntType& srcTy, const CHIR::IntType& targetTy) { return !srcTy.IsSigned() && targetTy.IsSigned(); } inline bool IsInt2UInt(const CHIR::IntType& srcTy, const CHIR::IntType& targetTy) { return srcTy.IsSigned() && !targetTy.IsSigned(); } inline bool IsInt2Int(const CHIR::IntType& srcTy, const CHIR::IntType& targetTy) { return srcTy.IsSigned() && targetTy.IsSigned(); } inline bool IsUInt2UInt(const CHIR::IntType& srcTy, const CHIR::IntType& targetTy) { return !srcTy.IsSigned() && !targetTy.IsSigned(); } llvm::Value* GenerateOverflowThrowingBasicBlock(IRBuilder2& irBuilder, const std::function& checkInfOrNaN, const std::function& checkLowerBound, const std::function& checkUpperBound, const std::function& typecast, const std::string& targetTyName) { auto [notInfOrNanBB, lowerBoundOKBB, upperBoundOKBB, upperBoundOverflowBB, lowerBoundOverflow, infOrNanBB, end] = Vec2Tuple<7>(irBuilder.CreateAndInsertBasicBlocks({GenNameForBB("not.inf.nan"), GenNameForBB("lower.bound.ok"), GenNameForBB("upper.bound.ok"), GenNameForBB("upper.bound.overflow"), GenNameForBB("lower.bound.overflow"), GenNameForBB("inf.or.nan"), GenNameForBB("overflow.throwing.end")})); if (checkInfOrNaN) { irBuilder.CreateCondBr(checkInfOrNaN(), notInfOrNanBB, infOrNanBB); irBuilder.SetInsertPoint(infOrNanBB); irBuilder.CreateOverflowOrArithmeticException("Casting Infinite or NaN value to integer."); irBuilder.CreateUnreachable(); irBuilder.SetInsertPoint(notInfOrNanBB); } else { (void)notInfOrNanBB->eraseFromParent(); (void)infOrNanBB->eraseFromParent(); } irBuilder.CreateCondBr(checkLowerBound(), lowerBoundOKBB, lowerBoundOverflow); // lower.bound.overflow. irBuilder.SetInsertPoint(lowerBoundOverflow); irBuilder.CreateOverflowOrArithmeticException("The result would be less than " + targetTyName + ".Min."); irBuilder.CreateUnreachable(); // Emit lower.bound.ok body. irBuilder.SetInsertPoint(lowerBoundOKBB); irBuilder.CreateCondBr(checkUpperBound(), upperBoundOKBB, upperBoundOverflowBB); // Emit upper.bound.overflow body. irBuilder.SetInsertPoint(upperBoundOverflowBB); irBuilder.CreateOverflowOrArithmeticException("The result would be greater than " + targetTyName + ".Max."); irBuilder.CreateUnreachable(); // Emit upper.bound.ok body. irBuilder.SetInsertPoint(upperBoundOKBB); auto typecastVal = typecast(irBuilder); irBuilder.CreateBr(end); // Emit end body. irBuilder.SetInsertPoint(end); return typecastVal; } llvm::Value* GenerateCondCheckInfiniteOrNaN( IRBuilder2& irBuilder, llvm::Value& srcValue, const CHIR::NumericType& srcTy) { auto& llvmCtx = irBuilder.GetLLVMContext(); using TypeKind = CHIR::Type::TypeKind; // Key: CHIR::TypeKind // Value:[bitWidth of srcTy, the vale if float is inf or nan in IEEE754, llvm::Type] const std::unordered_map> floatInIEEE754{ {TypeKind::TYPE_FLOAT16, {16, 0x7C00, llvm::Type::getInt16Ty(llvmCtx)}}, {TypeKind::TYPE_FLOAT32, {32, 0x7F800000, llvm::Type::getInt32Ty(llvmCtx)}}, {TypeKind::TYPE_FLOAT64, {64, 0x7FF0000000000000, llvm::Type::getInt64Ty(llvmCtx)}}, }; auto srcTyKind = irBuilder.GetTypeKindFromType(srcTy); unsigned bitwith = std::get<0>(floatInIEEE754.at(srcTyKind)); uint64_t magicVal = std::get<1>(floatInIEEE754.at(srcTyKind)); llvm::IntegerType* integerType = std::get<2>(floatInIEEE754.at(srcTyKind)); auto integerVal = irBuilder.CreateBitCast(&srcValue, integerType); auto exponentVal = llvm::ConstantInt::get(llvmCtx, llvm::APInt(bitwith, magicVal)); auto andVal = irBuilder.CreateAnd(integerVal, exponentVal); auto notInfOrNan = irBuilder.CreateICmpNE(andVal, exponentVal, "notInfOrNan"); return notInfOrNan; } template llvm::Value* GenerateCondCheckLowerBound( IRBuilder2& irBuilder, const CGType& targetType, llvm::Value& srcValue, const SrcT& srcTy) { auto& targetTy = StaticCast(targetType.GetOriginal()); if constexpr (std::is_same_v) { if (IsUInt2Int(srcTy, targetTy)) { return irBuilder.getTrue(); } else if (IsInt2UInt(srcTy, targetTy)) { auto minVal = llvm::ConstantInt::get(srcValue.getType(), 0); return irBuilder.CreateICmpSLE(minVal, &srcValue, "i2ui.lower"); } else if (IsInt2Int(srcTy, targetTy)) { if (irBuilder.GetTypeKindFromType(targetTy) > irBuilder.GetTypeKindFromType(srcTy)) { return irBuilder.getTrue(); } auto minVal = llvm::ConstantInt::getSigned( srcValue.getType(), GetIntMaxOrMin(irBuilder, targetTy, false)); return irBuilder.CreateICmpSLE(minVal, &srcValue, "i2i.lower"); } else { CJC_ASSERT(IsUInt2UInt(srcTy, targetTy)); return irBuilder.getTrue(); } } else { static_assert(std::is_same_v); /** * Conversion from floating-point to integer follows the round-toward-zero mode. * The round-toward-zero works by truncate the fractional part and round to the integer closest to zero. */ auto archType = irBuilder.GetCGContext().GetCompileOptions().target.GetArchType(); auto lowerBound = G_FLOAT2INT_BOUND_MAP.at(srcTy.ToString() + StringIntX(archType, targetTy)).first; if (lowerBound == 0) { return irBuilder.getTrue(); } auto lowerBoundValue = llvm::ConstantFP::get(srcValue.getType(), *reinterpret_cast(&lowerBound)); return irBuilder.CreateFCmpOLT(lowerBoundValue, &srcValue, "f2i.lt.min"); } } template llvm::Value* GenerateCondCheckUpperBound( IRBuilder2& irBuilder, const CGType& targetType, llvm::Value& srcValue, const SrcT& srcTy) { auto& targetTy = StaticCast(targetType.GetOriginal()); if constexpr (std::is_same_v) { auto targetTyKind = irBuilder.GetTypeKindFromType(targetTy); auto srcTyKind = irBuilder.GetTypeKindFromType(srcTy); if (IsUInt2Int(srcTy, targetTy)) { auto maxType = NeedsUIntToIntConversion(irBuilder, srcTy, targetTyKind) ? targetType.GetLLVMType() : srcValue.getType(); auto maxVal = llvm::ConstantInt::getSigned(maxType, GetIntMaxOrMin(irBuilder, targetTy, true)); auto tempSrcValue = NeedsUIntToIntConversion(irBuilder, srcTy, targetTyKind) ? irBuilder.CreateZExtOrTrunc(&srcValue, targetType.GetLLVMType()) : &srcValue; return irBuilder.CreateICmpULE(tempSrcValue, maxVal, "ui2i.upper"); } else if (IsInt2UInt(srcTy, targetTy)) { auto maxType = NeedsIntToUIntConversion(irBuilder, srcTy, targetTyKind) ? targetType.GetLLVMType() : srcValue.getType(); auto maxVal = llvm::ConstantInt::get(maxType, GetUIntMax(irBuilder, targetTy)); auto tempSrcValue = NeedsIntToUIntConversion(irBuilder, srcTy, targetTyKind) ? irBuilder.CreateZExtOrTrunc(&srcValue, targetType.GetLLVMType()) : &srcValue; return irBuilder.CreateICmpULE(tempSrcValue, maxVal, "i2ui.upper"); } else if (IsInt2Int(srcTy, targetTy)) { if (targetTyKind > srcTyKind) { return irBuilder.getTrue(); } auto maxVal = llvm::ConstantInt::getSigned(srcValue.getType(), GetIntMaxOrMin(irBuilder, targetTy, true)); return irBuilder.CreateICmpSLE(&srcValue, maxVal, "i2i.upper"); } else { CJC_ASSERT(IsUInt2UInt(srcTy, targetTy)); if (targetTyKind > srcTyKind) { return irBuilder.getTrue(); } auto maxVal = llvm::ConstantInt::getSigned(srcValue.getType(), static_cast(GetUIntMax(irBuilder, targetTy))); return irBuilder.CreateICmpULE(&srcValue, maxVal, "ui2ui.upper"); } } else { static_assert(std::is_same_v); /** * Conversion from floating-point to integer follows the round-toward-zero mode. * The round-toward-zero works by truncate the fractional part and round to the integer closest to zero. */ auto archType = irBuilder.GetCGContext().GetCompileOptions().target.GetArchType(); auto upperBound = G_FLOAT2INT_BOUND_MAP.at(srcTy.ToString() + StringIntX(archType, targetTy)).second; if (upperBound == 0) { return irBuilder.getTrue(); } auto upperBoundValue = llvm::ConstantFP::get(srcValue.getType(), *reinterpret_cast(&upperBound)); return irBuilder.CreateFCmpOLT(&srcValue, upperBoundValue, "f2i.gt.max"); } } llvm::Value* GenerateFloatToIntegerConvExpr(IRBuilder2& irBuilder, const OverflowStrategy& strategy, llvm::Value& origSrcValue, const CGType& targetType, const CHIR::FloatType& srcTy) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (strategy == OverflowStrategy::THROWING) { #endif auto checkInfOrNan = [&origSrcValue, &srcTy, &irBuilder] { return GenerateCondCheckInfiniteOrNaN(irBuilder, origSrcValue, srcTy); }; auto checkLowerBound = [&origSrcValue, &srcTy, &targetType, &irBuilder] { return GenerateCondCheckLowerBound(irBuilder, targetType, origSrcValue, srcTy); }; auto checkUpperBound = [&origSrcValue, &srcTy, &targetType, &irBuilder] { return GenerateCondCheckUpperBound(irBuilder, targetType, origSrcValue, srcTy); }; auto handleTypecast = [&origSrcValue, &targetType](IRBuilder2& irBuilder) { auto& targetTy = StaticCast(targetType.GetOriginal()); return targetTy.IsSigned() ? irBuilder.CreateFPToSI(&origSrcValue, targetType.GetLLVMType()) : irBuilder.CreateFPToUI(&origSrcValue, targetType.GetLLVMType()); }; return GenerateOverflowThrowingBasicBlock(irBuilder, checkInfOrNan, checkLowerBound, checkUpperBound, handleTypecast, targetType.GetOriginal().ToString()); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND } else { auto& targetTy = StaticCast(targetType.GetOriginal()); return irBuilder.CallIntrinsicFloatToIntegerSat(*targetType.GetLLVMType(), origSrcValue, targetTy.IsSigned()); } #endif } llvm::Value* GenerateOverflowSaturatingBasicBlock(IRBuilder2& irBuilder, const CGType& targetType, const std::function& checkLowerBound, const std::function& checkUpperBound, const std::function& typecast) { auto& targetTy = StaticCast(targetType.GetOriginal()); auto [lowerBoundOKBB, upperBoundOKBB, upperBoundOverflowBB, lowerBoundOverflow, satEndBB] = Vec2Tuple<5>( irBuilder.CreateAndInsertBasicBlocks({GenNameForBB("lower.bound.ok"), GenNameForBB("upper.bound.ok"), GenNameForBB("upper.bound.overflow"), GenNameForBB("lower.bound.overflow"), GenNameForBB("sat.end")})); (void)irBuilder.CreateCondBr(checkLowerBound(), lowerBoundOKBB, lowerBoundOverflow); // Emit lower.bound.overflow body. irBuilder.SetInsertPoint(lowerBoundOverflow); llvm::Value* retMin = targetTy.IsSigned() ? llvm::ConstantInt::getSigned(targetType.GetLLVMType(), GetIntMaxOrMin(irBuilder, targetTy, false)) : llvm::ConstantInt::get(targetType.GetLLVMType(), 0); irBuilder.CreateBr(satEndBB); // Emit lower.bound.ok body. irBuilder.SetInsertPoint(lowerBoundOKBB); irBuilder.CreateCondBr(checkUpperBound(), upperBoundOKBB, upperBoundOverflowBB); /// Emit upper.bound.overflow body. irBuilder.SetInsertPoint(upperBoundOverflowBB); llvm::Value* retMax = targetTy.IsSigned() ? llvm::ConstantInt::getSigned(targetType.GetLLVMType(),GetIntMaxOrMin(irBuilder, targetTy, true)) : llvm::ConstantInt::get(targetType.GetLLVMType(), GetUIntMax(irBuilder, targetTy)); irBuilder.CreateBr(satEndBB); /// Emit upper.bound.ok body. irBuilder.SetInsertPoint(upperBoundOKBB); auto typecastVal = typecast(irBuilder); irBuilder.CreateBr(satEndBB); // Emit sat.end body. irBuilder.SetInsertPoint(satEndBB); auto result = irBuilder.CreatePHI(targetType.GetLLVMType(), 3); // 3 branches for phi. result->addIncoming(typecastVal, upperBoundOKBB); result->addIncoming(retMin, lowerBoundOverflow); result->addIncoming(retMax, upperBoundOverflowBB); return result; } llvm::Value* GenerateIntegerConversionExpr(IRBuilder2& irBuilder, const OverflowStrategy& strategy, llvm::Value& srcValue, const CGType& targetType, const CHIR::IntType& srcTy) { // Get typecasted value. auto handleTypeCast = [&srcTy, &srcValue, &targetType](IRBuilder2& nativeBuilder) { auto isSrcSigned = srcTy.IsSigned(); return isSrcSigned ? nativeBuilder.CreateSExtOrTrunc(&srcValue, targetType.GetLLVMType()) : nativeBuilder.CreateZExtOrTrunc(&srcValue, targetType.GetLLVMType()); }; auto checkLowerBound = [&srcValue, &srcTy, &targetType, &irBuilder] { return GenerateCondCheckLowerBound(irBuilder, targetType, srcValue, srcTy); }; auto checkUpperBound = [&srcValue, &srcTy, &targetType, &irBuilder] { return GenerateCondCheckUpperBound(irBuilder, targetType, srcValue, srcTy); }; if (strategy == OverflowStrategy::SATURATING) { return GenerateOverflowSaturatingBasicBlock( irBuilder, targetType, checkLowerBound, checkUpperBound, handleTypeCast); } else if (strategy == OverflowStrategy::THROWING) { return GenerateOverflowThrowingBasicBlock(irBuilder, std::function(), checkLowerBound, checkUpperBound, handleTypeCast, targetType.GetOriginal().ToString()); } else { return handleTypeCast(irBuilder); } } void GenerateUnicodeScalarValueCheck(IRBuilder2& irBuilder, llvm::Value* srcValue, const CHIR::Type& srcTy) { auto srcTyKind = irBuilder.GetTypeKindFromType(srcTy); if (srcTyKind == CHIR::Type::TypeKind::TYPE_UINT8) { return; } auto type = CGType::GetOrCreate(irBuilder.GetCGModule(), &srcTy)->GetLLVMType(); auto bound1 = llvm::ConstantInt::get(type, 0); auto bound2 = llvm::ConstantInt::get(type, 0xD7FF); auto bound3 = llvm::ConstantInt::get(type, 0xE000); auto bound4 = llvm::ConstantInt::get(type, 0x10FFFF); llvm::Value* compareResult; if (srcTy.IsInteger() && StaticCast(srcTy).IsSigned()) { auto cond1 = irBuilder.CreateICmpSLT(srcValue, bound1); // i < 0 if (srcTyKind == CHIR::Type::TypeKind::TYPE_INT8 || srcTyKind == CHIR::Type::TypeKind::TYPE_INT16) { compareResult = cond1; // i < 0 } else { auto cond2 = irBuilder.CreateICmpSGT(srcValue, bound2); // i > 0xD7FF auto cond3 = irBuilder.CreateICmpSLT(srcValue, bound3); // i < 0XE000 auto cond4 = irBuilder.CreateICmpSGT(srcValue, bound4); // i > 0x10FFFF auto combo1 = irBuilder.CreateAnd(cond2, cond3); // i > 0xD7FF && i < 0XE000 auto combo2 = irBuilder.CreateOr(cond1, combo1); // i < 0 || (i > 0xD7FF && i < 0XE000) compareResult = irBuilder.CreateOr(combo2, cond4); // (i < 0 || (i > 0xD7FF && i < 0XE000)) || i > 0x10FFFF } } else { auto cond1 = irBuilder.CreateICmpUGT(srcValue, bound2); // i > 0xD7FF auto cond2 = irBuilder.CreateICmpULT(srcValue, bound3); // i < 0XE000 auto combo1 = irBuilder.CreateAnd(cond1, cond2); // i > 0xD7FF && i < 0XE000 if (srcTyKind == CHIR::Type::TypeKind::TYPE_UINT16) { compareResult = combo1; // i > 0xD7FF && i < 0XE000 } else { // UInt32 & UInt64 auto cond3 = irBuilder.CreateICmpUGT(srcValue, bound4); // i > 0x10FFFF compareResult = irBuilder.CreateOr(combo1, cond3); // (i > 0xD7FF && i < 0XE000) || i > 0x10FFFF } } auto [throwBB, endBB] = Vec2Tuple<2>(irBuilder.CreateAndInsertBasicBlocks({GenNameForBB("if.else"), GenNameForBB("if.end")})); (void)irBuilder.CreateCondBr(compareResult, throwBB, endBB); irBuilder.SetInsertPoint(throwBB); irBuilder.CreateOverflowOrArithmeticException("in Rune(num), num is not a valid Unicode scalar value!", true); irBuilder.CreateUnreachable(); // endBB irBuilder.SetInsertPoint(endBB); } llvm::Value* GenerateIntegerToRuneTypeCast(IRBuilder2& irBuilder, llvm::Value* srcValue, const CHIR::Type& srcTy) { GenerateUnicodeScalarValueCheck(irBuilder, srcValue, srcTy); auto type = CGType::GetUInt32CGType(irBuilder.GetCGModule()); if (irBuilder.GetTypeKindFromType(srcTy) == CHIR::Type::TypeKind::TYPE_UINT32) { return srcValue; } else { // Since we've checked the range of unicode scalar value, it's safe to use zext here. return irBuilder.CreateZExtOrTrunc(srcValue, type->GetLLVMType()); } } } // namespace namespace Cangjie { namespace CodeGen { llvm::Value* GenerateGenericTypeCast(IRBuilder2& irBuilder, const CGValue& cgSrcValue, const CHIR::Type& srcTy, const CHIR::Type& targetTy) { auto& cgMod = irBuilder.GetCGModule(); auto srcValue = *cgSrcValue; auto targetCGType = CGType::GetOrCreate(cgMod, &targetTy); auto srcCGType = CGType::GetOrCreate(cgMod, &srcTy); bool targetIsOptionLike = targetTy.IsEnum() ? StaticCast(targetCGType)->IsOptionLike() : false; bool srcIsOptionLike = srcTy.IsEnum() ? StaticCast(srcCGType)->IsOptionLike() : false; // auto cgSrcValue = (cgMod | typeCast.GetSourceValue()); if (srcTy.IsGeneric() && targetTy.IsGeneric()) { // T1 -> T2: do nothing return srcValue; } else if (srcTy.IsGeneric() && IsGenericRef(targetTy)) { // T1 -> T1&: Allocate and Store auto result = irBuilder.CreateEntryAlloca(targetCGType->GetLLVMType()); irBuilder.CreateStore(srcValue, result); return result; } else if (IsGenericRef(srcTy) && targetTy.IsGeneric()) { // T1& -> T1: Load auto targetType = CGType::GetOrCreate(cgMod, &targetTy); return irBuilder.CreateLoad(targetType->GetLLVMType(), srcValue); } else if (srcTy.IsGeneric() && (targetTy.IsStruct() || targetTy.IsRawArray() || targetTy.IsTuple() || IsTrivialEnum(targetTy) || targetIsOptionLike)) { // T1 -> struct/rawArray/Tuple/Enum if (targetCGType->GetSize()) { auto elementType = targetCGType->GetLLVMType(); auto srcPayload = irBuilder.CreateBitCast(irBuilder.GetPayloadFromObject(srcValue), elementType->getPointerTo(1U)); return irBuilder.CreateLoad(elementType, srcPayload); } return srcValue; } else if ((srcTy.IsStruct() || srcTy.IsRawArray() || srcTy.IsTuple() || IsTrivialEnum(srcTy) || srcIsOptionLike) && targetTy.IsGeneric()) { // struct/rawArray/Tuple/Enum -> T1 if (srcCGType->GetSize()) { // 1. Allocate memory for boxing srcValue. auto typeInfoOfSrc = irBuilder.CreateTypeInfo(srcTy); llvm::Value* temp = irBuilder.CallIntrinsicAllocaGeneric({typeInfoOfSrc, irBuilder.GetLayoutSize_32(srcTy)}); // 2. store srcValue to temp auto payloadPtr = irBuilder.GetPayloadFromObject(temp); auto addr = irBuilder.CreateBitCast(payloadPtr, srcCGType->GetLLVMType()->getPointerTo(1)); auto addrType = CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(cgMod.GetCGContext().GetCHIRBuilder(), srcTy), 1U); (void)irBuilder.CreateStore(cgSrcValue, CGValue(addr, addrType)); return temp; } return srcValue; } else if (srcTy.IsGeneric() && IsStructRef(targetTy)) { // T1 -> struct&: Incorrect CHIR node CJC_ASSERT(false && "Illegal GenericTypeCast."); return nullptr; } else if (IsStructRef(srcTy) && targetTy.IsGeneric()) { // struct& -> T1: Incorrect CHIR node CJC_ASSERT(false && "Illegal GenericTypeCast"); return nullptr; } else if (srcTy.IsGeneric() && (IsClassRef(targetTy) || targetTy.IsEnum())) { // T1 -> class&: do nothing return srcValue; } else if ((IsClassRef(srcTy) || srcTy.IsEnum()) && targetTy.IsGeneric()) { // class& -> T1: do nothing return srcValue; } else if (srcTy.IsGeneric() && IsDynamicStruct(targetTy)) { return nullptr; } else if (IsDynamicStruct(srcTy) && targetTy.IsGeneric()) { return nullptr; } else if (srcTy.IsGeneric() && IsDynamicStructRef(targetTy)) { return nullptr; } else if (IsDynamicStructRef(srcTy) && targetTy.IsGeneric()) { return nullptr; } else if (srcTy.IsGeneric() && IsDynamicClassRef(targetTy)) { // T1 -- class&: do nothing return srcValue; } else if (IsDynamicClassRef(srcTy) && targetTy.IsGeneric()) { // class& -- T1: do nothing return srcValue; } else if (IsStructRef(srcTy) && IsDynamicClassRef(targetTy)) { // struct& -> class& auto ti = irBuilder.CreateTypeInfo(srcTy); auto size = irBuilder.GetLayoutSize_32(srcTy); llvm::Value* temp = irBuilder.CallIntrinsicAllocaGeneric({ti, size}); irBuilder.CallGCWriteGenericPayload({temp, srcValue, size}); return temp; } else if (IsDynamicClassRef(srcTy) && IsStructRef(targetTy)) { // class& -> struct& return nullptr; } else if (srcTy.IsStructArray() && targetTy.IsStructArray()) { auto targetCGType = CGType::GetOrCreate(cgMod, &targetTy); auto tmp = irBuilder.CreateEntryAlloca(*targetCGType); llvm::MaybeAlign align{}; irBuilder.CreateMemCpy(tmp, align, srcValue, align, irBuilder.GetLayoutSize_32(targetTy)); return tmp; } else if (IsStaticStruct(srcTy) && IsDynamicStruct(targetTy)) { // struct -> struct auto ti = irBuilder.CreateTypeInfo(srcTy); auto size = irBuilder.GetLayoutSize_32(srcTy); llvm::Value* temp = irBuilder.CallIntrinsicAllocaGeneric({ti, size}); irBuilder.CallGCWriteGenericPayload({temp, srcValue, size}); return temp; } else if (IsDynamicStruct(srcTy) && IsStaticStruct(targetTy)) { // struct -> struct auto targetCGType = CGType::GetOrCreate(cgMod, &targetTy); auto tmp = irBuilder.CreateEntryAlloca(*targetCGType); auto dataPtr = irBuilder.GetPayloadFromObject(srcValue); if (IsTypeContainsRef(targetCGType->GetLLVMType())) { irBuilder.CallGCReadAgg({tmp, srcValue, dataPtr, irBuilder.GetLayoutSize_64(targetTy)}); } else { llvm::MaybeAlign align{}; irBuilder.CreateMemCpy(tmp, align, dataPtr, align, irBuilder.GetLayoutSize_32(targetTy)); } return tmp; } else if (IsStaticStruct(srcTy) && IsDynamicStruct(targetTy)) { // struct -> struct auto ti = irBuilder.CreateTypeInfo(srcTy); auto size = irBuilder.GetLayoutSize_32(srcTy); llvm::Value* temp = irBuilder.CallIntrinsicAllocaGeneric({ti, size}); irBuilder.CallGCWriteGenericPayload({temp, srcValue, size}); return temp; } else if (IsDynamicClass(srcTy) && IsStaticClass(targetTy)) { // class -> class: do nothing return srcValue; } else if (IsStaticClass(srcTy) && IsDynamicClass(targetTy)) { // class -> class: do nothing return srcValue; } else if ((srcTy.IsTuple() && targetTy.IsTuple()) && (!srcCGType->GetSize() && targetCGType->GetSize())) { auto elementType = targetCGType->GetLLVMType(); auto srcPayload = irBuilder.CreateBitCast(irBuilder.GetPayloadFromObject(srcValue), elementType->getPointerTo(1U)); return irBuilder.CreateLoad(elementType, srcPayload); } else { return nullptr; } } llvm::Value* GenerateTypeCast(IRBuilder2& irBuilder, const CHIRTypeCastWrapper& typeCast) { auto targetTy = typeCast.GetTargetTy(); auto srcTy = typeCast.GetSourceTy(); CJC_ASSERT(srcTy && targetTy); auto& cgMod = irBuilder.GetCGModule(); auto cgSrcValue = cgMod | typeCast.GetSourceValue(); auto srcValue = **cgSrcValue; if (srcTy == targetTy) { return srcValue; } auto result = GenerateGenericTypeCast(irBuilder, *cgSrcValue, *srcTy, *targetTy); if (result) { return result; } // No instructions are needed while typecasting an enum type to a tuple type. if (srcTy->IsEnum() && targetTy->IsTuple()) { return srcValue; } auto srcType = CGType::GetOrCreate(cgMod, srcTy); auto targetType = CGType::GetOrCreate(cgMod, targetTy); // No instructions are needed while typecasting enum and integer types. if ((targetTy->IsEnum() && srcTy->IsInteger()) || (targetTy->IsInteger() && srcTy->IsEnum())) { if (StaticCast(CGType::GetOrCreate(cgMod, targetTy->IsEnum() ? targetTy : srcTy))->IsTrivial()) { return srcValue; } else { return irBuilder.CreateNullValue(*targetTy); } } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (srcType->IsPointerType(1) && targetType->IsPointerType(1)) { return srcValue; } #endif if (targetTy->IsFloat() && srcTy->IsFloat()) { return GenerateFloatConvExpr(irBuilder, srcValue, targetType->GetLLVMType(), *StaticCast(srcTy), *StaticCast(targetTy)); } else if (targetTy->IsFloat() && srcTy->IsInteger()) { return GenerateIntegerToFloatConvExpr( irBuilder, srcValue, targetType->GetLLVMType(), *StaticCast(srcTy)); } else if (targetTy->IsInteger() && srcTy->IsFloat()) { return GenerateFloatToIntegerConvExpr( irBuilder, typeCast.GetOverflowStrategy(), *srcValue, *targetType, *StaticCast(srcTy)); } else if (irBuilder.GetTypeKindFromType(*targetTy) == CHIR::Type::TypeKind::TYPE_UINT32 && srcTy->IsRune()) { return srcValue; } else if (targetTy->IsRune() && srcTy->IsInteger()) { return GenerateIntegerToRuneTypeCast(irBuilder, srcValue, *srcTy); } else if (targetTy->IsInteger() && srcTy->IsRune()) { return irBuilder.CreateZExtOrTrunc(srcValue, targetType->GetLLVMType()); } else if (targetTy->IsTuple() && srcTy->IsTuple()) { return GenerateTupleTypeCast(irBuilder, *cgSrcValue, *targetType); } else if (targetTy->IsFunc() && srcTy->IsFunc()) { CJC_ASSERT(targetTy->GetTypeArgs().size() == srcTy->GetTypeArgs().size()); auto castedTo = static_cast(targetType) ->GetLLVMFunctionType() ->getPointerTo(srcValue->getType()->getPointerAddressSpace()); return irBuilder.CreateBitOrPointerCast(srcValue, castedTo); } else if (targetTy->IsInteger() && srcTy->IsInteger()) { return GenerateIntegerConversionExpr( irBuilder, typeCast.GetOverflowStrategy(), *srcValue, *targetType, *StaticCast(srcTy)); } else if (targetType->GetLLVMType() == srcType->GetLLVMType()) { return srcValue; } else if (IsCFunc(*targetTy) && srcTy->IsCPointer()) { return irBuilder.CreateBitOrPointerCast(srcValue, targetType->GetLLVMType()); } else if (targetTy->IsInteger() && srcTy->IsBoolean()) { // Handle TypeCast(Bool, Int) in CHIR #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND return irBuilder.CreateZExtOrTrunc(srcValue, targetType->GetLLVMType()); #endif } // In CHIR, if VArray as CFunc parameter type, it will translate to a reference. // `@C func c_f(a: VArray<...>&)` // c_f(inout varr) should typecast from CPointer> to VArray<...>&. if (IsVArrayRef(*targetTy) && srcTy->IsCPointer()) { return irBuilder.CreateBitOrPointerCast(srcValue, targetType->GetLLVMType()); } return irBuilder.CreateNullValue(*targetTy); } } // namespace CodeGen } // namespace Cangjie cangjie_compiler-1.0.7/src/CodeGen/Base/TypeCastImpl.h000066400000000000000000000016551510705540100225120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_TYPECASTIMPL_H #define CANGJIE_TYPECASTIMPL_H #include "llvm/IR/Value.h" namespace Cangjie { namespace CHIR { class Type; } namespace CodeGen { class CGType; class CGValue; class IRBuilder2; class CGValue; class CHIRTypeCastWrapper; llvm::Value* GenerateTypeCast(IRBuilder2& irBuilder, const CHIRTypeCastWrapper& typeCast); llvm::Value* GenerateTupleTypeCast(IRBuilder2& irBuilder, const CGValue& srcCGValue, const CGType& targetCGType); llvm::Value* GenerateGenericTypeCast(IRBuilder2& irBuilder, const CGValue& cgSrcValue, const CHIR::Type& srcTy, const CHIR::Type& targetTy); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_TYPECASTIMPL_H cangjie_compiler-1.0.7/src/CodeGen/Base/VArrayExprImpl.cpp000066400000000000000000000103521510705540100233460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements codegen for CHIR Varray creation. */ #include "Base/VArrayExprImpl.h" #include #include "CGModule.h" #include "IRBuilder.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" using namespace Cangjie; using namespace CodeGen; namespace { llvm::Value* GenerateConstantVArray( const IRBuilder2& irBuilder, const CHIR::VArray& varray, const std::string& serialized) { auto chirType = varray.GetResult()->GetType(); CJC_ASSERT(chirType->IsVArray() && "Should not reach here."); auto varrayChirType = StaticCast(chirType); auto varrayCGType = CGType::GetOrCreate(irBuilder.GetCGModule(), varrayChirType); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::vector params; for (size_t i = 0; i < varray.GetOperands().size(); ++i) { auto value = (irBuilder.GetCGModule() | varray.GetOperand(i))->GetRawValue(); auto tmp = llvm::dyn_cast(value); params.emplace_back(tmp ? tmp->getInitializer() : llvm::cast(value)); } auto arrayType = llvm::cast(varrayCGType->GetLLVMType()); auto constVal = llvm::ConstantArray::get(arrayType, params); return irBuilder.GetCGModule().GetOrCreateGlobalVariable(constVal, serialized, false); #endif } } // namespace llvm::Value* CodeGen::GenerateVArray(IRBuilder2& irBuilder, const CHIR::VArray& varray) { // let arr1: VArray = [1,2,3,4,5] auto [isConstantVArray, serialized] = IsConstantVArray(varray); if (isConstantVArray) { return GenerateConstantVArray(irBuilder, varray, serialized); } auto chirType = varray.GetResult()->GetType(); CJC_ASSERT(chirType->IsVArray() && "Should not reach here."); auto varrayChirType = StaticCast(chirType); auto varrayCGType = CGType::GetOrCreate(irBuilder.GetCGModule(), varrayChirType); auto varrayType = varrayCGType->GetLLVMType(); auto varrayPtr = irBuilder.CreateEntryAlloca(varrayType, nullptr, "varray"); for (size_t i = 0; i < varrayChirType->GetSize(); ++i) { auto indexName = "varray.idx" + std::to_string(i) + "E"; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto elementPtr = irBuilder.CreateGEP(varrayType, varrayPtr, {irBuilder.getInt64(0), irBuilder.getInt64(i)}, indexName); #endif auto cGValue = (irBuilder.GetCGModule() | varray.GetOperand(i)); auto& cgCtx = irBuilder.GetCGContext(); auto& cgMod = irBuilder.GetCGModule(); auto elementPtrCGType = CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(cgCtx.GetCHIRBuilder(), *varrayChirType->GetElementType())); (void)irBuilder.CreateStore(*cGValue, CGValue(elementPtr, elementPtrCGType)); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND return varrayPtr; #endif } llvm::Value* CodeGen::GenerateVArrayBuilder(IRBuilder2& irBuilder, const CHIR::VArrayBuilder& varrayBuilder) { auto& cgMod = irBuilder.GetCGModule(); auto varrayType = StaticCast(varrayBuilder.GetResult()->GetType()); auto varrayLen = (cgMod | varrayBuilder.GetSize())->GetRawValue(); auto item = DynamicCast(varrayBuilder.GetItem()); CJC_NULLPTR_CHECK(item); bool isInitedByItem = item && !item->GetExpr()->IsConstantNull(); if (!isInitedByItem) { // VArrayBuilder(size, nullptr, initLambda: Class-$Auto_Env_Base_XXXX) auto autoEnvOfInitFunc = varrayBuilder.GetInitFunc(); auto autoEnvType = DeRef(*autoEnvOfInitFunc->GetType()); CJC_ASSERT(autoEnvType->IsAutoEnvBase()); auto cgValue = (cgMod | autoEnvOfInitFunc); return irBuilder.VArrayInitedByLambda(varrayLen, *cgValue, *varrayType); } else { // VArrayBuilder(size, value, initLambda: nullptr) auto cgValue = (cgMod | item); return irBuilder.VArrayInitedByItem(varrayLen, *cgValue, *varrayType); } } cangjie_compiler-1.0.7/src/CodeGen/Base/VArrayExprImpl.h000066400000000000000000000013541510705540100230150ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_VARRAYEXPRIMPL_H #define CANGJIE_VARRAYEXPRIMPL_H #include "llvm/IR/Value.h" namespace Cangjie { namespace CHIR { class VArray; class VArrayBuilder; } // namespace CHIR namespace CodeGen { class IRBuilder2; llvm::Value* GenerateVArray(IRBuilder2& irBuilder, const CHIR::VArray& varray); llvm::Value* GenerateVArrayBuilder(IRBuilder2& irBuilder, const CHIR::VArrayBuilder& varrayBuilder); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_VARRAYEXPRIMPL_H cangjie_compiler-1.0.7/src/CodeGen/CGContext.cpp000066400000000000000000000070221510705540100214450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "CGContext.h" #include "CGContextImpl.h" #include "CGModule.h" #include "cangjie/Option/Option.h" namespace Cangjie { namespace CodeGen { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND CGContext::CGContext(const SubCHIRPackage& subCHIRPackage, CGPkgContext& cgPkgContext) : cgPkgContext(cgPkgContext), subCHIRPackage(subCHIRPackage), llvmContext(nullptr) { llvmContext = new llvm::LLVMContext(); // This `llvmContext` will be released in the de-constructor of `CGModule`. llvmContext->setOpaquePointers(cgPkgContext.GetGlobalOptions().enableOpaque); impl = std::make_unique(); } #endif CGContext::~CGContext() = default; llvm::StructType* CGContext::GetCjStringType() const { auto p1i8Type = llvm::Type::getInt8PtrTy(*llvmContext, 1u); auto int32Type = llvm::Type::getInt32Ty(*llvmContext); const std::string stringTypeStr = "record.std.core:String"; if (auto stringType = llvm::StructType::getTypeByName(*llvmContext, stringTypeStr)) { return stringType; } else { return llvm::StructType::create(*llvmContext, {p1i8Type, int32Type, int32Type}, stringTypeStr); } } void CGContext::Add2CGTypePool(CGType* cgType) { impl->cgTypePool.emplace_back(cgType); } void CGContext::Clear() { impl->Clear(); cjStrings.clear(); generatedStructType.clear(); globalsOfCompileUnit.clear(); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvmUsedGVs.clear(); subCHIRPackage.Clear(); callBasesToInline.clear(); callBasesToReplace.clear(); debugLocOfRetExpr.clear(); #endif } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::Value* CGContext::GetBasePtrOf(llvm::Value* val) const { if (auto it = impl->valueAndBasePtrMap.find(val); it != impl->valueAndBasePtrMap.end()) { return it->second; } return nullptr; } void CGContext::SetBasePtr(const llvm::Value* val, llvm::Value* basePtr) { impl->valueAndBasePtrMap[val] = basePtr; } #endif void CGContext::PushUnwindBlockStack(llvm::BasicBlock* unwindBlock) { unwindBlockStack.push(unwindBlock); } std::optional CGContext::TopUnwindBlockStack() const { if (unwindBlockStack.empty() || unwindBlockStack.top() == nullptr) { return std::nullopt; } else { return unwindBlockStack.top(); } } void CGContext::PopUnwindBlockStack() { if (!unwindBlockStack.empty()) { unwindBlockStack.pop(); } } void CGContext::AddGeneratedStructType(const std::string& structTypeName) { CJC_ASSERT(!structTypeName.empty()); generatedStructType.emplace(structTypeName); } const std::set& CGContext::GetGeneratedStructType() const { return generatedStructType; } bool CGContext::IsGeneratedStructType(const std::string& structTypeName) { return generatedStructType.find(structTypeName) != generatedStructType.end(); } void CGContext::AddGlobalsOfCompileUnit(const std::string& globalsName) { globalsOfCompileUnit.emplace(globalsName); } bool CGContext::IsGlobalsOfCompileUnit(const std::string& globalsName) { return globalsOfCompileUnit.find(globalsName) != globalsOfCompileUnit.end(); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void CGContext::AddNullableReference(llvm::Value* value) { (void)impl->nullableReference.emplace(value); } #endif } // namespace CodeGen } // namespace Cangjie cangjie_compiler-1.0.7/src/CodeGen/CGContext.h000066400000000000000000000321731510705540100211170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGCONTEXT_H #define CANGJIE_CGCONTEXT_H #include #include #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Value.h" #include "Base/CGTypes/CGType.h" #include "Base/CHIRExprWrapper.h" #include "CGPkgContext.h" #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "CJNative/CHIRSplitter.h" #endif #include "Utils/CGUtils.h" #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/CHIRCasting.h" namespace llvm::Intrinsic { typedef unsigned ID; } namespace Cangjie { class GlobalOptions; namespace CHIR { class CHIRBuilder; class ImportedValue; } // namespace CHIR namespace CodeGen { class CGContextImpl; class CGFunction; class CGValue; class CGEnumLayout; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND class CGCFFI; #endif struct CallBaseToReplaceInfo { llvm::CallBase* callWithoutTI; CHIRApplyWrapper applyExprW; CallBaseToReplaceInfo(llvm::CallBase* callWithoutTI, const CHIRApplyWrapper& applyExprW) : callWithoutTI(callWithoutTI), applyExprW(applyExprW) { } }; class CGContext { friend class CGModule; friend class CGType; friend class CGFunctionType; public: #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND explicit CGContext(const SubCHIRPackage& subCHIRPackage, CGPkgContext& cgPkgContext); #endif ~CGContext(); void Clear(); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND const SubCHIRPackage& GetSubCHIRPackage() const { return subCHIRPackage; } #endif llvm::StructType* GetCjStringType() const; CGPkgContext& GetCGPkgContext() const { return cgPkgContext; } CHIR::CHIRBuilder& GetCHIRBuilder() const { return cgPkgContext.GetCHIRBuilder(); } const CHIR::Package& GetCHIRPackage() const { return cgPkgContext.GetCHIRPackage(); } std::string GetCurrentPkgName() const { return cgPkgContext.GetCurrentPkgName(); } const GlobalOptions& GetCompileOptions() const { return cgPkgContext.GetGlobalOptions(); } llvm::LLVMContext& GetLLVMContext() { return *llvmContext; } CHIR::FuncBase* GetImplicitUsedFunc(const std::string& funcMangledName) { return cgPkgContext.GetImplicitUsedFunc(funcMangledName); } const CachedMangleMap& GetCachedMangleMap() const { return cgPkgContext.GetCachedMangleMap(); } bool IsCGParallelEnabled() const { return cgPkgContext.IsCGParallelEnabled(); } bool IsLineInfoEnabled() const { return cgPkgContext.IsLineInfoEnabled(); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool IsCustomTypeOfOtherLLVMModule(const CHIR::Type& chirType) { if (auto customType = DynamicCast(&chirType)) { if (customType->GetCustomTypeDef()->TestAttr(CHIR::Attribute::IMPORTED)) { return true; } auto customDef = customType->GetCustomTypeDef(); return subCHIRPackage.chirCustomDefs.find(customDef) == subCHIRPackage.chirCustomDefs.end(); } return false; } bool IsValueOfOtherLLVMModule(const CHIR::Value& chirValue) { if (chirValue.TestAttr(CHIR::Attribute::IMPORTED)) { return true; } if (chirValue.IsFuncWithBody()) { auto chirFunc = const_cast(DynamicCast(&chirValue)); return subCHIRPackage.chirFuncs.find(chirFunc) == subCHIRPackage.chirFuncs.end(); } else if (chirValue.IsGlobalVarInCurPackage()) { auto chirGV = const_cast(DynamicCast(&chirValue)); return subCHIRPackage.chirGVs.find(chirGV) == subCHIRPackage.chirGVs.end(); } return false; } void AddCallBaseToInline(llvm::CallBase* callBase, llvm::ReturnInst* retInst) { callBasesToInline.emplace_back(callBase, retInst); } const std::vector>& GetCallBasesToInline() { return callBasesToInline; } void AddCallBaseToReplace(llvm::CallBase* callWithoutTI, const CHIRApplyWrapper& applyExprW) { auto tmp = CallBaseToReplaceInfo(callWithoutTI, applyExprW); callBasesToReplace.emplace_back(tmp); } const std::vector& GetCallBasesToReplace() { return callBasesToReplace; } void AddDebugLocOfRetExpr(llvm::Function* func, CHIR::DebugLocation debugLoc) { debugLocOfRetExpr[func].emplace_back(debugLoc); } const std::vector& GetDebugLocOfRetExpr(llvm::Function* func) { return debugLocOfRetExpr[func]; } void AddLocalizedSymbol(const std::string& symName) { cgPkgContext.AddLocalizedSymbol(symName); } #endif void AddCJString(const std::string& cjStringName, const std::string& cjStringContent) { cjStrings.emplace(std::make_pair(cjStringName, cjStringContent)); } const std::unordered_map& GetCJStrings() const { return cjStrings; } void AddCodeGenAddedFuncsOrVars(const CHIR::Type& ty, std::string& codegenAddedName) { if (!cgPkgContext.GetGlobalOptions().enIncrementalCompilation) { return; } if (ty.IsClass() || ty.IsStruct() || ty.IsEnum()) { auto declMangledName = StaticCast(ty).GetCustomTypeDef()->GetIdentifierWithoutPrefix(); codegenAddedFuncsOrVars[declMangledName].insert(codegenAddedName); } } std::unordered_map> GetCodeGenAddedFuncsOrVars() { return codegenAddedFuncsOrVars; } void PushUnwindBlockStack(llvm::BasicBlock* unwindBlock); std::optional TopUnwindBlockStack() const; void PopUnwindBlockStack(); void AddGeneratedStructType(const std::string& structTypeName); const std::set& GetGeneratedStructType() const; bool IsGeneratedStructType(const std::string& structTypeName); void AddGlobalsOfCompileUnit(const std::string& globalsName); bool IsGlobalsOfCompileUnit(const std::string& globalsName); void RegisterStaticGIName(llvm::StringRef staticGIName) { (void)staticGINames.emplace(staticGIName.str()); } const std::set& GetStaticGINames() const { return staticGINames; } void RegisterReflectGeneratedStaticGIName(std::string staticGIName) { (void)reflectGeneratedStaticGINames.emplace_back(staticGIName); } const std::vector& GetReflectGeneratedStaticGINames() const { return reflectGeneratedStaticGINames; } static std::vector GetTINameArrayForUpperBounds(std::vector& upperBounds) { std::vector res; for (auto upperBound : upperBounds) { res.emplace_back(CGType::GetNameOfTypeInfoGV(*DeRef(*upperBound))); } return res; } std::string GetGenericTypeUniqueName(std::string& genericTypeName, std::vector& upperBounds) { if (genericTypeWithUpperBoundsMap.find(genericTypeName) != genericTypeWithUpperBoundsMap.end()) { auto upperBoundsVec = genericTypeWithUpperBoundsMap[genericTypeName]; for (size_t idx = 0; idx < upperBoundsVec.size(); idx++) { if (upperBoundsVec[idx] == GetTINameArrayForUpperBounds(upperBounds)) { return genericTypeName + "." + std::to_string(idx); } } genericTypeWithUpperBoundsMap[genericTypeName].emplace_back(GetTINameArrayForUpperBounds(upperBounds)); return genericTypeName + "." + std::to_string(upperBoundsVec.size()); } else { genericTypeWithUpperBoundsMap[genericTypeName].emplace_back(GetTINameArrayForUpperBounds(upperBounds)); return genericTypeName + ".0"; } CJC_ASSERT(false && "should not reach here"); return ""; } void AddDependentPartialOrderOfTypes(llvm::Constant* smaller, llvm::Constant* bigger) { if (dependentPartialOrderOfTypes.emplace(CGContext::PartialOrderPair{smaller, bigger}).second) { ++indegreeOfTypes[bigger]; } } struct PartialOrderPair { llvm::Constant* smaller; llvm::Constant* bigger; bool operator<(const PartialOrderPair& that) const { if (this->smaller < that.smaller) { return true; } else if (this->smaller == that.smaller) { return this->bigger < that.bigger; } else { return false; } } }; const std::set& GetDependentPartialOrderOfTypes() const { return dependentPartialOrderOfTypes; } const std::unordered_map& GetIndegreeOfTypes() const { return indegreeOfTypes; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void AddLLVMUsedVars(const std::string& mangledNameOfGV) { llvmUsedGVs.emplace(mangledNameOfGV); } const std::set& GetLLVMUsedVars() const { return llvmUsedGVs; } void AddNullableReference(llvm::Value* value); void SetBasePtr(const llvm::Value* val, llvm::Value* basePtr); llvm::Value* GetBasePtrOf(llvm::Value* val) const; void SetBoxedValueMap(llvm::Value* boxedRefVal, llvm::Value* originalNonRefVal) { (void)nonRefBox2RefMap.emplace(boxedRefVal, originalNonRefVal); } llvm::Value* GetOriginalNonRefValOfBoxedValue(llvm::Value* boxedRefVal) const { auto itor = nonRefBox2RefMap.find(boxedRefVal); if (itor != nonRefBox2RefMap.end()) { return itor->second; } else { return nullptr; } } #endif void Add2CGTypePool(CGType* cgType); uint16_t GetVTableSizeOf(const CHIR::ClassType* introType) { auto classDef = introType->GetClassDef(); if (classDef->IsInterface()) { return 0U; } if (auto it = vtableSizeMap.find(classDef); it != vtableSizeMap.end()) { return it->second; } uint16_t cnt = 0U; for (auto& it : classDef->GetVTable()) { if (it.first->GetClassDef()->IsClass() && !it.second.empty()) { ++cnt; } } vtableSizeMap.emplace(classDef, cnt); return cnt; } CGPkgContext& cgPkgContext; llvm::Value* debugValue = nullptr; std::unordered_map function2CGFunc; std::unordered_map> genericParamsCacheMap; std::unordered_map> genericParamsSizeBlockLevelCacheMap; std::unordered_map vtableSizeMap; private: #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND SubCHIRPackage subCHIRPackage; #endif llvm::LLVMContext* llvmContext; std::unique_ptr impl; std::stack unwindBlockStack; // llvm::StructType used by subModule to generate for_keeping_some_types. std::set generatedStructType; std::set globalsOfCompileUnit; std::set usedLLVMStructTypes; std::set dependentPartialOrderOfTypes; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // When HotReload is enabled, we should put those user-defined GVs or non-param constructors with `internal` // linkage into llvm.used to prevent them from being eliminated by llvm-opt. std::set llvmUsedGVs; std::set staticGINames; std::vector reflectGeneratedStaticGINames; std::vector> callBasesToInline; std::vector callBasesToReplace; #endif // Key: the generic type's srcCodeIdentifier // Value: {upperbounds vector1, upperbounds vector2, ...} std::unordered_map>> genericTypeWithUpperBoundsMap; // Key: The global variable's name of the cjString // Value: Store the content of cjString std::unordered_map cjStrings; std::unordered_map indegreeOfTypes; std::unordered_map> codegenAddedFuncsOrVars; std::unordered_map> enumInfoCache; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::map> debugLocOfRetExpr; // Key: boxed ref value; Value: original non-ref value std::unordered_map nonRefBox2RefMap; #endif }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGCONTEXT_H cangjie_compiler-1.0.7/src/CodeGen/CGContextImpl.cpp000066400000000000000000000015501510705540100222670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "CGContextImpl.h" #include "Base/CGTypes/CGFunctionType.h" #include "Base/CGTypes/CGRefType.h" #include "Base/CGTypes/CGTupleType.h" #include "cangjie/CHIR/Type/StructDef.h" namespace Cangjie::CodeGen { void CGContextImpl::Clear() { for (auto cgType : cgTypePool) { CJC_ASSERT(cgType != nullptr); delete cgType; cgType = nullptr; } cgTypePool.clear(); chirType2CGTypeMap.clear(); chirTypeName2CGTypeMap.clear(); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND valueAndBasePtrMap.clear(); nullableReference.clear(); #endif } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/CGContextImpl.h000066400000000000000000000024561510705540100217420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGCONTEXT_IMPL_H #define CANGJIE_CGCONTEXT_IMPL_H #include #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Type.h" #include "Base/CGTypes/CGType.h" #include "Utils/CGCommonDef.h" #include "cangjie/CHIR/Type/Type.h" namespace Cangjie { namespace CodeGen { class CGContextImpl { friend class CGContext; friend class CGType; friend class CGTupleType; public: CGContextImpl() = default; ~CGContextImpl() = default; void Clear(); private: std::vector cgTypePool; std::unordered_map, CGType::TypeExtraInfoHasher> chirType2CGTypeMap; std::unordered_map chirTypeName2CGTypeMap; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::unordered_map valueAndBasePtrMap; std::unordered_set nullableReference; // Record those i8(1)* which are nullable. #endif }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGCONTEXT_IMPL_H cangjie_compiler-1.0.7/src/CodeGen/CGFunction.cpp000066400000000000000000000336471510705540100216220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "llvm/Analysis/LoopInfo.h" #include "llvm/IR/Dominators.h" #include "Base/CGTypes/CGFunctionType.h" #include "CGModule.h" #include "IRBuilder.h" #include "Utils/BlockScopeImpl.h" #include "Utils/CGCommonDef.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/AttributeInfo.h" namespace Cangjie::CodeGen { void EmitBasicBlockIR(CGModule& cgMod, const CHIR::Block& chirBB); void BuildCJFunc(CGModule& cgMod, const CHIR::Func& chirFunc, const CGFunction& cgFunc); namespace { llvm::Function* CreateFunctionWrapperForNoBasePtrCases(const CHIR::Value* chirFunc, CGModule& cgMod) { auto func = VirtualCast(chirFunc); CJC_ASSERT(func); auto cgFunc = cgMod.GetOrInsertCGFunction(chirFunc, true); BuildCJFunc(cgMod, *func, *cgFunc); CodeGenUnwindBlockScope unwindBlockScope(cgMod, nullptr); EmitBasicBlockIR(cgMod, *func->GetBody()->GetEntryBlock()); for (auto bb : func->GetBody()->GetBlocks()) { cgMod.SetOrUpdateMappedBB(bb, nullptr); } return cgFunc->GetRawFunction(); } void CreateFunctionWrapperForNormalCases( llvm::Function* function, llvm::Function* wrapperF, const CGFunctionType& cgType, CGModule& cgMod) { wrapperF->setPersonalityFn(cgMod.GetExceptionIntrinsicPersonality()); SetGCCangjie(wrapperF); std::vector args; for (size_t i = 0; i < wrapperF->arg_size(); ++i) { args.emplace_back(wrapperF->getArg(static_cast(i))); } auto bb = llvm::BasicBlock::Create(cgMod.GetLLVMContext(), "entry", wrapperF); IRBuilder2 builder(cgMod, bb); CodeGenUnwindBlockScope unwindBlockScope(cgMod, nullptr); if (cgMod.GetCGContext().GetCompileOptions().enableCompileDebug) { auto allocasBB = llvm::BasicBlock::Create(cgMod.GetLLVMContext(), "allocas", wrapperF, bb); builder.SetInsertPoint(allocasBB); builder.CreateBr(bb); builder.SetInsertPoint(bb); } auto p1i8 = builder.getInt8PtrTy(1); auto thisValOffset = cgType.HasSRet() ? 1U : 0U; auto thisVal = wrapperF->getArg(thisValOffset); thisVal->setName("this.withTI"); auto p1This = CGType::GetOrCreate(cgMod, DeRef(cgType.GetParamType(0)->GetOriginal()))->GetLLVMType()->getPointerTo(1U); auto dataPtr = builder.CreateBitCast(builder.GetPayloadFromObject(thisVal), p1This); if (!cgType.HasBasePtr()) { auto ti = builder.GetTypeInfoFromObject(thisVal); auto size = builder.GetSizeFromTypeInfo(ti); auto paramDerefType = CGType::GetOrCreate(cgMod, DeRef(cgType.GetParamType(0)->GetOriginal()))->GetLLVMType(); CJC_ASSERT(paramDerefType->isIntegerTy(8U) && "Should not reach here."); auto tmp = builder.LLVMIRBuilder2::CreateAlloca(paramDerefType, size); builder.CallGCReadAgg({tmp, thisVal, dataPtr, builder.CreateSExt(size, builder.getInt64Ty())}); dataPtr = tmp; } args[thisValOffset] = dataPtr; if (cgType.HasBasePtr()) { // add basePtr args.insert(args.begin() + thisValOffset + 1U, builder.CreateBitCast(thisVal, p1i8)); } auto res = builder.CreateCallOrInvoke(function, args); llvm::ReturnInst* retInst = function->getReturnType()->isVoidTy() ? builder.CreateRetVoid() : builder.CreateRet(res); cgMod.GetCGContext().AddCallBaseToInline(res, retInst); } llvm::Function* CreateFunctionWrapper( llvm::Function* function, const CGFunctionType* cgType, const CHIR::Value* chirFunc, CGModule& cgMod) { if (function->hasFnAttribute("wrapper")) { return function; } auto& cgCtx = cgMod.GetCGContext(); if (chirFunc->TestAttr(CHIR::Attribute::STATIC) || chirFunc->Get()) { return nullptr; } auto outerType = VirtualCast(chirFunc)->GetParentCustomTypeOrExtendedType(); if (!outerType || !outerType->IsStruct() || CGType::GetOrCreate(cgMod, outerType)->GetSize()) { return nullptr; } auto wrapperFName = function->getName().str(); function->setName(wrapperFName + POSTFIX_WITHOUT_TI); function->addFnAttr(HAS_WITH_TI_WRAPPER_ATTR); if (!cgCtx.GetCompileOptions().enableCompileDebug && !cgType->HasBasePtr() && !cgCtx.IsValueOfOtherLLVMModule(*chirFunc)) { return CreateFunctionWrapperForNoBasePtrCases(chirFunc, cgMod); } auto wrapperFType = function->getFunctionType(); auto p1This = CGType::GetOrCreate(cgMod, DeRef(cgType->GetParamType(0)->GetOriginal()))->GetLLVMType()->getPointerTo(1U); if (cgType->HasBasePtr()) { std::vector params = function->getFunctionType()->params(); params.erase(params.begin() + (cgType->HasSRet() ? 2U : 1U)); wrapperFType = llvm::FunctionType::get(function->getReturnType(), params, function->isVarArg()); } else { std::vector params = function->getFunctionType()->params(); params[cgType->HasSRet() ? 1U : 0U] = p1This; wrapperFType = llvm::FunctionType::get(function->getReturnType(), params, function->isVarArg()); } auto wrapperF = llvm::cast(cgMod.GetLLVMModule()->getOrInsertFunction(wrapperFName, wrapperFType).getCallee()); if (cgType->HasSRet()) { auto sRetAttr = function->getParamAttribute(0, llvm::Attribute::StructRet); wrapperF->addParamAttr(0, sRetAttr); wrapperF->addParamAttr(0, llvm::Attribute::NoAlias); } AddLinkageTypeMetadata(*wrapperF, GetLinkageTypeOfGlobalObject(*function), cgCtx.IsCGParallelEnabled()); if (!cgCtx.IsValueOfOtherLLVMModule(*chirFunc)) { CreateFunctionWrapperForNormalCases(function, wrapperF, *cgType, cgMod); } return wrapperF; } } // namespace CGFunction::CGFunction( llvm::Function* function, const CGFunctionType* cgType, const CHIR::Value* chirFunc, CGModule& cgMod) : CGValue(function, cgType), cgMod(cgMod), isStructRet(cgType->HasSRet()), chirFunc(*chirFunc) { auto& cgCtx = cgMod.GetCGContext(); cgCtx.function2CGFunc.emplace(function, this); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (isStructRet) { auto retCGType = cgType->GetContainedTypeAt(0)->GetPointerElementType(); function->getArg(0)->addAttr(llvm::Attribute::NoAlias); auto sret = llvm::Attribute::getWithStructRetType(function->getContext(), (!retCGType->GetSize() && !retCGType->GetOriginal().IsGeneric()) ? llvm::Type::getInt8Ty(cgMod.GetLLVMContext()) : retCGType->GetLLVMType()); function->getArg(0)->addAttr(sret); } if (!cgType->closureParamIndices.empty()) { function->setMetadata( "ClosureParamIndices", llvm::MDTuple::get(function->getContext(), cgType->closureParamIndices)); } CJC_ASSERT(chirFunc->IsFunc()); bool isFastNative = VirtualCast(chirFunc)->IsFastNative(); if (isFastNative) { function->addAttributeAtIndex(static_cast(llvm::AttributeList::FunctionIndex), llvm::Attribute::get(function->getContext(), FAST_NATIVE_ATTR)); } else if (chirFunc->TestAttr(CHIR::Attribute::FOREIGN)) { function->addAttributeAtIndex(static_cast(llvm::AttributeList::FunctionIndex), llvm::Attribute::get(function->getContext(), CJ2C_ATTR)); } if (cgType->IsCFunc()) { return; } for (auto [structArgIdx, idxInContainedTypes] : std::as_const(cgType->structParamNeedsBasePtr)) { auto basePtrArg = function->getArg(static_cast(structArgIdx + 1)); auto curArg = function->getArg(static_cast(structArgIdx)); basePtrArg->setName(curArg->getName() + BASEPTR_SUFFIX); cgCtx.SetBasePtr(curArg, basePtrArg); } wrapperF = CreateFunctionWrapper(function, cgType, chirFunc, cgMod); #endif } llvm::Argument* CGFunction::GetArgByIndexFromCHIR(size_t idx) const { CJC_ASSERT(dynamic_cast(GetCGType())); return GetRawFunction()->getArg( static_cast(dynamic_cast(GetCGType())->realParamIndices[idx])); } void CGFunction::Opt() const { auto function = GetRawFunction(); RemoveUnreachableBlocks(*function); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND EraseReplaceableAlloca(cgMod, *function); AddZeroInitForStructWithRefField(cgMod, *function); #endif } void CGFunction::DumpIR() const { GetRawFunction()->print(llvm::outs(), nullptr); } void CGFunction::RemoveUnreachableBlocks(llvm::Function& function) { if (function.isDeclaration()) { return; } llvm::removeUnreachableBlocks(function); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND namespace { bool CompareType(llvm::Type* t1, llvm::Type* t2) { if (!t1 || !t2) { return t1 > t2; } if (t1->getTypeID() != t2->getTypeID()) { return t1->getTypeID() > t2->getTypeID(); } else if (t1->isStructTy()) { return t1->getStructName() > t2->getStructName(); } else if (t1->isPointerTy()) { return CompareType(GetPointerElementType(t1), GetPointerElementType(t2)); } else { return t1 > t2; } } struct Compare { bool operator()(llvm::Type* t1, llvm::Type* t2) const { return CompareType(t1, t2); } }; void EraseReplaceableInsts(llvm::Function& function, const std::vector& insts) { if (insts.size() <= 1) { return; } llvm::Instruction* specificInst = insts.front(); auto& entryBB = function.getEntryBlock(); specificInst->moveBefore(&entryBB.front()); // Separate replace & erase steps to avoid iterator smashing. std::for_each( insts.begin() + 1, insts.end(), [&specificInst](auto inst) { inst->replaceAllUsesWith(specificInst); }); std::for_each(insts.begin() + 1, insts.end(), [](auto inst) { inst->eraseFromParent(); }); if (HasNoUse(*specificInst)) { specificInst->eraseFromParent(); } } llvm::BasicBlock* GetNearestCommonAncestorOfBasicBlocks(const std::set& bbs) { if (bbs.empty()) { return nullptr; } auto pre = *bbs.begin(); auto function = pre->getParent(); llvm::DominatorTree dt(*function); for (auto curIter = ++bbs.begin(); curIter != bbs.end(); ++curIter) { auto cur = *curIter; pre = dt.findNearestCommonDominator(pre, cur); } return pre; } } // namespace void CGFunction::EraseReplaceableAlloca(const CGModule& cgModule, llvm::Function& function) { if (cgModule.GetCGContext().GetCompileOptions().enableCompileDebug || function.isDeclaration()) { return; } // Erase redundant 'Unit.Type' allocations from function. // Erase unused 'Unit.Type' allocations from function. // Erase redundant unnamed allocations from function, clustered by type. std::map, Compare> unnamedAllocas; auto replaceableAlloca = [](const llvm::Instruction& inst) { if (!llvm::isa(inst)) { return false; } auto type = inst.getType(); bool isUnit = IsStructPtrType(type) && GetCodeGenTypeName(GetPointerElementType(type)) == UNIT_TYPE_STR; return isUnit; }; for (auto& inst : function.getEntryBlock()) { if (replaceableAlloca(inst)) { unnamedAllocas[inst.getType()].push_back(&inst); } } std::for_each(unnamedAllocas.begin(), unnamedAllocas.end(), [&function](auto it) { EraseReplaceableInsts(function, it.second); }); } void CGFunction::AddZeroInitForStructWithRefField(CGModule& cgModule, llvm::Function& function) { if (function.isDeclaration()) { return; } // All allocations are at the beginning of the entry block of a function. auto entryBB = &function.getEntryBlock(); // Collect all the allocations for structs that contain ref element(s) directly or indirectly. std::vector structAllocas; std::for_each(entryBB->begin(), entryBB->end(), [&](llvm::Instruction& inst) { if (auto alloca = llvm::dyn_cast(&inst); alloca && alloca->getAllocatedType()->isStructTy() && IsTypeContainsRef(alloca->getAllocatedType())) { structAllocas.emplace_back(alloca); } }); llvm::DominatorTree domTree(function); auto loopInfo = llvm::LoopInfoBase(); loopInfo.analyze(domTree); IRBuilder2 builder(cgModule); // Add the memset-with-zero logic for each collected allocation before it is used for the first time. for (auto alloca : structAllocas) { std::set bbs; for (auto user : alloca->users()) { if (auto tmp = llvm::dyn_cast(user); tmp) { (void)bbs.emplace(tmp->getParent()); } } llvm::BasicBlock* nca = bbs.empty() ? alloca->getParent() : GetNearestCommonAncestorOfBasicBlocks(bbs); CJC_ASSERT(nca != nullptr); llvm::Instruction* insertPt = nca->getTerminator(); for (auto user : alloca->users()) { if (auto tmp = llvm::dyn_cast(user); tmp && tmp->getParent() == nca && tmp->comesBefore(insertPt)) { insertPt = tmp; } } CJC_NULLPTR_CHECK(insertPt); if (auto loop = loopInfo.getLoopFor(insertPt->getParent()); loop) { auto outermostLoop = loop->getOutermostLoop(); for (auto pred : llvm::predecessors(outermostLoop->getHeader())) { if (outermostLoop->contains(pred)) { continue; } builder.SetInsertPoint(pred->getTerminator()); (void)builder.CreateCJMemSetStructWith0(alloca); } } else { builder.SetInsertPoint(insertPt); (void)builder.CreateCJMemSetStructWith0(alloca); } } } #endif } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/CGModule.cpp000066400000000000000000000736101510705540100212540ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "CGModule.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" #include "llvm/IR/Verifier.h" #include "llvm/Support/Host.h" #include "Base/CGTypes/CGFunctionType.h" #include "Base/CGTypes/CGType.h" #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "CJNative/CGTypes/CGExtensionDef.h" #include "CJNative/CJNativeCGCFFI.h" #endif #include "DIBuilder.h" #include "IRAttribute.h" #include "IRBuilder.h" #include "IncrementalGen/IncrementalGen.h" #include "Utils/CGCommonDef.h" #include "cangjie/CHIR/AttributeInfo.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Value.h" #include "cangjie/Utils/ProfileRecorder.h" namespace Cangjie::CodeGen { inline std::string CGModule::GetDataLayoutString(const Triple::Info& target) { if (target.IsMacOS() && target.arch == Triple::ArchType::AARCH64) { return "e-m:o-i64:64-i128:128-n32:64-S128"; } if (target.arch == Triple::ArchType::ARM32) { return "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"; } return target.IsMinGW() ? "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" : "e-m:e-i64:64-f80:128-n8:16:32:64-S128"; } inline std::string CGModule::GetTargetTripleString(const Triple::Info& target) { if (target.IsMacOS()) { if (target.arch == Triple::ArchType::AARCH64) { return "arm64-apple-macosx12.0.0"; } return target.ArchToString() + "-apple-macosx12.0.0"; } else if (target.arch == Triple::ArchType::ARM32 && target.os == Triple::OSType::LINUX) { return "armv7a-linux-gnu"; } else { return llvm::sys::getDefaultTargetTriple(); } } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND CGModule::CGModule(SubCHIRPackage& subCHIRPackage, CGPkgContext& cgPkgCtx) : cgCtx(std::make_unique(subCHIRPackage, cgPkgCtx)), module(std::make_unique( std::to_string(subCHIRPackage.subCHIRPackageIdx) + '-' + cgPkgCtx.GetCurrentPkgName(), cgCtx->GetLLVMContext())), commonBitMap(std::make_unique(*module)) #endif { // This flag is used to specify that the generated bc is cangjie bc in LTO mode. module->addModuleFlag(llvm::Module::Warning, "CJBC", static_cast(1)); // This flag is used to specify that the generated bc has not been opt-optimized. module->addModuleFlag(llvm::Module::Warning, "Cangjie_OPT", static_cast(0)); // LLVM BE requires package name to distinguish difference instantiations of generic functions between packages. if (cgCtx->GetCompileOptions().target.IsMacOS()) { auto identifier = cgCtx->GetCurrentPkgName(); std::replace(identifier.begin(), identifier.end(), '/', '.'); module->addModuleFlag( llvm::Module::Warning, "Cangjie_PACKAGE_ID", llvm::MDString::get(module->getContext(), identifier)); } const auto& options = cgCtx->GetCompileOptions(); module->setDataLayout(CGModule::GetDataLayoutString(options.target)); module->setTargetTriple(CGModule::GetTargetTripleString(options.target)); InitDebugInfo(); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (options.target.os == Triple::OSType::WINDOWS && options.target.arch == Triple::ArchType::X86_64) { cffi = std::make_unique(*this); } else if (Utils::In(options.target.os, {Triple::OSType::LINUX, Triple::OSType::DARWIN}) && options.target.arch == Triple::ArchType::X86_64) { cffi = std::make_unique(*this); } else if (options.target.arch == Triple::ArchType::AARCH64 && Utils::In(options.target.os, {Triple::OSType::LINUX})) { cffi = std::make_unique(*this); #ifdef __APPLE__ } else if (options.target.arch == Triple::ArchType::AARCH64 && options.target.os == Triple::OSType::DARWIN) { cffi = std::make_unique(*this); } else if (options.target.arch == Triple::ArchType::AARCH64 && options.target.os == Triple::OSType::IOS) { cffi = std::make_unique(*this); #endif } else if (options.target.arch == Triple::ArchType::ARM32) { cffi = std::make_unique(*this); } else { // Rollback to linux x86_64 abi. cffi = std::make_unique(*this); } #endif } CGModule::~CGModule() { diBuilder = nullptr; incrementalGen = nullptr; if (module) { auto context = &module->getContext(); // Then, release `module`: module = nullptr; // Finally, release llvm context delete context; context = nullptr; } } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void CGModule::GenTypeInfo() { while (!delayGenTypeInfo.empty()) { auto cgType = delayGenTypeInfo.top(); delayGenTypeInfo.pop(); cgType->GenTypeInfo(); } } void CGModule::GenTypeTemplate() { while (!delayGenTypeTemplate.empty()) { auto cgType = delayGenTypeTemplate.top(); delayGenTypeTemplate.pop(); cgType->GenTypeTemplate(); } } bool CGModule::InitIncrementalGen() { CJC_ASSERT(cgCtx->GetCGPkgContext().IsIncrementEnabled()); incrementalGen = std::make_unique(cgCtx->IsCGParallelEnabled()); auto cachePath = cgCtx->GetCompileOptions().GenerateCachedPathNameForCodeGen(module->getSourceFileName(), "_cache.bc"); return incrementalGen->Init(cachePath, cgCtx->GetLLVMContext()); } void CGModule::GenIncremental() { auto& option = cgCtx->GetCompileOptions(); GenCodeGenAddedMetadata(); if (cgCtx->GetCGPkgContext().IsIncrementEnabled()) { CJC_NULLPTR_CHECK(incrementalGen); auto& llvmStdout = llvm::outs(); if (llvm::verifyModule(*module, &llvmStdout)) { InternalError("Incremental part failed to be compiled"); } DumpIR(*module, cgCtx->GetCurrentPkgName() + "/Incre/" + module->getSourceFileName() + ".incre.bc", option.codegenDebugMode); auto newModule = incrementalGen->LinkModules(ReleaseLLVMModule(), cgCtx->GetCachedMangleMap()); for (auto name: incrementalGen->GetIncrLLVMUsedNames()) { GetCGContext().AddLLVMUsedVars(name); } for (auto name : incrementalGen->GetIncrCachedStaticGINames()) { GetCGContext().RegisterStaticGIName(name); } ResetLLVMModule(newModule); } if (option.enIncrementalCompilation) { // Save .bc for the potential incremental compilation before deeper erasure. auto cachePath = option.GenerateCachedPathNameForCodeGen(module->getSourceFileName(), "_cache.bc"); CodeGen::SaveToBitcodeFile(*module, cachePath); if (auto namedMD = module->getNamedMetadata("CodeGenAddedForIncr"); namedMD) { namedMD->eraseFromParent(); } if (auto namedMD = module->getNamedMetadata("StaticGenericTIsForIncr"); namedMD) { namedMD->eraseFromParent(); } } } void CGModule::GenCodeGenAddedMetadata() const { auto codegenAddedNamedMD = GetLLVMModule()->getOrInsertNamedMetadata("CodeGenAddedForIncr"); auto codegenAddedFuncsOrVars = GetCGContext().GetCodeGenAddedFuncsOrVars(); for (auto& kv : codegenAddedFuncsOrVars) { std::vector ops; ops.push_back(llvm::MDString::get(GetLLVMContext(), kv.first)); for (auto& name : kv.second) { ops.push_back(llvm::MDString::get(GetLLVMContext(), name)); } auto md = llvm::MDTuple::get(GetLLVMContext(), ops); codegenAddedNamedMD->addOperand(md); } auto staticGINames = GetCGContext().GetStaticGINames(); auto staticGINamedMD = GetLLVMModule()->getOrInsertNamedMetadata("StaticGenericTIsForIncr"); for (auto name : staticGINames) { staticGINamedMD->addOperand(llvm::MDTuple::get(GetLLVMContext(), llvm::MDString::get(GetLLVMContext(), name))); } } #endif namespace { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND inline void SetCFFIStub(const CHIR::Value& func, llvm::Function* function) { CJC_NULLPTR_CHECK(function); if (func.IsFuncWithBody()) { const CHIR::Func& funcNode = VirtualCast(func); if (funcNode.IsCFunc()) { if (!funcNode.IsCFFIWrapper()) { function->addFnAttr(C2CJ_ATTR); } else if (funcNode.GetIdentifier().find(':') != std::string::npos) { function->addFnAttr("pkg_c_wrapper"); } else { function->addFnAttr(CJSTUB_ATTR); } } } } #endif void AddToGenericParamMapUnderExtendScope(const llvm::Function* function, std::unique_ptr& cgFunc, const CHIR::FuncBase& f, std::optional outerTIIdx) { CJC_NULLPTR_CHECK(function); auto extendDef = StaticCast(f.GetParentCustomTypeDef()); auto extendedType = extendDef->GetExtendedType(); for (auto gt : extendDef->GetGenericTypeParams()) { std::vector path; CGExtensionDef::FoundGenericTypeAndCollectPath(*extendedType, *gt, path); auto outerTi = function->getArg(static_cast(outerTIIdx.value())); (void)cgFunc->genericParamsMap.emplace(gt, [outerTi, path, extendedType](IRBuilder2& irBuilder) { auto entryTypeArgs = irBuilder.GetTypeArgsFromTypeInfo(outerTi); std::unordered_map map; std::queue remainPath; for (auto idx : path) { remainPath.push(idx); } return CGExtensionDef::GetTypeInfoWithPath( irBuilder, *extendedType, entryTypeArgs, std::move(remainPath), map); }); } } void AddToGenericParamMapWithOuterTI(const llvm::Function* function, std::unique_ptr& cgFunc, const CHIR::Value& func, std::optional outerTIIdx) { CJC_NULLPTR_CHECK(function); uint32_t idx = 0; auto outerTiArg = function->getArg(static_cast(outerTIIdx.value())); outerTiArg->setName("outerTI"); auto outerDef = VirtualCast(func).GetParentCustomTypeDef(); CJC_ASSERT(outerDef); if (!outerDef->GetGenericTypeParams().empty()) { auto outerType = outerDef->IsExtend() ? dynamic_cast(outerDef)->GetExtendedType() : outerDef->GetType(); (void)cgFunc->genericParamsMap.emplace( outerType, [outerTiArg]([[maybe_unused]] IRBuilder2& irBuilder) { return outerTiArg; }); } for (auto gt : outerDef->GetGenericTypeParams()) { (void)cgFunc->genericParamsMap.emplace(gt, [function, outerTIIdx, idx](IRBuilder2& irBuilder) { auto outerTI = function->getArg(static_cast(outerTIIdx.value())); auto tiPtrType = CGType::GetOrCreateTypeInfoPtrType(irBuilder.GetLLVMContext()); auto typeArgs = irBuilder.GetTypeArgsFromTypeInfo(outerTI); auto tiAddr = irBuilder.CreateConstInBoundsGEP1_32(tiPtrType, typeArgs, idx); return irBuilder.LLVMIRBuilder2::CreateLoad(tiPtrType, tiAddr); }); ++idx; } } } // namespace CGFunction* CGModule::GetOrInsertCGFunction(const CHIR::Value* func, bool forWrapper) { CJC_NULLPTR_CHECK(func); if (auto it = valueMapping.find(func); it != valueMapping.end()) { return dynamic_cast(it->second); } auto cgFuncType = StaticCast(CGType::GetOrCreateWithNode(*this, func, true, forWrapper)); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto chirFuncTy = StaticCast(func->GetType()); CJC_NULLPTR_CHECK(chirFuncTy); llvm::FunctionType* functionType = chirFuncTy->IsCFunc() ? cffi->GetCFuncType(*chirFuncTy) : cgFuncType->GetLLVMFunctionType(); #endif auto function = llvm::cast( module->getOrInsertFunction(func->GetIdentifierWithoutPrefix(), functionType).getCallee()); if (forWrapper) { function->addFnAttr("wrapper"); } if (func->TestAttr(CHIR::Attribute::NO_INLINE)) { function->addFnAttr(llvm::Attribute::NoInline); } if (cgFuncType->HasBasePtr()) { AddFnAttr(function, llvm::Attribute::get(module->getContext(), STRUCT_MUT_FUNC_ATTR)); // Deprecated AddFnAttr(function, llvm::Attribute::get(module->getContext(), THIS_PARAM_HAS_BP)); } auto chirFunc = VirtualCast(func); auto chirLinkage = chirFunc->Get(); if (func->IsFuncWithBody()) { auto chirFunc = VirtualCast(func); auto chirLinkage = chirFunc->Get(); bool markByMD = cgCtx->IsCGParallelEnabled() && !IsCHIRWrapper(func->GetIdentifierWithoutPrefix()); AddLinkageTypeMetadata(*function, CHIRLinkage2LLVMLinkage(chirLinkage), markByMD); } else if (chirLinkage == Linkage::EXTERNAL_WEAK) { // Import function in if branch of @IfAvailable need to mark as EXTERNAL_WEAK. function->setLinkage(llvm::GlobalValue::ExternalWeakLinkage); } auto cgFunc = std::make_unique(function, cgFuncType, func, *this); for (auto& [gt, idx] : cgFuncType->GetGenericParamIndicesMap()) { function->getArg(static_cast(idx))->setName("ti." + gt->GetSrcCodeIdentifier()); auto index = idx; (void)cgFunc->genericParamsMap.emplace( gt, [function, index](IRBuilder2&) { return function->getArg(static_cast(index)); }); } auto outerTIIdx = cgFuncType->GetOuterTypeInfoIndex(); // If this is a function defined within `Extend` scope, we need to // add the `T`, ..., `K` into the genericParamsMap of this function. if (auto f = DynamicCast(func); f && f->IsInExtend()) { CJC_ASSERT(outerTIIdx.has_value()); AddToGenericParamMapUnderExtendScope(function, cgFunc, *f, outerTIIdx); } if (outerTIIdx.has_value()) { AddToGenericParamMapWithOuterTI(function, cgFunc, *func, outerTIIdx); } if (auto thisTIIdx = cgFuncType->GetThisTypeInfoIndex()) { auto thisTiArg = function->getArg(static_cast(thisTIIdx.value())); thisTiArg->setName("thisTI"); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND SetCFFIStub(*func, function); if (chirFuncTy->IsCFunc()) { cffi->AddFunctionAttr(*chirFuncTy, *function); } else { SetGCCangjie(function); auto chirParamTypes = dynamic_cast(func->GetType())->GetParamTypes(); for (size_t idx = 0; idx < chirParamTypes.size(); ++idx) { if (chirParamTypes[idx]->IsStruct()) { cgFunc->GetArgByIndexFromCHIR(idx)->addAttr(llvm::Attribute::ReadOnly); cgFunc->GetArgByIndexFromCHIR(idx)->addAttr(llvm::Attribute::NoCapture); } } } if (func->IsFuncWithBody()) { if (!VirtualCast(func)->IsCFFIWrapper()) { SetGCCangjie(function); } } if (func->TestAttr(CHIR::Attribute::NO_SIDE_EFFECT)) { function->addFnAttr(llvm::Attribute::NoUnwind); function->addFnAttr(llvm::Attribute::ReadOnly); function->addFnAttr(llvm::Attribute::WillReturn); } #endif auto ret = cgFunc.get(); (void)functions.emplace(function, ret); SetOrUpdateMappedCGValue(func, std::move(cgFunc)); return ret; } CGValue* CGModule::GetOrInsertGlobalVariable(const CHIR::Value* chirGV) { CJC_ASSERT(chirGV && "chirGV is null"); if (auto it = valueMapping.find(chirGV); it != valueMapping.end()) { return it->second; } auto cgVarType = CGType::GetOrCreate(*this, chirGV->GetType()); llvm::GlobalVariable* gv = llvm::cast(module->getOrInsertGlobal( chirGV->GetIdentifierWithoutPrefix(), cgVarType->GetPointerElementType()->GetLLVMType())); if (chirGV->IsGlobalVarInCurPackage()) { auto chirLinkage = chirGV->Get(); AddLinkageTypeMetadata(*gv, CHIRLinkage2LLVMLinkage(chirLinkage), cgCtx->IsCGParallelEnabled()); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND gv->addAttribute(CJGLOBAL_VALUE_ATTR); if (cgCtx->GetCompileOptions().EnableAsan() || cgCtx->GetCompileOptions().EnableHwAsan()) { gv->addAttribute("address_sanitize_global"); } if (chirGV->TestAttr(CHIR::Attribute::NON_RECOMPILE)) { gv->addAttribute("nonRecompile"); } #endif auto cgVar = std::make_unique(gv, cgVarType); auto ret = cgVar.get(); SetOrUpdateMappedCGValue(chirGV, std::move(cgVar)); return ret; } CGValue* CGModule::GetMappedCGValue(const CHIR::Value* chirValue) { CJC_NULLPTR_CHECK(chirValue); if (auto cgValue = GetMappedValue(chirValue); cgValue) { return cgValue; } if (chirValue->IsImportedFunc()) { auto importedFunc = DynamicCast(chirValue); return GetOrInsertCGFunction(importedFunc); } else if (chirValue->IsFuncWithBody()) { auto func = DynamicCast(chirValue); return GetOrInsertCGFunction(func); } else if (chirValue->IsImportedVar()) { auto importedVar = DynamicCast(chirValue); return GetOrInsertGlobalVariable(importedVar); } else if (chirValue->IsGlobalVarInCurPackage()) { auto chirGV = DynamicCast(chirValue); return GetOrInsertGlobalVariable(chirGV); } else { CJC_ASSERT(false && "Should not reach here: value not dominate all uses in CHIR."); return nullptr; } } llvm::Value* CGModule::GenerateUnitTypeValue() { auto unitVal = module->getGlobalVariable(UNIT_VAL_STR); if (unitVal == nullptr) { llvm::Type* unitType = CGType::GetUnitCGType(*this)->GetLLVMType(); auto gv = module->getOrInsertGlobal(UNIT_VAL_STR, unitType); unitVal = llvm::cast(gv); CJC_NULLPTR_CHECK(unitVal); unitVal->addAttribute(CJGLOBAL_VALUE_ATTR); unitVal->setInitializer(llvm::UndefValue::get(unitType)); AddLinkageTypeMetadata(*unitVal, cgCtx->GetCompileOptions().target.IsMinGW() ? llvm::GlobalValue::PrivateLinkage : llvm::GlobalValue::WeakODRLinkage, false); } return unitVal; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::Constant* CGModule::GetExceptionIntrinsicPersonality() { auto funcName = "__cj_personality_v0$"; // MachO does not support "too many personalities for compact unwind to decode". __cj_personality_v0$" // It is defined by runtime on macOS and declaration is all we need here. if (cgCtx->GetCompileOptions().target.IsMacOS()) { auto retTy = llvm::Type::getInt32Ty(module->getContext()); return GetOrInsertFunction(funcName, llvm::FunctionType::get(retTy, true)); } llvm::Function* func = module->getFunction(funcName); if (!func) { auto& llvmCtx = module->getContext(); auto retTy = llvm::Type::getInt32Ty(llvmCtx); auto functionCallee = module->getOrInsertFunction(funcName, llvm::FunctionType::get(retTy, true)); func = llvm::cast(functionCallee.getCallee()); AddLinkageTypeMetadata(*func, llvm::GlobalValue::PrivateLinkage, false); auto entryBB = llvm::BasicBlock::Create(llvmCtx, "entry", func); IRBuilder2 irBuilder(*this, entryBB); (void)irBuilder.CreateRet(llvm::ConstantInt::get(retTy, 0)); } return func; } #endif void CGModule::Opt() { for (auto& [function, cgFunction] : std::as_const(functions)) { if (function == nullptr || cgFunction->GetRawValue() == nullptr) { continue; } cgFunction->Opt(); } } bool CGModule::Verify() const { bool wellFormed = true; if (llvm::verifyModule(*module, &llvm::outs())) { wellFormed = false; } return wellFormed; } void CGModule::InitDebugInfo() { module->addModuleFlag( llvm::Module::Warning, "Debug Info Version", static_cast(llvm::DEBUG_METADATA_VERSION)); module->addModuleFlag(llvm::Module::Warning, "Dwarf Version", 4); // Use dwarf 4 format. diBuilder = std::make_unique(*this); diBuilder->CreateCompileUnit(module->getSourceFileName()); // Create artificial CompileUnit. diBuilder->CreateNameSpace(GetCGContext().GetCurrentPkgName()); // Create current package as namespace. } llvm::Constant* CGModule::GenerateTypeNameConstantString(const std::string& typeMangledName, bool isWeakODR) const { auto linkageType = isWeakODR ? llvm::GlobalValue::LinkageTypes::WeakODRLinkage : llvm::GlobalValue::LinkageTypes::PrivateLinkage; auto strConstant = llvm::ConstantDataArray::getString(module->getContext(), typeMangledName); auto globalValue = module->getOrInsertGlobal(typeMangledName + ".name", strConstant->getType()); auto klassName = llvm::cast(globalValue); klassName->setInitializer(strConstant); klassName->setAlignment(llvm::Align(1)); klassName->addAttribute(CJTYPE_NAME_ATTR); AddLinkageTypeMetadata(*klassName, linkageType, false); return llvm::ConstantExpr::getBitCast(klassName, llvm::Type::getInt8PtrTy(module->getContext())); } llvm::GlobalVariable* CGModule::GeneratePrivateUnnamedAddrConstant( const std::string& name, llvm::Constant* constVal) const { auto type = constVal->getType(); auto res = llvm::cast(module->getOrInsertGlobal(name, type)); res->setConstant(true); res->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); AddLinkageTypeMetadata(*res, llvm::GlobalValue::PrivateLinkage, false); auto& layOut = module->getDataLayout(); auto align = llvm::MaybeAlign(layOut.getPrefTypeAlignment(type)); res->setAlignment(align); res->setInitializer(constVal); return res; } llvm::GlobalVariable* CGModule::GetOrCreateGlobalVariable( llvm::Constant* constVal, const std::string& gvContent, bool isTuple) { std::string name = isTuple ? GetConstantTupleName(gvContent) : GetConstantArrayName(gvContent); if (auto gv = module->getGlobalVariable(name, true); gv) { return gv; } else { return GeneratePrivateUnnamedAddrConstant(name, constVal); } } llvm::Function* CGModule::GetOrInsertFunction(const std::string& funcMangledName, llvm::FunctionType* funcType) const { auto functionCallee = module->getOrInsertFunction(funcMangledName, funcType); auto callee = functionCallee.getCallee(); CJC_ASSERT(llvm::isa(callee)); return llvm::cast(callee); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::string CGModule::GetEnumCaseClassName(const CHIR::EnumType& chirEnumType, std::size_t ctorIndex) { auto enumDef = chirEnumType.GetEnumDef(); auto name = enumDef->GetPackageName() + ':' + GetCustomTypeDefIdentifier(*enumDef) + ':' + std::to_string(ctorIndex); if (!CGType::GetOrCreate(*this, &chirEnumType)->IsDynamicGI() && !chirEnumType.GetTypeArgs().empty()) { name = name + '<' + GetTypeArgsQualifiedName(chirEnumType.GetTypeArgs()) + '>'; } return name; } llvm::GlobalVariable* CGModule::GetOrCreateEnumCtorTIOrTT(const CHIR::EnumType& chirEnumType, std::size_t ctorIndex) { auto isTT = CGType::GetOrCreate(*this, &chirEnumType)->IsDynamicGI(); auto className = GetEnumCaseClassName(chirEnumType, ctorIndex) + (isTT ? ".tt" : ".ti"); auto gvType = isTT ? CGType::GetOrCreateTypeTemplateType(cgCtx->GetLLVMContext()) : CGType::GetOrCreateTypeInfoType(cgCtx->GetLLVMContext()); auto gvOfEnumCtorTIOrTT = GetLLVMModule()->getGlobalVariable(className, true); if (!gvOfEnumCtorTIOrTT) { gvOfEnumCtorTIOrTT = llvm::cast(GetLLVMModule()->getOrInsertGlobal(className, gvType)); EnumCtorTIOrTTGenerator(*this, chirEnumType, ctorIndex).Emit(); } return gvOfEnumCtorTIOrTT; } #endif std::vector CGModule::InitRawArrayUInt8Constants() const { auto& ctx = GetLLVMContext(); auto int32Ty = llvm::Type::getInt32Ty(ctx); auto int32PtrTy = llvm::Type::getInt32PtrTy(ctx); auto int8Ty = llvm::Type::getInt8Ty(ctx); auto int8PtrTy = llvm::Type::getInt8PtrTy(ctx); auto int16Ty = llvm::Type::getInt16Ty(ctx); auto typeTemplateType = CGType::GetOrCreateTypeTemplateType(ctx); auto typeInfoType = CGType::GetOrCreateTypeInfoType(ctx); auto ttName = "RawArray.tt"; auto memberTiName = "UInt8.ti"; // When --apc, in some subpackage, UInt8.ti has not been generated, so we add relatedType here. if (module->getGlobalVariable(memberTiName, true) == nullptr) { auto gvUInt8Ti = llvm::cast(module->getOrInsertGlobal(memberTiName, typeInfoType)); auto metaUInt8Ti = llvm::MDTuple::get(ctx, {llvm::MDString::get(ctx, "UInt8.Type")}); gvUInt8Ti->setMetadata(GC_TYPE_META_NAME, metaUInt8Ti); } auto constantInt8Zero = llvm::ConstantInt::get(int8Ty, 0); auto constantInt16Zero = llvm::ConstantInt::get(int16Ty, 0); auto constantNullPtrInt8Ptr = llvm::ConstantPointerNull::get(int8PtrTy); std::vector typeInfoVec(TYPE_INFO_FIELDS_NUM); typeInfoVec[static_cast(TYPEINFO_NAME)] = GenerateTypeNameConstantString("RawArray", false); typeInfoVec[static_cast(TYPEINFO_TYPE_KIND)] = llvm::ConstantInt::get(int8Ty, static_cast(UGTypeKind::UG_RAWARRAY)); typeInfoVec[static_cast(TYPEINFO_FIELDS_NUM)] = constantInt16Zero; typeInfoVec[static_cast(TYPEINFO_FIELDS)] = constantNullPtrInt8Ptr; typeInfoVec[static_cast(TYPEINFO_SIZE)] = llvm::ConstantInt::get(int32Ty, 0); typeInfoVec[static_cast(TYPEINFO_UUID)] = llvm::Constant::getNullValue(int32Ty); typeInfoVec[static_cast(TYPEINFO_ALIGN)] = constantInt8Zero; typeInfoVec[static_cast(TYPEINFO_SOURCE_GENERIC)] = llvm::ConstantExpr::getBitCast(GetLLVMModule()->getOrInsertGlobal(ttName, typeTemplateType), int8PtrTy); typeInfoVec[static_cast(TYPEINFO_TYPE_ARGS_NUM)] = constantInt8Zero; typeInfoVec[static_cast(TYPEINFO_INHERITED_CLASS_NUM)] = llvm::ConstantInt::get(int16Ty, INHERITED_CLASS_NUM_FE_FLAG); typeInfoVec[static_cast(TYPEINFO_OFFSETS)] = llvm::ConstantPointerNull::get(int32PtrTy); typeInfoVec[static_cast(TYPEINFO_TYPE_ARGS)] = constantNullPtrInt8Ptr; typeInfoVec[static_cast(TYPEINFO_SUPER)] = llvm::cast(GetLLVMModule()->getOrInsertGlobal(memberTiName, typeInfoType)); typeInfoVec[static_cast(TYPEINFO_EXTENSIONDEF_PTR)] = llvm::ConstantPointerNull::get(CGType::GetOrCreateExtensionDefPtrType(ctx)->getPointerTo()); typeInfoVec[static_cast(TYPEINFO_MTABLE)] = constantNullPtrInt8Ptr; typeInfoVec[static_cast(TYPEINFO_REFLECTION)] = constantNullPtrInt8Ptr; typeInfoVec[static_cast(TYPEINFO_GC_TIB)] = llvm::ConstantPointerNull::get(CGType::GetBitMapType(ctx)->getPointerTo()); typeInfoVec[static_cast(TYPEINFO_FLAG)] = constantInt8Zero; return typeInfoVec; } bool CGModule::IsLinkNameUsedInMeta(const std::string& linkageName) { if (linkNameUsedInMeta.empty()) { std::vector metaTypes = {METADATA_PKG, METADATA_TYPES, METADATA_TYPETEMPLATES, METADATA_PRIMITIVE_TYPES, METADATA_PRIMITIVE_TYPETEMPLATES, METADATA_GLOBAL_VAR, METADATA_FUNCTIONS}; std::for_each(metaTypes.begin(), metaTypes.end(), [this](auto& type) { CollectLinkNameUsedInMeta(this->module->getNamedMetadata(type), this->linkNameUsedInMeta); }); } return linkNameUsedInMeta.find(linkageName) != linkNameUsedInMeta.end(); } const std::vector>& CGModule::GetAllCGExtensionDefs() { auto& subCHIRPkg = GetCGContext().GetSubCHIRPackage(); std::unordered_set traversedCustomTypeDefs; for (auto customDef : subCHIRPkg.chirCustomDefs) { if (!traversedCustomTypeDefs.emplace(customDef).second) { // It indicates this customDef has been traversed. CJC_ASSERT(false && "Duplicated customDef provided by CHIR."); continue; } switch (customDef->GetCustomKind()) { case CHIR::CustomDefKind::TYPE_CLASS: { // Core.Object is a special case, and doesn't need to generate extensionDef for it. auto classDef = dynamic_cast(customDef); if (!IsCoreObject(*classDef) && !IsClosureConversionEnvClass(*classDef)) { (void)cgExtensionDefs.emplace_back(std::make_unique(*this, *customDef)); } break; } case CHIR::CustomDefKind::TYPE_ENUM: case CHIR::CustomDefKind::TYPE_STRUCT: { (void)cgExtensionDefs.emplace_back(std::make_unique(*this, *customDef)); break; } case CHIR::CustomDefKind::TYPE_EXTEND: { auto extendedType = dynamic_cast(customDef)->GetExtendedType(); // No need to collect extensions for nominals, since we will traverse // these extensions while handling Class/Enum/Struct definitions. if (!extendedType->IsNominal() || dynamic_cast(extendedType)->GetCustomTypeDef() ->TestAttr(CHIR::Attribute::IMPORTED)) { (void)cgExtensionDefs.emplace_back(std::make_unique(*this, *customDef)); } break; } default: CJC_ASSERT(false && "Should not reach here."); return cgExtensionDefs; } } return cgExtensionDefs; } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/CGModule.h000066400000000000000000000265111510705540100207170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGMODULE_H #define CANGJIE_CGMODULE_H #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Module.h" #include "llvm/IR/CJStructTypeGCInfo.h" #include "llvm/Support/Host.h" #include "llvm/Transforms/Utils/Local.h" #include "Base/CGTypes/CGArrayType.h" #include "CGContext.h" #include "cangjie/Basic/UGTypeKind.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Value.h" #include "cangjie/Option/Option.h" namespace Cangjie { namespace CHIR { class GlobalVar; class Field; } // namespace CHIR namespace CodeGen { class CGType; class CGFunctionType; class DIBuilder; class IncrementalGen; class CGValue { public: CGValue() = default; virtual ~CGValue() = default; explicit CGValue(llvm::Value* rawVal, const CGType* cgType, bool isSRetArg = false) : rawVal(rawVal), cgType(cgType), isSRetArg(isSRetArg) { } virtual llvm::LLVMContext& GetLLVMContext() { return rawVal->getContext(); } inline llvm::Value* GetRawValue() const { return rawVal; } // It is a sugar to get `rawVal` which could simplify coding process. inline llvm::Value* operator*() const { return rawVal; } inline const CGType* GetCGType() const { return cgType; } inline const llvm::Type* GetLLVMType() const { return cgType->GetLLVMType(); } bool IsSRetArg() const { return isSRetArg; } private: llvm::Value* rawVal; const CGType* cgType; bool isSRetArg; }; class CGModule { friend class DIBuilder; public: #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND explicit CGModule(SubCHIRPackage& subCHIRPackage, CGPkgContext& cgPkgCtx); #endif ~CGModule(); inline CGContext& GetCGContext() const { CJC_ASSERT(cgCtx && "Should not be null."); return *cgCtx; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND CGCFFI& GetCGCFFI() const { CJC_ASSERT(cffi && "Should not be null."); return *cffi; } CGValue* CreateGhostCFuncArgValue(llvm::Value& llvmValue, const CGType& ty) { auto value = std::make_unique(&llvmValue, &ty); return ghostCFuncArgValues.emplace_back(std::move(value)).get(); } #endif inline llvm::Module* GetLLVMModule() const { CJC_ASSERT(module && "Should not be null."); return module.get(); } llvm::Module* ReleaseLLVMModule() { return module.release(); } void ResetLLVMModule(llvm::Module* newModule) { module.reset(newModule); } llvm::LLVMContext& GetLLVMContext() const { return cgCtx->GetLLVMContext(); } llvm::Constant* GetOrInsertBitMap(const std::string& bitStr, const std::string& name = "") { return commonBitMap->getOrInsertBitMap(bitStr, llvm::Type::getInt8PtrTy(cgCtx->GetLLVMContext()), name); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool InitIncrementalGen(); void GenIncremental(); void GenCodeGenAddedMetadata() const; #endif CGFunction* GetOrInsertCGFunction(const CHIR::Value* func, bool forWrapper = false); CGValue* GetOrInsertGlobalVariable(const CHIR::Value* chirGV); llvm::GlobalVariable* GetOrCreateGlobalVariable( llvm::Constant* constVal, const std::string& gvContent, bool isTuple); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::GlobalVariable* GetOrCreateEnumCtorTIOrTT(const CHIR::EnumType& chirEnumType, std::size_t ctorIndex); std::string GetEnumCaseClassName(const CHIR::EnumType& chirEnumType, std::size_t ctorIndex); llvm::Function* GetOrInsertFunction(const std::string& funcMangledName, llvm::FunctionType* funcType) const; #endif llvm::BasicBlock* GetMappedBB(const Cangjie::CHIR::Block* chirBB) const { if (auto it = bbMapping.find(chirBB); it != bbMapping.end()) { return it->second; } return nullptr; } CGValue* GetMappedValue(const Cangjie::CHIR::Value* chirValue) const { if (auto it = valueMapping.find(chirValue); it != valueMapping.end()) { return it->second; } return nullptr; } CGValue* GetMappedCGValue(const Cangjie::CHIR::Value* chirValue); // It is a sugar for `GetMappedCGValue` which could simplify coding process. inline CGValue* operator|(const Cangjie::CHIR::Value* chirValue) { return this->GetMappedCGValue(chirValue); } inline void SetOrUpdateMappedBB(const Cangjie::CHIR::Block* chirBB, llvm::BasicBlock* bb) { bbMapping[chirBB] = bb; } inline void SetOrUpdateMappedCGValue(const Cangjie::CHIR::Value* chirValue, std::unique_ptr value) { valueMapping[chirValue] = value.get(); allocatedCGValues.emplace_back(std::move(value)); } inline void SetValuesToLoad(const Cangjie::CHIR::Value* chirValue, llvm::Type* type, llvm::Value* value) { valuesToLoad[chirValue] = std::make_pair(type, value); } std::optional> GetValuesToLoad(const Cangjie::CHIR::Value* chirValue) { auto it = valuesToLoad.find(chirValue); return it != valuesToLoad.end() ? std::optional(it->second) : std::nullopt; } inline void SetBoxedValuesToLoad(const Cangjie::CHIR::Value* chirValue, llvm::Type* type, llvm::Value* value) { boxedValuesToLoad[chirValue] = std::make_pair(type, value); } std::optional> GetBoxedValuesToLoad(const Cangjie::CHIR::Value* chirValue) { auto it = boxedValuesToLoad.find(chirValue); return it != boxedValuesToLoad.end() ? std::optional(it->second) : std::nullopt; } void EraseUselessInstsAndDeclarations(); void EraseUselessGVAndFunctions(); void Opt(); /** * Check the module for errors. * @return false if anything is broken. */ bool Verify() const; void InitDebugInfo(); std::unique_ptr diBuilder; llvm::Constant* GenerateTypeNameConstantString(const std::string& typeMangledName, bool isWeakODR) const; llvm::Value* GenerateUnitTypeValue(); llvm::Constant* GetExceptionIntrinsicPersonality(); size_t GetTypeSize(llvm::Type* codeGenType) const { return module->getDataLayout().getTypeAllocSize(codeGenType); } std::vector InitRawArrayUInt8Constants() const; llvm::GlobalVariable* GeneratePrivateUnnamedAddrConstant(const std::string& name, llvm::Constant* constVal) const; inline void ClearLinkNameUsedInMeta() { linkNameUsedInMeta.clear(); } bool IsLinkNameUsedInMeta(const std::string& linkageName); inline static std::string GetDataLayoutString(const Triple::Info& target); inline static std::string GetTargetTripleString(const Triple::Info& target); void AddNonExternalExtensionDef(llvm::GlobalVariable* extensionDef) { (void)nonExternalExtensionDefs.emplace_back(extensionDef); } std::vector GetNonExternalExtensionDefs() const { return nonExternalExtensionDefs; } void AddExternalExtensionDef(llvm::GlobalVariable* extensionDef) { (void)externalExtensionDefs.emplace_back(extensionDef); } std::vector GetExternalExtensionDefs() const { return externalExtensionDefs; } size_t GetNonExternalExtensionDefsNum() const { return nonExternalExtensionDefs.size(); } const std::vector>& GetAllCGExtensionDefs(); std::vector GetAliveStaticGIs() const { std::vector staticGIs; for (auto& staticGIName : cgCtx->GetStaticGINames()) { if (auto gi = module->getNamedGlobal(staticGIName)) { (void)staticGIs.emplace_back(gi); } } return staticGIs; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void GenTypeInfo(); void GenTypeTemplate(); void DelayGenTypeInfo(CGType* cgType) { delayGenTypeInfo.push(cgType); } void DelayGenTypeTemplate(CGType* cgType) { delayGenTypeTemplate.push(cgType); } #endif private: std::unique_ptr cgCtx; std::unique_ptr module; std::unique_ptr commonBitMap; std::unique_ptr incrementalGen; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::unique_ptr cffi; std::vector> ghostCFuncArgValues; #endif std::unordered_map functions; std::unordered_map bbMapping; std::unordered_map valueMapping; std::unordered_map> valuesToLoad; std::unordered_map> boxedValuesToLoad; std::unordered_set linkNameUsedInMeta; std::vector> allocatedCGValues; std::vector externalExtensionDefs; std::vector nonExternalExtensionDefs; std::vector> cgExtensionDefs; std::stack delayGenTypeInfo; std::stack delayGenTypeTemplate; void MergeUselessBBIntoPreds(llvm::Function* function) const; void EraseUnusedFuncs(const std::function extraCond); void EraseUnusedGVs(const std::function extraCond); bool CheckUnusedGV(const llvm::GlobalVariable* var, const std::set& llvmUsed); }; class CGFunction : public CGValue { friend class IRBuilder2; public: explicit CGFunction( llvm::Function* function, const CGFunctionType* cgType, const CHIR::Value* chirFunc, CGModule& cgMod); bool operator==(CGFunction* that) { return GetRawValue()->getName() == that->GetRawValue()->getName(); } const CHIR::Value& GetOriginal() const { return chirFunc; } inline llvm::Function* GetRawFunction() const { return llvm::cast(GetRawValue()); } inline const CGFunctionType* GetCGFunctionType() const { return dynamic_cast(GetCGType()); } bool IsSRet() const { return isStructRet; } llvm::Argument* GetArgByIndexFromCHIR(size_t idx) const; void Opt() const; std::unordered_map> genericParamsMap; void DumpIR() const; static void RemoveUnreachableBlocks(llvm::Function& function); llvm::Function* GetWrapperFunction() const { return wrapperF; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND static void EraseReplaceableAlloca(const CGModule& cgModule, llvm::Function& function); static void AddZeroInitForStructWithRefField(CGModule& cgModule, llvm::Function& function); #endif private: CGModule& cgMod; const bool isStructRet; llvm::Function* wrapperF{nullptr}; const CHIR::Value& chirFunc; }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGMODULE_H cangjie_compiler-1.0.7/src/CodeGen/CGPkgContext.cpp000066400000000000000000000107101510705540100221050ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "CGPkgContext.h" #include "CGModule.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/Utils/ProfileRecorder.h" namespace Cangjie::CodeGen { CGPkgContext::CGPkgContext(CHIR::CHIRBuilder& chirBuilder, const CHIRData& chirData, const GlobalOptions& options, bool enableIncrement, const CachedMangleMap& cachedMangleMap) : chirBuilder(chirBuilder), chirData(chirData), options(options), enableIncrement(enableIncrement) { cachedMangleMap.Dump(); correctedCachedMangleMap.importedInlineDecls = cachedMangleMap.importedInlineDecls; correctedCachedMangleMap.newExternalDecls = cachedMangleMap.newExternalDecls; for (auto& incrRemovedDecl : cachedMangleMap.incrRemovedDecls) { if (FindCHIRGlobalValue(incrRemovedDecl)) { continue; } correctedCachedMangleMap.incrRemovedDecls.emplace(incrRemovedDecl); } correctedCachedMangleMap.Dump(); } CGPkgContext::~CGPkgContext() = default; void CGPkgContext::Clear() { for (auto& cgMod : cgMods) { cgMod->GetCGContext().Clear(); } correctedCachedMangleMap.Clear(); quickCHIRValues.Do([](std::unordered_map& object) { object.clear(); }); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND localizedSymbols.Do([](std::set& object) { object.clear(); }); #endif } std::string CGPkgContext::GetCurrentPkgName() const { auto curPackage = chirData.GetCurrentCHIRPackage(); CJC_NULLPTR_CHECK(curPackage); return curPackage->GetName(); } CHIR::FuncBase* CGPkgContext::GetImplicitUsedFunc(const std::string& funcMangledName) { auto funcs = chirData.GetImplicitFuncs(); auto it = funcs.find(funcMangledName); CJC_ASSERT(it != funcs.end()); return it->second; } void CGPkgContext::AddCGModule(std::unique_ptr& cgMod) { cgMods.emplace_back(std::move(cgMod)); } const std::vector>& CGPkgContext::GetCGModules() { return cgMods; } std::vector> CGPkgContext::ReleaseLLVMModules() { std::vector> llvmModules; std::for_each(cgMods.begin(), cgMods.end(), [&llvmModules](std::unique_ptr& cgMod) { llvmModules.emplace_back(cgMod->ReleaseLLVMModule()); cgMod->GetCGContext().Clear(); }); cgMods.clear(); return llvmModules; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void CGPkgContext::AddLocalizedSymbol(const std::string& symName) { localizedSymbols.Do([&symName](std::set& object) { object.emplace(symName); }); } const std::set& CGPkgContext::GetLocalizedSymbols() { return localizedSymbols.Do( [](const std::set& object) -> const std::set& { return object; }); } #endif CHIR::Value* CGPkgContext::FindCHIRGlobalValue(const std::string& mangledName) { const CHIR::Package& capturedChirPkg = GetCHIRPackage(); return quickCHIRValues.Do( [&capturedChirPkg, &mangledName](std::unordered_map& object) -> CHIR::Value* { if (object.empty()) { object.reserve(capturedChirPkg.GetGlobalFuncs().size() + capturedChirPkg.GetGlobalVars().size() + capturedChirPkg.GetImportedVarAndFuncs().size()); for (auto chirFunc : capturedChirPkg.GetGlobalFuncs()) { object.emplace(chirFunc->GetIdentifierWithoutPrefix(), chirFunc); } for (auto chirGv : capturedChirPkg.GetGlobalVars()) { object.emplace(chirGv->GetIdentifierWithoutPrefix(), chirGv); } for (auto importedValue : capturedChirPkg.GetImportedVarAndFuncs()) { object.emplace(importedValue->GetIdentifierWithoutPrefix(), importedValue); } } if (auto target = object.find(mangledName); target != object.end()) { return target->second; } else { return nullptr; } }); } const CHIR::Package& CGPkgContext::GetCHIRPackage() const { auto chirPackage = chirData.GetCurrentCHIRPackage(); CJC_NULLPTR_CHECK(chirPackage); return *chirPackage; } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/CGPkgContext.h000066400000000000000000000057301510705540100215600ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CODEGEN_PACKAGE_CONTEXT_H #define CANGJIE_CODEGEN_PACKAGE_CONTEXT_H #include #include "llvm/IR/Module.h" #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/IncrementalCompilation/CachedMangleMap.h" #include "cangjie/Option/Option.h" namespace Cangjie { namespace CodeGen { class CGModule; template class ObjectLocker { public: // Locks the associated object and calls the given function. template decltype(auto) Do(Func&& func) { std::unique_lock lock(this->locker); return func(object); } private: std::mutex locker; ObjectType object; }; class CGPkgContext { public: CGPkgContext(CHIR::CHIRBuilder& chirBuilder, const CHIRData& chirData, const GlobalOptions& options, bool enableIncrement, const CachedMangleMap& cachedMangleMap); ~CGPkgContext(); void Clear(); CHIR::CHIRBuilder& GetCHIRBuilder() const { return chirBuilder; } const CHIR::Package& GetCHIRPackage() const; std::string GetCurrentPkgName() const; const GlobalOptions& GetGlobalOptions() const { return options; } CHIR::FuncBase* GetImplicitUsedFunc(const std::string& funcMangledName); const CachedMangleMap& GetCachedMangleMap() const { return correctedCachedMangleMap; } bool IsIncrementEnabled() const { return enableIncrement; } bool IsCGParallelEnabled() const { return cgMods.size() > 1; } bool IsLineInfoEnabled() const { return options.enableCompileDebug || options.enableCoverage || options.displayLineInfo; } void AddCGModule(std::unique_ptr& cgMod); const std::vector>& GetCGModules(); std::vector> ReleaseLLVMModules(); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void AddLocalizedSymbol(const std::string& symName); const std::set& GetLocalizedSymbols(); #endif CHIR::Value* FindCHIRGlobalValue(const std::string& mangledName); CHIR::CHIRBuilder& chirBuilder; private: const CHIRData& chirData; const GlobalOptions& options; const bool enableIncrement; CachedMangleMap correctedCachedMangleMap; std::vector> cgMods; // Container that support quick search for target global chirValue. ObjectLocker> quickCHIRValues; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // The symbols, which need to be changed linkageType after the link. ObjectLocker> localizedSymbols; #endif }; } // namespace CodeGen } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/CodeGen/CJNative/000077500000000000000000000000001510705540100205455ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CodeGen/CJNative/CGCFFI.h000066400000000000000000000106651510705540100216470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the ABI converter APIs for codegen. */ #ifndef CANGJIE_CODEGEN_CGCFFI_H #define CANGJIE_CODEGEN_CGCFFI_H #include #include "IRBuilder.h" #include "cangjie/CHIR/Type/Type.h" namespace Cangjie { namespace CodeGen { /** * A class used for generating code related to the CFFI feature. */ class CGCFFI { public: using LLVMFuncArgIt = llvm::Function::arg_iterator; virtual ~CGCFFI() = default; /** * Get CodeGen function type by CHIR::FuncType. It's platform-related. * If any incomplete struct type exists in @p funcTy, this method will return a nullptr * and caller should check this and get a literal struct pointer type ({}*) directly if yes. * * @param funcTy It must be CFunc. */ virtual llvm::FunctionType* GetCFuncType(const CHIR::FuncType& chirFuncTy) = 0; /** * Add necessary attributes for function. It's platform-related. * * @param funcTy It must be CFunc. */ virtual void AddFunctionAttr(const CHIR::FuncType& chirFuncTy, llvm::Function& func) = 0; /** * Determine whether to perform platform-related processing on the i-th parameter. * * @param funcTy It must be CFunc. */ virtual bool NeedProcessParam(const CHIR::FuncType& chirFuncTy, size_t index) = 0; /** * Handles a specific parameter of a function. * This method needs to be used together with `NeedProcessParam`. * This function is invoked when the return result of `NeedProcessParam` is true. * * @param paramTy Parameter types in AST. * @param arg The specific parameter. * @param place The value used to hold the value in @p arg. * @param builder An IRBuilder to generate the necessary code. */ virtual void ProcessParam( CHIR::Type& chirParamTy, LLVMFuncArgIt& arg, llvm::Value* place, IRBuilder2& builder) = 0; /** * Process parameters at CFunc calls. * * @param funcTy It must be CFunc. * @param args Existing parameter for function call, which may be modified. * @param builder An IRBuilder to generate the necessary code. * * @return True if the return value of this function call needs to be processed additionally. Otherwise false. */ virtual bool ProcessInvocation( const CHIR::FuncType& chirFuncTy, std::vector& args, IRBuilder2& builder) = 0; /** * Process result of call inst. * This function can to be called only when the result of ProcessInvocation is true. * * @param retTy Callee's return type. * @param ret The call instruction. * @param sret The value used to hold the value in @p ret, it can be generated by CreateEntryAlloca. * @param builder An IRBuilder to generate the necessary code. * * @return Always @p sret now. */ virtual llvm::Value* ProcessCallRet( const CHIR::Type& chirRetTy, llvm::Value& ret, llvm::Value& sret, IRBuilder2& builder) { auto retType = ret.getType(); auto place = builder.CreateEntryAlloca(retType, nullptr, "cffi"); builder.CreateStore(&ret, place); auto& cgMod = builder.GetCGModule(); size_t actualSize = GetTypeSize(cgMod, chirRetTy); builder.CreateMemCpy(&sret, GetAlign(cgMod, chirRetTy), place, GetAlign(cgMod, *retType), actualSize); return &sret; } /** * Process the return value of CFunc on demand. * * @param retType Return type of the function. * @param val Return type of the function, its type can different with @p retType. * @param builder An IRBuilder to generate the necessary code. * * @return The final return value of the function, it can be same with @p val. */ virtual llvm::Value* ProcessRetValue(const llvm::Type& retType, llvm::Value& val, IRBuilder2& builder) = 0; protected: llvm::MaybeAlign GetAlign(const CGModule& cgMod, const CHIR::Type& chirTy) const { return llvm::MaybeAlign(GetTypeAlignment(cgMod, chirTy)); } llvm::MaybeAlign GetAlign(const CGModule& cgMod, llvm::Type& llvmTy) const { return llvm::MaybeAlign(GetTypeAlignment(cgMod, llvmTy)); } }; } // namespace CodeGen } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/CodeGen/CJNative/CGTypeInfo.cpp000066400000000000000000000133321510705540100232220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "CJNative/CGTypeInfo.h" namespace Cangjie::CodeGen { namespace CGTypeInfo { llvm::Function* GenTypeInfoFns(llvm::FunctionType* fieldFnType, CGModule& cgMod, const std::string& funcName, const std::unordered_map& outerGTIdxMap, const CHIR::Type& memberType, const std::string& memberName) { llvm::Function* getTiFn = llvm::Function::Create(fieldFnType, llvm::Function::PrivateLinkage, funcName, cgMod.GetLLVMModule()); getTiFn->addFnAttr("native-interface-fn"); auto cgType = CGType::GetOrCreate(cgMod, DeRef(memberType)); CodeGen::IRBuilder2 irBuilder(cgMod); auto entryBB = irBuilder.CreateEntryBasicBlock(getTiFn, "entry"); irBuilder.SetInsertPoint(entryBB); std::unordered_map> genericParamsMap; llvm::Value* ti{nullptr}; if (cgType->IsConcrete() || cgType->IsStaticGI()) { ti = irBuilder.CreateTypeInfo(memberType, genericParamsMap); } else if (memberType.IsGeneric()) { auto gt = static_cast(&memberType); std::string varName = "ti." + gt->GetSrcCodeIdentifier(); ti = irBuilder.GetTypeInfoFromTiArray(getTiFn->getArg(1), outerGTIdxMap.at(gt), varName); } else { auto args = getTiFn->getArg(1); for (auto& [gt, idx] : outerGTIdxMap) { auto localGt = gt; auto localIdx = idx; genericParamsMap.emplace(gt, [args, localGt, localIdx](IRBuilder2& irBuilderLocal) { auto varName = "ti." + localGt->GetSrcCodeIdentifier(); return irBuilderLocal.GetTypeInfoFromTiArray(args, localIdx, varName); }); } ti = irBuilder.CreateTypeInfo(*DeRef(memberType), genericParamsMap); } llvm::Value* bitcastedValue = irBuilder.CreateBitCast(ti, llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext())); irBuilder.CreateRet(bitcastedValue); return getTiFn; } llvm::Constant* GenSuperFnOfTypeTemplate(CGModule& cgMod, const std::string& funcName, const CHIR::Type& superType, const std::vector& typeArgs) { std::vector argTypes{llvm::Type::getInt32Ty(cgMod.GetLLVMContext()), CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext())->getPointerTo()}; llvm::FunctionType* superFnType = llvm::FunctionType::get(llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()), argTypes, false); std::unordered_map localGenericParamIndicesMap; size_t idx = 0; for (auto typeArg : typeArgs) { localGenericParamIndicesMap.emplace(typeArg, idx); ++idx; } llvm::Function* superFn = GenTypeInfoFns(superFnType, cgMod, funcName, localGenericParamIndicesMap, superType, superType.ToString()); return llvm::ConstantExpr::getBitCast(superFn, llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext())); } llvm::Constant* GenFieldsFnsOfTypeTemplate( CGModule& cgMod, const std::string& funcPrefixName, const std::vector& fieldsFn) { auto fieldNum = fieldsFn.size(); auto i8PtrTy = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); CJC_ASSERT(fieldNum > 0 && "field num should be at least 1"); llvm::ArrayType* arrayType = llvm::ArrayType::get(fieldsFn[0]->getType(), fieldNum); llvm::GlobalVariable* globalArray = llvm::cast( cgMod.GetLLVMModule()->getOrInsertGlobal(funcPrefixName + ".fieldTiFns", arrayType)); globalArray->setLinkage(llvm::GlobalValue::PrivateLinkage); globalArray->setInitializer(llvm::ConstantArray::get(arrayType, fieldsFn)); globalArray->addAttribute(CJTT_FIELDS_FNS_ATTR); return llvm::ConstantExpr::getBitCast(globalArray, i8PtrTy); } llvm::Constant* GenFieldsFnsOfTypeTemplate(CGModule& cgMod, const std::string& funcPrefixName, const std::vector& fieldTypes, const std::unordered_map& genericParamIndicesMap) { auto fieldNum = fieldTypes.size(); auto i8PtrTy = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); if (fieldNum == 0) { return llvm::ConstantPointerNull::get(i8PtrTy); } // Step1 create FunctionType which return (i32, ti**)->i8* std::vector argTypes; auto typeInfoType = CGType::GetOrCreateTypeInfoType(cgMod.GetLLVMContext()); argTypes.emplace_back(llvm::Type::getInt32Ty(cgMod.GetLLVMContext())); argTypes.emplace_back(typeInfoType->getPointerTo()->getPointerTo()); llvm::FunctionType* fieldFn = llvm::FunctionType::get(llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()), argTypes, false); // Step2 create a global array named "type_mangled_name + $GetFieldFns", which size is fieldNum, element is // FunctionType* fieldFn std::vector fieldFnTypes(fieldNum, fieldFn->getPointerTo()); // fill the elements inside the array std::vector initializers; // Step3:build Generic Param map for current customType eg:CA size_t idx = 0; for (auto fieldType : fieldTypes) { auto fieldName = std::to_string(idx); auto funcName = funcPrefixName + ".fieldTiFn." + fieldName; llvm::Function* fieldFnGlobal = CGTypeInfo::GenTypeInfoFns(fieldFn, cgMod, funcName, genericParamIndicesMap, *fieldType, fieldName); initializers.emplace_back(fieldFnGlobal); ++idx; } return GenFieldsFnsOfTypeTemplate(cgMod, funcPrefixName, initializers); } } // namespace CGTypeInfo } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/CJNative/CGTypeInfo.h000066400000000000000000000025141510705540100226670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the utils of TypeInfo. */ #ifndef CANGJIE_CGTYPEINFO_H #define CANGJIE_CGTYPEINFO_H #include "IRBuilder.h" namespace Cangjie::CodeGen { namespace CGTypeInfo { llvm::Function* GenTypeInfoFns(llvm::FunctionType* fieldFnType, CGModule& cgMod, const std::string& funcName, const std::unordered_map& outerGTIdxMap, const CHIR::Type& memberType, const std::string& memberName); llvm::Constant* GenSuperFnOfTypeTemplate(CGModule& cgMod, const std::string& funcName, const CHIR::Type& superType, const std::vector& typeArgs); llvm::Constant* GenFieldsFnsOfTypeTemplate( CGModule& cgMod, const std::string& funcPrefixName, const std::vector& fieldsFn); llvm::Constant* GenFieldsFnsOfTypeTemplate(CGModule& cgMod, const std::string& funcPrefixName, const std::vector& fieldTypes, const std::unordered_map& genericParamIndicesMap); } // namespace CGTypeInfo } // namespace Cangjie::CodeGen #endif // CANGJIE_CGTYPEINFO_H cangjie_compiler-1.0.7/src/CodeGen/CJNative/CGTypes/000077500000000000000000000000001510705540100220635ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CodeGen/CJNative/CGTypes/CGExtensionDef.h000066400000000000000000000116231510705540100250440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the class for determine the memory layout of Class/Interface. */ #ifndef CANGJIE_CG_EXTENSION_DEF_H #define CANGJIE_CG_EXTENSION_DEF_H #include "Base/CGTypes/CGType.h" #include #include #include "CGModule.h" #include "IRBuilder.h" #include "Utils/CGCommonDef.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Type/Type.h" namespace Cangjie::CodeGen { class CGExtensionDef { public: // Indicates the interpretation of 'value' based on the `isTypeInfo` flag. // 1. When `isTypeInfo` is true: `value` represents `typeInfo` // 2. When `isTypeInfo` is false: `value` represents `typeArgs` // (Note: These typeArgs are typically passed by the runtime, not read from `typeInfo`) struct InnerTiInfo { llvm::Value* value; bool isTypeInfo; }; explicit CGExtensionDef(CGModule& cgMod, const CHIR::CustomTypeDef& chirDef); std::pair>> Emit(); uint32_t GetStartIdx() const { return startIdxOfNonExternalExtensionDef; } const std::vector>& GetExtendInterfaces() const { return extendInterfaces; } static std::vector GetEmptyExtensionDefContent(CGModule& cgMod, const CHIR::Type& targetType); static bool CreateExtensionDefForType(CGModule& cgMod, const std::string& extensionDefName, const std::vector& content, const CHIR::ClassType& inheritedType, bool isForExternalType = false); static bool FoundGenericTypeAndCollectPath( const CHIR::Type& srcType, CHIR::GenericType& gt, std::vector& path); static llvm::Value* GetTypeInfoWithPath(IRBuilder2& irBuilder, const CHIR::Type& type, llvm::Value* entryTypeArgs, std::queue&& remainPath, std::unordered_map& innerTypeMap); private: bool CreateExtensionDefForType(const CHIR::ClassType& inheritedType); /** * Returns a pair: * The first element is FuncPtr if the second is `false` or TypeInfo if the second is `true`. */ std::pair GenerateInterfaceFn(const CHIR::ClassType& inheritedType); llvm::Constant* GenerateFuncTableForType(const std::vector& virtualFuncInfos); llvm::Constant* GenerateWhereConditionFn(); llvm::Value* CreateTypeComparison( IRBuilder2& irBuilder, llvm::Value* typeInfo, const CHIR::Type& staticType, const std::string& prefix); llvm::Value* CreateCompareArgs(IRBuilder2& irBuilder, llvm::Value* typeInfos, const std::vector& typeArgs, const std::string& prefix = ""); llvm::Value* GetTypeInfoOfGeneric(IRBuilder2& irBuilder, CHIR::GenericType& gt); void CollectGenericParamIndicesMap(); llvm::Value* CheckGenericParams(IRBuilder2& irBuilder, llvm::Value* retVal); static llvm::Constant* GetTargetType(CGModule& cgModule, const CHIR::Type& type); private: CGModule& cgMod; CGContext& cgCtx; const CHIR::CustomTypeDef& chirDef; std::string typeMangle; std::string extendDefName; const CHIR::Type* targetType{nullptr}; bool isForExternalType = false; llvm::Constant* whereCondFn{nullptr}; std::unordered_map> genericParamsMap; /** * This map is creating & using during 'GenerateWhereConditionFn', and will be cleared at quit time. * Record every inner type args local value. * eg: for 'Array<(Option, Int64)>' map will contains: * typeinfo of '(Option, Int64)', typeinfo of 'Option' in accessing order. */ std::unordered_map innerTypeMap; /** * Map of the path to get generic type from 'extendedType'. * eg: decl is 'extend A> {}', path for 'T' is '1, 0, 1'. */ std::unordered_map> gtAccessPathMap; /** * Map of CHIR generic type's id to their runtime typeInfo. * In partial instantiated extend, one generic type may mapping to multiple runtime typeInfo. eg: * class A {} * interface I{} * extend A <: I {} * For this extend, 'T' have two runtime typeInfo, and they must be same. */ std::map> generatedGenerics; std::map extendInterfaces2; std::vector> extendInterfaces; uint32_t startIdxOfNonExternalExtensionDef = 0; }; } // namespace Cangjie::CodeGen #endif // CANGJIE_CG_EXTENSION_DEF_H cangjie_compiler-1.0.7/src/CodeGen/CJNative/CGTypes/CJNativeCGExtensionDef.cpp000066400000000000000000000620221510705540100267620ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the class for determine the memory layout of Class/Interface. */ #include "CJNative/CGTypes/CGExtensionDef.h" #include "Base/CGTypes/CGCustomType.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Type/CustomTypeDef.h" #include "cangjie/CHIR/Value.h" using namespace Cangjie; using namespace CodeGen; CGExtensionDef::CGExtensionDef(CGModule& cgMod, const CHIR::CustomTypeDef& chirDef) : cgMod(cgMod), cgCtx(cgMod.GetCGContext()), chirDef(chirDef) { if (chirDef.GetCustomKind() == CHIR::CustomDefKind::TYPE_EXTEND) { targetType = StaticCast(chirDef).GetExtendedType(); } else { targetType = chirDef.GetType(); } CJC_NULLPTR_CHECK(targetType); isForExternalType = IsExternalDefinedType(*targetType); typeMangle = GetTypeQualifiedName(*targetType); } namespace { inline void HandleShortcutBranch(IRBuilder2& irBuilder, llvm::Value* condition, const std::string& prefix) { auto [trueBB, falseBB] = Vec2Tuple<2>( irBuilder.CreateAndInsertBasicBlocks({GenNameForBB(prefix + "_true"), GenNameForBB(prefix + "_false")})); irBuilder.CreateCondBr(condition, trueBB, falseBB); irBuilder.SetInsertPoint(falseBB); irBuilder.CreateRet(irBuilder.getInt1(false)); irBuilder.SetInsertPoint(trueBB); } } // namespace llvm::Constant* CGExtensionDef::GetTargetType(CGModule& cgModule, const CHIR::Type& type) { llvm::Constant* res{nullptr}; auto cgType = CGType::GetOrCreate(cgModule, &type); if (cgType->IsDynamicGI()) { if (type.IsNominal()) { cgType = CGType::GetOrCreate(cgModule, StaticCast(type).GetCustomTypeDef()->GetType()); } res = cgType->GetOrCreateTypeTemplate(); } else { res = cgType->GetOrCreateTypeInfo(); } return llvm::ConstantExpr::getBitCast(res, llvm::Type::getInt8PtrTy(cgModule.GetLLVMContext())); } llvm::Value* CGExtensionDef::CreateTypeComparison( IRBuilder2& irBuilder, llvm::Value* typeInfo, const CHIR::Type& staticType, const std::string& prefix) { auto derefType = DeRef(staticType); auto cgType = CGType::GetOrCreate(cgMod, derefType); if (cgType->IsConcrete()) { // For concrete type, determine equality based on the address. return irBuilder.CreateICmpEQ(typeInfo, cgType->GetOrCreateTypeInfo()); } else if (cgType->IsStaticGI()) { // For StaticGI, determine equality through runtime API. return irBuilder.CallIntrinsicIsTypeEqualTo({typeInfo, cgType->GetOrCreateTypeInfo()}); } else if (staticType.IsGeneric()) { // isSubtype(ki, Uppers); eg: T <: A & I auto& gt = StaticCast(staticType); generatedGenerics[gt.ToString()].emplace_back(typeInfo); auto& uppers = gt.GetUpperBounds(); std::vector fixedUppers; for (auto upper : uppers) { derefType = DeRef(*upper); if (derefType->IsCType()) { continue; } (void)fixedUppers.emplace_back(derefType); } if (fixedUppers.empty()) { return irBuilder.getInt1(true); } llvm::Value* res{nullptr}; for (size_t i = 0; i < fixedUppers.size(); ++i) { if (i == 0) { auto upperBoundTypeInfo = irBuilder.CreateTypeInfo(*fixedUppers[i], genericParamsMap, false); res = irBuilder.CallIntrinsicIsSubtype({typeInfo, upperBoundTypeInfo}); } else { auto upperPrefix = prefix + "_cs_" + std::to_string(i); HandleShortcutBranch(irBuilder, res, upperPrefix); auto upperBoundTypeInfo = irBuilder.CreateTypeInfo(*fixedUppers[i], genericParamsMap, false); res = irBuilder.CallIntrinsicIsSubtype({typeInfo, upperBoundTypeInfo}); } } return res; } else { // and(cmp(ki.template, ti.template), compare(ti.typeArgs, ki.typeArgs)) auto typeInfoType = CGType::GetOrCreateTypeInfoType(cgMod.GetLLVMContext()); auto realTypeTemplatePtr = irBuilder.CreateStructGEP(typeInfoType, typeInfo, static_cast(TYPEINFO_SOURCE_GENERIC)); auto realTypeTemplate = irBuilder.LLVMIRBuilder2::CreateLoad(irBuilder.getInt8PtrTy(), realTypeTemplatePtr, prefix + "_tt"); auto typeCmp = irBuilder.CreateICmpEQ(realTypeTemplate, GetTargetType(cgMod, *derefType)); HandleShortcutBranch(irBuilder, typeCmp, prefix + "_tt"); auto backupBB = irBuilder.GetInsertBlock(); auto backupIt = irBuilder.GetInsertPoint(); irBuilder.SetInsertPointForPreparingTypeInfo(); auto typeInfosPtr = irBuilder.CreateStructGEP(typeInfoType, typeInfo, static_cast(TYPEINFO_TYPE_ARGS)); llvm::Value* typeInfos = irBuilder.LLVMIRBuilder2::CreateLoad(irBuilder.getInt8PtrTy(), typeInfosPtr); typeInfos = irBuilder.CreateBitCast(typeInfos, typeInfoType->getPointerTo()->getPointerTo()); irBuilder.SetInsertPoint(backupBB, backupIt); return CreateCompareArgs(irBuilder, typeInfos, derefType->GetTypeArgs(), prefix); } } llvm::Value* CGExtensionDef::CreateCompareArgs( IRBuilder2& irBuilder, llvm::Value* typeInfos, const std::vector& typeArgs, const std::string& prefix) { auto typeInfoPtrType = CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext()); auto argSize = typeArgs.size(); llvm::Value* retVal; for (size_t i = 0; i < argSize; ++i) { auto backupBB = irBuilder.GetInsertBlock(); auto backupIt = irBuilder.GetInsertPoint(); irBuilder.SetInsertPointForPreparingTypeInfo(); llvm::Value* tiPtr = irBuilder.CreateConstGEP1_32(typeInfoPtrType, typeInfos, i); auto ti = irBuilder.LLVMIRBuilder2::CreateLoad(typeInfoPtrType, tiPtr, std::to_string(i) + "ti"); auto chirType = typeArgs[i]; innerTypeMap.emplace(chirType, InnerTiInfo{ti, true}); irBuilder.SetInsertPoint(backupBB, backupIt); auto idxStr = prefix + "_" + std::to_string(i); if (i == 0) { retVal = CreateTypeComparison(irBuilder, ti, *chirType, idxStr); } else { HandleShortcutBranch(irBuilder, retVal, idxStr); retVal = CreateTypeComparison(irBuilder, ti, *chirType, idxStr); } } return retVal; } llvm::Value* CGExtensionDef::CheckGenericParams(IRBuilder2& irBuilder, llvm::Value* retVal) { for (auto it = generatedGenerics.begin(); it != generatedGenerics.end();) { it = it->second.size() <= 1 ? generatedGenerics.erase(it) : ++it; } if (generatedGenerics.empty()) { return retVal; // All generic type has 0 or 1 associated value. } llvm::Value* newRet = retVal; for (auto [_, values] : generatedGenerics) { CJC_ASSERT(!values.empty()); for (size_t i = 0; i < values.size() - 1; ++i) { HandleShortcutBranch(irBuilder, newRet, "type_cs"); newRet = irBuilder.CallIntrinsicIsTypeEqualTo({values[i], values[i + 1]}); } } CJC_ASSERT(newRet != retVal); // Return value must be updated. return newRet; } bool CGExtensionDef::FoundGenericTypeAndCollectPath( const CHIR::Type& srcType, CHIR::GenericType& gt, std::vector& path) { if (&srcType == >) { return true; } auto typeArgs = DeRef(srcType)->GetTypeArgs(); for (size_t i = 0; i < typeArgs.size(); ++i) { path.emplace_back(i); if (FoundGenericTypeAndCollectPath(*typeArgs[i], gt, path)) { return true; } path.pop_back(); } return false; } llvm::Value* CGExtensionDef::GetTypeInfoWithPath(IRBuilder2& irBuilder, const CHIR::Type& type, llvm::Value* entryTypeArgs, std::queue&& remainPath, std::unordered_map& innerTypeMap) { auto typeInfoType = CGType::GetOrCreateTypeInfoType(irBuilder.GetLLVMContext()); auto typeInfoPtrType = typeInfoType->getPointerTo(); auto curType = &type; auto typeArgs = entryTypeArgs; while (!remainPath.empty()) { auto idx = remainPath.front(); remainPath.pop(); auto backupBB = irBuilder.GetInsertBlock(); auto backupIt = irBuilder.GetInsertPoint(); irBuilder.SetInsertPointForPreparingTypeInfo(); auto curTiPtr = irBuilder.CreateConstGEP1_32(typeInfoPtrType, typeArgs, idx); auto curTi = irBuilder.LLVMIRBuilder2::CreateLoad(typeInfoPtrType, curTiPtr); // Also update 'innerTypeMap'. curType = DeRef(*curType)->GetTypeArgs()[idx]; (void)innerTypeMap.emplace(curType, InnerTiInfo{curTi, true}); irBuilder.SetInsertPoint(backupBB, backupIt); if (!remainPath.empty()) { auto typeInfosPtr = irBuilder.CreateStructGEP(typeInfoType, curTi, static_cast(TYPEINFO_TYPE_ARGS)); typeArgs = irBuilder.LLVMIRBuilder2::CreateLoad(irBuilder.getInt8PtrTy(), typeInfosPtr); typeArgs = irBuilder.CreateBitCast(typeArgs, typeInfoPtrType->getPointerTo()); } } return innerTypeMap.at(curType).value; } llvm::Value* CGExtensionDef::GetTypeInfoOfGeneric(IRBuilder2& irBuilder, CHIR::GenericType& gt) { std::stack>> candidates; std::queue remainPath; auto currentType = targetType; for (auto idx : gtAccessPathMap[>]) { remainPath.push(idx); } candidates.push(std::make_pair(currentType, remainPath)); while (!remainPath.empty()) { auto idx = remainPath.front(); remainPath.pop(); auto argType = currentType->GetTypeArgs()[idx]; candidates.push(std::make_pair(argType, remainPath)); currentType = DeRef(*argType); } while (!candidates.empty()) { auto [typeArg, remainPathQ] = candidates.top(); candidates.pop(); auto found = innerTypeMap.find(typeArg); if (found != innerTypeMap.end()) { auto typeArgs = found->second.isTypeInfo ? irBuilder.GetTypeArgsFromTypeInfo(found->second.value) : found->second.value; return GetTypeInfoWithPath(irBuilder, *typeArg, typeArgs, std::move(remainPathQ), innerTypeMap); } } InternalError("Failed to load generic type info for extended type '" + targetType->ToString() + "'"); return nullptr; } void CGExtensionDef::CollectGenericParamIndicesMap() { for (auto gt : chirDef.GetGenericTypeParams()) { auto ret = FoundGenericTypeAndCollectPath(*targetType, *gt, gtAccessPathMap[gt]); if (!ret) { InternalError("Generic type of extendDef '" + chirDef.GetIdentifier() + "' is not found in target type '" + targetType->ToString() + "'"); } } /** * For 'extend Array> where T <: Collection' * 'typeInfos' is [typeinfo_of(Option<(T, K)>)]. We need to get 'K' from the map ('T' is not used in upperBound). */ for (auto gt : chirDef.GetGenericTypeParams()) { genericParamsMap.emplace( gt, [gt, this](IRBuilder2& irBuilder) { return GetTypeInfoOfGeneric(irBuilder, *gt); }); } } llvm::Constant* CGExtensionDef::GenerateWhereConditionFn() { if (whereCondFn) { return whereCondFn; } auto& llvmCtx = cgMod.GetLLVMContext(); // Function signature is: bool (uint32_t, TypeInfo*[]). std::vector argTypes{ llvm::Type::getInt32Ty(llvmCtx), CGType::GetOrCreateTypeInfoPtrType(llvmCtx)->getPointerTo()}; llvm::FunctionType* whereCondFnType = llvm::FunctionType::get(llvm::Type::getInt1Ty(llvmCtx), argTypes, false); auto needGenerateFnForGenericType = [this]() { // 1. If any of the genericType has upperbound, the 'whereCondFn' function must be generated. // 2. If all genericTypes do not have upperbound and the current 'chirDef' is not 'ExtendDef', // OR, if the extended type's type arguments are in same order of 'ExtendDef's GetGenericTypeParams, // we do not need to generate the 'whereCondFn' function. // Otherwise, we always need to generate the 'whereCondFn' function. std::vector genericTypes; for (auto gt : chirDef.GetGenericTypeParams()) { if (!gt->GetUpperBounds().empty()) { return true; } genericTypes.emplace_back(gt); } bool isAllTypeArgsGeneric = true; for (auto typeArg : targetType->GetTypeArgs()) { if (!typeArg->IsGeneric()) { isAllTypeArgsGeneric = false; break; } } return !isAllTypeArgsGeneric && chirDef.GetCustomKind() == CHIR::CustomDefKind::TYPE_EXTEND && genericTypes != targetType->GetTypeArgs(); }; auto i8PtrTy = llvm::Type::getInt8PtrTy(llvmCtx); auto targetCGType = CGType::GetOrCreate(cgMod, targetType); if (!targetCGType->IsDynamicGI() || !needGenerateFnForGenericType()) { return llvm::Constant::getNullValue(i8PtrTy); } auto funcName = extendDefName + "_cs"; if (auto cs = cgMod.GetLLVMModule()->getFunction(funcName)) { return llvm::ConstantExpr::getBitCast(cs, i8PtrTy); } auto fn = llvm::Function::Create(whereCondFnType, llvm::Function::PrivateLinkage, funcName, cgMod.GetLLVMModule()); fn->addFnAttr("native-interface-fn"); auto entryBB = llvm::BasicBlock::Create(llvmCtx, "entry", fn); IRBuilder2 irBuilder(cgMod, entryBB); auto typeInfos = fn->getArg(1); // Parameter with index 1 is an array of typeinfo. innerTypeMap.emplace(targetType, InnerTiInfo{typeInfos, false}); llvm::Value* retVal = CreateCompareArgs(irBuilder, typeInfos, targetType->GetTypeArgs()); retVal = CheckGenericParams(irBuilder, retVal); irBuilder.CreateRet(retVal); innerTypeMap.clear(); whereCondFn = llvm::ConstantExpr::getBitCast(fn, i8PtrTy); return whereCondFn; } llvm::Constant* CGExtensionDef::GenerateFuncTableForType(const std::vector& virtualFuncInfos) { auto i8PtrType = llvm::Type::getInt8PtrTy(cgCtx.GetLLVMContext()); auto funcTableSize = virtualFuncInfos.size(); if (funcTableSize == 0) { return llvm::Constant::getNullValue(i8PtrType); } auto tableType = llvm::ArrayType::get(i8PtrType, funcTableSize); auto funcTableGV = llvm::cast(cgMod.GetLLVMModule()->getOrInsertGlobal(extendDefName + ".ft", tableType)); if (funcTableGV->hasInitializer()) { return llvm::ConstantExpr::getBitCast(funcTableGV, i8PtrType); } funcTableGV->setLinkage(llvm::GlobalVariable::PrivateLinkage); std::vector funcTable(funcTableSize); for (size_t i = 0; i < funcTableSize; ++i) { auto funcInfo = virtualFuncInfos[i]; if (funcInfo.instance) { auto function = cgMod.GetOrInsertCGFunction(funcInfo.instance)->GetRawFunction(); funcTable[i] = llvm::ConstantExpr::getBitCast(function, i8PtrType); } else { funcTable[i] = llvm::ConstantPointerNull::get(llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext())); } } funcTableGV->setInitializer(llvm::ConstantArray::get(tableType, funcTable)); funcTableGV->addAttribute(CJED_FUNC_TABLE_ATTR); return llvm::ConstantExpr::getBitCast(funcTableGV, i8PtrType); } std::pair CGExtensionDef::GenerateInterfaceFn(const CHIR::ClassType& inheritedType) { CJC_ASSERT(!inheritedType.IsGeneric()); auto& llvmCtx = cgMod.GetLLVMContext(); auto i8PtrTy = llvm::Type::getInt8PtrTy(llvmCtx); if (auto cgType = CGType::GetOrCreate(cgMod, &inheritedType); cgType && !cgType->IsDynamicGI()) { auto ti = cgType->GetOrCreateTypeInfo(); return {llvm::ConstantExpr::getBitCast(ti, i8PtrTy), true}; } std::vector argTypes{ llvm::Type::getInt32Ty(llvmCtx), CGType::GetOrCreateTypeInfoPtrType(llvmCtx)->getPointerTo()}; llvm::FunctionType* interfaceFnType = llvm::FunctionType::get(i8PtrTy, argTypes, false); std::string funcName = extendDefName + "_iFn"; if (auto iFn = cgMod.GetLLVMModule()->getFunction(funcName)) { return {llvm::ConstantExpr::getBitCast(iFn, i8PtrTy), false}; } llvm::Function* interfaceFn = llvm::Function::Create(interfaceFnType, llvm::Function::PrivateLinkage, funcName, cgMod.GetLLVMModule()); interfaceFn->addFnAttr("native-interface-fn"); auto entryBB = llvm::BasicBlock::Create(llvmCtx, "entry", interfaceFn); // Parameter with index 1 is an array of typeinfo. innerTypeMap.emplace(targetType, InnerTiInfo{interfaceFn->getArg(1), false}); IRBuilder2 irBuilder(cgMod, entryBB); auto derefType = DeRef(inheritedType); llvm::Value* ti = irBuilder.CreateTypeInfo(*derefType, genericParamsMap, false); innerTypeMap.clear(); llvm::Value* retVal = irBuilder.CreateBitCast(ti, i8PtrTy); irBuilder.CreateRet(retVal); return {llvm::ConstantExpr::getBitCast(interfaceFn, i8PtrTy), false}; } std::vector CGExtensionDef::GetEmptyExtensionDefContent(CGModule& cgMod, const CHIR::Type& targetType) { auto& llvmCtx = cgMod.GetLLVMContext(); auto i8NullVal = llvm::ConstantInt::getNullValue(llvm::Type::getInt8Ty(llvmCtx)); auto i8PtrNullVal = llvm::ConstantInt::getNullValue(llvm::Type::getInt8PtrTy(llvmCtx)); std::vector defConstants(static_cast(EXTENSION_DEF_FIELDS_NUM)); defConstants[static_cast(IS_INTERFACE_TI)] = i8NullVal; defConstants[static_cast(INTERFACE_FN_OR_INTERFACE_TI)] = i8PtrNullVal; defConstants[static_cast(WHERE_CONDITION_FN)] = i8PtrNullVal; defConstants[static_cast(FUNC_TABLE)] = i8PtrNullVal; defConstants[static_cast(TARGET_TYPE)] = GetTargetType(cgMod, targetType); auto cgType = CGType::GetOrCreate(cgMod, &targetType); auto i32Ty = llvm::Type::getInt32Ty(llvmCtx); defConstants[static_cast(TYPE_PARAM_COUNT)] = llvm::ConstantInt::get(i32Ty, cgType->IsDynamicGI() ? targetType.GetTypeArgs().size() : 0U); return defConstants; } bool CGExtensionDef::CreateExtensionDefForType(CGModule& cgMod, const std::string& extensionDefName, const std::vector& content, const CHIR::ClassType& inheritedType, bool isForExternalType) { auto& llvmCtx = cgMod.GetLLVMContext(); auto extensionDefType = CGType::GetOrCreateExtensionDefType(llvmCtx); auto extensionDef = llvm::cast(cgMod.GetLLVMModule()->getOrInsertGlobal(extensionDefName, extensionDefType)); CJC_ASSERT(extensionDef); if (extensionDef->hasInitializer()) { return false; } extensionDef->setLinkage(llvm::GlobalValue::PrivateLinkage); extensionDef->setInitializer(llvm::ConstantStruct::get(extensionDefType, content)); extensionDef->addAttribute(GC_MTABLE_ATTR); if (inheritedType.GetTypeArgs().size() == 1) { auto inheritedTypeMeta = UnwindGenericRelateType(llvmCtx, inheritedType); extensionDef->setMetadata("inheritedType", llvm::MDTuple::get(llvmCtx, inheritedTypeMeta)); } else if (inheritedType.GetTypeArgs().empty()) { auto inheritedTypeMeta = CGType::GetNameOfTypeInfoGV(inheritedType); extensionDef->setMetadata( "inheritedType", llvm::MDTuple::get(llvmCtx, llvm::MDString::get(llvmCtx, inheritedTypeMeta))); } if (isForExternalType) { cgMod.AddExternalExtensionDef(extensionDef); } else { cgMod.AddNonExternalExtensionDef(extensionDef); } return true; } bool CGExtensionDef::CreateExtensionDefForType(const CHIR::ClassType& inheritedType) { auto& vtable = chirDef.GetVTable(); auto found = vtable.find(const_cast(&inheritedType)); auto funcTableSize = found == vtable.end() ? 0 : found->second.size(); if (funcTableSize == 0 && inheritedType.GetClassDef()->IsClass()) { return false; } // 'inheritedType' may be instantiated type extendDefName = typeMangle + "_ed_" + GetTypeQualifiedName(inheritedType); auto content = GetEmptyExtensionDefContent(cgMod, *targetType); auto [iFnOrTi, isTi] = GenerateInterfaceFn(inheritedType); content[static_cast(INTERFACE_FN_OR_INTERFACE_TI)] = iFnOrTi; content[static_cast(IS_INTERFACE_TI)] = llvm::ConstantInt::get(llvm::Type::getInt8Ty(cgCtx.GetLLVMContext()), static_cast(isTi)); content[static_cast(WHERE_CONDITION_FN)] = GenerateWhereConditionFn(); content[static_cast(FUNC_TABLE)] = GenerateFuncTableForType(funcTableSize == 0 ? std::vector() : found->second); return CreateExtensionDefForType(cgMod, extendDefName, content, inheritedType, isForExternalType); } namespace { bool HasStaticMethods(const CHIR::CustomTypeDef& chirDef) { for (auto method : chirDef.GetMethods()) { if (method->TestAttr(Cangjie::CHIR::Attribute::STATIC)) { return true; } } return false; } void GetOrderedParentTypesRecusively( CHIR::ClassType& type, std::list& parents, CHIR::CHIRBuilder& builder) { if (auto superClass = type.GetSuperClassTy(&builder)) { parents.emplace_front(superClass); GetOrderedParentTypesRecusively(*superClass, parents, builder); } auto classDef = type.GetClassDef(); auto [res, replaceTable] = classDef->GetType()->CalculateGenericTyMapping(type); CJC_ASSERT(res); for (auto ty : classDef->GetImplementedInterfaceTys()) { auto instType = StaticCast(ReplaceRawGenericArgType(*ty, replaceTable, builder)); if (std::find(parents.begin(), parents.end(), instType) != parents.end()) { continue; } parents.emplace_back(instType); GetOrderedParentTypesRecusively(*instType, parents, builder); } } /** @brief the order is {grandparent class, parent class, sub class ... , interface1, interface2, ... interfaceN} * all classes are front of all interfaces, and parent class must be front of sub class * but we don't care about the order of interfaces */ void GetOrderedParentTypesRecusively( const CHIR::CustomTypeDef& def, std::list& parents, CHIR::CHIRBuilder& builder) { if (auto classDef = DynamicCast(&def)) { if (auto superClass = classDef->GetSuperClassTy()) { parents.emplace_front(superClass); GetOrderedParentTypesRecusively(*superClass, parents, builder); } } for (auto interface : def.GetImplementedInterfaceTys()) { if (std::find(parents.begin(), parents.end(), interface) != parents.end()) { continue; } parents.emplace_back(interface); if (def.TestAttr(CHIR::Attribute::COMPILER_ADD) && def.IsExtend()) { continue; } GetOrderedParentTypesRecusively(*interface, parents, builder); } } } // namespace std::pair>> CGExtensionDef::Emit() { uint32_t extensionDefsNum = 0U; if (!chirDef.TestAttr(CHIR::Attribute::IMPORTED) && !chirDef.TestAttr(CHIR::Attribute::GENERIC_INSTANTIATED)) { CollectGenericParamIndicesMap(); std::list orderedInheritedTypes; if (chirDef.GetCustomKind() == CHIR::CustomDefKind::TYPE_CLASS) { auto& def = StaticCast(chirDef); auto isClassOrHasStaticMethods = def.IsClass() || HasStaticMethods(chirDef); if (isClassOrHasStaticMethods) { orderedInheritedTypes.push_back(StaticCast(targetType)); } } CHIR::CHIRBuilder& chirBuilder = cgMod.GetCGContext().GetCHIRBuilder(); GetOrderedParentTypesRecusively(chirDef, orderedInheritedTypes, chirBuilder); for (auto iTy : orderedInheritedTypes) { if (iTy->IsAny()) { continue; } if (CreateExtensionDefForType(*iTy)) { ++extensionDefsNum; if (iTy->GetClassDef()->IsInterface()) { extendInterfaces.emplace_back(iTy, chirDef.IsExtend() ? targetType : nullptr); } } } } for (auto extend : chirDef.GetExtends()) { auto rst = CGExtensionDef(cgMod, *extend).Emit(); extensionDefsNum += rst.first; auto tt = rst.second; extendInterfaces.insert(extendInterfaces.end(), tt.begin(), tt.end()); } if (!isForExternalType && extensionDefsNum != 0) { auto totalExtensionDefsNum = cgMod.GetNonExternalExtensionDefsNum(); CJC_ASSERT(totalExtensionDefsNum >= extensionDefsNum); startIdxOfNonExternalExtensionDef = totalExtensionDefsNum - extensionDefsNum; auto targetDefType = targetType->IsNominal() ? static_cast(targetType)->GetCustomTypeDef()->GetType() : targetType; CGType::GetOrCreate(cgMod, targetDefType)->SetCGExtensionDef(this); } return {extensionDefsNum, extendInterfaces}; } cangjie_compiler-1.0.7/src/CodeGen/CJNative/CGTypes/CJNativeClassType.cpp000066400000000000000000000137101510705540100260640ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/CGTypes/CGClassType.h" #include "CGContext.h" #include "CGModule.h" #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "CJNative/CGTypeInfo.h" #endif #include "Utils/CGUtils.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Type/ClassDef.h" namespace Cangjie::CodeGen { CGClassType::CGClassType(CGModule& cgMod, CGContext& cgCtx, const CHIR::ClassType& chirType) : CGCustomType(cgMod, cgCtx, chirType, CGTypeKind::CG_CLASS), numOfAllFields(chirType.GetClassDef()->GetAllInstanceVarNum()) { } llvm::Type* CGClassType::GenLLVMType() { auto& llvmCtx = cgCtx.GetLLVMContext(); llvmType = llvm::Type::getInt8Ty(llvmCtx); if (!IsSized()) { return llvmType; } auto& classType = StaticCast(chirType); std::string layoutName{}; if (auto defType = classType.GetClassDef()->GetType(); &chirType != defType && CGType::GetOrCreate(cgMod, defType)->GetSize()) { layoutName = GetClassObjLayoutName(GetTypeQualifiedName(*defType)); } else { layoutName = GetClassObjLayoutName(GetTypeQualifiedName(classType)); } layoutType = llvm::StructType::getTypeByName(llvmCtx, layoutName); if (layoutType && cgCtx.IsGeneratedStructType(layoutName)) { return llvmType; } else if (!layoutType) { layoutType = llvm::StructType::create(llvmCtx, layoutName); } cgCtx.AddGeneratedStructType(layoutName); std::vector bodyVec{}; if (classType.IsAutoEnv()) { (void)bodyVec.emplace_back(llvm::Type::getInt8PtrTy(llvmCtx)); (void)bodyVec.emplace_back(llvm::Type::getInt8PtrTy(llvmCtx)); } auto& nonConstClassType = const_cast(classType); for (auto fieldType : nonConstClassType.GetInstantiatedMemberTys(cgMod.GetCGContext().GetCHIRBuilder())) { (void)bodyVec.emplace_back(CGType::GetOrCreate(cgMod, fieldType)->GetLLVMType()); } SetStructTypeBody(layoutType, bodyVec); return llvmType; } void CGClassType::GenContainedCGTypes() { auto& classType = StaticCast(chirType); if (classType.IsAutoEnv()) { (void)containedCGTypes.emplace_back(CGType::GetInt8PtrCGType(cgMod)); (void)containedCGTypes.emplace_back(CGType::GetInt8PtrCGType(cgMod)); } auto& nonConstClassType = const_cast(classType); for (auto& fieldType : nonConstClassType.GetInstantiatedMemberTys(cgMod.GetCGContext().GetCHIRBuilder())) { (void)containedCGTypes.emplace_back(CGType::GetOrCreate(cgMod, fieldType)); } } llvm::Constant* CGClassType::GenSuperOfTypeInfo() { auto& classType = StaticCast(chirType); auto& nonConstClassType = const_cast(classType); if (auto superType = nonConstClassType.GetSuperClassTy(&cgMod.GetCGContext().GetCHIRBuilder())) { return CGType::GetOrCreate(cgMod, superType)->GetOrCreateTypeInfo(); } else { return llvm::ConstantPointerNull::get(CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext())); } } llvm::Constant* CGClassType::GenSourceGenericOfTypeInfo() { // union { // const TypeTemplate* generic_from; // void* finalizer; // } auto& classType = StaticCast(chirType); if (auto finalizer = classType.GetClassDef()->GetFinalizer(); finalizer && !IsStaticGI()) { CJC_ASSERT(classType.GetTypeArgs().empty()); auto p0i8 = llvm::Type::getInt8PtrTy(cgCtx.GetLLVMContext()); return llvm::ConstantExpr::getBitCast(cgMod.GetOrInsertCGFunction(finalizer)->GetRawFunction(), p0i8); } return CGType::GenSourceGenericOfTypeInfo(); } llvm::Constant* CGClassType::GenSuperFnOfTypeTemplate() { auto& classType = static_cast(chirType); auto& nonConstClassType = const_cast(classType); auto superType = nonConstClassType.GetSuperClassTy(&cgMod.GetCGContext().GetCHIRBuilder()); if (!classType.GetClassDef()->IsClass() || !superType) { return llvm::ConstantPointerNull::get(llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext())); } auto funcName = CGType::GetNameOfTypeTemplateGV(chirType) + ".superTiFn"; return CGTypeInfo::GenSuperFnOfTypeTemplate( cgMod, funcName, *superType, classType.GetClassDef()->GetGenericTypeParams()); } llvm::Constant* CGClassType::GenFinalizerOfTypeTemplate() { auto& classType = static_cast(chirType); auto p0i8 = llvm::Type::getInt8PtrTy(cgCtx.GetLLVMContext()); if (auto finalizer = classType.GetClassDef()->GetFinalizer()) { return llvm::ConstantExpr::getBitCast(cgMod.GetOrInsertCGFunction(finalizer)->GetRawFunction(), p0i8); } else { return llvm::ConstantPointerNull::get(p0i8); } } void CGClassType::CalculateSizeAndAlign() { if (layoutType) { auto layOut = cgMod.GetLLVMModule()->getDataLayout(); size = layOut.getTypeAllocSize(layoutType); align = layOut.getABITypeAlignment(layoutType); } } void CGClassType::PreActionOfGenTypeInfo() { auto& classDef = *static_cast(chirType).GetClassDef(); if (IsCoreFutureClass(classDef) || IsSyncRelatedClass(classDef)) { typeInfo->addAttribute(classDef.GetSrcCodeIdentifier()); } if (classDef.GetFinalizer()) { typeInfo->addAttribute(GC_FINALIZER_ATTR); } } void CGClassType::PreActionOfGenTypeTemplate() { auto& classDef = *static_cast(chirType).GetClassDef(); if (IsCoreFutureClass(classDef)) { typeTemplate->addAttribute(classDef.GetSrcCodeIdentifier()); } if (classDef.GetFinalizer()) { typeTemplate->addAttribute(GC_FINALIZER_ATTR); } } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/CJNative/CGTypes/EnumCtorTIOrTTGenerator.cpp000066400000000000000000000313021510705540100271770ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "CJNative/CGTypes/EnumCtorTIOrTTGenerator.h" #include "Base/CGTypes/CGEnumType.h" #include "CGModule.h" #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "CJNative/CGTypeInfo.h" #endif #include "IRBuilder.h" #include "cangjie/CHIR/Type/EnumDef.h" namespace Cangjie::CodeGen { EnumCtorTIOrTTGenerator::EnumCtorTIOrTTGenerator( CGModule& cgMod, const CHIR::EnumType& chirEnumType, std::size_t ctorIndex) : cgMod(cgMod), cgCtx(cgMod.GetCGContext()), chirEnumType(chirEnumType), ctorIndex(ctorIndex) { } llvm::Constant* EnumCtorTIOrTTGenerator::GenTypeArgsNumOfTypeInfo() { return llvm::ConstantInt::get(llvm::Type::getInt8Ty(cgMod.GetLLVMContext()), chirEnumType.GetGenericArgs().size()); } llvm::Constant* EnumCtorTIOrTTGenerator::GenTypeArgsOfTypeInfo() { auto& llvmCtx = cgMod.GetLLVMContext(); auto i8Ptr = llvm::Type::getInt8PtrTy(llvmCtx); auto typeArgsNum = chirEnumType.GetGenericArgs().size(); if (!typeArgsNum) { return llvm::ConstantPointerNull::get(i8Ptr); } auto typeInfoPtrTy = CGType::GetOrCreateTypeInfoPtrType(llvmCtx); auto typeOfGenericArgsGV = llvm::ArrayType::get(typeInfoPtrTy, typeArgsNum); auto typeInfoOfGenericArgs = llvm::cast(cgMod.GetLLVMModule()->getOrInsertGlobal( CGType::GetNameOfTypeInfoGV(chirEnumType) + ".typeArgs", typeOfGenericArgsGV)); typeInfoOfGenericArgs->addAttribute(CJTI_TYPE_ARGS_ATTR); AddLinkageTypeMetadata(*typeInfoOfGenericArgs, llvm::GlobalVariable::PrivateLinkage, cgCtx.IsCGParallelEnabled()); return llvm::ConstantExpr::getBitCast(typeInfoOfGenericArgs, llvm::Type::getInt8PtrTy(llvmCtx)); } llvm::Constant* EnumCtorTIOrTTGenerator::GenSourceGenericOfTypeInfo() { auto p0i8 = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); if (chirEnumType.GetCustomTypeDef()->IsGenericDef()) { auto genericDecl = chirEnumType.GetCustomTypeDef(); return llvm::ConstantExpr::getBitCast( cgMod.GetOrCreateEnumCtorTIOrTT(*StaticCast(genericDecl->GetType()), ctorIndex), p0i8); } else { return llvm::ConstantPointerNull::get(p0i8); } } void EnumCtorTIOrTTGenerator::GenerateNonGenericEnumCtorTypeInfo(llvm::GlobalVariable& ti) { auto& llvmCtx = cgMod.GetLLVMContext(); auto i8Ty = llvm::Type::getInt8Ty(llvmCtx); auto i16Ty = llvm::Type::getInt16Ty(llvmCtx); auto i32Ty = llvm::Type::getInt32Ty(llvmCtx); auto i8PtrNull = llvm::ConstantPointerNull::get(i8Ty->getPointerTo()); auto tiName = ti.getName().str(); auto className = tiName.substr(0, tiName.size() - 3); // 3: the length of ".ti" or ".tt" const auto& ctors = chirEnumType.GetConstructorInfos(cgMod.GetCGContext().GetCHIRBuilder()); CJC_ASSERT(ctorIndex < ctors.size()); const auto& paramTypes = ctors[ctorIndex].funcType->GetParamTypes(); std::vector fieldsTypes; StaticCast(CGType::GetOrCreate(cgMod, &chirEnumType))->IsOptionLike() ? fieldsTypes.emplace_back(cgMod.GetCGContext().GetCHIRBuilder().GetBoolTy()) : fieldsTypes.emplace_back(cgMod.GetCGContext().GetCHIRBuilder().GetInt32Ty()); fieldsTypes.insert(fieldsTypes.end(), paramTypes.cbegin(), paramTypes.cend()); std::vector typeInfoVec(TYPE_INFO_FIELDS_NUM); typeInfoVec[static_cast(TYPEINFO_NAME)] = cgMod.GenerateTypeNameConstantString(className, false); typeInfoVec[static_cast(TYPEINFO_TYPE_KIND)] = llvm::ConstantInt::get(i8Ty, UGTypeKind::UG_COMMON_ENUM); typeInfoVec[static_cast(TYPEINFO_FIELDS_NUM)] = llvm::ConstantInt::get(i16Ty, fieldsTypes.size()); auto fieldConstants = CGCustomType::GenTypeInfoConstantVectorForTypes(cgMod, fieldsTypes); typeInfoVec[static_cast(TYPEINFO_FIELDS)] = CGCustomType::GenTypeInfoArray(cgMod, tiName + ".fields", fieldConstants); auto layoutType = GetLLVMStructType(cgMod, fieldsTypes, GetClassObjLayoutName(className)); typeInfoVec[static_cast(TYPEINFO_SIZE)] = llvm::ConstantInt::get(i32Ty, 8U); typeInfoVec[static_cast(TYPEINFO_UUID)] = llvm::Constant::getNullValue(i32Ty); typeInfoVec[static_cast(TYPEINFO_ALIGN)] = llvm::ConstantInt::get(i8Ty, 8U); typeInfoVec[static_cast(TYPEINFO_SOURCE_GENERIC)] = GenSourceGenericOfTypeInfo(); typeInfoVec[static_cast(TYPEINFO_TYPE_ARGS_NUM)] = GenTypeArgsNumOfTypeInfo(); typeInfoVec[static_cast(TYPEINFO_INHERITED_CLASS_NUM)] = llvm::ConstantInt::get(i16Ty, INHERITED_CLASS_NUM_FE_FLAG); typeInfoVec[static_cast(TYPEINFO_OFFSETS)] = CGCustomType::GenOffsetsArray(cgMod, tiName + ".offsets", layoutType); typeInfoVec[static_cast(TYPEINFO_TYPE_ARGS)] = GenTypeArgsOfTypeInfo(); typeInfoVec[static_cast(TYPEINFO_SUPER)] = CGType::GetOrCreate(cgMod, &chirEnumType)->GetOrCreateTypeInfo(); typeInfoVec[static_cast(TYPEINFO_EXTENSIONDEF_PTR)] = llvm::ConstantPointerNull::get(CGType::GetOrCreateExtensionDefPtrType(llvmCtx)->getPointerTo()); typeInfoVec[static_cast(TYPEINFO_MTABLE)] = i8PtrNull; typeInfoVec[static_cast(TYPEINFO_REFLECTION)] = i8PtrNull; typeInfoVec[static_cast(TYPEINFO_GC_TIB)] = llvm::ConstantPointerNull::get(CGType::GetBitMapType(llvmCtx)->getPointerTo()); typeInfoVec[static_cast(TYPEINFO_FLAG)] = llvm::ConstantInt::get(llvm::Type::getInt8Ty(cgMod.GetLLVMContext()), 0); ti.setInitializer(llvm::ConstantStruct::get(CGType::GetOrCreateTypeInfoType(llvmCtx), typeInfoVec)); ti.addAttribute(GC_KLASS_ATTR); auto meta = llvm::MDTuple::get(llvmCtx, {llvm::MDString::get(llvmCtx, layoutType->getStructName().str())}); ti.setMetadata(GC_TYPE_META_NAME, meta); // This line seems only for Parallel-Compilation: cgCtx.AddGeneratedStructType(layoutType->getStructName().str()); cgCtx.RegisterStaticGIName(ti.getName()); } llvm::Constant* EnumCtorTIOrTTGenerator::GenTypeArgsNumOfTypeTemplate() { return llvm::ConstantInt::get(llvm::Type::getInt16Ty(cgMod.GetLLVMContext()), chirEnumType.GetGenericArgs().size()); } llvm::Constant* EnumCtorTIOrTTGenerator::GenSuperFnOfTypeTemplate(const std::string& funcName) { auto i8PtrTy = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); std::vector argTypes{ llvm::Type::getInt32Ty(cgMod.GetLLVMContext()), CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext())->getPointerTo() }; auto superTiFnType = llvm::FunctionType::get(i8PtrTy, argTypes, false); auto superTiFn = llvm::Function::Create(superTiFnType, llvm::Function::PrivateLinkage, funcName, cgMod.GetLLVMModule()); superTiFn->addFnAttr("native-interface-fn"); CodeGen::IRBuilder2 irBuilder(cgMod); auto entryBB = irBuilder.CreateEntryBasicBlock(superTiFn, "entry"); irBuilder.SetInsertPoint(entryBB); auto tt = CGType::GetOrCreate(cgMod, &chirEnumType)->GetOrCreateTypeTemplate(); auto ti = irBuilder.CallIntrinsicGetTypeInfo({tt, superTiFn->getArg(0), superTiFn->getArg(1)}); irBuilder.CreateRet(irBuilder.CreateBitCast(ti, i8PtrTy)); return llvm::ConstantExpr::getBitCast(superTiFn, i8PtrTy); } void EnumCtorTIOrTTGenerator::GenerateGenericEnumCtorTypeTemplate(llvm::GlobalVariable& tt) { auto& llvmCtx = cgMod.GetLLVMContext(); auto i8Ty = llvm::Type::getInt8Ty(llvmCtx); auto i8PtrTy = llvm::Type::getInt8PtrTy(llvmCtx); auto i16Ty = llvm::Type::getInt16Ty(llvmCtx); auto i8PtrNull = llvm::ConstantPointerNull::get(i8PtrTy); auto ttName = tt.getName().str(); auto className = ttName.substr(0, ttName.size() - 3); // 3: the length of ".ti" or ".tt" const auto& ctors = chirEnumType.GetConstructorInfos(cgMod.GetCGContext().GetCHIRBuilder()); CJC_ASSERT(ctorIndex < ctors.size()); const auto& paramTypes = ctors[ctorIndex].funcType->GetParamTypes(); std::vector fieldsTypes; StaticCast(CGType::GetOrCreate(cgMod, &chirEnumType))->IsOptionLike() ? fieldsTypes.emplace_back(cgMod.GetCGContext().GetCHIRBuilder().GetBoolTy()) : fieldsTypes.emplace_back(cgMod.GetCGContext().GetCHIRBuilder().GetInt32Ty()); fieldsTypes.insert(fieldsTypes.end(), paramTypes.cbegin(), paramTypes.cend()); std::vector typeTemplateVec(TYPE_TEMPLATE_FIELDS_NUM); typeTemplateVec[static_cast(TYPETEMPLATE_NAME)] = cgMod.GenerateTypeNameConstantString(className, false); typeTemplateVec[static_cast(TYPETEMPLATE_TYPE_KIND)] = llvm::ConstantInt::get(i8Ty, UGTypeKind::UG_COMMON_ENUM); typeTemplateVec[static_cast(TYPETEMPLATE_TYPE_ARGS_NUM)] = GenTypeArgsNumOfTypeTemplate(); typeTemplateVec[static_cast(TYPETEMPLATE_FIELDS_NUM)] = llvm::ConstantInt::get(i16Ty, fieldsTypes.size()); std::unordered_map localGenericParamIndicesMap; std::size_t genericTypeIdx = 0; for (auto typeArg : GetGenericArgsFromCHIRType(chirEnumType)) { auto genericType = typeArg.GetGenericType(); localGenericParamIndicesMap.emplace(genericType, genericTypeIdx++); } typeTemplateVec[static_cast(TYPETEMPLATE_FIELDS_FNS)] = CGTypeInfo::GenFieldsFnsOfTypeTemplate(cgMod, ttName, fieldsTypes, localGenericParamIndicesMap); typeTemplateVec[static_cast(TYPETEMPLATE_SUPER_FN)] = GenSuperFnOfTypeTemplate(ttName + ".superTiFn"); typeTemplateVec[static_cast(TYPETEMPLATE_FINALIZER)] = i8PtrNull; typeTemplateVec[static_cast(TYPETEMPLATE_REFLECTION)] = llvm::ConstantPointerNull::get(i8PtrTy); typeTemplateVec[static_cast(TYPETEMPLATE_FLAG)] = llvm::ConstantInt::get(i8Ty, 0); typeTemplateVec[static_cast(TYPETEMPLATE_EXTENSIONDEF_PTR)] = llvm::ConstantPointerNull::get(CGType::GetOrCreateExtensionDefPtrType(llvmCtx)->getPointerTo()); typeTemplateVec[static_cast(TYPETEMPLATE_INHERITED_CLASS_NUM)] = llvm::ConstantInt::get(i16Ty, 0U); tt.setInitializer(llvm::ConstantStruct::get(CGType::GetOrCreateTypeTemplateType(llvmCtx), typeTemplateVec)); tt.addAttribute(TYPE_TEMPLATE_ATTR); } void EnumCtorTIOrTTGenerator::EmitForDynamicGI() { auto enumDef = chirEnumType.GetCustomTypeDef(); auto tt = cgMod.GetOrCreateEnumCtorTIOrTT(chirEnumType, ctorIndex); if (tt->hasInitializer() || enumDef->TestAttr(CHIR::Attribute::IMPORTED) || enumDef->TestAttr(CHIR::Attribute::NON_RECOMPILE)) { return; } const auto& subCHIRPkg = cgCtx.GetSubCHIRPackage(); bool definedInOtherLLVMModule = subCHIRPkg.chirCustomDefs.find(enumDef) == subCHIRPkg.chirCustomDefs.end(); if (definedInOtherLLVMModule) { return; } GenerateGenericEnumCtorTypeTemplate(*tt); auto linkageType = CHIRLinkage2LLVMLinkage(enumDef->Get()); if (linkageType == llvm::GlobalValue::InternalLinkage) { linkageType = llvm::GlobalValue::PrivateLinkage; } AddLinkageTypeMetadata(*tt, linkageType, cgCtx.IsCGParallelEnabled()); } void EnumCtorTIOrTTGenerator::EmitForStaticGI() { auto ti = cgMod.GetOrCreateEnumCtorTIOrTT(chirEnumType, ctorIndex); if (ti->hasInitializer()) { return; } GenerateNonGenericEnumCtorTypeInfo(*ti); AddLinkageTypeMetadata(*ti, llvm::GlobalValue::PrivateLinkage, false); } void EnumCtorTIOrTTGenerator::EmitForConcrete() { auto enumDef = chirEnumType.GetCustomTypeDef(); auto ti = cgMod.GetOrCreateEnumCtorTIOrTT(chirEnumType, ctorIndex); if (ti->hasInitializer() || enumDef->TestAttr(CHIR::Attribute::IMPORTED) || enumDef->TestAttr(CHIR::Attribute::NON_RECOMPILE)) { return; } const auto& subCHIRPkg = cgCtx.GetSubCHIRPackage(); bool definedInOtherLLVMModule = subCHIRPkg.chirCustomDefs.find(enumDef) == subCHIRPkg.chirCustomDefs.end(); if (definedInOtherLLVMModule) { return; } GenerateNonGenericEnumCtorTypeInfo(*ti); auto linkageType = CHIRLinkage2LLVMLinkage(enumDef->Get()); if (linkageType == llvm::GlobalValue::InternalLinkage) { linkageType = llvm::GlobalValue::PrivateLinkage; } AddLinkageTypeMetadata(*ti, linkageType, cgCtx.IsCGParallelEnabled()); } void EnumCtorTIOrTTGenerator::Emit() { auto cgEnumType = CGType::GetOrCreate(cgMod, &chirEnumType); if (cgEnumType->IsDynamicGI()) { EmitForDynamicGI(); } else if (cgEnumType->IsStaticGI()) { EmitForStaticGI(); } else if (cgEnumType->IsConcrete()) { EmitForConcrete(); } else { CJC_ASSERT(false && "shouldn't reach here."); } } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/CJNative/CGTypes/EnumCtorTIOrTTGenerator.h000066400000000000000000000030031510705540100266410ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the class for determine the memory layout of Class/Interface. */ #ifndef CANGJIE_CGENUMTYPELAYOUT_H #define CANGJIE_CGENUMTYPELAYOUT_H #include "Base/CGTypes/CGType.h" #include "Utils/CGCommonDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" namespace Cangjie { namespace CodeGen { class EnumCtorTIOrTTGenerator { public: explicit EnumCtorTIOrTTGenerator(CGModule& cgMod, const CHIR::EnumType& chirEnumType, std::size_t ctorIndex); void Emit(); private: void EmitForDynamicGI(); void EmitForStaticGI(); void EmitForConcrete(); void GenerateNonGenericEnumCtorTypeInfo(llvm::GlobalVariable& ti); llvm::Constant* GenTypeArgsNumOfTypeInfo(); llvm::Constant* GenTypeArgsOfTypeInfo(); llvm::Constant* GenSourceGenericOfTypeInfo(); void GenerateGenericEnumCtorTypeTemplate(llvm::GlobalVariable& tt); llvm::Constant* GenTypeArgsNumOfTypeTemplate(); llvm::Constant* GenSuperFnOfTypeTemplate(const std::string& funcName); private: CGModule& cgMod; CGContext& cgCtx; const CHIR::EnumType& chirEnumType; std::size_t ctorIndex; }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGENUMTYPELAYOUT_H cangjie_compiler-1.0.7/src/CodeGen/CJNative/CHIRSplitter.cpp000066400000000000000000000512621510705540100235330ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * Split CHIRPackage to SubCHIRPackage. */ #include "CJNative/CHIRSplitter.h" #include #include "llvm/IR/Module.h" #include "llvm/IRReader/IRReader.h" #include "llvm/Support/SourceMgr.h" #include "Base/CGTypes/CGEnumType.h" #include "CGPkgContext.h" #include "Utils/CGUtils.h" #include "cangjie/Basic/StringConvertor.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" #include "cangjie/Utils/ProfileRecorder.h" namespace Cangjie { namespace CodeGen { namespace { std::size_t GetSubCHIRPackageIdx(std::size_t splitNum) { static std::size_t counter = 0; return counter++ % splitNum; } } // namespace bool ChirTypeDefCmp::operator()(const CHIR::CustomTypeDef* lhs, const CHIR::CustomTypeDef* rhs) const { return lhs->GetIdentifierWithoutPrefix() < rhs->GetIdentifierWithoutPrefix(); } bool ChirValueCmp::operator()(const CHIR::Value* lhs, const CHIR::Value* rhs) const { return lhs->GetIdentifierWithoutPrefix() < rhs->GetIdentifierWithoutPrefix(); } SubCHIRPackage::SubCHIRPackage(std::size_t splitNum) : mainModule(false), subCHIRPackageIdx(GetSubCHIRPackageIdx(splitNum)), exprNumInChirFuncs(0), splitNum(splitNum) { } void SubCHIRPackage::Clear() { chirCustomDefs.clear(); chirGVs.clear(); chirFuncs.clear(); chirForeigns.clear(); chirImportedCFuncs.clear(); } CHIRSplitter::CHIRSplitter(const CGPkgContext& cgPkgCtx) : cgPkgCtx(cgPkgCtx), splitNum(0), index(0), subCHIRPackagesCache() { } void CHIRSplitter::CalcSplitsNum() { if (subCHIRPackagesCache.splitNum.has_value()) { splitNum = subCHIRPackagesCache.splitNum.value(); return; } auto& options = cgPkgCtx.GetGlobalOptions(); auto& chirPkg = cgPkgCtx.GetCHIRPackage(); splitNum = options.aggressiveParallelCompile.value_or(1); if (splitNum > chirPkg.GetGlobalFuncs().size()) { splitNum = chirPkg.GetGlobalFuncs().size(); } } // Split chirPkg into splitNum SubCHIRPackages. // chirPkg: complete data of the packet transferred from CHIR // splitNum: parallelism/splitting degree std::vector CHIRSplitter::SplitCHIRPackage() { Utils::ProfileRecorder recorder("EmitIR", "SplitCHIRPackage"); LoadSubCHIRPackagesInfo(); CalcSplitsNum(); // Compute the degree of parallelism std::vector subCHIRPackages; subCHIRPackages.reserve(splitNum); for (std::size_t idx = 0; idx < splitNum; ++idx) { subCHIRPackages.emplace_back(SubCHIRPackage(splitNum)); } SplitCHIRFuncs(subCHIRPackages); SplitCHIRClasses(subCHIRPackages); SplitCHIREnums(subCHIRPackages); SplitCHIRStructs(subCHIRPackages); SplitCHIRExtends(subCHIRPackages); SplitCHIRGlobalVars(subCHIRPackages); SplitCHIRImportedCFuncs(subCHIRPackages); SaveSubCHIRPackagesInfo(); return subCHIRPackages; } namespace { struct SubCHIRPackageCmp { bool operator()(const SubCHIRPackage& lhs, const SubCHIRPackage& rhs) const { return lhs.exprNumInChirFuncs == rhs.exprNumInChirFuncs ? lhs.subCHIRPackageIdx < rhs.subCHIRPackageIdx : lhs.exprNumInChirFuncs < rhs.exprNumInChirFuncs; } }; auto FindTargetSubCHIRPackage(const CHIR::Value& chirValue, const std::set& subCHIRPackagesSet, const std::map& cache) { auto target = subCHIRPackagesSet.begin(); if (auto iterOfCache = cache.find(chirValue.GetIdentifierWithoutPrefix()); iterOfCache != cache.end()) { auto idxInCache = iterOfCache->second; target = std::find_if(subCHIRPackagesSet.begin(), subCHIRPackagesSet.end(), [idxInCache](const SubCHIRPackage& sub) { return sub.subCHIRPackageIdx == idxInCache; }); } return target; }; void SplitSpecialFuncs(CHIR::Func& globalInitFunc, CHIR::Func& globalInitLiteralFunc, const std::vector& toAnyFuncs, std::set& subCHIRPackagesSet, std::map& cache) { auto target = FindTargetSubCHIRPackage(globalInitFunc, subCHIRPackagesSet, cache); auto targetSubCHIRPackage = subCHIRPackagesSet.extract(target); auto& subCHIRPackage = targetSubCHIRPackage.value(); subCHIRPackage.mainModule = true; subCHIRPackage.chirFuncs.emplace(&globalInitFunc); subCHIRPackage.chirFuncs.emplace(&globalInitLiteralFunc); subCHIRPackage.exprNumInChirFuncs += globalInitFunc.GetExpressionsNum() + globalInitLiteralFunc.GetExpressionsNum(); cache.emplace(globalInitFunc.GetIdentifierWithoutPrefix(), subCHIRPackage.subCHIRPackageIdx); cache.emplace(globalInitLiteralFunc.GetIdentifierWithoutPrefix(), subCHIRPackage.subCHIRPackageIdx); for (auto toAny : toAnyFuncs) { subCHIRPackage.chirFuncs.emplace(toAny); subCHIRPackage.exprNumInChirFuncs += toAny->GetExpressionsNum(); cache.emplace(toAny->GetIdentifierWithoutPrefix(), subCHIRPackage.subCHIRPackageIdx); } subCHIRPackagesSet.insert(std::move(targetSubCHIRPackage)); } void SplitNormalFunc(const std::multimap& sortedChirFuncs, std::set& subCHIRPackagesSet, std::map& cache) { for (auto iter = sortedChirFuncs.rbegin(); iter != sortedChirFuncs.rend(); iter++) { auto func = iter->second; auto target = FindTargetSubCHIRPackage(*func, subCHIRPackagesSet, cache); auto targetSubCHIRPackage = subCHIRPackagesSet.extract(target); auto& subCHIRPackage = targetSubCHIRPackage.value(); subCHIRPackage.chirFuncs.emplace(func); subCHIRPackage.exprNumInChirFuncs += iter->first; cache.emplace(func->GetIdentifierWithoutPrefix(), subCHIRPackage.subCHIRPackageIdx); subCHIRPackagesSet.insert(std::move(targetSubCHIRPackage)); } } void SplitForeign(const CHIR::Package& chirPkg, std::set& subCHIRPackagesSet, std::map& cache) { for (auto importedValue : chirPkg.GetImportedVarAndFuncs()) { if (!importedValue->TestAttr(CHIR::Attribute::FOREIGN) || importedValue->GetSourcePackageName() != chirPkg.GetName()) { continue; } auto foreign = DynamicCast(importedValue); CJC_NULLPTR_CHECK(foreign); auto target = FindTargetSubCHIRPackage(*foreign, subCHIRPackagesSet, cache); auto targetSubCHIRPackage = subCHIRPackagesSet.extract(target); auto& subCHIRPackage = targetSubCHIRPackage.value(); subCHIRPackage.chirForeigns.emplace(foreign); subCHIRPackage.exprNumInChirFuncs += 1; cache.emplace(foreign->GetIdentifierWithoutPrefix(), subCHIRPackage.subCHIRPackageIdx); subCHIRPackagesSet.insert(std::move(targetSubCHIRPackage)); } } }; // namespace // Split chirPkg.GetGlobalFuncs evenly into splitNum subCHIRPackages, // so that the total number of expressions of all functions in each subCHIRPackage is close to. void CHIRSplitter::SplitCHIRFuncs(std::vector& subCHIRPackages) { std::set subCHIRPackagesSet; for (auto subCHIRPackage : subCHIRPackages) { subCHIRPackagesSet.emplace(subCHIRPackage); } auto& chirPkg = cgPkgCtx.GetCHIRPackage(); // 1. Sorts chirFuncs based on the number of expressions in a chirFunc. auto globalInitFunc = chirPkg.GetPackageInitFunc(); std::string targetSuffix = SPECIAL_NAME_FOR_INIT_LITERAL_FUNCTION + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX; std::string globalInitFuncName = globalInitFunc->GetIdentifierWithoutPrefix(); // init func must have suffix iiHv, index 4 is the start of ii. 2 is the length of il. auto globalInitLiteralFunc = VirtualCast(const_cast(cgPkgCtx).FindCHIRGlobalValue( globalInitFuncName.replace(globalInitFuncName.size() - 4, 2, "il"))); std::vector toAnyFuncs{}; std::multimap sortedChirFuncs{}; for (auto chirFunc : chirPkg.GetGlobalFuncs()) { if (chirPkg.GetName() == REFLECT_PACKAGE_NAME && chirFunc->GetSrcCodeIdentifier() == "toAny") { toAnyFuncs.emplace_back(chirFunc); } else if (chirFunc != globalInitFunc && chirFunc != globalInitLiteralFunc) { sortedChirFuncs.emplace(chirFunc->GetExpressionsNum(), chirFunc); } } // 2. Add the chirFunc with the most expressions to the subCHIRPackage with the least exprNumInChirFuncs. SplitSpecialFuncs( *globalInitFunc, *globalInitLiteralFunc, toAnyFuncs, subCHIRPackagesSet, subCHIRPackagesCache.funcsCache); SplitNormalFunc(sortedChirFuncs, subCHIRPackagesSet, subCHIRPackagesCache.funcsCache); SplitForeign(chirPkg, subCHIRPackagesSet, subCHIRPackagesCache.foreignsCache); for (auto subCHIRPackage : subCHIRPackagesSet) { subCHIRPackages[subCHIRPackage.subCHIRPackageIdx] = subCHIRPackage; } } unsigned long CHIRSplitter::FindIdxInCache(const std::string& key) { auto iter = subCHIRPackagesCache.classesCache.find(key); if (iter != subCHIRPackagesCache.classesCache.cend()) { return iter->second; } iter = subCHIRPackagesCache.structsCache.find(key); if (iter != subCHIRPackagesCache.structsCache.cend()) { return iter->second; } iter = subCHIRPackagesCache.enumsCache.find(key); if (iter != subCHIRPackagesCache.enumsCache.cend()) { return iter->second; } return index++ % splitNum; } void CHIRSplitter::SplitCHIRClasses(std::vector& subCHIRPackages) { for (auto chirClass : cgPkgCtx.GetCHIRPackage().GetClasses()) { size_t idx = 0; if (cgPkgCtx.GetGlobalOptions().enIncrementalCompilation) { auto key = chirClass->GetPackageName() + ":" + chirClass->GetSrcCodeIdentifier(); idx = FindIdxInCache(key); subCHIRPackagesCache.classesCache.emplace(key, idx); } else { idx = index++ % splitNum; } subCHIRPackages[idx].chirCustomDefs.emplace(chirClass); } } void CHIRSplitter::SplitCHIREnums(std::vector& subCHIRPackages) { for (auto chirEnum : cgPkgCtx.GetCHIRPackage().GetEnums()) { size_t idx = 0; if (cgPkgCtx.GetGlobalOptions().enIncrementalCompilation) { auto key = chirEnum->GetPackageName() + ":" + chirEnum->GetSrcCodeIdentifier(); idx = FindIdxInCache(key); subCHIRPackagesCache.enumsCache.emplace(key, idx); } else { idx = index++ % splitNum; } subCHIRPackages[idx].chirCustomDefs.emplace(chirEnum); } for (auto chirEnum : cgPkgCtx.GetCHIRPackage().GetImportedEnums()) { size_t idx = 0; if (cgPkgCtx.GetGlobalOptions().enIncrementalCompilation) { auto key = chirEnum->GetPackageName() + ":" + chirEnum->GetSrcCodeIdentifier(); idx = FindIdxInCache(key); subCHIRPackagesCache.enumsCache.emplace(key, idx); } else { idx = index++ % splitNum; } subCHIRPackages[idx].chirCustomDefs.emplace(chirEnum); } } void CHIRSplitter::SplitCHIRStructs(std::vector& subCHIRPackages) { for (auto chirStruct : cgPkgCtx.GetCHIRPackage().GetStructs()) { size_t idx = 0; if (cgPkgCtx.GetGlobalOptions().enIncrementalCompilation) { auto key = chirStruct->GetPackageName() + ":" + chirStruct->GetSrcCodeIdentifier(); idx = FindIdxInCache(key); subCHIRPackagesCache.structsCache.emplace(key, idx); } else { idx = index++ % splitNum; } subCHIRPackages[idx].chirCustomDefs.emplace(chirStruct); } } void CHIRSplitter::SplitCHIRExtends(std::vector& subCHIRPackages) { for (auto chirExtendDefs : cgPkgCtx.GetCHIRPackage().GetExtends()) { auto key = chirExtendDefs->GetIdentifierWithoutPrefix(); auto iter = subCHIRPackagesCache.extendDefCache.find(key); auto idx = iter == subCHIRPackagesCache.extendDefCache.end() ? (index++ % splitNum) : iter->second; subCHIRPackages[idx].chirCustomDefs.emplace(chirExtendDefs); subCHIRPackagesCache.extendDefCache.emplace(key, idx); } } void CHIRSplitter::SplitCHIRGlobalVars(std::vector& subCHIRPackages) { for (auto chirGV : cgPkgCtx.GetCHIRPackage().GetGlobalVars()) { auto key = chirGV->GetIdentifierWithoutPrefix(); auto iter = subCHIRPackagesCache.gvsCache.find(key); auto idx = iter == subCHIRPackagesCache.gvsCache.cend() ? (index++ % splitNum) : iter->second; subCHIRPackages[idx].chirGVs.emplace(chirGV); subCHIRPackagesCache.gvsCache.emplace(key, idx); } } void CHIRSplitter::SplitCHIRImportedCFuncs(std::vector& subCHIRPackages) { for (auto importedValue : cgPkgCtx.GetCHIRPackage().GetImportedVarAndFuncs()) { // We only process imported non-public CFunc here, // whether it's explicitly imported, implicitly imported, or a foreign function. if (importedValue->IsImportedVar() || !StaticCast(importedValue->GetType())->IsCFunc() || (!importedValue->TestAttr(CHIR::Attribute::PRIVATE) && !importedValue->TestAttr(CHIR::Attribute::FOREIGN))) { continue; } auto importedCFunc = DynamicCast(importedValue); CJC_NULLPTR_CHECK(importedCFunc); auto iter = subCHIRPackagesCache.importedCFuncsCache.find(importedCFunc->GetIdentifierWithoutPrefix()); auto idx = iter == subCHIRPackagesCache.importedCFuncsCache.cend() ? (index++ % splitNum) : iter->second; subCHIRPackages[idx].chirImportedCFuncs.emplace(importedCFunc); subCHIRPackagesCache.importedCFuncsCache.emplace(importedCFunc->GetIdentifierWithoutPrefix(), idx); } } void CHIRSplitter::LoadSubCHIRPackagesInfo() { if (!cgPkgCtx.IsIncrementEnabled()) { return; } auto path = cgPkgCtx.GetGlobalOptions().GenerateCachedPathNameForCodeGen(cgPkgCtx.GetCurrentPkgName(), ".cgCache"); if (!FileUtil::FileExist(path)) { return; } #ifdef _WIN32 auto tempPath = StringConvertor::NormalizeStringToUTF8(path); CJC_ASSERT(tempPath.has_value() && "Incorrect file name encoding."); path = tempPath.value(); #endif llvm::SMDiagnostic err; auto context = llvm::LLVMContext(); auto module = llvm::parseIRFile(path, err, context); auto splitNumMD = module->getNamedMetadata("splitNum"); for (llvm::MDNode* mdNode : splitNumMD->operands()) { auto numMD = llvm::cast(mdNode->getOperand(0)); subCHIRPackagesCache.splitNum = llvm::cast(numMD->getValue())->getZExtValue(); } auto parseEntries = [](const llvm::MDNode& mdNode) -> std::tuple { auto nameMD = llvm::cast(mdNode.getOperand(0)); auto name = nameMD->getString().str(); auto idxMD = llvm::cast(mdNode.getOperand(1)); auto idx = llvm::cast(idxMD->getValue())->getZExtValue(); return {name, idx}; }; auto classesMD = module->getNamedMetadata("classes"); for (llvm::MDNode* mdNode : classesMD->operands()) { const auto [name, idx] = parseEntries(*mdNode); subCHIRPackagesCache.classesCache.emplace(name, idx); } auto enumsMD = module->getNamedMetadata("enums"); for (llvm::MDNode* mdNode : enumsMD->operands()) { const auto [name, idx] = parseEntries(*mdNode); subCHIRPackagesCache.enumsCache.emplace(name, idx); } auto structsMD = module->getNamedMetadata("structs"); for (llvm::MDNode* mdNode : structsMD->operands()) { const auto [name, idx] = parseEntries(*mdNode); subCHIRPackagesCache.structsCache.emplace(name, idx); } auto extendsMD = module->getNamedMetadata("extends"); for (llvm::MDNode* mdNode : extendsMD->operands()) { const auto [name, idx] = parseEntries(*mdNode); subCHIRPackagesCache.extendDefCache.emplace(name, idx); } auto gvsMD = module->getNamedMetadata("gvs"); for (llvm::MDNode* mdNode : gvsMD->operands()) { const auto [name, idx] = parseEntries(*mdNode); subCHIRPackagesCache.gvsCache.emplace(name, idx); } auto funcsMD = module->getNamedMetadata("funcs"); for (llvm::MDNode* mdNode : funcsMD->operands()) { const auto [name, idx] = parseEntries(*mdNode); subCHIRPackagesCache.funcsCache.emplace(name, idx); } auto foreignsMD = module->getNamedMetadata("foreigns"); for (llvm::MDNode* mdNode : foreignsMD->operands()) { const auto [name, idx] = parseEntries(*mdNode); subCHIRPackagesCache.foreignsCache.emplace(name, idx); } } // Using flatbuffers might be a better way void CHIRSplitter::SaveSubCHIRPackagesInfo() { if (!cgPkgCtx.GetGlobalOptions().enIncrementalCompilation) { return; } auto context = llvm::LLVMContext(); auto module = llvm::Module(cgPkgCtx.GetCurrentPkgName(), context); auto i32Ty = llvm::Type::getInt32Ty(context); auto splitNumMD = module.getOrInsertNamedMetadata("splitNum"); auto numMD = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(i32Ty, splitNum)); splitNumMD->addOperand(llvm::MDTuple::get(context, {numMD})); auto classesMD = module.getOrInsertNamedMetadata("classes"); for (auto& klass : std::as_const(subCHIRPackagesCache.classesCache)) { auto nameMD = llvm::MDString::get(context, klass.first); auto idxMD = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(i32Ty, klass.second)); classesMD->addOperand(llvm::MDTuple::get(context, {nameMD, idxMD})); } auto enumsMD = module.getOrInsertNamedMetadata("enums"); for (auto& en : std::as_const(subCHIRPackagesCache.enumsCache)) { auto nameMD = llvm::MDString::get(context, en.first); auto idxMD = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(i32Ty, en.second)); enumsMD->addOperand(llvm::MDTuple::get(context, {nameMD, idxMD})); } auto structsMD = module.getOrInsertNamedMetadata("structs"); for (auto& st : std::as_const(subCHIRPackagesCache.structsCache)) { auto nameMD = llvm::MDString::get(context, st.first); auto idxMD = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(i32Ty, st.second)); structsMD->addOperand(llvm::MDTuple::get(context, {nameMD, idxMD})); } auto extendsMD = module.getOrInsertNamedMetadata("extends"); for (auto extend : std::as_const(subCHIRPackagesCache.extendDefCache)) { auto nameMD = llvm::MDString::get(context, extend.first); auto idxMD = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(i32Ty, extend.second)); extendsMD->addOperand(llvm::MDTuple::get(context, {nameMD, idxMD})); } auto gvsMD = module.getOrInsertNamedMetadata("gvs"); for (auto& gv : std::as_const(subCHIRPackagesCache.gvsCache)) { auto nameMD = llvm::MDString::get(context, gv.first); auto idxMD = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(i32Ty, gv.second)); gvsMD->addOperand(llvm::MDTuple::get(context, {nameMD, idxMD})); } auto funcsMD = module.getOrInsertNamedMetadata("funcs"); for (auto& func : std::as_const(subCHIRPackagesCache.funcsCache)) { auto nameMD = llvm::MDString::get(context, func.first); auto idxMD = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(i32Ty, func.second)); funcsMD->addOperand(llvm::MDTuple::get(context, {nameMD, idxMD})); } auto foreignsMD = module.getOrInsertNamedMetadata("foreigns"); for (auto& foreign : std::as_const(subCHIRPackagesCache.foreignsCache)) { auto nameMD = llvm::MDString::get(context, foreign.first); auto idxMD = llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(i32Ty, foreign.second)); foreignsMD->addOperand(llvm::MDTuple::get(context, {nameMD, idxMD})); } auto path = cgPkgCtx.GetGlobalOptions().GenerateCachedPathNameForCodeGen(cgPkgCtx.GetCurrentPkgName(), ".cgCache"); #ifdef _WIN32 auto tempPath = StringConvertor::NormalizeStringToUTF8(path); CJC_ASSERT(tempPath.has_value() && "Incorrect file name encoding."); path = tempPath.value(); #endif if (cgPkgCtx.GetGlobalOptions().codegenDebugMode) { DumpIR(module, path, true); } else { CodeGen::SaveToBitcodeFile(module, path); } } } // namespace CodeGen } // namespace Cangjie cangjie_compiler-1.0.7/src/CodeGen/CJNative/CHIRSplitter.h000066400000000000000000000056551510705540100232050ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CODEGEN_CHIRSPLITTER_H #define CANGJIE_CODEGEN_CHIRSPLITTER_H #include #include #include #include #include namespace Cangjie { namespace CHIR { class CustomTypeDef; class ClassDef; class EnumDef; class StructDef; class ExtendDef; class GlobalVar; class Value; class FuncBase; class Func; class ImportedFunc; } namespace CodeGen { class CGPkgContext; struct ChirTypeDefCmp { bool operator()(const CHIR::CustomTypeDef* lhs, const CHIR::CustomTypeDef* rhs) const; }; struct ChirValueCmp { bool operator()(const CHIR::Value* lhs, const CHIR::Value* rhs) const; }; struct SubCHIRPackage { bool mainModule = false; std::size_t subCHIRPackageIdx; std::size_t exprNumInChirFuncs; std::size_t splitNum; std::set chirCustomDefs; std::set chirGVs; std::set chirFuncs; std::set chirForeigns; std::set chirImportedCFuncs; explicit SubCHIRPackage(std::size_t splitNum); void Clear(); }; class CHIRSplitter { public: explicit CHIRSplitter(const CGPkgContext& cgPkgCtx); std::vector SplitCHIRPackage(); private: struct SubCHIRPackagesCache { std::optional splitNum = std::nullopt; std::map classesCache; std::map enumsCache; std::map structsCache; std::map extendDefCache; std::map gvsCache; std::map funcsCache; std::map foreignsCache; std::map importedCFuncsCache; }; void CalcSplitsNum(); void SplitCHIRFuncs(std::vector& subCHIRPackages); void SplitCHIRClasses(std::vector& subCHIRPackages); void SplitCHIREnums(std::vector& subCHIRPackages); void SplitCHIRStructs(std::vector& subCHIRPackages); void SplitCHIRExtends(std::vector& subCHIRPackages); void SplitCHIRGlobalVars(std::vector& subCHIRPackages); void SplitCHIRImportedCFuncs(std::vector& subCHIRPackages); unsigned long FindIdxInCache(const std::string& key); void LoadSubCHIRPackagesInfo(); void SaveSubCHIRPackagesInfo(); const CGPkgContext& cgPkgCtx; std::size_t splitNum; std::size_t index; SubCHIRPackagesCache subCHIRPackagesCache; }; } // namespace CodeGen } // namespace Cangjie #endifcangjie_compiler-1.0.7/src/CodeGen/CJNative/CJNativeCGCFFI.cpp000066400000000000000000001462651510705540100235740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements LLVM CodeGen. * LLVM CodeGen CFFI extends BaseGen CFFI. */ #include "CJNative/CJNativeCGCFFI.h" #include "CGModule.h" #include "IRAttribute.h" #include "Utils/CGCommonDef.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/Utils/Casting.h" using namespace Cangjie; using namespace CodeGen; namespace { const unsigned int BITS_PER_BYTE = 8; const unsigned int BYTES_PER_WORD = 8; inline bool IsStructPtrTy(llvm::Type* type) { CJC_NULLPTR_CHECK(type); if (!type->isPointerTy()) { return false; } auto ptrType = llvm::cast(type); if (GetPointerElementType(ptrType)->isStructTy()) { return true; } return false; } inline llvm::Type* GetArgType(const llvm::Function* func, unsigned idx) { CJC_ASSERT(idx < func->arg_size()); return func->getArg(idx)->getType(); } bool IsMetCType(const CHIR::Type& chirTy) { if (chirTy.IsVArray() || IsVArrayRef(chirTy)) { auto varrTy = chirTy.IsRef() ? chirTy.GetTypeArgs()[0] : &chirTy; auto elemType = StaticCast(varrTy)->GetElementType(); CJC_ASSERT(elemType && !elemType->IsInvalid()); return IsMetCType(*elemType); } bool isPrimitiveCType = Utils::In(chirTy.GetTypeKind(), {CHIR::Type::TypeKind::TYPE_UNIT, CHIR::Type::TypeKind::TYPE_BOOLEAN, CHIR::Type::TypeKind::TYPE_INT8, CHIR::Type::TypeKind::TYPE_UINT8, CHIR::Type::TypeKind::TYPE_INT16, CHIR::Type::TypeKind::TYPE_UINT16, CHIR::Type::TypeKind::TYPE_INT32, CHIR::Type::TypeKind::TYPE_UINT32, CHIR::Type::TypeKind::TYPE_INT64, CHIR::Type::TypeKind::TYPE_UINT64, CHIR::Type::TypeKind::TYPE_INT_NATIVE, CHIR::Type::TypeKind::TYPE_UINT_NATIVE, CHIR::Type::TypeKind::TYPE_FLOAT32, CHIR::Type::TypeKind::TYPE_FLOAT64}); return isPrimitiveCType || chirTy.IsCPointer() || chirTy.IsCString() || IsCFunc(chirTy) || IsCStruct(chirTy); } CGType* GetCGType(CGModule& cgMod, const llvm::Type& llvmTy) { if (llvmTy.isIntegerTy(1)) { return CGType::GetBoolCGType(cgMod); } constexpr unsigned bits8 = 8; if (llvmTy.isIntegerTy(bits8)) { return CGType::GetInt8CGType(cgMod); } constexpr unsigned bits16 = 16; if (llvmTy.isIntegerTy(bits16)) { return CGType::GetInt16CGType(cgMod); } constexpr unsigned bits32 = 32; if (llvmTy.isIntegerTy(bits32)) { return CGType::GetInt32CGType(cgMod); } constexpr unsigned bits64 = 64; if (llvmTy.isIntegerTy(bits64)) { return CGType::GetInt64CGType(cgMod); } if (llvmTy.isFloatTy()) { return CGType::GetFloat32CGType(cgMod); } if (llvmTy.isDoubleTy()) { return CGType::GetFloat64CGType(cgMod); } // Rollback return CGType::GetInt8CGType(cgMod); } #ifdef __APPLE__ inline void AddAttrForIntTypeArgOnMacMx(llvm::Function& llvmFunc, const llvm::Argument& arg) { auto intType = llvm::dyn_cast(arg.getType()); if (!intType) { return; } AddParamAttr(&llvmFunc, arg.getArgNo(), llvm::Attribute::NoUndef); if (intType->isIntegerTy(1u)) { // 1u means bool type. AddParamAttr(&llvmFunc, arg.getArgNo(), llvm::Attribute::ZExt); } else if (intType->getBitWidth() < 32) { // 32 means int32_t type. AddParamAttr(&llvmFunc, arg.getArgNo(), llvm::Attribute::SExt); } } #endif } // namespace bool LinuxCJNativeCGCFFI::ProcessInvocation( const CHIR::FuncType& chirFuncTy, std::vector& args, IRBuilder2& builder) { auto found = cfuncMap.find(&chirFuncTy); if (found == cfuncMap.end()) { return false; } auto& kinds = found->second.second; for (size_t i = 0, argIdx = 0; i < kinds.size(); ++i, ++argIdx) { while (i < kinds.size() && kinds[i] == ProcessKind::SKIP) { ++i; } if (i >= kinds.size()) { break; } if (kinds[i] == ProcessKind::NO_PROCESS) { continue; } auto paramTys = chirFuncTy.GetParamTypes(); CJC_ASSERT(i < paramTys.size() && paramTys[i]); auto& paramTy = StaticCast(*paramTys[i]); ProcessInvocationArg(paramTy, kinds[i], argIdx, args, builder); } auto foundRet = typeMap.find(chirFuncTy.GetReturnType()); if (foundRet == typeMap.end()) { return false; } return !foundRet->second.IsIndirect(); } llvm::Type* LinuxCJNativeCGCFFI::GetReturnType(CHIR::Type& chirTy, std::vector& params) { auto& llvmCtx = ctx.GetLLVMContext(); if (IsUnitOrNothing(chirTy)) { return llvm::Type::getVoidTy(llvmCtx); } if (IsUnsizedStructTy(chirTy)) { return nullptr; } if (chirTy.IsStruct()) { return GetStructReturnType(StaticCast(chirTy), params); } return GetLLVMType(chirTy); } llvm::FunctionType* LinuxAmd64CJNativeCGCFFI::GetCFuncType(const CHIR::FuncType& chirFuncTy) { auto found = cfuncMap.find(&chirFuncTy); if (found != cfuncMap.end()) { return found->second.first; } CJC_ASSERT(chirFuncTy.IsCFunc()); int8_t intNum = 6; // 6 is integer register num. int8_t sseNum = 8; // 8 is vector register num. std::vector paramTys; auto chirParamTys = chirFuncTy.GetParamTypes(); paramTys.reserve(chirParamTys.size()); std::vector kinds; kinds.reserve(chirParamTys.size()); llvm::Type* retTy = GetReturnType(*chirFuncTy.GetReturnType(), paramTys); if (!retTy) { return nullptr; } for (auto it : chirParamTys) { CJC_ASSERT(IsMetCType(*it)); if (IsUnsizedStructTy(*it)) { return nullptr; } kinds.emplace_back(GetParamType(*it, intNum, sseNum, paramTys)); } auto resTy = llvm::FunctionType::get(retTy, paramTys, chirFuncTy.HasVarArg()); cfuncMap.emplace(&chirFuncTy, std::make_pair(resTy, kinds)); return resTy; } void LinuxAmd64CJNativeCGCFFI::AddFunctionAttr(const CHIR::FuncType& chirFuncTy, llvm::Function& llvmFunc) { CJC_ASSERT(chirFuncTy.IsCFunc()); auto found = cfuncMap.find(&chirFuncTy); if (found == cfuncMap.end()) { return; } auto found1 = typeMap.find(chirFuncTy.GetReturnType()); unsigned argIdx = 0; if (found1 != typeMap.end() && found1->second.IsIndirect()) { AddSRetAttribute(llvmFunc.arg_begin()); argIdx++; } else if (llvmFunc.getReturnType()->isIntegerTy(1)) { AddRetAttr(&llvmFunc, llvm::Attribute::ZExt); } auto& kinds = found->second.second; for (auto it = kinds.begin(); it != kinds.end(); ++it, ++argIdx) { it = std::find_if(it, kinds.end(), [](auto kind) { return kind != ProcessKind::SKIP; }); if (it == kinds.end()) { break; } if (GetArgType(&llvmFunc, argIdx)->isIntegerTy(1)) { (llvmFunc.arg_begin() + argIdx)->addAttr(llvm::Attribute::ZExt); continue; } if (*it == ProcessKind::NO_PROCESS || *it == ProcessKind::DIRECT) { continue; } if (*it == ProcessKind::EXPAND) { ++argIdx; continue; } if (*it == ProcessKind::INDIRECT) { CJC_ASSERT(IsStructPtrTy(GetArgType(&llvmFunc, argIdx))); auto argument = llvmFunc.arg_begin() + argIdx; AddByValAttribute(argument, GetTypeAlignment(cgMod, *argument->getType())); } } AddFnAttr(&llvmFunc, llvm::Attribute::get(llvmFunc.getContext(), CodeGen::CFUNC_ATTR)); } void LinuxAmd64CJNativeCGCFFI::ProcessParam( CHIR::Type& chirParamTy, LLVMFuncArgIt& arg, llvm::Value* place, IRBuilder2& builder) { if (!chirParamTy.IsStruct()) { return; } auto found = typeMap.find(&chirParamTy); if (found == typeMap.end()) { return; } auto& info = found->second; if (info.IsDirect()) { place = builder.CreateBitCast(place, arg->getType()->getPointerTo()); builder.CreateStore(arg, place); } else if (info.IsExpand()) { llvm::Value* arg1 = arg; llvm::Value* arg2 = ++arg; auto arg1Type = arg1->getType(); auto arg2Type = arg2->getType(); auto destTy = llvm::StructType::get(ctx.GetLLVMContext(), {arg1Type, arg2Type}); auto alloca = builder.CreateEntryAlloca(destTy); auto part0 = builder.CreateStructGEP(destTy, alloca, 0); builder.CreateStore(arg1, part0); auto part1 = builder.CreateStructGEP(destTy, alloca, 1); builder.CreateStore(arg2, part1); size_t actualSize = GetTypeSize(cgMod, chirParamTy); builder.CreateMemCpy(place, GetAlign(cgMod, chirParamTy), alloca, GetAlign(cgMod, *destTy), actualSize); } } void LinuxAmd64CJNativeCGCFFI::ProcessInvocationArg( CHIR::StructType& chirParamTy, ProcessKind kind, size_t& argIdx, std::vector& args, IRBuilder2& builder) { if (kind == ProcessKind::INDIRECT) { return; } auto found = typeMap.find(&chirParamTy); CJC_ASSERT(found != typeMap.end()); auto& info = found->second; if (kind == ProcessKind::DIRECT) { CJC_ASSERT(info.IsDirect()); const unsigned int addrSpace = args[argIdx]->GetRawValue()->getType()->getPointerAddressSpace(); auto cast = builder.CreateBitCast(args[argIdx]->GetRawValue(), info[0]->getPointerTo(addrSpace)); args[argIdx] = cgMod.CreateGhostCFuncArgValue( *builder.LLVMIRBuilder2::CreateLoad(info[0], cast), *GetCGType(cgMod, *info[0])); } else { CJC_ASSERT(info.IsExpand()); auto& llvmCtx = ctx.GetLLVMContext(); auto tmpTy = llvm::StructType::get(llvmCtx, {info[0], info[1]}); auto alloca = builder.CreateEntryAlloca(tmpTy, nullptr, "arg.copy"); builder.CreateMemCpy(alloca, GetAlign(cgMod, *tmpTy), args[argIdx]->GetRawValue(), GetAlign(cgMod, chirParamTy), GetTypeSize(cgMod, chirParamTy)); auto argPtr1 = builder.CreateStructGEP(tmpTy, alloca, 0); auto argPtr2 = builder.CreateStructGEP(tmpTy, alloca, 1); args.emplace(args.begin() + static_cast(argIdx), cgMod.CreateGhostCFuncArgValue( *builder.LLVMIRBuilder2::CreateLoad(info[0], argPtr1), *GetCGType(cgMod, *info[0]))); args[++argIdx] = cgMod.CreateGhostCFuncArgValue( *builder.LLVMIRBuilder2::CreateLoad(info[1], argPtr2), *GetCGType(cgMod, *info[1])); } } llvm::Value* LinuxAmd64CJNativeCGCFFI::ProcessRetValue(const llvm::Type& retType, llvm::Value& val, IRBuilder2& builder) { CJC_ASSERT(!retType.isVoidTy()); if (auto structType = llvm::dyn_cast(&retType); structType && structType->isLiteral()) { auto tmpVal = &val; if (val.getType() != structType->getPointerTo()) { tmpVal = builder.CreateBitCast(&val, structType->getPointerTo()); } return builder.LLVMIRBuilder2::CreateLoad(const_cast(&retType), tmpVal); } llvm::Value* castRes = &val; if (!retType.isStructTy() && IsStructPtrTy(val.getType())) { castRes = builder.CreateBitCast(&val, retType.getPointerTo()); } return builder.LLVMIRBuilder2::CreateLoad(const_cast(&retType), castRes); } llvm::Type* LinuxAmd64CJNativeCGCFFI::GetStructReturnType(CHIR::StructType& chirTy, std::vector& params) { auto& llvmCtx = ctx.GetLLVMContext(); if (GetTypeSize(cgMod, chirTy) == 0) { return llvm::Type::getVoidTy(llvmCtx); } auto info = GetMappingArgInfo(chirTy, false); if (info.IsIndirect()) { auto structType = GetLLVMType(chirTy); CJC_ASSERT(structType->isStructTy()); params.emplace_back(structType->getPointerTo()); return llvm::Type::getVoidTy(llvmCtx); } if (info.IsDirect()) { return info[0]; } return llvm::StructType::get(llvmCtx, {info[0], info[1]}); } ProcessKind LinuxAmd64CJNativeCGCFFI::GetParamType( CHIR::Type& chirTy, int8_t& intNum, int8_t& sseNum, std::vector& params) { CJC_ASSERT(!IsUnitOrNothing(chirTy)); if (chirTy.IsStruct()) { if (GetTypeSize(cgMod, chirTy) == 0) { return ProcessKind::SKIP; } auto info = GetMappingArgInfo(StaticCast(chirTy), true); auto structType = GetLLVMType(chirTy); CJC_ASSERT(structType->isStructTy()); if (info.IsIndirect()) { params.emplace_back(structType->getPointerTo()); return ProcessKind::INDIRECT; } else { auto resTy = GetParamType(structType, info, intNum, sseNum); if (resTy == nullptr) { params.emplace_back(info[0]); params.emplace_back(info[1]); return ProcessKind::EXPAND; } if (resTy != structType) { params.emplace_back(resTy); return ProcessKind::DIRECT; } else { params.emplace_back(structType->getPointerTo()); return ProcessKind::INDIRECT; } } } llvm::Type* paramType = GetLLVMType(chirTy); // VArray as CFunc parament pass by a i8*. if (chirTy.IsVArray()) { paramType = llvm::Type::getInt8PtrTy(ctx.GetLLVMContext()); } if (paramType->isIntOrPtrTy()) { intNum -= intNum > 0 ? 1 : 0; } else if (paramType->isFloatingPointTy()) { sseNum -= sseNum > 0 ? 1 : 0; } params.emplace_back(paramType); return ProcessKind::NO_PROCESS; } // If return nullptr, the callee should use the ty(s) stored in @p info. // Otherwise, use thr return value. llvm::Type* LinuxAmd64CJNativeCGCFFI::GetParamType( llvm::Type* ty, const ABIArgInfo& info, int8_t& intNum, int8_t& sseNum) const { CJC_ASSERT(!info.IsIndirect() && "ArgInfo.size() cannot be 0."); if (info.IsDirect()) { if (info[0]->isIntOrPtrTy() && intNum > 0) { intNum--; } else if (info[0]->isFloatingPointTy() && sseNum > 0) { sseNum--; } return info[0]; } CJC_ASSERT(info.IsExpand() && "ArgInfo.size() must be 2 here."); const int8_t infoSize = 2; if (info[0]->isIntOrPtrTy() && info[1]->isIntOrPtrTy()) { if (intNum < infoSize) { return ty; } intNum -= infoSize; return nullptr; } if (info[0]->isFloatingPointTy() && info[1]->isFloatingPointTy()) { if (sseNum < infoSize) { return ty; } sseNum -= infoSize; return nullptr; } if (intNum < 1 || sseNum < 1) { return ty; } intNum--; sseNum--; return nullptr; } ABIArgInfo LinuxAmd64CJNativeCGCFFI::GetMappingArgInfo(CHIR::StructType& chirTy, bool isArg) { auto found = typeMap.find(&chirTy); if (found != typeMap.end()) { return found->second; } auto llvmType = GetLLVMType(chirTy); auto classes = ClassifyType(*llvmType); // According to the ABI SPEC of Linux x86_64, if a struct doesn't be passed by stack pointer, // it can be represented as one or two primitive types in the function type, // we mark it as a pair (low, high), in this case, the low part type cannot be nullptr, and // the high part type can be nullptr. // If the low part type is nullptr, the struct will be passed by stack pointer. auto lowPartTy = GetLowPartType(llvmType, classes, isArg); if (lowPartTy == nullptr) { return typeMap.emplace(&chirTy, ABIArgInfo::GetIndirect()).first->second; } if (classes.size() <= 1 || (lowPartTy->isVoidTy() && classes[1] == WordClass::NO_CLASS)) { return typeMap.emplace(&chirTy, ABIArgInfo::GetDirect(lowPartTy)).first->second; } llvm::Type* highPartTy = nullptr; if (classes[1] == WordClass::INTEGER) { // CONSIDER: When does this scenario occur? if (classes[0] == WordClass::NO_CLASS) { return typeMap.emplace(&chirTy, ABIArgInfo::GetIndirect()).first->second; } highPartTy = GetIntegerTypeWithOffset(*llvmType, BYTES_PER_WORD, *llvmType, BYTES_PER_WORD); } else if (classes[1] == WordClass::SSE) { // CONSIDER: When does this scenario occur? if (classes[0] == WordClass::NO_CLASS) { return typeMap.emplace(&chirTy, ABIArgInfo::GetIndirect()).first->second; } highPartTy = GetSseTypeWithOffset(*llvmType, BYTES_PER_WORD, *llvmType, BYTES_PER_WORD); } else if (classes[1] == WordClass::SSEUP) { lowPartTy = llvm::Type::getFP128Ty(ctx.GetLLVMContext()); } else if (classes[1] == WordClass::X87UP) { // CONSIDER: When does this scenario occur? CJC_ASSERT(classes[0] == WordClass::X87); } else { CJC_ASSERT(false && "MEMORY, NO_CLASS, X87, COMPLEX_X87 cannot be here."); } if (highPartTy == nullptr) { return typeMap.emplace(&chirTy, ABIArgInfo::GetDirect(lowPartTy)).first->second; } return typeMap.emplace(&chirTy, ABIArgInfo::GetExpand(lowPartTy, highPartTy)).first->second; } llvm::Type* LinuxAmd64CJNativeCGCFFI::GetLowPartType( llvm::Type* type, std::vector& classes, bool isArg) const { auto& llvmCtx = ctx.GetLLVMContext(); auto lowPartTy = llvm::Type::getVoidTy(llvmCtx); if (classes[0] == WordClass::NO_CLASS) { return lowPartTy; } else if (classes[0] == WordClass::MEMORY) { return nullptr; } else if (classes[0] == WordClass::INTEGER) { lowPartTy = GetIntegerTypeWithOffset(*type, 0, *type, 0); // CONSIDER: Is ZEXT & SEXT needed? } else if (classes[0] == WordClass::SSE) { lowPartTy = GetSseTypeWithOffset(*type, 0, *type, 0); } else if (classes[0] == WordClass::X87) { // CONSIDER: CType does NOT contain long double type now. if (isArg) { return nullptr; } lowPartTy = llvm::Type::getX86_FP80Ty(llvmCtx); } else if (classes[0] == WordClass::COMPLEX_X87) { // CONSIDER: CType does NOT contain complex type now. if (isArg) { return nullptr; } lowPartTy = llvm::Type::getX86_FP80Ty(llvmCtx); } else { CJC_ASSERT(false && "SSEUP, X87UP cannot be here."); } return lowPartTy; } static inline void FillWithMemory(std::vector& wordClasses) { std::fill(wordClasses.begin(), wordClasses.end(), WordClass::MEMORY); } static void UnifyWordClass(std::vector::iterator wordi, WordClass newClass) { if (*wordi == newClass) { return; } else if (*wordi == WordClass::NO_CLASS) { *wordi = newClass; } else if (newClass == WordClass::NO_CLASS) { return; } else if (*wordi == WordClass::MEMORY || newClass == WordClass::MEMORY) { *wordi = WordClass::MEMORY; } else if (*wordi == WordClass::INTEGER || newClass == WordClass::INTEGER) { *wordi = WordClass::INTEGER; } else if (*wordi == WordClass::X87 || *wordi == WordClass::X87UP || *wordi == WordClass::COMPLEX_X87 || newClass == WordClass::X87 || newClass == WordClass::X87UP || newClass == WordClass::COMPLEX_X87) { *wordi = WordClass::MEMORY; } else { *wordi = newClass; } } static inline size_t AsBits(size_t bytes) { return bytes * BITS_PER_BYTE; } std::vector LinuxAmd64CJNativeCGCFFI::ClassifyType(llvm::Type& type) const { // Eight bytes are a word. auto wordSize = (GetTypeSize(cgMod, type) + BYTES_PER_WORD - 1) / BYTES_PER_WORD; std::vector wordClasses(wordSize, WordClass::NO_CLASS); if (wordSize > 4) { // 4 is the upper limit of word size of a small struct. FillWithMemory(wordClasses); return wordClasses; } ClassifyType(type, wordClasses.begin(), 0); FixupClassification(type, wordClasses); return wordClasses; } void LinuxAmd64CJNativeCGCFFI::ClassifyType( llvm::Type& type, const std::vector::iterator begin, size_t offset) const { size_t misAlign = offset % GetTypeAlignment(cgMod, type); if (misAlign != 0) { size_t i = offset / BYTES_PER_WORD; for (; i < (offset + GetTypeSize(cgMod, type) + BYTES_PER_WORD - 1) / BYTES_PER_WORD; i++) { auto newBegin = begin + static_cast(i); UnifyWordClass(newBegin, WordClass::MEMORY); } return; } auto wordOffset = offset / BYTES_PER_WORD; if (type.isIntOrPtrTy()) { UnifyWordClass(begin + static_cast(wordOffset), WordClass::INTEGER); } else if (type.isFloatingPointTy()) { uint64_t bits = type.getPrimitiveSizeInBits(); if (bits == 16 || bits == 32 || bits == 64) { // Float16, Float32, Float64 in Cangjie. UnifyWordClass(begin + static_cast(wordOffset), WordClass::SSE); } else if (bits == 80) { // 80 bits means long double in C, there is no mapping type in Cangjie. UnifyWordClass(begin + static_cast(wordOffset), WordClass::X87); UnifyWordClass(begin + static_cast(wordOffset + 1), WordClass::X87UP); } else { CJC_ASSERT(false && "Bits in FloatingPointTy are wrong."); } } else if (type.isStructTy()) { auto structType = llvm::cast(&type); auto fieldTypes = structType->elements(); ClassifyStructType(fieldTypes, begin, offset); } else if (type.isArrayTy()) { auto arrayType = llvm::cast(&type); auto elemNum = arrayType->getNumElements(); auto elemType = arrayType->getElementType(); for (uint64_t idx = 0; idx < elemNum; ++idx) { ClassifyType(*elemType, begin, offset); offset += GetTypeSize(cgMod, *elemType); } } else { CJC_ASSERT(false && "Unsupported type."); } } void LinuxAmd64CJNativeCGCFFI::ClassifyStructType( llvm::ArrayRef fields, const std::vector::iterator begin, size_t offset) const { if (fields.empty()) { // CONSIDER: When this is a empty struct, there is a special process in clay's algorithm. So why? // Here is the code: ClassifyType(llvm::Type::getInt8Ty(llvmCtx), begin, offset); return; } size_t fieldOffset = offset; for (auto field : fields) { fieldOffset = AlignedUpTo(cgMod, fieldOffset, *field); ClassifyType(*field, begin, fieldOffset); fieldOffset += GetTypeSize(cgMod, *field); } } void LinuxAmd64CJNativeCGCFFI::FixupClassification(const llvm::Type& type, std::vector& wordClasses) const { auto it = wordClasses.begin(); auto end = wordClasses.end(); if (wordClasses.size() > MAX_SPLIT_NUM && type.isStructTy()) { if (*it != WordClass::SSE) { FillWithMemory(wordClasses); return; } ++it; for (; it != end; ++it) { if (*it != WordClass::SSEUP) { FillWithMemory(wordClasses); return; } } return; } while (it != end) { if (*it == WordClass::MEMORY || *it == WordClass::X87UP) { FillWithMemory(wordClasses); return; } if (*it == WordClass::SSE) { do { ++it; } while ((it != end) && (*it == WordClass::SSEUP)); } else if (*it == WordClass::X87) { do { ++it; } while ((it != end) && (*it == WordClass::X87UP)); } else { ++it; } } } // All offset's unit is byte. llvm::Type* LinuxAmd64CJNativeCGCFFI::GetIntegerTypeWithOffset( llvm::Type& curType, size_t offset, llvm::Type& srcType, size_t srcOffset) const { CJC_ASSERT(srcOffset == 0 || srcOffset == BYTES_PER_WORD); if (offset == 0) { auto typeSize = GetTypeSize(cgMod, curType); if (curType.isIntOrPtrTy() && typeSize == BYTES_PER_WORD) { return &curType; } if (curType.isIntOrPtrTy() && (typeSize == 1 || typeSize == 2 || typeSize == 4) && // 1, 2, 4 means (U)Int8, (U)Int16, (U)Int32. ContainsUselessBits(srcType, AsBits(srcOffset + typeSize), AsBits(srcOffset + BYTES_PER_WORD))) { return &curType; } } if (curType.isStructTy() && offset < GetTypeSize(cgMod, curType)) { auto structType = llvm::cast(&curType); auto fieldOffsets = CalcFieldOffsets(*structType); auto fieldIdx = GetFieldContainsOffset(fieldOffsets, offset); auto relOffset = offset - fieldOffsets[fieldIdx]; return GetIntegerTypeWithOffset(*structType->getElementType(fieldIdx), relOffset, srcType, srcOffset); } auto typeSize = GetTypeSize(cgMod, srcType); CJC_ASSERT(typeSize != srcOffset); auto intSize = std::min(typeSize - srcOffset, BYTES_PER_WORD); return llvm::IntegerType::get(ctx.GetLLVMContext(), static_cast(AsBits(intSize))); } llvm::Type* LinuxAmd64CJNativeCGCFFI::GetSseTypeWithOffset( llvm::Type& curType, size_t offset, llvm::Type& srcType, size_t srcOffset) const { CJC_ASSERT(srcOffset == 0 || srcOffset == BYTES_PER_WORD); auto& llvmCtx = ctx.GetLLVMContext(); const size_t bytesOfHalfWord = 4; if (ContainsUselessBits(srcType, AsBits(srcOffset + bytesOfHalfWord), AsBits(srcOffset + BYTES_PER_WORD))) { return llvm::Type::getFloatTy(llvmCtx); } if (ContainsFloatAtOffset(curType, offset) && ContainsFloatAtOffset(curType, offset + bytesOfHalfWord)) { return llvm::VectorType::get(llvm::Type::getFloatTy(llvmCtx), 2, false); // { float, float } -> <2 x float> } return llvm::Type::getDoubleTy(llvmCtx); } // Unit of start and end is bits. bool LinuxAmd64CJNativeCGCFFI::ContainsUselessBits(llvm::Type& type, size_t start, size_t end) const { CJC_ASSERT(start <= end); if (AsBits(GetTypeSize(cgMod, type)) <= start) { return true; } if (type.isStructTy()) { auto structType = llvm::cast(&type); auto fieldOffsets = CalcFieldOffsets(*structType); auto fields = structType->elements(); for (size_t i = 0; i < fields.size(); i++) { auto field = fields[i]; auto fieldOffset = fieldOffsets[i]; auto fieldOffsetBits = AsBits(fieldOffset); if (fieldOffsetBits >= end) { break; } auto fieldStart = fieldOffsetBits < start ? start - fieldOffsetBits : 0; if (!ContainsUselessBits(*field, fieldStart, end - fieldOffsetBits)) { return false; } } return true; } return false; } LinuxAmd64CJNativeCGCFFI::FieldOffsets LinuxAmd64CJNativeCGCFFI::CalcFieldOffsets(const llvm::StructType& type) const { FieldOffsets offsets; auto fields = type.elements(); offsets.reserve(fields.size()); size_t offset = 0; for (auto field : fields) { offset = AlignedUpTo(cgMod, offset, *field); offsets.emplace_back(offset); offset += GetTypeSize(cgMod, *field); } return offsets; } unsigned LinuxAmd64CJNativeCGCFFI::GetFieldContainsOffset(FieldOffsets fieldOffsets, size_t offset) const { size_t idx = 0; for (; idx + 1 < fieldOffsets.size(); idx++) { if (fieldOffsets[idx] <= offset && fieldOffsets[idx + 1] > offset) { break; } } return static_cast(idx); } bool LinuxAmd64CJNativeCGCFFI::ContainsFloatAtOffset(llvm::Type& type, size_t offset) const { if (offset == 0 && type.isFloatTy()) { return true; } if (type.isStructTy()) { auto structType = llvm::cast(&type); auto fieldOffsets = CalcFieldOffsets(*structType); auto fieldIdx = GetFieldContainsOffset(fieldOffsets, offset); auto relOffset = offset - fieldOffsets[fieldIdx]; return ContainsFloatAtOffset(*structType->getElementType(fieldIdx), relOffset); } return false; } llvm::FunctionType* LinuxAarch64CJNativeCGCFFI::GetCFuncType(const CHIR::FuncType& chirFuncTy) { auto found = cfuncMap.find(&chirFuncTy); if (found != cfuncMap.end()) { return found->second.first; } CJC_ASSERT(chirFuncTy.IsCFunc()); std::vector paramTys; auto chirParamTys = chirFuncTy.GetParamTypes(); paramTys.reserve(chirParamTys.size()); std::vector kinds; kinds.reserve(chirParamTys.size()); auto retType = GetReturnType(*chirFuncTy.GetReturnType(), paramTys); if (retType == nullptr) { return nullptr; } for (auto ty : chirParamTys) { CJC_ASSERT(IsMetCType(*ty)); if (IsUnsizedStructTy(*ty)) { return nullptr; } auto typeSize = GetTypeSize(cgMod, *ty); if (IsUnitOrNothing(*ty) || typeSize == 0) { kinds.emplace_back(ProcessKind::SKIP); continue; } kinds.emplace_back(GetParamType(*ty, paramTys)); } auto resTy = llvm::FunctionType::get(retType, paramTys, chirFuncTy.HasVarArg()); cfuncMap.emplace(&chirFuncTy, std::make_pair(resTy, kinds)); return resTy; } void LinuxAarch64CJNativeCGCFFI::AddFunctionAttr(const CHIR::FuncType& chirFuncTy, llvm::Function& llvmFunc) { CJC_ASSERT(chirFuncTy.IsCFunc()); auto found = cfuncMap.find(&chirFuncTy); if (found == cfuncMap.end()) { return; } auto retTy = chirFuncTy.GetReturnType(); auto found1 = typeMap.find(retTy); if (found1 != typeMap.end() && found1->second.IsIndirect()) { AddSRetAttribute(llvmFunc.arg_begin()); } AddFnAttr(&llvmFunc, llvm::Attribute::get(llvmFunc.getContext(), CodeGen::CFUNC_ATTR)); } void LinuxAarch64CJNativeCGCFFI::ProcessParam( CHIR::Type& chirParamTy, LLVMFuncArgIt& arg, llvm::Value* place, IRBuilder2& builder) { if (!chirParamTy.IsStruct()) { return; } auto found = typeMap.find(&chirParamTy); if (found == typeMap.end()) { return; } auto& argInfo = found->second; if (argInfo.IsDirect()) { auto argType = arg->getType(); llvm::Value* argVal = arg; auto alloca = builder.CreateEntryAlloca(argType); builder.CreateStore(argVal, alloca); size_t actualSize = GetTypeSize(cgMod, chirParamTy); builder.CreateMemCpy(place, GetAlign(cgMod, chirParamTy), alloca, GetAlign(cgMod, *argType), actualSize); } CJC_ASSERT(!argInfo.IsExpand() && "ArgInfo in aarch64 cannot be Expand."); } void LinuxAarch64CJNativeCGCFFI::ProcessInvocationArg( CHIR::StructType& chirParamTy, ProcessKind kind, size_t& argIdx, std::vector& args, IRBuilder2& builder) { if (kind == ProcessKind::INDIRECT) { return; } if (kind != ProcessKind::DIRECT) { CJC_ASSERT(false && "ArgInfo in aarch64 cannot be Expand."); return; } auto found = typeMap.find(&chirParamTy); CJC_ASSERT(found != typeMap.end()); auto& info = found->second; CJC_ASSERT(info.IsDirect()); CJC_ASSERT(argIdx < args.size()); auto arg = args[argIdx]; size_t actualSize = GetTypeSize(cgMod, chirParamTy); if (info[0]->isIntegerTy()) { auto numBits = static_cast(AsBits(actualSize)); auto actualIntTy = llvm::IntegerType::get(ctx.GetLLVMContext(), numBits); const unsigned int addrSpace = arg->GetRawValue()->getType()->getPointerAddressSpace(); auto tmpVal = builder.CreateBitCast(arg->GetRawValue(), actualIntTy->getPointerTo(addrSpace)); tmpVal = builder.LLVMIRBuilder2::CreateLoad(actualIntTy, tmpVal); if (GetTypeSize(cgMod, *info[0]) != actualSize) { tmpVal = builder.CreateIntCast(tmpVal, info[0], false); } args[argIdx] = cgMod.CreateGhostCFuncArgValue(*tmpVal, *GetCGType(cgMod, *info[0])); } else { CJC_ASSERT(info[0]->isArrayTy()); auto tmpVal = builder.CreateEntryAlloca(info[0], nullptr, "arg.copy"); builder.CreateMemCpy( tmpVal, GetAlign(cgMod, *info[0]), arg->GetRawValue(), GetAlign(cgMod, chirParamTy), actualSize); args[argIdx] = cgMod.CreateGhostCFuncArgValue( *builder.LLVMIRBuilder2::CreateLoad(info[0], tmpVal), *GetCGType(cgMod, *info[0])); } } llvm::Value* LinuxAarch64CJNativeCGCFFI::ProcessRetValue( const llvm::Type& retType, llvm::Value& val, IRBuilder2& builder) { CJC_ASSERT(!retType.isVoidTy()); llvm::Value* castRes = &val; if (!retType.isStructTy() && IsStructPtrTy(val.getType())) { castRes = builder.CreateBitCast(&val, retType.getPointerTo()); } return builder.LLVMIRBuilder2::CreateLoad(const_cast(&retType), castRes); } llvm::Type* LinuxAarch64CJNativeCGCFFI::GetStructReturnType(CHIR::StructType& chirTy, std::vector& params) { auto& llvmCtx = ctx.GetLLVMContext(); if (GetTypeSize(cgMod, chirTy) == 0) { return llvm::Type::getVoidTy(llvmCtx); } auto argInfo = GetMappingArgInfo(chirTy, false); if (argInfo.IsIndirect()) { auto structType = GetLLVMType(chirTy); CJC_ASSERT(structType->isStructTy()); params.emplace_back(structType->getPointerTo()); return llvm::Type::getVoidTy(llvmCtx); } CJC_ASSERT(argInfo.IsDirect() && "ArgInfo in aarch64 cannot be Expand."); return argInfo[0]; } ProcessKind LinuxAarch64CJNativeCGCFFI::GetParamType(CHIR::Type& chirTy, std::vector& params) { if (chirTy.IsStruct()) { auto info = GetMappingArgInfo(StaticCast(chirTy), true); auto structType = GetLLVMType(chirTy); CJC_ASSERT(structType->isStructTy()); if (info.IsIndirect()) { params.emplace_back(structType->getPointerTo()); return ProcessKind::INDIRECT; } else { CJC_ASSERT(info.IsDirect() && "ArgInfo in aarch64 cannot be Expand."); params.emplace_back(info[0]); return ProcessKind::DIRECT; } } llvm::Type* paramType = GetLLVMType(chirTy); // VArray as CFunc parament pass by a i8*. if (chirTy.IsVArray()) { paramType = llvm::Type::getInt8PtrTy(ctx.GetLLVMContext()); } params.emplace_back(paramType); return ProcessKind::NO_PROCESS; } ABIArgInfo LinuxAarch64CJNativeCGCFFI::GetMappingArgInfo(CHIR::StructType& chirTy, [[maybe_unused]] bool isArg) { auto found = typeMap.find(&chirTy); if (found != typeMap.end()) { return found->second; } auto type = GetLLVMType(chirTy); size_t size = GetTypeSize(cgMod, *type); llvm::Type* base = nullptr; if (size_t members = 0; IsHomogeneousAggregate(*type, base, members)) { return typeMap.emplace(&chirTy, ABIArgInfo::GetDirect(llvm::ArrayType::get(base, members))).first->second; } const size_t limitSizeOfSmallStruct = 16; if (size > limitSizeOfSmallStruct) { return typeMap.emplace(&chirTy, ABIArgInfo::GetIndirect()).first->second; } size_t alignment = GetTypeAlignment(cgMod, *type); size = llvm::alignTo(size, BYTES_PER_WORD); auto& llvmCtx = ctx.GetLLVMContext(); if (alignment < limitSizeOfSmallStruct && size == limitSizeOfSmallStruct) { llvm::Type* baseTy = llvm::Type::getInt64Ty(llvmCtx); llvm::Type* resTy = llvm::ArrayType::get(baseTy, static_cast(size) / BYTES_PER_WORD); return typeMap.emplace(&chirTy, ABIArgInfo::GetDirect(resTy)).first->second; } llvm::Type* resTy = llvm::IntegerType::get(llvmCtx, static_cast(size) * BITS_PER_BYTE); return typeMap.emplace(&chirTy, ABIArgInfo::GetDirect(resTy)).first->second; } bool LinuxAarch64CJNativeCGCFFI::IsHomogeneousAggregate(llvm::Type& type, llvm::Type*& base, size_t& members) { if (auto st = llvm::dyn_cast(&type)) { members = 0; for (auto fdTy : st->elements()) { if (GetTypeSize(cgMod, *fdTy) == 0) { continue; } size_t fdMembers = 0; if (!IsHomogeneousAggregate(*fdTy, base, fdMembers)) { return false; } members += fdMembers; } if (base == nullptr) { return false; } if (GetTypeSize(cgMod, *base) * members != GetTypeSize(cgMod, type)) { return false; } } else { members = 1; if (!type.isFloatingPointTy()) { return false; } if (base == nullptr) { base = &type; } } return members > 0 && members <= 4; // 4 is maximum number of homogeneous aggregate's members. } #ifdef __APPLE__ void MacAArch64CJNativeCGCFFI::AddFunctionAttr(const CHIR::FuncType& chirFuncTy, llvm::Function& llvmFunc) { LinuxAarch64CJNativeCGCFFI::AddFunctionAttr(chirFuncTy, llvmFunc); for (auto& arg : llvmFunc.args()) { AddAttrForIntTypeArgOnMacMx(llvmFunc, arg); } if (auto intRetType = llvm::dyn_cast(llvmFunc.getReturnType()); intRetType && chirFuncTy.GetReturnType()->IsPrimitive()) { if (intRetType->isIntegerTy(1u)) { // 1u means bool type. AddRetAttr(&llvmFunc, llvm::Attribute::ZExt); } else if (intRetType->getBitWidth() < 32) { // 32 means int32_t type. AddRetAttr(&llvmFunc, llvm::Attribute::SExt); } } } llvm::Type* MacAArch64CJNativeCGCFFI::GetStructReturnType(CHIR::StructType& chirTy, std::vector& params) { auto type = GetLLVMType(chirTy); size_t size = GetTypeSize(cgMod, *type); auto& llvmCtx = ctx.GetLLVMContext(); if (size == 0) { return llvm::Type::getVoidTy(llvmCtx); } llvm::Type* base = nullptr; if (size_t members = 0; IsHomogeneousAggregate(*type, base, members)) { return type; } if (size < BYTES_PER_WORD) { return llvm::IntegerType::get(llvmCtx, static_cast(size) * BITS_PER_BYTE); } return LinuxAarch64CJNativeCGCFFI::GetStructReturnType(chirTy, params); } #endif llvm::FunctionType* WindowsAmd64CJNativeCGCFFI::GetCFuncType(const CHIR::FuncType& chirFuncTy) { if (auto found = cfuncMap.find(&chirFuncTy); found != cfuncMap.end()) { return found->second.first; } CJC_ASSERT(chirFuncTy.IsCFunc()); std::vector paramTypes; auto chirParamTypes = chirFuncTy.GetParamTypes(); paramTypes.reserve(chirParamTypes.size()); std::vector kinds; kinds.reserve(chirParamTypes.size()); auto retTy = GetReturnType(*chirFuncTy.GetReturnType(), paramTypes); if (retTy == nullptr) { return nullptr; } for (auto paramTy : chirParamTypes) { CJC_ASSERT(IsMetCType(*paramTy)); if (IsUnsizedStructTy(*paramTy)) { return nullptr; } CJC_ASSERT(!IsUnitOrNothing(*paramTy)); kinds.emplace_back(GetParamType(*paramTy, paramTypes)); } auto resTy = llvm::FunctionType::get(retTy, paramTypes, chirFuncTy.HasVarArg()); cfuncMap.emplace(&chirFuncTy, std::make_pair(resTy, kinds)); return resTy; } void WindowsAmd64CJNativeCGCFFI::AddFunctionAttr(const CHIR::FuncType& chirFuncTy, llvm::Function& llvmFunc) { CJC_ASSERT(chirFuncTy.IsCFunc()); if (cfuncMap.find(&chirFuncTy) == cfuncMap.end()) { return; } auto retTy = chirFuncTy.GetReturnType(); if (retTy->IsBoolean()) { AddRetAttr(&llvmFunc, llvm::Attribute::ZExt); } else if (auto found = typeMap.find(retTy); found != typeMap.end() && found->second.IsIndirect()) { auto sretArg = llvmFunc.arg_begin(); AddSRetAttribute(sretArg); sretArg->addAttr(llvm::Attribute::NoAlias); auto align = llvm::Align(GetTypeAlignment(cgMod, *GetPointerElementType(sretArg->getType()))); auto alignAttr = llvm::Attribute::getWithAlignment(llvmFunc.getContext(), align); sretArg->addAttr(alignAttr); } for (const auto& param : llvmFunc.args()) { if (param.getType()->isIntegerTy(1)) { AddParamAttr(&llvmFunc, param.getArgNo(), llvm::Attribute::ZExt); } } AddFnAttr(&llvmFunc, llvm::Attribute::get(llvmFunc.getContext(), CodeGen::CFUNC_ATTR)); } void WindowsAmd64CJNativeCGCFFI::ProcessParam( CHIR::Type& chirParamTy, LLVMFuncArgIt& arg, llvm::Value* place, IRBuilder2& builder) { if (!chirParamTy.IsStruct()) { return; } auto found = typeMap.find(&chirParamTy); if (found == typeMap.end()) { return; } ABIArgInfo& argInfo = found->second; if (argInfo.IsDirect()) { auto argType = arg->getType(); llvm::Value* argVal = arg; auto alloca = builder.CreateEntryAlloca(argType); builder.CreateStore(argVal, alloca); builder.CreateMemCpy( place, GetAlign(cgMod, chirParamTy), alloca, GetAlign(cgMod, *argType), GetTypeSize(cgMod, chirParamTy)); } CJC_ASSERT(!argInfo.IsExpand() && "ArgInfo in Windows x64 cannot be Expand."); } llvm::Value* WindowsAmd64CJNativeCGCFFI::ProcessRetValue( const llvm::Type& retType, llvm::Value& val, IRBuilder2& builder) { CJC_ASSERT(!retType.isVoidTy()); llvm::Value* castRes = &val; if (!retType.isStructTy() && IsStructPtrTy(val.getType())) { castRes = builder.CreateBitCast(&val, retType.getPointerTo()); } return builder.LLVMIRBuilder2::CreateLoad(const_cast(&retType), castRes); } void WindowsAmd64CJNativeCGCFFI::ProcessInvocationArg( CHIR::StructType& chirParamTy, ProcessKind kind, size_t& argIdx, std::vector& args, IRBuilder2& builder) { auto found = typeMap.find(&chirParamTy); CJC_ASSERT(found != typeMap.end()); auto& info = found->second; if (kind == ProcessKind::DIRECT) { CJC_ASSERT(info.IsDirect()); CJC_ASSERT(argIdx < args.size()); const unsigned int addrSpace = args[argIdx]->GetRawValue()->getType()->getPointerAddressSpace(); llvm::Value* arg = builder.CreateBitCast(args[argIdx]->GetRawValue(), info[0]->getPointerTo(addrSpace)); args[argIdx] = cgMod.CreateGhostCFuncArgValue( *builder.LLVMIRBuilder2::CreateLoad(info[0], arg), *GetCGType(cgMod, *info[0])); } else if (kind == ProcessKind::INDIRECT) { CJC_ASSERT(info.IsIndirect()); CJC_ASSERT(argIdx < args.size()); auto& arg = args[argIdx]; auto copy = builder.CreateEntryAlloca(*arg->GetCGType()->GetPointerElementType(), "arg.copy"); llvm::MaybeAlign align(GetTypeAlignment(cgMod, *const_cast(arg->GetLLVMType()))); builder.CreateMemCpy(copy, align, arg->GetRawValue(), align, GetTypeSize(cgMod, chirParamTy)); arg = cgMod.CreateGhostCFuncArgValue(*copy, *arg->GetCGType()); } else { CJC_ASSERT(false && "ArgInfo in Windows x64 cannot be Expand."); } } llvm::Type* WindowsAmd64CJNativeCGCFFI::GetStructReturnType(CHIR::StructType& chirTy, std::vector& params) { auto& llvmCtx = ctx.GetLLVMContext(); auto argInfo = GetMappingArgInfo(chirTy, false); if (!argInfo.IsIndirect()) { CJC_ASSERT(argInfo.IsDirect() && "ArgInfo in Windows x64 cannot be Expand."); return argInfo[0]; } auto structType = GetLLVMType(chirTy); CJC_ASSERT(structType->isStructTy()); params.emplace_back(structType->getPointerTo()); return llvm::Type::getVoidTy(llvmCtx); } ProcessKind WindowsAmd64CJNativeCGCFFI::GetParamType(CHIR::Type& chirTy, std::vector& params) { if (chirTy.IsStruct()) { ABIArgInfo info = GetMappingArgInfo(StaticCast(chirTy), true); llvm::Type* structType = GetLLVMType(chirTy); CJC_ASSERT(structType->isStructTy()); if (info.IsIndirect()) { params.emplace_back(structType->getPointerTo()); return ProcessKind::INDIRECT; } CJC_ASSERT(info.IsDirect() && "ArgInfo in Windows x64 cannot be Expand."); params.emplace_back(info[0]); return ProcessKind::DIRECT; } llvm::Type* paramType = GetLLVMType(chirTy); // VArray as CFunc parament pass by a i8*. if (chirTy.IsVArray()) { paramType = llvm::Type::getInt8PtrTy(ctx.GetLLVMContext()); } params.emplace_back(paramType); return ProcessKind::NO_PROCESS; } ABIArgInfo WindowsAmd64CJNativeCGCFFI::GetMappingArgInfo(CHIR::StructType& chirTy, [[maybe_unused]] bool isArg) { if (auto found = typeMap.find(&chirTy); found != typeMap.end()) { return found->second; } auto type = GetLLVMType(chirTy); size_t size = GetTypeSize(cgMod, *type); const size_t limitSizeOfSmallStruct = 8; if (size > limitSizeOfSmallStruct || Utils::NotIn(size, {1, 2, 4, 8})) { return typeMap.emplace(&chirTy, ABIArgInfo::GetIndirect()).first->second; } llvm::Type* resTy = llvm::IntegerType::get(ctx.GetLLVMContext(), static_cast(AsBits(size))); return typeMap.emplace(&chirTy, ABIArgInfo::GetDirect(resTy)).first->second; } llvm::FunctionType* LinuxOhosArm32CJNativeCGCFFI::GetCFuncType(const CHIR::FuncType& chirFuncTy) { auto found = cfuncMap.find(&chirFuncTy); if (found != cfuncMap.end()) { return found->second.first; } CJC_ASSERT(chirFuncTy.IsCFunc()); std::vector paramTys; auto chirParamTys = chirFuncTy.GetParamTypes(); paramTys.reserve(chirParamTys.size()); std::vector kinds; kinds.reserve(chirParamTys.size()); auto retType = GetReturnType(*chirFuncTy.GetReturnType(), paramTys); if (retType == nullptr) { return nullptr; } for (auto ty : chirParamTys) { CJC_ASSERT(IsMetCType(*ty)); if (IsUnsizedStructTy(*ty)) { return nullptr; } auto typeSize = GetTypeSize(cgMod, *ty); if (IsUnitOrNothing(*ty) || typeSize == 0) { kinds.emplace_back(ProcessKind::SKIP); continue; } kinds.emplace_back(GetParamType(*ty, paramTys)); } auto resTy = llvm::FunctionType::get(retType, paramTys, chirFuncTy.HasVarArg()); cfuncMap.emplace(&chirFuncTy, std::make_pair(resTy, kinds)); return resTy; } void LinuxOhosArm32CJNativeCGCFFI::ProcessParam( CHIR::Type& chirParamTy, LLVMFuncArgIt& arg, llvm::Value* place, IRBuilder2& builder) { if (!chirParamTy.IsStruct()) { return; } auto found = paramTypeMap.find(&chirParamTy); if (found == paramTypeMap.end()) { return; } auto& argInfo = found->second; if (argInfo.IsDirect()) { auto argType = arg->getType(); llvm::Value* argVal = arg; auto alloca = builder.CreateEntryAlloca(argType); builder.CreateStore(argVal, alloca); size_t actualSize = GetTypeSize(cgMod, chirParamTy); builder.CreateMemCpy(place, GetAlign(cgMod, chirParamTy), alloca, GetAlign(cgMod, *argType), actualSize); } CJC_ASSERT(!argInfo.IsExpand() && "ArgInfo in arm32 cannot be Expand."); } void LinuxOhosArm32CJNativeCGCFFI::ProcessInvocationArg( CHIR::StructType& chirParamTy, ProcessKind kind, size_t& argIdx, std::vector& args, IRBuilder2& builder) { if (kind == ProcessKind::INDIRECT) { return; } if (kind != ProcessKind::DIRECT) { CJC_ASSERT(false && "ArgInfo in arm32 cannot be Expand."); return; } auto found = paramTypeMap.find(&chirParamTy); CJC_ASSERT(found != paramTypeMap.end()); auto& info = found->second; CJC_ASSERT(info.IsDirect()); CJC_ASSERT(argIdx < args.size()); auto arg = args[argIdx]; size_t actualSize = GetTypeSize(cgMod, chirParamTy); if (info[0]->isIntegerTy()) { auto numBits = static_cast(AsBits(actualSize)); auto actualIntTy = llvm::IntegerType::get(ctx.GetLLVMContext(), numBits); const unsigned int addrSpace = arg->GetRawValue()->getType()->getPointerAddressSpace(); auto tmpVal = builder.CreateBitCast(arg->GetRawValue(), actualIntTy->getPointerTo(addrSpace)); tmpVal = builder.LLVMIRBuilder2::CreateLoad(actualIntTy, tmpVal); if (GetTypeSize(cgMod, *info[0]) != actualSize) { tmpVal = builder.CreateIntCast(tmpVal, info[0], false); } args[argIdx] = cgMod.CreateGhostCFuncArgValue(*tmpVal, *GetCGType(cgMod, *info[0])); } else { CJC_ASSERT(info[0]->isArrayTy()); auto tmpVal = builder.CreateEntryAlloca(info[0], nullptr, "arg.copy"); builder.CreateMemCpy( tmpVal, GetAlign(cgMod, *info[0]), arg->GetRawValue(), GetAlign(cgMod, chirParamTy), actualSize); args[argIdx] = cgMod.CreateGhostCFuncArgValue( *builder.LLVMIRBuilder2::CreateLoad(info[0], tmpVal), *GetCGType(cgMod, *info[0])); } } llvm::Type* LinuxOhosArm32CJNativeCGCFFI::GetStructReturnType(CHIR::StructType& chirTy, std::vector& params) { auto& llvmCtx = ctx.GetLLVMContext(); if (GetTypeSize(cgMod, chirTy) == 0) { return llvm::Type::getVoidTy(llvmCtx); } auto argInfo = GetMappingArgInfo(chirTy, false); if (argInfo.IsIndirect()) { auto structType = GetLLVMType(chirTy); CJC_ASSERT(structType->isStructTy()); params.emplace_back(structType->getPointerTo()); return llvm::Type::getVoidTy(llvmCtx); } CJC_ASSERT(argInfo.IsDirect() && "ArgInfo in arm32 cannot be Expand."); return argInfo[0]; } ProcessKind LinuxOhosArm32CJNativeCGCFFI::GetParamType(CHIR::Type& chirTy, std::vector& params) { if (chirTy.IsStruct()) { auto info = GetMappingArgInfo(StaticCast(chirTy), true); auto structType = GetLLVMType(chirTy); CJC_ASSERT(structType->isStructTy()); if (info.IsIndirect()) { params.emplace_back(structType->getPointerTo()); return ProcessKind::INDIRECT; } else { CJC_ASSERT(info.IsDirect() && "ArgInfo in arm32 cannot be Expand."); params.emplace_back(info[0]); return ProcessKind::DIRECT; } } llvm::Type* paramType = GetLLVMType(chirTy); // VArray as CFunc parament pass by a i8*. if (chirTy.IsVArray()) { paramType = llvm::Type::getInt8PtrTy(ctx.GetLLVMContext()); } params.emplace_back(paramType); return ProcessKind::NO_PROCESS; } ABIArgInfo LinuxOhosArm32CJNativeCGCFFI::GetMappingArgInfo(CHIR::StructType& chirTy, bool isArg) { if (isArg) { auto found = paramTypeMap.find(&chirTy); if (found != paramTypeMap.end()) { return found->second; } } else { auto found = typeMap.find(&chirTy); if (found != typeMap.end()) { return found->second; } } auto type = GetLLVMType(chirTy); size_t size = GetTypeSize(cgMod, *type); auto& llvmCtx = ctx.GetLLVMContext(); if (isArg) { size_t alignment = GetTypeAlignment(cgMod, *type); llvm::Type* baseTy = nullptr; if (alignment <= 4) { alignment = 4; baseTy = llvm::Type::getInt32Ty(llvmCtx); } else if (alignment >= 8) { alignment = 8; baseTy = llvm::Type::getInt64Ty(llvmCtx); } else { CJC_ASSERT(true && "Alignment should not be between 4 and 8 bytes."); } size = llvm::alignTo(size, alignment); llvm::Type* resTy = llvm::ArrayType::get(baseTy, static_cast(size) / alignment); return paramTypeMap.emplace(&chirTy, ABIArgInfo::GetDirect(resTy)).first->second; } const size_t limitSizeOfSmallStruct = 4; if (size > limitSizeOfSmallStruct) { return typeMap.emplace(&chirTy, ABIArgInfo::GetIndirect()).first->second; } else { llvm::Type* resTy = llvm::Type::getInt32Ty(llvmCtx); return typeMap.emplace(&chirTy, ABIArgInfo::GetDirect(resTy)).first->second; } } cangjie_compiler-1.0.7/src/CodeGen/CJNative/CJNativeCGCFFI.h000066400000000000000000000251171510705540100232310ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file defines classes used for cjnative backend to generate code related to the CFFI feature. */ #ifndef CANGJIE_CODEGEN_CJNATIVECGCFFI_H #define CANGJIE_CODEGEN_CJNATIVECGCFFI_H #include "CGContext.h" #include "CJNative/CGCFFI.h" #include "IRBuilder.h" #include "cangjie/Utils/SafePointer.h" namespace Cangjie { namespace CodeGen { // Maximum number of a small struct type can be splited. const unsigned MAX_SPLIT_NUM = 2; class ABIArgInfo { public: virtual ~ABIArgInfo() = default; /** * Pass or return the type indirectly via a hidden pointer. */ static ABIArgInfo GetIndirect() { return {}; } /** * Pass or return the type directly using the normal converted LLVM IR type. */ static ABIArgInfo GetDirect(llvm::Type* ty) { auto ai = ABIArgInfo(); ai.types.emplace_back(ty); return ai; } /** * Only valid for aggregate types. * The structure should be expanded into consecutive type for its constituent fields when as parameter type. * Or be wrapped in a literal struct type when as return type. */ static ABIArgInfo GetExpand(llvm::Type* ty1, llvm::Type* ty2) { auto ai = ABIArgInfo(); ai.types.emplace_back(ty1); ai.types.emplace_back(ty2); return ai; } bool IsDirect() const { return types.size() == 1; } bool IsIndirect() const { return types.empty(); } bool IsExpand() const { return types.size() == MAX_SPLIT_NUM; } size_t ArgNums() const { return types.size(); } llvm::Type* operator[](const size_t idx) const { CJC_ASSERT(idx < types.size()); return types[idx]; } private: ABIArgInfo() = default; llvm::SmallVector types; }; enum class ProcessKind : uint8_t { NO_PROCESS, SKIP, DIRECT, INDIRECT, EXPAND }; enum class WordClass : uint8_t { NO_CLASS, // Initial value used by classification algorithm. INTEGER, // Contains Bool, Int*, UInt*, CPointer, CString in Cangjie. SSE, // Contains Float*. SSEUP, // Useless in Cangjie's CType currently. X87, // Useless in Cangjie's CType currently. X87UP, // Useless in Cangjie's CType currently. COMPLEX_X87, // Useless in Cangjie's CType currently. MEMORY // Other. Types that will be passed and returned in memory via the stack. }; // Maximum number of fields that a small struct type can contain. const unsigned MAX_FIELD_NUM = 8; class LinuxCJNativeCGCFFI : public CGCFFI { public: explicit LinuxCJNativeCGCFFI(CGModule& cgMod) : ctx(cgMod.GetCGContext()), cgMod(cgMod) { } bool ProcessInvocation( const CHIR::FuncType& chirFuncTy, std::vector& args, IRBuilder2& builder) override; bool NeedProcessParam(const CHIR::FuncType& chirFuncTy, size_t idx) override { auto found = cfuncMap.find(&chirFuncTy); if (found == cfuncMap.end()) { return false; } auto paramTys = chirFuncTy.GetParamTypes(); CJC_ASSERT(idx < paramTys.size() && paramTys[idx]); if (paramTys[idx]->IsVArray()) { return true; } auto& kinds = found->second.second; return idx < kinds.size() && (kinds[idx] == ProcessKind::DIRECT || kinds[idx] == ProcessKind::EXPAND); } protected: /** * Get the codegen return type. * If it needs to be passed through parameter (sret), it will be emplace in @p params and return a void type. * If it is a incomplete struct type, this method will return nullptr * and related cfunc type will be a literal struct pointer type. * Otherwise, the actual return type will be returned. */ llvm::Type* GetReturnType(CHIR::Type& chirTy, std::vector& params); bool IsUnsizedStructTy(const CHIR::Type& chirTy) const { return chirTy.IsStruct() && !GetLLVMType(chirTy)->isSized(); } llvm::Type* GetLLVMType(const CHIR::Type& chirTy) const { auto cgType = CGType::GetOrCreate(cgMod, &chirTy); CJC_NULLPTR_CHECK(cgType); return cgType->GetLLVMType(); } virtual void ProcessInvocationArg(CHIR::StructType& chirParamTy, ProcessKind kind, size_t& argIdx, std::vector& args, IRBuilder2& builder) = 0; virtual llvm::Type* GetStructReturnType(CHIR::StructType& chirTy, std::vector& params) = 0; std::unordered_map, ABIArgInfo> typeMap; std::unordered_map, std::pair>> cfuncMap; CGContext& ctx; CGModule& cgMod; }; class LinuxAmd64CJNativeCGCFFI : public LinuxCJNativeCGCFFI { public: using FieldOffsets = llvm::SmallVector; explicit LinuxAmd64CJNativeCGCFFI(CGModule& cgMod) : LinuxCJNativeCGCFFI(cgMod) { } llvm::FunctionType* GetCFuncType(const CHIR::FuncType& chirFuncTy) override; void AddFunctionAttr(const CHIR::FuncType& chirFuncTy, llvm::Function& llvmFunc) override; llvm::Value* ProcessRetValue(const llvm::Type& retType, llvm::Value& val, IRBuilder2& builder) override; void ProcessParam(CHIR::Type& chirParamTy, LLVMFuncArgIt& arg, llvm::Value* place, IRBuilder2& builder) override; protected: void ProcessInvocationArg(CHIR::StructType& chirParamTy, ProcessKind kind, size_t& argIdx, std::vector& args, IRBuilder2& builder) override; llvm::Type* GetStructReturnType(CHIR::StructType& chirTy, std::vector& params) override; private: ProcessKind GetParamType(CHIR::Type& chirTy, int8_t& intNum, int8_t& sseNum, std::vector& params); llvm::Type* GetParamType(llvm::Type* ty, const ABIArgInfo& info, int8_t& intNum, int8_t& sseNum) const; ABIArgInfo GetMappingArgInfo(CHIR::StructType& chirTy, bool isArg); llvm::Type* GetLowPartType(llvm::Type* type, std::vector& classes, bool isArg) const; std::vector ClassifyType(llvm::Type& type) const; void ClassifyType(llvm::Type& type, const std::vector::iterator begin, size_t offset) const; void ClassifyStructType( llvm::ArrayRef fields, const std::vector::iterator begin, size_t offset) const; void FixupClassification(const llvm::Type& type, std::vector& wordClasses) const; llvm::Type* GetIntegerTypeWithOffset( llvm::Type& curType, size_t offset, llvm::Type& srcType, size_t srcOffset) const; llvm::Type* GetSseTypeWithOffset(llvm::Type& curType, size_t offset, llvm::Type& srcType, size_t srcOffset) const; bool ContainsUselessBits(llvm::Type& type, size_t start, size_t end) const; FieldOffsets CalcFieldOffsets(const llvm::StructType& type) const; unsigned GetFieldContainsOffset(FieldOffsets fieldOffsets, size_t offset) const; bool ContainsFloatAtOffset(llvm::Type& type, size_t offset) const; }; class LinuxAarch64CJNativeCGCFFI : public LinuxCJNativeCGCFFI { public: explicit LinuxAarch64CJNativeCGCFFI(CGModule& cgMod) : LinuxCJNativeCGCFFI(cgMod) { } llvm::FunctionType* GetCFuncType(const CHIR::FuncType& chirFuncTy) override; void AddFunctionAttr(const CHIR::FuncType& chirFuncTy, llvm::Function& llvmFunc) override; void ProcessParam(CHIR::Type& chirParamTy, LLVMFuncArgIt& arg, llvm::Value* place, IRBuilder2& builder) override; llvm::Value* ProcessRetValue(const llvm::Type& retType, llvm::Value& val, IRBuilder2& builder) override; protected: void ProcessInvocationArg(CHIR::StructType& chirParamTy, ProcessKind kind, size_t& argIdx, std::vector& args, IRBuilder2& builder) override; llvm::Type* GetStructReturnType(CHIR::StructType& chirTy, std::vector& params) override; bool IsHomogeneousAggregate(llvm::Type& type, llvm::Type*& base, size_t& members); private: ProcessKind GetParamType(CHIR::Type& chirTy, std::vector& params); ABIArgInfo GetMappingArgInfo(CHIR::StructType& chirTy, bool isArg); }; #ifdef __APPLE__ class MacAArch64CJNativeCGCFFI : public LinuxAarch64CJNativeCGCFFI { public: explicit MacAArch64CJNativeCGCFFI(CGModule& cgMod) : LinuxAarch64CJNativeCGCFFI(cgMod) { } void AddFunctionAttr(const CHIR::FuncType& chirFuncTy, llvm::Function& llvmFunc) override; protected: llvm::Type* GetStructReturnType(CHIR::StructType& chirTy, std::vector& params) override; }; #endif class WindowsAmd64CJNativeCGCFFI : public LinuxCJNativeCGCFFI { public: explicit WindowsAmd64CJNativeCGCFFI(CGModule& cgMod) : LinuxCJNativeCGCFFI(cgMod) { } llvm::FunctionType* GetCFuncType(const CHIR::FuncType& chirFuncTy) override; void ProcessParam(CHIR::Type& chirParamTy, LLVMFuncArgIt& arg, llvm::Value* place, IRBuilder2& builder) override; llvm::Value* ProcessRetValue(const llvm::Type& retType, llvm::Value& val, IRBuilder2& builder) override; void AddFunctionAttr(const CHIR::FuncType& chirFuncTy, llvm::Function& llvmFunc) override; protected: llvm::Type* GetStructReturnType(CHIR::StructType& chirTy, std::vector& params) override; void ProcessInvocationArg(CHIR::StructType& chirParamTy, ProcessKind kind, size_t& argIdx, std::vector& args, IRBuilder2& builder) override; private: ProcessKind GetParamType(CHIR::Type& chirTy, std::vector& params); ABIArgInfo GetMappingArgInfo(CHIR::StructType& chirTy, bool isArg); }; class LinuxOhosArm32CJNativeCGCFFI : public LinuxAarch64CJNativeCGCFFI { public: explicit LinuxOhosArm32CJNativeCGCFFI(CGModule& cgMod) : LinuxAarch64CJNativeCGCFFI(cgMod) { } llvm::FunctionType* GetCFuncType(const CHIR::FuncType& chirFuncTy) override; void ProcessParam(CHIR::Type& chirParamTy, LLVMFuncArgIt& arg, llvm::Value* place, IRBuilder2& builder) override; protected: void ProcessInvocationArg(CHIR::StructType& chirParamTy, ProcessKind kind, size_t& argIdx, std::vector& args, IRBuilder2& builder) override; llvm::Type* GetStructReturnType(CHIR::StructType& chirTy, std::vector& params) override; std::unordered_map, ABIArgInfo> paramTypeMap; private: ProcessKind GetParamType(CHIR::Type& chirTy, std::vector& params); ABIArgInfo GetMappingArgInfo(CHIR::StructType& chirTy, bool isArg); }; } // namespace CodeGen } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/CodeGen/CJNative/CJNativeDIBuilder.cpp000066400000000000000000000314511510705540100244440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "DIBuilder.h" namespace Cangjie { namespace CodeGen { llvm::DIType* DIBuilder::CreateRefType() { if (!refDIType) { // 64-bits is size of int64 pointer type. llvm::DIType* basicType = createBasicType("Int64", 64u, static_cast(llvm::dwarf::DW_ATE_signed)); refDIType = CreatePointerType(basicType); } return refDIType; } llvm::DIType* DIBuilder::GetOrCreateObjectClassType() { if (!objDIType) { auto tag = static_cast(llvm::dwarf::DW_TAG_class_type); llvm::DICompositeType* fwdDecl = createReplaceableCompositeType(tag, OBJECT_NAME, diCompileUnit, defaultFile, 0, 0, 64u, 0u, llvm::DINode::FlagTypePassByReference, OBJECT_NAME); objDIType = llvm::MDNode::replaceWithDistinct(llvm::TempDICompositeType(fwdDecl)); } return objDIType; } llvm::DIType* DIBuilder::CreateClosureType(const CHIR::Type& ty) { auto classTy = StaticCast(&ty); auto identifier = ty.ToString(); auto name = GenerateTypeName(*classTy); auto& position = classTy->GetClassDef()->GetDebugLocation(); auto diFile = GetOrCreateFile(position); auto tag = static_cast(llvm::dwarf::DW_TAG_class_type); llvm::DICompositeType* fwdDecl = createReplaceableCompositeType(tag, name, diNamespace, diFile, position.GetBeginPos().line, 0, 128u, 0u, llvm::DINode::FlagTypePassByReference, identifier); fwdDecl = llvm::MDNode::replaceWithDistinct(llvm::TempDICompositeType(fwdDecl)); // SmallVector Size: 8. llvm::SmallVector eltTys; // Function return type. auto [paramTypes, retType] = GetFuncTypeWithoutThisPtrFromAutoEnvBaseType(*classTy); eltTys.push_back(GetOrCreateType(*retType)); for (auto paramType : paramTypes) { llvm::DIType* cgType = GetOrCreateType(*paramType); eltTys.push_back(cgType); } llvm::DISubroutineType* subroutineType = createSubroutineType(getOrCreateTypeArray(eltTys)); auto funcType = CreatePointerType(subroutineType, 64); auto tiType = createMemberType( fwdDecl, "$ti", diFile, 0u, CreateRefType()->getSizeInBits(), 0u, 0u, llvm::DINode::FlagZero, CreateRefType()); auto classPointerType = createMemberType( fwdDecl, "ptr", diFile, 0u, CreateRefType()->getSizeInBits(), 0u, 64u, llvm::DINode::FlagZero, funcType); replaceArrays(fwdDecl, getOrCreateArray({tiType, classPointerType})); return fwdDecl; } uint64_t DIBuilder::GetClassSize(llvm::Type* classLayout) const { // For cjnative backend, just get the size information from classLayout. return GetTypeSize(classLayout); } llvm::DIType* DIBuilder::CreateInterfaceType(const CHIR::ClassType& interfaceTy) { auto interfaceDef = interfaceTy.GetClassDef(); auto mangledName = interfaceTy.ToString(); auto& position = interfaceDef->GetDebugLocation(); auto name = "Interface$" + RemoveCustomTypePrefix(GenerateTypeName(interfaceTy)); auto diFile = GetOrCreateFile(position); auto classSize = 64u; auto defPackage = createNameSpace(diCompileUnit, interfaceDef->GetPackageName(), false); auto tag = static_cast(llvm::dwarf::DW_TAG_class_type); llvm::DICompositeType* fwdDecl = createReplaceableCompositeType(tag, name, defPackage, diFile, position.GetBeginPos().line, 0, classSize, 0u, llvm::DINode::FlagTypePassByReference, mangledName); fwdDecl = llvm::MDNode::replaceWithDistinct(llvm::TempDICompositeType(fwdDecl)); typeCache[&interfaceTy] = llvm::TrackingMDRef(fwdDecl); CodeGenDIVector16 elements; CreateInheritedInterface(elements, fwdDecl, interfaceTy); for (auto funcInfo : interfaceTy.GetInstAbstractMethodTypes(cgMod.GetCGContext().GetCHIRBuilder())) { auto funcIdentifier = GenerateGenericFuncName(funcInfo.methodName, funcInfo.methodGenericTypeParams); auto funcName = funcInfo.GetASTMangledName(); bool hasThis = funcInfo.TestAttr(CHIR::Attribute::STATIC) ? false : true; auto funcType = CreateFuncType(StaticCast(funcInfo.methodTy), false, hasThis); llvm::DINode::DIFlags flags = llvm::DINode::FlagPrototyped; llvm::DISubprogram::DISPFlags spFlags = funcInfo.hasBody ? llvm::DISubprogram::SPFlagZero : llvm::DISubprogram::SPFlagPureVirtual; auto funcElement = createFunction(fwdDecl, funcIdentifier, funcName, diFile, 0u, funcType, 0u, flags, spFlags); elements.emplace_back(funcElement); finalizeSubprogram(funcElement); } CreateGetGenericFunc(interfaceTy, fwdDecl, elements); replaceArrays(fwdDecl, getOrCreateArray(elements)); return createTypedef(fwdDecl, RemoveCustomTypePrefix(GenerateTypeName(interfaceTy)), diFile, 0, diCompileUnit); } void DIBuilder::CreateClassMemberType(const CHIR::ClassType& classTy, llvm::Type* classLayout, CodeGenDIVector16& elements, llvm::DICompositeType* fwdDecl) { auto classDef = classTy.GetClassDef(); auto& position = classDef->GetDebugLocation(); auto diFile = GetOrCreateFile(position); auto layout = cgMod.GetLLVMModule()->getDataLayout().getStructLayout(llvm::cast(classLayout)); // the new objLayout of Class does not have object. CHIR::CHIRBuilder& chirBuilder = cgMod.GetCGContext().GetCHIRBuilder(); auto& nonConstClassType = const_cast(classTy); CJC_ASSERT(nonConstClassType.GetSuperClassTy(&chirBuilder)); uint32_t i = nonConstClassType.GetSuperClassTy(&chirBuilder)->GetInstantiatedMemberTys(chirBuilder).size(); CJC_ASSERT(nonConstClassType.GetSuperClassTy(&chirBuilder)->GetInstantiatedMemberTys(chirBuilder).size() + classDef->GetDirectInstanceVarNum() == nonConstClassType.GetInstantiatedMemberTys(chirBuilder).size()); for (auto& it : classDef->GetDirectInstanceVars()) { if (it.TestAttr(CHIR::Attribute::NO_DEBUG_INFO) && classDef->GetSrcCodeIdentifier().rfind(CC_DEF_PREFIX, 0) == 0) { ++i; continue; } uint32_t elementIndex = IsClosureConversionEnvClass(*classDef) ? i + 2U : i; auto elementTy = nonConstClassType.GetInstantiatedMemberTys(chirBuilder)[i]; auto elemType = GetOrCreateType(*elementTy); auto cgElemType = classLayout->getStructElementType(elementIndex); if (IsReferenceType(*elementTy, cgMod)) { elemType = CreatePointerType(elemType, CreateRefType()->getSizeInBits()); } auto bitInSize = GetTypeSize(cgElemType); auto offset = layout->getElementOffsetInBits(elementIndex) + 64; auto align = layout->getAlignment().value(); auto memberType = createMemberType(fwdDecl, it.name, diFile, it.loc.GetBeginPos().line, bitInSize, static_cast(align), offset, llvm::DINode::FlagZero, elemType); elements.push_back(memberType); ++i; } } // Array.Type has 3 elements. void DIBuilder::CreateArrayMemberType(const CHIR::RawArrayType& arrTy, llvm::DICompositeType* fwdDecl, CodeGenDIVector3& elements, llvm::Type* arrayLayout) { auto layout = cgMod.GetLLVMModule()->getDataLayout().getStructLayout(llvm::cast(arrayLayout)); auto pointerSize = CreateRefType()->getSizeInBits(); auto sizeType = createBasicType("Int64", 64u, static_cast(llvm::dwarf::DW_ATE_unsigned)); // int64 type : size. CJC_NULLPTR_CHECK(sizeType); auto memberType = createMemberType( fwdDecl, "$ti", defaultFile, 0u, pointerSize, 0u, 0u, llvm::DINode::FlagArtificial, CreateRefType()); elements.push_back(memberType); memberType = createMemberType( fwdDecl, "size", defaultFile, 0u, sizeType->getSizeInBits(), 0u, pointerSize, llvm::DINode::FlagZero, sizeType); // Offset for element is size of ArrayBase for chir2llvm. elements.push_back(memberType); auto elemTy = arrTy.GetElementType(); auto elemType = GetOrCreateType(*elemTy); // Multi-dimension array or class/enum is pointer element. if (arrTy.GetDims() > 1 || IsReferenceType(*elemTy, cgMod)) { elemType = CreatePointerType(elemType, CreateRefType()->getSizeInBits()); } auto bitInSize = GetSizeInBits(elemType); // layout is %ArrayBase, which is behind an object. auto offset = layout->getElementOffsetInBits(1) + 64u; auto align = layout->getAlignment().value(); memberType = createMemberType(fwdDecl, "elements", defaultFile, 0u, bitInSize, static_cast(align), offset, llvm::DINode::FlagZero, elemType); elements.push_back(memberType); replaceArrays(fwdDecl, getOrCreateArray(elements)); } llvm::DICompositeType* DIBuilder::CreateEnumWithArgsType( const CHIR::EnumType& enumTy, CodeGenDIVector16& enumMembers, const CHIR::Type* boxTy) { /** * The Enum is stored as * 64-bits // TypeInfo* of ctor * 32-bits // constructor * ... // arg */ auto enumDef = enumTy.GetEnumDef(); auto defPackage = createNameSpace(diCompileUnit, enumDef->GetPackageName(), false); auto& position = enumDef->GetDebugLocation(); auto diFile = GetOrCreateFile(position); auto name = RemoveCustomTypePrefix(GenerateTypeName(enumTy)); uint64_t maxSize = 0; for (size_t ctorIndex = 0; ctorIndex < enumTy.GetConstructorInfos(cgMod.GetCGContext().GetCHIRBuilder()).size(); ctorIndex++) { auto enumCtorlayoutType = CGType::GetOrCreateEnumCtorLayoutType(cgMod, enumTy, ctorIndex, enumTy.GetConstructorInfos(cgMod.GetCGContext().GetCHIRBuilder())[ctorIndex].funcType->GetParamTypes()); maxSize = std::max(maxSize, GetTypeSize(enumCtorlayoutType)); } // add size of typeInfo maxSize += 64u; auto fwdDecl = createStructType(defPackage, "E1$" + name, diFile, position.GetBeginPos().line, maxSize, 0u, llvm::DINode::FlagVirtual, nullptr, {}, 0u, nullptr, "$" + enumTy.ToString()); if (boxTy) { typeCache[boxTy] = llvm::TrackingMDRef(fwdDecl); } else { typeCache[&enumTy] = llvm::TrackingMDRef(fwdDecl); } // Generate all constructors as the subclass for the enum. std::size_t ctorIndex = 0; for (auto& ctor : enumTy.GetConstructorInfos(cgMod.GetCGContext().GetCHIRBuilder())) { llvm::DIType* enumClassType = CreateEnumClassType(enumTy, ctor, fwdDecl, ctorIndex); auto inheritType = createInheritance(fwdDecl, enumClassType, 0u, 0u, llvm::DINode::FlagZero); enumMembers.push_back(inheritType); ++ctorIndex; } return fwdDecl; } llvm::DICompositeType* DIBuilder::CreateEnumClassType(const CHIR::EnumType& enumTy, const CHIR::EnumCtorInfo& constructor, llvm::DICompositeType* subEnumType, std::size_t ctorIndex) { CodeGenDIVector4 enumClassElements{}; auto enumCtorlayoutType = CGType::GetOrCreateEnumCtorLayoutType(cgMod, enumTy, ctorIndex, constructor.funcType->GetParamTypes()); CJC_ASSERT(!enumCtorlayoutType->isOpaque() && "The body is not set for enumCtorlayoutType."); uint64_t sizeofPointer = 64u; auto totalSize = GetTypeSize(enumCtorlayoutType) + sizeofPointer; auto enumDef = enumTy.GetEnumDef(); auto& position = enumDef->GetDebugLocation(); auto diFile = GetOrCreateFile(position); auto layout = cgMod.GetLLVMModule()->getDataLayout().getStructLayout(enumCtorlayoutType); auto name = RemoveCustomTypePrefix(GenerateTypeName(enumTy)); std::string ctorName = name + "_ctor_" + std::to_string(ctorIndex); auto enumClassType = createStructType(subEnumType, ctorName, diFile, 0u, totalSize, 0u, llvm::DINode::FlagZero, nullptr, {}); auto constructorType = GetOrCreateEnumCtorType(enumTy); auto ctorType = createMemberType( enumClassType, "constructor", diFile, 0u, 32u, 0u, 64u, llvm::DINode::FlagZero, constructorType); enumClassElements.push_back(ctorType); // in ObjLayout, the first element is constructor. size_t argIndex = 1; for (auto argTy : constructor.funcType->GetParamTypes()) { auto argType = GetOrCreateType(*argTy); if (IsReferenceType(*argTy, cgMod) || argTy->IsRawArray()) { argType = CreatePointerType(argType, CreateRefType()->getSizeInBits()); } auto offset = layout->getElementOffsetInBits(argIndex) + sizeofPointer; auto align = layout->getAlignment().value(); auto argMember = createMemberType(enumClassType, "arg_" + std::to_string(argIndex), diFile, 0u, GetSizeInBits(argType), static_cast(align), offset, llvm::DINode::FlagZero, argType); enumClassElements.push_back(argMember); ++argIndex; } replaceArrays(enumClassType, getOrCreateArray(enumClassElements)); return enumClassType; } } // namespace CodeGen } // namespace Cangjie cangjie_compiler-1.0.7/src/CodeGen/CJNative/CJNativeGenMetadata.cpp000066400000000000000000001040321510705540100250070ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * Implements metadata generator. */ #include "CJNative/CJNativeMetadata.h" #include "Base/CGTypes/CGEnumType.h" #include "IRBuilder.h" #include "Utils/CGUtils.h" using namespace Cangjie; using namespace CodeGen; using ChirTypeKind = Cangjie::CHIR::Type::TypeKind; using CGEnumKind = Cangjie::CodeGen::CGEnumType::CGEnumTypeKind; namespace { inline bool IsClassForBoxType(const std::string& className) { return className.find(BOX_DECL_PREFIX) != std::string::npos; } std::string GetEnumKindName(CGModule& cgMod, const CHIR::EnumDef& ed) { auto cgType = StaticCast(CGType::GetOrCreate(cgMod, ed.GetType())); switch (cgType->GetCGEnumTypeKind()) { case CGEnumKind::NON_EXHAUSTIVE_UNASSOCIATED: case CGEnumKind::EXHAUSTIVE_ZERO_SIZE: case CGEnumKind::EXHAUSTIVE_UNASSOCIATED: return "enumKind0"; case CGEnumKind::NON_EXHAUSTIVE_ASSOCIATED: case CGEnumKind::EXHAUSTIVE_OTHER: return "enumKind1"; case CGEnumKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_REF: case CGEnumKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_NONREF: case CGEnumKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_T: return "enumKind2"; case CGEnumKind::EXHAUSTIVE_ASSOCIATED_NONREF: return "enumKind3"; default: CJC_ASSERT(false && "should not reach here"); return "UNKNOWN"; } } uint8_t GetSRetMode(CHIR::Type& retTy, const CodeGen::CGFunction& cgFunc) { if (!cgFunc.IsSRet()) { return SRetMode::NO_SRET; } if (!retTy.IsGenericRelated()) { return SRetMode::SRET_NON_GENERIC; } if (retTy.IsGeneric()) { return SRetMode::SRET_GENERIC; } auto sretTy = cgFunc.GetRawFunction()->getArg(0)->getType(); if (sretTy->isPointerTy()) { if (sretTy->getPointerAddressSpace() == 0) { return SRetMode::SRET_KNOWN_GENERIC_CUSTOM; } return SRetMode::SRET_UNKNOWN_GENERIC_CUSTOM; } CJC_ASSERT(false && "should not reach here"); return SRetMode::SRET_NON_GENERIC; } } // namespace llvm::MDTuple* MetadataInfo::GenerateParametersMetadata( const std::vector& argsInfo, bool hasThis) const { llvm::LLVMContext& llvmCtx = module.GetLLVMContext(); MetadataVector res(llvmCtx); for (auto arg : argsInfo) { auto argId = arg->GetSrcCodeIdentifier(); // If this is non-static member function, the first parameter is "this" added by the compiler. // And we don't generate metadata for "this" since it is known by runtime. if (arg == argsInfo.front() && hasThis) { continue; } // {!param name, !type name, !attributes} res.AddSubItem(MetadataVector(llvmCtx) .Concat(argId) .Concat(GetTiName(*arg->GetType())) .Concat(GenerateAttrsMetadata(CHIR::AttributeInfo(), {}, arg->GetAnnoInfo().mangledName))); } return res.CreateMDTuple(); } llvm::MDTuple* MetadataInfo::GenerateParametersMetadata( const std::vector& paramInfos, bool hasThis) const { llvm::LLVMContext& llvmCtx = module.GetLLVMContext(); MetadataVector res(llvmCtx); for (auto& param : paramInfos) { auto paramId = param.paramName; // If this is non-static member function, the first parameter is "this" added by the compiler. // And we don't generate metadata for "this" since it is known by runtime. if (¶m == ¶mInfos.front() && hasThis) { continue; } // {!param name, !type name, !attributes} res.AddSubItem(MetadataVector(llvmCtx) .Concat(paramId) .Concat(GetTiName(*param.type)) .Concat(GenerateAttrsMetadata(CHIR::AttributeInfo(), {}, param.annoInfo.mangledName))); } return res.CreateMDTuple(); } llvm::MDTuple* MetadataInfo::GenerateParametersMetadata(const std::vector& genericParamInfos) const { llvm::LLVMContext& llvmCtx = module.GetLLVMContext(); MetadataVector res(llvmCtx); for (auto arg : genericParamInfos) { // !{!genericParamType name} res.Concat(GetTiName(*arg)); } return res.CreateMDTuple(); } llvm::MDTuple* MetadataInfo::GenerateAttrsMetadata(const CHIR::AttributeInfo& attrs, ExtraAttribute extraAttr, const std::string& gettingAnnotationMethod, uint8_t hasSRetMode, const std::string& enumKind) const { static const std::map TEST_ATTRS{ {CHIR::Attribute::PUBLIC, "public"}, {CHIR::Attribute::PROTECTED, "protected"}, {CHIR::Attribute::PRIVATE, "private"}, {CHIR::Attribute::VIRTUAL, "open"}, {CHIR::Attribute::OVERRIDE, "override"}, {CHIR::Attribute::REDEF, "redef"}, {CHIR::Attribute::MUT, "mut"}, {CHIR::Attribute::STATIC, "static"}, {CHIR::Attribute::ABSTRACT, "abstract"}, {CHIR::Attribute::SEALED, "sealed"}, }; std::set attrsStr; switch (extraAttr) { case ExtraAttribute::METHOD_FROM_INTERFACE: attrsStr.emplace("abstract"); break; case ExtraAttribute::IMMUTABLE_FIELD: // let variable attrsStr.emplace(ATTR_IMMUTABLE); break; case ExtraAttribute::INTERFACE: attrsStr.emplace(METADATA_ATTR_OPEN); break; case ExtraAttribute::ENUM: attrsStr.emplace(enumKind); break; case ExtraAttribute::BOX_CLASS: attrsStr.emplace("box"); break; default: break; } if (hasSRetMode != SRetMode::NO_SRET) { attrsStr.emplace("hasSRet" + std::to_string(hasSRetMode)); } for (auto& attr : TEST_ATTRS) { if (attrs.TestAttr(attr.first)) { attrsStr.emplace(attr.second); } } MetadataVector ops(module.GetLLVMContext()); for (auto& str : attrsStr) { (void)ops.Concat(str); } (void)ops.Concat(gettingAnnotationMethod); if (gettingAnnotationMethod != "none") { auto chirFunc = module.GetCGContext().GetCGPkgContext().FindCHIRGlobalValue(gettingAnnotationMethod); (void)module.GetOrInsertCGFunction(chirFunc); } return ops.CreateMDTuple(); } std::string MetadataInfo::GetTiName(const CHIR::Type& ty) const { if (ty.IsRef()) { auto refTy = StaticCast(ty).GetBaseType(); return GetTiName(*refTy); } auto cgType = CGType::GetOrCreate(module, &ty); if (ty.IsGenericRelated()) { auto ti = cgType->GetOrCreateTypeInfo(); return ti->getName().str(); } if (ty.IsAutoEnvBase()) { if (ty.IsAutoEnvInstBase()) { auto superClassTy = const_cast(static_cast(ty)) .GetSuperClassTy(&module.GetCGContext().GetCHIRBuilder()); return GetTiName(static_cast(*superClassTy)); } } auto tiName = CGType::GetNameOfTypeInfoGV(ty); if (!module.GetLLVMModule()->getNamedGlobal(tiName)) { auto ti = cgType->GetOrCreateTypeInfo(); if (cgType->IsStaticGI()) { module.GetCGContext().RegisterReflectGeneratedStaticGIName(ti->getName().str()); } return ti->getName().str(); } return tiName; } llvm::MDTuple* MetadataInfo::GenerateInstanceFieldMetadata(const CHIR::MemberVarInfo& field, size_t idx) { llvm::LLVMContext& llvmCtx = module.GetLLVMContext(); return MetadataVector(llvmCtx) .Concat(idx) .Concat(field.name) .Concat(GenerateAttrsMetadata(field.attributeInfo, field.TestAttr(CHIR::Attribute::READONLY) ? ExtraAttribute::IMMUTABLE_FIELD : ExtraAttribute::MUTABLE_FIELD, field.annoInfo.mangledName)) .CreateMDTuple(); } llvm::MDTuple* MetadataInfo::GenerateStaticFieldMetadata(const CHIR::GlobalVarBase& staticField) { llvm::LLVMContext& llvmCtx = module.GetLLVMContext(); return MetadataVector(llvmCtx) .Concat(staticField.GetSrcCodeIdentifier()) .Concat(GetTiName(*staticField.GetType())) .Concat(staticField.GetIdentifierWithoutPrefix()) .Concat(GenerateAttrsMetadata(staticField.GetAttributeInfo(), staticField.TestAttr(CHIR::Attribute::READONLY) ? ExtraAttribute::IMMUTABLE_FIELD : ExtraAttribute::MUTABLE_FIELD, staticField.GetAnnoInfo().mangledName)) .CreateMDTuple(); } llvm::MDTuple* MetadataInfo::GenerateMethodMetadata(const CHIR::FuncBase& method, bool isFromInterface) { llvm::LLVMContext& llvmCtx = module.GetLLVMContext(); CJC_ASSERT(method.GetType()->IsFunc()); auto methodName = llvm::MDString::get(llvmCtx, method.GetSrcCodeIdentifier()); auto methodLinkageName = llvm::MDString::get(llvmCtx, method.GetIdentifierWithoutPrefix()); auto funcType = StaticCast(method.GetType()); auto retTypeInfo = GetTiName(*funcType->GetReturnType()); MetadataVector methodMD(llvmCtx); methodMD.Concat(methodName).Concat(retTypeInfo).Concat(methodLinkageName); auto extraAttr = isFromInterface ? ExtraAttribute::METHOD_FROM_INTERFACE : ExtraAttribute::METHOD; if (method.IsFuncWithBody()) { auto methodValue = StaticCast(&method); CJC_NULLPTR_CHECK(methodValue); uint8_t hasSRetMode = GetSRetMode(*funcType->GetReturnType(), *module.GetOrInsertCGFunction(methodValue)); methodMD .Concat(method.TestAttr(CHIR::Attribute::STATIC) ? GenerateParametersMetadata(methodValue->GetParams()) : GenerateParametersMetadata(methodValue->GetParams(), true)) .Concat(GenerateParametersMetadata(methodValue->GetGenericTypeParams())) .Concat(GenerateAttrsMetadata( method.GetAttributeInfo(), extraAttr, methodValue->GetAnnoInfo().mangledName, hasSRetMode)); (void)module.GetOrInsertCGFunction(methodValue); } else { auto importedMethodValue = StaticCast(&method); CJC_NULLPTR_CHECK(importedMethodValue); uint8_t hasSRetMode = GetSRetMode(*funcType->GetReturnType(), *module.GetOrInsertCGFunction(importedMethodValue)); methodMD .Concat(method.TestAttr(CHIR::Attribute::STATIC) ? GenerateParametersMetadata(importedMethodValue->GetParamInfo()) : GenerateParametersMetadata(importedMethodValue->GetParamInfo(), true)) .Concat(GenerateParametersMetadata(importedMethodValue->GetGenericTypeParams())) .Concat(GenerateAttrsMetadata( method.GetAttributeInfo(), extraAttr, importedMethodValue->GetAnnoInfo().mangledName, hasSRetMode)); (void)module.GetOrInsertCGFunction(importedMethodValue); } return methodMD.CreateMDTuple(); } void PkgMetadataInfo::GeneratePkgMetadata() const { auto llvmMod = module.GetLLVMModule(); auto& llvmCtx = module.GetLLVMContext(); llvm::NamedMDNode* pkgInfo = llvmMod->getOrInsertNamedMetadata(METADATA_PKG); auto names = Utils::SplitQualifiedName(module.GetCGContext().GetCurrentPkgName()); auto moduleName = names.front(); auto pkgName = module.GetCGContext().GetCurrentPkgName(); auto currentIdx = subCHIRPkg.subCHIRPackageIdx; std::string version = ""; // for dynamic loading pkgInfo->addOperand( MetadataVector(llvmCtx) .Concat(static_cast(reflectionMode == GenReflectMode::FULL_REFLECT)) .Concat(version) .Concat(moduleName) .Concat(pkgName) .Concat(subCHIRPkg.splitNum == 1 ? moduleName + "_" + pkgName : moduleName + "_" + pkgName + "_" + std::to_string(currentIdx)) .Concat(currentIdx < subCHIRPkg.splitNum - 1 ? moduleName + "_" + pkgName + "_" + std::to_string(currentIdx + 1) : "") .CreateMDTuple()); if (pkgName == CORE_PACKAGE_NAME && currentIdx == 0) { AddPrimitiveTypeInfoToCorePkgInfo(); } } void PkgMetadataInfo::AddPrimitiveTypeInfoToCorePkgInfo() const { auto primitiveTIsMD = module.GetLLVMModule()->getOrInsertNamedMetadata(METADATA_PRIMITIVE_TYPES); auto primitiveTTsMD = module.GetLLVMModule()->getOrInsertNamedMetadata(METADATA_PRIMITIVE_TYPETEMPLATES); auto& llvmCtx = module.GetLLVMContext(); auto typeTemplateType = CGType::GetOrCreateTypeTemplateType(llvmCtx); auto typeInfoType = CGType::GetOrCreateTypeInfoType(llvmCtx); for (auto& tiName : PRIMITIVE_TIS) { auto ti = llvm::cast(module.GetLLVMModule()->getOrInsertGlobal(tiName, typeInfoType)); ti->addAttribute(GC_KLASS_ATTR); primitiveTIsMD->addOperand(MetadataVector(llvmCtx).Concat(tiName).CreateMDTuple()); } for (auto& ttName : PRIMITIVE_TTS) { module.GetLLVMModule()->getOrInsertGlobal(ttName, typeTemplateType); primitiveTTsMD->addOperand(MetadataVector(llvmCtx).Concat(ttName).CreateMDTuple()); } } void StructMetadataInfo::GenerateAllStructsMetadata() { std::set doneSet; for (auto typeDef : subCHIRPkg.chirCustomDefs) { if (typeDef->TestAttr(CHIR::Attribute::GENERIC_INSTANTIATED) || typeDef->TestAttr(CHIR::Attribute::IMPORTED) || typeDef->TestAttr(CHIR::Attribute::NON_RECOMPILE) || typeDef->TestAttr(CHIR::Attribute::NO_REFLECT_INFO) || !typeDef->IsStruct()) { continue; } GenerateStructMetadata(StaticCast(*typeDef), doneSet); } for (auto& typeName : doneSet) { module.GetCGContext().AddGeneratedStructType(typeName); } } void StructMetadataInfo::GenerateStructMetadata(const CHIR::StructDef& sd, std::set& doneSet) { auto typeName = sd.GetIdentifierWithoutPrefix(); auto structTypeName = STRUCT_TYPE_PREFIX + typeName; if (doneSet.count(structTypeName) > 0) { return; } auto typesMD = sd.TestAttr(CHIR::Attribute::GENERIC) ? module.GetLLVMModule()->getOrInsertNamedMetadata(METADATA_TYPETEMPLATES) : module.GetLLVMModule()->getOrInsertNamedMetadata(METADATA_TYPES); std::string tiOrTTName = sd.TestAttr(CHIR::Attribute::GENERIC) ? CGType::GetNameOfTypeTemplateGV(*sd.GetType()) : CGType::GetNameOfTypeInfoGV(*sd.GetType()); llvm::GlobalVariable* reflectTIOrTT = module.GetLLVMModule()->getGlobalVariable(tiOrTTName, true); if (!reflectTIOrTT) { return; } llvm::LLVMContext& llvmCtx = module.GetLLVMContext(); if (reflectionMode != GenReflectMode::FULL_REFLECT) { auto mdTuple = MetadataVector(llvmCtx).Concat(tiOrTTName).Concat(GenerateStructFieldMetadata(sd)).CreateMDTuple(); typesMD->addOperand(mdTuple); reflectTIOrTT->addMetadata("Reflection", *mdTuple); doneSet.emplace(structTypeName); return; } auto declaredGenericTi = sd.TestAttr(CHIR::Attribute::GENERIC) ? GetTiName(*sd.GetType()) : ""; std::vector methodsVec{}; std::vector staticMethodsVec{}; GenerateStructMethodMetadata(sd, methodsVec, staticMethodsVec); MetadataTypeItem item(llvm::MDString::get(llvmCtx, tiOrTTName), llvm::MDString::get(llvmCtx, declaredGenericTi), GenerateStructFieldMetadata(sd), GenerateStructStaticFieldMetadata(sd), llvm::MDTuple::get(llvmCtx, methodsVec), llvm::MDTuple::get(llvmCtx, staticMethodsVec), GenerateAttrsMetadata(sd.GetAttributeInfo(), ExtraAttribute::STRUCT, sd.GetAnnoInfo().mangledName)); auto mdTuple = item.CreateMDTuple(llvmCtx); typesMD->addOperand(mdTuple); reflectTIOrTT->addMetadata("Reflection", *mdTuple); doneSet.emplace(structTypeName); } llvm::MDTuple* StructMetadataInfo::GenerateStructFieldMetadata(const CHIR::StructDef& sd) { llvm::LLVMContext& llvmCtx = module.GetLLVMContext(); MetadataVector fieldsVec(llvmCtx); int idx = 0; for (auto& field : sd.GetAllInstanceVars()) { fieldsVec.Concat(GenerateInstanceFieldMetadata(field, idx)); idx++; } return fieldsVec.CreateMDTuple(); } llvm::MDTuple* StructMetadataInfo::GenerateStructStaticFieldMetadata(const CHIR::StructDef& sd) { llvm::LLVMContext& llvmCtx = module.GetLLVMContext(); MetadataVector staticFieldsVec(llvmCtx); for (auto staticField : sd.GetStaticMemberVars()) { staticFieldsVec.Concat(GenerateStaticFieldMetadata(*staticField)); (void)module.GetOrInsertGlobalVariable(staticField); } return staticFieldsVec.CreateMDTuple(); } void StructMetadataInfo::GenerateStructMethodMetadata(const CHIR::StructDef& sd, std::vector& methodsVec, std::vector& staticMethodsVec) { for (auto method : sd.GetMethods()) { if (method->TestAttr(CHIR::Attribute::NO_REFLECT_INFO) || method->TestAttr(CHIR::Attribute::IMPORTED)) { continue; } auto methodMD = GenerateMethodMetadata(*method); (void)module.GetOrInsertCGFunction(method); if (method->TestAttr(CHIR::Attribute::STATIC)) { staticMethodsVec.push_back(methodMD); } else { methodsVec.push_back(methodMD); } } } void ClassMetadataInfo::GenerateAllClassesMetadata() { for (const auto typeDef : subCHIRPkg.chirCustomDefs) { if (typeDef->TestAttr(CHIR::Attribute::GENERIC_INSTANTIATED) || typeDef->TestAttr(CHIR::Attribute::NON_RECOMPILE) || typeDef->TestAttr(CHIR::Attribute::IMPORTED) || !typeDef->IsClassLike()) { continue; } GenerateClassLikeMetadata(StaticCast(*typeDef)); } } void ClassMetadataInfo::GenerateClassLikeMetadata(const CHIR::ClassDef& cd) { if (cd.TestAttr(CHIR::Attribute::NO_REFLECT_INFO) && !IsClassForBoxType(cd.GetSrcCodeIdentifier())) { return; } auto typesMD = cd.TestAttr(CHIR::Attribute::GENERIC) ? module.GetLLVMModule()->getOrInsertNamedMetadata(METADATA_TYPETEMPLATES) : module.GetLLVMModule()->getOrInsertNamedMetadata(METADATA_TYPES); std::string tiOrTTName = cd.TestAttr(CHIR::Attribute::GENERIC) ? CGType::GetNameOfTypeTemplateGV(*cd.GetType()) : CGType::GetNameOfTypeInfoGV(*cd.GetType()); auto reflectTIOrTT = module.GetLLVMModule()->getGlobalVariable(tiOrTTName, true); if (!reflectTIOrTT) { return; } llvm::LLVMContext& llvmCtx = module.GetLLVMContext(); if (reflectionMode != GenReflectMode::FULL_REFLECT) { auto mdTuple = MetadataVector(llvmCtx).Concat(tiOrTTName).Concat(GenerateClassLikeFieldMetadata(cd)).CreateMDTuple(); typesMD->addOperand(mdTuple); reflectTIOrTT->addMetadata("Reflection", *mdTuple); return; } auto declaredGenericTi = cd.TestAttr(CHIR::Attribute::GENERIC) ? GetTiName(*cd.GetType()) : ""; std::vector methodsVec{}; std::vector staticMethodsVec{}; GenerateClassLikeMethodMetadata(cd, methodsVec, staticMethodsVec); MetadataTypeItem item(llvm::MDString::get(llvmCtx, tiOrTTName), llvm::MDString::get(llvmCtx, declaredGenericTi), GenerateClassLikeFieldMetadata(cd), GenerateClassLikeStaticFieldMetadata(cd), llvm::MDTuple::get(llvmCtx, methodsVec), llvm::MDTuple::get(llvmCtx, staticMethodsVec), GenerateClassLikeTypeAttrsMetadata(cd)); auto mdTuple = item.CreateMDTuple(llvmCtx); typesMD->addOperand(mdTuple); reflectTIOrTT->addMetadata("Reflection", *mdTuple); } void ClassMetadataInfo::GenerateClassLikeMethodMetadata( const CHIR::ClassDef& cd, std::vector& methodsVec, std::vector& staticMethodsVec) { if (IsClassForBoxType(cd.GetSrcCodeIdentifier())) { return; } for (auto method : cd.GetMethods()) { if (method->TestAttr(CHIR::Attribute::NO_REFLECT_INFO)) { continue; } auto methodMD = GenerateMethodMetadata(*method, cd.IsInterface()); if (method->TestAttr(CHIR::Attribute::STATIC)) { staticMethodsVec.push_back(methodMD); } else { methodsVec.push_back(methodMD); } } for (auto& absMethod : cd.GetAbstractMethods()) { if (absMethod.TestAttr(CHIR::Attribute::NO_REFLECT_INFO) || absMethod.hasBody) { continue; } auto methodMD = GenerateClassAbsMethodMetadata(absMethod); if (absMethod.TestAttr(CHIR::Attribute::STATIC)) { staticMethodsVec.push_back(methodMD); } else { methodsVec.push_back(methodMD); } } } llvm::MDNode* ClassMetadataInfo::GenerateClassAbsMethodMetadata(const CHIR::AbstractMethodInfo& absMethod) { auto& llvmCtx = module.GetLLVMContext(); auto methodType = absMethod.methodTy; CJC_ASSERT(methodType->IsFunc()); auto funcTy = StaticCast(methodType); // Since the metadata corresponding to the type is collected in the process // of `GetCodeGenType`, and the abstract method does not need to generate // the corresponding ir, naturally, `GetCodeGenType` won't be invoked for the // formal parameter. To collect the metadata corresponding to the parameter // type, we need to explicitly call `GetCodeGenType`. // Handle params. std::vector params = absMethod.paramInfos; if (!absMethod.TestAttr(CHIR::Attribute::STATIC) && !params.empty()) { // The first parameter name of a non-static function should be `this`. params[0].paramName = "this"; } auto attr = absMethod.attributeInfo; if (!absMethod.TestAttr(CHIR::Attribute::STATIC)) { attr.SetAttr(CHIR::Attribute::VIRTUAL, true); } auto retTypeInfo = GetTiName(*funcTy->GetReturnType()); auto mdTuple = MetadataVector(llvmCtx) .Concat(absMethod.methodName) .Concat(retTypeInfo) .Concat("") .Concat(GenerateParametersMetadata(params, !absMethod.TestAttr(CHIR::Attribute::STATIC))) .Concat(GenerateParametersMetadata(absMethod.methodGenericTypeParams)) .Concat(GenerateAttrsMetadata(attr, ExtraAttribute::METHOD, absMethod.annoInfo.mangledName)) .CreateMDTuple(); return mdTuple; } llvm::MDTuple* ClassMetadataInfo::GenerateClassLikeStaticFieldMetadata(const CHIR::ClassDef& cd) { auto& llvmCtx = module.GetCGContext().GetLLVMContext(); if (cd.IsInterface()) { return llvm::MDTuple::get(llvmCtx, {}); } MetadataVector fieldsVec(llvmCtx); for (auto value : cd.GetStaticMemberVars()) { fieldsVec.Concat(GenerateStaticFieldMetadata(*value)); (void)module.GetOrInsertGlobalVariable(value); } return fieldsVec.CreateMDTuple(); } llvm::MDTuple* ClassMetadataInfo::GenerateClassLikeFieldMetadata(const CHIR::ClassDef& cd) { auto& llvmCtx = module.GetCGContext().GetLLVMContext(); if (cd.IsInterface()) { return llvm::MDTuple::get(llvmCtx, {}); } MetadataVector fieldsVec(llvmCtx); size_t idx = cd.GetAllInstanceVarNum() - cd.GetDirectInstanceVarNum(); for (auto& field : cd.GetDirectInstanceVars()) { fieldsVec.Concat(GenerateInstanceFieldMetadata(field, idx)); idx++; } return fieldsVec.CreateMDTuple(); } llvm::MDTuple* ClassMetadataInfo::GenerateClassLikeTypeAttrsMetadata(const CHIR::ClassDef& cd) const { auto metadataType = ExtraAttribute::CLASS; if (cd.IsInterface()) { metadataType = ExtraAttribute::INTERFACE; } if (IsClassForBoxType(cd.GetSrcCodeIdentifier())) { metadataType = ExtraAttribute::BOX_CLASS; } return GenerateAttrsMetadata(cd.GetAttributeInfo(), metadataType, cd.GetAnnoInfo().mangledName); } void EnumMetadataInfo::GenerateAllEnumsMetadata() { for (const auto typeDef : subCHIRPkg.chirCustomDefs) { if (typeDef->TestAttr(CHIR::Attribute::GENERIC_INSTANTIATED) || typeDef->TestAttr(CHIR::Attribute::NO_REFLECT_INFO) || typeDef->TestAttr(CHIR::Attribute::NON_RECOMPILE) || typeDef->TestAttr(CHIR::Attribute::IMPORTED) || !typeDef->IsEnum()) { continue; } GenerateEnumMetadata(StaticCast(*typeDef)); } } void EnumMetadataInfo::GenerateEnumMetadata(const CHIR::EnumDef& ed) { llvm::LLVMContext& llvmCtx = module.GetLLVMContext(); auto typesMD = ed.TestAttr(CHIR::Attribute::GENERIC) ? module.GetLLVMModule()->getOrInsertNamedMetadata(METADATA_TYPETEMPLATES) : module.GetLLVMModule()->getOrInsertNamedMetadata(METADATA_TYPES); std::string tiOrTTName = ed.TestAttr(CHIR::Attribute::GENERIC) ? CGType::GetNameOfTypeTemplateGV(*ed.GetType()) : CGType::GetNameOfTypeInfoGV(*ed.GetType()); auto reflectTIOrTT = module.GetLLVMModule()->getGlobalVariable(tiOrTTName, true); if (!reflectTIOrTT) { return; } if (reflectionMode != GenReflectMode::FULL_REFLECT) { auto mdTuple = MetadataVector(llvmCtx) .Concat(tiOrTTName) .Concat(GenerateEnumConstructorMetadata(ed)) .Concat(GenerateAttrsMetadata(ed.GetAttributeInfo(), ExtraAttribute::ENUM, ed.GetAnnoInfo().mangledName, SRetMode::NO_SRET, GetEnumKindName(module, ed))) .CreateMDTuple(); typesMD->addOperand(mdTuple); reflectTIOrTT->addMetadata("Reflection", *mdTuple); return; } std::vector methodsVec{}; std::vector staticMethodsVec{}; GenerateEnumMethodMetadata(ed, methodsVec, staticMethodsVec); MetadataTypeItem item(llvm::MDString::get(llvmCtx, tiOrTTName), llvm::MDString::get(llvmCtx, ""), GenerateEnumConstructorMetadata(ed), llvm::MDTuple::get(llvmCtx, {}), llvm::MDTuple::get(llvmCtx, methodsVec), llvm::MDTuple::get(llvmCtx, staticMethodsVec), GenerateAttrsMetadata(ed.GetAttributeInfo(), ExtraAttribute::ENUM, ed.GetAnnoInfo().mangledName, SRetMode::NO_SRET, GetEnumKindName(module, ed))); auto mdTuple = item.CreateMDTuple(llvmCtx, true); typesMD->addOperand(mdTuple); reflectTIOrTT->addMetadata("Reflection", *mdTuple); } llvm::MDTuple* EnumMetadataInfo::GenerateEnumConstructorMetadata(const CHIR::EnumDef& ed) { llvm::LLVMContext& llvmCtx = module.GetLLVMContext(); MetadataVector fieldsVec(llvmCtx); size_t index = 0; for (auto& ctor : ed.GetCtors()) { auto ctorName = ctor.name; auto currentCgType = StaticCast(CGType::GetOrCreate(module, ed.GetType())); if (currentCgType->IsOptionLike()) { size_t nonArgIndex = currentCgType->IsAntiOptionLike() ? 0 : 1; ctorName = nonArgIndex == index && !IsCoreOption(ed) ? "N$_" + ctor.name : ctor.name; } if (currentCgType->IsTrivial() || currentCgType->IsZeroSizeEnum()) { fieldsVec.AddSubItem(MetadataVector(llvmCtx).Concat(ctorName).Concat("")); } else { std::string ti = GenerateCtorFn(ed, index++, GetTypeQualifiedName(*ed.GetType())); fieldsVec.AddSubItem(MetadataVector(llvmCtx).Concat(ctorName).Concat(ti)); } } return fieldsVec.CreateMDTuple(); } std::string EnumMetadataInfo::GenerateCtorFn( const CHIR::EnumDef& enumDef, size_t index, const std::string& qualifiedName) { auto funcName = qualifiedName + ".Ctor_" + std::to_string(index) + ".Fn"; auto getTiFn = module.GetLLVMModule()->getFunction(funcName); if (!getTiFn) { std::vector argTypes; auto typeInfoType = CGType::GetOrCreateTypeInfoType(module.GetLLVMContext()); argTypes.emplace_back(llvm::Type::getInt32Ty(module.GetLLVMContext())); argTypes.emplace_back(typeInfoType->getPointerTo()->getPointerTo()); llvm::FunctionType* ctorFn = llvm::FunctionType::get(llvm::Type::getInt8PtrTy(module.GetLLVMContext()), argTypes, false); getTiFn = llvm::Function::Create(ctorFn, llvm::Function::PrivateLinkage, funcName, module.GetLLVMModule()); CodeGen::IRBuilder2 irBuilder(module); auto entryBB = irBuilder.CreateEntryBasicBlock(getTiFn, "entry"); irBuilder.SetInsertPoint(entryBB); llvm::Value* ti{nullptr}; auto chirEnumType = StaticCast(enumDef.GetType()); if (CGType::GetOrCreate(module, chirEnumType)->IsDynamicGI()) { auto tt = module.GetOrCreateEnumCtorTIOrTT(*chirEnumType, index); ti = irBuilder.CallIntrinsicGetTypeInfo({tt, getTiFn->getArg(0), getTiFn->getArg(1)}); } else { ti = module.GetOrCreateEnumCtorTIOrTT(*chirEnumType, index); } llvm::Value* bitcastedValue = irBuilder.CreateBitCast(ti, llvm::Type::getInt8PtrTy(module.GetLLVMContext())); irBuilder.CreateRet(bitcastedValue); } return funcName; } void EnumMetadataInfo::GenerateEnumMethodMetadata( const CHIR::EnumDef& ed, std::vector& methodsVec, std::vector& staticMethodsVec) { for (auto method : ed.GetMethods()) { if (method->TestAttr(CHIR::Attribute::NO_REFLECT_INFO) || method->TestAttr(CHIR::Attribute::IMPORTED)) { continue; } auto methodMD = GenerateMethodMetadata(*method); if (method->TestAttr(CHIR::Attribute::STATIC)) { staticMethodsVec.push_back(methodMD); } else { methodsVec.push_back(methodMD); } } } void GFMetadataInfo::GenerateAllFunctionsMetadata() { if (reflectionMode != GenReflectMode::FULL_REFLECT) { return; } auto& llvmCtx = module.GetLLVMContext(); auto llvmMod = module.GetLLVMModule(); /// Generate "functions" metadata for functions without "@C"\"@Java"\"foreign". llvm::NamedMDNode* functionsMdNode = llvmMod->getOrInsertNamedMetadata(METADATA_FUNCTIONS); for (auto gf : subCHIRPkg.chirFuncs) { // Member functions also have the Global attribute, which cannot be determined by `TestAttr(Attribute::GLOBAL)`. if (gf->GetParentCustomTypeDef()) { continue; } if (gf->TestAttr(CHIR::Attribute::NO_REFLECT_INFO) || gf->TestAttr(CHIR::Attribute::IMPORTED) || gf->GetSrcCodeIdentifier().empty() || gf->GetFuncType()->IsCFunc() || gf->TestAttr(CHIR::Attribute::GENERIC_INSTANTIATED)) { continue; } auto retTypeInfo = GetTiName(*gf->GetReturnType()); uint8_t hasSRetMode = GetSRetMode(*gf->GetReturnType(), *module.GetOrInsertCGFunction(gf)); if (auto func = llvmMod->getFunction(gf->GetIdentifierWithoutPrefix()); func) { auto funcMD = MetadataVector(llvmCtx) .Concat(gf->GetSrcCodeIdentifier()) .Concat(retTypeInfo) .Concat(gf->GetIdentifierWithoutPrefix()) .Concat(GenerateParametersMetadata(gf->GetParams())) .Concat(GenerateParametersMetadata(gf->GetGenericTypeParams())) .Concat(GenerateAttrsMetadata(gf->GetAttributeInfo(), ExtraAttribute::METHOD, gf->GetAnnoInfo().mangledName, hasSRetMode)) .CreateMDTuple(); functionsMdNode->addOperand(funcMD); func->addMetadata("ReflectionFunc", *funcMD); } } } void GVMetadataInfo::GenerateGlobalVariablesMetadata() { if (reflectionMode != GenReflectMode::FULL_REFLECT) { return; } auto genGlobalVarsMetadata = [this](const std::vector& variables) { auto varsMdNode = module.GetLLVMModule()->getOrInsertNamedMetadata(METADATA_GLOBAL_VAR); for (const auto it : variables) { if (it->TestAttr(CHIR::Attribute::STATIC) || it->TestAttr(CHIR::Attribute::NO_REFLECT_INFO) || it->TestAttr(CHIR::Attribute::IMPORTED)) { continue; } if (const auto variableMetadata = GenerateVariableMetadata(*it); variableMetadata) { varsMdNode->addOperand(variableMetadata); module.GetLLVMModule() ->getNamedGlobal(it->GetIdentifierWithoutPrefix()) ->addMetadata("ReflectionGV", *variableMetadata); } } }; /// "global_variables" contains the variables which are global but not static. genGlobalVarsMetadata(std::vector(subCHIRPkg.chirGVs.begin(), subCHIRPkg.chirGVs.end())); } llvm::MDNode* GVMetadataInfo::GenerateVariableMetadata(const CHIR::GlobalVar& variable) { auto llvmMod = module.GetLLVMModule(); auto variableMangleName = variable.GetIdentifierWithoutPrefix(); /// If a CHIR::Variable node is not generated as a global variable in the current module, or the node is imported /// to the current module, we don't need to generate metadata for it. Just return directly. const auto globalVariable = llvmMod->getGlobalVariable(variableMangleName, true); bool isStringGV = variable.GetInitializer() != nullptr && variable.GetInitializer()->GetType()->IsString(); if (!globalVariable || (!globalVariable->hasInitializer() && !isStringGV)) { return nullptr; } llvm::LLVMContext& llvmCtx = module.GetLLVMContext(); return MetadataVector(llvmCtx) .Concat(variable.GetSrcCodeIdentifier()) .Concat(GetTiName(*variable.GetType())) .Concat(variableMangleName) .Concat(GenerateAttrsMetadata(variable.GetAttributeInfo(), variable.TestAttr(CHIR::Attribute::READONLY) ? ExtraAttribute::IMMUTABLE_FIELD : ExtraAttribute::MUTABLE_FIELD, variable.GetAnnoInfo().mangledName)) .CreateMDTuple(); } cangjie_compiler-1.0.7/src/CodeGen/CJNative/CJNativeGenOverflow.cpp000066400000000000000000000750611510705540100251030ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file realizes generate overflow APIs for cjnative codegen. */ #include "Base/OverflowDispatcher.h" #include "Base/ArithmeticOpImpl.h" #include "Base/CGTypes/CGEnumType.h" #include "Base/ExprDispatcher/ExprDispatcher.h" #include "Base/TypeCastImpl.h" #include "IRBuilder.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Value.h" namespace { using namespace Cangjie; using namespace CodeGen; // store some instructions for calculating pow. struct OverflowCalcAllocaInsts { llvm::AllocaInst* base; llvm::AllocaInst* acc; llvm::AllocaInst* exp; }; struct StructInfo { llvm::AllocaInst* alc; llvm::Type* alcTy; std::vector tys; }; struct OverflowHandler { public: IRBuilder2& irBuilder; const OverflowStrategy strategy; const std::pair& tys; const CHIR::ExprKind& kind; const std::vector& argGenValues; const CHIR::IntType* ty{nullptr}; OverflowHandler(IRBuilder2& irBuilder, const OverflowStrategy& strategy, const CHIR::ExprKind& kind, const std::pair& tys, const std::vector& argGenValues) : irBuilder(irBuilder), strategy(strategy), tys(tys), kind(kind), argGenValues(argGenValues) { ty = tys.first; } inline bool IsChecked() const { return strategy == OverflowStrategy::CHECKED; } std::vector GetLLVMValues() const { if (argGenValues.size() == 1) { return {argGenValues[0]->GetRawValue()}; } else { return {argGenValues[0]->GetRawValue(), argGenValues[1]->GetRawValue()}; } } inline bool IsDivOrMod() const { return kind == CHIR::ExprKind::DIV || kind == CHIR::ExprKind::MOD; } const CHIR::Type* GetElemTy() const { return tys.first; } const CHIR::Type* GetOptionTy() const { if (tys.second == nullptr) { return tys.first; } return tys.second; } llvm::Value* GenerateExpectValueAsFalse(llvm::Value* val); std::tuple GenerateCheckOverflowFlag(llvm::Value* flag); llvm::Value* GenerateOverflowLogicCondition(const std::function& condLeft, const std::function& condRight, bool isLogicAnd = true); void GenerateOverflowElseBody(llvm::AllocaInst* ifValue, const StructInfo& valInfo); void GenerateOverflowThenBody(llvm::AllocaInst* ifValue, bool& needUnreachableTerminator); void GenerateOverflowOption(bool isSome, llvm::Value* val, llvm::AllocaInst* ifValue); llvm::Value* GenerateOverflowOpKindOption(); llvm::Value* GenerateOverflowDivOrMod(); void GenerateOverflowCalcMul(const OverflowCalcAllocaInsts& allocaInsts, bool isOddExp, const StructInfo& valInfo, llvm::BasicBlock* powEndBB); void GenerateOverflowCalcPowBody( const OverflowCalcAllocaInsts& allocaInsts, const StructInfo& valInfo, llvm::BasicBlock* powEndBB); void GenerateOverflowPowCalc(const StructInfo& valInfo, llvm::BasicBlock* powEndBB); void GenerateOverflowPowCheckParamBaseEqNegOne(const StructInfo& valInfo, llvm::BasicBlock* powEndBB); void GenerateOverflowPowCheckParam( const StructInfo& valInfo, llvm::BasicBlock* powCalcBB, llvm::BasicBlock* powEndBB); void GenerateOverflowCheckPow(const StructInfo& valInfo); void GenerateOverflowCheck(const StructInfo& valInfo); void GenerateOverflowSaturatingOp(llvm::AllocaInst* retValue); void GenerateOverflowSaturating(llvm::AllocaInst* retValue); void GenerateOverflowStrategy(llvm::AllocaInst* ifValue, bool& needUnreachableTerminator); }; llvm::Value* OverflowHandler::GenerateExpectValueAsFalse(llvm::Value* val) { CJC_ASSERT(val->getType()->isIntegerTy(1) && "val should be bool type!"); CGType* boolType = CGType::GetBoolCGType(irBuilder.GetCGModule()); llvm::Value* falseVal = irBuilder.GetFalse(); // Call the intrinsic llvm.expect.i1(ov-flag, false) return irBuilder.GenerateCallExpectFunction(boolType, val, falseVal); } std::tuple OverflowHandler::GenerateCheckOverflowFlag( llvm::Value* flag) { auto condVal = GenerateExpectValueAsFalse(flag); auto [normalBB, overflowBB, endBB] = Vec2Tuple<3>( irBuilder.CreateAndInsertBasicBlocks({GenNameForBB("normal"), GenNameForBB("overflow"), GenNameForBB("end")})); (void)irBuilder.CreateCondBr(condVal, overflowBB, normalBB); return std::make_tuple(overflowBB, normalBB, endBB); } void OverflowHandler::GenerateOverflowElseBody(llvm::AllocaInst* ifValue, const StructInfo& valInfo) { // emit else body. llvm::Value* res = nullptr; if (valInfo.alc == nullptr) { res = GenerateArithmeticOperation(irBuilder, kind, GetElemTy(), argGenValues[0], argGenValues[1]); } else { auto s0 = irBuilder.CreateStructGEP(valInfo.alcTy, valInfo.alc, 0); res = irBuilder.CreateLoad(valInfo.tys[0], s0); } if (IsChecked()) { // generate Option.Some(num) GenerateOverflowOption(true, res, ifValue); } else { (void)irBuilder.CreateStore(res, ifValue); } } void OverflowHandler::GenerateOverflowThenBody(llvm::AllocaInst* ifValue, bool& needUnreachableTerminator) { if (IsChecked()) { // generate Option.None CGType* elemType = CGType::GetOrCreate(irBuilder.GetCGModule(), GetElemTy()); auto zeroVal = llvm::ConstantInt::get(elemType->GetLLVMType(), 0); GenerateOverflowOption(false, zeroVal, ifValue); needUnreachableTerminator = false; } else { // throwing/saturating/wrapping. GenerateOverflowStrategy(ifValue, needUnreachableTerminator); } } /* Generate the IR for `if (condLeft && condRight)` or `if (condLeft || condRight)`: * If `isLogicAnd` is `true`, generate IR as follows: * entry: * %and.val = alloca i1, align 1 * ... * %0 = condLeft() * br %0 %land.rsh, %tmpLabel * * tmpLabel: * store i1 false, i1* %and.val, align 1 * br %land.end * * land.rsh: * %1 = condRight() * store i1 %1, i1* %and.val, align 1 * br %land.end * * land.end: * %2 = load i1, i1* %and.val * * If `isLogicAnd` is false, here it refers to logic or, generate IR as follows: * entry: * %or.val = alloca i1, align 1 * ... * %0 = condLeft() * br %0 %tmpLabel, %lor.rsh * * tmpLabel: * store i1 true, i1* %or.val, align 1 * br %lor.end * * land.rsh: * %1 = condRight() * store i1 %1, i1* %or.val, align 1 * br %lor.end * * lor.end: * %2 = load i1, i1* %or.val, align 1 */ llvm::Value* OverflowHandler::GenerateOverflowLogicCondition( const std::function& condLeft, const std::function& condRight, bool isLogicAnd) { std::string logicValueName = isLogicAnd ? "and.val" : "or.val"; std::string rhsLabel = "l" + std::string(isLogicAnd ? "and" : "or") + ".rhs"; std::string endLabel = "l" + std::string(isLogicAnd ? "and" : "or") + ".end"; CGType* boolType = CGType::GetBoolCGType(irBuilder.GetCGModule()); llvm::AllocaInst* logicValue = irBuilder.CreateEntryAlloca(boolType->GetLLVMType(), nullptr, logicValueName); llvm::Value* shortValue = isLogicAnd ? irBuilder.GetFalse() : irBuilder.GetTrue(); auto [tmpBB, rhsBB, endBB] = Vec2Tuple<3>(irBuilder.CreateAndInsertBasicBlocks( {GenNameForBB("tmpLabel"), GenNameForBB(rhsLabel), GenNameForBB(endLabel)})); llvm::Value* condLeftVal = condLeft(); CJC_ASSERT(condLeftVal->getType() == boolType->GetLLVMType()); if (isLogicAnd) { (void)irBuilder.CreateCondBr(condLeftVal, rhsBB, tmpBB); } else { (void)irBuilder.CreateCondBr(condLeftVal, tmpBB, rhsBB); } // Append br instruction to tmp block. irBuilder.SetInsertPoint(tmpBB); (void)irBuilder.CreateStore(shortValue, logicValue); (void)irBuilder.CreateBr(endBB); irBuilder.SetInsertPoint(rhsBB); llvm::Value* condRightVal = condRight(); CJC_ASSERT(condRightVal->getType() == boolType->GetLLVMType()); (void)irBuilder.CreateStore(condRightVal, logicValue); (void)irBuilder.CreateBr(endBB); irBuilder.SetInsertPoint(endBB); return irBuilder.CreateLoad(boolType->GetLLVMType(), logicValue); } void OverflowHandler::GenerateOverflowOption(bool isSome, llvm::Value* val, llvm::AllocaInst* ifValue) { llvm::Value* firstVal = isSome ? irBuilder.getFalse() : irBuilder.getTrue(); (void)irBuilder.CreateStore(firstVal, irBuilder.CreateStructGEP(ifValue->getAllocatedType(), ifValue, 0)); (void)irBuilder.CreateStore(val, irBuilder.CreateStructGEP(ifValue->getAllocatedType(), ifValue, 1)); } llvm::Value* OverflowHandler::GenerateOverflowOpKindOption() { if (!IsChecked()) { return GenerateOverflowWrappingArithmeticOp(irBuilder, kind, ty, argGenValues); } llvm::Value* val = GenerateArithmeticOperation(irBuilder, kind, GetElemTy(), argGenValues[0], argGenValues[1]); const CHIR::Type* optionTy = GetOptionTy(); CGType* optionType = CGType::GetOrCreate(irBuilder.GetCGModule(), optionTy); llvm::AllocaInst* retValue = irBuilder.CreateEntryAlloca(optionType->GetLLVMType()); // Generate option some. GenerateOverflowOption(true, val, retValue); return irBuilder.CreateLoad(retValue->getAllocatedType(), retValue); } llvm::Value* OverflowHandler::GenerateOverflowDivOrMod() { auto& cgMod = irBuilder.GetCGModule(); const CHIR::Type* elemTy = GetElemTy(); if (!ty->IsSigned()) { return GenerateOverflowOpKindOption(); } const CHIR::Type* optionTy = GetOptionTy(); CGType* elemType = CGType::GetOrCreate(cgMod, elemTy); CGType* optionType = CGType::GetOrCreate(cgMod, optionTy); // Overflow condition: x <= MinInt8 && y == -1. auto minVal = llvm::ConstantInt::getSigned(elemType->GetLLVMType(), GetIntMaxOrMin(irBuilder, *ty, false)); auto leftCond = [this, &minVal]() { return irBuilder.CreateICmpSLE(argGenValues[0]->GetRawValue(), minVal); }; auto negativeOne = llvm::ConstantInt::getSigned(elemType->GetLLVMType(), -1); auto rightCond = [this, &negativeOne]() { return irBuilder.CreateICmpEQ(argGenValues[1]->GetRawValue(), negativeOne); }; llvm::Value* condV = GenerateOverflowLogicCondition(leftCond, rightCond); llvm::BasicBlock* overflowBB = nullptr; llvm::BasicBlock* normalBB = nullptr; llvm::BasicBlock* endBB = nullptr; std::tie(overflowBB, normalBB, endBB) = GenerateCheckOverflowFlag(condV); CJC_NULLPTR_CHECK(normalBB); CJC_NULLPTR_CHECK(overflowBB); CJC_NULLPTR_CHECK(endBB); CGType* type = IsChecked() ? optionType : elemType; llvm::AllocaInst* ifValue = irBuilder.CreateEntryAlloca(type->GetLLVMType()); // Emit non-overflow body first to make it closer to the above block. irBuilder.SetInsertPoint(normalBB); GenerateOverflowElseBody(ifValue, {nullptr, nullptr, {}}); (void)irBuilder.CreateBr(endBB); // Emit overflow body. irBuilder.SetInsertPoint(overflowBB); bool needUnrachableTerminator = false; GenerateOverflowThenBody(ifValue, needUnrachableTerminator); needUnrachableTerminator ? (void)irBuilder.CreateUnreachable() : (void)irBuilder.CreateBr(endBB); irBuilder.SetInsertPoint(endBB); return irBuilder.CreateLoad(ifValue->getAllocatedType(), ifValue); } void OverflowHandler::GenerateOverflowCheck(const StructInfo& valInfo) { std::vector values = GetLLVMValues(); auto rightVal = irBuilder.GenerateOverflowCheckedFunc(kind, *ty, values); CJC_ASSERT(rightVal); (void)irBuilder.CreateStore(rightVal, valInfo.alc); } void OverflowHandler::GenerateOverflowCheckPow(const StructInfo& valInfo) { auto [powCheckParamBB, powCalcBB, powEndBB] = Vec2Tuple<3>(irBuilder.CreateAndInsertBasicBlocks( {GenNameForBB("pow.check.param"), GenNameForBB("pow.calc"), GenNameForBB("pow.end")})); (void)irBuilder.CreateBr(powCheckParamBB); // emit pow.check.param. irBuilder.SetInsertPoint(powCheckParamBB); GenerateOverflowPowCheckParam(valInfo, powCalcBB, powEndBB); // emit pow.calc. irBuilder.SetInsertPoint(powCalcBB); GenerateOverflowPowCalc(valInfo, powEndBB); (void)irBuilder.CreateBr(powEndBB); irBuilder.SetInsertPoint(powEndBB); } /** Generate the IR for the following code: * if (base == 1 || exp == 0) { * return (1, false) * } else if (base == 0) { * return (0, false) * } else if (base == -1) { * if ((exp & 1) == 1) { * return (-1, false) * } else { * return (1, false) * } * } */ void OverflowHandler::GenerateOverflowPowCheckParam( const StructInfo& valInfo, llvm::BasicBlock* powCalcBB, llvm::BasicBlock* powEndBB) { llvm::Type* type = CGType::GetOrCreate(irBuilder.GetCGModule(), ty)->GetLLVMType(); auto zeroVal = llvm::ConstantInt::get(type, 0); auto oneVal = llvm::ConstantInt::get(type, 1); auto negOneVal = llvm::ConstantInt::getSigned(type, -1); auto falseVal = irBuilder.GetFalse(); auto [baseEqOneBB, baseNeqOneBB, baseEqZeroBB, baseNeqZeroBB, baseEqNegOneBB] = Vec2Tuple<5>(irBuilder.CreateAndInsertBasicBlocks({GenNameForBB("base.eq.one"), GenNameForBB("base.neq.one"), GenNameForBB("base.eq.zero"), GenNameForBB("base.neq.zero"), GenNameForBB("base.eq.neg.one")})); // condition: (base == 1 || exp == 0). auto leftCond = [this, &oneVal]() { return irBuilder.CreateICmpEQ(argGenValues[0]->GetRawValue(), oneVal); }; auto rightCond = [this, &zeroVal]() { return irBuilder.CreateICmpEQ(argGenValues[1]->GetRawValue(), zeroVal); }; auto baseEqOneCond = GenerateOverflowLogicCondition(leftCond, rightCond, false); (void)irBuilder.CreateCondBr(baseEqOneCond, baseEqOneBB, baseNeqOneBB); irBuilder.SetInsertPoint(baseEqOneBB); // retVal: (1, false). (void)irBuilder.CreateStore(oneVal, irBuilder.CreateStructGEP(valInfo.alcTy, valInfo.alc, 0)); (void)irBuilder.CreateStore(falseVal, irBuilder.CreateStructGEP(valInfo.alcTy, valInfo.alc, 1)); (void)irBuilder.CreateBr(powEndBB); irBuilder.SetInsertPoint(baseNeqOneBB); // condition: (base == 0). auto baseEqZeroCond = irBuilder.CreateICmpEQ(argGenValues[0]->GetRawValue(), zeroVal); (void)irBuilder.CreateCondBr(baseEqZeroCond, baseEqZeroBB, baseNeqZeroBB); irBuilder.SetInsertPoint(baseEqZeroBB); // retVal: (0, false) (void)irBuilder.CreateStore(zeroVal, irBuilder.CreateStructGEP(valInfo.alcTy, valInfo.alc, 0)); (void)irBuilder.CreateStore(falseVal, irBuilder.CreateStructGEP(valInfo.alcTy, valInfo.alc, 1)); (void)irBuilder.CreateBr(powEndBB); irBuilder.SetInsertPoint(baseNeqZeroBB); // condition: (base == -1). auto baseEqNegOneCond = irBuilder.CreateICmpEQ(argGenValues[0]->GetRawValue(), negOneVal); (void)irBuilder.CreateCondBr(baseEqNegOneCond, baseEqNegOneBB, powCalcBB); irBuilder.SetInsertPoint(baseEqNegOneBB); GenerateOverflowPowCheckParamBaseEqNegOne(valInfo, powEndBB); } /** Generate the IR for the following code: * if ((exp & 1) == 1) { * return (-1, false) * } else { * return (1, false) * } */ void OverflowHandler::GenerateOverflowPowCheckParamBaseEqNegOne(const StructInfo& valInfo, llvm::BasicBlock* powEndBB) { llvm::Type* type = CGType::GetOrCreate(irBuilder.GetCGModule(), ty)->GetLLVMType(); auto oneVal = llvm::ConstantInt::get(type, 1); auto negOneVal = llvm::ConstantInt::getSigned(type, -1); auto falseVal = irBuilder.GetFalse(); auto [expIsOddBB, expIsEvenBB] = Vec2Tuple<2>(irBuilder.CreateAndInsertBasicBlocks({GenNameForBB("exp.is.odd"), GenNameForBB("exp.is.even")})); // condition: ((exp & 1) == 1). auto expAndOne = irBuilder.CreateAnd(argGenValues[1]->GetRawValue(), oneVal); auto expIsOddCond = irBuilder.CreateICmpEQ(expAndOne, oneVal); (void)irBuilder.CreateCondBr(expIsOddCond, expIsOddBB, expIsEvenBB); irBuilder.SetInsertPoint(expIsOddBB); // retVal: (-1, false) (void)irBuilder.CreateStore(negOneVal, irBuilder.CreateStructGEP(valInfo.alcTy, valInfo.alc, 0)); (void)irBuilder.CreateStore(falseVal, irBuilder.CreateStructGEP(valInfo.alcTy, valInfo.alc, 1)); (void)irBuilder.CreateBr(powEndBB); irBuilder.SetInsertPoint(expIsEvenBB); // retVal: (1, false) (void)irBuilder.CreateStore(oneVal, irBuilder.CreateStructGEP(valInfo.alcTy, valInfo.alc, 0)); (void)irBuilder.CreateStore(falseVal, irBuilder.CreateStructGEP(valInfo.alcTy, valInfo.alc, 1)); (void)irBuilder.CreateBr(powEndBB); } /* Generate the IR for the following code: * var base = base(param) * var exp = exponent(param) * var acc: Int64 = 1 * while (exp > 1) { * if ((exp & 1) == 1) { * let (ret, bOverflow) = overflowingMul(acc, base) * if (bOverflow && bRet) { * return (ret, true) * } * acc = ret * } * exp /= 2 * let (ret, bOverflow) = overflowingMul(base, base) * if (bOverflow && bRet) { * return (ret, true) * } * base = ret * } * let (ret, bOverflow) = overflowingMul(acc, base) * return (ret, bOverflow) */ void OverflowHandler::GenerateOverflowPowCalc(const StructInfo& valInfo, llvm::BasicBlock* powEndBB) { OverflowCalcAllocaInsts allocaInsts; CGType* type = CGType::GetOrCreate(irBuilder.GetCGModule(), ty); auto oneVal = llvm::ConstantInt::get(type->GetLLVMType(), 1); allocaInsts.base = irBuilder.CreateEntryAlloca(type->GetLLVMType(), nullptr, "base"); allocaInsts.acc = irBuilder.CreateEntryAlloca(type->GetLLVMType(), nullptr, "acc"); allocaInsts.exp = irBuilder.CreateEntryAlloca(argGenValues[1]->GetRawValue()->getType(), nullptr, "exp"); (void)irBuilder.CreateStore(argGenValues[0]->GetRawValue(), allocaInsts.base); (void)irBuilder.CreateStore(argGenValues[1]->GetRawValue(), allocaInsts.exp); (void)irBuilder.CreateStore(oneVal, allocaInsts.acc); auto [whileBB, thenBB, endBB] = Vec2Tuple<3>(irBuilder.CreateAndInsertBasicBlocks( {GenNameForBB("while"), GenNameForBB("while.then"), GenNameForBB("while.end")})); (void)irBuilder.CreateBr(whileBB); irBuilder.SetInsertPoint(whileBB); // while Condition: exp > 1. auto condV = irBuilder.CreateICmpUGT( irBuilder.CreateLoad(allocaInsts.exp->getAllocatedType(), allocaInsts.exp), oneVal, "icmpugt"); (void)irBuilder.CreateCondBr(condV, thenBB, endBB); // emit while then body. irBuilder.SetInsertPoint(thenBB); GenerateOverflowCalcPowBody(allocaInsts, valInfo, powEndBB); (void)irBuilder.CreateBr(whileBB); // emit while end body. irBuilder.SetInsertPoint(endBB); auto accVal = irBuilder.CreateLoad(allocaInsts.acc->getAllocatedType(), allocaInsts.acc); auto baseVal = irBuilder.CreateLoad(allocaInsts.base->getAllocatedType(), allocaInsts.base); std::vector argGenNewValues{accVal, baseVal}; auto rightVal = irBuilder.GenerateOverflowCheckedFunc(CHIR::ExprKind::MUL, *ty, argGenNewValues); (void)irBuilder.CreateStore(rightVal, valInfo.alc); } /* Generate the IR for the following code: * if ((exp & 1) == 1) { * let (ret, bOverflow) = overflowingMul(acc, base) * if (bOverflow) { * return (ret, true) * } * acc = ret * } * exp /= 2 * let (ret, bOverflow) = overflowingMul(base, base) * if (bOverflow) { * return (ret, true) * } * base = ret */ void OverflowHandler::GenerateOverflowCalcPowBody( const OverflowCalcAllocaInsts& allocaInsts, const StructInfo& valInfo, llvm::BasicBlock* powEndBB) { CGType* type = CGType::GetOrCreate(irBuilder.GetCGModule(), ty); auto [thenBB, endBB] = Vec2Tuple<2>(irBuilder.CreateAndInsertBasicBlocks({GenNameForBB("if.then"), GenNameForBB("if.end")})); auto expVal = irBuilder.CreateLoad(allocaInsts.exp->getAllocatedType(), allocaInsts.exp); auto oneVal = llvm::ConstantInt::get(type->GetLLVMType(), 1); // condition: (exp & 1) == 1 auto left = irBuilder.CreateAnd(expVal, oneVal, "and"); auto condV = irBuilder.CreateICmpEQ(left, oneVal, "icmpeq"); (void)irBuilder.CreateCondBr(condV, thenBB, endBB); irBuilder.SetInsertPoint(thenBB); GenerateOverflowCalcMul(allocaInsts, true, valInfo, powEndBB); (void)irBuilder.CreateBr(endBB); irBuilder.SetInsertPoint(endBB); auto twoVal = llvm::ConstantInt::get(type->GetLLVMType(), 2); // "2" is the value of divisor. llvm::Value* newExpVal = irBuilder.CreateUDiv(expVal, twoVal); (void)irBuilder.CreateStore(newExpVal, allocaInsts.exp); GenerateOverflowCalcMul(allocaInsts, false, valInfo, powEndBB); } void OverflowHandler::GenerateOverflowCalcMul( const OverflowCalcAllocaInsts& allocaInsts, bool isOddExp, const StructInfo& valInfo, llvm::BasicBlock* powEndBB) { auto [thenBB, endBB] = Vec2Tuple<2>(irBuilder.CreateAndInsertBasicBlocks({GenNameForBB("if.then"), GenNameForBB("if.end")})); auto rVal = irBuilder.CreateLoad(allocaInsts.base->getAllocatedType(), allocaInsts.base); auto lVal = isOddExp ? irBuilder.CreateLoad(allocaInsts.acc->getAllocatedType(), allocaInsts.acc) : rVal; std::vector argGenNewValues{lVal, rVal}; auto rightVal = irBuilder.GenerateOverflowCheckedFunc(CHIR::ExprKind::MUL, *ty, argGenNewValues); (void)irBuilder.CreateStore(rightVal, valInfo.alc); auto s1 = irBuilder.CreateStructGEP(valInfo.alcTy, valInfo.alc, 1); auto condV = irBuilder.CreateLoad(valInfo.tys[1], s1); (void)irBuilder.CreateCondBr(condV, thenBB, endBB); irBuilder.SetInsertPoint(thenBB); (void)irBuilder.CreateBr(powEndBB); irBuilder.SetInsertPoint(endBB); auto s0 = irBuilder.CreateStructGEP(valInfo.alcTy, valInfo.alc, 0); auto retNewVal = irBuilder.CreateLoad(valInfo.tys[0], s0); if (isOddExp) { (void)irBuilder.CreateStore(retNewVal, allocaInsts.acc); } else { (void)irBuilder.CreateStore(retNewVal, allocaInsts.base); } } void OverflowHandler::GenerateOverflowSaturatingOp(llvm::AllocaInst* retValue) { CJC_ASSERT(argGenValues.size() == 2 && "should have two operands"); // Binary operations should have 2 operands. auto& cgMod = irBuilder.GetCGModule(); llvm::Type* type = CGType::GetOrCreate(cgMod, ty)->GetLLVMType(); auto zeroVal = llvm::ConstantInt::get(type, 0); llvm::Value* condV = nullptr; if (kind == CHIR::ExprKind::ADD) { // condition: x > 0 && y > 0. auto leftCond = [this, &zeroVal]() { return irBuilder.CreateICmpSGT(argGenValues[0]->GetRawValue(), zeroVal); }; auto rightCond = [this, &zeroVal]() { return irBuilder.CreateICmpSGT(argGenValues[1]->GetRawValue(), zeroVal); }; condV = GenerateOverflowLogicCondition(leftCond, rightCond); } if (kind == CHIR::ExprKind::SUB) { // condition: x >= 0 && y < 0. auto leftCond = [this, &zeroVal]() { return irBuilder.CreateICmpSGE(argGenValues[0]->GetRawValue(), zeroVal); }; auto rightCond = [this, &zeroVal]() { return irBuilder.CreateICmpSLT(argGenValues[1]->GetRawValue(), zeroVal); }; condV = GenerateOverflowLogicCondition(leftCond, rightCond); } if (kind == CHIR::ExprKind::MUL) { // condition: (x > 0) == (y > 0). auto leftCond = irBuilder.CreateICmpSGT(argGenValues[0]->GetRawValue(), zeroVal, "icmpsgt"); auto rightCond = irBuilder.CreateICmpSGT(argGenValues[1]->GetRawValue(), zeroVal, "icmpsgt"); condV = irBuilder.CreateICmpEQ(leftCond, rightCond, "icmpeq"); } if (kind == CHIR::ExprKind::EXP) { // condition: base > 0 || (exponent & 1) == 0 auto leftCond = [this, &zeroVal]() { return irBuilder.CreateICmpSGT(argGenValues[0]->GetRawValue(), zeroVal); }; auto rightCond = [this, &type]() { auto right = irBuilder.CreateAnd(argGenValues[1]->GetRawValue(), llvm::ConstantInt::get(type, 1)); return irBuilder.CreateICmpEQ(right, llvm::ConstantInt::get(type, 0)); }; condV = GenerateOverflowLogicCondition(leftCond, rightCond, false); } auto [thenBB, elseBB, endBB] = Vec2Tuple<3>(irBuilder.CreateAndInsertBasicBlocks( {GenNameForBB("if.then"), GenNameForBB("if.else"), GenNameForBB("if.end")})); (void)irBuilder.CreateCondBr(condV, thenBB, elseBB); // emit then body. irBuilder.SetInsertPoint(thenBB); // Saturating: MaxInt8. auto maxVal = llvm::ConstantInt::getSigned(type, GetIntMaxOrMin(irBuilder, *ty, true)); (void)irBuilder.CreateStore(maxVal, retValue); (void)irBuilder.CreateBr(endBB); // emit else body. irBuilder.SetInsertPoint(elseBB); // Saturating: MinInt8. auto minVal = llvm::ConstantInt::getSigned(type, GetIntMaxOrMin(irBuilder, *ty, false)); (void)irBuilder.CreateStore(minVal, retValue); (void)irBuilder.CreateBr(endBB); irBuilder.SetInsertPoint(endBB); } void OverflowHandler::GenerateOverflowSaturating(llvm::AllocaInst* retValue) { auto& cgMod = irBuilder.GetCGModule(); llvm::Type* type = CGType::GetOrCreate(cgMod, ty)->GetLLVMType(); if (!ty->IsSigned()) { // Unsigned Integer: MaxUInt8/0. if (kind == CHIR::ExprKind::ADD || kind == CHIR::ExprKind::MUL || kind == CHIR::ExprKind::EXP) { // add/mul/pow: MaxUInt8 auto maxVal = llvm::ConstantInt::get(type, GetUIntMax(irBuilder, *ty)); (void)irBuilder.CreateStore(maxVal, retValue); } else { // sub/dec/neg: 0 auto minVal = llvm::ConstantInt::get(type, 0); (void)irBuilder.CreateStore(minVal, retValue); } return; } if (kind == CHIR::ExprKind::ADD || kind == CHIR::ExprKind::SUB || kind == CHIR::ExprKind::MUL || kind == CHIR::ExprKind::EXP) { GenerateOverflowSaturatingOp(retValue); return; } llvm::Value* val; if (kind == CHIR::ExprKind::MOD) { val = llvm::ConstantInt::getSigned(type, 0); } else { // inc/neg: MaxInt8 val = llvm::ConstantInt::getSigned(type, GetIntMaxOrMin(irBuilder, *ty, true)); } (void)irBuilder.CreateStore(val, retValue); } void OverflowHandler::GenerateOverflowStrategy(llvm::AllocaInst* ifValue, bool& needUnreachableTerminator) { // Throwing. if (strategy == OverflowStrategy::THROWING) { // If the operator is remainder, according to previous logic, // the expression must be INT_MIN % -1. // The result of INT_MIN % -1 is 0 which is defined in spec. if (kind == CHIR::ExprKind::MOD) { llvm::Type* type = CGType::GetOrCreate(irBuilder.GetCGModule(), ty)->GetLLVMType(); (void)irBuilder.CreateStore(llvm::ConstantInt::getSigned(type, 0), ifValue); needUnreachableTerminator = false; } else if (kind == CHIR::ExprKind::NEG) { irBuilder.CreateOverflowOrArithmeticException("sub"); needUnreachableTerminator = true; } else { auto op = OPERATOR_KIND_TO_OP_MAP.at(kind); irBuilder.CreateOverflowOrArithmeticException(op); needUnreachableTerminator = true; } return; } // Wrapping. if (strategy == OverflowStrategy::WRAPPING) { auto values = GetLLVMValues(); llvm::Value* minVal = irBuilder.GenerateOverflowWrappingFunc(kind, *ty, values); (void)irBuilder.CreateStore(minVal, ifValue); needUnreachableTerminator = false; return; } // Saturating. GenerateOverflowSaturating(ifValue); needUnreachableTerminator = false; return; } } // namespace llvm::Value* Cangjie::CodeGen::GenerateOverflow(IRBuilder2& irBuilder, const OverflowStrategy& strategy, const CHIR::ExprKind& kind, const std::pair& tys, const std::vector& argGenValues) { OverflowHandler handler = OverflowHandler(irBuilder, strategy, kind, tys, argGenValues); if (strategy == OverflowStrategy::SATURATING) { // Simply handle `add op` and non-signed-integer `sub op`. auto values = handler.GetLLVMValues(); auto rightValue = irBuilder.GenerateOverflowSaturatingFunc(kind, *handler.ty, values); if (rightValue != nullptr) { return rightValue; } } if (handler.IsDivOrMod()) { return handler.GenerateOverflowDivOrMod(); } if (strategy == OverflowStrategy::WRAPPING) { return handler.GenerateOverflowOpKindOption(); } const CHIR::Type* elemTy = handler.GetElemTy(); const CHIR::Type* optionTy = handler.GetOptionTy(); CGType* elemType = CGType::GetOrCreate(irBuilder.GetCGModule(), elemTy); CGType* optionType = CGType::GetOrCreate(irBuilder.GetCGModule(), optionTy); // Compute the result with an extra overflow flag. // Return value: (result, ov-flag: indicating whether overflow happens). llvm::Type* boolType = CGType::GetBoolCGType(irBuilder.GetCGModule())->GetLLVMType(); std::vector types{elemType->GetLLVMType(), boolType}; llvm::Type* structTy = llvm::StructType::get(irBuilder.GetLLVMContext(), types); StructInfo valInfo = {nullptr, structTy, types}; if (kind == CHIR::ExprKind::EXP) { valInfo.alc = irBuilder.CreateEntryAlloca(structTy, nullptr, "pow.ov"); handler.GenerateOverflowCheckPow(valInfo); } else { valInfo.alc = irBuilder.CreateEntryAlloca(structTy, nullptr, "val.ov"); handler.GenerateOverflowCheck(valInfo); } auto ovFlag = irBuilder.CreateLoad(valInfo.tys[1], irBuilder.CreateStructGEP(valInfo.alcTy, valInfo.alc, 1)); llvm::BasicBlock* overflowBB = nullptr; llvm::BasicBlock* normalBB = nullptr; llvm::BasicBlock* endBB = nullptr; std::tie(overflowBB, normalBB, endBB) = handler.GenerateCheckOverflowFlag(ovFlag); CJC_NULLPTR_CHECK(normalBB); CJC_NULLPTR_CHECK(overflowBB); CJC_NULLPTR_CHECK(endBB); CGType* ifType = handler.IsChecked() ? optionType : elemType; llvm::AllocaInst* ifValue = irBuilder.CreateEntryAlloca(ifType->GetLLVMType()); // Emit non-overflow body first to make it closer to the above block. irBuilder.SetInsertPoint(normalBB); handler.GenerateOverflowElseBody(ifValue, valInfo); (void)irBuilder.CreateBr(endBB); // Emit overflow body. irBuilder.SetInsertPoint(overflowBB); bool needUnrachableTerminator = false; handler.GenerateOverflowThenBody(ifValue, needUnrachableTerminator); needUnrachableTerminator ? (void)irBuilder.CreateUnreachable() : (void)irBuilder.CreateBr(endBB); irBuilder.SetInsertPoint(endBB); return irBuilder.CreateLoad(ifValue->getAllocatedType(), ifValue); } cangjie_compiler-1.0.7/src/CodeGen/CJNative/CJNativeGenReflectionInfo.cpp000066400000000000000000000016701510705540100262010ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * Implements the generator for llvm reflection info. */ #include "CJNative/CJNativeReflectionInfo.h" #include "CJNative/CJNativeMetadata.h" using namespace Cangjie; using namespace CodeGen; void CJNativeReflectionInfo::Gen() const { // Generate metadata for packages, classes, interfaces, structs, enums, global functions, and global variables. CGMetadata(cgMod, subCHIRPkg) .Needs(MetadataKind::PKG_METADATA) ->Needs(MetadataKind::CLASS_METADATA) ->Needs(MetadataKind::STRUCT_METADATA) ->Needs(MetadataKind::ENUM_METADATA) ->Needs(MetadataKind::GF_METADATA) ->Needs(MetadataKind::GV_METADATA) ->Gen(); } cangjie_compiler-1.0.7/src/CodeGen/CJNative/CJNativeIRBuilder.cpp000066400000000000000000002740211510705540100244640ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "IRBuilder.h" #include "llvm/IR/Value.h" #include "llvm/Support/Alignment.h" #include "Base/CGTypes/CGClassType.h" #include "Base/CGTypes/CGEnumType.h" #include "Base/CGTypes/CGFunctionType.h" #include "Base/CGTypes/CGType.h" #include "Base/CHIRExprWrapper.h" #include "CGModule.h" #include "IRAttribute.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Value.h" namespace Cangjie::CodeGen { llvm::Value* IRBuilder2::FixFuncArg(const CGValue& srcValue, const CGType& destType, bool isThisArgInStructMut) { auto srcRawVal = srcValue.GetRawValue(); auto srcRawValType = srcRawVal->getType(); const CGType* destDerefType = CGType::GetOrCreate(cgMod, DeRef(destType.GetOriginal())); // Note: this condition implicitly means: // - `srcValue` is not of a reference type because it is not a pointer type or its addrspace is 0. // That is, `srcValue` must be of value type. if ((!srcRawValType->isPointerTy() || srcRawValType->getPointerAddressSpace() == 0 || srcValue.GetCGType()->IsOptionLikeRef()) && !destDerefType->GetSize()) { if (isThisArgInStructMut) { // Note: in this branch, we should realize the dest doesn't begin with TypeInfo*. CJC_ASSERT(srcRawValType->getPointerAddressSpace() == 0); if (destType.GetLLVMType()->getPointerAddressSpace() == 0) { return CreateBitCast(srcRawVal, destType.GetLLVMType()); } else { return CreateAddrSpaceCast(srcRawVal, destType.GetLLVMType()); } } else { // Note: in this branch, we should realize the dest must begin with TypeInfo*. auto srcDerefType = DeRef(srcValue.GetCGType()->GetOriginal()); // 1. Allocate a stack memory for storing srcValue. auto temp = CallIntrinsicAllocaGeneric({CreateTypeInfo(*srcDerefType), GetLayoutSize_32(*srcDerefType)}); // 2. store srcValue to temp auto payloadPtr = GetPayloadFromObject(temp); const CGType* srcDerefCGType = CGType::GetOrCreate(cgMod, srcDerefType); if (srcDerefType->IsStruct()) { // Note: in this branch, it means: // - we are assigning a "struct" that doesn't begin with TypeInfo* to an address // that should begin with TypeInfo*. // - we are in the scope of a struct instance method(without "$withTI" postfix), // the "struct" mentioned above is the `this` parameter of the method. auto size = GetLayoutSize_64(*srcDerefType); if (IsTypeContainsRef(srcDerefCGType->GetLLVMType())) { CallGCWriteAgg({temp, payloadPtr, srcRawVal, size}); } else { CreateMemCpy(payloadPtr, llvm::MaybeAlign(), srcRawVal, llvm::MaybeAlign(), size); } } else { (void)CreateStore(srcValue, CGValue(CreateBitCast(payloadPtr, srcDerefCGType->GetLLVMType()->getPointerTo(1)), CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(cgMod.GetCGContext().GetCHIRBuilder(), *srcDerefType), CGType::TypeExtraInfo{1U}))); } return temp; } } const CGType* srcType = srcValue.GetCGType(); if (!srcType->IsStructPtrType() && !srcType->IsVArrayPtrType()) { return srcRawVal; } CJC_ASSERT(destType.GetLLVMType()->isPointerTy()); auto srcTypeAddrspace = srcRawVal->getType()->getPointerAddressSpace(); // 1 auto destTypeAddrspace = destType.GetAddrspace(); // 0 auto res = srcRawVal; if (srcTypeAddrspace == 0 && destTypeAddrspace == 1) { auto destTypePointerElementType = destType.GetPointerElementType()->GetLLVMType(); if (srcType->GetPointerElementType()->GetLLVMType() != destTypePointerElementType) { res = CreateBitCast(srcRawVal, destTypePointerElementType->getPointerTo(srcTypeAddrspace)); } return CreateAddrSpaceCast(res, destTypePointerElementType->getPointerTo(1)); } CJC_ASSERT(srcTypeAddrspace == destTypeAddrspace && "incorrect addrspace for argument"); return CreateBitCast(res, destType.GetLLVMType()); } llvm::Value* IRBuilder2::CreateCallOrInvoke(const CGFunctionType& calleeType, llvm::Value* callee, std::vector args, bool isClosureCall, llvm::Value* thisTypeInfo) { const auto& structParamNeedsBasePtr = calleeType.GetStructParamNeedsBasePtrIndices(); const auto& realArgIndices = calleeType.GetRealArgIndices(); std::vector argsVal; size_t idx = 0; for (auto arg : args) { bool isThisArgInStruct = false; if (idx == 0 && this->chirExpr && dynamic_cast(this->chirExpr)) { auto applyWrapper = static_cast(this->chirExpr); isThisArgInStruct = applyWrapper->IsCalleeStructInstanceMethod(); } auto llvmVal = FixFuncArg(*arg, *calleeType.GetParamType(idx), isThisArgInStruct); (void)argsVal.emplace_back(llvmVal); // Insert the fixed argument, may do some casting meanwhile. auto cgType = arg->GetCGType(); CJC_ASSERT(!cgType->IsStructType()); if (structParamNeedsBasePtr.find(realArgIndices[idx]) != structParamNeedsBasePtr.end()) { // Insert the basePtr for the structure argument. if (auto basePtr = cgMod.GetCGContext().GetBasePtrOf(llvmVal)) { (void)argsVal.emplace_back(basePtr); } else { auto addrspace = calleeType.GetParamType(idx)->GetAddrspace(); (void)argsVal.emplace_back( llvm::Constant::getNullValue(llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext(), addrspace))); } } ++idx; } // Determine whether we need to add an extra argument at the beginning to store the result. auto& chirFuncType = StaticCast(calleeType.GetOriginal()); auto returnCHIRType = chirFuncType.GetReturnType(); auto returnCGType = CGType::GetOrCreate(cgMod, returnCHIRType); if (calleeType.HasSRet()) { llvm::Value* allocaForRetVal = nullptr; if (!returnCGType->GetSize()) { CHIR::Type* retValType = nullptr; if (auto applyWrapper = dynamic_cast(this->chirExpr); applyWrapper) { retValType = applyWrapper->GetResult()->GetType(); } else if (this->chirExpr->GetExprKind() == CHIR::ExprKind::VARRAY_BUILDER) { auto& varrayBuilder = StaticCast(this->chirExpr->GetChirExpr()); retValType = StaticCast(varrayBuilder.GetResult()->GetType())->GetElementType(); } else { CJC_ASSERT(false && "Should not reach here."); } if (retValType->IsGeneric()) { // `retValType` is `T` allocaForRetVal = CreateEntryAlloca(*returnCGType); CreateStore(llvm::ConstantPointerNull::get(getInt8PtrTy(1U)), allocaForRetVal); auto [prepareForNonRefBB, endBB] = Vec2Tuple<2>(CreateAndInsertBasicBlocks({"prepNRSRet", "end"})); auto ti = CreateTypeInfo(*retValType); CreateCondBr(CreateTypeInfoIsReferenceCall(*retValType), endBB, prepareForNonRefBB); SetInsertPoint(prepareForNonRefBB); CreateStore(CallIntrinsicAllocaGeneric({ti, GetLayoutSize_32(*retValType)}), allocaForRetVal); CreateBr(endBB); SetInsertPoint(endBB); } else { // `retValType` is NOT `T` auto retValCGType = CGType::GetOrCreate(cgMod, retValType); if (returnCHIRType->IsGeneric()) { // generate a pointer to ti memory allocaForRetVal = CreateEntryAlloca(*returnCGType); CreateStore(llvm::ConstantPointerNull::get(getInt8PtrTy(1U)), allocaForRetVal); if (!retValCGType->IsReference()) { std::vector parameters{ CreateTypeInfo(*retValType), GetLayoutSize_32(*retValType)}; CreateStore(CallIntrinsicAllocaGeneric(parameters), allocaForRetVal); } } else if (returnCHIRType->IsStruct() || returnCHIRType->IsTuple() || (returnCHIRType->IsEnum() && StaticCast(returnCGType)->IsOptionLike())) { std::vector parameters{CreateTypeInfo(*retValType), GetLayoutSize_32(*retValType)}; allocaForRetVal = CallIntrinsicAllocaGeneric(parameters); } else { allocaForRetVal = CreateEntryAlloca(*retValCGType); } } } else { allocaForRetVal = CreateEntryAlloca(*returnCGType); } argsVal.insert(argsVal.begin(), allocaForRetVal); } if (this->chirExpr && dynamic_cast(this->chirExpr)) { auto applyWrapper = static_cast(this->chirExpr); for (auto instantiateArg : applyWrapper->GetInstantiatedTypeArgs()) { auto typeInfo = CreateTypeInfo(*instantiateArg); typeInfo = CreateBitCast(typeInfo, CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext())); (void)argsVal.emplace_back(typeInfo); } // OuterTypeInfo if (applyWrapper->IsCalleeMethod()) { llvm::Value* typeInfo{nullptr}; if (applyWrapper->IsCalleeStatic()) { if (this->chirExpr->GetExprKind() == CHIR::ExprKind::APPLY || this->chirExpr->GetExprKind() == CHIR::ExprKind::APPLY_WITH_EXCEPTION) { typeInfo = CreateBitCast(CreateTypeInfo( applyWrapper->GetOuterType(GetCGContext().GetCHIRBuilder())), CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext())); } else { if (DeRef(*applyWrapper->GetThisType())->IsThis()) { typeInfo = thisTypeInfo; } else { typeInfo = CreateBitCast(CreateTypeInfo(applyWrapper->GetThisType()), CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext())); } } } else { if (this->chirExpr->GetExprKind() == CHIR::ExprKind::APPLY || this->chirExpr->GetExprKind() == CHIR::ExprKind::APPLY_WITH_EXCEPTION) { typeInfo = CreateBitCast(CreateTypeInfo( applyWrapper->GetOuterType(GetCGContext().GetCHIRBuilder())), CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext())); } else { auto thisVal = **(cgMod | applyWrapper->GetThisParam()); typeInfo = GetTypeInfoFromObject(thisVal); } } (void)argsVal.emplace_back(typeInfo); } else if (isClosureCall) { // Handle invocation via closure auto thisVal = **(cgMod | applyWrapper->GetOperand(1)); (void)argsVal.emplace_back(GetTypeInfoFromObject(thisVal)); } // ThisTypeInfo if (applyWrapper->IsCalleeStatic()) { CJC_NULLPTR_CHECK(thisTypeInfo); (void)argsVal.emplace_back(thisTypeInfo); } } else if (this->chirExpr && this->chirExpr->GetExprKind() == CHIR::ExprKind::VARRAY_BUILDER) { auto& varrayBuilder = StaticCast(this->chirExpr->GetChirExpr()); auto autoEnvOfInitFunc = varrayBuilder.GetInitFunc(); auto autoEnvVal = **(cgMod | autoEnvOfInitFunc); auto typeInfo = CreateBitCast( GetTypeInfoFromObject(autoEnvVal), CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext())); (void)argsVal.emplace_back(typeInfo); } // Emit the call or invoke instruction. llvm::CallBase* callBaseInst = nullptr; CallSetLocation(); auto unwindBlock = cgMod.GetCGContext().TopUnwindBlockStack(); if (unwindBlock.has_value()) { auto currentBB = GetInsertBlock(); auto normalDest = llvm::BasicBlock::Create(getContext(), "normalDest", currentBB->getParent()); normalDest->moveAfter(currentBB); callBaseInst = CreateInvoke(calleeType.GetLLVMFunctionType(), callee, normalDest, unwindBlock.value(), argsVal); SetInsertPoint(normalDest); } else { callBaseInst = CreateCall(calleeType.GetLLVMFunctionType(), callee, argsVal); } if (auto f = llvm::dyn_cast(callee); f && f->hasFnAttribute(HAS_WITH_TI_WRAPPER_ATTR)) { auto applyExprW = dynamic_cast(chirExpr); CJC_ASSERT(applyExprW); cgMod.GetCGContext().AddCallBaseToReplace(callBaseInst, *applyExprW); } // Determine which value we should return: // - if this is an SRet call, the first one argument is the result. // - otherwise, the value of callBaseInst is the result. llvm::Value* ret = callBaseInst; if (calleeType.HasSRet()) { callBaseInst->addAttributeAtIndex(llvm::AttributeList::FirstArgIndex, llvm::Attribute::NoAlias); auto sretAttr = llvm::Attribute::getWithStructRetType(callBaseInst->getContext(), (!returnCGType->GetSize() && !returnCGType->GetOriginal().IsGeneric()) ? llvm::Type::getInt8Ty(GetLLVMContext()) : returnCGType->GetLLVMType()); callBaseInst->addAttributeAtIndex(llvm::AttributeList::FirstArgIndex, sretAttr); ret = argsVal.front(); if (!returnCGType->GetSize()) { CHIR::Type* rstType = nullptr; if (this->chirExpr->GetExprKind() == CHIR::ExprKind::VARRAY_BUILDER) { auto& varrayBuilder = StaticCast(this->chirExpr->GetChirExpr()); rstType = StaticCast(varrayBuilder.GetResult()->GetType())->GetElementType(); } else { rstType = this->chirExpr->GetResult()->GetType(); } auto rstCGType = CGType::GetOrCreate(cgMod, rstType); if (chirFuncType.GetReturnType()->IsGeneric()) { ret = CreateLoad(returnCGType->GetLLVMType(), ret); } if (!rstCGType->GetSize() && !rstType->IsGeneric()) { // Opt: if we can return `ret` without the copy? auto ti = CreateTypeInfo(*rstType); auto tmp = CallIntrinsicAllocaGeneric({ti, GetLayoutSize_32(*rstType)}); CallIntrinsicAssignGeneric({tmp, ret, ti}); ret = tmp; } else if (rstCGType->GetSize() && !rstCGType->IsReference()) { auto elementType = rstCGType->GetLLVMType(); auto srcPayload = CreateBitCast(GetPayloadFromObject(ret), elementType->getPointerTo(1U)); return CreateLoad(elementType, srcPayload); } } } else if (chirFuncType.GetReturnType()->IsGeneric()) { CJC_ASSERT(false); auto rstType = chirExpr->GetResult()->GetType(); auto rstCGType = CGType::GetOrCreate(cgMod, rstType); if (rstCGType->GetSize() && !rstCGType->IsReference()) { auto elementType = rstCGType->GetLLVMType(); auto srcPayload = CreateBitCast(GetPayloadFromObject(ret), elementType->getPointerTo(1U)); return CreateLoad(elementType, srcPayload); } } // Determine whether we need to add the metadata that would infer backend to do some optimizations. if (isClosureCall) { auto& ctx = getContext(); // `IsClosureCall` is used to confirm that the call does not // cause env escape when analyzing whether an escape occurs. // In this way, the env can be allocated to the stack. const std::string closureMeta = "IsClosureCall"; callBaseInst->setMetadata(closureMeta, llvm::MDTuple::get(ctx, {llvm::MDString::get(ctx, closureMeta)})); } if (chirFuncType.GetReturnType()->IsUnit()) { ret = cgMod.GenerateUnitTypeValue(); } return ret; } namespace { void ConvertArgsType(llvm::IRBuilder<>& irBuilder, const llvm::Function* func, std::vector& args) { // Callee guarantees func not null and arguments size is equal to function parameters size. CJC_ASSERT(args.size() == func->arg_size()); auto functionType = func->getFunctionType(); for (unsigned i = 0; i < func->arg_size(); ++i) { if (!args[i]->getType()->isPointerTy()) { continue; } args[i] = irBuilder.CreatePointerCast(args[i], functionType->getParamType(i)); } } } // namespace llvm::Type* IRBuilder2::GetSizetLLVMType() const { if (GetCGContext().GetCompileOptions().target.arch == Triple::ArchType::ARM32) { return llvm::Type::getInt32Ty(GetLLVMContext()); } else { return llvm::Type::getInt64Ty(GetLLVMContext()); } } CHIR::Type::TypeKind IRBuilder2::GetTypeKindFromType(const CHIR::Type& ty) const { auto typeKind = ty.GetTypeKind(); auto isArm32 = GetCGContext().GetCompileOptions().target.arch == Triple::ArchType::ARM32; if (typeKind == CHIR::Type::TypeKind::TYPE_UINT_NATIVE) { typeKind = isArm32 ? CHIR::Type::TypeKind::TYPE_UINT32 : CHIR::Type::TypeKind::TYPE_UINT64; } else if (typeKind == CHIR::Type::TypeKind::TYPE_INT_NATIVE) { typeKind = isArm32 ? CHIR::Type::TypeKind::TYPE_INT32 : CHIR::Type::TypeKind::TYPE_INT64; } return typeKind; } // For 64 bit OS: sizeof a pointer is 8U // For 32 bit OS: sizeof a pointer is 4U size_t IRBuilder2::GetPtrSize() const { return GetCGContext().GetCompileOptions().target.arch == Triple::ArchType::ARM32 ? 4U : 8U; } // For 64 bit OS: {typeinfo*, } // For 32 bit OS: {typeinfo*, i32, } size_t IRBuilder2::GetPayloadOffset() const { return GetCGContext().GetCompileOptions().target.arch == Triple::ArchType::ARM32 ? GetPtrSize() + 4U : GetPtrSize(); } bool IRBuilder2::IsGlobalVariableBasePtr(llvm::Value* val) const { return llvm::isa(val->stripInBoundsOffsets()); } llvm::Instruction* IRBuilder2::CallGCRead(std::vector args) { // Func: i8 addr1* @llvm.cj.gcread.ref(i8 addr1* baseObj, i8 addr1*addr1* place) auto func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_gcread_ref); ConvertArgsType(*this, func, args); return CreateCall(func, args); } llvm::Instruction* IRBuilder2::CallGCReadWeakRef(std::vector args) { // Func: i8 addr1* @llvm.cj.gcread.weakref(i8 addr1* baseObj, i8 addr1*addr1* place) auto func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_gcread_weakref); ConvertArgsType(*this, func, args); return CreateCall(func, args); } llvm::Instruction* IRBuilder2::CallGCReadAgg(std::vector args) { // Func: void @llvm.cj.gcread.struct(i8 addr1* baseObj, i8 addr1* dst, i8 addr1*/ i8* src, i64 length) auto func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_gcread_struct, {GetSizetLLVMType()}); args[3] = CreateZExtOrTrunc(args[3], GetSizetLLVMType()); ConvertArgsType(*this, func, args); return CreateCall(func, args); } llvm::Instruction* IRBuilder2::CallGCReadStaticRef(const std::vector& args) { // Func: void @llvm.cj.gcread.static.ref(i8 addr1** val) return CreateCall( llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_gcread_static_ref), args); } llvm::Instruction* IRBuilder2::CallGCReadStaticAgg(llvm::StructType* type, std::vector args) { // Func: void @llvm.cj.gcread.static.struct(i8* dst, i8* src, i64 len) // 3 arguments. CJC_ASSERT(args.size() == 3); llvm::LLVMContext& ctx = getContext(); auto func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_gcread_static_struct, {GetSizetLLVMType()}); args[2] = CreateZExtOrTrunc(args[2], GetSizetLLVMType()); auto typeName = GetCodeGenTypeName(type); auto meta = llvm::MDTuple::get(ctx, {llvm::MDString::get(ctx, typeName)}); ConvertArgsType(*this, func, args); auto inst = CreateCall(func, args); inst->setMetadata("AggType", meta); return inst; } llvm::Instruction* IRBuilder2::CallGCWrite(std::vector args) { // Func: void @llvm.cj.gcwrite.ref(type val, i8 addr1* baseObj, type addr1* place) // E.g. void @llvm.cj.gcwrite.ref(i8 addr1* val, i8 addr1* baseObj, i8 addr1*addr1* refPtr) auto func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_gcwrite_ref); ConvertArgsType(*this, func, args); return CreateCall(func, args); } llvm::Instruction* IRBuilder2::CallGCWriteAgg(std::vector args) { // The intrinsic function has 4 arguments. CJC_ASSERT(args.size() == 4); // The last one (3rd) argument means size. if (auto size = llvm::dyn_cast(args[3]); size && size->isZero()) { return nullptr; } // real type of third parameter: args[2]->getType() llvm::Type* type = llvm::Type::getInt8PtrTy(GetLLVMContext(), args[2]->getType()->getPointerAddressSpace()); // Func: void @llvm.cj.gcwrite.struct(i8 addr1* baseObj, i8 addr1* dst, i8 addr1*/ i8* src, i64 length) auto func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_gcwrite_struct, {type, GetSizetLLVMType()}); args[3] = CreateZExtOrTrunc(args[3], GetSizetLLVMType()); ConvertArgsType(*this, func, args); return CreateCall(func, args); } llvm::Instruction* IRBuilder2::CallGCWriteStaticRef(const std::vector& args) { // Func: void @llvm.cj.gcwrite.static(i8 addr1* val, i8 addr1** place) return CreateCall( llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_gcwrite_static_ref), args); } llvm::Instruction* IRBuilder2::CallGCWriteStaticAgg(llvm::StructType* type, std::vector args) { // Func: void @llvm.cj.gcwrite.static.struct(i8* dst, i8* src, i64 len) // 3 arguments. CJC_ASSERT(args.size() == 3); // The last one (2nd) argument means size. if (auto size = llvm::dyn_cast(args[2]); size && size->isZero()) { return nullptr; } llvm::LLVMContext& ctx = getContext(); auto func = llvm::Intrinsic::getDeclaration( cgMod.GetLLVMModule(), static_cast(llvm::Intrinsic::cj_gcwrite_static_struct), {GetSizetLLVMType()}); args[2] = CreateZExtOrTrunc(args[2], GetSizetLLVMType()); auto typeName = GetCodeGenTypeName(type); auto meta = llvm::MDTuple::get(ctx, {llvm::MDString::get(ctx, typeName)}); ConvertArgsType(*this, func, args); auto inst = CreateCall(func, args); inst->setMetadata("AggType", meta); return inst; } llvm::Instruction* IRBuilder2::CreateWriteBarrierForGlobalVariable(const CGValue& value, const CGValue& globalVar) { const CGType* valueType = value.GetCGType(); CJC_ASSERT(valueType != nullptr); llvm::Instruction* inst = nullptr; if (valueType->IsStructPtrType()) { auto structType = llvm::cast(valueType->GetPointerElementType()->GetLLVMType()); auto layOut = cgMod.GetLLVMModule()->getDataLayout().getStructLayout(structType); auto size = llvm::ConstantInt::get(llvm::Type::getInt64Ty(GetLLVMContext()), layOut->getSizeInBytes()); inst = CallGCWriteStaticAgg(structType, {globalVar.GetRawValue(), value.GetRawValue(), size}); } else { inst = CallGCWriteStaticRef({value.GetRawValue(), globalVar.GetRawValue()}); } if (llvm::isa(value.GetRawValue()) && inst) { inst->setDebugLoc(llvm::DebugLoc()); } return inst; } llvm::StoreInst* IRBuilder2::CreateStore(llvm::Value* val, const CGValue& cgDestAddr, bool isVolatile) { return LLVMIRBuilder2::CreateStore(val, *cgDestAddr, isVolatile); } llvm::Instruction* IRBuilder2::CreateStore( llvm::Value* val, llvm::Value* ptr, const Cangjie::CHIR::Type* type, bool isVolatile) { auto valCGType = CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(GetCGContext().GetCHIRBuilder(), *type)); auto ptrCGType = CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(GetCGContext().GetCHIRBuilder(), *type)); return CreateStore(CGValue(val, valCGType), CGValue(ptr, ptrCGType)); } llvm::Instruction* IRBuilder2::CreateStore(const CGValue& cgVal, const CGValue& cgDestAddr) { bool isMemberWrite = GetCGContext().GetBasePtrOf(cgDestAddr.GetRawValue()) != nullptr; bool isVolatile = false; const CGType* valType = cgVal.GetCGType(); const CGType* destDerefType = cgDestAddr.GetCGType()->GetPointerElementType(); CJC_ASSERT(destDerefType != nullptr); auto val = cgVal.GetRawValue(); auto destAddr = cgDestAddr.GetRawValue(); // GetTypeInfoIsReference if (valType && valType->GetOriginal().IsGeneric() && !valType->GetSize() && destDerefType->GetOriginal().IsGeneric() && !destDerefType->GetSize() && !isMemberWrite) { auto dstTypeInfo = CreateTypeInfo(destDerefType->GetOriginal()); // Check whether dstType is a reference. auto [handleRefBB, handleNonRefBB, exitBB] = Vec2Tuple<3>(CreateAndInsertBasicBlocks( {GenNameForBB("handle_store_ref"), GenNameForBB("handle_store_non_ref"), GenNameForBB("store_exit")})); destAddr = CreateBitCast(destAddr, getInt8PtrTy(1U)->getPointerTo(destAddr->getType()->getPointerAddressSpace())); CreateCondBr(CreateTypeInfoIsReferenceCall(destDerefType->GetOriginal()), handleRefBB, handleNonRefBB); SetInsertPoint(handleRefBB); LLVMIRBuilder2::CreateStore(val, destAddr, isVolatile); CreateBr(exitBB); SetInsertPoint(handleNonRefBB); llvm::Value* tmpPtr = nullptr; if (cgDestAddr.IsSRetArg()) { tmpPtr = CreateLoad(cgDestAddr); } else { auto dstTypeSize = GetLayoutSize_32(destDerefType->GetOriginal()); tmpPtr = CallIntrinsicAllocaGeneric({dstTypeInfo, dstTypeSize}); (void)CreateStore(tmpPtr, destAddr); } std::vector copyGenericParams{tmpPtr, val, dstTypeInfo}; CallIntrinsicAssignGeneric(copyGenericParams); CreateBr(exitBB); SetInsertPoint(exitBB); return nullptr; } if (!isMemberWrite) { if (destDerefType && !destDerefType->GetOriginal().IsRef() && cgVal.GetCGType() && cgVal.GetCGType()->GetOriginal().IsRef()) { if (auto valSize = cgVal.GetCGType()->GetPointerElementType()->GetSize()) { if (!destDerefType->GetSize()) { auto dataPtr = GetPayloadFromObject(destAddr); auto size = GetLayoutSize_32(cgVal.GetCGType()->GetOriginal()); return CallGCWriteAgg({destAddr, dataPtr, val, size}); } } else { CJC_ASSERT(!destDerefType->GetSize()); if (destAddr->getType()->getPointerAddressSpace() == 1U && val->getType()->getPointerAddressSpace() == 0U) { auto size = GetSize_32(cgVal.GetCGType()->GetOriginal()); return CallGCWriteGenericPayload({destAddr, val, size}); } auto ti = CreateTypeInfo(cgVal.GetCGType()->GetOriginal()); return CallIntrinsicAssignGeneric({destAddr, val, ti}); } } } // When storing to global variable and cgVal contains a cj class type value, // we need to use gc write barrier, i.e. llvm.cj.gcwrite.static.xxx if (IsGlobalVariableBasePtr(destAddr) && IsTypeContainsRef(destDerefType->GetLLVMType())) { return CreateWriteBarrierForGlobalVariable(cgVal, cgDestAddr); } auto dealWithGCWriteBarrier = [this, &cgDestAddr, &cgVal, &isMemberWrite, &isVolatile]( const CGType* cgValType, llvm::Value* basePtr) -> llvm::Instruction* { if (auto elemType = cgDestAddr.GetCGType()->GetPointerElementType(); !elemType->GetSize()) { CJC_ASSERT(isMemberWrite); auto size = GetSize_32(cgValType->GetOriginal()); auto [gcwriteRefBB, gcwriteNonRefBB, gcwriteExitBB] = Vec2Tuple<3>(CreateAndInsertBasicBlocks( {GenNameForBB("gcwrite_ref"), GenNameForBB("gcwrite_non_ref"), GenNameForBB("gcwrite_exit")})); CreateCondBr(CreateTypeInfoIsReferenceCall(elemType->GetOriginal()), gcwriteRefBB, gcwriteNonRefBB); SetInsertPoint(gcwriteRefBB); CallGCWrite({cgVal.GetRawValue(), basePtr, cgDestAddr.GetRawValue()}); CreateBr(gcwriteExitBB); SetInsertPoint(gcwriteNonRefBB); CallIntrinsicGCWriteGeneric({basePtr, cgDestAddr.GetRawValue(), cgVal.GetRawValue(), size}); CreateBr(gcwriteExitBB); SetInsertPoint(gcwriteExitBB); return nullptr; } if (cgValType->IsStructPtrType()) { auto structType = llvm::cast(cgValType->GetPointerElementType()->GetLLVMType()); auto layOut = cgMod.GetLLVMModule()->getDataLayout().getStructLayout(structType); CJC_NULLPTR_CHECK(layOut); if (IsTypeContainsRef(cgValType->GetPointerElementType()->GetLLVMType())) { if (cgVal.GetRawValue()->getType()->getPointerAddressSpace() == 1U) { auto base = GetCGContext().GetBasePtrOf(cgVal.GetRawValue()); auto heapStructType = llvm::cast(cgValType->GetPointerElementType()->GetLLVMType()); auto tempVal = CreateEntryAlloca(heapStructType); auto heapLayout = cgMod.GetLLVMModule()->getDataLayout().getStructLayout(heapStructType); auto size = getInt64(heapLayout->getSizeInBytes()); CallGCReadAgg({tempVal, base, cgVal.GetRawValue(), size}); return CallGCWriteAgg({basePtr, cgDestAddr.GetRawValue(), tempVal, size}); } auto size = llvm::ConstantInt::get(llvm::Type::getInt64Ty(GetLLVMContext()), layOut->getSizeInBytes()); return CallGCWriteAgg({basePtr, cgDestAddr.GetRawValue(), cgVal.GetRawValue(), size}); } auto align = layOut->getAlignment(); return CreateMemCpy(cgDestAddr.GetRawValue(), align, cgVal.GetRawValue(), align, layOut->getSizeInBytes()); } else if (cgValType->IsVArrayPtrType() && cgDestAddr.GetCGType()->IsVArrayPtrType()) { auto valueType = cgValType->GetPointerElementType()->GetLLVMType(); auto layout = cgMod.GetLLVMModule()->getDataLayout(); auto size = getInt64(layout.getTypeAllocSize(valueType)); if (IsTypeContainsRef(cgValType->GetPointerElementType()->GetLLVMType())) { return CallGCWriteAgg({basePtr, cgDestAddr.GetRawValue(), cgVal.GetRawValue(), size}); } auto align = llvm::Align(layout.getABITypeAlignment(valueType)); return CreateMemCpy(cgDestAddr.GetRawValue(), align, cgVal.GetRawValue(), align, size); } else if (cgValType->IsReference() || cgValType->IsOptionLikeRef()) { return CallGCWrite({cgVal.GetRawValue(), basePtr, cgDestAddr.GetRawValue()}); } return LLVMIRBuilder2::CreateStore(*cgVal, *cgDestAddr, isVolatile); }; // When writing to an object field, it need to use GC write barrier, i.e. llvm.cj.gcwrite intrinsic if (auto basePtr = cgMod.GetCGContext().GetBasePtrOf(destAddr); basePtr && basePtr->getType()->getPointerAddressSpace() == 1) { if (auto cgValType = cgVal.GetCGType(); cgValType) { return dealWithGCWriteBarrier(cgValType, basePtr); } return LLVMIRBuilder2::CreateStore(val, destAddr, isVolatile); } if (cgVal.GetCGType() && cgVal.GetCGType()->IsStructPtrType() && cgDestAddr.GetCGType() && cgDestAddr.GetCGType()->IsStructPtrType()) { auto layOut = cgMod.GetLLVMModule()->getDataLayout().getStructLayout( llvm::cast(cgVal.GetCGType()->GetPointerElementType()->GetLLVMType())); CJC_NULLPTR_CHECK(layOut); if (auto sizeInBytes = layOut->getSizeInBytes(); sizeInBytes) { auto align = layOut->getAlignment(); return CreateMemCpy(destAddr, align, val, align, sizeInBytes); } else { return nullptr; } } if (cgVal.GetCGType() && cgVal.GetCGType()->IsVArrayPtrType() && cgDestAddr.GetCGType() && cgDestAddr.GetCGType()->IsVArrayPtrType()) { auto valueType = cgVal.GetCGType()->GetPointerElementType()->GetLLVMType(); auto size = getInt64(cgMod.GetLLVMModule()->getDataLayout().getTypeAllocSize(valueType)); auto align = llvm::Align(cgMod.GetLLVMModule()->getDataLayout().getABITypeAlignment(valueType)); return CreateMemCpy(destAddr, align, val, align, size); } if (IsFuncPtrType(val->getType()) && IsLitStructPtrType(GetPointerElementType((destAddr)->getType()))) { val = CreateBitCast(val, llvm::PointerType::get(llvm::StructType::get(cgMod.GetLLVMContext()), 0)); return LLVMIRBuilder2::CreateStore(val, destAddr, isVolatile); } return LLVMIRBuilder2::CreateStore(val, destAddr, isVolatile); } namespace { bool IsLoadExprForAllocatedGenericRef(const CHIR::Expression& chirExpr) { if (chirExpr.GetExprKind() != CHIR::ExprKind::LOAD) { return false; } auto location = StaticCast(chirExpr).GetLocation(); CJC_NULLPTR_CHECK(location); if (!location->IsLocalVar() || !IsGenericRef(*location->GetType())) { return false; } auto locationExpr = StaticCast(location)->GetExpr(); CJC_NULLPTR_CHECK(locationExpr); auto locationExprKind = locationExpr->GetExprKind(); if (locationExprKind != CHIR::ExprKind::ALLOCATE && locationExprKind != CHIR::ExprKind::ALLOCATE_WITH_EXCEPTION) { return false; } return true; } } // namespace llvm::Value* IRBuilder2::CreateLoad(llvm::Type* elementType, llvm::Value* addr, const llvm::Twine& name) { // For CJNative backend, the structure type is coerced passed by reference, // no real load operation happens. To simplify this process, we prefer to do // a lazy loading instead of an instant loading. So return the value directly. if (IsGlobalVariableBasePtr(addr) && IsTypeContainsRef(elementType)) { if (elementType->isStructTy()) { auto tempVal = CreateEntryAlloca(elementType); auto structType = llvm::cast(elementType); auto layOut = GetLLVMModule()->getDataLayout().getStructLayout(structType); auto size = getInt64(layOut->getSizeInBytes()); CallGCReadStaticAgg(structType, {tempVal, addr, size}); return tempVal; } else { return CallGCReadStaticRef({addr}); } } if (elementType->isStructTy() && elementType->getStructName() == UNIT_TYPE_STR) { return cgMod.GenerateUnitTypeValue(); } if (elementType->isStructTy()) { auto tempVal = CreateEntryAlloca(elementType); if (auto base = GetCGContext().GetBasePtrOf(addr); base && base->getType()->getPointerAddressSpace() == 1 && IsTypeContainsRef(elementType)) { auto structType = llvm::cast(elementType); auto layOut = GetLLVMModule()->getDataLayout().getStructLayout(structType); auto size = getInt64(layOut->getSizeInBytes()); CallGCReadAgg({tempVal, base, addr, size}); } else { auto layOut = GetLLVMModule()->getDataLayout().getStructLayout(llvm::cast(elementType)); CJC_NULLPTR_CHECK(layOut); if (auto sizeInBytes = layOut->getSizeInBytes(); sizeInBytes) { auto align = layOut->getAlignment(); (void)CreateMemCpy(tempVal, align, addr, align, sizeInBytes); } } return tempVal; } if (elementType->isArrayTy()) { auto tempVal = CreateEntryAlloca(elementType); auto size = getInt64(GetLLVMModule()->getDataLayout().getTypeAllocSize(elementType)); auto align = llvm::Align(GetLLVMModule()->getDataLayout().getABITypeAlignment(elementType)); (void)CreateMemCpy(tempVal, align, addr, align, size); return tempVal; } if (elementType->isFunctionTy()) { return addr; } // If the field is of generic type: if (this->chirExpr && (this->chirExpr->GetExprKind() == CHIR::ExprKind::INTRINSIC || this->chirExpr->GetExprKind() == CHIR::ExprKind::LOAD || this->chirExpr->GetExprKind() == CHIR::ExprKind::FIELD)) { auto elemCHIRType = this->chirExpr->GetResult()->GetType(); if (auto t = DeRef(*elemCHIRType); t && t->IsGeneric()) { if (IsLoadExprForAllocatedGenericRef(chirExpr->GetChirExpr())) { return LLVMIRBuilder2::CreateLoad(elementType, addr, name); } auto [handleRefBB, handleNonRefBB, exitBB] = Vec2Tuple<3>(CreateAndInsertBasicBlocks( {GenNameForBB("handle_load_ref"), GenNameForBB("handle_load_non_ref"), GenNameForBB("load_exit")})); auto ti = CreateTypeInfo(*t); CreateCondBr(CreateTypeInfoIsReferenceCall(*t), handleRefBB, handleNonRefBB); // gcread.ref while T is reference type: SetInsertPoint(handleRefBB); llvm::Value* refVal{nullptr}; if (auto base = GetCGContext().GetBasePtrOf(addr)) { refVal = CallGCRead({base, addr}); } else { auto addrFixedType = getInt8PtrTy(1)->getPointerTo(addr->getType()->getPointerAddressSpace()); auto casetedAddr = CreateBitCast(addr, addrFixedType); refVal = LLVMIRBuilder2::CreateLoad(getInt8PtrTy(1), casetedAddr); } CreateBr(exitBB); // gcread.generic while T is value type: SetInsertPoint(handleNonRefBB); auto tiSize = GetLayoutSize_32(*t); auto valueVal = CallIntrinsicAllocaGeneric({ti, tiSize}); if (auto base = GetCGContext().GetBasePtrOf(addr)) { CallGCReadGeneric({valueVal, base, addr, tiSize}); } else if (addr->getType() == getInt8PtrTy()) { CallGCWriteGenericPayload({valueVal, addr, tiSize}); } else { CJC_ASSERT(addr->getType() == getInt8PtrTy(1U)->getPointerTo()); CallIntrinsicAssignGeneric({valueVal, LLVMIRBuilder2::CreateLoad(getInt8PtrTy(1), addr), ti}); } CreateBr(exitBB); SetInsertPoint(exitBB); auto phi = CreatePHI(elementType, 2U); phi->addIncoming(refVal, handleRefBB); phi->addIncoming(valueVal, handleNonRefBB); return phi; } else if (!CGType::GetOrCreate(cgMod, elemCHIRType)->GetSize()) { auto tiOfElement = CreateTypeInfo(elemCHIRType); auto tiSize = GetSizeFromTypeInfo(tiOfElement); auto valueVal = CallIntrinsicAllocaGeneric({tiOfElement, tiSize}); if (auto base = GetCGContext().GetBasePtrOf(addr)) { CallGCReadGeneric({valueVal, base, addr, tiSize}); } else if (addr->getType() == getInt8PtrTy()) { CallGCWriteGenericPayload({valueVal, addr, tiSize}); } else { CJC_ASSERT(addr->getType() == getInt8PtrTy(1U)->getPointerTo()); CallIntrinsicAssignGeneric({valueVal, LLVMIRBuilder2::CreateLoad(getInt8PtrTy(1), addr), tiOfElement}); } return valueVal; } } if (auto base = GetCGContext().GetBasePtrOf(addr); base && elementType == CGType::GetRefType(GetLLVMContext())) { CJC_ASSERT(base->getType()->getPointerAddressSpace() == 1); return CallGCRead({base, addr}); } return LLVMIRBuilder2::CreateLoad(elementType, addr, name); } llvm::CallInst* IRBuilder2::CreateMemsetStructWith0(llvm::AllocaInst* addr) { auto curPt = LLVMIRBuilder2::GetInsertPoint(); auto curBB = LLVMIRBuilder2::GetInsertBlock(); CJC_ASSERT(curBB && "Can't get the current basic block."); auto curFunc = curBB->getParent(); CJC_ASSERT(curFunc && "Can't get the current function."); auto entryBB = &curFunc->getEntryBlock(); auto currentLoc = getCurrentDebugLocation(); SetInsertPointToAllocas(curFunc); SetInsertPoint(entryBB->getTerminator()); CJC_ASSERT(addr->getAllocatedType()->isStructTy() && "incorrect argument"); auto structType = llvm::cast(addr->getAllocatedType()); auto layOut = GetLLVMModule()->getDataLayout().getStructLayout(structType); CJC_NULLPTR_CHECK(layOut); auto size = layOut->getSizeInBytes(); auto align = layOut->getAlignment(); auto memSetInst = CreateMemSet(addr, getInt8(0), size, align); LLVMIRBuilder2::SetInsertPoint(curBB, curPt); SetCurrentDebugLocation(currentLoc); return memSetInst; } llvm::CallInst* IRBuilder2::CreateCJMemSetStructWith0(llvm::AllocaInst* addr) { CJC_ASSERT(addr->getAllocatedType()->isStructTy() && "incorrect argument"); auto structType = llvm::cast(addr->getAllocatedType()); auto layOut = GetLLVMModule()->getDataLayout().getStructLayout(structType); CJC_NULLPTR_CHECK(layOut); auto size = layOut->getSizeInBytes(); auto align = layOut->getAlignment(); auto currentLoc = getCurrentDebugLocation(); SetCurrentDebugLocation(llvm::DebugLoc()); auto res = CreateMemSet(addr, getInt8(0), size, align); SetCurrentDebugLocation(currentLoc); auto cjMemsetFunc = llvm::Intrinsic::getDeclaration( GetLLVMModule(), llvm::Intrinsic::cj_memset, {*res->getFunctionType()->param_begin()}); res->setCalledFunction(cjMemsetFunc); return res; } llvm::Value* IRBuilder2::CreateNullValue(const CHIR::Type& ty) { CGType* cgType = CGType::GetOrCreate(cgMod, &ty); // There are some conventions for creating null value of a type: // ----------------------------------------------------------------------------------------- // Codegen | GV(global variable) | LV(local variable) // ------------|-----------------------|---------------------------------------------------- // CJNATIVE | llvm::Constant:: | 1) struct type: memset a struct with 0, returns // | getNullValue | the struct; // | | 2) non-struct type: llvm::Constant::getNullValue; // | | 3) array type: return alloca arrayType; // ----------------------------------------------------------------------------------------- // It indicates that we are generating null value for a GV while the current Insert Block is nullptr. llvm::Type* type = cgType->GetLLVMType(); if (!GetInsertBlock()) { return llvm::Constant::getNullValue(type); } if (!cgType->GetSize()) { auto typeInfoOfSrc = CreateTypeInfo(ty); std::vector parameters{typeInfoOfSrc, GetLayoutSize_32(ty)}; return CallIntrinsicAllocaGeneric(parameters); } else if (type == CGType::GetUnitCGType(cgMod)->GetLLVMType()) { return cgMod.GenerateUnitTypeValue(); } else if (type->isStructTy()) { auto alloca = CreateEntryAlloca(type); // Why we just handle with the structs that don't contain ref element? // Since `AddZeroInitForStructWithRefField` method will be invoked to deal with the // structs that contain ref element(s) uniformly. if (!IsTypeContainsRef(llvm::cast(alloca->getAllocatedType()))) { (void)CreateMemsetStructWith0(alloca); } return alloca; } else if (type->isArrayTy()) { auto alloca = CreateEntryAlloca(type); auto typeSize = GetCGModule().GetTypeSize(type); size_t idx = 0; constexpr auto SIZE_OF_2G = INT32_MAX; while (idx + SIZE_OF_2G < typeSize) { auto dataPtr = CreateGEP(type, alloca, {getInt64(0), getInt64(idx)}); (void)CreateMemSet(dataPtr, getInt8(0), SIZE_OF_2G, llvm::MaybeAlign(1)); idx += SIZE_OF_2G; } auto dataPtr = CreateGEP(type, alloca, {getInt64(0), getInt64(idx)}); (void)CreateMemSet(dataPtr, getInt8(0), typeSize - idx, llvm::MaybeAlign(1)); return alloca; } return llvm::Constant::getNullValue(type); } llvm::Value* IRBuilder2::CreateGEP( llvm::Type* type, llvm::Value* value, llvm::ArrayRef place, const llvm::Twine& name) { auto res = CreateInBoundsGEP(type, value, place, name); if (auto basePtr = GetCGContext().GetBasePtrOf(value)) { GetCGContext().SetBasePtr(res, basePtr); } else if (value->getType()->getPointerAddressSpace() == 1) { GetCGContext().SetBasePtr(res, value); } return res; } CGValue IRBuilder2::CreateGEP( const CGValue& cgVal, const std::vector& idxList, [[maybe_unused]] const llvm::Twine& name) { std::vector idxes; auto ret = cgVal.GetRawValue(); auto eleType = cgVal.GetCGType()->GetPointerElementType(); CJC_NULLPTR_CHECK(eleType); llvm::Value* basePtr = GetCGContext().GetBasePtrOf(ret); bool hasSkippedTypeInfo = basePtr && !eleType->IsReference(); if (!basePtr && !eleType->GetSize() && ret->getType() == getInt8PtrTy(1U)) { basePtr = ret; } // Sometimes we need to skip TypeInfo* at most 1 time. // For example: // %x: Tuple> = ... // %field: Generic-T1 = Field(%x, 1, 1) // \ \__ no TypeInfo* to skip when getting the second subscript element // \__ skip the TypeInfo* of %x when getting the first subscript element for (auto idx : idxList) { if (eleType->IsReference() || eleType->IsOptionLikeRef()) { // Cache the basePtr for the field basePtr = ret; } size_t offsetForAutoEnv = 0U; if (eleType->GetOriginal().IsAutoEnv()) { offsetForAutoEnv = 2U; } // Firstly, get the element. if (chirExpr && !eleType->GetSize()) { std::vector params = { CreateTypeInfo(eleType->GetOriginal()), getInt64(idx + offsetForAutoEnv)}; if (eleType->GetOriginal().IsRawArray()) { /* pre-offset: arrLength */ params.emplace_back(getInt32(8U)); } llvm::Value* offset = CallIntrinsicGetFieldOffset(params); CJC_NULLPTR_CHECK(chirExpr->GetTopLevelFunc()); CJC_NULLPTR_CHECK(GetInsertFunction()); if ((GetInsertFunction()->hasFnAttribute(HAS_WITH_TI_WRAPPER_ATTR) || GetInsertFunction()->hasFnAttribute(THIS_PARAM_HAS_BP)) && **(cgMod | chirExpr->GetTopLevelFunc()->GetParam(0)) == ret) { // It means this param doesn't have typeinfo, no need to add the offset. } else if (!hasSkippedTypeInfo) { offset = CreateAdd(offset, llvm::ConstantInt::get(offset->getType(), GetPayloadOffset())); hasSkippedTypeInfo = true; } ret = CreateInBoundsGEP(getInt8Ty(), ret, offset); } else if (auto classCGType = dynamic_cast(eleType)) { auto baseType = classCGType; ret = GetPayloadFromObject(ret); auto layoutType = static_cast(baseType)->GetLayoutType(); ret = CreateBitCast(ret, layoutType->getPointerTo(1)); ret = LLVMIRBuilder2::CreateStructGEP(layoutType, ret, idx + offsetForAutoEnv); } else if (auto arrayCGType = dynamic_cast(eleType)) { ret = GetArrayElementAddr( StaticCast(arrayCGType->GetOriginal()), ret, getInt64(idx), false); } else { CJC_ASSERT(!eleType->GetOriginal().IsRef()); ret = CreateBitCast(ret, eleType->GetLLVMType()->getPointerTo(ret->getType()->getPointerAddressSpace())); ret = LLVMIRBuilder2::CreateStructGEP(eleType->GetLLVMType(), ret, idx); } // Then, update the eleType for next iteration. if (auto arrayCGType = dynamic_cast(eleType)) { eleType = arrayCGType->GetElementCGType(); } else { eleType = eleType->GetContainedTypeAt(idx + offsetForAutoEnv); if (!eleType->GetOriginal().IsGeneric()) { ret = CreateBitCast(ret, eleType->GetLLVMType()->getPointerTo(ret->getType()->getPointerAddressSpace())); } } // Set the basePtr of the field, if any. if (basePtr) { GetCGContext().SetBasePtr(ret, basePtr); } } auto eleRefType = CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(GetCGContext().GetCHIRBuilder(), eleType->GetOriginal())); return CGValue(ret, eleRefType); } llvm::Value* IRBuilder2::InitArrayFilledWithConstant(llvm::Value* array, const CHIR::RawArrayType& arrTy, const std::vector& arrayConstantElems, const std::string& serialized) { auto arrayType = static_cast(CGType::GetOrCreate(cgMod, &arrTy))->GetLayoutType(); auto type = CGType::GetOrCreate(cgMod, arrTy.GetElementType()); CJC_NULLPTR_CHECK(type); auto elemType = type->IsStructPtrType() ? type->GetPointerElementType() : type; auto arrayLitLen = arrayConstantElems.size(); auto arrayLitType = llvm::ArrayType::get(elemType->GetLLVMType(), arrayLitLen); auto constVal = llvm::ConstantArray::get(arrayLitType, arrayConstantElems); auto layOut = GetLLVMModule()->getDataLayout(); auto& cgCtx = GetCGContext(); llvm::GlobalVariable* arrayConstant = GetCGModule().GetOrCreateGlobalVariable(constVal, serialized, false); auto arrPayloadPtr = GetPayloadFromObject(array); auto arrPtr = CreateBitCast(arrPayloadPtr, arrayType->getPointerTo(1u)); auto arrDataPtr = CreateStructGEP(arrayType, arrPtr, 1); auto srcArrI8Ptr = CreatePointerBitCastOrAddrSpaceCast(arrayConstant, getInt8PtrTy(1)); auto dstArrI8Ptr = CreateBitCast(arrDataPtr, getInt8PtrTy(1)); auto int64Type = llvm::Type::getInt64Ty(cgCtx.GetLLVMContext()); auto size = layOut.getTypeAllocSize(arrayConstant->getValueType()); auto dataSize = llvm::ConstantInt::get(int64Type, size); CreateCopyTo(ArrayCopyToInfo(srcArrI8Ptr, array, srcArrI8Ptr, dstArrI8Ptr, dataSize, getInt64(0), getInt64(0), elemType, getInt64(arrayLitLen))); return array; } void IRBuilder2::CreateCopyTo(ArrayCopyToInfo arrayCopyToInfo) { auto i8Ty = llvm::Type::getInt8Ty(GetLLVMContext()); auto i8PtrTy = llvm::Type::getInt8PtrTy(GetLLVMContext(), 1u); auto srcArrPtr = CreateBitCast(arrayCopyToInfo.srcArrPtr, i8PtrTy); auto dstArrPtr = CreateBitCast(arrayCopyToInfo.dstArrPtr, i8PtrTy); auto srcArrI8Ptr = CreateGEP(i8Ty, srcArrPtr, arrayCopyToInfo.srcIndex); auto dstArrI8Ptr = CreateGEP(i8Ty, dstArrPtr, arrayCopyToInfo.dstIndex); auto elemCGType = arrayCopyToInfo.elemType; if (!elemCGType->GetSize()) { auto func = llvm::Intrinsic::getDeclaration( GetLLVMModule(), static_cast(llvm::Intrinsic::cj_array_copy_generic), {GetSizetLLVMType()}); CreateCall(func->getFunctionType(), func, {arrayCopyToInfo.dstBP, dstArrI8Ptr, arrayCopyToInfo.srcBP, srcArrI8Ptr, CreateZExtOrTrunc(arrayCopyToInfo.dataSize, GetSizetLLVMType())}); return; } if (IsTypeContainsRef(elemCGType->GetLLVMType())) { bool isRefType = arrayCopyToInfo.elemType->GetLLVMType() == CGType::GetRefType(GetLLVMContext()); auto copyToInstrinsic = isRefType ? llvm::Intrinsic::cj_array_copy_ref : llvm::Intrinsic::cj_array_copy_struct; auto func = llvm::Intrinsic::getDeclaration(GetLLVMModule(), static_cast(copyToInstrinsic), {GetSizetLLVMType()}); // llvm.cj.array.copyto(i8 addrspace(1)* destBP, i8 addrspace(1)* , // i8 addrspace(1)* srcBP, i8 // addrspace(1)* , i64 ) CreateCall(func->getFunctionType(), func, {arrayCopyToInfo.dstBP, dstArrI8Ptr, arrayCopyToInfo.srcBP, srcArrI8Ptr, CreateZExtOrTrunc(arrayCopyToInfo.dataSize, GetSizetLLVMType())}); } else { auto align = llvm::Align(GetLLVMModule()->getDataLayout().getPrefTypeAlignment(elemCGType->GetLLVMType())); CreateMemMove(dstArrI8Ptr, align, srcArrI8Ptr, align, arrayCopyToInfo.dataSize); // Copy array data. } } llvm::Value* IRBuilder2::CallArrayIntrinsicAllocWithConstantContent(llvm::Value* array, const std::vector& args, const CHIR::RawArrayType& arrTy, const std::string& serialized) { // For the array literal that filled with constants // e.g., [1, 3, 5], // it is better to be initialized with array constant by using `memcpy`. auto isAllElemsConstant = IsAllConstantValue(args); CJC_ASSERT(isAllElemsConstant); std::vector cons(args.size()); std::vector newArgs; std::for_each(args.begin(), args.end(), [&newArgs](const CGValue* it) { newArgs.emplace_back(it->GetRawValue()); }); std::transform(newArgs.begin(), newArgs.end(), cons.begin(), [](llvm::Value* it) { auto tmp = llvm::dyn_cast(it); return tmp ? tmp->getInitializer() : llvm::cast(it); }); return InitArrayFilledWithConstant(array, arrTy, cons, serialized); } llvm::Value* IRBuilder2::AllocateArray(const CHIR::RawArrayType& rawArrayType, llvm::Value* length) { auto& llvmCtx = GetLLVMContext(); auto rawArrayTI = CreateBitCast(CreateTypeInfo(rawArrayType), llvm::Type::getInt8PtrTy(llvmCtx, 0)); auto int64Type = llvm::Type::getInt64Ty(llvmCtx); if (length->getType()->getIntegerBitWidth() < int64Type->getIntegerBitWidth()) { length = CreateSExt(length, int64Type); } auto elementSize = GetSize_64(*rawArrayType.GetElementType()); bool isArrayElementGeneric = CGType::GetOrCreate(cgMod, rawArrayType.GetElementType())->IsDynamicGI(); auto allocFunc = isArrayElementGeneric ? GetArrayGenericElemMalloc() : GetArrayNonGenericElemMalloc(); std::vector args = {rawArrayTI, length}; if (!isArrayElementGeneric) { args.emplace_back(elementSize); } auto callInst = CreateCallOrInvoke(allocFunc, args); AddRetAttr(llvm::cast(callInst), llvm::Attribute::NoAlias); return callInst; } llvm::Function* IRBuilder2::GetArrayNonGenericElemMalloc() const { auto gcArrayMallocFunc = llvm::Intrinsic::getDeclaration(GetLLVMModule(), static_cast(llvm::Intrinsic::cj_malloc_array)); AddRetAttr(gcArrayMallocFunc, llvm::Attribute::NoAlias); return gcArrayMallocFunc; } llvm::Function* IRBuilder2::GetArrayGenericElemMalloc() const { auto gcArrayMallocFunc = llvm::Intrinsic::getDeclaration( GetLLVMModule(), static_cast(llvm::Intrinsic::cj_malloc_array_generic)); AddRetAttr(gcArrayMallocFunc, llvm::Attribute::NoAlias); return gcArrayMallocFunc; } llvm::Value* IRBuilder2::CallArrayIntrinsicInitWithContent(const CHIR::RawArrayLiteralInit& arrayLiteralInit) { auto arrTy = arrayLiteralInit.GetRawArray()->GetType()->GetTypeArgs()[0]; CJC_ASSERT(arrTy->GetTypeKind() == CHIR::Type::TYPE_RAWARRAY); auto value = cgMod.GetMappedValue(arrayLiteralInit.GetRawArray()); CJC_ASSERT(value); auto arrayV = **value; auto rawArrTy = static_cast(arrTy); auto elements = arrayLiteralInit.GetElements(); auto [isConstantArray, serialized] = IsConstantArray(arrayLiteralInit); if (isConstantArray) { std::vector params; for (auto ele : elements) { params.emplace_back(cgMod.GetMappedValue(ele)); } return CallArrayIntrinsicAllocWithConstantContent(arrayV, params, *rawArrTy, serialized); } auto elemType = CGType::GetOrCreate(cgMod, rawArrTy->GetElementType()); for (size_t i = 0; i < elements.size(); ++i) { auto element = cgMod.GetMappedValue(elements[i]); CJC_ASSERT(element); llvm::Value* elemValue = element->GetRawValue(); CJC_ASSERT(elemValue); #ifdef NDEBUG auto idxName = "arr.idx" + std::to_string(i) + "E"; #else auto idxName = "arr" + arrayLiteralInit.GetResult()->GetSrcCodeIdentifier() + ".idx" + std::to_string(i) + "E"; #endif auto elemPtr = CreateGEP(*value, {i}, idxName).GetRawValue(); // fixme need to implement CreateRefStore if (elemType->IsStructType()) { auto structType = llvm::cast(elemType->GetLLVMType()); auto layOut = GetLLVMModule()->getDataLayout().getStructLayout(structType); auto size = getInt64(layOut->getSizeInBytes()); CJC_NULLPTR_CHECK(layOut); if (IsTypeContainsRef(elemType->GetLLVMType())) { CallGCWriteAgg({arrayV, elemPtr, elemValue, size}); } else { auto align = layOut->getAlignment(); CreateMemCpy(elemPtr, align, elemValue, align, size); } } else if (elemType->IsVArrayType()) { auto layout = GetLLVMModule()->getDataLayout(); auto size = getInt64(layout.getTypeAllocSize(elemType->GetLLVMType())); if (IsTypeContainsRef(elemType->GetLLVMType())) { CallGCWriteAgg({arrayV, elemPtr, elemValue, size}); } else { auto align = llvm::Align(layout.getABITypeAlignment(elemType->GetLLVMType())); CreateMemCpy(elemPtr, align, elemValue, align, size); } } else if (elemType->IsRefType()) { CallGCWrite({elemValue, arrayV, elemPtr}); } else { CreateStore(elemValue, elemPtr); } } return arrayV; } llvm::Value* IRBuilder2::CreateStringLiteral(const std::string& str) { std::string cjStringName = GetCjStringLiteralName(str); auto cachedCjString = cgMod.GetLLVMModule()->getNamedValue(cjStringName); if (cachedCjString) { return cachedCjString; } // Generate ArrayLayout.UInt8 here to ensure it is inserted to func for_keeping_some_types$ (void)CGArrayType::GenerateArrayLayoutTypeInfo(cgMod.GetCGContext(), ARRAY_LAYOUT_PREFIX + "UInt8", getInt8Ty()); // in Cangjie // struct String { // private let myData: RawArray // private let start: UInt32 = 0 // private let length: UInt32 = 0 // } // in LLVM IR: // cj string = type { i8 addrspace(1)*, i32, i32 } auto cjStringType = cgMod.GetCGContext().GetCjStringType(); auto gvCjString = llvm::cast(cgMod.GetLLVMModule()->getOrInsertGlobal(cjStringName, cjStringType)); CJC_NULLPTR_CHECK(gvCjString); gvCjString->setConstant(true); gvCjString->addAttribute(CJSTRING_LITERAL_ATTR); gvCjString->setLinkage(llvm::GlobalValue::PrivateLinkage); GetCGContext().AddCJString(gvCjString->getName().str(), str); return gvCjString; } namespace { struct ByteArrayInfo { llvm::Value* arrPtr{nullptr}; llvm::Type* arrType{nullptr}; llvm::Value* dataSize{nullptr}; llvm::Value* index{nullptr}; }; void CreateArrayMemSet(IRBuilder2& irBuilder, const ByteArrayInfo& arrInfo, llvm::Value* value) { auto& layout = irBuilder.GetCGModule().GetLLVMModule()->getDataLayout(); bool isInputValid = layout.getTypeAllocSize(value->getType()) == 1; CJC_ASSERT(isInputValid && "The type size of the value used to initialize the byte-array should be one byte."); // There is no gc points between allocation and initialization of `arrPtr`, so // `memset` is feasible to be used. // The type of `value` could be i1 or i8. value = value->getType()->isIntegerTy(8) ? value : irBuilder.CreateZExt(value, irBuilder.getInt8Ty()); auto dataPtr = irBuilder.CreateGEP( arrInfo.arrType, arrInfo.arrPtr, {irBuilder.getInt32(0), irBuilder.getInt32(1), arrInfo.index}); irBuilder.CreateMemSet(dataPtr, value, arrInfo.dataSize, llvm::MaybeAlign(1)); } /** * Initialize an array with `memset`. * Since the restriction of `memset`, we have to split the array as small chunks if the array size >= 2GB, * and initialize each chunk. * -------------- Pseudocode in Cangjie ------------- * var iterator = 0 * while(iterator + 2GB) < arrayLen) { * memset(arrayPtr, // address of the array 2GB, // size to memset iterator, // start index to memset of the array value) // value to fill into the array * iterator += 2GB * } * memset(arrayPtr, arrayLen - iterator, iterator, value) */ void InitByteArrayData( IRBuilder2& irBuilder, llvm::Value* arrPtr, llvm::Value* arrayLen, llvm::Value* value, llvm::Type* arrayType) { auto arrayPtr = irBuilder.CreateBitCast(irBuilder.GetPayloadFromObject(arrPtr), arrayType->getPointerTo(1u)); auto valType = value->getType(); value = valType->isPointerTy() ? irBuilder.CreateLoad(irBuilder.getInt8Ty(), irBuilder.CreateBitCast(value, irBuilder.getInt8PtrTy(valType->getPointerAddressSpace()))) : value; // In order to avoid long IR, If array size less than or equal to INT32_MAX, quit early. if (auto constVal = llvm::dyn_cast(arrayLen); constVal && *constVal->getValue().getRawData() <= INT32_MAX) { CreateArrayMemSet(irBuilder, {arrayPtr, arrayType, arrayLen, irBuilder.getInt64(0)}, value); return; } auto [whileLoopHeader, whileLoopBody, afterWhileLoop] = Vec2Tuple<3>(irBuilder.CreateAndInsertBasicBlocks( {GenNameForBB("byte.arr.init.start"), GenNameForBB("byte.arr.init.body"), GenNameForBB("byte.arr.init.end")})); const auto SIZE_OF_2G = irBuilder.getInt64(INT32_MAX); auto i64Ty = irBuilder.getInt64Ty(); auto iterator = irBuilder.CreateEntryAlloca(i64Ty, nullptr, "iterator"); (void)irBuilder.CreateStore(irBuilder.getInt64(0), iterator); (void)irBuilder.CreateBr(whileLoopHeader); // generate whileLoopHeader // `while(iterator + 2G) < arrayLen) {` irBuilder.SetInsertPoint(whileLoopHeader); auto cmpResult = irBuilder.CreateICmpSLT( irBuilder.CreateAdd(irBuilder.CreateLoad(i64Ty, iterator), SIZE_OF_2G), arrayLen, "max.size"); (void)irBuilder.CreateCondBr(cmpResult, whileLoopBody, afterWhileLoop); // generate whileLoopBody // `memset(arrayPtr, 2G, iterator, value)` irBuilder.SetInsertPoint(whileLoopBody); auto iteratorIndex = irBuilder.CreateLoad(i64Ty, iterator); CreateArrayMemSet(irBuilder, {arrayPtr, arrayType, SIZE_OF_2G, iteratorIndex}, value); // `iterator += 2G` auto newIterator = irBuilder.CreateAdd(iteratorIndex, SIZE_OF_2G); (void)irBuilder.CreateStore(newIterator, iterator); (void)irBuilder.CreateBr(whileLoopHeader); // generate afterWhileLoop // `memset(arrayPtr, arrayLen - iterator, iterator, value)` irBuilder.SetInsertPoint(afterWhileLoop); auto index = irBuilder.CreateLoad(i64Ty, iterator); auto residualSize = irBuilder.CreateSub(arrayLen, index); CreateArrayMemSet(irBuilder, {arrayPtr, arrayType, residualSize, index}, value); } void InitArrayDataUG(IRBuilder2& irBuilder, llvm::Value* arrPtr, llvm::Value* arrayLen, llvm::Value* value, llvm::Type* arrayType, const CHIR::RawArrayType& arrTy) { // Prepare basic blocks. auto [startBB, bodyBB, endBB] = Vec2Tuple<3>(irBuilder.CreateAndInsertBasicBlocks( {GenNameForBB("arr.init.start"), GenNameForBB("arr.init.body"), GenNameForBB("arr.init.end")})); auto isNotZeroLen = irBuilder.CreateIntrinsic(llvm::Intrinsic::expect, {irBuilder.getInt1Ty()}, {irBuilder.CreateICmpNE(arrayLen, irBuilder.getInt64(0)), irBuilder.getTrue()}); (void)irBuilder.CreateCondBr(isNotZeroLen, startBB, endBB); irBuilder.SetInsertPoint(startBB); auto i64Ty = irBuilder.getInt64Ty(); // Prepare iterator to assign the array element. auto iterator = irBuilder.CreateEntryAlloca(i64Ty, nullptr, "iterator"); (void)irBuilder.CreateStore(irBuilder.getInt64(0), iterator, true); (void)irBuilder.CreateBr(bodyBB); // Generate body. irBuilder.SetInsertPoint(bodyBB); auto index = irBuilder.CreateLoad(i64Ty, iterator); llvm::Value* offset = irBuilder.CallIntrinsicGetFieldOffset({irBuilder.CreateTypeInfo(arrTy), index, /* pre-offset: arrLength */ irBuilder.getInt32(8U)}); offset = irBuilder.CreateAdd(offset, llvm::ConstantInt::get(offset->getType(), irBuilder.GetPayloadOffset())); auto fieldType = arrTy.GetElementType(); auto fieldAddr = irBuilder.CreateInBoundsGEP(irBuilder.getInt8Ty(), arrPtr, offset); auto fieldAddrCGType = CGType::GetOrCreate( irBuilder.GetCGModule(), CGType::GetRefTypeOf(irBuilder.GetCGContext().GetCHIRBuilder(), *fieldType)); auto fieldCGType = CGType::GetOrCreate(irBuilder.GetCGModule(), fieldType); if (!fieldType->IsGeneric()) { fieldAddr = irBuilder.CreateBitCast(fieldAddr, fieldCGType->GetLLVMType()->getPointerTo(1U)); } irBuilder.GetCGContext().SetBasePtr(fieldAddr, arrPtr); irBuilder.CreateStore(CGValue(value, fieldCGType), CGValue(fieldAddr, fieldAddrCGType)); // Generate increment and compare size. auto incremented = irBuilder.CreateAdd(index, irBuilder.getInt64(1)); (void)irBuilder.CreateStore(incremented, iterator); auto cmpResult = irBuilder.CreateICmpSLT(incremented, arrayLen, "iterator.size.cmp"); (void)irBuilder.CreateCondBr(cmpResult, bodyBB, endBB); // Change to end BB. irBuilder.SetInsertPoint(endBB); } void InitArrayData( IRBuilder2& irBuilder, llvm::Value* arrPtr, llvm::Value* arrayLen, llvm::Value* value, llvm::Type* arrayType) { // Prepare basic blocks. auto [startBB, bodyBB, endBB] = Vec2Tuple<3>(irBuilder.CreateAndInsertBasicBlocks( {GenNameForBB("arr.init.start"), GenNameForBB("arr.init.body"), GenNameForBB("arr.init.end")})); // Prepare iterator to assign the array element. (void)irBuilder.CreateBr(startBB); irBuilder.SetInsertPoint(startBB); auto i64Ty = irBuilder.getInt64Ty(); auto iterator = irBuilder.CreateEntryAlloca(i64Ty, nullptr, "iterator"); (void)irBuilder.CreateStore(irBuilder.getInt64(0), iterator); (void)irBuilder.CreateBr(bodyBB); // Generate body. irBuilder.SetInsertPoint(bodyBB); auto index = irBuilder.CreateLoad(i64Ty, iterator); auto arrHead = irBuilder.CreateBitCast(irBuilder.GetPayloadFromObject(arrPtr), arrayType->getPointerTo(1u)); auto dataPtr = irBuilder.CreateGEP(arrayType, arrHead, {irBuilder.getInt32(0), irBuilder.getInt32(1), irBuilder.getInt32(0)}); auto elemPtr = irBuilder.CreateGEP(arrayType->getStructElementType(1)->getArrayElementType(), dataPtr, index); auto elemType = arrayType->getStructElementType(1)->getArrayElementType(); if (elemType->isStructTy()) { if (IsTypeContainsRef(elemType)) { auto structType = llvm::cast(elemType); auto layOut = irBuilder.GetLLVMModule()->getDataLayout().getStructLayout(structType); auto size = irBuilder.getInt64(layOut->getSizeInBytes()); irBuilder.CallGCWriteAgg({arrPtr, elemPtr, value, size}); } else { auto layOut = irBuilder.GetCGModule().GetLLVMModule()->getDataLayout().getStructLayout( llvm::cast(elemType)); CJC_NULLPTR_CHECK(layOut); auto size = layOut->getSizeInBytes(); auto align = layOut->getAlignment(); irBuilder.CreateMemCpy(elemPtr, align, value, align, size); } } else if (elemType->isArrayTy()) { auto size = irBuilder.getInt64(irBuilder.GetLLVMModule()->getDataLayout().getTypeAllocSize(elemType)); irBuilder.CallGCWriteAgg({arrPtr, elemPtr, value, size}); } else if (elemType == CGType::GetRefType(irBuilder.GetLLVMContext())) { irBuilder.CallGCWrite({value, arrPtr, elemPtr}); } else { irBuilder.CreateStore(value, elemPtr); } // Generate increment and compare size. auto incremented = irBuilder.CreateAdd(index, irBuilder.getInt64(1)); (void)irBuilder.CreateStore(incremented, iterator); auto cmpResult = irBuilder.CreateICmpSLT(incremented, arrayLen, "iterator.size.cmp"); (void)irBuilder.CreateCondBr(cmpResult, bodyBB, endBB); // Change to end BB. irBuilder.SetInsertPoint(endBB); } } // namespace void IRBuilder2::CallArrayInit( llvm::Value* arrPtr, llvm::Value* arrayLen, llvm::Value* elemValue, const CHIR::RawArrayType& arrTy) { // The memory of the array has been cleared when the memory of the array is // allocated at runtime. Therefore, when all elements of the array are null // values, unnecessary zero initialization instructions do not need to be // inserted. if (auto constVal = llvm::dyn_cast(elemValue); constVal && constVal->isNullValue()) { // keep ArrayLayout type. return; } auto arrayType = CGArrayType::GenerateArrayLayoutType(cgMod, arrTy); if (auto elemCGType = CGType::GetOrCreate(cgMod, arrTy.GetElementType()); !elemCGType->GetSize()) { InitArrayDataUG(*this, arrPtr, arrayLen, elemValue, arrayType, arrTy); return; } auto elemType = elemValue->getType(); auto [nonOptBB, optBB] = Vec2Tuple<2>(CreateAndInsertBasicBlocks({GenNameForBB("arr.init.non-opt"), GenNameForBB("arr.init.opt")})); // If the value equals null-value, no need to assign that null-value to the // elements because the memory of the array is cleared while runtime allocates // the array. Otherwise, set the value of each element in the array by looping. auto isValNull = IsFPType(elemType) ? CreateFCmpOEQ(elemValue, llvm::ConstantFP::get(elemType, 0), "val.is-zero") : CreateIsNull(elemValue, "val.is-null"); auto isZeroSize = CreateICmpEQ(arrayLen, getInt64(0), "size.is-zero"); // If array is zero size or array value equals null-value, quit initialization step. (void)CreateCondBr(CreateOr(isZeroSize, isValNull), optBB, nonOptBB); // generate nonOptBB SetInsertPoint(nonOptBB); size_t typeSize = GetCGModule().GetTypeSize(elemType); CJC_ASSERT(typeSize > 0 && "Zero size type is not expected to be array element type."); if (typeSize == 1) { // There are two kinds of `value`: // 1) Pointer to a struct of which type is one byte; // 2) The amount that alloca reserves for it is one byte. InitByteArrayData(*this, arrPtr, arrayLen, elemValue, arrayType); (void)CreateBr(optBB); // generate optBB SetInsertPoint(optBB); return; } InitArrayData(*this, arrPtr, arrayLen, elemValue, arrayType); (void)CreateBr(optBB); // generate optBB SetInsertPoint(optBB); } llvm::Value* IRBuilder2::CreateEnumGEP(const CHIR::Field& field) { CJC_ASSERT(field.GetPath().size() == 1); auto index = field.GetPath()[0]; if (index == 0) { return GetEnumTag(field); } else { auto res = GetEnumAssociatedValue(field); if (auto cgType = dynamic_cast(CGType::GetOrCreate(cgMod, field.GetResult()->GetType())); cgType && cgType->IsOptionLikeRef()) { llvm::cast(res)->setMetadata( "untrusted_ref", llvm::MDNode::get(cgMod.GetLLVMContext(), {})); } return res; } } llvm::Value* IRBuilder2::GetEnumTag(const CHIR::Field& field) { auto cgEnum = GetCGModule().GetMappedValue(field.GetBase()); CJC_NULLPTR_CHECK(cgEnum); CJC_ASSERT(field.GetBase()->GetType()->IsEnum()); auto enumVal = cgEnum->GetRawValue(); auto chirType = StaticCast(field.GetBase()->GetType()); auto cgEnumType = StaticCast(CGType::GetOrCreate(cgMod, chirType)); switch (cgEnumType->GetCGEnumTypeKind()) { case CGEnumType::CGEnumTypeKind::NON_EXHAUSTIVE_UNASSOCIATED: case CGEnumType::CGEnumTypeKind::EXHAUSTIVE_UNASSOCIATED: { return enumVal; } case CGEnumType::CGEnumTypeKind::NON_EXHAUSTIVE_ASSOCIATED: case CGEnumType::CGEnumTypeKind::EXHAUSTIVE_OTHER: { auto payload = GetPayloadFromObject(enumVal); auto tagType = CGType::GetOrCreate(cgMod, field.GetResult()->GetType())->GetLLVMType(); return CreateLoad(tagType, CreateBitCast(payload, tagType->getPointerTo(1U))); } case CGEnumType::CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_NONREF: { auto tagType = CGType::GetOrCreate(cgMod, field.GetResult()->GetType())->GetLLVMType(); return CreateLoad(tagType, CreateBitCast(enumVal, tagType->getPointerTo())); } case CGEnumType::CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_T: { auto i1Ty = getInt1Ty(); auto payload = GetPayloadFromObject(enumVal); auto [refBB, nonRefBB, endBB] = Vec2Tuple<3>(CreateAndInsertBasicBlocks({"ref", "nonRef", "end"})); auto isRef = CreateTypeInfoIsReferenceCall(*chirType->GetTypeArgs()[0]); CreateCondBr(isRef, refBB, nonRefBB); SetInsertPoint(refBB); auto refResult = cgEnumType->IsAntiOptionLike() ? CallIntrinsicIsNonNull(CallGCRead({enumVal, payload})) : CallIntrinsicIsNull(CallGCRead({enumVal, payload})); CreateBr(endBB); SetInsertPoint(nonRefBB); auto nonRefResult = LLVMIRBuilder2::CreateLoad(i1Ty, CreateBitCast(payload, i1Ty->getPointerTo(1U))); CreateBr(endBB); SetInsertPoint(endBB); auto phi = CreatePHI(i1Ty, 2U); // 2U: ref and nonRef branch phi->addIncoming(refResult, refBB); phi->addIncoming(nonRefResult, nonRefBB); return phi; } case CGEnumType::CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_REF: { return cgEnumType->IsAntiOptionLike() ? CallIntrinsicIsNonNull(cgEnum->GetRawValue()) : CallIntrinsicIsNull(cgEnum->GetRawValue()); } case CGEnumType::CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_NONREF: { if (cgEnumType->GetSize()) { auto enumType = cgEnumType->GetLLVMType(); auto casted = CreateBitCast(enumVal, enumType->getPointerTo(enumVal->getType()->getPointerAddressSpace())); return LLVMIRBuilder2::CreateLoad(getInt1Ty(), CreateStructGEP(enumType, casted, 0U)); } auto casted = CreateBitCast(GetPayloadFromObject(enumVal), llvm::Type::getInt1PtrTy(cgMod.GetLLVMContext(), 1U)); return LLVMIRBuilder2::CreateLoad(getInt1Ty(), casted); } case CGEnumType::CGEnumTypeKind::EXHAUSTIVE_ZERO_SIZE: { return llvm::ConstantInt::get(getInt32Ty(), 0U); } default: CJC_ASSERT(false && "Should not reach here: UNKNOWN enum kind"); return nullptr; } } namespace { llvm::Value* GetOptionLikeNonRefAssociatedValue(IRBuilder2& irBuilder, const CHIR::Type& enumType, const CHIR::Type& associatedValueType, llvm::Value* enumValueWithTI) { auto associatedValueTypeTI = irBuilder.CreateTypeInfo(associatedValueType); auto sizeOfAssociatedValueType = irBuilder.GetLayoutSize_32(associatedValueType); auto nonRefResult = irBuilder.CallIntrinsicAllocaGeneric({associatedValueTypeTI, sizeOfAssociatedValueType}); auto ti = irBuilder.CreateTypeInfo(enumType); // `1` means the second field, `8` means skipping TypeInfo* additionally. auto associatedValOffset = irBuilder.CallIntrinsicGetFieldOffset({ti, irBuilder.getInt64(1U), irBuilder.getInt32(8U)}); auto associatedValPtr = irBuilder.CreateInBoundsGEP(irBuilder.getInt8Ty(), enumValueWithTI, associatedValOffset); irBuilder.CallGCReadGeneric({nonRefResult, enumValueWithTI, associatedValPtr, sizeOfAssociatedValueType}); return nonRefResult; } llvm::Value* GetCommonEnumAssociatedValue(IRBuilder2& irBuilder, const CHIR::Field& field, const CGValue& cgEnum) { auto& cgMod = irBuilder.GetCGModule(); auto& cgCtx = irBuilder.GetCGContext(); std::vector elemTypes; bool isSized = true; auto args = field.GetBase()->GetType()->GetTypeArgs(); for (size_t idx = 0; idx < args.size(); ++idx) { auto cgType = CGType::GetOrCreate(cgMod, args[idx]); if (cgType->GetSize()) { (void)elemTypes.emplace_back(cgType->GetLLVMType()); } else { isSized = false; break; } } auto enumVal = cgEnum.GetRawValue(); auto associatedValIdx = static_cast(field.GetPath()[0]); if (isSized) { auto payload = irBuilder.GetPayloadFromObject(enumVal); auto layoutType = llvm::StructType::get(cgCtx.GetLLVMContext(), elemTypes); auto castedEnumValPtr = irBuilder.CreateBitCast(payload, layoutType->getPointerTo(1)); auto fieldPtr = irBuilder.CreateStructGEP(layoutType, castedEnumValPtr, associatedValIdx); cgCtx.SetBasePtr(fieldPtr, enumVal); return irBuilder.CreateLoad(layoutType->getStructElementType(associatedValIdx), fieldPtr); } else { auto typeCastExpr = StaticCast(StaticCast(field.GetBase())->GetExpr()); const auto& chirEnumType = *StaticCast(typeCastExpr->GetSourceTy()); CJC_ASSERT(typeCastExpr->GetResult()->Get().has_value()); std::vector typeArgs; for (auto typeArg : GetGenericArgsFromCHIRType(*chirEnumType.GetEnumDef()->GetType())) { auto arg = GetTypeInnerType(*typeCastExpr->GetSourceValue()->GetType(), typeArg); (void)typeArgs.emplace_back(arg); } auto fieldType = field.GetResult()->GetType(); auto fieldAddrType = CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(cgCtx.GetCHIRBuilder(), *fieldType)); CJC_ASSERT(typeCastExpr->GetResult()->Get().has_value()); auto enumCaseTT = cgMod.GetOrCreateEnumCtorTIOrTT( chirEnumType, typeCastExpr->GetResult()->Get().value()); auto enumCaseTi = irBuilder.CallIntrinsicGetTypeInfo( {enumCaseTT, irBuilder.getInt32(typeArgs.size()), irBuilder.CreateTypeInfoArray(typeArgs)}); llvm::Value* fieldOffset = irBuilder.CallIntrinsicGetFieldOffset({enumCaseTi, irBuilder.getInt64(associatedValIdx)}); fieldOffset = irBuilder.CreateAdd(fieldOffset, llvm::ConstantInt::get(fieldOffset->getType(), irBuilder.GetPayloadOffset())); auto fieldAddr = irBuilder.CreateInBoundsGEP(irBuilder.getInt8Ty(), enumVal, fieldOffset); if (!fieldType->IsGeneric()) { auto fieldCGType = CGType::GetOrCreate(cgMod, fieldType); fieldAddr = irBuilder.CreateBitCast( fieldAddr, fieldCGType->GetLLVMType()->getPointerTo(fieldAddr->getType()->getPointerAddressSpace())); } cgCtx.SetBasePtr(fieldAddr, enumVal); auto cgAddr = CGValue(fieldAddr, fieldAddrType); return irBuilder.CreateLoad(cgAddr); } } llvm::Value* GetZeroSizeEnumAssociatedValue(IRBuilder2& irBuilder, const CHIR::Field& field, const CGValue& cgEnum) { auto& cgMod = irBuilder.GetCGModule(); auto& cgCtx = irBuilder.GetCGContext(); auto fieldType = field.GetResult()->GetType(); auto fieldAddrType = CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(cgCtx.GetCHIRBuilder(), *fieldType)); auto cgAddr = CGValue(cgEnum.GetRawValue(), fieldAddrType); return irBuilder.CreateLoad(cgAddr); } llvm::Value* GetAssociatedNonRefEnumAssociatedValue( IRBuilder2& irBuilder, const CHIR::Field& field, const CGValue& cgEnum) { auto& cgMod = irBuilder.GetCGModule(); auto i8Ty = irBuilder.getInt8Ty(); auto associatedValIdx = static_cast(field.GetPath()[0]); // 1. get the memory layout of the Enum's constructor. auto offset = 0U; auto args = field.GetBase()->GetType()->GetTypeArgs(); CJC_ASSERT(associatedValIdx < args.size()); for (size_t idx = 0U; idx < associatedValIdx; ++idx) { offset += CGType::GetOrCreate(cgMod, args[idx])->GetSize().value(); } // 2. get the associated values. auto casted = irBuilder.CreateBitCast(cgEnum.GetRawValue(), i8Ty->getPointerTo()); auto fieldPtr = irBuilder.CreateConstGEP1_32(i8Ty, casted, offset, "enum.field.ptr"); auto retLLVMType = CGType::GetOrCreate(cgMod, field.GetResult()->GetType())->GetLLVMType(); return irBuilder.CreateLoad(retLLVMType, irBuilder.CreateBitCast(fieldPtr, retLLVMType->getPointerTo())); } } // namespace llvm::Value* IRBuilder2::GetEnumAssociatedValue(const CHIR::Field& field) { CJC_ASSERT(field.GetBase()->IsLocalVar()); CJC_ASSERT(StaticCast(field.GetBase())->GetExpr()->GetExprKind() == CHIR::ExprKind::TYPECAST); auto typeCast = StaticCast(StaticCast(field.GetBase())->GetExpr()); auto cgEnumType = static_cast(CGType::GetOrCreate(cgMod, typeCast->GetSourceValue()->GetType())); auto cgEnum = GetCGModule().GetMappedValue(field.GetBase()); CJC_NULLPTR_CHECK(cgEnum); CJC_ASSERT(field.GetPath().size() == 1); auto index = static_cast(field.GetPath()[0]); switch (cgEnumType->GetCGEnumTypeKind()) { case CGEnumType::CGEnumTypeKind::NON_EXHAUSTIVE_ASSOCIATED: case CGEnumType::CGEnumTypeKind::EXHAUSTIVE_OTHER: { return GetCommonEnumAssociatedValue(*this, field, *cgEnum); } case CGEnumType::CGEnumTypeKind::EXHAUSTIVE_ZERO_SIZE: { return GetZeroSizeEnumAssociatedValue(*this, field, *cgEnum); } case CGEnumType::CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_NONREF: { return GetAssociatedNonRefEnumAssociatedValue(*this, field, *cgEnum); } case CGEnumType::CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_T: { CJC_ASSERT(index == 1); auto [refBB, nonRefBB, endBB] = Vec2Tuple<3>(CreateAndInsertBasicBlocks({"ref", "nonRef", "end"})); auto isRef = CreateTypeInfoIsReferenceCall(*cgEnumType->GetOriginal().GetTypeArgs()[0]); CreateCondBr(isRef, refBB, nonRefBB); SetInsertPoint(refBB); auto enumVal = cgEnum->GetRawValue(); auto payload = CreateBitCast(GetPayloadFromObject(enumVal), getInt8PtrTy(1U)); auto refResult = CallGCRead({enumVal, payload}); CreateBr(endBB); SetInsertPoint(nonRefBB); auto associatedValueType = field.GetResult()->GetType(); auto nonRefResult = GetOptionLikeNonRefAssociatedValue( *this, cgEnum->GetCGType()->GetOriginal(), *associatedValueType, enumVal); CreateBr(endBB); SetInsertPoint(endBB); auto phi = CreatePHI(getInt8PtrTy(1U), 2U); // 2U: ref and nonRef branch phi->addIncoming(refResult, refBB); phi->addIncoming(nonRefResult, nonRefBB); return phi; } case CGEnumType::CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_REF: { CJC_ASSERT(index == 1); auto value = cgEnum->GetRawValue(); cgMod.GetCGContext().AddNullableReference(value); return value; } case CGEnumType::CGEnumTypeKind::EXHAUSTIVE_ASSOCIATED_OPTION_LIKE_NONREF: { CJC_ASSERT(index == 1); if (cgEnumType->GetSize()) { auto enumVal = cgEnum->GetRawValue(); auto enumType = cgEnumType->GetLLVMType(); auto casted = CreateBitCast( cgEnum->GetRawValue(), enumType->getPointerTo(enumVal->getType()->getPointerAddressSpace())); return CreateLoad(enumType->getStructElementType(1U), CreateStructGEP(enumType, casted, 1U)); } auto associatedValueType = field.GetResult()->GetType(); auto nonRefResult = GetOptionLikeNonRefAssociatedValue( *this, cgEnum->GetCGType()->GetOriginal(), *associatedValueType, cgEnum->GetRawValue()); return nonRefResult; } case CGEnumType::CGEnumTypeKind::NON_EXHAUSTIVE_UNASSOCIATED: case CGEnumType::CGEnumTypeKind::EXHAUSTIVE_UNASSOCIATED: default: CJC_ASSERT(false && "Should not reach here: UNKNOWN enum kind"); return nullptr; } } llvm::Value* IRBuilder2::GetSizeFromTypeInfo(llvm::Value* typeInfo) { auto block = GetInsertBlock(); auto& cacheMap = GetCGContext().genericParamsSizeBlockLevelCacheMap; if (auto blockIt = cacheMap.find(block); blockIt != cacheMap.end()) { auto blockCacheMap = blockIt->second; if (auto it = blockCacheMap.find(typeInfo); it != blockCacheMap.end()) { return it->second; } } auto& llvmCtx = cgMod.GetLLVMContext(); auto typeInfoTy = CGType::GetOrCreateTypeInfoType(llvmCtx); auto typeInfoPtrTy = typeInfoTy->getPointerTo(); auto ptr = CreateStructGEP(typeInfoTy, LLVMIRBuilder2::CreateBitCast(typeInfo, typeInfoPtrTy), static_cast(TYPEINFO_SIZE), "ti.size"); auto size = LLVMIRBuilder2::CreateLoad(llvm::Type::getInt32Ty(llvmCtx), ptr); size->setMetadata(llvm::LLVMContext::MD_invariant_load, llvm::MDNode::get(llvmCtx, {})); (void)cacheMap[block].emplace(typeInfo, size); return size; } llvm::Value* IRBuilder2::GetLayoutSize_32(const CHIR::Type& type) { auto baseType = DeRef(type); if (auto gt = DynamicCast(baseType); gt && gt->orphanFlag) { CJC_ASSERT(gt->GetUpperBounds().size() == 1U); baseType = gt->GetUpperBounds()[0]; } auto cgType = CGType::GetOrCreate(cgMod, baseType); if (auto s = cgType->GetSize()) { return llvm::ConstantInt::get(getInt32Ty(), s.value()); } auto ti = CreateTypeInfo(*baseType); return GetSizeFromTypeInfo(ti); } llvm::Value* IRBuilder2::GetLayoutSize_64(const CHIR::Type& type) { auto baseType = DeRef(type); if (auto gt = DynamicCast(baseType); gt && gt->orphanFlag) { CJC_ASSERT(gt->GetUpperBounds().size() == 1U); baseType = gt->GetUpperBounds()[0]; } auto cgType = CGType::GetOrCreate(cgMod, baseType); if (auto s = cgType->GetSize()) { return llvm::ConstantInt::get(getInt64Ty(), s.value()); } auto ti = CreateTypeInfo(*baseType); return CreateSExt(GetSizeFromTypeInfo(ti), getInt64Ty()); } llvm::Value* IRBuilder2::GetSize_32(const CHIR::Type& type) { auto baseType = DeRef(type); if (auto gt = DynamicCast(baseType); gt && gt->orphanFlag) { CJC_ASSERT(gt->GetUpperBounds().size() == 1U); baseType = gt->GetUpperBounds()[0]; } if (baseType->IsGeneric()) { auto [refSizeBB, nonRefSizeBB, exitBB] = Vec2Tuple<3>(CreateAndInsertBasicBlocks( {GenNameForBB("get_ref_size"), GenNameForBB("get_non_ref_size"), GenNameForBB("get_size_exit")})); CreateCondBr(CreateTypeInfoIsReferenceCall(type), refSizeBB, nonRefSizeBB); SetInsertPoint(refSizeBB); CreateBr(exitBB); SetInsertPoint(nonRefSizeBB); auto nonRefSize = GetLayoutSize_32(type); CreateBr(exitBB); SetInsertPoint(exitBB); auto sizePHI = CreatePHI(getInt32Ty(), 2U); sizePHI->addIncoming(getInt32(GetPtrSize()), refSizeBB); sizePHI->addIncoming(nonRefSize, nonRefSizeBB); return sizePHI; } else if (auto cgType = CGType::GetOrCreate(cgMod, &type); cgType->IsReference()) { return getInt32(GetPayloadOffset()); } else { return GetLayoutSize_32(type); } } llvm::Value* IRBuilder2::GetSize_64(const CHIR::Type& type) { return CreateSExt(GetSize_32(type), getInt64Ty()); } llvm::Value* IRBuilder2::GetSize_Native(const CHIR::Type& type) { return GetCGContext().GetCompileOptions().target.arch == Triple::ArchType::ARM32 ? GetSize_32(type) : GetSize_64(type); } llvm::Value* IRBuilder2::GetAlign(const CHIR::Type& type, llvm::Type* targetType) { auto ti = CreateTypeInfo(type); auto [refAlignBB, nonRefAlignBB, exitBB] = Vec2Tuple<3>(CreateAndInsertBasicBlocks( {GenNameForBB("get_ref_align"), GenNameForBB("get_non_ref_align"), GenNameForBB("get_align_exit")})); CreateCondBr(CreateTypeInfoIsReferenceCall(type), refAlignBB, nonRefAlignBB); SetInsertPoint(refAlignBB); CreateBr(exitBB); SetInsertPoint(nonRefAlignBB); auto& llvmCtx = cgMod.GetLLVMContext(); auto typeInfoTy = CGType::GetOrCreateTypeInfoType(llvmCtx); auto ptr = CreateStructGEP(typeInfoTy, LLVMIRBuilder2::CreateBitCast(ti, typeInfoTy->getPointerTo()), static_cast(TYPEINFO_ALIGN), "ti.align"); auto loadInst = LLVMIRBuilder2::CreateLoad(llvm::Type::getInt8Ty(llvmCtx), ptr); loadInst->setMetadata(llvm::LLVMContext::MD_invariant_load, llvm::MDNode::get(llvmCtx, {})); auto nonRefAlign = CreateSExt(loadInst, targetType); CreateBr(exitBB); SetInsertPoint(exitBB); auto sizePHI = CreatePHI(targetType, 2U); sizePHI->addIncoming(llvm::ConstantInt::get(targetType, GetPayloadOffset()), refAlignBB); sizePHI->addIncoming(nonRefAlign, nonRefAlignBB); return sizePHI; } llvm::Value* IRBuilder2::GetTypeKindFromTypeInfo(llvm::Value* typeInfo) { auto& llvmCtx = cgMod.GetLLVMContext(); auto typeInfoTy = CGType::GetOrCreateTypeInfoType(llvmCtx); auto typeInfoPtrTy = typeInfoTy->getPointerTo(); auto ptr = CreateStructGEP( typeInfoTy, LLVMIRBuilder2::CreateBitCast(typeInfo, typeInfoPtrTy), static_cast(TYPEINFO_TYPE_KIND)); auto inst = LLVMIRBuilder2::CreateLoad(llvm::Type::getInt8Ty(llvmCtx), ptr, "ti.kind"); inst->setMetadata(llvm::LLVMContext::MD_invariant_load, llvm::MDNode::get(llvmCtx, {})); return inst; } llvm::Value* IRBuilder2::GetTypeForTypeParameter(llvm::Value* typeInfo) { auto& llvmCtx = cgMod.GetLLVMContext(); auto typeInfoTy = CGType::GetOrCreateTypeInfoType(llvmCtx); auto typeInfoPtrTy = typeInfoTy->getPointerTo(); auto ptr = CreateStructGEP( typeInfoTy, LLVMIRBuilder2::CreateBitCast(typeInfo, typeInfoPtrTy), static_cast(TYPEINFO_NAME)); auto inst = LLVMIRBuilder2::CreateLoad(llvm::Type::getInt8PtrTy(llvmCtx), ptr, "ti.typeInfoName"); inst->setMetadata(llvm::LLVMContext::MD_invariant_load, llvm::MDNode::get(llvmCtx, {})); return inst; } llvm::Value* IRBuilder2::GetTypeArgsFromTypeInfo(llvm::Value* typeInfo) { auto& llvmCtx = cgMod.GetLLVMContext(); auto typeInfoTy = CGType::GetOrCreateTypeInfoType(llvmCtx); auto typeInfoPtrTy = typeInfoTy->getPointerTo(); auto ptr = CreateStructGEP(typeInfoTy, LLVMIRBuilder2::CreateBitCast(typeInfo, typeInfoPtrTy), static_cast(TYPEINFO_TYPE_ARGS), "ti.typeArgs"); auto inst = LLVMIRBuilder2::CreateLoad(getInt8PtrTy(), ptr); inst->setMetadata(llvm::LLVMContext::MD_invariant_load, llvm::MDNode::get(llvmCtx, {})); return LLVMIRBuilder2::CreateBitCast(inst, typeInfoPtrTy->getPointerTo()); } llvm::Value* IRBuilder2::GetTypeInfoFromTiArray(llvm::Value* tiArray, std::size_t index, const std::string& name) { auto tiPtrType = CGType::GetOrCreateTypeInfoPtrType(getContext()); auto tiPtrPtr = LLVMIRBuilder2::CreateConstInBoundsGEP1_32(tiPtrType, tiArray, static_cast(index)); auto inst = LLVMIRBuilder2::CreateLoad(tiPtrType, tiPtrPtr, name); inst->setMetadata(llvm::LLVMContext::MD_invariant_load, llvm::MDNode::get(getContext(), {})); return inst; } llvm::Value* IRBuilder2::GetTypeInfoFromObject(llvm::Value* obj) { auto tiPtrType = CGType::GetOrCreateTypeInfoPtrType(getContext()); auto inst = LLVMIRBuilder2::CreateLoad(tiPtrType, LLVMIRBuilder2::CreateBitCast(obj, tiPtrType->getPointerTo(1U)), "ti"); inst->setMetadata(llvm::LLVMContext::MD_invariant_load, llvm::MDNode::get(getContext(), {})); return inst; } llvm::Value* IRBuilder2::GetPayloadFromObject(llvm::Value* obj) { auto p0i8 = getInt8PtrTy(); auto payloadIdx = GetCGContext().GetCompileOptions().target.arch == Triple::ArchType::ARM32 ? 2U : 1U; auto payloadPtr = CreateConstGEP1_32(p0i8, LLVMIRBuilder2::CreateBitCast(obj, p0i8->getPointerTo(1U)), payloadIdx, "ti.payload"); GetCGContext().SetBasePtr(payloadPtr, obj); return payloadPtr; } llvm::Value* IRBuilder2::CreateTypeInfoIsReferenceCall(llvm::Value* ti) { auto func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_is_reference); return CreateCall(func, LLVMIRBuilder2::CreateBitCast(ti, func->getFunctionType()->getParamType(0))); } llvm::Value* IRBuilder2::CreateTypeInfoIsReferenceCall(const CHIR::Type& chirType) { auto baseType = DeRef(chirType); if (auto gt = DynamicCast(baseType); gt && gt->orphanFlag) { CJC_ASSERT(gt->GetUpperBounds().size() == 1U); baseType = gt->GetUpperBounds()[0]; } if (baseType->IsGeneric()) { auto baseTypeTI = CreateTypeInfo(*baseType); return CreateTypeInfoIsReferenceCall(baseTypeTI); } else { return getInt1(CGType::GetOrCreate(cgMod, baseType)->IsReference()); } } // Yield a specialized basic block as the entry or behind `allocas` to prepare // all TypeInfo in advance. For more details, see the header file. void IRBuilder2::SetInsertPointForPreparingTypeInfo() { auto curFunc = GetInsertFunction(); CJC_NULLPTR_CHECK(curFunc); auto insertBB = &curFunc->getEntryBlock(); if (auto& entryBB = curFunc->getEntryBlock(); entryBB.getName() == "allocas") { auto successor = entryBB.getUniqueSuccessor(); CJC_ASSERT(successor); if (successor->getName() == "prepTi") { insertBB = successor; } else { auto preTi = llvm::BasicBlock::Create(getContext(), "prepTi", curFunc, successor); CJC_NULLPTR_CHECK(insertBB->getTerminator()); insertBB->getTerminator()->setOperand(0, preTi); SetInsertPoint(preTi); CreateBr(successor); insertBB = preTi; } } else if (insertBB->getName() != "prepTi") { auto preTi = CreateEntryBasicBlock(curFunc, "prepTi"); SetInsertPoint(preTi); CreateBr(insertBB); insertBB = preTi; } SetInsertPoint(insertBB->getTerminator()); } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/CJNative/CJNativeIntrinsicsCall.cpp000066400000000000000000002075761510705540100255770ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "IRBuilder.h" #include "Base/CGTypes/CGArrayType.h" #include "Base/CGTypes/CGClassType.h" #include "Base/CHIRExprWrapper.h" #include "CGModule.h" #include "IRAttribute.h" #include "Utils/CGCommonDef.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Type/ClassDef.h" namespace Cangjie::CodeGen { llvm::Value* IRBuilder2::CallIntrinsic( const CHIRIntrinsicWrapper& intrinsic, const std::vector& parameters, [[maybe_unused]] bool isC) { if (auto it = INTRINSIC_KIND_TO_FUNCNAME_MAP.find(intrinsic.GetIntrinsicKind()); it != INTRINSIC_KIND_TO_FUNCNAME_MAP.end()) { auto funcInfo = it->second; auto retCGType = CGType::GetOrCreate(cgMod, intrinsic.GetResult()->GetType()); return CallIntrinsicFunction(retCGType->GetLLVMType(), funcInfo.funcName, parameters, funcInfo.attributes); } std::vector args; transform(parameters.begin(), parameters.end(), back_inserter(args), [](const CGValue* value) { return value->GetRawValue(); }); return CallIntrinsic(intrinsic, args); } namespace { bool IsPlatformDependent(CHIR::IntrinsicKind intrinsicKind) { return (intrinsicKind >= CHIR::IntrinsicKind::GET_MAX_HEAP_SIZE && intrinsicKind <= CHIR::IntrinsicKind::GET_NATIVE_THREAD_NUMBER) || intrinsicKind == CHIR::IntrinsicKind::GET_GC_COUNT || intrinsicKind==CHIR::IntrinsicKind::GET_GC_FREED_SIZE; } } llvm::Value* IRBuilder2::CallIntrinsic( const CHIRIntrinsicWrapper& intrinsic, const std::vector& args, const std::vector& tys) { if (auto iter = INTRINSIC_KIND_TO_ID_MAP.find(intrinsic.GetIntrinsicKind()); iter != INTRINSIC_KIND_TO_ID_MAP.end()) { std::vector newTys = {GetSizetLLVMType()}; auto func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), iter->second, IsPlatformDependent(intrinsic.GetIntrinsicKind()) ? newTys : tys); if (intrinsic.GetIntrinsicKind() == CHIR::IntrinsicKind::BLACK_BOX) { CJC_ASSERT(args.size() == 1U); if (!args[0]->getType()->isPointerTy()) { return args[0]; } llvm::PointerType *argType = llvm::cast(args[0]->getType()); if (argType->getAddressSpace() == 0) { auto arg = CreateBitCast(args[0], getInt8Ty()->getPointerTo(0)); return CreateBitCast(CreateCall(func, arg), argType); } else { auto arg = CreateAddrSpaceCast(args[0], getInt8Ty()->getPointerTo(0)); return CreateAddrSpaceCast(CreateCall(func, arg), argType); } } return CreateCall(func, args); } CJC_ASSERT(false && "Unsupported intrinsic."); return nullptr; } llvm::Function* IRBuilder2::GetExceptionIntrinsicThrow() const { // declare void @cj_throw_exception(i8 addrspace(1)* param) return llvm::Intrinsic::getDeclaration(GetLLVMModule(), llvm::Intrinsic::cj_throw_exception); } llvm::Value* IRBuilder2::CallSetLocation() { if (!GetCGContext().GetCompileOptions().enableCompileDebug) { return nullptr; } // Func: anyregcc void @llvm.cj.set.location() auto setLocation = llvm::Intrinsic::getDeclaration(GetLLVMModule(), llvm::Intrinsic::cj_set_location); auto callInst = CreateCall(setLocation); if (GetCGContext().GetCompileOptions().target.arch != Triple::ArchType::ARM32) { callInst->setCallingConv(llvm::CallingConv::AnyReg); } return callInst; } llvm::Value* IRBuilder2::CallExceptionIntrinsicGetExceptionValue() { // declare i8* cj_get_exception_wrapper() auto getExceptionValue = llvm::Intrinsic::getDeclaration(GetLLVMModule(), llvm::Intrinsic::cj_get_exception_wrapper); return CreateCall(getExceptionValue); } llvm::Value* IRBuilder2::CallPostThrowExceptionIntrinsic(llvm::Value* exceptionValue) { // declare i8 addrspace(1)* @llvm.cj.post.throw.exception(i8* param) auto beginCatch = llvm::Intrinsic::getDeclaration(GetLLVMModule(), llvm::Intrinsic::cj_post_throw_exception); return CreateCall(beginCatch, {exceptionValue}); } llvm::Value* IRBuilder2::CallStackTraceIntrinsic(const CHIRIntrinsicWrapper& syscall, std::vector& parameters) { auto intrinsicKind = syscall.GetIntrinsicKind(); if (intrinsicKind == CHIR::IntrinsicKind::FILL_IN_STACK_TRACE) { CJC_ASSERT(!parameters.empty() && "the arguments number of parameters is incorrect."); const auto& uint64ArrayCHIRType = *StaticCast( StaticCast(syscall.GetResult()->GetType())->GetBaseType()); std::vector funcArgs; transform(parameters.begin(), parameters.end(), back_inserter(funcArgs), [](const CGValue* cgValue) { return cgValue->GetRawValue(); }); funcArgs[0] = CreateBitCast(CreateTypeInfo(uint64ArrayCHIRType), getInt8PtrTy()); // declare i8 addrspace(1)* @cj_get_exception_wrapper(i8* rawArray, i8 addrspace(1)* rawArray) auto fillStackTrace = llvm::Intrinsic::getDeclaration(GetLLVMModule(), llvm::Intrinsic::cj_fill_in_stack_trace); return llvm::cast(CreateCall(fillStackTrace, funcArgs)); } else if (intrinsicKind == CHIR::IntrinsicKind::DECODE_STACK_TRACE) { auto& cgCtx = GetCGContext(); auto retCHIRType = syscall.GetResult()->GetType(); auto retCGType = CGType::GetOrCreate(cgMod, retCHIRType); // Get the def of struct-StackTraceData auto resultStructDef = StaticCast(retCHIRType)->GetStructDef(); // Get the CHIRType of uint8Array const auto& uint8ArrayCHIRType = *StaticCast( StaticCast(resultStructDef->GetInstanceVar(0).type)->GetBaseType()); // The last parameter, uint8ArrayClassInfo, is used to construct CJ Ojbect(RawArray) at runtime. // Since runtime know nothing about CJ Object, CodeGen need to pass the ClassInfo to runtime. auto tempVal = CGValue(CreateBitCast(CreateTypeInfo(uint8ArrayCHIRType), getInt8PtrTy()), CGType::GetOrCreate(cgMod, CGType::GetRefTypeOfCHIRInt8(cgCtx.GetCHIRBuilder()))); parameters.emplace_back(&tempVal); return CallIntrinsicFunction(retCGType->GetLLVMType(), "CJ_MCC_DecodeStackTrace", parameters, {}); } CJC_ASSERT(false && "Unsupported syscall."); return nullptr; } void IRBuilder2::CallIntrinsicIndexCheck(llvm::Value* len, llvm::Value* index) { auto zero = llvm::ConstantInt::get(llvm::Type::getInt64Ty(GetLLVMContext()), 0); // index is lower than zero auto cmpZero = CreateICmpSLT(index, zero); // greater than or equal to len auto cmpLen = CreateICmpSGE(index, len); auto isThrow = CreateOr(cmpZero, cmpLen); auto [thenBB, elseBB] = Vec2Tuple<2>(CreateAndInsertBasicBlocks({GenNameForBB("arr.if.then"), GenNameForBB("arr.if.else")})); (void)CreateCondBr(isThrow, thenBB, elseBB); // 1: generate then branch(throw exception) SetInsertPoint(thenBB); // IndexOutOfBoundsException: throw IndexOutOfBoundsException() CreateOutOfBoundException(); // after calling CJ_MCC_ThrowException should be set unreachable (void)CreateUnreachable(); // 2: generate else branch SetInsertPoint(elseBB); } void IRBuilder2::CallArrayIntrinsicIndexCheck(llvm::Value* array, llvm::Value* index) { CallIntrinsicIndexCheck(CallArrayIntrinsicGetSize(array), index); } llvm::Value* IRBuilder2::CallArrayIntrinsicGetSize(llvm::Value* array) { auto& cgCtx = GetCGContext(); auto i64Ty = getInt64Ty(); auto arrayBaseType = CGType::GetArrayBaseType(cgCtx); auto payloadPtr = CreateBitCast(GetPayloadFromObject(array), arrayBaseType->getPointerTo(1U)); auto ptrToArrLen = CreateBitCast(payloadPtr, i64Ty->getPointerTo(1U)); // Array size is index 1. type i64. return LLVMIRBuilder2::CreateLoad(i64Ty, ptrToArrLen); } llvm::Value* IRBuilder2::GetArrayElementAddr( const CHIR::RawArrayType& arrTy, llvm::Value* array, llvm::Value* index, bool isChecked) { // Safe get will generate index check in codegen. if (isChecked) { CallArrayIntrinsicIndexCheck(array, index); } auto arrayType = CGArrayType::GenerateArrayLayoutType(GetCGModule(), arrTy); auto arrPtr = CreateBitCast(GetPayloadFromObject(array), arrayType->getPointerTo(1u)); auto int32Type = llvm::Type::getInt32Ty(cgMod.GetLLVMContext()); auto zero = llvm::ConstantInt::get(int32Type, 0); auto arrHeadIndex = llvm::ConstantInt::get(int32Type, 1); return CreateGEP(arrayType, arrPtr, {zero, arrHeadIndex, index}, "arr.idx.get.gep"); } void IRBuilder2::CallArrayIntrinsicSet( const CHIR::RawArrayType& arrTy, llvm::Value* array, llvm::Value* index, CGValue& cgVal, bool isChecked) { auto elemCGType = CGType::GetOrCreate(cgMod, arrTy.GetElementType()); auto elemType = elemCGType->GetLLVMType(); auto rawArrayCGType = CGType::GetOrCreate(cgMod, &arrTy); if (!rawArrayCGType->GetSize()) { llvm::Value* offset = CreateMul(GetSize_64(*arrTy.GetElementType()), index); offset = CreateAdd(offset, getInt64(GetPayloadOffset() + 8U)); // 8U: size of rawArray's len field auto fieldAddr = CreateInBoundsGEP(getInt8Ty(), array, offset); fieldAddr = CreateBitCast(fieldAddr, elemType->getPointerTo(array->getType()->getPointerAddressSpace())); GetCGContext().SetBasePtr(fieldAddr, array); auto fieldAddrCGType = CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(GetCGContext().GetCHIRBuilder(), *arrTy.GetElementType())); CreateStore(cgVal, CGValue(fieldAddr, fieldAddrCGType)); return; } auto value = cgVal.GetRawValue(); auto elePtr = GetArrayElementAddr(arrTy, array, index, isChecked); if (elemType->isStructTy()) { if (IsTypeContainsRef(elemType)) { auto structType = llvm::cast(elemType); auto layOut = GetLLVMModule()->getDataLayout().getStructLayout(structType); auto size = getInt64(layOut->getSizeInBytes()); CallGCWriteAgg({array, elePtr, value, size}); } else { auto layOut = GetCGModule().GetLLVMModule()->getDataLayout().getStructLayout(llvm::cast(elemType)); CJC_NULLPTR_CHECK(layOut); auto size = layOut->getSizeInBytes(); auto align = layOut->getAlignment(); CreateMemCpy(elePtr, align, value, align, size); } } else if (elemType->isArrayTy()) { auto size = getInt64(GetLLVMModule()->getDataLayout().getTypeAllocSize(elemType)); CallGCWriteAgg({array, elePtr, value, size}); } else if (elemType == CGType::GetRefType(GetLLVMContext())) { CallGCWrite({value, array, elePtr}); } else { CreateStore(value, elePtr); } } llvm::Value* IRBuilder2::CallArrayIntrinsicGet( const CHIR::RawArrayType& arrTy, llvm::Value* array, llvm::Value* index, bool isChecked) { llvm::Value* elePtr = nullptr; auto elemCHIRTy = arrTy.GetElementType(); auto elemType = CGType::GetOrCreate(cgMod, elemCHIRTy)->GetLLVMType(); auto rawArrayCGType = CGType::GetOrCreate(cgMod, &arrTy); if (!rawArrayCGType->GetSize()) { llvm::Value* offset = CreateMul(GetSize_64(*arrTy.GetElementType()), index); offset = CreateAdd(offset, getInt64(GetPayloadOffset() + 8U)); // 8U: size of rawArray's len field elePtr = CreateInBoundsGEP(getInt8Ty(), array, offset); elePtr = CreateBitCast(elePtr, elemType->getPointerTo(array->getType()->getPointerAddressSpace())); GetCGContext().SetBasePtr(elePtr, array); } else { elePtr = GetArrayElementAddr(arrTy, array, index, isChecked); } auto loadInst = CreateLoad(elemType, elePtr); if (elemCHIRTy->IsEnum()) { llvm::cast(loadInst)->setMetadata("untrusted_ref", llvm::MDNode::get(GetLLVMContext(), {})); } return loadInst; } llvm::Value* IRBuilder2::AcquireRawData(const CHIRIntrinsicWrapper& intrinsic) { std::vector args; for (auto arg : intrinsic.GetOperands()) { args.emplace_back(**(cgMod | arg)); } CJC_ASSERT(args.size() == 1); auto& llvmCtx = GetLLVMContext(); auto int1ty = llvm::Type::getInt1Ty(llvmCtx); CJC_NULLPTR_CHECK(int1ty); auto tmp = CreateEntryAlloca(int1ty); (void)CreateStore(getFalse(), tmp); auto isCopyPtr = CreateBitCast(tmp, llvm::Type::getInt8PtrTy(llvmCtx)); // i8* @cj_acquire_rawdata(i8 addrspace(1)*, i8*) auto func = llvm::Intrinsic::getDeclaration( cgMod.GetLLVMModule(), static_cast(llvm::Intrinsic::cj_acquire_rawdata)); return CreateCallOrInvoke(func, {args[0], isCopyPtr}); } llvm::Value* IRBuilder2::ReleaseRawData(const CHIRIntrinsicWrapper& intrinsic) { std::vector args; for (auto arg : intrinsic.GetOperands()) { args.emplace_back(**(cgMod | arg)); } // The size of parameters must be 2. CJC_ASSERT(args.size() == 2); // CPointer is 2nd. auto recordVal = args[1]; auto mode = llvm::cast( llvm::ConstantInt::getSigned(llvm::Type::getInt32Ty(GetLLVMContext()), static_cast(0))); // void @cj_release_rawdata(i8 addrspace(1)*, i8*, i32) auto func = llvm::Intrinsic::getDeclaration( GetLLVMModule(), static_cast(llvm::Intrinsic::cj_release_rawdata)); return CreateCallOrInvoke(func, {args[0], recordVal, mode}); } void IRBuilder2::CreateRefStore(CGValue* cgValue, llvm::Value* basePtr, llvm::Value* place, bool isBaseObjStruct) { auto currentFunction = GetInsertFunction(); CJC_ASSERT(currentFunction && "Can't get the current function."); auto inStructMut = currentFunction->hasFnAttribute(STRUCT_MUT_FUNC_ATTR); auto valueCGType = cgValue->GetCGType(); auto value = cgValue->GetRawValue(); auto base = basePtr; bool isDstAddrspace1 = base->getType()->getPointerAddressSpace() == 1; if (isBaseObjStruct) { if (IsGlobalVariableBasePtr(place) && valueCGType->IsPointerType() && IsTypeContainsRef(valueCGType->GetPointerElementType()->GetLLVMType())) { CreateWriteBarrierForGlobalVariable(*cgValue, CGValue(place, valueCGType)); return; } if (!inStructMut || !llvm::isa(base)) { if (valueCGType->IsPointerType()) { CreateStore(*cgValue, CGValue(place, valueCGType)); } else { CreateStore(value, place); } return; } else { // If base object is 'this' of struct mut function, base should be previous // param(e.g. __auto_v_xx$BP). if (base->getName() == "this") { base = ¤tFunction->arg_begin()[llvm::cast(base)->getArgNo() - 1]; } } } if (valueCGType->IsStructPtrType()) { if (isDstAddrspace1 && IsTypeContainsRef(valueCGType->GetPointerElementType()->GetLLVMType())) { auto structType = llvm::cast(valueCGType->GetPointerElementType()->GetLLVMType()); auto layOut = cgMod.GetLLVMModule()->getDataLayout().getStructLayout(structType); auto size = llvm::ConstantInt::get(llvm::Type::getInt64Ty(GetLLVMContext()), layOut->getSizeInBytes()); CallGCWriteAgg({base, place, value, size}); } else { auto layOut = GetCGModule().GetLLVMModule()->getDataLayout().getStructLayout( llvm::cast(valueCGType->GetPointerElementType()->GetLLVMType())); CJC_NULLPTR_CHECK(layOut); auto size = layOut->getSizeInBytes(); auto align = layOut->getAlignment(); CreateMemCpy(place, align, value, align, size); } } else if (isDstAddrspace1 && valueCGType->IsVArrayPtrType()) { auto valueType = valueCGType->GetPointerElementType()->GetLLVMType(); auto size = getInt64(cgMod.GetLLVMModule()->getDataLayout().getTypeAllocSize(valueType)); CallGCWriteAgg({base, place, value, size}); } else if (isDstAddrspace1 && valueCGType->IsRefType()) { CallGCWrite({value, base, place}); } else { CreateStore(value, place); } } // This method compare 32 bytes is equal or not, <32 x i8> // It does not check the boundary which is the caller's duty // func vectorCompare32(arr1: RawArray, offset1: Int64, // arr2: RawArray, offset2: Int64): i64 // Return the index of first not equal byte // Return 32 if all 32 bytes are equal llvm::Value* IRBuilder2::EmitVectorCompare32( llvm::Value* arr1, llvm::Value* offset1, llvm::Value* arr2, llvm::Value* offset2) { auto& ctx = GetLLVMContext(); llvm::Type* arrayType = CGArrayType::GenerateArrayLayoutTypeInfo( cgMod.GetCGContext(), ARRAY_LAYOUT_PREFIX + "UInt8", llvm::Type::getInt8Ty(ctx)); llvm::Type* int32Type = llvm::Type::getInt32Ty(ctx); // <32 x i8> llvm::VectorType* vecType = llvm::VectorType::get(llvm::Type::getInt8Ty(ctx), 32, false); llvm::ConstantInt* zero = getInt32(0); // load <32 x i8> vec1 from arr1 llvm::Value* arrPtr1 = CreateBitCast(GetPayloadFromObject(arr1), arrayType->getPointerTo(1u)); llvm::Value* ptr1 = CreateGEP(arrayType, arrPtr1, {zero, llvm::ConstantInt::get(int32Type, 1), offset1}); llvm::Value* vec1 = static_cast(this)->CreateAlignedLoad( vecType, CreateBitCast(ptr1, vecType->getPointerTo(1u)), llvm::MaybeAlign(1), "vec1"); // load <32 x i8> vec2 from arr2 llvm::Value* arrPtr2 = CreateBitCast(GetPayloadFromObject(arr2), arrayType->getPointerTo(1u)); llvm::Value* ptr2 = CreateGEP(arrayType, arrPtr2, {zero, llvm::ConstantInt::get(int32Type, 1), offset2}); llvm::Value* vec2 = static_cast(this)->CreateAlignedLoad( vecType, CreateBitCast(ptr2, vecType->getPointerTo(1u)), llvm::MaybeAlign(1), "vec2"); // compare vec1 and vec2 and get the result <32 x i8> vec3 // CmpNE is used here since we are concerned about which byte is not matched llvm::Value* vec3 = CreateICmpNE(vec1, vec2); // trunc <32 x i8> to <32 x i1> llvm::Value* bitVec = CreateTrunc(vec3, llvm::VectorType::get(llvm::Type::getInt1Ty(ctx), 32, false)); // call i32 @llvm.cttz.i32(i32 bitcast (<32 x i1> to i32)) to get the index of first not equal byte llvm::Value* res = CreateCall( llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cttz, {llvm::Type::getInt32Ty(ctx)}), {CreateBitCast(bitVec, llvm::Type::getInt32Ty(ctx)), llvm::ConstantInt::getFalse(ctx)}); return CreateZExt(res, llvm::Type::getInt64Ty(ctx)); } // func vectorEqualWithByte32(arr: RawArray, offset: Int64, byte: Byte): Int64 // `CallVectorEqualWithByte32` compares each byte in arr[offset..(offset+32)] with the given argument byte // It does not check the boundary which is the caller's duty // Return the position of matched byte in arr which is [0, 32] // Return 32 if the input byte is not found in arr[offset..(offset+32)] // e.g. vectorEqualWithByte32([1, 2, 3, ..., 32], 0, 2) returns 1, since arr[1] == 2 llvm::Value* IRBuilder2::EmitVectorIndexByte32(llvm::Value* arr, llvm::Value* offset, llvm::Value* byte) { auto& ctx = GetLLVMContext(); llvm::Type* arrayType = CGArrayType::GenerateArrayLayoutTypeInfo( cgMod.GetCGContext(), ARRAY_LAYOUT_PREFIX + "UInt8", llvm::Type::getInt8Ty(ctx)); llvm::Type* int32Type = llvm::Type::getInt32Ty(ctx); // <32 x i8> llvm::VectorType* vecType = llvm::VectorType::get(llvm::Type::getInt8Ty(ctx), 32, false); llvm::ConstantInt* zero = getInt32(0); // load <32 x i8> vec1 from arr llvm::Value* arrPtr = CreateBitCast(GetPayloadFromObject(arr), arrayType->getPointerTo(1u)); llvm::Value* ptr = CreateGEP(arrayType, arrPtr, {zero, llvm::ConstantInt::get(int32Type, 1), offset}); llvm::Value* vec1 = static_cast(this)->CreateAlignedLoad( vecType, CreateBitCast(ptr, vecType->getPointerTo(1u)), llvm::MaybeAlign(1), "vec1"); // create <32 x i8> vec from argument byte llvm::Value* tempVec2 = static_cast(this)->CreateInsertElement(vecType, byte, llvm::ConstantInt::get(int32Type, 0)); llvm::Value* vec2 = static_cast(this)->CreateShuffleVector(tempVec2, std::vector(32, 0), "vec2"); // compare vec1 and vec2 and get the result <64 x i8> vec3 llvm::Value* vec3 = CreateICmpEQ(vec1, vec2); // trunc <32 x i8> to <32 x i1> llvm::Value* bitVec = CreateTrunc(vec3, llvm::VectorType::get(llvm::Type::getInt1Ty(ctx), 32, false)); // call i32 @llvm.cttz.i32(i32 bitcast (<32 x i1> to i32)) to get the index of first not equal byte llvm::Value* res = CreateCall( llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cttz, {llvm::Type::getInt32Ty(ctx)}), {CreateBitCast(bitVec, llvm::Type::getInt32Ty(ctx)), llvm::ConstantInt::getFalse(ctx)}); return CreateZExt(res, llvm::Type::getInt64Ty(ctx)); } // (index > len || index < 0) void IRBuilder2::CallVArrayIntrinsicIndexCheck(const CGValue* arrayPtr, std::vector& index) { auto varray = llvm::cast(arrayPtr->GetCGType()->GetPointerElementType()->GetLLVMType()); for (auto& iter : index) { auto len = llvm::ConstantInt::get(llvm::Type::getInt64Ty(GetLLVMContext()), varray->getNumElements()); CallIntrinsicIndexCheck(len, iter); if (!varray->getElementType()->isArrayTy()) { return; } varray = llvm::dyn_cast(varray->getElementType()); } } void IRBuilder2::CreateVArrayStore(CGValue* cgValue, llvm::Value* place) { llvm::Value* basePtr = llvm::Constant::getNullValue(CGType::GetRefType(GetLLVMContext())); bool isBaseObjStruct = true; if (llvm::isa(place)) { auto operand = llvm::cast(place)->getPointerOperand(); while (llvm::isa(operand)) { operand = llvm::cast(operand)->getOperand(0); if (llvm::isa(operand)) { basePtr = llvm::cast(operand)->getOperand(0); isBaseObjStruct = false; break; } } } CreateRefStore(cgValue, basePtr, place, isBaseObjStruct); } namespace { inline llvm::Function* GenerateIntrinsicFunctionDecl( IRBuilder2& irBuilder, llvm::Type* retType, const std::string& name, const std::vector& parameters, const std::vector& attributes) { auto& llvmCtx = irBuilder.GetLLVMContext(); std::vector parameterTypes; for (auto value : parameters) { CJC_ASSERT(value); CJC_ASSERT(value->GetRawValue()); auto paramType = value->GetRawValue()->getType(); if (IsStructPtrType(paramType) && !value->GetCGType()->IsCGTI()) { if (REFLECT_INTRINSIC_FUNC.find(name) == REFLECT_INTRINSIC_FUNC.end()) { (void)parameterTypes.emplace_back(irBuilder.getInt8PtrTy(1)); } if (paramType->getPointerAddressSpace() != 1) { paramType = value->GetCGType()->GetPointerElementType()->GetLLVMType()->getPointerTo(1); } } (void)parameterTypes.emplace_back(paramType); } // The return value is treated as void type when it is Unit type. // Otherwise, if it is struct type, sret needs to be added. llvm::FunctionType* functionType = retType == CGType::GetUnitType(llvmCtx) ? llvm::FunctionType::get(retType, parameterTypes, false) : CGType::GetCodeGenFunctionType(llvmCtx, retType, parameterTypes); llvm::Function* func = irBuilder.GetCGModule().GetOrInsertFunction(name, functionType); if (retType != CGType::GetUnitType(llvmCtx) && retType->isStructTy()) { func->arg_begin()->addAttr(llvm::Attribute::NoAlias); func->arg_begin()->addAttr(llvm::Attribute::getWithStructRetType(llvmCtx, retType)); } AddLinkageTypeMetadata(*func, llvm::GlobalValue::ExternalLinkage, irBuilder.GetCGContext().IsCGParallelEnabled()); for (auto& attribute : attributes) { AddFnAttr(func, llvm::Attribute::get(llvmCtx, attribute)); } return func; } } // namespace // Cangjie function calls the intrinsic function. LLVMCustomizedCreationAPI.cpp:99 // For CJNATIVE: If Cangjie call C functions and C functions call Cangjie functions back, need to add "cj2c" attribute. llvm::Value* IRBuilder2::CallIntrinsicFunction(llvm::Type* retType, const std::string& name, const std::vector& parameters, const std::vector& attributes) { CJC_ASSERT(retType && "The return type of intrinsic function is a nullptr."); auto module = cgMod.GetLLVMModule(); llvm::Function* func = module->getFunction(name); std::vector parametersValues; if (!func) { func = GenerateIntrinsicFunctionDecl(*this, retType, name, parameters, attributes); } if (retType != CGType::GetUnitType(GetLLVMContext()) && retType->isStructTy()) { parametersValues.emplace_back(CreateEntryAlloca(retType)); } for (auto value : parameters) { CJC_ASSERT(value); CJC_ASSERT(value->GetRawValue()); auto llvmVal = value->GetRawValue(); if (IsStructPtrType(llvmVal->getType()) && !value->GetCGType()->IsCGTI()) { // Insert the basePtr for the structure argument. if (REFLECT_INTRINSIC_FUNC.find(name) == REFLECT_INTRINSIC_FUNC.end()) { if (auto basePtr = cgMod.GetCGContext().GetBasePtrOf(llvmVal)) { parametersValues.emplace_back(basePtr); } else { parametersValues.emplace_back(llvm::Constant::getNullValue(getInt8PtrTy(1))); } } if (llvmVal->getType()->getPointerAddressSpace() != 1) { llvmVal = CreateAddrSpaceCast( llvmVal, value->GetCGType()->GetPointerElementType()->GetLLVMType()->getPointerTo(1)); } } parametersValues.emplace_back(llvmVal); } auto callInst = CreateCallOrInvoke(func, parametersValues); if (func->hasStructRetAttr()) { return callInst->getArgOperand(0); } return callInst; } // Call runtime API to create a thread. // There are two APIs // 1. Create a thread with a future object and `Future.execute` as the entry function // 2. Create a thread with a closure and `Future.executeClosure` as the entry function llvm::Value* IRBuilder2::CallSpawnIntrinsic( CGValue& obj, CGFunction& cgFunc, std::optional& threadCtx, bool spawnWithFuture, llvm::Value* futureTI) { // Choose the runtime API to create a new thread // If `spawnWithFuture` is true, `obj` refers to a future object and `executeFuncName` is `Future.execute`; // otherwise, `obj` is a struct Clsoure and `executeFuncName` is `Future.execuClosure`. auto rtFuncName = spawnWithFuture ? PREFIX_OF_BACKEND_SYMS + "NewCJThread" : PREFIX_OF_BACKEND_SYMS + "NewCJThreadNoReturn"; auto i8PtrTy = llvm::Type::getInt8PtrTy(GetLLVMContext()); std::vector args = {&cgFunc, &obj}; auto futureTIVal = CGValue(spawnWithFuture ? futureTI : CreateBitCast(futureTI, i8PtrTy), CGType::GetCGTI(cgMod)); if (threadCtx.has_value()) { (void)args.emplace_back(threadCtx.value()); if (!spawnWithFuture) { (void)args.emplace_back(&futureTIVal); } return CallIntrinsicFunction(i8PtrTy, rtFuncName, args, {}); } else { auto chirType = CGType::GetRefTypeOfCHIRInt8(GetCGContext().GetCHIRBuilder()); auto cgType = CGType::GetOrCreate(cgMod, chirType); auto nullThreadCtx = CGValue(CreateNullValue(*chirType), cgType); (void)args.emplace_back(&nullThreadCtx); if (!spawnWithFuture) { (void)args.emplace_back(&futureTIVal); } return CallIntrinsicFunction(i8PtrTy, rtFuncName, args, {}); } } llvm::Value* IRBuilder2::GenerateDivLikeCheck(llvm::Value* dividend, llvm::Value* divisor, bool isSigned, bool isDiv) { auto zero = llvm::ConstantInt::get(divisor->getType(), 0); auto cond = CreateICmpEQ(divisor, zero); auto [isZeroBB, notZeroBB, endBB] = Vec2Tuple<3>( CreateAndInsertBasicBlocks({GenNameForBB("divisorIs0"), GenNameForBB("divisorIsNot0"), GenNameForBB("end")})); CreateCondBr(cond, isZeroBB, notZeroBB); SetInsertPoint(isZeroBB); CreateOverflowOrArithmeticException("Divided by zero!", false); CreateUnreachable(); SetInsertPoint(notZeroBB); if (isDiv) { return isSigned ? CreateSDiv(dividend, divisor) : CreateUDiv(dividend, divisor); } else { return isSigned ? CreateSRem(dividend, divisor) : CreateURem(dividend, divisor); } CreateBr(endBB); SetInsertPoint(endBB); } llvm::Value* IRBuilder2::GenerateOverflowCheckedFunc( CHIR::ExprKind opKind, const CHIR::Type& ty, std::vector& argGenValues) { bool isNeg = false; CGType* cgType = CGType::GetOrCreate(cgMod, &ty); llvm::Type* type = cgType->GetLLVMType(); // ident: sadd/uadd/ssub/usub/smul/umul. bool isSignedInteger = ty.IsInteger() ? StaticCast(ty).IsSigned() : false; std::string ident = isSignedInteger ? "s" : "u"; if (opKind == CHIR::ExprKind::NEG) { isNeg = true; ident += std::string("sub"); argGenValues.insert(argGenValues.begin(), llvm::ConstantInt::get(type, 0)); } else { ident += OPERATOR_KIND_TO_OP_MAP.at(opKind); } // funcName: llvm.sadd.with.overflow.i8 auto funcName = "llvm." + ident + ".with.overflow." + GetTypeName(cgType); auto boolType = llvm::Type::getInt1Ty(GetLLVMContext()); std::vector params{type, boolType}; auto retType = llvm::StructType::get(GetLLVMContext(), params); std::vector paramsTypes{type, type}; llvm::FunctionType::get(retType, paramsTypes, false); auto funcType = llvm::FunctionType::get(retType, paramsTypes, false); auto func = cgMod.GetOrInsertFunction(funcName, funcType); // call llvm.sadd.with.overflow.i8 auto rightVal = CreateCall(func, argGenValues); if (isNeg) { argGenValues.erase(argGenValues.begin()); } return rightVal; } namespace { llvm::Function* GetGCIntrinsicAlloc(const CGModule& cgMod) { auto module = cgMod.GetLLVMModule(); auto function = llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::cj_malloc_object); function->addAttributeAtIndex(static_cast(llvm::AttributeList::ReturnIndex), llvm::Attribute::get(function->getContext(), llvm::Attribute::NoAlias)); return function; } } // namespace llvm::Value* IRBuilder2::CallClassIntrinsicAlloc(const CHIR::Type& type) { auto value = CallClassIntrinsicAlloc({CreateTypeInfo(type), GetLayoutSize_32(type)}); if (type.IsClass()) { CJC_ASSERT(!type.IsAutoEnvBase() && "Should not reach here, please check CHIR."); auto& classDef = *static_cast(type).GetClassDef(); std::vector mallocType; auto& llvmCtx = getContext(); if (classDef.GetFinalizer()) { mallocType.emplace_back(llvm::MDString::get(llvmCtx, GC_FINALIZER_ATTR)); } if (IsCoreFutureClass(classDef) || IsSyncRelatedClass(classDef) || IsWeakRefClass(classDef)) { mallocType.emplace_back(llvm::MDString::get(llvmCtx, classDef.GetSrcCodeIdentifier())); } if (!mallocType.empty()) { auto meta = llvm::MDTuple::get(llvmCtx, mallocType); value->setMetadata("MallocType", meta); } } return value; } // parameters = {TypeInfo* ti, i32 size} llvm::Instruction* IRBuilder2::CallClassIntrinsicAlloc(const std::vector& parameters) { CJC_ASSERT(parameters.size() == 2U); auto allocFunc = GetGCIntrinsicAlloc(cgMod); auto fixedParams = {CreateBitCast(parameters[0], getInt8PtrTy()), parameters[1]}; auto callInst = CreateCallOrInvoke(allocFunc, fixedParams); callInst->addAttributeAtIndex( llvm::AttributeList::ReturnIndex, llvm::Attribute::get(GetLLVMContext(), llvm::Attribute::NoAlias)); return callInst; } llvm::Value* IRBuilder2::GenerateOverflowSaturatingFunc( CHIR::ExprKind opKind, const CHIR::Type& ty, const std::vector& argGenValues) { CGType* cgType = CGType::GetOrCreate(cgMod, &ty); llvm::Type* type = cgType->GetLLVMType(); bool isSignedInteger = ty.IsInteger() ? StaticCast(ty).IsSigned() : false; if (opKind == CHIR::ExprKind::ADD || (opKind == CHIR::ExprKind::SUB)) { // ident: sadd/uadd/ssub/usub. auto ident = (isSignedInteger ? "s" : "u") + OPERATOR_KIND_TO_OP_MAP.at(opKind); // funcName: llvm.sadd.sat.i8 auto funcName = "llvm." + ident + ".sat." + GetTypeName(cgType); std::vector paramsTypes{type, type}; llvm::FunctionType::get(type, paramsTypes, false); auto funcType = llvm::FunctionType::get(type, paramsTypes, false); auto func = cgMod.GetOrInsertFunction(funcName, funcType); // call llvm.sadd.sat.i8 return CreateCall(func, argGenValues); } if (opKind == CHIR::ExprKind::NEG && !isSignedInteger) { // saturatingNeg return llvm::ConstantInt::get(type, 0); } return nullptr; } llvm::Value* IRBuilder2::GenerateOverflowWrappingFunc( CHIR::ExprKind opKind, const CHIR::Type& ty, [[maybe_unused]] std::vector& argGenValues) { llvm::Type* type = CGType::GetOrCreate(cgMod, &ty)->GetLLVMType(); auto minVal = CodeGen::GetIntMaxOrMin(*this, StaticCast(ty), false); return llvm::ConstantInt::getSigned(type, opKind == CHIR::ExprKind::MOD ? 0 : minVal); } llvm::Value* IRBuilder2::GenerateCallExpectFunction(CGType* cgType, llvm::Value* val, llvm::Value* expectVal) { // intrinsic func: llvm.expect.{type}( , ) llvm::Type* type = cgType->GetLLVMType(); auto funcName = "llvm.expect." + GetTypeName(cgType); auto retType = cgType->GetLLVMType(); std::vector paramsTypes{type, type}; llvm::FunctionType::get(retType, paramsTypes, false); auto funcType = llvm::FunctionType::get(retType, paramsTypes, false); auto func = cgMod.GetOrInsertFunction(funcName, funcType); // call the intrinsic function: llvm.expect.{type} std::vector argValues{val, expectVal}; return CreateCall(func, argValues); } // The IR generated manually by this function depends on the ABI of the Cangjie CPointer. If the processing of the // CPointer changes, adaptation is required. void IRBuilder2::CreateOverflowOrArithmeticException(const std::string& ident, bool isOverflow) { const std::string createOverflowExceptionFuncName = "rt$CreateOverflowException_msg"; const std::string createArithmeticExceptionFuncName = "rt$CreateArithmeticException_msg"; auto funcName = isOverflow ? createOverflowExceptionFuncName : createArithmeticExceptionFuncName; auto createExceptionFuncCHIRNode = GetCGContext().GetImplicitUsedFunc(funcName); auto createExceptionFunc = cgMod.GetOrInsertCGFunction(createExceptionFuncCHIRNode); auto rawFunction = createExceptionFunc->GetRawFunction(); auto createExceptionFuncRawType = rawFunction->getFunctionType(); CJC_ASSERT(createExceptionFuncRawType->getNumParams() == 1U && "Incorrect number of parameters"); auto chirTypeOfParam = StaticCast(createExceptionFuncCHIRNode->GetType())->GetParamTypes()[0]; auto cgTypeOfParam = CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(GetCGContext().GetCHIRBuilder(), *chirTypeOfParam)); auto cjString = FixFuncArg( CGValue(CreateStringLiteral(ident), cgTypeOfParam), *createExceptionFunc->GetCGFunctionType()->GetParamType(0)); auto exceptionValue = CreateCallOrInvoke(rawFunction, {cjString}); CallExceptionIntrinsicThrow(exceptionValue); } namespace { inline void CreateImplicitUsedFuncCall( IRBuilder2& irBuilder, const std::string& funcMangledName, const std::vector& args) { auto implicitImportedFunc = irBuilder.GetCGModule().GetOrInsertCGFunction(irBuilder.GetCGContext().GetImplicitUsedFunc(funcMangledName)); CJC_ASSERT(implicitImportedFunc && "Cant get the implicit Imported function."); auto implicitImportedFuncRaw = implicitImportedFunc->GetRawFunction(); irBuilder.CreateCallOrInvoke(implicitImportedFuncRaw, args); } inline void CreateExceptionAndThrow( IRBuilder2& irBuilder, const std::string& exceptionClassName, const std::string& exceptionInitFuncName) { auto exceptionClassDef = DynamicCast( GetTypeDefFromImplicitUsedFuncParam(irBuilder.GetCGModule(), exceptionInitFuncName, 0)); CJC_NULLPTR_CHECK(exceptionClassDef); auto exceptionValue = irBuilder.CallClassIntrinsicAlloc(*exceptionClassDef->GetType()); CreateImplicitUsedFuncCall( irBuilder, exceptionInitFuncName, {exceptionValue, irBuilder.CreateTypeInfo(*exceptionClassDef->GetType())}); irBuilder.CallExceptionIntrinsicThrow(exceptionValue); } } // namespace void IRBuilder2::CreateNegativeArraySizeException() { const std::string negativeArraySizeExceptionClassName = "_CNat26NegativeArraySizeExceptionE"; const std::string negativeArraySizeExceptionInitFuncName = "_CNat26NegativeArraySizeException6Hv"; CreateExceptionAndThrow(*this, negativeArraySizeExceptionClassName, negativeArraySizeExceptionInitFuncName); } void IRBuilder2::CreateOutOfBoundException() { const std::string indexOutOfBoundsExceptionClassName = "_CNat25IndexOutOfBoundsExceptionE"; const std::string indexOutOfBoundsExceptionInitFuncName = "_CNat25IndexOutOfBoundsException6Hv"; CreateExceptionAndThrow(*this, indexOutOfBoundsExceptionClassName, indexOutOfBoundsExceptionInitFuncName); } void IRBuilder2::CreateRunTimeInvalidGenericParamException() { const std::string invalidGenericParamExceptionClassName = "_CNat9ExceptionE"; const std::string invalidGenericParamExceptionInitFuncName = "_CNat9Exception6Hv"; CreateExceptionAndThrow(*this, invalidGenericParamExceptionClassName, invalidGenericParamExceptionInitFuncName); } void IRBuilder2::CreateSpawnException() { const std::string spawnExceptionClassName = "_CNat14SpawnExceptionE"; const std::string spawnExceptionInitFuncName = "_CNat14SpawnException6Hv"; CreateExceptionAndThrow(*this, spawnExceptionClassName, spawnExceptionInitFuncName); } void IRBuilder2::CreateExecAtExitCallbacksCall() { const std::string execAtExitCallbacksFuncName = "_CNat27CJ_CORE_ExecAtexitCallbacksHv"; CreateImplicitUsedFuncCall( *this, execAtExitCallbacksFuncName, {CreateEntryAlloca(CGType::GetUnitType(GetLLVMContext()))}); } void IRBuilder2::CreateGetCommandLineArgsCall(const std::vector& args) { const std::string getCommandLineArgsFuncName = "_CNat18getCommandLineArgsHv"; CreateImplicitUsedFuncCall(*this, getCommandLineArgsFuncName, args); } void IRBuilder2::CreateHandleExceptionCall(const std::vector& args) { const std::string handleExceptionFuncName = "_CNat15handleExceptionHCNat9ExceptionE"; CreateImplicitUsedFuncCall(*this, handleExceptionFuncName, args); } void IRBuilder2::CreateSetRuntimeCJThreadHandleCall(const std::vector& args) { const std::string setRuntimeCJThreadHandleFuncName = "_CNat6Thread24setRuntimeCJThreadHandleHPu"; CreateImplicitUsedFuncCall(*this, setRuntimeCJThreadHandleFuncName, args); } void IRBuilder2::CreateEPrintlnCall(const std::string& eMsg) { const std::string eprintlnFuncName = "_CNat8eprintlnHRNat6StringE"; auto eprintlnFuncCHIRNode = GetCGContext().GetImplicitUsedFunc(eprintlnFuncName); auto eprintlnFunc = cgMod.GetOrInsertCGFunction(eprintlnFuncCHIRNode); auto rawFunction = eprintlnFunc->GetRawFunction(); auto eprintlnFuncRawType = rawFunction->getFunctionType(); constexpr size_t size2 = 2; CJC_ASSERT(eprintlnFuncRawType->getNumParams() == size2 && "Incorrect number of parameters"); auto chirTypeOfParam = StaticCast(eprintlnFuncCHIRNode->GetType())->GetParamTypes()[0]; auto cgTypeOfParam = CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(GetCGContext().GetCHIRBuilder(), *chirTypeOfParam)); std::vector params{CreateEntryAlloca(CGType::GetUnitType(GetLLVMContext())), FixFuncArg( CGValue(CreateStringLiteral(eMsg), cgTypeOfParam), *eprintlnFunc->GetCGFunctionType()->GetParamType(0))}; CreateCallOrInvoke(rawFunction, params); } llvm::Value* IRBuilder2::CallSyncIntrinsics( const CHIRIntrinsicWrapper& intrinsic, const std::vector& parameters) { if (IsAtomicIntrinsic(intrinsic.GetIntrinsicKind())) { return CallAtomicIntrinsics(intrinsic, parameters); } return CallIntrinsic(intrinsic, parameters); } llvm::Value* IRBuilder2::CallAtomicIntrinsics(const CHIRIntrinsicWrapper& intrinsic, const std::vector& args) { llvm::Value* fieldPtr = CreateGEP(*args[0], {0U}).GetRawValue(); auto atomicIntrinsicGenericTypeInfo = intrinsic.GetInstantiatedTypeArgs(); CJC_ASSERT(atomicIntrinsicGenericTypeInfo.size() == 2U); if (atomicIntrinsicGenericTypeInfo[1]->IsPrimitive()) { return CallAtomicPrimitiveIntrinsics(intrinsic, args, fieldPtr); } std::vector parameters; // func compareAndSwap has two memoryorder parameters bool isCompareAndSwapFunc = intrinsic.GetIntrinsicKind() == CHIR::IntrinsicKind::ATOMIC_COMPARE_AND_SWAP; size_t size = !isCompareAndSwapFunc ? args.size() - 1 : args.size() - 2; for (unsigned int i = 1; i < size; i++) { parameters.push_back(**args[i]); } // obj parameters.push_back(**args[0]); // fieldptr fieldPtr = CreateBitCast(fieldPtr, getInt8PtrTy(1U)->getPointerTo(fieldPtr->getType()->getPointerAddressSpace())); parameters.push_back(fieldPtr); // Prepare memoryOrder arguments. // Currently, Cangjie only supports one memory order `SeqCst`. // `compareAndSwap` has two memory order parameters, while other methods have one. const uint32_t seqCstAsInt32 = 5u; // 5 is memory_order_seq_cst in C11 memory model if (isCompareAndSwapFunc) { auto successOrder = getInt32(seqCstAsInt32); auto failureOrder = getInt32(seqCstAsInt32); parameters.push_back(successOrder); parameters.push_back(failureOrder); } else { auto memoryOrder = getInt32(seqCstAsInt32); parameters.push_back(memoryOrder); } if (intrinsic.GetIntrinsicKind() == CHIR::ATOMIC_STORE || intrinsic.GetIntrinsicKind() == CHIR::ATOMIC_SWAP) { // Get type of value to be stored or swapped. auto type = intrinsic.GetOperand(1)->GetType(); if (auto cgType = CGType::GetOrCreate(cgMod, type); !cgType->GetSize() && !type->IsGeneric()) { CJC_ASSERT(cgType->IsCGEnum()); auto payloadPtr = GetPayloadFromObject(**args[1]); parameters[0] = CallGCRead({**args[1], payloadPtr}); } } // llvm.cj.atomic.xx(e.g.llvm.cj.atomic.store) auto res = CallIntrinsic(intrinsic, parameters); if (auto rstType = intrinsic.GetResult()->GetType(); !rstType->IsGeneric()) { if (auto cgType = CGType::GetOrCreate(cgMod, rstType); !cgType->GetSize()) { CJC_ASSERT(intrinsic.GetIntrinsicKind() == CHIR::ATOMIC_LOAD || intrinsic.GetIntrinsicKind() == CHIR::ATOMIC_SWAP); CJC_ASSERT(cgType->IsCGEnum()); auto boxVal = CallIntrinsicAllocaGeneric({CreateTypeInfo(*rstType), getInt32(8U)}); auto payloadPtr = GetPayloadFromObject(boxVal); CallGCWrite({res, boxVal, payloadPtr}); return boxVal; } } return res; } llvm::Value* IRBuilder2::CallAtomicPrimitiveIntrinsics( const CHIRIntrinsicWrapper& intrinsic, const std::vector& args, llvm::Value* fieldPtr) { llvm::Type* fieldType = llvm::cast(fieldPtr)->getResultElementType(); if (intrinsic.GetIntrinsicKind() == CHIR::IntrinsicKind::ATOMIC_COMPARE_AND_SWAP) { auto cas = CreateAtomicCmpXchg(fieldPtr, **args[1], **args[2], llvm::AtomicOrdering::SequentiallyConsistent, llvm::AtomicOrdering::SequentiallyConsistent); // The type of the return value cas is a structure type that contains two fields. The first field is the old // value, and the second field is of the bool type, indicating whether the CmpXchg operation is successful. return llvm::ExtractValueInst::Create(cas, 1, "", GetInsertBlock()); } static const std::unordered_map OPMAP = { {CHIR::IntrinsicKind::ATOMIC_SWAP, llvm::AtomicRMWInst::Xchg}, {CHIR::IntrinsicKind::ATOMIC_FETCH_ADD, llvm::AtomicRMWInst::Add}, {CHIR::IntrinsicKind::ATOMIC_FETCH_SUB, llvm::AtomicRMWInst::Sub}, {CHIR::IntrinsicKind::ATOMIC_FETCH_AND, llvm::AtomicRMWInst::And}, {CHIR::IntrinsicKind::ATOMIC_FETCH_OR, llvm::AtomicRMWInst::Or}, {CHIR::IntrinsicKind::ATOMIC_FETCH_XOR, llvm::AtomicRMWInst::Xor}}; if (OPMAP.find(intrinsic.GetIntrinsicKind()) != OPMAP.end()) { return CreateAtomicRMW(OPMAP.at(intrinsic.GetIntrinsicKind()), fieldPtr, **args[1], llvm::MaybeAlign(), llvm::AtomicOrdering::SequentiallyConsistent); } // Integer type's alignment depends on bit width. unsigned long align = fieldType->getPrimitiveSizeInBits(); if (intrinsic.GetIntrinsicKind() == CHIR::IntrinsicKind::ATOMIC_STORE) { auto store = CreateStore(**args[1], fieldPtr); CJC_NULLPTR_CHECK(store); store->setAlignment(llvm::Align(align)); store->setAtomic(llvm::AtomicOrdering::SequentiallyConsistent); return nullptr; } if (intrinsic.GetIntrinsicKind() == CHIR::IntrinsicKind::ATOMIC_LOAD) { auto load = LLVMIRBuilder2::CreateLoad(fieldType, fieldPtr); load->setAlignment(llvm::Align(align)); load->setAtomic(llvm::AtomicOrdering::SequentiallyConsistent); return load; } return nullptr; } llvm::Value* IRBuilder2::CallMathIntrinsics( const CHIRIntrinsicWrapper& intrinsic, std::vector& parameters) { switch (intrinsic.GetIntrinsicKind()) { case CHIR::IntrinsicKind::POWI: { constexpr size_t size2 = 2; CJC_ASSERT(parameters.size() == size2); return CallIntrinsic(intrinsic, parameters, {parameters[0]->getType(), parameters[1]->getType()}); } case CHIR::IntrinsicKind::ABS: { // llvm.abs.i8/i16/i32/i64(type, bool) parameters.emplace_back(getFalse()); return CallIntrinsic(intrinsic, parameters, {parameters[0]->getType()}); } default: { return CallIntrinsic(intrinsic, parameters, {parameters[0]->getType()}); } } } llvm::Value* IRBuilder2::CallIntrinsicForUninitialized(const CHIR::Type& ty) { if (ty.IsGeneric()) { auto [handleRef, handleNonRef, handleNothing, getRes] = Vec2Tuple<4>(CreateAndInsertBasicBlocks({GenNameForBB("handle_ref"), GenNameForBB("handle_non_ref"), GenNameForBB("handle_nothing"), GenNameForBB("get_uninitialized_value")})); auto ti = CreateBitCast(CreateTypeInfo(ty), getInt8PtrTy()); auto nothingTi = CreateBitCast(CGType::GetNothingCGType(cgMod)->GetOrCreateTypeInfo(), getInt8PtrTy()); auto isNothing = CreateIntrinsic(llvm::Intrinsic::expect, {getInt1Ty()}, {CreateICmpEQ(ti, nothingTi), getFalse()}); CreateCondBr(isNothing, handleNothing, handleRef); // For zeroValue, throw exception: SetInsertPoint(handleNothing); CreateRunTimeInvalidGenericParamException(); CreateUnreachable(); // For zeroValue, use null pointer: SetInsertPoint(handleRef); auto isRef = CreateTypeInfoIsReferenceCall(ti); auto lastBBOfHandleRef = GetInsertBlock(); CreateCondBr(isRef, getRes, handleNonRef); // For zeroValue, create null value: SetInsertPoint(handleNonRef); auto zeroValue = CreateNullValue(ty); auto lastBBOfHandleNonRef = GetInsertBlock(); CreateBr(getRes); // Select value according to the predecessor: SetInsertPoint(getRes); auto p1i8 = getInt8PtrTy(1U); auto phi = CreatePHI(p1i8, 2U); phi->addIncoming(llvm::ConstantPointerNull::get(p1i8), lastBBOfHandleRef); phi->addIncoming(zeroValue, lastBBOfHandleNonRef); return phi; } return CreateNullValue(ty); } llvm::Value* IRBuilder2::CallRuntimeIntrinsics( const CHIRIntrinsicWrapper& syscall, const std::vector& parameters) { return CallIntrinsic(syscall, parameters); } llvm::Value* IRBuilder2::VArrayInitedByLambda( llvm::Value* varrayLen, CGValue& autoEnv, const CHIR::VArrayType& vArrayType) { // Get InitFunc. auto autoEnvCGType = autoEnv.GetCGType(); auto autoEnvClsDef = StaticCast(DeRef(autoEnvCGType->GetOriginal()))->GetClassDef(); auto abstractMethods = autoEnvClsDef->GetAbstractMethods(); CJC_ASSERT(abstractMethods.size() == 1); auto abstractMethod = abstractMethods.back(); auto abstractMethodIdx = CHIR::GetMethodIdxInAutoEnvObject(abstractMethod.methodName); auto initFuncCGType = static_cast( CGType::GetOrCreate(cgMod, abstractMethod.methodTy, CGType::TypeExtraInfo{0, true, false, true, {}})); auto autoEnvPayload = GetPayloadFromObject(*autoEnv); auto initFuncPtr = CreateConstGEP1_32(getInt8PtrTy(), autoEnvPayload, static_cast(abstractMethodIdx), "virtualFPtr"); initFuncPtr = CreateLoad(getInt8PtrTy(), initFuncPtr); initFuncPtr = CreateBitCast(initFuncPtr, initFuncCGType->GetLLVMFunctionType()->getPointerTo()); auto arrayCGType = CGType::GetOrCreate(cgMod, &vArrayType); auto varrPtr = CreateEntryAlloca(*arrayCGType, "varray.initialized.by.lambda"); // Prepare basic blocks. auto [startBB, bodyBB, endBB] = Vec2Tuple<3>(CreateAndInsertBasicBlocks( {GenNameForBB("varr.init.start"), GenNameForBB("varr.init.body"), GenNameForBB("varr.init.end")})); // Prepare iterator to assign the varray element. (void)CreateBr(startBB); SetInsertPoint(startBB); auto indexCGType = CGType::GetInt64CGType(cgMod); auto iterator = CreateEntryAlloca(*indexCGType, "iterator"); (void)CreateStore(getInt64(0), iterator); (void)CreateBr(bodyBB); // Generate body. SetInsertPoint(bodyBB); auto index = CreateLoad(indexCGType->GetLLVMType(), iterator); auto cgValueOfIdx = CGValue(index, indexCGType); llvm::Value* value = CreateCallOrInvoke(*initFuncCGType, initFuncPtr, {&autoEnv, &cgValueOfIdx}); auto elemType = vArrayType.GetElementType(); auto elemCGType = CGType::GetOrCreate(cgMod, elemType); auto elemPtrCGType = CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(GetCGContext().GetCHIRBuilder(), *elemType)); auto elemPtr = CreateGEP(arrayCGType->GetLLVMType(), varrPtr, {getInt64(0), index}, "varr.idx"); if (elemCGType->IsVArrayType() || elemCGType->IsStructType()) { (void)CreateStore(CGValue(value, elemPtrCGType), CGValue(elemPtr, elemPtrCGType)); } else { (void)CreateStore(CGValue(value, elemCGType), CGValue(elemPtr, elemPtrCGType)); } // Generate increment and compare size. auto incremented = CreateAdd(index, getInt64(1)); (void)CreateStore(incremented, iterator); auto cmpResult = CreateICmpSLT(incremented, varrayLen, "iterator.size.cmp"); (void)CreateCondBr(cmpResult, bodyBB, endBB); // Change to end BB. SetInsertPoint(endBB); return varrPtr; } llvm::Value* IRBuilder2::VArrayInitedByItem(llvm::Value* varrayLen, const CGValue& cgVal, const CHIR::Type& vArrayType) { auto& cgCtx = GetCGContext(); CGType* arrayCGType = CGType::GetOrCreate(cgMod, &vArrayType); auto arrayType = arrayCGType->GetLLVMType(); auto varrPtr = CreateEntryAlloca(*arrayCGType, "varray.initialized.by.item"); // Prepare basic blocks. auto [startBB, bodyBB, endBB] = Vec2Tuple<3>(CreateAndInsertBasicBlocks( {GenNameForBB("varr.init.start"), GenNameForBB("varr.init.body"), GenNameForBB("varr.init.end")})); // Prepare iterator to assign the varray element. (void)CreateBr(startBB); SetInsertPoint(startBB); auto iterator = CreateEntryAlloca(*CGType::GetInt64CGType(cgMod), "iterator"); (void)CreateStore(getInt64(0), iterator); (void)CreateBr(bodyBB); // Generate body. SetInsertPoint(bodyBB); auto index = CreateLoad(CGType::GetInt64CGType(cgMod)->GetLLVMType(), iterator); auto elemPtr = CreateGEP(arrayType, varrPtr, {getInt64(0), index}, "varr.idx"); auto elementPtrCGType = CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf( cgCtx.GetCHIRBuilder(), *StaticCast(&vArrayType)->GetElementType())); (void)CreateStore(cgVal, CGValue(elemPtr, elementPtrCGType)); // Generate increment and compare size. auto incremented = CreateAdd(index, getInt64(1)); (void)CreateStore(incremented, iterator); auto cmpResult = CreateICmpSLT(incremented, varrayLen, "iterator.size.cmp"); (void)CreateCondBr(cmpResult, bodyBB, endBB); // Change to end BB. SetInsertPoint(endBB); return varrPtr; } llvm::Value* IRBuilder2::CallIntrinsicFloatToIntegerSat(llvm::Type& destTy, llvm::Value& srcValue, bool isSigned) { auto id = static_cast(isSigned ? llvm::Intrinsic::fptosi_sat : llvm::Intrinsic::fptoui_sat); llvm::Function* func = llvm::Intrinsic::getDeclaration(GetLLVMModule(), id, {&destTy, srcValue.getType()}); return CreateCall(func, &srcValue); } llvm::Instruction* IRBuilder2::CallIntrinsicGetTypeInfo(const std::vector& parameters) { CJC_ASSERT(parameters.size() == 3U); llvm::Function* func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_get_type_info); auto fixedParams = { CreateBitCast(parameters[0], getInt8PtrTy()), parameters[1], CreateBitCast(parameters[2], getInt8PtrTy()->getPointerTo()), }; return CreateCall(func, fixedParams); } llvm::Instruction* IRBuilder2::CallIntrinsicGetFieldOffset(const std::vector& parameters) { CJC_ASSERT(parameters.size() == 2U || parameters.size() == 3U); CJC_ASSERT(parameters[1]->getType()->isIntegerTy(64U)); llvm::Function* func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_get_field_offset); const size_t idxTypeinfo = 0; const size_t idxIndex = 1; std::vector fixedParams = { CreateBitCast(parameters[idxTypeinfo], getInt8PtrTy()), parameters[idxIndex]}; // llvm.cj.get.field.offset needs 3 arguments: typeinfo, index, pre-offset // If only 2 arguments are provided, it means the default value of pre-offset is 0. if (parameters.size() == 2U) { (void)fixedParams.emplace_back(getInt32(0)); } else { const size_t idxPreOffset = 2; (void)fixedParams.emplace_back(parameters[idxPreOffset]); } return CreateCall(func, fixedParams); } llvm::Instruction* IRBuilder2::CallIntrinsicIsSubtype(const std::vector& parameters) { CJC_ASSERT(parameters.size() == 2U); llvm::Function* func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_is_subtype); auto fixedParams = {CreateBitCast(parameters[0], getInt8PtrTy()), CreateBitCast(parameters[1], getInt8PtrTy())}; return CreateCall(func, fixedParams); } llvm::Instruction* IRBuilder2::CallIntrinsicIsTupleTypeOf(const std::vector& parameters) { CJC_ASSERT(parameters.size() == 3U); auto boolTy = getInt1Ty(); auto pi8 = getInt8PtrTy(); auto p1i8 = getInt8PtrTy(1U); auto objectLLVMType = parameters[0]->getType(); bool isRefType = objectLLVMType->isPointerTy() && objectLLVMType->getPointerAddressSpace() == 1U; auto funcName = std::string("llvm.cj.is.tupletype.of.") + (isRefType ? "p1i8" : "p0i8"); auto funcType = llvm::FunctionType::get(boolTy, {(isRefType ? p1i8 : pi8), pi8, pi8}, false); auto func = cgMod.GetOrInsertFunction(funcName, funcType); auto fixedParams = {CreateBitCast(parameters[0], isRefType ? p1i8 : pi8), CreateBitCast(parameters[1], pi8), CreateBitCast(parameters[2], pi8)}; return CreateCall(func, fixedParams); } llvm::Instruction* IRBuilder2::CallIntrinsicIsTypeEqualTo(const std::vector& parameters) { CJC_ASSERT(parameters.size() == 2U); llvm::Function* func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_is_typeinfo_equal); auto fixedParams = {CreateBitCast(parameters[0], getInt8PtrTy()), CreateBitCast(parameters[1], getInt8PtrTy())}; return CreateCall(func, fixedParams); } // parameters = {TypeInfo* ti, i32 size} llvm::Instruction* IRBuilder2::CallIntrinsicAllocaGeneric(const std::vector& parameters) { auto curLoc = getCurrentDebugLocation(); SetCurrentDebugLocation(llvm::DebugLoc()); CJC_ASSERT(parameters.size() == 2U); llvm::Function* func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_alloca_generic); auto fixedParams = {CreateBitCast(parameters[0], getInt8PtrTy()), parameters[1]}; auto inst = CreateCallOrInvoke(func, fixedParams); SetCurrentDebugLocation(curLoc); return inst; } llvm::Instruction* IRBuilder2::CallIntrinsicGCWriteGeneric(const std::vector& parameters) { CJC_ASSERT(parameters.size() == 4U); llvm::Function* func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_gcwrite_generic); auto fixedParams = {parameters[0], CreateBitCast(parameters[1], getInt8PtrTy(1U)), parameters[2], parameters[3]}; return CreateCall(func, fixedParams); } llvm::Instruction* IRBuilder2::CallGCWriteGenericPayload(const std::vector& parameters) { CJC_ASSERT(parameters.size() == 3U); llvm::Function* func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_gcwrite_generic_payload); return CreateCall(func, parameters); } llvm::Instruction* IRBuilder2::CallGCReadGeneric(const std::vector& parameters) { CJC_ASSERT(parameters.size() == 4U); llvm::Function* func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_gcread_generic); auto fixedParams = {parameters[0], parameters[1], CreateBitCast(parameters[2], getInt8PtrTy(1U)), parameters[3]}; return CreateCall(func, fixedParams); } llvm::Instruction* IRBuilder2::CallIntrinsicMTable(const std::vector& parameters) { CJC_ASSERT(parameters.size() == 3U); llvm::Function* func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_get_mtable_func); auto fixedParams = { CreateBitCast(parameters[0], getInt8PtrTy()), CreateBitCast(parameters[1], getInt8PtrTy()), parameters[2]}; return CreateCall(func, fixedParams); } llvm::Instruction* IRBuilder2::CallIntrinsicGetVTableFunc( llvm::Value* ti, llvm::Value* introTypeIdx, llvm::Value* funcOffset) { llvm::Function* func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_get_vtable_func); return CreateCall(func, {CreateBitCast(ti, getInt8PtrTy()), introTypeIdx, funcOffset}); } // parameters = {i8 any addrspace* dst, i8 addrspace(1)* src, TypeInfo* ti} llvm::Instruction* IRBuilder2::CallIntrinsicAssignGeneric(const std::vector& parameters) { CJC_ASSERT(parameters.size() == 3U); llvm::Function* func = llvm::Intrinsic::getDeclaration(cgMod.GetLLVMModule(), llvm::Intrinsic::cj_assign_generic); auto fixedParams = {parameters[0], parameters[1], CreateBitCast(parameters[2], getInt8PtrTy())}; return CreateCall(func, fixedParams); } llvm::Value* IRBuilder2::CreateTypeInfoArray(const std::vector& types, const std::unordered_map>& map) { auto typeInfoPtrType = CGType::GetOrCreateTypeInfoPtrType(GetLLVMContext()); auto argSize = types.size(); auto arrayType = llvm::ArrayType::get(typeInfoPtrType, argSize); auto typeArgsArr = CreateEntryAlloca(arrayType); auto storeInst = CreateStore(llvm::Constant::getNullValue(arrayType), typeArgsArr); std::vector typeInfoElements; for (uint32_t idx = 0; idx < argSize; ++idx) { auto type = DeRef(*types[idx]); auto localGt = CGType::GetOrCreate(cgMod, type); if (localGt->IsDynamicGI()) { typeInfoElements.emplace_back(llvm::Constant::getNullValue(typeInfoPtrType)); auto tiDynamic = CreateBitCast(CreateTypeInfo(*type, map), typeInfoPtrType); auto storeAt = CreateConstInBoundsGEP2_32(arrayType, typeArgsArr, 0, idx); (void)CreateStore(tiDynamic, storeAt); } else { typeInfoElements.emplace_back(localGt->GetOrCreateTypeInfo()); } } auto realConstantArray = llvm::ConstantArray::get(arrayType, typeInfoElements); if (realConstantArray && realConstantArray->isNullValue()) { CJC_NULLPTR_CHECK(storeInst); storeInst->eraseFromParent(); } else { CJC_NULLPTR_CHECK(storeInst); storeInst->setOperand(0, realConstantArray); } return typeArgsArr; } llvm::Value* IRBuilder2::CreateTypeInfoArray(const std::vector& types) { CJC_ASSERT( cgFunction && "The current IRBuilder doesn't correspond to any CGFunction. Use another CreateTypeInfo method."); return CreateTypeInfoArray(types, cgFunction->genericParamsMap); } llvm::Value* IRBuilder2::CreateTypeInfo(const CHIR::Type& gt, const std::unordered_map>& map, bool canChangeBB) { auto baseType = DeRef(const_cast(gt)); if (baseType->IsThis()) { CJC_ASSERT(cgFunction && "Should not reach here."); auto cgFuncType = cgFunction->GetCGFunctionType(); if (cgFuncType->IsStaticMethodType()) { auto thisTIIdx = cgFunction->GetCGFunctionType()->GetThisTypeInfoIndex(); CJC_ASSERT(thisTIIdx.has_value()); return cgFunction->GetRawFunction()->getArg(thisTIIdx.value()); } else { CJC_ASSERT(cgFuncType->IsMethodType() && "CHIR should not use `ThisType` outside a method"); auto f = cgFunction->GetRawFunction(); auto thisArg = f->getArg(f->hasStructRetAttr() ? 1U : 0U); CJC_ASSERT(thisArg->getType()->isPointerTy() && thisArg->getType()->getPointerAddressSpace() == 1U); return GetTypeInfoFromObject(thisArg); } } auto& cacheMap = GetCGContext().genericParamsCacheMap[GetInsertFunction()]; if (auto it = cacheMap.find(baseType); it != cacheMap.end()) { return it->second; } auto cgType = CGType::GetOrCreate(cgMod, baseType); auto typeInfoPtrType = CGType::GetOrCreateTypeInfoPtrType(GetLLVMContext()); std::vector genericArgs; llvm::GlobalVariable* tt{nullptr}; llvm::Value* res{nullptr}; TemporarilySetDebugLocInThisScope withEmptyLoc(*this, llvm::DebugLoc()); if (auto foundIt = map.find(baseType); foundIt != map.end()) { auto curPt = LLVMIRBuilder2::GetInsertPoint(); auto curBB = LLVMIRBuilder2::GetInsertBlock(); if (canChangeBB) { SetInsertPointForPreparingTypeInfo(); } res = foundIt->second(*this); LLVMIRBuilder2::SetInsertPoint(curBB, curPt); } else if (baseType->IsGeneric()) { if (auto upperBounds = static_cast(baseType)->GetUpperBounds(); upperBounds.size() == 1 && static_cast(baseType)->orphanFlag) { res = CreateTypeInfo(upperBounds[0]); } else { CJC_ASSERT(false && "CHIR uses an unexpected Generic-Type."); return nullptr; } } else if (cgType->IsConcrete()) { res = cgType->GetOrCreateTypeInfo(); } else if (cgType->IsStaticGI()) { res = cgType->GetOrCreateTypeInfo(); } else if (baseType->IsTuple()) { auto& tupleType = StaticCast(*baseType); tt = CGType::GetOrCreate(cgMod, baseType)->GetOrCreateTypeTemplate(); genericArgs = tupleType.GetElementTypes(); } else if (baseType->IsFunc()) { tt = CGType::GetOrCreate(cgMod, baseType)->GetOrCreateTypeTemplate(); auto& funcType = StaticCast(*baseType); genericArgs = funcType.GetParamTypes(); genericArgs.insert(genericArgs.begin(), funcType.GetReturnType()); } else if (baseType->IsVArray()) { auto& varrayType = StaticCast(*baseType); tt = CGType::GetOrCreate(cgMod, baseType)->GetOrCreateTypeTemplate(); auto argType = varrayType.GetElementType(); for (int64_t arrayIdx = 0; arrayIdx < varrayType.GetSize(); arrayIdx++) { genericArgs.emplace_back(argType); } } else if (baseType->IsRawArray()) { auto& rawArrayType = StaticCast(*baseType); tt = CGType::GetOrCreate(cgMod, baseType)->GetOrCreateTypeTemplate(); auto argType = rawArrayType.GetElementType(); genericArgs.emplace_back(argType); } else if (baseType->IsCPointer()) { auto& cpointerType = StaticCast(*baseType); tt = CGType::GetOrCreate(cgMod, baseType)->GetOrCreateTypeTemplate(); auto argType = cpointerType.GetElementType(); genericArgs.emplace_back(argType); } else if (baseType->IsBox()) { auto& boxType = StaticCast(*baseType); tt = CGType::GetOrCreate(cgMod, baseType)->GetOrCreateTypeTemplate(); genericArgs.emplace_back(boxType.GetBaseType()); } else { // if not tuple/varray/cpointer/Box: auto customType = StaticCast(baseType); tt = CGType::GetOrCreate(cgMod, customType->GetCustomTypeDef()->GetType())->GetOrCreateTypeTemplate(); if (customType->IsAutoEnvBase()) { if (customType->IsAutoEnvInstBase()) { customType = const_cast(static_cast(customType)) ->GetSuperClassTy(&cgMod.GetCGContext().GetCHIRBuilder()); } CJC_ASSERT(customType->IsAutoEnvGenericBase()); auto args = customType->GetGenericArgs(); std::vector paramTypes(args.begin(), args.end() - 1); genericArgs = {GetCGContext().GetCHIRBuilder().GetType(paramTypes, args.back())}; } else { genericArgs = customType->GetGenericArgs(); } } if (!genericArgs.empty()) { auto curPt = LLVMIRBuilder2::GetInsertPoint(); auto curBB = LLVMIRBuilder2::GetInsertBlock(); if (canChangeBB) { SetInsertPointForPreparingTypeInfo(); } auto typeArgsArr = CreateTypeInfoArray(genericArgs, map); res = CallIntrinsicGetTypeInfo({tt, getInt32(genericArgs.size()), typeArgsArr}); LLVMIRBuilder2::SetInsertPoint(curBB, curPt); } if (llvm::isa(res) && res->getType() == typeInfoPtrType->getPointerTo()) { res = CreateLoad(typeInfoPtrType, res); } (void)cacheMap.emplace(baseType, res); return res; } llvm::Value* IRBuilder2::CreateTypeInfo(const CHIR::Type& type, bool canChangeBB) { return CreateTypeInfo(type, cgFunction ? cgFunction->genericParamsMap : std::unordered_map>(), canChangeBB); } llvm::Value* IRBuilder2::CreateTypeInfo(const CHIR::Type* type, bool canChangeBB) { if (type == nullptr) { // Just a temporary assertion CJC_ASSERT(type && "Meets a nullptr from CHIR."); } return CreateTypeInfo(*type, canChangeBB); } llvm::Value* IRBuilder2::CallInteropIntrinsics( const CHIRIntrinsicWrapper& intrinsic, const std::vector& parameters) { return CallIntrinsic(intrinsic, parameters); } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/CJNative/CJNativeMetadata.h000066400000000000000000000262131510705540100240260ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the generator for metadata. */ #ifndef CANGJIE_METADATAGEN_H #define CANGJIE_METADATAGEN_H #include "CGContext.h" #include "CGModule.h" #include "CJNative/CHIRSplitter.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/StructDef.h" namespace Cangjie { namespace CodeGen { enum MetadataKind : uint8_t { PKG_METADATA = 0, STRUCT_METADATA, CLASS_METADATA, ENUM_METADATA, GV_METADATA, // Global variables GF_METADATA, // Global functions COUNT }; enum GenReflectMode : uint8_t { NO_REFLECT = 0, FULL_REFLECT }; enum class ExtraAttribute { MUTABLE_FIELD, IMMUTABLE_FIELD, INTERFACE, CLASS, STRUCT, ENUM, METHOD, BOX_CLASS, METHOD_FROM_INTERFACE }; enum SRetMode : uint8_t { SRET_NON_GENERIC = 0, // has SRet but retTy is neither 'T' nor 'struct' SRET_GENERIC = 1, // has SRet and retTy is 'T' SRET_KNOWN_GENERIC_CUSTOM = 2, // has SRet and retTy is 'struct' and this struct has no field with generic type SRET_UNKNOWN_GENERIC_CUSTOM = 3, // has SRet and retTy is 'struct' and this struct has fields with generic type NO_SRET = 4 // no SRet }; const std::vector PRIMITIVE_TIS = {"Nothing.ti", "Unit.ti", "Bool.ti", "Rune.ti", "UInt8.ti", "UInt16.ti", "UInt32.ti", "UInt64.ti", "UIntNative.ti", "Int8.ti", "Int16.ti", "Int32.ti", "Int64.ti", "IntNative.ti", "Float16.ti", "Float32.ti", "Float64.ti", "CString.ti"}; const std::vector PRIMITIVE_TTS = { "Tuple.tt", "VArray.tt", "RawArray.tt", "CPointer.tt", "CFunc.tt", "Closure.tt"}; class MetadataVector { public: explicit MetadataVector(llvm::LLVMContext& llvmCtx) : llvmCtx(llvmCtx) { } MetadataVector(const MetadataVector&) = delete; MetadataVector& operator=(const MetadataVector&) = delete; MetadataVector(MetadataVector&&) = delete; MetadataVector& operator=(MetadataVector&&) = delete; MetadataVector& Concat(llvm::Metadata* md) { (void)data.emplace_back(md); return *this; } MetadataVector& Concat(const std::string& rawStr) { (void)data.emplace_back(llvm::MDString::get(llvmCtx, rawStr)); return *this; } MetadataVector& Concat(const size_t num) { (void)data.emplace_back( llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvmCtx), num))); return *this; } MetadataVector& AddSubItem(MetadataVector& mdVec) { (void)data.emplace_back(mdVec.CreateMDTuple()); return *this; } llvm::MDTuple* CreateMDTuple() { return llvm::MDTuple::get(llvmCtx, data); } private: std::vector data{}; llvm::LLVMContext& llvmCtx; }; struct MetadataTypeItem { llvm::MDString* name; llvm::MDString* declaredGenericTi; // {} for enum llvm::MDTuple* instanceFields; // ctors for enum llvm::MDTuple* staticFields; // {} for enum llvm::MDTuple* instanceMethods; llvm::MDTuple* staticMethods; llvm::MDTuple* typeAttrs; explicit MetadataTypeItem(llvm::MDString* name, llvm::MDString* declaredGenericTi, llvm::MDTuple* instanceFields, llvm::MDTuple* staticFields, llvm::MDTuple* instanceMethods, llvm::MDTuple* staticMethods, llvm::MDTuple* typeAttrs) : name(name), declaredGenericTi(declaredGenericTi), instanceFields(instanceFields), staticFields(staticFields), instanceMethods(instanceMethods), staticMethods(staticMethods), typeAttrs(typeAttrs) { } llvm::MDTuple* CreateMDTuple(llvm::LLVMContext& ctx, bool isEnum = false) { if (isEnum) { return llvm::MDTuple::get(ctx, {name, instanceFields, instanceMethods, staticMethods, typeAttrs}); } else { return llvm::MDTuple::get(ctx, {name, declaredGenericTi, instanceFields, staticFields, instanceMethods, staticMethods, typeAttrs}); } } }; class MetadataInfo { public: explicit MetadataInfo(CGModule& cgMod, const SubCHIRPackage& subCHIRPkg, uint8_t reflectionMode) : module(cgMod), subCHIRPkg(subCHIRPkg), reflectionMode(reflectionMode) { } virtual ~MetadataInfo() = default; virtual void Gen() = 0; protected: llvm::MDTuple* GenerateInstanceFieldMetadata(const CHIR::MemberVarInfo& field, size_t idx); llvm::MDTuple* GenerateStaticFieldMetadata(const CHIR::GlobalVarBase& staticField); llvm::MDTuple* GenerateMethodMetadata(const CHIR::FuncBase& method, bool isFromInterface = false); llvm::MDTuple* GenerateAttrsMetadata(const CHIR::AttributeInfo& attrs, ExtraAttribute extraAttr, const std::string& gettingAnnotationMethod, uint8_t hasSRetMode = SRetMode::NO_SRET, const std::string& enumKind = "") const; llvm::MDTuple* GenerateParametersMetadata( const std::vector& argsInfo, bool hasThis = false) const; llvm::MDTuple* GenerateParametersMetadata( const std::vector& paramInfos, bool hasThis = false) const; llvm::MDTuple* GenerateParametersMetadata(const std::vector& genericParamInfos) const; std::string GetTiName(const CHIR::Type& ty) const; CGModule& module; const SubCHIRPackage& subCHIRPkg; uint8_t reflectionMode; }; class StructMetadataInfo : public MetadataInfo { public: StructMetadataInfo(CGModule& cgMod, const SubCHIRPackage& subCHIRPkg, uint8_t reflectionMode) : MetadataInfo(cgMod, subCHIRPkg, reflectionMode) { } void Gen() override { return GenerateAllStructsMetadata(); } private: void GenerateAllStructsMetadata(); void GenerateStructMetadata(const CHIR::StructDef& sd, std::set& doneSet); llvm::MDTuple* GenerateStructFieldMetadata(const CHIR::StructDef& sd); llvm::MDTuple* GenerateStructStaticFieldMetadata(const CHIR::StructDef& sd); void GenerateStructMethodMetadata(const CHIR::StructDef& sd, std::vector& methodsVec, std::vector& staticMethodsVec); }; class ClassMetadataInfo : public MetadataInfo { public: ClassMetadataInfo(CGModule& cgMod, const SubCHIRPackage& subCHIRPkg, uint8_t reflectionMode) : MetadataInfo(cgMod, subCHIRPkg, reflectionMode) { } void Gen() override { return GenerateAllClassesMetadata(); } private: void GenerateAllClassesMetadata(); void GenerateClassLikeMetadata(const CHIR::ClassDef& cd); llvm::MDTuple* GenerateClassLikeFieldMetadata(const CHIR::ClassDef& cd); llvm::MDTuple* GenerateClassLikeStaticFieldMetadata(const CHIR::ClassDef& cd); void GenerateClassLikeMethodMetadata(const CHIR::ClassDef& cd, std::vector& methodsVec, std::vector& staticMethodsVec); llvm::MDTuple* GenerateClassLikeTypeAttrsMetadata(const CHIR::ClassDef& cd) const; llvm::MDNode* GenerateClassAbsMethodMetadata(const CHIR::AbstractMethodInfo& absMethod); }; class EnumMetadataInfo : public MetadataInfo { public: EnumMetadataInfo(CGModule& cgMod, const SubCHIRPackage& subCHIRPkg, uint8_t reflectionMode) : MetadataInfo(cgMod, subCHIRPkg, reflectionMode) { } void Gen() override { return GenerateAllEnumsMetadata(); } private: void GenerateAllEnumsMetadata(); void GenerateEnumMetadata(const CHIR::EnumDef& ed); llvm::MDTuple* GenerateEnumConstructorMetadata(const CHIR::EnumDef& ed); void GenerateEnumMethodMetadata(const CHIR::EnumDef& ed, std::vector& methodsVec, std::vector& staticMethodsVec); std::string GenerateCtorFn(const CHIR::EnumDef& enumDef, size_t index, const std::string& qualifiedName); }; class GVMetadataInfo : public MetadataInfo { public: GVMetadataInfo(CGModule& cgMod, const SubCHIRPackage& subCHIRPkg, uint8_t reflectionMode) : MetadataInfo(cgMod, subCHIRPkg, reflectionMode) { } void Gen() override { return GenerateGlobalVariablesMetadata(); } private: void GenerateGlobalVariablesMetadata(); llvm::MDNode* GenerateVariableMetadata(const CHIR::GlobalVar& variable); }; class GFMetadataInfo : public MetadataInfo { public: GFMetadataInfo(CGModule& cgMod, const SubCHIRPackage& subCHIRPkg, uint8_t reflectionMode) : MetadataInfo(cgMod, subCHIRPkg, reflectionMode) { } void Gen() override { return GenerateAllFunctionsMetadata(); } private: void GenerateAllFunctionsMetadata(); }; class PkgMetadataInfo : public MetadataInfo { public: PkgMetadataInfo(CGModule& cgMod, const SubCHIRPackage& subCHIRPkg, uint8_t reflectionMode) : MetadataInfo(cgMod, subCHIRPkg, reflectionMode) { } void Gen() override { GeneratePkgMetadata(); } private: void GeneratePkgMetadata() const; void AddPrimitiveTypeInfoToCorePkgInfo() const; }; class CGMetadata { public: CGMetadata(CGModule& module, const SubCHIRPackage& subCHIRPkg) : module(module), subCHIRPkg(subCHIRPkg) { auto globalOptions = module.GetCGContext().GetCGPkgContext().GetGlobalOptions(); if (globalOptions.disableReflection) { reflectionMode = GenReflectMode::NO_REFLECT; } else { reflectionMode = GenReflectMode::FULL_REFLECT; } } CGMetadata* Needs(MetadataKind mdKind) { (void)mdInfoFlags.set(static_cast(mdKind)); return this; } void Gen() { for (uint8_t i = 0; i < MetadataKind::COUNT; ++i) { if (mdInfoFlags.test(i)) { mdCtors.at(static_cast(i))()->Gen(); } } } private: std::bitset mdInfoFlags; CGModule& module; const SubCHIRPackage& subCHIRPkg; uint8_t reflectionMode; const std::unordered_map()>> mdCtors = { {MetadataKind::STRUCT_METADATA, [this]() { return std::make_unique(module, subCHIRPkg, reflectionMode); }}, {MetadataKind::CLASS_METADATA, [this]() { return std::make_unique(module, subCHIRPkg, reflectionMode); }}, {MetadataKind::ENUM_METADATA, [this]() { return std::make_unique(module, subCHIRPkg, reflectionMode); }}, {MetadataKind::GV_METADATA, [this]() { return std::make_unique(module, subCHIRPkg, reflectionMode); }}, {MetadataKind::GF_METADATA, [this]() { return std::make_unique(module, subCHIRPkg, reflectionMode); }}, {MetadataKind::PKG_METADATA, [this]() { return std::make_unique(module, subCHIRPkg, reflectionMode); }}, }; }; } // namespace CodeGen } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/CodeGen/CJNative/CJNativeReflectionInfo.h000066400000000000000000000015301510705540100252070ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the generator for llvm reflection info. */ #ifndef CANGJIE_CJNATIVE_REFLECTION_INFO_GEN_H #define CANGJIE_CJNATIVE_REFLECTION_INFO_GEN_H #include "CGModule.h" namespace Cangjie { namespace CodeGen { struct SubCHIRPackage; class CJNativeReflectionInfo { public: CJNativeReflectionInfo(CGModule& cgMod, const SubCHIRPackage& subCHIRPackage) : cgMod(cgMod), subCHIRPkg(subCHIRPackage) { } void Gen() const; private: CGModule& cgMod; const SubCHIRPackage& subCHIRPkg; }; } // namespace CodeGen } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/CodeGen/CJNative/CJNativeTypeCastImpl.cpp000066400000000000000000000012651510705540100252170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Base/TypeCastImpl.h" #include "IRBuilder.h" namespace Cangjie { namespace CodeGen { llvm::Value* GenerateTupleTypeCast(IRBuilder2& irBuilder, const CGValue& srcCGValue, const CGType& targetCGType) { return irBuilder.CreateBitOrPointerCast(srcCGValue.GetRawValue(), targetCGType.GetLLVMType()->getPointerTo(srcCGValue.GetLLVMType()->getPointerAddressSpace())); } } // namespace CodeGen } // namespace Cangjie cangjie_compiler-1.0.7/src/CodeGen/CJNative/EmitPackageIR.cpp000066400000000000000000001212131510705540100236560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "llvm/Analysis/CallGraph.h" #include "llvm/IR/Verifier.h" #include "llvm/Transforms/Utils/Cloning.h" #include "Base/CGTypes/CGType.h" #include "DIBuilder.h" #include "IRAttribute.h" #include "IRBuilder.h" #include "Utils/BlockScopeImpl.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Value.h" #include "cangjie/Utils/ProfileRecorder.h" namespace Cangjie::CodeGen { llvm::Value* GenerateMainRetVal(IRBuilder2& irBuilder, llvm::Value* userMainRetVal); namespace { void CreatePrintStackTraceCall(IRBuilder2& irBuilder, llvm::Value* exceptionValue) { const std::string errorClassName = "_CNat5ErrorE"; const std::string printStackTraceIdent = "printStackTrace"; const std::string printStackTraceFuncName = "_CNat5Error15printStackTraceHv"; auto& cgMod = irBuilder.GetCGModule(); auto& cgCtx = irBuilder.GetCGContext(); auto printStackTraceFuncNode = cgCtx.GetImplicitUsedFunc(printStackTraceFuncName); auto printStackTraceFuncType = static_cast(printStackTraceFuncNode->GetType()); // For now: auto exceptionTi = irBuilder.GetTypeInfoFromObject(exceptionValue); // Get the func ptr of `printStackTrace` // `printStackTrace` is the 3rd virtual method defined in `class Error` auto idxOfError = irBuilder.getInt64(0U); auto idxOfPrintStackTrace = irBuilder.getInt64(2U); auto funcInt8Ptr = irBuilder.CallIntrinsicGetVTableFunc(exceptionTi, idxOfError, idxOfPrintStackTrace); auto meta = llvm::MDTuple::get(cgCtx.GetLLVMContext(), llvm::MDString::get(cgCtx.GetLLVMContext(), "std.core:Error")); funcInt8Ptr->setMetadata("IntroType", meta); auto concreteFuncType = static_cast(CGType::GetOrCreate(cgMod, printStackTraceFuncType)); auto funcPtr = irBuilder.LLVMIRBuilder2::CreateBitCast(funcInt8Ptr, concreteFuncType->GetLLVMFunctionType()->getPointerTo()); auto paramCGType = CGType::GetOrCreate(cgMod, printStackTraceFuncType->GetParamTypes()[0]); CGValue paramCGValue(exceptionValue, paramCGType); irBuilder.CreateCallOrInvoke(*concreteFuncType, funcPtr, {¶mCGValue}); } /* * @brief The IR layout generated by CreateUnwindBlockForTryCatchWrapper is as follows: * ... * invoke xxx_function() * to label %normalDest unwind label %unwindDest * * normalDest: ; preds = %invoke.continue * ... * * unwindDest: ; preds = %invoke.continue * %0 = landingpad token * catch i8* null * %1 = call i8* @llvm.cj.get.exception.wrapper() * %2 = call i8 addrspace(1)* @llvm.cj.begin.catch(i8* %1) * %3 = call i1 @llvm.cj.is.instance.of(i8 addrspace(1)* %2, i8* bitcast (%KlassInfo.0* * @"_CNat16OutOfMemoryErrorE.objKlass" to i8*)) * br i1 %3, label %is.OOM, label %judge.Error * * is.OOM: ; preds = %unwindDest * invoke void @_CNat8eprintlnHRNat6StringE(...) * to label %normalDest2 unwind label %rethrow * * normalDest2: ; preds = %is.OOM * br label %judge.end * * judge.Error: ; preds = %unwindDest * %x = call i1 @llvm.cj.is.instance.of(i8 addrspace(1)* %8, i8* bitcast (%KlassInfo.1* * @_CNat5ErrorE.objKlass to i8*)) * br i1 %x, label %is.Error, label %judge.Exception * * is.Error: ; preds = %judge.Error * %4 = call i8* @llvm.cj.get.virtual.func(i8 addrspace(1)* noalias %2, i64 3) * %5 = bitcast i8* %4 to void (%Unit.Type*, i8 addrspace(1)*)* * invoke void %5(%Unit.Type* noalias sret(%Unit.Type) %X, i8 addrspace(1)* %2) * to label %normalDest3 unwind label %rethrow * * normalDest3: ; preds = %is.Error * br label %judge.end * * judge.Exception: ; preds = %judge.Error * %6 = call i1 @llvm.cj.is.instance.of(i8 addrspace(1)* %2, i8* bitcast (%KlassInfo.0* * @"_CNat9ExceptionE.objKlass" to i8*)) * br i1 %6, label %is.Exception, label %judge.end * * is.Exception: ; preds = %judge.Exception * invoke void @"_CNat15handleExceptionHCNat9ExceptionE"(%Unit.Type* noalias sret(%Unit.Type) %X, i8 * addrspace(1)* %2) * to label %normalDest4 unwind label %rethrow * * normalDest4: ; preds = %is.Exception * br label %judge.end * * judge.end: ; preds = %is.Exception, %judge.Exception, %is.Error, %is.OOM * call void @llvm.cj.end.catch() * call void @"_CNat27CJ_CORE_ExecAtexitCallbacksEv"(%Unit.Type* %X) * ret xxx * * rethrow: ; preds = %judge.end, %is.Exception, %is.Error * %17 = landingpad token * catch i8* null * call void @llvm.cj.end.catch() * call void @"_CNat27CJ_CORE_ExecAtexitCallbacksEv"(%Unit.Type* noalias sret(%Unit.Type) %0) * ret xxx */ void CreateUnwindBlockForTryCatchWrapper( IRBuilder2& irBuilder, const std::function& createRetInst, bool needAtexitCallbacks) { auto& cgMod = irBuilder.GetCGModule(); auto classOutOfMemoryErrorDef = StaticCast( GetTypeDefFromImplicitUsedFuncParam(cgMod, "_CNat16OutOfMemoryError6Hv", 0)); auto classErrorDef = StaticCast( GetTypeDefFromImplicitUsedFuncParam(cgMod, "_CNat5Error15printStackTraceHv", 0)); auto classExceptionDef = StaticCast( GetTypeDefFromImplicitUsedFuncParam(cgMod, "_CNat9Exception15printStackTraceHv", 0)); auto& cgCtx = irBuilder.GetCGContext(); // Generate IR for landPadBb BasicBlock auto landingPad = irBuilder.CreateLandingPad(CGType::GetLandingPadType(cgCtx.GetLLVMContext()), 0); landingPad->addClause(llvm::Constant::getNullValue(irBuilder.getInt8PtrTy())); auto exceptionObjWrapper = irBuilder.CallExceptionIntrinsicGetExceptionValue(); auto exceptionValue = irBuilder.CallPostThrowExceptionIntrinsic(exceptionObjWrapper); // Create basicBlocks for catching Exception and Error, These BBs need to be inserted under the LandingPad. auto [isOOMBb, judgeErrorBb, isErrorBb, judgeExceptionBb, isExceptionBb, judgeEndBb, rethowBb] = Vec2Tuple<7>(irBuilder.CreateAndInsertBasicBlocks( {"is.OOM", "judge.Error", "is.Error", "judge.Exception", "is.Exception", "judge.end", "rethrow"})); // 1. attempt to capture Error or Exception { CodeGenUnwindBlockScope unwindBlockScope(cgMod, rethowBb); // 1.1. attempt to capture OutOfMemoryError auto isOOM = irBuilder.CallClassIntrinsicInstanceOf(exceptionValue, classOutOfMemoryErrorDef->GetType()); (void)irBuilder.CreateCondBr(isOOM, isOOMBb, judgeErrorBb); // 1.2. successfully captured an Error irBuilder.SetInsertPoint(isOOMBb); irBuilder.CreateEPrintlnCall("An exception has occurred:"); irBuilder.CreateEPrintlnCall(" Out of memory"); (void)irBuilder.CreateBr(judgeEndBb); // 1.3. attempt to capture an Error irBuilder.SetInsertPoint(judgeErrorBb); auto isError = irBuilder.CallClassIntrinsicInstanceOf(exceptionValue, classErrorDef->GetType()); (void)irBuilder.CreateCondBr(isError, isErrorBb, judgeExceptionBb); // 1.4. successfully captured an Error irBuilder.SetInsertPoint(isErrorBb); CreatePrintStackTraceCall(irBuilder, exceptionValue); (void)irBuilder.CreateBr(judgeEndBb); // 1.5. attempt to capture an Exception irBuilder.SetInsertPoint(judgeExceptionBb); auto isException = irBuilder.CallClassIntrinsicInstanceOf(exceptionValue, classExceptionDef->GetType()); (void)irBuilder.CreateCondBr(isException, isExceptionBb, judgeEndBb); // 1.6. successfully captured an Exception irBuilder.SetInsertPoint(isExceptionBb); irBuilder.CreateHandleExceptionCall( {irBuilder.CreateEntryAlloca(CGType::GetUnitType(cgCtx.GetLLVMContext())), exceptionValue}); (void)irBuilder.CreateBr(judgeEndBb); } // 2. Capture completed irBuilder.SetInsertPoint(judgeEndBb); if (needAtexitCallbacks) { irBuilder.CreateExecAtExitCallbacksCall(); } createRetInst(); // 3. rethrow irBuilder.SetInsertPoint(rethowBb); auto landingPad1 = irBuilder.CreateLandingPad(CGType::GetLandingPadType(cgCtx.GetLLVMContext()), 0); landingPad1->addClause(llvm::Constant::getNullValue(irBuilder.getInt8PtrTy())); if (needAtexitCallbacks) { irBuilder.CreateExecAtExitCallbacksCall(); } createRetInst(); } /* @brief Create a wrapper of try-catch: * try { * createTryScopeInst() ; try scope instruction * } catch (e: Error) { * e.printStackTrace() * createRetInst() ; the return instructions while an exception occurs * } catch (e: Exception) { * handleException(e) * createRetInst() ; the return instructions while an exception occurs * } */ void CreateTryCatchWrapper(IRBuilder2& irBuilder, const std::function& createTryScopeInst, const std::function& createRetInst, bool needAtexitCallbacks) { auto unwindBlock = irBuilder.CreateAndInsertBasicBlocks({"unwindDest"})[0]; { CodeGenUnwindBlockScope unwindBlockScope(irBuilder.GetCGModule(), unwindBlock); createTryScopeInst(); } irBuilder.SetInsertPoint(unwindBlock); CreateUnwindBlockForTryCatchWrapper(irBuilder, createRetInst, needAtexitCallbacks); } llvm::Function* GenerateEntryFunction(CGModule& cgMod, llvm::Function& userMain) { IRBuilder2 irBuilder(cgMod); auto cjEntryFuncType = llvm::FunctionType::get(irBuilder.getInt32Ty(), {}, false); auto cjEntryFunc = llvm::cast( cgMod.GetLLVMModule()->getOrInsertFunction(CJ_ENTRY_FUNC_NAME, cjEntryFuncType).getCallee()); cjEntryFunc->setPersonalityFn(cgMod.GetExceptionIntrinsicPersonality()); SetGCCangjie(cjEntryFunc); auto entryBB = llvm::BasicBlock::Create(cgMod.GetLLVMContext(), "entry", cjEntryFunc); irBuilder.SetInsertPoint(entryBB); (void)irBuilder.CallIntrinsicFunction( irBuilder.getVoidTy(), PREFIX_OF_BACKEND_SYMS + "CheckThreadLocalDataOffset", {}); auto createTryScopeInst = [&irBuilder, &userMain]() { auto& cgMod = irBuilder.GetCGModule(); auto globalInitFunc = cgMod.GetOrInsertCGFunction(cgMod.GetCGContext().GetCHIRPackage().GetPackageInitFunc()); CJC_ASSERT(globalInitFunc && "Global init function should exist."); (void)irBuilder.CreateCallOrInvoke(globalInitFunc->GetRawFunction(), {}); // Create user.main function call std::vector userMainArgs{}; if (!userMain.arg_empty()) { auto getCommandLineArgsFuncCHIRNode = cgMod.GetCGContext().GetImplicitUsedFunc("_CNat18getCommandLineArgsHv"); auto getCommandLineArgsFuncReturnType = StaticCast(getCommandLineArgsFuncCHIRNode->GetType())->GetReturnType(); auto retCGType = CGType::GetOrCreate(cgMod, getCommandLineArgsFuncReturnType); auto commandLineArgs = irBuilder.CreateEntryAlloca(retCGType->GetLLVMType()); irBuilder.CreateCJMemSetStructWith0(commandLineArgs); irBuilder.CreateGetCommandLineArgsCall({commandLineArgs}); userMainArgs.emplace_back(irBuilder.CreatePointerBitCastOrAddrSpaceCast( commandLineArgs, userMain.args().begin()->getType())); } auto userMainRetVal = irBuilder.CreateCallOrInvoke(&userMain, userMainArgs); irBuilder.CreateExecAtExitCallbacksCall(); (void)irBuilder.CreateRet(GenerateMainRetVal(irBuilder, userMainRetVal)); }; auto createRetInst = [&irBuilder]() { irBuilder.CreateRet(llvm::ConstantInt::get(irBuilder.getInt32Ty(), 1)); }; CreateTryCatchWrapper(irBuilder, createTryScopeInst, createRetInst, true); return cjEntryFunc; } } // namespace llvm::Function* CreateMainFunc(const CGModule& cgMod); void EmitMain(CGModule& cgMod) { if (!cgMod.GetCGContext().GetCompileOptions().CompileExecutable()) { return; } Utils::ProfileRecorder recorder("EmitIR", "EmitMain"); auto chirUserMain = cgMod.GetCGContext().GetCGPkgContext().FindCHIRGlobalValue(USER_MAIN_MANGLED_NAME); CJC_ASSERT(chirUserMain && "Cannt find userMain."); auto userMainFunc = cgMod.GetOrInsertCGFunction(chirUserMain); auto mainFunction = CreateMainFunc(cgMod); auto entryBB = llvm::BasicBlock::Create(cgMod.GetLLVMContext(), "entry", mainFunction); IRBuilder2 irBuilder(cgMod, entryBB); auto& llvmCtx = cgMod.GetLLVMContext(); auto voidType = llvm::Type::getVoidTy(llvmCtx); /// @main func call CjRuntimeInit auto fType = llvm::FunctionType::get(voidType, false); auto f = llvm::cast( cgMod.GetLLVMModule()->getOrInsertFunction(PREFIX_OF_RUNTIME_SYMS + "CjRuntimeInit", fType).getCallee()); (void)irBuilder.CreateCallOrInvoke(fType, f, {}); auto argcValue = mainFunction->args().begin(); auto argvValue = mainFunction->args().begin() + 1; /// @main func call SetCommandLineArgs fType = llvm::FunctionType::get(voidType, {argcValue->getType(), argvValue->getType()}, false); f = llvm::cast( cgMod.GetLLVMModule()->getOrInsertFunction(PREFIX_OF_RUNTIME_SYMS + "SetCommandLineArgs", fType).getCallee()); (void)irBuilder.CreateCallOrInvoke(fType, f, {argcValue, argvValue}); // Create @cj_entry func auto cjEntryFunc = GenerateEntryFunction(cgMod, *userMainFunc->GetRawFunction()); CJC_ASSERT(cjEntryFunc != nullptr); /// @main func call CjRuntimeStart, and it will execute @user.main. fType = llvm::FunctionType::get(voidType, {cjEntryFunc->getType()}, false); f = llvm::cast( cgMod.GetLLVMModule()->getOrInsertFunction(PREFIX_OF_RUNTIME_SYMS + "CjRuntimeStart", fType).getCallee()); (void)irBuilder.CreateCallOrInvoke(fType, f, {cjEntryFunc}); (void)irBuilder.CreateRetVoid(); } llvm::Function* GetGVResetCallBack(CGModule& cgMod, llvm::FunctionType* callBackFuncType) { auto& llvmCtx = cgMod.GetLLVMContext(); auto wrapFuncName = "wrapper.F0uPuE"; auto voidType = llvm::Type::getVoidTy(llvmCtx); auto wrapFuncType = llvm::FunctionType::get( voidType, {callBackFuncType->getPointerTo(), llvm::Type::getInt8Ty(llvmCtx)->getPointerTo()}, false); auto wrapFunc = llvm::cast(cgMod.GetLLVMModule()->getOrInsertFunction(wrapFuncName, wrapFuncType).getCallee()); auto entryBB = llvm::BasicBlock::Create(llvmCtx, "entry", wrapFunc); IRBuilder2 irBuilder(cgMod, entryBB); irBuilder.LLVMIRBuilder2::CreateCall(callBackFuncType, wrapFunc->getArg(0), {wrapFunc->getArg(1)}); (void)irBuilder.CreateRetVoid(); wrapFunc->setLinkage(llvm::GlobalValue::PrivateLinkage); AddFnAttr(wrapFunc, llvm::Attribute::NoInline); AddFnAttr(wrapFunc, llvm::Attribute::get(llvmCtx, CJ2C_ATTR)); return wrapFunc; } void CollectDependentPackages(llvm::Function& func, std::vector& giList) { const std::string CALL_DEPENDENT_PACKAGE_INIT_SUFFIX = SPECIAL_NAME_FOR_INIT_FLAG_RESET_FUNCTION + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX; const std::string CALL_DEPENDENT_PACKAGES_INITS_SUFFIX = SPECIAL_NAME_FOR_IMPORTS_INIT_FUNCTION + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX; for (auto& block : func.getBasicBlockList()) { for (auto& inst : block) { auto ci = llvm::dyn_cast(&inst); if (!ci) { continue; } auto callee = ci->getCalledFunction(); if (!callee) { continue; } auto calleeName = callee->getName(); auto calleeNamePrefixSize = calleeName.str().size() - CALL_DEPENDENT_PACKAGE_INIT_SUFFIX.size(); auto calleePrefix = calleeName.str().substr(0, calleeNamePrefixSize); auto calleeSuffix = calleeName.str().substr(calleeNamePrefixSize); if (calleeSuffix == CALL_DEPENDENT_PACKAGES_INITS_SUFFIX) { CollectDependentPackages(*callee, giList); } else if (calleeName.startswith(MANGLE_CANGJIE_PREFIX + MANGLE_GLOBAL_PACKAGE_INIT_PREFIX)) { giList.emplace_back(calleePrefix + CALL_DEPENDENT_PACKAGE_INIT_SUFFIX); } } } } void CallDependentPackageGlobalResetCall(IRBuilder2 & irBuilder) { auto& cgCtx = irBuilder.GetCGContext(); auto llvmMod = irBuilder.GetLLVMModule(); auto llvmFunc = llvmMod->getFunction(cgCtx.GetCHIRPackage().GetPackageInitFunc()->GetIdentifierWithoutPrefix()); CJC_ASSERT(llvmFunc); std::vector giList; CollectDependentPackages(*llvmFunc, giList); auto giFuncType = llvm::FunctionType::get(irBuilder.getVoidTy(), {}, false); for (auto gi : giList) { auto flagResetFunc = llvm::cast(llvmMod->getOrInsertFunction(gi, giFuncType).getCallee()); flagResetFunc->addFnAttr(llvm::Attribute::NoInline); irBuilder.LLVMIRBuilder2::CreateCall(giFuncType, flagResetFunc); } } llvm::Function* CreatePackageInitFlagResetFunction(CGModule& cgMod) { auto& cgCtx = cgMod.GetCGContext(); auto llvmMod = cgMod.GetLLVMModule(); auto pkgInitFuncName = cgCtx.GetCHIRPackage().GetPackageInitFunc()->GetIdentifierWithoutPrefix(); std::string targetSuffix = SPECIAL_NAME_FOR_INIT_FLAG_RESET_FUNCTION + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX; auto funcName = pkgInitFuncName.substr(0, pkgInitFuncName.size() - targetSuffix.size()) + targetSuffix; if (auto f = llvmMod->getFunction(funcName)) { return f; } auto voidType = llvm::Type::getVoidTy(llvmMod->getContext()); auto functionType = llvm::FunctionType::get(voidType, {}, false); auto func = cgMod.GetOrInsertFunction(funcName, functionType); AddFnAttr(func, llvm::Attribute::NoInline); func->setPersonalityFn(cgMod.GetExceptionIntrinsicPersonality()); AddLinkageTypeMetadata(*func, llvm::GlobalValue::ExternalLinkage, cgCtx.IsCGParallelEnabled()); SetGCCangjie(func); IRBuilder2 irBuilder(cgMod); auto entryBB = irBuilder.CreateEntryBasicBlock(func, "entry"); irBuilder.SetInsertPoint(entryBB); // step 1: call flag reset functions of dependent packages. CallDependentPackageGlobalResetCall(irBuilder); // Step 2: store i1 false, i1* @"has_invoke_xx_global_init$_primitive. if (auto resetFlag = cgMod.GetLLVMModule()->getGlobalVariable("has_invoked_pkg_init_literal", true)) { (void)irBuilder.CreateStore(irBuilder.getFalse(), resetFlag); } // Step 3: store i1 false, i1* @"has_invoke_xx_global_init$. if (auto initFlag = cgMod.GetLLVMModule()->getGlobalVariable(CHIR::GV_PKG_INIT_ONCE_FLAG, true)) { (void)irBuilder.CreateStore(irBuilder.getFalse(), initFlag); } (void)irBuilder.CreateRetVoid(); return func; } void RegisterExceptionRaiser(CGModule& cgMod) { if (cgMod.GetCGContext().GetCurrentPkgName() == CORE_PACKAGE_NAME) { auto& llvmCtx = cgMod.GetLLVMContext(); auto module = cgMod.GetLLVMModule(); auto pkgInitFunc = module->getFunction( cgMod.GetCGContext().GetCHIRPackage().GetPackageInitFunc()->GetIdentifierWithoutPrefix()); auto bi = llvm::cast(pkgInitFunc->getEntryBlock().getTerminator()); CJC_ASSERT(bi != nullptr); auto bb = bi->getSuccessor(1); CJC_ASSERT(bb != nullptr); IRBuilder2 irBuilder(cgMod); irBuilder.SetInsertPoint(bb->getTerminator()); auto int8PtrType = llvm::Type::getInt8PtrTy(llvmCtx); auto chirFunc = cgMod.GetCGContext().GetCGPkgContext().FindCHIRGlobalValue("rt$ThrowImplicitException"); auto func = cgMod.GetOrInsertCGFunction(chirFunc)->GetRawFunction(); auto param = irBuilder.CreateBitCast(func, int8PtrType); // declare void @cj_register_implicit_exception_raisers(i8*) llvm::Function* registerExceptionFunc = llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::cj_register_implicit_exception_raisers); (void)irBuilder.LLVMIRBuilder2::CreateCall(registerExceptionFunc, {param}); } } void CreatePackageInitResetFunction(CGModule& cgMod) { auto llvmMod = cgMod.GetLLVMModule(); std::string pkgInitFuncName = cgMod.GetCGContext().GetCHIRPackage().GetPackageInitFunc()-> GetIdentifierWithoutPrefix(); std::string targetSuffix = SPECIAL_NAME_FOR_INIT_RESET_FUNCTION + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX; std::string funcName = pkgInitFuncName.substr(0, pkgInitFuncName.size() - targetSuffix.size()) + targetSuffix; if (!llvmMod->getFunction(funcName)) { /** * define void @"xx_global_init$_reset"(i8* %param). * struct param { * cFuncType* cFuncPtr; * i8* cFuncParam; * } */ IRBuilder2 irBuilder(cgMod); auto functionType = llvm::FunctionType::get(irBuilder.getVoidTy(), {irBuilder.getInt8PtrTy()}, false); auto func = cgMod.GetOrInsertFunction(funcName, functionType); func->arg_begin()->setName("param"); AddFnAttr(func, llvm::Attribute::NoInline); func->setPersonalityFn(cgMod.GetExceptionIntrinsicPersonality()); AddLinkageTypeMetadata(*func, llvm::GlobalValue::ExternalLinkage, cgMod.GetCGContext().IsCGParallelEnabled()); SetGCCangjie(func); auto entryBB = irBuilder.CreateEntryBasicBlock(func, "entry"); CodeGenFunctionScope funcScope(irBuilder, func); irBuilder.SetInsertPoint(entryBB); auto createTryScopeInst = [&irBuilder, func, &pkgInitFuncName]() { auto& cgMod = irBuilder.GetCGModule(); auto llvmMod = irBuilder.GetLLVMModule(); // Step 1: reset all package init flag to `false` auto flagResetFunc = CreatePackageInitFlagResetFunction(cgMod); (void)irBuilder.CreateCallOrInvoke(flagResetFunc, {}); // Step 2: re-initialize the literal GVs. std::string targetSuffix = SPECIAL_NAME_FOR_INIT_LITERAL_FUNCTION + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX; auto literalInitFunc = llvmMod->getFunction( pkgInitFuncName.substr(0, pkgInitFuncName.size() - targetSuffix.size()) + targetSuffix); if (literalInitFunc) { (void)irBuilder.CreateCallOrInvoke(literalInitFunc, {}); } // Step 3: re-initialize the non-literal GVs. auto gvInitFunc = llvmMod->getFunction(pkgInitFuncName); CJC_NULLPTR_CHECK(gvInitFunc); (void)irBuilder.CreateCallOrInvoke(gvInitFunc, {}); auto cFuncType = llvm::FunctionType::get(irBuilder.getVoidTy(), {irBuilder.getInt8PtrTy()}, false); auto param = llvm::StructType::getTypeByName(llvmMod->getContext(), "struct._param"); if (!param) { param = llvm::StructType::create(llvmMod->getContext(), "struct._param"); } SetStructTypeBody(param, std::vector{cFuncType->getPointerTo(), irBuilder.getInt8PtrTy()}); /// Add a wrapper function for the CFunc (for details see GetIndirectCFuncCallWrapper). auto wrapperFunc = GetGVResetCallBack(cgMod, cFuncType); auto castParam = irBuilder.CreateBitCast(func->arg_begin(), param->getPointerTo()); (void)irBuilder.CreateCallOrInvoke(wrapperFunc, { // 0: the index of cfunc irBuilder.LLVMIRBuilder2::CreateLoad( param->getStructElementType(0), irBuilder.CreateStructGEP(param, castParam, 0)), // 1: the index of cFunc param irBuilder.LLVMIRBuilder2::CreateLoad( param->getStructElementType(1), irBuilder.CreateStructGEP(param, castParam, 1)) }); (void)irBuilder.CreateRetVoid(); }; createTryScopeInst(); } } void CreateLLVMUsedGVs(const CGModule& cgMod) { const auto& llvmUsedGVs = cgMod.GetCGContext().GetLLVMUsedVars(); if (llvmUsedGVs.empty()) { return; } auto i8pTy = llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext()); std::vector gvOrGFs{}; gvOrGFs.reserve(llvmUsedGVs.size()); for (const auto& gvName : llvmUsedGVs) { if (auto gv = cgMod.GetLLVMModule()->getNamedValue(gvName)) { (void)gvOrGFs.emplace_back(llvm::ConstantExpr::getBitCast(gv, i8pTy)); } } auto arrType = llvm::ArrayType::get(i8pTy, gvOrGFs.size()); auto llvmUsedGV = llvm::cast(cgMod.GetLLVMModule()->getOrInsertGlobal("llvm.used", arrType)); llvmUsedGV->setConstant(false); llvmUsedGV->setInitializer(llvm::ConstantArray::get(arrType, gvOrGFs)); llvmUsedGV->setSection("llvm.metadata"); llvmUsedGV->setLinkage(llvm::GlobalValue::AppendingLinkage); } void ReplaceFunction(CGModule& cgMod) { if (!cgMod.GetCGContext().GetCompileOptions().enableCompileDebug) { return; } /// Briefly, we want to rewrite the CallInst from LHS to RHS. // call void @mutF$withoutTI(%thisWithoutTI, %basePtr, %arg1) --> call void @mutF(%thisWithTI, %arg1) // call void @nonMutF$withoutTI(%thisWithoutTI, %arg1) --> call void @nonMutF(%thisWithTI, %arg1) IRBuilder2 irBuilder(cgMod); auto& callBasesToReplace = cgMod.GetCGContext().GetCallBasesToReplace(); for (auto& item : callBasesToReplace) { auto& applyW = item.applyExprW; irBuilder.SetInsertCGFunction(*cgMod.GetOrInsertCGFunction(applyW.GetTopLevelFunc())); auto oldCall = item.callWithoutTI; irBuilder.SetInsertPoint(oldCall); /// step1: prepare new arguments bool hasSRet = oldCall->hasStructRetAttr(); auto thisParamOffset = hasSRet ? 1U : 0U; llvm::Value* thisParamWithoutTI = *(oldCall->arg_begin() + thisParamOffset); std::vector argsVal{}; for (auto& arg : oldCall->args()) { argsVal.emplace_back(&(*arg)); } bool isCalleeMutOrCtor = applyW.IsCalleeStructMutOrCtorMethod(); // drop the `basePtr` parameter for mut function llvm::Value* basePtr = nullptr; if (isCalleeMutOrCtor) { auto basePtrItor = argsVal.begin() + thisParamOffset + 1U; basePtr = *basePtrItor; argsVal.erase(basePtrItor); } /// step2: alloca memory for `this` with TypeInfo CJC_NULLPTR_CHECK(applyW.GetThisParam()); auto thisCHIRType = DeRef(*applyW.GetThisParam()->GetType()); cgMod.GetCGContext().genericParamsCacheMap[irBuilder.GetInsertFunction()].clear(); cgMod.GetCGContext().genericParamsSizeBlockLevelCacheMap[irBuilder.GetInsertBlock()].clear(); auto thisParamTypeInfo = irBuilder.CreateTypeInfo(thisCHIRType); auto size32 = irBuilder.GetLayoutSize_32(*thisCHIRType); auto size64 = irBuilder.CreateSExt(size32, irBuilder.getInt64Ty()); auto thisParamWithTI = irBuilder.CallIntrinsicAllocaGeneric({thisParamTypeInfo, size32}); argsVal[thisParamOffset] = thisParamWithTI; /// step3: store `this` without TypeInfo to the memory allocated by step2 auto payloadPtr = irBuilder.GetPayloadFromObject(thisParamWithTI); if (auto thisType = CGType::GetOrCreate(cgMod, thisCHIRType)->GetLLVMType(); IsTypeContainsRef(thisType)) { if (isCalleeMutOrCtor) { auto load = irBuilder.CreateEntryAlloca(thisType); (void)irBuilder.CreateCJMemSetStructWith0(load); irBuilder.CallGCReadAgg({load, basePtr, thisParamWithoutTI, size64}); irBuilder.CallGCWriteAgg({thisParamWithTI, payloadPtr, load, size64}); } else { irBuilder.CallGCWriteAgg({thisParamWithTI, payloadPtr, thisParamWithoutTI, size64}); } } else { irBuilder.CreateMemCpy(payloadPtr, llvm::MaybeAlign(), thisParamWithoutTI, llvm::MaybeAlign(), size32); } /// step4: emit a new CallInst with "xxx" auto callee = cgMod.GetOrInsertCGFunction(applyW.GetCallee())->GetWrapperFunction(); CJC_NULLPTR_CHECK(callee); llvm::CallBase* newCall = nullptr; if (llvm::isa(oldCall)) { newCall = irBuilder.LLVMIRBuilder2::CreateCall(callee, argsVal); irBuilder.SetInsertPoint(oldCall->getNextNode()); } else if (auto invokeInst = llvm::dyn_cast(oldCall)) { auto normalDest = invokeInst->getNormalDest(); auto unwindDest = invokeInst->getUnwindDest(); newCall = irBuilder.LLVMIRBuilder2::CreateInvoke(callee, normalDest, unwindDest, argsVal); irBuilder.SetInsertPoint(normalDest, normalDest->getFirstInsertionPt()); } if (hasSRet) { auto sretAttr = oldCall->getAttributeAtIndex(llvm::AttributeList::FirstArgIndex, llvm::Attribute::StructRet); newCall->addAttributeAtIndex(llvm::AttributeList::FirstArgIndex, sretAttr); newCall->addAttributeAtIndex(llvm::AttributeList::FirstArgIndex, llvm::Attribute::NoAlias); } oldCall->replaceAllUsesWith(newCall); oldCall->eraseFromParent(); /// step5: find out the BB to update `this` without TypeInfo // Note: non-mut function could skip this step. mut or ctor function should do this step. if (!isCalleeMutOrCtor) { continue; } if (auto thisType = CGType::GetOrCreate(cgMod, thisCHIRType)->GetLLVMType(); IsTypeContainsRef(thisType)) { auto load = irBuilder.CreateEntryAlloca(thisType); (void)irBuilder.CreateCJMemSetStructWith0(load); irBuilder.CallGCReadAgg({load, thisParamWithTI, payloadPtr, size64}); irBuilder.CallGCWriteAgg({basePtr, thisParamWithoutTI, load, size64}); } else { irBuilder.CreateMemCpy(thisParamWithoutTI, llvm::MaybeAlign(), payloadPtr, llvm::MaybeAlign(), size32); } } } void InlineFunction(CGModule& cgMod) { if (!cgMod.GetCGContext().GetCompileOptions().enableCompileDebug) { return; } // Briefly, we want to inline the @mutF$withoutTI(%thisWithoutTI, %basePtr, %arg1) // to the @mutF(%thisWithTI, %arg1). auto findAllocInstOfThisWithoutTI = [](llvm::Function* function) -> llvm::Instruction* { llvm::Instruction* allocInstOfThisWithoutTI = nullptr; auto& entryBB = function->getEntryBlock(); for (auto& inst : entryBB.getInstList()) { if (inst.getOpcode() == llvm::Instruction::Alloca && inst.getName().equals("this.debug.i")) { allocInstOfThisWithoutTI = &inst; } } CJC_ASSERT(allocInstOfThisWithoutTI && "Alloc inst not found."); return allocInstOfThisWithoutTI; }; IRBuilder2 irBuilder(cgMod); llvm::InlineFunctionInfo fnInfo; auto& callBasesToInline = cgMod.GetCGContext().GetCallBasesToInline(); for (auto item : callBasesToInline) { auto callBase = item.first; auto retInst = item.second; // step1: get information of callee auto callee = callBase->getCalledFunction(); CJC_NULLPTR_CHECK(callee); auto sp = callee->getSubprogram(); // step2: inline the call auto parentFunction = callBase->getFunction(); auto inlineRes = llvm::InlineFunction(*callBase, fnInfo, nullptr, false); if (!inlineRes.isSuccess()) { Errorln(inlineRes.getFailureReason()); CJC_ASSERT(false && "Can't be inlined."); } auto funcNameMD = llvm::MDString::get(cgMod.GetLLVMContext(), parentFunction->getName()); sp->replaceRawLinkageName(funcNameMD); parentFunction->setSubprogram(sp); auto debugLocOfRetExpr = cgMod.GetCGContext().GetDebugLocOfRetExpr(callee); if (debugLocOfRetExpr.size() == 1) { auto beginPos = debugLocOfRetExpr.begin()->GetBeginPos(); retInst->setDebugLoc(cgMod.diBuilder->CreateDILoc(sp, beginPos)); } callee->setSubprogram(nullptr); // step3: update the dbgDeclare of 'this' llvm::Value* thisWithTI = nullptr; for (auto& arg : parentFunction->args()) { if (arg.getName().equals("this.withTI")) { thisWithTI = &arg; break; } } irBuilder.SetInsertPoint(parentFunction->getEntryBlock().getTerminator()); CJC_NULLPTR_CHECK(thisWithTI); auto debugThis = irBuilder.CreateEntryAlloca(thisWithTI->getType(), nullptr, thisWithTI->getName() + ".debug"); (void)irBuilder.CreateStore(thisWithTI, debugThis); auto thisWithoutTI = findAllocInstOfThisWithoutTI(parentFunction); auto thisParamMetadata = llvm::ValueAsMetadata::getIfExists(thisWithoutTI); CJC_NULLPTR_CHECK(thisParamMetadata); llvm::cast(thisParamMetadata) ->replaceAllUsesWith(llvm::ValueAsMetadata::get(debugThis)); } } namespace { llvm::Constant* GetReadOnlyArrayKlassInfo(const CGModule& cgMod) { auto module = cgMod.GetLLVMModule(); static const std::string name("RawArray.ti"); if (auto gv = module->getGlobalVariable(name, true); gv) { return gv; } auto& ctx = cgMod.GetLLVMContext(); auto& cgCtx = cgMod.GetCGContext(); // Generate Type ArrayLayout.UInt8 auto classInfoType = CGType::GetOrCreateTypeInfoType(ctx); auto gv = llvm::cast(module->getOrInsertGlobal(name, classInfoType)); CJC_NULLPTR_CHECK(gv); std::vector klassConstants = cgMod.InitRawArrayUInt8Constants(); gv->setInitializer(llvm::ConstantStruct::get(llvm::cast(classInfoType), klassConstants)); gv->addAttribute(GC_KLASS_ATTR); AddLinkageTypeMetadata(*gv, llvm::GlobalValue::InternalLinkage, false); // Generate ArrayLayout.UInt8 here to ensure it is created. auto layoutType = CGArrayType::GenerateArrayLayoutTypeInfo(cgCtx, ARRAY_LAYOUT_PREFIX + "UInt8", llvm::Type::getInt8Ty(ctx)); auto meta = llvm::MDTuple::get(ctx, {llvm::MDString::get(ctx, layoutType->getStructName())}); gv->setMetadata(GC_TYPE_META_NAME, meta); return gv; } } // namespace void InitializeCjStringLiteral(const CGModule& cgMod) { auto& ctx = cgMod.GetLLVMContext(); auto int8PtrType = llvm::Type::getInt8PtrTy(ctx); auto int32Type = llvm::Type::getInt32Ty(ctx); auto int64Type = llvm::Type::getInt64Ty(ctx); auto& cjStrings = cgMod.GetCGContext().GetCJStrings(); if (cjStrings.empty()) { return; } // 1. Traverse all cj string literals' data, concatenate them as a big string named rawData std::set cjStringContents{}; // 1.1 Sort and deduplicate cjStringContents to obtain the minimum rawData for (auto iter = cjStrings.begin(); iter != cjStrings.end(); ++iter) { auto& cjStringName = iter->first; auto& cjStringContent = iter->second; if (cgMod.GetLLVMModule()->getNamedValue(cjStringName)) { cjStringContents.emplace(cjStringContent); } else { // cjString may be codeGen built-in data and optimized. } } // 1.1 Smaller rawData can be obtained by reverse order std::string rawData = ""; for (auto iter = cjStringContents.crbegin(); iter != cjStringContents.crend(); ++iter) { auto& cjStringContent = *iter; if (rawData.find(cjStringContent) == std::string::npos) { rawData.append(cjStringContent); } } // 2. Construct RawArray which stores all cj string literal data auto strContent = llvm::ConstantDataArray::getString(ctx, rawData, false); // RawArray's memory layout is struct {i8* ArrayKlassInfo, i64 length, [? x i8] rawdata} std::vector elemTypes{int8PtrType, int64Type, strContent->getType()}; auto rawArrayType = llvm::StructType::get(ctx, elemTypes); auto arrayKlassInfo = GetReadOnlyArrayKlassInfo(cgMod); auto rawArrayData = llvm::ConstantStruct::get(rawArrayType, {llvm::ConstantExpr::getBitCast(arrayKlassInfo, int8PtrType), llvm::ConstantInt::getSigned(int64Type, static_cast(rawData.size())), strContent}); std::string cjStringDataName = GetCjStringDataLiteralName(rawData); auto gvRawData = llvm::cast(cgMod.GetLLVMModule()->getOrInsertGlobal(cjStringDataName, rawArrayType)); CJC_NULLPTR_CHECK(gvRawData); gvRawData->setInitializer(rawArrayData); gvRawData->setConstant(true); gvRawData->addAttribute(CJSTRING_DATA_ATTR); gvRawData->setLinkage(llvm::GlobalValue::PrivateLinkage); // 3. Set Initializer for all cj string literal auto cjStringType = cgMod.GetCGContext().GetCjStringType(); auto i8Ptr = llvm::ConstantExpr::getBitCast(gvRawData, int8PtrType); auto i8PtrAddr1 = llvm::ConstantExpr::getAddrSpaceCast(i8Ptr, llvm::Type::getInt8PtrTy(ctx, 1u)); for (const auto& cjStringIter : cjStrings) { auto& cjStringName = cjStringIter.first; auto& cjStringContent = cjStringIter.second; size_t pos = rawData.find(cjStringContent); if (!cgMod.GetLLVMModule()->getNamedValue(cjStringName) || pos == std::string::npos) { // cjString may have been optimized and deleted. continue; } auto dataStart = llvm::ConstantInt::getSigned(int32Type, static_cast(pos)); auto strSize = llvm::ConstantInt::getSigned(int32Type, static_cast(cjStringContent.size())); // in LLVM IR: // cj string = type { i8 addrspace(1)*, i32, i32 } auto cjStringInit = llvm::ConstantStruct::get(cjStringType, {i8PtrAddr1, dataStart, strSize}); auto gv = llvm::cast(cgMod.GetLLVMModule()->getNamedValue(cjStringName)); CJC_NULLPTR_CHECK(gv); gv->setInitializer(cjStringInit); } } namespace { void TraverseAndGenerateCallSetFrom(std::set& ret, llvm::CallGraphNode* root) { std::queue queue; queue.emplace(root); do { auto node = queue.front(); queue.pop(); if (ret.find(node) != ret.end()) { continue; } ret.emplace(node); for (auto callee = node->begin(); callee != node->end(); ++callee) { auto calleeNode = callee->second; if (ret.find(calleeNode) == ret.end()) { queue.emplace(calleeNode); } } } while (!queue.empty()); } } // namespace void GenerateBinarySectionInfo(const CGModule& cgMod) { if (cgMod.GetCGContext().GetCGPkgContext().IsCGParallelEnabled()) { return; } auto module = cgMod.GetLLVMModule(); const std::string cjInitAttrStr = "cjinit"; auto callGraph = llvm::CallGraphWrapperPass(); callGraph.runOnModule(*module); auto globalInitFuncName = cgMod.GetCGContext().GetCHIRPackage().GetPackageInitFunc()->GetIdentifierWithoutPrefix(); auto pkgInitFunc = module->getFunction(globalInitFuncName); CJC_NULLPTR_CHECK(pkgInitFunc); auto pkgInitNode = callGraph[pkgInitFunc]; std::set callSet; TraverseAndGenerateCallSetFrom(callSet, pkgInitNode); if (pkgInitFunc->getSection().empty()) { AddFnAttr(pkgInitFunc, llvm::Attribute::get(pkgInitFunc->getContext(), cjInitAttrStr)); } // Add `cjinit` attr to `cj_entry` and `main` std::vector mainFuncs{CJ_ENTRY_FUNC_NAME, "main"}; for (auto& f : mainFuncs) { if (auto func = module->getFunction(f); func && func->getSection().empty()) { func->addFnAttr(llvm::Attribute::get(func->getContext(), cjInitAttrStr)); } } // Add `cjinit` attr to `user.main` and functions that are directly called by `user.main` if (auto userMainFunc = module->getFunction(USER_MAIN_MANGLED_NAME); userMainFunc && userMainFunc->getSection().empty()) { userMainFunc->addFnAttr(llvm::Attribute::get(userMainFunc->getContext(), cjInitAttrStr)); auto userMainNode = callGraph[userMainFunc]; for (auto callee = userMainNode->begin(); callee != userMainNode->end(); ++callee) { if (auto calleeFunc = callee->second->getFunction(); calleeFunc && calleeFunc->getSection().empty()) { calleeFunc->addFnAttr(llvm::Attribute::get(calleeFunc->getContext(), cjInitAttrStr)); } } } for (auto node : callSet) { auto func = node->getFunction(); if (!func) { // The callee func is not defined in this module, so it should be ignored. continue; } if (func->getSection().empty()) { func->addFnAttr(llvm::Attribute::get(func->getContext(), cjInitAttrStr)); } } } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/CMakeLists.txt000066400000000000000000000020041510705540100216360ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB CodeGenBase *.cpp Base/*.cpp Base/CGTypes/*.cpp Base/ExprDispatcher/*.cpp Utils/*.cpp ) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) file(GLOB CodeGenCJNative CJNative/*.cpp CJNative/CGTypes/*.cpp) list(REMOVE_ITEM CodeGenCJNative ${CMAKE_CURRENT_SOURCE_DIR}/CJNative/CJNativeGenArray.cpp) set(CODEGEN_SRC ${CodeGenBase} ${CodeGenCJNative}) add_subdirectory(IncrementalGen) endif() add_library(CangjieCodeGen OBJECT ${CODEGEN_SRC}) if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_dependencies(CangjieCodeGen cjnative) endif() target_include_directories(CangjieCodeGen PRIVATE ${LLVM_INCLUDE_DIRS}) target_compile_options(CangjieCodeGen PRIVATE ${CJC_WITH_LLVM_EXTRA_WARNINGS}) cangjie_compiler-1.0.7/src/CodeGen/DIBuilder.cpp000066400000000000000000002152471510705540100214240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "DIBuilder.h" #include "Base/CGTypes/CGType.h" #include "Utils/CGUtils.h" #include "cangjie/Basic/SourceManager.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/Utils/CheckUtils.h" namespace Cangjie { namespace CodeGen { DIBuilder::DIBuilder(CGModule& cgMod) : LLVMDIBuilder(*cgMod.GetLLVMModule()), cgMod(cgMod) { enabled = cgMod.GetCGContext().GetCompileOptions().enableCompileDebug || cgMod.GetCGContext().GetCompileOptions().enableCoverage; enableLineInfo = cgMod.GetCGContext().GetCompileOptions().displayLineInfo; } namespace { std::string RemovePathPrefix(const std::string& absolutePath, const std::vector& removedPathPrefix) { // for the path: aa/bb/cc.cj, the absolutePath is "aa/bb", and the last "/" is need to be added. #ifdef _WIN32 char split = '\\'; std::string res = absolutePath + split; std::string tempRes{}; tempRes.reserve(res.size()); std::transform(res.begin(), res.end(), std::back_inserter(tempRes), &tolower); for (auto prefix : removedPathPrefix) { std::transform(prefix.begin(), prefix.end(), prefix.begin(), &tolower); auto pos = tempRes.find(prefix); #else char split = '/'; std::string res = absolutePath + split; for (auto& prefix : removedPathPrefix) { auto pos = res.find(prefix); #endif // when pos is 0, it means the path starts with the prefix. if (pos == 0) { res = res.substr(prefix.length()); if (res.empty()) { return res; } res = res[0] == split ? res.substr(1) : res; break; } } // remove the last symbol. return res.empty() ? res : res.substr(0, res.size() - 1); } std::string GenerateLinkageName(const CHIR::GlobalVar& variable) { std::string linkageName = variable.GetPackageName() + "::"; if (variable.TestAttr(CHIR::Attribute::STATIC)) { auto typeIdentifier = variable.GetParentCustomTypeDef()->GetSrcCodeIdentifier(); linkageName += typeIdentifier; if (!variable.GetParentCustomTypeDef()->GetGenericTypeParams().empty()) { linkageName += "<"; for (auto genericTy : variable.GetParentCustomTypeDef()->GetGenericTypeParams()) { linkageName += genericTy->GetSrcCodeIdentifier() + ","; } linkageName.pop_back(); linkageName += ">"; } linkageName += "::"; } return linkageName += variable.GetSrcCodeIdentifier(); } const CHIR::Type* GetOptionLikeArgTy(const CHIR::EnumType& enumTy, CGModule& cgMod) { auto cgType = StaticCast(CGType::GetOrCreate(cgMod, &enumTy)); size_t index = cgType->IsAntiOptionLike() ? 1 : 0; auto someCtor = enumTy.GetConstructorInfos(cgMod.GetCGContext().GetCHIRBuilder())[index]; auto argTy = DeRef(*someCtor.funcType->GetParamTypes()[0]); return argTy; } bool IsBoxSelf(const CHIR::Type& ty, CGModule& cgMod) { if (!ty.IsBox()) { return false; } auto boxedTy = StaticCast(&ty); auto baseTy = boxedTy->GetBaseType(); auto cgType = CGType::GetOrCreate(cgMod, baseTy); if (baseTy->IsEnum() && StaticCast(cgType)->IsOptionLike()) { auto enumTy = StaticCast(baseTy); auto argTy = DeRef(*GetOptionLikeArgTy(*enumTy, cgMod)); return argTy == baseTy || argTy == boxedTy; } return false; } bool IsParaInitFunc(const CHIR::Value& chirFunc) { auto chirFuncName = chirFunc.GetIdentifierWithoutPrefix(); return chirFuncName.find(MANGLE_CANGJIE_PREFIX + MANGLE_FUNC_PARA_INIT_PREFIX) == 0; } } // namespace void DIBuilder::CreateCompileUnit(const std::string& pkgName) { std::string tempPkgName(pkgName); if (cgMod.GetCGContext().GetCompileOptions().enableCoverage) { // When compiled with '--test', files with the "_test.cj" suffix will also be compiled, which means the output // of '.gcno' file for the same package is affected by the '--test' option. Therefore, when compiled with the // '--test' option, the "$test" suffix is added to the corresponding '.gcno' file. // NB: in the "test-only" mode, packages are already suffixed with "$test". if (cgMod.GetCGContext().GetCompileOptions().enableCompileTest && !cgMod.GetCGContext().GetCompileOptions().compileTestsOnly) { tempPkgName += SourceManager::testPkgSuffix; } // When compiled with the `--coverage` option, a corner case should be handled. // If the package name contains a "." symbol, e.g., "some.package.name", // a gcov note file will be generated // whose path is constructed by replacing the extension of the package name with the ".gcno", // i.e., "some.package.gcno". // However, since the ".name" is not a part of the extension, an incorrect file is generated here. // To fix this bug, we add an extra extension ".pkg" to the package name intentionally; // thus, a gcov file will always be created by replacing ".pkg" with ".gcno", i.e., "some.package.name.gcno". if (pkgName.find('.') != std::string::npos) { tempPkgName += ".pkg"; } } defaultFile = createFile(pkgName, "."); diCompileUnit = createCompileUnit(static_cast(llvm::dwarf::DW_LANG_C_plus_plus_14), createFile(tempPkgName, "."), "Cangjie Compiler", false, "", 0u); } void DIBuilder::CreateNameSpace(const std::string& pkgName) { diNamespace = createNameSpace(diCompileUnit, pkgName, false); } void DIBuilder::Finalize() { if (!enabled && !enableLineInfo) { return; } finalize(); lexicalBlocks.clear(); } void DIBuilder::CreateGlobalVar(const CHIR::GlobalVar& variable) { if (!cgMod.GetCGContext().GetCompileOptions().enableCompileDebug || variable.TestAttr(CHIR::Attribute::NO_DEBUG_INFO)) { return; } CJC_ASSERT(!variable.TestAttr(CHIR::Attribute::IMPORTED)); auto& position = variable.GetDebugLocation(); auto ty = DeRef(*variable.GetType()); llvm::DIFile* file = GetOrCreateFile(position); bool isReadOnly = variable.TestAttr(CHIR::Attribute::READONLY); llvm::DIType* type = GetOrCreateType(*ty, isReadOnly); auto llvmValue = llvm::cast(cgMod.GetMappedCGValue(&variable)->GetRawValue()); std::vector expr; // To add the deref flag, need to guarantee the refType is not nullptr, which corresponds to Option.None. if (IsReferenceType(*ty, cgMod)) { if (IsOptionOrOptionLike(*ty)) { type = CreatePointerType(type); } else { expr.emplace_back(llvm::dwarf::DW_OP_deref); } } auto linkageName = GenerateLinkageName(variable); bool isMemberVar = variable.GetParentCustomTypeDef() != nullptr; llvm::DIGlobalVariableExpression* globalVar = createGlobalVariableExpression(diNamespace, variable.GetSrcCodeIdentifier(), linkageName, file, position.GetBeginPos().line, type, isMemberVar, true, createExpression(expr), nullptr); llvmValue->addDebugInfo(globalVar); cgMod.GetCGContext().AddGlobalsOfCompileUnit(variable.GetIdentifierWithoutPrefix()); } void DIBuilder::CreateAnonymousTypeForGenericType(llvm::GlobalVariable* typeTemplate, const CHIR::Type& ty) { // class/struct genericDef generated by init. bool isInterface = ty.IsClass() && StaticCast(&ty)->GetClassDef()->IsInterface(); if (!cgMod.GetCGContext().GetCompileOptions().enableCompileDebug) { return; } if (!ty.IsEnum() && !isInterface) { return; } ++genericID; llvm::DIType* type = GetOrCreateType(ty); llvm::DIGlobalVariableExpression* globalVar = createGlobalVariableExpression( diNamespace, "$" + std::to_string(genericID), "$" + std::to_string(genericID), defaultFile, 0, type, false); typeTemplate->addDebugInfo(globalVar); } llvm::DISubroutineType* DIBuilder::CreateDefaultFunctionType() { return createSubroutineType(getOrCreateTypeArray({})); } #ifndef __APPLE__ static const CHIR::Type* GetOuterTypeOfMemberFunc(const CHIR::Func& func) { const auto parent = func.GetParentCustomTypeDef(); CJC_NULLPTR_CHECK(parent); if (parent->GetCustomKind() != CHIR::CustomDefKind::TYPE_EXTEND) { return parent->GetType(); } const auto extendType = StaticCast(parent)->GetExtendedType(); CJC_NULLPTR_CHECK(extendType); return extendType; } #endif void DIBuilder::SetSubprogram(const CHIR::Func* func, llvm::Function* function) { if (!enabled && !enableLineInfo) { return; } CJC_ASSERT(function->getSubprogram() == nullptr); // reset the parameter number when generating a brand new one. paramNo = 1; auto funcTy = DeRef(*func->GetType()); bool lineInfoOnly = !enabled && enableLineInfo; auto funcName = lineInfoOnly ? "" : func->GetIdentifierWithoutPrefix(); // the name of parameter init function is useless for debug, and may cause duplicate identifier error auto funcIdentifier = lineInfoOnly || IsParaInitFunc(*func) ? "" : GenerateGenericFuncName(func->GetSrcCodeIdentifier(), func->GetOriginalGenericTypeParams()); auto& position = func->GetDebugLocation(); auto diFile = GetOrCreateFile(position); bool isGV = funcName.find(MANGLE_CANGJIE_PREFIX + MANGLE_GLOBAL_VARIABLE_INIT_PREFIX) == 0; bool isCompilerAddInit = func->GetSrcCodeIdentifier() == "init" && func->TestAttr(CHIR::Attribute::NO_DEBUG_INFO); auto scopeLine = isGV ? 0 : position.GetBeginPos().line; scopeLine = isCompilerAddInit ? position.GetEndPos().line : scopeLine; auto scope = llvm::cast(diNamespace); llvm::DISubprogram* decl = nullptr; #ifndef __APPLE__ bool isMemberFunc = func->GetParentCustomTypeDef() != nullptr; if (isMemberFunc && cgMod.GetCGContext().GetCompileOptions().enableCompileDebug) { auto parentTy = GetOuterTypeOfMemberFunc(*func); bool isWrapperFunc = parentTy->IsClass() && StaticCast(parentTy)->IsAutoEnv(); if (!parentTy->IsPrimitive() && !isWrapperFunc) { scope = GetOrCreateType(*parentTy); auto realScope = scope->getTag() == llvm::dwarf::DW_TAG_typedef ? llvm::cast(scope)->getBaseType() : scope; auto compositeType = llvm::cast(realScope); for (auto element : compositeType->getElements()) { if (element->getTag() != llvm::dwarf::DW_TAG_subprogram) { continue; } auto memberSP = llvm::cast(element); if (memberSP->getLinkageName() == funcName) { decl = memberSP; break; } } } } #endif auto funcType = lineInfoOnly ? CreateDefaultFunctionType() : CreateFuncType(StaticCast(funcTy)); llvm::DINode::DIFlags flags = llvm::DINode::FlagPrototyped; flags |= isGV ? llvm::DINode::FlagArtificial : llvm::DINode::FlagZero; llvm::DISubprogram::DISPFlags spFlags = llvm::DISubprogram::SPFlagDefinition; if (func->GetSrcCodeIdentifier() == INST_VIRTUAL_FUNC || func->GetSrcCodeIdentifier() == GENERIC_VIRTUAL_FUNC) { // To properly step in to the closure function body, need to set a valid value for scopeLine. scopeLine = 1; } llvm::DISubprogram* sp = createFunction( scope, funcIdentifier, funcName, diFile, scopeLine, funcType, scopeLine, flags, spFlags, nullptr, decl); function->setSubprogram(sp); curScope = sp; } void DIBuilder::FinalizeSubProgram(llvm::Function& function) { lexicalBlocks.clear(); curLocation = {0, 0}; curScope = nullptr; finalizeSubprogram(function.getSubprogram()); if (!needSubprogram) { function.setSubprogram(nullptr); } needSubprogram = false; } void DIBuilder::EmitDeclare(const CHIR::Debug& debugNode, llvm::BasicBlock& curBB, bool pointerWrapper) { CJC_ASSERT(cgMod.GetCGContext().GetCompileOptions().enableCompileDebug); if (curBB.getParent()->getSubprogram() == nullptr) { return; } if (debugNode.GetValue()->IsParameter()) { CreateParameter(debugNode, curBB, pointerWrapper); return; } auto identifier = debugNode.GetSrcCodeIdentifier(); auto position = debugNode.GetDebugLocation(); auto scope = GetOrCreateScope(position, curBB); auto diFile = GetOrCreateFile(position); auto ty = DeRef(*debugNode.GetValue()->GetType()); bool isReadOnly = debugNode.GetValue()->TestAttr(CHIR::Attribute::READONLY); auto type = GetOrCreateType(*ty, isReadOnly); std::vector expr; auto flags = llvm::DINode::FlagZero; // To add the deref flag, need to guarantee the refType is not nullptr, which corresponds to Option.None. auto hasSize = CGType::GetOrCreate(cgMod, ty)->GetSize(); if (IsReferenceType(*ty, cgMod) || ty->IsGeneric() || !hasSize) { // for the generic option(does not have size), does not need wrap a pointer, because it has typeInfo. if (IsOptionOrOptionLike(*ty) && hasSize) { type = CreatePointerType(type); } else { flags |= llvm::DINode::FlagObjectPointer; expr.emplace_back(llvm::dwarf::DW_OP_deref); } } else if (pointerWrapper) { expr.emplace_back(llvm::dwarf::DW_OP_deref); } auto llvmValue = **(cgMod | debugNode.GetValue()); auto localVariable = createAutoVariable(scope, identifier, diFile, position.GetBeginPos().line, type, true, flags, 0u); insertDeclare(llvmValue, localVariable, createExpression(expr), llvm::DILocation::get( cgMod.GetLLVMContext(), position.GetBeginPos().line, position.GetBeginPos().column, scope), &curBB); } void DIBuilder::EmitUnBoxDeclare(const CHIR::Expression& exprNode, const CHIR::Type& ty, const std::string& name, llvm::Value* value, llvm::BasicBlock& curBB) { auto position = exprNode.GetDebugLocation(); auto scope = GetOrCreateScope(position, curBB); auto diFile = GetOrCreateFile(position); auto type = GetOrCreateType(ty); // the value is a pointer. auto flags = llvm::DINode::FlagObjectPointer; std::vector expr = {llvm::dwarf::DW_OP_deref}; if (IsReferenceType(ty, cgMod)) { type = CreatePointerType(type); // this case for optionLikeRef. if (!ty.IsEnum()) { expr.emplace_back(llvm::dwarf::DW_OP_deref); } } auto localVariable = createAutoVariable(scope, name, diFile, position.GetBeginPos().line, type, true, flags, 0u); insertDeclare(value, localVariable, createExpression(expr), llvm::DILocation::get( cgMod.GetLLVMContext(), position.GetBeginPos().line, position.GetBeginPos().column, scope), &curBB); } void DIBuilder::EmitGenericDeclare( const CHIR::GenericType& genericTy, llvm::Value* arg, llvm::BasicBlock& curBB, size_t index) { auto scope = curBB.getParent()->getSubprogram(); auto name = "$G_" + std::to_string(index); auto type = createTypedef(CreateRefType(), genericTy.GetSrcCodeIdentifier(), defaultFile, 0, diCompileUnit); auto localVariable = createAutoVariable(scope, name, defaultFile, 0u, type, true, llvm::DINode::FlagArtificial); insertDeclare( arg, localVariable, createExpression({}), llvm::DILocation::get(cgMod.GetLLVMContext(), 0u, 0u, scope), &curBB); } void DIBuilder::CreateParameter(const CHIR::Debug& debugNode, llvm::BasicBlock& curBB, bool pointerWrapper) { bool isEnv = debugNode.GetSrcCodeIdentifier() == "$env"; auto paraTy = DeRef(*debugNode.GetValue()->GetType()); // skip DebugNode generation when env is empty. if (isEnv && IsCoreObject(*StaticCast(paraTy)->GetClassDef())) { return; } CJC_NULLPTR_CHECK(curBB.getParent()); auto scope = curBB.getParent()->getSubprogram(); std::vector expr; auto& position = debugNode.GetDebugLocation(); auto file = GetOrCreateFile(position); auto type = GetOrCreateType(*paraTy, true); auto name = debugNode.GetSrcCodeIdentifier(); auto llvmValue = **(cgMod | debugNode.GetValue()); if (name == "this") { bool isTrivialEnum = paraTy->IsEnum() && StaticCast(paraTy)->GetEnumDef()->IsAllCtorsTrivial(); bool isSizeableGenericStruct = paraTy->IsStruct() && CGType::GetOrCreate(cgMod, paraTy)->GetSize() && paraTy->IsGenericRelated(); if ((!isTrivialEnum && !isSizeableGenericStruct) || pointerWrapper) { expr.emplace_back(llvm::dwarf::DW_OP_deref); } auto flag = llvm::DINode::FlagObjectPointer | llvm::DINode::FlagArtificial; auto localVariable = createParameterVariable(scope, name, 1, file, 0u, type, true, flag); insertDeclare(llvmValue, localVariable, createExpression(expr), llvm::DILocation::get(cgMod.GetLLVMContext(), 0u, 0u, scope), &curBB); } else { bool isRefEnum = paraTy->IsEnum() && !StaticCast(paraTy)->GetEnumDef()->IsAllCtorsTrivial() && !IsReferenceType(*paraTy, cgMod); // To add the deref flag, need to guarantee the refType is not nullptr, which corresponds to Option.None. bool isTrivialEnum = paraTy->IsEnum() && StaticCast(paraTy)->GetEnumDef()->IsAllCtorsTrivial(); bool isSizeableGenericStruct = paraTy->IsStruct() && CGType::GetOrCreate(cgMod, paraTy)->GetSize() && paraTy->IsGenericRelated(); bool isPointerType = IsReferenceType(*paraTy, cgMod) || paraTy->IsStruct() || isRefEnum || paraTy->IsVArray() || paraTy->IsTuple() || paraTy->IsGeneric(); if (!isTrivialEnum && !isSizeableGenericStruct && isPointerType) { auto cgType = CGType::GetOrCreate(cgMod, paraTy); bool isRefOption = paraTy->IsEnum() && (cgType->IsOptionLikeRef() || StaticCast(cgType)->IsOptionLikeT()); if (isRefOption || isEnv) { type = CreatePointerType(type); } else { expr.emplace_back(llvm::dwarf::DW_OP_deref); } } else if (pointerWrapper) { expr.emplace_back(llvm::dwarf::DW_OP_deref); } auto flag = llvm::DINode::FlagZero; name = isEnv ? "$CapturedVars" : name; llvm::DILocalVariable* localVariable = createParameterVariable(scope, name, paramNo, file, scope->getLine(), type, true, flag); insertDeclare(llvmValue, localVariable, createExpression(expr), llvm::DILocation::get( cgMod.GetLLVMContext(), position.GetBeginPos().line, position.GetBeginPos().column, scope), &curBB); } ++paramNo; } llvm::DILocation* DIBuilder::HandleDefaultParamLocation(const CHIRExprWrapper& chirNode, llvm::DIScope* scope) { auto& position = chirNode.GetDebugLocation(); bool isApply = chirNode.GetExprKind() == CHIR::ExprKind::APPLY || chirNode.GetExprKind() == CHIR::ExprKind::APPLY_WITH_EXCEPTION; if (!position.GetBeginPos().IsZero() && isApply) { curLocation = Position(position.GetBeginPos().line, position.GetBeginPos().column); } return llvm::DILocation::get(cgMod.GetLLVMContext(), static_cast(curLocation.line), static_cast(curLocation.column), scope); } llvm::DILocation* DIBuilder::CreateDILoc(const CHIRExprWrapper& chirNode) { CJC_ASSERT(enabled || enableLineInfo); llvm::BasicBlock* currentBB = cgMod.GetMappedBB(chirNode.GetParentBlock()); auto& position = chirNode.GetDebugLocation(); // Should not try to generate DebugLocation in the function without subprogram. CJC_NULLPTR_CHECK(currentBB); if (!currentBB->getParent()->getSubprogram()) { return nullptr; } needSubprogram = true; llvm::DIScope* scope = GetOrCreateScope(position, *currentBB); auto parentFunc = chirNode.GetTopLevelFunc(); if (parentFunc && parentFunc->GetFuncKind() == CHIR::DEFAULT_PARAMETER_FUNC) { return HandleDefaultParamLocation(chirNode, scope); } return llvm::DILocation::get( cgMod.GetLLVMContext(), position.GetBeginPos().line, position.GetBeginPos().column, scope); } llvm::DILocation* DIBuilder::CreateDILoc(llvm::DIScope* currentScope, const CHIR::Position& position) { if (!enabled && !enableLineInfo) { return nullptr; } return llvm::DILocation::get(cgMod.GetLLVMContext(), position.line, position.column, currentScope); } llvm::DIScope* DIBuilder::GetOrCreateScope(const CHIR::DebugLocation& position, llvm::BasicBlock& currentBB) { if (position.GetScopeInfo().size() > 1) { std::vector scopeInfo = position.GetScopeInfo(); curScope = GetOrCreateLexicalScope(position, currentBB, scopeInfo); return curScope; } if (position.GetScopeInfo().size() == 1) { curScope = currentBB.getParent()->getSubprogram(); } return position.GetScopeInfo().empty() && curScope ? curScope : currentBB.getParent()->getSubprogram(); } llvm::DIScope* DIBuilder::GetOrCreateLexicalScope( const CHIR::DebugLocation& position, llvm::BasicBlock& currentBB, std::vector& scopeInfo) { if (lexicalBlocks.find(scopeInfo) != lexicalBlocks.end()) { return lexicalBlocks[scopeInfo]; } auto file = currentBB.getParent()->getSubprogram()->getFile(); scopeInfo.pop_back(); auto parentScope = scopeInfo.size() == 1 ? currentBB.getParent()->getSubprogram() : GetOrCreateLexicalScope(position, currentBB, scopeInfo); auto lb = createLexicalBlock(parentScope, file, position.GetBeginPos().line, position.GetBeginPos().column); lexicalBlocks[position.GetScopeInfo()] = lb; return lb; } llvm::DISubroutineType* DIBuilder::CreateFuncType(const CHIR::FuncType* func, bool isImported, bool hasThis) { // SmallVector Size: 8. llvm::SmallVector eltTys; // Function return type. auto returnType = GetOrCreateType(*func->GetReturnType()); auto paramsTypes = func->GetParamTypes(); eltTys.push_back(returnType); bool skipFirstParm = false; if (isImported) { eltTys.push_back(CreateRefType()); // Context or this. skipFirstParm = true; } // Function paramsType. for (size_t index = 0; index < paramsTypes.size(); index++) { if (skipFirstParm) { skipFirstParm = false; continue; } llvm::DIType* cgType = GetOrCreateType(*paramsTypes[index]); if (hasThis && index == 0) { auto flag = llvm::DINode::FlagObjectPointer; flag |= llvm::DINode::FlagArtificial; cgType = createMemberPointerType(cgType, cgType, 64u, 0, flag); } eltTys.push_back(cgType); } llvm::DISubroutineType* subroutineType = createSubroutineType(getOrCreateTypeArray(eltTys)); return subroutineType; } llvm::DIFile* DIBuilder::GetOrCreateFile(const CHIR::DebugLocation& position) { std::string dirPath = position.GetAbsPath(); auto pos = dirPath.find_last_of(DIR_SEPARATOR); std::string fileName = ""; if (pos != std::string::npos) { fileName = dirPath.substr(pos + 1); dirPath = dirPath.substr(0, pos); } // Do option '--trimpath'. // if '--coverage' is enabled, '--trimpath' would be disabled. if (!cgMod.GetCGContext().GetCompileOptions().removedPathPrefix.empty()) { dirPath = RemovePathPrefix(dirPath, cgMod.GetCGContext().GetCompileOptions().removedPathPrefix); } auto diFile = createFile(fileName, dirPath); return diFile; } llvm::DIType* DIBuilder::GetOrCreateType(const CHIR::Type& ty, bool isReadOnly) { const CHIR::Type* baseTy = DeRef(const_cast(ty)); auto ref = typeCache.find(baseTy); if (ref != typeCache.end()) { auto diType = llvm::cast(ref->second); return isReadOnly ? createQualifiedType(llvm::dwarf::DW_TAG_const_type, diType) : diType; } auto diType = CreateDIType(*baseTy); CJC_ASSERT(diType); typeCache[baseTy] = llvm::TrackingMDRef(diType); return isReadOnly ? createQualifiedType(llvm::dwarf::DW_TAG_const_type, diType) : diType; } uint64_t DIBuilder::GetBasicTypeSize(const CHIR::Type& ty) const { llvm::Type* type = nullptr; switch (ty.GetTypeKind()) { case CHIR::Type::TypeKind::TYPE_INT8: case CHIR::Type::TypeKind::TYPE_UINT8: type = llvm::Type::getInt8Ty(cgMod.GetLLVMContext()); break; case CHIR::Type::TypeKind::TYPE_INT16: case CHIR::Type::TypeKind::TYPE_UINT16: type = llvm::Type::getInt16Ty(cgMod.GetLLVMContext()); break; case CHIR::Type::TypeKind::TYPE_RUNE: case CHIR::Type::TypeKind::TYPE_INT32: case CHIR::Type::TypeKind::TYPE_UINT32: type = llvm::Type::getInt32Ty(cgMod.GetLLVMContext()); break; case CHIR::Type::TypeKind::TYPE_INT64: case CHIR::Type::TypeKind::TYPE_UINT64: type = llvm::Type::getInt64Ty(cgMod.GetLLVMContext()); break; case CHIR::Type::TypeKind::TYPE_INT_NATIVE: case CHIR::Type::TypeKind::TYPE_UINT_NATIVE: { const int int64Width = 64; type = StaticCast(ty).GetBitness() == int64Width ? llvm::Type::getInt64Ty(cgMod.GetLLVMContext()) : llvm::Type::getInt32Ty(cgMod.GetLLVMContext()); break; } case CHIR::Type::TypeKind::TYPE_FLOAT16: type = llvm::Type::getHalfTy(cgMod.GetLLVMContext()); break; case CHIR::Type::TypeKind::TYPE_FLOAT32: type = llvm::Type::getFloatTy(cgMod.GetLLVMContext()); break; case CHIR::Type::TypeKind::TYPE_FLOAT64: type = llvm::Type::getDoubleTy(cgMod.GetLLVMContext()); break; case CHIR::Type::TypeKind::TYPE_BOOLEAN: type = llvm::Type::getInt1Ty(cgMod.GetLLVMContext()); break; default: return 0; } return GetTypeSize(type); } uint64_t DIBuilder::GetTypeSize(llvm::Type* llvmType) const { CJC_ASSERT(llvmType->isSized()); uint64_t size = 8u * cgMod.GetLLVMModule()->getDataLayout().getTypeAllocSize(llvmType); // 1 Byte = 8 bit. return size; } // If DIType obtained from 'GetOrCreateType' method, using this API to avoid getting an invalid result. uint64_t DIBuilder::GetSizeInBits(llvm::DIType* type) const { if (type->getTag() == llvm::dwarf::DW_TAG_typedef) { CJC_ASSERT(llvm::isa(type)); type = llvm::cast(type)->getBaseType(); } return type->getSizeInBits(); } llvm::DIType* DIBuilder::CreateDIType(const CHIR::Type& ty) { if (!enabled) { return nullptr; } llvm::dwarf::TypeKind encoding = llvm::dwarf::DW_ATE_unsigned; auto boxTy = ty.IsBox() ? &ty : nullptr; auto baseTy = ty.IsBox() ? StaticCast(ty).GetBaseType() : &ty; switch (baseTy->GetTypeKind()) { case CHIR::Type::TypeKind::TYPE_UNIT: case CHIR::Type::TypeKind::TYPE_NOTHING: case CHIR::Type::TypeKind::TYPE_VOID: return CreateUnitType(); case CHIR::Type::TypeKind::TYPE_RUNE: encoding = llvm::dwarf::DW_ATE_UTF; break; case CHIR::Type::TypeKind::TYPE_UINT8: case CHIR::Type::TypeKind::TYPE_UINT16: case CHIR::Type::TypeKind::TYPE_UINT32: case CHIR::Type::TypeKind::TYPE_UINT64: case CHIR::Type::TypeKind::TYPE_UINT_NATIVE: { encoding = llvm::dwarf::DW_ATE_unsigned; break; } case CHIR::Type::TypeKind::TYPE_INT8: case CHIR::Type::TypeKind::TYPE_INT16: case CHIR::Type::TypeKind::TYPE_INT32: case CHIR::Type::TypeKind::TYPE_INT64: case CHIR::Type::TypeKind::TYPE_INT_NATIVE: { encoding = llvm::dwarf::DW_ATE_signed; break; } case CHIR::Type::TypeKind::TYPE_FLOAT16: case CHIR::Type::TypeKind::TYPE_FLOAT32: case CHIR::Type::TypeKind::TYPE_FLOAT64: { encoding = llvm::dwarf::DW_ATE_float; break; } case CHIR::Type::TypeKind::TYPE_BOOLEAN: { encoding = llvm::dwarf::DW_ATE_boolean; break; } case CHIR::Type::TypeKind::TYPE_FUNC: return CreateVarFuncType(StaticCast(ty)); case CHIR::Type::TypeKind::TYPE_TUPLE: return CreateTupleType(StaticCast(*baseTy), boxTy); case CHIR::Type::TypeKind::TYPE_RAWARRAY: return CreateArrayType(StaticCast(ty)); case CHIR::Type::TypeKind::TYPE_VARRAY: return CreateVArrayType(StaticCast(ty)); case CHIR::Type::TypeKind::TYPE_CPOINTER: return CreateCPointerType(StaticCast(ty)); case CHIR::Type::TypeKind::TYPE_CSTRING: return CreateCStringType(StaticCast(ty)); case CHIR::Type::TypeKind::TYPE_CLASS: return CreateClassType(StaticCast(ty)); case CHIR::Type::TypeKind::TYPE_STRUCT: return CreateStructType(StaticCast(*baseTy), boxTy); case CHIR::Type::TypeKind::TYPE_ENUM: return CreateEnumType(StaticCast(*baseTy), boxTy); case CHIR::Type::TypeKind::TYPE_GENERIC: return CreateGenericType(StaticCast(ty)); case CHIR::Type::TypeKind::TYPE_INVALID: default: return createNullPtrType(); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::string tyName = baseTy->ToString(); #endif uint64_t size = GetBasicTypeSize(*baseTy); auto diType = createBasicType(tyName, size, static_cast(encoding)); auto typeDefType = createTypedef(diType, tyName, diCompileUnit->getFile(), 0, diCompileUnit); if (boxTy) { auto fwdDecl = createStructType(diNamespace, tyName, defaultFile, 0u, size + 64u, 0u, llvm::DINode::FlagZero, nullptr, getOrCreateArray({}), 0u, nullptr, ty.ToString()); auto memberType = createMemberType(fwdDecl, "base", defaultFile, 0u, size, 0u, 64u, llvm::DINode::FlagZero, typeDefType); replaceArrays(fwdDecl, getOrCreateArray({memberType})); return fwdDecl; } return typeDefType; } llvm::DIType* DIBuilder::CreateUnitType() { auto layOut = cgMod.GetLLVMModule()->getDataLayout().getStructLayout( llvm::cast(CGType::GetUnitCGType(cgMod)->GetLLVMType())); auto align = layOut->getAlignment().value(); auto unitType = createStructType(diCompileUnit, "Unit", defaultFile, 0u, 8, static_cast(align), llvm::DINode::FlagZero, nullptr, getOrCreateArray({}), 0, nullptr, UNIT_TYPE_STR); return unitType; } llvm::DIType* DIBuilder::CreatePointerType(llvm::DIType* diType, uint64_t sizeInBits) { return createPointerType(diType, sizeInBits); } /** * Pure Function type is a pointer of function address. */ llvm::DIType* DIBuilder::CreateVarFuncType(const CHIR::FuncType& funcTy) { auto size = CreateRefType()->getSizeInBits(); auto name = GenerateFuncName(funcTy); auto tag = static_cast(llvm::dwarf::DW_TAG_class_type); llvm::DICompositeType* fwdDecl = createReplaceableCompositeType( tag, name, diNamespace, defaultFile, 0u, 0, 64u, 0u, llvm::DINode::FlagTypePassByReference, funcTy.ToString()); fwdDecl = llvm::MDNode::replaceWithDistinct(llvm::TempDICompositeType(fwdDecl)); llvm::DIType* funcDIType = CreateFuncType(&funcTy); auto funcPtrType = createMemberType(fwdDecl, "ptr", defaultFile, 0u, CreateRefType()->getSizeInBits(), 0u, 0u, llvm::DINode::FlagZero, CreatePointerType(funcDIType, size)); replaceArrays(fwdDecl, getOrCreateArray({funcPtrType})); return fwdDecl; } llvm::DIType* DIBuilder::CreateTupleType(const CHIR::TupleType& tupleTy, const CHIR::Type* boxTy) { auto tupleTypeName = GenerateTypeName(tupleTy); auto scope = diCompileUnit; auto diFile = scope->getFile(); auto llvmType = CGType::GetOrCreate(cgMod, &tupleTy)->GetLLVMType(); auto identifier = tupleTy.ToString(); CodeGenDIVector16 elements; // SmallVector Size: 16. if (!CGType::GetOrCreate(cgMod, &tupleTy)->GetSize()) { auto fwdDecl = createStructType( scope, tupleTypeName, diFile, 0u, 64u, 0u, llvm::DINode::FlagZero, nullptr, {}, 0u, nullptr, identifier); auto memberType = CreateTypeInfoMember(fwdDecl, diFile); elements.push_back(memberType); replaceArrays(fwdDecl, getOrCreateArray(elements)); return fwdDecl; } // Tuple type is a temporary type which do no have fixed definition file & line. uint64_t boxOffset = boxTy ? 64u : 0u; auto totalSize = GetTypeSize(llvmType) + boxOffset; llvm::DICompositeType* fwdDecl = createStructType(scope, tupleTypeName, diFile, 0u, totalSize, 0u, llvm::DINode::FlagZero, nullptr, getOrCreateArray(elements), 0, nullptr, "$" + std::to_string(boxOffset) + "_" + identifier); if (boxTy) { typeCache[boxTy] = llvm::TrackingMDRef(fwdDecl); } unsigned i = 0; auto layOut = cgMod.GetLLVMModule()->getDataLayout().getStructLayout(llvm::cast(llvmType)); auto align = layOut->getAlignment().value(); for (auto& ty : tupleTy.GetElementTypes()) { auto elemType = GetOrCreateType(*ty); auto bitInSize = GetTypeSize(llvmType->getStructElementType(i)); auto offset = layOut->getElementOffsetInBits(i) + boxOffset; if (IsReferenceType(*ty, cgMod) || ty->IsRawArray()) { elemType = CreatePointerType(elemType, CreateRefType()->getSizeInBits()); } auto memberType = createMemberType(fwdDecl, "_" + std::to_string(i), diFile, 0u, bitInSize, static_cast(align), offset, llvm::DINode::FlagZero, elemType); elements.push_back(memberType); i++; } replaceArrays(fwdDecl, getOrCreateArray(elements)); return fwdDecl; } llvm::DIType* DIBuilder::CreateArrayType(const CHIR::RawArrayType& arrTy) { auto identifier = arrTy.ToString(); uint32_t dims = arrTy.GetDims(); CJC_ASSERT(dims != 0); auto arrayTypeName = GenerateTypeName(arrTy); auto diFile = defaultFile; auto scope = diCompileUnit; CodeGenDIVector3 elements; // Array.Type has 3 elements. #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto elemTy = arrTy.GetElementType(); auto elemType = GetOrCreateType(*elemTy); auto arrayLayout = dynamic_cast(CGType::GetOrCreate(cgMod, &arrTy))->GetLayoutType(); // when arrayLayout is nullptr, means this rawArray is not sized. auto arrayLayoutSize = arrayLayout ? GetTypeSize(arrayLayout) : 64u; // %ArrayBase is not include object now, so we should add 64bit for the object. uint64_t totalSize = 64u + arrayLayoutSize + GetSizeInBits(elemType); auto flag = llvm::DINode::FlagZero; #endif // Array type is a temporary type which do no have fixed definition file nor line. // type struct: {klass*, i32 arrayLength, [0 x T]}, where 'klass*' is invisible for user. llvm::DICompositeType* fwdDecl = createStructType( scope, arrayTypeName, diFile, 0u, totalSize, 0u, flag, nullptr, getOrCreateArray({}), 0, nullptr, identifier); if (!arrayLayout) { auto memberType = CreateTypeInfoMember(fwdDecl, diFile); elements.push_back(memberType); replaceArrays(fwdDecl, getOrCreateArray(elements)); return fwdDecl; } CreateArrayMemberType(arrTy, fwdDecl, elements, arrayLayout); return fwdDecl; } llvm::DIType* DIBuilder::CreateVArrayType(const CHIR::VArrayType& vArrTy) { auto size = vArrTy.GetSize(); auto elementType = GetOrCreateType(*vArrTy.GetElementType()); auto name = GenerateTypeName(vArrTy); auto llvmType = CGType::GetOrCreate(cgMod, &vArrTy)->GetLLVMType(); auto typeSize = GetTypeSize(llvmType); // Align in 64-bit. auto range = getOrCreateSubrange(0, size); auto diType = createArrayType(typeSize, 0, elementType, getOrCreateArray(range)); return createTypedef(diType, name, diCompileUnit->getFile(), 0, diCompileUnit); } llvm::DIType* DIBuilder::CreateCPointerType(const CHIR::CPointerType& ptrTy) { auto name = GenerateTypeName(ptrTy); auto diFile = defaultFile; llvm::DICompositeType* fwdDecl; auto uniqueName = ptrTy.ToString(); auto sizeInBits = CreateRefType()->getSizeInBits(); llvm::DIType* basicType = createBasicType("Int8", sizeInBits, static_cast(llvm::dwarf::DW_ATE_unsigned)); if (!ptrTy.GetTypeArgs().empty() && ptrTy.GetTypeArgs()[0] && !ptrTy.GetTypeArgs()[0]->IsCPointer()) { basicType = GetOrCreateType(*ptrTy.GetTypeArgs()[0]); } fwdDecl = createStructType( diCompileUnit, name, diFile, 0, sizeInBits, 0u, llvm::DINode::FlagZero, nullptr, {}, 0u, nullptr, uniqueName); auto elemType = CreatePointerType(basicType, sizeInBits); auto eleType = createMemberType( fwdDecl, "ptr", diFile, 0, sizeInBits, 8, 0, llvm::DINode::FlagZero, elemType); // 8 is default align size. replaceArrays(fwdDecl, getOrCreateArray({eleType})); return fwdDecl; } llvm::DIType* DIBuilder::CreateCStringType(const CHIR::CStringType& cStringType) { auto name = GenerateTypeName(cStringType); auto uniqueName = cStringType.ToString(); auto diFile = defaultFile; auto refSizeInBits = CreateRefType()->getSizeInBits(); // Generates the CString struct. auto fwdDecl = createStructType(diCompileUnit, name, diFile, 0, refSizeInBits, 0u, llvm::DINode::FlagZero, nullptr, {}, 0u, nullptr, uniqueName); // Generates a member 'chars' of the 'Int8 *' type in CString struct. auto elemSizeInBits = GetTypeSize(llvm::Type::getInt8Ty(cgMod.GetLLVMContext())); llvm::DIType* basicType = createBasicType("char", elemSizeInBits, static_cast(llvm::dwarf::DW_ATE_unsigned_char)); auto elemType = CreatePointerType(basicType, elemSizeInBits); auto eleType = createMemberType(fwdDecl, "chars", diFile, 0, elemSizeInBits, 8, 0, llvm::DINode::FlagZero, elemType); // 8 is default align size. replaceArrays(fwdDecl, getOrCreateArray({eleType})); return fwdDecl; } llvm::DIType* DIBuilder::CreateTypeInfoMember(llvm::DICompositeType* fwdDecl, llvm::DIFile* diFile) { return createMemberType( fwdDecl, "$ti", diFile, 0u, 64u, static_cast(0u), 0u, llvm::DINode::FlagZero, CreateRefType()); } void DIBuilder::CreateInheritedInterface( CodeGenDIVector16& elements, llvm::DICompositeType* fwdDecl, const CHIR::CustomType& customTy) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND for (auto interfaceTy : const_cast(customTy).GetImplementedInterfaceTysWithoutExtend( cgMod.GetCGContext().GetCHIRBuilder())) { auto interfaceDiType = GetOrCreateType(*interfaceTy); auto interfaceType = createInheritance(fwdDecl, interfaceDiType, 0u, 0u, llvm::DINode::FlagZero); elements.push_back(interfaceType); } for (auto extendDef : customTy.GetCustomTypeDef()->GetExtends()) { for (auto interfaceTy : extendDef->GetImplementedInterfaceTys()) { auto interfaceDiType = GetOrCreateType(*interfaceTy); auto interfaceType = createInheritance(fwdDecl, interfaceDiType, 0u, 0u, llvm::DINode::FlagZero); elements.push_back(interfaceType); } } #endif } void DIBuilder::CreateMethodType( const CHIR::CustomTypeDef& customDef, CodeGenDIVector16& elements, llvm::DICompositeType* fwdDecl) { auto allMethods = customDef.GetType()->GetDeclareAndExtendMethods(cgMod.GetCGContext().GetCHIRBuilder()); if (allMethods.empty()) { return; } auto diFile = GetOrCreateFile(customDef.GetDebugLocation()); for (auto method : allMethods) { bool hasThis = !method->TestAttr(CHIR::Attribute::STATIC); // the name of parameter init function is useless for debug, and may cause duplicate identifier error auto name = IsParaInitFunc(*method) ? "" : GenerateGenericFuncName(method->GetSrcCodeIdentifier(), method->GetOriginalGenericTypeParams()); auto subprogramTy = CreateFuncType(StaticCast(method->GetType()), false, hasThis); auto& position = method->GetDebugLocation(); llvm::DINode::DIFlags flags = llvm::DINode::FlagPrototyped; flags |= method->TestAttr(CHIR::Attribute::PUBLIC) ? llvm::DINode::FlagPublic : llvm::DINode::FlagZero; flags |= method->TestAttr(CHIR::Attribute::STATIC) ? llvm::DINode::FlagStaticMember : llvm::DINode::FlagZero; flags |= method->TestAttr(CHIR::Attribute::PROTECTED) ? llvm::DINode::FlagProtected : llvm::DINode::FlagZero; llvm::DISubprogram::DISPFlags spFlags = method->TestAttr(CHIR::Attribute::ABSTRACT) ? llvm::DISubprogram::SPFlagPureVirtual : llvm::DISubprogram::SPFlagZero; llvm::DISubprogram* sp = createMethod(fwdDecl, name, method->GetIdentifierWithoutPrefix(), diFile, position.GetBeginPos().line, subprogramTy, 0, 0, nullptr, flags, spFlags); elements.push_back(sp); } } llvm::DIType* DIBuilder::CreateClassType(const CHIR::ClassType& classTy) { auto classDef = classTy.GetClassDef(); auto identifier = classTy.ToString(); if (!CGType::GetOrCreate(cgMod, &classTy)->GetSize()) { return CreateGenericClassType(classTy); } if (IsCoreObject(*classDef)) { return GetOrCreateObjectClassType(); } if (classDef->IsInterface()) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND return CreateInterfaceType(classTy); #endif } if (classTy.IsAutoEnvBase()) { return CreateClosureType(classTy); } else if (IsClosureConversionEnvClass(*classDef)) { return CreateCapturedVars(classTy); } auto classLayout = StaticCast(CGType::GetOrCreate(cgMod, &classTy))->GetLayoutType(); CJC_ASSERT(!classLayout->isOpaque() && "The body is not set for classLayout."); auto& position = classDef->GetDebugLocation(); std::string name; if (IsCapturedClass(*classDef)) { name = "$Captured_"; auto capturedTy = DeRef(*classDef->GetDirectInstanceVar(0).type); if (capturedTy->IsClass() || capturedTy->IsStruct() || capturedTy->IsEnum()) { name += RemoveCustomTypePrefix(GenerateTypeName(*capturedTy)); } else { name += GenerateTypeName(*capturedTy); } } else { name = RemoveCustomTypePrefix(GenerateTypeName(classTy)); } auto diFile = GetOrCreateFile(position); auto classSize = GetClassSize(classLayout) + 64u; auto superDiType = GetOrCreateType( *const_cast(classTy).GetSuperClassTy(&cgMod.GetCGContext().GetCHIRBuilder())); auto defPackage = createNameSpace(diCompileUnit, classDef->GetPackageName(), false); auto tag = static_cast(llvm::dwarf::DW_TAG_class_type); llvm::DICompositeType* fwdDecl = createReplaceableCompositeType(tag, name, defPackage, diFile, position.GetBeginPos().line, 0, classSize, 0u, llvm::DINode::FlagTypePassByReference, identifier); fwdDecl = llvm::MDNode::replaceWithDistinct(llvm::TempDICompositeType(fwdDecl)); typeCache[&classTy] = llvm::TrackingMDRef(fwdDecl); CodeGenDIVector16 elements; auto inheritType = createInheritance(fwdDecl, superDiType, 0u, 0u, llvm::DINode::FlagZero); elements.push_back(inheritType); CreateInheritedInterface(elements, fwdDecl, classTy); CreateClassMemberType(classTy, classLayout, elements, fwdDecl); CreateMethodType(*classDef, elements, fwdDecl); replaceArrays(fwdDecl, getOrCreateArray(elements)); return fwdDecl; } llvm::DIType* DIBuilder::CreateGenericClassType(const CHIR::ClassType& classTy) { auto classDef = classTy.GetClassDef(); auto identifier = classTy.ToString(); auto& position = classDef->GetDebugLocation(); auto diFile = GetOrCreateFile(position); auto defPackage = createNameSpace(diCompileUnit, classDef->GetPackageName(), false); auto name = RemoveCustomTypePrefix(GenerateTypeName(classTy)); auto tag = static_cast(llvm::dwarf::DW_TAG_class_type); auto size = 64 + classDef->GetDirectInstanceVars().size() * 64; llvm::DICompositeType* fwdDecl = createReplaceableCompositeType(tag, name, defPackage, diFile, position.GetBeginPos().line, 0, size, 0u, llvm::DINode::FlagTypePassByReference, identifier); fwdDecl = llvm::MDNode::replaceWithDistinct(llvm::TempDICompositeType(fwdDecl)); typeCache[&classTy] = llvm::TrackingMDRef(fwdDecl); CodeGenDIVector16 elements; auto superDiType = GetOrCreateType( *const_cast(classTy).GetSuperClassTy(&cgMod.GetCGContext().GetCHIRBuilder())); auto inheritType = createInheritance(fwdDecl, superDiType, 0u, 0u, llvm::DINode::FlagZero); elements.push_back(inheritType); CreateInheritedInterface(elements, fwdDecl, classTy); CreateCustomTypeMember(classDef->GetDirectInstanceVars(), elements, fwdDecl, diFile); CreateMethodType(*classDef, elements, fwdDecl); CreateGetGenericFunc(classTy, fwdDecl, elements); replaceArrays(fwdDecl, getOrCreateArray(elements)); return fwdDecl; } llvm::DIType* DIBuilder::CreateCapturedVars(const CHIR::ClassType& classTy) { auto classLayout = StaticCast(CGType::GetOrCreate(cgMod, &classTy))->GetLayoutType(); auto classSize = GetClassSize(classLayout) + 64u; auto tag = static_cast(llvm::dwarf::DW_TAG_class_type); llvm::DICompositeType* fwdDecl = createReplaceableCompositeType(tag, "$CapturedVars", diNamespace, defaultFile, 0, 0, classSize, 0u, llvm::DINode::FlagTypePassByReference, classTy.ToString()); fwdDecl = llvm::MDNode::replaceWithDistinct(llvm::TempDICompositeType(fwdDecl)); CodeGenDIVector16 elements; auto superDiType = GetOrCreateObjectClassType(); auto inheritType = createInheritance(fwdDecl, superDiType, 0u, 0u, llvm::DINode::FlagZero); elements.push_back(inheritType); typeCache[&classTy] = llvm::TrackingMDRef(fwdDecl); CreateClassMemberType(classTy, classLayout, elements, fwdDecl); replaceArrays(fwdDecl, getOrCreateArray(elements)); return fwdDecl; } void DIBuilder::CreateCustomTypeMember(std::vector members, CodeGenDIVector16& elements, llvm::DICompositeType* fwdDecl, llvm::DIFile* diFile) { auto memberType = CreateTypeInfoMember(fwdDecl, diFile); elements.push_back(memberType); uint64_t offset = 0; for (auto& it : members) { auto elementTy = it.type; auto elemType = GetOrCreateType(*elementTy); auto bitInSize = GetSizeInBits(elemType); offset += bitInSize; auto align = 0u; memberType = createMemberType(fwdDecl, it.name, diFile, it.loc.GetBeginPos().line, bitInSize, static_cast(align), offset, llvm::DINode::FlagZero, elemType); elements.push_back(memberType); } } void DIBuilder::CreateGetGenericFunc( const CHIR::CustomType& customTy, llvm::DICompositeType* fwdDecl, CodeGenDIVector16& elements) { if (!customTy.IsGenericRelated()) { return; } auto defType = GetOrCreateType(*customTy.GetCustomTypeDef()->GetType()); // SmallVector Size: 8. llvm::SmallVector eltTys; eltTys.push_back(defType); llvm::DISubroutineType* subroutineType = createSubroutineType(getOrCreateTypeArray(eltTys)); auto funcElement = createFunction(fwdDecl, "$GetGenericDef", "$GetGenericDef", defaultFile, 0u, subroutineType, 0u, llvm::DINode::FlagPrototyped, llvm::DISubprogram::SPFlagZero); elements.emplace_back(funcElement); finalizeSubprogram(funcElement); } llvm::DIType* DIBuilder::CreateStructType(const CHIR::StructType& structTy, const CHIR::Type* boxTy) { auto structDef = structTy.GetStructDef(); auto identifier = structTy.ToString(); if (!CGType::GetOrCreate(cgMod, &structTy)->GetSize()) { auto& position = structDef->GetDebugLocation(); auto diFile = GetOrCreateFile(position); auto defPackage = createNameSpace(diCompileUnit, structDef->GetPackageName(), false); auto name = RemoveCustomTypePrefix(GenerateTypeName(structTy)); auto size = 64 + structDef->GetAllInstanceVars().size() * 64; auto fwdDecl = createStructType(defPackage, name, diFile, position.GetBeginPos().line, size, 0u, llvm::DINode::FlagZero, nullptr, {}, 0u, nullptr, identifier); typeCache[&structTy] = llvm::TrackingMDRef(fwdDecl); CodeGenDIVector16 elements; CreateInheritedInterface(elements, fwdDecl, structTy); CreateCustomTypeMember(structDef->GetAllInstanceVars(), elements, fwdDecl, diFile); CreateMethodType(*structDef, elements, fwdDecl); CreateGetGenericFunc(structTy, fwdDecl, elements); replaceArrays(fwdDecl, getOrCreateArray(elements)); return fwdDecl; } auto name = RemoveCustomTypePrefix(GenerateTypeName(structTy)); auto& position = structDef->GetDebugLocation(); auto diFile = GetOrCreateFile(position); auto cgType = CGType::GetOrCreate(cgMod, &structTy)->GetLLVMType(); uint64_t boxOffset = boxTy ? 64u : 0u; auto defPackage = createNameSpace(diCompileUnit, structDef->GetPackageName(), false); auto fwdDecl = createStructType(defPackage, name, diFile, position.GetBeginPos().line, GetTypeSize(cgType) + boxOffset, 0u, llvm::DINode::FlagZero, nullptr, {}, 0u, nullptr, "$" + std::to_string(boxOffset) + "_" + identifier); if (boxTy) { typeCache[boxTy] = llvm::TrackingMDRef(fwdDecl); } else { typeCache[&structTy] = llvm::TrackingMDRef(fwdDecl); } CodeGenDIVector16 elements; CreateInheritedInterface(elements, fwdDecl, structTy); CreateStructMemberType(structTy, cgType, elements, fwdDecl, boxTy); CreateMethodType(*structDef, elements, fwdDecl); replaceArrays(fwdDecl, getOrCreateArray(elements)); return fwdDecl; } void DIBuilder::CreateStructMemberType(const CHIR::StructType& structTy, llvm::Type* cgType, CodeGenDIVector16& elements, llvm::DICompositeType* fwdDecl, const CHIR::Type* boxTy) { auto structDef = structTy.GetStructDef(); auto& position = structDef->GetDebugLocation(); auto diFile = GetOrCreateFile(position); auto layOut = cgMod.GetLLVMModule()->getDataLayout().getStructLayout(llvm::cast(cgType)); unsigned i = 0; CHIR::CHIRBuilder& chirBuilder = cgMod.GetCGContext().GetCHIRBuilder(); auto& nonConstStructTy = const_cast(structTy); uint64_t boxOffset = boxTy ? 64u : 0u; CJC_ASSERT(nonConstStructTy.GetInstantiatedMemberTys(chirBuilder).size() == structDef->GetAllInstanceVars().size()); for (auto& it : structDef->GetAllInstanceVars()) { auto elementTy = nonConstStructTy.GetInstantiatedMemberTys(chirBuilder)[i]; auto elemTy = DeRef(*elementTy); bool isRef = IsReferenceType(*elemTy, cgMod); auto elemType = GetOrCreateType(*elemTy, it.TestAttr(CHIR::Attribute::READONLY)); if (isRef) { elemType = CreatePointerType(elemType); } auto cgElemType = cgType->getStructElementType(i); auto bitInSize = GetTypeSize(cgElemType); CJC_NULLPTR_CHECK(layOut); auto offset = layOut->getElementOffsetInBits(i) + boxOffset; auto align = layOut->getAlignment().value(); size_t line = 1; // the position info should obtain from MemberVarInfo. auto memberType = createMemberType(fwdDecl, it.name, diFile, line, bitInSize, static_cast(align), offset, llvm::DINode::FlagZero, elemType); elements.push_back(memberType); i++; } } llvm::DIType* DIBuilder::CreateEnumType(const CHIR::EnumType& enumTy, const CHIR::Type* boxTy) { auto enumDef = enumTy.GetEnumDef(); // Set small vector as 4 elements. CodeGenDIVector16 enumMembers; llvm::DICompositeType* fwdDecl = nullptr; auto identifier = enumTy.ToString(); auto cgType = StaticCast(CGType::GetOrCreate(cgMod, &enumTy)); if (!CGType::GetOrCreate(cgMod, &enumTy)->GetSize()) { auto& position = enumDef->GetDebugLocation(); auto diFile = GetOrCreateFile(position); auto defPackage = createNameSpace(diCompileUnit, enumDef->GetPackageName(), false); CJC_ASSERT(cgType->IsOptionLike() || cgType->IsAllAssociatedValuesAreNonRef()); // There are just two cases where we cannot calculate enum size: OptionLike and EnumWithNonRef. // Ugly implementation, needs to be refactored auto prefix = cgType->IsOptionLike() ? "E2$" : "E3$"; auto name = prefix + RemoveCustomTypePrefix(GenerateTypeName(enumTy)); auto size = 64 + enumDef->GetAllInstanceVars().size() * 64; size = cgType->IsOptionLike() ? size + 32 : size; // The size of Int32 is 32-bit. fwdDecl = createStructType(defPackage, name, diFile, position.GetBeginPos().line, size, 0u, llvm::DINode::FlagZero, nullptr, {}, 0u, nullptr, identifier); typeCache[&enumTy] = llvm::TrackingMDRef(fwdDecl); CodeGenDIVector16 elements; CreateInheritedInterface(elements, fwdDecl, enumTy); if (cgType->IsOptionLike()) { auto constructorType = GetOrCreateEnumCtorType(enumTy); auto ctorSize = cgType->IsOptionLike() ? 8u : 32u; auto ctorType = createMemberType( fwdDecl, "constructor", diFile, 0u, ctorSize, 0u, 64u, llvm::DINode::FlagZero, constructorType); elements.push_back(ctorType); } CreateMethodType(*enumDef, elements, fwdDecl); CreateGetGenericFunc(enumTy, fwdDecl, elements); replaceArrays(fwdDecl, getOrCreateArray(elements)); return fwdDecl; } bool isOption = enumTy.IsOption() && !const_cast(&enumTy)->IsBoxed(cgMod.GetCGContext().GetCHIRBuilder()); if (enumDef->IsAllCtorsTrivial()) { fwdDecl = CreateTrivial(enumTy, enumMembers); } else if (isOption || cgType->IsOptionLike()) { fwdDecl = CreateEnumOptionType(enumTy, enumMembers, boxTy); } else if (cgType->IsAllAssociatedValuesAreNonRef()) { fwdDecl = CreateEnumWithNonRefArgsType(enumTy, enumMembers); } else { fwdDecl = CreateEnumWithArgsType(enumTy, enumMembers, boxTy); } typeCache[&enumTy] = llvm::TrackingMDRef(fwdDecl); CreateInheritedInterface(enumMembers, fwdDecl, enumTy); CreateMethodType(*enumDef, enumMembers, fwdDecl); replaceArrays(fwdDecl, getOrCreateArray(enumMembers)); return fwdDecl; } llvm::DICompositeType* DIBuilder::GetOrCreateEnumCtorType(const CHIR::EnumType& enumTy) { auto ref = enumCtorCache.find(&enumTy); if (ref != enumCtorCache.end()) { return llvm::cast(ref->second); } auto enumDef = enumTy.GetEnumDef(); auto name = RemoveCustomTypePrefix(GenerateTypeName(enumTy)); auto& position = enumDef->GetDebugLocation(); auto diFile = GetOrCreateFile(position); CodeGenDIVector16 ctors; uint32_t fieldIdx = 0; auto cgType = StaticCast(CGType::GetOrCreate(cgMod, &enumTy)); bool isOption = enumTy.IsOption() && !const_cast(&enumTy)->IsBoxed(cgMod.GetCGContext().GetCHIRBuilder()); bool isOptionLike = isOption || cgType->IsOptionLike(); if (isOptionLike) { for (size_t index = 0; index < enumDef->GetCtors().size(); index++) { size_t nonArgIndex = cgType->IsAntiOptionLike() ? 0 : 1; auto ctorName = nonArgIndex == index && !enumTy.IsOption() ? "N$_" + enumDef->GetCtors()[index].name : enumDef->GetCtors()[index].name; auto enumDITy = createEnumerator(ctorName, fieldIdx); ctors.push_back(enumDITy); fieldIdx++; } } else { for (auto& it : enumDef->GetCtors()) { auto enumDITy = createEnumerator(it.name, fieldIdx); ctors.push_back(enumDITy); fieldIdx++; } } auto size = isOptionLike ? 8u : 32u; // The size of Int32 is 32-bit. auto basicType = createBasicType("Int32", size, static_cast(llvm::dwarf::DW_ATE_signed)); auto enumCtorType = createEnumerationType(diNamespace, name, diFile, position.GetBeginPos().line, size, 0u, getOrCreateArray(ctors), basicType, enumTy.ToString(), true); // Cache it. enumCtorCache[&enumTy] = llvm::TrackingMDRef(enumCtorType); return enumCtorType; } llvm::DICompositeType* DIBuilder::CreateTrivial(const CHIR::EnumType& enumTy, CodeGenDIVector16& enumMembers) { auto enumDef = enumTy.GetEnumDef(); auto defPackage = createNameSpace(diCompileUnit, enumDef->GetPackageName(), false); auto& position = enumDef->GetDebugLocation(); auto diFile = GetOrCreateFile(position); auto typeName = RemoveCustomTypePrefix(GenerateTypeName(enumTy)); auto fwdDecl = createStructType(defPackage, "E0$" + typeName, diFile, position.GetBeginPos().line, 32u, 0u, llvm::DINode::FlagZero, nullptr, {}, 0u, nullptr, "$" + enumTy.ToString()); auto constructorType = GetOrCreateEnumCtorType(enumTy); auto ctorType = createMemberType(fwdDecl, "constructor", diFile, 0u, 32u, 0u, 0u, llvm::DINode::FlagZero, constructorType); typeCache[&enumTy] = llvm::TrackingMDRef(fwdDecl); enumMembers.push_back(ctorType); return fwdDecl; } llvm::DICompositeType* DIBuilder::CreateEnumWithNonRefArgsType( const CHIR::EnumType& enumTy, CodeGenDIVector16& enumMembers) { /** * The Enum is stored as * 32-bits // constructor * X-bits // X is max size of union */ auto enumDef = enumTy.GetEnumDef(); auto defPackage = createNameSpace(diCompileUnit, enumDef->GetPackageName(), false); auto& position = enumDef->GetDebugLocation(); auto diFile = GetOrCreateFile(position); auto typeName = RemoveCustomTypePrefix(GenerateTypeName(enumTy)); auto cgType = StaticCast(CGType::GetOrCreate(cgMod, &enumTy)); auto fwdDecl = createStructType(defPackage, "E3$" + typeName, diFile, position.GetBeginPos().line, cgType->GetsizeOfConstructorUnion() + 32, 0u, llvm::DINode::FlagZero, nullptr, {}, 0u, nullptr, "$" + enumTy.ToString()); typeCache[&enumTy] = llvm::TrackingMDRef(fwdDecl); auto constructorType = GetOrCreateEnumCtorType(enumTy); uint32_t fieldIdx = 0; for (auto& ctor : enumTy.GetConstructorInfos(cgMod.GetCGContext().GetCHIRBuilder())) { CodeGenDIVector4 enumLayer{}; auto size = 32u; std::string ctorName = RemoveCustomTypePrefix(GenerateTypeName(enumTy)) + "_ctor_" + std::to_string(fieldIdx); // For the constructor without args. if (ctor.funcType->GetParamTypes().empty()) { auto subEnumType = createStructType(defPackage, ctorName, diFile, 0u, size, 0u, llvm::DINode::FlagZero, nullptr, {}); auto enumId = createMemberType( subEnumType, "constructor", diFile, 0u, size, 0u, 0u, llvm::DINode::FlagZero, constructorType); enumLayer.push_back(enumId); replaceArrays(subEnumType, getOrCreateArray(enumLayer)); auto inheritType = createInheritance(fwdDecl, subEnumType, 0u, 0u, llvm::DINode::FlagZero); enumMembers.push_back(inheritType); ++fieldIdx; continue; } std::vector sizeOfCtors = std::vector(ctor.funcType->GetParamTypes().size() + 1, 0); // The size of constructor is 32-bits. size_t totalSize = 32u; sizeOfCtors[0] = totalSize; for (uint32_t argIndex = 0; argIndex < ctor.funcType->GetParamTypes().size(); ++argIndex) { auto arg = ctor.funcType->GetParamTypes()[argIndex]; auto argTy = GetOrCreateType(*arg); if (IsReferenceType(*arg, cgMod) || arg->IsRawArray()) { argTy = CreatePointerType(argTy, CreateRefType()->getSizeInBits()); } auto argSize = GetSizeInBits(argTy); sizeOfCtors[argIndex + 1] = argSize; totalSize += argSize; } auto subEnumType = createStructType(defPackage, ctorName, diFile, 0u, totalSize, 0u, llvm::DINode::FlagZero, nullptr, {}); auto enumId = createMemberType( subEnumType, "constructor", diFile, 0u, size, 0u, 0u, llvm::DINode::FlagZero, constructorType); enumLayer.push_back(enumId); size_t offsetIndex = 1; size_t offset = 0; for (uint32_t argIndex = 0; argIndex < ctor.funcType->GetParamTypes().size(); ++argIndex) { auto arg = ctor.funcType->GetParamTypes()[argIndex]; auto argTy = GetOrCreateType(*arg); if (IsReferenceType(*arg, cgMod) || arg->IsRawArray()) { argTy = CreatePointerType(argTy, CreateRefType()->getSizeInBits()); } offset += sizeOfCtors[argIndex]; auto align = 0u; auto argType = createMemberType(subEnumType, "arg_" + std::to_string(offsetIndex), diFile, 0u, GetSizeInBits(argTy), static_cast(align), offset, llvm::DINode::FlagZero, argTy); enumLayer.push_back(argType); ++offsetIndex; } replaceArrays(subEnumType, getOrCreateArray(enumLayer)); auto inheritType = createInheritance(fwdDecl, subEnumType, 0u, 0u, llvm::DINode::FlagZero); enumMembers.push_back(inheritType); ++fieldIdx; } return fwdDecl; } llvm::DICompositeType* DIBuilder::CreateEnumOptionType( const CHIR::EnumType& enumTy, CodeGenDIVector16& enumMembers, const CHIR::Type* boxTy) { auto argTy = GetOptionLikeArgTy(enumTy, cgMod); auto enumDef = enumTy.GetEnumDef(); auto& position = enumDef->GetDebugLocation(); auto diFile = GetOrCreateFile(position); if (IsBoxSelf(*argTy, cgMod)) { return CreateBoxSelfOptionType(enumTy, enumMembers); } CJC_ASSERT(argTy); llvm::DICompositeType* fwdDecl = nullptr; bool argTypeIsOption = IsOptionOrOptionLike(*argTy); bool isRefArg = IsReferenceType(*argTy, cgMod) && !argTypeIsOption; // Processing constructor for reference type that is not core/Option. if (isRefArg) { auto argType = GetOrCreateType(*argTy); // It might have been generated when generating argType. auto ref = typeCache.find(&enumTy); if (ref != typeCache.end()) { fwdDecl = llvm::cast(ref->second); CodeGenDIVector16 vec(fwdDecl->getElements().begin(), fwdDecl->getElements().end()); // Update enumMember from generated fwdDecl. enumMembers.assign(vec.begin(), vec.end()); return fwdDecl; } uint64_t boxOffset = boxTy ? 64u : 0u; auto defPackage = createNameSpace(diCompileUnit, enumDef->GetPackageName(), false); auto name = enumTy.IsOption() ? RemoveCustomTypePrefix(GenerateTypeName(enumTy)) : "E2$" + RemoveCustomTypePrefix(GenerateTypeName(enumTy)); fwdDecl = createStructType(defPackage, name, diFile, position.GetBeginPos().line, GetSizeInBits(argType) + boxOffset, argType->getAlignInBits(), llvm::DINode::FlagZero, nullptr, getOrCreateArray({}), 0u, nullptr, "$" + std::to_string(boxOffset) + "_" + enumTy.ToString()); if (boxTy) { typeCache[boxTy] = llvm::TrackingMDRef(fwdDecl); } auto offsetBits = argTy->IsRawArray() ? GetSizeInBits(argType) : 0u; auto valueType = createMemberType(fwdDecl, "val", diFile, 0u, GetSizeInBits(argType), 0u, offsetBits + boxOffset, llvm::DINode::FlagZero, argType); // For optionLike, should generate constructor. if (!enumTy.IsOption()) { auto constructorType = GetOrCreateEnumCtorType(enumTy); auto ctorType = createMemberType( fwdDecl, "constructor_R", diFile, 0u, 8u, 0u, 64u, llvm::DINode::FlagZero, constructorType); enumMembers.push_back(ctorType); } // Reference type wrapped by option does not have the constructor Some or None. enumMembers.push_back(valueType); return fwdDecl; } else if (argTypeIsOption) { // Processing constructor for option type. return CreateNestedOptionType(enumTy, enumMembers, boxTy); } else { return CreateNonRefOptionType(enumTy, enumMembers, boxTy); } } llvm::DICompositeType* DIBuilder::CreateBoxSelfOptionType(const CHIR::EnumType& enumTy, CodeGenDIVector16& enumMembers) { auto enumDef = enumTy.GetEnumDef(); auto& position = enumDef->GetDebugLocation(); auto diFile = GetOrCreateFile(position); auto defPackage = createNameSpace(diCompileUnit, enumDef->GetPackageName(), false); auto name = "E2$" + RemoveCustomTypePrefix(GenerateTypeName(enumTy)); auto fwdDecl = createStructType(defPackage, name, diFile, position.GetBeginPos().line, 128u, 0u, llvm::DINode::FlagZero, nullptr, getOrCreateArray({}), 0u, nullptr, enumTy.ToString()); auto constructorType = GetOrCreateEnumCtorType(enumTy); auto ctorType = createMemberType(fwdDecl, "constructor_R", diFile, 0u, 8u, 0u, 64u, llvm::DINode::FlagZero, constructorType); auto valType = createMemberType(fwdDecl, "val", diFile, 0u, 64u, 0u, 64u, llvm::DINode::FlagZero, CreatePointerType(fwdDecl)); enumMembers.push_back(ctorType); enumMembers.push_back(valType); return fwdDecl; } llvm::DICompositeType* DIBuilder::CreateNestedOptionType( const CHIR::EnumType& enumTy, CodeGenDIVector16& enumMembers, const CHIR::Type* boxTy) { // Generate Option> type. auto enumDef = enumTy.GetEnumDef(); auto& position = enumDef->GetDebugLocation(); auto diFile = GetOrCreateFile(position); llvm::DICompositeType* fwdDecl = nullptr; auto optionTy = StaticCast(GetOptionLikeArgTy(enumTy, cgMod)); auto argTy = GetOptionLikeArgTy(*optionTy, cgMod); auto constructorType = GetOrCreateEnumCtorType(enumTy); bool hasCoreOption = false; // case enumTy = Option> where T2 = Option if (argTy->IsEnum()) { hasCoreOption = IsOptionOrOptionLike(*argTy); } // ArgType is a reference option type. #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND constexpr uint32_t enumFieldAlignment = 64uL; #endif if (IsReferenceType(*optionTy, cgMod) && !hasCoreOption) { auto defPackage = createNameSpace(diCompileUnit, enumDef->GetPackageName(), false); auto name = enumTy.IsOption() ? RemoveCustomTypePrefix(GenerateTypeName(enumTy)) : "E2$" + RemoveCustomTypePrefix(GenerateTypeName(enumTy)); uint64_t boxOffset = boxTy ? 64u : 0u; fwdDecl = createStructType(defPackage, name, diFile, position.GetBeginPos().line, 64u + enumFieldAlignment + boxOffset, 0u, llvm::DINode::FlagZero, nullptr, getOrCreateArray({}), 0u, nullptr, "$" + std::to_string(boxOffset) + "_" + enumTy.ToString()); if (boxTy) { typeCache[boxTy] = llvm::TrackingMDRef(fwdDecl); } else { typeCache[&enumTy] = llvm::TrackingMDRef(fwdDecl); } auto argType = GetOrCreateType(*optionTy); argType = CreatePointerType(argType); CJC_NULLPTR_CHECK(argType); auto ctorType = createMemberType( fwdDecl, "constructor", diFile, 0u, 8u, 0u, boxOffset, llvm::DINode::FlagZero, constructorType); auto valueType = createMemberType(fwdDecl, "val", diFile, 0u, GetSizeInBits(argType), 0u, enumFieldAlignment + boxOffset, llvm::DINode::FlagZero, argType); enumMembers.push_back(ctorType); enumMembers.push_back(valueType); } else { // Processing the case that argType is nested option fwdDecl = CreateNonRefOptionType(enumTy, enumMembers, boxTy); } return fwdDecl; } llvm::DICompositeType* DIBuilder::CreateNonRefOptionType( const CHIR::EnumType& enumTy, CodeGenDIVector16& enumMembers, const CHIR::Type* boxTy) { // Generate Option type. auto enumDef = enumTy.GetEnumDef(); auto& position = enumDef->GetDebugLocation(); auto diFile = GetOrCreateFile(position); auto argTy = GetOptionLikeArgTy(enumTy, cgMod); auto argType = GetOrCreateType(*argTy); auto constructorType = GetOrCreateEnumCtorType(enumTy); auto cgType = CGType::GetOrCreate(cgMod, &enumTy)->GetLLVMType(); auto totalSize = GetTypeSize(cgType); auto defPackage = createNameSpace(diCompileUnit, enumDef->GetPackageName(), false); auto name = enumTy.IsOption() ? RemoveCustomTypePrefix(GenerateTypeName(enumTy)) : "E2$" + RemoveCustomTypePrefix(GenerateTypeName(enumTy)); uint64_t boxOffset = boxTy ? 64u : 0u; auto fwdDecl = createStructType(defPackage, name, diFile, position.GetBeginPos().line, totalSize + boxOffset, argType->getAlignInBits(), llvm::DINode::FlagZero, nullptr, getOrCreateArray({}), 0u, nullptr, "$" + std::to_string(boxOffset) + "_" + enumTy.ToString()); auto layOut = cgMod.GetLLVMModule()->getDataLayout().getStructLayout(llvm::cast(cgType)); CJC_NULLPTR_CHECK(layOut); auto align = layOut->getAlignment().value(); auto ctorCodeGenType = cgType->getStructElementType(0); auto ctorSize = GetTypeSize(ctorCodeGenType); auto ctorType = createMemberType(fwdDecl, "constructor", diFile, 0u, ctorSize, static_cast(align), boxOffset, llvm::DINode::FlagZero, constructorType); auto valCodeGenType = cgType->getStructElementType(1); auto valSize = GetTypeSize(valCodeGenType); auto valOffset = layOut->getElementOffsetInBits(1) + boxOffset; auto valName = "val"; auto valType = createMemberType(fwdDecl, valName, diFile, 0u, valSize, static_cast(align), valOffset, llvm::DINode::FlagZero, argType); enumMembers.push_back(ctorType); enumMembers.push_back(valType); return fwdDecl; } llvm::DIType* DIBuilder::CreateGenericType(const CHIR::GenericType& genericTy) { auto name = GenerateTypeName(genericTy); auto genericSize = 64u; auto genericType = createStructType(diCompileUnit, name, defaultFile, 0u, genericSize, 0u, llvm::DINode::FlagZero, nullptr, getOrCreateArray({}), 0, nullptr, genericTy.GetIdentifier()); auto ti = CreateTypeInfoMember(genericType, defaultFile); CodeGenDIVector4 elements{ti}; for (auto upperBound : genericTy.GetUpperBounds()) { auto upperBoundType = GetOrCreateType(*upperBound); auto inheritType = createInheritance(genericType, upperBoundType, 0u, 0u, llvm::DINode::FlagZero); elements.emplace_back(inheritType); } replaceArrays(genericType, getOrCreateArray(elements)); return genericType; } } // namespace CodeGen } // namespace Cangjie cangjie_compiler-1.0.7/src/CodeGen/DIBuilder.h000066400000000000000000000333541510705540100210660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_DIBUILDER_H #define CANGJIE_DIBUILDER_H #include "llvm/IR/DIBuilder.h" #include "Base/CGTypes/CGClassType.h" #include "Base/CGTypes/CGEnumType.h" #include "Base/CHIRExprWrapper.h" #include "CGModule.h" #include "CJNative/CGTypes/EnumCtorTIOrTTGenerator.h" #include "Utils/CGCommonDef.h" #include "cangjie/CHIR/DebugLocation.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" namespace Cangjie { namespace CHIR { class Type; class GlobalVar; } // namespace CHIR namespace CodeGen { class CGModule; class CGType; using LLVMDIBuilder = llvm::DIBuilder; /* llvm::SmallVector has improved performance when No. of elements is smaller than declared, * so some commonly used sizes of SmallVectors are declared below for easy use. */ using CodeGenDIVector3 = llvm::SmallVector; // A vector of 3, Array.Type has 3 elements. using CodeGenDIVector4 = llvm::SmallVector; // A vector of 4, commonly for 4 enum elements. using CodeGenDIVector16 = llvm::SmallVector; // A vector of 16, for even larger use. class DIBuilder : public LLVMDIBuilder { public: explicit DIBuilder(CGModule& cgMod); void Finalize(); void CreateGlobalVar(const CHIR::GlobalVar& variable); void CreateAnonymousTypeForGenericType(llvm::GlobalVariable* typeTemplate, const CHIR::Type& ty); void SetSubprogram(const CHIR::Func* func, llvm::Function* function); void FinalizeSubProgram(llvm::Function& function); void EmitDeclare(const CHIR::Debug& debugNode, llvm::BasicBlock& curBB, bool pointerWrapper = false); void EmitUnBoxDeclare(const CHIR::Expression& exprNode, const CHIR::Type& ty, const std::string& name, llvm::Value* value, llvm::BasicBlock& curBB); void EmitGenericDeclare( const CHIR::GenericType& genericTy, llvm::Value* arg, llvm::BasicBlock& curBB, size_t index); llvm::DILocation* CreateDILoc(const CHIRExprWrapper& chirNode); llvm::DILocation* CreateDILoc(llvm::DIScope* currentScope, const CHIR::Position& position); void CreateCompileUnit(const std::string& pkgName); void CreateNameSpace(const std::string& pkgName); private: bool enabled = false; bool enableLineInfo = false; bool needSubprogram = false; size_t genericID = 0; llvm::DINamespace* diNamespace{nullptr}; llvm::DICompileUnit* diCompileUnit{nullptr}; llvm::DIType* objDIType{nullptr}; llvm::DIType* refDIType = nullptr; CGModule& cgMod; size_t paramNo = 0; llvm::DIFile* defaultFile{nullptr}; Position curLocation = {0, 0}; llvm::DIScope* curScope{nullptr}; std::vector recursiveChains; std::map, llvm::DIScope*> lexicalBlocks; llvm::DenseMap typeCache; llvm::DenseMap enumCtorCache; llvm::DIType* CreateDIType(const CHIR::Type& ty); llvm::DIScope* GetOrCreateScope(const CHIR::DebugLocation& position, llvm::BasicBlock& currentBB); llvm::DIScope* GetOrCreateLexicalScope( const CHIR::DebugLocation& position, llvm::BasicBlock& currentBB, std::vector& scopeInfo); llvm::DILocation* HandleDefaultParamLocation(const CHIRExprWrapper& chirNode, llvm::DIScope* scope); llvm::DIFile* GetOrCreateFile(const CHIR::DebugLocation& position); llvm::DIType* GetOrCreateType(const CHIR::Type& ty, bool isReadOnly = false); void CreateParameter(const CHIR::Debug& debugNode, llvm::BasicBlock& curBB, bool pointerWrapper); llvm::DIType* CreateUnitType(); llvm::DIType* CreateRefType(); llvm::DIType* CreatePointerType(llvm::DIType* diType, uint64_t sizeInBits = 64uL); llvm::DIType* GetOrCreateObjectClassType(); llvm::DIType* CreateClosureType(const CHIR::Type& ty); llvm::DIType* CreateVarFuncType(const CHIR::FuncType& funcTy); llvm::DISubroutineType* CreateFuncType(const CHIR::FuncType* func, bool isImported = false, bool hasThis = false); llvm::DISubroutineType* CreateDefaultFunctionType(); llvm::DIType* CreateTupleType(const CHIR::TupleType& tupleTy, const CHIR::Type* boxTy); llvm::DIType* CreateArrayType(const CHIR::RawArrayType& arrTy); void CreateArrayMemberType(const CHIR::RawArrayType& arrTy, llvm::DICompositeType* fwdDecl, CodeGenDIVector3& elements, llvm::Type* arrayLayout); llvm::DIType* CreateVArrayType(const CHIR::VArrayType& vArrTy); llvm::DIType* CreateCPointerType(const CHIR::CPointerType& ptrTy); llvm::DIType* CreateCStringType(const CHIR::CStringType& cStringType); llvm::DIType* CreateTypeInfoMember(llvm::DICompositeType* fwdDecl, llvm::DIFile* diFile); void CreateInheritedInterface( CodeGenDIVector16& elements, llvm::DICompositeType* fwdDecl, const CHIR::CustomType& customTy); void CreateMethodType( const CHIR::CustomTypeDef& customDef, CodeGenDIVector16& elements, llvm::DICompositeType* fwdDecl); llvm::DIType* CreateClassType(const CHIR::ClassType& classTy); llvm::DIType* CreateGenericClassType(const CHIR::ClassType& classTy); llvm::DIType* CreateCapturedVars(const CHIR::ClassType& classTy); void CreateClassMemberType(const CHIR::ClassType& classTy, llvm::Type* classLayout, CodeGenDIVector16& elements, llvm::DICompositeType* fwdDecl); llvm::DIType* CreateStructType(const CHIR::StructType& structTy, const CHIR::Type* boxTy); void CreateStructMemberType(const CHIR::StructType& structTy, llvm::Type* cgType, CodeGenDIVector16& elements, llvm::DICompositeType* fwdDecl, const CHIR::Type* boxTy); void CreateCustomTypeMember(std::vector members, CodeGenDIVector16& elements, llvm::DICompositeType* fwdDecl, llvm::DIFile* diFile); void CreateGetGenericFunc( const CHIR::CustomType& customTy, llvm::DICompositeType* fwdDecl, CodeGenDIVector16& elements); llvm::DIType* CreateEnumType(const CHIR::EnumType& enumTy, const CHIR::Type* boxTy); llvm::DICompositeType* GetOrCreateEnumCtorType(const CHIR::EnumType& enumTy); llvm::DICompositeType* CreateEnumOptionType( const CHIR::EnumType& enumTy, CodeGenDIVector16& enumMembers, const CHIR::Type* boxTy); llvm::DICompositeType* CreateBoxSelfOptionType(const CHIR::EnumType& enumTy, CodeGenDIVector16& enumMembers); llvm::DICompositeType* CreateNestedOptionType( const CHIR::EnumType& enumTy, CodeGenDIVector16& enumMembers, const CHIR::Type* boxTy); llvm::DICompositeType* CreateNonRefOptionType( const CHIR::EnumType& enumTy, CodeGenDIVector16& enumMembers, const CHIR::Type* boxTy); llvm::DICompositeType* CreateEnumWithArgsType( const CHIR::EnumType& enumTy, CodeGenDIVector16& enumMembers, const CHIR::Type* boxTy); llvm::DICompositeType* CreateTrivial(const CHIR::EnumType& enumTy, CodeGenDIVector16& enumMembers); llvm::DICompositeType* CreateEnumWithNonRefArgsType(const CHIR::EnumType& enumTy, CodeGenDIVector16& enumMembers); uint64_t GetBasicTypeSize(const CHIR::Type& ty) const; uint64_t GetTypeSize(llvm::Type* llvmType) const; uint64_t GetSizeInBits(llvm::DIType* type) const; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::DIType* CreateInterfaceType(const CHIR::ClassType& interfaceTy); llvm::DICompositeType* CreateEnumClassType(const CHIR::EnumType& enumTy, const CHIR::EnumCtorInfo& constructor, llvm::DICompositeType* subEnumType, std::size_t ctorIndex); llvm::DIType* CreateGenericType(const CHIR::GenericType& genericTy); uint64_t GetClassSize(llvm::Type* classLayout) const; #endif bool IsOptionOrOptionLike(const CHIR::Type& ty) { if (!ty.IsEnum()) { return false; } return StaticCast(CGType::GetOrCreate(cgMod, &ty))->IsOptionLike(); } static std::string RemoveCustomTypePrefix(const std::string& typeName) { std::string prefix = "::"; auto prefixPos = typeName.find(prefix); return typeName.substr(prefixPos + prefix.length()); } static std::string GenerateGenericFuncName( const std::string& funcName, const std::vector& genericTypeParams) { std::string typeName(funcName); if (!genericTypeParams.empty()) { typeName += "<"; for (auto ty : genericTypeParams) { typeName += ty->GetSrcCodeIdentifier(); typeName += ","; } typeName.pop_back(); typeName += ">"; } return typeName; } std::string GenerateTypeName(const CHIR::Type& type) { if (type.IsPrimitive()) { return type.ToString(); } switch (type.GetTypeKind()) { case CHIR::Type::TypeKind::TYPE_RAWARRAY: return GenerateRawArrayName(StaticCast(type)); case CHIR::Type::TypeKind::TYPE_TUPLE: return GenerateTupleName(StaticCast(type)); case CHIR::Type::TypeKind::TYPE_FUNC: return GenerateFuncName(StaticCast(type)); case CHIR::Type::TypeKind::TYPE_VARRAY: return GenerateVArrayName(StaticCast(type)); case CHIR::Type::TypeKind::TYPE_CPOINTER: return GenerateCPointerName(StaticCast(type)); case CHIR::Type::TypeKind::TYPE_CSTRING: return type.ToString(); case CHIR::Type::TypeKind::TYPE_CLASS: case CHIR::Type::TypeKind::TYPE_STRUCT: case CHIR::Type::TypeKind::TYPE_ENUM: return GenerateCustomTypeName(StaticCast(type)); case CHIR::Type::TypeKind::TYPE_GENERIC: return GenerateGenericTypeName(StaticCast(type)); case CHIR::Type::TypeKind::TYPE_REFTYPE: return GenerateTypeName(*StaticCast(type).GetBaseType()); case CHIR::Type::TypeKind::TYPE_VOID: case CHIR::Type::TypeKind::TYPE_NOTHING: case CHIR::Type::TypeKind::TYPE_UNIT: return "()"; default: CJC_ABORT(); return ""; } } std::string GenerateRawArrayName(const CHIR::RawArrayType& type) { std::stringstream typeName; for (auto i = 0u; i < type.GetDims(); i++) { typeName << "RawArray<"; } typeName << GenerateTypeName(*type.GetElementType()); std::string suffix(type.GetDims(), '>'); typeName << suffix; return typeName.str(); } std::string GenerateTupleName(const CHIR::TupleType& type) { std::string typeName = "Tuple<"; for (auto arg : type.GetTypeArgs()) { typeName += GenerateTypeName(*arg); typeName += ","; } typeName.pop_back(); typeName += ">"; return typeName; } std::string GenerateFuncName(const CHIR::FuncType& type) { std::string typeName = "("; if (!type.GetParamTypes().empty()) { for (auto param : type.GetParamTypes()) { typeName += GenerateTypeName(*param); typeName += ","; } typeName.pop_back(); } typeName += ")->"; typeName += GenerateTypeName(*type.GetReturnType()); return typeName; } std::string GenerateVArrayName(const CHIR::VArrayType& type) { std::stringstream typeName; typeName << "VArray<"; typeName << GenerateTypeName(*StaticCast(type).GetElementType()); typeName << ",$" << type.GetSize() << ">"; return typeName.str(); } std::string GenerateCPointerName(const CHIR::CPointerType& type) { std::string typeName = "CPointer<"; for (auto arg : type.GetTypeArgs()) { typeName += GenerateTypeName(*arg); typeName += ","; } typeName.pop_back(); typeName += ">"; return typeName; } std::string GenerateCustomTypeName(const CHIR::CustomType& type) { if (type.IsAutoEnvBase()) { return GenerateClosureTypeName(StaticCast(type)); } auto pkgName = type.GetCustomTypeDef()->GetPackageName(); std::string typeName = pkgName + "::" + CHIR::GetCustomTypeIdentifier(type); if (!type.GetTypeArgs().empty()) { typeName += "<"; for (auto arg : type.GetTypeArgs()) { typeName += GenerateTypeName(*arg); typeName += ","; } typeName.pop_back(); typeName += ">"; } return typeName; } std::string GenerateClosureTypeName(const CHIR::ClassType& type) { std::string typeName = "("; auto [paramTypes, retType] = GetFuncTypeWithoutThisPtrFromAutoEnvBaseType(type); if (!paramTypes.empty()) { for (auto ty : paramTypes) { typeName += GenerateTypeName(*ty); typeName += ","; } typeName.pop_back(); } typeName += ")->"; typeName += GenerateTypeName(*retType); return typeName; } std::string GenerateGenericTypeName(const CHIR::GenericType& type) { return GENERIC_PREFIX + type.GetSrcCodeIdentifier(); } }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_DIBUILDER_H cangjie_compiler-1.0.7/src/CodeGen/EmitBasicBlockIR.cpp000066400000000000000000000070121510705540100226540ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "EmitBasicBlockIR.h" #include #include "CGModule.h" #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "CJNative/CJNativeCGCFFI.h" #endif #include "EmitExpressionIR.h" #include "IRBuilder.h" #include "IRGenerator.h" #include "Utils/BlockScopeImpl.h" #include "cangjie/CHIR/Value.h" namespace Cangjie { namespace CodeGen { class BasicBlockGeneratorImpl : public IRGeneratorImpl { public: BasicBlockGeneratorImpl(CGModule& cgMod, const CHIR::Block& chirBB) : cgMod(cgMod) { worklist.reserve(chirBB.GetParentBlockGroup()->GetBlocks().size()); (void)worklist.emplace_back(&chirBB); } void EmitIR() override; private: void CreateLandingPad(const CHIR::Block* block) const; private: CGModule& cgMod; std::vector worklist; }; template <> class IRGenerator : public IRGenerator<> { public: IRGenerator(CGModule& cgMod, const CHIR::Block& chirBB) : IRGenerator<>(std::make_unique(cgMod, chirBB)) { } }; void BasicBlockGeneratorImpl::EmitIR() { if (worklist.empty()) { return; } auto parentFunc = worklist.at(0)->GetTopLevelFunc(); auto functionToEmitIR = cgMod.GetOrInsertCGFunction(parentFunc)->GetRawFunction(); // Emit all basicBlock to function. for (size_t idx = 0; idx < worklist.size(); ++idx) { auto currChirBB = worklist.at(idx); if (auto bb = cgMod.GetMappedBB(currChirBB); bb == nullptr) { bb = llvm::BasicBlock::Create(cgMod.GetLLVMContext(), PREFIX_FOR_BB_NAME + currChirBB->GetIdentifierWithoutPrefix(), functionToEmitIR); cgMod.SetOrUpdateMappedBB(currChirBB, bb); } CreateLandingPad(currChirBB); for (auto succChirBB : currChirBB->GetSuccessors()) { // Some BasicBlock may have back edges. if (std::find(worklist.rbegin(), worklist.rend(), succChirBB) != worklist.rend()) { continue; } (void)worklist.emplace_back(succChirBB); } } // Emit expressions for each basicBlock. for (auto& idx : worklist) { EmitExpressionIR(cgMod, idx->GetExpressions()); } } void BasicBlockGeneratorImpl::CreateLandingPad(const CHIR::Block* block) const { if (!block->IsLandingPadBlock()) { return; } IRBuilder2 irBuilder(cgMod); CodeGenBlockScope codeGenBlockScope(irBuilder, *block); auto landingPad = irBuilder.CreateLandingPad(CGType::GetLandingPadType(cgMod.GetLLVMContext()), 0); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (block->GetExceptions().empty()) { landingPad->addClause(llvm::Constant::getNullValue(irBuilder.getInt8PtrTy())); } else { for (auto exceptClass : block->GetExceptions()) { auto exceptName = exceptClass->GetClassDef()->GetIdentifierWithoutPrefix(); auto typeInfo = irBuilder.CreateTypeInfo(*exceptClass); auto clause = irBuilder.CreateBitCast(typeInfo, irBuilder.getInt8PtrTy()); landingPad->addClause(static_cast(clause)); } } #endif } void EmitBasicBlockIR(CGModule& cgMod, const CHIR::Block& chirBB) { IRGenerator(cgMod, chirBB).EmitIR(); } } // namespace CodeGen } // namespace Cangjie cangjie_compiler-1.0.7/src/CodeGen/EmitBasicBlockIR.h000066400000000000000000000010731510705540100223220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_EMITBASICBLOCKIR_H #define CANGJIE_EMITBASICBLOCKIR_H namespace Cangjie { namespace CHIR { class Block; } namespace CodeGen { class CGModule; void EmitBasicBlockIR(CGModule& cgMod, const CHIR::Block& chirBB); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_EMITBASICBLOCKIR_H cangjie_compiler-1.0.7/src/CodeGen/EmitExpressionIR.cpp000066400000000000000000000116511510705540100230230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "EmitExpressionIR.h" #include "Base/CHIRExprWrapper.h" #include "Base/ExprDispatcher/ExprDispatcher.h" #include "CGModule.h" #include "IRBuilder.h" #include "IRGenerator.h" #include "cangjie/CHIR/Value.h" using namespace Cangjie::CHIR; namespace Cangjie::CodeGen { class ExpressionGeneratorImpl : public IRGeneratorImpl { public: ExpressionGeneratorImpl(CGModule& cgMod, const std::vector& chirExprs) : cgMod(cgMod), chirExprs(chirExprs) { // All chirExprs should be in the same BasicBlock. for (auto it : chirExprs) { CJC_ASSERT(it->GetParentBlock() == chirExprs[0]->GetParentBlock()); } } void EmitIR() override; private: CGModule& cgMod; const std::vector chirExprs; }; template <> class IRGenerator : public IRGenerator<> { public: IRGenerator(CGModule& cgMod, std::vector chirExprs) : IRGenerator<>(std::make_unique(cgMod, chirExprs)) { } }; void ExpressionGeneratorImpl::EmitIR() { CJC_ASSERT(!chirExprs.empty()); IRBuilder2 irBuilder(cgMod); // Since we restricted all the expressions are in the same basic block, // we are able to just use the first expression to determine the insert // point. if (auto parentBB = chirExprs.front()->GetParentBlock()) { irBuilder.SetInsertPoint(cgMod.GetMappedBB(parentBB)); } irBuilder.CreateGenericParaDeclare(*cgMod.GetOrInsertCGFunction(chirExprs.front()->GetTopLevelFunc())); for (auto chirExpr : chirExprs) { CHIRExprWrapper chirExprWrapper = CHIRExprWrapper(*chirExpr); if (chirExpr->GetParentBlock()) { irBuilder.EmitLocation(chirExprWrapper); } irBuilder.SetCHIRExpr(&chirExprWrapper); if (cgMod.GetCGContext().GetCompileOptions().enableCompileDebug) { irBuilder.CreateLoadInstForParameter(*chirExpr); } llvm::Value* rawRet = nullptr; switch (chirExpr->GetExprMajorKind()) { case ExprMajorKind::TERMINATOR: rawRet = HandleTerminatorExpression(irBuilder, *chirExpr); break; case ExprMajorKind::UNARY_EXPR: { auto unaryExpr = StaticCast(chirExpr); irBuilder.EmitLocation(CHIRExprWrapper(*chirExpr)); rawRet = HandleUnaryExpression(irBuilder, CHIRUnaryExprWrapper(*unaryExpr)); break; } case ExprMajorKind::BINARY_EXPR: { auto binaryExpr = StaticCast(chirExpr); irBuilder.EmitLocation(CHIRExprWrapper(*chirExpr)); rawRet = HandleBinaryExpression(irBuilder, CHIRBinaryExprWrapper(*binaryExpr)); break; } case ExprMajorKind::MEMORY_EXPR: rawRet = HandleMemoryExpression(irBuilder, *chirExpr); break; case ExprMajorKind::OTHERS: if (chirExpr->IsConstant()) { rawRet = HandleConstantExpression(irBuilder, *StaticCast(chirExpr)); } else { rawRet = HandleOthersExpression(irBuilder, *chirExpr); } break; default: { CJC_ASSERT(false && "Should not reach here."); break; } } if (auto rst = chirExpr->GetResult(); rst && rawRet) { CGType* cgType = CGType::GetOrCreate(cgMod, rst->GetType()); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto& cgCtx = cgMod.GetCGContext(); // A value that is neither a reference nor a RefType, and if it is passed by a pointer, // we need to yield a new CGType for it. if (!rst->GetType()->IsRef() && !rst->GetType()->IsGeneric() && rawRet->getType()->isPointerTy() && !cgType->IsReference() && !cgType->IsOptionLikeRef()) { cgType = CGType::GetOrCreate(cgMod, CGType::GetRefTypeOf(cgCtx.GetCHIRBuilder(), *rst->GetType()), rawRet->getType()->getPointerAddressSpace()); } #endif CJC_NULLPTR_CHECK(chirExpr->GetTopLevelFunc()); bool isSRetArg = irBuilder.GetInsertCGFunction()->IsSRet() && chirExpr->GetTopLevelFunc()->GetReturnValue() == rst; cgMod.SetOrUpdateMappedCGValue(rst, std::make_unique(rawRet, cgType, isSRetArg)); } } } void EmitExpressionIR(CGModule& cgMod, std::vector chirExprs) { IRGenerator(cgMod, chirExprs).EmitIR(); } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/EmitExpressionIR.h000066400000000000000000000011421510705540100224620ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_EMITEXPRESSIONIR_H #define CANGJIE_EMITEXPRESSIONIR_H #include namespace Cangjie { namespace CHIR { class Expression; } namespace CodeGen { class CGModule; void EmitExpressionIR(CGModule& cgMod, std::vector chirExprs); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_EMITEXPRESSIONIR_H cangjie_compiler-1.0.7/src/CodeGen/EmitFunctionIR.cpp000066400000000000000000000237731510705540100224610ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "EmitFunctionIR.h" #include "Base/CGTypes/CGEnumType.h" #include "CGModule.h" #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "CJNative/CJNativeCGCFFI.h" #endif #include "DIBuilder.h" #include "EmitBasicBlockIR.h" #include "IRAttribute.h" #include "IRBuilder.h" #include "IRGenerator.h" #include "Utils/CGCommonDef.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Value.h" namespace { using namespace Cangjie; using namespace CodeGen; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND inline bool NeedProcessParamOnDemand(const CGModule& cgMod, const CHIR::FuncType& chirFuncTy, size_t index) { return cgMod.GetCGCFFI().NeedProcessParam(chirFuncTy, index); } inline llvm::Value* ProcessParamOnDemand( CGModule& cgMod, CHIR::Type& chirParamTy, llvm::Function::arg_iterator& arg, IRBuilder2& builder) { auto cgType = CGType::GetOrCreate(cgMod, &chirParamTy); if (chirParamTy.IsVArray()) { auto paramType = cgType->GetLLVMType(); return builder.CreateBitCast(arg, paramType->getPointerTo()); } auto place = builder.CreateEntryAlloca(*cgType); cgMod.GetCGCFFI().ProcessParam(chirParamTy, arg, place, builder); return place; } inline void InitIRBuilder( std::unique_ptr& builder, CGModule& cgMod, const CHIR::Func& chirFunc, llvm::Function& llvmFunc) { if (!builder) { builder = std::make_unique(cgMod); auto chirEntryBB = chirFunc.GetEntryBlock(); auto entryBB = llvm::BasicBlock::Create( cgMod.GetLLVMContext(), "entry:" + chirEntryBB->GetIdentifierWithoutPrefix(), &llvmFunc); builder->SetInsertPoint(entryBB); cgMod.SetOrUpdateMappedBB(chirEntryBB, entryBB); } } void HandleCFuncParams(CGModule& cgMod, const CHIR::Func& chirFunc, llvm::Function& llvmFunc) { auto chirFuncTy = chirFunc.GetFuncType(); std::unique_ptr builder; auto llvmArgIt = llvmFunc.arg_begin(); if (chirFunc.GetNumOfParams() == 0) { return; } if (llvmArgIt != llvmFunc.arg_end() && llvmArgIt->hasStructRetAttr()) { ++llvmArgIt; } for (size_t chirArgIdx = 0; chirArgIdx < chirFunc.GetNumOfParams(); ++chirArgIdx) { CJC_ASSERT(chirArgIdx < chirFunc.GetNumOfParams()); auto chirFuncArg = chirFunc.GetParam(chirArgIdx); auto chirFuncArgTy = chirFuncArg->GetType(); llvm::Value* llvmValue = nullptr; if (IsZeroSizedTypeInC(cgMod, *chirFuncArgTy)) { InitIRBuilder(builder, cgMod, chirFunc, llvmFunc); llvmValue = builder->CreateEntryAlloca(*CGType::GetOrCreate(cgMod, chirFuncArgTy)); } else { CJC_ASSERT(llvmArgIt != llvmFunc.arg_end()); llvmValue = llvmArgIt; if (NeedProcessParamOnDemand(cgMod, *chirFuncTy, chirArgIdx)) { InitIRBuilder(builder, cgMod, chirFunc, llvmFunc); llvmValue = ProcessParamOnDemand(cgMod, *chirFuncArgTy, llvmArgIt, *builder); } ++llvmArgIt; } FixedCGTypeOfFuncArg(cgMod, *chirFuncArg, *llvmValue); } } inline void HandleCFuncParams(CGModule& cgMod, const CHIR::Value& chirFunc, llvm::Function& llvmFunc) { if (chirFunc.IsFuncWithBody()) { HandleCFuncParams(cgMod, *VirtualCast(&chirFunc), llvmFunc); } else { // The remaining case can only be imported function, we needn't process params for it. CJC_ASSERT(chirFunc.IsImportedFunc()); } } #endif void HandleFuncParams(CGModule& cgMod, const CHIR::Func& chirFunc, const CGFunction& cgFunc) { for (size_t idx = 0; idx < chirFunc.GetNumOfParams(); ++idx) { auto chirFuncArg = chirFunc.GetParam(idx); auto llvmArg = cgFunc.GetArgByIndexFromCHIR(idx); auto fixedCGType = FixedCGTypeOfFuncArg(cgMod, *chirFuncArg, *llvmArg); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (chirFunc.IsConstructor() && idx == 0) { llvmArg->addAttr(llvm::Attribute::NoAlias); } #endif if (fixedCGType->IsStructPtrType() || fixedCGType->IsVArrayPtrType()) { llvmArg->addAttr(llvm::Attribute::NoAlias); } } } inline void BuildCFunc(CGModule& cgMod, const CHIR::Value& chirFunc, const CGFunction& cgFunc, IRBuilder2& builder) { CJC_ASSERT(chirFunc.GetType()->IsFunc() && StaticCast(chirFunc.GetType())->IsCFunc()); llvm::Function* llvmFunc = cgFunc.GetRawFunction(); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto chirFuncTy = StaticCast(chirFunc.GetType()); cgMod.GetCGCFFI().AddFunctionAttr(*chirFuncTy, *llvmFunc); #endif if (chirFunc.TestAttr(CHIR::Attribute::FOREIGN)) { return; } if (chirFunc.IsFuncWithBody()) { // CFFI wrap func should not set Personality auto funcNode = VirtualCast(&chirFunc); if (!funcNode->IsCFFIWrapper()) { llvmFunc->setPersonalityFn(cgMod.GetExceptionIntrinsicPersonality()); } else { auto scope = llvmFunc->getSubprogram(); auto curLoc = builder.GetCGModule().diBuilder->CreateDILoc(scope, {scope ? scope->getLine() : 0, 0}); builder.SetCurrentDebugLocation(curLoc); } } else { CJC_ASSERT(chirFunc.IsImportedFunc()); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND HandleCFuncParams(cgMod, chirFunc, *llvmFunc); #endif return; } } // namespace namespace Cangjie { namespace CodeGen { void BuildCJFunc(CGModule& cgMod, const CHIR::Func& chirFunc, const CGFunction& cgFunc) { llvm::Function* llvmFunc = cgFunc.GetRawFunction(); llvmFunc->setPersonalityFn(cgMod.GetExceptionIntrinsicPersonality()); HandleFuncParams(cgMod, chirFunc, cgFunc); } class FunctionGeneratorImpl : public IRGeneratorImpl { friend class CGModule; public: explicit FunctionGeneratorImpl(CGModule& cgMod, const CHIR::Func& chirFunc) : cgMod(cgMod), chirFunc(chirFunc) { } void EmitIR() override; private: CGModule& cgMod; const CHIR::Func& chirFunc; }; template <> class IRGenerator : public IRGenerator<> { public: IRGenerator(CGModule& cgMod, const CHIR::Func& chirFunc) : IRGenerator<>(std::make_unique(cgMod, chirFunc)) { } }; void FunctionGeneratorImpl::EmitIR() { IRBuilder2 builder(cgMod); auto cgFunc = cgMod.GetOrInsertCGFunction(&chirFunc); auto rawFunction = cgFunc->GetRawFunction(); cgMod.diBuilder->SetSubprogram(&chirFunc, rawFunction); auto chirFuncTy = chirFunc.GetFuncType(); CJC_NULLPTR_CHECK(chirFuncTy); if (chirFuncTy->IsCFunc()) { CJC_ASSERT(!chirFunc.TestAttr(CHIR::Attribute::FOREIGN)); BuildCFunc(cgMod, chirFunc, *cgFunc, builder); } else { BuildCJFunc(cgMod, chirFunc, *cgFunc); } if (!ShouldReturnVoid(chirFunc)) { SetSRetAttrForStructReturnType(*chirFuncTy, *rawFunction); } auto entryBB = chirFunc.GetBody()->GetEntryBlock(); EmitBasicBlockIR(cgMod, *entryBB); if (rawFunction->getSubprogram()) { cgMod.GetCGContext().debugValue = nullptr; cgMod.diBuilder->FinalizeSubProgram(*rawFunction); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND const auto& options = cgMod.GetCGContext().GetCompileOptions(); if ((options.enableTimer || options.enableMemoryCollect) && (chirFunc.GetGenericDecl() != nullptr)) { if (chirFunc.GetGenericDecl()->TestAttr(CHIR::Attribute::IMPORTED)) { cgFunc->GetRawFunction()->addFnAttr(GENERIC_DECL_IN_IMPORTED_PKG_ATTR); } else { cgFunc->GetRawFunction()->addFnAttr(GENERIC_DECL_IN_CURRENT_PKG_ATTR); } } if (chirFunc.IsLambda()) { cgFunc->GetRawFunction()->addFnAttr(FUNC_USED_BY_CLOSURE); } #endif } void EmitFunctionIR(CGModule& cgMod, const CHIR::Func& chirFunc) { IRGenerator(cgMod, chirFunc).EmitIR(); } void EmitFunctionIR(CGModule& cgMod, const std::vector& chirFuncs) { for (auto chirFunc : chirFuncs) { EmitFunctionIR(cgMod, *chirFunc); } } void EmitImportedCFuncIR(CGModule& cgMod, const std::vector& importedCFuncs) { IRBuilder2 builder(cgMod); for (auto importedCFunc : importedCFuncs) { auto chirType = importedCFunc->GetType(); CJC_ASSERT(chirType->IsFunc() && StaticCast(chirType)->IsCFunc()); auto chirFuncTy = StaticCast(chirType); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto llvmFuncTy = cgMod.GetCGCFFI().GetCFuncType(*chirFuncTy); #endif CJC_NULLPTR_CHECK(llvmFuncTy); auto srcPkgName = importedCFunc->GetSourcePackageName(); const auto& funcPkgName = srcPkgName.empty() ? cgMod.GetCGContext().GetCurrentPkgName() : srcPkgName; // Case 1: the CFunc is an implicitly imported one, we need to use its wrapper func when calling it. if (funcPkgName != cgMod.GetCGContext().GetCurrentPkgName()) { continue; } // Case 2: the CFunc is a foreign function. auto cgFunc = cgMod.GetOrInsertCGFunction(importedCFunc); BuildCFunc(cgMod, *importedCFunc, *cgFunc, builder); // Adaption cffi wrapper name for incremental compilation // If the package of an imported variable is the current package, // the imported variable is from the previous compilation product. if (cgMod.GetCGContext().GetCompileOptions().enIncrementalCompilation && IsNonPublicCFunc(*chirFuncTy, *importedCFunc) && importedCFunc->TestAttr(CHIR::Attribute::NON_RECOMPILE)) { cgFunc->GetRawFunction()->addFnAttr(CodeGen::INCREMENTAL_CFUNC_ATTR); cgFunc->GetRawFunction()->addFnAttr(CodeGen::INTERNAL_CFUNC_ATTR); } SetSRetAttrForStructReturnType(*chirFuncTy, *cgFunc->GetRawFunction()); } } } // namespace CodeGen } // namespace Cangjie cangjie_compiler-1.0.7/src/CodeGen/EmitFunctionIR.h000066400000000000000000000013371510705540100221160ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_EMITFUNCTIONIR_H #define CANGJIE_EMITFUNCTIONIR_H #include namespace Cangjie { namespace CHIR { class Func; class ImportedFunc; } // namespace CHIR namespace CodeGen { class CGModule; void EmitFunctionIR(CGModule& cgMod, const std::vector& chirFuncs); void EmitImportedCFuncIR(CGModule& cgMod, const std::vector& importedCFuncs); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_EMITFUNCTIONIR_H cangjie_compiler-1.0.7/src/CodeGen/EmitGlobalVariableIR.cpp000066400000000000000000000061601510705540100235310ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "EmitGlobalVariableIR.h" #include "Base/ExprDispatcher/ExprDispatcher.h" #include "CGModule.h" #include "DIBuilder.h" #include "EmitExpressionIR.h" #include "IRBuilder.h" #include "IRGenerator.h" #include "cangjie/CHIR/Value.h" namespace Cangjie { namespace CodeGen { class GlobalVariableGeneratorImpl : public IRGeneratorImpl { public: GlobalVariableGeneratorImpl(CGModule& cgMod, const std::vector& chirGVs) : cgMod(cgMod), chirGVs(chirGVs) { } void EmitIR() override; private: CGModule& cgMod; const std::vector chirGVs; }; template <> class IRGenerator : public IRGenerator<> { public: IRGenerator(CGModule& cgMod, const std::vector& chirGVs) : IRGenerator<>(std::make_unique(cgMod, chirGVs)) { } }; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void GlobalVariableGeneratorImpl::EmitIR() { IRBuilder2 irBuilder(cgMod); std::set quickGVs(chirGVs.begin(), chirGVs.end()); for (auto chirGV : cgMod.GetCGContext().GetCHIRPackage().GetGlobalVars()) { auto rawGV = llvm::cast(cgMod.GetOrInsertGlobalVariable(chirGV)->GetRawValue()); cgMod.diBuilder->CreateGlobalVar(*chirGV); if (quickGVs.find(chirGV) == quickGVs.end()) { continue; } const auto align = cgMod.GetLLVMModule()->getDataLayout().getPrefTypeAlignment(rawGV->getType()); rawGV->setAlignment(llvm::MaybeAlign(align)); if (auto literal = chirGV->GetInitializer()) { auto literalValue = HandleLiteralValue(irBuilder, *literal); if (literal->GetType()->IsString()) { cgMod.GetCGContext().AddCJString( rawGV->getName().str(), StaticCast(literal)->GetVal()); } else { rawGV->setInitializer(llvm::cast(literalValue)); } if (chirGV->TestAttr(CHIR::Attribute::READONLY)) { rawGV->addAttribute(llvm::Attribute::ReadOnly); rawGV->setConstant(true); } } else { auto chirType = StaticCast(chirGV->GetType())->GetBaseType(); rawGV->setInitializer(llvm::cast(irBuilder.CreateNullValue(*chirType))); } if (!chirGV->GetParentCustomTypeDef()) { auto fieldMeta = llvm::MDTuple::get( cgMod.GetLLVMContext(), {llvm::MDString::get(cgMod.GetLLVMContext(), MangleType(*chirGV->GetType()))}); rawGV->setMetadata(GC_GLOBAL_VAR_TYPE, fieldMeta); } } } #endif void EmitGlobalVariableIR(CGModule& cgMod, const std::vector& chirGVs) { IRGenerator(cgMod, chirGVs).EmitIR(); } } // namespace CodeGen } // namespace Cangjie cangjie_compiler-1.0.7/src/CodeGen/EmitGlobalVariableIR.h000066400000000000000000000011651510705540100231760ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_EMITGLOBALVARIABLEIR_H #define CANGJIE_EMITGLOBALVARIABLEIR_H #include namespace Cangjie { namespace CHIR { class GlobalVar; } namespace CodeGen { class CGModule; void EmitGlobalVariableIR(CGModule& cgMod, const std::vector& chirGVs); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_EMITGLOBALVARIABLEIR_H cangjie_compiler-1.0.7/src/CodeGen/EmitPackageIR.cpp000066400000000000000000000573121510705540100222230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/CodeGen/EmitPackageIR.h" #include "llvm/IR/Verifier.h" #include "Base/CGTypes/CGEnumType.h" #include "Base/CGTypes/CGPrimitiveType.h" #include "CGModule.h" #include "CJNative/CGTypes/CGExtensionDef.h" #include "CJNative/CGTypes/EnumCtorTIOrTTGenerator.h" #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "CJNative/CHIRSplitter.h" #include "CJNative/CJNativeReflectionInfo.h" #endif #include "DIBuilder.h" #include "EmitFunctionIR.h" #include "EmitGlobalVariableIR.h" #include "IRBuilder.h" #include "IRGenerator.h" #include "Utils/CGCommonDef.h" #include "Utils/CGUtils.h" #include "cangjie/CHIR/Package.h" #include "cangjie/CHIR/Value.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/FrontendTool/IncrementalCompilerInstance.h" #include "cangjie/Utils/ProfileRecorder.h" #include "cangjie/Utils/TaskQueue.h" namespace Cangjie::CodeGen { void EmitMain(CGModule& cgMod); void CreatePackageInitResetFunction(CGModule& cgMod); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void RegisterExceptionRaiser(CGModule& cgMod); void InitializeCjStringLiteral(const CGModule& cgMod); void GenerateBinarySectionInfo(const CGModule& cgMod); void CreateLLVMUsedGVs(const CGModule& cgMod); void ReplaceFunction(CGModule& cgMod); void InlineFunction(CGModule& cgMod); #endif namespace { #ifdef CJ_SDK_VERSION const std::string CANGJIE_SDK_VERSION = CJ_SDK_VERSION; #else const std::string CANGJIE_SDK_VERSION = ""; #endif #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void GenerateExtensionDefs(CGModule& cgMod) { for (auto& def : cgMod.GetAllCGExtensionDefs()) { def->Emit(); // NOTE: Class with 'Object' parent or extend with non-inherited will not generate extensionDef. } std::vector content; for (auto def : cgMod.GetNonExternalExtensionDefs()) { (void)content.emplace_back(def); } if (!content.empty()) { auto extensionDefPrtTy = CGType::GetOrCreateExtensionDefPtrType(cgMod.GetLLVMContext()); content.emplace_back(llvm::ConstantPointerNull::get(extensionDefPrtTy)); // indicates the end of array auto extensionDefsArrayTy = llvm::ArrayType::get(extensionDefPrtTy, content.size()); auto allDefsVar = llvm::cast( cgMod.GetLLVMModule()->getOrInsertGlobal("NonExternalExtensionDefs", extensionDefsArrayTy)); allDefsVar->setInitializer(llvm::ConstantArray::get(extensionDefsArrayTy, content)); allDefsVar->setLinkage(llvm::GlobalValue::PrivateLinkage); allDefsVar->addAttribute("InnerTypeExtensions"); cgMod.GetCGContext().AddLLVMUsedVars(allDefsVar->getName().str()); } std::vector content2; for (auto def : cgMod.GetExternalExtensionDefs()) { (void)content2.emplace_back(def); } if (!content2.empty()) { auto extensionDefPrtTy = CGType::GetOrCreateExtensionDefPtrType(cgMod.GetLLVMContext()); auto extensionDefsArrayTy = llvm::ArrayType::get(extensionDefPrtTy, content2.size()); auto allDefsVar = llvm::cast( cgMod.GetLLVMModule()->getOrInsertGlobal("ExternalExtensionDefs", extensionDefsArrayTy)); allDefsVar->setInitializer(llvm::ConstantArray::get(extensionDefsArrayTy, content2)); allDefsVar->setLinkage(llvm::GlobalValue::PrivateLinkage); allDefsVar->addAttribute("OuterTypeExtensions"); cgMod.GetCGContext().AddLLVMUsedVars(allDefsVar->getName().str()); } } std::vector TopologicalSortStaticGIs(CGContext& cgCtx, std::vector content) { auto indegree = cgCtx.GetIndegreeOfTypes(); auto& partialOrder = cgCtx.GetDependentPartialOrderOfTypes(); std::queue q; std::vector res; for (auto c : content) { if (auto it = indegree.find(c); it == indegree.end() || it->second == 0) { q.push(c); } } while (!q.empty()) { llvm::Constant* u = q.front(); q.pop(); res.emplace_back(u); for (auto c : content) { if (auto it = partialOrder.find(CGContext::PartialOrderPair{u, c}); it != partialOrder.end()) { if (auto& i = indegree.at(c); --i == 0) { q.push(c); } } } } CJC_ASSERT(content.size() == res.size()); return res; } void GenerateStaticGIs(CGModule& cgMod) { std::vector content; for (auto staticGI : cgMod.GetAliveStaticGIs()) { if (staticGI->getNumUses() > 0 || !staticGI->isLocalLinkage(staticGI->getLinkage())) { CJC_ASSERT(staticGI->hasInitializer()); (void)content.emplace_back(staticGI); } } for (auto& staticGIName : cgMod.GetCGContext().GetReflectGeneratedStaticGINames()) { if (auto staticGI = cgMod.GetLLVMModule()->getNamedGlobal(staticGIName)) { CJC_ASSERT(staticGI->hasInitializer()); if (find(content.begin(), content.end(), staticGI) == content.end()) { (void)content.emplace_back(staticGI); } } } if (content.empty()) { return; } content = TopologicalSortStaticGIs(cgMod.GetCGContext(), content); auto tiPtrType = CGType::GetOrCreateTypeInfoPtrType(cgMod.GetLLVMContext()); auto staticGIArrayType = llvm::ArrayType::get(tiPtrType, content.size()); auto staticGIs = llvm::cast( cgMod.GetLLVMModule()->getOrInsertGlobal("StaticGenericTIs", staticGIArrayType)); staticGIs->setInitializer(llvm::ConstantArray::get(staticGIArrayType, content)); staticGIs->setLinkage(llvm::GlobalValue::PrivateLinkage); staticGIs->addAttribute("CFileStaticGenericTI"); cgMod.GetCGContext().AddLLVMUsedVars(staticGIs->getName().str()); } #endif void KeepSomeTypesManually(CGModule& cgMod) { // 1) For BaseGen, if a signature of a type has only been used in metadata, // 2) For LLVM IR, if an `ObjLayout.xxx` has only been used in metadata, // ensure that the type will not be automatically cleared by LLVM. This method // is to build an internal function and apply for this type of memory in the // unreachable block of the function. NOTE that this function will never be // carried out. auto& cgCtx = cgMod.GetCGContext(); auto& llvmCtx = cgCtx.GetLLVMContext(); auto module = cgMod.GetLLVMModule(); auto functionType = llvm::FunctionType::get(llvm::Type::getVoidTy(llvmCtx), {}, false); auto function = llvm::cast( module->getOrInsertFunction(FOR_KEEPING_SOME_TYPES_FUNC_NAME, functionType).getCallee()); function->addFnAttr(llvm::Attribute::NoInline); function->addFnAttr(llvm::Attribute::OptimizeNone); AddLinkageTypeMetadata(*function, llvm::GlobalValue::PrivateLinkage, false); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND cgCtx.AddLLVMUsedVars(function->getName().str()); #endif auto entryBB = llvm::BasicBlock::Create(llvmCtx, "entry.unreachable", function); IRBuilder2 irBuilder(cgMod, entryBB); for (auto& structTypeName : cgCtx.GetGeneratedStructType()) { auto structType = llvm::StructType::getTypeByName(cgMod.GetLLVMContext(), structTypeName); if (structType && structType->isSized()) { (void)StaticCast(&irBuilder)->CreateAlloca(structType); } } (void)irBuilder.CreateRetVoid(); } void RecordCodeInfoInCodeGen(const std::string& suffix, const CGModule& cgMod) { auto& options = cgMod.GetCGContext().GetCompileOptions(); if (!options.enableTimer && !options.enableMemoryCollect) { return; } size_t allInstantiated = 0; size_t curPkgInstantiated = 0; size_t importedPkgInstantiated = 0; size_t funcNum = 0; for (auto& function : cgMod.GetLLVMModule()->functions()) { if (function.isDeclaration()) { continue; } funcNum++; auto count = function.getInstructionCount(); if (function.hasFnAttribute(GENERIC_DECL_IN_IMPORTED_PKG_ATTR)) { importedPkgInstantiated += count; allInstantiated += count; } else if (function.hasFnAttribute(GENERIC_DECL_IN_CURRENT_PKG_ATTR)) { curPkgInstantiated += count; allInstantiated += count; } } std::string suffixWithModuleName = suffix + "(" + cgMod.GetLLVMModule()->getSourceFileName() + ")"; static std::mutex profileMutex; std::lock_guard g{profileMutex}; Utils::ProfileRecorder::RecordCodeInfo( "all generic ins llvm ir after " + suffixWithModuleName, static_cast(allInstantiated)); Utils::ProfileRecorder::RecordCodeInfo( "imported generic ins llvm ir after " + suffixWithModuleName, static_cast(importedPkgInstantiated)); Utils::ProfileRecorder::RecordCodeInfo( "cur pkg generic ins llvm ir after " + suffixWithModuleName, static_cast(curPkgInstantiated)); Utils::ProfileRecorder::RecordCodeInfo( "llvm ir after " + suffixWithModuleName, cgMod.GetLLVMModule()->getInstructionCount()); Utils::ProfileRecorder::RecordCodeInfo( "llvm global func after " + suffixWithModuleName, static_cast(funcNum)); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void TransformFFIs(const CGModule& cgMod) { std::vector cFuncs; for (auto& func : cgMod.GetLLVMModule()->getFunctionList()) { if (func.hasFnAttribute(CodeGen::CFUNC_ATTR)) { func.removeFnAttr(CodeGen::CFUNC_ATTR); cFuncs.emplace_back(&func); } } } void SpecifyPackageInitFunc(const CGModule& cgMod) { auto& llvmCtx = cgMod.GetLLVMContext(); auto pkgInitFuncName = cgMod.GetCGContext().GetCHIRPackage().GetPackageInitFunc()->GetIdentifierWithoutPrefix(); cgMod.GetLLVMModule() ->getOrInsertNamedMetadata("pkg_init_func") ->addOperand(llvm::MDTuple::get(llvmCtx, llvm::MDString::get(llvmCtx, pkgInitFuncName))); cgMod.GetCGContext().AddLLVMUsedVars(pkgInitFuncName); } void SetSymbolLinkageType(const CGModule& cgMod) { auto llvmModule = cgMod.GetLLVMModule(); for (auto& globalObject : llvmModule->global_objects()) { if (!globalObject.hasMetadata("LinkageType")) { continue; } auto linkageType = GetLinkageTypeOfGlobalObject(globalObject); // 1. Clear LinkageType metadata globalObject.setMetadata("LinkageType", nullptr); if (globalObject.isDeclaration()) { continue; } // 2. Set linkageType if (llvm::GlobalValue::isWeakODRLinkage(linkageType) || llvm::GlobalValue::isLinkOnceLinkage(linkageType)) { globalObject.setLinkage(linkageType); } else if (llvm::GlobalValue::isLocalLinkage(linkageType)) { cgMod.GetCGContext().AddLocalizedSymbol(globalObject.getName().str()); } } } /* * @brief generate TypeInfo or TypeTemplate for every CustomDef in sub package. */ void EmitTIOrTTForCustomDefs(CGModule& cgMod) { auto& subCHIRPkg = cgMod.GetCGContext().GetSubCHIRPackage(); for (auto customDef : subCHIRPkg.chirCustomDefs) { if (!customDef->IsExtend()) { auto cgType = CGType::GetOrCreate(cgMod, customDef->GetType()); if (cgType->IsConcrete()) { cgType->GetOrCreateTypeInfo(); } if (customDef->IsGenericDef()) { cgType->GetOrCreateTypeTemplate(); } } if (customDef->TestAttr(CHIR::Attribute::IMPORTED) || customDef->TestAttr(CHIR::Attribute::NON_RECOMPILE)) { continue; } switch (customDef->GetCustomKind()) { case CHIR::CustomDefKind::TYPE_ENUM: { auto chirEnumType = StaticCast(customDef)->GetType(); auto cgEnumType = StaticCast(CGType::GetOrCreate(cgMod, chirEnumType)); if (!cgEnumType->IsTrivial() && !cgEnumType->IsZeroSizeEnum()) { const auto& ctors = chirEnumType->GetConstructorInfos(cgMod.GetCGContext().GetCHIRBuilder()); for (auto ctorIndex = 0U; ctorIndex < ctors.size(); ++ctorIndex) { EnumCtorTIOrTTGenerator(cgMod, *chirEnumType, ctorIndex).Emit(); } } break; } case CHIR::CustomDefKind::TYPE_CLASS: case CHIR::CustomDefKind::TYPE_STRUCT: case CHIR::CustomDefKind::TYPE_EXTEND: break; default: CJC_ASSERT(false && "Should not reach here."); return; } } } void EmitCJSDKVersion(const CGModule& cgMod) { auto& llvmCtx = cgMod.GetLLVMContext(); auto linkageType = llvm::GlobalValue::LinkageTypes::PrivateLinkage; auto strConstant = llvm::ConstantDataArray::getString(llvmCtx, CANGJIE_SDK_VERSION); auto cjSdkVersion = llvm::cast( cgMod.GetLLVMModule()->getOrInsertGlobal("cj.sdk.version", strConstant->getType())); cjSdkVersion->setInitializer(strConstant); cjSdkVersion->setAlignment(llvm::Align(1)); cjSdkVersion->setLinkage(linkageType); cgMod.GetCGContext().AddLLVMUsedVars(cjSdkVersion->getName().str()); } void GenSubCHIRPackage(CGModule& cgMod) { auto& cgPkgCtx = cgMod.GetCGContext().GetCGPkgContext(); auto& subCHIRPkg = cgMod.GetCGContext().GetSubCHIRPackage(); EmitTIOrTTForCustomDefs(cgMod); EmitGlobalVariableIR(cgMod, std::vector(subCHIRPkg.chirGVs.begin(), subCHIRPkg.chirGVs.end())); EmitFunctionIR(cgMod, std::vector(subCHIRPkg.chirFuncs.begin(), subCHIRPkg.chirFuncs.end())); EmitImportedCFuncIR(cgMod, std::vector(subCHIRPkg.chirImportedCFuncs.begin(), subCHIRPkg.chirImportedCFuncs.end())); if (subCHIRPkg.mainModule) { EmitCJSDKVersion(cgMod); EmitMain(cgMod); RegisterExceptionRaiser(cgMod); CreatePackageInitResetFunction(cgMod); SpecifyPackageInitFunc(cgMod); } GenerateExtensionDefs(cgMod); cgMod.GenTypeTemplate(); cgMod.GenTypeInfo(); auto& globalOptions = cgPkgCtx.GetGlobalOptions(); DumpIR(*cgMod.GetLLVMModule(), cgPkgCtx.GetCurrentPkgName() + "/00_subModules/" + cgMod.GetLLVMModule()->getSourceFileName(), globalOptions.codegenDebugMode); ReplaceFunction(cgMod); cgMod.Opt(); InlineFunction(cgMod); CJNativeReflectionInfo(cgMod, subCHIRPkg).Gen(); cgMod.GenTypeInfo(); // for reflect generated typeinfo cgMod.diBuilder->Finalize(); TransformFFIs(cgMod); InitializeCjStringLiteral(cgMod); GenerateBinarySectionInfo(cgMod); KeepSomeTypesManually(cgMod); DumpIR(*cgMod.GetLLVMModule(), cgPkgCtx.GetCurrentPkgName() + "/01_subModules/" + cgMod.GetLLVMModule()->getSourceFileName(), globalOptions.codegenDebugMode); cgMod.EraseUselessInstsAndDeclarations(); RecordCodeInfoInCodeGen("CodeGen EmitIR", cgMod); cgMod.GenIncremental(); SetSymbolLinkageType(cgMod); DumpIR(*cgMod.GetLLVMModule(), cgPkgCtx.GetCurrentPkgName() + "/02_subModules/" + cgMod.GetLLVMModule()->getSourceFileName(), globalOptions.codegenDebugMode); cgMod.EraseUselessGVAndFunctions(); GenerateStaticGIs(cgMod); CreateLLVMUsedGVs(cgMod); // Incremental compilation generates invalid IRs, which need to be deleted by EraseUselessGVAndFunctions. Therefore, // the verification is performed after incremental compilation. if (!cgMod.Verify()) { #ifndef NDEBUG std::string path = cgPkgCtx.GetCurrentPkgName() + "/brokenIR/" + cgMod.GetLLVMModule()->getSourceFileName(); DumpIR(*cgMod.GetLLVMModule(), path, true); InternalError("Broken llvm ir! The result is saved in " + path); #else InternalError("Broken llvm ir!"); #endif } DumpIR(*cgMod.GetLLVMModule(), cgPkgCtx.GetCurrentPkgName() + "/03_subModules/" + cgMod.GetLLVMModule()->getSourceFileName(), globalOptions.codegenDebugMode); RecordCodeInfoInCodeGen("CodeGen stage", cgMod); } #endif } // namespace class PackageGeneratorImpl : public IRGeneratorImpl { public: PackageGeneratorImpl(CHIR::CHIRBuilder& chirBuilder, const CHIRData& chirData, const GlobalOptions& options, bool enableIncrement, const CachedMangleMap& cachedMangleMap) : cgPkgCtx(chirBuilder, chirData, options, enableIncrement, cachedMangleMap) { } void EmitIR() override; std::vector> ReleaseLLVMModules() { return cgPkgCtx.ReleaseLLVMModules(); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND private: bool InitIncrementalGen() { bool ret = true; if (!cgPkgCtx.IsIncrementEnabled()) { return ret; } Utils::ProfileRecorder::Start("EmitIR", "InitIncrementalGen"); auto& cgMods = cgPkgCtx.GetCGModules(); size_t threadNum = cgPkgCtx.GetGlobalOptions().codegenDebugMode ? 1 : cgMods.size(); if (threadNum == 1) { for (auto& cgMod : cgMods) { ret = ret && cgMod->InitIncrementalGen(); } } else { Utils::TaskQueue taskQueueInitIncrementalGen(threadNum); std::vector> results; for (auto& cgMod : cgMods) { results.emplace_back( taskQueueInitIncrementalGen.AddTask([&cgMod]() { return cgMod->InitIncrementalGen(); })); } taskQueueInitIncrementalGen.RunAndWaitForAllTasksCompleted(); for (auto& result : results) { ret = ret && result.get(); } } Utils::ProfileRecorder::Start("EmitIR", "InitIncrementalGen"); return ret; } void GenSubCHIRPackages() { Utils::ProfileRecorder::Start("EmitIR", "GenSubCHIRPackages"); auto& cgMods = cgPkgCtx.GetCGModules(); size_t threadNum = cgPkgCtx.GetGlobalOptions().codegenDebugMode ? 1 : cgMods.size(); if (threadNum == 1) { for (auto& cgMod : cgMods) { GenSubCHIRPackage(*cgMod); } } else { Utils::TaskQueue taskQueueCHIRIR2LLVMIR(threadNum); for (auto& cgMod : cgMods) { taskQueueCHIRIR2LLVMIR.AddTask([&cgMod]() { GenSubCHIRPackage(*cgMod); }); } taskQueueCHIRIR2LLVMIR.RunAndWaitForAllTasksCompleted(); } Utils::ProfileRecorder::Stop("EmitIR", "GenSubCHIRPackages"); } #endif private: CGPkgContext cgPkgCtx; }; // Limit values[0] from values[1] to values[2], values' ty must be Integer. // The basic block is automatically inserted after the current insert block. llvm::Value* SaturatingIntegerValue(IRBuilder2& irBuilder, const std::vector& values, llvm::Type* type) { CJC_ASSERT(values.size() == 3); // 3 elements: src, min, max CJC_ASSERT(type->isIntegerTy()); auto [minBb, maxBb, checkOverBb] = Vec2Tuple<3>(irBuilder.CreateAndInsertBasicBlocks({"min.bb", "max.bb", "check.over"})); const int srcIndex = 0; const int minIndex = 1; const int maxIndex = 2; const int branchNum = 3; auto previousBB = irBuilder.GetInsertBlock(); auto downOverflow = irBuilder.CreateICmpSLT(values[srcIndex], values[minIndex]); auto upOverflow = irBuilder.CreateICmpSGT(values[srcIndex], values[maxIndex]); auto overflow = irBuilder.CreateOr(downOverflow, upOverflow); (void)irBuilder.CreateCondBr(overflow, minBb, checkOverBb); irBuilder.SetInsertPoint(minBb); (void)irBuilder.CreateCondBr(downOverflow, checkOverBb, maxBb); irBuilder.SetInsertPoint(maxBb); (void)irBuilder.CreateBr(checkOverBb); irBuilder.SetInsertPoint(checkOverBb); auto result = irBuilder.CreatePHI(type, branchNum, "result"); // 3 branches for phi. result->addIncoming(values[srcIndex], previousBB); result->addIncoming(values[minIndex], minBb); result->addIncoming(values[maxIndex], maxBb); return result; } llvm::Value* GenerateMainRetVal(IRBuilder2& irBuilder, llvm::Value* userMainRetVal) { auto i32Ty = llvm::Type::getInt32Ty(irBuilder.GetLLVMContext()); if (userMainRetVal->getType()->isVoidTy()) { return llvm::ConstantInt::get(i32Ty, 0); } else { auto i64Ty = llvm::Type::getInt64Ty(irBuilder.GetLLVMContext()); auto minValue = irBuilder.getInt64(static_cast(std::numeric_limits::min())); auto maxValue = irBuilder.getInt64(static_cast(std::numeric_limits::max())); auto result = SaturatingIntegerValue(irBuilder, {userMainRetVal, minValue, maxValue}, i64Ty); return irBuilder.CreateTrunc(result, i32Ty); } } llvm::Function* CreateMainFunc(const CGModule& cgMod) { auto& context = cgMod.GetCGContext().GetLLVMContext(); auto i32Ty = llvm::Type::getInt32Ty(context); auto i8PtrPtrTy = llvm::Type::getInt8PtrTy(context)->getPointerTo(); // Create @main func. auto module = cgMod.GetLLVMModule(); auto mainFunc = module->getFunction("main"); CJC_ASSERT(!mainFunc && "The main function is generated repeatedly."); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto voidType = llvm::Type::getVoidTy(context); auto mainFuncType = llvm::FunctionType::get(voidType, {i32Ty, i8PtrPtrTy}, false); mainFunc = llvm::cast(module->getOrInsertFunction("main", mainFuncType).getCallee()); #endif NameFunctionParam(mainFunc, {"argc", "argv"}); return mainFunc; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void PackageGeneratorImpl::EmitIR() { Utils::ProfileRecorder recorder("CodeGen", "EmitIR"); // Splitting CHIRPackage into n subCHIRPackages and Construct cgMods. CHIRSplitter chirSplitter(cgPkgCtx); auto subCHIRPkgs = chirSplitter.SplitCHIRPackage(); for (auto& subCHIRPkg : subCHIRPkgs) { auto cgMod = std::make_unique(subCHIRPkg, cgPkgCtx); cgPkgCtx.AddCGModule(cgMod); } // Reads the buffered bitcode. if (!InitIncrementalGen()) { return; } // Translate CHIR to LLVM IR GenSubCHIRPackages(); auto localizedSymbols = cgPkgCtx.GetLocalizedSymbols(); const_cast(cgPkgCtx.GetGlobalOptions()).symbolsNeedLocalized = std::vector(localizedSymbols.begin(), localizedSymbols.end()); cgPkgCtx.Clear(); } #endif #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::vector> GenPackageModules(CHIR::CHIRBuilder& chirBuilder, const CHIRData& chirData, const GlobalOptions& options, DefaultCompilerInstance& compilerInstance, bool enableIncrement) { CachedMangleMap cachedMangleMap; if (enableIncrement) { cachedMangleMap = StaticCast(compilerInstance).cacheMangles; } auto temp = PackageGeneratorImpl(chirBuilder, chirData, options, enableIncrement, cachedMangleMap); temp.EmitIR(); return temp.ReleaseLLVMModules(); } #endif bool SavePackageModule(const llvm::Module& module, const std::string& bcFilePath) { return SaveToBitcodeFile(module, bcFilePath); } void ClearPackageModules(std::vector>& packageModules) { std::unordered_set contexts; std::for_each(packageModules.begin(), packageModules.end(), [&contexts](std::unique_ptr& module) { if (module) { contexts.insert(&module->getContext()); } }); // Release module first, it will automatically unregistered from context. Then can safely release llvm context. packageModules.clear(); std::for_each(contexts.begin(), contexts.end(), [](llvm::LLVMContext* llvmCtx) { CJC_NULLPTR_CHECK(llvmCtx); delete llvmCtx; }); } } // namespace Cangjie::CodeGen cangjie_compiler-1.0.7/src/CodeGen/EraseUselessIRs.cpp000066400000000000000000000225671510705540100226430ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements codegen for erasing useless IRs. */ #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "CGModule.h" #include "IRBuilder.h" #include "cangjie/Utils/ProfileRecorder.h" using namespace Cangjie; using namespace CodeGen; namespace { const std::string MUST_RETAINED_VAR = "MustBeRetained"; // Indicates that a variable must be retained. inline bool HasNoUse(const llvm::Value& value) { return !value.hasNUsesOrMore(1); } void TryPropagateLocalConstantVars(llvm::Module& module) { // For the constants with local linkage type, they can be propagated // and then able to be eliminated in release compilation. for (auto& gv : module.globals()) { if (llvm::GlobalObject::isLocalLinkage(gv.getLinkage()) && gv.hasInitializer() && gv.isConstant()) { auto it = gv.user_begin(); while (it != gv.user_end()) { auto loadI = llvm::dyn_cast(*it); ++it; // to avoid iterator failure, let it self-increase first. if (loadI) { loadI->replaceAllUsesWith(gv.getInitializer()); (void)loadI->eraseFromParent(); } } } } } std::vector CollectInstructions( llvm::BasicBlock* block, const std::function& condition = nullptr) { std::vector collectedInsts; for (auto& inst : block->getInstList()) { if (!condition || condition(inst)) { collectedInsts.emplace_back(&inst); } } return collectedInsts; } std::vector CollectInstructions( llvm::Function* function, const std::function& condition = nullptr) { std::vector collectedInsts; for (auto& block : function->getBasicBlockList()) { auto bbRes = CollectInstructions(&block, condition); collectedInsts.insert(collectedInsts.end(), bbRes.begin(), bbRes.end()); } return collectedInsts; } void EraseUselessLoad(llvm::Function* function) { // Erase unused load instruction from function blocks. auto isUnusedLoad = [](const llvm::Instruction& inst) { return llvm::isa(inst) && HasNoUse(inst); }; std::vector insts = CollectInstructions(function, isUnusedLoad); std::for_each(insts.begin(), insts.end(), [](auto inst) { inst->eraseFromParent(); }); } void EraseTmpMetadata(llvm::Function* function) { std::vector insts = CollectInstructions(function); std::for_each(insts.begin(), insts.end(), [](const auto inst) { inst->setMetadata(MUST_RETAINED_VAR, nullptr); }); } void EraseUnreachableBBs(llvm::Function* function) { llvm::removeUnreachableBlocks(*function); } } // namespace void CGModule::MergeUselessBBIntoPreds(llvm::Function* function) const { // MergeBlockIntoPredecessor causes function UpdateCallingOrderOfInitFunction to not find filesInitBB if (function->getName().find(PKG_GV_INIT_PREFIX)) { return; } auto& tmpBlocks = function->getBasicBlockList(); std::vector blocks; std::for_each(tmpBlocks.begin(), tmpBlocks.end(), [&blocks](auto& block) { blocks.emplace_back(&block); }); // The first BB cannot be merged to any basic block, so erase it from `blocks`. blocks.erase(blocks.begin()); auto entryBB = &function->getEntryBlock(); // If a basic block has only one predecessor and the predecessor has only one successor, // merge the basic block with its predecessor. // The exception is that we do not want the content of the entry block to be // affected. Therefore, when the predecessor is an entry, we skip it. for (auto& block : blocks) { // We would like to save the basic blocks which are used to specify the `start` and `end` // of each global variable. if (block->getName().equals("init.files")) { continue; } auto uniquePreBB = block->getUniquePredecessor(); if (!uniquePreBB || uniquePreBB == entryBB) { continue; } // If the terminator of predecessor has debug location, // we skip it with '-g' or '--coverage' option to guarantee proper procedural behavior. if (GetCGContext().GetCompileOptions().enableCompileDebug || GetCGContext().GetCompileOptions().enableCoverage) { auto terminator = uniquePreBB->getTerminator(); CJC_NULLPTR_CHECK(terminator); if (terminator->getDebugLoc()) { continue; } } if (uniquePreBB->getUniqueSuccessor()) { llvm::MergeBlockIntoPredecessor(block); } } } namespace Cangjie::CodeGen { void EraseUselessAlloca(llvm::Function* function) { auto isUnusedAlloca = [](const llvm::Instruction& inst) { return llvm::isa(inst) && HasNoUse(inst); }; std::vector unusedAllocations = CollectInstructions(&function->getEntryBlock(), isUnusedAlloca); std::for_each(unusedAllocations.begin(), unusedAllocations.end(), [](auto inst) { inst->eraseFromParent(); }); } } // namespace Cangjie::CodeGen bool CGModule::CheckUnusedGV(const llvm::GlobalVariable* var, const std::set& llvmUsed) { return var->user_empty() && !cgCtx->IsGlobalsOfCompileUnit(var->getName().str()) && !IsLinkNameUsedInMeta(var->getName().str()) && !llvmUsed.count(var->getName().str()); } void CGModule::EraseUnusedGVs(const std::function extraCond) { auto& gvList = module->getGlobalList(); std::vector globals; std::for_each(gvList.begin(), gvList.end(), [&globals](auto& var) { globals.emplace_back(&var); }); auto& llvmUsed = GetCGContext().GetLLVMUsedVars(); for (auto var : globals) { var->removeDeadConstantUsers(); bool unused = CheckUnusedGV(var, llvmUsed); if (unused && extraCond && extraCond(*var)) { var->eraseFromParent(); } } } void CGModule::EraseUnusedFuncs(const std::function extraCond) { std::vector funcs; std::vector globals; auto& funcList = module->getFunctionList(); std::for_each(funcList.begin(), funcList.end(), [&funcs](auto& func) { funcs.emplace_back(&func); }); auto& gvList = module->getGlobalList(); std::for_each(gvList.begin(), gvList.end(), [&globals](auto& var) { globals.emplace_back(&var); }); auto& llvmUsed = GetCGContext().GetLLVMUsedVars(); bool erased; do { erased = false; for (auto varIt = globals.begin(); varIt != globals.end();) { auto var = *varIt; var->removeDeadConstantUsers(); bool unused = CheckUnusedGV(var, llvmUsed); if (unused && extraCond && extraCond(*var)) { var->eraseFromParent(); erased = true; } varIt = unused ? globals.erase(varIt) : varIt + 1; } for (auto funcIt = funcs.begin(); funcIt != funcs.end();) { auto func = *funcIt; func->removeDeadConstantUsers(); bool unused = func->user_empty() && !func->hasAddressTaken() && !IsLinkNameUsedInMeta(func->getName().str()) && !llvmUsed.count(func->getName().str()); // erase unused functions. if (unused && extraCond && extraCond(*func)) { func->eraseFromParent(); erased = true; } funcIt = unused ? funcs.erase(funcIt) : funcIt + 1; } } while (erased); } void CGModule::EraseUselessInstsAndDeclarations() { ClearLinkNameUsedInMeta(); auto& funcList = module->getFunctionList(); std::vector funcs; std::for_each(funcList.begin(), funcList.end(), [&funcs](auto& func) { funcs.emplace_back(&func); }); std::for_each(funcs.begin(), funcs.end(), [this](auto func) { // Function which does not have body and users can be erased from module. if (!func->isDeclaration() && !func->getName().equals(FOR_KEEPING_SOME_TYPES_FUNC_NAME)) { EraseUnreachableBBs(func); MergeUselessBBIntoPreds(func); EraseUselessLoad(func); EraseUselessAlloca(func); EraseTmpMetadata(func); } }); auto cond = [](const llvm::GlobalObject& gvOrFunc) { return gvOrFunc.isDeclaration(); }; EraseUnusedGVs(cond); EraseUnusedFuncs(cond); // Also clear the insertion point, as the previous insertion point // may become invalid after erasing some IRs. IRBuilder2 irBuilder(*this); irBuilder.ClearInsertionPoint(); } void CGModule::EraseUselessGVAndFunctions() { if (GetCGContext().GetCompileOptions().enableCompileDebug || GetCGContext().GetCompileOptions().enableCoverage) { return; } TryPropagateLocalConstantVars(*module); auto cond = [](const llvm::GlobalObject& gvOrFunc) { return llvm::Function::isLocalLinkage(gvOrFunc.getLinkage()) || gvOrFunc.isDeclaration(); }; ClearLinkNameUsedInMeta(); EraseUnusedGVs(cond); EraseUnusedFuncs(cond); } cangjie_compiler-1.0.7/src/CodeGen/IRAttribute.h000066400000000000000000000132741510705540100214600ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CODEGEN_IR_ATTRIBUTE_H #define CANGJIE_CODEGEN_IR_ATTRIBUTE_H #include "llvm/IR/Function.h" #include "llvm/IR/InstrTypes.h" #include "Utils/CGUtils.h" namespace Cangjie { namespace CodeGen { /********* Attribute adder for llvm::CallBase *********/ inline void AddAttributeAtIndex(llvm::CallBase* callBase, unsigned idx, llvm::Attribute attr) { callBase->addAttributeAtIndex(idx, attr); } inline void AddAttributeAtIndex(llvm::CallBase* callBase, unsigned idx, llvm::Attribute::AttrKind kind) { callBase->addAttributeAtIndex(idx, kind); } inline void AddParamAttr(llvm::CallBase* callBase, unsigned paramIdx, llvm::Attribute attr) { AddAttributeAtIndex(callBase, llvm::AttributeList::FirstArgIndex + paramIdx, attr); } inline void AddParamAttr(llvm::CallBase* callBase, unsigned paramIdx, llvm::Attribute::AttrKind kind) { AddAttributeAtIndex(callBase, llvm::AttributeList::FirstArgIndex + paramIdx, kind); } inline void AddRetAttr(llvm::CallBase* callBase, llvm::Attribute attr) { AddAttributeAtIndex(callBase, llvm::AttributeList::ReturnIndex, attr); } inline void AddRetAttr(llvm::CallBase* callBase, llvm::Attribute::AttrKind kind) { AddAttributeAtIndex(callBase, llvm::AttributeList::ReturnIndex, kind); } inline void AddSRetAttribute(llvm::CallBase* call) { auto retType = call->arg_begin()->get()->getType(); CJC_ASSERT(retType->isPointerTy()); auto type = GetPointerElementType(retType); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto sRetAttr = llvm::Attribute::getWithStructRetType(call->getContext(), type); AddParamAttr(call, 0, sRetAttr); #endif } inline void SetZExtAttrForCFunc(llvm::CallBase& call) { if (call.getFunctionType()->getReturnType()->isIntegerTy(1)) { AddRetAttr(&call, llvm::Attribute::ZExt); } uint32_t i = 0; for (auto arg = call.arg_begin(); arg != call.arg_end(); arg++, i++) { if (arg->get()->getType()->isIntegerTy(1)) { call.addParamAttr(i, llvm::Attribute::ZExt); } } } /********* Attribute adder for llvm::Function *********/ inline void AddAttributeAtIndex(llvm::Function* func, unsigned idx, llvm::Attribute attr) { func->addAttributeAtIndex(idx, attr); } inline void AddAttributeAtIndex(llvm::Function* func, unsigned idx, llvm::Attribute::AttrKind kind) { func->addAttributeAtIndex(idx, llvm::Attribute::get(func->getContext(), kind)); } inline void AddFnAttr(llvm::Function* func, llvm::Attribute attr) { AddAttributeAtIndex(func, llvm::AttributeList::FunctionIndex, attr); } inline void AddFnAttr(llvm::Function* func, llvm::Attribute::AttrKind kind) { AddAttributeAtIndex(func, llvm::AttributeList::FunctionIndex, kind); } inline void AddParamAttr(llvm::Function* func, unsigned paramIdx, llvm::Attribute attr) { AddAttributeAtIndex(func, llvm::AttributeList::FirstArgIndex + paramIdx, attr); } inline void AddParamAttr(llvm::Function* func, unsigned paramIdx, llvm::Attribute::AttrKind kind) { AddAttributeAtIndex(func, llvm::AttributeList::FirstArgIndex + paramIdx, kind); } inline void AddRetAttr(llvm::Function* func, llvm::Attribute attr) { AddAttributeAtIndex(func, llvm::AttributeList::ReturnIndex, attr); } inline void AddRetAttr(llvm::Function* func, llvm::Attribute::AttrKind kind) { AddAttributeAtIndex(func, llvm::AttributeList::ReturnIndex, kind); } /********* Attribute adder for llvm::Argument *********/ inline void AddSRetAttribute(llvm::Argument* value) { CJC_ASSERT(value->getType()->isPointerTy()); auto type = GetPointerElementType(value->getType()); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto sRet = llvm::Attribute::getWithStructRetType(value->getContext(), type); value->addAttr(sRet); #endif } inline void AddByValAttribute(llvm::Argument* value, uint64_t align = 8) { CJC_ASSERT(value->getType()->isPointerTy()); CJC_ASSERT(GetPointerElementType(value->getType())->isStructTy()); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto& llvmCtx = value->getContext(); auto byValAttr = llvm::Attribute::getWithByValType(llvmCtx, GetPointerElementType(value->getType())); value->addAttr(byValAttr); auto alignAttr = llvm::Attribute::getWithAlignment(llvmCtx, llvm::Align(align)); value->addAttr(alignAttr); #endif } inline bool ShouldReturnVoid(const CHIR::Func& func) { return Utils::In(func.GetFuncKind(), { CHIR::FuncKind::CLASS_CONSTRUCTOR, CHIR::FuncKind::PRIMAL_CLASS_CONSTRUCTOR, CHIR::FuncKind::STRUCT_CONSTRUCTOR, CHIR::FuncKind::PRIMAL_STRUCT_CONSTRUCTOR, CHIR::FuncKind::GLOBALVAR_INIT, CHIR::FuncKind::FINALIZER, }); } inline void SetSRetAttrForStructReturnType(const CHIR::FuncType& chirFuncTy, llvm::Function& llvmFunc) { bool shouldSetSRet = false; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (chirFuncTy.IsCFunc()) { // If the function is a CFunc, its SRet attribute has been determined whether to set. shouldSetSRet = llvmFunc.hasStructRetAttr(); } else if (llvmFunc.getReturnType()->isVoidTy()) { // Unit and Struct return type will be changed into Void type in IR, // and the return value is moved to the first place of the parameters shouldSetSRet = true; } #endif if (!llvmFunc.arg_empty() && shouldSetSRet) { llvmFunc.arg_begin()->addAttr(llvm::Attribute::NoAlias); AddSRetAttribute(llvmFunc.arg_begin()); } } } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CODEGEN_IR_ATTRIBUTE_H cangjie_compiler-1.0.7/src/CodeGen/IRBuilder.cpp000066400000000000000000000570341510705540100214400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "llvm/IR/DerivedTypes.h" #include "Base/CGTypes/CGType.h" #include "CGModule.h" #include "DIBuilder.h" #include "IRBuilder.h" namespace Cangjie { namespace CodeGen { IRBuilder2::IRBuilder2(CGModule& cgMod) : LLVMIRBuilder2(cgMod.GetLLVMContext()), cgMod(cgMod) { if (cgMod.GetCGContext().GetCompileOptions().fastMathMode) { llvm::FastMathFlags fmf; fmf.setFast(true); setFastMathFlags(fmf); } } CGContext& IRBuilder2::GetCGContext() const { return cgMod.GetCGContext(); } llvm::LLVMContext& IRBuilder2::GetLLVMContext() const { return cgMod.GetLLVMContext(); } llvm::CallBase* IRBuilder2::CreateCallOrInvoke( llvm::FunctionType* fTy, llvm::Value* callee, std::vector args) { // Emit the call or invoke instruction. auto unwindBlock = cgMod.GetCGContext().TopUnwindBlockStack(); if (unwindBlock.has_value()) { auto currentBB = GetInsertBlock(); auto normalDest = llvm::BasicBlock::Create(getContext(), "normalDest", currentBB->getParent()); normalDest->moveAfter(currentBB); auto resultVal = CreateInvoke(fTy, callee, normalDest, unwindBlock.value(), args); SetInsertPoint(normalDest); return resultVal; } else { return CreateCall(fTy, callee, args); } } llvm::CallBase* IRBuilder2::CreateCallOrInvoke(llvm::Function* callee, const llvm::ArrayRef args) { // Emit the call or invoke instruction. auto unwindBlock = cgMod.GetCGContext().TopUnwindBlockStack(); llvm::CallBase* callBase = nullptr; if (unwindBlock.has_value()) { auto currentBB = GetInsertBlock(); auto normalDest = llvm::BasicBlock::Create(getContext(), "normalDest", currentBB->getParent()); normalDest->moveAfter(currentBB); callBase = CreateInvoke(callee, normalDest, unwindBlock.value(), args); SetInsertPoint(normalDest); } else { callBase = CreateCall(callee, args); } if (callee->hasStructRetAttr()) { callBase->addAttributeAtIndex( static_cast(llvm::AttributeList::FirstArgIndex), llvm::Attribute::NoAlias); auto sRetAttr = llvm::Attribute::getWithStructRetType(callBase->getContext(), callee->getArg(0)->getParamStructRetType()); callBase->addAttributeAtIndex(static_cast(llvm::AttributeList::FirstArgIndex), sRetAttr); } return callBase; } // DebugLoc will be deleted. void IRBuilder2::SetInsertPointToAllocas(llvm::Function* curFunc) { auto entryBB = &curFunc->getEntryBlock(); if (entryBB->getName() != "allocas") { auto allocasBB = llvm::BasicBlock::Create(getContext(), "allocas", GetInsertFunction(), entryBB); SetInsertPoint(allocasBB); auto inst = CreateBr(entryBB); inst->setDebugLoc(llvm::DebugLoc()); entryBB = allocasBB; } SetInsertPoint(entryBB->getTerminator()); } llvm::AllocaInst* IRBuilder2::CreateEntryAlloca(llvm::Type* type, llvm::Value* arraySize, const llvm::Twine& name) { CJC_ASSERT(type); auto curPt = LLVMIRBuilder2::GetInsertPoint(); auto curBB = LLVMIRBuilder2::GetInsertBlock(); CJC_ASSERT(curBB && "Can't get the current basic block."); auto curFunc = curBB->getParent(); CJC_ASSERT(curFunc && "Can't get the current function."); auto currentLoc = getCurrentDebugLocation(); SetInsertPointToAllocas(curFunc); auto allocaInst = LLVMIRBuilder2::CreateAlloca(type, arraySize, name); LLVMIRBuilder2::SetInsertPoint(curBB, curPt); SetCurrentDebugLocation(currentLoc); return allocaInst; } llvm::Instruction* IRBuilder2::CreateEntryAlloca(const CGType& cgType, const llvm::Twine& name) { if (!GetCGContext().GetCompileOptions().disableInstantiation || cgType.GetSize()) { auto allocatedType = cgType.IsCGFunction() ? dynamic_cast(cgType).GetLLVMFunctionType()->getPointerTo() : cgType.GetLLVMType(); auto allocaInst = CreateEntryAlloca(allocatedType, nullptr, name); auto& options = cgMod.GetCGContext().GetCompileOptions(); if (allocatedType->isStructTy() && options.enableCompileDebug && options.optimizationLevel == GlobalOptions::OptimizationLevel::O0) { (void)CreateMemsetStructWith0(allocaInst); } return allocaInst; } if (cgType.GetOriginal().IsGeneric()) { return CreateEntryAlloca(getInt8PtrTy(1), nullptr, name); } llvm::Value* size = GetLayoutSize_32(cgType.GetOriginal()); auto allocaInst = CallIntrinsicAllocaGeneric({CreateTypeInfo(cgType.GetOriginal()), size}); allocaInst->setDebugLoc(llvm::DebugLoc()); return allocaInst; } llvm::Value* IRBuilder2::CreateLoad(const CGValue& cgVal, const llvm::Twine& name) { auto elementCGType = cgVal.GetCGType()->GetPointerElementType(); auto elementType = elementCGType->GetLLVMType(); if (cgMod.GetCGContext().GetCompileOptions().disableInstantiation) { if (auto& ori = elementCGType->GetOriginal(); !elementCGType->GetSize() && !ori.IsGeneric()) { auto ti = CreateTypeInfo(ori); auto payloadSize = GetLayoutSize_32(ori); auto tmp = CallIntrinsicAllocaGeneric({ti, payloadSize}); if (GetCGContext().GetBasePtrOf(cgVal.GetRawValue()) || cgVal.GetRawValue()->getType()->getPointerAddressSpace() == 0U) { CreateMemCpy(GetPayloadFromObject(tmp), llvm::MaybeAlign(), *cgVal, llvm::MaybeAlign(), payloadSize); } else { CallIntrinsicAssignGeneric({tmp, *cgVal, ti}); } return tmp; } } auto* loadInst = CreateLoad(elementType, cgVal.GetRawValue(), name); if (auto& ori = elementCGType->GetOriginal(); ori.IsEnum()) { llvm::cast(loadInst)->setMetadata( "untrusted_ref", llvm::MDNode::get(cgMod.GetLLVMContext(), {})); } return loadInst; } llvm::Value* IRBuilder2::CreateStructGEP(llvm::Type* ty, llvm::Value* ptr, unsigned idx, const llvm::Twine& name) { return LLVMIRBuilder2::CreateConstInBoundsGEP2_32(ty, ptr, 0, idx, name); } llvm::Value* IRBuilder2::CreateVArrayGEP( const CGValue* varrayPtr, std::vector& idxList, const llvm::Twine& name) { (void)idxList.insert(idxList.begin(), getInt64(0)); auto varray = llvm::dyn_cast(varrayPtr->GetCGType()->GetPointerElementType()->GetLLVMType()); return LLVMIRBuilder2::CreateGEP(varray, varrayPtr->GetRawValue(), idxList, name); } llvm::BasicBlock* IRBuilder2::CreateEntryBasicBlock(llvm::Function* parent, const std::string& name) const { auto& functionBBs = parent->getBasicBlockList(); llvm::BasicBlock* oldEntryBB = nullptr; if (!functionBBs.empty()) { oldEntryBB = &functionBBs.front(); } return llvm::BasicBlock::Create(getContext(), name, parent, oldEntryBB); } std::vector IRBuilder2::CreateAndInsertBasicBlocks( const std::initializer_list& basicBlocksName) const { std::vector ret; auto bb = GetInsertBlock(); auto function = bb->getParent(); for (auto& bbName : basicBlocksName) { auto it = ret.emplace_back(llvm::BasicBlock::Create(getContext(), bbName, function)); it->moveAfter(bb); bb = it; } return ret; } void IRBuilder2::EmitDeclare(const CHIR::Debug& debugNode, bool pointerWrapper) { CJC_ASSERT(cgMod.GetCGContext().GetCompileOptions().enableCompileDebug); cgMod.diBuilder->EmitDeclare(debugNode, *GetInsertBlock(), pointerWrapper); EmitLocation(CHIRExprWrapper(debugNode)); } void IRBuilder2::CreateLocalVarPointer(const CHIR::Debug& debugNode, const CGValue& cgValue) { CJC_ASSERT(cgMod.GetCGContext().GetCompileOptions().enableCompileDebug); auto cgType = cgValue.GetCGType(); auto arg = cgValue.GetRawValue(); auto tmpArg = CreateEntryAlloca( llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext(), 1), nullptr, debugNode.GetSrcCodeIdentifier() + ".debug"); auto inst = CreateStore(arg, tmpArg); llvm::cast(tmpArg)->setDebugLoc(llvm::DebugLoc()); CJC_NULLPTR_CHECK(inst); inst->setDebugLoc(llvm::DebugLoc()); cgMod.SetOrUpdateMappedCGValue(debugNode.GetValue(), std::make_unique(tmpArg, cgType)); EmitDeclare(debugNode); cgMod.SetValuesToLoad(debugNode.GetValue(), llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext(), 1), tmpArg); cgMod.SetOrUpdateMappedCGValue(debugNode.GetValue(), std::make_unique(arg, cgType)); } void IRBuilder2::CreateUnBoxDeclare(const CHIR::Debug& debugNode, const CGValue& cgValue) { // For capturedVars, should unbox from the class "$Captured_xxx", and store as a pointer. CJC_ASSERT(DeRef(*debugNode.GetOperand(0)->GetType())->IsClass()); auto capturedTy = StaticCast(DeRef(*debugNode.GetOperand(0)->GetType())) ->GetClassDef() ->GetDirectInstanceVar(0) .type; auto capturedCGType = CGType::GetOrCreate(cgMod, capturedTy); auto load = CreateLoad(cgValue); auto eleType = cgValue.GetCGType()->GetPointerElementType()->GetPointerElementType(); auto ret = GetPayloadFromObject(load); auto layoutType = dynamic_cast(eleType)->GetLayoutType(); ret = CreateBitCast(ret, layoutType->getPointerTo(1)); ret = LLVMIRBuilder2::CreateStructGEP(layoutType, ret, 0); CJC_ASSERT(ret->getType()->isPointerTy()); llvm::Value* alloca; if (capturedCGType->IsRefType() || capturedCGType->IsStructType()) { alloca = CreateEntryAlloca(getInt8PtrTy(1), nullptr, debugNode.GetSrcCodeIdentifier() + ".debug"); auto bitCast = CreateBitCast(ret, getInt8PtrTy(1)); CreateStore(bitCast, alloca); } else { alloca = CreateEntryAlloca(ret->getType(), nullptr, debugNode.GetSrcCodeIdentifier() + ".debug"); CreateStore(ret, alloca); } cgMod.diBuilder->EmitUnBoxDeclare( debugNode, *DeRef(*capturedTy), debugNode.GetSrcCodeIdentifier(), alloca, *GetInsertBlock()); EmitLocation(CHIRExprWrapper(debugNode)); } void IRBuilder2::CreateEnvDeclare(const CHIR::GetElementRef& getEleRef, llvm::Value* gep) { auto envTy = DeRef(*getEleRef.GetOperand(0)->GetType()); CJC_ASSERT(envTy->IsClass()); /* for capturedVar with var of CHIR is like: func (%1: env) { %2: $Captured_XXE&& = GetElementRef(%1, memberIndex) // this gep what we are translating now %3: $Captured_XXE& = Load(%2) ... %n: XX& = GetElementRef(%3, 0) } and for capturedVar with let of CHIR is like: func (%1: env) { %2: XXE& = GetElementRef(%1, memberIndex) // this gep what we are translating now } */ CJC_NULLPTR_CHECK(getEleRef.GetResultType()); auto retTy = DeRef(*getEleRef.GetResultType()); bool isCapturedWithVar = retTy->IsClass() && IsCapturedClass(*StaticCast(retTy)->GetClassDef()); auto name = StaticCast(envTy)->GetClassDef()->GetDirectInstanceVar(getEleRef.GetPath()[0]).name; if (isCapturedWithVar) { auto capturedClass = getEleRef.GetResultType(); // $Captured_XXE& auto load = CreateLoad(CGValue(gep, CGType::GetOrCreate(cgMod, capturedClass))); CJC_ASSERT(capturedClass->IsRef()); auto capturedVarPointer = CreateGEP( CGValue(load, CGType::GetOrCreate(cgMod, StaticCast(capturedClass)->GetBaseType())), {0}); auto capturedTy = StaticCast(DeRef(*capturedClass))->GetClassDef()->GetDirectInstanceVar(0).type; auto capturedCGType = CGType::GetOrCreate(cgMod, capturedTy); auto pointer = capturedCGType->IsRefType() || capturedCGType->IsStructType() ? getInt8PtrTy(1) : capturedVarPointer.GetRawValue()->getType(); auto ret = capturedCGType->IsRefType() || capturedCGType->IsStructType() ? CreateBitCast(capturedVarPointer.GetRawValue(), getInt8PtrTy(1)) : capturedVarPointer.GetRawValue(); auto alloca = CreateEntryAlloca(pointer, nullptr, name + ".debug"); CreateStore(ret, alloca); cgMod.diBuilder->EmitUnBoxDeclare(getEleRef, *DeRef(*capturedTy), name, alloca, *GetInsertBlock()); } else { // the result of gep must be a pointer. CJC_NULLPTR_CHECK(getEleRef.GetResultType()); CJC_ASSERT(getEleRef.GetResultType()->IsRef()); auto capturedCGType = CGType::GetOrCreate(cgMod, StaticCast(getEleRef.GetResultType())->GetBaseType()); auto pointer = capturedCGType->IsRefType() || capturedCGType->IsStructType() ? getInt8PtrTy(1) : gep->getType(); auto ret = capturedCGType->IsRefType() || capturedCGType->IsStructType() ? CreateBitCast(gep, getInt8PtrTy(1)) : gep; auto alloca = CreateEntryAlloca(pointer, nullptr, name + ".debug"); CreateStore(ret, alloca); cgMod.diBuilder->EmitUnBoxDeclare(getEleRef, *retTy, name, alloca, *GetInsertBlock()); } } llvm::Type* IRBuilder2::HandleArgPointerType(const CGType& argType) const { // If the pointer type is Ref type, it should be stored in addrSpace(1)**. // If the pointer type is Derive type, it should be stored in addrSpace(0)*. if (argType.IsRefType()) { return argType.GetPointerElementType()->GetLLVMType()->getPointerTo(1); } else { return GetPointerToWithSpecificAddrSpace(&argType, 0); } } void IRBuilder2::CreateValuePointer(const CHIR::Debug& debugNode, const CGValue& cgValue) { auto cgType = cgValue.GetCGType(); CJC_NULLPTR_CHECK(cgType); bool isRefType = cgType->IsRefType(); auto arg = cgValue.GetRawValue(); CJC_NULLPTR_CHECK(arg); auto argType = arg->getType(); CJC_NULLPTR_CHECK(argType); auto isDeriveType = argType->isPointerTy() && !isRefType; argType = cgType->IsPointerType() ? HandleArgPointerType(*cgType) : argType; // For deriveType, the argPtr should cast to addrSpace(0)* // AddrSpaceCast instruction can be used for store. llvm::Value* debugArg = isDeriveType ? CreateAddrSpaceCast(arg, argType) : arg; auto tmpArg = CreateEntryAlloca(argType, nullptr, arg->getName() + ".debug"); auto inst = CreateStore(debugArg, tmpArg); // Parameter info should not have debug location, and we set an empty location for it. llvm::cast(tmpArg)->setDebugLoc(llvm::DebugLoc()); CJC_NULLPTR_CHECK(inst); inst->setDebugLoc(llvm::DebugLoc()); cgMod.SetOrUpdateMappedCGValue(debugNode.GetValue(), std::make_unique(tmpArg, cgType)); EmitDeclare(debugNode); if (!isDeriveType) { cgMod.SetValuesToLoad(debugNode.GetValue(), argType, tmpArg); } cgMod.SetOrUpdateMappedCGValue(debugNode.GetValue(), std::make_unique(arg, cgType)); } void IRBuilder2::CreateBoxedValueForValueType(const CHIR::Debug& debugNode, const CGValue& cgValue) { auto curLoc = getCurrentDebugLocation(); SetCurrentDebugLocation(llvm::DebugLoc()); auto arg = cgValue.GetRawValue(); auto cgType = cgValue.GetCGType(); auto payloadSize = GetLayoutSize_32(*debugNode.GetValue()->GetType()); CJC_NULLPTR_CHECK(arg); auto argType = arg->getType(); // Alloca and store typeInfo auto tiValue = CreateTypeInfo(DeRef(*debugNode.GetValue()->GetType())); auto thisDebug = CallIntrinsicAllocaGeneric({tiValue, payloadSize}); thisDebug->setName(arg->getName() + ".generic.alloca"); // Store value for parameter auto ty = DeRef(*debugNode.GetValue()->GetType()); CJC_NULLPTR_CHECK(ty); if (debugNode.GetValue()->IsParameter()) { auto payload = GetPayloadFromObject(thisDebug); auto castedPtr = CreateBitCast(payload, argType->getPointerTo(1)); auto addrType = CGType::GetOrCreate( cgMod, CGType::GetRefTypeOf(cgMod.GetCGContext().GetCHIRBuilder(), *debugNode.GetValue()->GetType()), 1U); CreateStore(cgValue, CGValue(castedPtr, addrType)); if (ty->IsStruct()) { auto curCHIRFunc = DynamicCast(&GetInsertCGFunction()->GetOriginal()); CJC_NULLPTR_CHECK(curCHIRFunc); bool shouldUpdateThis = curCHIRFunc->GetSrcCodeIdentifier() == "init" || curCHIRFunc->TestAttr(CHIR::Attribute::MUT); cgMod.GetCGContext().debugValue = shouldUpdateThis && arg->getName() == "this" ? thisDebug : cgMod.GetCGContext().debugValue; } } // Create dbg declare. auto tempAlloca = thisDebug; bool pointerWrapper = false; bool isZeroSize = CGType::GetOrCreate(cgMod, ty)->GetSize() == 0; if (arg->getName() != "this" || !argType->isPointerTy() || isZeroSize) { tempAlloca = CreateEntryAlloca(getInt8PtrTy(1), nullptr, arg->getName() + ".debug"); CreateStore(thisDebug, tempAlloca); pointerWrapper = true; } else { thisDebug->setName(arg->getName() + ".debug"); } cgMod.SetOrUpdateMappedCGValue(debugNode.GetValue(), std::make_unique(tempAlloca, cgType)); EmitDeclare(debugNode, pointerWrapper); bool isStructThis = ty->IsStruct() && debugNode.GetSrcCodeIdentifier() == "this"; if (!debugNode.GetValue()->IsParameter() || isStructThis) { cgMod.SetBoxedValuesToLoad(debugNode.GetValue(), argType, thisDebug); } cgMod.SetOrUpdateMappedCGValue(debugNode.GetValue(), std::make_unique(arg, cgType)); SetCurrentDebugLocation(curLoc); } void IRBuilder2::CreateLoadInstForParameter(const CHIR::Expression& chirExpression) { for (auto operand : chirExpression.GetOperands()) { if (auto it = cgMod.GetValuesToLoad(operand); it.has_value()) { auto loadInst = LLVMIRBuilder2::CreateLoad(it->first, it->second); if (auto basePtr = GetCGContext().GetBasePtrOf(**(cgMod | operand))) { GetCGContext().SetBasePtr(loadInst, basePtr); } FixedCGTypeOfFuncArg(cgMod, *operand, *loadInst); } if (auto it = cgMod.GetBoxedValuesToLoad(operand); it.has_value()) { if (chirExpression.GetExprKind() == CHIR::ExprKind::STORE_ELEMENT_REF && chirExpression.GetOperand(1) == operand) { auto gep = CreateGEP(getInt8Ty(), it->second, getInt64(8)); auto castToCurrentValue = CreateBitCast(gep, it->first); FixedCGTypeOfFuncArg(cgMod, *operand, *castToCurrentValue); continue; } auto payload = GetPayloadFromObject(it->second); auto castedPtr = CreateBitCast(payload, llvm::cast(it->first)->getElementType()->getPointerTo(1)); if (chirExpression.GetExprKind() == CHIR::ExprKind::STORE && chirExpression.GetOperand(1) == operand) { FixedCGTypeOfFuncArg(cgMod, *operand, *castedPtr); continue; } CJC_ASSERT(castedPtr->getType()->isPointerTy() && it->first->isPointerTy()); llvm::Value* temp = castedPtr; if (llvm::cast(it->first)->getElementType()->isStructTy() && llvm::cast(it->first)->getAddressSpace() == 0) { auto elemType = llvm::cast(it->first)->getElementType(); temp = CreateEntryAlloca(llvm::cast(it->first)->getElementType(), nullptr, it->second->getName() + ".copied"); if (IsTypeContainsRef(elemType)) { auto structType = llvm::cast(elemType); auto layOut = GetLLVMModule()->getDataLayout().getStructLayout(structType); auto size = getInt64(layOut->getSizeInBytes()); CallGCReadAgg({temp, it->second, castedPtr, size}); } else { auto layOut = GetCGModule().GetLLVMModule()->getDataLayout().getStructLayout( llvm::cast(elemType)); CJC_NULLPTR_CHECK(layOut); auto size = layOut->getSizeInBytes(); auto align = layOut->getAlignment(); CreateMemCpy(temp, align, castedPtr, align, size); } } FixedCGTypeOfFuncArg(cgMod, *operand, *temp); } } } void IRBuilder2::CreateGenericParaDeclare(const CGFunction& cgFunc) { if (!cgMod.GetCGContext().GetCompileOptions().enableCompileDebug) { return; } auto chirFunc = dynamic_cast(&cgFunc.GetOriginal()); size_t genericIndex = 0; auto null = llvm::ConstantPointerNull::get(llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext())); // For member function only. if (cgFunc.GetCGFunctionType()->GetOuterTypeInfoIndex().has_value()) { auto outerDef = chirFunc->GetParentCustomTypeDef(); for (auto genericTy : outerDef->GetGenericTypeParams()) { cgMod.diBuilder->EmitGenericDeclare(*genericTy, null, *GetInsertBlock(), genericIndex); ++genericIndex; } } for (auto genericTy : chirFunc->GetGenericTypeParams()) { cgMod.diBuilder->EmitGenericDeclare(*genericTy, null, *GetInsertBlock(), genericIndex); ++genericIndex; } } void IRBuilder2::EmitLocation(const CHIRExprWrapper& debugNode) { if (!cgMod.GetCGContext().IsLineInfoEnabled()) { return; } llvm::DILocation* diLoc = cgMod.diBuilder->CreateDILoc(debugNode); SetCurrentDebugLocation(diLoc); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::Value* IRBuilder2::CallIntrinsicNull() const { return llvm::ConstantPointerNull::get(llvm::cast(CGType::GetRefType(GetLLVMContext()))); } #endif llvm::Value* IRBuilder2::CallIntrinsicIsNull(llvm::Value* value) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto rhs = CallIntrinsicNull(); CJC_NULLPTR_CHECK(rhs); return CreateICmpEQ(value, rhs); #endif } llvm::Value* IRBuilder2::CallIntrinsicIsNonNull(llvm::Value* value) { auto rhs = CallIntrinsicNull(); CJC_NULLPTR_CHECK(rhs); return CreateICmpNE(value, rhs); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::Value* IRBuilder2::CallIntrinsicRef2Null(llvm::Value* value) { return value; } #endif llvm::Value* IRBuilder2::CallClassIntrinsicInstanceOf( llvm::Value* instance, [[maybe_unused]] const CHIR::Type* targetTy) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto instanceInt8Ptr = CreateBitCast(instance, getInt8PtrTy()->getPointerTo(1)); auto instanceTi = CreateLoad(getInt8PtrTy(), instanceInt8Ptr); auto targetTiPtr = CreateTypeInfo(*targetTy); auto targetTi = CreateBitCast(targetTiPtr, llvm::Type::getInt8PtrTy(cgMod.GetLLVMContext())); return CallIntrinsicIsSubtype({instanceTi, targetTi}); #endif } void IRBuilder2::CallExceptionIntrinsicThrow(llvm::Value* exceptionValue) { auto throwFunc = GetExceptionIntrinsicThrow(); CJC_NULLPTR_CHECK(throwFunc); std::vector params{exceptionValue}; auto unwindBlock = cgMod.GetCGContext().TopUnwindBlockStack(); if (unwindBlock.has_value()) { auto currentBB = GetInsertBlock(); auto normalDest = llvm::BasicBlock::Create(getContext(), "normalDest", currentBB->getParent()); normalDest->moveAfter(currentBB); (void)CreateInvoke(throwFunc, normalDest, unwindBlock.value(), params); SetInsertPoint(normalDest); } else { (void)CreateCall(throwFunc, params); } } } // namespace CodeGen } // namespace Cangjie cangjie_compiler-1.0.7/src/CodeGen/IRBuilder.h000066400000000000000000000566541510705540100211140ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_IRBUILDER2_H #define CANGJIE_IRBUILDER2_H #include "llvm/IR/IRBuilder.h" #include "Base/CHIRExprWrapper.h" #include "CGModule.h" #include "cangjie/Utils/CheckUtils.h" namespace Cangjie { namespace CHIR { class Type; } namespace CodeGen { class CGModule; class CGContext; class CGValue; class CGFunction; class CGType; class CHIRIntrinsicWrapper; using LLVMIRBuilder2 = llvm::IRBuilder<>; class IRBuilder2 : public LLVMIRBuilder2 { public: explicit IRBuilder2(CGModule& cgMod); explicit IRBuilder2(CGModule& cgMod, llvm::BasicBlock* theBB) : LLVMIRBuilder2(theBB), cgMod(cgMod) { if (cgMod.GetCGContext().GetCompileOptions().fastMathMode) { llvm::FastMathFlags fmf; fmf.setFast(true); setFastMathFlags(fmf); } } void SetCHIRExpr(const CHIRExprWrapper* chirExprWrapper) { if (cgMod.GetCGContext().GetCompileOptions().disableInstantiation) { this->chirExpr = chirExprWrapper; this->cgFunction = cgMod.GetOrInsertCGFunction(chirExprWrapper->GetTopLevelFunc()); } } inline void SetInsertCGFunction(const CGFunction& cgFunc) { this->cgFunction = &cgFunc; } const CGFunction* GetInsertCGFunction() const { return cgFunction; } inline CGModule& GetCGModule() const { return cgMod; } CGContext& GetCGContext() const; llvm::LLVMContext& GetLLVMContext() const; inline llvm::Module* GetLLVMModule() const { return cgMod.GetLLVMModule(); } inline llvm::Value* GetTrue() const { return llvm::ConstantInt::getTrue(llvm::Type::getInt1Ty(GetLLVMContext())); } inline llvm::Value* GetFalse() const { return llvm::ConstantInt::getFalse(llvm::Type::getInt1Ty(GetLLVMContext())); } llvm::Function* GetInsertFunction() const { auto bb = GetInsertBlock(); return bb != nullptr ? bb->getParent() : nullptr; } llvm::AllocaInst* CreateEntryAlloca( llvm::Type* type, llvm::Value* arraySize = nullptr, const llvm::Twine& name = ""); llvm::Instruction* CreateEntryAlloca(const CGType& cgType, const llvm::Twine& name = ""); llvm::Value* CreateLoad(llvm::Type* elementType, llvm::Value* addr, const llvm::Twine& name = ""); llvm::Value* CreateLoad(const CGValue& cgVal, const llvm::Twine& name = ""); inline llvm::StoreInst* CreateStore(llvm::Value* val, llvm::Value* addr, bool isVolatile = false) { return LLVMIRBuilder2::CreateStore(val, addr, isVolatile); } llvm::StoreInst* CreateStore(llvm::Value* val, const CGValue& cgDestAddr, bool isVolatile = false); llvm::Instruction* CreateStore( llvm::Value* val, llvm::Value* ptr, const Cangjie::CHIR::Type* type, bool isVolatile = false); llvm::Instruction* CreateStore(const CGValue& cgVal, const CGValue& cgDestAddr); CGValue CreateGEP(const CGValue& cgVal, const std::vector& idxList, const llvm::Twine& name = ""); llvm::Value* CreateGEP( llvm::Type* type, llvm::Value* value, llvm::ArrayRef place, const llvm::Twine& name = ""); llvm::Value* CreateStructGEP(llvm::Type* ty, llvm::Value* ptr, unsigned idx, const llvm::Twine& name = ""); llvm::Value* CreateVArrayGEP(const CGValue* varrayPtr, std::vector& idxList, const llvm::Twine& name); inline llvm::BranchInst* CreateBr(llvm::BasicBlock* dest) { return LLVMIRBuilder2::CreateBr(dest); } inline llvm::BranchInst* CreateCondBr( llvm::Value* cond, llvm::BasicBlock* trueBranch, llvm::BasicBlock* falseBranch) { CJC_ASSERT(!GetInsertBlock()->getTerminator()); return LLVMIRBuilder2::CreateCondBr(cond, trueBranch, falseBranch); } llvm::BasicBlock* CreateEntryBasicBlock(llvm::Function* parent, const std::string& name) const; std::vector CreateAndInsertBasicBlocks( const std::initializer_list& basicBlocksName) const; private: inline llvm::CallInst* CreateCall(llvm::Function* callee, llvm::ArrayRef args = llvm::None, const llvm::Twine& name = "", llvm::MDNode* fpMathTag = nullptr) { return LLVMIRBuilder2::CreateCall(callee, args, name, fpMathTag); } inline llvm::CallInst* CreateCall(llvm::FunctionType* fTy, llvm::Value* callee, llvm::ArrayRef args = llvm::None, const llvm::Twine& name = "", llvm::MDNode* fpMathTag = nullptr) { if (auto f = llvm::dyn_cast(callee)) { return CreateCall(f, args, name, fpMathTag); } return LLVMIRBuilder2::CreateCall(fTy, callee, args, name, fpMathTag); } inline llvm::InvokeInst* CreateInvoke(llvm::Function* callee, llvm::BasicBlock* normalDest, llvm::BasicBlock* unwindDest, llvm::ArrayRef args = llvm::None, const llvm::Twine& name = "") { return LLVMIRBuilder2::CreateInvoke(callee, normalDest, unwindDest, args, name); } inline llvm::InvokeInst* CreateInvoke(llvm::FunctionType* fTy, llvm::Value* callee, llvm::BasicBlock* normalDest, llvm::BasicBlock* unwindDest, llvm::ArrayRef args = llvm::None, const llvm::Twine& name = "") { if (auto f = llvm::dyn_cast(callee)) { return CreateInvoke(f, normalDest, unwindDest, args, name); } return LLVMIRBuilder2::CreateInvoke(fTy, callee, normalDest, unwindDest, args, name); } public: llvm::CallBase* CreateCallOrInvoke(llvm::FunctionType* fTy, llvm::Value* callee, std::vector args); llvm::CallBase* CreateCallOrInvoke(llvm::Function* callee, const llvm::ArrayRef args = llvm::None); llvm::Value* CreateCallOrInvoke(const CGFunctionType& calleeType, llvm::Value* callee, std::vector args, bool isClosureCall = false, llvm::Value* thisTypeInfo = nullptr); llvm::Value* CreateCallOrInvoke( const CGFunction& callee, std::vector args = {}, bool isClosureCall = false) { return CreateCallOrInvoke( *dynamic_cast(callee.GetCGType()), callee.GetRawFunction(), args, isClosureCall); } void EmitDeclare(const CHIR::Debug& debugNode, bool pointerWrapper = false); void CreateLocalVarPointer(const CHIR::Debug& debugNode, const CGValue& cgValue); void CreateUnBoxDeclare(const CHIR::Debug& debugNode, const CGValue& cgValue); void CreateEnvDeclare(const CHIR::GetElementRef& getEleRef, llvm::Value* gep); void EmitLocation(const CHIRExprWrapper& debugNode); llvm::Type* HandleArgPointerType(const CGType& argType) const; void CreateValuePointer(const CHIR::Debug& debugNode, const CGValue& cgValue); void CreateBoxedValueForValueType(const CHIR::Debug& debugNode, const CGValue& cgValue); void CreateLoadInstForParameter(const CHIR::Expression& chirExpression); void CreateGenericParaDeclare(const CGFunction& cgFunc); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::Value* CreateBitCast(llvm::Value* value, llvm::Type* destTy, const llvm::Twine& name = "") { auto res = LLVMIRBuilder2::CreateBitCast(value, destTy, name); if (auto basePtr = GetCGContext().GetBasePtrOf(value)) { CJC_ASSERT(value->getType()->getPointerAddressSpace() == 1U); GetCGContext().SetBasePtr(res, basePtr); } return res; } /** Platform dependent */ llvm::Type* GetSizetLLVMType() const; size_t GetPtrSize() const; size_t GetPayloadOffset() const; CHIR::Type::TypeKind GetTypeKindFromType(const CHIR::Type& ty) const; /** GC Write Barrier Intrinsics */ bool IsGlobalVariableBasePtr(llvm::Value* val) const; llvm::Instruction* CallGCRead(std::vector args); llvm::Instruction* CallGCReadWeakRef(std::vector args); llvm::Instruction* CallGCReadAgg(std::vector args); llvm::Instruction* CallGCReadStaticRef(const std::vector& args); llvm::Instruction* CallGCReadStaticAgg(llvm::StructType* type, std::vector args); llvm::Instruction* CallGCWrite(std::vector args); llvm::Instruction* CallGCWriteAgg(std::vector args); llvm::Instruction* CallGCWriteStaticRef(const std::vector& args); llvm::Instruction* CallGCWriteStaticAgg(llvm::StructType* type, std::vector args); llvm::Instruction* CreateWriteBarrierForGlobalVariable(const CGValue& value, const CGValue& globalVar); /** * @brief Use `llvm.memset` to zero initialize the memory where a * struct pointer points to. */ llvm::CallInst* CreateMemsetStructWith0(llvm::AllocaInst* addr); /** * Since `llvm.memset` will be optimized if the llvm finds that the * object to be memset-ed is completely initialized later. * The FE and BE conventions use a customized `llvm.cj_memset`, and * the BE will ensure that it can be retained. */ llvm::CallInst* CreateCJMemSetStructWith0(llvm::AllocaInst* addr); #endif /** * Do not create allocations these ways. * Please use CreateEntryAlloca instead. */ llvm::AllocaInst* CreateAlloca( llvm::Type* type, unsigned addrSpace, llvm::Value* ArraySize = nullptr, const llvm::Twine& Name = "") = delete; llvm::AllocaInst* CreateAlloca( llvm::Type* type, llvm::Value* arraySize = nullptr, const llvm::Twine& name = "") = delete; /** * @brief Create a null value of a specific CHIR type. */ llvm::Value* CreateNullValue(const CHIR::Type& ty); void CreateOverflowOrArithmeticException(const std::string& ident, bool isOverflow = true); void CreateNegativeArraySizeException(); void CreateOutOfBoundException(); void CreateSpawnException(); void CreateRunTimeInvalidGenericParamException(); void CreateExecAtExitCallbacksCall(); void CreateGetCommandLineArgsCall(const std::vector& args); void CreateHandleExceptionCall(const std::vector& args); void CreateSetRuntimeCJThreadHandleCall(const std::vector& args); void CreateEPrintlnCall(const std::string& eMsg); llvm::AtomicCmpXchgInst* CreateAtomicCmpXchg(llvm::Value* Ptr, llvm::Value* Cmp, llvm::Value* New, llvm::AtomicOrdering SuccessOrdering, llvm::AtomicOrdering FailureOrdering, llvm::SyncScope::ID SSID = llvm::SyncScope::System) { return LLVMIRBuilder2::CreateAtomicCmpXchg( Ptr, Cmp, New, llvm::MaybeAlign(), SuccessOrdering, FailureOrdering, SSID); } llvm::Value* CallIntrinsicRef2Null(llvm::Value* value); llvm::Value* CreateStringLiteral(const std::string& str); void CallArrayInit( llvm::Value* arrPtr, llvm::Value* arrayLen, llvm::Value* elemValue, const CHIR::RawArrayType& arrTy); /// Intrinsics struct CodeGenFieldInfo { std::string className{""}; std::string qualifiedName{""}; std::string fieldName{""}; const CHIR::Type* fieldChirType; CGType* fieldType{nullptr}; llvm::Value* instance{nullptr}; const CHIR::Type& baseTy; CodeGenFieldInfo(const std::string className, const std::string& qualifiedName, const std::string fieldName, const CHIR::Type* fieldChirType, CGType* fieldType, llvm::Value* instance, const CHIR::Type& baseTy) : className(className), qualifiedName(qualifiedName), fieldName(fieldName), fieldChirType(fieldChirType), fieldType(fieldType), instance(instance), baseTy(baseTy) { } }; llvm::Value* CallIntrinsic( const CHIRIntrinsicWrapper& intrinsic, const std::vector& parameters, bool isC = false); llvm::Value* CallIntrinsic(const CHIRIntrinsicWrapper& intrinsic, const std::vector& args, const std::vector& tys = {}); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::Value* CallIntrinsicNull() const; #endif llvm::Value* CallIntrinsicIsNull(llvm::Value* value); llvm::Value* CallIntrinsicIsNonNull(llvm::Value* value); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::Value* CallIntrinsicFloatToIntegerSat(llvm::Type& destTy, llvm::Value& srcValue, bool isSigned); #endif llvm::Value* CallSetLocation(); ///*----------------- Spawn related --------------------// // Call runtime API to create a thread. llvm::Value* CallSpawnIntrinsic(CGValue& obj, CGFunction& cgFunc, std::optional& threadCtx, bool spawnWithFuture, llvm::Value* futureTI = nullptr); ///*----------------- Sync related --------------------// llvm::Value* CallSyncIntrinsics(const CHIRIntrinsicWrapper& intrinsic, const std::vector& parameters); ///*----------------- Overflow related --------------------// llvm::Value* GenerateOverflowCheckedFunc( CHIR::ExprKind opKind, const CHIR::Type& ty, std::vector& argGenValues); llvm::Value* GenerateDivLikeCheck(llvm::Value* dividend, llvm::Value* divisor, bool isSigned, bool isDiv); llvm::Value* GenerateOverflowSaturatingFunc( CHIR::ExprKind opKind, const CHIR::Type& ty, const std::vector& argGenValues); llvm::Value* GenerateOverflowWrappingFunc( CHIR::ExprKind opKind, const CHIR::Type& ty, std::vector& argGenValues); ///*----------------- Exception related --------------------// void CallExceptionIntrinsicThrow(llvm::Value* exceptionValue); void CallExceptionIntrinsicThrow(CGValue* exceptionValue) { CallExceptionIntrinsicThrow(exceptionValue->GetRawValue()); } llvm::Value* CallExceptionIntrinsicGetExceptionValue(); llvm::Value* CallPostThrowExceptionIntrinsic(llvm::Value* exceptionValue); ///*----------------- Runtime related --------------------// llvm::Value* GenerateCallExpectFunction(CGType* cgType, llvm::Value* val, llvm::Value* expectVal); llvm::Value* CallRuntimeIntrinsics(const CHIRIntrinsicWrapper& syscall, const std::vector& parameters); ///*----------------- Fill Or Get StackTrace related --------------------// llvm::Value* CallStackTraceIntrinsic(const CHIRIntrinsicWrapper& syscall, std::vector& parameters); ///*----------------- Math related --------------------// llvm::Value* CallMathIntrinsics(const CHIRIntrinsicWrapper& intrinsic, std::vector& parameters); ///*----------------- zeroValue related --------------------// llvm::Value* CallIntrinsicForUninitialized(const CHIR::Type& ty); ///*----------------- Array related --------------------// llvm::Value* CallArrayIntrinsicGetSize(llvm::Value* array); void CallArrayIntrinsicSet( const CHIR::RawArrayType& arrTy, llvm::Value* array, llvm::Value* index, CGValue& cgVal, bool isChecked); llvm::Value* CallArrayIntrinsicGet( const CHIR::RawArrayType& arrTy, llvm::Value* array, llvm::Value* index, bool isChecked); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::Value* GetArrayElementAddr( const CHIR::RawArrayType& arrTy, llvm::Value* array, llvm::Value* index, bool isChecked); #endif llvm::Value* CallArrayIntrinsicInitWithContent(const CHIR::RawArrayLiteralInit& arrayLiteralInit); /** * For CJNative, call gc write barrier if store ref to ref object's ref field. */ void CreateRefStore(CGValue* cgValue, llvm::Value* basePtr, llvm::Value* place, bool isBaseObjStruct); ///*----------------- unit test related --------------------// #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND ///*----------------- FFI OHOS related --------------------// llvm::Value* CallInteropIntrinsics(const CHIRIntrinsicWrapper& intrinsic, const std::vector& parameters); ///*----------------- VArray related --------------------// void CallVArrayIntrinsicIndexCheck(const CGValue* arrayPtr, std::vector& index); void CreateVArrayStore(CGValue* cgValue, llvm::Value* place); ///*----------------- Atomic related --------------------// llvm::Value* CallAtomicIntrinsics(const CHIRIntrinsicWrapper& intrinsic, const std::vector& args); llvm::Value* CallAtomicPrimitiveIntrinsics( const CHIRIntrinsicWrapper& intrinsic, const std::vector& args, llvm::Value* fieldPtr); ///*----------------- Array related --------------------// void CallArrayIntrinsicIndexCheck(llvm::Value* array, llvm::Value* index); ///*----------------- Vector operation related --------------------// llvm::Value* EmitVectorCompare32(llvm::Value* arr1, llvm::Value* offset1, llvm::Value* arr2, llvm::Value* offset2); llvm::Value* EmitVectorIndexByte32(llvm::Value* arr, llvm::Value* offset, llvm::Value* byte); // Since the parameters should be of type CGValue, and llvmValue cannot be converted to CGValue, // which comes from the llvm API, put them into nonStructParams. They MUST NOT be of Structure Type. llvm::Value* CallIntrinsicFunction(llvm::Type* retType, const std::string& name, const std::vector& parameters, const std::vector& attributes = {}); #endif ///*----------------- VArray related --------------------// llvm::Value* VArrayInitedByLambda(llvm::Value* varrayLen, CGValue& autoEnv, const CHIR::VArrayType& vArrayType); llvm::Value* VArrayInitedByItem(llvm::Value* varrayLen, const CGValue& cgVal, const CHIR::Type& vArrayType); ///*----------------- RawData related --------------------// llvm::Value* AcquireRawData(const CHIRIntrinsicWrapper& intrinsic); llvm::Value* ReleaseRawData(const CHIRIntrinsicWrapper& intrinsic); ///*----------------- Class related --------------------// #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::Value* CallClassIntrinsicAlloc(const CHIR::Type& type); llvm::Instruction* CallClassIntrinsicAlloc(const std::vector& parameters); #endif llvm::Value* CallClassIntrinsicInstanceOf(llvm::Value* instance, const CHIR::Type* targetTy); llvm::Value* CallArrayIntrinsicAllocWithConstantContent(llvm::Value* array, const std::vector& args, const CHIR::RawArrayType& arrTy, const std::string& serialized); llvm::Value* CreateEnumGEP(const CHIR::Field& field); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::Value* AllocateArray(const CHIR::RawArrayType& rawArrayType, llvm::Value* length); llvm::Function* GetArrayNonGenericElemMalloc() const; llvm::Function* GetArrayGenericElemMalloc() const; llvm::Value* InitArrayFilledWithConstant(llvm::Value* array, const CHIR::RawArrayType& arrTy, const std::vector& arrayConstantElems, const std::string& serialized); void CreateCopyTo(ArrayCopyToInfo arrayCopyToInfo); // Universal Generic Begin llvm::Value* GetSizeFromTypeInfo(llvm::Value* typeInfo); llvm::Value* GetLayoutSize_32(const CHIR::Type& type); llvm::Value* GetLayoutSize_64(const CHIR::Type& type); llvm::Value* GetSize_32(const CHIR::Type& type); llvm::Value* GetSize_64(const CHIR::Type& type); llvm::Value* GetSize_Native(const CHIR::Type& type); llvm::Value* GetAlign(const CHIR::Type& type, llvm::Type* targetType); llvm::Value* GetTypeKindFromTypeInfo(llvm::Value* typeInfo); llvm::Value* GetTypeForTypeParameter(llvm::Value* typeInfo); llvm::Value* GetTypeArgsFromTypeInfo(llvm::Value* typeInfo); llvm::Value* GetTypeInfoFromTiArray(llvm::Value* tiArray, std::size_t index, const std::string& name = ""); llvm::Value* GetTypeInfoFromObject(llvm::Value* obj); llvm::Value* GetPayloadFromObject(llvm::Value* obj); // `canChangeBB` is just used in `CJNativeExtensionDef.cpp`, it is a workaround for now :( // This parameter should be removed at some point! llvm::Value* CreateTypeInfo(const CHIR::Type& type, const std::unordered_map>& tiMap, bool canChangeBB = true); llvm::Value* CreateTypeInfo(const CHIR::Type& type, bool canChangeBB = true); llvm::Value* CreateTypeInfo(const CHIR::Type* type, bool canChangeBB = true); llvm::Value* CreateTypeInfoArray(const std::vector& types, const std::unordered_map>& map); llvm::Value* CreateTypeInfoArray(const std::vector& types); llvm::Instruction* CallIntrinsicGetTypeInfo(const std::vector& parameters); llvm::Instruction* CallIntrinsicGetFieldOffset(const std::vector& parameters); llvm::Instruction* CallIntrinsicIsSubtype(const std::vector& parameters); llvm::Instruction* CallIntrinsicIsTupleTypeOf(const std::vector& parameters); llvm::Instruction* CallIntrinsicIsTypeEqualTo(const std::vector& parameters); llvm::Instruction* CallIntrinsicAllocaGeneric(const std::vector& parameters); llvm::Instruction* CallIntrinsicGCWriteGeneric(const std::vector& parameters); llvm::Instruction* CallGCWriteGenericPayload(const std::vector& parameters); llvm::Instruction* CallGCReadGeneric(const std::vector& parameters); llvm::Instruction* CallIntrinsicMTable(const std::vector& parameters); llvm::Instruction* CallIntrinsicGetVTableFunc(llvm::Value* ti, llvm::Value* introTypeIdx, llvm::Value* funcOffset); llvm::Instruction* CallIntrinsicAssignGeneric(const std::vector& parameters); llvm::Value* CreateTypeInfoIsReferenceCall(llvm::Value* ti); llvm::Value* CreateTypeInfoIsReferenceCall(const CHIR::Type& chirType); // Universal Generic End /** * ** MOTIVATION: ** * Yield a specialized basic block as the entry or behind `allocas` to prepare * all TypeInfo in advance, to avoid creating the same typeinfo in different * blocks repeatedly. * ** NOTE: ** * This may seem like an increase in execution time at the beginning of the * function, but overall, you have a better chance of getting both code size and * performance benefits. * ** USAGE: ** * This method is used to set the insert point of a builder to this specialized * basic block. */ void SetInsertPointForPreparingTypeInfo(); #endif private: void CallIntrinsicIndexCheck(llvm::Value* len, llvm::Value* index); void SetInsertPointToAllocas(llvm::Function* curFunc); llvm::Function* GetExceptionIntrinsicThrow() const; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND llvm::Value* FixFuncArg(const CGValue& srcValue, const CGType& destType, bool isThisArgInStructMut = false); #endif llvm::Value* GetEnumTag(const CHIR::Field& field); llvm::Value* GetEnumAssociatedValue(const CHIR::Field& field); private: CGModule& cgMod; const CHIRExprWrapper* chirExpr{nullptr}; const CGFunction* cgFunction{nullptr}; }; class TemporarilySetDebugLocInThisScope { public: TemporarilySetDebugLocInThisScope(LLVMIRBuilder2& irBuilder, llvm::DebugLoc newLocation) : irBuilder(irBuilder), oldLocation(irBuilder.getCurrentDebugLocation()) { irBuilder.SetCurrentDebugLocation(newLocation); } ~TemporarilySetDebugLocInThisScope() { irBuilder.SetCurrentDebugLocation(oldLocation); } private: LLVMIRBuilder2& irBuilder; llvm::DebugLoc oldLocation; }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_IRBUILDER2_H cangjie_compiler-1.0.7/src/CodeGen/IRGenerator.h000066400000000000000000000024161510705540100214370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_IRGENERATOR_H #define CANGJIE_IRGENERATOR_H #include #include namespace Cangjie { namespace CHIR { class Package; class Func; class BasicBlock; class Expression; } // namespace CHIR namespace CodeGen { class IRGeneratorImpl { public: virtual ~IRGeneratorImpl() = default; virtual void EmitIR() = 0; protected: IRGeneratorImpl() = default; }; /** * Each `IRGenerator` instantiation needs an implementation class which inherits `IRGeneratorImpl`. * The implementation class can have its specific `EmitIR` method. */ template ::value>::type> class IRGenerator { public: void EmitIR() { impl->EmitIR(); } IRGenerator() = delete; protected: explicit IRGenerator(std::unique_ptr impl) : impl(std::move(impl)) { } std::unique_ptr impl; }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_IRGENERATOR_H cangjie_compiler-1.0.7/src/CodeGen/IncrementalGen/000077500000000000000000000000001510705540100217755ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CodeGen/IncrementalGen/CMakeLists.txt000066400000000000000000000016051510705540100245370ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB CodeGenIncrementalGen *.cpp) set(INCREMENTAL_COMPILATION_SRC ${CodeGenIncrementalGen}) # The libraries of llvm are not compiled with RTTI, since # some llvm classes without typeinfo are used in codegen # incremental compilation part, it is required to set # `-fno-rtti` flag to compile this part. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") add_library(CangjieCodeGenIncrementalGen OBJECT ${INCREMENTAL_COMPILATION_SRC}) # use llvm if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_dependencies(CangjieCodeGenIncrementalGen cjnative) endif() target_include_directories(CangjieCodeGenIncrementalGen PRIVATE ${LLVM_INCLUDE_DIRS}) cangjie_compiler-1.0.7/src/CodeGen/IncrementalGen/IncrementalGen.cpp000066400000000000000000000522141510705540100254000ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file introduces methods to support incremental compilation in CodeGen. * */ #include "IncrementalGen/IncrementalGen.h" #include #include #include "llvm/IR/DIBuilder.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IRReader/IRReader.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Transforms/Utils/Cloning.h" #include "Utils/CGCommonDef.h" #include "Utils/Constants.h" #include "cangjie/Utils/FileUtil.h" #ifdef _WIN32 #include "cangjie/Basic/StringConvertor.h" #endif using namespace Cangjie; using namespace Cangjie::CodeGen; namespace { const size_t SMALL_VECTOR_SIZE = 8; // The bodies of functions that used to initialize global variables or // to keep some type declarations should not be directly replaced as // a whole, but should be replaced from the basic block level. inline bool AllowFunctionBodyBeReplaced(const llvm::Function& func) { auto funcName = func.getName(); return !funcName.equals(FOR_KEEPING_SOME_TYPES_FUNC_NAME); } inline bool IsFileInitFuncName(const std::string& funcName) { return funcName.find(FILE_GV_INIT_PREFIX) == 0; } llvm::GlobalVariable* CopyGlobalVariableTo( llvm::Module& destModule, const std::string& name, const llvm::GlobalVariable& gv) { auto newGV = llvm::cast(destModule.getOrInsertGlobal(name, gv.getValueType())); // All the accompanying information of new global variable needs to be consistent with the old. newGV->setLinkage(gv.getLinkage()); newGV->setAlignment(gv.getAlign()); newGV->setUnnamedAddr(gv.getUnnamedAddr()); newGV->setConstant(gv.isConstant()); newGV->setVisibility(gv.getVisibility()); newGV->setAttributes(gv.getAttributes()); newGV->setThreadLocalMode(gv.getThreadLocalMode()); newGV->copyAttributesFrom(&gv); return newGV; } void UpdateCompileUnitWith(const llvm::Function& function, llvm::DICompileUnit& newCU) { if (auto oriSP = function.getSubprogram(); oriSP) { auto sp = function.getSubprogram(); sp->replaceUnit(&newCU); } } void DeleteFuncBody(llvm::Function* func) { CJC_NULLPTR_CHECK(func); for (llvm::BasicBlock& bb : *func) { bb.dropAllReferences(); } while (func->begin() != func->end()) { (void)func->begin()->eraseFromParent(); } func->clearMetadata(); } bool IsGVForExtensionDefs(const llvm::GlobalVariable& gv) { return gv.hasAttribute("InnerTypeExtensions") || gv.hasAttribute("OuterTypeExtensions"); } } // namespace IncrementalGen::IncrementalGen(bool cgParallelEnabled) : cgParallelEnabled(cgParallelEnabled) { } bool IncrementalGen::Init(const std::string& cachedIRPath, llvm::LLVMContext& llvmContext) { if (FileUtil::FileExist(cachedIRPath)) { llvm::SMDiagnostic err; #ifdef _WIN32 std::optional tempPath = StringConvertor::NormalizeStringToUTF8(cachedIRPath); CJC_ASSERT(tempPath.has_value() && "Incorrect file name encoding."); injectedModule = llvm::parseIRFile(tempPath.value(), err, llvmContext); #else injectedModule = llvm::parseIRFile(cachedIRPath, err, llvmContext); #endif if (injectedModule == nullptr) { Errorln("Illegal bitcode cache detected during incremental compilation."); return false; } return true; } else { Errorln("the cached bitcode file is lost."); return false; } } void IncrementalGen::InitCodeGenAddedCachedMap() { auto mds = injectedModule->getNamedMetadata("CodeGenAddedForIncr"); for (size_t i = 0; i < mds->getNumOperands(); i++) { auto md = mds->getOperand(i); auto key = llvm::dyn_cast_or_null(md->getOperand(0))->getString().str(); for (size_t j = 1; j < md->getNumOperands(); j++) { codegenAddedCachedMap[key].insert( llvm::dyn_cast_or_null(md->getOperand(j))->getString().str()); } } } std::vector IncrementalGen::GetIncrLLVMUsedNames() { return llvmUsedGVNames; } std::vector IncrementalGen::GetIncrCachedStaticGINames() { return staticGINames; } llvm::Module* IncrementalGen::LinkModules(llvm::Module* incremental, const CachedMangleMap& cachedMangles) { CJC_ASSERT(incremental != nullptr && injectedModule.get()); incrementalModule.reset(incremental); InitCodeGenAddedCachedMap(); for (auto& func : incrementalModule->functions()) { if (func.getSubprogram()) { func.getSubprogram()->replaceUnit(nullptr); } } // Step 0: Update changed decls from incremental module. UpdateCachedDeclsFromInjectedModule(cachedMangles); CopyDeclarationsToInjectedModule(); // Step 1: Fill valueMap. // valueMap.first: value in incremental module; valueMap.second: value in injected module. llvm::ValueToValueMapTy valueMap; FillValueMap(valueMap); // Step 2: Update injected module for the constant initializations of global variables. UpdateInitializationsOfGlobalVariables(valueMap); // Step 3: Update injected module for the function definitions. UpdateDefinitionsOfFunction(valueMap); // Step 4: Update the bodies of functions that used to keep some type declarations. UpdateBodyOfKeepTypesFunction(valueMap); // Step 5: Collect and erase useless functions in injected module. CollectUselessFunctions(); EraseUselessFunctions(); // Step 6: Update named metadata in injected module. UpdateReflectionMetadata(); UpdateCodeGenAddedMetadata(); UpdateIncrLLVMUsedNames(); return injectedModule.release(); } void IncrementalGen::UpdateCachedDeclsFromInjectedModule(const CachedMangleMap& cachedMangles) { for (auto& name : std::as_const(cachedMangles.incrRemovedDecls)) { if (auto oldF = injectedModule->getFunction(name)) { oldF->setName(name + "$useless$"); CollectUselessDefinitions(oldF); if (name.find("macroCall_c_") == 0 || name.find("macroCall_a_") == 0) { auto nameWrapper = name + "$real"; auto oldFWrapper = injectedModule->getFunction(nameWrapper); oldFWrapper->setName(nameWrapper + "$useless$"); CollectUselessDefinitions(oldFWrapper); } } else if (auto oldV = injectedModule->getNamedGlobal(name)) { oldV->setName(name + "$useless$"); CollectUselessDefinitions(oldV); } if (codegenAddedCachedMap.find(name) != codegenAddedCachedMap.end()) { for (auto& codegenAddedName : codegenAddedCachedMap[name]) { if (auto old = injectedModule->getNamedGlobal(codegenAddedName)) { old->setName(name + "$useless$"); CollectUselessDefinitions(old); } } codegenAddedCachedMap.erase(name); } } for (auto& name : std::as_const(cachedMangles.newExternalDecls)) { if (auto oldF = injectedModule->getFunction(name)) { AddLinkageTypeMetadata(*oldF, llvm::Function::ExternalLinkage, cgParallelEnabled); } else if (auto oldV = injectedModule->getNamedGlobal(name)) { AddLinkageTypeMetadata(*oldV, llvm::GlobalVariable::ExternalLinkage, cgParallelEnabled); } } for (auto& name : std::as_const(cachedMangles.importedInlineDecls)) { if (auto oldF = injectedModule->getFunction(name)) { DeleteFuncBody(oldF); AddLinkageTypeMetadata(*oldF, llvm::Function::ExternalLinkage, cgParallelEnabled); oldF->setDSOLocal(false); oldF->setPersonalityFn(nullptr); } else if (auto oldV = injectedModule->getNamedGlobal(name)) { AddLinkageTypeMetadata(*oldV, llvm::GlobalVariable::ExternalLinkage, cgParallelEnabled); oldV->setInitializer(nullptr); oldV->setDSOLocal(false); } } } void IncrementalGen::CopyDeclarationsToInjectedModule() { // Copy global variable declarations to injected module from incremental module. for (auto& gv : incrementalModule->globals()) { auto gvName = gv.getName(); if (IsGVForExtensionDefs(gv)) { size_t idx = 0U; std::string gvNameStr = gvName.str(); auto found = injectedModule->getNamedGlobal(gvNameStr + std::to_string(idx)); while (found) { ++idx; found = injectedModule->getNamedGlobal(gvNameStr + std::to_string(idx)); } found = llvm::cast( injectedModule->getOrInsertGlobal(gvNameStr + std::to_string(idx), gv.getValueType())); gv.setName(found->getName()); (void)CopyGlobalVariableTo(*injectedModule, found->getName().str(), gv); continue; } auto injectedGv = injectedModule->getNamedGlobal(gvName); if (injectedGv && (injectedGv->getType() != gv.getType() || (injectedGv->isConstant() != gv.isConstant() && !gv.isDeclaration()))) { injectedGv->setName(gvName + "$useless$"); injectedGv = nullptr; } if (injectedGv == nullptr) { (void)CopyGlobalVariableTo(*injectedModule, gvName.str(), gv); } } // Copy function declarations to injected module from incremental module. for (auto& func : *incrementalModule) { auto funcName = func.getName(); auto injectedFunc = injectedModule->getFunction(funcName); if (injectedFunc && injectedFunc->getFunctionType() != func.getFunctionType()) { injectedFunc->setName(funcName + "$useless$"); CollectUselessDefinitions(injectedFunc); injectedFunc = nullptr; } if (injectedFunc == nullptr) { auto newFunc = llvm::Function::Create( func.getFunctionType(), func.getLinkage(), func.getAddressSpace(), funcName, injectedModule.get()); newFunc->copyAttributesFrom(&func); } } } void IncrementalGen::FillValueMap(llvm::ValueToValueMapTy& valueMap) { for (llvm::Function& funcNew : *incrementalModule) { if (auto funcOld = injectedModule->getFunction(funcNew.getName()); funcOld) { valueMap[&funcNew] = funcOld; if (funcOld->arg_size() != funcNew.arg_size()) { continue; } auto destIt = funcOld->arg_begin(); for (const llvm::Argument& i : funcNew.args()) { destIt->setName(i.getName()); valueMap[&i] = &*destIt; ++destIt; } } } for (llvm::GlobalVariable& gv : incrementalModule->globals()) { if (auto existedGVInBase = injectedModule->getNamedGlobal(gv.getName()); existedGVInBase) { valueMap[&gv] = existedGVInBase; } } } void IncrementalGen::UpdateInitializationsOfGlobalVariables(llvm::ValueToValueMapTy& valueMap) { llvm::DIBuilder diBuilder(*injectedModule); auto compileUnitToBeUpdated = llvm::cast(injectedModule->getNamedMetadata("llvm.dbg.cu")->getOperand(0)); auto currentGlobalVars = compileUnitToBeUpdated->getGlobalVariables(); auto newGlobalVars = currentGlobalVars ? currentGlobalVars->clone() : diBuilder.getOrCreateArray({})->clone(); for (auto& gv : incrementalModule->globals()) { if (gv.hasAttribute("nonRecompile")) { gv.setAttributes(gv.getAttributes().removeAttribute(incrementalModule->getContext(), "nonRecompile")); continue; } auto gvToBeUpdated = injectedModule->getNamedGlobal(gv.getName()); CJC_NULLPTR_CHECK(gvToBeUpdated); if (IsGVForExtensionDefs(gv)) { gvToBeUpdated->setInitializer(llvm::MapValue(gv.getInitializer(), valueMap)); continue; } // 1. Update metadata gvToBeUpdated->clearMetadata(); llvm::SmallVector, SMALL_VECTOR_SIZE> metadatas; gv.getAllMetadata(metadatas); for (auto md : metadatas) { gvToBeUpdated->setMetadata(md.first, llvm::MapMetadata(md.second, valueMap)); } if (gv.getMetadata(llvm::LLVMContext::MD_dbg)) { auto gvToBeUpdatedDbgInfo = gvToBeUpdated->getMetadata(llvm::LLVMContext::MD_dbg); newGlobalVars->push_back(gvToBeUpdatedDbgInfo); } // 2. Update initializer if (!gv.isDeclaration()) { gvToBeUpdated->setInitializer(llvm::MapValue(gv.getInitializer(), valueMap)); gvToBeUpdated->setLinkage(gv.getLinkage()); } } auto permanentNode = llvm::MDNode::replaceWithPermanent(newGlobalVars->clone()); compileUnitToBeUpdated->replaceGlobalVariables(permanentNode); } void IncrementalGen::UpdateDefinitionsOfFunction(llvm::ValueToValueMapTy& valueMap) { for (auto& funcToBeInjected : *incrementalModule) { if (funcToBeInjected.isDeclaration()) { continue; // Declaration has no function body. } if (!AllowFunctionBodyBeReplaced(funcToBeInjected)) { continue; } auto funcName = funcToBeInjected.getName().str(); auto funcToBeUpdated = injectedModule->getFunction(funcName); DeleteFuncBody(funcToBeUpdated); llvm::SmallVector returns; llvm::CloneFunctionInto( funcToBeUpdated, &funcToBeInjected, valueMap, llvm::CloneFunctionChangeType::DifferentModule, returns); funcToBeUpdated->setLinkage(funcToBeInjected.getLinkage()); UpdateCompileUnitWith(*funcToBeUpdated, **injectedModule->debug_compile_units_begin()); } } void IncrementalGen::UpdateBodyOfKeepTypesFunction(llvm::ValueToValueMapTy& valueMap) { auto funcToKeepTypes = injectedModule->getFunction(FOR_KEEPING_SOME_TYPES_FUNC_NAME); for (const auto& bb : incrementalModule->getFunction(FOR_KEEPING_SOME_TYPES_FUNC_NAME)->getBasicBlockList()) { (void)llvm::CloneBasicBlock(&bb, valueMap, "", funcToKeepTypes); } } void IncrementalGen::CollectUselessDefinitions(llvm::GlobalObject* uselessDefinition) { if (uselessDefinition == nullptr) { return; } (void)uselessDefinitions.insert(uselessDefinition); } void IncrementalGen::CollectUselessFunctions() { auto collectCallerDef = [](auto& q, auto item) { // When erasing a definition, we also need to erase the definitions which use current definition. for (auto it : item->users()) { // The users only can be either of two types: 1).Instruction, 2). Constant // If user's type is Instruction, then we need to collect and erase the func where the instruction lives. // If user's type is Constant, then we need to collect and erase itself. if (auto inst = llvm::dyn_cast(it); inst) { auto callInst = llvm::dyn_cast(it); // In the case where the init function for a global variable is deleted during incremental mode, // we should only remove the call to that global variable's initialization function, // and not proceed to handle the function in which the call is made. if (callInst && IsFileInitFuncName(callInst->getFunction()->getName().str())) { callInst->eraseFromParent(); break; } else { q.push(inst->getFunction()); } } else { q.push(it); } } }; std::unordered_set removed; for (auto item : uselessDefinitions) { std::queue q; std::list willBeRemoved; q.push(item); while (!q.empty()) { auto current = q.front(); q.pop(); if (auto [_, success] = removed.emplace(current); !success) { continue; } willBeRemoved.emplace_front(current); collectCallerDef(q, current); } // Release GlobalObject's user before releasing the GlobalObject for (auto object : std::as_const(willBeRemoved)) { if (auto def = llvm::dyn_cast(object); def) { deferErase.emplace(def); def->clearMetadata(); // For those functions need to be erased, they may have invoking relationships, // Firstly, we erase their bodies, // Secondly, we erase the declarations to avoid unexpected pointers double-free. if (auto func = llvm::dyn_cast(object)) { DeleteFuncBody(func); } else if (auto gv = llvm::dyn_cast(object)) { gv->setInitializer(nullptr); } } } } uselessDefinitions.clear(); } void IncrementalGen::EraseUselessFunctions() { // Because normal funcs are from chir and the lambdas are calculated by codegen. // considering the call graph, we need to erase normal funcs first, and then lambdas. // 1. Erase the normal funcs which are useless from deferErase for (auto& item : deferErase) { item->eraseFromParent(); } // 2. Erase the lambdas which are useless bool erased; do { erased = false; auto f = injectedModule->begin(); while (f != injectedModule->end()) { auto cur = f++; bool isFromCFFI = (cur->hasFnAttribute(C2CJ_ATTR) || cur->hasFnAttribute(CJSTUB_ATTR)); if (!cur->hasFnAttribute(FUNC_USED_BY_CLOSURE)) { continue; } cur->removeDeadConstantUsers(); if (cur->user_empty() && (llvm::Function::isLocalLinkage(cur->getLinkage()) || isFromCFFI)) { cur->eraseFromParent(); erased = true; } } } while (erased); } void IncrementalGen::UpdateReflectionMetadata() { std::vector metaBoundByTI; std::vector metaBoundByTT; std::vector metaBoundByGV; for (auto& gv : injectedModule->getGlobalList()) { if (gv.isDeclaration()) { continue; } // Collect reflection ti/tt if (gv.hasMetadata("Reflection")) { if (gv.hasAttribute(TYPE_TEMPLATE_ATTR)) { (void)metaBoundByTT.emplace_back(gv.getMetadata("Reflection")); } else { (void)metaBoundByTI.emplace_back(gv.getMetadata("Reflection")); } } // Collect reflection global variable if (gv.hasMetadata("ReflectionGV")) { (void)metaBoundByGV.emplace_back(gv.getMetadata("ReflectionGV")); } } auto namedMDForTI = injectedModule->getOrInsertNamedMetadata(METADATA_TYPES); namedMDForTI->clearOperands(); for (auto meta : metaBoundByTI) { namedMDForTI->addOperand(meta); } auto namedMDForTT = injectedModule->getOrInsertNamedMetadata(METADATA_TYPETEMPLATES); namedMDForTT->clearOperands(); for (auto meta : metaBoundByTT) { namedMDForTT->addOperand(meta); } if (auto namedMD = injectedModule->getNamedMetadata(METADATA_GLOBAL_VAR)) { namedMD->clearOperands(); for (auto meta : metaBoundByGV) { namedMD->addOperand(meta); } } // Collect reflection function std::vector metaBoundByF; for (auto& f : injectedModule->getFunctionList()) { if (f.isDeclaration()) { continue; } if (f.hasMetadata("ReflectionFunc")) { (void)metaBoundByF.emplace_back(f.getMetadata("ReflectionFunc")); } } if (auto namedMD = injectedModule->getNamedMetadata(METADATA_FUNCTIONS)) { namedMD->clearOperands(); for (auto meta : metaBoundByF) { namedMD->addOperand(meta); } } } void IncrementalGen::UpdateCodeGenAddedMetadata() { if (auto namedMD = injectedModule->getNamedMetadata("CodeGenAddedForIncr"); namedMD) { if (auto incrNamedMD = incrementalModule->getNamedMetadata("CodeGenAddedForIncr"); incrNamedMD) { for (auto meta : incrNamedMD->operands()) { namedMD->addOperand(meta); } } } if (auto namedMD = injectedModule->getNamedMetadata("StaticGenericTIsForIncr"); namedMD) { if (auto incrNamedMD = incrementalModule->getNamedMetadata("StaticGenericTIsForIncr"); incrNamedMD) { for (auto meta : incrNamedMD->operands()) { namedMD->addOperand(meta); } } for (size_t i = 0; i < namedMD->getNumOperands(); i++) { auto md = namedMD->getOperand(i); staticGINames.emplace_back(llvm::dyn_cast_or_null(md->getOperand(0))->getString().str()); } } } void IncrementalGen::UpdateIncrLLVMUsedNames() { std::vector specialGVs = {"ExternalExtensionDefs", "NonExternalExtensionDefs"}; for (auto name : specialGVs) { if (injectedModule->getNamedGlobal(name) != nullptr) { llvmUsedGVNames.emplace_back(name); } } } cangjie_compiler-1.0.7/src/CodeGen/IncrementalGen/IncrementalGen.h000066400000000000000000000043541510705540100250470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_INCREMENTALGEN2_H #define CANGJIE_INCREMENTALGEN2_H #include #include #include "llvm/Transforms/Utils/ValueMapper.h" #include "cangjie/IncrementalCompilation/CachedMangleMap.h" namespace Cangjie::CodeGen { class IncrementalGen { public: explicit IncrementalGen(bool cgParallelEnabled); ~IncrementalGen() = default; bool Init(const std::string& cachedIRPath, llvm::LLVMContext& llvmContext); llvm::Module* LinkModules(llvm::Module* incremental, const CachedMangleMap& cachedMangles = {}); std::vector GetIncrLLVMUsedNames(); std::vector GetIncrCachedStaticGINames(); private: void InitCodeGenAddedCachedMap(); void UpdateCachedDeclsFromInjectedModule(const CachedMangleMap& cachedMangles); void CopyDeclarationsToInjectedModule(); void FillValueMap(llvm::ValueToValueMapTy& valueMap); void UpdateInitializationsOfGlobalVariables(llvm::ValueToValueMapTy& valueMap); void UpdateDefinitionsOfFunction(llvm::ValueToValueMapTy& valueMap); void UpdateBodyOfKeepTypesFunction(llvm::ValueToValueMapTy& valueMap); void CollectUselessFunctions(); void EraseUselessFunctions(); void CollectUselessDefinitions(llvm::GlobalObject* uselessDefinition); void UpdateReflectionMetadata(); void UpdateCodeGenAddedMetadata(); void UpdateIncrLLVMUsedNames(); const bool cgParallelEnabled; std::unique_ptr incrementalModule; std::unique_ptr injectedModule; // Key: decl name from CHIR // Value: codegen added variable name or codegen added function name for specific decl name std::unordered_map> codegenAddedCachedMap; std::unordered_set uselessDefinitions; std::unordered_set deferErase; std::vector llvmUsedGVNames; std::vector staticGINames; }; } // namespace Cangjie::CodeGen #endif // CANGJIE_INCREMENTALGEN2_H cangjie_compiler-1.0.7/src/CodeGen/Utils/000077500000000000000000000000001510705540100202025ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/CodeGen/Utils/BlockScopeImpl.h000066400000000000000000000036251510705540100232270ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares Block Scope for codegen. */ #ifndef CANGJIE_BLOCKSCOPEIMPL_H #define CANGJIE_BLOCKSCOPEIMPL_H #include "llvm/IR/Value.h" #include "IRBuilder.h" namespace Cangjie { namespace CHIR { class Func; } namespace CodeGen { class IRBuilder2; class CodeGenBlockScope { public: CodeGenBlockScope(IRBuilder2& irBuilder, llvm::BasicBlock* bb) : irBuilder(irBuilder), oldBb(irBuilder.GetInsertBlock()) { irBuilder.SetInsertPoint(bb); } CodeGenBlockScope(IRBuilder2& irBuilder, const CHIR::Block& chirBlock) : irBuilder(irBuilder), oldBb(irBuilder.GetInsertBlock()) { irBuilder.SetInsertPoint(irBuilder.GetCGModule().GetMappedBB(&chirBlock)); irBuilder.SetInsertCGFunction(*irBuilder.GetCGModule().GetOrInsertCGFunction(chirBlock.GetTopLevelFunc())); } ~CodeGenBlockScope() { irBuilder.SetInsertPoint(oldBb); } private: IRBuilder2& irBuilder; llvm::BasicBlock* oldBb; }; class CodeGenFunctionScope { public: CodeGenFunctionScope(IRBuilder2& irBuilder, llvm::Function* function) : block(irBuilder, &function->getEntryBlock()) { } ~CodeGenFunctionScope() = default; private: CodeGenBlockScope block; }; class CodeGenUnwindBlockScope { public: CodeGenUnwindBlockScope(CGModule& cgMod, llvm::BasicBlock* unwindBlock) : cgMod(cgMod) { cgMod.GetCGContext().PushUnwindBlockStack(unwindBlock); } ~CodeGenUnwindBlockScope() { cgMod.GetCGContext().PopUnwindBlockStack(); } private: CGModule& cgMod; }; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_BLOCKSCOPEIMPL_H cangjie_compiler-1.0.7/src/CodeGen/Utils/CGCommonDef.h000066400000000000000000000273041510705540100224420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGCOMMONDEF_H #define CANGJIE_CGCOMMONDEF_H /** * @file * * To declare the common definitions that are used in CodeGen */ #include #include "llvm/IR/GlobalObject.h" #include "llvm/IR/Intrinsics.h" #include "Utils/Constants.h" #include "cangjie/Basic/Linkage.h" #include "cangjie/CHIR/IntrinsicKind.h" #include "cangjie/CHIR/Type/Type.h" namespace Cangjie { namespace CodeGen { enum TypeInfoFields : uint8_t { TYPEINFO_NAME = 0, // mangledName : i8*. TYPEINFO_TYPE_KIND, // typeKind : i8 TYPEINFO_FLAG, // flag filled by llvm : i8 TYPEINFO_FIELDS_NUM, // number of member variables : i16 TYPEINFO_SIZE, // size of the type (unit: byte) : i32 TYPEINFO_GC_TIB, // which field is reference : %BitMap* TYPEINFO_UUID, // unique ID of a type in a process : i32 TYPEINFO_ALIGN, // alignment of the type : i8 TYPEINFO_TYPE_ARGS_NUM, // number of generic type args : i8 TYPEINFO_INHERITED_CLASS_NUM, // number of inherited classes : i16 TYPEINFO_OFFSETS, // i32* TYPEINFO_SOURCE_GENERIC, // i8* TYPEINFO_TYPE_ARGS, // i8 TYPEINFO_FIELDS, // typeInfos list of member variables : i8* TYPEINFO_SUPER, // typeinfo* TYPEINFO_EXTENSIONDEF_PTR, // %ExtensionDef* TYPEINFO_MTABLE, // i8* TYPEINFO_REFLECTION, // i8* TYPE_INFO_FIELDS_NUM // the number of TypeInfo's fields }; enum GenericTypeInfoFields : uint8_t { GENERIC_TYPEINFO_NAME = 0, // srcCodeIdentifier : i8* GENERIC_TYPEINFO_TYPE_KIND, // typeKind : i8 GENERIC_TYPEINFO_UPPERBOUNDS_NUM, // the number of generic upper bounds : i32 GENERIC_TYPEINFO_UPPERBOUNDS, // the list of upperbounds : i8* GENERIC_TYPE_INFO_FIELDS_NUM // the number of TypeInfo's fields }; enum GenericCustomTypeInfoFields : uint8_t { GENERIC_CUSTOM_TYPEINFO_NAME = 0, // i8* GENERIC_CUSTOM_TYPEINFO_TYPE_KIND, // typeKind : i8 GENERIC_CUSTOM_TYPEINFO_TYPE_ARGS_NUM, // i8 GENERIC_CUSTOM_TYPEINFO_TYPE_ARGS, // i8* GENERIC_CUSTOM_TYPEINFO_SOURCE_GENERIC, // i8* GENERIC_CUSTOM_TYPE_INFO_FIELDS_NUM // the number of TypeInfo's fields }; enum TypeTemplateFields : uint8_t { TYPETEMPLATE_NAME = 0, // mangledName : i8*. TYPETEMPLATE_TYPE_KIND, // typeKind : i8 TYPETEMPLATE_FLAG, // i8 TYPETEMPLATE_FIELDS_NUM, // number of member variables : i16 TYPETEMPLATE_TYPE_ARGS_NUM, // i8 TYPETEMPLATE_FIELDS_FNS, // typeInfos list of member variables : i8* TYPETEMPLATE_SUPER_FN, // i8* TYPETEMPLATE_FINALIZER, // i8* TYPETEMPLATE_REFLECTION, // i8* TYPETEMPLATE_EXTENSIONDEF_PTR, // %ExtensionDef* TYPETEMPLATE_INHERITED_CLASS_NUM, // i16 TYPE_TEMPLATE_FIELDS_NUM // the number of TypeTemplate's fields }; enum ExtensionDefFields : uint8_t { TYPE_PARAM_COUNT = 0, // i32 IS_INTERFACE_TI, // i8 TARGET_TYPE, // i8* INTERFACE_FN_OR_INTERFACE_TI, // i8* WHERE_CONDITION_FN, // i8* FUNC_TABLE, // i8* EXTENSION_DEF_FIELDS_NUM // the number of ExtensionDef's fields }; enum ArrayBaseFields : uint8_t { ARRAY_BASE_ARRAY_LEN, // array length : i64 ARRAY_BASE_FIELDS_NUM, // the number of ArrayBase's fields }; enum BitMapFields : uint8_t { BITMAP_NUMS = 0, // nums of bit map : i32 BITMAP_BITMAP, // bitmap : [0 x i8] BITMAP_FIELDS_NUM // the number of BitMap's fields }; struct IntrinsicFuncInfo { std::string funcName; std::vector attributes; }; struct GenericTypeAndPath { public: GenericTypeAndPath(const CHIR::GenericType& gt, std::vector path) : genericType(gt), path(path) { } const std::vector& GetPath() const { return path; } const CHIR::GenericType* GetGenericType() const { return &genericType; } private: const CHIR::GenericType& genericType; const std::vector path; }; const std::unordered_map> G_SIGNED_INT_MAP = { {CHIR::BooleanType::TypeKind::TYPE_INT8, {std::numeric_limits::min(), std::numeric_limits::max()}}, {CHIR::BooleanType::TypeKind::TYPE_INT16, {std::numeric_limits::min(), std::numeric_limits::max()}}, {CHIR::BooleanType::TypeKind::TYPE_INT32, {std::numeric_limits::min(), std::numeric_limits::max()}}, {CHIR::BooleanType::TypeKind::TYPE_INT64, {std::numeric_limits::min(), std::numeric_limits::max()}}}; const std::unordered_map G_UNSIGNED_INT_MAP = { {CHIR::Type::TypeKind::TYPE_UINT8, std::numeric_limits::max()}, {CHIR::Type::TypeKind::TYPE_UINT16, std::numeric_limits::max()}, {CHIR::Type::TypeKind::TYPE_UINT32, std::numeric_limits::max()}, {CHIR::Type::TypeKind::TYPE_UINT64, std::numeric_limits::max()}}; const std::set REFLECT_INTRINSIC_FUNC = {"CJ_MCC_ApplyCJInstanceMethod", "CJ_MCC_ApplyCJStaticMethod", "CJ_MCC_ApplyCJGenericInstanceMethod", "CJ_MCC_ApplyCJGenericStaticMethod", "CJ_MCC_CheckMethodActualArgs", "CJ_MCC_GetOrCreateTypeInfoForReflect"}; const std::unordered_map INTRINSIC_KIND_TO_FUNCNAME_MAP = { // future {CHIR::IntrinsicKind::FUTURE_INIT, {"CJ_MCC_FutureInit", {FAST_NATIVE_ATTR}}}, {CHIR::IntrinsicKind::FUTURE_IS_COMPLETE, {"CJ_MCC_FutureIsComplete", {FAST_NATIVE_ATTR}}}, {CHIR::IntrinsicKind::FUTURE_WAIT, {"CJ_MCC_FutureWait", {}}}, {CHIR::IntrinsicKind::FUTURE_NOTIFYALL, {"CJ_MCC_FutureNotifyAll", {FAST_NATIVE_ATTR}}}, {CHIR::IntrinsicKind::IS_THREAD_OBJECT_INITED, {"CJ_MCC_IsThreadObjectInited", {FAST_NATIVE_ATTR}}}, {CHIR::IntrinsicKind::GET_THREAD_OBJECT, {"CJ_MCC_GetCurrentCJThreadObject", {FAST_NATIVE_ATTR}}}, {CHIR::IntrinsicKind::SET_THREAD_OBJECT, {"CJ_MCC_SetCurrentCJThreadObject", {FAST_NATIVE_ATTR}}}, // sync {CHIR::IntrinsicKind::MUTEX_INIT, {"CJ_MCC_MutexInit", {FAST_NATIVE_ATTR}}}, {CHIR::IntrinsicKind::CJ_MUTEX_LOCK, {"CJ_MCC_MutexLock", {}}}, {CHIR::IntrinsicKind::MUTEX_TRY_LOCK, {"CJ_MCC_MutexTryLock", {FAST_NATIVE_ATTR}}}, {CHIR::IntrinsicKind::MUTEX_CHECK_STATUS, {"CJ_MCC_MutexCheckStatus", {FAST_NATIVE_ATTR}}}, {CHIR::IntrinsicKind::MUTEX_UNLOCK, {"CJ_MCC_MutexUnlock", {FAST_NATIVE_ATTR}}}, {CHIR::IntrinsicKind::WAITQUEUE_INIT, {"CJ_MCC_WaitQueueInit", {FAST_NATIVE_ATTR}}}, {CHIR::IntrinsicKind::MONITOR_INIT, {"CJ_MCC_WaitQueueForMonitorInit", {FAST_NATIVE_ATTR}}}, {CHIR::IntrinsicKind::MOITIOR_WAIT, {"CJ_MCC_MonitorWait", {}}}, {CHIR::IntrinsicKind::MOITIOR_NOTIFY, {"CJ_MCC_MonitorNotify", {FAST_NATIVE_ATTR}}}, {CHIR::IntrinsicKind::MOITIOR_NOTIFY_ALL, {"CJ_MCC_MonitorNotifyAll", {FAST_NATIVE_ATTR}}}, {CHIR::IntrinsicKind::MULTICONDITION_WAIT, {"CJ_MCC_MultiConditionMonitorWait", {}}}, {CHIR::IntrinsicKind::MULTICONDITION_NOTIFY, {"CJ_MCC_MultiConditionMonitorNotify", {FAST_NATIVE_ATTR}}}, {CHIR::IntrinsicKind::MULTICONDITION_NOTIFY_ALL, {"CJ_MCC_MultiConditionMonitorNotifyAll", {FAST_NATIVE_ATTR}}}, {CHIR::IntrinsicKind::SLEEP, {"CJ_MRT_Sleep", {}}}, // reflection #define REFLECTION_KIND_TO_RUNTIME_FUNCTION(REFLECTION_KIND, CJ_FUNCTION, RUNTIME_FUNCTION) \ {CHIR::IntrinsicKind::REFLECTION_KIND, {#RUNTIME_FUNCTION, {}}}, #include "cangjie/CHIR/LLVMReflectionIntrinsics.def" #undef REFLECTION_KIND_TO_RUNTIME_FUNCTION }; const std::unordered_map INTEGER_CONVERT_MAP = { {CHIR::Type::TypeKind::TYPE_UINT8, CHIR::Type::TypeKind::TYPE_INT8}, {CHIR::Type::TypeKind::TYPE_UINT16, CHIR::Type::TypeKind::TYPE_INT16}, {CHIR::Type::TypeKind::TYPE_UINT32, CHIR::Type::TypeKind::TYPE_INT32}, {CHIR::Type::TypeKind::TYPE_UINT64, CHIR::Type::TypeKind::TYPE_INT64}, {CHIR::Type::TypeKind::TYPE_UINT_NATIVE, CHIR::Type::TypeKind::TYPE_INT_NATIVE}}; const std::unordered_map INTRINSIC_KIND_TO_ID_MAP = { // runtime {CHIR::IntrinsicKind::GET_MAX_HEAP_SIZE, llvm::Intrinsic::cj_get_max_heap_size}, {CHIR::IntrinsicKind::GET_ALLOCATE_HEAP_SIZE, llvm::Intrinsic::cj_get_allocated_heap_size}, {CHIR::IntrinsicKind::GET_REAL_HEAP_SIZE, llvm::Intrinsic::cj_get_real_heap_size}, {CHIR::IntrinsicKind::GET_THREAD_NUMBER, llvm::Intrinsic::cj_get_thread_number}, {CHIR::IntrinsicKind::GET_BLOCKING_THREAD_NUMBER, llvm::Intrinsic::cj_get_blocking_thread_number}, {CHIR::IntrinsicKind::GET_NATIVE_THREAD_NUMBER, llvm::Intrinsic::cj_get_native_thread_number}, {CHIR::IntrinsicKind::DUMP_CJ_HEAP_DATA, llvm::Intrinsic::cj_dump_heap_data}, {CHIR::IntrinsicKind::GET_GC_COUNT, llvm::Intrinsic::cj_get_gc_count}, {CHIR::IntrinsicKind::GET_GC_TIME_US, llvm::Intrinsic::cj_get_gc_time_us}, {CHIR::IntrinsicKind::GET_GC_FREED_SIZE, llvm::Intrinsic::cj_get_gc_freed_size}, {CHIR::IntrinsicKind::START_CJ_CPU_PROFILING, llvm::Intrinsic::cj_start_cpu_profiling}, {CHIR::IntrinsicKind::STOP_CJ_CPU_PROFILING, llvm::Intrinsic::cj_stop_cpu_profiling}, {CHIR::IntrinsicKind::INVOKE_GC, llvm::Intrinsic::cj_invoke_gc}, {CHIR::IntrinsicKind::SET_GC_THRESHOLD, llvm::Intrinsic::cj_set_gc_threshold}, {CHIR::IntrinsicKind::CROSS_ACCESS_BARRIER, llvm::Intrinsic::cj_cross_access_barrier}, {CHIR::IntrinsicKind::CREATE_EXPORT_HANDLE, llvm::Intrinsic::cj_create_export_handle}, {CHIR::IntrinsicKind::GET_EXPORTED_REF, llvm::Intrinsic::cj_get_exported_ref}, {CHIR::IntrinsicKind::REMOVE_EXPORTED_REF, llvm::Intrinsic::cj_remove_exported_ref}, // math {CHIR::IntrinsicKind::ABS, llvm::Intrinsic::abs}, {CHIR::IntrinsicKind::POW, llvm::Intrinsic::pow}, {CHIR::IntrinsicKind::POWI, llvm::Intrinsic::powi}, {CHIR::IntrinsicKind::FABS, llvm::Intrinsic::fabs}, {CHIR::IntrinsicKind::FLOOR, llvm::Intrinsic::floor}, {CHIR::IntrinsicKind::CEIL, llvm::Intrinsic ::ceil}, {CHIR::IntrinsicKind::TRUNC, llvm::Intrinsic::trunc}, {CHIR::IntrinsicKind::SIN, llvm::Intrinsic::sin}, {CHIR::IntrinsicKind::COS, llvm::Intrinsic::cos}, {CHIR::IntrinsicKind::EXP, llvm::Intrinsic::exp}, {CHIR::IntrinsicKind::EXP2, llvm::Intrinsic::exp2}, {CHIR::IntrinsicKind::LOG, llvm::Intrinsic::log}, {CHIR::IntrinsicKind::LOG2, llvm::Intrinsic::log2}, {CHIR::IntrinsicKind::LOG10, llvm::Intrinsic::log10}, {CHIR::IntrinsicKind::SQRT, llvm::Intrinsic::sqrt}, {CHIR::IntrinsicKind::ROUND, llvm::Intrinsic::round}, // atomic {CHIR::IntrinsicKind::ATOMIC_STORE, llvm::Intrinsic::cj_atomic_store}, {CHIR::IntrinsicKind::ATOMIC_COMPARE_AND_SWAP, llvm::Intrinsic::cj_atomic_compare_swap}, {CHIR::IntrinsicKind::ATOMIC_LOAD, llvm::Intrinsic::cj_atomic_load}, {CHIR::IntrinsicKind::ATOMIC_SWAP, llvm::Intrinsic::cj_atomic_swap}, {CHIR::IntrinsicKind::BLACK_BOX, llvm::Intrinsic::cj_blackhole}, }; bool IsCHIRWrapper(const std::string& chirFuncName); llvm::GlobalValue::LinkageTypes CHIRLinkage2LLVMLinkage(Linkage chirLinkage); void AddLinkageTypeMetadata( llvm::GlobalObject& globalObject, llvm::GlobalValue::LinkageTypes linkageType, bool markByMD); llvm::GlobalValue::LinkageTypes GetLinkageTypeOfGlobalObject(const llvm::GlobalObject& globalObject); } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGCOMMONDEF_H cangjie_compiler-1.0.7/src/CodeGen/Utils/CGUtils.cpp000066400000000000000000000505561510705540100222330ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Utils/CGUtils.h" #include #include #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Type.h" #include "llvm/Support/Casting.h" #include "Base/CGTypes/CGEnumType.h" #include "Utils/CGCommonDef.h" #include "CGModule.h" #include "IRBuilder.h" #include "cangjie/Basic/Linkage.h" #include "cangjie/Basic/StringConvertor.h" #include "cangjie/CHIR/Expression/Terminator.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Type/Type.h" #include "cangjie/CHIR/Value.h" #include "cangjie/Option/Option.h" #include "cangjie/Utils/CheckUtils.h" using ChirTypeKind = Cangjie::CHIR::Type::TypeKind; using namespace Cangjie::CHIR; namespace { // type mangling look-up table const std::unordered_map TYPE_MANGLING_LUT = { {ChirTypeKind::TYPE_INT8, "a"}, {ChirTypeKind::TYPE_INT16, "s"}, {ChirTypeKind::TYPE_INT32, "i"}, {ChirTypeKind::TYPE_INT64, "l"}, {ChirTypeKind::TYPE_INT_NATIVE, "q"}, {ChirTypeKind::TYPE_UINT8, "h"}, {ChirTypeKind::TYPE_UINT16, "t"}, {ChirTypeKind::TYPE_UINT32, "j"}, {ChirTypeKind::TYPE_UINT64, "m"}, {ChirTypeKind::TYPE_UINT_NATIVE, "r"}, {ChirTypeKind::TYPE_FLOAT16, "Dh"}, {ChirTypeKind::TYPE_FLOAT32, "f"}, {ChirTypeKind::TYPE_FLOAT64, "d"}, {ChirTypeKind::TYPE_RUNE, "c"}, {ChirTypeKind::TYPE_BOOLEAN, "b"}, {ChirTypeKind::TYPE_UNIT, "u"}, {ChirTypeKind::TYPE_NOTHING, "n"}, {ChirTypeKind::TYPE_CSTRING, "k"}, {ChirTypeKind::TYPE_VOID, "u"}, }; void GetGenericArgsFromCHIRTypeHelper(const Cangjie::CHIR::Type& type, std::vector path, std::vector& res) { auto baseType = Cangjie::CodeGen::DeRef(type); if (baseType->IsGeneric()) { (void)res.emplace_back( Cangjie::CodeGen::GenericTypeAndPath(dynamic_cast(*baseType), path)); return; } size_t idx = 0; for (auto typeArg : baseType->GetTypeArgs()) { path.emplace_back(idx); GetGenericArgsFromCHIRTypeHelper(*typeArg, path, res); path.pop_back(); ++idx; } } } // namespace namespace Cangjie { namespace CodeGen { int64_t GetIntMaxOrMin(IRBuilder2& irBuilder, const CHIR::IntType& ty, bool isMax) { auto tyKind = irBuilder.GetTypeKindFromType(ty); auto minMax = G_SIGNED_INT_MAP.at(tyKind); return isMax ? minMax.second : minMax.first; } std::vector UnwindGenericRelateType(llvm::LLVMContext& llvmCtx, const CHIR::Type& ty) { std::vector tyArgMeta{}; if (ty.IsGeneric()) { return tyArgMeta; } if (ty.GetTypeArgs().size() > 0) { std::string ttName = CGType::GetNameOfTypeTemplateGV(ty); tyArgMeta.emplace_back(llvm::MDString::get(llvmCtx, ttName)); auto tyArg = ty.GetTypeArgs()[0]; if (tyArg->IsValueType()) { std::string tiName = CGType::GetNameOfTypeInfoGV(*tyArg); tyArgMeta.emplace_back(llvm::MDString::get(llvmCtx, tiName)); } else { auto ti = UnwindGenericRelateType(llvmCtx, *DeRef(*tyArg)); if (!ti.empty()) { tyArgMeta.emplace_back(llvm::MDTuple::get(llvmCtx, ti)); } } } return tyArgMeta; } uint64_t GetUIntMax(IRBuilder2& irBuilder, const CHIR::IntType& ty) { auto tyKind = irBuilder.GetTypeKindFromType(ty); return G_UNSIGNED_INT_MAP.at(tyKind); } std::string GetTypeName(const CGType* type) { static const std::unordered_map typeNameMap = { {UNIT_TYPE_STR, "Unit"}, }; auto llvmType = type->GetLLVMType(); std::string name; // # isStructTy // such as `%Ref.Type = type{}`, // we can get the string "%Ref.Type" by calling `type->getStructName()`. // # Otherwise // such as `i8` // we can get the string "i8" by calling `type->print()`. if (llvmType->isStructTy()) { name = GetCodeGenTypeName(llvmType); } else { std::string typeStr; llvm::raw_string_ostream rso(typeStr); llvmType->print(rso); name = rso.str(); if (llvmType->isPointerTy() && !type->IsCGFunction()) { auto baseType = type->GetPointerElementType()->GetLLVMType(); if (baseType->isStructTy()) { name = GetCodeGenTypeName(baseType); } } } CJC_ASSERT(!name.empty()); if (auto it = typeNameMap.find(name); it != typeNameMap.end()) { name = it->second; } return name; } namespace { std::string ConstantValueToString(const CHIR::Constant& expr) { std::string res; if (expr.IsBoolLit()) { res = std::to_string(expr.GetBoolLitVal()); } else if (expr.IsFloatLit()) { res = std::to_string(expr.GetFloatLitVal()); } else if (expr.IsSignedIntLit()) { res = std::to_string(expr.GetSignedIntLitVal()); } else if (expr.IsUnSignedIntLit()) { res = std::to_string(expr.GetUnsignedIntLitVal()); } else if (expr.IsRuneLit()) { res = std::to_string(expr.GetRuneLitVal()); } else { CJC_ABORT(); } return res; } std::tuple IsAllConstantNode(const std::vector& args) { if (args.empty()) { return std::make_tuple(false, ""); } bool isAllConstantNode = true; std::string serialized = ""; for (auto node : args) { if (!node->IsLocalVar()) { isAllConstantNode = false; break; } auto localVar = StaticCast(node); auto expr = localVar->GetExpr(); if (auto constant = DynamicCast(expr); constant && (constant->IsBoolLit() || constant->IsIntLit() || constant->IsFloatLit() || constant->IsRuneLit())) { serialized += "{" + ConstantValueToString(*constant) + localVar->GetType()->ToString() + "}"; continue; } CJC_NULLPTR_CHECK(expr); bool isEleConstant = false; std::string serializedEle = ""; if (expr->GetExprKind() == CHIR::ExprKind::TUPLE && localVar->GetType()->IsTuple()) { std::tie(isEleConstant, serializedEle) = IsConstantTuple(StaticCast(*expr)); serializedEle += "Tuple"; } else if (expr->GetExprKind() == CHIR::ExprKind::VARRAY) { std::tie(isEleConstant, serializedEle) = IsConstantVArray(StaticCast(*expr)); serializedEle += "VArray"; } else { isAllConstantNode = false; break; } if (isEleConstant) { serialized += "{" + serializedEle + "}"; continue; } else { isAllConstantNode = false; break; } } if (!isAllConstantNode) { serialized = ""; } return std::make_tuple(isAllConstantNode, serialized); } } // namespace std::tuple IsConstantArray(const CHIR::RawArrayLiteralInit& arrayLiteralInit) { const auto& operands = arrayLiteralInit.GetOperands(); const std::vector args(operands.begin() + 1, operands.end()); return IsAllConstantNode(args); } std::tuple IsConstantVArray(const CHIR::VArray& varray) { return IsAllConstantNode(varray.GetOperands()); } std::tuple IsConstantTuple(const CHIR::Tuple& tuple) { return IsAllConstantNode(tuple.GetOperands()); } bool IsReferenceType(const CHIR::Type& ty, CGModule& cgMod) { if (ty.IsRef()) { return IsReferenceType(*StaticCast(ty).GetBaseType(), cgMod); } if (ty.IsEnum()) { auto enumCGType = StaticCast(CGType::GetOrCreate(cgMod, &ty)); return enumCGType->IsCommonEnum() || enumCGType->IsOptionLikeRef() || enumCGType->IsOptionLikeT(); } return ty.IsClass() || ty.IsBox() || ty.IsRawArray(); } bool IsAllConstantValue(const std::vector& args) { return !args.empty() && std::all_of(args.begin(), args.end(), [](auto it) { return llvm::dyn_cast(it->GetRawValue()); }); } bool SaveToBitcodeFile(const llvm::Module& module, const std::string& bcFilePath) { std::error_code errorCode; #ifdef _WIN32 std::optional tempPath = StringConvertor::NormalizeStringToUTF8(bcFilePath); CJC_ASSERT(tempPath.has_value() && "Incorrect file name encoding."); llvm::raw_fd_ostream os(tempPath.value(), errorCode); #else llvm::raw_fd_ostream os(bcFilePath, errorCode); #endif llvm::WriteBitcodeToFile(module, os); if (errorCode) { Errorln("Failed to write bitcode to file:", errorCode.message()); return false; } os.close(); return true; } uint64_t GetTypeSize(const CGModule& cgMod, llvm::Type& ty) { return cgMod.GetLLVMModule()->getDataLayout().getTypeAllocSize(&ty); } uint64_t GetTypeSize(const CGModule& cgMod, const CHIR::Type& ty) { if (ty.IsUnit() || ty.IsNothing()) { return 0U; } auto cgType = CGType::GetOrCreate(const_cast(cgMod), &ty); return GetTypeSize(cgMod, *cgType->GetLLVMType()); } uint64_t GetTypeAlignment(const CGModule& cgMod, const CHIR::Type& ty) { if (ty.IsUnit() || ty.IsNothing()) { return 1U; } auto cgType = CGType::GetOrCreate(const_cast(cgMod), &ty); return GetTypeAlignment(cgMod, *cgType->GetLLVMType()); } uint64_t GetTypeAlignment(const CGModule& cgMod, llvm::Type& ty) { return cgMod.GetLLVMModule()->getDataLayout().getABITypeAlignment(&ty); } bool IsZeroSizedTypeInC(const CGModule& cgMod, const CHIR::Type& chirTy) { if (chirTy.IsRef()) { return IsZeroSizedTypeInC(cgMod, *StaticCast(chirTy).GetBaseType()); } // For windows-gnu target, an (nested) empty struct is not treated as zero-sized type. return chirTy.IsUnit() || chirTy.IsNothing() || (cgMod.GetCGContext().GetCompileOptions().target.os != Triple::OSType::WINDOWS && GetTypeSize(cgMod, chirTy) == 0); } bool IsTypeContainsRef(llvm::Type* type) { if (type->isPointerTy() && type->getPointerAddressSpace() == 1) { return true; } if (auto st = llvm::dyn_cast(type)) { return std::any_of(st->element_begin(), st->element_end(), IsTypeContainsRef); } if (auto at = llvm::dyn_cast(type)) { return IsTypeContainsRef(at->getElementType()); } if (auto vt = llvm::dyn_cast(type)) { return IsTypeContainsRef(vt->getScalarType()); } return false; } void CollectLinkNameUsedInMetaInsert( std::queue& queueMD, const llvm::MDOperand& op, std::unordered_set& ctxSet) { const llvm::MDNode* mdNodeOp = llvm::dyn_cast_or_null(op); if (mdNodeOp) { // Add the MDNode to the queue. queueMD.push(mdNodeOp); } else { // Add leaf node. const llvm::MDString* mds = llvm::dyn_cast(op); if (mds) { ctxSet.emplace(mds->getString()); } } } void CollectLinkNameUsedInMeta(const llvm::NamedMDNode* n, std::unordered_set& ctxSet) { if (!n) { return; } std::unordered_set mdVisitedSet; std::queue queueMD; for (unsigned i = 0; i < n->getNumOperands(); i++) { queueMD.push(n->getOperand(i)); } while (!queueMD.empty()) { const llvm::MDNode* front = queueMD.front(); queueMD.pop(); for (unsigned i = 0; i < front->getNumOperands(); i++) { if (mdVisitedSet.find(front->getOperand(i).get()) == mdVisitedSet.end()) { CollectLinkNameUsedInMetaInsert(queueMD, front->getOperand(i), ctxSet); mdVisitedSet.insert(front->getOperand(i).get()); } } } } CHIR::CustomTypeDef* GetTypeDefFromImplicitUsedFuncParam( const CGModule& cgModule, const std::string& funcName, const size_t paramIndex) { auto func = cgModule.GetCGContext().GetImplicitUsedFunc(funcName); CJC_ASSERT(func); auto fType = dynamic_cast(func->GetType()); auto classBaseType = dynamic_cast(fType->GetParamType(paramIndex))->GetBaseType(); CJC_ASSERT(classBaseType); auto classDef = dynamic_cast(classBaseType)->GetClassDef(); CJC_ASSERT(classDef); return classDef; } bool IsGetElementRefOfClass(const CHIR::Expression& expr, CHIR::CHIRBuilder& builder) { if (expr.GetExprKind() != CHIR::ExprKind::GET_ELEMENT_REF) { return false; } auto& getEleRef = dynamic_cast(expr); auto baseType = getEleRef.GetLocation()->GetType()->GetTypeArgs()[0]; auto& path = getEleRef.GetPath(); CJC_ASSERT(!path.empty()); for (size_t idx = 0; idx < path.size() - 1; ++idx) { baseType = GetFieldOfType(*baseType, path[idx], builder); } return baseType->IsClass(); } bool IsCHIRWrapper(const std::string& chirFuncName) { auto endsWith = [](const std::string& str, const std::string& suffix) -> bool { return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; }; return endsWith(chirFuncName, "_cc_imported_wrapper") || endsWith(chirFuncName, "_cc_wrapper") || endsWith(chirFuncName, "_cc_abstractFunc_wrapper"); } llvm::GlobalValue::LinkageTypes CHIRLinkage2LLVMLinkage(Linkage chirLinkage) { switch (chirLinkage) { case Linkage::WEAK_ODR: return llvm::GlobalValue::WeakODRLinkage; case Linkage::EXTERNAL: return llvm::GlobalValue::ExternalLinkage; case Linkage::INTERNAL: return llvm::GlobalValue::InternalLinkage; case Linkage::LINKONCE_ODR: return llvm::GlobalValue::LinkOnceODRLinkage; default: return llvm::GlobalValue::ExternalLinkage; } } void AddLinkageTypeMetadata( llvm::GlobalObject& globalObject, llvm::GlobalValue::LinkageTypes linkageType, bool markByMD) { if (!markByMD) { globalObject.setLinkage(linkageType); return; } static const std::unordered_map linkageType2Str = { {llvm::GlobalValue::ExternalLinkage, "ExternalLinkage"}, {llvm::GlobalValue::AvailableExternallyLinkage, "AvailableExternallyLinkage"}, {llvm::GlobalValue::LinkOnceAnyLinkage, "LinkOnceAnyLinkage"}, {llvm::GlobalValue::LinkOnceODRLinkage, "LinkOnceODRLinkage"}, {llvm::GlobalValue::WeakAnyLinkage, "WeakAnyLinkage"}, {llvm::GlobalValue::WeakODRLinkage, "WeakODRLinkage"}, {llvm::GlobalValue::AppendingLinkage, "AppendingLinkage"}, {llvm::GlobalValue::InternalLinkage, "InternalLinkage"}, {llvm::GlobalValue::PrivateLinkage, "PrivateLinkage"}, {llvm::GlobalValue::ExternalWeakLinkage, "ExternalWeakLinkage"}, {llvm::GlobalValue::CommonLinkage, "CommonLinkage"}, }; auto linkageTypeStr = linkageType2Str.find(linkageType); CJC_ASSERT(linkageTypeStr != linkageType2Str.end()); auto md = llvm::MDTuple::get( globalObject.getContext(), {llvm::MDString::get(globalObject.getContext(), linkageTypeStr->second)}); globalObject.addMetadata("LinkageType", *md); } llvm::GlobalValue::LinkageTypes GetLinkageTypeOfGlobalObject(const llvm::GlobalObject& globalObject) { static const std::unordered_map str2LinkageType = { {"ExternalLinkage", llvm::GlobalValue::ExternalLinkage}, {"AvailableExternallyLinkage", llvm::GlobalValue::AvailableExternallyLinkage}, {"LinkOnceAnyLinkage", llvm::GlobalValue::LinkOnceAnyLinkage}, {"LinkOnceODRLinkage", llvm::GlobalValue::LinkOnceODRLinkage}, {"WeakAnyLinkage", llvm::GlobalValue::WeakAnyLinkage}, {"WeakODRLinkage", llvm::GlobalValue::WeakODRLinkage}, {"AppendingLinkage", llvm::GlobalValue::AppendingLinkage}, {"InternalLinkage", llvm::GlobalValue::InternalLinkage}, {"PrivateLinkage", llvm::GlobalValue::PrivateLinkage}, {"ExternalWeakLinkage", llvm::GlobalValue::ExternalWeakLinkage}, {"CommonLinkage", llvm::GlobalValue::CommonLinkage}, }; if (auto md = globalObject.getMetadata("LinkageType"); md) { auto linkageTypeStr = llvm::cast(md->getOperand(0))->getString().str(); auto linkageType = str2LinkageType.find(linkageTypeStr); CJC_ASSERT(linkageType != str2LinkageType.end()); return linkageType->second; } else { return globalObject.getLinkage(); } } CGType* FixedCGTypeOfFuncArg(CGModule& cgMod, const CHIR::Value& chirFuncArg, llvm::Value& llvmValue) { auto chirFuncArgType = chirFuncArg.GetType(); auto cgType = CGType::GetOrCreate(cgMod, chirFuncArgType); bool byRef = chirFuncArgType->IsStruct() || chirFuncArgType->IsTuple() || chirFuncArgType->IsVArray() || chirFuncArgType->IsUnit() || chirFuncArgType->IsNothing() || (chirFuncArgType->IsEnum() && (StaticCast(cgType)->PassByReference())); if (byRef) { auto refType = CGType::GetRefTypeOf(cgMod.GetCGContext().GetCHIRBuilder(), *chirFuncArgType); cgType = CGType::GetOrCreate(cgMod, refType, {llvmValue.getType()->getPointerAddressSpace()}); } cgMod.SetOrUpdateMappedCGValue(&chirFuncArg, std::make_unique(&llvmValue, cgType)); return cgType; } void DumpIR(const llvm::Module& llvmModule, const std::string& filePath, bool debugMode) { #ifndef NDEBUG if (!debugMode) { return; } if (filePath.empty()) { llvmModule.print(llvm::outs(), nullptr); return; } std::string normalizedPath = filePath; #ifdef _WIN32 std::optional tempPath = StringConvertor::NormalizeStringToUTF8(filePath); CJC_ASSERT(tempPath.has_value() && "Incorrect file name encoding."); normalizedPath = tempPath.value(); #endif if (FileUtil::CreateDirs(normalizedPath) < 0) { return; } std::error_code errorCode; llvm::raw_fd_ostream fileOS(normalizedPath, errorCode); llvmModule.print(fileOS, nullptr); #else (void)llvmModule; (void)filePath; (void)debugMode; #endif } llvm::StructType* GetLLVMStructType( CGModule& cgMod, const std::vector& elementTypes, const std::string& name) { auto& cgCtx = cgMod.GetCGContext(); auto& llvmCtx = cgMod.GetLLVMContext(); auto layoutType = llvm::StructType::getTypeByName(llvmCtx, name); if (layoutType && cgCtx.IsGeneratedStructType(name)) { return layoutType; } else if (!layoutType) { layoutType = llvm::StructType::create(llvmCtx, name); } cgCtx.AddGeneratedStructType(layoutType->getName().str()); std::vector elemTypes; for (auto elemType : elementTypes) { auto paramCGType = CGType::GetOrCreate(cgMod, elemType); elemTypes.emplace_back(paramCGType->GetLLVMType()); } SetStructTypeBody(layoutType, elemTypes); return layoutType; } std::vector GetGenericArgsFromCHIRType(const CHIR::Type& type) { std::vector res; std::vector path; GetGenericArgsFromCHIRTypeHelper(type, path, res); return res; } CHIR::Type* GetTypeInnerType(const CHIR::Type& type, const GenericTypeAndPath& gtAndPath) { auto& path = gtAndPath.GetPath(); size_t idx = 0; const CHIR::Type* res = DeRef(type); while (idx < path.size()) { const auto& pathPoint = path[idx]; auto typeArgsOfRes = res->GetTypeArgs(); CJC_ASSERT(pathPoint < typeArgsOfRes.size() && "Invalid generic type path."); res = DeRef(*typeArgsOfRes[pathPoint]); ++idx; } return const_cast(res); } bool IsThisArgOfStructMethod(const CHIR::Value& chirValue) { if (!chirValue.IsParameter()) { return false; } auto& chirParam = StaticCast(chirValue); auto chirFunc = chirParam.GetTopLevelFunc(); if (!chirFunc || !chirFunc->GetNumOfParams() || chirFunc->GetParam(0) != &chirParam) { return false; } if (chirFunc->TestAttr(CHIR::Attribute::STATIC) || !IsStructOrExtendMethod(*chirFunc)) { return false; } return true; } } // namespace CodeGen } // namespace Cangjie cangjie_compiler-1.0.7/src/CodeGen/Utils/CGUtils.h000066400000000000000000000332161510705540100216720ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CGUTILS_H #define CANGJIE_CGUTILS_H #include #include #include "llvm/IR/Attributes.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" #include "Base/CGTypes/CGFunctionType.h" #include "Utils/CGCommonDef.h" #include "cangjie/CHIR/CHIRContext.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/CHIR/Utils.h" #include "cangjie/Mangle/CHIRTypeManglingUtils.h" #include "cangjie/Utils/ConstantsUtils.h" #include "cangjie/Utils/Utils.h" namespace Cangjie { namespace CHIR { class Type; class IntType; } // namespace CHIR namespace CodeGen { class IRBuilder2; class CGValue; std::vector UnwindGenericRelateType(llvm::LLVMContext& llvmCtx, const CHIR::Type& ty); inline std::string GenNameForBB(const std::string& name, [[maybe_unused]] uint64_t extraInfo = 0) { return name; } template inline bool In(const T element, const std::unordered_set& set) { return set.find(element) != set.cend(); } template inline bool NotIn(T element, const std::vector& container) { auto it = std::find(container.begin(), container.end(), element); return it == container.end(); } template auto Vec2TupleHelper(const std::vector& v, std::index_sequence) { return std::make_tuple(v[Indices]...); } template auto Vec2Tuple(const std::vector& v) { CJC_ASSERT(v.size() >= N); return Vec2TupleHelper(v, std::make_index_sequence()); } inline void SetStructTypeBody(llvm::StructType* structType, const std::vector& bodyVec) { CJC_NULLPTR_CHECK(structType); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND structType->setBodyForCJ(bodyVec); #endif } inline std::string GetClassObjLayoutName(const std::string& className) { return "ObjLayout." + className; } inline void SetGCCangjie(llvm::Function* func) { func->setGC("cangjie"); } inline std::string MangleName(const std::string& str) { return std::to_string(str.length()) + str; } inline std::string GetMangledNameOfCompilerAddedClass(const std::string& className) { // As for the class added by FE, it’s not in any package, so // in it’s mangled name, the would be empty // (i.e., length is zero). return MANGLE_NESTED_PREFIX + MangleName(className) + "E"; } const std::unordered_map OPERATOR_KIND_TO_OP_MAP = { {CHIR::ExprKind::ADD, "add"}, {CHIR::ExprKind::SUB, "sub"}, {CHIR::ExprKind::MUL, "mul"}, {CHIR::ExprKind::DIV, "div"}, {CHIR::ExprKind::MOD, "mod"}, {CHIR::ExprKind::EXP, "pow"}, {CHIR::ExprKind::NEG, "neg"}, }; inline std::string GetCodeGenTypeName(llvm::Type* type) { if (type->isStructTy()) { return llvm::cast(type)->isLiteral() ? "" : type->getStructName().str(); } return ""; // Other types do not have llvm type name. } std::string GetTypeName(const CGType* type); bool SaveToBitcodeFile(const llvm::Module& module, const std::string& bcFilePath); inline llvm::Type* GetPointerToWithSpecificAddrSpace(const CGType* srcType, unsigned dstAddrSpace) { CJC_ASSERT(srcType->IsPointerType() && "a pointer type is expected"); return srcType->GetPointerElementType()->GetLLVMType()->getPointerTo(dstAddrSpace); } inline llvm::Type* GetPointerElementType(const llvm::Type* type) { return type->getNonOpaquePointerElementType(); } /** * Check if @p type is a pointer type which points to a struct. */ inline bool IsStructPtrType(llvm::Type* type) { return type->isPointerTy() && GetPointerElementType(type)->isStructTy(); } inline bool HasNoUse(const llvm::Value& value) { return !value.hasNUsesOrMore(1); } inline const CHIR::Type* DeRef(const CHIR::Type& chirTy) { return chirTy.StripAllRefs(); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool IsReferenceType(const CHIR::Type& ty, CGModule& cgMod); #endif inline bool IsGenericRef(const CHIR::Type& chirTy) { return chirTy.IsRef() && !chirTy.GetTypeArgs().empty() && chirTy.GetTypeArgs()[0]->IsGeneric(); } inline bool IsTrivialEnum(const CHIR::Type& chirTy) { return chirTy.IsEnum() && StaticCast(chirTy).GetEnumDef()->IsAllCtorsTrivial(); } inline bool IsStructRef(const CHIR::Type& chirTy) { return chirTy.IsRef() && !chirTy.GetTypeArgs().empty() && chirTy.GetTypeArgs()[0]->IsStruct(); } inline bool IsStaticStruct(const CHIR::Type& chirTy) { return chirTy.IsStruct() && CGType::GetCGGenericKind(chirTy) == CGType::CGGenericKind::STATIC_GI; } inline bool IsDynamicStruct(const CHIR::Type& chirTy) { return chirTy.IsStruct() && CGType::GetCGGenericKind(chirTy) == CGType::CGGenericKind::DYNAMIC_GI; } inline bool IsDynamicStructRef(const CHIR::Type& chirTy) { if (!chirTy.IsRef() || chirTy.GetTypeArgs().empty()) { return false; } auto baseType = chirTy.GetTypeArgs()[0]; return baseType->IsStruct() && CGType::GetCGGenericKind(*baseType) == CGType::CGGenericKind::DYNAMIC_GI; } inline bool IsClassRef(const CHIR::Type& chirTy) { return chirTy.IsRef() && !chirTy.GetTypeArgs().empty() && chirTy.GetTypeArgs()[0]->IsClass(); } inline bool IsStaticClass(const CHIR::Type& chirTy) { return chirTy.IsClass() && CGType::GetCGGenericKind(chirTy) == CGType::CGGenericKind::STATIC_GI; } inline bool IsDynamicClass(const CHIR::Type& chirTy) { return chirTy.IsClass() && CGType::GetCGGenericKind(chirTy) == CGType::CGGenericKind::DYNAMIC_GI; } inline bool IsDynamicClassRef(const CHIR::Type& chirTy) { if (!chirTy.IsRef() || chirTy.GetTypeArgs().empty()) { return false; } auto baseType = chirTy.GetTypeArgs()[0]; return baseType->IsClass() && CGType::GetCGGenericKind(*baseType) == CGType::CGGenericKind::DYNAMIC_GI; } inline bool IsVArrayRef(const CHIR::Type& chirTy) { return chirTy.IsRef() && !chirTy.GetTypeArgs().empty() && chirTy.GetTypeArgs()[0]->IsVArray(); } inline bool IsUnitOrNothing(const CHIR::Type& chirTy) { return chirTy.IsUnit() || chirTy.IsNothing(); } bool IsThisArgOfStructMethod(const CHIR::Value& chirValue); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND inline bool IsAtomicIntrinsic(const CHIR::IntrinsicKind& intrinsicKind) { return intrinsicKind >= CHIR::IntrinsicKind::ATOMIC_LOAD && intrinsicKind <= CHIR::IntrinsicKind::ATOMIC_FETCH_XOR; } int64_t GetIntMaxOrMin(IRBuilder2& irBuilder, const CHIR::IntType& ty, bool isMax); uint64_t GetUIntMax(IRBuilder2& irBuilder, const CHIR::IntType& ty); inline bool IsPassedByReference(llvm::Type* type) { return (type->isStructTy() || type->isArrayTy()); } #endif inline void NameFunctionParam(llvm::Function* function, std::vector paramName) { CJC_ASSERT(function->arg_size() == paramName.size()); for (size_t idx = 0; idx < paramName.size(); idx++) { auto arg = function->args().begin() + idx; arg->setName(paramName[idx]); } } /** * Check if @p type is a pointer type which points to a Function. */ inline bool IsFuncPtrType(llvm::Type* type) { return type->isPointerTy() && GetPointerElementType(type)->isFunctionTy(); } /** * A literal struct pointer type looks like: {}*. * For now, this type means a cFunc pointer type with a incomplete parameter or return type. */ inline bool IsLitStructPtrType(llvm::Type* type) { if (!type || !type->isPointerTy()) { return false; } auto structType = llvm::dyn_cast(GetPointerElementType(type)); return structType && structType->isLiteral(); } class ArrayCopyToInfo { public: ArrayCopyToInfo(llvm::Value* srcBP, llvm::Value* dstBP, llvm::Value* srcArrPtr, llvm::Value* dstArrPtr, llvm::Value* dataSize, llvm::Value* srcIndex, llvm::Value* dstIndex, const CGType* elemType, llvm::Value* elemCnt) : srcBP(srcBP), dstBP(dstBP), srcArrPtr(srcArrPtr), dstArrPtr(dstArrPtr), dataSize(dataSize), srcIndex(srcIndex), dstIndex(dstIndex), elemType(elemType), elemCnt(elemCnt) { } llvm::Value* srcBP{nullptr}; // address of the source array (i8Ptr) llvm::Value* dstBP{nullptr}; // address of the destination array (i8Ptr) llvm::Value* srcArrPtr{nullptr}; // address of the source array (arrayTypePtr) llvm::Value* dstArrPtr{nullptr}; // address of the destination array (arrayTypePtr) llvm::Value* dataSize{nullptr}; // size : ** deprecated ** llvm::Value* srcIndex{nullptr}; // start index of source array llvm::Value* dstIndex{nullptr}; // start index of destination array const CGType* elemType{nullptr}; // array element type llvm::Value* elemCnt{nullptr}; // the number of elements to be copied }; /* * Return a pair consisting of a bool value set to true if the input parameter is a constant, and * a string representing the result of the serialization. */ std::string ConstantValueToString(const CHIR::Constant& expr); std::tuple IsConstantArray(const CHIR::RawArrayLiteralInit& arrayLiteralInit); std::tuple IsConstantVArray(const CHIR::VArray& varray); std::tuple IsConstantTuple(const CHIR::Tuple& tuple); bool IsAllConstantValue(const std::vector& args); inline bool IsFPType(llvm::Type* type) { return type->isHalfTy() || type->isFloatTy() || type->isDoubleTy() || type->isFP128Ty() || type->isPPC_FP128Ty() || type->isX86_FP80Ty(); } inline bool IsObject(const CHIR::ClassDef& classDef) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND return IsCoreObject(classDef); #endif } inline bool IsNonPublicCFunc(const CHIR::FuncType& funcTy, const CHIR::Value& func) { // When incremental compile is enabled, foreign function is regarded as // imported variable, it should not be wrapped. if (func.TestAttr(CHIR::Attribute::FOREIGN)) { return false; } return funcTy.IsCFunc() && func.TestAttr(CHIR::Attribute::PRIVATE); } inline bool IsCommonStruct(const CHIR::Type& ty) { return Utils::AllOf(ty.IsStruct(), !ty.IsCPointer(), !ty.IsCString()); } inline bool IsCFunc(const CHIR::Type& chirTy) { if (!chirTy.IsFunc()) { return false; } return StaticCast(chirTy).IsCFunc(); } inline bool IsCStruct(const CHIR::Type& chirTy) { if (!chirTy.IsStruct()) { return false; } auto def = StaticCast(chirTy).GetStructDef(); CJC_NULLPTR_CHECK(def); return def->IsCStruct(); } class CGModule; uint64_t GetTypeSize(const CGModule& cgMod, llvm::Type& ty); uint64_t GetTypeSize(const CGModule& cgMod, const CHIR::Type& ty); uint64_t GetTypeAlignment(const CGModule& cgMod, const CHIR::Type& ty); uint64_t GetTypeAlignment(const CGModule& cgMod, llvm::Type& ty); bool IsZeroSizedTypeInC(const CGModule& cgMod, const CHIR::Type& chirTy); inline size_t AlignedUpTo(size_t offset, size_t align) { return ((offset + align - 1) / align) * align; } inline size_t AlignedUpTo(const CGModule& cgMod, size_t offset, llvm::Type& type) { return AlignedUpTo(offset, GetTypeAlignment(cgMod, type)); } bool IsTypeContainsRef(llvm::Type* type); /** * Collect link name used in metadata by bfs. */ void CollectLinkNameUsedInMeta(const llvm::NamedMDNode* n, std::unordered_set& ctxSet); bool IsGetElementRefOfClass(const CHIR::Expression& expr, CHIR::CHIRBuilder& builder); CHIR::CustomTypeDef* GetTypeDefFromImplicitUsedFuncParam( const CGModule& cgModule, const std::string& funcName, const size_t paramIndex); inline std::string GetCjStringDataLiteralName(const std::string& cjStr) { return CJSTRING_DATA_PREFIX + Utils::HashString64(cjStr); } inline std::string GetCjStringLiteralName(const std::string& cjStr) { return CJSTRING_LITERAL_PREFIX + Utils::HashString64(cjStr); } inline std::string GetConstantTupleName(const std::string& string) { return CONST_TUPLE_PREFIX + Utils::HashString64(string); } inline std::string GetConstantArrayName(const std::string& string) { return CONST_ARRAY_PREFIX + Utils::HashString64(string); } CGType* FixedCGTypeOfFuncArg(CGModule& cgMod, const CHIR::Value& chirFuncArg, llvm::Value& llvmValue); void DumpIR(const llvm::Module& llvmModule, const std::string& filePath, bool debugMode); llvm::StructType* GetLLVMStructType( CGModule& cgMod, const std::vector& elementTypes, const std::string& name = ""); std::vector GetGenericArgsFromCHIRType(const CHIR::Type& type); CHIR::Type* GetTypeInnerType(const CHIR::Type& type, const GenericTypeAndPath& gtAndPath); inline bool IsCoreFutureClass(const CHIR::ClassDef& classDef) { return CheckCustomTypeDefIsExpected(classDef, CORE_PACKAGE_NAME, STD_LIB_FUTURE); } inline bool IsSyncRelatedClass(const CHIR::ClassDef& classDef) { return CheckCustomTypeDefIsExpected(classDef, SYNC_PACKAGE_NAME, STD_LIB_MONITOR) || CheckCustomTypeDefIsExpected(classDef, SYNC_PACKAGE_NAME, STD_LIB_MUTEX) || CheckCustomTypeDefIsExpected(classDef, SYNC_PACKAGE_NAME, STD_LIB_WAIT_QUEUE); } inline bool IsWeakRefClass(const CHIR::ClassDef& classDef) { return CheckCustomTypeDefIsExpected(classDef, REF_PACKAGE_NAME, STD_LIB_WEAK_REF); } inline bool IsExternalDefinedType(const CHIR::Type& type) { if (!type.IsNominal()) { return true; } return dynamic_cast(type).GetCustomTypeDef()->TestAttr(CHIR::Attribute::IMPORTED); } } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CGUTILS_H cangjie_compiler-1.0.7/src/CodeGen/Utils/Constants.h000066400000000000000000000102511510705540100223260ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * TThis file declares the constants used in CodeGen. */ #ifndef CANGJIE_CODEGENCONSTANTS_H #define CANGJIE_CODEGENCONSTANTS_H #include #include namespace Cangjie { namespace CodeGen { const size_t UI64_WIDTH = 64; const size_t I64_WIDTH = 64; const unsigned long INHERITED_CLASS_NUM_FE_FLAG = 1UL << 15; const std::string UNIT_TYPE_STR = "Unit.Type"; const std::string UNIT_VAL_STR = "Unit.Val"; const std::string ARRAY_LAYOUT_PREFIX = "ArrayLayout."; const std::string INCREMENTAL_CFUNC_ATTR = "incremental"; const std::string INTERNAL_CFUNC_ATTR = "internal"; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND const std::string FAST_NATIVE_ATTR = "gc-leaf-function"; #endif const std::string VTABLE_LOOKUP = "VTABLE_LOOKUP"; // Indicates that it is the instruction of VTable lookup. const std::string GC_TYPE_META_NAME = "RelatedType"; const std::string GC_GLOBAL_VAR_TYPE = "GlobalVarType"; const std::string GC_KLASS_ATTR = "CFileKlass"; const std::string GC_CAN_MALLOC_WITH_FIXED_SIZE = "can_malloc_with_fixed_size"; const std::string GC_MTABLE_ATTR = "CFileMTable"; const std::string GC_FINALIZER_ATTR = "HasFinalizer"; const std::string STRUCT_MUT_FUNC_ATTR = "record_mut"; const std::string THIS_PARAM_HAS_BP = "thisParamHasBP"; const std::string STRUCT_TYPE_PREFIX = "record."; const std::string CJ2C_ATTR = "cj2c"; const std::string C2CJ_ATTR = "c2cj"; const std::string CJSTUB_ATTR = "cjstub"; const std::string CFUNC_ATTR = "cfunc"; const std::string PREFIX_OF_BUILT_IN_SYMS = "CJ_"; const std::string PREFIX_OF_RUNTIME_SYMS = PREFIX_OF_BUILT_IN_SYMS + "MRT_"; const std::string PREFIX_OF_BACKEND_SYMS = PREFIX_OF_BUILT_IN_SYMS + "MCC_"; const std::string FOR_KEEPING_SOME_TYPES_FUNC_NAME = "0_for_keeping_some_types"; const std::string USER_MAIN_MANGLED_NAME = "user.main"; const std::string CJ_ENTRY_FUNC_NAME = "cj_entry$"; const std::string CONST_TUPLE_PREFIX = "$const_tuple."; const std::string CONST_ARRAY_PREFIX = "$const_array."; const std::string FUNC_USED_BY_CLOSURE = "UsedByClosure"; const std::string ENUM_TYPE_PREFIX = "enum."; const std::string CJSTRING_LITERAL_PREFIX = "$const_cjstring."; const std::string CJSTRING_DATA_PREFIX = "$const_cjstring_data."; const std::string CJSTRING_LITERAL_ATTR = "cjstring_literal"; const std::string CJSTRING_DATA_ATTR = "cjstring_data"; const std::string CJGLOBAL_VALUE_ATTR = "CJGlobalValue"; const std::string CJTYPE_NAME_ATTR = "CJTypeName"; const std::string CJTI_OFFSETS_ATTR = "CJTIOffsets"; const std::string CJTI_TYPE_ARGS_ATTR = "CJTITypeArgs"; const std::string CJTI_FIELDS_ATTR = "CJTIFields"; const std::string CJTI_UPPER_BOUNDS_ATTR = "CJTIUpperBounds"; const std::string CJTT_FIELDS_FNS_ATTR = "CJTTFieldsFns"; const std::string CJED_FUNC_TABLE_ATTR = "CJFuncTable"; const std::string BASEPTR_SUFFIX = "$BP"; const std::string METADATA_TYPES = "types"; const std::string METADATA_PRIMITIVE_TYPES = "primitive_tis"; const std::string METADATA_PRIMITIVE_TYPETEMPLATES = "primitive_tts"; const std::string METADATA_TYPETEMPLATES = "type_templates"; const std::string METADATA_PKG = "pkg_info"; const std::string METADATA_FUNCTIONS = "functions"; const std::string ATTR_IMMUTABLE = "immutable"; // for let-fields const std::string METADATA_ATTR_OPEN = "open"; // for mut-functions const std::string METADATA_GLOBAL_VAR = "global_variables"; const std::string PREFIX_FOR_BB_NAME = "bb"; const std::string GENERIC_DECL_IN_IMPORTED_PKG_ATTR = "generic_decl_in_imported_pkg"; const std::string GENERIC_DECL_IN_CURRENT_PKG_ATTR = "generic_decl_in_current_pkg"; const std::string TYPE_TEMPLATE_ATTR = "cj_tt"; const std::string GENERIC_TYPEINFO_ATTR = "cj_generic_ti"; const std::string POSTFIX_WITHOUT_TI = "$withoutTI"; const std::string GENERIC_PREFIX = "$G_"; const std::string HAS_WITH_TI_WRAPPER_ATTR = "hasWithTIWrapper"; const std::string PKG_GV_INIT_PREFIX = "_CGP"; const std::string FILE_GV_INIT_PREFIX = "_CGF"; } // namespace CodeGen } // namespace Cangjie #endif // CANGJIE_CODEGENCONSTANTS_H cangjie_compiler-1.0.7/src/ConditionalCompilation/000077500000000000000000000000001510705540100222405ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/ConditionalCompilation/CMakeLists.txt000066400000000000000000000012211510705540100247740ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. if(CANGJIE_BUILD_CJC) set(CONDITIONALCOMPILATION_SRC ConditionalCompilation.cpp) add_library(CangjieConditionalCompilation OBJECT ${CONDITIONALCOMPILATION_SRC}) target_compile_options(CangjieConditionalCompilation PRIVATE ${CJC_EXTRA_WARNINGS}) if(CANGJIE_ENABLE_GCOV) target_compile_options(CangjieConditionalCompilation PUBLIC "-fno-exceptions") endif() endif() cangjie_compiler-1.0.7/src/ConditionalCompilation/ConditionalCompilation.cpp000066400000000000000000000450151510705540100274130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements macro conditional compilation related apis for compiler. */ #include #include "ConditionalCompilationImpl.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Basic/Utils.h" #include "cangjie/Basic/Version.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Option/Option.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/ConditionalCompilation/ConditionalCompilation.h" using namespace Cangjie::FileUtil; using namespace Cangjie::Triple; using namespace Cangjie; using namespace Cangjie::AST; namespace { const std::string BACKEND_STR = "backend"; const std::string ARCH_STR = "arch"; const std::string OS_STR = "os"; const std::string CJC_VERSION_STR = "cjc_version"; const std::string DEBUG_STR = "debug"; const std::string TEST_STR = "test"; // Condition value map. const std::unordered_set TARGET_CONDITION = { BACKEND_STR, ARCH_STR, OS_STR, CJC_VERSION_STR, DEBUG_STR, TEST_STR, }; // Condition supported op map. const std::unordered_map> CONDITION_OP = { { BACKEND_STR, { TokenKind::EQUAL, TokenKind::NOTEQ, }, }, { ARCH_STR, { TokenKind::EQUAL, TokenKind::NOTEQ, }, }, { OS_STR, { TokenKind::EQUAL, TokenKind::NOTEQ, }, }, { CJC_VERSION_STR, { TokenKind::EQUAL, TokenKind::NOTEQ, TokenKind::GT, TokenKind::LT, TokenKind::GE, TokenKind::LE, }, }, { DEBUG_STR, {}, }, { TEST_STR, {}, }, }; // Condition supported values map. const std::map> CONDITION_VALUES = { { BACKEND_STR, { "cjnative", }, }, { ARCH_STR, { "x86_64", "aarch64", }, }, { OS_STR, // consistent with the official style. { "Windows", "Linux", "macOS", "iOS", "HarmonyOS", }, }, }; } // namespace static auto GetVersionUInt(const std::string& version) -> uint32_t { std::string res = ""; const int digitaSize = 2; auto digitals = Utils::SplitString(version, "."); for (const auto& it : digitals) { if (it.size() == digitaSize) { res += it; } else if (it.size() == 1) { res += "0" + it; } } int32_t iRes = std::stoi(res); CJC_ASSERT(iRes >= 0); return static_cast(iRes); } ConditionalCompilationImpl::ConditionalCompilationImpl(CompilerInstance* c) : ci(c), backendType(ci->invocation.globalOptions.backend), triple(ci->invocation.globalOptions.target), cjcVersion(GetVersionUInt(CANGJIE_VERSION)), debug(ci->invocation.globalOptions.enableCompileDebug), test(ci->invocation.globalOptions.enableCompileTest), passedCondition(ci->invocation.globalOptions.passedWhenKeyValue) { // in cjc cfg has been parsed, in lsp need parse and check cfg here. if (ci->invocation.globalOptions.enableMacroInLSP) { if (!passedCondition.empty()) { for (auto it : passedCondition) { if (TARGET_CONDITION.count(it.first) != 0) { ci->diag.DiagnoseRefactor(DiagKindRefactor::dirver_cfg_same_with_builtin, DEFAULT_POSITION); } } if (!ci->invocation.globalOptions.passedWhenCfgPaths.empty()) { ci->diag.DiagnoseRefactor(DiagKindRefactor::driver_cfg_path_ignored, DEFAULT_POSITION); } } else { const std::string cfgFileName("cfg.toml"); for (auto& path : ci->invocation.globalOptions.passedWhenCfgPaths) { std::string filePath = JoinPath(path, cfgFileName); if (!FileExist(filePath)) { ci->diag.DiagnoseRefactor( DiagKindRefactor::driver_warning_no_such_file, DEFAULT_POSITION, filePath); continue; } (void)SetupConditionalCompilationCfgFromFile(filePath, passedCondition, ci->diag); return; } // parse cfg.toml in default path std::string defaultCfgFilePath = ci->invocation.globalOptions.packagePaths.empty() ? cfgFileName : JoinPath(ci->invocation.globalOptions.packagePaths[0], cfgFileName); if (!FileExist(defaultCfgFilePath)) { return; } (void)SetupConditionalCompilationCfgFromFile(defaultCfgFilePath, passedCondition, ci->diag); } } } static inline auto IsLogicBinaryExpr(const BinaryExpr& expr) -> bool { return expr.op >= TokenKind::AND && expr.op <= TokenKind::OR; } static inline auto IsJudgeBinaryExpr(const BinaryExpr& expr) -> bool { return expr.op >= TokenKind::NOTEQ && expr.op <= TokenKind::EQUAL; } static inline auto IsRangeBinaryExpr(const BinaryExpr& expr) -> bool { return expr.op >= TokenKind::LT && expr.op <= TokenKind::GE; } std::string ConditionalCompilationImpl::GetOSType() const { switch (triple.os) { case Triple::OSType::DARWIN: return "macOS"; case Triple::OSType::IOS: return "iOS"; case Triple::OSType::WINDOWS: return "Windows"; case Triple::OSType::LINUX: return "Linux"; case Triple::OSType::UNKNOWN: default: break; } return triple.OSToString(); } std::string ConditionalCompilationImpl::GetUserDefinedInfoByName(const std::string& name) const { auto passedValues = GetPassedValues(); if (passedValues.count(name) == 0) { return ""; } return passedValues.at(name); } bool ConditionalCompilationImpl::EvalLogicBinaryExpr(const BinaryExpr& be) { if (be.op == TokenKind::AND) { return Utils::AllOf(EvalConditionExpr(be.leftExpr.operator*()), EvalConditionExpr(be.rightExpr.operator*())); } else { return Utils::AnyOf(EvalConditionExpr(be.leftExpr.operator*()), EvalConditionExpr(be.rightExpr.operator*())); } } bool ConditionalCompilationImpl::ConditionCheck( const std::string& conditionStr, const Position& begin, const std::string& right) { // If condition is not builtin and not user defined, break it. auto isBuiltin = TARGET_CONDITION.count(conditionStr) > 0; auto isUserDefined = !isBuiltin && passedCondition.count(conditionStr) == 1; if (!isBuiltin && !isUserDefined) { (void)ci->diag.DiagnoseRefactor( DiagKindRefactor::conditional_compilation_not_support_this_condition, begin, conditionStr); return false; } // Check `backend` and `os` condition value. if (CONDITION_VALUES.count(conditionStr) > 0 && !Utils::In(right, CONDITION_VALUES.at(conditionStr))) { std::string supportedValues = ""; for (const auto& it : CONDITION_VALUES.at(conditionStr)) { supportedValues += it + " "; } if (!supportedValues.empty()) { supportedValues.pop_back(); } (void)ci->diag.DiagnoseRefactor(DiagKindRefactor::conditional_compilation_not_support_builtin_value, begin, conditionStr, right, supportedValues); return false; } // Check `cjc_version`. if (conditionStr == CJC_VERSION_STR) { std::regex cjcVersionRegex("[0-9]{1,2}[.][0-9]{1,2}[.][0-9]{1,2}"); if (!std::regex_match(right, cjcVersionRegex)) { (void)ci->diag.DiagnoseRefactor( DiagKindRefactor::conditional_compilation_not_support_cjc_version_format, begin); return false; } } return true; } bool ConditionalCompilationImpl::Eval(const BinaryExpr& expr, const std::string& left, const std::string& right) const { // Eval value. switch (expr.op) { case TokenKind::EQUAL: { return left == right; } case TokenKind::NOTEQ: { return left != right; } case TokenKind::GT: return left > right; case TokenKind::LT: return left < right; case TokenKind::GE: return left >= right; default: return left <= right; } } bool ConditionalCompilationImpl::EvalJudgeBinaryExpr(const BinaryExpr& be) { if (be.leftExpr == nullptr || be.rightExpr == nullptr) { (void)ci->diag.DiagnoseRefactor(DiagKindRefactor::conditional_compilation_invalid_condition_expr, be.begin); return false; } if (be.leftExpr->astKind != ASTKind::REF_EXPR) { (void)ci->diag.DiagnoseRefactor(DiagKindRefactor::conditional_compilation_invalid_condition_expr, be.begin); return false; } if (be.rightExpr->astKind != ASTKind::LIT_CONST_EXPR) { (void)ci->diag.DiagnoseRefactor( DiagKindRefactor::conditional_compilation_invalid_condition_value, be.rightExpr->begin); return false; } auto right = RawStaticCast(be.rightExpr.get()); if (right->kind != LitConstKind::STRING || right->siExpr) { (void)ci->diag.DiagnoseRefactor( DiagKindRefactor::conditional_compilation_invalid_condition_value, be.rightExpr->begin); return false; } auto left = RawStaticCast(be.leftExpr.get()); auto conditionStr = left->ref.identifier; if (!ConditionCheck(conditionStr, be.begin, right->stringValue)) { return false; } auto relatedInfo = GetRelatedInfo(conditionStr); if (relatedInfo.empty()) { return false; } // Filter not support op. if (CONDITION_OP.count(conditionStr) > 0 && Utils::NotIn(be.op, CONDITION_OP.at(conditionStr))) { (void)ci->diag.DiagnoseRefactor(DiagKindRefactor::conditional_compilation_not_support_op, be.begin, conditionStr.Val(), TOKENS[static_cast(be.op)]); return false; } // Decode cjc version to judge. if (conditionStr == CJC_VERSION_STR) { std::string version = std::to_string(GetVersionUInt(right->stringValue)); right->stringValue = RefreshVersionStr(version); } auto leftValue = relatedInfo; return Eval(be, leftValue, right->stringValue); } bool ConditionalCompilationImpl::EvalBinaryExpr(const BinaryExpr& be) { if (IsLogicBinaryExpr(be)) { return EvalLogicBinaryExpr(be); } if (IsJudgeBinaryExpr(be) || IsRangeBinaryExpr(be)) { return EvalJudgeBinaryExpr(be); } (void)ci->diag.DiagnoseRefactor(DiagKindRefactor::conditional_compilation_invalid_condition_expr, be.begin); return false; } bool ConditionalCompilationImpl::EvalParenExpr(const ParenExpr& pe) { if (pe.expr == nullptr) { return false; } return EvalConditionExpr(pe.expr.operator*()); } bool ConditionalCompilationImpl::EvalUnaryExpr(const UnaryExpr& ue) const { if (ue.expr == nullptr || ue.expr->astKind != ASTKind::REF_EXPR) { (void)ci->diag.DiagnoseRefactor(DiagKindRefactor::conditional_compilation_invalid_condition_expr, ue.begin); return false; } auto conditionExpr = RawStaticCast(ue.expr.get()); if (conditionExpr == nullptr || (conditionExpr->ref.identifier != DEBUG_STR && conditionExpr->ref.identifier != TEST_STR)) { (void)ci->diag.DiagnoseRefactor(DiagKindRefactor::conditional_compilation_invalid_condition_expr, ue.begin); return false; } auto relatedInfo = GetRelatedInfo(conditionExpr->ref.identifier); if (relatedInfo.empty()) { return false; } return relatedInfo != "1"; } bool ConditionalCompilationImpl::EvalRefExpr(const RefExpr& re) const { if (re.ref.identifier != DEBUG_STR && re.ref.identifier != TEST_STR) { (void)ci->diag.DiagnoseRefactor(DiagKindRefactor::conditional_compilation_invalid_condition_expr, re.begin); return false; } auto relatedInfo = GetRelatedInfo(re.ref.identifier); if (relatedInfo.empty()) { return false; } return relatedInfo == "1"; } bool ConditionalCompilationImpl::EvalConditionExpr(const Expr& condition) { switch (condition.astKind) { case ASTKind::BINARY_EXPR: return EvalBinaryExpr(static_cast(condition)); case ASTKind::UNARY_EXPR: return EvalUnaryExpr(static_cast(condition)); case ASTKind::REF_EXPR: return EvalRefExpr(static_cast(condition)); case ASTKind::PAREN_EXPR: return EvalParenExpr(static_cast(condition)); default: { (void)ci->diag.DiagnoseRefactor( DiagKindRefactor::conditional_compilation_invalid_condition_expr, condition.begin); return false; } } } void ConditionalCompilationImpl::HandleConditionalCompilation(const Package& root) { for (auto& file : root.files) { HandleFileConditionalCompilation(*file.get()); if (file->hasMacro) { // update hasMacro flag file->hasMacro = false; auto action = [&file](Ptr curNode) -> VisitAction { if (curNode->astKind == ASTKind::MACRO_EXPAND_DECL || curNode->astKind == ASTKind::MACRO_EXPAND_EXPR || curNode->astKind == ASTKind::MACRO_EXPAND_PARAM) { file->hasMacro = true; return VisitAction::STOP_NOW; } return VisitAction::WALK_CHILDREN; }; Walker astWalker(file, action); astWalker.Walk(); } } } template bool ConditionalCompilationImpl::EvalNodeCondition(Ptr node) { // Eval condition. auto evalResult = true; auto filterPred = [this, &evalResult](const auto& anno) -> bool { if (anno->kind != AnnotationKind::WHEN) { return false; } if (anno->condExpr == nullptr) { (void)ci->diag.DiagnoseRefactor( DiagKindRefactor::conditional_compilation_not_have_condition_expr, anno->begin); return true; } evalResult = EvalConditionExpr(anno->condExpr.operator*()); return true; }; Utils::EraseIf(node->annotations, filterPred); return evalResult; }; void ConditionalCompilationImpl::HandleFileConditionalCompilation(File& file) { auto pred = [this](Ptr node) -> bool { if (auto decl = DynamicCast(node); decl) { return EvalNodeCondition(decl); } else if (auto import = DynamicCast(node); import) { return EvalNodeCondition(import); } else if (auto mc = DynamicCast(node); mc) { return EvalNodeCondition(mc); } return true; }; // Remove Nodes. auto conditionalAction = [&pred](Ptr curNode) -> VisitAction { switch (curNode->astKind) { case ASTKind::FILE: { auto file = StaticAs(curNode); Utils::EraseIf(file->imports, std::not_fn(pred)); Utils::EraseIf(file->decls, std::not_fn(pred)); break; } case ASTKind::INTERFACE_BODY: { auto ib = StaticAs(curNode); Utils::EraseIf(ib->decls, std::not_fn(pred)); break; } case ASTKind::CLASS_BODY: { auto cb = StaticAs(curNode); Utils::EraseIf(cb->decls, std::not_fn(pred)); break; } case ASTKind::STRUCT_BODY: { auto sb = StaticAs(curNode); Utils::EraseIf(sb->decls, std::not_fn(pred)); break; } case ASTKind::ENUM_DECL: { auto ed = StaticAs(curNode); Utils::EraseIf(ed->members, std::not_fn(pred)); Utils::EraseIf(ed->constructors, std::not_fn(pred)); break; } case ASTKind::EXTEND_DECL: { auto ed = StaticAs(curNode); Utils::EraseIf(ed->members, std::not_fn(pred)); break; } case ASTKind::PROP_DECL: { auto pd = StaticAs(curNode); Utils::EraseIf(pd->setters, std::not_fn(pred)); Utils::EraseIf(pd->getters, std::not_fn(pred)); break; } case ASTKind::FUNC_PARAM_LIST: { auto fpl = StaticAs(curNode); Utils::EraseIf(fpl->params, std::not_fn(pred)); break; } case ASTKind::BLOCK: { auto block = StaticAs(curNode); Utils::EraseIf(block->body, std::not_fn(pred)); break; } case ASTKind::MACRO_EXPAND_DECL: case ASTKind::MACRO_EXPAND_EXPR: case ASTKind::MACRO_EXPAND_PARAM: return VisitAction::SKIP_CHILDREN; default: break; } return VisitAction::WALK_CHILDREN; }; Walker astWalker(&file, conditionalAction); astWalker.Walk(); } ConditionalCompilation::ConditionalCompilation(CompilerInstance* ci) : impl{new ConditionalCompilationImpl{ci}} {} ConditionalCompilation::~ConditionalCompilation() { delete impl; } void ConditionalCompilation::HandleConditionalCompilation(const Package& root) const { impl->HandleConditionalCompilation(root); } void ConditionalCompilation::HandleFileConditionalCompilation(File& file) const { impl->HandleFileConditionalCompilation(file); } std::string ConditionalCompilationImpl::GetRelatedInfo(const std::string& target) const { if (target == BACKEND_STR) { return GetBackendType(); } else if (target == ARCH_STR) { return GetArchType(); } else if (target == OS_STR) { return GetOSType(); } else if (target == CJC_VERSION_STR) { return GetCJCVersion(); } else if (target == DEBUG_STR) { return GetDebug(); } else if (target == TEST_STR) { return GetTest(); } return GetUserDefinedInfoByName(target); } cangjie_compiler-1.0.7/src/ConditionalCompilation/ConditionalCompilationImpl.h000066400000000000000000000062071510705540100277020ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares class ConditionalCompilationImpl. */ #ifndef CANGJIE_CONDITIONALCOMPILATION_CONDITIONALCOMPILATIONIMPL_H #define CANGJIE_CONDITIONALCOMPILATION_CONDITIONALCOMPILATIONIMPL_H #include #include "cangjie/AST/Match.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Basic/Utils.h" #include "cangjie/Basic/Version.h" #include "cangjie/ConditionalCompilation/ConditionalCompilation.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Option/Option.h" #include "cangjie/Utils/FileUtil.h" namespace Cangjie::AST { const std::size_t CJC_VERSION_LENGTH = 6; // length of cjc version class ConditionalCompilationImpl { public: explicit ConditionalCompilationImpl(CompilerInstance* c); void HandleConditionalCompilation(const Package& root); void HandleFileConditionalCompilation(File& file); private: inline std::string GetBackendType() const { if (backendType == Triple::BackendType::CJNATIVE) { return "cjnative"; } else { return Triple::BackendToString(backendType); } } inline std::string GetArchType() const { return triple.ArchToString(); } std::string GetOSType() const; inline std::string GetCJCVersion() const { std::string version = std::to_string(cjcVersion); return RefreshVersionStr(version); } inline std::string GetDebug() const { return std::to_string(static_cast(debug)); } inline std::string GetTest() const { return std::to_string(static_cast(test)); } std::string GetUserDefinedInfoByName(const std::string& name) const; inline auto GetPassedValues() const { return passedCondition; } std::string GetRelatedInfo(const std::string& target) const; CompilerInstance* ci{nullptr}; Triple::BackendType backendType; Triple::Info triple; uint32_t cjcVersion; bool debug; bool test; std::unordered_map passedCondition; bool EvalConditionExpr(const Expr& condition); bool ConditionCheck(const std::string& conditionStr, const Position& begin, const std::string& right); bool Eval(const BinaryExpr& expr, const std::string& left, const std::string& right) const; bool EvalBinaryExpr(const BinaryExpr& be); bool EvalParenExpr(const ParenExpr& pe); bool EvalUnaryExpr(const UnaryExpr& ue) const; bool EvalRefExpr(const RefExpr& re) const; bool EvalLogicBinaryExpr(const BinaryExpr& be); bool EvalJudgeBinaryExpr(const BinaryExpr& be); std::string RefreshVersionStr(std::string& version) const { while (version.size() < CJC_VERSION_LENGTH) { version = "0" + version; } return version; } template bool EvalNodeCondition(Ptr node); }; } // namespace Cangjie::AST #endifcangjie_compiler-1.0.7/src/Driver/000077500000000000000000000000001510705540100170315ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Driver/Backend/000077500000000000000000000000001510705540100203605ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Driver/Backend/Backend.cpp000066400000000000000000000026501510705540100224160ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Backend related classes. */ #include "cangjie/Driver/Backend/Backend.h" #include "cangjie/Basic/Print.h" #include "Job.h" namespace { // List of extensions of output files which should not be customized. const std::string AST_EXT = "cjo"; // *.ast file, otherwise it will shadow the real ast file. using namespace Cangjie; // Check file name for final output. bool CheckOutputNameByTrustList(const std::string& file) { auto ext = FileUtil::GetFileExtension(file); if (ext == AST_EXT) { return false; } return true; } }; // namespace bool Backend::Generate() { if (!GenerateToolChain() || !TC) { return false; } TC->InitializeLibraryPaths(); if (!PrepareDependencyPath()) { return false; } // Check invalid suffix of output name for different backends if (!FileUtil::IsDir(driverOptions.output) && !CheckOutputNameByTrustList(driverOptions.output)) { Errorf("file extension '.%s' is not allowed, please change it\n", FileUtil::GetFileExtension(driverOptions.output).c_str()); return false; } return ProcessGeneration(); }cangjie_compiler-1.0.7/src/Driver/Backend/CJNATIVEBackend.cpp000066400000000000000000000363031510705540100235440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the cjnative backend class. */ #include "cangjie/Driver/Backend/CJNATIVEBackend.h" #include "Job.h" #include "cangjie/Driver/TempFileManager.h" #include "cangjie/Driver/ToolOptions.h" #include "Toolchains/CJNATIVE/Linux_CJNATIVE.h" #include "Toolchains/CJNATIVE/Darwin_CJNATIVE.h" #include "Toolchains/CJNATIVE/IOS_CJNATIVE.h" #include "Toolchains/CJNATIVE/Android_CJNATIVE.h" #include "Toolchains/CJNATIVE/MinGW_CJNATIVE.h" #include "Toolchains/CJNATIVE/Ohos_CJNATIVE.h" using namespace Cangjie; using namespace Cangjie::Triple; bool CJNATIVEBackend::GenerateToolChain() { std::string errFieldStr; switch (driverOptions.target.os) { case OSType::LINUX: if (driverOptions.target.env == Environment::OHOS) { TC = std::make_unique(driver, driverOptions, backendCmds); return true; } if (driverOptions.target.env == Environment::ANDROID) { TC = std::make_unique(driver, driverOptions, backendCmds); return true; } if (driverOptions.target.env == Environment::GNU || driverOptions.target.env == Environment::NOT_AVAILABLE) { TC = std::make_unique(driver, driverOptions, backendCmds); return true; } errFieldStr = driverOptions.target.EnvironmentToString(); Errorf("Unsupported Environment Type: %s\n", errFieldStr.c_str()); break; case OSType::WINDOWS: if (driverOptions.target.env == Triple::Environment::GNU) { TC = std::make_unique(driver, driverOptions, backendCmds); return true; } errFieldStr = driverOptions.target.EnvironmentToString(); Errorf("Unsupported Environment Type: %s\n", errFieldStr.c_str()); break; case OSType::DARWIN: TC = std::make_unique(driver, driverOptions, backendCmds); return true; case OSType::IOS: TC = std::make_unique(driver, driverOptions, backendCmds); return true; case OSType::UNKNOWN: default: errFieldStr = driverOptions.target.OSToString(); Errorf("Unsupported OS Type: %s\n", errFieldStr.c_str()); break; } return false; } bool CJNATIVEBackend::PrepareDependencyPath() { std::vector cjnativeBinSearchPaths; // search in CANGJIE_HOME if it is available if (driverOptions.environment.cangjieHome.has_value()) { cjnativeBinSearchPaths.emplace_back( FileUtil::JoinPath(driverOptions.environment.cangjieHome.value(), "third_party/llvm/bin")); } // relative path to the current executing cjc cjnativeBinSearchPaths.emplace_back(FileUtil::JoinPath(driver.cangjieHome, "third_party/llvm/bin")); // They don't have to be in the same location. optPath = FileUtil::FindProgramByName(g_toolList.at(ToolID::OPT).name, cjnativeBinSearchPaths); if (optPath.empty()) { Errorln("not found `opt` in the Cangjie installation, " + CANGJIE_HOME); return false; } llcPath = FileUtil::FindProgramByName(g_toolList.at(ToolID::LLC).name, cjnativeBinSearchPaths); if (llcPath.empty()) { Errorln("not found `llc` in the Cangjie installation, " + CANGJIE_HOME); return false; } return TC->PrepareDependencyPath(); } std::vector CJNATIVEBackend::GetFrontendOutputs() { // Treating input .bc files as input makes passing .bc files possible. The following two commands // should be identical, except that the first one requires two extra jobs for backend compilation. // 1) cjc main.cj pkg1.bc // 2) cjc main.cj pkg1.o std::vector bitCodeFiles; for (const auto& frontendOutput : driverOptions.frontendOutputFiles) { bitCodeFiles.emplace_back(frontendOutput); } for (const auto& bcFile : driverOptions.bcInputFiles) { auto filename = FileUtil::GetFileNameWithoutExtension(bcFile); bitCodeFiles.emplace_back(TempFileInfo{filename, bcFile, bcFile, false, true}); } return bitCodeFiles; } void CJNATIVEBackend::GenerateCacheCopyTool(const std::vector& files, const std::string& extension) { ToolBatch batch{}; for (auto& file : files) { if (file.isForeignInput) { continue; } std::string srcFile = file.filePath; std::string destFile = driverOptions.GetHashedObjFileName(FileUtil::GetFileNameWithoutExtension(srcFile)) + extension; auto tool = std::make_unique("CacheCopy", ToolType::INTERNAL_IMPLEMENTED, driverOptions.environment.allVariables); tool->AppendArg(srcFile, destFile); batch.emplace_back(std::move(tool)); } backendCmds.emplace_back(std::move(batch)); } /** * Generate backend tool and linker. */ bool CJNATIVEBackend::ProcessGeneration() { CJC_ASSERT(!driverOptions.frontendOutputFiles.empty() && "non-compiled file found!"); std::vector bitCodeFiles = GetFrontendOutputs(); return driverOptions.incrementalCompileNoChange ? ProcessGenerationOfIncrementalNoChangeCompile(bitCodeFiles) : ProcessGenerationOfNormalCompile(bitCodeFiles); } bool CJNATIVEBackend::ProcessGenerationOfNormalCompile(const std::vector& bitCodeFiles) { if (driverOptions.IsLTOEnabled() && driverOptions.enIncrementalCompilation) { GenerateCacheCopyTool(bitCodeFiles, ".bc"); } if (driverOptions.IsLTOEnabled()) { std::vector frontendOutputFiles; std::vector preprocessedFiles; for (auto& fileInfo : bitCodeFiles) { if (fileInfo.isFrontendOutput) { frontendOutputFiles.emplace_back(fileInfo); } else { preprocessedFiles.emplace_back(fileInfo); } } auto processedFrontendFiles = GeneratePreprocessTools(frontendOutputFiles); preprocessedFiles.insert(preprocessedFiles.end(), processedFrontendFiles.begin(), processedFrontendFiles.end()); // When compiling a static library in LTO mode, the compilation process stops at the opt stage. if (driverOptions.outputMode == GlobalOptions::OutputMode::STATIC_LIB) { return true; } // In LTO mode, compilation is not performed using llc. return TC->ProcessGeneration(preprocessedFiles); } auto preprocessedFiles = GeneratePreprocessTools(bitCodeFiles); if (driverOptions.saveTemps) { (void)GenerateCompileTool(preprocessedFiles, true); } auto objFiles = GenerateCompileTool(preprocessedFiles); // copy each obj file from temporary directory to cache directory in normal compile case ToolBatch batch{}; for (auto& objFile : objFiles) { std::string srcFile = objFile.filePath; std::string destFile = driverOptions.GetHashedObjFileName(FileUtil::GetFileNameWithoutExtension(srcFile)) + ".o"; if (driverOptions.aggressiveParallelCompile.value_or(1) > 1) { // the format of parallel compile objFile is: number-pkgName.o auto isParallelCompileObjFile = [srcFile] { auto fileName = FileUtil::GetFileNameWithoutExtension(srcFile); auto extension = FileUtil::GetFileExtension(srcFile); auto posOfHyphen = fileName.find("-"); if (posOfHyphen == std::string::npos) { return false; } auto parallelId = fileName.substr(0, posOfHyphen); // if the parallelId is number and extension is 'o', then it is a parallelCompileObjFile. return extension == "o" && parallelId.find_first_not_of("0123456789") == std::string::npos; }; if (isParallelCompileObjFile()) { continue; } } auto tool = std::make_unique("CacheCopy", ToolType::INTERNAL_IMPLEMENTED, driverOptions.environment.allVariables); tool->AppendArg(srcFile); tool->AppendArg(destFile); batch.emplace_back(std::move(tool)); } backendCmds.emplace_back(std::move(batch)); return TC->ProcessGeneration(objFiles); } bool CJNATIVEBackend::ProcessGenerationOfIncrementalNoChangeCompile(const std::vector& bitCodeFiles) { std::vector tempBitCodeFiles = bitCodeFiles; if (driverOptions.IsLTOEnabled()) { std::vector preprocessorInputs; for (auto& file : tempBitCodeFiles) { auto tempFile = file; if (!file.isForeignInput) { std::string filePath = driverOptions.GetHashedObjFileName(FileUtil::GetFileNameWithoutExtension(file.fileName)) + ".bc"; tempFile.filePath = filePath; tempFile.rawPath = filePath; } preprocessorInputs.emplace_back(tempFile); } auto preprocessedFiles = GeneratePreprocessTools(preprocessorInputs); if (driverOptions.outputMode == GlobalOptions::OutputMode::STATIC_LIB) { return true; } return TC->ProcessGeneration(preprocessedFiles); } // 1. check if all `.o` file exist, if any of them does not exist, then run back to // `ProcessGenerationOfNormalCompile`. // 2. if all the `.o` files exist, Update the file path in the `bitCodeFiles` from referencing `.bc` file to the // corresponding `.o` file, and then trigger the ld. for (auto& file : tempBitCodeFiles) { auto objFile = driverOptions.GetHashedObjFileName(file.fileName) + ".o"; if (!FileUtil::FileExist(objFile)) { Errorf("The cache directory is incomplete.\n"); return false; } file.filePath = driverOptions.GetHashedObjFileName(file.fileName) + ".o"; } return TC->ProcessGeneration(tempBitCodeFiles); } void CJNATIVEBackend::PreprocessOfNewPassManager(Tool& tool) { std::string passesCollector = "-passes="; std::vector passItems; { using namespace ToolOptions; SetFuncType setOptimizationLevelHandler = [&passItems]( const std::string& option) { passItems.emplace_back(option); }; SetOptions(setOptimizationLevelHandler, driverOptions, OPT::SetOptimizationLevelOptions); // remove the initial hyphen in the options. SetFuncType setOptionHandler = [&passItems]( const std::string& option) { passItems.emplace_back(option.substr(1)); }; SetOptions(setOptionHandler, driverOptions, OPT::SetNewPassManagerOptions); } for (auto& it : passItems) { passesCollector += it; if (&it != &passItems.back()) { passesCollector += ","; } } tool.AppendArg(passesCollector); } std::vector CJNATIVEBackend::GeneratePreprocessTools(const std::vector& bitCodeFiles) { std::vector outputFiles; ToolBatch batch{}; for (const auto& bitCodeFile : bitCodeFiles) { // 'opt' can only process one file in one execution, for each bitCodeFile, generate one 'opt' command for it. std::unique_ptr tool = GenerateCJNativeBaseTool(optPath); // set input tool->AppendArg(bitCodeFile.filePath); // set options // handle the new pass manager of 'opt' PreprocessOfNewPassManager(*tool); { using namespace ToolOptions; SetFuncType setOptionHandler = [&tool](const std::string& option) { tool->AppendArg(option); }; std::vector setOptionsPass = { OPT::SetOptions, // Comment ensure vector members are arranged vertically. OPT::SetVerifyOptions, // OPT::SetTripleOptions, // OPT::SetCodeObfuscationOptions, // OPT::SetLTOOptions, // OPT::SetPgoOptions, // OPT::SetTransparentOptions // The transparent options must after other options. }; SetOptions(setOptionHandler, driverOptions, setOptionsPass); } // set output // When compiling a static library in LTO mode // the optimized bc file generated by the opt phase is used as the output file. TempFileKind kind = driverOptions.IsLTOEnabled() && driverOptions.outputMode == GlobalOptions::OutputMode::STATIC_LIB ? TempFileKind::O_OPT_BC : TempFileKind::T_OPT_BC; TempFileInfo optBcFileInfo = TempFileManager::Instance().CreateNewFileInfo(bitCodeFile, kind); tool->AppendArg("-o", optBcFileInfo.filePath); outputFiles.emplace_back(optBcFileInfo); batch.emplace_back(std::move(tool)); } backendCmds.emplace_back(std::move(batch)); return outputFiles; } std::vector CJNATIVEBackend::GenerateCompileTool( const std::vector& bitCodeFiles, bool emitAssembly) { std::vector outputFiles; ToolBatch batch{}; for (const auto& bitCodeFile : bitCodeFiles) { // 'llc' can only process one file in one execution, for each bitCodeFile, // generate one 'llc' command for it, just like 'opt'. std::unique_ptr tool = GenerateCJNativeBaseTool(llcPath); // set input tool->AppendArg(bitCodeFile.filePath); // set options { using namespace ToolOptions; SetFuncType setOptionHandler = [&tool](const std::string& option) { tool->AppendArg(option); }; std::vector setOptionsPass = { LLC::SetOptions, // Comment ensure vector members are arranged vertically. LLC::SetTripleOptions, // LLC::SetOptimizationLevelOptions, // LLC::SetTransparentOptions, // The transparent options must after other options. }; SetOptions(setOptionHandler, driverOptions, setOptionsPass); } // set output tool->AppendArg(emitAssembly ? "--filetype=asm" : "--filetype=obj"); auto fileKind = emitAssembly ? TempFileKind::T_ASM : TempFileKind::T_OBJ; TempFileInfo fileInfo = TempFileManager::Instance().CreateNewFileInfo(bitCodeFile, fileKind); tool->AppendArg("-o", fileInfo.filePath); outputFiles.emplace_back(fileInfo); batch.emplace_back(std::move(tool)); } backendCmds.emplace_back(std::move(batch)); return outputFiles; } std::unique_ptr CJNATIVEBackend::GenerateCJNativeBaseTool(const std::string& toolPath) { auto tool = std::make_unique(toolPath, ToolType::BACKEND, driverOptions.environment.allVariables); tool->SetLdLibraryPath(FileUtil::JoinPath(FileUtil::GetDirPath(toolPath), "../lib")); return tool; } cangjie_compiler-1.0.7/src/Driver/CMakeLists.txt000066400000000000000000000032161510705540100215730ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. if(CANGJIE_BUILD_CJC) set(SRC_FILES ./*.cpp Toolchains/GCCPathScanner.cpp Toolchains/Gnu.cpp Toolchains/MachO.cpp ./Backend/Backend.cpp) if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) set(SRC_FILES ${SRC_FILES} Backend/CJNATIVEBackend.cpp Toolchains/ToolChain.cpp Toolchains/CJNATIVE/Linux_CJNATIVE.cpp Toolchains/CJNATIVE/MinGW_CJNATIVE.cpp Toolchains/CJNATIVE/Darwin_CJNATIVE.cpp Toolchains/CJNATIVE/IOS_CJNATIVE.cpp Toolchains/CJNATIVE/Android_CJNATIVE.cpp Toolchains/CJNATIVE/Ohos_CJNATIVE.cpp) endif() file(GLOB DRIVER_SRC ${SRC_FILES}) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) add_library(CangjieDriver OBJECT ${DRIVER_SRC}) # use llvm if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_dependencies(CangjieDriver cjnative) endif() target_include_directories(CangjieDriver PRIVATE ${LLVM_INCLUDE_DIRS}) if(CANGJIE_DISABLE_STACK_GROW_FEATURE) target_compile_definitions(CangjieDriver PRIVATE CANGJIE_DISABLE_STACK_GROW_FEATURE) endif() target_compile_options(CangjieDriver PRIVATE ${CJC_WITH_LLVM_EXTRA_WARNINGS}) endif() # build for lsp set(LIB_TEMP_FILES_UTIL TempFileManager.cpp) add_library(CangjieTempFilesUtil OBJECT ${LIB_TEMP_FILES_UTIL}) target_compile_options(CangjieTempFilesUtil PRIVATE ${CJC_WITH_LLVM_EXTRA_WARNINGS}) cangjie_compiler-1.0.7/src/Driver/Driver.cpp000066400000000000000000000175161510705540100210020ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Driver. */ #include "cangjie/Driver/Driver.h" #ifdef __linux__ #include #endif #include #include #include #include "cangjie/Basic/Print.h" #include "cangjie/Basic/Version.h" #include "cangjie/Driver/TempFileManager.h" #include "cangjie/Driver/Utils.h" #include "cangjie/FrontendTool/DefaultCompilerInstance.h" #include "cangjie/FrontendTool/IncrementalCompilerInstance.h" #include "cangjie/FrontendTool/FrontendTool.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/ProfileRecorder.h" // DO NOT remove this header file, otherwise, the signal handling of Driver will fail: #include "cangjie/Utils/Signal.h" #include "Job.h" using namespace Cangjie; Driver::Driver(const std::vector& args, DiagnosticEngine& diag, const std::string& exeName) : args(args), diag(diag), executableName(exeName) { optionTable = CreateOptionTable(false); CJC_ASSERT(optionTable && "create option table failed"); argList = std::make_unique(); driverOptions = std::make_unique(); // Get absolute path of `cjc` executable program. driverOptions->executablePath = executableName; cangjieHome = FileUtil::GetDirPath(FileUtil::GetDirPath(FileUtil::GetAbsPath(exeName) | FileUtil::IdenticalFunc)); } namespace { bool StartsWith(const std::string str, const std::string prefix) { return str.rfind(prefix, 0) == 0; } bool IsWarningArg(const std::string& arg) { return StartsWith(arg, "-Woff") || StartsWith(arg, "--warn-off") || StartsWith(arg, "-Won") || StartsWith(arg, "--warn-on"); } void FrontendCmdPrint(const GlobalOptions& globalOptions, const std::string& cangjieHome, const std::vector& args) { CJC_ASSERT(args.size() != 0); std::string binPath = FileUtil::JoinPath(cangjieHome, "bin"); std::string exeExtension = #ifdef _WIN32 ".exe"; #else ""; #endif binPath = FileUtil::JoinPath(binPath, "cjc-frontend" + exeExtension); std::string frontendCmd = GetCommandLineArgumentQuoted(binPath); std::vector optionList = globalOptions.GenerateFrontendOptions(); for (auto& str : optionList) { frontendCmd += " " + GetCommandLineArgumentQuoted(str); } Println(FileUtil::Normalize(frontendCmd)); } bool DeleteInstance(DefaultCompilerInstance* instance) { Utils::ProfileRecorder recorder("DeleteInstance", "CompleteTime"); delete instance; #ifdef __linux__ (void)malloc_trim(0); // After the memory is released, the heap memory space shrinks to the minimum size, which // makes better use of the memory space and avoids memory waste. #endif return true; } } // namespace bool Driver::ParseArgs() { CJC_NULLPTR_CHECK(optionTable); if (!optionTable->ParseArgs(args, *argList)) { return false; } // All the warning options need to be preceded by other options. // Otherwise, the warnings generated by some options cannot be applied to. std::vector> warningArgs; std::vector> otherArgs; CJC_NULLPTR_CHECK(argList); for (auto& arg : argList->args) { if (IsWarningArg(arg->str)) { warningArgs.push_back(std::move(arg)); } else { otherArgs.push_back(std::move(arg)); } } argList->args.clear(); argList->args.resize(warningArgs.size() + otherArgs.size()); std::transform( warningArgs.begin(), warningArgs.end(), argList->args.begin(), [](auto& it) { return std::move(it); }); std::transform(otherArgs.begin(), otherArgs.end(), argList->args.begin() + static_cast(warningArgs.size()), [](auto& it) { return std::move(it); }); CJC_NULLPTR_CHECK(driverOptions); if (!driverOptions->ParseFromArgs(*argList)) { return false; } driverOptions->SetCompilationCachedPath(); return true; } void Driver::EnvironmentSetup(const std::unordered_map& environmentVars) { CJC_NULLPTR_CHECK(driverOptions); driverOptions->ReadPathsFromEnvironmentVars(environmentVars); driverOptions->cangjieHome = driverOptions->environment.cangjieHome.value_or(cangjieHome); } bool Driver::ExecuteCompilation() const { if (driverOptions == nullptr) { return false; } diag.RegisterHandler(driverOptions->diagFormat); // Do nothing else but show usage. if (driverOptions->showUsage) { const std::set groups{Options::Group::GLOBAL, Options::Group::DRIVER}; optionTable->Usage(driverOptions->GetOptionsBackend(), groups, driverOptions->experimentalMode); if (!argList->GetInputs().empty()) { Warningln("cjc doesn't do compilation job in show usage mode."); } return true; } if (driverOptions->enableVerbose || driverOptions->printVersionOnly) { Cangjie::PrintVersion(); } if (driverOptions->printVersionOnly) { if (!argList->GetInputs().empty()) { Warningln("cjc doesn't do compilation job in version check mode."); } return true; } if (driverOptions->enableTimer) { Utils::ProfileRecorder::Enable(true, Utils::ProfileRecorder::Type::TIMER); } if (driverOptions->enableMemoryCollect) { Utils::ProfileRecorder::Enable(true, Utils::ProfileRecorder::Type::MEMORY); } if (!TempFileManager::Instance().Init(*driverOptions, false)) { return false; } CompilerInvocation compilerInvocation; compilerInvocation.globalOptions = *driverOptions; compilerInvocation.globalOptions.executablePath = executableName; compilerInvocation.globalOptions.environment = driverOptions->environment; // In Driver mode, FrontendOptions is never parsed, we need to do setup manually. if (compilerInvocation.globalOptions.scanDepPkg) { compilerInvocation.frontendOptions.dumpAction = FrontendOptions::DumpAction::DUMP_DEP_PKG; } diag.SetErrorCountLimit(compilerInvocation.globalOptions.errorCountLimit); diag.RegisterHandler(driverOptions->diagFormat); DefaultCompilerInstance* instance = nullptr; if (NeedCreateIncrementalCompilerInstance(compilerInvocation.globalOptions)) { instance = new IncrementalCompilerInstance(compilerInvocation, diag); } else { instance = new DefaultCompilerInstance(compilerInvocation, diag); } if (!ExecuteFrontendByDriver(*instance, *this)) { DeleteInstance(instance); return false; } if (driverOptions->enableVerbose) { FrontendCmdPrint(*driverOptions.get(), cangjieHome, args); } if (driverOptions->frontendOutputFiles.empty() || driverOptions->IsEmitCHIREnable()) { DeleteInstance(instance); return true; } std::future future = std::async(DeleteInstance, instance); bool res = InvokeCompileToolchain(); Utils::ProfileRecorder::Start("Main Stage", "DeleteInstanceLeftTime"); future.get(); Utils::ProfileRecorder::Stop("Main Stage", "DeleteInstanceLeftTime"); return res; } bool Driver::InvokeCompileToolchain() const { #ifdef SIGNAL_TEST // The interrupt signal triggers the function. In normal cases, this function does not take effect. Cangjie::SignalTest::ExecuteSignalTestCallbackFunc(Cangjie::SignalTest::TriggerPointer::DRIVER_POINTER); #endif // Job Initialization. std::unique_ptr job = std::make_unique(); if (!job->Assemble(*driverOptions.get(), *this)) { return false; } // Execute Job. bool executeResult = job->Execute(); return executeResult; } cangjie_compiler-1.0.7/src/Driver/DriverOptions.cpp000066400000000000000000000312571510705540100223540ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the DriverOptions. */ #include "cangjie/Driver/DriverOptions.h" #include "cangjie/Utils/Semaphore.h" #include #include "cangjie/Basic/DiagnosticEngine.h" /** * OPTION_TRUE_ACTION is a help macro for defining an option action which takes no option * arguments and does no error checks. It takes simple expression as argument and expands * the expression to an action lambda. */ #define OPTION_TRUE_ACTION(EXPR) [](DriverOptions& opts, OptionArgInstance&) { (EXPR); return true; } namespace { const std::unordered_set X86_64_TARGET_CPUS = { #define CANGJIE_X86_64_TARGET_CPU(TARGET_CPU_NAME) (TARGET_CPU_NAME), #include "TargetCPUs.inc" #undef CANGJIE_X86_64_TARGET_CPU }; const std::unordered_set AARCH64_TARGET_CPUS = { #define CANGJIE_AARCH64_TARGET_CPU(TARGET_CPU_NAME) (TARGET_CPU_NAME), #include "TargetCPUs.inc" #undef CANGJIE_AARCH64_TARGET_CPU }; } using namespace Cangjie; namespace { std::unordered_map> g_actions = { { Options::ID::STATIC, OPTION_TRUE_ACTION(opts.linkStatic = true) }, { Options::ID::TARGET_CPU, [](DriverOptions& opts, const OptionArgInstance& arg) { opts.targetCPU = arg.value; return true; }}, { Options::ID::OPT_OPTIONS, [](DriverOptions& opts, const OptionArgInstance& arg) { opts.optArg = arg.value; return true; }}, { Options::ID::LLC_OPTIONS, [](DriverOptions& opts, const OptionArgInstance& arg) { opts.llcArg = arg.value; return true; }}, { Options::ID::LIBRARY_PATH, [](DriverOptions& opts, const OptionArgInstance& arg) { auto maybePath = opts.CheckDirectoryPath(arg.value); if (maybePath.has_value()) { opts.librarySearchPaths.emplace_back(maybePath.value()); } return true; }}, { Options::ID::LIBRARY, [](DriverOptions& opts, const OptionArgInstance& arg) { opts.libraries.emplace_back(arg.value); return true; }}, { Options::ID::TOOLCHAIN, [](DriverOptions& opts, const OptionArgInstance& arg) { auto maybePath = opts.CheckDirectoryPath(arg.value); if (maybePath.has_value()) { opts.toolChainPaths.emplace_back(maybePath.value()); } return true; }}, { Options::ID::SYSROOT, [](DriverOptions& opts, const OptionArgInstance& arg) { auto maybePath = opts.CheckDirectoryPath(arg.value); if (maybePath.has_value()) { opts.sysroot = maybePath.value(); opts.customizedSysroot = true; } return true; }}, { Options::ID::LINK_OPTION, [](DriverOptions& opts, const OptionArgInstance& arg) { opts.linkOption.emplace_back(arg.value); return true; }}, { Options::ID::LINK_OPTIONS, [](DriverOptions& opts, const OptionArgInstance& arg) { opts.linkOptions.emplace_back(arg.value); return true; }}, { Options::ID::STRIP_ALL, OPTION_TRUE_ACTION(opts.stripSymbolTable = true) }, { Options::ID::USE_RUNTIME_RPATH, OPTION_TRUE_ACTION(opts.useRuntimeRpath = true) }, { Options::ID::SANITIZE_SET_RPATH, OPTION_TRUE_ACTION(opts.sanitizerEnableRpath = true) }, // ---------- CODE OBFUSCATION OPTIONS ---------- { Options::ID::OBF_STRING, OPTION_TRUE_ACTION(opts.enableStringObfuscation = true) }, { Options::ID::NO_OBF_STRING, OPTION_TRUE_ACTION(opts.enableStringObfuscation = false) }, { Options::ID::OBF_CONST, OPTION_TRUE_ACTION(opts.enableConstObfuscation = true) }, { Options::ID::NO_OBF_CONST, OPTION_TRUE_ACTION(opts.enableConstObfuscation = false) }, { Options::ID::OBF_LAYOUT, OPTION_TRUE_ACTION(opts.enableLayoutObfuscation = true) }, { Options::ID::NO_OBF_LAYOUT, OPTION_TRUE_ACTION(opts.enableLayoutObfuscation = false) }, { Options::ID::OBF_CF_FLATTEN, OPTION_TRUE_ACTION(opts.enableCFflattenObfuscation = true) }, { Options::ID::NO_OBF_CF_FLATTEN, OPTION_TRUE_ACTION(opts.enableCFflattenObfuscation = false) }, { Options::ID::OBF_CF_BOGUS, OPTION_TRUE_ACTION(opts.enableCFBogusObfuscation = true) }, { Options::ID::NO_OBF_CF_BOGUS, OPTION_TRUE_ACTION(opts.enableCFBogusObfuscation = false) }, { Options::ID::OBF_EXPORT_SYMS, OPTION_TRUE_ACTION(opts.enableObfExportSyms = true) }, { Options::ID::NO_OBF_EXPORT_SYMS, OPTION_TRUE_ACTION(opts.enableObfExportSyms = false) }, { Options::ID::OBF_LINE_NUMBER, OPTION_TRUE_ACTION(opts.enableObfLineNumber = true) }, { Options::ID::NO_OBF_LINE_NUMBER, OPTION_TRUE_ACTION(opts.enableObfLineNumber = false) }, { Options::ID::OBF_SOURCE_PATH, OPTION_TRUE_ACTION(opts.enableObfSourcePath = true) }, { Options::ID::NO_OBF_SOURCE_PATH, OPTION_TRUE_ACTION(opts.enableObfSourcePath = false) }, { Options::ID::OBF_ALL, OPTION_TRUE_ACTION(opts.enableObfAll = true) }, { Options::ID::OBF_SYM_INPUT_MAPPING, [](DriverOptions& opts, const OptionArgInstance& arg) { opts.layoutObfInputSymMappingFiles = arg.value; return true; }}, { Options::ID::OBF_SYM_OUTPUT_MAPPING, [](DriverOptions& opts, const OptionArgInstance& arg) { opts.layoutObfOutputSymMappingFile = arg.value; return true; }}, { Options::ID::OBF_MAP_FILE, [](DriverOptions& opts, const OptionArgInstance& arg) { opts.layoutObfUserMappingFile = opts.ValidateInputFilePath( arg.value, DiagKindRefactor::no_such_file_or_directory); return opts.layoutObfUserMappingFile.has_value(); }}, { Options::ID::OBF_SYM_PREFIX, [](DriverOptions& opts, const OptionArgInstance& arg) { opts.layoutObfSymPrefix = arg.value; return true; }}, { Options::ID::OBF_CONFIG, [](DriverOptions& opts, const OptionArgInstance& arg) { opts.obfuscationConfigFile = opts.ValidateInputFilePath(arg.value, DiagKindRefactor::no_such_file_or_directory); return opts.obfuscationConfigFile.has_value(); }}, { Options::ID::OBF_LEVEL, [](DriverOptions& opts, const OptionArgInstance& arg) { auto maybeNumber = DriverOptions::ParseIntOptionValue(arg, DriverOptions::OBFUCATION_LEVEL_MIN, DriverOptions::OBFUCATION_LEVEL_MAX); if (!maybeNumber.has_value()) { return false; } opts.obfuscationLevel = {maybeNumber.value()}; return true; }}, { Options::ID::OBF_SEED, [](DriverOptions& opts, const OptionArgInstance& arg) { auto maybeNumber = DriverOptions::ParseIntOptionValue(arg); if (!maybeNumber.has_value()) { return false; } opts.obfuscationSeed = maybeNumber.value(); return true; }}, }; } // namespace std::optional DriverOptions::ParseOption(OptionArgInstance& arg) { if (g_actions.find(arg.info.GetID()) == g_actions.end()) { return GlobalOptions::ParseOption(arg); } return {g_actions[arg.info.GetID()](*this, arg)}; } bool DriverOptions::ReprocessObfuseOption() { if (enableObfAll) { if (!enableStringObfuscation.has_value()) { enableStringObfuscation = true; } if (!enableConstObfuscation.has_value()) { enableConstObfuscation = true; } if (!enableLayoutObfuscation.has_value()) { enableLayoutObfuscation = true; } if (!enableCFflattenObfuscation.has_value()) { enableCFflattenObfuscation = true; } if (!enableCFBogusObfuscation.has_value()) { enableCFBogusObfuscation = true; } if (!enableObfLineNumber.has_value() && enableLayoutObfuscation) { enableObfLineNumber = true; } } return true; } bool DriverOptions::PerformPostActions() { if (!GlobalOptions::PerformPostActions()) { return false; } bool success = true; success = success && CheckTargetCPUOption(); success = success && CheckObfuscationOptions(); success = success && SetupSysroot(); success = success && CheckRuntimeRPath(); success = success && CheckSanitizerRPath(); success = success && CheckStaticOption(); return success; } bool DriverOptions::CheckObfuscationOptions() const { if (enableCompileDebug) { bool obfuscationEnabled = enableStringObfuscation || enableConstObfuscation || enableLayoutObfuscation || enableCFflattenObfuscation || enableCFBogusObfuscation; if (obfuscationEnabled) { Warningf("Obfuscation options should be used without '-g' or debugging may not work properly.\n"); } } if (!enableLayoutObfuscation && layoutObfInputSymMappingFiles.has_value()) { Errorf("Input symbol mapping files can be specified only when layout obfuscation is enabled.\n"); return false; } if (!enableLayoutObfuscation && layoutObfOutputSymMappingFile.has_value()) { Errorf("Output symbol mapping file can be specified only when layout obfuscation is enabled.\n"); return false; } if (!enableLayoutObfuscation && layoutObfUserMappingFile.has_value()) { Errorf("User-defined symbol mapping file can be specified only when layout obfuscation is enabled.\n"); return false; } if (!enableLayoutObfuscation && enableObfExportSyms.has_value()) { Errorf("You can set whether to obfuscate export symbols only when layout obfuscation is enabled.\n"); return false; } if (!enableLayoutObfuscation && enableObfLineNumber.has_value()) { Errorf("You can set whether to obfuscate line number only when layout obfuscation is enabled.\n"); return false; } if (!enableLayoutObfuscation && enableObfSourcePath.has_value()) { Errorf("You can set whether to obfuscate source path only when layout obfuscation is enabled.\n"); return false; } if (!enableLayoutObfuscation && layoutObfSymPrefix.has_value()) { Errorf("Symbol prefix can be specified only when layout obfuscation is enabled.\n"); return false; } return true; } bool DriverOptions::CheckTargetCPUOption() { // If target is MacOS, target CPU can be a null value, no further process is needed. if (target.IsMacOS() && !targetCPU.has_value()) { return true; } // If target is not MacOS, we set generic as default target CPU. targetCPU = targetCPU.value_or("generic"); if (targetCPU.value() == "generic" || targetCPU.value() == "native") { return true; } bool isSupportedTargetCPU = (target.arch == Triple::ArchType::AARCH64 && AARCH64_TARGET_CPUS.find(targetCPU.value()) != AARCH64_TARGET_CPUS.end()) || (target.arch == Triple::ArchType::X86_64 && X86_64_TARGET_CPUS.find(targetCPU.value()) != X86_64_TARGET_CPUS.end()); if (isSupportedTargetCPU) { return true; } DiagnosticEngine diag; (void)diag.DiagnoseRefactor(DiagKindRefactor::driver_unsupported_target_cpu, DEFAULT_POSITION, targetCPU.value()); return false; } bool DriverOptions::SetupSysroot() { if (!customizedSysroot) { if (target.IsMacOS() && environment.macOSSDKRoot.has_value()) { sysroot = environment.macOSSDKRoot.value(); } } return true; } bool DriverOptions::CheckRuntimeRPath() const { if (!useRuntimeRpath) { return true; } if (target.os == Triple::OSType::WINDOWS) { Warningln("'--set-runtime-rpath' option has no effect on PE targets."); } else if (IsCrossCompiling()) { Warningln("'--set-runtime-rpath' option has no effect on cross-compiled targets."); } return true; } bool DriverOptions::CheckSanitizerRPath() const { if (!sanitizerEnableRpath) { return true; } if (!EnableSanitizer()) { Errorln("Option '--sanitize-set-rpath' can only be enabled along with '--sanitize'"); return false; } if (useRuntimeRpath) { Errorln("Option '--sanitize-set-rpath' and '--set-runtime-rpath' cannot be enabled together."); return false; } if (IsCrossCompiling()) { Warningln("'--sanitize-set-rpath' option has no effect on cross-compiled targets."); } return true; } bool DriverOptions::CheckStaticOption() { if (linkStatic && (target.os != Triple::OSType::LINUX || target.env == Triple::Environment::OHOS)) { linkStatic = false; Warningln("'--static' option has only effect on Linux targets."); } if (linkStatic && (outputMode != OutputMode::EXECUTABLE)) { linkStatic = false; Warningln("'--static' option is only effective when compiling executables."); } return true; } cangjie_compiler-1.0.7/src/Driver/Job.cpp000066400000000000000000000074571510705540100202640ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Job Class. */ #include "Job.h" #include "cangjie/Basic/Print.h" #include "cangjie/Driver/Tool.h" #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "cangjie/Driver/Backend/CJNATIVEBackend.h" #endif #include "cangjie/Driver/TempFileManager.h" #include "cangjie/Driver/Toolchains/ToolChain.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/ProfileRecorder.h" #include "cangjie/Utils/Semaphore.h" namespace { bool CheckExecuteResult(std::map>& checklist, bool returnIfAnyToolFinished = false) { auto printError = [](std::string cmd) { if (!TempFileManager::Instance().IsDeleted()) { Errorln(cmd, ": command failed (use -V to see invocation)"); } }; bool success = true; while (!checklist.empty()) { size_t totalTasks = checklist.size(); for (auto it = checklist.cbegin(); it != checklist.cend();) { auto state = it->second->GetState(); if (state == ToolFuture::State::FAILED) { Utils::Semaphore::Get().Release(); printError(it->first); checklist.erase(it++); success = false; } else if (state == ToolFuture::State::SUCCESS) { Utils::Semaphore::Get().Release(); checklist.erase(it++); } else { ++it; } } if (returnIfAnyToolFinished && totalTasks != checklist.size()) { // Some tasks are finished and removed from the list. return success; } std::this_thread::sleep_for(std::chrono::microseconds(200)); // Check running tasks every 200 ms. } return success; } } // namespace using namespace Cangjie; bool Job::Assemble(const DriverOptions& driverOptions, const Driver& driver) { switch (driverOptions.backend) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND case Triple::BackendType::CJNATIVE: backend = std::make_unique(*this, driverOptions, driver); break; #endif case Triple::BackendType::UNKNOWN: default: Errorln("Toolchain: Unsupported backend"); return false; } if (!backend->Generate()) { return false; } verbose = driverOptions.enableVerbose; return true; } bool Job::Execute() const { const std::vector& commandList = backend->GetBackendCmds(); for (const ToolBatch& cmdBatch : commandList) { if (cmdBatch.empty()) { continue; } std::map> childWorkers{}; Utils::ProfileRecorder recorder("Main Stage", "Execute " + FileUtil::GetFileName(cmdBatch[0]->GetName()), ""); for (auto& cmd : cmdBatch) { // NOTE: `CheckExecuteResult` acquires semaphore without condition. We must ensure that there is still // available slot in semaphore before executing next command. If there is no more slot available, wait // any of previous created threads finish. while (Utils::Semaphore::Get().GetCount() == 0) { if (!CheckExecuteResult(childWorkers, true)) { return false; } } auto future = cmd->Execute(verbose); if (!future) { return false; } childWorkers.emplace(cmd->GetCommandString(), std::move(future)); } if (!CheckExecuteResult(childWorkers)) { return false; } } return true; }cangjie_compiler-1.0.7/src/Driver/Job.h000066400000000000000000000020511510705540100177120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Driver's Job class. */ #ifndef CANGJIE_DRIVER_JOB_H #define CANGJIE_DRIVER_JOB_H #include #include #include "cangjie/Driver/Backend/Backend.h" #include "cangjie/Driver/DriverOptions.h" #include "cangjie/Driver/Toolchains/ToolChain.h" #include "cangjie/Option/Option.h" namespace Cangjie { class Job { public: Job() { } ~Job() = default; /** * Assembly the job and toolchain command. */ bool Assemble(const DriverOptions& driverOptions, const Driver& driver); /** * Execute compilation job. */ bool Execute() const; private: std::unique_ptr backend; std::vector tmpFiles; bool verbose{false}; }; } // namespace Cangjie #endif // CANGJIE_DRIVER_JOB_H cangjie_compiler-1.0.7/src/Driver/TargetCPUs.inc000066400000000000000000000135131510705540100215100ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file lists all values --target-cpu option supports (Equivalent to what LLVM 15.0.4 supports). */ #ifdef CANGJIE_X86_64_TARGET_CPU CANGJIE_X86_64_TARGET_CPU("alderlake") CANGJIE_X86_64_TARGET_CPU("amdfam10") CANGJIE_X86_64_TARGET_CPU("athlon") CANGJIE_X86_64_TARGET_CPU("athlon-4") CANGJIE_X86_64_TARGET_CPU("athlon-fx") CANGJIE_X86_64_TARGET_CPU("athlon-mp") CANGJIE_X86_64_TARGET_CPU("athlon-tbird") CANGJIE_X86_64_TARGET_CPU("athlon-xp") CANGJIE_X86_64_TARGET_CPU("athlon64") CANGJIE_X86_64_TARGET_CPU("athlon64-sse3") CANGJIE_X86_64_TARGET_CPU("atom") CANGJIE_X86_64_TARGET_CPU("barcelona") CANGJIE_X86_64_TARGET_CPU("bdver1") CANGJIE_X86_64_TARGET_CPU("bdver2") CANGJIE_X86_64_TARGET_CPU("bdver3") CANGJIE_X86_64_TARGET_CPU("bdver4") CANGJIE_X86_64_TARGET_CPU("bonnell") CANGJIE_X86_64_TARGET_CPU("broadwell") CANGJIE_X86_64_TARGET_CPU("btver1") CANGJIE_X86_64_TARGET_CPU("btver2") CANGJIE_X86_64_TARGET_CPU("c3") CANGJIE_X86_64_TARGET_CPU("c3-2") CANGJIE_X86_64_TARGET_CPU("cannonlake") CANGJIE_X86_64_TARGET_CPU("cascadelake") CANGJIE_X86_64_TARGET_CPU("cooperlake") CANGJIE_X86_64_TARGET_CPU("core-avx-i") CANGJIE_X86_64_TARGET_CPU("core-avx2") CANGJIE_X86_64_TARGET_CPU("core2") CANGJIE_X86_64_TARGET_CPU("corei7") CANGJIE_X86_64_TARGET_CPU("corei7-avx") CANGJIE_X86_64_TARGET_CPU("generic") CANGJIE_X86_64_TARGET_CPU("geode") CANGJIE_X86_64_TARGET_CPU("goldmont") CANGJIE_X86_64_TARGET_CPU("goldmont-plus") CANGJIE_X86_64_TARGET_CPU("haswell") CANGJIE_X86_64_TARGET_CPU("i386") CANGJIE_X86_64_TARGET_CPU("i486") CANGJIE_X86_64_TARGET_CPU("i586") CANGJIE_X86_64_TARGET_CPU("i686") CANGJIE_X86_64_TARGET_CPU("icelake-client") CANGJIE_X86_64_TARGET_CPU("icelake-server") CANGJIE_X86_64_TARGET_CPU("ivybridge") CANGJIE_X86_64_TARGET_CPU("k6") CANGJIE_X86_64_TARGET_CPU("k6-2") CANGJIE_X86_64_TARGET_CPU("k6-3") CANGJIE_X86_64_TARGET_CPU("k8") CANGJIE_X86_64_TARGET_CPU("k8-sse3") CANGJIE_X86_64_TARGET_CPU("knl") CANGJIE_X86_64_TARGET_CPU("knm") CANGJIE_X86_64_TARGET_CPU("lakemont") CANGJIE_X86_64_TARGET_CPU("nehalem") CANGJIE_X86_64_TARGET_CPU("nocona") CANGJIE_X86_64_TARGET_CPU("opteron") CANGJIE_X86_64_TARGET_CPU("opteron-sse3") CANGJIE_X86_64_TARGET_CPU("penryn") CANGJIE_X86_64_TARGET_CPU("pentium") CANGJIE_X86_64_TARGET_CPU("pentium-m") CANGJIE_X86_64_TARGET_CPU("pentium-mmx") CANGJIE_X86_64_TARGET_CPU("pentium2") CANGJIE_X86_64_TARGET_CPU("pentium3") CANGJIE_X86_64_TARGET_CPU("pentium3m") CANGJIE_X86_64_TARGET_CPU("pentium4") CANGJIE_X86_64_TARGET_CPU("pentium4m") CANGJIE_X86_64_TARGET_CPU("pentiumpro") CANGJIE_X86_64_TARGET_CPU("prescott") CANGJIE_X86_64_TARGET_CPU("rocketlake") CANGJIE_X86_64_TARGET_CPU("sandybridge") CANGJIE_X86_64_TARGET_CPU("sapphirerapids") CANGJIE_X86_64_TARGET_CPU("silvermont") CANGJIE_X86_64_TARGET_CPU("skx") CANGJIE_X86_64_TARGET_CPU("skylake") CANGJIE_X86_64_TARGET_CPU("skylake-avx512") CANGJIE_X86_64_TARGET_CPU("slm") CANGJIE_X86_64_TARGET_CPU("tigerlake") CANGJIE_X86_64_TARGET_CPU("tremont") CANGJIE_X86_64_TARGET_CPU("westmere") CANGJIE_X86_64_TARGET_CPU("winchip-c6") CANGJIE_X86_64_TARGET_CPU("winchip2") CANGJIE_X86_64_TARGET_CPU("x86-64") CANGJIE_X86_64_TARGET_CPU("x86-64-v2") CANGJIE_X86_64_TARGET_CPU("x86-64-v3") CANGJIE_X86_64_TARGET_CPU("x86-64-v4") CANGJIE_X86_64_TARGET_CPU("yonah") CANGJIE_X86_64_TARGET_CPU("znver1") CANGJIE_X86_64_TARGET_CPU("znver2") CANGJIE_X86_64_TARGET_CPU("znver3") #endif #ifdef CANGJIE_AARCH64_TARGET_CPU CANGJIE_AARCH64_TARGET_CPU("a64fx") CANGJIE_AARCH64_TARGET_CPU("ampere1") CANGJIE_AARCH64_TARGET_CPU("apple-a10") CANGJIE_AARCH64_TARGET_CPU("apple-a11") CANGJIE_AARCH64_TARGET_CPU("apple-a12") CANGJIE_AARCH64_TARGET_CPU("apple-a13") CANGJIE_AARCH64_TARGET_CPU("apple-a14") CANGJIE_AARCH64_TARGET_CPU("apple-a7") CANGJIE_AARCH64_TARGET_CPU("apple-a8") CANGJIE_AARCH64_TARGET_CPU("apple-a9") CANGJIE_AARCH64_TARGET_CPU("apple-latest") CANGJIE_AARCH64_TARGET_CPU("apple-m1") CANGJIE_AARCH64_TARGET_CPU("apple-s4") CANGJIE_AARCH64_TARGET_CPU("apple-s5") CANGJIE_AARCH64_TARGET_CPU("carmel") CANGJIE_AARCH64_TARGET_CPU("cortex-a34") CANGJIE_AARCH64_TARGET_CPU("cortex-a35") CANGJIE_AARCH64_TARGET_CPU("cortex-a510") CANGJIE_AARCH64_TARGET_CPU("cortex-a53") CANGJIE_AARCH64_TARGET_CPU("cortex-a55") CANGJIE_AARCH64_TARGET_CPU("cortex-a57") CANGJIE_AARCH64_TARGET_CPU("cortex-a65") CANGJIE_AARCH64_TARGET_CPU("cortex-a65ae") CANGJIE_AARCH64_TARGET_CPU("cortex-a710") CANGJIE_AARCH64_TARGET_CPU("cortex-a72") CANGJIE_AARCH64_TARGET_CPU("cortex-a73") CANGJIE_AARCH64_TARGET_CPU("cortex-a75") CANGJIE_AARCH64_TARGET_CPU("cortex-a76") CANGJIE_AARCH64_TARGET_CPU("cortex-a76ae") CANGJIE_AARCH64_TARGET_CPU("cortex-a77") CANGJIE_AARCH64_TARGET_CPU("cortex-a78") CANGJIE_AARCH64_TARGET_CPU("cortex-a78c") CANGJIE_AARCH64_TARGET_CPU("cortex-r82") CANGJIE_AARCH64_TARGET_CPU("cortex-x1") CANGJIE_AARCH64_TARGET_CPU("cortex-x1c") CANGJIE_AARCH64_TARGET_CPU("cortex-x2") CANGJIE_AARCH64_TARGET_CPU("cyclone") CANGJIE_AARCH64_TARGET_CPU("exynos-m3") CANGJIE_AARCH64_TARGET_CPU("exynos-m4") CANGJIE_AARCH64_TARGET_CPU("exynos-m5") CANGJIE_AARCH64_TARGET_CPU("falkor") CANGJIE_AARCH64_TARGET_CPU("generic") CANGJIE_AARCH64_TARGET_CPU("kryo") CANGJIE_AARCH64_TARGET_CPU("neoverse-512tvb") CANGJIE_AARCH64_TARGET_CPU("neoverse-e1") CANGJIE_AARCH64_TARGET_CPU("neoverse-n1") CANGJIE_AARCH64_TARGET_CPU("neoverse-n2") CANGJIE_AARCH64_TARGET_CPU("neoverse-v1") CANGJIE_AARCH64_TARGET_CPU("saphira") CANGJIE_AARCH64_TARGET_CPU("thunderx") CANGJIE_AARCH64_TARGET_CPU("thunderx2t99") CANGJIE_AARCH64_TARGET_CPU("thunderx3t110") CANGJIE_AARCH64_TARGET_CPU("thunderxt81") CANGJIE_AARCH64_TARGET_CPU("thunderxt83") CANGJIE_AARCH64_TARGET_CPU("thunderxt88") CANGJIE_AARCH64_TARGET_CPU("tsv110") #endif cangjie_compiler-1.0.7/src/Driver/TempFileManager.cpp000066400000000000000000000405561510705540100225470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the TempFileManager Class. */ #include "cangjie/Driver/TempFileManager.h" #include "cangjie/Basic/Print.h" #include "cangjie/Basic/StringConvertor.h" #include "cangjie/Option/Option.h" #include "cangjie/Utils/ConstantsUtils.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/Utils.h" #include #include #include #include #ifdef _WIN32 #include #include #include #endif namespace { #ifdef _WIN32 const std::string TMP_DIR_ENVIRONMENT_KEY = "TMP"; const std::string DEFAULT_TMP_DIR = "C:\\Windows\\Temp"; const char CODE[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; #else const std::string TMP_DIR_ENVIRONMENT_KEY = "TMPDIR"; const std::string DEFAULT_TMP_DIR = "/tmp"; const char CODE[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; #endif const std::string CANGJIE_TMP_DIR_PERFIX = "/cangjie-tmp-"; constexpr size_t MULTIPLE_OF_SECOND_TO_NANOSECOND = 1'000'000'000; constexpr uint8_t NOT_DELETED = 0; constexpr uint8_t DELETING = 1; constexpr uint8_t DELETED = 2; std::optional TempDirLegalityCheck(const std::string& path, bool fromEnvSetting) { std::optional absPath = Cangjie::FileUtil::GetAbsPath(path); if (!absPath.has_value()) { Cangjie::Errorf("%s (%s) does not exist.\n", fromEnvSetting ? "Temporary directory path set by environment variable" : "Temporary directory path", fromEnvSetting ? TMP_DIR_ENVIRONMENT_KEY.c_str() : DEFAULT_TMP_DIR.c_str()); return {}; } if (fromEnvSetting) { if (!Cangjie::FileUtil::IsDir(path)) { Cangjie::Errorf("Temporary directory path set by environment variable(%s) is not a directory.\n", TMP_DIR_ENVIRONMENT_KEY.c_str()); return {}; } } std::string temp = absPath.value(); if (!Cangjie::FileUtil::CanWrite(temp)) { Cangjie::Errorf("%s (%s) does not have write permission.\n", fromEnvSetting ? "Temporary directory path set by environment variable" : "Temporary directory path", fromEnvSetting ? TMP_DIR_ENVIRONMENT_KEY.c_str() : DEFAULT_TMP_DIR.c_str()); return {}; }; return temp; } std::string GetErrMessage(int error) { constexpr size_t buffSize = 512; char buf[buffSize] = {0}; #ifdef _WIN32 if (strerror_s(buf, buffSize, error) != 0) { return ""; } return std::string(buf); #elif defined(__ohos__) || defined(__APPLE__) if (strerror_r(error, buf, buffSize) != 0) { return ""; } return std::string(buf); #else return std::string(strerror_r(error, buf, buffSize)); #endif } std::string SetNowTimeEncodedString() { struct timespec wallNow = {0, 0}; (void)clock_gettime(CLOCK_REALTIME, &wallNow); size_t ns = static_cast(wallNow.tv_sec) * MULTIPLE_OF_SECOND_TO_NANOSECOND + static_cast(wallNow.tv_nsec); char res[32] = {0}; // 32 is much greater than the length of the result size_t index = 0; size_t numberOfcode = strlen(CODE); while (ns > numberOfcode) { size_t i = ns % numberOfcode; ns = ns / numberOfcode; res[index] = CODE[i]; index++; } res[index] = CODE[ns]; std::string s = std::string(res); std::reverse(s.begin(), s.end()); return s; } std::string CreateTempDirName(const std::string& tempDir) { std::stringstream ss; ss << CANGJIE_TMP_DIR_PERFIX; ss << SetNowTimeEncodedString(); ss << "-" << Cangjie::Utils::GenerateRandomHexString(); return Cangjie::FileUtil::JoinPath(tempDir, ss.str()); } std::optional MakeTempDir(const std::string& tempDir, bool fromEnvSetting) { int8_t loopSize = 10; // Loop 10 times to try creating a temporary directory. do { std::string path = CreateTempDirName(tempDir); loopSize--; #ifdef _WIN32 std::optional tempValue = Cangjie::StringConvertor::StringToWString(path); if (tempValue.has_value()) { if (_wmkdir(tempValue.value().c_str()) == 0) { return path; } } #else // The permission on the new directory is 775(drwxrwxr-x) if (mkdir(const_cast(path.c_str()), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0) { return path; } #endif } while (errno == EEXIST && loopSize > 0); std::string errMsg = GetErrMessage(errno); Cangjie::Errorf("Failed to create the directory in temporary directory path %s(%s)%s\n", fromEnvSetting ? "set by environment variable" : "", fromEnvSetting ? TMP_DIR_ENVIRONMENT_KEY.c_str() : DEFAULT_TMP_DIR.c_str(), errMsg.empty() ? errMsg.c_str() : (": " + errMsg).c_str()); return {}; } #ifdef _WIN32 void RemoveDirRecursively(const std::wstring& dirPath) { HANDLE hFind; WIN32_FIND_DATAW findData; std::wstring dirNew = dirPath + std::wstring(L"\\*.*"); hFind = FindFirstFileW(dirNew.c_str(), &findData); if (findData.dwFileAttributes == INVALID_FILE_ATTRIBUTES) { FindClose(hFind); return; } do { std::wstring fileName = std::wstring(findData.cFileName); if (fileName == std::wstring(L".") || fileName == std::wstring(L"..") || fileName == std::wstring()) { FindNextFileW(hFind, &findData); continue; } std::wstring newPath = dirPath + std::wstring(L"\\") + fileName; if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { RemoveDirRecursively(newPath); } else { DeleteFileW(newPath.c_str()); } } while (FindNextFileW(hFind, &findData)); FindClose(hFind); RemoveDirectoryW(dirPath.c_str()); } #else void RemoveDirRecursively(const std::string& dirPath) { DIR* dir = opendir(dirPath.c_str()); if (!dir) { return; } for (auto entry = readdir(dir); entry != nullptr; entry = readdir(dir)) { std::string fileName = std::string(entry->d_name); if (fileName == "." || fileName == "..") { continue; } std::string newPath = Cangjie::FileUtil::JoinPath(dirPath, fileName); if (entry->d_type == DT_REG) { (void)unlink(newPath.c_str()); } else if (entry->d_type == DT_DIR) { RemoveDirRecursively(newPath); } } (void)closedir(dir); (void)rmdir(dirPath.c_str()); } #endif } // namespace using namespace Cangjie; bool TempFileManager::Init(const GlobalOptions& options, bool isFrontend) { isCjcFrontend = isFrontend; opts = options; isDeleted.store(NOT_DELETED); if (!InitOutPutDir()) { return false; } fileSuffixMap = { {TempFileKind::O_CJO, []() { return SERIALIZED_FILE_EXTENSION; }}, {TempFileKind::O_FULL_BCHIR, []() { return FULL_BCHIR_SERIALIZED_FILE_EXTENSION; }}, {TempFileKind::O_BCHIR, []() { return BCHIR_SERIALIZED_FILE_EXTENSION; }}, {TempFileKind::T_BC, []() { return ".bc"; }}, {TempFileKind::O_BC, []() { return ".bc"; }}, #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND {TempFileKind::O_STATICLIB, []() { return ".a"; }}, {TempFileKind::O_EXE, [this]() { return opts.target.os == Triple::OSType::WINDOWS ? ".exe" : ""; }}, {TempFileKind::O_DYLIB, [this]() { return GetDylibSuffix(); }}, {TempFileKind::O_MACRO, [this]() { return GetDylibSuffix(); }}, {TempFileKind::T_OPT_BC, []() { return ".opt.bc"; }}, {TempFileKind::O_OPT_BC, []() { return ".bc"; }}, {TempFileKind::T_ASM, []() { return ".s"; }}, #endif {TempFileKind::O_CHIR, []() { return CHIR_SERIALIZATION_FILE_EXTENSION; }}, {TempFileKind::T_OBJ, []() { return ".o"; }}, {TempFileKind::T_EXE_MAC, []() { return "_temp"; }}, {TempFileKind::T_DYLIB_MAC, []() { return "_temp.dylib"; }}, }; return InitTempDir(); } bool TempFileManager::InitOutPutDir() { if (FileUtil::IsDir(opts.output)) { outputDir = opts.output; outputName = ""; } else { outputDir = FileUtil::GetDirPath(opts.output); outputName = FileUtil::GetFileName(opts.output); } std::optional absPath = FileUtil::GetAbsPath(outputDir); if (absPath.has_value()) { outputDir = absPath.value(); } else { Errorln("Output path does not exist."); return false; } if (!opts.ValidateDirectoryPath(outputDir, FileUtil::FileMode::FM_WRITE).has_value()) { return false; } return true; } bool TempFileManager::InitTempDir() { if (opts.saveTemps) { tempDir = opts.tempFolderPath; std::optional absPath = FileUtil::GetAbsPath(tempDir); if (!absPath.has_value()) { return false; } tempDir = absPath.value(); return true; } std::unordered_map& envs = opts.environment.allVariables; auto found = envs.find(TMP_DIR_ENVIRONMENT_KEY); bool fromEnvSetting = false; if (found != envs.end()) { tempDir = found->second; fromEnvSetting = true; } else { tempDir = DEFAULT_TMP_DIR; } std::optional ret = TempDirLegalityCheck(tempDir, fromEnvSetting); if (!ret.has_value()) { return false; } std::optional dir = MakeTempDir(ret.value(), fromEnvSetting); if (dir.has_value()) { tempDir = dir.value(); // Record temporary directory to be deleted later. deletedFiles.push_back(tempDir); return true; } return false; } std::string TempFileManager::GetTempFolder() { return tempDir; } TempFileInfo TempFileManager::CreateNewFileInfo(const TempFileInfo& info, TempFileKind kind) { TempFileInfo newInfo; switch (kind) { case TempFileKind::O_CJO: case TempFileKind::O_FULL_BCHIR: case TempFileKind::O_BCHIR: newInfo = CreateIntermediateFileInfo(info, kind); break; case TempFileKind::O_CHIR: newInfo = CreateTempFileInfo(info, kind); break; case TempFileKind::T_BC: newInfo = CreateTempBcFileInfo(info, kind); break; case TempFileKind::O_BC: case TempFileKind::O_EXE: case TempFileKind::O_MACRO: case TempFileKind::O_DYLIB: case TempFileKind::O_STATICLIB: newInfo = CreateOutputFileInfo(info, kind); break; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND case TempFileKind::T_OPT_BC: case TempFileKind::T_ASM: case TempFileKind::T_OBJ: case TempFileKind::T_DYLIB_MAC: case TempFileKind::T_EXE_MAC: newInfo = CreateTempFileInfo(info, kind); break; case TempFileKind::O_OPT_BC: newInfo = CreateLinuxLLVMOptOutputBcFileInfo(info, kind); break; #endif default: CJC_ASSERT(false); return {}; } newInfo.rawPath = info.rawPath; newInfo.isFrontendOutput = info.isFrontendOutput; newInfo.isForeignInput = info.isForeignInput; return newInfo; } TempFileInfo TempFileManager::CreateIntermediateFileInfo(const TempFileInfo& info, TempFileKind kind) { std::string dir = opts.outputDir.value_or(outputDir); std::string outputFilePath = FileUtil::JoinPath(dir, info.fileName) + fileSuffixMap.at(kind)(); return TempFileInfo{info.fileName, outputFilePath}; } TempFileInfo TempFileManager::CreateTempBcFileInfo(const TempFileInfo& info, TempFileKind kind) { if (isCjcFrontend) { // If compile with cjc-frontend, the temporary bc file is used as the output file. return CreateOutputFileInfo(info, kind); } std::string tempFilePath = FileUtil::JoinPath(tempDir, info.fileName); tempFilePath = tempFilePath + fileSuffixMap.at(kind)(); #ifndef COMPILER_EXPLORER_RACE_FIX if (!opts.saveTemps) { // Record temporary file to be deleted later. deletedFiles.push_back(tempFilePath); } #endif return TempFileInfo{info.fileName, tempFilePath}; } TempFileInfo TempFileManager::CreateOutputFileInfo(const TempFileInfo& info, TempFileKind kind) { if (outputName.empty()) { std::string outputFileName; if (kind == TempFileKind::O_EXE) { outputFileName = "main"; } else if (kind == TempFileKind::O_DYLIB || kind == TempFileKind::O_STATICLIB) { outputFileName = "lib" + info.fileName; } else if (kind == TempFileKind::O_MACRO) { outputFileName = "lib-macro_" + info.fileName; } else { outputFileName = info.fileName; } std::string outputFilePath = FileUtil::JoinPath(outputDir, outputFileName); outputFilePath = outputFilePath + fileSuffixMap.at(kind)(); return TempFileInfo{outputFileName, outputFilePath}; } else { std::string outputFilePath = FileUtil::JoinPath(outputDir, outputName); return TempFileInfo{outputName, outputFilePath}; } } TempFileInfo TempFileManager::CreateTempFileInfo(const TempFileInfo& info, TempFileKind kind) { std::string tempFilePath = FileUtil::JoinPath(tempDir, info.fileName); tempFilePath = tempFilePath + fileSuffixMap.at(kind)(); if (!opts.saveTemps) { // Record temporary file to be deleted later. deletedFiles.push_back(tempFilePath); } return TempFileInfo{info.fileName, tempFilePath}; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND TempFileInfo TempFileManager::CreateLinuxLLVMOptOutputBcFileInfo(const TempFileInfo& info, TempFileKind kind) { // The output is in the same directory as the .cjo file std::string dir = opts.outputDir.value_or(outputDir); if (outputName.empty()) { std::string outputFileName = "lib" + info.fileName; std::string outputFilePath = FileUtil::JoinPath(dir, outputFileName); outputFilePath = outputFilePath + fileSuffixMap.at(kind)(); return TempFileInfo{outputFileName, outputFilePath}; } else { std::string outputFilePath = FileUtil::JoinPath(dir, outputName); return TempFileInfo{outputName, outputFilePath}; } } #endif void TempFileManager::DeleteTempFiles(bool isSignalSafe) { if (isDeleted.exchange(DELETING) != NOT_DELETED && IsDeleted()) { return; } #ifdef _WIN32 int i = static_cast(deletedFiles.size()) - 1; while (i >= 0) { std::string filePath = deletedFiles[i]; std::optional tempValue = Cangjie::StringConvertor::StringToWString(filePath); if (!tempValue.has_value()) { i--; continue; } std::wstring wFilePath = tempValue.value(); DWORD dwAttr = GetFileAttributesW(wFilePath.c_str()); if (dwAttr == INVALID_FILE_ATTRIBUTES) { i--; continue; } if ((dwAttr & FILE_ATTRIBUTE_DIRECTORY) != 0) { RemoveDirRecursively(wFilePath); } else { DeleteFileW(wFilePath.c_str()); } i--; } #else for (auto it = deletedFiles.crbegin(); it != deletedFiles.crend(); ++it) { const char* filePath = it->c_str(); struct stat buf; if (stat(filePath, &buf) != 0) { continue; } if (S_ISREG(buf.st_mode)) { (void)unlink(filePath); } else if (S_ISDIR(buf.st_mode)) { (void)rmdir(filePath); if (!isSignalSafe) { RemoveDirRecursively(filePath); } } } #endif isDeleted.store(DELETED); } bool TempFileManager::IsDeleted() const { if (isDeleted.load() == DELETED) { return true; } if (isDeleted.load() == NOT_DELETED) { return false; } if (isDeleted.load() == DELETING) { size_t n = MULTIPLE_OF_SECOND_TO_NANOSECOND; while (n > 0) { if (isDeleted.load() == DELETED) { return true; } n--; } } return true; } std::string TempFileManager::GetDylibSuffix() const { switch (opts.target.os) { case Triple::OSType::WINDOWS: return ".dll"; case Triple::OSType::DARWIN: case Triple::OSType::IOS: return ".dylib"; case Triple::OSType::LINUX: case Triple::OSType::UNKNOWN: default: return ".so"; } } cangjie_compiler-1.0.7/src/Driver/Tool.cpp000066400000000000000000000163301510705540100204550ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements Tool class and its functions. */ #include "cangjie/Driver/Tool.h" #ifdef _WIN32 #include #include #else #include #include #endif #include #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/Semaphore.h" #include "cangjie/Driver/TempFileManager.h" #include "cangjie/Driver/Utils.h" using namespace Cangjie; #ifdef __APPLE__ const static std::string LD_LIBRARY_PATH = "DYLD_LIBRARY_PATH"; #elif !defined(_WIN32) const static std::string LD_LIBRARY_PATH = "LD_LIBRARY_PATH"; #endif void Tool::AppendMultiArgs(const std::string& argStr) { auto splitArgs = Utils::SplitString(argStr, " "); for (const auto& a : splitArgs) { if (!a.empty()) { args.emplace_back(a); } } } std::string Tool::GenerateCommand() const { std::string tempCommand; std::string argStr; auto& argList = GetArgs(); std::for_each(argList.begin(), argList.end(), [&argStr](const std::string& s) { #ifdef _WIN32 argStr = argStr + FileUtil::GetQuoted(s) + " "; #else argStr = argStr + GetSingleQuoted(s) + " "; #endif }); #ifndef _WIN32 if (!GetLdLibraryPath().empty()) { tempCommand = "LD_LIBRARY_PATH=" + GetSingleQuoted(GetLdLibraryPath()) + ":$" + LD_LIBRARY_PATH + " "; } #endif #ifdef _WIN32 tempCommand += FileUtil::GetQuoted(name); #else tempCommand += GetSingleQuoted(name); #endif tempCommand += " " + argStr; #ifdef _WIN32 // Command run as `cmd /C`. The leading double quote will be removed by `cmd` causing the semantic of command // changes. Extra pair of double quotes is added to make the command works when there is leading double quote. return "\"" + tempCommand + "\""; #else return tempCommand; #endif } bool Tool::InternalImplementedCommandExec() const { bool res = false; if (type == ToolType::INTERNAL_IMPLEMENTED && name == "CacheCopy") { auto& arguments = GetFullArgs(); // arguments[0] - name of command // arguments[1] - absolute name of srcFile std::ifstream srcStream(arguments[1], std::ios::binary); // arguments[2] - absolute name of destFile std::ofstream destStream(arguments[2], std::ios::binary); destStream << srcStream.rdbuf(); destStream.close(); #ifdef _WIN32 FileUtil::HideFile(FileUtil::GetDirPath(arguments[2])); #endif res = true; } return res; } std::unique_ptr Tool::Execute(bool verbose) const { if (verbose) { Println(FileUtil::Normalize(GetCommandString())); } // If the file is deleted, the subsequent commands are not executed. if (TempFileManager::Instance().IsDeleted()) { return nullptr; } return Run(); } #ifdef _WIN32 std::unique_ptr Tool::Run() const { if (type == ToolType::INTERNAL_IMPLEMENTED) { Utils::Semaphore::Get().Acquire(); return std::make_unique(std::async(&Tool::InternalImplementedCommandExec, this)); } STARTUPINFOA si; PROCESS_INFORMATION pi; auto& arguments = GetFullArgs(); std::ostringstream oss; for (size_t i = 0; i < arguments.size(); ++i) { if (i != 0) { oss << " "; } if (arguments[i].empty() || arguments[i].find_first_of("\t \"&\'()*<>\\`^|\n") != std::string::npos) { oss << std::quoted(arguments[i]); } else { oss << arguments[i]; } } std::string commandLine = oss.str(); ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); Utils::Semaphore::Get().Acquire(); if (!CreateProcessA( name.c_str(), const_cast(commandLine.c_str()), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { Utils::Semaphore::Get().Release(); return nullptr; } return std::make_unique(pi); } #else std::unique_ptr Tool::Run() const { if (type == ToolType::INTERNAL_IMPLEMENTED) { Utils::Semaphore::Get().Acquire(); return std::make_unique(std::async(&Tool::InternalImplementedCommandExec, this)); } auto& arguments = GetFullArgs(); // Convert arguments to char * array for argv argument. std::vector rawArgumentArray = {}; rawArgumentArray.reserve(arguments.size() + 1); size_t index = 0; while (index < arguments.size()) { rawArgumentArray[index] = const_cast(arguments[index].c_str()); index++; } rawArgumentArray[index] = nullptr; // Note: `childEnvironment` must be alive until the call of `posix_spawn` finishes. The c pointer // of environment we get will be invalid if `childEnvironment` is destroyed too early. std::list childEnvironment = BuildEnvironmentVector(); // Make an array for envp argument. std::vector rawEnvironmentArray; for (const std::string& s : std::as_const(childEnvironment)) { rawEnvironmentArray.emplace_back(const_cast(s.c_str())); } rawEnvironmentArray.push_back(nullptr); pid_t pid; int status = 0; Utils::Semaphore::Get().Acquire(); // If name has no directory separator, then it should be a program name that needs to be // searched in PATH. `posix_spawnp` does program searches for us. if (name.find_first_of(DIR_SEPARATOR) == std::string::npos) { status = posix_spawnp(&pid, name.c_str(), nullptr, nullptr, rawArgumentArray.data(), rawEnvironmentArray.data()); } else { status = posix_spawn(&pid, name.c_str(), nullptr, nullptr, rawArgumentArray.data(), rawEnvironmentArray.data()); } // Failed to start the child process if (status != 0) { Utils::Semaphore::Get().Release(); return nullptr; } return std::make_unique(pid); } std::list Tool::BuildEnvironmentVector() const { std::list environment; // Append our ld library path to the front of LD_LIBRARY_PATH if (ldLibraryPath.empty()) { if (environmentVars.find(LD_LIBRARY_PATH) != environmentVars.end()) { environment.emplace_back(LD_LIBRARY_PATH + "=" + environmentVars.at(LD_LIBRARY_PATH)); } } else { std::string newLdLibraryPath = ldLibraryPath; // If LD_LIBRARY_PATH is empty, we don't append `:` or ld.so will treat the trailing empty path as ".". // See documentation of ld.so for details. if (environmentVars.find(LD_LIBRARY_PATH) != environmentVars.end() && !environmentVars.at(LD_LIBRARY_PATH).empty()) { newLdLibraryPath += ":" + environmentVars.at(LD_LIBRARY_PATH); } environment.emplace_back(LD_LIBRARY_PATH + "=" + newLdLibraryPath); } for (const auto& environmentVar : environmentVars) { if (environmentVar.first == LD_LIBRARY_PATH) { continue; } environment.emplace_back(environmentVar.first + "=" + environmentVar.second); } return environment; } #endif cangjie_compiler-1.0.7/src/Driver/ToolFuture.cpp000066400000000000000000000027471510705540100216570ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the ToolFuture class and its subclasses. */ #include "cangjie/Driver/ToolFuture.h" using namespace Cangjie; ToolFuture::State ThreadFuture::GetState() { if (!result.has_value()) { result = future.get(); } return result.value() ? State::SUCCESS : State::FAILED; } #ifdef _WIN32 ToolFuture::State WindowsProcessFuture::GetState() { DWORD state = WaitForSingleObject(pi.hProcess, 0); if (state == WAIT_FAILED || state == WAIT_ABANDONED) { return State::FAILED; } else if (state == WAIT_TIMEOUT) { return State::RUNNING; } DWORD exit_code; if (FALSE == GetExitCodeProcess(pi.hProcess, &exit_code)) { return State::FAILED; } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return exit_code == 0 ? State::SUCCESS : State::FAILED; } #else ToolFuture::State LinuxProcessFuture::GetState() { int status = 0; int result = waitpid(pid, &status, WNOHANG); if (result < 0 || status != 0) { // If an error occurs because the file is deleted, the error information is not printed. return State::FAILED; } if (result > 0) { return State::SUCCESS; } return State::RUNNING; } #endif cangjie_compiler-1.0.7/src/Driver/ToolOptions.cpp000066400000000000000000000311561510705540100220340ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements Tool Options functions. */ #include "cangjie/Driver/ToolOptions.h" #include namespace Cangjie::ToolOptions { void SetOptionStringArgs(SetFuncType setOptionHandler, const std::string& stringArg) { auto args = Utils::SplitString(stringArg, " "); for (const auto& a : args) { if (!a.empty()) { setOptionHandler(a); } } } void SetOptionIf(SetFuncType setOptionHandler, bool condition, const std::string& stringArg) { if (condition) { setOptionHandler(stringArg); } } void SetMtripleOption(SetFuncType setOptionHandler, const DriverOptions& driverOptions) { bool canSetMtriple = driverOptions.IsCrossCompiling() || driverOptions.target.IsMinGW(); SetOptionIf(setOptionHandler, canSetMtriple, "--mtriple=" + driverOptions.target.GetEffectiveTripleString()); } namespace OPT { void SetOptimizationLevelOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions) { auto optimizationLevel = driverOptions.disableBackendOpt ? GlobalOptions::OptimizationLevel::O0 : driverOptions.optimizationLevel; setOptionHandler("default<" + OPTIMIZATION_LEVEL_TO_BACKEND_OPTION.find(optimizationLevel)->second.substr(1) + ">"); } void SetNewPassManagerOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions) { // the new pass manager only supports the following pass settings: // - optimization (in function SetOptimizationLevelOptions) // - coverage // - obfuscation (ObfData, ObfLayout, ObfControlFlow) // - optPassOptions SetOptionIf(setOptionHandler, driverOptions.enableCoverage, "-insert-gcov-profiling"); SetOptionIf(setOptionHandler, driverOptions.EnableAsan(), "-asan-module"); SetOptionIf(setOptionHandler, driverOptions.EnableTsan(), "-tsan"); SetOptionIf(setOptionHandler, driverOptions.EnableHwAsan(), "-hwasan"); SetOptionIf(setOptionHandler, (!driverOptions.optPassOptions.empty()), "-" + driverOptions.optPassOptions); } void SetCodeLayoutObfuscationOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions) { setOptionHandler("--obf-layout=true"); if (driverOptions.layoutObfInputSymMappingFiles.has_value()) { setOptionHandler( std::string("-obf-input-symbol-mapping-files=") + driverOptions.layoutObfInputSymMappingFiles.value()); } if (driverOptions.layoutObfOutputSymMappingFile.has_value()) { setOptionHandler( std::string("-obf-output-symbol-mapping-file=") + driverOptions.layoutObfOutputSymMappingFile.value()); } if (driverOptions.layoutObfUserMappingFile.has_value()) { setOptionHandler( std::string("-obf-apply-symbol-mapping=") + driverOptions.layoutObfUserMappingFile.value()); } if (driverOptions.layoutObfSymPrefix.has_value()) { setOptionHandler(std::string("-obf-sym-prefix=") + driverOptions.layoutObfSymPrefix.value()); } setOptionHandler( std::string("-obf-export-symbol=") + (driverOptions.enableObfExportSyms.value_or(true) ? "true" : "false")); setOptionHandler( std::string("-obf-line-number=") + (driverOptions.enableObfLineNumber.value_or(false) ? "true" : "false")); setOptionHandler( std::string("-obf-source-path=") + (driverOptions.enableObfSourcePath.value_or(true) ? "true" : "false")); } void SetCodeObfuscationOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions) { bool obfEnabled = false; bool obfString = driverOptions.enableStringObfuscation.value_or(false); bool obfConst = driverOptions.enableConstObfuscation.value_or(false); bool obfLayout = driverOptions.enableLayoutObfuscation.value_or(false); bool obfCFFlatten = driverOptions.enableCFflattenObfuscation.value_or(false); bool obfCFBogus = driverOptions.enableCFBogusObfuscation.value_or(false); if (obfString || obfConst) { setOptionHandler(std::string("-obf-string=") + (obfString ? "true" : "false")); setOptionHandler(std::string("-obf-const=") + (obfConst ? "true" : "false")); obfEnabled = true; } if (obfLayout) { SetCodeLayoutObfuscationOptions(setOptionHandler, driverOptions); obfEnabled = true; } if (obfCFFlatten || obfCFBogus) { setOptionHandler( std::string("-control-flow-flatten=") + (obfCFFlatten ? "true" : "false")); setOptionHandler( std::string("-control-flow-bogus=") + (obfCFBogus ? "true" : "false")); obfEnabled = true; } if (obfEnabled) { setOptionHandler(std::string("-obf-level=") + std::to_string(driverOptions.obfuscationLevel)); if (driverOptions.obfuscationSeed.has_value()) { setOptionHandler(std::string("-obf-seed=") + std::to_string(driverOptions.obfuscationSeed.value())); } if (driverOptions.obfuscationConfigFile.has_value()) { setOptionHandler(std::string("-obf-config-file=") + driverOptions.obfuscationConfigFile.value()); } } } void SetLTOOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions) { if (driverOptions.IsLTOEnabled()) { setOptionHandler("--cangjie-lto"); setOptionHandler("--module-summary"); setOptionHandler("--module-hash"); } } void SetVerifyOptions(SetFuncType setOptionHandler, [[maybe_unused]] const DriverOptions& driverOptions) { setOptionHandler("--only-verify-out"); } void SetOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions) { setOptionHandler("--cangjie-pipeline"); SetOptionIf(setOptionHandler, driverOptions.EnableAsan(), "-cj-asan=true"); SetOptionIf(setOptionHandler, driverOptions.EnableTsan(), "-cj-tsan=true"); SetOptionIf(setOptionHandler, driverOptions.EnableTsan(), "-tsan-instrument-atomics=false"); SetOptionIf(setOptionHandler, driverOptions.EnableTsan(), "--cj-tsan-support-for-mutexopt"); SetOptionIf(setOptionHandler, driverOptions.EnableHwAsan(), "-cj-hwasan=true"); SetOptionIf(setOptionHandler, driverOptions.enableFuncSections, "-function-sections"); SetOptionIf(setOptionHandler, driverOptions.enableDataSections, "-data-sections"); SetOptionIf(setOptionHandler, driverOptions.target.env == Triple::Environment::OHOS && driverOptions.target.arch == Triple::ArchType::AARCH64, "--cj-ptrauth-backward-cfi=true"); } void SetTripleOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions) { SetMtripleOption(setOptionHandler, driverOptions); } void SetTransparentOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions) { // optArg may contain multiple opt arguments separated by spaces. SetOptionStringArgs(setOptionHandler, driverOptions.optArg); } void SetPgoOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions) { if (driverOptions.enablePgoInstrGen) { setOptionHandler("-pgo-kind=pgo-instr-gen-pipeline"); setOptionHandler("-runtime-counter-relocation"); } if (driverOptions.enablePgoInstrUse) { setOptionHandler("-pgo-kind=pgo-instr-use-pipeline"); setOptionHandler("-profile-file=" + driverOptions.pgoProfileFile); } } } // namespace OPT namespace LLC { void SetOptimizationLevelOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions) { auto optimizationLevel = driverOptions.disableBackendOpt ? GlobalOptions::OptimizationLevel::O0 : driverOptions.optimizationLevel; // when the optimize option '-Os' or 'Oz' is enabled in opt stage, then it will trigger '-O2' in llc stage. if (optimizationLevel == GlobalOptions::OptimizationLevel::Os || optimizationLevel == GlobalOptions::OptimizationLevel::Oz) { optimizationLevel = GlobalOptions::OptimizationLevel::O2; } setOptionHandler(OPTIMIZATION_LEVEL_TO_BACKEND_OPTION.find(optimizationLevel)->second); } void SetOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions) { setOptionHandler("--cangjie-pipeline"); SetOptionIf(setOptionHandler, (!driverOptions.enableCompileDebug && !driverOptions.enableCoverage && driverOptions.displayLineInfo), "-disable-debug-info-print"); setOptionHandler("--relocation-model=pic"); setOptionHandler("--frame-pointer=non-leaf"); setOptionHandler(driverOptions.GetStackTraceFormat()); SetOptionIf(setOptionHandler, driverOptions.fastMathMode, "-fp-contract=fast"); SetOptionIf(setOptionHandler, driverOptions.enableFuncSections, "-function-sections"); SetOptionIf(setOptionHandler, driverOptions.enableDataSections, "-data-sections"); // thread sanitizer pass for atomics instrument SetOptionIf(setOptionHandler, driverOptions.EnableTsan(), "-tsan"); SetOptionIf(setOptionHandler, driverOptions.EnableHwAsan(), "-mattr=+tagged-globals"); #ifdef CANGJIE_DISABLE_STACK_GROW_FEATURE setOptionHandler("--cj-stack-grow=false"); #else SetOptionIf(setOptionHandler, driverOptions.target.env == Triple::Environment::OHOS || driverOptions.target.env == Triple::Environment::ANDROID, "--cj-stack-grow=false"); #endif if (driverOptions.targetCPU.has_value() && !driverOptions.targetCPU.value().empty()) { setOptionHandler("-mcpu=" + driverOptions.targetCPU.value()); // AVX instruction generation may cause runtime fail on stack tracing. Manually disable here. SetOptionIf(setOptionHandler, driverOptions.target.arch == Triple::ArchType::X86_64, "-mattr=-avx"); } } void SetTripleOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions) { SetMtripleOption(setOptionHandler, driverOptions); } void SetTransparentOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions) { // llcArg may contain multiple opt arguments separated by spaces. SetOptionStringArgs(setOptionHandler, driverOptions.llcArg); } } // namespace LLC namespace LLD { void SetLTOOptimizationLevelOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions) { auto optimizationLevel = driverOptions.disableBackendOpt ? GlobalOptions::OptimizationLevel::O0 : driverOptions.optimizationLevel; switch (optimizationLevel) { case GlobalOptions::OptimizationLevel::O3: setOptionHandler("--lto-O3"); break; // when the optimize option '-Os' or 'Oz' is enabled in opt stage, then it will trigger '-O2' in llc stage. case GlobalOptions::OptimizationLevel::Os: case GlobalOptions::OptimizationLevel::Oz: case GlobalOptions::OptimizationLevel::O2: setOptionHandler("--lto-O2"); break; case GlobalOptions::OptimizationLevel::O1: setOptionHandler("--lto-O1"); break; case GlobalOptions::OptimizationLevel::O0: setOptionHandler("--lto-O0"); break; default: setOptionHandler("--lto-O2"); break; } } void SetLTOOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions) { if (driverOptions.IsFullLTOEnabled()) { setOptionHandler("--mllvm"); setOptionHandler("--cangjie-full-lto"); } else { // The backend lld defaults to supporting incremental compilation in thin LTO mode. setOptionHandler("--thinlto-cache-dir=" + driverOptions.compilationCachedPath); } // Differentiate the optimization in the LLD and OPT phases. setOptionHandler("--mllvm"); setOptionHandler("--cj-lto-opt"); setOptionHandler("--allow-multiple-definition"); setOptionHandler("--plugin-opt=no-opaque-pointers"); if (driverOptions.IsCompileAsExeEnabled()) { setOptionHandler("--compile-as-exe"); } } void SetPgoOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions) { if (driverOptions.enablePgoInstrGen) { setOptionHandler("-cj-pgo-kind=pgo-instr-gen-pipeline"); } if (driverOptions.enablePgoInstrUse) { setOptionHandler("-cj-pgo-kind=pgo-instr-use-pipeline"); setOptionHandler("-cj-profile-file=" + driverOptions.pgoProfileFile); } } } // namespace LLD void SetOptions(SetFuncType setOptionHandler, const DriverOptions& driverOptions, const ToolOptionType typeFunc) { typeFunc(setOptionHandler, driverOptions); } void SetOptions( SetFuncType setOptionHandler, const DriverOptions& driverOptions, const std::vector& typeFuncs) { for (const auto& typefunc : typeFuncs) { typefunc(setOptionHandler, driverOptions); } } } // namespace Cangjie::ToolOptions cangjie_compiler-1.0.7/src/Driver/Toolchains/000077500000000000000000000000001510705540100211345ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Driver/Toolchains/BackendOptions.inc000066400000000000000000000077441510705540100245460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file lists library options to be passed to linker, which is included by headers. */ #ifdef CJNATIVE_STD_OPTIONS // Must be linux. CJNATIVE_STD_OPTIONS("-lcangjie-runtime") #endif #ifdef CJNATIVE_DARWIN_BASIC_OPTIONS CJNATIVE_DARWIN_BASIC_OPTIONS("-lboundscheck") CJNATIVE_DARWIN_BASIC_OPTIONS("-lSystem") #endif #ifdef CJNATIVE_GNU_LINUX_BASIC_OPTIONS CJNATIVE_GNU_LINUX_BASIC_OPTIONS("-lboundscheck") CJNATIVE_GNU_LINUX_BASIC_OPTIONS("-lm") CJNATIVE_GNU_LINUX_BASIC_OPTIONS("-lc") #endif #ifdef CJNATIVE_STATIC_LINK_BASIC_OPTIONS CJNATIVE_STATIC_LINK_BASIC_OPTIONS("-lstdc++") CJNATIVE_STATIC_LINK_BASIC_OPTIONS("-lpthread") CJNATIVE_STATIC_LINK_BASIC_OPTIONS("-ldl") CJNATIVE_STATIC_LINK_BASIC_OPTIONS("-lm") CJNATIVE_STATIC_LINK_BASIC_OPTIONS("-lc") #endif #ifdef CJNATIVE_WINDOWS_BASIC_OPTIONS #ifndef _MSC_VER CJNATIVE_WINDOWS_BASIC_OPTIONS("-lboundscheck") CJNATIVE_WINDOWS_BASIC_OPTIONS("-lm") // libssp is used when stack protector related options is on, such as -fstack-protector-all. // When linking statically, libssp_nonshared.a should be linked against first, then libssp.a. CJNATIVE_WINDOWS_BASIC_OPTIONS("-lmsvcrt") CJNATIVE_WINDOWS_BASIC_OPTIONS("-lmingw32") CJNATIVE_WINDOWS_BASIC_OPTIONS("-lmoldname") CJNATIVE_WINDOWS_BASIC_OPTIONS("-lmingwex") // Don't delete the repeated libs below! The order below is same as the linker commands generated by MinGW gcc. // It's intended to repeat linking them multiple times to resolve all symbols. CJNATIVE_WINDOWS_BASIC_OPTIONS("-lmingw32") CJNATIVE_WINDOWS_BASIC_OPTIONS("-lmsvcrt") CJNATIVE_WINDOWS_BASIC_OPTIONS("-lpthread") CJNATIVE_WINDOWS_BASIC_OPTIONS("-ladvapi32") CJNATIVE_WINDOWS_BASIC_OPTIONS("-lshell32") CJNATIVE_WINDOWS_BASIC_OPTIONS("-luser32") CJNATIVE_WINDOWS_BASIC_OPTIONS("-lkernel32") CJNATIVE_WINDOWS_BASIC_OPTIONS("-lmingw32") CJNATIVE_WINDOWS_BASIC_OPTIONS("-lmoldname") CJNATIVE_WINDOWS_BASIC_OPTIONS("-lmingwex") CJNATIVE_WINDOWS_BASIC_OPTIONS("-lmsvcrt") #else CJNATIVE_WINDOWS_BASIC_OPTIONS("-lmsvcrt") #endif #endif // If C code is introduced in the standard library, // please add the name of the corresponding CFFI library for the static library here. #ifdef CJNATIVE_LTO_CSTD_FFI_OPTIONS CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-std-core.a", "libcangjie-std-coreFFI.a") CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-std-ast.a", "libcangjie-std-astFFI.a") CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-std-convert.a", "libcangjie-std-convertFFI.a") CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-std-fs.a", "libcangjie-std-fsFFI.a") CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-std-console.a", "libcangjie-std-envFFI.a") CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-std-math.a", "libcangjie-std-mathFFI.a") CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-std-time.a", "libcangjie-std-timeFFI.a") CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-std-posix.a", "libcangjie-std-posixFFI.a") CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-std-process.a", "libcangjie-std-processFFI.a") CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-std-env.a", "libcangjie-std-envFFI.a") CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-std-random.a", "libcangjie-std-randomFFI.a") CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-std-net.a", "libcangjie-std-netFFI.a") CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-std-objectpool.a", "libcangjie-std-objectpoolFFI.a") CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-std-unittest.a", "libcangjie-std-unittestFFI.a") CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-std-regex.a", "libcangjie-std-regexFFI.a") CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-std-collection.concurrent.a", "libcangjie-std-collection.concurrentFFI.a") CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-dynamicLoader-openssl.a", "libcangjie-dynamicLoader-opensslFFI.a") CJNATIVE_LTO_CSTD_FFI_OPTIONS("libcangjie-std-runtime.a", "libcangjie-std-runtimeFFI.a") #endif cangjie_compiler-1.0.7/src/Driver/Toolchains/CJNATIVE/000077500000000000000000000000001510705540100223375ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Driver/Toolchains/CJNATIVE/Android_CJNATIVE.cpp000066400000000000000000000143321510705540100257110ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Android_CJNATIVE ToolChain base class. */ #include "Toolchains/CJNATIVE/Android_CJNATIVE.h" #include #include #include "cangjie/Driver/Toolchains/GCCPathScanner.h" #include "cangjie/Utils/FileUtil.h" using namespace Cangjie; using namespace Cangjie::Triple; void Android_CJNATIVE::InitializeLibraryPaths() { auto deduceToolchainLibPath = [this](const std::string& toolchainPath) { auto toolChainRoot = FileUtil::GetDirPath(toolchainPath); auto clangrtLibRoot = FileUtil::JoinPath(toolChainRoot, "lib64/clang"); if (!FileUtil::FileExist(clangrtLibRoot)) { clangrtLibRoot = FileUtil::JoinPath(toolChainRoot, "lib/clang"); } std::vector subDirs = FileUtil::GetDirectories(clangrtLibRoot); GCCVersion selectedClangVersion{0, 0, 0}; std::string selectedClangPath{}; for (const auto& dir : subDirs) { std::optional gccVersion = GCCPathScanner::StrToGCCVersion(dir.name); if (gccVersion && selectedClangVersion < *gccVersion) { selectedClangPath = dir.path; selectedClangVersion = *gccVersion; } } if (!selectedClangPath.empty()) { AddLibraryPath(FileUtil::JoinPath(selectedClangPath, "lib/linux/aarch64")); AddLibraryPath(FileUtil::JoinPath(selectedClangPath, "lib/linux")); } }; // 1. Deduce libs path from toolchain. // If the toolchain path is not specified by -B/--toolchain, deduce libs path from the environment path. auto clangPath = FileUtil::FindProgramByName(driverOptions.target.GetEffectiveTripleString() + "-clang", driverOptions.toolChainPaths.empty() ? driverOptions.environment.paths : driverOptions.toolChainPaths); if (!clangPath.empty()) { auto clangDir = FileUtil::GetDirPath(clangPath); deduceToolchainLibPath(clangDir); sysroot = FileUtil::JoinPath(FileUtil::GetDirPath(clangDir), "sysroot"); } if (driverOptions.customizedSysroot) { sysroot = driverOptions.sysroot; } // 2. Deduce libs path from sysroot. if (!sysroot.empty()) { std::string tripleDirectory = "usr/lib/" + driverOptions.target.ArchToString() + "-linux-android/"; AddLibraryPath(FileUtil::JoinPath(sysroot, tripleDirectory + driverOptions.target.apiLevel)); AddLibraryPath(FileUtil::JoinPath(sysroot, tripleDirectory)); AddLibraryPath(sysroot); } AddCRuntimeLibraryPaths(); } void Android_CJNATIVE::AddCRuntimeLibraryPaths() { std::string tripleDirectory = "usr/lib/" + driverOptions.target.ArchToString() + "-linux-android/"; AddCRuntimeLibraryPath(FileUtil::JoinPath(sysroot, tripleDirectory + driverOptions.target.apiLevel)); } bool Android_CJNATIVE::PrepareDependencyPath() { if ((objcopyPath = FindCangjieLLVMToolPath(g_toolList.at(ToolID::LLVM_OBJCOPY).name)).empty()) { return false; } if ((arPath = FindUserToolPath(g_toolList.at(ToolID::LLVM_AR).name)).empty()) { return false; } if ((ldPath = FindCangjieLLVMToolPath(g_toolList.at(ToolID::LLD).name)).empty()) { return false; } return true; } bool Android_CJNATIVE::GenerateLinking(const std::vector& objFiles) { // Different linking mode requires different gcc crt files GenerateLinkingTool(objFiles, "", {}); return true; } void Android_CJNATIVE::GenerateLinkingTool(const std::vector& objFiles, const std::string& gccLibPath, const std::pair& /* gccCrtFilePair */) { auto tool = std::make_unique(ldPath, ToolType::BACKEND, driverOptions.environment.allVariables); tool->SetLdLibraryPath(FileUtil::JoinPath(FileUtil::GetDirPath(ldPath.c_str()), "../lib")); std::string outputFile = GetOutputFileInfo(objFiles).filePath; tool->AppendArg("-o", outputFile); if (driverOptions.IsLTOEnabled()) { GenerateLinkOptionsForLTO(*tool.get()); // The -z notext option is the default for ld, while the -z noexecstack option is the default for lld. // Therefore, ld needs to explicitly pass -z noexecstack, and lld needs to explicitly pass -z notext. tool->AppendArg("-z", "notext"); } else { tool->AppendArg("-z", "noexecstack"); } tool->AppendArgIf(driverOptions.stripSymbolTable, "-s"); // Hot reload relies on .gnu.hash section. tool->AppendArg("--hash-style=both"); tool->AppendArg("-EL"); tool->AppendArg("-m", GetEmulation()); std::string cjldScript = GetCjldScript(tool); if (driverOptions.outputMode == GlobalOptions::OutputMode::EXECUTABLE) { tool->AppendArg("-pie"); } std::string crtBeginName = driverOptions.outputMode == GlobalOptions::OutputMode::EXECUTABLE ? "crtbegin_dynamic.o" : "crtbegin_so.o"; auto maybeCrtBegin = FileUtil::FindFileByName(crtBeginName, GetCRuntimeLibraryPath()); tool->AppendArg(maybeCrtBegin.value_or(crtBeginName)); HandleLLVMLinkOptions(objFiles, gccLibPath, *tool, cjldScript); std::string crtEndName = driverOptions.outputMode == GlobalOptions::OutputMode::EXECUTABLE ? "crtend_android.o" : "crtend_so.o"; auto maybeCrtEnd = FileUtil::FindFileByName(crtEndName, GetCRuntimeLibraryPath()); tool->AppendArg(maybeCrtEnd.value_or(crtEndName)); backendCmds.emplace_back(MakeSingleToolBatch({std::move(tool)})); } void Android_CJNATIVE::GenerateLinkOptions(Tool& tool) { tool.AppendArg("-lclang_rt.builtins-" + driverOptions.target.ArchToString() + "-android"); tool.AppendArg("-l:libcangjie-runtime.so"); tool.AppendArg(LINUX_CJNATIVE_LINK_OPTIONS); tool.AppendArg("-lclang_rt.builtins-" + driverOptions.target.ArchToString() + "-android"); tool.AppendArg("-ldl"); tool.AppendArg("-z", "max-page-size=4096"); } void Android_CJNATIVE::HandleSanitizerDependencies(Tool& tool) { for (const auto& arg : {"-lpthread", "-lrt", "-lm", "-ldl", "-lresolv"}) { tool.AppendArg(arg); } }cangjie_compiler-1.0.7/src/Driver/Toolchains/CJNATIVE/Android_CJNATIVE.h000066400000000000000000000030611510705540100253530ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Android_CJNATIVE ToolChain class. */ #ifndef CANGJIE_DRIVER_TOOLCHAIN_ANDROID_CJNATIVE_H #define CANGJIE_DRIVER_TOOLCHAIN_ANDROID_CJNATIVE_H #include #include "Toolchains/CJNATIVE/Linux_CJNATIVE.h" #include "cangjie/Driver/Backend/Backend.h" #include "cangjie/Driver/Driver.h" #include "cangjie/Driver/Toolchains/ToolChain.h" namespace Cangjie { class Android_CJNATIVE : public Linux_CJNATIVE { public: Android_CJNATIVE( const Cangjie::Driver& driver, const DriverOptions& driverOptions, std::vector& backendCmds) : Linux_CJNATIVE(driver, driverOptions, backendCmds) {}; ~Android_CJNATIVE() override {}; protected: void InitializeLibraryPaths() override; void AddCRuntimeLibraryPaths() override; bool PrepareDependencyPath() override; bool GenerateLinking(const std::vector& objFiles) override; void GenerateLinkingTool(const std::vector& objFiles, const std::string& gccLibPath, const std::pair& gccCrtFilePair) override; void GenerateLinkOptions(Tool& tool) override; void HandleSanitizerDependencies(Tool& tool) override; private: std::string sysroot; }; } // namespace Cangjie #endif // CANGJIE_DRIVER_TOOLCHAIN_ANDROID_CJNATIVE_Hcangjie_compiler-1.0.7/src/Driver/Toolchains/CJNATIVE/Darwin_CJNATIVE.cpp000066400000000000000000000054521510705540100255600ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Darwin_CJNATIVE ToolChain base class. */ #include "Toolchains/CJNATIVE/Darwin_CJNATIVE.h" #include "cangjie/Driver/TempFileManager.h" #include "cangjie/Driver/ToolOptions.h" #include "cangjie/Driver/Utils.h" #include "cangjie/Utils/FileUtil.h" using namespace Cangjie; using namespace Cangjie::Triple; void Darwin_CJNATIVE::AddSystemLibraryPaths() { if (driverOptions.IsCrossCompiling() && driverOptions.customizedSysroot) { // user-specified sysroot is only considered in cross-compilation AddLibraryPaths(ComputeLibPaths()); } MachO::AddSystemLibraryPaths(); } TempFileInfo Darwin_CJNATIVE::GenerateLinkingTool(const std::vector& objFiles, const std::string& darwinSDKVersion) { auto tool = std::make_unique(ldPath, ToolType::BACKEND, driverOptions.environment.allVariables); auto outputFileInfo = TempFileInfo{}; if (driverOptions.stripSymbolTable) { TempFileKind kind = driverOptions.outputMode == GlobalOptions::OutputMode::SHARED_LIB ? TempFileKind::T_DYLIB_MAC : TempFileKind::T_EXE_MAC; outputFileInfo = TempFileManager::Instance().CreateNewFileInfo(objFiles[0], kind); } else { outputFileInfo = GetOutputFileInfo(objFiles); } std::string outputFile = outputFileInfo.filePath; tool->AppendArg("-o", outputFile); tool->AppendArgIf(driverOptions.outputMode == GlobalOptions::OutputMode::SHARED_LIB, "-dylib"); tool->AppendArg("-arch", GetTargetArchString()); tool->AppendArg("-platform_version"); tool->AppendArg("macos"); tool->AppendArg("12.0.0"); tool->AppendArg(darwinSDKVersion); tool->AppendArg("-syslibroot"); tool->AppendArg(driverOptions.sysroot.empty() ? "/" : driverOptions.sysroot); if (driverOptions.stripSymbolTable) { tool->AppendArg("-install_name", GetOutputFileInfo(objFiles).filePath); } if (driverOptions.outputMode == GlobalOptions::OutputMode::EXECUTABLE) { tool->AppendArg("-pie"); } HandleLLVMLinkOptions(objFiles, *tool); GenerateRuntimePath(*tool); backendCmds.emplace_back(MakeSingleToolBatch({std::move(tool)})); return outputFileInfo; } void Darwin_CJNATIVE::GenerateLinkOptions(Tool& tool) { for (auto& option : LINUX_CJNATIVE_LINK_OPTIONS) { tool.AppendArg(option); } auto cangjieLibPath = FileUtil::JoinPath(FileUtil::JoinPath(driver.cangjieHome, "lib"), driverOptions.GetCangjieLibTargetPathName()); tool.AppendArg(FileUtil::JoinPath(cangjieLibPath, "libclang_rt.osx.a")); } cangjie_compiler-1.0.7/src/Driver/Toolchains/CJNATIVE/Darwin_CJNATIVE.h000066400000000000000000000031031510705540100252140ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Linux_CJNATIVE class. */ #ifndef CANGJIE_DRIVER_TOOLCHAIN_Darwin_CJNATIVE_H #define CANGJIE_DRIVER_TOOLCHAIN_Darwin_CJNATIVE_H #include "cangjie/Driver/Backend/Backend.h" #include "cangjie/Driver/Driver.h" #include "Toolchains/MachO.h" #include "cangjie/Driver/Toolchains/ToolChain.h" namespace Cangjie { class Darwin_CJNATIVE : public MachO { public: Darwin_CJNATIVE(const Cangjie::Driver& driver, const DriverOptions& driverOptions, std::vector& backendCmds) : MachO(driver, driverOptions, backendCmds) {}; ~Darwin_CJNATIVE() override = default; protected: const std::vector LINUX_CJNATIVE_LINK_OPTIONS = { #define CJNATIVE_STD_OPTIONS(OPTION) (OPTION), #include "Toolchains/BackendOptions.inc" #undef CJNATIVE_STD_OPTIONS #define CJNATIVE_DARWIN_BASIC_OPTIONS(OPTION) (OPTION), #include "Toolchains/BackendOptions.inc" #undef CJNATIVE_DARWIN_BASIC_OPTIONS }; // Gather library paths from LIBRARY_PATH and compiler guesses. void AddSystemLibraryPaths() override; TempFileInfo GenerateLinkingTool( const std::vector& objFiles, const std::string& darwinSDKVersion) override; void GenerateLinkOptions(Tool& tool) override; }; } // namespace Cangjie #endif // CANGJIE_DRIVER_TOOLCHAIN_Darwin_CJNATIVE_H cangjie_compiler-1.0.7/src/Driver/Toolchains/CJNATIVE/IOS_CJNATIVE.cpp000066400000000000000000000060361510705540100247650ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the IOS_CJNATIVE ToolChain base class. */ #include "Toolchains/CJNATIVE/IOS_CJNATIVE.h" #include "cangjie/Driver/TempFileManager.h" #include "cangjie/Driver/ToolOptions.h" #include "cangjie/Driver/Utils.h" #include "cangjie/Utils/FileUtil.h" using namespace Cangjie; using namespace Cangjie::Triple; void IOS_CJNATIVE::AddSystemLibraryPaths() { if (driverOptions.IsCrossCompiling() && driverOptions.customizedSysroot) { // user-specified sysroot is only considered in cross-compilation AddLibraryPaths(ComputeLibPaths()); } MachO::AddSystemLibraryPaths(); } TempFileInfo IOS_CJNATIVE::GenerateLinkingTool( const std::vector& objFiles, const std::string& darwinSDKVersion) { auto tool = std::make_unique(ldPath, ToolType::BACKEND, driverOptions.environment.allVariables); auto outputFileInfo = TempFileInfo{}; if (driverOptions.stripSymbolTable) { TempFileKind kind = driverOptions.outputMode == GlobalOptions::OutputMode::SHARED_LIB ? TempFileKind::T_DYLIB_MAC : TempFileKind::T_EXE_MAC; outputFileInfo = TempFileManager::Instance().CreateNewFileInfo(objFiles[0], kind); } else { outputFileInfo = GetOutputFileInfo(objFiles); } std::string outputFile = outputFileInfo.filePath; tool->AppendArg("-o", outputFile); tool->AppendArgIf(driverOptions.outputMode == GlobalOptions::OutputMode::SHARED_LIB, "-dylib"); tool->AppendArg("-arch", GetTargetArchString()); tool->AppendArg("-platform_version"); if (driverOptions.target.env == Triple::Environment::SIMULATOR) { tool->AppendArg("ios-simulator"); tool->AppendArg("17.5.0"); } else { tool->AppendArg("ios"); tool->AppendArg("17.5.0"); } tool->AppendArg(darwinSDKVersion); tool->AppendArg("-syslibroot"); tool->AppendArg(driverOptions.sysroot.empty() ? "/" : driverOptions.sysroot); if (driverOptions.outputMode == GlobalOptions::OutputMode::EXECUTABLE) { tool->AppendArg("-pie"); } HandleLLVMLinkOptions(objFiles, *tool); GenerateRuntimePath(*tool); backendCmds.emplace_back(MakeSingleToolBatch({std::move(tool)})); return outputFileInfo; } void IOS_CJNATIVE::GenerateLinkOptions(Tool& tool) { for (auto& option : LINUX_CJNATIVE_LINK_OPTIONS) { tool.AppendArg(option); } auto cangjieLibPath = FileUtil::JoinPath(FileUtil::JoinPath(driver.cangjieHome, "lib"), driverOptions.GetCangjieLibTargetPathName()); if (driverOptions.target.env == Triple::Environment::SIMULATOR) { tool.AppendArg(FileUtil::JoinPath(cangjieLibPath, "libclang_rt.iossim.a")); } else { tool.AppendArg(FileUtil::JoinPath(cangjieLibPath, "libclang_rt.ios.a")); } }cangjie_compiler-1.0.7/src/Driver/Toolchains/CJNATIVE/IOS_CJNATIVE.h000066400000000000000000000030761510705540100244330ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the IOS_CJNATIVE class. */ #ifndef CANGJIE_DRIVER_TOOLCHAIN_IOS_CJNATIVE_H #define CANGJIE_DRIVER_TOOLCHAIN_IOS_CJNATIVE_H #include "Toolchains/CJNATIVE/Darwin_CJNATIVE.h" #include "cangjie/Driver/Backend/Backend.h" #include "cangjie/Driver/Driver.h" #include "cangjie/Driver/Toolchains/ToolChain.h" using namespace Cangjie; class IOS_CJNATIVE : public Darwin_CJNATIVE { public: IOS_CJNATIVE(const Cangjie::Driver& driver, const DriverOptions& driverOptions, std::vector& backendCmds) : Darwin_CJNATIVE(driver, driverOptions, backendCmds) {}; ~IOS_CJNATIVE() override {}; protected: const std::vector LINUX_CJNATIVE_LINK_OPTIONS = { #define CJNATIVE_STD_OPTIONS(OPTION) (OPTION), #include "Toolchains/BackendOptions.inc" #undef CJNATIVE_STD_OPTIONS #define CJNATIVE_DARWIN_BASIC_OPTIONS(OPTION) (OPTION), #include "Toolchains/BackendOptions.inc" #undef CJNATIVE_DARWIN_BASIC_OPTIONS }; // Gather library paths from LIBRARY_PATH and compiler guesses. void AddSystemLibraryPaths() override; TempFileInfo GenerateLinkingTool( const std::vector& objFiles, const std::string& darwinSDKVersion) override; void GenerateLinkOptions(Tool& tool) override; }; #endif // CANGJIE_DRIVER_TOOLCHAIN_IOS_CJNATIVE_Hcangjie_compiler-1.0.7/src/Driver/Toolchains/CJNATIVE/Linux_CJNATIVE.cpp000066400000000000000000000203001510705540100254200ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Linux_CJNATIVE ToolChain base class. */ #include "Toolchains/CJNATIVE/Linux_CJNATIVE.h" #include "cangjie/Driver/ToolOptions.h" #include "cangjie/Driver/Utils.h" #include "cangjie/Utils/FileUtil.h" using namespace Cangjie; using namespace Cangjie::Triple; void Linux_CJNATIVE::AddSystemLibraryPaths() { if (driverOptions.IsCrossCompiling() && driverOptions.customizedSysroot) { // user-specified sysroot is only considered in cross-compilation AddLibraryPaths(ComputeLibPaths()); } Gnu::AddSystemLibraryPaths(); } std::pair Linux_CJNATIVE::GetGccCrtFilePair() const { switch (driverOptions.outputMode) { case GlobalOptions::OutputMode::EXECUTABLE: case GlobalOptions::OutputMode::SHARED_LIB: return gccSharedCrtFilePair; // WARNING: OutputMode::STATIC_LIB has no relation with gcc static crt files. // OutputMode::STATIC_LIB indicates that we should produce an archive(.a) file, which is similar to '-c' // Static crt files are for 'ld -static' but it is not supported currently. case GlobalOptions::OutputMode::STATIC_LIB: default: break; } return {}; } std::string Linux_CJNATIVE::GetCjldScript(const std::unique_ptr& tool) const { std::string cjldScript = "cjld.lds"; switch (driverOptions.outputMode) { case GlobalOptions::OutputMode::SHARED_LIB: tool->AppendArg("-shared"); cjldScript = "cjld.shared.lds"; [[fallthrough]]; case GlobalOptions::OutputMode::EXECUTABLE: tool->AppendArg("-dynamic-linker"); tool->AppendArg(GetDynamicLinkerPath(driverOptions.target)); break; case GlobalOptions::OutputMode::STATIC_LIB: default: break; } return cjldScript; } void Linux_CJNATIVE::GenerateLinkingTool(const std::vector& objFiles, const std::string& gccLibPath, const std::pair& gccCrtFilePair) { auto tool = std::make_unique(ldPath, ToolType::BACKEND, driverOptions.environment.allVariables); std::string outputFile = GetOutputFileInfo(objFiles).filePath; tool->AppendArg("-o", outputFile); if (driverOptions.IsLTOEnabled()) { tool->SetLdLibraryPath(FileUtil::JoinPath(FileUtil::GetDirPath(ldPath), "../lib")); GenerateLinkOptionsForLTO(*tool.get()); } else if (driverOptions.EnableHwAsan()) { // same args as lto except GenerateLinkOptionsForLTO tool->SetLdLibraryPath(FileUtil::JoinPath(FileUtil::GetDirPath(ldPath), "../lib")); tool->AppendArg("-z", "notext"); } else { tool->AppendArg("-z", "noexecstack"); } tool->AppendArgIf(driverOptions.stripSymbolTable, "-s"); tool->AppendArg("-m", GetEmulation()); // Hot reload relies on .gnu.hash section. tool->AppendArg("--hash-style=both"); tool->AppendArgIf(driverOptions.enableGcSections, "-gc-sections"); std::string cjldScript = GetCjldScript(tool); // Link order: crt1 -> crti -> crtbegin -> other input files -> crtend -> crtn. if (driverOptions.outputMode == GlobalOptions::OutputMode::EXECUTABLE) { tool->AppendArg("-pie"); auto maybeCrt1 = FileUtil::FindFileByName("Scrt1.o", GetCRuntimeLibraryPath()); // If we found Scrt1.o, we use the absolute path of system Scrt1.o, otherwise we let it simply be Scrt1.o, // we don't expect such cases though. Same as crti.o and crtend.o. tool->AppendArg(maybeCrt1 ? maybeCrt1.value() : "Scrt1.o"); } auto maybeCrti = FileUtil::FindFileByName("crti.o", GetCRuntimeLibraryPath()); tool->AppendArg(maybeCrti ? maybeCrti.value() : "crti.o"); tool->AppendArg(GetGccLibFile(gccCrtFilePair.first, gccLibPath)); // Add crtfastmath.o if fast math is enabled and crtfastmath.o is found. if (driverOptions.fastMathMode) { auto crtfastmathFilepath = GetGccLibFile("crtfastmath.o", gccLibPath); tool->AppendArgIf(FileUtil::FileExist(crtfastmathFilepath), crtfastmathFilepath); } HandleLLVMLinkOptions(objFiles, gccLibPath, *tool, cjldScript); GenerateRuntimePath(*tool); tool->AppendArg(GetGccLibFile(gccCrtFilePair.second, gccLibPath)); auto maybeCrtn = FileUtil::FindFileByName("crtn.o", GetCRuntimeLibraryPath()); tool->AppendArg(maybeCrtn ? maybeCrtn.value() : "crtn.o"); backendCmds.emplace_back(MakeSingleToolBatch({std::move(tool)})); } void Linux_CJNATIVE::GenerateLinkOptions(Tool& tool) { if (driverOptions.linkStatic) { if (driverOptions.EnableSanitizer()) { auto cangjieLibPath = FileUtil::JoinPath(FileUtil::JoinPath( FileUtil::JoinPath(driver.cangjieHome, "lib"), driverOptions.GetCangjieLibTargetPathName()), driverOptions.SanitizerTypeToShortString()); tool.AppendArg(FileUtil::JoinPath(cangjieLibPath, "libcangjie-runtime.a")); tool.AppendArg(FileUtil::JoinPath(cangjieLibPath, "libcangjie-thread.a")); tool.AppendArg(FileUtil::JoinPath(cangjieLibPath, "libboundscheck-static.a")); } else { tool.AppendArg("-l:libcangjie-runtime.a"); tool.AppendArg("-l:libcangjie-thread.a"); tool.AppendArg("-l:libboundscheck-static.a"); } tool.AppendArg(LINUX_STATIC_LINK_OPTIONS); } else { tool.AppendArg("-l:libcangjie-runtime.so"); tool.AppendArg(LINUX_CJNATIVE_LINK_OPTIONS); } // `__gnu_h2f_ieee` `__gnu_f2h_ieee` two symbols are needed to float16 on non-arm64 platform. // Because std-core package is always imported, we put it here. if (driverOptions.target.arch != Triple::ArchType::AARCH64) { tool.AppendArg("-lclang_rt-builtins"); } else { if (driverOptions.target.os == Triple::OSType::LINUX && driverOptions.target.env == Triple::Environment::GNU) { tool.AppendArg("-lgcc"); } } } void Linux_CJNATIVE::GenerateLinkOptionsForLTO(Tool& tool) const { using namespace ToolOptions; // Set LTO lld options SetFuncType setOptionHandler = [&tool](const std::string& option) { tool.AppendArg(option); }; std::vector setOptionsPass = { LLD::SetLTOOptimizationLevelOptions, // Comment ensure vector members are arranged vertically. LLD::SetLTOOptions, // }; SetOptions(setOptionHandler, driverOptions, setOptionsPass); // Set opt passes std::string passesCollector = "--lto-newpm-passes="; std::vector passItems; { using namespace ToolOptions; // remove the initial hyphen in the options. SetFuncType handler = [&passItems](const std::string& option) { passItems.emplace_back(option.substr(1)); }; SetOptions(handler, driverOptions, OPT::SetNewPassManagerOptions); } for (auto& it : passItems) { passesCollector += it; if (&it != &passItems.back()) { passesCollector += ","; } } tool.AppendArg(passesCollector); // use set to avoid duplicate options std::unordered_set optionSet = {}; // set composite option SetFuncType setCompositeOption = [&tool, &optionSet](const std::string& option) { if (optionSet.find(option) == optionSet.end()) { optionSet.emplace(option); tool.AppendArg("--mllvm"); tool.AppendArg(option); } }; std::vector setCompositeOptionsPass = { OPT::SetOptions, // Comment ensure vector members are arranged vertically. OPT::SetCodeObfuscationOptions, // OPT::SetTransparentOptions, // The transparent OPT options must after other OPT options. LLD::SetPgoOptions, // LLC::SetOptions, // LLC::SetTransparentOptions, // The transparent LLC options must after other LLC options. }; SetOptions(setCompositeOption, driverOptions, setCompositeOptionsPass); }cangjie_compiler-1.0.7/src/Driver/Toolchains/CJNATIVE/Linux_CJNATIVE.h000066400000000000000000000037371510705540100251040ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Linux_CJNATIVE class. */ #ifndef CANGJIE_DRIVER_TOOLCHAIN_Linux_CJNATIVE_H #define CANGJIE_DRIVER_TOOLCHAIN_Linux_CJNATIVE_H #include "cangjie/Driver/Backend/Backend.h" #include "cangjie/Driver/Driver.h" #include "Toolchains/Gnu.h" #include "cangjie/Driver/Toolchains/ToolChain.h" namespace Cangjie { class Linux_CJNATIVE : public Gnu { public: Linux_CJNATIVE(const Cangjie::Driver& driver, const DriverOptions& driverOptions, std::vector& backendCmds) : Gnu(driver, driverOptions, backendCmds) {}; ~Linux_CJNATIVE() override = default; protected: const std::vector LINUX_CJNATIVE_LINK_OPTIONS = { #define CJNATIVE_GNU_LINUX_BASIC_OPTIONS(OPTION) (OPTION), #include "Toolchains/BackendOptions.inc" #undef CJNATIVE_GNU_LINUX_BASIC_OPTIONS }; const std::vector LINUX_STATIC_LINK_OPTIONS = { #define CJNATIVE_STATIC_LINK_BASIC_OPTIONS(OPTION) (OPTION), #include "Toolchains/BackendOptions.inc" #undef CJNATIVE_STATIC_LINK_BASIC_OPTIONS }; // Gather library paths from LIBRARY_PATH and compiler guesses. void AddSystemLibraryPaths() override; // crtbeginS.o is used in place of crtbegin.o when generating PIEs. std::pair GetGccCrtFilePair() const override; std::string GetCjldScript(const std::unique_ptr& tool) const; void GenerateLinkingTool(const std::vector& objFiles, const std::string& gccLibPath, const std::pair& gccCrtFilePair) override; void GenerateLinkOptions(Tool& tool) override; void GenerateLinkOptionsForLTO(Tool& tool) const; }; } // namespace Cangjie #endif // CANGJIE_DRIVER_TOOLCHAIN_Linux_CJNATIVE_H cangjie_compiler-1.0.7/src/Driver/Toolchains/CJNATIVE/MinGW_CJNATIVE.cpp000066400000000000000000000175211510705540100253150ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the MinGW_CJNATIVE ToolChain base class. */ #include "Toolchains/CJNATIVE/MinGW_CJNATIVE.h" #include "cangjie/Driver/TempFileManager.h" #include "cangjie/Driver/Utils.h" #include "cangjie/Utils/FileUtil.h" using namespace Cangjie; using namespace Cangjie::Triple; void MinGW_CJNATIVE::InitializeLibraryPaths() { // The paths from --toolchain/-B can be used to search system libs, such as Scrt1.o, crti.o, etc. for (auto& libPath : driverOptions.toolChainPaths) { AddCRuntimeLibraryPath(libPath); } InitializeMinGWSysroot(); AddCRuntimeLibraryPaths(); AddSystemLibraryPaths(); } void MinGW_CJNATIVE::InitializeMinGWSysroot() { if (driverOptions.customizedSysroot) { sysroot = driverOptions.sysroot; } else { auto gccPath = FileUtil::FindProgramByName("x86_64-w64-mingw32-gcc.exe", driverOptions.environment.paths); if (gccPath.empty()) { return; } sysroot = FileUtil::GetDirPath(FileUtil::GetDirPath(gccPath)); } } void MinGW_CJNATIVE::AddCRuntimeLibraryPaths() { // We search /lib path for gcc libraries. auto libPath = FileUtil::JoinPath(sysroot, "lib"); AddCRuntimeLibraryPath(libPath); // Some libraries can be found in a compatible toolchain folder, we try to add such folder as well. auto directories = FileUtil::GetDirectories(libPath); for (const auto& dir : directories) { if (Triple::IsPossibleMatchingTripleName(driverOptions.target, dir.name)) { AddCRuntimeLibraryPath(dir.path); } } } void MinGW_CJNATIVE::AddSystemLibraryPaths() { AddLibraryPaths(ComputeLibPaths()); Gnu::AddSystemLibraryPaths(); } std::vector MinGW_CJNATIVE::ComputeLibPaths() const { return {sysroot + "/lib", sysroot + "/x86_64-w64-mingw32/lib"}; } std::string MinGW_CJNATIVE::GetCjldScript(const std::unique_ptr& tool) const { std::string cjldScript = "cjld.lds"; switch (driverOptions.outputMode) { case GlobalOptions::OutputMode::SHARED_LIB: tool->AppendArg("-shared"); cjldScript = "cjld.shared.lds"; [[fallthrough]]; case GlobalOptions::OutputMode::STATIC_LIB: case GlobalOptions::OutputMode::EXECUTABLE: default: break; } return cjldScript; } std::string MinGW_CJNATIVE::GenerateGCCLibPath( [[maybe_unused]] const std::pair& gccCrtFilePair) const { return mingwLibPath; } void MinGW_CJNATIVE::GenerateArchiveTool(const std::vector& objFiles) { auto tool = std::make_unique(arPath, ToolType::BACKEND, driverOptions.environment.allVariables); // llvm-ar in our package may be used for creating archive file, so LD_LIBRARY_PATH need to be set to llvm lib dir. if (driverOptions.IsCrossCompiling() && FileUtil::GetFileName(arPath).find("llvm-ar") != std::string::npos) { tool->SetLdLibraryPath(FileUtil::JoinPath(FileUtil::GetDirPath(arPath), "../lib")); } // c for no warn if the library had to be created // r for replacing existing or insert new file(s) into the archive // D for deterministic mode tool->AppendArg("crD"); // When we reach here, we must be at the final phase of the compilation, // which means that is the final output. TempFileInfo fileInfo = TempFileManager::Instance().CreateNewFileInfo(objFiles[0], TempFileKind::O_STATICLIB); std::string outputFile = fileInfo.filePath; // If archive exists, ar attempts to insert given obj files into the archive. // We always try to remove the archive before creating a new one. (void)FileUtil::Remove(outputFile.c_str()); tool->AppendArg(outputFile); // Note: We do not use tool->inputs here since it is always placed right after executable // the first arg of ar should be the option not input for (const auto& objFile : objFiles) { tool->AppendArg(objFile.filePath); } backendCmds.emplace_back(MakeSingleToolBatch({std::move(tool)})); } void MinGW_CJNATIVE::GenerateLinkingTool(const std::vector& objFiles, const std::string& gccLibPath, const std::pair& gccCrtFilePair) { auto tool = std::make_unique(ldPath, ToolType::BACKEND, driverOptions.environment.allVariables); std::string outputFile = GetOutputFileInfo(objFiles).filePath; tool->AppendArg("-o", outputFile); tool->AppendArgIf(driverOptions.stripSymbolTable, "-s"); // --nxcompat prevents executing codes from stack. tool->AppendArg("--nxcompat"); // --dynamic-base makes that the base address of the program is randomly set // every time it is executed and loaded into memory. tool->AppendArg("--dynamicbase"); // --high-entropy-va makes the executable image supports high-entropy 64-bit // address space layout randomization (ASLR), which means ASLR can use the entire 64-bit address space. tool->AppendArg("--high-entropy-va"); tool->AppendArg("-m", GetEmulation()); std::string cjldScript = GetCjldScript(tool); tool->AppendArg("-Bdynamic"); if (driverOptions.outputMode == GlobalOptions::OutputMode::SHARED_LIB) { tool->AppendArg("-e", "DllMainCRTStartup"); tool->AppendArg("--export-all-symbols"); } // Link order: crt2 -> crtbegin -> other input files -> crtend. if (driverOptions.outputMode == GlobalOptions::OutputMode::EXECUTABLE || driverOptions.outputMode == GlobalOptions::OutputMode::SHARED_LIB) { auto crtObjName = driverOptions.outputMode == GlobalOptions::OutputMode::EXECUTABLE ? "crt2.o" : "dllcrt2.o"; auto crtObjPath = mingwLibPath + crtObjName; // If we found crt2.o, we use the absolute path of system crt2.o, otherwise we let it simply be (dll)crt2.o, // we don't expect such cases though. Same as crtend.o. tool->AppendArg(crtObjPath); } tool->AppendArg(GetGccLibFile(gccCrtFilePair.first, gccLibPath)); // Add crtfastmath.o if fast math is enabled and crtfastmath.o is found. if (driverOptions.fastMathMode) { auto crtfastmathFilepath = GetGccLibFile("crtfastmath.o", gccLibPath); tool->AppendArgIf(FileUtil::FileExist(crtfastmathFilepath), crtfastmathFilepath); } HandleLLVMLinkOptions(objFiles, gccLibPath, *tool, cjldScript); // extra ld options given by -ld-options tool->AppendArg(GetGccLibFile(gccCrtFilePair.second, gccLibPath)); backendCmds.emplace_back(MakeSingleToolBatch({std::move(tool)})); } void MinGW_CJNATIVE::HandleLibrarySearchPaths(Tool& tool, const std::string& cangjieLibPath) { // Append -L (those specified in command) as search paths tool.AppendArg("-L" + mingwLibPath); Gnu::HandleLibrarySearchPaths(tool, cangjieLibPath); } void MinGW_CJNATIVE::GenerateLinkOptions(Tool& tool) { tool.AppendArg("-l:libcangjie-runtime.dll"); tool.AppendArg("-lclang_rt-builtins"); tool.AppendArg(MINGW_CJNATIVE_LINK_OPTIONS); } bool MinGW_CJNATIVE::PrepareDependencyPath() { if ((arPath = FindCangjieLLVMToolPath(g_toolList.at(ToolID::LLVM_AR).name)).empty()) { return false; } if ((ldPath = FindCangjieLLVMToolPath(g_toolList.at(ToolID::LLD).name)).empty()) { return false; } return true; } std::string MinGW_CJNATIVE::FindCangjieMinGWToolPath(const std::string toolName) const { std::string toolPath = FindToolPath( toolName, std::vector{FileUtil::JoinPath(driver.cangjieHome, "third_party/mingw/bin/")}); if (toolPath.empty()) { Errorf("not found `%s` in search paths. Your Cangjie installation might be broken.", toolName.c_str()); } return toolPath; } cangjie_compiler-1.0.7/src/Driver/Toolchains/CJNATIVE/MinGW_CJNATIVE.h000066400000000000000000000050161510705540100247560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the MinGW_CJNATIVE class. */ #ifndef CANGJIE_DRIVER_TOOLCHAIN_MINGW_CJNATIVE_H #define CANGJIE_DRIVER_TOOLCHAIN_MINGW_CJNATIVE_H #include "cangjie/Driver/Backend/Backend.h" #include "cangjie/Driver/Driver.h" #include "Toolchains/Gnu.h" #include "cangjie/Driver/Toolchains/ToolChain.h" namespace Cangjie { class MinGW_CJNATIVE : public Gnu { public: MinGW_CJNATIVE(const Cangjie::Driver& driver, const DriverOptions& driverOptions, std::vector& backendCmds) : Gnu(driver, driverOptions, backendCmds) { mingwLibPath = FileUtil::JoinPath(driver.cangjieHome, "third_party/mingw/lib/"); }; ~MinGW_CJNATIVE() override = default; protected: std::string GetSharedLibraryExtension() const override { return ".dll"; } void InitializeLibraryPaths() override; void InitializeMinGWSysroot(); void AddCRuntimeLibraryPaths() override; bool PrepareDependencyPath() override; // Gather library paths from LIBRARY_PATH and compiler guesses. void AddSystemLibraryPaths() override; std::vector ComputeLibPaths() const override; std::string GenerateGCCLibPath(const std::pair& gccCrtFilePair) const override; void GenerateArchiveTool(const std::vector& objFiles) override; void HandleLibrarySearchPaths(Tool& tool, const std::string& cangjieLibPath) override; std::pair GetGccCrtFilePair() const override { return gccExecCrtFilePair; } std::string GetCjldScript(const std::unique_ptr& tool) const; void GenerateLinkingTool(const std::vector& objFiles, const std::string& gccLibPath, const std::pair& gccCrtFilePair) override; void GenerateLinkOptions(Tool& tool) override; std::string FindCangjieMinGWToolPath(const std::string toolName) const; private: const std::vector MINGW_CJNATIVE_LINK_OPTIONS = { #define CJNATIVE_WINDOWS_BASIC_OPTIONS(OPTION) (OPTION), #include "Toolchains/BackendOptions.inc" #undef CJNATIVE_WINDOWS_BASIC_OPTIONS }; std::string sysroot; std::string mingwLibPath; }; } // namespace Cangjie #endif // CANGJIE_DRIVER_TOOLCHAIN_MINGW_CJNATIVE_H cangjie_compiler-1.0.7/src/Driver/Toolchains/CJNATIVE/Ohos_CJNATIVE.cpp000066400000000000000000000077071510705540100252510ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Ohos_CJNATIVE ToolChain base class. */ #include "Toolchains/CJNATIVE/Ohos_CJNATIVE.h" using namespace Cangjie; using namespace Cangjie::Triple; void Ohos_CJNATIVE::AddCRuntimeLibraryPaths() { for (const auto& path : driverOptions.toolChainPaths) { AddCRuntimeLibraryPath(path); } } bool Ohos_CJNATIVE::PrepareDependencyPath() { if ((objcopyPath = FindCangjieLLVMToolPath(g_toolList.at(ToolID::LLVM_OBJCOPY).name)).empty()) { return false; } if ((arPath = FindCangjieLLVMToolPath(g_toolList.at(ToolID::LLVM_AR).name)).empty()) { return false; } if ((ldPath = FindCangjieLLVMToolPath(g_toolList.at(ToolID::LLD).name)).empty()) { return false; } return true; } bool Ohos_CJNATIVE::GenerateLinking(const std::vector& objFiles) { // Different linking mode requires different gcc crt files GenerateLinkingTool(objFiles, "", {}); return true; } void Ohos_CJNATIVE::GenerateLinkingTool(const std::vector& objFiles, const std::string& gccLibPath, const std::pair& /* gccCrtFilePair */) { auto tool = std::make_unique(ldPath, ToolType::BACKEND, driverOptions.environment.allVariables); tool->SetLdLibraryPath(FileUtil::JoinPath(FileUtil::GetDirPath(ldPath.c_str()), "../lib")); std::string outputFile = GetOutputFileInfo(objFiles).filePath; tool->AppendArg("-o", outputFile); if (driverOptions.IsLTOEnabled()) { GenerateLinkOptionsForLTO(*tool.get()); } else if (driverOptions.EnableHwAsan()) { // Same args as lto except GenerateLinkOptionsForLTO tool->AppendArg("-z", "notext"); } else { tool->AppendArg("-z", "noexecstack"); } tool->AppendArg("-z", "max-page-size=4096"); tool->AppendArgIf(driverOptions.stripSymbolTable, "-s"); // Hot reload relies on .gnu.hash section. tool->AppendArg("--hash-style=both"); tool->AppendArg("-m", GetEmulation()); std::string cjldScript = GetCjldScript(tool); // Link order: Scrt1 -> crti -> crtbegin -> other input files -> crtend -> crtn. if (driverOptions.outputMode == GlobalOptions::OutputMode::EXECUTABLE) { tool->AppendArg("-pie"); auto maybeCrt1 = FileUtil::FindFileByName("Scrt1.o", GetCRuntimeLibraryPath()); // If we found Scrt1.o, we use the absolute path of system Scrt1.o, otherwise we let it simply be Scrt1.o, // we don't expect such cases though. Same as crti.o and crtend.o. tool->AppendArg(maybeCrt1.value_or("Scrt1.o")); } auto maybeCrti = FileUtil::FindFileByName("crti.o", GetCRuntimeLibraryPath()); tool->AppendArg(maybeCrti.value_or("crti.o")); HandleLLVMLinkOptions(objFiles, gccLibPath, *tool, cjldScript); GenerateRuntimePath(*tool); auto maybeCrtn = FileUtil::FindFileByName("crtn.o", GetCRuntimeLibraryPath()); tool->AppendArg(maybeCrtn.value_or("crtn.o")); backendCmds.emplace_back(MakeSingleToolBatch({std::move(tool)})); } void Ohos_CJNATIVE::GenerateLinkOptions(Tool& tool) { tool.AppendArg("-l:libcangjie-runtime.so"); for (auto& option : LINUX_CJNATIVE_LINK_OPTIONS) { // No libgcc_s.so in hm toolchain if (option.compare("-l gcc_s") != 0) { tool.AppendArg(option); } } tool.AppendArg("-lclang_rt.builtins"); // Remind runtime to remove dependency of unwind_s in hm tool.AppendArg("-lunwind"); } void Ohos_CJNATIVE::HandleSanitizerDependencies(Tool& tool) { for (const auto& arg : {"-lpthread", "-lrt", "-lm", "-ldl", "-lresolv"}) { tool.AppendArg(arg); } // The ohos has no gcc_s, unwind (has same function as gcc_s) is // always linked in GenerateLinkOptions } cangjie_compiler-1.0.7/src/Driver/Toolchains/CJNATIVE/Ohos_CJNATIVE.h000066400000000000000000000030641510705540100247060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Ohos_CJNATIVE ToolChain class. */ #ifndef CANGJIE_DRIVER_TOOLCHAIN_Ohos_CJNATIVE_H #define CANGJIE_DRIVER_TOOLCHAIN_Ohos_CJNATIVE_H #include "cangjie/Driver/Backend/Backend.h" #include "cangjie/Driver/Driver.h" #include "cangjie/Driver/Toolchains/ToolChain.h" #include "Toolchains/CJNATIVE/Linux_CJNATIVE.h" namespace Cangjie { class Ohos_CJNATIVE : public Linux_CJNATIVE { public: Ohos_CJNATIVE(const Cangjie::Driver& driver, const DriverOptions& driverOptions, std::vector& backendCmds) : Linux_CJNATIVE(driver, driverOptions, backendCmds) {}; ~Ohos_CJNATIVE() override = default; protected: void AddCRuntimeLibraryPaths() override; bool PrepareDependencyPath() override; bool GenerateLinking(const std::vector& objFiles) override; void GenerateLinkingTool(const std::vector& objFiles, const std::string& gccLibPath, const std::pair& gccCrtFilePair) override; void GenerateLinkOptions(Tool& tool) override; void HandleSanitizerDependencies(Tool& tool) override; std::string GetClangRTProfileLibraryName() const override { return "libclang_rt.profile.a"; } }; } // namespace Cangjie #endif // CANGJIE_DRIVER_TOOLCHAIN_Ohos_CJNATIVE_H cangjie_compiler-1.0.7/src/Driver/Toolchains/GCCPathScanner.cpp000066400000000000000000000135301510705540100243650ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements GCCPath and GCCPathScanner. */ #include "cangjie/Driver/Toolchains/GCCPathScanner.h" #include #include "cangjie/Utils/FileUtil.h" using namespace Cangjie; bool GCCPathScanner::IsCompatiableTripleName(const Triple::Info& info, const std::string& name) { if (info.os == Triple::OSType::LINUX && info.env == Triple::Environment::GNU && info.vendor == Triple::Vendor::UNKNOWN) { std::string archString = info.ArchToString(); std::vector compatibleNames = { info.ToFullTripleString(), archString + "-linux-gnu", archString + "-pc-linux-gnu", archString + "-suse-linux", archString + "-redhat-linux" }; return std::find(compatibleNames.begin(), compatibleNames.end(), name) != compatibleNames.end(); } std::string tripleName = info.ToFullTripleString(); if (tripleName == "x86_64-unknown-windows-gnu") { return name == tripleName || name == "x86_64-w64-mingw32"; } return false; } std::vector GCCPathScanner::GetAllMatchingSubDirectories( const Triple::Info& target, const std::vector& directories) { std::vector possibleDirectories; for (const FileUtil::Directory& directory : directories) { if (IsCompatiableTripleName(target, directory.name) || Triple::IsPossibleMatchingTripleName(target, directory.name)) { auto subDirectories = FileUtil::GetDirectories(directory.path); possibleDirectories.insert(possibleDirectories.end(), subDirectories.begin(), subDirectories.end()); } } return possibleDirectories; } std::optional GCCPathScanner::Scan() { if (searchPaths.empty()) { return std::nullopt; } std::string selectedGccPath; GCCVersion selectedGccVersion{0, 0, 0}; for (std::string& dirPrefix : searchPaths) { auto gccDirName = FileUtil::JoinPath(dirPrefix, "gcc"); auto directories = GetAllMatchingSubDirectories(tripleTarget, FileUtil::GetDirectories(gccDirName)); for (const auto& dir : directories) { std::optional gccVersion = StrToGCCVersion(dir.name); if (gccVersion && selectedGccVersion < *gccVersion && AllForFilesExist(dir.path)) { selectedGccPath = dir.path; selectedGccVersion = *gccVersion; } } } if (selectedGccPath.empty()) { return std::nullopt; } else { return {GCCPath{selectedGccPath, selectedGccVersion}}; } } std::optional GCCPathScanner::StrToGCCVersion(const std::string& versionStr) { // For std::isdigit, non-zero value if the character is a numeric character, zero otherwise. auto hasInvalidDigits = [](unsigned char c) { return std::isdigit(c) == 0; }; GCCVersion ver{0, 0, 0}; auto majorEndIndex = versionStr.find_first_of('.'); if (majorEndIndex == 0) { // e.g. ".whatever", ".y.z" or such things are not valid gcc version names return std::nullopt; } // If . is not found, it may have only major number, e.g. "7", "8" or such things are valid gcc version name if (majorEndIndex == std::string::npos) { majorEndIndex = versionStr.length(); } // If it is not a number, it is not a valid gcc version name bool invalidMajorVer = std::any_of( versionStr.begin(), versionStr.begin() + static_cast(majorEndIndex), hasInvalidDigits); if (invalidMajorVer) { return std::nullopt; } int majorVer = std::stoi(versionStr.substr(0, majorEndIndex)); CJC_ASSERT(majorVer >= std::numeric_limits::min() && majorVer <= std::numeric_limits::max()); ver.major = static_cast(majorVer); if (majorEndIndex == versionStr.length()) { return {ver}; } auto minorEndIndex = versionStr.find_first_of('.', majorEndIndex + 1); if (minorEndIndex == majorEndIndex + 1) { return std::nullopt; } if (minorEndIndex == std::string::npos) { minorEndIndex = versionStr.length(); } bool invalidMinorVer = std::any_of(versionStr.begin() + static_cast(majorEndIndex + 1), versionStr.begin() + static_cast(minorEndIndex), hasInvalidDigits); if (invalidMinorVer) { return std::nullopt; } int minorVer = std::stoi(versionStr.substr(majorEndIndex + 1, (minorEndIndex - majorEndIndex) - 1)); CJC_ASSERT(minorVer >= std::numeric_limits::min() && minorVer <= std::numeric_limits::max()); ver.minor = static_cast(minorVer); if (minorEndIndex == versionStr.length()) { return {ver}; } // Build is empty, it is not a valid gcc version name if (versionStr.length() == minorEndIndex + 1) { return std::nullopt; } bool invalidBuildVer = std::any_of( versionStr.begin() + static_cast(minorEndIndex + 1), versionStr.end(), hasInvalidDigits); if (invalidBuildVer) { return std::nullopt; } int buildVer = std::stoi(versionStr.substr(minorEndIndex + 1, (versionStr.length() - minorEndIndex) - 1)); CJC_ASSERT(buildVer >= std::numeric_limits::min() && buildVer <= std::numeric_limits::max()); ver.build = static_cast(buildVer); return {ver}; } bool GCCPathScanner::AllForFilesExist(const std::string& path) { return std::all_of(forFiles.cbegin(), forFiles.cend(), [&path](const std::string& filename) { return FileUtil::FileExist(FileUtil::JoinPath(path, filename)); }); } cangjie_compiler-1.0.7/src/Driver/Toolchains/Gnu.cpp000066400000000000000000000501311510705540100223710ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the GNU ToolChain class. */ #include "Toolchains/Gnu.h" #include #include #include "cangjie/Driver/TempFileManager.h" #include "cangjie/Driver/Toolchains/GCCPathScanner.h" #include "cangjie/Driver/Toolchains/ToolChain.h" #include "cangjie/Driver/Utils.h" #include "cangjie/Utils/FileUtil.h" using namespace Cangjie; using namespace Cangjie::Triple; namespace { inline void LinkStaticLibrary(Tool& tool, const std::string& lib) { tool.AppendArg("--whole-archive"); tool.AppendArg(lib); tool.AppendArg("--no-whole-archive"); } } std::string Gnu::GenerateGCCLibPath(const std::pair& gccCrtFilePair) const { std::string gccLibPath; // Scan some possible directories where gcc may be installed, possible directories vary in different // triple targets. If we cannot find any available gcc lib path, stop proceeding to linking stage. // Selected gcc lib path must contain crt files we need for linking. std::vector forFiles{gccCrtFilePair.first, gccCrtFilePair.second}; auto gccPathScanner = GCCPathScanner(driverOptions.target, forFiles, GetCRuntimeLibraryPath()); auto maybeGccPath = gccPathScanner.Scan(); if (maybeGccPath.has_value()) { gccLibPath = maybeGccPath.value().libPath; } return gccLibPath; } std::string Gnu::GetGccLibFile(const std::string& filename, const std::string& gccLibPath) const { auto maybeCrtFile = FileUtil::FindFileByName(filename, GetCRuntimeLibraryPath()); if (maybeCrtFile.has_value()) { return maybeCrtFile.value(); } else if (!gccLibPath.empty()) { return FileUtil::JoinPath(gccLibPath, filename); } return filename; } std::pair Gnu::GetGccCrtFilePair() const { // Mini FAQ about the misc libc/gcc crt files. // Some definitions: // PIC - position independent code (-fPIC) // PIE - position independent executable (-fPIE -pie) // crt - C runtime // crtbegin.o // GCC uses this to find the start of the constructors. // crtbeginS.o // Used in place of crtbegin.o when generating shared objects/PIEs. // crtbeginT.o // Used in place of crtbegin.o when generating static executables. // crtend.o // GCC uses this to find the start of the destructors. // crtendS.o // Used in place of crtend.o when generating shared objects/PIEs. switch (driverOptions.outputMode) { case GlobalOptions::OutputMode::EXECUTABLE: return gccExecCrtFilePair; case GlobalOptions::OutputMode::SHARED_LIB: return gccSharedCrtFilePair; case GlobalOptions::OutputMode::STATIC_LIB: default: break; } return {}; } bool Gnu::PrepareDependencyPath() { auto toolPrefix = driverOptions.IsCrossCompiling() ? driverOptions.target.GetEffectiveTripleString() + "-" : ""; if ((arPath = FindUserToolPath(toolPrefix + g_toolList.at(ToolID::AR).name)).empty()) { return false; } if ((objcopyPath = FindCangjieLLVMToolPath(g_toolList.at(ToolID::LLVM_OBJCOPY).name)).empty()) { return false; } if (driverOptions.IsLTOEnabled() || driverOptions.EnableHwAsan()) { if ((ldPath = FindCangjieLLVMToolPath(g_toolList.at(ToolID::LLD).name)).empty()) { return false; } } else { if ((ldPath = FindUserToolPath(toolPrefix + g_toolList.at(ToolID::LD).name)).empty()) { return false; } } return true; } void Gnu::GenerateArchiveTool(const std::vector& objFiles) { auto tool = std::make_unique(arPath, ToolType::BACKEND, driverOptions.environment.allVariables); // The c for no warn if the library had to be created // The r for replacing existing or insert new file(s) into the archive // The D for deterministic mode tool->AppendArg("crD"); // When we reach here, we must be at the final phase of the compilation, // which means that is the final output. TempFileInfo fileInfo = TempFileManager::Instance().CreateNewFileInfo(objFiles[0], TempFileKind::O_STATICLIB); std::string outputFile = fileInfo.filePath; // If archive exists, ar attempts to insert given obj files into the archive. // We always try to remove the archive before creating a new one. (void)FileUtil::Remove(outputFile.c_str()); tool->AppendArg(outputFile); // Note: We do not use tool->inputs here since it is always placed right after executable // the first arg of ar should be the option not input for (const auto& objFile : objFiles) { tool->AppendArg(objFile.filePath); } backendCmds.emplace_back(MakeSingleToolBatch({std::move(tool)})); } std::optional Gnu::SearchClangLibrary(const std::string libName, const std::string libSuffix) { // Clang lib format: // libclang_rt.[-]. std::vector searchPaths = {}; auto libraryPaths = GetLibraryPaths(); auto runtimeLibPath = GetCRuntimeLibraryPath(); (void)searchPaths.insert(searchPaths.end(), driverOptions.librarySearchPaths.begin(), driverOptions.librarySearchPaths.end()); (void)searchPaths.insert(searchPaths.end(), libraryPaths.begin(), libraryPaths.end()); (void)searchPaths.insert(searchPaths.end(), runtimeLibPath.begin(), runtimeLibPath.end()); std::string prefix = "libclang_rt."; auto clangLib = FileUtil::FindFileByName(prefix + libName + libSuffix, searchPaths); if (clangLib.has_value()) { return clangLib; } // Clang asan lib with arch return FileUtil::FindFileByName( prefix + libName + "-" + driverOptions.target.ArchToString() + libSuffix, searchPaths); } void Gnu::HandleSanitizerDependencies(Tool& tool) { for (const auto& arg : {"-lpthread", "-lrt", "-lm", "-ldl", "-lresolv", "-lgcc_s"}) { tool.AppendArg(arg); } } void Gnu::HandleAsanDependencies(Tool& tool, const std::string& cangjieLibPath, const std::string& gccLibPath) { if (driverOptions.outputMode != Cangjie::GlobalOptions::OutputMode::EXECUTABLE) { return; } // General args HandleSanitizerDependencies(tool); tool.AppendArg("--export-dynamic"); // Static library auto asanLib = FileUtil::FindFileByName("libclang_rt-asan.a", {cangjieLibPath}); if (!asanLib.has_value()) { asanLib = SearchClangLibrary("asan", ".a"); } if (asanLib.has_value()) { LinkStaticLibrary(tool, asanLib.value()); return; } // Dynamic library // Clang asanLib = SearchClangLibrary("asan-preinit", ".a"); if (asanLib.has_value()) { // preinit static library LinkStaticLibrary(tool, asanLib.value()); tool.AppendArg("-lclang_rt.asan"); return; } // The gnu lib tool.AppendArg(FileUtil::JoinPath(gccLibPath, "libasan_preinit.o")); tool.AppendArg("-lasan"); } void Gnu::HandleHwasanDependencies(Tool& tool, const std::string& cangjieLibPath) { if (driverOptions.outputMode != Cangjie::GlobalOptions::OutputMode::EXECUTABLE) { return; } // General args HandleSanitizerDependencies(tool); tool.AppendArg("--export-dynamic"); // Static library auto asanLib = FileUtil::FindFileByName("libclang_rt-hwasan.a", {cangjieLibPath}); if (!asanLib.has_value()) { asanLib = SearchClangLibrary("hwasan", ".a"); } if (asanLib.has_value()) { LinkStaticLibrary(tool, asanLib.value()); return; } // Dynamic library asanLib = SearchClangLibrary("hwasan-preinit", ".a"); LinkStaticLibrary(tool, asanLib.value_or("libclang_rt.hwasan-preinit.a")); tool.AppendArg("-lclang_rt.hwasan"); } void Gnu::HandleSanitizer(Tool& tool, const std::string& cangjieLibPath, const std::string& gccLibPath) { if (!driverOptions.EnableSanitizer()) { return; } auto sanitizerPath = FileUtil::JoinPath(cangjieLibPath, driverOptions.SanitizerTypeToShortString()); if (driverOptions.EnableAsan()) { HandleAsanDependencies(tool, sanitizerPath, gccLibPath); } else if (driverOptions.EnableTsan()) { if (driverOptions.outputMode == Cangjie::GlobalOptions::OutputMode::EXECUTABLE) { tool.AppendArg("--export-dynamic"); LinkStaticLibrary(tool, FileUtil::JoinPath(sanitizerPath, "libclang_rt-tsan.a")); HandleSanitizerDependencies(tool); } } else if (driverOptions.EnableHwAsan()) { HandleHwasanDependencies(tool, sanitizerPath); } // The eh frame header is needed for all sanitizer tool.AppendArg("--eh-frame-hdr"); } void Gnu::HandleLLVMLinkOptions( const std::vector& objFiles, const std::string& gccLibPath, Tool& tool, const std::string& cjldScript) { // The order of linkage of object files & libraries is as follows: // // 1. object file(s) we are compiling, which is the current package // 2. object file(s) specified in the cjc command // 3. libraries (-l) specified in the cjc command // 4. built-in static library dependencies // 5. built-in dynamic library direct dependencies // 6. built-in dynamic library indirect dependencies // 7. system library dependencies // // Since object files are main inputs, and they are always linked into the final product, // they are linked first. // // Libraries specified by -l in the cjc command could be cangjie packages // with built-in library dependencies. They must be linked before built-in libraries are linked // or built-in libraries it depends may be discarded by the linker. // // For built-in libraries, static libraries have to be linked before dynamic libraries. To be specific, // core.a must be linked before runtime.so get linked. Runtime requires some symbols defined in // core package, therefore these symbols need to be linked firstly. // // System libraries are linked at the end. First, user libraries may contain symbols whose names are // the same as some hidden symbols in system libraries. Linking system libraries too early may cause // symbol overriding on user symbols. Second, built-in static libraries may use symbols of system // libraries, thus they need to be linked after built-in libraries. // auto cangjieLibPath = FileUtil::JoinPath(FileUtil::JoinPath(driver.cangjieHome, "lib"), driverOptions.GetCangjieLibTargetPathName()); // 1. The -L library path HandleLibrarySearchPaths(tool, cangjieLibPath); // Where to search for gcc & backend libraries if (!gccLibPath.empty()) { tool.AppendArg("-L" + gccLibPath); } if (driverOptions.discardEhFrame && driverOptions.target.os != Triple::OSType::WINDOWS) { tool.AppendArg("-T"); tool.AppendArg(FileUtil::JoinPath(cangjieLibPath, "discard_eh_frame.lds")); } // 2. The cjld.lds and cjstart.o if (driverOptions.target.os == Triple::OSType::WINDOWS) { tool.AppendArg(FileUtil::JoinPath(cangjieLibPath, "section.o")); } else { tool.AppendArg("-T"); tool.AppendArg(FileUtil::JoinPath(cangjieLibPath, cjldScript)); } tool.AppendArg(FileUtil::JoinPath(cangjieLibPath, "cjstart.o")); // 3. Frontend output or Input files // 3.1 Frontend output files (.o) // 3.2 Pass to the linker in the order of input files (.o, .a, -l) SortInputlibraryFileAndAppend(tool, objFiles); // Note that the pgo options must be inserted after those from SortInputlibraryFileAndAppend tool.AppendArgIf(driverOptions.enablePgoInstrGen, "-u", "__llvm_profile_runtime"); tool.AppendArgIf(driverOptions.enablePgoInstrGen || driverOptions.enableCoverage, FileUtil::JoinPath(cangjieLibPath, GetClangRTProfileLibraryName())); HandleSanitizer(tool, cangjieLibPath, gccLibPath); // 4. built-in library dependencies GenerateLinkOptionsOfBuiltinLibs(tool); // 5. system library dependencies required by the backend GenerateLinkOptions(tool); } void Gnu::HandleLibrarySearchPaths(Tool& tool, const std::string& cangjieLibPath) { // Append -L (those specified in command) as search paths tool.AppendArg(PrependToPaths("-L", driverOptions.librarySearchPaths)); // Path to built-in libraries is placed between -L search paths and LIBRARY_PATH so that // 1) user can explicitly specify a library path which contains libcangjie- to force Linker // link against such libraries. // 2) Linker will not be confused if user's LIBRARY_PATH is polluted, for example, LIBRARY_PATH // contains the path to the library folder of a different version of cjc. tool.AppendArgIf(driverOptions.EnableSanitizer(), "-L" + FileUtil::JoinPath(cangjieLibPath, driverOptions.SanitizerTypeToShortString())); tool.AppendArg("-L" + cangjieLibPath); auto cangjieRuntimeLibPath = FileUtil::JoinPath( FileUtil::JoinPath(driver.cangjieHome, "runtime/lib"), driverOptions.GetCangjieLibTargetPathName()); tool.AppendArgIf(driverOptions.EnableSanitizer(), "-L" + FileUtil::JoinPath(cangjieRuntimeLibPath, driverOptions.SanitizerTypeToShortString())); tool.AppendArg("-L" + cangjieRuntimeLibPath); // Append LIBRARY_PATH as search paths tool.AppendArg(PrependToPaths("-L", GetLibraryPaths())); if (driverOptions.IsCrossCompiling() && !driverOptions.target.IsMinGW()) { tool.AppendArg("-rpath-link"); tool.AppendArg(cangjieRuntimeLibPath); } } std::string Gnu::GetEmulation() const { switch (driverOptions.target.arch) { case Triple::ArchType::ARM32: return "armelf_linux_eabi"; case Triple::ArchType::X86_64: if (driverOptions.target.os == Triple::OSType::LINUX) { return "elf_x86_64"; } else if (driverOptions.target.os == Triple::OSType::WINDOWS) { return "i386pep"; } break; case Triple::ArchType::AARCH64: if (driverOptions.target.os == Triple::OSType::LINUX) { return "aarch64linux"; } break; case Triple::ArchType::UNKNOWN: default: break; } return ""; } void Gnu::InitializeLibraryPaths() { // The paths from --toolchain/-B can be used to search system libs, such as Scrt1.o, crti.o, etc. for (auto& libPath : driverOptions.toolChainPaths) { AddCRuntimeLibraryPath(libPath); } AddCRuntimeLibraryPaths(); AddSystemLibraryPaths(); } void Gnu::AddCRuntimeLibraryPaths() { // The order that library paths are added to the list does matter. Libraries are searched in order // and the first appearance of a matching library is chosen. // The rule how default library paths are added is defined as follows: // 1. one of lib64 and lib32 folder that matches the target arch. // 2. root folder, and then '/usr' folder. // For example, in the case of x86_64, the search list may be ["/lib64", "/usr/lib64", "/lib", "/usr/lib"] // Search for system object files. auto archFolderName = GetArchFolderName(driverOptions.target.arch); AddCRuntimeLibraryPath(FileUtil::JoinPath(driverOptions.sysroot, archFolderName)); auto usrPath = FileUtil::JoinPath(driverOptions.sysroot, "usr"); AddCRuntimeLibraryPath(FileUtil::JoinPath(usrPath, archFolderName)); auto libPath = FileUtil::JoinPath(driverOptions.sysroot, "lib"); AddCRuntimeLibraryPath(libPath); auto usrLibPath = FileUtil::JoinPath(usrPath, "lib"); AddCRuntimeLibraryPath(usrLibPath); // Some libraries can be found in the compatible toolchain folder auto directories = FileUtil::GetDirectories(libPath); for (const auto& dir : directories) { if (Triple::IsPossibleMatchingTripleName(driverOptions.target, dir.name)) { AddCRuntimeLibraryPath(dir.path); } } auto usrDirectories = FileUtil::GetDirectories(usrLibPath); for (const auto& dir : usrDirectories) { if (Triple::IsPossibleMatchingTripleName(driverOptions.target, dir.name)) { AddCRuntimeLibraryPath(dir.path); } } } void Gnu::AddSystemLibraryPaths() { AddLibraryPaths(driverOptions.environment.libraryPaths); } bool Gnu::ProcessGeneration(std::vector& objFiles) { size_t codegenOutputBCNum = 0; for (auto objName : objFiles) { codegenOutputBCNum += objName.isForeignInput ? 0 : 1; } if (codegenOutputBCNum == 1) { // The '--output-type=staticlib', one more step to go, create an archive // file consisting of all generated object files if (driverOptions.outputMode == GlobalOptions::OutputMode::STATIC_LIB) { GenerateArchiveTool(objFiles); return true; } else { return GenerateLinking(objFiles); } } auto tool = std::make_unique(ldPath, ToolType::BACKEND, driverOptions.environment.allVariables); // Recover the 'outputFile' from 'path/0-xx.o' to 'path/xx.o' std::string outputFile = objFiles[0].filePath; std::string dirPath = FileUtil::GetDirPath(outputFile); std::string fileName = FileUtil::GetFileName(outputFile).substr(2); // 2 is the length of the prefix "0-" outputFile = FileUtil::JoinPath(dirPath, fileName); tool->AppendArg("-o", outputFile); tool->AppendArg("-r"); std::vector processedObjFiles{}; for (auto objName : objFiles) { if (objName.isForeignInput) { processedObjFiles.emplace_back(objName); } else { tool->AppendArg(objName.filePath); } } backendCmds.emplace_back(MakeSingleToolBatch({std::move(tool)})); auto outputDir = FileUtil::GetAbsPath(FileUtil::GetDirPath(outputFile)); CJC_ASSERT(outputDir.has_value()); auto name = FileUtil::JoinPath(outputDir.value(), FileUtil::GetFileNameWithoutExtension(fileName) + ".__symbols"); std::ofstream file(name); CJC_ASSERT(file.is_open()); for (const auto& str : driverOptions.symbolsNeedLocalized) { file << str << "\n"; } file.close(); if (!driverOptions.symbolsNeedLocalized.empty()) { auto changeSymVis = std::make_unique( objcopyPath, ToolType::BACKEND, driverOptions.environment.allVariables); changeSymVis->AppendArg(outputFile); changeSymVis->AppendArg("--localize-symbols=" + name); backendCmds.emplace_back(MakeSingleToolBatch({std::move(changeSymVis)})); } TempFileInfo fileInfo = {FileUtil::GetFileNameWithoutExtension(outputFile), outputFile, outputFile, true, false}; processedObjFiles.insert(processedObjFiles.begin(), fileInfo); objFiles = std::move(processedObjFiles); // If aggressiveParallelCompile is enabled, we combined 0-xx.o, 1-xx.o, etc. to xx.o (aka 'outputFile') by using // 'ld', we need to copy xx.o to cache. if (driverOptions.aggressiveParallelCompile.value_or(1) > 1) { std::string destFile = driverOptions.GetHashedObjFileName(FileUtil::GetFileNameWithoutExtension(outputFile)) + ".o"; auto toolOfCacheCopy = std::make_unique("CacheCopy", ToolType::INTERNAL_IMPLEMENTED, driverOptions.environment.allVariables); toolOfCacheCopy->AppendArg(outputFile); toolOfCacheCopy->AppendArg(destFile); backendCmds.emplace_back(MakeSingleToolBatch({std::move(toolOfCacheCopy)})); } // The '--output-type=staticlib', one more step to go, create an archive file consisting of all generated object files if (driverOptions.outputMode == GlobalOptions::OutputMode::STATIC_LIB) { GenerateArchiveTool(objFiles); return true; } else { return GenerateLinking(objFiles); } } bool Gnu::GenerateLinking(const std::vector& objFiles) { // Different linking mode requires different gcc crt files auto gccCrtFilePair = GetGccCrtFilePair(); std::string gccLibPath = GenerateGCCLibPath(gccCrtFilePair); if (driverOptions.enableVerbose) { Infoln("selected gcc lib path: " + gccLibPath); } GenerateLinkingTool(objFiles, gccLibPath, gccCrtFilePair); return true; } cangjie_compiler-1.0.7/src/Driver/Toolchains/Gnu.h000066400000000000000000000064731510705540100220500ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Gnu class. */ #ifndef CANGJIE_DRIVER_TOOLCHAIN_GNU_H #define CANGJIE_DRIVER_TOOLCHAIN_GNU_H #include "cangjie/Driver/Backend/Backend.h" #include "cangjie/Driver/Driver.h" namespace Cangjie { class ToolChain; class Gnu : public ToolChain { public: Gnu(const Cangjie::Driver& driver, const DriverOptions& driverOptions, std::vector& backendCmds) : ToolChain(driver, driverOptions, backendCmds) {}; ~Gnu() override = default; protected: std::string ldPath; std::string arPath; std::string objcopyPath; const std::pair gccExecCrtFilePair = std::make_pair("crtbegin.o", "crtend.o"); const std::pair gccSharedCrtFilePair = std::make_pair("crtbeginS.o", "crtendS.o"); std::string GetGccLibFile(const std::string& filename, const std::string& gccLibPath) const; // Get the executable formats of target system for the linker, // and the linker will emulate the target to do cross-compilation. virtual std::string GetEmulation() const; // Libc uses crtbegin.o/crtend.o to find the start of the constructors/destructors. virtual std::pair GetGccCrtFilePair() const; bool PrepareDependencyPath() override; bool ProcessGeneration(std::vector& objFiles) override; virtual std::string GenerateGCCLibPath(const std::pair& gccCrtFilePair) const; virtual void GenerateArchiveTool(const std::vector& objFiles); // utility method to find clang library, used to find asan and libfuzzer // clang library format: libclang_rt.[-]. std::optional SearchClangLibrary(const std::string libName, const std::string libSuffix); void HandleSanitizer(Tool& tool, const std::string& cangjieLibPath, const std::string& gccLibPath); virtual void HandleSanitizerDependencies(Tool& tool); void HandleLLVMLinkOptions(const std::vector& objFiles, const std::string& gccLibPath, Tool& tool, const std::string& cjldScript); virtual void HandleLibrarySearchPaths(Tool& tool, const std::string& cangjieLibPath); void InitializeLibraryPaths() override; virtual void AddCRuntimeLibraryPaths(); // Gather library paths from LIBRARY_PATH and compiler guesses. virtual void AddSystemLibraryPaths(); virtual void GenerateLinkOptions(Tool& tool) { (void)tool; } virtual void GenerateLinkingTool(const std::vector& objFiles, const std::string& gccLibPath, const std::pair& gccCrtFilePair) { (void)objFiles; (void)gccLibPath; (void)gccCrtFilePair; } virtual bool GenerateLinking(const std::vector& objFiles); private: void HandleAsanDependencies(Tool& tool, const std::string& cangjieLibPath, const std::string& gccLibPath); void HandleHwasanDependencies(Tool& tool, const std::string& cangjieLibPath); }; } // namespace Cangjie #endif // CANGJIE_DRIVER_TOOLCHAIN_GNU_H cangjie_compiler-1.0.7/src/Driver/Toolchains/MachO.cpp000066400000000000000000000351141510705540100226330ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the MachO ToolChain class. */ #include "Toolchains/MachO.h" #include #include "cangjie/Driver/TempFileManager.h" #include "cangjie/Driver/Toolchains/GCCPathScanner.h" #include "cangjie/Driver/Toolchains/ToolChain.h" #include "cangjie/Driver/Utils.h" namespace { const std::string LINK_PREFIX = "-l"; const std::string STATIC_LIB_EXTEBSION = ".a"; const std::map LLVM_LTO_CSTD_FFI_OPTION_MAP = { #define CJNATIVE_LTO_CSTD_FFI_OPTIONS(STATILIB, FFILIB) {STATILIB, FFILIB}, #include "Toolchains/BackendOptions.inc" #undef CJNATIVE_LTO_CSTD_FFI_OPTIONS }; }; // namespace using namespace Cangjie; using namespace Cangjie::Triple; bool MachO::PrepareDependencyPath() { if ((arPath = FindUserToolPath(g_toolList.at(ToolID::AR).name)).empty()) { return false; } if ((ldPath = FindCangjieLLVMToolPath(g_toolList.at(ToolID::LD64_LLD).name)).empty()) { return false; } if ((dsymutilPath = FindUserToolPath(g_toolList.at(ToolID::DSYMUTIL).name)).empty()) { return false; } if ((stripPath = FindUserToolPath(g_toolList.at(ToolID::STRIP).name)).empty()) { return false; } return true; } void MachO::GenerateArchiveTool(const std::vector& objFiles) { auto tool = std::make_unique(arPath, ToolType::BACKEND, driverOptions.environment.allVariables); // The c for no warn if the library had to be created // The r for replacing existing or insert new file(s) into the archive tool->AppendArg("cr"); // When we reach here, we must be at the final phase of the compilation, // which means that it is the final output. TempFileInfo fileInfo = TempFileManager::Instance().CreateNewFileInfo(objFiles[0], TempFileKind::O_STATICLIB); std::string outputFile = fileInfo.filePath; // If archive exists, ar attempts to insert given obj files into the archive. // We always try to remove the archive before creating a new one. (void)FileUtil::Remove(outputFile.c_str()); tool->AppendArg(outputFile); // Note: We do not use tool->inputs here since it is always placed right after executable // the first arg of ar should be the option not input for (const auto& objFile : objFiles) { tool->AppendArg(objFile.filePath); } backendCmds.emplace_back(MakeSingleToolBatch({std::move(tool)})); } // Have no idea how to use linker script on MacOS currently. void MachO::HandleLLVMLinkOptions(const std::vector& objFiles, Tool& tool) { // The order of linkage of object files & libraries is as follows: // // 1. object file(s) we are compiling, which is the current package // 2. object file(s) specified in the cjc command // 3. libraries (-l) specified in the cjc command // 4. built-in static library dependencies // 5. built-in dynamic library direct dependencies // 6. built-in dynamic library indirect dependencies // 7. system library dependencies // // Since object files are main inputs, and they are always linked into the final product, // they are linked first. // // Libraries specified by -l in the cjc command could be cangjie packages // with built-in library dependencies. They must be linked before built-in libraries are linked // or built-in libraries it depends may be discarded by the linker. // // For built-in libraries, static libraries have to be linked before dynamic libraries. To be specific, // core.a must be linked before runtime.so get linked. Runtime requires some symbols defined in // core package, therefore these symbols need to be linked firstly. // // System libraries are linked at the end. First, user libraries may contain symbols whose names are // the same as some hidden symbols in system libraries. Linking system libraries too early may cause // symbol overriding on user symbols. Second, built-in static libraries may use symbols of system // libraries, thus they need to be linked after built-in libraries. // auto cangjieLibPath = FileUtil::JoinPath(FileUtil::JoinPath(driver.cangjieHome, "lib"), driverOptions.GetCangjieLibTargetPathName()); // 1. The -L library path HandleLibrarySearchPaths(tool, cangjieLibPath); // 2. cjstart.o tool.AppendArg(FileUtil::JoinPath(cangjieLibPath, "section.o")); tool.AppendArg(FileUtil::JoinPath(cangjieLibPath, "cjstart.o")); // 3. Frontend output or input files // 3.1 Frontend output files (.o) // 3.2 Pass to the linker in the order of input files (.o, .a, -l) SortInputlibraryFileAndAppend(tool, objFiles); // Note that the pgo options must be inserted after those from SortInputlibraryFileAndAppend tool.AppendArgIf(driverOptions.enablePgoInstrGen, "-u", "___llvm_profile_runtime"); tool.AppendArgIf(driverOptions.enablePgoInstrGen || driverOptions.enableCoverage, FileUtil::JoinPath(cangjieLibPath, GetClangRTProfileLibraryName())); // 4. The built-in library dependencies GenerateLinkOptionsOfBuiltinLibs(tool); // 5. System library dependencies required by the backend GenerateLinkOptions(tool); } void MachO::HandleLibrarySearchPaths(Tool& tool, const std::string& cangjieLibPath) { // Append -L (those specified in command) as search paths tool.AppendArg(PrependToPaths("-L", driverOptions.librarySearchPaths)); // path to built-in libraries is placed between -L search paths and LIBRARY_PATH so that // 1) user can explicitly specify a library path which contains libcangjie- to force Linker // link against such libraries. // 2) Linker will not be confused if user's LIBRARY_PATH is polluted, for example, LIBRARY_PATH // contains the path to the library folder of a different version of cjc. auto cangjieRuntimeLibPath = FileUtil::JoinPath( FileUtil::JoinPath(driver.cangjieHome, "runtime/lib"), driverOptions.GetCangjieLibTargetPathName()); // Different from Gnu toolchain, we pass runtime library path first here. ld64 does not support -l: // passing style, thus we use -l for dynamic linkage and pass full path for static libraries (.a files). // Passing runtime library path first ensures -l always find dynamic libraries before the static one. tool.AppendArg("-L" + cangjieRuntimeLibPath); tool.AppendArg("-L" + cangjieLibPath); // Append LIBRARY_PATH as search paths tool.AppendArg(PrependToPaths("-L", GetLibraryPaths())); if (driverOptions.IsCrossCompiling()) { tool.AppendArg("-L"); tool.AppendArg(cangjieRuntimeLibPath); } } std::string MachO::GetTargetArchString() const { switch (driverOptions.target.arch) { case ArchType::AARCH64: return "arm64"; case ArchType::X86_64: return "x86_64"; case ArchType::UNKNOWN: default: return ""; } } void MachO::InitializeLibraryPaths() { // The paths from --toolchain/-B can be used to search system libs, such as Scrt1.o, crti.o, etc. for (auto& libPath : driverOptions.toolChainPaths) { AddCRuntimeLibraryPath(libPath); } AddCRuntimeLibraryPaths(); AddSystemLibraryPaths(); } void MachO::AddCRuntimeLibraryPaths() { auto archFolderName = GetArchFolderName(driverOptions.target.arch); AddCRuntimeLibraryPath(FileUtil::JoinPath(driverOptions.sysroot, archFolderName)); auto usrPath = FileUtil::JoinPath(driverOptions.sysroot, "usr"); AddCRuntimeLibraryPath(FileUtil::JoinPath(usrPath, archFolderName)); auto usrLibPath = FileUtil::JoinPath(usrPath, "lib"); AddCRuntimeLibraryPath(usrLibPath); auto usrDirectories = FileUtil::GetDirectories(usrLibPath); for (const auto& dir : usrDirectories) { if (Triple::IsPossibleMatchingTripleName(driverOptions.target, dir.name)) { AddCRuntimeLibraryPath(dir.path); } } } void MachO::AddSystemLibraryPaths() { AddLibraryPaths(driverOptions.environment.libraryPaths); } bool MachO::ProcessGeneration(std::vector& objFiles) { // The '--output-type=staticlib', one more step to go, create an archive file consisting of all generated object files if (driverOptions.outputMode == GlobalOptions::OutputMode::STATIC_LIB) { GenerateArchiveTool(objFiles); return true; } auto outputFile = GenerateLinking(objFiles); if (driverOptions.enableCompileDebug) { GenerateDebugSymbolFile(outputFile); } if (driverOptions.stripSymbolTable) { GenerateStripSymbolFile(outputFile); } return true; } TempFileInfo MachO::GenerateLinking(const std::vector& objFiles) { std::optional darwinSDKVersion = GetDarwinSDKVersion(driverOptions.sysroot); if (driverOptions.enableVerbose) { Infoln("selected Darwin SDK path: " + driverOptions.sysroot + " (SDK Version: " + darwinSDKVersion.value_or("N/A") + ")"); } // If SDK version cannot be detected, default version 12 is used here. return GenerateLinkingTool(objFiles, darwinSDKVersion.value_or("12")); } void MachO::GenerateDebugSymbolFile(const TempFileInfo& binaryFile) { auto tool = std::make_unique(dsymutilPath, ToolType::OTHER, driverOptions.environment.allVariables); tool->AppendArg(binaryFile.filePath); tool->AppendArg("-o", binaryFile.filePath + ".dSYM"); backendCmds.emplace_back(MakeSingleToolBatch({std::move(tool)})); auto codesignTool = std::make_unique(g_toolList.at(ToolID::CODESIGN).name, ToolType::OTHER, driverOptions.environment.allVariables); codesignTool->AppendArg("-s", "-", "-f"); codesignTool->AppendArg("--entitlements", FileUtil::JoinPath(driver.cangjieHome, "lib/entitlement.plist")); codesignTool->AppendArg(binaryFile.filePath); backendCmds.emplace_back(MakeSingleToolBatch({std::move(codesignTool)})); } void MachO::GenerateStripSymbolFile(const TempFileInfo& binaryFile) { auto tool = std::make_unique(stripPath, ToolType::BACKEND, driverOptions.environment.allVariables); switch (driverOptions.outputMode) { case GlobalOptions::OutputMode::EXECUTABLE: tool->AppendArg("-u", "-r"); break; case GlobalOptions::OutputMode::SHARED_LIB: tool->AppendArg("-x"); break; case GlobalOptions::OutputMode::STATIC_LIB: default: break; } std::vector binaryFiles; binaryFiles.emplace_back(binaryFile); auto newBinaryFile = GetOutputFileInfo(binaryFiles); tool->AppendArg(binaryFile.filePath); tool->AppendArg("-o"); tool->AppendArg(newBinaryFile.filePath); backendCmds.emplace_back(MakeSingleToolBatch({std::move(tool)})); } void MachO::GenerateLinkOptionsOfBuiltinLibsForStaticLink(Tool& tool) const { std::set dynamicLibraries; std::set staticLibraries; std::set ltoBuiltInDependencies; const std::function appendStaticLibsToTool = [this, &dynamicLibraries, &staticLibraries, <oBuiltInDependencies](const std::string& cjoFileName) { auto staticLib = FileUtil::ConvertFilenameToLibCangjieFormat(cjoFileName, STATIC_LIB_EXTEBSION); if (ALWAYS_DYNAMIC_LINK_STD_LIBRARIES.find(staticLib) != ALWAYS_DYNAMIC_LINK_STD_LIBRARIES.end()) { dynamicLibraries.emplace(LINK_PREFIX + FileUtil::ConvertFilenameToLibCangjieBaseFormat(cjoFileName)); return; } if (driverOptions.IsLTOEnabled()) { auto found = LLVM_LTO_CSTD_FFI_OPTION_MAP.find(staticLib); if (found != LLVM_LTO_CSTD_FFI_OPTION_MAP.end()) { staticLibraries.emplace(LINK_PREFIX + found->second); } ltoBuiltInDependencies.emplace(FileUtil::ConvertFilenameToLtoLibCangjieFormat(cjoFileName)); } else { // In the case of static linkage, we pass full path of static library to the linker, otherwise // we use -l passing style. auto cangjieLibPath = FileUtil::JoinPath(FileUtil::JoinPath(driver.cangjieHome, "lib"), driverOptions.GetCangjieLibTargetPathName()); // Search sanitizer path if (driverOptions.sanitizerType != GlobalOptions::SanitizerType::NONE) { cangjieLibPath = FileUtil::JoinPath(cangjieLibPath, driverOptions.SanitizerTypeToShortString()); } std::string libLinkOpt = driverOptions.linkStaticStd.value_or(true) ? FileUtil::JoinPath(cangjieLibPath, staticLib) : LINK_PREFIX + FileUtil::ConvertFilenameToLibCangjieBaseFormat(cjoFileName); staticLibraries.emplace(libLinkOpt); } CheckOtherDependeniesOfStaticLib(staticLib, dynamicLibraries, staticLibraries); }; ForEachBuiltinDependencies(driverOptions.directBuiltinDependencies, appendStaticLibsToTool); ForEachBuiltinDependencies(driverOptions.indirectBuiltinDependencies, appendStaticLibsToTool); if (driverOptions.IsLTOEnabled()) { for (const auto& bcFile : ltoBuiltInDependencies) { tool.AppendArg(bcFile); } } // Static libraries are not sorted, thus we need to group them or symbols may be discarded by the linker. for (const auto& other : staticLibraries) { tool.AppendArg(other); } for (const auto& lib : dynamicLibraries) { tool.AppendArg(lib); } } void MachO::GenerateLinkOptionsOfBuiltinLibsForDyLink(Tool& tool) const { const std::function appendDyLibsToTool = [&tool](const std::string& cjoFileName) { tool.AppendArg(LINK_PREFIX + FileUtil::ConvertFilenameToLibCangjieBaseFormat(cjoFileName)); }; ForEachBuiltinDependencies(driverOptions.directBuiltinDependencies, appendDyLibsToTool); // Link indirect dependent dynamic libraries surrounded by `--as-needed` and `--no-as-needed`. // For the current implementation of generic types of cangjie, some symbols may be shared across // libraries, which means that an indirect dependency may be a direct dependency. Thus, we must link // indirect dependencies here. Indirect dependencies are passed after `--as-needed` options // so unnecessary dependencies will be discarded by the linker. ForEachBuiltinDependencies(driverOptions.indirectBuiltinDependencies, appendDyLibsToTool); } cangjie_compiler-1.0.7/src/Driver/Toolchains/MachO.h000066400000000000000000000050651510705540100223020ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the MachO class. */ #ifndef CANGJIE_DRIVER_TOOLCHAIN_MACHO_H #define CANGJIE_DRIVER_TOOLCHAIN_MACHO_H #include "cangjie/Driver/Backend/Backend.h" #include "cangjie/Driver/Driver.h" namespace Cangjie { class MachO : public ToolChain { public: MachO(const Cangjie::Driver& driver, const DriverOptions& driverOptions, std::vector& backendCmds) : ToolChain(driver, driverOptions, backendCmds) {}; ~MachO() override = default; bool ProcessGeneration(std::vector& objFiles) override; bool PrepareDependencyPath() override; void InitializeLibraryPaths() override; protected: std::string ldPath; std::string arPath; std::string dsymutilPath; std::string stripPath; std::string GetSharedLibraryExtension() const override { return ".dylib"; } // Get the target architecture string. It is passed to the linker. virtual std::string GetTargetArchString() const; virtual void GenerateArchiveTool(const std::vector& objFiles); void HandleLLVMLinkOptions(const std::vector& objFiles, Tool& tool); virtual void HandleLibrarySearchPaths(Tool& tool, const std::string& cangjieLibPath); virtual void AddCRuntimeLibraryPaths(); // Gather library paths from LIBRARY_PATH and compiler guesses. virtual void AddSystemLibraryPaths(); virtual void GenerateLinkOptions([[maybe_unused]] Tool& tool) {}; virtual TempFileInfo GenerateLinkingTool([[maybe_unused]] const std::vector& objFiles, [[maybe_unused]] const std::string& darwinSDKVersion) { return TempFileInfo{}; }; virtual TempFileInfo GenerateLinking(const std::vector& objFiles); virtual void GenerateDebugSymbolFile(const TempFileInfo& binaryFile); void GenerateStripSymbolFile(const TempFileInfo& binaryFile); // Generate the static link options of built-in libraries except 'std-ast'. // The 'std-ast' library is dynamically linked by default. void GenerateLinkOptionsOfBuiltinLibsForStaticLink(Tool& tool) const override; // Generate the dynamic link options of built-in libraries. void GenerateLinkOptionsOfBuiltinLibsForDyLink(Tool& tool) const override; }; } // namespace Cangjie #endif // CANGJIE_DRIVER_TOOLCHAIN_MACHO_H cangjie_compiler-1.0.7/src/Driver/Toolchains/ToolChain.cpp000066400000000000000000000363661510705540100235360ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the ToolChain base class. */ #include "cangjie/Driver/Toolchains/ToolChain.h" #include "cangjie/Driver/Utils.h" #include "cangjie/Driver/TempFileManager.h" namespace { const std::string LINK_PREFIX = "-l:"; const std::string STATIC_LIB_EXTEBSION = ".a"; const std::map LLVM_LTO_CSTD_FFI_OPTION_MAP = { #define CJNATIVE_LTO_CSTD_FFI_OPTIONS(STATILIB, FFILIB) {STATILIB, FFILIB}, #include "Toolchains/BackendOptions.inc" #undef CJNATIVE_LTO_CSTD_FFI_OPTIONS }; std::optional> GetElementFromOrderVector( std::vector>& orderVector, const std::string& name) { std::optional> result = {}; for (auto it = orderVector.begin(); it < orderVector.end(); ++it) { std::tuple tuple = *it; if (std::get<0>(tuple) == name) { (void)orderVector.erase(it); result = tuple; break; } } return result; } }; // namespace using namespace Cangjie; void ToolChain::GenerateLinkOptionsOfBuiltinLibs(Tool& tool) const { bool notOutputDylib = driverOptions.outputMode != GlobalOptions::OutputMode::SHARED_LIB; // When user compiling to a shared library, standard library must be dynamic linked or there might be multiple // versions of standard library exists in different dynamic libraries. A user could still specify --static-std // to reproduct the problem. Here we only ensure the default behavior of cjc is correct. bool isStaticLink = driverOptions.linkStaticStd.value_or(notOutputDylib); if (isStaticLink) { return GenerateLinkOptionsOfBuiltinLibsForStaticLink(tool); } else { return GenerateLinkOptionsOfBuiltinLibsForDyLink(tool); } } void ToolChain::GenerateLinkOptionsOfBuiltinLibsForStaticLink(Tool& tool) const { std::set dynamicLibraries; std::set staticLibraries; std::set ltoBuiltInDependencies; std::unordered_set dyDependencies; const std::function&)> getDyDependencies = [this, &dyDependencies](const std::unordered_set& dependencies) { for (auto& cjoFileName : dependencies) { auto staticLib = FileUtil::ConvertFilenameToLibCangjieFormat(cjoFileName, STATIC_LIB_EXTEBSION); if (ALWAYS_DYNAMIC_LINK_STD_LIBRARIES.find(staticLib) != ALWAYS_DYNAMIC_LINK_STD_LIBRARIES.end()) { dyDependencies.emplace(staticLib); if (driverOptions.target.os != Triple::OSType::WINDOWS) { dyDependencies.insert(ALWAYS_DYNAMIC_LINK_STD_LIBRARIES.at(staticLib).begin(), ALWAYS_DYNAMIC_LINK_STD_LIBRARIES.at(staticLib).end()); } } } }; getDyDependencies(driverOptions.directBuiltinDependencies); getDyDependencies(driverOptions.indirectBuiltinDependencies); const std::function appendStaticLibsToTool = [this, &dynamicLibraries, &staticLibraries, <oBuiltInDependencies, &dyDependencies] (const std::string& cjoFileName) { auto staticLib = FileUtil::ConvertFilenameToLibCangjieFormat(cjoFileName, STATIC_LIB_EXTEBSION); if (dyDependencies.find(staticLib) != dyDependencies.end() && !driverOptions.linkStatic) { dynamicLibraries.emplace(LINK_PREFIX + FileUtil::ConvertFilenameToLibCangjieFormat(cjoFileName, GetSharedLibraryExtension())); return; } if (driverOptions.IsLTOEnabled()) { auto found = LLVM_LTO_CSTD_FFI_OPTION_MAP.find(staticLib); if (found != LLVM_LTO_CSTD_FFI_OPTION_MAP.end()) { staticLibraries.emplace(LINK_PREFIX + found->second); } ltoBuiltInDependencies.emplace(FileUtil::ConvertFilenameToLtoLibCangjieFormat(cjoFileName)); } else { // search sanitizer path if (driverOptions.sanitizerType != GlobalOptions::SanitizerType::NONE) { auto cangjieLibPath = FileUtil::JoinPath( FileUtil::JoinPath(FileUtil::JoinPath(driver.cangjieHome, "lib"), driverOptions.GetCangjieLibTargetPathName()), driverOptions.SanitizerTypeToShortString()); staticLibraries.emplace(FileUtil::JoinPath(cangjieLibPath, staticLib)); return; } staticLibraries.emplace(LINK_PREFIX + staticLib); } CheckOtherDependeniesOfStaticLib(staticLib, dynamicLibraries, staticLibraries); }; ForEachBuiltinDependencies(driverOptions.directBuiltinDependencies, appendStaticLibsToTool); ForEachBuiltinDependencies(driverOptions.indirectBuiltinDependencies, appendStaticLibsToTool); if (driverOptions.IsLTOEnabled()) { for (const auto& bcFile : ltoBuiltInDependencies) { tool.AppendArg(bcFile); } } // Static libraries are not sorted, thus we need to group them or symbols may be discarded by the linker. tool.AppendArg("--start-group"); for (const auto& other : staticLibraries) { tool.AppendArg(other); } tool.AppendArg("--end-group"); for (const auto& lib : dynamicLibraries) { tool.AppendArg(lib); } } void ToolChain::GenerateLinkOptionsOfBuiltinLibsForDyLink(Tool& tool) const { const std::function appendDyLibsToTool = [this, &tool](const std::string& cjoFileName) { tool.AppendArg(LINK_PREFIX + FileUtil::ConvertFilenameToLibCangjieFormat(cjoFileName, GetSharedLibraryExtension())); }; ForEachBuiltinDependencies(driverOptions.directBuiltinDependencies, appendDyLibsToTool); // Link indirect dependent dynamic libraries surrounded by `--as-needed` and `--no-as-needed`. // For the current implementation of generic types of cangjie, some symbols may be shared across // libraries, which means that an indirect dependency may be an direct dependency. Thus we must link // indirect dependencies here. Indirect dependencies are passed after `--as-needed` options // so unnecessary dependencies will be discarded by the linker. tool.AppendArgIf(!driverOptions.target.IsMinGW(), "--as-needed"); ForEachBuiltinDependencies(driverOptions.indirectBuiltinDependencies, appendDyLibsToTool); tool.AppendArgIf(!driverOptions.target.IsMinGW(), "--no-as-needed"); } void ToolChain::CheckOtherDependeniesOfStaticLib( const std::string& libName, std::set& dynamicLibraries, std::set& otherLibs) const { if (libName == "libcangjie-std-net.a") { if (driverOptions.target.IsMinGW()) { otherLibs.emplace("-l:libws2_32.a"); } } else if (libName == "cangjie-dynamicLoader-openssl.a") { if (!driverOptions.target.IsMinGW()) { dynamicLibraries.emplace("-ldl"); } if (driverOptions.target.IsMinGW()) { dynamicLibraries.emplace("-lcrypt32"); } } else if (libName == "libcangjie-std-ffi.python.a") { // From Glibc 2.34 libdl is provided as a part of libc and libdl.so is no longer provided as a // separated library. However, a dummy libdl.a is still provided for backwards compatibility. // Here we have to also link against `libdl` instead of `libdl.so` for compatibility. dynamicLibraries.emplace("-ldl"); } else if (libName == "libcangjie-std-ast.a") { otherLibs.emplace("-l:libcangjie-std-astFFI.a"); // std-ast relies on unwind symbols, which supplied by clang or gcc runtime library. Linking some versions of // gcc.a before clang_rt-builtins causes multiple definitions, thus we need to link clang_rt-builtins first. if (driverOptions.target.arch != Triple::ArchType::AARCH64 && driverOptions.target.os == Triple::OSType::LINUX && driverOptions.target.env != Triple::Environment::OHOS) { otherLibs.emplace("-lclang_rt-builtins"); } if (driverOptions.target.os == Triple::OSType::LINUX && driverOptions.target.env == Triple::Environment::ANDROID) { otherLibs.emplace("-lc++"); otherLibs.emplace("-lunwind"); } else { dynamicLibraries.emplace("-lgcc_s"); } } } void ToolChain::AppendObjectsFromCompiled(Tool& tool, const std::vector& objFiles, std::vector>& inputOrderTuples) { std::vector> itemTuples; itemTuples.insert(itemTuples.begin(), driverOptions.inputFileOrder.begin(), driverOptions.inputFileOrder.end()); for (const auto& objFile : objFiles) { if (objFile.isFrontendOutput) { tool.AppendArg(objFile.filePath); } else { auto found = GetElementFromOrderVector(itemTuples, objFile.rawPath); if (found.has_value()) { inputOrderTuples.emplace_back(std::make_tuple(objFile.filePath, std::get<1>(found.value()))); } else { InternalError("Input file not in inputFileOrder."); } } } } void ToolChain::AppendObjectsFromInput(std::vector>& inputOrderTuples) { std::vector> itemTuples; itemTuples.insert(itemTuples.begin(), driverOptions.inputFileOrder.begin(), driverOptions.inputFileOrder.end()); for (const std::string& obj : driverOptions.inputObjs) { auto found = GetElementFromOrderVector(itemTuples, obj); if (found.has_value()) { inputOrderTuples.emplace_back(found.value()); } else { InternalError("Input object file not in inputFileOrder."); } } } void ToolChain::AppendLibrariesFromInput(std::vector>& inputOrderTuples) { for (const std::tuple& library : driverOptions.inputLibraryOrder) { inputOrderTuples.emplace_back(std::make_tuple("-l" + std::get<0>(library), std::get<1>(library))); } } void ToolChain::AppendLinkOptionFromInput(std::vector>& inputOrderTuples) { for (const std::tuple& optionTuple : driverOptions.inputLinkOptionOrder) { std::string option = std::get<0>(optionTuple); if (!option.empty()) { inputOrderTuples.emplace_back(std::make_tuple(option, std::get<1>(optionTuple))); } } } void ToolChain::AppendLinkOptionsFromInput(std::vector>& inputOrderTuples) { for (const std::tuple& optionTuple : driverOptions.inputLinkOptionsOrder) { std::string option = std::get<0>(optionTuple); auto splitArgs = Utils::SplitString(option, " "); for (const auto& arg : splitArgs) { if (!arg.empty()) { inputOrderTuples.emplace_back(std::make_tuple(arg, std::get<1>(optionTuple))); } } } } void ToolChain::SortInputlibraryFileAndAppend(Tool& tool, const std::vector& objFiles) { std::vector> inputOrderTuples; // Object file compiled from the cangjie file AppendObjectsFromCompiled(tool, objFiles, inputOrderTuples); AppendObjectsFromInput(inputOrderTuples); AppendLibrariesFromInput(inputOrderTuples); AppendLinkOptionFromInput(inputOrderTuples); AppendLinkOptionsFromInput(inputOrderTuples); // need to maintain front-to-back relative position std::stable_sort(inputOrderTuples.begin(), inputOrderTuples.end(), [](auto& a, auto& b) { return std::get<1>(a) < std::get<1>(b); }); for (auto& tuple : inputOrderTuples) { tool.AppendArg(std::get<0>(tuple)); } } TempFileInfo ToolChain::GetOutputFileInfo(const std::vector& objFiles) const { TempFileKind fileKind; if (driverOptions.compileMacroPackage) { fileKind = TempFileKind::O_MACRO; } else { fileKind = driverOptions.outputMode == GlobalOptions::OutputMode::SHARED_LIB ? TempFileKind::O_DYLIB : TempFileKind::O_EXE; } return TempFileManager::Instance().CreateNewFileInfo(objFiles[0], fileKind); } std::string ToolChain::GetArchFolderName(const Triple::ArchType& arch) const { switch (arch) { case Triple::ArchType::ARM32: return "lib"; case Triple::ArchType::X86_64: case Triple::ArchType::AARCH64: return "lib64"; case Triple::ArchType::UNKNOWN: default: return ""; } } std::vector ToolChain::ComputeLibPaths() const { return {}; } std::vector ToolChain::ComputeBinPaths() const { std::vector result; result.emplace_back(FileUtil::JoinPath(driverOptions.sysroot, "bin")); result.emplace_back(FileUtil::JoinPath(driverOptions.sysroot, "usr/bin")); return result; } void ToolChain::GenerateRuntimePath(Tool& tool) { // driver option has already warned the user, so here just ignore it // effective for useRuntimeRpath and sanitizerEnableRpath if (driverOptions.IsCrossCompiling()) { return; } if (driverOptions.useRuntimeRpath) { auto cangjieRuntimeLibPath = FileUtil::JoinPath( FileUtil::JoinPath(driver.cangjieHome, "runtime/lib"), driverOptions.GetCangjieLibTargetPathName()); tool.AppendArg("-rpath", cangjieRuntimeLibPath); } else if (driverOptions.sanitizerEnableRpath) { auto sanitizerRuntimePath = FileUtil::JoinPath(FileUtil::JoinPath(FileUtil::JoinPath( driver.cangjieHome, "runtime/lib"), driverOptions.GetCangjieLibTargetPathName()), driverOptions.SanitizerTypeToShortString()); // --sanitize-set-rpath needs rpath, not runpath tool.AppendArg("--disable-new-dtags"); tool.AppendArg("-rpath", sanitizerRuntimePath); } } std::string ToolChain::FindCangjieLLVMToolPath(const std::string& toolName) const { std::string toolPath = FindToolPath( toolName, std::vector{FileUtil::JoinPath(driver.cangjieHome, "third_party/llvm/bin")}); if (toolPath.empty()) { Errorf("not found `%s` in search paths. Your Cangjie installation might be broken.\n", toolName.c_str()); } return toolPath; } std::string ToolChain::FindUserToolPath(const std::string& toolName) const { // ComputeBinPaths makes some guesses on --sysroot option for protential available search paths. It has lower // precedence than toolchain paths so users may always use -B to specifiy which path to search first. std::string toolPath = FindToolPath(toolName, driverOptions.toolChainPaths, driverOptions.customizedSysroot ? ComputeBinPaths() : std::vector{}, driverOptions.environment.paths); if (toolPath.empty()) { Errorf("not found `%s` in search paths. You may add search path by `-B` option.\n", toolName.c_str()); } return toolPath; } cangjie_compiler-1.0.7/src/Driver/Utils.cpp000066400000000000000000000044731510705540100206450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the utility classes and functions. */ #include "cangjie/Driver/Utils.h" #include #include "cangjie/Utils/FileUtil.h" #include "llvm/Support/JSON.h" #include "llvm/Support/Error.h" namespace Cangjie { std::string GetSingleQuoted(const std::string& str) { std::stringstream ss; ss << "'"; for (char c : str) { // backslash cannot be used as escape character in Shell Command Language. To be able to // use single quote in a command, we generate two single quoted string and join them with // `\'`. For example, ab'cd is transformed to 'ab'\''cd'. if (c == '\'') { ss << "'\\''"; } else { ss << c; } } ss << "'"; return ss.str(); } std::string GetCommandLineArgumentQuoted(const std::string& arg) { #ifdef _WIN32 return FileUtil::GetQuoted(arg); #else return GetSingleQuoted(arg); #endif } std::vector PrependToPaths(const std::string& prefix, const std::vector& paths, bool quoted) { std::vector searchPaths; for (const auto& path : paths) { std::string tmp = quoted ? FileUtil::GetQuoted(prefix + path) : (prefix + path); searchPaths.emplace_back(tmp); } return searchPaths; } std::optional GetDarwinSDKVersion(const std::string& sdkPath) { auto settingFilePath = FileUtil::JoinPath(sdkPath, "SDKSettings.json"); std::string errMessage; std::optional maybeSettingFileContent = FileUtil::ReadFileContent(settingFilePath, errMessage); if (!maybeSettingFileContent.has_value()) { return std::nullopt; } llvm::Expected result = llvm::json::parse(maybeSettingFileContent.value()); if (!result) { return std::nullopt; } if (const llvm::json::Object *obj = result->getAsObject()) { auto value = obj->getString("Version"); if (!value) { return std::nullopt; } return value->str(); } return std::nullopt; } } // namespace Cangjie cangjie_compiler-1.0.7/src/Frontend/000077500000000000000000000000001510705540100173555ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Frontend/CMakeLists.txt000066400000000000000000000010461510705540100221160ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB FRONTEND_SRC ./*.cpp) add_library(CangjieFrontend OBJECT ${FRONTEND_SRC}) add_dependencies(CangjieFrontend CangjieFlatbuffersHeaders) target_include_directories(CangjieFrontend PRIVATE ${FLATBUFFERS_INCLUDE_DIR}) target_compile_options(CangjieFrontend PRIVATE ${CJC_EXTRA_WARNINGS}) cangjie_compiler-1.0.7/src/Frontend/CompileStrategy.cpp000066400000000000000000000475361510705540100232130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the CompileStrategy related classes. */ #include "cangjie/Frontend/CompileStrategy.h" #include "MergeAnnoFromCjd.h" #include "cangjie/AST/PrintNode.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/ConditionalCompilation/ConditionalCompilation.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Macro/MacroExpansion.h" #include "cangjie/Parse/Parser.h" #if defined(CMAKE_ENABLE_ASSERT) || !defined(NDEBUG) #include "cangjie/Parse/ASTChecker.h" #endif #include "cangjie/Sema/Desugar.h" #include "cangjie/Sema/TypeChecker.h" #if (defined RELEASE) #include "cangjie/Utils/Signal.h" #endif #include "cangjie/Utils/ProfileRecorder.h" using namespace Cangjie; using namespace Utils; using namespace FileUtil; void CompileStrategy::TypeCheck() const { if (!ci->typeChecker) { ci->typeChecker = new TypeChecker(ci); CJC_NULLPTR_CHECK(ci->typeChecker); } ci->typeChecker->TypeCheckForPackages(ci->GetSourcePackages()); } bool CompileStrategy::ConditionCompile() const { auto beforeErrCnt = ci->diag.GetErrorCount(); ConditionalCompilation cc{ci}; for (auto& pkg : ci->srcPkgs) { cc.HandleConditionalCompilation(*pkg.get()); } return beforeErrCnt == ci->diag.GetErrorCount(); } void CompileStrategy::DesugarAfterSema() const { ci->typeChecker->PerformDesugarAfterSema(ci->GetSourcePackages()); } bool CompileStrategy::OverflowStrategy() const { if (!ci->typeChecker) { auto typeChecker = new TypeChecker(ci); CJC_NULLPTR_CHECK(typeChecker); ci->typeChecker = typeChecker; } CJC_ASSERT(ci->invocation.globalOptions.overflowStrategy != OverflowStrategy::NA); ci->typeChecker->SetOverflowStrategy(ci->GetSourcePackages()); return true; } void CompileStrategy::PerformDesugar() const { for (auto& [pkg, ctx] : ci->pkgCtxMap) { Cangjie::PerformDesugarBeforeTypeCheck(*pkg, ci->invocation.globalOptions.enableMacroInLSP); } } namespace Cangjie { class FullCompileStrategyImpl final { public: explicit FullCompileStrategyImpl(FullCompileStrategy& strategy) : s{strategy} { } void MergePackage(const Ptr target, const Ptr source) { if (target->accessible != source->accessible) { s.ci->diag.DiagnoseRefactor(DiagKindRefactor::packages_visibility_inconsistent, DEFAULT_POSITION, AST::GetAccessLevelStr(*target), AST::GetAccessLevelStr(*source)); } if (target->isMacroPackage != source->isMacroPackage) { s.ci->diag.DiagnoseRefactor(DiagKindRefactor::packages_macro_inconsistent, DEFAULT_POSITION); } for (auto& file : source->files) { file->curPackage = target; if (target->files.size() > 0) { file->indexOfPackage = target->files.at(0)->indexOfPackage; } target->files.push_back(std::move(file)); } } bool NeedToAddPackage(const Ptr package) { bool packageAlreadyExist = false; for (auto& srcPackage : s.ci->srcPkgs) { if (package->fullPackageName == srcPackage->fullPackageName) { MergePackage(srcPackage, package); packageAlreadyExist = true; } } if (!packageAlreadyExist) { bool isCJLint = s.ci->isCJLint; if (s.ci->srcPkgs.size() > 0 && !isCJLint) { // We can't validate it before because we can have multi-folder packages. s.ci->diag.DiagnoseRefactor(DiagKindRefactor::driver_require_one_package_directory, DEFAULT_POSITION); return false; } if (package->fullPackageName != "default" || package->files.size() != 0 || isCJLint) { return true; } } return false; } void ParseModule(bool& success) { std::string moduleSrcPath = s.ci->invocation.globalOptions.moduleSrcPath; std::unordered_set includeFileSet; if (!s.ci->invocation.globalOptions.srcFiles.empty()) { includeFileSet.insert( s.ci->invocation.globalOptions.srcFiles.begin(), s.ci->invocation.globalOptions.srcFiles.end()); } for (auto& srcDir : s.ci->srcDirs) { std::vector allSrcFiles; auto currentPkg = DEFAULT_PACKAGE_NAME; if (!moduleSrcPath.empty()) { auto basePath = IsDir(moduleSrcPath) ? JoinPath(moduleSrcPath, "") : moduleSrcPath; currentPkg = GetPkgNameFromRelativePath(GetRelativePath(basePath, srcDir) | IdenticalFunc); } auto parseTest = s.ci->invocation.globalOptions.parseTest; auto compileTestsOnly = s.ci->invocation.globalOptions.compileTestsOnly; for (auto& srcFile : GetAllFilesUnderCurrentPath(srcDir, "cj", !parseTest, compileTestsOnly)) { std::string filename = JoinPath(srcDir, srcFile); if (includeFileSet.empty()) { // If no srcFiles, compile the whole module defaultly allSrcFiles.push_back(filename); } else if (includeFileSet.find(filename) != includeFileSet.end()) { // If there are srcFiles, use them to select files to compile allSrcFiles.push_back(filename); } } auto package = ParseOnePackage(allSrcFiles, success, currentPkg); if (srcDir == moduleSrcPath) { package->needExported = false; } if (NeedToAddPackage(package)) { s.ci->srcPkgs.emplace_back(std::move(package)); } } for (auto& package : s.ci->srcPkgs) { std::sort(package->files.begin(), package->files.end(), [](const OwnedPtr& fileOne, const OwnedPtr& fileTwo) { return fileOne->fileName < fileTwo->fileName; }); } if (s.ci->srcPkgs.empty()) { s.ci->srcPkgs.emplace_back(MakeOwned()); } bool compilePackage = s.ci->invocation.globalOptions.compilePackage; // cjlint support multipackage compile if (compilePackage && s.ci->srcPkgs.size() > 1 && !s.ci->isCJLint) { s.ci->diag.DiagnoseRefactor(DiagKindRefactor::driver_require_one_package_directory, DEFAULT_POSITION); } } bool PreReadCommonPartCjo() const { bool hasInputCHIR = s.ci->invocation.globalOptions.inputChirFiles.size() > 0; if (hasInputCHIR) { auto mbFilesFromCommonPart = s.ci->importManager.GetCjoManager()->PreReadCommonPartCjoFiles(); if (!mbFilesFromCommonPart) { return false; } std::vector filesFromCommonPart = *mbFilesFromCommonPart; s.ci->GetSourceManager().ReserveCommonPartSources(filesFromCommonPart); } return true; } OwnedPtr GetMultiThreadParseOnePackage( std::queue, TokenVecMap, size_t>>>& futureQueue, const std::string& defaultPackageName) const { auto package = MakeOwned(defaultPackageName); size_t lineNumInOnePackage = 0; const size_t filePtrIdx = 0; const size_t commentIdx = 1; const size_t lineNumIdx = 2; while (!futureQueue.empty()) { auto curFuture = futureQueue.front().get(); std::get(curFuture)->curPackage = package.get(); std::get(curFuture)->indexOfPackage = package->files.size(); package->files.push_back(std::move(std::get(curFuture))); s.ci->GetSourceManager().AddComments(std::get(curFuture)); lineNumInOnePackage += std::get(curFuture); futureQueue.pop(); } Utils::ProfileRecorder::RecordCodeInfo("package line num", static_cast(lineNumInOnePackage)); if (!package->files.empty()) { // Only update name of package node for first parsed file. if (auto packageSpec = package->files[0]->package.get()) { package->fullPackageName = packageSpec->GetPackageName(); package->accessible = !packageSpec->modifier ? AccessLevel::PUBLIC : packageSpec->modifier->modifier == TokenKind::PROTECTED ? AccessLevel::PROTECTED : packageSpec->modifier->modifier == TokenKind::INTERNAL ? AccessLevel::INTERNAL : AccessLevel::PUBLIC; } } // Checking package consistency: The macro definition package cannot contain the declaration of a common // package. CheckPackageConsistency(*package); return package; } void CheckPackageConsistency(Package& package) const { if (package.files.empty() || !package.files[0]->package) { return; } for (const auto& file : package.files) { if (file->package && package.files[0]->package->hasMacro != file->package->hasMacro) { (void)s.ci->diag.DiagnoseRefactor(DiagKindRefactor::package_name_inconsistent_with_macro, file->begin); return; } } package.isMacroPackage = package.files[0]->package->hasMacro; } OwnedPtr MultiThreadParseOnePackage( std::queue>& fileInfoQueue, const std::string& defaultPackageName) const { std::queue, TokenVecMap, size_t>>> futureQueue; while (!fileInfoQueue.empty()) { auto curFile = fileInfoQueue.front(); futureQueue.push( std::async(std::launch::async, [this, curFile]() -> std::tuple, TokenVecMap, size_t> { #if (defined RELEASE) #if (defined __unix__) // Since alternate signal stack is per thread, we have to create an alternate signal stack for each // thread. Cangjie::CreateAltSignalStack(); #elif _WIN32 // When the SIGABRT, SIGFPE, SIGSEGV and SIGILL signals are triggered in a subthread, // the signals cannot be captured and the process exits directly. Therefore, // the signal processing function must be set for each thread. Cangjie::RegisterCrashSignalHandler(); #endif #endif auto parser = CreateParser(curFile); parser->SetCompileOptions(s.ci->invocation.globalOptions); auto file = parser->ParseTopLevel(); #ifdef SIGNAL_TEST // The interrupt signal triggers the function. In normal cases, this function does not take effect. Cangjie::SignalTest::ExecuteSignalTestCallbackFunc( Cangjie::SignalTest::TriggerPointer::PARSER_POINTER); #endif return {std::move(file), parser->GetCommentsMap(), parser->GetLineNum()}; })); fileInfoQueue.pop(); } auto package = GetMultiThreadParseOnePackage(futureQueue, defaultPackageName); return package; } OwnedPtr CreateParser(const std::tuple& curFile) const { return MakeOwned(std::get<1>(curFile), std::get<0>(curFile), s.ci->diag, s.ci->GetSourceManager(), s.ci->invocation.globalOptions.enableAddCommentToAst, s.ci->invocation.globalOptions.compileCjd); } OwnedPtr ParseOnePackage( const std::vector& files, bool& success, const std::string& defaultPackageName) { std::queue> fileInfoQueue; // Parse source code files to File node list. if (s.ci->loadSrcFilesFromCache) { for (auto& it : s.ci->bufferCache) { const unsigned int fileID = s.ci->GetSourceManager().AddSource(it.first, it.second); if (s.fileIds.count(fileID) > 0) { (void)s.ci->diag.DiagnoseRefactor( DiagKindRefactor::module_read_file_conflicted, DEFAULT_POSITION, it.first); } (void)s.fileIds.insert(fileID); fileInfoQueue.emplace(it.second, fileID); } } else { // The readdir cannot guarantee stable order of inputted files, need sort before adding to sourceManager. std::vector parseFiles{files}; std::sort(parseFiles.begin(), parseFiles.end(), [&](auto& f, auto& second) { return GetFileName(f) < GetFileName(second); }); std::for_each(parseFiles.begin(), parseFiles.end(), [this, &success, &fileInfoQueue](auto file) { std::string failedReason; auto content = ReadFileContent(file, failedReason); if (!content.has_value()) { s.ci->diag.DiagnoseRefactor( DiagKindRefactor::module_read_file_to_buffer_failed, DEFAULT_POSITION, file, failedReason); success = false; return; } const unsigned int fileID = s.ci->GetSourceManager().AddSource(file | IdenticalFunc, content.value()); if (s.fileIds.count(fileID) > 0) { (void)s.ci->diag.DiagnoseRefactor( DiagKindRefactor::module_read_file_conflicted, DEFAULT_POSITION, file); return; } (void)s.fileIds.insert(fileID); fileInfoQueue.emplace(std::move(content.value()), fileID); }); } auto package = MultiThreadParseOnePackage(fileInfoQueue, defaultPackageName); s.ci->diag.EmitCategoryGroup(); std::sort(package->files.begin(), package->files.end(), [](const OwnedPtr& fileOne, const OwnedPtr& fileTwo) { return fileOne->fileName < fileTwo->fileName; }); return package; } FullCompileStrategy& s; }; } // namespace Cangjie FullCompileStrategy::FullCompileStrategy(CompilerInstance* ci) : CompileStrategy(ci), impl{new FullCompileStrategyImpl{*this}} { type = StrategyType::FULL_COMPILE; } FullCompileStrategy::~FullCompileStrategy() { delete impl; } bool FullCompileStrategy::Parse() { if (!impl->PreReadCommonPartCjo()) { return false; } bool ret = true; if (ci->loadSrcFilesFromCache || ci->compileOnePackageFromSrcFiles) { auto package = impl->ParseOnePackage(ci->srcFilePaths, ret, DEFAULT_PACKAGE_NAME); ci->srcPkgs.emplace_back(std::move(package)); } else { impl->ParseModule(ret); } return ret; } bool CompileStrategy::ImportPackages() const { auto ret = ci->ImportPackages(); ParseAndMergeCjds(); return ret; } namespace { // All instance objects share, do not clean. The cjd content of the same process should not be inconsistent. std::unordered_map> g_cjdAstCache; std::mutex g_cjdAstCacheLock; std::mutex g_sourceManageLock; void ParseAndMergeCjd(Ptr ci, std::pair cjdInfo) { std::string failedReason; auto cjoPath = cjdInfo.second; auto sourceCode = FileUtil::ReadFileContent(cjoPath, failedReason); if (!failedReason.empty() || !sourceCode.has_value()) { // In the LSP scenario, the cjd file path cannot be obtained based on the dependency package information // configured in the cache. The cjd file path is searched in searchPath. auto searchPath = ci->importManager.GetSearchPath(); auto cjdPath = FileUtil::FindSerializationFile(cjdInfo.first, CJ_D_FILE_EXTENSION, searchPath); if (cjdPath.empty()) { return; } sourceCode = FileUtil::ReadFileContent(cjdPath, failedReason); if (!failedReason.empty() || !sourceCode.has_value()) { return; } } // Parse unsigned int fileId = 0; SourceManager& sm = ci->diag.GetSourceManager(); { std::lock_guard guardOfSm(g_sourceManageLock); fileId = sm.AddSource(cjoPath, sourceCode.value(), cjdInfo.first); } auto fileAst = Parser(fileId, sourceCode.value(), ci->diag, ci->diag.GetSourceManager(), false, true).ParseTopLevel(); auto pkg = MakeOwned(cjdInfo.first); fileAst->curPackage = pkg.get(); pkg->files.emplace_back(std::move(fileAst)); auto originPkg = ci->importManager.GetPackage(cjdInfo.first); if (!originPkg) { InternalError(cjdInfo.first + " cannot find origin ast"); } MergeCusAnno(originPkg, pkg.get()); { std::lock_guard guard(g_cjdAstCacheLock); g_cjdAstCache[cjdInfo.first] = std::move(pkg); } } } // namespace void CompileStrategy::ParseAndMergeCjds() const { auto& option = ci->invocation.globalOptions; auto hasLevelFlg = option.passedWhenKeyValue.find("APILevel_level") != option.passedWhenKeyValue.end(); auto hasSyscapFlg = option.passedWhenKeyValue.find("APILevel_syscap") != option.passedWhenKeyValue.end(); if (!hasLevelFlg && !hasSyscapFlg) { return; } Utils::ProfileRecorder::Start("ImportPackages", "ParseAndMergeCjds"); auto cjdPaths = ci->importManager.GetDepPkgCjdPaths(); std::vector> futures; futures.reserve(cjdPaths.size()); // Reuse current CompilerInstance, but the Parser in the macro expansion phase uses the DParser. option.compileCjd = true; // cjdInfo is [fullPackageName, cjdPath]. for (auto& cjdInfo : cjdPaths) { if (option.jobs == 1) { ParseAndMergeCjd(ci, cjdInfo); } else { // In the LSP scenario, concurrent calls may occur. std::lock_guard guard(g_cjdAstCacheLock); auto [iter, succ] = g_cjdAstCache.try_emplace(cjdInfo.first, nullptr); if (!succ && iter->second) { auto originPkg = ci->importManager.GetPackage(cjdInfo.first); if (!originPkg) { InternalError(cjdInfo.first + " cannot find origin ast"); } MergeCusAnno(originPkg, iter->second.get()); } else { futures.emplace_back(std::async(std::launch::async, ParseAndMergeCjd, ci, cjdInfo)); } } } for (auto& future : futures) { future.get(); } ci->diag.EmitCategoryGroup(); option.compileCjd = false; Utils::ProfileRecorder::Stop("ImportPackages", "ParseAndMergeCjds"); } bool CompileStrategy::MacroExpand() const { auto beforeErrCnt = ci->diag.GetErrorCount(); MacroExpansion me(ci); me.Execute(ci->srcPkgs); ci->diag.EmitCategoryDiagnostics(DiagCategory::PARSE); #if defined(CMAKE_ENABLE_ASSERT) || !defined(NDEBUG) AST::ASTChecker astChecker; astChecker.CheckAST(ci->srcPkgs); astChecker.CheckBeginEnd(ci->srcPkgs); #endif ci->tokensEvalInMacro = me.tokensEvalInMacro; bool hasNoMacroErr = beforeErrCnt == ci->diag.GetErrorCount(); return hasNoMacroErr; } bool FullCompileStrategy::Sema() { { Utils::ProfileRecorder recorder("Semantic", "Desugar Before TypeCheck"); PerformDesugar(); } TypeCheck(); #ifdef SIGNAL_TEST // The interrupt signal triggers the function. In normal cases, this function does not take effect. Cangjie::SignalTest::ExecuteSignalTestCallbackFunc(Cangjie::SignalTest::TriggerPointer::SEMA_POINTER); #endif // Report number of warnings and errors. if (ci->diag.GetErrorCount() != 0) { return false; } return true; } cangjie_compiler-1.0.7/src/Frontend/CompilerInstance.cpp000066400000000000000000001210021510705540100233140ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the CompilerInstance. */ #include "cangjie/Frontend/CompilerInstance.h" #include #include "PrintSymbolTable.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Basic/Match.h" #include "cangjie/Basic/Print.h" #include "cangjie/Basic/Version.h" #include "cangjie/CHIR/CHIR.h" #include "cangjie/CHIR/CHIRPrinter.h" #include "cangjie/CHIR/Interpreter/CHIR2BCHIR.h" #include "cangjie/CHIR/Serializer/CHIRDeserializer.h" #include "cangjie/CHIR/Serializer/CHIRSerializer.h" #include "cangjie/CHIR/UserDefinedType.h" #include "cangjie/Frontend/CompileStrategy.h" #include "cangjie/IncrementalCompilation/ASTCacheCalculator.h" #include "cangjie/IncrementalCompilation/IncrementalCompilationLogger.h" #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Modules/ModulesUtils.h" #include "cangjie/Modules/PackageManager.h" #include "cangjie/Parse/ASTHasher.h" #include "cangjie/Sema/Desugar.h" #include "cangjie/Sema/GenericInstantiationManager.h" #include "cangjie/Sema/TestManager.h" #include "cangjie/Sema/TypeChecker.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/ICEUtil.h" #include "cangjie/Utils/ProfileRecorder.h" #include "cangjie/Utils/TaskQueue.h" #include "cangjie/Utils/Utils.h" #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "cangjie/Mangle/CHIRMangler.h" #include "cangjie/CHIR/Checker/ComputeAnnotations.h" #endif #ifdef RELEASE #include "cangjie/Utils/Signal.h" #endif using namespace Cangjie; CompilerInstance::CompilerInstance(CompilerInvocation& invocation, DiagnosticEngine& diag) : invocation(invocation), diag(diag), packageManager(new PackageManager(importManager)), typeManager(new TypeManager()), importManager(diag, *typeManager, invocation.globalOptions), testManager(new TestManager(importManager, *typeManager, diag, invocation.globalOptions)), mangler(std::make_unique()) { CJC_NULLPTR_CHECK(typeManager); CJC_NULLPTR_CHECK(packageManager); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND chirData.InitData(&fileNameMap, invocation.globalOptions.GetJobs()); #endif if (invocation.globalOptions.compilePackage) { std::copy(invocation.globalOptions.packagePaths.begin(), invocation.globalOptions.packagePaths.end(), std::inserter(srcDirs, srcDirs.end())); // Check whether compiling one package with multi source files according to srcDirs. compileOnePackageFromSrcFiles = srcDirs.empty(); } else { compileOnePackageFromSrcFiles = true; srcFilePaths = invocation.globalOptions.srcFiles; } compileStrategy = new FullCompileStrategy(this); CJC_NULLPTR_CHECK(compileStrategy); diag.SetSourceManager(&sm); AST::ASTHasher::Init(invocation.globalOptions); } CompilerInstance::~CompilerInstance() { // AST must be released before ASTContext for correct symbol detaching. srcPkgs.clear(); pkgCtxMap.clear(); delete compileStrategy; compileStrategy = nullptr; delete typeChecker; typeChecker = nullptr; delete typeManager; typeManager = nullptr; delete testManager; testManager = nullptr; delete gim; gim = nullptr; delete packageManager; packageManager = nullptr; } bool CompilerInstance::InitCompilerInstance() { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND performMap.insert_or_assign(CompileStage::LOAD_PLUGINS, &CompilerInstance::PerformPluginLoad); #endif performMap.insert_or_assign(CompileStage::PARSE, &CompilerInstance::PerformParse); performMap.insert_or_assign(CompileStage::CONDITION_COMPILE, &CompilerInstance::PerformConditionCompile); performMap.insert_or_assign(CompileStage::IMPORT_PACKAGE, &CompilerInstance::PerformImportPackage); performMap.insert_or_assign(CompileStage::MACRO_EXPAND, &CompilerInstance::PerformMacroExpand); performMap.insert_or_assign(CompileStage::AST_DIFF, &CompilerInstance::PerformIncrementalScopeAnalysis); performMap.insert_or_assign(CompileStage::SEMA, &CompilerInstance::PerformSema); performMap.insert_or_assign(CompileStage::DESUGAR_AFTER_SEMA, &CompilerInstance::PerformDesugarAfterSema); performMap.insert_or_assign(CompileStage::GENERIC_INSTANTIATION, &CompilerInstance::PerformGenericInstantiation); performMap.insert_or_assign(CompileStage::OVERFLOW_STRATEGY, &CompilerInstance::PerformOverflowStrategy); performMap.insert_or_assign(CompileStage::MANGLING, &CompilerInstance::PerformMangling); performMap.insert_or_assign(CompileStage::CHIR, &CompilerInstance::PerformCHIRCompilation); performMap.insert_or_assign(CompileStage::CODEGEN, &CompilerInstance::PerformCodeGen); performMap.insert_or_assign(CompileStage::SAVE_RESULTS, &CompilerInstance::PerformCjoAndBchirSaving); return true; } bool CompilerInstance::Compile(CompileStage stage) { if (!InitCompilerInstance()) { diag.ReportErrorAndWarningCount(); return false; } for (int i = 0; i <= static_cast(stage); i++) { Cangjie::ICE::TriggerPointSetter iceSetter(static_cast(i)); if (!performMap[static_cast(i)](this)) { diag.ReportErrorAndWarningCount(); return false; } } Cangjie::ICE::TriggerPointSetter iceSetter(stage); diag.ReportErrorAndWarningCount(); return true; } static bool IsNeedSaveIncrCompilationLogFile(const GlobalOptions& globalOpts, const FrontendOptions& frontOpts) { if (!globalOpts.enIncrementalCompilation) { return false; } if (globalOpts.scanDepPkg) { return false; } if (frontOpts.dumpAction != FrontendOptions::DumpAction::NO_ACTION) { return false; } if (globalOpts.compileCjd) { return false; } return true; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND namespace { class MetaTransformPlugin { public: static MetaTransformPlugin Get(const std::string& path); void RegisterCallbackTo(MetaTransformPluginBuilder& mtm) const; bool IsValid() const { return !pluginPath.empty() && metaTransformPluginInfo.cjcVersion == CANGJIE_VERSION && metaTransformPluginInfo.registerTo; } void* GetHandle() const { return handle; } private: MetaTransformPlugin() = default; MetaTransformPlugin(const std::string& pluginPath, const MetaTransformPluginInfo& info, HANDLE handle); private: std::string pluginPath; MetaTransformPluginInfo metaTransformPluginInfo; HANDLE handle; }; MetaTransformPlugin::MetaTransformPlugin( const std::string& pluginPath, const MetaTransformPluginInfo& info, HANDLE handle) : pluginPath(pluginPath), metaTransformPluginInfo(info), handle(handle) { } MetaTransformPlugin MetaTransformPlugin::Get(const std::string& path) { HANDLE handle = nullptr; #ifdef _WIN32 handle = InvokeRuntime::OpenSymbolTable(path); #elif defined(__linux__) || defined(__APPLE__) handle = InvokeRuntime::OpenSymbolTable(path, RTLD_NOW | RTLD_LOCAL); #endif if (!handle) { #ifndef CANGJIE_ENABLE_GCOV throw NullPointerException(); #else CJC_ABORT(); #endif } void* fPtr = InvokeRuntime::GetMethod(handle, "getMetaTransformPluginInfo"); if (!fPtr) { #ifndef CANGJIE_ENABLE_GCOV throw NullPointerException(); #else CJC_ABORT(); #endif } auto pluginInfo = reinterpret_cast(fPtr)(); return MetaTransformPlugin(path, pluginInfo, handle); } void MetaTransformPlugin::RegisterCallbackTo(MetaTransformPluginBuilder& mtm) const { metaTransformPluginInfo.registerTo(mtm); } } // namespace bool CompilerInstance::PerformPluginLoad() { for (auto pluginPath : invocation.globalOptions.pluginPaths) { // loop for all plugins try { auto metaTransformPlugin = MetaTransformPlugin::Get(pluginPath); if (!metaTransformPlugin.IsValid()) { diag.DiagnoseRefactor(DiagKindRefactor::not_a_valid_plugin, DEFAULT_POSITION, pluginPath); } AddPluginHandle(metaTransformPlugin.GetHandle()); metaTransformPlugin.RegisterCallbackTo(metaTransformPluginBuilder); // register MetaTransform into builder } catch (...) { diag.DiagnoseRefactor(DiagKindRefactor::not_a_valid_plugin, DEFAULT_POSITION, pluginPath); return false; } } return true; } #endif bool CompilerInstance::PerformParse() { auto ret = compileStrategy->Parse(); if (!srcPkgs.empty()) { const auto& globalOpts = invocation.globalOptions; srcPkgs.front()->noSubPkg = globalOpts.noSubPkg; Utils::ProfileRecorder::SetPackageName(srcPkgs[0]->fullPackageName); Utils::ProfileRecorder::SetOutputDir(globalOpts.output); if (IsNeedSaveIncrCompilationLogFile(globalOpts, invocation.frontendOptions)) { std::string incrLogPath = invocation.globalOptions.GenerateCachedPathName(srcPkgs[0]->fullPackageName, CACHED_LOG_EXTENSION); IncrementalCompilationLogger::GetInstance().InitLogFile(incrLogPath); IncrementalCompilationLogger::GetInstance().WriteBuffToFile(); } } return ret; } bool CompilerInstance::PerformConditionCompile() { return compileStrategy->ConditionCompile(); } bool CompilerInstance::PerformMacroExpand() { auto ret = compileStrategy->MacroExpand(); // Constant evaluation and the interpreter needs to load bchir, which requires an AST loader. if (!invocation.globalOptions.IsConstEvalEnabled() && !invocation.globalOptions.interpreter) { importManager.DeleteASTLoaders(); } importManager.ClearPackageBCHIRCache(); if (invocation.globalOptions.compileTestsOnly && invocation.globalOptions.enableVerbose) { Print("Source files to compile for the test-only mode: {"); for (auto& pkg : srcPkgs) { for (auto& srcFile : pkg->files) { Print(pkg->fullPackageName + "::" + srcFile->fileName); } } Println("}"); } return ret; } void CompilerInstance::CacheCompileArgs() { cachedInfo.compileArgs = invocation.globalOptions.ToSerialized(); } void CompilerInstance::CacheSemaUsage(SemanticInfo&& info) { // Move previous unchanged sema usage info to current info. for (auto& [decl, usage] : cachedInfo.semaInfo.usages) { if (decl->toBeCompiled) { continue; } auto found = info.usages.find(decl); // MainDecl's usage is recorded as desugared. if (auto mainDecl = DynamicCast(decl); mainDecl && mainDecl->desugarDecl) { found = info.usages.find(mainDecl->desugarDecl.get()); } else if (auto macroDecl = DynamicCast(decl); macroDecl && macroDecl->desugarDecl) { found = info.usages.find(macroDecl->desugarDecl.get()); } if (found != info.usages.end()) { found->second = std::move(usage); } } // Update cached info. cachedInfo.semaInfo = std::move(info); } bool CompilerInstance::ShouldWriteCacheFile() const { if (!invocation.globalOptions.enIncrementalCompilation) { return false; } // if set "printVersionOnly" and "showUsage", must not get here if (invocation.globalOptions.printVersionOnly || invocation.globalOptions.showUsage) { InternalError("'printVersionOnly' or 'showUsage' cannot be true."); } // if compiled with "cjc-frontend --dump-xxx ...", we don't write cache file // that's OK, in this case, we can not run incremental compiling if (invocation.frontendOptions.dumpAction != FrontendOptions::DumpAction::NO_ACTION) { return false; } if (invocation.globalOptions.IsEmitCHIREnable()) { return false; } if (invocation.globalOptions.compileCjd) { return false; } if (srcPkgs.size() != 1) { InternalError("source packages should only have one element."); } // if current package is empty package, we don't write cache file if (srcPkgs[0]->IsEmpty() || kind == IncreKind::NO_CHANGE) { return false; } return true; } namespace { void UpdateMemberDeclMangleNameForCachedInfo(const RawMangled2DeclMap& rawMangleName2DeclMap, MemberDeclCache& memCache) { if (auto d = rawMangleName2DeclMap.find(memCache.rawMangle); d != rawMangleName2DeclMap.end()) { memCache.cgMangle = d->second->mangledName; } else { CJC_ABORT(); } for (auto &m : memCache.members) { UpdateMemberDeclMangleNameForCachedInfo(rawMangleName2DeclMap, m); } } void UpdateTopLevelDeclMangleNameForCachedInfo( const RawMangled2DeclMap& rawMangleName2DeclMap, const std::string& rawMangle, TopLevelDeclCache& topCache) { if (auto d = rawMangleName2DeclMap.find(rawMangle); d != rawMangleName2DeclMap.end()) { topCache.cgMangle = d->second->mangledName; } else { CJC_ABORT(); } for (auto &m : topCache.members) { UpdateMemberDeclMangleNameForCachedInfo(rawMangleName2DeclMap, m); } } } void CompilerInstance::UpdateMangleNameForCachedInfo() { for (auto &it : cachedInfo.curPkgASTCache) { auto rawMangle = it.first; UpdateTopLevelDeclMangleNameForCachedInfo(rawMangleName2DeclMap, rawMangle, it.second); } for (auto &it : cachedInfo.importedASTCache) { auto rawMangle = it.first; UpdateTopLevelDeclMangleNameForCachedInfo(rawMangleName2DeclMap, rawMangle, it.second); } } void CompilerInstance::UpdateCachedInfo() { // update mangle name for codegen UpdateMangleNameForCachedInfo(); } bool CompilerInstance::WriteCachedInfo() { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto& pkg{*srcPkgs[0]}; std::string path{invocation.globalOptions.GenerateCachedPathName(pkg.fullPackageName, CACHED_AST_EXTENSION)}; WriteCache(pkg, std::move(cachedInfo), std::move(order), path); #endif return true; } bool CompilerInstance::UpdateAndWriteCachedInfoToDisk() { if (!ShouldWriteCacheFile()) { return true; } UpdateCachedInfo(); return WriteCachedInfo(); } bool CompilerInstance::CalculateASTCache() { auto& package = srcPkgs[0]; // In some case, we will terminate the calculation bool terminate = false; std::string message; if (diag.GetErrorCount() != 0) { terminate = true; kind = IncreKind::ROLLBACK; message = "frontend error detected, roll back to full compilation"; } else if (package->IsEmpty()) { terminate = true; kind = IncreKind::EMPTY_PKG; message = "empty package, skip"; } if (terminate) { IncrementalCompilationLogger::GetInstance().LogLn(message); return false; } // even the incremental compilation is not enabled in current package, // we still need to caculate the RawMangleName of the decls in AST, // since it will be exported to and used in downstream packages where // the incremental compilation is enabled auto needCodePosInfo = std::make_pair(invocation.globalOptions.enableCompileDebug, invocation.globalOptions.displayLineInfo); IncrementalCompilation::ASTCacheCalculator pc{*package, needCodePosInfo}; pc.Walk(); rawMangleName2DeclMap = std::move(pc.mangled2Decl); astCacheInfo = std::move(pc.ret); declsWithDuplicatedRawMangleName = std::move(pc.duplicatedMangleNames); directExtends = std::move(pc.directExtends); fileMap = std::move(pc.fileMap); order = std::move(pc.order); return true; } bool CompilerInstance::PerformIncrementalScopeAnalysis() { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (invocation.globalOptions.compileCjd) { return true; } Utils::ProfileRecorder recorder("Main Stage", "AST Diff"); CalculateASTCache(); #endif return true; } bool CompilerInstance::PerformImportPackage() { return compileStrategy->ImportPackages(); } bool CompilerInstance::PerformSema() { return compileStrategy->Sema(); } bool CompilerInstance::PerformOverflowStrategy() { if (invocation.globalOptions.overflowStrategy == OverflowStrategy::NA) { return true; } compileStrategy->OverflowStrategy(); return true; } bool CompilerInstance::PerformDesugarAfterSema() { testManager->MarkDeclsForTestIfNeeded(GetSourcePackages()); compileStrategy->DesugarAfterSema(); return true; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool CompilerInstance::PerformGenericInstantiation() { if (!importManager.IsSourceCodeImported()) { InternalError("Generic instantiation should not be performed when imported source code is not reparsed."); return false; } if (gim == nullptr) { gim = new GenericInstantiationManager(*this); CJC_NULLPTR_CHECK(gim); testManager->Init(gim); } if (!invocation.globalOptions.enIncrementalCompilation) { Utils::ProfileRecorder::Start("Generic Instantiation", "ResetGenericInstantiationStage"); // Perform instantiation for source package. NOTE: must only exist exact one source package except unittest. gim->ResetGenericInstantiationStage(); Utils::ProfileRecorder::Stop("Generic Instantiation", "ResetGenericInstantiationStage"); Utils::ProfileRecorder::Start("Generic Instantiation", "GenericInstantiatePackage"); for (auto& srcPkg : srcPkgs) { gim->GenericInstantiatePackage(*srcPkg); // Avoid enter post-desugar process if error occurs in generic instantiation. if (diag.GetErrorCount() != 0) { return false; } } Utils::ProfileRecorder::Stop("Generic Instantiation", "GenericInstantiatePackage"); } Utils::ProfileRecorder recorder("Generic Instantiation", "desugar after instantiation"); // Perform desugar after generic instantiation. // NOTE: This step will not generate any error. for (auto& srcPkg : srcPkgs) { auto astCtx = GetASTContextByPackage(srcPkg.get()); CJC_ASSERT(astCtx); typeChecker->PerformDesugarAfterInstantiation(*astCtx, *srcPkg); } return true; } #endif namespace { using DeclAndPackageName = std::pair; using LambdaAndPackageName = std::pair; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void DoNewMangling( const BaseMangler& baseMangler, const std::vector& decls, size_t start, size_t end) { std::vector> prefix; auto handleMangle = [&baseMangler, &prefix](Ptr node) -> VisitAction { std::vector> filteredPrefix; for (size_t i = 0; i < prefix.size(); i++) { if (filteredPrefix.size() != 0 && Is(filteredPrefix.back().get()) && static_cast(filteredPrefix.back().get())->desugarExpr.get() == prefix[i].get()) { filteredPrefix.pop_back(); } filteredPrefix.emplace_back(prefix[i]); } auto nextAction = Meta::match(*node)( [&baseMangler, &filteredPrefix](Decl& decl) { if (decl.astKind == ASTKind::MAIN_DECL) { return VisitAction::WALK_CHILDREN; } if (decl.astKind == ASTKind::MACRO_DECL) { auto& desugar = *StaticCast(&decl)->desugarDecl; desugar.mangledName = baseMangler.Mangle(desugar); return VisitAction::WALK_CHILDREN; } if (!Ty::IsTyCorrect(decl.ty)) { return VisitAction::SKIP_CHILDREN; } decl.mangledName = baseMangler.Mangle(decl, filteredPrefix); return VisitAction::WALK_CHILDREN; }, [&baseMangler, &filteredPrefix](LambdaExpr& lambda) { if (lambda.TestAttr(Attribute::GENERIC) || !Ty::IsTyCorrect(lambda.ty)) { return VisitAction::SKIP_CHILDREN; } lambda.mangledName = baseMangler.MangleLambda(lambda, filteredPrefix); return VisitAction::WALK_CHILDREN; }, []([[maybe_unused]] const Annotation& anno) { // The annotation node should be desugared during the Sema, this node is only used in ExportAST. return VisitAction::SKIP_CHILDREN; }, []([[maybe_unused]] const Node& node) { return VisitAction::WALK_CHILDREN; }, []() { return VisitAction::WALK_CHILDREN; }); prefix.emplace_back(node); return nextAction; }; auto cleanUpNode = [&prefix]([[maybe_unused]] const Ptr& node) -> VisitAction { prefix.pop_back(); return VisitAction::KEEP_DECISION; }; for (size_t idx = start; idx < end; ++idx) { auto& decl = decls[idx]; Walker(decl.first, handleMangle, cleanUpNode).Walk(); } } #endif /** * For all top-level declarations in @p topDecls, split them into tasks in * such a way that each task processes 30 decls serially. * Use a TaskQueue with a concurrency of parallelNum to execute these tasks. * For each decl in a task, the walker is used to mangle the decl and the * internal nodes that need to be mangled. When lambda expression nodes are * encountered, they are collected because of the particularity of their * mangle rules (unsuitable for concurrency). * When all tasks are complete, mangle the lambda expression nodes collected * in serial mode. */ #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void DoMangling(const BaseMangler& baseMangler, size_t parallelNum, const std::vector& topDecls) { // when the 'jobs' is 1, we disable the parallel of mangle to simplify the debug if (parallelNum == 1) { DoNewMangling(baseMangler, topDecls, 0, topDecls.size()); } else { // Mangle 30 decls serially in a task. constexpr size_t batchSize = 30U; auto tasksNum = topDecls.size() / batchSize; size_t start = 0; size_t end = batchSize; std::unordered_map> lambdasCollectedByTask; // Creating a Concurrent Task Queue Utils::TaskQueue taskQueue(parallelNum); for (size_t i = 0; i < tasksNum; ++i) { taskQueue.AddTask( [&baseMangler, &topDecls, start, end]() { DoNewMangling(baseMangler, topDecls, start, end); }); start += batchSize; end += batchSize; } // Concurrent processing of grouped decls taskQueue.RunInBackground(); // Wait until all mangle tasks are complete. taskQueue.WaitForAllTasksCompleted(); DoNewMangling(baseMangler, topDecls, start, topDecls.size()); } } void SortForBep(Package& pkg) { std::unordered_map, std::string> declMangleMap; auto compare = [&declMangleMap](const Ptr d1, const Ptr d2) { const std::string& mangle1 = declMangleMap[d1]; const std::string& mangle2 = declMangleMap[d2]; if (mangle1 == mangle2) { return CompNodeByPos(d1, d2); } return mangle1 < mangle2; }; // Reorder genericInstantiatedDecls for bep. std::set, decltype(compare)> orderedDecls(compare); std::for_each(pkg.genericInstantiatedDecls.begin(), pkg.genericInstantiatedDecls.end(), [&orderedDecls, &declMangleMap](auto& it) { BaseMangler mangler; declMangleMap.emplace(it.get(), mangler.Mangle(*it)); orderedDecls.emplace(it.release()); }); pkg.genericInstantiatedDecls.clear(); std::for_each(orderedDecls.cbegin(), orderedDecls.cend(), [&pkg](auto it) { pkg.genericInstantiatedDecls.emplace_back(OwnedPtr(it)); }); } #endif } // namespace #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void CompilerInstance::ManglingHelpFunction(const BaseMangler& baseMangler) #endif { // Collect all top-level decls std::vector topDecls; auto deduplicatedEmplace = [&topDecls](AST::Decl* decl, std::string pkgName) { if (std::find(topDecls.begin(), topDecls.end(), std::make_pair(decl, pkgName)) == topDecls.end()) { topDecls.emplace_back(decl, pkgName); } }; for (auto& package : GetPackages()) { auto fullPackageName = package->fullPackageName; Walker(package, [&fullPackageName](Ptr node) { if (auto vd = DynamicCast(node); vd && vd->fullPackageName.empty()) { vd->fullPackageName = fullPackageName; return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }).Walk(); for (auto& file : package->files) { for (auto& decl : file->decls) { deduplicatedEmplace(decl.get(), package->fullPackageName); } } for (auto& decl : package->genericInstantiatedDecls) { deduplicatedEmplace(decl.get(), package->fullPackageName); } } if (invocation.globalOptions.disableInstantiation) { for (auto& importPkg : importManager.GetAllImportedPackages()) { CJC_NULLPTR_CHECK(importPkg->srcPackage.get()); // exclude current package if (!importPkg->srcPackage->TestAttr(AST::Attribute::IMPORTED)) { continue; } for (auto& file : importPkg->srcPackage->files) { for (auto& decl : file->decls) { deduplicatedEmplace(decl.get(), importPkg->fullPackageName); } for (auto& decl : file->exportedInternalDecls) { deduplicatedEmplace(decl.get(), importPkg->fullPackageName); } } for (auto& decl : importPkg->srcPackage->genericInstantiatedDecls) { if (decl->IsNominalDecl()) { deduplicatedEmplace(decl.get(), importPkg->fullPackageName); } } } } DoMangling(baseMangler, invocation.globalOptions.GetJobs(), topDecls); } bool CompilerInstance::PerformMangling() { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND using namespace CHIR; mangler = CreateUniquePtr(invocation.globalOptions.enableCompileTest); // Load the manglerCtxTable std::vector> manglerCtxVec; // Get all imported packages and source packages. for (auto& package : importManager.GetAllImportedPackages()) { std::string pkgName = ManglerContext::ReduceUnitTestPackageName(package->fullPackageName); if (mangler->manglerCtxTable.find(pkgName) == mangler->manglerCtxTable.end()) { auto manglerCtx = std::make_unique(); mangler->manglerCtxTable[pkgName] = manglerCtx.get(); manglerCtxVec.emplace_back(std::move(manglerCtx)); } mangler->CollectVarOrLambda(*mangler->manglerCtxTable.at(pkgName), *package->srcPackage); } #endif mangler->lambdaCounter = cachedInfo.lambdaCounter; ManglingHelpFunction(*mangler); cachedInfo.lambdaCounter = mangler->lambdaCounter; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (!invocation.globalOptions.disableInstantiation) { for (auto& package : GetSourcePackages()) { SortForBep(*package); } } #endif return true; } bool CompilerInstance::GenerateCHIRForPkg(AST::Package& pkg) { if (pkg.files.empty()) { return true; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // use this result when APILevel check supports arbitrary const expressions (void)CHIR::ComputeAnnotations(pkg, *this); auto& constAnalysisWrapper = chirData.GetConstAnalysisResultRef(); #endif CHIR::CHIRBuilder builder1(GetCHIRContext(), invocation.globalOptions.GetJobs()); CHIR::ToCHIR convertor(*this, pkg, constAnalysisWrapper, builder1); if (!convertor.Run()) { return false; } auto chirPkg = convertor.GetPackage(); CJC_NULLPTR_CHECK(chirPkg); astPkg2chirPkgMap.emplace(&pkg, chirPkg); chirInfo.optEffectMap = convertor.GetOptEffectMap(); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND chirData.AppendNewPackage(chirPkg); chirData.SetImplicitFuncs(convertor.GetImplicitFuncs()); chirData.SetConstVarInitFuncs(convertor.GetConstVarInitFuncs()); chirInfo.curVirtFuncWrapDep = convertor.GetCurVirtualFuncWrapperDepForIncr(); chirInfo.delVirtFuncWrapForIncr = convertor.GetDeleteVirtualFuncWrapperForIncr(); chirInfo.ccOutFuncsRawMangle = convertor.GetCCOutFuncsRawMangle(); chirInfo.varInitDepMap = convertor.GetVarInitDepMap(); #endif #ifdef SIGNAL_TEST // The interrupt signal triggers the function. In normal cases, this function does not take effect. Cangjie::SignalTest::ExecuteSignalTestCallbackFunc(Cangjie::SignalTest::TriggerPointer::CHIR_POINTER); #endif return true; } bool CompilerInstance::PerformCHIRCompilation() { bool ret = true; auto sourcePackagesCHIR = GetSourcePackages(); auto pkgsOrderedCHIR = packageManager->GetBuildOrders(); for (auto& pkgsInfos : pkgsOrderedCHIR) { auto isSrcPkg = [&pkgsInfos]( Ptr pkg) { return pkg->fullPackageName == pkgsInfos[0]->fullPackageName; }; auto pkg = std::find_if(sourcePackagesCHIR.begin(), sourcePackagesCHIR.end(), isSrcPkg); if (pkg != sourcePackagesCHIR.end()) { ret = ret && GenerateCHIRForPkg(**pkg); sourcePackagesCHIR.erase(pkg); } }; for (auto& remainingSourcePackage : sourcePackagesCHIR) { ret = ret && GenerateCHIRForPkg(*remainingSourcePackage); } diag.EmitCategoryDiagnostics(DiagCategory::CHIR); return ret; } bool CompilerInstance::DumpTokens() { bool ret = true; std::for_each(srcFilePaths.begin(), srcFilePaths.end(), [this, &ret](auto filePath) { std::string failedReason; auto content = FileUtil::ReadFileContent(filePath, failedReason); if (!content.has_value()) { diag.DiagnoseRefactor( DiagKindRefactor::module_read_file_to_buffer_failed, DEFAULT_POSITION, filePath, failedReason); ret = false; return; } const unsigned int fileID = GetSourceManager().AddSource(FileUtil::GetAbsPath(filePath) | FileUtil::IdenticalFunc, content.value()); Lexer lexer(fileID, content.value(), diag, GetSourceManager()); for (;;) { Token tok = lexer.Next(); if (tok.kind == TokenKind::END) { break; } std::string position = "<" + filePath + ":" + std::to_string(tok.Begin().line) + ":" + std::to_string(tok.Begin().column) + ">"; if (tok.kind == TokenKind::COMMENT) { Println(TOKEN_KIND_VALUES[static_cast(tok.kind)], position); } else if (tok.kind == TokenKind::NL) { Println(tok == "\n" ? "\\n" : "\\r\\n", TOKEN_KIND_VALUES[static_cast(tok.kind)], position); } else { Println(tok.Value(), TOKEN_KIND_VALUES[static_cast(tok.kind)], position); } } }); return ret; } void CompilerInstance::DumpSymbols() { PrintSymbolTable(*this); } void CompilerInstance::DumpMacro() { Println("Dump tokens after macro expansion."); Println("==== Start Dumping ====\n"); for (auto ts : tokensEvalInMacro) { Println(ts); } } SourceManager& CompilerInstance::GetSourceManager() { return sm; } std::vector> CompilerInstance::GetSourcePackages() const { std::vector> packages; for (auto& srcPkg : srcPkgs) { packages.push_back(srcPkg.get()); } return packages; } ASTContext* CompilerInstance::GetASTContextByPackage(Ptr pkg) const { if (!pkg) { return nullptr; } auto found = pkgCtxMap.find(pkg); return found != pkgCtxMap.end() ? found->second.get() : nullptr; } void CompilerInstance::AddSourceToMember() { if (!pkgs.empty()) { return; } for (auto& it : GetSourcePackages()) { pkgs.push_back(it); } } bool CompilerInstance::ImportPackages() { // If CANGJIE_HOME not set, detect from executablePath. if (cangjieHome.empty()) { if (!DetectCangjieHome()) { return false; } } if (cangjieModules.empty()) { if (!DetectCangjieModules()) { return false; } } AddSourceToMember(); if (invocation.globalOptions.scanDepPkg) { if (!invocation.globalOptions.inputCjoFile.empty()) { depPackageInfo = importManager.GeneratePkgDepInfoByCjo(invocation.globalOptions.inputCjoFile); } else { depPackageInfo = importManager.GeneratePkgDepInfo(pkgs); } return true; } if (!importManager.BuildIndex(cangjieModules, invocation.globalOptions, pkgs)) { return false; } MergePackages(); ModularizeCompilation(); return true; } void CompilerInstance::MergePackages() { for (auto& pkg : srcPkgs) { pkgCtxMap.insert_or_assign(pkg.get(), std::make_unique(diag, *pkg)); } } std::set> CompilerInstance::GetExtendDecls( const std::variant, Ptr>& type) const { if (!typeManager) { return {}; } if (type.index() == 0) { if (auto ty = std::get>(type)) { return typeManager->GetAllExtendsByTy(*ty); } } else if (type.index() == 1) { if (auto decl = std::get>(type)) { return typeManager->GetDeclExtends(*decl); } } return {}; } std::vector> CompilerInstance::GetAllVisibleExtendMembers( const std::variant, Ptr>& type, const File& curFile) const { std::set> extends = GetExtendDecls(type); std::vector> members; Ptr exprTy = nullptr; if (type.index() == 0) { exprTy = std::get>(type); } else if (type.index() == 1) { exprTy = std::get>(type)->ty; } if (!Ty::IsTyCorrect(exprTy)) { return {}; } for (auto& e : extends) { if (!importManager.IsExtendAccessible(curFile, *e)) { continue; } auto& extendMember = e->GetMemberDecls(); if (curFile.curPackage->fullPackageName == e->fullPackageName) { members.insert(members.end(), extendMember.begin(), extendMember.end()); } else { for (auto& m : extendMember) { if (importManager.IsExtendMemberAccessible(curFile, *m, *exprTy)) { members.emplace_back(m.get()); } } } std::function collectInheritMember = [&collectInheritMember, &members]( const InheritableDecl& id) { for (auto& super : id.inheritedTypes) { auto targetDecl = DynamicCast(super->GetTarget()); if (targetDecl == nullptr) { continue; } collectInheritMember(*targetDecl); } if (id.astKind != ASTKind::INTERFACE_DECL) { return; } auto& inherMembers = id.GetMemberDecls(); std::for_each(inherMembers.begin(), inherMembers.end(), [&members](auto& m) { if (!m->TestAttr(Attribute::ABSTRACT)) { members.emplace_back(m); } }); }; collectInheritMember(*e); typeChecker->RemoveTargetNotMeetExtendConstraint(exprTy, members); } return members; } Candidate CompilerInstance::GetGivenReferenceTarget( ASTContext& ctx, const std::string& scopeName, Expr& expr, bool hasLocalDecl) const { if (!typeChecker) { return {}; } return typeChecker->SynReferenceSeparately(ctx, scopeName, expr, hasLocalDecl); } bool CompilerInstance::DetectCangjieHome() { if (invocation.globalOptions.environment.cangjieHome.has_value()) { cangjieHome = invocation.globalOptions.environment.cangjieHome.value(); return true; } // Detect from exepath. if (invocation.globalOptions.executablePath.empty()) { diag.DiagnoseRefactor(DiagKindRefactor::frontend_failed_to_detect_cangjie_home, DEFAULT_POSITION, "can not resolve executable path"); return false; } else { cangjieHome = FileUtil::JoinPath(FileUtil::GetDirPath(FileUtil::GetAbsPath(invocation.globalOptions.executablePath) | FileUtil::IdenticalFunc), ".."); if (!FileUtil::FileExist(cangjieHome) || !FileUtil::FileExist(FileUtil::JoinPath(cangjieHome, "modules"))) { diag.DiagnoseRefactor( DiagKindRefactor::frontend_failed_to_detect_cangjie_home, DEFAULT_POSITION, "invalid cjc home"); return false; } } return true; } bool CompilerInstance::DetectCangjieModules() { if (cangjieHome.empty()) { diag.DiagnoseRefactor(DiagKindRefactor::frontend_failed_to_detect_cangjie_modules, MakeRange(DEFAULT_POSITION, DEFAULT_POSITION), "cangjie home is empty"); return false; } auto libPathName = invocation.globalOptions.GetCangjieLibTargetPathName(); if (libPathName.empty()) { diag.DiagnoseRefactor(DiagKindRefactor::frontend_failed_to_detect_cangjie_modules, MakeRange(DEFAULT_POSITION, DEFAULT_POSITION), "target library path name is empty"); return false; } cangjieModules = FileUtil::JoinPath(FileUtil::JoinPath(cangjieHome, "modules"), libPathName); if (!FileUtil::FileExist(cangjieModules)) { diag.DiagnoseRefactor(DiagKindRefactor::frontend_failed_to_detect_cangjie_modules, MakeRange(DEFAULT_POSITION, DEFAULT_POSITION), "target library path is not exist : " + cangjieModules); return false; } return true; } bool CompilerInstance::ModularizeCompilation() { for (auto& objFile : importManager.GetUsedSTDLibFiles(DepType::DIRECT)) { invocation.globalOptions.directBuiltinDependencies.insert(objFile); } for (auto& objFile : importManager.GetUsedSTDLibFiles(DepType::INDIRECT)) { invocation.globalOptions.indirectBuiltinDependencies.insert(objFile); } return packageManager->ResolveDependence(pkgs); } bool CompilerInstance::DeserializeCHIR() { CHIR::CHIRBuilder chirBuilder(GetCHIRContext()); CHIR::ToCHIR::Phase phase; CHIR::CHIRDeserializer::Deserialize(invocation.frontendOptions.chirDeserializePath, chirBuilder, phase); // print serialize extension info which just for serialization not necessary for chir nodes CHIR::CHIRPrinter::PrintCHIRSerializeInfo(phase, "deserialized.chir"); CHIR::CHIRPrinter::PrintPackage(*GetCHIRContext().GetCurPackage(), "deserialized.chir"); return true; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND CHIR::CHIRContext& CompilerInstance::GetCHIRContext() { return chirData.GetCHIRContext(); } // used only by cjlint const CHIR::AnalysisWrapper& CompilerInstance::GetConstAnalysisWrapper() const { return chirData.GetConstAnalysisResult(); } std::vector CompilerInstance::GetAllCHIRPackages() const { return chirData.GetAllCHIRPackages(); } void CHIRData::InitData(std::unordered_map* fileNameMap, size_t threadNum) { cctx.SetFileNameMap(fileNameMap); cctx.SetThreadNum(threadNum); } CHIR::CHIRContext& CHIRData::GetCHIRContext() { return cctx; } void CHIRData::AppendNewPackage(CHIR::Package* package) { chirPkgs.emplace_back(package); } std::vector CHIRData::GetAllCHIRPackages() const { return chirPkgs; } CHIR::Package* CHIRData::GetCurrentCHIRPackage() const { if (chirPkgs.empty()) { return nullptr; } return chirPkgs[0]; } void CHIRData::SetImplicitFuncs(const std::unordered_map& funcs) { implicitFuncs = funcs; } std::unordered_map CHIRData::GetImplicitFuncs() const { return implicitFuncs; } void CHIRData::SetConstVarInitFuncs(const std::vector& funcs) { initFuncsForConstVar = funcs; } std::vector CHIRData::GetConstVarInitFuncs() const { return initFuncsForConstVar; } CHIR::AnalysisWrapper& CHIRData::GetConstAnalysisResultRef() { return constAnalysisWrapper; } const CHIR::AnalysisWrapper& CHIRData::GetConstAnalysisResult() const { return constAnalysisWrapper; } #endif cangjie_compiler-1.0.7/src/Frontend/CompilerInvocation.cpp000066400000000000000000000030611510705540100236650ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the CompilerInvocation. */ #include "cangjie/Frontend/CompilerInvocation.h" #include "cangjie/Driver/Backend/CJNATIVEBackend.h" #include "cangjie/Driver/DriverOptions.h" #include "cangjie/Macro/InvokeUtil.h" using namespace Cangjie; bool CompilerInvocation::ParseArgs(const std::vector& args) { if (!optionTable->ParseArgs(args, *argList)) { return false; } frontendOptions.SetFrontendMode(); if (!frontendOptions.ParseFromArgs(*argList)) { return false; } frontendOptions.SetCompilationCachedPath(); globalOptions = frontendOptions; return true; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::string CompilerInvocation::GetRuntimeLibPath(const std::string& relativePath) { auto runtimeLib = "libcangjie-runtime.so"; #ifdef _WIN64 runtimeLib = "libcangjie-runtime.dll"; #elif defined(__APPLE__) runtimeLib = "libcangjie-runtime.dylib"; #endif std::string& exePath = globalOptions.executablePath; std::string hostPathName = globalOptions.GetCangjieLibHostPathName(); auto basePath = FileUtil::JoinPath(FileUtil::GetDirPath(exePath), relativePath); auto runtimeLibPath = FileUtil::JoinPath(FileUtil::JoinPath(basePath, hostPathName), runtimeLib); return runtimeLibPath; } #endifcangjie_compiler-1.0.7/src/Frontend/FrontendOptions.cpp000066400000000000000000000052141510705540100232160ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the FrontendOptions. */ #include "cangjie/Frontend/FrontendOptions.h" #define OPTION_TRUE_ACTION(EXPR) [](FrontendOptions& opts, OptionArgInstance&) { (EXPR); return true; } using namespace Cangjie; namespace { std::unordered_map> g_actions = { {Options::ID::DUMP_TOKENS, OPTION_TRUE_ACTION(opts.dumpAction = FrontendOptions::DumpAction::DUMP_TOKENS)}, {Options::ID::DUMP_PARSE, OPTION_TRUE_ACTION(opts.dumpAction = FrontendOptions::DumpAction::DUMP_PARSE)}, {Options::ID::DUMP_AST, OPTION_TRUE_ACTION(opts.dumpAction = FrontendOptions::DumpAction::DUMP_AST)}, {Options::ID::DUMP_SYMBOLS, OPTION_TRUE_ACTION(opts.dumpAction = FrontendOptions::DumpAction::DUMP_SYMBOLS)}, {Options::ID::TYPE_CHECK, OPTION_TRUE_ACTION(opts.dumpAction = FrontendOptions::DumpAction::TYPE_CHECK)}, {Options::ID::DUMP_MACRO, OPTION_TRUE_ACTION(opts.dumpAction = FrontendOptions::DumpAction::DUMP_MACRO)}, {Options::ID::DUMP_IR, OPTION_TRUE_ACTION(opts.dumpAction = FrontendOptions::DumpAction::DUMP_IR)}, {Options::ID::DUMP_BC, OPTION_TRUE_ACTION(opts.dumpAction = FrontendOptions::DumpAction::DUMP_BC)}, // DUMP_DEPENDENT_PACKAGE (--scan-dependency option) should be handled with extra care. The behavior of // the option has defined in GlobalOptions. Since, we are overriding its behavior here, we need to // set `scanDepPkg` field again. {Options::ID::DUMP_DEPENDENT_PACKAGE, [](FrontendOptions& opts, OptionArgInstance& /* arg */) { opts.scanDepPkg = true; opts.dumpAction = FrontendOptions::DumpAction::DUMP_DEP_PKG; return true; }}, {Options::ID::DESERIALIZE_CHIR_AND_DUMP, [](FrontendOptions& opts, [[maybe_unused]] const OptionArgInstance& arg) { opts.chirDeserialize = true; opts.chirDumpDebugMode = true; opts.chirDeserializePath = arg.value; opts.dumpAction = FrontendOptions::DumpAction::DESERIALIZE_CHIR; return true; }}, {Options::ID::COMPILE_CJD, OPTION_TRUE_ACTION(opts.compileCjd = true)}, }; } std::optional FrontendOptions::ParseOption(OptionArgInstance& arg) { if (g_actions.find(arg.info.GetID()) == g_actions.end()) { return GlobalOptions::ParseOption(arg); } return {g_actions[arg.info.GetID()](*this, arg)}; } cangjie_compiler-1.0.7/src/Frontend/MergeAnnoFromCjd.cpp000066400000000000000000000473221510705540100232110ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the MergeCusAnno. */ #include "MergeAnnoFromCjd.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/PrintNode.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Utils/Casting.h" #include "cangjie/Utils/ICEUtil.h" #include "cangjie/Utils/SafePointer.h" using namespace Cangjie; using namespace Cangjie::AST; namespace { void ExpandToAPILevel(MacroInvocation& invocation) { auto cache = std::find_if(invocation.decl->annotations.begin(), invocation.decl->annotations.end(), [](auto& anno) { return anno && anno->identifier.Val() == "APILevel"; }); if (cache != invocation.decl->annotations.end()) { return; } auto expandedDecl = invocation.decl.get(); auto apilevelAnno = MakeOwned(); apilevelAnno->identifier = SrcIdentifier("APILevel"); std::string lastIdentifier; for (size_t i = 0; i < invocation.attrs.size(); ++i) { if (invocation.attrs[i].kind == TokenKind::IDENTIFIER) { lastIdentifier = invocation.attrs[i].Value(); } // Only support string for now, syscap must be a string literal. if (invocation.attrs[i].kind == TokenKind::STRING_LITERAL) { apilevelAnno->args.emplace_back( CreateFuncArg(CreateLitConstExpr(LitConstKind::STRING, invocation.attrs[i].Value(), Ty::GetInitialTy()), lastIdentifier)); } if (invocation.attrs[i].kind == TokenKind::INTEGER_LITERAL) { apilevelAnno->args.emplace_back(CreateFuncArg( CreateLitConstExpr(LitConstKind::INTEGER, invocation.attrs[i].Value(), Ty::GetInitialTy()), lastIdentifier)); } if (invocation.attrs[i].kind == TokenKind::BOOL_LITERAL) { apilevelAnno->args.emplace_back( CreateFuncArg(CreateLitConstExpr(LitConstKind::BOOL, invocation.attrs[i].Value(), Ty::GetInitialTy()), lastIdentifier)); } } apilevelAnno->kind = AnnotationKind::CUSTOM; expandedDecl->annotations.emplace_back(std::move(apilevelAnno)); } bool IsSameType(Ptr lt, Ptr rty); // Only extend's extendedType or class/struct/enum/interface/extend's inheritedType can be in. bool IsSameType(Ptr lt, Ptr rt) { CJC_ASSERT(Ty::IsTyCorrect(rt->ty)); switch (lt->astKind) { case ASTKind::REF_TYPE: { auto lrt = StaticCast(lt); if (rt->astKind != ASTKind::REF_TYPE) { return false; } auto rrtName = rt->ty->IsPrimitive() ? rt->ty->String() : rt->ty->name; if (lrt->ref.identifier.Val() != rrtName) { return false; } if (lrt->typeArguments.size() != rt->ty->typeArgs.size()) { return false; } for (size_t i = 0; i < lrt->typeArguments.size(); ++i) { if (!IsSameType(lrt->typeArguments[i].get(), rt->ty->typeArgs[i])) { return false; } } break; } case ASTKind::QUALIFIED_TYPE: { auto lqt = StaticCast(lt); if (rt->astKind != ASTKind::QUALIFIED_TYPE) { return false; } auto rqt = StaticCast(rt); if (lqt->field.Val() != rqt->field.Val()) { return false; } if (!IsSameType(lqt->baseType.get(), rqt->baseType.get())) { return false; } break; } case ASTKind::OPTION_TYPE: { if (!IsSameType(lt, rt->ty)) { return false; } break; } case ASTKind::PRIMITIVE_TYPE: { auto lpt = StaticCast(lt); auto lptName = lpt->str; auto rtyName = rt->ty->IsPrimitive() ? rt->ty->String() : rt->ty->name; lptName = lptName == "Rune" ? "UInt8" : lptName; rtyName = rtyName == "Rune" ? "UInt8" : rtyName; if (lptName != rtyName) { return false; } if (lpt->kind != rt->ty->kind) { return false; } break; } default: break; } return true; } bool IsSameType(Ptr lt, Ptr rty) { switch (lt->astKind) { case ASTKind::REF_TYPE: { auto lrt = StaticCast(lt); auto rtyName = rty->IsPrimitive() ? rty->String() : rty->name; // If the identifier is the name after the type alias, the comparison will not succeed. if (lrt->ref.identifier.Val() != rtyName) { return false; } if (lrt->typeArguments.size() != rty->typeArgs.size()) { return false; } for (size_t i = 0; i < lrt->typeArguments.size(); ++i) { if (!IsSameType(lrt->typeArguments[i].get(), rty->typeArgs[i])) { return false; } } break; } case ASTKind::QUALIFIED_TYPE: { auto lqt = StaticCast(lt); if (lqt->field.Val() != rty->name) { return false; } break; } case ASTKind::FUNC_TYPE: { if (!rty->IsFunc()) { return false; } auto lft = StaticCast(lt); auto rfty = StaticCast(rty); if (lft->paramTypes.size() != rfty->paramTys.size()) { return false; } for (size_t i = 0; i < lft->paramTypes.size(); ++i) { if (!IsSameType(lft->paramTypes[i].get(), rfty->paramTys[i])) { return false; } } if (!IsSameType(lft->retType.get(), rfty->retTy)) { return false; } break; } case ASTKind::TUPLE_TYPE: { if (!rty->IsTuple()) { return false; } auto ltt = StaticCast(lt); auto rtty = StaticCast(rty); if (ltt->fieldTypes.size() != rtty->typeArgs.size()) { return false; } for (size_t i = 0; i < ltt->fieldTypes.size(); ++i) { if (!IsSameType(ltt->fieldTypes[i].get(), rtty->typeArgs[i])) { return false; } } break; } case ASTKind::OPTION_TYPE: { if (!rty->IsCoreOptionType()) { return false; } auto lot = StaticCast(lt); auto roty = StaticCast(rty); if (!IsSameType(lot->componentType.get(), roty->typeArgs[0])) { return false; } break; } case ASTKind::VARRAY_TYPE: { if (rty->kind != TypeKind::TYPE_VARRAY) { return false; } auto lat = StaticCast(lt); auto rat = StaticCast(rty); if (!IsSameType(lat->typeArgument.get(), rat->typeArgs[0])) { return false; } auto lct = DynamicCast(lat->constantType.get()); CJC_NULLPTR_CHECK(lct); auto lce = DynamicCast(lct->constantExpr.get()); CJC_NULLPTR_CHECK(lce); if (lce->stringValue != std::to_string(rat->size)) { return false; } break; } case ASTKind::PRIMITIVE_TYPE: { if (!rty->IsPrimitive()) { return false; } auto lpt = StaticCast(lt); auto rpty = StaticCast(rty); if (lpt->str != rpty->String()) { return false; } break; } default: break; } return true; } bool IsSameGeneric(Ptr lg, Ptr rg) { if (lg->typeParameters.size() != rg->typeParameters.size()) { return false; } for (size_t i = 0; i < lg->typeParameters.size(); ++i) { if (lg->typeParameters[i]->identifier.Val() != rg->typeParameters[i]->identifier.Val()) { return false; } } // The extension declaration inserts the upper bound one more time. if (lg->genericConstraints.empty() != rg->genericConstraints.empty()) { return false; } if (lg->genericConstraints.size() > rg->genericConstraints.size()) { return false; } auto& lgc = lg->genericConstraints; auto& rgc = rg->genericConstraints; for (size_t i = 0; i < lg->genericConstraints.size(); ++i) { if (!IsSameType(lgc[i]->type.get(), rgc[i]->type->ty)) { return false; } if (lgc[i]->upperBounds.size() != rgc[i]->upperBounds.size()) { return false; } for (size_t j = 0; j < lgc[i]->upperBounds.size(); ++j) { if (!IsSameType(lgc[i]->upperBounds[j].get(), rgc[i]->upperBounds[j]->ty)) { return false; } } } return true; } bool IsSameFuncByIdentifier(Ptr lb, Ptr rb) { if ((lb->generic == nullptr) != (rb->generic == nullptr)) { return false; } if (lb->generic && !IsSameGeneric(lb->generic.get(), rb->generic.get())) { return false; } if (lb->paramLists[0]->params.size() != rb->paramLists[0]->params.size()) { return false; } for (size_t i = 0; i < lb->paramLists[0]->params.size(); ++i) { Ptr expandedParam = lb->paramLists[0]->params[i].get(); if (auto mep = DynamicCast(lb->paramLists[0]->params[i].get()); mep && mep->invocation.identifier == "APILevel") { ExpandToAPILevel(mep->invocation); CopyBasicInfo(mep, mep->invocation.decl->annotations.back().get()); expandedParam = StaticCast(mep->invocation.decl.get()); } if (expandedParam->identifier.Val() != rb->paramLists[0]->params[i]->identifier.Val()) { return false; } if (!IsSameType(expandedParam->type.get(), rb->paramLists[0]->params[i]->ty)) { return false; } CJC_ASSERT(rb->ty->IsFunc()); auto lFuncTy = StaticCast(rb->ty); if (lb->retType && !IsSameType(lb->retType.get(), lFuncTy->retTy)) { return false; } } return true; } bool IsSameDeclByIdentifier(Ptr l, Ptr r); void ColllectPattern(Ptr pattern, std::unordered_map, Ptr>& topDeclMapping, std::vector>& annos) { switch (pattern->astKind) { case ASTKind::VAR_PATTERN: { auto vp = StaticCast(pattern); for (auto& anno : annos) { vp->varDecl->annotations.emplace_back(ASTCloner::Clone(anno.get())); } topDeclMapping.emplace(vp->varDecl.get(), nullptr); break; } case ASTKind::TUPLE_PATTERN: { auto ltp = StaticCast(pattern); for (size_t i = 0; i < ltp->patterns.size(); ++i) { ColllectPattern(ltp->patterns[i].get(), topDeclMapping, annos); } break; } case ASTKind::ENUM_PATTERN: { auto lep = StaticCast(pattern); for (size_t i = 0; i < lep->patterns.size(); ++i) { ColllectPattern(lep->patterns[i].get(), topDeclMapping, annos); } break; } case ASTKind::VAR_OR_ENUM_PATTERN: { auto lvep = StaticCast(pattern); ColllectPattern(lvep->pattern.get(), topDeclMapping, annos); break; } case ASTKind::TYPE_PATTERN: case ASTKind::EXCEPT_TYPE_PATTERN: default: break; } } // NOTE: l from .cj.d, r from .cjo. bool IsSameDeclByIdentifier(Ptr l, Ptr r) { auto lId = l->astKind == ASTKind::PRIMARY_CTOR_DECL ? "init" : l->identifier.Val(); auto lKind = l->astKind == ASTKind::PRIMARY_CTOR_DECL ? ASTKind::FUNC_DECL : l->astKind; bool fastQuit = lId != r->identifier.Val() || lKind != r->astKind || (l->generic == nullptr) != (r->generic == nullptr); if (fastQuit) { return false; } if (l->generic && !IsSameGeneric(l->generic.get(), r->generic.get())) { return false; } switch (l->astKind) { case ASTKind::FUNC_DECL: { auto lfd = StaticCast(l); auto rfd = StaticCast(r); if (!IsSameFuncByIdentifier(lfd->funcBody.get(), rfd->funcBody.get())) { return false; } break; } case ASTKind::PRIMARY_CTOR_DECL: { auto pcd = StaticCast(l); auto fd = StaticCast(r); if (!IsSameFuncByIdentifier(pcd->funcBody.get(), fd->funcBody.get())) { return false; } break; } case ASTKind::EXTEND_DECL: { auto led = StaticCast(l); auto red = StaticCast(r); if (!IsSameType(led->extendedType.get(), red->extendedType.get())) { return false; } if (led->inheritedTypes.size() != red->inheritedTypes.size()) { return false; } for (size_t i = 0; i < led->inheritedTypes.size(); ++i) { if (!IsSameType(led->inheritedTypes[i].get(), red->inheritedTypes[i].get())) { return false; } } break; } case ASTKind::MACRO_EXPAND_DECL: { auto lmed = StaticCast(l); auto rmed = StaticCast(r); if (lmed->invocation.fullName != rmed->invocation.fullName || lmed->invocation.identifier != rmed->invocation.identifier) { return false; } break; } case ASTKind::VAR_DECL: case ASTKind::PROP_DECL: { auto lvd = StaticCast(l); auto rvd = StaticCast(r); if (lvd->type && !IsSameType(lvd->type.get(), rvd->ty)) { return false; } break; } default: break; } return true; } /** * 1) Type declarations (class, struct, enum, interface) * 2) Extension declarations * 3) Top-level function declarations * 4) Top-level variable declarations */ void MergeTopLevelDecl( Ptr target, Ptr source, std::unordered_map, Ptr>& topDeclMapping) { auto topDeclMapInsert = [&topDeclMapping](OwnedPtr& toplevelDecl) { Ptr expandedDecl = toplevelDecl.get(); if (auto med = DynamicCast(toplevelDecl.get()); med && med->invocation.identifier == "APILevel") { ExpandToAPILevel(med->invocation); CopyBasicInfo(med, med->invocation.decl->annotations.back().get()); expandedDecl = med->invocation.decl.get(); } if (expandedDecl->astKind == ASTKind::MAIN_DECL) { return; } if (auto vwpd = DynamicCast(expandedDecl.get())) { ColllectPattern(vwpd->irrefutablePattern.get(), topDeclMapping, vwpd->annotations); return; } topDeclMapping.emplace(expandedDecl, nullptr); }; IterateToplevelDecls(*source, topDeclMapInsert); auto topDeclMapMatch = [&topDeclMapping](OwnedPtr& toplevelDecl) { // Short circuit. if (toplevelDecl->astKind == ASTKind::BUILTIN_DECL || !toplevelDecl->IsExportedDecl()) { return; } auto found = std::find_if(topDeclMapping.begin(), topDeclMapping.end(), [&toplevelDecl](auto l) { if (l.second) { return false; } return IsSameDeclByIdentifier(l.first, toplevelDecl); }); if (found == topDeclMapping.end()) { return; } found->second = toplevelDecl; for (auto& anno : found->first->annotations) { if (anno->kind != AnnotationKind::CUSTOM) { continue; } found->second->annotations.emplace_back(std::move(anno)); } found->first->annotations.clear(); }; IterateToplevelDecls(*target, topDeclMapMatch); } /** * 1) Constructor declarations * 2) Member function declarations * 3) Member variable declarations * 4) Member property declarations * 5) Enum constructor declarations */ void MergeMemberDecl(std::pair, Ptr>& declPair) { std::unordered_map, Ptr> memberMapping; for (auto& member : declPair.first->GetMemberDeclPtrs()) { Ptr extendedDecl = member; if (auto med = DynamicCast(member); med && med->invocation.identifier == "APILevel") { ExpandToAPILevel(med->invocation); CopyBasicInfo(med, med->invocation.decl->annotations.back().get()); extendedDecl = med->invocation.decl.get(); } memberMapping.emplace(extendedDecl, nullptr); } for (auto& member : declPair.second->GetMemberDeclPtrs()) { auto found = std::find_if(memberMapping.begin(), memberMapping.end(), [&member](auto l) { return IsSameDeclByIdentifier(l.first, member); }); if (found == memberMapping.end()) { continue; } found->second = member; for (auto& anno : found->first->annotations) { if (anno->kind != AnnotationKind::CUSTOM) { continue; } found->second->annotations.emplace_back(std::move(anno)); } found->first->annotations.clear(); } // 3. Parameters in member functions/constructors for (auto memberPair : memberMapping) { if (!memberPair.second) { continue; } if (!memberPair.first->IsFunc()) { continue; } auto s = StaticCast(memberPair.first); auto t = StaticCast(memberPair.second); for (size_t i = 0; i < s->funcBody->paramLists[0]->params.size(); ++i) { Ptr expandedParam = s->funcBody->paramLists[0]->params[i].get(); if (auto mep = DynamicCast(s->funcBody->paramLists[0]->params[i].get()); mep && mep->invocation.identifier == "APILevel") { ExpandToAPILevel(mep->invocation); CopyBasicInfo(mep, mep->invocation.decl->annotations.back().get()); expandedParam = StaticCast(mep->invocation.decl.get()); } for (auto& anno : expandedParam->annotations) { if (anno->kind != AnnotationKind::CUSTOM) { continue; } t->funcBody->paramLists[0]->params[i]->annotations.emplace_back(std::move(anno)); } expandedParam->annotations.clear(); } } } } // namespace void Cangjie::MergeCusAnno(Ptr target, Ptr source) { // source to target, source from '.cj.d', target from '.cjo'. std::unordered_map, Ptr> topDeclMapping; MergeTopLevelDecl(target, source, topDeclMapping); for (auto declPair : topDeclMapping) { if (!declPair.second) { continue; } MergeMemberDecl(declPair); } } cangjie_compiler-1.0.7/src/Frontend/MergeAnnoFromCjd.h000066400000000000000000000010731510705540100226470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares MergeCusAnno. */ #ifndef CANGJIE_MERGE_ANNO_FROM_CJD_H #define CANGJIE_MERGE_ANNO_FROM_CJD_H #include "cangjie/AST/Node.h" #include "cangjie/Basic/DiagnosticEngine.h" namespace Cangjie { void MergeCusAnno(Ptr target, Ptr source); } #endif cangjie_compiler-1.0.7/src/Frontend/PrintSymbolTable.cpp000066400000000000000000000203601510705540100233140ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the symbol table printing function. */ #include "PrintSymbolTable.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Symbol.h" #include "cangjie/Basic/Print.h" using namespace Cangjie; using namespace AST; namespace { constexpr unsigned int ONE_INDENT = 1; constexpr unsigned int TWO_INDENT = 2; constexpr unsigned int THREE_INDENT = 3; constexpr unsigned int FOUR_INDENT = 4; constexpr unsigned int FIVE_INDENT = 5; template inline void PrintIndentNoSplitNoEndl(const unsigned int indent, const Args... args) { PrintIndentOnly(indent); PrintNoSplit(args...); } template inline void PrintIndentNoSplit(const unsigned int indent, const Args... args) { PrintIndentNoSplitNoEndl(indent, args...); Println(); } void PrintPackage(const Symbol& symbol) { PrintIndent(TWO_INDENT, "{"); PrintIndentNoSplit(THREE_INDENT, "\"name\": \"", symbol.name, "\","); PrintIndent(THREE_INDENT, "\"files\": ["); Ptr node = symbol.node; CJC_NULLPTR_CHECK(node); CJC_ASSERT(node->astKind == ASTKind::PACKAGE); Ptr package = StaticAs(node); auto iter = package->files.cbegin(); PrintIndentNoSplitNoEndl(FOUR_INDENT, "\"", (*iter)->filePath, "\""); ++iter; std::for_each(iter, package->files.cend(), [](auto& file) { Println(","); PrintIndentNoSplitNoEndl(FOUR_INDENT, "\"", file->filePath, "\""); }); Println(); PrintIndent(THREE_INDENT, "]"); PrintIndentNoSplitNoEndl(TWO_INDENT, "}"); } void PrintPackages(const std::vector>& packages) { if (packages.empty()) { return; } std::unordered_set packageNames; auto iter = packages.cbegin(); packageNames.emplace((*iter).get().name); PrintPackage(*iter); ++iter; std::for_each(iter, packages.cend(), [&packageNames](const Symbol& sym) { if (packageNames.emplace(sym.name).second) { Println(","); PrintPackage(sym); } }); Println(); } void PrintPosition(unsigned int indent, const Position& pos, const std::string& name, bool hasComma) { PrintIndentNoSplit(indent, "\"", name, "\": {"); PrintIndentNoSplit(indent + ONE_INDENT, "\"line\": ", pos.line, ","); PrintIndent(indent + ONE_INDENT, "\"column\":", pos.column); PrintIndentNoSplit(indent, "}", hasComma ? "," : ""); } void PrintPackageSpecPart(const PackageSpec& packageSpec) { PrintIndentNoSplit(FIVE_INDENT, "\"packageName\": \"", packageSpec.packageName.Val(), "\","); PrintPosition(FIVE_INDENT, packageSpec.macroPos, "macroPos", true); PrintPosition(FIVE_INDENT, packageSpec.packagePos, "packagePos", true); PrintPosition(FIVE_INDENT, packageSpec.packageName.Begin(), "packageNamePos", true); } void PrintImportSpecPart(const ImportSpec& importSpec) { PrintPosition(FIVE_INDENT, importSpec.importPos, "importPos", true); PrintIndentNoSplit(FIVE_INDENT, "\"packageName\": \"", GetImportedItemFullName(importSpec.content), "\","); PrintPosition(FIVE_INDENT, importSpec.content.begin, "packageNamePos", true); if (importSpec.IsImportAlias()) { PrintPosition(FIVE_INDENT, importSpec.content.asPos, "asPos", true); PrintIndentNoSplit(FIVE_INDENT, "\"asIdentifier\": \"", importSpec.content.aliasName.Val(), "\","); PrintPosition(FIVE_INDENT, importSpec.content.aliasName.Begin(), "asIdentifierPos", true); } } void PrintDeclPart(const Decl& decl) { PrintIndentNoSplit(FIVE_INDENT, "\"identifier\": \"", decl.identifier.Val(), "\","); PrintPosition(FIVE_INDENT, decl.identifier.Begin(), "identifierPos", true); } void PrintNode(const Node& node) { if (node.TestAttr(Attribute::COMPILER_ADD)) { return; } PrintIndent(FOUR_INDENT, "{"); PrintIndentNoSplit(FIVE_INDENT, "\"astKind\": \"", ASTKIND_TO_STRING_MAP[node.astKind], "\","); PrintIndentNoSplit(FIVE_INDENT, "\"name\": \"", node.symbol ? node.symbol->name : "", "\","); if (auto packageSpec = DynamicCast(&node); packageSpec) { PrintPackageSpecPart(*packageSpec); } else if (auto importSpec = DynamicCast(&node); importSpec) { PrintImportSpecPart(*importSpec); } else if (auto decl = DynamicCast(&node); decl) { PrintDeclPart(*decl); } PrintPosition(FIVE_INDENT, node.begin, "begin", true); PrintPosition(FIVE_INDENT, node.end, "end", false); PrintIndentNoSplitNoEndl(FOUR_INDENT, "}"); } void PrintFile(const File& file, const std::vector>& nodes) { PrintIndent(TWO_INDENT, "{"); PrintIndentNoSplit(THREE_INDENT, "\"path\": \"", file.filePath, "\","); PrintIndentNoSplit(THREE_INDENT, "\"symbols\": ["); if (!nodes.empty()) { auto iter = nodes.cbegin(); PrintNode(*iter); ++iter; std::for_each(iter, nodes.cend(), [](const Node& node) { Println(","); PrintNode(node); }); Println(); } PrintIndentNoSplit(THREE_INDENT, "]"); PrintIndentNoSplitNoEndl(TWO_INDENT, "}"); } void PrintFiles( const std::unordered_map, std::vector>>& file2nodesMap) { if (file2nodesMap.empty()) { return; } auto iter = file2nodesMap.cbegin(); PrintFile(*(*iter).first, (*iter).second); ++iter; std::for_each(iter, file2nodesMap.cend(), [](const std::pair, std::vector>>& pair) { Println(","); PrintFile(*pair.first, pair.second); }); Println(); } void HandleFileNode(std::unordered_map, std::vector>>& file2nodesMap, const Symbol& symbol, const Node& node) { if (node.astKind != ASTKind::FILE) { return; } if (&symbol == node.symbol) { return; } const File& file = static_cast(node); std::vector>& nodes = file2nodesMap.emplace(std::piecewise_construct, std::make_tuple(&file), std::make_tuple()).first->second; if (file.package) { nodes.push_back(*file.package); } for (auto& import : file.imports) { if (import->importPos.line != 0) { nodes.push_back(*import); } } } void AddSymbolToInfo(std::vector>& packages, std::unordered_map, std::vector>>& file2nodesMap, std::optional>& fileOpt, const Symbol& symbol) { CJC_NULLPTR_CHECK(symbol.node); Node& node = *symbol.node; if (node.TestAttr(Attribute::COMPILER_ADD)) { return; } if (node.astKind == ASTKind::PACKAGE) { packages.push_back(symbol); } else { if (node.astKind == ASTKind::FILE) { fileOpt = static_cast(node); } CJC_ASSERT(fileOpt); file2nodesMap.emplace(std::piecewise_construct, std::make_tuple(&fileOpt->get()), std::make_tuple()) .first->second.push_back(node); HandleFileNode(file2nodesMap, symbol, node); } } } // namespace void Cangjie::PrintSymbolTable(CompilerInstance& ci) { std::vector> packages; std::unordered_map, std::vector>> file2nodesMap; for (auto srcPkg : ci.GetSourcePackages()) { ASTContext* ctx = ci.GetASTContextByPackage(srcPkg); CJC_NULLPTR_CHECK(ctx); std::optional> fileOpt; for (auto& sym : std::as_const(ctx->symbolTable)) { AddSymbolToInfo(packages, file2nodesMap, fileOpt, *sym); } } Println("{"); PrintIndent(ONE_INDENT, "\"packages\": ["); PrintPackages(packages); PrintIndent(ONE_INDENT, "],"); PrintIndent(ONE_INDENT, "\"files\": ["); PrintFiles(file2nodesMap); PrintIndent(ONE_INDENT, "]"); Println("}"); } cangjie_compiler-1.0.7/src/Frontend/PrintSymbolTable.h000066400000000000000000000057261510705540100227720ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the symbol table printing function. */ #ifndef CANGJIE_AST_PRINTSYMBOLTABLE_H #define CANGJIE_AST_PRINTSYMBOLTABLE_H #include "cangjie/Frontend/CompilerInstance.h" namespace Cangjie { /** * Print the symbol tables of the compiler instance. * * The output is formatted as JSON and the following schema is required: * * { * "packages": [ * { * "name": , * "files": [ * * ] * } * ], * "files": [ * { * "path": , * "symbols": [ * { * "astKind": , * "name": , * if astKind = package_spec: * "packageName": , * "packagePos": { * "line": , * "column": * }, * "packageNamePos": { * "line": , * "column": * }, * endif * if astKind = import_spec: * if has from keyword: * "fromPos": { * "line": , * "column": * }, * "moduleName": , * "modulePos": { * "line": , * "column": * }, * endif * "importPos": { * "line": , * "column": * }, * "packageName": , * "PackageNamePos": { * "line": , * "column": * }, * "importedItemName": , * "importedItemNamePos": { * "line": , * "column": * }, * if has as keyword: * "asPos": { * "line": , * "column": * }, * "asIdentifier": , * "asIdentifierPos": { * "line": , * "column": * }, * endif * endif * if astKind = *decl * "identifier": , * "identifierPos": { * "line": , * "column": * }, * endif * "begin": { * "line": , * "column": * }, * "end": { * "line": , * "column": * } * } * ] * } * ] * } */ void PrintSymbolTable(CompilerInstance& ci); } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/FrontendTool/000077500000000000000000000000001510705540100202135ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/FrontendTool/CMakeLists.txt000066400000000000000000000016161510705540100227570ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set(FRONTENDTOOL_SRC FrontendTool.cpp DefaultCompilerInstance.cpp IncrementalCompilerInstance.cpp) add_library(CangjieFrontendTool OBJECT ${FRONTENDTOOL_SRC}) add_dependencies(CangjieFrontendTool CangjieFlatbuffersHeaders) target_include_directories(CangjieFrontendTool PRIVATE ${FLATBUFFERS_INCLUDE_DIR}) # use llvm if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_dependencies(CangjieFrontendTool cjnative) endif() target_include_directories(CangjieFrontendTool PRIVATE ${BOUNDSCHECK}/include) target_include_directories(CangjieFrontendTool PRIVATE ${LLVM_INCLUDE_DIRS}) target_compile_options(CangjieFrontendTool PRIVATE ${CJC_WITH_LLVM_EXTRA_WARNINGS}) cangjie_compiler-1.0.7/src/FrontendTool/DefaultCompilerInstance.cpp000066400000000000000000000337501510705540100254730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the DefaultCompilerInstance. */ #include "cangjie/FrontendTool/DefaultCompilerInstance.h" #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/IR/Verifier.h" #include "cangjie/Basic/StringConvertor.h" #include "cangjie/CodeGen/EmitPackageIR.h" #include "cangjie/Driver/StdlibMap.h" #include "cangjie/Driver/TempFileManager.h" #include "cangjie/Modules/PackageManager.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/ProfileRecorder.h" #include "cangjie/AST/Walker.h" #if (defined RELEASE) #include "cangjie/Utils/Signal.h" #endif using namespace Cangjie; using namespace AST; namespace Cangjie { class DefaultCIImpl final { public: explicit DefaultCIImpl(DefaultCompilerInstance& ref) : ci{ref} { } ~DefaultCIImpl(); bool PerformCodeGen(); bool PerformCjoAndBchirSaving(); void DumpDepPackage(); void DumpIR(); void DumpBC(); bool SaveCjoAndBchir(AST::Package& pkg); bool SaveCjo(const AST::Package& pkg); void RearrangeImportedPackageDependence(); bool CodegenOnePackage(AST::Package& pkg, bool enableIncrement); private: DefaultCompilerInstance& ci; bool EmitLLVMSimilarBytecode(AST::Package& pkg, bool enableIncrement); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void SaveBchir([[maybe_unused]] const AST::Package& pkg) const { } #endif std::string GenerateFileName(const std::string& fullPackageName, const std::string& idx) const; std::string GenerateBCFilePathAndUpdateToInvocation( const TempFileKind& kind, const std::string& pkgName, const std::string& idx = ""); std::vector> llvmModules; }; DefaultCompilerInstance::DefaultCompilerInstance(CompilerInvocation& invocation, DiagnosticEngine& diag) : CompilerInstance(invocation, diag), impl{new DefaultCIImpl{*this}} { buildTrie = false; } DefaultCIImpl::~DefaultCIImpl() { CodeGen::ClearPackageModules(llvmModules); } DefaultCompilerInstance::~DefaultCompilerInstance() { delete impl; } bool DefaultCompilerInstance::PerformParse() { Utils::ProfileRecorder recorder("Main Stage", "Parser"); return CompilerInstance::PerformParse(); } bool DefaultCompilerInstance::PerformConditionCompile() { Utils::ProfileRecorder recorder("Main Stage", "ConditionalCompilation"); return CompilerInstance::PerformConditionCompile(); } bool DefaultCompilerInstance::PerformImportPackage() { Utils::ProfileRecorder recorder("Main Stage", "ImportPackages"); return CompilerInstance::PerformImportPackage(); } bool DefaultCompilerInstance::PerformMacroExpand() { Utils::ProfileRecorder recorder("Main Stage", "MacroExpand"); return CompilerInstance::PerformMacroExpand(); } bool DefaultCompilerInstance::PerformSema() { Utils::ProfileRecorder recorder("Main Stage", "Semantic"); return CompilerInstance::PerformSema(); } bool DefaultCompilerInstance::PerformOverflowStrategy() { Utils::ProfileRecorder recorder("Main Stage", "Overflow Strategy"); return CompilerInstance::PerformOverflowStrategy(); } bool DefaultCompilerInstance::PerformDesugarAfterSema() { Utils::ProfileRecorder recorder("Main Stage", "Desugar after Sema"); return CompilerInstance::PerformDesugarAfterSema(); } bool DefaultCompilerInstance::PerformGenericInstantiation() { Utils::ProfileRecorder recorder("Main Stage", "Generic Instantiation"); return CompilerInstance::PerformGenericInstantiation(); } bool DefaultCompilerInstance::PerformCHIRCompilation() { Utils::ProfileRecorder recorder("Main Stage", "CHIR"); return CompilerInstance::PerformCHIRCompilation(); } std::string DefaultCIImpl::GenerateFileName(const std::string& fullPackageName, const std::string& idx) const { std::string fileName; auto pkgNameSuffix = FileUtil::ToCjoFileName(fullPackageName); if (ci.invocation.globalOptions.compilePackage) { fileName = (idx.empty() ? "" : (idx + "-")) + pkgNameSuffix; } else if (fullPackageName != DEFAULT_PACKAGE_NAME) { fileName = (idx.empty() ? "" : (idx + "-")) + pkgNameSuffix; } else if (ci.invocation.globalOptions.srcFiles.empty()) { fileName = (idx.empty() ? "" : (idx + "-")) + pkgNameSuffix; } else { fileName = (idx.empty() ? "" : (idx + "-")) + FileUtil::GetFileNameWithoutExtension(ci.invocation.globalOptions.srcFiles[0]); } return fileName; } std::string DefaultCIImpl::GenerateBCFilePathAndUpdateToInvocation( const TempFileKind& kind, const std::string& pkgName, const std::string& idx) { std::string fileName = GenerateFileName(pkgName, idx); TempFileInfo bcFileInfo = TempFileManager::Instance().CreateNewFileInfo(TempFileInfo{fileName, "", "", true}, kind); ci.invocation.globalOptions.frontendOutputFiles.emplace_back(bcFileInfo); auto bcFilePath = bcFileInfo.filePath; if (FileUtil::FileExist(bcFilePath) && !FileUtil::Remove(bcFilePath)) { Errorln("The file " + bcFilePath + " already exists, but it fails to be removed before being updated."); return ""; } if (auto dir{FileUtil::GetDirPath(bcFilePath)}; !FileUtil::FileExist(dir) && FileUtil::CreateDirs(dir + "/") != 0) { Errorln("The directory " + dir + " fails to be created before creating " + bcFilePath); return ""; } // If file is deleted, no more data written to the file. if (TempFileManager::Instance().IsDeleted()) { return ""; } return bcFilePath; } bool DefaultCIImpl::SaveCjo(const AST::Package& pkg) { if (pkg.IsEmpty()) { return true; } auto pkgName = FileUtil::ToCjoFileName(pkg.fullPackageName); // If compiled with the `-g` or '--coverage', files should be saved with absolute paths. // When compiling stdlib without options '--coverage', do not save file with abs path. // Then can not debugging stdlib with abs path. bool saveFileWithAbsPath = ci.invocation.globalOptions.enableCompileDebug || ci.invocation.globalOptions.enableCoverage; if ((STANDARD_LIBS.find(pkg.fullPackageName) != STANDARD_LIBS.end())) { saveFileWithAbsPath = ci.invocation.globalOptions.enableCoverage; } std::vector astData; Utils::ProfileRecorder::Start("Save cjo and bchir", "Serialize ast"); ci.importManager.ExportAST(saveFileWithAbsPath, astData, pkg); Utils::ProfileRecorder::Stop("Save cjo and bchir", "Serialize ast"); // Write astData into file according to given package name by '--output' opt. TempFileInfo astFileInfo = TempFileManager::Instance().CreateNewFileInfo(TempFileInfo{pkgName, ""}, TempFileKind::O_CJO); std::string astFileName = astFileInfo.filePath; Utils::ProfileRecorder::Start("Save cjo and bchir", "Save ast"); bool res = FileUtil::WriteBufferToASTFile(astFileName, astData); Utils::ProfileRecorder::Stop("Save cjo and bchir", "Save ast"); if (!res) { Errorln("fail to generate file: " + astFileName); } return res; } bool DefaultCIImpl::SaveCjoAndBchir(Package& pkg) { SaveBchir(pkg); return SaveCjo(pkg); } bool DefaultCIImpl::CodegenOnePackage(Package& pkg, bool enableIncrement) { if (pkg.IsEmpty()) { return true; } if (ci.invocation.globalOptions.disableCodeGen) { return true; } auto backend = ci.invocation.globalOptions.backend; switch (backend) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND case Triple::BackendType::CJNATIVE: { if (!EmitLLVMSimilarBytecode(pkg, enableIncrement)) { return false; } break; } #endif case Triple::BackendType::UNKNOWN: { Errorln("unknown backend"); break; } default: return false; } #ifdef SIGNAL_TEST // The interrupt signal triggers the function. In normal cases, this function does not take effect. Cangjie::SignalTest::ExecuteSignalTestCallbackFunc(Cangjie::SignalTest::TriggerPointer::CODEGEN_POINTER); #endif return true; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool DefaultCIImpl::EmitLLVMSimilarBytecode(Package& pkg, bool enableIncrement) { // 1. translate CHIR to LLVM IR CHIR::CHIRBuilder builder(ci.chirData.GetCHIRContext()); llvmModules = CodeGen::GenPackageModules(builder, ci.chirData, ci.invocation.globalOptions, ci, enableIncrement); // 2. save LLVM IR to bc file Utils::ProfileRecorder recorder("CodeGen", "Save bc file"); ci.invocation.globalOptions.UpdateCachedDirName(pkg.fullPackageName); if (llvmModules.size() == 1) { auto filePath = GenerateBCFilePathAndUpdateToInvocation(TempFileKind::T_BC, pkg.fullPackageName); if (filePath.empty()) { return false; } CodeGen::SavePackageModule(*llvmModules[0], filePath); } else { Utils::TaskQueue taskQueueSaveBitcode(llvmModules.size()); std::vector allBCFilePath; for (size_t i = 0; i < llvmModules.size(); ++i) { auto filePath = GenerateBCFilePathAndUpdateToInvocation(TempFileKind::T_BC, pkg.fullPackageName, std::to_string(i)); if (filePath.empty()) { return false; } allBCFilePath.emplace_back(filePath); } for (size_t i = 0; i < llvmModules.size(); ++i) { auto& module = *llvmModules[i]; auto& bcFilePath = allBCFilePath[i]; taskQueueSaveBitcode.AddTask( [&module, &bcFilePath]() { CodeGen::SavePackageModule(module, bcFilePath); }); } taskQueueSaveBitcode.RunAndWaitForAllTasksCompleted(); } if (ci.invocation.globalOptions.enIncrementalCompilation) { auto fileName = GenerateFileName(pkg.fullPackageName, ""); ci.cachedInfo.bitcodeFilesName = std::vector{fileName}; } return true; } #endif bool DefaultCompilerInstance::PerformMangling() { Utils::ProfileRecorder recorder("Main Stage", "Perform Mangling"); return CompilerInstance::PerformMangling(); } bool DefaultCIImpl::PerformCodeGen() { Utils::ProfileRecorder recorder("Main Stage", "CodeGen"); // Before CodeGen, the dependency relationship of a package contains only some packages. // So this function rearranges the dependencies of all packages. RearrangeImportedPackageDependence(); bool ret = true; for (auto& srcPkg : ci.GetSourcePackages()) { ret = ret && CodegenOnePackage(*srcPkg, false); } return ret; } bool DefaultCIImpl::PerformCjoAndBchirSaving() { Utils::ProfileRecorder recorder("Main Stage", "Save cjo and bchir"); bool ret = true; for (auto& srcPkg : ci.GetSourcePackages()) { ret = ret && SaveCjoAndBchir(*srcPkg); } return ret; } void DefaultCIImpl::DumpIR() { auto backend = ci.invocation.globalOptions.backend; switch (backend) { case Triple::BackendType::CJNATIVE: for (auto& llvmModule : llvmModules) { llvmModule->print(llvm::outs(), nullptr); } break; case Triple::BackendType::UNKNOWN: Errorln("unknown backend"); break; default: break; } } void DefaultCIImpl::DumpBC() { auto backend = ci.invocation.globalOptions.backend; auto output = ci.invocation.globalOptions.output; if (output.empty() || output == "-") { Errorln("Output path must be specified with -o for bitcode dump"); return; } auto dumpBitcode = [&output](llvm::Module const& mod) { auto ec = std::error_code(); #ifdef _WIN32 std::optional tempPath = StringConvertor::NormalizeStringToUTF8(output); if (!tempPath.has_value()) { Errorln("Incorrect file name encoding."); } auto os = llvm::raw_fd_ostream(tempPath.value(), ec); #else auto os = llvm::raw_fd_ostream(output, ec); #endif if (ec) { Errorln(ec.message()); } else { WriteBitcodeToFile(mod, os); } }; switch (backend) { case Triple::BackendType::CJNATIVE: for (auto& llvmModule : llvmModules) { if (!llvmModule) { Errorln("No valid codegen module!"); return; } dumpBitcode(*llvmModule); } break; case Triple::BackendType::UNKNOWN: Errorln("bitcode dumping is only supported on CJNATIVE backend"); break; default: break; } } void DefaultCompilerInstance::DumpDepPackage() { for (auto& depPkgInfo : GetDepPkgInfo()) { Println(depPkgInfo); } } void DefaultCIImpl::RearrangeImportedPackageDependence() { Utils::ProfileRecorder recorder("CodeGen", "RearrangeImportedPackageDependence"); std::vector> allImportedPackages; for (auto pd : ci.importManager.GetAllImportedPackages(true)) { CJC_ASSERT(pd && pd->srcPackage); allImportedPackages.push_back(pd->srcPackage); } ci.packageManager->ResolveDependence(allImportedPackages); } bool DefaultCompilerInstance::PerformCodeGen() { return impl->PerformCodeGen(); } bool DefaultCompilerInstance::PerformCjoAndBchirSaving() { return impl->PerformCjoAndBchirSaving(); } void DefaultCompilerInstance::DumpIR() const { impl->DumpIR(); } void DefaultCompilerInstance::DumpBC() const { impl->DumpBC(); } bool DefaultCompilerInstance::SaveCjoAndBchir(AST::Package& pkg) const { return impl->SaveCjoAndBchir(pkg); } bool DefaultCompilerInstance::SaveCjo(const AST::Package& pkg) const { return impl->SaveCjo(pkg); } void DefaultCompilerInstance::RearrangeImportedPackageDependence() const { return impl->RearrangeImportedPackageDependence(); } bool DefaultCompilerInstance::CodegenOnePackage(AST::Package& pkg, bool enableIncrement) const { return impl->CodegenOnePackage(pkg, enableIncrement); } } cangjie_compiler-1.0.7/src/FrontendTool/FrontendTool.cpp000066400000000000000000000247211510705540100233420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements frontend execute apis. */ #include "cangjie/FrontendTool/FrontendTool.h" #ifdef __linux__ #include #endif #include #include #include #include "cangjie/AST/PrintNode.h" #include "cangjie/Basic/Version.h" #include "cangjie/CHIR/Serializer/CHIRDeserializer.h" #include "cangjie/Driver/Driver.h" #include "cangjie/Driver/TempFileManager.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/FrontendTool/CjdCompilerInstance.h" #include "cangjie/FrontendTool/DefaultCompilerInstance.h" #include "cangjie/FrontendTool/IncrementalCompilerInstance.h" #include "cangjie/Macro/InvokeUtil.h" #include "cangjie/Modules/PackageManager.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/ProfileRecorder.h" #if (defined RELEASE) #include "cangjie/Utils/Signal.h" #endif using namespace Cangjie; static bool InitializeCompilerInvocation(CompilerInvocation& ci, const std::vector& args) { if (!ci.ParseArgs(args)) { WriteError("Invalid options. Try: 'cjc-frontend --help' for more information.\n"); return false; } return true; } static bool PerformDumpAction(DefaultCompilerInstance& instance, const FrontendOptions::DumpAction dumpAction) { bool ret = true; switch (dumpAction) { case FrontendOptions::DumpAction::DUMP_TOKENS: return instance.DumpTokens(); case FrontendOptions::DumpAction::DUMP_PARSE: ret = instance.Compile(CompileStage::PARSE); for (auto srcPkg : instance.GetSourcePackages()) { PrintNode(srcPkg); } break; case FrontendOptions::DumpAction::DUMP_SYMBOLS: ret = instance.Compile(CompileStage::SEMA); instance.DumpSymbols(); break; case FrontendOptions::DumpAction::DUMP_AST: ret = instance.Compile(CompileStage::MANGLING); for (auto srcPkg : instance.GetSourcePackages()) { PrintNode(srcPkg, 0, "after MANGLING"); } break; case FrontendOptions::DumpAction::TYPE_CHECK: ret = instance.Compile(CompileStage::CHIR); break; case FrontendOptions::DumpAction::DUMP_IR: ret = instance.Compile(CompileStage::CODEGEN); instance.DumpIR(); break; case FrontendOptions::DumpAction::DUMP_BC: ret = instance.Compile(CompileStage::CODEGEN); instance.DumpBC(); break; case FrontendOptions::DumpAction::DUMP_MACRO: ret = instance.Compile(CompileStage::MACRO_EXPAND); instance.DumpMacro(); break; case FrontendOptions::DumpAction::DUMP_DEP_PKG: ret = instance.Compile(CompileStage::IMPORT_PACKAGE); instance.DumpDepPackage(); break; case FrontendOptions::DumpAction::DESERIALIZE_CHIR: ret = instance.DeserializeCHIR(); break; default: break; } ret = ret && instance.diag.GetErrorCount() == 0; return ret; } static bool IsEmptyInputFile(const DefaultCompilerInstance& instance) { auto& globalOptions = instance.invocation.globalOptions; if (globalOptions.scanDepPkg) { // In scan dependency mode, .cjo file input is required or `-p` must be specified (with package path input). return !globalOptions.compilePackage && globalOptions.inputCjoFile.empty(); } else { // In code compilation mode, .cj file input is required or `-p` must be specified (with package path input). return !globalOptions.compilePackage && globalOptions.srcFiles.empty() && globalOptions.inputChirFiles.empty(); } } static bool HandleEmptyInputFileSituation(const DefaultCompilerInstance& instance) { auto& globalOptions = instance.invocation.globalOptions; if (!globalOptions.scanDepPkg && globalOptions.srcFiles.empty() && globalOptions.inputChirFiles.empty()) { instance.diag.DiagnoseRefactor(DiagKindRefactor::driver_source_file_empty, DEFAULT_POSITION); return false; } if (globalOptions.scanDepPkg && globalOptions.inputCjoFile.empty()) { instance.diag.DiagnoseRefactor(DiagKindRefactor::driver_source_cjo_empty, DEFAULT_POSITION); return false; } return true; } static bool ExecuteCompile(DefaultCompilerInstance& instance) { FrontendOptions& opts = instance.invocation.frontendOptions; bool isEmitCHIR = instance.invocation.globalOptions.IsEmitCHIREnable(); if (IsEmptyInputFile(instance) && opts.dumpAction != FrontendOptions::DumpAction::DESERIALIZE_CHIR) { return HandleEmptyInputFileSituation(instance); } if (opts.dumpAction != FrontendOptions::DumpAction::NO_ACTION) { return PerformDumpAction(instance, opts.dumpAction); } // Process compilation of frontend. if (!instance.Compile(CompileStage::CHIR)) { return false; } bool res = true; if (!isEmitCHIR && instance.invocation.globalOptions.outputMode != GlobalOptions::OutputMode::CHIR) { Cangjie::ICE::TriggerPointSetter iceSetter(CompileStage::CODEGEN); res = instance.PerformCodeGen() && res; } if (!isEmitCHIR) { Cangjie::ICE::TriggerPointSetter iceSetter(CompileStage::SAVE_RESULTS); res = instance.PerformCjoAndBchirSaving() && res; } return res; } int Cangjie::ExecuteFrontend(const std::string& exePath, const std::vector& args, const std::unordered_map& environmentVars) { DiagnosticEngine diag; CompilerInvocation invocation; // Get absolute path of `cjc` executable program. auto cangjieHome = FileUtil::GetDirPath(FileUtil::GetDirPath(FileUtil::GetAbsPath(exePath) | FileUtil::IdenticalFunc)); invocation.frontendOptions.executablePath = exePath; invocation.frontendOptions.ReadPathsFromEnvironmentVars(environmentVars); invocation.frontendOptions.cangjieHome = invocation.frontendOptions.environment.cangjieHome.value_or(cangjieHome); invocation.globalOptions.executablePath = invocation.frontendOptions.executablePath; invocation.globalOptions.environment = invocation.frontendOptions.environment; invocation.globalOptions.cangjieHome = invocation.frontendOptions.cangjieHome; if (!InitializeCompilerInvocation(invocation, args)) { return 1; } GlobalOptions& globalOptions = invocation.globalOptions; if (globalOptions.showUsage) { std::set groups{Options::Group::GLOBAL, Options::Group::FRONTEND}; invocation.optionTable->Usage(globalOptions.GetOptionsBackend(), groups); return 0; } if (globalOptions.enableVerbose || globalOptions.printVersionOnly) { Cangjie::PrintVersion(); if (globalOptions.printVersionOnly) { return 0; } } if (globalOptions.enableTimer) { Utils::ProfileRecorder::Enable(true, Utils::ProfileRecorder::Type::TIMER); } if (globalOptions.enableMemoryCollect) { Utils::ProfileRecorder::Enable(true, Utils::ProfileRecorder::Type::MEMORY); } if (!TempFileManager::Instance().Init(globalOptions, true)) { return 1; } diag.SetErrorCountLimit(globalOptions.errorCountLimit); diag.RegisterHandler(globalOptions.diagFormat); std::unique_ptr instance; if (NeedCreateIncrementalCompilerInstance(globalOptions)) { instance = std::make_unique(invocation, diag); } else if (globalOptions.compileCjd) { instance = std::make_unique(invocation, diag); } else { instance = std::make_unique(invocation, diag); } if (ExecuteCompile(*instance)) { { Cangjie::ICE::TriggerPointSetter iceSetter(Cangjie::ICE::TriggerPointSetter::writeCahedTP); instance->UpdateAndWriteCachedInfoToDisk(); } return 0; } return 1; } bool Cangjie::ExecuteFrontendByDriver(DefaultCompilerInstance& instance, const Driver& driver) { if (ExecuteCompile(instance)) { driver.driverOptions->frontendOutputFiles = instance.invocation.globalOptions.frontendOutputFiles; driver.driverOptions->directBuiltinDependencies = instance.invocation.globalOptions.directBuiltinDependencies; driver.driverOptions->indirectBuiltinDependencies = instance.invocation.globalOptions.indirectBuiltinDependencies; driver.driverOptions->compilationCachedPath = instance.invocation.globalOptions.compilationCachedPath; driver.driverOptions->compilationCachedDir = instance.invocation.globalOptions.compilationCachedDir; driver.driverOptions->compilationCachedFileName = instance.invocation.globalOptions.compilationCachedFileName; driver.driverOptions->incrementalCompileNoChange = (instance.invocation.globalOptions.enIncrementalCompilation && instance.kind == IncreKind::NO_CHANGE); driver.driverOptions->symbolsNeedLocalized = instance.invocation.globalOptions.symbolsNeedLocalized; { Cangjie::ICE::TriggerPointSetter iceSetter(Cangjie::ICE::TriggerPointSetter::writeCahedTP); instance.UpdateAndWriteCachedInfoToDisk(); } return true; } RuntimeInit::GetInstance().CloseRuntime(); return false; } bool Cangjie::NeedCreateIncrementalCompilerInstance(const GlobalOptions& opts) { auto& logger = IncrementalCompilationLogger::GetInstance(); logger.SetDebugPrint(opts.printIncrementalInfo); if (opts.enIncrementalCompilation) { if (opts.mock == MockMode::ON) { logger.LogLn("enable mock, roll back to full compilation"); } else if (opts.enableCoverage) { logger.LogLn("enable coverage, roll back to full compilation"); } else if (opts.outputMode == GlobalOptions::OutputMode::CHIR) { logger.LogLn("enable compile common part mode, roll back to full compilation"); } else if (opts.commonPartCjo) { logger.LogLn("enable compile platform part mode, roll back to full compilation"); } } return opts.enIncrementalCompilation && opts.mock != MockMode::ON && !opts.enableCoverage && !(opts.outputMode == GlobalOptions::OutputMode::CHIR) && !opts.commonPartCjo; } cangjie_compiler-1.0.7/src/FrontendTool/IncrementalCompilerInstance.cpp000066400000000000000000000473001510705540100263440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the IncrementalCompilerInstance. */ #include "cangjie/FrontendTool/IncrementalCompilerInstance.h" #include #include "cangjie/AST/Utils.h" #include "cangjie/Basic/StringConvertor.h" #include "cangjie/Basic/Version.h" #include "cangjie/Driver/TempFileManager.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Modules/PackageManager.h" #include "cangjie/Parse/ASTHasher.h" #include "cangjie/Sema/IncrementalUtils.h" #include "cangjie/Utils/ConstantsUtils.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/ProfileRecorder.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" #include "llvm/IRReader/IRReader.h" #include "llvm/Support/SourceMgr.h" #include "cangjie/IncrementalCompilation/IncrementalCompilationLogger.h" #if (defined RELEASE) #include "cangjie/Utils/Signal.h" #endif using namespace Cangjie; using namespace AST; IncrementalCompilerInstance::~IncrementalCompilerInstance() { } bool IncrementalCompilerInstance::InitCompilerInstance() { return CompilerInstance::InitCompilerInstance(); } void IncrementalCompilerInstance::SaveCompilationResult(Cangjie::IncreResult&& result) { kind = result.kind; rawMangleName2DeclMap = result.mangle2decl; switch (kind) { case IncreKind::ROLLBACK: declsToBeReCompiled.clear(); [[fallthrough]]; case IncreKind::INCR: cachedInfo = std::move(result.cacheInfo); CacheCompileArgs(); declsToBeReCompiled = std::move(result.declsToRecompile); rawIncrRemovedDecls = std::move(result.deleted); // Copy a non-const version for 'Sema' usage. for (auto& [mangle, decl] : result.mangle2decl) { if (!decl->TestAttr(Attribute::IMPORTED)) { mangledName2DeclMap.emplace(mangle, Ptr(const_cast(decl.get()))); } } for (auto changed : declsToBeReCompiled) { if (changed->TestAttr(Attribute::IMPORTED) && (changed->IsFunc() || IsGlobalOrStaticVar(*changed))) { cacheMangles.EmplaceImportedInlineDeclPtr(*changed); } else { Sema::CollectRemovedManglesForReCompile(*changed, cachedInfo.semaInfo, cacheMangles.incrRemovedDecls); } } for (auto removed : rawIncrRemovedDecls) { Sema::CollectRemovedMangles(removed, cachedInfo.semaInfo, cacheMangles.incrRemovedDecls); } // NOTE: since 'declsToBeReCompiled' and 'rawIncrRemovedDecls' have been process, // this loop will only remove the re-boxed built-in types' related decls. for (auto typeMangle : result.reBoxedTypes) { Sema::CollectRemovedMangles(typeMangle, cachedInfo.semaInfo, cacheMangles.incrRemovedDecls); } break; case IncreKind::NO_CHANGE: cachedInfo.bitcodeFilesName = std::move(result.cacheInfo.bitcodeFilesName); break; case IncreKind::EMPTY_PKG: break; case IncreKind::INVALID: CJC_ABORT(); break; } } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND namespace { std::optional GetCachedSplitNum(const std::string& cachedCodeGenPath) { std::optional splitNum{std::nullopt}; llvm::SMDiagnostic err; auto context = llvm::LLVMContext(); #ifdef _WIN32 auto tempPath = StringConvertor::NormalizeStringToUTF8(cachedCodeGenPath); CJC_ASSERT(tempPath.has_value() && "Incorrect file name encoding."); auto module = llvm::parseIRFile(tempPath.value(), err, context); #else auto module = llvm::parseIRFile(cachedCodeGenPath, err, context); #endif if (module == nullptr) { Errorln("Illegal cached codegen info detected during incremental compilation."); return splitNum; } auto splitNumMD = module->getNamedMetadata("splitNum"); for (llvm::MDNode* mdNode : splitNumMD->operands()) { auto numMD = llvm::cast(mdNode->getOperand(0)); splitNum = llvm::cast(numMD->getValue())->getZExtValue(); } return splitNum; } bool CheckCachedBitcode(const std::string& packageName, size_t splitNum, const GlobalOptions& globalOp) { if (splitNum == 0) { return false; } for (size_t i = 0; i < splitNum; ++i) { std::string cachedBcPath = globalOp.GenerateCachedPathNameForCodeGen(std::to_string(i) + '-' + packageName, "_cache.bc"); if (!FileUtil::FileExist(cachedBcPath)) { IncrementalCompilationLogger::GetInstance().LogLn( "the cached bitcode file is lost." + cachedBcPath + " index " + std::to_string(i)); return false; } } return true; } bool CheckCachedBitcodeInfo(const std::string& packageName, const GlobalOptions& options) { auto cgCachePath = options.GenerateCachedPathNameForCodeGen(packageName, ".cgCache"); if (!FileUtil::FileExist(cgCachePath)) { IncrementalCompilationLogger::GetInstance().LogLn( "load cached codegen info failed, roll back to full compilation"); return false; } auto splitNum = GetCachedSplitNum(cgCachePath); if (!splitNum.has_value()) { IncrementalCompilationLogger::GetInstance().LogLn("get cached split num failed, roll back to full compilation"); return false; } bool bcCachedGood = CheckCachedBitcode(packageName, splitNum.value(), options); if (!bcCachedGood) { IncrementalCompilationLogger::GetInstance().LogLn( "load cached bitcode file failed, roll back to full compilation"); return false; } return true; } bool CheckCachedObjFile(const GlobalOptions& options, CompilationCache& cachedInfo) { auto& logger = IncrementalCompilationLogger::GetInstance(); for (const auto& fileName : cachedInfo.bitcodeFilesName) { auto objFile = options.GetHashedObjFileName(fileName) + ".o"; if (!FileUtil::FileExist(objFile)) { logger.LogLn("cached obj file not exist, roll back to full compilation"); return false; } } return true; } bool CheckCachedBitcodeInfoForLTO(const GlobalOptions& options, CompilationCache& cachedInfo) { auto& logger = IncrementalCompilationLogger::GetInstance(); for (const auto& fileName : cachedInfo.bitcodeFilesName) { auto objFile = options.GetHashedObjFileName(FileUtil::GetFileNameWithoutExtension(fileName)) + ".bc"; if (!FileUtil::FileExist(objFile)) { logger.LogLn("load cached bitcode file for opt failed, roll back to full compilation"); return false; } } return true; } } // namespace #endif bool IncrementalCompilerInstance::PerformIncrementalScopeAnalysis() { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND Utils::ProfileRecorder recorder("Main Stage", "AST Diff"); CJC_ASSERT(srcPkgs.size() == 1); auto& package = srcPkgs[0]; // calculate the AST cache info bool res = CalculateASTCache(); if (!res) { return true; } auto& options = invocation.globalOptions; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (!options.pluginPaths.empty()) { kind = IncreKind::ROLLBACK; Warningf("`--incremental-compile` is incompatible with `--plugin` and will be ignored.\n"); return true; } #endif if (!declsWithDuplicatedRawMangleName.empty()) { kind = IncreKind::ROLLBACK; IncrementalCompilationLogger::GetInstance().LogLn( "illegal source code leads to duplicated raw mangle name, roll back to full compilation"); return true; } auto increRes = IncrementalScopeAnalysis({rawMangleName2DeclMap, std::move(astCacheInfo), *package, options, importManager, cachedInfo, fileMap, std::move(directExtends)}); if (increRes.kind == IncreKind::INCR) { // check cached bitcode infos const std::string& packageName = package->fullPackageName; bool isCachedBitcodeGood = CheckCachedBitcodeInfo(packageName, options); if (!isCachedBitcodeGood) { increRes.kind = IncreKind::ROLLBACK; } else { // set deleted mangles Name for codegen cacheMangles.incrRemovedDecls.insert( increRes.deletedMangleNames.begin(), increRes.deletedMangleNames.end()); } } if (increRes.kind == IncreKind::NO_CHANGE) { // check cached obj file for (auto& srcPkg : GetSourcePackages()) { options.UpdateCachedDirName(srcPkg->fullPackageName); } if (!options.IsLTOEnabled() && !CheckCachedObjFile(options, increRes.cacheInfo)) { increRes.kind = IncreKind::ROLLBACK; } if (options.IsLTOEnabled() && !CheckCachedBitcodeInfoForLTO(options, increRes.cacheInfo)) { increRes.kind = IncreKind::ROLLBACK; } } increRes.Dump(); SaveCompilationResult(std::move(increRes)); #endif return true; } static bool HasSimpleInitialiser(const AST::VarDecl& decl) { auto& init = decl.initializer; if (!init) { return false; } if (init->astKind == ASTKind::LIT_CONST_EXPR) { auto kind = StaticCast(init.get())->kind; return kind != LitConstKind::JSTRING && kind != LitConstKind::UNIT; } return false; } bool IncrementalCompilerInstance::PerformSema() { Utils::ProfileRecorder recorder("Main Stage", "Semantic"); if (auto& logger = IncrementalCompilationLogger::GetInstance(); logger.IsEnable()) { std::string message = kind == IncreKind::NO_CHANGE ? "no change, skip sema" : (kind == IncreKind::ROLLBACK ? "full sema" : "incremental sema"); logger.LogLn(message); } if (kind == IncreKind::NO_CHANGE || kind == IncreKind::EMPTY_PKG) { return true; } else if (kind == IncreKind::ROLLBACK) { return CompilerInstance::PerformSema(); } CJC_NULLPTR_CHECK(typeManager); // Load cached types. Should only exist exactly one source package. for (auto srcPkg : GetSourcePackages()) { srcPkg->EnableAttr(Attribute::INCRE_COMPILE); Sema::HandleCtorForIncr(*srcPkg, mangledName2DeclMap, cachedInfo.semaInfo); cacheMangles.incrRemovedDecls.merge(importManager.LoadCachedTypeForPackage(*srcPkg, mangledName2DeclMap)); } Sema::MarkIncrementalCheckForCtor(declsToBeReCompiled); auto visit = [](Decl& decl) { // NOTE: Avoid some sema rules to be rechecked. // To guarantee the member decls will be checked, the type decl cannot be marked with following attributes. if (!decl.IsNominalDecl() && decl.astKind != ASTKind::PROP_DECL) { decl.EnableAttr(Attribute::IS_CHECK_VISITED, Attribute::INITIALIZED, Attribute::INITIALIZATION_CHECKED, Attribute::INCRE_COMPILE); } // Remove body to avoid walker walk-in. if (auto fd = DynamicCast(&decl); fd && fd->funcBody) { fd->funcBody->body.reset(); return; } if (auto vd = DynamicCast(&decl)) { if (!vd->initializer) { return; } vd->EnableAttr(Attribute::DEFAULT); if (HasSimpleInitialiser(*vd)) { vd->DisableAttr(Attribute::IS_CHECK_VISITED); } } // PropDecl's getter/setter will be counted separately and should not be cleared from propDecl. }; for (auto& [_, decl] : mangledName2DeclMap) { if (!decl->toBeCompiled && !IsInDeclWithAttribute(*decl, Attribute::GENERIC) && Ty::IsTyCorrect(decl->ty) && (decl->astKind != ASTKind::FUNC_DECL || !decl->TestAttr(Attribute::DEFAULT))) { // NOTE: for now, generic definitions and default implementations should be kept for code re-generation. visit(*decl); } } return CompilerInstance::PerformSema(); } bool IncrementalCompilerInstance::PerformOverflowStrategy() { Utils::ProfileRecorder recorder("Main Stage", "Overflow Strategy"); if (kind == IncreKind::NO_CHANGE || kind == IncreKind::EMPTY_PKG) { return true; } return CompilerInstance::PerformOverflowStrategy(); } bool IncrementalCompilerInstance::PerformDesugarAfterSema() { Utils::ProfileRecorder recorder("Main Stage", "Desugar after Sema"); if (kind == IncreKind::NO_CHANGE || kind == IncreKind::EMPTY_PKG) { return true; } return CompilerInstance::PerformDesugarAfterSema(); } bool IncrementalCompilerInstance::PerformGenericInstantiation() { Utils::ProfileRecorder recorder("Main Stage", "Generic Instantiation"); if (kind == IncreKind::NO_CHANGE || kind == IncreKind::EMPTY_PKG) { return true; } bool ret = CompilerInstance::PerformGenericInstantiation(); if (ret) { for (auto& srcPkg : GetSourcePackages()) { changedTypes.merge(Sema::CollectChangedStructTypes(*srcPkg, declsToBeReCompiled)); IterateAllExportableDecls(*srcPkg, [this](auto& decl) { // If non-export function/variable is marked as external linkage, codegen should change its linkage. if (!decl.IsExportedDecl() && decl.linkage == Linkage::EXTERNAL && !decl.mangledName.empty()) { cacheMangles.newExternalDecls.emplace(decl.mangledName); } }); } } return ret; } bool IncrementalCompilerInstance::PerformMangling() { if (kind == IncreKind::NO_CHANGE || kind == IncreKind::EMPTY_PKG) { return true; } bool success = DefaultCompilerInstance::PerformMangling(); if (success) { // mangle of full exported decl is empty in cjo, we need update it after mangling. cacheMangles.UpdateImportedInlineDeclsMangle(); } return success; } void IncrementalCompilerInstance::UpdateCachedInfo() { UpdateMangleNameForCachedInfo(); UpdateCHIROptEffectMap(); if (kind == IncreKind::ROLLBACK) { cachedInfo.virtualFuncDep = std::move(chirInfo.curVirtFuncWrapDep); cachedInfo.ccOutFuncs = std::move(chirInfo.ccOutFuncsRawMangle); cachedInfo.varInitDepMap = std::move(chirInfo.varInitDepMap); } else if (kind == IncreKind::INCR) { for (auto& [raw, _] : chirInfo.delVirtFuncWrapForIncr) { cachedInfo.virtualFuncDep.erase(raw); } for (auto& [raw, warp] : chirInfo.curVirtFuncWrapDep) { cachedInfo.virtualFuncDep[raw] = warp; } for (auto& rm : std::as_const(rawIncrRemovedDecls)) { cachedInfo.varInitDepMap.erase(rm); } for (auto& [raw, warp] : chirInfo.varInitDepMap) { cachedInfo.varInitDepMap[raw] = warp; } cachedInfo.ccOutFuncs.merge(std::move(chirInfo.ccOutFuncsRawMangle)); } // Update usage of compiler added. for (auto pkg : GetSourcePackages()) { Sema::CollectCompilerAddedDeclUsage(*pkg, cachedInfo.semaInfo); } } namespace Cangjie { /// implemented in IncrementalScopeAnalysis void MergeCHIROptEffectMap( const OptEffectStrMap& newMap, OptEffectStrMap& lastCachedMap, const RawMangled2DeclMap& rawMangled2Decl); void DeleteRemovedNodesInCHIROptEffectMap(const std::list& removedDecls, OptEffectStrMap& effectMap); void DeleteRecompiledNodesInCHIROptEffectMap( const std::unordered_set>& recompiledNodes, OptEffectStrMap& effectMap); } void IncrementalCompilerInstance::UpdateCHIROptEffectMap() { DeleteRemovedNodesInCHIROptEffectMap(rawIncrRemovedDecls, cachedInfo.chirOptInfo); DeleteRecompiledNodesInCHIROptEffectMap(declsToBeReCompiled, cachedInfo.chirOptInfo); MergeCHIROptEffectMap(chirInfo.optEffectMap, cachedInfo.chirOptInfo, rawMangleName2DeclMap); } namespace { void UpdateIncrRemovedDeclsForVirtualFuncDep(CachedMangleMap& cacheMangles, std::list& rawIncrRemovedDecls, VirtualWrapperDepMap& virtualFuncDep, VirtualWrapperDepMap& delVirtFuncWrapForIncr) { auto& logger = IncrementalCompilationLogger::GetInstance(); for (auto& delRaw : rawIncrRemovedDecls) { if (auto it = virtualFuncDep.find(delRaw); it != virtualFuncDep.end()) { logger.LogLn("[delVirDep][remove] raw: " + delRaw + " mg: " + it->second); cacheMangles.incrRemovedDecls.emplace(it->second); } } for (auto& [delRaw, del] : delVirtFuncWrapForIncr) { logger.LogLn("[delVirDep][change] raw: " + delRaw + " mg: " + del); cacheMangles.incrRemovedDecls.emplace(del); } } void UpdateIncrRemovedDeclsForVarInitDep(CachedMangleMap& cacheMangles, std::list& rawIncrRemovedDecls, VarInitDepMap& varInitDepMap) { auto& logger = IncrementalCompilationLogger::GetInstance(); for (auto& delRaw : std::as_const(rawIncrRemovedDecls)) { if (auto it = varInitDepMap.find(delRaw); it != varInitDepMap.end()) { logger.LogLn("[delVarInitDep][remove] raw: " + delRaw + " mg: " + it->second); cacheMangles.incrRemovedDecls.emplace(it->second); } } } } // namespace bool IncrementalCompilerInstance::PerformCHIRCompilation() { Utils::ProfileRecorder recorder("Main Stage", "CHIR"); if (kind == IncreKind::NO_CHANGE || kind == IncreKind::EMPTY_PKG) { return true; } auto ret = CompilerInstance::PerformCHIRCompilation(); for (auto ty : chirInfo.ccTys) { changedTypes.insert(ty); } if (kind == IncreKind::INCR) { UpdateIncrRemovedDeclsForVirtualFuncDep(cacheMangles, rawIncrRemovedDecls, cachedInfo.virtualFuncDep, chirInfo.delVirtFuncWrapForIncr); UpdateIncrRemovedDeclsForVarInitDep(cacheMangles, rawIncrRemovedDecls, cachedInfo.varInitDepMap); } return ret; } void IncrementalCompilerInstance::LoadCachedCodegenResult() const { for (auto fileName : cachedInfo.bitcodeFilesName) { auto cachePath = invocation.globalOptions.GenerateCachedPathNameForCodeGen(fileName, ".bc"); TempFileInfo bcFileInfo = TempFileInfo{fileName, cachePath, "", true}; invocation.globalOptions.frontendOutputFiles.emplace_back(bcFileInfo); } } bool IncrementalCompilerInstance::PerformCodeGen() { Utils::ProfileRecorder recorder("Main Stage", "CodeGen"); if (kind == IncreKind::NO_CHANGE) { for (auto& srcPkg : GetSourcePackages()) { LoadCachedCodegenResult(); invocation.globalOptions.UpdateCachedDirName(srcPkg->fullPackageName); } return true; } // Before CodeGen, the dependency relationship of a package contains only some packages. // So this function rearranges the dependencies of all packages. RearrangeImportedPackageDependence(); bool ret = true; for (auto& srcPkg : GetSourcePackages()) { ret = ret && CodegenOnePackage(*srcPkg, kind == IncreKind::INCR); } return ret; } bool IncrementalCompilerInstance::PerformCjoAndBchirSaving() { Utils::ProfileRecorder recorder("Main Stage", "Save cjo and bchir"); if (kind == IncreKind::NO_CHANGE || kind == IncreKind::EMPTY_PKG) { return true; } bool ret = true; for (auto& srcPkg : GetSourcePackages()) { ret = ret && SaveCjoAndBchir(*srcPkg); if (ret && !srcPkg->IsEmpty()) { // Write astData for incremental compilation in cache path. std::string cachedCjo = invocation.globalOptions.GenerateCachedPathName(srcPkg->fullPackageName, SERIALIZED_FILE_EXTENSION); ret = ret && FileUtil::WriteBufferToASTFile(cachedCjo, importManager.ExportASTSignature(*srcPkg)); } } return ret; }cangjie_compiler-1.0.7/src/IncrementalCompilation/000077500000000000000000000000001510705540100222365ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/IncrementalCompilation/ASTCacheCalculator.cpp000066400000000000000000000410401510705540100263260ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/IncrementalCompilation/ASTCacheCalculator.h" #include "cangjie/AST/Utils.h" #include "cangjie/IncrementalCompilation/IncrementalScopeAnalysis.h" #include "cangjie/IncrementalCompilation/Utils.h" #include "cangjie/Mangle/ASTMangler.h" using namespace Cangjie::AST; namespace Cangjie::IncrementalCompilation { class ASTCacheCalculatorImpl { public: explicit ASTCacheCalculatorImpl( ASTCacheCalculator& calculator, const AST::Package& p1, std::pair srcInfo1) : p{p1}, mangler{p1.fullPackageName}, srcInfo{srcInfo1}, calc{calculator}, mangled2Decl{calc.mangled2Decl}, ret{calc.ret}, duplicatedMangleNames{calc.duplicatedMangleNames}, directExtends{calc.directExtends}, order{calc.order}, fileMap{calc.fileMap} { } void Walk() { for (auto& file : p.files) { gid = 0; invp = false; curFile = &fileMap[GetTrimmedPath(file.get())]; for (size_t i{0}; i < file->decls.size(); ++i) { auto& decl = *file->decls[i]; if (i == 0 && DynamicCast(&decl)) { --gid; // the first decl of a file always has gid 0 } WalkDecl(decl); } } for (auto& [mangle, extends] : directExtends) { mangled2Decl[mangle] = extends.cbegin()->first; ret.emplace(mangle, ComputeDirectExtend(std::move(extends))); } } private: const AST::Package& p; ASTMangler mangler; // Note: polish the implementation here // We have two cases where we need to care about the code position info // 1) debug mode, we need all code position info // 2) stacktrace, we need code position info of expressions and functions std::pair srcInfo; int gid{0}; // current gvid to increment when visiting a global or static variable or to use otherwise bool invp{false}; // in the visit of varwithpattern ASTCacheCalculator& calc; // these fields are all output of cache computation RawMangled2DeclMap& mangled2Decl; // RawMangledName -> Ptr map ASTCache& ret; std::unordered_set>& duplicatedMangleNames; // decls with duplicate RawMangledName std::unordered_map, int>>>& directExtends; std::vector& order; FileMap& fileMap; // output fields end FileMap::mapped_type* curFile{nullptr}; TopLevelDeclCache ComputeDirectExtend(std::list, int>>&& extends) { TopLevelDeclCache r{}; std::vector> members; // collect public funcs & props for (auto extend : std::as_const(extends)) { for (auto& member : extend.first->members) { r.members.push_back(RevisitDirectExtendMember(*member, extend.second)); if (member->TestAttr(Attribute::PUBLIC)) { members.push_back(member.get()); } } } auto& decl = *extends.begin()->first; decl.hash.srcUse = r.srcUse = ASTHasher::SrcUseHash(decl); decl.hash.sig = r.sigHash = ASTHasher::SigHash(decl); decl.hash.bodyHash = r.bodyHash = ASTHasher::HashMemberAPIs(std::move(members)); r.gvid = {GetTrimmedPath(decl.curFile.get()), extends.begin()->second}; decl.hash.gvid = r.gvid.id; r.astKind = static_cast(decl.astKind); r.isGV = false; return r; } void WalkDecl(Decl& decl) { if (auto extend = DynamicCast(&decl); extend && extend->inheritedTypes.empty()) { // collect direct extends but do not compute the cache; delay the computation until all direct extends // with the same name are seen extend->rawMangleName = mangler.Mangle(*extend); auto& it = directExtends[extend->rawMangleName]; if (it.empty()) { order.push_back(&decl); // ensure direct extends are written to the cache only once } it.push_back(std::make_pair(extend, gid)); for (auto member : extend->GetMemberDeclPtrs()) { PrevisitDirectExtendMember(*member); // visit extend members purely for their side effects } return; } // global vars inside varwithpattern are also treated as global var order.push_back(&decl); auto declCache = VisitTopLevel(decl); (void)ret.emplace(decl.rawMangleName, std::move(declCache)); CollectDecl(decl); } static void CombineDeclHash(size_t& acc, const Decl& decl) { acc = ASTHasher::CombineHash(acc, Utils::SipHash::GetHashValue(decl.rawMangleName)); acc = ASTHasher::CombineHash(acc, ASTHasher::SigHash(decl)); acc = ASTHasher::CombineHash(acc, ASTHasher::SrcUseHash(decl)); } size_t VisitMemberVariables(const Decl& decl) const { size_t hashed{0}; Ptr pc{nullptr}; for (auto member : decl.GetMemberDeclPtrs()) { switch (member->astKind) { case ASTKind::VAR_DECL: if (IsInstance(*member)) { CombineDeclHash(hashed, *member); // For those member without explicit type annotation, // we need to also hash its body auto memberVar = StaticCast(member); if (memberVar->type == nullptr) { // here we hardcode the srcinfo is true hashed = ASTHasher::CombineHash(hashed, ASTHasher::BodyHash(*memberVar, {true, true})); } } break; case ASTKind::PRIMARY_CTOR_DECL: pc = StaticCast(member); break; default: break; } } if (pc) { for (auto& pl : pc->funcBody->paramLists) { for (auto& param : pl->params) { if (param->isMemberParam) { CombineDeclHash(hashed, *param); } } } } return hashed; } size_t VisitEnumConstructors(const EnumDecl& decl) const { size_t hashed = Utils::SipHash::GetHashValue(decl.hasEllipsis); for (auto& cons : decl.constructors) { CombineDeclHash(hashed, *cons); } return hashed; } size_t VirtualHashOfInterface(const Decl& decl) const { size_t hashed{0}; std::vector> allMemberFuncs{}; for (auto member : decl.GetMemberDeclPtrs()) { switch (member->astKind) { case ASTKind::FUNC_DECL: { allMemberFuncs.emplace_back(member); break; } case ASTKind::PROP_DECL: { auto propDecl = StaticCast(member); allMemberFuncs.emplace_back(propDecl); for (auto& getter : propDecl->getters) { allMemberFuncs.emplace_back(getter.get()); } for (auto& setter : propDecl->setters) { allMemberFuncs.emplace_back(setter.get()); } break; } default: break; } } std::stable_sort(allMemberFuncs.begin(), allMemberFuncs.end(), [](auto a, auto b) { return a->rawMangleName < b->rawMangleName; }); for (auto member : std::as_const(allMemberFuncs)) { CombineDeclHash(hashed, *member); if (IsUntyped(*member)) { hashed = ASTHasher::CombineHash(hashed, ASTHasher::BodyHash(*member, {false, false})); } } return hashed; } void VisitVarWithPattern(const VarWithPatternDecl& decl) { Walker(decl.irrefutablePattern.get(), [this](Ptr node) { if (auto varDecl = DynamicCast(node)) { WalkDecl(*varDecl); } return VisitAction::WALK_CHILDREN; }).Walk(); } void VisitTopLevelDeclMembers(const Decl& decl, TopLevelDeclCache& result) { for (auto member : decl.GetMemberDeclPtrs()) { // visit static var first so their gvid is smaller than other decls if (IsStaticVar(*member)) { result.members.push_back(VisitMember(*member)); } } for (auto member : decl.GetMemberDeclPtrs()) { // then visit other member decls if (!IsStaticVar(*member)) { result.members.push_back(VisitMember(*member)); // visit member parameters if (auto pd = DynamicCast(member); pd && pd->funcBody && !pd->funcBody->paramLists.empty()) { for (auto& param : pd->funcBody->paramLists[0]->params) { if (param->isMemberParam) { result.members.push_back(VisitMember(*param)); } } } } } } static bool IsDirectExtend(const Decl& decl) { if (decl.astKind != ASTKind::EXTEND_DECL) { return false; } return static_cast(decl).inheritedTypes.empty(); } // Visit a direct extend member as all members are visited. // This function is only needed for its side effects; the result during this visit is to be discarded, as // direct extend members are revisited after other decls. void PrevisitDirectExtendMember(Decl& decl) { for (auto member : GetMembers(decl)) { PrevisitDirectExtendMember(*member); } decl.rawMangleName = mangler.Mangle(decl); CollectDecl(decl); decl.hash.srcUse = ASTHasher::SrcUseHash(decl); decl.hash.bodyHash = ASTHasher::BodyHash(decl, srcInfo); decl.hash.sig = ASTHasher::SigHash(decl); decl.hash.gvid = gid; if (IsOOEAffectedDecl(decl)) { CJC_NULLPTR_CHECK(curFile); curFile->push_back(&decl); } } // Implementation of visit top level decl that varies on ast kind of decl. Note that this function does not // process for direct extend; they are specially processed later. void VisitTopLevelImpl1(const Decl& decl, TopLevelDeclCache& result) { switch (decl.astKind) { case ASTKind::ENUM_DECL: result.instVarHash = VisitEnumConstructors(static_cast(decl)); break; case ASTKind::INTERFACE_DECL: // Instantce interface members are also treated as open. result.virtHash = VirtualHashOfInterface(decl); break; case ASTKind::CLASS_DECL: result.virtHash = ASTHasher::VirtualHash(static_cast(decl)); result.instVarHash = VisitMemberVariables(decl); break; case ASTKind::STRUCT_DECL: result.instVarHash = VisitMemberVariables(decl); break; case ASTKind::VAR_WITH_PATTERN_DECL: ++gid; invp = true; VisitVarWithPattern(static_cast(decl)); invp = false; result.isGV = true; CJC_ASSERT(IsOOEAffectedDecl(decl)); CJC_NULLPTR_CHECK(curFile); curFile->push_back(&decl); // global varwithpattern always has an initialiser break; case ASTKind::VAR_DECL: result.isGV = !invp; if (!invp) { ++gid; if (IsOOEAffectedDecl(decl)) { CJC_NULLPTR_CHECK(curFile); curFile->push_back(&decl); } } break; case ASTKind::FUNC_DECL: if (IsOOEAffectedDecl(decl)) { // foreign global func does not have a body CJC_NULLPTR_CHECK(curFile); curFile->push_back(&decl); } break; case ASTKind::MAIN_DECL: case ASTKind::MACRO_DECL: CJC_NULLPTR_CHECK(curFile); curFile->push_back(&decl); break; default: break; } } Cangjie::TopLevelDeclCache VisitTopLevel(Decl& decl) { TopLevelDeclCache result{}; VisitTopLevelDeclMembers(decl, result); decl.rawMangleName = mangler.Mangle(decl); decl.hash.srcUse = result.srcUse = ASTHasher::SrcUseHash(decl); decl.hash.bodyHash = result.bodyHash = ASTHasher::BodyHash(decl, srcInfo); decl.hash.sig = result.sigHash = ASTHasher::SigHash(decl); VisitTopLevelImpl1(decl, result); decl.hash.instVar = result.instVarHash; decl.hash.virt = result.virtHash; result.gvid = {GetTrimmedPath(decl.curFile.get()), decl.hash.gvid = gid}; result.astKind = static_cast(decl.astKind); return result; } Cangjie::MemberDeclCache VisitMember(Decl& decl) { MemberDeclCache result{}; for (auto member : GetMembers(decl)) { result.members.push_back(VisitMember(*member)); } result.rawMangle = decl.rawMangleName = mangler.Mangle(decl); CollectDecl(decl); decl.hash.srcUse = result.srcUse = ASTHasher::SrcUseHash(decl); decl.hash.bodyHash = result.bodyHash = ASTHasher::BodyHash(decl, srcInfo); decl.hash.sig = result.sigHash = ASTHasher::SigHash(decl); if (IsStaticVar(decl)) { ++gid; // only increment gid, not basegid } result.gvid = {GetTrimmedPath(decl.curFile.get()), decl.hash.gvid = gid}; if (IsOOEAffectedDecl(decl)) { CJC_NULLPTR_CHECK(curFile); curFile->push_back(&decl); } if (IsMemberParam(decl)) { // Member param in primary ctor should be counted as varDecl. result.astKind = static_cast(ASTKind::VAR_DECL); result.isGV = false; } else { result.astKind = static_cast(decl.astKind); result.isGV = decl.astKind == ASTKind::VAR_DECL && decl.TestAttr(Attribute::STATIC); } return result; } // direct extends are first collected and then visited later, but their gvid should follow that of the outer decl // where it is collected, so the gvid must be passed manually // it happens that extend cannot have variable member, so a single gvid is enough for all inner decls of an extend MemberDeclCache RevisitDirectExtendMember(Decl& decl, int gvid) { MemberDeclCache result{}; for (auto member : GetMembers(decl)) { result.members.push_back(RevisitDirectExtendMember(*member, gvid)); } result.rawMangle = decl.rawMangleName; result.srcUse = decl.hash.srcUse; result.bodyHash = decl.hash.bodyHash; result.sigHash = decl.hash.sig; result.astKind = static_cast(decl.astKind); result.gvid.file = GetTrimmedPath(decl.curFile.get()); decl.hash.gvid = result.gvid.id = gvid; for (auto member : decl.GetMemberDeclPtrs()) { member->hash.gvid = gvid; } for (auto& member : result.members) { member.gvid.id = gvid; } return result; } void CollectDecl(const Decl& decl) { const RawMangledName& mangled = decl.rawMangleName; if (auto it{mangled2Decl.find(mangled)}; it != mangled2Decl.cend()) { (void)duplicatedMangleNames.insert(&decl); (void)duplicatedMangleNames.insert(it->second); } mangled2Decl[mangled] = &decl; } }; ASTCacheCalculator::ASTCacheCalculator(const AST::Package& p, const std::pair& srcInfo) : impl{new ASTCacheCalculatorImpl{*this, p, srcInfo}} { } ASTCacheCalculator::~ASTCacheCalculator() { delete impl; } void ASTCacheCalculator::Walk() const { impl->Walk(); } } cangjie_compiler-1.0.7/src/IncrementalCompilation/ASTDiff.cpp000066400000000000000000000342171510705540100241710ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "ASTDiff.h" #include "cangjie/AST/Utils.h" #include "cangjie/IncrementalCompilation/CompilationCache.h" #include "CompilationCacheSerialization.h" using namespace Cangjie; using namespace Cangjie::AST; using namespace Cangjie::IncrementalCompilation; namespace { class OrderDiffCompare { public: // `compareExclude`: exclude these decls when comparing order. In current implementation, these are added decls. // parameter `mod`: extract info of added top-level and member OOEAffectedDecl from OrderDiffCompare(const RawMangled2DeclMap& mangled2Decl, const CachedFileMap& cachedFileMap, const FileMap& curFileMap, const ModifiedDecls& mod) : md{mangled2Decl}, exc{GetAddedDecls(mod)} { for (auto& [file, l1]: cachedFileMap) { auto it = curFileMap.find(file); if (it == curFileMap.cend()) { continue; } auto& l2 = it->second; for (auto decl : CompareOrders(l1, l2)) { orderChanges.push_back(decl); } } CachedFileMap::mapped_type e{}; for (auto& [file, l2]: curFileMap) { if (cachedFileMap.count(file) == 0) { for (auto decl : CompareOrders(e, l2)) { orderChanges.push_back(decl); } } } } std::list orderChanges; private: static std::unordered_set GetAddedDecls(const ModifiedDecls& mod) { std::unordered_set res; for (auto decl : mod.added) { CJC_NULLPTR_CHECK(decl); // Raw mangle of direct extend not contain pakge name, so it's recorded decl maybe imported, skip it if (IsImported(*decl)) { continue; } if (IsOOEAffectedDecl(*decl)) { // OOEAffectedDecl never has member decl res.insert(decl->rawMangleName); continue; } for (auto member : GetMembers(*decl)) { if (IsOOEAffectedDecl(*member)) { res.insert(member->rawMangleName); continue; } for (auto mem1 : GetMembers(*member)) { if (IsOOEAffectedDecl(*mem1)) { res.insert(decl->rawMangleName); } } } } for (auto& decl : mod.types) { // Raw mangle of direct extend not contain pakge name, so it's recorded decl maybe imported, skip it if (IsImported(*decl.first)) { continue; } for (auto member : GetMembers(*decl.first)) { if (IsOOEAffectedDecl(*member)) { res.insert(member->rawMangleName); continue; } for (auto mem1 : GetMembers(*member)) { if (IsOOEAffectedDecl(*mem1)) { res.insert(decl.first->rawMangleName); } } } } return res; } static bool IsGV(const Decl& decl) { return IsGlobalOrStaticVar(decl) || decl.astKind == ASTKind::VAR_WITH_PATTERN_DECL; } std::set CompareOrders(const CachedFileMap::mapped_type& old, const FileMap::mapped_type& cur) { using I = FileMap::mapped_type::size_type; // gvid in the second compilation std::unordered_map h{}; for (I i{0}; i < cur.size(); ++i) { h[cur[i]->rawMangleName] = cur[i]->hash.gvid; } // maxids[i+1] denotes max of // maxids[j], where j < i && old[j] is gv // id[old[i]], the order at which the decl old[i] appears in cur // maxids[0] = 0 is a dummy std::vector maxids{0}; int last{0}; for (auto& b: old) { // not found in cur, this decl is removed from this file, skipped if (auto it = h.find(b); it == h.cend()) { continue; } else { int maxid = std::max(it->second, last); maxids.push_back(maxid); auto decl = md.at(b); if (IsGV(*decl)) { // only gv has impact on other decls and therefore changes maxid of following decls last = maxid; } } } // result part I: if any decl preceding the decl in the cache has larger gvid than it in incremental, the decl // needs recompile std::set res; I rid{0}; for (I i{0}; i < old.size(); ++i) { if (auto it = h.find(old[i]); it == h.cend()) { continue; } else { if (it->second < maxids[++rid]) { res.insert(md.at(old[i])); } } } // store the names in the old file, used to check that a decl is moved to this file std::unordered_set oldNames{old.cbegin(), old.cend()}; // result part II: for a decl that is moved to this file, recompiles it; if there is any decl preceding it in // incremental, they need recompile I i = cur.size(); while (i > 0) { i--; // added decls are excluded if (exc.count(cur[i]->rawMangleName) == 1) { continue; } // name exists in old file, skip if (oldNames.count(cur[i]->rawMangleName) == 1) { continue; } /* Theoretically only other decls before it need recompile if a decl changes the file it belongs to. However, circular dependency among files can be formed with file change only. For example, in old compile: file 1: a, b(use a) file 2: c, d(use c) in new compile: file 1: d, a file 2: b, c since the decls moved are b and d, and there are no decls before them respectively, no decl is to be recompiled. This is however incorrect, because in the new compilation a circular depdency on the two files emerge. By recompiling all decls that have file change, this error is fixed. */ res.insert(cur[i]); // file changed gv, add all decls preceding it into recompile if (IsGV(*cur[i])) { // this new gv, however, need not recompile for (I j{0}; j < i; ++j) { res.insert(cur[j]); } break; } } return res; } const RawMangled2DeclMap& md; std::unordered_set exc; }; class ASTDiffImpl { public: std::pair srcInfo; ModifiedDecls ret{}; RawMangled2DeclMap mangled2Decl; explicit ASTDiffImpl(const std::pair& srcInfo) : srcInfo{srcInfo} { } void CompareCurPkgASTCache(const ASTCache& cached, const ASTCache& cur, const CachedFileMap& cachedFileMap, const FileMap& curFileMap) { CompareCommonDecls(cached, cur); // const decls are to be recompiled even when no change OrderDiffCompare cp{mangled2Decl, cachedFileMap, curFileMap, ret}; ret.orderChanges = std::move(cp.orderChanges); } void CompareImportedASTCache(const ASTCache& cached, const ASTCache& cur) { CompareCommonDecls(cached, cur); } private: void CompareCommonDecls(const ASTCache& cached, const ASTCache& cur) { for (auto& [mangled, topDecl] : cached) { auto curIt = cur.find(mangled); if (curIt == cur.end()) { CollectDeletedTopLevelDecl(mangled, topDecl); continue; } auto& curDecl{curIt->second}; CompareTopLevelDecl(topDecl, curDecl, mangled); } for (auto& [mangled, _] : cur) { if (auto cachedIt = cached.find(mangled); cachedIt == cached.end()) { CollectAddedTopLevelDecl(*mangled2Decl.at(mangled)); } } } std::tuple>> CollectSourcePackage(const Package& p) { ASTCacheCalculator pc{p, srcInfo}; pc.Walk(); mangled2Decl = std::move(pc.mangled2Decl); return {std::move(pc.ret), std::move(pc.duplicatedMangleNames)}; } void CollectAddedTopLevelDecl(const AST::Decl& decl) { ret.added.push_back(&decl); } void CollectDeletedTopLevelDecl(const RawMangledName& mangled, const TopLevelDeclCache& topDecl) { if (topDecl.astKind == static_cast(ASTKind::TYPE_ALIAS_DECL)) { ret.deletedTypeAlias.push_back(mangled); } else { ret.deletes.push_back(mangled); } for (auto& p : topDecl.members) { ret.deletes.push_back(p.rawMangle); } } void CompareMemberDecl(TypeChange& typeChange, const MemberDeclCache& cached, const MemberDeclCache& cur) { CommonChange r{}; r.decl = mangled2Decl.at(cur.rawMangle); r.srcUse = cached.srcUse != cur.srcUse; r.body = cached.bodyHash != cur.bodyHash; r.sig = cached.sigHash != cur.sigHash; if (r) { typeChange.changed.push_back(r); } CompareMembers(typeChange, cached.members, cur.members); } // compare all members of two decls. the order of decls is ignored void CompareMembers( TypeChange& typeChange, const std::vector& cached, const std::vector& cur) { auto sortedCache = ToSorted(cached, [](auto& a, auto& b) { return a.rawMangle < b.rawMangle; }); auto sortedCur = ToSorted(cur, [](auto& a, auto& b) { return a.rawMangle < b.rawMangle; }); auto cachedIt = sortedCache.cbegin(); auto curIt = sortedCur.cbegin(); while (cachedIt != sortedCache.cend() && curIt != sortedCur.cend()) { if ((*cachedIt)->rawMangle < (*curIt)->rawMangle) { typeChange.del.push_back((*cachedIt)->rawMangle); for (auto childDel : (*cachedIt)->members) { typeChange.del.push_back(childDel.rawMangle); } ++cachedIt; continue; } if ((*cachedIt)->rawMangle > (*curIt)->rawMangle) { typeChange.added.push_back(mangled2Decl.at((*curIt++)->rawMangle)); continue; } CompareMemberDecl(typeChange, **cachedIt++, **curIt++); } while (cachedIt != sortedCache.cend()) { typeChange.del.push_back((*cachedIt)->rawMangle); // For deleted property, we should also add the getter/setter into the list for (auto childDel : (*cachedIt)->members) { typeChange.del.push_back(childDel.rawMangle); } ++cachedIt; } while (curIt != sortedCur.cend()) { typeChange.added.push_back(mangled2Decl.at((*curIt++)->rawMangle)); } } TypeChange CompareTypeDecl(const TopLevelDeclCache& cached, const TopLevelDeclCache& cur) { TypeChange typeChange{}; typeChange.instVar = cached.instVarHash != cur.instVarHash; typeChange.virtFun = cached.virtHash != cur.virtHash; typeChange.sig = cached.sigHash != cur.sigHash; typeChange.srcUse = cached.srcUse != cur.srcUse; typeChange.body = cached.bodyHash != cur.bodyHash; CompareMembers(typeChange, cached.members, cur.members); // compare extends with typerel return typeChange; } static CommonChange CommonCompare(const TopLevelDeclCache& a, const TopLevelDeclCache& b, const AST::Decl& decl) { return {&decl, a.sigHash != b.sigHash, a.srcUse != b.srcUse, a.bodyHash != b.bodyHash}; } void CompareTopLevelDecl( const TopLevelDeclCache& cachedDecl, const TopLevelDeclCache curDecl, const RawMangledName& mangled) { auto decl = mangled2Decl.at(mangled); switch (decl->astKind) { case ASTKind::TYPE_ALIAS_DECL: if (cachedDecl.sigHash != curDecl.sigHash) { ret.aliases.push_back(RawStaticCast(decl)); } break; case ASTKind::FUNC_DECL: case ASTKind::MACRO_DECL: case ASTKind::MAIN_DECL: case ASTKind::VAR_DECL: case ASTKind::VAR_WITH_PATTERN_DECL: if (auto change = CommonCompare(cachedDecl, curDecl, *decl)) { (void)ret.commons.emplace(decl, std::move(change)); } break; default: if (auto change = CompareTypeDecl(cachedDecl, curDecl)) { (void)ret.types.emplace(RawStaticCast(decl), std::move(change)); } break; } } }; } namespace Cangjie { std::ostream& operator<<(std::ostream& out, const GlobalVarIndex& id) { return out << '(' << id.file << ", " << id.id << ')'; } } namespace Cangjie::IncrementalCompilation { ASTDiffResult ASTDiff(ASTDiffArgs&& args) { ASTDiffImpl impl{std::make_pair(args.op.enableCompileDebug, args.op.displayLineInfo)}; // Move source package mangled2Decl into imported package and use it. for (std::move_iterator it{args.rawMangleName2DeclMap.begin()}; it != std::move_iterator{args.rawMangleName2DeclMap.end()}; ++it) { (void)args.importedMangled2Decl.emplace(*it); } impl.mangled2Decl = std::move(args.importedMangled2Decl); // compare source package impl.CompareCurPkgASTCache( args.prevCache.curPkgASTCache, args.astCacheInfo, args.prevCache.fileMap, args.curFileMap); // compare imported packages impl.CompareImportedASTCache(args.prevCache.importedASTCache, args.curImports); return {std::move(impl.ret), std::move(impl.mangled2Decl)}; } } cangjie_compiler-1.0.7/src/IncrementalCompilation/ASTDiff.h000066400000000000000000000145521510705540100236360ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CACHE_DATA_ASTDIFF_IMPL_H #define CANGJIE_CACHE_DATA_ASTDIFF_IMPL_H #include "cangjie/AST/Node.h" #include "cangjie/IncrementalCompilation/ASTCacheCalculator.h" #include "cangjie/IncrementalCompilation/IncrementalScopeAnalysis.h" #include "cangjie/IncrementalCompilation/Utils.h" #include "cangjie/IncrementalCompilation/IncrementalCompilationLogger.h" #include "cangjie/Modules/ImportManager.h" namespace Cangjie::IncrementalCompilation { constexpr int DELIMITER_NUM{60}; // function, variable, or property change struct CommonChange { Ptr decl; bool sig; bool srcUse; bool body; // returns true if there is any change explicit operator bool() const { return sig || srcUse || body; } friend std::ostream& operator<<(std::ostream& out, const CommonChange& m) { out << m.decl->rawMangleName << ": "; if (!m) { return out << "no change\n"; } if (m.sig) { out << "sig "; } if (m.srcUse) { out << "srcuse "; } if (m.body) { out << "body "; } return out << '\n'; } }; struct TypeChange { bool instVar; bool virtFun; bool sig; bool srcUse; bool body; bool order; std::list changed; std::list> added; // added non-virtual functions and properties, include extended ones std::list del; explicit operator bool() const { return instVar || virtFun || sig || srcUse || body || order || !changed.empty() || !added.empty() || !del.empty(); } friend std::ostream& operator<<(std::ostream& out, const TypeChange& m) { if (!m) { return out << "no change\n"; } if (m.instVar) { out << "memory "; } if (m.virtFun) { out << "virtual "; } if (m.sig) { out << "sig "; } if (m.srcUse) { out << "srcuse "; } if (m.body) { out << "body "; } out << '\n'; if (!m.added.empty()) { out << " added members " << m.added.size() << ": "; for (auto decl : m.added) { out << decl->rawMangleName << ' '; } out << '\n'; } if (!m.del.empty()) { out << " deleted members " << m.del.size() << ": "; for (auto& d : m.del) { out << d << ' '; } out << '\n'; } if (!m.changed.empty()) { out << " changed members " << m.changed.size() << ":\n"; for (auto& change : m.changed) { out << " " << change; } } return out; } }; struct ModifiedDecls { // added top level decls std::list added; // all deleted decls goes here std::list deletes; bool import{false}; // change of import hash bool args{false}; // change of compile args // changed top level decls begin here: std::unordered_map, TypeChange> types; std::unordered_map, CommonChange> commons; // changed top level variable and functions std::list> aliases; std::list deletedTypeAlias; std::list orderChanges; operator bool() const { return !added.empty() || !deletedTypeAlias.empty() || !deletes.empty() || !types.empty() || !commons.empty() || !orderChanges.empty() || !aliases.empty(); } void Dump() const { auto& logger = IncrementalCompilationLogger::GetInstance(); if (!logger.IsEnable()) { return; } if (!operator bool()) { logger.LogLn("no raw modified decls"); return; } std::stringstream out; for (int i{0}; i < DELIMITER_NUM; ++i) { out << '='; } out << "\nbegin dump raw modified decls:\n"; for (auto a : ToSortedPointers(added, [](auto a, auto b) { return a->begin < b->begin; })) { CJC_NULLPTR_CHECK(a); out << "added "; if (!a->identifier.Empty()) { // skip empty identifier, i.e. in extend out << a->identifier.Val() << " "; } out << a->rawMangleName << " at " << a->identifier.Begin().line << ',' << a->identifier.Begin().column << '\n'; } for (auto& d : ToSorted(deletedTypeAlias)) { out << "deleted " << *d << '\n'; } for (auto& d : ToSorted(deletes)) { out << "deleted " << *d << '\n'; } for (auto &t : ToSorted(types, [](auto&a, auto&b) { return a.first->begin < b.first->begin; })) { if (t->second) { PrintDecl(out, *t->first); out << ": " << t->second; } } for (auto &t : ToSorted(commons, [](auto&a, auto&b) { return a.first->begin < b.first->begin; })) { if (t->second) { out << t->second; } } if (!orderChanges.empty()) { out << orderChanges.size() << " order changed decl(s).\n"; } for (auto& t: ToSortedPointers(orderChanges, [](auto a, auto b) { return a->begin < b->begin; })) { CJC_NULLPTR_CHECK(t); out << "order change " << t->rawMangleName << '\n'; } for (int i{0}; i < DELIMITER_NUM; ++i) { out << '='; } // flush for debugging purpose logger.LogLn(out.str()); } }; struct ASTDiffArgs { const CompilationCache& prevCache; const ASTCache& curImports; RawMangled2DeclMap importedMangled2Decl; const RawMangled2DeclMap& rawMangleName2DeclMap; const ASTCache& astCacheInfo; const FileMap& curFileMap; const GlobalOptions& op; }; struct ASTDiffResult { ModifiedDecls changedDecls; RawMangled2DeclMap mangled2Decl; }; ASTDiffResult ASTDiff(ASTDiffArgs&& args); } // namespace Cangjie::IncrementalCompilation #endif cangjie_compiler-1.0.7/src/IncrementalCompilation/CMakeLists.txt000066400000000000000000000011631510705540100247770ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file( GLOB INCRE_COMPILE_SRC *.cpp) add_library(CangjieIncrementalCompilation OBJECT ${INCRE_COMPILE_SRC}) add_dependencies(CangjieIncrementalCompilation CangjieFlatbuffersHeaders) target_include_directories(CangjieIncrementalCompilation PRIVATE ${FLATBUFFERS_INCLUDE_DIR}) target_compile_options(CangjieIncrementalCompilation PRIVATE ${CJC_EXTRA_WARNINGS}) cangjie_compiler-1.0.7/src/IncrementalCompilation/CachedDataSerialization.cpp000066400000000000000000000470071510705540100274510ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * This file implements the serialization of cached data for incremental compilation. */ #include "CompilationCacheSerialization.h" #include "flatbuffers/CachedASTFormat_generated.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/Version.h" #include "cangjie/Parse/ASTHasher.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/AST/ASTCasting.h" using namespace Cangjie; using namespace AST; using namespace ::flatbuffers; using namespace CachedASTFormat; namespace { Offset Write(FlatBufferBuilder& builder, MemberDeclCache&& decl) { auto man = builder.CreateSharedString(decl.rawMangle); auto fil = builder.CreateSharedString(decl.gvid.file); auto gvid = CreateGlobalVarIndex(builder, fil, decl.gvid.id); Offset>> memberOffset{0}; if (!decl.members.empty()) { size_t i{0}; std::vector> members(decl.members.size()); for (std::move_iterator it{decl.members.begin()}; it != std::move_iterator{decl.members.end()}; ++it) { members[i++] = Write(builder, *it); } memberOffset = builder.CreateVector(members); } auto cgMangle = builder.CreateSharedString(decl.cgMangle); return CreateMemberDecl( builder, man, decl.sigHash, decl.srcUse, decl.bodyHash, decl.astKind, decl.isGV, gvid, memberOffset, cgMangle); } Offset Write(FlatBufferBuilder& builder, const RawMangledName& mangle, TopLevelDeclCache&& decl) { auto man = builder.CreateSharedString(mangle); auto fil = builder.CreateSharedString(decl.gvid.file); auto gvid = CreateGlobalVarIndex(builder, fil, decl.gvid.id); // write members in the order of increasing gvid // static variables first, other member decls following, each group in source code order Offset>> memberOffset{0}; if (!decl.members.empty()) { std::vector> members(decl.members.size()); size_t i{0}; for (std::move_iterator it{decl.members.begin()}; it != std::move_iterator{decl.members.end()}; ++it) { if (it->isGV) { members[i++] = Write(builder, *it); } } for (std::move_iterator it{decl.members.begin()}; it != std::move_iterator{decl.members.end()}; ++it) { if (!it->isGV) { members[i++] = Write(builder, *it); } } memberOffset = builder.CreateVector(members); } Offset>> extendsOffset{0}; if (!decl.extends.empty()) { std::vector> members(decl.extends.size()); size_t i{0}; for (std::move_iterator it{decl.extends.begin()}; it != std::move_iterator{decl.extends.end()}; ++it) { members[i++] = builder.CreateSharedString(*it); } extendsOffset = builder.CreateVector(members); } auto cgMangle = builder.CreateSharedString(decl.cgMangle); return CreateTopDecl(builder, man, decl.sigHash, decl.srcUse, decl.bodyHash, decl.astKind, decl.isGV, decl.instVarHash, decl.virtHash, gvid, memberOffset, extendsOffset, cgMangle); } Offset>> WriteCachedAST(FlatBufferBuilder& builder, ASTCache&& ast, std::vector&& order) { if (ast.empty()) { return 0; } std::vector> res{}; for (auto decl : order) { CJC_ASSERT(decl != nullptr); // rawMangleName of main is moved into its desugared decl. Get that mangle in this case auto& mangled{(Is(decl) ? *StaticCast(*decl).desugarDecl : *decl).rawMangleName}; res.push_back(Write(builder, mangled, std::move(ast.at(mangled)))); } return builder.CreateVector(res); } Offset>> WriteImported(FlatBufferBuilder& builder, ASTCache&& imports) { if (imports.empty()) { return 0; } std::vector> res(imports.size()); size_t i{0}; for (std::move_iterator it{imports.begin()}; it != std::move_iterator{imports.end()}; ++it) { res[i++] = Write(builder, it->first, std::move(it->second)); } return builder.CreateVector(res); } template std::optional, std::vector>>> GetDepRelationAsDecl( const RawMangledName& declMangel, const T& dependencies, const RawMangled2DeclMap& mangledName2DeclMap) { auto it = mangledName2DeclMap.find(declMangel); if (it == mangledName2DeclMap.end()) { return {}; } Ptr decl = it->second.get(); std::vector> deps; for (auto& d : std::as_const(dependencies)) { it = mangledName2DeclMap.find(d); if (it != mangledName2DeclMap.end()) { deps.emplace_back(it->second.get()); } } return std::make_pair(decl, deps); } void LoadCHIROptInfo(const CachedASTFormat::HashedPackage& package, const RawMangled2DeclMap& mangledName2DeclMap, CompilationCache& cached) { if (package.varAndFunc()) { for (uoffset_t i = 0; i < package.varAndFunc()->size(); i++) { auto declDep = package.varAndFunc()->Get(i); std::string decl = declDep->decl()->str(); std::vector dependency; if (!declDep->dependency()) { return; } for (uoffset_t j = 0; j < declDep->dependency()->size(); j++) { (void)dependency.emplace_back(declDep->dependency()->Get(j)->str()); } if (auto relation = GetDepRelationAsDecl(decl, dependency, mangledName2DeclMap)) { (void)cached.varAndFuncDep.emplace_back(relation.value()); } } } if (package.chirOptInfo()) { for (uoffset_t i = 0; i < package.chirOptInfo()->size(); i++) { auto effectMap = package.chirOptInfo()->Get(i); if (!effectMap->effectedDecls()) { return; } auto& effectItem = cached.chirOptInfo[effectMap->srcDecl()->str()]; for (uoffset_t j = 0; j < effectMap->effectedDecls()->size(); j++) { effectItem.emplace(effectMap->effectedDecls()->Get(j)->str()); } } } } void LoadVirtualFuncDep(const CachedASTFormat::HashedPackage& package, CompilationCache& cached) { if (package.virtualDep()) { for (uoffset_t i = 0; i < package.virtualDep()->size(); i++) { auto dep = package.virtualDep()->Get(i); cached.virtualFuncDep[dep->raw()->str()] = dep->wrapper()->str(); } } } void LoadVarInitDep(const CachedASTFormat::HashedPackage& package, CompilationCache& cached) { if (package.varInitDep()) { for (uoffset_t i = 0; i < package.varInitDep()->size(); i++) { auto dep = package.varInitDep()->Get(i); cached.varInitDepMap[dep->raw()->str()] = dep->wrapper()->str(); } } } void LoadCCOutFunc(const CachedASTFormat::HashedPackage& package, CompilationCache& cached) { if (package.ccOutFuncs()) { for (uoffset_t i = 0; i < package.ccOutFuncs()->size(); i++) { cached.ccOutFuncs.emplace(package.ccOutFuncs()->Get(i)->str()); } } } } // namespace #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND namespace Cangjie { bool WriteCache(const Package& pkg, CompilationCache&& cachedInfo, std::vector&& order, const std::string& path) { HashedASTWriter writer; writer.SetImportSpecs(pkg); writer.SetLambdaCounter(cachedInfo.lambdaCounter); writer.SetEnvClassCounter(cachedInfo.envClassCounter); writer.SetStringLiteralCounter(cachedInfo.stringLiteralCounter); writer.SetCompileArgs(cachedInfo.compileArgs); writer.SetVarAndFuncDependency(cachedInfo.varAndFuncDep); writer.SetCHIROptInfo(cachedInfo.chirOptInfo); writer.SetVirtualFuncDep(cachedInfo.virtualFuncDep); writer.SetVarInitDep(cachedInfo.varInitDepMap); writer.SetCCOutFuncs(cachedInfo.ccOutFuncs); writer.SetSemanticInfo(cachedInfo.semaInfo); writer.SetBitcodeFilesName(cachedInfo.bitcodeFilesName); writer.WriteAllDecls( std::move(cachedInfo.curPkgASTCache), std::move(cachedInfo.importedASTCache), std::move(order)); return FileUtil::WriteBufferToASTFile(path, writer.AST2FB(pkg.fullPackageName)); } } #endif void HashedASTWriter::WriteAllDecls(ASTCache&& ast, ASTCache&& imports, std::vector&& order) { allAST = WriteCachedAST(builder, std::move(ast), std::move(order)); importedDecls = WriteImported(builder, std::move(imports)); } void HashedASTWriter::SetImportSpecs(const Package& package) { specs = ASTHasher::HashSpecs(package); } void HashedASTWriter::SetLambdaCounter(uint64_t counter) { lambdaCounter = counter; } void HashedASTWriter::SetEnvClassCounter(uint64_t counter) { envClassCounter = counter; } void HashedASTWriter::SetStringLiteralCounter(uint64_t counter) { stringLiteralCounter = counter; } void HashedASTWriter::SetCompileArgs(const std::vector& args) { for (const auto& it : std::as_const(args)) { (void)compileArgs.emplace_back(static_cast(builder.CreateSharedString(it))); } } using StringVecOffset = flatbuffers::Offset>>; void HashedASTWriter::SetVarAndFuncDependency( const std::vector, std::vector>>>& varAndFuncDep) { std::map>*> orderedDep; for (auto& [decl, vecp] : std::as_const(varAndFuncDep)) { orderedDep.emplace(decl->rawMangleName, &vecp); } for (auto& [rawMangleName, vec] : std::as_const(orderedDep)) { std::set strDeps; for (auto node : *vec) { (void)strDeps.emplace(node->rawMangleName); } auto depsIdx = builder.CreateVectorOfStrings(Utils::SetToVec(strDeps)); auto declNameIdx = builder.CreateString(rawMangleName); (void)varAndFunc.emplace_back(CachedASTFormat::CreateDeclDep(builder, declNameIdx, depsIdx)); } } void HashedASTWriter::SetCHIROptInfo(const OptEffectStrMap& optInfo) { for (auto& it : optInfo) { auto declNameIdx = builder.CreateString(it.first); auto vecIdx = builder.CreateVectorOfStrings(Utils::SetToVec(it.second)); chirOptInfo.emplace_back(CachedASTFormat::CreateEffectMap(builder, declNameIdx, vecIdx)); } } void HashedASTWriter::SetVirtualFuncDep(const VirtualWrapperDepMap& depMap) { for (const auto& [raw, wrapper] : depMap) { auto rawOffset = builder.CreateSharedString(raw); auto wrapperOffset = builder.CreateSharedString(wrapper); virtualFuncDep.emplace_back(CachedASTFormat::CreateVirtualDep(builder, rawOffset, wrapperOffset)); } } void HashedASTWriter::SetVarInitDep(const VarInitDepMap& depMap) { for (const auto& [raw, wrapper] : depMap) { auto rawOffset = builder.CreateSharedString(raw); auto wrapperOffset = builder.CreateSharedString(wrapper); varInitDep.emplace_back(CachedASTFormat::CreateVirtualDep(builder, rawOffset, wrapperOffset)); } } void HashedASTWriter::SetCCOutFuncs(const std::set& funcs) { for (const auto& func : std::as_const(funcs)) { (void)ccOutFuncs.emplace_back(static_cast(builder.CreateSharedString(func))); } } void HashedASTWriter::SetBitcodeFilesName(const std::vector& bitcodeFiles) { for (const auto& fileName : std::as_const(bitcodeFiles)) { (void)bitcodeFilesName.emplace_back(static_cast(builder.CreateSharedString(fileName))); } } std::vector HashedASTWriter::AST2FB(const std::string& pkgName) { auto cjcVersion = builder.CreateString(CANGJIE_VERSION); auto packageName = builder.CreateString(pkgName); auto optArgs = builder.CreateVector(compileArgs); auto declDep = builder.CreateVector(varAndFunc); auto optInfo = builder.CreateVector(chirOptInfo); auto virDep = builder.CreateVector(virtualFuncDep); auto varDep = builder.CreateVector(varInitDep); auto bcFilesName = builder.CreateVector(bitcodeFilesName); auto ccOutFuncVec = builder.CreateVector(ccOutFuncs); auto root = CachedASTFormat::CreateHashedPackage(builder, cjcVersion, packageName, specs, optArgs, allAST, importedDecls, declDep, optInfo, virDep, varDep, ccOutFuncVec, lambdaCounter, envClassCounter, stringLiteralCounter, semaUsages, bcFilesName); FinishHashedPackageBuffer(builder, root); auto size = static_cast(builder.GetSize()); std::vector data; data.resize(size); uint8_t* buf = builder.GetBufferPointer(); CJC_NULLPTR_CHECK(buf); std::copy(buf, buf + size, data.begin()); return data; } bool HashedASTLoader::VerifyData() { // We need to verify the size first. flatbuffers::Verifier verifier(serializedData.data(), serializedData.size(), FB_MAX_DEPTH, FB_MAX_TABLES); return CachedASTFormat::VerifyHashedPackageBuffer(verifier); } std::pair HashedASTLoader::DeserializeData(const RawMangled2DeclMap& mangledName2DeclMap) { if (!VerifyData()) { return {false, {}}; } const auto package = CachedASTFormat::GetHashedPackage(serializedData.data()); CJC_NULLPTR_CHECK(package); if (package->version()->str() != CANGJIE_VERSION) { // Incremental compilation do not use cached data created with different version. return {false, {}}; } CompilationCache cached; if (package->compileOptionArgs()) { for (uoffset_t i = 0; i < package->compileOptionArgs()->size(); i++) { auto arg = package->compileOptionArgs()->Get(i); (void)cached.compileArgs.emplace_back(arg->str()); } } if (package->bitcodeFilesName()) { for (uoffset_t i = 0; i < package->bitcodeFilesName()->size(); i++) { auto fileName = package->bitcodeFilesName()->Get(i); (void)cached.bitcodeFilesName.emplace_back(fileName->str()); } } cached.curPkgASTCache = LoadCachedAST(*package); for (std::move_iterator it{fileMap.begin()}; it != std::move_iterator{fileMap.end()}; ++it) { auto&& m{*it}; // sort by gvid std::sort(m.second.begin(), m.second.end(), [](const auto& a, const auto& b) { return a.second < b.second; }); std::vector tmp{}; for (auto& mangledName : m.second) { tmp.push_back(std::move(mangledName.first)); } cached.fileMap.emplace(std::move(const_cast(m.first)), std::move(tmp)); } cached.importedASTCache = LoadImported(*package); cached.specs = package->specs(); cached.lambdaCounter = package->lambdaCounter(); cached.envClassCounter = package->envClassCounter(); cached.stringLiteralCounter = package->strLitCounter(); cached.semaInfo = LoadSemanticInfos(*package, mangledName2DeclMap); LoadCHIROptInfo(*package, mangledName2DeclMap, cached); LoadVirtualFuncDep(*package, cached); LoadVarInitDep(*package, cached); LoadCCOutFunc(*package, cached); return {true, std::move(cached)}; } MemberDeclCache HashedASTLoader::Load(const MemberDecl& decl) { MemberDeclCache res{}; res.sigHash = decl.sig(); res.srcUse = decl.srcUse(); res.bodyHash = decl.body(); res.astKind = decl.type(); res.gvid = {decl.gvid()->file()->str(), decl.gvid()->id()}; res.isGV = decl.isGV(); if (decl.members()) { for (const auto& member : *decl.members()) { res.members.emplace_back(Load(*member)); } } res.rawMangle = decl.mangle()->str(); res.cgMangle = decl.cgMangle()->str(); return res; } static bool IsOOEAffectedDecl(const MemberDeclCache& decl) { return decl.astKind == static_cast(ASTKind::VAR_DECL) || decl.astKind == static_cast(ASTKind::PRIMARY_CTOR_DECL) || decl.astKind == static_cast(ASTKind::FUNC_DECL); } static bool IsOOEAffectedDecl(const TopLevelDeclCache& decl) { return decl.astKind == static_cast(ASTKind::VAR_DECL) || decl.astKind == static_cast(ASTKind::VAR_WITH_PATTERN_DECL) || decl.astKind == static_cast(ASTKind::FUNC_DECL) || decl.astKind == static_cast(ASTKind::MAIN_DECL) || decl.astKind == static_cast(ASTKind::MACRO_DECL); } // param `srcPkg`: true if is src package decl TopLevelDeclCache HashedASTLoader::Load(const TopDecl& decl, bool srcPkg) { TopLevelDeclCache res{}; res.sigHash = decl.sig(); res.srcUse = decl.srcUse(); res.bodyHash = decl.body(); res.astKind = decl.type(); res.instVarHash = decl.instVar(); res.virtHash = decl.virt(); res.astKind = decl.type(); if (srcPkg) { res.gvid = {decl.gvid()->file()->str(), decl.gvid()->id()}; res.isGV = decl.isGV(); } if (decl.members()) { for (const auto& member : *decl.members()) { res.members.emplace_back(Load(*member)); } if (srcPkg) { // push static vars first for (auto& member : res.members) { if (member.isGV) { fileMap[member.gvid.file].emplace_back(member.rawMangle, member.gvid.id); } } // push other decls later, so in the filemap gvid increases for (auto& member : res.members) { // all members of members are property accessors, no gv for (auto& m1: member.members) { fileMap[m1.gvid.file].emplace_back(m1.rawMangle, m1.gvid.id); } // redundant decls pushed here: enum constructors. They need not check against order change; however // on the one hand, collecting them does not affect the result as deleted decls are ignored. On the // other hand, current cache does not store this information, so they are not excluded here. if (IsOOEAffectedDecl(member) && !member.isGV) { fileMap[member.gvid.file].emplace_back(member.rawMangle, member.gvid.id); } } } } if (decl.extends()) { for (const auto& e : *decl.extends()) { res.extends.emplace_back(e->str()); } } res.cgMangle = decl.cgMangle()->str(); return res; } ASTCache HashedASTLoader::LoadCachedAST(const HashedPackage& p) { ASTCache ret{}; if (p.topDecls()) { std::optional last{}; for (const auto& t : *p.topDecls()) { auto r = Load(*t, true); if (IsOOEAffectedDecl(r) && !(r.astKind == static_cast(ASTKind::VAR_DECL) && !r.isGV)) { fileMap[t->gvid()->file()->str()].emplace_back(t->mangle()->str(), t->gvid()->id()); } // then load the cache of the decl ret.emplace(t->mangle()->str(), std::move(r)); } } return ret; } std::unordered_map HashedASTLoader::LoadImported(const HashedPackage& p) { std::unordered_map ret{}; if (p.importedDecls()) { for (const auto& t : *p.importedDecls()) { ret.emplace(t->mangle()->str(), Load(*t, false)); } } return ret; } cangjie_compiler-1.0.7/src/IncrementalCompilation/CompilationCacheSerialization.h000066400000000000000000000074511510705540100303560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the AST Serialization related classes, which provides AST serialization capabilities. */ #ifndef CANGJIE_COMPILATION_CACHE_SERIALIZATION_H #define CANGJIE_COMPILATION_CACHE_SERIALIZATION_H #include #include #include #include #include #include "cangjie/IncrementalCompilation/CompilationCache.h" #include "cangjie/Modules/ASTSerialization.h" namespace Cangjie { using TVirtualDepOffset = flatbuffers::Offset; class HashedASTWriter { public: HashedASTWriter() = default; ~HashedASTWriter() = default; // Export external decls of a package AST to a buffer. // NOTE: should notice bep. void SetImportSpecs(const AST::Package& package); void SetLambdaCounter(uint64_t counter); void SetEnvClassCounter(uint64_t counter); void SetStringLiteralCounter(uint64_t counter); void SetCompileArgs(const std::vector& args); void SetVarAndFuncDependency( const std::vector, std::vector>>>& varAndFuncDep); void SetCHIROptInfo(const OptEffectStrMap& optInfo); void SetVirtualFuncDep(const VirtualWrapperDepMap& depMap); void SetVarInitDep(const VarInitDepMap& depMap); void SetCCOutFuncs(const std::set& funcs); void SetBitcodeFilesName(const std::vector& bitcodeFiles); void SetSemanticInfo(const SemanticInfo& info); void WriteAllDecls(ASTCache&& ast, ASTCache&& imports, std::vector&& order); std::vector AST2FB(const std::string& pkgName); private: flatbuffers::FlatBufferBuilder builder{INITIAL_FILE_SIZE}; std::vector bitcodeFilesName; std::vector compileArgs; std::vector varAndFunc; std::vector chirOptInfo; std::vector virtualFuncDep; std::vector varInitDep; std::vector ccOutFuncs; // NOTE: For incremental compilation 2.0. Above members will be removed later. flatbuffers::Offset semaUsages; flatbuffers::Offset>> allAST; flatbuffers::Offset>> importedDecls; uint64_t lambdaCounter = 0; uint64_t envClassCounter = 0; uint64_t stringLiteralCounter = 0; uint64_t specs = 0; }; class HashedASTLoader { public: explicit HashedASTLoader(std::vector&& astData) : serializedData(std::move(astData)) { } ~HashedASTLoader() = default; std::pair DeserializeData(const RawMangled2DeclMap& mangledName2DeclMap); private: bool VerifyData(); ASTCache LoadCachedAST(const CachedASTFormat::HashedPackage& p); std::unordered_map LoadImported(const CachedASTFormat::HashedPackage& p); static SemanticInfo LoadSemanticInfos( const CachedASTFormat::HashedPackage& hasedPackage, const RawMangled2DeclMap& mangledName2DeclMap); MemberDeclCache Load(const CachedASTFormat::MemberDecl& decl); TopLevelDeclCache Load(const CachedASTFormat::TopDecl& decl, bool srcPkg); std::vector serializedData; // pair of mangledname and gvid, to be sorted by gvid std::unordered_map>> fileMap{}; }; } // namespace Cangjie #endif // CANGJIE_COMPILATION_CACHE_SERIALIZATION_H cangjie_compiler-1.0.7/src/IncrementalCompilation/DependencySerialization.cpp000066400000000000000000000212031510705540100275540ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * This file implements the methods of serialize semantic dependency informations. */ #include "CompilationCacheSerialization.h" #include "flatbuffers/CachedASTFormat_generated.h" #include "cangjie/IncrementalCompilation/IncrementalScopeAnalysis.h" using namespace Cangjie; using namespace AST; // Incremental compilation only enable in cjnative backend for now. #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND namespace { using TUsageOffset = flatbuffers::Offset; using TUseInfoOffset = flatbuffers::Offset; using TNameInfoOffset = flatbuffers::Offset; using TRelationOffset = flatbuffers::Offset; using TAddedRelationOffset = flatbuffers::Offset; const int NAME_INFO_CONDITION_SIZE = 2; std::vector CreateNameUsages( flatbuffers::FlatBufferBuilder& builder, const std::map& nameUsages) { std::vector ret(nameUsages.size()); size_t i = 0; for (auto& [name, usage] : std::as_const(nameUsages)) { auto nameIdx = builder.CreateSharedString(name); auto condIdx = builder.CreateVector(std::vector{usage.hasUnqualifiedUsage, usage.hasUnqualifiedUsageOfImported}); auto parentIdx = builder.CreateVectorOfStrings(Utils::SetToVec(usage.parentDecls)); auto qualifierIdx = builder.CreateVectorOfStrings(Utils::SetToVec(usage.packageQualifiers)); ret[i++] = CachedASTFormat::CreateNameInfo(builder, nameIdx, condIdx, parentIdx, qualifierIdx); } return ret; } TUseInfoOffset CreateUseInfos(flatbuffers::FlatBufferBuilder& builder, const UseInfo& usage) { auto usedDeclsIdx = builder.CreateVectorOfStrings(Utils::SetToVec(usage.usedDecls)); auto nameUsageIdx = builder.CreateVector(CreateNameUsages(builder, usage.usedNames)); return CachedASTFormat::CreateUseInfo(builder, usedDeclsIdx, nameUsageIdx); } std::vector CreateUsages(flatbuffers::FlatBufferBuilder& builder, const std::unordered_map, SemaUsage>& unorderedUsageMap) { std::map orderedUsages; for (auto& [decl, usage] : std::as_const(unorderedUsageMap)) { orderedUsages.emplace(decl->rawMangleName, &usage); } std::vector ret(orderedUsages.size()); size_t i = 0; for (auto& [rawMangleName, usage] : std::as_const(orderedUsages)) { auto declIdx = builder.CreateSharedString(rawMangleName); auto apiUsageIdx = CreateUseInfos(builder, usage->apiUsages); auto bodyUsageIdx = CreateUseInfos(builder, usage->bodyUsages); auto boxedTypeIdx = builder.CreateVectorOfStrings(Utils::SetToVec(usage->boxedTypes)); auto usageIdx = CachedASTFormat::CreateUsage(builder, declIdx, apiUsageIdx, bodyUsageIdx, boxedTypeIdx); ret[i++] = usageIdx; } return ret; } std::vector CreateAddedRelations(flatbuffers::FlatBufferBuilder& builder, const std::unordered_map>& relations) { std::vector ret(relations.size()); size_t i = 0; for (auto& [declMangle, relation] : std::as_const(relations)) { auto nameIdx = builder.CreateSharedString(declMangle); auto inheritedIdx = builder.CreateVectorOfStrings(Utils::SetToVec(relation)); auto relationIdx = CachedASTFormat::CreateCompilerAddedUsage(builder, nameIdx, inheritedIdx); ret[i++] = relationIdx; } return ret; } std::vector CreateRelations( flatbuffers::FlatBufferBuilder& builder, const std::unordered_map& relations) { std::map orderedRelations; for (auto& [declMangle, relation] : std::as_const(relations)) { orderedRelations.emplace(declMangle, &relation); } std::vector ret(orderedRelations.size()); size_t i = 0; for (auto& [declMangle, relation] : std::as_const(orderedRelations)) { auto nameIdx = builder.CreateSharedString(declMangle); auto inheritedIdx = builder.CreateVectorOfStrings(Utils::SetToVec(relation->inherits)); auto extendsIdx = builder.CreateVectorOfStrings(Utils::SetToVec(relation->extends)); auto extendInterfacesIdx = builder.CreateVectorOfStrings(Utils::SetToVec(relation->extendedInterfaces)); auto relationIdx = CachedASTFormat::CreateRelation(builder, nameIdx, inheritedIdx, extendsIdx, extendInterfacesIdx); ret[i++] = relationIdx; } return ret; } } // namespace void HashedASTWriter::SetSemanticInfo(const SemanticInfo& info) { semaUsages = CachedASTFormat::CreateSemanticInfo(builder, builder.CreateVector(CreateUsages(builder, info.usages)), builder.CreateVector(CreateRelations(builder, info.relations)), builder.CreateVector(CreateRelations(builder, info.builtInTypeRelations)), builder.CreateVector(CreateAddedRelations(builder, info.compilerAddedUsages))); } namespace { template std::set GetSetStrings(const T* data) { if (data == nullptr) { return {}; } std::set strs; for (uoffset_t i = 0; i < data->size(); ++i) { (void)strs.emplace(data->Get(i)->str()); } return strs; } NameUsage GetNameUsage(const CachedASTFormat::NameInfo* info) { if (info == nullptr) { return {}; } NameUsage usage; usage.parentDecls = GetSetStrings(info->parentDecls()); usage.packageQualifiers = GetSetStrings(info->qualifiers()); CJC_ASSERT(info->conditions()->size() == NAME_INFO_CONDITION_SIZE); usage.hasUnqualifiedUsage = info->conditions()->Get(0); usage.hasUnqualifiedUsageOfImported = info->conditions()->Get(1); return usage; } UseInfo GetUseInfo(const CachedASTFormat::UseInfo* usage) { if (usage == nullptr) { return {}; } UseInfo apiUsage; apiUsage.usedDecls = GetSetStrings(usage->usedDecls()); for (size_t i = 0; i < usage->usedNames()->size(); ++i) { auto nameInfoIdx = usage->usedNames()->Get(static_cast(i)); apiUsage.usedNames.emplace(nameInfoIdx->name()->str(), GetNameUsage(nameInfoIdx)); } return apiUsage; } } // namespace #endif SemanticInfo HashedASTLoader::LoadSemanticInfos( const CachedASTFormat::HashedPackage& hasedPackage, const RawMangled2DeclMap& mangledName2DeclMap) { SemanticInfo info; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto semaInfo = hasedPackage.semanticInfo(); if (semaInfo == nullptr) { return info; } for (uoffset_t i = 0; i < semaInfo->usages()->size(); i++) { auto usageOff = semaInfo->usages()->Get(i); auto found = mangledName2DeclMap.find(usageOff->definition()->str()); if (found == mangledName2DeclMap.end()) { continue; } auto& usage = info.usages[found->second]; usage.apiUsages = GetUseInfo(usageOff->apiUsage()); usage.bodyUsages = GetUseInfo(usageOff->bodyUsage()); usage.boxedTypes = GetSetStrings(usageOff->boxedTypes()); } for (uoffset_t i = 0; i < semaInfo->relations()->size(); i++) { auto relationOff = semaInfo->relations()->Get(i); auto defMangle = relationOff->definition()->str(); auto& relation = info.relations[defMangle]; relation.inherits = GetSetStrings(relationOff->inherited()); relation.extends = GetSetStrings(relationOff->extends()); relation.extendedInterfaces = GetSetStrings(relationOff->extendInterfaces()); } for (uoffset_t i = 0; i < semaInfo->builtInTypeRelations()->size(); i++) { auto relationOff = semaInfo->builtInTypeRelations()->Get(i); auto builtIntypeName = relationOff->definition()->str(); auto& relation = info.builtInTypeRelations[builtIntypeName]; relation.extends = GetSetStrings(relationOff->extends()); relation.extendedInterfaces = GetSetStrings(relationOff->extendInterfaces()); } for (uoffset_t i = 0; i < semaInfo->compilerAddedUsages()->size(); i++) { auto relationOff = semaInfo->compilerAddedUsages()->Get(i); auto rawMangle = relationOff->definition()->str(); info.compilerAddedUsages[rawMangle] = GetSetStrings(relationOff->related()); } #endif return info; } cangjie_compiler-1.0.7/src/IncrementalCompilation/IncrementalScopeAnalysis.cpp000066400000000000000000000572131510705540100277110ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/IncrementalCompilation/IncrementalScopeAnalysis.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/IncrementalCompilation/ASTCacheCalculator.h" #include "CompilationCacheSerialization.h" #include "cangjie/IncrementalCompilation/IncrementalCompilationLogger.h" #include "cangjie/IncrementalCompilation/Utils.h" #include "cangjie/Parse/ASTHasher.h" #include "ASTDiff.h" #include "PollutionAnalyzer.h" using namespace Cangjie; using namespace Cangjie::AST; using namespace Cangjie::IncrementalCompilation; namespace { struct DumpInfo { std::string identifier; std::string fileName; size_t lineNum; size_t columnNum; DumpInfo(const std::string& identifier, const std::string& fileName, size_t lineNum, size_t columnNum) : identifier(identifier), fileName(fileName), lineNum(lineNum), columnNum(columnNum) { } bool operator<(const DumpInfo& rhs) const { // compare by file, line, and column in dictionary order // Note that some compiler-generated decls do not have line info, and for these decls their identifier is // compared in dictionary order instead return std::tie(fileName, lineNum, columnNum, identifier) < std::tie(rhs.fileName, rhs.lineNum, rhs.columnNum, rhs.identifier); } }; void DumpDeclsToRecompile(const std::unordered_set>& declsToRecompile) { auto& logger = IncrementalCompilationLogger::GetInstance(); if (!logger.IsEnable()) { return; } if (!declsToRecompile.empty()) { logger.LogLn("changed decls to recompile: " + std::to_string(declsToRecompile.size())); std::vector infos; for (auto decl : declsToRecompile) { if (decl->identifier.Empty()) { if (decl->astKind == ASTKind::VAR_WITH_PATTERN_DECL) { infos.emplace_back("VarWithPatternDecl", GetTrimmedPath(decl->curFile.get()), decl->begin.line, decl->begin.column); } else if (decl->astKind == ASTKind::EXTEND_DECL) { infos.emplace_back( "ExtendDecl", GetTrimmedPath(decl->curFile.get()), decl->begin.line, decl->begin.column); } else { CJC_ABORT(); } } else { infos.emplace_back(decl->identifier.Val(), GetTrimmedPath(decl->curFile.get()), decl->identifier.Begin().line, decl->identifier.Begin().column); } } std::sort(infos.begin(), infos.end()); for (const auto& info : infos) { logger.LogLn("the changed decl after Spreading is:" + info.identifier + " in file " + info.fileName + " in line " + std::to_string(info.lineNum) + " in column " + std::to_string(info.columnNum)); } } } void DumpDeleted(const std::list& deleted) { auto& logger = IncrementalCompilationLogger::GetInstance(); if (!logger.IsEnable()) { return; } if (!deleted.empty()) { logger.LogLn("deleted decls: " + std::to_string(deleted.size())); for (auto del : deleted) { logger.LogLn("the deleted decl is:" + del); } } } } // namespace void IncreResult::Dump() const { if (!IncrementalCompilationLogger::GetInstance().IsEnable()) { return; } auto& logger = IncrementalCompilationLogger::GetInstance(); if (kind != IncreKind::INCR) { switch (kind) { case IncreKind::ROLLBACK: logger.LogLn("incremental compile result kind: rollback"); return; case IncreKind::EMPTY_PKG: logger.LogLn("incremental compile result kind: empty package"); return; case IncreKind::NO_CHANGE: logger.LogLn("incremental compile result kind: no change"); return; default: CJC_ABORT(); return; } } std::stringstream out{}; std::string deliStr = std::string(DELIMITER_NUM, '='); logger.LogLn(deliStr); logger.LogLn("incremental compilation triggered"); logger.LogLn("begin dump incremental compile result:"); DumpDeclsToRecompile(declsToRecompile); logger.LogLn(deliStr); DumpDeleted(deleted); logger.LogLn(deliStr); } static const std::string GetName(const AST::Decl& decl) { if (decl.astKind == AST::ASTKind::EXTEND_DECL) { return "extend"; } else { return decl.identifier; } } static const std::string OutputDeclInfo(const RawMangled2DeclMap& rawMangled2Decl, const std::string& rawMangledName) { auto srcIt = rawMangled2Decl.find(rawMangledName); if (srcIt == rawMangled2Decl.end()) { return rawMangledName; } CJC_NULLPTR_CHECK(srcIt->second); const AST::Decl* decl = srcIt->second; return GetName(*decl) + "(l:" + std::to_string(decl->begin.line) + ", c:" + std::to_string(decl->begin.column) + ")"; } static void PrintCHIROptEffectMap( const OptEffectStrMap& effectMap, const RawMangled2DeclMap& rawMangled2Decl) { auto& logger = IncrementalCompilationLogger::GetInstance(); if (!logger.IsEnable()) { return; } logger.LogLn("[CHIR Effect Map] START"); // convert `std::unordered_map` to `std::map` std::map> orderedMap; for (auto effectIt : effectMap) { std::set orderedDecls; for (auto nameIt : effectIt.second) { orderedDecls.emplace(nameIt); } orderedMap.emplace(effectIt.first, orderedDecls); } std::string msg; for (auto mapIt : std::as_const(orderedMap)) { msg = OutputDeclInfo(rawMangled2Decl, mapIt.first) + " -> "; for (auto name : mapIt.second) { msg += OutputDeclInfo(rawMangled2Decl, name) + ","; } logger.LogLn(msg); } logger.LogLn("[CHIR Effect Map] END"); } namespace Cangjie { ///@{ /// used by IncrementalCompilerInstance extern void MergeCHIROptEffectMap( const OptEffectStrMap& newMap, OptEffectStrMap& lastCachedMap, const RawMangled2DeclMap& rawMangled2Decl) { for (auto mapIt : newMap) { CJC_ASSERT(!mapIt.first.empty()); CJC_ASSERT(mapIt.second.find(mapIt.first) == mapIt.second.end()); if (mapIt.second.empty()) { continue; } auto& effectInfo = lastCachedMap[mapIt.first]; for (auto effectedIt : mapIt.second) { CJC_ASSERT(!effectedIt.empty()); effectInfo.emplace(effectedIt); } } PrintCHIROptEffectMap(lastCachedMap, rawMangled2Decl); } extern void DeleteRemovedNodesInCHIROptEffectMap( const std::list& removedDecls, OptEffectStrMap& effectMap) { auto it = effectMap.begin(); while (it != effectMap.end()) { auto rawIt = std::find(removedDecls.begin(), removedDecls.end(), it->first); if (rawIt != removedDecls.end()) { it = effectMap.erase(it); continue; } auto effectedIt = it->second.begin(); while (effectedIt != it->second.end()) { auto rawIt2 = std::find(removedDecls.begin(), removedDecls.end(), *effectedIt); if (rawIt2 != removedDecls.end()) { effectedIt = it->second.erase(effectedIt); } else { ++effectedIt; } } ++it; } } extern void DeleteRecompiledNodesInCHIROptEffectMap( const std::unordered_set>& recompiledNodes, OptEffectStrMap& effectMap) { std::unordered_set recompiledMangledName; for (auto decl : recompiledNodes) { if (auto mainDecl = DynamicCast(decl); mainDecl) { recompiledMangledName.emplace(mainDecl->desugarDecl->rawMangleName); } else { recompiledMangledName.emplace(decl->rawMangleName); } } // if one decl is recompiled, we need to remove it from old `effectMap` // structure of `effectMap` is `src decl -> effected decls`, means `src decl` is optimized in `effected decls` // we only remove recompiled decl from `effected decls`, mustn't remove it from `src decl` auto it = effectMap.begin(); while (it != effectMap.end()) { auto effectedIt = it->second.begin(); while (effectedIt != it->second.end()) { if (recompiledMangledName.find(*effectedIt) != recompiledMangledName.cend()) { effectedIt = it->second.erase(effectedIt); } else { ++effectedIt; } } if (it->second.empty()) { it = effectMap.erase(it); } else { ++it; } } } ///@} } static std::optional LoadCacheInfo( const std::string& path, const RawMangled2DeclMap& mangledName2DeclMap) { std::vector data; std::string failedReason; if (!FileUtil::ReadBinaryFileToBuffer(path, data, failedReason)) { return {}; } HashedASTLoader loader(std::move(data)); auto [succ, cachedInfo] = loader.DeserializeData(mangledName2DeclMap); if (!succ) { return {}; } return cachedInfo; } namespace { struct ImportPackageWalker { static std::tuple Get( const AST::Package& sourcePackage, const ImportManager& man) { ASTCache ret{}; RawMangled2DeclMap retMap{}; SrcImportedDepMap srcImportedDeclDeps{}; TypeMap relations{}; // extra relations of imported packages std::vector> collectedDecls; ImportPackageWalker w{retMap, srcImportedDeclDeps}; for (auto& p : man.GetAllImportedPackages()) { if (p->srcPackage == &sourcePackage) { continue; } for (auto decl : GetAllDecls(*p)) { // Some compiler-added decls do not have raw mangle name, skip them if (decl->rawMangleName.empty()) { continue; } w.UpdateMangleNameAndCollectDep(*decl); retMap.emplace(decl->rawMangleName, decl); collectedDecls.emplace_back(decl); } } for (auto decl : collectedDecls) { auto declCache = w.VisitTopLevel(*decl); ret.emplace(decl->rawMangleName, declCache); relations.CollectImportedDeclExtraRelation(*decl); } return {std::move(ret), std::move(retMap), std::move(srcImportedDeclDeps), std::move(relations)}; } static std::list> GetAllDecls(const AST::PackageDecl& package) { std::list> res{}; for (auto& file : package.srcPackage->files) { for (auto& decl : file->decls) { res.push_back(decl.get()); } for (auto& decl : file->exportedInternalDecls) { res.push_back(decl.get()); } } return res; } /** * @brief Convert decl's rawMangleName to appendedMangleName based on ty, * used to compare imported decls at ASTDiff * * @param decl Imported AST::Decl from cjo * * erase characters after '$$' on rawMangleName and concatenate ty->String() * e.g `func foo(){Int32(0)}` * rawMangleName is _CN1A3foob$bFE$$E * after processing: _CN1A3foob$bFE$$Int32E */ static void ReplaceTypeWithTyInMangleName(AST::Decl& decl) { // macro return type is always tokens; primary ctor always returns nothing; // main decl is never imported, so only func and var decl are covered here if (Utils::NotIn(decl.astKind, {AST::ASTKind::FUNC_DECL, AST::ASTKind::VAR_DECL}) || decl.TestAnyAttr(AST::Attribute::CONSTRUCTOR, AST::Attribute::MACRO_FUNC) || !AST::Ty::IsTyCorrect(decl.ty) || decl.rawMangleName == TO_ANY) { // 1. Ignore VarWithPatternDecl because there is no VarWithPatternDecl in cjo. // 2. Constructor's return type will not be changed. return; } auto& mangledName = decl.rawMangleName; auto pos = mangledName.rfind("$$"); CJC_ASSERT(pos != std::string::npos); const static auto DELIMITER_LENGTH = std::string("$$").size(); (void)mangledName.erase(pos + DELIMITER_LENGTH); if (decl.astKind == AST::ASTKind::FUNC_DECL) { auto funcTy = StaticCast(decl.ty); CJC_ASSERT(funcTy && funcTy->retTy); mangledName += funcTy->retTy->String(); } else { mangledName += decl.ty->String(); } } ImportPackageWalker(RawMangled2DeclMap& map, SrcImportedDepMap& srcImportedDeps) : map{map}, srcImportedDeps{srcImportedDeps} { } RawMangled2DeclMap& map; // mangled -> decl is collected here SrcImportedDepMap& srcImportedDeps; bool SkipImportedBodyHash(const AST::Decl& decl) const { if (decl.IsConst()) { return false; } if (auto varDecl = DynamicCast(&decl)) { if (varDecl->initializer == nullptr) { return true; } } if (auto funcDecl = DynamicCast(&decl)) { CJC_ASSERT(funcDecl->funcBody); if (funcDecl->funcBody->body != nullptr) { return false; } // if any parameter has desugar decl, it has a default value and the default value function can be inlined, // so body hash of the function cannot be ignored for (auto& param: funcDecl->funcBody->paramLists[0]->params) { if (param->desugarDecl && param->desugarDecl->funcBody && param->desugarDecl->funcBody->body) { return false; } } return true; } return false; } TopLevelDeclCache VisitTopLevel(AST::Decl& decl) { (void)map.emplace(decl.rawMangleName, &decl); TopLevelDeclCache ret{}; for (auto member : GetMembers(decl)) { // some compiler-added decls do not have raw mangle name, skip them if (member->rawMangleName.empty()) { continue; } ret.members.push_back(VisitMember(*member)); } ret.instVarHash = decl.hash.instVar; ret.virtHash = decl.hash.virt; ret.sigHash = decl.hash.sig; ret.srcUse = decl.hash.srcUse; ret.bodyHash = SkipImportedBodyHash(decl) ? 0 : ASTHasher::ImportedDeclBodyHash(decl); // no gvid for imported decl return ret; } MemberDeclCache VisitMember(AST::Decl& decl) { (void)map.emplace(decl.rawMangleName, &decl); MemberDeclCache ret{}; for (auto member : IncrementalCompilation::GetMembers(decl)) { // some compiler-added decls do not have raw mangle name, skip them if (member->rawMangleName.empty()) { continue; } ret.members.push_back(VisitMember(*member)); } ret.rawMangle = decl.rawMangleName; ret.sigHash = decl.hash.sig; ret.srcUse = decl.hash.srcUse; ret.bodyHash = SkipImportedBodyHash(decl) ? 0 : ASTHasher::ImportedDeclBodyHash(decl); return ret; } void UpdateMangleNameAndCollectDep(AST::Decl& decl) { ReplaceTypeWithTyInMangleName(decl); switch (decl.astKind) { // primary ctors are desugared into normal ctors in imported packages; // macro are not collected as they are used only before ASTDiff but not after it; // main are never exported; so only normal functions are considered here. case ASTKind::FUNC_DECL: { auto& d = StaticCast(decl); // collect source dependencies only when the function decl has a body in imported packages if (d.funcBody && d.funcBody->body) { CollectSrcImportedDep(decl, *d.funcBody->body); } break; } case ASTKind::VAR_DECL: { auto& d = StaticCast(decl); if (d.initializer) { CollectSrcImportedDep(decl, *d.initializer); } break; } default: break; } for (auto member : GetMembers(decl)) { // some compiler-added decls do not have raw mangle name, skip them if (member->rawMangleName.empty()) { continue; } UpdateMangleNameAndCollectDep(*member); } } void CollectSrcImportedDep(const AST::Decl& decl, AST::Expr& bodyExpr) const { AST::Walker(&bodyExpr, [&](auto node) { if (auto usedDecl = node->GetTarget()) { srcImportedDeps[usedDecl].insert(&decl); } return AST::VisitAction::WALK_CHILDREN; }).Walk(); } }; } // namespace static IncreResult TerminateAnalysisAndRollback( CompilationCache&& cache, IncrementalScopeAnalysisArgs&& args, const std::string& msg) { auto [importedASTCache, importedMangled2Decl, sourcePopulations, rel] = ImportPackageWalker::Get(args.srcPackage, args.importer); cache.curPkgASTCache = std::move(args.astCacheInfo); cache.importedASTCache = std::move(importedASTCache); importedMangled2Decl.insert(args.rawMangleName2DeclMap.begin(), args.rawMangleName2DeclMap.end()); IncrementalCompilationLogger::GetInstance().LogLn(msg); return {IncreKind::ROLLBACK, {}, {}, {}, std::move(cache), std::move(importedMangled2Decl)}; } static bool IsCCOutFuncNeedToRecompileOrDelete( const CompilationCache& preCache, const PollutionResult& pollutionRes) { if (preCache.ccOutFuncs.empty()) { return false; } for (auto& rawMangle : pollutionRes.deleted) { if (auto it = preCache.ccOutFuncs.find(rawMangle); it != preCache.ccOutFuncs.end()) { IncrementalCompilationLogger::GetInstance().LogLn("closure convert out func recompile: " + rawMangle); return true; } } for (auto decl : pollutionRes.declsToRecompile) { if (auto it = preCache.ccOutFuncs.find(decl->rawMangleName); it != preCache.ccOutFuncs.end()) { IncrementalCompilationLogger::GetInstance().LogLn( "closure convert out func recompile: " + decl->rawMangleName); return true; } } return false; } namespace { void BuildMangleMapForCachedInfo( const MemberDeclCache& memCache, std::unordered_map& mangleMap) { mangleMap[memCache.rawMangle] = memCache.cgMangle; for (auto& m : memCache.members) { BuildMangleMapForCachedInfo(m, mangleMap); } } void BuildMangleMapForCachedInfo(const std::string& rawMangle, const TopLevelDeclCache& topCache, std::unordered_map& mangleMap) { mangleMap[rawMangle] = topCache.cgMangle; for (auto& m : topCache.members) { BuildMangleMapForCachedInfo(m, mangleMap); } } std::list GetCgManglesForDelete( const CompilationCache& cachedInfo, const std::list& deleted) { if (deleted.empty()) { return {}; } std::unordered_map mangleMap{}; for (const auto& it : cachedInfo.curPkgASTCache) { auto rawMangle = it.first; BuildMangleMapForCachedInfo(rawMangle, it.second, mangleMap); } for (const auto& it : cachedInfo.importedASTCache) { auto rawMangle = it.first; BuildMangleMapForCachedInfo(rawMangle, it.second, mangleMap); } std::list deletedMangleNames; for (auto d : deleted) { if (auto it = mangleMap.find(d); it != mangleMap.end()) { deletedMangleNames.push_back(it->second); } else { CJC_ABORT(); } } return deletedMangleNames; } } IncreResult Cangjie::IncrementalScopeAnalysis(IncrementalScopeAnalysisArgs&& args) { // Load the compilation cache std::string cachePath = args.op.GenerateCachedPathName(args.srcPackage.fullPackageName, CACHED_AST_EXTENSION); auto loadingRes = LoadCacheInfo(cachePath, args.rawMangleName2DeclMap); bool badCache = !loadingRes.has_value(); std::string cjoPath = args.op.GenerateCachedPathName(args.srcPackage.fullPackageName, SERIALIZED_FILE_EXTENSION); if (badCache || !FileUtil::FileExist(cjoPath)) { return TerminateAnalysisAndRollback( CompilationCache{}, std::move(args), "load cached info failed, roll back to full compilation"); } auto& prevCompilationCache = loadingRes.value(); bool compilationArgsChange = args.op.ToSerialized() != prevCompilationCache.compileArgs; if (compilationArgsChange) { return TerminateAnalysisAndRollback( std::move(prevCompilationCache), std::move(args), "compile args changed, roll back to full compilation"); } bool specsChange = ASTHasher::HashSpecs(args.srcPackage) != prevCompilationCache.specs; if (specsChange) { return TerminateAnalysisAndRollback(std::move(prevCompilationCache), std::move(args), "package or import specs changed, roll back to full compilation"); } auto [importedASTCache, importedMangled2Decl, sourcePopulations, importRelations] = ImportPackageWalker::Get(args.srcPackage, args.importer); // Compare the previous compilation cache with the current AST (including the imported part) auto [modifiedDecls, mangle2decl] = ASTDiff({prevCompilationCache, importedASTCache, importedMangled2Decl, args.rawMangleName2DeclMap, args.astCacheInfo, args.fileMap, args.op}); modifiedDecls.Dump(); if (!modifiedDecls.aliases.empty()) { return TerminateAnalysisAndRollback( std::move(prevCompilationCache), std::move(args), "type alias changed, roll back to full compilation"); } // Analyse the pollution scope caused by the raw code changes auto srcPkgDecl = args.importer.GetPackageDecl(args.srcPackage.fullPackageName); CJC_NULLPTR_CHECK(srcPkgDecl); auto pollutionRes = PollutionAnalyzer::Get({std::move(modifiedDecls), *srcPkgDecl, sourcePopulations, prevCompilationCache.semaInfo, prevCompilationCache.chirOptInfo, prevCompilationCache.fileMap, args.importer, mangle2decl, std::move(args.directExtends), std::move(importRelations)}); // not support closure convert incr if (IsCCOutFuncNeedToRecompileOrDelete(prevCompilationCache, pollutionRes)) { DumpDeclsToRecompile(pollutionRes.declsToRecompile); DumpDeleted(pollutionRes.deleted); return TerminateAnalysisAndRollback(std::move(prevCompilationCache), std::move(args), "closure convert out func recompile, roll back to full compilation"); } // Mark changed decls to be recompiled. if (pollutionRes.kind == IncreKind::INCR) { for (auto decl : pollutionRes.declsToRecompile) { decl->toBeCompiled = true; } } auto deletedMangleNames = GetCgManglesForDelete(prevCompilationCache, pollutionRes.deleted); // Update compilation cache as the result of current compilation process prevCompilationCache.curPkgASTCache = std::move(args.astCacheInfo); prevCompilationCache.importedASTCache = std::move(importedASTCache); return {pollutionRes.kind, std::move(pollutionRes.declsToRecompile), std::move(pollutionRes.deleted), deletedMangleNames, std::move(prevCompilationCache), std::move(mangle2decl), std::move(pollutionRes.reBoxedTypes)}; } cangjie_compiler-1.0.7/src/IncrementalCompilation/PollutionAnalyzer.cpp000066400000000000000000001441251510705540100264440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "PollutionAnalyzer.h" #include "PollutionMapGen.h" #include "cangjie/IncrementalCompilation/Utils.h" #include "cangjie/Mangle/ASTMangler.h" #include "cangjie/Sema/IncrementalUtils.h" using namespace Cangjie; using namespace Cangjie::AST; using namespace Cangjie::IncrementalCompilation; namespace { } // namespace void TypeMap::CollectImportedDeclExtraRelation(const AST::Decl& decl) { if (!decl.IsNominalDecl()) { return; } auto& type{StaticCast(decl)}; for (auto& parentType: type.inheritedTypes) { auto parentDecl = Ty::GetDeclOfTy(parentType->ty); CJC_NULLPTR_CHECK(parentDecl); // all builtin types are struct or enum and cannot have child types AddParent(*parentDecl, decl); } if (auto extend = DynamicCast(&type)) { AddExtend(Sema::GetTypeRawMangleName(*extend->ty), decl.rawMangleName); } } static std::list> GetAllMembers(const Decl& decl) { std::list> res{}; for (auto member : decl.GetMemberDeclPtrs()) { res.splice(res.cend(), GetAllMembers(*member)); res.push_back(member); } if (auto p = DynamicCast(&decl)) { for (auto& getter : p->getters) { res.push_back(getter.get()); } for (auto& setter : p->setters) { res.push_back(setter.get()); } } return res; } PollutionResult PollutionAnalyzer::Get(PollutionAnalyseArgs&& args) { if (!args.rawModified.aliases.empty()) { if (auto& logger = IncrementalCompilationLogger::GetInstance(); logger.IsEnable()) { std::stringstream r; for (auto decl : std::as_const(args.rawModified.aliases)) { r << "changed typealias: "; PrintDecl(r, *decl); r << '\n'; } logger.LogLn(r.str()); } return {IncreKind::ROLLBACK}; } // Get population graph and type relation. auto [pollutedMap, typeMap] = PollutionMapGen::Get( args.pkg, args.mangled2Decl, args.sourcePopulations, args.semaInfo, args.chirOptInfo, args.man); typeMap.Merge(std::move(args.importedRelations)); PollutionAnalyzer analyzer{ std::move(pollutedMap), std::move(typeMap), args.mangled2Decl, args.chirOptInfo, std::move(args.extends)}; for (auto decl : std::as_const(args.rawModified.added)) { CJC_NULLPTR_CHECK(decl); analyzer.PollutionForAddedDecl(*decl); } if (analyzer.FallBack()) { analyzer.PrintFallbackInfo(); return {IncreKind::ROLLBACK}; } // Start pollution for deleted decls for (auto& decl : std::as_const(args.rawModified.deletes)) { analyzer.PollutionForDeletedDecl(decl); } if (analyzer.FallBack()) { analyzer.PrintFallbackInfo(); return {IncreKind::ROLLBACK}; } for (auto& decl : std::as_const(args.rawModified.deletedTypeAlias)) { analyzer.removedNotSupported.push_back(decl); } if (analyzer.FallBack()) { analyzer.PrintFallbackInfo(); return {IncreKind::ROLLBACK}; } // Start pollution for changed non-type decls for (auto& change : std::as_const(args.rawModified.commons)) { analyzer.PollutionForChangedNonTypeDecl(change.second); } if (analyzer.FallBack()) { analyzer.PrintFallbackInfo(); return {IncreKind::ROLLBACK}; } // Start pollution for changed type decls for (auto& change : std::as_const(args.rawModified.types)) { analyzer.PollutionForChangedTypeDecl(*change.first, change.second); } if (analyzer.FallBack()) { analyzer.PrintFallbackInfo(); return {IncreKind::ROLLBACK}; } for (auto t: std::as_const(args.rawModified.orderChanges)) { CJC_NULLPTR_CHECK(t); analyzer.PollutionForOrderChangeDecl(*t); } PollutionResult res{ IncreKind::INCR, analyzer.GetPollutionResult(), analyzer.GetDeletedResult(), analyzer.GetReBoxedTypes()}; if (res.declsToRecompile.empty() && res.deleted.empty()) { res.kind = IncreKind::NO_CHANGE; } return res; } void PollutionAnalyzer::AddToPollutedDecls(const Decl& decl) { pollutedDecls.emplace(&decl); if (auto func = DynamicCast(&decl); func && IsImported(*func) && func->funcBody) { // insert default parameter functions into the recompile list if the body hash of the function changes; // the reason of doing this here rather than in ASTDiff is that it is possible for imported decls to be // propagated to, e.g. from a change in some class into its subclass, then to the default value parameter // whose declaration type is the subclass. for (auto& param: func->funcBody->paramLists[0]->params) { // collect it whether the desugared func has body or not, otherwise the change of inlinability of a default // parameter function cannot be detected if (param->desugarDecl) { param->desugarDecl->rawMangleName = param->desugarDecl->mangledName; AddToPollutedDecls(*param->desugarDecl); } } } else if (auto varWithPattern = DynamicCast(&decl)) { auto allPatterns = FlattenVarWithPatternDecl(*varWithPattern); for (auto pattern : allPatterns) { if (pattern->astKind != ASTKind::VAR_PATTERN) { continue; } auto varPattern = StaticCast(pattern); pollutedDecls.emplace(varPattern->varDecl); } } } // Propagate pollution for added decl, which can be either: // 1) a top-level VarDecl, VarWithPatternDecl, FuncDecl, type related decl // 2) a member VarDecl, FuncDecl, PropDecl. In this case, we assume the parent decl has been handled void PollutionAnalyzer::PollutionForAddedDecl(const Decl& decl) { // Special case handling for type alias decl if (decl.astKind == ASTKind::TYPE_ALIAS_DECL) { typeAliases.push_back(&decl); return; } if (Is(&decl)) { PollutionForAddedTypeDecl(decl); } else { PollutionForAddedNonTypeDecl(decl); } } void PollutionAnalyzer::PollutionForAddedTypeDecl(const Decl& decl) { if (decl.TestAttr(Attribute::IMPORTED)) { // All imported stuff has been sema-d in upstream package. We only need to: // 1) analysis the pollution for the added decl // 2) recompile those generics bool genericImported = IsInDeclWithAttribute(decl, Attribute::GENERIC); if (genericImported) { AddToPollutedDecls(decl); for (const auto member : decl.GetMemberDeclPtrs()) { PollutionForAddedNonTypeDecl(*member); } } else { for (const auto member : decl.GetMemberDeclPtrs()) { PollutionForAddedNonTypeDecl(*member); } } } else { AddToPollutedDecls(decl); const auto &allMembers = GetAllMembers(decl); for (const auto member : std::as_const(allMembers)) { PollutionForAddedNonTypeDecl(*member); } } PollutedUnqualifiedUses(decl); PollutedGlobalChangeToPackageQualifiedUses(decl); PolluteGlobalChangeToQualifiedUses(decl); if (auto extend = DynamicCast(&decl)) { AdditionPollutionForAddedExtendDecl(*extend); } } void PollutionAnalyzer::PollutionForAddedNonTypeDecl(const Decl& decl) { if (decl.TestAttr(Attribute::IMPORTED)) { // All imported stuff has been sema-d in upstream package. We only need to: // 1) analysis the pollution for the added decl // 2) recompile those source imported functions, global variables and generics bool srcImported = false; if (auto varDecl = DynamicCast(&decl)) { if (varDecl->TestAttr(Attribute::GLOBAL) && varDecl->initializer != nullptr) { srcImported = true; } } else if (auto funcDecl = DynamicCast(&decl)) { if (funcDecl->funcBody != nullptr && funcDecl->funcBody->body != nullptr) { srcImported = true; } } bool genericImported = IsInDeclWithAttribute(decl, Attribute::GENERIC); if (srcImported || genericImported) { AddToPollutedDecls(decl); } } else { AddToPollutedDecls(decl); } PollutionForAddedNonTypeDeclImpl(decl); } void PollutionAnalyzer::PollutionForAddedNonTypeDeclImpl(const Decl& decl) { if (auto propDecl = DynamicCast(&decl)) { for (auto& getter : propDecl->getters) { AddToPollutedDecls(*getter); PollutionForAddedNonTypeDeclImpl(*getter); } for (auto& setter : propDecl->setters) { AddToPollutedDecls(*setter); PollutionForAddedNonTypeDeclImpl(*setter); } } if (auto varWithPatternDecl = DynamicCast(&decl)) { auto allPatterns = FlattenVarWithPatternDecl(*varWithPatternDecl); for (auto pattern : allPatterns) { if (pattern->astKind != ASTKind::VAR_PATTERN) { continue; } auto varPattern = StaticCast(pattern); AddToPollutedDecls(*varPattern->varDecl); PollutionForAddedNonTypeDeclImpl(*varPattern->varDecl); } } // For newly added member VarDecl, FuncDecl or PropDecl, can we pollute a smaller scope here? // For example, we just need to pollute the qualified usage and unqualified usage inside the // parent struct/class. Besides, the EnumDecl is another story where we should pollute both // qualified usage and unqualified usage in whole program PollutedUnqualifiedUses(decl); PollutedGlobalChangeToPackageQualifiedUses(decl); PolluteGlobalChangeToQualifiedUses(decl); } void PollutionAnalyzer::AdditionPollutionForBodyChangedExtendDecl(const ExtendDecl& decl) { if (decl.inheritedTypes.empty()) { if (auto extendsIt = directExtends.find(decl.rawMangleName); extendsIt != directExtends.cend()) { for (auto& extend : std::as_const(extendsIt->second)) { if (pollutedDecls.count(extend) == 0) { AddToPollutedDecls(*extend); PollutionForAddedTypeDecl(*extend); } } } } } void PollutionAnalyzer::AdditionPollutionAPIOfExtendedDecl(const AST::ExtendDecl& decl) { if (auto extendedTypeRawMangleName = GetExtendedTypeRawMangleName(decl)) { // Serveral cases here: // 1) the extended type is a user-defined type which have proper raw mangle name, then // we can find the type decl in the `mangled2Decl` map and pollute it // 2) the extended type is a primitive type (Int64, Boolean, etc) or built-in type (CPointer, CString), // which don't have a type decl. In this case, we manually find and pollute all other ExtendDecls that // extends this type if (auto it = mangled2Decl.find(*extendedTypeRawMangleName); it != mangled2Decl.cend()) { PolluteAPIOfDecl(*it->second); } else { for (auto extend : t.GetAllExtendsOfType(*extendedTypeRawMangleName)) { if (auto itt = mangled2Decl.find(extend); itt != mangled2Decl.cend()) { auto extendOfBuiltinDecl = StaticCast(itt->second); AddToPollutedDecls(*extendOfBuiltinDecl); PollutedInstantiationChangeFromDecl(*extendOfBuiltinDecl); } } } } } void PollutionAnalyzer::AdditionPollutionAPIOfExtendDecl(const AST::ExtendDecl& decl) { // Pollute the extended type AdditionPollutionAPIOfExtendedDecl(decl); // Specially, for direct extends, since we merge them into one when calculating the raw mangle name and // hash, so we have to manually pollute all other same direct extends AdditionPollutionAPIOfDirectExtendDecls(decl.rawMangleName); } void PollutionAnalyzer::AdditionPollutionForAddedExtendDecl(const ExtendDecl& decl) { if (decl.inheritedTypes.empty() && decl.GetMemberDeclPtrs().empty()) { return; } AdditionPollutionAPIOfExtendedDecl(decl); // Specially, for direct extends, since we merge them into one when calculating the raw mangle name and // hash, so we have to manually pollute all other same direct extends AdditionPollutionForBodyChangedExtendDecl(decl); } static size_t TruncatePrecedingNumber(const std::string& mangle, size_t begin, size_t end) { CJC_ASSERT(end <= mangle.size()); for (size_t i{begin}; i < end; ++i) { // skip the number if there is one preceding the identifier if (!isdigit(mangle[i])) { return i; } } CJC_ASSERT(false); return std::string::npos; } // search for last appearance of '.' and return the position after it from \p begin to \p end exclusive. // returns begin if '.' is not found. static size_t TruncatePackage(const std::string& mangle, size_t end) { CJC_ASSERT(end != 0 && end <= mangle.size()); for (auto i{end - 1}; ; --i) { if (mangle[i] == '.') { return i + 1; } if (i == 0) { break; } } return 0; } static size_t TruncateGeneric(const std::string& mangle) { auto end = mangle.find("<"); if (end == std::string::npos) { return mangle.size(); } return end; } static std::string GetIdentifierFromTruncatedName(const std::string& mangle) { CJC_ASSERT(!mangle.empty()); auto end = TruncateGeneric(mangle); auto begin = TruncatePackage(mangle, end); auto begin2 = TruncatePrecedingNumber(mangle, begin, end); CJC_ASSERT(end > begin2); return mangle.substr(begin2, end - begin2); } // Propagate pollution for deleted decl, which can be either: // 1) a top-level VarDecl, VarWithPatternDecl, FuncDecl, type related decl // 2) a member VarDecl, FuncDecl, PropDecl. In this case, we assume the parent decl has been handled void PollutionAnalyzer::PollutionForDeletedDecl(const RawMangledName& mangle) { deletedDecls.emplace_back(mangle); // For deleted decl, we need to pollute the sema-based precise usage PollutePreciseUsages(mangle); // For deleted extend decl, we also need to pollute the extended type decl if (auto extendedTypeName = t.FindExtendedTypeByExtendDeclMangleName(mangle)) { if (auto extendedType = mangled2Decl.find(*extendedTypeName); extendedType != mangled2Decl.cend()) { PolluteAPIOfDecl(*extendedType->second); } else { for (auto extend : t.GetAllExtendsOfType(*extendedTypeName)) { if (auto it = mangled2Decl.find(extend); it != mangled2Decl.cend()) { auto extendOfBuiltinDecl = StaticCast(it->second); AddToPollutedDecls(*extendOfBuiltinDecl); PollutedInstantiationChangeFromDecl(*extendOfBuiltinDecl); } } } } else { // deleted decl not found in cache, it is a deleted imported decl // if that is an ExtendDecl, find the extended type if (auto truncatedExtendName = ASTMangler::TruncateExtendMangledName(mangle)) { auto identifier = GetIdentifierFromTruncatedName(*truncatedExtendName); std::list candidateSet{}; for (auto& pair : mangled2Decl) { auto decl = pair.second; if (decl->identifier == identifier && decl->IsNominalDecl() && decl->astKind != ASTKind::EXTEND_DECL) { candidateSet.push_back(decl); } } for (auto t1: std::as_const(candidateSet)) { CJC_NULLPTR_CHECK(t1); AddToPollutedDecls(*t1); PolluteBoxUsesFromDecl(*t1); PollutedInstantiationChangeFromDecl(*t1); } // extended type not found, this is an imported decl of builtin type // propagate to its extends if (candidateSet.empty()) { PollutedToBoxUses(identifier); } } } // For deleted type decl, we also need to pollute all the ExtendDecls which extends it for (auto element : FindAllExtendDeclsOfType(mangle)) { AddToPollutedDecls(*element); PollutedInstantiationChangeFromDecl(*element); } } void PollutionAnalyzer::PollutionForConstDecl(const Decl& decl) { AddToPollutedDecls(decl); if (decl.TestAttr(Attribute::GENERIC)) { PollutedInstantiationChangeFromDecl(decl); } } void PollutionAnalyzer::PollutionForOrderChangeDecl(const AST::Decl& decl) { // the minimal propagation for a changed decl is instantiation change, which is the implementation of const decl // change. PollutionForConstDecl(decl); } void PollutionAnalyzer::PollutionForChangedNonTypeDecl(const CommonChange& c) { AddToPollutedDecls(*c.decl); if (c.sig) { PollutionForSigChangedDecl(*c.decl); } else if (c.srcUse) { PollutionForSrcUseChangedDecl(*c.decl); } else if (c.body) { PollutionForBodyChangedDecl(*c.decl); } // Specially, if we are changing a decl inside a generic decl, we need to // pollute the generic decl due to the need of re-instantiation if (c.decl->outerDecl && IsInDeclWithAttribute(*c.decl->outerDecl, Attribute::GENERIC)) { PollutedInstantiationChangeFromDecl(*c.decl->outerDecl); } } void PollutionAnalyzer::PollutionForBodyChangedDecl(const Decl& decl) { if (auto type = DynamicCast(&decl)) { if (typeChanges[type].body) { return; } typeChanges[type].body = true; // propagate the extended type if the type changed is an ExtendDecl if (auto extendDecl = DynamicCast(&decl)) { if (auto extendedTypeRawMangleName = GetExtendedTypeRawMangleName(*extendDecl)) { // Serveral cases here: // 1) the extended type is a user-defined type which have proper raw mangle name, then // we can find the type decl in the `mangled2Decl` map and pollute it // 2) the extended type is a primitive type (Int64, Boolean, etc) or built-in type (CPointer, CString), // which don't have a type decl. In this case, we manually find and pollute all other ExtendDecls // that extends this type if (auto it = mangled2Decl.find(*extendedTypeRawMangleName); it != mangled2Decl.cend()) { if (auto inheritableDecl = DynamicCast(it->second)) { PollutionForBodyChangedDecl(*inheritableDecl); } } else { for (auto extend : t.GetAllExtendsOfType(*extendedTypeRawMangleName)) { if (auto itt = mangled2Decl.find(extend); itt != mangled2Decl.cend()) { auto extendOfBuiltinDecl = StaticCast(itt->second); AddToPollutedDecls(*extendOfBuiltinDecl); } } } } // Specially, for direct extends, since we merge them into one when calculating the raw mangle name and // hash, so we have to manually pollute all other same direct extends AdditionPollutionForBodyChangedExtendDecl(*extendDecl); } if (decl.astKind == ASTKind::CLASS_DECL) { // default implementation of interface functions may copy to the type when public visiable functions // change, add this type as well as child types to recompile to trigger this copy behaviour. // Also propagate to all interface extends of this type, as the interface implementation can be defined // in the class body of any of its interface extends. Note also that keyword `override` is optional, so // public/protected member func changes in open class may have an impact on the vtable of its subclasses. PolluteDownStreamTypes(decl); } // All public APIs need a wrapper in Box, thus any change here should trigger re-Box PolluteBoxUsesFromDecl(decl); AddToPollutedDecls(decl); PollutedInstantiationChangeFromDecl(decl); } else { if (changes[&decl].body) { return; } changes[&decl].body = true; AddToPollutedDecls(decl); // when there is change of override/redef on PropDecl, // the PropDecl should be recompiled at sema (check whether the override/redef is legal) // in addition, AST2CHIR::SetParentInfo will traverse member of PropDecl,so here also add // member of PropDecl if (decl.astKind == ASTKind::PROP_DECL) { for (auto member : GetMembers(decl)) { AddToPollutedDecls(*member); } } // For decl with explicit type, there is no pollution. Yet if the decl has no explicit type, // we should assume the body change will affect the type thus change the signature if (IsUntyped(decl)) { PollutionForSigChangedDecl(decl); } PollutedInstantiationChangeFromDecl(decl); if (decl.IsConst()) { PollutionForSrcUseChangedDecl(decl); } PolluteCHIROptAffectDecl(decl); // propagate to constructors for member variables if (decl.outerDecl && !decl.TestAttr(Attribute::STATIC)) { if (auto enclosingType = DynamicCast(decl.outerDecl)) { PolluteToConstructors(*enclosingType); } } } } void PollutionAnalyzer::PollutionForSigChangedDecl(const Decl& decl) { switch (decl.astKind) { case ASTKind::FUNC_DECL: case ASTKind::PRIMARY_CTOR_DECL: PollutionForSigChangedFuncDecl(decl); break; case ASTKind::MAIN_DECL: case ASTKind::MACRO_DECL: CJC_ABORT(); break; case ASTKind::VAR_DECL: case ASTKind::FUNC_PARAM: CJC_ASSERT(decl.astKind == ASTKind::VAR_DECL || (decl.astKind == ASTKind::FUNC_PARAM && StaticCast(decl).isMemberParam)); PollutionForSigChangedVarDecl(static_cast(decl)); break; case ASTKind::VAR_WITH_PATTERN_DECL: PollutionForSigChangedVarWithPatternDecl(static_cast(decl)); break; case ASTKind::PROP_DECL: PollutionForSigChangedPropDecl(static_cast(decl)); break; default: PollutionForSigChangedInheritableDecl(static_cast(decl)); break; } } void PollutionAnalyzer::PollutionForSigChangedFuncDecl(const Decl& decl) { if (changes[&decl].sig) { return; } changes[&decl].sig = true; PollutedUnqualifiedUses(decl); PollutedGlobalChangeToPackageQualifiedUses(decl); PolluteGlobalChangeToQualifiedUses(decl); PollutedInstantiationChangeFromDecl(decl); if (decl.outerDecl && decl.outerDecl->astKind == ASTKind::EXTEND_DECL) { AdditionPollutionAPIOfExtendDecl(StaticCast(*decl.outerDecl)); } } void PollutionAnalyzer::PollutionForSigChangedVarDecl(const VarDecl& decl) { if (changes[&decl].sig) { return; } changes[&decl].sig = true; if (!decl.outerDecl) { PollutedUnqualifiedUses(decl); PollutedToExprUsages( p.GetPackageQualifiedUses(ChangePollutedMap::Idx::BODY, decl.identifier, decl.fullPackageName)); } else { // If this is a member VarDecl of struct or class, then it will NOT overload with // other same-identifier global variables, we just need to pollute the precise place PollutePreciseUsages(decl); } } void PollutionAnalyzer::PollutionForSigChangedVarWithPatternDecl(const VarWithPatternDecl& decl) { if (changes[&decl].sig) { return; } changes[&decl].sig = true; auto allPatterns = FlattenVarWithPatternDecl(decl); for (auto pattern : allPatterns) { if (pattern->astKind != ASTKind::VAR_PATTERN) { continue; } auto varPattern = StaticCast(pattern); PollutionForSigChangedDecl(*varPattern->varDecl); } } void PollutionAnalyzer::PollutionForSigChangedPropDecl(const PropDecl& decl) { if (changes[&decl].sig) { return; } changes[&decl].sig = true; for (auto& getter : decl.getters) { AddToPollutedDecls(*getter); PollutionForSigChangedDecl(*getter); } for (auto& setter : decl.setters) { AddToPollutedDecls(*setter); PollutionForSigChangedDecl(*setter); } } void PollutionAnalyzer::PollutionForSigChangedInheritableDecl(const InheritableDecl& decl) { if (typeChanges[&decl].sig) { return; } typeChanges[&decl].sig = true; PollutePreciseUsages(decl); // we might want to have a separate hash value for annotations // The sig change includes the annotations change, we need to propagate to // all inner functions // not only the annotation impact the inner functions, the inheritance // change should also impact all the init functions cause they implict call super, // and it will also change the layout!!! for (const auto& member : decl.GetMemberDeclPtrs()) { // Filter out the construtor in EnumDecl if (decl.astKind == ASTKind::ENUM_DECL) { if (member->astKind == ASTKind::FUNC_DECL && member->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { continue; } } // For imported decl, we might have compiler-added member which has no raw mangle name if (member->rawMangleName.empty()) { continue; } if (member->astKind == ASTKind::FUNC_DECL || member->astKind == ASTKind::PROP_DECL || member->astKind == ASTKind::PRIMARY_CTOR_DECL) { PolluteAPIOfDecl(*member); } } for (auto extend : FindAllExtendDeclsOfType(decl.rawMangleName)) { PolluteAPIOfDecl(*extend); } PolluteBoxUsesFromDecl(decl); } void PollutionAnalyzer::PollutionForSrcUseChangedDecl(const Decl& decl) { if (IsEnumConstructor(decl)) { // enum constructor does not have src-use change return; } if (auto type = DynamicCast(&decl)) { if (typeChanges[type].srcUse) { return; } typeChanges[type].srcUse = true; } else { if (changes[&decl].srcUse) { return; } changes[&decl].srcUse = true; // special rule: change of property shall propagate to its getters and setters // only property may have members in this branch auto allMembers = GetAllMembers(decl); for (auto member : std::as_const(allMembers)) { AddToPollutedDecls(*member); PollutionForSrcUseChangedDecl(*member); } } PollutePreciseUsages(decl); PolluteBoxUsesFromDecl(decl); } void PollutionAnalyzer::PollutionForChangedTypeDecl(const InheritableDecl& decl, const TypeChange& c) { CJC_ASSERT(decl.astKind == ASTKind::ENUM_DECL || decl.astKind == ASTKind::STRUCT_DECL || decl.astKind == ASTKind::CLASS_DECL || decl.astKind == ASTKind::INTERFACE_DECL || decl.astKind == ASTKind::EXTEND_DECL); // For imported non-public type decl, we just add it into the recompilation list so the // backend will update its metadata info if (decl.TestAttr(Attribute::IMPORTED) && decl.TestAttr(Attribute::PRIVATE)) { AddToPollutedDecls(decl); return; } if (c.instVar) { AddToPollutedDecls(decl); PollutionForLayoutChangedDecl(decl); } if (c.virtFun) { AddToPollutedDecls(decl); PollutionForVTableChangedDecl(decl); } if (c.sig) { AddToPollutedDecls(decl); PollutionForSigChangedDecl(decl); } if (c.srcUse) { AddToPollutedDecls(decl); PollutionForSrcUseChangedDecl(decl); } if (c.body) { AddToPollutedDecls(decl); PollutionForBodyChangedDecl(decl); } if (!c.added.empty() || !c.del.empty() || !c.changed.empty()) { // Specially, if we are changing inside a generic decl, we need to // pollute the generic decl due to the need of re-instantiation PollutedInstantiationChangeFromDecl(decl); } for (auto member : c.added) { PollutionForAddedDecl(*member); } for (auto& member : c.del) { PollutionForDeletedDecl(member); } for (auto& member : c.changed) { PollutionForChangedNonTypeDecl(member); } } void PollutionAnalyzer::PolluteBoxUsesFromDecl(const Decl& decl) { if (auto extend = DynamicCast(&decl)) { if (auto extendedTypeRawMangleName = GetExtendedTypeRawMangleName(*extend)) { PollutedToBoxUses(*extendedTypeRawMangleName); } } else if (auto type = DynamicCast(&decl)) { PollutedToBoxUses(type->rawMangleName); } } void PollutionAnalyzer::PolluteToConstructors(const AST::Decl& decl) { const AST::PrimaryCtorDecl* pc{nullptr}; bool hasCtor = false; for (auto member : decl.GetMemberDeclPtrs()) { if (member->rawMangleName.empty()) { continue; } if (IsClassOrEnumConstructor(*member)) { hasCtor = true; PolluteAPIOfDecl(*member); } if (member->astKind == ASTKind::PRIMARY_CTOR_DECL) { hasCtor = true; pc = StaticCast(member); PolluteAPIOfDecl(*member); } } // If no explicit ctor is provided in source code, we will have to manually pollute // the call site of the implicitly generated ctor if (!hasCtor) { PollutePreciseUsages(decl.rawMangleName + ""); } // Remember to count in the member variable defined in the primal constructor if (pc) { for (auto& pl : pc->funcBody->paramLists) { for (auto& param : pl->params) { if (param->isMemberParam) { PolluteAPIOfDecl(*param); } } } } } void PollutionAnalyzer::PollutionForLayoutChangedDecl(const InheritableDecl& decl) { if (typeChanges[&decl].instVar) { return; } typeChanges[&decl].instVar = true; switch (decl.astKind) { case ASTKind::ENUM_DECL: for (auto& member : static_cast(decl).constructors) { // populate to all usages of enum constructor identifier since the enum index is changed PolluteAPIOfDecl(*member); } // the enum constructors change need to trigger more pollution because some enum will be generated // in special type in CodeGen (the ref-enum for recursive case), but there is opportunity to optimize PolluteAPIOfDecl(decl); break; case ASTKind::STRUCT_DECL: // also struct decls PolluteAPIOfDecl(decl); [[fallthrough]]; case ASTKind::CLASS_DECL: { // populate to instance variables only. // need not populate to subtype or instance variables of this type of other declarations, // because the target of incremental compilation is LLVM IR. In generated LLVM IR, memory // alignment of nested super class or instance member is represented as one single block, so the // change inside the single injected block does not affect the outer memory alignment. for (auto member : decl.GetMemberDeclPtrs()) { if (member->astKind == ASTKind::VAR_DECL && !member->TestAttr(AST::Attribute::STATIC)) { PolluteAPIOfDecl(*member); } } PolluteToConstructors(decl); break; } case ASTKind::EXTEND_DECL: case ASTKind::INTERFACE_DECL: CJC_ABORT(); break; default: break; } } void PollutionAnalyzer::PollutionForVTableChangedDecl(const InheritableDecl& decl) { if (typeChanges[&decl].virt) { return; } typeChanges[&decl].virt = true; // 1) Pollute the direct usage of the type decl so its children types can also update VTable PolluteDownStreamTypes(decl); PolluteBoxUsesFromDecl(decl); // 2) Pollute the virtual funcs, so all their usage will be recalculated for (auto member : GetMembers(decl)) { if (IsVirtual(*member)) { PolluteAPIOfDecl(*member); } } } void PollutionAnalyzer::PolluteBodyOfDecl(const Decl& decl) { if (visitedBodyPollutedDecls.find(&decl) != visitedBodyPollutedDecls.end()) { return; } visitedBodyPollutedDecls.emplace(&decl); AddToPollutedDecls(decl); if (IsUntyped(decl)) { PolluteAPIOfDecl(decl); } PollutedInstantiationChangeFromDecl(decl); PolluteCHIROptAffectDecl(decl); } std::set> PollutionAnalyzer::FindAllExtendDeclsOfType(const RawMangledName& name) { std::set> ret{}; for (auto mangle : t.GetAllExtendsOfType(name)) { if (auto it = mangled2Decl.find(mangle); it != mangled2Decl.cend()) { (void)ret.insert(it->second); } } return ret; } // CPointer and CString are neither builtin types nor keyword types (user can define types with the same // name), we make special treatments for these two types static std::optional LookupSpecialBuiltinType(const RefType& type) { // pair of typename and type parameter number // use vector instead of map because the number is quite small // change this to map when the number grow over 8 static const std::vector> BUILTIN_NON_PRIMITIVE_TYPES{ {CPOINTER_NAME, 1}, {CSTRING_NAME, 0}, {CFUNC_NAME, 1}}; for (auto& pair : BUILTIN_NON_PRIMITIVE_TYPES) { if (pair.first == type.ref.identifier.Val() && pair.second == type.typeArguments.size()) { return std::string{pair.first}; } } return {}; } std::optional PollutionAnalyzer::GetExtendedTypeRawMangleNameImpl(const Type& extendedType) { // If this `type` is from imported AST, then we can get what we want directly from sema ty info auto ty = extendedType.ty; if (Ty::IsTyCorrect(ty)) { return Sema::GetTypeRawMangleName(*ty); } const std::string* typeId{nullptr}; switch (extendedType.astKind) { case ASTKind::PRIMITIVE_TYPE: { auto primitiveType = StaticCast(&extendedType); return ASTMangler::ManglePrimitiveType(*primitiveType); } case ASTKind::REF_TYPE: { auto refType = StaticCast(&extendedType); // use special lookup rule for builtin non-primitive types if (auto specialName = LookupSpecialBuiltinType(*refType)) { return *specialName; } typeId = &refType->ref.identifier.Val(); break; } case ASTKind::QUALIFIED_TYPE: { auto qualifiedType = StaticCast(&extendedType); typeId = &qualifiedType->field.Val(); break; } // Following type can't be extended case ASTKind::OPTION_TYPE: case ASTKind::CONSTANT_TYPE: case ASTKind::VARRAY_TYPE: case ASTKind::FUNC_TYPE: case ASTKind::TUPLE_TYPE: case ASTKind::PAREN_TYPE: case ASTKind::THIS_TYPE: case ASTKind::INVALID_TYPE: { break; } // Other types can't be extended default: { CJC_ABORT(); break; } } if (typeId) { for (auto pair : mangled2Decl) { if (pair.second->identifier == *typeId) { if (pair.second->IsNominalDecl()) { return pair.first; } else if (auto typeAlias = DynamicCast(pair.second)) { // For type alias, we need to get its real type and then continue the search return GetExtendedTypeRawMangleNameImpl(*typeAlias->type); } } } } return {}; } std::optional PollutionAnalyzer::GetExtendedTypeRawMangleName(const ExtendDecl& extend) { return GetExtendedTypeRawMangleNameImpl(*extend.extendedType); } void PollutionAnalyzer::PolluteAPIOfDecl(const Decl& decl) { if (visitedAPIPollutedDecls.find(&decl) != visitedAPIPollutedDecls.end()) { return; } visitedAPIPollutedDecls.emplace(&decl); AddToPollutedDecls(decl); PolluteBoxUsesFromDecl(decl); PollutedInstantiationChangeFromDecl(decl); if (decl.outerDecl && decl.outerDecl->astKind == ASTKind::EXTEND_DECL) { AdditionPollutionAPIOfExtendDecl(StaticCast(*decl.outerDecl)); } switch (decl.astKind) { case ASTKind::PROP_DECL: { auto propDecl = StaticCast(&decl); for (auto& getter : propDecl->getters) { PolluteAPIOfDecl(*getter); } for (auto& setter : propDecl->setters) { PolluteAPIOfDecl(*setter); } break; } case ASTKind::VAR_DECL: { // If an instance member variable is polluted in API, then the parent struct/class will be // impacted in layout. Mostly this is already handled when we detect the layout hash changes. // However, there is a special case when the variable is with enum type, where the layout of enum // will be changed according to the constructors inside. Sometimes it is a single integer, sometimes it is a // tuple or even a class object. Thus the layout of parent struct/class will be changed in codegen without // layout hash changes. So here we make sure the parent decl will be polluted. if (!decl.TestAttr(Attribute::STATIC)) { if (auto parentDecl = DynamicCast(decl.outerDecl)) { AddToPollutedDecls(*parentDecl); PollutionForLayoutChangedDecl(*parentDecl); } } [[fallthrough]]; } case ASTKind::FUNC_PARAM: case ASTKind::VAR_WITH_PATTERN_DECL: case ASTKind::FUNC_DECL: case ASTKind::PRIMARY_CTOR_DECL: case ASTKind::ENUM_DECL: case ASTKind::STRUCT_DECL: case ASTKind::CLASS_DECL: case ASTKind::INTERFACE_DECL: { PollutePreciseUsages(decl); PollutedUnqualifiedUses(decl); PollutedGlobalChangeToPackageQualifiedUses(decl); PolluteGlobalChangeToQualifiedUses(decl); for (auto ext : FindAllExtendDeclsOfType(decl.rawMangleName)) { AddToPollutedDecls(*ext); PollutedInstantiationChangeFromDecl(*ext); } break; } case ASTKind::EXTEND_DECL: { AdditionPollutionAPIOfExtendDecl(StaticCast(decl)); break; } case ASTKind::MAIN_DECL: break; default: CJC_ABORT(); } } void PollutionAnalyzer::AdditionPollutionAPIOfDirectExtendDecls(const RawMangledName& mangle) { if (auto extendsIt = std::as_const(directExtends).find(mangle); extendsIt != directExtends.cend()) { for (auto extendWithSameMangleName : extendsIt->second) { PolluteAPIOfDecl(*extendWithSameMangleName); } } } std::unordered_set> PollutionAnalyzer::GetPollutionResult() const { std::unordered_set> ret; for (auto it : pollutedDecls) { (void)ret.insert(Ptr(const_cast(it.get()))); } return ret; } std::list PollutionAnalyzer::GetDeletedResult() const { return deletedDecls; } bool PollutionAnalyzer::NeedPollutedInstantiationChange(const Decl& decl) const { return IsInDeclWithAttribute(decl, Attribute::GENERIC); } void PollutionAnalyzer::PollutedInstantiationChangeFromDecl(const Decl& decl) { if (otherChanges[decl.rawMangleName].instantiation) { return; } otherChanges[decl.rawMangleName].instantiation = true; if (decl.outerDecl) { PollutedInstantiationChangeFromDecl(*decl.outerDecl); } if (NeedPollutedInstantiationChange(decl)) { AddToPollutedDecls(decl); const auto& allMembers = GetAllMembers(decl); for (const auto member : std::as_const(allMembers)) { AddToPollutedDecls(*member); } PollutePreciseUsages(decl); } } void PollutionAnalyzer::PolluteCHIROptAffectDecl(const Decl& decl) { // Specially, for VarWithPattern decl, we need to propagate // the pollution based on the contained VarDecl if (const auto varWithPatternDecl = DynamicCast(&decl)) { auto allPatterns = FlattenVarWithPatternDecl(*varWithPatternDecl); for (auto pattern : allPatterns) { if (pattern->astKind != ASTKind::VAR_PATTERN) { continue; } auto varPattern = StaticCast(pattern); PolluteCHIROptAffectDecl(*varPattern->varDecl); } } auto chirOptIt = chirOptMap.find(decl.rawMangleName); if (chirOptIt == chirOptMap.cend()) { return; } if (otherChanges[decl.rawMangleName].chirOpt) { return; } otherChanges[decl.rawMangleName].chirOpt = true; for (auto user : chirOptIt->second) { // it is a trick where box-generated functions inline a func/var, the ExtendDecl against which the box is // generated is marked CHIR opt target. In this case, trigger the propagation rules of box on the extend. if (auto extend = DynamicCast(user)) { PolluteAPIOfDecl(*extend); PolluteCHIROptAffectDecl(*extend); } else { PolluteBodyOfDecl(*user); } } } // propagate to box sites and interface extend of the boxed type to correctly trigger rebox. Also propagate to that // of subclasses void PollutionAnalyzer::PollutedToBoxUses(const RawMangledName& mangle) { if (otherChanges[mangle].box) { return; } otherChanges[mangle].box = true; reBoxedTypes.emplace_back(mangle); if (auto usesIt = p.boxUses.find(mangle); usesIt != p.boxUses.cend()) { for (auto b : std::as_const(usesIt->second)) { PollutionForBodyChangedDecl(*b); } } // The box usages of the downstream type are also impacted if (auto decl = mangled2Decl.find(mangle); decl != mangled2Decl.cend()) { if (auto children = t.children.find(decl->second); children != t.children.cend()) { for (auto downstreamType : children->second) { AddToPollutedDecls(*downstreamType); PollutedToBoxUses(downstreamType->rawMangleName); } } } // Direct extends may share the same mangle name, but they are never propagated to by box. Only interface extends // are. Add all interface extends of this type to recompilation to trigger recheck of interface copy behaviour as // interfaces may be implemented by extend for (auto ext : FindAllExtendDeclsOfType(mangle)) { auto extend = StaticCast(ext); if (!extend->inheritedTypes.empty()) { AddToPollutedDecls(*extend); PolluteCHIROptAffectDecl(*extend); } } } void PollutionAnalyzer::PollutedToExprUsages(const std::set>& usages) { for (auto usageDecl : usages) { PolluteBodyOfDecl(*usageDecl); } } void PollutionAnalyzer::PollutePreciseUsages(const Decl& decl) { if (decl.rawMangleName.empty()) { return; } PollutePreciseUsages(decl.rawMangleName); } void PollutionAnalyzer::PollutePreciseUsages(const RawMangledName& mangled) { for (auto u : p.directUses[ChangePollutedMap::Idx::BODY][mangled]) { PolluteBodyOfDecl(*u); } for (auto u : p.directUses[ChangePollutedMap::Idx::API][mangled]) { PolluteAPIOfDecl(*u); } } void PollutionAnalyzer::PolluteDownStreamTypes(const Decl& decl) { CJC_ASSERT(decl.astKind == ASTKind::STRUCT_DECL || decl.astKind == ASTKind::CLASS_DECL || decl.astKind == ASTKind::INTERFACE_DECL || decl.astKind == ASTKind::ENUM_DECL || decl.astKind == ASTKind::EXTEND_DECL); if (auto extendDecl = DynamicCast(&decl)) { auto extendedType = extendDecl->extendedType.get(); std::string extendedTypeIdentifier; if (auto rt = DynamicCast(extendedType)) { extendedTypeIdentifier = rt->ref.identifier; } else if (auto qt = DynamicCast(extendedType)) { extendedTypeIdentifier = qt->field; } Ptr extendedTypeDecl = nullptr; for (auto element : mangled2Decl) { if (element.second->identifier == extendedTypeIdentifier) { extendedTypeDecl = element.second; break; } } if (extendedTypeDecl) { PolluteDownStreamTypes(*extendedTypeDecl); } else { // so this should be a extend of a imported type decl, // how do we check this and what we should do for this? } } else { if (auto it = t.children.find(&decl); it != t.children.cend()) { for (auto downstreamTy : it->second) { PolluteAPIOfDecl(*downstreamTy); } } if (auto it = t.interfaceExtendTypes.find(decl.rawMangleName); it != t.interfaceExtendTypes.cend()) { for (auto f : it->second) { if (auto downstreamTy = mangled2Decl.find(f); downstreamTy != mangled2Decl.cend()) { PolluteAPIOfDecl(*downstreamTy->second); } else { // For primitive type and built-in type, they don't have decl thus we have to manually // pollute all their other extends for (auto extend : t.GetAllExtendsOfType(f)) { if (auto itt = std::as_const(mangled2Decl).find(extend); itt != mangled2Decl.cend()) { AddToPollutedDecls(*itt->second); PollutedInstantiationChangeFromDecl(*itt->second); } } } } } } } // populate to changes of a global name to unqualified usages void PollutionAnalyzer::PollutedUnqualifiedUses(const Decl& decl) { auto identifiers = p.GetAccessibleDeclName(decl); // Get real accessible decl name after alias. for (auto& identifier : identifiers) { auto& bodyUses = IsImported(decl) ? p.unqUsesOfImported[ChangePollutedMap::Idx::BODY] : p.unqUses[ChangePollutedMap::Idx::BODY]; auto it = bodyUses.find(identifier); if (it == bodyUses.cend()) { continue; } for (auto& mp : std::as_const(it->second)) { for (auto g : mp.second) { PolluteBodyOfDecl(*g); } } } if (!decl.IsNominalDecl()) { return; } for (auto& identifier : identifiers) { // Only type decl needs checking 'API' usage. auto& apiUses = IsImported(decl) ? p.unqUsesOfImported[ChangePollutedMap::Idx::API] : p.unqUses[ChangePollutedMap::Idx::API]; auto it = apiUses.find(identifier); if (it == apiUses.cend()) { continue; } for (auto& mp : std::as_const(it->second)) { for (auto g : mp.second) { PolluteAPIOfDecl(*g); } } } } void PollutionAnalyzer::PolluteGlobalChangeToQualifiedUses(const Decl& decl) { if (!decl.outerDecl) { return; } for (auto user : p.GetQUses(ChangePollutedMap::Idx::BODY, decl.identifier)) { PolluteBodyOfDecl(*user); } } // Populate to changes of a global name to qualified usages. void PollutionAnalyzer::PollutedGlobalChangeToPackageQualifiedUses(const Decl& decl) { // NOTE: decl alias and it's package's alias cannot exist at same time. auto fullPackageNames = p.GetAccessiblePackageName(decl.fullPackageName); for (auto& fullPackageName : fullPackageNames) { for (auto& user : p.pqUses[ChangePollutedMap::Idx::BODY][decl.identifier][fullPackageName]) { PolluteBodyOfDecl(*user); } } if (!decl.IsNominalDecl()) { return; } for (auto& fullPackageName : fullPackageNames) { for (auto& user : p.pqUses[ChangePollutedMap::Idx::API][decl.identifier][fullPackageName]) { PolluteAPIOfDecl(*user); } } } // Returns true if we can tell incremental compilation must rollback to full bool PollutionAnalyzer::FallBack() const { return !typeAliases.empty() || !unfoundExtends.empty() || !unfoundNames.empty() || !removedNotSupported.empty(); } void PollutionAnalyzer::PrintFallbackInfo() { if (!IncrementalCompilationLogger::GetInstance().IsEnable()) { return; } std::stringstream out{}; for (int i{0}; i < DELIMITER_NUM; ++i) { out << '='; } out << "\nFallback info:\n"; if (!typeAliases.empty()) { for (auto decl : std::as_const(typeAliases)) { out << "changed typealias: "; PrintDecl(out, *decl); out << '\n'; } } if (!unfoundExtends.empty()) { unfoundExtends.unique(); for (auto decl : std::as_const(unfoundExtends)) { out << "unfound extend: "; PrintDecl(out, *decl); out << '\n'; } } if (!unfoundNames.empty()) { unfoundNames.unique(); for (auto& name : std::as_const(unfoundNames)) { out << "unfound name: " << name << '\n'; } } for (auto& r : std::as_const(removedNotSupported)) { out << "removed type " << r << '\n'; } for (int i{0}; i < DELIMITER_NUM; ++i) { out << '='; } IncrementalCompilationLogger::GetInstance().LogLn(out.str()); } cangjie_compiler-1.0.7/src/IncrementalCompilation/PollutionAnalyzer.h000066400000000000000000000447701510705540100261160ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CACHE_DATA_POLLUTION_ANALYZER_IMPL_H #define CANGJIE_CACHE_DATA_POLLUTION_ANALYZER_IMPL_H #include "cangjie/IncrementalCompilation/IncrementalScopeAnalysis.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/IncrementalCompilation/ASTCacheCalculator.h" #include "CompilationCacheSerialization.h" #include "cangjie/IncrementalCompilation/Utils.h" #include "cangjie/Parse/ASTHasher.h" #include "ASTDiff.h" namespace Cangjie::IncrementalCompilation { // A map that records additional relation info among types struct TypeMap { using TypeRel = std::unordered_map, std::set>>; // A map that record interface extends. key is interface, value is types that extends it std::unordered_map> interfaceExtendTypes; // likewise, an instance of this map (a, {b...}) means decl a has direct child types {b...} TypeRel children; void AddParent(const AST::Decl& parent, const AST::Decl& child) { (void)children[&parent].insert(&child); } // truncate raw mangled name of extend to only the type it extends static inline std::string TruncateExtendName(const RawMangledName& mangled) { if (auto pos = mangled.find("<:"); pos != std::string::npos) { return mangled.substr(0, pos); } CJC_ABORT(); return std::string{}; } const RawMangledName* FindExtendedTypeByExtendDeclMangleName(const RawMangledName& mangle) const { auto it = extend2Decl.find(mangle); if (it != extend2Decl.cend()) { return &it->second; } return {}; } const std::list& GetAllExtendsOfType(const RawMangledName& decl) const { if (auto it = extends.find(decl); it != extends.end()) { return it->second; } return dummyExtends; } void AddExtend(const RawMangledName& extendedType, const RawMangledName& extend) { extends[extendedType].push_back(extend); extend2Decl[extend] = extendedType; } void CollectImportedDeclExtraRelation(const AST::Decl& decl); // Merge another TypeMap into this. This function is now only used to merge a cached TypeMap instance and a // TypeMap generated from reading AST's of imported packages. void Merge(TypeMap&& other) { extend2Decl.merge(other.extend2Decl); for (std::move_iterator childrenIt{other.children.begin()}; childrenIt != std::move_iterator{other.children.end()}; ++childrenIt) { if (auto it = children.find(childrenIt->first); it != children.cend()) { it->second.merge(childrenIt->second); } else { children[childrenIt->first] = std::move(childrenIt->second); } } extends.merge(other.extends); for (std::move_iterator extendsIt{other.extends.begin()}; extendsIt != std::move_iterator{other.extends.end()}; ++extendsIt) { if (auto it = extends.find(extendsIt->first); it != extends.cend()) { it->second.splice(it->second.end(), extendsIt->second); } else { extends[extendsIt->first] = std::move(extendsIt->second); } } CJC_ASSERT(other.interfaceExtendTypes.empty()); } private: // map from RawMangledName of extend decl to the decl of the type it extends. used when looking for the extended // type when an ExtendDecl is deleted std::unordered_map extend2Decl; static const inline std::list dummyExtends{}; // A map to record the ExtendDecl infos, where the key here will be the raw mangle name of // a type `A`, and the value will be the raw mangle name of all the ExtendDecl which extend `A` std::unordered_map> extends; }; // A qualified usage is identified by the name being used and the type to the left of '.' operator struct QualifiedUsage { RawMangledName leftDecl; std::string name; bool operator==(const QualifiedUsage& other) const { return name == other.name && leftDecl == other.leftDecl; } }; struct Hasher { size_t operator()(const QualifiedUsage& usage) const { return std::hash{}(usage.name) ^ std::hash{}(usage.leftDecl); } }; // the first key is the identifier of the unqualified name being used, and // the second key is the scope of the user. // scope of an unqualified usage is the outerDecl of the decl that uses the unqualified name // specially, the scope of a top level decl is its package using UnqualifiedName2Usages = std::unordered_map, std::set>>>; using QualifiedName2Usages = std::unordered_map>, Hasher>; // the first key is the type identifier decl being used, and // the second key is the package identifier left to the type name, and is empty when it is an unqualified type usage using TypeUsages = std::unordered_map>>>; using PackageQualifedUsages = TypeUsages; // A map sufficient to populate the impact of any AST change struct ChangePollutedMap { static const inline std::set> emptyUses{}; enum class Idx : size_t { BODY, API }; template struct Pack { T body; T api; const T& operator [](Idx i) const { return i == Idx::BODY ? body : api; } T& operator[](Idx i) { return i == Idx::BODY ? body : api; } }; // unqualified usages that ever resolve to imported decl must be recompiled when the name of either the source // package or imported packages change // A member decl affects only unqualified usages of the same name within its subtype trees; // while a global decl affects all unqualifed usages. Pack unqUses; // unqualified usages that resolve to imported decl can only be dirtified by the change of the same name // from imported packages Pack unqUsesOfImported; // qualified usages. When index == API, it contains usages in API; when index == BODY, it contains usages in body. Pack qUses; // package qualified usages Pack pqUses; // direct usages Pack>>> directUses; // a pair (A, [B...]) indicates type A is boxed in decls B... std::unordered_map>> boxUses; // Map of fullPackageName -> aliased package name. NOTE: this will only be used as pkg qualified usage. std::unordered_map> packageAliasMap; // Map of (fullPackageName, decl identifier) -> aliased name. NOTE: this will only be used as unqualified usage. std::unordered_map, std::set, HashPair> declAliasMap; const std::set>& GetUnqUses( bool getImportOnly, ChangePollutedMap::Idx index, const std::string& identifier, const AST::Decl& scope) const { auto& cont = getImportOnly ? unqUsesOfImported[index] : unqUses[index]; auto pit = cont.find(identifier); if (pit == cont.end()) { return emptyUses; } auto sit = pit->second.find(&scope); if (sit == pit->second.end()) { return emptyUses; } return sit->second; } std::set> GetQUses(ChangePollutedMap::Idx index, const std::string& identifier) const { auto& cont = qUses[index]; std::set> usages; for (auto& element : cont) { if (element.first.name == identifier) { usages.insert(element.second.begin(), element.second.end()); } } return usages; } const std::set>& GetPackageQualifiedUses( ChangePollutedMap::Idx index, const std::string& identifier, const std::string& packageName) const { auto& cont = pqUses[index]; auto pit = cont.find(identifier); if (pit == cont.end()) { return emptyUses; } auto sit = pit->second.find(packageName); if (sit == pit->second.end()) { return emptyUses; } return sit->second; } inline std::set GetAccessibleDeclName(const AST::Decl& decl) const { auto pair = std::make_pair(decl.fullPackageName, decl.identifier.Val()); auto found = declAliasMap.find(pair); return found == declAliasMap.end() ? std::set{decl.identifier} : found->second; } inline std::set GetAccessiblePackageName(const std::string& fullPackageName) const { auto found = packageAliasMap.find(fullPackageName); return found == packageAliasMap.end() ? std::set{fullPackageName} : found->second; } }; struct PollutionResult { IncreKind kind; std::unordered_set> declsToRecompile; std::list deleted; std::list reBoxedTypes; }; struct PollutionAnalyseArgs { ModifiedDecls&& rawModified; const AST::PackageDecl& pkg; const std::unordered_map, std::set>>& sourcePopulations; const SemanticInfo& semaInfo; const OptEffectStrMap& chirOptInfo; const CachedFileMap& fileMap; const ImportManager& man; const RawMangled2DeclMap& mangled2Decl; std::unordered_map, int>>>&& extends; TypeMap&& importedRelations; }; class PollutionAnalyzer { public: static PollutionResult Get(PollutionAnalyseArgs&& args); private: void AddToPollutedDecls(const AST::Decl& decl); // Propagate pollution for added decl, which can be either: // 1) a top-level VarDecl, VarWithPatternDecl, FuncDecl, type related decl // 2) a member VarDecl, FuncDecl, PropDecl. In this case, we assume the parent decl has been handled void PollutionForAddedDecl(const AST::Decl& decl); void PollutionForAddedTypeDecl(const AST::Decl& decl); void PollutionForAddedNonTypeDecl(const AST::Decl& decl); void PollutionForAddedNonTypeDeclImpl(const AST::Decl& decl); void AdditionPollutionForAddedExtendDecl(const AST::ExtendDecl& decl); void AdditionPollutionAPIOfExtendDecl(const AST::ExtendDecl& decl); void AdditionPollutionAPIOfExtendedDecl(const AST::ExtendDecl& decl); // Specially, for direct extends, since we merge them into one when calculating the raw mangle name and // hash, so we have to manually pollute all other same direct extends void AdditionPollutionForBodyChangedExtendDecl(const AST::ExtendDecl& decl); void AdditionPollutionAPIOfDirectExtendDecls(const RawMangledName& mangle); void PollutionForConstDecl(const AST::Decl& decl); void PollutionForOrderChangeDecl(const AST::Decl& decl); // Propagate pollution for deleted decl, which can be either: // 1) a top-level VarDecl, VarWithPatternDecl, FuncDecl, type related decl // 2) a member VarDecl, FuncDecl, PropDecl. In this case, we assume the parent decl has been handled void PollutionForDeletedDecl(const RawMangledName& mangle); // Propagate pollution for changed non-type decl, which can be either: // 1) a top-level VarDecl, VarWithPatternDecl, FuncDecl, type related decl // 2) a member VarDecl, FuncDecl, PropDecl. In this case, we assume the parent decl has been handled void PollutionForChangedNonTypeDecl(const CommonChange& c); void PollutionForBodyChangedDecl(const AST::Decl& decl); void PollutionForSigChangedDecl(const AST::Decl& decl); void PollutionForSigChangedFuncDecl(const AST::Decl& decl); // Following changes will leads to sig change of a VarDecl: // 1) body change of a non-explicitly-typed VarDecl void PollutionForSigChangedVarDecl(const AST::VarDecl& decl); // Following changes will leads to sig change of a VarWithPatternDecl: // 1) body change of a non-explicitly-typed VarWithPatternDecl void PollutionForSigChangedVarWithPatternDecl(const AST::VarWithPatternDecl& decl); void PollutionForSigChangedPropDecl(const AST::PropDecl& decl); void PollutionForSigChangedInheritableDecl(const AST::InheritableDecl& decl); void PollutionForSrcUseChangedDecl(const AST::Decl& decl); // Propagate pollution for changed type decl, which can be enum/struct/class/interface/extend decl void PollutionForChangedTypeDecl(const AST::InheritableDecl& decl, const TypeChange& c); void PolluteBoxUsesFromDecl(const AST::Decl& decl); void PollutionForLayoutChangedDecl(const AST::InheritableDecl& decl); void PollutionForVTableChangedDecl(const AST::InheritableDecl& decl); void PolluteBodyOfDecl(const AST::Decl& decl); std::set> FindAllExtendDeclsOfType(const RawMangledName& name); std::optional GetExtendedTypeRawMangleNameImpl(const AST::Type& extendedType); std::optional GetExtendedTypeRawMangleName(const AST::ExtendDecl& extend); void PolluteAPIOfDecl(const AST::Decl& decl); std::unordered_set> GetPollutionResult() const; std::list GetDeletedResult() const; std::list GetReBoxedTypes() const { return reBoxedTypes; } // Returns true when the body change of decl need populate, // that is, it is src-imported or in generic or is default implementation inside interface. // inlining and generic instantiations will be triggered by SEMA and CHIR if the users of the change decl // are populate to. bool NeedPollutedInstantiationChange(const AST::Decl& decl) const; void PollutedInstantiationChangeFromDecl(const AST::Decl& decl); void PolluteCHIROptAffectDecl(const AST::Decl& decl); void PollutedToBoxUses(const RawMangledName& mangle); // propagate to constructors of a type decl. This function is used when a type has instvar change or the initial // value of member variables changed. void PolluteToConstructors(const AST::Decl& decl); void PollutedToExprUsages(const std::set>& usages); void PollutePreciseUsages(const AST::Decl& decl); void PollutePreciseUsages(const RawMangledName& mangled); void PolluteDownStreamTypes(const AST::Decl& decl); void PolluteDownStreamTypes(const RawMangledName& mangled); // populate to changes of a global name to unqualified usages void PollutedUnqualifiedUses(const AST::Decl& decl); void PolluteGlobalChangeToQualifiedUses(const AST::Decl& decl); // Populate to changes of a global name to qualified usages. void PollutedGlobalChangeToPackageQualifiedUses(const AST::Decl& decl); // Returns true if we can tell incremental compilation must rollback to full bool FallBack() const; void PrintFallbackInfo(); PollutionAnalyzer(ChangePollutedMap&& p, TypeMap&& t, const RawMangled2DeclMap& mp, const OptEffectStrMap& chirOpt, std::unordered_map, int>>>&& ex) : p(p), t(t), mangled2Decl(mp) { for (auto pair : chirOpt) { std::vector> affected{}; for (auto use : pair.second) { if (auto it = mangled2Decl.find(use); it != mangled2Decl.cend()) { affected.push_back(it->second); } } if (!affected.empty()) { (void)chirOptMap.emplace(pair.first, std::move(affected)); } } for (auto extend : std::move(ex)) { std::list> l; for (auto e : std::as_const(extend.second)) { l.emplace_back(e.first); } directExtends.emplace(std::move(extend.first), std::move(l)); } } ChangePollutedMap p; TypeMap t; const RawMangled2DeclMap& mangled2Decl; std::unordered_map>> directExtends; // changed decls that cause rollback std::list> typeAliases; // type alias cannot be incrementally compiled and must fall back to // full compilation if any change to them exists std::list> unfoundExtends; // ExtendDecl's whose extended type is not found. std::list unfoundNames; // std::list> virtChanges{}; // changes of vtable shall rollback std::unordered_map>> chirOptMap{}; // cache changed types to prevent circular population and to boost performance struct PollutedCommonChangeRecord { bool sig{false}, srcUse{false}, body{false}; // returns true if any field is true in this record operator bool() const { return sig || srcUse || body; } }; std::unordered_map, PollutedCommonChangeRecord> changes{}; struct PollutedTypeChangeRecord { bool instVar{false}, virt{false}, sig{false}, visibleAPI{false}, srcUse{false}, body{false}, member{false}; operator bool() const { return instVar || virt || sig || visibleAPI || srcUse || member; } }; std::unordered_map, PollutedTypeChangeRecord> typeChanges{}; std::list removedNotSupported{}; struct PollutedOtherUseRecord { bool instantiation{false}, chirOpt{false}, box{false}; }; std::unordered_map otherChanges{}; // The set of polluted decls, note that we will NOT use this data structure to further // propagate the pollution. std::unordered_set> pollutedDecls; std::list deletedDecls; std::list reBoxedTypes; // To avoid repeat analysis std::unordered_set> visitedBodyPollutedDecls; std::unordered_set> visitedAPIPollutedDecls; }; } // namespace Cangjie::IncrementalCompilation #endif cangjie_compiler-1.0.7/src/IncrementalCompilation/PollutionMapGen.h000066400000000000000000000151351510705540100254710ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_CACHE_DATA_POLLUTION_MAP_GEN_H #define CANGJIE_CACHE_DATA_POLLUTION_MAP_GEN_H #include "PollutionAnalyzer.h" namespace Cangjie::IncrementalCompilation { // Get all the usages of a decl from a map that records all usages in a decl by mainly reversing it. // Also for RawMangledName's that cannot be found in cache, its user is directly stored in a recompilation list. class PollutionMapGen { public: static std::tuple Get(const AST::PackageDecl& p, const RawMangled2DeclMap& mangledName2AST, const std::unordered_map, std::set>>& sourceImportedInfo, const SemanticInfo& usage, const OptEffectStrMap& chirOpt, const ImportManager& man) { PollutionMapGen i{p, mangledName2AST, sourceImportedInfo, usage}; i.Collect(); i.CollectCHIROpt(chirOpt); i.CollectAlias(man, *p.srcPackage); return {std::move(i.resp), std::move(i.rest)}; } private: PollutionMapGen(const AST::PackageDecl& p, const RawMangled2DeclMap& mangledName2AST, const std::unordered_map, std::set>>& sourcePopulations, const SemanticInfo& graph) : mangled2Decl(mangledName2AST), sourcePopulations(sourcePopulations), graph(graph), pdecl(&p) { } const RawMangled2DeclMap& mangled2Decl; const std::unordered_map, std::set>>& sourcePopulations; const SemanticInfo& graph; // Will be used for dumping new cache, should not use move. Ptr pdecl; ChangePollutedMap resp{}; TypeMap rest{}; void Collect() { for (auto& p : graph.usages) { CollectPopulation(*p.first, p.second); } for (auto& p : sourcePopulations) { CollectSourceImportedPopulation(*p.first, p.second); } for (auto& p : graph.relations) { CollectRelation(p.first, p.second); } for (auto& p : graph.builtInTypeRelations) { CollectBuiltinRelation(p.first, p.second); } } void CollectCHIROpt(const OptEffectStrMap& chirOpt) { for (auto& r : chirOpt) { auto& l = resp.directUses[ChangePollutedMap::Idx::BODY][r.first]; for (auto& use : r.second) { if (auto it = mangled2Decl.find(use); it != mangled2Decl.cend()) { (void)l.insert(it->second); } } } } void CollectRelation(const RawMangledName& mangle, const SemaRelation& rel) { for (auto& ext : rel.extends) { rest.AddExtend(mangle, ext); } for (auto& interf : rel.extendedInterfaces) { rest.interfaceExtendTypes[interf].insert(mangle); } auto declIt = mangled2Decl.find(mangle); if (declIt == mangled2Decl.cend()) { return; } auto& decl = *declIt->second; for (auto& type : rel.inherits) { if (auto it = mangled2Decl.find(type); it != mangled2Decl.cend()) { rest.AddParent(*it->second, decl); } } } void CollectBuiltinRelation(const std::string& name, const SemaRelation& rel) { for (auto& ext : rel.extends) { rest.AddExtend(name, ext); } for (auto& interf : rel.extendedInterfaces) { (void)rest.interfaceExtendTypes[interf].insert(name); } } Ptr GetUnqualifiedUsageScope(const AST::Decl& decl) const { if (!decl.outerDecl) { return pdecl; } Ptr outer{decl.outerDecl}; while (outer->outerDecl) { outer = outer->outerDecl; } return outer; } void CollectUsedNameInfo( const AST::Decl& decl, const std::string& usedName, const NameUsage& use, ChangePollutedMap::Idx index) { for (auto& u : use.packageQualifiers) { (void)resp.pqUses[index][usedName][u].emplace(&decl); } for (auto& u : use.parentDecls) { (void)resp.qUses[index][{u, usedName}].emplace(&decl); } if (use.hasUnqualifiedUsageOfImported) { (void)resp.unqUsesOfImported[index][usedName][GetUnqualifiedUsageScope(decl)].emplace(&decl); } if (use.hasUnqualifiedUsage || use.hasUnqualifiedUsageOfImported) { (void)resp.unqUses[index][usedName][GetUnqualifiedUsageScope(decl)].emplace(&decl); } } void CollectUseInfo(const AST::Decl& decl, const UseInfo& info, ChangePollutedMap::Idx index) { // collect direct usages for (auto& u : info.usedDecls) { (void)resp.directUses[index][u].emplace(&decl); } for (auto& u : info.usedNames) { CollectUsedNameInfo(decl, u.first, u.second, index); } } void CollectPopulation(const AST::Decl& enclosingDecl, const SemaUsage& usage) { CollectUseInfo(enclosingDecl, usage.apiUsages, ChangePollutedMap::Idx::API); CollectUseInfo(enclosingDecl, usage.bodyUsages, ChangePollutedMap::Idx::BODY); for (auto& name : usage.boxedTypes) { resp.boxUses[name].emplace_back(&enclosingDecl); } } void CollectSourceImportedPopulation(const AST::Decl& src, const std::set>& dsts) { for (auto& dst : dsts) { resp.directUses[ChangePollutedMap::Idx::BODY][src.rawMangleName].emplace(dst); } } void CollectAlias(const ImportManager& importMgr, const AST::Package& pkg) { for (auto& file : std::as_const(pkg.files)) { for (auto& import : file->imports) { if (!import->IsImportAlias() && !import->IsImportSingle()) { continue; } auto& im = import->content; std::string prefix = im.GetPrefixPath(); auto mayBeName = im.GetImportedPackageName(); auto& name = import->IsImportAlias() ? im.aliasName : im.identifier; if (auto pd = importMgr.GetPackageDecl(mayBeName)) { resp.packageAliasMap[mayBeName].emplace(name.Val()); } else { resp.declAliasMap[std::make_pair(prefix, im.identifier)].emplace(im.aliasName.Val()); } } } } }; } #endifcangjie_compiler-1.0.7/src/IncrementalCompilation/Utils.cpp000066400000000000000000000103631510705540100240450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/IncrementalCompilation/Utils.h" namespace Cangjie::IncrementalCompilation { using namespace Cangjie::AST; void PrintDecl(std::ostream& out, const Decl& decl) { if (!decl.identifier.Empty()) { out << decl.identifier.Val() << ' '; } out << decl.rawMangleName << ' '; if (decl.curFile) { out << decl.curFile->fileName << ' '; } if (!decl.begin.IsZero()) { out << decl.begin.line; } } bool IsVirtual(const Decl& decl) { if (IsImported(decl)) { return decl.TestAttr(Attribute::OPEN) || decl.TestAttr(Attribute::ABSTRACT); } if (!decl.outerDecl) { return false; } if (decl.outerDecl->astKind != ASTKind::CLASS_DECL && decl.outerDecl->astKind != ASTKind::INTERFACE_DECL) { return false; } if (decl.outerDecl->astKind == ASTKind::INTERFACE_DECL && !decl.TestAttr(Attribute::STATIC)) { return true; } if (decl.TestAnyAttr(Attribute::CONSTRUCTOR, Attribute::ENUM_CONSTRUCTOR)) { return false; } if (decl.outerDecl->TestAttr(Attribute::ABSTRACT) && !decl.TestAttr(Attribute::STATIC)) { if (auto f = DynamicCast(&decl); f && !f->funcBody->body) { return true; } if (auto p = DynamicCast(&decl); p && p->getters.empty() && p->setters.empty()) { return true; } } return decl.TestAttr(Attribute::OPEN) || decl.TestAttr(Attribute::ABSTRACT); } bool IsTyped(const Decl& decl) { if (decl.TestAttr(Attribute::ENUM_CONSTRUCTOR)) { return true; } if (auto patternDecl = DynamicCast(&decl)) { return patternDecl->type || IsImported(*patternDecl); } if (auto varDecl = DynamicCast(&decl)) { if (varDecl->outerDecl) { if (auto pat = DynamicCast(varDecl->outerDecl)) { return IsTyped(*pat); } } return varDecl->type || IsImported(*varDecl); } if (decl.astKind == ASTKind::FUNC_DECL) { auto& funcDecl = static_cast(decl); return funcDecl.funcBody->retType || funcDecl.isGetter || funcDecl.isSetter || funcDecl.TestAttr(Attribute::CONSTRUCTOR) || funcDecl.IsFinalizer(); } return true; } std::vector> GetMembers(const Decl& decl) { if (auto p = DynamicCast(&decl)) { std::vector> res(p->members.size()); for (size_t i{0}; i < res.size(); ++i) { res[i] = p->members[i].get(); } return res; } if (auto p = DynamicCast(&decl)) { std::vector> res{}; for (auto& getter : p->getters) { res.push_back(getter.get()); } for (auto& setter : p->setters) { res.push_back(setter.get()); } return res; } if (auto vp = DynamicCast(&decl)) { std::vector> res{}; Walker(vp->irrefutablePattern.get(), [&res](auto node) { if (auto varDecl = DynamicCast(node)) { res.push_back(varDecl); } return VisitAction::WALK_CHILDREN; }).Walk(); return res; } return decl.GetMemberDeclPtrs(); } bool IsOOEAffectedDecl(const Decl& decl) { CJC_ASSERT(!decl.TestAttr(Attribute::IMPORTED) || decl.TestAttr(Attribute::FROM_COMMON_PART)); // variable with initialiser or function with body can be affected by order. Other decls do not contain // expression and therefore cannot be affected by order. if (auto var = DynamicCast(&decl)) { return var->initializer; } if (auto func = DynamicCast(&decl)) { return func->funcBody->body; } if (decl.astKind == ASTKind::MACRO_DECL || decl.astKind == ASTKind::MAIN_DECL || decl.astKind == ASTKind::PRIMARY_CTOR_DECL) { return true; } return false; } } // namespace Cangjie::IncrementalCompilation cangjie_compiler-1.0.7/src/Lex/000077500000000000000000000000001510705540100163265ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Lex/CMakeLists.txt000066400000000000000000000006371510705540100210740ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set(LEX_SRC Lexer.cpp LexerDiag.cpp LexerImpl.cpp) add_library(CangjieLex OBJECT ${LEX_SRC}) target_compile_options(CangjieLex PRIVATE ${CJC_EXTRA_WARNINGS}) cangjie_compiler-1.0.7/src/Lex/Lexer.cpp000066400000000000000000002034371510705540100201220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Lexer. */ #include "LexerImpl.h" #include #include #include #include "cangjie/Basic/Display.h" #include "cangjie/Basic/Utils.h" #include "cangjie/Utils/Unicode.h" using namespace Cangjie; namespace { inline bool IsLegalEscape(int32_t ch) { const std::string legalEscape("tbrnfv0'\"\\"); auto pos = legalEscape.find(static_cast(ch)); return pos != std::string::npos; } inline bool IsMacroEscape(char ch) { const std::string macroEscape("$@()[]"); auto pos = macroEscape.find(ch); return pos != std::string::npos; } inline bool IsSingleQuote(int32_t ch) { return ch == '\''; } } // namespace namespace Cangjie { const std::vector& GetContextualKeyword() { static const std::vector CONTEXTUAL_KEYWORD_TOKEN = {TokenKind::PUBLIC, TokenKind::PRIVATE, TokenKind::INTERNAL, TokenKind::PROTECTED, TokenKind::OVERRIDE, TokenKind::REDEF, TokenKind::ABSTRACT, TokenKind::SEALED, TokenKind::OPEN, TokenKind::COMMON, TokenKind::PLATFORM, TokenKind::FEATURES}; return CONTEXTUAL_KEYWORD_TOKEN; } bool IsContextualKeyword(std::string_view s) { static std::unordered_set names{}; if (names.empty()) { for (auto ct : GetContextualKeyword()) { names.insert(TOKENS[static_cast(ct)]); } } return names.count(s) == 1; } } bool LexerImpl::IsCurrentCharLineTerminator() const { if (Utils::GetLineTerminatorLength(pCurrent, pInputEnd) > 0) { return true; } return false; } Lexer::Lexer(unsigned int fileID, const std::string& input, DiagnosticEngine& diag, SourceManager& sm, bool cts) : impl{new LexerImpl{input, diag, sm, {.fileID = fileID, .posBase{fileID, 1, 1}, .collectTokenStream = cts}}} { } Lexer::Lexer( unsigned int fileID, const std::string& input, DiagnosticEngine& diag, SourceManager& sm, const Position& pos) : impl{new LexerImpl{input, diag, sm, {.fileID = fileID, .posBase{pos}}}} { } Lexer::Lexer(const std::string& input, DiagnosticEngine& diag, SourceManager& sm, bool cts, bool splitAmbi) : impl{new LexerImpl{input, diag, sm, {.collectTokenStream = cts, .splitAmbiguousToken = splitAmbi}}} { } Lexer::Lexer(const std::string& input, DiagnosticEngine& diag, SourceManager& sm, const Position& pos, bool cts) : impl{new LexerImpl{input, diag, sm, {.posBase{pos}, .collectTokenStream = cts}}} { } Lexer::Lexer(const std::string& input, DiagnosticEngine& diag, Source& s, const Position& pos, bool cts) : impl{new LexerImpl{input, diag, s, {.posBase{pos}, .collectTokenStream = cts}}} { } Lexer::Lexer(const std::vector& inputTokens, DiagnosticEngine& diag, SourceManager& sm, bool cts) : impl{new LexerImpl{inputTokens, diag, sm, {.collectTokenStream = cts}}} { } Lexer::~Lexer() { delete impl; } const Position& Token::End() const { return end; } bool LexerImpl::CheckArraySize(size_t len, int32_t& ch) { if (pNext + len > pInputEnd) { if (success) { int32_t cha = static_cast(static_cast((reinterpret_cast(pNext))[0])); diag.DiagnoseRefactor(DiagKindRefactor::lex_illegal_UTF8_encoding_byte, GetPos(pNext), ToBinaryString(static_cast(cha))); success = false; } pNext = pInputEnd; ch = ERROR_UTF8; return false; } return true; } std::string Cangjie::ProcessQuotaMarks(const std::string& value, bool processSingle) { bool inDollar{false}; auto lCurl = 0; auto num = 0; // Quotation marks that are in interpolation do not need to be escaped, like "${"abc"}". std::string ret; for (const char ch : value) { if (!ret.empty() && ret.back() == '$' && ch == '{') { inDollar = true; } if (inDollar && ch == '{') { lCurl++; } if (inDollar && ch == '}') { if (lCurl > 0) { lCurl--; } if (lCurl == 0) { inDollar = false; } } if (inDollar) { ret += ch; continue; } if (ch == '\"' && (ret.empty() || ret.back() != '\\')) { num++; if (num == 3) { // MultiLinesString(with 3 ") in MultiLinesString need to convert to \"\"\" num = 0; ret += "\\\"\\\"\\\""; } continue; } for (; num > 0; num--) { ret += processSingle ? "\\\"" : "\""; } ret += ch; } for (; num > 0; num--) { ret += processSingle ? "\\\"" : "\""; } return ret; } const std::vector& LexerImpl::GetStrParts(const Token& t) { static const std::set tkKinds = {TokenKind::STRING_LITERAL, TokenKind::MULTILINE_STRING}; CJC_ASSERT(tkKinds.count(t.kind) != 0); // If the Lexer is constructed from vector, stringPartsMap will be built when // we see a STRING_LITERAL Token. if (!enableScan) { std::string input; if (t.kind == TokenKind::MULTILINE_STRING) { #ifdef _WIN32 input = "\"\"\"\r\n" + ProcessQuotaMarks(t.Value()) + "\"\"\""; #else input = "\"\"\"\n" + ProcessQuotaMarks(t.Value()) + "\"\"\""; #endif } else { input = "\"" + ProcessQuotaMarks(t.Value(), true) + "\""; } // We have to build StringPart using a lexer. LexerImpl tempLexer( input, diag, source, {0, t.Begin()}); // the token.pos is always zero, which is not good I suppose. tempLexer.Scan(); stringPartsMap.insert(tempLexer.stringPartsMap.begin(), tempLexer.stringPartsMap.end()); } CJC_ASSERT(stringPartsMap.find(t) != stringPartsMap.end() && "stringPartsMap is empty"); return stringPartsMap[t]; } void LexerImpl::ReadUTF8Char() { pCurrent = pNext; if (pNext >= pInputEnd) { currentChar = -1; return; } auto ch = static_cast(static_cast((reinterpret_cast(pNext))[0])); if (ch < BYTE_2_FLAG) { currentChar = ch; pNext += (currentChar == '\r' && GetNextChar(1) == '\n') ? BYTE_2_STEP : BYTE_1_STEP; TryRegisterLineOffset(); if (ch >= BYTE_X_FLAG && success) { diag.DiagnoseRefactor(DiagKindRefactor::lex_illegal_UTF8_encoding_byte, GetPos(pCurrent), ToBinaryString(static_cast(*pCurrent))); success = false; } return; } ReadUTF8CharFromMultiBytes(ch); // If there is illegal utf8, eating out of all of them. while (ch == ERROR_UTF8 && pNext < pInputEnd) { ch = static_cast(static_cast((reinterpret_cast(pNext))[0])); if (ch < BYTE_X_FLAG) { pCurrent = pNext; pNext += BYTE_1_STEP; currentChar = ERROR_UTF8; return; } ReadUTF8CharFromMultiBytes(ch); } currentChar = ch; } void LexerImpl::ReadUTF8CharFromMultiBytes(int32_t& ch) { if (!CheckArraySize(BYTE_2_STEP, ch)) { return; } uint32_t byte1 = static_cast(ch); uint32_t byte2 = reinterpret_cast(pNext)[1]; CheckIllegalUTF8InStringLiteral(byte2); if (byte2 >= BYTE_2_FLAG || byte2 < BYTE_X_FLAG) { ch = ERROR_UTF8; pNext += (byte2 < BYTE_X_FLAG) ? BYTE_1_STEP : BYTE_2_STEP; } else if (ch >= BYTE_4_FLAG) { if (!CheckArraySize(BYTE_4_STEP, ch)) { return; } uint32_t byte3 = reinterpret_cast(pNext)[BYTE_3_INDEX]; uint32_t byte4 = reinterpret_cast(pNext)[BYTE_4_INDEX]; CheckIllegalUTF8InStringLiteral(byte3); CheckIllegalUTF8InStringLiteral(byte4); ch = int32_t(((byte1 & LOW_3_BIT_MASK) << LEFT_SHIFT_18) | ((byte2 & LOW_6_BIT_MASK) << LEFT_SHIFT_12) | ((byte3 & LOW_6_BIT_MASK) << LEFT_SHIFT_6) | (byte4 & LOW_6_BIT_MASK)); CheckUnsecureUnicodeValue(ch); // disallow ambiguous code points for security if (ch < BYTE_4_BASE) { ch = ERROR_UTF8; } pNext += BYTE_4_STEP; } else if (ch >= BYTE_3_FLAG) { if (!CheckArraySize(BYTE_3_STEP, ch)) { return; } uint32_t byte3 = reinterpret_cast(pNext)[BYTE_3_INDEX]; CheckIllegalUTF8InStringLiteral(byte3); ch = int32_t(((byte1 & LOW_4_BIT_MASK) << LEFT_SHIFT_12) | ((byte2 & LOW_6_BIT_MASK) << LEFT_SHIFT_6) | (byte3 & LOW_6_BIT_MASK)); CheckUnsecureUnicodeValue(ch); // disallow ambiguous code points for security if (ch < BYTE_3_BASE) { ch = ERROR_UTF8; } pNext += BYTE_3_STEP; } else { ch = int32_t(((byte1 & LOW_5_BIT_MASK) << LEFT_SHIFT_6) | (byte2 & LOW_6_BIT_MASK)); CheckUnsecureUnicodeValue(ch); // disallow ambiguous code points for security if (ch < BYTE_2_BASE) { ch = ERROR_UTF8; } pNext += BYTE_2_STEP; } } TokenKind LexerImpl::LookupKeyword(const std::string& literal) { if (LexerImpl::tokenMap.count(literal) != 0) { // keyword return LexerImpl::tokenMap[literal]; } // identifier return TokenKind::IDENTIFIER; } void LexerImpl::Back() { pNext = pCurrent; } std::string LexerImpl::GetSuffix(const char* pSuffixStart) { while (std::isalnum(currentChar)) { ReadUTF8Char(); } std::string suffix; if (pSuffixStart + 1 < pInputEnd) { if (pCurrent != pInputEnd && pSuffixStart + 1 < pNext - 1) { if (*(pNext - 1) == '\n' && *(pNext - BYTE_2_STEP) == '\r') { suffix = std::string(pSuffixStart + 1, pNext - BYTE_2_STEP); } else { suffix = std::string(pSuffixStart + 1, pNext - 1); } } else if (pCurrent == pInputEnd && pSuffixStart + 1 < pNext) { suffix = std::string(pSuffixStart + 1, pNext); } } return suffix; } void LexerImpl::ProcessIntegerSuffix() { std::string suffixType{static_cast(currentChar)}; auto pSuffixStart = pCurrent; auto suffix = GetSuffix(pSuffixStart); if (!(suffix == "64" || suffix == "32" || suffix == "8" || suffix == "16")) { DiagUnexpectedIntegerLiteralTypeSuffix(pSuffixStart, suffixType, suffix); success = false; tokenKind = TokenKind::ILLEGAL; } Back(); return; } bool LexerImpl::ProcessXdigit(const int& base, bool& hasDigit, const char* reasonPoint) { // If it is 'e' or 'E', it could be exponent part. if (base != HEX_BASE && (currentChar == 'e' || currentChar == 'E')) { return false; } if (base != HEX_BASE && currentChar == 'f') { return false; } hasDigit = true; if (base != HEX_BASE && success) { DiagUnexpectedDigit(base, reasonPoint); success = false; } return true; } /// ProcessDigits function processes number and _ in the number lexer, /// if the digit >= base,report error. /// finally return hasDigit and the index of invalid char. // return true if it has integer suffix, false otherwise. bool LexerImpl::ProcessDigits(const int& base, bool& hasDigit, const char* reasonPoint) { bool hasTypeSuffix = false; int max = base + static_cast('0'); for (;;) { if (currentChar > ASCII_BASE) { if (success) { DiagExpectedDigit(*reasonPoint); success = false; } } else if (isdigit(currentChar) && base <= DEC_BASE) { // Process base <= 10. hasDigit = true; if (currentChar >= max && success) { DiagUnexpectedDigit(base, reasonPoint); success = false; } } else if (isxdigit(currentChar)) { // Process hexadecimal. if (!ProcessXdigit(base, hasDigit, reasonPoint)) { break; } } else if (tokenKind != TokenKind::FLOAT_LITERAL && hasDigit && (currentChar == 'i' || currentChar == 'u')) { hasTypeSuffix = true; ProcessIntegerSuffix(); return hasTypeSuffix; } else if (currentChar != '_') { break; } ReadUTF8Char(); } return hasTypeSuffix; } const char* LexerImpl::PrefixName(const char prefix) const { switch (prefix) { case 'x': return "hexadecimal"; case 'o': return "octal"; case 'b': return "binary"; default: return "decimal"; } } bool LexerImpl::ScanNumberIntegerPart( const char* pStart, int& base, char& prefix, bool& hasDigit, const char*& reasonPoint) { tokenKind = TokenKind::INTEGER_LITERAL; char nextChar = GetNextChar(0); if (currentChar == '0') { if (nextChar == 'i' || nextChar == 'u' || nextChar == 'f') { base = DEC_BASE; prefix = 'd'; hasDigit = true; currentChar = *pCurrent; return ProcessDigits(base, hasDigit, reasonPoint); } ReadUTF8Char(); switch (tolower(currentChar)) { case 'x': reasonPoint = pCurrent; ReadUTF8Char(); base = HEX_BASE; prefix = 'x'; break; case 'o': reasonPoint = pCurrent; ReadUTF8Char(); base = OCT_BASE; prefix = 'o'; break; case 'b': reasonPoint = pCurrent; ReadUTF8Char(); base = BIN_BASE; prefix = 'b'; break; default: reasonPoint = pStart; base = DEC_BASE; prefix = 'd'; hasDigit = true; } } if (prefix != 'd' && currentChar == '_' && success) { DiagExpectedDigit(*reasonPoint); success = false; } return ProcessDigits(base, hasDigit, reasonPoint); } void LexerImpl::ScanNumberDecimalPart(const int& base, const char& prefix, bool& hasDigit, const char* reasonPoint) { tokenKind = TokenKind::FLOAT_LITERAL; if ((prefix == 'o' || prefix == 'b') && success) { DiagUnexpectedDecimalPoint(reasonPoint); success = false; } ReadUTF8Char(); ProcessDigits(base, hasDigit, reasonPoint); } void LexerImpl::ScanNumberExponentPart(const char* reasonPoint) { reasonPoint = pCurrent; ReadUTF8Char(); tokenKind = TokenKind::FLOAT_LITERAL; if (currentChar == '-') { ReadUTF8Char(); } if (currentChar == '_' && success) { DiagExpectedDigit('d'); success = false; } bool expHasDigit{false}; ProcessDigits(DEC_BASE, expHasDigit, reasonPoint); if (!expHasDigit && success) { DiagExpectedDigit('d'); success = false; } } void LexerImpl::ProcessFloatSuffix(const char prefix) { tokenKind = TokenKind::FLOAT_LITERAL; // 0f64 should be float token const char* pSuffixStart = pCurrent; if (prefix != 'd') { diag.DiagnoseRefactor(DiagKindRefactor::lex_illegal_non_decimal_float, GetPos(pSuffixStart)); success = false; tokenKind = TokenKind::ILLEGAL; } auto suffix = GetSuffix(pSuffixStart); if (!(suffix == "64" || suffix == "32" || suffix == "16")) { DiagUnexpectedFloatLiteralTypeSuffix(pSuffixStart, suffix); success = false; tokenKind = TokenKind::ILLEGAL; } } void LexerImpl::ProcessNumberExponentPart(const char prefix, const char* reasonPoint, bool& isFloat) { char exp = static_cast(std::tolower(static_cast(currentChar))); if (exp == 'e' || exp == 'p') { if (success && ((prefix != 'd' && prefix != 'x') || (exp == 'e' && prefix == 'x') || (exp == 'p' && prefix == 'd'))) { DiagUnexpectedExponentPart(exp, prefix, reasonPoint); success = false; } if (exp == 'e') { isFloat = true; } ScanNumberExponentPart(reasonPoint); } else if (success && prefix == 'x' && tokenKind == TokenKind::FLOAT_LITERAL) { DiagExpectedExponentPart(reasonPoint); success = false; } } void LexerImpl::ProcessNumberFloatSuffix(const char& prefix, bool isFloat) { using namespace Unicode; auto tempPoint = pCurrent; auto hasSuffix{false}; if (currentChar == 'f') { ProcessFloatSuffix(prefix); hasSuffix = true; } auto suffixBegin = pCurrent; while (std::isalnum(currentChar) || currentChar == '.' || currentChar == '_') { // The range .. is legal. if (currentChar == '.' && GetNextChar(0) == '.' && GetNextChar(1) != '.') { Back(); break; } if (pNext == pInputEnd) { break; } // The .identifier is legal. auto ppNext = reinterpret_cast(&pNext); auto ppEnd = reinterpret_cast(pInputEnd); UTF32 cp; UTF32* input{&cp}; if (currentChar == '.') { auto cur = pNext; auto conv = ConvertUTF8toUTF32(ppNext, ppEnd, &input, input + 1); if (conv == ConversionResult::OK) { if (IsCJXIDStart(cp)) { // valid identifier after '.', this is a member access, where the member name is possibly // a Unicode identifier // example: 2n.0 pNext = cur; break; } // failed to scan an identifier after '.' but still valid unicode (e.g. excessive .), continue to // consume. example: .1... currentChar = static_cast(cp); continue; } // failed to scan a Unicode char, break to report an error pNext = cur; currentChar = static_cast(cp); break; } pCurrent = pNext; auto conv = ConvertUTF8toUTF32(ppNext, ppEnd, &input, input + 1); if (conv != ConversionResult::OK) { cp = *reinterpret_cast(pNext); } currentChar = static_cast(cp); } if (((!isFloat && hasSuffix) || (suffixBegin != pNext && !IsAdjacent(suffixBegin, pNext))) && success) { auto errPoint = isFloat ? suffixBegin : tempPoint; auto args = std::string{errPoint, pCurrent}; /// the suffix is empty, add dot to prevent empty string in diag message if (args.empty()) { args = "."; } auto builder = diag.DiagnoseRefactor( DiagKindRefactor::lex_unknown_suffix, MakeRange(GetPos(errPoint), GetPos(pCurrent)), args); builder.AddMainHintArguments(args); } Back(); } // illegal start decimal part,e.g 01、0_1 bool LexerImpl::IsIllegalStartDecimalPart(const char* pStart, const char* pEnd) const { if (pStart > pEnd) { return false; } const char* start = pStart; if (*start != '0') { return false; } // decimal starts with 0 start++; if (start > pEnd) { return false; } if (isdigit(*start)) { return true; } if (*start != '_') { return false; } // decimal starts with 0_ start++; if (start > pEnd) { return false; } for (; start <= pEnd && *start == '_'; start++) { } return start <= pEnd && isdigit(*start); } Token LexerImpl::ScanNumber(const char* pStart) { int base = DEC_BASE; // number base char prefix = 'd'; // 0(default), d(decimal),'O'(octal),'x'(Hex),'b'(bin) const char* reasonPoint{pStart}; bool hasDigit = false; // hasDigit is true when digit present // 1. Process integer part. if (currentChar != '.') { if (ScanNumberIntegerPart(pStart, base, prefix, hasDigit, reasonPoint)) { if (IsIllegalStartDecimalPart(pStart, pCurrent)) { diag.DiagnoseRefactor( DiagKindRefactor::lex_cannot_start_with_digit, GetPos(pStart), "integer", std::string{*pStart}); } Back(); return Token(tokenKind, std::string(pStart, pNext), pos, GetPos(pNext)); } } bool isFloat{false}; // 2. Process fractional part. if (currentChar == '.') { // 0. is not a float number 0.b is lex to 0 DOT b if (prefix == 'x') { if (!isxdigit(GetNextChar(0))) { DiagSmallExpectedDigit(hasDigit, prefix); Back(); return Token(tokenKind, std::string(pStart, pNext), pos, GetPos(pNext)); } } else { if (!isdigit(GetNextChar(0))) { DiagSmallExpectedDigit(hasDigit, prefix); Back(); return Token(tokenKind, std::string(pStart, pNext), pos, GetPos(pNext)); } } hasDigit = false; isFloat = true; if (IsIllegalStartDecimalPart(pStart, pCurrent)) { diag.DiagnoseRefactor( DiagKindRefactor::lex_cannot_start_with_digit, GetPos(pStart), "float", std::string{*pStart}); } ScanNumberDecimalPart(base, prefix, hasDigit, reasonPoint); } else { if (IsIllegalStartDecimalPart(pStart, pCurrent)) { diag.DiagnoseRefactor( DiagKindRefactor::lex_cannot_start_with_digit, GetPos(pStart), "integer", std::string{*pStart}); } } DiagSmallExpectedDigit(hasDigit, prefix); // 3. Process exponent part. ProcessNumberExponentPart(prefix, reasonPoint, isFloat); // 4. Process float number suffix ProcessNumberFloatSuffix(prefix, isFloat); return Token(tokenKind, std::string(pStart, pNext), pos, GetPos(pNext)); } Token LexerImpl::ScanDotPrefixSymbol() { if (GetNextChar(0) == '.') { ReadUTF8Char(); if (GetNextChar(0) == '.') { ReadUTF8Char(); return Token(TokenKind::ELLIPSIS, "...", pos, GetPos(pNext)); } else if (GetNextChar(0) == '=') { ReadUTF8Char(); return Token(TokenKind::CLOSEDRANGEOP, "..=", pos, GetPos(pNext)); } else { return Token(TokenKind::RANGEOP, "..", pos, GetPos(pNext)); } } else { return Token(TokenKind::DOT, ".", pos, GetPos(pNext)); } } using namespace Unicode; bool LexerImpl::TryConsumeIdentifierUTF8Char() { CJC_ASSERT(pNext != pCurrent); pCurrent = pNext; unsigned codePoint; unsigned* p{&codePoint}; auto suc = ConvertUTF8toUTF32(reinterpret_cast(&pNext), reinterpret_cast(pInputEnd), &p, p + 1); if (suc != ConversionResult::OK) { codePoint = *reinterpret_cast(pNext++); // consume one error char currentChar = static_cast(codePoint); DiagIllegalUnicode(); success = false; return false; } if (!IsXIDContinue(codePoint)) { currentChar = static_cast(codePoint); DiagIllegalUnicode(); success = false; return false; } return true; } void LexerImpl::DiagIllegalUnicode() { auto args = ConvertUnicode(currentChar); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::lex_illegal_unicode, GetPos(pCurrent), args); builder.AddMainHintArguments(args); } void LexerImpl::ScanIdentifierContinue(Token& res, const char* pStart) { CJC_ASSERT(pCurrent == pStart); res.kind = TokenKind::IDENTIFIER; while (pNext != pInputEnd) { // input may end at the last identifier currentChar = *pNext; auto cp = static_cast(currentChar); if (IsASCIIIdContinue(cp)) { pCurrent = pNext++; continue; } // pNext and pCurrent advance in call to TryConsumeIdentifierUTF8Char if (IsASCII(cp)) { break; } if (!TryConsumeIdentifierUTF8Char()) { res.kind = TokenKind::ILLEGAL; break; } } std::string s{pStart, pNext}; if (res.kind == TokenKind::IDENTIFIER) { NFC(s); } res.SetValuePos(std::move(s), GetPos(pStart), GetPos(pNext)); } void LexerImpl::ScanUnicodeIdentifierStart(Token& res, unsigned codePoint, const char* pStart) { if (IsCJXIDStart(codePoint)) { return ScanIdentifierContinue(res, pStart); } if (!IsASCII(codePoint)) { std::string s{pStart, pCurrent}; currentChar = static_cast(codePoint); if (IsXIDContinue(codePoint)) { diag.DiagnoseRefactor( DiagKindRefactor::lex_unrecognized_symbol, MakeRange(GetPos(pStart), GetPos(pCurrent)), s); } else { DiagIllegalUnicode(); } success = false; pNext = pCurrent; res.kind = TokenKind::ILLEGAL; res.SetValuePos(s, GetPos(pStart), GetPos(pNext)); return; } CJC_ABORT(); } void LexerImpl::ScanIdentifierOrKeyword(Token& res, const char* pStart) { // starts with ascii identifier start character if (IsASCIIIdStart(static_cast(currentChar))) { ScanIdentifierContinue(res, pStart); res.kind = LookupKeyword(res.Value()); return; } // if the first character is a valid unicode codepoint, try scan it as a unicode identifier UTF32 codePoint; auto codePointPtr{&codePoint}; auto convSt = ConvertUTF8toUTF32(reinterpret_cast(&pStart), reinterpret_cast(pInputEnd), &codePointPtr, codePointPtr + 1); if (convSt == ConversionResult::OK) { ScanUnicodeIdentifierStart(res, codePoint, pCurrent); // no need to check for TokenKind as keyword never begins with a non-ASCII identifier return; } // unicode conversion failure, issue a diagnostic std::string s{pStart, pCurrent}; if (IsXIDContinue(codePoint)) { diag.DiagnoseRefactor( DiagKindRefactor::lex_unrecognized_symbol, MakeRange(GetPos(pStart), GetPos(pCurrent)), s); } else { currentChar = static_cast(codePoint); DiagIllegalUnicode(); } success = false; pCurrent = pNext; res = Token{TokenKind::ILLEGAL}; res.SetValue(std::move(s)); } Token LexerImpl::ScanBackquotedIdentifier(const char* pStart) { Token res{TokenKind::ILLEGAL}; std::function scanIdentifier = [this, &res]() { ReadUTF8Char(); auto codePoint = static_cast(currentChar); if (IsCJXIDStart(codePoint)) { // identifier ScanUnicodeIdentifierStart(res, codePoint, pCurrent); if (res == "_") { res.kind = TokenKind::WILDCARD; diag.DiagnoseRefactor(DiagKindRefactor::lex_expected_letter_after_underscore, MakeRange(GetPos(pCurrent), GetPos(pCurrent + 1))); } // forward pCurrent, pNext and currentChar currentChar = *(pCurrent = pNext++); return true; } return false; }; bool isPackageIdent{false}; if (scanIdentifier()) { while (currentChar == '.' || currentChar == '-' || currentChar == ' ') { isPackageIdent = true; auto pLast = pCurrent; if (scanIdentifier()) { continue; } if (IsAdjacent(pLast, pCurrent) && success) { diag.DiagnoseRefactor( DiagKindRefactor::lex_expected_identifier, GetPos(pCurrent), std::string{*pCurrent}); success = false; } break; } } // reset identifier position to the first backquote res.SetValuePos("`" + res.Value(), GetPos(pStart), GetPos(pNext)); res.kind = isPackageIdent ? TokenKind::PACKAGE_IDENTIFIER : TokenKind::IDENTIFIER; // scanning closing '`' after identifier if (currentChar == '`') { if (IsAdjacent(pStart, pCurrent) && success) { diag.DiagnoseRefactor(DiagKindRefactor::lex_expected_identifier, GetPos(pCurrent), std::string{*pCurrent}); success = false; } // the token range exclude '`' for raw identifiers res.SetValuePos(res.Value() + "`", res.Begin(), res.End()); } else { // error handling Back(); if (!IsAdjacent(pStart, pCurrent) && success) { auto builder = diag.DiagnoseRefactor( DiagKindRefactor::lex_expected_back_quote, GetPos(pCurrent), ConvertCurrentChar()); builder.AddHint(GetPos(pStart)); success = false; } else if (success) { diag.DiagnoseRefactor(DiagKindRefactor::lex_expected_identifier, GetPos(pCurrent), ConvertCurrentChar()); success = false; } while (currentChar != '`' && currentChar != ' ' && !IsCurrentCharLineTerminator() && currentChar != -1) { ReadUTF8Char(); } } return res; } std::string LexerImpl::ConvertCurrentChar() { if (IsCurrentCharLineTerminator()) { return "new line character"; } const int32_t& ch = currentChar; return ConvertChar(ch); } void LexerImpl::ProcessUnicodeEscape() { int hexNum = 0; UTF32 hexVal{0}; const char* uniStart = pCurrent; const char* old = pCurrent - 1; ReadUTF8Char(); if (currentChar != '{' && success) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::lex_expected_left_bracket, GetPos(pCurrent), ConvertCurrentChar()); builder.AddHint(MakeRange(GetPos(pCurrent - std::string("\\u").size()), GetPos(pCurrent))); success = false; return; } for (;;) { ReadUTF8Char(); if (hexNum == UNICODE_MAX_NUM) { Back(); break; } if (isxdigit(currentChar)) { hexNum++; UTF32 n{0}; constexpr UTF32 base{4}; // count to shift when converting hexademical to decimal constexpr UTF32 ten{10}; // base to add when converting hexademical to decimal // this char is already valid hex value, since '9' < 'A' < 'a', only one check is necessary if (currentChar <= '9') { // 0-9 n = static_cast(currentChar - static_cast('0')); } else if (currentChar >= 'a') { // a-z n = static_cast(static_cast(currentChar - static_cast('a')) + ten); } else { n = static_cast(static_cast(currentChar - static_cast('A')) + ten); } hexVal = (hexVal << base) | static_cast(n); } else { Back(); break; } } if (hexNum == 0 && success) { DiagExpectedDigit('x'); success = false; } if (hexNum == UNICODE_MAX_NUM && isxdigit(currentChar) && success) { // 1 to 8 hex digits allowed DiagExpectedRightBracket(uniStart - 1); Back(); success = false; } ReadUTF8Char(); if (currentChar != '}' && success) { if (hexNum == UNICODE_MAX_NUM) { DiagExpectedRightBracket(uniStart - 1); } else { DiagExpectedRightBracketOrHexadecimal(uniStart - 1); } Back(); success = false; } if (!IsLegalUnicode(hexVal) && success) { std::stringstream stream; stream << std::hex << hexVal; std::string result(stream.str()); diag.DiagnoseRefactor( DiagKindRefactor::lex_illegal_uni_character_literal, MakeRange(GetPos(old), GetPos(pNext)), result); } } void LexerImpl::ProcessEscape(const char* pStart, bool isInString, bool isByteLiteral) { ReadUTF8Char(); if (IsLegalEscape(currentChar) || (!isByteLiteral && static_cast(currentChar) == '$')) { return; } if (currentChar == 'u') { ProcessUnicodeEscape(); } else { if (success) { DiagUnrecognizedEscape(pStart, isInString, isByteLiteral); success = false; } Back(); } } // Return position of arbitrary location. Position LexerImpl::GetPos(const char* current) { size_t offset = static_cast((current >= pInputEnd ? pInputEnd : current) - pInputStart); size_t loc = lineOffsetsFromBase.size() - 1; // Find target line base from line base offset vector. if (offset < lineOffsetsFromBase.back()) { auto offsetIndex = std::upper_bound(lineOffsetsFromBase.begin(), lineOffsetsFromBase.end(), offset); loc = static_cast(std::distance(lineOffsetsFromBase.begin(), offsetIndex) - 1); } // If reach to the end and last character is newline, decline last extra column. if (pInputStart < pInputEnd && current == pInputEnd && loc != 0) { if (*(current - 1) == '\n') { loc--; } if (current - BYTE_2_STEP >= pInputStart && *(current - BYTE_2_STEP) == '\r' && *(current - 1) == '\n') { offset--; } } // Only first line need the column base. int columnBase = loc == 0 ? posBase.column : 1; auto column = columnBase + static_cast(offset - lineOffsetsFromBase[loc]); return Position{posBase.fileID, posBase.line + static_cast(loc), static_cast(column)}; } std::pair LexerImpl::ScanStringOrJString(const char* pStart, bool needStringParts, bool isJString) { bool res{true}; std::vector stringParts; const char* begin = pStart + 1; stringStarts.emplace_back(pStart, false); tokenKind = isJString ? TokenKind::JSTRING_LITERAL : TokenKind::STRING_LITERAL; size_t offset = 1; Position beginPos = GetPos(begin); if (isJString) { ReadUTF8Char(); ++offset; } auto quote = currentChar; for (;;) { ReadUTF8Char(); if (currentChar == quote) { stringParts.emplace_back(StringPart::STR, std::string(begin, pCurrent), beginPos, GetPos(pCurrent)); break; } else if (IsCurrentCharLineTerminator() || (currentChar == -1)) { stringStarts.pop_back(); return {ProcessIllegalToken(needStringParts, false, isJString ? pStart + 1 : pStart, isJString), false}; } else if (currentChar == '\\') { ProcessEscape(pStart, true, false); } else if (currentChar == '$' && GetNextChar(0) == '{' && !isJString) { std::tie(begin, res) = ProcessStringInterpolation(begin, beginPos, stringParts); if (!res) { break; } } } auto ret = Token(tokenKind, std::string(pStart + offset, pCurrent), pos, GetPos(pNext)); ret.isSingleQuote = IsSingleQuote(quote); if (needStringParts) { stringPartsMap.emplace(std::pair{ret, stringParts}); } stringStarts.pop_back(); return {ret, res}; } Token LexerImpl::ProcessIllegalToken(bool needStringParts, bool multiLine, const char* pStart, bool isJString) { bool isMatchEnd = currentChar == -1; if (success) { if (!multiLine) { DiagUnterminatedSingleLineString(pStart, isMatchEnd, isJString); } else { DiagUnterminatedMultiLineString(pStart); } success = false; } auto tokKind = multiLine ? TokenKind::MULTILINE_STRING : (isJString ? TokenKind::JSTRING_LITERAL : TokenKind::STRING_LITERAL); auto tok = Token(tokKind, std::string{pStart, pCurrent}, pos, GetPos(pCurrent)); std::vector stringParts{ StringPart{StringPart::STR, std::string(pStart, pCurrent), pos, GetPos(pCurrent)}}; if (needStringParts) { stringPartsMap.emplace(std::pair{tok, stringParts}); } return tok; } bool LexerImpl::ScanInterpolationString(const char* pStart, bool allowNewLine) { interpolations.push_back(pCurrent); ReadUTF8Char(); if (!ScanInterpolationStringLiteralHoleBalancedText(pStart, '}', allowNewLine)) { interpolations.pop_back(); return false; } interpolations.pop_back(); if (currentChar == '}') { return true; } return false; } bool LexerImpl::ScanInterpolationStringLiteralHoleComment(bool allowNewline) { if (GetNextChar(0) == '/' && !allowNewline) { for (;;) { ReadUTF8Char(); if (IsCurrentCharLineTerminator() || (currentChar == -1)) { break; } } if (success) { DiagUnterminatedInterpolation(); success = false; } Back(); return false; } if (GetNextChar(0) == '*' || GetNextChar(0) == '/') { auto [t, res] = ScanComment(pCurrent, allowNewline); return res; } return true; } bool LexerImpl::ProcessInterpolationStringLiteralLineBreak(bool allowNewline) { if (IsCurrentCharLineTerminator()) { if (allowNewline) { return true; } } if (success) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::lex_unterminated_interpolation, MakeRange(GetPos(interpolations.back()), GetPos(pCurrent) + Position{0, 0, 1})); if (!allowNewline && currentChar != -1) { builder.AddHint(GetPos(stringStarts.back().first)); } success = false; } return false; } bool LexerImpl::ScanInterpolationStringLiteralHoleBalancedTextString() { CJC_ASSERT(currentChar == '\'' || currentChar == '"'); if (GetNextChar(0) == currentChar && GetNextChar(1) == currentChar) { return ScanMultiLineString(pCurrent, false).second; } return ScanStringOrJString(pCurrent, false).second; } bool LexerImpl::ScanInterpolationStringLiteralHoleBalancedText(const char* pStart, char endingChar, bool allowNewline) { for (;;) { ReadUTF8Char(); if (currentChar == '"' || currentChar == '\'') { if (!ScanInterpolationStringLiteralHoleBalancedTextString()) { return false; } } else if (currentChar == '#' && GetNextChar(0) == '#') { if (!ScanMultiLineRawString(pCurrent).second) { return false; } } else if (currentChar == '{') { if (!ScanInterpolationStringLiteralHoleBalancedText(pStart, '}', allowNewline)) { return false; } } else if (currentChar == '}') { if (endingChar == currentChar) { break; } } else if (currentChar == -1 || IsCurrentCharLineTerminator()) { if (!ProcessInterpolationStringLiteralLineBreak(allowNewline)) { return false; } } else if (currentChar == '/') { if (!ScanInterpolationStringLiteralHoleComment(allowNewline)) { return false; } } } return true; } std::pair LexerImpl::ProcessStringInterpolation( const char* pStart, Position& beginPos, std::vector& stringParts, bool allowNewLine) { const char* begin = pStart; // If the String Interpolation ${ is at the very beginning of the line, use allowNewLine also means // ScanMultiLineString. bool interpolationAtBegin = allowNewLine && GetPos(pCurrent).column == 1; if (begin != pCurrent || interpolationAtBegin) { stringParts.emplace_back(StringPart(StringPart::STR, std::string(begin, pCurrent), beginPos, GetPos(pCurrent))); begin = pCurrent; beginPos = GetPos(begin); } bool closeBrace = ScanInterpolationString(pStart, allowNewLine); if (closeBrace) { stringParts.emplace_back( StringPart(StringPart::EXPR, std::string(begin, pCurrent + 1), beginPos, GetPos(pCurrent + 1))); begin = pCurrent + 1; beginPos = GetPos(begin); } return {begin, closeBrace}; } std::pair LexerImpl::ScanMultiLineString(const char* pStart, bool needStringParts) { bool res{true}; std::vector stringParts; stringStarts.emplace_back(pCurrent, true); auto quote = currentChar; ReadUTF8Char(); // consume second " or ' ReadUTF8Char(); // consume third " or ' std::string beginDelimiters = R"(""")"; size_t multiStringBeginOffset = beginDelimiters.size(); size_t multiStringEndOffset = multiStringBeginOffset - 1; // should be 2 uint8_t terminatorLength = Utils::GetLineTerminatorLength(pNext, pInputEnd); if (terminatorLength > 0) { multiStringBeginOffset += terminatorLength; } else { if (success) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::lex_multiline_string_start_from_newline, GetPos(pNext)); builder.AddHint(MakeRange(GetPos(pStart), GetPos(pNext))); success = false; } } const char* begin = pStart + multiStringBeginOffset; Position beginPos = GetPos(begin); for (;;) { ReadUTF8Char(); if (currentChar == '\\') { ProcessEscape(pStart, true, false); } else if ((currentChar == quote) && (GetNextChar(0) == quote) && (GetNextChar(1) == quote)) { stringParts.emplace_back(StringPart::STR, std::string(begin, pCurrent), beginPos, GetPos(pCurrent)); ReadUTF8Char(); ReadUTF8Char(); break; } else if (currentChar == -1) { stringStarts.pop_back(); return {ProcessIllegalToken(needStringParts, true, pStart), false}; } else if (currentChar == '$' && GetNextChar(0) == '{') { std::tie(begin, res) = ProcessStringInterpolation(begin, beginPos, stringParts, true); } } auto ret = Token(TokenKind::MULTILINE_STRING, std::string(pStart + multiStringBeginOffset, pCurrent - multiStringEndOffset), pos, GetPos(pNext)); // """ is not token value ret.isSingleQuote = IsSingleQuote(quote); if (needStringParts) { stringPartsMap.emplace(std::pair{ret, stringParts}); } stringStarts.pop_back(); return {ret, res}; } void LexerImpl::ConsumeNChar(uint32_t n) { for (uint32_t i = 0; i < n; i++) { ReadUTF8Char(); } } std::pair LexerImpl::ScanMultiLineRawString(const char* pStart) { uint32_t delimiterNum = 0; while (currentChar == '#') { delimiterNum++; ReadUTF8Char(); } if (currentChar != '"' && currentChar != '\'' && success) { auto builder = diag.DiagnoseRefactor( DiagKindRefactor::lex_expected_quote_in_raw_string, GetPos(pCurrent), ConvertCurrentChar()); builder.AddHint(MakeRange(GetPos(pStart), GetPos(pCurrent))); success = false; return {Token(TokenKind::MULTILINE_RAW_STRING, "", pos, GetPos(pCurrent)), false}; } auto quote = currentChar; uint32_t count = delimiterNum; for (;;) { ReadUTF8Char(); if ((currentChar == quote) && (GetNextChar(0) == '#')) { const char* pTmp = pNext; while (pTmp[0] == '#' && count > 0) { count--; pTmp++; } if (count == 0) { ConsumeNChar(delimiterNum); break; } count = delimiterNum; } else if (currentChar == -1) { if (success) { DiagUnterminatedRawString(pStart); success = false; } return {Token(TokenKind::MULTILINE_RAW_STRING, "", pos, GetPos(pNext)), false}; } } auto tok = Token(TokenKind::MULTILINE_RAW_STRING, std::string(pStart + delimiterNum + 1, pCurrent - delimiterNum), pos, GetPos(pNext)); // Delimiters are not token value, but included in position range begin..end. tok.delimiterNum = delimiterNum; tok.isSingleQuote = IsSingleQuote(quote); return {tok, false}; } std::pair LexerImpl::ScanMultiLineComment(const char* pStart, bool allowNewLine) { size_t level = 1; while ((currentChar != -1) && (level > 0)) { ReadUTF8Char(); if (IsCurrentCharLineTerminator()) { if (success && !allowNewLine) { DiagUnterminatedInterpolation(); success = false; break; } } if (currentChar == '*' && GetNextChar(0) == '/') { level -= 1; ReadUTF8Char(); } else if (currentChar == '/' && GetNextChar(0) == '*') { level += 1; ReadUTF8Char(); } } if (level > 0 && success) { diag.DiagnoseRefactor( DiagKindRefactor::lex_unterminated_block_comment, MakeRange(GetPos(pStart), GetPos(pCurrent))); success = false; return {Token(TokenKind::COMMENT, std::string(pStart, pNext), pos, GetPos(pNext)), false}; } return {Token(TokenKind::COMMENT, std::string(pStart, pNext), pos, GetPos(pNext)), true}; } std::pair LexerImpl::ScanComment(const char* pStart, bool allowNewLine) { ReadUTF8Char(); if (currentChar == '*') { return ScanMultiLineComment(pStart, allowNewLine); } else { while ((currentChar != -1) && !IsCurrentCharLineTerminator()) { ReadUTF8Char(); } if (IsCurrentCharLineTerminator()) { Back(); } return {Token(TokenKind::COMMENT, std::string(pStart, pNext), pos, GetPos(pNext)), true}; } } void LexerImpl::ScanCharDiagUnterminated(const char* pStart) { if (success) { diag.DiagnoseRefactor( DiagKindRefactor::lex_unterminated_char_literal, MakeRange(GetPos(pStart), GetPos(pCurrent) + 1)); success = false; } } std::pair LexerImpl::ScanChar(const char* pStart, bool isByteLiteral) { /* part1: process char value between left `r'`(or `r"`) and right `'` (or '"') */ ReadUTF8Char(); auto quote = currentChar; // Skip 2 chars, `r` and (`'` or `"`) constexpr size_t beginOffset = 2; ReadUTF8Char(); if (currentChar == quote || IsCurrentCharLineTerminator()) { return ProcessIllegalCharValue(pStart, beginOffset); } if (currentChar == '\\') { ProcessEscape(pStart, false, isByteLiteral); } /* part2: process and expect right `'` (or `"`) */ ReadUTF8Char(); bool isScanRightSingleQuotationSuccess = true; if (currentChar != quote) { isScanRightSingleQuotationSuccess = ProcessIllegalRightQuotation(pStart, quote); } auto tok = Token(TokenKind::RUNE_LITERAL, std::string(pStart + beginOffset, pCurrent), pos, GetPos(pNext)); tok.isSingleQuote = IsSingleQuote(quote); return {tok, isScanRightSingleQuotationSuccess}; } bool LexerImpl::ProcessIllegalRightQuotation(const char* pStart, int32_t quote) { // can't find right `'` or `"`, following code is error handling. while (currentChar != quote && !IsCurrentCharLineTerminator() && currentChar != -1) { ReadUTF8Char(); } if (currentChar != quote) { // encounter `line terminator` or `end of file` ScanCharDiagUnterminated(pStart); return false; } else if (success) { DiagCharactersOverflow(pStart); success = false; } return true; } std::pair LexerImpl::ProcessIllegalCharValue(const char* pStart, size_t beginOffset) { // currentChar must be one of '\'' , '\r' , '\n' CJC_ASSERT(currentChar == '\'' || IsCurrentCharLineTerminator()); if (currentChar == '\'') { // unexpected `'` if (success) { diag.DiagnoseRefactor(DiagKindRefactor::lex_expected_character_in_char_literal, MakeRange(GetPos(pStart), GetPos(pCurrent + 1))); success = false; } return { Token{TokenKind::RUNE_LITERAL, std::string(pStart + beginOffset, pCurrent), pos, GetPos(pCurrent)}, true}; } if (success) { diag.DiagnoseRefactor(DiagKindRefactor::lex_unterminated_char_literal, MakeRange(GetPos(pStart), GetPos(pCurrent) + Position{0, 0, 1})); success = false; } return {Token{TokenKind::RUNE_LITERAL, std::string(pStart + beginOffset, pCurrent), pos, GetPos(pCurrent)}, false}; } void LexerImpl::ScanSymbolPlus() { ReadUTF8Char(); if (currentChar == '+') { return; } else if (currentChar == '&') { ReadUTF8Char(); if (currentChar == '=') { return; } } else if (currentChar == '=') { return; } Back(); } void LexerImpl::ScanSymbolHyphen() { ReadUTF8Char(); if (currentChar == '-') { return; } else if (currentChar == '=') { return; } else if (currentChar == '&') { ReadUTF8Char(); if (currentChar == '=') { return; } } else if (currentChar == '>') { return; } Back(); } void LexerImpl::ScanSymbolAsterisk() { ReadUTF8Char(); if (currentChar == '*') { ReadUTF8Char(); if (currentChar == '&') { ReadUTF8Char(); if (currentChar == '=') { return; } } else if (currentChar == '=') { return; } } else if (currentChar == '=') { return; } else if (currentChar == '&') { ReadUTF8Char(); if (currentChar == '=') { return; } } Back(); } void LexerImpl::ScanSymbolAmpersand() { ReadUTF8Char(); if (currentChar == '&') { ReadUTF8Char(); if (currentChar == '=') { return; } } else if (currentChar == '=') { return; } Back(); } void LexerImpl::ScanSymbolExclamation() { /// Lexer read a stream of token and classify them into some syntactic /// category. constexpr int nextIndex = 2; if (GetNextChar(0) == 'i' && GetNextChar(1) == 'n' && (GetNextChar(nextIndex) == ' ' || GetNextChar(nextIndex) == '\r' || GetNextChar(nextIndex) == '\n')) { // process '!in' token ReadUTF8Char(); ReadUTF8Char(); return; } ReadUTF8Char(); if (currentChar == '=') { return; } Back(); } void LexerImpl::ScanSymbolLessThan() { ReadUTF8Char(); if (currentChar == '<') { ReadUTF8Char(); if (currentChar == '=') { return; } } else if (currentChar == '=') { return; } else if (currentChar == ':') { return; } else if (currentChar == '-') { return; } Back(); } void LexerImpl::ScanSymbolGreaterThan() { if (splitAmbiguousToken) { return; } ReadUTF8Char(); if (currentChar == '>') { ReadUTF8Char(); if (currentChar == '=') { return; } } else if (currentChar == '=') { return; } Back(); } void LexerImpl::ScanSymbolAt() { ReadUTF8Char(); if (currentChar == '!') { return; } Back(); } void LexerImpl::ScanSymbolEqual() { ReadUTF8Char(); if (currentChar == '=') { return; } else if (currentChar == '>') { return; } Back(); } void LexerImpl::ScanSymbolPercentSign() { ReadUTF8Char(); if (currentChar == '=') { return; } Back(); } void LexerImpl::ScanSymbolVerticalBar() { ReadUTF8Char(); if (currentChar == '|') { ReadUTF8Char(); if (currentChar == '=') { return; } } else if (currentChar == '=') { return; } else if (currentChar == '>') { return; } Back(); } void LexerImpl::ScanSymbolTilde() { ReadUTF8Char(); if (currentChar != '>') { Back(); } } void LexerImpl::ScanSymbolCaret() { if (GetNextChar(0) == '=') { ReadUTF8Char(); } } void LexerImpl::ScanSymbolQuest() { if (splitAmbiguousToken) { return; } if (GetNextChar(0) == '?') { ReadUTF8Char(); } } void LexerImpl::ScanSymbolColon() { ReadUTF8Char(); if (currentChar != ':') { Back(); } } Token LexerImpl::GetSymbolToken(const char* pStart) { TokenKind kind = LookupKeyword(std::string(pStart, pNext)); if (kind == TokenKind::IDENTIFIER) { if (success) { DiagUnknownStartOfToken(GetPos(pStart)); success = false; } return Token(TokenKind::ILLEGAL, std::string(pStart, pNext), pos, GetPos(pNext)); } return Token(kind, std::string(pStart, pNext), pos, GetPos(pNext)); } Token LexerImpl::ScanSymbol(const char* pStart) { static const std::unordered_map SCAN_SYMBOL_STRATEGY = { {'~', &LexerImpl::ScanSymbolTilde}, {'+', &LexerImpl::ScanSymbolPlus}, {'-', &LexerImpl::ScanSymbolHyphen}, {'*', &LexerImpl::ScanSymbolAsterisk}, {'&', &LexerImpl::ScanSymbolAmpersand}, {'^', &LexerImpl::ScanSymbolCaret}, {'!', &LexerImpl::ScanSymbolExclamation}, {'<', &LexerImpl::ScanSymbolLessThan}, {'>', &LexerImpl::ScanSymbolGreaterThan}, {'=', &LexerImpl::ScanSymbolEqual}, {'%', &LexerImpl::ScanSymbolPercentSign}, {'|', &LexerImpl::ScanSymbolVerticalBar}, {'?', &LexerImpl::ScanSymbolQuest}, {'@', &LexerImpl::ScanSymbolAt}, {':', &LexerImpl::ScanSymbolColon}, }; if (currentChar == '$') { ReadUTF8Char(); Token tok(TokenKind::IDENTIFIER); // Identifier or keyword. UTF32 uch = static_cast(currentChar); if (IsCJXIDStart(uch)) { // unicode identifier begin ScanIdentifierOrKeyword(tok, pStart + 1); if (tok.kind != TokenKind::IDENTIFIER) { DiagUnexpectedDollarIdentifier(tok); } return Token(TokenKind::DOLLAR_IDENTIFIER, "$" + tok.Value(), pos, GetPos(pNext)); } if (currentChar == '`') { tok = ScanBackquotedIdentifier(pStart + 1); return Token(TokenKind::DOLLAR_IDENTIFIER, "$" + tok.Value(), pos, GetPos(pNext)); } Back(); } else if (auto iter = SCAN_SYMBOL_STRATEGY.find(static_cast(currentChar)); iter != SCAN_SYMBOL_STRATEGY.end()) { (this->*(iter->second))(); } return GetSymbolToken(pStart); } Token LexerImpl::ScanFromTokens() { if (curToken >= tokens.size()) { // If empty, return END with initialized pos. return Token(TokenKind::END, "", pos, pos); } if (splitAmbiguousToken) { auto itAmb = ambiCombinedTokensDegTable.find(tokens[curToken].kind); if (itAmb != ambiCombinedTokensDegTable.end()) { const auto& [lkind, lval, rkind, rval] = itAmb->second; tokens[curToken].kind = rkind; auto curPos = tokens[curToken].Begin(); auto endPos = tokens[curToken].End(); tokens[curToken].SetValuePos(std::string{rval}, curPos + 1, endPos + 1); return {lkind, std::string{lval}, curPos, endPos}; } } Token t = tokens[curToken]; curToken++; return t; } Token LexerImpl::ScanIllegalSymbol(const char* pStart) { if (success) { DiagIllegalSymbol(pStart); success = false; } return Token(TokenKind::ILLEGAL, std::string(pStart, pNext), pos, GetPos(pNext)); } Token LexerImpl::ScanByteUInt8(const char* pStart) { static const size_t intByteGapLen = 3; // `\u{**}`中`{`和`}`的最大下标距离 auto token = ScanChar(pStart, true).first; // check number of digits in \u auto position = token.Value().find("\\u", 0); if (position != std::string::npos) { auto lCurl = token.Value().find_first_of('{', 0); auto rCurl = token.Value().find_first_of('}', 0); // \u{00} position of { is 2, } is 5, 5 - 2 = 3, so the gap is at most 3 if (lCurl != std::string::npos && rCurl != std::string::npos && rCurl - lCurl > intByteGapLen && success) { diag.DiagnoseRefactor(DiagKindRefactor::lex_too_many_digits, GetPos(pStart + intByteGapLen), token.Value()); success = false; } } auto wString = UTF8ToChar32(token.Value()); if (!wString.empty() && wString[0] > ASCII_BASE && success) { DiagUnrecognizedCharInByte(static_cast(wString[0]), "character byte literal", pStart, {GetPos(pStart + std::string("b'").size()), GetPos(pCurrent)}); success = false; } token.SetValue("b\'" + token.Value() + "\'"); token.kind = TokenKind::RUNE_BYTE_LITERAL; return token; } void LexerImpl::TryRegisterLineOffset() { // establishing the mapping between line number and offset size_t terminateOffset = 0; if (IsCurrentCharLineTerminator()) { terminateOffset = static_cast(pNext - pInputStart); } // avoid to register offsets on same point. if (terminateOffset > lineOffsetsFromBase.back()) { lineOffsetsFromBase.emplace_back(terminateOffset); } } bool LexerImpl::IsCharOrString() const { return (currentChar == 'b' && GetNextChar(0) == '\'') || (currentChar == 'r' && (GetNextChar(0) == '\'' || GetNextChar(0) == '\"')) || currentChar == '#' || currentChar == '\'' || currentChar == '"'; } Token LexerImpl::ScanCharOrString(const char* pStart) { if (currentChar == 'b' && GetNextChar(0) == '\'') { return ScanByteUInt8(pStart); } if (currentChar == 'r' && (GetNextChar(0) == '\'' || GetNextChar(0) == '\"')) { return ScanChar(pStart).first; } if (currentChar == '#') { return ScanMultiLineRawString(pStart).first; } if (currentChar == '\'' || currentChar == '"') { return ScanSingleOrMultiLineString(pStart); } return ScanSymbol(pStart); } Token LexerImpl::ScanBase() { const char* pStart = pNext; // pStart record the first position of a token. success = true; ReadUTF8Char(); pos = GetPos(pStart); if (currentChar == -1) { return Token(TokenKind::END, "", pos, pos); } if (currentChar == '\n') { return Token(TokenKind::NL, "\n", pos, pos + 1); } if (Utils::GetLineTerminatorLength(pCurrent, pInputEnd) == Utils::WINDOWS_LINE_TERMINATOR_LENGTH) { return Token(TokenKind::NL, "\r\n", pos, pos + 1); } if (IsCharOrString()) { return ScanCharOrString(pStart); } if (currentChar == 'J' && GetNextChar(0) == '\"') { return ScanStringOrJString(pStart, false, true).first; } // identifier or keyword if (IsCJXIDStart(static_cast(currentChar))) { Token c{TokenKind::IDENTIFIER}; ScanIdentifierOrKeyword(c, pStart); return c; } if (currentChar == '`') { return ScanBackquotedIdentifier(pStart); } if (isdigit(currentChar) || currentChar == '.') { // number return ScanNumberOrDotPrefixSymbol(pStart); } if (currentChar == '/') { return ScanDivOrComment(pStart); } if (currentChar == '\\' && IsMacroEscape(GetNextChar(0))) { return Token(TokenKind::ILLEGAL, "\\", pos, pos + 1); } return ScanSymbol(pStart); } /// Next function: return the next token /// GetNextChar(0): the next char /// pStart: the first char position of the current token; /// pCurrent: first position of current char. Token LexerImpl::Scan() { Token ret{TokenKind::INTEGER_LITERAL, "", Position(0, 1, 1), Position{0, 1, 1}}; if (!enableScan) { ret = ScanFromTokens(); if (enableCollectTokenStream) { tokenStream.emplace_back(ret); } return ret; } if (pInputStart >= pInputEnd) { ret = Token(TokenKind::END, "", posBase, posBase); } else if (pNext >= pInputEnd) { ret = Token(TokenKind::END, "", GetPos(pNext), GetPos(pNext)); } else { char next; while ((next = GetNextChar(0)) == ' ' || next == '\t' || next == '\f') { pNext++; } ret = ScanBase(); } if (enableCollectTokenStream) { tokenStream.emplace_back(ret); } return ret; } Token LexerImpl::ScanNumberOrDotPrefixSymbol(const char* pStart) { if (currentChar == '.' && (!isdigit(GetNextChar(0)))) { return ScanDotPrefixSymbol(); } return ScanNumber(pStart); } Token LexerImpl::ScanSingleOrMultiLineString(const char* pStart) { success = true; CJC_ASSERT(currentChar == '\'' || currentChar == '"'); if (GetNextChar(0) == currentChar && GetNextChar(1) == currentChar) { return ScanMultiLineString(pStart).first; } return ScanStringOrJString(pStart).first; } Token LexerImpl::ScanDivOrComment(const char* pStart) { if (GetNextChar(0) != '/' && GetNextChar(0) != '*') { if (GetNextChar(0) == '=') { ReadUTF8Char(); return Token(TokenKind::DIV_ASSIGN, "/=", pos, pos + std::string_view{"/="}.size()); } else { return Token(TokenKind::DIV, "/", pos, pos + 1); } } Token tok = ScanComment(pStart).first; // Save the comments content and their positions. comments.emplace_back(tok); return tok; } void LexerImpl::SetResetPoint() { pResetCurrent = pCurrent; pResetNext = pNext; resetLookAheadCache = lookAheadCache; lineResetOffsetsFromBase = static_cast(lineOffsetsFromBase.size()); resetToken = curToken; } void LexerImpl::Reset() { pCurrent = pResetCurrent; pNext = pResetNext; lookAheadCache = resetLookAheadCache; curToken = resetToken; unsigned curLineOffsetsFromBase = static_cast(lineOffsetsFromBase.size()); while (curLineOffsetsFromBase > lineResetOffsetsFromBase && !lineOffsetsFromBase.empty()) { lineOffsetsFromBase.pop_back(); --curLineOffsetsFromBase; } } Token LexerImpl::Next() { Token token{TokenKind::SENTINEL, "", Position(0, 1, 1), Position{0, 1, 1}}; if (lookAheadCache.empty()) { token = Scan(); } else { token = lookAheadCache.front(); lookAheadCache.pop_front(); } CollectToken(token); return token; } const std::list& LexerImpl::LookAhead(size_t num) { if (num <= lookAheadCache.size()) { return lookAheadCache; } while (lookAheadCache.size() < num) { Token token = Scan(); if (token.kind != TokenKind::COMMENT) { // Skip comments. lookAheadCache.push_back(token); } if (token.kind == TokenKind::END) { break; } } return lookAheadCache; } /** reserved num Tokens, * by default, TokenKind::COMMENT is ignored * additionally, e.g skipNewline == false, to make sure num is small or equal than lookAheadCache.size() * if Scan reaching the end of file, lookAheadCache will pad TokenKind::END(s) and eventually num == * lookAheadCache.size() */ void LexerImpl::ReserveToken(size_t num, bool skipNewline) { size_t index = 0; if (skipNewline) { for (auto it = lookAheadCache.begin(); it != lookAheadCache.end() && index < num; it++) { index += static_cast(it->kind != TokenKind::NL); } } else { index = lookAheadCache.size(); } while (index < num) { Token token = Scan(); if (token.kind == TokenKind::COMMENT) { // Skip comments. continue; } lookAheadCache.push_back(token); index += static_cast(!skipNewline || token.kind != TokenKind::NL); } } bool LexerImpl::Seeing(const std::vector::const_iterator& begin, const std::vector::const_iterator& end, bool skipNewline) { CJC_ASSERT(begin <= end); size_t kindSize = static_cast(std::distance(begin, end)); ReserveToken(kindSize + 1, skipNewline); if (!skipNewline) { return std::equal(begin, end, lookAheadCache.begin(), [](const TokenKind kind, const Token& token) { return kind == token.kind; }); } // compare two container auto kindIter = begin; // ReserveToken will ensure lookAheadCache will never smaller than std::distance(begin, end) // so we needn't check lookAheadIter != lookAheadCache.end() for (auto lookAheadIter = lookAheadCache.begin(); kindIter != end; lookAheadIter++) { if (lookAheadIter->kind == TokenKind::NL) { continue; } if (*kindIter != lookAheadIter->kind) { return false; } kindIter++; } return true; } bool LexerImpl::Seeing(const std::vector& kinds, bool skipNewline) { return Seeing(kinds.begin(), kinds.end(), skipNewline); } std::list LexerImpl::LookAheadSkipNL(size_t num) { std::list ret; for (const Token& token : lookAheadCache) { if (token.kind != TokenKind::NL) { ret.push_back(token); } if (ret.size() >= num) { return ret; } } while (ret.size() < num) { Token token = Scan(); if (token.kind == TokenKind::COMMENT) { continue; // Skip comments. } lookAheadCache.push_back(token); if (token.kind != TokenKind::NL) { ret.push_back(token); } if (token.kind == TokenKind::END) { break; } } return ret; } std::vector LexerImpl::GetTokens() { std::vector tks; auto tk = Next(); while (tk.kind != TokenKind::END) { tks.emplace_back(tk); tk = Next(); } return tks; } std::vector LexerImpl::GetCollectTokens() const { return collectTokens; } bool LexerImpl::StartCollectTokens() { if (enableCollect) { return false; } enableCollect = true; collectTokens.clear(); return true; } void LexerImpl::StopCollectTokens(bool bStart) { if (bStart) { enableCollect = false; } } void LexerImpl::CollectToken(const Token& token) { if (!enableCollect) { return; } auto it = std::find_if(collectTokens.begin(), collectTokens.end(), [&token](auto& t) { return t == token; }); if (it == collectTokens.end()) { collectTokens.push_back(token); } } inline void LexerImpl::DiagSmallExpectedDigit(const bool& hasDigit, const char base) { if (!hasDigit && success) { DiagExpectedDigit(base); success = false; } } inline void LexerImpl::CheckIllegalUTF8InStringLiteral(uint32_t byte) { if ((byte >= BYTE_2_FLAG || byte < BYTE_X_FLAG) && success) { diag.DiagnoseRefactor(DiagKindRefactor::lex_illegal_UTF8_encoding_byte, GetPos(pCurrent), ToBinaryString(static_cast(byte))); success = false; } } std::size_t LexerImpl::GetCurrentToken() const { auto tokenNumber = this->curToken; for (auto token : this->lookAheadCache) { if (token.kind != TokenKind::SENTINEL && token.kind != TokenKind::ILLEGAL && token.kind != TokenKind::END) { tokenNumber--; } } return tokenNumber; } cangjie_compiler-1.0.7/src/Lex/LexerDiag.cpp000066400000000000000000000375521510705540100207120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Lexer Diagnostic. */ #include "LexerImpl.h" #include "cangjie/Basic/Display.h" namespace Cangjie { const int ESCAPE_NUM_OF_CHAR_STRING_LITERAL = 12; const int ESCAPE_NUM_OF_CHAR_STRING_BYTE_LITERAL = 11; // ruled out \$ void LexerImpl::DiagUnexpectedDecimalPoint(const char* reasonPoint) { auto baseChar = std::tolower(*reasonPoint); auto prefix = PrefixName(static_cast(baseChar)); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::lex_unexpected_decimal_point, GetPos(pCurrent), prefix); auto r = MakeRange(GetPos(reasonPoint - 1), GetPos(reasonPoint + 1)); builder.AddHint(r, prefix); auto note = SubDiagnostic("only decimal or hexadecimal number can support fractional part"); builder.AddNote(note); auto help = DiagHelp("if you expect a decimal fraction, could remove the prefix"); help.AddSubstitution(r, " "); builder.AddHelp(help); help = DiagHelp("if you expect a hexadecimal fraction, could use hexadecimal prefix"); help.AddSubstitution(r, "0x"); builder.AddHelp(help); } void LexerImpl::DiagExpectedDigit(const char base) { auto name = PrefixName(base); auto begin = GetPos(pCurrent); auto range = MakeRange(begin, begin + Position{0, 0, static_cast(pNext - pCurrent)}); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::lex_expected_digit, range, name, ConvertCurrentChar()); builder.AddMainHintArguments(name); } void LexerImpl::DiagUnexpectedDigit(const int& base, const char* reasonPoint) { auto baseChar = static_cast(std::tolower(*reasonPoint)); static std::unordered_map map = { {'b', "0~1"}, {'o', "0~7"}, {'d', "0~9"}, {'x', "0~9 or a~f"} }; auto name = PrefixName(baseChar); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::lex_unexpected_digit, GetPos(pCurrent), std::string{*pCurrent}, name); char c; if (base != DEC_BASE) { auto r = MakeRange(GetPos(reasonPoint - std::string("0").size()), GetPos(reasonPoint) + Position{0, 0, 1}); builder.AddHint(r, "of this " + std::string{name} + " prefix"); c = baseChar; } else if (baseChar == 'e' || baseChar == 'p') { // Means exponent part. builder.AddHint(GetPos(reasonPoint), "the exponent part base is decimal"); c = 'd'; } else { auto r = MakeRange(GetPos(reasonPoint), GetPos(pCurrent)); builder.AddHint(r, "the default base is decimal"); c = 'd'; } CJC_ASSERT(map.find(c) != map.end()); std::string sub = std::string{name} + " may only contain digit within " + map[c]; auto note = SubDiagnostic(sub); builder.AddNote(note); } void LexerImpl::DiagUnexpectedExponentPart(const char exp, const char prefix, const char* reasonPoint) { auto preName = PrefixName(prefix); if (prefix != 'd' && prefix != 'x') { auto reaRange = MakeRange(GetPos(reasonPoint - std::string("0").size()), GetPos(reasonPoint) + Position{0, 0, 1}); auto builder = diag.DiagnoseRefactor( DiagKindRefactor::lex_unexpected_exponent_part, GetPos(pCurrent), std::string{*pCurrent}, preName); builder.AddHint(reaRange, "of this " + std::string{preName} + " prefix"); auto note = SubDiagnostic("only decimal or hexadecimal number can support exponent part"); builder.AddNote(note); return; } if (exp == 'e' && prefix == 'x') { auto reaRange = MakeRange(GetPos(reasonPoint - std::string("0").size()), GetPos(reasonPoint) + Position{0, 0, 1}); auto builder = diag.DiagnoseRefactor( DiagKindRefactor::lex_unexpected_exponent_part, GetPos(pCurrent), std::string{*pCurrent}, preName); builder.AddHint(reaRange, "of this " + std::string{preName} + " prefix"); auto help = DiagHelp("could try to modify it to hexadecimal exponent type"); help.AddSubstitution(GetPos(pCurrent), "p"); builder.AddHelp(help); return; } if (exp == 'p' && prefix == 'd') { auto reaRange = MakeRange(GetPos(reasonPoint), GetPos(pCurrent)); auto builder = diag.DiagnoseRefactor( DiagKindRefactor::lex_unexpected_exponent_part, GetPos(pCurrent), std::string{*pCurrent}, preName); builder.AddHint(reaRange, "the default base is decimal"); auto help = DiagHelp("could try to modify it to " + std::string{preName} + " exponent type"); help.AddSubstitution(GetPos(pCurrent), "e"); builder.AddHelp(help); } } void LexerImpl::DiagExpectedExponentPart(const char* reasonPoint) { auto start = reasonPoint - std::string("0").size(); Range r = MakeRange(GetPos(start), GetPos(pCurrent)); auto builder = diag.DiagnoseRefactor( DiagKindRefactor::lex_expected_exponent_part, GetPos(pCurrent), std::string{start, pCurrent}); builder.AddHint(r); } void LexerImpl::DiagUnexpectedDollarIdentifier(const Token& t) { bool foundWildcard = t.kind == TokenKind::WILDCARD; std::string foundMsg = (foundWildcard ? "symbol '" : "keyword '") + t.Value() + "'"; auto builder = diag.DiagnoseRefactor(DiagKindRefactor::lex_expected_identifier_after_dollar, t, foundMsg); if (foundWildcard) { builder.diagnostic.mainHint.str = "expected a Unicode XID_Continue after underscore"; return; } auto help = DiagHelp("you could escape keyword as identifier using '`'"); help.AddSubstitution(t, "`" + t.Value() + "`"); builder.AddHelp(help); } void LexerImpl::DiagIllegalSymbol(const char* pStart) { do { ReadUTF8Char(); } while (currentChar > ASCII_BASE); Range r = MakeRange(GetPos(pStart), GetPos(pCurrent)); diag.DiagnoseRefactor(DiagKindRefactor::lex_unrecognized_symbol, r, std::string{pStart, pCurrent}); } void LexerImpl::DiagUnterminatedSingleLineString(const char* pStart, bool isMatchEnd, bool isJString) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::lex_unterminated_single_line_string, MakeRange(GetPos(pStart), GetPos(pCurrent) + Position{0, 0, 1})); if (!interpolations.empty() && interpolations.back() < pStart && !stringStarts.back().second) { auto p = interpolations.back(); builder.AddHint(MakeRange(GetPos(p), GetPos(p + std::string("${").size()))); } if (!isMatchEnd && !isJString) { auto note = SubDiagnostic("consider using multi-line string if you intended to include '\\n' in string"); builder.AddNote(note); } } void LexerImpl::DiagUnterminatedMultiLineString(const char* pStart) { auto builder = diag.DiagnoseRefactor( DiagKindRefactor::lex_unterminated_multi_line_string, MakeRange(GetPos(pStart), GetPos(pCurrent))); if (!interpolations.empty() && interpolations.back() < pStart && stringStarts.back().second) { auto p = interpolations.back(); builder.AddHint(MakeRange(GetPos(p), GetPos(p + std::string("${").size()))); } } void LexerImpl::DiagUnterminatedRawString(const char* pStart) { auto builder = diag.DiagnoseRefactor( DiagKindRefactor::lex_unterminated_raw_string, MakeRange(GetPos(pStart), GetPos(pCurrent))); if (!interpolations.empty() && interpolations.back() < pStart && !stringStarts.back().second) { auto p = interpolations.back(); builder.AddHint(MakeRange(GetPos(p), GetPos(p + std::string("${").size()))); } } void LexerImpl::DiagUnrecognizedEscape(const char* pStart, bool isInString, bool isByteLiteral) { std::string target = isInString ? "string" : "rune"; if (isByteLiteral) { target += " byte"; } if (isInString && isByteLiteral) { target = "byte array"; } auto c = ConvertCurrentChar(); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::lex_unrecognized_escape, MakeRange(GetPos(pCurrent - 1), GetPos(pCurrent) + c.size()), "\\" + c, target); if (*pStart == '"' && *(pStart + 1) == '"') { builder.AddHint(MakeRange(GetPos(pStart), GetPos(pStart + std::string(R"(""")").size())), target); } else { builder.AddHint(GetPos(pStart), target); } // Escape number, there are 12 escapes in string/char literal, 11 escapes in char byte or byte array literal std::string noteMsg = "found " + std::to_string(isByteLiteral ? ESCAPE_NUM_OF_CHAR_STRING_BYTE_LITERAL : ESCAPE_NUM_OF_CHAR_STRING_LITERAL) + " possible escapes: "; for (auto& [f, s] : escapePrintMap) { noteMsg += "'" + s + "', "; } // Extra escapes comparing to normal ASCII. noteMsg += "'\\0', "; noteMsg += "'\\\\', "; noteMsg += "'\\'', "; noteMsg += "'\\\"', "; noteMsg += "'\\u{__}'"; if (!isByteLiteral) { noteMsg += ", '\\$'"; } noteMsg += " in " + target + " literal"; auto note = SubDiagnostic(noteMsg); builder.AddNote(note); } void LexerImpl::DiagUnexpectedIntegerLiteralTypeSuffix( const char* pSuffixStart, const std::string& signednessType, const std::string& suffix) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::lex_illegal_integer_suffix, MakeRange(GetPos(pSuffixStart), GetPos(pSuffixStart + suffix.size() + 1)), signednessType + suffix); auto note = SubDiagnostic( "integer literal type suffix can only be 'u8', 'u16', 'u32', 'u64', 'i8', 'i16', 'i32', 'i64'"); builder.AddNote(note); } void LexerImpl::DiagUnexpectedFloatLiteralTypeSuffix(const char* pSuffixStart, const std::string& suffix) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::lex_illegal_float_suffix, MakeRange(GetPos(pSuffixStart), GetPos(pSuffixStart + suffix.size() + 1)), "f" + suffix); auto note = SubDiagnostic("float literal type suffix can only be 'f16', 'f32', 'f64'"); builder.AddNote(note); } void LexerImpl::DiagExpectedRightBracket(const char* pStart) { const size_t escapeLens = 2; auto builder = diag.DiagnoseRefactor(DiagKindRefactor::lex_expected_right_bracket, GetPos(pCurrent), ConvertCurrentChar()); builder.AddHint(GetPos(pStart + escapeLens)); auto note = SubDiagnostic("unicode escape may contain 8 hexadecimal digits at most"); builder.AddNote(note); } void LexerImpl::DiagExpectedRightBracketOrHexadecimal(const char* pStart) { const size_t escapeLens = 2; auto builder = diag.DiagnoseRefactor( DiagKindRefactor::lex_expected_right_bracket_or_hexadecimal, GetPos(pCurrent), ConvertCurrentChar()); builder.AddHint(MakeRange(GetPos(pStart), GetPos(pStart + escapeLens))); auto note = SubDiagnostic("unicode escape may only contain hexadecimal digits"); builder.AddNote(note); } void LexerImpl::DiagCharactersOverflow(const char* pStart) { auto builder = diag.DiagnoseRefactor( DiagKindRefactor::lex_characters_overflow, MakeRange(GetPos(pStart), GetPos(pCurrent + 1))); auto help = DiagHelp("if you intended to write a string literal, use '\"'"); help.AddSubstitution(MakeRange(GetPos(pStart), GetPos(pCurrent + 1)), "\"" + std::string{pStart + 1 + 1, pCurrent} + "\""); builder.AddHelp(help); } bool LexerImpl::CheckUnicodeSecurity(const int32_t& c) const { // Special areas and format characters bool isControlPictures = (c >= 0x2400 && c <= 0x243F); bool isPrivateUseAreas = (c >= 0xE000 && c <= 0xF8FF) || (c >= 0xF0000 && c <= 0xFFFFD) || (c >= 0x100000 && c <= 0x10FFFD); bool isSpecials = (c >= 0xFFF0 && c <= 0xFFFB); bool isTags = (c >= 0xE0000 && c <= 0xE007F); bool isBidirectionalNeutralFormatting = (c == 0x061C || c == 0x200E || c == 0x200F); bool isBidirectionalGeneralFormatting = (c >= 0x202A && c <= 0x202E) || (c >= 0x2066 && c <= 0x2069); bool isInterlinearAnnotationChar = (c == 0xFFF9 || c == 0xFFFA || c == 0xFFFB); bool isPrefixedFormatControl = ((c >= 0x0600 && c <= 0x0605) || c == 0x06DD || c == 0x070F || c == 0x0890 || c == 0x0891 || c == 0x110BD || c == 0x110CD); bool isEgyptianHieroglyphs = (c >= 0x13430 && c <= 0x13438); bool isBrahmi = (c == 0x1107F || c == 0x094D || c == 0x09CD || c == 0x0A4D || c == 0x0ACD || c == 0x0B4D || c == 0x0BCD || c == 0x0C4D || c == 0x0CCD || c == 0x0D3B || c == 0x0D3C || c == 0x0D4D || c == 0x0DCA || c == 0x0E3A || c == 0x0E4E || c == 0x0EBA || c == 0x1039 || c == 0x1714 || c == 0x1715 || c == 0x1734 || c == 0x17D1 || c == 0x17D2 || c == 0x1A60 || c == 0x1A7A || c == 0x1B44 || c == 0x1BAA || c == 0x1BAB || c == 0x1BF2 || c == 0x1BF3 || c == 0xA806 || c == 0xA82C || c == 0xA8C4 || c == 0xA953 || c == 0xA9C0 || c == 0xAAF6 || c == 0x10A3F || c == 0x11046 || c == 0x11070 || c == 0x110B9 || c == 0x11133 || c == 0x111C0 || c == 0x11235 || c == 0x112EA || c == 0x1134D || c == 0x11442 || c == 0x114C2 || c == 0x115BF || c == 0x1163F || c == 0x116B6 || c == 0x1172B || c == 0x11839 || c == 0x1193D || c == 0x1193E || c == 0x119E0 || c == 0x11A34 || c == 0x11A47 || c == 0x11A99 || c == 0x11C3F || c == 0x11D44 || c == 0x11D45 || c == 0x11D97); bool isHistoricalViramas = (c == 0x0F84 || c == 0x103A || c == 0x193B || c == 0xABED || c == 0x11134); bool isMongolianVariation = (c >= 0x180B && c <= 0x180E); bool isGenericVariation = (c >= 0xFE00 && c <= 0xFE0F) || (c >= 0xE0100 && c <= 0xE01EF); bool isTagCharacters = (c >= 0xE0020 && c <= 0xE007F) || c == 0xE0001 || c == 0x2D7F || c == 0x1680; bool isIdeographic = (c >= 0x2FF0 && c <= 0x2FFB) || c == 0x303E; bool isMusicalFormatControl = (c >= 0x1D173 && c <= 0x1D17A); bool isShorthandFormat = (c >= 0x1BCA0 && c <= 0x1BCA3); bool isDeprecatedAlternate = (c >= 0x206A && c <= 0x206F); bool isOther = (c == 0xFFFC || c == 0xFFFD); bool isSpecialPurposeCharacters = isBidirectionalNeutralFormatting || isBidirectionalGeneralFormatting || isInterlinearAnnotationChar || isPrefixedFormatControl || isEgyptianHieroglyphs || isBrahmi || isHistoricalViramas || isMongolianVariation || isGenericVariation || isTagCharacters || isIdeographic || isMusicalFormatControl || isShorthandFormat || isDeprecatedAlternate || isOther; bool isSecurity = isControlPictures || isPrivateUseAreas || isSpecials || isTags || isSpecialPurposeCharacters; return !isSecurity; } /// Check whether \param ch has an unsecure unicode value. /// This indicates a unicode value that can help trigger security bugs, e.g. a unicode char that looks like a comment /// punctuation when rendered, but actually not. void LexerImpl::CheckUnsecureUnicodeValue(const int32_t& ch) { if (!CheckUnicodeSecurity(ch)) { auto args = ConvertUnicode(ch); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::lex_unsecure_unicode, GetPos(pCurrent), args); builder.AddMainHintArguments(args); success = false; } } void LexerImpl::DiagUnterminatedInterpolation() { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::lex_unterminated_interpolation, MakeRange(GetPos(interpolations.back()), GetPos(pCurrent) + Position{0, 0, 1})); builder.AddHint(GetPos(stringStarts.back().first)); } void LexerImpl::DiagUnknownStartOfToken(const Position curPos) { auto agr = ConvertCurrentChar(); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::lex_unknown_start_of_token, curPos, agr); builder.AddMainHintArguments(agr); } void LexerImpl::DiagUnrecognizedCharInByte(const int32_t& c, const std::string& str, const char* pStart, const std::pair& range) { CJC_ASSERT(c > ASCII_BASE); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::lex_unrecognized_char_in_binary_string, MakeRange(range.first, range.second), ConvertChar(c), str); auto hintPos = GetPos(pStart); builder.AddHint(MakeRange(hintPos, hintPos + std::string("b\"").size()), str); builder.AddNote("character in " + str + " must be ASCII"); } } cangjie_compiler-1.0.7/src/Lex/LexerImpl.cpp000066400000000000000000000045571510705540100207460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements class LexerImpl. */ #include "LexerImpl.h" namespace Cangjie { Token Lexer::Next() { return impl->Next(); } const std::list& Lexer::LookAhead(size_t num) { return impl->LookAhead(num); } bool Lexer::Seeing(const std::vector::const_iterator& begin, const std::vector::const_iterator& end, bool skipNewline) { return impl->Seeing(begin, end, skipNewline); } bool Lexer::Seeing(const std::vector& kinds, bool skipNewline) { return impl->Seeing(kinds, skipNewline); } std::list Lexer::LookAheadSkipNL(size_t num) { return impl->LookAheadSkipNL(num); } std::vector Lexer::GetComments() { return std::move(impl->comments); } const std::vector& Lexer::GetTokenStream() const { return impl->tokenStream; } void Lexer::SetResetPoint() { impl->SetResetPoint(); } void Lexer::Reset() { impl->Reset(); } const Position& Lexer::GetPosBase() const { return impl->posBase; } unsigned Lexer::GetFileID() const { return impl->fileID; } void Lexer::ClearStringParts(const Token& t) { impl->stringPartsMap.erase(t); } void Lexer::SetEHEnabled(bool enabled) const { static const auto EH_KEYWORDS = { TokenKind::PERFORM, TokenKind::RESUME, TokenKind::THROWING, TokenKind::HANDLE }; for (auto keyword : EH_KEYWORDS) { const auto tokenStr = TOKENS[static_cast(keyword)]; if (enabled) { impl->tokenMap[tokenStr] = keyword; } else { impl->tokenMap.erase(tokenStr); } } } const std::vector& Lexer::GetStrParts(const Token& t) { return impl->GetStrParts(t); } std::vector Lexer::GetTokens() { return impl->GetTokens(); } std::vector Lexer::GetCollectTokens() const { return impl->GetCollectTokens(); } bool Lexer::StartCollectTokens() { return impl->StartCollectTokens(); } void Lexer::StopCollectTokens(bool bStart) { impl->StopCollectTokens(bStart); } std::size_t Lexer::GetCurrentToken() const { return impl->GetCurrentToken(); } } // namespace Cangjie cangjie_compiler-1.0.7/src/Lex/LexerImpl.h000066400000000000000000000377101510705540100204100ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares class LexerImpl, the impl class of class Lexer. */ #ifndef CANGJIE_LEX_LEXERIMPL_H #define CANGJIE_LEX_LEXERIMPL_H #include "cangjie/Lex/Lexer.h" #include namespace Cangjie { /// arg pack for creation of LexerImpl struct LexerConfig { unsigned fileID{0}; const Position& posBase{0, 1, 1}; bool collectTokenStream{false}; bool splitAmbiguousToken{true}; }; class LexerImpl final { public: LexerImpl(const std::string& input, DiagnosticEngine& diag, SourceManager& sm, LexerConfig args) : LexerImpl{input, diag, sm.GetSource(args.fileID), std::move(args)} { } LexerImpl(const std::string& input, DiagnosticEngine& diag, Source& s, LexerConfig args) : diag{diag}, fileID{args.fileID}, source{s}, pInputStart{input.c_str()}, pNext{input.c_str()}, pInputEnd{pInputStart + input.length()}, posBase{args.posBase}, splitAmbiguousToken{args.splitAmbiguousToken}, enableCollectTokenStream{args.collectTokenStream} { Init(); } LexerImpl(const std::vector& inputTokens, DiagnosticEngine& diag, SourceManager& sm, LexerConfig args) : diag{diag}, fileID{args.fileID}, source{sm.GetSource(fileID)}, posBase{args.posBase}, splitAmbiguousToken{args.splitAmbiguousToken}, enableScan{false}, enableCollectTokenStream{args.collectTokenStream} { lineResetOffsetsFromBase = 0; for (auto& tk : inputTokens) { if (tk.kind == TokenKind::IDENTIFIER && LookupKeyword(tk.Value()) != TokenKind::IDENTIFIER) { // In macro definition, the user can create an identifier token // or obtain an identifier token from getIdentifier() of libast, // which is actually a keyword token. Change token from identifier to keyword here. Lexer tempLexer(tk.Value(), diag, sm, tk.Begin()); auto tks = tempLexer.GetTokens(); (void)tokens.insert(tokens.end(), tks.begin(), tks.end()); } else { (void)tokens.emplace_back(tk); } } // Initialize pos, which is in the next column to the last token. if (!tokens.empty()) { auto lastToken = tokens.back(); pos = Position{lastToken.Begin().fileID, lastToken.Begin().line, lastToken.Begin().column + 1}; } else { pos = Position{0, 0, 0}; } } // FrontendTool/SourceManager/Parse/Macro/stdlib/unittests. Token Next(); // read and return next token void ReserveToken(size_t num, bool skipNewline); // Parse/unittests. const std::list& LookAhead(size_t num); bool Seeing(const std::vector::const_iterator& begin, const std::vector::const_iterator& end, bool skipNewline = false); bool Seeing(const std::vector& kinds, bool skipNewline = false); // Parse. std::list LookAheadSkipNL(size_t num); std::vector comments; // All Comments. void EnableCollectTokenStream() { enableCollectTokenStream = true; } const std::vector& GetTokenStream() const { return tokenStream; } void SetResetPoint(); void Reset(); Position GetPosBase() const { return posBase; } unsigned int GetFileID() const { return fileID; } void ClearStringParts(const Token& t) { stringPartsMap.erase(t); } // Parse/Macro. const std::vector& GetStrParts(const Token& t); std::vector GetTokens(); std::vector GetCollectTokens() const; bool StartCollectTokens(); void StopCollectTokens(bool bStart); std::size_t GetCurrentToken() const; private: friend class Lexer; std::vector resetLineOffsets{0}; std::map> stringPartsMap; bool IsCurrentCharLineTerminator() const; bool ScanInterpolationString(const char* pStart, bool allowNewLine); bool ScanInterpolationStringLiteralHoleBalancedTextString(); bool ScanInterpolationStringLiteralHoleBalancedText(const char* pStart, char endingChar, bool allowNewline = false); Position GetPos(const char* current); std::pair ProcessStringInterpolation( const char* pStart, Position& beginPos, std::vector& stringParts, bool allowNewLine = false); bool ScanInterpolationStringLiteralHoleComment(bool allowNewline); bool ProcessInterpolationStringLiteralLineBreak(bool allowNewline); size_t CountTerminator(const std::string& str); DiagnosticEngine& diag; // diag for lexer unsigned int fileID{0}; Source& source; const char* pInputStart{nullptr}; // point to begin of input file const char* pNext{nullptr}; // point to the next character, traversing source code const char* pResetNext{nullptr}; const char* pInputEnd{nullptr}; // point to end of input file const char* pCurrent{nullptr}; // point to the current character const char* pResetCurrent{nullptr}; int32_t currentChar{-1}; // currently processing character std::unordered_map tokenMap{}; unsigned lineResetOffsetsFromBase{0}; TokenKind tokenKind{TokenKind::ILLEGAL}; Position pos; Position posBase{0, 1, 1}; // ambiguous tokens like <=, <<, <<=, ??, will be splited for parser case. e.g.(a < b, c >= d) and let a: ??T = 1 bool splitAmbiguousToken = true; const std::unordered_map> ambiCombinedTokensDegTable{ {TokenKind::COALESCING, {TokenKind::QUEST, "?", TokenKind::QUEST, "?"}}, {TokenKind::RSHIFT_ASSIGN, {TokenKind::GT, ">", TokenKind::GE, ">="}}, {TokenKind::RSHIFT, {TokenKind::GT, ">", TokenKind::GT, ">"}}, {TokenKind::GE, {TokenKind::GT, ">", TokenKind::ASSIGN, "="}}, }; /* * Record the offset of the start position of each line in the source code relative to pInputStart. * every two adjacent elements in this vector can be seen as a left closed right open interval pair, * one pair corresponds to one line * * e.g. * `lineOffsetsFromBase == {0, 5}` means the source code have two lines, * and the first line has five chars (including line terminator), the pair can be abbreviated as [0, 5), * 0 is the offset of start of the first line relative to pInputStart, * 5 is the offset of start of the second line relative to pInputStart. * */ std::vector lineOffsetsFromBase{0}; std::list lookAheadCache; std::list resetLookAheadCache; bool enableScan{true}; std::vector tokens; size_t curToken{0}; size_t resetToken{0}; bool enableCollect{false}; bool enableCollectTokenStream{false}; std::vector collectTokens; std::vector tokenStream; mutable bool success{true}; /// Used for diagnostic. mutable std::vector> stringStarts; /// Used for diagnostic, string start position. mutable std::vector interpolations; /// Used for diagnostic, string interpolation position. /// Get an UTF-8 character, skipping spaces. /// \param diagUnsafe when true, issue a diagnostic error on unsafe Unicode values void ReadUTF8Char(); void ReadUTF8CharFromMultiBytes(int32_t& ch); bool CheckUnicodeSecurity(const int32_t& c) const; bool ProcessXdigit(const int& base, bool& hasDigit, const char* reasonPoint); bool ProcessDigits(const int& base, bool& hasDigit, const char* reasonPoint); std::string GetSuffix(const char* pSuffixStart); void ProcessIntegerSuffix(); TokenKind LookupKeyword(const std::string& literal); void ProcessEscape(const char* pStart, bool isInString, bool isByteLiteral); void ProcessUnicodeEscape(); Token ProcessIllegalToken(bool needStringParts, bool multiLine, const char* pStart, bool isJString = false); std::pair ScanStringOrJString(const char* pStart, bool needStringParts = true, bool isJString = false); std::pair ScanMultiLineString(const char* pStart, bool needStringParts = true); void ConsumeNChar(uint32_t n); std::pair ScanMultiLineRawString(const char* pStart); Token ScanFromTokens(); Token ScanIllegalSymbol(const char* pStart); Token ScanDivOrComment(const char* pStart); void ScanCharDiagUnterminated(const char* pStart); std::pair ScanChar(const char* pStart, bool isByteLiteral = false); void ScanSymbolPlus(); void ScanSymbolHyphen(); void ScanSymbolAsterisk(); void ScanSymbolAmpersand(); void ScanSymbolExclamation(); void ScanSymbolLessThan(); void ScanSymbolGreaterThan(); void ScanSymbolAt(); void ScanSymbolEqual(); void ScanSymbolPercentSign(); void ScanSymbolVerticalBar(); void ScanSymbolTilde(); void ScanSymbolCaret(); void ScanSymbolQuest(); void ScanSymbolColon(); Token GetSymbolToken(const char* pStart); Token ScanSymbol(const char* pStart); std::pair ScanComment(const char* pStart, bool allowNewLine = true); std::pair ScanMultiLineComment(const char* pStart, bool allowNewLine); /// Check if there are f32, f64 after a float literal void ProcessFloatSuffix(const char prefix); void ProcessNumberExponentPart(const char prefix, const char* reasonPoint, bool& isFloat); /// Check if there a f32, f64 after a float literal. After that, this float is completed; if there are excessive /// '.' and chars following it without space between, check whether the following chars are possibly valid. /// The valid cases are /// 1.3f32.. -> .. is range operator /// 1.3f32.什么 -> .什么 is a member prop/func access (in this case Float32/Float64 is extended) void ProcessNumberFloatSuffix(const char& prefix, bool isFloat); Token ScanNumber(const char* pStart); Token ScanNumberOrDotPrefixSymbol(const char* pStart); bool ScanNumberIntegerPart(const char* pStart, int& base, char& prefix, bool& hasDigit, const char*& reasonPoint); void ScanNumberDecimalPart(const int& base, const char& prefix, bool& hasDigit, const char* reasonPoint); void ScanNumberExponentPart(const char* reasonPoint); Token ScanDotPrefixSymbol(); Token ScanBackquotedIdentifier(const char* pStart); void ScanIdentifierOrKeyword(Token& res, const char* pStart); Token ScanSingleOrMultiLineString(const char* pStart); bool IsIllegalStartDecimalPart(const char* pStart, const char* pEnd) const; void DiagUnexpectedDecimalPoint(const char* reasonPoint); void DiagExpectedDigit(const char base); inline void DiagSmallExpectedDigit(const bool& hasDigit, const char base); inline void CheckIllegalUTF8InStringLiteral(uint32_t byte); void CheckUnsecureUnicodeValue(const int32_t& ch); void DiagUnexpectedDigit(const int& base, const char* reasonPoint); void DiagUnexpectedExponentPart(const char exp, const char prefix, const char* reasonPoint); void DiagExpectedExponentPart(const char* reasonPoint); void DiagUnexpectedDollarIdentifier(const Token& t); void DiagIllegalSymbol(const char* pStart); void DiagUnterminatedSingleLineString(const char* pStart, bool isMatchEnd, bool isJString = false); void DiagUnterminatedMultiLineString(const char* pStart); void DiagUnterminatedRawString(const char* pStart); void DiagUnrecognizedEscape(const char* pStart, bool isInString, bool isByteLiteral); void DiagUnexpectedIntegerLiteralTypeSuffix( const char* pSuffixStart, const std::string& signednessType, const std::string& suffix); void DiagUnexpectedFloatLiteralTypeSuffix(const char* pSuffixStart, const std::string& suffix); void DiagExpectedRightBracket(const char* pStart); void DiagExpectedRightBracketOrHexadecimal(const char* pStart); void DiagCharactersOverflow(const char* pStart); void DiagUnterminatedInterpolation(); void DiagUnknownStartOfToken(const Position curPos); void DiagUnrecognizedCharInByte( const int32_t& c, const std::string& str, const char* pStart, const std::pair& range); std::string ConvertCurrentChar(); inline bool IsAdjacent(const char* first, const char* second) const { if ((*first == '\r') && (first + 1 < pInputEnd) && *(first + 1) == '\n') { return second - first == BYTE_2_STEP; } return second - first == 1; } void TryRegisterLineOffset(); const char* PrefixName(const char prefix) const; template void Diagnose(DiagKind kind, T... args) { diag.Diagnose(pos, kind, args...); } template void Diagnose(const Position diagPos, DiagKind kind, T... args) { diag.Diagnose(diagPos, kind, args...); } void Init() { for (unsigned char i = 0; i < static_cast(TokenKind::IDENTIFIER); i++) { tokenMap[TOKENS[i]] = static_cast(i); } // @! is added after TokenKind::IDENTIFIER auto atExclIndex = static_cast(TokenKind::AT_EXCL); tokenMap[TOKENS[atExclIndex]] = static_cast(atExclIndex); auto commonIndex = static_cast(TokenKind::COMMON); tokenMap[TOKENS[commonIndex]] = static_cast(commonIndex); auto platformIndex = static_cast(TokenKind::PLATFORM); tokenMap[TOKENS[platformIndex]] = static_cast(platformIndex); auto dcIndex = static_cast(TokenKind::DOUBLE_COLON); tokenMap[TOKENS[dcIndex]] = static_cast(dcIndex); tokenMap["true"] = TokenKind::BOOL_LITERAL; tokenMap["false"] = TokenKind::BOOL_LITERAL; auto ftrIndex = static_cast(TokenKind::FEATURES); tokenMap[TOKENS[ftrIndex]] = TokenKind::FEATURES; } void Back(); bool IsCharOrString() const; Token ScanCharOrString(const char* pStart); Token ScanBase(); Token Scan(); Token ScanByteUInt8(const char* pStart); /** * Lookahead a character. * @param index Start from 0, 0 means lookahead one char. * @return Return the lookahead value, if cross the border, return '\0'. */ char GetNextChar(const unsigned char index) const { if (pNext + index >= pInputEnd) { return '\0'; } else { return pNext[index]; } } bool CheckArraySize(size_t len, int32_t& ch); void CollectToken(const Token& token); std::pair ProcessIllegalCharValue(const char* pStart, size_t beginOffset); /** * Scan a unicode identifier at \p pStart, with first unicode char \p codePoint. \p codePoint may not be a valid * unicode value, issue a diagnostic in that case. */ void ScanUnicodeIdentifierStart(Token& res, unsigned codePoint, const char* pStart); /** * Continue to scan identifier after scanning one ascii id char. \p pStart points to the next identfier char to * scan, which is one unicode character past #pCurrent; #pCurrent points to the start of this identifier. */ void ScanIdentifierContinue(Token& res, const char* pStart); /** * Diagnose illegal unicode using \p currentChar */ void DiagIllegalUnicode(); /** * Try to consume a unicode character. Returns true if unicode character read succeeded, and advance #pCurrent and * #pNext in this case; otherwise, #pCurrent and #pNext is untouched. */ bool TryConsumeIdentifierUTF8Char(); bool ProcessIllegalRightQuotation(const char* pStart, int32_t quote); }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Macro/000077500000000000000000000000001510705540100166375ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Macro/CMakeLists.txt000066400000000000000000000031031510705540100213740ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set(AST_SERIALIZATION_SRC NodeSerialization.cpp ExprSerialization.cpp TokenSerialization.cpp MacroCommon.cpp MacroCall.cpp) add_library(CangjieAstSerialization OBJECT ${AST_SERIALIZATION_SRC}) add_dependencies(CangjieAstSerialization CangjieFlatbuffersHeaders) target_include_directories(CangjieAstSerialization PRIVATE ${FLATBUFFERS_INCLUDE_DIR}) if(CANGJIE_BUILD_CJC) set(MACRO_SRC MacroExpansion.cpp MacroProcess.cpp MacroEvaluation.cpp InvokeUtil.cpp MacroCallResolve.cpp TestEntryConstructor.cpp) list(APPEND MACRO_SRC MacroEvaluationSrv.cpp MacroEvaluationClient.cpp MacroEvalMsgSerializer.cpp) if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) list(APPEND MACRO_SRC MacroEvaluationCJNative.cpp) list(APPEND MACRO_SRC InvokeUtilCJNative.cpp) endif() add_library(CangjieMacro OBJECT ${MACRO_SRC}) add_dependencies(CangjieMacro CangjieFlatbuffersHeaders) target_include_directories(CangjieMacro PRIVATE ${FLATBUFFERS_INCLUDE_DIR}) if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_dependencies(CangjieMacro cjnative) endif() target_include_directories(CangjieMacro PRIVATE ${LLVM_INCLUDE_DIRS}) target_include_directories(CangjieMacro PRIVATE ${BOUNDSCHECK}/include) target_compile_options(CangjieMacro PRIVATE ${CJC_EXTRA_WARNINGS}) endif() cangjie_compiler-1.0.7/src/Macro/ExprSerialization.cpp000066400000000000000000001147641510705540100230340ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /* * @file * * This file implements the NodeWriter. */ #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/Print.h" #include "cangjie/Macro/NodeSerialization.h" #include "cangjie/AST/Symbol.h" using namespace Cangjie; using namespace AST; using namespace NodeSerialization; flatbuffers::Offset NodeWriter::SerializeWildcardExpr(AstExpr expr) { auto wildcardExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(wildcardExpr); auto fbWildcardExpr = NodeFormat::CreateWildcardExpr(builder, fbNodeBase); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_WILDCARD_EXPR, fbWildcardExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeBinaryExpr(AstExpr expr) { auto binaryExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(binaryExpr); auto leftExpr = SerializeExpr(binaryExpr->leftExpr.get()); auto rightExpr = SerializeExpr(binaryExpr->rightExpr.get()); auto operatorPos = FlatPosCreateHelper(binaryExpr->operatorPos); auto fbBinaryExpr = NodeFormat::CreateBinaryExpr( builder, fbNodeBase, leftExpr, rightExpr, static_cast(binaryExpr->op), &operatorPos); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_BINARY_EXPR, fbBinaryExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeIsExpr(AstExpr expr) { auto isExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(isExpr); auto leftExpr = SerializeExpr(isExpr->leftExpr.get()); auto isType = SerializeType(isExpr->isType.get()); auto isPos = FlatPosCreateHelper(isExpr->isPos); auto fbIsExpr = NodeFormat::CreateIsExpr(builder, fbNodeBase, leftExpr, isType, &isPos); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_IS_EXPR, fbIsExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeAsExpr(AstExpr expr) { auto asExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(asExpr); auto leftExpr = SerializeExpr(asExpr->leftExpr.get()); auto asType = SerializeType(asExpr->asType.get()); auto asPos = FlatPosCreateHelper(asExpr->asPos); auto fbAsExpr = NodeFormat::CreateAsExpr(builder, fbNodeBase, leftExpr, asType, &asPos); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_AS_EXPR, fbAsExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeLitConstExpr(AstExpr expr) { auto litConstExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(litConstExpr); auto value = builder.CreateString(litConstExpr->rawString); auto fbLitConstExpr = NodeFormat::CreateLitConstExpr(builder, fbNodeBase, value, static_cast(litConstExpr->kind), static_cast(litConstExpr->delimiterNum), static_cast(litConstExpr->stringKind), static_cast(litConstExpr->isSingleQuote)); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_LIT_CONST_EXPR, fbLitConstExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeUnaryExpr(AstExpr expr) { auto unaryExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(unaryExpr); auto onlyExpr = SerializeExpr(unaryExpr->expr.get()); uint16_t op = static_cast(unaryExpr->op); auto operatorPos = FlatPosCreateHelper(unaryExpr->operatorPos); auto fbUnaryExpr = NodeFormat::CreateUnaryExpr(builder, fbNodeBase, onlyExpr, op, &operatorPos); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_UNARY_EXPR, fbUnaryExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeParenExpr(AstExpr expr) { auto parenExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(parenExpr); auto onlyExpr = SerializeExpr(parenExpr->expr.get()); auto leftParenPos = FlatPosCreateHelper(parenExpr->leftParenPos); auto rightParenPos = FlatPosCreateHelper(parenExpr->rightParenPos); auto fbParenExpr = NodeFormat::CreateParenExpr(builder, fbNodeBase, &leftParenPos, onlyExpr, &rightParenPos); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_PAREN_EXPR, fbParenExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeFuncArg(AstFuncArg funcArg) { if (funcArg == nullptr) { return flatbuffers::Offset(); } auto base = SerializeNodeBase(funcArg); auto name = builder.CreateString(funcArg->name.GetRawText()); auto namePos = FlatPosCreateHelper(funcArg->name.GetRawPos()); auto colonPos = FlatPosCreateHelper(funcArg->colonPos); auto fbExpr = SerializeExpr(funcArg->expr.get()); auto commaPos = FlatPosCreateHelper(funcArg->commaPos); return NodeFormat::CreateFuncArg(builder, base, name, &namePos, &colonPos, fbExpr, &commaPos, funcArg->withInout); } flatbuffers::Offset NodeWriter::SerializeCallExpr(const CallExpr* callExpr) { if (callExpr == nullptr) { return flatbuffers::Offset(); } auto fbNodeBase = SerializeNodeBase(callExpr); auto baseFunc = callExpr->baseFunc.get(); auto fbBaseFunc = SerializeExpr(baseFunc); auto leftParenPos = FlatPosCreateHelper(callExpr->leftParenPos); auto rightParenPos = FlatPosCreateHelper(callExpr->rightParenPos); auto fbArgs = FlatVectorCreateHelper(callExpr->args, &NodeWriter::SerializeFuncArg); return NodeFormat::CreateCallExpr(builder, fbNodeBase, fbBaseFunc, &leftParenPos, fbArgs, &rightParenPos); } flatbuffers::Offset NodeWriter::SerializeCallExpr(const Expr* expr) { auto callExpr = RawStaticCast(expr); auto fbCallExpr = SerializeCallExpr(callExpr); return NodeFormat::CreateExpr( builder, SerializeNodeBase(callExpr), NodeFormat::AnyExpr_CALL_EXPR, fbCallExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeRefExpr(const RefExpr* refExpr) { if (refExpr == nullptr) { return flatbuffers::Offset(); } auto fbNodeBase = SerializeNodeBase(refExpr); auto ref = refExpr->ref; auto identifier = builder.CreateString(ref.identifier.GetRawText()); auto identifierPos = FlatPosCreateHelper(ref.identifier.GetRawPos()); auto fbRef = NodeFormat::CreateReference(builder, identifier, &identifierPos); auto leftAnglePos = FlatPosCreateHelper(refExpr->leftAnglePos); auto fbTypeVec = FlatVectorCreateHelper(refExpr->typeArguments, &NodeWriter::SerializeType); auto rightAnglePos = FlatPosCreateHelper(refExpr->rightAnglePos); return NodeFormat::CreateRefExpr(builder, fbNodeBase, fbRef, &leftAnglePos, fbTypeVec, &rightAnglePos, refExpr->isThis, refExpr->isSuper, refExpr->isQuoteDollar); } flatbuffers::Offset NodeWriter::SerializeRefExpr(const Expr* expr) { auto refExpr = RawStaticCast(expr); auto fbRefExpr = SerializeRefExpr(refExpr); return NodeFormat::CreateExpr(builder, SerializeNodeBase(refExpr), NodeFormat::AnyExpr_REF_EXPR, fbRefExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeBlockExpr(AstExpr expr) { auto block = RawStaticCast(expr); auto fbBlock = SerializeBlock(block); return NodeFormat::CreateExpr(builder, SerializeNodeBase(block), NodeFormat::AnyExpr_BLOCK, fbBlock.Union()); } flatbuffers::Offset NodeWriter::SerializeReturnExpr(AstExpr expr) { auto returnExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(returnExpr); auto returnPos = FlatPosCreateHelper(returnExpr->returnPos); auto fbExpr = SerializeExpr(returnExpr->expr.get()); if (returnExpr->expr->TestAttr(Attribute::COMPILER_ADD)) { fbExpr = flatbuffers::Offset(); } auto fbReturnExpr = NodeFormat::CreateReturnExpr(builder, fbNodeBase, &returnPos, fbExpr); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_RETURN_EXPR, fbReturnExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeDoWhileExpr(AstExpr expr) { auto doWhileExpr = RawStaticCast(expr); auto base = SerializeNodeBase(doWhileExpr); auto doPos = FlatPosCreateHelper(doWhileExpr->doPos); auto body = SerializeBlock(doWhileExpr->body.get()); auto whilePos = FlatPosCreateHelper(doWhileExpr->whilePos); auto leftParenPos = FlatPosCreateHelper(doWhileExpr->leftParenPos); auto condExpr = SerializeExpr(doWhileExpr->condExpr.get()); auto rightParenPos = FlatPosCreateHelper(doWhileExpr->rightParenPos); auto fbDoWhileExpr = NodeFormat::CreateDoWhileExpr(builder, base, &doPos, body, &whilePos, &leftParenPos, condExpr, &rightParenPos); return NodeFormat::CreateExpr(builder, base, NodeFormat::AnyExpr_DO_WHILE_EXPR, fbDoWhileExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeAssignExpr(AstExpr expr) { auto assignExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(assignExpr); auto fbLeftValue = SerializeExpr(assignExpr->leftValue.get()); auto assignOp = static_cast(assignExpr->op); auto assignPos = FlatPosCreateHelper(assignExpr->assignPos); auto fbRightExpr = SerializeExpr(assignExpr->rightExpr.get()); auto fbAssignExpr = NodeFormat::CreateAssignExpr(builder, fbNodeBase, fbLeftValue, assignOp, &assignPos, fbRightExpr); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_ASSIGN_EXPR, fbAssignExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeMemberAccess(AstExpr expr) { auto memberAccess = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(memberAccess); auto fbBaseExpr = SerializeExpr(memberAccess->baseExpr.get()); auto dotPos = FlatPosCreateHelper(memberAccess->dotPos); auto field = builder.CreateString(memberAccess->field.GetRawText()); auto fieldPos = FlatPosCreateHelper(memberAccess->field.GetRawPos()); auto fbTypeArguments = FlatVectorCreateHelper( memberAccess->typeArguments, &NodeWriter::SerializeType); auto leftAnglePos = FlatPosCreateHelper(memberAccess->leftAnglePos); auto rightAnglePos = FlatPosCreateHelper(memberAccess->rightAnglePos); auto fbMemberAccess = NodeFormat::CreateMemberAccess( builder, fbNodeBase, fbBaseExpr, &dotPos, field, &fieldPos, &leftAnglePos, fbTypeArguments, &rightAnglePos); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_MEMBER_ACCESS, fbMemberAccess.Union()); } flatbuffers::Offset NodeWriter::SerializeLetPatternDestructor(AstExpr expr) { auto letExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(letExpr); auto fbPatterns = FlatVectorCreateHelper( letExpr->patterns, &NodeWriter::SerializePattern); auto backarrowPos = FlatPosCreateHelper(letExpr->backarrowPos); auto initializer = SerializeExpr(letExpr->initializer.get()); auto bitOrPosVector = CreatePositionVector(letExpr->orPos); auto fbLetExpr = NodeFormat::CreateLetPatternDestructor(builder, fbNodeBase, fbPatterns, bitOrPosVector, &backarrowPos, initializer); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_LET_PATTERN_DESTRUCTOR, fbLetExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeIfExpr(AstExpr expr) { auto ifExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(ifExpr); auto ifPos = FlatPosCreateHelper(ifExpr->ifPos); auto leftParenPos = FlatPosCreateHelper(ifExpr->leftParenPos); auto fbCondExpr = SerializeExpr(ifExpr->condExpr.get()); auto rightParenPos = FlatPosCreateHelper(ifExpr->rightParenPos); auto fbBody = SerializeBlock(ifExpr->thenBody.get()); auto elsePos = FlatPosCreateHelper(ifExpr->elsePos); flatbuffers::Offset fbElseBody; if (!ifExpr->hasElse) { auto fbIfExpr = NodeFormat::CreateIfExpr(builder, fbNodeBase, &ifPos, fbCondExpr, fbBody, ifExpr->hasElse, &elsePos, fbElseBody, ifExpr->isElseIf, &leftParenPos, &rightParenPos); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_IF_EXPR, fbIfExpr.Union()); } auto fbExpr = SerializeExpr(ifExpr->elseBody.get()); auto fbIfExpr = NodeFormat::CreateIfExpr(builder, fbNodeBase, &ifPos, fbCondExpr, fbBody, ifExpr->hasElse, &elsePos, fbExpr, ifExpr->isElseIf, &leftParenPos, &rightParenPos); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_IF_EXPR, fbIfExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeMatchExpr(AstExpr expr) { auto matchExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(matchExpr); auto leftParenPos = FlatPosCreateHelper(matchExpr->leftParenPos); auto fbSelector = SerializeExpr(matchExpr->selector.get()); auto rightParenPos = FlatPosCreateHelper(matchExpr->rightParenPos); auto leftCurlPos = FlatPosCreateHelper(matchExpr->leftCurlPos); auto fbMatchcases = FlatVectorCreateHelper( matchExpr->matchCases, &NodeWriter::SerializeMatchCase); auto fbMatchcaseother = FlatVectorCreateHelper( matchExpr->matchCaseOthers, &NodeWriter::SerializeMatchCaseOther); auto rightCurlPos = FlatPosCreateHelper(matchExpr->rightCurlPos); auto fbMatchExpr = NodeFormat::CreateMatchExpr(builder, fbNodeBase, matchExpr->matchMode, &leftParenPos, fbSelector, &rightParenPos, &leftCurlPos, fbMatchcases, fbMatchcaseother, &rightCurlPos); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_MATCH_EXPR, fbMatchExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeWhileExpr(AstExpr expr) { auto whileExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(whileExpr); auto whilePos = FlatPosCreateHelper(whileExpr->whilePos); auto leftParenPos = FlatPosCreateHelper(whileExpr->leftParenPos); auto fbCondExpr = SerializeExpr(whileExpr->condExpr.get()); auto rightParenPos = FlatPosCreateHelper(whileExpr->rightParenPos); auto fbBody = SerializeBlock(whileExpr->body.get()); auto fbWhileExpr = NodeFormat::CreateWhileExpr(builder, fbNodeBase, &whilePos, &leftParenPos, fbCondExpr, &rightParenPos, fbBody); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_WHILE_EXPR, fbWhileExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeArrayLit(AstExpr expr) { auto arrayLit = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(arrayLit); auto leftCurlPos = FlatPosCreateHelper(arrayLit->leftSquarePos); auto fbExs = FlatVectorCreateHelper(arrayLit->children, &NodeWriter::SerializeExpr); auto commaPosVector = CreatePositionVector(arrayLit->commaPosVector); auto rightCurlPos = FlatPosCreateHelper(arrayLit->rightSquarePos); auto fbArrayLit = NodeFormat::CreateArrayLit(builder, fbNodeBase, &leftCurlPos, fbExs, commaPosVector, &rightCurlPos); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_ARRAY_LIT, fbArrayLit.Union()); } flatbuffers::Offset NodeWriter::SerializeTupleLit(AstExpr expr) { auto tupleLit = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(tupleLit); auto leftParenPos = FlatPosCreateHelper(tupleLit->leftParenPos); auto fbExs = FlatVectorCreateHelper(tupleLit->children, &NodeWriter::SerializeExpr); auto commaPositions = CreatePositionVector(tupleLit->commaPosVector); auto rightParenPos = FlatPosCreateHelper(tupleLit->rightParenPos); auto fbTupleLit = NodeFormat::CreateTupleLit(builder, fbNodeBase, &leftParenPos, fbExs, commaPositions, &rightParenPos); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_TUPLE_LIT, fbTupleLit.Union()); } flatbuffers::Offset NodeWriter::SerializeSubscriptExpr(AstExpr expr) { auto subscriptExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(subscriptExpr); auto fbBaseExpr = SerializeExpr(subscriptExpr->baseExpr.get()); auto leftSquarePos = FlatPosCreateHelper(subscriptExpr->leftParenPos); auto fbIndexExprs = FlatVectorCreateHelper(subscriptExpr->indexExprs, &NodeWriter::SerializeExpr); auto rightSquarePos = FlatPosCreateHelper(subscriptExpr->rightParenPos); bool isTupleAccess = subscriptExpr->isTupleAccess; auto fbSubscriptExpr = NodeFormat::CreateSubscriptExpr( builder, fbNodeBase, fbBaseExpr, &leftSquarePos, fbIndexExprs, &rightSquarePos, isTupleAccess); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_SUBSCRIPT_EXPR, fbSubscriptExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeRangeExpr(AstExpr expr) { auto rangeExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(rangeExpr); auto fbStartExpr = SerializeExpr(rangeExpr->startExpr.get()); auto rangePos = FlatPosCreateHelper(rangeExpr->rangePos); auto fbStopExpr = SerializeExpr(rangeExpr->stopExpr.get()); auto colonPos = FlatPosCreateHelper(rangeExpr->colonPos); auto fbStepExpr = SerializeExpr(rangeExpr->stepExpr.get()); bool isClosed = rangeExpr->isClosed; auto fbRangeExpr = NodeFormat::CreateRangeExpr( builder, fbNodeBase, fbStartExpr, &rangePos, fbStopExpr, &colonPos, fbStepExpr, isClosed); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_RANGE_EXPR, fbRangeExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeLambdaExpr(const LambdaExpr* lambdaExpr) { if (lambdaExpr == nullptr) { return flatbuffers::Offset(); } auto fbNodeBase = SerializeNodeBase(lambdaExpr); auto fbBody = SerializeFuncBody(lambdaExpr->funcBody.get()); auto mockSupported = lambdaExpr->TestAttr(Attribute::MOCK_SUPPORTED); return NodeFormat::CreateLambdaExpr(builder, fbNodeBase, fbBody, mockSupported); } flatbuffers::Offset NodeWriter::SerializeLambdaExpr(const Expr* expr) { auto lambdaExpr = RawStaticCast(expr); auto fbLambdaExpr = SerializeLambdaExpr(lambdaExpr); return NodeFormat::CreateExpr( builder, SerializeNodeBase(lambdaExpr), NodeFormat::AnyExpr_LAMBDA_EXPR, fbLambdaExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeSpawnExpr(AstExpr expr) { auto spawnExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(spawnExpr); auto spawnPos = FlatPosCreateHelper(spawnExpr->spawnPos); auto taskExpr = SerializeExpr(spawnExpr->task.get()); auto hasArg = (spawnExpr->arg.get() != nullptr); auto spawnArgExpr = SerializeExpr(spawnExpr->arg.get()); auto leftParenPos = FlatPosCreateHelper(spawnExpr->leftParenPos); auto rightParenPos = FlatPosCreateHelper(spawnExpr->rightParenPos); auto fbSpawnExpr = NodeFormat::CreateSpawnExpr( builder, fbNodeBase, &spawnPos, taskExpr, hasArg, spawnArgExpr, &leftParenPos, &rightParenPos); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_SPAWN_EXPR, fbSpawnExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeSynchronizedExpr(AstExpr expr) { auto synchronizedExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(synchronizedExpr); auto syncPos = FlatPosCreateHelper(synchronizedExpr->syncPos); auto leftParenPos = FlatPosCreateHelper(synchronizedExpr->leftParenPos); auto mutexExpr = SerializeExpr(synchronizedExpr->mutex.get()); auto rightParenPos = FlatPosCreateHelper(synchronizedExpr->rightParenPos); auto body = SerializeBlock(synchronizedExpr->body.get()); auto fbSynchronizedExpr = NodeFormat::CreateSynchronizedExpr( builder, fbNodeBase, &syncPos, &leftParenPos, mutexExpr, &rightParenPos, body); return NodeFormat::CreateExpr( builder, fbNodeBase, NodeFormat::AnyExpr_SYNCHRONIZED_EXPR, fbSynchronizedExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeTrailingClosureExpr(AstExpr expr) { auto type = NodeFormat::AnyExpr_TRAILING_CLOSURE_EXPR; auto trailingClosureExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(trailingClosureExpr); auto leftLambdaPos = FlatPosCreateHelper(trailingClosureExpr->leftLambda); auto fbExpr = SerializeExpr(trailingClosureExpr->expr.get()); auto fbLambdaExpr = SerializeLambdaExpr(trailingClosureExpr->lambda.get()); auto rightLambdaPos = FlatPosCreateHelper(trailingClosureExpr->rightLambda); auto fbTrailingClosureExpr = NodeFormat::CreateTrailingClosureExpr( builder, fbNodeBase, &leftLambdaPos, fbExpr, fbLambdaExpr, &rightLambdaPos); return NodeFormat::CreateExpr(builder, fbNodeBase, type, fbTrailingClosureExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeTypeConvExpr(AstExpr expr) { auto type = NodeFormat::AnyExpr_TYPE_CONV_EXPR; auto typeConvExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(typeConvExpr); auto fbPrimitiveType = SerializeType(typeConvExpr->type.get()); auto leftParenPos = FlatPosCreateHelper(typeConvExpr->leftParenPos); auto fbExpr = SerializeExpr(typeConvExpr->expr.get()); auto rightParenPos = FlatPosCreateHelper(typeConvExpr->rightParenPos); auto fbTypeConvExpr = NodeFormat::CreateTypeConvExpr(builder, fbNodeBase, fbPrimitiveType, &leftParenPos, fbExpr, &rightParenPos); return NodeFormat::CreateExpr(builder, fbNodeBase, type, fbTypeConvExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeTryExpr(AstExpr expr) { auto tryExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(tryExpr); auto fbLParenPos = FlatPosCreateHelper(tryExpr->lParen); auto fbResource = FlatVectorCreateHelper( tryExpr->resourceSpec, &NodeWriter::SerializeVarDecl); auto fbRParenPos = FlatPosCreateHelper(tryExpr->rParen); auto fbCommaPos = CreatePositionVector(tryExpr->resourceSpecCommaPos); auto fbTryBlock = SerializeBlock(tryExpr->tryBlock.get()); auto fbCatchPos = CreatePositionVector(tryExpr->catchPosVector); auto fbCatchLParenPos = CreatePositionVector(tryExpr->catchLParenPosVector); auto fbCatchRParenPos = CreatePositionVector(tryExpr->catchRParenPosVector); auto fbCatchBlocks = FlatVectorCreateHelper(tryExpr->catchBlocks, &NodeWriter::SerializeBlock); auto fbCatchPatterns = FlatVectorCreateHelper( tryExpr->catchPatterns, &NodeWriter::SerializePattern); std::vector> vecHandlers; for (auto& handler : tryExpr->handlers) { auto fbPos = FlatPosCreateHelper(handler.pos); auto fbCommandPattern = SerializePattern(handler.commandPattern.get()); auto fbHandleBlock = SerializeBlock(handler.block.get()); auto fbHanlder = NodeFormat::CreateHandler(builder, &fbPos, fbCommandPattern, fbHandleBlock); vecHandlers.push_back(fbHanlder); } auto fbHandlers = builder.CreateVector(vecHandlers); auto finallyPos = FlatPosCreateHelper(tryExpr->finallyPos); auto fbFinallyBlock = SerializeBlock(tryExpr->finallyBlock.get()); auto fbTryExpr = NodeFormat::CreateTryExpr(builder, fbNodeBase, fbResource, tryExpr->isDesugaredFromTryWithResources, fbTryBlock, fbCatchBlocks, fbCatchPatterns, &finallyPos, fbFinallyBlock, &fbLParenPos, &fbRParenPos, fbCommaPos, fbCatchPos, fbCatchLParenPos, fbCatchRParenPos, fbHandlers); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_TRY_EXPR, fbTryExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeThrowExpr(AstExpr expr) { auto throwExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(throwExpr); auto fbExpr = SerializeExpr(throwExpr->expr.get()); auto fbThrowExpr = NodeFormat::CreateThrowExpr(builder, fbNodeBase, fbExpr); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_THROW_EXPR, fbThrowExpr.Union()); } flatbuffers::Offset NodeWriter::SerializePerformExpr(AstExpr expr) { auto performExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(performExpr); auto fbExpr = SerializeExpr(performExpr->expr.get()); auto fbPerformExpr = NodeFormat::CreatePerformExpr(builder, fbNodeBase, fbExpr); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_PERFORM_EXPR, fbPerformExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeResumeExpr(AstExpr expr) { auto resumeExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(resumeExpr); auto withPos = FlatPosCreateHelper(resumeExpr->withPos); auto withExpr = SerializeExpr(resumeExpr->withExpr); auto throwingPos = FlatPosCreateHelper(resumeExpr->throwingPos); auto throwingExpr = SerializeExpr(resumeExpr->throwingExpr); auto fbResumeExpr = NodeFormat::CreateResumeExpr(builder, fbNodeBase, &withPos, withExpr, &throwingPos, throwingExpr); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_RESUME_EXPR, fbResumeExpr.Union()); } flatbuffers::Offset NodeWriter::SerializePrimitiveTypeExpr(AstExpr expr) { auto primitiveTypeExpr = RawStaticCast(expr); auto fbTypeBase = SerializeNodeBase(primitiveTypeExpr); auto typeKind = static_cast(primitiveTypeExpr->typeKind); auto fbPrimTypeExpr = NodeFormat::CreatePrimitiveTypeExpr(builder, fbTypeBase, typeKind); return NodeFormat::CreateExpr(builder, fbTypeBase, NodeFormat::AnyExpr_PRIMITIVE_TYPE_EXPR, fbPrimTypeExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeForInExpr(AstExpr expr) { auto forinExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(forinExpr); auto leftParenPos = FlatPosCreateHelper(forinExpr->leftParenPos); auto fbPattern = SerializePattern(forinExpr->pattern.get()); auto inPos = FlatPosCreateHelper(forinExpr->inPos); auto fbInExpr = SerializeExpr(forinExpr->inExpression.get()); auto rightParenPos = FlatPosCreateHelper(forinExpr->rightParenPos); auto ifPos = FlatPosCreateHelper(forinExpr->wherePos); auto fbPatternGuard = SerializeExpr(forinExpr->patternGuard.get()); auto fbBody = SerializeBlock(forinExpr->body.get()); auto fbForInExpr = NodeFormat::CreateForInExpr(builder, fbNodeBase, &leftParenPos, fbPattern, &inPos, fbInExpr, &rightParenPos, &ifPos, fbPatternGuard, fbBody); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_FOR_IN_EXPR, fbForInExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeNodeBase(AstNode node) { if (node == nullptr) { return flatbuffers::Offset(); } // NodeBase is the attrs every Node inherits from auto beginPos = FlatPosCreateHelper(node->begin); auto endPos = FlatPosCreateHelper(node->end); auto str = Cangjie::AST::ASTKIND_TO_STRING_MAP[node->astKind]; auto astKind = builder.CreateString(str); return NodeFormat::CreateNodeBase(builder, &beginPos, &endPos, astKind); } flatbuffers::Offset NodeWriter::SerializeJumpExpr(AstExpr expr) { auto jumpExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(jumpExpr); auto isBreak = jumpExpr->isBreak; auto fbJumpExpr = NodeFormat::CreateJumpExpr(builder, fbNodeBase, isBreak); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_JUMP_EXPR, fbJumpExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeIncOrDecExpr(AstExpr expr) { auto incOrDecExpr = RawStaticCast(expr); auto base = SerializeNodeBase(incOrDecExpr); uint16_t op = static_cast(incOrDecExpr->op); auto operatorPos = FlatPosCreateHelper(incOrDecExpr->operatorPos); auto expr0 = SerializeExpr(incOrDecExpr->expr.get()); auto fbIncOrDecExpr = NodeFormat::CreateIncOrDecExpr(builder, base, op, &operatorPos, expr0); return NodeFormat::CreateExpr(builder, base, NodeFormat::AnyExpr_INC_OR_DEC_EXPR, fbIncOrDecExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeOptionalExpr(AstExpr expr) { auto optionalExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(optionalExpr); auto baseExpr = SerializeExpr(optionalExpr->baseExpr.get()); auto questPos = FlatPosCreateHelper(optionalExpr->questPos); auto fbOptionalExpr = NodeFormat::CreateOptionalExpr(builder, fbNodeBase, baseExpr, &questPos); return NodeFormat::CreateExpr(builder, fbNodeBase, NodeFormat::AnyExpr_OPTIONAL_EXPR, fbOptionalExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeOptionalChainExpr(AstExpr expr) { auto optionalChainExpr = RawStaticCast(expr); auto fbNodeBase = SerializeNodeBase(optionalChainExpr); auto optexpr = SerializeExpr(optionalChainExpr->expr.get()); auto fbOptionalChainExpr = NodeFormat::CreateOptionalChainExpr(builder, fbNodeBase, optexpr); return NodeFormat::CreateExpr( builder, fbNodeBase, NodeFormat::AnyExpr_OPTIONAL_CHAIN_EXPR, fbOptionalChainExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeTokenPart(AstExpr expr) { auto tokenPart = RawStaticCast(expr); auto vecToken = TokensVectorCreateHelper(tokenPart->tokens); auto fbTokens = builder.CreateVector(vecToken); auto fbTokenPart = NodeFormat::CreateTokenPart(builder, fbTokens); return NodeFormat::CreateExpr( builder, SerializeNodeBase(tokenPart), NodeFormat::AnyExpr_TOKEN_PART, fbTokenPart.Union()); } flatbuffers::Offset NodeWriter::SerializeQuoteExpr(AstExpr expr) { auto quoteExpr = RawStaticCast(expr); auto base = SerializeNodeBase(quoteExpr); auto leftParenPos = FlatPosCreateHelper(quoteExpr->leftParenPos); auto rightParenPos = FlatPosCreateHelper(quoteExpr->rightParenPos); std::vector> vecExpr; for (auto& child : quoteExpr->exprs) { vecExpr.push_back(SerializeExpr(child.get())); } auto fbExprs = builder.CreateVector(vecExpr); auto fbQuoteExpr = NodeFormat::CreateQuoteExpr(builder, base, &leftParenPos, fbExprs, &rightParenPos); return NodeFormat::CreateExpr(builder, base, NodeFormat::AnyExpr_QUOTE_EXPR, fbQuoteExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeMacroExpandExpr(AstExpr expr) { auto macroExpandExpr = RawStaticCast(expr); auto base = SerializeNodeBase(macroExpandExpr); auto invocation = MacroInvocationCreateHelper(macroExpandExpr->invocation); auto identifier = builder.CreateString(macroExpandExpr->identifier.Val()); auto identifierPos = FlatPosCreateHelper(macroExpandExpr->identifier.Begin()); auto annotationVec = FlatVectorCreateHelper( macroExpandExpr->annotations, &NodeWriter::SerializeAnnotation); std::vector> vecModifier; auto modifiersVec = SortModifierByPos(macroExpandExpr->modifiers); for (auto& mod : modifiersVec) { auto fbMod = SerializeModifier(mod); vecModifier.push_back(fbMod); } auto fbModVec = builder.CreateVector(vecModifier); auto fbMacroExpandExpr = NodeFormat::CreateMacroExpandExpr( builder, base, invocation, identifier, &identifierPos, annotationVec, fbModVec); return NodeFormat::CreateExpr(builder, base, NodeFormat::AnyExpr_MACRO_EXPAND_EXPR, fbMacroExpandExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeArrayExpr(AstExpr expr) { auto arrayExpr = RawStaticCast(expr); auto base = SerializeNodeBase(arrayExpr); auto type = SerializeType(arrayExpr->type.get()); auto leftParenPos = FlatPosCreateHelper(arrayExpr->leftParenPos); auto args = FlatVectorCreateHelper( arrayExpr->args, &NodeWriter::SerializeFuncArg); auto rightParenPos = FlatPosCreateHelper(arrayExpr->rightParenPos); auto isValueArray = arrayExpr->isValueArray; auto fbArrayExpr = NodeFormat::CreateArrayExpr(builder, base, type, &leftParenPos, args, &rightParenPos, isValueArray); return NodeFormat::CreateExpr(builder, base, NodeFormat::AnyExpr_ARRAY_EXPR, fbArrayExpr.Union()); } flatbuffers::Offset NodeWriter::SerializeExpr(AstExpr expr) { if (expr == nullptr) { return flatbuffers::Offset(); } static std::unordered_map> serializeExprMap = { {ASTKind::WILDCARD_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeWildcardExpr(expr); }}, {ASTKind::BINARY_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeBinaryExpr(expr); }}, {ASTKind::LIT_CONST_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeLitConstExpr(expr); }}, {ASTKind::UNARY_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeUnaryExpr(expr); }}, {ASTKind::PAREN_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeParenExpr(expr); }}, {ASTKind::CALL_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeCallExpr(expr); }}, {ASTKind::REF_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeRefExpr(expr); }}, {ASTKind::RETURN_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeReturnExpr(expr); }}, {ASTKind::ASSIGN_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeAssignExpr(expr); }}, {ASTKind::MEMBER_ACCESS, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeMemberAccess(expr); }}, {ASTKind::IF_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeIfExpr(expr); }}, {ASTKind::BLOCK, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeBlockExpr(expr); }}, {ASTKind::LAMBDA_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeLambdaExpr(expr); }}, {ASTKind::TYPE_CONV_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeTypeConvExpr(expr); }}, {ASTKind::FOR_IN_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeForInExpr(expr); }}, {ASTKind::ARRAY_LIT, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeArrayLit(expr); }}, {ASTKind::TUPLE_LIT, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeTupleLit(expr); }}, {ASTKind::SUBSCRIPT_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeSubscriptExpr(expr); }}, {ASTKind::RANGE_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeRangeExpr(expr); }}, {ASTKind::MATCH_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeMatchExpr(expr); }}, {ASTKind::TRY_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeTryExpr(expr); }}, {ASTKind::THROW_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeThrowExpr(expr); }}, {ASTKind::PERFORM_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializePerformExpr(expr); }}, {ASTKind::RESUME_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeResumeExpr(expr); }}, {ASTKind::JUMP_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeJumpExpr(expr); }}, {ASTKind::WHILE_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeWhileExpr(expr); }}, {ASTKind::DO_WHILE_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeDoWhileExpr(expr); }}, {ASTKind::INC_OR_DEC_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeIncOrDecExpr(expr); }}, {ASTKind::TOKEN_PART, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeTokenPart(expr); }}, {ASTKind::QUOTE_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeQuoteExpr(expr); }}, {ASTKind::IS_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeIsExpr(expr); }}, {ASTKind::AS_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeAsExpr(expr); }}, {ASTKind::SPAWN_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeSpawnExpr(expr); }}, {ASTKind::SYNCHRONIZED_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeSynchronizedExpr(expr); }}, {ASTKind::OPTIONAL_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeOptionalExpr(expr); }}, {ASTKind::OPTIONAL_CHAIN_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeOptionalChainExpr(expr); }}, {ASTKind::TRAIL_CLOSURE_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeTrailingClosureExpr(expr); }}, {ASTKind::PRIMITIVE_TYPE_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializePrimitiveTypeExpr(expr); }}, {ASTKind::LET_PATTERN_DESTRUCTOR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeLetPatternDestructor(expr); }}, {ASTKind::MACRO_EXPAND_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeMacroExpandExpr(expr); }}, {ASTKind::ARRAY_EXPR, [](NodeWriter& nw, AstExpr expr) { return nw.SerializeArrayExpr(expr); }}, }; // Match ReplaceExpr func. auto serializeFunc = serializeExprMap.find(expr->astKind); if (serializeFunc != serializeExprMap.end()) { return serializeFunc->second(*this, expr); } Errorln("Expr Not Supported in Libast Yet\n"); return flatbuffers::Offset(); } cangjie_compiler-1.0.7/src/Macro/InvokeUtil.cpp000066400000000000000000000063641510705540100214450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/Macro/InvokeUtil.h" #include "cangjie/Basic/Print.h" #ifdef _WIN32 #include #endif namespace Cangjie { using namespace Utils; using namespace InvokeRuntime; #ifdef _WIN32 #ifdef UNICODE #define LoadLibrary LoadLibraryW #else #define LoadLibrary LoadLibraryA #endif HANDLE InvokeRuntime::OpenSymbolTable(const std::string& libPath) { HANDLE handle = LoadLibrary(libPath.c_str()); // Judge load dynamic lib correctly or not. if (!handle) { Errorln("could not load the dynamic library: ", libPath); } return handle; } #elif defined(__linux__) || defined(__APPLE__) HANDLE InvokeRuntime::OpenSymbolTable(const std::string& libPath, int dlopenMode) { HANDLE handle = nullptr; auto realpathRes = realpath(libPath.c_str(), nullptr); if (!realpathRes) { Errorln("could not get realpath of library: ", libPath); return handle; } handle = dlopen(realpathRes, dlopenMode); free(realpathRes); // Judge load dynamic lib correctly or not. if (!handle) { Errorln("could not load the dynamic library: ", libPath); Errorln("error info is: ", dlerror()); } return handle; } #endif HANDLE InvokeRuntime::GetMethod(HANDLE handle, const char* name) { #ifdef _WIN32 return (HANDLE)GetProcAddress((HMODULE)handle, name); #elif defined(__linux__) || defined(__APPLE__) return dlsym(handle, name); #else CJC_ASSERT(false); return nullptr; #endif } int InvokeRuntime::CloseSymbolTable(HANDLE handle) { int retCode = -1; #ifdef _WIN32 if (FreeLibrary(reinterpret_cast(handle))) { retCode = 0; } #elif defined(__linux__) || defined(__APPLE__) retCode = dlclose(handle); #endif return retCode; } RuntimeInit& RuntimeInit::GetInstance() { static RuntimeInit runtimeInit; return runtimeInit; } bool RuntimeInit::InitRuntime(const std::string& runtimeLibPath, InvokeRuntime::RuntimeInitArg initArgs) { std::unique_lock lock(mutex); if (!initRuntime) { handle = InvokeRuntime::OpenSymbolTable(runtimeLibPath); if (handle) { bool ret = InvokeRuntime::PrepareRuntime(handle, initArgs); if (!ret) { return false; } } bool ret = InitRuntimeMethod(); if (!ret) { return false; } initRuntime = true; } return true; } void RuntimeInit::CloseRuntime() { if (handle != nullptr) { // if PrepareRuntime failed, initRuntime is false, no need to FinishRuntime. if (initRuntime) { InvokeRuntime::FinishRuntime(handle); initRuntime = false; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND InvokeRuntime::CloseSymbolTable(handle); #endif handle = nullptr; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // close macro dynamic library CloseMacroDynamicLibrary(); #endif } MacroProcMsger& MacroProcMsger::GetInstance() { static MacroProcMsger macProcMsger; return macProcMsger; } }cangjie_compiler-1.0.7/src/Macro/InvokeUtilCJNative.cpp000066400000000000000000000166641510705540100230350ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/Macro/InvokeUtil.h" #include "cangjie/Basic/Print.h" #include "cangjie/Macro/InvokeConfig.h" #include "cangjie/Utils/Utils.h" #include "cangjie/Utils/StdUtils.h" namespace Cangjie { using namespace Utils; using namespace InvokeRuntime; namespace { const static std::string G_CJ_RUNTIME_INIT = "InitCJRuntime"; const static std::string G_CJ_RUNTIME_FINI = "FiniCJRuntime"; const static std::string G_CJ_NEW_TASK_FROM_C = "RunCJTask"; const static std::string G_RELEASE_HANDLE_FROM_C = "ReleaseHandle"; using CangjieInitFromC = int64_t (*)(ConfigParam*); using CangjieFiniFromC = int64_t (*)(); std::vector g_openedLibHandles; std::mutex g_openedLibMutex; #ifdef _WIN32 // environment variable names in windows is case-insensitive, so they all uppercase in frontend global option const static std::string G_CJHEAPSIZE = "CJHEAPSIZE"; const static std::string G_CJSTACKSIZE = "CJSTACKSIZE"; #else const static std::string G_CJHEAPSIZE = "cjHeapSize"; const static std::string G_CJSTACKSIZE = "cjStackSize"; #endif const static size_t UNIT_LEN = 2; // supports "kb", "mb", "gb". const static size_t KB = 1024; const static size_t MB = KB * KB; const static size_t G_MIN_HEAP_SIZE = 4UL * KB; // min: 4MB. const static size_t G_MIN_STACK_SIZE = 64; // min: 64KB. const static size_t G_MAX_STACK_SIZE = 1UL * MB; // max: 1GB. size_t GetSizeFromEnv(std::string& str) { (void)str.erase(std::remove(str.begin(), str.end(), ' '), str.end()); size_t len = str.length(); // The last two characters are units, such as "kb". if (len <= UNIT_LEN) { return SIZE_MAX; } // Split size and unit. auto unit = str.substr(len - UNIT_LEN, UNIT_LEN); (void)std::transform(unit.begin(), unit.end(), unit.begin(), ::tolower); // unit must be kb mb or gb if (unit != "kb" && unit != "mb" && unit != "gb") { return SIZE_MAX; } int32_t iRes = 0; auto num = str.substr(0, len - UNIT_LEN); if (auto r = Stoi(num)) { // str must be a number iRes = *r; // number must > 0 if (iRes <= 0) { return 0; } } else { return SIZE_MAX; } size_t tempSize = static_cast(iRes); if (unit == "kb") { return tempSize; } else if (unit == "mb") { // unit: 1024 * 1KB = 1MB return tempSize * KB; } else if (unit == "gb") { // unit: 1024 * 1024 * 1KB = 1GB return tempSize * MB; } return SIZE_MAX; } /** * Get heap size from environment variable. * The unit must be added when configuring "cjHeapSize", it supports "kb", "mb", "gb". * Valid heap size must >= 4MB. * for example: * export cjHeapSize = 16GB */ size_t GetHeapSizeFromEnv(std::unordered_map& envs) { if (envs.find(G_CJHEAPSIZE) == envs.end()) { return HEAP_SIZE; } auto heapSize = GetSizeFromEnv(envs.at(G_CJHEAPSIZE)); if (heapSize == SIZE_MAX) { Warningln("unsupported cjHeapSize for macro, using 1GB as default size"); return HEAP_SIZE; } if (heapSize < G_MIN_HEAP_SIZE) { Warningln("unsupported cjHeapSize for macro, must >= 4MB, using 1GB as default size"); return HEAP_SIZE; } return heapSize; } /** * Get stack size from environment variable. * The unit must be added when configuring "cjStackSize", it supports "kb", "mb", "gb". * Valid stack size range is [64KB, 1GB]. * for example: * export cjStackSize = 128kb */ size_t GetStackSizeFromEnv(std::unordered_map& envs) { if (envs.find(G_CJSTACKSIZE) == envs.end()) { return CO_STACK_SIZE; } auto stackSize = GetSizeFromEnv(envs.at(G_CJSTACKSIZE)); if (stackSize == SIZE_MAX) { Warningln("unsupported cjStackSize for macro, using 4MB as default size"); return CO_STACK_SIZE; } if (stackSize < G_MIN_STACK_SIZE || stackSize > G_MAX_STACK_SIZE) { Warningln("unsupported cjStackSize for macro, the valid range is [64KB, 1GB], using 4MB as default size"); return CO_STACK_SIZE; } return stackSize; } } // namespace void InvokeRuntime::SetOpenedLibHandles(HANDLE handle) { std::unique_lock lock(g_openedLibMutex); (void)g_openedLibHandles.emplace_back(handle); } std::vector InvokeRuntime::GetOpenedLibHandles() { std::unique_lock lock(g_openedLibMutex); return g_openedLibHandles; } void InvokeRuntime::ClearOpenedLibHandles() { std::unique_lock lock(g_openedLibMutex); g_openedLibHandles.clear(); } int64_t InvokeRuntime::CallRuntime( const HANDLE handle, const std::string& method, std::unordered_map& envs) { auto runtimeFunc = reinterpret_cast(GetMethod(handle, method.c_str())); if (runtimeFunc == nullptr) { Errorln("could not find runtime method: ", method); return 1; } auto heapSize = GetHeapSizeFromEnv(envs); HeapParam hParam = HeapParam(REGION_SIZE, heapSize, EXEMPTION_THRESHOLD, HEAP_UTILIZATION, HEAP_GROWTH, ALLOCATION_RATE, ALLOCATION_WAIT_TIME); GCParam gcParam = GCParam(GC_THRESHOLD, GARBAGE_THRESHOLD, GC_INTERVAL, BACKUP_GC_INTERNAL, GC_THREADS); auto logParam = LogParam(RTLogLevel::RTLOG_FATAL); ConcurrencyParam cParam = ConcurrencyParam(STACK_SIZE, GetStackSizeFromEnv(envs), PROCESSOR_NUM); ConfigParam param = {hParam, gcParam, logParam, cParam}; return runtimeFunc(¶m); } bool InvokeRuntime::PrepareRuntime(const HANDLE handle, std::unordered_map& initArgs) { auto callInit = CallRuntime(handle, G_CJ_RUNTIME_INIT, initArgs); if (callInit != 0) { Errorln("macro expansion failed because of runtime initiate failed. "); return false; } return true; } void InvokeRuntime::FinishRuntime(const HANDLE handle) { auto method = GetMethod(handle, G_CJ_RUNTIME_FINI.c_str()); auto runtimeFunc = reinterpret_cast(method); auto finiInit = runtimeFunc(); if (finiInit != 0) { Errorln("runtime finish failed: "); return; } } bool RuntimeInit::InitRuntimeMethod() { runtimeMethodFunc = InvokeRuntime::GetMethod(handle, std::string(G_CJ_NEW_TASK_FROM_C).c_str()); runtimeReleaseFunc = InvokeRuntime::GetMethod(handle, std::string(G_RELEASE_HANDLE_FROM_C).c_str()); if (runtimeMethodFunc == nullptr || runtimeReleaseFunc == nullptr) { auto errorInfo = runtimeMethodFunc == nullptr ? G_CJ_NEW_TASK_FROM_C : G_RELEASE_HANDLE_FROM_C; Errorln("could not find the create task method: ", errorInfo); return false; } return true; } void RuntimeInit::CloseMacroDynamicLibrary() { int retCode = -1; for (auto& openedLib : InvokeRuntime::GetOpenedLibHandles()) { retCode = InvokeRuntime::CloseSymbolTable(openedLib); if (retCode != 0) { Errorln("close macro related dynamic library failed: "); return; } } InvokeRuntime::ClearOpenedLibHandles(); } } // namespace Cangjiecangjie_compiler-1.0.7/src/Macro/MacroCall.cpp000066400000000000000000000203431510705540100212020ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /* * @file * * This file implements the MacroCall. */ #include "cangjie/Utils/ConstantsUtils.h" #include "cangjie/Macro/TokenSerialization.h" #include "cangjie/Basic/DiagnosticEmitter.h" #include "cangjie/Macro/MacroCall.h" namespace Cangjie { MacroCall::MacroCall(Ptr node): node(node), begin(node->begin), end(node->end) { if (node->astKind == AST::ASTKind::MACRO_EXPAND_EXPR) { kind = MacroKind::EXPR_KIND; auto macExpr = RawStaticCast(node); invocation = &(macExpr->invocation); modifiers = &macExpr->modifiers; } else if (node->astKind == AST::ASTKind::MACRO_EXPAND_DECL) { kind = MacroKind::DECL_KIND; auto macDecl = RawStaticCast(node); invocation = &(macDecl->invocation); modifiers = &macDecl->modifiers; } else if (node->astKind == AST::ASTKind::MACRO_EXPAND_PARAM) { kind = MacroKind::PARAM_KIND; auto macDecl = RawStaticCast(node); invocation = &(macDecl->invocation); modifiers = &macDecl->modifiers; } else { kind = MacroKind::UNINITIALIZED; Errorln("Illegal initialization of MacroCall"); } } std::vector> MacroCall::GetAnnotations() const { std::vector> ret; if (kind == MacroKind::UNINITIALIZED) { return ret; } if (kind == MacroKind::EXPR_KIND) { auto macExpr = RawStaticCast(node); ret = std::move(macExpr->annotations); } else if (kind == MacroKind::DECL_KIND) { auto macDecl = RawStaticCast(node); ret = std::move(macDecl->annotations); } else { auto macDecl = RawStaticCast(node); ret = std::move(macDecl->annotations); } return ret; } extern "C" { /* * Recursively searches for the target parent node of the current macroCall node. */ bool MacroCall::TraverseParentMacroNode(MacroCall* mcNode, const std::string& parentName) { if (!mcNode) { return false; } if (mcNode && !mcNode->parentMacroCall) { return false; } if (mcNode->parentMacroCall->GetFullName() == parentName) { return true; } return TraverseParentMacroNode(mcNode->parentMacroCall, parentName); } /* * Check whether the target node exists. */ bool MacroCall::CheckParentContext(const char* parentStr, bool report) { std::string parentName(parentStr); bool success{false}; if (this->parentNames.empty()) { success = TraverseParentMacroNode(this, parentName); } else { // For child process in lsp. for (auto& parent : parentNames) { if (parent == parentName) { success = true; break; } } } if (!success && report) { if (this->ci) { (void)this->ci->diag.Diagnose( this->GetBeginPos(), DiagKind::macro_assert_parent_context_failed, this->GetFullName(), parentName); } else { // Save parent name if assert failed, and nodify macrocall result with parent name to main process. this->assertParents.emplace_back(parentName); } } return success; } void MacroCall::DiagReport(const int level, const Range range, const char *message, const char* hint) const { DiagKindRefactor diagKind; if (level == static_cast(DiagSeverity::DS_ERROR)) { diagKind = DiagKindRefactor::parse_diag_error; } else if (level == static_cast(DiagSeverity::DS_WARNING)) { diagKind = DiagKindRefactor::parse_diag_warning; } else { return; } auto builder = this->ci->diag.DiagnoseRefactor(diagKind, range, message); builder.AddMainHintArguments(hint); } /* * Record the information sent by the inner macro. */ void MacroCall::SetItemMacroContext(char* key, void* value, uint8_t type) { (void)this->recordMacroInfo.emplace_back(key); void* valueP = value; if (static_cast(type) == ItemKind::TKS) { auto tokens = TokenSerialization::GetTokensFromBytes(reinterpret_cast(value)); valueP = TokenSerialization::GetTokensBytesWithHead(tokens); free(value); } (void)this->recordMacroInfo.emplace_back(valueP); uint8_t* typeP = (uint8_t*)malloc(sizeof(uint8_t)); if (typeP == nullptr) { Errorln("Macro With Context Memory Allocation Failed."); (void)this->recordMacroInfo.emplace_back(typeP); return; } *typeP = type; (void)this->recordMacroInfo.emplace_back(typeP); } void MacroCall::TraverseMacroNode( MacroCall* macroNode, const std::string& childName, std::vector>& macroInfos) { if (!macroNode) { return; } if (!macroNode->childMessages.empty()) { // For child process in lsp. for (auto& cm : macroNode->childMessages) { if (cm.childName != childName) { continue; } std::vector itemInfo; for (auto& it : cm.items) { itemInfo.emplace_back(it.key.data()); if (it.kind == ItemKind::STRING) { itemInfo.emplace_back(it.sValue.data()); // string value } else if (it.kind == ItemKind::INT) { itemInfo.emplace_back(&it.iValue); // int value } else if (it.kind == ItemKind::BOOL) { itemInfo.emplace_back(&it.bValue); // bool value } else if (it.kind == ItemKind::TKS) { itemInfo.emplace_back(TokenSerialization::GetTokensBytesWithHead(it.tValue)); // tokens value } uint8_t* typeP = (uint8_t*)malloc(sizeof(uint8_t)); if (typeP == nullptr) { Errorln("Macro With Context Memory Allocation Failed."); itemInfo.emplace_back(typeP); continue; } *typeP = static_cast(it.kind); itemInfo.emplace_back(typeP); } (void)macroInfos.emplace_back(itemInfo); } return; } if (macroNode->GetFullName() == childName) { (void)macroInfos.emplace_back(macroNode->recordMacroInfo); } for (MacroCall* child : macroNode->children) { TraverseMacroNode(child, childName, macroInfos); } } /* * Obtaining information from the child nodes: * For example, * @Outter(@Inner()... @Inner()... @Inner) * The data structure wil be created for 'Outter' as follows. * | Inner | Inner | Inner | * | * |(k,v,t)..|(k,v,t)..|(k,v,t)..| */ void*** MacroCall::GetChildMessagesFromMacroContext(const char* childrenStr) { this->macroInfoVec.clear(); if (this->children.empty() && this->childMessages.empty()) { return nullptr; } std::string childName(childrenStr); this->TraverseMacroNode(this, childName, this->macroInfoVec); if (this->macroInfoVec.empty()) { return nullptr; } void*** rawPtr = static_cast(malloc((this->macroInfoVec.size() + 1) * sizeof(void**))); if (rawPtr == nullptr) { Errorln("Macro With Context Memory Allocation Failed."); return rawPtr; } void** vecPtr; for (size_t i = 0; i < this->macroInfoVec.size(); i++) { vecPtr = static_cast(malloc((this->macroInfoVec[i].size() + 1) * sizeof(void*))); if (vecPtr == nullptr) { Errorln("Macro With Context Memory Allocation Failed."); return rawPtr; } for (size_t j = 0; j <= this->macroInfoVec[i].size(); j++) { if (j == this->macroInfoVec[i].size()) { // Null pointer is required to identify different segments when interoperate with C. vecPtr[j] = nullptr; break; } vecPtr[j] = this->macroInfoVec[i][j]; } rawPtr[i] = vecPtr; } rawPtr[this->macroInfoVec.size()] = nullptr; return rawPtr; } } }cangjie_compiler-1.0.7/src/Macro/MacroCallResolve.cpp000066400000000000000000000230731510705540100225450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /* * @file * * This file implements the API ResolveMacroCall. */ #include "cangjie/Macro/MacroCall.h" #include "cangjie/Utils/ConstantsUtils.h" #include "cangjie/Macro/TokenSerialization.h" #include "cangjie/Basic/DiagnosticEmitter.h" namespace { using namespace Cangjie; using namespace AST; /* Collect parentName for assertParentContext */ void CollectParentMacroName(MacroCall& mc) { auto pmc = mc.parentMacroCall; if (!pmc) { return; } if (!pmc->parentNames.empty()) { mc.parentNames.insert(mc.parentNames.end(), pmc->parentNames.begin(), pmc->parentNames.end()); } mc.parentNames.emplace_back(pmc->GetFullName()); return; } bool InvokeMethod(MacroCall& macCall, CompilerInstance* ci) { if (ci->invocation.globalOptions.enableMacroInLSP) { // Collect parent name for MacroContext in child process. CollectParentMacroName(macCall); } return macCall.FindMacroDefMethod(ci); } void FindMacroDefPkg(MacroCall& macCall, CompilerInstance* ci) { auto macroDefFunc = macCall.GetDefinition(); auto importedMacroPackages = ci->importManager.GetImportedStdMacroPackages(); auto foundStdMacroPkg = std::find_if(importedMacroPackages.begin(), importedMacroPackages.end(), [¯oDefFunc](const std::string packageName) { return packageName == macroDefFunc->fullPackageName; }); if (foundStdMacroPkg != importedMacroPackages.end()) { auto basePath = FileUtil::JoinPath( FileUtil::GetDirPath(ci->invocation.globalOptions.executablePath), "../runtime/lib"); auto libName = "lib" + FileUtil::ConvertPackageNameToLibCangjieBaseFormat(*foundStdMacroPkg) + LIB_SUFFIX; macCall.libPath = FileUtil::JoinPath( FileUtil::JoinPath(basePath, ci->invocation.globalOptions.GetCangjieLibHostPathName()), libName); } else { auto libName = "lib-macro_" + macroDefFunc->fullPackageName + LIB_SUFFIX; auto names = Utils::SplitQualifiedName(macroDefFunc->fullPackageName); auto fileName = names.front() + DIR_SEPARATOR + libName; macCall.libPath = FileUtil::FindFileByName(fileName, ci->importManager.GetSearchPath()).value_or(""); if (macCall.libPath.empty()) { // Temporarily, find file indirectly when the package is root package. macCall.libPath = FileUtil::FindFileByName(libName, ci->importManager.GetSearchPath()).value_or(""); } } } } namespace Cangjie { bool MacroCall::GetAllDeclsForMacroName(const std::string ¯oName, std::vector>& decls) { auto file = node->curFile; CJC_NULLPTR_CHECK(file); if (macroName.find(".") != std::string::npos) { // We may have alias for package name: import p0.* as p1.* , p1 is the alias for p0. std::string pkgNameMaybeAlias = macroName.substr(0, macroName.rfind(".")); auto [packageDecl, isConflicted] = ci->importManager.GetImportedPackageDecl(node, pkgNameMaybeAlias); if (packageDecl == nullptr) { (void)ci->diag.Diagnose(begin, DiagKind::macro_undefined_pkg_name, pkgNameMaybeAlias); return false; } else if (isConflicted) { (void)ci->diag.Diagnose(begin, DiagKind::sema_package_name_conflict, pkgNameMaybeAlias); return false; } auto foundDecls = ci->importManager.GetPackageMembersByName(*packageDecl->srcPackage, invocation->identifier); decls.insert(decls.end(), foundDecls.begin(), foundDecls.end()); } else { decls = ci->importManager.GetImportedDeclsByName(*file, invocation->identifier); } return true; } bool MacroCall::GetValidFuncDecl(std::vector>& decls) { std::vector> fds; for (auto& dl : decls) { if (!(dl->TestAttr(Attribute::MACRO_FUNC)) || dl->astKind != ASTKind::FUNC_DECL) { if (invocation->hasParenthesis) { (void)ci->diag.Diagnose(begin, DiagKind::macro_expect_macro_definition, invocation->identifier); return false; } } // If macro decl is in non-macro package, we should report error in this function, otherwise it will have // some asserts in interpreter. if (!(dl->curFile && dl->curFile->curPackage->isMacroPackage)) { continue; } fds.push_back(RawStaticCast(dl)); } if (fds.empty()) { if (invocation->hasParenthesis && !invocation->IsIfAvailable()) { auto pos = invocation->macroNamePos; auto content = ci->diag.GetSourceManager().GetContentBetween(pos, pos + invocation->identifier.length()); if (content == invocation->identifier) { (void)ci->diag.Diagnose(begin, DiagKind::macro_undeclared_identifier, invocation->identifier); } } return false; } if (fds.size() == 1 && fds[0]->funcBody->paramLists.size() > 0) { auto argsSize = fds[0]->funcBody->paramLists[0]->params.size(); // FuncParam if (HasAttribute()) { // Make sure fd has two input arguments: macro M(attr: Tokens, input: Tokens) if (argsSize != MACRO_ATTR_ARGS) { (void)ci->diag.Diagnose(begin, DiagKind::macro_expect_attributed_macro, invocation->identifier); return false; } } else { if (argsSize != MACRO_COMMON_ARGS) { (void)ci->diag.Diagnose(begin, DiagKind::macro_expect_plain_macro, invocation->identifier); return false; } } this->BindDefinition(fds[0]); return true; } if (fds.size() == MACRO_DEF_NUM && fds[0]->funcBody->paramLists.size() > 0 && fds[1]->funcBody->paramLists.size() > 0) { auto argsSize1 = fds[0]->funcBody->paramLists[0]->params.size(); auto argsSize2 = fds[1]->funcBody->paramLists[0]->params.size(); if (argsSize1 == argsSize2) { // args should have the size of {1, 2} (void)ci->diag.Diagnose(begin, DiagKind::macro_ambiguous_match, invocation->identifier); return false; } if (HasAttribute()) { this->BindDefinition(argsSize1 == MACRO_ATTR_ARGS ? fds[0] : fds[1]); } else { this->BindDefinition(argsSize1 == MACRO_COMMON_ARGS ? fds[0] : fds[1]); } return true; } return true; } bool MacroCall::BindDefinition(const std::string ¯oName) { if (definition) { return true; } std::vector> decls; // get all decls match macroName bool ret = GetAllDeclsForMacroName(macroName, decls); if (!ret) { return ret; } // filter to get all valid Macro FuncDecl ret = GetValidFuncDecl(decls); if (!ret) { return ret; } if (definition) { return true; } (void)ci->diag.Diagnose(begin, DiagKind::macro_ambiguous_match, invocation->identifier); return false; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool MacroCall::FindMacroDefMethod(CompilerInstance* instance) { if (libPath.empty()) { // The following code will be deleted. for (auto& handle : InvokeRuntime::GetOpenedLibHandles()) { // Check whether this dynamic lib contains macro definition or not. invokeFunc = InvokeRuntime::GetMethod(handle, methodName.c_str()); if (invokeFunc) { return true; } } return false; } // Without macrolib option. auto handle = InvokeRuntime::OpenSymbolTable(libPath); if (handle == nullptr) { status = MacroEvalStatus::FAIL; (void)instance->diag.Diagnose(DiagKind::can_not_open_macro_library, libPath); return false; } invokeFunc = InvokeRuntime::GetMethod(handle, methodName.c_str()); InvokeRuntime::SetOpenedLibHandles(handle); return invokeFunc != nullptr; } #endif bool MacroCall::BindInvokeFunc() { CJC_ASSERT(definition); this->methodName = Utils::GetMacroFuncName(definition->fullPackageName, invocation->HasAttr(), definition->identifier); this->packageName = definition->fullPackageName; if (ci->invocation.globalOptions.macroLib.empty()) { (void)FindMacroDefPkg(*this, ci); return InvokeMethod(*this, ci); } return InvokeMethod(*this, ci); } bool MacroCall::ResolveMacroCall(CompilerInstance* instance) { CJC_NULLPTR_CHECK(instance); this->ci = instance; std::string macCallFullName = invocation->fullName; if (Utils::In(macCallFullName, BUILD_IN_MACROS)) { return true; } // CustomAnnotation is valid on typealias decl, but MacroExpansion is not if (auto expr = DynamicCast(node)) { if (Is(expr->invocation.decl)) { return false; } } // @IfAvailable is not a macro, don't resolve if (auto expr = DynamicCast(node)) { if (expr->invocation.IsIfAvailable()) { return false; } } bool findMacroDef = this->BindDefinition(macCallFullName); if (!findMacroDef) { return findMacroDef; } bool bindInvokeFunc = this->BindInvokeFunc(); if (!bindInvokeFunc) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND (void)instance->diag.Diagnose(GetBeginPos(), DiagKind::macro_cannot_find_method, GetFullName()); status = MacroEvalStatus::FAIL; #endif return false; } return true; } }cangjie_compiler-1.0.7/src/Macro/MacroCommon.cpp000066400000000000000000000244611510705540100215640ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements macro utility api. */ #include "cangjie/Macro/MacroCommon.h" #include #include #include #include "cangjie/Basic/Print.h" #include "cangjie/Basic/Utils.h" #include "cangjie/Lex/Lexer.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Macro/TokenSerialization.h" #include "cangjie/Basic/DiagnosticEmitter.h" #ifdef _WIN32 #include #include #include #elif defined(__linux__) || defined(__APPLE__) #include #include #endif using namespace Cangjie; using namespace AST; namespace { const int SPACE_NUM = 4; bool IsPreEscapeBackslash(const std::string& value) { char backslash = '\\'; auto count = 0; for (auto ch = value.rbegin(); ch != value.rend(); ++ch) { if (*ch == backslash) { count++; } else { break; } } auto mod2 = 2; // count mod 2 is odd means the previous symbol is escaped backslash return (count % mod2 == 0) ? true : false; } std::string ProcessQuotaMarksForSingle(const std::string& value) { bool inDollar{false}; auto lCurlCnt = 0; // Quotation marks that are in interpolation do not need to be escaped, like "${"abc"}". std::string ret; for (const char ch : value) { if (!ret.empty() && ret.back() == '$' && ch == '{') { inDollar = true; } if (inDollar && ch == '{') { lCurlCnt++; } if (inDollar && ch == '}') { if (lCurlCnt > 0) { lCurlCnt--; } if (lCurlCnt == 0) { inDollar = false; } } if (inDollar) { ret += ch; continue; } // Special marks that are not in interpolation need to be escaped once. if (ch == '\"' && (ret.back() != '\\' || IsPreEscapeBackslash(ret))) { ret += "\\\""; } else if (ch == '\r' && ret.back() != '\\') { ret += "\\r"; } else if (ch == '\n' && ret.back() != '\\') { ret += "\\n"; } else { ret += ch; } } return ret; } std::string RepeatString(const std::string str, int times) { std::string ret; for (int i = 0; i < times; i++) { ret += str; } return ret; } std::string GetMultiStringValue(const Token& tk) { // """\nabc""" return R"(""")" + Utils::GetLineTerminator() + ProcessQuotaMarks(tk.Value()) + R"(""")"; } std::string GetMultiRawStringValue(const Token& tk) { auto value = tk.Value(); auto delimiter = RepeatString("#", static_cast(tk.delimiterNum)); // ###"xxx"#yyy"### return delimiter + "\"" + value + "\"" + delimiter; } bool AddSpace(TokenVector& line, size_t column) { if (line.empty() || column >= line.size() - 1) { return false; } auto curToken = line[column]; auto nextToken = line[column + 1]; return CheckAddSpace(curToken, nextToken); } } // namespace namespace Cangjie { bool CheckAddSpace(Token curToken, Token nextToken) { // Add no space after current token. static std::set noSpaceKinds1{TokenKind::DOT, TokenKind::QUEST, TokenKind::DOLLAR, TokenKind::LPAREN, TokenKind::LSQUARE, TokenKind::AT, TokenKind::AT_EXCL, TokenKind::ILLEGAL, TokenKind::NL}; if (noSpaceKinds1.find(curToken.kind) != noSpaceKinds1.end()) { return false; } // Add no space before next token. static std::set noSpaceKinds2{TokenKind::DOT, TokenKind::COLON, TokenKind::COMMA, TokenKind::SEMI, TokenKind::QUEST, TokenKind::LPAREN, TokenKind::RPAREN, TokenKind::LSQUARE, TokenKind::RSQUARE, TokenKind::NL, TokenKind::END}; if (noSpaceKinds2.find(nextToken.kind) != noSpaceKinds2.end()) { return false; } // Add no space between current token and next token. static std::set> tkSet = { std::pair(TokenKind::GT, TokenKind::GT), std::pair(TokenKind::GT, TokenKind::ASSIGN), std::pair(TokenKind::QUEST, TokenKind::QUEST), std::pair(TokenKind::LPAREN, TokenKind::RPAREN), std::pair(TokenKind::LSQUARE, TokenKind::RSQUARE), std::pair(TokenKind::IDENTIFIER, TokenKind::NOT), std::pair(TokenKind::BITNOT, TokenKind::INIT)}; return tkSet.find(std::pair(curToken.kind, nextToken.kind)) == tkSet.end(); } } // namespace Cangjie bool MacroFormatter::SeeCurlyBracket(const TokenVector& lineOfTk, const TokenKind& tk) const { TokenKind other; TokenVector temp = lineOfTk; if (tk == TokenKind::RCURL) { other = TokenKind::LCURL; } else if (tk == TokenKind::LCURL) { other = TokenKind::RCURL; // Look for bracket from backwards. std::reverse(temp.begin(), temp.end()); } else { return false; } // Return true if see a single bracket in lineOfTk. for (auto itr = temp.begin(); itr != temp.end(); ++itr) { if ((*itr).kind == other) { return false; } if ((*itr).kind == tk) { return true; } } return false; } void MacroFormatter::PushIntoLines() { TokenVector tmp; for (const auto& tok : input) { tmp.push_back(tok); if (tok.kind == TokenKind::NL) { lines.push_back(tmp); tmp.clear(); } } if (!tmp.empty()) { lines.push_back(tmp); tmp.clear(); } return; } void MacroFormatter::LinesToString(bool hasComment) { // Determine the initial indentation. if (lines.empty()) { return; } auto genIndent = [](int times) { std::string tab = " "; return RepeatString(tab, times); }; auto initialIndent = (offset - 1) / SPACE_NUM; auto indentation = 0; for (size_t i = 0; i < lines.size(); i++) { if (lines[i].size() == 0) { continue; } if (hasComment && lines[i].size() > 1) { if (i == 0) { retStr += lines[i][0].Value(); } else { retStr += genIndent(initialIndent) + lines[i][0].Value(); } (void)lines[i].erase(lines[i].begin()); } auto lineStr = LineToString(lines[i]); if (i == 0) { retStr += genIndent(indentation) + lineStr; continue; } // Determine the indentation. if (!lines[i - 1].empty() && SeeCurlyBracket(lines[i - 1], TokenKind::LCURL)) { indentation += 1; // Right ident when see "{" last line. } if (!lines[i].empty() && SeeCurlyBracket(lines[i], TokenKind::RCURL)) { indentation -= 1; // Left ident when see "}" this line. } retStr += genIndent(indentation) + lineStr; } } const std::string MacroFormatter::Produce(bool hasComment) { PushIntoLines(); LinesToString(hasComment); return retStr; } namespace Cangjie { std::string LineToString(TokenVector& line) { // Maybe more cases to deal with. std::string ret; auto size = line.size(); for (size_t i = 0; i < size; i++) { auto blank = AddSpace(line, i) ? " " : ""; auto token = line[i]; std::string quote = token.isSingleQuote ? "\'" : "\""; if (token.kind == TokenKind::STRING_LITERAL) { // For case like: let s = "hello world\n" ret += quote + ProcessQuotaMarksForSingle(token.Value()) + quote + blank; } else if (token.kind == TokenKind::JSTRING_LITERAL) { ret += "J" + quote + ProcessQuotaMarksForSingle(token.Value()) + quote + blank; } else if (token.kind == TokenKind::RUNE_LITERAL) { // For case: let c = '\'' ret += ((token == "\'") ? "r\'\\'\'" : "r\'" + token.Value() + "\'") + blank; } else if (token.kind == TokenKind::MULTILINE_STRING) { ret += GetMultiStringValue(token) + blank; } else if (token.kind == TokenKind::MULTILINE_RAW_STRING) { ret += GetMultiRawStringValue(token) + blank; } else if (token.kind == TokenKind::NL) { ret += Utils::GetLineTerminator(); } else { ret += line[i].Value() + blank; } } return ret; } /** * Use Lexer to get the tokens from the string. */ std::vector GetTokensFromString(const std::string& source, DiagnosticEngine& diag, const Position pos) { if (pos == INVALID_POSITION) { Lexer lexer(source, diag, diag.GetSourceManager()); return lexer.GetTokens(); } Lexer lexer(pos.fileID, source, diag, diag.GetSourceManager(), pos); return lexer.GetTokens(); } bool IsCurFile(const SourceManager& sm, const Token& tk, unsigned int fileID) { if (tk.kind == TokenKind::NL || tk.kind == TokenKind::COMMENT) { return true; } if (fileID != 0 && fileID != tk.Begin().fileID) { return false; } auto begin = tk.Begin(); auto end = tk.End(); auto content = sm.GetContentBetween(begin, end); // For case like: content is "\"name\"" and tk.Value() is "name". if (tk == content || content == "\"" + tk.Value() + "\"") { return true; } // For case like: content is "r's'" and tk.value is "s". if (tk.kind == TokenKind::RUNE_LITERAL && content == "r\'" + tk.Value() + "\'") { return true; } // For case like: content is "###"abc#xyz"###" and tk.Value() is "abc#xyz". if (tk.kind == TokenKind::MULTILINE_RAW_STRING && content == GetMultiRawStringValue(tk)) { return true; } // For case like: content is """\nabc""" if (tk.kind == TokenKind::MULTILINE_STRING && content == GetMultiStringValue(tk)) { return true; } return false; } /** * MacroExpand failed with return TokenKind::ILLEGAL, this function is used to check it. */ bool MacroExpandFailed(const std::vector& retTokens) { if (retTokens.size() == 1 && retTokens[0].kind == TokenKind::ILLEGAL) { return true; } return false; } } cangjie_compiler-1.0.7/src/Macro/MacroEvalMsgSerializer.cpp000066400000000000000000000461421510705540100237240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Macro evaluation messages serializer for macro evaluation. */ #include "cangjie/Macro/MacroEvalMsgSerializer.h" #include "cangjie/Macro/TokenSerialization.h" using namespace MacroMsgFormat; using namespace flatbuffers; using namespace Cangjie; namespace { static const int MACRO_SETITEM_INFO_NUM = 3; static auto CreateTokenVec(FlatBufferBuilder& builder, const std::vector& tks) { std::vector> offsetVec; for (auto& tk : tks) { auto tbegin = MacroMsgFormat::Position(tk.Begin().fileID, tk.Begin().line, tk.Begin().column); auto tend = MacroMsgFormat::Position(tk.End().fileID, tk.End().line, tk.End().column); offsetVec.push_back(CreateToken( builder, static_cast(tk.kind), builder.CreateString(tk.Value()), &tbegin, &tend, tk.delimiterNum)); } return offsetVec; } static void GetChildMessages(std::vector& childMessages, const Cangjie::MacroCall& macCall) { for (auto& child : macCall.children) { if (!child->items.empty()) { childMessages.emplace_back(ChildMessage{child->GetFullName(), child->items}); } GetChildMessages(childMessages, *child); } } static auto CreatItmeInfoVec(FlatBufferBuilder& builder, const std::vector& items) { std::vector> itemsVec; for (auto& it : items) { auto key = builder.CreateString(it.key); switch (it.kind) { case ItemKind::STRING: { auto str = builder.CreateString(it.sValue); itemsVec.push_back(CreateItemInfo(builder, key, OptionValue_sValue, str.Union())); break; } case ItemKind::INT: { auto i = builder.CreateStruct(IntValue(it.iValue)); itemsVec.push_back(CreateItemInfo(builder, key, OptionValue_iValue, i.Union())); break; } case ItemKind::BOOL: { auto b = builder.CreateStruct(BoolValue(it.bValue)); itemsVec.push_back(CreateItemInfo(builder, key, OptionValue_bValue, b.Union())); break; } case ItemKind::TKS: { auto tksVec = builder.CreateVector(CreateTokenVec(builder, it.tValue)); auto t = CreateTokensValue(builder, tksVec); itemsVec.push_back(CreateItemInfo(builder, key, OptionValue_tValue, t.Union())); break; } } } return itemsVec; } static auto CreateDiagVec(FlatBufferBuilder& builder, const std::vector& diags) { std::vector> offsetVec; for (auto& diag : diags) { auto& beginPos = diag.mainHint.range.begin; auto& endPos = diag.mainHint.range.end; auto begin = MacroMsgFormat::Position(beginPos.fileID, beginPos.line, beginPos.column); auto end = MacroMsgFormat::Position(endPos.fileID, endPos.line, endPos.column); auto errorMessage = builder.CreateString(diag.errorMessage); auto mainHint = builder.CreateString(diag.mainHint.str); offsetVec.push_back( CreateDiagnostic(builder, static_cast(diag.diagSeverity), &begin, &end, errorMessage, mainHint)); } return offsetVec; } static auto CreateMacroCall(FlatBufferBuilder& builder, const Cangjie::MacroCall& macCall) { auto& inv = *(macCall.GetInvocation()); auto& idPos = inv.identifierPos; auto pos = MacroMsgFormat::Position(idPos.fileID, idPos.line, idPos.column); auto id = CreateIdInfo(builder, builder.CreateString(inv.identifier), &pos); auto args = builder.CreateVector(CreateTokenVec(builder, inv.args)); auto attrs = builder.CreateVector(CreateTokenVec(builder, inv.attrs)); auto parentNames = builder.CreateVectorOfStrings(macCall.parentNames); std::vector> childMsgVec; std::vector childMessages; GetChildMessages(childMessages, macCall); for (auto& msg : childMessages) { auto childName = builder.CreateString(msg.childName); childMsgVec.push_back( CreateChildMsg(builder, childName, builder.CreateVector(CreatItmeInfoVec(builder, msg.items)))); } auto childMsges = builder.CreateVector(childMsgVec); auto methodName = builder.CreateString(macCall.methodName); auto packageName = builder.CreateString(macCall.packageName); auto libPath = builder.CreateString(macCall.libPath); auto beginPos = macCall.GetBeginPos(); auto endPos = macCall.GetEndPos(); auto bPos = MacroMsgFormat::Position(beginPos.fileID, beginPos.line, beginPos.column); auto ePos = MacroMsgFormat::Position(endPos.fileID, endPos.line, endPos.column); return CreateMacroCall(builder, id, inv.hasAttr, args, attrs, parentNames, childMsges, methodName, packageName, libPath, &bPos, &ePos); } static auto CreateItemsVecFromRecordMacroInfo( FlatBufferBuilder& builder, const std::vector& recordMacroInfo) { std::vector> itemsVec; auto itemNum = recordMacroInfo.size() / MACRO_SETITEM_INFO_NUM; for (size_t i = 0; i < itemNum; i++) { auto itemid = i * MACRO_SETITEM_INFO_NUM; // Item: key. Offset key; auto pKey = recordMacroInfo[itemid]; // 0: key malloc from ffi, need free in child process. if (pKey) { key = builder.CreateString(std::string(static_cast(pKey))); free(pKey); } else { continue; } auto pValue = recordMacroInfo[itemid + 1]; auto pType = recordMacroInfo[itemid + 2]; auto type = static_cast(*static_cast(pType)); free(pType); if (type == ItemKind::STRING) { auto val = builder.CreateString(std::string(static_cast(pValue))); itemsVec.push_back(CreateItemInfo(builder, key, OptionValue_sValue, val.Union())); } else if (type == ItemKind::INT) { auto val = builder.CreateStruct(IntValue(*static_cast(pValue))); itemsVec.push_back(CreateItemInfo(builder, key, OptionValue_iValue, val.Union())); } else if (type == ItemKind::BOOL) { auto val = builder.CreateStruct(BoolValue(*static_cast(pValue))); itemsVec.push_back(CreateItemInfo(builder, key, OptionValue_bValue, val.Union())); } else if (type == ItemKind::TKS) { if (pValue == nullptr) { auto tksVec = builder.CreateVector(CreateTokenVec(builder, std::vector())); auto val = CreateTokensValue(builder, tksVec); itemsVec.push_back(CreateItemInfo(builder, key, OptionValue_tValue, val.Union())); } else { auto tokens = TokenSerialization::GetTokensFromBytes(static_cast(pValue) + sizeof(uint32_t)); auto tksVec = builder.CreateVector(CreateTokenVec(builder, tokens)); auto val = CreateTokensValue(builder, tksVec); itemsVec.push_back(CreateItemInfo(builder, key, OptionValue_tValue, val.Union())); } } if (pValue) { free(pValue); } } return builder.CreateVector(itemsVec); } static void DeserializePositionFromPositionBuf(Cangjie::Position& pos, const MacroMsgFormat::Position& posBuf) { pos.fileID = posBuf.file_id(); pos.line = posBuf.line(); pos.column = posBuf.column(); } static void DeserializeTokenFromTokenBuf(Cangjie::Token& tk, const MacroMsgFormat::Token& tkBuffer) { tk.kind = static_cast(tkBuffer.kind()); tk.delimiterNum = tkBuffer.delimiterNum(); Cangjie::Position begin; Cangjie::Position end; DeserializePositionFromPositionBuf(begin, *tkBuffer.begin()); DeserializePositionFromPositionBuf(end, *tkBuffer.end()); tk.SetValuePos(tkBuffer.value()->str(), begin, end); } static void DeserializeTokensFromOffsetTokenVec( std::vector& tks, const Vector>& offsetVec) { tks.clear(); uoffset_t num = offsetVec.size(); Cangjie::Token tmp{TokenKind::ILLEGAL}; for (uoffset_t i = 0; i < num; i++) { DeserializeTokenFromTokenBuf(tmp, *offsetVec.Get(i)); tks.push_back(tmp); } } static void DeserializeDiagFromDiagBuf(Cangjie::Diagnostic& diag, const MacroMsgFormat::Diagnostic& diagBuffer) { DeserializePositionFromPositionBuf(diag.start, *diagBuffer.begin()); DeserializePositionFromPositionBuf(diag.end, *diagBuffer.end()); diag.errorMessage = diagBuffer.errorMessage()->str(); diag.mainHint.str = diagBuffer.mainHint()->str(); diag.diagSeverity = static_cast(diagBuffer.diagSeverity()); } static void DeserializeDiagsFromOffsetDiagVec( std::vector& diags, const Vector>& offsetVec) { diags.clear(); uoffset_t num = offsetVec.size(); Cangjie::Diagnostic tmp{}; for (uoffset_t i = 0; i < num; i++) { DeserializeDiagFromDiagBuf(tmp, *offsetVec.Get(i)); diags.push_back(tmp); } } static void DeserializeItemsFromItemsBuf( std::vector& items, const Vector>& itemsBuf) { uoffset_t num = itemsBuf.size(); items.resize(num); for (uoffset_t i = 0; i < num; i++) { items[i].key = itemsBuf.Get(i)->key()->str(); switch (itemsBuf.Get(i)->value_type()) { case OptionValue_sValue: items[i].kind = ItemKind::STRING; items[i].sValue = itemsBuf.Get(i)->value_as_sValue()->str(); break; case OptionValue_iValue: items[i].kind = ItemKind::INT; items[i].iValue = itemsBuf.Get(i)->value_as_iValue()->val(); break; case OptionValue_bValue: items[i].kind = ItemKind::BOOL; items[i].bValue = itemsBuf.Get(i)->value_as_bValue()->val(); break; case OptionValue_tValue: items[i].kind = ItemKind::TKS; DeserializeTokensFromOffsetTokenVec(items[i].tValue, *(itemsBuf.Get(i)->value_as_tValue()->val())); break; case OptionValue_NONE: [[fallthrough]]; // error default: Errorln("DeserializeItemsFromItemsBuf value type error, key: ", items[i].key); break; } } } } // namespace void MacroEvalMsgSerializer::SerializeDeflibMsg( const std::unordered_set& macrolibs, std::vector& bufferData) { builder.Clear(); std::vector> pathsVec; for (auto& p : macrolibs) { pathsVec.push_back(builder.CreateString(p)); } auto paths = builder.CreateVector(pathsVec); auto content = CreateDefLib(builder, paths); auto macroMsg = CreateMacroMsg(builder, MsgContent_defLib, content.Union()); builder.Finish(macroMsg); bufferData.assign(builder.GetBufferPointer(), builder.GetBufferPointer() + builder.GetSize()); } void MacroEvalMsgSerializer::SerializeMacroCallMsg(const Cangjie::MacroCall& macCall, std::vector& bufferData) { builder.Clear(); std::vector> callsVec; if (macCall.GetInvocation() == nullptr) { Errorln("SerializeMacroCallMsg nullptr"); return; } callsVec.push_back(CreateMacroCall(builder, macCall)); auto calls = builder.CreateVector(callsVec); auto cont = CreateMultiMacroCalls(builder, calls); auto macroMsg = CreateMacroMsg(builder, MsgContent_multiCalls, cont.Union()); builder.Finish(macroMsg); bufferData.assign(builder.GetBufferPointer(), builder.GetBufferPointer() + builder.GetSize()); } void MacroEvalMsgSerializer::SerializeMultiCallsMsg( const std::list& macCalls, std::vector& bufferData) { builder.Clear(); std::vector> callsVec; for (auto callPtr : macCalls) { if (callPtr->GetInvocation() == nullptr) { Errorln("SerializeMultiCallsMsg nullptr"); return; } callsVec.push_back(CreateMacroCall(builder, *callPtr)); } auto calls = builder.CreateVector(callsVec); auto cont = CreateMultiMacroCalls(builder, calls); auto macroMsg = CreateMacroMsg(builder, MsgContent_multiCalls, cont.Union()); builder.Finish(macroMsg); bufferData.assign(builder.GetBufferPointer(), builder.GetBufferPointer() + builder.GetSize()); } bool MacroEvalMsgSerializer::SerializeMacroCallResultMsg(const MacroCall& macCall, std::vector& bufferData) { if (macCall.GetInvocation() == nullptr) { return false; } builder.Clear(); auto& inv = *(macCall.GetInvocation()); auto& idPos = inv.identifierPos; auto pos = MacroMsgFormat::Position(idPos.fileID, idPos.line, idPos.column); auto id = CreateIdInfo(builder, builder.CreateString(inv.identifier), &pos); auto tks = builder.CreateVector(CreateTokenVec(builder, inv.newTokens)); Offset>> items; if (!macCall.items.empty()) { items = builder.CreateVector(CreatItmeInfoVec(builder, macCall.items)); } else if (macCall.recordMacroInfo.size() / MACRO_SETITEM_INFO_NUM > 0) { // serialize and free ffi malloc. items = CreateItemsVecFromRecordMacroInfo(builder, macCall.recordMacroInfo); } else { std::vector> itemsVec; items = builder.CreateVector(itemsVec); } auto assertParents = builder.CreateVectorOfStrings(macCall.assertParents); auto diags = macCall.ci->diag.GetCategoryDiagnostic(DiagCategory::PARSE); auto diagnostics = builder.CreateVector(CreateDiagVec(builder, diags)); auto macroResultCont = CreateMacroResult(builder, id, static_cast(macCall.status), tks, items, assertParents, diagnostics); auto macroMsg = CreateMacroMsg(builder, MsgContent_macroResult, macroResultCont.Union()); builder.Finish(macroMsg); bufferData.assign(builder.GetBufferPointer(), builder.GetBufferPointer() + builder.GetSize()); return true; } void MacroEvalMsgSerializer::SerializeExitMsg(std::vector& bufferData, bool flag) { builder.Clear(); auto cont = builder.CreateStruct(MacroMsgFormat::ExitTask(flag)); auto macroMsg = CreateMacroMsg(builder, MsgContent_exitTask, cont.Union()); builder.Finish(macroMsg); bufferData.assign(builder.GetBufferPointer(), builder.GetBufferPointer() + builder.GetSize()); } MacroMsgFormat::MsgContent MacroEvalMsgSerializer::GetMacroMsgContenType(const std::vector& bufferData) { return GetMacroMsg(bufferData.data())->content_type(); } void MacroEvalMsgSerializer::DeSerializeDeflibMsg( std::list& macroLibs, const std::vector& bufferData) { auto defLib = GetMacroMsg(bufferData.data())->content_as_defLib(); uoffset_t pathNum = defLib->paths()->size(); macroLibs.clear(); for (uoffset_t i = 0; i < pathNum; i++) { macroLibs.push_back(defLib->paths()->Get(i)->str()); } } void MacroEvalMsgSerializer::DeSerializeRangeFromCall( Position& begin, Position& end, const MacroMsgFormat::MacroCall& callFmt) { DeserializePositionFromPositionBuf(begin, *callFmt.begin()); DeserializePositionFromPositionBuf(end, *callFmt.end()); } void MacroEvalMsgSerializer::DeSerializeIdInfoFromCall( std::string& id, Position& pos, const MacroMsgFormat::MacroCall& callFmt) { id = callFmt.id()->name()->str(); DeserializePositionFromPositionBuf(pos, *callFmt.id()->pos()); } void MacroEvalMsgSerializer::DeSerializeArgsFromCall(std::vector& args, const MacroMsgFormat::MacroCall& callFmt) { DeserializeTokensFromOffsetTokenVec(args, *callFmt.args()); } void MacroEvalMsgSerializer::DeSerializeAttrsFromCall( std::vector& attrs, const MacroMsgFormat::MacroCall& callFmt) { DeserializeTokensFromOffsetTokenVec(attrs, *callFmt.attrs()); } void MacroEvalMsgSerializer::DeSerializeParentNamesFromCall( std::vector& parentNames, const MacroMsgFormat::MacroCall& callFmt) { uoffset_t num = callFmt.parentNames()->size(); parentNames.resize(num); for (uoffset_t i = 0; i < num; i++) { parentNames[i] = callFmt.parentNames()->Get(i)->str(); } } void MacroEvalMsgSerializer::DeSerializeChildMsgesFromCall( std::vector& childMsges, const MacroMsgFormat::MacroCall& callFmt) { uoffset_t msgNum = callFmt.childMsges()->size(); childMsges.resize(msgNum); for (uoffset_t i = 0; i < msgNum; i++) { childMsges[i].childName = callFmt.childMsges()->Get(i)->childName()->str(); DeserializeItemsFromItemsBuf(childMsges[i].items, *callFmt.childMsges()->Get(i)->items()); } } void MacroEvalMsgSerializer::DeSerializeIdInfoFromResult( std::string& id, Position& pos, const std::vector& bufferData) { auto result = GetMacroMsg(bufferData.data())->content_as_macroResult(); if (result != nullptr) { id = result->id()->name()->str(); DeserializePositionFromPositionBuf(pos, *result->id()->pos()); } } void MacroEvalMsgSerializer::DeSerializeTksFromResult(std::vector& tks, const std::vector& bufferData) { auto result = GetMacroMsg(bufferData.data())->content_as_macroResult(); if (result != nullptr) { DeserializeTokensFromOffsetTokenVec(tks, *result->tks()); } } void MacroEvalMsgSerializer::DeSerializeDiagsFromResult(std::vector& diags, const std::vector& bufferData) { auto result = GetMacroMsg(bufferData.data())->content_as_macroResult(); if (result != nullptr) { DeserializeDiagsFromOffsetDiagVec(diags, *result->diags()); } } MacroEvalStatus MacroEvalMsgSerializer::DeSerializeStatusFromResult(const std::vector& bufferData) { auto result = GetMacroMsg(bufferData.data())->content_as_macroResult(); if (result != nullptr) { return static_cast(result->status()); } return MacroEvalStatus::REEVALFAILED; } void MacroEvalMsgSerializer::DeSerializeItemsFromResult( std::vector& items, const std::vector& bufferData) { auto result = GetMacroMsg(bufferData.data())->content_as_macroResult(); if (result != nullptr) { DeserializeItemsFromItemsBuf(items, *result->items()); } } void MacroEvalMsgSerializer::DeSerializeAssertParentsFromResult( std::vector& assertParents, const std::vector& bufferData) { auto result = GetMacroMsg(bufferData.data())->content_as_macroResult(); if (result != nullptr) { uoffset_t num = result->assertParents()->size(); assertParents.resize(num); for (uoffset_t i = 0; i < num; i++) { assertParents[i] = result->assertParents()->Get(i)->str(); } } } cangjie_compiler-1.0.7/src/Macro/MacroEvaluation.cpp000066400000000000000000001204501510705540100224360ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements macro evaluation related apis for macro. */ #include "cangjie/Macro/MacroEvaluation.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/Match.h" #include "cangjie/Basic/Print.h" #include "cangjie/Lex/Lexer.h" #include "cangjie/Macro/InvokeUtil.h" #include "cangjie/Macro/TokenSerialization.h" #include "cangjie/Modules/PackageManager.h" #include "cangjie/Parse/Parser.h" #include "cangjie/Utils/ProfileRecorder.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace AST; using namespace InvokeRuntime; namespace { /* * Preprocess Tokens to deal with escape for @. */ bool PreprocessTokens(TokenVector& inputTokens, std::vector& escapePosVec) { size_t tokLen = inputTokens.size(); size_t index = 0; TokenVector eraseTokens; // Update escapePosVec. while (index < tokLen) { if (inputTokens[index].Value() == "\\") { if (index == tokLen - 1) { return false; } if (inputTokens[index + 1].Value() == "@") { (void)escapePosVec.emplace_back(inputTokens[index + 1].Begin()); (void)eraseTokens.emplace_back(inputTokens[index]); } } index++; } // Erase escape. Utils::EraseIf(inputTokens, [&](const Token& t) { return Utils::In(t, eraseTokens) && t.Value() == "\\"; }); return true; } bool IsInPrimaryFuncParam(const OwnedPtr& decl, const Position& mcPos) { bool primaryFuncParam = false; auto checkMacroCallScope = [&primaryFuncParam, &mcPos](Ptr curNode) -> VisitAction { if (curNode->astKind == ASTKind::MACRO_EXPAND_PARAM && curNode->begin == mcPos) { if (auto sc = std::get_if(&(curNode->GetConstInvocation()->scope)); sc) { auto scope = *sc; if (scope == ScopeKind::PRIMARY_CONSTRUCTOR_BODY_FOR_CLASS || scope == ScopeKind::PRIMARY_CONSTRUCTOR_BODY_FOR_STRUCT) { primaryFuncParam = true; } } return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; Walker mcWalker(decl.get(), checkMacroCallScope); mcWalker.Walk(); return primaryFuncParam; } auto GetMacroScopeKind(MacroInvocation& invocation, const Position& mcPos) { static std::unordered_map scopeOfDecl = { {ASTKind::INTERFACE_DECL, ScopeKind::INTERFACE_BODY}, {ASTKind::EXTEND_DECL, ScopeKind::EXTEND_BODY}, {ASTKind::CLASS_DECL, ScopeKind::CLASS_BODY}, {ASTKind::STRUCT_DECL, ScopeKind::STRUCT_BODY}, {ASTKind::ENUM_DECL, ScopeKind::ENUM_BODY}, {ASTKind::FUNC_DECL, ScopeKind::FUNC_BODY}, }; // If outer macro have parenthesis, the scope rule is unknown to avoid scope related errors. if (invocation.hasParenthesis) { return static_cast(ScopeKind::UNKNOWN_SCOPE); } if (invocation.decl) { if (IsInPrimaryFuncParam(invocation.decl, mcPos)) { return static_cast(ScopeKind::PRIMARY_CONSTRUCTOR_FUNC_PARAM); } if (scopeOfDecl.find(invocation.decl->astKind) != scopeOfDecl.end()) { return static_cast(scopeOfDecl[invocation.decl->astKind]); } } if (auto sc = std::get_if(&invocation.scope); sc) { return static_cast(*sc); } return static_cast(ScopeKind::TOPLEVEL); } auto GetMacroScopeKindReEval(MacroInvocation& invocation) { // If outer macro have parenthesis, the scope rule is unknown to avoid scope related errors. if (invocation.hasParenthesis) { return static_cast(ScopeKind::UNKNOWN_SCOPE); } // macro in reEval step produced by another macro Invocation, use invocation's scope. if (auto sc = std::get_if(&invocation.scope); sc) { return static_cast(*sc); } return static_cast(ScopeKind::TOPLEVEL); } std::string GetPrimaryName(const MacroInvocation& invocation) { if (invocation.decl) { if (invocation.decl->astKind == ASTKind::STRUCT_DECL || invocation.decl->astKind == ASTKind::CLASS_DECL) { return invocation.decl->identifier; } else { return invocation.outerDeclIdent; } } return ""; } bool HasMacroCallToEval(const std::string& moduleName, const TokenVector& inputTokens, size_t curIndex, size_t tokenSize, std::vector& escapePosVec) { if (inputTokens[curIndex].Value() != "@" || curIndex >= tokenSize - 1) { return false; } // Ignore an escape @. if (std::find(escapePosVec.begin(), escapePosVec.end(), inputTokens[curIndex].Begin()) != escapePosVec.end()) { return false; } if (IsBuiltinAnnotation(moduleName, inputTokens[curIndex + 1].Value())) { return false; } if (!IsIdentifierOrContextualKeyword(inputTokens[curIndex + 1].kind)) { return false; } return true; } bool HasChildMacroCall(MacroCall& macCall) { for (auto mc : macCall.children) { auto pInvocation = mc->GetInvocation(); if (pInvocation && !pInvocation->isCustom) { return true; } } return false; } size_t CalculateEndIdx(const TokenVector& tks, const Position& targetPos) { if (tks.empty()) { return 0; } auto size = tks.size(); for (size_t i = 0; i < size; i++) { if (tks[i].Begin() == targetPos) { return i; } if (tks[i].End() == targetPos) { return i + 1; } } return size - 1; } size_t GetUnUsedThread(std::vector& isThreadUseds) { auto size = isThreadUseds.size(); for (size_t i = 0; i < size; i++) { if (!isThreadUseds[i]) { return i; } } return size; } /** * Refresh the tokens position for nestedMacro, * Macro invocation without parentheses: we need to parse if "EvaluateTokens" generates new tokens. */ void RefreshTokensPos(std::vector& inputTokens, const Position pos, SourceManager& sm) { if (inputTokens.empty()) { return; } auto meFileID = pos.fileID; auto meLine = pos.line; auto lastColumn = pos.column + static_cast(inputTokens[0].Length()) + 1; for (auto& token : inputTokens) { if (IsCurFile(sm, token)) { meFileID = token.Begin().fileID; meLine = token.Begin().line; lastColumn = token.Begin().column + static_cast(token.Value().size()) + 1; } else { token.SetValuePos(token.Value(), {meFileID, meLine, lastColumn, token.Begin().isCurFile}, /* multiline string is compressed into one line in macro expansion */ {meFileID, meLine, lastColumn + static_cast(token.Value().size()), token.End().isCurFile}); } } } bool IsMacroDefUndeclared(const MacroCall& macCall) { auto pInvocation = macCall.GetInvocation(); if (!pInvocation) { return false; } // For case: @M(...). if (pInvocation->hasParenthesis && !pInvocation->IsIfAvailable()) { return true; } // For custom annotation. return false; } void RefreshMacroCallArgs(MacroCall& macCall, DiagnosticEngine& diag) { auto pInvocation = macCall.GetInvocation(); TokenVector tokensAfterEval; if (macCall.status == MacroEvalStatus::ANNOTATION) { // Add @annotation[a,b] tokens. size_t atTokenSize{1}; if (pInvocation->isCompileTimeVisible) { std::string atExclToken = "@!"; atTokenSize = atExclToken.size(); tokensAfterEval.emplace_back( TokenKind::AT_EXCL, atExclToken, pInvocation->atPos, pInvocation->atPos + atTokenSize); } else { tokensAfterEval.emplace_back(Token(TokenKind::AT, "@", pInvocation->atPos, pInvocation->atPos + 1)); } auto tks = GetTokensFromString(pInvocation->fullName, diag, pInvocation->atPos + atTokenSize); (void)tokensAfterEval.insert(tokensAfterEval.end(), tks.begin(), tks.end()); if (!pInvocation->attrs.empty()) { (void)tokensAfterEval.emplace_back( Token(TokenKind::LSQUARE, "[", pInvocation->leftSquarePos, pInvocation->leftSquarePos + 1)); (void)tokensAfterEval.insert(tokensAfterEval.end(), pInvocation->attrs.begin(), pInvocation->attrs.end()); (void)tokensAfterEval.emplace_back( Token(TokenKind::RSQUARE, "]", pInvocation->rightSquarePos, pInvocation->rightSquarePos + 1)); } if (pInvocation->hasParenthesis && pInvocation->nodes.empty()) { (void)tokensAfterEval.emplace_back( Token(TokenKind::LPAREN, "(", pInvocation->leftParenPos, pInvocation->leftParenPos + 1)); (void)tokensAfterEval.insert(tokensAfterEval.end(), pInvocation->args.begin(), pInvocation->args.end()); (void)tokensAfterEval.emplace_back( Token(TokenKind::RPAREN, ")", pInvocation->rightParenPos, pInvocation->rightParenPos + 1)); } else { auto nlpos = tokensAfterEval.back().Begin() + 1; (void)tokensAfterEval.emplace_back(Token(TokenKind::NL, "\n", nlpos, nlpos + 1)); if (pInvocation->nodes.empty()) { (void)tokensAfterEval.insert(tokensAfterEval.end(), pInvocation->args.begin(), pInvocation->args.end()); } } } if (macCall.status == MacroEvalStatus::ANNOTATION && pInvocation->hasParenthesis && !pInvocation->nodes.empty()) { (void)tokensAfterEval.emplace_back( Token(TokenKind::LPAREN, "(", pInvocation->leftParenPos, pInvocation->leftParenPos + 1)); } for (auto& node : pInvocation->nodes) { if (node->astKind == ASTKind::TOKEN_PART) { auto tp = RawStaticCast(node.get()); (void)tokensAfterEval.insert(tokensAfterEval.end(), tp->tokens.begin(), tp->tokens.end()); } else { auto pInv = node->GetInvocation(); (void)tokensAfterEval.insert(tokensAfterEval.end(), pInv->newTokens.begin(), pInv->newTokens.end()); } } if (macCall.status == MacroEvalStatus::ANNOTATION && pInvocation->hasParenthesis && !pInvocation->nodes.empty()) { (void)tokensAfterEval.emplace_back( Token(TokenKind::RPAREN, ")", pInvocation->rightParenPos, pInvocation->rightParenPos + 1)); } if (macCall.isForInterpolation) { pInvocation->newTokens = {Token(macCall.strKind, LineToString(tokensAfterEval))}; macCall.status = MacroEvalStatus::SUCCESS; return; } if (macCall.status == MacroEvalStatus::REEVAL || macCall.status == MacroEvalStatus::ANNOTATION) { pInvocation->newTokens.assign(tokensAfterEval.begin(), tokensAfterEval.end()); macCall.status = MacroEvalStatus::SUCCESS; return; } pInvocation->args.assign(tokensAfterEval.begin(), tokensAfterEval.end()); macCall.status = MacroEvalStatus::READY; } void CheckMacroCallArgs(MacroCall& macCall, DiagnosticEngine& diag, bool compileCjd) { if (macCall.status != MacroEvalStatus::READY) { return; } auto pInvocation = macCall.GetInvocation(); // If the inner macrocall does not have parentheses, new generated tokens need to be parsed again. if (!macCall.isOuterMost && !pInvocation->hasParenthesis && !pInvocation->nodes.empty()) { auto scopeKind = *std::get_if(&(pInvocation->scope)); auto primaryName = GetPrimaryName(*pInvocation); auto pos = macCall.GetBeginPos() + macCall.GetIdentifier().size(); std::vector newTokens = pInvocation->args; RefreshTokensPos(newTokens, pos, diag.GetSourceManager()); auto errCnt = diag.GetErrorCount(); Parser parser{newTokens, diag, diag.GetSourceManager(), false, compileCjd}; parser.SetPrimaryDecl(primaryName).SetCurFile(macCall.GetNode()->curFile); parser.ParseDecl(scopeKind); if (diag.GetErrorCount() > errCnt) { macCall.status = MacroEvalStatus::FAIL; return; } } } void SetMacroCallEvalResult(MacroCall& macCall, DiagnosticEngine& diag) { // Evaluate failed or success. auto pInvocation = macCall.GetInvocation(); if (MacroExpandFailed(pInvocation->newTokens) || macCall.status == MacroEvalStatus::FAIL) { macCall.status = MacroEvalStatus::FAIL; (void)diag.Diagnose(macCall.GetBeginPos(), DiagKind::macro_evaluate_failed, macCall.GetFullName()); } else { macCall.status = MacroEvalStatus::SUCCESS; } } bool HandleBeforeMacroCallEval(MacroCall& macCall, DiagnosticEngine& diag) { std::string macCallFullName = macCall.GetFullName(); // If use build-in macros, the macro is converted to token information. if (Utils::In(macCallFullName, BUILD_IN_MACROS)) { if (macCall.GetInvocation()->HasAttr()) { (void)diag.Diagnose(macCall.GetBeginPos(), DiagKind::macro_build_in_unexpect_params_attrs, macCallFullName); } if (!macCall.GetInvocation()->args.empty()) { (void)diag.Diagnose(macCall.GetBeginPos(), DiagKind::macro_build_in_unexpect_params, macCallFullName); } auto macroNode = macCall.GetNode(); TokenVector newTokens; if (macCallFullName == SOURCE_PACKAGE) { auto pos = macCall.GetBeginPos(); (void)newTokens.emplace_back(Token(macCall.strKind, macroNode->curFile->curPackage->fullPackageName, pos, pos + macroNode->curFile->curPackage->fullPackageName.size())); } else if (macCallFullName == SOURCE_FILE) { auto pos = macCall.GetBeginPos(); (void)newTokens.emplace_back( Token(macCall.strKind, macroNode->curFile->fileName, pos, pos + macroNode->curFile->fileName.size())); } else { auto pos = macCall.useParentPos ? macCall.parentMacroCall->GetBeginPos() : macCall.GetBeginPos(); (void)newTokens.emplace_back(Token( TokenKind::INTEGER_LITERAL, std::to_string(pos.line), pos, pos + std::to_string(pos.line).size())); } macCall.GetInvocation()->newTokens = newTokens; macCall.status = MacroEvalStatus::SUCCESS; return true; } return false; } bool IsMacroCallReadyForEval(MacroCall& macCall, DiagnosticEngine& diag, bool compileCjd) { if (HandleBeforeMacroCallEval(macCall, diag)) { return false; } if (macCall.status == MacroEvalStatus::INIT || macCall.status == MacroEvalStatus::REEVAL || macCall.status == MacroEvalStatus::ANNOTATION) { size_t sucCnt = 0; for (auto& mc : macCall.children) { if (mc->status == MacroEvalStatus::SUCCESS) { sucCnt++; continue; } if (mc->status == MacroEvalStatus::FAIL) { macCall.status = MacroEvalStatus::FAIL; break; } } if (sucCnt == macCall.children.size()) { RefreshMacroCallArgs(macCall, diag); } } CheckMacroCallArgs(macCall, diag, compileCjd); if (macCall.status == MacroEvalStatus::READY) { macCall.status = MacroEvalStatus::EVAL; return true; } return false; } void ExpandBuildInMacro(TokenVector& attrTokens, MacroCall& macCall) { auto replaceTokens = [&macCall, &attrTokens](auto begin, std::string& macCallFullName, auto pos) { if (macCallFullName == SOURCE_PACKAGE) { (void)attrTokens.emplace(begin, Token(macCall.strKind, macCall.GetNode()->curFile->curPackage->fullPackageName, pos, pos + macCall.GetNode()->curFile->curPackage->fullPackageName.size())); } else if (macCallFullName == SOURCE_FILE) { (void)attrTokens.emplace(begin, Token(macCall.strKind, macCall.GetNode()->curFile->fileName, pos, pos + std::to_string(begin->Begin().line).size())); } else { (void)attrTokens.emplace(begin, Token(TokenKind::INTEGER_LITERAL, std::to_string(begin->Begin().line), pos, pos + std::to_string(begin->Begin().line).size())); } }; for (auto it = attrTokens.begin(); it != attrTokens.end();) { auto begin = it; if ((++it) == attrTokens.end() || begin->kind != TokenKind::AT) { continue; } auto buildInMacro = it->Value(); auto pos = macCall.GetBeginPos(); if ((++it) == attrTokens.end() || !Utils::In(buildInMacro, BUILD_IN_MACROS)) { continue; } auto kind = it->kind; if ((++it) == attrTokens.end() || kind != TokenKind::LPAREN) { continue; } if (it->kind != TokenKind::RPAREN) { ++it; continue; } it = attrTokens.erase(begin, it + 1); replaceTokens(begin, buildInMacro, pos); } } } // namespace bool MacroEvaluation::CheckAttrTokens(TokenVector& attrTokens, MacroCall& macCall) { if (attrTokens.empty()) { return true; } // For the macro call: @M[@sourceFile()](), we need to make further conversions. // After conversion: @M["default.cj"]() ExpandBuildInMacro(attrTokens, macCall); // Make sure @ is used as escape. if (!PreprocessTokens(attrTokens, escapePosVec)) { (void)ci->diag.Diagnose(attrTokens[0].Begin(), DiagKind::macro_expand_invalid_escape); return false; } for (size_t i = 0; i < attrTokens.size(); i++) { auto posTmp = attrTokens[i].Begin(); if (attrTokens[i].kind == TokenKind::AT && std::find(escapePosVec.begin(), escapePosVec.end(), posTmp) == escapePosVec.end()) { (void)ci->diag.Diagnose(posTmp, DiagKind::macro_expand_invalid_attr_tokens, attrTokens[i].Value()); return false; } } return true; } bool MacroEvaluation::HasMacroCallInStrInterpolation(const std::string& str, MacroCall& macCall) { std::string strExprStart = "${"; std::string strExprEnd = "}"; size_t strExprStartLen = strExprStart.length(); size_t strExprEndLen = strExprEnd.length(); auto interpStr = str.substr(strExprStartLen, str.length() - (strExprStartLen + strExprEndLen)); auto tmpMacExpr = MakeOwned(); tmpMacExpr->curFile = macCall.GetNode()->curFile; auto tmpMacCall = std::make_unique(tmpMacExpr.get()); tmpMacCall->isForInterpolation = true; auto ptmpInvocation = tmpMacCall->GetInvocation(); ptmpInvocation->args = GetTokensFromString(interpStr, ci->diag); CreateMacroCallTree(*tmpMacCall); if (ptmpInvocation->nodes.empty()) { return false; } macCall.newStr += strExprStart; (void)macCall.children.insert(macCall.children.begin(), tmpMacCall->children.begin(), tmpMacCall->children.end()); auto pInvocation = macCall.GetInvocation(); auto tks = GetTokensFromString(macCall.newStr, ci->diag); (void)pInvocation->nodes.emplace_back(MakeOwned(tks)); (void)pInvocation->nodes.insert(pInvocation->nodes.end(), std::make_move_iterator(ptmpInvocation->nodes.begin()), std::make_move_iterator(ptmpInvocation->nodes.end())); macCall.newStr = strExprEnd; return true; } bool MacroEvaluation::HasMacroCallInStrInterpolation( TokenVector& input, size_t startIndex, size_t curIndex, MacroCall& parentMacCall) { auto tk = input[curIndex]; std::set tkKinds = {TokenKind::STRING_LITERAL, TokenKind::MULTILINE_STRING}; if (tkKinds.count(tk.kind) == 0 || tk.Value().find("${") == std::string::npos) { return false; } auto macExpr = MakeOwned(); macExpr->curFile = parentMacCall.GetNode()->curFile; auto macCall = std::make_unique(macExpr.get()); macCall->isForInterpolation = true; macCall->strKind = tk.kind; std::vector tokens{tk}; Lexer lexer(tokens, ci->diag, ci->diag.GetSourceManager()); auto strParts = lexer.GetStrParts(tk); for (auto& strP : strParts) { // Check interpolation contains macrocall or not. if (strP.strKind == StringPart::EXPR && HasMacroCallInStrInterpolation(strP.value, *macCall)) { continue; } macCall->newStr += strP.value; } auto pInvocation = macCall->GetInvocation(); if (pInvocation->nodes.empty()) { return false; } auto tks = GetTokensFromString(macCall->newStr, ci->diag); (void)pInvocation->nodes.emplace_back(MakeOwned(tks)); if (curIndex > startIndex) { auto beginIter = input.begin() + static_cast(startIndex); auto endIter = input.begin() + static_cast(curIndex); TokenVector tks1{beginIter, endIter}; (void)parentMacCall.GetInvocation()->nodes.emplace_back(MakeOwned(tks1)); } // For save child macrocall expression in memory. (void)parentMacCall.GetInvocation()->nodes.emplace_back(std::move(macExpr)); (void)parentMacCall.children.emplace_back(macCall.get()); // For evaluate status. (void)pMacroCalls.emplace_back(macCall.get()); // For evaluate. (void)childMacCalls.emplace_back(std::move(macCall)); // For save child macrocall in memory. return true; } void MacroEvaluation::ProcessTokensInQuoteExpr( TokenVector& input, size_t& startIndex, size_t& curIndex, MacroCall& macCall, bool reEval) { // Start with 0 for the opening parenthesis we found auto balance = 0; auto inQuoteInterpolation = false; while (curIndex < input.size()) { // macro M in quote($(@M(111))) should be expanded if (inQuoteInterpolation && input[curIndex].kind == TokenKind::AT) { CreateChildMacroCall(input, startIndex, curIndex, macCall, reEval); inQuoteInterpolation = false; } if (input[curIndex].kind == TokenKind::DOLLAR && input[curIndex + 1].kind == TokenKind::LPAREN) { inQuoteInterpolation = true; } // ignore escaped parenthesis \( \) if (input[curIndex].kind == TokenKind::LPAREN && input[curIndex].Begin() + input[curIndex].Value().size() + 1 != input[curIndex].End()) { balance++; } else if (input[curIndex].kind == TokenKind::RPAREN && input[curIndex].Begin() + input[curIndex].Value().size() + 1 != input[curIndex].End()) { balance--; // If balance reaches 0, we found the matching closing parenthesis if (balance == 0) { return; } } curIndex++; } return; } void MacroEvaluation::CreateChildMacroCall( TokenVector& inputTokens, size_t& startIndex, size_t& curIndex, MacroCall& macCall, bool reEval) { auto pInvocation = macCall.GetInvocation(); auto primaryName = GetPrimaryName(*pInvocation); auto scopeKind = reEval ? GetMacroScopeKindReEval(*pInvocation) : GetMacroScopeKind(*pInvocation, inputTokens[curIndex].Begin()); DiagnosticEngine tempDiag; SourceManager sm; tempDiag.SetSourceManager(&sm); // No need to diagnose the error here if in reEvaluate status, ReplaceEachMacro will diagnose the error. auto& diag = reEval ? tempDiag : ci->diag; auto errCnt = diag.GetErrorCount(); TokenVector tokensToParse(inputTokens.begin() + static_cast(curIndex), inputTokens.end()); // we don't trust the position of inputTokens. they may all have the same pos // and parser relay on position information to collect macro input tokens std::vector originPos; if (reEval) { for (size_t i = 0; i < tokensToParse.size(); i++) { originPos.emplace_back(tokensToParse[i].Begin()); } for (size_t i = 1; i < tokensToParse.size(); i++) { auto begin = tokensToParse[i - 1].End() + 1; auto end = begin + tokensToParse[i].Value().size(); tokensToParse[i].SetValuePos(tokensToParse[i].Value(), begin, end); } } Parser parser{tokensToParse, diag, ci->diag.GetSourceManager(), false, ci->invocation.globalOptions.compileCjd}; parser.SetPrimaryDecl(primaryName).SetCurFile(macCall.GetNode()->curFile); parser.SetCompileOptions(ci->invocation.globalOptions); auto decl = parser.ParseDecl(ScopeKind(scopeKind)); if (!decl->IsMacroCallNode()) { // Filter decls such as builtin annotations. curIndex++; return; } auto mc = std::make_unique(decl.get()); mc->parentMacroCall = &macCall; (void)macCall.children.emplace_back(mc.get()); // For evaluate status. if (diag.GetErrorCount() > errCnt) { mc->status = MacroEvalStatus::FAIL; } else { CreateMacroCallTree(*mc); } if (mc->status == MacroEvalStatus::FAIL) { macCall.status = MacroEvalStatus::FAIL; } (void)pInvocation->macroAtPosition.emplace_back(mc->GetBeginPos()); if (!mc->GetInvocation()->macroAtPosition.empty()) { pInvocation->macroAtPosition.insert(pInvocation->macroAtPosition.end(), mc->GetInvocation()->macroAtPosition.begin(), mc->GetInvocation()->macroAtPosition.end()); } (void)childMacCalls.emplace_back(std::move(mc)); // For save child macrocall in memory. if (startIndex < curIndex) { auto beginIter = inputTokens.begin() + static_cast(startIndex); auto endIter = inputTokens.begin() + static_cast(curIndex); TokenVector tokens{beginIter, endIter}; (void)pInvocation->nodes.emplace_back(MakeOwned(tokens)); } auto endIndex = curIndex + CalculateEndIdx(tokensToParse, decl->end); (void)pInvocation->nodes.emplace_back(std::move(decl)); // For save child macrocall expression in memory. // refresh back tokens position. CalculateEndIdx also use the position, // so we have to refresh back position after CalculateEndIdx. if (reEval) { for (size_t i = 1; i < tokensToParse.size(); i++) { tokensToParse[i].SetValuePos( tokensToParse[i].Value(), originPos[i], originPos[i] + tokensToParse[i].Value().size()); } } startIndex = endIndex; curIndex = endIndex; } void MacroEvaluation::CheckDeprecatedMacrosUsage(MacroCall& macCall) const { auto definition = macCall.GetDefinition(); if (!definition) { return; } for (auto& anno : definition->annotations) { if (anno->kind == AnnotationKind::DEPRECATED) { auto deprecated = anno.get(); std::string message = " "; std::string since = "."; bool strict = false; AST::ExtractArgumentsOfDeprecatedAnno(deprecated, message, since, strict); auto diagnoseKind = strict ? DiagKind::macro_is_deprecated_error : DiagKind::macro_is_deprecated_warning; ci->diag.Diagnose(macCall.GetBeginPos(), diagnoseKind, macCall.GetFullName(), since, message); } } } void MacroEvaluation::SaveUsedMacros(MacroCall& macCall) { SaveUsedMacroPkgs(macCall.packageName); auto node = macCall.GetNode(); auto decl = macCall.GetDefinition(); if (!node || !node->curFile || !decl) { return; } ci->importManager.AddUsedMacroDecls(macCall.GetNode()->curFile, decl); } void MacroEvaluation::SaveUsedMacroPkgs(const std::string packageName) { if (packageName.empty() || usedMacroPkgs.find(packageName) != usedMacroPkgs.end()) { return; } usedMacroPkgs[packageName] = false; } bool MacroEvaluation::NeedCreateMacroCallTree(MacroCall& macCall, bool reEval) { if (macCall.isForInterpolation) { return true; } auto pInvocation = macCall.GetInvocation(); if (reEval) { if (pInvocation->isCustom) { // No need to reEvaluate Annotation. macCall.status = MacroEvalStatus::FINISH; return false; } if (macCall.status == MacroEvalStatus::REEVALFAILED) { return false; } return true; } if (macCall.ResolveMacroCall(ci)) { SaveUsedMacros(macCall); CheckDeprecatedMacrosUsage(macCall); // Find macrodef and check attr. if (!CheckAttrTokens(pInvocation->attrs, macCall)) { macCall.status = MacroEvalStatus::FAIL; return false; } if (pInvocation->isCompileTimeVisible) { // issue a non-fatal error when evaluating macro call with @! ci->diag.Diagnose(macCall.GetBeginPos(), DiagKind::macro_expand_atexcl); } return true; } // Can not find macrodef. if (IsMacroDefUndeclared(macCall)) { macCall.status = MacroEvalStatus::FAIL; } // if macroCall status fail, macro is not find, return false. if (macCall.status == MacroEvalStatus::FAIL) { return false; } // For annotation case, should change Macro to Annotation. macCall.status = MacroEvalStatus::ANNOTATION; pInvocation->isCustom = true; return true; } void MacroEvaluation::CreateMacroCallTree(MacroCall& macCall, bool reEval) { macCall.ci = ci; if (!NeedCreateMacroCallTree(macCall, reEval)) { return; } auto pInvocation = macCall.GetInvocation(); pInvocation->nodes.clear(); auto& inputTokens = reEval ? pInvocation->newTokens : pInvocation->args; if (!PreprocessTokens(inputTokens, escapePosVec)) { (void)ci->diag.Diagnose(inputTokens[0].Begin(), DiagKind::macro_expand_invalid_escape); return; } auto node = macCall.GetNode(); CJC_ASSERT(node->curFile && node->curFile->curPackage); auto names = Utils::SplitQualifiedName(node->curFile->curPackage->fullPackageName); auto moduleName = names.size() > 1 ? names.front() : ""; size_t startIndex = 0; size_t curIndex = 0; auto tokenSize = inputTokens.size(); while (curIndex < tokenSize) { auto posTmp = inputTokens[curIndex].Begin(); // if current Token is not escaped @ or not macrocall like @MacroIdentidier, should be error if (inputTokens[curIndex].kind == TokenKind::AT && std::find(escapePosVec.begin(), escapePosVec.end(), posTmp) == escapePosVec.end() && (curIndex == tokenSize - 1 || !IsIdentifierOrContextualKeyword(inputTokens[curIndex + 1].kind))) { (void)ci->diag.Diagnose(posTmp, DiagKind::macro_expand_invalid_input_tokens); } if (inputTokens[curIndex].kind == TokenKind::QUOTE && inputTokens[curIndex + 1].kind == TokenKind::LPAREN) { curIndex++; ProcessTokensInQuoteExpr(inputTokens, startIndex, curIndex, macCall, reEval); continue; } if (HasMacroCallInStrInterpolation(inputTokens, startIndex, curIndex, macCall)) { curIndex++; startIndex = curIndex; continue; } if (!HasMacroCallToEval(moduleName, inputTokens, curIndex, tokenSize, escapePosVec)) { // Skip a normal token, which is not '@'. curIndex++; continue; } CreateChildMacroCall(inputTokens, startIndex, curIndex, macCall, reEval); } if (reEval && macCall.status == MacroEvalStatus::FAIL) { macCall.status = MacroEvalStatus::REEVALFAILED; return; } if (!pInvocation->nodes.empty()) { // Has child macrocall. TokenVector tokens{inputTokens.begin() + static_cast(startIndex), inputTokens.end()}; if (!tokens.empty()) { (void)pInvocation->nodes.emplace_back(MakeOwned(tokens)); } if (reEval && macCall.status == MacroEvalStatus::SUCCESS) { macCall.status = HasChildMacroCall(macCall) ? MacroEvalStatus::REEVAL : MacroEvalStatus::FINISH; } } else { // Has no child macrocall. if (macCall.status == MacroEvalStatus::ANNOTATION) { pInvocation->isCurFile = true; (void)pMacroCalls.emplace_back(&macCall); // For evaluate. return; } macCall.status = reEval ? MacroEvalStatus::FINISH : MacroEvalStatus::READY; } if (!macCall.isForInterpolation && macCall.status != MacroEvalStatus::FAIL && macCall.status != MacroEvalStatus::FINISH) { (void)pMacroCalls.emplace_back(&macCall); // For evaluate. } } void MacroEvaluation::CreateMacroCallsTree(bool reEval) { pMacroCalls.clear(); for (auto& macCall : macroCollector->macCalls) { if (reEval && macCall.status != MacroEvalStatus::SUCCESS) { continue; } CreateMacroCallTree(macCall, reEval); } } bool MacroEvaluation::CreateThreadToEvalMacroCall(MacroCall& macCall) { auto unUsedThreadId = GetUnUsedThread(isThreadUseds); if (unUsedThreadId >= threadNum) { return false; } isThreadUseds[unUsedThreadId] = true; macCall.threadId = unUsedThreadId; if (!useChildProcess) { // ParallelMacro for cjc. auto name = macCall.GetIdentifier() + macCall.GetBeginPos().ToString(); Utils::ProfileRecorder::Start("Parallel Evaluate Macros", name); EvalOneMacroCall(macCall); } return true; } bool MacroEvaluation::WaitForOneMacroCallEvalFinish(std::list& evalMacCalls) { if (useChildProcess) { // ParallelMacro for compiled Macro in lsp: // send macrocalls to child process, and wait macrocall's evalresult from child process. if (!SendMacroCallsTask(evalMacCalls)) { // If the child process exits abnormally, the sending may fail. return false; } if (!WaitMacroCallsEvalResult(evalMacCalls)) { // If send successful, but the child process exits abnormally, the waiting may fail. return false; } } bool bEvalFinish = false; while (!bEvalFinish) { auto iter = evalMacCalls.begin(); while (iter != evalMacCalls.end()) { auto& evalMacCall = *iter; // need wait if (evalMacCall->isDataReady) { // Evaluate macroCall failed or success. SetMacroCallEvalResult(*evalMacCall, ci->diag); } else { // Evaluating macroCall. (void)++iter; continue; } auto name = evalMacCall->GetIdentifier() + evalMacCall->GetBeginPos().ToString(); Utils::ProfileRecorder::Stop("Parallel Evaluate Macros", name); if (!useChildProcess) { // For compiled macro, need to release coroutine handle. ReleaseThreadHandle(*evalMacCall); } isThreadUseds[evalMacCall->threadId] = false; (void)evalMacCalls.erase(iter); bEvalFinish = true; break; } } return true; } void MacroEvaluation::EvalMacroCallsOnMultiThread() { // For compiled macro. if (!useChildProcess) { // Init global variable. Utils::ProfileRecorder::Start("MacroExpand", "InitGlobalVariable"); InitGlobalVariable(); Utils::ProfileRecorder::Stop("MacroExpand", "InitGlobalVariable"); } std::list evalMacCalls; size_t needEvalMacCalls = pMacroCalls.size(); while (needEvalMacCalls > 0) { for (auto& macCall : std::as_const(pMacroCalls)) { if (macCall->status == MacroEvalStatus::FAIL) { continue; } if (!IsMacroCallReadyForEval(*macCall, ci->diag, ci->invocation.globalOptions.compileCjd)) { if (macCall->status == MacroEvalStatus::FAIL) { needEvalMacCalls--; } continue; } if (!CreateThreadToEvalMacroCall(*macCall)) { break; } (void)evalMacCalls.emplace_back(macCall); if (evalMacCalls.size() >= threadNum) { break; } } if (evalMacCalls.empty()) { break; } if (!WaitForOneMacroCallEvalFinish(evalMacCalls)) { for (auto& macCall : std::as_const(pMacroCalls)) { if (macCall->status != MacroEvalStatus::SUCCESS && macCall->status != MacroEvalStatus::FINISH) { macCall->status = MacroEvalStatus::FAIL; } } break; } needEvalMacCalls--; } } void MacroEvaluation::EvalMacroCallsOnSingleThread() { for (auto& macCall : std::as_const(pMacroCalls)) { if (!IsMacroCallReadyForEval(*macCall, ci->diag, ci->invocation.globalOptions.compileCjd)) { continue; } auto name = macCall->GetIdentifier() + macCall->GetBeginPos().ToString(); Utils::ProfileRecorder::Start("Serial Evaluate Macros", name); EvalOneMacroCall(*macCall); SetMacroCallEvalResult(*macCall, ci->diag); Utils::ProfileRecorder::Stop("Serial Evaluate Macros", name); } } void MacroEvaluation::EvalOneMacroCall(MacroCall& macCall) { EvaluateWithRuntime(macCall); } void MacroEvaluation::FreeMacroInfoVecForMacroCall(MacroCall& mc) const { for (size_t i = 0; i < mc.recordMacroInfo.size(); i++) { if (mc.recordMacroInfo[i] != nullptr) { free(mc.recordMacroInfo[i]); mc.recordMacroInfo[i] = nullptr; } } for (auto& child : mc.children) { FreeMacroInfoVecForMacroCall(*child); } } void MacroEvaluation::EvalMacroCalls() { if (enableParallelMacro) { EvalMacroCallsOnMultiThread(); } else { EvalMacroCallsOnSingleThread(); } pMacroCalls.clear(); // Free address of malloc in Macro with Context. for (auto& mc : macroCollector->macCalls) { FreeMacroInfoVecForMacroCall(mc); mc.children.clear(); } } void MacroEvaluation::RefreshBuildInMacroPostionInfo() { for (auto& macCall : std::as_const(pMacroCalls)) { std::string macCallFullName = macCall->GetFullName(); if (macCall->parentMacroCall && Utils::In(macCallFullName, BUILD_IN_MACROS)) { macCall->useParentPos = true; } } } /** * We need constantly evaluate the tokens until no new macrocall is found in the new tokens. */ void MacroEvaluation::ReEvalAfterEvalMacroCalls() { CreateMacroCallsTree(true); while (!pMacroCalls.empty()) { RefreshBuildInMacroPostionInfo(); EvalMacroCalls(); CreateMacroCallsTree(true); } for (auto& macCall : macroCollector->macCalls) { if (macCall.status != MacroEvalStatus::FINISH && macCall.status != MacroEvalStatus::REEVALFAILED) { continue; } ProcessNewTokens(macCall); } } void MacroEvaluation::EvalMacros() { Utils::ProfileRecorder::Start("MacroExpand", "Evaluate Macros"); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND CollectMacroLibs(); #endif CreateMacroCallsTree(); EvalMacroCalls(); ReEvalAfterEvalMacroCalls(); Utils::ProfileRecorder::Stop("MacroExpand", "Evaluate Macros"); } std::unordered_set MacroEvaluation::GetMacroDefDynamicFiles() { std::unordered_set macroDynFiles; // For built-in macro packages the corresponding dynamic library needs to be added. for (auto& packageName : ci->importManager.GetImportedStdMacroPackages()) { auto basePath = FileUtil::JoinPath(FileUtil::GetDirPath(ci->invocation.globalOptions.executablePath), "../runtime/lib"); auto libName = "lib" + FileUtil::ConvertPackageNameToLibCangjieBaseFormat(packageName) + LIB_SUFFIX; auto macroPath = FileUtil::JoinPath( FileUtil::JoinPath(basePath, ci->invocation.globalOptions.GetCangjieLibHostPathName()), libName); (void)macroDynFiles.emplace(macroPath); } if (ci->invocation.globalOptions.macroLib.empty() && macroDynFiles.empty()) { return macroDynFiles; } for (auto lib : ci->invocation.globalOptions.macroLib) { (void)macroDynFiles.emplace(FileUtil::NormalizePath(lib)); } return macroDynFiles; } void MacroEvaluation::Evaluate() { if (useChildProcess) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (!ci->invocation.globalOptions.macroLib.empty()) { auto dyfiles = GetMacroDefDynamicFiles(); if (SendMacroDefTask(dyfiles)) { WaitMacroDefResult(); } } #endif EvalMacros(); SendExitStgTask(); return; } bool findMethodFlag{true}; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND findMethodFlag = RuntimeInit::GetInstance().InitRuntime( ci->invocation.GetRuntimeLibPath(), ci->invocation.globalOptions.environment.allVariables); #endif if (findMethodFlag) { EvalMacros(); } } cangjie_compiler-1.0.7/src/Macro/MacroEvaluationCJNative.cpp000066400000000000000000000163121510705540100240230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements macro evaluation related apis for macro. */ #include "cangjie/Macro/MacroEvaluation.h" #include "cangjie/Macro/TokenSerialization.h" using namespace Cangjie; using namespace AST; using namespace InvokeRuntime; namespace { using TaskFunc = void* (*)(void*); using InvokeNewTaskFromC = void* (*)(TaskFunc, void*); using ReleaseHandleFromC = void (*)(void*); /* * For compiled macro, evaluate macrocall by invoking method in runtime. */ struct ResetNotify { void* cFunc; void* param; ResetNotify(void* notifyFunc, void* notifyParam) : cFunc(notifyFunc), param(notifyParam) { } }; struct MacroInvoke { public: MacroCall* macCall; std::mutex mtx; std::condition_variable cv; bool isDataReady{false}; explicit MacroInvoke(MacroCall* mc) : macCall(mc) { } }; void ResetVariableFinishNotify(void* mi) { auto mInvoke = static_cast(mi); // here, we need to wait main thread get the mutex first, and then cv.wait unlock the mutex, // then we get the lock and notify, to make sure that wait in main thread is before notify. std::unique_lock lck(mInvoke->mtx); mInvoke->isDataReady = true; mInvoke->cv.notify_one(); } HANDLE InvokeMacroFunc(HANDLE mc) { auto pMacCall = static_cast(mc); auto pInvocation = pMacCall->GetInvocation(); auto attrTksBytes = TokenSerialization::GetTokensBytes(pInvocation->attrs); auto inputTksBytes = TokenSerialization::GetTokensBytes(pInvocation->args); uint8_t* retBuffer{nullptr}; if (pInvocation->hasAttr) { auto attrMacroFunc = reinterpret_cast(pMacCall->invokeFunc); retBuffer = attrMacroFunc(attrTksBytes.data(), static_cast(attrTksBytes.size()), inputTksBytes.data(), static_cast(inputTksBytes.size()), pMacCall); } else { auto commonMacroFunc = reinterpret_cast(pMacCall->invokeFunc); retBuffer = commonMacroFunc(inputTksBytes.data(), static_cast(inputTksBytes.size()), pMacCall); } pInvocation->newTokens = TokenSerialization::GetTokensFromBytes(retBuffer); if (retBuffer != nullptr) { free(retBuffer); retBuffer = nullptr; } pMacCall->isDataReady = true; return nullptr; } HANDLE InvokeMacro(HANDLE mi) { // For serial macro expansion. auto mInvoke = static_cast(mi); // here, we need to wait main thread get the mutex first, and then cv.wait unlock the mutex, // then we get the lock and notify, to make sure that wait in main thread is before notify. std::unique_lock lck(mInvoke->mtx); (void)InvokeMacroFunc(mInvoke->macCall); mInvoke->cv.notify_one(); return nullptr; } } // namespace void MacroEvaluation::CollectMacroLibs() { if (!ci->invocation.globalOptions.macroLib.empty()) { // Support to use --macro-lib option. // The following code will be deleted. auto dynFiles = GetMacroDefDynamicFiles(); for (auto& dyfile : dynFiles) { auto handle = InvokeRuntime::OpenSymbolTable(dyfile); if (handle == nullptr) { (void)ci->diag.Diagnose(DiagKind::can_not_open_macro_library, dyfile); return; } InvokeRuntime::SetOpenedLibHandles(handle); } } } void MacroEvaluation::EvaluateWithRuntime(MacroCall& macCall) { if (useChildProcess) { // For lsp, send task to child process for expanding macrocall. if (SendMacroCallTask(macCall)) { if (!enableParallelMacro) { // If send task successful and in serial macro mode, wait for eval result from child process. WaitMacroCallEvalResult(macCall); } } return; } auto invokeMethodFuncFromC = reinterpret_cast(RuntimeInit::GetInstance().runtimeMethodFunc); if (enableParallelMacro) { // For parallel macro expansion. macCall.coroutineHandle = invokeMethodFuncFromC(InvokeMacroFunc, &macCall); CJC_NULLPTR_CHECK(macCall.coroutineHandle); } else { // For serial macro expansion. MacroInvoke mi(&macCall); // here we only have one main-thead, so we wouldn't have multi-thread change the attrTksBytes. // make sure that we get the mutex before invoke thread. std::unique_lock lck(mi.mtx); macCall.coroutineHandle = invokeMethodFuncFromC(InvokeMacro, &mi); CJC_NULLPTR_CHECK(macCall.coroutineHandle); mi.cv.wait(lck, [&macCall] { return macCall.isDataReady; }); ReleaseThreadHandle(macCall); } } void MacroEvaluation::InitGlobalVariable() { // Find RunCJTask and ReleaseHandle from runtime dylib. auto invokeMethodFuncFromC = reinterpret_cast(RuntimeInit::GetInstance().runtimeMethodFunc); auto invokeReleaseHandleFromC = reinterpret_cast(RuntimeInit::GetInstance().runtimeReleaseFunc); for (auto& [mpkgname, hasInit] : usedMacroPkgs) { if (hasInit) { continue; } // method: _global_init$_reset / __global_init$_reset auto macrodef = mpkgname; auto method = MANGLE_CANGJIE_PREFIX + MANGLE_GLOBAL_PACKAGE_INIT_PREFIX + MangleUtils::GetOptPkgName(macrodef) + SPECIAL_NAME_FOR_INIT_RESET_FUNCTION + MANGLE_FUNC_PARAM_TYPE_PREFIX + MANGLE_VOID_TY_SUFFIX; bool findGlobalFunc = false; for (auto& handle : InvokeRuntime::GetOpenedLibHandles()) { auto initGlobalFunc = reinterpret_cast(GetMethod(handle, method.c_str())); if (!initGlobalFunc) { continue; } MacroInvoke mi(nullptr); // Get the mutex before invoke thread. std::unique_lock lck(mi.mtx); auto resetNotify = std::make_unique(reinterpret_cast(ResetVariableFinishNotify), &mi); auto coroutineHandle = invokeMethodFuncFromC(initGlobalFunc, resetNotify.get()); CJC_NULLPTR_CHECK(coroutineHandle); // Unlock the mutex after invoke thread notify. mi.cv.wait(lck, [&mi] { return mi.isDataReady; }); invokeReleaseHandleFromC(coroutineHandle); findGlobalFunc = true; usedMacroPkgs.at(mpkgname) = true; break; } // Can not find global reset func. if (!findGlobalFunc) { Warningln("could not find global reset method in macro dylib: ", method); } } } void MacroEvaluation::ReleaseThreadHandle(MacroCall& macCall) { auto invokeReleaseHandle = reinterpret_cast(RuntimeInit::GetInstance().runtimeReleaseFunc); invokeReleaseHandle(macCall.coroutineHandle); }cangjie_compiler-1.0.7/src/Macro/MacroEvaluationClient.cpp000066400000000000000000000543441510705540100236050ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements macro evaluation apis for macro child process. */ #include "cangjie/Macro/MacroEvaluation.h" #include "cangjie/Utils/ProfileRecorder.h" #if defined(__linux__) || defined(__APPLE__) #include #include #include #else #include #endif #ifdef __linux__ #include #endif #include #include using namespace Cangjie; using namespace AST; using namespace InvokeRuntime; MacroEvalMsgSerializer MacroEvaluation::msgSlzer; namespace { const std::string MACRO_SRV_NAME = "LSPMacroServer"; void SignalHandler(int) { Cangjie::MacroProcMsger::GetInstance().CloseMacroSrv(); _exit(1); } void SetExitSignal(void) { std::signal(SIGABRT, SignalHandler); std::signal(SIGINT, SignalHandler); std::signal(SIGTERM, SignalHandler); std::signal(SIGSEGV, SignalHandler); } inline bool IsResultForMacCall(const std::string& id, const Position& pos, const MacroInvocation& mi) { if (id != mi.identifier) { return false; } if (pos != mi.identifierPos) { return false; } if (pos.fileID != mi.identifierPos.fileID) { return false; } return true; } } // namespace // MacroProcMsger for client #ifdef _WIN32 void MacroProcMsger::SetSrvPipeHandle(HANDLE hRead, HANDLE hWrite) { hChildRead = hRead; hChildWrite = hWrite; } #else void MacroProcMsger::SetSrvPipeHandle(int hRead, int hWrite) { pipefdP2C[0] = hRead; pipefdC2P[1] = hWrite; } #endif void MacroProcMsger::CloseClientResource() { #ifdef _WIN32 if (hParentRead != nullptr) { if (CloseHandle(hParentRead) == TRUE) { hParentRead = nullptr; } else { Errorln("CloseHandle hParentRead error"); } } if (hParentWrite != nullptr) { if (CloseHandle(hParentWrite) == TRUE) { hParentWrite = nullptr; } else { Errorln("CloseHandle hParentWrite error"); } } if (hProcess != nullptr) { if (CloseHandle(hProcess) == TRUE) { hProcess = nullptr; } else { Errorln("CloseHandle hProcess error"); } } if (hThread != nullptr) { if (CloseHandle(hThread) == TRUE) { hThread = nullptr; } else { Errorln("CloseHandle hThread error"); } } #else if (pipefdP2C[1] != -1) { close(pipefdP2C[1]); pipefdP2C[1] = -1; } if (pipefdC2P[0] != -1) { close(pipefdC2P[0]); pipefdC2P[0] = -1; } #endif } void MacroProcMsger::CloseMacroSrv() { if (macroSrvRun.load()) { std::vector msg; MacroEvalMsgSerializer msgSlzer; msgSlzer.SerializeExitMsg(msg); if (SendMsgToSrv(msg)) { auto start = std::chrono::steady_clock::now(); auto end = std::chrono::steady_clock::now(); std::chrono::duration elapsedSeconds = end - start; double waitTime{5.0}; while (macroSrvRun.load() && (elapsedSeconds.count() < waitTime)) { end = std::chrono::steady_clock::now(); elapsedSeconds = end - start; } if (elapsedSeconds.count() >= waitTime) { Errorln(getpid(), " wait macro srv exit time out ", waitTime); } } else { std::cerr << getpid() << ": error Send Exit Task" << std::endl; } } CloseClientResource(); return; } bool MacroProcMsger::WriteToSrvPipe(const uint8_t* buf, size_t size) const { #ifdef _WIN32 return WriteFile(hParentWrite, buf, size, nullptr, 0) == TRUE; #else ssize_t res = write(pipefdP2C[1], buf, size); while (res >= 0 && res < static_cast(size)) { size -= static_cast(res); buf += res; res = write(pipefdP2C[1], buf, size); } return res >= 0; #endif } bool MacroProcMsger::ReadFromSrvPipe(uint8_t* buf, size_t size) const { #ifdef _WIN32 return ReadFile(hParentRead, buf, size, nullptr, nullptr) == TRUE; #else ssize_t res = read(pipefdC2P[0], buf, size); // res == 0, means end of file; res == -1, indicates error accurred while (res > 0 && res < static_cast(size)) { size -= static_cast(res); buf += res; res = read(pipefdC2P[0], buf, size); } return res > 0; #endif } bool MacroProcMsger::SendMsgToSrv(const std::vector& msg) { if (msg.empty()) { return false; } if (pipeError.load()) { return false; } // Pipe capacity is limited, send msg slice by slice size_t sumSize = msg.size(); size_t rest = sumSize % msgSliceLen; size_t msgNum = rest == 0 ? msg.size() / msgSliceLen : msg.size() / msgSliceLen + 1; size_t lastSize = rest == 0 ? msgSliceLen : rest; if (!WriteToSrvPipe(reinterpret_cast(&sumSize), sizeof(sumSize))) { perror("WriteToSrvPipe"); pipeError.store(true); return false; } for (size_t i = 0; i < msgNum; ++i) { size_t curNum = (i == msgNum - 1) ? lastSize : msgSliceLen; if (!WriteToSrvPipe(msg.data() + i * msgSliceLen, curNum)) { perror("WriteToSrvPipe"); pipeError.store(true); return false; } } return true; } bool MacroProcMsger::ReadMsgFromSrv(std::vector& msg) { size_t msgSize{0}; msg.clear(); if (pipeError.load()) { return false; } if (!ReadFromSrvPipe(reinterpret_cast(&msgSize), sizeof(msgSize))) { perror("ReadFromSrvPipe"); pipeError.store(true); return false; } if (msgSize == 0) { perror("ReadFromSrvPipe size err"); pipeError.store(true); return false; } size_t rest = msgSize % msgSliceLen; size_t msgNum = rest == 0 ? msgSize / msgSliceLen : msgSize / msgSliceLen + 1; size_t lastSize = rest == 0 ? msgSliceLen : rest; msg.resize(msgSize); for (size_t i = 0; i < msgNum; ++i) { size_t curNum = (i == msgNum - 1) ? lastSize : msgSliceLen; if (!ReadFromSrvPipe(msg.data() + i * msgSliceLen, curNum)) { perror("ReadFromSrvPipe"); pipeError.store(true); return false; } } return true; } bool MacroProcMsger::ReadAllMsgFromSrv(std::list>& msgVec) { msgVec.clear(); std::vector msg; bool errFlag = true; while (ReadMsgFromSrv(msg)) { msgVec.push_back(msg); // Check whether there is any msg in pipe #ifdef _WIN32 // if totalBytesAvail > 0, needs to keep reading DWORD totalBytesAvail; if (PeekNamedPipe(hParentRead, NULL, 0, nullptr, &totalBytesAvail, nullptr) == FALSE) { Errorln("PeekNamedPipe failed with error %d", GetLastError()); return false; } if (totalBytesAvail == 0) { return true; } #else fd_set readset; FD_ZERO(&readset); FD_SET(pipefdC2P[0], &readset); timeval timeout = {0, 0}; int ret = select(pipefdC2P[0] + 1, &readset, nullptr, nullptr, &timeout); if (ret == -1) { perror("srv proc select pipe fail, stop read."); return false; } else if (ret == 0) { return true; } else if (FD_ISSET(pipefdC2P[0], &readset)) { continue; } else { perror("srv proc FD_ISSET fail, stop read."); return false; } #endif } return !errFlag; } void MacroEvaluation::DeserializeMacroCallsResult( std::list& calls, const std::list>& msgList) const { std::string id; Position pos; for (auto& msg : msgList) { MacroEvalMsgSerializer::DeSerializeIdInfoFromResult(id, pos, msg); bool findFlag{false}; for (auto& mc : std::as_const(calls)) { auto pInvocation = mc->GetInvocation(); if (!IsResultForMacCall(id, pos, *pInvocation)) { continue; } findFlag = true; MacroEvalMsgSerializer::DeSerializeTksFromResult(pInvocation->newTokens, msg); mc->status = MacroEvalMsgSerializer::DeSerializeStatusFromResult(msg); MacroEvalMsgSerializer::DeSerializeItemsFromResult(mc->items, msg); MacroEvalMsgSerializer::DeSerializeAssertParentsFromResult(mc->assertParents, msg); std::vector diags; MacroEvalMsgSerializer::DeSerializeDiagsFromResult(diags, msg); for (auto& diag : diags) { mc->DiagReport(static_cast(diag.diagSeverity), MakeRange(diag.start, diag.end), diag.errorMessage.c_str(), diag.mainHint.str.c_str()); } mc->isDataReady = true; for (auto& parentName : mc->assertParents) { (void)ci->diag.Diagnose( mc->GetBeginPos(), DiagKind::macro_assert_parent_context_failed, mc->GetFullName(), parentName); } break; } if (!findFlag) { Errorln("DeserializeMacroCallsResult not find macCall for result ", id, pos.ToString()); } } } // Client bool MacroEvaluation::SendMacroDefTask(const std::unordered_set& macroLibs) const { std::vector msg; msgSlzer.SerializeDeflibMsg(macroLibs, msg); if (!MacroProcMsger::GetInstance().SendMsgToSrv(msg)) { std::cerr< msg; if (!MacroProcMsger::GetInstance().ReadMsgFromSrv(msg)) { std::cerr<diag.Diagnose(DiagKind::can_not_open_macro_library, dyfile); } return; } /* Send a macrocall task for serial macro expansion. */ bool MacroEvaluation::SendMacroCallTask(MacroCall& call) const { std::vector msg; msgSlzer.SerializeMacroCallMsg(call, msg); if (!MacroProcMsger::GetInstance().SendMsgToSrv(msg)) { std::cerr< msg; msgSlzer.SerializeExitMsg(msg, false); if (!MacroProcMsger::GetInstance().SendMsgToSrv(msg)) { std::cerr << getpid() << ": error Send exit stg false" << std::endl; } } /* Wait for a macrocall eval result for serial macro expansion. */ void MacroEvaluation::WaitMacroCallEvalResult(MacroCall& call) const { std::list> resBuf; if (!MacroProcMsger::GetInstance().ReadAllMsgFromSrv(resBuf)) { std::cerr<newTokens, resBuf.front()); call.status = MacroEvalMsgSerializer::DeSerializeStatusFromResult(resBuf.front()); MacroEvalMsgSerializer::DeSerializeItemsFromResult(call.items, resBuf.front()); MacroEvalMsgSerializer::DeSerializeAssertParentsFromResult(call.assertParents, resBuf.front()); std::vector diags; MacroEvalMsgSerializer::DeSerializeDiagsFromResult(diags, resBuf.front()); for (auto& diag : diags) { call.DiagReport(static_cast(diag.diagSeverity), MakeRange(diag.start, diag.end), diag.errorMessage.c_str(), diag.mainHint.str.c_str()); } call.isDataReady = true; // Check assertParentContext failed or not in child process. for (auto& parentName : call.assertParents) { (void)ci->diag.Diagnose(call.GetBeginPos(), DiagKind::macro_assert_parent_context_failed, call.GetFullName(), parentName); } return; } /* Send macrocalls task for parallel macro expansion. */ bool MacroEvaluation::SendMacroCallsTask(std::list& calls) const { std::vector msgData; std::list needSendCalls; for (auto& mc : std::as_const(calls)) { if (mc->hasSend) { continue; } auto name = mc->GetIdentifier() + mc->GetBeginPos().ToString(); Utils::ProfileRecorder::Start("Parallel Evaluate Macros", name); needSendCalls.push_back(mc); mc->hasSend = true; } msgSlzer.SerializeMultiCallsMsg(needSendCalls, msgData); // If all macrocall tasks have been sent to the server already, the msgs are empty, // but we need to send the MsgContent_multiCalls to the server to wait for the result of unfinished macrocall. if (!MacroProcMsger::GetInstance().SendMsgToSrv(msgData)) { std::cerr<status = MacroEvalStatus::FAIL; } return false; } return true; } /* Wait for macrocall eval result for parallel macro expansion */ bool MacroEvaluation::WaitMacroCallsEvalResult(std::list& calls) const { const auto foundEvalmc = std::find_if(calls.cbegin(), calls.cend(), [](auto& mc) { return !mc->isDataReady; }); if (foundEvalmc == calls.end()) { // All macrocalls eval finish. return true; } std::list> msgList; if (!MacroProcMsger::GetInstance().ReadAllMsgFromSrv(msgList)) { std::cerr<status = MacroEvalStatus::FAIL; } return false; } DeserializeMacroCallsResult(calls, msgList); return true; } #if defined(__linux__) || defined(__APPLE__) namespace { static void WaitProcessExit(pid_t pid) { [[maybe_unused]] int status; pid_t ret = waitpid(pid, &status, 0); // Wait child process exit MacroProcMsger::GetInstance().macroSrvRun.store(false); if (ret == -1) { Errorln("Error waiting for macro srv process"); } } static void CloseBothEndsOfPipe(int* pipe) { close(pipe[0]); close(pipe[1]); pipe[0] = -1; pipe[1] = -1; } inline void RedirectStdOutTOStdErr() { int ret = dup2(STDERR_FILENO, STDOUT_FILENO); if (ret == -1) { perror("macro srv dup2 from STDOUT_FILENO to STDERR_FILENO fail"); } } } // namespace void MacroEvaluation::ExecMacroSrv(pid_t pid) const { // args std::vector cstrings; std::string macSrvName = MACRO_SRV_NAME; std::string hRead = std::to_string(MacroProcMsger::GetInstance().pipefdP2C[0]); std::string hWrite = std::to_string(MacroProcMsger::GetInstance().pipefdC2P[1]); std::string enPara = enableParallelMacro ? "1" : "0"; std::string pidStr = std::to_string(pid); cstrings.push_back(macSrvName.data()); cstrings.push_back(hRead.data()); cstrings.push_back(hWrite.data()); cstrings.push_back(enPara.data()); cstrings.push_back(ci->invocation.globalOptions.executablePath.data()); cstrings.push_back(pidStr.data()); cstrings.push_back(nullptr); // for execvp argv execvp(macSrvName.c_str(), cstrings.data()); } void MacroEvaluation::CreateMacroSrvProcess() { [[maybe_unused]] std::lock_guard lg(MacroProcMsger::GetInstance().mutex); if (MacroProcMsger::GetInstance().macroSrvRun.load()) { return; } // close old pipe MacroProcMsger::GetInstance().CloseClientResource(); // create pipe if (pipe(MacroProcMsger::GetInstance().pipefdP2C) == -1) { perror("Create P2C pipe fail: "); return; } if (pipe(MacroProcMsger::GetInstance().pipefdC2P) == -1) { CloseBothEndsOfPipe(MacroProcMsger::GetInstance().pipefdP2C); perror("Create C2P pipe fail: "); return; } MacroProcMsger::GetInstance().pipeError.store(false); pid_t ppid = getpid(); pid_t pid = fork(); if (pid < 0) { CloseBothEndsOfPipe(MacroProcMsger::GetInstance().pipefdP2C); CloseBothEndsOfPipe(MacroProcMsger::GetInstance().pipefdC2P); return; } MacroProcMsger::GetInstance().macroSrvRun.store(true); if (pid == 0) { // child process #ifdef __linux__ if (prctl(PR_SET_PDEATHSIG, SIGHUP) == -1) { perror("PR_SET_PDEATHSIG: "); } #endif RedirectStdOutTOStdErr(); ExecMacroSrv(ppid); // if exec fails, the following code will be run: perror("run macro srv in fork, due to exec fail"); RunMacroSrv(); } else { // main process SetExitSignal(); std::thread w(WaitProcessExit, pid); w.detach(); // close unused pipe close(MacroProcMsger::GetInstance().pipefdP2C[0]); close(MacroProcMsger::GetInstance().pipefdC2P[1]); } return; } #else void WaitProcessExit(PROCESS_INFORMATION pi) { if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) { Errorln("Getting child process exit code: ", GetLastError()); } MacroProcMsger::GetInstance().macroSrvRun.store(false); } // if parent process exits child proc should also exit void CreateJobObjectForMacroSrv() { HANDLE ghJob = CreateJobObject(nullptr, nullptr); if (ghJob == nullptr) { Errorln("Create job object for macro srv fail!"); return; } else { JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0}; // Configure all child processes associated with the job to terminate when the job end jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; if (0 == SetInformationJobObject(ghJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli))) { Errorln("Could not SetInformationJobObject for macro srv"); return; } } if (AssignProcessToJobObject(ghJob, MacroProcMsger::GetInstance().hProcess) == FALSE) { Errorln("Assign process to JobObject fail!"); } } void RedirectStdOutForMarcoSrv(STARTUPINFO& si) { HANDLE hParentErr = GetStdHandle(STD_ERROR_HANDLE); si.hStdOutput = hParentErr; si.hStdError = hParentErr; si.dwFlags = STARTF_USESTDHANDLES; } void CloseSrvPipe() { if (MacroProcMsger::GetInstance().hChildWrite != nullptr) { if (CloseHandle(MacroProcMsger::GetInstance().hChildWrite) == TRUE) { MacroProcMsger::GetInstance().hChildWrite = nullptr; } else { Errorln("CloseHandle hParentRead error"); } } if (MacroProcMsger::GetInstance().hChildRead != nullptr) { if (CloseHandle(MacroProcMsger::GetInstance().hChildRead) == TRUE) { MacroProcMsger::GetInstance().hChildRead = nullptr; } else { Errorln("CloseHandle hParentWrite error"); } } } bool CreateMacroMsgPipe() { SECURITY_ATTRIBUTES sa = {0}; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; BOOL bRet = CreatePipe(&MacroProcMsger::GetInstance().hParentRead, &MacroProcMsger::GetInstance().hChildWrite, &sa, 0); if (bRet == FALSE) { Errorln("Create macro srv pipe fail!"); return false; } bRet = CreatePipe(&MacroProcMsger::GetInstance().hChildRead, &MacroProcMsger::GetInstance().hParentWrite, &sa, 0); if (bRet == FALSE) { CloseSrvPipe(); MacroProcMsger::GetInstance().CloseClientResource(); Errorln("Create macro srv pipe fail!"); return false; } return true; } std::string GetMacroSrvCmd(bool enableParallelMacro, std::string& cjcPath) { std::string cmdStr = MACRO_SRV_NAME + ".exe"; const size_t buffLen = 20; char handlebuffer[buffLen]; sprintf_s(handlebuffer, buffLen, "%d", MacroProcMsger::GetInstance().hChildRead); cmdStr += " " + std::string(handlebuffer); sprintf_s(handlebuffer, buffLen, "%d", MacroProcMsger::GetInstance().hChildWrite); cmdStr += " " + std::string(handlebuffer); cmdStr += enableParallelMacro ? " 1" : " 0"; cmdStr += " \"" + cjcPath +"\""; // cjc folder to find runtime for lsp not in sdk return cmdStr; } void MacroEvaluation::CreateMacroSrvProcess() { [[maybe_unused]] std::lock_guard lg(MacroProcMsger::GetInstance().mutex); if (MacroProcMsger::GetInstance().macroSrvRun.load()) { return; } // close old resource MacroProcMsger::GetInstance().CloseClientResource(); if (!CreateMacroMsgPipe()) { return; } MacroProcMsger::GetInstance().pipeError.store(false); // The LSP uses the std out for communication. To avoid affecting it, the std out is redirected to the std err. STARTUPINFO si = {0}; si.cb = sizeof(si); RedirectStdOutForMarcoSrv(si); PROCESS_INFORMATION pi; BOOL bRet = CreateProcess(nullptr, const_cast(GetMacroSrvCmd(enableParallelMacro, ci->invocation.globalOptions.executablePath).c_str()), nullptr, nullptr, TRUE, NULL, nullptr, nullptr, &si, &pi); if (bRet == FALSE) { CloseSrvPipe(); MacroProcMsger::GetInstance().CloseClientResource(); Errorln("Create ", MACRO_SRV_NAME, " fail!"); return; } MacroProcMsger::GetInstance().macroSrvRun.store(true); std::thread w(WaitProcessExit, pi); w.detach(); MacroProcMsger::GetInstance().hProcess = pi.hProcess; MacroProcMsger::GetInstance().hThread = pi.hThread; CreateJobObjectForMacroSrv(); CloseSrvPipe(); return; } #endifcangjie_compiler-1.0.7/src/Macro/MacroEvaluationSrv.cpp000066400000000000000000000306011510705540100231270ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements macro evaluation server related apis for macro. */ #include "cangjie/Macro/MacroEvaluation.h" #if defined (__linux__) || defined(__APPLE__) #include #include #include #include #endif #ifdef __linux__ #include #endif using namespace Cangjie; using namespace AST; namespace { enum class TaskType : unsigned int { FIND_MACRO_DEFLIB, EVAL_MACRO_CALL, EXIT_MACRO_SRV, EXIT_MACRO_STG, }; inline TaskType GetMacroTaskType(const std::vector& msg) { switch (MacroEvalMsgSerializer::GetMacroMsgContenType(msg)) { case MacroMsgFormat::MsgContent_defLib: return TaskType::FIND_MACRO_DEFLIB; case MacroMsgFormat::MsgContent_multiCalls: return TaskType::EVAL_MACRO_CALL; case MacroMsgFormat::MsgContent_exitTask: if (!MacroMsgFormat::GetMacroMsg(msg.data())->content_as_exitTask()->flag()) { return TaskType::EXIT_MACRO_STG; } [[fallthrough]]; // need exit case MacroMsgFormat::MsgContent_NONE: [[fallthrough]]; // need exit case MacroMsgFormat::MsgContent_macroResult: [[fallthrough]]; // need exit default: break; } return TaskType::EXIT_MACRO_SRV; } } // namespace // MacroProcMsger for srv bool MacroProcMsger::WriteToClientPipe(const uint8_t* buf, size_t size) const { #ifdef _WIN32 return WriteFile(hChildWrite, buf, size, nullptr, nullptr) == TRUE; #else ssize_t res = write(pipefdC2P[1], buf, size); while (res >= 0 && res < static_cast(size)) { size -= static_cast(res); buf += res; res = write(pipefdC2P[1], buf, size); } return res >= 0; #endif } bool MacroProcMsger::ReadFromClientPipe(uint8_t* buf, size_t size) const { #ifdef _WIN32 return ReadFile(hChildRead, buf, size, nullptr, nullptr) == TRUE; #else ssize_t res = read(pipefdP2C[0], buf, size); // res == 0, means end of file; res == -1, indicates error accurred while (res > 0 && res < static_cast(size)) { size -= static_cast(res); buf += res; res = read(pipefdP2C[0], buf, size); } return res > 0; #endif } bool MacroProcMsger::SendMsgToClient(const std::vector& msg) { if (msg.empty()) { return false; } size_t sumSize = msg.size(); size_t rest = sumSize % msgSliceLen; size_t msgNum = rest == 0 ? msg.size() / msgSliceLen : msg.size() / msgSliceLen + 1; size_t lastSize = rest == 0 ? msgSliceLen : rest; if (!WriteToClientPipe(reinterpret_cast(&sumSize), sizeof(sumSize))) { perror("WriteToClientPipe"); return false; } for (size_t i = 0; i < msgNum; ++i) { size_t curNum = (i == msgNum - 1) ? lastSize : msgSliceLen; if (!WriteToClientPipe(msg.data() + i * msgSliceLen, curNum)) { perror("WriteToClientPipe"); return false; } } return true; } bool MacroProcMsger::ReadMsgFromClient(std::vector& msg) { size_t msgSize{0}; msg.clear(); if (!ReadFromClientPipe(reinterpret_cast(&msgSize), sizeof(msgSize))) { perror("ReadFromClientPipe"); return false; } if (msgSize == 0) { Errorln(getpid(), " Msg size error, size: ", msgSize); return false; } size_t rest = msgSize % msgSliceLen; size_t msgNum = rest == 0 ? msgSize / msgSliceLen : msgSize / msgSliceLen + 1; size_t lastSize = rest == 0 ? msgSliceLen : rest; msg.resize(msgSize); for (size_t i = 0; i < msgNum; ++i) { size_t curNum = (i == msgNum - 1) ? lastSize : msgSliceLen; if (!ReadFromClientPipe(msg.data() + i * msgSliceLen, curNum)) { perror("ReadFromClientPipe"); return false; } } return true; } #ifdef __linux__ namespace { inline void RenameSrvProcess() { std::string subName = "msrv" + std::to_string(getpid()); size_t maxProcNameSize = 15; if (subName.size() > maxProcNameSize) { subName = subName.substr(0, maxProcNameSize); } int ret = prctl(PR_SET_NAME, subName.c_str()); if (ret == -1) { Errorln(getpid(), "Rename macro srv to ", subName, " fail"); return; } } } // namespace #endif #if defined(__linux__) || defined(__APPLE__) void MacroEvaluation::RunMacroSrv() { useChildProcess = false; // macro srv is child #ifdef __linux__ RenameSrvProcess(); #endif #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND RuntimeInit::GetInstance().InitRuntime( ci->invocation.GetRuntimeLibPath(), ci->invocation.globalOptions.environment.allVariables); #endif // close unused pipe close(MacroProcMsger::GetInstance().pipefdP2C[1]); close(MacroProcMsger::GetInstance().pipefdC2P[0]); ExecuteEvalSrvTask(); RuntimeInit::GetInstance().CloseRuntime(); close(MacroProcMsger::GetInstance().pipefdP2C[0]); close(MacroProcMsger::GetInstance().pipefdC2P[1]); exit(0); } #endif // Server (macro srv process) #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool MacroEvaluation::FindDef(const std::vector& msg) const { std::list macroLibs; std::string resMsg{"RespondFindDef "}; MacroEvalMsgSerializer::DeSerializeDeflibMsg(macroLibs, msg); for (auto& obj : std::as_const(macroLibs)) { auto dyfile = FileUtil::NormalizePath(obj); auto handle = InvokeRuntime::OpenSymbolTable(dyfile); if (!handle) { resMsg += dyfile; break; } InvokeRuntime::SetOpenedLibHandles(handle); } std::vector res(resMsg.begin(), resMsg.end()); if (!MacroProcMsger::GetInstance().SendMsgToClient(res)) { std::cerr << getpid() << ": error Respond FindDef" << std::endl; return false; } return true; } #endif bool MacroEvaluation::SerializeAndNotifyResult(MacroCall& macCall) const { auto pInvocation = macCall.GetInvocation(); if (pInvocation && MacroExpandFailed(pInvocation->newTokens)) { // Check eval result and set status failed. macCall.status = MacroEvalStatus::FAIL; } // Notify the macrocall result to client. std::vector mcResult; msgSlzer.SerializeMacroCallResultMsg(macCall, mcResult); if (!MacroProcMsger::GetInstance().SendMsgToClient(mcResult)) { std::cerr << getpid() << ": error MacroCall Result" << std::endl; return false; } return true; } /* Eval macrocalls in parallel mode, and Wait for a macrocall to eval complete and notify the result. */ bool MacroEvaluation::EvalMacroCallsAndWaitResult() { // Find macrodef's method from dylib. for (auto& mc : std::as_const(macCalls)) { if (mc->status != MacroEvalStatus::INIT) { continue; } if (!mc->FindMacroDefMethod(ci)) { mc->status = MacroEvalStatus::FAIL; if (!SerializeAndNotifyResult(*mc)) { return false; } continue; } SaveUsedMacros(*mc); } const auto foundEvalMacroCall = std::find_if(macCalls.cbegin(), macCalls.cend(), [](auto& mc) { return mc->status != MacroEvalStatus::FAIL; }); if (foundEvalMacroCall == macCalls.end()) { // All macrocalls FindMacroDef failed. return true; } // Init global variable of macrodef package before parallel macro expansion. InitGlobalVariable(); // ParallelMacro, evaluate macrocall in runtime coroutine. for (auto& mc : std::as_const(macCalls)) { if (mc->status != MacroEvalStatus::INIT) { continue; } EvalOneMacroCall(*mc); mc->status = MacroEvalStatus::EVAL; } // Wait for a macrocall evaluation to complete and notify the result. bool bEvalFinish = false; while (!bEvalFinish) { auto iter = macCalls.begin(); while (iter != macCalls.end()) { auto& macCall = *iter; // Wait for a macroCall eval complete. if (!macCall->isDataReady) { (void)++iter; continue; } // Release coroutine handle after eval complete. ReleaseThreadHandle(*macCall); if (!SerializeAndNotifyResult(*macCall)) { return false; } (void)macCalls.erase(iter); bEvalFinish = true; break; } } return true; } std::unique_ptr MacroEvaluation::CreateMacroExpand(const MacroMsgFormat::MacroCall& callFmt) const { auto med = std::make_unique(); MacroEvalMsgSerializer::DeSerializeRangeFromCall(med->begin, med->end, callFmt); auto pInvocation = med->GetInvocation(); MacroEvalMsgSerializer::DeSerializeIdInfoFromCall(pInvocation->identifier, pInvocation->identifierPos, callFmt); MacroEvalMsgSerializer::DeSerializeArgsFromCall(pInvocation->args, callFmt); MacroEvalMsgSerializer::DeSerializeAttrsFromCall(pInvocation->attrs, callFmt); pInvocation->hasAttr = callFmt.hasAttrs(); return med; } void MacroEvaluation::DeSerializeMacroCall(const MacroMsgFormat::MacroCall& callFmt) { macDecls.emplace_back(CreateMacroExpand(callFmt)); auto macCall = std::make_unique(macDecls.back().get()); MacroEvalMsgSerializer::DeSerializeParentNamesFromCall(macCall->parentNames, callFmt); MacroEvalMsgSerializer::DeSerializeChildMsgesFromCall(macCall->childMessages, callFmt); macCall->methodName = callFmt.methodName()->str(); macCall->packageName = callFmt.packageName()->str(); macCall->libPath = callFmt.libPath()->str(); macCall->ci = ci; macCalls.emplace_back(std::move(macCall)); } bool MacroEvaluation::EvalMacroCall(std::vector& msg) { if (MacroEvalMsgSerializer::GetMacroMsgContenType(msg) == MacroMsgFormat::MsgContent::MsgContent_multiCalls) { // callsOffset->calls() may be empty, but need to wait macrocall eval result for parallel macro. auto callsOffset = MacroMsgFormat::GetMacroMsg(msg.data())->content_as_multiCalls(); flatbuffers::uoffset_t callSize = callsOffset->calls()->size(); for (flatbuffers::uoffset_t i = 0; i < callSize; i++) { DeSerializeMacroCall(*callsOffset->calls()->Get(i)); } } if (macCalls.empty()) { return false; } if (enableParallelMacro) { // Parallel macro expansion. return EvalMacroCallsAndWaitResult(); } // Serial macro expansion. auto macCall = macCalls.back().get(); if (!macCall) { Errorln("cannot find macro method."); return false; } if (!macCall->FindMacroDefMethod(ci)) { Errorln("cannot find macro method ", macCall->methodName); return false; } SaveUsedMacroPkgs(macCall->packageName); InitGlobalVariable(); EvalOneMacroCall(*macCall); return SerializeAndNotifyResult(*macCall); } void MacroEvaluation::ResetForNextEval() { usedMacroPkgs.clear(); // for global var macDecls.clear(); macCalls.clear(); ci->diag.Reset(); } void MacroEvaluation::ExecuteEvalSrvTask() { std::vector msgInf; for (;;) { if (!MacroProcMsger::GetInstance().ReadMsgFromClient(msgInf)) { Errorln(getpid(), " Macro srv read message fail"); break; } TaskType msgT = GetMacroTaskType(msgInf); bool exit = false; switch (msgT) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND case TaskType::FIND_MACRO_DEFLIB: if (!FindDef(msgInf)) { Errorln(getpid(), " Macro srv find define fail"); exit = true; } break; #endif case TaskType::EVAL_MACRO_CALL: if (!EvalMacroCall(msgInf)) { Errorln(getpid(), " Macro srv eval macro call fail"); exit = true; } break; case TaskType::EXIT_MACRO_STG: ResetForNextEval(); break; case TaskType::EXIT_MACRO_SRV: [[fallthrough]]; // need exit default: exit = true; break; } if (exit) { break; } } } cangjie_compiler-1.0.7/src/Macro/MacroExpansion.cpp000066400000000000000000000444231510705540100223000ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/Macro/MacroExpansion.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Basic/Print.h" #include "cangjie/ConditionalCompilation/ConditionalCompilation.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Macro/MacroEvaluation.h" #include "cangjie/Macro/TestEntryConstructor.h" #include "cangjie/Parse/Parser.h" #include #include #include "MacroUpdateInfo.h" using namespace Cangjie; using namespace AST; namespace { bool HasNoMacros(const MacroCollector& mc) { return mc.macroDefFuncs.empty() && mc.macCalls.empty(); } bool HasNoMacroCalls(const std::vector& macCalls) { return macCalls.empty(); } bool HasDefAndCallInSamePkg(const MacroCollector& macroCollector, DiagnosticEngine& diag) { // macro-def and macro-call can't be in the same package. bool ret = false; std::unordered_set s1; for (auto fd : macroCollector.macroDefFuncs) { (void)s1.insert(fd->identifier); } for (auto call : macroCollector.macCalls) { if (s1.find(call.GetFullName()) != s1.end()) { ret = true; (void)diag.Diagnose(call.GetBeginPos(), DiagKind::macro_unexpect_def_and_call_in_same_pkg); } } return ret; } bool HasMacroCallInNode(const OwnedPtr& node, DiagnosticEngine& diag) { bool hasMacroCall = false; auto checkMacroCall = [&hasMacroCall, &node, &diag](Ptr curNode) -> VisitAction { if (curNode->IsMacroCallNode()) { hasMacroCall = true; (void)diag.Diagnose(*node, curNode->GetConstInvocation()->identifierPos, DiagKind::macro_undeclared_identifier, curNode->GetConstInvocation()->identifier); return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; Walker macWalker(node.get(), checkMacroCall); macWalker.Walk(); return hasMacroCall; } void CollectMacroEachFile(File& file, MacroCollector& collector) { auto collectFunc = [&collector](Ptr curNode) -> VisitAction { if (curNode->astKind == ASTKind::MACRO_DECL) { // macro definitions. (void)collector.macroDefFuncs.emplace_back(RawStaticCast(curNode)); } UpdateMacroInfo(curNode, collector); if (curNode->IsMacroCallNode()) { return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; Walker macWalker(&file, collectFunc); macWalker.Walk(); } void CheckUnhandledMacroCall(Package& package, DiagnosticEngine& diag) { if (diag.GetErrorCount() > 0) { // Already have diagnose error, no need to check. return; } for (const auto& file : package.files) { if (!file || !file->hasMacro) { continue; } // Check whether there are unhandled macrocalls. auto checkMacroCall = [&diag](Ptr curNode) -> VisitAction { // do not report on @IfAvailable, to be dealt with later if (auto me = DynamicCast(curNode); me && me->invocation.IsIfAvailable()) { return VisitAction::WALK_CHILDREN; } if (curNode->IsMacroCallNode()) { (void)diag.Diagnose(*curNode, curNode->GetConstInvocation()->identifierPos, DiagKind::macro_undeclared_identifier, curNode->GetConstInvocation()->identifier); return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; Walker mcWalker(file.get(), checkMacroCall); mcWalker.Walk(); } } void CheckWhenAfterMacroExpand(Ptr curNode, DiagnosticEngine& diag) { // Check whether there are unhandled macrocalls. auto checkWhen = [&diag](Ptr curNode) -> VisitAction { if (auto decl = DynamicCast(curNode); decl) { auto annotation = std::find_if(decl->annotations.begin(), decl->annotations.end(), [](auto& anno) { return anno->kind == AST::AnnotationKind::WHEN; }); if (annotation != decl->annotations.end()) { diag.DiagnoseRefactor(DiagKindRefactor::conditional_compilation_unexpected_after_macro, **annotation); } } return VisitAction::WALK_CHILDREN; }; ConstWalker walker(curNode, checkWhen); walker.Walk(); } } // namespace void MacroExpansion::ReplaceEachFileNode(const File& file) { auto debugFileID = ci->GetSourceManager().GetFileID(file.macroCallFilePath); if (debugFileID == -1) { return; } auto newBuffer = ci->GetSourceManager().GetSource(static_cast(debugFileID)).buffer; Parser newParser(static_cast(debugFileID), newBuffer, ci->diag, ci->diag.GetSourceManager(), ci->invocation.globalOptions.enableAddCommentToAst); auto names = Utils::SplitQualifiedName(file.curPackage->fullPackageName); std::string moduleName = names.size() > 1 ? names[0] : ""; // Only used for 'std' package case. (void)newParser.SetModuleName(moduleName).EnableCustomAnno(); auto newFile = newParser.ParseTopLevel(); ConditionalCompilation cc{ci}; cc.HandleFileConditionalCompilation(*newFile); if (curPackage) { ci->importManager.UpdateFileNodeImportInfo(*curPackage, file, newFile); } } void MacroExpansion::CollectMacros(Package& package) { // Each time we collect macros, we should reset the old one. macroCollector.clear(); macroCollector.curPkg = &package; size_t oldMacCallNum = 0; size_t oldMacDefNum = 0; for (const auto& file : package.files) { if (!file || !file->hasMacro) { continue; } oldMacCallNum = macroCollector.macCalls.size(); oldMacDefNum = macroCollector.macroDefFuncs.size(); CollectMacroEachFile(*file.get(), macroCollector); // it is certain that at least one of macCalls and macroDefFuncs will increase, because file->hasMacro is true. CJC_ASSERT(ci->diag.GetErrorCount() != 0 || ((macroCollector.macCalls.size() > oldMacCallNum || macroCollector.macroDefFuncs.size() > oldMacDefNum) && "Collect Macro Failed.")); } // The macroCall is not collected by order strictly, need sort. std::sort(macroCollector.macCalls.begin(), macroCollector.macCalls.end(), [](auto& m1, auto& m2) -> bool { auto pos1 = m1.GetBeginPos(); auto pos2 = m2.GetBeginPos(); return std::tie(pos1.fileID, pos1) < std::tie(pos2.fileID, pos2); }); macroCollector.importedMacroPkgs = ci->importManager.GetImportedPkgsForMacro(); } void MacroExpansion::CheckReplacedEnumCaseMember(MacroCall& macroNode, PtrVector& newNodes) const { auto invocation = macroNode.GetInvocation(); if (!invocation || !invocation->parent) { return; } if (invocation->parent->astKind != ASTKind::ENUM_DECL) { return; } auto ed = StaticAs(invocation->parent); if (macroNode.GetNode()->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { if (!newNodes[0].get()->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { (void)ci->diag.Diagnose(*newNodes[0].get(), DiagKind::macro_expect_enum_constructor); } if (newNodes.size() > 1) { (void)ci->diag.Diagnose(*newNodes[1].get(), DiagKind::macro_expect_one_enum_constructor); } if (newNodes[0]->astKind == ASTKind::FUNC_DECL) { ed->hasArguments = true; } } else { (void)std::for_each(newNodes.begin(), newNodes.end(), [&, this](auto& decl) { if (decl.get()->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { (void)ci->diag.Diagnose( *decl, DiagKind::macro_expect_decl_not_enum_constructor, decl->identifier.Val()); } }); } } template void MacroExpansion::CheckNodesConsistency( PtrVector& nodes, PtrVector& newNodes, VectorTarget>& target) const { (void)std::for_each(newNodes.begin(), newNodes.end(), [&, this](auto& node) { auto targetNode = dynamic_cast(node.get().get()); if (!targetNode) { (void)ci->diag.Diagnose(*node, DiagKind::macro_expect_declaration); return; } // Assign the outer decl. targetNode->outerDecl = (target.pointer->begin() + static_cast(target.loc))->get()->outerDecl; (void)nodes.emplace_back(static_cast(node.release())); }); } void MacroExpansion::ReplaceDecls( MacroCall& macroNode, PtrVector& newNodes, VectorTarget>& target) const { if (newNodes.empty()) { auto file = macroNode.GetNode()->curFile; if (file) { (void)file->originalMacroCallNodes.emplace_back(target.pointer->at(target.loc).release()); } (void)target.pointer->erase(target.pointer->begin() + static_cast(target.loc)); return; } PtrVector decls; CheckNodesConsistency(decls, newNodes, target); if (decls.empty()) { return; } CheckReplacedEnumCaseMember(macroNode, decls); (void)decls[0]->curFile->originalMacroCallNodes.emplace_back(target.pointer->at(target.loc).release()); (void)target.pointer->erase(target.pointer->begin() + static_cast(target.loc)); // Erase macro Node. (void)target.pointer->insert(target.pointer->begin() + static_cast(target.loc), // Insert real macro Node. std::make_move_iterator(decls.begin()), std::make_move_iterator(decls.end())); } void MacroExpansion::CheckReplacedFuncParamList( const MacroCall& macroNode, const VectorTarget>& target) const { auto invocation = macroNode.GetInvocation(); if (!invocation || !invocation->parent) { return; } if (invocation->parent->astKind != ASTKind::FUNC_PARAM_LIST) { return; } bool meetNamedParameter = false; for (auto it = target.pointer->begin(); it != target.pointer->end(); ++it) { if (!meetNamedParameter && (*it)->isNamedParam) { meetNamedParameter = true; } else if (meetNamedParameter && !(*it)->isNamedParam) { (void)ci->diag.Diagnose(**it, DiagKind::macro_named_parameter_after_unnamed); } } } void MacroExpansion::ReplaceParams( MacroCall& macroNode, PtrVector& newNodes, VectorTarget>& target) const { if (newNodes.empty()) { (void)ci->diag.Diagnose(macroNode.GetBeginPos(), DiagKind::macro_unexpected_empty_parameter); return; } PtrVector decls; CheckNodesConsistency(decls, newNodes, target); if (decls.empty()) { return; } (void)decls[0]->curFile->originalMacroCallNodes.emplace_back(target.pointer->at(target.loc).release()); (void)target.pointer->erase(target.pointer->begin() + static_cast(target.loc)); (void)target.pointer->insert(target.pointer->begin() + static_cast(target.loc), std::make_move_iterator(decls.begin()), std::make_move_iterator(decls.end())); CheckReplacedFuncParamList(macroNode, target); } void MacroExpansion::ReplaceEachMacroHelper(MacroCall& macroNode, PtrVector& newNodes) const { auto file = macroNode.GetNode()->curFile; if (!file) { return; } if (auto expr = std::get_if*>(¯oNode.replaceLoc); expr) { // For all expr. if (newNodes.empty()) { (void)ci->diag.Diagnose(macroNode.GetBeginPos(), DiagKind::macro_unexpect_no_expr); return; } if (newNodes.size() == 1 && DynamicCast(newNodes[0].get())) { (void)file->originalMacroCallNodes.emplace_back((*expr)->release()); (*expr)->reset(RawStaticCast(newNodes[0].release())); return; } (void)ci->diag.Diagnose(*newNodes.back(), DiagKind::macro_expect_one_expr); } else if (auto nodes = std::get_if>>(¯oNode.replaceLoc); nodes) { if (newNodes.empty()) { (void)file->originalMacroCallNodes.emplace_back((*nodes).pointer->at((*nodes).loc).release()); (void)(*nodes).pointer->erase((*nodes).pointer->begin() + static_cast((*nodes).loc)); return; } (void)file->originalMacroCallNodes.emplace_back((*nodes).pointer->at((*nodes).loc).release()); // Erase macro Node. (void)(*nodes).pointer->erase((*nodes).pointer->begin() + static_cast((*nodes).loc)); // Insert real macro Node. (void)(*nodes).pointer->insert((*nodes).pointer->begin() + static_cast((*nodes).loc), std::make_move_iterator(newNodes.begin()), std::make_move_iterator(newNodes.end())); } else if (auto exprs = std::get_if>>(¯oNode.replaceLoc); exprs) { if (newNodes.empty()) { (void)ci->diag.Diagnose(macroNode.GetBeginPos(), DiagKind::macro_unexpect_no_expr); return; } if (newNodes.size() == 1 && DynamicCast(newNodes[0].get())) { (void)file->originalMacroCallNodes.emplace_back((*exprs).pointer->at((*exprs).loc).release()); // Erase macro Node. (void)(*exprs).pointer->erase((*exprs).pointer->begin() + static_cast((*exprs).loc)); (void)(*exprs).pointer->insert((*exprs).pointer->begin() + static_cast((*exprs).loc), OwnedPtr(RawStaticCast(newNodes.front().release()))); return; } (void)ci->diag.Diagnose(*newNodes.back(), DiagKind::macro_expect_one_expr); } else if (auto decls = std::get_if>>(¯oNode.replaceLoc); decls) { ReplaceDecls(macroNode, newNodes, *decls); } else if (auto params = std::get_if>>(¯oNode.replaceLoc); params) { ReplaceParams(macroNode, newNodes, *params); } else { (void)ci->diag.Diagnose(macroNode.GetBeginPos(), DiagKind::macro_expand_invalid_node_replace); } } void MacroExpansion::ReplaceEachMacro(MacroCall& macCall) { auto pInvocation = macCall.GetInvocation(); // macro expand failed will get ILLEGAL token. if (MacroExpandFailed(pInvocation->newTokens) || macCall.status == MacroEvalStatus::FAIL) { return; } Parser parser{pInvocation->newTokens, ci->diag, ci->diag.GetSourceManager(), ci->invocation.globalOptions.enableAddCommentToAst, ci->invocation.globalOptions.compileCjd}; parser.SetCompileOptions(ci->invocation.globalOptions); (void)parser.SetPrimaryDecl(pInvocation->outerDeclIdent).EnableCustomAnno(); // Reparsing newTokens to get macro-generate ASTs. auto nodes = parser.ParseNodes( pInvocation->scope, *macCall.GetNode(), macCall.GetModifiers(), macCall.GetAnnotations()); for (auto& node : nodes) { if (!node || node->IsInvalid()) { return; } // Make sure no macro calls exist after macro expansion. if (HasMacroCallInNode(node, ci->diag)) { return; } CheckWhenAfterMacroExpand(node, ci->diag); } ReplaceEachMacroHelper(macCall, nodes); } void MacroExpansion::ReplaceAST(Package& package) { // Normal mode or Debug mode: replace all macro nodes, and check if ast is valid. auto macroCalls = macroCollector.macCalls; std::reverse(macroCalls.begin(), macroCalls.end()); for (auto mc : macroCalls) { ReplaceEachMacro(mc); } if (ci->diag.GetErrorCount() != 0) { ci->diag.EmitCategoryGroup(); } // Debug mode: replace file nodes which have macro calls. if (ci->diag.GetErrorCount() == 0 && ci->invocation.globalOptions.enableMacroDebug) { for (auto& file : package.files) { ReplaceEachFileNode(*file); } } } void MacroExpansion::EvaluateMacros() { // Notice: When interpreter-based evaluation strategy is introduced, just modify the // implementation of class MacroEvaluation. Make sure the key data structure to // communicate between Expansion and Evaluation. bool useChildProcess = ci->invocation.globalOptions.enableMacroInLSP; MacroEvaluation evaluator(ci, ¯oCollector, useChildProcess); evaluator.Evaluate(); tokensEvalInMacro = evaluator.GetVecOfGeneratedCodes(); return; } // In the LSP scenario, there may be concurrent calls to macro expansion when process cjd file. If Execute is executed // concurrently with the same macro declaration, it will cause a problem. The global lock is used to protect macro // expansion calls in different CompilerInstances. std::mutex globalMacroExpandLock; void MacroExpansion::Execute(Package& package) { std::lock_guard guard(globalMacroExpandLock); curPackage = &package; // Step 1: Collect macro-defs, macro-calls. CollectMacros(package); if (HasNoMacros(this->macroCollector) || HasNoMacroCalls(this->macroCollector.macCalls) || HasDefAndCallInSamePkg(this->macroCollector, ci->diag) || (ci->diag.GetErrorCount() > 0 && !ci->invocation.globalOptions.enableMacroInLSP)) { return; } // Step 2: Evaluate macros. Generate new tokens for further AST replacement. EvaluateMacros(); // Step 3: Map macro information and save the expanded macro contents to a file. ProcessMacros(package); // Step 4: Replace MacroCall AST with new generated AST. ReplaceAST(package); } void MacroExpansion::Execute(std::vector>& packages) { if (ci->invocation.globalOptions.enableCompileTest) { TestEntryConstructor testEntry(ci->diag); testEntry.CheckTestSuite(packages); } for (auto& package : packages) { if (package) { Execute(*package); } } if (ci->invocation.globalOptions.enableCompileTest) { TestEntryConstructor::ConstructTestSuite(ci->invocation.globalOptions.moduleName, packages, ci->importManager.GetAllImportedPackages(), ci->invocation.globalOptions.compileTestsOnly); } for (auto& package : packages) { AddCurFile(*package); CheckUnhandledMacroCall(*package, ci->diag); } } cangjie_compiler-1.0.7/src/Macro/MacroProcess.cpp000066400000000000000000000541011510705540100217440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Macro/MacroEvaluation.h" #include "cangjie/Macro/MacroExpansion.h" #include "cangjie/Lex/Lexer.h" using namespace Cangjie; using namespace AST; namespace { bool IsSpecialToken(TokenKind prevTokenKind, TokenKind curTokenKind) { // For case: A() A[] A<> A?? >> >= ~init. static std::set tkSet0 = { TokenKind::LPAREN, TokenKind::RPAREN, TokenKind::LSQUARE, TokenKind::RSQUARE, TokenKind::LT, TokenKind::QUEST}; static std::set> tkSet1 = { std::pair(TokenKind::GT, TokenKind::GT), std::pair(TokenKind::GT, TokenKind::ASSIGN), std::pair(TokenKind::BITNOT, TokenKind::INIT)}; return tkSet0.find(curTokenKind) != tkSet0.end() || tkSet1.find(std::pair(prevTokenKind, curTokenKind)) != tkSet1.end(); } void ReportMappingFailedDiag( std::vector& oldTokens, std::vector& newTokens, const Position mcPos, CompilerInstance& ci) { auto oldSize = oldTokens.size(); auto newSize = newTokens.size(); auto minSize = oldSize < newSize ? oldSize : newSize; for (size_t i = 0; i < minSize; ++i) { // not check newline if (oldTokens[i].kind == TokenKind::NL && newTokens[i].kind == TokenKind::NL) { continue; } if (oldTokens[i].kind != newTokens[i].kind || oldTokens[i].Value() != newTokens[i].Value()) { if (oldTokens[i].Value().empty()) { (void)ci.diag.Diagnose(mcPos, DiagKind::macro_call_map_to_empty_value_token, TOKEN_KIND_VALUES[static_cast(oldTokens[i].kind)]); break; } (void)ci.diag.Diagnose( mcPos, DiagKind::macro_call_map_info_failed, oldTokens[i].Value(), newTokens[i].Value()); break; } } return; } size_t GetTokenLenth(const Token& token) { return GetTokenLength(token.Value().size(), token.kind, token.delimiterNum); } void CollectMacroDebugPosition( MacroInvocation& invocation, const Token& token, unsigned int lastColumn) { if (!token.Begin().isCurFile || token.kind == TokenKind::COMMENT) { return; } auto key = token.Begin().Hash64(); if (invocation.origin2newPosMap.find(key) != invocation.origin2newPosMap.end()) { return; } // Collect the position information of tokens from macrocalls for debugging purposes. if (token.kind == TokenKind::NL) { if (!invocation.macroDebugMap.empty()) { auto lastPos = invocation.macroDebugMap.begin()->second; // If the last token is newline, then the lastPos's column is 1, and it only needs to be collected once. if (lastPos.column != 1) { invocation.macroDebugMap[lastColumn] = Position{lastPos.fileID, lastPos.line + 1, 1}; } } return; } // Other tokens except newline. if (!invocation.macroDebugMap.empty()) { auto lastPos = invocation.macroDebugMap.begin()->second; if (lastPos == token.Begin()) { // No need to collect the same position. return; } // If the last token is newline, replace the last position with the position of current token. if (lastPos.column == 1) { invocation.macroDebugMap.begin()->second = token.Begin(); invocation.macroDebugMap[static_cast(lastColumn + GetTokenLenth(token))] = token.End(); return; } } invocation.macroDebugMap[lastColumn] = token.Begin(); invocation.macroDebugMap[static_cast(lastColumn + GetTokenLenth(token))] = token.End(); } static void HandleInterplationStringPosMap(Token& strToken, Position newStartPos, const Ptr pMacroInvocation, const CompilerInstance& ci, bool isMacroMap = false) { std::vector tokens{strToken}; Lexer lexer(tokens, ci.diag, ci.diag.GetSourceManager()); auto strParts = lexer.GetStrParts(strToken); Position deltaBegin = newStartPos - strToken.Begin(); // multi_string start in newline auto multiStrDeltaLine = 1; for (auto& strP : strParts) { if (strP.strKind == StringPart::STR && strToken.kind == TokenKind::MULTILINE_STRING) { if (strP.value.find("\n") != std::string::npos) { multiStrDeltaLine += std::count(strP.value.begin(), strP.value.end(), '\n'); } continue; } else if (strP.strKind != StringPart::EXPR) { continue; } std::vector tks = GetTokensFromString(strP.value, ci.diag, strP.begin); // first token must be `$` and second token must be `{` CJC_ASSERT(!tks.empty()); for (size_t i = 2; i < tks.size() - 1; i++) { auto &t = tks[i]; if (t.kind == TokenKind::COMMENT) { continue; } t.SetCurFile(strToken.Begin().isCurFile); Position newPos = t.Begin() + deltaBegin; if (isMacroMap) { t.SetCurFile(strToken.Begin().isCurFile); auto key = pMacroInvocation->isCurFile ? newPos.Hash32() : static_cast(newPos.column); pMacroInvocation->new2macroCallPosMap[key] = t.Begin(); } else if (t.kind != TokenKind::NL) { Position begin{ newStartPos.fileID, newStartPos.line, static_cast(newPos.column), t.Begin().isCurFile}; if (strToken.kind == TokenKind::MULTILINE_STRING) { newPos = Position(newStartPos.fileID, newStartPos.line + multiStrDeltaLine, t.Begin().column, t.Begin().isCurFile); begin = newPos; } pMacroInvocation->new2originPosMap[newPos.Hash32()] = t.Begin(); auto key = t.Begin().Hash64(); if (pMacroInvocation->origin2newPosMap.find(key) == pMacroInvocation->origin2newPosMap.end()) { pMacroInvocation->origin2newPosMap[key] = begin; } } } } } /** * Update positions of generated Tokens after macro expansion. * * // Before * @Moo // line l1 * class A{ * var a = 42 * } * * // After * class A { var a = 42; func getA() {return a} } // line l1 * * We put all generated Tokens into the same line where the macro call lies. */ void RefreshNewTokensPos(Node& node, SourceManager& sm, const CompilerInstance& ci) { auto pMacroInvocation = node.GetInvocation(); if (pMacroInvocation->isCurFile) { return; } auto mcPos = node.begin; pMacroInvocation->origin2newPosMap[mcPos.Hash64()] = mcPos; auto lastColumn = static_cast(sm.GetLineEnd(mcPos) + 1); auto prevTokenKind = TokenKind::END; pMacroInvocation->macroAtPosition.emplace_back(mcPos); size_t threadNum = std::thread::hardware_concurrency(); Cangjie::Utils::TaskQueue taskQueue(threadNum); for (auto& token : pMacroInvocation->newTokens) { taskQueue.AddTask([&token, &pMacroInvocation, &mcPos]() { if (token.kind == TokenKind::NL || token.kind == TokenKind::COMMENT) { return token.SetCurFile(true); } // Tokens defined in topLevel was not refreshed to at position. if (mcPos.fileID != token.Begin().fileID) { return token.SetCurFile(false); } bool notCurFile = std::any_of(pMacroInvocation->macroAtPosition.begin(), pMacroInvocation->macroAtPosition.end(), [&token](auto& atPos) { return token.Begin() == atPos || token.Begin() == atPos + 1; }); return token.SetCurFile(!notCurFile); }); } taskQueue.RunAndWaitForAllTasksCompleted(); for (auto& token : pMacroInvocation->newTokens) { // For case: A() A[] A<> A?? >> >=. if (!IsSpecialToken(prevTokenKind, token.kind)) { lastColumn += 1; } if (ci.invocation.globalOptions.enableCompileDebug || ci.invocation.globalOptions.displayLineInfo) { CollectMacroDebugPosition(*pMacroInvocation, token, lastColumn); } // still have problem with MULTILINE_STRING, turns out Node generated from interpolation part // not stay in same line, impossible to establish a mapping Position keyPos(mcPos.line, static_cast(lastColumn)); if (token.kind == TokenKind::STRING_LITERAL || token.kind == TokenKind::MULTILINE_STRING) { pMacroInvocation->new2originPosMap[keyPos.Hash32()] = token.Begin(); // map original STRING_LITERAL token HandleInterplationStringPosMap(token, Position(mcPos.fileID, mcPos.line, static_cast(lastColumn)), pMacroInvocation, ci); } else if (token.kind != TokenKind::NL && token.kind != TokenKind::COMMENT) { pMacroInvocation->new2originPosMap[keyPos.Hash32()] = token.Begin(); } auto key = token.Begin().Hash64(); Position begin = token.Begin(); begin.fileID = mcPos.fileID; begin.line = mcPos.line; begin.column = static_cast(lastColumn); lastColumn += static_cast(GetTokenLenth(token)); Position end = token.End(); end.fileID = mcPos.fileID; end.line = mcPos.line; end.column = static_cast(lastColumn); token.SetValuePos(token.Value(), begin, end); if (token.kind != TokenKind::NL && token.kind != TokenKind::COMMENT && pMacroInvocation->origin2newPosMap.find(key) == pMacroInvocation->origin2newPosMap.end()) { pMacroInvocation->origin2newPosMap[key] = token.Begin(); } prevTokenKind = token.kind; } if (ci.invocation.globalOptions.enableCompileDebug || ci.invocation.globalOptions.displayLineInfo) { pMacroInvocation->macroDebugMap[lastColumn] = node.end; } } void RefreshTokensPosMap(Node& node, SourceManager& sm, CompilerInstance& ci) { auto pMacroInvocation = node.GetInvocation(); if (pMacroInvocation->newTokens.empty()) { return; } auto find = std::find_if(pMacroInvocation->newTokens.begin(), pMacroInvocation->newTokens.end(), [&sm, &node](auto& token) { return !IsCurFile(sm, token, node.begin.fileID); }); if (find == pMacroInvocation->newTokens.end()) { pMacroInvocation->isCurFile = true; } // Generate new tokens in *.macrocall file. pMacroInvocation->mcBegin = node.curFile->macroCallPosBase; auto tokens = GetTokensFromString(pMacroInvocation->newTokensStr, ci.diag, node.curFile->macroCallPosBase); if (tokens.empty()) { return; } pMacroInvocation->mcEnd = tokens.back().End(); if (tokens.size() != pMacroInvocation->newTokens.size()) { ReportMappingFailedDiag(pMacroInvocation->newTokens, tokens, node.begin, ci); return; } // Debug mode: no need to Mapping position. if (ci.invocation.globalOptions.enableMacroDebug) { return; } // Bind new tokens pos after macrocall node's begin Pos. RefreshNewTokensPos(node, sm, ci); // Mapping new position column with new source position in *.macrocall file. for (size_t i = 0; i < tokens.size(); ++i) { if (tokens[i].kind != TokenKind::COMMENT) { auto pos = pMacroInvocation->newTokens[i].Begin(); auto key = pMacroInvocation->isCurFile ? pos.Hash32() : static_cast(pos.column); pMacroInvocation->new2macroCallPosMap[key] = tokens[i].Begin(); } // only fixed STRING_LITERAL, MULTILINE_STRING still has problem if (tokens[i].kind == TokenKind::STRING_LITERAL || tokens[i].kind == TokenKind::MULTILINE_STRING) { HandleInterplationStringPosMap( tokens[i], pMacroInvocation->newTokens[i].Begin(), pMacroInvocation, ci, true); } } return; } bool WriteStringToFile(const std::string& filePath, const std::string& str) { std::ofstream outStream = std::ofstream(filePath, std::ofstream::out | std::ofstream::binary); if (!outStream.is_open()) { return false; } outStream << str; outStream.close(); return true; } void RemoveMacroCallFile(File& file) { if (FileUtil::FileExist(file.macroCallFilePath)) { (void)FileUtil::Remove(file.macroCallFilePath.data()); } } /** * Append to SourceManager and save file. */ void GenerateMacroCallFile(const std::vector>& fileSucVec, std::map, std::vector>>& macroCallMap, CompilerInstance& ci) { auto& sm = ci.GetSourceManager(); std::string content; for (const auto& file : fileSucVec) { auto nodes = macroCallMap.at(file); auto curFilePosBase = file->begin; auto fileID = sm.AddSource(file->macroCallFilePath, ""); for (auto& node : nodes) { auto pMacroInvocation = node->GetInvocation(); if (!pMacroInvocation) { continue; } // Append source code between macroCalls to newFile. content = sm.GetContentBetween(curFilePosBase, node->begin); (void)sm.AppendSource(file->macroCallFilePath, content); curFilePosBase = node->end; file->macroCallPosBase = sm.GetSource(fileID).GetEndPos(); // Append contents generated by macroCall to newFile. (void)sm.AppendSource(file->macroCallFilePath, pMacroInvocation->newTokensStr); // Map macro information. RefreshTokensPosMap(*node, sm, ci); pMacroInvocation->isForLSP = ci.invocation.globalOptions.enableMacroInLSP; } content = sm.GetContentBetween(curFilePosBase.fileID, curFilePosBase, file->end); (void)sm.AppendSource(file->macroCallFilePath, content); // If lsp or debug-macro, then save the expanded macro contents to a file. if (ci.invocation.globalOptions.enableMacroInLSP || ci.invocation.globalOptions.enableMacroDebug) { auto source = sm.GetSource(fileID).buffer; if (!WriteStringToFile(file->macroCallFilePath, source)) { (void)ci.diag.Diagnose(file->begin, DiagKind::macro_call_save_file_failed); } } else { // Need to remove *.macrocall. RemoveMacroCallFile(*file); } } } bool IsPureCustomAnnotation(MacroInvocation& invocation, CompilerInstance& ci) { if (!invocation.isCustom || invocation.newTokens.empty()) { return false; } // The current macrocall is actually a custom annotation. if (invocation.isCurFile) { return true; } auto& sm = ci.GetSourceManager(); auto find = std::find_if(invocation.newTokens.begin(), invocation.newTokens.end(), [&sm](auto& token) { return !IsCurFile(sm, token); }); if (find == invocation.newTokens.end()) { invocation.isCurFile = true; return true; } // The current custom annotation contains a macrocall inside. return false; } void EraseFirstNLAndLastEndToken(std::list& retTokens) { while (!retTokens.empty()) { if (retTokens.back().kind == TokenKind::END) { retTokens.pop_back(); continue; } if (retTokens.begin()->kind != TokenKind::NL) { break; } (void)retTokens.erase(retTokens.begin()); // Erase newline. } } void ProcessCombinedToken(std::vector& newTokens, const Token& tk, DiagnosticEngine& diag) { size_t len2{2}; if (tk.kind == TokenKind::COALESCING) { (void)newTokens.emplace_back(Token(TokenKind::QUEST, "?", tk.Begin(), tk.Begin() + 1)); (void)newTokens.emplace_back(Token(TokenKind::QUEST, "?", tk.Begin() + 1, tk.Begin() + len2)); } else if (tk.kind == TokenKind::RSHIFT) { (void)newTokens.emplace_back(Token(TokenKind::GT, ">", tk.Begin(), tk.Begin() + 1)); (void)newTokens.emplace_back(Token(TokenKind::GT, ">", tk.Begin() + 1, tk.Begin() + len2)); } else if (tk.kind == TokenKind::RSHIFT_ASSIGN) { (void)newTokens.emplace_back(Token(TokenKind::GT, ">", tk.Begin(), tk.Begin() + 1)); auto pos = tk.Begin() + 1; (void)newTokens.emplace_back(Token(TokenKind::GT, ">", pos, pos + 1)); pos = pos + 1; (void)newTokens.emplace_back(Token(TokenKind::ASSIGN, "=", pos, pos + 1)); } else if (tk.kind == TokenKind::GE) { (void)newTokens.emplace_back(Token(TokenKind::GT, ">", tk.Begin(), tk.Begin() + 1)); (void)newTokens.emplace_back(Token(TokenKind::ASSIGN, "=", tk.Begin() + 1, tk.Begin() + len2)); } else if (tk.kind == TokenKind::IDENTIFIER) { // In macro definition, the user can create an identifier token // or obtain an identifier token from getIdentifier() of libast, // which is actually a keyword token. Change token from identifier to keyword here. auto tokens = GetTokensFromString(tk.Value(), diag, tk.Begin()); if (!tokens.empty()) { (void)newTokens.insert(newTokens.end(), tokens.begin(), tokens.end()); } else { (void)newTokens.emplace_back(tk); } } else { (void)newTokens.emplace_back(tk); } } } // namespace void MacroEvaluation::ProcessNewTokens(MacroCall& macCall) { auto pInvocation = macCall.GetInvocation(); if (!pInvocation || IsPureAnnotation(*pInvocation)) { return; } std::list retTokens(pInvocation->newTokens.begin(), pInvocation->newTokens.end()); EraseFirstNLAndLastEndToken(retTokens); if (retTokens.empty()) { return; } pInvocation->newTokens.clear(); // Append line comment, "/* 7.1 */". auto subline = 1; auto mcline = std::to_string(macCall.GetBeginPos().line); auto comment = "/* " + mcline + "." + std::to_string(subline) + " */"; (void)pInvocation->newTokens.emplace_back( TokenKind::COMMENT, comment, macCall.GetBeginPos(), macCall.GetEndPos(), true); for (const auto& tk : std::as_const(retTokens)) { ProcessCombinedToken(pInvocation->newTokens, tk, ci->diag); if (tk.kind == TokenKind::NL) { subline++; auto lineComment = "/* " + mcline + "." + std::to_string(subline) + " */"; auto pos = Position{tk.Begin().fileID, tk.Begin().line + 1, 1}; (void)pInvocation->newTokens.emplace_back( TokenKind::COMMENT, lineComment, pos, pos + lineComment.size(), true); } } if (ci->invocation.globalOptions.enableMacroInLSP || ci->invocation.globalOptions.enableMacroDebug) { auto needNewLine = (macCall.GetNode()->IsDecl() || subline > 1) ? true : false; auto beginComment = "/* ===== Emitted by MacroCall " + macCall.GetMacroInfo() + " ===== */"; auto endComment = "/* ===== End of the Emit ===== */"; if (needNewLine) { (void)pInvocation->newTokens.insert(pInvocation->newTokens.begin(), Token(TokenKind::NL)); (void)pInvocation->newTokens.insert( pInvocation->newTokens.begin(), Token(TokenKind::COMMENT, beginComment, macCall.GetBeginPos(), macCall.GetEndPos(), true)); auto pos = pInvocation->newTokens.back().Begin(); pInvocation->newTokens.emplace_back(Token(TokenKind::NL, Utils::GetLineTerminator(), pos, pos + 1, true)); pos = pos + 1; (void)pInvocation->newTokens.emplace_back(Token(TokenKind::COMMENT, endComment, pos, pos + 1, true)); } else { (void)pInvocation->newTokens.insert( pInvocation->newTokens.begin(), Token(TokenKind::COMMENT, beginComment, macCall.GetBeginPos(), macCall.GetEndPos(), true)); (void)pInvocation->newTokens.emplace_back( Token(TokenKind::COMMENT, endComment, pInvocation->newTokens.back().Begin(), pInvocation->newTokens.back().Begin() + 1, true)); } } pInvocation->newTokensStr = ConvertTokensToString(pInvocation->newTokens, macCall.GetBeginPos().column); vecOfGeneratedCodes.push_back(pInvocation->newTokensStr); } std::string MacroEvaluation::ConvertTokensToString( const TokenVector& tokens, int offset) { MacroFormatter formatter = MacroFormatter(tokens, escapePosVec, offset); return formatter.Produce(); } /** * Map macro information and save the expanded macro contents to a file. */ void MacroExpansion::ProcessMacros(Package& package) { // Collect successful macrocall. std::map, std::vector>> foundSucMacroMap; std::vector> fileSucVec; for (auto& mc : macroCollector.macCalls) { auto pInvocation = mc.GetInvocation(); if (!pInvocation || MacroExpandFailed(pInvocation->newTokens) || mc.status == MacroEvalStatus::FAIL || IsPureCustomAnnotation(*pInvocation, *ci)) { continue; } auto file = mc.GetNode()->curFile; if (!file) { continue; } if (foundSucMacroMap.find(file) != foundSucMacroMap.end()) { (void)foundSucMacroMap[file].emplace_back(mc.GetNode()); } else { (void)fileSucVec.emplace_back(file); foundSucMacroMap[file] = {mc.GetNode()}; } } // Need to remove *.macrocall. for (const auto& file : package.files) { if (!file) { continue; } file->macroCallFilePath = file->filePath + ".macrocall"; if (foundSucMacroMap.find(file.get()) == foundSucMacroMap.end() && (ci->invocation.globalOptions.enableMacroInLSP || ci->invocation.globalOptions.enableMacroDebug)) { RemoveMacroCallFile(*file); } } if (foundSucMacroMap.empty()) { return; } // Generate *.macrocall file. GenerateMacroCallFile(fileSucVec, foundSucMacroMap, *ci); return; } cangjie_compiler-1.0.7/src/Macro/MacroUpdateInfo.h000066400000000000000000000151241510705540100220330ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the UpdateMacroInfo API. */ #ifndef CANGJIE_MACROUPDATEINFO_H #define CANGJIE_MACROUPDATEINFO_H #include "cangjie/AST/Node.h" namespace { using namespace Cangjie; using namespace AST; // Update parent for macro node. template void UpdateExpr(OwnedPtr& item, const Ptr parent, MacroCollector& collector) { if (!item) { return; } if (item->IsMacroCallNode()) { auto macroNode = item.get(); (void)collector.macCalls.emplace_back(macroNode); collector.macCalls.back().replaceLoc = &item; collector.macCalls.back().isOuterMost = true; auto pinvocation = macroNode->GetInvocation(); if (pinvocation && pinvocation->newTokens.empty()) { pinvocation->parent = parent; (void)pinvocation->newTokens.emplace_back(Token(TokenKind::ILLEGAL, "")); } } } // Update parent for macro node. template void UpdateContainer(PtrVector& container, const Ptr parent, MacroCollector& collector) { CJC_NULLPTR_CHECK(parent); for (size_t i = 0; i < container.size(); i++) { if (container[i]->IsMacroCallNode()) { auto macroNode = container[i].get(); (void)collector.macCalls.emplace_back(macroNode); collector.macCalls.back().replaceLoc = PtrType(VectorTarget>{&container, i}); collector.macCalls.back().isOuterMost = true; auto pinvocation = macroNode->GetInvocation(); if (pinvocation && pinvocation->newTokens.empty()) { pinvocation->parent = parent; (void)pinvocation->newTokens.emplace_back(Token(TokenKind::ILLEGAL, "")); } } } } template void UpdateSingleMacroInfo([[maybe_unused]] Node& curNode, [[maybe_unused]] MacroCollector& collector) { } #define UPDATE(KIND, FIELD) \ template <> void UpdateSingleMacroInfo(Node & curNode, MacroCollector & collector) \ { \ auto parent = StaticAs(&curNode); \ UpdateContainer(parent->FIELD, parent, collector); \ } UPDATE(FILE, decls) UPDATE(CLASS_BODY, decls) UPDATE(STRUCT_BODY, decls) UPDATE(INTERFACE_BODY, decls) UPDATE(EXTEND_DECL, members) UPDATE(BLOCK, body) UPDATE(ARRAY_LIT, children) UPDATE(TUPLE_LIT, children) UPDATE(QUOTE_EXPR, exprs) #undef UPDATE #define UPDATE(KIND, FIELD) \ template <> void UpdateSingleMacroInfo(Node & curNode, MacroCollector & collector) \ { \ auto parent = StaticAs(&curNode); \ UpdateExpr(parent->FIELD, parent, collector); \ } UPDATE(VAR_DECL, initializer) UPDATE(LET_PATTERN_DESTRUCTOR, initializer) UPDATE(ASSIGN_EXPR, rightExpr) UPDATE(FUNC_PARAM, assignment) UPDATE(CALL_EXPR, baseFunc) UPDATE(MATCH_EXPR, selector) UPDATE(MATCH_CASE, patternGuard) UPDATE(MATCH_CASE_OTHER, matchExpr) UPDATE(MEMBER_ACCESS, baseExpr) UPDATE(IF_EXPR, condExpr) UPDATE(WHILE_EXPR, condExpr) UPDATE(DO_WHILE_EXPR, condExpr) UPDATE(FUNC_ARG, expr) UPDATE(RETURN_EXPR, expr) UPDATE(PAREN_EXPR, expr) UPDATE(TYPE_CONV_EXPR, expr) UPDATE(UNARY_EXPR, expr) UPDATE(VAR_WITH_PATTERN_DECL, initializer) UPDATE(SPAWN_EXPR, task) UPDATE(THROW_EXPR, expr) UPDATE(PERFORM_EXPR, expr) UPDATE(TRAIL_CLOSURE_EXPR, expr) UPDATE(IS_EXPR, leftExpr) UPDATE(AS_EXPR, leftExpr) #undef UPDATE template <> void UpdateSingleMacroInfo(Node& curNode, MacroCollector& collector) { auto parent = StaticAs(&curNode); UpdateExpr(parent->rightExpr, parent, collector); UpdateExpr(parent->leftExpr, parent, collector); } template <> void UpdateSingleMacroInfo(Node& curNode, MacroCollector& collector) { auto parent = StaticAs(&curNode); UpdateContainer(parent->members, parent, collector); UpdateContainer(parent->constructors, parent, collector); } template <> void UpdateSingleMacroInfo(Node& curNode, MacroCollector& collector) { auto parent = StaticAs(&curNode); UpdateExpr(parent->startExpr, parent, collector); UpdateExpr(parent->stopExpr, parent, collector); UpdateExpr(parent->stepExpr, parent, collector); } template <> void UpdateSingleMacroInfo(Node& curNode, MacroCollector& collector) { auto parent = StaticAs(&curNode); UpdateExpr(parent->inExpression, parent, collector); UpdateExpr(parent->patternGuard, parent, collector); } template <> void UpdateSingleMacroInfo(Node& curNode, MacroCollector& collector) { auto parent = StaticAs(&curNode); UpdateExpr(parent->baseExpr, parent, collector); for (auto& expr : parent->indexExprs) { UpdateExpr(expr, parent, collector); } } template <> void UpdateSingleMacroInfo(Node& curNode, MacroCollector& collector) { auto parent = StaticAs(&curNode); UpdateContainer(parent->params, parent, collector); } void UpdateMacroInfo(const Ptr node, MacroCollector& collector) { switch (node->astKind) { #define ASTKIND(KIND, VALUE, NODE, SIZE) \ case AST::ASTKind::KIND: { \ UpdateSingleMacroInfo(*node, collector); \ break; \ } #include "cangjie/AST/ASTKind.inc" #undef ASTKIND } } } #endifcangjie_compiler-1.0.7/src/Macro/NodeSerialization.cpp000066400000000000000000001735121510705540100227770ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /* * @file * * This file implements the NodeWriter. */ #include "cangjie/Macro/NodeSerialization.h" #include "cangjie/AST/ASTCasting.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/Match.h" #include "cangjie/Basic/Print.h" #include "cangjie/Macro/TokenSerialization.h" #include "flatbuffers/NodeFormat_generated.h" using namespace Cangjie; using namespace AST; using namespace Meta; using namespace NodeSerialization; using namespace Utils; using namespace TokenSerialization; NodeFormat::Position NodeWriter::FlatPosCreateHelper(const Position& pos) const { return NodeFormat::Position(pos.fileID, pos.line, pos.column); } flatbuffers::Offset> NodeWriter::CreatePositionVector( const std::vector& positions) { std::function filler = [&positions, this](size_t i, NodeFormat::Position* p) -> void { *p = FlatPosCreateHelper(positions[i]); }; return builder.CreateVectorOfStructs(positions.size(), filler); } flatbuffers::Offset NodeWriter::SerializeFeaturesDirective( AstFeaturesDirective featuresDirective) { if (featuresDirective == nullptr) { return flatbuffers::Offset(); } auto ftrNodeBase = SerializeNodeBase(featuresDirective); auto commas = CreatePositionVector(featuresDirective->commaPoses); std::vector> itemsVec; for (const auto& item : featuresDirective->content) { itemsVec.emplace_back(SerializeFeatureId(item)); } auto items = builder.CreateVector(itemsVec); return NodeFormat::CreateFeaturesDirective(builder, ftrNodeBase, items, commas); } flatbuffers::Offset NodeWriter::SerializeFeatureId(const AST::FeatureId& content) { auto nodeBase = SerializeNodeBase(&content); std::vector idents; std::vector poses; for (auto& ident : content.identifiers) { idents.emplace_back(ident.Val()); poses.emplace_back(ident.Begin()); } auto identifiers = builder.CreateVectorOfStrings(idents); auto identPoses = CreatePositionVector(poses); auto dotPoses = CreatePositionVector(content.dotPoses); return NodeFormat::CreateFeatureId(builder, nodeBase, identifiers, identPoses, dotPoses); } flatbuffers::Offset NodeWriter::SerializePackageSpec(AstPackageSpec packageSpec) { if (packageSpec == nullptr) { return flatbuffers::Offset(); } auto paNodeBase = SerializeNodeBase(packageSpec); auto macroPos = NodeFormat::Position(packageSpec->macroPos.fileID, packageSpec->macroPos.line, packageSpec->macroPos.column); auto packagePos = NodeFormat::Position( packageSpec->packagePos.fileID, packageSpec->packagePos.line, packageSpec->packagePos.column); auto prefixPaths = builder.CreateVectorOfStrings(packageSpec->prefixPaths); auto prefixPoses = CreatePositionVector(packageSpec->prefixPoses); auto prefixDotPoses = CreatePositionVector(packageSpec->prefixDotPoses); auto packageName = builder.CreateString(packageSpec->packageName.Val()); auto packageNamePos = NodeFormat::Position(packageSpec->packageName.Begin().fileID, packageSpec->packageName.Begin().line, packageSpec->packageName.Begin().column); auto accessible = packageSpec->modifier == nullptr ? NodeFormat::AccessibleKind_ACCESSIBLE_PUBLIC : packageSpec->modifier->modifier == TokenKind::INTERNAL ? NodeFormat::AccessibleKind_ACCESSIBLE_INTERNAL : packageSpec->modifier->modifier == TokenKind::PROTECTED ? NodeFormat::AccessibleKind_ACCESSIBLE_PROTECTED : NodeFormat::AccessibleKind_ACCESSIBLE_PUBLIC; return NodeFormat::CreatePackageSpec(builder, paNodeBase, ¯oPos, &packagePos, prefixPaths, prefixPoses, prefixDotPoses, packageName, &packageNamePos, accessible, packageSpec->hasMacro); } flatbuffers::Offset NodeWriter::SerializeImportContent(const AST::ImportContent& content) { auto icNodeBase = SerializeNodeBase(&content); auto kind = content.kind == ImportKind::IMPORT_ALIAS ? NodeFormat::ImportKind::ImportKind_IMPORT_ALIAS : content.kind == ImportKind::IMPORT_ALL ? NodeFormat::ImportKind_IMPORT_ALL : content.kind == ImportKind::IMPORT_MULTI ? NodeFormat::ImportKind_IMPORT_MULTI : NodeFormat::ImportKind_IMPORT_SINGLE; auto prefixPaths = builder.CreateVectorOfStrings(content.prefixPaths); auto prefixPoses = CreatePositionVector(content.prefixPoses); auto prefixDotPoses = CreatePositionVector(content.prefixDotPoses); auto ident = builder.CreateString(content.identifier.Val()); auto identPos = NodeFormat::Position( content.identifier.Begin().fileID, content.identifier.Begin().line, content.identifier.Begin().column); auto asPos = NodeFormat::Position(content.asPos.fileID, content.asPos.line, content.asPos.column); auto asIdent = builder.CreateString(content.aliasName.Val()); auto asIdentPos = NodeFormat::Position( content.aliasName.Begin().fileID, content.aliasName.Begin().line, content.aliasName.Begin().column); auto leftCurlPos = NodeFormat::Position(content.leftCurlPos.fileID, content.leftCurlPos.line, content.leftCurlPos.column); std::vector> itemsVec; for (const auto& item : content.items) { itemsVec.emplace_back(SerializeImportContent(item)); } auto items = builder.CreateVector(itemsVec); auto commaPoses = CreatePositionVector(content.commaPoses); auto rightCurlPos = NodeFormat::Position(content.rightCurlPos.fileID, content.rightCurlPos.line, content.rightCurlPos.column); return NodeFormat::CreateImportContent(builder, icNodeBase, kind, prefixPaths, prefixPoses, prefixDotPoses, ident, &identPos, &asPos, asIdent, &asIdentPos, &leftCurlPos, items, commaPoses, &rightCurlPos); } flatbuffers::Offset NodeWriter::SerializeImportSpec(AstImportSpec importSpec) { if (importSpec == nullptr) { return flatbuffers::Offset(); } auto imNodeBase = SerializeNodeBase(importSpec); auto reExport = !importSpec->modifier ? NodeFormat::ReExportKind_REEXPORT_PRIVATE : importSpec->modifier->modifier == TokenKind::PUBLIC ? NodeFormat::ReExportKind_REEXPORT_PUBLIC : importSpec->modifier->modifier == TokenKind::PROTECTED ? NodeFormat::ReExportKind_REEXPORT_PROTECTED : importSpec->modifier->modifier == TokenKind::INTERNAL ? NodeFormat::ReExportKind_REEXPORT_INTERNAL : NodeFormat::ReExportKind_REEXPORT_PRIVATE; auto importPos = NodeFormat::Position(importSpec->importPos.fileID, importSpec->importPos.line, importSpec->importPos.column); auto content = SerializeImportContent(importSpec->content); return NodeFormat::CreateImportSpec(builder, imNodeBase, reExport, &importPos, content); } flatbuffers::Offset NodeWriter::SerializeFile(AstFile file) { if (file == nullptr) { return flatbuffers::Offset(); } auto fNodeBase = SerializeNodeBase(file); auto fileName = builder.CreateString(file->fileName); auto filePath = builder.CreateString(file->filePath); auto feature = SerializeFeaturesDirective(file->feature.get()); auto package = SerializePackageSpec(file->package.get()); std::vector> importsVec; for (auto& import : file->imports) { if (import->TestAttr(Attribute::COMPILER_ADD)) { continue; } auto res = NodeWriter::SerializeImportSpec(import.get()); importsVec.push_back(res); } auto imports = builder.CreateVector(importsVec); auto decls = FlatVectorCreateHelper(file->decls, &NodeWriter::SerializeDecl); return NodeFormat::CreateFile(builder, fNodeBase, fileName, filePath, package, imports, decls, feature); } flatbuffers::Offset NodeWriter::SerializeMatchCase(AstMatchCase matchcase) { if (matchcase == nullptr) { return flatbuffers::Offset(); } auto mcNodeBase = SerializeNodeBase(matchcase); auto fbPatterns = FlatVectorCreateHelper( matchcase->patterns, &NodeWriter::SerializePattern); auto bitOrPosVector = CreatePositionVector(matchcase->bitOrPosVector); auto fbPatternGuard = SerializeExpr(matchcase->patternGuard.get()); auto ifPos = NodeFormat::Position(matchcase->wherePos.fileID, matchcase->wherePos.line, matchcase->wherePos.column); auto arrowPos = NodeFormat::Position(matchcase->arrowPos.fileID, matchcase->arrowPos.line, matchcase->arrowPos.column); auto fbexprOrdecls = SerializeBlock(matchcase->exprOrDecls.get()); return NodeFormat::CreateMatchCase( builder, mcNodeBase, fbPatterns, fbPatternGuard, &ifPos, &arrowPos, fbexprOrdecls, bitOrPosVector); } flatbuffers::Offset NodeWriter::SerializeMatchCaseOther(AstMatchCaseOther matchcaseother) { if (matchcaseother == nullptr) { return flatbuffers::Offset(); } auto mcNodeBase = SerializeNodeBase(matchcaseother); auto fbExpr = SerializeExpr(matchcaseother->matchExpr.get()); // flatbuffers::Offset auto arrowPos = NodeFormat::Position( matchcaseother->arrowPos.fileID, matchcaseother->arrowPos.line, matchcaseother->arrowPos.column); auto fbExprOrdecls = SerializeBlock(matchcaseother->exprOrDecls.get()); return NodeFormat::CreateMatchCaseOther(builder, mcNodeBase, fbExpr, &arrowPos, fbExprOrdecls); } flatbuffers::Offset NodeWriter::SerializePattern(AstPattern pattern) { if (pattern == nullptr) { return flatbuffers::Offset(); } static std::unordered_map> serializePatternMap = { {ASTKind::CONST_PATTERN, [](NodeWriter& nw, AstPattern pattern) { return nw.SerializeConstPattern(pattern); }}, {ASTKind::VAR_PATTERN, [](NodeWriter& nw, AstPattern pattern) { return nw.SerializeVarPattern(pattern); }}, {ASTKind::TUPLE_PATTERN, [](NodeWriter& nw, AstPattern pattern) { return nw.SerializeTuplePattern(pattern); }}, {ASTKind::TYPE_PATTERN, [](NodeWriter& nw, AstPattern pattern) { return nw.SerializeTypePattern(pattern); }}, {ASTKind::ENUM_PATTERN, [](NodeWriter& nw, AstPattern pattern) { return nw.SerializeEnumPattern(pattern); }}, {ASTKind::VAR_OR_ENUM_PATTERN, [](NodeWriter& nw, AstPattern pattern) { return nw.SerializeVarOrEnumPattern(pattern); }}, {ASTKind::EXCEPT_TYPE_PATTERN, [](NodeWriter& nw, AstPattern pattern) { return nw.SerializeExceptTypePattern(pattern); }}, {ASTKind::COMMAND_TYPE_PATTERN, [](NodeWriter& nw, AstPattern pattern) { return nw.SerializeCommandTypePattern(pattern); }}, {ASTKind::WILDCARD_PATTERN, [](NodeWriter& nw, AstPattern pattern) { return nw.SerializeWildcardPattern(pattern); }}, }; auto serializeFunc = serializePatternMap.find(pattern->astKind); if (serializeFunc != serializePatternMap.end()) { return serializeFunc->second(*this, pattern); } Errorln("Unkown Pattern in libast Serialization."); return flatbuffers::Offset(); } flatbuffers::Offset NodeWriter::SerializeConstPattern(AstPattern pattern) { auto constPattern = RawStaticCast(pattern); auto fbNodeBase = SerializeNodeBase(constPattern); auto fbLiteral = SerializeExpr(constPattern->literal.get()); auto fbCallExpr = SerializeCallExpr(constPattern->operatorCallExpr.get()); auto fbConstPattern = NodeFormat::CreateConstPattern(builder, fbNodeBase, fbLiteral, fbCallExpr); return NodeFormat::CreatePattern(builder, fbNodeBase, NodeFormat::AnyPattern_CONST_PATTERN, fbConstPattern.Union()); } flatbuffers::Offset NodeWriter::SerializeWildcardPattern(AstPattern pattern) { auto wildcardPattern = RawStaticCast(pattern); auto fbNodeBase = SerializeNodeBase(wildcardPattern); auto fbConstPattern = NodeFormat::CreateWildcardPattern(builder, fbNodeBase); return NodeFormat::CreatePattern( builder, fbNodeBase, NodeFormat::AnyPattern_WILDCARD_PATTERN, fbConstPattern.Union()); } flatbuffers::Offset NodeWriter::SerializeVarPattern(AstPattern pattern) { auto varPattern = RawStaticCast(pattern); auto fbNodeBase = SerializeNodeBase(varPattern); auto fbVarDecl = SerializeVarDecl(varPattern->varDecl.get()); auto fbVarPattern = NodeFormat::CreateVarPattern(builder, fbNodeBase, fbVarDecl); return NodeFormat::CreatePattern(builder, fbNodeBase, NodeFormat::AnyPattern_VAR_PATTERN, fbVarPattern.Union()); } flatbuffers::Offset NodeWriter::SerializeTypePattern(AstPattern pattern) { auto typePattern = RawStaticCast(pattern); auto fbNodeBase = SerializeNodeBase(typePattern); auto fbPattern = SerializePattern(typePattern->pattern.get()); auto colonPos = FlatPosCreateHelper(typePattern->colonPos); auto fbType = SerializeType(typePattern->type.get()); auto fbTypePattern = NodeFormat::CreateTypePattern(builder, fbNodeBase, fbPattern, &colonPos, fbType); return NodeFormat::CreatePattern(builder, fbNodeBase, NodeFormat::AnyPattern_TYPE_PATTERN, fbTypePattern.Union()); } flatbuffers::Offset NodeWriter::SerializeEnumPattern(const EnumPattern* enumPattern) { if (enumPattern == nullptr) { return flatbuffers::Offset(); } auto fbNodeBase = SerializeNodeBase(enumPattern); auto fbConstructor = SerializeExpr(enumPattern->constructor.get()); auto leftParenPos = FlatPosCreateHelper(enumPattern->leftParenPos); auto fbPatterns = FlatVectorCreateHelper( enumPattern->patterns, &NodeWriter::SerializePattern); auto commaPosVector = CreatePositionVector(enumPattern->commaPosVector); auto rightParenPos = FlatPosCreateHelper(enumPattern->rightParenPos); return NodeFormat::CreateEnumPattern( builder, fbNodeBase, fbConstructor, &leftParenPos, fbPatterns, &rightParenPos, commaPosVector); } flatbuffers::Offset NodeWriter::SerializeEnumPattern(const Pattern* pattern) { auto enumPattern = RawStaticCast(pattern); auto fbEnumPattern = SerializeEnumPattern(enumPattern); return NodeFormat::CreatePattern( builder, SerializeNodeBase(enumPattern), NodeFormat::AnyPattern_ENUM_PATTERN, fbEnumPattern.Union()); } flatbuffers::Offset NodeWriter::SerializeVarOrEnumPattern(AstPattern pattern) { auto varOrEnumPattern = RawStaticCast(pattern); auto fbNodeBase = SerializeNodeBase(varOrEnumPattern); auto fbIdentifier = builder.CreateString(varOrEnumPattern->identifier.GetRawText()); auto fbPattern = SerializePattern(varOrEnumPattern->pattern.get()); auto fbVarOrEnumPattern = NodeFormat::CreateVarOrEnumPattern(builder, fbNodeBase, fbIdentifier, fbPattern); return NodeFormat::CreatePattern( builder, fbNodeBase, NodeFormat::AnyPattern_VAR_OR_ENUM_PATTERN, fbVarOrEnumPattern.Union()); } flatbuffers::Offset NodeWriter::SerializeTuplePattern(AstPattern pattern) { auto tuplePattern = RawStaticCast(pattern); auto fbNodeBase = SerializeNodeBase(tuplePattern); auto leftParenPos = FlatPosCreateHelper(tuplePattern->leftBracePos); auto fbPatterns = FlatVectorCreateHelper( tuplePattern->patterns, &NodeWriter::SerializePattern); auto commaPosVector = CreatePositionVector(tuplePattern->commaPosVector); auto rightParenPos = FlatPosCreateHelper(tuplePattern->rightBracePos); auto fbTuplePattern = NodeFormat::CreateTuplePattern(builder, fbNodeBase, &leftParenPos, fbPatterns, &rightParenPos, commaPosVector); return NodeFormat::CreatePattern(builder, fbNodeBase, NodeFormat::AnyPattern_TUPLE_PATTERN, fbTuplePattern.Union()); } flatbuffers::Offset NodeWriter::SerializeExceptTypePattern(AstPattern pattern) { auto exceptTypePattern = RawStaticCast(pattern); auto fbNodeBase = SerializeNodeBase(exceptTypePattern); auto fbPattern = SerializePattern(exceptTypePattern->pattern.get()); auto patternPos = FlatPosCreateHelper(exceptTypePattern->patternPos); auto colonPos = FlatPosCreateHelper(exceptTypePattern->colonPos); auto fbTypes = FlatVectorCreateHelper(exceptTypePattern->types, &NodeWriter::SerializeType); auto bitOrPosVector = CreatePositionVector(exceptTypePattern->bitOrPosVector); auto fbExceptTypePattern = NodeFormat::CreateExceptTypePattern( builder, fbNodeBase, fbPattern, &patternPos, &colonPos, fbTypes, bitOrPosVector); return NodeFormat::CreatePattern( builder, fbNodeBase, NodeFormat::AnyPattern_EXCEPT_TYPE_PATTERN, fbExceptTypePattern.Union()); } flatbuffers::Offset NodeWriter::SerializeCommandTypePattern(AstPattern pattern) { auto effectTypePattern = RawStaticCast(pattern); auto fbNodeBase = SerializeNodeBase(effectTypePattern); auto fbPattern = SerializePattern(effectTypePattern->pattern.get()); auto patternPos = FlatPosCreateHelper(effectTypePattern->patternPos); auto colonPos = FlatPosCreateHelper(effectTypePattern->colonPos); auto fbTypes = FlatVectorCreateHelper(effectTypePattern->types, &NodeWriter::SerializeType); auto bitOrPosVector = CreatePositionVector(effectTypePattern->bitOrPosVector); auto fbCommandTypePattern = NodeFormat::CreateCommandTypePattern( builder, fbNodeBase, fbPattern, &patternPos, &colonPos, fbTypes, bitOrPosVector); return NodeFormat::CreatePattern( builder, fbNodeBase, NodeFormat::AnyPattern_COMMAND_TYPE_PATTERN, fbCommandTypePattern.Union()); } flatbuffers::Offset NodeWriter::SerializeFuncParam(AstFuncParam funcParam) { if (funcParam == nullptr) { return flatbuffers::Offset(); } auto fbNodeBase = SerializeNodeBase(funcParam); auto fbVarDeclBase = SerializeVarDecl(funcParam.get()); auto colonPos = FlatPosCreateHelper(funcParam->colonPos); auto fbExpr = SerializeExpr((funcParam->assignment).get()); auto commaPos = FlatPosCreateHelper(funcParam->commaPos); auto notMarkPos = FlatPosCreateHelper(funcParam->notMarkPos); return NodeFormat::CreateFuncParam(builder, fbNodeBase, fbVarDeclBase, &colonPos, fbExpr, &commaPos, funcParam->isNamedParam, funcParam->isMemberParam, ¬MarkPos, funcParam->hasLetOrVar); } flatbuffers::Offset NodeWriter::SerializeDeclOfFuncParam(const Decl* decl) { auto funcParam = RawStaticCast(decl); auto fbFuncParam = SerializeFuncParam(funcParam); return NodeFormat::CreateDecl( builder, SerializeDeclBase(funcParam), NodeFormat::AnyDecl_FUNC_PARAM, fbFuncParam.Union()); } flatbuffers::Offset NodeWriter::SerializeBlock(AstBlock block) { if (block == nullptr) { return flatbuffers::Offset(); } auto fbNodeBase = SerializeNodeBase(block); auto leftCurPos = FlatPosCreateHelper(block->leftCurlPos); auto rightCurPos = FlatPosCreateHelper(block->rightCurlPos); auto unsafePos = FlatPosCreateHelper(block->unsafePos); std::vector> vecNode; for (auto& node : block->body) { auto nPtr = node.get(); match (*nPtr)( [&, this](const Expr& expr) { auto fbExpr = SerializeExpr(&expr); // flatbuffers::Offset auto nodeBase = SerializeNodeBase(&expr); auto fbNode = NodeFormat::CreateNode(builder, nodeBase, NodeFormat::AnyNode_EXPR, fbExpr.Union()); vecNode.push_back(fbNode); }, [&, this](const Decl& decl) { auto fbDecl = SerializeDecl(&decl); // flatbuffers::Offset auto nodeBase = SerializeNodeBase(&decl); auto fbNode = NodeFormat::CreateNode(builder, nodeBase, NodeFormat::AnyNode_DECL, fbDecl.Union()); vecNode.push_back(fbNode); }, [&]() { Errorln("Match Nothing in Block!"); }); } auto fbBody = builder.CreateVector(vecNode); return NodeFormat::CreateBlock( builder, fbNodeBase, &leftCurPos, fbBody, &rightCurPos, block->TestAttr(Attribute::UNSAFE), &unsafePos); } flatbuffers::Offset NodeWriter::SerializeFuncBody(AstFuncBody funcBody) { if (funcBody == nullptr) { return flatbuffers::Offset(); } auto fbNodeBaseFnBody = SerializeNodeBase(funcBody); auto paramList = (funcBody->paramLists)[0].get(); // a pointer to FuncParamList auto fbNodeBasePaList = SerializeNodeBase(paramList); auto leftParenPos = FlatPosCreateHelper(paramList->leftParenPos); auto rightParenPos = FlatPosCreateHelper(paramList->rightParenPos); std::vector> vecNode; for (auto& param : paramList->params) { auto nPtr = param.get(); match (*nPtr)([&, this](const MacroExpandParam& mep) { vecNode.push_back(SerializeMacroExpandParam(&mep)); }, [&, this]() { vecNode.push_back(SerializeFuncParam(param)); }); } auto fbParamList = NodeFormat::CreateFuncParamList( builder, fbNodeBasePaList, &leftParenPos, builder.CreateVector(vecNode), &rightParenPos); // Serialize FuncBlock auto bodyPtr = (funcBody->body).get(); auto arrowPos = FlatPosCreateHelper(funcBody->doubleArrowPos); auto colonPos = FlatPosCreateHelper(funcBody->colonPos); auto fbRetType = SerializeType(funcBody->retType.get()); auto hasBody = bodyPtr != nullptr; auto fbBody = SerializeBlock(bodyPtr); auto fbGeneric = SerializeGeneric(funcBody->generic.get()); return NodeFormat::CreateFuncBody( builder, fbNodeBaseFnBody, fbParamList, &arrowPos, &colonPos, fbRetType, hasBody, fbBody, fbGeneric); } flatbuffers::Offset NodeWriter::SerializeDeclBase(AstDecl decl) { if (decl == nullptr) { return flatbuffers::Offset(); } // Serialize DeclBase for all Decls auto fbNodeBase = SerializeNodeBase(decl); auto identifier = builder.CreateString(decl->identifier.GetRawText()); auto identifierPos = FlatPosCreateHelper(decl->identifier.GetRawPos()); auto keywordPos = FlatPosCreateHelper(decl->keywordPos); auto fbAnnVec = FlatVectorCreateHelper( decl->annotations, &NodeWriter::SerializeAnnotation); std::vector> vecMod; auto modifiersVec = SortModifierByPos(decl->modifiers); for (auto& mod : modifiersVec) { auto fbMod = SerializeModifier(mod); vecMod.push_back(fbMod); } auto fbModVec = builder.CreateVector(vecMod); auto generic = SerializeGeneric(decl->generic.get()); auto isConst = decl->IsConst(); return NodeFormat::CreateDeclBase( builder, fbNodeBase, identifier, &identifierPos, &keywordPos, fbAnnVec, fbModVec, generic, isConst); } flatbuffers::Offset NodeWriter::SerializeTypeBase(AstType type) { if (type == nullptr) { return flatbuffers::Offset(); } auto fbBase = SerializeNodeBase(type); auto commaPos = FlatPosCreateHelper(type->commaPos); auto bitAndPos = FlatPosCreateHelper(type->bitAndPos); auto typeParameterName = builder.CreateString(type->GetTypeParameterNameRawText()); auto colonPos = FlatPosCreateHelper(type->colonPos); auto typePos = FlatPosCreateHelper(type->typePos); return NodeFormat::CreateTypeBase(builder, fbBase, &commaPos, typeParameterName, &colonPos, &typePos, &bitAndPos); } flatbuffers::Offset NodeWriter::SerializeRefType(const RefType* refType) { if (refType == nullptr) { return flatbuffers::Offset(); } auto fbTypeBase = SerializeTypeBase(refType); auto ref = refType->ref; auto identifier = builder.CreateString(ref.identifier.GetRawText()); auto identifierPos = FlatPosCreateHelper(ref.identifier.GetRawPos()); auto fbRef = NodeFormat::CreateReference(builder, identifier, &identifierPos); auto leftAnglePos = FlatPosCreateHelper(refType->leftAnglePos); auto fbTypeVec = FlatVectorCreateHelper(refType->typeArguments, &NodeWriter::SerializeType); auto rightAnglePos = FlatPosCreateHelper(refType->rightAnglePos); return NodeFormat::CreateRefType(builder, fbTypeBase, fbRef, &leftAnglePos, fbTypeVec, &rightAnglePos); } flatbuffers::Offset NodeWriter::SerializeRefType(const Type* type) { auto refType = RawStaticCast(type); auto fbRefType = SerializeRefType(refType); return NodeFormat::CreateType(builder, SerializeTypeBase(refType), NodeFormat::AnyType_REF_TYPE, fbRefType.Union()); } flatbuffers::Offset NodeWriter::SerializePrimitiveType(const PrimitiveType* primitiveType) { if (primitiveType == nullptr) { return flatbuffers::Offset(); } auto fbTypeBase = SerializeTypeBase(primitiveType); auto fbTypeStr = builder.CreateString(primitiveType->str); auto typeKind = static_cast(primitiveType->kind); return NodeFormat::CreatePrimitiveType(builder, fbTypeBase, fbTypeStr, typeKind); } flatbuffers::Offset NodeWriter::SerializePrimitiveType(const Type* type) { auto primitiveType = RawStaticCast(type); auto fbPrimType = SerializePrimitiveType(primitiveType); return NodeFormat::CreateType( builder, SerializeTypeBase(primitiveType), NodeFormat::AnyType_PRIMITIVE_TYPE, fbPrimType.Union()); } flatbuffers::Offset NodeWriter::SerializeThisType(AstType type) { auto thisType = RawStaticCast(type); auto fbTypeBase = SerializeTypeBase(thisType); auto fbThisType = NodeFormat::CreateThisType(builder, fbTypeBase); return NodeFormat::CreateType(builder, fbTypeBase, NodeFormat::AnyType_THIS_TYPE, fbThisType.Union()); } flatbuffers::Offset NodeWriter::SerializeParenType(AstType type) { auto parenType = RawStaticCast(type); auto fbTypeBase = SerializeTypeBase(parenType); auto leftParenPos = FlatPosCreateHelper(parenType->leftParenPos); auto ptype = SerializeType(parenType->type.get()); auto rightParenPos = FlatPosCreateHelper(parenType->rightParenPos); auto fbParenType = NodeFormat::CreateParenType(builder, fbTypeBase, &leftParenPos, ptype, &rightParenPos); return NodeFormat::CreateType(builder, fbTypeBase, NodeFormat::AnyType_PAREN_TYPE, fbParenType.Union()); } flatbuffers::Offset NodeWriter::SerializeQualifiedType(AstType type) { auto qualifiedType = RawStaticCast(type); auto fbTypeBase = SerializeTypeBase(qualifiedType); auto baseType = SerializeType(qualifiedType->baseType.get()); auto dotPos = FlatPosCreateHelper(qualifiedType->dotPos); auto fieldStr = builder.CreateString(qualifiedType->field.GetRawText()); auto fieldPos = FlatPosCreateHelper(qualifiedType->field.GetRawPos()); auto leftAnglePos = FlatPosCreateHelper(qualifiedType->leftAnglePos); auto typeArguments = FlatVectorCreateHelper( qualifiedType->typeArguments, &NodeWriter::SerializeType); auto rightAnglePos = FlatPosCreateHelper(qualifiedType->rightAnglePos); auto fbQualifiedType = NodeFormat::CreateQualifiedType( builder, fbTypeBase, baseType, &dotPos, fieldStr, &fieldPos, &leftAnglePos, typeArguments, &rightAnglePos); return NodeFormat::CreateType(builder, fbTypeBase, NodeFormat::AnyType_QUALIFIED_TYPE, fbQualifiedType.Union()); } flatbuffers::Offset NodeWriter::SerializeOptionType(AstType type) { auto optionType = RawStaticCast(type); auto fbTypeBase = SerializeTypeBase(optionType); auto componentType = SerializeType(optionType->componentType.get()); auto questVector = CreatePositionVector(optionType->questVector); auto fbOptionType = NodeFormat::CreateOptionType( builder, fbTypeBase, componentType, static_cast(optionType->questNum), questVector); return NodeFormat::CreateType(builder, fbTypeBase, NodeFormat::AnyType_OPTION_TYPE, fbOptionType.Union()); } flatbuffers::Offset NodeWriter::SerializeTupleType(AstType type) { auto tupleType = RawStaticCast(type); auto fbTypeBase = SerializeTypeBase(tupleType); auto fieldTypes = FlatVectorCreateHelper(tupleType->fieldTypes, &NodeWriter::SerializeType); auto leftParenPos = FlatPosCreateHelper(tupleType->leftParenPos); auto rightParenPos = FlatPosCreateHelper(tupleType->rightParenPos); auto commaPosVector = CreatePositionVector(tupleType->commaPosVector); if (tupleType->commaPosVector.size() == 0) { Errorln("Legacy tuple type syntax no longer allowed after version 0.28.4, use ',' instead."); } auto fbTupleType = NodeFormat::CreateTupleType(builder, fbTypeBase, fieldTypes, &leftParenPos, &rightParenPos, commaPosVector); return NodeFormat::CreateType(builder, fbTypeBase, NodeFormat::AnyType_TUPLE_TYPE, fbTupleType.Union()); } flatbuffers::Offset NodeWriter::SerializeFuncType(AstType type) { auto funcType = RawStaticCast(type); auto fbTypeBase = SerializeTypeBase(funcType); auto leftParenPos = FlatPosCreateHelper(funcType->leftParenPos); auto fbTypeVec = FlatVectorCreateHelper(funcType->paramTypes, &NodeWriter::SerializeType); auto rightParenPos = FlatPosCreateHelper(funcType->rightParenPos); auto arrowPos = FlatPosCreateHelper(funcType->arrowPos); auto fbRetType = SerializeType(funcType->retType.get()); auto fbFuncType = NodeFormat::CreateFuncType( builder, fbTypeBase, &leftParenPos, fbTypeVec, &rightParenPos, &arrowPos, fbRetType, funcType->isC); return NodeFormat::CreateType(builder, fbTypeBase, NodeFormat::AnyType_FUNC_TYPE, fbFuncType.Union()); } flatbuffers::Offset NodeWriter::SerializeVArrayType(AstType type) { auto vArrayType = RawStaticCast(type); auto fbTypeBase = SerializeTypeBase(vArrayType); auto varrayPos = FlatPosCreateHelper(vArrayType->varrayPos); auto leftAnglePos = FlatPosCreateHelper(vArrayType->leftAnglePos); auto typeArgument = SerializeType(vArrayType->typeArgument.get()); auto constantType = SerializeType(vArrayType->constantType.get()); auto rightAnglePos = FlatPosCreateHelper(vArrayType->rightAnglePos); auto fbVArrayType = NodeFormat::CreateVArrayType( builder, fbTypeBase, &varrayPos, &leftAnglePos, typeArgument, constantType, &rightAnglePos); return NodeFormat::CreateType(builder, fbTypeBase, NodeFormat::AnyType_VARRAY_TYPE, fbVArrayType.Union()); } flatbuffers::Offset NodeWriter::SerializeConstantType(AstType type) { auto constantType = RawStaticCast(type); auto fbTypeBase = SerializeTypeBase(constantType); auto constantExpr = SerializeExpr(constantType->constantExpr.get()); auto dollarPos = FlatPosCreateHelper(constantType->dollarPos); auto fbConstantType = NodeFormat::CreateConstantType(builder, fbTypeBase, constantExpr, &dollarPos); return NodeFormat::CreateType(builder, fbTypeBase, NodeFormat::AnyType_CONSTANT_TYPE, fbConstantType.Union()); } flatbuffers::Offset NodeWriter::SerializeType(AstType type) { if (type == nullptr) { return flatbuffers::Offset(); } static std::unordered_map> serializeTypeMap = { {ASTKind::REF_TYPE, [](NodeWriter& nw, AstType type) { return nw.SerializeRefType(type); }}, {ASTKind::PRIMITIVE_TYPE, [](NodeWriter& nw, AstType type) { return nw.SerializePrimitiveType(type); }}, {ASTKind::FUNC_TYPE, [](NodeWriter& nw, AstType type) { return nw.SerializeFuncType(type); }}, {ASTKind::THIS_TYPE, [](NodeWriter& nw, AstType type) { return nw.SerializeThisType(type); }}, {ASTKind::PAREN_TYPE, [](NodeWriter& nw, AstType type) { return nw.SerializeParenType(type); }}, {ASTKind::QUALIFIED_TYPE, [](NodeWriter& nw, AstType type) { return nw.SerializeQualifiedType(type); }}, {ASTKind::OPTION_TYPE, [](NodeWriter& nw, AstType type) { return nw.SerializeOptionType(type); }}, {ASTKind::TUPLE_TYPE, [](NodeWriter& nw, AstType type) { return nw.SerializeTupleType(type); }}, {ASTKind::VARRAY_TYPE, [](NodeWriter& nw, AstType type) { return nw.SerializeVArrayType(type); }}, {ASTKind::CONSTANT_TYPE, [](NodeWriter& nw, AstType type) { return nw.SerializeConstantType(type); }}, }; // Serialize Type. auto serializeFunc = serializeTypeMap.find(type->astKind); if (serializeFunc != serializeTypeMap.end()) { return serializeFunc->second(*this, type); } Errorln("Type Not Supported in Libast Yet\n"); return flatbuffers::Offset(); } flatbuffers::Offset NodeWriter::SerializeVarDecl(const VarDecl* varDecl) { if (varDecl == nullptr) { return flatbuffers::Offset(); } auto fbDeclBase = SerializeDeclBase(varDecl); auto typePtr = varDecl->type.get(); auto fbType = SerializeType(typePtr); auto colonPos = FlatPosCreateHelper(varDecl->colonPos); auto initializer = SerializeExpr(varDecl->initializer.get()); auto assignPos = FlatPosCreateHelper(varDecl->assignPos); auto isVar = varDecl->isVar; auto isEnumConstruct = varDecl->TestAttr(Attribute::ENUM_CONSTRUCTOR); // Situations where keywords are not required: // case1: the varDecl is in pattern auto emptyKeyword = varDecl->parentPattern == nullptr ? false : true; // case2: the varDecl is in tryWithResource if (varDecl->parentPattern == nullptr) { emptyKeyword = varDecl->isResourceVar; } return NodeFormat::CreateVarDecl( builder, fbDeclBase, fbType, &colonPos, initializer, &assignPos, isVar, isEnumConstruct, emptyKeyword); } flatbuffers::Offset NodeWriter::SerializeVarDecl(const Decl* decl) { auto varDecl = RawStaticCast(decl); auto fbVarDecl = SerializeVarDecl(varDecl); auto fbDeclBase = SerializeDeclBase(varDecl); return NodeFormat::CreateDecl(builder, fbDeclBase, NodeFormat::AnyDecl_VAR_DECL, fbVarDecl.Union()); } flatbuffers::Offset NodeWriter::SerializeFuncDecl(const FuncDecl* funcDecl) { if (funcDecl == nullptr) { return flatbuffers::Offset(); } // Serialize FuncDecl. auto fbDeclBase = SerializeDeclBase(funcDecl); auto leftParenPos = FlatPosCreateHelper(funcDecl->leftParenPos); auto rightParenPos = FlatPosCreateHelper(funcDecl->rightParenPos); auto fbFuncBody = SerializeFuncBody(funcDecl->funcBody.get()); auto isEnumConstruct = funcDecl->TestAttr(Attribute::ENUM_CONSTRUCTOR); return NodeFormat::CreateFuncDecl(builder, fbDeclBase, &leftParenPos, &rightParenPos, fbFuncBody, funcDecl->isSetter, funcDecl->isGetter, static_cast(funcDecl->op), isEnumConstruct); } flatbuffers::Offset NodeWriter::SerializeFuncDecl(const Decl* decl) { auto funcDecl = RawStaticCast(decl); auto fbFuncDecl = SerializeFuncDecl(funcDecl); auto fbDeclBase = SerializeDeclBase(funcDecl); return NodeFormat::CreateDecl(builder, fbDeclBase, NodeFormat::AnyDecl_FUNC_DECL, fbFuncDecl.Union()); } flatbuffers::Offset NodeWriter::SerializeMainDecl(const MainDecl* mainDecl) { if (mainDecl == nullptr) { return flatbuffers::Offset(); } // Serialize MainDecl. auto fbDeclBase = SerializeDeclBase(mainDecl); auto fbFuncBody = SerializeFuncBody(mainDecl->funcBody.get()); return NodeFormat::CreateMainDecl(builder, fbDeclBase, fbFuncBody); } flatbuffers::Offset NodeWriter::SerializeMainDecl(const Decl* decl) { auto mainDecl = RawStaticCast(decl); auto fbMainDecl = SerializeMainDecl(mainDecl); auto fbDeclBase = SerializeDeclBase(mainDecl); return NodeFormat::CreateDecl(builder, fbDeclBase, NodeFormat::AnyDecl_MAIN_DECL, fbMainDecl.Union()); } flatbuffers::Offset NodeWriter::SerializeMacroDecl(const MacroDecl* macroDecl) { if (macroDecl == nullptr) { return flatbuffers::Offset(); } // Serialize MacroDecl. auto fbDeclBase = SerializeDeclBase(macroDecl); auto leftParenPos = FlatPosCreateHelper(macroDecl->leftParenPos); auto rightParenPos = FlatPosCreateHelper(macroDecl->rightParenPos); auto fbFuncBody = SerializeFuncBody(macroDecl->funcBody.get()); return NodeFormat::CreateMacroDecl(builder, fbDeclBase, &leftParenPos, &rightParenPos, fbFuncBody); } flatbuffers::Offset NodeWriter::SerializeMacroDecl(const Decl* decl) { auto macroDecl = RawStaticCast(decl); auto fbMacroDecl = SerializeMacroDecl(macroDecl); auto fbDeclBase = SerializeDeclBase(macroDecl); return NodeFormat::CreateDecl(builder, fbDeclBase, NodeFormat::AnyDecl_MACRO_DECL, fbMacroDecl.Union()); } flatbuffers::Offset NodeWriter::SerializeGenericParamDecl( AstGenericParamDecl genericParamDecl) { if (genericParamDecl == nullptr) { return flatbuffers::Offset(); } auto fbBase = SerializeDeclBase(genericParamDecl); auto commaPos = FlatPosCreateHelper(genericParamDecl->commaPos); return NodeFormat::CreateGenericParamDecl(builder, fbBase, &commaPos); } flatbuffers::Offset NodeWriter::SerializeGenericConstraint( AstGenericConstraint genericConstraint) { if (genericConstraint == nullptr) { return flatbuffers::Offset(); } auto fbBase = SerializeNodeBase(genericConstraint); auto wherePos = FlatPosCreateHelper(genericConstraint->wherePos); auto fbRefType = SerializeRefType(genericConstraint->type.get()); auto operatorPos = FlatPosCreateHelper(genericConstraint->operatorPos); auto fbUpperBounds = FlatVectorCreateHelper( genericConstraint->upperBounds, &NodeWriter::SerializeType); auto bitAndPos = CreatePositionVector(genericConstraint->bitAndPos); auto commaPos = FlatPosCreateHelper(genericConstraint->commaPos); return NodeFormat::CreateGenericConstraint( builder, fbBase, &wherePos, fbRefType, &operatorPos, fbUpperBounds, bitAndPos, &commaPos); } flatbuffers::Offset NodeWriter::SerializeGeneric(AstGeneric generic) { if (generic == nullptr) { return flatbuffers::Offset(); } auto leftAnglePos = FlatPosCreateHelper(generic->leftAnglePos); auto fbTypeParameters = FlatVectorCreateHelper( generic->typeParameters, &NodeWriter::SerializeGenericParamDecl); auto rightAnglePos = FlatPosCreateHelper(generic->rightAnglePos); auto fbGenericConstraints = FlatVectorCreateHelper( generic->genericConstraints, &NodeWriter::SerializeGenericConstraint); return NodeFormat::CreateGeneric(builder, &leftAnglePos, fbTypeParameters, &rightAnglePos, fbGenericConstraints); } flatbuffers::Offset NodeWriter::SerializeClassBody(AstClassBody classBody) { if (classBody == nullptr) { return flatbuffers::Offset(); } // Serialize ClassBody auto fbBase = SerializeNodeBase(classBody); auto leftCurlPos = FlatPosCreateHelper(classBody->leftCurlPos); auto fbDecls = FlatVectorCreateHelper(classBody->decls, &NodeWriter::SerializeDecl); auto rightCurlPos = FlatPosCreateHelper(classBody->rightCurlPos); return NodeFormat::CreateClassBody(builder, fbBase, &leftCurlPos, fbDecls, &rightCurlPos); } flatbuffers::Offset NodeWriter::SerializeClassDecl(AstDecl decl) { auto classDecl = RawStaticCast(decl); auto fbBase = SerializeDeclBase(classDecl); auto upperBoundPos = FlatPosCreateHelper(classDecl->upperBoundPos); auto fbSuperTypes = FlatVectorCreateHelper(classDecl->inheritedTypes, &NodeWriter::SerializeType); auto fbBody = SerializeClassBody(classDecl->body.get()); auto fbClassDecl = NodeFormat::CreateClassDecl(builder, fbBase, &upperBoundPos, fbSuperTypes, fbBody); return NodeFormat::CreateDecl(builder, fbBase, NodeFormat::AnyDecl_CLASS_DECL, fbClassDecl.Union()); } flatbuffers::Offset NodeWriter::SerializeInterfaceBody(AstInterfaceBody interfaceBody) { if (interfaceBody == nullptr) { return flatbuffers::Offset(); } auto fbBase = SerializeNodeBase(interfaceBody); auto leftCurlPos = FlatPosCreateHelper(interfaceBody->leftCurlPos); // Serialize vector of decl auto fbDecls = FlatVectorCreateHelper(interfaceBody->decls, &NodeWriter::SerializeDecl); auto rightCurlPos = FlatPosCreateHelper(interfaceBody->rightCurlPos); return NodeFormat::CreateInterfaceBody(builder, fbBase, &leftCurlPos, fbDecls, &rightCurlPos); } flatbuffers::Offset NodeWriter::SerializeInterfaceDecl(AstDecl decl) { auto interfaceDecl = RawStaticCast(decl); auto fbBase = SerializeDeclBase(interfaceDecl); auto upperBoundPos = FlatPosCreateHelper(interfaceDecl->upperBoundPos); auto fbSuperTypes = FlatVectorCreateHelper( interfaceDecl->inheritedTypes, &NodeWriter::SerializeType); auto fbBody = SerializeInterfaceBody(interfaceDecl->body.get()); auto fbInterfaceDecl = NodeFormat::CreateInterfaceDecl(builder, fbBase, &upperBoundPos, fbSuperTypes, fbBody); return NodeFormat::CreateDecl(builder, fbBase, NodeFormat::AnyDecl_INTERFACE_DECL, fbInterfaceDecl.Union()); } flatbuffers::Offset NodeWriter::SerializePrimaryCtorDecl(AstDecl decl) { auto primaryCtorDecl = RawStaticCast(decl); auto fbBase = SerializeDeclBase(primaryCtorDecl); auto fbFuncBody = SerializeFuncBody(primaryCtorDecl->funcBody.get()); auto fbPrimaryCtorDecl = NodeFormat::CreatePrimaryCtorDecl(builder, fbBase, fbFuncBody); return NodeFormat::CreateDecl(builder, fbBase, NodeFormat::AnyDecl_PRIMARY_CTOR_DECL, fbPrimaryCtorDecl.Union()); } flatbuffers::Offset NodeWriter::SerializeDecl(AstDecl decl) { if (decl == nullptr) { return flatbuffers::Offset(); } static std::unordered_map> serializeDeclMap = { {ASTKind::VAR_DECL, [](NodeWriter& nw, AstDecl decl) { return nw.SerializeVarDecl(decl); }}, {ASTKind::FUNC_DECL, [](NodeWriter& nw, AstDecl decl) { return nw.SerializeFuncDecl(decl); }}, {ASTKind::MAIN_DECL, [](NodeWriter& nw, AstDecl decl) { return nw.SerializeMainDecl(decl); }}, {ASTKind::STRUCT_DECL, [](NodeWriter& nw, AstDecl decl) { return nw.SerializeStructDecl(decl); }}, {ASTKind::CLASS_DECL, [](NodeWriter& nw, AstDecl decl) { return nw.SerializeClassDecl(decl); }}, {ASTKind::INTERFACE_DECL, [](NodeWriter& nw, AstDecl decl) { return nw.SerializeInterfaceDecl(decl); }}, {ASTKind::PRIMARY_CTOR_DECL, [](NodeWriter& nw, AstDecl decl) { return nw.SerializePrimaryCtorDecl(decl); }}, {ASTKind::PROP_DECL, [](NodeWriter& nw, AstDecl decl) { return nw.SerializePropDecl(decl); }}, {ASTKind::ENUM_DECL, [](NodeWriter& nw, AstDecl decl) { return nw.SerializeEnumDecl(decl); }}, {ASTKind::TYPE_ALIAS_DECL, [](NodeWriter& nw, AstDecl decl) { return nw.SerializeTypeAliasDecl(decl); }}, {ASTKind::EXTEND_DECL, [](NodeWriter& nw, AstDecl decl) { return nw.SerializeExtendDecl(decl); }}, {ASTKind::MACRO_DECL, [](NodeWriter& nw, AstDecl decl) { return nw.SerializeMacroDecl(decl); }}, {ASTKind::MACRO_EXPAND_DECL, [](NodeWriter& nw, AstDecl decl) { return nw.SerializeMacroExpandDecl(decl); }}, {ASTKind::FUNC_PARAM, [](NodeWriter& nw, AstDecl decl) { return nw.SerializeDeclOfFuncParam(decl); }}, {ASTKind::VAR_WITH_PATTERN_DECL, [](NodeWriter& nw, AstDecl decl) { return nw.SerializeVarWithPatternDecl(decl); }}, }; // Serialize Decl. auto serializeFunc = serializeDeclMap.find(decl->astKind); if (serializeFunc != serializeDeclMap.end()) { return serializeFunc->second(*this, decl); } Errorln("Decl Not Supported in Libast Yet\n"); return flatbuffers::Offset(); } flatbuffers::Offset NodeWriter::SerializeStructBody(AstStructBody structBody) { if (structBody == nullptr) { return flatbuffers::Offset(); } auto fbNodeBase = SerializeNodeBase(structBody); auto leftCurlPos = FlatPosCreateHelper(structBody->leftCurlPos); auto fbDecls = FlatVectorCreateHelper(structBody->decls, &NodeWriter::SerializeDecl); auto rightCurlPos = FlatPosCreateHelper(structBody->rightCurlPos); return NodeFormat::CreateStructBody(builder, fbNodeBase, &leftCurlPos, fbDecls, &rightCurlPos); } flatbuffers::Offset NodeWriter::SerializeStructDecl(AstDecl decl) { auto structDecl = RawStaticCast(decl); auto fbDeclBase = SerializeDeclBase(structDecl); auto structBodyPtr = structDecl->body.get(); auto fbStructBody = SerializeStructBody(structBodyPtr); auto upperBoundPos = FlatPosCreateHelper(structDecl->upperBoundPos); auto fbSuperTypes = FlatVectorCreateHelper(structDecl->inheritedTypes, &NodeWriter::SerializeType); auto fbStructDecl = NodeFormat::CreateStructDecl(builder, fbDeclBase, fbStructBody, &upperBoundPos, fbSuperTypes); return NodeFormat::CreateDecl(builder, fbDeclBase, NodeFormat::AnyDecl_STRUCT_DECL, fbStructDecl.Union()); } flatbuffers::Offset NodeWriter::SerializeVarWithPatternDecl(AstDecl decl) { auto varWithPatternDecl = RawStaticCast(decl); auto fbDeclBase = SerializeDeclBase(varWithPatternDecl); auto type = SerializeType(varWithPatternDecl->type.get()); auto colonPos = FlatPosCreateHelper(varWithPatternDecl->colonPos); auto initializer = SerializeExpr(varWithPatternDecl->initializer.get()); auto pattern = SerializePattern(varWithPatternDecl->irrefutablePattern.get()); auto assignPos = FlatPosCreateHelper(varWithPatternDecl->assignPos); auto isVar = varWithPatternDecl->isVar; auto fbVarWithPatternDecl = NodeFormat::CreateVarWithPatternDecl( builder, fbDeclBase, type, &colonPos, initializer, pattern, &assignPos, isVar); return NodeFormat::CreateDecl( builder, fbDeclBase, NodeFormat::AnyDecl_VAR_WITH_PATTERN_DECL, fbVarWithPatternDecl.Union()); } flatbuffers::Offset NodeWriter::SerializeEnumDecl(AstDecl decl) { auto enumDecl = RawStaticCast(decl); auto fbDeclBase = SerializeDeclBase(enumDecl); auto leftCurlPos = FlatPosCreateHelper(enumDecl->leftCurlPos); auto constructors = FlatVectorCreateHelper(enumDecl->constructors, &NodeWriter::SerializeDecl); auto bitOrPosVector = CreatePositionVector(enumDecl->bitOrPosVector); auto members = FlatVectorCreateHelper(enumDecl->members, &NodeWriter::SerializeDecl); auto rightCurlPos = FlatPosCreateHelper(enumDecl->rightCurlPos); auto upperBoundPos = FlatPosCreateHelper(enumDecl->upperBoundPos); auto ellipsisPos = FlatPosCreateHelper(enumDecl->ellipsisPos); auto fbSuperTypes = FlatVectorCreateHelper(enumDecl->inheritedTypes, &NodeWriter::SerializeType); auto fbEnumDecl = NodeFormat::CreateEnumDecl(builder, fbDeclBase, enumDecl->hasArguments, &leftCurlPos, constructors, bitOrPosVector, members, &rightCurlPos, &upperBoundPos, fbSuperTypes, enumDecl->hasEllipsis, &ellipsisPos); return NodeFormat::CreateDecl(builder, fbDeclBase, NodeFormat::AnyDecl_ENUM_DECL, fbEnumDecl.Union()); } flatbuffers::Offset NodeWriter::SerializePropDecl(AstDecl decl) { auto propDecl = RawStaticCast(decl); auto fbVarDecl = SerializeVarDecl(propDecl); auto fbDeclBase = SerializeDeclBase(propDecl); auto colonPos = FlatPosCreateHelper(propDecl->colonPos); auto leftCurlPos = FlatPosCreateHelper(propDecl->leftCurlPos); auto fbGetters = FlatVectorCreateHelper( propDecl->getters, &NodeWriter::SerializeFuncDecl); auto fbSetters = FlatVectorCreateHelper( propDecl->setters, &NodeWriter::SerializeFuncDecl); auto rightCurlPos = FlatPosCreateHelper(propDecl->rightCurlPos); auto fbPropDecl = NodeFormat::CreatePropDecl(builder, fbVarDecl, &colonPos, &leftCurlPos, fbGetters, fbSetters, &rightCurlPos); return NodeFormat::CreateDecl(builder, fbDeclBase, NodeFormat::AnyDecl_PROP_DECL, fbPropDecl.Union()); } flatbuffers::Offset NodeWriter::SerializeTypeAliasDecl(AstDecl decl) { auto typeAliasDecl = RawStaticCast(decl); auto fbDeclBase = SerializeDeclBase(typeAliasDecl); auto assignPos = FlatPosCreateHelper(typeAliasDecl->assignPos); auto type = SerializeType(typeAliasDecl->type.get()); auto fbTypeAliasDecl = NodeFormat::CreateTypeAliasDecl(builder, fbDeclBase, &assignPos, type); return NodeFormat::CreateDecl(builder, fbDeclBase, NodeFormat::AnyDecl_TYPE_ALIAS_DECL, fbTypeAliasDecl.Union()); } flatbuffers::Offset NodeWriter::SerializeExtendDecl(AstDecl decl) { auto extendDecl = RawStaticCast(decl); auto fbDeclBase = SerializeDeclBase(extendDecl); auto extendedType = SerializeType(extendDecl->extendedType.get()); auto upperBoundPos = FlatPosCreateHelper(extendDecl->upperBoundPos); auto interfaces = FlatVectorCreateHelper(extendDecl->inheritedTypes, &NodeWriter::SerializeType); auto wherePos = FlatPosCreateHelper(extendDecl->wherePos); auto leftCurlPos = FlatPosCreateHelper(extendDecl->leftCurlPos); auto members = FlatVectorCreateHelper(extendDecl->members, &NodeWriter::SerializeDecl); auto rightCurlPos = FlatPosCreateHelper(extendDecl->rightCurlPos); auto fbExtendDecl = NodeFormat::CreateExtendDecl( builder, fbDeclBase, extendedType, &upperBoundPos, interfaces, &wherePos, &leftCurlPos, members, &rightCurlPos); return NodeFormat::CreateDecl(builder, fbDeclBase, NodeFormat::AnyDecl_EXTEND_DECL, fbExtendDecl.Union()); } flatbuffers::Offset NodeWriter::SerializeMacroExpandDecl(const Decl* decl) { auto macroExpandDecl = RawStaticCast(decl); auto fbMacroExpandDecl = SerializeMacroExpandDecl(macroExpandDecl); return NodeFormat::CreateDecl( builder, SerializeDeclBase(macroExpandDecl), NodeFormat::AnyDecl_MACRO_EXPAND_DECL, fbMacroExpandDecl.Union()); } flatbuffers::Offset NodeWriter::SerializeMacroExpandDecl( const MacroExpandDecl* macroExpandDecl) { if (macroExpandDecl == nullptr) { return flatbuffers::Offset(); } auto base = SerializeDeclBase(macroExpandDecl); auto invocation = MacroInvocationCreateHelper(macroExpandDecl->invocation); return NodeFormat::CreateMacroExpandDecl(builder, base, invocation); } flatbuffers::Offset NodeWriter::SerializeMacroExpandParamOfDecl(const Decl* decl) { auto macroExpandParam = RawStaticCast(decl); auto fbMacroExpandParam = SerializeMacroExpandParam(macroExpandParam); return NodeFormat::CreateDecl( builder, SerializeDeclBase(macroExpandParam), NodeFormat::AnyDecl_MACRO_EXPAND_PARAM, fbMacroExpandParam.Union()); } flatbuffers::Offset NodeWriter::SerializeMacroExpandParam(AstMacroExpandParam mep) { auto macroExpandParam = RawStaticCast(mep); auto base = SerializeFuncParam(macroExpandParam); auto fbNodeBase = SerializeNodeBase(mep); auto fbVarDeclBase = SerializeVarDecl(mep.get()); auto colonPos = FlatPosCreateHelper(mep->colonPos); auto fbExpr = SerializeExpr((mep->assignment).get()); auto commaPos = FlatPosCreateHelper(mep->commaPos); auto notMarkPos = FlatPosCreateHelper(mep->notMarkPos); auto invocation = MacroInvocationCreateHelper(mep->invocation); auto mepNode = NodeFormat::CreateMacroExpandParam(builder, base, invocation); return NodeFormat::CreateFuncParam(builder, fbNodeBase, fbVarDeclBase, &colonPos, fbExpr, &commaPos, mep->isNamedParam, mep->isMemberParam, ¬MarkPos, mep->hasLetOrVar, NodeFormat::MacroParam_MACRO_EXPAND_PARAM, mepNode.Union()); } flatbuffers::Offset NodeWriter::SerializeAnnotation(AstAnnotation annotation) { if (annotation == nullptr) { return flatbuffers::Offset(); } auto base = SerializeNodeBase(annotation); auto isCompileTimeVisible = annotation->isCompileTimeVisible; auto kind = static_cast(annotation->kind); auto identPos = FlatPosCreateHelper(annotation->identifier.Begin()); auto identifier = builder.CreateString(annotation->identifier.Val()); auto lsquarePos = FlatPosCreateHelper(annotation->lsquarePos); auto fbArgs = FlatVectorCreateHelper( annotation->args, &NodeWriter::SerializeFuncArg); auto rsquarePos = FlatPosCreateHelper(annotation->rsquarePos); auto overlowStrategy = builder.CreateString(OverflowStrategyName(annotation->overflowStrategy)); std::vector> vecAttrs; for (auto& item : annotation->attrs) { auto fbItem = builder.CreateString(item.Value()); vecAttrs.push_back(fbItem); } auto fbAttrs = builder.CreateVector(vecAttrs); auto condExpr = SerializeExpr(annotation->condExpr.get()); return NodeFormat::CreateAnnotation(builder, base, kind, &identPos, identifier, fbArgs, overlowStrategy, fbAttrs, condExpr, &lsquarePos, &rsquarePos, isCompileTimeVisible); } flatbuffers::Offset NodeWriter::SerializeModifier(AstModifier modifier) { if (modifier == nullptr) { return flatbuffers::Offset(); } auto base = SerializeNodeBase(modifier); auto kind = static_cast(modifier->modifier); auto isExplicit = modifier->isExplicit; return NodeFormat::CreateModifier(builder, base, kind, isExplicit); } std::vector> NodeWriter::TokensVectorCreateHelper( std::vector tokenVector) { std::vector> vecToken; for (auto& token : tokenVector) { auto kind = static_cast(token.kind); auto value = builder.CreateString(token.Value()); auto& escapes = GetEscapeTokenKinds(); NodeFormat::Position pos; if (std::find(escapes.begin(), escapes.end(), token.kind) != escapes.end() && token.Begin() + token.Value().size() + 1 == token.End()) { pos = FlatPosCreateHelper(token.Begin() + 1); } else { pos = FlatPosCreateHelper(token.Begin()); } vecToken.push_back(NodeFormat::CreateToken(builder, kind, value, &pos)); } return vecToken; } flatbuffers::Offset NodeWriter::MacroInvocationCreateHelper( const MacroInvocation& macroInvocation) { auto fullName = builder.CreateString(macroInvocation.fullName); auto identifier = builder.CreateString(macroInvocation.identifier); auto identifierPos = FlatPosCreateHelper(macroInvocation.identifierPos); auto leftSquarePos = FlatPosCreateHelper(macroInvocation.leftSquarePos); auto rightSquarePos = FlatPosCreateHelper(macroInvocation.rightSquarePos); auto leftParenPos = FlatPosCreateHelper(macroInvocation.leftParenPos); auto rightParenPos = FlatPosCreateHelper(macroInvocation.rightParenPos); auto atPos = FlatPosCreateHelper(macroInvocation.atPos); auto attrsTokens = TokensVectorCreateHelper(macroInvocation.attrs); auto attrs = builder.CreateVector(attrsTokens); auto argsTokens = TokensVectorCreateHelper(macroInvocation.args); auto args = builder.CreateVector(argsTokens); flatbuffers::Offset decl; if (macroInvocation.decl != nullptr) { if (macroInvocation.decl->astKind == ASTKind::MACRO_EXPAND_PARAM) { decl = SerializeMacroExpandParamOfDecl(macroInvocation.decl.get()); } else { decl = SerializeDecl(macroInvocation.decl.get()); } } auto hasParenthesis = macroInvocation.hasParenthesis; auto isCompileTimeVisible = macroInvocation.isCompileTimeVisible; return NodeFormat::CreateMacroInvocation(builder, fullName, identifier, &identifierPos, &leftSquarePos, &rightSquarePos, &leftParenPos, &rightParenPos, &atPos, attrs, args, decl, hasParenthesis, isCompileTimeVisible); } uint8_t* NodeWriter::ExportNode() { // Match the nodePtr to Expr or Decl. match (*nodePtr)( [&, this](const Expr& expr) { // Serialize Expr. auto fbExpr = SerializeExpr(&expr); // flatbuffers::Offset auto nodeBase = SerializeNodeBase(&expr); auto fbNode = NodeFormat::CreateNode(builder, nodeBase, NodeFormat::AnyNode_EXPR, fbExpr.Union()); builder.Finish(fbNode); }, [&, this](const Pattern& pattern) { // Serialize Pattern. auto fbPattern = SerializePattern(&pattern); auto nodeBase = SerializeNodeBase(&pattern); auto fbNode = NodeFormat::CreateNode(builder, nodeBase, NodeFormat::AnyNode_PATTERN, fbPattern.Union()); builder.Finish(fbNode); }, [&, this](const Decl& decl) { // Serialize Decl. auto fbDecl = SerializeDecl(&decl); auto nodeBase = SerializeNodeBase(&decl); auto fbNode = NodeFormat::CreateNode(builder, nodeBase, NodeFormat::AnyNode_DECL, fbDecl.Union()); builder.Finish(fbNode); }, [&, this](const Type& ty) { // Serialize Type. auto fbType = SerializeType(&ty); auto nodeBase = SerializeNodeBase(&ty); auto fbNode = NodeFormat::CreateNode(builder, nodeBase, NodeFormat::AnyNode_TYPE, fbType.Union()); builder.Finish(fbNode); }, [&, this](const File& file) { // Serialize File. auto fbFile = SerializeFile(&file); auto nodeBase = SerializeNodeBase(&file); auto fbNode = NodeFormat::CreateNode(builder, nodeBase, NodeFormat::AnyNode_FILE, fbFile.Union()); builder.Finish(fbNode); }, [&, this](const Annotation& annotation) { // Serialize Annotation. auto anno = SerializeAnnotation(&annotation); auto nodeBase = SerializeNodeBase(&annotation); auto fbNode = NodeFormat::CreateNode(builder, nodeBase, NodeFormat::AnyNode_ANNOTATION, anno.Union()); builder.Finish(fbNode); }, [&]() { // nodePtr not supported Errorln("nodePtr not supported type"); }); // Serialize data into buffer. // Flatbuffer maximum size is 2GB, for 32-bit signed offsets. uint32_t length = static_cast(builder.GetSize()); bufferData.resize(sizeof(uint32_t) + length); // Add length of buffer in front. uint8_t* buf = builder.GetBufferPointer(); uint32_t bufferSize = length + static_cast(sizeof(uint32_t)); uint8_t* pBufferSize = reinterpret_cast(&bufferSize); (void)std::copy(pBufferSize, pBufferSize + sizeof(uint32_t), bufferData.begin()); (void)std::copy( buf, buf + static_cast(length), bufferData.begin() + static_cast(sizeof(uint32_t))); uint8_t* rawPtr = (uint8_t*)malloc(bufferData.size()); if (rawPtr == nullptr) { Errorln("Memory Allocation Failed."); return rawPtr; } (void)std::copy_n(bufferData.begin(), bufferData.size(), rawPtr); return rawPtr; } cangjie_compiler-1.0.7/src/Macro/TestEntryConstructor.cpp000066400000000000000000000444021510705540100235560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/Basic/SourceManager.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Macro/TestEntryConstructor.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Walker.h" using namespace Cangjie; using namespace AST; namespace { const std::string TEST_REGISTER_ATTR = "TEST_REGISTER"; OwnedPtr CreateMethodCall( const std::string& refName, Ptr decl, std::vector>&& args) { auto method = MakeOwnedNode(); method->baseExpr = CreateRefExpr(refName); method->field = decl->identifier; method->target = decl; method->targets.emplace_back(decl); return CreateCallExpr(std::move(method), std::move(args), decl); } OwnedPtr CreateSuiteVarDecl( const std::string& varName, const std::string& suiteName, const std::string& suiteKind) { auto vd = MakeOwnedNode(); vd->identifier = varName; vd->fullPackageName = suiteName; vd->type = CreateRefType(suiteKind); auto fa = MakeOwnedNode(); auto litExpr = MakeOwnedNode(); litExpr->kind = LitConstKind::STRING; litExpr->stringValue = suiteName; fa->expr = std::move(litExpr); std::vector> args; args.emplace_back(std::move(fa)); vd->initializer = CreateCallExpr(CreateRefExpr(suiteKind), std::move(args)); return vd; } bool IsValidTestSuiteRegisterFunction(Ptr decl) { auto fun = DynamicCast(decl); if (fun == nullptr) { return false; } const auto& paramList = fun->funcBody->paramLists; if (paramList.size() > 1) { return false; } if (paramList.size() == 1 && paramList[0]->params.size() > 0) { return false; } for (const auto& anno : fun->annotations) { for (const auto& attr : anno->attrs) { if (attr == TEST_REGISTER_ATTR) { return true; } } } return false; } std::vector> GetTestSuiteRegisterFunctions(Node& pkg) { std::vector> results; std::function)> preVisit = [&results](Ptr node) -> VisitAction { if (node->TestAttr(Attribute::IS_BROKEN)) { return VisitAction::SKIP_CHILDREN; } if (node->astKind == ASTKind::EXTEND_DECL) { auto ed = StaticAs(node); // TEMPORARY should change to 'unittest.TestPackage' if (auto rt = DynamicCast(ed->extendedType.get()); rt && rt->ref.identifier == "TestPackage") { for (const auto& member : ed->members) { if (auto memberFunc = As(member); memberFunc && IsValidTestSuiteRegisterFunction(member.get())) { results.emplace_back(memberFunc); } } } return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; Walker walker(&pkg, preVisit); walker.Walk(); return results; } Ptr GetDeclNode(const MacroExpandDecl& med, ASTKind dclKind) { if (med.invocation.decl == nullptr) { return nullptr; } if (med.invocation.decl->astKind == dclKind) { return med.invocation.decl.get(); } if (med.invocation.decl->astKind == ASTKind::MACRO_EXPAND_DECL) { Ptr nextMed = StaticAs(med.invocation.decl.get()); return GetDeclNode(*nextMed, dclKind); } return nullptr; } Ptr GetMacroDecl(MacroExpandDecl& med, const std::string& target) { if (med.identifier == target || med.invocation.decl == nullptr) { return &med; } if (med.invocation.decl->astKind == ASTKind::MACRO_EXPAND_DECL) { Ptr nextMed = StaticAs(med.invocation.decl.get()); return GetMacroDecl(*nextMed, target); } return &med; } void GetTopLevelFuncDcl(Node& pkg, std::vector>& results) { std::function)> preVisit = [&results](Ptr node) -> VisitAction { if (node->TestAttr(Attribute::IS_BROKEN)) { return VisitAction::SKIP_CHILDREN; } if (node->astKind == ASTKind::MACRO_EXPAND_DECL) { Ptr med = StaticAs(node); auto fd = GetDeclNode(*med, ASTKind::FUNC_DECL); if (fd != nullptr) { results.push_back(StaticAs(fd)); } return VisitAction::SKIP_CHILDREN; } else if (node->astKind == ASTKind::FUNC_DECL) { Ptr fd = StaticAs(node); results.push_back(fd); return VisitAction::SKIP_CHILDREN; } else if (node->IsNominalDecl()) { return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; Walker walker(&pkg, preVisit); walker.Walk(); } bool FindTopLevelFunc(const std::vector>& funcs, Ptr fd) { if (funcs.empty()) { return false; } if (std::find(funcs.begin(), funcs.end(), fd) != funcs.end()) { return true; } return false; } bool FindFuncModifier(const FuncDecl& fd, const std::string& target) { if (fd.modifiers.empty()) { return false; } for (auto& i : fd.modifiers) { if (std::string(TOKENS[static_cast(i.modifier)]) == target) { return true; } } return false; } bool FindClassModifier(const ClassDecl& cd, const std::string& target) { if (cd.modifiers.empty()) { return false; } for (auto& i : cd.modifiers) { if (std::string(TOKENS[static_cast(i.modifier)]) == target) { return true; } } return false; } bool FindMacro(const MacroExpandDecl& med, const std::string& target) { if (med.identifier == target) { return true; } if (med.invocation.decl != nullptr && med.invocation.decl->astKind == ASTKind::MACRO_EXPAND_DECL) { Ptr nextMed = StaticAs(med.invocation.decl.get()); return FindMacro(*nextMed, target); } return false; } bool IsRightClassConstructor(const ClassDecl& cd) { bool hasConstructorWithInput = false; bool hasConstructorWithoutInput = false; const std::string primaryConstructor = cd.identifier; std::function)> preVisit = [&hasConstructorWithInput, &hasConstructorWithoutInput, &primaryConstructor](Ptr node) -> VisitAction { if (node->TestAttr(Attribute::IS_BROKEN)) { return VisitAction::SKIP_CHILDREN; } if (node->astKind == ASTKind::FUNC_DECL) { Ptr fd = StaticAs(node); if (fd->TestAttr(Attribute::CONSTRUCTOR) && !fd->funcBody->paramLists.empty() && fd->funcBody->paramLists[0]->params.empty()) { hasConstructorWithoutInput = true; return VisitAction::STOP_NOW; } if (fd->TestAttr(Attribute::CONSTRUCTOR) && !fd->funcBody->paramLists.empty() && !fd->funcBody->paramLists[0]->params.empty()) { hasConstructorWithInput = true; } return VisitAction::SKIP_CHILDREN; } if (node->astKind == ASTKind::PRIMARY_CTOR_DECL) { Ptr pcd = StaticAs(node); if (pcd->identifier == primaryConstructor && !pcd->funcBody->paramLists.empty() && pcd->funcBody->paramLists[0]->params.empty()) { hasConstructorWithoutInput = true; return VisitAction::STOP_NOW; } if (pcd->identifier == primaryConstructor && !pcd->funcBody->paramLists.empty() && !pcd->funcBody->paramLists[0]->params.empty()) { hasConstructorWithInput = true; } return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; Walker walker(cd.body.get(), preVisit); walker.Walk(); if (hasConstructorWithoutInput) { return true; } if (hasConstructorWithInput) { return false; } return true; } bool IsUnitFunction(const FuncDecl& fd) { if (auto pt = DynamicCast(fd.funcBody->retType.get()); pt && pt->kind == AST::TypeKind::TYPE_UNIT) { return true; } if (fd.funcBody->retType == nullptr) { return true; } return false; } } // namespace void TestEntryConstructor::CheckFunctionWithAtTest( MacroExpandDecl& med, const std::vector>& funcs, const std::string& macroName) { if (!FindMacro(med, macroName) || med.invocation.decl == nullptr) { return; } auto node = GetDeclNode(med, ASTKind::FUNC_DECL); if (node == nullptr) { return; } Ptr fd = StaticAs(node); if (macroName == "Test" && FindMacro(med, macroName) && !FindTopLevelFunc(funcs, fd)) { diag.Diagnose(*GetMacroDecl(med, macroName), DiagKind::macro_using_error, "@" + macroName + " modified function is not top level"); } if (macroName == "TestCase" && FindMacro(med, macroName) && FindTopLevelFunc(funcs, fd)) { diag.Diagnose(*GetMacroDecl(med, macroName), DiagKind::macro_using_error, "@" + macroName + " modified top level function"); } if (FindMacro(med, macroName) && !IsUnitFunction(*fd)) { diag.Diagnose(*GetMacroDecl(med, macroName), DiagKind::macro_using_error, "@" + macroName + " modified function type is not ()->Unit"); } if (FindMacro(med, macroName) && FindFuncModifier(*fd, "foreign")) { diag.Diagnose( *GetMacroDecl(med, macroName), DiagKind::macro_using_error, "@" + macroName + " modified foreign function"); } } void TestEntryConstructor::CheckClassWithMacro(MacroExpandDecl& med) { if ((!FindMacro(med, "Test") && !FindMacro(med, "TestCase")) || med.invocation.decl == nullptr) { return; } auto node = GetDeclNode(med, ASTKind::CLASS_DECL); if (node == nullptr) { return; } Ptr cd = StaticAs(node); if (FindMacro(med, "Test") && FindClassModifier(*cd, "foreign")) { diag.Diagnose(*GetMacroDecl(med, "Test"), DiagKind::macro_using_error, "@Test modified foreign class"); } if (FindMacro(med, "TestCase") && FindClassModifier(*cd, "foreign")) { diag.Diagnose(*GetMacroDecl(med, "TestCase"), DiagKind::macro_using_error, "@TestCase modified foreign class"); } if (FindMacro(med, "Test") && !IsRightClassConstructor(*cd)) { diag.Diagnose(*GetMacroDecl(med, "Test"), DiagKind::macro_using_error, "Class modified by @Test must contain a parameterless construction."); } } void TestEntryConstructor::CheckTestSuiteConstraints(Node& root, const std::vector>& funcs) { std::function)> preVisit = [&funcs, this](Ptr node) -> VisitAction { if (node->TestAttr(Attribute::IS_BROKEN)) { return VisitAction::SKIP_CHILDREN; } if (node->astKind != ASTKind::MACRO_EXPAND_DECL) { return VisitAction::WALK_CHILDREN; } Ptr med = StaticAs(node); CheckFunctionWithAtTest(*med, funcs, "Test"); CheckFunctionWithAtTest(*med, funcs, "TestCase"); CheckClassWithMacro(*med); if (med->invocation.decl != nullptr && med->invocation.decl->astKind == ASTKind::CLASS_DECL) { CheckTestSuiteConstraints(*(med->invocation.decl.get()), funcs); } return VisitAction::WALK_CHILDREN; }; Walker walker(&root, preVisit); walker.Walk(); } void TestEntryConstructor::CheckTestSuite(const std::vector>& packages) { // get top level dcl std::vector> topFuncDcl; for (auto& pkg : packages) { GetTopLevelFuncDcl(*pkg, topFuncDcl); } // check constraints for (auto& pkg : packages) { CheckTestSuiteConstraints(*pkg, topFuncDcl); } } void TestEntryConstructor::ConstructTestImports(AST::Package& pkg, TestModule& module) { unsigned long i = 0; for (auto& it : module.testPackages) { if (it->packageName != pkg.fullPackageName && i < module.testPackages.size() - 1) { auto import = MakeOwned(); import->content.kind = ImportKind::IMPORT_ALL; import->content.prefixPaths.emplace_back(it->packageName); import->content.identifier = "*"; pkg.files[0]->imports.emplace_back(std::move(import)); } i++; } } void TestEntryConstructor::ConstructTestEntry(AST::Package& pkg, TestModule& module) { // Construct test_entry function. auto entryFunc = MakeOwnedNode(); entryFunc->identifier = TEST_ENTRY_NAME; auto packageName = ImportManager::IsTestPackage(pkg.fullPackageName) ? ImportManager::GetMainPartPkgNameForTestPkg(pkg.fullPackageName) : pkg.fullPackageName; entryFunc->fullPackageName = packageName; entryFunc->EnableAttr(Attribute::GLOBAL); // Set curFile in file 0 which should conform rules below that send this func to file 0. entryFunc->curFile = pkg.files[0].get(); // Construct test_entry function body. auto funcBody = MakeOwnedNode(); auto type = MakeOwnedNode(); type->kind = TypeKind::TYPE_INT64; funcBody->retType = std::move(type); funcBody->paramLists.emplace_back(MakeOwnedNode()); funcBody->funcDecl = entryFunc.get(); funcBody->body = MakeOwnedNode(); entryFunc->funcBody = std::move(funcBody); // Construct test_entry function body decls. // 1. declare testsuite creation. auto pk = CreateSuiteVarDecl("testPkg", packageName, "TestPackage"); entryFunc->funcBody->body->body.emplace_back(std::move(pk)); // 2. call add for each testcase decl. for (auto& testPackage : module.testPackages) { for (auto& it : testPackage->testRegisterFunctions) { entryFunc->funcBody->body->body.emplace_back(CreateMethodCall("testPkg", it, {})); } } // 3. call testsuite execution, print result. auto fa = MakeOwnedNode(); fa->expr = CreateRefExpr("testPkg"); std::vector> args; args.emplace_back(std::move(fa)); auto entryMainCall = CreateCallExpr(CreateRefExpr("entryMain"), std::move(args)); auto litExprZero = MakeOwnedNode(); litExprZero->kind = LitConstKind::INTEGER; litExprZero->stringValue = "0"; auto binaryExpr = CreateBinaryExpr(std::move(entryMainCall), std::move(litExprZero), TokenKind::GT); auto thenExprBlock = MakeOwnedNode(); auto retExprOne = MakeOwnedNode(); auto litExprOne = MakeOwnedNode(); litExprOne->kind = LitConstKind::INTEGER; litExprOne->stringValue = "1"; retExprOne->expr = std::move(litExprOne); thenExprBlock->body.push_back(std::move(retExprOne)); // In the current implementation, two methods are used to determine whether elseBlock exists: null and hasElse. // In the future, the two methods should be unified. auto ifExpr = CreateIfExpr(std::move(binaryExpr), std::move(thenExprBlock)); ifExpr->hasElse = false; entryFunc->funcBody->body->body.emplace_back(std::move(ifExpr)); // 4. Create result return, return 0. auto retExpr = MakeOwnedNode(); auto litExpr = MakeOwnedNode(); litExpr->kind = LitConstKind::INTEGER; litExpr->stringValue = "0"; retExpr->expr = std::move(litExpr); entryFunc->funcBody->body->body.emplace_back(std::move(retExpr)); // Set funcdecl, synthesis it. pkg.files[0]->decls.emplace_back(std::move(entryFunc)); } void TestEntryConstructor::ConstructTestSuite(const std::string& moduleName, std::vector>& srcPkgs, const std::vector> importedPkgs, bool compileTestsOnly) { auto currentPkg = srcPkgs.back().get(); auto importedMainPartPkgForTestPkg = compileTestsOnly && ImportManager::IsTestPackage(currentPkg->fullPackageName) ? FindMainPartPkgForTestPkg(currentPkg, importedPkgs) : nullptr; auto testMod = TestModule(moduleName); for (auto& pkg : srcPkgs) { auto testPkg = MakeOwned(pkg->fullPackageName); testPkg->testRegisterFunctions = GetTestSuiteRegisterFunctions(*pkg); testMod.testPackages.emplace_back(std::move(testPkg)); } if (importedMainPartPkgForTestPkg) { auto testPkg = MakeOwned(importedMainPartPkgForTestPkg->fullPackageName); testPkg->testRegisterFunctions = GetTestSuiteRegisterFunctions(*importedMainPartPkgForTestPkg); testMod.testPackages.emplace_back(std::move(testPkg)); } CJC_ASSERT(!srcPkgs.empty()); if (srcPkgs[0]->files.empty()) { return; } ConstructTestImports(*srcPkgs[0], testMod); ConstructTestEntry(*srcPkgs[0], testMod); } Ptr TestEntryConstructor::FindMainPartPkgForTestPkg( const Ptr testPackage, std::vector> importedPkgs) { CJC_ASSERT(ImportManager::IsTestPackage(testPackage->fullPackageName)); auto mainPartPkgName = ImportManager::GetMainPartPkgNameForTestPkg(testPackage->fullPackageName); for (auto& package : importedPkgs) { if (package->srcPackage && package->srcPackage->fullPackageName == mainPartPkgName) { return package->srcPackage; } } return nullptr; } bool TestEntryConstructor::IsTestRegistrationFunction(const Ptr decl) { if (!Is(decl)) { return false; } for (const auto& anno : decl->annotations) { for (const auto& attr : anno->attrs) { if (attr == TEST_REGISTER_ATTR) { return true; } } } return false; } cangjie_compiler-1.0.7/src/Macro/TokenSerialization.cpp000066400000000000000000000144711510705540100231700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /* * @file * * This file implements the Cangjie Token Serialization. */ #include "cangjie/Macro/TokenSerialization.h" #include #include "cangjie/Basic/Print.h" #include "cangjie/Basic/SourceManager.h" #include "cangjie/Macro/MacroCommon.h" using namespace TokenSerialization; namespace { std::string GetStringFromBytes(const uint8_t* pBuffer, uint32_t strLen) { if (pBuffer == nullptr) { return ""; } std::string value; for (uint32_t i = 0; i < strLen; i++) { if (*pBuffer == '\0') { value += "\\0"; pBuffer += 1; continue; } value.push_back(static_cast(*pBuffer)); pBuffer += 1; } return value; } } // namespace /** * Encoding tokens in memory like this. * * -> uint32_t [uint16_t uint32_t char+ uint32_t int32_t int32_t]+ * ~~~~~~~~ ~~~~~~~~ ~~~~~~~~ ~~~~~ ~~~~~~~~ ~~~~~~~ ~~~~~~~ ~ * | | | | | | | | * a b c d e f g h * * a: size of tokens * b: token kind as number * c: size of token value * d: token value as char stream * e: fileID as number * f: line number * g: column number * h: iterate each token in tokens * * @param expr : QuoteExpr desugared. */ std::vector TokenSerialization::GetTokensBytes(const std::vector& tokens) { if (tokens.empty()) { return {}; } std::vector tokensBytes; auto pushBytes = [&tokensBytes]( const uint8_t* v, const size_t l) { (void)tokensBytes.insert(tokensBytes.end(), v, v + l); }; uint32_t numberOfTokens = static_cast(tokens.size()); pushBytes(reinterpret_cast(&numberOfTokens), sizeof(uint32_t)); for (const auto& tk : tokens) { uint16_t kind = static_cast(tk.kind); pushBytes(reinterpret_cast(&kind), sizeof(uint16_t)); // use uint32_t(4 Bytes) to encode the length of string. size_t strLen = tk.Value().size(); pushBytes(reinterpret_cast(&strLen), sizeof(uint32_t)); (void)tokensBytes.insert(tokensBytes.end(), tk.Value().begin(), tk.Value().end()); auto begin = tk.Begin(); pushBytes(reinterpret_cast(&(begin.fileID)), sizeof(uint32_t)); pushBytes(reinterpret_cast(&(begin.line)), sizeof(int32_t)); auto& escapes = GetEscapeTokenKinds(); int32_t column = begin.column; if (std::find(escapes.begin(), escapes.end(), tk.kind) != escapes.end() && column + 1 + static_cast(strLen) == tk.End().column) { ++column; } pushBytes(reinterpret_cast(&column), sizeof(int32_t)); pushBytes(reinterpret_cast(&(tk.isSingleQuote)), sizeof(uint16_t)); if (tk.kind == TokenKind::MULTILINE_RAW_STRING) { pushBytes(reinterpret_cast(&(tk.delimiterNum)), sizeof(uint16_t)); } } return tokensBytes; } std::vector TokenSerialization::GetTokensFromBytes(const uint8_t* pBuffer) { if (pBuffer == nullptr) { return {}; } std::vector tokens{}; uint32_t numberOfTokens = 0; (void)std::copy(pBuffer, pBuffer + sizeof(uint32_t), reinterpret_cast(&numberOfTokens)); pBuffer += sizeof(uint32_t); for (uint32_t i = 0; i < numberOfTokens; ++i) { uint16_t kind = 0; (void)std::copy(pBuffer, pBuffer + sizeof(uint16_t), reinterpret_cast(&kind)); pBuffer += sizeof(uint16_t); uint32_t strLen = 0; (void)std::copy(pBuffer, pBuffer + sizeof(uint32_t), reinterpret_cast(&strLen)); pBuffer += sizeof(uint32_t); std::string value = GetStringFromBytes(pBuffer, strLen); pBuffer += strLen; uint32_t fileID = 0; constexpr auto i4 = sizeof(int32_t); (void)std::copy(pBuffer, pBuffer + i4, reinterpret_cast(&fileID)); pBuffer += i4; int32_t line = 0; int32_t column = 0; (void)std::copy(pBuffer, pBuffer + i4, reinterpret_cast(&line)); pBuffer += i4; (void)std::copy(pBuffer, pBuffer + i4, reinterpret_cast(&column)); pBuffer += i4; Position begin{fileID, line, column}; uint16_t isSingle = 0; (void)std::copy(pBuffer, pBuffer + sizeof(uint16_t), reinterpret_cast(&isSingle)); pBuffer += sizeof(uint16_t); unsigned delimiterNum{1}; if (static_cast(kind) == TokenKind::MULTILINE_RAW_STRING) { (void)std::copy(pBuffer, pBuffer + sizeof(uint16_t), reinterpret_cast(&delimiterNum)); pBuffer += sizeof(uint16_t); } Position end{begin == INVALID_POSITION ? INVALID_POSITION : begin + GetTokenLength(value.size(), static_cast(kind), delimiterNum)}; Token token{static_cast(kind), std::move(value), begin, end}; token.delimiterNum = delimiterNum; token.isSingleQuote = (isSingle == 1) ? true : false; (void)tokens.emplace_back(std::move(token)); } return tokens; } uint8_t* TokenSerialization::GetTokensBytesWithHead(const std::vector& tokens) { if (tokens.empty()) { return nullptr; } std::vector tokensBytes = TokenSerialization::GetTokensBytes(tokens); size_t bufferSize = tokensBytes.size() + sizeof(uint32_t); if (bufferSize == 0 || bufferSize > std::numeric_limits::max()) { Errorln("Memory Allocated Size is Not Valid."); return nullptr; } uint8_t* rawPtr = (uint8_t*)malloc(bufferSize); if (rawPtr == nullptr) { Errorln("Memory Allocation Failed."); return rawPtr; } uint32_t head = static_cast(bufferSize); auto pHead = reinterpret_cast(&head); (void)tokensBytes.insert(tokensBytes.begin(), pHead, pHead + sizeof(uint32_t)); (void)std::copy(tokensBytes.begin(), tokensBytes.end(), rawPtr); return rawPtr; }cangjie_compiler-1.0.7/src/Mangle/000077500000000000000000000000001510705540100170015ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Mangle/ASTMangler.cpp000066400000000000000000000454141510705540100214520ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/Mangle/ASTMangler.h" #include #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/Parse/ASTHasher.h" #include "cangjie/AST/ASTCasting.h" #include "cangjie/Utils/Utils.h" #include "cangjie/Mangle/MangleUtils.h" using namespace Cangjie; using namespace AST; using namespace MangleUtils; using namespace Cangjie::Utils; static std::string MangleType(const AST::Type& typeAnnotation); static std::string MangleRefTypeAnnotation(const AST::Type& type) { CJC_ASSERT(type.astKind == ASTKind::REF_TYPE); auto& refTyAnno = StaticCast(type); std::string mangledName = std::to_string(refTyAnno.ref.identifier.Length()) + refTyAnno.ref.identifier; if (!refTyAnno.typeArguments.empty()) { mangledName += MANGLE_LT_PREFIX; for (auto& arg : refTyAnno.typeArguments) { mangledName += MangleType(*arg); } mangledName += MANGLE_GT_PREFIX; } return mangledName; } static std::string MangleParenTypeAnnotation(const AST::Type& type) { CJC_ASSERT(type.astKind == ASTKind::PAREN_TYPE); auto& parenTyAnno = StaticCast(type); std::string mangledName = MANGLE_PAREN_PREFIX + MANGLE_WILDCARD_PREFIX; mangledName += MangleType(*parenTyAnno.type); return mangledName; } static std::string MangleOptionTypeAnnotation(const AST::Type& type) { CJC_ASSERT(type.astKind == ASTKind::OPTION_TYPE); auto& optionTyAnno = StaticCast(type); std::string mangledName = MANGLE_OPTION_PREFIX + std::to_string(optionTyAnno.questNum) + MANGLE_WILDCARD_PREFIX; mangledName += MangleType(*optionTyAnno.componentType); return mangledName; } static std::string MangleFuncTypeAnnotation(const AST::Type& type) { CJC_ASSERT(type.astKind == ASTKind::FUNC_TYPE); auto& funcTyAnno = StaticCast(type); std::string mangledName = funcTyAnno.isC ? MANGLE_AST_CFUNC_PREFIX : MANGLE_FUNC_PREFIX; mangledName += MangleType(*funcTyAnno.retType); for (auto& it : funcTyAnno.paramTypes) { mangledName += MangleType(*it); } return mangledName; } static std::string MangleConstantTypeAnnotation(const AST::Type& type) { CJC_ASSERT(type.astKind == ASTKind::CONSTANT_TYPE); auto& constType = StaticCast(type); std::string mangledName = MANGLE_DOLLAR_PREFIX; auto ce = constType.constantExpr.get(); CJC_ASSERT(ce->astKind == ASTKind::LIT_CONST_EXPR); mangledName += RawStaticCast(ce)->stringValue; return mangledName; } static std::string MangleVArrayTypeAnnotation(const AST::Type& type) { CJC_ASSERT(type.astKind == ASTKind::VARRAY_TYPE); auto& varrType = StaticCast(type); std::string mangledName = MANGLE_VARRAY_PREFIX + MANGLE_WILDCARD_PREFIX; mangledName += MangleType(*varrType.typeArgument); mangledName += MangleType(*varrType.constantType); return mangledName; } static std::string MangleTupleTypeAnnotation(const AST::Type& type) { CJC_ASSERT(type.astKind == ASTKind::TUPLE_TYPE); auto& tupleTyAnno = StaticCast(type); std::string mangledName = MANGLE_TUPLE_PREFIX + std::to_string(tupleTyAnno.fieldTypes.size()) + MANGLE_WILDCARD_PREFIX; for (auto& it : tupleTyAnno.fieldTypes) { mangledName += MangleType(*it); } return mangledName; } static std::string MangleQualifiedTypeAnnotation(const AST::Type& type) { CJC_ASSERT(type.astKind == ASTKind::QUALIFIED_TYPE); auto& qualifiedTyAnno = StaticCast(type); std::string mangledName = MANGLE_QUALIFIED_PREFIX; mangledName += MangleType(*qualifiedTyAnno.baseType); mangledName += MANGLE_DOT_PREFIX + std::to_string(qualifiedTyAnno.field.Length()) + qualifiedTyAnno.field; if (!qualifiedTyAnno.typeArguments.empty()) { mangledName += MANGLE_LT_PREFIX; for (auto& arg : qualifiedTyAnno.typeArguments) { mangledName += MangleType(*arg); } mangledName += MANGLE_GT_PREFIX; } return mangledName; } static std::optional MangleBuiltinTypeImpl(const std::string& type) { static const std::unordered_map PTYPE_NAMEMAP{ {"Nothing", "n"}, {"Unit", "u"}, {"Rune", "c"}, {"Bool", "b"}, {"Float16", "Dh"}, {"Float32", "f"}, {"Float64", "d"}, {"Int8", "a"}, {"Int16", "s"}, {"Int32", "i"}, {"Int64", "l"}, {"IntNative", "q"}, {"UInt8", "h"}, {"UInt16", "t"}, {"UInt32", "j"}, {"UInt64", "m"}, {"UIntNative", "r"}, {CSTRING_NAME, "CString"}, {CFUNC_NAME, "CFunc"}, {CPOINTER_NAME, "CPointer"}, }; auto it = PTYPE_NAMEMAP.find(type); if (it != PTYPE_NAMEMAP.cend()) { return it->second; } return {}; } std::string ASTMangler::ManglePrimitiveType(const AST::Type& type) { CJC_ASSERT(type.astKind == ASTKind::PRIMITIVE_TYPE); auto& primitiveTy = StaticCast(type); if (auto t = MangleBuiltinTypeImpl(primitiveTy.str)) { return *t; } // Initial type is valid only in the return type of function $test.entry when enabling --test CJC_ASSERT(primitiveTy.ty && primitiveTy.ty->kind == TypeKind::TYPE_INITIAL); return MANGLE_INITIAL_PREFIX; } std::string ASTMangler::MangleBuiltinType(const std::string& type) { auto t = MangleBuiltinTypeImpl(type); if (t) { return *t; } return type; } std::optional ASTMangler::TruncateExtendMangledName(const std::string& name) { // Member of extend, ignored if (name.find(std::to_string(MANGLE_EXIG_PREFIX.length()) + MANGLE_EXIG_PREFIX) != std::string::npos) { return {}; } auto it = name.find(MANGLE_LT_COLON_PREFIX); if (it == std::string::npos) { return {}; } return name.substr(0, it); } static std::string MangleType(const AST::Type& typeAnnotation) { std::function mangleThisType = [](const AST::Type &) { return MANGLE_THIS_PREFIX; }; static const std::unordered_map> HANDLE_TYPE_MAP = { {ASTKind::PRIMITIVE_TYPE, ASTMangler::ManglePrimitiveType}, {ASTKind::REF_TYPE, MangleRefTypeAnnotation}, {ASTKind::PAREN_TYPE, MangleParenTypeAnnotation}, {ASTKind::OPTION_TYPE, MangleOptionTypeAnnotation}, {ASTKind::CONSTANT_TYPE, MangleConstantTypeAnnotation}, {ASTKind::VARRAY_TYPE, MangleVArrayTypeAnnotation}, {ASTKind::FUNC_TYPE, MangleFuncTypeAnnotation}, {ASTKind::TUPLE_TYPE, MangleTupleTypeAnnotation}, {ASTKind::QUALIFIED_TYPE, MangleQualifiedTypeAnnotation}, {ASTKind::THIS_TYPE, mangleThisType} }; if (auto found = HANDLE_TYPE_MAP.find(typeAnnotation.astKind); found != HANDLE_TYPE_MAP.end()) { return found->second(typeAnnotation); } CJC_ASSERT(false && "Unexpected type annotation."); return ""; } static std::string MangleAccessibility(const AST::Decl& decl) { std::string res{}; for (auto& mod : decl.modifiers) { if (mod.modifier == TokenKind::PUBLIC) { res += MANGLE_PUBLIC_PREFIX; continue; } if (mod.modifier == TokenKind::PROTECTED) { res += MANGLE_PROTECTED_PREFIX; continue; } if (mod.modifier == TokenKind::PRIVATE) { res += MANGLE_PRIVATE_PREFIX; continue; } } return res; } static std::string MangleDeclAstKind(ASTKind k) { // ASTKind beyond PROP_DECL is not mangled constexpr uint8_t mangledDeclKindMax{static_cast(ASTKind::PROP_DECL) + 1}; // Character 'A' for N/A, suggesting this decl is not to be mangled static const char kind2StrTable[mangledDeclKindMax] { 'A', 'A', 'A', 'A', 'F', 'M', 'A', 'C', 'I', 'X', 'U', 'S', 'T', 'Y', 'A', 'V', 'P' }; uint8_t k1{static_cast(k)}; if (k1 < mangledDeclKindMax && kind2StrTable[k1] != 'A') { return {kind2StrTable[k1]}; } return ""; } struct Cangjie::ASTManglerImpl { explicit ASTManglerImpl(const std::string& package) : fullPackageName{package} { } // Declare as rvalue method to avoid careless reentrant of this function std::string Mangle(const Decl& decl) { if (auto vp = DynamicCast(&decl)) { MangleVarWithPatternDecl(*vp); return std::move(mangled); } // Don't use base name mangling for extend if (auto extend = DynamicCast(&decl)) { MangleExtendDecl(*extend); return std::move(mangled); } MangleDeclCommon(decl); return std::move(mangled); } private: std::string mangled; std::string fullPackageName; std::unordered_map wildcardMap{}; std::string MangleFullPackageName([[maybe_unused]] const AST::Decl& decl) { auto ret = MangleUtils::MangleName(fullPackageName); return decl.TestAttr(Attribute::GLOBAL, Attribute::PRIVATE) ? ret + MangleUtils::MangleFilePrivate(decl) : ret; } void MangleFuncParameters(const std::vector>& params) { for (const auto& param : params) { if (param->type) { mangled += MangleType(*param->type); } if (param->isMemberParam || param->isNamedParam) { mangled += '!'; } if (param->isMemberParam) { if (param->hasLetOrVar) { mangled += MangleAccessibility(*param); mangled += (param->isVar ? MANGLE_VARIABLE_PREFIX : MANGLE_LET_PREFIX); } } if (param->isMemberParam || param->isNamedParam) { mangled += MangleUtils::MangleName(param->identifier); if (param->assignment) { mangled += '='; } } } } void MangleDeclCommon(const Decl& decl) { mangled += MangleFullPackageName(decl); MangleExtendLiteralIfInExtend(decl); MangleNestedDecl(decl); // Add additional angle brackets to avoid duplication with raw identifier names. bool useInternalIdent = decl.TestAnyAttr(Attribute::CONSTRUCTOR, Attribute::MAIN_ENTRY) && decl.astKind != ASTKind::PRIMARY_CTOR_DECL; if (useInternalIdent) { mangled += MangleUtils::MangleName(MANGLE_LT_PREFIX + decl.identifier + MANGLE_GT_PREFIX); } else { mangled += MangleUtils::MangleName(decl.identifier); } MangleGenericArguments(decl); mangled += MangleDeclAstKind(decl.astKind); MangleOthers(decl); } void MangleOthers(const Decl& decl) { if (decl.astKind == ASTKind::VAR_DECL || IsMemberParam(decl)) { mangled += MANGLE_DIDOLLAR_PREFIX; if (auto& varType = StaticCast(decl).type) { mangled += MangleType(*varType); } } if (auto mainDecl = DynamicCast(&decl)) { MangleFuncParameters(mainDecl->funcBody->paramLists[0]->params); if (auto retType = mainDecl->funcBody->retType.get()) { mangled += MangleType(*retType); } } if (auto funcDecl = DynamicCast(&decl)) { CJC_ASSERT(!funcDecl->funcBody->paramLists.empty()); MangleFuncParameters(funcDecl->funcBody->paramLists[0]->params); // Special mangle rule for static constructor // so that instance and static constructor have different mangle name if (funcDecl->TestAttr(Attribute::CONSTRUCTOR) && funcDecl->TestAttr(Attribute::STATIC)) { constexpr ssize_t initNameAndEnd = 8; // length of "6F" mangled.replace(mangled.size() - initNameAndEnd, initNameAndEnd, MangleUtils::MangleName("")); } mangled += MANGLE_DIDOLLAR_PREFIX; if (auto& retType = funcDecl->funcBody->retType) { mangled += MangleType(*retType); } else if (funcDecl->outerDecl && funcDecl->outerDecl->astKind == ASTKind::PROP_DECL) { auto propDecl = StaticCast(funcDecl->outerDecl); mangled += MangleType(*propDecl->type); } } if (auto ctor = DynamicCast(&decl)) { MangleFuncParameters(ctor->funcBody->paramLists[0]->params); } if (auto macro = DynamicCast(&decl)) { MangleFuncParameters(macro->funcBody->paramLists[0]->params); // Return type mangling is not needed in MacroDecl, because parser checks that the return type of macro // is either absent or a 'Tokens' literal } } void MangleExtendLiteralIfInExtend(const AST::Decl& decl) { // If a decl in an extended type, Mangle `extend` info. // Mangle the package name if a generic type is extended. auto outerDecl = decl.outerDecl; if (outerDecl == nullptr || outerDecl->astKind != ASTKind::EXTEND_DECL) { return; } mangled += MangleUtils::MangleName(MANGLE_EXIG_PREFIX); } void MangleGenericArguments(const Decl& decl) { std::vector args; if (decl.astKind == ASTKind::FUNC_DECL) { auto& fd = static_cast(decl); if (fd.funcBody->generic) { for (auto& arg : fd.funcBody->generic->typeParameters) { args.push_back(&arg->identifier.Val()); } } } else if (decl.generic) { if (decl.astKind == ASTKind::EXTEND_DECL) { for (auto& arg : decl.generic->typeParameters) { args.push_back(&arg->identifier.Val()); } } else { // Since generic types with same number of typeParameters may result in same mangle name, // only mangle number of typeParameter for non-extend type decl (the detail will be combined in // sigHash). mangled += MANGLE_LT_PREFIX; mangled += std::to_string(decl.generic->typeParameters.size()); mangled += MANGLE_GT_PREFIX; return; } } if (args.empty()) { return; } mangled += MANGLE_LT_PREFIX; for (auto it : args) { mangled += MangleUtils::MangleName(*it); } mangled += MANGLE_GT_PREFIX; } void MangleNestedDecl(const Decl& decl) { auto outerDecl = decl.outerDecl; // Concat all outerDecls' name back to forward. while (outerDecl != nullptr) { if (outerDecl->TestAttr(Attribute::GLOBAL, Attribute::PRIVATE)) { mangled += MangleUtils::MangleFilePrivate(*outerDecl); } if (outerDecl->astKind == ASTKind::EXTEND_DECL) { auto ed = RawStaticCast(outerDecl); MangleExtendDecl(*ed); } else { mangled += MangleUtils::MangleName(outerDecl->identifier); MangleGenericArguments(*outerDecl); mangled += MangleDeclAstKind(outerDecl->astKind); } outerDecl = outerDecl->outerDecl; } } void MangleExtendDecl(const ExtendDecl& decl) { mangled += MangleType(*decl.extendedType); mangled += MANGLE_LT_COLON_PREFIX; // Always keep a separator, used in extend2decl relation std::vector inherits(decl.inheritedTypes.size()); // Sort mangled inherited type names for (size_t i{0}; i < inherits.size(); ++i) { inherits[i] = MangleType(*decl.inheritedTypes[i]); } std::stable_sort(inherits.begin(), inherits.end()); for (size_t i{0}; i < decl.inheritedTypes.size(); ++i) { if (i != 0) { mangled += "&"; } mangled += inherits[i]; } if (decl.generic) { MangleGenericConstraints(*decl.generic); } mangled += MangleDeclAstKind(ASTKind::EXTEND_DECL); } // Only generic constraints of ExtendDecl are mangled void MangleGenericConstraints(const Generic& generic) { // Sort generic constraint by constrained type std::vector, std::string>> gcs(generic.genericConstraints.size()); for (size_t i{0}; i < gcs.size(); ++i) { gcs[i] = {generic.genericConstraints[i].get(), MangleType(*generic.genericConstraints[i]->type)}; } std::stable_sort(gcs.begin(), gcs.end(), [](const auto& a, const auto& b) { return a.second < b.second; }); for (const auto& gc : gcs) { mangled += MANGLE_GEXTEND_PREFIX; mangled += gc.second; // Sort upperbounds std::vector uppers(gc.first->upperBounds.size()); for (size_t i{0}; i < uppers.size(); ++i) { uppers[i] = MangleType(*gc.first->upperBounds[i]); } std::stable_sort(uppers.begin(), uppers.end()); for (const auto& upper : uppers) { mangled += ':'; mangled += upper; } } } void MangleVarWithPatternDecl(const AST::VarWithPatternDecl& decl) { std::string ret{}; std::vector> patterns = FlattenVarWithPatternDecl(decl); for (auto pattern : patterns) { if (pattern->astKind == AST::ASTKind::VAR_PATTERN) { ret += MangleUtils::MangleName(StaticCast(pattern)->varDecl->identifier); } } // Means all sub-patterns are wildcard pattern if (ret.empty()) { ret += MANGLE_WILDCARD_PREFIX; ret += MangleUtils::MangleName(decl.curFile->fileName); ret += std::to_string(NextWildcard(decl.curFile->fileName)); } mangled += ret; if (auto ty = decl.type.get()) { mangled += MangleType(*ty); } } int NextWildcard(const std::string& filename) { return wildcardMap[filename]++; } }; ASTMangler::ASTMangler(const std::string& fullPackageName): pimpl{new ASTManglerImpl{fullPackageName}} {} ASTMangler::~ASTMangler() { delete pimpl; } std::string ASTMangler::Mangle(const AST::Decl& decl) const { return pimpl->Mangle(decl); } cangjie_compiler-1.0.7/src/Mangle/BaseMangler.cpp000066400000000000000000001730631510705540100216770ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements name mangling utils. */ #include "cangjie/Mangle/BaseMangler.h" #include "Compression.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Types.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/Match.h" #include "cangjie/Utils/FileUtil.h" #include using namespace Cangjie; using namespace AST; using namespace Meta; namespace { Ptr GetParentDecl(const FuncDecl& funcDecl) { auto fd = RawStaticCast(&funcDecl); auto outerDecl = funcDecl.outerDecl; if (fd->funcBody->parentClassLike != nullptr) { outerDecl = fd->funcBody->parentClassLike; } else if (fd->funcBody->parentStruct != nullptr) { outerDecl = fd->funcBody->parentStruct; } else if (fd->funcBody->parentEnum != nullptr) { outerDecl = fd->funcBody->parentEnum; } return outerDecl; } const std::unordered_map> SPECIAL_FUNC_NAMES = { {CORE_PACKAGE_NAME, { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND {"throwGlobalOutOfMemoryError", "rt$ThrowGlobalOutOfMemoryError"}, {"throwStackOverflowError", "rt$ThrowStackOverflowError"}, #endif {"throwOutOfMemoryError", "rt$ThrowOutOfMemoryError"}, {"throwImplicitException", "rt$ThrowImplicitException"}, {"throwArithmeticException", "rt$ThrowArithmeticException"}, {"throwArithmeticExceptionMsg", "rt$ThrowArithmeticException_msg"}, {"createArithmeticExceptionMsg", "rt$CreateArithmeticException_msg"}, {"createOverflowExceptionMsg", "rt$CreateOverflowException_msg"}, {"throwOverflowException", "rt$ThrowOverflowException"}, {"throwIndexOutOfBoundsException", "rt$ThrowIndexOutOfBoundsException"}, {"throwNegativeArraySizeException", "rt$ThrowNegativeArraySizeException"}, {"callToString", "rt$CallToString"}, {"checkIsError", "rt$CheckIsError"}, {"throwError", "rt$ThrowError"}, {"callPrintStackTrace", "rt$CallPrintStackTrace"}, {"callPrintStackTraceError", "rt$CallPrintStackTraceError"}, }}, }; std::optional GetSpecialFuncName(const FuncDecl& fd) { auto found = SPECIAL_FUNC_NAMES.find(fd.fullPackageName); if (found == SPECIAL_FUNC_NAMES.end()) { return std::nullopt; } auto& nameMap = found->second; if (auto has = nameMap.find(fd.identifier); has != nameMap.end()) { return {has->second}; } return std::nullopt; } inline std::string ToHex(uint64_t val) { std::stringstream ss; const int width = 2; ss << MANGLE_DOLLAR_PREFIX << std::hex << std::uppercase << std::setfill('0') << std::setw(width) << val; return ss.str(); } bool IsMangledToExportForTestBefore(const Decl& decl, const std::vector>& prefix) { if (decl.TestAttr(Attribute::FOR_TEST)) { return true; } for (auto& node : prefix) { if (node->TestAttr(Attribute::FOR_TEST)) { return true; } } return false; } uint32_t Fnv1aHash(const std::string& str) { const uint32_t FNV_OFFSET_BASIS = 2166136261; const uint32_t FNV_PRIME = 16777619; uint32_t hash = FNV_OFFSET_BASIS; for (char c : str) { hash ^= static_cast(c); hash *= FNV_PRIME; } return hash; } std::string ToBase62(uint64_t value) { const std::string base62Chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; std::string result; while (value > 0) { result.push_back(base62Chars[value % base62Chars.size()]); value /= base62Chars.size(); } // The length of hash is 13. size_t length = 13; while (result.size() < length) { result.push_back(base62Chars[0]); } std::reverse(result.begin(), result.end()); return result.size() > length ? result.substr(0, length) : result; } } // namespace namespace Cangjie::MangleUtils { std::string MangleFilePrivate(const AST::Decl& decl) { const size_t cjExtLen = 3; const size_t maxLen = 16; CJC_NULLPTR_CHECK(decl.curFile); const size_t filenameLen = decl.curFile->fileName.size(); if (filenameLen <= maxLen + cjExtLen) { return MangleUtils::MangleName(MANGLE_DOLLAR_PREFIX + decl.curFile->fileName.substr(0, filenameLen - cjExtLen)); } return MangleUtils::MangleName(ToHex(decl.curFile->fileHash)); } std::string DecimalToManglingNumber(const std::string& decimal) { const std::string base62Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; const int base62num = static_cast(base62Chars.size()); for (size_t i = 0; i < decimal.length(); i++) { if (!std::isdigit(decimal[i])) { return ""; } } int num = std::stoi(decimal) - 1; if (num < 0) { return MANGLE_WILDCARD_PREFIX; } std::string base62 = num > 0 ? "" : "0"; while (num > 0) { base62 += base62Chars[static_cast(num % base62num)]; num /= base62num; } return base62 + MANGLE_WILDCARD_PREFIX; } } // namespace MangleUtils std::string BaseMangler::MangleFullPackageName(const std::string& packageName) const { std::string orgName; std::string newPkgName = packageName; size_t pos = packageName.find(TOKENS[static_cast(TokenKind::DOUBLE_COLON)]); if (pos != std::string::npos) { orgName = packageName.substr(0, pos + 1); auto sz = std::string_view{TOKENS[static_cast(TokenKind::DOUBLE_COLON)]}.size(); if (packageName.length() > pos + sz) { newPkgName = packageName.substr(pos + sz, packageName.length() - pos - sz); } else { newPkgName = ""; } } if (MangleUtils::STD_PKG_MANGLE.find(newPkgName) != MangleUtils::STD_PKG_MANGLE.end()) { return MangleUtils::STD_PKG_MANGLE.at(newPkgName); } return MangleUtils::MangleName(orgName + newPkgName); } std::string BaseMangler::MangleFullPackageName(const AST::Decl& decl) const { return MangleFullPackageName(decl.fullPackageName); } std::string BaseMangler::GetPrefixOfType(const AST::Ty& ty) const { switch (ty.kind) { case TypeKind::TYPE_ENUM: { return MANGLE_TYPE_ENUM_PREFIX; } case TypeKind::TYPE_INTERFACE: case TypeKind::TYPE_CLASS: { return MANGLE_TYPE_CLASS_PREFIX; } case TypeKind::TYPE_STRUCT: { return MANGLE_TYPE_STRUCT_PREFIX; } default: { CJC_ASSERT(false && "unexpected type to be mangled"); return ""; } } } std::string BaseMangler::MangleUserDefinedType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare, bool isCollectGTy) const { static const std::set SUPPORT_TYS = { TypeKind::TYPE_ENUM, TypeKind::TYPE_STRUCT, TypeKind::TYPE_CLASS, TypeKind::TYPE_INTERFACE}; if (SUPPORT_TYS.find(ty.kind) == SUPPORT_TYS.end()) { CJC_ASSERT(false && "unexpected type to be mangled"); return ""; } auto decl = Ty::GetDeclOfTy(&ty); CJC_NULLPTR_CHECK(decl); if (MangleUtils::IsAutoBoxedBaseDecl(*decl)) { return decl->mangledName; } // Re-mangle a ty is to make different versions of instantiation have the same type. Therefore, remove the package // name where the generic is instantiated, and ignore the package where extend behavior occurs rather than use its // decl's `mangledName`. auto mangledName = GetPrefixOfType(ty) + MANGLE_NESTED_PREFIX; std::string genericPkgName = ManglePackageNameForGeneric(*decl); mangledName += genericPkgName.empty() ? MangleFullPackageName(*decl) : genericPkgName; if (decl->TestAttr(Attribute::PRIVATE) && decl->linkage == Linkage::INTERNAL) { std::string fileName = decl->curFile->fileName; mangledName += MANGLE_FILE_ID_PREFIX + (IsHashable(fileName) ? HashToBase62(fileName) : (FileNameWithoutExtension(fileName) + MANGLE_DOLLAR_PREFIX)); } mangledName += MangleUtils::MangleName(decl->identifier); if (decl->GetGeneric()) { mangledName += MANGLE_GENERIC_PREFIX; } for (auto arg : ty.typeArgs) { mangledName += MangleType(*arg, genericsTypeStack, declare, isCollectGTy); } mangledName += MANGLE_SUFFIX; return mangledName; } std::string BaseMangler::MangleType(const AST::Ty& ty) const { std::vector genericsTypeStack; return MangleType(ty, genericsTypeStack, true); } std::string BaseMangler::MangleType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare, bool isCollectGTy) const { if (MangleUtils::PRIMITIVE_TYPE_MANGLE.count(ty.kind) != 0) { return MangleUtils::PRIMITIVE_TYPE_MANGLE.at(ty.kind); } if (ty.kind == TypeKind::TYPE_GENERICS) { if (declare || isCollectGTy) { return MangleGenericType(ty, genericsTypeStack, declare); } else { return MangleGenericType(ty); } } else if (ty.kind == TypeKind::TYPE_POINTER) { return MangleCPointerType(ty, genericsTypeStack, declare, isCollectGTy); } else if (ty.kind == TypeKind::TYPE_ENUM) { return MangleEnumType(ty, genericsTypeStack, declare, isCollectGTy); } else if (ty.kind == TypeKind::TYPE_ARRAY) { return MangleRawArrayType(ty, genericsTypeStack, declare, isCollectGTy); } else if (ty.kind == TypeKind::TYPE_VARRAY) { return MangleVArrayType(ty, genericsTypeStack, declare, isCollectGTy); } else if (ty.kind == TypeKind::TYPE_TUPLE) { return MangleTupleType(ty, genericsTypeStack, declare, isCollectGTy); } else if (ty.kind == TypeKind::TYPE_FUNC) { return MangleFuncType(ty, genericsTypeStack, declare, isCollectGTy); } else if (ty.kind == TypeKind::TYPE_STRUCT || ty.kind == TypeKind::TYPE_INTERFACE || ty.kind == TypeKind::TYPE_CLASS) { return MangleUserDefinedType(ty, genericsTypeStack, declare, isCollectGTy); } else if (ty.kind == TypeKind::TYPE_CSTRING) { return MangleCStringType(); } return ""; } std::string BaseMangler::MangleGenericArgumentsHelper(const Decl& decl, std::vector& genericsTypeStack, bool declare, bool isCollectGTy) const { std::string mangled; std::vector> args; if (decl.ty && !decl.ty->IsFunc()) { args = decl.ty->typeArgs; } else if (decl.astKind == ASTKind::FUNC_DECL) { auto& fd = static_cast(decl); if (fd.funcBody->generic) { for (auto& arg : fd.funcBody->generic->typeParameters) { args.push_back(arg->ty); } } } if (args.empty()) { return ""; } mangled += MANGLE_GENERIC_PREFIX; for (auto it : args) { mangled += MangleType(*it, genericsTypeStack, declare, isCollectGTy); } return mangled; } std::string BaseMangler::MangleGenericArguments(const Decl& decl, std::vector& genericsTypeStack, bool declare) const { return MangleGenericArgumentsHelper(decl, genericsTypeStack, declare, false); } std::string BaseMangler::MangleVarDecl(const AST::Decl& decl, const std::vector>& prefix) const { std::string mangleStr = ""; mangleStr += MANGLE_CANGJIE_PREFIX + MANGLE_NESTED_PREFIX; std::vector genericsTypeStack; mangleStr += ManglePrefix(decl, prefix, genericsTypeStack, false); mangleStr += MangleUtils::MangleName(decl.identifier.Val()); if (IsLocalVariable(decl) && manglerCtxTable.find(decl.fullPackageName) != manglerCtxTable.end()) { mangleStr += MANGLE_COUNT_PREFIX; auto manglerCtx = manglerCtxTable.at(decl.fullPackageName).get(); if (!manglerCtx) { return ""; } auto findOuterNodeOfLocalVariable = [&prefix]() { for (auto item = prefix.rbegin(); item != prefix.rend(); ++item) { auto curPrefix = *item; if (curPrefix->astKind == AST::ASTKind::FUNC_DECL || curPrefix->astKind == AST::ASTKind::PRIMARY_CTOR_DECL || curPrefix->astKind == AST::ASTKind::LAMBDA_EXPR || curPrefix->astKind == AST::ASTKind::CLASS_DECL || curPrefix->astKind == AST::ASTKind::INTERFACE_DECL || curPrefix->astKind == AST::ASTKind::STRUCT_DECL || // For VarDecl and VarWithPatternDecl, we only handle it when it is global (Is(curPrefix) && curPrefix->TestAttr(Attribute::GLOBAL))) { return curPrefix; } } return Ptr(); }; Ptr outerNode = findOuterNodeOfLocalVariable(); if (outerNode) { if (outerNode->astKind == AST::ASTKind::CLASS_DECL || outerNode->astKind == AST::ASTKind::INTERFACE_DECL || outerNode->astKind == AST::ASTKind::STRUCT_DECL) { // Member variable of class/interface/struct must be counted as 0. mangleStr += MangleUtils::DecimalToManglingNumber("0"); } else { // Local variable need to be counted from the scope of outerNode. std::optional index = manglerCtx->GetIndexOfVar(outerNode, static_cast(&decl)); CJC_ASSERT(index.has_value() && "index of local variable is not found."); mangleStr += MangleUtils::DecimalToManglingNumber(std::to_string(index.value())); } } } mangleStr += MANGLE_SUFFIX; return mangleStr; } std::optional BaseMangler::GetIndexOfWildcard( const AST::VarWithPatternDecl& vwpDecl, const std::vector>& prefix) const { CJC_ASSERT(ManglerContext::CheckAllElementsWildcard(vwpDecl.irrefutablePattern.get())); std::string pkgName = ManglerContext::ReduceUnitTestPackageName(vwpDecl.fullPackageName); CJC_ASSERT(!pkgName.empty() && "pkgName of varWithPatternDecl is empty."); CJC_ASSERT(manglerCtxTable.find(pkgName) != manglerCtxTable.end() && "can not find pkgName in manglerCtxTable."); auto mangleCtx = manglerCtxTable.at(pkgName); CJC_NULLPTR_CHECK(mangleCtx.get()); std::optional index = {}; if (vwpDecl.TestAttr(Attribute::GLOBAL)) { index = mangleCtx->GetIndexOfGlobalWildcardVar(vwpDecl.curFile, &vwpDecl); } else { for (auto item = prefix.rbegin(); item != prefix.rend(); ++item) { auto curPrefix = *item; if (curPrefix->astKind == AST::ASTKind::FUNC_DECL || curPrefix->astKind == AST::ASTKind::PRIMARY_CTOR_DECL || curPrefix->astKind == AST::ASTKind::LAMBDA_EXPR || curPrefix->astKind == AST::ASTKind::CLASS_DECL || curPrefix->astKind == AST::ASTKind::INTERFACE_DECL || curPrefix->astKind == AST::ASTKind::STRUCT_DECL || // For VarDecl and VarWithPatternDecl, we only handle it when it is global (Is(curPrefix) && curPrefix->TestAttr(Attribute::GLOBAL))) { index = mangleCtx->GetIndexOfLocalWildcardVar(curPrefix, &vwpDecl); break; } } } CJC_ASSERT(index.has_value() && "index of varWithPatternDecl has no value."); return index; } std::string BaseMangler::MangleVarWithPatternDecl( const AST::VarWithPatternDecl& vwpDecl, const ::std::vector>& prefix) const { std::string mangleStr = ""; std::function patternWalkThrough = [this, &patternWalkThrough, &prefix, &mangleStr]( Pattern* pattern) { if (!mangleStr.empty()) { return; } if (auto varPattern = DynamicCast(pattern); varPattern) { mangleStr = MangleVarDecl(*varPattern->varDecl, prefix); } if (auto tuplePattern = DynamicCast(pattern); tuplePattern) { for (auto& item : tuplePattern->patterns) { patternWalkThrough(item.get()); } } else if (auto enumPattern = DynamicCast(pattern); enumPattern) { for (auto& item : enumPattern->patterns) { patternWalkThrough(item.get()); } } }; // In the case `let (x, _) = (1, 2)`, we mangle the VarWithPatternDecl `(x, _)` as Var `x`. if (auto tp = DynamicCast(vwpDecl.irrefutablePattern.get())) { for (auto& pattern : tp->patterns) { patternWalkThrough(pattern.get()); if (!mangleStr.empty()) { return mangleStr; } } } else if (auto ep = DynamicCast(vwpDecl.irrefutablePattern.get())) { for (auto& pattern : ep->patterns) { patternWalkThrough(pattern.get()); if (!mangleStr.empty()) { return mangleStr; } } } std::optional index = GetIndexOfWildcard(vwpDecl, prefix); mangleStr += MANGLE_CANGJIE_PREFIX + MANGLE_NESTED_PREFIX; std::vector genericsTypeStack; mangleStr += ManglePrefix(vwpDecl, prefix, genericsTypeStack, true); if (vwpDecl.TestAttr(Attribute::GLOBAL)) { auto fileName = vwpDecl.curFile->fileName; mangleStr += MANGLE_FILE_ID_PREFIX + (IsHashable(fileName) ? HashToBase62(fileName) : (FileNameWithoutExtension(fileName) + MANGLE_DOLLAR_PREFIX)); } mangleStr += MANGLE_ANONYMOUS_VARIABLE_PREFIX; mangleStr += MangleUtils::DecimalToManglingNumber(std::to_string(index.value())); mangleStr += MANGLE_SUFFIX; return mangleStr; } std::optional BaseMangler::MangleEntryFunction(const FuncDecl& funcDecl) const { // Change user main function to user.main, so that the function entry can be changed to RuntimeMain. // Initialization of light-weight thread scheduling can be performed in runtime.main. if (funcDecl.identifier == MAIN_INVOKE && funcDecl.TestAttr(Attribute::GLOBAL)) { return USER_MAIN_MANGLED_NAME; } if (funcDecl.identifier == TEST_ENTRY_NAME) { return TEST_ENTRY_NAME; } return std::nullopt; } std::string BaseMangler::Mangle(const Decl& decl) const { std::vector> prefix = {}; auto outerDecl = decl.outerDecl; while (outerDecl) { prefix.insert(prefix.begin(), outerDecl); outerDecl = outerDecl->outerDecl; } return Mangle(decl, prefix); } std::string BaseMangler::Mangle(const Decl& decl, const std::vector>& prefix) const { if (decl.TestAttr(Attribute::NO_MANGLE)) { return decl.identifier; } std::vector genericsTypeStack; switch (decl.astKind) { case ASTKind::VAR_DECL: return Compression::CJMangledCompression(MangleVarDecl(decl, prefix)); case ASTKind::FUNC_DECL: return Compression::CJMangledCompression(MangleFunctionDecl(static_cast(decl), prefix, genericsTypeStack, true)); case ASTKind::VAR_WITH_PATTERN_DECL: return Compression::CJMangledCompression(MangleVarWithPatternDecl( static_cast(decl), prefix)); default: return Compression::CJMangledCompression(MangleDecl(decl, prefix, genericsTypeStack, true)); } } std::string BaseMangler::MangleFunctionDecl(const FuncDecl& funcDecl, const std::vector>& prefix, std::vector& genericsTypeStack, bool /* declare */) const { // If the decl has already been mangled. if (!funcDecl.mangledName.empty()) { return funcDecl.mangledName; } // Quick fix: for those const instantiated generic funcs, they are lifted to toplevel and the prefix is empty. // we use the chain of outerDecl as the prefix and generate the mangled name.(currently there is no number count.) if (funcDecl.TestAttr(Attribute::GENERIC_INSTANTIATED) && prefix.empty()) { auto outerDecl = funcDecl.outerDecl; std::vector> newPrefix; while (outerDecl != nullptr) { newPrefix.insert(newPrefix.begin(), outerDecl); outerDecl = outerDecl->outerDecl; } std::string mangled = MangleDecl(funcDecl, newPrefix, genericsTypeStack, true, false); mangled += MANGLE_FUNC_PARAM_TYPE_PREFIX; mangled += MangleFuncParams(funcDecl, genericsTypeStack, false); return mangled; } // For built-in hard-coding functions. if (funcDecl.outerDecl == nullptr) { if (auto specialName = GetSpecialFuncName(funcDecl); specialName) { return specialName.value(); } } // For built-in hard-coding entry functions. std::string mangled = MangleEntryFunction(funcDecl).value_or(""); if (!mangled.empty()) { return mangled; } if (funcDecl.TestAttr(Attribute::HAS_INITIAL)) { std::string param = funcDecl.identifier; CJC_NULLPTR_CHECK(funcDecl.ownerFunc); // Use function mangle(contains params types) to identify params which have same name in overloading function. // eg: func foo3(x!:Int32=1):Int32 // f1 // func foo3(x!:Int64=1):Int32 // f2 // default param functions for f1,f2 need to have different mangle names. auto outerDecl = funcDecl.ownerFunc->outerDecl; std::vector> newPrefix; newPrefix.insert(newPrefix.begin(), funcDecl.ownerFunc); while (outerDecl != nullptr) { newPrefix.insert(newPrefix.begin(), outerDecl); outerDecl = outerDecl->outerDecl; } std::string mangledPrefix = MANGLE_CANGJIE_PREFIX + MANGLE_NESTED_PREFIX; std::string mangledOwnerFunc = MangleDecl(funcDecl, newPrefix, genericsTypeStack, true, true); mangled += MANGLE_CANGJIE_PREFIX + MANGLE_FUNC_PARA_INIT_PREFIX + mangledOwnerFunc.substr(mangledPrefix.size(), mangledOwnerFunc.size() - mangledPrefix.size() - 1); mangled += MANGLE_FUNC_PARAM_TYPE_PREFIX; } else { mangled = MangleDecl(funcDecl, prefix, genericsTypeStack, true, false); mangled += MANGLE_FUNC_PARAM_TYPE_PREFIX; } // Mangle function parameters. mangled += MangleFuncParams(funcDecl, genericsTypeStack, false); return mangled; } std::string BaseMangler::MangleDecl(const Decl& decl, const std::vector>& prefix, std::vector& genericsTypeStack, bool /* declare */, bool withSuffix) const { if (!decl.mangledName.empty() && IsMangledToExportForTestBefore(decl, prefix)) { return decl.mangledName; } std::string newMangled = MANGLE_CANGJIE_PREFIX + MANGLE_NESTED_PREFIX; if (decl.astKind == ASTKind::VAR_WITH_PATTERN_DECL) { newMangled += ManglePackage(decl); if (DynamicCast(&decl) != nullptr) { newMangled += MangleVarWithPatternDecl(*DynamicCast(&decl), prefix); } } else if (decl.astKind == ASTKind::EXTEND_DECL) { newMangled = newMangled + ManglePrefix(decl, prefix, genericsTypeStack, true); newMangled = newMangled + MangleExtendEntity(static_cast(decl), genericsTypeStack, true); newMangled += MangleGenericArguments(decl, genericsTypeStack, true); } else { newMangled = newMangled + ManglePrefix(decl, prefix, genericsTypeStack, true); // Add additional angle brackets to avoid duplication with raw identifier names. bool useInternalIdent = decl.TestAnyAttr(Attribute::CONSTRUCTOR, Attribute::MAIN_ENTRY) && decl.astKind != ASTKind::PRIMARY_CTOR_DECL; if (useInternalIdent) { std::string staticPrefix = decl.TestAttr(Attribute::STATIC) && decl.identifier != STATIC_INIT_FUNC ? "static." : ""; newMangled += MangleUtils::MangleName(MANGLE_LT_PREFIX + staticPrefix + decl.identifier + MANGLE_GT_PREFIX); } else if (decl.TestAnyAttr(AST::Attribute::OPERATOR) && MangleUtils::OPERATOR_TYPE_MANGLE.count(decl.identifier) != 0) { newMangled += MangleUtils::OPERATOR_TYPE_MANGLE.at(decl.identifier); } else if (decl.identifier.Val().find(MANGLE_DOLLAR_PREFIX, 0) == 0 && (decl.identifier.Val().rfind(SPECIAL_NAME_FOR_SET) == (decl.identifier.Val().size() - MANGLE_PROP_LEN) || decl.identifier.Val().rfind(SPECIAL_NAME_FOR_GET) == (decl.identifier.Val().size() - MANGLE_PROP_LEN))) { // MANGLE_CHAR_LEN is the length of "$" and MANGLE_PROP_LEN is "set"/"get". newMangled += MangleUtils::MangleName(decl.identifier.Val().substr(MANGLE_CHAR_LEN, decl.identifier.Val().size() - MANGLE_CHAR_LEN - MANGLE_PROP_LEN)); newMangled += decl.identifier.Val().rfind(SPECIAL_NAME_FOR_SET) == (decl.identifier.Val().size() - MANGLE_PROP_LEN) ? "ps" : "pg"; } else { std::string name = decl.identifier; if (decl.TestAttr(Attribute::HAS_INITIAL)) { // eg: c.2 ==> pos = 1, name = c, idx = "2" size_t pos = decl.identifier.Val().find(MANGLE_DOT_PREFIX); name = decl.identifier.Val().substr(0, pos); } newMangled += MangleUtils::MangleName(name); } newMangled += MangleGenericArguments(decl, genericsTypeStack, true); } if (withSuffix) { newMangled += MANGLE_SUFFIX; } return newMangled; } std::string BaseMangler::MangleExtendEntity(const AST::ExtendDecl& extendDecl, std::vector& genericsTypeStack, bool declare) const { CJC_NULLPTR_CHECK(extendDecl.extendedType); CJC_NULLPTR_CHECK(extendDecl.extendedType->ty); std::string mangled; mangled += MANGLE_EXTEND_PREFIX + MangleType(*extendDecl.extendedType->ty, genericsTypeStack, declare); auto ctx = manglerCtxTable.find(ManglerContext::ReduceUnitTestPackageName(extendDecl.fullPackageName)); CJC_ASSERT(ctx != manglerCtxTable.end()); std::string fileName = extendDecl.curFile.get()->fileName; mangled += MANGLE_FILE_ID_PREFIX + (IsHashable(fileName) ? HashToBase62(fileName) : (FileNameWithoutExtension(fileName) + MANGLE_DOLLAR_PREFIX)); auto index = ctx->second->GetIndexOfExtend(extendDecl.curFile, &extendDecl); CJC_ASSERT(index.has_value()); mangled += MANGLE_COUNT_PREFIX+ MangleUtils::DecimalToManglingNumber(std::to_string(index.value())); return mangled; } std::string BaseMangler::ManglePrefix(const Node& node, const std::vector>& prefix, std::vector& genericsTypeStack, bool declare) const { std::string mangled = ""; Ptr outerMostNode = nullptr; for (auto item = prefix.begin(); item != prefix.end(); ++item) { auto curPrefix = *item; switch (curPrefix->astKind) { case ASTKind::CLASS_DECL: case ASTKind::INTERFACE_DECL: case ASTKind::ENUM_DECL: case ASTKind::STRUCT_DECL: case ASTKind::BUILTIN_DECL: { auto& decl = static_cast(*curPrefix); mangled += MangleUtils::MangleName(decl.identifier); if (auto genericArgs = MangleGenericArgumentsHelper(decl, genericsTypeStack, true); !genericArgs.empty()) { mangled += genericArgs + MANGLE_SUFFIX; } break; } case ASTKind::VAR_DECL: if (curPrefix->TestAttr(Attribute::GLOBAL)) { mangled += MangleUtils::MangleName(static_cast(*curPrefix).identifier); } break; case ASTKind::FUNC_DECL: { auto& decl = static_cast(*curPrefix); auto funcMangled = MangleUtils::MangleName(decl.identifier) + MangleGenericArgumentsHelper(decl, genericsTypeStack, true); funcMangled += MANGLE_FUNC_PARAM_TYPE_PREFIX + MangleFuncParams(decl, genericsTypeStack, false); mangled += funcMangled + MANGLE_SUFFIX; break; } case ASTKind::EXTEND_DECL: { auto& decl = static_cast(*curPrefix); bool isInitFunc = node.astKind == ASTKind::FUNC_DECL && node.TestAttr(Attribute::HAS_INITIAL); bool isExtend = (node.astKind == ASTKind::GENERIC_PARAM_DECL) || (node.TestAttr(Attribute::PRIVATE) && !isInitFunc) || (isInitFunc && static_cast(node).ownerFunc->TestAttr(Attribute::PRIVATE)); if (isExtend) { mangled += MangleExtendEntity(decl, genericsTypeStack, declare); } else { mangled += MANGLE_EXTEND_PREFIX + MangleType(*decl.extendedType->ty, genericsTypeStack, true); } break; } case ASTKind::LAMBDA_EXPR: { auto& lambda = static_cast(*curPrefix); // The outerNode is the outer container of lambda which can be another lambda. // If outerNode is a decl, we use outerNode->fullPackageName to get mangleCtx, // otherwise, we find a prefixDecl from prefix and use prefixDecl->fullPackageName to get mangleCtx. auto outerNode = FindOuterNodeOfLambda(item, prefix); std::string pkgName = ""; if (auto decl = dynamic_cast(outerNode.get()); decl) { pkgName = decl->fullPackageName; } else { for (size_t i = 0; i < prefix.size(); i++) { if (auto prefixDecl = dynamic_cast(prefix[i].get()); prefixDecl) { pkgName = prefixDecl->fullPackageName; break; } } } pkgName = ManglerContext::ReduceUnitTestPackageName(pkgName); CJC_ASSERT(!pkgName.empty() && "pkgName of lambda is empty."); CJC_ASSERT(manglerCtxTable.find(pkgName) != manglerCtxTable.end() && "can not find pkgName in manglerCtxTable."); auto mangleCtx = manglerCtxTable.at(pkgName); CJC_NULLPTR_CHECK(mangleCtx.get()); std::optional index = mangleCtx->GetIndexOfLambda(outerNode, &lambda); mangled += MANGLE_LAMBDA_PREFIX + MangleUtils::DecimalToManglingNumber(std::to_string(index.value())); break; } case ASTKind::VAR_WITH_PATTERN_DECL: { if (!curPrefix->TestAttr(Attribute::GLOBAL)) { break; } std::string mangleStr = ""; bool patternContainsVar = false; std::unordered_set varDeclCollection; auto& vwpDecl = static_cast(*curPrefix); std::function patternWalkThrough; std::function collectAllVarPattern; patternWalkThrough = [&patternWalkThrough, &mangleStr, &patternContainsVar, &varDeclCollection](Pattern* pattern) { if (!mangleStr.empty()) { return; } if (auto varPattern = DynamicCast(pattern); varPattern) { mangleStr = MangleUtils::MangleName(varPattern->varDecl->identifier); if (varDeclCollection.find(varPattern->varDecl.get()) != varDeclCollection.end() ) { patternContainsVar = true; } } if (auto tuplePattern = DynamicCast(pattern); tuplePattern) { for (auto& item : tuplePattern->patterns) { patternWalkThrough(item.get()); } } else if (auto enumPattern = DynamicCast(pattern); enumPattern) { for (auto& item : enumPattern->patterns) { patternWalkThrough(item.get()); } } }; collectAllVarPattern = [&collectAllVarPattern, &varDeclCollection](Pattern* pattern) { if (auto vp = DynamicCast(pattern); vp) { varDeclCollection.emplace(vp->varDecl.get()); return; } if (auto tuplePattern = DynamicCast(pattern); tuplePattern) { for (auto& item : tuplePattern->patterns) { collectAllVarPattern(item.get()); } } else if (auto enumPattern = DynamicCast(pattern); enumPattern) { for (auto& item : enumPattern->patterns) { collectAllVarPattern(item.get()); } } }; if (auto tp = DynamicCast(vwpDecl.irrefutablePattern.get())) { for (auto& pattern : tp->patterns) { collectAllVarPattern(pattern.get()); } } else if (auto ep = DynamicCast(vwpDecl.irrefutablePattern.get())) { for (auto& pattern : ep->patterns) { collectAllVarPattern(pattern.get()); } } if (auto tp = DynamicCast(vwpDecl.irrefutablePattern.get())) { for (auto& pattern : tp->patterns) { patternWalkThrough(pattern.get()); if (!mangleStr.empty()) { break; } } } else if (auto ep = DynamicCast(vwpDecl.irrefutablePattern.get())) { for (auto& pattern : ep->patterns) { patternWalkThrough(pattern.get()); if (!mangleStr.empty()) { break; } } } if (patternContainsVar) { break; } if (!mangleStr.empty()) { mangled += mangleStr; break; } std::optional index = GetIndexOfWildcard(vwpDecl, prefix); mangleStr += MANGLE_ANONYMOUS_VARIABLE_PREFIX; mangleStr += MangleUtils::DecimalToManglingNumber(std::to_string(index.value())); mangled += mangleStr; break; } default: break; } if (outerMostNode == nullptr) { outerMostNode = curPrefix; } } // Mangle outer most node (Top level node) auto& outerMostNodeRef = outerMostNode ? static_cast(*outerMostNode) : node; if (outerMostNodeRef.TestAttr(Attribute::PRIVATE)) { std::string fileName = outerMostNodeRef.curFile.get()->fileName; mangled = MANGLE_FILE_ID_PREFIX + (IsHashable(fileName) ? HashToBase62(fileName) : (FileNameWithoutExtension(fileName) + MANGLE_DOLLAR_PREFIX)) + mangled; } // Mangle package if (node.astKind == ASTKind::LAMBDA_EXPR) { for (auto it = prefix.rbegin(); it != prefix.rend(); ++it) { if (auto decl = dynamic_cast(it->get()); decl) { mangled = ManglePackage(*decl) + mangled; break; } } } else { mangled = ManglePackage(static_cast(node)) + mangled; } return mangled; } std::string BaseMangler::ManglePackage(const Decl& decl) const { std::string genericPackageName = ManglePackageNameForGeneric(decl); return genericPackageName.empty() ? MangleFullPackageName(decl.fullPackageName) : genericPackageName; } std::unique_ptr BaseMangler::PrepareContextForPackage(const Ptr pkg) { std::string pkgName = ManglerContext::ReduceUnitTestPackageName(pkg->fullPackageName); auto manglerCtx = std::make_unique(); manglerCtxTable[pkgName] = manglerCtx.get(); return manglerCtx; } Ptr BaseMangler::FindOuterNodeOfLambda( const std::vector>::const_iterator& iter, const ::std::vector>& prefix) const { auto prevItem = prev(iter); auto cnt = prevItem - prefix.begin() + 1; while (cnt--) { auto prevPrefix = *prevItem; prevItem = prev(prevItem); auto vda = dynamic_cast(prevPrefix.get()); if (prevPrefix->astKind == AST::ASTKind::FUNC_DECL || prevPrefix->astKind == AST::ASTKind::PRIMARY_CTOR_DECL || prevPrefix->astKind == AST::ASTKind::LAMBDA_EXPR || (vda && vda->TestAttr(Attribute::GLOBAL)) || (vda && vda->outerDecl.get() && (vda->outerDecl->astKind == ASTKind::CLASS_DECL || vda->outerDecl->astKind == ASTKind::INTERFACE_DECL || vda->outerDecl->astKind == ASTKind::STRUCT_DECL || vda->outerDecl->astKind == ASTKind::ENUM_DECL))) { return prevPrefix; } } CJC_ASSERT(false && "no outer container of lambda found."); return Ptr(); } std::string BaseMangler::MangleLambda(const LambdaExpr& lambda, const::std::vector>& prefix) const { // The outerNode is the outer container of lambda which can be another lambda. // If outerNode is a decl, we use outerNode->fullPackageName to get mangleCtx, // otherwise, we use outerDecl->fullPackageName to get mangleCtx. auto outerNode = FindOuterNodeOfLambda(prefix.end(), prefix); std::string pkgName = ""; if (auto decl = dynamic_cast(outerNode.get()); decl) { pkgName = decl->fullPackageName; } else { for (size_t i = 0; i < prefix.size(); i++) { if (auto prefixDecl = dynamic_cast(prefix[i].get()); prefixDecl) { pkgName = prefixDecl->fullPackageName; break; } } } pkgName = ManglerContext::ReduceUnitTestPackageName(pkgName); CJC_ASSERT(!pkgName.empty() && "pkgName of lambda is empty."); CJC_ASSERT(manglerCtxTable.find(pkgName) != manglerCtxTable.end() && "can not find pkgName in manglerCtxTable."); auto mangleCtx = manglerCtxTable.at(pkgName); CJC_NULLPTR_CHECK(mangleCtx.get()); std::optional index = mangleCtx->GetIndexOfLambda(outerNode, &lambda); std::string mangleStr = MANGLE_CANGJIE_PREFIX + MANGLE_NESTED_PREFIX; std::vector genericsTypeStack; mangleStr += ManglePrefix(lambda, prefix, genericsTypeStack, true); mangleStr += MANGLE_LAMBDA_PREFIX + MangleUtils::DecimalToManglingNumber(std::to_string(index.value())) + MANGLE_SUFFIX; return Compression::CJMangledCompression(mangleStr); } void BaseMangler::CollectVarOrLambda(ManglerContext& ctx, AST::Package& pkg) const { Walker(&pkg, [&ctx](const Ptr node) { if (auto ed = DynamicCast(node); ed) { ctx.SaveExtend2CurFile(ed->curFile, ed); } if (auto vpd = DynamicCast(node); vpd && vpd->TestAttr(Attribute::GLOBAL)) { ctx.SaveGlobalWildcardVar(vpd->curFile, vpd); } bool isFunc = node->astKind == ASTKind::FUNC_DECL; bool isLambda = node->astKind == ASTKind::LAMBDA_EXPR; bool isGlobal = Is(node) && node->TestAttr(Attribute::GLOBAL); auto vda = DynamicCast(node); bool isCompositeType = vda && vda->outerDecl && (vda->outerDecl->astKind == ASTKind::CLASS_DECL || vda->outerDecl->astKind == ASTKind::INTERFACE_DECL || vda->outerDecl->astKind == ASTKind::STRUCT_DECL || vda->outerDecl->astKind == ASTKind::ENUM_DECL); bool needCollect = isFunc || isLambda || isGlobal || isCompositeType; if (needCollect) { ctx.SaveVar2CurDecl(node); ctx.SaveLambda2CurDecl(node); ctx.SaveLocalWildcardVar2Decl(node); } return VisitAction::WALK_CHILDREN; }).Walk(); } void BaseMangler::MangleExportId(Package& pkg) { std::string pkgName = ManglerContext::ReduceUnitTestPackageName(pkg.fullPackageName); auto manglerCtx = std::make_unique(); manglerCtxTable[pkgName] = manglerCtx.get(); CollectVarOrLambda(*manglerCtxTable.at(pkgName), pkg); exportIdMode = true; Walker(&pkg, [this](auto node) { if (auto decl = DynamicCast(node); decl && Ty::IsTyCorrect(decl->ty) && decl->TestAttr(Attribute::GLOBAL)) { // Only global decl and member decls that may be referenced from other package need exportId! // NOTE: For cjo's compatibility of different version, the exportId must be decl's signature. // ExtendDecl itself does not need exportId, but it's member needs. decl->exportId = MangleExportId(*decl); } return VisitAction::WALK_CHILDREN; }).Walk(); } std::string BaseMangler::MangleExportId(Decl& decl) const { if (decl.TestAttr(Attribute::NO_MANGLE)) { return decl.identifier; } return match(decl)( [&, this](FuncDecl& fd) { fd.exportId = Mangle(fd); if (fd.TestAttr(Attribute::DEFAULT) && fd.outerDecl && fd.outerDecl->astKind == ASTKind::INTERFACE_DECL) { // Interface default implementation may be copied to other package's type decl and referenced. MangleExportIdForGenericParamDecl(fd); } return fd.exportId; }, [this](PropDecl& propDecl) { // PropDecl itself does not need exportId, only getters/setters need. auto mangleExport = [this](auto& it) { it->exportId = Mangle(*it); }; std::for_each(propDecl.getters.begin(), propDecl.getters.end(), mangleExport); std::for_each(propDecl.setters.begin(), propDecl.setters.end(), mangleExport); return Mangle(propDecl); }, [](const PrimaryCtorDecl& /* primaryCtorDecl */) { return std::string(); }, [&decl, this]() { decl.exportId = Mangle(decl); for (auto& it : decl.GetMemberDeclPtrs()) { it->exportId = MangleExportId(*it); } if (decl.astKind == ASTKind::INTERFACE_DECL && decl.TestAttr(Attribute::GENERIC)) { // Interface's generic type may be referenced in other package for upperbound call. // NOTE: only original generic declaration needs mangle. MangleExportIdForGenericParamDecl(decl); } return decl.exportId; }); } void BaseMangler::MangleExportIdForGenericParamDecl(const Decl& decl) const { if (auto generic = decl.GetGeneric()) { for (auto& gpd : generic->typeParameters) { CJC_NULLPTR_CHECK(gpd->outerDecl); std::vector genericsTypeStack; gpd->exportId = gpd->outerDecl->exportId + MANGLE_WILDCARD_PREFIX + gpd->identifier + MangleType(*gpd->ty, genericsTypeStack, false, false); } } } Ptr BaseMangler::GetOuterDecl(const Decl& decl) const { auto outerDecl = decl.outerDecl; if (!Is(&decl)) { return outerDecl; } return GetParentDecl(StaticCast(decl)); } std::string BaseMangler::MangleEnumType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare, bool isCollectGTy) const { CJC_ASSERT(ty.kind == TypeKind::TYPE_ENUM); auto enumTy = StaticCast(ty); return MangleUserDefinedType(enumTy, genericsTypeStack, declare, isCollectGTy); } std::string BaseMangler::MangleRawArrayType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare, bool isCollectGTy) const { CJC_ASSERT(ty.kind == TypeKind::TYPE_ARRAY); auto arrayTy = StaticCast(ty); return MANGLE_TYPE_ARRAY_PREFIX + MangleUtils::DecimalToManglingNumber(std::to_string(arrayTy.dims)) + MangleType(*arrayTy.typeArgs[0], genericsTypeStack, declare, isCollectGTy); } std::string BaseMangler::MangleVArrayType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare, bool isCollectGTy) const { CJC_ASSERT(ty.kind == TypeKind::TYPE_VARRAY); auto varrayTy = StaticCast(ty); // V_ std::string mangled = MANGLE_VARRAY_PREFIX + MangleUtils::DecimalToManglingNumber(std::to_string(varrayTy.size)); for (const auto it : varrayTy.typeArgs) { mangled += MangleType(*it, genericsTypeStack, declare, isCollectGTy); } return mangled; } std::string BaseMangler::MangleTupleType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare, bool isCollectGTy) const { CJC_ASSERT(ty.kind == TypeKind::TYPE_TUPLE); auto tupleTy = StaticCast(ty); std::string mangled = MANGLE_TUPLE_PREFIX + MangleUtils::DecimalToManglingNumber(std::to_string(tupleTy.typeArgs.size())); for (const auto it : tupleTy.typeArgs) { mangled += MangleType(*it, genericsTypeStack, declare, isCollectGTy); } mangled += MANGLE_SUFFIX; return mangled; } std::string BaseMangler::MangleFuncType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare, bool isCollectGTy) const { CJC_ASSERT(ty.kind == TypeKind::TYPE_FUNC); auto funcTy = StaticCast(ty); std::string mangled = funcTy.IsCFunc() ? MANGLE_CFUNC_PREFIX : MANGLE_GENERAL_FUNC_PREFIX; std::string retTy = MangleType(*funcTy.retTy, genericsTypeStack, declare, isCollectGTy); if (retTy.empty()) { retTy = MANGLE_VOID_TY_SUFFIX; } std::string params = ""; for (auto it : funcTy.paramTys) { params += MangleType(*it, genericsTypeStack, declare, isCollectGTy); } mangled += retTy + params + MANGLE_SUFFIX; return mangled; } std::string BaseMangler::MangleGenericType(const AST::Ty& ty) const { CJC_ASSERT(ty.kind == TypeKind::TYPE_GENERICS); auto genericsTy = StaticCast(ty); std::string mangled = MANGLE_GENERIC_TYPE_PREFIX + MangleUtils::MangleName(genericsTy.name); return mangled; } std::string BaseMangler::MangleGenericType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare) const { CJC_ASSERT(ty.kind == TypeKind::TYPE_GENERICS); auto genericsTy = StaticCast(ty); size_t index = 0; if (declare) { index = genericsTypeStack.size(); genericsTypeStack.emplace_back(genericsTy.name); } else { auto result = std::find_if(genericsTypeStack.rbegin(), genericsTypeStack.rend(), [&genericsTy](const std::string& name) { return name == genericsTy.name; }); index = static_cast(std::distance(result, genericsTypeStack.rend())) - MANGLE_CHAR_LEN; CJC_ASSERT(result != genericsTypeStack.rend() && "Using undeclared generic type!"); } auto number = MangleUtils::DecimalToManglingNumber(std::to_string(index)); return MANGLE_GENERIC_TYPE_PREFIX + number; } std::string BaseMangler::MangleFuncParams(const AST::FuncDecl& funcDecl, std::vector& genericsTypeStack, bool declare, bool isCollectGTy) const { if (!funcDecl.funcBody || funcDecl.funcBody->paramLists.empty() || funcDecl.funcBody->paramLists[0]->params.empty()) { return MANGLE_VOID_TY_SUFFIX; } std::string mangled = ""; for (auto& param : funcDecl.funcBody->paramLists[0]->params) { CJC_NULLPTR_CHECK(param->ty); if (Ty::IsInitialTy(param->ty)) { continue; } mangled += MangleType(*param->ty, genericsTypeStack, declare, isCollectGTy); } return mangled; } std::string BaseMangler::MangleCPointerType(const AST::Ty& ty, std::vector& genericsTypeStack, bool declare, bool isCollectGTy) const { CJC_ASSERT(ty.kind == TypeKind::TYPE_POINTER); auto pointerTy = StaticCast(ty); CJC_ASSERT(!pointerTy.typeArgs.empty() && pointerTy.typeArgs[0]); return !pointerTy.typeArgs.empty() && pointerTy.typeArgs[0] ? MANGLE_POINTER_PREFIX + MangleType(*pointerTy.typeArgs[0], genericsTypeStack, declare, isCollectGTy) : ""; } std::string BaseMangler::ManglePackageNameForGeneric(const AST::Decl& decl) const { std::string genericPkg; // Scenario: Imported generic functions can be overloaded in two imported different packages. // such as public pkg1.foo() and public pkg2.foo() with different implementations // Iterate itself to get the package name where defines the generic. auto genericDecl = decl.genericDecl; while (genericDecl != nullptr) { genericPkg = MangleFullPackageName(*genericDecl); genericDecl = genericDecl->genericDecl; } if (!genericPkg.empty()) { return genericPkg; } // Scenario: Two generic functions can be overloaded in two imported different types. // such as public pkg1.A and public pkg2.A both have function foo() // Iterate outerDecl to get a package name where defines the generic. auto outer = &decl; while (outer != nullptr) { if (outer->genericDecl != nullptr) { return MangleFullPackageName(*outer->genericDecl); } outer = GetOuterDecl(*outer); } return genericPkg; } bool BaseMangler::IsLocalVariable(const AST::Decl& decl) const { auto varDecl = dynamic_cast(&decl); if (varDecl == nullptr) { return false; } // Check if varDecl is a static member var of class/interface. auto isStaticMemberVar = [varDecl]() { return varDecl->outerDecl && (varDecl->outerDecl->IsClassLikeDecl() || varDecl->outerDecl->IsStructOrClassDecl()) && varDecl->TestAttr(AST::Attribute::STATIC); }; // Static member variable of Class/interface is not treated as local variable. if (!varDecl->TestAttr(AST::Attribute::GLOBAL) && !isStaticMemberVar()) { return true; } return false; } std::string BaseMangler::HashToBase62(const std::string& input) { uint32_t hashValue = Fnv1aHash(input); return ToBase62(hashValue); } bool BaseMangler::IsHashable(const std::string& str) { auto fileName = FileNameWithoutExtension(str); // Restrict the maximum length of fileName is 12. if (fileName.size() > 12) { return true; } for (char c : fileName) { // [a-zA-Z0-9._-] if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '.') || (c == '_') || (c == '-'))) { return true; } } return false; } std::string BaseMangler::FileNameWithoutExtension(const std::string& fileName) { std::string fileNameWithoutExtension = fileName; // The length of ".cj" is 3. If it ends with ".cj", strip the extension part. size_t length = 3; if (fileName.size() > length && fileName.rfind(".cj") == fileName.size() - length) { fileNameWithoutExtension = fileName.substr(0, fileName.size() - length); } return fileNameWithoutExtension; } void ManglerContext::SaveGlobalWildcardVar(const Ptr file, const Ptr vpd) { if (vpd->irrefutablePattern->astKind == AST::ASTKind::WILDCARD_PATTERN || CheckAllElementsWildcard(vpd->irrefutablePattern.get())) { file2GlobalWildcardVar[file].emplace_back(vpd); } } bool ManglerContext::CheckAllElementsWildcard(const Ptr& root) { if (root->astKind == AST::ASTKind::WILDCARD_PATTERN) { return true; } bool areAllElementsWildcard = true; if (auto tp = DynamicCast(root)) { for (auto& pattern : tp->patterns) { if (!pattern) { continue; } areAllElementsWildcard = areAllElementsWildcard && CheckAllElementsWildcard(pattern.get()); } } else if (auto ep = DynamicCast(root)) { if (ep->patterns.empty()) { areAllElementsWildcard = false; } for (auto& pattern : ep->patterns) { if (!pattern) { continue; } areAllElementsWildcard = areAllElementsWildcard && CheckAllElementsWildcard(pattern.get()); } } else { areAllElementsWildcard = false; } return areAllElementsWildcard; } void ManglerContext::SaveLocalWildcardVar2Decl(const Ptr node) { Ptr key = nullptr; if (auto lambda = DynamicCast(node)) { key = lambda->funcBody.get(); } else if (auto function = DynamicCast(node)) { key = function->funcBody.get(); } else if (auto pcd = DynamicCast(node)) { key = pcd->funcBody.get(); } else if (auto vda = DynamicCast(node); vda && vda->TestAttr(Attribute::GLOBAL)) { key = vda; } if (!key) { return; } Walker(key, [this, &key](const Ptr& node) { if (auto vda = DynamicCast(node); vda && node != key) { node2LocalWildcardVar[key].emplace_back(vda); } else if (Is(node) && node != key) { return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }).Walk(); } void ManglerContext::SaveVar2CurDecl(const Ptr node) { Ptr key = nullptr; if (auto lambda = DynamicCast(node)) { key = lambda->funcBody.get(); } else if (auto function = DynamicCast(node)) { key = function->funcBody.get(); } else if (auto pcd = DynamicCast(node)) { key = pcd->funcBody.get(); } else if (auto vda = DynamicCast(node); vda && vda->TestAttr(Attribute::GLOBAL)) { key = vda; } if (!key) { return; } Walker(key, [this, &key](const Ptr& node) { if (auto vda = DynamicCast(node); vda && node != key) { auto& mapOfName2Var = node2LocalVar[key][vda->identifier.Val()]; mapOfName2Var.emplace_back(vda); } else if (Is(node) && node != key) { return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }).Walk(); } void ManglerContext::SaveLambda2CurDecl(const Ptr node) { std::vector> keys; if (auto lambda = DynamicCast(node)) { keys.emplace_back(lambda->funcBody.get()); } else if (auto function = DynamicCast(node)) { keys.emplace_back(function->funcBody.get()); keys.emplace_back(function->annotationsArray.get()); } else if (auto pcd = DynamicCast(node)) { keys.emplace_back(pcd->funcBody.get()); keys.emplace_back(pcd->annotationsArray.get()); } else if (auto vda = DynamicCast(node); vda && (vda->TestAttr(Attribute::GLOBAL) || (vda->outerDecl && (vda->outerDecl->astKind == ASTKind::CLASS_DECL || vda->outerDecl->astKind == ASTKind::INTERFACE_DECL || vda->outerDecl->astKind == ASTKind::STRUCT_DECL || vda->outerDecl->astKind == ASTKind::ENUM_DECL)))) { keys.emplace_back(vda); keys.emplace_back(vda->annotationsArray.get()); } for (Ptr key : keys) { if (!key) { continue; } Walker(key, [this, &key](const Ptr& node) { if (auto le = DynamicCast(node); le && node != key) { node2Lambda[key].emplace_back(le); } else if (Is(node) && node != key) { return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }).Walk(); } } void ManglerContext::SaveExtend2CurFile(const Ptr file, const Ptr node) { file2ExtendDecl[file][node->extendedType->ty->String()].emplace_back(node); } std::optional ManglerContext::GetIndexOfGlobalWildcardVar( const Ptr file, const Ptr target) const { if (!file || file->fileHash == 0) { return {}; } auto found = file2GlobalWildcardVar.find(file); if (found != file2GlobalWildcardVar.end()) { for (size_t i = 0; i < found->second.size(); ++i) { if (found->second[i] == target) { return i; } } } return {}; } std::optional ManglerContext::GetIndexOfExtend( const Ptr file, const Ptr target) const { if (!file) { return {}; } auto fileMap = file2ExtendDecl.find(file); CJC_ASSERT(fileMap != file2ExtendDecl.end()); auto elementsVector = fileMap->second.find(target->extendedType->ty->String()); CJC_ASSERT(elementsVector != fileMap->second.end()); auto found = std::find(elementsVector->second.begin(), elementsVector->second.end(), target); if (found != elementsVector->second.end()) { return found - elementsVector->second.begin(); } return {}; } std::optional ManglerContext::GetIndexOfLocalWildcardVar( const Ptr node, const Ptr target) const { auto foundFunc = node2LocalWildcardVar.end(); if (auto lambda = DynamicCast(node)) { foundFunc = node2LocalWildcardVar.find(lambda->funcBody.get()); } else if (auto function = DynamicCast(node)) { foundFunc = node2LocalWildcardVar.find(function->funcBody.get()); } else if (auto pcd = DynamicCast(node)) { foundFunc = node2LocalWildcardVar.find(pcd->funcBody.get()); } else if (auto vda = DynamicCast(node); vda && vda->TestAttr(Attribute::GLOBAL)) { foundFunc = node2LocalWildcardVar.find(vda); } if (foundFunc != node2LocalWildcardVar.end()) { auto targetIt = std::find(foundFunc->second.begin(), foundFunc->second.end(), target); if (targetIt != foundFunc->second.end()) { return targetIt - foundFunc->second.begin(); } return {}; } return {}; } std::optional ManglerContext::GetIndexOfVar( const Ptr node, const Ptr target) const { auto foundFunc = node2LocalVar.end(); if (auto lambda = DynamicCast(node)) { foundFunc = node2LocalVar.find(lambda->funcBody.get()); } else if (auto function = DynamicCast(node)) { foundFunc = node2LocalVar.find(function->funcBody.get()); } else if (auto pcd = DynamicCast(node)) { foundFunc = node2LocalVar.find(pcd->funcBody.get()); } else if (auto vda = DynamicCast(node); vda && vda->TestAttr(Attribute::GLOBAL)) { foundFunc = node2LocalVar.find(vda); } if (foundFunc != node2LocalVar.end()) { auto foundId = foundFunc->second.find(target->identifier); if (foundId == foundFunc->second.end()) { return {}; } auto targetIt = std::find(foundId->second.begin(), foundId->second.end(), target); if (targetIt != foundId->second.end()) { return targetIt - foundId->second.begin(); } return {}; } return {}; } std::optional ManglerContext::GetIndexOfLambda( const Ptr node, const Ptr target) const { auto foundFunc = node2Lambda.end(); auto foundArrayLit = node2Lambda.end(); if (auto lambda = DynamicCast(node)) { foundFunc = node2Lambda.find(lambda->funcBody.get()); } else if (auto function = DynamicCast(node)) { foundFunc = node2Lambda.find(function->funcBody.get()); foundArrayLit = node2Lambda.find(function->annotationsArray.get()); } else if (auto pcd = DynamicCast(node)) { foundFunc = node2Lambda.find(pcd->funcBody.get()); foundArrayLit = node2Lambda.find(pcd->annotationsArray.get()); } else if (auto vda = DynamicCast(node); vda && (vda->TestAttr(Attribute::GLOBAL) || (vda->outerDecl && (vda->outerDecl->astKind == ASTKind::CLASS_DECL || vda->outerDecl->astKind == ASTKind::INTERFACE_DECL || vda->outerDecl->astKind == ASTKind::STRUCT_DECL || vda->outerDecl->astKind == ASTKind::ENUM_DECL)))) { foundFunc = node2Lambda.find(vda); foundArrayLit = node2Lambda.find(vda->annotationsArray.get()); } auto getIndex = [this, &target](std::map, std::vector>>::const_iterator it) -> std::optional { if (it != node2Lambda.end()) { auto targetIt = std::find(it->second.begin(), it->second.end(), target); if (targetIt != it->second.end()) { return targetIt - it->second.begin(); } } return {}; }; std::optional funcIdx = getIndex(foundFunc); if (funcIdx.has_value()) { return funcIdx; } std::optional arrayIdx = getIndex(foundArrayLit); CJC_ASSERT(arrayIdx.has_value() && "index of lambda has no value."); size_t offset = foundFunc != node2Lambda.end() ? foundFunc->second.size() : 0; return arrayIdx.value() + offset; } cangjie_compiler-1.0.7/src/Mangle/CHIRMangler.cpp000066400000000000000000000024471510705540100215470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the CHIRMangle. */ #include "cangjie/Mangle/CHIRMangler.h" #include "cangjie/AST/Node.h" using namespace Cangjie; using namespace CHIR; using namespace AST; using namespace MangleUtils; std::optional CHIRMangler::MangleEntryFunction(const FuncDecl& funcDecl) const { // Change user main function to user.main, so that the function entry can be changed to RuntimeMain. // Initialization of light-weight thread scheduling can be performed in runtime.main. if (enableCompileTest && funcDecl.identifier == TEST_ENTRY_NAME) { return USER_MAIN_MANGLED_NAME; } if (!enableCompileTest && (funcDecl.identifier == MAIN_INVOKE && funcDecl.IsStaticOrGlobal())) { return USER_MAIN_MANGLED_NAME; } return std::nullopt; } std::string CHIRMangler::MangleCFuncSignature(const AST::FuncTy& cFuncTy) const { std::string mangled = MangleType(*cFuncTy.retTy); for (auto paramTy : cFuncTy.paramTys) { mangled += MangleType(*paramTy); } return mangled; } cangjie_compiler-1.0.7/src/Mangle/CHIRManglingUtils.cpp000066400000000000000000000170071510705540100227350ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements some util functions for CHIR mangling. */ #include "cangjie/Mangle/CHIRManglingUtils.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/CustomTypeDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/Utils/CastingTemplate.h" #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Mangle/CHIRTypeManglingUtils.h" #include using namespace Cangjie::CHIR; using namespace Cangjie::MangleUtils; namespace { std::string ReplaceManglePrefixWith(const std::string& mangledName, const std::string newPrefix) { if (mangledName.find(Cangjie::MANGLE_CANGJIE_PREFIX) == 0) { // Replace _C (2 characters) with user specified prefix. return newPrefix + mangledName.substr(Cangjie::MANGLE_PREFIX_LEN); } else { CJC_ASSERT(false && "Mangle name has no _C prefix to be replaced."); return mangledName; } } } // namespace namespace Cangjie::CHIRMangling { std::string GenerateVirtualFuncMangleName( const FuncBase* rawFunc, const CustomTypeDef& customTypeDef, const ClassType* parentTy, bool isVirtual) { std::stringstream ss; // "_CV" represents function wrapper for virtual functions. "_CM" represents function wrapper for mutable functions. std::string prefix = isVirtual ? MANGLE_VIRTUAL_PREFIX : MANGLE_MUTABLE_PREFIX; std::string originalName = ReplaceManglePrefixWith(rawFunc->GetIdentifierWithoutPrefix(), prefix); ss << originalName; std::vector customTyGenericTypeStack; for (auto genericType : customTypeDef.GetGenericTypeParams()) { customTyGenericTypeStack.emplace_back(genericType->GetSrcCodeIdentifier()); } if (customTypeDef.IsExtend()) { ss << MANGLE_EXTEND_PREFIX << MangleType(*customTypeDef.GetType(), customTyGenericTypeStack); std::vector implementTypes; for (auto& ty : customTypeDef.GetImplementedInterfaceTys()) { implementTypes.emplace_back(MangleType(*ty, customTyGenericTypeStack)); } // Sort implement types, so the order of how implement types show up don't affect compatibility. std::sort(implementTypes.begin(), implementTypes.end()); for (auto& implementType : implementTypes) { ss << implementType; } ss << customTypeDef.GetPackageName().size() << customTypeDef.GetPackageName(); } else { ss << MANGLE_DOLLAR_PREFIX << ReplaceManglePrefixWith(customTypeDef.GetIdentifierWithoutPrefix(), ""); } ss << MANGLE_DOLLAR_PREFIX << MangleType(*parentTy, customTyGenericTypeStack); return ss.str(); } std::string GenerateInstantiateFuncMangleName(const std::string& baseName, const std::vector& instTysInFunc) { std::stringstream ss; // "_CI" represents function wrapper for Instantiate functions. ss << ReplaceManglePrefixWith(baseName, MANGLE_INSTANTIATE_PREFIX) << MANGLE_DOLLAR_PREFIX; if (instTysInFunc.empty()) { ss << MANGLE_VOID_TY_SUFFIX; } else { for (auto& paramTy : instTysInFunc) { ss << MangleType(*paramTy); } } return ss.str(); } std::string GenerateLambdaFuncMangleName(const Func& baseFunc, size_t counter) { std::stringstream ss; // "_CL" represents for compiler generated anonymous functions (lambdas). ss << ReplaceManglePrefixWith(baseFunc.GetIdentifierWithoutPrefix(), MANGLE_LAMBDA_PREFIX) << MANGLE_DOLLAR_PREFIX << counter << MANGLE_WILDCARD_PREFIX; return ss.str(); } std::string OverflowStrategyToString(OverflowStrategy ovf) { switch (ovf) { case OverflowStrategy::WRAPPING: return "&"; case OverflowStrategy::THROWING: return "~"; default: return "%"; } } std::string GenerateOverflowOperatorFuncMangleName(const std::string& name, OverflowStrategy ovf, bool isBinary, const BuiltinType& type) { std::stringstream ss; // "_CO" represents for compiler generated operator split functions. ss << MANGLE_OPERATOR_PREFIX << OverflowStrategyToString(ovf); if (MangleUtils::OPERATOR_TYPE_MANGLE.count(name) == 0) { CJC_ASSERT(false && "Unsupported name for overflow operator mangling."); } ss << MangleUtils::OPERATOR_TYPE_MANGLE.at(name) << MANGLE_FUNC_PARAM_TYPE_PREFIX << MangleType(type); if (isBinary) { ss << MangleType(type); } return ss.str(); } std::string GenerateAnnotationFuncMangleName(const std::string& name) { // replace "_CN" with "_CAF" return MANGLE_CANGJIE_PREFIX + "AF" + name.substr(Cangjie::MANGLE_PREFIX_LEN + 1); } namespace ClosureConversion { std::string GenerateGenericBaseClassMangleName(size_t paramNum) { std::stringstream ss; // `$C` is a special prefix for closure conversion class declarations. `g` stands for generic. // `$Cg` is followed by a number with an underscore suffix. ss << MANGLE_CLOSURE_GENERIC_PREFIX << paramNum << MANGLE_WILDCARD_PREFIX; return ss.str(); } std::string GenerateInstantiatedBaseClassMangleName(const FuncType& funcType) { std::stringstream ss; // `$C` is a special prefix for closure conversion class declarations. `i` stands for instantiate. // `$Ci` is followed by a type mangle name. ss << MANGLE_CLOSURE_INSTANTIATE_PREFIX << MangleType(funcType); return ss.str(); } std::string GenerateGlobalImplClassMangleName(const FuncBase& func) { std::stringstream ss; ss << ReplaceManglePrefixWith(func.GetIdentifierWithoutPrefix(), MANGLE_CLOSURE_FUNC_PREFIX); return ss.str(); } std::string GenerateLambdaImplClassMangleName(const Lambda& func, size_t count) { std::stringstream ss; ss << ReplaceManglePrefixWith(func.GetIdentifier(), MANGLE_CLOSURE_LAMBDA_PREFIX) << MANGLE_DOLLAR_PREFIX << count; return ss.str(); } std::string GenerateWrapperClassMangleName(const ClassDef &def) { std::stringstream ss; // `$C` is a special prefix for closure conversion class declarations. `w` stands for wrapper. // `$Cw` is followed by a class (declaration) mangle name. ss << MANGLE_CLOSURE_WRAPPER_PREFIX << def.GetIdentifierWithoutPrefix(); return ss.str(); } std::string GenerateGenericAbstractFuncMangleName(const ClassDef &def) { return MANGLE_FUNC_PREFIX + def.GetIdentifier() + MANGLE_ABSTRACT_GENERIC_PREFIX; } std::string GenerateInstantiatedAbstractFuncMangleName(const ClassDef &def) { return MANGLE_FUNC_PREFIX + def.GetIdentifier() + MANGLE_ABSTRACT_INSTANTIATED_PREFIX; } std::string GenerateGenericOverrideFuncMangleName(const FuncBase &func) { return ReplaceManglePrefixWith(func.GetIdentifierWithoutPrefix(), MANGLE_FUNC_PREFIX) + MANGLE_GENERIC_PREFIX; } std::string GenerateInstOverrideFuncMangleName(const FuncBase &func) { return ReplaceManglePrefixWith(func.GetIdentifierWithoutPrefix(), MANGLE_FUNC_PREFIX) + MANGLE_ABSTRACT_INST_PREFIX; } std::string GenerateWrapperClassGenericOverrideFuncMangleName(const ClassDef &def) { return MANGLE_FUNC_PREFIX + def.GetIdentifierWithoutPrefix() + MANGLE_GENERIC_PREFIX; } std::string GenerateWrapperClassInstOverrideFuncMangleName(const ClassDef &def) { return MANGLE_FUNC_PREFIX + def.GetIdentifierWithoutPrefix() + MANGLE_ABSTRACT_INST_PREFIX; } } // namespace ClosureConversion } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/Mangle/CHIRTypeManglingUtils.cpp000066400000000000000000000335311510705540100235770ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements some utils for CHIR type mangling. */ #include "cangjie/Mangle/CHIRTypeManglingUtils.h" #include "cangjie/CHIR/CHIRCasting.h" #include "cangjie/CHIR/Type/ClassDef.h" #include "cangjie/CHIR/Type/CustomTypeDef.h" #include "cangjie/CHIR/Type/EnumDef.h" #include "cangjie/CHIR/Type/StructDef.h" #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Utils/CastingTemplate.h" #include #include using namespace Cangjie::CHIR; using namespace Cangjie::MangleUtils; using ChirTypeKind = Cangjie::CHIR::Type::TypeKind; namespace { // Type mangling look-up table const std::unordered_map TYPE_MANGLING_LUT = { {ChirTypeKind::TYPE_INT8, "a"}, {ChirTypeKind::TYPE_INT16, "s"}, {ChirTypeKind::TYPE_INT32, "i"}, {ChirTypeKind::TYPE_INT64, "l"}, {ChirTypeKind::TYPE_INT_NATIVE, "q"}, {ChirTypeKind::TYPE_UINT8, "h"}, {ChirTypeKind::TYPE_UINT16, "t"}, {ChirTypeKind::TYPE_UINT32, "j"}, {ChirTypeKind::TYPE_UINT64, "m"}, {ChirTypeKind::TYPE_UINT_NATIVE, "r"}, {ChirTypeKind::TYPE_FLOAT16, "Dh"}, {ChirTypeKind::TYPE_FLOAT32, "f"}, {ChirTypeKind::TYPE_FLOAT64, "d"}, {ChirTypeKind::TYPE_RUNE, "c"}, {ChirTypeKind::TYPE_BOOLEAN, "b"}, {ChirTypeKind::TYPE_UNIT, "u"}, {ChirTypeKind::TYPE_NOTHING, "n"}, {ChirTypeKind::TYPE_CSTRING, "k"}, {ChirTypeKind::TYPE_VOID, "u"}, }; } // namespace namespace Cangjie::CHIR { std::string MangleType(const Cangjie::CHIR::Type& t, const std::vector& genericsTypeStack, bool useGenericName); // Type mangling for CHIR::TupleType std::string MangleType(const CHIR::TupleType& t, const std::vector& genericsTypeStack, bool useGenericName) { auto items = t.GetElementTypes(); std::stringstream ss; ss << MANGLE_TUPLE_PREFIX << MangleUtils::DecimalToManglingNumber(std::to_string(items.size())); for (auto i : items) { ss << MangleType(*i, genericsTypeStack, useGenericName); } ss << MANGLE_SUFFIX; return ss.str(); } // Type mangling for CHIR::StructType std::string MangleType(const CHIR::StructType& t, const std::vector& genericsTypeStack, bool useGenericName) { auto s = t.GetStructDef(); CJC_NULLPTR_CHECK(s); std::stringstream ss; std::string mangled = s->GetIdentifierWithoutPrefix(); ss << MANGLE_TYPE_STRUCT_PREFIX << Cangjie::CHIR::StripCangjiePrefix(mangled); for (auto argTy : t.GetGenericArgs()) { ss << MangleType(*argTy, genericsTypeStack, useGenericName); } return ss.str(); } // Type mangling for CHIR::EnumType std::string MangleType(const CHIR::EnumType& t, const std::vector& genericsTypeStack, bool useGenericName) { auto enumDef = t.GetEnumDef(); CJC_NULLPTR_CHECK(enumDef); std::stringstream ss; std::string mangled = enumDef->GetIdentifierWithoutPrefix(); ss << MANGLE_NESTED_PREFIX << StripCangjiePrefix(mangled); for (auto argTy : t.GetGenericArgs()) { ss << MangleType(*argTy, genericsTypeStack, useGenericName); } return ss.str(); } // Type mangling for CHIR::FuncType std::string MangleType(const CHIR::FuncType& t, const std::vector& genericsTypeStack, bool useGenericName) { std::stringstream ss; if (t.IsCFunc()) { // FC + return + param + E ss << MANGLE_CFUNC_PREFIX; } else { // F0 + return + param + E ss << MANGLE_GENERAL_FUNC_PREFIX; } std::string retTy = MangleType(*t.GetReturnType(), genericsTypeStack, useGenericName); if (retTy.empty()) { retTy = MANGLE_VOID_TY_SUFFIX; } ss << retTy; if (t.GetParamTypes().empty()) { return ss.str() + MANGLE_VOID_TY_SUFFIX + MANGLE_SUFFIX; } for (auto p : t.GetParamTypes()) { std::string mangled = MangleType(*p, genericsTypeStack, useGenericName); ss << mangled; } ss << MANGLE_SUFFIX; return ss.str(); } // Type mangling for CHIR::ClassTpe std::string MangleType(const CHIR::ClassType& t, const std::vector& genericsTypeStack, bool useGenericName) { auto c = t.GetClassDef(); CJC_NULLPTR_CHECK(c); std::stringstream ss; std::string mangled = c->GetIdentifierWithoutPrefix(); ss << "C" << StripCangjiePrefix(mangled); for (auto argTy : t.GetGenericArgs()) { ss << MangleType(*argTy, genericsTypeStack, useGenericName); } return ss.str(); } // Type mangling for CHIR::RawArray std::string MangleType(const CHIR::RawArrayType& t, const std::vector& genericsTypeStack, bool useGenericName) { std::stringstream ss; ss << MANGLE_TYPE_ARRAY_PREFIX; ss << MangleUtils::DecimalToManglingNumber(std::to_string(t.GetDims())); ss << MangleType(*t.GetElementType(), genericsTypeStack, useGenericName); return ss.str(); } // Type mangling for CHIR::RefType std::string MangleType(const CHIR::RefType& t, const std::vector& genericsTypeStack, bool useGenericName) { return MangleType(*t.GetBaseType(), genericsTypeStack, useGenericName); } std::string MangleType(const CHIR::VArrayType& t, const std::vector& genericsTypeStack, bool useGenericName) { std::stringstream ss; ss << MANGLE_VARRAY_PREFIX; ss << MangleUtils::DecimalToManglingNumber(std::to_string(t.GetSize())); ss << MangleType(*t.GetElementType(), genericsTypeStack, useGenericName); return ss.str(); } std::string MangleType(const CHIR::CPointerType& t, const std::vector& genericsTypeStack, bool useGenericName) { std::stringstream ss; ss << MANGLE_POINTER_PREFIX; ss << MangleType(*t.GetElementType(), genericsTypeStack, useGenericName); return ss.str(); } std::string MangleType(const CHIR::GenericType& t, const std::vector& genericsTypeStack, bool useGenericName) { std::stringstream ss; if (useGenericName) { ss << MANGLE_GENERIC_PREFIX << t.GetSrcCodeIdentifier() << std::endl; return ss.str(); } auto result = std::find_if(genericsTypeStack.rbegin(), genericsTypeStack.rend(), [&t](const std::string& name) { return name == t.GetSrcCodeIdentifier(); }); CJC_ASSERT(result != genericsTypeStack.rend() && "Using undeclared generic type!"); size_t index = static_cast(std::distance(result, genericsTypeStack.rend())) - 1; ss << MANGLE_GENERIC_PREFIX << MangleUtils::DecimalToManglingNumber(std::to_string(index)); return ss.str(); } std::string ManglePrimitive(const ChirTypeKind& kind) { if (TYPE_MANGLING_LUT.count(kind) != 0) { return TYPE_MANGLING_LUT.at(kind); } return ""; } std::string MangleArraySliceType(const CHIR::Type& t, const std::vector& genericsTypeStack, bool useGenericName) { std::stringstream ss; ss << MANGLE_ARRAY_SLICE_PREFIX; ss << MangleType(*t.GetTypeArgs()[0], genericsTypeStack, useGenericName); ss << MANGLE_SUFFIX; return ss.str(); } // Type mangling entry and dispatcher std::string MangleType(const CHIR::Type& t) { std::vector genericsTypeStack; return MangleType(t, genericsTypeStack); } // Type mangling entry and dispatcher std::string MangleType(const CHIR::Type& t, const std::vector& genericsTypeStack, bool useGenericName) { auto k = t.GetTypeKind(); if (auto prim = ManglePrimitive(k); !prim.empty()) { return prim; } CJC_ASSERT(k != ChirTypeKind::TYPE_INVALID); switch (k) { case ChirTypeKind::TYPE_TUPLE: return MangleType(StaticCast(t), genericsTypeStack, useGenericName); case ChirTypeKind::TYPE_STRUCT: return MangleType(StaticCast(t), genericsTypeStack, useGenericName); case ChirTypeKind::TYPE_ENUM: return MangleType(StaticCast(t), genericsTypeStack, useGenericName); case ChirTypeKind::TYPE_FUNC: return MangleType(StaticCast(t), genericsTypeStack, useGenericName); case ChirTypeKind::TYPE_CLASS: { return MangleType(StaticCast(t), genericsTypeStack, useGenericName); } case ChirTypeKind::TYPE_RAWARRAY: return MangleType(StaticCast(t), genericsTypeStack, useGenericName); case ChirTypeKind::TYPE_REFTYPE: return MangleType(StaticCast(t), genericsTypeStack, useGenericName); case ChirTypeKind::TYPE_VARRAY: return MangleType(StaticCast(t), genericsTypeStack, useGenericName); case ChirTypeKind::TYPE_CPOINTER: return MangleType(StaticCast(t), genericsTypeStack, useGenericName); case ChirTypeKind::TYPE_GENERIC: return MangleType(StaticCast(t), genericsTypeStack, useGenericName); default: CJC_ASSERT(false && "Unexpected type to be mangled."); return ""; } } std::string MangleType(const CHIR::Type& t, const std::vector& genericsTypeStack) { return MangleType(t, genericsTypeStack, false); } std::string GetTypeArgsQualifiedName(const std::vector& typeArgs, bool forNameFieldOfTi) { std::string res; for (auto typeArg : typeArgs) { res += GetTypeQualifiedName(*typeArg, forNameFieldOfTi) + ','; } if (!typeArgs.empty()) { res.pop_back(); // The last ',' is redundant. } return res; } std::string GetTypeQualifiedNameOfCustomType(const CHIR::CustomType& type, bool forNameFieldOfTi) { CJC_ASSERT(!type.IsAutoEnvInstBase()); std::stringstream ss; if (type.IsAutoEnvGenericBase()) { auto typeArgs = type.GetGenericArgs(); CJC_ASSERT(!typeArgs.empty()); auto retIter = typeArgs.end() - 1U; std::vector paramType(typeArgs.begin(), retIter); ss << MANGLE_CLOSURE_STR << MANGLE_LT_PREFIX; ss << '(' << GetTypeArgsQualifiedName(paramType, true) << ")->" << GetTypeQualifiedName(**retIter, true); ss << MANGLE_GT_PREFIX; } else { auto def = type.GetCustomTypeDef(); auto packageName = def->GetGenericDecl() ? def->GetGenericDecl()->GetPackageName() : def->GetPackageName(); ss << packageName << ':' << GetCustomTypeIdentifier(type); if (auto typeArgs = type.GetGenericArgs(); !typeArgs.empty()) { ss << MANGLE_LT_PREFIX << GetTypeArgsQualifiedName(typeArgs, forNameFieldOfTi) << MANGLE_GT_PREFIX; } } return ss.str(); } std::string GetTypeQualifiedName(const CHIR::Type& t, bool forNameFieldOfTi) { if (t.IsPrimitive()) { return t.ToString(); } auto k = t.GetTypeKind(); CJC_ASSERT(k != ChirTypeKind::TYPE_INVALID); switch (k) { case ChirTypeKind::TYPE_RAWARRAY: return MANGLE_RAWARR_STR + MANGLE_LT_PREFIX + \ GetTypeQualifiedName(*t.GetTypeArgs()[0], forNameFieldOfTi) + MANGLE_GT_PREFIX; case ChirTypeKind::TYPE_REFTYPE: return GetTypeQualifiedName(*static_cast(t).GetBaseType(), forNameFieldOfTi); case ChirTypeKind::TYPE_VARRAY: { auto& varrType = StaticCast(t); return MANGLE_VARR_STR + MANGLE_LT_PREFIX + \ GetTypeQualifiedName(*varrType.GetElementType(), forNameFieldOfTi) + ',' + \ std::to_string(varrType.GetSize()) + MANGLE_GT_PREFIX; } case ChirTypeKind::TYPE_CPOINTER: return MANGLE_CPTR_STR + MANGLE_LT_PREFIX + \ GetTypeQualifiedName(*t.GetTypeArgs()[0], forNameFieldOfTi) + MANGLE_GT_PREFIX; case ChirTypeKind::TYPE_CSTRING: return MANGLE_CSTRING_STR; case ChirTypeKind::TYPE_TUPLE: { auto& type = static_cast(t); return MANGLE_TUPLE_STR + MANGLE_LT_PREFIX + \ GetTypeArgsQualifiedName(type.GetElementTypes(), forNameFieldOfTi) + MANGLE_GT_PREFIX; } case ChirTypeKind::TYPE_STRUCT: case ChirTypeKind::TYPE_ENUM: case ChirTypeKind::TYPE_CLASS: { auto& type = static_cast(t); return GetTypeQualifiedNameOfCustomType(type, forNameFieldOfTi); } case ChirTypeKind::TYPE_FUNC: { auto& ft = static_cast(t); std::string name = '(' + GetTypeArgsQualifiedName(ft.GetParamTypes(), forNameFieldOfTi) + ")->" + GetTypeQualifiedName(*ft.GetReturnType(), forNameFieldOfTi); return name; } case ChirTypeKind::TYPE_GENERIC: return static_cast(t).GetSrcCodeIdentifier(); case ChirTypeKind::TYPE_VOID: return MANGLE_UNIT_STR; case ChirTypeKind::TYPE_BOXTYPE: { auto& type = static_cast(t); auto typeQualifiedName = GetTypeQualifiedName(*type.GetBaseType(), forNameFieldOfTi); return forNameFieldOfTi ? typeQualifiedName : (MANGLE_BOX_STR + MANGLE_LT_PREFIX + \ typeQualifiedName + MANGLE_GT_PREFIX); } default: CJC_ASSERT(false && "Should not reach here."); return ""; } } std::string GetCustomTypeIdentifier(const CHIR::CustomType& type) { return GetCustomTypeDefIdentifier(*type.GetCustomTypeDef()); } std::string GetCustomTypeDefIdentifier(const CHIR::CustomTypeDef& def) { CJC_ASSERT(!def.GetType()->IsAutoEnvBase()); auto name = def.GetSrcCodeIdentifier(); if (name.empty() || (def.TestAttr(CHIR::Attribute::PRIVATE) && def.Get() == Linkage::INTERNAL)) { return def.GetIdentifierWithoutPrefix(); } else { return name; } } } // namespace Cangjie::CHIR cangjie_compiler-1.0.7/src/Mangle/CMakeLists.txt000066400000000000000000000020461510705540100215430ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set(Mangle_SRC BaseMangler.cpp ASTMangler.cpp CHIRMangler.cpp Compression.cpp) set(Mangle_UNITTESTS_SRC BaseMangler.cpp ASTMangler.cpp CHIRMangler.cpp Compression.cpp) if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) get_filename_component(CHIRMANGLE CHIRTypeManglingUtils.cpp ABSOLUTE) list(APPEND Mangle_SRC ${CHIRMANGLE} CHIRManglingUtils.cpp) endif() add_library(CangjieMangle OBJECT ${Mangle_SRC}) add_library(CangjieUnittestsMangle OBJECT ${Mangle_UNITTESTS_SRC}) # use llvm if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) add_dependencies(CangjieMangle cjnative) endif() # IsEnumTrans2Class method in CodeGenUtils.h could be separated with llvm util methods. target_include_directories(CangjieMangle PRIVATE ${LLVM_INCLUDE_DIRS}) target_compile_options(CangjieMangle PRIVATE ${CJC_WITH_LLVM_EXTRA_WARNINGS}) cangjie_compiler-1.0.7/src/Mangle/Compression.cpp000066400000000000000000001275441510705540100220230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements name mangling compression. */ #include "Compression.h" #include "cangjie/Mangle/BaseMangler.h" using namespace Cangjie::MangleUtils; namespace Cangjie::Compression { std::string DecimalToManglingNumber(const std::string& decimal) { CJC_ASSERT(!decimal.empty() && "the string of decimal is empty."); const size_t n = 62; const std::string base62Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; for (size_t i = 0; i < decimal.length(); i++) { if (!std::isdigit(decimal[i])) { return ""; } } // Because _ represents 1, decimal to mangling number requires subtracting 1. int num = std::stoi(decimal) - 1; if (num < 0) { return MANGLE_WILDCARD_PREFIX; } std::string base62 = num > 0 ? "" : "0"; while (num > 0) { base62 = base62Chars[num % n] + base62; num /= n; } return base62 + MANGLE_WILDCARD_PREFIX; } inline void RecursionTypes(size_t& idx, char pre, std::string& compressed, std::tuple>&, std::unordered_map&, size_t&> tuple) { // The subscript indexes of the tuple are as follows: // { 0: nextIdx, 1: tys, 2: treeIdMap, 3: mid } size_t nextIdx = std::get<0>(tuple); if (nextIdx != idx + MANGLE_CHAR_LEN && !std::get<1>(tuple).empty()) { compressed += pre; for (const auto& ty : std::get<1>(tuple)) { // The index 2 of the tuple is treeIdMap and 3 is mid. RecursionType(ty, std::get<2>(tuple), std::get<3>(tuple), compressed, false); } idx = nextIdx; } } // Get identifier without prefix "@" inline std::string StripCangjieAt(std::string& identifier, size_t &pid) { if (identifier != "" && identifier[0] == MANGLE_AT_PREFIX[0]) { pid += MANGLE_CHAR_LEN; return identifier.substr(MANGLE_CHAR_LEN); } return identifier; } // Get identifier without prefix "_C" inline std::string StripCangjiePrefix(std::string& identifier, size_t &pid) { if (identifier.size() > MANGLE_PREFIX_LEN) { pid += MANGLE_PREFIX_LEN; return identifier.substr(MANGLE_PREFIX_LEN); } return identifier; } inline bool IsGeneralEntity(EntityType entityTy) { if (entityTy == EntityType::GENERAL_FUNCTION || entityTy == EntityType::GENERIC_FUNCTION || entityTy == EntityType::GENERIC_DATA || entityTy == EntityType::EXTEND) { return false; } return true; } inline bool IsGeneralType(BaseType bt) { if (bt == BaseType::TUPLE_TYPE || bt == BaseType::FUNCTION_TYPE || bt == BaseType::ENUM_TYPE || bt == BaseType::STRUCT_TYPE || bt == BaseType::CLASS_TYPE) { return false; } return true; } inline bool IsPrimitiveType(char ch) { if (PRIMITIVE_PREFIX_SET.find(ch) != PRIMITIVE_PREFIX_SET.end()) { return true; } return false; } inline bool IsPropName(std::string& mangled, size_t idx) { if (idx + MANGLE_CHAR_LEN >= mangled.size()) { return false; } // Judge current mangled name is prop name "ps/pg". if (mangled[idx] == 'p' && (mangled[idx + MANGLE_CHAR_LEN] == 's' || mangled[idx + MANGLE_CHAR_LEN] == 'g')) { return true; } return false; } inline bool NeedCompressed(std::string& identifier) { size_t pos = 0; while ((pos = identifier.find(MANGLE_COMPRESSED_PREFIX, pos)) != static_cast(-1)) { size_t nextIdx = ForwardNumber(identifier, pos + MANGLE_CHAR_LEN); if (nextIdx != pos + MANGLE_CHAR_LEN) { return true; } else { pos += MANGLE_CHAR_LEN; } } return false; } inline bool IsGlobalEncode(std::string& mangled) { if (mangled.find(MANGLE_GLOBAL_VARIABLE_INIT_PREFIX, 0) == 0 || mangled.find(MANGLE_GLOBAL_PACKAGE_INIT_PREFIX, 0) == 0 || mangled.find(MANGLE_GLOBAL_FILE_INIT_PREFIX, 0) == 0) { return true; } return false; } /** * Determine whether the code is a var decl code. * _CNE * _CNKE * */ bool IsVarDeclEncode(std::string& mangled) { bool isCompressed = false; const size_t n = mangled.size(); size_t idx = 0; if (mangled[0] == MANGLE_SUFFIX[0]) { return true; } else if (isdigit(mangled[0])) { idx = ForwardName(mangled, isCompressed); if (idx < n && mangled[idx] == MANGLE_COUNT_PREFIX[0]) { idx = ForwardNumber(mangled, idx + MANGLE_CHAR_LEN); if (idx < n && mangled[idx] == MANGLE_SUFFIX[0]) { return true; } else { return false; } } else if (idx < n && mangled[idx] == MANGLE_SUFFIX[0]) { return true; } } else if (mangled[0] == MANGLE_COUNT_PREFIX[0]) { idx = ForwardNumber(mangled, idx + MANGLE_CHAR_LEN); if (idx < n && mangled[idx] == MANGLE_SUFFIX[0]) { return true; } } return false; } /** * Determine whether the code is a default param function code. * _CPIH+EH+ */ bool IsDefaultParamFuncEncode(std::string& mangled) { if (mangled.find(MANGLE_FUNC_PARA_INIT_PREFIX, 0) == 0) { return true; } return false; } // Determine whether the code is operator name or std package name. inline bool IsSpecialName(std::string& mangled, size_t idx, const std::unordered_map& map) { // Special name range is [a-y][a-y], z is for extra scene. if (idx + MANGLE_CHAR_LEN < mangled.size() && mangled[idx] >= 'a' && mangled[idx] <= 'y' && mangled[idx + MANGLE_CHAR_LEN] >= 'a' && mangled[idx + MANGLE_CHAR_LEN] <= 'y') { std::string curName = mangled.substr(idx, MANGLE_SPECIAL_NAME_LEN); for (auto it = map.begin(); it != map.end(); ++it) { if (it->second == curName) { return true; } } } return false; } inline size_t ForwardCompressed(std::string& mangled, size_t idx, bool& isCompressed) { if (idx >= mangled.size()) { return idx; } if (mangled[idx] == MANGLE_COMPRESSED_PREFIX[0]) { isCompressed = true; return ForwardNumber(mangled, idx + MANGLE_CHAR_LEN); } return idx; } // Forward name, . size_t ForwardName(std::string& mangled, bool& isCompressed, size_t idx) { size_t cIdx = ForwardCompressed(mangled, idx, isCompressed); if (isCompressed) { return cIdx; } if (cIdx >= mangled.size()) { return cIdx; } if (IsSpecialName(mangled, cIdx, MangleUtils::OPERATOR_TYPE_MANGLE) || IsPropName(mangled, cIdx)) { return cIdx + MANGLE_SPECIAL_NAME_LEN; } if (!isdigit(mangled[cIdx])) { return cIdx; } if (mangled[cIdx] == MANGLE_ANONYMOUS_VARIABLE_PREFIX[0]) { return idx + MANGLE_CHAR_LEN; } size_t numberLen = 0; while (idx < mangled.size() && isdigit(mangled[idx + numberLen])) { numberLen++; } size_t number = static_cast(std::stoi(mangled.substr(idx, numberLen).c_str())); return idx + numberLen + number; } // Forward base62 number. size_t ForwardNumber(std::string& mangled, size_t idx) { size_t curIdx = idx; while (curIdx < mangled.size() && isalnum(mangled[curIdx])) { curIdx++; } if (curIdx < mangled.size() && mangled[curIdx] == MANGLE_WILDCARD_PREFIX[0]) { return curIdx + MANGLE_CHAR_LEN; } return idx; } /** * Forward filename number. * if filename length < 12, then $ * else then , the length of is 13 */ size_t ForwardFileNameNumber(std::string& mangled, size_t idx) { size_t curIdx = idx; bool isValid = false; while (true) { if (curIdx >= mangled.size()) { return idx; } if (mangled[curIdx] == MANGLE_DOLLAR_PREFIX[0]) { return ++curIdx; } if (curIdx + 1 - idx == FILE_HASH_LEN) { isValid = true; break; } curIdx++; } if (isValid) { for (size_t i = idx; i <= curIdx; i++) { if (!isalnum(mangled[i])) { return idx; } } return ++curIdx; } return idx; } // Forward package name, or std pkg. inline size_t ForwardUnitPackageName(std::string& mangled, size_t idx, bool& isCompressed) { size_t curIdx = idx; if (IsSpecialName(mangled, curIdx, MangleUtils::STD_PKG_MANGLE)) { curIdx += MANGLE_SPECIAL_NAME_LEN; } if (curIdx != idx + MANGLE_SPECIAL_NAME_LEN) { curIdx = ForwardName(mangled, isCompressed, idx); size_t cIdx = ForwardCompressed(mangled, curIdx, isCompressed); if (isCompressed) { return cIdx; } } return curIdx; } // Forward package name, [U]. inline size_t ForwardPackageName(std::string& mangled, size_t idx, bool& isCompressed) { size_t curIdx = ForwardUnitPackageName(mangled, idx, isCompressed); if (curIdx < mangled.size() && mangled[curIdx] == MANGLE_FILE_ID_PREFIX[0]) { return ForwardFileNameNumber(mangled, curIdx + MANGLE_CHAR_LEN); } return curIdx; } inline size_t ForwardPrimitiveType(std::string& mangled, size_t idx) { const size_t n = mangled.size(); if (mangled[idx] != 'D' && IsPrimitiveType(mangled[idx])) { return idx + MANGLE_CHAR_LEN; } if (mangled[idx] == 'D' && idx + MANGLE_CHAR_LEN < n && mangled[idx + MANGLE_CHAR_LEN] == 'h') { return idx + MANGLE_SPECIAL_NAME_LEN; } return idx; } // Forward generic types, I + E. inline size_t ForwardGenericTypes(std::string& mangled, std::vector>& tys, bool& isCompressed, size_t idx) { size_t curIdx = ForwardTypes(mangled, tys, isCompressed, idx + MANGLE_CHAR_LEN); if (isCompressed) { return idx; } if (curIdx != idx && mangled[curIdx] == MANGLE_SUFFIX[0]) { return curIdx + MANGLE_CHAR_LEN; } return idx; } // Forward generic type, G. inline size_t ForwardGenericType(std::string& mangled, size_t idx) { return ForwardNumber(mangled, idx + MANGLE_CHAR_LEN); } /** * Forward class type, CNE. * CNI+E */ inline size_t ForwardClassType(std::string& mangled, BaseType bt, std::vector>& tys, size_t idx, bool& isCompressed) { ForwardCompressed(mangled, idx, isCompressed); if (isCompressed) { return idx; } size_t curIdx = idx; const size_t n = mangled.size(); std::string mangledName; std::vector> genericTys; std::string pkg; std::string name; // Skip 'CN' 'RN' 'NN' if (idx + MANGLE_PREFIX_LEN < n && (mangled[idx] == 'C' || mangled[idx] == 'R' || mangled[idx] == 'N') && mangled[idx + MANGLE_CHAR_LEN] == MANGLE_NESTED_PREFIX[0]) { curIdx += MANGLE_PREFIX_LEN; } else { tys.push_back(std::make_unique(CompositeType(mangledName, BaseType::ERROR_BASE_TYPE, genericTys, pkg, name))); return idx; } size_t pIdx = curIdx; curIdx = ForwardPackageName(mangled, curIdx, isCompressed); if (isCompressed) { return idx; } if (pIdx < n) { pkg = mangled.substr(pIdx, curIdx - pIdx); } size_t nIdx = curIdx; curIdx = ForwardName(mangled, isCompressed, curIdx); if (isCompressed) { return idx; } if (nIdx < n) { name = mangled.substr(nIdx, curIdx - nIdx); } if (mangled[curIdx] == MANGLE_GENERIC_PREFIX[0]) { size_t genericIdx = ForwardGenericTypes(mangled, genericTys, isCompressed, curIdx); if (isCompressed) { return idx; } if (curIdx != genericIdx) { mangledName = mangled.substr(idx, genericIdx - idx); tys.push_back(std::make_unique(CompositeType(mangledName, bt, genericTys, pkg, name))); return genericIdx; } } else if (mangled[curIdx] == MANGLE_SUFFIX[0]) { mangledName = mangled.substr(idx, curIdx + MANGLE_CHAR_LEN - idx); tys.push_back(std::make_unique(CompositeType(mangledName, bt, genericTys, pkg, name))); return curIdx + MANGLE_CHAR_LEN; } tys.push_back(std::make_unique(CompositeType(mangledName, BaseType::ERROR_BASE_TYPE, genericTys, pkg, name))); return idx; } /** * Forward function type, include general function and C function. * F0E * FCE */ inline size_t ForwardFunctionType(std::string& mangled, std::vector>& tys, size_t idx, bool& isCompressed) { ForwardCompressed(mangled, idx, isCompressed); if (isCompressed) { return idx; } std::vector> retTy; std::vector> paramTys; std::string mangledName; size_t curIdx = ForwardType(mangled, retTy, isCompressed, idx + MANGLE_PREFIX_LEN); if (isCompressed) { return idx; } curIdx = ForwardTypes(mangled, paramTys, isCompressed, curIdx); if (isCompressed) { return idx; } if (curIdx != idx && mangled[curIdx] == MANGLE_SUFFIX[0]) { mangledName = mangled.substr(idx, curIdx + MANGLE_CHAR_LEN - idx); tys.push_back(std::make_unique(FunctionType(mangledName, BaseType::FUNCTION_TYPE, std::move(retTy[0]), paramTys))); return curIdx + MANGLE_CHAR_LEN; } tys.push_back(std::make_unique(FunctionType(mangledName, BaseType::ERROR_BASE_TYPE, std::move(retTy[0]), paramTys))); return idx; } // Forward tuple type, T+E. inline size_t ForwardTupleType(std::string& mangled, std::vector>& tys, size_t idx, bool& isCompressed) { ForwardCompressed(mangled, idx, isCompressed); if (isCompressed) { return idx; } std::vector> elementTys; std::string mangledName; size_t curIdx = idx + MANGLE_CHAR_LEN; if (curIdx < mangled.size()) { curIdx = ForwardNumber(mangled, curIdx); } if (curIdx < mangled.size()) { size_t endIdx = ForwardTypes(mangled, elementTys, isCompressed, curIdx); if (isCompressed) { return idx; } if (curIdx != endIdx && mangled[endIdx] == MANGLE_SUFFIX[0]) { mangledName = mangled.substr(idx, endIdx + MANGLE_CHAR_LEN - idx); tys.push_back(std::make_unique(TupleType(mangledName, BaseType::TUPLE_TYPE, elementTys))); return endIdx + MANGLE_CHAR_LEN; } } tys.push_back(std::make_unique(TupleType(mangledName, BaseType::ERROR_BASE_TYPE, elementTys))); return idx; } // Forward CPointer type, P. inline size_t ForwardCPointer(std::string& mangled, size_t idx, bool& isCompressed) { ForwardCompressed(mangled, idx, isCompressed); if (isCompressed) { return idx; } std::vector> ty; size_t curIdx = ForwardType(mangled, ty, isCompressed, idx + MANGLE_CHAR_LEN); if (isCompressed) { return idx; } if (curIdx != idx) { return curIdx; } return idx; } /** * Forward RawArray type or VArray type. * A * V */ inline size_t ForwardArrayType(std::string& mangled, size_t idx, bool& isCompressed) { ForwardCompressed(mangled, idx, isCompressed); if (isCompressed) { return idx; } size_t curIdx = idx + MANGLE_CHAR_LEN; if (curIdx < mangled.size()) { curIdx = ForwardNumber(mangled, curIdx); } if (curIdx < mangled.size()) { std::vector> ty; size_t endIdx = ForwardType(mangled, ty, isCompressed, curIdx); if (isCompressed) { return idx; } if (endIdx != curIdx) { return endIdx; } } return idx; } // Forward type, if type is invalid, return idx is initial value. size_t ForwardType(std::string& mangled, std::vector>& tys, bool& isCompressed, size_t idx) { char ch = mangled[idx]; BaseType bt; size_t nextIdx = idx; switch (ch) { case 'C': bt = BaseType::CLASS_TYPE; nextIdx = ForwardClassType(mangled, bt, tys, idx, isCompressed); return nextIdx; case 'R': bt = BaseType::STRUCT_TYPE; nextIdx = ForwardClassType(mangled, bt, tys, idx, isCompressed); return nextIdx; case 'N': bt = BaseType::ENUM_TYPE; nextIdx = ForwardClassType(mangled, bt, tys, idx, isCompressed); return nextIdx; case 'F': if (idx + MANGLE_CHAR_LEN < mangled.size() && (mangled[idx + MANGLE_CHAR_LEN] == '0' || mangled[idx + MANGLE_CHAR_LEN] == 'C')) { nextIdx = ForwardFunctionType(mangled, tys, idx, isCompressed); } else { nextIdx = idx; } return nextIdx; case 'T': nextIdx = ForwardTupleType(mangled, tys, idx, isCompressed); return nextIdx; case 'P': bt = BaseType::CPOINTER_TYPE; nextIdx = ForwardCPointer(mangled, idx, isCompressed); break; case 'A': bt = BaseType::RAWARRAY_TYPE; nextIdx = ForwardArrayType(mangled, idx, isCompressed); break; case 'V': bt = BaseType::VARRAY_TYPE; nextIdx = ForwardArrayType(mangled, idx, isCompressed); break; case 'k': bt = BaseType::CSTRING_TYPE; nextIdx = idx + MANGLE_CHAR_LEN; break; case 'G': bt = BaseType::GENERIC_TYPE; nextIdx = ForwardGenericType(mangled, idx); break; default: if (IsPrimitiveType(ch)) { bt = BaseType::PRIMITIVE_TYPE; nextIdx = ForwardPrimitiveType(mangled, idx); } else { bt = BaseType::ERROR_BASE_TYPE; nextIdx = idx; } break; } if (isCompressed) { return idx; } std::string mangledTy; if (nextIdx != idx) { mangledTy = mangled.substr(idx, nextIdx - idx); if (IsGeneralType(bt)) { tys.push_back(std::make_unique(CJType(mangledTy, bt))); } return nextIdx; } else { tys.push_back(std::make_unique(CJType(mangledTy, bt))); return idx; } } size_t ForwardTypes(std::string& mangled, std::vector>& tys, bool& isCompressed, size_t startId) { const size_t n = mangled.size(); if (n - startId <= 0) { return startId; } size_t idx = startId; while (idx < n && mangled[idx] != MANGLE_SUFFIX[0]) { size_t curIdx = ForwardType(mangled, tys, isCompressed, idx); if (curIdx == idx || isCompressed) { return curIdx; } idx = curIdx; } return idx; } std::string CJMangledCompression(const std::string mangled, bool isType) { bool isCompressed = false; size_t preIdx = 0; std::string cMangled = mangled; cMangled = StripCangjieAt(cMangled, preIdx); if (cMangled.empty() || isType || cMangled.find(MANGLE_CANGJIE_PREFIX, 0) != 0 || NeedCompressed(cMangled)) { return mangled; } // Skip "_C" cMangled = StripCangjiePrefix(cMangled, preIdx); if (IsGlobalEncode(cMangled)) { return mangled; } std::string rest; if (IsDefaultParamFuncEncode(cMangled)) { std::string compressed = ""; std::string nestedName = cMangled.substr(MANGLE_PREFIX_LEN); preIdx += MANGLE_PREFIX_LEN; std::vector> entities = TryParsePath(nestedName, rest, isCompressed); if (isCompressed) { return mangled; } isCompressed = SpanningDefaultParamFuncDeclTree(entities, rest, compressed); if (isCompressed) { return mangled; } return preIdx == 0 ? compressed : mangled.substr(0, preIdx) + compressed; } if (!cMangled.empty() && cMangled[0] == MANGLE_NESTED_PREFIX[0]) { std::string compressed = ""; std::string nestedName = cMangled.substr(MANGLE_CHAR_LEN); preIdx += MANGLE_CHAR_LEN; std::vector> entities = TryParsePath(nestedName, rest, isCompressed); if (isCompressed) { return mangled; } if (IsVarDeclEncode(rest)) { isCompressed = SpanningVarDeclTree(entities, rest, compressed); } else { // Determine whether the code is a function code. // _CN[I+]H+ // _CNK[I+]H+ // _CNLE isCompressed = SpanningFuncDeclTree(entities, rest, compressed); } if (isCompressed) { return mangled; } return preIdx == 0 ? compressed : mangled.substr(0, preIdx) + compressed; } return mangled; } /** * Get Entity of path. * Complying with the mangle rule the name prefix entity which contains I+. */ std::tuple TryGenericPrefixPath(std::string& mangled, bool& isCompressed, std::vector>& entities, size_t idx, size_t nextIdx, EntityType entityTy) { const size_t n = mangled.size(); std::string entityName = ""; std::vector> genericTys; nextIdx = ForwardTypes(mangled, genericTys, isCompressed, nextIdx + MANGLE_CHAR_LEN); if (isCompressed) { return std::tuple{nextIdx, entityTy, entityName, true}; } if (nextIdx < n && mangled[nextIdx] == MANGLE_SUFFIX[0]) { entityTy = EntityType::GENERIC_DATA; entityName = mangled.substr(idx, nextIdx - idx); entities.push_back(std::make_unique(DataEntity(entityName, entityTy, genericTys))); nextIdx += MANGLE_CHAR_LEN; } else if (nextIdx < n && mangled[nextIdx] == MANGLE_FUNC_PARAM_TYPE_PREFIX[0]) { std::vector> paramTys; nextIdx = ForwardTypes(mangled, paramTys, isCompressed, nextIdx + MANGLE_CHAR_LEN); if (isCompressed) { return std::tuple{nextIdx, entityTy, entityName, true}; } if (nextIdx < n && mangled[nextIdx] == MANGLE_SUFFIX[0]) { nextIdx++; entityTy = EntityType::GENERIC_FUNCTION; entityName = mangled.substr(idx, nextIdx - idx); entities.push_back(std::make_unique(FunctionEntity(entityName, entityTy, paramTys, genericTys))); } else { nextIdx = idx; } } else { nextIdx = idx; } return std::tuple{nextIdx, entityTy, entityName, false}; } /** * Get Entity of path. * Complying with the mangle rule of the name prefix entity. */ std::tuple TryNamePrefixPath(std::string& mangled, bool& isCompressed, std::vector>& entities, size_t idx) { const size_t n = mangled.size(); std::string entityName = ""; EntityType entityTy = EntityType::NAME; size_t nextIdx = ForwardName(mangled, isCompressed, idx); if (isCompressed) { return std::tuple{nextIdx, entityTy, entityName, true}; } size_t nameIdx = nextIdx; if (nextIdx < n && mangled[nextIdx] == MANGLE_COUNT_PREFIX[0]) { nextIdx = ForwardNumber(mangled, nextIdx + MANGLE_CHAR_LEN); } if (nextIdx < n) { if (mangled[nextIdx] == MANGLE_FILE_ID_PREFIX[0]) { nextIdx = ForwardFileNameNumber(mangled, nameIdx + MANGLE_CHAR_LEN); entityTy = nextIdx == nameIdx + MANGLE_CHAR_LEN ? EntityType::ERROR_ENTITY_TYPE : EntityType::PACKAGE; nextIdx = nextIdx == nameIdx + MANGLE_CHAR_LEN ? nameIdx : nextIdx; } else if (mangled[nextIdx] == MANGLE_GENERIC_PREFIX[0]) { return TryGenericPrefixPath(mangled, isCompressed, entities, idx, nextIdx, entityTy); } else if (mangled[nextIdx] == MANGLE_FUNC_PARAM_TYPE_PREFIX[0]) { std::vector> paramTys; std::vector> genericTys; nextIdx = ForwardTypes(mangled, paramTys, isCompressed, nameIdx + MANGLE_CHAR_LEN); if (isCompressed) { return std::tuple{nextIdx, entityTy, entityName, true}; } if (nextIdx < n && mangled[nextIdx] == MANGLE_SUFFIX[0]) { nextIdx++; entityTy = EntityType::GENERAL_FUNCTION; entityName = mangled.substr(idx, nextIdx - idx); entities.push_back(std::make_unique(FunctionEntity(entityName, entityTy, paramTys, genericTys))); } else { nextIdx = idx; } } else { nextIdx = nameIdx; } } return std::tuple{nextIdx, entityTy, entityName, false}; } /** * Get Entity of path. * Complying with the mangle rule of lambda entity. */ std::tuple TryLambdaPath(std::string& mangled, size_t idx) { EntityType entityTy = EntityType::ERROR_ENTITY_TYPE; size_t nextIdx = ForwardNumber(mangled, idx + MANGLE_CHAR_LEN); if (nextIdx == mangled.size() - MANGLE_CHAR_LEN && mangled[nextIdx] == MANGLE_SUFFIX[0]) { nextIdx = idx; } else { entityTy = nextIdx == idx + MANGLE_CHAR_LEN ? EntityType::ERROR_ENTITY_TYPE : EntityType::LAMBDA; nextIdx = nextIdx == idx + MANGLE_CHAR_LEN ? idx : nextIdx; } return std::tuple{nextIdx, entityTy}; } /** * Get Entity of path. * Complying with the mangle rule of extend entity. */ std::tuple TryExtendPath(std::string& mangled, bool& isCompressed, std::vector>& entities, size_t idx) { const size_t n = mangled.size(); EntityType entityTy = EntityType::ERROR_ENTITY_TYPE; std::string entityName = ""; std::vector> baseTy; size_t nextIdx = ForwardType(mangled, baseTy, isCompressed, idx + MANGLE_CHAR_LEN); if (isCompressed) { return std::tuple{nextIdx, entityTy, entityName, true}; } size_t tyId = nextIdx; std::string extendTy = mangled.substr(idx + MANGLE_CHAR_LEN, nextIdx - idx - MANGLE_CHAR_LEN); std::string fileId; std::string localId; if (nextIdx < n && mangled[nextIdx] == MANGLE_FILE_ID_PREFIX[0]) { size_t fileIdx = nextIdx; nextIdx = ForwardFileNameNumber(mangled, nextIdx + MANGLE_CHAR_LEN); fileId = mangled.substr(fileIdx, nextIdx - fileIdx); if (nextIdx < n && mangled[nextIdx] == MANGLE_COUNT_PREFIX[0]) { size_t localIdx = nextIdx; nextIdx = ForwardNumber(mangled, nextIdx); localId = mangled.substr(localIdx, nextIdx - localIdx); entityName = mangled.substr(idx, nextIdx - idx); entityTy = EntityType::EXTEND; entities.push_back(std::make_unique(ExtendEntity(entityName, entityTy, std::move(baseTy[0]), fileId, localId))); } else { nextIdx = idx; } } else { nextIdx = tyId; entityTy = EntityType::EXTEND; entityName = mangled.substr(idx, nextIdx - idx); entities.push_back(std::make_unique(ExtendEntity(entityName, entityTy, std::move(baseTy[0]), fileId, localId))); } return std::tuple{nextIdx, entityTy, entityName, false}; } /** * Get Entities of path. * Complying with the mangle rule of the entity. */ std::vector> TryParsePath(std::string& mangled, std::string& rest, bool& isCompressed) { const size_t n = mangled.size(); std::vector> entities; size_t idx = 0; while (idx < n) { char ch = mangled[idx]; size_t nextIdx = idx; EntityType entityTy = EntityType::ERROR_ENTITY_TYPE; std::string entityName = ""; if (ch == MANGLE_ANONYMOUS_VARIABLE_PREFIX[0]) { nextIdx = ForwardNumber(mangled, nextIdx + MANGLE_CHAR_LEN); entityTy = nextIdx == idx + MANGLE_CHAR_LEN ? EntityType::ERROR_ENTITY_TYPE : EntityType::ANONYMOUS; nextIdx = nextIdx == idx + MANGLE_CHAR_LEN ? idx : nextIdx; } else if (isdigit(ch) || IsSpecialName(mangled, idx, MangleUtils::OPERATOR_TYPE_MANGLE) || IsPropName(mangled, idx)) { bool exit = false; std::tie(nextIdx, entityTy, entityName, exit) = TryNamePrefixPath(mangled, isCompressed, entities, idx); if (exit) { return entities; } } else if (ch == MANGLE_EXTEND_PREFIX[0]) { bool exit = false; std::tie(nextIdx, entityTy, entityName, exit) = TryExtendPath(mangled, isCompressed, entities, idx); if (exit) { return entities; } } else if (ch == MANGLE_LAMBDA_PREFIX[0]) { std::tie(nextIdx, entityTy) = TryLambdaPath(mangled, idx); } else if (IsSpecialName(mangled, idx, MangleUtils::STD_PKG_MANGLE)) { nextIdx = ForwardPackageName(mangled, idx, isCompressed); entityTy = EntityType::PACKAGE; entityName = mangled.substr(idx, MANGLE_PREFIX_LEN); } if (idx != nextIdx && entityTy != EntityType::ERROR_ENTITY_TYPE) { if (IsGeneralEntity(entityTy)) { std::string mangledName = mangled.substr(idx, nextIdx - idx); entities.push_back(std::make_unique(Entity(mangledName, entityTy))); } idx = nextIdx; } else { break; } } if (idx < n) { rest = mangled.substr(idx); } return entities; } /** * Generate a tree for var decl. * Iterates through the generated index mapping, and compresses mangled name. */ bool SpanningVarDeclTree(std::vector>& entities, std::string& mangled, std::string& compressed) { bool isCompressed = false; std::unordered_map treeIdMap; size_t mid = 0; for (const auto& entity : entities) { RecursionEntity(entity, treeIdMap, mid, compressed); } if (mangled.size() > 0 && isdigit(mangled[0])) { size_t idx = ForwardName(mangled, isCompressed); if (isCompressed) { return true; } std::string name = mangled.substr(0, idx); RecursionHelper(name, treeIdMap, mid, compressed, false); if (idx < mangled.size()) { compressed += mangled.substr(idx); } } else if (mangled.size() > 0 && !isdigit(mangled[0])) { compressed += mangled; } return false; } /** * Generate a tree for sub func decl. */ std::tuple SpanningFuncSubTree(std::unordered_map& treeIdMap, std::string& mangled, std::string compressed, size_t idx, size_t& mid, bool& isCompressed) { const size_t n = mangled.size(); if (idx < n && mangled[idx] == MANGLE_GENERIC_PREFIX[0]) { compressed += MANGLE_GENERIC_PREFIX; std::vector> genericsTys; size_t nextIdx = ForwardTypes(mangled, genericsTys, isCompressed, idx + MANGLE_CHAR_LEN); if (isCompressed) { return std::tuple{true, idx, compressed}; } if (nextIdx != idx + MANGLE_CHAR_LEN) { idx = nextIdx; } for (const auto& ty : genericsTys) { RecursionType(ty, treeIdMap, mid, compressed, false); } } if (idx < n && mangled[idx] == MANGLE_FUNC_PARAM_TYPE_PREFIX[0]) { std::vector> paramTys; size_t nextIdx = ForwardTypes(mangled, paramTys, isCompressed, idx + MANGLE_CHAR_LEN); if (isCompressed) { return std::tuple{true, idx, compressed}; } auto tu = std::tie(nextIdx, paramTys, treeIdMap, mid); RecursionTypes(idx, MANGLE_FUNC_PARAM_TYPE_PREFIX[0], compressed, tu); } return std::tuple{false, idx, compressed}; } /** * Generate a tree for func decl. * Iterates through the generated index mapping, and compresses mangled name. */ bool SpanningFuncDeclTree(std::vector>& entities, std::string& mangled, std::string& compressed) { bool isCompressed = false; size_t mid = 0; const size_t n = mangled.size(); std::unordered_map treeIdMap; for (const auto& entity : entities) { RecursionEntity(entity, treeIdMap, mid, compressed); } if (n == 0 || (n > 0 && mangled[0] == MANGLE_LAMBDA_PREFIX[0])) { compressed += mangled; return false; } size_t idx = 0; if (isdigit(mangled[idx]) || IsSpecialName(mangled, idx, MangleUtils::OPERATOR_TYPE_MANGLE) || IsPropName(mangled, idx)) { idx = ForwardName(mangled, isCompressed); if (isCompressed) { return true; } std::string funcName = mangled.substr(0, idx); if (idx != 0) { RecursionHelper(mangled, treeIdMap, mid, compressed, true); RecursionHelper(funcName, treeIdMap, mid, compressed, false); } } bool isFunc = false; std::tie(isFunc, idx, compressed) = SpanningFuncSubTree(treeIdMap, mangled, compressed, idx, mid, isCompressed); if (idx < n) { compressed += mangled.substr(idx); } return false; } /** * Generate a tree for default param func decl. * Iterates through the generated index mapping, and compresses mangled name. */ bool SpanningDefaultParamFuncDeclTree(std::vector>& entities, std::string& mangled, std::string& compressed) { bool isCompressed = false; const size_t n = mangled.size(); size_t mid = 0; std::unordered_map treeIdMap; for (const auto& entity : entities) { RecursionEntity(entity, treeIdMap, mid, compressed); } if (n == 0) { return false; } size_t idx = 0; if (isdigit(mangled[idx]) || IsSpecialName(mangled, idx, MangleUtils::OPERATOR_TYPE_MANGLE) || IsPropName(mangled, idx)) { idx = ForwardName(mangled, isCompressed); if (isCompressed) { return true; } std::string funcName = mangled.substr(0, idx); if (idx != 0) { RecursionHelper(mangled, treeIdMap, mid, compressed, true); RecursionHelper(funcName, treeIdMap, mid, compressed, false); } bool isFunc = false; std::tie(isFunc, idx, compressed) = SpanningFuncSubTree(treeIdMap, mangled, compressed, idx, mid, isCompressed); } if (idx < n) { compressed += mangled.substr(idx); } return false; } /** * Help RecursionEntity achieve compressed generation. * The meaning of the isReplaced is whether it is added to the suffix of the compressed. */ bool RecursionHelper(std::string& name, std::unordered_map& treeIdMap, size_t& mid, std::string& compressed, bool isReplaced, bool isLeaf) { if (name.empty()) { return false; } if (treeIdMap.find(name) == treeIdMap.end()) { treeIdMap.emplace(name, mid++); if (!isReplaced && isLeaf) { compressed += name; } } else { if (!isReplaced) { std::string cid = MANGLE_COMPRESSED_PREFIX + DecimalToManglingNumber(std::to_string(treeIdMap.at(name))); if (name.size() > cid.size()) { compressed += cid; } else { compressed += name; } return true; } } return false; } /** * Generate the treeIdMap and obtain the compressed name. * Compression of a generic func entity. */ std::string RecursionFuncEntity(const std::unique_ptr& entity, std::unordered_map& treeIdMap, size_t& mid, std::string compressed, std::tuple& exInfo) { // The subscript indexes of the exInfo are as follows: // { 0: isGeneric, 1: isReplaced } bool isCompressed = false; bool isGeneric = std::get<0>(exInfo); bool isReplaced = std::get<1>(exInfo); size_t idx = ForwardName(entity->mangledName, isCompressed); std::string funcName = entity->mangledName.substr(0, idx); RecursionHelper(funcName, treeIdMap, mid, compressed, isReplaced); FunctionEntity* fe = static_cast(entity.get()); if (isGeneric) { if (!fe->genericTys.empty()) { compressed += MANGLE_GENERIC_PREFIX; } for (const auto& gty : fe->genericTys) { RecursionType(gty, treeIdMap, mid, compressed, isReplaced); } } if (!fe->paramTys.empty()) { compressed += MANGLE_FUNC_PARAM_TYPE_PREFIX; } for (const auto& pty : fe->paramTys) { RecursionType(pty, treeIdMap, mid, compressed, isReplaced); } if (!fe->paramTys.empty()) { compressed += MANGLE_SUFFIX; } return compressed; } /** * Generate the treeIdMap and obtain the compressed name. * Compression of an entity. */ void RecursionEntity(const std::unique_ptr& entity, std::unordered_map& treeIdMap, size_t& mid, std::string& compressed) { bool isCompressed = false; bool isReplaced = false; if (entity->mangledName.empty()) { return; } if (entity->entityTy == EntityType::PACKAGE || (entity->entityTy >= EntityType::GENERAL_FUNCTION && entity->entityTy <= EntityType::EXTEND)) { isReplaced = RecursionHelper(entity->mangledName, treeIdMap, mid, compressed, false, false); if (isReplaced) { return; } } switch (entity->entityTy) { case EntityType::NAME: case EntityType::LAMBDA: case EntityType::ANONYMOUS: RecursionHelper(entity->mangledName, treeIdMap, mid, compressed, false); break; case EntityType::PACKAGE: { size_t idx = ForwardUnitPackageName(entity->mangledName, 0, isCompressed); std::string pkg = entity->mangledName.substr(0, idx); RecursionHelper(pkg, treeIdMap, mid, compressed, isReplaced); std::string fid = entity->mangledName.substr(idx); RecursionHelper(fid, treeIdMap, mid, compressed, isReplaced); break; } case EntityType::GENERAL_FUNCTION: { std::tuple exInfo(false, isReplaced); compressed = RecursionFuncEntity(entity, treeIdMap, mid, compressed, exInfo); break; } case EntityType::GENERIC_FUNCTION: { std::tuple exInfo(true, isReplaced); compressed = RecursionFuncEntity(entity, treeIdMap, mid, compressed, exInfo); break; } case EntityType::GENERIC_DATA: { size_t idx = ForwardName(entity->mangledName, isCompressed); std::string name = entity->mangledName.substr(0, idx); RecursionHelper(name, treeIdMap, mid, compressed, isReplaced); DataEntity* de = static_cast(entity.get()); if (!de->genericTys.empty()) { compressed += MANGLE_GENERIC_PREFIX; } for (const auto& gty : de->genericTys) { RecursionType(gty, treeIdMap, mid, compressed, isReplaced); } if (!de->genericTys.empty()) { compressed += MANGLE_SUFFIX; } break; } case EntityType::EXTEND: { ExtendEntity* ee = static_cast(entity.get()); compressed += MANGLE_EXTEND_PREFIX; RecursionType(ee->extendTy, treeIdMap, mid, compressed, isReplaced); RecursionHelper(ee->fileId, treeIdMap, mid, compressed, isReplaced); RecursionHelper(ee->localId, treeIdMap, mid, compressed, isReplaced); break; } default: RecursionHelper(entity->mangledName, treeIdMap, mid, compressed, false); break; } } /** * Generate the treeIdMap and obtain the compressed name. * Compression of type. */ void RecursionType(const std::unique_ptr& ty, std::unordered_map& treeIdMap, size_t& mid, std::string& compressed, bool isReplaced) { if (isReplaced || ty->mangledName.empty()) { return; } if ((ty->baseTy >= BaseType::RAWARRAY_TYPE && ty->baseTy <= BaseType::CPOINTER_TYPE) || (ty->baseTy >= BaseType::ENUM_TYPE && ty->baseTy <= BaseType::FUNCTION_TYPE)) { isReplaced = RecursionHelper(ty->mangledName, treeIdMap, mid, compressed, isReplaced, false); if (isReplaced) { return; } } bool isCompressed = false; switch (ty->baseTy) { case BaseType::PRIMITIVE_TYPE: compressed += ty->mangledName; break; case BaseType::CSTRING_TYPE: case BaseType::GENERIC_TYPE: RecursionHelper(ty->mangledName, treeIdMap, mid, compressed, isReplaced); break; case BaseType::RAWARRAY_TYPE: case BaseType::VARRAY_TYPE: { size_t idx = ForwardNumber(ty->mangledName, MANGLE_CHAR_LEN); compressed += ty->mangledName.substr(0, idx); std::vector> vty; ForwardType(ty->mangledName, vty, isCompressed, idx); RecursionType(vty[0], treeIdMap, mid, compressed, isReplaced); break; } case BaseType::TUPLE_TYPE: { size_t idx = ForwardNumber(ty->mangledName, MANGLE_CHAR_LEN); compressed += ty->mangledName.substr(0, idx); TupleType* tty = static_cast(ty.get()); for (const auto& param : tty->elementTys) { RecursionType(param, treeIdMap, mid, compressed, isReplaced); } compressed += MANGLE_SUFFIX; break; } case BaseType::CPOINTER_TYPE: { compressed += MANGLE_POINTER_PREFIX; std::vector> pty; ForwardType(ty->mangledName, pty, isCompressed, MANGLE_CHAR_LEN); RecursionType(pty[0], treeIdMap, mid, compressed, isReplaced); break; } case BaseType::ENUM_TYPE: case BaseType::STRUCT_TYPE: case BaseType::CLASS_TYPE: { compressed += ty->mangledName.substr(0, MANGLE_PREFIX_LEN); CompositeType* ct = static_cast(ty.get()); size_t idx = ForwardPackageName(ct->pkg, 0, isCompressed); std::string pkg = ct->pkg.substr(0, idx); RecursionHelper(pkg, treeIdMap, mid, compressed, isReplaced); if (idx < ct->pkg.size()) { std::string fid = ct->pkg.substr(idx); RecursionHelper(fid, treeIdMap, mid, compressed, isReplaced); } RecursionHelper(ct->name, treeIdMap, mid, compressed, isReplaced); if (!ct->genericTys.empty()) { compressed += MANGLE_GENERIC_PREFIX; } for (const auto& gty : ct->genericTys) { RecursionType(gty, treeIdMap, mid, compressed, isReplaced); } if (!isReplaced) { compressed += MANGLE_SUFFIX; } break; } case BaseType::FUNCTION_TYPE: { compressed += ty->mangledName.substr(0, MANGLE_PREFIX_LEN); auto* ft = static_cast(ty.get()); RecursionType(ft->retTy, treeIdMap, mid, compressed, isReplaced); for (const auto& param : ft->paramTys) { RecursionType(param, treeIdMap, mid, compressed, isReplaced); } compressed += MANGLE_SUFFIX; break; } default: RecursionHelper(ty->mangledName, treeIdMap, mid, compressed, false); break; } } } // namespace Cangjie::Compressioncangjie_compiler-1.0.7/src/Mangle/Compression.h000066400000000000000000000230151510705540100214540ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Mangle Compression. */ #ifndef CANGJIE_MANGLE_COMPRESSION_H #define CANGJIE_MANGLE_COMPRESSION_H #include #include #include #include #include #include namespace Cangjie::Compression { enum class EntityType { NAME, PACKAGE, ANONYMOUS, GENERAL_FUNCTION, GENERIC_FUNCTION, GENERIC_DATA, EXTEND, LAMBDA, ERROR_ENTITY_TYPE }; enum class BaseType { PRIMITIVE_TYPE, ARRAY_TYPE, RAWARRAY_TYPE, VARRAY_TYPE, TUPLE_TYPE, CPOINTER_TYPE, CSTRING_TYPE, GENERIC_TYPE, ENUM_TYPE, STRUCT_TYPE, CLASS_TYPE, FUNCTION_TYPE, ERROR_BASE_TYPE }; struct Entity { explicit Entity(const std::string& mangledName, EntityType entityTy) : mangledName(mangledName), entityTy(entityTy) {} std::string mangledName; EntityType entityTy; virtual ~Entity() = default; }; struct CJType { explicit CJType(const std::string& mangledName, BaseType baseTy) : mangledName(mangledName), baseTy(baseTy) { this->mangledName = mangledName; } virtual ~CJType() = default; std::string mangledName = ""; BaseType baseTy = BaseType::ERROR_BASE_TYPE; }; struct FunctionEntity : public Entity { explicit FunctionEntity(const std::string& mangledName, EntityType entityTy, std::vector>& paramTys, std::vector>& genericTys) : Entity(mangledName, entityTy) { this->paramTys = std::move(paramTys); this->genericTys = std::move(genericTys); } std::vector> paramTys; std::vector> genericTys; }; struct DataEntity : public Entity { explicit DataEntity(const std::string& mangledName, EntityType entityTy, std::vector>& genericTys) : Entity(mangledName, entityTy) { this->genericTys = std::move(genericTys); } std::vector> genericTys; }; struct ExtendEntity : public Entity { explicit ExtendEntity(const std::string& mangledName, EntityType entityTy, std::unique_ptr extendTy, const std::string& fileId, const std::string& localId) : Entity(mangledName, entityTy), fileId(fileId), localId(localId) { this->extendTy = std::move(extendTy); } std::unique_ptr extendTy; std::string fileId; std::string localId; }; struct CompositeType : public CJType { explicit CompositeType(const std::string& mangledName, BaseType baseTy, std::vector>& genericTys, const std::string& pkg, const std::string& name) : CJType(mangledName, baseTy), pkg(pkg), name(name) { this->genericTys = std::move(genericTys); } std::vector> genericTys; std::string pkg; std::string name; }; struct FunctionType : public CJType { explicit FunctionType(const std::string& mangledName, BaseType baseTy, std::unique_ptr retTy, std::vector>& paramTys) : CJType(mangledName, baseTy) { this->retTy = std::move(retTy); this->paramTys = std::move(paramTys); } std::unique_ptr retTy; std::vector> paramTys; }; struct TupleType : public CJType { explicit TupleType(const std::string& mangledName, BaseType baseTy, std::vector>& elementTys) : CJType(mangledName, baseTy) { this->elementTys = std::move(elementTys); } std::vector> elementTys; }; /** * @brief Check whether the mangled name is variable decl. * * @param mangled The mangled name. * @return bool If yes, true is returned. Otherwise, false is returned. */ bool IsVarDeclEncode(std::string& mangled); /** * @brief Check whether the mangled name is default param function. * * @param mangled The mangled name. * @return bool If yes, true is returned. Otherwise, false is returned. */ bool IsDefaultParamFuncEncode(std::string& mangled); /** * @brief Get the index at the end of the type. * * @param mangled The mangled name. * @param tys the vector to save pointers of CJType. * @param isCompressed Whether the mangled name has been compressed. * @param idx The start index of the mangled name. * @return size_t The end index of the mangled name. */ size_t ForwardType(std::string& mangled, std::vector>& tys, bool& isCompressed, size_t idx = 0); /** * @brief Get the index at the end of the types. * * @param mangled The mangled name. * @param tys the vector to save pointers of CJType. * @param isCompressed Whether the mangled name has been compressed. * @param startId The start index of the mangled name. * @return size_t The end index of the mangled name. */ size_t ForwardTypes(std::string& mangled, std::vector>& tys, bool& isCompressed, size_t startId = 0); /** * @brief Get the index at the end of the name. * * @param mangled The mangled name. * @param isCompressed Whether the mangled name has been compressed. * @param idx The start index of the mangled name. * @return size_t The end index of the mangled name. */ size_t ForwardName(std::string& mangled, bool& isCompressed, size_t idx = 0); /** * @brief Get the index at the end of the number. * * @param mangled The mangled name. * @param idx The start index of the mangled name. * @return size_t The end index of the mangled name. */ size_t ForwardNumber(std::string& mangled, size_t idx = 0); /** * @brief Main entry of Mangler compression. * * @param mangled The mangled name. * @param isType Whether the mangled name is type. * @return std::string The mangled name after compression. */ std::string CJMangledCompression(const std::string mangled, bool isType = false); /** * @brief Try parse path of the mangled name to generate entity vector. * * @param mangled The mangled name. * @param rest The mangled name after removing entities string. * @param isCompressed Whether the mangled name has been compressed. * @return std::vector> The entities. */ std::vector> TryParsePath(std::string& mangled, std::string& rest, bool& isCompressed); /** * @brief Generate variable decl compressed mangled name. * * @param entities It belongs to prefix path of variable decl. * @param mangled The mangled name. * @param compressed The compressed mangled name to be modified. * @return bool If generate compressed mangled name success, true is returned. Otherwise, false is returned. */ bool SpanningVarDeclTree(std::vector>& entities, std::string& mangled, std::string& compressed); /** * @brief Generate function decl compressed mangled name. * * @param entities It belongs to prefix path of function decl. * @param mangled The mangled name. * @param compressed The compressed mangled name to be modified. * @return bool If generate compressed mangled name success, true is returned. Otherwise, false is returned. */ bool SpanningFuncDeclTree(std::vector>& entities, std::string& mangled, std::string& compressed); /** * @brief Generate default param function decl compressed mangled name. * * @param entities It belongs to prefix path of default param function decl. * @param mangled The mangled name. * @param compressed The compressed mangled name to be modified. * @return bool If generate compressed mangled name success, true is returned. Otherwise, false is returned. */ bool SpanningDefaultParamFuncDeclTree(std::vector>& entities, std::string& mangled, std::string& compressed); /** * @brief Generate compressed mangled name via recursion entity. * * @param entity Recursed entity. * @param treeIdMap The map which key is substring of mangled name, value is compressed index. * @param mid The treeIdMap size. * @param compressed The compressed mangled name. */ void RecursionEntity(const std::unique_ptr& entity, std::unordered_map& treeIdMap, size_t& mid, std::string& compressed); /** * @brief Generate compressed mangled name via recursion type. * * @param ty Recursed type. * @param treeIdMap The map which key is substring of mangled name, value is compressed index. * @param mid The treeIdMap size. * @param compressed The compressed mangled name. */ void RecursionType(const std::unique_ptr& ty, std::unordered_map& treeIdMap, size_t& mid, std::string& compressed, bool isReplaced); /** * @brief Helper function for recursive unit, which is used to update treeIdMap and compressed. * * @param name The string which may be added to treeIdMap. * @param treeIdMap The map which may be updated. * @param mid The treeIdMap size. * @param compressed The compressed mangled name which may be updated. * @param isReplaced Whether the string has been traversed. * @param isLeaf Whether the string is leaf. * @return bool The end index of the mangled name. */ bool RecursionHelper(std::string& name, std::unordered_map& treeIdMap, size_t& mid, std::string& compressed, bool isReplaced, bool isLeaf = true); } // namespace Cangjie::Compression #endif // CANGJIE_MANGLE_COMPRESSION_Hcangjie_compiler-1.0.7/src/MetaTransformation/000077500000000000000000000000001510705540100214135ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/MetaTransformation/CMakeLists.txt000066400000000000000000000010571510705540100241560ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. if (CANGJIE_CODEGEN_CJNATIVE_BACKEND) set(META_TRANSFORMATION_SRC MetaTransformPluginManager.cpp MetaTransformPluginBuilder.cpp) add_library(CangjieMetaTransformation OBJECT ${META_TRANSFORMATION_SRC}) target_compile_options(CangjieMetaTransformation PRIVATE ${CJC_EXTRA_WARNINGS}) endif() cangjie_compiler-1.0.7/src/MetaTransformation/MetaTransformPluginBuilder.cpp000066400000000000000000000012261510705540100273700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "cangjie/MetaTransformation/MetaTransform.h" using namespace Cangjie; CHIRPluginManager MetaTransformPluginBuilder::BuildCHIRPluginManager(CHIR::CHIRBuilder& builder) { CHIRPluginManager chirPluginManager; for (auto& callback : chirPluginCallbacks) { callback(chirPluginManager, builder); } return chirPluginManager; } #endif cangjie_compiler-1.0.7/src/MetaTransformation/MetaTransformPluginManager.cpp000066400000000000000000000006741510705540100273620ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "cangjie/MetaTransformation/MetaTransform.h" namespace Cangjie { template class MetaTransformPluginManager; } #endif cangjie_compiler-1.0.7/src/Modules/000077500000000000000000000000001510705540100172065ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Modules/ASTSerialization/000077500000000000000000000000001510705540100223735ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Modules/ASTSerialization/ASTLoader.cpp000066400000000000000000001113461510705540100246630ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * This file implements the AST Loader related classes. */ #include "ASTLoaderImpl.h" #include "flatbuffers/ModuleFormat_generated.h" #include "cangjie/AST/ASTCasting.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/Version.h" #include "cangjie/Lex/Token.h" #include "cangjie/Utils/CheckUtils.h" using namespace Cangjie; using namespace AST; namespace Cangjie { const std::unordered_map ACCESS_LEVEL_RMAP = { #define ACCESS_LEVEL(AST_KIND, FBS_KIND) {PackageFormat::AccessLevel_##FBS_KIND, AST::AccessLevel::AST_KIND}, #include "Mapping.inc" #undef ACCESS_LEVEL }; const std::unordered_map> ACCESS_MODIFIER_RMAP = { #define ACCESS_MODIFIER(AST_KIND, FBS_KIND) \ {PackageFormat::AccessModifier_##FBS_KIND, {TokenKind::AST_KIND, AST::Attribute::AST_KIND}}, #include "Mapping.inc" #undef ACCESS_MODIFIER }; const std::unordered_map BUILTIN_TYPE_RMAP = { #define BUILTIN_TYPE(AST_KIND, FBS_KIND) {PackageFormat::BuiltInType_##FBS_KIND, AST::BuiltInType::AST_KIND}, #include "Mapping.inc" #undef BUILTIN_TYPE }; const std::unordered_map STRATEGY_RMAP = { #define OVERFLOW_STRATEGY(AST_KIND, FBS_KIND) {PackageFormat::OverflowPolicy_##FBS_KIND, OverflowStrategy::AST_KIND}, #include "Mapping.inc" #undef OVERFLOW_STRATEGY }; const std::unordered_map TYPE_KIND_RMAP = { #define TYPE_KIND(AST_KIND, FBS_KIND) {PackageFormat::TypeKind_##FBS_KIND, AST::TypeKind::AST_KIND}, #include "Mapping.inc" #undef TYPE_KIND }; const std::unordered_map OP_KIND_RMAP = { #define OPERATOR_KIND(AST_KIND, FBS_KIND) {PackageFormat::OperatorKind_##FBS_KIND, TokenKind::AST_KIND}, #include "Mapping.inc" #undef OPERATOR_KIND }; const std::unordered_map CALL_KIND_RMAP = { #define CALL_KIND(AST_KIND, FBS_KIND) {PackageFormat::CallKind_##FBS_KIND, AST::CallKind::AST_KIND}, #include "Mapping.inc" #undef CALL_KIND }; const std::unordered_map LIT_CONST_KIND_RMAP = { #define LIT_CONST_KIND(AST_KIND, FBS_KIND) {PackageFormat::LitConstKind_##FBS_KIND, AST::LitConstKind::AST_KIND}, #include "Mapping.inc" #undef LIT_CONST_KIND }; const std::unordered_map STRING_KIND_RMAP = { #define STRING_KIND(AST_KIND, FBS_KIND) {PackageFormat::StringKind_##FBS_KIND, AST::StringKind::AST_KIND}, #include "Mapping.inc" #undef STRING_KIND }; const std::unordered_map FOR_IN_KIND_RMAP = { #define FOR_IN_KIND(AST_KIND, FBS_KIND) {PackageFormat::ForInKind_##FBS_KIND, AST::ForInKind::AST_KIND}, #include "Mapping.inc" #undef FOR_IN_KIND }; } // namespace Cangjie namespace { template void SetDeclBody(DeclT& decl) { if constexpr (std::is_same_v || std::is_same_v) { decl.bodyScope = MakeOwned(); } else if constexpr (std::is_same_v) { // 'funBody' will be loaded in 'LoadFuncDeclAdvancedInfo'. } else if constexpr (std::is_same_v) { decl.body = MakeOwned(); } else if constexpr (std::is_same_v) { decl.body = MakeOwned(); } else if constexpr (std::is_same_v) { decl.body = MakeOwned(); } else { // Always false, not reachable, guarantees this function is not used in unexpected situations. static_assert(std::is_same_v); } } void SetOuterDeclForParamDecl(FuncDecl& func, Decl& parentDecl) { if (func.funcBody->paramLists.empty()) { return; } for (auto& param : func.funcBody->paramLists[0]->params) { if (param->desugarDecl) { param->desugarDecl->outerDecl = &parentDecl; } } } void SetOuterDeclForSetterAndGetter(PropDecl& propDecl) { for (auto& it : propDecl.setters) { it->propDecl = &propDecl; it->outerDecl = propDecl.outerDecl; it->scopeLevel = propDecl.scopeLevel; it->isSetter = true; } for (auto& it : propDecl.getters) { it->propDecl = &propDecl; it->outerDecl = propDecl.outerDecl; it->scopeLevel = propDecl.scopeLevel; it->isGetter = true; } } // Used for set imported struct decls' member's scope level. const uint32_t MEMBER_FUNC_SCOPE_LEVEL = 2; void SetOuterDeclForMemberDecl(Decl& member, Decl& parentDecl) { member.outerDecl = &parentDecl; member.scopeLevel = MEMBER_FUNC_SCOPE_LEVEL; if (auto propDecl = DynamicCast(&member); propDecl) { SetOuterDeclForSetterAndGetter(*propDecl); } else if (auto func = DynamicCast(&member); func && func->funcBody) { SetOuterDeclForParamDecl(*func, parentDecl); } } } // namespace ASTLoader::ASTLoader(std::vector&& data, const std::string& fullPackageName, TypeManager& typeManager, const CjoManager& cjoManager, const GlobalOptions& opts) { pImpl = MakeOwned(std::move(data), fullPackageName, typeManager, cjoManager, opts); } ASTLoader::~ASTLoader() { } void ASTLoader::SetImportSourceCode(bool enable) const { pImpl->SetImportSourceCode(enable); } std::string ASTLoader::GetImportedPackageName() const { CJC_NULLPTR_CHECK(pImpl); return pImpl->importedPackageName; } std::string ASTLoader::PreReadAndSetPackageName() { return pImpl->PreReadAndSetPackageName(); } std::string ASTLoader::ASTLoaderImpl::PreReadAndSetPackageName() { if (!package) { package = PackageFormat::GetPackage(data.data()); } CJC_NULLPTR_CHECK(package); CJC_NULLPTR_CHECK(package->fullPkgName()); importedPackageName = package->fullPkgName()->str(); return importedPackageName; } std::vector ASTLoader::ReadFileNames() const { return pImpl->ReadFileNames(); } std::vector ASTLoader::ASTLoaderImpl::ReadFileNames() const { std::vector files; auto allFiles = package->allFiles(); CJC_NULLPTR_CHECK(allFiles); for (uoffset_t i = 0; i < allFiles->size(); i++) { files.push_back(allFiles->Get(i)->str()); } return files; } const std::vector ASTLoader::GetDependentPackageNames() const { std::vector dep; (void)std::copy_if(pImpl->importedFullPackageNames.begin(), pImpl->importedFullPackageNames.end(), std::back_inserter(dep), [this](auto& it) { return it != pImpl->importedPackageName; }); return dep; } bool ASTLoader::ASTLoaderImpl::VerifyForData(const std::string& id) { size_t size = data.size(); // We need to verify the size first. flatbuffers::Verifier verifier(data.data(), size, FB_MAX_DEPTH, FB_MAX_TABLES); if (!PackageFormat::VerifyPackageBuffer(verifier)) { diag.DiagnoseRefactor( DiagKindRefactor::module_loaded_ast_failed, DEFAULT_POSITION, id, importedPackageName, CANGJIE_VERSION); return false; } return true; } std::string ASTLoader::LoadPackageDepInfo() const { CJC_NULLPTR_CHECK(pImpl); return pImpl->LoadPackageDepInfo(); } std::string ASTLoader::ASTLoaderImpl::LoadPackageDepInfo() { if (!VerifyForData("ast")) { return ""; } package = PackageFormat::GetPackage(data.data()); return package->pkgDepInfo()->str(); } OwnedPtr ASTLoader::LoadPackageDependencies() const { CJC_NULLPTR_CHECK(pImpl); return pImpl->LoadPackageDependencies(); } OwnedPtr ASTLoader::ASTLoaderImpl::LoadPackageDependencies() { if (!VerifyForData("ast")) { return {}; } return PreLoadImportedPackageNode(); } // Deserialize common part of package into already existing Package node void ASTLoader::PreloadCommonPartOfPackage(AST::Package& pkg) const { CJC_NULLPTR_CHECK(pImpl); pImpl->deserializingCommon = true; return pImpl->PreloadCommonPartOfPackage(pkg); } std::vector> ASTLoader::ASTLoaderImpl::LoadImportSpecs(const PackageFormat::Imports* imports) { std::vector> importSpecsVec; auto importSpecs = imports->importSpecs(); CJC_NULLPTR_CHECK(importSpecs); for (uoffset_t i = 0; i < importSpecs->size(); i++) { auto rawImportSpec = importSpecs->Get(i); OwnedPtr importSpec = MakeOwned(); auto begin = rawImportSpec->begin(); auto end = rawImportSpec->end(); importSpec->begin = LoadPos(begin); importSpec->end = LoadPos(end); importSpec->EnableAttr(Attribute::IMPORTED); // This is WA to pass checks, because attributes do not deserialized for ImportSpec bool compilerAdded = rawImportSpec->end()->line() == 0; if (compilerAdded) { importSpec->EnableAttr(Attribute::COMPILER_ADD); } auto [modifierKind, attr] = ACCESS_MODIFIER_RMAP.at(rawImportSpec->reExport()); importSpec->modifier = MakeOwned(modifierKind, DEFAULT_POSITION); importSpec->EnableAttr(attr); LoadImportContent(importSpec->content, *rawImportSpec); LoadNodePos(*rawImportSpec, *importSpec); importSpecsVec.emplace_back(std::move(importSpec)); } return importSpecsVec; } void ASTLoader::ASTLoaderImpl::PreloadCommonPartOfPackage(AST::Package& pkg) { if (!VerifyForData("ast")) { CJC_ABORT(); } package = PackageFormat::GetPackage(data.data()); CJC_NULLPTR_CHECK(package); curPackage = &pkg; // Deserialize common part AST into current platform package AST allTypes.resize(package->allTypes()->size(), nullptr); auto fileSize = package->allFiles()->size(); allFileIds.resize(fileSize); for (uoffset_t i = 0; i < fileSize; i++) { CJC_NULLPTR_CHECK(package->allFileImports()); auto&& importInfos = LoadImportSpecs(package->allFileImports()->Get(i)); CJC_NULLPTR_CHECK(package->allFileInfo()); // Load file info for CJMP auto fileInfo = package->allFileInfo()->Get(i); allFileIds[i] = fileInfo->fileID(); auto file = CreateFileNode(*curPackage, fileInfo->fileID(), std::move(importInfos)); file->EnableAttr(Attribute::COMMON); file->isCommon = true; file->begin = LoadPos(fileInfo->begin()); file->end = LoadPos(fileInfo->end()); pkg.files.emplace_back(std::move(file)); } AddCurFile(pkg); auto imports = package->imports(); CJC_NULLPTR_CHECK(imports); uoffset_t nImports = imports->size(); for (uoffset_t i = 0; i < nImports; i++) { std::string importItem = imports->Get(i)->str(); importedFullPackageNames.emplace_back(importItem); } } void ASTLoader::LoadPackageDecls() const { pImpl->LoadPackageDecls(); } /** 'LoadPackageDependencies' must be called before this method. */ void ASTLoader::ASTLoaderImpl::LoadPackageDecls() { CJC_ASSERT(curPackage && package); // Get toplevel decls. uoffset_t nDecls = package->allDecls()->size(); for (uoffset_t i = 0; i < nDecls; i++) { // Only toplevel decls are loaded. if (package->allDecls()->Get(i)->isTopLevel()) { if (IgnoreDecl(i)) { continue; } // NOTE: FormattedIndex is vector offset plus 1. auto tmpDecl = LoadDecl(i + 1); if (!tmpDecl) { continue; } auto fileID = tmpDecl->begin.fileID; if (auto found = idToFileMap.find(fileID); found != idToFileMap.end()) { tmpDecl->curFile = found->second; } CJC_NULLPTR_CHECK(tmpDecl->curFile); AddCurFile(*tmpDecl, tmpDecl->curFile); // Guarantees all sub-nodes have 'curFile'. if (tmpDecl->TestAttr(Attribute::GENERIC_INSTANTIATED)) { curPackage->genericInstantiatedDecls.emplace_back(std::move(tmpDecl)); continue; } // In this branch, decl's 'curFile' nodes must exist in current package. CJC_ASSERT(tmpDecl->curFile->curPackage == curPackage); if (tmpDecl->IsExportedDecl() || tmpDecl->TestAttr(Attribute::FROM_COMMON_PART)) { tmpDecl->curFile->decls.emplace_back(std::move(tmpDecl)); } else { tmpDecl->curFile->exportedInternalDecls.emplace_back(std::move(tmpDecl)); } } } // Remove decls inside any generic decl. Utils::EraseIf( importNonGenericSrcFuncDecls, [](auto it) { return IsInDeclWithAttribute(*it, Attribute::GENERIC); }); curPackage->srcImportedNonGenericDecls = importNonGenericSrcFuncDecls; // hasSourceImportedDecl true will trait as import package, but common part is not when compile platform if (!deserializingCommon) { curPackage->hasSourceImportedDecl = !allLoadedExprs.empty() || curPackage->TestAttr(Attribute::TOOL_ADD); } AddCurFile(*curPackage); // Guarantees all nodes have 'curFile'. } OwnedPtr ASTLoader::ASTLoaderImpl::CreateFileNode( Package& pkg, unsigned int fileId, std::vector>&& imports) { auto file = MakeOwned(); file->curFile = file.get(); file->curPackage = &pkg; Source& source = sourceManager.GetSource(fileId); file->fileName = FileUtil::GetFileName(source.path); file->filePath = source.path; file->fileHash = source.fileHash; file->imports = std::move(imports); file->begin.fileID = fileId; idToFileMap.emplace(fileId, file.get()); return file; } OwnedPtr ASTLoader::ASTLoaderImpl::PreLoadImportedPackageNode() { // Begin to parse the flatbuffer. package = PackageFormat::GetPackage(data.data()); CJC_NULLPTR_CHECK(package); // Imported package is PackageDecl Node. OwnedPtr packageNode = MakeOwned(); packageNode->EnableAttr(Attribute::IMPORTED); curPackage = packageNode.get(); // Module name and package name packageNode->fullPackageName = package->fullPkgName()->str(); if (package->kind() == PackageFormat::PackageKind_Foreign) { packageNode->EnableAttr(Attribute::TOOL_ADD); } else if (package->kind() == PackageFormat::PackageKind_Mock) { packageNode->EnableAttr(Attribute::MOCK_SUPPORTED); } packageNode->isMacroPackage = package->kind() == PackageFormat::PackageKind_Macro; packageNode->accessible = ACCESS_LEVEL_RMAP.at(package->access()); allTypes.resize(package->allTypes()->size(), nullptr); // Get all files in current package. auto fileSize = package->allFiles()->size(); allFileIds.resize(fileSize); for (uoffset_t i = 0; i < fileSize; i++) { auto tmpFilePath = package->allFiles()->Get(i)->str(); auto tmpFileId = sourceManager.AddSource(tmpFilePath, "", package->fullPkgName()->str()); allFileIds[i] = tmpFileId; auto&& importInfos = LoadImportSpecs(package->allFileImports()->Get(i)); packageNode->files.emplace_back(CreateFileNode(*packageNode, tmpFileId, std::move(importInfos))); } AddCurFile(*packageNode); uoffset_t nImports = package->imports()->size(); for (uoffset_t i = 0; i < nImports; i++) { std::string importItem = package->imports()->Get(i)->str(); importedFullPackageNames.emplace_back(importItem); } return packageNode; } void ASTLoader::ASTLoaderImpl::LoadImportContent(ImportContent& content, const PackageFormat::ImportSpec& is) const { content.kind = !is.asIdentifier()->string_view().empty() ? ImportKind::IMPORT_ALIAS : is.identifier()->string_view() == "*" ? ImportKind::IMPORT_ALL : ImportKind::IMPORT_SINGLE; content.prefixPaths.reserve(is.prefixPaths()->size()); for (const auto& path : *is.prefixPaths()) { content.prefixPaths.emplace_back(path->str()); } for (size_t i = 0; i < content.prefixPaths.size(); ++i) { content.prefixPoses.emplace_back(DEFAULT_POSITION); content.prefixDotPoses.emplace_back(DEFAULT_POSITION); } content.hasDoubleColon = is.hasDoubleColon(); content.identifier = is.identifier()->str(); content.identifier.SetPos(DEFAULT_POSITION, DEFAULT_POSITION); content.asPos = DEFAULT_POSITION; if (content.kind == ImportKind::IMPORT_ALIAS) { content.aliasName = is.asIdentifier()->str(); content.aliasName.SetPos(DEFAULT_POSITION, DEFAULT_POSITION); } content.begin = DEFAULT_POSITION; content.end = DEFAULT_POSITION; content.isDecl = is.isDecl(); } Position ASTLoader::ASTLoaderImpl::LoadPos(const PackageFormat::Position* posObj) { CJC_NULLPTR_CHECK(posObj); auto fileIndex = posObj->file(); auto pkgIndex = posObj->pkgId(); auto line = posObj->line(); auto column = posObj->column(); unsigned int fileId = 0; // When 'pkgIndex' is default, it means the file is from current package. if (pkgIndex == INVALID_FORMAT_INDEX) { // NOTE: current package's 'fileIndex' is vector offset plus 1. fileId = (fileIndex == INVALID_FORMAT_INDEX) ? 0 : allFileIds.at(static_cast(fileIndex - 1)); } else { // file is from other package. // NOTE: other package's 'pkgIndex' is vector offset plus 1. CJC_ASSERT(pkgIndex - 1 < importedFullPackageNames.size()); std::string fullPackageName = importedFullPackageNames.at(pkgIndex - 1); if (auto pd = cjoManager.GetPackageDecl(fullPackageName)) { CJC_ASSERT(fileIndex < pd->srcPackage->files.size()); fileId = pd->srcPackage->files[fileIndex]->begin.fileID; idToFileMap.emplace(fileId, pd->srcPackage->files[fileIndex].get()); } } return {fileId, static_cast(line), static_cast(column)}; } void ASTLoader::ASTLoaderImpl::AddDeclToImportedPackage(Decl& decl) { if (decl.astKind == ASTKind::EXTEND_DECL) { return; // ExtendDecl will never be referenced. } // NOTE: only exactly one 'ASTLoader' will write 'exportIdDeclMap' with given 'importedPackageName'. // So following process will not exist racing condition in multithreading. auto& exportId = decl.exportId; auto exportIdDeclMap = cjoManager.GetExportIdDeclMap(importedPackageName); // Current loading package must existed in 'packageMap'. CJC_NULLPTR_CHECK(exportIdDeclMap); if (exportId.empty()) { exportIdDeclMap->emplace(decl.identifier, &decl); } else { auto it1 = exportIdDeclMap->find(exportId); if (it1 == exportIdDeclMap->end()) { exportIdDeclMap->emplace(exportId, &decl); } else { // NOTE: when 'importSrcCode' is disabled, the imported ast cannot to be used for code generation, // this kind of situation is for LSP usage now, so the duplication error can be ignored. bool reportError = (it1->second != &decl) && importSrcCode; reportError = reportError && !isChirNow; if (reportError) { InternalError("Found same exportID when import a package."); } } } } /** * Get decl pointer according to packageIndex and declIndex. * NOTE: This function should only be used during 'LoadRef' stage that all decls are loaded. */ Ptr ASTLoader::ASTLoaderImpl::GetDeclFromIndex(const PackageFormat::FullId* fullId) { CJC_NULLPTR_CHECK(fullId); auto pkgIndex = fullId->pkgId(); if (pkgIndex == INVALID_PACKAGE_INDEX) { return nullptr; } if (pkgIndex == PKG_REFERENCE_INDEX) { // When index is 'PKG_REFERENCE_INDEX' the 'decl' is referenced full package name. return cjoManager.GetPackageDecl(fullId->decl()->str()); } else if (pkgIndex == CURRENT_PKG_INDEX) { // NOTE: for incremental loading case, the decl may not exist in cache. auto found = allLoadedDecls.find(fullId->index()); return found != allLoadedDecls.end() ? found->second : nullptr; } std::string fullPackageName = importedFullPackageNames.at(static_cast(pkgIndex)); // Get decl from imported packages. auto exportIdDeclMap = cjoManager.GetExportIdDeclMap(fullPackageName); auto exportId = fullId->decl()->str(); if (exportIdDeclMap != nullptr) { auto it1 = exportIdDeclMap->find(exportId); if (it1 != exportIdDeclMap->end()) { return it1->second; } } if (cjoManager.GetPackageDecl(fullPackageName) == nullptr && !isLoadCache) { diag.DiagnoseRefactor( DiagKindRefactor::package_invalid_cjo_dependency, DEFAULT_POSITION, importedPackageName, fullPackageName); } return nullptr; } /** Start of loading AST Nodes */ OwnedPtr ASTLoader::ASTLoaderImpl::LoadGeneric(Decl& decl, const PackageFormat::Generic* generic) { if (generic == nullptr) { return {}; } CJC_NULLPTR_CHECK(generic->typeParameters()); auto genericNode = MakeOwned(); uoffset_t length = static_cast(generic->typeParameters()->size()); for (uoffset_t i = 0; i < length; i++) { auto index = generic->typeParameters()->Get(i); auto gpd = LoadDecl(index); CJC_NULLPTR_CHECK(gpd); gpd->outerDecl = &decl; genericNode->typeParameters.emplace_back(std::move(gpd)); } return genericNode; } void ASTLoader::ASTLoaderImpl::LoadDeclBasicInfo(const PackageFormat::Decl& decl, Decl& astDecl) { astDecl.identifier = decl.identifier()->str(); astDecl.exportId = decl.exportId()->str(); astDecl.mangledName = decl.mangledName()->str(); astDecl.fullPackageName = decl.fullPkgName()->str(); astDecl.moduleName = package->moduleName()->str(); auto pos = LoadPos(decl.identifierPos()); auto endPos = pos; if (pos != INVALID_POSITION && pos != DEFAULT_POSITION) { endPos = endPos + astDecl.identifier.Val().size(); } astDecl.identifier.SetPos(pos, endPos); astDecl.CopyAttrs(GetAttributes(decl)); if (!astDecl.TestAttr(Attribute::FROM_COMMON_PART) || !deserializingCommon) { astDecl.EnableAttr(Attribute::IMPORTED); } // Position is loaded in 'CreateAndLoadBasicInfo'. // Load mangle and hash for incremental compilation. astDecl.rawMangleName = decl.mangledBeforeSema()->str(); CJC_NULLPTR_CHECK(decl.hash()); astDecl.hash.instVar = decl.hash()->instVar(); astDecl.hash.virt = decl.hash()->virt(); astDecl.hash.sig = decl.hash()->sig(); astDecl.hash.srcUse = decl.hash()->srcUse(); astDecl.hash.bodyHash = decl.hash()->bodyHash(); if (auto fullId = decl.genericDecl()) { astDecl.genericDecl = GetDeclFromIndex(fullId); } uoffset_t length = static_cast(decl.annotations()->size()); for (uoffset_t i = 0; i < length; i++) { auto rawAnno = decl.annotations()->Get(i); auto anno = LoadAnnotation(*rawAnno); astDecl.annotations.emplace_back(std::move(anno)); } } OwnedPtr ASTLoader::ASTLoaderImpl::LoadAnnotation(const PackageFormat::Anno& rawAnno) { auto annotation = MakeOwned(); if (rawAnno.kind() == PackageFormat::AnnoKind_Deprecated) { annotation->kind = AnnotationKind::DEPRECATED; } else if (rawAnno.kind() == PackageFormat::AnnoKind_TestRegistration) { annotation->kind = AnnotationKind::ATTRIBUTE; annotation->attrs.emplace_back(TokenKind::IDENTIFIER, "TEST_REGISTER"); } else if (rawAnno.kind() == PackageFormat::AnnoKind_Frozen) { annotation->kind = AnnotationKind::FROZEN; } else if (rawAnno.kind() == PackageFormat::AnnoKind_JavaMirror) { annotation->kind = AnnotationKind::JAVA_MIRROR; } else if (rawAnno.kind() == PackageFormat::AnnoKind_JavaImpl) { annotation->kind = AnnotationKind::JAVA_IMPL; } else if (rawAnno.kind() == PackageFormat::AnnoKind_ObjCMirror) { annotation->kind = AnnotationKind::OBJ_C_MIRROR; } else if (rawAnno.kind() == PackageFormat::AnnoKind_ObjCImpl) { annotation->kind = AnnotationKind::OBJ_C_IMPL; } else if (rawAnno.kind() == PackageFormat::AnnoKind_ForeignName) { annotation->kind = AnnotationKind::FOREIGN_NAME; } else if (rawAnno.kind() == PackageFormat::AnnoKind_Custom) { annotation->kind = AnnotationKind::CUSTOM; annotation->isCompileTimeVisible = true; } else { InternalError("Unhandled annotation kind."); } CJC_NULLPTR_CHECK(rawAnno.identifier()); annotation->identifier = rawAnno.identifier()->str(); CJC_NULLPTR_CHECK(rawAnno.args()); uoffset_t length = static_cast(rawAnno.args()->size()); for (uoffset_t i = 0; i < length; i++) { auto rawArg = rawAnno.args()->Get(i); auto arg = LoadAnnotationArg(*rawArg); annotation->args.emplace_back(std::move(arg)); } return annotation; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadAnnotationArg(const PackageFormat::AnnoArg& rawArg) { auto arg = MakeOwned(); arg->name = rawArg.name()->str(); arg->expr = LoadExpr(rawArg.expr()); if (arg->expr->astKind != ASTKind::LIT_CONST_EXPR) { InternalError("Kind of deserialized argument must be constant literal."); } return arg; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadVarDecl(const PackageFormat::Decl& decl, int64_t declIndex) { auto varDecl = CreateAndLoadBasicInfo(decl, declIndex); auto info = decl.info_as_VarInfo(); CJC_NULLPTR_CHECK(info); varDecl->isVar = info->isVar(); varDecl->isMemberParam = info->isMemberParam(); varDecl->isConst = info->isConst(); LoadDeclBasicInfo(decl, *varDecl); if ((importSrcCode && !varDecl->TestAttr(AST::Attribute::FROM_COMMON_PART)) || deserializingCommon) { varDecl->initializer = LoadExpr(info->initializer()); } if (varDecl->initializer && IsGlobalOrMember(*varDecl)) { // Only collect global and member decls. importNonGenericSrcFuncDecls.emplace_back(varDecl.get()); varDecl->EnableAttr(Attribute::SRC_IMPORTED); } if (IsGlobalOrMember(*varDecl)) { // Global or member variable may be referenced by other package. AddDeclToImportedPackage(*varDecl); } return varDecl; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadVarWithPatternDecl(const PackageFormat::Decl& decl, int64_t declIndex) { auto vpd = CreateAndLoadBasicInfo(decl, declIndex); auto info = decl.info_as_VarWithPatternInfo(); CJC_NULLPTR_CHECK(info); vpd->isVar = info->isVar(); vpd->isConst = info->isConst(); LoadDeclBasicInfo(decl, *vpd); vpd->initializer = LoadExpr(info->initializer()); vpd->irrefutablePattern = LoadPattern(*info->irrefutablePattern()); return vpd; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadPropDecl(const PackageFormat::Decl& decl, int64_t declIndex) { auto propDecl = CreateAndLoadBasicInfo(decl, declIndex); auto info = decl.info_as_PropInfo(); CJC_NULLPTR_CHECK(info); propDecl->isVar = info->isMutable(); LoadDeclBasicInfo(decl, *propDecl); // setters and getters auto length = static_cast(info->setters()->size()); for (uoffset_t i = 0; i < length; i++) { auto index = info->setters()->Get(i); auto setter = LoadDecl(index); propDecl->setters.emplace_back(std::move(setter)); } length = static_cast(info->getters()->size()); for (uoffset_t i = 0; i < length; i++) { auto index = info->getters()->Get(i); auto getter = LoadDecl(index); propDecl->getters.emplace_back(std::move(getter)); } return propDecl; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadFuncParamList(const PackageFormat::FuncParamList* funcParamList) { auto ret = MakeOwned(); if (funcParamList == nullptr) { return ret; } uoffset_t length = static_cast(funcParamList->params()->size()); for (uoffset_t i = 0; i < length; i++) { auto index = funcParamList->params()->Get(i); auto param = LoadDecl(index); index = funcParamList->desugars()->Get(i); if (index != INVALID_FORMAT_INDEX) { param->desugarDecl = LoadDecl(index); } ret->params.emplace_back(std::move(param)); } return ret; }; OwnedPtr ASTLoader::ASTLoaderImpl::LoadFuncParam(const PackageFormat::Decl& decl, int64_t declIndex) { auto funcParam = CreateAndLoadBasicInfo(decl, declIndex); LoadDeclBasicInfo(decl, *funcParam); auto info = decl.info_as_ParamInfo(); CJC_NULLPTR_CHECK(info); funcParam->isNamedParam = info->isNamedParam(); funcParam->isMemberParam = info->isMemberParam(); funcParam->assignment = LoadExpr(info->defaultVal()); return funcParam; } void ASTLoader::ASTLoaderImpl::LoadFuncDeclAdvancedInfo(const PackageFormat::Decl& decl, FuncDecl& funcDecl) { auto info = decl.info_as_FuncInfo(); CJC_NULLPTR_CHECK(info); funcDecl.overflowStrategy = STRATEGY_RMAP.at(info->overflowPolicy()); funcDecl.isInline = info->isInline() && cjoManager.GetCanInline(); funcDecl.isFastNative = info->isFastNative(); funcDecl.isConst = info->isConst(); funcDecl.op = OP_KIND_RMAP.at(info->op()); // TokenKind op of operator overload function. auto& annos = funcDecl.annotations; funcDecl.isFrozen = Utils::In(annos, [](const auto& anno) { return anno->kind == AnnotationKind::FROZEN; }); CJC_NULLPTR_CHECK(info->funcBody()); funcDecl.funcBody = LoadFuncBody(*info->funcBody(), funcDecl.isInline); auto& funcBody = funcDecl.funcBody; funcBody->funcDecl = &funcDecl; funcBody->generic = LoadGeneric(funcDecl, decl.generic()); // If 'funcBody->body' means funcBody implementation is loaded. if (importSrcCode && funcBody->body) { if (!funcBody->generic && IsGlobalOrMember(funcDecl)) { importNonGenericSrcFuncDecls.emplace_back(&funcDecl); // Only collect global and member decls. } funcDecl.EnableAttr(Attribute::SRC_IMPORTED); } SetOuterFunctionDecl(funcDecl); if (funcDecl.funcBody->paramLists.empty()) { // For LSP usage, this may be empty. return; } // Update param functions' references. for (auto& param : funcDecl.funcBody->paramLists[0]->params) { if (!param->desugarDecl) { continue; } param->desugarDecl->ownerFunc = &funcDecl; param->desugarDecl->isFrozen = funcDecl.isFrozen; // If owner function is non-generic global/member function, collecting source imported param function. bool needCollect = importSrcCode && !funcDecl.TestAttr(Attribute::GENERIC) && IsGlobalOrMember(funcDecl) && param->desugarDecl->funcBody->body; if (needCollect) { (void)importNonGenericSrcFuncDecls.emplace_back(param->desugarDecl.get()); } } } void ASTLoader::ASTLoaderImpl::LoadEnumDeclAdvancedInfo(const PackageFormat::Decl& decl, EnumDecl& enumDecl) { auto info = decl.info_as_EnumInfo(); CJC_NULLPTR_CHECK(info); enumDecl.hasArguments = info->hasArguments(); enumDecl.hasEllipsis = info->nonExhaustive(); if (enumDecl.hasEllipsis) { enumDecl.ellipsisPos = LoadPos(info->ellipsisPos()); } // Body. auto length = info->body()->size(); for (uint32_t i = 0; i < length; i++) { auto index = static_cast(info->body()->Get(i)); auto d = LoadDecl(index); SetOuterDeclForMemberDecl(*d, enumDecl); if (d->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { enumDecl.constructors.emplace_back(std::move(d)); } else { enumDecl.members.emplace_back(std::move(d)); } } } static void LoadAnnoTargets(const PackageFormat::ClassInfo& info, Annotation& anno) { auto anno1 = info.annoTargets(); constexpr AnnotationTargetT allTargetsEnabled{0xffff}; constexpr unsigned char allTargetsSerialised{0xff}; if (anno1 == allTargetsSerialised) { anno.target = allTargetsEnabled; return; } auto anno2 = info.annoTargets2(); constexpr unsigned anno1Bits = CHAR_BIT - 1; anno.target = static_cast(static_cast(anno2) << anno1Bits) | anno1; } void ASTLoader::ASTLoaderImpl::LoadInheritableDeclAdvancedInfo(const PackageFormat::Decl& decl, InheritableDecl& id) { auto& members = id.GetMemberDecls(); if (id.TestAttr(Attribute::IS_ANNOTATION)) { auto ann = MakeOwned(); ann->kind = AnnotationKind::ANNOTATION; auto info = decl.info_as_ClassInfo(); CJC_NULLPTR_CHECK(info); LoadAnnoTargets(*info, *ann); ann->runtimeVisible = info->runtimeVisible(); (void)id.annotations.emplace_back(std::move(ann)); } auto body = id.astKind == ASTKind::CLASS_DECL ? decl.info_as_ClassInfo()->body() : id.astKind == ASTKind::INTERFACE_DECL ? decl.info_as_InterfaceInfo()->body() : id.astKind == ASTKind::ENUM_DECL ? decl.info_as_EnumInfo()->body() : id.astKind == ASTKind::STRUCT_DECL ? decl.info_as_StructInfo()->body() : id.astKind == ASTKind::EXTEND_DECL ? decl.info_as_ExtendInfo()->body() : nullptr; CJC_NULLPTR_CHECK(body); for (uoffset_t i = 0; i < body->size(); i++) { auto index = body->Get(i); members.emplace_back(LoadDecl(index)); SetOuterDeclForMemberDecl(*members[i], id); } } template OwnedPtr ASTLoader::ASTLoaderImpl::LoadNominalDecl(const PackageFormat::Decl& decl, int64_t declIndex) { auto astDecl = CreateAndLoadBasicInfo(decl, declIndex); SetDeclBody(*astDecl); LoadDeclBasicInfo(decl, *astDecl); AddDeclToImportedPackage(*astDecl); // Load unique information for different types. if constexpr (std::is_same_v) { LoadEnumDeclAdvancedInfo(decl, *astDecl); } else if constexpr (std::is_same_v) { LoadFuncDeclAdvancedInfo(decl, *astDecl); } else { LoadInheritableDeclAdvancedInfo(decl, *astDecl); } if constexpr (std::is_base_of::value) { astDecl->generic = LoadGeneric(*astDecl, decl.generic()); } return astDecl; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadTypeAliasDecl(const PackageFormat::Decl& decl, int64_t declIndex) { auto tad = CreateAndLoadBasicInfo(decl, declIndex); LoadDeclBasicInfo(decl, *tad); AddDeclToImportedPackage(*tad); tad->generic = LoadGeneric(*tad, decl.generic()); return tad; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadBuiltInDecl(const PackageFormat::Decl& decl, int64_t declIndex) { auto info = decl.info_as_BuiltInInfo(); CJC_NULLPTR_CHECK(info); auto bid = CreateAndLoadBasicInfo(decl, declIndex, BUILTIN_TYPE_RMAP.at(info->builtInType())); LoadDeclBasicInfo(decl, *bid); AddDeclToImportedPackage(*bid); bid->generic = LoadGeneric(*bid, decl.generic()); return bid; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadGenericParamDecl(const PackageFormat::Decl& decl, int64_t declIndex) { auto gpd = CreateAndLoadBasicInfo(decl, declIndex); LoadDeclBasicInfo(decl, *gpd); if (!gpd->exportId.empty()) { // Only add reference for the decl which have valid export id. AddDeclToImportedPackage(*gpd); } return gpd; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadInvalidDecl(const PackageFormat::Decl& decl) { diag.DiagnoseRefactor(DiagKindRefactor::package_unsupported_load, DEFAULT_POSITION, "declaration"); auto invalidDecl = MakeOwned(INVALID_POSITION); LoadDeclBasicInfo(decl, *invalidDecl); LoadNodePos(decl, *invalidDecl); return invalidDecl; } // Load Decl from saved binary according to different decl kind. It should be noted that the load order should be // exactly the same as the save order, which is important to reproduce the ty vector. Always add to decl pointer to // loaded decls once the decl is created. template OwnedPtr ASTLoader::ASTLoaderImpl::LoadDecl(FormattedIndex declIndex); // For expr loading. template OwnedPtr ASTLoader::ASTLoaderImpl::LoadDecl(FormattedIndex declIndex) { OwnedPtr ret; auto decl = GetFormatDeclByIndex(declIndex); auto foundHandler = declLoaderMap.find(decl->kind()); if (foundHandler != declLoaderMap.end()) { ret = foundHandler->second(this, *decl, declIndex); } else { ret = LoadInvalidDecl(*decl); } // Caller guarantees casting type is matched. return OwnedPtr(StaticCast(ret.release())); } cangjie_compiler-1.0.7/src/Modules/ASTSerialization/ASTLoaderImpl.h000066400000000000000000000510301510705540100251430ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares theAST Loader related classes. */ #ifndef CANGJIE_MODULES_ASTSERIALIZATION_ASTLOADER_IMPL_H #define CANGJIE_MODULES_ASTSERIALIZATION_ASTLOADER_IMPL_H #include "flatbuffers/ModuleFormat_generated.h" #include "cangjie/AST/ASTCasting.h" #include "cangjie/Modules/ASTSerialization.h" #include "cangjie/Modules/CjoManager.h" #include "ASTSerializeUtils.h" namespace Cangjie { class ASTLoader::ASTLoaderImpl { public: ASTLoaderImpl(std::vector&& data, const std::string& fullPackageName, TypeManager& typeManager, const CjoManager& cjoManager, const GlobalOptions& opts) : importedPackageName(fullPackageName), data(std::move(data)), typeManager(typeManager), diag(cjoManager.GetDiag()), sourceManager(diag.GetSourceManager()), cjoManager(cjoManager), opts(opts) { InitializeTypeLoader(); } ~ASTLoaderImpl() { package = nullptr; curPackage = nullptr; } OwnedPtr LoadPackageDependencies(); void LoadPackageDecls(); // Add for CJMP void PreloadCommonPartOfPackage(AST::Package& pkg); std::vector> LoadImportSpecs(const PackageFormat::Imports* imports); std::string PreReadAndSetPackageName(); std::vector ReadFileNames() const; std::unordered_set LoadCachedTypeForPackage( const AST::Package& sourcePackage, const std::map>& mangledName2DeclMap); std::string LoadPackageDepInfo(); void LoadRefs(); void SetImportSourceCode(bool enable) { importSrcCode = enable; } std::string importedPackageName; Ptr LoadType(FormattedIndex type); // A flag to avoid conflicts when we are reusing the AST serialiser from CHIR bool isChirNow = false; private: friend ASTLoader; std::vector data; TypeManager& typeManager; DiagnosticEngine& diag; SourceManager& sourceManager; const CjoManager& cjoManager; const GlobalOptions& opts; const PackageFormat::Package* package{nullptr}; // Store dependent package names in loading index order. std::vector importedFullPackageNames; // Loaded all types and decls std::vector> allTypes; // When exporting ast, we use fileOffest in each package and index is offset in vector puls 1. std::vector allFileIds; std::unordered_map> idToFileMap; std::unordered_map> allLoadedDecls; std::unordered_map> allLoadedExprs; bool HasInlineDefaultParamFunc(const PackageFormat::Decl& decl) const; std::unordered_set> allDummyExprs; std::vector> importNonGenericSrcFuncDecls; // Only collect global and member decls. Ptr curPackage{nullptr}; bool importSrcCode{false}; bool deserializingCommon{false}; // Define the context status for loading type cache of incremental compilation. bool isLoadCache{false}; class CacheLoadingStatus { public: explicit CacheLoadingStatus(bool& v) : status(v) { status = true; } ~CacheLoadingStatus() { status = false; } private: bool& status; }; // Verify legality of data. bool VerifyForData(const std::string& id); // Methods for loading cache during incremental compilation. void PrepareForLoadTypeCache(const AST::Package& pkg); void ClearInstantiatedCache(const std::vector& instantiatedDeclIndexes, std::unordered_set& needRemoved); void CollectRemovedDecls(const PackageFormat::Decl& decl, std::unordered_set& needRemoved) const; void CollectRemovedDefaultImpl( const AST::Decl& astDecl, const PackageFormat::Decl& decl, std::unordered_set& needRemoved) const; template void LoadDesugarDecl(DeclT& decl, uoffset_t index); // Load super class type or super interface types. void LoadInheritedTypes(const PackageFormat::Decl& decl, AST::InheritableDecl& id); void LoadGenericConstraintsRef(const PackageFormat::Generic* genericRef, Ptr generic); template void LoadNominalDeclRef(const PackageFormat::Decl& decl, DeclT& astDecl); void LoadTypeAliasDeclRef(const PackageFormat::Decl& decl, AST::TypeAliasDecl& tad); void LoadDeclDependencies(const PackageFormat::Decl& decl, AST::Decl& astDecl); void LoadDeclRefs(const PackageFormat::Decl& declObj, AST::Decl& decl); OwnedPtr LoadFuncParam(const PackageFormat::Decl& decl, int64_t declIndex); OwnedPtr LoadPropDecl(const PackageFormat::Decl& decl, int64_t declIndex); OwnedPtr LoadVarDecl(const PackageFormat::Decl& decl, int64_t declIndex); OwnedPtr LoadVarWithPatternDecl(const PackageFormat::Decl& decl, int64_t declIndex); template OwnedPtr LoadNominalDecl(const PackageFormat::Decl& decl, int64_t declIndex); OwnedPtr LoadTypeAliasDecl(const PackageFormat::Decl& decl, int64_t declIndex); OwnedPtr LoadBuiltInDecl(const PackageFormat::Decl& decl, int64_t declIndex); OwnedPtr LoadGenericParamDecl(const PackageFormat::Decl& decl, int64_t declIndex); OwnedPtr LoadInvalidDecl(const PackageFormat::Decl& decl); OwnedPtr LoadGeneric(AST::Decl& decl, const PackageFormat::Generic* generic); OwnedPtr LoadFuncBody( const PackageFormat::FuncBody& body, bool isInline = false, bool isConst = true); void LoadDeclBasicInfo(const PackageFormat::Decl& decl, AST::Decl& astDecl); void LoadFuncDeclAdvancedInfo(const PackageFormat::Decl& decl, AST::FuncDecl& funcDecl); void LoadEnumDeclAdvancedInfo(const PackageFormat::Decl& decl, AST::EnumDecl& enumDecl); void LoadInheritableDeclAdvancedInfo(const PackageFormat::Decl& decl, AST::InheritableDecl& id); void AddDeclToImportedPackage(AST::Decl& decl); // Get decl pointer according to DeclId obtained from flatbuffers. Ptr GetDeclFromIndex(const PackageFormat::FullId* fullId); template OwnedPtr CreateAndLoadBasicInfo(const FormatT& nodeObj, int64_t index, Args&&... args) { auto node = MakeOwned(std::forward(args)...); if (index != INVALID_FORMAT_INDEX) { if constexpr (std::is_base_of::value) { node->overflowStrategy = static_cast(nodeObj.overflowPolicy()); (void)allLoadedExprs.emplace(index, node.get()); } else if constexpr (std::is_base_of::value) { (void)allLoadedDecls.emplace(index, node.get()); } } LoadNodePos(nodeObj, *node); return node; } template void LoadNodePos(const FormatT& nodeObj, AST::Node& node) { node.begin = LoadPos(nodeObj.begin()); node.end = LoadPos(nodeObj.end()); } static inline AST::AttributePack GetAttributes(const flatbuffers::Vector* rawAttrs) { std::vector> bitSets(rawAttrs->size()); for (uoffset_t i = 0; i < rawAttrs->size(); ++i) { bitSets[i] = rawAttrs->Get(i); } return AST::AttributePack(bitSets); } static inline AST::AttributePack GetAttributes(const PackageFormat::Decl& decl) { return GetAttributes(decl.attributes()); } inline const PackageFormat::Decl* GetFormatDeclByIndex(FormattedIndex declIndex) const { // NOTE: Serialized 'declIndex' is vector offset plus 1. CJC_ASSERT(declIndex != INVALID_FORMAT_INDEX); return package->allDecls()->Get(static_cast(declIndex - 1)); } inline const PackageFormat::Expr* GetFormatExprByIndex(FormattedIndex exprIndex) const { // NOTE: Serialized FormattedIndex 'exprIndex' is vector offset plus 1. CJC_ASSERT(exprIndex != INVALID_FORMAT_INDEX); return package->allExprs()->Get(static_cast(exprIndex - 1)); } inline static bool IsTypeDecl(const PackageFormat::Decl& decl) { return Utils::In(decl.kind(), {PackageFormat::DeclKind_StructDecl, PackageFormat::DeclKind_EnumDecl, PackageFormat::DeclKind_ClassDecl, PackageFormat::DeclKind_InterfaceDecl, PackageFormat::DeclKind_BuiltInDecl}); } inline bool IgnoreDecl(FormattedIndex offset) const { auto decl = package->allDecls()->Get(static_cast(offset)); auto attr = GetAttributes(*decl); // 1. Ignore main entry function during loading (it is only needed for incremental compilation). // 2. Ignore instantiated decl and non-external, non-implicit used decl when 'importSrcCode' is disabled. // 3. When current is macro package: // a) ignore instantiated decl. NOTE: macro package's instantiated decls will not be used in CodeGen. // b) ignore non-macro decl when interpretation is off. // c) ignore decl which is not macro and not type when interpretation is on. return attr.TestAttr(AST::Attribute::MAIN_ENTRY) || (!importSrcCode && (attr.TestAttr(AST::Attribute::GENERIC_INSTANTIATED) || (attr.TestAttr(AST::Attribute::PRIVATE) && !attr.TestAttr(AST::Attribute::IMPLICIT_USED)))) || (curPackage->isMacroPackage && (attr.TestAttr(AST::Attribute::GENERIC_INSTANTIATED) || (!attr.TestAttr(AST::Attribute::MACRO_FUNC)))); } // Load and construct Decl Node according to declIndex in package. template OwnedPtr LoadDecl(FormattedIndex declIndex); // Load FuncParamList Node. OwnedPtr LoadFuncParamList(const PackageFormat::FuncParamList* funcParamList); // Load Position Node according to Position object. Position LoadPos(const PackageFormat::Position* posObj); void LoadImportContent(AST::ImportContent& content, const PackageFormat::ImportSpec& is) const; std::vector> LoadTypeArgs(const PackageFormat::SemaTy& typeObj); void SetGenericTy(FormattedIndex type, const PackageFormat::SemaTy& typeObj); bool GetPrimitiveTy(FormattedIndex type, const PackageFormat::SemaTy* typeObj); template void SetTypeTy(FormattedIndex type, const PackageFormat::SemaTy& typeObj); OwnedPtr PreLoadImportedPackageNode(); OwnedPtr CreateFileNode( AST::Package& pkg, unsigned int fileID, std::vector>&& imports); void LoadCachedTypeForDecl(const PackageFormat::Decl& decl, AST::Decl& astDecl); using DeclLoaderT = std::function(ASTLoaderImpl*, const PackageFormat::Decl&, int64_t exprIndex)>; std::unordered_map declLoaderMap{ {PackageFormat::DeclKind_FuncParam, &ASTLoaderImpl::LoadFuncParam}, {PackageFormat::DeclKind_PropDecl, &ASTLoaderImpl::LoadPropDecl}, {PackageFormat::DeclKind_VarDecl, &ASTLoaderImpl::LoadVarDecl}, {PackageFormat::DeclKind_VarWithPatternDecl, &ASTLoaderImpl::LoadVarWithPatternDecl}, {PackageFormat::DeclKind_FuncDecl, &ASTLoaderImpl::LoadNominalDecl}, {PackageFormat::DeclKind_StructDecl, &ASTLoaderImpl::LoadNominalDecl}, {PackageFormat::DeclKind_EnumDecl, &ASTLoaderImpl::LoadNominalDecl}, {PackageFormat::DeclKind_InterfaceDecl, &ASTLoaderImpl::LoadNominalDecl}, {PackageFormat::DeclKind_ClassDecl, &ASTLoaderImpl::LoadNominalDecl}, {PackageFormat::DeclKind_ExtendDecl, &ASTLoaderImpl::LoadNominalDecl}, {PackageFormat::DeclKind_GenericParamDecl, &ASTLoaderImpl::LoadGenericParamDecl}, {PackageFormat::DeclKind_TypeAliasDecl, &ASTLoaderImpl::LoadTypeAliasDecl}, {PackageFormat::DeclKind_BuiltInDecl, &ASTLoaderImpl::LoadBuiltInDecl}, }; using TyLoaderT = std::function; std::unordered_map tyLoaderMap; /** Initialize map with function to instantiate template separately. */ void InitializeTypeLoader(); static inline AST::TypeKind GetASTTypeKind(PackageFormat::TypeKind kind) { auto found = TYPE_KIND_RMAP.find(kind); return found != TYPE_KIND_RMAP.end() ? found->second : AST::TypeKind::TYPE_INVALID; } /** * Load and construct Expr Node according to 'exprIndex' in package. * NOTE: Main entrance of 'LoadExpr' is only from 'LoadFuncBody', 'LoadVarDecl' and 'LoadVarWithPatternDecl'. */ template OwnedPtr LoadExpr(FormattedIndex exprIndex); OwnedPtr LoadInvalidExpr(const PackageFormat::Expr& expr); OwnedPtr LoadWildcardExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadPrimitiveTypeExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadReturnExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadJumpExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadMemberAccess(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadRefExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadCallExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadUnaryExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadIncOrDecExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadLitConstExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadBinaryExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadSubscriptExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadAssignExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadArrayExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadPointerExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadTypeConvExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadThrowExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadPerformExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadResumeExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadSpawnExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadArrayLit(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadTupleLit(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadIfExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadTryExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadWhileExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadDoWhileExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadLambdaExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadBlock(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadMatchExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadLetPatternDestructor(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadForInExpr(const PackageFormat::Expr& expr, int64_t exprIndex); OwnedPtr LoadFuncArg(FormattedIndex index); OwnedPtr LoadMatchCase(FormattedIndex index); OwnedPtr LoadMatchCaseOther(FormattedIndex index); using ExprLoaderT = std::function(ASTLoaderImpl*, const PackageFormat::Expr& expr, int64_t exprIndex)>; std::unordered_map exprLoaderMap{ {PackageFormat::ExprKind_WildcardExpr, &ASTLoaderImpl::LoadWildcardExpr}, {PackageFormat::ExprKind_PrimitiveTypeExpr, &ASTLoaderImpl::LoadPrimitiveTypeExpr}, {PackageFormat::ExprKind_ReturnExpr, &ASTLoaderImpl::LoadReturnExpr}, {PackageFormat::ExprKind_JumpExpr, &ASTLoaderImpl::LoadJumpExpr}, {PackageFormat::ExprKind_MemberAccess, &ASTLoaderImpl::LoadMemberAccess}, {PackageFormat::ExprKind_RefExpr, &ASTLoaderImpl::LoadRefExpr}, {PackageFormat::ExprKind_CallExpr, &ASTLoaderImpl::LoadCallExpr}, {PackageFormat::ExprKind_UnaryExpr, &ASTLoaderImpl::LoadUnaryExpr}, {PackageFormat::ExprKind_IncOrDecExpr, &ASTLoaderImpl::LoadIncOrDecExpr}, {PackageFormat::ExprKind_LitConstExpr, &ASTLoaderImpl::LoadLitConstExpr}, {PackageFormat::ExprKind_BinaryExpr, &ASTLoaderImpl::LoadBinaryExpr}, {PackageFormat::ExprKind_SubscriptExpr, &ASTLoaderImpl::LoadSubscriptExpr}, {PackageFormat::ExprKind_AssignExpr, &ASTLoaderImpl::LoadAssignExpr}, {PackageFormat::ExprKind_ArrayExpr, &ASTLoaderImpl::LoadArrayExpr}, {PackageFormat::ExprKind_PointerExpr, &ASTLoaderImpl::LoadPointerExpr}, {PackageFormat::ExprKind_TypeConvExpr, &ASTLoaderImpl::LoadTypeConvExpr}, {PackageFormat::ExprKind_ThrowExpr, &ASTLoaderImpl::LoadThrowExpr}, {PackageFormat::ExprKind_PerformExpr, &ASTLoaderImpl::LoadPerformExpr}, {PackageFormat::ExprKind_ResumeExpr, &ASTLoaderImpl::LoadResumeExpr}, {PackageFormat::ExprKind_SpawnExpr, &ASTLoaderImpl::LoadSpawnExpr}, {PackageFormat::ExprKind_ArrayLit, &ASTLoaderImpl::LoadArrayLit}, {PackageFormat::ExprKind_TupleLit, &ASTLoaderImpl::LoadTupleLit}, {PackageFormat::ExprKind_IfExpr, &ASTLoaderImpl::LoadIfExpr}, {PackageFormat::ExprKind_TryExpr, &ASTLoaderImpl::LoadTryExpr}, {PackageFormat::ExprKind_WhileExpr, &ASTLoaderImpl::LoadWhileExpr}, {PackageFormat::ExprKind_DoWhileExpr, &ASTLoaderImpl::LoadDoWhileExpr}, {PackageFormat::ExprKind_LambdaExpr, &ASTLoaderImpl::LoadLambdaExpr}, {PackageFormat::ExprKind_Block, &ASTLoaderImpl::LoadBlock}, {PackageFormat::ExprKind_MatchExpr, &ASTLoaderImpl::LoadMatchExpr}, {PackageFormat::ExprKind_LetPatternDestructor, &ASTLoaderImpl::LoadLetPatternDestructor}, {PackageFormat::ExprKind_ForInExpr, &ASTLoaderImpl::LoadForInExpr}, }; /** Only can be used after all exprs has been loaded. */ inline Ptr GetExprByIndex(FormattedIndex index) { auto found = allLoadedExprs.find(index); return found != allLoadedExprs.end() ? found->second : nullptr; } /** Main entrance of 'LoadPattern' is only from 'LoadExpr' and 'LoadVarWithPatternDecl' . */ OwnedPtr LoadPattern(const PackageFormat::Pattern& pattern); OwnedPtr LoadConstPattern(const PackageFormat::Pattern& pattern); OwnedPtr LoadWildcardPattern(const PackageFormat::Pattern& pattern); OwnedPtr LoadVarPattern(const PackageFormat::Pattern& pattern); OwnedPtr LoadTuplePattern(const PackageFormat::Pattern& pattern); OwnedPtr LoadTypePattern(const PackageFormat::Pattern& pattern); OwnedPtr LoadEnumPattern(const PackageFormat::Pattern& pattern); OwnedPtr LoadExceptTypePattern(const PackageFormat::Pattern& pattern); OwnedPtr LoadCommandTypePattern(const PackageFormat::Pattern& pattern); void LoadExprRefs(const PackageFormat::Expr& exprObj, AST::Expr& expr); void LoadSubNodeRefs(const PackageFormat::Expr& exprObj, AST::Expr& expr); void LoadSubNodeRefs2(const PackageFormat::Expr& exprObj, AST::Expr& expr); void LoadPatternRefs(const PackageFormat::Pattern& pObj, AST::Pattern& pattern); void LoadMatchCaseRef(FormattedIndex index, AST::MatchCase& mc, AST::Expr& selector); OwnedPtr LoadAnnotation(const PackageFormat::Anno& rawAnno); OwnedPtr LoadAnnotationArg(const PackageFormat::AnnoArg& rawArg); }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Modules/ASTSerialization/ASTSerializeUtils.h000066400000000000000000000101071510705540100260630ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the AST Serialization related definitions. */ #ifndef CANGJIE_MODULES_ASTSERIALIZE_UTILS_H #define CANGJIE_MODULES_ASTSERIALIZE_UTILS_H #include "flatbuffers/ModuleFormat_generated.h" #include "cangjie/AST/Node.h" namespace Cangjie { using TTypeOffset = flatbuffers::Offset; using TDeclOffset = flatbuffers::Offset; using TExprOffset = flatbuffers::Offset; using TAnnoOffset = flatbuffers::Offset; using TAnnoArgOffset = flatbuffers::Offset; using TPatternOffset = flatbuffers::Offset; using TFullIdOffset = flatbuffers::Offset; using TImportsOffset = flatbuffers::Offset; using TFileInfoOffset = flatbuffers::Offset; using TImportSpecOffset = flatbuffers::Offset; using TFuncBodyOffset = flatbuffers::Offset; template using TVectorOffset = flatbuffers::Offset>; using TPosition = PackageFormat::Position; using TDeclHash = PackageFormat::DeclHash; using PackageIndex = int32_t; constexpr PackageIndex INVALID_PACKAGE_INDEX = -1L; constexpr PackageIndex CURRENT_PKG_INDEX = -2L; constexpr PackageIndex PKG_REFERENCE_INDEX = -3L; extern const std::unordered_map ACCESS_LEVEL_MAP; extern const std::unordered_map ACCESS_LEVEL_RMAP; extern const std::unordered_map ACCESS_MODIFIER_MAP; extern const std::unordered_map> ACCESS_MODIFIER_RMAP; extern const std::unordered_map BUILTIN_TYPE_MAP; extern const std::unordered_map BUILTIN_TYPE_RMAP; extern const std::unordered_map STRATEGY_MAP; extern const std::unordered_map STRATEGY_RMAP; extern const std::unordered_map TYPE_KIND_MAP; extern const std::unordered_map TYPE_KIND_RMAP; extern const std::unordered_map OP_KIND_MAP; extern const std::unordered_map OP_KIND_RMAP; extern const std::unordered_map CALL_KIND_MAP; extern const std::unordered_map CALL_KIND_RMAP; extern const std::unordered_map LIT_CONST_KIND_MAP; extern const std::unordered_map LIT_CONST_KIND_RMAP; extern const std::unordered_map STRING_KIND_MAP; extern const std::unordered_map STRING_KIND_RMAP; extern const std::unordered_map FOR_IN_KIND_MAP; extern const std::unordered_map FOR_IN_KIND_RMAP; inline uint32_t GetFileIndexInPkg(const AST::Decl& decl) { if (!decl.curFile || !decl.curFile->curPackage) { return 0; } auto pkg = decl.curFile->curPackage; for (uint32_t i = 0; i < pkg->files.size(); ++i) { if (pkg->files[i].get() == decl.curFile) { return i; } } return 0; } inline bool IsFullInstantiatedDecl(const AST::Decl& decl) { return decl.TestAttr(AST::Attribute::GENERIC_INSTANTIATED) && !decl.TestAttr(AST::Attribute::GENERIC); } inline bool IsExportedMemberFunc(const AST::FuncDecl& fd) { return fd.linkage != Linkage::INTERNAL; } } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Modules/ASTSerialization/ASTWriter.cpp000066400000000000000000002207621510705540100247340ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * This file implements the AST Writer related classes. */ #include "ASTWriterImpl.h" #include #include "flatbuffers/ModuleFormat_generated.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/Version.h" #include "cangjie/AST/Match.h" #include "cangjie/Mangle/ASTMangler.h" #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Mangle/CHIRMangler.h" #include "cangjie/Modules/CjoManager.h" #include "cangjie/Utils/CheckUtils.h" using namespace Cangjie; using namespace AST; namespace Cangjie { const std::unordered_map ACCESS_LEVEL_MAP = { #define ACCESS_LEVEL(AST_KIND, FBS_KIND) {AST::AccessLevel::AST_KIND, PackageFormat::AccessLevel_##FBS_KIND}, #include "Mapping.inc" #undef ACCESS_LEVEL }; const std::unordered_map ACCESS_MODIFIER_MAP = { #define ACCESS_MODIFIER(AST_KIND, FBS_KIND) {TokenKind::AST_KIND, PackageFormat::AccessModifier_##FBS_KIND}, #include "Mapping.inc" #undef ACCESS_MODIFIER }; const std::unordered_map BUILTIN_TYPE_MAP = { #define BUILTIN_TYPE(AST_KIND, FBS_KIND) {AST::BuiltInType::AST_KIND, PackageFormat::BuiltInType_##FBS_KIND}, #include "Mapping.inc" #undef BUILTIN_TYPE }; const std::unordered_map STRATEGY_MAP = { #define OVERFLOW_STRATEGY(AST_KIND, FBS_KIND) {OverflowStrategy::AST_KIND, PackageFormat::OverflowPolicy_##FBS_KIND}, #include "Mapping.inc" #undef OVERFLOW_STRATEGY }; const std::unordered_map TYPE_KIND_MAP = { #define TYPE_KIND(AST_KIND, FBS_KIND) {AST::TypeKind::AST_KIND, PackageFormat::TypeKind_##FBS_KIND}, #include "Mapping.inc" #undef TYPE_KIND }; const std::unordered_map OP_KIND_MAP = { #define OPERATOR_KIND(AST_KIND, FBS_KIND) {TokenKind::AST_KIND, PackageFormat::OperatorKind_##FBS_KIND}, #include "Mapping.inc" #undef OPERATOR_KIND }; const std::unordered_map CALL_KIND_MAP = { #define CALL_KIND(AST_KIND, FBS_KIND) {AST::CallKind::AST_KIND, PackageFormat::CallKind_##FBS_KIND}, #include "Mapping.inc" #undef CALL_KIND }; const std::unordered_map LIT_CONST_KIND_MAP = { #define LIT_CONST_KIND(AST_KIND, FBS_KIND) {AST::LitConstKind::AST_KIND, PackageFormat::LitConstKind_##FBS_KIND}, #include "Mapping.inc" #undef LIT_CONST_KIND }; const std::unordered_map STRING_KIND_MAP = { #define STRING_KIND(AST_KIND, FBS_KIND) {AST::StringKind::AST_KIND, PackageFormat::StringKind_##FBS_KIND}, #include "Mapping.inc" #undef STRING_KIND }; const std::unordered_map FOR_IN_KIND_MAP = { #define FOR_IN_KIND(AST_KIND, FBS_KIND) {AST::ForInKind::AST_KIND, PackageFormat::ForInKind_##FBS_KIND}, #include "Mapping.inc" #undef FOR_IN_KIND }; } // namespace Cangjie namespace { void AppendDecl(std::vector>& decls, Ptr decl) { CJC_NULLPTR_CHECK(decl); if (auto md = DynamicCast(decl); md && md->desugarDecl) { decls.push_back(md->desugarDecl.get()); // Collect desugared main decl for incremental compilation. } else if (auto macroDecl = DynamicCast(decl); macroDecl && macroDecl->desugarDecl) { // Find references in macro scenarios for lsp. decls.push_back(macroDecl->desugarDecl.get()); } else if (auto vpd = DynamicCast(decl); vpd) { // A VarWithPatternDecl is viewed as a collection of VarDecls. Walker walker(vpd->irrefutablePattern.get(), [&decls](Ptr node) { if (node->astKind == ASTKind::VAR_DECL) { decls.push_back(StaticCast(node)); } return VisitAction::WALK_CHILDREN; }); walker.Walk(); } else if (auto fd = DynamicCast(decl); fd == nullptr || fd->identifier != MAIN_INVOKE) { // The following two cases will be exported: // 1. decl is not a FuncDecl; // 2. decl is a FuncDecl but not a mainInvoke function. decls.push_back(decl); } } void CheckForConflictExportId(const std::vector>& decls) { std::unordered_map> exportIdDeclMap; for (auto decl : decls) { // NOTE: ExtendDecl will not be reference by any other expression or type node, so it does not need exportId. // Only global decl and member decls that may be referenced from other package need exportId! if (!decl->TestAttr(Attribute::GLOBAL) || decl->astKind == ASTKind::EXTEND_DECL) { continue; } if (auto found = exportIdDeclMap.find(decl->exportId); found != exportIdDeclMap.end()) { InternalError("Found same exportID when export the package."); return; } exportIdDeclMap[decl->exportId] = decl; for (auto it : decl->GetMemberDeclPtrs()) { if (it->doNotExport) { continue; } if (auto found = exportIdDeclMap.find(it->exportId); found != exportIdDeclMap.end()) { InternalError("Found same exportID when export the package."); return; } exportIdDeclMap[it->exportId] = it; } } } inline bool SaveInlineLabel(const FuncDecl& funcDecl) { // Instantiated decl or decl inside instantiated decl should not export inline condition. return !IsInDeclWithAttribute(funcDecl, Attribute::GENERIC_INSTANTIATED) && funcDecl.isInline; } inline bool IsExternalNorminalDecl(const Decl& decl) { return decl.IsNominalDecl() && decl.linkage != Linkage::INTERNAL; } inline bool ShouldAlwaysExport(const Decl& decl, bool serializingCommon) { // When 'exportAddition' is enabled, we have following rules: // 1. For BCHIR(include const evaluation) type usage, type decl must be exported. // Otherwise, only export external decls. // 2. For an extend in serializingCommon, always export it. return decl.IsExportedDecl() || decl.TestAttr(Attribute::IMPLICIT_USED) || (decl.TestAttr(Attribute::GLOBAL) && decl.linkage != Linkage::INTERNAL) || (serializingCommon && decl.astKind == ASTKind::EXTEND_DECL); } void MangleExportId(Package& pkg) { BaseMangler mangler; mangler.MangleExportId(pkg); } void CollectBodyToQueue(const Decl& decl, std::queue>& queue) { Ptr nodeToBeWalk = nullptr; if (auto vd = DynamicCast(&decl)) { nodeToBeWalk = vd->initializer.get(); } else if (auto fd = DynamicCast(&decl)) { nodeToBeWalk = fd->funcBody->body.get(); } Walker(nodeToBeWalk, [&queue](auto node) { // Collect used global decls, NOTE: type decls are treated as must exported. Only non-types are needed here. if (auto target = node->GetTarget(); target && !target->TestAttr(Attribute::IMPORTED) && target->TestAttr(Attribute::GLOBAL)) { queue.push(target); } return VisitAction::WALK_CHILDREN; }).Walk(); } void CollectFullExportParamDecl(std::vector>& decls, FuncDecl& fd, std::queue>& queue) { if (!Ty::IsTyCorrect(fd.ty)) { return; } // When 'fd''s type is correct, following conditions must fit. CJC_NULLPTR_CHECK(fd.funcBody); CJC_ASSERT(!fd.funcBody->paramLists.empty()); bool fullExport = fd.isConst || fd.isInline || fd.isFrozen || IsDefaultImplementation(fd); if (fullExport) { decls.emplace_back(&fd); CollectBodyToQueue(fd, queue); } for (auto& param : fd.funcBody->paramLists[0]->params) { // If parent function is full exported, default param decl must also exported. if (param->desugarDecl && (param->desugarDecl->isInline || fullExport)) { decls.emplace_back(param->desugarDecl.get()); CollectBodyToQueue(*param->desugarDecl, queue); } } } void CollectInstantiatedTys(Ty* ty, std::unordered_set& tys) { if (!Ty::IsTyCorrect(ty) || ty->typeArgs.empty()) { return; } if (ty->IsNominal()) { tys.emplace(ty); } for (auto it : ty->typeArgs) { CollectInstantiatedTys(it, tys); } } // Collect instantiated types used by type declarations' members. std::unordered_set CollectInstantiations(const Decl& decl) { if (decl.TestAttr(Attribute::GENERIC)) { return {}; } std::unordered_set instTys; if (auto id = DynamicCast(&decl)) { for (auto& it : id->inheritedTypes) { CollectInstantiatedTys(it->ty, instTys); } for (auto it : id->GetMemberDeclPtrs()) { if (!it->TestAttr(Attribute::GENERIC)) { CollectInstantiatedTys(it->ty, instTys); } } } else { CollectInstantiatedTys(decl.ty, instTys); } return instTys; } void CollectInstantiationRecursively(std::unordered_set& instTys) { std::unordered_set visited; std::queue q; for (auto it : instTys) { q.push(it); } while (!q.empty()) { auto ty = q.front(); q.pop(); if (auto [_, success] = visited.emplace(ty); !success) { continue; } auto id = Ty::GetDeclOfTy(ty); CJC_NULLPTR_CHECK(id); std::unordered_set newTys; for (auto& it : id->inheritedTypes) { CollectInstantiatedTys(it->ty, newTys); } for (auto it : id->GetMemberDeclPtrs()) { if (!it->TestAttr(Attribute::GENERIC)) { CollectInstantiatedTys(it->ty, newTys); } } for (auto it : newTys) { instTys.emplace(it); q.push(it); } } } inline Ptr GetCallee(const CallExpr& ce) { return ce.resolvedFunction ? ce.resolvedFunction : ce.baseFunc->GetTarget(); } bool ShouldExportSource(const VarDecl& varDecl) { if (!Ty::IsTyCorrect(varDecl.ty) || !varDecl.initializer || varDecl.TestAttr(Attribute::IMPORTED)) { return false; } // If 'varDecl' is not global variable and: // it does not have outerDecl or it is an instance member variable in generic decl, always export initializer. if (!varDecl.TestAttr(Attribute::GLOBAL) && (!varDecl.outerDecl || IsInstMemberVarInGenericDecl(varDecl))) { // Only export complicated content when flag is enabled. return true; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // For other case, we will export source code for: // 1) public global const variables // 2) public const or frozen Class/Struct(has const or frozen constructor) member variables if (varDecl.outerDecl && varDecl.outerDecl->TestAttr(Attribute::GENERIC_INSTANTIATED)) { return false; // Do not export source code for instantiated decl's static var. } bool isExternal = varDecl.linkage != Linkage::INTERNAL || varDecl.IsExportedDecl(); bool isGlobalVar = varDecl.TestAttr(Attribute::GLOBAL); // Here we assume the outer ClassDecl or StructDecl must be `public` when we check on the member variable bool isMemberVar = (varDecl.TestAttr(Attribute::IN_CLASSLIKE) || varDecl.TestAttr(Attribute::IN_STRUCT)); bool isConstGlobalOrMemberVar = varDecl.isConst && (isGlobalVar || isMemberVar); if (isExternal && (isConstGlobalOrMemberVar || IsMemberVarShouldBeSrcExported(varDecl))) { auto ce = DynamicCast(varDecl.initializer.get()); auto callee = ce ? GetCallee(*ce) : nullptr; bool isCallInstantiatedFunc = callee && IsInDeclWithAttribute(*callee, Attribute::GENERIC_INSTANTIATED); if (!isCallInstantiatedFunc) { return true; } } #endif return false; } void SaveDeclBasicInfo(const DeclInfo& declInfo, PackageFormat::DeclBuilder& dbuilder) { dbuilder.add_identifier(declInfo.name); dbuilder.add_exportId(declInfo.exportId); dbuilder.add_mangledName(declInfo.mangledName); dbuilder.add_fullPkgName(declInfo.fullPackageName); // NOTE: Positions should be alive before 'dbuilder.Finish' is called. dbuilder.add_begin(&declInfo.begin); dbuilder.add_end(&declInfo.end); dbuilder.add_identifierPos(&declInfo.identifierPos); dbuilder.add_attributes(declInfo.attributes); dbuilder.add_type(declInfo.ty); dbuilder.add_isTopLevel(declInfo.isTopLevel); // For incremental compilation. dbuilder.add_mangledBeforeSema(declInfo.mangledBeforeSema); dbuilder.add_hash(&declInfo.hash); dbuilder.add_annotations(declInfo.annotations); // Only add for common part of CJMP if (!declInfo.dependencies.IsNull()) { dbuilder.add_dependencies(declInfo.dependencies); } } // Check whether the decl is exported for common part of CJMP when it is not exported for general package. inline bool CanSkip4CJMP(const Decl& decl, bool serializingCommon) { // Export global func or var, maybe used by global var initializer. if (serializingCommon && !decl.IsNominalDecl() && decl.TestAttr(Attribute::GLOBAL)) { return false; } return true; } } // namespace ASTWriter::ASTWriter(DiagnosticEngine& diag, const std::string& packageDepInfo, const ExportConfig& exportCfg, const CjoManager& cjoManager) { pImpl = std::make_unique(diag, packageDepInfo, exportCfg, cjoManager); } ASTWriter::~ASTWriter() { } void ASTWriter::ExportAST(const PackageDecl& package) const { CJC_NULLPTR_CHECK(pImpl); pImpl->ExportAST(package); } void ASTWriter::PreSaveFullExportDecls(Package& package) const { CJC_NULLPTR_CHECK(pImpl); pImpl->PreSaveFullExportDecls(package); } template TVectorOffset ASTWriter::ASTWriterImpl::GetBody(T& decl) { // Body. std::vector body; // Incr compilation need load ty by cached cjo, so not only cache visible signature bool onlyVisibleSig = !config.exportForIncr && !config.exportContent; // For LSP usage, when decl is not external, ignore all members, only keep the typeDecl it self. if (onlyVisibleSig && decl.TestAttr(AST::Attribute::PRIVATE)) { return builder.CreateVector(body); } for (auto& it : decl.GetMemberDeclPtrs()) { CJC_NULLPTR_CHECK(it); // Because member variables determine the memory layout of a type, all of its member variables should be // stored in cjo as long as the type is externally visible. const bool isInstMemberVar = it->astKind == ASTKind::VAR_DECL && !it->TestAttr(Attribute::STATIC); // For LSP usage, the invalid vpd will exists, we can ignore it. if (it->doNotExport || it->astKind == AST::ASTKind::VAR_WITH_PATTERN_DECL || // For LSP usage, we can ignore invisible members. (onlyVisibleSig && !it->IsExportedDecl())) { continue; } if (decl.astKind == AST::ASTKind::EXTEND_DECL && serializingCommon) { body.push_back(GetDeclIndex(it)); continue; } // Incr compilation need load ty by cached cjo, so still cache internal or inst member var decls if (!config.exportForIncr && !decl.TestAttr(Attribute::COMMON) && !isInstMemberVar && it->linkage == Linkage::INTERNAL) { continue; } body.push_back(GetDeclIndex(it)); } return builder.CreateVector(body); } /** * Update AST attributes related to common/platform, e.g. set FROM_COMMON_PART. */ void ASTWriter::ASTWriterImpl::SetAttributesIfSerializingCommonPartOfPackage(Package &package) { for (auto &file : package.files) { if (file->package && file->package->hasCommon) { serializingCommon = true; break; } } if (!serializingCommon) { return; } std::function)> visitor = [&visitor](const Ptr &node) { switch (node->astKind) { case ASTKind::PACKAGE: { return VisitAction::WALK_CHILDREN; } case ASTKind::FILE: { auto file = StaticAs(node); for (auto &decl : file->decls) { decl->EnableAttr(Attribute::FROM_COMMON_PART); Walker(decl->generic.get(), visitor).Walk(); } return VisitAction::WALK_CHILDREN; } case ASTKind::INTERFACE_DECL: { auto id = StaticAs(node); for (auto &member : id->body->decls) { member->EnableAttr(Attribute::FROM_COMMON_PART); Walker(member.get(), visitor).Walk(); } return VisitAction::SKIP_CHILDREN; } case ASTKind::CLASS_DECL: { auto cd = StaticAs(node); for (auto &member : cd->body->decls) { member->EnableAttr(Attribute::FROM_COMMON_PART); Walker(member.get(), visitor).Walk(); } return VisitAction::SKIP_CHILDREN; } case ASTKind::STRUCT_DECL: { auto sd = StaticAs(node); for (auto& member : sd->body->decls) { member->EnableAttr(Attribute::FROM_COMMON_PART); Walker(member.get(), visitor).Walk(); } return VisitAction::SKIP_CHILDREN; } case ASTKind::ENUM_DECL: { auto ed = StaticAs(node); for (auto& member: ed->members) { member->EnableAttr(Attribute::FROM_COMMON_PART); Walker(member.get(), visitor).Walk(); } for (auto& constructor: ed->constructors) { constructor->EnableAttr(Attribute::FROM_COMMON_PART); } return VisitAction::SKIP_CHILDREN; } case ASTKind::EXTEND_DECL: { auto ed = StaticAs(node); for (auto& member: ed->members) { member->EnableAttr(Attribute::FROM_COMMON_PART); Walker(member.get(), visitor).Walk(); } return VisitAction::SKIP_CHILDREN; } case ASTKind::FUNC_DECL: { auto fd = StaticAs(node); for (auto ¶m : fd->funcBody->paramLists[0]->params) { if (param->desugarDecl) { param->desugarDecl->EnableAttr(Attribute::FROM_COMMON_PART); } } Walker(fd->funcBody->generic.get(), visitor).Walk(); return VisitAction::SKIP_CHILDREN; } case ASTKind::PROP_DECL: { auto pd = StaticAs(node); for (auto &getter : pd->getters) { getter->EnableAttr(Attribute::FROM_COMMON_PART); } for (auto &setter : pd->setters) { setter->EnableAttr(Attribute::FROM_COMMON_PART); } return VisitAction::SKIP_CHILDREN; } case ASTKind::GENERIC: { auto generic = StaticAs(node); for (auto &it : generic->typeParameters) { it->EnableAttr(Attribute::FROM_COMMON_PART); } return VisitAction::SKIP_CHILDREN; } case ASTKind::VAR_WITH_PATTERN_DECL: { auto varDecl = StaticAs(node); varDecl->irrefutablePattern->EnableAttr(Attribute::FROM_COMMON_PART); Walker(varDecl->irrefutablePattern, visitor).Walk(); return VisitAction::SKIP_CHILDREN; } case ASTKind::TUPLE_PATTERN: { auto tuplePattern = StaticAs(node); for (auto& pattern : tuplePattern->patterns) { Walker(pattern, visitor).Walk(); } return VisitAction::SKIP_CHILDREN; } case ASTKind::VAR_PATTERN: { auto varPattern = StaticAs(node); varPattern->varDecl->EnableAttr(Attribute::FROM_COMMON_PART); return VisitAction::SKIP_CHILDREN; } default: return VisitAction::SKIP_CHILDREN; } }; Walker walker(&package, visitor); walker.Walk(); } /** * Pre-save full exporting decls after sema's desugar before generic instantiation. * NOTE: avoid export boxed decl creation. */ void ASTWriter::ASTWriterImpl::PreSaveFullExportDecls(Package& package) { SetAttributesIfSerializingCommonPartOfPackage(package); for (auto& file : package.files) { CJC_NULLPTR_CHECK(file.get()); SaveFileInfo(*file); } // 1. If definition body does not need export, skip decl collection step. // 2. If current package is macro package, should not export any decl's definition. if (!config.exportContent || package.isMacroPackage) { return; } // Since using 'searched' to avoid duplication, we can use vector to collect all decls. // Visiting order of 'searchingQueue' is base on user code's calling stack, it will have stable order. std::vector> fullExportDecls; std::queue> searchingQueue; std::unordered_set> searched; IterateToplevelDecls(package, [&searchingQueue, this](auto& decl) { // External decls, type decls should always be considered as exported (For bchir's expression type usage). if (ShouldAlwaysExport(*decl, serializingCommon)) { searchingQueue.push(decl.get()); } }); while (!searchingQueue.empty()) { auto decl = searchingQueue.front(); searchingQueue.pop(); if (auto [it, success] = searched.emplace(decl); !success) { continue; } // Since signature of public decl must only using external type, we do not need to check type separately. // Collect generic, inline, default toplevel & member decls. if (auto fd = DynamicCast(decl)) { CollectFullExportParamDecl(fullExportDecls, *fd, searchingQueue); } else if (auto pd = DynamicCast(decl)) { auto addToQueue = [&searchingQueue](auto& it) { searchingQueue.push(it.get()); }; std::for_each(pd->getters.begin(), pd->getters.end(), addToQueue); std::for_each(pd->setters.begin(), pd->setters.end(), addToQueue); } else if (auto vd = DynamicCast(decl); vd && ShouldExportSource(*vd)) { CollectBodyToQueue(*decl, searchingQueue); fullExportDecls.emplace_back(decl); } if (decl->TestAttr(Attribute::GLOBAL)) { topLevelDecls.emplace(decl); } if (!IsExternalNorminalDecl(*decl)) { continue; } for (auto& member : decl->GetMemberDecls()) { CJC_NULLPTR_CHECK(member); if (member->linkage != Linkage::INTERNAL) { searchingQueue.emplace(member.get()); } } } if (fullExportDecls.empty()) { return; } MangleExportId(package); // Serialize decls. for (auto decl : fullExportDecls) { (void)GetDeclIndex(decl); } } inline bool ASTWriter::ASTWriterImpl::NeedToExportDecl(Ptr decl) { if (decl->doNotExport) { return false; } // For incremental data or common part, export all toplevel decls. // For normal cases, decl which does not always need to be export or does not be collected during preExport. bool canSkip = !config.exportForIncr && !ShouldAlwaysExport(*decl, serializingCommon) && (topLevelDecls.count(decl.get()) == 0 || decl->linkage == Linkage::INTERNAL) && CanSkip4CJMP(*decl, serializingCommon); if (canSkip) { return false; } return true; } // Collect declarations file by file, so, // all dependent file declarations goes before declaration of file that depend. // NOTE: File dependency defined in specification. void ASTWriter::ASTWriterImpl::DFSCollectFilesDeclarations(Ptr file, std::unordered_set& alreadyVisitedFiles, std::vector>& topLevelDeclsOrdered, std::unordered_set& usedTys) { if (alreadyVisitedFiles.find(file) != alreadyVisitedFiles.end()) { // File was visited as dependency of another file return; } alreadyVisitedFiles.emplace(file); std::vector> topLevelDeclsOfFile; for (auto& decl : file->decls) { if (!NeedToExportDecl(decl)) { continue; } AppendDecl(topLevelDeclsOfFile, decl); // Collecting decls in all dependent files std::unordered_set dependentFiles; for (auto dependency : decl->dependencies) { dependentFiles.emplace(dependency->curFile); } for (auto dependentFile : dependentFiles) { if (alreadyVisitedFiles.find(dependentFile) != alreadyVisitedFiles.end()) { continue; } if (dependentFile->curPackage != file->curPackage) { continue; } DFSCollectFilesDeclarations(dependentFile, alreadyVisitedFiles, topLevelDeclsOrdered, usedTys); } if (config.exportContent) { usedTys.merge(CollectInstantiations(*decl)); } AddCurFile(*decl, file); // For decls added in AD stage which may not have 'curFile'. } std::move(topLevelDeclsOfFile.begin(), topLevelDeclsOfFile.end(), std::back_inserter(topLevelDeclsOrdered)); } // Export external decls of a Package AST to a buffer. void ASTWriter::ASTWriterImpl::ExportAST(const PackageDecl& package) { exportFuncBody = false; // Content can only be saved during 'PreSaveFullExportDecls' step. CJC_NULLPTR_CHECK(package.srcPackage); // 1. Mangle exportId. MangleExportId(*package.srcPackage); // 2. Obtain all topLevelDecl std::vector> topLevelDeclsOrdered; std::unordered_set alreadyVisitedFiles; std::unordered_set usedTys; for (auto& file : package.srcPackage->files) { CJC_NULLPTR_CHECK(file.get()); DFSCollectFilesDeclarations(file.get(), alreadyVisitedFiles, topLevelDeclsOrdered, usedTys); } if (config.exportContent) { // Instantiated tys are only need to be collected when 'config.exportContent' is enabled. CollectInstantiationRecursively(usedTys); } // If error count is not zero, we should not check the exportId. // NOTE: Only lsp or unittest will call current function when error count is not 0, which may contains error. if (diag.GetErrorCount() == 0) { CheckForConflictExportId(topLevelDeclsOrdered); } topLevelDecls.insert(topLevelDeclsOrdered.begin(), topLevelDeclsOrdered.end()); // 3. Serialize decls. // NOTE: All the toplevel and member decls are exported now, no matter they are internal or external. for (auto decl : topLevelDeclsOrdered) { (void)GetDeclIndex(decl); } } void ASTWriter::SetSerializingCommon() { pImpl->SetSerializingCommon(); } void ASTWriter::ASTWriterImpl::SetSerializingCommon() { serializingCommon = true; } void ASTWriter::AST2FB(std::vector& data, const PackageDecl& package) const { CJC_NULLPTR_CHECK(pImpl); pImpl->AST2FB(data, package); } void ASTWriter::ASTWriterImpl::AST2FB(std::vector& data, const PackageDecl& package) { auto cjcVersion = builder.CreateString(CANGJIE_VERSION); auto packageName = builder.CreateString(package.srcPackage->fullPackageName); auto dependencyInfo = builder.CreateString(packageDepInfo); auto vimports = builder.CreateVector(allPackages); auto vfiles = builder.CreateVector(allFiles); auto vfileImports = builder.CreateVector(allFileImports); // Only for common part of CJMP flatbuffers::Offset> vfileInfo; if (serializingCommon) { vfileInfo = builder.CreateVector(allFileInfo); } auto vdecls = builder.CreateVector(allDecls); auto vexprs = builder.CreateVector(allExprs); auto vtypes = builder.CreateVector(allTypes); PackageFormat::PackageKind kind = PackageFormat::PackageKind_Normal; if (package.srcPackage->isMacroPackage) { kind = PackageFormat::PackageKind_Macro; } else if (package.srcPackage->TestAttr(AST::Attribute::TOOL_ADD)) { kind = PackageFormat::PackageKind_Foreign; } else if (package.srcPackage->TestAttr(AST::Attribute::MOCK_SUPPORTED)) { kind = PackageFormat::PackageKind_Mock; } auto access = ACCESS_LEVEL_MAP.at(package.srcPackage->accessible); bool hasModuleName = package.srcPackage->TestAttr(Attribute::TOOL_ADD) && !package.srcPackage->files.empty() && !package.srcPackage->files.front()->decls.empty(); auto moduleName = builder.CreateString(hasModuleName ? package.srcPackage->files.front()->decls.front()->moduleName : ""); PackageFormat::CjoVersion cjoVersion(CJO_MAJOR_VERSION, CJO_MINOR_VERSION, CJO_PATCH_VERSION); auto root = PackageFormat::CreatePackage(builder, cjcVersion, &cjoVersion, packageName, dependencyInfo, vimports, vfiles, vfileImports, vtypes, vdecls, vexprs, INVALID_FORMAT_INDEX, kind, access, moduleName, vfileInfo); FinishPackageBuffer(builder, root); auto size = static_cast(builder.GetSize()); data.resize(size); uint8_t* buf = builder.GetBufferPointer(); std::copy(buf, buf + size, data.begin()); } // Get decl index from savedDeclMap, if not found, save the Decl node. FormattedIndex ASTWriter::ASTWriterImpl::GetDeclIndex(Ptr decl) { if (!decl) { return INVALID_FORMAT_INDEX; } auto found = savedDeclMap.find(decl); if (found != savedDeclMap.end() && preSavedDecls.count(decl) == 0) { return found->second; } else { // need to check whether the decl is topLevel. // NOTE: desugared param decl is not real global decl. bool isGlobal = decl->TestAttr(Attribute::GLOBAL) && !decl->TestAttr(Attribute::HAS_INITIAL); bool isTopLevel = isGlobal || topLevelDecls.count(decl) == 1; return SaveDecl(*decl, isTopLevel); } } // Get full decl index. // 1. if decl is packageDecl, get the index from imported map. // 2. if decl is imported, get the index form imported map. // 3. if decl is not imported, get the index from savedDeclMap. TFullIdOffset ASTWriter::ASTWriterImpl::GetFullDeclIndex(Ptr decl) { if (!decl || decl->doNotExport) { return PackageFormat::CreateFullId(builder, INVALID_PACKAGE_INDEX, builder.CreateString("")); } if (decl->astKind == ASTKind::PACKAGE_DECL) { return PackageFormat::CreateFullId(builder, PKG_REFERENCE_INDEX, builder.CreateString(decl->fullPackageName)); } else if (decl->TestAttr(Attribute::IMPORTED)) { // Get full decl index from imported map. auto pkgIndex = static_cast(SavePackageName(decl->fullPackageName)); // NOTE: FullId using 'int32' to distinguish with current package and invalid references. return PackageFormat::CreateFullId( builder, pkgIndex, builder.CreateString(decl->exportId.empty() ? decl->identifier.Val() : decl->exportId)); } else { auto index = PreSaveDecl(*decl); return PackageFormat::CreateFullId(builder, CURRENT_PKG_INDEX, 0, index); } } flatbuffers::Offset ASTWriter::ASTWriterImpl::SaveFileImports(const File& file) { auto importSpecs = std::vector(); auto [pkgIndex, fileIndex] = GetFileIndex(file.begin.fileID); for (auto& importSpec : file.imports) { if (importSpec->IsImportMulti()) { continue; } auto posBegin = TPosition(fileIndex, pkgIndex, importSpec->begin.line, importSpec->begin.column, false); auto posEnd = TPosition(fileIndex, pkgIndex, importSpec->end.line, importSpec->end.column, false); auto reExport = !importSpec->modifier ? PackageFormat::AccessModifier_Private : ACCESS_MODIFIER_MAP.at(importSpec->modifier->modifier); importSpecs.push_back(PackageFormat::CreateImportSpec(builder, &posBegin, &posEnd, builder.CreateVectorOfStrings(importSpec->content.prefixPaths), builder.CreateString(importSpec->content.identifier.Val()), builder.CreateString(importSpec->content.aliasName.Val()), reExport, importSpec->content.isDecl, importSpec->content.hasDoubleColon)); if (importSpec->IsReExport()) { // If the import package is reExported, it should be stored as used. SavePackageName(cjoManager.GetPackageNameByImport(*importSpec)); } } auto vimportSpecs = builder.CreateVector(importSpecs); PackageFormat::ImportsBuilder importsBuilder(builder); importsBuilder.add_importSpecs(vimportSpecs); return importsBuilder.Finish(); } // Save fileNames, and add to savedFileMap and return its index. void ASTWriter::ASTWriterImpl::SaveFileInfo(const File& file) { // if already saved, just return its index. auto found = savedFileMap.find(file.begin.fileID); if (found == savedFileMap.end()) { // NOTE: current package's fileIndex is vector offset plus 1. FormattedIndex fileIndex = static_cast(allFiles.size()) + 1; if (config.needAbsPath || serializingCommon) { allFiles.push_back(builder.CreateString(file.filePath)); } else { allFiles.push_back(builder.CreateString( FileUtil::JoinPath(file.curPackage->fullPackageName, FileUtil::GetFileName(file.filePath)))); } savedFileMap.emplace(file.begin.fileID, std::make_pair(0, fileIndex)); allFileImports.push_back(SaveFileImports(file)); // Add additional file info for CJMP auto fileID = file.begin.fileID; auto begin = TPosition(fileIndex, 0, file.begin.line, file.begin.column, false); auto end = TPosition(fileIndex, 0, file.end.line, file.end.column, false); allFileInfo.push_back(PackageFormat::CreateFileInfo(builder, fileID, &begin, &end)); } } // Save fileNames, and add to savedFileMap and return its index. std::pair ASTWriter::ASTWriterImpl::GetFileIndex(unsigned int fileId) { auto found = savedFileMap.find(fileId); return found == savedFileMap.end() ? std::make_pair(INVALID_FORMAT_INDEX, INVALID_FORMAT_INDEX) : found->second; } // Save packageName. If already saved, return its index; if not, save it, add to savedPackageMap and return its index. FormattedIndex ASTWriter::ASTWriterImpl::SavePackageName(const std::string& fullPackageName) { CJC_ASSERT(!fullPackageName.empty()); // If already saved, just return its index. auto found = savedPackageMap.find(fullPackageName); if (found != savedPackageMap.end()) { return found->second; } FormattedIndex pkgIndex = static_cast(allPackages.size()); allPackages.push_back(builder.CreateString(fullPackageName)); savedPackageMap.emplace(fullPackageName, pkgIndex); return pkgIndex; } FormattedIndex ASTWriter::SaveType(Ptr pType) const { CJC_NULLPTR_CHECK(pImpl); return pImpl->SaveType(pType); } namespace { template std::optional> TryGetPlatformImplementationTy(const Ptr& pType) { auto ty = StaticCast(pType); if (ty->decl && ty->decl->platformImplementation) { return ty->decl->platformImplementation->ty; } return std::nullopt; } std::optional> TryGetPlatformImplementationTy(const Ptr& pType) { switch (pType->kind) { case TypeKind::TYPE_CLASS: return TryGetPlatformImplementationTy(pType); case TypeKind::TYPE_STRUCT: return TryGetPlatformImplementationTy(pType); case TypeKind::TYPE_ENUM: return TryGetPlatformImplementationTy(pType); default: return std::nullopt; } } } // namespace // Save ty. If already saved, return its index; if not saved, save its // member according to different ty kind, add to savedTypeMap and return // its index. FormattedIndex ASTWriter::ASTWriterImpl::SaveType(Ptr pType) { // Do not use !Ty::IsTyCorrect(pType). if (Ty::IsInitialTy(pType)) { return INVALID_FORMAT_INDEX; } // if ty already saved, just return its index. auto found = savedTypeMap.find(pType); if (found != savedTypeMap.end()) { return found->second; } // Checking if has already saved platform decl auto platformImplTy = TryGetPlatformImplementationTy(pType); if (platformImplTy) { found = savedTypeMap.find(*platformImplTy); if (found != savedTypeMap.end()) { savedTypeMap.emplace(pType, found->second); return found->second; } } // Create a type index BEFORE actually creating the type object // this is needed to handle recursion in type details, like a class using its own pointer for a member. // NOTE: valid index start from 1. FormattedIndex typeIndex = static_cast(allTypes.size()) + 1; allTypes.emplace_back(TTypeOffset()); savedTypeMap.emplace(pType, typeIndex); // Also saving type for platform decl if any if (platformImplTy) { savedTypeMap.emplace(*platformImplTy, typeIndex); } TTypeOffset typeObject; switch (pType->kind) { case TypeKind::TYPE: case TypeKind::TYPE_ENUM: case TypeKind::TYPE_STRUCT: case TypeKind::TYPE_CLASS: case TypeKind::TYPE_INTERFACE: typeObject = SaveNominalTy(*pType); break; case TypeKind::TYPE_POINTER: typeObject = SavePointerTy(*StaticCast(pType)); break; case TypeKind::TYPE_ARRAY: typeObject = SaveArrayTy(*StaticCast(pType)); break; case TypeKind::TYPE_VARRAY: typeObject = SaveVArrayTy(*StaticCast(pType)); break; case TypeKind::TYPE_TUPLE: typeObject = SaveTupleTy(*StaticCast(pType)); break; case TypeKind::TYPE_FUNC: typeObject = SaveFuncTy(*StaticCast(pType)); break; case TypeKind::TYPE_GENERICS: typeObject = SaveGenericsTy(*StaticCast(pType)); break; default: // PrimitiveTy, ThisTy and CStringTy only need store type kind. PackageFormat::SemaTyBuilder tbuilder(builder); tbuilder.add_kind(GetFormatTypeKind(pType->kind)); typeObject = tbuilder.Finish(); break; } // Overwrite the slot with the actual type object. allTypes[static_cast(typeIndex - 1)] = typeObject; return typeIndex; } TTypeOffset ASTWriter::ASTWriterImpl::SaveNominalTy(const Ty& type) { auto declPtr = Ty::GetDeclPtrOfTy(&type); CJC_NULLPTR_CHECK(declPtr); auto fullIdDeclPtr = GetFullDeclIndex(declPtr); std::vector typeArgs; for (auto& it : type.typeArgs) { typeArgs.push_back(SaveType(it)); } auto vtypeArgs = builder.CreateVector(typeArgs); auto info = PackageFormat::CreateCompositeTyInfo(builder, fullIdDeclPtr, Is(type)); PackageFormat::SemaTyBuilder tbuilder(builder); tbuilder.add_kind(GetFormatTypeKind(type.kind)); tbuilder.add_typeArgs(vtypeArgs); tbuilder.add_info_type(PackageFormat::SemaTyInfo_CompositeTyInfo); tbuilder.add_info(info.Union()); return tbuilder.Finish(); } TTypeOffset ASTWriter::ASTWriterImpl::SaveGenericsTy(const GenericsTy& gty) { std::vector uppers; std::set, CmpTyByName> sortedUppers; // Sort for bep. sortedUppers.insert(gty.upperBounds.begin(), gty.upperBounds.end()); for (auto& it : sortedUppers) { uppers.push_back(SaveType(it)); } auto vUppers = builder.CreateVector(uppers); auto decl = GetFullDeclIndex(gty.decl); auto info = PackageFormat::CreateGenericTyInfo(builder, decl, vUppers); PackageFormat::SemaTyBuilder tbuilder(builder); tbuilder.add_kind(GetFormatTypeKind(gty.kind)); tbuilder.add_info_type(PackageFormat::SemaTyInfo_GenericTyInfo); tbuilder.add_info(info.Union()); return tbuilder.Finish(); } TTypeOffset ASTWriter::ASTWriterImpl::SaveFuncTy(const FuncTy& type) { std::vector paramTypes; for (auto& it : type.paramTys) { paramTypes.push_back(SaveType(it)); } auto vParamTypes = builder.CreateVector(paramTypes); // SaveType has side effect (it allocates an offset for the type) // DO NOT put it in another expression FormattedIndex retType = SaveType(type.retTy); auto info = PackageFormat::CreateFuncTyInfo(builder, retType, type.isC, type.hasVariableLenArg); PackageFormat::SemaTyBuilder tbuilder(builder); tbuilder.add_kind(GetFormatTypeKind(type.kind)); tbuilder.add_typeArgs(vParamTypes); tbuilder.add_info_type(PackageFormat::SemaTyInfo_FuncTyInfo); tbuilder.add_info(info.Union()); // AST node's ty will never be closureTy. return tbuilder.Finish(); } TTypeOffset ASTWriter::ASTWriterImpl::SaveTupleTy(const TupleTy& type) { std::vector paramTypes; for (auto& it : type.typeArgs) { paramTypes.push_back(SaveType(it)); } auto vTypeArgs = builder.CreateVector(paramTypes); PackageFormat::SemaTyBuilder tbuilder(builder); tbuilder.add_kind(GetFormatTypeKind(type.kind)); tbuilder.add_typeArgs(vTypeArgs); // AST node's ty will never be closureTy. return tbuilder.Finish(); } TTypeOffset ASTWriter::ASTWriterImpl::SaveArrayTy(const ArrayTy& type) { CJC_ASSERT(!type.typeArgs.empty()); FormattedIndex elemTy = SaveType(type.typeArgs[0]); auto vTypeArgs = builder.CreateVector({elemTy}); auto info = PackageFormat::CreateArrayTyInfo(builder, type.dims); PackageFormat::SemaTyBuilder tbuilder(builder); tbuilder.add_kind(GetFormatTypeKind(type.kind)); tbuilder.add_typeArgs(vTypeArgs); tbuilder.add_info_type(PackageFormat::SemaTyInfo_ArrayTyInfo); tbuilder.add_info(info.Union()); return tbuilder.Finish(); } TTypeOffset ASTWriter::ASTWriterImpl::SaveVArrayTy(const VArrayTy& type) { CJC_ASSERT(!type.typeArgs.empty()); FormattedIndex elemTy = SaveType(type.typeArgs[0]); auto vTypeArgs = builder.CreateVector({elemTy}); auto info = PackageFormat::CreateArrayTyInfo(builder, type.size); PackageFormat::SemaTyBuilder tbuilder(builder); tbuilder.add_kind(GetFormatTypeKind(type.kind)); tbuilder.add_typeArgs(vTypeArgs); tbuilder.add_info_type(PackageFormat::SemaTyInfo_ArrayTyInfo); tbuilder.add_info(info.Union()); return tbuilder.Finish(); } TTypeOffset ASTWriter::ASTWriterImpl::SavePointerTy(const PointerTy& type) { CJC_ASSERT(!type.typeArgs.empty()); FormattedIndex elemTy = SaveType(type.typeArgs[0]); auto vTypeArgs = builder.CreateVector({elemTy}); PackageFormat::SemaTyBuilder tbuilder(builder); tbuilder.add_kind(GetFormatTypeKind(type.kind)); tbuilder.add_typeArgs(vTypeArgs); return tbuilder.Finish(); } // Save FuncParamList. flatbuffers::Offset ASTWriter::ASTWriterImpl::SaveFuncParamList( const FuncParamList& paramList) { std::vector params; std::vector desugars; for (auto& it : paramList.params) { params.push_back(GetDeclIndex(it.get())); desugars.push_back(GetDeclIndex(it->desugarDecl.get())); } auto vParamList = builder.CreateVector(params); auto vDesugarList = builder.CreateVector(desugars); PackageFormat::FuncParamListBuilder dbuilder(builder); dbuilder.add_params(vParamList); dbuilder.add_desugars(vDesugarList); return dbuilder.Finish(); } // Save Generic. flatbuffers::Offset ASTWriter::ASTWriterImpl::SaveGeneric(const Decl& decl) { // 1. Enum constructor and other constructor must not have generic. // 2. Partial instantiated decl's 'generic'can be ignored. See 'SaveDecl' bool ignored = decl.TestAnyAttr(Attribute::ENUM_CONSTRUCTOR, Attribute::CONSTRUCTOR) || decl.TestAttr(Attribute::GENERIC_INSTANTIATED, Attribute::GENERIC); auto generic = ignored ? nullptr : decl.GetGeneric(); if (generic == nullptr) { return {}; } // Export generic type parameters if existing. std::vector typeParameters; std::vector> constraints; for (auto& gpd : generic->typeParameters) { auto gty = DynamicCast(gpd->ty); // When 'gty' is valid, and its parent decl is a local function, using generic type decl, // otherwise keep 'gpd' itself. CJC_NULLPTR_CHECK(gpd->outerDecl); typeParameters.push_back(GetDeclIndex(gty && !IsGlobalOrMember(*gpd->outerDecl) ? gty->decl : gpd.get())); } for (auto& constraint : generic->genericConstraints) { CJC_NULLPTR_CHECK(constraint); std::vector uppers; for (auto& upper : constraint->upperBounds) { CJC_NULLPTR_CHECK(upper); uppers.emplace_back(SaveType(upper->ty)); } constraint->ty = constraint->type->ty; // Sync ty to re-use 'PackNodeInfo'. auto info = PackNodeInfo(*constraint); auto vUppers = builder.CreateVector(uppers); constraints.emplace_back(PackageFormat::CreateConstraint(builder, &info.begin, &info.end, info.ty, vUppers)); } auto vTypeParameters = builder.CreateVector(typeParameters); auto vConstraints = builder.CreateVector>(constraints); PackageFormat::GenericBuilder dbuilder(builder); dbuilder.add_typeParameters(vTypeParameters); dbuilder.add_constraints(vConstraints); return dbuilder.Finish(); } void ASTWriter::SetIsChirNow(bool isChirNow) { CJC_NULLPTR_CHECK(pImpl); pImpl->isChirNow = isChirNow; } TDeclOffset ASTWriter::ASTWriterImpl::SaveParamDecl(const FuncParam& paramDecl, const DeclInfo& declInfo) { FormattedIndex defaultVal = INVALID_FORMAT_INDEX; if (paramDecl.assignment && (paramDecl.TestAttr(Attribute::TOOL_ADD) || paramDecl.assignment->isConst || Is(paramDecl.assignment.get()))) { defaultVal = SaveExpr(*paramDecl.assignment); } auto info = PackageFormat::CreateParamInfo(builder, paramDecl.isNamedParam, paramDecl.isMemberParam, defaultVal); PackageFormat::DeclBuilder dbuilder(builder); SaveDeclBasicInfo(declInfo, dbuilder); dbuilder.add_kind(PackageFormat::DeclKind_FuncParam); dbuilder.add_info_type(PackageFormat::DeclInfo_ParamInfo); dbuilder.add_info(info.Union()); return dbuilder.Finish(); } TDeclOffset ASTWriter::ASTWriterImpl::SavePropDecl(const PropDecl& propDecl, const DeclInfo& declInfo) { flatbuffers::Offset info; if (!config.compileCjd) { // Global variable initializers should be dropped too, they are // implemented in the global constructor. // add setters and getters std::vector setBody; for (auto& it : propDecl.setters) { setBody.push_back(GetDeclIndex(it.get())); } auto setters = builder.CreateVector(setBody); std::vector getBody; for (auto& it : propDecl.getters) { getBody.push_back(GetDeclIndex(it.get())); } auto getters = builder.CreateVector(getBody); info = PackageFormat::CreatePropInfo(builder, propDecl.isConst, propDecl.isVar, setters, getters); } else { info = PackageFormat::CreatePropInfo(builder, propDecl.isConst, propDecl.isVar); } PackageFormat::DeclBuilder dbuilder(builder); SaveDeclBasicInfo(declInfo, dbuilder); dbuilder.add_kind(PackageFormat::DeclKind_PropDecl); dbuilder.add_info_type(PackageFormat::DeclInfo_PropInfo); dbuilder.add_info(info.Union()); return dbuilder.Finish(); } TDeclOffset ASTWriter::ASTWriterImpl::SaveVarDecl(const VarDecl& varDecl, const DeclInfo& declInfo) { bool exportSourceCode = config.exportContent && ShouldExportSource(varDecl); FormattedIndex initializer = exportSourceCode ? SaveExpr(*varDecl.initializer) : INVALID_FORMAT_INDEX; auto info = PackageFormat::CreateVarInfo(builder, varDecl.isVar, varDecl.isConst, varDecl.isMemberParam, initializer); PackageFormat::DeclBuilder dbuilder(builder); SaveDeclBasicInfo(declInfo, dbuilder); dbuilder.add_kind(PackageFormat::DeclKind_VarDecl); dbuilder.add_info_type(PackageFormat::DeclInfo_VarInfo); dbuilder.add_info(info.Union()); return dbuilder.Finish(); } TDeclOffset ASTWriter::ASTWriterImpl::SaveVarWithPatternDecl(const VarWithPatternDecl& vpd, const DeclInfo& declInfo) { // Save 'VarWithPatternDecl' is always local variable. CJC_NULLPTR_CHECK(vpd.initializer); FormattedIndex initializer = SaveExpr(*vpd.initializer); auto pIdx = SavePattern(*vpd.irrefutablePattern); auto info = PackageFormat::CreateVarWithPatternInfo(builder, vpd.isVar, vpd.isConst, pIdx, initializer); PackageFormat::DeclBuilder dbuilder(builder); SaveDeclBasicInfo(declInfo, dbuilder); dbuilder.add_kind(PackageFormat::DeclKind_VarWithPatternDecl); dbuilder.add_info_type(PackageFormat::DeclInfo_VarWithPatternInfo); dbuilder.add_info(info.Union()); return dbuilder.Finish(); } auto ASTWriter::ASTWriterImpl::GetVirtualParamLists(const FuncBody& funcBody) { // FuncParamList. std::vector> paramLists; for (auto& it : funcBody.paramLists) { paramLists.push_back(SaveFuncParamList(*it.get())); } return builder.CreateVector>(paramLists); } TFuncBodyOffset ASTWriter::ASTWriterImpl::SaveFuncBody(const FuncBody& funcBody) { auto fd = funcBody.funcDecl; // Partial instantiated decl's can be ignored. Reason see 'SaveDecl' and 'UNREACHABLE' was set for export. if (fd && fd->TestAttr(Attribute::GENERIC_INSTANTIATED, Attribute::GENERIC)) { std::vector> paramLists; auto dummyList = builder.CreateVector>(paramLists); return PackageFormat::CreateFuncBody(builder, dummyList, INVALID_FORMAT_INDEX, INVALID_FORMAT_INDEX, false, 0); } auto vparamLists = GetVirtualParamLists(funcBody); FormattedIndex retType = funcBody.retType ? SaveType(funcBody.retType->ty) : INVALID_FORMAT_INDEX; // The frozen attribute is passed to a nested function. if (fd && fd->outerDecl && fd->outerDecl->astKind == ASTKind::FUNC_DECL) { auto outerFunc = StaticCast(fd->outerDecl); fd->isFrozen = outerFunc->isFrozen; } // If funcBody is valid, and for following situations need to export func body content: // 1. funcBody of LamdaExpr or inside other FuncDecl/VarDecl; // 2. funcBody of @Frozen modified generic funcDecl; // 3. funcbody of inline function; // 4. funcBody of constant decl; // 5. funcBody of default implementation which is defined in interface. // NOTE: desugared param function has same 'outerDecl' and 'GLOBAL' attribute will its owner function. bool shouldExportBody = config.exportContent && exportFuncBody && (!fd || CanBeSrcExported(*fd)); bool validBody = shouldExportBody && Ty::IsTyCorrect(funcBody.ty) && funcBody.body; auto bodyIdx = validBody ? SaveExpr(*funcBody.body) : INVALID_FORMAT_INDEX; // CaptureKind is need if the 'funcBody' is exported. uint8_t kind = validBody ? static_cast(funcBody.captureKind) : 0; return PackageFormat::CreateFuncBody(builder, vparamLists, retType, bodyIdx, false, kind); } TDeclOffset ASTWriter::ASTWriterImpl::SaveFuncDecl(const FuncDecl& funcDecl, const DeclInfo& declInfo) { auto isInline = SaveInlineLabel(funcDecl); CJC_NULLPTR_CHECK(funcDecl.funcBody); auto body = SaveFuncBody(*funcDecl.funcBody); auto generic = SaveGeneric(funcDecl); auto genericDeclIndex = GetGenericDeclIndex(funcDecl); auto info = PackageFormat::CreateFuncInfo(builder, body, STRATEGY_MAP.at(funcDecl.overflowStrategy), OP_KIND_MAP.at(funcDecl.op), 0, funcDecl.isConst, isInline, funcDecl.isFastNative); PackageFormat::DeclBuilder dbuilder(builder); SaveDeclBasicInfo(declInfo, dbuilder); dbuilder.add_kind(PackageFormat::DeclKind_FuncDecl); if (!generic.IsNull()) { dbuilder.add_generic(generic); } dbuilder.add_genericDecl(genericDeclIndex); dbuilder.add_info_type(PackageFormat::DeclInfo_FuncInfo); dbuilder.add_info(info.Union()); return dbuilder.Finish(); } TDeclOffset ASTWriter::ASTWriterImpl::SaveStructDecl(const StructDecl& structDecl, const DeclInfo& declInfo) { auto structBody = GetBody(structDecl); auto vInterfaceTypes = GetVirtualInterfaces(structDecl); auto generic = SaveGeneric(structDecl); auto genericDeclIndex = GetGenericDeclIndex(structDecl); auto info = PackageFormat::CreateStructInfo(builder, vInterfaceTypes, structBody, 0); PackageFormat::DeclBuilder dbuilder(builder); SaveDeclBasicInfo(declInfo, dbuilder); dbuilder.add_kind(PackageFormat::DeclKind_StructDecl); if (!generic.IsNull()) { dbuilder.add_generic(generic); } dbuilder.add_genericDecl(genericDeclIndex); dbuilder.add_info_type(PackageFormat::DeclInfo_StructInfo); dbuilder.add_info(info.Union()); return dbuilder.Finish(); } TDeclOffset ASTWriter::ASTWriterImpl::SaveEnumDecl(const EnumDecl& enumDecl, const DeclInfo& declInfo) { auto enumBody = GetBody(enumDecl); auto vinterfaceTypes = GetVirtualInterfaces(enumDecl); auto generic = SaveGeneric(enumDecl); auto genericDeclIndex = GetGenericDeclIndex(enumDecl); auto& pos = enumDecl.ellipsisPos; auto [pkgIndex, fileIndex] = GetFileIndex(pos.fileID); TPosition position{fileIndex, pkgIndex, pos.line, pos.column, pos.GetStatus() == PositionStatus::IGNORE}; auto info = PackageFormat::CreateEnumInfo( builder, vinterfaceTypes, enumBody, 0, enumDecl.hasArguments, enumDecl.hasEllipsis, &position); PackageFormat::DeclBuilder dbuilder(builder); SaveDeclBasicInfo(declInfo, dbuilder); dbuilder.add_kind(PackageFormat::DeclKind_EnumDecl); if (!generic.IsNull()) { dbuilder.add_generic(generic); } dbuilder.add_genericDecl(genericDeclIndex); dbuilder.add_info_type(PackageFormat::DeclInfo_EnumInfo); dbuilder.add_info(info.Union()); return dbuilder.Finish(); } TDeclOffset ASTWriter::ASTWriterImpl::SaveInterfaceDecl(const InterfaceDecl& interfaceDecl, const DeclInfo& declInfo) { auto vinterfaceBody = GetBody(interfaceDecl); auto vinterfaceTypes = GetVirtualInterfaces(interfaceDecl); auto generic = SaveGeneric(interfaceDecl); auto genericDeclIndex = GetGenericDeclIndex(interfaceDecl); auto info = PackageFormat::CreateInterfaceInfo(builder, vinterfaceTypes, vinterfaceBody); PackageFormat::DeclBuilder dbuilder(builder); SaveDeclBasicInfo(declInfo, dbuilder); dbuilder.add_kind(PackageFormat::DeclKind_InterfaceDecl); if (!generic.IsNull()) { dbuilder.add_generic(generic); } dbuilder.add_genericDecl(genericDeclIndex); dbuilder.add_info_type(PackageFormat::DeclInfo_InterfaceInfo); dbuilder.add_info(info.Union()); return dbuilder.Finish(); } static void WriteAnnoTargets(PackageFormat::ClassInfoBuilder& builder, AnnotationTargetT targets) { // these manglings are for backward compatibility of .fbs constexpr AnnotationTargetT allTargetsEnabled{0xffff}; constexpr unsigned char allTargetsSerialised{0xff}; if (targets == allTargetsEnabled) { builder.add_annoTargets(allTargetsSerialised); return; } constexpr AnnotationTargetT anno2Mask{0x180}; constexpr AnnotationTargetT anno1Mask{0x7f}; if (auto anno2 = static_cast(anno2Mask & targets); anno2 != 0) { builder.add_annoTargets2(static_cast(anno2 >> (CHAR_BIT - 1))); builder.add_annoTargets(static_cast(anno1Mask & targets)); } else { builder.add_annoTargets(static_cast(targets)); } } TDeclOffset ASTWriter::ASTWriterImpl::SaveClassDecl(const ClassDecl& classDecl, const DeclInfo& declInfo) { auto vclassBody = GetBody(classDecl); auto vinheritedTypes = GetVirtualInterfaces(classDecl); auto generic = SaveGeneric(classDecl); auto genericDeclIndex = GetGenericDeclIndex(classDecl); PackageFormat::ClassInfoBuilder ibuilder(builder); ibuilder.add_body(vclassBody); ibuilder.add_inheritedTypes(vinheritedTypes); if (classDecl.TestAttr(Attribute::IS_ANNOTATION)) { ibuilder.add_isAnno(true); for (auto& ann : classDecl.annotations) { CJC_NULLPTR_CHECK(ann); if (ann->kind == AnnotationKind::ANNOTATION) { WriteAnnoTargets(ibuilder, ann->target); ibuilder.add_runtimeVisible(ann->runtimeVisible); } } } auto info = ibuilder.Finish(); PackageFormat::DeclBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::DeclKind_ClassDecl); SaveDeclBasicInfo(declInfo, dbuilder); if (!generic.IsNull()) { dbuilder.add_generic(generic); } dbuilder.add_genericDecl(genericDeclIndex); dbuilder.add_info_type(PackageFormat::DeclInfo_ClassInfo); dbuilder.add_info(info.Union()); return dbuilder.Finish(); } TDeclOffset ASTWriter::ASTWriterImpl::SaveExtendDecl(const ExtendDecl& extendDecl, const DeclInfo& declInfo) { auto vextendInterfaceTypes = GetVirtualInterfaces(extendDecl); auto vextendMembers = GetBody(extendDecl); auto generic = SaveGeneric(extendDecl); auto genericDeclIndex = GetGenericDeclIndex(extendDecl); auto info = PackageFormat::CreateExtendInfo(builder, vextendInterfaceTypes, vextendMembers); PackageFormat::DeclBuilder dbuilder(builder); SaveDeclBasicInfo(declInfo, dbuilder); dbuilder.add_kind(PackageFormat::DeclKind_ExtendDecl); if (!generic.IsNull()) { dbuilder.add_generic(generic); } dbuilder.add_genericDecl(genericDeclIndex); dbuilder.add_info_type(PackageFormat::DeclInfo_ExtendInfo); dbuilder.add_info(info.Union()); return dbuilder.Finish(); } TDeclOffset ASTWriter::ASTWriterImpl::SaveTypeAliasDecl(const TypeAliasDecl& typeAliasDecl, const DeclInfo& declInfo) { FormattedIndex aliasedTy = SaveType(typeAliasDecl.type->ty); auto generic = SaveGeneric(typeAliasDecl); auto info = PackageFormat::CreateAliasInfo(builder, aliasedTy); PackageFormat::DeclBuilder dbuilder(builder); SaveDeclBasicInfo(declInfo, dbuilder); dbuilder.add_kind(PackageFormat::DeclKind_TypeAliasDecl); if (!generic.IsNull()) { dbuilder.add_generic(generic); } dbuilder.add_info_type(PackageFormat::DeclInfo_AliasInfo); dbuilder.add_info(info.Union()); return dbuilder.Finish(); } TDeclOffset ASTWriter::ASTWriterImpl::SaveBuiltInDecl(const BuiltInDecl& builtInDecl, const DeclInfo& declInfo) { auto generic = SaveGeneric(builtInDecl); auto info = PackageFormat::CreateBuiltInInfo(builder, BUILTIN_TYPE_MAP.at(builtInDecl.type)); PackageFormat::DeclBuilder dbuilder(builder); SaveDeclBasicInfo(declInfo, dbuilder); dbuilder.add_kind(PackageFormat::DeclKind_BuiltInDecl); if (!generic.IsNull()) { dbuilder.add_generic(generic); } dbuilder.add_info_type(PackageFormat::DeclInfo_BuiltInInfo); dbuilder.add_info(info.Union()); return dbuilder.Finish(); } TDeclOffset ASTWriter::ASTWriterImpl::SaveGenericParamDecl(const GenericParamDecl& /* gpd */, const DeclInfo& declInfo) { // In most case, user defined 'GenericParamDecl' referenced in current package, // except for interface's generic type and its generic default function, // their 'GenericParamDecl' may be reference by other package. PackageFormat::DeclBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::DeclKind_GenericParamDecl); SaveDeclBasicInfo(declInfo, dbuilder); return dbuilder.Finish(); } TDeclOffset ASTWriter::ASTWriterImpl::SaveUnsupportDecl(const DeclInfo& declInfo, const Decl& decl) { (void)diag.DiagnoseRefactor( DiagKindRefactor::package_unsupport_save, decl, "declaration", std::string(typeid(decl).name())); PackageFormat::DeclBuilder dbuilder(builder); SaveDeclBasicInfo(declInfo, dbuilder); dbuilder.add_kind(PackageFormat::DeclKind_InvalidDecl); return dbuilder.Finish(); } flatbuffers::Offset> ASTWriter::ASTWriterImpl::SaveAttributes( const AttributePack& attrs ) { std::vector attrVec; for (auto it : attrs.GetRawAttrs()) { (void)attrVec.emplace_back(static_cast(it.to_ullong())); } return builder.CreateVector(attrVec); } bool ASTWriter::ASTWriterImpl::PlannedToBeSerialized(Ptr decl) { return savedDeclMap.find(decl) != savedDeclMap.end(); } /// Collect dependencies on which the current `decl` depends. /// In case if declaration is not exported, its transitive dependencies will be saved. std::vector ASTWriter::ASTWriterImpl::CollectInitializationDependencies( const Decl& decl, std::set visited) { bool wasVisited = visited.find(&decl) != visited.end(); if (wasVisited) { return {}; } visited.insert(&decl); std::vector dependencies; std::vector dependenciesVector; for (auto dependency : decl.dependencies) { if (PlannedToBeSerialized(dependency)) { TFullIdOffset depFullId = GetFullDeclIndex(dependency); dependencies.push_back(depFullId); } else { for (auto transitiveFullId : CollectInitializationDependencies(*dependency, visited)) { dependencies.push_back(transitiveFullId); } } } return dependencies; } // Save decl, add to savedDeclMap and return its index. // It should be noted that: first saved all decls, then save types, to avoid // searching unsaved decls in SaveType. FormattedIndex ASTWriter::ASTWriterImpl::SaveDecl(const Decl& decl, bool isTopLevel) { FormattedIndex declIndex = PreSaveDecl(decl); preSavedDecls.erase(&decl); // Names: identifier, exportId, packageName. auto name = builder.CreateSharedString(decl.identifier.Val()); auto exportId = builder.CreateSharedString(decl.exportId); // The Decl meet one of the following conditions does not need to export 'mangledName': // 1. decl in generic decls. // 2. decl is GenericParamDecl, propDecl, typeAliasDecl,extendDecl // 3. deck is instance member variable. bool ignoreMangledName = IsInDeclWithAttribute(decl, Attribute::GENERIC) || Utils::In(decl.astKind, {ASTKind::GENERIC_PARAM_DECL, ASTKind::PROP_DECL, ASTKind::TYPE_ALIAS_DECL, ASTKind::EXTEND_DECL}) || (decl.astKind == ASTKind::VAR_DECL && !decl.TestAnyAttr(Attribute::STATIC, Attribute::GLOBAL)); bool isInExtendToExportForTest = decl.outerDecl && Is(decl.outerDecl) && !decl.outerDecl->TestAttr(Attribute::IMPORTED) && !decl.outerDecl->TestAttr(Attribute::COMPILER_ADD) && !decl.TestAttr(Attribute::PRIVATE); auto mangledName = builder.CreateSharedString( ignoreMangledName && (!config.exportForTest || !isInExtendToExportForTest) ? "" : decl.mangledName); auto rawMangleName = !config.exportForIncr && decl.TestAttr(Attribute::DEFAULT) && decl.rawMangleName.empty() ? builder.CreateSharedString(ASTMangler(decl.fullPackageName).Mangle(decl)) : builder.CreateSharedString(decl.rawMangleName); auto fullPackageName = builder.CreateSharedString(decl.fullPackageName); // NOTE: Type of partial instantiated member function can be ignored. // Since this kind of member function will never be referenced during 'Sema' step // and will be replaced during instantiation step, we ignore the ty of this kind of decl. auto attrs = decl.GetAttrs(); if (decl.TestAttr(Attribute::GENERIC_INSTANTIATED, Attribute::GENERIC)) { attrs.SetAttr(Attribute::UNREACHABLE, true); // Set 'UNREACHABLE' for export. } if (auto varDecl = DynamicCast(&decl)) { if (varDecl->TestAttr(Attribute::FROM_COMMON_PART) && varDecl->outerDecl && varDecl->outerDecl->TestAttr(Attribute::PLATFORM)) { attrs.SetAttr(Attribute::COMMON, false); attrs.SetAttr(Attribute::FROM_COMMON_PART, false); } if (varDecl->outerDecl && varDecl->outerDecl->TestAttr(Attribute::COMMON)) { bool hasInitializer = varDecl->initializer; attrs.SetAttr(Attribute::INITIALIZED, hasInitializer); if (varDecl->TestAttr(Attribute::STATIC) && varDecl->TestAttr(Attribute::INITIALIZED)) { attrs.SetAttr(Attribute::INITIALIZED, true); } } if (varDecl->TestAttr(Attribute::PLATFORM)) { attrs.SetAttr(Attribute::PLATFORM, false); } } auto type = attrs.TestAttr(Attribute::UNREACHABLE) ? INVALID_FORMAT_INDEX : SaveType(decl.ty); auto begin = decl.GetBegin(); auto end = decl.GetEnd(); auto [pkgIndex, fileIndex] = GetFileIndex(begin.fileID); // When fileIndex is not in savedMap, it means the fileID is for imported package, // and current decl may be or is inside cloned default member decl. // NOTE: cloned default member decl should keep origin 'curFile'. if (fileIndex == INVALID_FORMAT_INDEX) { CJC_ASSERT(decl.curFile && decl.curFile->curPackage); // New non-current package's file must be first found from decl, then expression. fileIndex = GetFileIndexInPkg(decl); // NOTE: Other package's 'pkgIndex' is vector offset plus 1. pkgIndex = SavePackageName(decl.curFile->curPackage->fullPackageName) + 1; // Update file map cache. savedFileMap.emplace(begin.fileID, std::make_pair(pkgIndex, fileIndex)); } TPosition posBegin(fileIndex, pkgIndex, begin.line, begin.column, begin.GetStatus() == PositionStatus::IGNORE); TPosition posEnd(fileIndex, pkgIndex, end.line, end.column, end.GetStatus() == PositionStatus::IGNORE); TPosition identifierPos(fileIndex, pkgIndex, decl.GetIdentifierPos().line, decl.GetIdentifierPos().column, false); TDeclHash declHash(decl.hash.instVar, decl.hash.virt, decl.hash.sig, decl.hash.srcUse, decl.hash.bodyHash); auto attributes = SaveAttributes(attrs); auto isConst = decl.IsConst(); auto annotations = builder.CreateVector(SaveAnnotations(decl)); // The dependencies default is null for compatibility, only may have value for common part of CJMP. flatbuffers::Offset> dependencies; if (serializingCommon) { dependencies = builder.CreateVector(CollectInitializationDependencies(decl, {})); } DeclInfo declInfo{name, exportId, mangledName, rawMangleName, declHash, fullPackageName, posBegin, posEnd, identifierPos, attributes, isConst, type, isTopLevel, annotations, dependencies}; TDeclOffset offset; auto foundWriter = declWriterMap.find(decl.astKind); if (foundWriter != declWriterMap.end()) { offset = foundWriter->second(decl, declInfo); } else { offset = SaveUnsupportDecl(declInfo, decl); } // Overwrite the slot with the actual decl. allDecls[static_cast(declIndex - 1)] = offset; return declIndex; } // `public let x!: Bool = false` // Affect both **member variable** and **primary constructor parameter**. // But separate records will be created for them. // Means there is no global `allSavedAnnotations` map. std::vector ASTWriter::ASTWriterImpl::SaveAnnotations(const Decl& decl) { std::vector annotations; for (auto& annotation : std::as_const(decl.annotations)) { if (annotation->kind == AST::AnnotationKind::DEPRECATED) { auto args = builder.CreateVector(SaveAnnotationArgs(*annotation)); auto serialized = PackageFormat::CreateAnno( builder, PackageFormat::AnnoKind_Deprecated, builder.CreateString(annotation->identifier.Val()), args); annotations.emplace_back(serialized); } else if (annotation->kind == AST::AnnotationKind::ATTRIBUTE) { auto hasTestRegisterAttr = false; for (auto attr : std::as_const(annotation->attrs)) { if (attr == "TEST_REGISTER") { hasTestRegisterAttr = true; break; } } if (hasTestRegisterAttr) { auto serialized = PackageFormat::CreateAnno(builder, PackageFormat::AnnoKind_TestRegistration, builder.CreateString(annotation->identifier.Val()), builder.CreateVector({})); annotations.emplace_back(serialized); } } else if (annotation->kind == AST::AnnotationKind::FROZEN) { auto frozen = PackageFormat::CreateAnno(builder, PackageFormat::AnnoKind_Frozen, builder.CreateString(annotation->identifier.Val()), builder.CreateVector({})); annotations.emplace_back(frozen); } else if (annotation->kind == AST::AnnotationKind::JAVA_MIRROR) { auto args = builder.CreateVector(SaveAnnotationArgs(*annotation)); auto mirror = PackageFormat::CreateAnno(builder, PackageFormat::AnnoKind_JavaMirror, builder.CreateString(annotation->identifier.Val()), args); annotations.emplace_back(mirror); } else if (annotation->kind == AST::AnnotationKind::JAVA_IMPL) { auto args = builder.CreateVector(SaveAnnotationArgs(*annotation)); auto impl = PackageFormat::CreateAnno(builder, PackageFormat::AnnoKind_JavaImpl, builder.CreateString(annotation->identifier.Val()), args); annotations.emplace_back(impl); } else if (annotation->kind == AST::AnnotationKind::OBJ_C_MIRROR) { auto args = builder.CreateVector(SaveAnnotationArgs(*annotation)); auto mirror = PackageFormat::CreateAnno(builder, PackageFormat::AnnoKind_ObjCMirror, builder.CreateString(annotation->identifier.Val()), args); annotations.emplace_back(mirror); } else if (annotation->kind == AST::AnnotationKind::OBJ_C_IMPL) { auto args = builder.CreateVector(SaveAnnotationArgs(*annotation)); auto impl = PackageFormat::CreateAnno(builder, PackageFormat::AnnoKind_ObjCImpl, builder.CreateString(annotation->identifier.Val()), args); annotations.emplace_back(impl); } else if (annotation->kind == AST::AnnotationKind::FOREIGN_NAME) { auto args = builder.CreateVector(SaveAnnotationArgs(*annotation)); auto impl = PackageFormat::CreateAnno(builder, PackageFormat::AnnoKind_ForeignName, builder.CreateString(annotation->identifier.Val()), args); annotations.emplace_back(impl); } else if (annotation->kind == AST::AnnotationKind::CUSTOM && annotation->isCompileTimeVisible) { auto args = builder.CreateVector(SaveAnnotationArgs(*annotation)); auto custom = PackageFormat::CreateAnno( builder, PackageFormat::AnnoKind_Custom, builder.CreateString(annotation->identifier.Val()), args); annotations.emplace_back(custom); } } return annotations; } std::vector ASTWriter::ASTWriterImpl::SaveAnnotationArgs(const Annotation& annotation) { std::vector args; for (auto& arg : annotation.args) { // Only literal support yet. if (arg->expr->astKind != ASTKind::LIT_CONST_EXPR) { continue; } auto serialized = PackageFormat::CreateAnnoArg(builder, builder.CreateString(arg->name.GetRawText()), SaveExpr(*arg->expr)); args.emplace_back(serialized); } return args; } cangjie_compiler-1.0.7/src/Modules/ASTSerialization/ASTWriterImpl.h000066400000000000000000000443711510705540100252230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the AST Writer related classes. */ #ifndef CANGJIE_MODULES_ASTSERIALIZATION_ASTWRITER_IMPL_H #define CANGJIE_MODULES_ASTSERIALIZATION_ASTWRITER_IMPL_H #include "flatbuffers/ModuleFormat_generated.h" #include "cangjie/AST/ASTCasting.h" #include "cangjie/Modules/ASTSerialization.h" #include "ASTSerializeUtils.h" namespace Cangjie { struct DeclInfo { TStringOffset name; TStringOffset exportId; TStringOffset mangledName; TStringOffset mangledBeforeSema; TDeclHash hash; TStringOffset fullPackageName; TPosition begin; TPosition end; TPosition identifierPos; flatbuffers::Offset> attributes; bool isConst = false; FormattedIndex ty; bool isTopLevel; flatbuffers::Offset> annotations; flatbuffers::Offset> dependencies; }; struct NodeInfo { TPosition begin; TPosition end; FormattedIndex ty; Ptr mapExpr = nullptr; OverflowStrategy ov{OverflowStrategy::NA}; }; class ASTWriter::ASTWriterImpl { public: ASTWriterImpl(DiagnosticEngine& diag, const std::string& packageDepInfo, const ExportConfig& exportCfg, const CjoManager& cjoManager) : diag(diag), config(exportCfg), packageDepInfo(packageDepInfo), cjoManager(cjoManager) { } ~ASTWriterImpl() { } void SetSerializingCommon(); void SetAttributesIfSerializingCommonPartOfPackage(AST::Package &package); void PreSaveFullExportDecls(AST::Package& package); inline bool NeedToExportDecl(Ptr decl); void DFSCollectFilesDeclarations(Ptr file, std::unordered_set& alreadyVisitedFiles, std::vector>& topLevelDeclsOrdered, std::unordered_set& usedTys); // Export external decls of a package AST to a buffer. void ExportAST(const AST::PackageDecl& package); void AST2FB(std::vector& data, const AST::PackageDecl& package); // Save semaTypes, return TyIndex and construct savedTypeMap. FormattedIndex SaveType(Ptr pType); bool isChirNow = false; private: DiagnosticEngine& diag; ExportConfig config; bool exportFuncBody = true; bool serializingCommon = false; flatbuffers::FlatBufferBuilder builder{INITIAL_FILE_SIZE}; std::string packageDepInfo; const CjoManager& cjoManager; /** All serialized node index in order. */ // Save all imported packages, the vector pos is package index. std::vector allPackages; // Save all file names in the package, the vector pos is file index. std::vector allFiles; std::vector allFileImports; std::vector allFileInfo; std::vector allTypes; std::vector allDecls; std::vector allExprs; // Saved packages with indexes, which is used for indexing imported decl. std::unordered_map savedPackageMap; // Saved files with indexes, which is used for indexing source file. // fileID -> package index, file index std::unordered_map> savedFileMap; // Saved types, decls and exprs with indexes, that are used for mapping in sema. std::unordered_map, FormattedIndex> savedTypeMap; std::unordered_map, FormattedIndex> savedDeclMap; std::unordered_map, FormattedIndex> savedExprMap; std::unordered_set> topLevelDecls; std::unordered_set> preSavedDecls; inline FormattedIndex PreSaveDecl(const AST::Decl& decl) { if (auto found = savedDeclMap.find(&decl); found != savedDeclMap.end()) { return found->second; } // NOTE: valid index start from 1. FormattedIndex declIndex = static_cast(allDecls.size()) + 1; (void)allDecls.emplace_back(TDeclOffset()); (void)savedDeclMap.emplace(&decl, declIndex); (void)preSavedDecls.emplace(&decl); return declIndex; } // Save imported packageNames, return FormattedIndex and construct 'savedPackageMap'. // NOTE: saved package indices are valid which start from 0. FormattedIndex SavePackageName(const std::string& fullPackageName); // Save file names and add to savedFileMap. void SaveFileInfo(const AST::File& file); flatbuffers::Offset SaveFileImports(const AST::File& file); /** * Save decls, return FormattedIndex and construct savedDeclMap, isTopLevel * Indicates saving toplevel decl. * NOTE: should only be called in GetDeclIndex or used when saving local decl. */ FormattedIndex SaveDecl(const AST::Decl& decl, bool isTopLevel = false); TDeclOffset SaveVarDecl(const AST::VarDecl& varDecl, const DeclInfo& declInfo); // Only store local 'VarWithPatternDecl' inside expression exporting. TDeclOffset SaveVarWithPatternDecl(const AST::VarWithPatternDecl& vpd, const DeclInfo& declInfo); TDeclOffset SavePropDecl(const AST::PropDecl& propDecl, const DeclInfo& declInfo); TDeclOffset SaveFuncDecl(const AST::FuncDecl& funcDecl, const DeclInfo& declInfo); TDeclOffset SaveParamDecl(const AST::FuncParam& paramDecl, const DeclInfo& declInfo); TDeclOffset SaveStructDecl(const AST::StructDecl& structDecl, const DeclInfo& declInfo); TDeclOffset SaveEnumDecl(const AST::EnumDecl& enumDecl, const DeclInfo& declInfo); TDeclOffset SaveInterfaceDecl(const AST::InterfaceDecl& interfaceDecl, const DeclInfo& declInfo); TDeclOffset SaveClassDecl(const AST::ClassDecl& classDecl, const DeclInfo& declInfo); TDeclOffset SaveExtendDecl(const AST::ExtendDecl& extendDecl, const DeclInfo& declInfo); TDeclOffset SaveTypeAliasDecl(const AST::TypeAliasDecl& typeAliasDecl, const DeclInfo& declInfo); TDeclOffset SaveBuiltInDecl(const AST::BuiltInDecl& builtInDecl, const DeclInfo& declInfo); TDeclOffset SaveGenericParamDecl(const AST::GenericParamDecl& gpd, const DeclInfo& declInfo); TDeclOffset SaveUnsupportDecl(const DeclInfo& declInfo, const AST::Decl& decl); using DeclWriterT = std::function; template , void>> DeclWriterT Proxy(TDeclOffset (ASTWriterImpl::*saveFunc)(const DeclT&, const DeclInfo&)) { return [this, saveFunc](const AST::Decl& decl, const DeclInfo& declInfo) { return (this->*saveFunc)(StaticCast(decl), declInfo); }; } std::unordered_map declWriterMap { {AST::ASTKind::VAR_DECL, Proxy(&ASTWriterImpl::SaveVarDecl)}, {AST::ASTKind::VAR_WITH_PATTERN_DECL, Proxy(&ASTWriterImpl::SaveVarWithPatternDecl)}, {AST::ASTKind::PROP_DECL, Proxy(&ASTWriterImpl::SavePropDecl)}, {AST::ASTKind::FUNC_DECL, Proxy(&ASTWriterImpl::SaveFuncDecl)}, {AST::ASTKind::FUNC_PARAM, Proxy(&ASTWriterImpl::SaveParamDecl)}, {AST::ASTKind::STRUCT_DECL, Proxy(&ASTWriterImpl::SaveStructDecl)}, {AST::ASTKind::ENUM_DECL, Proxy(&ASTWriterImpl::SaveEnumDecl)}, {AST::ASTKind::INTERFACE_DECL, Proxy(&ASTWriterImpl::SaveInterfaceDecl)}, {AST::ASTKind::CLASS_DECL, Proxy(&ASTWriterImpl::SaveClassDecl)}, {AST::ASTKind::EXTEND_DECL, Proxy(&ASTWriterImpl::SaveExtendDecl)}, {AST::ASTKind::TYPE_ALIAS_DECL, Proxy(&ASTWriterImpl::SaveTypeAliasDecl)}, {AST::ASTKind::BUILTIN_DECL, Proxy(&ASTWriterImpl::SaveBuiltInDecl)}, {AST::ASTKind::GENERIC_PARAM_DECL, Proxy(&ASTWriterImpl::SaveGenericParamDecl)}, }; bool PlannedToBeSerialized(Ptr decl); std::vector CollectInitializationDependencies(const AST::Decl& decl, std::set visited); TFuncBodyOffset SaveFuncBody(const AST::FuncBody& funcBody); // Save generic Information flatbuffers::Offset SaveGeneric(const AST::Decl& decl); flatbuffers::Offset SaveFuncParamList(const AST::FuncParamList& paramList); // Get package index and file index from saveFileMap. std::pair GetFileIndex(unsigned int fileId); // Get decl index in current package from savedDeclMap, if not found, save // the decl node. FormattedIndex GetDeclIndex(Ptr decl); // Get full decl index from current package or imported packages. TFullIdOffset GetFullDeclIndex(Ptr decl); std::vector SaveAnnotations(const AST::Decl& decl); std::vector SaveAnnotationArgs(const AST::Annotation& annotation); flatbuffers::Offset> SaveAttributes(const AST::AttributePack& attrs); template auto GetGenericDeclIndex(T& decl) { // NOTE: do not add partial instnatied decl's generic reference. if (IsFullInstantiatedDecl(decl) || (decl.IsMemberDecl() && IsFullInstantiatedDecl(*decl.outerDecl))) { return GetFullDeclIndex(decl.genericDecl); } return PackageFormat::CreateFullId(builder, INVALID_PACKAGE_INDEX, builder.CreateString("")); } auto GetVirtualParamLists(const AST::FuncBody& funcBody); template TVectorOffset GetBody(T& decl); template auto GetVirtualInterfaces(T& decl) { // SuperInterfaceTypes. std::vector superInterfaceTypes; for (auto& it : decl.inheritedTypes) { superInterfaceTypes.push_back(SaveType(it->ty)); } return builder.CreateVector(superInterfaceTypes); } NodeInfo PackNodeInfo(const AST::Node& node); /** NOTE: Main entrance of 'SaveExpr' is only from 'SaveFuncBody', 'SaveVarDecl' and 'SaveVarWithPatternDecl'. */ FormattedIndex SaveExpr(const AST::Expr& expr); TExprOffset SaveExpression(const AST::PrimitiveTypeExpr& pte, const NodeInfo& info); TExprOffset SaveExpression(const AST::WildcardExpr& we, const NodeInfo& info); TExprOffset SaveExpression(const AST::ReturnExpr& re, const NodeInfo& info); TExprOffset SaveExpression(const AST::JumpExpr& je, const NodeInfo& info); TExprOffset SaveExpression(const AST::MemberAccess& ma, const NodeInfo& info); TExprOffset SaveExpression(const AST::RefExpr& re, const NodeInfo& info); TExprOffset SaveExpression(const AST::CallExpr& ce, const NodeInfo& info); TExprOffset SaveExpression(const AST::UnaryExpr& ue, const NodeInfo& info); TExprOffset SaveExpression(const AST::IncOrDecExpr& ide, const NodeInfo& info); TExprOffset SaveExpression(const AST::LitConstExpr& lce, const NodeInfo& info); TExprOffset SaveExpression(const AST::BinaryExpr& be, const NodeInfo& info); TExprOffset SaveExpression(const AST::SubscriptExpr& se, const NodeInfo& info); TExprOffset SaveExpression(const AST::AssignExpr& ae, const NodeInfo& info); TExprOffset SaveExpression(const AST::ArrayExpr& ae, const NodeInfo& info); TExprOffset SaveExpression(const AST::PointerExpr& pe, const NodeInfo& info); TExprOffset SaveExpression(const AST::TypeConvExpr& tce, const NodeInfo& info); TExprOffset SaveExpression(const AST::ThrowExpr& te, const NodeInfo& info); TExprOffset SaveExpression(const AST::PerformExpr& pe, const NodeInfo& info); TExprOffset SaveExpression(const AST::SpawnExpr& se, const NodeInfo& info); TExprOffset SaveExpression(const AST::ArrayLit& al, const NodeInfo& info); TExprOffset SaveExpression(const AST::TupleLit& tl, const NodeInfo& info); TExprOffset SaveExpression(const AST::IfExpr& ie, const NodeInfo& info); TExprOffset SaveExpression(const AST::TryExpr& te, const NodeInfo& info); TExprOffset SaveExpression(const AST::WhileExpr& we, const NodeInfo& info); TExprOffset SaveExpression(const AST::DoWhileExpr& dwe, const NodeInfo& info); TExprOffset SaveExpression(const AST::LambdaExpr& le, const NodeInfo& info); TExprOffset SaveExpression(const AST::Block& block, const NodeInfo& info); TExprOffset SaveExpression(const AST::MatchExpr& me, const NodeInfo& info); TExprOffset SaveExpression(const AST::LetPatternDestructor& e, const NodeInfo& info); TExprOffset SaveUnsupportExpr(const AST::Expr& expr, const NodeInfo& info); TExprOffset SaveExpression(const AST::ForInExpr& fie, const NodeInfo& info); using ExprWriterT = std::function; template , void>> ExprWriterT Proxy(TExprOffset (ASTWriterImpl::*saveFunc)(const ExprT&, const NodeInfo&)) { return [this, saveFunc](const AST::Expr& expr, const NodeInfo& info) { return (this->*saveFunc)(StaticCast(expr), info); }; } std::unordered_map exprWriterMap{ {AST::ASTKind::PRIMITIVE_TYPE_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::WILDCARD_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::RETURN_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::JUMP_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::MEMBER_ACCESS, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::REF_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::CALL_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::UNARY_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::INC_OR_DEC_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::LIT_CONST_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::BINARY_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::SUBSCRIPT_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::ASSIGN_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::ARRAY_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::POINTER_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::TYPE_CONV_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::THROW_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::PERFORM_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::SPAWN_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::ARRAY_LIT, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::TUPLE_LIT, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::IF_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::TRY_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::WHILE_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::DO_WHILE_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::LAMBDA_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::BLOCK, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::MATCH_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::LET_PATTERN_DESTRUCTOR, Proxy(&ASTWriterImpl::SaveExpression)}, {AST::ASTKind::FOR_IN_EXPR, Proxy(&ASTWriterImpl::SaveExpression)}, }; // Following types of ast also saved with 'PackageFormat::Expr'. FormattedIndex SaveFuncArg(const AST::FuncArg& arg); FormattedIndex SaveMatchCase(const AST::MatchCase& mc); FormattedIndex SaveMatchCaseOther(const AST::MatchCaseOther& mco); /** NOTE: Main entrance of 'SavePattern' is only from 'SaveExpr' and 'SaveVarWithPatternDecl' */ TPatternOffset SavePattern(const AST::Pattern& pattern); TPatternOffset SaveConstPattern(const AST::ConstPattern& cp); TPatternOffset SaveWildcardPattern(const AST::WildcardPattern& wp); TPatternOffset SaveVarPattern(const AST::VarPattern& vp); TPatternOffset SaveTuplePattern(const AST::TuplePattern& tp); TPatternOffset SaveTypePattern(const AST::TypePattern& tp); TPatternOffset SaveEnumPattern(const AST::EnumPattern& ep); TPatternOffset SaveExceptTypePattern(const AST::ExceptTypePattern& etp); TPatternOffset SaveCommandTypePattern(const AST::CommandTypePattern& ctp); // Save sema types. TTypeOffset SavePointerTy(const AST::PointerTy& type); TTypeOffset SaveArrayTy(const AST::ArrayTy& type); TTypeOffset SaveVArrayTy(const AST::VArrayTy& type); TTypeOffset SaveTupleTy(const AST::TupleTy& type); TTypeOffset SaveFuncTy(const AST::FuncTy& type); TTypeOffset SaveGenericsTy(const AST::GenericsTy& gty); TTypeOffset SaveNominalTy(const AST::Ty& type); void SaveBasicNodeInfo(PackageFormat::ExprBuilder& dbuilder, const NodeInfo& info); Ptr GetRealExpr(const AST::Expr& expr); /** Only can be used after @p expr has been saved. */ inline FormattedIndex GetExprIndex(const AST::Expr& expr) { auto realExpr = GetRealExpr(expr); auto found = savedExprMap.find(realExpr); CJC_ASSERT(found != savedExprMap.end()); return found->second; } static inline PackageFormat::TypeKind GetFormatTypeKind(AST::TypeKind kind) { auto found = TYPE_KIND_MAP.find(kind); return found != TYPE_KIND_MAP.end() ? found->second : PackageFormat::TypeKind_Invalid; } }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Modules/ASTSerialization/ExprLoader.cpp000066400000000000000000000575561510705540100251660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * This file implements the AST Loader related classes. */ #include "ASTLoaderImpl.h" #include "flatbuffers/ModuleFormat_generated.h" #include "cangjie/AST/ASTCasting.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/StringConvertor.h" #include "cangjie/Lex/Token.h" #include "cangjie/Utils/CheckUtils.h" using namespace Cangjie; using namespace AST; OwnedPtr ASTLoader::ASTLoaderImpl::LoadInvalidExpr(const PackageFormat::Expr& expr) { (void)diag.DiagnoseRefactor(DiagKindRefactor::package_unsupported_load, DEFAULT_POSITION, "expression"); auto invalidExpr = MakeOwned(INVALID_POSITION); LoadNodePos(expr, *invalidExpr); return invalidExpr; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadWildcardExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto we = CreateAndLoadBasicInfo(expr, exprIndex); return we; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadPrimitiveTypeExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto index = expr.type(); CJC_ASSERT(index != INVALID_FORMAT_INDEX); auto typeObj = package->allTypes()->Get(static_cast(expr.type() - 1)); auto kind = GetASTTypeKind(typeObj->kind()); CJC_ASSERT(kind != TypeKind::TYPE_INVALID); auto pte = CreateAndLoadBasicInfo(expr, exprIndex, kind); return pte; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadReturnExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { CJC_ASSERT(expr.operands()->size() == 1); auto re = CreateAndLoadBasicInfo(expr, exprIndex); re->expr = LoadExpr(expr.operands()->Get(0)); return re; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadJumpExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto je = CreateAndLoadBasicInfo(expr, exprIndex); auto info = expr.info_as_JumpInfo(); CJC_NULLPTR_CHECK(info); je->isBreak = info->isBreak(); return je; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadMemberAccess(const PackageFormat::Expr& expr, int64_t exprIndex) { auto ma = CreateAndLoadBasicInfo(expr, exprIndex); CJC_ASSERT(expr.operands()->size() == 1); ma->baseExpr = LoadExpr(expr.operands()->Get(0)); auto info = expr.info_as_ReferenceInfo(); CJC_NULLPTR_CHECK(info); ma->field = info->reference()->str(); if (auto nre = DynamicCast(ma->baseExpr.get())) { nre->isAlone = false; } return ma; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadRefExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto re = CreateAndLoadBasicInfo(expr, exprIndex); auto info = expr.info_as_ReferenceInfo(); CJC_NULLPTR_CHECK(info); re->ref.identifier = info->reference()->str(); re->isThis = re->ref.identifier == "this"; re->isSuper = re->ref.identifier == "super"; return re; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadCallExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto ce = CreateAndLoadBasicInfo(expr, exprIndex); CJC_ASSERT(expr.operands()->size() >= 1); ce->baseFunc = LoadExpr(expr.operands()->Get(0)); std::vector> args; for (uoffset_t i = 1; i < expr.operands()->size(); i++) { auto fa = LoadFuncArg(expr.operands()->Get(i)); args.emplace_back(fa.get()); ce->args.emplace_back(std::move(fa)); } ce->desugarArgs = args; auto info = expr.info_as_CallInfo(); CJC_NULLPTR_CHECK(info); ce->callKind = CALL_KIND_RMAP.at(info->callKind()); if (info->hasSideEffect()) { ce->EnableAttr(Attribute::SIDE_EFFECT); } return ce; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadUnaryExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto ue = CreateAndLoadBasicInfo(expr, exprIndex); CJC_NULLPTR_CHECK(expr.operands()); CJC_ASSERT(expr.operands()->size() == 1); ue->expr = LoadExpr(expr.operands()->Get(0)); auto info = expr.info_as_UnaryInfo(); CJC_NULLPTR_CHECK(info); ue->op = OP_KIND_RMAP.at(info->op()); return ue; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadIncOrDecExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto ide = CreateAndLoadBasicInfo(expr, exprIndex); CJC_ASSERT(expr.operands()->size() == 1); ide->expr = LoadExpr(expr.operands()->Get(0)); auto info = expr.info_as_IncOrDecInfo(); CJC_NULLPTR_CHECK(info); ide->op = OP_KIND_RMAP.at(info->op()); return ide; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadLitConstExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto info = expr.info_as_LitConstInfo(); CJC_NULLPTR_CHECK(info); auto lce = CreateAndLoadBasicInfo( expr, exprIndex, LIT_CONST_KIND_RMAP.at(info->constKind()), info->strValue()->str()); lce->stringKind = STRING_KIND_RMAP.at(info->strKind()); if (lce->kind == LitConstKind::RUNE || lce->kind == LitConstKind::STRING) { lce->codepoint = StringConvertor::UTF8ToCodepoint(lce->stringValue); } return lce; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadBinaryExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto be = CreateAndLoadBasicInfo(expr, exprIndex); CJC_ASSERT(expr.operands()->size() == 2); // Has 2 sub nodes. be->leftExpr = LoadExpr(expr.operands()->Get(0)); be->rightExpr = LoadExpr(expr.operands()->Get(1)); auto info = expr.info_as_BinaryInfo(); CJC_NULLPTR_CHECK(info); be->op = OP_KIND_RMAP.at(info->op()); return be; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadSubscriptExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto se = CreateAndLoadBasicInfo(expr, exprIndex); CJC_ASSERT(expr.operands()->size() > 1); se->baseExpr = LoadExpr(expr.operands()->Get(0)); for (uoffset_t i = 1; i < expr.operands()->size(); i++) { auto index = expr.operands()->Get(i); se->indexExprs.emplace_back(LoadExpr(index)); } auto info = expr.info_as_SubscriptInfo(); CJC_NULLPTR_CHECK(info); se->isTupleAccess = info->isTupleAccess(); return se; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadAssignExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto ae = CreateAndLoadBasicInfo(expr, exprIndex); ae->leftValue = LoadExpr(expr.operands()->Get(0)); ae->rightExpr = LoadExpr(expr.operands()->Get(1)); auto info = expr.info_as_AssignInfo(); CJC_NULLPTR_CHECK(info); ae->op = OP_KIND_RMAP.at(info->op()); ae->isCompound = info->isCompound(); return ae; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadArrayExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto ae = CreateAndLoadBasicInfo(expr, exprIndex); for (uoffset_t i = 0; i < expr.operands()->size(); i++) { auto index = expr.operands()->Get(i); ae->args.emplace_back(LoadFuncArg(index)); } auto info = expr.info_as_ArrayInfo(); CJC_NULLPTR_CHECK(info); ae->isValueArray = info->isValueArray(); return ae; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadPointerExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto pe = CreateAndLoadBasicInfo(expr, exprIndex); if (expr.operands()->size() == 1) { pe->arg = LoadFuncArg(expr.operands()->Get(0)); } return pe; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadTypeConvExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto tce = CreateAndLoadBasicInfo(expr, exprIndex); CJC_ASSERT(expr.operands()->size() == 1); tce->expr = LoadExpr(expr.operands()->Get(0)); return tce; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadThrowExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto te = CreateAndLoadBasicInfo(expr, exprIndex); CJC_ASSERT(expr.operands()->size() == 1); te->expr = LoadExpr(expr.operands()->Get(0)); return te; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadPerformExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto pe = CreateAndLoadBasicInfo(expr, exprIndex); CJC_ASSERT(expr.operands()->size() == 1); pe->expr = LoadExpr(expr.operands()->Get(0)); return pe; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadResumeExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto re = CreateAndLoadBasicInfo(expr, exprIndex); return re; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadSpawnExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto se = CreateAndLoadBasicInfo(expr, exprIndex); // Sub node 'task' does not used for SpawnExpr with futureObj. if (expr.operands()->size() > 0) { // Optional argument. se->arg = LoadExpr(expr.operands()->Get(0)); } // 'futureObj' is local decl, can be loaded at now. auto info = expr.info_as_SpawnInfo(); CJC_NULLPTR_CHECK(info); auto fullId = info->future(); se->futureObj = LoadDecl(fullId->index()); return se; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadArrayLit(const PackageFormat::Expr& expr, int64_t exprIndex) { auto al = CreateAndLoadBasicInfo(expr, exprIndex); for (uoffset_t i = 0; i < expr.operands()->size(); i++) { auto index = expr.operands()->Get(i); al->children.emplace_back(LoadExpr(index)); } return al; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadTupleLit(const PackageFormat::Expr& expr, int64_t exprIndex) { auto tl = CreateAndLoadBasicInfo(expr, exprIndex); for (uoffset_t i = 0; i < expr.operands()->size(); i++) { auto index = expr.operands()->Get(i); tl->children.emplace_back(LoadExpr(index)); } return tl; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadIfExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto ie = CreateAndLoadBasicInfo(expr, exprIndex); CJC_ASSERT(expr.operands()->size() >= 2); // Has at least 2 sub nodes. uoffset_t idx = 0; ie->condExpr = LoadExpr(expr.operands()->Get(idx++)); ie->thenBody = LoadExpr(expr.operands()->Get(idx++)); ie->elseBody = idx < expr.operands()->size() ? LoadExpr(expr.operands()->Get(idx)) : nullptr; ie->hasElse = ie->elseBody != nullptr; ie->isElseIf = ie->hasElse && ie->elseBody->astKind == ASTKind::IF_EXPR; if (ie->hasElse && ie->elseBody->desugarExpr) { // Recover normal block condition for else body. allDummyExprs.erase(ie->elseBody.get()); ie->elseBody = std::move(ie->elseBody->desugarExpr); } return ie; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadTryExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto te = CreateAndLoadBasicInfo(expr, exprIndex); CJC_ASSERT(expr.operands()->size() >= 1); // Has at least 1 sub node. uoffset_t idx = 0; te->tryBlock = LoadExpr(expr.operands()->Get(idx++)); CJC_ASSERT(expr.operands()->size() > 0); for (; idx < expr.operands()->size() - 1; idx++) { auto index = expr.operands()->Get(idx); te->catchBlocks.emplace_back(LoadExpr(index)); } te->finallyBlock = idx < expr.operands()->size() ? LoadExpr(expr.operands()->Get(idx)) : nullptr; auto info = expr.info_as_TryInfo(); CJC_NULLPTR_CHECK(info); te->catchPatterns.resize(info->patterns()->size()); for (uoffset_t i = 0; i < info->patterns()->size(); i++) { te->catchPatterns[i] = LoadPattern(*info->patterns()->Get(i)); } // 'resourceSpec' is local decl, can be loaded at now. te->resourceSpec.resize(info->resources()->size()); for (uoffset_t i = 0; i < info->resources()->size(); i++) { auto fullId = info->resources()->Get(i); te->resourceSpec[i] = LoadDecl(fullId->index()); } return te; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadWhileExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto we = CreateAndLoadBasicInfo(expr, exprIndex); CJC_ASSERT(expr.operands()->size() == 2); // Has 2 sub nodes. we->condExpr = LoadExpr(expr.operands()->Get(0)); we->body = LoadExpr(expr.operands()->Get(1)); return we; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadDoWhileExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto dwe = CreateAndLoadBasicInfo(expr, exprIndex); CJC_ASSERT(expr.operands()->size() == 2); // Has 2 sub nodes. dwe->body = LoadExpr(expr.operands()->Get(0)); dwe->condExpr = LoadExpr(expr.operands()->Get(1)); return dwe; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadLambdaExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto le = CreateAndLoadBasicInfo(expr, exprIndex); auto info = expr.info_as_LambdaInfo(); CJC_NULLPTR_CHECK(info); CJC_NULLPTR_CHECK(info->funcBody()); le->funcBody = LoadFuncBody(*info->funcBody()); if (info->supportMock()) { le->EnableAttr(Attribute::MOCK_SUPPORTED); } return le; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadBlock(const PackageFormat::Expr& expr, int64_t exprIndex) { auto block = CreateAndLoadBasicInfo(expr, exprIndex); // Additional last condition is whether the block is unsafe. auto info = expr.info_as_BlockInfo(); CJC_NULLPTR_CHECK(info); CJC_ASSERT(expr.operands()->size() <= info->isExpr()->size()); auto size = expr.operands()->size(); block->body.resize(size); for (uoffset_t i = 0; i < size; ++i) { if (info->isExpr()->Get(i)) { block->body[i] = LoadExpr(expr.operands()->Get(i)); } else { block->body[i] = LoadDecl(expr.operands()->Get(i)); } } if (info->isExpr()->size() > size && info->isExpr()->Get(size) != 0) { block->EnableAttr(Attribute::UNSAFE); } return block; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadMatchExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto me = CreateAndLoadBasicInfo(expr, exprIndex); auto info = expr.info_as_MatchInfo(); CJC_NULLPTR_CHECK(info); me->matchMode = info->matchMode(); if (me->matchMode) { me->selector = LoadExpr(expr.operands()->Get(0)); for (uoffset_t i = 1; i < expr.operands()->size(); i++) { auto index = expr.operands()->Get(i); me->matchCases.emplace_back(LoadMatchCase(index)); } } else { for (uoffset_t i = 0; i < expr.operands()->size(); i++) { auto index = expr.operands()->Get(i); me->matchCaseOthers.emplace_back(LoadMatchCaseOther(index)); } } return me; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadLetPatternDestructor( const PackageFormat::Expr& expr, int64_t exprIndex) { auto res = CreateAndLoadBasicInfo(expr, exprIndex); res->initializer = LoadExpr(expr.operands()->Get(0)); auto info = expr.info_as_LetPatternDestructorInfo(); CJC_NULLPTR_CHECK(info); for (auto pattern : *info->patterns()) { res->patterns.push_back(LoadPattern(*pattern)); } return res; } template OwnedPtr ASTLoader::ASTLoaderImpl::LoadExpr(FormattedIndex exprIndex) { if (exprIndex == INVALID_FORMAT_INDEX) { return {}; } OwnedPtr ret; auto expr = GetFormatExprByIndex(exprIndex); auto foundHandler = exprLoaderMap.find(expr->kind()); if (foundHandler != exprLoaderMap.end()) { ret = foundHandler->second(this, *expr, exprIndex); } else { ret = LoadInvalidExpr(*expr); } if constexpr (!std::is_same_v) { // Create wrapped 'desugarExpr' for CHIR to generate 'goto' correctly. if (ret->astKind == ASTKind::BLOCK) { auto dummy = MakeOwned(); dummy->EnableAttr(Attribute::COMPILER_ADD); dummy->desugarExpr = std::move(ret); ret = std::move(dummy); allDummyExprs.emplace(ret.get()); } } // Caller guarantees casting type is matched. return OwnedPtr(StaticCast(ret.release())); } OwnedPtr ASTLoader::ASTLoaderImpl::LoadMatchCase(FormattedIndex index) { auto mcObj = GetFormatExprByIndex(index); auto mc = CreateAndLoadBasicInfo(*mcObj, INVALID_FORMAT_INDEX); CJC_ASSERT(mcObj->operands()->size() == 2); // Has 2 sub nodes. mc->patternGuard = LoadExpr(mcObj->operands()->Get(0)); mc->exprOrDecls = LoadExpr(mcObj->operands()->Get(1)); auto info = mcObj->info_as_MatchCaseInfo(); CJC_NULLPTR_CHECK(info); for (uoffset_t i = 0; i < info->patterns()->size(); i++) { mc->patterns.emplace_back(LoadPattern(*info->patterns()->Get(i))); } return mc; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadForInExpr(const PackageFormat::Expr& expr, int64_t exprIndex) { auto fie = CreateAndLoadBasicInfo(expr, exprIndex); CJC_ASSERT(expr.operands()->size() == 3U); auto info = expr.info_as_ForInInfo(); CJC_NULLPTR_CHECK(info); fie->forInKind = FOR_IN_KIND_RMAP.at(info->forInKind()); if (fie->forInKind == ForInKind::FORIN_INVALID) { fie->inExpression = LoadExpr(expr.operands()->Get(0)); } else { fie->inExpression = LoadExpr(expr.operands()->Get(0)); } fie->patternGuard = LoadExpr(expr.operands()->Get(1)); fie->body = LoadExpr(expr.operands()->Get(2U)); fie->pattern = LoadPattern(*info->pattern()); return fie; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadMatchCaseOther(FormattedIndex index) { auto mcoObj = GetFormatExprByIndex(index); auto mco = CreateAndLoadBasicInfo(*mcoObj, INVALID_FORMAT_INDEX); CJC_ASSERT(mcoObj->operands()->size() == 2); // Has 2 sub nodes. mco->matchExpr = LoadExpr(mcoObj->operands()->Get(0)); mco->exprOrDecls = LoadExpr(mcoObj->operands()->Get(1)); return mco; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadFuncBody( const PackageFormat::FuncBody& body, bool isInline, bool isConst) { auto funcBody = MakeOwned(); // FuncParamList for (uoffset_t i = 0; i < body.paramLists()->size(); i++) { funcBody->paramLists.emplace_back(LoadFuncParamList(body.paramLists()->Get(i))); } bool shouldImportBody = body.always() || isInline || isConst; if (importSrcCode && shouldImportBody) { funcBody->body = LoadExpr(body.body()); funcBody->captureKind = static_cast(body.captureKind()); // Set returnExpr's reference inside current funcBody. Walker(funcBody->body.get(), [&funcBody](auto node) { if (node->astKind == ASTKind::FUNC_BODY) { return VisitAction::SKIP_CHILDREN; } if (auto re = DynamicCast(node)) { re->refFuncBody = funcBody.get(); } return VisitAction::WALK_CHILDREN; }).Walk(); } // Mark imported for funcBody to create filter in generic instantiation step for partial instantiate. funcBody->EnableAttr(Attribute::IMPORTED); return funcBody; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadFuncArg(FormattedIndex index) { auto argObj = GetFormatExprByIndex(index); auto arg = CreateAndLoadBasicInfo(*argObj, INVALID_FORMAT_INDEX); // FuncArg has 2 condition values for 'withInout' and 'isDefaultVal'. auto info = argObj->info_as_FuncArgInfo(); CJC_NULLPTR_CHECK(info); arg->withInout = info->withInout(); if (info->isDefaultVal()) { arg->expr = MakeOwned(INVALID_POSITION); // Create as a placeholder for default arg. arg->EnableAttr(Attribute::HAS_INITIAL); } else { CJC_ASSERT(argObj->operands()->size() == 1); arg->expr = LoadExpr(argObj->operands()->Get(0)); } return arg; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadPattern(const PackageFormat::Pattern& pattern) { switch (pattern.kind()) { case PackageFormat::PatternKind_ConstPattern: return LoadConstPattern(pattern); case PackageFormat::PatternKind_WildcardPattern: return LoadWildcardPattern(pattern); case PackageFormat::PatternKind_VarPattern: return LoadVarPattern(pattern); case PackageFormat::PatternKind_TuplePattern: return LoadTuplePattern(pattern); case PackageFormat::PatternKind_TypePattern: return LoadTypePattern(pattern); case PackageFormat::PatternKind_EnumPattern: return LoadEnumPattern(pattern); case PackageFormat::PatternKind_ExceptTypePattern: return LoadExceptTypePattern(pattern); case PackageFormat::PatternKind_CommandTypePattern: return LoadCommandTypePattern(pattern); default: CJC_ABORT(); // Should be unreachable. return CreateAndLoadBasicInfo(pattern, INVALID_FORMAT_INDEX); } } OwnedPtr ASTLoader::ASTLoaderImpl::LoadConstPattern(const PackageFormat::Pattern& pattern) { auto cp = CreateAndLoadBasicInfo(pattern, INVALID_FORMAT_INDEX); CJC_ASSERT(pattern.exprs()->size() >= 1); uoffset_t idx = 0; cp->literal = LoadExpr(pattern.exprs()->Get(idx++)); if (idx < pattern.exprs()->size()) { cp->operatorCallExpr = LoadExpr(pattern.exprs()->Get(idx)); } return cp; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadWildcardPattern(const PackageFormat::Pattern& pattern) { auto wp = CreateAndLoadBasicInfo(pattern, INVALID_FORMAT_INDEX); return wp; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadVarPattern(const PackageFormat::Pattern& pattern) { auto vp = CreateAndLoadBasicInfo(pattern, INVALID_FORMAT_INDEX); CJC_ASSERT(pattern.exprs()->size() == 1); vp->varDecl = LoadDecl(pattern.exprs()->Get(0)); CJC_NULLPTR_CHECK(vp->varDecl); vp->varDecl->parentPattern = vp.get(); return vp; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadTuplePattern(const PackageFormat::Pattern& pattern) { auto tp = CreateAndLoadBasicInfo(pattern, INVALID_FORMAT_INDEX); CJC_NULLPTR_CHECK(pattern.patterns()); for (uoffset_t i = 0; i < pattern.patterns()->size(); ++i) { tp->patterns.emplace_back(LoadPattern(*pattern.patterns()->Get(i))); } return tp; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadTypePattern(const PackageFormat::Pattern& pattern) { auto tp = CreateAndLoadBasicInfo(pattern, INVALID_FORMAT_INDEX); CJC_ASSERT(pattern.patterns()->size() == 1); tp->pattern = LoadPattern(*pattern.patterns()->Get(0)); tp->matchBeforeRuntime = pattern.matchBeforeRuntime(); tp->needRuntimeTypeCheck = pattern.needRuntimeTypeCheck(); return tp; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadEnumPattern(const PackageFormat::Pattern& pattern) { auto ep = CreateAndLoadBasicInfo(pattern, INVALID_FORMAT_INDEX); CJC_NULLPTR_CHECK(pattern.patterns()); for (uoffset_t i = 0; i < pattern.patterns()->size(); ++i) { ep->patterns.emplace_back(LoadPattern(*pattern.patterns()->Get(i))); } CJC_NULLPTR_CHECK(pattern.exprs()); CJC_ASSERT(pattern.exprs()->size() == 1); ep->constructor = LoadExpr(pattern.exprs()->Get(0)); return ep; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadExceptTypePattern(const PackageFormat::Pattern& pattern) { auto etp = CreateAndLoadBasicInfo(pattern, INVALID_FORMAT_INDEX); CJC_ASSERT(pattern.patterns()->size() == 1); etp->pattern = LoadPattern(*pattern.patterns()->Get(0)); return etp; } OwnedPtr ASTLoader::ASTLoaderImpl::LoadCommandTypePattern(const PackageFormat::Pattern& pattern) { auto ctp = CreateAndLoadBasicInfo(pattern, INVALID_FORMAT_INDEX); CJC_ASSERT(pattern.patterns()->size() == 1); ctp->pattern = LoadPattern(*pattern.patterns()->Get(0)); return ctp; } cangjie_compiler-1.0.7/src/Modules/ASTSerialization/ExprWriter.cpp000066400000000000000000001063041510705540100252160ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * This file implements the AST Writer related classes. */ #include "ASTWriterImpl.h" #include "flatbuffers/ModuleFormat_generated.h" #include "cangjie/AST/ASTCasting.h" #include "cangjie/AST/Utils.h" #include "cangjie/Utils/CheckUtils.h" using namespace Cangjie; using namespace AST; void ASTWriter::ASTWriterImpl::SaveBasicNodeInfo(PackageFormat::ExprBuilder& dbuilder, const NodeInfo& info) { dbuilder.add_begin(&info.begin); dbuilder.add_end(&info.end); dbuilder.add_type(info.ty); FormattedIndex mapIndx = info.mapExpr ? GetExprIndex(*info.mapExpr) : INVALID_FORMAT_INDEX; dbuilder.add_mapExpr(mapIndx); dbuilder.add_overflowPolicy(STRATEGY_MAP.at(info.ov)); } NodeInfo ASTWriter::ASTWriterImpl::PackNodeInfo(const Node& node) { auto begin = node.GetBegin(); auto end = node.GetEnd(); auto [pkgIndex, fileIndex] = GetFileIndex(begin.fileID); TPosition posBegin(fileIndex, pkgIndex, begin.line, begin.column, begin.GetStatus() == PositionStatus::IGNORE); TPosition posEnd(fileIndex, pkgIndex, end.line, end.column, end.GetStatus() == PositionStatus::IGNORE); auto ty = SaveType(node.ty); return {posBegin, posEnd, ty}; } // Only get desugared expression. 'ParenExpr' is just a wrapper which can be ignore. Ptr ASTWriter::ASTWriterImpl::GetRealExpr(const Expr& expr) { auto realExpr = &expr; while (realExpr->desugarExpr != nullptr) { realExpr = realExpr->desugarExpr.get(); } if (auto pe = DynamicCast(realExpr)) { CJC_NULLPTR_CHECK(pe->expr); realExpr = GetRealExpr(*pe->expr); } return realExpr; } FormattedIndex ASTWriter::ASTWriterImpl::SaveExpr(const Expr& expr) { auto realExpr = GetRealExpr(expr); auto found = savedExprMap.find(realExpr); if (found != savedExprMap.end()) { return found->second; } FormattedIndex exprIndex = static_cast(allExprs.size()) + 1; allExprs.emplace_back(TExprOffset()); (void)savedExprMap.emplace(realExpr, exprIndex); auto exprInfo = PackNodeInfo(*realExpr); exprInfo.ov = expr.overflowStrategy; // 'JumpExpr' will not have mapExpr, so re-use this field for 'refLoop'. exprInfo.mapExpr = realExpr->astKind == ASTKind::JUMP_EXPR ? StaticCast(realExpr)->refLoop : realExpr->mapExpr; TExprOffset offset; auto foundWriter = exprWriterMap.find(realExpr->astKind); if (foundWriter != exprWriterMap.end()) { offset = foundWriter->second(*realExpr, exprInfo); } else { offset = SaveUnsupportExpr(*realExpr, exprInfo); } // Overwrite the slot with the actual expr. allExprs[static_cast(exprIndex - 1)] = offset; return exprIndex; } TExprOffset ASTWriter::ASTWriterImpl::SaveUnsupportExpr(const Expr& expr, const NodeInfo& info) { (void)diag.DiagnoseRefactor( DiagKindRefactor::package_unsupport_save, expr, "expression", std::string(typeid(expr).name())); PackageFormat::ExprBuilder dbuilder(builder); SaveBasicNodeInfo(dbuilder, info); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const PrimitiveTypeExpr& /* pte */, const NodeInfo& info) { PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_PrimitiveTypeExpr); SaveBasicNodeInfo(dbuilder, info); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const WildcardExpr& /* we */, const NodeInfo& info) { PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_WildcardExpr); SaveBasicNodeInfo(dbuilder, info); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const ReturnExpr& re, const NodeInfo& info) { CJC_NULLPTR_CHECK(re.expr); auto subExprIdx = SaveExpr(*re.expr); auto subExprs = builder.CreateVector({subExprIdx}); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_ReturnExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(subExprs); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const JumpExpr& je, const NodeInfo& info) { auto eInfo = PackageFormat::CreateJumpInfo(builder, je.isBreak); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_JumpExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_info_type(PackageFormat::ExprInfo_JumpInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const MemberAccess& ma, const NodeInfo& info) { CJC_NULLPTR_CHECK(ma.baseExpr); auto baseIdx = SaveExpr(*ma.baseExpr); auto operands = builder.CreateVector({baseIdx}); auto fieldIdx = builder.CreateString(ma.field.Val()); auto target = GetFullDeclIndex(ma.target); std::vector types(ma.instTys.size()); for (size_t i = 0; i < ma.instTys.size(); ++i) { types[i] = SaveType(ma.instTys[i]); } auto tyIdx = builder.CreateVector(types); auto parentTy = SaveType(ma.matchedParentTy); auto eInfo = PackageFormat::CreateReferenceInfo(builder, fieldIdx, target, tyIdx, parentTy); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_MemberAccess); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operands); dbuilder.add_info_type(PackageFormat::ExprInfo_ReferenceInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const RefExpr& re, const NodeInfo& info) { auto name = builder.CreateString(re.ref.identifier.Val()); auto refTarget = re.ref.target && re.ref.target->genericDecl ? re.ref.target->genericDecl : re.ref.target; auto target = GetFullDeclIndex(refTarget); std::vector types(re.instTys.size()); for (size_t i = 0; i < re.instTys.size(); ++i) { types[i] = SaveType(re.instTys[i]); } auto tyIdx = builder.CreateVector(types); auto parentTy = SaveType(re.matchedParentTy); auto eInfo = PackageFormat::CreateReferenceInfo(builder, name, target, tyIdx, parentTy); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_RefExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_info_type(PackageFormat::ExprInfo_ReferenceInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const CallExpr& ce, const NodeInfo& info) { CJC_NULLPTR_CHECK(ce.baseFunc); std::vector operands; operands.emplace_back(SaveExpr(*ce.baseFunc)); auto saveArgs = [this, &operands](auto& args) { for (auto& arg : args) { operands.emplace_back(SaveFuncArg(*arg)); } }; ce.desugarArgs.has_value() ? saveArgs(ce.desugarArgs.value()) : saveArgs(ce.args); auto operandsIdx = builder.CreateVector(operands); // For now, only 'SIDE_EFFECT' attribute is needed for exporting. auto eInfo = PackageFormat::CreateCallInfo(builder, ce.TestAttr(Attribute::SIDE_EFFECT), CALL_KIND_MAP.at(ce.callKind)); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_CallExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); dbuilder.add_info_type(PackageFormat::ExprInfo_CallInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const UnaryExpr& ue, const NodeInfo& info) { CJC_NULLPTR_CHECK(ue.expr); auto baseIdx = SaveExpr(*ue.expr); auto operands = builder.CreateVector({baseIdx}); auto eInfo = PackageFormat::CreateUnaryInfo(builder, OP_KIND_MAP.at(ue.op)); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_UnaryExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operands); dbuilder.add_info_type(PackageFormat::ExprInfo_UnaryInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const IncOrDecExpr& ide, const NodeInfo& info) { CJC_NULLPTR_CHECK(ide.expr); auto baseIdx = SaveExpr(*ide.expr); auto operands = builder.CreateVector({baseIdx}); auto eInfo = PackageFormat::CreateIncOrDecInfo(builder, OP_KIND_MAP.at(ide.op)); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_IncOrDecExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operands); dbuilder.add_info_type(PackageFormat::ExprInfo_IncOrDecInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const LitConstExpr& lce, const NodeInfo& info) { auto stringIdx = builder.CreateString(lce.stringValue); auto eInfo = PackageFormat::CreateLitConstInfo( builder, stringIdx, LIT_CONST_KIND_MAP.at(lce.kind), STRING_KIND_MAP.at(lce.stringKind)); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_LitConstExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_info_type(PackageFormat::ExprInfo_LitConstInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const BinaryExpr& be, const NodeInfo& info) { CJC_NULLPTR_CHECK(be.leftExpr); CJC_NULLPTR_CHECK(be.rightExpr); std::vector operands; operands.emplace_back(SaveExpr(*be.leftExpr)); operands.emplace_back(SaveExpr(*be.rightExpr)); auto operandsIdx = builder.CreateVector(operands); auto eInfo = PackageFormat::CreateBinaryInfo(builder, OP_KIND_MAP.at(be.op)); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_BinaryExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); dbuilder.add_info_type(PackageFormat::ExprInfo_BinaryInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const SubscriptExpr& se, const NodeInfo& info) { CJC_NULLPTR_CHECK(se.baseExpr); std::vector operands; operands.emplace_back(SaveExpr(*se.baseExpr)); for (auto& idxExpr : se.indexExprs) { CJC_NULLPTR_CHECK(idxExpr); operands.emplace_back(SaveExpr(*idxExpr)); } auto operandsIdx = builder.CreateVector(operands); auto eInfo = PackageFormat::CreateSubscriptInfo(builder, se.isTupleAccess); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_SubscriptExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); dbuilder.add_info_type(PackageFormat::ExprInfo_SubscriptInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const AssignExpr& ae, const NodeInfo& info) { CJC_NULLPTR_CHECK(ae.leftValue); CJC_NULLPTR_CHECK(ae.rightExpr); std::vector operands; operands.emplace_back(SaveExpr(*ae.leftValue)); operands.emplace_back(SaveExpr(*ae.rightExpr)); auto operandsIdx = builder.CreateVector(operands); auto eInfo = PackageFormat::CreateAssignInfo(builder, ae.isCompound, OP_KIND_MAP.at(ae.op)); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_AssignExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); dbuilder.add_info_type(PackageFormat::ExprInfo_AssignInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const ArrayExpr& ae, const NodeInfo& info) { std::vector operands; for (auto& arg : ae.args) { operands.emplace_back(SaveFuncArg(*arg)); } auto operandsIdx = builder.CreateVector(operands); auto initFunc = GetFullDeclIndex(ae.initFunc); auto eInfo = PackageFormat::CreateArrayInfo(builder, initFunc, ae.isValueArray); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_ArrayExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); dbuilder.add_info_type(PackageFormat::ExprInfo_ArrayInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const PointerExpr& pe, const NodeInfo& info) { std::vector operands; if (pe.arg) { operands.emplace_back(SaveFuncArg(*pe.arg)); } auto operandsIdx = builder.CreateVector(operands); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_PointerExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const TypeConvExpr& tce, const NodeInfo& info) { CJC_NULLPTR_CHECK(tce.expr); std::vector operands; operands.emplace_back(SaveExpr(*tce.expr)); auto operandsIdx = builder.CreateVector(operands); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_TypeConvExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const ThrowExpr& te, const NodeInfo& info) { CJC_NULLPTR_CHECK(te.expr); std::vector operands; operands.emplace_back(SaveExpr(*te.expr)); auto operandsIdx = builder.CreateVector(operands); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_ThrowExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const PerformExpr& pe, const NodeInfo& info) { CJC_NULLPTR_CHECK(pe.expr); std::vector operands; operands.emplace_back(SaveExpr(*pe.expr)); auto operandsIdx = builder.CreateVector(operands); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_PerformExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const SpawnExpr& se, const NodeInfo& info) { // Sub node 'task' does not used for SpawnExpr with futureObj. CJC_NULLPTR_CHECK(se.futureObj); std::vector operands; if (se.arg) { // Optional argument. operands.emplace_back(SaveExpr(*se.arg)); } auto operandsIdx = builder.CreateVector(operands); // 'futureObj' is a local decl which should be serialized first. auto targetIdx = SaveDecl(*se.futureObj); auto future = PackageFormat::CreateFullId(builder, CURRENT_PKG_INDEX, 0, targetIdx); auto eInfo = PackageFormat::CreateSpawnInfo(builder, future); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_SpawnExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); dbuilder.add_info_type(PackageFormat::ExprInfo_SpawnInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const ArrayLit& al, const NodeInfo& info) { std::vector operands(al.children.size()); for (size_t i = 0; i < al.children.size(); ++i) { operands[i] = SaveExpr(*al.children[i]); } auto operandsIdx = builder.CreateVector(operands); auto initFunc = GetFullDeclIndex(al.initFunc); auto eInfo = PackageFormat::CreateArrayInfo(builder, initFunc); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_ArrayLit); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); dbuilder.add_info_type(PackageFormat::ExprInfo_ArrayInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const TupleLit& tl, const NodeInfo& info) { std::vector operands(tl.children.size()); for (size_t i = 0; i < tl.children.size(); ++i) { operands[i] = SaveExpr(*tl.children[i]); } auto operandsIdx = builder.CreateVector(operands); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_TupleLit); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const IfExpr& ie, const NodeInfo& info) { CJC_NULLPTR_CHECK(ie.condExpr); CJC_NULLPTR_CHECK(ie.thenBody); std::vector operands; operands.emplace_back(SaveExpr(*ie.condExpr)); operands.emplace_back(SaveExpr(*ie.thenBody)); FormattedIndex elseIdx = ie.elseBody ? SaveExpr(*ie.elseBody) : INVALID_FORMAT_INDEX; operands.emplace_back(elseIdx); auto operandsIdx = builder.CreateVector(operands); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_IfExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const TryExpr& te, const NodeInfo& info) { std::vector resources; for (auto& it : te.resourceSpec) { // Local variables should be saved directly. auto index = SaveDecl(*it); auto fullId = PackageFormat::CreateFullId(builder, CURRENT_PKG_INDEX, 0, index); resources.emplace_back(fullId); } auto targetsIdx = builder.CreateVector({resources}); std::vector operands; operands.emplace_back(SaveExpr(*te.tryBlock)); for (auto& catchBlock : te.catchBlocks) { operands.emplace_back(SaveExpr(*catchBlock)); } // Always save finally index to make size of operands retrievable. operands.emplace_back(te.finallyBlock ? SaveExpr(*te.finallyBlock) : INVALID_FORMAT_INDEX); std::vector patterns; for (auto& pattern : te.catchPatterns) { patterns.emplace_back(SavePattern(*pattern)); } auto operandsIdx = builder.CreateVector(operands); auto patternsIdx = builder.CreateVector(patterns); auto eInfo = PackageFormat::CreateTryInfo(builder, targetsIdx, patternsIdx); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_TryExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); dbuilder.add_info_type(PackageFormat::ExprInfo_TryInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const WhileExpr& we, const NodeInfo& info) { CJC_NULLPTR_CHECK(we.condExpr); CJC_NULLPTR_CHECK(we.body); std::vector operands; operands.emplace_back(SaveExpr(*we.condExpr)); operands.emplace_back(SaveExpr(*we.body)); auto operandsIdx = builder.CreateVector(operands); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_WhileExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const DoWhileExpr& dwe, const NodeInfo& info) { CJC_NULLPTR_CHECK(dwe.body); CJC_NULLPTR_CHECK(dwe.condExpr); std::vector operands; operands.emplace_back(SaveExpr(*dwe.body)); operands.emplace_back(SaveExpr(*dwe.condExpr)); auto operandsIdx = builder.CreateVector(operands); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_DoWhileExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const LambdaExpr& le, const NodeInfo& info) { CJC_NULLPTR_CHECK(le.funcBody); auto funcBody = SaveFuncBody(*le.funcBody); auto eInfo = PackageFormat::CreateLambdaInfo(builder, funcBody, le.TestAttr(Attribute::MOCK_SUPPORTED)); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_LambdaExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_info_type(PackageFormat::ExprInfo_LambdaInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const Block& block, const NodeInfo& info) { auto size = block.body.size(); std::vector operands(size); std::vector childrenCond(size + 1); for (uoffset_t i = 0; i < size; ++i) { auto node = block.body[i].get(); if (auto expr = DynamicCast(node)) { operands[i] = SaveExpr(*expr); childrenCond[i] = true; } else if (auto decl = DynamicCast(node)) { operands[i] = SaveDecl(*decl); childrenCond[i] = false; } else { CJC_ABORT(); } } // Save the 'unsafe' condition as the last value. childrenCond[size] = block.TestAttr(Attribute::UNSAFE); auto operandsIdx = builder.CreateVector(operands); auto condIdx = builder.CreateVector(childrenCond); auto eInfo = PackageFormat::CreateBlockInfo(builder, condIdx); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_Block); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); dbuilder.add_info_type(PackageFormat::ExprInfo_BlockInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const MatchExpr& me, const NodeInfo& info) { std::vector operands; if (me.matchMode) { CJC_NULLPTR_CHECK(me.selector); operands.emplace_back(SaveExpr(*me.selector)); for (auto& matchCase : me.matchCases) { CJC_NULLPTR_CHECK(matchCase); operands.emplace_back(SaveMatchCase(*matchCase)); } } else { for (auto& matchCase : me.matchCaseOthers) { CJC_NULLPTR_CHECK(matchCase); operands.emplace_back(SaveMatchCaseOther(*matchCase)); } } // Do not export for box desugars, it will be re-boxed in imported package.s auto operandsIdx = builder.CreateVector(operands); auto eInfo = PackageFormat::CreateMatchInfo(builder, me.matchMode); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_MatchExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); dbuilder.add_info_type(PackageFormat::ExprInfo_MatchInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const AST::LetPatternDestructor& e, const NodeInfo& info) { std::vector ps{}; for (auto& p : e.patterns) { ps.push_back(SavePattern(*p)); } auto init = SaveExpr(*e.initializer); auto patternsIdx = builder.CreateVector(ps); auto operandsIdx = builder.CreateVector({init}); auto eInfo = PackageFormat::CreateLetPatternDestructorInfo(builder, patternsIdx); PackageFormat::ExprBuilder dbuilder{builder}; dbuilder.add_kind(PackageFormat::ExprKind_LetPatternDestructor); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); dbuilder.add_info_type(PackageFormat::ExprInfo_LetPatternDestructorInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } TExprOffset ASTWriter::ASTWriterImpl::SaveExpression(const ForInExpr& fie, const NodeInfo& info) { std::vector operands; CJC_NULLPTR_CHECK(fie.pattern); TPatternOffset pIdx = SavePattern(*fie.pattern); CJC_NULLPTR_CHECK(fie.inExpression); CJC_NULLPTR_CHECK(fie.body); operands.emplace_back(SaveExpr(*fie.inExpression)); FormattedIndex guradIdx = fie.patternGuard ? SaveExpr(*fie.patternGuard) : INVALID_FORMAT_INDEX; operands.emplace_back(guradIdx); operands.emplace_back(SaveExpr(*fie.body)); auto operandsIdx = builder.CreateVector(operands); auto eInfo = PackageFormat::CreateForInInfo(builder, pIdx, FOR_IN_KIND_MAP.at(fie.forInKind)); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_ForInExpr); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); dbuilder.add_info_type(PackageFormat::ExprInfo_ForInInfo); dbuilder.add_info(eInfo.Union()); return dbuilder.Finish(); } FormattedIndex ASTWriter::ASTWriterImpl::SaveMatchCase(const MatchCase& mc) { FormattedIndex index = static_cast(allExprs.size()) + 1; allExprs.emplace_back(TExprOffset()); auto info = PackNodeInfo(mc); std::vector operands; FormattedIndex guradIdx = mc.patternGuard ? SaveExpr(*mc.patternGuard) : INVALID_FORMAT_INDEX; operands.emplace_back(guradIdx); std::vector patterns(mc.patterns.size()); for (size_t i = 0; i < mc.patterns.size(); ++i) { patterns[i] = SavePattern(*mc.patterns[i]); } CJC_NULLPTR_CHECK(mc.exprOrDecls); operands.emplace_back(SaveExpr(*mc.exprOrDecls)); auto operandsIdx = builder.CreateVector(operands); auto patternsIdx = builder.CreateVector(patterns); auto eInfo = PackageFormat::CreateMatchCaseInfo(builder, patternsIdx); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_MatchCase); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); dbuilder.add_info_type(PackageFormat::ExprInfo_MatchCaseInfo); dbuilder.add_info(eInfo.Union()); allExprs[static_cast(index - 1)] = dbuilder.Finish(); return index; } FormattedIndex ASTWriter::ASTWriterImpl::SaveMatchCaseOther(const MatchCaseOther& mco) { FormattedIndex index = static_cast(allExprs.size()) + 1; allExprs.emplace_back(TExprOffset()); auto info = PackNodeInfo(mco); CJC_NULLPTR_CHECK(mco.matchExpr); CJC_NULLPTR_CHECK(mco.exprOrDecls); std::vector operands; operands.emplace_back(SaveExpr(*mco.matchExpr)); operands.emplace_back(SaveExpr(*mco.exprOrDecls)); auto operandsIdx = builder.CreateVector(operands); PackageFormat::ExprBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::ExprKind_MatchCaseOther); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); allExprs[static_cast(index - 1)] = dbuilder.Finish(); return index; } // FuncArg wrapper, this is directly used when saving 'CallExpr'. FormattedIndex ASTWriter::ASTWriterImpl::SaveFuncArg(const FuncArg& arg) { FormattedIndex index = static_cast(allExprs.size()) + 1; allExprs.emplace_back(TExprOffset()); auto info = PackNodeInfo(arg); // If the function arg is default parameter's placeholder, do not export it's expression. bool isDefaultVal = arg.TestAttr(Attribute::HAS_INITIAL); std::vector operands; if (!isDefaultVal) { operands.emplace_back(SaveExpr(*arg.expr)); } auto operandsIdx = builder.CreateVector(operands); auto eInfo = PackageFormat::CreateFuncArgInfo(builder, arg.withInout, isDefaultVal); PackageFormat::ExprBuilder dbuilder(builder); SaveBasicNodeInfo(dbuilder, info); dbuilder.add_operands(operandsIdx); dbuilder.add_info_type(PackageFormat::ExprInfo_FuncArgInfo); dbuilder.add_info(eInfo.Union()); allExprs[static_cast(index - 1)] = dbuilder.Finish(); return index; } TPatternOffset ASTWriter::ASTWriterImpl::SavePattern(const Pattern& pattern) { switch (pattern.astKind) { case ASTKind::CONST_PATTERN: return SaveConstPattern(StaticCast(pattern)); case ASTKind::WILDCARD_PATTERN: return SaveWildcardPattern(StaticCast(pattern)); case ASTKind::VAR_PATTERN: return SaveVarPattern(StaticCast(pattern)); case ASTKind::TUPLE_PATTERN: return SaveTuplePattern(StaticCast(pattern)); case ASTKind::TYPE_PATTERN: return SaveTypePattern(StaticCast(pattern)); case ASTKind::ENUM_PATTERN: return SaveEnumPattern(StaticCast(pattern)); case ASTKind::EXCEPT_TYPE_PATTERN: return SaveExceptTypePattern(StaticCast(pattern)); case ASTKind::COMMAND_TYPE_PATTERN: return SaveCommandTypePattern(StaticCast(pattern)); case ASTKind::VAR_OR_ENUM_PATTERN: { auto& vep = StaticCast(pattern); CJC_NULLPTR_CHECK(vep.pattern); return SavePattern(*vep.pattern); } default: // Should be unreachable. CJC_ABORT(); return TPatternOffset(); } } TPatternOffset ASTWriter::ASTWriterImpl::SaveConstPattern(const ConstPattern& cp) { CJC_NULLPTR_CHECK(cp.literal); std::vector exprs; exprs.emplace_back(SaveExpr(*cp.literal)); if (cp.operatorCallExpr) { exprs.emplace_back(SaveExpr(*cp.operatorCallExpr)); } auto info = PackNodeInfo(cp); auto tyIdx = builder.CreateVector({info.ty}); auto exprsIdx = builder.CreateVector(exprs); PackageFormat::PatternBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::PatternKind_ConstPattern); dbuilder.add_begin(&info.begin); dbuilder.add_end(&info.end); dbuilder.add_exprs(exprsIdx); dbuilder.add_types(tyIdx); return dbuilder.Finish(); } TPatternOffset ASTWriter::ASTWriterImpl::SaveWildcardPattern(const WildcardPattern& wp) { auto info = PackNodeInfo(wp); auto tyIdx = builder.CreateVector({info.ty}); PackageFormat::PatternBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::PatternKind_WildcardPattern); dbuilder.add_begin(&info.begin); dbuilder.add_end(&info.end); dbuilder.add_types(tyIdx); return dbuilder.Finish(); } TPatternOffset ASTWriter::ASTWriterImpl::SaveVarPattern(const VarPattern& vp) { CJC_NULLPTR_CHECK(vp.varDecl); auto declIdx = SaveDecl(*vp.varDecl); auto info = PackNodeInfo(vp); auto tyIdx = builder.CreateVector({info.ty}); auto exprsIdx = builder.CreateVector({declIdx}); PackageFormat::PatternBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::PatternKind_VarPattern); dbuilder.add_begin(&info.begin); dbuilder.add_end(&info.end); dbuilder.add_types(tyIdx); dbuilder.add_exprs(exprsIdx); return dbuilder.Finish(); } TPatternOffset ASTWriter::ASTWriterImpl::SaveTuplePattern(const TuplePattern& tp) { std::vector patterns(tp.patterns.size()); for (size_t i = 0; i < tp.patterns.size(); ++i) { patterns[i] = SavePattern(*tp.patterns[i]); } auto info = PackNodeInfo(tp); auto tyIdx = builder.CreateVector({info.ty}); auto patternsIdx = builder.CreateVector(patterns); PackageFormat::PatternBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::PatternKind_TuplePattern); dbuilder.add_begin(&info.begin); dbuilder.add_end(&info.end); dbuilder.add_types(tyIdx); dbuilder.add_patterns(patternsIdx); return dbuilder.Finish(); } TPatternOffset ASTWriter::ASTWriterImpl::SaveTypePattern(const TypePattern& tp) { CJC_NULLPTR_CHECK(tp.pattern); auto pIdx = SavePattern(*tp.pattern); auto info = PackNodeInfo(tp); auto tyIdx = builder.CreateVector({info.ty}); auto patternsIdx = builder.CreateVector({pIdx}); PackageFormat::PatternBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::PatternKind_TypePattern); dbuilder.add_begin(&info.begin); dbuilder.add_end(&info.end); dbuilder.add_types(tyIdx); dbuilder.add_patterns(patternsIdx); dbuilder.add_matchBeforeRuntime(tp.matchBeforeRuntime); dbuilder.add_needRuntimeTypeCheck(tp.needRuntimeTypeCheck); return dbuilder.Finish(); } TPatternOffset ASTWriter::ASTWriterImpl::SaveEnumPattern(const EnumPattern& ep) { CJC_NULLPTR_CHECK(ep.constructor); auto ctor = SaveExpr(*ep.constructor); std::vector patterns(ep.patterns.size()); for (size_t i = 0; i < ep.patterns.size(); ++i) { patterns[i] = SavePattern(*ep.patterns[i]); } auto info = PackNodeInfo(ep); auto tyIdx = builder.CreateVector({info.ty}); auto exprsIdx = builder.CreateVector({ctor}); auto patternsIdx = builder.CreateVector(patterns); PackageFormat::PatternBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::PatternKind_EnumPattern); dbuilder.add_begin(&info.begin); dbuilder.add_end(&info.end); dbuilder.add_types(tyIdx); dbuilder.add_exprs(exprsIdx); dbuilder.add_patterns(patternsIdx); return dbuilder.Finish(); } TPatternOffset ASTWriter::ASTWriterImpl::SaveExceptTypePattern(const ExceptTypePattern& etp) { auto pIdx = SavePattern(*etp.pattern); auto info = PackNodeInfo(etp); std::vector types{info.ty}; for (auto& type : etp.types) { CJC_NULLPTR_CHECK(type); types.emplace_back(SaveType(type->ty)); } auto tyIdx = builder.CreateVector(types); auto patternsIdx = builder.CreateVector({pIdx}); PackageFormat::PatternBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::PatternKind_ExceptTypePattern); dbuilder.add_begin(&info.begin); dbuilder.add_end(&info.end); dbuilder.add_types(tyIdx); dbuilder.add_patterns(patternsIdx); return dbuilder.Finish(); } TPatternOffset ASTWriter::ASTWriterImpl::SaveCommandTypePattern(const CommandTypePattern& ctp) { auto pIdx = SavePattern(*ctp.pattern); auto info = PackNodeInfo(ctp); std::vector types{info.ty}; for (auto& type : ctp.types) { CJC_NULLPTR_CHECK(type); types.emplace_back(SaveType(type->ty)); } auto tyIdx = builder.CreateVector(types); auto patternsIdx = builder.CreateVector({pIdx}); PackageFormat::PatternBuilder dbuilder(builder); dbuilder.add_kind(PackageFormat::PatternKind_CommandTypePattern); dbuilder.add_begin(&info.begin); dbuilder.add_end(&info.end); dbuilder.add_types(tyIdx); dbuilder.add_patterns(patternsIdx); return dbuilder.Finish(); } cangjie_compiler-1.0.7/src/Modules/ASTSerialization/IncrementalLoader.cpp000066400000000000000000000371011510705540100264710ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * This file implements the AST Loader related classes. */ #include "ASTLoaderImpl.h" #include #include "flatbuffers/ModuleFormat_generated.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/Version.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/AST/ASTCasting.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/IncrementalCompilation/IncrementalCompilationLogger.h" using namespace Cangjie; using namespace AST; namespace { inline bool IsChangedDeclMayOmitType(const Decl& decl) { // Return type of constructor is always omitted and will not be changed. return decl.toBeCompiled && !decl.TestAttr(Attribute::CONSTRUCTOR) && (decl.astKind == ASTKind::FUNC_DECL || decl.astKind == ASTKind::VAR_DECL || decl.TestAttr(Attribute::GENERIC)); } inline bool DoNotLoadCache(const Decl& decl) { return Utils::In(decl.astKind, {ASTKind::PRIMARY_CTOR_DECL, ASTKind::TYPE_ALIAS_DECL, ASTKind::FUNC_PARAM}); } } // namespace /** For incremental compilation to loading cached types. */ std::unordered_set ASTLoader::LoadCachedTypeForPackage( const Package& sourcePackage, const std::map>& mangledName2DeclMap) { return pImpl->LoadCachedTypeForPackage(sourcePackage, mangledName2DeclMap); } void ASTLoader::ASTLoaderImpl::LoadCachedTypeForDecl(const PackageFormat::Decl& decl, Decl& astDecl) { astDecl.exportId = decl.exportId()->str(); astDecl.mangledName = decl.mangledName()->str(); // Add target for type decl. AddDeclToImportedPackage(astDecl); if (auto fd = DynamicCast(&astDecl); fd && fd->funcBody && !fd->funcBody->paramLists.empty()) { auto info = decl.info_as_FuncInfo(); CJC_NULLPTR_CHECK(info); CJC_NULLPTR_CHECK(info->funcBody()); auto paramList = info->funcBody()->paramLists()->Get(0); uoffset_t length = static_cast(paramList->params()->size()); auto paramLen = fd->funcBody->paramLists[0]->params.size(); CJC_ASSERT(paramLen == length); for (uoffset_t offset = 0; offset < length; ++offset) { auto index = paramList->params()->Get(offset); (void)allLoadedDecls.emplace(index, fd->funcBody->paramLists[0]->params[offset].get()); } } // Load target for unchanged annotation decl. if (!astDecl.toBeCompiled && GetAttributes(decl).TestAttr(Attribute::IS_ANNOTATION)) { astDecl.EnableAttr(Attribute::IS_ANNOTATION); auto& annotations = astDecl.annotations; auto found = std::find_if(annotations.begin(), annotations.end(), [](const auto& it) { return it->kind == AnnotationKind::ANNOTATION; }); CJC_ASSERT(found != annotations.end()); auto info = decl.info_as_ClassInfo(); CJC_NULLPTR_CHECK(info); found->get()->target = info->annoTargets(); found->get()->runtimeVisible = info->runtimeVisible(); } // Add target for generic parameter decl. if (auto generic = astDecl.GetGeneric(); generic && decl.generic()) { auto genericIdx = decl.generic(); generic->EnableAttr(Attribute::INCRE_COMPILE); CJC_NULLPTR_CHECK(genericIdx->typeParameters()); uoffset_t length = static_cast(genericIdx->typeParameters()->size()); auto paramLen = generic->typeParameters.size(); CJC_ASSERT(paramLen == length); for (uoffset_t i = 0; i < length; i++) { auto gpdIdx = genericIdx->typeParameters()->Get(i); CJC_ASSERT(gpdIdx != INVALID_FORMAT_INDEX); auto gpd = generic->typeParameters[i].get(); gpd->exportId = GetFormatDeclByIndex(gpdIdx)->exportId()->str(); gpd->outerDecl = &astDecl; AddDeclToImportedPackage(*gpd); (void)allLoadedDecls.emplace(gpdIdx, gpd); } } } void ASTLoader::ASTLoaderImpl::PrepareForLoadTypeCache(const Package& pkg) { for (uoffset_t i = 0; i < package->imports()->size(); i++) { std::string importItem = package->imports()->Get(i)->str(); importedFullPackageNames.emplace_back(importItem); } importedPackageName = pkg.fullPackageName; // Default file ids' values for cache are 0 which is kept for dummy 'LoadPos' usage during loding cached type. // NOTE: Size should be the lager value between current and privous number of files. allFileIds.resize(std::max(pkg.files.size(), static_cast(package->allFiles()->size()))); // Prepare file id info for loading cache. for (uint32_t i = 0; i < pkg.files.size(); ++i) { allFileIds[i] = pkg.files[i]->begin.fileID; } } void ASTLoader::ASTLoaderImpl::CollectRemovedDefaultImpl( const Decl& astDecl, const PackageFormat::Decl& decl, std::unordered_set& needRemoved) const { auto body = astDecl.astKind == ASTKind::CLASS_DECL ? decl.info_as_ClassInfo()->body() : astDecl.astKind == ASTKind::INTERFACE_DECL ? decl.info_as_InterfaceInfo()->body() : astDecl.astKind == ASTKind::ENUM_DECL ? decl.info_as_EnumInfo()->body() : astDecl.astKind == ASTKind::STRUCT_DECL ? decl.info_as_StructInfo()->body() : astDecl.astKind == ASTKind::EXTEND_DECL ? decl.info_as_ExtendInfo()->body() : nullptr; if (!astDecl.toBeCompiled || !body) { return; } for (auto index : *body) { auto member = GetFormatDeclByIndex(index); CJC_NULLPTR_CHECK(member); // Copied default implementation inside changed decl should be removed. if (member->mangledBeforeSema()->str().empty() && GetAttributes(*member).TestAttr(Attribute::DEFAULT)) { CollectRemovedDecls(*member, needRemoved); } } } namespace { void CollectRemovedDecl(const std::string& mangledName, std::unordered_set& needRemoved) { needRemoved.emplace(mangledName); if (IncrementalCompilationLogger::GetInstance().IsEnable()) { IncrementalCompilationLogger::GetInstance().LogLn("[CollectRemovedDecl] removed mangled: " + mangledName); } } } // namespace void ASTLoader::ASTLoaderImpl::CollectRemovedDecls( const PackageFormat::Decl& decl, std::unordered_set& needRemoved) const { if (decl.kind() == PackageFormat::DeclKind_FuncDecl) { CollectRemovedDecl(decl.mangledName()->str(), needRemoved); auto info = decl.info_as_FuncInfo(); CJC_NULLPTR_CHECK(info); CJC_ASSERT(info->funcBody() && info->funcBody()->paramLists()); auto fpls = info->funcBody()->paramLists(); CJC_NULLPTR_CHECK(fpls); for (auto fpl : *fpls) { auto desugars = fpl->desugars(); CJC_NULLPTR_CHECK(desugars); for (auto desugarIndex : *fpl->desugars()) { if (desugarIndex == INVALID_FORMAT_INDEX) { continue; } auto member = GetFormatDeclByIndex(desugarIndex); CJC_NULLPTR_CHECK(member); CollectRemovedDecl(member->mangledName()->str(), needRemoved); } } } else if (decl.kind() == PackageFormat::DeclKind_PropDecl) { auto info = decl.info_as_PropInfo(); CJC_NULLPTR_CHECK(info); for (auto index : *info->setters()) { auto setter = GetFormatDeclByIndex(index); CollectRemovedDecl(setter->mangledName()->str(), needRemoved); } for (auto index : *info->getters()) { auto getter = GetFormatDeclByIndex(index); CollectRemovedDecl(getter->mangledName()->str(), needRemoved); } } else if (decl.kind() == PackageFormat::DeclKind_VarDecl) { auto attr = GetAttributes(decl); if (attr.TestAttr(Attribute::GLOBAL) || attr.TestAttr(Attribute::STATIC)) { CollectRemovedDecl(decl.mangledName()->str(), needRemoved); } } else { auto body = decl.kind() == PackageFormat::DeclKind_ClassDecl ? decl.info_as_ClassInfo()->body() : decl.kind() == PackageFormat::DeclKind_InterfaceDecl ? decl.info_as_InterfaceInfo()->body() : decl.kind() == PackageFormat::DeclKind_EnumDecl ? decl.info_as_EnumInfo()->body() : decl.kind() == PackageFormat::DeclKind_StructDecl ? decl.info_as_StructInfo()->body() : decl.kind() == PackageFormat::DeclKind_ExtendDecl ? decl.info_as_ExtendInfo()->body() : nullptr; if (!body) { return; } if (decl.kind() != PackageFormat::DeclKind_ExtendDecl) { CollectRemovedDecl(decl.mangledName()->str(), needRemoved); } for (auto index : *body) { auto member = GetFormatDeclByIndex(index); CJC_NULLPTR_CHECK(member); CollectRemovedDecls(*member, needRemoved); } } } void ASTLoader::ASTLoaderImpl::ClearInstantiatedCache( const std::vector& instantiatedDeclIndexes, std::unordered_set& needRemoved) { // Load previous generated instantiated decls. for (auto idx : instantiatedDeclIndexes) { auto declInfo = GetFormatDeclByIndex(idx); auto genericDecl = GetDeclFromIndex(declInfo->genericDecl()); if (!genericDecl || genericDecl->toBeCompiled) { // Ignore the instantiated version when: // 1. The generic version is not found from current package; // 2. The generic version is modified and need to be recompiled. CollectRemovedDecls(*declInfo, needRemoved); } } } template void ASTLoader::ASTLoaderImpl::LoadDesugarDecl(DeclT& decl, uoffset_t index) { if (decl.toBeCompiled) { return; } // Load desugared decl if current main or macro decl does not need to be compiled in incremental step. decl.desugarDecl = LoadDecl(index); CJC_NULLPTR_CHECK(decl.desugarDecl); decl.desugarDecl->EnableAttr(Attribute::INCRE_COMPILE); decl.desugarDecl->DisableAttr(Attribute::IMPORTED); } bool ASTLoader::ASTLoaderImpl::HasInlineDefaultParamFunc(const PackageFormat::Decl& decl) const { std::vector ret; if (decl.kind() == PackageFormat::DeclKind_FuncDecl) { ret.push_back(decl.mangledName()->str()); auto info = decl.info_as_FuncInfo(); CJC_NULLPTR_CHECK(info); CJC_ASSERT(info->funcBody() && info->funcBody()->paramLists()); auto pl = info->funcBody()->paramLists(); CJC_NULLPTR_CHECK(pl); for (auto l : *pl) { auto desugars = l->desugars(); CJC_NULLPTR_CHECK(desugars); for (auto desugarIndex : *desugars) { if (desugarIndex == INVALID_FORMAT_INDEX) { continue; } auto& desugarDecl = *GetFormatDeclByIndex(desugarIndex); auto attrs = GetAttributes(desugarDecl); auto info_ = desugarDecl.info_as_FuncInfo(); if (attrs.TestAttr(Attribute::HAS_INITIAL) && attrs.TestAttr(Attribute::IMPLICIT_ADD) && info_ && info_->isInline() && cjoManager.GetCanInline()) { return true; } } } } return false; } // Should be called after import package, before typecheck. std::unordered_set ASTLoader::ASTLoaderImpl::LoadCachedTypeForPackage( const Package& sourcePackage, const std::map>& mangledName2DeclMap) { // 1. Verify data and prepare cache for imported package name. if (!VerifyForData("cached type")) { return {}; } CacheLoadingStatus ctx(isLoadCache); package = PackageFormat::GetPackage(data.data()); // 2. Prepare cache for file ids. // NOTE: 'ASTDiff' guarantees files in previous compilation are same as the current compilation. // So the 'fileID' is same with the 'fileIndex'. PrepareForLoadTypeCache(sourcePackage); // 3. Load type cache for decls which are not changed, and collect some unfounded for incr compile to remove auto& logger = IncrementalCompilationLogger::GetInstance(); logger.LogLn("[LoadCachedTypeForPackage] begin collect some unfounded for incr compile to remove:"); std::vector instantiatedDeclIndexes; std::vector genericIdx; std::unordered_set unfounded; for (uoffset_t i = 0; i < package->allDecls()->size(); i++) { // Only toplevel decls are loaded. // NOTE: FormattedIndex is vector offset plus 1. auto realIdx = i + 1; auto decl = GetFormatDeclByIndex(realIdx); CJC_NULLPTR_CHECK(decl); if (GetAttributes(*decl).TestAttr(Attribute::GENERIC_INSTANTIATED)) { instantiatedDeclIndexes.emplace_back(realIdx); continue; } auto mangleIdx = decl->mangledBeforeSema(); CJC_NULLPTR_CHECK(mangleIdx); auto found = mangledName2DeclMap.find(mangleIdx->str()); if (found == mangledName2DeclMap.end()) { // Record 'mangledName' of decls which have rawMangledName. if (!mangleIdx->str().empty()) { CollectRemovedDecls(*decl, unfounded); } continue; } if (IsChangedDeclMayOmitType(*found->second) || DoNotLoadCache(*found->second)) { if (found->second->TestAttr(Attribute::GENERIC)) { // Add generic index for finding generic reference of instantiation. allLoadedDecls.emplace(realIdx, found->second); genericIdx.emplace_back(realIdx); } continue; } if (auto mainDecl = DynamicCast(found->second)) { LoadDesugarDecl(*mainDecl, realIdx); } else if (auto macroDecl = DynamicCast(found->second)) { LoadDesugarDecl(*macroDecl, realIdx); } else { LoadCachedTypeForDecl(*decl, *found->second); CollectRemovedDefaultImpl(*found->second, *decl, unfounded); (void)allLoadedDecls.emplace(realIdx, found->second); // If the decl's definition body was exported, it should be marked as re-compiled. auto info = decl->info_as_FuncInfo(); auto index = decl->info_as_VarInfo() ? decl->info_as_VarInfo()->initializer() : decl->info_as_VarWithPatternInfo() ? decl->info_as_VarWithPatternInfo()->initializer() : decl->info_as_AliasInfo() ? decl->info_as_AliasInfo()->aliasedTy() : decl->info_as_ParamInfo() ? decl->info_as_ParamInfo()->defaultVal() : INVALID_FORMAT_INDEX; if (index != INVALID_FORMAT_INDEX || (info && info->isInline() && cjoManager.GetCanInline()) || HasInlineDefaultParamFunc(*decl)) { found->second->toBeCompiled = true; } } } // 3. Clear instantiated cache, and remove generic index from 'allLoadedDecls' ClearInstantiatedCache(instantiatedDeclIndexes, unfounded); std::for_each(genericIdx.begin(), genericIdx.end(), [this](auto idx) { allLoadedDecls.erase(idx); }); // NOTE: // For first stage of incremental compilation, the source imported non-generic decls in caced packge can be ignored. // That kind of decl is not allowed in incremental compilation. // NOT support LSP situation for now. allTypes.resize(package->allTypes()->size(), nullptr); LoadRefs(); return unfounded; } cangjie_compiler-1.0.7/src/Modules/ASTSerialization/JavaMangler.h000066400000000000000000000025771510705540100247460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the JavaMangler used to AST Writer. */ #ifndef CANGJIE_MODULES_ASTSERIALIZATION_JAVA_MANGLER_H #define CANGJIE_MODULES_ASTSERIALIZATION_JAVA_MANGLER_H #include "cangjie/AST/Types.h" #include "cangjie/Mangle/BaseMangler.h" namespace Cangjie { bool ContainJavaGenerics(const AST::Ty& ty) { if (!AST::Ty::IsTyCorrect(&ty)) { return false; } if (AST::IsJClassOrInterface(ty) && !ty.typeArgs.empty()) { return true; } for (auto tyArg : ty.typeArgs) { if (ContainJavaGenerics(*tyArg)) { return true; } } return false; } class JavaMangler : public BaseMangler { public: bool NeedRemangle(const AST::FuncDecl& funcDecl) const override { bool isMemberFunc = funcDecl.outerDecl && funcDecl.TestAnyAttr(AST::Attribute::IN_CLASSLIKE, AST::Attribute::IN_ENUM, AST::Attribute::IN_STRUCT, AST::Attribute::IN_EXTEND); return exportIdMode && (ContainJavaGenerics(*funcDecl.ty) || (isMemberFunc && ContainJavaGenerics(*funcDecl.outerDecl->ty))); } }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Modules/ASTSerialization/Mapping.inc000066400000000000000000000105131510705540100244610ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifdef ACCESS_LEVEL ACCESS_LEVEL(PUBLIC, Public) ACCESS_LEVEL(PROTECTED, Protected) ACCESS_LEVEL(INTERNAL, Internal) #endif #ifdef ACCESS_MODIFIER ACCESS_MODIFIER(PUBLIC, Public) ACCESS_MODIFIER(PROTECTED, Protected) ACCESS_MODIFIER(INTERNAL, Internal) ACCESS_MODIFIER(PRIVATE, Private) #endif #ifdef BUILTIN_TYPE BUILTIN_TYPE(ARRAY, Array) BUILTIN_TYPE(VARRAY, VArray) BUILTIN_TYPE(POINTER, CPointer) BUILTIN_TYPE(CSTRING, CString) BUILTIN_TYPE(CFUNC, CFunc) #endif #ifdef OVERFLOW_STRATEGY OVERFLOW_STRATEGY(NA, NA) OVERFLOW_STRATEGY(CHECKED, Checked) OVERFLOW_STRATEGY(WRAPPING, Wrapping) OVERFLOW_STRATEGY(THROWING, Throwing) OVERFLOW_STRATEGY(SATURATING, Saturating) #endif #ifdef TYPE_KIND TYPE_KIND(TYPE_UNIT, Unit) TYPE_KIND(TYPE_INT8, Int8) TYPE_KIND(TYPE_INT16, Int16) TYPE_KIND(TYPE_INT32, Int32) TYPE_KIND(TYPE_INT64, Int64) TYPE_KIND(TYPE_INT_NATIVE, IntNative) TYPE_KIND(TYPE_UINT8, UInt8) TYPE_KIND(TYPE_UINT16, UInt16) TYPE_KIND(TYPE_UINT32, UInt32) TYPE_KIND(TYPE_UINT64, UInt64) TYPE_KIND(TYPE_UINT_NATIVE, UIntNative) TYPE_KIND(TYPE_FLOAT16, Float16) TYPE_KIND(TYPE_FLOAT32, Float32) TYPE_KIND(TYPE_FLOAT64, Float64) TYPE_KIND(TYPE_RUNE, Rune) TYPE_KIND(TYPE_NOTHING, Nothing) TYPE_KIND(TYPE_BOOLEAN, Bool) TYPE_KIND(TYPE_TUPLE, Tuple) TYPE_KIND(TYPE_ENUM, Enum) TYPE_KIND(TYPE_FUNC, Func) TYPE_KIND(TYPE_STRUCT, Struct) TYPE_KIND(TYPE_ARRAY, Array) TYPE_KIND(TYPE_VARRAY, VArray) TYPE_KIND(TYPE_POINTER, CPointer) TYPE_KIND(TYPE_CSTRING, CString) TYPE_KIND(TYPE_CLASS, Class) TYPE_KIND(TYPE_INTERFACE, Interface) TYPE_KIND(TYPE, Type) TYPE_KIND(TYPE_GENERICS, Generic) #endif #ifdef OPERATOR_KIND OPERATOR_KIND(ILLEGAL, NA) OPERATOR_KIND(LPAREN, Call) OPERATOR_KIND(LSQUARE, Index) OPERATOR_KIND(NOT, Not) OPERATOR_KIND(SUB, Subtract) OPERATOR_KIND(EXP, Power) OPERATOR_KIND(MUL, Multiply) OPERATOR_KIND(DIV, Divide) OPERATOR_KIND(MOD, Remainder) OPERATOR_KIND(ADD, Add) OPERATOR_KIND(LSHIFT, BitLeftShift) OPERATOR_KIND(RSHIFT, BitRightShift) OPERATOR_KIND(LT, LT) OPERATOR_KIND(LE, LE) OPERATOR_KIND(GT, GT) OPERATOR_KIND(GE, GE) OPERATOR_KIND(EQUAL, Equal) OPERATOR_KIND(NOTEQ, NotEqual) OPERATOR_KIND(BITAND, BitAnd) OPERATOR_KIND(BITXOR, BitXor) OPERATOR_KIND(BITOR, BitOr) OPERATOR_KIND(INCR, PostInc) OPERATOR_KIND(DECR, PostDec) OPERATOR_KIND(IS, Is) OPERATOR_KIND(AS, As) OPERATOR_KIND(AND, LogicAnd) OPERATOR_KIND(OR, LogicOr) OPERATOR_KIND(COALESCING, Coalescing) OPERATOR_KIND(PIPELINE, Pipeline) OPERATOR_KIND(COMPOSITION, Composition) OPERATOR_KIND(ASSIGN, Assign) OPERATOR_KIND(EXP_ASSIGN, PowerAssign) OPERATOR_KIND(MUL_ASSIGN, MultiplyAssign) OPERATOR_KIND(DIV_ASSIGN, DivideAssign) OPERATOR_KIND(MOD_ASSIGN, RemainderAssign) OPERATOR_KIND(ADD_ASSIGN, AddAssign) OPERATOR_KIND(SUB_ASSIGN, SubtractAssign) OPERATOR_KIND(LSHIFT_ASSIGN, LeftShiftAssign) OPERATOR_KIND(RSHIFT_ASSIGN, RightShiftAssign) OPERATOR_KIND(BITAND_ASSIGN, BitAndAssign) OPERATOR_KIND(BITXOR_ASSIGN, BitXorAssign) OPERATOR_KIND(BITOR_ASSIGN, BitOrAssign) OPERATOR_KIND(AND_ASSIGN, LogicAndAssign) OPERATOR_KIND(OR_ASSIGN, LogicOrAssign) #endif #ifdef CALL_KIND CALL_KIND(CALL_INVALID, NA) CALL_KIND(CALL_DECLARED_FUNCTION, CallDeclaredFunction) CALL_KIND(CALL_OBJECT_CREATION, CallObjectCreation) CALL_KIND(CALL_FUNCTION_PTR, CallStructCreation) CALL_KIND(CALL_STRUCT_CREATION, CallSuperFunction) CALL_KIND(CALL_SUPER_FUNCTION, CallVariadicFunction) CALL_KIND(CALL_VARIADIC_FUNCTION, CallFunctionPtr) CALL_KIND(CALL_ANNOTATION, CallAnnotation) CALL_KIND(CALL_BUILTIN_FUNCTION, CallBuiltinFunction) CALL_KIND(CALL_INTRINSIC_FUNCTION, CallIntrinsicFunction) #endif #ifdef LIT_CONST_KIND LIT_CONST_KIND(INTEGER, Integer) LIT_CONST_KIND(RUNE_BYTE, RuneByte) LIT_CONST_KIND(FLOAT, Float) LIT_CONST_KIND(RUNE, Rune) LIT_CONST_KIND(STRING, String) LIT_CONST_KIND(JSTRING, JString) LIT_CONST_KIND(BOOL, Bool) LIT_CONST_KIND(UNIT, Unit) #endif #ifdef STRING_KIND STRING_KIND(NORMAL, Normal) STRING_KIND(JSTRING, JString) STRING_KIND(MULTILINE, MultiLine) STRING_KIND(MULTILINE_RAW, MultiLineRaw) #endif #ifdef FOR_IN_KIND FOR_IN_KIND(FORIN_INVALID, NA) FOR_IN_KIND(FORIN_RANGE, Range) FOR_IN_KIND(FORIN_STRING, String) FOR_IN_KIND(FORIN_ITER, Iterator) #endif cangjie_compiler-1.0.7/src/Modules/ASTSerialization/ReferenceLoader.cpp000066400000000000000000000700401510705540100261250ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * This file implements the AST Loader related classes. */ #include "ASTLoaderImpl.h" #include "flatbuffers/ModuleFormat_generated.h" #include "cangjie/AST/Utils.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/AST/ASTCasting.h" using namespace Cangjie; using namespace AST; namespace { void SetFuncParentReference(const FuncDecl& fd, Decl& parentDecl) { CJC_ASSERT(fd.funcBody); if (parentDecl.IsClassLikeDecl()) { fd.funcBody->parentClassLike = RawStaticCast(&parentDecl); } else if (parentDecl.astKind == ASTKind::STRUCT_DECL) { fd.funcBody->parentStruct = RawStaticCast(&parentDecl); } else if (parentDecl.astKind == ASTKind::ENUM_DECL) { fd.funcBody->parentEnum = RawStaticCast(&parentDecl); } } void SetMemberDeclReference(Decl& member, Decl& parentDecl) { auto parentTypeDecl = &parentDecl; if (parentDecl.astKind == ASTKind::EXTEND_DECL) { if (auto extendedDecl = Ty::GetDeclPtrOfTy(parentDecl.ty)) { parentTypeDecl = extendedDecl; } } if (auto propDecl = DynamicCast(&member)) { for (auto& get : propDecl->getters) { SetFuncParentReference(*get, *parentTypeDecl); } for (auto& set : propDecl->setters) { SetFuncParentReference(*set, *parentTypeDecl); } } else if (auto func = DynamicCast(&member)) { SetFuncParentReference(*func, *parentTypeDecl); if (func->funcBody->paramLists.empty()) { return; } for (auto& param : func->funcBody->paramLists[0]->params) { if (param->desugarDecl) { SetFuncParentReference(*param->desugarDecl, *parentTypeDecl); } } } } // Wrap ty using RefType without identifier. OwnedPtr WrapTypeInNode(Ptr type) { auto refType = MakeOwned(); refType->ty = type; if (auto decl = Ty::GetDeclPtrOfTy(type)) { refType->ref.target = decl; refType->ref.identifier = Is(refType->ty) ? "This" : decl->identifier.Val(); } refType->EnableAttr(Attribute::IMPORTED); refType->EnableAttr(Attribute::COMPILER_ADD); refType->EnableAttr(Attribute::IS_CHECK_VISITED); return refType; } void UpdateType(OwnedPtr& astType, OwnedPtr loadedType) { CJC_NULLPTR_CHECK(loadedType); if (astType == nullptr) { astType = std::move(loadedType); } else if (Ty::IsTyCorrect(loadedType->ty)) { // If type is invalid, it means the type decl is need to be recompiled, do not load type cache further. astType->ty = loadedType->ty; astType->EnableAttr(Attribute::IS_CHECK_VISITED); if (auto rt = DynamicCast(astType.get())) { rt->ref.target = loadedType->GetTarget(); } else if (auto qt = DynamicCast(astType.get())) { qt->target = loadedType->GetTarget(); } } } void PostLoadReference(Expr& expr) { switch (expr.astKind) { case ASTKind::CALL_EXPR: { auto& ce = StaticCast(expr); // Only copy expr's ty to arg's ty for 'ArrayExpr' & 'PointerExpr', // Type of 'CallExpr' 's argTy may be different from argExprTy when 'withInout' is true, // it is loaded during 'LoadSubNodeRefs'. CJC_NULLPTR_CHECK(ce.baseFunc); ce.resolvedFunction = DynamicCast(ce.baseFunc->GetTarget()); break; } case ASTKind::ARRAY_EXPR: for (auto& arg : StaticCast(expr).args) { CJC_NULLPTR_CHECK(arg->expr); arg->ty = arg->expr->ty; } break; case ASTKind::POINTER_EXPR: { auto& arg = StaticCast(expr).arg; if (arg) { CJC_NULLPTR_CHECK(arg->expr); arg->ty = arg->expr->ty; } break; } default: break; } } } // namespace Ptr ASTLoader::LoadType(FormattedIndex type) const { CJC_NULLPTR_CHECK(pImpl); return pImpl->LoadType(type); } void ASTLoader::SetIsChirNow(bool isChirNow) { CJC_NULLPTR_CHECK(pImpl); pImpl->isChirNow = isChirNow; } void ASTLoader::ASTLoaderImpl::InitializeTypeLoader() { tyLoaderMap = { {PackageFormat::TypeKind_CPointer, &ASTLoaderImpl::SetTypeTy}, {PackageFormat::TypeKind_Array, &ASTLoaderImpl::SetTypeTy}, {PackageFormat::TypeKind_VArray, &ASTLoaderImpl::SetTypeTy}, {PackageFormat::TypeKind_CString, &ASTLoaderImpl::SetTypeTy}, {PackageFormat::TypeKind_Struct, &ASTLoaderImpl::SetTypeTy}, {PackageFormat::TypeKind_Enum, &ASTLoaderImpl::SetTypeTy}, {PackageFormat::TypeKind_Interface, &ASTLoaderImpl::SetTypeTy}, {PackageFormat::TypeKind_Class, &ASTLoaderImpl::SetTypeTy}, {PackageFormat::TypeKind_Type, &ASTLoaderImpl::SetTypeTy}, {PackageFormat::TypeKind_Tuple, &ASTLoaderImpl::SetTypeTy}, {PackageFormat::TypeKind_Func, &ASTLoaderImpl::SetTypeTy}, {PackageFormat::TypeKind_Generic, &ASTLoaderImpl::SetGenericTy}, }; } void ASTLoader::LoadRefs() const { CJC_NULLPTR_CHECK(pImpl); pImpl->LoadRefs(); } void ASTLoader::ASTLoaderImpl::LoadRefs() { for (auto [index, decl] : allLoadedDecls) { auto declObj = GetFormatDeclByIndex(static_cast(index)); CJC_NULLPTR_CHECK(declObj); CJC_NULLPTR_CHECK(decl); LoadDeclRefs(*declObj, *decl); LoadDeclDependencies(*declObj, *decl); } for (auto [index, expr] : allLoadedExprs) { auto exprObj = GetFormatExprByIndex(static_cast(index)); CJC_NULLPTR_CHECK(exprObj); CJC_NULLPTR_CHECK(expr); LoadExprRefs(*exprObj, *expr); } // Set type for created dummy expressions. for (auto dummyExpr : allDummyExprs) { CJC_ASSERT(dummyExpr->desugarExpr); dummyExpr->ty = dummyExpr->desugarExpr->ty; } // NOTE: funcArg expr's type reference is loaded after funcArg itself, // so we need to load for arg's ty after finish loading all reference for expression. // Also callExpr is loading before call base expr, loading possibly 'resolvedFunction' here. for (auto [_, expr] : allLoadedExprs) { PostLoadReference(*expr); } } // Load and reproduce ty according to type index, the mapping pointers are constructed using decl index. Ptr ASTLoader::ASTLoaderImpl::LoadType(FormattedIndex type) { if (type == INVALID_FORMAT_INDEX) { return TypeManager::GetInvalidTy(); } // NOTE: serialized index is real table offset plus 1. auto index = type - 1; // If ty is found in allTypes, return it. if (auto ty = allTypes[index]; ty) { return ty; } auto typeObj = package->allTypes()->Get(static_cast(index)); if (GetPrimitiveTy(index, typeObj)) { return allTypes[index]; } auto tyHandler = tyLoaderMap.find(typeObj->kind()); if (tyHandler != tyLoaderMap.end()) { tyHandler->second(this, index, *typeObj); } else { allTypes[index] = TypeManager::GetInvalidTy(); } return allTypes[index]; } bool ASTLoader::ASTLoaderImpl::GetPrimitiveTy(FormattedIndex type, const PackageFormat::SemaTy* typeObj) { if (typeObj && typeObj->kind() >= PackageFormat::TypeKind_Unit && typeObj->kind() <= PackageFormat::TypeKind_Bool) { auto astKind = GetASTTypeKind(typeObj->kind()); CJC_ASSERT(astKind != TypeKind::TYPE_INVALID); auto t = TypeManager::GetPrimitiveTy(astKind); allTypes[type] = t; return true; } return false; } void ASTLoader::ASTLoaderImpl::SetGenericTy(FormattedIndex type, const PackageFormat::SemaTy& typeObj) { auto info = typeObj.info_as_GenericTyInfo(); CJC_NULLPTR_CHECK(info); auto decl = GetDeclFromIndex(info->declPtr()); if (decl == nullptr) { return; // For invalid indirect importing, this may be true. } auto gpd = StaticCast(decl); auto t = typeManager.GetGenericsTy(*gpd); allTypes[type] = t; if (isLoadCache) { return; // Do not load upper bounds cache for generic type. } if (auto gTy = DynamicCast(t)) { CJC_NULLPTR_CHECK(info->upperBounds()); auto length = info->upperBounds()->size(); for (uoffset_t i = 0; i < length; i++) { gTy->upperBounds.emplace(LoadType(info->upperBounds()->Get(i))); } CJC_NULLPTR_CHECK(gpd->outerDecl); auto generic = gpd->outerDecl->GetGeneric(); CJC_NULLPTR_CHECK(generic); generic->assumptionCollection.emplace(gTy, gTy->upperBounds); } } std::vector> ASTLoader::ASTLoaderImpl::LoadTypeArgs(const PackageFormat::SemaTy& typeObj) { std::vector> typeArgs; CJC_NULLPTR_CHECK(typeObj.typeArgs()); auto length = static_cast(typeObj.typeArgs()->size()); for (uoffset_t i = 0; i < length; i++) { typeArgs.emplace_back(LoadType(typeObj.typeArgs()->Get(i))); } return typeArgs; } template void ASTLoader::ASTLoaderImpl::SetTypeTy(FormattedIndex type, const PackageFormat::SemaTy& typeObj) { Ptr ty = TypeManager::GetInvalidTy(); if constexpr (std::is_same_v) { ty = TypeManager::GetCStringTy(); } else if constexpr (std::is_same_v) { CJC_ASSERT(typeObj.typeArgs() && typeObj.typeArgs()->size() == 1u); ty = typeManager.GetPointerTy(LoadType(typeObj.typeArgs()->Get(0))); } else if constexpr (std::is_same_v) { auto info = typeObj.info_as_ArrayTyInfo(); CJC_NULLPTR_CHECK(info); unsigned int dims = static_cast(info->dimsOrSize()); CJC_ASSERT(typeObj.typeArgs() && typeObj.typeArgs()->size() == 1u); ty = typeManager.GetArrayTy(LoadType(typeObj.typeArgs()->Get(0)), dims); } else if constexpr (std::is_same_v) { auto info = typeObj.info_as_ArrayTyInfo(); CJC_NULLPTR_CHECK(info); CJC_ASSERT(typeObj.typeArgs() && typeObj.typeArgs()->size() == 1u); ty = typeManager.GetVArrayTy(*LoadType(typeObj.typeArgs()->Get(0)), info->dimsOrSize()); } else if constexpr (std::is_same_v) { ty = typeManager.GetTupleTy(LoadTypeArgs(typeObj)); } else if constexpr (std::is_same_v) { auto info = typeObj.info_as_FuncTyInfo(); CJC_NULLPTR_CHECK(info); ty = typeManager.GetFunctionTy( LoadTypeArgs(typeObj), LoadType(info->retType()), {info->isC(), false, info->hasVariableLenArg()}); } else { auto info = typeObj.info_as_CompositeTyInfo(); CJC_NULLPTR_CHECK(info); auto typeDecl = DynamicCast(GetDeclFromIndex(info->declPtr())); if (typeDecl == nullptr) { allTypes[type] = ty; return; } auto typeArgs = LoadTypeArgs(typeObj); if constexpr (std::is_same_v) { ty = typeManager.GetTypeAliasTy(*typeDecl, typeArgs); } else if constexpr (std::is_same_v) { ty = info->isThisTy() ? typeManager.GetClassThisTy(*typeDecl, typeArgs) : typeManager.GetClassTy(*typeDecl, typeArgs); } else if constexpr (std::is_same_v) { ty = typeManager.GetInterfaceTy(*typeDecl, typeArgs); } else if constexpr (std::is_same_v) { ty = typeManager.GetStructTy(*typeDecl, typeArgs); } else if constexpr (std::is_same_v) { ty = typeManager.GetEnumTy(*typeDecl, typeArgs); } else { static_assert(std::is_same_v); } } allTypes[type] = ty; } void ASTLoader::ASTLoaderImpl::LoadInheritedTypes(const PackageFormat::Decl& decl, InheritableDecl& id) { // Super class type or super interface types. auto inheritedTypes = id.astKind == ASTKind::CLASS_DECL ? decl.info_as_ClassInfo()->inheritedTypes() : id.astKind == ASTKind::INTERFACE_DECL ? decl.info_as_InterfaceInfo()->inheritedTypes() : id.astKind == ASTKind::ENUM_DECL ? decl.info_as_EnumInfo()->inheritedTypes() : id.astKind == ASTKind::STRUCT_DECL ? decl.info_as_StructInfo()->inheritedTypes() : id.astKind == ASTKind::EXTEND_DECL ? decl.info_as_ExtendInfo()->inheritedTypes() : nullptr; CJC_NULLPTR_CHECK(inheritedTypes); auto length = static_cast(inheritedTypes->size()); std::vector> loadedTypes; for (uoffset_t i = 0; i < length; i++) { auto index = inheritedTypes->Get(i); if (index != INVALID_FORMAT_INDEX) { auto type = WrapTypeInNode(LoadType(index)); CJC_NULLPTR_CHECK(type); if (!Ty::IsTyCorrect(type->ty) && isLoadCache) { return; // If any invalid type existed, do not load type cache for current 'id'. } if (auto classLikeTy = DynamicCast(type->ty)) { (void)classLikeTy->directSubtypes.emplace(id.ty); } loadedTypes.emplace_back(std::move(type)); } } id.inheritedTypes.resize(loadedTypes.size()); for (size_t i = 0; i < loadedTypes.size(); ++i) { UpdateType(id.inheritedTypes[i], std::move(loadedTypes[i])); } } void ASTLoader::ASTLoaderImpl::LoadGenericConstraintsRef( const PackageFormat::Generic* genericRef, Ptr generic) { // Do not load constraints if the generic is incremental compiled node. if (generic == nullptr || genericRef == nullptr || generic->TestAttr(Attribute::INCRE_COMPILE)) { return; } uoffset_t length = genericRef->constraints()->size(); for (uoffset_t i = 0; i < length; i++) { auto vConstraint = genericRef->constraints()->Get(i); auto constraint = CreateAndLoadBasicInfo(*vConstraint, INVALID_FORMAT_INDEX); constraint->type = WrapTypeInNode(LoadType(vConstraint->type())); auto upperSize = vConstraint->uppers()->size(); for (uoffset_t j = 0; j < upperSize; j++) { constraint->upperBounds.emplace_back(WrapTypeInNode(LoadType(vConstraint->uppers()->Get(j)))); } generic->genericConstraints.emplace_back(std::move(constraint)); } } template void ASTLoader::ASTLoaderImpl::LoadNominalDeclRef(const PackageFormat::Decl& decl, DeclT& astDecl) { if constexpr (std::is_base_of::value) { for (auto& member : astDecl.GetMemberDeclPtrs()) { SetMemberDeclReference(*member, astDecl); } // For incremental compilation, when 'id' is changed, the 'inheritedTypes' may be changed and cannot be loaded. // But type for original decl and reference relation can still be loaded. if (!astDecl.toBeCompiled) { LoadInheritedTypes(decl, astDecl); } } else if constexpr (std::is_same_v) { // Do not load 'retType' node for constructor which is not inside generic type decl. // Generic type decl's constructor will have funcBody content. // NOTE: for cjlint check -- 'DataflowRuleGFIO01Check::IsFileInit' requires 'retType' not exist. bool nonGenericCtor = astDecl.TestAttr(Attribute::CONSTRUCTOR) && !astDecl.funcBody->body; if (!nonGenericCtor) { auto info = decl.info_as_FuncInfo(); CJC_NULLPTR_CHECK(info); auto type = WrapTypeInNode(LoadType(info->funcBody()->retType())); UpdateType(astDecl.funcBody->retType, std::move(type)); } astDecl.funcBody->ty = astDecl.ty; } if constexpr (std::is_same_v) { UpdateType(astDecl.extendedType, WrapTypeInNode(astDecl.ty)); } if (astDecl.TestAttr(Attribute::GENERIC_INSTANTIATED)) { astDecl.genericDecl = GetDeclFromIndex(decl.genericDecl()); } } void ASTLoader::ASTLoaderImpl::LoadTypeAliasDeclRef(const PackageFormat::Decl& decl, TypeAliasDecl& tad) { auto info = decl.info_as_AliasInfo(); CJC_NULLPTR_CHECK(info); auto refType = WrapTypeInNode(LoadType(info->aliasedTy())); for (auto typeArg : refType->ty->typeArgs) { refType->typeArguments.emplace_back(WrapTypeInNode(typeArg)); } tad.type = std::move(refType); } void ASTLoader::ASTLoaderImpl::LoadDeclDependencies(const PackageFormat::Decl& decl, Decl& astDecl) { auto rawDeps = decl.dependencies(); if (deserializingCommon && rawDeps) { auto length = static_cast(rawDeps->size()); for (uoffset_t i = 0; i < length; i++) { auto index = rawDeps->Get(i); auto dependency = GetDeclFromIndex(index); astDecl.dependencies.emplace_back(dependency); } } } void ASTLoader::ASTLoaderImpl::LoadDeclRefs(const PackageFormat::Decl& declObj, Decl& decl) { // Load ty for all decls and try to load generic constraints. decl.ty = LoadType(declObj.type()); if (!Ty::IsTyCorrect(decl.ty) && isLoadCache) { return; // Skip following steps if ty is not correct during loading cache. } LoadGenericConstraintsRef(declObj.generic(), decl.GetGeneric()); switch (declObj.kind()) { case PackageFormat::DeclKind_FuncDecl: { LoadNominalDeclRef(declObj, StaticCast(decl)); break; } case PackageFormat::DeclKind_StructDecl: { LoadNominalDeclRef(declObj, StaticCast(decl)); break; } case PackageFormat::DeclKind_EnumDecl: { LoadNominalDeclRef(declObj, StaticCast(decl)); break; } case PackageFormat::DeclKind_InterfaceDecl: { LoadNominalDeclRef(declObj, StaticCast(decl)); break; } case PackageFormat::DeclKind_ClassDecl: { LoadNominalDeclRef(declObj, StaticCast(decl)); break; } case PackageFormat::DeclKind_ExtendDecl: { LoadNominalDeclRef(declObj, StaticCast(decl)); break; } case PackageFormat::DeclKind_TypeAliasDecl: { LoadTypeAliasDeclRef(declObj, StaticCast(decl)); break; } case PackageFormat::DeclKind_VarWithPatternDecl: { auto info = declObj.info_as_VarWithPatternInfo(); CJC_NULLPTR_CHECK(info); LoadPatternRefs(*info->irrefutablePattern(), *StaticCast(decl).irrefutablePattern); break; } default: break; } } void ASTLoader::ASTLoaderImpl::LoadExprRefs(const PackageFormat::Expr& exprObj, Expr& expr) { expr.ty = LoadType(exprObj.type()); expr.mapExpr = GetExprByIndex(exprObj.mapExpr()); // Only following 4 kind of expression has referenced target decl. switch (exprObj.kind()) { case PackageFormat::ExprKind_MemberAccess: { auto info = exprObj.info_as_ReferenceInfo(); CJC_ASSERT(info && info->instTys()); StaticCast(expr).target = GetDeclFromIndex(info->target()); for (uoffset_t i = 0; i < info->instTys()->size(); ++i) { StaticCast(expr).instTys.emplace_back(LoadType(info->instTys()->Get(i))); } StaticCast(expr).matchedParentTy = LoadType(info->matchedParentTy()); break; } case PackageFormat::ExprKind_RefExpr: { auto info = exprObj.info_as_ReferenceInfo(); CJC_ASSERT(info && info->instTys()); StaticCast(expr).ref.target = GetDeclFromIndex(info->target()); for (uoffset_t i = 0; i < info->instTys()->size(); ++i) { StaticCast(expr).instTys.emplace_back(LoadType(info->instTys()->Get(i))); } StaticCast(expr).matchedParentTy = LoadType(info->matchedParentTy()); break; } case PackageFormat::ExprKind_ArrayLit: { if (expr.ty->kind == TypeKind::TYPE_VARRAY) { break; } auto info = exprObj.info_as_ArrayInfo(); CJC_NULLPTR_CHECK(info); StaticCast(expr).initFunc = DynamicCast(GetDeclFromIndex(info->initFunc())); CJC_NULLPTR_CHECK(StaticCast(expr).initFunc); break; } case PackageFormat::ExprKind_ArrayExpr: { if (expr.ty->kind == TypeKind::TYPE_VARRAY) { break; } auto info = exprObj.info_as_ArrayInfo(); CJC_NULLPTR_CHECK(info); // ArrayExpr's 'initFunc' is optional. StaticCast(expr).initFunc = DynamicCast(GetDeclFromIndex(info->initFunc())); break; } case PackageFormat::ExprKind_LambdaExpr: StaticCast(expr).funcBody->ty = expr.ty; break; case PackageFormat::ExprKind_LitConstExpr: if (expr.ty->IsString()) { StaticCast(expr).ref = WrapTypeInNode(expr.ty); } InitializeLitConstValue(StaticCast(expr)); break; case PackageFormat::ExprKind_JumpExpr: { // 'JumpExpr' will not have mapExpr, the 'mapExpr' field is reused for 'refLoop'. auto& je = StaticCast(expr); je.refLoop = std::move(je.mapExpr); break; } default: break; } LoadSubNodeRefs(exprObj, expr); } /** * NOTE: sub nodes which are not subtype of 'Expr' were not stored in 'allLoadedExprs', * so we need to load their reference separately. eg: FuncArg, MatchCase, MatchCaseOther, Pattern and it subtypes. */ void ASTLoader::ASTLoaderImpl::LoadSubNodeRefs(const PackageFormat::Expr& exprObj, Expr& expr) { switch (exprObj.kind()) { case PackageFormat::ExprKind_CallExpr: { auto& ce = StaticCast(expr); CJC_ASSERT(exprObj.operands() && (exprObj.operands()->size() - 1) == ce.args.size()); for (uoffset_t i = 1; i < exprObj.operands()->size(); i++) { auto index = exprObj.operands()->Get(i); CJC_ASSERT(index != INVALID_FORMAT_INDEX); auto typeIdx = GetFormatExprByIndex(index)->type(); auto arg = ce.args[i - 1].get(); arg->ty = LoadType(typeIdx); if (arg->TestAttr(Attribute::HAS_INITIAL)) { // Default param's arg does not loaded expr, which should retrieve type from arg's type. arg->expr->ty = arg->ty; } } break; } case PackageFormat::ExprKind_TryExpr: { auto info = exprObj.info_as_TryInfo(); CJC_ASSERT(info && info->patterns() && info->patterns()->size() == StaticCast(expr).catchPatterns.size()); for (uoffset_t i = 0; i < info->patterns()->size(); i++) { LoadPatternRefs(*info->patterns()->Get(i), *StaticCast(expr).catchPatterns[i]); } break; } case PackageFormat::ExprKind_MatchExpr: { auto& me = StaticCast(expr); CJC_NULLPTR_CHECK(exprObj.operands()); if (me.matchMode) { CJC_ASSERT((exprObj.operands()->size() - 1) == me.matchCases.size()); // For matchExpr with selector, the start index of matchCases is 1. for (uoffset_t i = 1; i < exprObj.operands()->size(); i++) { LoadMatchCaseRef(exprObj.operands()->Get(i), *me.matchCases[i - 1], *me.selector); } } else { for (uoffset_t i = 0; i < exprObj.operands()->size(); i++) { auto mcObj = GetFormatExprByIndex(exprObj.operands()->Get(i)); me.matchCaseOthers[i]->ty = LoadType(mcObj->type()); } } break; } case PackageFormat::ExprKind_ForInExpr: { auto info = exprObj.info_as_ForInInfo(); CJC_NULLPTR_CHECK(info); LoadPatternRefs(*info->pattern(), *StaticCast(expr).pattern); break; } default: break; } LoadSubNodeRefs2(exprObj, expr); } void ASTLoader::ASTLoaderImpl::LoadSubNodeRefs2(const PackageFormat::Expr& exprObj, AST::Expr& expr) { switch (exprObj.kind()) { case PackageFormat::ExprKind_LetPatternDestructor: { auto& let = StaticCast(expr); auto info = exprObj.info_as_LetPatternDestructorInfo(); CJC_NULLPTR_CHECK(info); for (uoffset_t i{0}; i < info->patterns()->size(); ++i) { LoadPatternRefs(*info->patterns()->Get(i), *let.patterns[i]); } let.initializer->ty = LoadType(GetFormatExprByIndex(exprObj.operands()->Get(0))->type()); break; } default: break; } } void ASTLoader::ASTLoaderImpl::LoadMatchCaseRef(FormattedIndex index, MatchCase& mc, Expr& selector) { auto mcObj = GetFormatExprByIndex(index); mc.ty = LoadType(mcObj->type()); auto info = mcObj->info_as_MatchCaseInfo(); CJC_NULLPTR_CHECK(info); CJC_ASSERT(info->patterns()->size() == mc.patterns.size()); for (uoffset_t i = 0; i < info->patterns()->size(); i++) { LoadPatternRefs(*info->patterns()->Get(i), *mc.patterns[i]); mc.patterns[i]->ctxExpr = &selector; } } void ASTLoader::ASTLoaderImpl::LoadPatternRefs(const PackageFormat::Pattern& pObj, Pattern& pattern) { CJC_ASSERT(pObj.types()->size() >= 1); pattern.ty = LoadType(pObj.types()->Get(0)); // Sub patterns is guaranteed and created during 'LoadPattern'. switch (pObj.kind()) { case PackageFormat::PatternKind_TuplePattern: for (uoffset_t i = 0; i < pObj.patterns()->size(); i++) { LoadPatternRefs(*pObj.patterns()->Get(i), *StaticCast(pattern).patterns[i]); } break; case PackageFormat::PatternKind_TypePattern: LoadPatternRefs(*pObj.patterns()->Get(0), *StaticCast(pattern).pattern); StaticCast(pattern).type = WrapTypeInNode(pattern.ty); break; case PackageFormat::PatternKind_EnumPattern: for (uoffset_t i = 0; i < pObj.patterns()->size(); i++) { LoadPatternRefs(*pObj.patterns()->Get(i), *StaticCast(pattern).patterns[i]); } break; case PackageFormat::PatternKind_ExceptTypePattern: { auto& etp = StaticCast(pattern); LoadPatternRefs(*pObj.patterns()->Get(0), *etp.pattern); CJC_ASSERT(pObj.types()->size() >= 1); etp.types.resize(pObj.types()->size() - 1); for (uoffset_t i = 1; i < pObj.types()->size(); i++) { etp.types[i - 1] = WrapTypeInNode(LoadType(pObj.types()->Get(i))); } break; } case PackageFormat::PatternKind_CommandTypePattern: { auto& ctp = StaticCast(pattern); LoadPatternRefs(*pObj.patterns()->Get(0), *ctp.pattern); CJC_ASSERT(pObj.types()->size() >= 1); ctp.types.resize(pObj.types()->size() - 1); for (uoffset_t i = 1; i < pObj.types()->size(); i++) { ctp.types[i - 1] = WrapTypeInNode(LoadType(pObj.types()->Get(i))); } break; } default: break; } } cangjie_compiler-1.0.7/src/Modules/CMakeLists.txt000066400000000000000000000011061510705540100217440ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file( GLOB MODULES_SRC *.cpp ASTSerialization/*.cpp) add_library(CangjieModules OBJECT ${MODULES_SRC}) add_dependencies(CangjieModules CangjieFlatbuffersHeaders) target_include_directories(CangjieModules PRIVATE ${FLATBUFFERS_INCLUDE_DIR}) target_compile_options(CangjieModules PRIVATE ${CJC_EXTRA_WARNINGS}) cangjie_compiler-1.0.7/src/Modules/CjoManager.cpp000066400000000000000000000546551510705540100217370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the CjoManager related classes. */ #include "cangjie/Modules/CjoManager.h" #include #include "CjoManagerImpl.h" #include "cangjie/AST/ASTCasting.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Modules/ASTSerialization.h" #include "cangjie/Modules/ModulesUtils.h" using namespace Cangjie; using namespace AST; namespace Cangjie { namespace { void AddDeclToMap(Decl& decl, std::map& declMap) { if (decl.astKind == ASTKind::EXTEND_DECL) { return; // ExtendDecl cannot be referenced by name. } if (auto vpd = DynamicCast(&decl); vpd) { // A VarWithPatternDecl is viewed as a collection of VarDecls. Walker walker(vpd->irrefutablePattern.get(), [&declMap](Ptr node) { if (auto vd = DynamicCast(node)) { declMap[vd->identifier].emplace(vd); } return VisitAction::WALK_CHILDREN; }); walker.Walk(); } else if (auto funcDecl = DynamicCast(&decl); funcDecl == nullptr || (!funcDecl->TestAttr(Attribute::MAIN_ENTRY) && funcDecl->identifier != MAIN_INVOKE)) { // Main function won't be imported. declMap[decl.identifier].emplace(&decl); } } bool CanInline(const GlobalOptions& opts) { return opts.chirLLVM && opts.optimizationLevel > GlobalOptions::OptimizationLevel::O1 && !opts.enableCompileTest && !opts.enableHotReload; } } // namespace CjoManager::CjoManager(const CjoManager::Config& config) : impl{new CjoManagerImpl{config}} { } CjoManager::~CjoManager() { DeleteASTLoaders(); delete impl; } void CjoManager::DeleteASTLoaders() const noexcept { for (auto& p : impl->GetPackageNameMap()) { delete p.second->loader.get(); p.second->loader = nullptr; } } CjoManagerImpl::CjoManagerImpl(const CjoManager::Config& config) : diag(config.diag), typeManager(config.typeManager), globalOptions(config.globalOptions), importSrcCode(config.importSrcCode), canInline(CanInline(config.globalOptions)) { } Ptr CjoManagerImpl::GetPackageInfo(const std::string& fullPackageName) const { auto iter = packageNameMap.find(fullPackageName); if (iter != packageNameMap.cend()) { CJC_NULLPTR_CHECK(iter->second); return iter->second.get(); } return nullptr; } bool CjoManager::IsOnlyUsedByMacro(const std::string& fullPackageName) const { auto info = impl->GetPackageInfo(fullPackageName); return info ? info->onlyUsedByMacro : false; } void CjoManager::SetOnlyUsedByMacro(const std::string& fullPackageName, bool onlyUsedByMacro) const { auto info = impl->GetPackageInfo(fullPackageName); if (info != nullptr) { info->onlyUsedByMacro = onlyUsedByMacro; } } bool CjoManager::IsMacroRelatedPackageName(const std::string& fullPackageName) const { auto info = impl->GetPackageInfo(fullPackageName); return info != nullptr ? info->onlyUsedByMacro : false; } void CjoManager::UpdateSearchPath(const std::string& cangjieModules) const { impl->UpdateSearchPath(cangjieModules); } const std::vector& CjoManager::GetSearchPath() const { return impl->GetSearchPath(); } bool CjoManager::GetCanInline() const { return impl->GetCanInline(); } void CjoManager::SetPackageCjoCache(const std::string& fullPackageName, const std::vector& cjoData) const { impl->SetPackageCjoCache(fullPackageName, cjoData); } void CjoManager::ClearCjoCache() const { impl->ClearCjoCache(); } void CjoManager::ClearVisitedPkgs() const { impl->ClearVisitedPkgs(); } DiagnosticEngine& CjoManager::GetDiag() const { return impl->GetDiag(); } Ptr>> CjoManager::GetExportIdDeclMap( const std::string& fullPackageName) const { return impl->GetExportIdDeclMap(fullPackageName); } std::optional> CjoManager::PreReadCommonPartCjoFiles() { return impl->PreReadCommonPartCjoFiles(*this); } Ptr CjoManager::GetCommonPartCjo(std::string expectedName) const { return impl->GetCommonPartCjo(expectedName); } Ptr CjoManager::GetPackageDecl(const std::string& fullPackageName) const { auto info = impl->GetPackageInfo(fullPackageName); if (info == nullptr) { return nullptr; } return info->pkgDecl.get(); } Ptr CjoManager::GetPackage(const std::string& fullPackageName) const { auto info = impl->GetPackageInfo(fullPackageName); if (info == nullptr) { return nullptr; } return info->pkg.get(); } std::string CjoManager::GetPackageDepInfo(const std::string& cjoPath) const { auto loader = impl->ReadCjo("", cjoPath, *this); if (loader == nullptr) { return ""; } return loader->LoadPackageDepInfo(); } Ptr>> CjoManagerImpl::GetExportIdDeclMap( const std::string& fullPackageName) const { auto iter = packageNameMap.find(fullPackageName); if (iter != packageNameMap.cend()) { CJC_NULLPTR_CHECK(iter->second); return &iter->second->exportIDDeclMap; } return nullptr; } const std::map& CjoManager::GetPackageMembers(const std::string& fullPackageName) const { const static std::map EMPTY_MAP; auto iter = impl->GetPackageNameMap().find(fullPackageName); if (iter != impl->GetPackageNameMap().cend()) { CJC_NULLPTR_CHECK(iter->second); return iter->second->declMap; } return EMPTY_MAP; } const OrderedDeclSet& CjoManager::GetPackageMembersByName( const std::string& fullPackageName, const std::string& name) const { const static OrderedDeclSet EMPTY_DECLS; auto& declMap = GetPackageMembers(fullPackageName); auto iter = declMap.find(name); if (iter != declMap.cend()) { return iter->second; } return EMPTY_DECLS; } Ptr CjoManager::GetImplicitPackageMembersByName(const std::string& fullPackageName, const std::string& name) const { auto info = impl->GetPackageInfo(fullPackageName); if (!info) { return nullptr; } auto found = info->implicitDeclMap.find(name); return found == info->implicitDeclMap.end() ? nullptr : found->second; } bool CjoManager::LoadPackageHeader(const std::string& fullPackageName, const std::string& cjoPath) const { auto iter = impl->GetPackageNameMap().find(fullPackageName); if (iter != impl->GetPackageNameMap().cend()) { return true; } auto loader = impl->ReadCjo(fullPackageName, cjoPath, *this); if (loader == nullptr) { return false; } auto pkg = loader->LoadPackageDependencies(); if (pkg == nullptr) { return false; } auto pkgInfo = MakeOwned(CjoManagerImpl::PackageInfo( {.loader = loader.release(), .pkg = pkg.get(), .pkgDecl = MakeOwned(*pkg)})); impl->AddImportedPackages(pkg); impl->GetPackageNameMap().emplace(fullPackageName, std::move(pkgInfo)); return true; } bool CjoManagerImpl::IsReExportBy(const std::string& srcPackage, const std::string& reExportPackage) const { auto info = GetPackageInfo(srcPackage); CJC_NULLPTR_CHECK(info); for (auto& file : info->pkg->files) { for (auto& importSpec : file->imports) { if (!importSpec->IsReExport()) { continue; } auto found = importedPackageNameMap.find(importSpec.get()); if (found != importedPackageNameMap.end() && found->second.first == reExportPackage) { return true; } } } return false; } bool CjoManager::NeedCollectDependency(std::string curName, bool isCurMacro, std::string depName) const { if (depName == curName || impl->AlreadyLoaded(depName)) { return false; } auto pkgInfo = impl->GetPackageInfo(depName); if (!pkgInfo) { // This common dependency was not imported in current platform part, so it need to be add manually auto cjoPath = GetPackageCjoPath(depName); if (!cjoPath || !LoadPackageHeader(depName, *cjoPath)) { DiagnosticBuilder builder = GetDiag().DiagnoseRefactor(DiagKindRefactor::package_search_error, DEFAULT_POSITION, depName); builder.AddHelp(DiagHelp(Modules::NO_CJO_HELP_INFO)); return false; } } // If current is macro package, only load decls for dependent macro package, // otherwise, load decls for all dependent package (macro package was filtered before). // NOTE: non-macro package's will never be used through macro package. if (auto depPd = GetPackageDecl(depName); depPd) { bool isDepMacro = depPd->srcPackage->isMacroPackage; if (!isCurMacro || isDepMacro || depName == AST_PACKAGE_NAME || impl->IsReExportBy(curName, depName)) { return true; } } return false; } void CjoManager::LoadPackageDeclsOnDemand(const std::vector>& packages, bool fromLsp) const { // Add all directly imported package's loader. std::queue> q; for (auto pkg : packages) { if (fromLsp) { q.push(impl->GetPackageInfo(pkg->fullPackageName)); } for (auto& file : pkg->files) { for (auto& import : file->imports) { auto pkgName = GetPackageNameByImport(*import); if (!pkgName.empty()) { q.push(impl->GetPackageInfo(pkgName)); } } } } std::vector> loaders; // Load common part cjo for (auto pkg : packages) { if (impl->GetGlobalOptions().inputChirFiles.size() > 0) { std::string expectedPackageName = pkg->fullPackageName; auto commonLoader = GetCommonPartCjo(expectedPackageName); if (!commonLoader) { continue; } commonLoader->PreloadCommonPartOfPackage(*pkg); commonLoader->LoadPackageDecls(); loaders.emplace_back(commonLoader); for (auto commonDependencyName : commonLoader->GetDependentPackageNames()) { if (NeedCollectDependency(expectedPackageName, pkg->isMacroPackage, commonDependencyName)) { q.push(impl->GetPackageInfo(commonDependencyName)); } } } } while (!q.empty()) { auto cur = q.front(); q.pop(); if (cur == nullptr || cur->loader == nullptr) { continue; // If any error happens during loading 'cjo', the loader will be null. } auto pkgName = cur->loader->GetImportedPackageName(); if (auto [_, success] = impl->AddLoadedPackages(pkgName); !success) { continue; } loaders.emplace_back(cur->loader); cur->loader->LoadPackageDecls(); bool isCurMacro = cur->pkg->isMacroPackage; auto deps = cur->loader->GetDependentPackageNames(); for (auto pkg : deps) { if (NeedCollectDependency(pkgName, isCurMacro, pkg)) { q.push(impl->GetPackageInfo(pkg)); } } } for (auto loader : loaders) { loader->LoadRefs(); } } void CjoManager::LoadAllDeclsAndRefs() const { // 'packageNameMap' also contains source package, we only need to load for imported package. for (auto& p : impl->GetPackageNameMap()) { auto& pkgInfo = p.second; CJC_NULLPTR_CHECK(pkgInfo); if (pkgInfo->loader) { pkgInfo->loader->LoadPackageDecls(); } } for (auto& p : impl->GetPackageNameMap()) { if (p.second->loader) { p.second->loader->LoadRefs(); } } } // Reading common part .cjo is required before parsing to keep fileID stable. // This method only reads file content and does not build ast nodes. std::optional> CjoManagerImpl::PreReadCommonPartCjoFiles(CjoManager& cjoManager) { // use `cjoFileCacheMap` std::vector buffer; std::string failedReason; if (!globalOptions.commonPartCjo) { diag.DiagnoseRefactor(DiagKindRefactor::module_common_part_path_is_required, DEFAULT_POSITION); return std::nullopt; } CJC_ASSERT(globalOptions.commonPartCjo); std::string commonPartCjoPath = *globalOptions.commonPartCjo; FileUtil::ReadBinaryFileToBuffer(commonPartCjoPath, buffer, failedReason); if (!failedReason.empty()) { diag.DiagnoseRefactor( DiagKindRefactor::module_read_file_to_buffer_failed, DEFAULT_POSITION, commonPartCjoPath, failedReason); return {}; } // name of package is unknown before parsing and reading .cjo, so fake is used. std::string fakeName = ""; commonPartLoader = MakeOwned(std::move(buffer), fakeName, typeManager, cjoManager, globalOptions); commonPartLoader->SetImportSourceCode(importSrcCode); commonPartLoader->PreReadAndSetPackageName(); return commonPartLoader->ReadFileNames(); } Ptr CjoManagerImpl::GetCommonPartCjo(std::string expectedName) { CJC_ASSERT(commonPartLoader); CJC_ASSERT(globalOptions.commonPartCjo); std::string realName = commonPartLoader->PreReadAndSetPackageName(); if (realName != expectedName) { diag.DiagnoseRefactor( DiagKindRefactor::module_common_cjo_wrong_package, DEFAULT_POSITION, realName, expectedName); return nullptr; } return commonPartLoader.get(); } OwnedPtr CjoManagerImpl::ReadCjo( const std::string& fullPackageName, const std::string& cjoPath, const CjoManager& cjoManager, bool printErr) const { std::vector buffer; if (auto found = cjoFileCacheMap.find(fullPackageName); found != cjoFileCacheMap.end()) { buffer = found->second; } else { std::string failedReason; FileUtil::ReadBinaryFileToBuffer(cjoPath, buffer, failedReason); if (printErr && !failedReason.empty()) { diag.DiagnoseRefactor( DiagKindRefactor::module_read_file_to_buffer_failed, DEFAULT_POSITION, cjoPath, failedReason); return nullptr; } } auto loader = MakeOwned(std::move(buffer), fullPackageName, typeManager, cjoManager, globalOptions); loader->SetImportSourceCode(importSrcCode); return loader; } std::unordered_set CjoManager::LoadCachedPackage(const AST::Package& pkg, const std::string& cjoPath, const std::map>& mangledName2DeclMap) const { auto loader = impl->ReadCjo(pkg.fullPackageName, cjoPath, *this, false); if (loader == nullptr) { return {}; } return loader->LoadCachedTypeForPackage(pkg, mangledName2DeclMap); } void CjoManager::AddSourcePackage(AST::Package& pkg) const { auto pkgInfo = MakeOwned( CjoManagerImpl::PackageInfo({.pkg = &pkg, .pkgDecl = MakeOwned(pkg)})); impl->GetPackageNameMap().emplace(pkg.fullPackageName, std::move(pkgInfo)); } void CjoManager::AddImportedPackageFromASTNode(OwnedPtr&& pkg) const { auto pkgInfo = MakeOwned( CjoManagerImpl::PackageInfo({.pkg = pkg.get(), .pkgDecl = MakeOwned(*pkg)})); impl->GetPackageNameMap().emplace(pkg->fullPackageName, std::move(pkgInfo)); pkg->EnableAttr(Attribute::TOOL_ADD); impl->AddImportedPackages(pkg); } void CjoManagerImpl::AddImportsToMap( const ImportSpec& import, const std::string& importedPackage, std::map& declMap) const { auto pkgInfo = GetPackageInfo(importedPackage); if (!pkgInfo) { return; // Failed to load current package. } auto importLevel = GetAccessLevel(import); if (import.content.kind == ImportKind::IMPORT_ALL) { for (auto& [name, decls] : pkgInfo->declMap) { auto& targetMap = declMap[name]; Modules::AddImportedDeclToMap(decls, targetMap, importLevel); } return; } auto& decls = pkgInfo->declMap[import.content.identifier]; if (import.content.kind == ImportKind::IMPORT_SINGLE) { auto& targetMap = declMap[import.content.identifier]; Modules::AddImportedDeclToMap(decls, targetMap, importLevel); } else if (import.content.kind == ImportKind::IMPORT_ALIAS) { auto& targetMap = declMap[import.content.aliasName]; Modules::AddImportedDeclToMap(decls, targetMap, importLevel); } } void CjoManager::AddPackageDeclMap(const std::string& fullPackageName, const std::string& importedPackage) { auto pkgInfo = impl->GetPackageInfo(fullPackageName); // Failed to load current package or already collect the decls. if (!pkgInfo || impl->IsVisitedPackage(fullPackageName)) { return; } impl->AddVisitedPackage(fullPackageName); auto relation = importedPackage.empty() ? Modules::PackageRelation::CHILD : Modules::GetPackageRelation(importedPackage, fullPackageName); for (auto& file : pkgInfo->pkg->files) { // For imported package, 'file->decls' will only contains public decls. for (auto& decl : file->decls) { if (decl->astKind == ASTKind::MACRO_EXPAND_DECL) { continue; // Macro expand decl cannot be reference. } AddDeclToMap(*decl, pkgInfo->declMap); } for (auto& decl : file->exportedInternalDecls) { if (decl->TestAttr(Attribute::IMPLICIT_USED)) { pkgInfo->implicitDeclMap.emplace(decl->identifier, decl.get()); } } } for (auto& file : pkgInfo->pkg->files) { for (auto& import : file->imports) { if (import->IsImportMulti()) { continue; } auto pkgName = GetPackageNameByImport(*import); if (pkgName.empty()) { continue; } AddPackageDeclMap(pkgName, fullPackageName); if (import->IsReExport() && Modules::IsVisible(*import, relation)) { impl->AddImportsToMap(*import, pkgName, pkgInfo->declMap); } } } } std::optional CjoManager::GetPackageCjoPath(std::string fullPackageName) const { if (auto found = impl->GetCjoFileCacheMap().find(fullPackageName); found != impl->GetCjoFileCacheMap().end()) { return fullPackageName; // Set dummy path for cached cjo data. } else { return FileUtil::FindSerializationFile(fullPackageName, SERIALIZED_FILE_EXTENSION, GetSearchPath()); } return std::nullopt; } std::pair CjoManager::GetPackageCjo(const AST::ImportSpec& importSpec) const { std::string cjoPath; std::string cjoName; for (auto it : GetPossibleCjoNames(importSpec)) { cjoName = it; if (auto found = impl->GetCjoFileCacheMap().find(cjoName); found != impl->GetCjoFileCacheMap().end()) { cjoPath = cjoName; // Set dummy path for cached cjo data. } else { cjoPath = FileUtil::FindSerializationFile(cjoName, SERIALIZED_FILE_EXTENSION, GetSearchPath()); } if (!cjoPath.empty()) { break; } } CJC_ASSERT(!cjoName.empty()); auto cjoPackageName = FileUtil::ToPackageName(cjoName); // Store importSpec with packageName. std::string possibleName = importSpec.content.GetImportedPackageName(); impl->AddImportedPackageName(&importSpec, std::make_pair(cjoPackageName, cjoPackageName == possibleName && importSpec.content.kind != ImportKind::IMPORT_ALL)); return {cjoPackageName, cjoPath}; } std::vector CjoManager::GetPossibleCjoNames(const ImportSpec& import) const { // Multi-imports are desugared after parser which should not be used for get package name. CJC_ASSERT(import.content.kind != ImportKind::IMPORT_MULTI); if (import.content.prefixPaths.empty()) { return {import.content.identifier}; } std::string name; std::string_view dot = TOKENS[static_cast(TokenKind::DOT)]; bool needDc{import.content.hasDoubleColon}; for (size_t i{0}; i < import.content.prefixPaths.size(); ++i) { name += import.content.prefixPaths[i]; if (i == import.content.prefixPaths.size() - 1) { break; } if (i == 0 && needDc) { name += ORG_NAME_SEPARATOR; needDc = false; } else { name += dot; } } if (import.content.kind == ImportKind::IMPORT_ALL) { return {std::move(name)}; } if (auto it = GetPackageNameByImport(import); !it.empty()) { return {FileUtil::ToCjoFileName(it)}; } // if needDc, this import must be of from a::b // in this case, the only possible pacakge name is a::b if (needDc) { return {name + std::string{ORG_NAME_SEPARATOR} + import.content.identifier}; } auto maybePackageName = name + std::string{dot} + import.content.identifier.Val(); return {std::move(maybePackageName), std::move(name)}; } std::string CjoManager::GetPackageNameByImport(const AST::ImportSpec& importSpec) const { return impl->GetPackageNameByImport(importSpec); } bool CjoManager::IsImportPackage(const AST::ImportSpec& importSpec) const { return impl->IsImportPackage(importSpec); } std::vector> CjoManager::GetAllPackageDecls(bool includeMacroPkg) const { std::vector> ret; for (auto& p : impl->GetPackageNameMap()) { auto& pkgInfo = p.second; CJC_ASSERT(pkgInfo && pkgInfo->pkgDecl); // Temporarily contains source package to keep same implementation as before. if (!pkgInfo->pkg->TestAttr(Attribute::IMPORTED) || includeMacroPkg || (!pkgInfo->onlyUsedByMacro && !pkgInfo->pkg->isMacroPackage)) { ret.emplace_back(pkgInfo->pkgDecl.get()); } } return ret; } void CjoManager::RemovePackage(const std::string& fullPkgName, const Ptr package) const { impl->RemoveImportedPackages(package); if (auto found = impl->GetPackageNameMap().find(fullPkgName); found != impl->GetPackageNameMap().end()) { delete found->second->loader.get(); found->second->loader = nullptr; impl->GetPackageNameMap().erase(fullPkgName); } } } // namespace Cangjie cangjie_compiler-1.0.7/src/Modules/CjoManagerImpl.h000066400000000000000000000131471510705540100222150ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares class CjoManagerImpl. */ #ifndef CANGJIE_MODULES_CJO_MANAGERIMPL_H #define CANGJIE_MODULES_CJO_MANAGERIMPL_H #include "cangjie/Modules/CjoManager.h" namespace Cangjie { class CjoManagerImpl { public: explicit CjoManagerImpl(const CjoManager::Config& config); struct PackageInfo { Ptr loader; Ptr pkg; OwnedPtr pkgDecl; std::unordered_map> exportIDDeclMap; std::map declMap; std::map> implicitDeclMap; std::string cjoPath; bool onlyUsedByMacro{false}; }; std::unordered_map>& GetPackageNameMap() { return packageNameMap; } Ptr GetPackageInfo(const std::string& fullPackageName) const; DiagnosticEngine& GetDiag() { return diag; } Ptr>> GetExportIdDeclMap(const std::string& fullPackageName) const; bool GetCanInline() const { return canInline; } OwnedPtr ReadCjo(const std::string& fullPackageName, const std::string& cjoPath, const CjoManager& cjoManager, bool printErr = true) const; void AddImportedPackages(OwnedPtr& pkg) { importedPackages.emplace_back(std::move(pkg)); } void RemoveImportedPackages(const Ptr pkg) { for (auto it = importedPackages.cbegin(); it != importedPackages.cend(); ++it) { if (it->get() == pkg) { it = importedPackages.erase(it); return; } } } auto AddLoadedPackages(std::string pkgName) { return loadedPackages.emplace(pkgName); } bool AlreadyLoaded(std::string pkgName) { return std::find(loadedPackages.begin(), loadedPackages.end(), pkgName) != loadedPackages.end(); } bool IsReExportBy(const std::string& srcPackage, const std::string& reExportPackage) const; void AddImportedPackageName(Ptr importSpec, std::pair pkgNamePair) { importedPackageNameMap.emplace(importSpec, pkgNamePair); } std::string GetPackageNameByImport(const AST::ImportSpec& importSpec) const { auto found = importedPackageNameMap.find(&importSpec); return found == importedPackageNameMap.end() ? "" : found->second.first; } bool IsImportPackage(const AST::ImportSpec& importSpec) const { auto found = importedPackageNameMap.find(&importSpec); return found == importedPackageNameMap.end() ? false : found->second.second; } void UpdateSearchPath(const std::string& cangjieModules) { searchPath.clear(); searchPath.insert(searchPath.end(), globalOptions.importPaths.cbegin(), globalOptions.importPaths.cend()); searchPath.emplace_back("."); searchPath.insert(searchPath.end(), globalOptions.environment.cangjiePaths.cbegin(), globalOptions.environment.cangjiePaths.cend()); searchPath.emplace_back(cangjieModules); } const std::vector& GetSearchPath() const { return searchPath; } void SetPackageCjoCache(const std::string& fullPackageName, const std::vector& cjoData) { if (fullPackageName.empty() || cjoData.empty()) { return; } cjoFileCacheMap[fullPackageName] = cjoData; } std::unordered_map>& GetCjoFileCacheMap() { return cjoFileCacheMap; } bool IsVisitedPackage(const std::string& fullPackageName) { return visitedPkgs.count(fullPackageName) != 0; } void AddVisitedPackage(const std::string& fullPackageName) { visitedPkgs.emplace(fullPackageName); } void AddImportsToMap(const AST::ImportSpec& import, const std::string& importedPackage, std::map& declMap) const; void ClearCjoCache() { cjoFileCacheMap.clear(); } void ClearVisitedPkgs() { visitedPkgs.clear(); } std::optional> PreReadCommonPartCjoFiles(CjoManager& cjoManager); Ptr GetCommonPartCjo(std::string expectedName); const GlobalOptions& GetGlobalOptions() { return globalOptions; } private: DiagnosticEngine& diag; TypeManager& typeManager; const GlobalOptions& globalOptions; bool& importSrcCode; std::vector searchPath; /** Only used to hold ownership of imported packages. */ std::vector> importedPackages; std::unordered_map> packageNameMap; std::unordered_map> cjoFileCacheMap; std::unordered_map, std::pair> importedPackageNameMap; // Searching cache. std::unordered_set visitedPkgs; // Indirectly imported packages which have been used is recorded in loader. Load their decls on demand. std::unordered_set loadedPackages; // common part loader also stored in `packageNameMap`. OwnedPtr commonPartLoader; bool canInline{false}; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Modules/DependencyGraph.cpp000066400000000000000000000156361510705540100227650ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the sub-class DependencyGraph of ImportManager. */ #include "cangjie/Modules/ImportManager.h" #include #include #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Modules/ASTSerialization.h" #include "cangjie/Modules/ModulesUtils.h" using namespace Cangjie; using namespace AST; const std::vector>& ImportManager::DependencyGraph::GetDirectDependencyPackageDecls( const std::string& fullPackageName) { auto cacheIter = cacheDirectDependencyPackageDecls.find(fullPackageName); if (cacheIter != cacheDirectDependencyPackageDecls.cend()) { return cacheIter->second; } std::vector> packageDecls; std::unordered_set> collected; std::queue> tmpQueue; for (auto& edge : GetEdges(fullPackageName)) { auto pkgDecl = cjoManager.GetPackageDecl(edge.first); if (pkgDecl == nullptr) { continue; } tmpQueue.push(pkgDecl); } while (!tmpQueue.empty()) { auto pkgDecl = tmpQueue.front(); tmpQueue.pop(); if (auto [_, succ] = collected.emplace(pkgDecl); succ) { packageDecls.emplace_back(pkgDecl); } if (!pkgDecl->srcPackage->isMacroPackage) { continue; } for (auto& reExportPackageName : pkgReExportMap[pkgDecl->srcPackage->fullPackageName]) { auto reExportPkgDecl = cjoManager.GetPackageDecl(reExportPackageName); CJC_NULLPTR_CHECK(reExportPkgDecl); if (!reExportPkgDecl->srcPackage->isMacroPackage && collected.count(reExportPkgDecl) == 0) { packageDecls.emplace_back(reExportPkgDecl); collected.emplace(reExportPkgDecl); } else if (reExportPkgDecl->srcPackage->isMacroPackage) { tmpQueue.push(reExportPkgDecl); collected.emplace(reExportPkgDecl); } } } auto [cacheIter1, _] = cacheDirectDependencyPackageDecls.emplace(fullPackageName, std::move(packageDecls)); return cacheIter1->second; } const std::vector>& ImportManager::DependencyGraph::GetAllDependencyPackageDecls( const std::string& fullPackageName, bool includeMacroPkg) { auto keyPair = std::make_pair(fullPackageName, includeMacroPkg); auto cacheIter = cacheDependencyPackageDecls.find(keyPair); if (cacheIter != cacheDependencyPackageDecls.cend()) { return cacheIter->second; } auto& packageNames = GetAllDependencyPackageNames(fullPackageName, includeMacroPkg); std::vector> packageDecls; for (auto& pkgName : packageNames) { auto pkgDecl = cjoManager.GetPackageDecl(pkgName); if (pkgDecl != nullptr) { packageDecls.emplace_back(pkgDecl); } } auto [cacheIter1, _] = cacheDependencyPackageDecls.emplace(keyPair, std::move(packageDecls)); return cacheIter1->second; } const std::set& ImportManager::DependencyGraph::GetAllDependencyPackageNames( const std::string& fullPackageName, bool includeMacroPkg) { auto cacheIter = cacheDependencyPackageNames.find(fullPackageName); if (cacheIter != cacheDependencyPackageNames.cend()) { return cacheIter->second; } std::set packageNames; // Collect all the dependencies by DFS. std::vector stack; for (auto& edge : GetEdges(fullPackageName)) { stack.emplace_back(edge.first); } while (!stack.empty()) { auto pkgName = std::move(stack.back()); stack.pop_back(); if (packageNames.count(pkgName) > 0) { continue; } for (auto& edge : GetEdges(pkgName)) { stack.emplace_back(edge.first); } packageNames.emplace(std::move(pkgName)); } Utils::EraseIf(packageNames, [this, includeMacroPkg](auto it) { return !includeMacroPkg && cjoManager.IsMacroRelatedPackageName(it); }); auto [cacheIter1, _] = cacheDependencyPackageNames.emplace(fullPackageName, std::move(packageNames)); return cacheIter1->second; } const std::map, CmpNodeByPos>>& ImportManager::DependencyGraph::GetEdges( const std::string& fullPackageName) const { const static std::map, AST::CmpNodeByPos>> EMPTY_EDGES; auto iter = dependencyMap.find(fullPackageName); if (iter != dependencyMap.cend()) { return iter->second; } return EMPTY_EDGES; } void ImportManager::DependencyGraph::AddDependenciesForPackage(Package& pkg) { if (dependencyMap.count(pkg.fullPackageName) != 0) { // Ignore recursive dependency. return; } for (auto& file : pkg.files) { CJC_NULLPTR_CHECK(file); for (auto& import : file->imports) { CJC_NULLPTR_CHECK(import); AddDependenciesForImport(pkg, *import); } } } void ImportManager::DependencyGraph::AddDependenciesForImport(Package& pkg, const ImportSpec& import) { auto fullPackageName = cjoManager.GetPackageNameByImport(import); auto package = cjoManager.GetPackage(fullPackageName); if (package == nullptr || package->fullPackageName == pkg.fullPackageName) { return; // The import may be failed. Also ignore self import. } AddDependency(pkg.fullPackageName, package->fullPackageName, import); AddDependenciesForPackage(*package); auto relation = Modules::GetPackageRelation(pkg.fullPackageName, fullPackageName); if (import.IsReExport() && Modules::IsVisible(import, relation)) { pkgReExportMap[pkg.fullPackageName].emplace(fullPackageName); } } // There is an `import v` in package `u`. void ImportManager::DependencyGraph::AddDependency( const std::string& u, const std::string& v, const AST::ImportSpec& import) { auto uIter = dependencyMap.find(u); if (uIter == dependencyMap.cend()) { std::set, AST::CmpNodeByPos> imports = {&import}; std::map, AST::CmpNodeByPos>> edges; edges.emplace(v, std::move(imports)); dependencyMap.emplace(u, std::move(edges)); } else { std::map, AST::CmpNodeByPos>>& edges = uIter->second; auto vIter = edges.find(v); if (vIter == edges.cend()) { std::set, AST::CmpNodeByPos> imports = {&import}; edges.emplace(v, std::move(imports)); } else { vIter->second.emplace(&import); } } } cangjie_compiler-1.0.7/src/Modules/ImportManager.cpp000066400000000000000000001520561510705540100224700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the ImportManager related classes. */ #include "cangjie/Modules/ImportManager.h" #include #include #include #include #include #include "ModulesDiag.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/SourceManager.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Modules/ASTSerialization.h" #include "cangjie/Modules/ModulesUtils.h" using namespace Cangjie; using namespace AST; using namespace Modules; namespace { OwnedPtr CreateImportSpec( const std::string& fullPackageName, const std::string& item = "*", const std::string& alias = "") { auto import = MakeOwned(); auto names = Utils::SplitQualifiedName(fullPackageName); CJC_ASSERT(names.size() >= 1 && !item.empty()); if (!alias.empty()) { import->content.kind = ImportKind::IMPORT_ALIAS; } else if (item != "*") { import->content.kind = ImportKind::IMPORT_SINGLE; } else { import->content.kind = ImportKind::IMPORT_ALL; } import->content.prefixPaths = names; import->content.identifier = item; import->content.identifier.SetPos(DEFAULT_POSITION, DEFAULT_POSITION); import->content.aliasName = alias; import->content.aliasName.SetPos(DEFAULT_POSITION, DEFAULT_POSITION); import->EnableAttr(Attribute::COMPILER_ADD, Attribute::IMPLICIT_ADD, Attribute::PRIVATE); return import; } void AddImport( File& file, const std::string& fullPackageName, const std::string& item = "*", const std::string& alias = "") { file.imports.emplace_back(CreateImportSpec(fullPackageName, item, alias)); } bool HasIfAvailable(Package& pkg) { bool ret = false; auto hasIfAvailable = [&ret](Ptr node) { if (auto mee = DynamicCast(node); mee && mee->invocation.IsIfAvailable()) { ret = true; return VisitAction::STOP_NOW; } return VisitAction::WALK_CHILDREN; }; Walker(&pkg, hasIfAvailable).Walk(); return ret; } void AddImplicitImports(Package& pkg, const GlobalOptions& opts) { for (auto& file : pkg.files) { CJC_NULLPTR_CHECK(file); if (opts.implicitPrelude) { AddImport(*file, CORE_PACKAGE_NAME); } if (opts.enableCompileTest) { AddImport(*file, "std.unittest"); AddImport(*file, "std.unittest.testmacro"); } if (opts.compileTestsOnly && ImportManager::IsTestPackage(pkg.fullPackageName)) { AddImport(*file, ImportManager::GetMainPartPkgNameForTestPkg(pkg.fullPackageName)); } if (opts.target.env == Triple::Environment::OHOS && HasIfAvailable(pkg)) { // For @IfAvailable desugar, depend on 'ohos.device_info.DeviceInfo' and 'ohos.base.canIUse'. AddImport(*file, "ohos.device_info"); AddImport(*file, "ohos.base"); } } } struct DependencyInfoItem { std::string package; std::string orPackage; bool isStd{false}; std::set, CmpNodeByPos> imports; }; std::string Jsonfy(bool b) { return b ? "true" : "false"; } std::string Jsonfy(AccessLevel level) { CJC_ASSERT(level != AccessLevel::PRIVATE); return level == AccessLevel::PUBLIC ? "public" : level == AccessLevel::PROTECTED ? "protected" : "internal"; } std::string Jsonfy(const std::string& s) { // Accroding to RFC 8259: All Unicode characters may be placed within the quotation marks, // except for the characters that MUST be escaped: quotation mark, reverse solidus, // and the control characters (U+0000 through U+001F) std::ostringstream out; for (char c : s) { if (c == '\\' || c == '"') { out << '\\' << c; } else if ('\x00' <= c && c <= '\x1f') { // According to RFC 8259: 4 hexadecimal digits are used to encode the character's code point. out << '\\' << 'u' << std::setfill('0') << std::setw(4) << std::hex << static_cast(c); } else { out << c; } } return out.str(); } std::string Jsonfy(const Position& pos) { std::ostringstream out; out << "{" << "\"line\":" << pos.line << "," << "\"column\":" << pos.column << "}"; return out.str(); } std::string Jsonfy(const ImportSpec& import, bool exportCJO) { CJC_NULLPTR_CHECK(import.curFile); std::ostringstream out; out << "{" << "\"file\":\"" << Jsonfy(exportCJO ? import.curFile->fileName : import.curFile->filePath) << "\"," << "\"begin\":" << Jsonfy(import.begin) << "," << "\"end\":" << Jsonfy(import.end) << "}"; return out.str(); } std::string Jsonfy(const std::set, CmpNodeByPos>& imports, bool exportCJO) { if (imports.empty()) { return "[]"; } std::ostringstream out; out << "["; auto iter = imports.cbegin(); out << Jsonfy(**iter, exportCJO); ++iter; while (iter != imports.cend()) { out << "," << Jsonfy(**iter, exportCJO); ++iter; } out << "]"; return out.str(); } std::string Jsonfy(const DependencyInfoItem& info, bool exportCJO) { std::ostringstream out; out << "{" << "\"package\":\"" << Jsonfy(info.package) << "\","; if (!info.orPackage.empty()) { out << "\"orPackage\":\"" << Jsonfy(info.orPackage) << "\","; } out << "\"isStd\":" << Jsonfy(info.isStd) << ","; out << "\"imports\":" << Jsonfy(info.imports, exportCJO) << "}"; return out.str(); } std::string Jsonfy(const std::map& dependencies, bool exportCJO) { if (dependencies.empty()) { return "[]"; } std::ostringstream out; out << "["; auto iter = dependencies.cbegin(); out << Jsonfy(iter->second, exportCJO); ++iter; while (iter != dependencies.cend()) { out << "," << Jsonfy(iter->second, exportCJO); ++iter; } out << "]"; return out.str(); } std::string Jsonfy(const std::set& features) { if (features.empty()) { return "[]"; } std::ostringstream out; out << "["; auto iter = features.cbegin(); out << "\"" << Jsonfy(*iter) << "\""; ++iter; while (iter != features.cend()) { out << "," << "\"" << Jsonfy(*iter) << "\""; ++iter; } out << "]"; return out.str(); } Range GetPackageNameRange(const CjoManager& cjoManager, const ImportSpec& import) { auto& im = import.content; return import.TestAttr(Attribute::IMPLICIT_ADD) ? MakeRange(DEFAULT_POSITION, im.identifier) : (cjoManager.IsImportPackage(import) ? (im.prefixPoses.empty() ? MakeRange(im.identifier) : MakeRange(im.prefixPoses.front(), im.identifier.End())) : MakeRange(im.prefixPoses.front(), im.prefixPoses.back() + im.prefixPaths.back().size())); } } // namespace void ImportManager::ExportAST(bool saveFileWithAbsPath, std::vector& astData, const Package& pkg, const std::function additionalSerializations) { ASTWriter writer(diag, GeneratePkgDepInfo(pkg), {importSrcCode, false, opts.exportForTest, saveFileWithAbsPath, opts.compileCjd}, *cjoManager); if (opts.outputMode == GlobalOptions::OutputMode::CHIR) { writer.SetSerializingCommon(); } auto realWriter = &writer; auto packageDecl = cjoManager->GetPackageDecl(pkg.fullPackageName); CJC_NULLPTR_CHECK(packageDecl); auto found = astWriters.find(&pkg); if (found != astWriters.end()) { realWriter = found->second; } else { writer.PreSaveFullExportDecls(*packageDecl->srcPackage); } realWriter->ExportAST(*packageDecl); additionalSerializations(*realWriter); realWriter->AST2FB(astData, *packageDecl); } std::vector ImportManager::ExportASTSignature(const Package& pkg) { ASTWriter writer( diag, {}, {.exportContent = true, .exportForIncr = true, .compileCjd = opts.compileCjd}, *cjoManager); auto packageDecl = cjoManager->GetPackageDecl(pkg.fullPackageName); CJC_NULLPTR_CHECK(packageDecl); writer.PreSaveFullExportDecls(*packageDecl->srcPackage); writer.ExportAST(*packageDecl); std::vector astData; writer.AST2FB(astData, *packageDecl); return astData; } void ImportManager::ExportDeclsWithContent(bool saveFileWithAbsPath, Package& package) { // NOTE: If 'importSrcCode' is disabled, we also do not need to export source code. auto writer = new ASTWriter(diag, GeneratePkgDepInfo(package), {importSrcCode, false, opts.exportForTest, saveFileWithAbsPath, opts.compileCjd}, *cjoManager); if (opts.outputMode == GlobalOptions::OutputMode::CHIR) { writer->SetSerializingCommon(); } writer->PreSaveFullExportDecls(package); if (auto [it, success] = astWriters.emplace(&package, writer); !success) { delete it->second; astWriters[&package] = writer; } } std::unordered_set ImportManager::LoadCachedTypeForPackage( const Package& pkg, const std::map>& mangledName2DeclMap) { auto cachedCjo = opts.GenerateCachedPathName(pkg.fullPackageName, SERIALIZED_FILE_EXTENSION); return cjoManager->LoadCachedPackage(pkg, cachedCjo, mangledName2DeclMap); } bool ImportManager::HandleParsedPackage( const Package& package, const std::string& filePath, bool isUsedAsCommon, bool isRecursive) { // `onlyUsedByMacro = false` means we have collected STD deps before, but `isRecursive` // may be different from previous scene, so we need handle for STD deps again. if (cjoManager->IsOnlyUsedByMacro(package.fullPackageName) && (curPackage->isMacroPackage || (isUsedAsCommon && !package.isMacroPackage))) { // The current package has ever been macro-related, but now it's used in non-macro scene, // so we need collect STD deps for this package and its dependent packages. cjoManager->SetOnlyUsedByMacro(package.fullPackageName, false); HandleSTDPackage(package.fullPackageName, filePath, isRecursive); return ResolveImportedPackageHeaders(package, true); } return true; } void ImportManager::SaveDepPkgCjdPath(const std::string& fullPackageName, const std::string& cjoPath) { // Replace the suffix directly. Ensure that .cjo and .cj.d are in the same path. std::string cjdPath = cjoPath.substr(0, cjoPath.rfind(SERIALIZED_FILE_EXTENSION)); cjdPath = cjdPath + CJ_D_FILE_EXTENSION; cjdFilePaths.emplace(fullPackageName, cjdPath); } bool ImportManager::ResolveImportedPackageForFile(File& file, bool isRecursive) { bool success{true}; auto curPkg = file.curPackage; CJC_NULLPTR_CHECK(curPkg); bool isMacroRelated = curPkg->isMacroPackage || IsMacroRelatedPackageName(curPkg->fullPackageName); auto relation = Modules::GetPackageRelation(curPackage->fullPackageName, curPkg->fullPackageName); for (auto& import : file.imports) { if (import->IsImportMulti()) { continue; } auto isVisible = import->IsReExport() && Modules::IsVisible(*import, relation); auto [fullPackageName, cjoPath] = cjoManager->GetPackageCjo(*import); SaveDepPkgCjdPath(fullPackageName, cjoPath); // 1. Handle the package which has been parsed before. auto package = cjoManager->GetPackage(fullPackageName); if (package != nullptr) { CJC_ASSERT( !package->TestAttr(Attribute::IMPORTED) || package->TestAttr(Attribute::TOOL_ADD) || !cjoPath.empty()); if (!package->TestAttr(Attribute::IMPORTED) && curPkg->TestAttr(Attribute::IMPORTED)) { // Source package have same name with indirect dependent package. Was reported during loading package. success = false; diag.DiagnoseRefactor(DiagKindRefactor::module_same_name_with_indirect_dependent_pkg, DEFAULT_POSITION, fullPackageName, curPkg->fullPackageName); } else { success = HandleParsedPackage(*package, cjoPath, !isMacroRelated || isVisible, isRecursive) && success; } continue; } auto isMainPartPkgForTestPkg = opts.compileTestsOnly && IsTestPackage(curPkg->fullPackageName) && GetMainPartPkgNameForTestPkg(curPkg->fullPackageName) == fullPackageName; // 2. Check and try to load cjo data. if (!CheckCjoPathLegality(import, cjoPath, fullPackageName, isRecursive, isMainPartPkgForTestPkg) || !cjoManager->LoadPackageHeader(fullPackageName, cjoPath)) { success = false; continue; } package = cjoManager->GetPackage(fullPackageName); CJC_NULLPTR_CHECK(package); // 3. Store loaded package node. // Do not need to load indirectly dependent macro package which is not re-exported and is macro related. // NOTE: since allowing macro package to re-export normal package, // we cannot ignore macro package imported by normal package. if (package->isMacroPackage && isRecursive && !isVisible && isMacroRelated) { // If it's the first time to load `fullPackageName` and current package is macro-related, // `fullPackageName` isn't being used now, we need to remove it and then re-import it when it's being used. cjoManager->RemovePackage(fullPackageName, package); continue; } // Package are needed for CodeGen in the following scenarios: // a. If the package is not a macro dependency package (import by normal package or import by case c's package). // b. In the scenario of compiling a macro package. // c. If the package is not a macro package and non-internal reExported by a macro package. If the package is // internal, it's only necessary if current package is its subpackage. auto macroReExportCommonPackage = isVisible && isMacroRelated && !package->isMacroPackage; if ((!IsMacroRelatedPackageName(file.curPackage->fullPackageName) && !package->isMacroPackage) || curPackage->isMacroPackage || macroReExportCommonPackage) { // If package is needed for CodeGen, we also should collect the standard library dependencies. HandleSTDPackage(fullPackageName, cjoPath, isRecursive); } else { cjoManager->SetOnlyUsedByMacro(fullPackageName, true); } // 4. Resolve current package's dependent packages by DFS. success = ResolveImportedPackageHeaders(*package, true) && success; } return success; } void ImportManager::SetImportedPackageFromASTNode(std::vector>& pkgs) { for (auto& pkg : pkgs) { cjoManager->AddImportedPackageFromASTNode(std::move(pkg)); } } void ImportManager::HandleSTDPackage(const std::string& fullPackageName, const std::string& cjoPath, bool isRecursive) { auto type = isRecursive ? DepType::INDIRECT : DepType::DIRECT; auto item = stdDepsMap.find(cjoPath); if (item != stdDepsMap.end()) { if (item->second != type) { item->second = DepType::BOTH; } return; } if ((STANDARD_LIBS.find(fullPackageName) != STANDARD_LIBS.end()) && FileUtil::FileExist(cjoPath)) { stdDepsMap.emplace(cjoPath, type); } } bool ImportManager::CheckCjoPathLegality(const OwnedPtr& import, const std::string& cjoPath, const std::string& fullPackageName, bool isRecursive, bool isMainPartPkgForTestPkg) { // Call guarantees the 'cjoPath' existing when it is not empty. if (cjoPath.empty()) { Range range = isRecursive ? MakeRange(import->begin, import->begin + 1) : GetPackageNameRange(*cjoManager, *import); CJC_ASSERT(!range.HasZero()); // Except for IMPLICIT_ADD, there should be no zero. DiagnosticBuilder builder = diag.DiagnoseRefactor(isMainPartPkgForTestPkg ? DiagKindRefactor::package_missed_cjo_main_pkg_part_for_test_pkg : DiagKindRefactor::package_search_error, range, fullPackageName); builder.AddHelp(DiagHelp(Modules::NO_CJO_HELP_INFO)); return false; } return true; } bool ImportManager::ResolveImportedPackageHeaders(const Package& package, bool isRecursive) { bool success{true}; for (auto& file : package.files) { if (!ResolveImportedPackageForFile(*file, isRecursive)) { success = false; } } return success; } void ImportManager::UpdateMacroPackageUsage(const AST::Package& pkg) { for (auto& pd : GetCurImportedPackages(pkg.fullPackageName)) { // Collect dependent cangjie packages. if (!pd->srcPackage->TestAttr(Attribute::TOOL_ADD) && pd->srcPackage->isMacroPackage) { (void)directMacroDeps.emplace(pd->srcPackage); } } } bool ImportManager::ResolveImportedPackages(const std::vector>& packages) { bool success = true; for (auto pkg : packages) { curPackage = pkg; success = ResolveImportedPackageHeaders(*curPackage, false) && success; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND cjoManager->LoadPackageDeclsOnDemand(packages); #endif for (auto pkg : packages) { cjoManager->AddPackageDeclMap(pkg->fullPackageName); } return success; } void ImportManager::DeleteASTWriters() noexcept { for (auto& [_, writer] : astWriters) { delete writer; writer = nullptr; } astWriters.clear(); } void ImportManager::DeleteASTLoaders() noexcept { if (cjoManager) { cjoManager->DeleteASTLoaders(); } } void ImportManager::ResolveImports(const AST::Package& pkg) { // NOTE: only update macro package usage for source package. UpdateMacroPackageUsage(pkg); // 1. Report errors for invalid imports. CheckImports(pkg); // 2. Report shadowing & collision warnings for source package. CheckRedefinition(pkg); } void ImportManager::CheckImports(const AST::Package& pkg) { for (auto& file : pkg.files) { for (auto& import : file->imports) { if (import->IsImportMulti()) { continue; } auto fullPackageName = cjoManager->GetPackageNameByImport(*import); auto package = cjoManager->GetPackage(fullPackageName); if (fullPackageName.empty() || !package) { continue; // Failed to import the package. } auto range = GetPackageNameRange(*cjoManager, *import); if (fullPackageName != package->fullPackageName) { diag.DiagnoseRefactor( DiagKindRefactor::package_import_inconsistent, range, package->fullPackageName, fullPackageName); continue; } else if (fullPackageName == pkg.fullPackageName) { diag.DiagnoseRefactor(DiagKindRefactor::package_import_itself_illegal, range, fullPackageName); continue; } if (import->TestAttr(Attribute::IMPLICIT_ADD)) { continue; } auto relation = GetPackageRelation(pkg.fullPackageName, fullPackageName); if ((package->accessible == AccessLevel::PROTECTED && relation == PackageRelation::NONE) || (package->accessible == AccessLevel::INTERNAL && relation != PackageRelation::CHILD && !IsSuperPackage(pkg.fullPackageName, fullPackageName))) { // Report error when the package is not accessible. diag.DiagnoseRefactor(DiagKindRefactor::package_accessibility, range, fullPackageName, GetAccessLevelStr(*package), RelationToString(relation), pkg.fullPackageName); } else if (import->IsReExport() && !pkg.isMacroPackage && package->isMacroPackage) { diag.DiagnoseRefactor(DiagKindRefactor::package_cannot_export_macro_package, range); continue; } else if (import->IsImportAll()) { continue; } else if (cjoManager->IsImportPackage(*import)) { if (import->IsReExport()) { diag.DiagnoseRefactor(DiagKindRefactor::package_re_export_package_name, range, fullPackageName, GetAccessLevelStr(*import)); } continue; } // Report error when the imported name cannot be found. auto decls = cjoManager->GetPackageMembersByName(package->fullPackageName, import->content.identifier); Utils::EraseIf(decls, [relation](auto it) { return !Modules::IsVisible(*it, relation); }); if (decls.empty()) { auto& im = import->content; auto nameRange = MakeRange(im.prefixPoses.front(), im.identifier.End()); diag.DiagnoseRefactor( DiagKindRefactor::package_decl_not_find_in_package, nameRange, im.identifier, fullPackageName); } } } } void ImportManager::CheckRedefinition(const AST::Package& pkg) { std::unordered_map pkgImportNamePos; std::unordered_map, std::unordered_map> fileImportNamePos; for (auto& file : pkg.files) { for (auto& import : file->imports) { if (import->IsImportMulti() || import->IsImportAll()) { continue; } auto& posMap = import->IsReExport() ? pkgImportNamePos : fileImportNamePos[file.get()]; auto& im = import->content; auto& name = im.kind == ImportKind::IMPORT_SINGLE ? im.identifier : im.aliasName; auto decls = GetImportedDeclsByName(*file, name); if (decls.empty()) { continue; // Was failed to import package member. } auto range = im.kind == ImportKind::IMPORT_SINGLE ? MakeRange(im.identifier) : MakeRange(im.aliasName); auto [it, success] = posMap.emplace(name, range); if (import->IsReExport() && success) { std::tie(it, success) = fileImportNamePos[file.get()].emplace(name, range); } auto& pkgMembers = cjoManager->GetPackageMembersByName(pkg.fullPackageName, name); std::vector> sourceDecls; std::copy_if(pkgMembers.cbegin(), pkgMembers.cend(), std::back_inserter(sourceDecls), [&pkg](auto it1) { return it1->curFile->curPackage == &pkg; }); if (!IsAllFuncDecl(sourceDecls)) { WarnUselessImport(diag, range, *(*sourceDecls.begin())); } else if (!success) { WarnConflictImport(diag, name, range, it->second); } } } } std::string ImportManager::GeneratePkgDepInfo(const Package& pkg, bool exportCJO) const { auto isStd = [](const std::string& fullPackageName) { return STANDARD_LIBS.count(fullPackageName) > 0; }; std::map dependencies; std::set refSet; for (auto& file : pkg.files) { for (auto& import : file->imports) { if (import->IsImportMulti() || import->TestAttr(Attribute::IMPLICIT_ADD)) { continue; } auto names = cjoManager->GetPossibleCjoNames(*import); CJC_ASSERT(!names.empty()); std::string longName = FileUtil::ToPackageName(names.front()); std::string shortName = FileUtil::ToPackageName(names.back()); auto iter = dependencies.find(longName); if (iter == dependencies.cend()) { DependencyInfoItem dependInfo{"", "", false, {}}; if (isStd(longName)) { dependInfo.package = longName; dependInfo.isStd = true; } else if (isStd(shortName)) { dependInfo.package = shortName; dependInfo.isStd = true; } else { dependInfo.package = shortName; dependInfo.orPackage = longName == shortName ? "" : longName; dependInfo.isStd = isStd(shortName); } dependencies.emplace(longName, dependInfo); } dependencies.at(longName).imports.emplace(import.get()); } if (file->feature != nullptr) { for (auto& feature : file->feature->content) { refSet.insert(feature.ToString()); } } } std::ostringstream out; out << "{" << "\"package\":\"" << Jsonfy(pkg.fullPackageName) << "\"," << "\"isMacro\":" << Jsonfy(pkg.isMacroPackage) << "," << "\"accessLevel\":\"" << Jsonfy(pkg.accessible) << "\"," << "\"dependencies\":" << Jsonfy(dependencies, exportCJO) << "," << "\"features\":" << Jsonfy(refSet) << "}"; return out.str(); } std::vector ImportManager::GeneratePkgDepInfoByCjo(const std::string& cjoPath) { std::string packageDepInfo = cjoManager->GetPackageDepInfo(cjoPath); if (packageDepInfo.empty()) { return {}; } // Note: wrap with std::vector to adapt current interface. return {packageDepInfo}; } std::vector ImportManager::GeneratePkgDepInfo(const std::vector>& packages) const { std::vector res; for (auto& pkg : packages) { CJC_NULLPTR_CHECK(pkg); std::string depInfo = GeneratePkgDepInfo(*pkg, false); res.emplace_back(depInfo); } return res; } std::set ImportManager::CollectDirectDepPkg(const Package& package) const { std::set depPkgs; for (auto& file : package.files) { for (auto& import : file->imports) { if (import->IsImportMulti()) { continue; } // Collect all possible package names. for (auto name : cjoManager->GetPossibleCjoNames(*import)) { depPkgs.emplace(name); } } } return depPkgs; } static void ValidateFileFeatureSpec(DiagnosticEngine &diag, const Package& pkg, std::unordered_map& refMap, Ptr& refFile) { size_t refSize = 0; std::unordered_map rangeMap; for (auto& file : pkg.files) { CJC_NULLPTR_CHECK(file); if (file->feature != nullptr) { for (auto& feature : file->feature->content) { std::string ftrStr = feature.ToString(); auto prevPos = rangeMap.find(ftrStr); bool contains = prevPos != rangeMap.end(); Range current = MakeRange(feature.begin, feature.end); if (contains) { WarnRepeatedFeatureName(diag, ftrStr, current, prevPos->second); } else { rangeMap.emplace(ftrStr, current); } } if (refSize < rangeMap.size()) { refSize = rangeMap.size(); refFile = file; } rangeMap.clear(); } } for (auto& ftr : refFile->feature->content) { refMap.emplace(ftr.ToString(), false); } } static void CollectInvalidFeatureFiles(const Package& pkg, std::vector>& invalidFeatures, std::unordered_map& refMap) { for (auto& file : pkg.files) { if (file->feature == nullptr) { invalidFeatures.emplace_back(file); continue; } bool hasInvalidName{false}; for (auto& feature : file->feature->content) { std::string ftrStr = feature.ToString(); auto pair = refMap.find(ftrStr); if (pair != refMap.end()) { pair->second = true; } else { hasInvalidName = true; invalidFeatures.emplace_back(file); break; } } if (!hasInvalidName) { for (auto& pair : refMap) { if (!pair.second) { invalidFeatures.emplace_back(file); break; } } } std::for_each(refMap.begin(), refMap.end(), [](auto& pair) { pair.second = false; } ); } } static void CheckPackageFeatureSpec(DiagnosticEngine& diag, const Package& pkg) { std::unordered_map refMap; std::vector> invalidFeatures; Ptr refFile; ValidateFileFeatureSpec(diag, pkg, refMap, refFile); CollectInvalidFeatureFiles(pkg, invalidFeatures, refMap); if (!invalidFeatures.empty()) { uint8_t counter = 0; for (auto& file : invalidFeatures) { if (counter > 1) { return; } if (file->feature == nullptr) { DiagForNullPackageFeature(diag, MakeRange(file->begin, file->end), refFile->feature); } else { auto& feature = file->feature; DiagForDifferentPackageFeatureConsistency(diag, feature, refFile->feature); } counter++; } } } static bool IsRootPackage(const PackageSpec& p) { if (p.hasDoubleColon && p.prefixPaths.size() == 1) { return true; } return p.prefixPaths.empty(); } static void CheckPackageSpecsIdentical(DiagnosticEngine& diag, const Package& pkg) { // the map of the key is declared package name with it's modifier (default is 'public'), // and the value is a pair whose first element is package name // position and second indicates whether current package declaration is an implicit default package. std::map, std::pair> packageNamePosMap; for (auto& file : pkg.files) { CJC_NULLPTR_CHECK(file); if ((file->package == nullptr || file->begin == file->end) && !Utils::InKeys(std::make_pair(DEFAULT_PACKAGE_NAME, std::string("public")), packageNamePosMap)) { packageNamePosMap[std::make_pair(DEFAULT_PACKAGE_NAME, "public")] = {file->begin, false}; } else if (file->package != nullptr) { if (IsRootPackage(*file->package) && !file->package->TestAttr(Attribute::PUBLIC)) { DiagRootPackageModifier(diag, *file->package); } auto fullPackageName = file->package->GetPackageName(); auto keyPair = std::make_pair(std::move(fullPackageName), GetAccessLevelStr(*file->package)); if (!Utils::InKeys(keyPair, packageNamePosMap)) { auto position = file->package->prefixPaths.empty() ? file->package->packageName.Begin() : file->package->prefixPoses.front(); packageNamePosMap[keyPair] = {position, true}; } } } if (packageNamePosMap.size() > 1) { DiagForDifferentPackageNames(diag, packageNamePosMap); } } bool ImportManager::BuildIndex( const std::string& cangjieModules, const GlobalOptions& globalOptions, std::vector>& packages) { CJC_ASSERT(!packages.empty()); cjoManager->UpdateSearchPath(cangjieModules); for (auto pkg : packages) { if (pkg->HasFeature()) { CheckPackageFeatureSpec(diag, *pkg); } CheckPackageSpecsIdentical(diag, *pkg); if (opts.compileTestsOnly) { pkg->fullPackageName = pkg->fullPackageName + SourceManager::testPkgSuffix; } AddImplicitImports(*pkg, globalOptions); cjoManager->AddSourcePackage(*pkg); } ResolveImportedPackages(packages); ClearPackageCjoCache(); // Create dependency info after load all packages. for (auto pkg : packages) { dependencyGraph->AddDependenciesForPackage(*pkg); } for (auto pkg : packages) { AddImportedDeclsForSourcePackage(*pkg); } for (auto pkg : packages) { ResolveImports(*pkg); } for (auto& pd : GetAllImportedPackages()) { if (pd->srcPackage->hasSourceImportedDecl) { packages.push_back(pd->srcPackage); } } curPackage = packages.front(); return true; } Ptr ImportManager::LoadPackageFromCjo(const std::string& fullPkgName, const std::string& cjoPath) const { if (!cjoManager->LoadPackageHeader(fullPkgName, cjoPath)) { return nullptr; } std::function loadDependentPkgHeader = [&loadDependentPkgHeader, this](const Package& pkg) { for (auto& file : std::as_const(pkg.files)) { for (auto& import : std::as_const(file->imports)) { if (import->IsImportMulti()) { continue; } auto [fullDepPkgName, cjoPath1] = cjoManager->GetPackageCjo(*import); if (auto depPkg = cjoManager->GetPackage(fullDepPkgName); depPkg) { continue; } if (!cjoManager->LoadPackageHeader(fullDepPkgName, cjoPath1)) { return false; } auto depPkg = cjoManager->GetPackage(fullDepPkgName); CJC_NULLPTR_CHECK(depPkg); if (!loadDependentPkgHeader(*depPkg)) { return false; } } } return true; }; auto package = cjoManager->GetPackage(fullPkgName); CJC_NULLPTR_CHECK(package); if (!loadDependentPkgHeader(*package)) { return nullptr; } cjoManager->LoadPackageDeclsOnDemand({package}, true); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND cjoManager->ClearVisitedPkgs(); #endif cjoManager->AddPackageDeclMap(fullPkgName); return package; } // Use for macro debug: update the debug file info void ImportManager::UpdateFileNodeImportInfo(Package& package, const File& file, OwnedPtr& newFile) { CJC_ASSERT(newFile); newFile->curPackage = &package; // Replace the current file. CJC_ASSERT(file.fileHash != newFile->fileHash); // Copy the compiler added implicitly imports. for (auto& import : file.imports) { if (import->TestAttr(Attribute::IMPLICIT_ADD)) { (void)newFile->imports.emplace_back(ASTCloner::Clone(import.get())); } } for (auto& import : newFile->imports) { if (import->IsImportMulti()) { continue; } auto [_, cjoPath] = cjoManager->GetPackageCjo(*import); if (cjoPath.empty()) { InternalError("Failed to resolve imports for macro file"); } } // MacroExpansion will never change import declarations, just copy file import map for new file and remove old one. auto found = fileImportedDeclsMap.find(file.fileHash); if (found != fileImportedDeclsMap.end()) { (void)fileImportedDeclsMap.emplace(newFile->fileHash, std::move(found->second)); (void)fileImportedDeclsMap.erase(file.fileHash); } auto index = file.indexOfPackage; newFile->indexOfPackage = index; package.files[index] = std::move(newFile); } void ImportManager::SetPackageBchirCache(const std::string& fullPackageName, const std::vector& bchirData) { if (fullPackageName.empty() || bchirData.empty()) { return; } bchirFileCacheMap[fullPackageName] = bchirData; } const std::vector* ImportManager::GetBchirCache(const std::string& fullPackageName) { auto it = bchirFileCacheMap.find(fullPackageName); if (it != bchirFileCacheMap.end()) { return &(it->second); } return nullptr; } Ptr ImportManager::GetPackageDecl(const std::string& fullPackageName) const { return cjoManager->GetPackageDecl(fullPackageName); } Ptr ImportManager::GetPackage(const std::string& fullPackageName) const { return cjoManager->GetPackage(fullPackageName); } void ImportManager::AddUsedMacroDecls(const Ptr file, Ptr decl) { auto& usedDeclMap = fileUsedMacroDeclsMap[file->indexOfPackage]; auto& declSet = usedDeclMap[decl->fullPackageName]; declSet.emplace(decl); } std::map, AST::CmpNodeByPos>>& ImportManager::GetUsedMacroDecls( const AST::File& file) { return fileUsedMacroDeclsMap[file.indexOfPackage]; } void ImportManager::AddImportedDeclsForSourcePackage(const AST::Package& pkg) { for (auto& file : pkg.files) { for (auto& import : file->imports) { auto& declMap = import->IsReExport() ? importedDeclsMap : fileImportedDeclsMap[file->fileHash]; CJC_NULLPTR_CHECK(import); if (import->IsImportMulti()) { continue; } auto fullPackageName = cjoManager->GetPackageNameByImport(*import); auto pkgDecl = cjoManager->GetPackageDecl(fullPackageName); if (fullPackageName.empty() || !pkgDecl) { continue; // Load package failed. } auto relation = GetPackageRelation(pkg.fullPackageName, fullPackageName); auto importLevel = GetAccessLevel(*import); if (import->content.kind == ImportKind::IMPORT_ALL) { auto members = cjoManager->GetPackageMembers(fullPackageName); for (auto& member : std::as_const(members)) { auto& targetMap = declMap[member.first]; auto visibleDecls = GetVisibleDeclToMap(member.second, importLevel, relation); std::for_each(visibleDecls.cbegin(), visibleDecls.cend(), [this, &import, &pkg](auto it) { declsImportedByNodeMap[pkg.fullPackageName][it].emplace_back(import.get()); }); targetMap.merge(visibleDecls); std::for_each(member.second.begin(), member.second.end(), [this, member](auto decl) { if (decl->identifier != member.first) { declToTypeAlias[decl].emplace(decl->identifier); } }); } continue; } if (cjoManager->IsImportPackage(*import)) { auto name = import->content.kind == ImportKind::IMPORT_SINGLE ? Utils::SplitQualifiedName(fullPackageName, true).back() : import->content.aliasName.Val(); declMap[name].emplace(pkgDecl); declsImportedByNodeMap[pkg.fullPackageName][pkgDecl].emplace_back(import.get()); } else { auto members = cjoManager->GetPackageMembersByName(fullPackageName, import->content.identifier); const auto& name = import->content.kind == ImportKind::IMPORT_SINGLE ? import->content.identifier.Val() : import->content.aliasName.Val(); auto& targetMap = declMap[name]; auto visibleDecls = GetVisibleDeclToMap(members, importLevel, relation); for (auto member : members) { if (auto tad = DynamicCast(member); tad && tad->type) { auto originDecl = tad->type->GetTarget(); declToTypeAlias[originDecl].emplace(name); } else if (member->identifier != name) { declToTypeAlias[member].emplace(name); declToTypeAlias[member].emplace(member->identifier); } } std::for_each(visibleDecls.cbegin(), visibleDecls.cend(), [this, &import, &pkg](auto it) { declsImportedByNodeMap[pkg.fullPackageName][it].emplace_back(import.get()); }); targetMap.merge(visibleDecls); import->content.isDecl = true; } } } } std::vector> ImportManager::GetImportedDeclsByName(const File& file, const std::string& name) const { OrderedDeclSet ret; auto found = importedDeclsMap.find(name); if (found != importedDeclsMap.cend()) { ret.insert(found->second.cbegin(), found->second.cend()); } auto fileIter = fileImportedDeclsMap.find(file.fileHash); if (fileIter != fileImportedDeclsMap.cend()) { auto iter = fileIter->second.find(name); if (iter != fileIter->second.cend()) { ret.insert(iter->second.cbegin(), iter->second.cend()); } } return Utils::SetToVec>(ret); } std::vector>>> ImportManager::GetImportedDecls(const File& file) const { std::vector>>> res; std::transform(importedDeclsMap.cbegin(), importedDeclsMap.cend(), std::back_inserter(res), [](auto& pair) { std::vector> decls = Utils::SetToVec>(pair.second); return std::make_pair(pair.first, decls); }); auto iter = fileImportedDeclsMap.find(file.fileHash); if (iter != fileImportedDeclsMap.cend()) { std::transform(iter->second.cbegin(), iter->second.cend(), std::back_inserter(res), [](auto& pair) { std::vector> decls = Utils::SetToVec>(pair.second); return std::make_pair(pair.first, decls); }); } return res; } const OrderedDeclSet& ImportManager::GetPackageMembersByName(const Package& package, const std::string& name) const { return cjoManager->GetPackageMembersByName(package.fullPackageName, name); } // For LSP AST::OrderedDeclSet ImportManager::GetPackageMembers( const std::string& srcFullPackageName, const std::string& targetFullPackageName) const { if (srcFullPackageName == targetFullPackageName) { return {}; } auto relation = Modules::GetPackageRelation(srcFullPackageName, targetFullPackageName); auto members = cjoManager->GetPackageMembers(targetFullPackageName); AST::OrderedDeclSet res; for (auto& [_, decls] : members) { for (auto it : decls) { if (Modules::IsVisible(*it, relation)) { res.emplace(it.get()); } } } return res; } std::pair, bool> ImportManager::GetImportedPackageDecl( Ptr node, const std::string& packageName) const { CJC_NULLPTR_CHECK(node->curFile); auto decls = GetImportedDeclsByName(*node->curFile, packageName); Ptr ret = nullptr; for (auto decl : decls) { if (auto pd = DynamicCast(decl)) { if (ret == nullptr) { ret = pd; } else { return {ret, true}; } } } return {ret, false}; } std::vector> ImportManager::GetAllImportedPackages(bool includeMacroPkg) const { return cjoManager->GetAllPackageDecls(includeMacroPkg); } const std::vector>& ImportManager::GetCurImportedPackages(const std::string& fullPackageName) const { return dependencyGraph->GetDirectDependencyPackageDecls(fullPackageName); } const std::vector>& ImportManager::GetAllDependentPackageDecls( const std::string& fullPackageName, bool includeMacroPkg) const { return dependencyGraph->GetAllDependencyPackageDecls(fullPackageName, includeMacroPkg); } const std::set& ImportManager::GetAllDependentPackageNames(const std::string& fullPackageName) const { // NOTE: exclude packages which is only used by macro expansion. return dependencyGraph->GetAllDependencyPackageNames(fullPackageName, false); } Ptr ImportManager::GetImportedDecl(const std::string& fullPackageName, const std::string& name) const { auto& decls = cjoManager->GetPackageMembersByName(fullPackageName, name); if (decls.empty()) { auto decl = cjoManager->GetImplicitPackageMembersByName(fullPackageName, name); return decl; } return *decls.begin(); } void ImportManager::SetPackageCjoCache(const std::string& fullPackageName, const std::vector& cjoData) const { cjoManager->SetPackageCjoCache(fullPackageName, cjoData); } void ImportManager::ClearPackageCjoCache() const { cjoManager->ClearCjoCache(); } bool ImportManager::IsMacroRelatedPackageName(const std::string& fullPackageName) const { return cjoManager->IsMacroRelatedPackageName(fullPackageName); } std::string ImportManager::GetMainPartPkgNameForTestPkg(const std::string& testPkgName) { CJC_ASSERT(IsTestPackage(testPkgName)); return testPkgName.substr(0, testPkgName.length() - SourceManager::testPkgSuffix.length()); } bool ImportManager::IsTestPackage(const std::string& pkgName) { return pkgName.length() >= SourceManager::testPkgSuffix.length() && pkgName.compare(pkgName.length() - SourceManager::testPkgSuffix.length(), SourceManager::testPkgSuffix.length(), SourceManager::testPkgSuffix) == 0; } bool ImportManager::IsTypeAccessible(const File& file, const Type& type) const { auto decl = type.GetTarget(); if (!decl) { // Builtin type and primitive type is always accessible. return true; } return IsDeclAccessible(file, *decl); } bool ImportManager::IsTyAccessible(const File& file, const Ty& ty) const { Ptr decl = nullptr; if (auto sTy = DynamicCast(&ty)) { decl = sTy->declPtr; } else if (auto cTy = DynamicCast(&ty)) { decl = cTy->declPtr; } else if (auto iTy = DynamicCast(&ty)) { decl = iTy->declPtr; } else if (auto eTy = DynamicCast(&ty)) { decl = eTy->declPtr; } if (decl) { return IsDeclAccessible(file, *decl); } return true; } bool ImportManager::IsDeclAccessible(const File& file, Decl& decl) const { auto ret = decl.fullPackageName == file.curPackage->fullPackageName; auto relation = Modules::GetPackageRelation(file.curPackage->fullPackageName, decl.fullPackageName); if (!Utils::In(Ptr(&decl), GetImportedDeclsByName(file, decl.identifier))) { // Process 'import pkg1.pkg2; pkg2.Decl.extendMember', If 'Decl' is imported, it must visible in cur package. auto pkgNames = Utils::SplitQualifiedName(decl.fullPackageName); for (auto it = pkgNames.crbegin(); it != pkgNames.crend(); ++it) { ret = ret || (!GetImportedDeclsByName(file, *it).empty() && Modules::IsVisible(decl, relation)); } // Process import alias decl or alias-import. // If we have 'type TA = A; extend TA {...}' in pkg1, and other pkg import 'pkg1.TA', 'extend A' is accessable. auto found = declToTypeAlias.find(&decl); if (found != declToTypeAlias.end()) { for (auto& tadId : found->second) { ret = ret || !GetImportedDeclsByName(file, tadId).empty(); } } } else { ret = true; } return ret; } namespace { inline void AddNoteForExtendExportDiag(const Ptr builder, const std::string note) { if (builder) { builder->AddNote(note); } } inline void AddNoteForExtendExportDiag(const Ptr builder, const Range range, const std::string note) { if (builder) { builder->AddNote(range, "to use the following extension, import '" + note + "'"); } } } // namespace bool ImportManager::IsExtendAllUpperBoundsImported( const ExtendDecl& ed, const File& file, const Ptr builder) const { if (!ed.generic) { return true; } std::set upperboundsNotImported; bool areAllUpperBoundsImported = true; for (auto& gc : ed.generic->genericConstraints) { for (auto& ub : gc->upperBounds) { CJC_NULLPTR_CHECK(ub); if (!IsTypeAccessible(file, *ub)) { areAllUpperBoundsImported = false; upperboundsNotImported.emplace(ub->ty->String()); break; } } } for (auto& up : upperboundsNotImported) { AddNoteForExtendExportDiag(builder, MakeRange(ed.begin, ed.end), up); } return areAllUpperBoundsImported; } bool ImportManager::IsExtendAccessible( const File& file, const ExtendDecl& ed, const Ptr builder) const { if (!file.curPackage || ed.fullPackageName == file.curPackage->fullPackageName) { return true; } auto curPkgName = file.curPackage->fullPackageName; if (opts.compileTestsOnly && ed.TestAttr(Attribute::FOR_TEST) && IsTestPackage(curPkgName) && GetMainPartPkgNameForTestPkg(curPkgName) == ed.fullPackageName) { return true; } bool areAllUpperBoundsImported = IsExtendAllUpperBoundsImported(ed, file, builder); bool hasAnyInterfacesImported = false; bool isExtendedTypeAccessible = false; auto extendedDecl = Ty::GetDeclPtrOfTy(ed.ty); bool isInSamePkg = extendedDecl ? ed.fullPackageName == extendedDecl->fullPackageName : ed.fullPackageName == "std.core"; if (!isInSamePkg) { for (auto& inhert : ed.inheritedTypes) { if (inhert && IsTypeAccessible(file, *inhert)) { hasAnyInterfacesImported = true; } } isExtendedTypeAccessible = IsTypeAccessible(file, *ed.extendedType); } else { hasAnyInterfacesImported = true; if (extendedDecl) { auto relation = GetPackageRelation(file.curPackage->fullPackageName, extendedDecl->fullPackageName); isExtendedTypeAccessible = IsVisible(*extendedDecl, relation); } else { isExtendedTypeAccessible = true; } } if (!hasAnyInterfacesImported) { if (builder) { builder->AddNote(MakeRange(ed.begin, ed.end), "to use the following extension, you must import at least one of its inherited interfaces"); } } // For direct extension, all upperbound (if any) need to be imported. // For interface extension, all upperbound (if any) and at lest one interface need to be imported. if (!isExtendedTypeAccessible) { AddNoteForExtendExportDiag(builder, MakeRange(ed.begin, ed.end), ed.extendedType->ty->String()); } bool isExtendImported = areAllUpperBoundsImported && hasAnyInterfacesImported && isExtendedTypeAccessible; return ed.IsExportedDecl() && isExtendImported; } const Ptr ImportManager::FindImplmentInterface(const File& file, const Decl& member, const Ptr& it) const { auto targetDecl = DynamicCast(it->GetTarget()); if (targetDecl == nullptr) { return nullptr; } for (auto& super : targetDecl->inheritedTypes) { if (auto implInterface = FindImplmentInterface(file, member, super)) { return implInterface; } } for (auto& m : targetDecl->GetMemberDecls()) { bool isImpl = typeManager.PairIsOverrideOrImpl(member, *m); if (isImpl) { return it; } } return nullptr; } bool ImportManager::IsExtendMemberImported( const Ptr extend, const File& file, const Decl& member, const Ptr builder) const { if (extend->inheritedTypes.empty()) { return true; } auto extendedDecl = Ty::GetDeclPtrOfTy(extend->ty); bool isInSamePkg = extendedDecl ? extend->fullPackageName == extendedDecl->fullPackageName : extend->fullPackageName == "std.core"; if (isInSamePkg) { if (!IsTypeAccessible(file, *extend->extendedType)) { AddNoteForExtendExportDiag(builder, MakeRange(extend->begin, extend->end), extend->ty->String()); return false; } return true; } // Check whether the interface to which the member belongs is imported. for (auto& super : extend->inheritedTypes) { if (auto implInterface = FindImplmentInterface(file, member, super)) { if (!IsTypeAccessible(file, *implInterface)) { AddNoteForExtendExportDiag(builder, MakeRange(extend->begin, extend->end), implInterface->ty->String()); return false; } return true; } } return false; } bool ImportManager::IsExtendMemberAccessible( const File& file, const Decl& member, Ty& baseTy, const Ptr builder) const { Ptr extend = typeManager.GetExtendDeclByMember(member, baseTy); if (extend == nullptr || file.curPackage == nullptr) { return true; } auto curPkgName = file.curPackage->fullPackageName; if (opts.compileTestsOnly && !member.TestAttr(Attribute::PRIVATE) && extend->TestAttr(Attribute::FOR_TEST) && IsTestPackage(curPkgName) && GetMainPartPkgNameForTestPkg(curPkgName) == member.fullPackageName) { return true; } // Access to extended members must meet the following four requirements: // 1. extension is exported. // 2. extension is imported. `IsExtendAccessible` checks 1 and 2. // 3. extension member is exported. `IsExportedDecl` checks 3. // 4. extension member is imported. if (file.curPackage && extend->fullPackageName == file.curPackage->fullPackageName) { return true; } if (!IsExtendAccessible(file, *extend, builder)) { return false; } if (!member.IsExportedDecl()) { AddNoteForExtendExportDiag(builder, "cannot access this member because it is not declared in any interface implemented by the extension"); return false; } return IsExtendMemberImported(extend, file, member, builder); } ImportManager::~ImportManager() { DeleteASTWriters(); DeleteASTLoaders(); curPackage = nullptr; } cangjie_compiler-1.0.7/src/Modules/ModulesDiag.cpp000066400000000000000000000101241510705540100221050ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements diagnostics for modules. */ #include "ModulesDiag.h" #include "cangjie/AST/Match.h" #include "cangjie/Modules/ModulesUtils.h" namespace Cangjie::Modules { namespace { bool IsSamePosition(const Position& pos1, const Position& pos2) { return pos1 == pos2 && pos1.fileID == pos2.fileID; } } // namespace void WarnUselessImport(DiagnosticEngine& diag, const Range& importRange, const Decl& decl) { auto& name = decl.identifier; auto builder = diag.DiagnoseRefactor(DiagKindRefactor::package_shadowed_import, importRange, name); builder.AddNote(MakeRange(decl.identifier.Begin(), name), "'" + name + "' is declared here"); } void WarnConflictImport(DiagnosticEngine& diag, const std::string& name, const Range& current, const Range& previous) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::package_conflict_import, current, name); builder.AddNote(previous, "The previous was imported here"); } void WarnRepeatedFeatureName(DiagnosticEngine& diag, std::string& name, const Range& current, const Range& previous) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::feature_already_seen_name, current); builder.AddNote(previous, "feature '" + name + "' previously used here"); } // void DiagForNullPackageFeature(DiagnosticEngine& diag, const Ptr file, const Ptr refFeature) void DiagForNullPackageFeature(DiagnosticEngine& diag, const Range& current, const Ptr refFeature) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::feature_null_declaration, current); builder.AddNote( MakeRange(refFeature->content[0].begin, refFeature->end), "perhapse you meant these features"); } void DiagForDifferentPackageFeatureConsistency(DiagnosticEngine& diag, const Ptr feature, const Ptr refFeature) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::feature_different_consistency, MakeRange(feature->content[0].begin, feature->end)); builder.AddNote( MakeRange(refFeature->content[0].begin, refFeature->end), "perhapse you meant these features"); } void DiagForDifferentPackageNames(DiagnosticEngine& diag, const std::map, std::pair>& packageNamePosMap) { Position diagPosition; std::pair diagPackageDecl; for (auto [pkgPair, pair] : packageNamePosMap) { if (pair.second) { diagPosition = pair.first; diagPackageDecl = pkgPair; break; } } auto builder = diag.DiagnoseRefactor( DiagKindRefactor::package_multiple_package_declarations, MakeRange(diagPosition, diagPackageDecl.first)); uint8_t counter = 0; for (auto [pkgPair, pair] : packageNamePosMap) { // 2 is maximum number of diagnostic prints. if (counter >= 2) { break; } if (IsSamePosition(diagPosition, pair.first)) { continue; } if (pair.second) { builder.AddNote(MakeRange(pair.first, pkgPair.first), "another different package declaration '" + pkgPair.second + " package " + pkgPair.first + "'"); } else { builder.AddNote(MakeRange(pair.first, ""), "another different package declaration 'public package " + DEFAULT_PACKAGE_NAME + "'"); } counter++; } } void DiagRootPackageModifier(DiagnosticEngine& diag, const PackageSpec& packageSpec) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::package_root_package_should_be_public, packageSpec, MakeRange(packageSpec.packageName)); auto packageMsg = "package " + packageSpec.packageName; builder.AddNote("default modifier of 'package' is 'public', you can use '" + packageMsg + "' instead"); } } // namespace Cangjie::Modules cangjie_compiler-1.0.7/src/Modules/ModulesDiag.h000066400000000000000000000026631510705540100215630ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares diagnostic related functions for Modules. */ #ifndef CANGJIE_MODULES_DIAGS_H #define CANGJIE_MODULES_DIAGS_H #include #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Modules/ImportManager.h" namespace Cangjie::Modules { using namespace AST; void WarnUselessImport(DiagnosticEngine& diag, const Range& importRange, const Decl& decl); void WarnConflictImport(DiagnosticEngine& diag, const std::string& name, const Range& current, const Range& previous); void WarnRepeatedFeatureName(DiagnosticEngine& diag, std::string& name, const Range& current, const Range& previous); void DiagForNullPackageFeature(DiagnosticEngine& diag, const Range& current, const Ptr refFeature); void DiagForDifferentPackageFeatureConsistency(DiagnosticEngine& diag, const Ptr feature, const Ptr refFeature); void DiagForDifferentPackageNames(DiagnosticEngine& diag, const std::map, std::pair>& packageNamePosMap); void DiagRootPackageModifier(DiagnosticEngine& diag, const PackageSpec& packageSpec); } // namespace Cangjie::Modules #endif cangjie_compiler-1.0.7/src/Modules/ModulesUtils.cpp000066400000000000000000000031431510705540100223440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements utilities for modules. */ #include "cangjie/Modules/ModulesUtils.h" namespace Cangjie::Modules { PackageRelation GetPackageRelation(const std::string& srcFullPkgName, const std::string& targetFullPkgName) { auto pureSrcFullPackageName = ImportManager::IsTestPackage(srcFullPkgName) ? ImportManager::GetMainPartPkgNameForTestPkg(srcFullPkgName) : srcFullPkgName; auto pureTargetFullPackageName = ImportManager::IsTestPackage(targetFullPkgName) ? ImportManager::GetMainPartPkgNameForTestPkg(targetFullPkgName) : targetFullPkgName; if (pureSrcFullPackageName == pureTargetFullPackageName) { return PackageRelation::SAME_PACKAGE; } if (pureSrcFullPackageName == "" || pureTargetFullPackageName == "") { return PackageRelation::NONE; } auto srcPath = Utils::SplitQualifiedName(pureSrcFullPackageName); auto targetPath = Utils::SplitQualifiedName(pureTargetFullPackageName); if (targetPath.size() < srcPath.size() && std::equal(targetPath.begin(), targetPath.end(), srcPath.begin(), srcPath.begin() + static_cast(targetPath.size()))) { return PackageRelation::CHILD; } return srcPath.front() == targetPath.front() ? PackageRelation::SAME_MODULE : PackageRelation::NONE; } } // namespace Cangjie::Modules cangjie_compiler-1.0.7/src/Modules/PackageManager.cpp000066400000000000000000000103101510705540100225330ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements PackageManager related classes. */ #include "cangjie/Modules/PackageManager.h" using namespace Cangjie; using namespace AST; void PackageManager::CollectDepsInFile(File& file, const std::unique_ptr& pkgInfo, const Package& pkg) { for (auto& it : file.imports) { std::string fullPkgName = importManager.cjoManager->GetPackageNameByImport(*it); auto pd = importManager.cjoManager->GetPackageDecl(fullPkgName); if (pd == nullptr) { continue; } CJC_ASSERT(pd && pd->srcPackage); auto importPkg = pd->srcPackage; // Global init func should not be generated for Java package. if (importPkg->TestAttr(AST::Attribute::TOOL_ADD)) { continue; } fullPkgName = importPkg->fullPackageName; auto found = packageInfoMap.find(fullPkgName); if (found != packageInfoMap.end()) { pkgInfo->deps.insert(found->second.get()); } else { (void)packageToOtherSccMap[pkg.fullPackageName].emplace(importPkg->fullPackageName); } } } void PackageManager::CollectDeps(std::vector>& pkgs) { for (auto& pkg : pkgs) { auto [_, success] = packageInfoMap.emplace(pkg->fullPackageName, std::make_unique(pkg->fullPackageName)); CJC_ASSERT(success); } for (auto& pkg : pkgs) { auto& pkgInfo = packageInfoMap[pkg->fullPackageName]; for (auto& file : pkg->files) { CollectDepsInFile(*file, pkgInfo, *pkg); } } } // Using Tarjan Algorithm to resolve circular dependence between two or more packages // and get their buildOrders. // The function invoke TarjanForSCC and use "for loop" resolve the condition having isolated island. bool PackageManager::ResolveDependence(std::vector>& pkgs) { packageInfoMap.clear(); orderedPackageInfos.clear(); packageToOtherSccMap.clear(); CollectDeps(pkgs); TarjanContext ctx = {}; std::stack st; size_t index = 0; while (ctx.indices.size() < packageInfoMap.size()) { if (ctx.indices.empty()) { TarjanForSCC(ctx, st, index, packageInfoMap.begin()->second.get()); continue; } for (auto& i : packageInfoMap) { if (ctx.indices[i.second.get()] == 0) { TarjanForSCC(ctx, st, index, i.second.get()); } } } std::unordered_map> sortPackageMap; for (auto& pkg : pkgs) { sortPackageMap[pkg->fullPackageName] = pkg; } bool ret = true; size_t pkgIndex = 0; for (auto& it : orderedPackageInfos) { // Currently compiler does not support to compile multiple circular dependent source packages. // CjoGen will resolve circular dependency before this process. ret = ret && it.size() == 1; for (auto& pkg : it) { pkgs[pkgIndex++] = sortPackageMap[pkg->fullPackageName]; } } return ret; } void PackageManager::TarjanForSCC(TarjanContext& ctx, std::stack& st, size_t& index, PackageInfo* u) { // Set the bookkeeping info for `u` to the smallest unused `index`. ++index; ctx.indices[u] = index; ctx.lowlinks[u] = index; st.emplace(u); ctx.onStack[u] = true; PackageInfo* v = nullptr; for (auto w : u->deps) { v = w; if (ctx.indices[v] == 0) { TarjanForSCC(ctx, st, index, v); ctx.lowlinks[u] = std::min(ctx.lowlinks[u], ctx.lowlinks[v]); } else if (ctx.onStack[v]) { ctx.lowlinks[u] = std::min(ctx.lowlinks[u], ctx.indices[v]); } } if (ctx.lowlinks[u] == ctx.indices[u]) { std::vector infos; do { v = st.top(); infos.emplace_back(v); st.pop(); ctx.onStack[v] = false; } while (v != u); orderedPackageInfos.emplace_back(infos); } } cangjie_compiler-1.0.7/src/Option/000077500000000000000000000000001510705540100170465ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Option/CMakeLists.txt000066400000000000000000000013041510705540100216040ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set(OPTION_SRC OptionAction.cpp Option.cpp OptionTable.cpp) add_library(CangjieOption OBJECT ${OPTION_SRC}) target_compile_options(CangjieOption PRIVATE ${CJC_EXTRA_WARNINGS}) if(CANGJIE_VISIBLE_OPTIONS_ONLY) target_compile_definitions(CangjieOption PUBLIC CANGJIE_VISIBLE_OPTIONS_ONLY) add_library(CangjieOption-Fully-Enabled OBJECT ${OPTION_SRC}) target_compile_options(CangjieOption-Fully-Enabled PRIVATE ${CJC_EXTRA_WARNINGS}) endif() cangjie_compiler-1.0.7/src/Option/Option.cpp000066400000000000000000001510311510705540100210230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Option related classes. */ #include "cangjie/Option/Option.h" #include #include #include #include #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Basic/Print.h" #include "cangjie/Basic/Utils.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/Semaphore.h" #include "cangjie/Utils/SipHash.h" #include "cangjie/Utils/Utils.h" #include "cangjie/Utils/Unicode.h" #ifdef _WIN32 #include #endif using namespace Cangjie; using namespace Cangjie::Triple; using namespace Cangjie::Utils; using namespace Cangjie::FileUtil; namespace { const std::string CJO_EXTENSION = "cjo"; const std::string BC_EXTENSION = "bc"; const std::string CJ_EXTENSION = "cj"; const std::string CHIR_EXTENSION = "chir"; const std::string ARCHIVE_EXTENSION = "a"; const std::string OBJECT_EXTENSION = "o"; #ifdef _WIN32 const std::string DL_EXTENSION = "dll"; #elif defined(__APPLE__) const std::string DL_EXTENSION = "dylib"; #else const std::string DL_EXTENSION = "so"; #endif std::string VectorStrToSerializedString(const std::vector& vec, const std::string& sep) { std::string result; for (auto& v : vec) { result += v + sep; } return result; } std::string BoolToSerializedString(bool val) { return val ? "t" : "f"; } const std::unordered_map ARCH_STRING_MAP = { {ArchType::X86_64, "x86_64"}, {ArchType::AARCH64, "aarch64"}, {ArchType::ARM32, "arm"}, {ArchType::ARM64, "arm64"}, {ArchType::UNKNOWN, "unknown"}, }; const std::unordered_map OS_STRING_MAP = { {OSType::WINDOWS, "windows"}, {OSType::WINDOWS, "w64"}, {OSType::LINUX, "linux"}, {OSType::DARWIN, "darwin"}, {OSType::IOS, "ios"}, {OSType::UNKNOWN, "unknown"}, }; const std::unordered_map VENDOR_STRING_MAP = { {Vendor::PC, "pc"}, {Vendor::APPLE, "apple"}, {Vendor::UNKNOWN, "unknown"}, }; template inline void RaiseArgumentUnusedMessage(DiagnosticEngine& diag, DiagKindRefactor diagKind, const Args... args) { (void)diag.DiagnoseRefactor(diagKind, DEFAULT_POSITION, args..., " The argument is ignored."); } } // namespace namespace Cangjie { const std::unordered_map OPTIMIZATION_LEVEL_TO_BACKEND_OPTION = { {GlobalOptions::OptimizationLevel::O0, "-O0"}, {GlobalOptions::OptimizationLevel::O1, "-O1"}, {GlobalOptions::OptimizationLevel::O2, "-O2"}, {GlobalOptions::OptimizationLevel::O3, "-O3"}, {GlobalOptions::OptimizationLevel::Os, "-Os"}, {GlobalOptions::OptimizationLevel::Oz, "-Oz"}}; bool IsUnsafeBackend(Triple::BackendType backendType) { return backendType == Triple::BackendType::CJNATIVE; } } // namespace Cangjie std::string Triple::BackendToString(const BackendType& backend) { switch (backend) { case BackendType::CJNATIVE: return "cjnative"; case BackendType::UNKNOWN: default: break; } return "unknown"; } std::string Triple::Info::ArchToString() const { if (auto search = ARCH_STRING_MAP.find(arch); search != ARCH_STRING_MAP.end()) { return search->second; } else { return "unknown"; } } std::string Triple::Info::OSToString() const { if (auto search = OS_STRING_MAP.find(os); search != OS_STRING_MAP.end()) { return search->second; } else { return "unknown"; } } std::string Triple::Info::VendorToString() const { if (auto search = VENDOR_STRING_MAP.find(vendor); search != VENDOR_STRING_MAP.end()) { return search->second; } else { return "unknown"; } } std::string Triple::Info::ToTripleString() const { if (vendor == Vendor::APPLE) { std::string triple = ArchToString() + "-" + VendorToString() + "-" + OSToString(); if (os == OSType::IOS) { triple += apiLevel; } if (env != Environment::NOT_AVAILABLE) { triple += "-" + EnvironmentToString(); } return triple; } return ArchToString() + "-" + OSToString() + "-" + EnvironmentToString(); } std::string Triple::Info::ToFullTripleString() const { std::string systemStr = OSToString(); std::string envStr = EnvironmentToString(); if (systemStr != "unknown" && envStr != "unknown") { systemStr = systemStr + "-" + envStr; } return ArchToString() + "-" + VendorToString() + "-" + systemStr; } std::string Triple::Info::GetEffectiveTripleString() const { auto tripleString = ToTripleString(); if (tripleString == "aarch64-linux-ohos") { return "aarch64-linux-gnu"; } if (tripleString == "arm-linux-ohos") { return "armv7a-linux-gnu"; } if (tripleString == "x86_64-windows-gnu") { return "x86_64-w64-mingw32"; } if (tripleString == "aarch64-ios-simulator") { return "arm64-apple-ios17.5-simulator"; } return tripleString; } bool Triple::IsPossibleMatchingTripleName(const Triple::Info& info, const std::string& name) { // General triple has format {ARCH}-{VENDOR}-{SYSTEM}-{ENV}, {VENDOR} sometimes is unknown and is omitted, // thus both {ARCH}-{VENDOR}-{SYSTEM}-{ENV} and {ARCH}-{SYSTEM}-{ENV} are possible valid triple names. auto parts = Utils::SplitString(name, "-"); // Size of 3 for format {ARCH}-{SYSTEM}-{ENV}, and size of 4 for format {ARCH}-{VENDOR}-{SYSTEM}-{ENV}. if (parts.size() != 3 && parts.size() != 4) { // In other cases, the name must not be a valid triple name. return false; } if (parts[0] != info.ArchToString()) { return false; } // Size - 2 is {SYSTEM} if (parts[parts.size() - 2] != info.OSToString()) { return false; } // Size - 1 is {ENV} if (parts[parts.size() - 1] != info.EnvironmentToString()) { return false; } // We ignore vendor information for now return true; } bool GlobalOptions::SetOptimizationLevel(OptimizationLevel level) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND this->optimizationLevel = level; if (level >= OptimizationLevel::O2) { selectedCHIROpts.insert(OptimizationFlag::CONST_PROPAGATION); selectedCHIROpts.insert(OptimizationFlag::LETC_FOLDING); selectedCHIROpts.insert(OptimizationFlag::REF_FOLDING); selectedCHIROpts.insert(OptimizationFlag::REF_TO_LETC); selectedCHIROpts.insert(OptimizationFlag::REDUNDANT_RETURNS); selectedCHIROpts.insert(OptimizationFlag::REDUNDANT_FUTURE); selectedCHIROpts.insert(OptimizationFlag::FUNC_INLINING); selectedCHIROpts.insert(OptimizationFlag::DEVIRTUALIZATION); selectedCHIROpts.insert(OptimizationFlag::ARRAY_LAMBDA_OPT); selectedCHIROpts.insert(OptimizationFlag::SROA_OPT); selectedCHIROpts.insert(OptimizationFlag::SWITCH_OPT); selectedCHIROpts.insert(OptimizationFlag::VALUE_RANGE_ANALYSIS); selectedCHIROpts.insert(OptimizationFlag::REDUNDANT_LOAD); // Enabling CHIR Escape Analysis by default in O2 chirEA = true; chirLICM = true; } else { selectedCHIROpts.clear(); } return true; #endif } bool GlobalOptions::PerformPostActions() { SetupChirOptions(); bool success = SetupConditionalCompilationCfg(); // ReprocessInputs depends on the normalized output path which is processed in ReprocessOutputs, // so ReprocessOutputs must be run before ReprocessInputs. success = success && ReprocessOutputs(); success = success && ReprocessInputs(); success = success && ReprocessReflectionOption(); success = success && CheckCompileMacro(); success = success && ReprocessCoverageOptions(); success = success && CheckScanDependencyOptions(); success = success && CheckSanitizerOptions(); success = success && CheckLtoOptions(); success = success && CheckCompileAsExeOptions(); success = success && CheckPgoOptions(); success = success && ReprocessObfuseOption(); RefactJobs(); RefactAggressiveParallelCompileOption(); DisableStaticStdForOhos(); return success; } void GlobalOptions::SetupChirOptions() { // Why there is a marco here? In case of LSP, CANGJIE_CODEGEN_CJNATIVE_BACKEND // is not set. `chirLLVM` is expected to be false. // Since `backend` is always set to `BackendType::CJNATIVE` by default. // We need a macro to prevent `chirLLVM` from setting to true. #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND chirLLVM = backend == BackendType::CJNATIVE; #endif chirCC = chirCC || chirLLVM; // cjnative backend should enable chir-cc. // If using interpreter we need CHIR graph to be closure converted if (interpreter) { chirCC = true; } } bool GlobalOptions::ReprocessReflectionOption() { if (target.IsMacOS()) { disableReflection = true; } return true; } bool GlobalOptions::ReprocessOutputs() { DiagnosticEngine diag; if (outputDir.has_value()) { if (IsAbsolutePath(output)) { Errorf("'-o' does not accept absolute path when '--output-dir' is specified\n"); return false; } // The outputDir also affects the destination of the final product, thus we set output as well. output = JoinPath(outputDir.value(), output); if (auto maybePath = FileUtil::GetAbsPath(outputDir.value()); maybePath.has_value()) { outputDir = maybePath; } else { (void)diag.DiagnoseRefactor(DiagKindRefactor::invalid_path, DEFAULT_POSITION, outputDir.value()); return false; } } bool outputIsDir = IsDir(output); auto dirPath = GetDirPath(output); // If `output` is not a directory and `dirPath` is empty, we use root directory `/` as default. std::string saveDir = outputIsDir ? output : (dirPath.empty() ? "/" : dirPath); if (!ValidateDirectoryPath(saveDir, FileUtil::FileMode::FM_WRITE).has_value()) { return false; } if (!outputIsDir && !IsDir(saveDir)) { (void)diag.DiagnoseRefactor(DiagKindRefactor::not_a_directory, DEFAULT_POSITION, saveDir); return false; } if (auto maybePath = FileUtil::GetAbsPath(saveDir); maybePath.has_value()) { output = outputIsDir ? maybePath.value() : JoinPath(maybePath.value(), FileUtil::GetFileName(output)); } else { (void)diag.DiagnoseRefactor(DiagKindRefactor::invalid_path, DEFAULT_POSITION, output); return false; } if (!CheckOutputPathLength()) { return false; } auto fileName = FileUtil::GetFileName(output); if (!compileMacroPackage && fileName.find("lib-macro_") == 0) { Errorf("Cannot support specify file prefix 'lib-macro_' without '--compile-macro' option.\n"); return false; } return true; } bool GlobalOptions::CheckOutputPathLength() const { DiagnosticEngine diag; #ifdef _WIN32 // Length of "main.exe" is 8 size_t defaultOutputNameLen = 8; #else // Length of "main" is 4 size_t defaultOutputNameLen = 4; #endif auto fullLength = IsDir(output) ? output.size() + defaultOutputNameLen : output.size(); if (fullLength > FILE_PATH_MAX_LENGTH) { (void)diag.DiagnoseRefactor(DiagKindRefactor::driver_path_exceeds_length_limit, DEFAULT_POSITION, "the output file", std::to_string(FILE_PATH_MAX_LENGTH)); return false; // If the path length of the output is close to the upper limit, the compilation still has a possibility to // fail, because the ld linker on Windows will escape backslashs by doubling it ("\" -> "\\"), which may make // the resultant path exceed the limit. Therefore, a warning is generated when the output path is longer than // FILE_PATH_MAX_LENGTH - 15. } else if (fullLength > FILE_PATH_MAX_LENGTH - 15) { (void)diag.DiagnoseRefactor(DiagKindRefactor::driver_warning_path_close_to_length_limit, DEFAULT_POSITION, "the output file", std::to_string(FILE_PATH_MAX_LENGTH)); } return true; } bool GlobalOptions::ReprocessInputs() { if (FileUtil::IsDir(output)) { return true; } // Any inputs member must obey the following rules: // 1. input can not be empty; // 2. input can not be the same with output, otherwise input will be overwritten by output(executable). DiagnosticEngine diag; std::vector inputs; inputs = argList->GetInputs(); if (compilePackage) { for (auto& path : packagePaths) { std::vector files = GetAllFilesUnderCurrentPath(path, CJ_EXTENSION); std::for_each(files.begin(), files.end(), [&path, &inputs](std::string& file) { file = FileUtil::JoinPath(path, file); if (GetFileExtension(file) == CJ_EXTENSION) { inputs.emplace_back(file); } }); } } for (auto& inputFile : inputs) { auto absInputFile = FileUtil::GetAbsPath(inputFile); if (!absInputFile.has_value()) { (void)diag.DiagnoseRefactor(DiagKindRefactor::no_such_file_or_directory, DEFAULT_POSITION, inputFile); return false; } if (absInputFile.value() == output) { (void)diag.DiagnoseRefactor( DiagKindRefactor::input_file_overwritten_by_generated_output, DEFAULT_POSITION, inputFile); return false; } } return true; } bool GlobalOptions::CheckCompileMacro() const { if (compileMacroPackage && enableOutputType) { Errorf("using both '--compile-macro' and '--output-type' is not allowed.\n"); return false; } if (compileMacroPackage) { if (!output.empty() && !IsDir(output)) { Errorf("Cannot support specify output file name with '--compile-macro' option.\n"); return false; } } return true; } bool GlobalOptions::ReprocessCoverageOptions() { if (enableCoverage) { if (optimizationLevel != OptimizationLevel::O0) { Warningf("'--coverage' should be used without optimizations.\n"); (void)SetOptimizationLevel(OptimizationLevel::O0); } if (!removedPathPrefix.empty()) { Warningf("'--coverage' should be used without option '--trimpath'.\n "); removedPathPrefix.clear(); } } return true; } bool GlobalOptions::CheckScanDependencyOptions() const { if (!scanDepPkg) { return true; } DiagnosticEngine diag; if (compilePackage) { // Report error for `cjc--scan-dependency -p pkg.cjo ...`. When `-p` is specified .cjo file // inputs are not accepted. if (!inputCjoFile.empty()) { (void)diag.DiagnoseRefactor( DiagKindRefactor::driver_not_accept_cjo_inputs_when, DEFAULT_POSITION, "'-p' or '--package'"); return false; } // Report error for `cjc --scan-dependency -p`. A path input is required. if (packagePaths.empty()) { (void)diag.DiagnoseRefactor( DiagKindRefactor::driver_require_package_directory_scan_dependency, DEFAULT_POSITION); return false; } } else { // Report error for `cjc --scan-dependency`. A .cjo file input is required. if (inputCjoFile.empty()) { (void)diag.DiagnoseRefactor(DiagKindRefactor::driver_source_cjo_empty, DEFAULT_POSITION); return false; } } return true; } bool GlobalOptions::CheckSanitizerOptions() const { if (!EnableSanitizer()) { return true; } auto runtimeDirPath = FileUtil::JoinPath(cangjieHome, "runtime"); auto libraryDirPath = FileUtil::JoinPath(FileUtil::JoinPath(runtimeDirPath, "lib"), GetCangjieLibTargetPathName()); auto ext = GetSharedLibraryExtension(target.os); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto sanitizerPath = FileUtil::JoinPath(libraryDirPath, SanitizerTypeToShortString()); if (!FileUtil::FileExist(FileUtil::JoinPath(sanitizerPath, "libcangjie-runtime" + ext))) { Errorf("%s sanitizer feature is not supported.\n", SanitizerTypeToSerializedString().c_str()); return false; } #endif if (IsLTOEnabled()) { Errorln("Option '--sanitize' and '--lto' cannot be enabled together."); return false; } if (discardEhFrame) { Errorln("Option '--sanitize' and '--discard-eh-frame' cannot be enabled together."); return false; } if (compileMacroPackage) { Errorln("Option '--sanitize' and '--compile-macro' cannot be enabled together."); return false; } auto osType = target.GetOSFamily(); if (EnableAsan() && osType != OSType::LINUX) { Errorln("Address Sanitizer is only supported on Linux"); return false; } if (EnableHwAsan() && osType != OSType::LINUX) { Errorln("Hardware-Assisted Address Sanitizer is only supported on Linux"); return false; } auto arch = target.arch; if (EnableHwAsan() && (arch != ArchType::X86_64 && arch != ArchType::AARCH64)) { Errorln("Hardware-Assisted Address Sanitizer is only supported on X86_64 or aarch64"); return false; } if (EnableTsan() && (osType != OSType::LINUX && osType != OSType::WINDOWS)) { Errorln("Thread Sanitizer is only supported on Linux/Windows"); return false; } return true; } bool GlobalOptions::CheckLtoOptions() const { if (!IsLTOEnabled()) { return true; } auto osType = target.GetOSFamily(); if (osType == OSType::WINDOWS) { Errorln("Windows does not support LTO optimization."); return false; } if (outputMode == OutputMode::STATIC_LIB && !bcInputFiles.empty()) { Errorln("The input file cannot be bc files When generating a static library in LTO mode."); return false; } if (optimizationLevel == OptimizationLevel::Os || optimizationLevel == OptimizationLevel::Oz) { Errorln("-Os and -Oz optimize options are not supported in LTO mode."); return false; } return true; } bool GlobalOptions::CheckCompileAsExeOptions() const { if (!IsCompileAsExeEnabled()) { return true; } if (IsCompileAsExeEnabled() && !IsLTOEnabled()){ DiagnosticEngine diag; diag.DiagnoseRefactor(DiagKindRefactor::driver_invalid_compile_as_exe, DEFAULT_POSITION); return false; } auto osType = target.GetOSFamily(); if(osType == OSType::WINDOWS || osType == OSType::DARWIN || osType == OSType::IOS) { DiagnosticEngine diag; diag.DiagnoseRefactor(DiagKindRefactor::driver_invalid_compile_as_exe_platform, DEFAULT_POSITION); return false; } return true; } bool GlobalOptions::CheckPgoOptions() const { DiagnosticEngine diag; // Using both '--pgo-instr-gen' and '--pgo-instr-use' is not allowed. if (enablePgoInstrGen && enablePgoInstrUse) { (void)diag.DiagnoseRefactor(DiagKindRefactor::driver_pgo_both_gen_and_use, DEFAULT_POSITION); return false; } // Check if the pgo-profile-file is valid. if (enablePgoInstrUse) { if (!FileExist(pgoProfileFile)) { (void)diag.DiagnoseRefactor(DiagKindRefactor::no_such_file_or_directory, DEFAULT_POSITION, pgoProfileFile); return false; } else { const std::string PROFILE_EXT = "profdata"; if (FileUtil::GetFileExtension(pgoProfileFile) != PROFILE_EXT) { (void)diag.DiagnoseRefactor( DiagKindRefactor::driver_pgo_invalid_profile_extension, DEFAULT_POSITION, pgoProfileFile); return false; } } } return true; } void GlobalOptions::RefactJobs() { if (jobs.has_value()) { return; } jobs = std::optional{std::thread::hardware_concurrency()}; } void GlobalOptions::RefactAggressiveParallelCompileOption() { if (target.os == Triple::OSType::WINDOWS || target.os == Triple::OSType::DARWIN || target.os == Triple::OSType::IOS) { // When the user's input compile options contain `--apc` and the os is windows or mac, // aggressiveParallelCompile will be disabled because it is not supported yet. if (aggressiveParallelCompile.has_value() || aggressiveParallelCompileWithoutArg) { Warningf("'--apc' is not supported when targeting %s.\n", target.OSToString().c_str()); } aggressiveParallelCompile = 1; return; } // When the compile case enables Obfuscation/LTO/Coverage, we disable the parallel compile if (IsObfuscationEnabled() || IsLTOEnabled() || enableCoverage) { aggressiveParallelCompile = 1; return; } if (aggressiveParallelCompile.has_value()) { return; } else if (optimizationLevel == OptimizationLevel::O0 || enableCompileDebug || aggressiveParallelCompileWithoutArg) { // When the compile options contain `-O0`\'-g'\`--apc`, aggressiveParallelCompile will be enabled, // and the degree of parallelism is the same as that of `-j`. CJC_ASSERT(jobs.has_value()); constexpr std::size_t allowance = 2; Utils::Semaphore::Get().SetCount(jobs.value() + allowance); aggressiveParallelCompile = jobs; } else { aggressiveParallelCompile = 1; } } void GlobalOptions::DisableStaticStdForOhos() { if (target.env == Triple::Environment::OHOS) { if (linkStaticStd.has_value() && linkStaticStd.value()) { DiagnosticEngine diag; (void) diag.DiagnoseRefactor(DiagKindRefactor::driver_static_std_for_ohos, DEFAULT_POSITION); } linkStaticStd = false; } } bool GlobalOptions::HandleArchiveExtension(DiagnosticEngine& diag, const std::string& value) { auto maybePath = ValidateInputFilePath(value, DiagKindRefactor::driver_invalid_binary_file); if (!maybePath.has_value()) { return false; } auto ext = GetFileExtension(value); if (ext == ARCHIVE_EXTENSION && GetFileExtension(maybePath.value()) != ARCHIVE_EXTENSION) { RaiseArgumentUnusedMessage(diag, DiagKindRefactor::driver_warning_not_archive_file, value, maybePath.value()); return true; } if (ext == OBJECT_EXTENSION && GetFileExtension(maybePath.value()) != OBJECT_EXTENSION) { RaiseArgumentUnusedMessage(diag, DiagKindRefactor::driver_warning_not_object_file, value, maybePath.value()); return true; } inputObjs.emplace_back(maybePath.value()); // The .o and .a file is replaced with the absolute path ReplaceInputFileName(value, maybePath.value()); return true; } bool GlobalOptions::HandleCJOExtension(DiagnosticEngine& diag, const std::string& value) { auto maybePath = ValidateInputFilePath(value, DiagKindRefactor::driver_invalid_binary_file); if (!maybePath.has_value()) { return false; } if (GetFileExtension(maybePath.value()) != CJO_EXTENSION) { RaiseArgumentUnusedMessage(diag, DiagKindRefactor::driver_warning_not_cjo_file, value, maybePath.value()); return true; } if (!inputCjoFile.empty()) { (void)diag.DiagnoseRefactor(DiagKindRefactor::driver_require_one_package_directory_scan_dependency, DEFAULT_POSITION, "--scan-dependency"); return false; } inputCjoFile = value; return true; } bool GlobalOptions::HandleCHIRExtension(DiagnosticEngine& diag, const std::string& value) { auto maybePath = ValidateInputFilePath(value, DiagKindRefactor::no_such_file_or_directory); if (!maybePath.has_value()) { return false; } if (GetFileExtension(maybePath.value()) != CHIR_EXTENSION) { RaiseArgumentUnusedMessage(diag, DiagKindRefactor::driver_warning_not_chir_file, value, maybePath.value()); return true; } inputChirFiles.push_back(maybePath.value()); return true; } bool GlobalOptions::HandleCJExtension(DiagnosticEngine& diag, const std::string& value) { if (compilePackage) { (void)diag.DiagnoseRefactor( DiagKindRefactor::driver_unsupport_compile_package_with_source_file, DEFAULT_POSITION, value); return false; } auto maybePath = ValidateInputFilePath(value, DiagKindRefactor::driver_invalid_source_file); if (!maybePath.has_value()) { return false; } if (GetFileExtension(maybePath.value()) != CJ_EXTENSION) { RaiseArgumentUnusedMessage(diag, DiagKindRefactor::driver_warning_not_cj_file, value, maybePath.value()); return true; } srcFiles.push_back(value); return true; } bool GlobalOptions::HandleCJDExtension(DiagnosticEngine& diag, const std::string& value) { if (compilePackage) { (void)diag.DiagnoseRefactor( DiagKindRefactor::driver_unsupport_compile_package_with_source_file, DEFAULT_POSITION, value); return false; } auto maybePath = ValidateInputFilePath(value, DiagKindRefactor::driver_invalid_source_file); if (!maybePath.has_value()) { return false; } srcFiles.push_back(value); return true; } bool GlobalOptions::HandleBCExtension(DiagnosticEngine& diag, const std::string& value) { auto maybePath = ValidateInputFilePath(value, DiagKindRefactor::driver_invalid_source_file); if (!maybePath.has_value()) { return false; } if (GetFileExtension(maybePath.value()) != BC_EXTENSION) { RaiseArgumentUnusedMessage(diag, DiagKindRefactor::driver_warning_not_bc_file, value, maybePath.value()); return true; } bcInputFiles.push_back(maybePath.value()); // The .bc file is replaced with the absolute path ReplaceInputFileName(value, maybePath.value()); return true; } bool GlobalOptions::HandleNoExtension(DiagnosticEngine& diag, const std::string& value) { if (IsAbsolutePathAboveLengthLimit(value)) { (void)diag.DiagnoseRefactor(DiagKindRefactor::driver_path_exceeds_length_limit, DEFAULT_POSITION, value, std::to_string(FILE_PATH_MAX_LENGTH)); return false; } if (!FileExist(value)) { (void)diag.DiagnoseRefactor(DiagKindRefactor::driver_invalid_file_or_directory, DEFAULT_POSITION, value); return false; } if (IsDir(value)) { if (!compilePackage) { (void)diag.DiagnoseRefactor( DiagKindRefactor::driver_unsupport_compile_source_file_with_path, DEFAULT_POSITION, value); } if (!ValidateDirectoryPath(value, FileUtil::FileMode::FM_EXIST).has_value()) { return false; } packagePaths.emplace_back(value); } else { (void)diag.DiagnoseRefactor(DiagKindRefactor::driver_warning_argument_unused, DEFAULT_POSITION, value); } return true; } bool GlobalOptions::ProcessInputs(const std::vector& inputs) { DiagnosticEngine diag; bool ret = true; bool needChir = (commonPartCjo != std::nullopt); std::for_each(inputs.begin(), inputs.end(), [this, &ret, &diag, &needChir](const std::string& value) { if (!ret) { return; } std::string ext = GetFileExtension(value); if (ext == OBJECT_EXTENSION || ext == ARCHIVE_EXTENSION) { ret = HandleArchiveExtension(diag, value); } else if (ext == CJ_EXTENSION && !compileCjd) { ret = HandleCJExtension(diag, value); } else if (ext == BC_EXTENSION) { ret = HandleBCExtension(diag, value); } else if (ext == CHIR_EXTENSION) { needChir = false; ret = HandleCHIRExtension(diag, value); } else if (ext == CJO_EXTENSION) { ret = HandleCJOExtension(diag, value); } else if (HasCJDExtension(value) && compileCjd) { ret = HandleCJDExtension(diag, value); } else { ret = HandleNoExtension(diag, value); } }); // Check inputs. if (compilePackage && packagePaths.empty()) { (void)diag.DiagnoseRefactor(DiagKindRefactor::driver_require_package_directory, DEFAULT_POSITION); return false; } if (needChir) { (void)diag.DiagnoseRefactor(DiagKindRefactor::driver_require_chir_directory, DEFAULT_POSITION); return false; } return ret; } void GlobalOptions::CollectOrderedInputFiles(ArgInstance& arg, uint64_t idx) { switch (arg.argInstanceType) { case ArgInstanceType::Input: // The input files should be passed to the linker in the order they were inputted. inputFileOrder.push_back(std::make_tuple(static_cast(&arg)->value, idx)); break; case ArgInstanceType::Option: { // The library files entered with `--library` or `-l` should also be passed to the linker in the order // they were entered. OptionArgInstance& optArg = *static_cast(&arg); if (optArg.info.GetID() == Options::ID::LIBRARY) { inputLibraryOrder.push_back(std::make_tuple(optArg.value, idx)); } else if (optArg.info.GetID() == Options::ID::LINK_OPTION) { inputLinkOptionOrder.push_back(std::make_tuple(optArg.value, idx)); } else if (optArg.info.GetID() == Options::ID::LINK_OPTIONS) { inputLinkOptionsOrder.push_back(std::make_tuple(optArg.value, idx)); } break; } } } bool GlobalOptions::ParseFromArgs(ArgList& argList) { this->argList = &argList; std::vector optArgs; uint64_t inputIdx = 0; // First, filter out inputs and handle pre-process arguments. Pre-process arguments are a set of // arguments that have higher priority than other arguments. For example, the existence of '-h' would // suppress all other argument errors in a command. bool skipParsing = false; for (auto& arg : argList.args) { switch (arg->argInstanceType) { case ArgInstanceType::Input: argList.AddInput(static_cast(arg.get())->value); break; case ArgInstanceType::Option: { // If parse success, then the argument is consumed. Otherwise, the argument // is not a pre-process argument, keep for the second round of processing. OptionArgInstance& optArg = *static_cast(arg.get()); bool skip = false; if (!TryParsePreOption(optArg, argList, skip)) { optArgs.emplace_back(optArg); } skipParsing = skipParsing || skip; break; } } CollectOrderedInputFiles(*arg.get(), inputIdx); inputIdx++; } if (skipParsing) { return true; } // Process all remaining option arguments for (auto& optArg : optArgs) { // Failure on parsing implies that the argument is invalid. Stop parsing and exit with false. if (!TryParseOption(optArg, argList)) { return false; } } if (!ProcessInputs(argList.GetInputs())) { return false; } if (!PerformPostActions()) { return false; } return true; } std::vector GlobalOptions::GenerateFrontendOptions() const { std::vector optionList; optionList.reserve(argList->args.size()); for (auto& arg : argList->args) { switch (arg->argInstanceType) { case ArgInstanceType::Input: optionList.emplace_back(static_cast(arg.get())->value); break; case ArgInstanceType::Option: { OptionArgInstance& optArg = *static_cast(arg.get()); if (optArg.info.GetID() == Options::ID::OUTPUT_FILE) { break; } if (optArg.info.BelongsGroup(Options::Group::DRIVER)) { break; } optionList.emplace_back(optArg.name); if (optArg.info.GetKind() == Options::Kind::SEPARATED) { optionList.emplace_back(optArg.value); } break; // All constructors of ArgInstanceType are handled here. Without `default` case, // exhaustive type checking could help detect potential errors. } } } bool multiBC = aggressiveParallelCompile.value_or(1) > 1; if (!frontendOutputFiles.empty() && !multiBC) { optionList.emplace_back("-o"); optionList.emplace_back(frontendOutputFiles[0].filePath); } return optionList; } void GlobalOptions::DeprecatedOptionCheck(const OptionArgInstance& arg) const { // Check if the option is deprecated which will be removed in the future release Options::ID id = arg.info.GetID(); const std::unordered_set deprecatedOptions{Options::ID::STATIC_LIBS, Options::ID::DY_LIBS}; DiagnosticEngine diag; if (deprecatedOptions.find(id) != deprecatedOptions.end()) { std::string substitutableOption = ""; (void)diag.DiagnoseRefactor( DiagKindRefactor::driver_deprecated_option, DEFAULT_POSITION, arg.str, substitutableOption); } } bool GlobalOptions::TryParseOption(OptionArgInstance& arg, ArgList& argList) { #ifdef CANGJIE_VISIBLE_OPTIONS_ONLY if (std::count(arg.info.groups.begin(), arg.info.groups.end(), Options::Group::VISIBLE) == 0) { return false; }; #endif if (!experimentalMode && std::count(arg.info.groups.begin(), arg.info.groups.end(), Options::Group::STABLE) == 0) { ErrorExperimentalOption(arg.str); return false; } if (arg.info.values.size() != 0) { auto result = std::find_if(arg.info.values.begin(), arg.info.values.end(), [value = arg.value, this](const Options::OptionValue& optionValue) { std::set backends{GetOptionsBackend()}; return value == optionValue.value && OptionTable::BelongsTo(backends, optionValue.backends, Options::Backend::ALL) != 0; }); #ifdef CANGJIE_VISIBLE_OPTIONS_ONLY if (result != arg.info.values.end() && std::count(result->groups.begin(), result->groups.end(), Options::Group::VISIBLE) == 0) { Errorf("invalid value: '%s'\n", result->value.c_str()); return false; }; #endif if (result != arg.info.values.end() && !experimentalMode && std::count(result->groups.begin(), result->groups.end(), Options::Group::STABLE) == 0) { ErrorExperimentalOption(arg.str); return false; } } // Though the deprecated option will be removed in future release, it still works in current release. DeprecatedOptionCheck(arg); auto maybeResult = ParseOption(arg); CJC_ASSERT(maybeResult.has_value()); // We expect ParseOption always returns an option with a value. If it doesn't, it is an ICE. // Release version will fail on getting value and raise an exception, which triggers ICE fail process. auto success = maybeResult.value(); OccurrenceCheck(arg, argList); return success; } void GlobalOptions::OccurrenceCheck(const OptionArgInstance& arg, ArgList& argList) const { if (argList.IsSpecified(arg.info.GetID()) && arg.info.GetOccurrenceType() == Options::Occurrence::SINGLE_OCCURRENCE && !argList.IsWarned(arg.info.GetID())) { std::string name = arg.info.GetName(); name += arg.info.GetAlias().empty() ? "" : "' or '" + arg.info.GetAlias(); Warningf("'%s' is specified multiple times. The last one is chosen.\n", name.c_str()); argList.MarkWarned(arg.info.GetID()); } argList.MarkSpecified(arg); } std::optional GlobalOptions::CheckDirectoryPath(const std::string& path) const { DiagnosticEngine diag; if (!FileUtil::FileExist(path)) { RaiseArgumentUnusedMessage(diag, DiagKindRefactor::driver_warning_no_such_directory, path); return std::nullopt; } if (!FileUtil::IsDir(path)) { RaiseArgumentUnusedMessage(diag, DiagKindRefactor::driver_warning_not_a_directory, path); return std::nullopt; } auto maybePath = FileUtil::GetAbsPath(path); if (!maybePath.has_value()) { RaiseArgumentUnusedMessage(diag, DiagKindRefactor::driver_warning_invalid_path, path); } return maybePath; } std::optional GlobalOptions::ValidateDirectoryPath(const std::string& path, FileUtil::FileMode mode) const { DiagnosticEngine diag; auto printErrorMessage = [&path, &diag](AccessResultType resultType) { if (resultType == AccessResultType::NO_PERMISSION) { diag.DiagnoseRefactor(DiagKindRefactor::permission_denied, DEFAULT_POSITION, path); } else { diag.DiagnoseRefactor(DiagKindRefactor::no_such_directory, DEFAULT_POSITION, path); } }; auto res = FileUtil::AccessWithResult(path, mode); if (res != AccessResultType::OK) { printErrorMessage(res); return std::nullopt; } if (!FileUtil::IsDir(path)) { (void)diag.DiagnoseRefactor(DiagKindRefactor::not_a_directory, DEFAULT_POSITION, path); return std::nullopt; } if (mode == FileUtil::FileMode::FM_WRITE) { // EXE permission is also requires if we need to write to the directory res = FileUtil::AccessWithResult(path, FileUtil::FileMode::FM_EXE); if (res != AccessResultType::OK) { printErrorMessage(res); return std::nullopt; } } auto maybePath = FileUtil::GetAbsPath(path); if (!maybePath.has_value()) { (void)diag.DiagnoseRefactor(DiagKindRefactor::invalid_path, DEFAULT_POSITION, path); } return maybePath; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::optional GlobalOptions::CheckInputFilePath(const std::string& path) const { DiagnosticEngine diag; if (!FileUtil::FileExist(path)) { RaiseArgumentUnusedMessage(diag, DiagKindRefactor::driver_warning_no_such_file_or_directory, path); return std::nullopt; } if (!FileUtil::Access(path, FileUtil::FileMode::FM_READ)) { RaiseArgumentUnusedMessage(diag, DiagKindRefactor::driver_warning_permission_denied, path); return std::nullopt; } if (FileUtil::IsDir(path)) { RaiseArgumentUnusedMessage(diag, DiagKindRefactor::driver_warning_not_a_file, path); return std::nullopt; } auto maybePath = FileUtil::GetAbsPath(path); if (!maybePath.has_value()) { RaiseArgumentUnusedMessage(diag, DiagKindRefactor::driver_warning_invalid_path, path); } return maybePath; } #endif std::optional GlobalOptions::ValidateInputFilePath( const std::string& path, const DiagKindRefactor notFoundError) const { DiagnosticEngine diag; if (IsAbsolutePathAboveLengthLimit(path)) { (void)diag.DiagnoseRefactor(DiagKindRefactor::driver_path_exceeds_length_limit, DEFAULT_POSITION, path, std::to_string(FILE_PATH_MAX_LENGTH)); return std::nullopt; } if (!FileExist(path)) { (void)diag.DiagnoseRefactor(notFoundError, DEFAULT_POSITION, path); return std::nullopt; } if (!FileUtil::Access(path, FileUtil::FileMode::FM_READ)) { (void)diag.DiagnoseRefactor(DiagKindRefactor::permission_denied, DEFAULT_POSITION, path); return std::nullopt; } if (FileUtil::IsDir(path)) { (void)diag.DiagnoseRefactor(DiagKindRefactor::not_a_file, DEFAULT_POSITION, path); return std::nullopt; } auto maybePath = FileUtil::GetAbsPath(path); if (!maybePath.has_value()) { (void)diag.DiagnoseRefactor(DiagKindRefactor::invalid_path, DEFAULT_POSITION, path); } return maybePath; } std::optional GlobalOptions::ParseIntOptionValue(const OptionArgInstance& arg, int from, int to) { auto maybeNumber = Utils::TryParseInt(arg.value); if (!maybeNumber.has_value()) { Errorf("The value of %s is invalid.\n", arg.name.c_str()); return std::nullopt; } auto number = maybeNumber.value(); if (number < from || number > to) { Errorf("The value of %s is invalid.\n", arg.name.c_str()); return std::nullopt; } return maybeNumber; } std::string GlobalOptions::GetSharedLibraryExtension(Triple::OSType osType) { switch (osType) { case Triple::OSType::WINDOWS: return ".dll"; case Triple::OSType::DARWIN: return ".dylib"; case Triple::OSType::LINUX: case Triple::OSType::UNKNOWN: default: return ".so"; } } void GlobalOptions::ReadPathsFromEnvironmentVars(const std::unordered_map& environmentVars) { const std::string cangjiePath = "CANGJIE_PATH"; if (environmentVars.find(cangjiePath) != environmentVars.end()) { environment.cangjiePaths = FileUtil::SplitEnvironmentPaths(environmentVars.at(cangjiePath)); } if (environmentVars.find(CANGJIE_HOME) != environmentVars.end()) { environment.cangjieHome = FileUtil::GetAbsPath(environmentVars.at(CANGJIE_HOME)); } const std::string libraryPath = "LIBRARY_PATH"; if (environmentVars.find(libraryPath) != environmentVars.end()) { environment.libraryPaths = FileUtil::SplitEnvironmentPaths(environmentVars.at(libraryPath)); } const std::string path = "PATH"; if (environmentVars.find(path) != environmentVars.end()) { environment.paths = FileUtil::SplitEnvironmentPaths(environmentVars.at(path)); } const std::string sdkROOT = "SDKROOT"; if (environmentVars.find(sdkROOT) != environmentVars.end()) { environment.macOSSDKRoot = FileUtil::GetAbsPath(environmentVars.at(sdkROOT)); } environment.allVariables = environmentVars; } std::string GlobalOptions::GetCangjieLibHostPathName() const { return host.OSToString() + "_" + host.ArchToString() + "_" + BackendToString(backend); } std::string GlobalOptions::GetCangjieLibTargetPathName() const { std::string name = target.OSToString(); if (target.env != Triple::Environment::GNU && target.env != Triple::Environment::NOT_AVAILABLE) { name += "_" + target.EnvironmentToString(); } name += "_" + target.ArchToString() + "_" + BackendToString(backend); return name; } void GlobalOptions::SetCompilationCachedPath() { if (outputDir.has_value()) { compilationCachedPath = outputDir.value(); } else if (FileUtil::IsDir(output)) { compilationCachedPath = output; } else { compilationCachedPath = FileUtil::GetDirPath(output); } CJC_ASSERT(compilationCachedPath != ""); } static std::string HashString(const std::string& str) { std::stringstream ss; ss << std::hex << Utils::SipHash::GetHashValue(str); return std::move(*ss.rdbuf()).str(); } std::string GlobalOptions::GetHashedObjFileName(const std::string& objFileName) const { return FileUtil::JoinPath(compilationCachedDir, HashString(compilationCachedFileName + objFileName)); } std::pair GlobalOptions::GenerateNamesOfCachedDirAndFile( const std::string& fullPackageName) const { std::string fileName; if (compilePackage) { fileName = fullPackageName; } else { // Sort all srcFiles' relative path std::vector sortedSrcFiles; for (auto& srcFile : srcFiles) { (void)sortedSrcFiles.emplace_back(GetCacheRelativeFilePath(srcFile)); } std::sort(sortedSrcFiles.begin(), sortedSrcFiles.end()); size_t allFilePathLength = std::accumulate(sortedSrcFiles.begin(), sortedSrcFiles.end(), 0uL, [](const size_t length, const std::string& filePath) { return length + filePath.length(); }); fileName.reserve(allFilePathLength); for (auto& filePath : sortedSrcFiles) { fileName += filePath; } } auto hashedFileName = HashString(fileName); std::string folder = FileUtil::JoinPath(compilationCachedPath, ".cached"); return std::make_pair(std::move(folder), std::move(hashedFileName)); } std::string GlobalOptions::GenerateCachedPathName( const std::string& fullPackageName, const std::string& extension) const { auto [folder, fileName] = GenerateNamesOfCachedDirAndFile(fullPackageName); if (!FileUtil::FileExist(folder)) { (void)FileUtil::CreateDirs(folder + "/"); } auto ret = FileUtil::JoinPath(folder, fileName + extension); return ret; } std::string GlobalOptions::GenerateCachedPathNameForCodeGen( const std::string& subModuleName, const std::string& extension) const { auto hashedFileName = codegenDebugMode ? subModuleName : HashString(subModuleName); auto folder = FileUtil::JoinPath(compilationCachedPath, ".cached"); if (!FileUtil::FileExist(folder)) { (void)FileUtil::CreateDirs(folder + "/"); } auto ret = FileUtil::JoinPath(folder, hashedFileName + extension); return ret; } std::string GlobalOptions::GetCacheRelativeFilePath(const std::string& filePath) const { CJC_ASSERT(!compilationCachedPath.empty()); auto absFilePath = FileUtil::GetAbsPath(filePath); if (!absFilePath.has_value()) { return FileUtil::GetFileName(filePath); } auto relativePath = FileUtil::GetRelativePath(FileUtil::JoinPath(compilationCachedPath, ""), absFilePath.value()); if (!relativePath.has_value()) { return FileUtil::GetFileName(filePath); } return relativePath.value(); } void GlobalOptions::UpdateCachedDirName(const std::string& fullPackageName) { std::tie(compilationCachedDir, compilationCachedFileName) = GenerateNamesOfCachedDirAndFile(fullPackageName); } std::string GlobalOptions::PassedWhenKeyValueToSerializedString() const { std::string result; std::map orderedMap; for (auto& it : passedWhenKeyValue) { orderedMap.emplace(it.first, it.second); } for (const auto& it : std::as_const(orderedMap)) { result += (it.first + it.second); } return result; } std::string GlobalOptions::BackendTypeToSerializedString() const { switch (backend) { case Triple::BackendType::CJNATIVE: return "LL"; case Triple::BackendType::UNKNOWN: return "K"; default: CJC_ABORT(); return ""; } } std::string GlobalOptions::OptimizationLevelToSerializedString() const { switch (optimizationLevel) { case OptimizationLevel::O0: return "O0"; case OptimizationLevel::O1: return "O1"; case OptimizationLevel::O2: return "O2"; case OptimizationLevel::O3: return "O3"; case OptimizationLevel::Os: return "Os"; case OptimizationLevel::Oz: return "Oz"; default: CJC_ABORT(); return ""; } } std::string GlobalOptions::OutputModeToSerializedString() const { switch (outputMode) { case OutputMode::EXECUTABLE: return "E"; case OutputMode::STATIC_LIB: return "C"; case OutputMode::SHARED_LIB: return "S"; case OutputMode::CHIR: return "CH"; default: CJC_ABORT(); return ""; } } std::string GlobalOptions::SanitizerCoverageOptions::ToSerializedString() const { std::string result; result += "coverageType:"; switch (coverageType) { case Type::SCK_UNKNOW: result += "K"; break; case Type::SCK_NONE: result += "N"; break; case Type::SCK_FUNCTION: result += "F"; break; case Type::SCK_BB: result += "BB"; break; default: break; } result += ",traceCmp:" + BoolToSerializedString(traceCmp); result += ",traceMemCmp" + BoolToSerializedString(traceMemCmp); result += ",tracePCGuard:" + BoolToSerializedString(tracePCGuard); result += ",inline8bitCounters:" + BoolToSerializedString(inline8bitCounters); result += ",inlineBoolFlag:" + BoolToSerializedString(inlineBoolFlag); result += ",pcTable:" + BoolToSerializedString(pcTable); result += ",stackDepth:" + BoolToSerializedString(stackDepth); return result; } std::string GlobalOptions::StackTraceFormatToSerializedString() const { std::string result = "stack-trace-format="; switch (stackTraceFmt) { case StackTraceFormat::ALL: result += "all"; break; case StackTraceFormat::SIMPLE: result += "simple"; break; case StackTraceFormat::DEFAULT: default: result += "default"; } return result; } std::string GlobalOptions::SelectedCHIROptsToSerializedString() const { Utils::Out64 printer{0, false, false}; for (auto flag : selectedCHIROpts) { printer << (1ull << static_cast(flag)); } std::stringstream ss; ss << printer; return std::move(*ss.rdbuf()).str(); } std::string GlobalOptions::OverflowStrategyToSerializedString() const { switch (overflowStrategy) { case OverflowStrategy::NA: return "N"; case OverflowStrategy::CHECKED: return "C"; case OverflowStrategy::WRAPPING: return "W"; case OverflowStrategy::THROWING: return "T"; case OverflowStrategy::SATURATING: return "S"; default: CJC_ABORT(); return ""; } } std::string GlobalOptions::SanitizerTypeToSerializedString() const { switch (sanitizerType) { case SanitizerType::NONE: return "none"; case SanitizerType::ADDRESS: return "address"; case SanitizerType::THREAD: return "thread"; case SanitizerType::HWADDRESS: return "hardware address"; default: return ""; } } std::string GlobalOptions::SanitizerTypeToShortString() const { switch (sanitizerType) { case SanitizerType::ADDRESS: return "asan"; case SanitizerType::THREAD: return "tsan"; case SanitizerType::HWADDRESS: return "hwasan"; case SanitizerType::NONE: default: return ""; } } std::vector GlobalOptions::ToSerialized() const { std::vector result; constexpr decltype(result.size()) estimatedOptionNum = 70; result.reserve(estimatedOptionNum); result.emplace_back(BoolToSerializedString(enableCompileDebug)); result.emplace_back(target.ToFullTripleString()); result.emplace_back(PassedWhenKeyValueToSerializedString()); result.emplace_back(SelectedCHIROptsToSerializedString()); result.emplace_back(BoolToSerializedString(compilePackage)); result.emplace_back(BoolToSerializedString(noSubPkg)); result.emplace_back(BoolToSerializedString(displayLineInfo)); result.emplace_back(moduleName); result.emplace_back(std::to_string(static_cast(ltoMod))); result.emplace_back(VectorStrToSerializedString(removedPathPrefix, ":")); result.emplace_back(BackendTypeToSerializedString()); result.emplace_back(BoolToSerializedString(enableMacroInLSP)); result.emplace_back(BoolToSerializedString(enableCompileTest)); result.emplace_back(BoolToSerializedString(implicitPrelude)); result.emplace_back(BoolToSerializedString(enableInteropCJMapping)); result.emplace_back(BoolToSerializedString(chirEA)); result.emplace_back(BoolToSerializedString(chirLICM)); result.emplace_back(BoolToSerializedString(chirCC)); result.emplace_back(OptimizationLevelToSerializedString()); result.emplace_back(StackTraceFormatToSerializedString()); result.emplace_back(OutputModeToSerializedString()); result.emplace_back(BoolToSerializedString(enableHotReload)); result.emplace_back(BoolToSerializedString(disableChirOpt)); result.emplace_back(sancovOption.ToSerializedString()); result.emplace_back(BoolToSerializedString(chirLLVM)); result.emplace_back(BoolToSerializedString(strictNumberMode)); result.emplace_back(BoolToSerializedString(fastMathMode)); result.emplace_back(BoolToSerializedString(enableCoverage)); result.emplace_back(SanitizerTypeToSerializedString()); result.emplace_back(BoolToSerializedString(experimentalMode)); result.emplace_back(OverflowStrategyToSerializedString()); (void)result.emplace_back(BoolToSerializedString(interpreter)); (void)result.emplace_back(VectorStrToSerializedString(interpreterSearchPaths, ":")); (void)result.emplace_back(VectorStrToSerializedString(interpLoadLib, ":")); (void)result.emplace_back(VectorStrToSerializedString(interpreterArgs, "\\")); (void)result.emplace_back(BoolToSerializedString(interpreterPrintResult)); (void)result.emplace_back(BoolToSerializedString(interpMainNoLinkage)); (void)result.emplace_back(BoolToSerializedString(disableCodeGen)); (void)result.emplace_back(BoolToSerializedString(disableDeserializer)); return result; } cangjie_compiler-1.0.7/src/Option/OptionAction.cpp000066400000000000000000001317071510705540100221710ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Option related classes. */ #include "cangjie/Option/Option.h" #include #include #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Utils/Utils.h" #include "cangjie/Utils/Unicode.h" #define OPTION_TRUE_ACTION(EXPR) \ [](GlobalOptions& opts, OptionArgInstance&) { \ (EXPR); \ return true; \ } using namespace Cangjie; using namespace Cangjie::Triple; using namespace Cangjie::Utils; using namespace Cangjie::FileUtil; namespace { constexpr size_t ELEMENT_NUM_OF_KV_PAIR = 2; const std::unordered_map BACKEND_STR_MAP = { {"llvm", uint8_t(BackendType::CJNATIVE)}, }; const std::unordered_map LTO_MODE_MAP = { {"full", uint8_t(GlobalOptions::LTOMode::FULL_LTO)}, {"thin", uint8_t(GlobalOptions::LTOMode::THIN_LTO)}, }; const std::unordered_map WARN_GROUP_MAP = { #define WARN_GROUP(DESCR, KIND) {DESCR, uint8_t(WarnGroup::KIND)}, #include "cangjie/Basic/DiagRefactor/DiagnosticWarnGroupKind.def" #undef WARN_GROUP }; const std::unordered_map DIAG_FORMAT_MAP = { {"json", uint8_t(DiagFormat::JSON)}, {"noColor", uint8_t(DiagFormat::NO_COLOR)}, {"default", uint8_t(DiagFormat::DEFAULT)}, }; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND const std::unordered_map OUTPUT_MODE_MAP = { {"exe", uint8_t(GlobalOptions::OutputMode::EXECUTABLE)}, {"staticlib", uint8_t(GlobalOptions::OutputMode::STATIC_LIB)}, {"dylib", uint8_t(GlobalOptions::OutputMode::SHARED_LIB)}, {"chir", uint8_t(GlobalOptions::OutputMode::CHIR)}, }; #endif const std::unordered_map STRING_ARCH_MAP = { {"x86_64", ArchType::X86_64}, {"aarch64", ArchType::AARCH64}, {"arm", ArchType::ARM32}, {"arm64", ArchType::ARM64}, {"unknown", ArchType::UNKNOWN} }; const std::unordered_map STRING_OS_MAP = { {"windows", OSType::WINDOWS}, {"w64", OSType::WINDOWS}, {"linux", OSType::LINUX}, {"darwin", OSType::DARWIN}, {"ios", OSType::IOS}, {"unknown", OSType::UNKNOWN} }; const std::unordered_map STRING_ENVIRONMENT_MAP = { {"ohos", Environment::OHOS}, {"gnu", Environment::GNU}, {"mingw32", Environment::GNU}, {"android", Environment::ANDROID}, {"simulator", Environment::SIMULATOR}, {"", Environment::NOT_AVAILABLE}, }; const std::unordered_map ENVIRONMENT_STRING_MAP = { {Environment::OHOS, "ohos"}, {Environment::GNU, "gnu"}, {Environment::GNU, "mingw32"}, {Environment::ANDROID, "android"}, {Environment::SIMULATOR, "simulator"}, {Environment::NOT_AVAILABLE, ""}, }; const std::unordered_map DUMP_CHIR_MODE_MAP = { {"na", uint8_t(GlobalOptions::CHIRMode::NA)}, {"standard", uint8_t(GlobalOptions::CHIRMode::STANDARD)}, {"withID", uint8_t(GlobalOptions::CHIRMode::WITH_ID)}, {"all", uint8_t(GlobalOptions::CHIRMode::ALL)}, }; const std::unordered_map EMIT_CHIR_PHASE_MAP = { {"raw", GlobalOptions::CandidateEmitCHIRPhase::RAW}, {"opt", GlobalOptions::CandidateEmitCHIRPhase::OPT}}; const std::unordered_map PRINT_BCHIR_MODE_MAP = { {"deserialized", uint8_t(GlobalOptions::PrintBCHIROption::DESERIALIED)}, {"linked", uint8_t(GlobalOptions::PrintBCHIROption::LINKED)}, {"chir2bchir", uint8_t(GlobalOptions::PrintBCHIROption::CHIR2BCHIR)}, {"interpreter", uint8_t(GlobalOptions::PrintBCHIROption::INTERPRETER)}, {"ce-linked", uint8_t(GlobalOptions::PrintBCHIROption::CE_LINKED)}, {"ce-chir2bchir", uint8_t(GlobalOptions::PrintBCHIROption::CE_CHIR2BCHIR)}, {"all", uint8_t(GlobalOptions::PrintBCHIROption::ALL)}, }; bool SeeingTomlComment(const std::string& inputLine) { if (inputLine.empty()) { return false; } if (inputLine.find('#') == std::string::npos) { return false; } for (auto c : inputLine) { if (c == ' ' || c == '\t') { continue; } if (c != '#') { return false; } else { return true; } } return true; } std::optional ParseOptimizationValue(const std::string& value) { int intValue = Utils::TryParseInt(value).value_or(-1); if (intValue < 0) { Errorf("Optimization level %s is invalid\n", value.c_str()); return std::nullopt; } if (intValue == 0) { // 0 stands for O0 return {GlobalOptions::OptimizationLevel::O0}; } else if (intValue == 1) { // 1 stands for O1 return {GlobalOptions::OptimizationLevel::O1}; } else if (intValue == 2) { // 2 stands for O2 return {GlobalOptions::OptimizationLevel::O2}; } else { // Any value greater than 2 stands for O3 #ifdef CANGJIE_VISIBLE_OPTIONS_ONLY Errorf("Optimization level %s is invalid\n", value.c_str()); return std::nullopt; #else return {GlobalOptions::OptimizationLevel::O3}; #endif } } bool ParseAppleTargetTriple(GlobalOptions& opts, const std::vector& parts) { // Parts must contain at least 3 elements. if (parts.size() < 3) { return false; } // Index 1 corresponds to vendor name. if (parts[1] != "apple") { return false; } opts.target.vendor = Vendor::APPLE; // Index 2 corresponds to os name. if (parts[2] == "darwin") { opts.target.os = OSType::DARWIN; opts.target.env = Environment::NOT_AVAILABLE; return true; } // 2 represents os name with version. if (parts[2].rfind("ios", 0) == 0) { opts.target.os = OSType::IOS; opts.target.apiLevel = parts[2].substr(3); // 2 is the index of os, 3 is the length of "ios". if (opts.target.apiLevel.empty()) { opts.target.apiLevel = "11"; } // In case of parts contain 4 elements, 3 represents environment type (supports simulator only). if (parts.size() == 4 && parts[3] == "simulator") { opts.target.env = Environment::SIMULATOR; return true; } else if (parts.size() == 3) { // When parts has 3 elements only. The target represents the real device. opts.target.env = Environment::NOT_AVAILABLE; return true; } } return false; }; bool ParseTargetTriple(GlobalOptions& opts, const std::string& triple) { auto parts = Utils::SplitString(triple, "-"); if (parts.size() != 3 && parts.size() != 4) { // A well-formed triple should have 3 or 4 fields Errorln("The target must be in either form of {ARCH}-{VENDOR}-{SYSTEM}-{ENV} or {ARCH}-{SYSTEM}-{ENV}."); return false; } auto archStr = parts[0]; if (auto arch = STRING_ARCH_MAP.find(archStr); arch != STRING_ARCH_MAP.end()) { opts.target.vendor = Triple::Vendor::UNKNOWN; if (arch->second == ArchType::ARM64) { opts.target.arch = ArchType::AARCH64; } else { opts.target.arch = arch->second; } } else { Errorln("The architecture \"" + archStr + "\" is not found or supported!"); return false; } if (ParseAppleTargetTriple(opts, parts)) { return true; } auto osStr = parts[parts.size() - 2]; if (auto os = STRING_OS_MAP.find(osStr); os != STRING_OS_MAP.end()) { opts.target.os = os->second; } else { Errorln("The OS \"" + osStr + "\" is not found or supported!"); return false; } auto envStr = parts[parts.size() - 1]; if (auto env = STRING_ENVIRONMENT_MAP.find(envStr); env != STRING_ENVIRONMENT_MAP.end()) { opts.target.env = env->second; if (opts.target.env == Environment::ANDROID) { opts.target.apiLevel = "31"; Infoln("ANDROID API level is not suggested in the target. Use API level " + opts.target.apiLevel + " by default."); } // If targeting Android, the env field may contains the API level like "android31" } else if (envStr.rfind(ENVIRONMENT_STRING_MAP.at(Environment::ANDROID), 0) == 0) { opts.target.env = Environment::ANDROID; opts.target.apiLevel = envStr.substr(7); // 7 is the length of "android" } else { Errorln("The environment \"" + envStr + "\" is not found or supported!"); return false; } return true; }; const std::string LIMIT_ERROR_COUNT_ALL = "all"; bool ParseErrorCountLimit(GlobalOptions& opts, const OptionArgInstance& arg) { if (arg.value == LIMIT_ERROR_COUNT_ALL) { opts.errorCountLimit = std::nullopt; return true; } int number = Utils::TryParseInt(arg.value).value_or(-1); if (number < 0) { Errorf("The value of %s is invalid.\n", arg.name.c_str()); return false; } opts.errorCountLimit = static_cast(number); return true; } std::optional ParseSancovValue(const std::string& value) { int intValue = Utils::TryParseInt(value).value_or(-1); if (intValue < 0) { Errorf("Sancov level %s is invalid\n", value.c_str()); return std::nullopt; } if (intValue == 0) { return {GlobalOptions::SanitizerCoverageOptions::Type::SCK_NONE}; } else if (intValue == 1) { return {GlobalOptions::SanitizerCoverageOptions::Type::SCK_FUNCTION}; } else { // All values greater than 2 are considered level=2. return {GlobalOptions::SanitizerCoverageOptions::Type::SCK_BB}; } } enum class ConditionalCompileConfigMode { PATH, KEY_VALUE }; ConditionalCompileConfigMode ParseConditionalCompileConfigMode(const std::string& cfgValue) { auto countNum = [](const std::string str, const std::string sub) -> size_t { if (sub.length() == 0) { return 0; } size_t count = 0; for (size_t offset = str.find(sub); offset != std::string::npos; offset = str.find(sub, offset + sub.length())) { ++count; } return count; }; size_t equalNum = countNum(cfgValue, "="); size_t escapeEqualNum = countNum(cfgValue, "\\="); return equalNum == escapeEqualNum ? ConditionalCompileConfigMode::PATH : ConditionalCompileConfigMode::KEY_VALUE; } void RemoveEscapedSymbol(std::string &input, const std::string& from, const std::string& to) { size_t startPos = 0; while ((startPos = input.find(from, startPos)) != std::string::npos) { input.replace(startPos, from.length(), to); startPos += to.length(); } } bool CheckPassWhenKey(const std::string key, DiagnosticEngine& diag) { if (!IsIdentifier(key, true)) { diag.DiagnoseRefactor(DiagKindRefactor::dirver_cfg_invaild_identifier, DEFAULT_POSITION); return false; } return true; } const static std::unordered_set BUILTIN_CONDITION = { "os", "backend", "arch", "debug", "cjc_version", "test"}; bool ParseAndCheckConditionVar( std::string& id, std::unordered_map& res, DiagnosticEngine& diag) { if (id.size() > std::string("``").size() && id[0] == '`') { id.substr(1, id.size() - std::string("``").size()); // parse raw id } Unicode::NFC(id); if (BUILTIN_CONDITION.find(id) != BUILTIN_CONDITION.end()) { diag.DiagnoseRefactor(DiagKindRefactor::dirver_cfg_same_with_builtin, DEFAULT_POSITION); return false; } if (res.find(id) != res.end()) { diag.DiagnoseRefactor(DiagKindRefactor::dirver_cfg_key_repeat, DEFAULT_POSITION); return false; } return true; } bool ParsePassedWhen( const std::string& input, std::unordered_map& res, std::vector& paths) { DiagnosticEngine diag; auto cfgMode = ParseConditionalCompileConfigMode(input); if (cfgMode == ConditionalCompileConfigMode::PATH) { std::string cfgDir = input; RemoveEscapedSymbol(cfgDir, "\\=", "="); if (!IsDir(cfgDir)) { diag.DiagnoseRefactor(DiagKindRefactor::dirver_cfg_not_a_dir, DEFAULT_POSITION, cfgDir); return false; } paths.emplace_back(cfgDir); return true; } auto keyValuePairs = Utils::SplitString(input, ","); for (const auto& it : keyValuePairs) { auto kv = Utils::SplitString(it, "="); if (kv.size() != ELEMENT_NUM_OF_KV_PAIR) { diag.DiagnoseRefactor(DiagKindRefactor::driver_cfg_value_err, DEFAULT_POSITION); return false; } Utils::TrimStringVector(kv); std::string id = kv[0]; if (id.empty() || kv[1].empty()) { diag.DiagnoseRefactor(DiagKindRefactor::driver_cfg_value_err, DEFAULT_POSITION); return false; } if (!CheckPassWhenKey(id, diag)) { return false; } if (!ParseAndCheckConditionVar(id, res, diag)) { return false; } res.emplace(id, kv[1]); } return true; } bool ParseCHIREA(GlobalOptions& opts, const OptionArgInstance& arg) { // Get CHIR escape analysis flag. if (arg.value == "on") { opts.chirEA = true; } else { CJC_ASSERT(arg.value == "off"); if (arg.value != "off") { return false; } opts.chirEA = false; } return true; } bool ParseCHIRWFC(GlobalOptions& opts, const OptionArgInstance& arg) { // Get CHIR well-formness-check flag. if (arg.value == "on") { opts.chirWFC = true; } else { CJC_ASSERT(arg.value == "off"); if (arg.value != "off") { return false; } opts.chirWFC = false; } return true; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::optional ParseJobsValue(const OptionArgInstance& arg) { // check if the input of jobs contains illegal character. std::string numStr = arg.value; if (numStr.find_first_not_of("0123456789") != std::string::npos) { Errorf("'%s' only accepts a positive integer number as value.\n", arg.name.c_str()); return std::nullopt; } // check if the input of jobs is an illegal big number. constexpr std::size_t maxLen = 9; if (numStr.length() > maxLen) { Errorf("'%s' gets an invalid number input.\n", arg.name.c_str()); return std::nullopt; } auto inputJobs = std::stoul(arg.value); auto threadNum = std::thread::hardware_concurrency(); // refactor the input of jobs in a valid range which is [1, threadNum]. // 1. when the inputJobs > threadNum, refactor inputJobs to threadNum; // 2. when the inputJobs < 1, refactor inputJobs to 1. if (inputJobs < 1) { Warningf("'%s' value must be greater than 0, the compiler will use 1 as the value.\n", arg.name.c_str()); inputJobs = 1; } else if (inputJobs > threadNum) { inputJobs = threadNum; } return inputJobs; } bool ParseJobs(GlobalOptions& opts, const OptionArgInstance& arg) { // Not allowed to be empty. if (arg.value.empty()) { Errorf("'%s' requires a non-empty value.\n", arg.name.c_str()); return false; } auto jobsValue = ParseJobsValue(arg); if (!jobsValue.has_value()) { return false; } opts.jobs = jobsValue; return true; } bool ParseAPCJobs(GlobalOptions& opts, const OptionArgInstance& arg) { if (arg.value.empty()) { opts.aggressiveParallelCompileWithoutArg = true; return true; } auto jobsValue = ParseJobsValue(arg); if (!jobsValue.has_value()) { return false; } constexpr std::size_t allowance = 2; Utils::Semaphore::Get().SetCount(jobsValue.value() + allowance); opts.aggressiveParallelCompile = jobsValue; return true; } #endif bool IsEscaped(const std::string& str, size_t index) { unsigned int backslashes = 0; while (index < str.size() && str[index] == '\\') { backslashes++; index--; } // backslashes is odd means the previous symbol is escaped backslash return (backslashes & 0x1) == 0x1; } size_t FindTomlComment(const std::string& inputLine) { // The pound sign marks the rest of the line as a comment, unless it is in a string. bool insideString = false; for (size_t i = 0; i < inputLine.length(); i++) { if (!insideString && inputLine[i] == '#') { return i; } else if (inputLine[i] == '"' && (i == 0 || !IsEscaped(inputLine, i - 1))) { insideString = !insideString; } } return std::string::npos; } // parse key value like: key = "value" bool ParseConditionKeyValueLine( const std::string& inputLine, std::unordered_map& res, DiagnosticEngine& diag) { auto processedInput = inputLine; auto tomlCommentPos = FindTomlComment(inputLine); if (tomlCommentPos != std::string::npos) { processedInput = inputLine.substr(0, tomlCommentPos); } auto kv = Utils::SplitString(processedInput, "="); if (kv.size() != ELEMENT_NUM_OF_KV_PAIR) { diag.DiagnoseRefactor(DiagKindRefactor::driver_cfg_toml_content_err, DEFAULT_POSITION); return false; } Utils::TrimStringVector(kv); constexpr size_t quotesSizeOfValue = 2; // for "" std::string id = kv[0]; if (id.empty() || kv[1].size() <= quotesSizeOfValue || kv[1][0] != '"' || kv[1].back() != '"') { diag.DiagnoseRefactor(DiagKindRefactor::driver_cfg_toml_content_err, DEFAULT_POSITION); return false; } if (!CheckPassWhenKey(id, diag)) { return false; } if (!ParseAndCheckConditionVar(id, res, diag)) { return false; } auto value = kv[1].substr(1, kv[1].size() - quotesSizeOfValue); RemoveEscapedSymbol(value, "\\\"", "\""); res.emplace(id, value); // rm "" return true; } // Pre-actions are actions processed at the first, before In-actions are processed. // Some options, for example, `-h`, `-v`, should prevent parsing proceeds, therefore, // we define actions for such options in a separated action map. std::unordered_map> g_preactions = { {Options::ID::VERBOSE, [](GlobalOptions& opts, OptionArgInstance&, bool& skipParsing) { opts.enableVerbose = true; skipParsing = false; return true; }}, {Options::ID::VERSION, [](GlobalOptions& opts, OptionArgInstance&, bool& skipParsing) { opts.printVersionOnly = true; skipParsing = true; return true; }}, {Options::ID::HELP, [](GlobalOptions& opts, OptionArgInstance&, bool& skipParsing) { opts.showUsage = true; skipParsing = true; return true; }}, {Options::ID::EXPERIMENTAL, [](GlobalOptions& opts, OptionArgInstance&, bool& skipParsing) { opts.experimentalMode = true; skipParsing = false; return true; }}, {Options::ID::BACKEND_MODE, [](GlobalOptions& opts, const OptionArgInstance& arg, bool& skipParsing) { CJC_ASSERT(BACKEND_STR_MAP.count(arg.value) != 0); if (BACKEND_STR_MAP.count(arg.value) == 0) { return false; } opts.backend = BackendType(BACKEND_STR_MAP.at(arg.value)); skipParsing = false; return true; }}, }; // g_actions are actions for general options. After Pre-actions are processed, if // no special skipping logic is taken, In-actions are performed. Most options // should be defined in g_actions. std::unordered_map> g_actions = { // ---------- GENERAL COMPILE OPTIONS ---------- { Options::ID::MODULE_NAME, []([[maybe_unused]] GlobalOptions& opts, const OptionArgInstance& arg) { DiagnosticEngine diag; diag.DiagnoseRefactor(DiagKindRefactor::driver_useless_option, DEFAULT_POSITION, arg.name); return true; }}, { Options::ID::PACKAGE_COMPILE, OPTION_TRUE_ACTION(opts.compilePackage = true) }, { Options::ID::NO_PRELUDE, OPTION_TRUE_ACTION(opts.implicitPrelude = false) }, { Options::ID::ENABLE_INTEROP_CJMAPPING, OPTION_TRUE_ACTION(opts.enableInteropCJMapping = true) }, { Options::ID::INT_OVERFLOW_MODE, [](GlobalOptions& opts, const OptionArgInstance& arg) { CJC_ASSERT(ValidOverflowStrategy(arg.value)); if (!ValidOverflowStrategy(arg.value)) { return false; } opts.overflowStrategy = StringToOverflowStrategy(arg.value); return true; }}, { Options::ID::STATIC_STD, OPTION_TRUE_ACTION(opts.linkStaticStd = true) }, { Options::ID::DY_STD, OPTION_TRUE_ACTION(opts.linkStaticStd = false) }, { Options::ID::STATIC_LIBS, OPTION_TRUE_ACTION((void)opts) }, { Options::ID::DY_LIBS, OPTION_TRUE_ACTION((void)opts) }, { Options::ID::LTO, [](GlobalOptions& opts, const OptionArgInstance& arg) { if (LTO_MODE_MAP.count(arg.value) == 0) { return false; } opts.ltoMod = GlobalOptions::LTOMode(LTO_MODE_MAP.at(arg.value)); return true; }}, { Options::ID::COMPILE_AS_EXE, OPTION_TRUE_ACTION(opts.enableCompileAsExe = true) }, { Options::ID::TARGET, [](GlobalOptions& opts, const OptionArgInstance& arg) { return ParseTargetTriple(opts, arg.value); }}, { Options::ID::PROFILE_COMPILE_TIME, OPTION_TRUE_ACTION(opts.enableTimer = true) }, { Options::ID::PROFILE_COMPILE_MEMORY, OPTION_TRUE_ACTION(opts.enableMemoryCollect = true) }, { Options::ID::IMPORT_PATH, [](GlobalOptions& opts, const OptionArgInstance& arg) { auto maybePath = opts.CheckDirectoryPath(arg.value); if (maybePath.has_value()) { opts.importPaths.emplace_back(maybePath.value()); } return true; }}, #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND { Options::ID::PLUGIN_PATH, [](GlobalOptions& opts, const OptionArgInstance& arg) { auto maybePath = opts.CheckInputFilePath(arg.value); auto suffix = GlobalOptions::GetSharedLibraryExtension(opts.host.os); if (maybePath.has_value()) { auto path = maybePath.value(); if ('.' + FileUtil::GetFileExtension(path) == suffix) { opts.pluginPaths.emplace_back(maybePath.value()); return true; } Errorf("'%s' requires a dynamic library path with '%s' suffix.\n", arg.name.c_str(), suffix.c_str()); return false; } Errorf("'%s' only accepts an existing dynamic library path.\n", arg.name.c_str()); return false; }}, #endif { Options::ID::COMMON_PART_PATH, [](GlobalOptions& opts, const OptionArgInstance& arg) { opts.commonPartCjo = opts.ValidateInputFilePath(arg.value, DiagKindRefactor::driver_invalid_binary_file); return true; }}, { Options::ID::INCRE_COMPILE, OPTION_TRUE_ACTION(opts.enIncrementalCompilation = true) }, { Options::ID::INCRE_DEBUG, OPTION_TRUE_ACTION(opts.printIncrementalInfo = true) }, { Options::ID::DUMP_DEPENDENT_PACKAGE, OPTION_TRUE_ACTION(opts.scanDepPkg = true) }, { Options::ID::NO_SUB_PACKAGE, OPTION_TRUE_ACTION(opts.noSubPkg = true) }, // ---------- GENERAL DEBUG CONTROL OPTIONS ---------- { Options::ID::NO_STACKTRACE_INFO, OPTION_TRUE_ACTION(opts.displayLineInfo = false) }, { Options::ID::COMPILE_DEBUG, OPTION_TRUE_ACTION(opts.enableCompileDebug = true) }, { Options::ID::TRIMPATH, [](GlobalOptions& opts, const OptionArgInstance& arg) { auto tmpPath = arg.value; if (!tmpPath.empty()) { tmpPath = FileUtil::Normalize(tmpPath); #ifdef _WIN32 tmpPath = tmpPath[tmpPath.size() - 1] == '\\' ? tmpPath : tmpPath + "\\"; #else tmpPath = tmpPath[tmpPath.size() - 1] == '/' ? tmpPath : tmpPath + "/"; #endif opts.removedPathPrefix.emplace_back(tmpPath); } return true; }}, // ---------- COVERAGE OPTIONS ---------- { Options::ID::ENABLE_COVERAGE, OPTION_TRUE_ACTION(opts.enableCoverage = true) }, // ---------- WARNING & ERROR CONTROL OPTIONS ----------' { Options::ID::WARN_OFF, [](GlobalOptions& /* opts */, const OptionArgInstance& arg) { if (arg.value == "all") { WarningOptionMgr::UpdateFlags(std::vector(static_cast(WarnGroup::NONE), true)); } else { CJC_ASSERT(WARN_GROUP_MAP.count(arg.value) != 0); if (WARN_GROUP_MAP.count(arg.value) == 0) { return false; } auto groupIdx = static_cast(WarnGroup(WARN_GROUP_MAP.at(arg.value))); WarningOptionMgr::UpdateFlag(groupIdx, true); } return true; }}, { Options::ID::WARN_ON, [](GlobalOptions& /* opts */, const OptionArgInstance& arg) { if (arg.value == "all") { WarningOptionMgr::UpdateFlags(std::vector(static_cast(WarnGroup::NONE), false)); } else { CJC_ASSERT(WARN_GROUP_MAP.count(arg.value) != 0); if (WARN_GROUP_MAP.count(arg.value) == 0) { return false; } auto groupIdx = static_cast(WarnGroup(WARN_GROUP_MAP.at(arg.value))); WarningOptionMgr::UpdateFlag(groupIdx, false); } return true; }}, // ---------- SANITIZER OPTIONS ---------- { Options::ID::SANITIZE, [](GlobalOptions& opts, const OptionArgInstance& arg) { if (arg.value == "address") { opts.sanitizerType = Cangjie::GlobalOptions::SanitizerType::ADDRESS; } else if (arg.value == "thread") { opts.sanitizerType = Cangjie::GlobalOptions::SanitizerType::THREAD; } else if (arg.value == "hwaddress") { opts.sanitizerType = Cangjie::GlobalOptions::SanitizerType::HWADDRESS; } else { return false; } return true; }}, // ---------- ERROR CONTROL OPTIONS ---------- { Options::ID::ERROR_COUNT_LIMIT, ParseErrorCountLimit }, { Options::ID::DIAG_FORMAT, [](GlobalOptions& opts, const OptionArgInstance& arg) { CJC_ASSERT(DIAG_FORMAT_MAP.count(arg.value) != 0); if (DIAG_FORMAT_MAP.count(arg.value) == 0) { return false; } opts.diagFormat = DiagFormat(DIAG_FORMAT_MAP.at(arg.value)); return true; }}, // ---------- TEST OPTIONS ---------- { Options::ID::COMPILE_TEST, [](GlobalOptions& opts, [[maybe_unused]] OptionArgInstance& arg) { opts.parseTest = true; opts.enableCompileTest = true; return true; }}, { Options::ID::COMPILE_TESTS_ONLY, [](GlobalOptions& opts, [[maybe_unused]] OptionArgInstance& arg) { opts.parseTest = true; opts.enableCompileTest = true; opts.compileTestsOnly = true; return true; }}, { Options::ID::EXPORT_FOR_TESTS, [](GlobalOptions& opts, [[maybe_unused]] OptionArgInstance& arg) { opts.exportForTest = true; return true; }}, // ---------- MOCKING OPTIONS ---------- { Options::ID::MOCK, [](GlobalOptions& opts, const OptionArgInstance& arg) { if (arg.value == "on") { opts.mock = MockMode::ON; } else if (arg.value == "runtime-error") { opts.mock = MockMode::RUNTIME_ERROR; } else if (arg.value == "off") { opts.mock = MockMode::OFF; } return true; }}, // ---------- MACRO OPTIONS ---------- { Options::ID::COMPILE_DEBUG_MACRO, OPTION_TRUE_ACTION(opts.enableMacroDebug = true) }, { Options::ID::PARALLEL_MACRO_EXPANSION, OPTION_TRUE_ACTION(opts.enableParallelMacro = true) }, // ---------- GENERAL OPTIMIZATION OPTIONS ---------- { Options::ID::OPTIMIZATION_0, [](GlobalOptions& opts, [[maybe_unused]] OptionArgInstance& arg) { return opts.SetOptimizationLevel(GlobalOptions::OptimizationLevel::O0); }}, { Options::ID::OPTIMIZATION_1, [](GlobalOptions& opts, [[maybe_unused]] OptionArgInstance& arg) { return opts.SetOptimizationLevel(GlobalOptions::OptimizationLevel::O1); }}, { Options::ID::OPTIMIZATION_2, [](GlobalOptions& opts, [[maybe_unused]] OptionArgInstance& arg) { return opts.SetOptimizationLevel(GlobalOptions::OptimizationLevel::O2); }}, { Options::ID::OPTIMIZATION_3, [](GlobalOptions& opts, [[maybe_unused]] OptionArgInstance& arg) { return opts.SetOptimizationLevel(GlobalOptions::OptimizationLevel::O3); }}, { Options::ID::OPTIMIZATION_S, [](GlobalOptions& opts, [[maybe_unused]] OptionArgInstance& arg) { return opts.SetOptimizationLevel(GlobalOptions::OptimizationLevel::Os); }}, { Options::ID::OPTIMIZATION_Z, [](GlobalOptions& opts, [[maybe_unused]] OptionArgInstance& arg) { return opts.SetOptimizationLevel(GlobalOptions::OptimizationLevel::Oz); }}, { Options::ID::OPTIMIZATION_CUSTOM, [](GlobalOptions& opts, const OptionArgInstance& arg) { auto maybeOptimizationLevel = ParseOptimizationValue(arg.value); if (maybeOptimizationLevel.has_value()) { return opts.SetOptimizationLevel(maybeOptimizationLevel.value()); } return false; }}, { Options::ID::OPT_PASS_OPTIONS, [](GlobalOptions& opts, const OptionArgInstance& arg) { opts.optPassOptions = arg.value; return true; }}, { Options::ID::DISABLE_BACKEND_OPT, OPTION_TRUE_ACTION(opts.disableBackendOpt = true) }, { Options::ID::FAST_MATH_MODE, OPTION_TRUE_ACTION(opts.fastMathMode = true) }, // ---------- GENERAL SANCOV OPTIONS ---------- { Options::ID::SANCOV_LEVEL_0, [](GlobalOptions& opts, [[maybe_unused]] OptionArgInstance& arg) { opts.sancovOption.SetSancovLevel(GlobalOptions::SanitizerCoverageOptions::Type::SCK_NONE); return true; }}, { Options::ID::SANCOV_LEVEL_1, [](GlobalOptions& opts, [[maybe_unused]] OptionArgInstance& arg) { opts.sancovOption.SetSancovLevel(GlobalOptions::SanitizerCoverageOptions::Type::SCK_FUNCTION); return true; }}, { Options::ID::SANCOV_LEVEL_2, [](GlobalOptions& opts, [[maybe_unused]] OptionArgInstance& arg) { opts.sancovOption.SetSancovLevel(GlobalOptions::SanitizerCoverageOptions::Type::SCK_BB); return true; }}, { Options::ID::SANCOV_LEVEL_CUSTOM, [](GlobalOptions& opts, const OptionArgInstance& arg) { auto maybeSancovLevel = ParseSancovValue(arg.value); if (maybeSancovLevel.has_value()) { opts.sancovOption.SetSancovLevel(maybeSancovLevel.value()); return true; } return false; }}, // ---------- COMPILE CONTROL OPTIONS ----------*/ { Options::ID::OUTPUT_TYPE, [](GlobalOptions& opts, const OptionArgInstance& arg) { // `--output-type=hotreload` generates DSO as well, except that we need to do more (or less) work on it. // To prevent adding checking everywhere `SharedLib` is checked for newly added hotreload output type, // we use another variable, `enableHotReload`, to indicate whether hot reload feature is enabled. if (arg.value == "hotreload") { opts.outputMode = GlobalOptions::OutputMode::SHARED_LIB; opts.enableHotReload = true; opts.linkStaticStd = false; // waiting for hotreload's bugfix } else { CJC_ASSERT(OUTPUT_MODE_MAP.count(arg.value) != 0); if (OUTPUT_MODE_MAP.count(arg.value) == 0) { return false; } opts.outputMode = GlobalOptions::OutputMode(OUTPUT_MODE_MAP.at(arg.value)); opts.enableHotReload = false; } opts.enableOutputType = true; return true; }}, { Options::ID::COMPILE_MACRO, [](GlobalOptions& opts, [[maybe_unused]] const OptionArgInstance& arg) { opts.compileMacroPackage = true; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND opts.outputMode = GlobalOptions::OutputMode::SHARED_LIB; #endif return true; }}, // ---------- OUTPUT CONTROL OPTIONS ---------- { Options::ID::OUTPUT_FILE, [](GlobalOptions& opts, const OptionArgInstance& arg) { // Not allowed to be empty. if (arg.value.empty()) { Errorf("'%s' requires a non-empty value.\n", arg.name.c_str()); return false; } opts.output = arg.value; return true; }}, { Options::ID::OUTPUT_DIR, [](GlobalOptions& opts, const OptionArgInstance& arg) { DiagnosticEngine diag; if (!IsDir(arg.value)) { (void)diag.DiagnoseRefactor(DiagKindRefactor::no_such_directory, DEFAULT_POSITION, arg.value); return false; } opts.outputDir = {arg.value}; return true; }}, { Options::ID::OUTPUT_JAVA_GEN_DIR, [](GlobalOptions& opts, const OptionArgInstance& arg) { opts.outputJavaGenDir = {arg.value}; return true; }}, { Options::ID::SAVE_TEMPS, [](GlobalOptions& opts, const OptionArgInstance& arg) { if (!IsDir(arg.value)) { DiagnosticEngine diag; (void)diag.DiagnoseRefactor(DiagKindRefactor::no_such_directory, DEFAULT_POSITION, arg.value); return false; } opts.saveTemps = true; opts.tempFolderPath = arg.value; return true; }}, // ---------- CONDITIONAL COMPILE OPTIONS ---------- { Options::ID::CONDITIONAL_COMPILATION_CONFIG, [](GlobalOptions& opts, const OptionArgInstance& arg) { if (arg.value.empty()) { DiagnosticEngine diag; (void)diag.DiagnoseRefactor(DiagKindRefactor::driver_cfg_value_err, DEFAULT_POSITION); return false; } return ParsePassedWhen(arg.value, opts.passedWhenKeyValue, opts.passedWhenCfgPaths); }}, // ---------- CHIR GENERAL OPTIONS ---------- { Options::ID::CHIR_EA, ParseCHIREA }, { Options::ID::CHIR_WFC, ParseCHIRWFC }, { Options::ID::ENABLE_CHIR_REDUNDANT_GETORTHROW_ELIMINATION, [](GlobalOptions& opts, [[maybe_unused]] const OptionArgInstance& arg) { opts.selectedCHIROpts.insert(GlobalOptions::OptimizationFlag::REDUNDANT_LOAD); opts.enableChirRGetOrThrowE = true; return true; }}, { Options::ID::DISABLE_CHIR_USELESS_IMPORT_ELIMINATION, []( GlobalOptions& opts, [[maybe_unused]] const OptionArgInstance& arg) { opts.disableChirUselessImportElimination = true; return true; }}, { Options::ID::DISABLE_SEMA_VIC, OPTION_TRUE_ACTION(opts.disableSemaVic = true)}, { Options::ID::DISABLE_INSTANTIATION, [](GlobalOptions& opts, [[maybe_unused]] OptionArgInstance& arg) { opts.disableInstantiation = true; return true; }}, { Options::ID::DEBUG_CODEGEN, OPTION_TRUE_ACTION(opts.codegenDebugMode = true) }, { Options::ID::CHIR_OPT_DEBUG, OPTION_TRUE_ACTION(opts.chirDebugOptimizer = true) }, { Options::ID::DUMP_CHIR_DEBUG, [](GlobalOptions& opts, [[maybe_unused]] const OptionArgInstance& arg) { opts.chirDumpDebugMode = true; return true; }}, { Options::ID::EMIT_CHIR, [](GlobalOptions& opts, [[maybe_unused]] const OptionArgInstance& arg) { if (arg.value.empty()) { opts.emitCHIRPhase = GlobalOptions::CandidateEmitCHIRPhase::OPT; return true; } if (EMIT_CHIR_PHASE_MAP.count(arg.value) == 0) { return false; } opts.emitCHIRPhase = EMIT_CHIR_PHASE_MAP.at(arg.value); return true; }}, #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND { Options::ID::DUMP_ANNOTATIONS_DEBUG, [](GlobalOptions& opts, [[maybe_unused]] const OptionArgInstance& arg) { opts.computeAnnotationsDebug = true; return true; }}, #endif { Options::ID::RENDER_CHIR, [](GlobalOptions& opts, const OptionArgInstance& arg) { CJC_ASSERT(DUMP_CHIR_MODE_MAP.count(arg.value) != 0); if (DUMP_CHIR_MODE_MAP.count(arg.value) == 0) { return false; } opts.chirRenderMode = GlobalOptions::CHIRMode(DUMP_CHIR_MODE_MAP.at(arg.value)); return true; }}, // ------------CHIR SANCOV OPTIONS---------------- { Options::ID::SANCOV_INLINE_8BIT, OPTION_TRUE_ACTION(opts.sancovOption.inline8bitCounters = true) }, { Options::ID::SANCOV_INLINE_BOOL_FLAG, OPTION_TRUE_ACTION(opts.sancovOption.inlineBoolFlag = true) }, { Options::ID::SANCOV_TRACE_PC_GUARD, OPTION_TRUE_ACTION(opts.sancovOption.tracePCGuard = true) }, { Options::ID::SANCOV_PC_TABLE, OPTION_TRUE_ACTION(opts.sancovOption.pcTable = true) }, { Options::ID::SANCOV_STACK_DEPTH, OPTION_TRUE_ACTION(opts.sancovOption.stackDepth = true) }, { Options::ID::SANCOV_TRACE_COMPARES, OPTION_TRUE_ACTION(opts.sancovOption.traceCmp = true) }, { Options::ID::SANCOV_TRACE_MEM_COMPARES, OPTION_TRUE_ACTION(opts.sancovOption.traceMemCmp = true) }, // ---------- CHIR OPTIMIZATION OPTIONS ---------- { Options::ID::DISABLE_CHIR_OPT, OPTION_TRUE_ACTION(opts.disableChirOpt = true) }, { Options::ID::CONST_PROPAGATION, OPTION_TRUE_ACTION(opts.selectedCHIROpts.insert(GlobalOptions::OptimizationFlag::CONST_PROPAGATION)) }, { Options::ID::NO_CONST_PROPAGATION, OPTION_TRUE_ACTION(opts.selectedCHIROpts.erase(GlobalOptions::OptimizationFlag::CONST_PROPAGATION)) }, { Options::ID::FUNC_INLINING, OPTION_TRUE_ACTION(opts.selectedCHIROpts.insert(GlobalOptions::OptimizationFlag::FUNC_INLINING)) }, { Options::ID::NO_FUNC_INLINING, OPTION_TRUE_ACTION(opts.selectedCHIROpts.erase(GlobalOptions::OptimizationFlag::FUNC_INLINING)) }, { Options::ID::DEVIRTUALIZATION, OPTION_TRUE_ACTION(opts.selectedCHIROpts.insert(GlobalOptions::OptimizationFlag::DEVIRTUALIZATION)) }, { Options::ID::NO_DEVIRTUALIZATION, OPTION_TRUE_ACTION(opts.selectedCHIROpts.erase(GlobalOptions::OptimizationFlag::DEVIRTUALIZATION)) }, { Options::ID::SROA_OPT, OPTION_TRUE_ACTION(opts.selectedCHIROpts.insert(GlobalOptions::OptimizationFlag::SROA_OPT)) }, { Options::ID::NO_SROA_OPT, OPTION_TRUE_ACTION(opts.selectedCHIROpts.erase(GlobalOptions::OptimizationFlag::SROA_OPT)) }, { Options::ID::REDUANDANT_RETURN_REMOVAL, OPTION_TRUE_ACTION(opts.selectedCHIROpts.insert(GlobalOptions::OptimizationFlag::REDUNDANT_RETURNS)) }, { Options::ID::NO_REDUANDANT_RETURN_REMOVAL, OPTION_TRUE_ACTION(opts.selectedCHIROpts.erase(GlobalOptions::OptimizationFlag::REDUNDANT_RETURNS)) }, { Options::ID::REDUNDANT_FUTURE_REMOVAL, OPTION_TRUE_ACTION(opts.selectedCHIROpts.insert(GlobalOptions::OptimizationFlag::REDUNDANT_FUTURE)) }, { Options::ID::NO_REDUNDANT_FUTURE_REMOVAL, OPTION_TRUE_ACTION(opts.selectedCHIROpts.erase(GlobalOptions::OptimizationFlag::REDUNDANT_FUTURE)) }, { Options::ID::REDUANDANT_ASSIGN_REMOVAL, OPTION_TRUE_ACTION(opts.selectedCHIROpts.insert(GlobalOptions::OptimizationFlag::REMOVE_REDUNDANT_ASG)) }, { Options::ID::NO_REDUANDANT_ASSIGN_REMOVAL, OPTION_TRUE_ACTION(opts.selectedCHIROpts.erase(GlobalOptions::OptimizationFlag::REMOVE_REDUNDANT_ASG)) }, { Options::ID::LETC_FOLDING, OPTION_TRUE_ACTION(opts.selectedCHIROpts.insert(GlobalOptions::OptimizationFlag::LETC_FOLDING)) }, { Options::ID::SWITCH_OPT, [](GlobalOptions& opts, [[maybe_unused]] const OptionArgInstance& arg) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND opts.selectedCHIROpts.insert(GlobalOptions::OptimizationFlag::SWITCH_OPT); #endif return true; }}, { Options::ID::REF_FOLDING, OPTION_TRUE_ACTION(opts.selectedCHIROpts.insert(GlobalOptions::OptimizationFlag::REF_FOLDING)) }, { Options::ID::ARRAY_LAMBDA_OPT, OPTION_TRUE_ACTION(opts.selectedCHIROpts.insert(GlobalOptions::OptimizationFlag::ARRAY_LAMBDA_OPT)) }, { Options::ID::LOOP_INVARIANT_CODE_MOTION, OPTION_TRUE_ACTION(opts.chirLICM = true) }, { Options::ID::NO_LOOP_INVARIANT_CODE_MOTION, OPTION_TRUE_ACTION(opts.chirLICM = false) }, // ---------- CHIR INTERPRETER ---------- { Options::ID::PRINT_BCHIR, [](GlobalOptions& opts, const OptionArgInstance& arg) { CJC_ASSERT(PRINT_BCHIR_MODE_MAP.count(arg.value) != 0); if (PRINT_BCHIR_MODE_MAP.count(arg.value) == 0) { return false; } opts.printBCHIR[static_cast(PRINT_BCHIR_MODE_MAP.at(arg.value))] = true; return true; }}, { Options::ID::INTERP_CONST_EVAL_DEBUG, OPTION_TRUE_ACTION(opts.constEvalDebug = true) }, { Options::ID::DISABLE_CODEGEN, [](GlobalOptions& opts, [[maybe_unused]] OptionArgInstance& arg) { opts.disableCodeGen = true; return true; }}, { Options::ID::DISABLE_REFLECTION, OPTION_TRUE_ACTION(opts.disableReflection = true) }, #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND { Options::ID::FUNC_SECTIONS, OPTION_TRUE_ACTION(opts.enableFuncSections = true) }, { Options::ID::NO_FUNC_SECTIONS, OPTION_TRUE_ACTION(opts.enableFuncSections = false) }, { Options::ID::DATA_SECTIONS, OPTION_TRUE_ACTION(opts.enableDataSections = true) }, { Options::ID::NO_DATA_SECTIONS, OPTION_TRUE_ACTION(opts.enableDataSections = false) }, { Options::ID::GC_SECTIONS, OPTION_TRUE_ACTION(opts.enableGcSections = true) }, { Options::ID::NO_GC_SECTIONS, OPTION_TRUE_ACTION(opts.enableGcSections = false) }, { Options::ID::PGO_INSTR_GEN, OPTION_TRUE_ACTION(opts.enablePgoInstrGen = true) }, { Options::ID::PGO_INSTR_USE, [](GlobalOptions& opts, const OptionArgInstance& arg) { opts.enablePgoInstrUse = true; opts.pgoProfileFile = arg.value; return true; }}, { Options::ID::STACK_TRACE_FORMAT, [](GlobalOptions& opts, const OptionArgInstance& arg) { if (arg.value == "default") { opts.stackTraceFmt = GlobalOptions::StackTraceFormat::DEFAULT; } else if (arg.value == "simple") { opts.stackTraceFmt = GlobalOptions::StackTraceFormat::SIMPLE; } else if (arg.value == "all") { opts.stackTraceFmt = GlobalOptions::StackTraceFormat::ALL; } else { return false; } return true; }}, { Options::ID::DISCARD_EH_FRAME, OPTION_TRUE_ACTION(opts.discardEhFrame = true) }, {Options::ID::JOBS, ParseJobs}, {Options::ID::AGGRESSIVE_PARALLEL_COMPILE, ParseAPCJobs}, #ifndef DISABLE_EFFECT_HANDLERS {Options::ID::ENABLE_EFFECTS, OPTION_TRUE_ACTION(opts.enableEH = true) }, #endif #endif // CANGJIE_CODEGEN_CJNATIVE_BACKEND }; } // namespace namespace Cangjie { std::string Triple::Info::EnvironmentToString() const { if (auto search = ENVIRONMENT_STRING_MAP.find(env); search != ENVIRONMENT_STRING_MAP.end()) { if (env == Environment::ANDROID) { return search->second + apiLevel; } return search->second; } else { return ""; } } bool SetupConditionalCompilationCfgFromFile(const std::string& filePath, std::unordered_map& passedWhenKeyValue, DiagnosticEngine& diag) { std::string failReason; auto content = ReadFileContent(filePath, failReason); if (content.has_value()) { auto cfgLines = SplitLines(content.value()); for (auto kv : cfgLines) { if (kv.empty() || SeeingTomlComment(kv)) { continue; } if (!ParseConditionKeyValueLine(kv, passedWhenKeyValue, diag)) { return false; } } return true; } diag.DiagnoseRefactor(DiagKindRefactor::driver_cfg_file_read_failed, DEFAULT_POSITION, filePath, failReason); return false; } } // namespace Cangjie bool GlobalOptions::TryParsePreOption(OptionArgInstance& arg, ArgList& argList, bool& skipParsing) { if (g_preactions.find(arg.info.GetID()) == g_preactions.end()) { return false; } bool success = g_preactions[arg.info.GetID()](*this, arg, skipParsing); OccurrenceCheck(arg, argList); DeprecatedOptionCheck(arg); return success; } std::optional GlobalOptions::ParseOption(OptionArgInstance& arg) { if (g_actions.find(arg.info.GetID()) == g_actions.end()) { return std::nullopt; } return {g_actions[arg.info.GetID()](*this, arg)}; } bool GlobalOptions::SetupConditionalCompilationCfg() { DiagnosticEngine diag; if (!passedWhenKeyValue.empty()) { if (!passedWhenCfgPaths.empty()) { diag.DiagnoseRefactor(DiagKindRefactor::driver_cfg_path_ignored, DEFAULT_POSITION); } return true; } const std::string cfgFileName("cfg.toml"); for (auto& path : passedWhenCfgPaths) { std::string filePath = JoinPath(path, cfgFileName); if (!FileExist(filePath)) { diag.DiagnoseRefactor(DiagKindRefactor::driver_warning_no_such_file, DEFAULT_POSITION, filePath); continue; } return SetupConditionalCompilationCfgFromFile(filePath, passedWhenKeyValue, diag); } // parse cfg.toml in default path std::string defaultCfgFilePath = packagePaths.empty() ? cfgFileName : JoinPath(packagePaths[0], cfgFileName); if (!FileExist(defaultCfgFilePath)) { return true; } if (SetupConditionalCompilationCfgFromFile(defaultCfgFilePath, passedWhenKeyValue, diag)) { return true; } return false; } cangjie_compiler-1.0.7/src/Option/OptionTable.cpp000066400000000000000000000272671510705540100220100ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the OptionTable related classes. */ #include "cangjie/Option/OptionTable.h" #include #include #include #include #include #include #include "cangjie/Basic/Print.h" namespace { #define BACKEND(backend) Cangjie::Options::Backend::backend #define GROUP(group) Cangjie::Options::Group::group #define PREDEFVALUE(NAME, VALUE) std::vector NAME = VALUE #define VALUE(VALUE, HELPTEXT, BACKENDS, GROUPS) \ Cangjie::Options::OptionValue{std::string(VALUE), std::string(HELPTEXT), BACKENDS, GROUPS} #include "cangjie/Option/Options.inc" #undef VALUE #undef PREDEFVALUE const std::vector INFO_LIST = { #define OPTION(NAME, id, KIND, BACKENDS, GROUPS, ALIAS, FLAGS, OCCURRENCE, HELPTEXT) \ {NAME, static_cast(Cangjie::Options::ID::id), Cangjie::Options::Kind::KIND, BACKENDS, GROUPS, ALIAS, \ FLAGS, Cangjie::Options::Occurrence::OCCURRENCE, HELPTEXT}, #include "cangjie/Option/Options.inc" #undef OPTION }; #undef GROUP #undef BACKEND } // namespace using namespace Cangjie; using namespace Cangjie::Options; static bool CheckValueInFlags(const std::string& value, const OptionTable::OptionInfo& info) { auto optionValues = info.GetOptionValues(); if (!optionValues.empty()) { return std::any_of(optionValues.begin(), optionValues.end(), [value](auto item) { return item.value == value; }); } return true; } std::pair> OptionTable::ParseArgumentName(const std::string& argStr) const { // Might be a joined value arg size_t pos = argStr.find_first_of('='); std::string argName; if (pos != std::string::npos) { argName = argStr.substr(0, pos); return std::make_pair(argName, std::make_optional(argStr.substr(pos + 1))); } argName = argStr; return std::make_pair(argName, std::nullopt); } bool OptionTable::ShouldArgumentBeRecognized( const std::string& argName, bool hasValue, const OptionTable::OptionInfo& i) { #ifdef CANGJIE_VISIBLE_OPTIONS_ONLY if (std::count(i.groups.begin(), i.groups.end(), Options::Group::VISIBLE) == 0) { return false; }; #endif if (argName != i.name && argName != (i.alias ? i.alias : "")) { return false; } if (!BelongsTo(enabledBackends, i.backends, Options::Backend::ALL) || !BelongsTo(enabledGroups, i.groups, Options::Group::GLOBAL)) { return false; } if (i.kind == Kind::FLAG_WITH_ARG && hasValue) { return true; } // The option name is recognized but it is not a SEPARATED option (which doesn't match its usage). if (i.kind != Kind::SEPARATED && hasValue) { return false; } return true; } std::optional OptionTable::ParseOptionArg(const std::string& argStr) { std::string argName; std::optional maybeValue; std::tie(argName, maybeValue) = ParseArgumentName(argStr); // We first iterate entire INFO_LIST and look for an option exactly matching the argument name. auto result = std::find_if(INFO_LIST.begin(), INFO_LIST.end(), [&argName, hasValue = maybeValue.has_value(), this] (const OptionTable::OptionInfo& i) { return ShouldArgumentBeRecognized(argName, hasValue, i); }); auto value = maybeValue.value_or(""); // According to GNU argument syntax conventions, an option and its argument may or may not appear as // separate tokens (This rule is only applied to single alphanumeric character options). For example, // '-oa.out' should be equivalent to '-o a.out'. // Here we iterate INFO_LIST again if there is no exact match. If the prefix of the argument matches // any option alias, we will take the argument as the option matched. bool isSeparatedOptionWithoutSpace = false; if (result == std::end(INFO_LIST)) { result = std::find_if(INFO_LIST.begin(), INFO_LIST.end(), [&argStr, this] (const OptionTable::OptionInfo& i) { #ifdef CANGJIE_VISIBLE_OPTIONS_ONLY if (std::count(i.groups.begin(), i.groups.end(), Options::Group::VISIBLE) == 0) { return false; }; #endif auto shouldArgumentBeRecognized = BelongsTo(enabledBackends, i.backends, Options::Backend::ALL) && BelongsTo(enabledGroups, i.groups, Options::Group::GLOBAL); return shouldArgumentBeRecognized && ((i.kind == Kind::SEPARATED && i.alias && argStr.find(i.alias, 0) == 0) || (i.kind == Kind::CONTINOUS && argStr.find(i.name, 0) == 0)); }); if (result != std::end(INFO_LIST)) { isSeparatedOptionWithoutSpace = true; // In the case of SEPARATED option which takes this branch, alias must be defined. argName = result->kind == Kind::SEPARATED ? result->alias : result->name; value = argStr.substr(argName.size()); } } if (result == std::end(INFO_LIST)) { Errorf("invalid option: '%s'.\n", argName.c_str()); return std::nullopt; } OptionArgInstance arg = OptionArgInstance(*result, argName); arg.str = argStr; arg.value = value; arg.hasJoinedValue = maybeValue.has_value(); if (!frontendMode && arg.info.BelongsGroup(Options::Group::FRONTEND)) { Errorf("invalid option: '%s'.\n", argName.c_str()); return std::nullopt; } return SetArgValue(arg, value, isSeparatedOptionWithoutSpace); } std::optional OptionTable::SetArgValue( OptionArgInstance& arg, const std::string& value, bool isSeparatedOptionWithoutSpace) const { if (arg.hasJoinedValue || arg.info.GetKind() == Kind::CONTINOUS) { if (value.empty()) { Warningf("option '%s' requires some values, format: option=value or option=\"v1, v2...\"\n", arg.name.c_str()); } if (!CheckValueInFlags(value, arg.info)) { Errorf("invalid value: '%s'\n", value.c_str()); return std::nullopt; } arg.value = value; } else if (isSeparatedOptionWithoutSpace) { if (!CheckValueInFlags(value, arg.info)) { Errorf("invalid value: '%s'\n", value.c_str()); return std::nullopt; } arg.value = value; } bool partialParsed = !arg.hasJoinedValue && arg.info.GetKind() == Options::Kind::SEPARATED && !isSeparatedOptionWithoutSpace; arg.argType = partialParsed ? ArgType::PartiallyParsed : ArgType::FullyParsed; return {arg}; } bool OptionTable::ParseShortTermArgs(const std::vector& argsStrs, size_t& idx, ArgList& argList) { auto isSpace = [](bool res, const char c) { return res && isspace(c); }; const auto& argStr = argsStrs[idx]; // Ignore empty argument if (argStr.empty() || std::accumulate(argStr.begin(), argStr.end(), true, isSpace)) { return true; } // This is an input source. if (argStr[0] != '-') { argList.args.emplace_back(std::make_unique(argStr)); return true; } // If the length of the option is 1, then it must contain a dash only. if (argStr.size() <= 1) { Errorln("invalid option: '-'."); return false; } std::optional maybeArg = ParseOptionArg(argStr); if (!maybeArg) { return false; } auto arg = maybeArg.value(); if (arg.argType == ArgType::PartiallyParsed && !ParseSeparatedArgValue(arg, argsStrs, idx)) { return false; } argList.args.emplace_back(std::make_unique(arg)); return true; } bool OptionTable::ParseSeparatedArgValue( OptionArgInstance& arg, const std::vector& argsStrs, size_t& idx) const { idx++; if (idx >= argsStrs.size()) { Errorf("this Option needs a value: '%s'\n", arg.name.c_str()); return false; } if (CheckValueInFlags(argsStrs[idx], arg.info)) { arg.value = {argsStrs[idx]}; arg.str = arg.name + " " + argsStrs[idx]; return true; } Errorf("invalid value: '%s'\n", argsStrs[idx].c_str()); return false; } bool OptionTable::ParseArgs(const std::vector& argsStrs, ArgList& argList) { if (argsStrs.size() <= 1) { return true; } // The first argument is the tool name, like "cjc". size_t idx{1}; size_t len = argsStrs.size(); while (idx < len) { if (!ParseShortTermArgs(argsStrs, idx, argList)) { return false; } idx++; } return true; } void OptionTable::PrintInfo(const OptionInfo& info, Options::Backend backend, bool showExperimental) const { std::string name(info.name); if (info.alias != nullptr) { name = std::string(info.alias) + ", " + name; } if (info.kind == Options::Kind::SEPARATED) { name = name + " "; } else if (info.kind == Options::Kind::CONTINOUS) { name = name + ""; } std::string desc(info.help); if (std::count(info.groups.begin(), info.groups.end(), Options::Group::STABLE) == 0) { desc += " (Experimental)"; } if (name.length() < OPTION_WIDTH) { PrintCommandDesc(name, desc, OPTION_WIDTH); } else { PrintCommandDesc(name, "", OPTION_WIDTH); PrintCommandDesc("", desc, OPTION_WIDTH); } for (const Options::OptionValue& optionValue : info.values) { #if defined(CANGJIE_VISIBLE_OPTIONS_ONLY) if (std::count(optionValue.groups.begin(), optionValue.groups.end(), Options::Group::VISIBLE) == 0) { continue; } #endif if (std::count(optionValue.backends.begin(), optionValue.backends.end(), Options::Backend::ALL) == 0 && std::count(optionValue.backends.begin(), optionValue.backends.end(), backend) == 0) { continue; } if (std::count(optionValue.groups.begin(), optionValue.groups.end(), Options::Group::STABLE) == 0 && !showExperimental) { continue; } // 2 space indent for values std::string value = " =" + optionValue.value; std::string help = " " + optionValue.help; if (std::count(optionValue.groups.begin(), optionValue.groups.end(), Options::Group::STABLE) == 0) { help += " (Experimental)"; } PrintCommandDesc(value, help, OPTION_WIDTH); } } void OptionTable::Usage(Options::Backend backend, std::set groups, bool showExperimental) { if (optionInfos.empty()) { return; } const std::string usage = "Usage:"; Println(usage); PrintIndentOnly(static_cast(usage.size()), 1); if (groups.find(Options::Group::FRONTEND) != groups.end()) { Println("cjc-frontend [option] file..."); } else { Println("cjc [option] file..."); } Println(); Println("Options:"); for (const auto& info : optionInfos) { #if defined(CANGJIE_VISIBLE_OPTIONS_ONLY) bool doPrint = std::count(info.groups.begin(), info.groups.end(), Options::Group::VISIBLE) != 0; #else bool doPrint = true; #endif if (doPrint && info.BelongsToAnyOfGroup(groups) && info.BelongsToBackend(backend)) { PrintInfo(info, backend, showExperimental); } } } namespace Cangjie { std::unique_ptr CreateOptionTable(bool frontendMode) { return std::make_unique(INFO_LIST, frontendMode); } } // namespace Cangjie cangjie_compiler-1.0.7/src/Parse/000077500000000000000000000000001510705540100166505ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Parse/ASTChecker.cpp000066400000000000000000000717231510705540100213020ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the ASTChecker related classes. */ #include "cangjie/Parse/ASTChecker.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Walker.h" namespace Cangjie::AST { void ASTChecker::CheckNode(Ptr node) { ZERO_POSITION_CHECK(node, node->begin); ZERO_POSITION_CHECK(node, node->end); if (node->astKind != ASTKind::PACKAGE && node->astKind != ASTKind::PACKAGE_SPEC) { ATTR_NULLPTR_CHECK(node, node->curFile); } } void ASTChecker::CheckExpr(Ptr node) { auto expr = StaticAs(node); if (expr->hasSemi) { ZERO_POSITION_CHECK(node, expr->semiPos); } } void ASTChecker::CheckIfExpr(Ptr node) { auto ifExpr = StaticAs(node); ZERO_POSITION_CHECK(node, ifExpr->ifPos); ZERO_POSITION_CHECK(node, ifExpr->leftParenPos); ZERO_POSITION_CHECK(node, ifExpr->rightParenPos); AST_NULLPTR_CHECK(node, ifExpr->condExpr); AST_NULLPTR_CHECK(node, ifExpr->thenBody); if (ifExpr->hasElse) { ZERO_POSITION_CHECK(node, ifExpr->elsePos); AST_NULLPTR_CHECK(node, ifExpr->elseBody); } } void ASTChecker::CheckAST(Node& node) { auto visitPre = [this](Ptr curNode) { if (curNode->IsInvalid() || curNode->TestAttr(Attribute::IS_BROKEN)) { return VisitAction::SKIP_CHILDREN; } if (curNode->TestAttr(Attribute::FROM_COMMON_PART)) { return VisitAction::SKIP_CHILDREN; } CheckNode(curNode); if (curNode->IsDecl()) { CheckDecl(curNode); } else if (curNode->IsExpr()) { CheckExpr(curNode); } if (checkFuncMap.count(curNode->astKind) != 0) { checkFuncMap[curNode->astKind](this, curNode); } return VisitAction::WALK_CHILDREN; }; Walker walker = Walker(&node, visitPre); walker.Walk(); }; void ASTChecker::CheckAST(const std::vector>& pkgs) { for (auto& pkg : pkgs) { CheckAST(*pkg.get()); } if (!checkInfoSet.empty()) { for (auto& checkInfo : checkInfoSet) { std::cerr << checkInfo << std::endl; } CJC_ABORT(); } } void ASTChecker::CheckBeginEnd(Ptr node) { std::set ignoredKindSet = {ASTKind::PACKAGE, ASTKind::PACKAGE_DECL}; auto visitPre = [&ignoredKindSet, this](Ptr curNode) { if (curNode->IsInvalid() || curNode->TestAttr(Attribute::IS_BROKEN)) { return VisitAction::SKIP_CHILDREN; } if (curNode->TestAttr(Attribute::FROM_COMMON_PART)) { return VisitAction::SKIP_CHILDREN; } if (ignoredKindSet.count(curNode->astKind) > 0) { return VisitAction::WALK_CHILDREN; } if (curNode->TestAttr(Attribute::COMPILER_ADD)) { return VisitAction::WALK_CHILDREN; } if (curNode->begin.IsZero()) { CollectInfo(curNode, "->begin"); } if (curNode->end.IsZero()) { CollectInfo(curNode, "->end"); } return VisitAction::WALK_CHILDREN; }; Walker walker = Walker(node, visitPre); walker.Walk(); } void ASTChecker::CheckBeginEnd(const std::vector>& pkgs) { for (auto& pkg : pkgs) { CheckBeginEnd(pkg.get()); } if (!checkInfoSet.empty()) { for (auto& filePath : checkInfoSet) { std::cerr << filePath << std::endl; } CJC_ABORT(); } } void ASTChecker::CheckAnnotation(Ptr node) { auto anno = StaticAs(node); EMPTY_STRING_CHECK(node, anno->identifier.Val()); VEC_AST_NULLPTR_CHECK(node, anno->args); switch (anno->kind) { case AnnotationKind::JAVA: EMPTY_STRING_CHECK(node, anno->definedPackage); break; case AnnotationKind::WHEN: AST_NULLPTR_CHECK(node, anno->condExpr); break; case AnnotationKind::ATTRIBUTE: VEC_ZERO_POS_CHECK(node, anno->attrCommas); break; default: break; } } void ASTChecker::CheckClassBody(Ptr node) { auto cb = StaticAs(node); ZERO_POSITION_CHECK(node, cb->leftCurlPos); VEC_AST_NULLPTR_CHECK(node, cb->decls); ZERO_POSITION_CHECK(node, cb->rightCurlPos); } void ASTChecker::CheckDecl(Ptr node) { auto decl = StaticAs(node); VEC_AST_NULLPTR_CHECK(node, decl->annotations); EMPTY_IDENTIFIER_CHECK(node, decl->identifier); ZERO_POSITION_CHECK(node, decl->keywordPos); EMPTY_STRING_CHECK(node, decl->fullPackageName); } void ASTChecker::CheckFile(Ptr node) { auto file = StaticAs(node); EMPTY_STRING_CHECK(node, file->fileName); EMPTY_STRING_CHECK(node, file->filePath); VEC_AST_NULLPTR_CHECK(node, file->imports); VEC_AST_NULLPTR_CHECK(node, file->decls); VEC_AST_NULLPTR_CHECK(node, file->trashBin); ATTR_NULLPTR_CHECK(node, file->curPackage); } void ASTChecker::CheckFuncArg(Ptr node) { auto funcArg = StaticAs(node); AST_NULLPTR_CHECK(node, funcArg->expr); } void ASTChecker::CheckFuncBody(Ptr node) { auto funcBody = StaticAs(node); VEC_AST_NULLPTR_CHECK(node, funcBody->paramLists); } void ASTChecker::CheckFuncParamList(Ptr node) { auto fpl = StaticAs(node); ZERO_POSITION_CHECK(node, fpl->leftParenPos); ZERO_POSITION_CHECK(node, fpl->rightParenPos); VEC_AST_NULLPTR_CHECK(node, fpl->params); } void ASTChecker::CheckMacroExpandParam(Ptr node) { auto mp = StaticAs(node); EMPTY_STRING_CHECK(node, mp->invocation.fullName); VEC_ZERO_POS_CHECK(node, mp->invocation.fullNameDotPos); EMPTY_STRING_CHECK(node, mp->invocation.identifier); ZERO_POSITION_CHECK(node, mp->invocation.identifierPos); } void ASTChecker::CheckGeneric(Ptr node) { auto ge = StaticAs(node); ZERO_POSITION_CHECK(node, ge->leftAnglePos); ZERO_POSITION_CHECK(node, ge->rightAnglePos); VEC_AST_NULLPTR_CHECK(node, ge->typeParameters); VEC_AST_NULLPTR_CHECK(node, ge->genericConstraints); } void ASTChecker::CheckGenericConstraint(Ptr node) { auto gc = StaticAs(node); ZERO_POSITION_CHECK(node, gc->wherePos); AST_NULLPTR_CHECK(node, gc->type); ZERO_POSITION_CHECK(node, gc->operatorPos); VEC_ZERO_POS_CHECK(node, gc->bitAndPos); VEC_AST_NULLPTR_CHECK(node, gc->upperBounds); ZERO_POSITION_CHECK(node, gc->commaPos); } void ASTChecker::CheckImportContent(Ptr node) { auto ic = StaticAs(node); VEC_EMPTY_STRING_CHECK(node, ic->prefixPaths); VEC_ZERO_POS_CHECK(node, ic->prefixPoses); VEC_ZERO_POS_CHECK(node, ic->prefixDotPoses); if (ic->kind != ImportKind::IMPORT_MULTI) { EMPTY_IDENTIFIER_CHECK(node, ic->identifier); if (ic->kind == ImportKind::IMPORT_ALIAS) { EMPTY_IDENTIFIER_CHECK(node, ic->aliasName); } } else { ZERO_POSITION_CHECK(node, ic->leftCurlPos); VEC_ZERO_POS_CHECK(node, ic->commaPoses); ZERO_POSITION_CHECK(node, ic->rightCurlPos); for (auto& item : ic->items) { CheckImportContent(&item); } } } void ASTChecker::CheckImportSpec(Ptr node) { auto is = StaticAs(node); ZERO_POSITION_CHECK(node, is->importPos); VEC_AST_NULLPTR_CHECK(node, is->annotations); } void ASTChecker::CheckInterfaceBody(Ptr node) { auto ib = StaticAs(node); ZERO_POSITION_CHECK(node, ib->leftCurlPos); ZERO_POSITION_CHECK(node, ib->rightCurlPos); VEC_AST_NULLPTR_CHECK(node, ib->decls); } void ASTChecker::CheckMatchCase(Ptr node) { auto mc = StaticAs(node); VEC_AST_NULLPTR_CHECK(node, mc->patterns); ZERO_POSITION_CHECK(node, mc->arrowPos); AST_NULLPTR_CHECK(node, mc->exprOrDecls); } void ASTChecker::CheckMatchCaseOther(Ptr node) { auto mco = StaticAs(node); AST_NULLPTR_CHECK(node, mco->matchExpr); ZERO_POSITION_CHECK(node, mco->arrowPos); AST_NULLPTR_CHECK(node, mco->exprOrDecls); } void ASTChecker::CheckPackage(Ptr node) { auto p = StaticAs(node); VEC_AST_NULLPTR_CHECK(node, p->files); VEC_AST_NULLPTR_CHECK(node, p->genericInstantiatedDecls); VEC_AST_NULLPTR_CHECK(node, p->inlineFuncDecls); } void ASTChecker::CheckPackageSpec(Ptr node) { auto ps = StaticAs(node); EMPTY_IDENTIFIER_CHECK(node, ps->packageName); ZERO_POSITION_CHECK(node, ps->packagePos); VEC_EMPTY_STRING_CHECK(node, ps->prefixPaths); VEC_ZERO_POS_CHECK(node, ps->prefixPoses); VEC_ZERO_POS_CHECK(node, ps->prefixDotPoses); if (ps->hasMacro) { ZERO_POSITION_CHECK(node, ps->macroPos); } } void ASTChecker::CheckFeaturesDirective(Ptr node) { auto fs = StaticAs(node); EMPTY_VEC_CHECK(node, fs->content); VEC_ZERO_POS_CHECK(node, fs->commaPoses); } void ASTChecker::CheckFeatureId(Ptr node) { auto fc = StaticAs(node); for (auto &ident : fc->identifiers) { EMPTY_IDENTIFIER_CHECK(node, ident); } VEC_ZERO_POS_CHECK(node, fc->dotPoses); } void ASTChecker::CheckStructBody(Ptr node) { auto sb = StaticAs(node); ZERO_POSITION_CHECK(node, sb->leftCurlPos); ZERO_POSITION_CHECK(node, sb->rightCurlPos); VEC_AST_NULLPTR_CHECK(node, sb->decls); } void ASTChecker::CheckFuncDecl(Ptr node) { auto fd = StaticAs(node); ZERO_POSITION_CHECK(node, fd->leftParenPos); ZERO_POSITION_CHECK(node, fd->rightParenPos); AST_NULLPTR_CHECK(node, fd->funcBody); } void ASTChecker::CheckInheritableDecl(Ptr node) { auto id = RawStaticCast(node); ZERO_POSITION_CHECK(node, id->upperBoundPos); VEC_AST_NULLPTR_CHECK(node, id->inheritedTypes); } void ASTChecker::CheckMacroDecl(Ptr node) { auto md = StaticAs(node); ZERO_POSITION_CHECK(node, md->leftParenPos); ZERO_POSITION_CHECK(node, md->rightParenPos); AST_NULLPTR_CHECK(node, md->funcBody); } void ASTChecker::CheckMacroExpandDecl(Ptr node) { auto me = StaticAs(node); EMPTY_STRING_CHECK(node, me->invocation.fullName); VEC_ZERO_POS_CHECK(node, me->invocation.fullNameDotPos); EMPTY_STRING_CHECK(node, me->invocation.identifier); ZERO_POSITION_CHECK(node, me->invocation.identifierPos); } void ASTChecker::CheckMainDecl(Ptr node) { auto md = StaticAs(node); AST_NULLPTR_CHECK(node, md->funcBody); } void ASTChecker::CheckPackageDecl(Ptr node) { auto pd = StaticAs(node); ATTR_NULLPTR_CHECK(node, pd->srcPackage); } void ASTChecker::CheckPrimaryCtorDecl(Ptr node) { auto pc = StaticAs(node); AST_NULLPTR_CHECK(node, pc->funcBody); } void ASTChecker::CheckTypeAliasDecl(Ptr node) { auto ta = StaticAs(node); ZERO_POSITION_CHECK(node, ta->assignPos); AST_NULLPTR_CHECK(node, ta->type); } void ASTChecker::CheckVarWithPatternDecl(Ptr node) { auto vwp = StaticAs(node); AST_NULLPTR_CHECK(node, vwp->irrefutablePattern); } void ASTChecker::CheckFuncParam(Ptr node) { auto fp = StaticAs(node); ZERO_POSITION_CHECK(node, fp->colonPos); } void ASTChecker::CheckPropDecl(Ptr node) { auto pd = StaticAs(node); ZERO_POSITION_CHECK(node, pd->colonPos); AST_NULLPTR_CHECK(node, pd->type); ZERO_POSITION_CHECK(node, pd->leftCurlPos); VEC_AST_NULLPTR_CHECK(node, pd->getters); VEC_AST_NULLPTR_CHECK(node, pd->setters); ZERO_POSITION_CHECK(node, pd->rightCurlPos); } void ASTChecker::CheckClassLikeDecl(Ptr node) { CheckInheritableDecl(node); } void ASTChecker::CheckClassDecl(Ptr node) { CheckClassLikeDecl(node); auto cd = StaticAs(node); AST_NULLPTR_CHECK(node, cd->body); } void ASTChecker::CheckInterfaceDecl(Ptr node) { CheckClassLikeDecl(node); auto id = StaticAs(node); AST_NULLPTR_CHECK(node, id->body); } void ASTChecker::CheckEnumDecl(Ptr node) { CheckInheritableDecl(node); auto ed = StaticAs(node); ZERO_POSITION_CHECK(node, ed->leftCurlPos); AST_NULLPTR_CHECK(node, ed->bodyScope); VEC_AST_NULLPTR_CHECK(node, ed->constructors); VEC_ZERO_POS_CHECK(node, ed->bitOrPosVector); VEC_AST_NULLPTR_CHECK(node, ed->members); } void ASTChecker::CheckExtendDecl(Ptr node) { CheckInheritableDecl(node); auto ed = StaticAs(node); AST_NULLPTR_CHECK(node, ed->extendedType); AST_NULLPTR_CHECK(node, ed->bodyScope); ZERO_POSITION_CHECK(node, ed->leftCurlPos); ZERO_POSITION_CHECK(node, ed->rightCurlPos); VEC_AST_NULLPTR_CHECK(node, ed->members); } void ASTChecker::CheckStructDecl(Ptr node) { CheckInheritableDecl(node); auto sd = StaticAs(node); AST_NULLPTR_CHECK(node, sd->body); } void ASTChecker::CheckArrayExpr(Ptr node) { auto ae = StaticAs(node); AST_NULLPTR_CHECK(node, ae->type); ZERO_POSITION_CHECK(node, ae->leftParenPos); VEC_AST_NULLPTR_CHECK(node, ae->args); VEC_ZERO_POS_CHECK(node, ae->commaPosVector); ZERO_POSITION_CHECK(node, ae->rightParenPos); } void ASTChecker::CheckArrayLit(Ptr node) { auto al = StaticAs(node); ZERO_POSITION_CHECK(node, al->leftSquarePos); VEC_AST_NULLPTR_CHECK(node, al->children); VEC_ZERO_POS_CHECK(node, al->commaPosVector); ZERO_POSITION_CHECK(node, al->rightSquarePos); } void ASTChecker::CheckAsExpr(Ptr node) { auto ae = StaticAs(node); AST_NULLPTR_CHECK(node, ae->leftExpr); AST_NULLPTR_CHECK(node, ae->asType); ZERO_POSITION_CHECK(node, ae->asPos); } void ASTChecker::CheckBlock(Ptr node) { auto ae = StaticAs(node); ZERO_POSITION_CHECK(node, ae->leftCurlPos); VEC_AST_NULLPTR_CHECK(node, ae->body); ZERO_POSITION_CHECK(node, ae->rightCurlPos); } void ASTChecker::CheckCallExpr(Ptr node) { auto ce = StaticAs(node); AST_NULLPTR_CHECK(node, ce->baseFunc); ZERO_POSITION_CHECK(node, ce->leftParenPos); VEC_AST_NULLPTR_CHECK(node, ce->args); ZERO_POSITION_CHECK(node, ce->rightParenPos); } void ASTChecker::CheckDoWhileExpr(Ptr node) { auto dwe = StaticAs(node); ZERO_POSITION_CHECK(node, dwe->doPos); AST_NULLPTR_CHECK(node, dwe->body); ZERO_POSITION_CHECK(node, dwe->whilePos); ZERO_POSITION_CHECK(node, dwe->leftParenPos); AST_NULLPTR_CHECK(node, dwe->condExpr); ZERO_POSITION_CHECK(node, dwe->rightParenPos); } void ASTChecker::CheckForInExpr(Ptr node) { auto fie = StaticAs(node); ZERO_POSITION_CHECK(node, fie->leftParenPos); AST_NULLPTR_CHECK(node, fie->pattern); ZERO_POSITION_CHECK(node, fie->rightParenPos); if (!fie->wherePos.IsZero()) { AST_NULLPTR_CHECK(node, fie->patternGuard); } AST_NULLPTR_CHECK(node, fie->body); } void ASTChecker::CheckIsExpr(Ptr node) { auto ie = StaticAs(node); AST_NULLPTR_CHECK(node, ie->leftExpr); AST_NULLPTR_CHECK(node, ie->isType); ZERO_POSITION_CHECK(node, ie->isPos); } void ASTChecker::CheckLambdaExpr(Ptr node) { auto le = StaticAs(node); AST_NULLPTR_CHECK(node, le->funcBody); } void ASTChecker::CheckLetPatternDestructor(Ptr node) { auto lpd = StaticAs(node); for (auto& p : lpd->patterns) { AST_NULLPTR_CHECK(node, p); } for (auto& ors : lpd->orPos) { ZERO_POSITION_CHECK(node, ors); } AST_NULLPTR_CHECK(node, lpd->initializer); ZERO_POSITION_CHECK(node, lpd->backarrowPos); } void ASTChecker::CheckLitConstExpr(Ptr node) { auto lce = StaticAs(node); EMPTY_STRING_CHECK(node, lce->stringValue); } void ASTChecker::CheckMacroExpandExpr(Ptr node) { auto me = StaticAs(node); EMPTY_STRING_CHECK(node, me->invocation.fullName); VEC_ZERO_POS_CHECK(node, me->invocation.fullNameDotPos); EMPTY_STRING_CHECK(node, me->invocation.identifier); ZERO_POSITION_CHECK(node, me->invocation.identifierPos); VEC_AST_NULLPTR_CHECK(node, me->annotations); } void ASTChecker::CheckMatchExpr(Ptr node) { auto me = StaticAs(node); ZERO_POSITION_CHECK(node, me->leftCurlPos); VEC_AST_NULLPTR_CHECK(node, me->matchCases); VEC_AST_NULLPTR_CHECK(node, me->matchCaseOthers); ZERO_POSITION_CHECK(node, me->rightCurlPos); if (me->matchMode) { AST_NULLPTR_CHECK(node, me->selector); ZERO_POSITION_CHECK(node, me->leftParenPos); ZERO_POSITION_CHECK(node, me->rightParenPos); } } void ASTChecker::CheckMemberAccess(Ptr node) { auto ma = StaticAs(node); AST_NULLPTR_CHECK(node, ma->baseExpr); ZERO_POSITION_CHECK(node, ma->dotPos); EMPTY_IDENTIFIER_CHECK(node, ma->field); } void ASTChecker::CheckTypeConvExpr(Ptr node) { auto ntc = StaticAs(node); AST_NULLPTR_CHECK(node, ntc->type); ZERO_POSITION_CHECK(node, ntc->leftParenPos); AST_NULLPTR_CHECK(node, ntc->expr); ZERO_POSITION_CHECK(node, ntc->rightParenPos); } void ASTChecker::CheckIfAvailableExpr(Ptr node) { auto ie = StaticCast(node); AST_NULLPTR_CHECK(ie, ie->GetArg()); AST_NULLPTR_CHECK(ie, ie->GetLambda1()); AST_NULLPTR_CHECK(ie, ie->GetLambda2()); } void ASTChecker::CheckParenExpr(Ptr node) { auto pe = StaticAs(node); ZERO_POSITION_CHECK(node, pe->leftParenPos); AST_NULLPTR_CHECK(node, pe->expr); ZERO_POSITION_CHECK(node, pe->rightParenPos); } void ASTChecker::CheckPointerExpr(Ptr node) { auto pe = StaticAs(node); AST_NULLPTR_CHECK(node, pe->type); AST_NULLPTR_CHECK(node, pe->arg); } void ASTChecker::CheckQuoteExpr(Ptr node) { auto qe = StaticAs(node); ZERO_POSITION_CHECK(node, qe->quotePos); ZERO_POSITION_CHECK(node, qe->leftParenPos); ZERO_POSITION_CHECK(node, qe->rightParenPos); } void ASTChecker::CheckRangeExpr(Ptr node) { auto re = StaticAs(node); ZERO_POSITION_CHECK(node, re->rangePos); } void ASTChecker::CheckRefExpr(Ptr node) { auto re = StaticAs(node); EMPTY_IDENTIFIER_CHECK(node, re->ref.identifier); } void ASTChecker::CheckReturnExpr(Ptr node) { auto re = StaticAs(node); ZERO_POSITION_CHECK(node, re->returnPos); AST_NULLPTR_CHECK(node, re->expr); } void ASTChecker::CheckSpawnExpr(Ptr node) { auto se = StaticAs(node); ZERO_POSITION_CHECK(node, se->spawnPos); AST_NULLPTR_CHECK(node, se->task); } void ASTChecker::CheckSynchronizedExpr(Ptr node) { auto se = StaticAs(node); ZERO_POSITION_CHECK(node, se->syncPos); ZERO_POSITION_CHECK(node, se->leftParenPos); AST_NULLPTR_CHECK(node, se->mutex); ZERO_POSITION_CHECK(node, se->rightParenPos); AST_NULLPTR_CHECK(node, se->body); } void ASTChecker::CheckThrowExpr(Ptr node) { auto te = StaticAs(node); ZERO_POSITION_CHECK(node, te->throwPos); AST_NULLPTR_CHECK(node, te->expr); } void ASTChecker::CheckPerformExpr(Ptr node) { auto pe = StaticAs(node); ZERO_POSITION_CHECK(node, pe->performPos); AST_NULLPTR_CHECK(node, pe->expr) ; } void ASTChecker::CheckResumeExpr(Ptr node) { auto re = StaticAs(node); ZERO_POSITION_CHECK(node, re->resumePos); if (re->withExpr) { ZERO_POSITION_CHECK(node, re->withPos); } if (re->throwingExpr) { ZERO_POSITION_CHECK(node, re->throwingPos); } } void ASTChecker::CheckTrailingClosureExpr(Ptr node) { auto tce = StaticAs(node); ZERO_POSITION_CHECK(node, tce->leftLambda); AST_NULLPTR_CHECK(node, tce->expr); AST_NULLPTR_CHECK(node, tce->lambda); ZERO_POSITION_CHECK(node, tce->rightLambda); } void ASTChecker::CheckTryExpr(Ptr node) { auto te = StaticAs(node); ZERO_POSITION_CHECK(node, te->tryPos); VEC_AST_NULLPTR_CHECK(node, te->resourceSpec); AST_NULLPTR_CHECK(node, te->tryBlock); VEC_ZERO_POS_CHECK(node, te->catchPosVector); VEC_AST_NULLPTR_CHECK(node, te->catchBlocks); VEC_AST_NULLPTR_CHECK(node, te->catchPatterns); for (const auto& handler : te->handlers) { ZERO_POSITION_CHECK(node, handler.pos); AST_NULLPTR_CHECK(node, handler.block); AST_NULLPTR_CHECK(node, handler.commandPattern); } } void ASTChecker::CheckTupleLit(Ptr node) { auto te = StaticAs(node); ZERO_POSITION_CHECK(node, te->leftParenPos); VEC_AST_NULLPTR_CHECK(node, te->children); VEC_ZERO_POS_CHECK(node, te->commaPosVector); ZERO_POSITION_CHECK(node, te->rightParenPos); } void ASTChecker::CheckWhileExpr(Ptr node) { auto we = StaticAs(node); ZERO_POSITION_CHECK(node, we->whilePos); ZERO_POSITION_CHECK(node, we->leftParenPos); AST_NULLPTR_CHECK(node, we->condExpr); ZERO_POSITION_CHECK(node, we->rightParenPos); AST_NULLPTR_CHECK(node, we->body); } void ASTChecker::CheckAssignExpr(Ptr node) { auto ae = StaticAs(node); AST_NULLPTR_CHECK(node, ae->leftValue); AST_NULLPTR_CHECK(node, ae->rightExpr); ZERO_POSITION_CHECK(node, ae->assignPos); } void ASTChecker::CheckBinaryExpr(Ptr node) { auto be = StaticAs(node); AST_NULLPTR_CHECK(node, be->leftExpr); AST_NULLPTR_CHECK(node, be->rightExpr); ZERO_POSITION_CHECK(node, be->operatorPos); } void ASTChecker::CheckIncOrDecExpr(Ptr node) { auto iod = StaticAs(node); ZERO_POSITION_CHECK(node, iod->operatorPos); AST_NULLPTR_CHECK(node, iod->expr); } void ASTChecker::CheckSubscriptExpr(Ptr node) { auto se = StaticAs(node); AST_NULLPTR_CHECK(node, se->baseExpr); ZERO_POSITION_CHECK(node, se->leftParenPos); VEC_AST_NULLPTR_CHECK(node, se->indexExprs); VEC_ZERO_POS_CHECK(node, se->commaPos); ZERO_POSITION_CHECK(node, se->rightParenPos); } void ASTChecker::CheckUnaryExpr(Ptr node) { auto ue = StaticAs(node); AST_NULLPTR_CHECK(node, ue->expr); ZERO_POSITION_CHECK(node, ue->operatorPos); } void ASTChecker::CheckConstantType(Ptr node) { auto ct = StaticAs(node); AST_NULLPTR_CHECK(node, ct->constantExpr); ZERO_POSITION_CHECK(node, ct->dollarPos); } void ASTChecker::CheckConstPattern(Ptr node) { auto cp = StaticAs(node); AST_NULLPTR_CHECK(node, cp->literal); } void ASTChecker::CheckEnumPattern(Ptr node) { auto ep = StaticAs(node); AST_NULLPTR_CHECK(node, ep->constructor); VEC_AST_NULLPTR_CHECK(node, ep->patterns); ZERO_POSITION_CHECK(node, ep->leftParenPos); ZERO_POSITION_CHECK(node, ep->rightParenPos); } void ASTChecker::CheckExceptTypePattern(Ptr node) { auto etp = StaticAs(node); AST_NULLPTR_CHECK(node, etp->pattern); ZERO_POSITION_CHECK(node, etp->patternPos); ZERO_POSITION_CHECK(node, etp->colonPos); VEC_AST_NULLPTR_CHECK(node, etp->types); VEC_ZERO_POS_CHECK(node, etp->bitOrPosVector); } void ASTChecker::CheckCommandTypePattern(Ptr node) { auto etp = StaticAs(node); AST_NULLPTR_CHECK(node, etp->pattern); ZERO_POSITION_CHECK(node, etp->patternPos); ZERO_POSITION_CHECK(node, etp->colonPos); VEC_AST_NULLPTR_CHECK(node, etp->types); VEC_ZERO_POS_CHECK(node, etp->bitOrPosVector); } void ASTChecker::CheckTuplePattern(Ptr node) { auto tp = StaticAs(node); ZERO_POSITION_CHECK(node, tp->leftBracePos); VEC_AST_NULLPTR_CHECK(node, tp->patterns); ZERO_POSITION_CHECK(node, tp->rightBracePos); } void ASTChecker::CheckTypePattern(Ptr node) { auto tp = StaticAs(node); AST_NULLPTR_CHECK(node, tp->pattern); ZERO_POSITION_CHECK(node, tp->colonPos); AST_NULLPTR_CHECK(node, tp->type); } void ASTChecker::CheckVarOrEnumPattern(Ptr node) { auto voe = StaticAs(node); EMPTY_STRING_CHECK(node, voe->identifier.Val()); } void ASTChecker::CheckVarPattern(Ptr node) { auto vp = StaticAs(node); AST_NULLPTR_CHECK(node, vp->varDecl); } void ASTChecker::CheckVArrayType(Ptr node) { auto vat = StaticAs(node); AST_NULLPTR_CHECK(node, vat->typeArgument); AST_NULLPTR_CHECK(node, vat->constantType); ZERO_POSITION_CHECK(node, vat->commaPos); ZERO_POSITION_CHECK(node, vat->leftAnglePos); ZERO_POSITION_CHECK(node, vat->rightAnglePos); } void ASTChecker::CheckFuncType(Ptr node) { auto ft = StaticAs(node); ZERO_POSITION_CHECK(node, ft->leftParenPos); VEC_AST_NULLPTR_CHECK(node, ft->paramTypes); ZERO_POSITION_CHECK(node, ft->rightParenPos); ZERO_POSITION_CHECK(node, ft->arrowPos); AST_NULLPTR_CHECK(node, ft->retType); } void ASTChecker::CheckOptionType(Ptr node) { auto ot = StaticAs(node); if (ot->questNum > 0) { AST_NULLPTR_CHECK(node, ot->componentType); } VEC_ZERO_POS_CHECK(node, ot->questVector); } void ASTChecker::CheckParenType(Ptr node) { auto pt = StaticAs(node); ZERO_POSITION_CHECK(node, pt->leftParenPos); AST_NULLPTR_CHECK(node, pt->type); ZERO_POSITION_CHECK(node, pt->rightParenPos); } void ASTChecker::CheckPrimitiveType(Ptr node) { auto pt = StaticAs(node); EMPTY_STRING_CHECK(node, pt->str); } void ASTChecker::CheckQualifiedType(Ptr node) { auto qt = StaticAs(node); AST_NULLPTR_CHECK(node, qt->baseType); ZERO_POSITION_CHECK(node, qt->dotPos); EMPTY_IDENTIFIER_CHECK(node, qt->field); } void ASTChecker::CheckRefType(Ptr node) { auto rt = StaticAs(node); EMPTY_IDENTIFIER_CHECK(node, rt->ref.identifier); } void ASTChecker::CheckTupleType(Ptr node) { auto tt = StaticAs(node); ZERO_POSITION_CHECK(node, tt->leftParenPos); ZERO_POSITION_CHECK(node, tt->rightParenPos); VEC_AST_NULLPTR_CHECK(node, tt->fieldTypes); VEC_ZERO_POS_CHECK(node, tt->commaPosVector); } void ASTChecker::CollectInfo(Ptr node, const std::string& subInfo) { std::ostringstream item; item << "ASTChecker: " << ASTKIND_TO_STR.at(node->astKind) << "'s " << subInfo.substr(subInfo.rfind("->") + std::string("->").size()) << " is not initialized at "; if (node->curFile != nullptr) { item << node->curFile->filePath; } if (!node->begin.IsZero()) { item << ":" << node->begin.line << ":" << node->begin.column; } else if (!node->end.IsZero()) { item << ":" << node->end.line << ":" << node->end.column; } checkInfoSet.emplace(item.str()); } // The following syntax tree nodes do not have attributes or do not check their attributes.So generate Null // implementation #define NULL_IMPLEMENTATION(nodeName) \ void ASTChecker::Check##nodeName(Ptr) \ { \ } NULL_IMPLEMENTATION(Modifier) NULL_IMPLEMENTATION(BuiltInDecl) NULL_IMPLEMENTATION(VarDecl) NULL_IMPLEMENTATION(GenericParamDecl) NULL_IMPLEMENTATION(InvalidDecl) NULL_IMPLEMENTATION(Pattern) NULL_IMPLEMENTATION(WildcardPattern) NULL_IMPLEMENTATION(InvalidPattern) NULL_IMPLEMENTATION(Type) NULL_IMPLEMENTATION(ThisType) NULL_IMPLEMENTATION(InvalidType) NULL_IMPLEMENTATION(OptionalExpr) NULL_IMPLEMENTATION(OptionalChainExpr) NULL_IMPLEMENTATION(PrimitiveTypeExpr) NULL_IMPLEMENTATION(InterpolationExpr) NULL_IMPLEMENTATION(StrInterpolationExpr) NULL_IMPLEMENTATION(TokenPart) NULL_IMPLEMENTATION(JumpExpr) NULL_IMPLEMENTATION(InvalidExpr) NULL_IMPLEMENTATION(DummyBody) NULL_IMPLEMENTATION(WildcardExpr) #undef NULL_IMPLEMENTATION } // namespace Cangjie::AST cangjie_compiler-1.0.7/src/Parse/ASTHasher.cpp000066400000000000000000001620071510705540100211440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the ASTHasher related classes. */ #include "cangjie/Parse/ASTHasher.h" #ifndef NDEBUG #include #include #endif #include "cangjie/AST/Node.h" #include "cangjie/AST/Walker.h" #include "cangjie/IncrementalCompilation/Utils.h" #include "cangjie/AST/ASTCasting.h" #include "cangjie/AST/Utils.h" #include "cangjie/Utils/SipHash.h" static constexpr int ALL = 0; static constexpr int ONLY_POSITION = 1; static constexpr int NON_POSITION = 2; static bool g_needLineInfo{false}; // when hashing src info, trim the package path if one present. This is to ensure // 1. code position does not change when the total number of source files (including source files of imported packages) // changes // 2. code position does not change when all the source files are moved from one directory to another as long as the // relative path of each source file to the package does not change static std::optional g_packagePath{}; static std::vector g_trimPaths{}; #ifndef NDEBUG namespace Cangjie::AST { namespace { // This variable is purely used for debug. ASTHasher has been proved to be hard to debug by the means of breakpoints, // as the hash value changes after almost every line, even by multiple times. As a fallback debug method, track the // falty hash value of a decl by printing the hash value after every change is a good idea. Based on this method, // turn this variable to true when the falty decl is detected as input to any entry function of ASTHasher module, print // after each mutating method, and then turn it off when the hash of the decl ends to compute. bool g_print{false}; [[maybe_unused]] void PrintDebug(Cangjie::AST::ASTHasher::hash_type v, int line = __LINE__) { if (!g_print) { return; } std::cout << line << ":" << v << "\n"; } struct [[maybe_unused]] DebugWhen { DebugWhen(const Cangjie::AST::Decl& decl, std::string_view m) noexcept { if (decl.rawMangleName == m) { g_print = true; } } explicit DebugWhen(bool v) noexcept { if (v) { g_print = true; } } ~DebugWhen() noexcept { g_print = false; } }; } // namespace } #define PRINTV PrintDebug(value, __LINE__) #define PRINTAV PrintDebug(a.value, __LINE__) #endif namespace Cangjie::IncrementalCompilation { std::string TrimPackagePath(const std::string& path) { // remove path prefix when --trimpath is provided if (!g_trimPaths.empty()) { return FileUtil::RemovePathPrefix(path, g_trimPaths); } // otherwise, trim the package path when -p is provided if (g_packagePath) { if (path.find(*g_packagePath) == 0) { if (path.size() > g_packagePath->size() && FileUtil::IsSlash(path[g_packagePath->size()]) && !FileUtil::IsSlash(g_packagePath->at(g_packagePath->size() - 1))) { // trim the slash character if package path does not end with slash return path.substr(g_packagePath->size() + 1); } return path.substr(g_packagePath->size()); } } // otherwise, return a copy of the original path return path; } std::string GetTrimmedPath(AST::File* file) { if (!file) { return ""; } // remove begining package name and the following '/' auto path = file->filePath; if (file->curPackage) { auto pkgName = file->curPackage->fullPackageName; #ifdef _WIN32 pkgName += "\\"; #else pkgName += "/"; #endif if (path.find(pkgName) == 0) { path = path.substr(pkgName.size()); } } return TrimPackagePath(path); } } namespace Cangjie::AST { using namespace Cangjie::IncrementalCompilation; struct ASTHasherImpl { using hash_type = ASTHasher::hash_type; ASTHasherImpl() : value{0} { } hash_type value; hash_type HashSpecs(const Package& pk) { HashPackage(pk); return value; } /** CombineHash is a function used to create hash with fewer collisions. */ template void CombineHash(const T& v) { (void)CombineTwoHashes(Utils::SipHash::GetHashValue(v)); } template void CombineHash(const OwnedPtr& v) { CombineHash(v.get()); } template void CombineHash(Ptr v) { static_assert(std::is_base_of_v || std::is_same_v, "only unique pointer of Node and its derived class and MacroInvocation is hashable"); static_assert( !std::is_base_of_v && !std::is_base_of_v, "`Package` and `File` is not allowed"); (void)Hash(v); } template void CombineHash(const std::vector& v) { for (auto& item : v) { SUPERHash(item); } } template void CombineHash(const Token& tok) { SUPERHash(static_cast(tok.kind), tok.Begin(), tok.Value()); } template void CombineHash(const StringPart& sp) { SUPERHash(sp.begin, sp.end, static_cast(sp.strKind), sp.value); } template void CombineHash(const Position& pos) { if constexpr (whatTypeToHash == NON_POSITION) { return; } if (pos.GetStatus() == PositionStatus::IGNORE) { return; } CombineHash(pos.line); CombineHash(pos.column); CombineHash(pos.isCurFile); } template void CombineHash(const Identifier& id) { CombineHash(id.Val()); CombineHash(id.Begin()); } template void CombineHash(const SrcIdentifier& id) { CombineHash(static_cast(id)); CombineHash(id.IsRaw()); } template void CombineHash(const AST::AttributePack& attrs) { for (auto attr : attrs.GetRawAttrs()) { CombineHash(attr); } } template void CombineHash(const std::set& modifiers) { // specially handle decl.modifiers, we only care about AST::Modifier's modifier std::vector modifierVec; modifierVec.reserve(modifiers.size()); for (auto& modifier : modifiers) { (void)modifierVec.emplace_back(modifier.modifier); } std::stable_sort(modifierVec.begin(), modifierVec.end()); for (auto& modifier : modifierVec) { CombineHash(static_cast(modifier)); } } template inline void SuperHash(const Arg& input) { if constexpr ((whatTypeToHash == ONLY_POSITION && std::is_same::value) || (whatTypeToHash == NON_POSITION && !std::is_same::value) || whatTypeToHash == ALL) { // if there is `-g` in cjc's parameter, will ignore position's hash if (g_needLineInfo || !std::is_same::value) { CombineHash(input); } } } // skip debug info of this function template inline void SUPERHash(const First& firstArg, const Last&... inputs) { SuperHash(firstArg); if constexpr (sizeof...(inputs) != 0) { SUPERHash(inputs...); } } size_t CombineTwoHashes(const size_t ha) { value = ASTHasher::CombineHash(value, ha); return value; } using NodeKindT = std::underlying_type_t; static constexpr int nodeKind{static_cast(ASTKind::NODE) + 1}; static void (*hashFuncMap[nodeKind])(ASTHasherImpl&, Ptr); static void (*hashFuncMapOnlyPosition[nodeKind])(ASTHasherImpl&, Ptr); static void (*hashFuncMapNonPosition[nodeKind])(ASTHasherImpl&, Ptr); static constexpr unsigned declKindNum{static_cast(ASTKind::INVALID_DECL) + 1}; static inline bool IsNonZero(const Position& pos) { return pos.line != 0; } template void HashVarDeclAbstract(const VarDeclAbstract& vda) { HashDecl(vda); SUPERHash(vda.type, vda.initializer, vda.isVar); } template void HashNode(const Node& node) { SUPERHash(static_cast(node.astKind), node.scopeName, node.exportId, node.scopeLevel); // attrs below are set before sema auto nonSemaAttr = {Attribute::ABSTRACT, Attribute::CONSTRUCTOR, Attribute::DEFAULT, Attribute::ENUM_CONSTRUCTOR, Attribute::INTERNAL, Attribute::FOREIGN, Attribute::COMMON, Attribute::PLATFORM, Attribute::GENERIC, Attribute::GLOBAL, Attribute::INTRINSIC, Attribute::JAVA_APP, Attribute::JAVA_MIRROR, Attribute::OBJ_C_MIRROR, Attribute::OBJ_C_MIRROR_SUBTYPE, Attribute::MACRO_EXPANDED_NODE, Attribute::MACRO_FUNC, Attribute::MUT, Attribute::NUMERIC_OVERFLOW, Attribute::OPERATOR, Attribute::PRIVATE, Attribute::PUBLIC, Attribute::PROTECTED, Attribute::STATIC, Attribute::TOOL_ADD, Attribute::UNSAFE}; for (auto attr : nonSemaAttr) { SUPERHash(node.TestAttr(attr)); } } template void HashExpr(const Expr& expr) { SUPERHash(expr.begin, expr.end); if constexpr (whatTypeToHash != NON_POSITION) { if (expr.begin.GetStatus() != PositionStatus::IGNORE) { CJC_ASSERT(expr.curFile); SUPERHash(TrimPackagePath(expr.curFile->filePath)); } } HashNode(expr); // expr.overflowStrategy is from annos // expr.semiPos and expr.hasSemi are somehow overlapped SUPERHash(expr.semiPos, expr.hasSemi); } template void HashIfExpr(const IfExpr& ifExpr) { HashExpr(ifExpr); SUPERHash(ifExpr.ifPos, ifExpr.leftParenPos, ifExpr.rightParenPos, ifExpr.condExpr, ifExpr.thenBody, ifExpr.elsePos, ifExpr.elseBody, ifExpr.isElseIf, ifExpr.hasElse); } template void HashAnnotation(const Cangjie::AST::Annotation& anno) { HashNode(anno); // ignore annotation argument and expression positions SUPERHash(static_cast(anno.kind), anno.identifier.Val(), anno.args, static_cast(anno.overflowStrategy), anno.definedPackage, anno.condExpr, anno.isCompileTimeVisible, anno.attrCommas); for (auto& att : anno.attrs) { CombineHash(att); } } template void HashDecl(const Decl& decl) { HashNode(decl); SUPERHash(decl.modifiers, decl.annotations, decl.identifier.Val(), decl.generic); if constexpr (whatTypeToHash != NON_POSITION) { if (decl.begin.GetStatus() != PositionStatus::IGNORE) { CJC_ASSERT(decl.curFile); SUPERHash(TrimPackagePath(decl.curFile->filePath)); } } } template void HashFuncArg(const FuncArg& funcArg) { HashNode(funcArg); SUPERHash(funcArg.name, funcArg.expr, funcArg.withInout); } template void HashFuncBody(const FuncBody& funcBody) { HashNode(funcBody); SUPERHash(funcBody.paramLists, funcBody.retType, funcBody.body, funcBody.generic); } template void HashFuncParamList(const FuncParamList& fpl) { HashNode(fpl); SUPERHash(fpl.params, fpl.variadicArgIndex, fpl.hasVariableLenArg); } template void HashGeneric(const Generic& ge) { HashNode(ge); SUPERHash(ge.typeParameters, ge.genericConstraints); } template void HashGenericConstraint(const GenericConstraint& gc) { HashNode(gc); SUPERHash(gc.type, gc.upperBounds); } template void HashImportSpec(const ImportSpec& is) { HashNode(is); auto& im = is.content; if (im.kind != ImportKind::IMPORT_MULTI) { SUPERHash( static_cast(im.kind), im.prefixPaths, im.identifier.Val(), im.aliasName); } else { SUPERHash(static_cast(im.kind), im.prefixPaths, im.leftCurlPos, im.rightCurlPos); for (const auto& item : im.items) { SUPERHash( static_cast(item.kind), item.prefixPaths, item.identifier.Val(), item.aliasName.Val()); } } SUPERHash(im.hasDoubleColon); } template void HashPackageSpec(const PackageSpec& ps) { HashNode(ps); SUPERHash(ps.prefixPaths, ps.packageName, ps.hasMacro, ps.hasDoubleColon); } template void HashMatchCase(const MatchCase& mc) { HashNode(mc); SUPERHash(mc.patterns, mc.patternGuard, mc.wherePos, mc.arrowPos, mc.exprOrDecls); } template void HashMatchCaseOther(const MatchCaseOther& mco) { HashNode(mco); SUPERHash(mco.matchExpr, mco.arrowPos, mco.exprOrDecls); } template void HashPackage(const Package& p) { for (auto& file : p.files) { for (auto& import : file->imports) { if (!import->TestAttr(Attribute::IMPLICIT_ADD)) { HashImportSpec(*import); } } if (file->package) { HashPackageSpec(*file->package); } } } template void HashFuncDecl(const FuncDecl& fd) { HashDecl(fd); SUPERHash(fd.funcBody, static_cast(fd.op)); } template void HashPrimaryCtorDecl(const PrimaryCtorDecl& pc) { HashDecl(pc); SUPERHash(pc.funcBody); } template void HashVarWithPatternDecl(const VarWithPatternDecl& vwp) { HashVarDeclAbstract(vwp); SUPERHash(vwp.irrefutablePattern); } template void HashFuncParam(const FuncParam& fp) { HashVarDecl(fp); SUPERHash(fp.assignment, fp.isNamedParam, fp.hasLetOrVar); } template void HashArrayExpr(const ArrayExpr& ae) { HashExpr(ae); SUPERHash(ae.type, ae.args); } template void HashArrayLit(const ArrayLit& al) { HashExpr(al); SUPERHash(al.children); } template void HashAsExpr(const AsExpr& ae) { HashExpr(ae); SUPERHash(ae.leftExpr, ae.asType); } template void HashBlock(const Block& ae) { HashExpr(ae); // only position of unsafe block is used if (IsNonZero(ae.unsafePos)) { SUPERHash(ae.leftCurlPos, ae.body, ae.rightCurlPos); } else { SUPERHash(ae.body); } SUPERHash(ae.unsafePos); } template void HashCallExpr(const CallExpr& ce) { HashExpr(ce); SUPERHash(ce.baseFunc, ce.args); } template void HashDoWhileExpr(const DoWhileExpr& dwe) { HashExpr(dwe); SUPERHash(dwe.body, dwe.condExpr); } template void HashForInExpr(const ForInExpr& fie) { HashExpr(fie); SUPERHash(fie.pattern, fie.inPos, fie.inExpression, fie.patternGuard, fie.body); } template void HashIsExpr(const IsExpr& ie) { HashExpr(ie); SUPERHash(ie.leftExpr, ie.isType); } template void HashLambdaExpr(const LambdaExpr& le) { HashExpr(le); SUPERHash(le.funcBody); } template void HashLetPatternDestructor(const LetPatternDestructor& lpd) { SUPERHash(lpd.patterns, lpd.orPos, lpd.initializer, lpd.backarrowPos); } template void HashLitConstExpr(const LitConstExpr& lce) { HashExpr(lce); SUPERHash(lce.rawString, lce.siExpr, lce.stringValue, lce.codepoint, static_cast(lce.kind), static_cast(lce.stringKind), lce.delimiterNum); } template void HashMatchExpr(const MatchExpr& me) { HashExpr(me); SUPERHash( me.matchMode, me.selector, me.leftCurlPos, me.matchCases, me.matchCaseOthers, me.rightCurlPos); } template void HashMemberAccess(const MemberAccess& ma) { SUPERHash(ma.baseExpr, ma.field, ma.typeArguments, ma.isPattern); if (auto maTarget = ma.target) { SUPERHash(maTarget->rawMangleName); CJC_ASSERT(maTarget->ty); SUPERHash(maTarget->ty->String()); } } template void HashTypeConvExpr(const TypeConvExpr& ntc) { HashExpr(ntc); SUPERHash(ntc.type, ntc.leftParenPos, ntc.expr, ntc.rightParenPos); } template void HashParenExpr(const ParenExpr& pe) { HashExpr(pe); SUPERHash(pe.expr); } template void HashPointerExpr(const PointerExpr& pe) { HashExpr(pe); SUPERHash(pe.type, pe.arg); } template void HashQuoteExpr(const QuoteExpr& qe) { HashExpr(qe); SUPERHash(qe.quotePos); } template void HashRangeExpr(const RangeExpr& re) { HashExpr(re); SUPERHash(re.startExpr, re.rangePos, re.stopExpr, re.colonPos, re.stepExpr, re.isClosed); } template void HashRefExpr(const RefExpr& re) { HashExpr(re); SUPERHash( re.ref.identifier.Val(), re.typeArguments, re.isThis, re.isSuper, re.isQuoteDollar); if (auto refTarget = re.ref.target) { SUPERHash(refTarget->rawMangleName); CJC_ASSERT(refTarget->ty); SUPERHash(refTarget->ty->String()); } } template void HashReturnExpr(const ReturnExpr& re) { HashExpr(re); SUPERHash(re.expr); } template void HashSpawnExpr(const SpawnExpr& se) { HashExpr(se); SUPERHash(se.spawnPos, se.futureObj, se.task, se.arg); } template void HashSynchronizedExpr(const SynchronizedExpr& se) { HashExpr(se); SUPERHash(se.syncPos, se.leftParenPos, se.mutex, se.rightParenPos, se.body); } template void HashThrowExpr(const ThrowExpr& te) { HashExpr(te); SUPERHash(te.throwPos, te.expr); } template void HashPerformExpr(const PerformExpr& pe) { HashExpr(pe); SUPERHash(pe.performPos, pe.expr); } template void HashResumeExpr(const ResumeExpr& re) { HashExpr(re); SUPERHash(re.resumePos); } template void HashTrailingClosureExpr(const TrailingClosureExpr& tce) { HashExpr(tce); SUPERHash(tce.leftLambda, tce.expr, tce.lambda, tce.rightLambda); } template void HashTryExpr(const TryExpr& te) { HashExpr(te); SUPERHash(te.lParen, te.resourceSpec, te.tryBlock, te.catchPosVector, te.catchBlocks, te.catchPatterns, te.finallyPos, te.finallyBlock, te.isDesugaredFromTryWithResources); } template void HashTupleLit(const TupleLit& te) { SUPERHash(te.children); } template void HashWhileExpr(const WhileExpr& we) { HashExpr(we); SUPERHash(we.whilePos, we.condExpr, we.body); } template void HashOverloadableExpr(const OverloadableExpr& oe) { HashExpr(oe); SUPERHash(static_cast(oe.op)); } template void HashAssignExpr(const AssignExpr& ae) { HashOverloadableExpr(ae); SUPERHash(ae.leftValue, ae.assignPos, ae.rightExpr, ae.isCompound); } template void HashBinaryExpr(const BinaryExpr& be) { HashOverloadableExpr(be); SUPERHash(be.leftExpr, be.rightExpr, be.operatorPos); } template void HashIncOrDecExpr(const IncOrDecExpr& iod) { HashOverloadableExpr(iod); SUPERHash(iod.operatorPos, iod.expr); } template void HashSubscriptExpr(const SubscriptExpr& se) { HashExpr(se); // no need to hash op; also commaPos does not have debug info on it SUPERHash(se.baseExpr, se.leftParenPos, se.indexExprs); } template void HashUnaryExpr(const UnaryExpr& ue) { HashOverloadableExpr(ue); SUPERHash(ue.expr, ue.operatorPos); } template void HashConstPattern(const ConstPattern& cp) { HashPattern(cp); SUPERHash(cp.literal); } template void HashEnumPattern(const EnumPattern& ep) { HashPattern(ep); SUPERHash(ep.constructor, ep.patterns); } template void HashExceptTypePattern(const ExceptTypePattern& etp) { HashPattern(etp); SUPERHash(etp.pattern, etp.patternPos, etp.colonPos, etp.types, etp.bitOrPosVector); } template void HashTuplePattern(const TuplePattern& tp) { HashPattern(tp); SUPERHash(tp.leftBracePos, tp.patterns, tp.rightBracePos); } template void HashTypePattern(const TypePattern& tp) { HashPattern(tp); SUPERHash(tp.pattern, tp.type); } template void HashVarOrEnumPattern(const VarOrEnumPattern& voe) { HashPattern(voe); SUPERHash(voe.identifier.Val()); } template void HashVarPattern(const VarPattern& vp) { HashPattern(vp); SUPERHash(vp.varDecl); } template void HashFuncType(const FuncType& ft) { HashType(ft); SUPERHash(ft.paramTypes, ft.retType, ft.isC); } template void HashOptionType(const OptionType& ot) { HashType(ot); SUPERHash(ot.componentType, ot.questNum); } template void HashConstantType(const ConstantType& ct) { HashType(ct); SUPERHash(ct.constantExpr); } template void HashVArrayType(const VArrayType& vt) { HashType(vt); SUPERHash(vt.typeArgument, vt.constantType); } template void HashParenType(const ParenType& pt) { HashType(pt); SUPERHash(pt.type); } template void HashPrimitiveType(const PrimitiveType& pt) { HashType(pt); SUPERHash(static_cast(pt.kind)); } template void HashQualifiedType(const QualifiedType& qt) { HashType(qt); SUPERHash(qt.baseType, qt.field, qt.typeArguments); if (auto refTarget = qt.target) { SUPERHash(refTarget->rawMangleName); CJC_ASSERT(refTarget->ty); SUPERHash(refTarget->ty->String()); } } template void HashRefType(const RefType& rt) { HashType(rt); SUPERHash(rt.ref.identifier.Val(), rt.typeArguments); if (auto refTarget = rt.ref.target) { SUPERHash(refTarget->rawMangleName); CJC_ASSERT(refTarget->ty); SUPERHash(refTarget->ty->String()); } } template void HashTupleType(const TupleType& tt) { HashType(tt); SUPERHash(tt.fieldTypes); } template void HashVarDecl(const VarDecl& vd) { HashVarDeclAbstract(vd); SUPERHash(vd.isIdentifierCompilerAdd, vd.isResourceVar, vd.isMemberParam); } template void HashGenericParamDecl(const GenericParamDecl& gpd) { HashDecl(gpd); } template void HashPattern(const Pattern& pattern) { HashNode(pattern); } template void HashWildcardPattern(const WildcardPattern& wp) { HashPattern(wp); } template void HashType(const Type& type) { SUPERHash(type.typeParameterName); } template void HashThisType(const ThisType& tt) { HashType(tt); } template void HashOptionalExpr(const OptionalExpr& oe) { HashExpr(oe); SUPERHash(oe.baseExpr); } template void HashOptionalChainExpr(const OptionalChainExpr& oce) { HashExpr(oce); SUPERHash(oce.expr); } template void HashPrimitiveTypeExpr(const PrimitiveTypeExpr& pte) { HashExpr(pte); SUPERHash(static_cast(pte.typeKind)); } template void HashInterpolationExpr(const InterpolationExpr& ie) { HashExpr(ie); SUPERHash(ie.rawString, ie.dollarPos, ie.block); } template void HashStrInterpolationExpr(const StrInterpolationExpr& sie) { HashExpr(sie); SUPERHash(sie.rawString, sie.strParts, sie.strPartExprs); } template void HashJumpExpr(const JumpExpr& je) { HashExpr(je); SUPERHash(je.isBreak); } template void HashWildcardExpr(const WildcardExpr& we) { HashExpr(we); } template void HashInvalidExpr(const InvalidExpr&) { } template void HashIfAvailableExpr(const IfAvailableExpr& expr) { HashExpr(expr); SUPERHash(expr.GetArg(), expr.GetLambda1(), expr.GetLambda2()); } template hash_type Hash(Ptr node) { if (node == nullptr) { return value; } auto kind{static_cast(node->astKind)}; CJC_ASSERT(kind < nodeKind); if constexpr (whatTypeToHash == ALL) { CJC_NULLPTR_CHECK(hashFuncMap[kind]); hashFuncMap[kind](*this, node); } else if constexpr (whatTypeToHash == ONLY_POSITION) { CJC_NULLPTR_CHECK(hashFuncMapOnlyPosition[kind]); hashFuncMapOnlyPosition[kind](*this, node); } else { CJC_NULLPTR_CHECK(hashFuncMapNonPosition[kind]); hashFuncMapNonPosition[kind](*this, node); } return value; } static bool IsConsideredInSignatureHash(const Annotation& anno) { switch (anno.kind) { case AnnotationKind::C: case AnnotationKind::CALLING_CONV: case AnnotationKind::INTRINSIC: case AnnotationKind::JAVA: case AnnotationKind::JAVA_MIRROR: case AnnotationKind::JAVA_IMPL: case AnnotationKind::OBJ_C_MIRROR: case AnnotationKind::OBJ_C_IMPL: case AnnotationKind::FOREIGN_NAME: case AnnotationKind::WHEN: case AnnotationKind::DEPRECATED: return true; case AnnotationKind::ATTRIBUTE: case AnnotationKind::NUMERIC_OVERFLOW: case AnnotationKind::FASTNATIVE: case AnnotationKind::ANNOTATION: case AnnotationKind::CUSTOM: case AnnotationKind::FROZEN: return false; default: CJC_ASSERT(false); return false; } } static bool IsConsideredInBodyHash(const Annotation& anno) { switch (anno.kind) { case AnnotationKind::ATTRIBUTE: return false; case AnnotationKind::NUMERIC_OVERFLOW: case AnnotationKind::FASTNATIVE: case AnnotationKind::ANNOTATION: case AnnotationKind::CUSTOM: case AnnotationKind::DEPRECATED: case AnnotationKind::FROZEN: return true; case AnnotationKind::C: case AnnotationKind::CALLING_CONV: case AnnotationKind::INTRINSIC: case AnnotationKind::JAVA: case AnnotationKind::JAVA_MIRROR: case AnnotationKind::JAVA_IMPL: case AnnotationKind::OBJ_C_MIRROR: case AnnotationKind::OBJ_C_IMPL: case AnnotationKind::FOREIGN_NAME: // if anno.kind == WHEN, anno can only be in MacroDecl case AnnotationKind::WHEN: // annotations combined in body hash are basically the complementary of that combined in mangle name, // except for annotations with arguments, whose arguments are never mangled and therefore must be // computed in body hash return !anno.args.empty(); default: CJC_ASSERT(false); return false; } } void HashAnnotationsDeclBody(const std::vector>& annos) { std::vector> sigAnnos; sigAnnos.reserve(annos.size()); // in case multiple overflow annotation coexist on one function declaration, only the last one effects. // record the index of overflow annotation auto overflowAnnotationIndex = std::numeric_limits::max(); for (size_t i{0}; i < annos.size(); ++i) { const auto& anno{annos[i]}; // Annotation not considered by body hash are either @C or @CallingConv, they should not have arugments // In the case they do have, the arguments are not considered in the mangle name, only their kinds. Add // them to the body hash so their change are noticed and further diangostics are possible if (!IsConsideredInBodyHash(*anno)) { continue; } if (anno->kind == AnnotationKind::NUMERIC_OVERFLOW) { if (overflowAnnotationIndex == std::numeric_limits::max()) { overflowAnnotationIndex = i; } else { sigAnnos[overflowAnnotationIndex] = anno.get(); continue; } } sigAnnos.push_back(anno.get()); } // should there be annotations that cannot be identified by their kind, the declaration order of such is // preserved for performance issue instead of applying a total ordering on these annotations std::stable_sort(sigAnnos.begin(), sigAnnos.end(), [](auto a1, auto a2) { return a1->identifier.Val() < a2->identifier.Val(); }); for (auto anno : sigAnnos) { HashAnnotation(*anno); } } void HashDeclBody(const Decl& decl) { // generic constraint if (decl.generic && decl.astKind != ASTKind::EXTEND_DECL) { HashConstraints(*decl.generic); std::vector>> constraints( decl.generic->genericConstraints.size()); for (size_t i{0}; i < constraints.size(); ++i) { constraints[i] = { decl.generic->genericConstraints[i]->type->ToString(), decl.generic->genericConstraints[i].get()}; } std::stable_sort( constraints.begin(), constraints.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); for (const auto& constraint : constraints) { HashGenericConstraints(*constraint.second); } } HashAnnotationsDeclBody(decl.annotations); } // give types an arbitrary order so that the order in the source code does not matter static std::vector> SortTypes(const std::vector>& upperBounds) { std::vector> uppers(upperBounds.size()); std::transform(upperBounds.begin(), upperBounds.end(), uppers.begin(), std::mem_fn(&OwnedPtr::get)); std::stable_sort(uppers.begin(), uppers.end(), [](auto l, auto r) { return l->ToString() < r->ToString(); }); return uppers; } void HashGenericConstraints(const GenericConstraint& gc) { SUPERHash(gc.type, SortTypes(gc.upperBounds)); } void HashTypeAlias(const TypeAliasDecl& decl) { HashDeclBody(decl); SUPERHash(decl.type); } // incr 2.0 begins here void HashConstraints(const Generic& g) { std::vector>> constraints(g.genericConstraints.size()); for (size_t i{0}; i < constraints.size(); ++i) { constraints[i] = {g.genericConstraints[i]->type->ToString(), g.genericConstraints[i].get()}; } std::stable_sort( constraints.begin(), constraints.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); for (const auto& constraint : constraints) { HashGenericConstraints(*constraint.second); } } // hash specific modifier that has whose kind falls in \p kinds // order of annotations is ignored, but number is kept (i.e. two modifiers of the same kind will be hashed twice) void HashSpecificModifiers(const Decl& decl, std::set&& kinds) { std::vector> mods; mods.reserve(decl.modifiers.size()); bool impliedOpen{false}; bool hasOpen{false}; bool hashVisibilityModifier{false}; for (auto& mod : decl.modifiers) { if (kinds.count(mod.modifier) == 1) { mods.push_back(static_cast>(mod.modifier)); if (mod.modifier == TokenKind::ABSTRACT) { impliedOpen = true; } else if (mod.modifier == TokenKind::OPEN) { hasOpen = true; } else if (mod.modifier == TokenKind::SEALED) { impliedOpen = true; } } if (Utils::In( mod.modifier, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::PRIVATE, TokenKind::INTERNAL})) { hashVisibilityModifier = true; } } // All members in interface needs to add the 'public'. if (kinds.count(TokenKind::PUBLIC) != 0 && decl.outerDecl && decl.outerDecl->astKind == ASTKind::INTERFACE_DECL) { hashVisibilityModifier = true; mods.push_back(static_cast>(TokenKind::PUBLIC)); } // Omitted visibility of toplevel and member decl is 'internal'. if (kinds.count(TokenKind::INTERNAL) != 0 && !hashVisibilityModifier) { mods.push_back(static_cast>(TokenKind::INTERNAL)); } // abstract or sealed implies open, so adds open when abstract or sealed is present but open is not if (kinds.count(TokenKind::OPEN) != 0 && impliedOpen && !hasOpen) { mods.push_back(static_cast>(TokenKind::OPEN)); } std::sort(mods.begin(), mods.end()); for (auto mod : std::as_const(mods)) { CombineHash(mod); } } using TokenType = std::underlying_type_t; // hash open/abstract if decl is virtual void HashVirtual(const Decl& decl) { if (!decl.outerDecl) { return; } if (auto outer = dynamic_cast(decl.outerDecl.get()); !outer || !outer->IsOpen()) { return; } HashSpecificModifiers(decl, {TokenKind::OPEN, TokenKind::ABSTRACT}); } // hash funcbody of a funclet (func/main/primary ctor/macro/lambda decl) void HashBodyOfFuncLike(const FuncBody& funcBody, const std::pair& srcInfo) { for (auto& param : funcBody.paramLists[0]->params) { // only hash identifiers of normal parameters // identifiers of named parameters and variable parameters are part of mangled name if (!param->isNamedParam && !param->hasLetOrVar) { CombineHash(param->identifier.Val()); } if (srcInfo.first || srcInfo.second) { (void)CombineTwoHashes(Hash(param->assignment.get())); } else { (void)CombineTwoHashes(Hash(param->assignment.get())); } // only in debug mode we will need code position info of params if (srcInfo.first) { CombineHash(param->begin); } } if (funcBody.body) { if (srcInfo.first || srcInfo.second) { (void)CombineTwoHashes(Hash(funcBody.body.get())); } else { (void)CombineTwoHashes(Hash(funcBody.body.get())); } } } // hash annotations that contribute to body hash void BodyHashAnnotations(const std::vector>& annos) { std::vector> cons; cons.reserve(annos.size()); Ptr overflowAnnotation{nullptr}; for (auto& anno : annos) { // a numeric overflow annotation override other numeric overflow annotations before the annotated decl, // so only the last annotation of such kind is recorded if (anno->kind == AnnotationKind::NUMERIC_OVERFLOW) { overflowAnnotation = anno.get(); continue; } if (anno->kind != AnnotationKind::ANNOTATION) { cons.push_back(std::ref(*anno)); } } if (overflowAnnotation) { cons.push_back(std::ref(*overflowAnnotation)); } HashAnnotationsNoOrder(cons); } void HashAnnotationsNoOrder(const std::vector> annos) { std::vector hashes(annos.size()); for (size_t i{0}; i < annos.size(); ++i) { ASTHasherImpl p{}; p.HashAnnotation(annos[i]); hashes[i] = p.value; } std::sort(hashes.begin(), hashes.end()); // sort the hash values of annotations instead of sorting the Annotation's, because there are many fields in // an Annotation and it is difficult to define a total ordering on that for (auto h : std::as_const(hashes)) { CombineTwoHashes(h); } } void SrcUseHashAnnotations(const std::vector>& annos) { std::vector> cons; cons.reserve(annos.size()); for (auto& anno : annos) { if (anno->kind == AnnotationKind::ANNOTATION) { cons.push_back(std::ref(*anno)); } } HashAnnotationsNoOrder(cons); } void HashMemberSignature(const Decl& decl) { SUPERHash(decl.identifier.Val()); SUPERHash(decl.generic); if (auto func = DynamicCast(&decl)) { SUPERHash(func->funcBody->paramLists[0]->params); if (func->funcBody->retType) { SUPERHash(func->funcBody->retType); } else { SUPERHash(func->funcBody->body); } } else if (auto prop = DynamicCast(&decl)) { SUPERHash(prop->type); HashSpecificModifiers(decl, {TokenKind::MUT}); } else { // this function is only called on prop/func for now. // should this function be called on var, add the mutablility. otherwise, keep this branch unreachable. CJC_ASSERT(false); } } }; // Not all declaration has Hash...DeclSignature or Hash...DeclBody (or else, etc.) function, this set of templates // serves to create a lambda that wraps the member function with the specific type and a corresponding name with a // lambda that safely cast the Ptr to the member's dynamic type. // Note that if type Node strictly single inherits, it may be assumed that a pointer to the base class has the same // value as the pointer to the derived class of the same object, if the platform uses a type-independent pointer // representation (which most platforms satisfy). However, this behaviour is nowhere specified in the C++ standard // and not adapted here. #define ASTKIND(KIND, VALUE, NODE, SIZE) \ template struct HasHash##NODE : std::false_type { \ }; \ template \ struct HasHash##NODE(&T::template Hash##NODE<0>))>> : std::true_type { \ }; \ template constexpr bool hasHash##NODE##Value = HasHash##NODE::value; \ template constexpr void (T::*GetHash##NODE##Fun())(const NODE&) \ { \ if constexpr (hasHash##NODE##Value) { \ return &T::template Hash##NODE; \ } \ return nullptr; \ } \ template constexpr void (*GetHash##NODE##OrNull())(T&, Ptr) \ { \ if constexpr (hasHash##NODE##Value) { \ return [](T& h, Ptr node) { \ auto fn = GetHash##NODE##Fun(); \ return (h.*fn)(*static_cast(node.get())); \ }; \ } \ return nullptr; \ } #include "cangjie/AST/ASTKind.inc" #undef ASTKIND void (*ASTHasherImpl::hashFuncMap[nodeKind])(ASTHasherImpl&, Ptr) { #define ASTKIND(KIND, VALUE, NODE, SIZE) GetHash##NODE##OrNull(), #include "cangjie/AST/ASTKind.inc" #undef ASTKIND }; void (*ASTHasherImpl::hashFuncMapOnlyPosition[nodeKind])(ASTHasherImpl&, Ptr) { #define ASTKIND(KIND, VALUE, NODE, SIZE) GetHash##NODE##OrNull(), #include "cangjie/AST/ASTKind.inc" #undef ASTKIND }; void (*ASTHasherImpl::hashFuncMapNonPosition[nodeKind])(ASTHasherImpl&, Ptr) { #define ASTKIND(KIND, VALUE, NODE, SIZE) GetHash##NODE##OrNull(), #include "cangjie/AST/ASTKind.inc" #undef ASTKIND }; ASTHasher::hash_type ASTHasher::HashWithPos(Ptr node) { ASTHasherImpl a{}; return a.Hash(node); } ASTHasher::hash_type ASTHasher::HashNoPos(Ptr node) { ASTHasherImpl a{}; return a.Hash(node); } ASTHasher::hash_type ASTHasher::HashSpecs(const Package& pk) { ASTHasherImpl a{}; return a.HashSpecs(pk); } void ASTHasher::Init(const GlobalOptions& op) { g_needLineInfo = op.enableCompileDebug || op.displayLineInfo; if (op.packagePaths.size() > 0) { g_packagePath = op.packagePaths[0]; } g_trimPaths = op.removedPathPrefix; } static std::vector> SortParentTypes(const InheritableDecl& decl) { std::vector> parents{}; size_t beginIndex = decl.astKind == ASTKind::CLASS_DECL ? 1 : 0; for (size_t i{beginIndex}; i < decl.inheritedTypes.size(); ++i) { parents.push_back(decl.inheritedTypes[i].get()); } std::sort(parents.begin(), parents.end(), [](auto a, auto b) { return a->ToString() < b->ToString(); }); if (decl.astKind == ASTKind::CLASS_DECL && !decl.inheritedTypes.empty()) { parents.push_back(decl.inheritedTypes[0].get()); } return parents; } ASTHasher::hash_type ASTHasher::SigHash(const Decl& decl) { ASTHasherImpl a{}; a.HashSpecificModifiers(decl, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::PRIVATE, TokenKind::INTERNAL}); // GetGeneric() applied to constructors returns the generic of the enclosing type, rule it out; // constraints of extend are combined in mangled name if (!IsClassOrEnumConstructor(decl) && decl.astKind != ASTKind::EXTEND_DECL) { if (auto generic = decl.GetGeneric()) { a.CombineHash(generic->typeParameters); a.HashConstraints(*generic); } } if (auto typeAlias = DynamicCast(&decl)) { a.HashTypeAlias(*typeAlias); } if (auto type = DynamicCast(&decl); type && type->astKind != ASTKind::EXTEND_DECL) { for (auto& parent : SortParentTypes(*type)) { (void)a.Hash(parent); } } return a.value; } ASTHasher::hash_type ASTHasher::SrcUseHash(const AST::Decl& decl) { ASTHasherImpl a{}; a.HashSpecificModifiers(decl, {TokenKind::OPEN, TokenKind::ABSTRACT, TokenKind::SEALED, TokenKind::MUT, TokenKind::STATIC, TokenKind::CONST, TokenKind::FOREIGN}); a.SrcUseHashAnnotations(decl.annotations); // FuncParam is for member param in primary ctor. if (decl.astKind == ASTKind::VAR_DECL || decl.astKind == ASTKind::FUNC_PARAM) { auto& var = static_cast(decl); a.CombineHash(var.isVar); a.CombineHash(var.isConst); } if (auto prop = DynamicCast(&decl)) { a.SUPERHash(prop->type); } return a.value; } ASTHasher::hash_type ASTHasher::HashMemberAPIs(std::vector>&& memberAPIs) { ASTHasherImpl hasher{}; std::stable_sort(memberAPIs.begin(), memberAPIs.end(), [](const auto& a, const auto& b) { return a->rawMangleName < b->rawMangleName; }); for (auto memberAPI : memberAPIs) { hasher.HashMemberSignature(*memberAPI); hasher.HashSpecificModifiers(*memberAPI, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::PRIVATE, TokenKind::INTERNAL, TokenKind::MUT, TokenKind::STATIC}); } return hasher.value; } static bool IsVisibleAPI(const Decl& member) { // protected members in extend are not collected as they can never rewrite decls of the extended type nor // particpate in boxing. Otherwise, protected members in class can override supertype open decls without 'open' or // 'override' keyword and therefore have an impact both on its own table and its subclasses, so they are collected. return member.TestAttr(Attribute::PUBLIC) || (member.outerDecl->astKind != ASTKind::EXTEND_DECL && member.TestAttr(Attribute::PROTECTED)); } static std::vector> GetVisibleAPIs(const std::vector>& allMembers) { std::vector> ret; for (auto member : allMembers) { if (auto memberFunc = DynamicCast(member)) { if (memberFunc->TestAnyAttr(Attribute::OPEN, Attribute::ABSTRACT, Attribute::CONSTRUCTOR, Attribute::ENUM_CONSTRUCTOR, Attribute::PRIMARY_CONSTRUCTOR)) { continue; } if (memberFunc->IsFinalizer()) { continue; } if (IsVisibleAPI(*memberFunc)) { ret.emplace_back(memberFunc); } } if (auto memberProp = DynamicCast(member)) { if (memberProp->TestAnyAttr(Attribute::OPEN, Attribute::ABSTRACT)) { continue; } if (IsVisibleAPI(*memberProp)) { ret.emplace_back(memberProp); } } } return ret; } // returns the funcbody if \p decl is a funclet and the body is present static Ptr GetFuncBody(const Decl& decl) { switch (decl.astKind) { case ASTKind::FUNC_DECL: return DynamicCast(&decl)->funcBody.get(); case ASTKind::PRIMARY_CTOR_DECL: return DynamicCast(&decl)->funcBody.get(); case ASTKind::MAIN_DECL: return DynamicCast(&decl)->funcBody.get(); case ASTKind::MACRO_DECL: return DynamicCast(&decl)->funcBody.get(); default: return nullptr; } } ASTHasher::hash_type ASTHasher::BodyHash(const Decl& decl, const std::pair& srcInfo, bool hashAnnos) { ASTHasherImpl a{}; a.HashSpecificModifiers(decl, {TokenKind::OVERRIDE, TokenKind::REDEF, TokenKind::UNSAFE}); if (hashAnnos) { a.BodyHashAnnotations(decl.annotations); } // ===================== Body Hash For Var or Func Decl ===================== // // the constructors in enum don't have body or debug info if (decl.outerDecl && decl.outerDecl->astKind == ASTKind::ENUM_DECL) { if (decl.TestAttr(Attribute::ENUM_CONSTRUCTOR)) { return a.value; } } // only functions and variables may have debug info if (auto var = DynamicCast(&decl)) { // property is just a sugar, we only care the `get` and `set` func inside if (var->astKind == ASTKind::PROP_DECL) { return a.value; } if (var->initializer.get()) { if (srcInfo.first || srcInfo.second) { a.Hash(var->initializer.get()); } else { a.Hash(var->initializer.get()); } } if (var->outerDecl && (var->outerDecl->astKind == ASTKind::STRUCT_DECL || var->outerDecl->astKind == ASTKind::CLASS_DECL)) { return a.value; } if (srcInfo.first || srcInfo.second) { a.CombineHash(decl.begin); } } if (auto funcBody = GetFuncBody(decl)) { a.HashBodyOfFuncLike(*funcBody, srcInfo); } // ===================== Body Hash For Type Decl ===================== // if (auto classDecl = DynamicCast(&decl)) { // Also hash the existense of finalizer cause it is inserted into vtable in CodeGen for (auto member : classDecl->GetMemberDeclPtrs()) { if (auto memberFunc = DynamicCast(member)) { if (memberFunc->IsFinalizer()) { a.HashMemberSignature(*memberFunc); } } } } if (auto extendDecl = DynamicCast(&decl)) { // do not use this function on direct extends cause it needs to merge all same extends // first and calculate the body hash later if (extendDecl->inheritedTypes.empty()) { return a.value; } } if (auto typeDecl = DynamicCast(&decl)) { auto visibleAPIs = GetVisibleAPIs(typeDecl->GetMemberDeclPtrs()); std::stable_sort(visibleAPIs.begin(), visibleAPIs.end(), [](const auto& a, const auto& b) { return a->rawMangleName < b->rawMangleName; }); for (auto visibleAPI : visibleAPIs) { a.HashMemberSignature(*visibleAPI); a.HashSpecificModifiers(*visibleAPI, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::PRIVATE, TokenKind::MUT, TokenKind::STATIC}); } } return a.value; } ASTHasher::hash_type ASTHasher::ImportedDeclBodyHash(const AST::Decl& decl) { auto originalBodyHash = decl.hash.bodyHash; // For source imported decls which has body, it might be semantically changed even if the // source code is not changed. For example, a generic function `A` calls a non-generic function // `B` inside, if the signature of `B` changes, the `A` will be polluted. Yet the Sema doesn't // trace the dependency between `A` and `B` here (cause it is in up-stream pacakge), so we have // to take care of the dependency here. ASTHasherImpl a{}; auto newBodyHash = ASTHasher::BodyHash(decl, {false, false}); a.CombineTwoHashes(originalBodyHash); a.CombineTwoHashes(newBodyHash); return a.value; } ASTHasher::hash_type ASTHasher::VirtualHash(const Decl& decl) { // The virtual hash of interface is handled specially CJC_ASSERT(decl.astKind != ASTKind::INTERFACE_DECL); std::vector> virtuals; for (auto member : decl.GetMemberDeclPtrs()) { if (IncrementalCompilation::IsVirtual(*member)) { virtuals.push_back(member); } } ASTHasherImpl a{}; for (auto member : std::as_const(virtuals)) { a.HashMemberSignature(*member); } return a.value; } } // namespace Cangjie::AST cangjie_compiler-1.0.7/src/Parse/AttachComment.cpp000066400000000000000000000343641510705540100221150ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the attachment of comments to nodes. * * Line Header Comment and the comment on the same line or the next line form a comment group. Other * comment that are connected to the comment of multiple peers form an annotation group. * * The basic principle is to associate the nearest outermost node. The detailed rules are as follows: * For comment group cg, node n: * Rule 1: If cg comes after n and is connected to n in the same line, or is immediately followed by a non-comment, * non-whitespace character with at least one blank line in between, cg is called the trailing comment of n, where n is * the outermost node that satisfies the rule. * Rule 2: If Rule 1 is not satisfied, cg is located within the innermost node n, and the first outermost * node following n is found. cg is called the leading comment of n. If no such node is found, the first outermost node * preceding n on the same level is found, and cg is called the trailing comment of n. If neither can be found, cg is * called the internal comment of ni. */ #include "ParserImpl.h" #include #include "cangjie/AST/Walker.h" using namespace Cangjie; using namespace Cangjie::AST; namespace { // collect ptrs of ast nodes, ignore file, annotation and modifier std::vector> CollectPtrsOfASTNodes(Ptr node) { std::vector> ptrs; auto collectNodes = [&ptrs](Ptr curNode) -> VisitAction { if (curNode->astKind == ASTKind::ANNOTATION || curNode->astKind == ASTKind::MODIFIER) { return VisitAction::SKIP_CHILDREN; } bool ignoreFlag = false; if (curNode->astKind == ASTKind::FILE) { ignoreFlag = true; } else if (auto inv = curNode->GetConstInvocation(); inv && inv->decl) { ignoreFlag = true; } if (ignoreFlag) { return VisitAction::WALK_CHILDREN; } ptrs.push_back(curNode); return VisitAction::WALK_CHILDREN; }; Walker macWalker(node, collectNodes); macWalker.Walk(); return ptrs; } // Sort by begin position in ascending order. If the begins are the same, the node with a larger range is first. void SortNodesByRange(std::vector>& nodes) { auto cmpByRange = [](Ptr a, Ptr b) { if (a->GetBegin() < b->GetBegin()) { return true; } else if (a->GetBegin() == b->GetBegin() && a->GetEnd() > b->GetEnd()) { return true; } return false; }; std::sort(nodes.begin(), nodes.end(), cmpByRange); } void AppendCommentGroup(const Comment& comment, std::vector& cgs) { CommentGroup cg{}; cg.cms.push_back(comment); cgs.push_back(cg); } void AddCommentToBackGroup(const Comment& comment, std::vector& cgs) { CJC_ASSERT(!cgs.empty()); cgs.back().cms.push_back(comment); } void UpdateFollowInfoAndAppendCommentGroup(const std::optional& preTkIdxIgnTrivialStuff, const Comment& comment, std::unordered_map& cgPreInfo, std::vector& commentGroups) { if (preTkIdxIgnTrivialStuff) { cgPreInfo[commentGroups.size()] = *preTkIdxIgnTrivialStuff; } AppendCommentGroup(comment, commentGroups); } CommentKind GetCommentKind(const Token& token) { CJC_ASSERT(token.kind == TokenKind::COMMENT); if (token.Value().rfind("/*", 0) == std::string::npos) { return CommentKind::LINE; } if (token.Value().rfind("/**", 0) == std::string::npos) { return CommentKind::BLOCK; } if (token.Value().rfind("/***", 0) != std::string::npos) { return CommentKind::BLOCK; } if (token.Value().rfind("/**/", 0) != std::string::npos) { return CommentKind::BLOCK; } return CommentKind::DOCUMENT; } // if it appends a commentGroup return true bool UpdateCommentGroups(const Token& tk, const Token& preTokenIgnoreNL, const std::optional& preTkIdxIgnTrivialStuff, std::vector& commentGroups, std::unordered_map& cgPreInfo) { CommentKind commentKind = GetCommentKind(tk); int diffLine = tk.Begin().line - preTokenIgnoreNL.Begin().line; if (preTokenIgnoreNL.kind == TokenKind::COMMENT) { if (diffLine == 0) { AddCommentToBackGroup({CommentStyle::OTHER, commentKind, tk}, commentGroups); } else if (diffLine == 1) { if (commentGroups.back().cms.front().style == CommentStyle::LEAD_LINE) { AddCommentToBackGroup({CommentStyle::LEAD_LINE, commentKind, tk}, commentGroups); } else { UpdateFollowInfoAndAppendCommentGroup( preTkIdxIgnTrivialStuff, {CommentStyle::LEAD_LINE, commentKind, tk}, cgPreInfo, commentGroups); return true; } } else { UpdateFollowInfoAndAppendCommentGroup( preTkIdxIgnTrivialStuff, {CommentStyle::LEAD_LINE, commentKind, tk}, cgPreInfo, commentGroups); return true; } } else { CommentStyle commentStyle = diffLine == 0 ? CommentStyle::TRAIL_CODE : CommentStyle::LEAD_LINE; UpdateFollowInfoAndAppendCommentGroup( preTkIdxIgnTrivialStuff, {commentStyle, commentKind, tk}, cgPreInfo, commentGroups); return true; } return false; } bool IsTrailCommentsInRuleOne(const CommentGroup cg, size_t cgIdx, Ptr node, std::unordered_map& cgFollowInfo, const std::vector& tkStream) { CJC_ASSERT(!cg.cms.empty()); int diffline = cg.cms[0].info.Begin().line - node->GetEnd().line; if (diffline == 0) { return true; } if (diffline == 1) { if (cgFollowInfo.find(cgIdx) != cgFollowInfo.end()) { int blankLine = tkStream[cgFollowInfo[cgIdx]].Begin().line - cg.cms.back().info.End().line; if (blankLine > 1) { return true; } } else { // cg followed by another cg(at least one blank line) or nothing return true; } } return false; } std::pair WhetherExistNextNodeBeforeOuterNodeEnd( const std::vector>& nodes, size_t offsetIdx, Ptr curNode, const std::stack& nodeStack) { bool findFlag{false}; CJC_ASSERT(!nodes.empty()); auto searchEnd = nodes.back()->GetEnd(); if (!nodeStack.empty()) { searchEnd = nodes[nodeStack.top()]->GetEnd(); } for (size_t n = offsetIdx + 1; n < nodes.size(); n++) { if (nodes[n]->GetBegin() > searchEnd) { // out range, stop search break; } if (nodes[n]->GetBegin() > curNode->GetEnd() && nodes[n]->GetBegin() < searchEnd) { findFlag = true; break; } } return {findFlag, searchEnd}; } size_t AttchCommentToAheadNode( Ptr node, const Position& searchEnd, std::vector& commentGroups, size_t cgIdx) { node->comments.trailingComments.push_back(commentGroups[cgIdx]); while (cgIdx + 1 < commentGroups.size()) { CJC_ASSERT(!commentGroups[cgIdx + 1].cms.empty()); if (commentGroups[cgIdx + 1].cms[0].info.Begin() >= searchEnd) { break; } ++cgIdx; node->comments.trailingComments.push_back(commentGroups[cgIdx]); } return cgIdx; } // return the index of the next comment group needs to be attched size_t AttchCommentToOuterNode(const std::vector>& nodes, size_t nodeOffsetIdx, CommentGroupsLocInfo& cgInfo, size_t cgIdx, std::stack& nodeStack) { CJC_ASSERT(!nodes.empty()); CJC_ASSERT(!nodeStack.empty()); auto outerNode = nodes[nodeStack.top()]; nodeStack.pop(); while (!nodeStack.empty() && outerNode->GetEnd() == nodes[nodeStack.top()]->GetEnd()) { outerNode = nodes[nodeStack.top()]; nodeStack.pop(); } for (; cgIdx < cgInfo.commentGroups.size(); ++cgIdx) { CJC_ASSERT(cgInfo.cgPreInfo.find(cgIdx) != cgInfo.cgPreInfo.end()); if (outerNode->GetEnd() <= cgInfo.tkStream[cgInfo.cgPreInfo[cgIdx]].Begin()) { return cgIdx; } CJC_ASSERT(cgIdx < cgInfo.commentGroups.size()); if (!IsTrailCommentsInRuleOne( cgInfo.commentGroups[cgIdx], cgIdx, outerNode, cgInfo.cgFollowInfo, cgInfo.tkStream)) { break; } outerNode->comments.trailingComments.push_back(cgInfo.commentGroups[cgIdx]); } if (cgIdx >= cgInfo.commentGroups.size()) { return cgIdx; } auto [findFlag, searchEnd] = WhetherExistNextNodeBeforeOuterNodeEnd(nodes, nodeOffsetIdx, outerNode, nodeStack); if (!findFlag) { cgIdx = AttchCommentToAheadNode(outerNode, searchEnd, cgInfo.commentGroups, cgIdx); ++cgIdx; } return cgIdx; } /** * Attach comment groups to node * The control flow jump behavior of this loop: * If the comment is before the node, continue. * If both the comment and the next node are within range of the node, push the node to the stack and break. * If only the comment are within range of the node, continue. * If the comment is beyond the current outer node, attch comment to outer node and break * If the comment and the node are not closely connected then break. * If rule 1 is satisfied, continue. * If the comment is beyond the current node and rule 1 is not satisfied and there is a next node in the range of * the outer node then break. * If the comment is beyond the current node and rule 1 is not satisfied and there is no next node in the range of * the outer node then attach the subsequent comments in the range of the outer node and continue * @return the index of the next comment group needs to be attched */ size_t AttachCommentToNode(const std::vector>& nodes, size_t curNodeIdx, CommentGroupsLocInfo& cgInfo, size_t cgIdx, std::stack& nodeStack) { for (; cgIdx < cgInfo.commentGroups.size(); ++cgIdx) { auto& curCg = cgInfo.commentGroups[cgIdx]; auto& curCgBegin = curCg.cms[0].info.Begin(); const auto& curNodeBegin = nodes[curNodeIdx]->GetBegin(); const auto& curNodeEnd = nodes[curNodeIdx]->GetEnd(); CJC_ASSERT(!curCg.cms.empty()); CJC_ASSERT(curCgBegin != curNodeBegin); if (curCgBegin < curNodeBegin) { nodes[curNodeIdx]->comments.leadingComments.push_back(curCg); // rule2 } else if (curCgBegin < curNodeEnd) { if (curNodeIdx + 1 < nodes.size() && nodes[curNodeIdx + 1]->GetBegin() < curNodeEnd) { nodeStack.push(curNodeIdx); break; } nodes[curNodeIdx]->comments.innerComments.push_back(curCg); // rule2 } else { if (!nodeStack.empty() && nodes[nodeStack.top()]->GetEnd() < curCgBegin) { cgIdx = AttchCommentToOuterNode(nodes, curNodeIdx, cgInfo, cgIdx, nodeStack); break; } if (cgInfo.cgPreInfo.find(cgIdx) == cgInfo.cgPreInfo.end()) { break; // bad node location } if (curNodeEnd <= cgInfo.tkStream[cgInfo.cgPreInfo[cgIdx]].Begin()) { break; } if (IsTrailCommentsInRuleOne(curCg, cgIdx, nodes[curNodeIdx], cgInfo.cgFollowInfo, cgInfo.tkStream)) { nodes[curNodeIdx]->comments.trailingComments.push_back(curCg); continue; } // check to see if there is a next node before the end of top node in stack auto [findNextFlag, searchEnd] = WhetherExistNextNodeBeforeOuterNodeEnd(nodes, curNodeIdx, nodes[curNodeIdx], nodeStack); if (findNextFlag) { break; } if (!findNextFlag) { cgIdx = AttchCommentToAheadNode(nodes[curNodeIdx], searchEnd, cgInfo.commentGroups, cgIdx); // rule2 } } } return cgIdx; } } // namespace void ParserImpl::CollectCommentGroups(CommentGroupsLocInfo& cgInfo) const { Token preTokenIgnoreNL{TokenKind::SENTINEL, "", Position(0, 1, 1), Position{0, 1, 1}}; bool needUpdateFollowInfo = false; std::optional preTkIdxIgnTrivialStuff{std::nullopt}; // ignore NL, Semi, Comment for (size_t i = 0; i < cgInfo.tkStream.size(); ++i) { auto& tk = cgInfo.tkStream[i]; if (tk.kind == TokenKind::NL || tk.commentForMacroDebug) { continue; } if (tk.kind != TokenKind::COMMENT) { if (tk.kind != TokenKind::SEMI) { preTkIdxIgnTrivialStuff = i; } preTokenIgnoreNL = tk; if (needUpdateFollowInfo && tk.kind != TokenKind::END) { CJC_ASSERT(cgInfo.commentGroups.size() > 0); // it won't run here if it followed by a cg or nothing cgInfo.cgFollowInfo[cgInfo.commentGroups.size() - 1] = i; needUpdateFollowInfo = false; } continue; } if (UpdateCommentGroups( tk, preTokenIgnoreNL, preTkIdxIgnTrivialStuff, cgInfo.commentGroups, cgInfo.cgPreInfo)) { needUpdateFollowInfo = true; } preTokenIgnoreNL = tk; } } void ParserImpl::AttachCommentToSortedNodes(std::vector>& nodes) { CommentGroupsLocInfo cgInfo{{}, {}, {}, lexer->GetTokenStream()}; CollectCommentGroups(cgInfo); if (cgInfo.commentGroups.empty()) { return; } size_t cgIdx{0}; std::stack nodeStack; for (size_t i = 0; i < nodes.size() && cgIdx < cgInfo.commentGroups.size(); ++i) { if (nodes[i]->GetBegin().line < 1 || nodes[i]->GetBegin().column < 1) { // bad node pos continue; } cgIdx = AttachCommentToNode(nodes, i, cgInfo, cgIdx, nodeStack); } } void ParserImpl::AttachCommentToNodes(std::vector>& nodes) { std::vector> nps; nps.reserve(nodes.size()); for (const auto& n : nodes) { nps.emplace_back(n.get()); } SortNodesByRange(nps); AttachCommentToSortedNodes(nps); } void ParserImpl::AttachCommentToFile(Ptr node) { auto nodes = CollectPtrsOfASTNodes(node); if (nodes.empty()) { return; } AttachCommentToSortedNodes(nodes); }cangjie_compiler-1.0.7/src/Parse/CMakeLists.txt000066400000000000000000000020731510705540100214120ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB PARSE_SRC *.cpp) set(PARSE_FFI_SRC AttachComment.cpp ParseAnnotations.cpp ParseAtom.cpp ParseCJMPDecl.cpp ParseDecl.cpp ParseExpr.cpp ParseImports.cpp ParseFeatures.cpp ParseMacro.cpp ParseModifiers.cpp ParsePattern.cpp ParseQuote.cpp ParseType.cpp Parser.cpp ParserDiag.cpp ParserImpl.cpp ParserModifierRules.cpp ParserUtils.cpp ) add_subdirectory(NativeFFI) if(NOT CMAKE_ENABLE_ASSERT AND NOT CMAKE_BUILD_TYPE MATCHES Debug) get_filename_component(ASTChecker ${CMAKE_CURRENT_SOURCE_DIR}/ASTChecker.cpp ABSOLUTE) list(REMOVE_ITEM PARSE_SRC ${ASTChecker}) endif() add_library(CangjieParse OBJECT ${PARSE_SRC}) add_library(CangjieParseFFI OBJECT ${PARSE_FFI_SRC}) target_compile_options(CangjieParse PRIVATE ${CJC_EXTRA_WARNINGS}) cangjie_compiler-1.0.7/src/Parse/MPParserImpl.h000066400000000000000000000033531510705540100213400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares class MPParserImpl. */ #ifndef CANGJIE_PARSE_MPPARSERIMPL_H #define CANGJIE_PARSE_MPPARSERIMPL_H #include "cangjie/Parse/Parser.h" namespace Cangjie { using namespace AST; class MPParserImpl final { public: explicit MPParserImpl(const ParserImpl& parserImpl): ref(&parserImpl) { } ~MPParserImpl() = default; // set compile options for cjmp. void SetCompileOptions(const GlobalOptions& opts); // Check CJMP modifier rules. bool CheckCJMPModifiers(const std::set &modifiers) const; // The entry of checking CJMP decl rules. void CheckCJMPDecl(AST::Decl& decl) const; // Check whether the given modifier is a CJMP modifier (COMMON or PLATFORM). bool HasCJMPModifiers(const AST::Modifier& modifier) const; private: bool CheckCJMPModifiersOf(const AST::Decl& decl) const; bool CheckCJMPModifiersBetween(const AST::Decl& inner, const AST::Decl& outer) const; void CheckCJMPFuncParams(AST::Decl& decl, const Ptr funcBody) const; void CheckPlatformInterface(const AST::InterfaceDecl& decl) const; // Diag report void DiagOuterDeclMissMatch(const AST::Node& node, const std::string& p0, const std::string& p1, const std::string& p2, const std::string& p3) const; private: const ParserImpl* ref; bool compileCommon{false}; // true if compiling common part bool compilePlatform{false}; // true if compiling platform part }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Parse/NativeFFI/000077500000000000000000000000001510705540100204235ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Parse/NativeFFI/CMakeLists.txt000066400000000000000000000007351510705540100231700ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB PARSE_NATIVE_FFI_SRC *.cpp) add_subdirectory(Java) add_subdirectory(ObjC) set(PARSE_SRC ${PARSE_SRC} ${PARSE_NATIVE_FFI_SRC} PARENT_SCOPE) set(PARSE_FFI_SRC ${PARSE_FFI_SRC} ${PARSE_NATIVE_FFI_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Parse/NativeFFI/FFIParserImpl.cpp000066400000000000000000000124501510705540100235340ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file contains logics related to Cangjie Native interoperation with Java, Objective-C */ #include "../ParserImpl.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" #include "FFIParserImpl.h" namespace Cangjie { using namespace AST; namespace { bool IsLitString(Ptr expr) { if (!expr || expr->astKind != ASTKind::LIT_CONST_EXPR) { return false; } auto lce = DynamicCast(expr); if (!lce) { return false; } if (lce->kind != LitConstKind::STRING) { return false; } return true; } } // namespace void DiagConflictingAnnos(DiagnosticEngine &diag, Annotation &first, Annotation &second) { auto minPos = first.begin < second.begin ? first.begin : second.begin; auto maxPos = first.end > second.end ? first.end : second.end; diag.DiagnoseRefactor( DiagKindRefactor::parse_conflict_annotation, MakeRange(minPos, maxPos), first.identifier, second.identifier); } void FFIParserImpl::CheckAnnotationsConflict(const PtrVector& annos) const { std::vector> candidates; for (auto& it : annos) { if (CONFLICTED_FFI_ANNOS.find(it->kind) != CONFLICTED_FFI_ANNOS.end()) { candidates.push_back(it.get()); } } if (candidates.size() < 2) { return; } // report diag only in relation with first annotation auto first = *candidates.begin(); for (auto conflicted = std::next(candidates.begin()); conflicted != candidates.end(); ++conflicted) { DiagConflictingAnnos(p.diag, *first, **conflicted); } } void FFIParserImpl::CheckAnnotations(const PtrVector& annos) const { for (auto& it : annos) { auto& anno = *it; switch (it->kind) { case AnnotationKind::JAVA_MIRROR: case AnnotationKind::JAVA_IMPL: { jp.CheckAnnotation(anno); break; } case AnnotationKind::FOREIGN_NAME: { CheckForeignNameAnnoArgs(anno); CheckForeignNameAnnoTarget(anno); break; } case AnnotationKind::OBJ_C_MIRROR: case AnnotationKind::OBJ_C_IMPL: { op.CheckAnnotation(anno); break; } default: continue; } } CheckAnnotationsConflict(annos); } void FFIParserImpl::CheckForeignNameAnnoTarget(const Annotation& anno) const { if (p.SeeingAny({TokenKind::FUNC, TokenKind::PROP, TokenKind::INIT, TokenKind::LET, TokenKind::VAR, TokenKind::CONST})) { return; } if (anno.kind == AnnotationKind::FOREIGN_NAME) { auto& lah = p.lookahead; p.DiagUnexpectedAnnoOn(anno, lah.Begin(), anno.identifier, lah.Value()); } } void FFIParserImpl::CheckForeignNameAnnoArgs(const Annotation& anno) const { static const std::string FOREIGN_NAME_NAME = "@ForeignName"; if (anno.args.size() != 1 || !IsLitString(anno.args[0]->expr)) { p.DiagAnnotationExpectsOneArgument(anno, FOREIGN_NAME_NAME, "'String' literal"); return; } } void FFIParserImpl::CheckForeignNameAnnotation(Decl& decl) const { CJC_ASSERT(decl.IsFuncOrProp() || decl.astKind == ASTKind::VAR_DECL); for (auto& it : decl.annotations) { if (it->kind != AnnotationKind::FOREIGN_NAME) { continue; } if (!decl.outerDecl || !decl.outerDecl->TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE, Attribute::OBJ_C_MIRROR, Attribute::OBJ_C_MIRROR_SUBTYPE)) { p.diag.DiagnoseRefactor(DiagKindRefactor::parse_foreign_name_on_ffi_decl_member, decl); decl.EnableAttr(Attribute::IS_BROKEN); return; } } } JFFIParserImpl FFIParserImpl::Java() const { return jp; } OCFFIParserImpl FFIParserImpl::ObjC() const { return op; } void FFIParserImpl::CheckZeroOrSingleStringLitArgAnnotation(const AST::Annotation &anno, const std::string &annotationName) const { if (anno.args.size() == 0) { return; } if (anno.args.size() > 1 || !IsLitString(anno.args[0]->expr)) { p.DiagAnnotationMoreThanOneArgs(anno, annotationName, "'String' literal"); return; } } void FFIParserImpl::CheckClassLikeSignature(AST::ClassLikeDecl& decl, const PtrVector& annos) const { for (auto& anno : annos) { switch (anno->kind) { case AnnotationKind::JAVA_MIRROR: jp.CheckMirrorSignature(decl, annos); break; case AnnotationKind::JAVA_IMPL: jp.CheckImplSignature(decl, annos); break; case AnnotationKind::OBJ_C_MIRROR: op.CheckMirrorSignature(decl, annos); break; case AnnotationKind::OBJ_C_IMPL: op.CheckImplSignature(decl, annos); break; default: break; } } } namespace Native::FFI { } // namespace Native::FFI } // namespace Cangjie cangjie_compiler-1.0.7/src/Parse/NativeFFI/FFIParserImpl.h000066400000000000000000000036541510705540100232070ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares parser enhancement for Cangjie Native FFI with Java / Objective-C */ #ifndef CANGJIE_PARSE_NATIVEFFIPARSERIMPL_H #define CANGJIE_PARSE_NATIVEFFIPARSERIMPL_H #include "cangjie/Parse/Parser.h" #include "Java/JFFIParserImpl.h" #include "ObjC/OCFFIParserImpl.h" namespace Cangjie { using namespace AST; class FFIParserImpl final { public: explicit FFIParserImpl(ParserImpl& parserImpl): p(parserImpl), jp(parserImpl), op(parserImpl) { } ~FFIParserImpl() = default; void CheckAnnotationsConflict(const PtrVector& annos) const; void CheckAnnotations(const PtrVector& annos) const; void CheckForeignNameAnnotation(Decl& decl) const; void CheckZeroOrSingleStringLitArgAnnotation(const AST::Annotation &anno, const std::string &annotationName) const; void CheckClassLikeSignature(AST::ClassLikeDecl& decl, const PtrVector& annos) const; JFFIParserImpl Java() const; OCFFIParserImpl ObjC() const; private: // friend JFFIParserImpl; void CheckForeignNameAnnoArgs(const Annotation& anno) const; void CheckForeignNameAnnoTarget(const Annotation& anno) const; ParserImpl& p; JFFIParserImpl jp; OCFFIParserImpl op; }; void DiagConflictingAnnos(DiagnosticEngine& diag, Annotation& first, Annotation& second); const std::unordered_set CONFLICTED_FFI_ANNOS { AnnotationKind::JAVA_MIRROR, AnnotationKind::JAVA_IMPL, AnnotationKind::OBJ_C_IMPL, AnnotationKind::OBJ_C_MIRROR, AnnotationKind::C, AnnotationKind::JAVA }; namespace Native::FFI { } // namespace Native::FFI } // namespace Cangjie #endif // CANGJIE_PARSE_NATIVEFFIPARSERIMPL_H cangjie_compiler-1.0.7/src/Parse/NativeFFI/Java/000077500000000000000000000000001510705540100213045ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Parse/NativeFFI/Java/CMakeLists.txt000066400000000000000000000006071510705540100240470ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB PARSE_NATIVE_FFI_JAVA_SRC *.cpp) set(PARSE_NATIVE_FFI_SRC ${PARSE_NATIVE_FFI_SRC} ${PARSE_NATIVE_FFI_JAVA_SRC} PARENT_SCOPE) cangjie_compiler-1.0.7/src/Parse/NativeFFI/Java/JFFIParserImpl.cpp000066400000000000000000000066511510705540100245350ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements logics related to parsing in java FFI in Cangjie Native */ #include "../../ParserImpl.h" #include "cangjie/AST/Utils.h" #include "JFFIParserImpl.h" using namespace Cangjie; using namespace AST; void JFFIParserImpl::CheckAnnotation(const Annotation& anno) const { if (anno.kind == AnnotationKind::JAVA_MIRROR) { CheckMirrorAnnoArgs(anno); CheckMirrorAnnoTarget(anno); } else { CJC_ASSERT(anno.kind == AnnotationKind::JAVA_IMPL); CheckImplAnnoArgs(anno); CheckImplAnnoTarget(anno); } } void JFFIParserImpl::CheckMirrorAnnoTarget(const Annotation& anno) const { if (p.SeeingAny({TokenKind::CLASS, TokenKind::INTERFACE})) { return; } if (anno.kind == AnnotationKind::JAVA_MIRROR) { auto& lah = p.lookahead; p.DiagUnexpectedAnnoOn(anno, lah.Begin(), anno.identifier, lah.Value()); } } void JFFIParserImpl::CheckImplAnnoTarget(const Annotation& anno) const { if (p.SeeingAny({TokenKind::CLASS, TokenKind::INTERFACE})) { return; } if (anno.kind == AnnotationKind::JAVA_IMPL) { auto& lah = p.lookahead; p.DiagUnexpectedAnnoOn(anno, lah.Begin(), anno.identifier, lah.Value()); } } void JFFIParserImpl::CheckMirrorAnnoArgs(const Annotation& anno) const { static const std::string JAVA_MIRROR_NAME = "@JavaMirror"; p.ffiParser->CheckZeroOrSingleStringLitArgAnnotation(anno, JAVA_MIRROR_NAME); } void JFFIParserImpl::CheckImplAnnoArgs(const Annotation& anno) const { static const std::string JAVA_IMPL_NAME = "@JavaImpl"; p.ffiParser->CheckZeroOrSingleStringLitArgAnnotation(anno, JAVA_IMPL_NAME); } void JFFIParserImpl::CheckMirrorSignature(AST::ClassLikeDecl& decl, const PtrVector& annos) const { CJC_ASSERT(p.HasAnnotation(annos, AnnotationKind::JAVA_MIRROR)); decl.EnableAttr(Attribute::JAVA_MIRROR); if (!decl.inheritedTypes.empty()) { decl.EnableAttr(Attribute::JAVA_MIRROR_SUBTYPE); } if (decl.TestAttr(Attribute::SEALED)) { DiagJavaMirrorCannotBeSealed(decl); decl.EnableAttr(Attribute::IS_BROKEN); } } void JFFIParserImpl::CheckImplSignature(AST::ClassLikeDecl& decl, const PtrVector& annos) const { CJC_ASSERT(p.HasAnnotation(annos, AnnotationKind::JAVA_IMPL)); decl.EnableAttr(Attribute::JAVA_MIRROR_SUBTYPE); if (decl.GetGeneric() != nullptr) { DiagJavaImplCannotBeGeneric(decl); decl.EnableAttr(Attribute::IS_BROKEN); } if (decl.TestAttr(Attribute::ABSTRACT)) { DiagJavaImplCannotBeAbstract(decl); decl.EnableAttr(Attribute::IS_BROKEN); } if (decl.TestAttr(Attribute::SEALED)) { DiagJavaImplCannotBeSealed(decl); decl.EnableAttr(Attribute::IS_BROKEN); } if (decl.astKind == ASTKind::CLASS_DECL && decl.TestAttr(Attribute::OPEN)) { p.diag.DiagnoseRefactor(DiagKindRefactor::parse_java_impl_cannot_be_open, decl); decl.EnableAttr(Attribute::IS_BROKEN); } else if (decl.astKind == ASTKind::INTERFACE_DECL) { p.diag.DiagnoseRefactor(DiagKindRefactor::parse_java_impl_cannot_be_interface, decl); decl.EnableAttr(Attribute::IS_BROKEN); } }cangjie_compiler-1.0.7/src/Parse/NativeFFI/Java/JFFIParserImpl.h000066400000000000000000000042031510705540100241710ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares class NativeFFIJavaParserImpl */ #ifndef CANGJIE_PARSE_NATIVEFFIJAVAPARSERIMPL_H #define CANGJIE_PARSE_NATIVEFFIJAVAPARSERIMPL_H #include "cangjie/Parse/Parser.h" namespace Cangjie { using namespace AST; class JFFIParserImpl final { public: explicit JFFIParserImpl(ParserImpl& parserImpl): p(parserImpl) { } ~JFFIParserImpl() = default; void CheckAnnotation(const Annotation& anno) const; void CheckMirrorSignature(AST::ClassLikeDecl& decl, const PtrVector& annos) const; void CheckImplSignature(AST::ClassLikeDecl& decl, const PtrVector& annos) const; void DiagJavaMirrorCannotHaveFinalizer(const AST::Node& node) const; void DiagJavaMirrorCannotHavePrivateMember(const AST::Node& node) const; void DiagJavaMirrorCannotHaveStaticInit(const AST::Node& node) const; void DiagJavaMirrorCannotHaveConstMember(const AST::Node& node) const; void DiagJavaImplCannotBeGeneric(const AST::Node& node) const; void DiagJavaImplCannotBeAbstract(const AST::Node& node) const; void DiagJavaImplCannotBeSealed(const AST::Node& node) const; void DiagJavaMirrorCannotBeSealed(const AST::Node& node) const; void DiagJavaImplCannotHaveStaticInit(const AST::Node& node) const; private: void CheckMirrorAnnoArgs(const Annotation& anno) const; void CheckImplAnnoArgs(const Annotation& anno) const; void CheckMirrorAnnoTarget(const Annotation& anno) const; void CheckImplAnnoTarget(const Annotation& anno) const; // Diag report void DiagOuterDeclMissMatch(const AST::Node& node, const std::string& p0, const std::string& p1, const std::string& p2, const std::string& p3) const; private: ParserImpl& p; bool compileCommon{false}; // true if compiling common part bool compilePlatform{false}; // true if compiling platform part }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Parse/NativeFFI/Java/ParserDiag.cpp000066400000000000000000000040431510705540100240320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements parser-related diagnostics for cangjie-native java FFI */ #include "../../ParserImpl.h" #include "JFFIParserImpl.h" using namespace Cangjie; using namespace AST; void JFFIParserImpl::DiagJavaMirrorCannotHaveFinalizer(const AST::Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_java_mirror_cannot_have_finalizer, node); } void JFFIParserImpl::DiagJavaMirrorCannotHavePrivateMember(const AST::Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_java_mirror_cannot_have_private_member, node); } void JFFIParserImpl::DiagJavaMirrorCannotHaveStaticInit(const AST::Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_java_mirror_cannot_have_static_init, node); } void JFFIParserImpl::DiagJavaMirrorCannotHaveConstMember(const AST::Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_java_mirror_cannot_have_const_member, node); } void JFFIParserImpl::DiagJavaImplCannotBeGeneric(const AST::Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_java_impl_cannot_be_generic, node); } void JFFIParserImpl::DiagJavaImplCannotBeAbstract(const AST::Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_java_impl_cannot_be_abstract, node); } void JFFIParserImpl::DiagJavaImplCannotBeSealed(const AST::Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_java_impl_cannot_be_sealed, node); } void JFFIParserImpl::DiagJavaMirrorCannotBeSealed(const AST::Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_java_mirror_cannot_be_sealed, node); } void JFFIParserImpl::DiagJavaImplCannotHaveStaticInit(const AST::Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_java_impl_cannot_have_static_init, node); } cangjie_compiler-1.0.7/src/Parse/NativeFFI/ObjC/000077500000000000000000000000001510705540100212405ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Parse/NativeFFI/ObjC/CMakeLists.txt000066400000000000000000000006061510705540100240020ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB PARSE_NATIVE_FFI_OBJC_SRC *.cpp) set(PARSE_NATIVE_FFI_SRC ${PARSE_NATIVE_FFI_SRC} ${PARSE_NATIVE_FFI_OBJC_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Parse/NativeFFI/ObjC/OCFFIParserImpl.cpp000066400000000000000000000063561510705540100246030ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements logics related to parsing in Objective-C FFI in Cangjie Native */ #include "OCFFIParserImpl.h" #include "../../ParserImpl.h" #include "cangjie/AST/Utils.h" using namespace Cangjie; using namespace AST; void OCFFIParserImpl::CheckAnnotation(const Annotation& anno) const { if (anno.kind == AnnotationKind::OBJ_C_MIRROR) { CheckMirrorAnnoArgs(anno); CheckMirrorAnnoTarget(anno); } else { CJC_ASSERT(anno.kind == AnnotationKind::OBJ_C_IMPL); CheckImplAnnoArgs(anno); CheckImplAnnoTarget(anno); } } void OCFFIParserImpl::CheckMirrorSignature(ClassLikeDecl& decl, const PtrVector& annos) const { CJC_ASSERT(p.HasAnnotation(annos, AnnotationKind::OBJ_C_MIRROR)); decl.EnableAttr(Attribute::OBJ_C_MIRROR); if (decl.TestAttr(Attribute::SEALED)) { DiagObjCMirrorCannotBeSealed(decl); decl.EnableAttr(Attribute::IS_BROKEN); } } void OCFFIParserImpl::CheckImplSignature(ClassLikeDecl& decl, const PtrVector& annos) const { CJC_ASSERT(p.HasAnnotation(annos, AnnotationKind::OBJ_C_IMPL)); decl.EnableAttr(Attribute::OBJ_C_MIRROR_SUBTYPE); if (decl.GetGeneric() != nullptr) { DiagObjCImplCannotBeGeneric(decl); decl.EnableAttr(Attribute::IS_BROKEN); } if (decl.TestAttr(Attribute::ABSTRACT)) { DiagObjCImplCannotBeAbstract(decl); decl.EnableAttr(Attribute::IS_BROKEN); } if (decl.TestAttr(Attribute::SEALED)) { DiagObjCImplCannotBeSealed(decl); decl.EnableAttr(Attribute::IS_BROKEN); } if (decl.astKind == ASTKind::CLASS_DECL && decl.TestAttr(Attribute::OPEN)) { DiagObjCImplCannotBeOpen(decl); decl.EnableAttr(Attribute::IS_BROKEN); } if (decl.astKind == ASTKind::INTERFACE_DECL) { DiagObjCImplCannotBeInterface(decl); decl.EnableAttr(Attribute::IS_BROKEN); } } void OCFFIParserImpl::CheckMirrorAnnoArgs(const Annotation& anno) const { static const std::string OBJC_MIRROR_NAME = "@ObjCMirror"; p.ffiParser->CheckZeroOrSingleStringLitArgAnnotation(anno, OBJC_MIRROR_NAME); } void OCFFIParserImpl::CheckImplAnnoArgs(const Annotation& anno) const { static const std::string OBJC_IMPL_NAME = "@ObjCImpl"; p.ffiParser->CheckZeroOrSingleStringLitArgAnnotation(anno, OBJC_IMPL_NAME); } void OCFFIParserImpl::CheckMirrorAnnoTarget(const Annotation& anno) const { if (p.SeeingAny({TokenKind::CLASS, TokenKind::INTERFACE})) { return; } if (anno.kind == AnnotationKind::OBJ_C_MIRROR) { auto& lah = p.lookahead; p.DiagUnexpectedAnnoOn(anno, lah.Begin(), anno.identifier, lah.Value()); } } void OCFFIParserImpl::CheckImplAnnoTarget(const Annotation& anno) const { if (p.SeeingAny({TokenKind::CLASS, TokenKind::INTERFACE})) { return; } if (anno.kind == AnnotationKind::OBJ_C_IMPL) { auto& lah = p.lookahead; p.DiagUnexpectedAnnoOn(anno, lah.Begin(), anno.identifier, lah.Value()); } }cangjie_compiler-1.0.7/src/Parse/NativeFFI/ObjC/OCFFIParserImpl.h000066400000000000000000000046211510705540100242410ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares class NativeFFIObjCParserImpl. */ #ifndef CANGJIE_PARSE_NATIVEFFIOBJCPARSERIMPL_H #define CANGJIE_PARSE_NATIVEFFIOBJCPARSERIMPL_H #include "cangjie/Parse/Parser.h" namespace Cangjie { class OCFFIParserImpl final { public: explicit OCFFIParserImpl(ParserImpl& parserImpl) : p(parserImpl) { } ~OCFFIParserImpl() = default; void CheckAnnotation(const AST::Annotation& anno) const; void CheckMirrorSignature(AST::ClassLikeDecl& decl, const PtrVector& annos) const; void CheckImplSignature(AST::ClassLikeDecl& decl, const PtrVector& annos) const; // Make private, when checking @ObjCMirror/@ObjCImpl decl members moved to this class void DiagObjCMirrorCannotHaveFinalizer(const AST::Node& node) const; void DiagObjCMirrorMemberMustHaveForeignName(const AST::Node& node) const; void DiagObjCMirrorCannotHavePrivateMember(const AST::Node& node) const; void DiagObjCMirrorCannotHaveConstMember(const AST::Node& node) const; void DiagObjCMirrorCannotHaveStaticInit(const AST::Node& node) const; void DiagObjCMirrorFieldCannotHaveInitializer(const AST::Node& node) const; void DiagObjCMirrorCannotHavePrimaryCtor(const AST::Node& node) const; void DiagObjCMirrorFieldCannotBeStatic(const AST::Node& node) const; void DiagObjCImplCannotHaveStaticInit(const AST::Node& node) const; void DiagObjCImplCannotBeGeneric(const AST::Node& node) const; private: void CheckMirrorAnnoArgs(const AST::Annotation& anno) const; void CheckImplAnnoArgs(const AST::Annotation& anno) const; void CheckMirrorAnnoTarget(const AST::Annotation& anno) const; void CheckImplAnnoTarget(const AST::Annotation& anno) const; void DiagObjCMirrorCannotBeSealed(const AST::Node& node) const; void DiagObjCImplCannotBeOpen(const AST::Node& node) const; void DiagObjCImplCannotBeInterface(const AST::Node& node) const; void DiagObjCImplCannotBeAbstract(const AST::Node& node) const; void DiagObjCImplCannotBeSealed(const AST::Node& node) const; ParserImpl& p; }; } // namespace Cangjie #endif // CANGJIE_PARSE_NATIVEFFIOBJCPARSERIMPL_Hcangjie_compiler-1.0.7/src/Parse/NativeFFI/ObjC/ParserDiag.cpp000066400000000000000000000061211510705540100237650ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements parser-related diagnostics for cangjie-native Objective-C FFI */ #include "../../ParserImpl.h" #include "OCFFIParserImpl.h" using namespace Cangjie; using namespace AST; void OCFFIParserImpl::DiagObjCMirrorCannotBeSealed(const Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_mirror_cannot_be_sealed, node); } void OCFFIParserImpl::DiagObjCMirrorCannotHaveFinalizer(const Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_mirror_cannot_have_finalizer, node); } void OCFFIParserImpl::DiagObjCMirrorMemberMustHaveForeignName(const Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_mirror_member_must_have_foreign_name, node); } void OCFFIParserImpl::DiagObjCMirrorCannotHavePrivateMember(const Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_mirror_cannot_have_private_member, node); } void OCFFIParserImpl::DiagObjCMirrorCannotHaveStaticInit(const Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_mirror_cannot_have_static_init, node); } void OCFFIParserImpl::DiagObjCMirrorCannotHaveConstMember(const Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_mirror_cannot_have_const_member, node); } void OCFFIParserImpl::DiagObjCMirrorFieldCannotHaveInitializer(const AST::Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_mirror_field_cannot_have_initializer, node); } void OCFFIParserImpl::DiagObjCMirrorCannotHavePrimaryCtor(const AST::Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_mirror_cannot_have_primary_ctor, node); } void OCFFIParserImpl::DiagObjCMirrorFieldCannotBeStatic(const AST::Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_mirror_field_cannot_be_static, node); } void OCFFIParserImpl::DiagObjCImplCannotBeGeneric(const Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_impl_cannot_be_generic, node); } void OCFFIParserImpl::DiagObjCImplCannotBeOpen(const Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_impl_cannot_be_open, node); } void OCFFIParserImpl::DiagObjCImplCannotBeInterface(const Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_impl_cannot_be_interface, node); } void OCFFIParserImpl::DiagObjCImplCannotBeAbstract(const Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_impl_cannot_be_abstract, node); } void OCFFIParserImpl::DiagObjCImplCannotBeSealed(const Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_impl_cannot_be_sealed, node); } void OCFFIParserImpl::DiagObjCImplCannotHaveStaticInit(const Node& node) const { p.ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_impl_cannot_have_static_init, node); } cangjie_compiler-1.0.7/src/Parse/NativeFFI/readme.md000066400000000000000000000002121510705540100221750ustar00rootroot00000000000000# Native FFI This directory includes parser logics related to Cangjie **Native** FFI with other languages such as: - Java - Objective-c cangjie_compiler-1.0.7/src/Parse/ParseAnnotations.cpp000066400000000000000000000272551510705540100226570ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "ParserImpl.h" #include "cangjie/AST/Match.h" #include "cangjie/Basic/Print.h" using namespace Cangjie; using namespace AST; namespace { const std::string OVERFLOW_STRATEGY = "overflow"; } // namespace void ParserImpl::ParseAttributeAnnotation(Annotation& anno) { if (!Seeing(TokenKind::LSQUARE)) { return; } auto pos = lookahead.Begin(); anno.lsquarePos = pos; Next(); while (true) { if (SeeingAny({TokenKind::IDENTIFIER, TokenKind::STRING_LITERAL}) || SeeingContextualKeyword()) { anno.attrs.push_back(lookahead); Next(); } if (Skip(TokenKind::COMMA)) { anno.attrCommas.push_back(lastToken.Begin()); continue; } break; } if (!Skip(TokenKind::RSQUARE)) { DiagExpectedRightDelimiter("[", pos); ConsumeUntil(TokenKind::NL); } else { anno.rsquarePos = lastToken.Begin(); } } void ParserImpl::ParseOverflowAnnotation(Annotation& anno) { if (Seeing(TokenKind::LSQUARE)) { auto pos = lookahead.Begin(); anno.lsquarePos = pos; Next(); if (Seeing(TokenKind::IDENTIFIER)) { anno.overflowStrategy = Utils::StringToOverflowStrategy(Utils::StrToLower(lookahead.Value())); Next(); } if (!Skip(TokenKind::RSQUARE)) { DiagExpectedRightDelimiter("[", pos); ConsumeUntil(TokenKind::NL); } else { anno.rsquarePos = lastToken.Begin(); } } else { anno.overflowStrategy = Utils::StringToOverflowStrategy(Utils::StrToLower(anno.identifier.Val().substr(OVERFLOW_STRATEGY.size()))); } } void ParserImpl::ParseWhenAnnotation(Annotation& anno) { if (!Seeing(TokenKind::LSQUARE)) { anno.EnableAttr(Attribute::IS_BROKEN); DiagExpectedLsquareAfter(anno, "@When", "when annotation must have condition"); return; } auto pos = lookahead.Begin(); anno.lsquarePos = pos; Next(); anno.condExpr = ParseExpr(); const static std::vector astKinds = { ASTKind::UNARY_EXPR, ASTKind::BINARY_EXPR, ASTKind::REF_EXPR, }; if (Utils::NotIn(anno.condExpr->astKind, astKinds)) { if (!anno.condExpr->TestAttr(Attribute::IS_BROKEN)) { DiagUnrecognizedExprInWhen(*anno.condExpr, anno); } } if (!Skip(TokenKind::RSQUARE)) { DiagExpectedRightDelimiter("[", pos); ConsumeUntil(TokenKind::NL); } else { anno.rsquarePos = lastToken.Begin(); } } OwnedPtr ParserImpl::ParseAnnotationArgument() { OwnedPtr ret = MakeOwned(); ret->begin = lookahead.Begin(); if (SeeingNamedFuncArgs()) { ret->name = ExpectIdentifierWithPos(*ret); Next(); ret->colonPos = lookahead.Begin(); } auto tmpExpr = ParseExpr(ExprKind::EXPR_IN_ANNOTATION); ret->expr = std::move(tmpExpr); if (ret->expr) { ret->end = ret->expr->end; } return ret; } void ParserImpl::ParseAnnotationArguments(Annotation& anno) { if (!Skip(TokenKind::LSQUARE)) { return; } auto pos = lastToken.Begin(); anno.lsquarePos = pos; bool backArgsIsInvalid{false}; for (;;) { if (DetectPrematureEnd()) { DiagExpectedRightDelimiter("[", pos); break; } if (Skip(TokenKind::RSQUARE)) { anno.rsquarePos = lastToken.Begin(); break; } (void)anno.args.emplace_back(ParseAnnotationArgument()); backArgsIsInvalid = anno.args.back()->expr->astKind == ASTKind::INVALID_EXPR; if (Skip(TokenKind::COMMA)) { if (Seeing(TokenKind::RSQUARE)) { DiagExpectCharacter("identifier"); } anno.args.back()->commaPos = lastToken.Begin(); continue; } if (!Seeing(TokenKind::RSQUARE)) { if (backArgsIsInvalid) { break; } DiagExpectedRightDelimiter("[", pos); } } anno.end = lastToken.End(); } namespace Cangjie { // Annotations that will only be parsed in the standard library. static const std::set STD_ONLY_ANNO = { // Disable const evaluation checker for items annotated with `@ConstSafe`. "ConstSafe"}; bool IsBuiltinAnnotation(const std::string& moduleName, const std::string& identifier) { if (STD_ONLY_ANNO.find(identifier) != STD_ONLY_ANNO.end()) { return moduleName == "std"; } return NAME_TO_ANNO_KIND.find(identifier) != NAME_TO_ANNO_KIND.end(); } } // namespace Cangjie bool ParserImpl::SeeingBuiltinAnnotation() { if (!Seeing(TokenKind::AT)) { return false; } // Get annotation identifier. auto tokens = lexer->LookAheadSkipNL(1); if (tokens.begin()->kind != TokenKind::IDENTIFIER) { return false; } return IsBuiltinAnnotation(moduleName, tokens.begin()->Value()); } bool ParserImpl::SeeingAtWhen() { if (!SeeingCombinator({TokenKind::AT, TokenKind::IDENTIFIER})) { return false; } auto tokens = lexer->LookAheadSkipNL(1); tokens.begin()->SetValue("When"); return true; } bool ParserImpl::SeeingMacroCall() { if (!Seeing(TokenKind::AT)) { return false; } // Get annotation identifier. auto tokens = lexer->LookAheadSkipNL(1); if (tokens.begin()->kind != TokenKind::IDENTIFIER && (tokens.begin()->kind < TokenKind::PUBLIC || tokens.begin()->kind > TokenKind::OPEN)) { return false; } return !IsBuiltinAnnotation(moduleName, tokens.begin()->Value()); } bool ParserImpl::SeeingMacroCallDecl() { if (!SeeingAny({TokenKind::AT, TokenKind::AT_EXCL})) { return false; } // Get annotation identifier. auto tokens = lexer->LookAheadSkipNL(1); if (tokens.begin()->kind != TokenKind::IDENTIFIER && (tokens.begin()->kind < TokenKind::PUBLIC || tokens.begin()->kind > TokenKind::OPEN)) { return false; } return !IsBuiltinAnnotation(moduleName, tokens.begin()->Value()); } void ParserImpl::ParseAnnotations(PtrVector& annos) { while (SeeingBuiltinAnnotation() || (this->enableCustomAnno && SeeingMacroCallDecl())) { auto annotation = ParseAnnotation(); auto anno = std::find_if(annos.begin(), annos.end(), [&annotation](const auto& anno) { return anno->kind != AnnotationKind::CUSTOM && anno->identifier == annotation->identifier; }); if (anno != annos.end()) { DiagDuplicatedAnno(*annotation, **anno); } annos.emplace_back(std::move(annotation)); } } OwnedPtr ParserImpl::ParseCustomAnnotation() { bool isCompileTimeVisible{false}; if (!Skip(TokenKind::AT)) { Skip(TokenKind::AT_EXCL); isCompileTimeVisible = true; } auto atPos = lastToken.Begin(); (void)Peek(); OwnedPtr expr = ParseRefExpr(); while (Skip(TokenKind::DOT)) { auto ret = ParseMemberAccess(std::move(expr)); expr = std::move(ret); } auto ident = expr->ToString(); auto annotation = MakeOwned(ident, AnnotationKind::CUSTOM, atPos); annotation->identifier.SetPos(expr->begin, expr->begin + annotation->identifier.Val().size()); annotation->baseExpr = std::move(expr); annotation->end = lastToken.End(); annotation->isCompileTimeVisible = isCompileTimeVisible; ParseAnnotationArguments(*annotation); return annotation; } void ParserImpl::ValidateDeprecatedAnnotationArgument( const Ptr lce, const std::string& name, const LitConstKind& expectedKind, bool& isArgumentFound) { if (isArgumentFound) { DiagDeprecatedArgumentDuplicated(*lce, name); } isArgumentFound = true; if (lce->stringValue == "") { DiagDeprecatedEmptyStringArgument(*lce, name); } else if (lce->kind != expectedKind) { const auto kindName = (expectedKind == LitConstKind::STRING) ? "String" : "Bool"; DiagDeprecatedWrongArgumentType(*lce, name, kindName); } } void ParserImpl::CheckDeprecatedAnnotation(const Annotation& anno) { bool messageFound = false; bool sinceFound = false; bool strictFound = false; for (auto& arg : anno.args) { if (!arg->expr || arg->expr->astKind != ASTKind::LIT_CONST_EXPR) { DiagDeprecatedArgumentNotLitConst(*arg); return; } auto lce = DynamicCast(arg->expr.get()); if (!lce) { DiagDeprecatedArgumentNotLitConst(*arg); return; } if (arg->name == "message" || arg->name == "") { ValidateDeprecatedAnnotationArgument(lce, "message", LitConstKind::STRING, messageFound); } else if (arg->name == "since") { ValidateDeprecatedAnnotationArgument(lce, "since", LitConstKind::STRING, sinceFound); } else if (arg->name == "strict") { ValidateDeprecatedAnnotationArgument(lce, "strict", LitConstKind::BOOL, strictFound); } else { DiagDeprecatedUnknownArgument(*arg, arg->name); } } } void ParserImpl::CheckObjCMirrorAnnotation(const Annotation& anno) const { static const std::string OBJ_C_MIRROR_NAME = "@ObjCMirror"; static const std::string OBJ_C_IMPL_NAME = "@ObjCImpl"; const auto& annotationName = anno.kind == AnnotationKind::OBJ_C_MIRROR ? OBJ_C_MIRROR_NAME : OBJ_C_IMPL_NAME; ffiParser->CheckZeroOrSingleStringLitArgAnnotation(anno, annotationName); } OwnedPtr ParserImpl::ParseAnnotation() { if (this->enableCustomAnno && SeeingMacroCallDecl()) { // Reparse as a custom annotation after macro expansion if one macrocall can't find it's macrodef. return ParseCustomAnnotation(); } // parse builtin annotation Skip(TokenKind::AT); auto beg = lastToken.Begin(); Skip(TokenKind::IDENTIFIER); CJC_ASSERT(NAME_TO_ANNO_KIND.find(lastToken.Value()) != NAME_TO_ANNO_KIND.end()); auto kind = NAME_TO_ANNO_KIND.at(lastToken.Value()); auto annotation = MakeOwned(lastToken.Value(), kind, beg); annotation->identifier.SetPos(lastToken.Begin(), lastToken.End()); annotation->end = lastToken.End(); auto tok = lastToken; ChainScope cs(*this, annotation.get()); switch (annotation->kind) { case AnnotationKind::ATTRIBUTE: { ParseAttributeAnnotation(*annotation); break; } case AnnotationKind::NUMERIC_OVERFLOW: { ParseOverflowAnnotation(*annotation); break; } case AnnotationKind::WHEN: { ParseWhenAnnotation(*annotation); break; } case AnnotationKind::DEPRECATED: { ParseAnnotationArguments(*annotation); CheckDeprecatedAnnotation(*annotation); break; } case AnnotationKind::JAVA_MIRROR: case AnnotationKind::JAVA_IMPL: { ParseAnnotationArguments(*annotation); break; } case AnnotationKind::FOREIGN_NAME: { ParseAnnotationArguments(*annotation); break; } case AnnotationKind::OBJ_C_MIRROR: case AnnotationKind::OBJ_C_IMPL: { ParseAnnotationArguments(*annotation); CheckObjCMirrorAnnotation(*annotation); break; } default: { // parse annotation arguments. ParseAnnotationArguments(*annotation); break; } } annotation->end = lastToken.End(); return annotation; } cangjie_compiler-1.0.7/src/Parse/ParseAtom.cpp000066400000000000000000001724651510705540100212660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file defines the implementation of parser of literals, including integer literal, float literal, bool literal, * char literal, list literal, set literal and map literal. */ #include "ParserImpl.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/StringConvertor.h" #include "cangjie/Utils/FileUtil.h" using namespace Cangjie; using namespace Cangjie::AST; OwnedPtr ParserImpl::ParseAtom(ExprKind ek) { if (IsConditionExpr(ek) && Skip(TokenKind::LPAREN)) { return ParseLeftParenExprInKind(ek); } if (const TokenKind peekKind = Peek().kind; exprHandlerMap.count(peekKind) != 0) { lastToken = lookahead; Next(); return exprHandlerMap[peekKind](this); } if (Seeing(TokenKind::IDENTIFIER) || SeeingContextualKeyword()) { return ParseRefExpr(ek); } if (SeeingMacroCall() || SeeingBuiltinAnnotation()) { return ParseMacroExprOrLambdaExpr(); } if (SeeingLiteral()) { return ParseLitConst(); } // Type convert expression. if (SeeingPrimitiveTypeAndLParen()) { return ParseTypeConvExpr(); } // Optimize those Seeing(). // If seeing a primitive type + dot, should be a static function call like Int64.foo(). if (SeeingPrimitiveTypeAndDot()) { auto ret = MakeOwned(TOKENKIND_TO_PRIMITIVE_TYPEKIND_MAP.at(lookahead.kind)); ret->begin = lookahead.Begin(); Next(); // Consume the TYPE token but keep the DOT token. ret->end = lastToken.End(); return ret; } if (Seeing(TokenKind::WILDCARD)) { return ParseWildcardExpr(); } return GetInvalidExprInAtom(lookahead.Begin()); } OwnedPtr ParserImpl::GetInvalidExprInAtom(Position pos) { auto ret = MakeOwned(pos); // Better to handle the invalid expr upper. ret->EnableAttr(Attribute::IS_BROKEN); if (chainedAST.empty()) { DiagExpectedExpression(); } if (!chainedAST.empty() && !chainedAST.back()->TestAttr(Attribute::HAS_BROKEN)) { DiagExpectedExpression(); chainedAST.back()->EnableAttr(Attribute::HAS_BROKEN); } ret->value = lookahead.Value(); if (ret->end.column == 1) { // The end position of the diag cannot be 1. ret->end = lookahead.End(); } return ret; } OwnedPtr ParserImpl::ParseUnsafeBlock() { if (!Seeing(TokenKind::LCURL)) { return GetInvalidExprInAtom(lookahead.Begin()); } auto unsafePos = lastToken.Begin(); auto expr = ParseBlock(ScopeKind::FUNC_BODY); // unsafe block add UNSAFE Attribute to Special treatment expr->EnableAttr(Attribute::UNSAFE); SetUnsafe(expr.get()); expr->unsafePos = unsafePos; expr->begin = expr->unsafePos; return expr; } OwnedPtr ParserImpl::ParseLitConst() { if (Seeing({TokenKind::LPAREN, TokenKind::RPAREN})) { // We enter this branch only from ParsePattern Skip(TokenKind::LPAREN); return ParseLeftParenExpr(); } OwnedPtr ret = MakeOwned(); ret->begin = lookahead.Begin(); if (Skip(TokenKind::STRING_LITERAL) || Skip(TokenKind::JSTRING_LITERAL) || Skip(TokenKind::MULTILINE_STRING)) { return ProcessStringInterpolation(lastToken); } else if (Skip(TokenKind::SUB)) { return ParseNegativeLiteral(); } ret->stringValue = lookahead.Value(); ret->isSingleQuote = lookahead.isSingleQuote; if (Skip(TokenKind::BOOL_LITERAL)) { ret->kind = LitConstKind::BOOL; } else if (Skip(TokenKind::INTEGER_LITERAL)) { ret->kind = LitConstKind::INTEGER; } else if (Skip(TokenKind::RUNE_BYTE_LITERAL)) { ret->kind = LitConstKind::RUNE_BYTE; } else if (Skip(TokenKind::MULTILINE_RAW_STRING)) { ret->kind = LitConstKind::STRING; ret->stringKind = StringKind::MULTILINE_RAW; ret->stringValue = StringConvertor::Normalize(lookahead.Value(), true); ret->codepoint = StringConvertor::UTF8ToCodepoint(ret->stringValue); ret->delimiterNum = lookahead.delimiterNum; } else if (Skip(TokenKind::RUNE_LITERAL)) { ret->kind = LitConstKind::RUNE; auto notNormalize = diag.ignoreScopeCheck && lastToken.Value().find("\\u") == 0; ret->stringValue = notNormalize ? lastToken.Value() : StringConvertor::Normalize(lastToken.Value()); ret->codepoint = StringConvertor::UTF8ToCodepoint(ret->stringValue); if (!lookahead.Value().empty() && lookahead.Value().find("\\u{") != std::string::npos && ret->codepoint.empty()) { DiagInvalidUnicodeScalar(lastToken.Begin(), lastToken.Value()); } } else if (Skip(TokenKind::FLOAT_LITERAL)) { ret->kind = LitConstKind::FLOAT; } else if (Skip(TokenKind::UNIT_LITERAL)) { // Current lexer is unlikely to produce TokenKind.UNIT_LITERAL, and it sees '()' as // TWO Tokens: LPAREN, RPAREN. But user provided TokenStream(macro-generated) can have // TokenKind.UNIT_LITERAL. Whether to remove UNIT_LITERAL is open to discuss. // This is very likely to be removed and make '()' a zero-length tuple in the future. ret->kind = LitConstKind::UNIT; } else { CJC_ABORT(); return MakeInvalid(lookahead.Begin()); } ret->rawString = lookahead.Value(); ret->end = lastToken.End(); return ret; } OwnedPtr ParserImpl::ParseNegativeLiteral() { OwnedPtr ret = MakeOwned(); auto subToken = lastToken; ret->begin = lookahead.Begin(); if (Skip(TokenKind::FLOAT_LITERAL)) { ret->kind = LitConstKind::FLOAT; } else if (Skip(TokenKind::INTEGER_LITERAL)) { ret->kind = LitConstKind::INTEGER; } else if (Skip(TokenKind::RUNE_BYTE_LITERAL)) { ret->kind = LitConstKind::RUNE_BYTE; } else { DiagExpectedLiteral(subToken.Begin()); return MakeInvalid(lookahead.Begin()); } ret->stringValue = "-" + lookahead.Value(); ret->rawString = ret->stringValue; ret->end = lastToken.End(); return ret; } OwnedPtr ParserImpl::GetLitConstExprFromStr( const std::string& value, const Token& token, const Position& pos) const { OwnedPtr ret = MakeOwned(); ret->begin = pos; ret->rawString = value; if (token.kind == TokenKind::MULTILINE_STRING) { ret->kind = LitConstKind::STRING; ret->stringKind = StringKind::MULTILINE; } else if (token.kind == TokenKind::STRING_LITERAL) { ret->kind = LitConstKind::STRING; ret->stringKind = StringKind::NORMAL; } else if (token.kind == TokenKind::JSTRING_LITERAL) { ret->kind = LitConstKind::JSTRING; ret->stringKind = StringKind::JSTRING; } ret->stringValue = StringConvertor::Normalize(value); ret->codepoint = StringConvertor::UTF8ToCodepoint(ret->stringValue); ret->isSingleQuote = token.isSingleQuote; ret->end = token.End(); return ret; } OwnedPtr ParserImpl::ParseInterpolationExpr(const std::string& value, const Position& pos) { auto ret = MakeOwned(); ChainScope cs(*this, ret.get()); ret->begin = pos; ret->end = pos; auto vals = Utils::SplitLines(value); ret->end.line += static_cast(vals.size() - 1); if (vals.size() == 1) { ret->end.column += static_cast(value.size()); } else { ret->end.column = static_cast(vals.back().size() + 1); } ret->rawString = value; ret->dollarPos = pos; // "{xxx}" auto wrapInBracket = value.substr(1, value.length() - 1); Position basePos = pos; basePos.column += 1; auto curLookahead = lookahead; auto curLastToken = lastToken; auto curlastNoneNLToken = lastNoneNLToken; auto hasSkipNewline = newlineSkipped; std::unique_ptr curlexer = std::move(lexer); lexer = std::make_unique(wrapInBracket, diag, diag.GetSourceManager(), basePos); if (enableEH) { lexer->SetEHEnabled(true); } ret->block = ParseBlock(ScopeKind::FUNC_BODY); if (!ret->block || ret->block->body.empty() || Peek().kind != TokenKind::END) { lexer = std::move(curlexer); newlineSkipped = hasSkipNewline; lastNoneNLToken = curlastNoneNLToken; lastToken = curLastToken; lookahead = curLookahead; ParseDiagnoseRefactor(DiagKindRefactor::parse_empty_string_interpolation, MakeRange(pos, ret->end)); return MakeInvalid(pos); } lexer = std::move(curlexer); newlineSkipped = hasSkipNewline; lastNoneNLToken = curlastNoneNLToken; lastToken = curLastToken; lookahead = curLookahead; // Need set IS_CLONED_SOURCE_CODE For lsp. auto block = ret->block.get(); Walker setAttrWalker(block, [block](Ptr node) -> VisitAction { if (node != block) { node->EnableAttr(Attribute::IS_CLONED_SOURCE_CODE); } return VisitAction::WALK_CHILDREN; }); setAttrWalker.Walk(); return ret; } OwnedPtr ParserImpl::ProcessStringInterpolation(const Token& token) { if (token.kind == TokenKind::JSTRING_LITERAL) { return GetLitConstExprFromStr(token.Value(), token, token.Begin()); } std::vector strParts = lexer->GetStrParts(token); auto tokenValue = token.Value(); if (strParts.size() == 1) { lexer->ClearStringParts(token); return GetLitConstExprFromStr(tokenValue, token, token.Begin()); } auto ret = MakeOwned(); ret->begin = token.Begin(); ret->rawString = tokenValue; ret->strParts = strParts; for (size_t i = 0; i < strParts.size(); i++) { auto strPart = strParts[i]; if (strPart.strKind == StringPart::STR) { if (i == 0) { // First string pos contains '"' or '"""'. auto beginPos = token.Begin(); strPart.begin.column = beginPos.column; strPart.begin.line = beginPos.line; } ret->strPartExprs.emplace_back(GetLitConstExprFromStr(strPart.value, token, strPart.begin)); } else { ret->strPartExprs.emplace_back(ParseInterpolationExpr(strPart.value, strPart.begin)); } } ret->end = token.End(); lexer->ClearStringParts(token); auto litConstExpr = GetLitConstExprFromStr(tokenValue, token, token.Begin()); litConstExpr->siExpr = std::move(ret); return litConstExpr; } OwnedPtr ParserImpl::ParseArrayLitExpr() { OwnedPtr ret = MakeOwned(); ret->begin = lastToken.Begin(); ret->leftSquarePos = lastToken.Begin(); ParseZeroOrMoreSepTrailing([&ret](const Position& pos) { ret->commaPosVector.push_back(pos); }, [this, &ret]() { ret->children.emplace_back(ParseExpr(ExprKind::EXPR_IN_ARRAY)); }, TokenKind::RSQUARE); if (!Skip(TokenKind::RSQUARE)) { DiagExpectedRightDelimiter("[", ret->leftSquarePos); ConsumeUntilAny({TokenKind::NL, TokenKind::SEMI, TokenKind::RSQUARE}); } ret->rightSquarePos = lastToken.Begin(); ret->end = lastToken.End(); return ret; } static bool IsCondition(const Expr& e) { if (auto paren = DynamicCast(&e)) { return IsCondition(*paren->expr); } if (Is(&e)) { return true; } return false; } bool ParserImpl::CheckCondition(Expr* e) { if (!e) { return false; } if (auto bin = DynamicCast(e)) { bool hasSubCondition = CheckCondition(bin->leftExpr.get()); hasSubCondition = CheckCondition(bin->rightExpr.get()) || hasSubCondition; if (hasSubCondition) { if (bin->op != TokenKind::AND && bin->op != TokenKind::OR) { diag.DiagnoseRefactor(DiagKindRefactor::parse_query_expected_logic_symbol, bin->operatorPos, TOKENS[static_cast(bin->op)]); bin->EnableAttr(Attribute::HAS_BROKEN); } } return hasSubCondition; } return IsCondition(*e); } void ParserImpl::ConsumeUntilIfExprEnd() { ConsumeUntil(TokenKind::LCURL); ConsumeUntil(TokenKind::RCURL); SkipBlank(TokenKind::NL, TokenKind::SEMI); } OwnedPtr ParserImpl::ParseIfExpr() { OwnedPtr ret = MakeOwned(); ret->begin = lookahead.Begin(); ret->ifPos = lookahead.Begin(); if (!Skip(TokenKind::LPAREN)) { DiagExpectedLeftParenAfter(ret->begin, "if"); ConsumeUntilIfExprEnd(); ret->EnableAttr(Attribute::IS_BROKEN); return MakeInvalid(ret->begin); } ret->leftParenPos = lastToken.Begin(); ret->condExpr = ParseExpr(ExprKind::IF_COND_EXPR); if (!ret->condExpr->TestAttr(Attribute::IS_BROKEN)) { (void)CheckCondition(ret->condExpr.get()); } if (!Skip(TokenKind::RPAREN)) { if (Is(ret->condExpr) && Skip(TokenKind::WHERE)) { diag.DiagnoseRefactor(DiagKindRefactor::parse_expected_if_let_andand, lastToken); } else { DiagExpectedRightDelimiter("(", ret->leftParenPos); } ret->EnableAttr(Attribute::IS_BROKEN); ret->thenBody = MakeInvalid(lookahead.Begin()); ret->end = lookahead.Begin(); ConsumeUntilIfExprEnd(); if (lastToken.kind == TokenKind::RCURL && Skip(TokenKind::ELSE)) { ParseElse(*ret); } return ret; } ret->rightParenPos = lastToken.Begin(); // The rule of block is alike with func body. ret->thenBody = ParseBlock(ScopeKind::FUNC_BODY); ret->end = ret->thenBody->end; SkipBlank(TokenKind::NL); if (Skip(TokenKind::ELSE)) { ParseElse(*ret); } return ret; } void ParserImpl::ParseElse(IfExpr& ret) { ret.hasElse = true; ret.elsePos = lastToken.Begin(); if (Skip(TokenKind::IF)) { ret.isElseIf = true; ret.elseBody = ParseIfExpr(); } else { // The rule of block is alike with func body. ret.elseBody = ParseBlock(ScopeKind::FUNC_BODY); } ret.end = lastToken.End(); } OwnedPtr ParserImpl::ParseExprOrDeclsInMatchCase() { OwnedPtr exprOrDecls = MakeOwned(); exprOrDecls->begin = lastToken.Begin(); auto flag = false; auto hasSemi = false; while (!SeeingAny({TokenKind::CASE, TokenKind::RCURL})) { if (flag && !newlineSkipped && !hasSemi && !exprOrDecls->TestAttr(Attribute::HAS_BROKEN)) { DiagExpectSemiOrNewline(); } flag = true; SkipBlank(TokenKind::SEMI); if (DetectPrematureEnd()) { break; } if (SeeingMacroCallDecl() || SeeingDecl() || SeeingExpr()) { auto node = ParseExprOrDecl(ScopeKind::FUNC_BODY); exprOrDecls->body.emplace_back(std::move(node)); } else { DiagMatchCaseExpectedExprOrDecl(); exprOrDecls->EnableAttr(Attribute::HAS_BROKEN); ConsumeUntilAny({TokenKind::CASE, TokenKind::RCURL, TokenKind::NL}, false); } hasSemi = Seeing(TokenKind::SEMI); SkipBlank(TokenKind::SEMI); } exprOrDecls->end = lastToken.End(); return exprOrDecls; } static OwnedPtr CreateLambdaFromBlock( OwnedPtr block, const Expr& expr, OwnedPtr paramList) { std::vector> paramLists; paramList->begin = expr.begin; paramList->end = expr.end; paramLists.emplace_back(std::move(paramList)); auto fb = CreateFuncBody(std::move(paramLists), nullptr, std::move(block)); // create the lambda expression auto le = CreateLambdaExpr(std::move(fb)); le->begin = expr.begin; le->end = expr.end; return le; } static void DesugarTry(const OwnedPtr& expr) { if (expr->handlers.empty()) { return; } // To: {=> try{...} catch{...}} // create block for function body OwnedPtr lambdaBlock; if (expr->catchBlocks.empty()) { // Careful: CHIR2 cannot compile a Try block without catch or finally. // Even if the catch block has a finally clause, we will remove it and turn // it into a lambda lambdaBlock = ASTCloner::Clone(expr->tryBlock.get(), SetIsClonedSourceCode); } else { auto teWithoutHandle = ASTCloner::Clone(expr.get(), SetIsClonedSourceCode); teWithoutHandle->handlers.clear(); teWithoutHandle->finallyBlock = nullptr; teWithoutHandle->ty = expr->ty; std::vector> lambdaBodyExprs; lambdaBodyExprs.emplace_back(std::move(teWithoutHandle)); lambdaBlock = CreateBlock(std::move(lambdaBodyExprs)); } expr->tryLambda = CreateLambdaFromBlock(std::move(lambdaBlock), *expr->tryBlock, MakeOwned()); // handler lambdas for (auto& handler : expr->handlers) { auto cmdPat = RawStaticCast(handler.commandPattern.get()); // no multiple command type patterns, it will be forbidden in typechecking if (expr->TestAttr(Attribute::HAS_BROKEN) || cmdPat->TestAttr(Attribute::IS_BROKEN) || cmdPat->types[0]->TestAttr(Attribute::IS_BROKEN)) { expr->EnableAttr(Attribute::HAS_BROKEN); return; } auto paramList = MakeOwned(); auto originalCommandPatternType = cmdPat->types[0].get(); auto commandTy = originalCommandPatternType->ty; auto commandPattern = ASTCloner::Clone(originalCommandPatternType); OwnedPtr command; if (auto varPattern = DynamicCast(cmdPat->pattern.get()); varPattern) { command = CreateFuncParam(varPattern->varDecl->identifier, std::move(commandPattern), nullptr, commandTy); } else { command = CreateFuncParam("_", std::move(commandPattern), nullptr, commandTy); } CopyBasicInfo(cmdPat->pattern, command); paramList->params.emplace_back(std::move(command)); handler.desugaredLambda = CreateLambdaFromBlock( ASTCloner::Clone(handler.block.get(), SetIsClonedSourceCode), *handler.block, std::move(paramList)); } if (expr->finallyBlock) { expr->finallyLambda = CreateLambdaFromBlock(ASTCloner::Clone(expr->finallyBlock.get(), SetIsClonedSourceCode), *expr->finallyBlock, MakeOwned()); } } void ParserImpl::ParseHandleBlock(TryExpr& tryExpr) { auto handler = Handler(); handler.pos = lastToken.Begin(); Position leftParenPos{0, 0, 0}; if (Skip(TokenKind::LPAREN)) { leftParenPos = lastToken.Begin(); } else if (!tryExpr.TestAttr(Attribute::HAS_BROKEN)) { DiagExpectedLeftParenAfter(handler.pos, "handle"); tryExpr.EnableAttr(Attribute::HAS_BROKEN); } // handle clause will have one or two arguments handler.commandPattern = ParseCommandTypePattern(); if (!Skip(TokenKind::RPAREN) && leftParenPos.line != 0 && !tryExpr.TestAttr(Attribute::HAS_BROKEN)) { DiagExpectedRightDelimiter("(", leftParenPos); tryExpr.EnableAttr(Attribute::HAS_BROKEN); } handler.block = ParseBlock(ScopeKind::FUNC_BODY); tryExpr.handlers.emplace_back(std::move(handler)); } void ParserImpl::ParseCatchBlock(TryExpr& tryExpr) { tryExpr.catchPosVector.push_back(lastToken.Begin()); Position leftParenPos{0, 0, 0}; if (Skip(TokenKind::LPAREN)) { leftParenPos = lastToken.Begin(); tryExpr.catchLParenPosVector.push_back(leftParenPos); } else if (!tryExpr.TestAttr(Attribute::HAS_BROKEN)) { DiagExpectedLeftParenAfter(tryExpr.catchPosVector.back(), "catch"); tryExpr.EnableAttr(Attribute::HAS_BROKEN); } tryExpr.catchPatterns.emplace_back(ParseExceptTypePattern()); if (!Skip(TokenKind::RPAREN) && leftParenPos.line != 0 && !tryExpr.TestAttr(Attribute::HAS_BROKEN)) { DiagExpectedRightDelimiter("(", leftParenPos); tryExpr.EnableAttr(Attribute::HAS_BROKEN); } if (lastToken.kind == TokenKind::RPAREN) { tryExpr.catchRParenPosVector.push_back(lastToken.Begin()); } tryExpr.catchBlocks.emplace_back(ParseBlock(ScopeKind::FUNC_BODY)); } OwnedPtr ParserImpl::ParseTryExpr() { OwnedPtr tryExpr = MakeOwned(); tryExpr->tryPos = lastToken.Begin(); tryExpr->begin = lookahead.Begin(); if (Seeing(TokenKind::LCURL)) { tryExpr->tryBlock = ParseBlock(ScopeKind::FUNC_BODY); } else if (Seeing(TokenKind::LPAREN)) { // try with resources ParseTryWithResource(ScopeKind::FUNC_BODY, *tryExpr); } else { DiagExpectCharacter("'{' or '('", "try expression must have block or resources"); tryExpr->EnableAttr(Attribute::HAS_BROKEN); tryExpr->EnableAttr(Attribute::IS_BROKEN); } while (Seeing(TokenKind::HANDLE) || Seeing(TokenKind::CATCH)) { if (Skip(TokenKind::HANDLE)) { ParseHandleBlock(*tryExpr); } else if (Skip(TokenKind::CATCH)) { ParseCatchBlock(*tryExpr); } } if (Skip(TokenKind::FINALLY)) { tryExpr->finallyPos = lastToken.Begin(); tryExpr->finallyBlock = ParseBlock(ScopeKind::FUNC_BODY); } else { if (tryExpr->catchBlocks.empty() && tryExpr->handlers.empty() && tryExpr->resourceSpec.empty() && !tryExpr->TestAttr(Attribute::HAS_BROKEN)) { DiagExpectedCatchOrHandleOrFinallyAfterTry(*tryExpr); tryExpr->EnableAttr(Attribute::HAS_BROKEN); } } tryExpr->end = lastToken.End(); if (!tryExpr->TestAttr(Attribute::HAS_BROKEN)) { DesugarTry(tryExpr); } return tryExpr; } OwnedPtr ParserImpl::ParseExceptTypePattern() { auto parsePatterns = [this](ExceptTypePattern& exceptTypePattern) { do { if (lastToken.kind == TokenKind::BITOR) { exceptTypePattern.bitOrPosVector.emplace_back(lastToken.Begin()); } exceptTypePattern.types.emplace_back(ParseType()); } while (Skip(TokenKind::BITOR)); }; if (Skip(TokenKind::WILDCARD)) { auto wildCardPos = lastToken.Begin(); auto wildCardPattern = MakeOwned(wildCardPos); if (Skip(TokenKind::COLON)) { OwnedPtr exceptTypePattern = MakeOwned(); exceptTypePattern->colonPos = lastToken.Begin(); exceptTypePattern->begin = wildCardPos; exceptTypePattern->pattern = std::move(wildCardPattern); parsePatterns(*exceptTypePattern); if (!exceptTypePattern->types.empty()) { exceptTypePattern->end = exceptTypePattern->types.back()->end; } return exceptTypePattern; } else { return wildCardPattern; } } else if (Seeing(TokenKind::IDENTIFIER) || SeeingContextualKeyword()) { OwnedPtr exceptTypePattern = MakeOwned(); auto ident = ParseIdentifierFromToken(lookahead); Position begin = lookahead.Begin(); exceptTypePattern->begin = begin; exceptTypePattern->pattern = MakeOwned(std::move(ident), begin); exceptTypePattern->patternPos = lookahead.Begin(); Next(); if (!Skip(TokenKind::COLON)) { ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_colon_in_catch_pattern, lookahead, ConvertToken(lookahead)); } exceptTypePattern->colonPos = lastToken.Begin(); parsePatterns(*exceptTypePattern); if (!exceptTypePattern->types.empty()) { exceptTypePattern->end = exceptTypePattern->types.back()->end; } return exceptTypePattern; } else { ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_wildcard_or_exception_pattern, lookahead, ConvertToken(lookahead)); return MakeInvalid(lookahead.Begin()); } } OwnedPtr ParserImpl::ParseCommandTypePattern() { auto parsePatterns = [this](CommandTypePattern& commandPattern) { do { if (lastToken.kind == TokenKind::BITOR) { commandPattern.bitOrPosVector.emplace_back(lastToken.Begin()); } commandPattern.types.emplace_back(ParseType()); } while (Skip(TokenKind::BITOR)); }; if (Skip(TokenKind::WILDCARD)) { auto wildCardPos = lastToken.Begin(); auto wildCardPattern = MakeOwned(wildCardPos); if (Skip(TokenKind::COLON)) { OwnedPtr commandPattern = MakeOwned(); commandPattern->colonPos = lastToken.Begin(); commandPattern->begin = wildCardPos; commandPattern->pattern = std::move(wildCardPattern); parsePatterns(*commandPattern); if (!commandPattern->types.empty()) { commandPattern->end = commandPattern->types.back()->end; } return commandPattern; } else { ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_colon_in_effect_pattern, lookahead, ConvertToken(lookahead)); return MakeInvalid(lookahead.Begin()); } } else if (Seeing(TokenKind::IDENTIFIER) || SeeingContextualKeyword()) { OwnedPtr commandPattern = MakeOwned(); auto ident = ParseIdentifierFromToken(lookahead); Position begin = lookahead.Begin(); commandPattern->begin = begin; commandPattern->pattern = MakeOwned(std::move(ident), begin); commandPattern->patternPos = lookahead.Begin(); Next(); if (!Skip(TokenKind::COLON)) { ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_colon_in_effect_pattern, lookahead, ConvertToken(lookahead)); } commandPattern->colonPos = lastToken.Begin(); parsePatterns(*commandPattern); if (!commandPattern->types.empty()) { commandPattern->end = commandPattern->types.back()->end; } return commandPattern; } else { ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_wildcard_or_effect_pattern, lookahead, ConvertToken(lookahead)); return MakeInvalid(lookahead.Begin()); } } void ParserImpl::ParsePatternsInCase(const OwnedPtr& matchCase, const MatchExpr& matchExpr) { auto first = ParsePattern(); first->ctxExpr = matchExpr.selector.get(); matchCase->patterns.emplace_back(std::move(first)); while (Skip(TokenKind::BITOR)) { matchCase->bitOrPosVector.push_back(lastToken.Begin()); auto pattern = ParsePattern(); pattern->ctxExpr = matchExpr.selector.get(); matchCase->patterns.emplace_back(std::move(pattern)); } } void ParserImpl::ParseMatchCases(AST::MatchExpr& matchExpr) { auto consumeTarget = [this]() { return SeeingAny({TokenKind::CASE, TokenKind::RCURL, TokenKind::DOUBLE_ARROW}) || SeeingCombinator(combinedDoubleArrow); }; while (Skip(TokenKind::CASE)) { OwnedPtr matchCase = MakeOwned(); ChainScope cs(*this, matchCase.get()); matchCase->begin = lookahead.Begin(); ParsePatternsInCase(matchCase, matchExpr); if (Skip(TokenKind::WHERE)) { matchCase->wherePos = lookahead.Begin(); matchCase->patternGuard = ParseExpr(); } else { matchCase->patternGuard = OwnedPtr(); } if (!Skip(TokenKind::DOUBLE_ARROW) && !SkipCombinedDoubleArrow()) { auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_double_arrow_in_case, lookahead, ConvertToken(lookahead)); builder.AddHint(MakeRange(matchCase->begin, "case")); ConsumeUntilAny(consumeTarget, false); matchCase->exprOrDecls = MakeInvalid(lookahead.Begin()); matchCase->end = matchCase->exprOrDecls->end; matchCase->EnableAttr(Attribute::HAS_BROKEN); if (!Skip(TokenKind::DOUBLE_ARROW) && !SkipCombinedDoubleArrow()) { matchExpr.matchCases.emplace_back(std::move(matchCase)); continue; } } matchCase->arrowPos = lastToken.Begin(); matchCase->exprOrDecls = ParseExprOrDeclsInMatchCase(); matchCase->end = matchCase->exprOrDecls->end; if (matchCase->exprOrDecls->body.empty() && !matchCase->exprOrDecls->TestAttr(Attribute::HAS_BROKEN)) { DiagMatchCaseBodyCannotBeEmpty(matchCase->arrowPos + std::string("=>").size()); matchCase->EnableAttr(Attribute::HAS_BROKEN); } matchExpr.matchCases.emplace_back(std::move(matchCase)); SkipBlank(TokenKind::SEMI); } } void ParserImpl::ParseMatchNoSelector(AST::MatchExpr& matchExpr) { matchExpr.matchMode = false; matchExpr.leftCurlPos = Peek().Begin(); Next(); auto consumeTarget = [this]() { return SeeingAny({TokenKind::CASE, TokenKind::RCURL, TokenKind::DOUBLE_ARROW}) || SeeingCombinator(combinedDoubleArrow); }; while (Skip(TokenKind::CASE)) { OwnedPtr matchCaseOther = MakeOwned(); matchCaseOther->begin = lookahead.Begin(); if (Skip(TokenKind::WILDCARD)) { auto wildcardExpr = MakeOwned(lastToken.Begin()); matchCaseOther->matchExpr = std::move(wildcardExpr); } else { matchCaseOther->matchExpr = ParseExpr(); } if (!Skip(TokenKind::DOUBLE_ARROW) && !SkipCombinedDoubleArrow()) { auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_double_arrow_in_case, lookahead, ConvertToken(lookahead)); builder.AddHint(MakeRange(matchCaseOther->begin, "case")); ConsumeUntilAny(consumeTarget, false); matchCaseOther->exprOrDecls = MakeInvalid(lookahead.Begin()); matchCaseOther->EnableAttr(Attribute::HAS_BROKEN); if (!Skip(TokenKind::DOUBLE_ARROW) && !SkipCombinedDoubleArrow()) { matchCaseOther->end = lastToken.End(); matchExpr.matchCaseOthers.emplace_back(std::move(matchCaseOther)); continue; } } matchCaseOther->arrowPos = lookahead.Begin(); matchCaseOther->exprOrDecls = ParseExprOrDeclsInMatchCase(); matchCaseOther->end = lastToken.Begin(); if (matchCaseOther->exprOrDecls->body.empty() && !matchCaseOther->TestAttr(Attribute::HAS_BROKEN)) { DiagMatchCaseBodyCannotBeEmpty(matchCaseOther->arrowPos + std::string("=>").size()); matchCaseOther->EnableAttr(Attribute::HAS_BROKEN); } matchExpr.matchCaseOthers.emplace_back(std::move(matchCaseOther)); } } OwnedPtr ParserImpl::ParseMatchExpr() { OwnedPtr matchExpr = MakeOwned(); ChainScope cs(*this, matchExpr.get()); matchExpr->begin = lookahead.Begin(); // If the next two valid tokens are '{' followed by a 'case', then this match expression does not have a // selector. skipNL = true; if (!Seeing({TokenKind::LCURL, TokenKind::CASE})) { // If this match expression has a selector. matchExpr->matchMode = true; if (!Skip(TokenKind::LPAREN)) { DiagExpectedSelectorOrMatchExprBody(matchExpr->begin); matchExpr->selector = MakeInvalid(lookahead.Begin()); matchExpr->EnableAttr(Attribute::HAS_BROKEN); ConsumeUntilAny({TokenKind::LCURL, TokenKind::NL}, false); } else { matchExpr->leftParenPos = lastToken.Begin(); matchExpr->selector = ParseExpr(); if (!Skip(TokenKind::RPAREN)) { DiagExpectedRightDelimiter("(", matchExpr->leftParenPos); } matchExpr->rightParenPos = lastToken.Begin(); } if (!Skip(TokenKind::LCURL)) { if (!matchExpr->TestAttr(Attribute::HAS_BROKEN)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_left_brace, lookahead, ConvertToken(lookahead)); matchExpr->EnableAttr(Attribute::HAS_BROKEN); } } else { matchExpr->leftCurlPos = lookahead.Begin(); } SkipSemi(); if (!Seeing(TokenKind::CASE)) { ConsumeUntil(TokenKind::RCURL, false); } ParseMatchCases(*matchExpr); if (matchExpr->matchCases.empty()) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_case, lookahead, ConvertToken(lookahead)); builder.AddHint(MakeRange(matchExpr->begin, "match")); } } else { // If this match expression does not have a selector. ParseMatchNoSelector(*matchExpr); if (matchExpr->matchCaseOthers.empty()) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_case, lookahead, ConvertToken(lookahead)); } } if (!Skip(TokenKind::RCURL) && !matchExpr->leftCurlPos.IsZero()) { DiagExpectedRightDelimiter("{", matchExpr->leftCurlPos); } matchExpr->rightCurlPos = lookahead.Begin(); matchExpr->end = lastToken.End(); return matchExpr; } OwnedPtr ParserImpl::ParseQuoteExpr() { OwnedPtr ret = MakeOwned(); ret->begin = lookahead.Begin(); ret->quotePos = lookahead.Begin(); skipNL = false; if (Skip(TokenKind::NL) || newlineSkipped) { ParseDiagnoseRefactor(DiagKindRefactor::parse_unexpected_line_break, lastToken); ret->EnableAttr(Attribute::HAS_BROKEN); } SkipBlank(TokenKind::NL); if (Skip(TokenKind::LPAREN)) { ret->leftParenPos = lookahead.Begin(); ParseQuoteTokens(*ret); if (!Skip(TokenKind::RPAREN)) { DiagExpectedRightDelimiter("(", ret->leftParenPos); } ret->rightParenPos = lookahead.Begin(); } else { if (!ret->TestAttr(Attribute::HAS_BROKEN)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_left_paren, lookahead, ConvertToken(lookahead)); } } skipNL = true; ret->end = lastToken.End(); return ret; } OwnedPtr ParserImpl::ParseReturnExpr() { OwnedPtr ret = MakeOwned(); ret->begin = lookahead.Begin(); ret->returnPos = lookahead.Begin(); if ((SeeingDecl() && !SeeingContextualKeyword()) || SeeingAny({TokenKind::SEMI, TokenKind::COMMA, TokenKind::RPAREN, TokenKind::RSQUARE, TokenKind::RCURL, TokenKind::CASE, TokenKind::END, TokenKind::DOUBLE_ARROW}) || SeeingCombinator(combinedDoubleArrow)) { OwnedPtr unitLit = MakeOwned(); unitLit->kind = LitConstKind::UNIT; unitLit->stringValue = "()"; ret->expr = std::move(unitLit); // Be aware to always add Position information when construct AST. ret->expr->begin = lastToken.End(); ret->expr->end = ret->expr->begin; if (Seeing(TokenKind::SEMI)) { ret->hasSemi = true; ret->semiPos = lookahead.Begin(); } ret->expr->EnableAttr(Attribute::COMPILER_ADD); } else { if (newlineSkipped) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_nl_warning, firstNLPosition); auto iter = std::find_if( combinator.rbegin(), combinator.rend(), [this](auto& k) { return SeeingCombinator(k.first); }); std::string tkValue = (iter != combinator.rend()) ? std::string(iter->second.second) : lookahead.Value(); builder.AddHint( MakeRange(lastNoneNLToken.Begin(), lookahead.Begin() + 1), lastNoneNLToken.Value(), tkValue); } ret->expr = ParseExpr(); if (ret->expr->TestAttr(Attribute::IS_BROKEN)) { ConsumeUntilAny({TokenKind::NL, TokenKind::SEMI}); } } ret->end = ret->expr->end; return ret; } OwnedPtr ParserImpl::ParseThrowExpr() { OwnedPtr ret = MakeOwned(); ret->throwPos = lastToken.Begin(); ret->begin = lookahead.Begin(); ret->expr = ParseExpr(); if (ret->expr) { ret->end = ret->expr->end; } return ret; } OwnedPtr ParserImpl::ParsePerformExpr() { OwnedPtr ret = MakeOwned(); ret->performPos = lastToken.Begin(); ret->begin = lookahead.Begin(); ret->expr = ParseExpr(); if (ret->expr) { ret->end = ret->expr->end; } return ret; } OwnedPtr ParserImpl::ParseResumeExpr() { OwnedPtr ret = MakeOwned(); ret->resumePos = lastToken.Begin(); ret->begin = lookahead.Begin(); ret->end = lookahead.End(); if (Skip(TokenKind::WITH)) { ret->withPos = lastToken.Begin(); ret->withExpr = ParseExpr(); if (ret->withExpr) { ret->end = ret->withExpr->end; } } else if (Skip(TokenKind::THROWING)) { ret->throwingPos = lastToken.Begin(); ret->throwingExpr = ParseExpr(); if (ret->throwingExpr) { ret->end = ret->throwingExpr->end; } } return ret; } OwnedPtr ParserImpl::ParseThisOrSuper() const { const std::string& str = TOKENS[static_cast(lastToken.kind)]; auto ret = MakeOwned(); ret->isThis = lastToken.kind == TokenKind::THIS; ret->isSuper = lastToken.kind == TokenKind::SUPER; ret->ref.identifier = str; ret->ref.identifier.SetPos(lastToken.Begin(), lastToken.End()); ret->begin = lastToken.Begin(); ret->end = ret->begin; ret->end.column += static_cast(str.length()); return ret; } OwnedPtr ParserImpl::ParseBreakJumpExpr() { OwnedPtr ret = MakeOwned(); ret->isBreak = true; ret->begin = lookahead.Begin(); ret->end = lastToken.End(); return ret; } OwnedPtr ParserImpl::ParseContinueJumpExpr() { OwnedPtr ret = MakeOwned(); ret->isBreak = false; ret->begin = lookahead.Begin(); ret->end = lastToken.End(); return ret; } OwnedPtr ParserImpl::ParseTupleLitForParenExpr(const Position& leftParenPos) { std::vector> refExprs; std::vector commaPosVector; do { if (Seeing(TokenKind::IDENTIFIER) || SeeingContextualKeyword()) { refExprs.emplace_back(ParseRefExpr(ExprKind::EXPR_IN_TUPLE)); } Next(); commaPosVector.emplace_back(lookahead.Begin()); } while (SeeingIdentifierAndTargetOp({TokenKind::COMMA})); // It's actually a tuple literal. OwnedPtr ret = MakeOwned(); ret->begin = leftParenPos; ret->leftParenPos = leftParenPos; for (auto& it : refExprs) { ret->children.emplace_back(std::move(it)); } ret->commaPosVector.assign(commaPosVector.begin(), commaPosVector.end()); ParseOneOrMoreWithSeparator(TokenKind::COMMA, ret->commaPosVector, [this, &ret]() { if (Seeing(TokenKind::RPAREN) && ret->children.size() > 1) { return; } auto expr = ParseExpr(ExprKind::EXPR_IN_TUPLE); ret->children.emplace_back(std::move(expr)); }); if (!Skip(TokenKind::RPAREN)) { DiagExpectedRightDelimiter("(", ret->leftParenPos); ConsumeUntilAny({TokenKind::NL, TokenKind::SEMI, TokenKind::RPAREN}); } ret->rightParenPos = lastToken.Begin(); ret->end = lastToken.End(); return ret; } OwnedPtr ParserImpl::ParseTupleLitForParenExprComma(const Position& leftParenPos, OwnedPtr expr) { OwnedPtr ret = MakeOwned(); ret->children.emplace_back(std::move(expr)); while (Skip(TokenKind::COMMA)) { ret->commaPosVector.push_back(lastToken.Begin()); if (Seeing(TokenKind::RPAREN) && ret->children.size() > 1) { break; } auto tmpExpr = ParseExpr(ExprKind::EXPR_IN_TUPLE); ret->children.emplace_back(std::move(tmpExpr)); } if (!Skip(TokenKind::RPAREN)) { DiagExpectedRightDelimiter("(", leftParenPos); ConsumeUntilAny({TokenKind::NL, TokenKind::SEMI, TokenKind::RPAREN}); } ret->leftParenPos = leftParenPos; ret->rightParenPos = lookahead.Begin(); ret->begin = leftParenPos; ret->end = lastToken.End(); return ret; } OwnedPtr ParserImpl::ParseLeftParenExpr() { return ParseLeftParenExprInKind(ExprKind::ALL); } OwnedPtr ParserImpl::ParseLeftParenExprInKind(ExprKind ek) { // Parse conventional ParenExpr Position leftParenPos = lookahead.Begin(); if (Skip(TokenKind::RPAREN)) { // LitConstExpr(unit_literal) is parsed from TWO Tokens. OwnedPtr ret = MakeOwned(); ret->begin = leftParenPos; ret->kind = LitConstKind::UNIT; ret->stringValue = sourceManager.GetContentBetween(leftParenPos.fileID, leftParenPos, lastToken.Begin() + 1); ret->end = lastToken.End(); return ret; } if (SeeingIdentifierAndTargetOp({TokenKind::COMMA})) { if (auto ret = ParseTupleLitForParenExpr(leftParenPos); ret) { return ret; } } // Not identifier. OwnedPtr expr = ParseExpr(ek == ExprKind::IF_COND_EXPR ? ExprKind::EXPR_IN_IF_COND_TUPLE : ek == ExprKind::WHILE_COND_EXPR ? ExprKind::EXPR_IN_WHILE_COND_TUPLE : ExprKind::EXPR_IN_TUPLE); if (Seeing(TokenKind::COMMA)) { // It's actually a tuple literal. auto ret = ParseTupleLitForParenExprComma(leftParenPos, std::move(expr)); if (ret) { return ret; } } if (!Skip(TokenKind::RPAREN)) { if (!Is(expr)) { // do not report an error again if the previous ParseExpr fails DiagExpectedRightDelimiter("(", leftParenPos); } ConsumeUntilAny({TokenKind::NL, TokenKind::SEMI, TokenKind::RPAREN}); } Position rightParenPos = lastToken.Begin(); OwnedPtr ret = MakeOwned(); ret->leftParenPos = leftParenPos; ret->rightParenPos = rightParenPos; ret->expr = std::move(expr); ret->begin = leftParenPos; ret->end = lastToken.End(); return ret; } OwnedPtr ParserImpl::ParseFuncArg() { OwnedPtr ret = MakeOwned(); ret->begin = lookahead.Begin(); if (SeeingNamedFuncArgs()) { ret->name = ExpectIdentifierWithPos(*ret); Next(); ret->colonPos = lookahead.Begin(); } else if (Skip(TokenKind::INOUT)) { ret->withInout = true; ret->inoutPos = lastToken.Begin(); } auto tmpExpr = ParseExpr(ExprKind::EXPR_IN_CALLSUFFIX); ret->expr = std::move(tmpExpr); if (ret->expr) { ret->end = ret->expr->end; } return ret; } void ParserImpl::ParseTryWithResource(const ScopeKind& scopeKind, TryExpr& tryExpr) { Next(); tryExpr.lParen = lookahead.Begin(); std::vector> varDeclName; do { if (lastToken.kind == TokenKind::COMMA) { tryExpr.resourceSpecCommaPos.push_back(lastToken.Begin()); } OwnedPtr decl = MakeOwned(); ChainScope cs(*this, decl.get()); decl->isVar = false; decl->isResourceVar = true; decl->identifier = ExpectIdentifierWithPos(*decl); decl->begin = lookahead.Begin(); auto iter = std::find_if( varDeclName.begin(), varDeclName.end(), [&decl](auto& p) { return p.first == decl->identifier; }); if (iter != varDeclName.end()) { DiagRedefinedResourceName({decl->identifier, decl->identifier.Begin()}, *iter); } else { varDeclName.emplace_back(decl->identifier, decl->identifier.Begin()); } if (Skip(TokenKind::COLON)) { decl->colonPos = lastToken.Begin(); decl->type = ParseRefType(); } if (!Skip(TokenKind::ASSIGN)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_assignment, lookahead, ConvertToken(lookahead)); } else { decl->assignPos = lastToken.Begin(); } decl->initializer = ParseExpr(ExprKind::VAR_INIT); decl->end = lastToken.End(); tryExpr.resourceSpec.emplace_back(std::move(decl)); } while (Skip(TokenKind::COMMA)); if (!Skip(TokenKind::RPAREN)) { DiagExpectedRightDelimiter("(", tryExpr.lParen); } else { tryExpr.rParen = lastToken.Begin(); } tryExpr.isDesugaredFromTryWithResources = true; tryExpr.tryBlock = ParseBlock(scopeKind); } OwnedPtr ParserImpl::ParseWhileExpr() { OwnedPtr ret = MakeOwned(); ret->begin = lookahead.Begin(); ret->whilePos = lookahead.Begin(); if (!Skip(TokenKind::LPAREN)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_left_paren, lookahead, ConvertToken(lookahead)); } else { ret->leftParenPos = lastToken.Begin(); } ret->condExpr = ParseExpr(ExprKind::WHILE_COND_EXPR); if (!ret->condExpr->TestAttr(Attribute::HAS_BROKEN)) { (void)CheckCondition(ret->condExpr.get()); } if (!Skip(TokenKind::RPAREN) && !ret->leftParenPos.IsZero()) { if (Is(ret->condExpr) && Skip(TokenKind::WHERE)) { diag.DiagnoseRefactor(DiagKindRefactor::parse_expected_if_let_andand, lastToken); } else { DiagExpectedRightDelimiter("(", ret->leftParenPos); } ret->body = MakeInvalid(lookahead.Begin()); ret->end = lookahead.Begin(); ConsumeUntilIfExprEnd(); return ret; } ret->rightParenPos = lastToken.Begin(); ret->body = ParseBlock(ScopeKind::FUNC_BODY); if (ret->body->TestAttr(Attribute::IS_BROKEN)) { ret->EnableAttr(Attribute::IS_BROKEN); } ret->end = ret->body->end; return ret; } OwnedPtr ParserImpl::ParseForInExpr() { Position begin = lookahead.Begin(); if (!Skip(TokenKind::LPAREN)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_left_paren, lookahead, ConvertToken(lookahead)); ConsumeUntilIfExprEnd(); return MakeInvalid(lastToken.End()); } OwnedPtr ret = MakeOwned(); ret->begin = begin; ret->leftParenPos = lastToken.Begin(); ret->pattern = ParsePattern({}, false, true); ret->pattern->ctxExpr = ret->inExpression.get(); if (!Skip(TokenKind::IN)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_in_forin_expression, lookahead, ConvertToken(lookahead)); ConsumeUntilIfExprEnd(); ret->EnableAttr(Attribute::IS_BROKEN); ret->end = lastToken.End(); return ret; } ret->inPos = lookahead.Begin(); ret->inExpression = ParseExpr(); if (Skip(TokenKind::WHERE)) { ret->wherePos = lookahead.Begin(); ret->patternGuard = ParseExpr(); } if (!Skip(TokenKind::RPAREN)) { DiagExpectedRightDelimiter("(", ret->leftParenPos); ConsumeUntilIfExprEnd(); ret->EnableAttr(Attribute::IS_BROKEN); ret->end = lastToken.End(); return ret; } ret->rightParenPos = lastToken.Begin(); SkipBlank(TokenKind::NL); ret->body = ParseBlock(ScopeKind::FUNC_BODY); ret->end = ret->body->end; return ret; } OwnedPtr ParserImpl::ParseDoWhileExpr() { OwnedPtr doWhileExpr = MakeOwned(); doWhileExpr->begin = lookahead.Begin(); doWhileExpr->doPos = lookahead.Begin(); doWhileExpr->body = ParseBlock(ScopeKind::FUNC_BODY); if (!Skip(TokenKind::WHILE)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_while_in_do_while, lookahead, ConvertToken(lookahead)); ConsumeUntilAny({TokenKind::NL, TokenKind::WHILE}); if (!Skip(TokenKind::WHILE)) { doWhileExpr->EnableAttr(Attribute::IS_BROKEN); doWhileExpr->end = lastToken.End(); return doWhileExpr; } } doWhileExpr->whilePos = lookahead.Begin(); if (!Skip(TokenKind::LPAREN)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_left_paren, lookahead, ConvertToken(lookahead)); } else { doWhileExpr->leftParenPos = lastToken.Begin(); } doWhileExpr->condExpr = ParseExpr(); if (!Skip(TokenKind::RPAREN)) { if (!doWhileExpr->leftParenPos.IsZero()) { DiagExpectedRightDelimiter("(", doWhileExpr->leftParenPos); } ConsumeUntil(TokenKind::NL); } doWhileExpr->rightParenPos = lastToken.Begin(); doWhileExpr->end = lastToken.End(); return doWhileExpr; } OwnedPtr ParserImpl::ParseWildcardExpr() { Next(); auto expr = MakeOwned(lastToken.Begin()); return expr; } OwnedPtr ParserImpl::ParseRefExpr(ExprKind ek) { OwnedPtr ret = MakeOwned(); ret->isThis = lookahead.Value() == "this"; ret->isSuper = lookahead.Value() == "super"; ret->begin = lookahead.Begin(); ret->ref.identifier = ExpectIdentifierWithPos(*ret); ret->end = ret->begin + lookahead.Value().size(); if (!Seeing(TokenKind::LT)) { return ret; } ParserScope scope(*this); ret->leftAnglePos = lookahead.Begin(); Next(); // collecting diagnoses in `ParseTypeArguments` and storing these diagnoses to a cache diag.Prepare(); auto [isGenericArgList, typeArguments] = ParseTypeArguments(ek); if (isGenericArgList) { // parse type success, handle those diagnoses which were stored in the cache ret->typeArguments = std::move(typeArguments); ret->rightAnglePos = lastToken.Begin(); diag.Commit(); } else { diag.ClearTransaction(); // if it is like: if a= d), reset parser. scope.ResetParserScope(); ret->leftAnglePos = INVALID_POSITION; ret->rightAnglePos = INVALID_POSITION; } if (ret->rightAnglePos != INVALID_POSITION) { ret->end = ret->rightAnglePos; ret->end.column += 1; } return ret; } OwnedPtr ParserImpl::ParseSpawnExpr() { OwnedPtr ret = MakeOwned(); ret->spawnPos = lastToken.Begin(); ret->begin = lookahead.Begin(); auto tok = lookahead; if (Seeing(TokenKind::LPAREN)) { if (newlineSkipped) { (void)ParseDiagnoseRefactor( DiagKindRefactor::parse_newline_not_allowed_between_spawn_and_argument, firstNLPosition); } if (Skip(TokenKind::LPAREN)) { ret->leftParenPos = lastToken.Begin(); ret->arg = ParseExpr(); } if (!Skip(TokenKind::RPAREN) && !ret->TestAttr(Attribute::HAS_BROKEN)) { ret->EnableAttr(Attribute::HAS_BROKEN); DiagExpectedRightDelimiter("(", ret->leftParenPos); } ret->rightParenPos = lastToken.Begin(); } if (SeeingBuiltinAnnotation()) { ret->task = ParseAnnotationLambdaExpr(true); } else if (Skip(TokenKind::LCURL)) { ret->task = ParseLambdaExprWithTrailingClosure(); } else { DiagExpectedExpression(); ret->task = MakeInvalid(lookahead.Begin()); } if (!ret->task || ret->task->astKind != ASTKind::LAMBDA_EXPR) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_left_brace, tok, ConvertToken(lookahead)); ret->EnableAttr(Attribute::IS_BROKEN); } if (ret->task && ret->task->astKind == ASTKind::LAMBDA_EXPR) { auto task = As(ret->task.get()); CJC_NULLPTR_CHECK(task); if (!task->funcBody->paramLists[0].get()->params.empty()) { DiagExpectedNoArgumentsInSpawn(task->funcBody->paramLists[0].get()->params, ret->begin); ret->EnableAttr(Attribute::IS_BROKEN); } } ret->end = lastToken.End(); return ret; } OwnedPtr ParserImpl::ParseSynchronizedExpr() { OwnedPtr ret = MakeOwned(); ret->syncPos = lastToken.Begin(); ret->begin = lookahead.Begin(); if (!Skip(TokenKind::LPAREN)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_left_paren, lookahead, ConvertToken(lookahead)); ret->EnableAttr(Attribute::HAS_BROKEN); } ret->leftParenPos = lastToken.Begin(); ret->mutex = ParseExpr(); if (!Skip(TokenKind::RPAREN) && !ret->TestAttr(Attribute::HAS_BROKEN)) { DiagExpectedRightDelimiter("(", ret->leftParenPos); ret->EnableAttr(Attribute::HAS_BROKEN); } ret->rightParenPos = lastToken.Begin(); ret->body = ParseBlock(); ret->end = ret->body->end; return ret; } OwnedPtr ParserImpl::ParseAnnotationLambdaExpr(bool isTailClosure) { std::vector> annos; ParseAnnotations(annos); Next(); auto ret = isTailClosure ? ParseLambdaExprWithTrailingClosure() : ParseLambdaExpr(); for (auto& it : annos) { if (it->kind == AnnotationKind::NUMERIC_OVERFLOW) { ret->EnableAttr(Attribute::NUMERIC_OVERFLOW); ret->overflowStrategy = it->overflowStrategy; } else if (it->kind == AnnotationKind::ENSURE_PREPARED_TO_MOCK) { ret->EnableAttr(Attribute::MOCK_SUPPORTED); } else { DiagExpectedDeclaration(ret->begin, "lambda expression"); } } return ret; } OwnedPtr ParserImpl::ParseMacroExprOrLambdaExpr() { if (SeeingAnnotationLambdaExpr()) { return ParseAnnotationLambdaExpr(); } if (SeeingBuiltinAnnotation()) { DiagExpectedExpression(); return MakeInvalid(lastToken.End()); } return ParseMacroCall(); } OwnedPtr ParserImpl::ParseFuncParam() { auto consumeTarget = [this]() { return SeeingAny({TokenKind::DOUBLE_ARROW, TokenKind::COLON, TokenKind::COMMA, TokenKind::NL, TokenKind::SEMI, TokenKind::RCURL}) || SeeingCombinator(combinedDoubleArrow); }; auto param = MakeOwned(); ChainScope c(*this, param.get()); // add process of '_' if (Skip(TokenKind::WILDCARD)) { param->identifier = "_"; param->identifier.SetPos(lookahead.Begin(), lookahead.End()); } else { param->identifier = ExpectIdentifierWithPos(*param); } if (param->identifier == INVALID_IDENTIFIER) { chainedAST.back()->EnableAttr(Attribute::IS_BROKEN); ConsumeUntilAny(consumeTarget, false); } param->isVar = false; param->begin = lookahead.Begin(); if (Skip(TokenKind::COLON)) { param->colonPos = lastToken.Begin(); param->type = ParseType(); if (param->type) { param->end = param->type->end; } } else { param->end = param->identifier.GetRawEndPos(); } return param; } OwnedPtr ParserImpl::ParseFuncParamListInLambdaExpr() { auto paramList = MakeOwned(); ChainScope cs(*this, paramList.get()); if (SeeingInvaildParamListInLambdaExpr()) { paramList->begin = lookahead.Begin(); paramList->end = lookahead.Begin(); return paramList; } // Parse paramList. ParseZeroOrMoreSepTrailing( [¶mList](const Position& pos) { paramList->params.back()->commaPos = pos; }, [this, ¶mList]() { auto param = ParseFuncParam(); if (param->TestAttr(Attribute::IS_BROKEN)) { paramList->EnableAttr(Attribute::IS_BROKEN); } paramList->params.emplace_back(std::move(param)); }, TokenKind::DOUBLE_ARROW); if (!paramList->params.empty()) { paramList->begin = paramList->params.front()->begin; paramList->end = paramList->params.back()->end; } return paramList; } OwnedPtr ParserImpl::ParseFuncBodyInLambdaExpr(bool isTailClosure) { auto funcBody = MakeOwned(); ChainScope cs(*this, funcBody.get()); auto paramList = ParseFuncParamListInLambdaExpr(); if (paramList->TestAttr(Attribute::IS_BROKEN)) { chainedAST.back()->EnableAttr(Attribute::HAS_BROKEN); } if (!paramList->params.empty() && !Skip(TokenKind::DOUBLE_ARROW) && !SkipCombinedDoubleArrow()) { if (!chainedAST.back()->TestAttr(Attribute::HAS_BROKEN)) { ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_double_arrow_in_lambda, lookahead, ConvertToken(lookahead)); } ConsumeUntilAny( [this]() { return SeeingAny({TokenKind::NL, TokenKind::SEMI, TokenKind::DOUBLE_ARROW, TokenKind::RCURL}) || SeeingCombinator(combinedDoubleArrow); }, false); if (!Skip(TokenKind::DOUBLE_ARROW)) { (void)SkipCombinedDoubleArrow(); } } else { if (paramList->params.empty() && !isTailClosure) { // TailClosure can omit => if param is empty ,and other lambdaExpr is not allowed. bool hasDoubleArrow = lastToken.kind == TokenKind::DOUBLE_ARROW || Skip(TokenKind::DOUBLE_ARROW) || SkipCombinedDoubleArrow(); bool hasWildard = Seeing({TokenKind::WILDCARD, TokenKind::DOUBLE_ARROW}) || Seeing({TokenKind::WILDCARD, TokenKind::COMMA}) || SeeingTokenAndCombinator(TokenKind::WILDCARD, combinedDoubleArrow); if (!hasDoubleArrow && !hasWildard) { ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_double_arrow_in_lambda, lookahead, ConvertToken(lookahead)); } } if (lastToken.kind == TokenKind::DOUBLE_ARROW || Skip(TokenKind::DOUBLE_ARROW) || SkipCombinedDoubleArrow()) { funcBody->doubleArrowPos = lastToken.Begin(); } } funcBody->begin = lastToken.Begin(); funcBody->paramLists.emplace_back(std::move(paramList)); funcBody->body = ParseExpressionOrDeclarations(ScopeKind::FUNC_BODY); funcBody->end = funcBody->body->end; return funcBody; } OwnedPtr ParserImpl::ParseLambdaExpr() { OwnedPtr ret = ParseBaseLambdaExpr(); ChainScope cs(*this, ret.get()); ret->funcBody = ParseFuncBodyInLambdaExpr(false); ret->end = ret->funcBody->end; ret->funcBody->body->leftCurlPos = ret->begin; return ret; } OwnedPtr ParserImpl::ParseVArrayExpr() { OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); ret->begin = lookahead.Begin(); if (!Seeing(TokenKind::LT)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_varray_type_parameter, lastToken.End()); return MakeOwned(lookahead.Begin()); } ret->type = ParseVarrayType(); ret->isValueArray = true; if (!Skip(TokenKind::LPAREN)) { // Only 'VArray (expr)' or 'VArray { i => expr }' is valid grammar. if (!ret->type->TestAttr(Attribute::IS_BROKEN) && !Seeing(TokenKind::LCURL)) { (void)ParseDiagnoseRefactor(DiagKindRefactor::parse_varray_with_paren, ret->type->end); ret->EnableAttr(Attribute::IS_BROKEN); } ret->end = ret->type->end; return ret; } ret->leftParenPos = lookahead.Begin(); ParseZeroOrMoreSepTrailing( [&ret](const Position& pos) { ret->args.back()->commaPos = pos; }, [this, &ret]() { ret->args.emplace_back(ParseFuncArg()); }, TokenKind::RPAREN); if (!Skip(TokenKind::RPAREN)) { ret->EnableAttr(Attribute::HAS_BROKEN); DiagExpectedRightDelimiter("(", ret->leftParenPos); } ret->rightParenPos = lookahead.Begin(); ret->end = lookahead.End(); return ret; } OwnedPtr ParserImpl::ParseLambdaExprWithTrailingClosure() { OwnedPtr ret = ParseBaseLambdaExpr(); ChainScope cs(*this, ret.get()); ret->funcBody = ParseFuncBodyInLambdaExpr(true); ret->end = ret->funcBody->end; ret->funcBody->body->leftCurlPos = ret->begin; return ret; } OwnedPtr ParserImpl::ParseBaseLambdaExpr() { auto ret = MakeOwned(); ret->begin = lastToken.Begin(); if (SeeingInvaildOperaterInLambdaExpr()) { Skip(TokenKind::IDENTIFIER); SkipKeyWordIdentifier(); Peek(); ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_ccd_in_lambda, lookahead, ConvertToken(lookahead)); ConsumeUntilAny( [this]() { return SeeingAny({TokenKind::NL, TokenKind::SEMI, TokenKind::DOUBLE_ARROW, TokenKind::RCURL}) || SeeingCombinator(combinedDoubleArrow); }, false); } return ret; } OwnedPtr ParserImpl::ParseTypeConvExpr() { OwnedPtr ret = MakeOwned(); ret->begin = lookahead.Begin(); OwnedPtr type = MakeOwned(); type->begin = lookahead.Begin(); type->end = lookahead.End(); type->str = lookahead.Value(); type->kind = TOKENKIND_TO_PRIMITIVE_TYPEKIND_MAP.at(lookahead.kind); Next(); ret->type = std::move(type); if (!Skip(TokenKind::LPAREN)) { DiagExpectedLeftParenAfter(ret->begin, lastToken.Value()); return MakeOwned(lookahead.Begin()); } ret->leftParenPos = lastToken.Begin(); ret->expr = ParseExpr(); if (!Skip(TokenKind::RPAREN)) { DiagExpectedRightDelimiter("(", ret->leftParenPos); ConsumeUntilAny({TokenKind::NL, TokenKind::SEMI, TokenKind::RPAREN}); return MakeOwned(lookahead.Begin()); } ret->rightParenPos = lastToken.Begin(); ret->end = lastToken.End(); return ret; } cangjie_compiler-1.0.7/src/Parse/ParseCJMPDecl.cpp000066400000000000000000000214751510705540100217010ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * This file implements parse and check CJMP Decl. */ #include "ParserImpl.h" #include "cangjie/AST/Utils.h" using namespace Cangjie; using namespace AST; namespace { // Check whether the decl has body or initializer. bool HasDefault(const AST::Decl& decl) { // Check on match CJMP decl for platform member. if (decl.TestAttr(Attribute::PLATFORM)) { return true; } switch (decl.astKind) { case ASTKind::FUNC_DECL: { auto& funcDecl = StaticCast(decl); if (funcDecl.funcBody->body) { return true; } break; } case ASTKind::PRIMARY_CTOR_DECL: { auto& pcDecl = StaticCast(decl); if (pcDecl.funcBody->body) { return true; } break; } case ASTKind::PROP_DECL: { auto& propDecl = StaticCast(decl); if (!propDecl.getters.empty() || !propDecl.setters.empty()) { return true; } break; } case ASTKind::VAR_DECL: { auto& varDecl = StaticCast(decl); if (varDecl.initializer) { return true; } break; } default: ; } return false; } void SetCJMPAttrs(AST::Decl& decl) { if (!decl.TestAttr(Attribute::COMMON)) { return; } if (HasDefault(decl)) { decl.EnableAttr(Attribute::COMMON_WITH_DEFAULT); } } // For cjmp diag error info. const std::unordered_map KIND_TO_STR = { {ASTKind::VAR_DECL, "variable"}, {ASTKind::VAR_WITH_PATTERN_DECL, "variable"}, {ASTKind::PRIMARY_CTOR_DECL, "primary constructor"}, {ASTKind::FUNC_DECL, "function"}, {ASTKind::CLASS_DECL, "class"}, {ASTKind::INTERFACE_DECL, "interface"}, {ASTKind::STRUCT_DECL, "struct"}, {ASTKind::ENUM_DECL, "enum"}, {ASTKind::PROP_DECL, "property"}, {ASTKind::EXTEND_DECL, "extend"}, {ASTKind::ENUM_PATTERN, "enum"}, {ASTKind::TUPLE_PATTERN, "tuple"}, {ASTKind::WILDCARD_PATTERN, "wildcard"}, {ASTKind::FUNC_PARAM, "parameter"}, }; std::string GetDiagKind(const AST::Node& node) { // init constructor if (node.astKind == ASTKind::FUNC_DECL && node.TestAttr(Attribute::CONSTRUCTOR)) { return "constructor"; } auto it = KIND_TO_STR.find(node.astKind); if (it != KIND_TO_STR.end()) { return it->second; } CJC_ASSERT(false); return "unknown decl"; } } // namespace void MPParserImpl::SetCompileOptions(const GlobalOptions& opts) { this->compileCommon = (opts.outputMode == GlobalOptions::OutputMode::CHIR); this->compilePlatform = (opts.commonPartCjo != std::nullopt); } bool MPParserImpl::CheckCJMPModifiers(const std::set &modifiers) const { auto currentFile = ref->currentFile; if (ref->HasModifier(modifiers, TokenKind::PLATFORM)) { if (!compilePlatform) { ref->diag.DiagnoseRefactor(DiagKindRefactor::parse_platform_in_non_platform_file, *currentFile); return false; } if (currentFile->isCommon) { ref->diag.DiagnoseRefactor(DiagKindRefactor::parse_common_and_platform_in_the_same_file, *currentFile); return false; } if (currentFile->package != nullptr) { currentFile->package->hasPlatform = true; } currentFile->isPlatform = true; } if (ref->HasModifier(modifiers, TokenKind::COMMON)) { if (!compileCommon) { ref->diag.DiagnoseRefactor(DiagKindRefactor::parse_common_in_non_common_file, *currentFile); return false; } if (currentFile->isPlatform) { ref->diag.DiagnoseRefactor(DiagKindRefactor::parse_common_and_platform_in_the_same_file, *currentFile); return false; } if (currentFile->package != nullptr) { currentFile->package->hasCommon = true; } currentFile->isCommon = true; } return true; } void MPParserImpl::CheckCJMPDecl(AST::Decl& decl) const { if (!compileCommon && !compilePlatform) { return; } if (!CheckCJMPModifiersOf(decl)) { return; } // Enable COMMON_WITH_DEFAULT attr for func/constructor/var SetCJMPAttrs(decl); // Check sema rules if (decl.astKind == ASTKind::INTERFACE_DECL) { // Check that the member of platform interface must have the body CheckPlatformInterface(StaticCast(decl)); } else if (decl.astKind == ASTKind::PRIMARY_CTOR_DECL) { auto& fn = StaticCast(decl); CheckCJMPFuncParams(fn, fn.funcBody.get()); } else if (decl.astKind == ASTKind::FUNC_DECL) { auto& fn = StaticCast(decl); CheckCJMPFuncParams(fn, fn.funcBody.get()); } } bool MPParserImpl::HasCJMPModifiers(const AST::Modifier& modifier) const { if (!compileCommon && !compilePlatform) { return false; } return (modifier.modifier == TokenKind::COMMON || modifier.modifier == TokenKind::PLATFORM); } bool MPParserImpl::CheckCJMPModifiersOf(const AST::Decl& decl) const { if (decl.IsCommonOrPlatform()) { auto kind = decl.TestAttr(Attribute::COMMON) ? "common" : "platform"; // generic decl if (decl.TestAttr(Attribute::GENERIC)) { ref->diag.DiagnoseRefactor(DiagKindRefactor::parse_cjmp_generic_decl, decl, kind); return false; } // tuple, enum, _ pattern if (decl.astKind == ASTKind::VAR_WITH_PATTERN_DECL && decl.TestAttr(Attribute::COMMON)) { auto& varDecl = StaticCast(decl); auto& pattern = *(varDecl.irrefutablePattern); ref->diag.DiagnoseRefactor(DiagKindRefactor::parse_cjmp_pattern_decl, pattern, GetDiagKind(pattern), kind); return false; } // static init if (IsStaticInitializer(decl)) { ref->diag.DiagnoseRefactor(DiagKindRefactor::parse_cjmp_static_init, decl, kind); return false; } } bool ret = true; // Check whether modifiers are same between members and outer decl. for (auto &member : decl.GetMemberDeclPtrs()) { ret = CheckCJMPModifiersBetween(*member, decl) && ret; } return ret; } /** * Checks whether the cjmp modifiers of two declarations are same. * * @param inner The inner declaration, member or member param decl. * @param outer The outer declaration, nominal decl or primary constructor. * @return True if the modifiers are same. * */ bool MPParserImpl::CheckCJMPModifiersBetween(const AST::Decl& inner, const AST::Decl& outer) const { auto p0 = GetDiagKind(inner) + " " + inner.identifier.Val(); if (inner.TestAttr(Attribute::COMMON) && !outer.TestAttr(Attribute::COMMON)) { DiagOuterDeclMissMatch(inner, p0, "common", GetDiagKind(outer), "common"); return false; } if (inner.TestAttr(Attribute::PLATFORM) && !outer.TestAttr(Attribute::PLATFORM)) { DiagOuterDeclMissMatch(inner, p0, "platform", GetDiagKind(outer), "platform"); return false; } return true; } void MPParserImpl::CheckCJMPFuncParams(AST::Decl& decl, const Ptr funcBody) const { if (!funcBody || funcBody->paramLists.size() != 1) { return; } auto& params = funcBody->paramLists[0]->params; for (size_t index = 0; index < params.size(); index++) { if (params[index]->assignment && decl.TestAttr(Attribute::PLATFORM)) { ref->diag.DiagnoseRefactor(DiagKindRefactor::parse_platform_function_parameter_cannot_have_default_value, *params[index], GetDiagKind(decl)); decl.EnableAttr(Attribute::IS_BROKEN); } CheckCJMPModifiersBetween(*params[index], decl); } } void MPParserImpl::CheckPlatformInterface(const AST::InterfaceDecl& decl) const { if (!decl.TestAttr(Attribute::PLATFORM)) { return; } // Check that the general member of platform interface must have the body for (auto& member : decl.GetMemberDeclPtrs()) { if (!HasDefault(*member)) { ref->diag.DiagnoseRefactor(DiagKindRefactor::parse_platform_member_must_have_implementation, *member, member->identifier.Val(), decl.identifier.Val()); } } } void MPParserImpl::DiagOuterDeclMissMatch(const AST::Node& node, const std::string& p0, const std::string& p1, const std::string& p2, const std::string& p3) const { ref->diag.DiagnoseRefactor(DiagKindRefactor::parse_cjmp_outdecl_miss_match, node, p0, p1, p2, p3); } cangjie_compiler-1.0.7/src/Parse/ParseDecl.cpp000066400000000000000000003026221510705540100212230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * This file implements pattern decl parse apis. */ #include "ParserImpl.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Parse/ParseModifiersRules.h" using namespace Cangjie; using namespace AST; namespace { constexpr std::string_view ELLIPSIS("..."); } // namespace OwnedPtr ParserImpl::ParseDecl(ScopeKind scopeKind, std::set modifiers, PtrVector annos) { if (SeeingBuiltinAnnotation() && !modifiers.empty() && annos.empty()) { DiagExpectNoModifierBefore(*SortModifierByPos(modifiers)[0], "annotation"); } if ((newlineSkipped && Seeing(TokenKind::NL)) || Seeing(TokenKind::SEMI)) { SkipNLOrSemi(); } ParseAnnotations(annos); ParseModifiers(modifiers); CheckOverflowAnno(annos, scopeKind); ffiParser->CheckAnnotations(annos); CheckAnnotationAnno(annos, modifiers); // Enum construtor. if (SeeingEnumConstructor(scopeKind)) { return ParseEnumConstructor(modifiers, annos); } // Macro expression. if (SeeingMacroCallDecl()) { return ParseMacroCall(scopeKind, modifiers, std::move(annos)); } if (auto tokenKind = Peek().kind; declHandlerMap.count(tokenKind) != 0) { auto ret = declHandlerMap[tokenKind](this, scopeKind, modifiers, std::move(annos)); if (scopeKind == ScopeKind::TOPLEVEL) { ret->EnableAttr(Attribute::GLOBAL); } if (HasModifier(modifiers, TokenKind::CONST) && ret->astKind == ASTKind::FUNC_DECL) { StaticCast(*ret).isConst = true; } SetDeclBeginPos(*ret); mpImpl->CheckCJMPDecl(*ret); return ret; } if (SeeingPropMember(scopeKind)) { return ParsePropMemberDecl(modifiers); } // Primary constructor decl. if (SeeingPrimaryConstructor(scopeKind)) { return ParsePrimaryConstructor(scopeKind, modifiers, std::move(annos)); } // Const Variable. if (HasModifier(modifiers, TokenKind::CONST) && lastToken == "const") { auto ret = ParseConstVariable(scopeKind, modifiers, std::move(annos)); SetDeclBeginPos(*ret); return ret; } // Finalizer if (SeeingFinalizer()) { return ParseFinalizer(scopeKind, modifiers, std::move(annos)); } auto ret = MakeOwned(lookahead.Begin()); DiagExpectedDeclaration(scopeKind); ret->EnableAttr(Attribute::IS_BROKEN); ImplementConsumeStrategy(scopeKind); ret->end = lastToken.End(); return ret; } OwnedPtr ParserImpl::ParseIrrefutablePattern(const std::set& attributes, bool isVar, bool inDecl) { if (Skip(TokenKind::WILDCARD)) { Position begin = lookahead.Begin(); OwnedPtr ret = MakeOwned(); ret->begin = begin; ret->end = lookahead.End(); return ret; } if (Seeing(TokenKind::LPAREN)) { return ParseTuplePattern(false, attributes, isVar, inDecl); } if (SeeingIdentifierAndTargetOp({TokenKind::LPAREN, TokenKind::DOT, TokenKind::LT})) { return ParseEnumPattern(attributes, isVar, inDecl); } if (Seeing(TokenKind::IDENTIFIER) || SeeingContextualKeyword()) { Position begin = lookahead.Begin(); auto identifier = ParseIdentifierFromToken(lookahead); return ParseVarPattern(attributes, std::move(identifier), begin, isVar); } // It is internal bug if reach here. CJC_ABORT(); return MakeOwned(lookahead.Begin()); } OwnedPtr ParserImpl::ParseVarWithPatternDecl( ScopeKind scopeKind, const std::set& modifiers, const Token& keyToken) { OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); bool isVar = keyToken == "var"; ret->isVar = isVar; ret->isConst = keyToken == "const"; ret->keywordPos = keyToken.Begin(); ret->begin = keyToken.Begin(); auto attrs = CheckDeclModifiers(modifiers, scopeKind, DefKind::VARIABLE); for (auto& it : attrs) { ret->EnableAttr(it); } ret->modifiers.insert(modifiers.begin(), modifiers.end()); ret->irrefutablePattern = ParseIrrefutablePattern(attrs, isVar, true); ret->end = lookahead.End(); if ((ret->irrefutablePattern->astKind == ASTKind::TUPLE_PATTERN || ret->irrefutablePattern->astKind == ASTKind::ENUM_PATTERN || ret->irrefutablePattern->astKind == ASTKind::WILDCARD_PATTERN) && (scopeKind == ScopeKind::CLASS_BODY || scopeKind == ScopeKind::STRUCT_BODY)) { DiagParseIllegalDeclarationPattern(ret, scopeKind); } ret->end = ret->irrefutablePattern->end; ParseTypeAndExpr(ret.get()); // Set `GLOBAL` attribute for toplevel declares in patterns. if (scopeKind == ScopeKind::TOPLEVEL) { auto setGlobalAttr = [](Ptr n) { CJC_NULLPTR_CHECK(n); n->EnableAttr(Attribute::GLOBAL); return VisitAction::WALK_CHILDREN; }; Walker(ret->irrefutablePattern.get(), setGlobalAttr).Walk(); } if (!ret->isConst && HasModifier(modifiers, TokenKind::CONST)) { // diag modifier DiagUnExpectedModifierOnDeclaration(*ret); return ret; } if (ret->initializer == nullptr) { if (ret->isConst) { DiagConstVariableExpectedInitializer(*ret); } else if (scopeKind != ScopeKind::TOPLEVEL && ret->type == nullptr) { DiagExpectedTypeOrInitializerInPattern(*ret); } else if (scopeKind == ScopeKind::TOPLEVEL && !diag.ignoreScopeCheck) { DiagExpectedInitializerForToplevelVar(*ret); } } return ret; } OwnedPtr ParserImpl::ParseVarOrLetOrConst(ScopeKind scopeKind, const std::set& modifiers, std::vector> annos, const Token& keyToken) { OwnedPtr ret; if (Seeing(TokenKind::WILDCARD) || Seeing(TokenKind::LPAREN) || (SeeingIdentifierAndTargetOp({TokenKind::LPAREN, TokenKind::DOT, TokenKind::LT}))) { ret = ParseVarWithPatternDecl(scopeKind, modifiers, keyToken); } else if (Seeing(TokenKind::IDENTIFIER) || SeeingContextualKeyword()) { ret = ParseVarDecl(scopeKind, modifiers, keyToken); } else { DiagExpectedIdentifierOrPattern( keyToken == "var", keyToken.Begin(), keyToken == "const"); ret = MakeOwned(lookahead.Begin()); ConsumeUntil(TokenKind::NL); } ret->annotations = std::move(annos); if (scopeKind == ScopeKind::TOPLEVEL) { ret->EnableAttr(Attribute::GLOBAL); } return ret; } OwnedPtr ParserImpl::ParseVarOrLet( ScopeKind scopeKind, const std::set& modifiers, std::vector> annos) { Next(); Token keyToken = lookahead; // Var or let. return ParseVarOrLetOrConst(scopeKind, modifiers, std::move(annos), keyToken); } OwnedPtr ParserImpl::ParseConstVariable( ScopeKind scopeKind, const std::set& modifiers, std::vector> annos) { Token keyToken = lastToken; // const. return ParseVarOrLetOrConst(scopeKind, modifiers, std::move(annos), keyToken); } void ParserImpl::CheckVarDeclModifiers( std::set modifiers, Ptr varDecl, ScopeKind scopeKind, const Token& keyToken) { if (HasModifier(modifiers, TokenKind::UNSAFE) || HasModifier(modifiers, TokenKind::FOREIGN)) { SetUnsafe(varDecl, modifiers); } if (!varDecl->isConst && HasModifier(modifiers, TokenKind::CONST)) { // Unexpected modifier: var or let variable has const. DiagUnExpectedModifierOnDeclaration(*varDecl); } else if (varDecl->isConst && (scopeKind == ScopeKind::CLASS_BODY || scopeKind == ScopeKind::STRUCT_BODY) && !HasModifier(modifiers, TokenKind::STATIC)) { DiagConstVariableExpectedStatic(keyToken); } else if (varDecl->initializer == nullptr && !varDecl->TestAttr(Attribute::HAS_BROKEN)) { if (varDecl->isConst) { DiagConstVariableExpectedInitializer(*varDecl); } else if (scopeKind != ScopeKind::TOPLEVEL && varDecl->type == nullptr && !HasModifier(modifiers, TokenKind::COMMON)) { DiagExpectedOneOfTypeOrInitializer(*varDecl, varDecl->identifier); } else if (scopeKind == ScopeKind::TOPLEVEL && !HasModifier(modifiers, TokenKind::FOREIGN) && (varDecl->type == nullptr || !diag.ignoreScopeCheck) && !HasModifier(modifiers, TokenKind::COMMON)) { DiagExpectedInitializerForToplevelVar(*varDecl); } } } OwnedPtr ParserImpl::ParseVarDecl( const ScopeKind& scopeKind, const std::set& modifiers, const Token& keyToken) { OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); ret->begin = keyToken.Begin(); ret->isVar = keyToken == "var"; ret->isConst = keyToken == "const"; ret->keywordPos = keyToken.Begin(); CheckDeclarationInScope(scopeKind, DefKind::VARIABLE); ret->identifier = ExpectIdentifierWithPos(*ret); ret->end = lookahead.End(); ParseTypeAndExpr(ret.get()); auto attrs = CheckDeclModifiers(modifiers, scopeKind, DefKind::VARIABLE); for (auto& it : attrs) { ret->EnableAttr(it); } ret->modifiers.insert(modifiers.begin(), modifiers.end()); CheckVarDeclModifiers(modifiers, ret.get(), scopeKind, keyToken); bool hasNoType = ret->type == nullptr; bool isCommonPlatform = ret->IsCommonOrPlatform(); if (hasNoType && isCommonPlatform) { auto kind = ret->TestAttr(Attribute::COMMON) ? "common" : "platform"; auto keyword = ret->isVar ? "var" : "let"; ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_type_with_cjmp_var, *ret, kind, keyword); } return ret; } void ParserImpl::ParseTypeAndExpr(Ptr const ret) { if (Skip(TokenKind::COLON)) { ret->colonPos = lookahead.Begin(); ret->type = ParseType(); ret->end = ret->type->end; if (ret->type->TestAttr(Attribute::IS_BROKEN) || ret->type->TestAttr(Attribute::HAS_BROKEN)) { ret->EnableAttr(Attribute::HAS_BROKEN); } } if (Skip(TokenKind::ASSIGN)) { ret->assignPos = lookahead.Begin(); ret->initializer = ParseExpr(ExprKind::VAR_INIT); if (ret->initializer) { ret->end = ret->initializer->end; } } } void ParserImpl::CheckGetterAnnotations(std::vector>& annos, const Ptr getter) { for (auto& anno : std::as_const(annos)) { DiagUnexpectedAnnoOn(*anno, getter->begin, anno->identifier, "get"); } } void ParserImpl::CheckSetterAnnotations(std::vector>& annos, const Ptr setter) { for (auto& anno : std::as_const(annos)) { if (anno->kind != AnnotationKind::DEPRECATED) { DiagUnexpectedAnnoOn(*anno, setter->begin, anno->identifier, "set"); } } } void ParserImpl::ParsePropBody(const std::set& modifiers, PropDecl& propDecl) { propDecl.leftCurlPos = lastToken.Begin(); std::vector> getPropMemberDecls; std::vector> setPropMemberDecls; Ptr setter{nullptr}; Ptr getter{nullptr}; while (true) { if (Skip(TokenKind::RCURL)) { propDecl.rightCurlPos = lastToken.Begin(); break; } if (DetectPrematureEnd()) { DiagExpectedRightDelimiter("{", propDecl.leftCurlPos); break; } std::vector> annos; ParseAnnotations(annos); std::set modis; ParseModifiers(modis); if (SeeingPropMember()) { if (lookahead == "get" && getter) { DiagDuplicatedGetOrSet(*getter, propDecl); } if (lookahead == "set" && setter) { DiagDuplicatedGetOrSet(*setter, propDecl); } if (lookahead == "get") { auto res = ParsePropMemberDecl(modis); CheckGetterAnnotations(annos, res); res->annotations = std::move(annos); res->outerDecl = &propDecl; getPropMemberDecls.emplace_back(std::move(res)); getter = getPropMemberDecls.back().get(); SkipSemi(); } else { auto res = ParsePropMemberDecl(modis); CheckSetterAnnotations(annos, res); res->annotations = std::move(annos); res->outerDecl = &propDecl; setPropMemberDecls.emplace_back(std::move(res)); setter = setPropMemberDecls.back().get(); SkipSemi(); } } else { DiagExpectedGetOrSetInProp(propDecl.begin); ConsumeUntilAny({TokenKind::NL, TokenKind::SEMI}); } } propDecl.getters = std::move(getPropMemberDecls); propDecl.setters = std::move(setPropMemberDecls); propDecl.rightCurlPos = lastToken.Begin(); propDecl.modifiers.insert(modifiers.begin(), modifiers.end()); propDecl.end = lookahead.End(); } OwnedPtr ParserImpl::ParsePropMemberDecl(const std::set& modifiers) { OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); ret->identifier.SetPos(lookahead.Begin(), lookahead.End()); ret->keywordPos = lookahead.Begin(); ret->begin = lookahead.Begin(); ret->identifier = lookahead.Value(); bool isGetter = lookahead == "get"; isGetter ? (ret->isGetter = true) : (ret->isSetter = true); Next(); ret->begin = lookahead.Begin(); if (!modifiers.empty()) { DiagExpectNoModifier(*modifiers.begin()); ret->begin = SortModifierByPos(modifiers)[0]->begin; } if (!isGetter) { ret->funcBody = ParseFuncBody(ScopeKind::PROP_MEMBER_SETTER_BODY); } else { ret->funcBody = ParseFuncBody(ScopeKind::PROP_MEMBER_GETTER_BODY); } if (!ret->funcBody->body) { DiagMissingBody(!ret->identifier.Valid() ? "" : "function", " '" + ret->identifier + "'", lastToken.End()); } ret->end = ret->funcBody->end; mpImpl->CheckCJMPDecl(*ret); return ret; } void ParserImpl::CheckAnnotationAnno(PtrVector& annos, const OwnedPtr& decl) { for (auto& anno : annos) { if (anno->kind != AnnotationKind::ANNOTATION) { continue; } if (decl->astKind == ASTKind::MACRO_EXPAND_DECL) { auto pInvocation = decl->GetInvocation(); if (!pInvocation || !pInvocation->decl) { return; } CheckAnnotationAnno(annos, pInvocation->decl); return; } // @Annotation can only modify a class, and not a class modified by abstract or open or sealed. if (decl->astKind != ASTKind::CLASS_DECL && decl->astKind != ASTKind::INVALID_DECL) { DiagUnexpectedAnnoOnKind(*anno, decl->keywordPos, anno->identifier, decl->astKind); return; } for (auto mod : decl->modifiers) { if (Utils::In(mod.modifier, {TokenKind::ABSTRACT, TokenKind::OPEN, TokenKind::SEALED})) { DiagUnexpectedAnnoOn(*anno, mod.begin, anno->identifier, mod.ToString()); return; } } } } void ParserImpl::CheckAnnotationAnno(PtrVector& annos, std::set modifiers) { for (auto& anno : annos) { if (anno->kind != AnnotationKind::ANNOTATION) { continue; } // @Annotation can only modify a class, and not a class modified by abstract or open or sealed. if (!Seeing(TokenKind::CLASS) && !Seeing(TokenKind::AT)) { DiagUnexpectedAnnoOn(*anno, lookahead.Begin(), anno->identifier, lookahead.Value()); return; } for (auto mod : modifiers) { if (Utils::In(mod.modifier, {TokenKind::ABSTRACT, TokenKind::OPEN, TokenKind::SEALED})) { DiagUnexpectedAnnoOn(*anno, mod.begin, anno->identifier, mod.ToString()); return; } } } } void ParserImpl::CheckOverflowAnno(PtrVector& annos, ScopeKind scopeKind) { if (SeeingAny({TokenKind::FUNC, TokenKind::MAIN, TokenKind::INIT, TokenKind::AT_EXCL}) || SeeingMacroCall() || annos.empty()) { return; } // Primary Constructor if (SeeingPrimaryConstructor(scopeKind)) { return; } for (auto& it : annos) { if (it->kind == AnnotationKind::NUMERIC_OVERFLOW) { DiagUnexpectedAnnoOn(*it, lookahead.Begin(), it->identifier, lookahead.Value()); break; } } } void ParserImpl::CheckPropDeclJavaMirror(PropDecl& decl) { if (decl.outerDecl && decl.outerDecl->TestAttr(Attribute::JAVA_MIRROR)) { decl.EnableAttr(Attribute::JAVA_MIRROR); ParseDiagnoseRefactor(DiagKindRefactor::parse_java_mirror_prop_is_deprecated, decl); if (decl.outerDecl && !decl.outerDecl->TestAttr(Attribute::ABSTRACT)) { decl.DisableAttr(Attribute::ABSTRACT); } if (decl.TestAttr(Attribute::PRIVATE)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_java_mirror_cannot_have_private_member, decl); decl.EnableAttr(Attribute::IS_BROKEN); decl.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } if (decl.TestAttr(Attribute::OPEN)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_java_mirror_cannot_have_open_prop, decl); decl.EnableAttr(Attribute::IS_BROKEN); decl.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } if (!decl.getters.empty()) { ParseDiagnoseRefactor(DiagKindRefactor::parse_java_mirror_prop_cannot_have_getter, decl); decl.EnableAttr(Attribute::IS_BROKEN); } else { InsertPropGetterSignature(decl, Attribute::JAVA_MIRROR); } if (!decl.setters.empty()) { ParseDiagnoseRefactor(DiagKindRefactor::parse_java_mirror_prop_cannot_have_setter, decl); decl.EnableAttr(Attribute::IS_BROKEN); } else if (decl.isVar) { InsertPropSetterSignature(decl, Attribute::JAVA_MIRROR); } } } void ParserImpl::CheckPrimaryCtorDeclJavaMirror(PrimaryCtorDecl& ctor) { if (ctor.outerDecl && ctor.outerDecl->TestAttr(Attribute::JAVA_MIRROR)) { ctor.EnableAttr(Attribute::JAVA_MIRROR); ctor.EnableAttr(Attribute::IS_BROKEN); ParseDiagnoseRefactor(DiagKindRefactor::parse_java_mirror_decl_cannot_have_primary_ctor, ctor); } } void ParserImpl::CheckInitCtorDeclBody(FuncDecl& ctor) { auto& fb = ctor.funcBody; if ((!fb || !fb->body) && !ctor.TestAnyAttr(Attribute::COMMON, Attribute::JAVA_MIRROR, Attribute::OBJ_C_MIRROR)) { DiagMissingBody("constructor", "", ctor.end); if (!parseDeclFile) { ctor.EnableAttr(Attribute::HAS_BROKEN); } } } void ParserImpl::CheckJavaInteropMember(Decl& decl) { if (decl.outerDecl->TestAttr(Attribute::JAVA_MIRROR_SUBTYPE) && !decl.outerDecl->TestAttr(Attribute::JAVA_MIRROR)) { if (decl.GetGeneric() != nullptr) { ffiParser->Java().DiagJavaImplCannotBeGeneric(decl); return; } if (decl.astKind == ASTKind::FUNC_DECL && decl.TestAttr(Attribute::CONSTRUCTOR, Attribute::STATIC)) { ffiParser->Java().DiagJavaImplCannotHaveStaticInit(decl); return; } } auto pdecl = Ptr(&decl); switch (decl.astKind) { case ASTKind::FUNC_DECL: { auto& fd = *StaticAs(pdecl); if (fd.TestAttr(Attribute::CONSTRUCTOR)) { CheckInitCtorDeclJavaMirror(fd); } else if (fd.TestAttr(Attribute::FINALIZER) && fd.outerDecl->TestAttr(Attribute::JAVA_MIRROR)) { ffiParser->Java().DiagJavaMirrorCannotHaveFinalizer(fd); } else { // method branch CheckMemberFuncJavaMirror(fd); ffiParser->CheckForeignNameAnnotation(fd); } break; } case ASTKind::PROP_DECL: { auto& propDecl = *StaticAs(pdecl); CheckPropDeclJavaMirror(propDecl); ffiParser->CheckForeignNameAnnotation(propDecl); break; } case ASTKind::PRIMARY_CTOR_DECL: { auto& ctorDecl = *StaticAs(pdecl); CheckPrimaryCtorDeclJavaMirror(ctorDecl); break; } default: break; } } void ParserImpl::CheckObjCInteropMember(Decl& member) { if (member.outerDecl->TestAttr(Attribute::OBJ_C_MIRROR_SUBTYPE)) { if (member.GetGeneric() != nullptr) { ffiParser->ObjC().DiagObjCImplCannotBeGeneric(member); return; } if (member.astKind == ASTKind::FUNC_DECL && member.TestAttr(Attribute::CONSTRUCTOR, Attribute::STATIC)) { ffiParser->ObjC().DiagObjCImplCannotHaveStaticInit(member); return; } } auto pMember = Ptr(&member); switch (member.astKind) { case ASTKind::FUNC_DECL: { auto& fd = *StaticAs(pMember); if (fd.TestAttr(Attribute::CONSTRUCTOR)) { CheckInitCtorDeclObjCMirror(fd); ffiParser->CheckForeignNameAnnotation(fd); } else if (fd.TestAttr(Attribute::FINALIZER) && fd.outerDecl->TestAttr(Attribute::OBJ_C_MIRROR)) { ffiParser->ObjC().DiagObjCMirrorCannotHaveFinalizer(fd); } else { // method branch CheckMemberFuncObjCMirror(fd); ffiParser->CheckForeignNameAnnotation(fd); } break; } case ASTKind::VAR_DECL: { auto& varDecl = *StaticAs(pMember); CheckVarDeclObjCMirror(varDecl); break; } case ASTKind::PROP_DECL: { auto& propDecl = *StaticAs(pMember); CheckPropDeclObjCMirror(propDecl); ffiParser->CheckForeignNameAnnotation(propDecl); break; } case ASTKind::PRIMARY_CTOR_DECL: { auto& ctorDecl = *StaticAs(pMember); CheckPrimaryCtorDeclObjCMirror(ctorDecl); break; } default: break; } } void ParserImpl::CheckMemberFuncJavaMirror(FuncDecl& decl) { if (!decl.outerDecl || !decl.outerDecl->TestAttr(Attribute::JAVA_MIRROR)) { return; } decl.EnableAttr(Attribute::JAVA_MIRROR); if (decl.TestAttr(Attribute::PRIVATE)) { ffiParser->Java().DiagJavaMirrorCannotHavePrivateMember(decl); decl.EnableAttr(Attribute::IS_BROKEN); decl.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } if (HasModifier(decl.modifiers, TokenKind::CONST)) { ffiParser->Java().DiagJavaMirrorCannotHaveConstMember(decl); decl.EnableAttr(Attribute::IS_BROKEN); decl.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } FFICheckClassLikeFuncBody(decl, DiagKindRefactor::parse_java_mirror_function_must_have_return_type, DiagKindRefactor::parse_java_mirror_function_cannot_have_body); } void ParserImpl::CheckMemberFuncObjCMirror(FuncDecl& func) { if (func.outerDecl && func.outerDecl->TestAttr(Attribute::OBJ_C_MIRROR_SUBTYPE) && !func.outerDecl->TestAttr(Attribute::OBJ_C_MIRROR) && !func.funcBody && !func.funcBody->paramLists.empty() && !func.funcBody->paramLists[0]->params.empty()) { ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_impl_member_must_have_foreign_name, func); } if (!func.outerDecl || !func.outerDecl->TestAttr(Attribute::OBJ_C_MIRROR)) { return; } func.EnableAttr(Attribute::OBJ_C_MIRROR); if (!func.HasAnno(AnnotationKind::FOREIGN_NAME) && !func.funcBody->paramLists[0]->params.empty()) { ffiParser->ObjC().DiagObjCMirrorMemberMustHaveForeignName(func); func.EnableAttr(Attribute::IS_BROKEN); func.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } if (func.TestAttr(Attribute::PRIVATE)) { ffiParser->ObjC().DiagObjCMirrorCannotHavePrivateMember(func); func.EnableAttr(Attribute::IS_BROKEN); func.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } if (HasModifier(func.modifiers, TokenKind::CONST)) { ffiParser->ObjC().DiagObjCMirrorCannotHaveConstMember(func); func.EnableAttr(Attribute::IS_BROKEN); func.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } FFICheckClassLikeFuncBody(func, DiagKindRefactor::parse_objc_mirror_method_must_have_return_type, DiagKindRefactor::parse_objc_mirror_method_cannot_have_body); } void ParserImpl::CheckInitCtorDeclJavaMirror(FuncDecl& ctor) { if (!ctor.TestAttr(Attribute::JAVA_MIRROR)) { return; } ctor.EnableAttr(Attribute::JAVA_MIRROR); ctor.DisableAttr(Attribute::ABSTRACT); ctor.constructorCall = ConstructorCall::OTHER_INIT; if (ctor.TestAttr(Attribute::STATIC)) { ffiParser->Java().DiagJavaMirrorCannotHaveStaticInit(ctor); ctor.EnableAttr(Attribute::IS_BROKEN); ctor.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } if (HasModifier(ctor.modifiers, TokenKind::CONST)) { ffiParser->Java().DiagJavaMirrorCannotHaveConstMember(ctor); ctor.EnableAttr(Attribute::IS_BROKEN); ctor.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } if (ctor.TestAttr(Attribute::PRIVATE)) { ffiParser->Java().DiagJavaMirrorCannotHavePrivateMember(ctor); ctor.EnableAttr(Attribute::IS_BROKEN); ctor.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } if (ctor.funcBody && ctor.funcBody->body) { ctor.EnableAttr(Attribute::IS_BROKEN); ParseDiagnoseRefactor( DiagKindRefactor::parse_java_mirror_constructor_cannot_have_body, ctor.funcBody->body->begin); } else if (ctor.funcBody) { auto body = MakeOwned(); body->EnableAttr(Attribute::COMPILER_ADD, Attribute::IMPLICIT_ADD); body->begin = ctor.end; body->curFile = ctor.curFile; body->end = ctor.end; body->ty = ctor.ty; ctor.funcBody->body = std::move(body); } } void ParserImpl::CheckInitCtorDeclObjCMirror(FuncDecl& ctor) { if (ctor.outerDecl && ctor.outerDecl->TestAttr(Attribute::OBJ_C_MIRROR_SUBTYPE) && !ctor.outerDecl->TestAttr(Attribute::OBJ_C_MIRROR) && !ctor.funcBody && !ctor.funcBody->paramLists.empty() && !ctor.funcBody->paramLists[0]->params.empty()) { ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_impl_member_must_have_foreign_name, ctor); } if (!ctor.outerDecl || !ctor.outerDecl->TestAttr(Attribute::OBJ_C_MIRROR)) { return; } ctor.constructorCall = ConstructorCall::OTHER_INIT; ctor.EnableAttr(Attribute::OBJ_C_MIRROR); if (!ctor.HasAnno(AnnotationKind::FOREIGN_NAME) && !ctor.funcBody->paramLists[0]->params.empty()) { ffiParser->ObjC().DiagObjCMirrorMemberMustHaveForeignName(ctor); ctor.EnableAttr(Attribute::IS_BROKEN); ctor.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } if (ctor.TestAttr(Attribute::STATIC)) { ffiParser->ObjC().DiagObjCMirrorCannotHaveStaticInit(ctor); ctor.EnableAttr(Attribute::IS_BROKEN); ctor.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } if (HasModifier(ctor.modifiers, TokenKind::CONST)) { ffiParser->ObjC().DiagObjCMirrorCannotHaveConstMember(ctor); ctor.EnableAttr(Attribute::IS_BROKEN); ctor.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } if (ctor.TestAttr(Attribute::PRIVATE)) { ffiParser->ObjC().DiagObjCMirrorCannotHavePrivateMember(ctor); ctor.EnableAttr(Attribute::IS_BROKEN); ctor.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } if (ctor.funcBody && ctor.funcBody->body) { ctor.EnableAttr(Attribute::IS_BROKEN); ParseDiagnoseRefactor( DiagKindRefactor::parse_objc_mirror_ctor_cannot_have_body, ctor.funcBody->body->begin); } else if (ctor.funcBody) { auto body = MakeOwned(); body->EnableAttr(Attribute::COMPILER_ADD, Attribute::IMPLICIT_ADD); body->begin = ctor.end; body->curFile = ctor.curFile; body->end = ctor.end; body->ty = ctor.ty; ctor.funcBody->body = std::move(body); } } void ParserImpl::CheckVarDeclObjCMirror(VarDecl& field) const { if (!field.outerDecl || !field.outerDecl->TestAnyAttr(Attribute::OBJ_C_MIRROR)) { return; } field.EnableAttr(Attribute::OBJ_C_MIRROR); if (field.TestAttr(Attribute::PRIVATE)) { ffiParser->ObjC().DiagObjCMirrorCannotHavePrivateMember(field); field.EnableAttr(Attribute::IS_BROKEN); field.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } if (field.TestAttr(Attribute::STATIC)) { ffiParser->ObjC().DiagObjCMirrorFieldCannotBeStatic(field); field.EnableAttr(Attribute::IS_BROKEN); field.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } if (field.initializer) { ffiParser->ObjC().DiagObjCMirrorFieldCannotHaveInitializer(field); field.EnableAttr(Attribute::IS_BROKEN); field.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } } void ParserImpl::CheckPropDeclObjCMirror(PropDecl& prop) { if (!prop.outerDecl || !prop.outerDecl->TestAttr(Attribute::OBJ_C_MIRROR)) { return; } prop.EnableAttr(Attribute::OBJ_C_MIRROR); if (prop.outerDecl && !prop.outerDecl->TestAttr(Attribute::ABSTRACT)) { prop.DisableAttr(Attribute::ABSTRACT); } if (prop.TestAttr(Attribute::PRIVATE)) { ffiParser->ObjC().DiagObjCMirrorCannotHavePrivateMember(prop); prop.EnableAttr(Attribute::IS_BROKEN); prop.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } if (!prop.getters.empty()) { ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_mirror_prop_cannot_have_getter, prop, prop.identifier); prop.EnableAttr(Attribute::IS_BROKEN); } else { InsertPropGetterSignature(prop, Attribute::OBJ_C_MIRROR); } if (!prop.setters.empty()) { ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_mirror_prop_cannot_have_setter, prop, prop.identifier); prop.EnableAttr(Attribute::IS_BROKEN); } else if (prop.isVar) { InsertPropSetterSignature(prop, Attribute::OBJ_C_MIRROR); } } void ParserImpl::CheckPrimaryCtorDeclObjCMirror(PrimaryCtorDecl& ctor) { if (!ctor.outerDecl || !ctor.outerDecl->TestAttr(Attribute::OBJ_C_MIRROR)) { return; } ctor.EnableAttr(Attribute::OBJ_C_MIRROR); ctor.EnableAttr(Attribute::IS_BROKEN); ParseDiagnoseRefactor(DiagKindRefactor::parse_objc_mirror_cannot_have_primary_ctor, ctor); } void ParserImpl::CheckCJMappingAttr(Decl& decl) const { if (enableInteropCJMapping && decl.TestAttr(Attribute::PUBLIC)) { // currently only support struct decl and enum decl. if (decl.astKind == ASTKind::STRUCT_DECL || decl.astKind == ASTKind::ENUM_DECL) { decl.EnableAttr(Attribute::JAVA_CJ_MAPPING); } } } template void ParserImpl::ParseFuncDeclAnnos(std::vector>& annos, T& funcDecl) { funcDecl.annotations = std::move(annos); for (auto& it : funcDecl.annotations) { if (it->kind == AnnotationKind::JAVA) { // We enable attribute JAVA_APP by default here to facilitate subsequent processing. // It will be checked and reset to the final correct attribute in the Sema phase. funcDecl.EnableAttr(Attribute::JAVA_APP); } if (it->kind == AnnotationKind::INTRINSIC) { CheckIntrinsicFunc(funcDecl); funcDecl.EnableAttr(Attribute::INTRINSIC); } if (it->kind == AnnotationKind::NUMERIC_OVERFLOW) { funcDecl.EnableAttr(Attribute::NUMERIC_OVERFLOW); funcDecl.overflowStrategy = it->overflowStrategy; } if (it->kind == AnnotationKind::FASTNATIVE) { funcDecl.isFastNative = true; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (auto fd = dynamic_cast(&funcDecl); fd && it->kind == AnnotationKind::FROZEN) { fd->isFrozen = true; } #endif } } Token ParserImpl::SkipAndReturnOverloadingOperator() { constexpr size_t pairLen{2}; if (SeeingCombinator({TokenKind::GT, TokenKind::GT})) { SkipCombinator({TokenKind::GT, TokenKind::GT}); lastToken = Token(TokenKind::RSHIFT, ">>", lastToken.Begin(), lastToken.Begin() + pairLen); return lastToken; } if (SeeingCombinator({TokenKind::GT, TokenKind::ASSIGN})) { SkipCombinator({TokenKind::GT, TokenKind::ASSIGN}); lastToken = Token(TokenKind::GE, ">=", lastToken.Begin(), lastToken.Begin() + pairLen); return lastToken; } if (Seeing({TokenKind::LSQUARE, TokenKind::RSQUARE}, false)) { (void)Skip(TokenKind::LSQUARE); auto lparenPos{lastToken.Begin()}; (void)Skip(TokenKind::RSQUARE); lastToken = Token(TokenKind::LSQUARE, "[]", lparenPos, lastToken.End()); return lastToken; } if (Seeing({TokenKind::LPAREN, TokenKind::RPAREN}, false)) { (void)Skip(TokenKind::LPAREN); auto lparenPos{lastToken.Begin()}; (void)Skip(TokenKind::RPAREN); lastToken = Token(TokenKind::LPAREN, "()", lparenPos, lastToken.End()); return lastToken; } if (SkipOperator()) { return lastToken; } return Token{TokenKind::ILLEGAL}; } // Trustlist checks the name of the operator overload function. std::string ParserImpl::ExpectOperatorIdentifier(FuncDecl& fd) { Peek(); auto operatorTok = SkipAndReturnOverloadingOperator(); if (operatorTok.kind == TokenKind::ILLEGAL) { DiagInvalidOverloadedOperator(); Next(); return INVALID_IDENTIFIER; } // operator function do not support generic, but we do error check in sema. if (!Seeing(TokenKind::LPAREN) && !Seeing(TokenKind::LT)) { DiagExpectCharacter("'('"); Next(); return INVALID_IDENTIFIER; } fd.identifier.SetPos(operatorTok.Begin(), operatorTok.End()); fd.op = operatorTok.kind; return operatorTok.Value(); } OwnedPtr ParserImpl::ParseFinalizer( ScopeKind scopeKind, const std::set modifiers, PtrVector annos) { Next(); // skip ~ auto tildeBegin{lastToken.Begin()}; OwnedPtr funcDecl = MakeOwned(); [[maybe_unused]]ChainScope cs(*this, funcDecl.get()); funcDecl->begin = tildeBegin; Skip(TokenKind::INIT); // skip init funcDecl->identifier = SrcIdentifier{"~init", tildeBegin, lastToken.End(), false}; funcDecl->keywordPos = lastToken.Begin(); funcDecl->EnableAttr(Attribute::FINALIZER); CheckDeclarationInScope(scopeKind, DefKind::FINALIZER); funcDecl->funcBody = ParseFuncBody(scopeKind); auto initAttrs = CheckDeclModifiers(modifiers, scopeKind, DefKind::FINALIZER); for (auto& it : initAttrs) { funcDecl->EnableAttr(it); } if (!funcDecl->funcBody->paramLists.empty() && !funcDecl->funcBody->paramLists[0]->params.empty()) { ParseDiagnoseRefactor( DiagKindRefactor::parse_finalizer_can_not_accept_any_parameter, *funcDecl->funcBody->paramLists[0]); } funcDecl->modifiers.insert(modifiers.begin(), modifiers.end()); if (funcDecl->funcBody) { funcDecl->end = funcDecl->funcBody->end; } CheckNoDeprecatedAnno(annos, "~init"); ParseFuncDeclAnnos(annos, *funcDecl); if (!funcDecl->funcBody || !funcDecl->funcBody->body) { DiagMissingBody("finalizer", "", funcDecl->end); if (!parseDeclFile) { funcDecl->EnableAttr(Attribute::HAS_BROKEN); } } if (funcDecl->funcBody && funcDecl->funcBody->retType) { ParseDiagnoseRefactor( DiagKindRefactor::parse_invalid_return_type, *funcDecl->funcBody->retType, "finalizer"); funcDecl->EnableAttr(Attribute::HAS_BROKEN); } return funcDecl; } OwnedPtr ParserImpl::ParseConstructor( ScopeKind scopeKind, const std::set& modifiers, PtrVector annos) { OwnedPtr funcDecl = MakeOwned(); ChainScope cs(*this, funcDecl.get()); funcDecl->begin = lookahead.Begin(); funcDecl->identifier = "init"; funcDecl->identifier.SetPos(lookahead.Begin(), lookahead.End()); funcDecl->keywordPos = lookahead.Begin(); funcDecl->EnableAttr(Attribute::CONSTRUCTOR); if (HasModifier(modifiers, TokenKind::STATIC)) { CheckNoDeprecatedAnno(annos, "static constructor"); } if (HasModifier(modifiers, TokenKind::COMMON)) { funcDecl->EnableAttr(Attribute::COMMON); } Next(); CheckDeclarationInScope(scopeKind, DefKind::CONSTRUCTOR); funcDecl->funcBody = ParseFuncBody(scopeKind); auto initAttrs = CheckDeclModifiers(modifiers, scopeKind, DefKind::CONSTRUCTOR); for (auto& it : initAttrs) { funcDecl->EnableAttr(it); } funcDecl->modifiers.insert(modifiers.begin(), modifiers.end()); CheckVariableParams(*funcDecl); if (funcDecl->TestAttr(Attribute::STATIC)) { // Static init is always 'private'. funcDecl->EnableAttr(Attribute::PRIVATE); if (!funcDecl->funcBody->paramLists.empty() && !funcDecl->funcBody->paramLists[0]->params.empty()) { ParseDiagnoseRefactor( DiagKindRefactor::parse_static_init_can_not_accept_any_parameter, *funcDecl->funcBody->paramLists[0]); } } if (funcDecl->funcBody) { funcDecl->end = funcDecl->funcBody->end; } ParseFuncDeclAnnos(annos, *funcDecl); CheckConstructorBody(*funcDecl, scopeKind); return funcDecl; } OwnedPtr ParserImpl::ParsePrimaryConstructor( ScopeKind scopeKind, const std::set& modifiers, PtrVector annos) { if (HasModifier(modifiers, TokenKind::STATIC)) { CheckNoDeprecatedAnno(annos, "static constructor"); } OwnedPtr primaryCtorDecl = MakeOwned(); ChainScope cs(*this, primaryCtorDecl.get()); if (!modifiers.empty()) { primaryCtorDecl->begin = SortModifierByPos(modifiers)[0]->begin; } else { primaryCtorDecl->begin = lookahead.Begin(); } primaryCtorDecl->keywordPos = lookahead.Begin(); auto identifier = ParseIdentifierFromToken(lookahead); if (identifier == INVALID_IDENTIFIER) { return primaryCtorDecl; } if (identifier != curPrimaryDeclIdent && !curPrimaryDeclIdent.empty()) { DiagUnknownPrimaryConstructor(curPrimaryDeclIdent); } primaryCtorDecl->identifier = std::move(identifier); Next(); if (scopeKind == ScopeKind::CLASS_BODY) { primaryCtorDecl->funcBody = ParseFuncBody(ScopeKind::PRIMARY_CONSTRUCTOR_BODY_FOR_CLASS); CheckVariableParams(*primaryCtorDecl); primaryCtorDecl->EnableAttr(Attribute::IN_CLASSLIKE); } if (scopeKind == ScopeKind::STRUCT_BODY) { primaryCtorDecl->funcBody = ParseFuncBody(ScopeKind::PRIMARY_CONSTRUCTOR_BODY_FOR_STRUCT); CheckVariableParams(*primaryCtorDecl); primaryCtorDecl->EnableAttr(Attribute::IN_STRUCT); } primaryCtorDecl->EnableAttr(Attribute::CONSTRUCTOR); auto initAttrs = CheckDeclModifiers(modifiers, scopeKind, DefKind::PRIMARY_CONSTRUCTOR); for (auto& it : initAttrs) { primaryCtorDecl->EnableAttr(it); } if (HasModifier(modifiers, TokenKind::CONST)) { primaryCtorDecl->isConst = true; } primaryCtorDecl->modifiers.insert(modifiers.begin(), modifiers.end()); if (HasModifier(modifiers, TokenKind::UNSAFE)) { SetUnsafe(primaryCtorDecl.get(), modifiers); } if ((!primaryCtorDecl->funcBody || !primaryCtorDecl->funcBody->body) && !primaryCtorDecl->TestAttr(Attribute::COMMON)) { DiagMissingBody("main constructor", !primaryCtorDecl->identifier.Valid() ? "" : " '" + primaryCtorDecl->identifier + "'", lastToken.End()); } ParseFuncDeclAnnos(annos, *primaryCtorDecl); if (primaryCtorDecl->funcBody) { primaryCtorDecl->end = primaryCtorDecl->funcBody->end; } mpImpl->CheckCJMPDecl(*primaryCtorDecl); if (primaryCtorDecl->funcBody && primaryCtorDecl->funcBody->retType) { ParseDiagnoseRefactor( DiagKindRefactor::parse_invalid_return_type, *primaryCtorDecl->funcBody->retType, "primary constructor"); } return primaryCtorDecl; } template bool ParserImpl::CheckSkipRcurOrPrematureEnd(T& ret) { if (Skip(TokenKind::RCURL)) { ret->rightCurlPos = lastToken.Begin(); return true; } if (DetectPrematureEnd() && !ret->TestAttr(Attribute::HAS_BROKEN)) { ret->EnableAttr(Attribute::IS_BROKEN); DiagExpectedRightDelimiter("{", ret->leftCurlPos); return true; } return false; } OwnedPtr ParserImpl::ParseClassBody(ClassDecl& cd) { OwnedPtr ret = MakeOwned(); if (!Skip(TokenKind::LCURL)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_left_brace, lookahead, ConvertToken(lookahead)); ConsumeUntilDecl(TokenKind::LCURL); Skip(TokenKind::LCURL); ret->EnableAttr(Attribute::HAS_BROKEN); ret->EnableAttr(Attribute::IS_BROKEN); return ret; } else { ret->leftCurlPos = lastToken.Begin(); } ret->begin = lookahead.Begin(); while (true) { bool hasNLorSEMI = SkipNLOrSemi(); if (CheckSkipRcurOrPrematureEnd(ret)) { break; } if (!ret->decls.empty() && !hasNLorSEMI) { DiagExpectSemiOrNewline(); } auto decl = ParseDecl(ScopeKind::CLASS_BODY); if (auto ctor = As(decl); ctor && ctor->TestAttr(Attribute::CONSTRUCTOR)) { if (cd.TestAttr(Attribute::JAVA_MIRROR)) { ctor->EnableAttr(Attribute::JAVA_MIRROR); } if (cd.TestAttr(Attribute::OBJ_C_MIRROR)) { ctor->EnableAttr(Attribute::OBJ_C_MIRROR); } CheckInitCtorDeclBody(*ctor); } if (decl->IsInvalid()) { continue; } SetMemberParentInheritableDecl(cd, decl); if (auto fd = As(decl.get())) { CheckClassLikeFuncBodyAbstractness(*fd); } else if (auto pd = As(decl.get())) { CheckClassLikePropAbstractness(*pd); } CheckJavaInteropMember(*decl); CheckObjCInteropMember(*decl); if (decl->astKind == ASTKind::PRIMARY_CTOR_DECL && decl->identifier != cd.identifier) { ret->decls.emplace_back(MakeOwned(decl->begin)); continue; } ret->decls.emplace_back(std::move(decl)); } ret->end = lastToken.End(); return ret; } OwnedPtr ParserImpl::ParseInterfaceBody(InterfaceDecl& id) { if (!Skip(TokenKind::LCURL)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_left_brace, lookahead, ConvertToken(lookahead)); return MakeInvalid(lastToken.End()); } OwnedPtr ret = MakeOwned(); ret->leftCurlPos = lastToken.Begin(); ret->begin = ret->leftCurlPos; while (true) { SkipBlank(TokenKind::SEMI); if (CheckSkipRcurOrPrematureEnd(ret)) { break; } auto decl = ParseDecl(ScopeKind::INTERFACE_BODY); if (decl->IsInvalid()) { continue; } SetMemberParentInheritableDecl(id, decl); if (auto fd = As(decl.get())) { CheckClassLikeFuncBodyAbstractness(*fd); } else if (auto pd = As(decl.get())) { CheckClassLikePropAbstractness(*pd); } CheckJavaInteropMember(*decl); CheckObjCInteropMember(*decl); ret->decls.emplace_back(std::move(decl)); } ret->end = ret->rightCurlPos; ret->end.column += 1; return ret; } OwnedPtr ParserImpl::ParseEnumConstructor( const std::set& modifiers, PtrVector& annos) { OwnedPtr ret; std::string caseIdent = lookahead.Value(); auto caseIdentPos(lookahead.Begin()); Next(); if (caseIdent == INVALID_IDENTIFIER) { return MakeOwned(lookahead.Begin()); } auto lkCopy(lookahead); ret = Skip(TokenKind::LPAREN) ? ParseEnumConstructorWithArgs(lkCopy, annos) : ParseNoArgsEnumConstructor(lkCopy, annos); [[maybe_unused]] ChainScope cs(*this, ret.get()); if (!modifiers.empty()) { auto firstMod = *SortModifierByPos(modifiers)[0]; DiagExpectNoModifier(firstMod); } for (auto& anno : annos) { if (!(anno->kind == AnnotationKind::WHEN || anno->kind == AnnotationKind::DEPRECATED)) { DiagUnexpectedAnnoOn(*annos[0], caseIdentPos, annos[0]->identifier, caseIdent); } } return ret; } OwnedPtr ParserImpl::ParseEnumConstructorWithArgs(const Token& id, PtrVector& annos) { OwnedPtr funcParamList = MakeOwned(); funcParamList->leftParenPos = lastToken.Begin(); int nameIndex = 1; ParseOneOrMoreWithSeparator( TokenKind::COMMA, [&funcParamList]( const Position commaPos) { funcParamList->params.back()->commaPos = commaPos; }, [this, &nameIndex, &funcParamList]() { OwnedPtr funcParam = MakeOwned(); funcParam->identifier = "p" + std::to_string(nameIndex); funcParam->isIdentifierCompilerAdd = true; funcParam->type = ParseType(); funcParam->begin = funcParam->type->begin; funcParam->identifier.SetPos(funcParam->type->begin, funcParam->type->begin); funcParam->isVar = false; funcParam->end = funcParam->type->end; (void)funcParamList->params.emplace_back(std::move(funcParam)); nameIndex++; }); if (!Skip(TokenKind::RPAREN)) { DiagExpectedRightDelimiter("(", funcParamList->leftParenPos); } funcParamList->rightParenPos = lastToken.Begin(); funcParamList->begin = funcParamList->leftParenPos; funcParamList->end = funcParamList->rightParenPos; OwnedPtr funcBody = MakeOwned(); funcBody->begin = lastToken.Begin(); funcBody->paramLists.emplace_back(std::move(funcParamList)); funcBody->end = lastToken.End(); OwnedPtr ret = MakeOwned(); ret->identifier = ParseIdentifierFromName(id.Value(), id.Begin(), id.End(), id.Length()); ret->funcBody = std::move(funcBody); ret->begin = id.Begin(); ret->end = lastToken.End(); ret->EnableAttr(Attribute::ENUM_CONSTRUCTOR); ret->annotations = std::move(annos); return ret; } OwnedPtr ParserImpl::ParseNoArgsEnumConstructor(const Token& id, PtrVector& annos) { OwnedPtr ret = MakeOwned(); ret->identifier = ParseIdentifierFromName(id.Value(), id.Begin(), id.End(), id.Length()); ret->begin = id.Begin(); ret->end = lastToken.End(); ret->isVar = false; ret->EnableAttr(Attribute::ENUM_CONSTRUCTOR); ret->annotations = std::move(annos); return ret; } void ParserImpl::ParseConstraints(const Decl& decl) { if (!decl.generic) { DiagUnexpectedWhere(lastToken); } else { decl.generic->genericConstraints = ParseGenericConstraints(); } } void ParserImpl::ParseInheritedTypes(InheritableDecl& decl) { decl.upperBoundPos = lastToken.Begin(); while (true) { auto interfaceType = ParseType(); if (auto ref = AST::As(interfaceType.get()); ref && ref->ref.identifier == decl.identifier) { DiagDeclCannotInheritTheirSelf(decl, *ref); } else if (Is(interfaceType.get()) || Is(interfaceType.get())) { decl.inheritedTypes.emplace_back(std::move(interfaceType)); } else { if (!interfaceType->TestAttr(Attribute::IS_BROKEN)) { DiagInvalidInheritType(*interfaceType); } } if (!Skip(TokenKind::BITAND)) { break; } if (!decl.inheritedTypes.empty()) { decl.inheritedTypes.back()->bitAndPos = lastToken.Begin(); } } } void ParserImpl::ParseInterfaceDeclOrClassDeclGeneric(InheritableDecl& ret) { if (Skip(TokenKind::LT)) { ret.EnableAttr(Attribute::GENERIC); ret.generic = ParseGeneric(); } if (SeeingAny({TokenKind::IDENTIFIER, TokenKind::COLON}) || SeeingContextualKeyword()) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_lt_brace, lookahead, ConvertToken(lookahead)); ConsumeUntilDecl(TokenKind::LCURL); } if (Skip(TokenKind::UPPERBOUND)) { ParseInheritedTypes(ret); } if (Skip(TokenKind::WHERE)) { ParseConstraints(ret); } } void ParserImpl::SetMemberParentInheritableDecl(InheritableDecl& ret, const OwnedPtr& decl) const { decl->outerDecl = &ret; if (auto macroExpandDecl = DynamicCast(decl.get()); macroExpandDecl && macroExpandDecl->invocation.decl) { SetMemberParentInheritableDecl(ret, macroExpandDecl->invocation.decl); } else if (auto pd = DynamicCast(decl.get()); pd && pd->funcBody && !pd->funcBody->paramLists.empty()) { for (auto& param : pd->funcBody->paramLists[0]->params) { if (param->isMemberParam) { param->outerDecl = &ret; } } } else if (auto func = DynamicCast(decl.get()); func && func->funcBody) { if (ret.IsClassLikeDecl()) { func->funcBody->parentClassLike = StaticCast(&ret); } else if (ret.astKind == ASTKind::ENUM_DECL) { func->funcBody->parentEnum = StaticCast(&ret); } } } void ParserImpl::SetDefaultFunc(ScopeKind scopeKind, AST::Decl& decl) const { if (scopeKind != ScopeKind::INTERFACE_BODY) { return; } if (decl.astKind == ASTKind::FUNC_DECL) { bool defaultFunc = !decl.TestAttr(Attribute::FOREIGN) && !decl.TestAttr(Attribute::ABSTRACT); if (defaultFunc) { decl.EnableAttr(Attribute::DEFAULT); } } else if (decl.astKind == ASTKind::PROP_DECL) { if (decl.TestAttr(Attribute::ABSTRACT)) { return; } decl.EnableAttr(Attribute::DEFAULT); auto pd = RawStaticCast(&decl); for (auto& setter : pd->setters) { setter->EnableAttr(Attribute::DEFAULT); } for (auto& getter : pd->getters) { getter->EnableAttr(Attribute::DEFAULT); } } } void ParserImpl::CheckDeclarationInScope(ScopeKind sk, DefKind dk) { // Unknown scope is with parser called by macro mostly. if (sk == ScopeKind::UNKNOWN_SCOPE) { return; } if (GetModifierRulesByDefKind(dk).count(sk) == 0) { if (!chainedAST.back()->TestAttr(Attribute::HAS_BROKEN)) { DiagUnexpectedDeclInScope(sk); chainedAST.back()->EnableAttr(Attribute::HAS_BROKEN); } } } OwnedPtr ParserImpl::ParseClassDecl( ScopeKind scopeKind, const std::set& modifiers, std::vector> annos) { Next(); OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); ret->begin = lookahead.Begin(); ret->keywordPos = lookahead.Begin(); ret->identifier = ExpectIdentifierWithPos(*ret); ParseInterfaceDeclOrClassDeclGeneric(*ret); CheckDeclarationInScope(scopeKind, DefKind::CLASS); auto attrs = CheckDeclModifiers(modifiers, scopeKind, DefKind::CLASS); for (auto& it : attrs) { ret->EnableAttr(it); } ffiParser->CheckClassLikeSignature(*ret, annos); ret->modifiers.insert(modifiers.begin(), modifiers.end()); // Used to parse nested classDecl or primary ctor decl. SetPrimaryDecl(ret->identifier, ret->identifier.IsRaw()); ret->body = ParseClassBody(*ret); RevertPrimaryDecl(); ret->end = lastToken.End(); ret->annotations = std::move(annos); if (Interop::Java::IsDeclAppropriateForSyntheticClassGeneration(*ret)) { Interop::Java::InsertSyntheticClassDecl(*ret, *currentFile); } return ret; } OwnedPtr ParserImpl::ParseInterfaceDecl( ScopeKind scopeKind, const std::set& modifiers, std::vector> annos) { Next(); OwnedPtr ret = MakeOwned(); ret->keywordPos = lookahead.Begin(); ChainScope cs(*this, ret.get()); ret->begin = lookahead.Begin(); ret->identifier = ExpectIdentifierWithPos(*ret); ParseInterfaceDeclOrClassDeclGeneric(*ret); CheckDeclarationInScope(scopeKind, DefKind::INTERFACE); auto attrs = CheckDeclModifiers(modifiers, scopeKind, DefKind::INTERFACE); for (auto& it : attrs) { ret->EnableAttr(it); } ffiParser->CheckClassLikeSignature(*ret, annos); ret->modifiers.insert(modifiers.begin(), modifiers.end()); ret->body = ParseInterfaceBody(*ret); ret->end = lastToken.End(); ret->annotations = std::move(annos); if (Interop::Java::IsDeclAppropriateForSyntheticClassGeneration(*ret)) { Interop::Java::InsertSyntheticClassDecl(*ret, *currentFile); } return ret; } void ParserImpl::ParseCaseBody(EnumDecl& enumDecl) { auto caseBody = ParseDecl(ScopeKind::ENUM_CONSTRUCTOR); if (caseBody->astKind == AST::ASTKind::FUNC_DECL) { enumDecl.hasArguments = true; } if (!caseBody->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_unknown_enum_constructor, *caseBody); } SetMemberParentInheritableDecl(enumDecl, caseBody); // common/platform enum attributes propagate to it's constructors to be used in further resolve if (enumDecl.TestAttr(Attribute::PLATFORM)) { caseBody->EnableAttr(Attribute::PLATFORM); } else if (enumDecl.TestAttr(Attribute::COMMON)) { caseBody->EnableAttr(Attribute::COMMON); } enumDecl.constructors.emplace_back(std::move(caseBody)); } void ParserImpl::ParseEnumBody(EnumDecl& enumDecl) { enumDecl.bodyScope = MakeOwned(); enumDecl.bodyScope->begin = lastToken.Begin(); if (Seeing(TokenKind::BITOR)) { // Skip the first (optional) 'BITOR' in the caseBody. Next(); } do { if (lastToken.kind == TokenKind::BITOR) { enumDecl.bitOrPosVector.emplace_back(lastToken.Begin()); } bool seeingEllipse = Seeing(TokenKind::ELLIPSIS); if ((!seeingEllipse && !Seeing(TokenKind::IDENTIFIER) && !SeeingMacroCallDecl() && !SeeingContextualKeyword() && !SeeingAtWhen()) || SeeingKeywordWithDecl()) { if (!chainedAST.back()->TestAttr(Attribute::IS_BROKEN)) { DiagExpectedIdentifierEnumDecl(&enumDecl); } TryConsumeUntilAny({TokenKind::BITOR}); chainedAST.back()->EnableAttr(Attribute::IS_BROKEN); } else { if (seeingEllipse) { Next(); if (enumDecl.constructors.empty()) { DiagExpectedIdentifierEnumDecl(&enumDecl); } enumDecl.hasEllipsis = true; enumDecl.ellipsisPos = lastToken.Begin(); // ellipse must be the last constructor break; } ParseCaseBody(enumDecl); } SkipBlank(TokenKind::SEMI); } while (Skip(TokenKind::BITOR)); // Parse functions. while (true) { SkipBlank(TokenKind::SEMI); if (Skip(TokenKind::RCURL)) { enumDecl.rightCurlPos = lastToken.Begin(); break; } if (DetectPrematureEnd()) { DiagExpectedRightDelimiter("{", enumDecl.leftCurlPos); break; } auto decl = ParseDecl(ScopeKind::ENUM_BODY); if (decl->IsInvalid()) { continue; } if (decl->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_decl, MakeRange(decl->identifier), decl->identifier); } SetMemberParentInheritableDecl(enumDecl, decl); enumDecl.members.emplace_back(std::move(decl)); } } OwnedPtr ParserImpl::ParseEnumDecl( ScopeKind scopeKind, const std::set& modifiers, std::vector> annos) { Next(); OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); ret->annotations = std::move(annos); ret->begin = lookahead.Begin(); ret->keywordPos = lookahead.Begin(); CheckDeclarationInScope(scopeKind, DefKind::ENUM); auto attrs = CheckDeclModifiers(modifiers, scopeKind, DefKind::ENUM); for (auto& it : attrs) { ret->EnableAttr(it); } CheckCJMappingAttr(*ret); ret->modifiers.insert(modifiers.begin(), modifiers.end()); ret->identifier = ExpectIdentifierWithPos(*ret); if (Skip(TokenKind::LT)) { ret->EnableAttr(Attribute::GENERIC); ret->generic = ParseGeneric(); } if (Skip(TokenKind::UPPERBOUND)) { ParseInheritedTypes(*ret); } if (Skip(TokenKind::WHERE)) { ParseConstraints(*ret); } if (!Skip(TokenKind::LCURL)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_left_brace, lookahead, ConvertToken(lookahead)); ConsumeUntilDeclOrNL(TokenKind::LCURL); if (!Skip(TokenKind::LCURL)) { ret->EnableAttr(Attribute::IS_BROKEN); ret->bodyScope = MakeOwned(); // The 'bodyScope' must exist. ret->end = lastToken.End(); return ret; } } ret->leftCurlPos = lastToken.Begin(); ParseEnumBody(*ret); ret->end = lastToken.End(); ret->bodyScope->end = ret->end; return ret; } OwnedPtr ParserImpl::ParseStructBody(StructDecl& sd) { if (!Skip(TokenKind::LCURL)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_left_brace, lookahead, ConvertToken(lookahead)); return MakeInvalid(lastToken.End()); } OwnedPtr ret = MakeOwned(); ret->leftCurlPos = lastToken.Begin(); ret->begin = lookahead.Begin(); while (true) { SkipBlank(TokenKind::SEMI); if (CheckSkipRcurOrPrematureEnd(ret)) { break; } auto decl = ParseDecl(ScopeKind::STRUCT_BODY); if (auto ctor = As(decl); ctor && ctor->TestAttr(Attribute::CONSTRUCTOR)) { if (sd.TestAttr(Attribute::JAVA_MIRROR)) { ctor->EnableAttr(Attribute::JAVA_MIRROR); } CheckInitCtorDeclBody(*ctor); } if (decl->IsInvalid()) { continue; } SetMemberParentInheritableDecl(sd, decl); if (decl->astKind == ASTKind::PRIMARY_CTOR_DECL && decl->identifier != sd.identifier) { ret->decls.emplace_back(MakeOwned(decl->begin)); continue; } ret->decls.emplace_back(std::move(decl)); } ret->rightCurlPos = lastToken.Begin(); ret->end = lastToken.End(); return ret; } OwnedPtr ParserImpl::ParseStructDecl( ScopeKind scopeKind, const std::set& modifiers, PtrVector annos) { Next(); OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); ret->begin = lookahead.Begin(); ret->keywordPos = lookahead.Begin(); CheckDeclarationInScope(scopeKind, DefKind::STRUCT); auto attrs = CheckDeclModifiers(modifiers, scopeKind, DefKind::STRUCT); for (auto& attr : attrs) { ret->EnableAttr(attr); } CheckCJMappingAttr(*ret); ret->modifiers.insert(modifiers.begin(), modifiers.end()); ret->identifier = ExpectIdentifierWithPos(*ret); if (Skip(TokenKind::LT)) { ret->generic = ParseGeneric(); ret->EnableAttr(Attribute::GENERIC); } if (Skip(TokenKind::UPPERBOUND)) { ret->upperBoundPos = lastToken.Begin(); ParseStructInheritedTypes(*ret); } if (Skip(TokenKind::WHERE)) { ParseConstraints(*ret); } // Used to parse nested structDecl or primary ctor decl. SetPrimaryDecl(ret->identifier, ret->identifier.IsRaw()); ret->body = ParseStructBody(*ret); RevertPrimaryDecl(); ret->end = lastToken.End(); ret->annotations = std::move(annos); return ret; } void ParserImpl::ParseStructInheritedTypes(StructDecl& structDecl) { while (true) { auto interfaceType = ParseType(); if (Is(interfaceType.get()) || Is(interfaceType.get())) { structDecl.inheritedTypes.emplace_back(std::move(interfaceType)); } else { if (!interfaceType->TestAttr(Attribute::IS_BROKEN)) { DiagInvalidInheritType(*interfaceType); } } if (!Skip(TokenKind::BITAND)) { break; } if (!structDecl.inheritedTypes.empty()) { structDecl.inheritedTypes.back()->bitAndPos = lastToken.Begin(); } } } OwnedPtr ParserImpl::ParseTypeAlias( ScopeKind scopeKind, const std::set& modifiers, PtrVector annos) { Next(); OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); ret->annotations = std::move(annos); ret->begin = lookahead.Begin(); ret->keywordPos = lookahead.Begin(); CheckDeclarationInScope(scopeKind, DefKind::TYPE); auto attrs = CheckDeclModifiers(modifiers, scopeKind, DefKind::TYPE); for (auto& it : attrs) { ret->EnableAttr(it); } while (SeeingOperator()) { DiagExpectedName("type", "after 'type'"); Next(); } ret->modifiers.insert(modifiers.begin(), modifiers.end()); ret->identifier = ExpectIdentifierWithPos(*ret); if (Skip(TokenKind::LT)) { ret->EnableAttr(Attribute::GENERIC); ret->generic = ParseGeneric(); } if (Skip(TokenKind::ASSIGN)) { ret->assignPos = lookahead.Begin(); } else { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_assignment, lookahead, ConvertToken(lookahead)); ConsumeUntilDecl(); } // Subtype or trait constraints are not allowed in typealias decl. If the type of RHS of = is a type that has // constraints, related constraints need to be applied to type parameters on the LHS of =. // Example: In typealias A = C, if T is constrained in type C's decl, the constraint should be // added to T for type A in a later compiler phase. ret->type = ParseType(); ret->end = lastToken.End(); return ret; } OwnedPtr ParserImpl::ParseExtendDecl( ScopeKind scopeKind, const std::set& modifiers, PtrVector annos) { Next(); // Consume the extend token. auto ret = MakeOwned(); ChainScope cs(*this, ret.get()); CheckNoDeprecatedAnno(annos, "extend"); ret->annotations = std::move(annos); ret->begin = lookahead.Begin(); ret->keywordPos = lookahead.Begin(); ret->identifier.SetPos(lookahead.Begin(), lookahead.Begin()); CheckDeclarationInScope(scopeKind, DefKind::EXTEND); if (modifiers.size() == 1 && mpImpl->HasCJMPModifiers(*modifiers.begin())) { mpImpl->CheckCJMPModifiers(modifiers); auto attr = GetAttributeByModifier(modifiers.begin()->modifier); ret->EnableAttr(attr.value()); } else if (!modifiers.empty() && !chainedAST.back()->TestAttr(Attribute::IS_BROKEN)) { DiagExpectNoModifier(*modifiers.begin()); } ParseExtendedType(*ret); if (Skip(TokenKind::UPPERBOUND)) { // Interface extension. ret->upperBoundPos = lastToken.Begin(); do { if (lastToken.kind == TokenKind::BITAND) { ret->inheritedTypes.back()->bitAndPos = lastToken.Begin(); } ret->inheritedTypes.push_back(ParseType()); } while (Skip(TokenKind::BITAND)); } if (Skip(TokenKind::WHERE)) { // Generic constraints. ParseConstraints(*ret); } ParseExtendBody(*ret); ret->end = lastToken.End(); ret->bodyScope->end = ret->end; return ret; } void ParserImpl::SetUnsafe(Ptr node, const std::set& modifiers) { auto iter = std::find_if(modifiers.begin(), modifiers.end(), [](auto& mod) { return mod.modifier == TokenKind::UNSAFE; }); if (!IsUnsafeBackend(backend) && iter != modifiers.end()) { DiagUnsafeWillBeIgnored(*iter); return; } auto walker = Walker(node, [](Ptr node) { if (node == nullptr) { return VisitAction::SKIP_CHILDREN; } if (node->astKind == ASTKind::CALL_EXPR) { node->EnableAttr(Attribute::UNSAFE); } if (auto expr = DynamicCast(node); expr) { expr->EnableAttr(Attribute::UNSAFE); } return VisitAction::WALK_CHILDREN; }); walker.Walk(); } OwnedPtr ParserImpl::ParseGenericParamDecl() { OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); ret->identifier = ParseIdentifierFromToken(lookahead); ret->begin = lookahead.Begin(); ret->end = lookahead.End(); Next(); return ret; } OwnedPtr ParserImpl::ParseGeneric() { CJC_ASSERT(lastToken.kind == TokenKind::LT); auto ret = MakeOwned(); ret->leftAnglePos = lastToken.Begin(); ret->begin = lastToken.Begin(); ChainScope cs(*this, ret.get()); ParseOneOrMoreSepTrailing( [&ret](const Position commaPos) { if (!ret->typeParameters.empty()) { ret->typeParameters.back()->commaPos = commaPos; } }, [&ret, this]() { OwnedPtr gpd; if (Seeing(TokenKind::IDENTIFIER) || SeeingContextualKeyword()) { gpd = ParseGenericParamDecl(); } else { if (!ret->TestAttr(Attribute::IS_BROKEN)) { DiagExpectedIdentifierGeneric(ret.get()); } TryConsumeUntilAny({TokenKind::COMMA, TokenKind::GT}); ret->EnableAttr(Attribute::IS_BROKEN); } if (lookahead.kind != TokenKind::GT && gpd) { ret->typeParameters.emplace_back(std::move(gpd)); } }, TokenKind::GT); if (!Skip(TokenKind::GT)) { if (!ret->TestAttr(Attribute::IS_BROKEN)) { ret->end = lastToken.End(); DiagExpectedRightDelimiter("<", ret->leftAnglePos); } } else { ret->rightAnglePos = lastToken.Begin(); ret->end = lastToken.End(); } return ret; } bool ParserImpl::ParseGenericUpperBound(const OwnedPtr& genericConstraint) { bool illegalConstraint{false}; if (!Skip(TokenKind::UPPERBOUND)) { if (!genericConstraint->TestAttr(Attribute::HAS_BROKEN)) { DiagExpectCharacter("'<:'"); genericConstraint->EnableAttr(Attribute::HAS_BROKEN); } TryConsumeUntilAny(GetTypeFirst()); illegalConstraint = true; } else { genericConstraint->operatorPos = lastToken.Begin(); } do { if (lastToken.kind == TokenKind::BITAND) { genericConstraint->bitAndPos.push_back(lastToken.Begin()); } auto upperBound = ParseType(); if (!upperBound->TestAttr(Attribute::IS_BROKEN) && upperBound->astKind != ASTKind::THIS_TYPE) { genericConstraint->upperBounds.push_back(std::move(upperBound)); } else { if (!genericConstraint->TestAttr(Attribute::HAS_BROKEN)) { DiagExpectedIdentifierGenericConstraint(genericConstraint.get()); genericConstraint->EnableAttr(Attribute::HAS_BROKEN); } auto match = TryConsumeUntilAny({TokenKind::BITAND}); illegalConstraint = true; if (!match) { break; } } } while (Skip(TokenKind::BITAND)); return illegalConstraint; } std::vector> ParserImpl::ParseGenericConstraints() { std::vector> ret; do { auto genericConstraint = MakeOwned(); ChainScope cs(*this, genericConstraint.get()); if (lastToken.kind == TokenKind::COMMA && !ret.empty()) { ret.back()->commaPos = lastToken.Begin(); } else if (lastToken.kind == TokenKind::WHERE) { genericConstraint->wherePos = lastToken.Begin(); } genericConstraint->type = ParseRefType(true); SpreadAttrAndConsume(genericConstraint->type.get(), genericConstraint.get(), {TokenKind::UPPERBOUND}); auto illegalConstraint = ParseGenericUpperBound(genericConstraint); if (!illegalConstraint) { genericConstraint->begin = genericConstraint->wherePos.IsZero() ? genericConstraint->type->begin : genericConstraint->wherePos; genericConstraint->end = lastToken.End(); ret.push_back(std::move(genericConstraint)); } } while (Skip(TokenKind::COMMA)); std::unordered_map posRecord; for (auto& gc : ret) { if (gc && gc->type->astKind == AST::ASTKind::REF_TYPE) { auto rt = StaticAs(gc->type.get()); if (posRecord.count(rt->ref.identifier.Val()) > 0) { DiagDuplicatedItem("type name", rt->ref.identifier, rt->begin, posRecord[rt->ref.identifier.Val()], " in generic constrain"); } else { posRecord[rt->ref.identifier.Val()] = rt->begin; } } } return ret; } void ParserImpl::ParseModifiers(std::set& modifiers) { while (SeeingModifier() && !SeeingKeywordAndOperater()) { auto modifier = Modifier(lookahead.kind, lookahead.Begin()); modifier.curFile = currentFile; if (!modifiers.emplace(modifier).second) { DiagDuplicatedModifier(modifier, *modifiers.find(modifier)); } Next(); } } bool ParserImpl::HasModifier(const std::set& modifiers, TokenKind tk) { return Utils::In(modifiers, [&](const auto& mod) { return mod.modifier == tk; }); } bool ParserImpl::HasAnnotation(const PtrVector& annos, AnnotationKind ak) { return Utils::In(annos, [&](const auto& anno) { return anno->kind == ak; }); } std::string ParserImpl::ParseForImportToGetContentBetween( unsigned int fileID, int beginLine, int beginColumn, int endLine, int endColumn) { Position posBase = lexer->GetPosBase(); int newBeginLine; int newBeginColumn; int newEndLine; int newEndColumn; if (beginLine == posBase.line && beginColumn == posBase.column) { newBeginLine = 1; newBeginColumn = 1; newEndLine = (endLine - beginLine) + 1; newEndColumn = endColumn; } else { newBeginLine = (beginLine - posBase.line) + 1; newBeginColumn = beginColumn; newEndLine = (endLine - posBase.line) + 1; newEndColumn = endColumn; } return sourceManager.GetContentBetween( fileID, Position(newBeginLine, newBeginColumn), Position(newEndLine, newEndColumn), inputString); } OwnedPtr ParserImpl::ParseMainDecl( ScopeKind scopeKind, const std::set& modifiers, PtrVector annos) { OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); CheckNoDeprecatedAnno(annos, "main"); ret->keywordPos = lookahead.Begin(); ret->begin = lookahead.Begin(); ret->identifier = {"main", lookahead.Begin(), lookahead.End()}; ret->modifiers.insert(modifiers.begin(), modifiers.end()); if (ret->identifier.Begin() == INVALID_POSITION) { ret->identifier.SetPos(lookahead.Begin(), lookahead.End()); } Next(); CheckDeclarationInScope(scopeKind, DefKind::MAIN); ret->funcBody = ParseFuncBody(ScopeKind::MAIN_BODY); if (ret->funcBody) { ret->end = ret->funcBody->end; // The Curring function is not supported in spec 0.22. if (ret->funcBody->paramLists.size() > 1) { ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_left_brace, ret->funcBody->paramLists[1]->begin, std::string("(")); } } const std::set annoOnMain = { AnnotationKind::WHEN, AnnotationKind::NUMERIC_OVERFLOW, AnnotationKind::C, }; for (auto& anno : annos) { if (std::find(annoOnMain.begin(), annoOnMain.end(), anno->kind) == annoOnMain.end()) { DiagUnexpectedAnnoOn(*annos[0], ret->identifier.Begin(), annos[0]->identifier.Val(), ret->identifier.Val()); } } if (!ret->funcBody || !ret->funcBody->body) { DiagMissingBody("main function", "", lastToken.End()); } ParseFuncDeclAnnos(annos, *ret); auto attrs = CheckDeclModifiers(modifiers, scopeKind, DefKind::MAIN); if (Utils::In(Attribute::UNSAFE, attrs)) { ret->EnableAttr(Attribute::UNSAFE); SetUnsafe(ret.get(), modifiers); } return ret; } OwnedPtr ParserImpl::ParseFuncDecl( ScopeKind scopeKind, const std::set& modifiers, PtrVector annos) { OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); ret->keywordPos = lookahead.Begin(); ret->begin = lookahead.Begin(); Next(); if (forImport && scopeKind == ScopeKind::CLASS_BODY && HasModifier(modifiers, TokenKind::MUT)) { // For import: funcdecl with Mut modifier only allow in interface. scopeKind = ScopeKind::INTERFACE_BODY; } auto attrs = CheckDeclModifiers(modifiers, scopeKind, DefKind::FUNC); for (auto& it : attrs) { ret->EnableAttr(it); } if (ret->TestAttr(Attribute::OPERATOR)) { ret->identifier = ExpectOperatorIdentifier(*ret); } else { ret->identifier = ExpectIdentifierWithPos(*ret); } if (ret->identifier.Begin() == INVALID_POSITION) { ret->identifier.SetPos(lookahead.Begin(), lookahead.End()); } ret->modifiers.insert(modifiers.begin(), modifiers.end()); ret->funcBody = ParseFuncBody(scopeKind); if (ret->TestAttr(Attribute::COMMON) && !ret->funcBody->retType) { ParseDiagnoseRefactor(DiagKindRefactor::parse_common_function_must_have_return_type, *ret); } if (ret->TestAttr(Attribute::PLATFORM) && !ret->funcBody->retType) { ParseDiagnoseRefactor(DiagKindRefactor::parse_platform_function_must_have_return_type, *ret); } if (HasModifier(modifiers, TokenKind::UNSAFE) || HasModifier(modifiers, TokenKind::FOREIGN)) { SetUnsafe(ret.get(), modifiers); } ParseFuncDeclAnnos(annos, *ret); CheckFuncBody(scopeKind, *ret); if (ret->funcBody) { ret->end = ret->funcBody->end; // The Curring function is not supported in spec 0.22. if (ret->funcBody->paramLists.size() > 1) { ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_left_brace, ret->funcBody->paramLists[1]->begin, std::string("(")); } if (ret->identifier == "main" && !ret->identifier.IsRaw()) { DiagIllegalFunc(ret); } } SetDefaultFunc(scopeKind, *ret); if (scopeKind != ScopeKind::CLASS_BODY && scopeKind != ScopeKind::INTERFACE_BODY) { ffiParser->CheckForeignNameAnnotation(*ret); } return ret; } void ParserImpl::CheckFuncBody(ScopeKind scopeKind, FuncDecl& decl) { auto& fb = decl.funcBody; if (fb && fb->generic) { decl.EnableAttr(Attribute::GENERIC); } if (decl.TestAttr(Attribute::INTRINSIC)) { if (scopeKind != ScopeKind::TOPLEVEL && scopeKind != ScopeKind::UNKNOWN_SCOPE) { ParseDiagnoseRefactor( DiagKindRefactor::parse_intrinsic_function_must_be_toplevel, MakeRange(decl.begin, lastToken.End())); } if (fb && fb->body) { ParseDiagnoseRefactor(DiagKindRefactor::parse_intrinsic_function_cannot_have_body, *fb->body); } } auto const inClassLikeScope = scopeKind == ScopeKind::CLASS_BODY || scopeKind == ScopeKind::INTERFACE_BODY; /* Check here if a function could be abstract, and when the outerDecl will be known - clarify it for mirrors / common declarations */ if (fb && !fb->body && !parseDeclFile && scopeKind != ScopeKind::UNKNOWN_SCOPE && !decl.TestAnyAttr(Attribute::FOREIGN, Attribute::INTRINSIC)) { if (CanBeAbstract(decl, scopeKind)) { decl.EnableAttr(Attribute::ABSTRACT); } else if (!fb->TestAttr(Attribute::HAS_BROKEN) && !inClassLikeScope && !decl.TestAttr(Attribute::COMMON)) { DiagMissingBody("function", !decl.identifier.Valid() ? "" : " '" + decl.identifier + "'", lastToken.End()); } } if (decl.TestAttr(Attribute::FOREIGN)) { if (decl.TestAttr(Attribute::GENERIC)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_foreign_func_should_not_be_generic, *fb->generic); decl.EnableAttr(Attribute::IS_BROKEN); } if (!fb->retType) { auto foreignPos = std::find_if(decl.modifiers.begin(), decl.modifiers.end(), [](const Modifier& it) { return it.modifier == TokenKind::FOREIGN; }); CJC_ASSERT(foreignPos != decl.modifiers.end()); ParseDiagnoseRefactor(DiagKindRefactor::parse_foreign_func_must_declare_return_type, MakeRange(foreignPos->begin, decl.keywordPos + 4)); // Keyword func's length is 4. decl.EnableAttr(Attribute::IS_BROKEN); } if (fb->body) { ParseDiagnoseRefactor(DiagKindRefactor::parse_foreign_function_with_body, *fb->body); decl.EnableAttr(Attribute::IS_BROKEN); } } CheckVariableParams(decl); } void ParserImpl::FFICheckClassLikeFuncBody( FuncDecl& decl, DiagKindRefactor functionMustHaveReturnType, DiagKindRefactor functionCanNotHaveBody) { if (!decl.funcBody) { return; } if (!decl.funcBody->retType && !decl.funcBody->TestAttr(Attribute::HAS_BROKEN)) { ParseDiagnoseRefactor(functionMustHaveReturnType, decl); decl.EnableAttr(Attribute::IS_BROKEN); } if (decl.funcBody->body) { ParseDiagnoseRefactor(functionCanNotHaveBody, decl, decl.identifier); decl.EnableAttr(Attribute::IS_BROKEN); } } // TODO: move to JFFIParserImpl namespace { using Cangjie::ParserImpl; bool IsAbstractClassAbstractFunction( bool hasFuncAbstractModifier, bool hasClassAbstractModifier, const Node& classDecl) { return hasFuncAbstractModifier && classDecl.astKind == ASTKind::CLASS_DECL && hasClassAbstractModifier; } } void ParserImpl::CheckClassLikeFuncBodyAbstractness(FuncDecl& decl) { bool isCommon = decl.TestAttr(Attribute::COMMON); auto outerModifiers = decl.outerDecl->modifiers; bool inAbstract = HasModifier(outerModifiers, TokenKind::ABSTRACT); bool inCJMP = HasModifier(outerModifiers, TokenKind::PLATFORM) || HasModifier(outerModifiers, TokenKind::COMMON); bool inAbstractCJMP = inAbstract && inCJMP; bool isJavaMirrorOrJavaMirrorSubtype = decl.outerDecl->TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE); // explicit ABSTRACT modifier allowed only in COMMON ABSTRACT class or in JFFI if (HasModifier(decl.modifiers, TokenKind::ABSTRACT) && !isCommon && !inAbstractCJMP && !isJavaMirrorOrJavaMirrorSubtype) { ParseDiagnoseRefactor(DiagKindRefactor::parse_explicitly_abstract_only_for_cjmp_abstract_class, decl, "function"); } if (decl.funcBody && decl.funcBody->body) { return; } CJC_NULLPTR_CHECK(decl.outerDecl); if (decl.TestAttr(Attribute::CONSTRUCTOR)) { return; } if (decl.outerDecl->TestAttr(Attribute::OBJ_C_MIRROR)) { decl.DisableAttr(Attribute::ABSTRACT); return; } bool inClass = decl.outerDecl->astKind == ASTKind::CLASS_DECL; bool inAbstractCJMPClass = inAbstractCJMP && inClass; if (inAbstractCJMP && !HasModifier(decl.modifiers, TokenKind::ABSTRACT)) { // OPEN func without ABSTRACT or COMMON must have body in CJMP ABSTRACT class if (HasModifier(decl.modifiers, TokenKind::OPEN) && inAbstractCJMP) { DiagMissingBody("function", !decl.identifier.Valid() ? "" : " '" + decl.identifier + "'", lastToken.End()); } decl.DisableAttr(Attribute::ABSTRACT); } bool hasAbstractModifier = HasModifier(decl.modifiers, TokenKind::ABSTRACT); bool hasOuterDeclAbstractModifier = HasModifier(decl.outerDecl->modifiers, TokenKind::ABSTRACT); if (isJavaMirrorOrJavaMirrorSubtype) { if ((IsAbstractClassAbstractFunction(hasAbstractModifier, hasOuterDeclAbstractModifier, *decl.outerDecl) || (decl.outerDecl->astKind == ASTKind::INTERFACE_DECL && !HasModifier(decl.modifiers, TokenKind::STATIC))) && !Interop::Java::IsImpl(*decl.outerDecl)) { decl.EnableAttr(Attribute::ABSTRACT); return; } else { decl.DisableAttr(Attribute::ABSTRACT); } } if (hasAbstractModifier && !inAbstractCJMPClass) { Ptr abstractMod = nullptr; for (auto& modifier : decl.modifiers) { if (modifier.modifier == TokenKind::ABSTRACT) { abstractMod = Ptr(&modifier); } } CJC_NULLPTR_CHECK(abstractMod); ChainScope cs(*this, Ptr(&decl)); DiagIllegalModifierInScope(*abstractMod); return; } if (isJavaMirrorOrJavaMirrorSubtype) { return; } if (isCommon && decl.outerDecl->astKind == ASTKind::INTERFACE_DECL) { decl.EnableAttr(Attribute::ABSTRACT); return; } if (decl.TestAnyAttr(Attribute::FOREIGN, Attribute::INTRINSIC)) { return; } auto& fb = decl.funcBody; if (fb->TestAttr(Attribute::HAS_BROKEN)) { return; } if (decl.TestAttr(Attribute::ABSTRACT) || (!decl.funcBody->body && !decl.TestAttr(Attribute::FINALIZER))) { if (!decl.funcBody->retType) { ParseDiagnoseRefactor(DiagKindRefactor::parse_abstract_func_must_have_return_type, lastToken.End()); } } else if (!isCommon) { decl.DisableAttr(Attribute::ABSTRACT); DiagMissingBody("function", !decl.identifier.Valid() ? "" : " '" + decl.identifier + "'", lastToken.End()); } } void ParserImpl::CheckVariableParams(AST::FuncDecl& decl) { if (!decl.funcBody || decl.funcBody->paramLists.size() != 1) { return; } auto& params = decl.funcBody->paramLists[0]->params; std::optional varargIdx = std::nullopt; for (size_t index = 0; index < params.size(); index++) { if (params[index]->identifier == ELLIPSIS) { decl.hasVariableLenArg = true; decl.variadicArgIndex = index; // For fmt, variable parameter is erased. decl.funcBody->paramLists[0]->hasVariableLenArg = true; decl.funcBody->paramLists[0]->variadicArgIndex = index; if (!decl.TestAttr(Attribute::FOREIGN)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_variable_length_parameter_only_in_the_foreign_function, MakeRange(decl.keywordPos, decl.identifier.End())); decl.EnableAttr(Attribute::IS_BROKEN); break; } if (index == 0) { ParseDiagnoseRefactor( DiagKindRefactor::parse_variable_length_parameter_can_not_be_first, *params[index]); decl.EnableAttr(Attribute::IS_BROKEN); } else if (index != params.size() - 1) { ParseDiagnoseRefactor( DiagKindRefactor::parse_variable_length_parameter_must_in_the_end, *params[index]); decl.EnableAttr(Attribute::IS_BROKEN); } else { varargIdx = {index}; } } } if (varargIdx) { auto nodeIter = decl.funcBody->paramLists[0]->params.begin() + static_cast(*varargIdx); decl.funcBody->paramLists[0]->params.erase(nodeIter); } } void ParserImpl::CheckVariableParams(AST::PrimaryCtorDecl& decl) { if (!decl.funcBody || decl.funcBody->paramLists.size() != 1) { return; } auto& params = decl.funcBody->paramLists[0]->params; for (size_t index = 0; index < params.size(); index++) { if (params[index]->identifier == ELLIPSIS) { decl.hasVariableLenArg = true; // For fmt, variable parameter is erased. decl.funcBody->paramLists[0]->hasVariableLenArg = true; decl.funcBody->paramLists[0]->variadicArgIndex = index; // Primary constructor is certainly not a foreign function. ParseDiagnoseRefactor(DiagKindRefactor::parse_variable_length_parameter_only_in_the_foreign_function, MakeRange(decl.keywordPos, decl.identifier.End())); decl.EnableAttr(Attribute::IS_BROKEN); break; } } } void ParserImpl::ParsePropMemberBody(const ScopeKind& scopeKind, FuncBody& fb) { if (fb.generic) { if (scopeKind == ScopeKind::PROP_MEMBER_SETTER_BODY) { DiagGetOrSetCannotBeGeneric("getter", *fb.generic); } if (scopeKind == ScopeKind::PROP_MEMBER_GETTER_BODY) { DiagGetOrSetCannotBeGeneric("setter", *fb.generic); } } if (!Seeing(TokenKind::LCURL)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_left_brace, lookahead, ConvertToken(lookahead)); ConsumeUntilDecl(TokenKind::LCURL); if (!Seeing(TokenKind::LCURL)) { return; } } fb.body = ParseBlock(ScopeKind::FUNC_BODY); fb.end = fb.body->end; } OwnedPtr ParserImpl::ParseFuncBody(ScopeKind scopeKind) { OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); ret->begin = lookahead.Begin(); // main is never generic if (scopeKind != ScopeKind::MAIN_BODY && Skip(TokenKind::LT)) { ret->generic = ParseGeneric(); } ParseFuncParameters(scopeKind, *ret); // To deal with the endPos of the FuncDecl without return type and funcBody. // ex: foreign func test(a: Int64) // The end pos of the FuncDecl above should be next to ). auto paramsEndToken = lastToken; if (scopeKind == ScopeKind::PROP_MEMBER_SETTER_BODY || scopeKind == ScopeKind::PROP_MEMBER_GETTER_BODY) { ParsePropMemberBody(scopeKind, *ret); return ret; } if (Skip(TokenKind::COLON)) { ret->colonPos = lastToken.Begin(); // `This` type can only be used as the return type of instance member function if (scopeKind == ScopeKind::CLASS_BODY && Seeing(TokenKind::THISTYPE)) { enableThis = true; } if (SeeingAny(GetTypeFirst()) || SeeingContextualKeyword()) { ret->retType = ParseType(); } else { ret->EnableAttr(Attribute::IS_BROKEN); DiagExpectedIdentifierFuncBody(ret.get()); TryConsumeUntilAny({TokenKind::LCURL, TokenKind::WHERE}); } if (scopeKind == ScopeKind::CLASS_BODY) { enableThis = false; } } ParseFuncGenericConstraints(*ret); if (Seeing(TokenKind::LCURL)) { ret->body = ParseBlock(ScopeKind::FUNC_BODY); ret->end = ret->body->end; } else if (ret->generic && !ret->generic->genericConstraints.empty()) { auto& lastGC = *ret->generic->genericConstraints.cbegin(); ret->end = lastGC->end; } else { ret->end = ret->retType ? ret->retType->end : paramsEndToken.End(); } return ret; } void ParserImpl::ParseFuncParameters(const ScopeKind& scopeKind, FuncBody& fb) { if (Seeing(TokenKind::LPAREN)) { do { if (scopeKind == ScopeKind::PRIMARY_CONSTRUCTOR_BODY_FOR_CLASS || scopeKind == ScopeKind::PRIMARY_CONSTRUCTOR_BODY_FOR_STRUCT || scopeKind == ScopeKind::PROP_MEMBER_SETTER_BODY || scopeKind == ScopeKind::PROP_MEMBER_GETTER_BODY) { (void)fb.paramLists.emplace_back(ParseParameterList(scopeKind)); } else { (void)fb.paramLists.emplace_back(ParseParameterList()); } } while (Seeing(TokenKind::LPAREN)); } else { (void)fb.paramLists.emplace_back(MakeInvalid(lookahead.Begin())); ParseDiagnoseRefactor(scopeKind == ScopeKind::MAIN_BODY ? DiagKindRefactor::parse_expected_left_paren : DiagKindRefactor::parse_expected_lt_paren, lookahead, ConvertToken(lookahead)); fb.EnableAttr(Attribute::HAS_BROKEN); } } void ParserImpl::ParseFuncGenericConstraints(const FuncBody& fb) { if (Skip(TokenKind::WHERE)) { if (!fb.generic) { DiagUnexpectedWhere(lastToken); } else { fb.generic->genericConstraints = ParseGenericConstraints(); } } else if (fb.generic && (Seeing(TokenKind::IDENTIFIER))) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_where_brace, lookahead, ConvertToken(lookahead)); } } void ParserImpl::ParseEllipsisParameter(FuncParam& funcParam) { funcParam.identifier = std::string{ELLIPSIS}; Next(); funcParam.identifier.SetPos(lookahead.Begin(), lookahead.End()); funcParam.begin = lookahead.Begin(); funcParam.end = lookahead.End(); funcParam.isVar = false; funcParam.end = lookahead.End(); } void ParserImpl::ParseParameter(ScopeKind scopeKind, FuncParam& fp) { Position memberStartPos; ParseLetOrVarInParamList(scopeKind, fp, memberStartPos); if (Seeing(TokenKind::ELLIPSIS)) { ParseEllipsisParameter(fp); return; } // process of '_'. And wildcard is not allowed in primary constructor let/var if (Skip(TokenKind::WILDCARD) && fp.hasLetOrVar) { ParseDiagnoseRefactor(DiagKindRefactor::parse_wildcard_can_not_be_used_as_member_name, lastToken); } if (lastToken.kind == TokenKind::WILDCARD) { fp.identifier = lastToken.Value(); fp.identifier.SetPos(lastToken.Begin(), lastToken.End()); } else { fp.identifier = ExpectIdentifierWithPos(fp); } fp.begin = lookahead.Begin(); if (fp.identifier == INVALID_IDENTIFIER) { fp.type = MakeOwned(lookahead.Begin()); fp.assignment = MakeOwned(lookahead.Begin()); fp.end = lookahead.End(); fp.EnableAttr(Attribute::HAS_BROKEN); fp.EnableAttr(Attribute::IS_BROKEN); return; } if (scopeKind == ScopeKind::PROP_MEMBER_SETTER_BODY || scopeKind == ScopeKind::PROP_MEMBER_GETTER_BODY) { fp.end = lastToken.End(); } else { if (Skip(TokenKind::NOT)) { if (fp.identifier == "_") { // add diagnose of '_!' auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_character, lastToken, "\':\'", lastToken.Value()); builder.AddMainHintArguments("\':\'"); } fp.notMarkPos = lastToken.Begin(); fp.isNamedParam = true; } if (!Skip(TokenKind::COLON) && !fp.TestAttr(Attribute::HAS_BROKEN)) { DiagExpectCharacter("':'"); fp.type = MakeOwned(lookahead.Begin()); fp.end = fp.type->end; fp.EnableAttr(Attribute::HAS_BROKEN); fp.EnableAttr(Attribute::IS_BROKEN); return; } fp.colonPos = lastToken.Begin(); fp.type = ParseType(); fp.end = fp.type->end; ParseAssignInParam(fp); } fp.begin = fp.isMemberParam ? memberStartPos : fp.begin; } void ParserImpl::ParseAssignInParam(FuncParam& fp) { if (Skip(TokenKind::ASSIGN)) { if (!fp.isNamedParam) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_dot_lparen, lastToken, ConvertToken(lastToken)); } fp.assignPos = lastToken.Begin(); fp.assignment = ParseExpr(); fp.end = fp.assignment->end; } } OwnedPtr ParserImpl::ParseParameterList(ScopeKind scopeKind) { if (!Skip(TokenKind::LPAREN)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_left_paren, lookahead, ConvertToken(lookahead)); return MakeInvalid(lookahead.Begin()); } OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); // Parameter with value, i.e. optional parameter must list behind all non-optional parameters. Ptr namedParameter{nullptr}; Ptr memberParam{nullptr}; ret->begin = lookahead.Begin(); ret->leftParenPos = lookahead.Begin(); ParseZeroOrMoreSepTrailing([&ret](const Position& pos) { ret->params.back()->commaPos = pos; }, [&ret, this, scopeKind, &namedParameter, &memberParam]() { ret->params.push_back(ParseParamInParamList(scopeKind, namedParameter, memberParam)); }, TokenKind::RPAREN); if (!Skip(TokenKind::RPAREN)) { DiagExpectedRightDelimiter("(", ret->leftParenPos); ret->end = lookahead.End(); ret->EnableAttr(Attribute::IS_BROKEN); return ret; } if (scopeKind == ScopeKind::PROP_MEMBER_SETTER_BODY) { if (ret->params.empty()) { ParseDiagnoseRefactor( DiagKindRefactor::parse_setter_must_contain_one_parameter, MakeRange(ret->begin, lastToken.End())); } else if (ret->params.size() > 1) { ParseDiagnoseRefactor( DiagKindRefactor::parse_setter_can_only_accept_one_parameter, MakeRange(ret->begin, lastToken.End())); } } ret->rightParenPos = lastToken.Begin(); ret->end = lastToken.End(); return ret; } OwnedPtr ParserImpl::ParseParamInParamList( const ScopeKind& scopeKind, Ptr& meetNamedParameter, Ptr& meetMemberParams) { // For the situation: func f(a: Int64, @M(), @M(), ...) // or func f(a: Int64, @!M() b: Int32, ...) if (SeeingMacroCallDecl() && !this->enableCustomAnno) { return ParseMacroCall(scopeKind); } OwnedPtr param = MakeOwned(); ChainScope cs(*this, param.get()); ParseAnnotations(param->annotations); ParseModifiers(param->modifiers); ParseParameter(scopeKind, *param); // Check named parameter rule. if (meetNamedParameter && !param->isNamedParam) { DiagNamedParameterAfterUnnamed(*param, *meetNamedParameter); } // Check member variable parameter rule. if (meetMemberParams && !param->isMemberParam) { DiagMemberParameterAfterRegular(*param, *meetMemberParams); } if (param->isNamedParam && !meetNamedParameter) { meetNamedParameter = param.get(); } if (param->isMemberParam && !meetMemberParams) { meetMemberParams = param.get(); } CheckModifierInParamList(scopeKind, param->modifiers, param->isMemberParam, *param); if (Seeing(TokenKind::COMMA)) { param->commaPos = lookahead.Begin(); } SetDeclBeginPos(*param); CheckDeprecationOfFuncParam(*param); return param; } void ParserImpl::ParseLetOrVarInParamList(const ScopeKind& scopeKind, FuncParam& fp, Position& memberStartPos) { bool isPrimaryConstr = (scopeKind == ScopeKind::PRIMARY_CONSTRUCTOR_BODY_FOR_CLASS) || (scopeKind == ScopeKind::PRIMARY_CONSTRUCTOR_BODY_FOR_STRUCT); if (Skip(TokenKind::LET) || Skip(TokenKind::VAR)) { fp.hasLetOrVar = true; fp.keywordPos = lastToken.Begin(); if (!isPrimaryConstr) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_parameter_rp, lastToken, ConvertToken(lastToken)); } else { // Member variable parameter for class/struct main constructor. fp.isMemberParam = true; fp.isVar = lastToken == "var"; fp.begin = memberStartPos = lastToken.Begin(); } } else if (HasModifier(fp.modifiers, TokenKind::CONST) && lastToken == "const") { std::string scope = isPrimaryConstr ? "primary constructor paramlist" : "paramlist"; std::string decl = isPrimaryConstr ? "const member variable declaration" : "const variable declaration"; auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_unexpected_declaration_in_scope, MakeRange(lastToken.Begin(), lastToken.Begin() + std::string("const").size()), decl, scope); builder.AddMainHintArguments(decl); } } void ParserImpl::CheckModifierInParamList( const ScopeKind& scopeKind, const std::set& modifiers, bool isMember, AST::FuncParam& fp) { if (!modifiers.empty()) { if (!isMember) { if (!fp.TestAttr(Attribute::HAS_BROKEN)) { DiagExpectNoModifier(*modifiers.begin()); } } else { if (HasModifier(modifiers, TokenKind::CONST)) { DiagUnExpectedModifierOnDeclaration(fp); } auto attrs = CheckDeclModifiers(modifiers, scopeKind, DefKind::VARIABLE); for (auto& it : attrs) { fp.EnableAttr(it); } } } } template void ParserImpl::CheckIntrinsicFunc(T& fd) { auto iter = std::find_if( intrinsics.begin(), intrinsics.end(), [&fd](auto fd1) { return fd.identifier == fd1->identifier; }); if (iter != intrinsics.end()) { DiagDuplicatedIntrinsicFunc(fd, **iter); } else { intrinsics.emplace(&fd); } } cangjie_compiler-1.0.7/src/Parse/ParseExpr.cpp000066400000000000000000001170101510705540100212650ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the expression parsing (except for atoms, which are * in ParseAtom.cpp). */ #include "ParserImpl.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Walker.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace Cangjie::AST; namespace { // ++ or -- (used later in ParseIncOrDec) const int INCREMENT_OP_LENGTH = 2; inline bool IsCompoundAssign(TokenKind token) { return token >= TokenKind::ADD_ASSIGN && token <= TokenKind::RSHIFT_ASSIGN; } // For fmt, these exprs no need to add semicolon info, because child expr of these nodes has semicolon already. const static std::vector G_NO_NEED_SEMI_EXPR_LIST = {ASTKind::THROW_EXPR, ASTKind::RETURN_EXPR, ASTKind::ASSIGN_EXPR, ASTKind::UNARY_EXPR, ASTKind::SPAWN_EXPR, ASTKind::IF_EXPR, ASTKind::TRY_EXPR, ASTKind::FOR_IN_EXPR, ASTKind::WHILE_EXPR}; // It looks like operators, but not. For better error reporting. const static std::vector FAKE_OPERATORS = {TokenKind::COLON}; } // namespace bool ParserImpl::SeeingExprOperator() { auto token = Peek(); if (newlineSkipped && (token.kind == TokenKind::SUB || token.kind == TokenKind::QUEST)) { return false; } for (auto& [k, _] : combinator) { if (SeeingCombinator(k)) { return true; } } if (Precedence(token.kind) != INVALID_PRECEDENCE) { return true; } return IsCompoundAssign(token.kind) || token.kind == TokenKind::ASSIGN; } bool ParserImpl::TypeArgsMaybeConfusedWithExprWithComma(const std::vector>& typeArgs) const { if (typeArgs.size() <= 1) { return false; // "a", "a>", "a<(b)>"... no confusion. } if (TypesMaybeConfusedWithExprWithComma(ConverVector(typeArgs))) { return true; } return false; } bool ParserImpl::TypesMaybeConfusedWithExprWithComma(const std::vector>& types) const { for (auto itr = types.rbegin(); itr != types.rend(); ++itr) { if ((*itr)->astKind == ASTKind::TUPLE_TYPE) { // "(ag)" may be confused auto curType = RawStaticCast(*itr); if (!TypesMaybeConfusedWithExprWithComma(ConverVector(curType->fieldTypes))) { return false; } } else if ((*itr)->astKind == ASTKind::PAREN_TYPE) { // "(ag)"" may be confused auto curType = RawStaticCast(*itr); if (!TypesMaybeConfusedWithExprWithComma({curType->type.get()})) { return false; } } else if ((*itr)->astKind == ASTKind::REF_TYPE || (*itr)->astKind == ASTKind::QUALIFIED_TYPE) { // "(x.a>=g)", "(a, f>g)", "(a, e>f)" may be confused if ((*itr)->GetTypeArgs().empty()) { continue; } if (!TypesMaybeConfusedWithExprWithComma((*itr)->GetTypeArgs())) { return false; } } else { // PrimitiveType OptionType VArrayType(ConstantType) ThisType FuncType cannot be used as expression return false; } } return true; } bool ParserImpl::IsLegFollowForGenArgInExprWithComma(ExprKind ek) { CJC_ASSERT(IsExprFollowedComma(ek)); if (Seeing(exprsFollowedCommas.at(ek).second)) { return true; } if (newlineSkipped && SeeingAny({TokenKind::LCURL, TokenKind::QUEST, TokenKind::LPAREN, TokenKind::LSQUARE, TokenKind::SUB})) { return false; } if (SeeingAny({TokenKind::LPAREN, TokenKind::LSQUARE, TokenKind::COMMA, TokenKind::DOT, TokenKind::LCURL})) { return true; } for (auto& [k, _] : combinator) { if (SeeingCombinator(k)) { return true; } } auto token = Peek(); if (Precedence(token.kind) != INVALID_PRECEDENCE) { return true; } return false; } bool ParserImpl::IsNeedToCreateOptionalChain(TokenKind token, AST::Expr& expr) const { if (!IsCompoundAssign(token) && token != TokenKind::ASSIGN && expr.hasQuestSuffix) { return true; } if (auto incdecl = DynamicCast(&expr); incdecl && incdecl->expr && incdecl->expr->hasQuestSuffix) { return true; } return false; } Token ParserImpl::GetExprOperator() { auto token = Peek(); auto iter = std::find_if(combinator.rbegin(), combinator.rend(), [this](auto& k) { return SeeingCombinator(k.first); }); if (iter != combinator.rend()) { token.kind = iter->second.first; std::string_view op{iter->second.second}; token.SetValuePos(std::string{op}, token.Begin(), token.Begin() + op.size()); } return token; } void ParserImpl::SkipExprOperator() { Peek(); auto iter = std::find_if(combinator.rbegin(), combinator.rend(), [this](auto& k) { return SeeingCombinator(k.first); }); if (iter != combinator.rend()) { SkipCombinator(iter->first); return; } Next(); } // Will only create leftExpr part and operator, the right part will register later. OwnedPtr ParserImpl::MakeOperatorExpr(OwnedPtr& lExpr, const Token& oTok) { if (oTok.kind == TokenKind::IS) { OwnedPtr isExpr = MakeOwned(); SpreadAttrAndConsume(lExpr.get(), isExpr.get(), {}); if (lExpr->astKind == ASTKind::IS_EXPR || lExpr->astKind == ASTKind::AS_EXPR) { DiagChainedAsExpr(*lExpr, oTok); } isExpr->begin = lExpr->begin; isExpr->isPos = oTok.Begin(); isExpr->leftExpr = std::move(lExpr); return isExpr; } else if (oTok.kind == TokenKind::AS) { OwnedPtr asExpr = MakeOwned(); SpreadAttrAndConsume(lExpr.get(), asExpr.get(), {}); if (lExpr->astKind == ASTKind::IS_EXPR || lExpr->astKind == ASTKind::AS_EXPR) { DiagChainedAsExpr(*lExpr, oTok); } asExpr->begin = lExpr->begin; asExpr->asPos = oTok.Begin(); asExpr->leftExpr = std::move(lExpr); return asExpr; } else if (oTok.kind == TokenKind::RANGEOP || oTok.kind == TokenKind::CLOSEDRANGEOP) { OwnedPtr rangeExpr = MakeOwned(); SpreadAttrAndConsume(lExpr.get(), rangeExpr.get(), {}); rangeExpr->begin = lExpr->begin; rangeExpr->isClosed = oTok.kind == TokenKind::CLOSEDRANGEOP; rangeExpr->rangePos = oTok.Begin(); rangeExpr->startExpr = std::move(lExpr); return rangeExpr; } else if (IsCompoundAssign(oTok.kind) || oTok.kind == TokenKind::ASSIGN) { OwnedPtr asinExpr = MakeOwned(); SpreadAttrAndConsume(lExpr.get(), asinExpr.get(), {}); asinExpr->begin = lExpr->begin; asinExpr->hasQuestSuffix = lExpr->hasQuestSuffix; asinExpr->leftValue = std::move(lExpr); asinExpr->assignPos = oTok.Begin(); asinExpr->op = oTok.kind; asinExpr->isCompound = IsCompoundAssign(oTok.kind); return asinExpr; } OwnedPtr biExpr = MakeOwned(); SpreadAttrAndConsume(lExpr.get(), biExpr.get(), {}); biExpr->begin = lExpr->begin; biExpr->op = oTok.kind; biExpr->operatorPos = oTok.Begin(); biExpr->leftExpr = std::move(lExpr); return biExpr; } void ParserImpl::CheckWildcardInExpr(const OwnedPtr& root) { // if any expr contains wildcard(_), e.g TupleLit、ParenExpr, complains Walker(root.get(), [this](Ptr node) -> VisitAction { if (node->astKind == ASTKind::MATCH_CASE_OTHER) { // Simple match expr, no match selector,so skip wildcard expr in the match case. return VisitAction::SKIP_CHILDREN; } if (node->astKind == ASTKind::ASSIGN_EXPR && StaticAs(node)->op == TokenKind::ASSIGN) { return VisitAction::SKIP_CHILDREN; } if (node->astKind == ASTKind::WILDCARD_EXPR) { if ((node->begin.line == lookahead.Begin().line) && (lookahead.Begin().line == lastToken.Begin().line) && lookahead.kind != TokenKind::SENTINEL) { ConsumeUntilAny({TokenKind::NL, TokenKind::RPAREN, TokenKind::COMMA, TokenKind::RCURL}, false); } auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_unexpected_expected_found, *node, "_ wildcard"); builder.AddMainHintArguments("expression", "wildcard"); } return VisitAction::WALK_CHILDREN; }).Walk(); } void ParserImpl::RegisterRightExpr(const OwnedPtr& expr, OwnedPtr&& rExpr) { // rExpr cannot be a wildcard, or tuple containing wildcards, e.g (1, _) 、(1, (2, _)) if (expr->astKind == ASTKind::ASSIGN_EXPR && StaticAs(expr.get())->op == TokenKind::ASSIGN) { CheckWildcardInExpr(rExpr); } expr->end = rExpr->end; if (expr->astKind == ASTKind::BINARY_EXPR) { auto biExpr = StaticAs(expr.get()); biExpr->rightExpr = std::move(rExpr); return; } if (expr->astKind == ASTKind::ASSIGN_EXPR) { auto assiExpr = StaticAs(expr.get()); assiExpr->rightExpr = std::move(rExpr); return; } if (expr->astKind == ASTKind::RANGE_EXPR) { auto rangeExpr = StaticAs(expr.get()); rangeExpr->end = rExpr->end; rangeExpr->stopExpr = std::move(rExpr); if (Skip(TokenKind::COLON)) { rangeExpr->colonPos = lastToken.Begin(); constexpr std::string_view dot2{".."}; auto preT = Token{ TokenKind::RANGEOP, std::string{dot2}, rangeExpr->rangePos, rangeExpr->rangePos + dot2.size()}; preT.kind = rangeExpr->isClosed ? TokenKind::CLOSEDRANGEOP : preT.kind; std::string op = rangeExpr->isClosed ? std::string{"..="} : preT.Value(); preT.SetValuePos(op, preT.Begin(), preT.Begin() + op.size()); rangeExpr->stepExpr = ParseExpr(preT); rangeExpr->end = rangeExpr->stepExpr->end; } return; } } const std::vector ParserImpl::combinedBackarrow{TokenKind::LT, TokenKind::SUB}; OwnedPtr ParserImpl::ParseLetPattern(ExprKind ek) { auto res = MakeOwned(); (void)Skip(TokenKind::LET); res->begin = lastToken.Begin(); // parse patterns auto first = ParsePattern(); first->ctxExpr = res; res->patterns.emplace_back(std::move(first)); while (Skip(TokenKind::BITOR)) { res->orPos.push_back(lastToken.Begin()); auto pattern = ParsePattern(); pattern->ctxExpr = res; res->patterns.emplace_back(std::move(pattern)); } // parse initializer auto consumeTarget = [this]() { return SeeingAny({TokenKind::AND, TokenKind::OR, TokenKind::RPAREN, TokenKind::LET, TokenKind::BACKARROW}) || SeeingCombinator(combinedBackarrow); }; bool foundBackarrow{true}; if (!Skip(TokenKind::BACKARROW) && !SeeingCombinator(combinedBackarrow)) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_backarrow_in_let_cond, lookahead, ek == ExprKind::WHILE_COND_EXPR ? "while-let" : "if-let", ConvertToken(lookahead)); ConsumeUntilAny(consumeTarget, false); foundBackarrow = false; res->initializer = MakeInvalid(lookahead.Begin()); res->end = res->initializer->end; res->EnableAttr(Attribute::HAS_BROKEN); } res->backarrowPos = lastToken.Begin(); if (foundBackarrow) { res->initializer = ParseExpr(ExprKind::LET_PATTERN); } res->end = res->initializer->end; if (!res->initializer && !res->TestAttr(Attribute::HAS_BROKEN)) { DiagMatchCaseBodyCannotBeEmpty(res->backarrowPos + std::string_view("<-").size()); res->EnableAttr(Attribute::HAS_BROKEN); } return res; } // Parse IndexAccess, e.g a[1]、a[1..]、a[..]、a[..1]、a[1..2] // It's important to note that IndexAccess and RangeExpr are different, RangeExpr is a subset of IndexAccess // RangeExpr must have two subexpressions, like 1 and 2 in a[1..2], // however IndexAccess can be a range with zero, one or two subexpression, like a[..]、 a[1..]、a[1..3] // or IndexAccess is an expression in itself, like 1 in a[1] // If IndexAccess is a range, here we just reuse AST::RangeExpr to store subexpression(s) OwnedPtr ParserImpl::ParseIndexAccess() { if (IsCompoundAssign(Peek().kind) || Peek().kind == TokenKind::ASSIGN) { // an error is reported for illeal index access, e.g: a[+=1], a[=1] ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_expression, lastToken.End(), lastToken.Value(), lookahead.Value()); ConsumeUntilAny({TokenKind::NL, TokenKind::SEMI, TokenKind::RSQUARE}, false); return MakeInvalid(lookahead.Begin()); } auto ret = MakeOwned(); ret->begin = Peek().Begin(); OwnedPtr firstExpr; // dealing with expr that follows '[' if (!Seeing(TokenKind::RANGEOP) && !Seeing(TokenKind::CLOSEDRANGEOP)) { // parse an expr and ensure that operator(s)'s priority in the expr is not lower than TokenKind::RANGEOP firstExpr = ParseExpr(Token{TokenKind::RANGEOP}); } if (!Seeing(TokenKind::RANGEOP) && !Seeing(TokenKind::CLOSEDRANGEOP)) { // two scenarios will get to this branch // 1. means IndexAccess is an expression in itself // 2. firstExpr is followed by an operator // and the priority of the operator is lower than that of TokenKind::RANGEOP return ParseExpr(Token{TokenKind::DOT}, std::move(firstExpr)); } // Seeing TokenKind::RANGEOP || Seeing(TokenKind::CLOSEDRANGEOP) ret->startExpr = std::move(firstExpr); ret->isClosed = lookahead.kind == TokenKind::CLOSEDRANGEOP; ret->rangePos = lookahead.Begin(); Next(); ret->end = lastToken.End(); if (!Seeing(TokenKind::RSQUARE) && !Seeing(TokenKind::COLON)) { // parse stop expr ret->stopExpr = ParseExpr(Token{TokenKind::RANGEOP}); ret->end = ret->stopExpr->end; } if (ret->isClosed && ret->stopExpr == nullptr) { // an error is reported for illeal index access,e.g: a[1..=]、a[..=] ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_expression, lastToken.End(), lastToken.Value(), lookahead.Value()); } if (Seeing(TokenKind::COLON)) { if (!ret->startExpr || !ret->stopExpr) { DiagUnexpectedColonInRange(*ret); ConsumeUntilAny({TokenKind::NL, TokenKind::SEMI, TokenKind::RSQUARE}, false); return ret; } Next(); ret->colonPos = lastToken.Begin(); ret->stepExpr = ParseExpr(Token{TokenKind::RANGEOP}); ret->end = ret->stepExpr->end; } return ParseExpr(Token{TokenKind::DOT}, std::move(ret)); } OwnedPtr ParserImpl::ParseExpr(ExprKind ek) { // The unknown_expr only work for libast. if (ek == ExprKind::UNKNOWN_EXPR) { if (Seeing(TokenKind::LET)) { return ParseLetPattern(ek); } } OwnedPtr ret; if (ek == ExprKind::INDEX_EXPR) { ret = ParseIndexAccess(); } else if (enableCustomAnno && SeeingIfAvailable()) { // only parse @IfAvailable after macro expansion ret = ParseIfAvailable(); } else if (ek == ExprKind::VAR_INIT) { ret = ParseExpr(Token{TokenKind::DOT}); if (ret->astKind == ASTKind::ASSIGN_EXPR) { DiagCannotHaveAssignmentInInit(*ret); } } else { ret = ParseExpr(Token{TokenKind::DOT}, nullptr, ek); } if (!SeeingExprOperator() && ek != ExprKind::EXPR_IN_TUPLE && ek != ExprKind::EXPR_IN_IF_COND_TUPLE && ek != ExprKind::EXPR_IN_WHILE_COND_TUPLE) { // wildcard and tuple that contains wildcard cannot appear alone,e.g: if(_){}, // judging EXPR_IN_TUPLE is to check(traverse) only once. CheckWildcardInExpr(ret); } // For fmt, record the semicolon position. if (Seeing(TokenKind::SEMI) && Utils::NotIn(ret->astKind, G_NO_NEED_SEMI_EXPR_LIST)) { ret->hasSemi = true; ret->semiPos = lookahead.Begin(); } if (ret->astKind == ASTKind::MACRO_EXPAND_EXPR) { auto mee = StaticAs(ret.get()); mee->invocation.scope = ek; } if (SeeingAny(FAKE_OPERATORS)) { DiagExpecetedOpeOrEnd(); ConsumeUntilAny({TokenKind::NL}); } return ret; } OwnedPtr ParserImpl::ParseIfAvailable() { OwnedPtr arg{}; OwnedPtr lambda1{}; OwnedPtr lambda2{}; Skip(TokenKind::AT); auto begin = lastToken.Begin(); Skip(TokenKind::IDENTIFIER); auto fail = [this, &begin, &arg, &lambda1, &lambda2]() { Next(); auto diagBag = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_character, lastToken.Begin(), "','", "'" + lastToken.Value() + "'"); diagBag.AddMainHintArguments("','"); auto ret = MakeOwned(std::move(arg), std::move(lambda1), std::move(lambda2)); ret->begin = std::move(begin); ret->EnableAttr(Attribute::IS_BROKEN); ret->end = lastToken.End(); return ret; }; bool bad{false}; Position lparenPos{}; bool lparenSkipped{false}; if (Skip(TokenKind::LPAREN)) { lparenPos = lastToken.Begin(); lparenSkipped = true; } else { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_character, lookahead.Begin(), "(", lookahead.Value()); bad = true; } arg = ParseFuncArg(); if (arg->name.Empty()) { ParseDiagnoseRefactor(DiagKindRefactor::parse_ifavailable_arg_no_name, lookahead); bad = true; } if (!Skip(TokenKind::COMMA)) { auto ret = fail(); ConsumeUntil(TokenKind::RPAREN); return ret; } auto parseLambda = [this, &bad] () -> OwnedPtr { auto expr1 = ParseExpr(); if (auto e1 = Is(expr1)) { return OwnedPtr{StaticCast(expr1.release())}; } else { ParseDiagnoseRefactor(DiagKindRefactor::parse_ifavailable_not_lambda, *expr1); bad = true; } return {}; }; lambda1 = parseLambda(); if (!Skip(TokenKind::COMMA)) { auto ret = fail(); ConsumeUntil(TokenKind::RPAREN); return ret; } lambda2 = parseLambda(); auto ret = MakeOwned(std::move(arg), std::move(lambda1), std::move(lambda2)); ret->begin = std::move(begin); if (!Skip(TokenKind::RPAREN)) { if (lparenSkipped) { auto db = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_right_delimiter, lookahead.Begin(), "("); db.AddMainHintArguments(")"); db.AddHint(lparenPos, "("); } bad = true; } if (bad) { ret->EnableAttr(Attribute::IS_BROKEN); } ret->end = lastToken.End(); return ret; } bool ParserImpl::IsConditionExpr(ExprKind ek) { return ek == ExprKind::IF_COND_EXPR || ek == ExprKind::WHILE_COND_EXPR || ek == ExprKind::EXPR_IN_IF_COND_TUPLE || ek == ExprKind::EXPR_IN_WHILE_COND_TUPLE; } // The preP stands for previous precedence. OwnedPtr ParserImpl::ParseExpr(const Token& preT, OwnedPtr expr, ExprKind ek) { OwnedPtr base; if (IsConditionExpr(ek) && Seeing(TokenKind::LET)) { base = ParseLetPattern(ek); } else { base = ParseBaseExpr(std::move(expr), ek); } if (!SeeingExprOperator()) { return base; } auto preP = Precedence(preT.kind); while (SeeingExprOperator()) { auto tok = GetExprOperator(); if (newlineSkipped) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_nl_warning, firstNLPosition); builder.AddHint(MakeRange( lastNoneNLToken.Begin(), lookahead.Begin() + 1), lastNoneNLToken.Value(), tok.Value()); } auto curP = Precedence(tok.kind); if (IsConditionExpr(ek) && Is(base.get()) && curP < Precedence(TokenKind::RANGEOP) && tok.kind != TokenKind::AND && tok.kind != TokenKind::OR) { return base; } if (ek == ExprKind::LET_PATTERN && curP < Precedence(TokenKind::RANGEOP)) { // according to if-let expression, only binary expression whose operator precedence is not lower than .. is // allowed in let pattern initializer. Operators with lower precedence would be considered an end of this // let initalizer return base; } if (preP > curP) { return base; } else if (preP == curP) { // Means it is default operator whose precedence is 0. // Only 'curT' is assignment token will enter this branch. CheckLeftExpression(preT, base, tok); // Right associative, if (tok.kind != TokenKind::COALESCING && tok.kind != TokenKind::EXP && !(preT.Begin().IsZero() && Precedence(tok.kind) == INVALID_PRECEDENCE)) { return base; } } auto res = CheckMacroExprRules(preT, tok, *base); // make base expr as another expr's sub expr base = MakeOperatorExpr(base, tok); if (!res && !base->TestAttr(Attribute::HAS_BROKEN)) { base->EnableAttr(Attribute::HAS_BROKEN); } ParseExprWithRightExprOrType(base, tok, ek); } return base; } void ParserImpl::CheckLeftExpression(const Token& preT, const OwnedPtr& base, const Token& tok) { if (preT.Begin().IsZero()) { if (!IsLeftValueExpression(base, tok) && !base->TestAttr(Attribute::HAS_BROKEN) && Precedence(tok.kind) == INVALID_PRECEDENCE) { DiagInvalidLeftHandExpr(*base, tok); base->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); } if (base->astKind == ASTKind::TUPLE_LIT && tok.kind != TokenKind::ASSIGN) { ParseDiagnoseRefactor(DiagKindRefactor::parse_cannot_operator_a_tuple, tok, tok.Value()); } } else if (IsNoneAssociative(tok) && !base->TestAttr(Attribute::HAS_BROKEN)) { DiagNoneAssociativeOp(preT, tok); base->EnableAttr(Attribute::HAS_BROKEN); } } void ParserImpl::ParseExprWithRightExprOrType(OwnedPtr& base, const Token& tok, ExprKind ek) { ChainScope cs(*this, base.get()); SkipExprOperator(); if (base->astKind == ASTKind::IS_EXPR || base->astKind == ASTKind::AS_EXPR) { OwnedPtr type; if (!SeeingAny(GetTypeFirst()) && !SeeingContextualKeyword()) { DiagExpectedTypeNameAfterAs(tok); type = MakeInvalid(lastToken.End()); base->EnableAttr(Attribute::HAS_BROKEN); } else { type = ParseType(); } base->end = type->end; base->astKind == ASTKind::IS_EXPR ? StaticAs(base.get())->isType = std::move(type) : StaticAs(base.get())->asType = std::move(type); } else { auto rExpr = ParseExpr(tok, nullptr, ek); auto res = CheckMacroExprRules(tok, Token{TokenKind::DOT}, *rExpr); if (!res || rExpr->TestAttr(Attribute::HAS_BROKEN) || rExpr->TestAttr(Attribute::IS_BROKEN)) { base->EnableAttr(Attribute::HAS_BROKEN); } RegisterRightExpr(base, std::move(rExpr)); if ((IsCompoundAssign(tok.kind) || tok.kind == TokenKind::ASSIGN) && base->hasQuestSuffix) { OwnedPtr optExpr = MakeOwned(); optExpr->begin = base->begin; optExpr->end = base->end; optExpr->expr = std::move(base); base = std::move(optExpr); } } } bool ParserImpl::IsLeftValueExpression(const OwnedPtr& expr, const Token& tok) { /* * leftValueExpression * : Identifier * | leftAuxExpression QUEST? assignableSuffix * | tupleLeftValueExpression * | WILDCARD * ; * assignableSuffix * : fieldAccess * | indexAccess * ; */ switch (expr->astKind) { case ASTKind::REF_EXPR: case ASTKind::REF_TYPE: case ASTKind::WILDCARD_PATTERN: case ASTKind::WILDCARD_EXPR: case ASTKind::OPTIONAL_CHAIN_EXPR: return true; case ASTKind::TUPLE_LIT: { auto& children = StaticAs(expr.get())->children; for (auto& child : children) { if (!IsLeftValueExpression(child, tok)) { // to avoid duplicate errors: // if child's astKind is TupleLit,then never come here, // because IsLeftValueExpression(child, tok) will always return true. DiagInvalidLeftHandExpr(*child, tok); } } return true; } case ASTKind::MEMBER_ACCESS: return IsLeftAuxExpression(StaticAs(expr.get())->baseExpr, tok); case ASTKind::SUBSCRIPT_EXPR: return IsLeftAuxExpression(StaticAs(expr.get())->baseExpr, tok); default: return false; } } bool ParserImpl::IsLeftAuxExpression(const OwnedPtr& expr, const Token& tok) { /* * leftAuxExpression * : Identifier (NL* typeArguments)? * | thisSuperExpression * | leftAuxExpression QUEST? NL* DOT NL* Identifier (NL* typeArguments)? * | leftAuxExpression QUEST? callSuffix * | leftAuxExpression QUEST? indexAccess * ; */ switch (expr->astKind) { case ASTKind::REF_EXPR: case ASTKind::REF_TYPE: case ASTKind::PRIMITIVE_TYPE_EXPR: return true; case ASTKind::OPTIONAL_EXPR: return IsLeftAuxExpression(StaticAs(expr.get())->baseExpr, tok); case ASTKind::MEMBER_ACCESS: return IsLeftAuxExpression(StaticAs(expr.get())->baseExpr, tok); case ASTKind::CALL_EXPR: return IsLeftAuxExpression(StaticAs(expr.get())->baseFunc, tok); case ASTKind::SUBSCRIPT_EXPR: return IsLeftAuxExpression(StaticAs(expr.get())->baseExpr, tok); default: return false; } } void ParserImpl::ParseIncOrDec(OwnedPtr& baseExpr) { while (SeeingAny({TokenKind::INCR, TokenKind::DECR}) && !newlineSkipped) { std::vector validSuffix{ASTKind::REF_EXPR, ASTKind::MEMBER_ACCESS, ASTKind::SUBSCRIPT_EXPR}; if (!Utils::In(baseExpr->astKind, validSuffix) && !baseExpr->TestAttr(Attribute::HAS_BROKEN) && !baseExpr->TestAttr(Attribute::IS_BROKEN)) { baseExpr->EnableAttr(Attribute::HAS_BROKEN); DiagInvalidIncreExpr(*baseExpr); } OwnedPtr incOrDec = MakeOwned(); SpreadAttrAndConsume(baseExpr.get(), incOrDec.get(), {}); incOrDec->begin = baseExpr->begin; incOrDec->expr = std::move(baseExpr); incOrDec->op = Peek().kind; Next(); if (incOrDec->expr) { incOrDec->begin = incOrDec->expr->begin; } incOrDec->operatorPos = lookahead.Begin(); incOrDec->end = lookahead.Begin(); incOrDec->end.column += INCREMENT_OP_LENGTH; baseExpr = std::move(incOrDec); } } static ExprKind RemoveParsingIfCondExpr(ExprKind ek) { switch (ek) { case ExprKind::EXPR_IN_IF_COND_TUPLE: case ExprKind::EXPR_IN_WHILE_COND_TUPLE: case ExprKind::IF_COND_EXPR: case ExprKind::WHILE_COND_EXPR: return ExprKind::ALL; default: return ek; } } OwnedPtr ParserImpl::ParseUnaryExpr(ExprKind ek) { // For prefixUnaryExpression check: No newline between prefix token and expression. if (Seeing({TokenKind::SUB, TokenKind::NL}, false) || Seeing({TokenKind::NOT, TokenKind::NL}, false)) { DiagExpectedNoNewLine(); return MakeOwned(lookahead.Begin()); } OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); Token opToken = lookahead; ret->op = opToken.kind; ret->begin = opToken.Begin(); ret->operatorPos = opToken.Begin(); Next(); // atomicCondition cannot be nested within unary expr (e.g. if (!(let ... <- ...))) auto tmpExpr = ParseBaseExpr(nullptr, RemoveParsingIfCondExpr(ek)); // "-" + LitConstExpr is also LitConstExpr if (opToken.kind == TokenKind::SUB) { if (auto le = AST::As(tmpExpr.get()); le && (le->kind == LitConstKind::INTEGER || le->kind == LitConstKind::FLOAT) && (!le->stringValue.empty() && le->stringValue[0] != '-')) { le->stringValue = '-' + le->stringValue; le->rawString = le->stringValue; le->begin = opToken.Begin(); return tmpExpr; } } ret->expr = std::move(tmpExpr); ret->end = ret->expr->end; return ret; } OwnedPtr ParserImpl::ParseBaseExpr(OwnedPtr expr, ExprKind ek) { if (SeeingAny({TokenKind::SUB, TokenKind::NOT}) && !expr) { return ParseUnaryExpr(ek); } OwnedPtr baseExpr; if (expr) { baseExpr = std::move(expr); } else { baseExpr = ParseAtom(ek); } // May change astKind of baseExpr in this function. ParseBaseExprPostfix(baseExpr, ek); // May change astKind of baseExpr in this function. ParseIncOrDec(baseExpr); if (IsNeedToCreateOptionalChain(GetExprOperator().kind, *baseExpr)) { OwnedPtr optExpr = MakeOwned(); optExpr->begin = baseExpr->begin; optExpr->end = baseExpr->end; optExpr->expr = std::move(baseExpr); return optExpr; } return baseExpr; } void ParserImpl::ParseQuestSuffixExpr(OwnedPtr& expr) { if (Skip(TokenKind::QUEST)) { auto pos = lastToken.Begin(); OwnedPtr ret = MakeOwned(); ret->begin = expr->begin; ret->end = expr->end; ret->questPos = pos; ret->baseExpr = std::move(expr); expr = std::move(ret); } } OwnedPtr ParserImpl::ParseOptionalExpr( const Position questPos, OwnedPtr baseExpr, SuffixKind suffix) const { if (suffix == SuffixKind::QUEST) { OwnedPtr ret = MakeOwned(); ret->questPos = questPos; ret->begin = baseExpr->begin; ret->end = baseExpr->end; ret->baseExpr = std::move(baseExpr); return ret; } return baseExpr; } OwnedPtr ParserImpl::ParseTrailingClosureExpr(OwnedPtr baseExpr) { OwnedPtr ret = MakeOwned(); std::vector> annos; ParseAnnotations(annos); ret->leftLambda = lastToken.Begin(); Next(); ret->lambda = ParseLambdaExprWithTrailingClosure(); ret->rightLambda = lastToken.Begin(); // Overflow lambdaExpr. for (auto& it : annos) { if (it->kind == AnnotationKind::NUMERIC_OVERFLOW) { ret->lambda->EnableAttr(Attribute::NUMERIC_OVERFLOW); ret->lambda->overflowStrategy = it->overflowStrategy; break; } } CheckBaseOfTrailingClosureExpr(baseExpr); ret->expr = std::move(baseExpr); ret->begin = ret->expr->begin; ret->end = ret->lambda->end; return ret; } void ParserImpl::CheckBaseOfTrailingClosureExpr(const OwnedPtr& baseExpr) { if (baseExpr->TestAttr(Attribute::IS_BROKEN)) { return; } // baseExpr can only be: // 1. RefExpr // 2. MemberAccess // 3. CallExpr // 4. VArrayExpr // 5. OptionalExpr // 6. MacroExpandExpr // if baseExpr is CallExpr, then the CallExpr's baseFunc must be RefExpr or MemberAccess // RefExpr and MemberAccess have common parent class: NameReferenceExpr if (Utils::NotIn(baseExpr->astKind, {ASTKind::MACRO_EXPAND_EXPR, ASTKind::ARRAY_EXPR, ASTKind::OPTIONAL_EXPR, ASTKind::CALL_EXPR, ASTKind::REF_EXPR, ASTKind::MEMBER_ACCESS})) { ParseDiagnoseRefactor(DiagKindRefactor::parse_trailing_closure_only_follow_name, *baseExpr); } if (baseExpr->astKind == ASTKind::CALL_EXPR) { auto callExpr = StaticAs(baseExpr.get()); if (callExpr->baseFunc->TestAttr(Attribute::IS_BROKEN)) { return; } if (Utils::NotIn( callExpr->baseFunc->astKind, {ASTKind::MEMBER_ACCESS, ASTKind::REF_EXPR, ASTKind::OPTIONAL_EXPR})) { ParseDiagnoseRefactor(DiagKindRefactor::parse_trailing_closure_only_follow_name, *baseExpr); } } } OwnedPtr ParserImpl::ParseSubscriptExpr(OwnedPtr baseExpr) { auto ret = MakeOwned(); ChainScope cs(*this, ret.get()); ret->begin = baseExpr->begin; ret->leftParenPos = lookahead.Begin(); ret->baseExpr = std::move(baseExpr); do { if (lastToken.kind == TokenKind::COMMA) { ret->commaPos.push_back(lastToken.Begin()); } ret->indexExprs.emplace_back(ParseExpr(ExprKind::INDEX_EXPR)); } while (Skip(TokenKind::COMMA)); if (!Skip(TokenKind::RSQUARE)) { DiagExpectedRightDelimiter("[", ret->leftParenPos); ret->EnableAttr(Attribute::HAS_BROKEN); } ret->rightParenPos = lookahead.Begin(); ret->end = lastToken.End(); return ret; } static void GetMemberAccessEndPos(const OwnedPtr& ma) { // In LSP case, when the identifier is waiting tobe input, and is at end of the file, we need plus one // for the column of end position, then the sort order of REF_EXPR and MEMBER_ACCESS will be determined. if (ma->field == INVALID_IDENTIFIER) { // The filedPos is the dot position, we need shift 2. ma->end = ma->field.Begin() + 2; } else { if (ma->rightAnglePos.IsZero()) { ma->end = ma->field.GetRawEndPos(); } else { ma->end = ma->rightAnglePos; } } } OwnedPtr ParserImpl::ParseMemberAccess(OwnedPtr baseExpr, ExprKind ek) { OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); ret->begin = baseExpr->begin; ret->baseExpr = std::move(baseExpr); ret->dotPos = lastToken.Begin(); ret->field = ExpectIdentifierWithPos(*ret); if (Seeing(TokenKind::LT)) { auto leftAnglePos = Peek().Begin(); ParserScope scope(*this); Next(); // collecting diagnoses in `ParseTypeArguments` and storing these diagnoses to a cache diag.Prepare(); auto [isGenericArgList, typeArguments] = ParseTypeArguments(ek); if (isGenericArgList) { // parse type success, handle those diagnoses which were stored in the cache ret->typeArguments = std::move(typeArguments); diag.Commit(); } else { diag.ClearTransaction(); // if it is like: if a.m= d), reset parser. scope.ResetParserScope(); ret->leftAnglePos = INVALID_POSITION; ret->rightAnglePos = INVALID_POSITION; GetMemberAccessEndPos(ret); return ret; } ret->leftAnglePos = leftAnglePos; ret->rightAnglePos = lastToken.Begin(); } GetMemberAccessEndPos(ret); return ret; } OwnedPtr ParserImpl::ParseCallExpr(OwnedPtr baseExpr) { OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); ret->begin = baseExpr->begin; ret->leftParenPos = lastToken.Begin(); baseExpr->isBaseFunc = true; ret->baseFunc = std::move(baseExpr); ParseZeroOrMoreSepTrailing( [&ret](const Position& pos) { ret->args.back()->commaPos = pos; }, [this, &ret]() { ret->args.emplace_back(ParseFuncArg()); }, TokenKind::RPAREN); if (!Skip(TokenKind::RPAREN) && !ret->TestAttr(Attribute::HAS_BROKEN)) { ret->EnableAttr(Attribute::HAS_BROKEN); DiagExpectedRightDelimiter("(", ret->leftParenPos); } ret->rightParenPos = lastToken.Begin(); ret->end = lastToken.End(); return ret; } AST::SuffixKind ParserImpl::ParseSuffix(OwnedPtr& baseExpr) { if (newlineSkipped) { return SuffixKind::NONE; } // For BNF: // itemAfterQuest // : DOT identifier (NL* typeArguments)? // | callSuffix // | indexAccess // | trailingLambdaExpression if (Seeing({TokenKind::QUEST, TokenKind::DOT}) || Seeing({TokenKind::QUEST, TokenKind::LSQUARE}, false) || Seeing({TokenKind::QUEST, TokenKind::LPAREN}, false) || Seeing({TokenKind::QUEST, TokenKind::LCURL}, false)) { if (baseExpr->astKind == ASTKind::WILDCARD_EXPR) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_unexpected_expected_found, *baseExpr.get(), "_ wildcard"); builder.AddMainHintArguments("expression", "wildcard"); } ParseQuestSuffixExpr(baseExpr); return SuffixKind::QUEST; } else if (Seeing(TokenKind::QUEST) && !SeeingCombinator({TokenKind::QUEST, TokenKind::QUEST})) { // a single QUEST as operator is grammatically incorrect. e.g. a?b ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_character_after, lookahead.Begin(), "'.', '(', '[', '{' or '?'", lookahead.Value()); } return SuffixKind::NONE; } void ParserImpl::ParseBaseExprPostfix(OwnedPtr& baseExpr, ExprKind ek) { // The check of jump_expr could put in while below. if ((newlineSkipped && !Seeing(TokenKind::DOT)) || baseExpr->astKind == ASTKind::JUMP_EXPR) { return; } auto suffix = ParseSuffix(baseExpr); bool hasQuestSuffix = (suffix == SuffixKind::QUEST); while (SeeingAny({TokenKind::LSQUARE, TokenKind::DOT, TokenKind::LPAREN}) || SeeingTrailingClosure(baseExpr)) { // Subscript expr (a[1], b[x]). if (!newlineSkipped && Skip(TokenKind::LSQUARE)) { auto ret = ParseSubscriptExpr(std::move(baseExpr)); baseExpr = std::move(ret); baseExpr->end = lastToken.End(); suffix = ParseSuffix(baseExpr); hasQuestSuffix = suffix == SuffixKind::QUEST ? true : hasQuestSuffix; } if (!Seeing(TokenKind::DOT) && newlineSkipped) { break; } // Member access (obj.x, obj.foo()). if (Skip(TokenKind::DOT)) { auto ret = ParseMemberAccess(std::move(baseExpr), ek); baseExpr = std::move(ret); suffix = ParseSuffix(baseExpr); hasQuestSuffix = suffix == SuffixKind::QUEST ? true : hasQuestSuffix; } // Function call. if (!newlineSkipped && Skip(TokenKind::LPAREN)) { auto ret = ParseCallExpr(std::move(baseExpr)); baseExpr = std::move(ret); suffix = ParseSuffix(baseExpr); hasQuestSuffix = suffix == SuffixKind::QUEST ? true : hasQuestSuffix; } // TrailingClosure. if (SeeingTrailingClosure(baseExpr) && !newlineSkipped) { auto ret = ParseTrailingClosureExpr(std::move(baseExpr)); baseExpr = std::move(ret); suffix = ParseSuffix(baseExpr); hasQuestSuffix = suffix == SuffixKind::QUEST ? true : hasQuestSuffix; } } if (hasQuestSuffix) { baseExpr->hasQuestSuffix = true; } } cangjie_compiler-1.0.7/src/Parse/ParseFeatures.cpp000066400000000000000000000054751510705540100221400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements feature parsing */ #include "ParserImpl.h" using namespace Cangjie; using namespace Cangjie::AST; /** * featureId * : Identifier (DOT Identifier)* * ; * * featuresDirective * : FEATURES NL* featureId * (COMMA NL* featureId)* * end+; */ void ParserImpl::ParseFeatureDirective(OwnedPtr& features) { Skip(TokenKind::FEATURES); features = MakeOwned(); features->begin = lastToken.Begin(); bool hasError {false}; while (!Seeing(TokenKind::END)) { if (!ParseFeatureId(features)) { DiagExpectedIdentifierFeatureDirective(features); hasError = true; break; } if (newlineSkipped || Seeing(TokenKind::SEMI) || Seeing(TokenKind::END)) { break; } if (!Skip(TokenKind::COMMA)) { DiagExpectedIdentifierFeatureDirective(features); hasError = true; break; } features->commaPoses.emplace_back(lastToken.Begin()); } if (hasError) { features->EnableAttr(Attribute::IS_BROKEN); if (!newlineSkipped) { ConsumeUntilAny({TokenKind::NL, TokenKind::SEMI, TokenKind::END}); } } features->end = features->content.empty() ? lastToken.End() : features->content.back().end; } bool ParserImpl::ParseFeatureId(OwnedPtr& features) { FeatureId content; bool firstIter{true}; bool noError{true}; while (Skip(TokenKind::IDENTIFIER) || Skip(TokenKind::DOT)) { if (firstIter) { content.begin = lastToken.Begin(); content.end = lastToken.End(); if (lastToken.kind != TokenKind::IDENTIFIER) { content.EnableAttr(Attribute::IS_BROKEN); noError = false; break; } firstIter = false; } std::string current = lastToken.Value(); if (lastToken.kind == TokenKind::IDENTIFIER) { content.identifiers.emplace_back(Identifier(current, lastToken.Begin(), lastToken.End())); } else { content.dotPoses.emplace_back(lastToken.Begin()); } content.end = lastToken.End(); if (Seeing(lastToken.kind) || IsRawIdentifier(current)) { content.EnableAttr(Attribute::IS_BROKEN); noError = false; break; } } if (lastToken.kind != TokenKind::IDENTIFIER) { noError = false; } if (!content.identifiers.empty()) { features->content.emplace_back(content); } return noError; } cangjie_compiler-1.0.7/src/Parse/ParseImports.cpp000066400000000000000000000265541510705540100220200ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements imports parsing. */ #include "ParserImpl.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Match.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace Cangjie::AST; using namespace Cangjie::Utils; void ParserImpl::ParseCommonImportSpec(PtrVector& imports, PtrVector& annos) { while (SeeingImport2() || (SeeingBuiltinAnnotation())) { if (SeeingBuiltinAnnotation()) { ParseAnnotations(annos); } if (!SeeingImport2()) { break; } ParseImportSpec(imports, annos); annos.clear(); /** * importList * : (FROM NL* Identifier)? NL* IMPORT NL* importAllOrSpecified * (NL* COMMA NL* importAllOrSpecified)* end+ * ; * */ if (!newlineSkipped) { if (Seeing(TokenKind::END)) { break; } if (!Skip(TokenKind::SEMI)) { DiagExpectSemiOrNewline(); ConsumeUntilAny({TokenKind::NL, TokenKind::SEMI}); } } SkipBlank(TokenKind::SEMI); } } void ParserImpl::CheckImportSpec(PtrVector& imports) { for (auto& import : imports) { if (import->TestAttr(Attribute::COMPILER_ADD)) { continue; } const auto& content = import->content; if (content.kind != ImportKind::IMPORT_MULTI) { auto fullPackageName = content.GetImportedPackageName(); if (!content.TestAttr(Attribute::IS_BROKEN) && fullPackageName.size() > PACKAGE_NAME_LEN_LIMIT) { auto packageNameBeginPos = content.prefixPaths.empty() ? content.identifier.Begin() : content.prefixPoses[0]; DiagNameLengthOverflow(MakeRange(packageNameBeginPos, content.end), fullPackageName, PACKAGE_NAME_LEN_LIMIT, fullPackageName.size()); } continue; } auto commonPrefix = content.GetImportedPackageName(); for (const auto& item : content.items) { auto suffix = item.GetImportedPackageName(); auto fullPackageName = commonPrefix.empty() ? suffix : commonPrefix + "." + suffix; if (!item.TestAttr(Attribute::IS_BROKEN) && fullPackageName.size() > PACKAGE_NAME_LEN_LIMIT) { auto packageNameBeginPos = content.prefixPaths.empty() ? item.prefixPaths.empty() ? item.identifier.Begin() : item.prefixPoses[0] : content.prefixPoses[0]; DiagNameLengthOverflow(MakeRange(packageNameBeginPos, content.end), fullPackageName, PACKAGE_NAME_LEN_LIMIT, fullPackageName.size()); } } } } void ParserImpl::ParseImportSpecInTop(PtrVector& imports, PtrVector& annos) { ParseCommonImportSpec(imports, annos); CheckImportSpec(imports); } void ParserImpl::ParseImportSpec(PtrVector& imports, const PtrVector& annos) { auto importSpec = MakeOwned(); if (SeeingAny({TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::PRIVATE})) { Next(); auto modifier = MakeOwned(lastToken.kind, lastToken.Begin()); modifier->end = lastToken.End(); importSpec->modifier = std::move(modifier); } Skip(TokenKind::IMPORT); importSpec->EnableAttr(GetModifierAttr(importSpec->modifier, ASTKind::IMPORT_SPEC)); importSpec->begin = importSpec->modifier ? importSpec->modifier->begin : lastToken.Begin(); importSpec->importPos = lastToken.Begin(); ParseImportContent(importSpec->content); importSpec->end = importSpec->content.end; // Check allowed annotation on import. std::for_each(annos.begin(), annos.end(), [&, this](const Ptr& anno) { if (NotIn(anno->kind, {AnnotationKind::WHEN})) { DiagUnexpectedAnnoOn(*anno, importSpec->importPos, anno->identifier, "import"); } }); importSpec->annotations = ASTCloner::CloneVector(annos); if (auto& import = imports.emplace_back(std::move(importSpec)); import->IsImportMulti()) { DesugarImportMulti(imports, *import); } } void ParserImpl::DesugarImportMulti(PtrVector& imports, ImportSpec& import) const { CJC_ASSERT(import.IsImportMulti()); // For lsp auto completion, desugar as much as possible const auto& items = import.content.items; if (std::find_if(items.begin(), items.end(), [](auto& it) { return it.TestAnyAttr(Attribute::IS_BROKEN, Attribute::HAS_BROKEN); }) != items.end()) { import.EnableAttr(Attribute::HAS_BROKEN); } for (auto& item : items) { auto desugaredImport = MakeOwned(); CopyBasicInfo(&item, desugaredImport.get()); // Copy modifier if (import.modifier) { desugaredImport->modifier = MakeOwned(import.modifier->modifier, import.modifier->begin); CopyBasicInfo(import.modifier.get(), desugaredImport->modifier.get()); desugaredImport->modifier->isExplicit = import.modifier->isExplicit; } desugaredImport->importPos = import.importPos; desugaredImport->annotations = ASTCloner::CloneVector(import.annotations); CopyBasicInfo(&import.content, &desugaredImport->content); desugaredImport->content.end = item.end; desugaredImport->content.kind = item.kind; desugaredImport->content.prefixPaths = import.content.prefixPaths; desugaredImport->content.prefixPoses = import.content.prefixPoses; desugaredImport->content.prefixDotPoses = import.content.prefixDotPoses; desugaredImport->content.hasDoubleColon = import.content.hasDoubleColon; desugaredImport->content.prefixPaths.insert( desugaredImport->content.prefixPaths.end(), item.prefixPaths.begin(), item.prefixPaths.end()); desugaredImport->content.prefixPoses.insert( desugaredImport->content.prefixPoses.end(), item.prefixPoses.begin(), item.prefixPoses.end()); desugaredImport->content.prefixDotPoses.insert( desugaredImport->content.prefixDotPoses.end(), item.prefixDotPoses.begin(), item.prefixDotPoses.end()); desugaredImport->content.identifier = item.identifier; desugaredImport->content.asPos = item.asPos; desugaredImport->content.aliasName = item.aliasName; desugaredImport->EnableAttr(Attribute::COMPILER_ADD, GetModifierAttr(import.modifier, ASTKind::IMPORT_SPEC)); imports.emplace_back(std::move(desugaredImport)); } } void ParserImpl::ParseImportContent(ImportContent& content) { if (!ParseImportSingle(content)) { ParseImportMulti(content); } // Parse import-alias if (content.kind == ImportKind::IMPORT_SINGLE && Skip(TokenKind::AS)) { ParseImportAliasPart(content); } } bool ParserImpl::ParseImportSingle(ImportContent& content, bool inMultiImport) { SrcIdentifier curIdent; bool firstIter{true}; bool skipDc{false}; do { skipDc = false; if (!curIdent.Empty()) { content.prefixPaths.emplace_back(curIdent.Val()); content.prefixPoses.emplace_back(curIdent.Begin()); CJC_ASSERT(!curIdent.Begin().IsZero()); content.prefixDotPoses.emplace_back(lastToken.Begin()); } if (!inMultiImport && Skip(TokenKind::LCURL)) { if (firstIter) { content.begin = lastToken.Begin(); } content.leftCurlPos = lastToken.Begin(); return false; } // Parse import-all if (!firstIter && Skip(TokenKind::MUL)) { curIdent = "*"; curIdent.SetPos(lastToken.Begin(), lastToken.End()); break; } // Parse import-single curIdent = ExpectPackageIdentWithPos(content); if (curIdent == INVALID_IDENTIFIER) { if (inMultiImport) { ConsumeUntilAny({TokenKind::NL, TokenKind::COMMA, TokenKind::RCURL}); if (lastToken.kind == TokenKind::RCURL) { content.rightCurlPos = lastToken.Begin(); } } else { ConsumeUntilAny({TokenKind::NL, TokenKind::COMMA}); } content.EnableAttr(Attribute::IS_BROKEN); break; } if (firstIter) { content.begin = curIdent.Begin(); firstIter = false; } if (content.prefixPaths.empty() && Skip(TokenKind::DOUBLE_COLON)) { content.hasDoubleColon = true; skipDc = true; } } while (skipDc || Skip(TokenKind::DOT)); content.kind = curIdent == "*" ? ImportKind::IMPORT_ALL : ImportKind::IMPORT_SINGLE; if (lastToken.kind == TokenKind::PACKAGE_IDENTIFIER) { DiagExpectedIdentifier(MakeRange(curIdent.Begin(), curIdent.End())); content.EnableAttr(Attribute::IS_BROKEN); } content.identifier = std::move(curIdent); content.end = curIdent.End(); return true; } void ParserImpl::ParseImportMulti(ImportContent& content) { content.kind = ImportKind::IMPORT_MULTI; bool firstIter{true}; do { if (!firstIter) { bool skippedComma = Skip(TokenKind::COMMA); if (skippedComma) { content.commaPoses.emplace_back(lastToken.Begin()); } // Import-multi should contain at least one item. if (Skip(TokenKind::RCURL)) { content.rightCurlPos = lastToken.Begin(); content.end = lastToken.End(); return; } if (!skippedComma) { DiagExpectCharacter(lastToken.End(), "','"); content.EnableAttr(Attribute::IS_BROKEN); } } firstIter = false; ImportContent item; ParseImportSingle(item, true); if (item.kind == ImportKind::IMPORT_SINGLE && Skip(TokenKind::AS)) { ParseImportAliasPart(item); } if (item.TestAttr(Attribute::IS_BROKEN)) { content.EnableAttr(Attribute::HAS_BROKEN); } content.items.emplace_back(std::move(item)); } while (SeeingAny({TokenKind::COMMA, TokenKind::IDENTIFIER, TokenKind::PACKAGE_IDENTIFIER, TokenKind::AS})); if (lastToken.kind != TokenKind::RCURL && !Skip(TokenKind::RCURL)) { DiagExpectedRightDelimiter("{", content.leftCurlPos); content.EnableAttr(Attribute::IS_BROKEN); content.end = lastToken.End(); } else { content.rightCurlPos = lastToken.Begin(); auto lastItem = content.items.rbegin(); CJC_ASSERT(lastItem != content.items.rend()); content.end = lastItem->end; } } void ParserImpl::ParseImportAliasPart(ImportContent& content) { content.kind = ImportKind::IMPORT_ALIAS; content.asPos = lastToken.Begin(); auto curIdent = ExpectIdentifierWithPos(content); if (curIdent == INVALID_IDENTIFIER) { ConsumeUntilAny({TokenKind::NL, TokenKind::COMMA}); content.EnableAttr(Attribute::IS_BROKEN); } else { content.aliasName = std::move(curIdent); content.end = lastToken.End(); } } cangjie_compiler-1.0.7/src/Parse/ParseMacro.cpp000066400000000000000000000611651510705540100214210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * This file implements macro parse apis. */ #include "ParserImpl.h" #include #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/StringConvertor.h" using namespace Cangjie; using namespace Cangjie::AST; namespace { // Macro decl's param limit num. const static int8_t G_LIMITED_PARAM_NUM = 2; // Valid decl list after macro expand decl. const static std::vector G_VALID_DECL_LIST = {ASTKind::FUNC_DECL, ASTKind::STRUCT_DECL, ASTKind::CLASS_DECL, ASTKind::MAIN_DECL, ASTKind::VAR_DECL, ASTKind::ENUM_DECL, ASTKind::INTERFACE_DECL, ASTKind::EXTEND_DECL, ASTKind::PROP_DECL, ASTKind::PRIMARY_CTOR_DECL, ASTKind::VAR_WITH_PATTERN_DECL, ASTKind::MACRO_EXPAND_DECL, ASTKind::FUNC_PARAM, ASTKind::MACRO_EXPAND_PARAM}; // Info when parse macro invocation, included matched paren, diag info. const static std::unordered_map G_PARSE_MACRO_INFO = { {TokenKind::LPAREN, TokenKind::RPAREN}, {TokenKind::LSQUARE, TokenKind::RSQUARE}, }; // Exprs that connected By Comma. const static std::vector G_EXPRS_CONNECTED_BY_COMMA_LIST = { ASTKind::ARRAY_LIT, ASTKind::TUPLE_LIT, ASTKind::FUNC_ARG}; } // namespace bool ParserImpl::SeeingParamInMacroCallExpr() { if (SeeingContextualKeyword() || Seeing(TokenKind::IDENTIFIER) || Seeing(TokenKind::WILDCARD)) { auto tokens = lexer->LookAheadSkipNL(1); if (tokens.begin()->kind == TokenKind::COLON || tokens.begin()->kind == TokenKind::NOT) { return true; } } return false; } std::vector> ParserImpl::ParseNodes(std::variant scope, Node& currentMacroCall, const std::set& modifiers, PtrVector annos) { this->currentFile = currentMacroCall.curFile; this->curMacroCall = ¤tMacroCall; auto isConnectedByComma = false; auto isParamMacro = false; auto pInvocation = currentMacroCall.GetConstInvocation(); Ptr namedParameter{nullptr}; Ptr memberParam{nullptr}; CJC_NULLPTR_CHECK(pInvocation); if (pInvocation->parent && Utils::In(pInvocation->parent->astKind, G_EXPRS_CONNECTED_BY_COMMA_LIST)) { isConnectedByComma = true; } if (pInvocation && pInvocation->parent && pInvocation->parent->astKind == ASTKind::FUNC_PARAM_LIST) { isParamMacro = true; } auto scopeKind = std::get_if(&scope); std::vector> nodes; while (!Seeing(TokenKind::END) && !DetectPrematureEnd()) { auto hasNode = !nodes.empty(); if (hasNode && isConnectedByComma && !Skip(TokenKind::COMMA)) { DiagExpectCharacter("','"); break; } if (isParamMacro) { isConnectedByComma = Skip(TokenKind::COMMA); } if (scopeKind || hasNode) { // The scopekind indicates that decls or nodes are parsed, // The first token can be a semicolon, which can be ignored. // But the first Expr starting with a semicolon (;) should not be entered here. auto hasNLorSEMI = SkipNLOrSemi(); if (hasNode && !isConnectedByComma && !hasNLorSEMI) { DiagExpectSemiOrNewline(); break; } } if (Seeing(TokenKind::END)) { break; } OwnedPtr node; if (scopeKind) { if (isParamMacro) { node = ParseParamInParamList(*scopeKind, namedParameter, memberParam); } else if (CheckIfSeeingDecl(*scopeKind)) { node = nodes.empty() ? ParseDecl(*scopeKind, modifiers, std::move(annos)) : ParseDecl(*scopeKind); } else { node = ParseExpr(); } if (auto ctor = As(node.get()); ctor && ctor->TestAttr(Attribute::CONSTRUCTOR)) { CheckConstructorBody(*ctor, *scopeKind, true); } } else if (auto scp = std::get_if(&scope)) { if (auto fa = DynamicCast(pInvocation->parent); fa && Seeing(TokenKind::INOUT)) { fa->withInout = true; Next(); } node = ParseExpr(*scp); } node->EnableAttr(Attribute::MACRO_EXPANDED_NODE); node->curFile = this->currentFile; node->curMacroCall = this->curMacroCall; AddMacroAttr(*node); nodes.emplace_back(std::move(node)); } AttachCommentToNodes(nodes); return nodes; } bool ParserImpl::CheckMacroExprRules(const Token& pre, const Token& tok, AST::Expr& expr) { if (expr.astKind != ASTKind::MACRO_EXPAND_EXPR) { return true; } Ptr node = &expr; while (node->IsMacroCallNode()) { auto pInvocation = node->GetInvocation(); if (!pInvocation->decl) { return true; } node = pInvocation->decl.get(); } auto me = StaticAs(&expr); if (!pre.Begin().IsZero() && !expr.TestAttr(Attribute::HAS_BROKEN)) { DiagInvalidMacroExpandExpr(pre, *me); return false; } if (!expr.TestAttr(Attribute::HAS_BROKEN)) { DiagUnrecognizedNodeAfterMacro(tok, *me); } return false; } static bool IsBuiltinMacro(const std::string& identifier) { const std::vector buildInMacros = {"sourcePackage", "sourceFile", "sourceLine"}; return std::find(buildInMacros.begin(), buildInMacros.end(), identifier) != buildInMacros.end(); } OwnedPtr ParserImpl::ParseMacroDecl( ScopeKind scopeKind, const std::set& modifiers, std::vector> annos) { OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); if (currentFile != nullptr) { ret->curFile = currentFile; ret->curFile->hasMacro = true; } ret->begin = lookahead.Begin(); ret->keywordPos = lookahead.Begin(); Next(); CheckDeclarationInScope(scopeKind, DefKind::MACRO); auto attrs = CheckDeclModifiers(modifiers, scopeKind, DefKind::MACRO); for (auto& it : attrs) { ret->EnableAttr(it); } ret->EnableAttr(Attribute::MACRO_FUNC); ret->identifier = ExpectIdentifierWithPos(*ret); if (IsBuiltinAnnotation(moduleName, ret->identifier) || IsBuiltinMacro(ret->identifier)) { auto builtinKind = IsBuiltinMacro(ret->identifier) ? "macro" : "annotation"; ParseDiagnoseRefactor(DiagKindRefactor::parse_macro_define_conflicted_with_builtin, MakeRange(ret->identifier), ret->identifier, builtinKind); chainedAST.back()->EnableAttr(Attribute::HAS_BROKEN); } ret->modifiers.insert(modifiers.begin(), modifiers.end()); ret->funcBody = ParseMacroBody(*ret); ret->end = ret->funcBody->end; ret->annotations = std::move(annos); if (!ret->TestAttr(Attribute::PUBLIC) && !chainedAST.back()->TestAttr(Attribute::IS_BROKEN)) { DiagExpectPublicBeforeMacroCall(ret); chainedAST.back()->EnableAttr(Attribute::HAS_BROKEN); } // default package does not contain package node or currentFile package does not have a macro if (currentFile != nullptr && (currentFile->package == nullptr || !currentFile->package->hasMacro)) { if (ret->identifier.Begin().IsZero()) { ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_macro_decl_define_in_macro_package, MakeRange(ret->begin, lastToken.End())); } else { ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_macro_decl_define_in_macro_package, MakeRange(ret->identifier)); } } return ret; } void ParserImpl::CheckMacroParamType(AST::Type& type, bool isReturnTy) { if (auto ref = AST::As(&type); ref && ref->ref.identifier == "Tokens") { return; } // Macro Parameter Type Must be Tokens. if (isReturnTy) { DiagExpectMacroParamType(type); } else { ParseDiagnoseRefactor(DiagKindRefactor::parse_macro_illegal_param_type, type); } chainedAST.back()->EnableAttr(Attribute::HAS_BROKEN); } OwnedPtr ParserImpl::ParseMacroBody(AST::MacroDecl& macro) { OwnedPtr ret = MakeOwned(); ret->begin = lookahead.Begin(); ret->paramLists.emplace_back(ParseMacroParameterList()); if (Skip(TokenKind::COLON)) { ret->colonPos = lastToken.Begin(); ret->retType = ParseType(); if (ret->retType) { CheckMacroParamType(*ret->retType, true); ret->end = ret->retType->end; } } // If macro do not have return type, we should add Tokens as its return type. if (!ret->retType) { auto tokensType = MakeOwned(); auto pos = lastToken.Begin() == INVALID_POSITION ? lookahead.Begin() : lastToken.Begin(); tokensType->begin = pos; tokensType->end = pos; tokensType->ref.identifier = "Tokens"; tokensType->ref.identifier.SetPos(pos, pos); ret->retType = std::move(tokensType); ret->retType->EnableAttr(Attribute::IN_MACRO); ret->retType->EnableAttr(Attribute::COMPILER_ADD); } if (Seeing(TokenKind::LCURL)) { ret->body = ParseBlock(ScopeKind::MACRO_BODY); } else { ret->body = MakeInvalid(lastToken.End()); // Macro decl must have a body, unless in declaration mode. DiagMissingBody("macro", !macro.identifier.Valid() ? "" : " '" + macro.identifier + "'", lookahead.End()); } ret->end = ret->body->end; ret->EnableAttr(Attribute::MACRO_FUNC); return ret; } OwnedPtr ParserImpl::ParseMacroParameterList() { auto paramList = ParseParameterList(); if ((paramList->params.empty() || paramList->params.size() > G_LIMITED_PARAM_NUM) && !paramList->TestAttr(Attribute::IS_BROKEN)) { auto pos = paramList->params.empty() ? lookahead.Begin() : paramList->params[0].get()->begin; auto diagKind = paramList->params.empty() ? DiagKindRefactor::parse_macro_unexpected_empty_parameter : DiagKindRefactor::parse_macro_expected_right_parameter_nums; ParseDiagnoseRefactor(diagKind, pos); } else { for (auto& param : paramList->params) { if (param->type == nullptr) { ParseDiagnoseRefactor(DiagKindRefactor::parse_macro_illegal_param_type, lookahead); chainedAST.back()->EnableAttr(Attribute::HAS_BROKEN); } else { CheckMacroParamType(*param->type); } // NamedParams are not allowed in macro: (a !: Int32 = 1) if (param->isNamedParam) { DiagMacroUnexpectNamedParam(param); chainedAST.back()->EnableAttr(Attribute::HAS_BROKEN); } } } return paramList; } bool ParserImpl::ParseMacroCallEscapeTokens(const TokenKind& left, std::vector& tokens, bool isAttr) { Skip(TokenKind::ILLEGAL); if ((Seeing(TokenKind::LSQUARE, TokenKind::RSQUARE) && (left == TokenKind::LSQUARE)) || (Seeing(TokenKind::LPAREN, TokenKind::RPAREN) && (left == TokenKind::LPAREN))) { auto tok = Peek(); (void)tokens.emplace_back(tok.kind, tok.Value(), tok.Begin(), tok.End()); } else if ((Seeing(TokenKind::AT) && (left == TokenKind::LPAREN)) || (Seeing(TokenKind::AT) && (left == TokenKind::LSQUARE))) { // \@ will both be saved into tokens for further analysis(nested macro). auto skipIll = lastToken; auto tok = Peek(); (void)tokens.emplace_back(skipIll.kind, skipIll.Value(), skipIll.Begin(), skipIll.End()); (void)tokens.emplace_back(tok.kind, tok.Value(), tok.Begin(), tok.End()); } else { auto tokenPos = lookahead; // Consume tokens to prevent being further parsed into array member access. while (SeeingAny({TokenKind::LSQUARE, TokenKind::LPAREN, TokenKind::DOT})) { Next(); } auto diagKind = isAttr ? DiagKindRefactor::parse_illegal_macro_expand_attr_args : DiagKindRefactor::parse_illegal_macro_expand_input_args; ParseDiagnoseRefactor(diagKind, tokenPos); return false; } return true; } bool ParserImpl::ParseMacroCallTokens( const TokenKind& left, const TokenKind& right, std::vector& tokens, bool isAttr) { skipNL = false; int lParenOrSquare = 1; while (!Seeing(TokenKind::END)) { if (Seeing(right)) { if (--lParenOrSquare == 0) { break; } } auto token = Peek(); if (token.kind == left) { lParenOrSquare++; } // Support (1) \[ \] in attr @Foo[\[](123) (2) \( \) in paren @Foo(\() (3) \@ in paren @Foo(\@Bar(123)) if (Seeing(TokenKind::ILLEGAL) && lookahead == "\\") { if (!ParseMacroCallEscapeTokens(left, tokens, isAttr)) { return false; } } else { (void)tokens.emplace_back(token); } Next(); } skipNL = true; return true; } bool ParserImpl::ParseMacroCallTokens(TokenKind left, std::vector& tokens) { auto info = G_PARSE_MACRO_INFO.find(left); auto lSquarePos = lookahead.Begin(); if (info == G_PARSE_MACRO_INFO.end()) { return false; } auto right = info->second; auto isAttr = right == TokenKind::RSQUARE; if (!ParseMacroCallTokens(left, right, tokens, isAttr)) { return false; } if (!Skip(right)) { if (right == TokenKind::RSQUARE) { DiagExpectedRightDelimiter("[", lSquarePos); } else { DiagExpectedRightDelimiter("(", lSquarePos); } tokens.clear(), tokens.shrink_to_fit(); return false; } return true; } void ParserImpl::ParseNodeToTokens(const Node& node, const Token& begin, std::vector& tokens) { // Get tokens from the token vector according to the begin position and the end position of the node. tokens.push_back(begin); auto collectTokens = lexer->GetCollectTokens(); if (collectTokens.empty() || begin.End() == node.end) { // The current node has only one token and the token has been pushed back into the vector tokens. return; } auto startIter = collectTokens.begin(); auto endIter = collectTokens.end(); for (auto iter = collectTokens.begin(); iter != collectTokens.end(); ++iter) { // Get startIter according to the begin position of the node. if (iter->Begin() == begin.Begin()) { startIter = iter; } // Get endIter according to the end position of the node. if (iter->Begin() == node.end) { endIter = iter; break; } // Get endIter according to the end position of the node. if (iter->End() == node.end) { endIter = iter + 1; break; } } // If the content of startIter is the begin token. if (startIter->Begin() == begin.Begin()) { (void)tokens.insert(tokens.end(), startIter + 1, endIter); } else { (void)tokens.insert(tokens.end(), startIter, endIter); } // Delete last token if it is NL or END. if (tokens.back().kind == TokenKind::NL || tokens.back().kind == TokenKind::END) { tokens.pop_back(); } } void ParserImpl::ParseMacroCallDeclInput( ScopeKind scopeKind, const std::set& modifiers, MacroInvocation& invocation) { // Node Begin Pos must include modifier. auto nodeBegin = lookahead; auto bStart = lexer->StartCollectTokens(); auto decl = MakeInvalid(lookahead.Begin()); if (invocation.isFuncParam || SeeingParamInMacroCallExpr() || scopeKind == ScopeKind::PRIMARY_CONSTRUCTOR_FUNC_PARAM) { // For macrocall in parameter of primary constructor, // use ScopeKind::PRIMARY_CONSTRUCTOR_BODY_FOR_CLASS to avoid reparse error. auto scope = (scopeKind == ScopeKind::PRIMARY_CONSTRUCTOR_FUNC_PARAM) ? ScopeKind::PRIMARY_CONSTRUCTOR_BODY_FOR_CLASS : scopeKind; Ptr namedParameter{nullptr}; Ptr memberParam{nullptr}; decl = ParseParamInParamList(scope, namedParameter, memberParam); } else { decl = ParseDecl(scopeKind, modifiers); } if (decl && decl->TestAttr(Attribute::IS_BROKEN)) { chainedAST.back()->EnableAttr(Attribute::HAS_BROKEN); } lexer->StopCollectTokens(bStart); if (Utils::In(decl->astKind, G_VALID_DECL_LIST) && decl->astKind != ASTKind::MACRO_DECL) { ParseNodeToTokens(*decl, nodeBegin, invocation.args); } else { ParseDiagnoseRefactor(DiagKindRefactor::parse_illegal_macro_expand_input_args_without_paren, decl->begin); } invocation.decl = std::move(decl); } void ParserImpl::ParseMacroCallArgsWithoutParen( ScopeKind scopeKind, const std::set& modifiers, MacroInvocation& invocation) { ParseMacroCallDeclInput(scopeKind, modifiers, invocation); invocation.hasParenthesis = false; } void ParserImpl::ParseMacroCallArgsWithParen(MacroInvocation& invocation) { // macro call: @macroCall(any Tokens) invocation.leftParenPos = lookahead.Begin(); if (!ParseMacroCallTokens(TokenKind::LPAREN, invocation.args)) { return; } invocation.rightParenPos = lookahead.Begin(); invocation.hasParenthesis = true; } void ParserImpl::ParseMacroCallArg( ScopeKind scopeKind, const std::set& modifiers, MacroInvocation& invocation) { // Parse macro call args. if (Skip(TokenKind::LPAREN)) { ParseMacroCallArgsWithParen(invocation); } else { ParseMacroCallArgsWithoutParen(scopeKind, modifiers, invocation); } } void ParserImpl::ParseMacroCallAttr(MacroInvocation& invocation) { invocation.leftSquarePos = lookahead.Begin(); if (!ParseMacroCallTokens(TokenKind::LSQUARE, invocation.attrs)) { return; } invocation.rightSquarePos = lookahead.Begin(); } static void ParseInterplationString2OriginPosMap( Token &strToken, MacroInvocation& pMacroInvocation, DiagnosticEngine &diag) { std::vector tokens{strToken}; Lexer lexer(tokens, diag, diag.GetSourceManager()); auto strParts = lexer.GetStrParts(strToken); for (auto &strP : strParts) { if (strP.strKind != StringPart::EXPR) { continue; } CJC_ASSERT(strP.begin != INVALID_POSITION); Lexer lexerStr(strP.begin.fileID, strP.value, diag, diag.GetSourceManager(), strP.begin); std::vector tks = lexerStr.GetTokens(); // first token must be `$` and second token must be `{` CJC_ASSERT(!tks.empty()); for (size_t i = 2; i < tks.size() - 1; i++) { auto& t = tks[i]; if (t.kind == TokenKind::COMMENT) { continue; } auto be = t.Begin(); be.isCurFile = strToken.Begin().isCurFile; t.SetValuePos(t.Value(), be, t.End()); pMacroInvocation.originPosMap[static_cast(t.Begin().Hash32())] = be; } } } void ParserImpl::ParseMacroInvocation( ScopeKind scopeKind, const std::set& modifiers, MacroInvocation& invocation) { // Parse macro call attrs. if (Skip(TokenKind::LSQUARE)) { // Skip func always lets lastToken = lookAhead // macro call: @macroCall[xxx] Decl if (IsBuiltinMacro(invocation.identifier)) { (void)ParseDiagnoseRefactor( DiagKindRefactor::parse_macro_call_illegal_with_builtin, lookahead, invocation.identifier); } invocation.hasAttr = true; ParseMacroCallAttr(invocation); } invocation.scope = scopeKind; ParseMacroCallArg(scopeKind, modifiers, invocation); if (!curPrimaryDeclIdent.empty()) { invocation.outerDeclIdent = curPrimaryDeclIdent; } // For lsp. for (auto& tk : invocation.attrs) { invocation.originPosMap[static_cast(tk.Begin().Hash32())] = tk.Begin(); } // For lsp. for (auto& tk : invocation.args) { invocation.originPosMap[static_cast(tk.Begin().Hash32())] = tk.Begin(); if (tk.kind == TokenKind::STRING_LITERAL || tk.kind == TokenKind::MULTILINE_STRING) { ParseInterplationString2OriginPosMap(tk, invocation, diag); } } } bool ParserImpl::ParseMacroCallIdentifier(MacroInvocation& invocation, Node& node) { auto tokAt = Peek(); // macro call has the form as followings // @M class A{} // @p1.p2.p3.M class A{} if (!Skip(TokenKind::AT)) { Skip(TokenKind::AT_EXCL); invocation.isCompileTimeVisible = true; } // MacroCall identifier. invocation.fullName = Peek().Value(); invocation.atPos = tokAt.Begin(); if (newlineSkipped) { // We forbid this form of macro call: // @ // M class A{} ParseDiagnoseRefactor( DiagKindRefactor::parse_unexpected_newline_between_at_and_mc, tokAt.Begin(), lookahead.Value()); } auto item = ParseIdentifierFromName( invocation.fullName, lookahead.Begin(), lookahead.End(), invocation.fullName.size()); invocation.identifierPos = item.Begin(); Next(); // If we see @p1..M, the error msg is not useful. while (Skip(TokenKind::DOT)) { invocation.fullName += "."; invocation.fullNameDotPos.push_back(lastToken.Begin()); item = ExpectIdentifierWithPos(node); if (item == INVALID_IDENTIFIER) { if (node.astKind == ASTKind::MACRO_EXPAND_EXPR) { ConsumeUntilAny({TokenKind::NL, TokenKind::SEMI}); } else { ConsumeUntilDecl(); } return false; } invocation.fullName += item; } invocation.macroNamePos = lastToken.Begin(); invocation.identifier = item; return true; } template OwnedPtr ParserImpl::ParseMacroCall( ScopeKind scopeKind, const std::set& modifiers, std::vector> annos) { CheckOverflowAnno(annos, scopeKind); ffiParser->CheckAnnotations(annos); // @! can be used before decl (param included), but not expr if constexpr (std::is_same_v || std::is_same_v) { CJC_ASSERT(SeeingMacroCallDecl()); } else { CJC_ASSERT(SeeingMacroCall()); } OwnedPtr macroCall = MakeOwned(); ChainScope cs(*this, macroCall.get()); if (currentFile != nullptr) { macroCall->curFile = currentFile; macroCall->curFile->hasMacro = true; } macroCall->begin = lookahead.Begin(); if (!modifiers.empty()) { auto firstModifier = *SortModifierByPos(modifiers)[0]; DiagExpectNoModifierBefore(firstModifier, "macro call"); macroCall->begin = firstModifier.begin; } if (!ParseMacroCallIdentifier(macroCall->invocation, *macroCall.get())) { macroCall->EnableAttr(Attribute::IS_BROKEN); macroCall->end = lastToken.End(); return macroCall; } macroCall->identifier = Identifier{macroCall->invocation.identifier, macroCall->invocation.identifierPos, macroCall->invocation.identifierPos + macroCall->invocation.identifier.size()}; // May have multiple annotations include macro or builtin annotation. But Now annos Only have builtin annotations. if (!annos.empty()) { macroCall->annotations = std::move(annos); } ParseMacroInvocation(scopeKind, modifiers, macroCall->invocation); if (macroCall->invocation.decl) { CheckAnnotationAnno(macroCall->annotations, macroCall->invocation.decl); macroCall->end = macroCall->invocation.decl->end; } else { macroCall->end = lastToken.End(); } if (scopeKind == ScopeKind::ENUM_CONSTRUCTOR && (macroCall->invocation.hasParenthesis || (macroCall->invocation.decl && macroCall->invocation.decl->TestAttr(Attribute::ENUM_CONSTRUCTOR)))) { macroCall->EnableAttr(Attribute::ENUM_CONSTRUCTOR); } return macroCall; } template OwnedPtr ParserImpl::ParseMacroCall( ScopeKind scopeKind, const std::set& modifiers, std::vector> annos); template OwnedPtr ParserImpl::ParseMacroCall( ScopeKind scopeKind, const std::set& modifiers, std::vector> annos); template OwnedPtr ParserImpl::ParseMacroCall( ScopeKind scopeKind, const std::set& modifiers, std::vector> annos); cangjie_compiler-1.0.7/src/Parse/ParseModifiers.cpp000066400000000000000000000140711510705540100222730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements Modifiers check apis. */ #include "ParserImpl.h" #include "cangjie/AST/Utils.h" #include "cangjie/Parse/ParseModifiersRules.h" using namespace Cangjie; bool ParserImpl::SeeingModifier() { return (Peek().kind >= TokenKind::STATIC && Peek().kind <= TokenKind::OPERATOR) || (Peek().kind == TokenKind::CONST) || (Peek().kind == TokenKind::COMMON) || (Peek().kind == TokenKind::PLATFORM); } void ParserImpl::SetDeclBeginPos(AST::Decl& decl) const { if (decl.modifiers.empty()) { return; } auto modVec = SortModifierByPos(decl.modifiers); if (inForeignBlock) { auto size = modVec.size(); for (size_t i = 0; i < size; i++) { if (modVec[i]->modifier != TokenKind::FOREIGN) { continue; } modVec.erase(modVec.begin(), modVec.begin() + static_cast(i) + 1); break; } } if (modVec.empty()) { return; } decl.begin = modVec[0]->begin; } // Make sure there is no repeat modifier in modifiers. std::set ParserImpl::CheckDeclModifiers(const std::set& modifiers, ScopeKind scopeKind, DefKind defKind) { // For libast, ignore all scope info. if (scopeKind == ScopeKind::UNKNOWN_SCOPE) { std::set attrs; std::for_each(modifiers.begin(), modifiers.end(), [&](auto& modi) { if (auto attr = GetAttributeByModifier(modi.modifier)) { attrs.insert(attr.value()); } }); return attrs; } const auto& defRules = GetModifierRulesByDefKind(defKind); if (defRules.find(scopeKind) == defRules.end()) { return {}; } if (modifiers.empty()) { return (scopeKind == ScopeKind::CLASS_BODY || scopeKind == ScopeKind::INTERFACE_BODY) ? std::set{AST::Attribute::IN_CLASSLIKE} : std::set{}; } auto modifiersVec = SortModifierByPos(modifiers); if (defRules.at(scopeKind).empty()) { DiagIllegalModifierInScope(**modifiersVec.begin()); return {}; } auto scopeRules = defRules.at(scopeKind); std::unordered_map> scopeWarningRules; const auto& rules = GetModifierWarningRulesByDefKind(defKind); if (auto foundRules = rules.find(scopeKind); foundRules != rules.end()) { scopeWarningRules = foundRules->second; } if (!mpImpl->CheckCJMPModifiers(modifiers)) { return {}; } return GetModifierAttrs(scopeKind, scopeRules, scopeWarningRules, modifiersVec); } std::set ParserImpl::GetModifierAttrs(const ScopeKind& scopeKind, std::unordered_map>& scopeRules, const std::unordered_map>& scopeWarningRules, const std::vector>& modifiersVec) { // Store the modifiers TokenKind that has been traversed. std::vector> traversedModifiers; std::set attrs; std::vector> validModifiers; // All error reporting forms in modifiers should be unified. Currently, a variety of error reporting is adopted for // compatible use cases, and adjustments will be made in the future. for (auto& it : modifiersVec) { // Check allowing modifiers if (scopeRules.find(it->modifier) == scopeRules.end()) { // Ignore modifiers check when Parsing from libast. if (diag.ignoreScopeCheck) { continue; } DiagIllegalModifierInScope(*it); if (auto attr = GetAttributeByModifier(it->modifier)) { attrs.insert(attr.value()); } continue; } bool hasCollision = false; // Check conflict modifiers. for (auto& cm : scopeRules[it->modifier]) { auto iter = std::find_if(traversedModifiers.begin(), traversedModifiers.end(), [&cm](auto& preMod) { return preMod->modifier == cm; }); if (iter == traversedModifiers.end()) { continue; } DiagConflictedModifier(**iter, *it); hasCollision = true; // Remove conflicted modifiers from valid list which used for report warning. Utils::EraseIf(validModifiers, [&cm](auto mod) { return mod->modifier == cm; }); } traversedModifiers.emplace_back(it); if (!hasCollision) { validModifiers.emplace_back(it); } if (auto attr = GetAttributeByModifier(it->modifier)) { attrs.insert(attr.value()); } } ReportModifierWarning(scopeKind, scopeWarningRules, validModifiers); return attrs; } void ParserImpl::ReportModifierWarning([[maybe_unused]] ScopeKind scopeKind, const std::unordered_map>& scopeWarningRules, const std::vector>& modifiers) { for (auto rule : scopeWarningRules) { auto iter = std::find_if( modifiers.begin(), modifiers.end(), [&rule](auto& mod) { return mod->modifier == rule.first; }); if (iter == modifiers.end()) { continue; } for (auto higherMod : rule.second) { // Do not report scope dependent redundant modifier warning for macro expansion. if (higherMod == TokenKind::ILLEGAL && !diag.ignoreScopeCheck) { DiagRedundantModifiers(**iter); break; } auto found = std::find_if(modifiers.begin(), modifiers.end(), [&higherMod](auto& mod) { return mod->modifier == higherMod; }); if (found != modifiers.end()) { DiagRedundantModifiers(**iter, **found); break; } } } } cangjie_compiler-1.0.7/src/Parse/ParsePattern.cpp000066400000000000000000000220121510705540100217610ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements parse pattern. */ #include "ParserImpl.h" #include "cangjie/AST/Match.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace AST; // Pattern OwnedPtr ParserImpl::ParsePattern(const std::set& attributes, bool isVar, bool inDecl) { if (SeeingAny({TokenKind::BOOL_LITERAL, TokenKind::INTEGER_LITERAL, TokenKind::RUNE_BYTE_LITERAL, TokenKind::STRING_LITERAL, TokenKind::MULTILINE_STRING, TokenKind::MULTILINE_RAW_STRING, TokenKind::SUB, TokenKind::RUNE_LITERAL, TokenKind::FLOAT_LITERAL}) || Seeing({TokenKind::LPAREN, TokenKind::RPAREN})) { return ParseConstPattern(); } if (Skip(TokenKind::WILDCARD)) { Position begin = lookahead.Begin(); if (Skip(TokenKind::COLON)) { return ParseTypePattern(begin); } else { OwnedPtr ret = MakeOwned(); ret->begin = begin; ret->end = begin; ret->end.column += 1; return ret; } } if (Seeing(TokenKind::LPAREN)) { return ParseTuplePattern(false, attributes, isVar, inDecl); } if (SeeingIdentifierAndTargetOp({TokenKind::LPAREN, TokenKind::LT, TokenKind::DOT})) { return ParseEnumPattern(attributes, isVar, inDecl); } if (Seeing(TokenKind::IDENTIFIER) || SeeingContextualKeyword()) { return ParseTypePatternOrVarOrEnumPattern(attributes, isVar, inDecl); } ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_pattern, lookahead, ConvertToken(lookahead)); return MakeInvalid(lookahead.Begin()); } OwnedPtr ParserImpl::ParseVarPattern( const std::set& attributes, const SrcIdentifier& identifier, const Position& begin, bool isVar) const { OwnedPtr ret = MakeOwned(identifier, begin); ret->varDecl->isVar = isVar; for (auto& it : attributes) { ret->EnableAttr(it); ret->varDecl->EnableAttr(it); } ret->end = ret->varDecl->identifier.GetRawEndPos(); return ret; } OwnedPtr ParserImpl::ParseVarOrEnumPattern( const std::string& identifier, const Position& begin, size_t len, bool isRawId) const { Position idPos = isRawId ? begin + 1 : begin; SrcIdentifier id{identifier, idPos, idPos + len, isRawId}; OwnedPtr ret = MakeOwned(std::move(id)); ret->begin = begin; ret->end = id.GetRawEndPos(); return ret; } OwnedPtr ParserImpl::ParseTypePatternOrVarOrEnumPattern( const std::set& attributes, bool isVar, bool inDecl) { auto identifier = ParseIdentifierFromToken(lookahead); Position begin = lookahead.Begin(); Next(); if (Skip(TokenKind::COLON)) { OwnedPtr typePattern = MakeOwned(); typePattern->colonPos = lastToken.Begin(); typePattern->pattern = MakeOwned(identifier, begin); typePattern->type = ParseType(); typePattern->begin = begin; typePattern->end = typePattern->type->end; return typePattern; } if (inDecl) { return ParseVarPattern(attributes, identifier, begin, isVar); } return ParseVarOrEnumPattern(identifier, begin, identifier.Length(), identifier.IsRaw()); } OwnedPtr ParserImpl::ParseConstPattern() { OwnedPtr constPattern = MakeOwned(); constPattern->begin = lookahead.Begin(); constPattern->literal = ParseLitConst(); constPattern->end = constPattern->literal->end; return constPattern; } OwnedPtr ParserImpl::ParseTypePattern(const Position& begin) { OwnedPtr typePattern = MakeOwned(); typePattern->colonPos = lastToken.Begin(); typePattern->pattern = MakeOwned(begin); typePattern->type = ParseType(); typePattern->begin = begin; typePattern->end = typePattern->type->end; return typePattern; } OwnedPtr ParserImpl::ParseEnumPattern(const std::set& attributes, bool isVar, bool inDecl) { OwnedPtr enumPattern = MakeOwned(); enumPattern->constructor = ParseAtom(); while (Skip(TokenKind::DOT)) { OwnedPtr ctor = ParseMemberAccess(std::move(enumPattern->constructor)); if (ctor) { ctor->isPattern = true; } enumPattern->constructor = std::move(ctor); } CheckTypeArgumentsInEnumPattern(enumPattern.get()); enumPattern->begin = enumPattern->constructor->begin; enumPattern->end = enumPattern->constructor->end; for (auto& it : attributes) { enumPattern->EnableAttr(it); } if (Seeing(TokenKind::LPAREN)) { OwnedPtr tuplePattern = ParseTuplePattern(true, attributes, isVar, inDecl); enumPattern->leftParenPos = tuplePattern->begin; Position rightParenPos = tuplePattern->end; rightParenPos.column -= 1; enumPattern->rightParenPos = rightParenPos; enumPattern->patterns = std::move(tuplePattern->patterns); enumPattern->commaPosVector = tuplePattern->commaPosVector; enumPattern->end = tuplePattern->end; } return enumPattern; } void ParserImpl::CheckTypeArgumentsInEnumPattern(Ptr enumPattern) { if (enumPattern->constructor->IsInvalid()) { return; } if (enumPattern->constructor->astKind == ASTKind::REF_EXPR) { Ptr refExpr = StaticAs(enumPattern->constructor.get()); if (!refExpr->typeArguments.empty()) { auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_unexpected_declaration_in_scope, refExpr->leftAnglePos, "'<'", "enum pattern"); builder.AddMainHintArguments("'<'"); } } else { CJC_ASSERT(enumPattern->constructor->astKind == ASTKind::MEMBER_ACCESS); Ptr expr = enumPattern->constructor.get(); for (; expr->astKind == ASTKind::MEMBER_ACCESS; expr = StaticAs(expr)->baseExpr.get()) { Ptr memberAccess = StaticAs(expr); if (!memberAccess->typeArguments.empty() && expr != StaticAs(enumPattern->constructor.get())->baseExpr.get()) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_unexpected_declaration_in_scope, memberAccess->leftAnglePos, "'<'", "enum pattern"); builder.AddMainHintArguments("'<'"); } } CJC_ASSERT(expr->astKind == ASTKind::REF_EXPR); if (expr != StaticAs(enumPattern->constructor.get())->baseExpr.get()) { Ptr refExpr = StaticAs(expr); if (!refExpr->typeArguments.empty()) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_unexpected_declaration_in_scope, refExpr->leftAnglePos, "'<'", "enum pattern"); builder.AddMainHintArguments("'<'"); } } } } // HACK: isEnumPatterParams is used to indicate whether this tuple pattern belongs to a EnumPattern, however // EnumPattern's params are slightly different with TuplePattern. OwnedPtr ParserImpl::ParseTuplePattern( bool isEnumPatternParams, const std::set& attributes, bool isVar, bool inDecl) { Next(); // skip ( OwnedPtr tuplePattern = MakeOwned(); ChainScope cs(*this, tuplePattern.get()); for (auto& it : attributes) { tuplePattern->EnableAttr(it); } tuplePattern->begin = lookahead.Begin(); tuplePattern->leftBracePos = lookahead.Begin(); std::vector commaPosVector; do { tuplePattern->patterns.emplace_back(ParsePattern(attributes, isVar, inDecl)); if (Seeing(TokenKind::BITOR)) { DiagOrPattern(); ConsumeUntilAny({TokenKind::COMMA, TokenKind::RPAREN}, false); } if (Seeing(TokenKind::COMMA)) { commaPosVector.push_back(lookahead.Begin()); } } while (Skip(TokenKind::COMMA)); if (!Skip(TokenKind::RPAREN)) { if (!std::any_of(tuplePattern->patterns.begin(), tuplePattern->patterns.end(), [](auto& pattern) { return pattern->TestAttr(Attribute::IS_BROKEN); })) { DiagExpectedRightDelimiter("(", tuplePattern->begin); } } tuplePattern->commaPosVector = std::move(commaPosVector); tuplePattern->rightBracePos = lastToken.Begin(); if (!isEnumPatternParams && tuplePattern->patterns.size() == 1) { DiagExpectedMoreFieldInTuplePattern(); } tuplePattern->end = lastToken.End(); return tuplePattern; } cangjie_compiler-1.0.7/src/Parse/ParseQuote.cpp000066400000000000000000000121201510705540100214400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * This file implements quote parse apis. */ #include "ParserImpl.h" #include "cangjie/Basic/SourceManager.h" #include "cangjie/Basic/StringConvertor.h" #include "cangjie/AST/Create.h" using namespace Cangjie; using namespace Cangjie::AST; void ParserImpl::ParseQuoteDollarInterpolationWithParen(AST::QuoteExpr& qe) { const static std::vector INVALID_AST_KIND = {ASTKind::INVALID_EXPR, ASTKind::RETURN_EXPR, ASTKind::WHILE_EXPR, ASTKind::DO_WHILE_EXPR, ASTKind::FOR_IN_EXPR, ASTKind::THROW_EXPR, ASTKind::TRY_EXPR, ASTKind::JUMP_EXPR}; auto dol = lookahead; Skip(TokenKind::DOLLAR); auto dollarPos = lastToken.Begin(); Skip(TokenKind::LPAREN); auto lpPos = lastToken.Begin(); skipNL = true; OwnedPtr expr = ParseExpr(); CheckMacroExprRules(dol, Token{TokenKind::DOT}, *expr); skipNL = false; if (Utils::In(expr->astKind, INVALID_AST_KIND)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_invalid_quote_dollar_expr, dol); } // add a level of ParenExpr to store the location of '$()', to be used by std.syntax // cjc/fmt/lsp does not need it auto quoteExprWrapper = MakeOwned(); quoteExprWrapper->expr = std::move(expr); quoteExprWrapper->begin = dollarPos; quoteExprWrapper->leftParenPos = lpPos; quoteExprWrapper->rightParenPos = lookahead.Begin(); quoteExprWrapper->end = lookahead.End(); qe.exprs.emplace_back(std::move(quoteExprWrapper)); if (!Seeing(TokenKind::RPAREN)) { DiagExpectedRightDelimiter("(", dol.Begin()); } } void ParserImpl::ParseQuoteDollarInterpolation(AST::QuoteExpr& qe) { // Support '$ast' use case. auto token = Peek(); auto ident = token.Value().substr(1, token.Value().length() - 1); bool isRawId = IsRawIdentifier(ident); if (isRawId) { ident = ParseNameFromRawIdentifier(ident); } auto ref = CreateRefExpr({ident, token.Begin(), token.End(), isRawId}, nullptr, token.Begin()); ref->begin.column += 1; ref->end = token.End(); ref->isQuoteDollar = true; ref->EnableAttr(Attribute::COMPILER_ADD); qe.exprs.emplace_back(std::move(ref)); } namespace Cangjie { const std::vector& GetEscapeTokenKinds() { const static std::vector ESCAPE_TOKEN_KINDS = { TokenKind::DOLLAR_IDENTIFIER, TokenKind::AT, TokenKind::DOLLAR, TokenKind::LPAREN, TokenKind::RPAREN}; return ESCAPE_TOKEN_KINDS; } } void ParserImpl::ParseQuoteEscapeToken(std::vector& tokens) { // In Lex/Lexer.cpp, IsMacroEscape() function, escaping of [ and ] is used in macro attribute input const static std::vector ESCAPE_FOR_MACRO_NOT_FOR_QUOTE = { TokenKind::LSQUARE, TokenKind::RSQUARE}; Skip(TokenKind::ILLEGAL); if (SeeingAny(GetEscapeTokenKinds())) { auto begin = lastToken.Begin(); auto token = Peek(); tokens.emplace_back(token.kind, token.Value(), begin, token.End()); } else if (SeeingAny(ESCAPE_FOR_MACRO_NOT_FOR_QUOTE)) { auto builder = diag.DiagnoseRefactor( DiagKindRefactor::lex_unknown_start_of_token, lastToken.Begin(), lastToken.Value()); builder.AddMainHintArguments(lastToken.Value()); tokens.emplace_back(lastToken); } else { tokens.emplace_back(lastToken); } } static void GenerateTokenPart(QuoteExpr& qe, std::vector& tokens) { if (tokens.empty()) { return; } qe.exprs.emplace_back(MakeOwned(tokens)); tokens.clear(); } void ParserImpl::ParseQuoteTokens(QuoteExpr& qe) { std::vector tokens; auto lparenCnt = 1 - static_cast(Seeing(TokenKind::RPAREN)); while (lparenCnt > 0) { if (Seeing(TokenKind::END)) { break; } if (Seeing(TokenKind::ILLEGAL) && lookahead == "\\") { ParseQuoteEscapeToken(tokens); } else if (Seeing({TokenKind::DOLLAR, TokenKind::LPAREN})) { GenerateTokenPart(qe, tokens); ParseQuoteDollarInterpolationWithParen(qe); } else if (Seeing(TokenKind::DOLLAR_IDENTIFIER)) { GenerateTokenPart(qe, tokens); ParseQuoteDollarInterpolation(qe); } else { if (Seeing(TokenKind::LPAREN)) { lparenCnt++; } else if (Seeing(TokenKind::DOLLAR)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expect_escape_dollar_token, lookahead.Begin()); } if (SkipAmbiguousToken() || SkipCombinedDoubleArrow()) { tokens.emplace_back(lastToken); lparenCnt -= static_cast(Seeing(TokenKind::RPAREN)); continue; } auto token = Peek(); tokens.emplace_back(token); } Next(); lparenCnt -= static_cast(Seeing(TokenKind::RPAREN)); } GenerateTokenPart(qe, tokens); } cangjie_compiler-1.0.7/src/Parse/ParseType.cpp000066400000000000000000000332511510705540100212740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * This file implements type parse apis. */ #include "ParserImpl.h" #include "cangjie/AST/Match.h" #include "cangjie/Basic/StringConvertor.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace AST; // BaseType is atomicType in BNF. OwnedPtr ParserImpl::ParseBaseType() { if (Seeing(TokenKind::IDENTIFIER) || SeeingContextualKeyword()) { return ParseQualifiedType(); } // Paren Type, Tuple Type, or Function Type if (Skip(TokenKind::LPAREN)) { return ParseTypeWithParen(); } if (SeeingPrimTypes()) { OwnedPtr primType = MakeOwned(); primType->begin = lookahead.Begin(); primType->end = lookahead.End(); primType->str = lookahead.Value(); primType->kind = TOKENKIND_TO_PRIMITIVE_TYPEKIND_MAP.at(lookahead.kind); Next(); return primType; } if (Skip(TokenKind::THISTYPE)) { if (enableThis) { return MakeOwned(lookahead.Begin()); } else { DiagThisTypeNotAllow(); // When 'This' appearred in invalid position, should return invalid type for correct semantic. return MakeOwned(lookahead.Begin()); } } if (Skip(TokenKind::VARRAY)) { return ParseVarrayType(); } auto type = MakeOwned(lookahead.Begin()); DiagExpectedTypeName(); type->EnableAttr(Attribute::IS_BROKEN); return type; } OwnedPtr ParserImpl::ParseVarrayType() { OwnedPtr ret = MakeOwned(); ret->varrayPos = lastToken.Begin(); ret->begin = lastToken.Begin(); ChainScope cs(*this, ret.get()); if (!Skip(TokenKind::LT)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_varray_type_parameter, lastToken.End()); return MakeOwned(lookahead.Begin()); } ret->leftAnglePos = lastToken.Begin(); // // ^ Parse the type argument of VArray. ret->typeArgument = ParseType(); if (ret->typeArgument->IsInvalid()) { return MakeOwned(lookahead.Begin()); } // // ^ Parse the comma between the type argument and constant type. if (!Skip(TokenKind::COMMA)) { DiagVArrayTypeArgMismatch(MakeRange(ret->leftAnglePos, lookahead.End()), "a type argument and size literal"); return MakeOwned(lookahead.Begin()); } ret->typeArgument->commaPos = lastToken.Begin(); // // ^ Parse the constant type prefix. skipNL = false; if (!Skip(TokenKind::DOLLAR)) { DiagVArrayTypeArgMismatch(MakeRange(lookahead.Begin(), lookahead.End()), "a '$' follows an integer literal as the second generic argument"); return MakeOwned(lookahead.Begin()); } OwnedPtr constType = MakeOwned(); constType->dollarPos = lastToken.Begin(); constType->begin = lastToken.Begin(); // // ^ Parse the constant value. if (!Seeing(TokenKind::INTEGER_LITERAL)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expect_integer_literal_varray, constType->dollarPos); ConsumeUntil(TokenKind::NL); return MakeOwned(lookahead.Begin()); } skipNL = true; constType->constantExpr = ParseLitConst(); constType->end = lookahead.End(); ret->constantType = std::move(constType); if (!Skip(TokenKind::GT)) { DiagExpectedRightDelimiter("<", ret->leftAnglePos); return MakeOwned(lookahead.Begin()); } ret->rightAnglePos = lookahead.Begin(); ret->end = lookahead.End(); return ret; } OwnedPtr ParserImpl::ParseQualifiedType() { OwnedPtr baseType = ParseRefType(false); while (Seeing(TokenKind::DOT)) { auto dotPos = Peek().Begin(); Next(); // Qualified type is userType in BNF. auto qualifiedType = MakeOwned(); ChainScope cs(*this, qualifiedType.get()); qualifiedType->begin = baseType->begin; qualifiedType->baseType = std::move(baseType); qualifiedType->dotPos = dotPos; qualifiedType->field = ExpectIdentifierWithPos(*qualifiedType); qualifiedType->end = qualifiedType->field.GetRawEndPos(); if (Skip(TokenKind::LT)) { qualifiedType->leftAnglePos = lastToken.Begin(); qualifiedType->typeArguments = ParseTypeArguments().second; qualifiedType->end = lastToken.End(); qualifiedType->rightAnglePos = lastToken.Begin(); } baseType = std::move(qualifiedType); } return baseType; } OwnedPtr ParserImpl::ParseTupleType( std::vector> types, const Position lParenPos, const Position rParenPos) const { OwnedPtr tupleType = MakeOwned(); tupleType->begin = lParenPos; tupleType->leftParenPos = lParenPos; tupleType->rightParenPos = rParenPos; for (auto& type : types) { tupleType->commaPosVector.emplace_back(type->commaPos); tupleType->fieldTypes.emplace_back(std::move(type)); } tupleType->end = rParenPos; tupleType->end.column += 1; return tupleType; } OwnedPtr ParserImpl::ParseTypeParameterInTupleType( std::unordered_map& typeNameMap) { while (Skip(TokenKind::NL)) { } Position colonPos; SrcIdentifier typeParameterName; if (Seeing({TokenKind::IDENTIFIER, TokenKind::COLON}) || SeeingPrimaryKeyWordContext(TokenKind::COLON)) { Next(); typeParameterName = ParseIdentifierFromToken(lastToken); auto it = typeNameMap.find(typeParameterName); if (it != typeNameMap.end()) { auto builder = diag.DiagnoseRefactor( DiagKindRefactor::parse_duplicate_type_parameter_name, MakeRange(typeParameterName), typeParameterName); builder.AddHint(MakeRange(it->second, typeParameterName)); } else { typeNameMap.emplace(typeParameterName, typeParameterName.Begin()); } while (Skip(TokenKind::NL)) { } Next(); colonPos = lastToken.Begin(); } auto type = ParseType(); type->typeParameterName = typeParameterName; type->typeParameterNameIsRawId = typeParameterName.IsRaw(); type->colonPos = colonPos; type->typePos = type->begin; type->begin = typeParameterName.ZeroPos() ? type->begin : typeParameterName.Begin(); return type; } OwnedPtr ParserImpl::ParseTypeWithParen() { Position lParenPos = lastToken.Begin(); std::vector> types; std::unordered_map typeNameMap; ParseZeroOrMoreSepTrailing([&types](const Position commaPos) { types.back()->commaPos = commaPos; }, [this, &types, &typeNameMap]() { if (Seeing(TokenKind::RPAREN) && types.size() > 1) { return; } types.emplace_back(ParseTypeParameterInTupleType(typeNameMap)); }, TokenKind::RPAREN); // in a parameter type list, either all parameters must be named, or none of them; mixed is not allowed if (std::any_of(types.begin(), types.end(), [](const OwnedPtr& type) { return type->typeParameterName.empty(); }) && std::any_of(types.begin(), types.end(), [](const OwnedPtr& type) { return !type->typeParameterName.empty(); })) { ParseDiagnoseRefactor( DiagKindRefactor::parse_all_parameters_must_be_named, MakeRange(types.front()->begin, types.back()->end)); } if (!Skip(TokenKind::RPAREN)) { DiagExpectedRightDelimiter("(", lParenPos); return MakeOwned(lookahead.Begin()); } Position rParenPos = lastToken.Begin(); if (Skip(TokenKind::ARROW)) { return ParseFuncType(std::move(types), lParenPos, rParenPos); } // This is a paren type. if (types.size() == 1) { return ParseParenType(lParenPos, rParenPos, std::move(types[0])); } // This is a tuple type. 2 is the minimum dimension allowed by tuple. if (types.size() >= 2) { return ParseTupleType(std::move(types), lParenPos, rParenPos); } // This is treated as a broken function, which has empty types and no arrow. ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_arrow_in_func_type, lookahead.Begin()); return ParseFuncType(std::move(types), lParenPos, rParenPos); } OwnedPtr ParserImpl::ParseParenType( const Position& lParenPos, const Position& rParenPos, OwnedPtr type) { if (!type->typeParameterName.empty()) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_only_tuple_and_func_type_allow_type_parameter_name, MakeRange(type->begin, type->begin + type->typeParameterName.size()), type->typeParameterName); builder.AddNote("only tuple type and function type support type parameter name"); } OwnedPtr pt = MakeOwned(); pt->type = std::move(type); pt->leftParenPos = lParenPos; pt->rightParenPos = rParenPos; pt->begin = lParenPos; pt->end = rParenPos; pt->end.column += 1; return pt; } OwnedPtr ParserImpl::ParseFuncType( std::vector> types, const Position& lParenPos, const Position& rParenPos) { OwnedPtr ft = MakeOwned(); ft->arrowPos = lastToken.Begin(); ft->begin = lParenPos; ft->leftParenPos = lParenPos; ft->rightParenPos = rParenPos; ft->retType = ParseType(); ft->end = ft->retType->end; ft->paramTypes = std::move(types); return ft; } // Parse the syntactic sugar of option types. OwnedPtr ParserImpl::ParsePrefixType() { // See the symbol '?'. if (Seeing(TokenKind::QUEST)) { OwnedPtr optionType = MakeOwned(); optionType->begin = lookahead.Begin(); // Handle all '?' bool prevSkipNL = skipNL; skipNL = false; while (Skip(TokenKind::QUEST)) { optionType->questNum++; if (lastToken.kind == TokenKind::QUEST) { optionType->questVector.emplace_back(lastToken.Begin()); } optionType->end = lastToken.End(); if (Seeing(TokenKind::NL)) { ParseDiagnoseRefactor( DiagKindRefactor::parse_newline_not_allowed_between_quest_and_type, lookahead.End()); SkipBlank(TokenKind::NL); } } // Parse the type after the last '?'. OwnedPtr baseType = ParseBaseType(); skipNL = true; // Skip tailing newline, if previous skip newline enabled. while (prevSkipNL && Skip(TokenKind::NL)) { } optionType->end = baseType->end; optionType->componentType = std::move(baseType); return optionType; } else { OwnedPtr baseType = ParseBaseType(); return baseType; } } OwnedPtr ParserImpl::ParseType() { auto postType = ParsePrefixType(); if (Seeing(TokenKind::ARROW)) { if (postType->astKind == ASTKind::FUNC_TYPE) { DiagRedundantArrowAfterFunc(*postType); ConsumeUntilAny({TokenKind::RCURL, TokenKind::NL}, false); } else { DiagParseExpectedParenthis(postType); } } return postType; } OwnedPtr ParserImpl::ParseRefType(bool onlyRef) { OwnedPtr ret = MakeOwned(); ChainScope cs(*this, ret.get()); ret->ref.identifier = ExpectIdentifierWithPos(*ret); if (ret->ref.identifier == INVALID_IDENTIFIER) { ret->EnableAttr(Attribute::IS_BROKEN); TryConsumeUntilAny({TokenKind::LT}); } ret->begin = lookahead.Begin(); ret->end = lookahead.End(); if (!onlyRef && Skip(TokenKind::LT)) { ret->leftAnglePos = lastToken.Begin(); ret->typeArguments = ParseTypeArguments().second; ret->rightAnglePos = lastToken.Begin(); ret->end = lastToken.End(); } return ret; } std::pair>> ParserImpl::ParseTypeArguments(ExprKind ek) { Position leftAnglePos = lastToken.Begin(); std::vector> ret; if (Skip(TokenKind::GT)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_type_argument, lastToken.Begin()); return {true, std::move(ret)}; } ParseOneOrMoreSepTrailing( [&ret](const Position commaPos) { if (!ret.empty()) { ret.back()->commaPos = commaPos; } }, [this, &ret]() { while (Skip(TokenKind::NL)) { } auto type = ParseType(); if (type && !type->TestAttr(Attribute::IS_BROKEN)) { ret.emplace_back(std::move(type)); } }, TokenKind::GT); if (!Skip(TokenKind::GT) && !ret.empty()) { DiagExpectedRightDelimiter("<", leftAnglePos); ret.clear(); return {false, std::move(ret)}; } if (ret.empty() || std::any_of(ret.begin(), ret.end(), [](auto& type) { return type->astKind == ASTKind::INVALID_TYPE; })) { ret.clear(); return {false, std::move(ret)}; } if (IsExprFollowedComma(ek) && TypeArgsMaybeConfusedWithExprWithComma(ret) && !IsLegFollowForGenArgInExprWithComma(ek)) { // it may be part of an expr with comma. e.g. in (a=d) ret.clear(); return {false, std::move(ret)}; } return {true, std::move(ret)}; } cangjie_compiler-1.0.7/src/Parse/Parser.cpp000066400000000000000000000514121510705540100206130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Parser. */ #include "ParserImpl.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Lex/Token.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace Cangjie::AST; Parser::Parser(unsigned int fileID, const std::string& input, DiagnosticEngine& diag, SourceManager& sm, bool attachComment, bool parseDeclFile) : impl{new ParserImpl{fileID, input, diag, sm, attachComment, parseDeclFile}} { } Parser::Parser(const std::string& input, DiagnosticEngine& diag, SourceManager& sm, const Position& pos, bool attachComment, bool parseDeclFile) : impl{new ParserImpl{input, diag, sm, pos, attachComment, parseDeclFile}} { } Parser::Parser(const std::vector& inputTokens, DiagnosticEngine& diag, SourceManager& sm, bool attachComment, bool parseDeclFile) : impl{new ParserImpl{inputTokens, diag, sm, attachComment, parseDeclFile}} { } uint8_t ParserImpl::Precedence(TokenKind kind) const { auto i = static_cast(kind); CJC_ASSERT(i < NUM_TOKENS); return TOKEN_TO_OPERATOR_PRECEDENCE[i]; } bool ParserImpl::ParsePackageHeaderEnd() { bool seeingFileEnd = false; if (newlineSkipped) { return seeingFileEnd; } if (Seeing(TokenKind::END)) { seeingFileEnd = true; } else if (!Skip(TokenKind::SEMI)) { DiagExpectSemiOrNewline(); } return seeingFileEnd; } size_t ParserImpl::GetLineNum() const { std::unordered_set lines; for (auto& item : allTokensInOneFile) { for (int64_t i = item.first; i <= item.second; ++i) { lines.emplace(i); } } return lines.size(); } OwnedPtr ParserImpl::ParseTopLevel() { OwnedPtr ret = MakeOwned(); currentFile = ret.get(); Peek(); // Set the begin of file at (fileID, 1, 1). ret->begin = {lexer->GetFileID(), 1, 1}; Source& source = sourceManager.GetSource(ret->begin.fileID); ret->fileName = FileUtil::GetFileName(source.path); ret->filePath = source.path; ret->fileHash = source.fileHash; SkipBlank(TokenKind::NL); /** * preamble * : featureSpec? packageHeader? importSpec* * ; */ // Parse features in TopLevel if (SeeingFeature()) { ParseFeatureDirective(ret->feature); } PtrVector annos; if (SeeingBuiltinAnnotation()) { ParseAnnotations(annos); } // mark a reset point ParserScope scope(*this); std::set modifiers; ParseModifiers(modifiers); if (SeeingPackage() || SeeingMacroPackage()) { ret->package = ParsePackageHeader(std::move(modifiers)); /** * packageHeader * : packageModifier? PACKAGE NL* (packageName NL* DOT NL*)* packageName end+ * ; */ if (ParsePackageHeaderEnd()) { // seeing end of file ret->end = lastToken.End(); ret->curFile = ret; return ret; } SkipBlank(TokenKind::SEMI); if (!annos.empty()) { DiagUnexpectedAnnoOn(*annos.front(), ret->package->packagePos, annos.front()->identifier, "package"); annos.clear(); } } else { scope.ResetParserScope(); } // Parse importSpec in TopLevel. ParseImportSpecInTop(ret->imports, annos); if (!scanDepPkg) { ParseTopLevelDecls(*ret, annos); } ret->end = lookahead.End(); if (ret->end.IsZero()) { ret->end = ret->begin; } if (enableAttachComment) { AttachCommentToFile(ret.get()); } commentsMap.emplace(std::make_pair(ret->begin.fileID, lexer->GetComments())); // Assign this->currentFile to curFile of every node except `Package` and `MacroExpandExpr's Modifier` AssignCurFile(ret); return ret; } void ParserImpl::AssignCurFile(const OwnedPtr& file) const { file->curFile = file.get(); auto visitPre = [&file](Ptr curNode) -> VisitAction { curNode->curFile = file.get(); return VisitAction::WALK_CHILDREN; }; Walker curFileAssignWalker(file.get(), visitPre); curFileAssignWalker.Walk(); } void ParserImpl::ParseTopLevelDecls(File& file, PtrVector& annos) { int toplevelCount = 0; while (true) { if (!newlineSkipped) { if (Seeing(TokenKind::END)) { break; } } if (toplevelCount >= 1) { /** * translationUnit * : NL* preamble end* (topLevelObject (end+ topLevelObject?)*)? EOF * ; * */ if (!newlineSkipped && !Skip(TokenKind::SEMI)) { DiagExpectSemiOrNewline(); } SkipBlank(TokenKind::SEMI); } if (DetectPrematureEnd()) { break; } ParseAnnotations(annos); ParseTopLevelDecl(file, annos); annos.clear(); bracketsStack.clear(); toplevelCount++; } } void ParserImpl::ParseTopLevelDecl(File& file, PtrVector& annos) { // Foreign block. std::set modifiers; ParseModifiers(modifiers); if (HasModifier(modifiers, TokenKind::FOREIGN)) { auto decls = ParseForeignDecls(modifiers, annos); if (!decls.empty()) { std::move(std::begin(decls), std::end(decls), std::back_inserter(file.decls)); } return; } auto decl = ParseDecl(ScopeKind::TOPLEVEL, modifiers, std::move(annos)); if (currentFile && currentFile->package) { if (HasModifier(decl->modifiers, TokenKind::PUBLIC) && currentFile->package->hasMacro && decl->astKind != AST::ASTKind::MACRO_DECL) { DiagDeclarationInMacroPackage(decl); } } file.decls.emplace_back(std::move(decl)); } OwnedPtr ParserImpl::ParsePackageHeader(std::set&& modifiers) { auto packageHeader = MakeOwned(); if (!modifiers.empty()) { const auto firstModifier = SortModifierByPos(modifiers)[0]; auto modifier = MakeOwned(firstModifier->modifier, firstModifier->begin); packageHeader->modifier = std::move(modifier); } packageHeader->EnableAttr(GetModifierAttr(packageHeader->modifier, ASTKind::PACKAGE_SPEC)); if (Skip(TokenKind::MACRO)) { packageHeader->macroPos = lastToken.Begin(); packageHeader->hasMacro = true; } Skip(TokenKind::PACKAGE); packageHeader->begin = packageHeader->modifier ? packageHeader->modifier->begin : packageHeader->hasMacro ? packageHeader->macroPos : lastToken.Begin(); packageHeader->packagePos = lastToken.Begin(); ChainScope cs(*this, packageHeader.get()); SrcIdentifier curIdent; bool skipDc{false}; do { skipDc = false; if (!curIdent.Empty()) { packageHeader->prefixPaths.emplace_back(curIdent.Val()); packageHeader->prefixPoses.emplace_back(curIdent.Begin()); packageHeader->prefixDotPoses.emplace_back(lastToken.Begin()); } curIdent = ExpectPackageIdentWithPos(*packageHeader); if (curIdent == INVALID_IDENTIFIER || curIdent.IsRaw()) { ConsumeUntilAny({TokenKind::NL}); packageHeader->EnableAttr(Attribute::IS_BROKEN); if (curIdent.IsRaw()) { // If isRawId is true, curIdentPos has been added 1, so we decrease 1 here to get the original position. ParseDiagnoseRefactor(DiagKindRefactor::parse_package_name_has_backtick, MakeRange(curIdent.Begin() - 1, curIdent.End() + 1)); } break; } if (packageHeader->prefixPaths.empty() && Skip(TokenKind::DOUBLE_COLON)) { packageHeader->hasDoubleColon = true; skipDc = true; } } while (skipDc || Skip(TokenKind::DOT)); packageHeader->packageName = std::move(curIdent); packageHeader->end = curIdent.End(); (void)CheckDeclModifiers(modifiers, ScopeKind::TOPLEVEL, DefKind::PACKAGE); auto fullPackageName = packageHeader->GetPackageName(); if (!packageHeader->TestAttr(Attribute::IS_BROKEN) && fullPackageName.size() > PACKAGE_NAME_LEN_LIMIT) { auto packageNameBeginPos = packageHeader->prefixPaths.empty() ? curIdent.Begin() : packageHeader->prefixPoses[0]; DiagNameLengthOverflow(MakeRange(packageNameBeginPos, curIdent.End()), fullPackageName, PACKAGE_NAME_LEN_LIMIT, fullPackageName.size()); } if (!packageHeader->prefixPaths.empty()) { // NOTE: will only used for 'std' module. this->moduleName = packageHeader->prefixPaths[0]; } return packageHeader; } std::vector> ParserImpl::ParseForeignDecls( const std::set& modifiers, PtrVector& annos) { std::vector> ret; if (!Skip(TokenKind::LCURL)) { // Single foreign declaration. auto decl = ParseDecl(ScopeKind::TOPLEVEL, modifiers, std::move(annos)); ret.emplace_back(std::move(decl)); return ret; } CheckNoDeprecatedAnno(annos, "foreign block"); Position leftCurlyBracePos = lastToken.Begin(); SkipBlank(TokenKind::SEMI); inForeignBlock = true; for (;;) { if (Skip(TokenKind::RCURL)) { break; } if (DetectPrematureEnd()) { DiagExpectedRightDelimiter("{", leftCurlyBracePos); break; } auto clonedMods = modifiers; // Copy annos here, because ParseDecl(xxx) will move annos. PtrVector clonedAnnos; for (auto& it : annos) { (void)clonedAnnos.emplace_back(ASTCloner::Clone(it.get())); } ParseAnnotations(clonedAnnos); auto decl = ParseDecl(ScopeKind::TOPLEVEL, clonedMods, std::move(clonedAnnos)); ret.emplace_back(std::move(decl)); SkipBlank(TokenKind::SEMI); } inForeignBlock = false; return ret; } void ParserImpl::CheckNoDeprecatedAnno(const PtrVector& annos, const std::string& invalidTarget) { for (auto& anno : annos) { if (anno->kind == AnnotationKind::DEPRECATED) { DiagDeprecatedInvalidTarget(*anno, invalidTarget); } } } void ParserImpl::CheckDeprecationOfFuncParam(const FuncParam& param) { for (auto& anno : param.annotations) { if (anno->kind != AnnotationKind::DEPRECATED) { continue; } if (!param.isNamedParam) { DiagDeprecatedInvalidTarget(*anno, "Not named parameter"); } bool hasDefaultValue = param.assignment; if (!hasDefaultValue) { DiagDeprecatedInvalidTarget(*anno, "Parameter without default value"); } } } OwnedPtr ParserImpl::ParsePropDecl( ScopeKind scopeKind, const std::set& modifiers, std::vector> annos) { Next(); OwnedPtr ret = MakeOwned(); ret->begin = lookahead.Begin(); ret->keywordPos = lookahead.Begin(); ChainScope cs(*this, ret.get()); CheckDeclarationInScope(scopeKind, DefKind::PROPERTY); ret->identifier = ExpectIdentifierWithPos(*ret); if (ret->identifier == INVALID_IDENTIFIER) { ret->EnableAttr(Attribute::IS_BROKEN); ret->end = lastToken.End(); ConsumeUntilDecl(TokenKind::RCURL); return ret; } ret->end = lookahead.End(); // PropDecl must declare type explicitly, otherwise we report error during Parsing. if (Skip(TokenKind::COLON)) { ret->colonPos = lastToken.Begin(); ret->type = ParseType(); } else { ret->type = MakeOwned(lookahead.Begin()); DiagExpectCharacter("':'", "property declaration must declare type explicitly"); } auto attrs = CheckDeclModifiers(modifiers, scopeKind, DefKind::PROPERTY); std::for_each(attrs.begin(), attrs.end(), [&](auto&& it) { ret->EnableAttr(it); }); if (ret->TestAttr(Attribute::MUT)) { ret->isVar = true; } ret->modifiers.insert(modifiers.begin(), modifiers.end()); auto inClassLikeScope = scopeKind == ScopeKind::CLASS_BODY || scopeKind == ScopeKind::INTERFACE_BODY; if (Skip(TokenKind::LCURL)) { ParsePropBody(modifiers, *ret); } else if (inClassLikeScope || scopeKind == ScopeKind::UNKNOWN_SCOPE || ret->TestAttr(Attribute::COMMON)) { if (CanBeAbstract(*ret, scopeKind)) { ret->EnableAttr(Attribute::ABSTRACT); } if (ret->type) { ret->end = ret->type->end; } } else { DiagMissingPropertyBody(*ret); ret->end = lastToken.End(); } ret->annotations = std::move(annos); SetDefaultFunc(scopeKind, *ret); return ret; } void ParserImpl::CheckClassLikePropAbstractness(AST::PropDecl& prop) { CJC_NULLPTR_CHECK(prop.outerDecl); if (prop.outerDecl->TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::OBJ_C_MIRROR)) { prop.DisableAttr(Attribute::ABSTRACT); return; } bool isJavaMirrorOrJavaMirrorSubtype = prop.outerDecl->TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE); bool isCommon = prop.TestAttr(Attribute::COMMON); auto outerModifiers = prop.outerDecl->modifiers; bool inAbstract = HasModifier(outerModifiers, TokenKind::ABSTRACT); bool inCJMP = HasModifier(outerModifiers, TokenKind::PLATFORM) || HasModifier(outerModifiers, TokenKind::COMMON); bool inAbstractCJMP = inAbstract && inCJMP; bool inClass = prop.outerDecl->astKind == ASTKind::CLASS_DECL; bool inAbstractCJMPClass = inAbstractCJMP && inClass; if (HasModifier(prop.modifiers, TokenKind::ABSTRACT) && !isCommon && !inAbstractCJMP && !isJavaMirrorOrJavaMirrorSubtype) { ParseDiagnoseRefactor(DiagKindRefactor::parse_explicitly_abstract_only_for_cjmp_abstract_class, lastToken.End(), "property"); } if (inAbstractCJMPClass && prop.TestAttr(Attribute::ABSTRACT) && !HasModifier(prop.modifiers, TokenKind::ABSTRACT)) { prop.DisableAttr(Attribute::ABSTRACT); return; } // `abstract` modifier valid only in COMMON ABSTRACT class if (HasModifier(prop.modifiers, TokenKind::ABSTRACT) && !inAbstractCJMPClass) { Ptr abstractMod = nullptr; for (auto& modifier : prop.modifiers) { if (modifier.modifier == TokenKind::ABSTRACT) { abstractMod = Ptr(&modifier); } } CJC_NULLPTR_CHECK(abstractMod); ChainScope cs(*this, Ptr(&prop)); DiagIllegalModifierInScope(*abstractMod); } } void ParserImpl::DiagMissingPropertyBody(AST::PropDecl& prop) { if (parseDeclFile) { return; } auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_character, lookahead, "'{'", ConvertToken(lookahead)); builder.AddMainHintArguments("'{'"); builder.AddNote("property declaration must have body"); prop.EnableAttr(Attribute::HAS_BROKEN); } void ParserImpl::ParseExtendedType(ExtendDecl& extendDecl) { if (Skip(TokenKind::LT)) { extendDecl.EnableAttr(Attribute::GENERIC); extendDecl.generic = ParseGeneric(); } extendDecl.extendedType = ParseType(); if (extendDecl.extendedType->astKind == ASTKind::PAREN_TYPE) { DiagUnexpectedTypeIn(*extendDecl.extendedType.get(), extendDecl.keywordPos, "extend", "Extend declaration cannot extend paren type"); } } void ParserImpl::ParseExtendBody(ExtendDecl& ed) { ed.bodyScope = MakeOwned(); if (!Skip(TokenKind::LCURL)) { DiagExpectCharacter("'{'", "extend declaration must have body"); // Do not need to do the folowing parsing ed.EnableAttr(Attribute::HAS_BROKEN); return; } else { ed.leftCurlPos = lookahead.Begin(); ed.bodyScope->begin = lookahead.Begin(); } SkipBlank(TokenKind::SEMI); // Parse extend members. while (true) { SkipBlank(TokenKind::SEMI); if (Skip(TokenKind::RCURL)) { ed.rightCurlPos = lastToken.Begin(); break; } if (DetectPrematureEnd()) { ed.EnableAttr(Attribute::IS_BROKEN); DiagExpectedRightDelimiter("{", ed.leftCurlPos); break; } auto decl = ParseDecl(ScopeKind::EXTEND_BODY); if (decl->IsInvalid()) { continue; } SetMemberParentInheritableDecl(ed, decl); ed.members.emplace_back(std::move(decl)); } } OwnedPtr ParserImpl::ParseExprOrDecl(ScopeKind sk) { if (SeeingKeywordAndOperater()) { return ParseExpr(); } if (SeeingMacroCall()) { if (curMacroCall || enableCustomAnno) { if (SeeingIfAvailable()) { return ParseIfAvailable(); } return ParseDecl(sk); } OwnedPtr expr = ParseMacroCall(sk); return ParseExpr(Token{TokenKind::DOT}, std::move(expr)); } else if (SeeingMacroCallDecl()) { return ParseMacroCall(sk); } else if (SeeingDecl()) { return ParseDecl(sk); } else if (SeeingExpr()) { return ParseExpr(); } return MakeInvalid(lookahead.Begin()); } /** * expressionOrDeclarations * : end* (expressionOrDeclaration (end+ expressionOrDeclaration?)*)? * ; * */ OwnedPtr ParserImpl::ParseExpressionOrDeclarations(ScopeKind sk) { OwnedPtr result = MakeOwned(); result->begin = lastToken.Begin(); result->leftCurlPos = lastToken.Begin(); ChainScope cs(*this, result.get()); SkipBlank(TokenKind::SEMI); for (;;) { bool hasNLorSEMI = SkipNLOrSemi(); if (Skip(TokenKind::RCURL)) { result->rightCurlPos = lastToken.Begin(); break; } if (DetectPrematureEnd()) { Position resPos = result->begin; // The distance from chainedAST.end() to LambdaExpr in ChainScope is 3. const static ptrdiff_t toLambdaLen = 3; if (chainedAST.size() >= toLambdaLen && (*(chainedAST.end() - toLambdaLen))->astKind == ASTKind::LAMBDA_EXPR) { auto le = StaticAs(*(chainedAST.end() - toLambdaLen)); resPos = le->begin; } DiagExpectedRightDelimiter("{", resPos); break; } if (!result->body.empty() && !hasNLorSEMI) { if (DiagForBlock(*result)) { result->end = lookahead.End(); return result; } ConsumeUntilDeclOrNL(TokenKind::RCURL); SkipBlank(TokenKind::NL); } if (SeeingMacroCallDecl() || SeeingDecl() || SeeingExpr()) { auto node = ParseExprOrDecl(sk); result->body.emplace_back(std::move(node)); } else { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_expr_or_decl_in, lookahead, ConvertToken(lookahead)); ConsumeUntilAny({TokenKind::RCURL, TokenKind::NL}, false); } } if (Seeing(TokenKind::SEMI)) { result->hasSemi = true; result->semiPos = lookahead.Begin(); } result->end = lastToken.End(); return result; } bool ParserImpl::DiagForBlock(const Block& block) { // Close the block. if (SeeingAny({TokenKind::RPAREN, TokenKind::RSQUARE, TokenKind::GT})) { if (CanMatchBracketInStack()) { DiagExpectedRightDelimiter("{", block.begin); return true; // early return. } else { ParseDiagnoseRefactor(DiagKindRefactor::parse_unmatched_right_delimiter, lookahead, lookahead.Value()); } } else { DiagExpectSemiOrNewline(); } return false; } OwnedPtr ParserImpl::ParseBlock(ScopeKind scopeKind) { if (!Skip(TokenKind::LCURL)) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_left_brace, lookahead, ConvertToken(lookahead)); return MakeInvalid(lookahead.Begin()); } return ParseExpressionOrDeclarations(scopeKind); } DiagnosticEngine& ParserImpl::GetDiagnosticEngine() const { return this->diag; } std::string ParserImpl::GetPrimaryDeclIdentRawValue() const { return curPrimaryDeclIdentIsRaw ? "`" + curPrimaryDeclIdent + "`" : curPrimaryDeclIdent; } void ParserImpl::SetPrimaryDecl(const std::string& decl, bool isRawId) { lastPrimaryDeclIdent = curPrimaryDeclIdent; lastPrimaryDeclIdentIsRaw = curPrimaryDeclIdentIsRaw; curPrimaryDeclIdent = decl; curPrimaryDeclIdentIsRaw = isRawId; } cangjie_compiler-1.0.7/src/Parse/ParserDiag.cpp000066400000000000000000001667751510705540100214230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Parser. */ #include #include "ParserImpl.h" #include "cangjie/AST/Match.h" namespace Cangjie { using namespace AST; static inline bool IsKeyWord(const Token& t) { return ((t.kind >= TokenKind::INT8 && t.kind < TokenKind::IDENTIFIER) && t.kind != TokenKind::DOLLAR && t.kind != TokenKind::UPPERBOUND) || t.kind == TokenKind::AS || t.kind == TokenKind::IS; } std::string ConvertToken(const Token& t) { if (t.kind == TokenKind::END) { return "''"; } if (t.kind == TokenKind::NL) { return "''"; } if (IsKeyWord(t)) { return std::string{"keyword '"} + TOKENS[static_cast(t.kind)] + "'"; } if (t.kind == TokenKind::INTEGER_LITERAL || t.kind == TokenKind::FLOAT_LITERAL) { return std::string{"literal '"} + t.Value() + "'"; } if (Utils::In(t.kind, {TokenKind::BOOL_LITERAL, TokenKind::IDENTIFIER, TokenKind::PACKAGE_IDENTIFIER})) { return std::string{"'"} + t.Value() + "'"; } return std::string{"'"} + TOKENS[static_cast(t.kind)] + "'"; } static Ptr TraceBackNode(std::vector>& chainAST, std::vector kinds) { auto iter = std::find_if(chainAST.rbegin(), chainAST.rend(), [&kinds](auto node) { return std::find(kinds.begin(), kinds.end(), node->astKind) != kinds.end(); }); if (iter != chainAST.rend()) { return *iter; } return nullptr; } void ParserImpl::DiagExpectedIdentifierClassDecl(Ptr node) { DiagExpectedIdentifier( MakeRange(node->begin, node->begin + std::string("class").size()), "a class name", "after keyword 'class'"); } void ParserImpl::DiagExpectedIdentifierInterfaceDecl(Ptr node) { DiagExpectedIdentifier(MakeRange(node->begin, node->begin + std::string("interface").size()), "a interface name", "after keyword 'interface'"); } void ParserImpl::DiagExpectedIdentifierStructDecl(Ptr node) { DiagExpectedIdentifier( MakeRange(node->begin, node->begin + std::string("struct").size()), "a struct name", "after keyword 'struct'"); } void ParserImpl::DiagExpectedIdentifierTypeAliasDecl(Ptr node) { DiagExpectedIdentifier( MakeRange(node->begin, node->begin + std::string("type").size()), "a type name", "after keyword 'type'"); } void ParserImpl::DiagExpectedIdentifierMacroExpandDecl(Ptr node) { auto med = StaticAs(node); auto range = MakeRange( med->invocation.fullNameDotPos.back(), med->invocation.fullNameDotPos.back() + std::string(".").size()); DiagExpectedIdentifier(range, "a type name", "after '.' in qualified name"); } void ParserImpl::DiagExpectedIdentifierMacroExpandExpr(Ptr node) { auto mee = StaticAs(node); auto range = MakeRange( mee->invocation.fullNameDotPos.back(), mee->invocation.fullNameDotPos.back() + std::string(".").size()); DiagExpectedIdentifier(range, "a type name", "after '.' in qualified name"); } void ParserImpl::DiagExpectedIdentifierFuncDecl(Ptr node) { DiagExpectedIdentifier( MakeRange(node->begin, node->begin + std::string("func").size()), "a func name", "after keyword 'func'"); } void ParserImpl::DiagExpectedIdentifierFuncBody(Ptr node) { auto fb = StaticAs(node); DiagExpectedIdentifier(MakeRange(fb->colonPos, fb->colonPos + std::string(":").size()), "a type name", "after ':' in function declaration"); } void ParserImpl::DiagExpectedIdentifierMacroDecl(Ptr node) { DiagExpectedIdentifier( MakeRange(node->begin, node->begin + std::string("macro").size()), "a macro name", "after keyword 'macro'"); } void ParserImpl::DiagExpectedIdentifierMemberAccess(Ptr node) { auto ma = StaticAs(node); DiagExpectedIdentifier( MakeRange(ma->dotPos, ma->dotPos + std::string(".").size()), "a member name", "after '.' in qualified name"); } void ParserImpl::DiagExpectedIdentifierQualifiedType(Ptr node) { auto qt = StaticAs(node); DiagExpectedIdentifier( MakeRange(qt->dotPos, qt->dotPos + std::string(".").size()), "a type name", "after '.' in qualified name"); } void ParserImpl::DiagExpectedIdentifierFuncParam(Ptr) { // Get func param list node. auto pNode = TraceBackNode(chainedAST, {ASTKind::FUNC_PARAM_LIST}); if (!pNode) { DiagExpectedIdentifier(MakeRange(lastToken.Begin(), lastToken.End())); return; } auto fpl = StaticAs(pNode); if (fpl->params.empty()) { DiagExpectedIdentifier(MakeRange(fpl->leftParenPos, fpl->leftParenPos + std::string("(").size()), "a argument name", "after '(' in parameter list"); } else if (!fpl->params.back()->commaPos.IsZero()) { DiagExpectedIdentifier( MakeRange(fpl->params.back()->commaPos, fpl->params.back()->commaPos + std::string("(").size()), "a argument name", "after ',' in parameter list"); } else { DiagExpectedIdentifier(MakeRange(lastToken.Begin(), lastToken.End())); } } void ParserImpl::DiagExpectedIdentifierRefType(Ptr) { const size_t toGenericLen = 2; if (chainedAST.size() <= toGenericLen) { DiagExpectedIdentifier(MakeRange(lastToken.Begin(), lastToken.End())); return; } auto iter = chainedAST.rbegin(); std::advance(iter, 1); if ((*iter)->astKind != ASTKind::GENERIC_CONSTRAINT) { DiagExpectedIdentifier(MakeRange(lastToken.Begin(), lastToken.End())); return; } auto gc = StaticAs(*iter); if (!gc->wherePos.IsZero()) { DiagExpectedIdentifier(MakeRange(gc->wherePos, gc->wherePos + std::string("where").size()), "a generic type name", "after 'where' in generic constraint"); } else if (lastToken == "," && !lastToken.Begin().IsZero()) { DiagExpectedIdentifier(MakeRange(lastToken.Begin(), lastToken.Begin() + std::string(",").size()), "a generic type name", "after ',' in generic constraint"); } else { CJC_ABORT(); } } void ParserImpl::DiagExpectedIdentifierPropDecl(Ptr node) { auto pd = StaticAs(node); DiagExpectedIdentifier(MakeRange(pd->keywordPos, pd->keywordPos + std::string("prop").size()), "a property name", "after keyword 'prop' in property definition"); } void ParserImpl::DiagExpectedIdentifierPackageSpec(Ptr node) { auto ps = StaticAs(node); if (ps->prefixDotPoses.empty()) { DiagExpectedIdentifier(MakeRange(ps->packagePos, ps->packagePos + std::string("package").size()), "a package name", "after keyword 'package'"); } else { DiagExpectedIdentifier( MakeRange(ps->prefixDotPoses.back(), ps->prefixDotPoses.back() + std::string(".").size()), "a package name", "after '.' in qualified name"); } } void ParserImpl::DiagExpectedIdentifierImportContent(Ptr node) { auto ic = StaticAs(node); if (lastToken.kind == TokenKind::IMPORT) { DiagExpectedIdentifier( MakeRange(lastToken.Begin(), lastToken.End()), "a package name", "after keyword 'import'"); } else if (!ic->prefixDotPoses.empty()) { DiagExpectedIdentifier( MakeRange(ic->prefixDotPoses.back(), ic->prefixDotPoses.back() + std::string(".").size()), "a '*' or identifier", "after '.' in qualified name"); } else { DiagExpectedIdentifier(MakeRange(lastToken.Begin(), lastToken.End())); } } void ParserImpl::DiagExpectedIdentifierImportSpec(Ptr node) { auto is = StaticAs(node); DiagExpectedIdentifier(MakeRange(is->importPos, is->importPos + std::string("import").size()), "a package name", "after keyword 'import'"); } void ParserImpl::DiagExpectedIdentifierFeatureDirective(Ptr node) { auto fs = StaticAs(node); if (fs->content.empty()) { if (lastToken.kind == TokenKind::DOT) { std::swap(lastToken, lookahead); } DiagExpectedIdentifier(MakeRange(fs->begin, fs->begin + std::string("features").size()), "a feature name", "after keyword 'features'", false); } else if (IsRawIdentifier(lastToken.Value())) { auto prevRange = fs->content.size() == 1 ? MakeRange(fs->begin, fs->begin + std::string("features").size()) : MakeRange(fs->content[fs->content.size() - 2].begin, fs->content[fs->content.size() - 2].end); std::swap(lastToken, lookahead); DiagExpectedIdentifier(prevRange, "an identifier", "after this", false); std::swap(lastToken, lookahead); } else if (lastToken.kind == TokenKind::COMMA) { std::stringstream ss; ss << "after '" << fs->content[fs->content.size() - 1].ToString() << lastToken.Value() << "'"; DiagExpectedIdentifier(MakeRange(fs->content[fs->content.size() - 1].begin, lastToken.End()), "an identifier", ss.str(), false); } else if (lastToken.kind == TokenKind::DOT) { DiagExpectedIdentifier(MakeRange(lastToken.Begin(), lastToken.End()), "an identifier", "after '.' in qualified name", false); } else { DiagExpectedIdentifier(MakeRange(fs->content[fs->content.size() - 1].begin, lastToken.End()), "';' or ''", "after this", false); } } void ParserImpl::DiagExpectedIdentifierGenericConstraint(Ptr node) { auto gc = StaticAs(node); if (gc->bitAndPos.empty()) { DiagExpectedIdentifier(MakeRange(gc->operatorPos, gc->operatorPos + std::string("<:").size()), "a type name", "after '<:' in generic constraint"); } else { DiagExpectedIdentifier(MakeRange(gc->bitAndPos.back(), gc->bitAndPos.back() + std::string("&").size()), "a type name", "after '&' in generic constraint"); } } void ParserImpl::DiagExpectedIdentifierGeneric(Ptr node) { auto ge = StaticAs(node); if (ge->typeParameters.empty()) { DiagExpectedIdentifier(MakeRange(ge->leftAnglePos, ge->leftAnglePos + std::string("<").size()), "a generic type name", "after '<' in generic"); } else { auto range = MakeRange( ge->typeParameters.back()->commaPos, ge->typeParameters.back()->commaPos + std::string(",").size()); DiagExpectedIdentifier(range, "a generic type name", "after ',' in generic"); } } void ParserImpl::DiagExpectedIdentifierEnumDecl(Ptr node) { auto ed = StaticAs(node); if (ed->identifier.ZeroPos()) { DiagExpectedIdentifier( MakeRange(ed->begin, ed->begin + std::string("enum").size()), "a enum name", "after keyword 'enum'"); } else if (ed->constructors.empty()) { if (lastToken.kind == TokenKind::ELLIPSIS) { auto db = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_name, lastToken, "an enum name", "at beginning of enum body", ConvertToken(lastToken)); db.AddMainHintArguments("an enum constructor name"); db.AddNote("ellipse cannot be the only constructor of an enum decl"); return; } DiagExpectedIdentifier(MakeRange(ed->leftCurlPos, ed->leftCurlPos + std::string("{").size()), "a enum value name", "at beginning of enum body"); } else if (!ed->bitOrPosVector.empty()) { DiagExpectedIdentifier( MakeRange(ed->bitOrPosVector.back(), ed->bitOrPosVector.back() + std::string("|").size()), "a enum value name", "after '|' in enum body"); } else { CJC_ABORT(); } } void ParserImpl::DiagExpectedIdentifier( const Range& range, const std::string& expectedName, const std::string& afterName, const bool callHelp) { auto foundName = ConvertToken(lookahead); auto expectedMsg = expectedName; auto otherHintMsg = afterName; auto otherHintRange = range; if (lookahead.kind == TokenKind::WILDCARD) { auto ahead = lexer->LookAheadSkipNL(1); if (!ahead.empty()) { if (lookahead.Begin().line == ahead.front().Begin().line && lookahead.Begin().column + 1 == ahead.front().Begin().column) { expectedMsg = "identifier"; otherHintMsg = "expected a Unicode XID_Continue after '_'"; otherHintRange = MakeRange(ahead.front().Begin(), ahead.front().Begin() + 1); } } } auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_name, lookahead, expectedMsg, afterName, foundName); builder.AddMainHintArguments(expectedMsg); builder.AddHint(otherHintRange, otherHintMsg); if (IsKeyWord(lookahead) && callHelp) { auto helpMes = std::string("you could escape keyword as ") + expectedName + " using '`'"; auto help = DiagHelp(helpMes); help.AddSubstitution(lookahead, "`" + lookahead.Value() + "`"); builder.AddHelp(help); } } void ParserImpl::DiagNameLengthOverflow(const Range& r, const std::string& tar, size_t maxLength, size_t realLength) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_package_name_length_overflow, r, tar); auto note = SubDiagnostic( "the maximum length is " + std::to_string(maxLength) + ", the real length is " + std::to_string(realLength)); builder.AddNote(note); } static const std::unordered_map> KIND_TO_STR = { {ASTKind::VAR_DECL, {"var", "variable declaration"}}, {ASTKind::VAR_WITH_PATTERN_DECL, {"var", "variable declaration"}}, {ASTKind::FUNC_DECL, {"func", "function declaration"}}, {ASTKind::MAIN_DECL, {"main", "main function"}}, {ASTKind::MACRO_DECL, {"macro", "macro declaration"}}, {ASTKind::CLASS_DECL, {"class", "class declaration"}}, {ASTKind::EXTEND_DECL, {"extend", "extend declaration"}}, {ASTKind::INTERFACE_DECL, {"interface", "interface declaration"}}, {ASTKind::STRUCT_DECL, {"struct", "struct declaration"}}, {ASTKind::ENUM_DECL, {"enum", "enum declaration"}}, {ASTKind::TYPE_ALIAS_DECL, {"type", "type alias declaration"}}, {ASTKind::PROP_DECL, {"prop", "property declaration"}}, {ASTKind::INTERPOLATION_EXPR, {"interpolation", "string interpolation"}}, {ASTKind::PACKAGE_SPEC, {"package", "package declaration"}}, {ASTKind::FEATURES_DIRECTIVE, {"features", "features declarations"}}, }; static const std::unordered_map> SCOPE_TO_DECL = { {ScopeKind::TOPLEVEL, {"", "'top-level' scope"}}, {ScopeKind::MACRO_BODY, {"macro", "macro body"}}, {ScopeKind::CLASS_BODY, {"class", "class body"}}, {ScopeKind::EXTEND_BODY, {"extend", "extend body"}}, {ScopeKind::INTERFACE_BODY, {"interface", "interface body"}}, {ScopeKind::STRUCT_BODY, {"struct", "struct body"}}, {ScopeKind::ENUM_BODY, {"enum", "enum body"}}, {ScopeKind::ENUM_CONSTRUCTOR, {"enum", "enum body"}}, {ScopeKind::FUNC_BODY, {"func", "function body"}}, {ScopeKind::PROP_MEMBER_SETTER_BODY, {"setter", "setter body"}}, {ScopeKind::PROP_MEMBER_GETTER_BODY, {"getter", "getter body"}}, }; static std::pair GetSignature(Node& node) { if (node.astKind == ASTKind::FUNC_DECL && node.TestAttr(Attribute::CONSTRUCTOR)) { return {MakeRange(node.begin, node.begin + std::string("init").size()), "constructor"}; } if (node.astKind == ASTKind::FUNC_DECL && node.TestAttr(Attribute::FINALIZER)) { return {MakeRange(node.begin, node.begin + std::string("~init").size()), "finalizer"}; } if (KIND_TO_STR.count(node.astKind) > 0) { if (node.astKind == ASTKind::VAR_DECL) { auto vd = StaticAs(&node); if (vd && vd->isConst) { return {MakeRange(node.begin, node.begin + std::string("const").size()), KIND_TO_STR.at(node.astKind).second}; } } return {MakeRange(node.begin, node.begin + KIND_TO_STR.at(node.astKind).first.size()), KIND_TO_STR.at(node.astKind).second}; } if (node.astKind == ASTKind::PRIMARY_CTOR_DECL) { auto pcd = StaticAs(&node); return {MakeRange(node.begin, node.begin + pcd->identifier.Length()), "primary constructor"}; } CJC_ABORT(); return {MakeRange(DEFAULT_POSITION, node.begin + DEFAULT_POSITION), "declaration"}; } // If if is only block node like `unsafe {..}`, which return a block. Otherwise return function and primary constructor // or other thing. static std::pair> IsOnlyBlockNode(std::vector>& chain) { if (chain.size() <= 1) { return {false, nullptr}; } std::vector> targetNodes; for (auto begin = chain.rbegin() + 1; begin != chain.rend(); ++begin) { if (KIND_TO_STR.count((*begin)->astKind) > 0 || (*begin)->astKind == ASTKind::PRIMARY_CTOR_DECL || (*begin)->astKind == ASTKind::BLOCK) { targetNodes.push_back(*begin); } } if (targetNodes.empty()) { return {false, nullptr}; } if (targetNodes[0]->astKind == ASTKind::BLOCK && targetNodes.size() > 1) { if (targetNodes[1]->astKind != ASTKind::FUNC_DECL && targetNodes[1]->astKind != ASTKind::PRIMARY_CTOR_DECL && targetNodes[1]->astKind != ASTKind::MAIN_DECL && targetNodes[1]->astKind != ASTKind::MACRO_DECL && targetNodes[1]->astKind != ASTKind::INTERPOLATION_EXPR) { return {true, targetNodes[0]}; } else { return {false, targetNodes[1]}; } } return {false, targetNodes[0]}; } static std::pair> TraceScopeInChain(std::vector> chain) { auto nodePair = IsOnlyBlockNode(chain); if (nodePair.second == nullptr) { return {MakeRange(DEFAULT_POSITION, DEFAULT_POSITION), {"'top-level' scope", "'top-level' scope"}}; } if (nodePair.first || nodePair.second->astKind == ASTKind::BLOCK) { auto block = StaticAs(nodePair.second); return {MakeRange(block->leftCurlPos, block->leftCurlPos + std::string{"{"}.size()), {"block", "block"}}; } auto tar = nodePair.second; if (tar->astKind == ASTKind::PRIMARY_CTOR_DECL) { auto pcd = StaticAs(tar); return {MakeRange(pcd->identifier), {"primary constructor", "primary constructor"}}; } if (tar->astKind == ASTKind::INTERPOLATION_EXPR) { return {MakeRange(tar->begin, "${"), {"string interpolation", "string interpolation"}}; } if (tar->astKind == ASTKind::FUNC_DECL) { auto fd = StaticAs(tar); if (fd->isGetter) { return { MakeRange(tar->begin, tar->begin + std::string("get").size()), std::make_pair("getter", "getter body")}; } if (fd->isSetter) { return { MakeRange(tar->begin, tar->begin + std::string("set").size()), std::make_pair("setter", "setter body")}; } } if (KIND_TO_STR.count(tar->astKind) > 0) { auto iden = KIND_TO_STR.at(tar->astKind).first; auto end = tar->begin + iden.size(); iden = iden == "func" ? "function" : iden; iden = iden == "main" ? "main function" : iden; return {MakeRange(tar->begin, end), {iden, iden + " body"}}; } return {MakeRange(DEFAULT_POSITION, DEFAULT_POSITION), {"'top-level' scope", "'top-level' scope"}}; } void ParserImpl::DiagIllegalModifierInScope(const AST::Modifier& mod) { CJC_ASSERT(!chainedAST.empty()); auto node = chainedAST.back(); auto scopeInfo = TraceScopeInChain(chainedAST); std::pair hintPos{}; std::string hintMes{}; bool needSecondHint{false}; if (node->astKind == ASTKind::FUNC_PARAM || node->astKind == ASTKind::VAR_DECL || node->astKind == ASTKind::FUNC_DECL || node->astKind == ASTKind::PROP_DECL) { needSecondHint = true; } if (KIND_TO_STR.count(node->astKind) > 0) { hintPos.first = node->begin; if (node->astKind == ASTKind::PACKAGE_SPEC) { hintPos.first = StaticAs(node)->packagePos; } hintPos.second = hintPos.first + KIND_TO_STR.at(node->astKind).first.size(); if (node->TestAttr(Attribute::CONSTRUCTOR)) { hintMes = "constructor"; } else if (node->TestAttr(Attribute::FINALIZER)) { hintMes = "finalizer"; } else { hintMes = KIND_TO_STR.at(node->astKind).second; } } else if (node->astKind == ASTKind::FUNC_PARAM) { hintPos.first = node->begin; hintPos.second = node->begin + std::string("var").size(); hintMes = "variable declaration"; } else if (node->astKind == ASTKind::PRIMARY_CTOR_DECL) { auto pcd = StaticAs(node); hintPos.first = pcd->identifier.Begin(); hintPos.second = pcd->identifier.End(); hintMes = "primary constructor"; } else { CJC_ABORT(); } auto modStr = TOKENS[static_cast(mod.modifier)]; auto secondAgr = needSecondHint ? " in " + scopeInfo.second.second : ""; auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_illegal_modifier_in_scope, mod, modStr, hintMes, secondAgr); if (scopeInfo.first.IsDefault() && needSecondHint) { hintMes += " in " + scopeInfo.second.first; } builder.AddHint(MakeRange(hintPos.first, hintPos.second), hintMes); if (!scopeInfo.first.IsDefault()) { builder.AddHint(scopeInfo.first, scopeInfo.second.first); } } void ParserImpl::DiagConflictedModifier(const Modifier& resMod, const Modifier& tarMod) { if (chainedAST.empty()) { InternalError("The chainAST is empty in DiagConflictedModifier"); return; } auto node = chainedAST.back(); auto resStr = std::string(TOKENS[static_cast(resMod.modifier)]); auto tarStr = std::string(TOKENS[static_cast(tarMod.modifier)]); auto resRange = MakeRange(resMod.begin, resMod.begin + resStr.size()); auto tarRange = MakeRange(tarMod.begin, tarMod.begin + tarStr.size()); std::string hintN1; std::string hintN2; if (KIND_TO_STR.count(node->astKind) != 0) { hintN1 = KIND_TO_STR.at(node->astKind).first; hintN2 = KIND_TO_STR.at(node->astKind).second; } else if (node->astKind == ASTKind::PRIMARY_CTOR_DECL) { auto pcd = StaticAs(node); hintN1 = pcd->identifier; hintN2 = "constructor"; } else if (node->astKind == ASTKind::FUNC_PARAM) { hintN1 = "var"; hintN2 = "variable declaration"; } else { CJC_ABORT(); // Unexpect } auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_conflict_modifier, tarRange, tarStr, resStr, hintN2); builder.AddHint(resRange); if (node->IsDecl()) { auto decl = RawStaticCast(node); if (decl->astKind == ASTKind::VAR_DECL && decl->IsConst()) { builder.AddHint(MakeRange(decl->identifier), hintN2); } else { builder.AddHint(MakeRange(decl->keywordPos, decl->keywordPos + hintN1.size()), hintN2); } } else if (node->astKind == ASTKind::PACKAGE_SPEC) { auto pkgPos = StaticAs(node)->packagePos; builder.AddHint(MakeRange(pkgPos, pkgPos + hintN1.size()), hintN2); } else { builder.AddHint(MakeRange(node->begin, node->begin + hintN1.size()), hintN2); } } std::string ParserImpl::GetSingleLineContent(const Position& begin, const Position& end) const { if (begin.line == end.line) { return sourceManager.GetContentBetween(begin.fileID, begin, end); } else { return sourceManager.GetContentBetween( begin.fileID, begin, Position(begin.line, std::numeric_limits::max())); } } void ParserImpl::DiagExpectNoModifierBefore(const Modifier& mod, const std::string& str) { std::string modStr = TOKEN_KIND_VALUES[static_cast(mod.modifier)]; auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_no_modifier, MakeRange(mod.begin, mod.end), str, modStr); auto tokens = lexer->LookAheadSkipNL(1); builder.AddHint(MakeRange(lookahead.Begin(), tokens.begin()->End()), str); } void ParserImpl::DiagUnexpectedAnnoOn( const Annotation& anno, const Position& onPos, const std::string& annoStr, const std::string& onStr) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_unexpected_anno_on, anno, "@" + annoStr, onStr); builder.AddHint(MakeRange(onPos, onStr), onStr); } void ParserImpl::DiagUnexpectedAnnoOnKind( const Annotation& anno, const Position& onPos, const std::string& annoStr, ASTKind kind) { CJC_ASSERT(KIND_TO_STR.count(kind) != 0); auto keyword = KIND_TO_STR.at(kind).first; DiagUnexpectedAnnoOn(anno, onPos, annoStr, keyword); } void ParserImpl::DiagUnexpectedWhenON(PtrVector& annos) { auto it = std::find_if(annos.begin(), annos.end(), [](auto& anno) { return anno->kind == AnnotationKind::WHEN; }); CJC_ASSERT(it != annos.end()); DiagUnexpectedAnnoOn(**it, lookahead.Begin(), it->get()->identifier, lookahead.Value()); } void PrepareExpectNoModifierArg(const Node& node, std::string& arg, Range& range) { switch (node.astKind) { case ASTKind::EXTEND_DECL: { arg = "extend declaration"; range = MakeRange(node.begin, node.begin + std::string("extend").size()); break; } case ASTKind::FUNC_DECL: { auto fd = StaticAs(&node); if (fd->isGetter) { arg = "getter function"; range = MakeRange(node.begin, node.begin + std::string("get").size()); } else if (fd->isSetter) { arg = "setter function"; range = MakeRange(node.begin, node.begin + std::string("set").size()); } else if (fd->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { arg = "enum constructor"; range = MakeRange(fd->identifier); } else { CJC_ABORT(); } break; } case ASTKind::FUNC_PARAM: { arg = "non-member variable parameter"; range = MakeRange(node.begin, node.end); break; } case ASTKind::VAR_DECL: { auto vd = StaticAs(&node); if (vd->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { arg = "enum constructor"; range = MakeRange(vd->identifier); } else { CJC_ABORT(); } break; } default: { break; } } } void ParserImpl::DiagExpectNoModifier(const Modifier& mod) { CJC_ASSERT(!chainedAST.empty()); auto node = chainedAST.back(); std::string arg1{}; auto range = MakeRange(DEFAULT_POSITION, DEFAULT_POSITION); PrepareExpectNoModifierArg(*node, arg1, range); auto modStr = mod.ToString(); auto modRange = MakeRange(mod.begin, mod.begin + modStr.size()); auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_no_modifier, modRange, arg1, modStr); builder.AddHint(range, arg1); if (Is(node)) { auto content = GetSingleLineContent(node->begin, node->end); auto help = DiagHelp("if you want an immutable member variable, use 'let'"); help.AddSubstitution(MakeRange(node->begin, node->end), "let " + content); builder.AddHelp(help); help = DiagHelp("or 'var' to get a mutable member variable"); help.AddSubstitution(MakeRange(node->begin, node->end), "var " + content); builder.AddHelp(help); } } void ParserImpl::DiagUnexpectedDeclInScope(ScopeKind sk) { CJC_ASSERT(!chainedAST.empty() && "The chainAST is empty in DiagIllegalModifierInScope"); auto node = chainedAST.back(); auto tar = GetSignature(*node); auto res = TraceScopeInChain(chainedAST); std::pair arg; // It can not trace the scope if the parser is from macro replace. if (res.first.IsDefault() && sk != ScopeKind::TOPLEVEL) { CJC_ASSERT(SCOPE_TO_DECL.find(sk) != SCOPE_TO_DECL.end()); arg = SCOPE_TO_DECL.at(sk); } else { arg = res.second; } auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_unexpected_declaration_in_scope, tar.first, tar.second, arg.second); if (!res.first.IsDefault()) { builder.AddHint(res.first, arg.first); builder.AddMainHintArguments(tar.second); } else { builder.AddMainHintArguments(tar.second + " in " + arg.second); } node->EnableAttr(Attribute::IS_BROKEN); } void ParserImpl::DiagExpectedMoreFieldInTuplePattern() { if (chainedAST.empty() || chainedAST.back()->astKind != ASTKind::TUPLE_PATTERN) { InternalError("The chainAST is empty in DiagExpectedMoreFieldInTuplePattern"); return; } auto tp = StaticAs(chainedAST.back()); ParseDiagnoseRefactor( DiagKindRefactor::parse_tuple_pattern_expected_more_field, MakeRange(tp->leftBracePos, tp->rightBracePos + 1)); } void ParserImpl::DiagExpectedTypeNameAfterAs(const Token& tok) { if (tok.kind != TokenKind::AS && tok.kind != TokenKind::IS) { return; } auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_type, lookahead, ConvertToken(tok), ConvertToken(lookahead)); builder.AddHint(tok, ConvertToken(tok)); } void ParserImpl::DiagExpectedTypeName() { auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_type, lookahead, ConvertToken(lastToken), ConvertToken(lookahead)); builder.AddHint(lastToken, ConvertToken(lastToken)); } void ParserImpl::DiagInvalidLeftHandExpr(const Expr& expr, const Token& tok) { if (expr.TestAttr(Attribute::IS_BROKEN)) { return; } auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_invalid_left_hand_expr, tok, tok.Value()); builder.AddHint(expr); } const std::unordered_map NONE_ASSO_OP = { {TokenKind::ASSIGN, "assignment"}, // Means assignment category, including '=', '**=', '*=' etc. {TokenKind::RANGEOP, "range"}, // '..', '..='. {TokenKind::EQUAL, "equality"}, // '==', '!='. {TokenKind::LT, "comparison"} // '<', '<=', '>', 'is', 'as' etc. }; bool ParserImpl::IsNoneAssociative(const Token& tok) const { return std::any_of(NONE_ASSO_OP.begin(), NONE_ASSO_OP.end(), [&tok, this](auto& kind) { return Precedence(kind.first) == Precedence(tok.kind); }); } void ParserImpl::DiagNoneAssociativeOp(const Token& preT, const Token& tok) { auto iter = std::find_if(NONE_ASSO_OP.begin(), NONE_ASSO_OP.end(), [&tok, this](auto& asso) { return Precedence(tok.kind) == Precedence(asso.first); }); CJC_ASSERT(iter != NONE_ASSO_OP.end()); auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_chained_none_associative, tok, std::string{iter->second}); builder.AddHint(preT); } void ParserImpl::DiagInvalidInheritType(const Type& type) { auto typeName = sourceManager.GetContentBetween(type.begin.fileID, type.begin, type.end); auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_invalid_super_declaration, type, typeName); builder.AddNote("only class types and interface types can be subtyped"); } void ParserImpl::DiagExpectedExpression() { ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_expression, lookahead, ConvertToken(lastToken), ConvertToken(lookahead)); } void ParserImpl::DiagExpectedRightDelimiter(const std::string& del, const Position& pos) { static std::map delMap = {{"{", "}"}, {"(", ")"}, {"[", "]"}, {"<", ">"}}; auto builder = lookahead.kind == TokenKind::NL ? ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_right_delimiter, lastToken, del) : ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_right_delimiter, lastToken.End(), del); builder.AddMainHintArguments(delMap[del]); builder.AddHint(pos, del); } void ParserImpl::DiagInvalidIncreExpr(const Expr& expr) { auto name = lookahead.kind == TokenKind::INCR ? "increment" : "decrement"; auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_invalid_incre_expr, lookahead, name); builder.AddHint(expr); } void ParserImpl::DiagInvalidMacroExpandExpr(const Token& tok, const MacroExpandExpr& expr) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_expression, *expr.invocation.decl, ConvertToken(tok), std::string{"a declaration"}); builder.AddHint(tok); } void ParserImpl::DiagUnrecognizedNodeAfterMacro(const Token& tok, const MacroExpandExpr& expr) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_unrecognized_token_after_macro_node, tok, ConvertToken(tok)); builder.AddHint(*expr.invocation.decl); } void ParserImpl::DiagChainedAsExpr(Expr& expr, const Token& tok) { Position pos; if (expr.astKind == ASTKind::AS_EXPR) { auto isE = StaticAs(&expr); pos = isE->asPos; } if (expr.astKind == ASTKind::IS_EXPR) { auto isE = StaticAs(&expr); pos = isE->isPos; } auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_chained_none_associative, tok, std::string{"`is` and `as`"}); builder.AddHint(MakeRange(pos, pos + std::string("is").size())); } void ParserImpl::DiagExpecetedOpeOrEnd() { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_operator_or_end, lookahead, ConvertToken(lookahead)); } void ParserImpl::DiagExpectedNoNewLine() { auto old = skipNL; skipNL = false; Next(); // Skip '-'/'!' operator. auto newline = Peek(); auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_no_newline_after, newline, ConvertToken(lastToken)); builder.AddHint(lastToken, ConvertToken(lastToken)); skipNL = old; } void ParserImpl::DiagCannotHaveAssignmentInInit(const Expr& expr) { ParseDiagnoseRefactor(DiagKindRefactor::parse_cannot_have_assi_in_init, expr); } void ParserImpl::DiagOrPattern() { ParseDiagnoseRefactor(DiagKindRefactor::parse_illegal_or_pattern, lookahead.Begin()); } void ParserImpl::DiagDeclarationInMacroPackage(const OwnedPtr& decl) { CJC_ASSERT(KIND_TO_STR.count(decl->astKind) != 0); auto typeKeyword = KIND_TO_STR.at(decl->astKind).first; auto publicModifier = std::find_if(decl->modifiers.begin(), decl->modifiers.end(), [](const auto& mod) { return mod.modifier == TokenKind::PUBLIC; }); CJC_ASSERT(publicModifier != decl->modifiers.end()); auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_macro_decl_in_macro_package, MakeRange(publicModifier->begin, publicModifier->begin + std::string("public").size()), typeKeyword); builder.AddHint( MakeRange(currentFile->package->macroPos, currentFile->package->packagePos + std::string("package").size())); SubDiagnostic note{ "Declarations in 'macro package' cannot be accessed in other packages, except macro declarations."}; builder.AddNote(note); } void ParserImpl::DiagExpectPublicBeforeMacroCall(const OwnedPtr& md) { ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_public_before_macro_decl, MakeRange(md->keywordPos, md->keywordPos + std::string("macro").size())); } void ParserImpl::DiagExpectMacroParamType(const Type& type) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_macro_illegal_ret_type, type); builder.AddMainHintArguments(ConvertToken(lastToken)); } void ParserImpl::DiagMacroUnexpectNamedParam(const OwnedPtr& param) { auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_macro_illegal_named_param, MakeRange(param->notMarkPos, param->notMarkPos + 1)); builder.AddHint(MakeRange(param->begin, param->identifier), param->identifier.Val()); } void ParserImpl::DiagIllegalFunc(const OwnedPtr& funcDecl) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_illegal_function_name, MakeRange(funcDecl->identifier)); builder.AddHint(MakeRange(funcDecl->keywordPos, funcDecl->keywordPos + std::string("func").size())); } void ParserImpl::DiagParseExpectedParenthis(const OwnedPtr& postType) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_parentheses, lastToken); auto help = DiagHelp("should be surrounded by '(' and ')'"); std::string content = "(" + lastToken.Value() + ")"; help.AddSubstitution(*postType, content); builder.AddHelp(help); } void ParserImpl::DiagParseIllegalDeclarationPattern(const OwnedPtr& decl, ScopeKind scopeKind) { std::string patternTypeStr; if (decl->irrefutablePattern->astKind == ASTKind::ENUM_PATTERN) { patternTypeStr = "enum"; } else if (decl->irrefutablePattern->astKind == ASTKind::TUPLE_PATTERN) { patternTypeStr = "tuple"; } else if (decl->irrefutablePattern->astKind == ASTKind::WILDCARD_PATTERN) { patternTypeStr = "wildcard"; } auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_illegal_declaration_pattern, *(decl->irrefutablePattern), patternTypeStr); auto pNode = TraceBackNode(chainedAST, {ASTKind::CLASS_DECL, ASTKind::STRUCT_DECL}); if (!pNode) { return; } auto node = StaticCast(pNode); if (scopeKind == ScopeKind::CLASS_BODY) { builder.AddHint(MakeRange(node->keywordPos, node->keywordPos + std::string("class").size()), "class"); } else if (scopeKind == ScopeKind::STRUCT_BODY) { builder.AddHint(MakeRange(node->keywordPos, node->keywordPos + std::string("struct").size()), "struct"); } } void ParserImpl::DiagThisTypeNotAllow() { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_this_type_not_allow, lookahead); SubDiagnostic note = SubDiagnostic("'This' type can only be used as the return type of an instance member function in class"); builder.AddNote(note); } void ParserImpl::DiagInvalidUnicodeScalar(const Position& startPos, const std::string& str) { auto prefixSize = std::string_view{"\\u{"}.size(); auto pos = startPos + prefixSize + 1; auto endPos = startPos + str.size(); auto args = "0x" + str.substr(prefixSize, (str.size() - prefixSize) - 1); auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_invalid_unicode_scalar, MakeRange(pos, endPos), args); builder.AddHint(MakeRange(startPos + 1, startPos + prefixSize + 1)); builder.AddHint(MakeRange(startPos + str.size(), startPos + str.size() + 1)); auto note = SubDiagnostic{"the valid range of code point is '0x0 ~ 0x7fffffff'"}; builder.AddNote(note); } void ParserImpl::DiagExpectedLiteral(const Position& subPos) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_literal, lookahead, ConvertToken(lookahead)); builder.AddHint(subPos); } void ParserImpl::DiagTypePatternInLetCondExpr(const Pattern& pat, ExprKind ek) { std::string exprName = ek == ExprKind::WHILE_COND_EXPR ? "while-let" : "if-let"; auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_type_pattern_in_let_cond, pat, exprName); auto note = SubDiagnostic{exprName + " expression can only accept constant, wildcard, binding, tuple and enum pattern"}; builder.AddNote(note); } void ParserImpl::DiagExpectedLeftParenAfter(const Position& pos, const std::string& str) { auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_left_paren_after, lookahead, str, ConvertToken(lookahead)); builder.AddHint(MakeRange(pos, str)); } void ParserImpl::DiagMatchCaseExpectedExprOrDecl() { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_expr_or_decl_in, lookahead, ConvertToken(lookahead)); DiagMatchCase(builder); } void ParserImpl::DiagMatchCaseBodyCannotBeEmpty(const Position& pos) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_case_body_cannot_be_empty, pos); DiagMatchCase(builder); } void ParserImpl::DiagMatchCase(DiagnosticBuilder& builder) { auto node = TraceBackNode(chainedAST, {ASTKind::MATCH_CASE_OTHER, ASTKind::MATCH_CASE}); if (!node) { return; } if (node->astKind == ASTKind::MATCH_CASE) { auto mc = StaticAs(node); builder.AddHint(MakeRange(mc->begin, std::string("case"))); } else if (node->astKind == ASTKind::MATCH_CASE_OTHER) { auto mco = StaticAs(node); builder.AddHint(MakeRange(mco->begin, std::string("case"))); } else { CJC_ABORT(); } } void ParserImpl::DiagExpectedCatchOrHandleOrFinallyAfterTry(const TryExpr& te) { auto diagKind = this->enableEH ? DiagKindRefactor::parse_expected_catch_or_handle_or_finally_in_try : DiagKindRefactor::parse_expected_catch_or_finally_in_try; auto builder = ParseDiagnoseRefactor(diagKind, lookahead, ConvertToken(lookahead)); builder.AddHint(MakeRange(te.begin, te.tryBlock->end)); } void ParserImpl::DiagExpectedSelectorOrMatchExprBody(const Position& pos) { auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_selector_or_match_expression_body, lookahead, ConvertToken(lookahead)); builder.AddHint(MakeRange(pos, "match")); } void ParserImpl::DiagRedefinedResourceName( const std::pair& cur, const std::pair& pre) { auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_redefined_resource_name, MakeRange(cur.second, cur.first), cur.first); builder.AddHint(MakeRange(pre.second, pre.first)); } void ParserImpl::DiagExpectedNoArgumentsInSpawn( const std::vector>& params, const Position& pos) { std::string arg = params.size() == 1 ? "argument" : "arguments"; auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_no_arguments_in_spawn, MakeRange(params.front()->begin, params.back()->end), arg); builder.AddMainHintArguments(arg); builder.AddHint(MakeRange(pos, "spawn")); } void ParserImpl::DiagExpectCharacter(const std::string& expectStr, const std::string& noteStr) { auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_character, lookahead, expectStr, ConvertToken(lookahead)); builder.AddMainHintArguments(expectStr); if (!noteStr.empty()) { builder.AddNote(noteStr); } } void ParserImpl::DiagExpectCharacter(const Position& pos, const std::string& expectStr, const std::string& noteStr) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_character, pos, expectStr, ConvertToken(lookahead)); builder.AddMainHintArguments(expectStr); if (!noteStr.empty()) { builder.AddNote(noteStr); } } void ParserImpl::DiagExpectSemiOrNewline() { DiagExpectCharacter("';' or ''"); } void ParserImpl::DiagRedundantArrowAfterFunc(const Type& type) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_redundant_arrow_after_func_type, lookahead); builder.AddHint(type, "function type"); } void ParserImpl::DiagInvalidOverloadedOperator() { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_invalid_overloaded_operator, lookahead, ConvertToken(lookahead)); auto note = SubDiagnostic("only operator '+', '-', ... can be overloaded"); builder.AddNote(note); } void ParserImpl::DiagExpectedDeclaration(ScopeKind scopeKind) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_decl, lookahead, ConvertToken(lookahead)); if (scopeKind == ScopeKind::TOPLEVEL) { auto note = SubDiagnostic("only declarations or macro expressions can be used in the top-level"); builder.AddNote(note); } } void ParserImpl::DiagExpectedDeclaration(const Position& pos, const std::string& str) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_decl, pos, str); } void ParserImpl::DiagUnExpectedModifierOnDeclaration(const Decl& vd) { auto constModifier = std::find_if( vd.modifiers.begin(), vd.modifiers.end(), [](const auto& mod) { return mod.modifier == TokenKind::CONST; }); CJC_ASSERT(constModifier != vd.modifiers.end()); auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_unexpected_const_modifier_on_variable, MakeRange(constModifier->begin, "const")); builder.AddHint(MakeRange(vd.keywordPos, "var")); } void ParserImpl::DiagConstVariableExpectedInitializer(Decl& vd) { if (parseDeclFile) { return; } auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_const_expected_initializer, vd.end); builder.AddHint(vd); } void ParserImpl::DiagConstVariableExpectedStatic(const Token& key) { (void)ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_static_for_const_member_var, key); } void ParserImpl::DiagExpectedOneOfTypeOrInitializer(const Decl& vd, const std::string& str) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_one_of_type_or_initializer, vd.end, str); builder.AddHint(vd); } void ParserImpl::DiagExpectedTypeOrInitializerInPattern(const Decl& vd) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_type_or_init_in_pattern, vd.end); builder.AddHint(vd); } void ParserImpl::DiagExpectedInitializerForToplevelVar(const Decl& vd) { if (parseDeclFile) { return; } auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_var_must_be_initialized, vd.end); builder.AddHint(vd); } void ParserImpl::DiagExpectedIdentifierOrPattern(bool isVar, const Position& pos, bool isConst) { if (isConst) { auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_one_of_identifier_or_pattern, lookahead, "const", ConvertToken(lookahead)); builder.AddHint(MakeRange(pos, "const")); return; } auto s = isVar ? "var" : "let"; auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_one_of_identifier_or_pattern, lookahead, s, ConvertToken(lookahead)); builder.AddHint(MakeRange(pos, s)); if (IsKeyWord(lookahead)) { auto helpMes = std::string("escape the keyword `" + lookahead.Value() + "` to use it as an identifier"); auto help = DiagHelp(helpMes); help.AddSubstitution(lookahead, "`" + lookahead.Value() + "`"); builder.AddHelp(help); } } void ParserImpl::DiagExpectedGetOrSetInProp(const Position& pos) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_get_or_set_in_prop, lookahead, ConvertToken(lookahead)); builder.AddHint(MakeRange(pos, "prop")); } void ParserImpl::DiagDuplicatedGetOrSet(const Node& node, const PropDecl& pd) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_duplicated_get_or_set, lookahead, lookahead.Value()); builder.AddMainHintArguments(lookahead.Value()); builder.AddHint(node); builder.AddHint(MakeRange(pd.begin, "prop")); } void ParserImpl::DiagUnknownPrimaryConstructor(const std::string& str) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_expected_decl, lookahead, ConvertToken(lookahead)); builder.AddHelp({"probably you want a primary constructor name: '" + str + "'"}); } void ParserImpl::DiagExpectedName(const std::string& str, const std::string& afterName) { auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_name, lookahead, str + " name", afterName, ConvertToken(lookahead)); builder.AddMainHintArguments(str + " name"); } void ParserImpl::DiagGetOrSetCannotBeGeneric(const std::string& str, const Generic& ge) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_getter_setter_cannot_be_generic, ge, str); auto isGetOrSet = [](Ptr node) { if (node->astKind != ASTKind::FUNC_DECL) { return false; } auto fd = StaticAs(node); if (!fd->isSetter && !fd->isGetter) { return false; } return true; }; auto iter = std::find_if(chainedAST.rbegin(), chainedAST.rend(), isGetOrSet); CJC_ASSERT(iter != chainedAST.rend() && "cannot find getter or setter"); auto fd = StaticAs(*iter); builder.AddHint(MakeRange(fd->begin, "get"), str); } void ParserImpl::DiagUnexpectedWhere(const Token& token) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_unexpected_where, token); auto iter = std::find_if(chainedAST.rbegin(), chainedAST.rend(), [](auto node) { return node->astKind == ASTKind::FUNC_DECL || node->astKind == ASTKind::CLASS_DECL || node->astKind == ASTKind::INTERFACE_DECL || node->astKind == ASTKind::ENUM_DECL || node->astKind == ASTKind::STRUCT_DECL || node->astKind == ASTKind::MAIN_DECL || node->astKind == ASTKind::EXTEND_DECL; }); CJC_ASSERT(iter != chainedAST.rend() && "cannot find declaration"); auto decl = StaticAs(*iter); auto hintRange = decl->astKind == ASTKind::EXTEND_DECL ? MakeRange(decl->keywordPos, "extend") : MakeRange(decl->identifier); builder.AddHint(hintRange); } void ParserImpl::DiagDuplicatedIntrinsicFunc(const Decl& decl, const Decl& prev) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_duplicated_intrinsic_function, decl, decl.identifier); builder.AddHint(prev); } void ParserImpl::DiagMissingBody(const std::string& str, const std::string& name, const Position& pos) { if (parseDeclFile) { return; } auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_missing_body, pos, name.empty() ? str : str + name); builder.AddMainHintArguments(str); } void ParserImpl::DiagDeclCannotInheritTheirSelf(const InheritableDecl& decl, const RefType& rt) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_decl_cannot_inherit_their_self, rt, rt.ref.identifier); builder.AddHint(MakeRange(decl.identifier), rt.ref.identifier); } void ParserImpl::DiagUnsafeWillBeIgnored(const Modifier& mod) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_unsafe_will_be_ignored, mod, Triple::BackendToString(backend)); builder.AddMainHintArguments(Triple::BackendToString(backend)); } void ParserImpl::DiagDuplicatedModifier(const Modifier& mod, const Modifier& pre) { auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_duplicate_modifier, mod, TOKEN_KIND_VALUES[static_cast(mod.modifier)]); builder.AddHint(pre); } void ParserImpl::DiagNamedParameterAfterUnnamed(const FuncParam& param, const FuncParam& pre) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_named_parameter_after_unnamed, param); builder.AddHint(pre); } void ParserImpl::DiagMemberParameterAfterRegular(const FuncParam& param, const FuncParam& pre) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_member_parameter_after_regular, param); builder.AddHint(pre); } void ParserImpl::DiagUnexpectedColonInRange(const RangeExpr& re) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_unexpected_colon_in_range, lookahead); builder.AddHint(re); builder.AddNote("range expression in index access cannot have step if it is without start or end"); } void ParserImpl::DiagDuplicatedAttiValue(const std::string& attr, const Position& pos, const Position& pre) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_duplicated_attr_value, MakeRange(pos, attr), attr); builder.AddHint(MakeRange(pre, attr)); } void ParserImpl::DiagDuplicatedItem(const std::string& name, const std::string& sourceName, const Position& errPos, const Position& prePos, const std::string& attachment) { auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_duplicated_item, MakeRange(errPos, sourceName), name, sourceName, attachment); builder.AddMainHintArguments(name); builder.AddHint(MakeRange(prePos, sourceName)); } void ParserImpl::DiagUnrecognizedAttrInAnno( const Annotation& anno, const std::string& attrName, const Position& pos, const std::set& acs) { CJC_ASSERT(!acs.empty() && "accepted attributes cannot be empty"); std::string str{"'" + *acs.begin() + "'"}; auto i = acs.begin(); auto j = acs.end(); ++i; --j; for (; i != acs.end(); ++i) { auto temp = i == j ? " or " : ", "; str += temp; str += "'" + *i + "'"; } auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_unrecognized_attr_in_anno, MakeRange(pos, attrName), attrName, anno.identifier); builder.AddHint(anno); builder.AddNote("annotation '@" + anno.identifier + "' only accept " + str + " as attribute"); } void ParserImpl::DiagDuplicatedAnno(const Annotation& anno, const Annotation& pre) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_duplicated_annotation, anno, "@" + anno.identifier); builder.AddHint(pre); } void ParserImpl::DiagExpectedLsquareAfter(const Node& node, const std::string& aName, const std::string& note) { auto builder = ParseDiagnoseRefactor( DiagKindRefactor::parse_expected_lsquare_after, lookahead, aName, ConvertToken(lookahead)); builder.AddNote(note); builder.AddHint(node); } void ParserImpl::DiagUnrecognizedExprInWhen(const Expr& expr, const Annotation& when) { auto str = sourceManager.GetContentBetween(expr.begin.fileID, expr.begin, expr.end); auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_unrecognized_expression_in_when, expr, str); builder.AddHint(when); builder.AddNote("annotation '@When' can only support unary, binary and ref expression"); } void ParserImpl::DiagDeprecatedArgumentNotLitConst(const Node& node) { ParseDiagnoseRefactor(DiagKindRefactor::parse_deprecated_arguments_must_be_lit_const_expr, node); } void ParserImpl::DiagDeprecatedArgumentDuplicated( const Node& node, const std::string& parameterName ) { ParseDiagnoseRefactor(DiagKindRefactor::parse_deprecated_argument_duplication, node, parameterName); } void ParserImpl::DiagDeprecatedWrongArgumentType( const Node& node, const std::string& paramName, const std::string& expectedType ) { ParseDiagnoseRefactor(DiagKindRefactor::parse_deprecated_wrong_argument, node, paramName, expectedType); } void ParserImpl::DiagDeprecatedUnknownArgument( const Node& arg, const std::string& name ) { ParseDiagnoseRefactor(DiagKindRefactor::parse_deprecated_unknown_argument, arg, name); } void ParserImpl::DiagDeprecatedEmptyStringArgument( const Node& node, const std::string& paramName ) { ParseDiagnoseRefactor(DiagKindRefactor::parse_deprecated_empty_string_argument, node, paramName); } void ParserImpl::DiagDeprecatedInvalidTarget( const Node& node, const std::string& invalidTarget ) { ParseDiagnoseRefactor(DiagKindRefactor::parse_deprecated_invalid_target, node, invalidTarget); } void ParserImpl::DiagAnnotationExpectsOneArgument(const AST::Annotation& node, const std::string& annotationName) { ParseDiagnoseRefactor( DiagKindRefactor::parse_annotation_one_argument, node, annotationName, ""); } void ParserImpl::DiagAnnotationExpectsOneArgument(const Annotation& node, const std::string& annotationName, const std::string& argInfo) { const Node* diagNode = node.args.size() == 1 ? StaticCast(node.args[0].get().get()) : &node; auto dbuilder = ParseDiagnoseRefactor(DiagKindRefactor::parse_annotation_one_argument, *diagNode, annotationName, " " + argInfo); switch (node.args.size()) { case 1: dbuilder.AddMainHintArguments(argInfo); break; default: dbuilder.AddMainHintArguments("single " + argInfo + " for " + annotationName); break; } } void ParserImpl::DiagAnnotationMoreThanOneArgs(const Annotation& node, const std::string& annotationName) { CJC_ASSERT(node.args.size() > 1); auto dbuilder = ParseDiagnoseRefactor( DiagKindRefactor::parse_annotation_max_one_argument, node, annotationName, ""); dbuilder.AddMainHintArguments("single argument for " + annotationName); } void ParserImpl::DiagAnnotationMoreThanOneArgs(const Annotation& node, const std::string& annotationName, const std::string& argInfo) { const Node* diagNode = node.args.size() == 1 ? StaticCast(node.args[0].get().get()) : &node; auto dbuilder = ParseDiagnoseRefactor(DiagKindRefactor::parse_annotation_max_one_argument, *diagNode, annotationName, " " + argInfo); if (node.args.size() == 1) { dbuilder.AddMainHintArguments(argInfo); } else { dbuilder.AddMainHintArguments("single " + argInfo + " for " + annotationName); } } void ParserImpl::DiagUnexpectedTypeIn( const Type& type, const Position& inPos, const std::string& inStr, const std::string& noteStr) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_unexpected_type_in, type, inStr); builder.AddHint(MakeRange(inPos, inStr), inStr); builder.AddNote(noteStr); } void ParserImpl::DiagExpectedIdentifierWithNode(Ptr node) { if (auto iter = diagExpectedIdentifierMap.find(node->astKind); iter != diagExpectedIdentifierMap.end()) { iter->second(this, node); } else { auto range = MakeRange(lastToken.Begin(), lastToken.End()); DiagExpectedIdentifier(range); } node->EnableAttr(Attribute::HAS_BROKEN); } void ParserImpl::DiagImportingByPackageNameIsNotSupported(const Position& expectPos) { auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_importing_by_package_name_is_not_supported, expectPos); builder.AddNote("importing by package name is not supported."); } void ParserImpl::DiagVArrayTypeArgMismatch(const Range& range, const std::string& note) { (void)ParseDiagnoseRefactor(DiagKindRefactor::parse_varray_type_args_mismatch, range, note); ConsumeUntil(TokenKind::NL); } void ParserImpl::DiagRedundantModifiers(const Modifier& modifier) { // NOTE: for now, only interface member decls will enter this method. CJC_ASSERT(!chainedAST.empty()); auto node = TraceBackNode(chainedAST, {ASTKind::INTERFACE_DECL}); if (!node) { // When parsing macro expand nodes, the decl of outer scope may not existed. return; } CJC_ASSERT(KIND_TO_STR.count(node->astKind) != 0); auto kindStr = KIND_TO_STR.at(node->astKind); auto scopeRange = MakeRange(node->begin, node->begin + kindStr.first.size()); auto modStr = std::string(TOKENS[static_cast(modifier.modifier)]); auto modRange = MakeRange(modifier.begin, modifier.begin + modStr.size()); auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_redundant_modifier, modRange, modStr); builder.AddMainHintArguments("interface member", modStr); builder.AddHint(scopeRange); } void ParserImpl::DiagRedundantModifiers(const Modifier& lowerModifier, const Modifier& higherModifier) { auto resStr = std::string(TOKENS[static_cast(lowerModifier.modifier)]); auto tarStr = std::string(TOKENS[static_cast(higherModifier.modifier)]); auto resRange = MakeRange(lowerModifier.begin, lowerModifier.begin + resStr.size()); auto tarRange = MakeRange(higherModifier.begin, higherModifier.begin + tarStr.size()); auto builder = ParseDiagnoseRefactor(DiagKindRefactor::parse_redundant_modifier, resRange, resStr); builder.AddMainHintArguments("'" + tarStr + "'", resStr); builder.AddHint(tarRange); } } // namespace Cangjie cangjie_compiler-1.0.7/src/Parse/ParserImpl.cpp000066400000000000000000000177241510705540100214450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements API of class Parser by encapsulating API of ParserImpl. */ #include "ParserImpl.h" #include "cangjie/AST/Match.h" namespace Cangjie { OwnedPtr Parser::ParseTopLevel() { return impl->ParseTopLevel(); } OwnedPtr Parser::ParseDecl(ScopeKind scopeKind) { return impl->ParseDecl(scopeKind, {}, {}); } OwnedPtr Parser::ParseExpr() { return impl->ParseExpr(); } OwnedPtr Parser::ParseExprLibast() { return impl->ParseExpr(ExprKind::UNKNOWN_EXPR); } OwnedPtr Parser::ParseType() { return impl->ParseType(); } OwnedPtr Parser::ParsePattern() { return impl->ParsePattern(); } std::vector> Parser::ParseNodes(std::variant scope, AST::Node& currentMacroCall, const std::set& modifiers, std::vector> annos) { return impl->ParseNodes(scope, currentMacroCall, modifiers, std::move(annos)); } void Parser::ParseAnnotationArguments(AST::Annotation& anno) const { return impl->ParseAnnotationArguments(anno); } OwnedPtr Parser::ParseCustomAnnotation() const { return impl->ParseCustomAnnotation(); } DiagnosticEngine& Parser::GetDiagnosticEngine() const { return impl->diag; } std::size_t Parser::GetProcessedTokens() const { return impl->GetProcessedTokens(); } std::string Parser::GetPrimaryDeclIdentRawValue() const { return impl->GetPrimaryDeclIdentRawValue(); } Parser& Parser::SetPrimaryDecl(const std::string& decl) { impl->SetPrimaryDecl(decl); return *this; } size_t Parser::GetLineNum() const { return impl->GetLineNum(); } Parser& Parser::SetModuleName(const std::string& name) { impl->moduleName = name; return *this; } Parser& Parser::SetForImport(bool isForImport) { impl->forImport = isForImport; return *this; } Parser& Parser::SetCurFile(Ptr curFile) { impl->currentFile = curFile; return *this; } Parser& Parser::EnableCustomAnno() { impl->enableCustomAnno = true; return *this; } Parser& Parser::SetEHEnabled(bool enabled) { impl->enableEH = enabled; impl->lexer->SetEHEnabled(enabled); return *this; } bool Parser::IsEHEnabled() const { return impl->enableEH; } TokenVecMap Parser::GetCommentsMap() const { return impl->commentsMap; } void Parser::SetCompileOptions(const GlobalOptions& opts) { impl->backend = opts.backend; impl->scanDepPkg = opts.scanDepPkg; impl->calculateLineNum = opts.enableTimer || opts.enableMemoryCollect; impl->enableInteropCJMapping = opts.enableInteropCJMapping; // set compile options for cjmp implementation impl->mpImpl->SetCompileOptions(opts); // Effect handlers break backwards compatibility by introducing new // keywords, so we disable them from the parser unless the user // explicitly asks to compile with effect handler support SetEHEnabled(opts.enableEH); } bool Parser::Skip(TokenKind kind) { return impl->Skip(kind); } const Token& Parser::Peek() { return impl->Peek(); } void Parser::Next() { return impl->Next(); } bool Parser::Seeing(TokenKind kind) { return impl->Seeing(kind); } bool Parser::Seeing(TokenKind rangeLeft, TokenKind rangeRight) { return impl->Seeing(rangeLeft, rangeRight); } bool Parser::SeeingAny(const std::vector& kinds) { return impl->SeeingAny(kinds); } bool Parser::Seeing(const std::vector& kinds, bool skipNewline) { return impl->Seeing(kinds, skipNewline); } bool Parser::SeeingCombinator(const std::vector& kinds) { return impl->SeeingCombinator(kinds); } bool Parser::SeeingTokenAndCombinator(TokenKind kind, const std::vector& cmb) { return impl->SeeingTokenAndCombinator(kind, cmb); } void Parser::SkipCombinator(const std::vector& kinds) { return impl->SkipCombinator(kinds); } const Token& Parser::LookAhead() const { return impl->lookahead; } const Token& Parser::LastToken() const { return impl->lastToken; } Ptr Parser::CurMacroCall() const { return impl->curMacroCall; } Parser::~Parser() { delete impl; } // implementation of ParserImpl ParserImpl::ParserImpl(unsigned int fileID, const std::string& input, DiagnosticEngine& diag, SourceManager& sm, bool attachComment, bool parsingDeclFiles) : diag(diag), sourceManager(sm), lexer{std::make_unique(fileID, input, diag, sm, attachComment)}, enableAttachComment(attachComment), parseDeclFile{parsingDeclFiles}, mpImpl{new MPParserImpl(*this)}, ffiParser{new FFIParserImpl(*this)} { } ParserImpl::ParserImpl(const std::string& input, DiagnosticEngine& diag, SourceManager& sm, const Position& pos, bool attachComment, bool parsingDeclFiles) : diag(diag), sourceManager(sm), lexer{std::make_unique(input, diag, sm, pos, attachComment)}, enableAttachComment{attachComment}, parseDeclFile{parsingDeclFiles}, mpImpl{new MPParserImpl(*this)}, ffiParser{new FFIParserImpl(*this)} { } ParserImpl::ParserImpl(const std::vector& inputTokens, DiagnosticEngine& diag, SourceManager& sm, bool attachComment, bool parsingDeclFiles) : diag(diag), sourceManager(sm), lexer{std::make_unique(inputTokens, diag, sm, attachComment)}, enableAttachComment{attachComment}, parseDeclFile{parsingDeclFiles}, mpImpl{new MPParserImpl(*this)}, ffiParser{new FFIParserImpl(*this)} { } ParserImpl::~ParserImpl() { delete mpImpl; mpImpl = nullptr; delete ffiParser; ffiParser = nullptr; } /** * Checks whether a member decl can be an abstract by context and already parsed info * @param decl member declaration */ bool ParserImpl::CanBeAbstract(const AST::Decl& decl, ScopeKind scopeKind) const { auto pdecl = Ptr(&decl); switch (decl.astKind) { case ASTKind::FUNC_DECL: { auto fd = StaticAs(pdecl); if (fd->funcBody && fd->funcBody->body) { return false; } break; } case ASTKind::PROP_DECL: { auto pd = StaticAs(pdecl); if (!pd->getters.empty() || !pd->setters.empty()) { return false; } break; } case ASTKind::PRIMARY_CTOR_DECL: { auto pcd = StaticAs(pdecl); if (!pcd->funcBody && pcd->funcBody->body) { return false; } break; } default: break; } if (scopeKind == ScopeKind::INTERFACE_BODY) { return true; } // modify to support common abstract if (scopeKind == ScopeKind::CLASS_BODY && !decl.TestAttr(Attribute::COMMON)) { return true; } return false; } void ParserImpl::CheckConstructorBody(AST::FuncDecl& ctor, ScopeKind scopeKind, bool inMacro) { CJC_ASSERT(ctor.TestAttr(Attribute::CONSTRUCTOR)); if (ctor.funcBody && ctor.funcBody->retType) { ParseDiagnoseRefactor( DiagKindRefactor::parse_invalid_return_type, *ctor.funcBody->retType, "constructor"); ctor.EnableAttr(Attribute::HAS_BROKEN); } /* If constructor parsed in macro or via libast does not have a body, then it's broken. If constructor is in scope of class/struct and it does not have a body, then it's acceptable*. * More concrete conditions are be checked during class/struct body parsing. */ auto isInClassLike = scopeKind == ScopeKind::CLASS_BODY || scopeKind == ScopeKind::STRUCT_BODY; if ((!isInClassLike || inMacro) && (!ctor.funcBody || !ctor.funcBody->body) && !ctor.TestAttr(Attribute::COMMON)) { DiagMissingBody("constructor", "", ctor.end); ctor.EnableAttr(Attribute::HAS_BROKEN); } } } cangjie_compiler-1.0.7/src/Parse/ParserImpl.h000066400000000000000000001766321510705540100211160ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares class ParserImpl. */ #ifndef CANGJIE_PARSE_PARSERIMPL_H #define CANGJIE_PARSE_PARSERIMPL_H #include "cangjie/Parse/Parser.h" #include "MPParserImpl.h" #include "NativeFFI/FFIParserImpl.h" namespace Cangjie { using namespace AST; const size_t PACKAGE_NAME_LEN_LIMIT = 200U; enum class DefKind : uint8_t { FUNC = 0, MAIN, MACRO, CLASS, INTERFACE, VARIABLE, ENUM, STRUCT, EXTEND, TYPE, CONSTRUCTOR, PRIMARY_CONSTRUCTOR, PROPERTY, FINALIZER, PACKAGE }; enum class ExprKind : uint8_t { ALL, VAR_INIT, INDEX_EXPR, IF_COND_EXPR, // if condition is actually not an expression, but this is kept for code compatibility WHILE_COND_EXPR, // so does while condition LET_PATTERN, LEFT_EXPR, EXPR_IN_TUPLE, EXPR_IN_IF_COND_TUPLE, EXPR_IN_WHILE_COND_TUPLE, EXPR_IN_ARRAY, EXPR_IN_CALLSUFFIX, EXPR_IN_ANNOTATION, UNKNOWN_EXPR, }; const std::vector& GetTypeFirst(); class ParserImpl final { public: ParserImpl(unsigned int fileID, const std::string& input, DiagnosticEngine& diag, SourceManager& sm, bool attachComment, bool parsingDeclFiles); ParserImpl(const std::string& input, DiagnosticEngine& diag, SourceManager& sm, const Position& pos, bool attachComment, bool parsingDeclFiles); ParserImpl(const std::vector& inputTokens, DiagnosticEngine& diag, SourceManager& sm, bool attachComment, bool parsingDeclFiles); ~ParserImpl(); size_t GetLineNum() const; OwnedPtr ParseTopLevel(); OwnedPtr ParseDecl( ScopeKind scopeKind, std::set modifiers = {}, std::vector> annos = {}); OwnedPtr ParseExpr(ExprKind ek = ExprKind::ALL); OwnedPtr ParseType(); OwnedPtr ParsePattern( const std::set& attributes = {}, bool isVar = false, bool inDecl = false); std::vector> ParseNodes(std::variant scope, AST::Node& currentMacroCall, const std::set& modifiers = {}, std::vector> annos = {}); Parser& EnableCustomAnno(); std::size_t GetProcessedTokens() const; std::string GetPrimaryDeclIdentRawValue() const; Parser& SetModuleName(const std::string& name); void SetPrimaryDecl(const std::string& decl, bool isRawId = false); Parser& SetForImport(const bool isForImport); Parser& SetCurFile(Ptr curFile); void SetCompileOptions(const GlobalOptions& opts); DiagnosticEngine& GetDiagnosticEngine() const; // Skip next token if token kind satisfied bool Skip(TokenKind kind); // Peek next token, save it to lookAhead const Token& Peek(); // Consume next token, put a sentinel symbol on it void Next(); // Whether the next token is the given token kind. bool Seeing(TokenKind kind) { return Peek().kind == kind; } inline bool Seeing(TokenKind rangeLeft, TokenKind rangeRight) { return Peek().kind >= rangeLeft && Peek().kind <= rangeRight; } inline bool SeeingAny(const std::vector& kinds) { return Utils::In(Peek().kind, kinds); } // Whether the following tokens are the given token vector. 'skipNL=true' means NL token should be skipped when scan // following tokens. bool Seeing(const std::vector& kinds, bool skipNewline = true); /** See several tokens without space between them, skip all the tokens except the last token. */ bool SeeingCombinator(const std::vector& kinds); /** Whether the next token is of the given token type and follows the given combined token*/ bool SeeingTokenAndCombinator(TokenKind kind, const std::vector& cmb); void SkipCombinator(const std::vector& kinds); private: DiagnosticEngine& diag; Token lookahead{TokenKind::SENTINEL, "", Position(0, 1, 1), Position(0, 1, 1)}; /**< Current scanned token. */ Token lastToken{TokenKind::SENTINEL, "", Position(0, 1, 1), Position(0, 1, 1)}; /**< Last scanned token. */ /**< Last scanned token which is not newline. */ Token lastNoneNLToken{TokenKind::SENTINEL, "", Position(0, 1, 1), Position(0, 1, 1)}; bool newlineSkipped{true}; /**< Mark that a new line token has been skipped. */ bool skipNL{true}; /**< Skip new line token by default. */ Position firstNLPosition{DEFAULT_POSITION}; /**< Record first nl position. */ SourceManager& sourceManager; // This type can only appear in trait or class // member function return type. when parsing trait // or class member function return type, enableThis // should be set true, and set false while leaving. bool enableThis{false}; bool deadlocked{false}; bool inForeignBlock{false}; bool enableCustomAnno{false}; // parse macrocall as custom annotation. std::unique_ptr lexer; std::string inputString{}; bool forImport{false}; std::string moduleName; inline bool SeeingTrailingClosure(OwnedPtr& baseExpr) { if (Seeing(TokenKind::LCURL)) { return true; } if (baseExpr->astKind != AST::ASTKind::CALL_EXPR && baseExpr->astKind != AST::ASTKind::REF_EXPR && baseExpr->astKind != AST::ASTKind::ARRAY_EXPR) { return false; } // SeeingAnnotationTrailClosure. return SeeingAnnotationLambdaExpr() || SeeingAnnotationTrailingClosure({TokenKind::LSQUARE}); } bool SkipAmbiguousToken(); bool SkipCombinedDoubleArrow(); inline bool SeeingImport2() { return Seeing(TokenKind::IMPORT) || Seeing({TokenKind::PUBLIC, TokenKind::IMPORT}) || Seeing({TokenKind::PROTECTED, TokenKind::IMPORT}) || Seeing({TokenKind::INTERNAL, TokenKind::IMPORT}) || Seeing({TokenKind::PRIVATE, TokenKind::IMPORT}); } inline bool SeeingPackage() { return Seeing(TokenKind::PACKAGE); } inline bool SeeingFeature() { return Seeing(TokenKind::FEATURES); } inline bool SeeingMacroPackage() { return Seeing({TokenKind::MACRO, TokenKind::PACKAGE}); } inline bool SeeingContextualKeyword() { return Utils::In(GetContextualKeyword(), [this](const TokenKind& kind) { return Seeing(kind); }); } inline bool SeeingPrimaryKeyWordContext(TokenKind tk) { return Utils::In(GetContextualKeyword(), [this, &tk](const TokenKind& kind) { return Seeing({kind, tk}); }); } // Avoid infinite loop when parsing parentheses and braces bool DetectPrematureEnd(); // Continues skip when blank tokens appear void SkipBlank(TokenKind blank0, TokenKind blank1 = TokenKind::SENTINEL); std::string ParseForImportToGetContentBetween( unsigned int fileID, int beginLine, int beginColumn, int endLine, int endColumn); std::unordered_map(ParserImpl*)>> exprHandlerMap{ {TokenKind::IF, &ParserImpl::ParseIfExpr}, {TokenKind::MATCH, &ParserImpl::ParseMatchExpr}, {TokenKind::QUOTE, &ParserImpl::ParseQuoteExpr}, {TokenKind::TRY, &ParserImpl::ParseTryExpr}, {TokenKind::THROW, &ParserImpl::ParseThrowExpr}, {TokenKind::PERFORM, &ParserImpl::ParsePerformExpr}, {TokenKind::RESUME, &ParserImpl::ParseResumeExpr}, {TokenKind::RETURN, &ParserImpl::ParseReturnExpr}, {TokenKind::FOR, &ParserImpl::ParseForInExpr}, {TokenKind::WHILE, &ParserImpl::ParseWhileExpr}, {TokenKind::DO, &ParserImpl::ParseDoWhileExpr}, {TokenKind::SPAWN, &ParserImpl::ParseSpawnExpr}, {TokenKind::SYNCHRONIZED, &ParserImpl::ParseSynchronizedExpr}, {TokenKind::LPAREN, &ParserImpl::ParseLeftParenExpr}, {TokenKind::CONTINUE, &ParserImpl::ParseContinueJumpExpr}, {TokenKind::BREAK, &ParserImpl::ParseBreakJumpExpr}, {TokenKind::LSQUARE, &ParserImpl::ParseArrayLitExpr}, {TokenKind::THIS, &ParserImpl::ParseThisOrSuper}, {TokenKind::SUPER, &ParserImpl::ParseThisOrSuper}, {TokenKind::LCURL, &ParserImpl::ParseLambdaExpr}, {TokenKind::UNSAFE, &ParserImpl::ParseUnsafeBlock}, {TokenKind::VARRAY, &ParserImpl::ParseVArrayExpr}, }; std::unordered_map( ParserImpl*, ScopeKind, const std::set&, std::vector>)>> declHandlerMap{ {TokenKind::CLASS, &ParserImpl::ParseClassDecl}, {TokenKind::INTERFACE, &ParserImpl::ParseInterfaceDecl}, {TokenKind::MAIN, &ParserImpl::ParseMainDecl}, {TokenKind::MACRO, &ParserImpl::ParseMacroDecl}, {TokenKind::STRUCT, &ParserImpl::ParseStructDecl}, {TokenKind::ENUM, &ParserImpl::ParseEnumDecl}, {TokenKind::TYPE, &ParserImpl::ParseTypeAlias}, {TokenKind::EXTEND, &ParserImpl::ParseExtendDecl}, {TokenKind::PROP, &ParserImpl::ParsePropDecl}, {TokenKind::FUNC, &ParserImpl::ParseFuncDecl}, {TokenKind::INIT, &ParserImpl::ParseConstructor}, {TokenKind::VAR, &ParserImpl::ParseVarOrLet}, {TokenKind::LET, &ParserImpl::ParseVarOrLet}, }; const std::map> exprsFollowedCommas{ {ExprKind::EXPR_IN_TUPLE, {TokenKind::LPAREN, TokenKind::RPAREN}}, {ExprKind::EXPR_IN_ARRAY, {TokenKind::LSQUARE, TokenKind::RSQUARE}}, {ExprKind::EXPR_IN_CALLSUFFIX, {TokenKind::LPAREN, TokenKind::RPAREN}}, {ExprKind::EXPR_IN_ANNOTATION, {TokenKind::LSQUARE, TokenKind::RSQUARE}}, }; const std::map, std::pair> combinator{ {{TokenKind::GT, TokenKind::GT, TokenKind::ASSIGN}, {TokenKind::RSHIFT_ASSIGN, ">>="}}, {{TokenKind::GT, TokenKind::ASSIGN}, {TokenKind::GE, ">="}}, {{TokenKind::GT, TokenKind::GT}, {TokenKind::RSHIFT, ">>"}}, {{TokenKind::QUEST, TokenKind::QUEST}, {TokenKind::COALESCING, "??"}}}; // Note that RSHIFT_ASSIGN should come before RSHIFT, because RSHIFT_ASSIGN contains RSHIFT. const std::vector, std::string>> ambiguoussCombinedTokens{ {TokenKind::COALESCING, {TokenKind::QUEST, TokenKind::QUEST}, "??"}, {TokenKind::RSHIFT_ASSIGN, {TokenKind::GT, TokenKind::GT, TokenKind::ASSIGN}, ">>="}, {TokenKind::RSHIFT, {TokenKind::GT, TokenKind::GT}, ">>"}, {TokenKind::GE, {TokenKind::GT, TokenKind::ASSIGN}, ">="}, }; const std::vector combinedDoubleArrow{TokenKind::ASSIGN, TokenKind::GT}; static const std::vector combinedBackarrow; friend class ParserScope; friend class ChainScope; Ptr currentFile{nullptr}; Ptr curMacroCall{nullptr}; std::string lastPrimaryDeclIdent; bool lastPrimaryDeclIdentIsRaw{false}; std::string curPrimaryDeclIdent; bool curPrimaryDeclIdentIsRaw{false}; std::vector bracketsStack; /* A stack to help match brackets. */ bool scanDepPkg{false}; bool enableAttachComment{false}; bool parseDeclFile{false}; bool enableEH{false}; bool enableInteropCJMapping{false}; Triple::BackendType backend{Triple::BackendType::CJNATIVE}; bool calculateLineNum{false}; // we store line number info from all tokens // pair std::list> allTokensInOneFile; TokenVecMap commentsMap; uint8_t Precedence(TokenKind kind) const; // cjmp parser implementation class friend class MPParserImpl; class MPParserImpl* mpImpl; friend class FFIParserImpl; friend class JFFIParserImpl; friend class OCFFIParserImpl; class FFIParserImpl* ffiParser; /// Common initializer shared among constructors. void Init(); // This is parsing chain currently, only used for diagnostic. std::vector> chainedAST; std::unordered_set> intrinsics{}; /* A set to record intrinsic function. */ /** * Attach comment group to the ast nodes in File */ void AttachCommentToFile(Ptr node); /** * Attach comment group to the ast nodes(sorted by range) */ void AttachCommentToSortedNodes(std::vector>& nodes); /** * Attach comment group to the ast nodes * note : only run in ParseNodes in macro expansion */ void AttachCommentToNodes(std::vector>& nodes); /** * Collect comment groups from token stream */ void CollectCommentGroups(AST::CommentGroupsLocInfo& cgInfo) const; bool SeeingExprOperator(); Token GetExprOperator(); void SkipExprOperator(); bool SeeingNamedFuncArgs(); bool SeeingKeywordAndOperater(); bool SeeingKeywordWithDecl(); bool SeeingParamInMacroCallExpr(); bool SeeingIdentifierAndTargetOp(const std::vector& tokenKinds); bool SeeingInvaildParamListInLambdaExpr(); bool SeeingInvaildOperaterInLambdaExpr(); bool SeeingAnnotationTrailingClosure(const std::vector& tokenKinds); bool IsRawIdentifier(const std::string& identifier) const; std::string ParseNameFromRawIdentifier(const std::string& rawIdentifier) const; SrcIdentifier ParseIdentifierFromToken(const Token& token) const; SrcIdentifier ParseIdentifierFromName( const std::string& identifier, const Position& tkPos, const Position& end, size_t len) const; SrcIdentifier ExpectIdentifierWithPos(AST::Node& node); SrcIdentifier ExpectPackageIdentWithPos(AST::Node& node); std::string ExpectOperatorIdentifier(AST::FuncDecl& fd); inline bool IsExprFollowedComma(ExprKind ek) const { return exprsFollowedCommas.find(ek) != exprsFollowedCommas.end(); } inline bool SeeingAnnotationLambdaExpr() { return Seeing({TokenKind::AT, TokenKind::IDENTIFIER, TokenKind::LCURL}) && SeeingBuiltinAnnotation(); } bool SeeingPrimTypes() { return (Peek().kind >= TokenKind::INT8) && (Peek().kind <= TokenKind::UNIT); } bool SeeingKeyword() { return (Peek().kind >= TokenKind::INT8 && Peek().kind < TokenKind::IDENTIFIER) || Peek().kind == TokenKind::AS; } bool SeeingOperator() { return (Peek().kind >= TokenKind::DOT) && (Peek().kind <= TokenKind::WILDCARD); } bool SeeingSoftKeyword() { return Peek().kind == TokenKind::FEATURES; } bool SeeingExpr(); // modifiers bool SeeingModifier(); bool SeeingLiteral() { return SeeingAny({TokenKind::INTEGER_LITERAL, TokenKind::RUNE_BYTE_LITERAL, TokenKind::BOOL_LITERAL, TokenKind::STRING_LITERAL, TokenKind::JSTRING_LITERAL, TokenKind::MULTILINE_STRING, TokenKind::MULTILINE_RAW_STRING, TokenKind::RUNE_LITERAL, TokenKind::FLOAT_LITERAL, TokenKind::UNIT_LITERAL}); } bool SeeingPrimitiveTypeAndLParen() { return Seeing({TokenKind::INT8, TokenKind::LPAREN}) || Seeing({TokenKind::INT16, TokenKind::LPAREN}) || Seeing({TokenKind::INT32, TokenKind::LPAREN}) || Seeing({TokenKind::INT64, TokenKind::LPAREN}) || Seeing({TokenKind::INTNATIVE, TokenKind::LPAREN}) || Seeing({TokenKind::UINT8, TokenKind::LPAREN}) || Seeing({TokenKind::UINT16, TokenKind::LPAREN}) || Seeing({TokenKind::UINT32, TokenKind::LPAREN}) || Seeing({TokenKind::UINT64, TokenKind::LPAREN}) || Seeing({TokenKind::UINTNATIVE, TokenKind::LPAREN}) || Seeing({TokenKind::FLOAT16, TokenKind::LPAREN}) || Seeing({TokenKind::FLOAT32, TokenKind::LPAREN}) || Seeing({TokenKind::FLOAT64, TokenKind::LPAREN}) || Seeing({TokenKind::RUNE, TokenKind::LPAREN}); } bool SeeingPrimitiveTypeAndDot() { return Seeing({TokenKind::INT8, TokenKind::DOT}) || Seeing({TokenKind::INT16, TokenKind::DOT}) || Seeing({TokenKind::INT32, TokenKind::DOT}) || Seeing({TokenKind::INT64, TokenKind::DOT}) || Seeing({TokenKind::INTNATIVE, TokenKind::DOT}) || Seeing({TokenKind::UINT8, TokenKind::DOT}) || Seeing({TokenKind::UINT16, TokenKind::DOT}) || Seeing({TokenKind::UINT32, TokenKind::DOT}) || Seeing({TokenKind::UINT64, TokenKind::DOT}) || Seeing({TokenKind::UINTNATIVE, TokenKind::DOT}) || Seeing({TokenKind::FLOAT16, TokenKind::DOT}) || Seeing({TokenKind::FLOAT32, TokenKind::DOT}) || Seeing({TokenKind::FLOAT64, TokenKind::DOT}) || Seeing({TokenKind::RUNE, TokenKind::DOT}) || Seeing({TokenKind::BOOLEAN, TokenKind::DOT}) || Seeing({TokenKind::UNIT, TokenKind::DOT}) || Seeing({TokenKind::NOTHING, TokenKind::DOT}); } bool SkipOperator() { return Skip(TokenKind::NOT) || Skip(TokenKind::ADD) || Skip(TokenKind::SUB) || Skip(TokenKind::EXP) || Skip(TokenKind::MUL) || Skip(TokenKind::DIV) || Skip(TokenKind::MOD) || Skip(TokenKind::LSHIFT) || Skip(TokenKind::LT) || Skip(TokenKind::LE) || Skip(TokenKind::GT) || Skip(TokenKind::EQUAL) || Skip(TokenKind::NOTEQ) || Skip(TokenKind::BITAND) || Skip(TokenKind::BITXOR) || Skip(TokenKind::BITOR); } bool SkipKeyWordIdentifier() { return Utils::In(GetContextualKeyword(), [this](const TokenKind& kind) { return Skip(kind); }); } bool SkipNLOrSemi() { bool hasNLOrSemi = newlineSkipped; auto preSkip = skipNL; skipNL = false; while (SeeingAny({TokenKind::SEMI, TokenKind::NL})) { hasNLOrSemi = true; Next(); } skipNL = preSkip; return hasNLOrSemi; } /** Skip all overloading operator and return the operator's token. */ Token SkipAndReturnOverloadingOperator(); /// Seeing IfAvailable Expr bool SeeingIfAvailable() { if (!Seeing(TokenKind::AT)) { return false; } // Get annotation identifier. auto tokens = lexer->LookAheadSkipNL(1); return !tokens.empty() && tokens.begin()->kind == TokenKind::IDENTIFIER && tokens.begin()->Value() == IF_AVAILABLE; } /// A valid ifAvailable expression has the following form. /// @IfAvailable(paramName: arg, lambda1, lambda2) OwnedPtr ParseIfAvailable(); bool SeeingDecl() { if (SeeingIfAvailable()) { return false; } return (SeeingModifier() && !Seeing({TokenKind::UNSAFE, TokenKind::LCURL})) || SeeingAny({TokenKind::FUNC, TokenKind::LET, TokenKind::VAR, TokenKind::ENUM, TokenKind::TYPE, TokenKind::STRUCT, TokenKind::CLASS, TokenKind::INTERFACE, TokenKind::MAIN}) || (SeeingBuiltinAnnotation() && !SeeingAnnotationLambdaExpr()); } bool SeeingPrimaryIdentifer(); bool SeeingPrimaryCtorDecl(const ScopeKind& scopeKind) { if (Utils::NotIn(scopeKind, {ScopeKind::STRUCT_BODY, ScopeKind::CLASS_BODY})) { return false; } return Seeing(TokenKind::INIT) || SeeingPrimaryIdentifer(); } bool SeeingPrimaryConstructor(const ScopeKind& scopeKind) { return (scopeKind == ScopeKind::STRUCT_BODY || scopeKind == ScopeKind::CLASS_BODY) && SeeingPrimaryIdentifer(); } bool SeeingPropMember(const ScopeKind& scopeKind) { return scopeKind == ScopeKind::PROP_MEMBER_SETTER_BODY || scopeKind == ScopeKind::PROP_MEMBER_GETTER_BODY; } bool SeeingPropMember() { return Seeing(TokenKind::IDENTIFIER) && (lookahead == "get" || lookahead == "set"); } bool SeeingEnumConstructor(const ScopeKind& scopeKind) { return (Seeing(TokenKind::IDENTIFIER) || SeeingContextualKeyword()) && (scopeKind == ScopeKind::ENUM_BODY || scopeKind == ScopeKind::ENUM_CONSTRUCTOR); } bool SeeingFinalizer() { if (Seeing({TokenKind::BITNOT, TokenKind::INIT}, false)) { return true; } return false; } bool SeeingIllegalDeclInBlock() { // Other Decls that illegal in FuncBody. return SeeingAny({TokenKind::CLASS, TokenKind::INIT, TokenKind::INTERFACE, TokenKind::EXTEND, TokenKind::STRUCT, TokenKind::PROP}) || SeeingImport2(); } // When a children ast is broken, the father ast will be broken. Only used on diagnostic. void SpreadAttrAndConsume(Ptr source, Ptr target, std::vector&& kind); bool CanMatchBracketInStack(); void SkipPairedBrackets(); /* * Common part of all consume strategies. */ bool ConsumeCommon(bool& flag); /* * Control if consume target token. */ void TargetTokenConsumedControl(bool& flag, bool targetTokenConsumed); /* * Consume tokens until start of declaration. */ void ConsumeUntilDecl(TokenKind kind = TokenKind::SENTINEL); /* * Consume tokens until start of declaration. */ void ConsumeUntilDeclOrNL(TokenKind kind = TokenKind::SENTINEL); /* * Consume tokens until target token, and if newline consumed the skipNL will be true. */ void ConsumeUntil(TokenKind kind, bool targetTokenConsumed = true); /* * Consume tokens until target tokens, and if newline consumed the skipNL will be true. * If token NL is among target tokens, it will be always consumed. */ void ConsumeUntilAny(std::vector&& tokens, bool targetTokenConsumed = true); void ConsumeUntilAny(const std::function& functor, bool targetTokenConsumed = true); bool TryConsumeUntilAny(std::vector tokens); template static OwnedPtr MakeInvalid(Position pos) { static_assert(std::is_convertible_v, "can only make invalid node inherited from AST::Node"); auto n = MakeOwned(); n->EnableAttr(AST::Attribute::IS_BROKEN); n->begin = pos; n->end = pos; return n; } std::vector> ParseForeignDecls( const std::set& modifiers, PtrVector& annos); void AssignCurFile(const OwnedPtr& file) const; void ParseWhenModifierHandler(std::vector>& ret, std::vector>& annos); OwnedPtr ParsePackageHeader(std::set&& modifiers); // return: seeing end of file. bool ParsePackageHeaderEnd(); void ParseTopLevelDecls(AST::File& file, std::vector>& annos); void ParseTopLevelDecl(AST::File& file, std::vector>& annos); void ParseAnnotations(PtrVector& annos); OwnedPtr ParseCustomAnnotation(); OwnedPtr ParseAnnotation(); void ParseModifiers(std::set& modifiers); OwnedPtr ParseMainDecl( ScopeKind scopeKind, const std::set& modifiers, std::vector> annos); OwnedPtr ParseVarWithPatternDecl( ScopeKind scopeKind, const std::set& modifiers, const Token& keyToken); OwnedPtr ParseIrrefutablePattern( const std::set& attributes, bool isVar = false, bool inDecl = false); OwnedPtr ParseVarOrLetOrConst(ScopeKind scopeKind, const std::set& modifiers, std::vector> annos, const Token& keyToken); OwnedPtr ParseVarOrLet(ScopeKind scopeKind, const std::set& modifiers, std::vector> annos = {}); OwnedPtr ParseConstVariable( ScopeKind scopeKind, const std::set& modifiers, std::vector> annos); void ImplementConsumeStrategy(ScopeKind sc); OwnedPtr ParsePropDecl(ScopeKind scopeKind, const std::set& modifiers, std::vector> annos = {}); void DiagMissingPropertyBody(AST::PropDecl& prop); void ParsePropBody(const std::set& modifiers, AST::PropDecl& propDecl); OwnedPtr ParsePropMemberDecl(const std::set& modifiers); void ParseFuncGenericConstraints(const AST::FuncBody& fb); void ParsePropMemberBody(const ScopeKind& scopeKind, AST::FuncBody& fb); void ParseFuncParameters(const ScopeKind& scopeKind, AST::FuncBody& fb); OwnedPtr ParseMacroDecl(ScopeKind scopeKind, const std::set& modifiers, std::vector> annos = {}); bool ParseMacroCallTokens(TokenKind left, std::vector& tokens); bool ParseMacroCallTokens(const TokenKind& left, const TokenKind& right, std::vector& tokens, bool isAttr); bool ParseMacroCallEscapeTokens(const TokenKind& left, std::vector& tokens, bool isAttr); void ParseNodeToTokens(const AST::Node& node, const Token& begin, std::vector& tokens); void ParseMacroCallAttr(AST::MacroInvocation& invocation); void ParseMacroCallArg( ScopeKind scopeKind, const std::set& modifiers, AST::MacroInvocation& invocation); void ParseMacroCallArgsWithParen(AST::MacroInvocation& invocation); void ParseMacroCallArgsWithoutParen( ScopeKind scopeKind, const std::set& modifiers, AST::MacroInvocation& invocation); void ParseMacroCallDeclInput( ScopeKind scopeKind, const std::set& modifiers, AST::MacroInvocation& invocation); void ParseMacroInvocation( ScopeKind scopeKind, const std::set& modifiers, AST::MacroInvocation& invocation); bool ParseMacroCallIdentifier(AST::MacroInvocation& invocation, AST::Node& node); template OwnedPtr ParseMacroCall(ScopeKind scopeKind = ScopeKind::UNKNOWN_SCOPE, const std::set& modifiers = {}, std::vector> annos = {}); OwnedPtr ParseFuncBody(ScopeKind scopeKind); void CheckMacroParamType(AST::Type& type, bool isReturnTy = false); OwnedPtr ParseMacroBody(AST::MacroDecl& macro); OwnedPtr ParseMacroParameterList(); void ParseEllipsisParameter(AST::FuncParam& funcParam); void ParseParameter(ScopeKind scopeKind, AST::FuncParam& fp); void ParseAssignInParam(AST::FuncParam& fp); OwnedPtr ParseParameterList(ScopeKind scopeKind = ScopeKind::UNKNOWN_SCOPE); void CheckSetterAnnotations(std::vector>& annos, const Ptr setter); void CheckGetterAnnotations(std::vector>& annos, const Ptr getter); void CheckModifierInParamList( const ScopeKind& scopeKind, const std::set& modifiers, bool isMember, AST::FuncParam& fp); void ParseLetOrVarInParamList(const ScopeKind& scopeKind, AST::FuncParam& fp, Position& memberStartPos); template void CheckIntrinsicFunc(T& fd); OwnedPtr ParseParamInParamList( const ScopeKind& scopeKind, Ptr& meetNamedParameter, Ptr& meetMemberParams); void ParseInterfaceDeclOrClassDeclGeneric(AST::InheritableDecl& ret); void SetDefaultFunc(ScopeKind scopeKind, AST::Decl& decl) const; void ParseCaseBody(AST::EnumDecl& enumDecl); void ParseEnumBody(AST::EnumDecl& enumDecl); OwnedPtr ParseEnumConstructorWithArgs(const Token& id, PtrVector& annos); OwnedPtr ParseNoArgsEnumConstructor(const Token& id, PtrVector& annos); OwnedPtr ParseStructBody(AST::StructDecl& sd); void ParseStructInheritedTypes(AST::StructDecl& structDecl); void ParseExtendedType(AST::ExtendDecl& extendDecl); void ParseExtendBody(AST::ExtendDecl& ed); OwnedPtr ParseRefType(bool onlyRef = false); OwnedPtr ParseBaseType(); OwnedPtr ParseQualifiedType(); OwnedPtr ParseVarrayType(); // Processes paren-related types, such as ParentType, TupleType, FuncType, and CFuncType. OwnedPtr ParseTypeWithParen(); OwnedPtr ParseTupleType( std::vector> types, const Position lParenPos, const Position rParenPos) const; OwnedPtr ParseParenType( const Position& lParenPos, const Position& rParenPos, OwnedPtr type); OwnedPtr ParseFuncType( std::vector> types, const Position& lParenPos, const Position& rParenPos); OwnedPtr ParsePrefixType(); OwnedPtr ParseGenericParamDecl(); OwnedPtr ParseGeneric(); /* return type is `>>` * 1. `bool` indicates whether source input `<...>` is (recognized as) a `generic arg list` * 2. vector contains parsed `Type` */ std::pair>> ParseTypeArguments(ExprKind ek = ExprKind::UNKNOWN_EXPR); bool TypeArgsMaybeConfusedWithExprWithComma(const std::vector>& typeArgs) const; bool TypesMaybeConfusedWithExprWithComma(const std::vector>& types) const; bool IsLegFollowForGenArgInExprWithComma(ExprKind ek = ExprKind::ALL); std::vector> ParseGenericConstraints(); bool ParseGenericUpperBound(const OwnedPtr& genericConstraint); static bool HasModifier(const std::set& modifiers, TokenKind tk); static bool HasAnnotation(const std::vector>& annos, AST::AnnotationKind ak); bool IsLeftValueExpression(const OwnedPtr& expr, const Token& tok); bool IsLeftAuxExpression(const OwnedPtr& expr, const Token& tok); bool CheckMacroExprRules(const Token& pre, const Token& tok, AST::Expr& expr); OwnedPtr MakeOperatorExpr(OwnedPtr& lExpr, const Token& oTok); bool IsNoneAssociative(const Token& tok) const; /// Register \param rExpr as part of \param expr, taking its memory and set necessary values. /// This should be called on right expr of a binary expr, or initializer of an initialization/assignment expr, /// for example. void RegisterRightExpr(const OwnedPtr& expr, OwnedPtr&& rExpr); void SetUnsafe(Ptr node, const std::set& modifiers = {}); void ParseIncOrDec(OwnedPtr& baseExpr); OwnedPtr ParseUnaryExpr(ExprKind ek = ExprKind::ALL); /// Parse UnaryExpr, PrefixExpr, PostfixExpr, or AtomExpr OwnedPtr ParseBaseExpr(OwnedPtr expr = nullptr, ExprKind ek = ExprKind::ALL); // Expr means base. Some ast need to parse base outside and register to this api. OwnedPtr ParseExpr(const Token& preT, OwnedPtr expr = nullptr, ExprKind ek = ExprKind::ALL); void ParseExprWithRightExprOrType(OwnedPtr& base, const Token& tok, ExprKind ek); static bool IsConditionExpr(ExprKind ek); OwnedPtr ParseIndexAccess(); OwnedPtr ParseLetPattern(ExprKind ek); OwnedPtr ParseSubscriptExpr(OwnedPtr baseExpr); OwnedPtr ParseMemberAccess(OwnedPtr baseExpr, ExprKind ek = ExprKind::ALL); OwnedPtr ParseCallExpr(OwnedPtr baseExpr); AST::SuffixKind ParseSuffix(OwnedPtr& baseExpr); void ParseBaseExprPostfix(OwnedPtr& baseExpr, ExprKind ek = ExprKind::ALL); OwnedPtr ParseOptionalExpr( const Position questPos, OwnedPtr baseExpr, AST::SuffixKind suffix) const; void ParseQuestSuffixExpr(OwnedPtr& expr); OwnedPtr ParseAtom(ExprKind ek = ExprKind::ALL); bool IsNeedToCreateOptionalChain(TokenKind token, AST::Expr& expr) const; /** * Parses expressions starting with '(' * * - ParenExpr -- parenthesized expressions * - UnitExpr -- the 0-tuple '()' * - TupleLiteral -- a tuple */ OwnedPtr ParseLeftParenExpr(); OwnedPtr ParseLeftParenExprInKind(ExprKind ek); OwnedPtr ParseTupleLitForParenExpr(const Position& leftParenPos); OwnedPtr ParseTupleLitForParenExprComma(const Position& leftParenPos, OwnedPtr expr); /** * Parses expressions starting with '[': array literals. like: [1, 2, 3] */ OwnedPtr ParseArrayLitExpr(); OwnedPtr ParseTypeConvExpr(); OwnedPtr ParseBreakJumpExpr(); OwnedPtr ParseContinueJumpExpr(); OwnedPtr ParseLitConst(); OwnedPtr ParseNegativeLiteral(); OwnedPtr ParseInterpolationExpr(const std::string& value, const Position& pos); OwnedPtr GetLitConstExprFromStr( const std::string& value, const Token& token, const Position& pos) const; /** * Combines zero or more string fragments into a concatenation of these fragments. String fragments are * either literal strings, or string interpolations, arbitrary expressions wrapped in code that converts * that expression's result to a string. */ OwnedPtr ProcessStringInterpolation(const Token& token); OwnedPtr ParseTrailingClosureExpr(OwnedPtr baseExpr); OwnedPtr ParseMacroExprOrLambdaExpr(); OwnedPtr ParseAnnotationLambdaExpr(bool isTailClosure = false); OwnedPtr ParseFuncParamListInLambdaExpr(); OwnedPtr ParseFuncParam(); OwnedPtr ParseFuncBodyInLambdaExpr(bool isTailClosure = false); OwnedPtr ParseLambdaExpr(); OwnedPtr ParseVArrayExpr(); OwnedPtr ParseLambdaExprWithTrailingClosure(); OwnedPtr ParseIfExpr(); void ParseElse(AST::IfExpr& ret); void ConsumeUntilIfExprEnd(); /// Check if the given condition is has a condition subclause. /// Side Effects: if a non &&, || is used to connect to conditions, emit a diagnostic. /// \returns whether this condition contains a condition subclause. The return value is only meant to be used /// recursively, and it DOES NOT mean whether the condition is good or not. bool CheckCondition(AST::Expr* e); OwnedPtr ParseThrowExpr(); OwnedPtr ParsePerformExpr(); OwnedPtr ParseResumeExpr(); // Parse 'this' or 'super'. OwnedPtr ParseThisOrSuper() const; OwnedPtr ParseReturnExpr(); OwnedPtr ParseQuoteExpr(); void ParseQuoteTokens(AST::QuoteExpr& qe); void ParseQuoteDollarInterpolationWithParen(AST::QuoteExpr& qe); void ParseQuoteDollarInterpolation(AST::QuoteExpr& qe); void ParseQuoteEscapeToken(std::vector& tokens); void ParsePatternsInCase(const OwnedPtr& matchCase, const AST::MatchExpr& matchExpr); void ParseMatchCases(AST::MatchExpr& matchExpr); void ParseMatchNoSelector(AST::MatchExpr& matchExpr); OwnedPtr ParseMatchExpr(); /** * Parses zero or more expressions or declarations in a match expression. This is what can occur to the * right of a '=>' in a match expression. This function parses but ignores any semicolons occurring before * each expression or declaration. It stops before either a 'case' token (indicating the next case), or * a '}' right curly brace (indicating the end of the match expression); it doesn't consume either of * those tokens. If this function finds that there were no expressions or declarations before the * terminator, it registers a non-fatal compiler diagnostic. */ OwnedPtr ParseExprOrDeclsInMatchCase(); /// \param modifiers already parsed modifiers, to be attached to the returned VarDecl OwnedPtr ParseVarDecl( const ScopeKind& scopeKind, const std::set& modifiers, const Token& keyToken); OwnedPtr ParseTryExpr(); void ParseHandleBlock(AST::TryExpr& tryExpr); void ParseCatchBlock(AST::TryExpr& tryExpr); void ParseTryWithResource(const ScopeKind& scopeKind, AST::TryExpr& tryExpr); OwnedPtr ParseExceptTypePattern(); OwnedPtr ParseCommandTypePattern(); OwnedPtr ParseForInExpr(); OwnedPtr ParseRefExpr(ExprKind ek = ExprKind::ALL); OwnedPtr ParseWildcardExpr(); OwnedPtr ParseWhileExpr(); OwnedPtr ParseDoWhileExpr(); OwnedPtr ParseExprOrDecl(ScopeKind sk); OwnedPtr ParseExpressionOrDeclarations(ScopeKind sk); OwnedPtr ParseBlock(ScopeKind scopeKind = ScopeKind::UNKNOWN_SCOPE); OwnedPtr ParseUnsafeBlock(); bool DiagForBlock(const AST::Block& block); template bool CheckSkipRcurOrPrematureEnd(T& ret); OwnedPtr ParseClassBody(AST::ClassDecl& cd); OwnedPtr ParseInterfaceBody(AST::InterfaceDecl& id); OwnedPtr ParseEnumConstructor( const std::set& modifiers, PtrVector& annos); OwnedPtr ParseTypePattern(const Position& begin); OwnedPtr ParseConstPattern(); OwnedPtr ParseTypePatternOrVarOrEnumPattern( const std::set& attributes, bool isVar, bool inDecl); OwnedPtr ParseVarPattern(const std::set& attributes, const SrcIdentifier& identifier, const Position& begin, bool isVar) const; OwnedPtr ParseVarOrEnumPattern( const std::string& identifier, const Position& begin, size_t len, bool isRawId) const; OwnedPtr ParseEnumPattern(const std::set& attributes, bool isVar, bool inDecl); OwnedPtr ParseTuplePattern(bool isEnumPatternParams = false, const std::set& attributes = {}, bool isVar = false, bool inDecl = false); OwnedPtr ParseSpawnExpr(); OwnedPtr ParseSynchronizedExpr(); void ParseFeatureDirective(OwnedPtr& features); bool ParseFeatureId(OwnedPtr& features); void ParseCommonImportSpec(PtrVector& imports, PtrVector& annos); void CheckImportSpec(PtrVector& imports); void CheckTypeArgumentsInEnumPattern(Ptr enumPattern); void ParseImportSpec(PtrVector& imports, const PtrVector& annos); void DesugarImportMulti(PtrVector& imports, AST::ImportSpec& import) const; void ParseImportContent(AST::ImportContent& content); /** * Return true if this is a complete import-single or import-all (considerless the possible alias), * return false if skipped a '{'. */ bool ParseImportSingle(AST::ImportContent& content, bool inMultiImport = false); void ParseImportMulti(AST::ImportContent& content); void ParseImportAliasPart(AST::ImportContent& content); void ParseImportSpecInTop(PtrVector& imports, PtrVector& annos); std::vector> TryParseIdentifierList( AST::Expr& expr, const std::string& attrName, const Position& pos); bool SeeingBuiltinAnnotation(); bool SeeingAtWhen(); void ParseAttributeAnnotation(AST::Annotation& anno); void ParseOverflowAnnotation(AST::Annotation& anno); void ParseWhenAnnotation(AST::Annotation& anno); void ParseAnnotationArguments(AST::Annotation& anno); void ValidateDeprecatedAnnotationArgument(const Ptr lce, const std::string& name, const AST::LitConstKind& expectedKind, bool& isArgumentFound); void CheckDeprecatedAnnotation(const AST::Annotation& anno); void CheckDeclarationInScope(ScopeKind sk, DefKind dk); bool SeeingMacroCall(); /// Returns true when seeing macro call before a decl. /// It can be either @Foo[...] class/func ... /// Or @!Foo[...] class/func ... /// Note that call to \ref ParserImpl::SeeingMacroCall() would always return true if this function returns true at /// some point, so no point in writing `SeeingMacroCall() || SeeingMacroCallDecl()` bool SeeingMacroCallDecl(); void ParseOneOrMoreWithSeparator( TokenKind separator, std::vector& positions, const std::function& parseElement); void ParseOneOrMoreWithSeparator(TokenKind separator, const std::function& storeSeparator, const std::function& parseElement); /// Parse one or more via \ref parse, separated by \ref separator, and allows trailing separator. void ParseOneOrMoreSepTrailing(std::function&& storeSeparator, std::function&& parseElement, TokenKind end, TokenKind separator = TokenKind::COMMA); /// Parse zero or more via \ref parse, separated by \ref separator, and allows trailing separator. /// Note that trailing comma is invalid if there are zero elements. void ParseZeroOrMoreSepTrailing(std::function&& storeSeparator, std::function&& parseElement, TokenKind end, TokenKind separator = TokenKind::COMMA); void ParseZeroOrMoreWithSeparator(TokenKind separator, const std::function& storeSeparator, const std::function& parseElement, TokenKind terminator); void CheckOverflowAnno( std::vector>& annos, ScopeKind scopeKind = ScopeKind::UNKNOWN_SCOPE); void CheckAnnotationAnno(PtrVector& annos, std::set modifiers); void CheckAnnotationAnno(PtrVector& annos, const OwnedPtr& decl); std::set CheckDeclModifiers( const std::set& modifiers, ScopeKind scopeKind, DefKind defKind); AST::Attribute GetModifierAttr(const OwnedPtr& modifier, AST::ASTKind kind) const; void SetDeclBeginPos(AST::Decl& decl) const; void CheckFuncBody(ScopeKind scopeKind, AST::FuncDecl& decl); void CheckVariableParams(AST::FuncDecl& decl); void CheckVariableParams(AST::PrimaryCtorDecl& decl); void CheckBaseOfTrailingClosureExpr(const OwnedPtr& baseExpr); template void ParseFuncDeclAnnos(std::vector>& annos, T& funcDecl); // Move this to sema. void CheckNoDeprecatedAnno(const PtrVector& annos, const std::string& invalidTarget); void CheckDeprecationOfFuncParam(const AST::FuncParam& param); std::string GetSingleLineContent(const Position& begin, const Position& end) const; void DiagExpectedIdentifier(const Range& range, const std::string& expectedName = "a name", const std::string& afterName = "after this", const bool callHelp = true); void DiagExpectedIdentifierWithNode(Ptr node); void DiagImportingByPackageNameIsNotSupported(const Position& expectPos); void DiagNameLengthOverflow(const Range& r, const std::string& tar, size_t maxLength, size_t realLength); void DiagIllegalModifierInScope(const AST::Modifier& mod); void DiagConflictedModifier(const AST::Modifier& resMod, const AST::Modifier& tarMod); void DiagRedundantModifiers(const AST::Modifier& modifier); void DiagRedundantModifiers(const AST::Modifier& lowerModifier, const AST::Modifier& higherModifier); void DiagExpectNoModifier(const AST::Modifier& mod); void DiagExpectNoModifierBefore(const AST::Modifier& mod, const std::string& str); void DiagUnexpectedAnnoOn( const AST::Annotation& anno, const Position& onPos, const std::string& annoStr, const std::string& onStr); void DiagUnexpectedAnnoOnKind( const AST::Annotation& anno, const Position& onPos, const std::string& annoStr, AST::ASTKind kind); void DiagUnexpectedWhenON(PtrVector& annos); void DiagUnexpectedDeclInScope(ScopeKind sk); void DiagExpectedMoreFieldInTuplePattern(); void DiagExpectedTypeNameAfterAs(const Token& tok); void DiagExpectedTypeName(); void DiagInvalidLeftHandExpr(const AST::Expr& expr, const Token& tok); void DiagNoneAssociativeOp(const Token& preT, const Token& tok); void DiagInvalidInheritType(const AST::Type& type); void DiagExpectedExpression(); /** * Working with Left and Right Paired Symbols, eg. '(' and ')' * @param del left symbol * @param pos position of left symbol */ void DiagExpectedRightDelimiter(const std::string& del, const Position& pos); void DiagInvalidIncreExpr(const AST::Expr& expr); void DiagInvalidMacroExpandExpr(const Token& tok, const AST::MacroExpandExpr& expr); void DiagUnrecognizedNodeAfterMacro(const Token& tok, const AST::MacroExpandExpr& expr); void DiagChainedAsExpr(AST::Expr& expr, const Token& tok); void DiagExpecetedOpeOrEnd(); void DiagExpectedNoNewLine(); void DiagCannotHaveAssignmentInInit(const AST::Expr& expr); void DiagOrPattern(); void DiagDeclarationInMacroPackage(const OwnedPtr& decl); void DiagIllegalFunc(const OwnedPtr& funcDecl); void DiagParseExpectedParenthis(const OwnedPtr& postType); void DiagParseIllegalDeclarationPattern(const OwnedPtr& decl, ScopeKind scopeKind); void DiagThisTypeNotAllow(); void DiagInvalidUnicodeScalar(const Position& startPos, const std::string& str); void DiagExpectedLiteral(const Position& subPos); void DiagTypePatternInLetCondExpr(const AST::Pattern& pat, ExprKind ek); void DiagExpectedLeftParenAfter(const Position& pos, const std::string& str); void DiagMatchCaseExpectedExprOrDecl(); void DiagMatchCaseBodyCannotBeEmpty(const Position& pos); void DiagExpectedCatchOrHandleOrFinallyAfterTry(const AST::TryExpr& te); void DiagExpectedSelectorOrMatchExprBody(const Position& pos); void DiagRedefinedResourceName( const std::pair& cur, const std::pair& pre); void DiagExpectedNoArgumentsInSpawn(const std::vector>& params, const Position& pos); void DiagExpectCharacter(const std::string& expectStr, const std::string& noteStr = ""); void DiagExpectCharacter(const Position& pos, const std::string& expectStr, const std::string& noteStr = ""); void DiagExpectSemiOrNewline(); void DiagExpectPublicBeforeMacroCall(const OwnedPtr& md); void DiagExpectMacroParamType(const AST::Type& type); void DiagMacroUnexpectNamedParam(const OwnedPtr& param); void DiagInvalidOverloadedOperator(); void DiagRedundantArrowAfterFunc(const AST::Type& type); void DiagExpectedDeclaration(ScopeKind scopeKind); void DiagExpectedDeclaration(const Position& pos, const std::string& str); void DiagUnExpectedModifierOnDeclaration(const AST::Decl& vd); void DiagConstVariableExpectedStatic(const Token& key); void DiagConstVariableExpectedInitializer(AST::Decl& vd); void DiagExpectedOneOfTypeOrInitializer(const AST::Decl& vd, const std::string& str); void DiagExpectedTypeOrInitializerInPattern(const AST::Decl& vd); void DiagExpectedInitializerForToplevelVar(const AST::Decl& vd); void DiagExpectedIdentifierOrPattern(bool isVar, const Position& pos, bool isConst = false); void DiagExpectedGetOrSetInProp(const Position& pos); void DiagDuplicatedGetOrSet(const AST::Node& node, const AST::PropDecl& pd); void DiagUnknownPrimaryConstructor(const std::string& str); void DiagExpectedName(const std::string& str, const std::string& afterName); void DiagGetOrSetCannotBeGeneric(const std::string& str, const AST::Generic& ge); void DiagUnexpectedWhere(const Token& token); void DiagDuplicatedIntrinsicFunc(const AST::Decl& decl, const AST::Decl& prev); void DiagMissingBody(const std::string& str, const std::string& name, const Position& pos); void DiagDeclCannotInheritTheirSelf(const AST::InheritableDecl& decl, const AST::RefType& rt); void DiagUnsafeWillBeIgnored(const AST::Modifier& mod); void DiagDuplicatedModifier(const AST::Modifier& mod, const AST::Modifier& pre); void DiagNamedParameterAfterUnnamed(const AST::FuncParam& param, const AST::FuncParam& pre); void DiagMemberParameterAfterRegular(const AST::FuncParam& param, const AST::FuncParam& pre); void DiagUnexpectedColonInRange(const AST::RangeExpr& re); void DiagDuplicatedAttiValue(const std::string& attr, const Position& pos, const Position& pre); void DiagDuplicatedItem(const std::string& name, const std::string& sourceName, const Position& errPos, const Position& prePos, const std::string& attachment = ""); void DiagUnrecognizedAttrInAnno(const AST::Annotation& anno, const std::string& attrName, const Position& pos, const std::set& acs); void DiagDuplicatedAnno(const AST::Annotation& anno, const AST::Annotation& pre); void DiagExpectedLsquareAfter(const AST::Node& node, const std::string& aName, const std::string& note); // @Deprecated void DiagDeprecatedArgumentNotLitConst(const AST::Node& node); void DiagDeprecatedArgumentDuplicated(const AST::Node& node, const std::string& parameterName); void DiagDeprecatedWrongArgumentType( const AST::Node& node, const std::string& paramName, const std::string& expectedType); void DiagDeprecatedUnknownArgument(const AST::Node& arg, const std::string& name); void DiagDeprecatedEmptyStringArgument(const AST::Node& node, const std::string& paramName); void DiagDeprecatedInvalidTarget(const AST::Node& node, const std::string& invalidTarget); void DiagUnrecognizedExprInWhen(const AST::Expr& expr, const AST::Annotation& when); void DiagUnexpectedTypeIn( const AST::Type& type, const Position& inPos, const std::string& inStr, const std::string& noteStr); void DiagVArrayTypeArgMismatch(const Range& range, const std::string& note); bool CheckIfSeeingDecl(const ScopeKind& scopeKind) { if (SeeingIfAvailable()) { return false; } return SeeingDecl() || SeeingMacroCallDecl() || SeeingIllegalDeclInBlock() || SeeingPrimaryCtorDecl(scopeKind) || SeeingEnumConstructor(scopeKind); } void SkipSemi() { SkipBlank(TokenKind::SEMI); } void RevertPrimaryDecl() { curPrimaryDeclIdent = lastPrimaryDeclIdent; curPrimaryDeclIdentIsRaw = lastPrimaryDeclIdentIsRaw; }; void ParseConstraints(const AST::Decl& decl); void ParseInheritedTypes(AST::InheritableDecl& decl); // ASTLoader. OwnedPtr ParseFuncDecl( ScopeKind scopeKind, const std::set& modifiers, std::vector> annos); OwnedPtr ParseEnumDecl(ScopeKind scopeKind, const std::set& modifiers, std::vector> annos = {}); OwnedPtr ParseStructDecl(ScopeKind scopeKind, const std::set& modifiers, std::vector> annos = {}); OwnedPtr ParseExtendDecl(ScopeKind scopeKind, const std::set& modifiers, std::vector> annos = {}); OwnedPtr ParseTypeAlias(ScopeKind scopeKind, const std::set& modifiers, std::vector> annos = {}); OwnedPtr ParseClassDecl(ScopeKind scopeKind, const std::set& modifiers, std::vector> annos = {}); OwnedPtr ParseInterfaceDecl(ScopeKind scopeKind, const std::set& modifiers, std::vector> annos = {}); OwnedPtr ParsePrimaryConstructor(ScopeKind scopeKind, const std::set& modifiers, std::vector> annos = {}); OwnedPtr ParseFinalizer( ScopeKind scopeKind, const std::set modifiers, PtrVector annos = {}); OwnedPtr ParseConstructor(ScopeKind scopeKind, const std::set& modifiers, std::vector> annos = {}); OwnedPtr ParseFuncArg(); OwnedPtr ParseAnnotationArgument(); std::set GetModifierAttrs(const ScopeKind& scopeKind, std::unordered_map>& scopeRules, const std::unordered_map>& scopeWarningRules, const std::vector>& modifiersVec); void ReportModifierWarning(ScopeKind scopeKind, const std::unordered_map>& scopeWarningRules, const std::vector>& modifiers); void ParseTypeAndExpr(Ptr const ret); void DiagMatchCase(DiagnosticBuilder& builder); OwnedPtr GetInvalidExprInAtom(Position pos); OwnedPtr ParseTypeParameterInTupleType(std::unordered_map& typeNameMap); OwnedPtr ParseBaseLambdaExpr(); std::unordered_map)>> diagExpectedIdentifierMap{ {AST::ASTKind::ENUM_DECL, &ParserImpl::DiagExpectedIdentifierEnumDecl}, {AST::ASTKind::GENERIC, &ParserImpl::DiagExpectedIdentifierGeneric}, {AST::ASTKind::GENERIC_CONSTRAINT, &ParserImpl::DiagExpectedIdentifierGenericConstraint}, {AST::ASTKind::IMPORT_CONTENT, &ParserImpl::DiagExpectedIdentifierImportContent}, {AST::ASTKind::IMPORT_SPEC, &ParserImpl::DiagExpectedIdentifierImportSpec}, {AST::ASTKind::FEATURES_DIRECTIVE, &ParserImpl::DiagExpectedIdentifierFeatureDirective}, {AST::ASTKind::PACKAGE_SPEC, &ParserImpl::DiagExpectedIdentifierPackageSpec}, {AST::ASTKind::PROP_DECL, &ParserImpl::DiagExpectedIdentifierPropDecl}, {AST::ASTKind::REF_TYPE, &ParserImpl::DiagExpectedIdentifierRefType}, {AST::ASTKind::FUNC_PARAM, &ParserImpl::DiagExpectedIdentifierFuncParam}, {AST::ASTKind::QUALIFIED_TYPE, &ParserImpl::DiagExpectedIdentifierQualifiedType}, {AST::ASTKind::MEMBER_ACCESS, &ParserImpl::DiagExpectedIdentifierMemberAccess}, {AST::ASTKind::MACRO_DECL, &ParserImpl::DiagExpectedIdentifierMacroDecl}, {AST::ASTKind::FUNC_BODY, &ParserImpl::DiagExpectedIdentifierFuncBody}, {AST::ASTKind::FUNC_DECL, &ParserImpl::DiagExpectedIdentifierFuncDecl}, {AST::ASTKind::CLASS_DECL, &ParserImpl::DiagExpectedIdentifierClassDecl}, {AST::ASTKind::INTERFACE_DECL, &ParserImpl::DiagExpectedIdentifierInterfaceDecl}, {AST::ASTKind::STRUCT_DECL, &ParserImpl::DiagExpectedIdentifierStructDecl}, {AST::ASTKind::TYPE_ALIAS_DECL, &ParserImpl::DiagExpectedIdentifierTypeAliasDecl}, {AST::ASTKind::MACRO_EXPAND_DECL, &ParserImpl::DiagExpectedIdentifierMacroExpandDecl}, {AST::ASTKind::MACRO_EXPAND_EXPR, &ParserImpl::DiagExpectedIdentifierMacroExpandExpr}, }; void DiagExpectedIdentifierEnumDecl(Ptr node); void DiagExpectedIdentifierGeneric(Ptr node); void DiagExpectedIdentifierGenericConstraint(Ptr node); void DiagExpectedIdentifierImportContent(Ptr node); void DiagExpectedIdentifierImportSpec(Ptr node); void DiagExpectedIdentifierFeatureDirective(Ptr node); void DiagExpectedIdentifierPackageSpec(Ptr node); void DiagExpectedIdentifierPropDecl(Ptr node); void DiagExpectedIdentifierRefType(Ptr); void DiagExpectedIdentifierFuncParam(Ptr); void DiagExpectedIdentifierQualifiedType(Ptr node); void DiagExpectedIdentifierMemberAccess(Ptr node); void DiagExpectedIdentifierMacroDecl(Ptr node); void DiagExpectedIdentifierFuncBody(Ptr node); void DiagExpectedIdentifierFuncDecl(Ptr node); void DiagExpectedIdentifierClassDecl(Ptr node); void DiagExpectedIdentifierInterfaceDecl(Ptr node); void DiagExpectedIdentifierStructDecl(Ptr node); void DiagExpectedIdentifierTypeAliasDecl(Ptr node); void DiagExpectedIdentifierMacroExpandDecl(Ptr node); void DiagExpectedIdentifierMacroExpandExpr(Ptr node); void CheckLeftExpression(const Token& preT, const OwnedPtr& base, const Token& tok); void CheckWildcardInExpr(const OwnedPtr &root); void CheckVarDeclModifiers( std::set modifiers, Ptr varDecl, ScopeKind scopeKind, const Token& keyToken); void DiagAnnotationExpectsOneArgument(const AST::Annotation& node, const std::string& annotationName); void DiagAnnotationExpectsOneArgument(const AST::Annotation& node, const std::string& annotationName, const std::string& argInfo); void DiagAnnotationMoreThanOneArgs(const AST::Annotation& node, const std::string& annotationName); void DiagAnnotationMoreThanOneArgs(const AST::Annotation& node, const std::string& annotationName, const std::string& argInfo); // Cangjie Native & Java/ObjC FFI void CheckObjCMirrorAnnotation(const AST::Annotation& anno) const; // Check whether member decl can be abstract bool CanBeAbstract(const AST::Decl& decl, ScopeKind scopeKind) const; void CheckConstructorBody(AST::FuncDecl& ctor, ScopeKind scopeKind, bool inMacro = false); void SetMemberParentInheritableDecl(AST::InheritableDecl& ret, const OwnedPtr& decl) const; void FFICheckClassLikeFuncBody( FuncDecl& decl, DiagKindRefactor functionMustHaveReturnType, DiagKindRefactor functionCanNotHaveBody); void CheckClassLikePropAbstractness(AST::PropDecl& prop); void CheckClassLikeFuncBodyAbstractness(AST::FuncDecl& decl); void CheckJavaInteropMember(Decl& decl); void CheckObjCInteropMember(Decl& member); void CheckPropDeclJavaMirror(AST::PropDecl& decl); void CheckPrimaryCtorDeclJavaMirror(AST::PrimaryCtorDecl& ctor); void CheckMemberFuncJavaMirror(AST::FuncDecl& decl); void CheckMemberFuncObjCMirror(AST::FuncDecl& func); void CheckCJMappingAttr(Decl& decl) const; void CheckInitCtorDeclBody(AST::FuncDecl& ctor); void CheckInitCtorDeclJavaMirror(AST::FuncDecl& ctor); void CheckInitCtorDeclObjCMirror(AST::FuncDecl& ctor); void CheckVarDeclObjCMirror(AST::VarDecl& field) const; void CheckPropDeclObjCMirror(AST::PropDecl& prop); void CheckPrimaryCtorDeclObjCMirror(AST::PrimaryCtorDecl& ctor); friend class Parser; template DiagnosticBuilder ParseDiagnoseRefactor(DiagKindRefactor kind, const Position pos, Args&&... args) { auto n = MakeOwned(); n->begin = pos; n->end = pos + 1; if (curMacroCall) { n->EnableAttr(AST::Attribute::MACRO_EXPANDED_NODE); n->curMacroCall = curMacroCall; } return diag.DiagnoseRefactor(kind, *n, pos, std::forward(args)...); } template DiagnosticBuilder ParseDiagnoseRefactor(DiagKindRefactor kind, const Range range, Args&&... args) { auto n = MakeOwned(); n->begin = range.begin; n->end = range.end; if (curMacroCall) { n->EnableAttr(AST::Attribute::MACRO_EXPANDED_NODE); n->curMacroCall = curMacroCall; } return diag.DiagnoseRefactor(kind, *n, range, std::forward(args)...); } template DiagnosticBuilder ParseDiagnoseRefactor(DiagKindRefactor kind, const Token& token, Args&&... args) { auto n = MakeOwned(); n->begin = token.Begin(); n->end = token.End(); if (curMacroCall) { n->EnableAttr(AST::Attribute::MACRO_EXPANDED_NODE); n->curMacroCall = curMacroCall; } return diag.DiagnoseRefactor(kind, *n, token, std::forward(args)...); } template DiagnosticBuilder ParseDiagnoseRefactor(DiagKindRefactor kind, const AST::Node& node, Args&&... args) { auto n = MakeOwned(); n->begin = node.begin; n->end = node.end; if (curMacroCall) { n->EnableAttr(AST::Attribute::MACRO_EXPANDED_NODE); n->curMacroCall = curMacroCall; } return diag.DiagnoseRefactor(kind, *n, std::forward(args)...); } }; extern template OwnedPtr ParserImpl::ParseMacroCall( ScopeKind scopeKind, const std::set& modifiers, std::vector> annos); extern template OwnedPtr ParserImpl::ParseMacroCall( ScopeKind scopeKind, const std::set& modifiers, std::vector> annos); extern template OwnedPtr ParserImpl::ParseMacroCall( ScopeKind scopeKind, const std::set& modifiers, std::vector> annos); class ChainScope { public: ChainScope(ParserImpl& parser, Ptr node) : ref(&parser.chainedAST) { ref->push_back(node); } ~ChainScope() { ref->pop_back(); } private: std::vector>* ref; }; class ParserScope { public: explicit ParserScope(ParserImpl& parser) : ref(&parser), oldLookAhead(parser.lookahead), oldSkipToken(parser.lastToken), oldLastToken(parser.lastToken), oldLastNoneNLToken(parser.lastNoneNLToken), oldNewlineSkipped(parser.newlineSkipped) { parser.lexer->SetResetPoint(); } void ResetParserScope() { ref->lookahead = oldLookAhead; ref->lastToken = oldSkipToken; ref->lexer->Reset(); ref->lastToken = oldLastToken; ref->lastNoneNLToken = oldLastNoneNLToken; ref->newlineSkipped = oldNewlineSkipped; } ~ParserScope() = default; private: ParserImpl* ref; Token oldLookAhead; Token oldSkipToken; Token oldLastToken; Token oldLastNoneNLToken; bool oldNewlineSkipped; }; /** * All primitive type kind map. */ const std::unordered_map TOKENKIND_TO_PRIMITIVE_TYPEKIND_MAP{ {TokenKind::INT8, AST::TypeKind::TYPE_INT8}, {TokenKind::INT16, AST::TypeKind::TYPE_INT16}, {TokenKind::INT32, AST::TypeKind::TYPE_INT32}, {TokenKind::INT64, AST::TypeKind::TYPE_INT64}, {TokenKind::INTNATIVE, AST::TypeKind::TYPE_INT_NATIVE}, {TokenKind::UINT8, AST::TypeKind::TYPE_UINT8}, {TokenKind::UINT16, AST::TypeKind::TYPE_UINT16}, {TokenKind::UINT32, AST::TypeKind::TYPE_UINT32}, {TokenKind::UINT64, AST::TypeKind::TYPE_UINT64}, {TokenKind::UINTNATIVE, AST::TypeKind::TYPE_UINT_NATIVE}, {TokenKind::FLOAT16, AST::TypeKind::TYPE_FLOAT16}, {TokenKind::FLOAT32, AST::TypeKind::TYPE_FLOAT32}, {TokenKind::FLOAT64, AST::TypeKind::TYPE_FLOAT64}, {TokenKind::RUNE, AST::TypeKind::TYPE_RUNE}, {TokenKind::BOOLEAN, AST::TypeKind::TYPE_BOOLEAN}, {TokenKind::NOTHING, AST::TypeKind::TYPE_NOTHING}, {TokenKind::UNIT, AST::TypeKind::TYPE_UNIT}, }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Parse/ParserModifierRules.cpp000066400000000000000000000516651510705540100233170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares static maps of trustlist modifiers for definitions in different scope kind. SCOPE_MODIFIER_RULES * contains modifier trustlists defined under different scope kinds. The trustlist contains all allowed modifiers of the * current scope kind and modifiers that conflict with the modifier. XXX_YYY_MODIFIERS naming contains three parts. XXX * means scope kind. YYY means definition kind. */ #include "ParserImpl.h" #include "cangjie/Parse/ParseModifiersRules.h" namespace { using namespace Cangjie; // TokenKind to Attribute Map. static const std::unordered_map TK2ATTRMAP = { {TokenKind::STATIC, AST::Attribute::STATIC}, {TokenKind::PUBLIC, AST::Attribute::PUBLIC}, {TokenKind::PRIVATE, AST::Attribute::PRIVATE}, {TokenKind::INTERNAL, AST::Attribute::INTERNAL}, {TokenKind::PROTECTED, AST::Attribute::PROTECTED}, {TokenKind::COMMON, AST::Attribute::COMMON}, {TokenKind::PLATFORM, AST::Attribute::PLATFORM}, {TokenKind::OVERRIDE, AST::Attribute::OVERRIDE}, {TokenKind::REDEF, AST::Attribute::REDEF}, {TokenKind::ABSTRACT, AST::Attribute::ABSTRACT}, {TokenKind::SEALED, AST::Attribute::SEALED}, {TokenKind::OPEN, AST::Attribute::OPEN}, {TokenKind::FOREIGN, AST::Attribute::FOREIGN}, {TokenKind::UNSAFE, AST::Attribute::UNSAFE}, {TokenKind::MUT, AST::Attribute::MUT}, {TokenKind::OPERATOR, AST::Attribute::OPERATOR}, }; } // namespace namespace Cangjie { std::optional GetAttributeByModifier(TokenKind tokenKind) { auto found = TK2ATTRMAP.find(tokenKind); return found == TK2ATTRMAP.end() ? std::optional{} : std::optional{found->second}; } const static std::unordered_map> PACKAGE_MODIFIERS = { {TokenKind::PUBLIC, {TokenKind::PROTECTED, TokenKind::INTERNAL}}, {TokenKind::PROTECTED, {TokenKind::PUBLIC, TokenKind::INTERNAL}}, {TokenKind::INTERNAL, {TokenKind::PUBLIC, TokenKind::PROTECTED}}, }; #define ACCESSIBLE_MODIFIERS \ {TokenKind::PUBLIC, {TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::PRIVATE}}, \ {TokenKind::PROTECTED, {TokenKind::PUBLIC, TokenKind::INTERNAL, TokenKind::PRIVATE}}, \ {TokenKind::INTERNAL, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::PRIVATE}}, \ {TokenKind::PRIVATE, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::INTERNAL}}, const static std::unordered_map> TOPLEVEL_COMMON_MODIFIERS = { ACCESSIBLE_MODIFIERS }; #define ACCESSIBLE_MODIFIERS_TOPLEVEL \ {TokenKind::PUBLIC, {TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::PRIVATE}}, \ {TokenKind::PROTECTED, {TokenKind::PUBLIC, TokenKind::INTERNAL, TokenKind::PRIVATE}}, \ {TokenKind::INTERNAL, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::PRIVATE}}, \ {TokenKind::PRIVATE, \ {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::PLATFORM, TokenKind::COMMON}}, \ {TokenKind::COMMON, {TokenKind::PLATFORM, TokenKind::PRIVATE}}, \ {TokenKind::PLATFORM, {TokenKind::COMMON, TokenKind::PRIVATE}} const static std::unordered_map> TOPLEVEL_ENUM_MODIFIERS = { ACCESSIBLE_MODIFIERS_TOPLEVEL }; const static std::unordered_map> CLASS_BODY_VARIABLE_MODIFIERS = { ACCESSIBLE_MODIFIERS {TokenKind::STATIC, {}}, {TokenKind::CONST, {TokenKind::COMMON, TokenKind::PLATFORM}}, {TokenKind::COMMON, {TokenKind::PLATFORM, TokenKind::CONST}}, {TokenKind::PLATFORM, {TokenKind::COMMON, TokenKind::CONST}}, }; const static std::unordered_map> TOPLEVEL_STRUCT_MODIFIERS = { ACCESSIBLE_MODIFIERS_TOPLEVEL }; #define ACCESSIBLE_MODIFIERS_WITH_FOREIGN \ {TokenKind::FOREIGN, {TokenKind::PUBLIC}}, \ {TokenKind::PUBLIC, {TokenKind::FOREIGN, TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::PRIVATE}}, \ {TokenKind::PROTECTED, {TokenKind::PUBLIC, TokenKind::INTERNAL, TokenKind::PRIVATE, TokenKind::SEALED}}, \ {TokenKind::INTERNAL, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::PRIVATE, TokenKind::SEALED}}, \ {TokenKind::PRIVATE, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::SEALED, \ TokenKind::COMMON, TokenKind::PLATFORM}}, \ {TokenKind::SEALED, {TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::PRIVATE}}, const static std::unordered_map> TOPLEVEL_CLASS_MODIFIERS = { ACCESSIBLE_MODIFIERS_WITH_FOREIGN {TokenKind::ABSTRACT, {}}, {TokenKind::OPEN, {}}, {TokenKind::COMMON, {TokenKind::PLATFORM, TokenKind::PRIVATE}}, {TokenKind::PLATFORM, {TokenKind::COMMON, TokenKind::PRIVATE}}, }; const static std::unordered_map> TOPLEVEL_INTERFACE_MODIFIERS = { ACCESSIBLE_MODIFIERS_WITH_FOREIGN {TokenKind::OPEN, {}}, {TokenKind::COMMON, {TokenKind::PLATFORM, TokenKind::PRIVATE}}, {TokenKind::PLATFORM, {TokenKind::COMMON, TokenKind::PRIVATE}}, }; const static std::unordered_map> TOPLEVEL_VARIABLE_MODIFIERS = { {TokenKind::FOREIGN, {TokenKind::UNSAFE, TokenKind::PUBLIC, TokenKind::CONST, TokenKind::PLATFORM, TokenKind::COMMON}}, {TokenKind::PUBLIC, {TokenKind::FOREIGN, TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::PRIVATE}}, {TokenKind::PROTECTED, {TokenKind::PUBLIC, TokenKind::INTERNAL, TokenKind::PRIVATE}}, {TokenKind::INTERNAL, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::PRIVATE}}, {TokenKind::PRIVATE, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::PLATFORM, TokenKind::COMMON}}, {TokenKind::CONST, {TokenKind::FOREIGN, TokenKind::PLATFORM, TokenKind::COMMON}}, {TokenKind::COMMON, {TokenKind::PLATFORM, TokenKind::PRIVATE, TokenKind::CONST, TokenKind::FOREIGN}}, {TokenKind::PLATFORM, {TokenKind::COMMON, TokenKind::PRIVATE, TokenKind::CONST, TokenKind::FOREIGN}}, }; const static std::unordered_map> TOPLEVEL_FUNCDECL_MODIFIERS = { {TokenKind::FOREIGN, {TokenKind::UNSAFE, TokenKind::PUBLIC, TokenKind::CONST, TokenKind::INTERNAL, TokenKind::PROTECTED, TokenKind::PRIVATE, TokenKind::COMMON}}, {TokenKind::PUBLIC, {TokenKind::FOREIGN, TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::PRIVATE}}, {TokenKind::PROTECTED, {TokenKind::FOREIGN, TokenKind::PUBLIC, TokenKind::INTERNAL, TokenKind::PRIVATE}}, {TokenKind::INTERNAL, {TokenKind::FOREIGN, TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::PRIVATE}}, {TokenKind::PRIVATE, {TokenKind::FOREIGN, TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::COMMON}}, {TokenKind::UNSAFE, {TokenKind::FOREIGN}}, {TokenKind::CONST, {TokenKind::FOREIGN, TokenKind::COMMON, TokenKind::PLATFORM}}, {TokenKind::COMMON, {TokenKind::PLATFORM, TokenKind::PRIVATE, TokenKind::FOREIGN, TokenKind::CONST}}, {TokenKind::PLATFORM, {TokenKind::COMMON, TokenKind::PRIVATE, TokenKind::CONST}}, }; const static std::unordered_map> TOPLEVEL_MACRODECL_MODIFIERS = { ACCESSIBLE_MODIFIERS }; const static std::unordered_map> TOPLEVEL_MAINDECL_MODIFIERS = { {TokenKind::UNSAFE, {}}, }; const static std::unordered_map> CLASS_BODY_FUNCDECL_MODIFIERS = { {TokenKind::PUBLIC, {TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::PRIVATE}}, {TokenKind::PROTECTED, {TokenKind::PUBLIC, TokenKind::INTERNAL, TokenKind::PRIVATE}}, {TokenKind::PRIVATE, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::OPEN}}, {TokenKind::INTERNAL, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::PRIVATE, TokenKind::OPEN}}, {TokenKind::STATIC, {TokenKind::OPEN, TokenKind::OVERRIDE, TokenKind::OPERATOR}}, {TokenKind::REDEF, {TokenKind::OPEN, TokenKind::OVERRIDE, TokenKind::OPERATOR}}, {TokenKind::OPEN, {TokenKind::STATIC, TokenKind::INTERNAL, TokenKind::PRIVATE, TokenKind::REDEF, TokenKind::CONST}}, {TokenKind::OVERRIDE, {TokenKind::STATIC, TokenKind::REDEF}}, {TokenKind::OPERATOR, {TokenKind::STATIC, TokenKind::REDEF}}, {TokenKind::UNSAFE, {}}, {TokenKind::CONST, {TokenKind::OPEN, TokenKind::COMMON, TokenKind::PLATFORM}}, {TokenKind::COMMON, {TokenKind::PLATFORM, TokenKind::CONST}}, {TokenKind::PLATFORM, {TokenKind::COMMON, TokenKind::CONST}}, {TokenKind::ABSTRACT, {}} }; const static std::unordered_map> INTERFACE_BODY_FUNCDECL_MODIFIERS = { {TokenKind::STATIC, {TokenKind::OPERATOR, TokenKind::MUT, TokenKind::OVERRIDE, TokenKind::OPEN}}, {TokenKind::REDEF, {TokenKind::OPEN, TokenKind::OVERRIDE, TokenKind::OPERATOR}}, {TokenKind::OPERATOR, {TokenKind::STATIC}}, {TokenKind::MUT, {TokenKind::STATIC}}, {TokenKind::UNSAFE, {}}, {TokenKind::OVERRIDE, {TokenKind::STATIC, TokenKind::REDEF}}, {TokenKind::OPEN, {TokenKind::STATIC, TokenKind::REDEF}}, {TokenKind::CONST, {TokenKind::COMMON, TokenKind::PLATFORM}}, {TokenKind::PUBLIC, {}}, {TokenKind::COMMON, {TokenKind::PLATFORM, TokenKind::CONST}}, {TokenKind::PLATFORM, {TokenKind::COMMON, TokenKind::CONST}} }; const static std::unordered_map> STRUCT_BODY_FUNCDECL_MODIFIERS = { ACCESSIBLE_MODIFIERS {TokenKind::STATIC, {TokenKind::OPERATOR, TokenKind::MUT}}, {TokenKind::OPERATOR, {TokenKind::STATIC}}, {TokenKind::MUT, {TokenKind::STATIC, TokenKind::CONST}}, {TokenKind::UNSAFE, {}}, {TokenKind::OVERRIDE, {TokenKind::STATIC, TokenKind::REDEF}}, {TokenKind::REDEF, {TokenKind::OPEN, TokenKind::OVERRIDE, TokenKind::OPERATOR}}, {TokenKind::CONST, {TokenKind::MUT, TokenKind::COMMON, TokenKind::PLATFORM}}, {TokenKind::COMMON, {TokenKind::PLATFORM, TokenKind::CONST}}, {TokenKind::PLATFORM, {TokenKind::COMMON, TokenKind::CONST}}, }; const static std::unordered_map> ENUM_BODY_FUNCDECL_MODIFIERS = { ACCESSIBLE_MODIFIERS {TokenKind::STATIC, {TokenKind::OPERATOR}}, {TokenKind::OPERATOR, {TokenKind::STATIC}}, {TokenKind::UNSAFE, {}}, {TokenKind::OVERRIDE, {TokenKind::STATIC, TokenKind::REDEF}}, {TokenKind::REDEF, {TokenKind::OVERRIDE, TokenKind::OPERATOR}}, {TokenKind::CONST, {TokenKind::COMMON, TokenKind::PLATFORM}}, {TokenKind::COMMON, {TokenKind::PLATFORM, TokenKind::CONST}}, {TokenKind::PLATFORM, {TokenKind::COMMON, TokenKind::CONST}} }; const static std::unordered_map> FUNC_BODY_FUNCDECL_MODIFIERS = { {TokenKind::UNSAFE, {}}, {TokenKind::CONST, {}}, }; const static std::unordered_map> STRUCT_BODY_VARIABLE_MODIFIERS = { ACCESSIBLE_MODIFIERS {TokenKind::STATIC, {}}, {TokenKind::CONST, {}}, {TokenKind::CONST, {TokenKind::COMMON, TokenKind::PLATFORM}}, {TokenKind::COMMON, {TokenKind::PLATFORM, TokenKind::CONST}}, {TokenKind::PLATFORM, {TokenKind::COMMON, TokenKind::CONST}} }; const static std::unordered_map> EXTEND_BODY_FUNCDECL_MODIFIERS = { ACCESSIBLE_MODIFIERS {TokenKind::OPERATOR, {TokenKind::STATIC}}, {TokenKind::MUT, {TokenKind::STATIC}}, {TokenKind::STATIC, {TokenKind::MUT, TokenKind::OPERATOR}}, {TokenKind::UNSAFE, {}}, {TokenKind::CONST, {}}, {TokenKind::COMMON, {TokenKind::PLATFORM}}, {TokenKind::PLATFORM, {TokenKind::COMMON}} }; const static std::unordered_map> CLASS_BODY_PROP_MODIFIERS = { {TokenKind::PUBLIC, {TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::PRIVATE}}, {TokenKind::PROTECTED, {TokenKind::PUBLIC, TokenKind::INTERNAL, TokenKind::PRIVATE}}, {TokenKind::INTERNAL, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::PRIVATE, TokenKind::OPEN}}, {TokenKind::PRIVATE, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::OPEN}}, {TokenKind::STATIC, {TokenKind::OPEN, TokenKind::OVERRIDE}}, {TokenKind::REDEF, {TokenKind::OPEN, TokenKind::OVERRIDE}}, {TokenKind::OPEN, {TokenKind::STATIC, TokenKind::INTERNAL, TokenKind::PRIVATE, TokenKind::REDEF}}, {TokenKind::OVERRIDE, {TokenKind::STATIC, TokenKind::REDEF}}, {TokenKind::MUT, {}}, {TokenKind::COMMON, {TokenKind::PLATFORM}}, {TokenKind::PLATFORM, {TokenKind::COMMON}}, {TokenKind::ABSTRACT, {}}, }; const static std::unordered_map> INTERFACE_BODY_PROP_MODIFIERS = { {TokenKind::STATIC, {TokenKind::OPEN, TokenKind::OVERRIDE}}, {TokenKind::REDEF, {TokenKind::OPEN, TokenKind::OVERRIDE}}, {TokenKind::OVERRIDE, {TokenKind::STATIC, TokenKind::REDEF}}, {TokenKind::OPEN, {TokenKind::STATIC, TokenKind::REDEF}}, {TokenKind::MUT, {}}, {TokenKind::PUBLIC, {}}, {TokenKind::COMMON, {TokenKind::PLATFORM}}, {TokenKind::PLATFORM, {TokenKind::COMMON}}, }; const static std::unordered_map> STRUCT_BODY_PROP_MODIFIERS = { ACCESSIBLE_MODIFIERS {TokenKind::STATIC, {TokenKind::OVERRIDE}}, {TokenKind::OVERRIDE, {TokenKind::STATIC, TokenKind::REDEF}}, {TokenKind::REDEF, {TokenKind::OVERRIDE}}, {TokenKind::MUT, {}}, {TokenKind::COMMON, {TokenKind::PLATFORM}}, {TokenKind::PLATFORM, {TokenKind::COMMON}}, }; const static std::unordered_map> ENUM_BODY_PROP_MODIFIERS = { ACCESSIBLE_MODIFIERS {TokenKind::STATIC, {TokenKind::OVERRIDE}}, {TokenKind::OVERRIDE, {TokenKind::STATIC, TokenKind::REDEF}}, {TokenKind::REDEF, {TokenKind::OVERRIDE}}, {TokenKind::COMMON, {TokenKind::PLATFORM}}, {TokenKind::PLATFORM, {TokenKind::COMMON}}, }; const static std::unordered_map> EXTEND_BODY_PROP_MODIFIERS = { ACCESSIBLE_MODIFIERS {TokenKind::STATIC, {}}, {TokenKind::MUT, {}}, {TokenKind::COMMON, {TokenKind::PLATFORM}}, {TokenKind::PLATFORM, {TokenKind::COMMON}}, }; // AGGREGATE means class and struct in Cangjie here. #define AGGREGATE_BODY_INSTANCE_INIT_MODIFIERS \ {TokenKind::PUBLIC, {TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::PRIVATE, TokenKind::STATIC}}, \ {TokenKind::PROTECTED, {TokenKind::PUBLIC, TokenKind::INTERNAL, TokenKind::PRIVATE, TokenKind::STATIC}}, \ {TokenKind::INTERNAL, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::PRIVATE, TokenKind::STATIC}}, \ {TokenKind::PRIVATE, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::STATIC}}, \ {TokenKind::CONST, {}}, \ {TokenKind::COMMON, {TokenKind::PLATFORM}}, \ {TokenKind::PLATFORM, {TokenKind::COMMON}}, #define AGGREGATE_BODY_INIT_MODIFIERS \ AGGREGATE_BODY_INSTANCE_INIT_MODIFIERS \ {TokenKind::STATIC, {TokenKind::PUBLIC, TokenKind::PROTECTED, TokenKind::INTERNAL, TokenKind::PRIVATE}}, /** * SCOPE_MODIFIER_RULES contains modifier trustlists defined under different scope kinds. The trustlist contains all * allowed modifiers of the current scope kind and modifiers that conflict with the modifier. */ const static std::unordered_map>>> SCOPE_MODIFIER_RULES = { {DefKind::PACKAGE, {{ScopeKind::TOPLEVEL, PACKAGE_MODIFIERS}}}, { DefKind::FUNC, { {ScopeKind::TOPLEVEL, TOPLEVEL_FUNCDECL_MODIFIERS}, {ScopeKind::CLASS_BODY, CLASS_BODY_FUNCDECL_MODIFIERS}, {ScopeKind::INTERFACE_BODY, INTERFACE_BODY_FUNCDECL_MODIFIERS}, {ScopeKind::STRUCT_BODY, STRUCT_BODY_FUNCDECL_MODIFIERS}, {ScopeKind::EXTEND_BODY, EXTEND_BODY_FUNCDECL_MODIFIERS}, {ScopeKind::ENUM_BODY, ENUM_BODY_FUNCDECL_MODIFIERS}, {ScopeKind::FUNC_BODY, FUNC_BODY_FUNCDECL_MODIFIERS}, }, }, {DefKind::MACRO, {{ScopeKind::TOPLEVEL, TOPLEVEL_MACRODECL_MODIFIERS}}}, {DefKind::CLASS, {{ScopeKind::TOPLEVEL, TOPLEVEL_CLASS_MODIFIERS}}}, {DefKind::INTERFACE, {{ScopeKind::TOPLEVEL, TOPLEVEL_INTERFACE_MODIFIERS}}}, { DefKind::VARIABLE, { {ScopeKind::TOPLEVEL, TOPLEVEL_VARIABLE_MODIFIERS}, {ScopeKind::CLASS_BODY, CLASS_BODY_VARIABLE_MODIFIERS}, {ScopeKind::STRUCT_BODY, STRUCT_BODY_VARIABLE_MODIFIERS}, {ScopeKind::PRIMARY_CONSTRUCTOR_BODY_FOR_CLASS, {AGGREGATE_BODY_INSTANCE_INIT_MODIFIERS}}, {ScopeKind::PRIMARY_CONSTRUCTOR_BODY_FOR_STRUCT, {AGGREGATE_BODY_INSTANCE_INIT_MODIFIERS}}, {ScopeKind::FUNC_BODY, {{TokenKind::CONST, {}}}}, {ScopeKind::MACRO_BODY, {{TokenKind::CONST, {}}}}, }, }, {DefKind::STRUCT, {{ScopeKind::TOPLEVEL, TOPLEVEL_STRUCT_MODIFIERS}}}, {DefKind::ENUM, {{ScopeKind::TOPLEVEL, TOPLEVEL_ENUM_MODIFIERS}}}, {DefKind::TYPE, {{ScopeKind::TOPLEVEL, TOPLEVEL_COMMON_MODIFIERS}}}, {DefKind::CONSTRUCTOR, { {ScopeKind::CLASS_BODY, {AGGREGATE_BODY_INIT_MODIFIERS}}, {ScopeKind::STRUCT_BODY, {AGGREGATE_BODY_INIT_MODIFIERS}}, }}, {DefKind::PRIMARY_CONSTRUCTOR, { {ScopeKind::CLASS_BODY, {AGGREGATE_BODY_INSTANCE_INIT_MODIFIERS}}, {ScopeKind::STRUCT_BODY, {AGGREGATE_BODY_INSTANCE_INIT_MODIFIERS}}, }}, { DefKind::PROPERTY, { {ScopeKind::CLASS_BODY, CLASS_BODY_PROP_MODIFIERS}, {ScopeKind::INTERFACE_BODY, INTERFACE_BODY_PROP_MODIFIERS}, {ScopeKind::STRUCT_BODY, STRUCT_BODY_PROP_MODIFIERS}, {ScopeKind::EXTEND_BODY, EXTEND_BODY_PROP_MODIFIERS}, {ScopeKind::ENUM_BODY, ENUM_BODY_PROP_MODIFIERS}, }, }, {DefKind::MAIN, {{ScopeKind::TOPLEVEL, TOPLEVEL_MAINDECL_MODIFIERS}}}, // Empty means it doesn't support any modifier. {DefKind::EXTEND, {{ScopeKind::TOPLEVEL, {}}}}, {DefKind::FINALIZER, {{ScopeKind::CLASS_BODY, {}}}}, }; // key: token to be checked, report when any of the token inside map value existed. const static std::unordered_map> TOPLEVEL_CLASS_WARNING_MODIFIERS = { {TokenKind::OPEN, {TokenKind::SEALED, TokenKind::ABSTRACT}}, {TokenKind::PUBLIC, {TokenKind::SEALED}}, }; const static std::unordered_map> TOPLEVEL_INTERFACE_WARNING_MODIFIERS = { // NOTE: interface with open (without sealed) also should report warning. // 'ILLEGAL' used to indicate that always report warning when key token existed. {TokenKind::OPEN, {TokenKind::SEALED, TokenKind::ILLEGAL}}, {TokenKind::PUBLIC, {TokenKind::SEALED}}, }; const static std::unordered_map> INTERFACE_MEMBER_WARNING_MODIFIERS = { {TokenKind::OPEN, {TokenKind::ILLEGAL}}, {TokenKind::PUBLIC, {TokenKind::ILLEGAL}}, }; const static std::unordered_map>>> SCOPE_MODIFIER_WARNING_RULES = { {DefKind::FUNC, {{ScopeKind::INTERFACE_BODY, INTERFACE_MEMBER_WARNING_MODIFIERS}}}, {DefKind::PROPERTY, {{ScopeKind::INTERFACE_BODY, INTERFACE_MEMBER_WARNING_MODIFIERS}}}, {DefKind::CLASS, {{ScopeKind::TOPLEVEL, TOPLEVEL_CLASS_WARNING_MODIFIERS}}}, {DefKind::INTERFACE, {{ScopeKind::TOPLEVEL, TOPLEVEL_INTERFACE_WARNING_MODIFIERS}}}, }; const ModifierRules& GetModifierRulesByDefKind(DefKind defKind) { CJC_ASSERT(SCOPE_MODIFIER_RULES.find(defKind) != SCOPE_MODIFIER_RULES.end()); return SCOPE_MODIFIER_RULES.at(defKind); } const ModifierRules& GetModifierWarningRulesByDefKind(DefKind defKind) { const static ModifierRules EMPTY; auto found = SCOPE_MODIFIER_WARNING_RULES.find(defKind); return found == SCOPE_MODIFIER_WARNING_RULES.end() ? EMPTY : found->second; } AST::Attribute ParserImpl::GetModifierAttr(const OwnedPtr& modifier, AST::ASTKind kind) const { if (modifier) { if (auto found = TK2ATTRMAP.find(modifier->modifier); found != TK2ATTRMAP.end()) { return found->second; } } // The default modifier of package statement is 'public'. if (kind == AST::ASTKind::PACKAGE_SPEC) { return AST::Attribute::PUBLIC; } // The default modifier of import statement is 'private'. if (kind == AST::ASTKind::IMPORT_SPEC) { return AST::Attribute::PRIVATE; } // Otherwise, the default modifier is 'internal'. return AST::Attribute::INTERNAL; } } // namespace Cangjie cangjie_compiler-1.0.7/src/Parse/ParserUtils.cpp000066400000000000000000000572071510705540100216440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Parser utils function. */ #include "ParserImpl.h" #include "cangjie/AST/Match.h" #include "cangjie/Utils/Unicode.h" namespace Cangjie { const static std::unordered_map MATCHING_OPENING_BRACKET_OF{{TokenKind::RCURL, TokenKind::LCURL}, {TokenKind::RSQUARE, TokenKind::LSQUARE}, {TokenKind::RPAREN, TokenKind::LPAREN}}; const std::vector& GetTypeFirst() { static const std::vector TYPE_FIRST_TOKEN = { TokenKind::INT8, TokenKind::INT16, TokenKind::INT32, TokenKind::INT64, TokenKind::INTNATIVE, TokenKind::UINT8, TokenKind::UINT16, TokenKind::UINT32, TokenKind::UINT64, TokenKind::UINTNATIVE, TokenKind::FLOAT16, TokenKind::FLOAT32, TokenKind::FLOAT64, TokenKind::RUNE, TokenKind::BOOLEAN, TokenKind::NOTHING, TokenKind::UNIT, TokenKind::QUEST, TokenKind::LPAREN, TokenKind::IDENTIFIER, TokenKind::THISTYPE, TokenKind::VARRAY, }; return TYPE_FIRST_TOKEN; } const Token& ParserImpl::Peek() { if (lookahead.kind != TokenKind::SENTINEL) { return lookahead; } deadlocked = false; bool firstNLFlag = true; // skip comments, newlines (conditionally) // as part of this, also keep `bracketsStack` up-to-date do { lookahead = lexer->Next(); if (calculateLineNum) { allTokensInOneFile.emplace_back(lookahead.Begin().line, lookahead.End().line); } if (lookahead.kind == TokenKind::LCURL || lookahead.kind == TokenKind::LPAREN || lookahead.kind == TokenKind::LSQUARE) { bracketsStack.push_back(lookahead.kind); } auto matchingBracket = MATCHING_OPENING_BRACKET_OF.find(lookahead.kind); if (matchingBracket != MATCHING_OPENING_BRACKET_OF.end()) { if (!bracketsStack.empty() && matchingBracket->second == bracketsStack.back()) { bracketsStack.pop_back(); } } if (lookahead.kind == TokenKind::NL) { newlineSkipped = true; if (firstNLFlag) { firstNLPosition = lookahead.Begin(); firstNLFlag = false; } } } while (lookahead.kind == TokenKind::COMMENT || (lookahead.kind == TokenKind::NL && skipNL)); // If reach end of file, the end pos should be adjacent with last non-comment token. if (lookahead.kind == TokenKind::END && !lastToken.Begin().IsZero()) { lookahead.SetValuePos(lookahead.Value(), lastToken.End(), lastToken.End()); } return lookahead; } void ParserImpl::Next() { // Ignore all comment. if (lookahead.kind != TokenKind::COMMENT && lookahead.kind != TokenKind::END) { lastToken = lookahead; if (lookahead.kind != TokenKind::NL) { lastNoneNLToken = lookahead; } } newlineSkipped = false; Peek(); bool firstNLFlag = true; if (lookahead.kind == TokenKind::NL) { firstNLPosition = lookahead.Begin(); firstNLFlag = false; newlineSkipped = true; } while ((skipNL && lexer->Seeing({TokenKind::NL})) || lexer->Seeing({TokenKind::COMMENT})) { if (lexer->Seeing({TokenKind::NL})) { if (firstNLFlag) { firstNLPosition = lexer->LookAhead(1).begin()->Begin(); firstNLFlag = false; } newlineSkipped = true; } lexer->Next(); } lookahead.kind = TokenKind::SENTINEL; } bool ParserImpl::Seeing(const std::vector& kinds, bool skipNewline) { if (lookahead.kind != TokenKind::SENTINEL) { if (lookahead.kind == kinds[0]) { return lexer->Seeing(kinds.begin() + 1, kinds.end(), skipNewline); } return false; } else { return lexer->Seeing(kinds, skipNewline); } } bool ParserImpl::SeeingExpr() { static const std::vector exprFirstToken = { TokenKind::SUB, TokenKind::NOT, TokenKind::IF, TokenKind::MATCH, TokenKind::QUOTE, TokenKind::TRY, TokenKind::THROW, TokenKind::PERFORM, TokenKind::RESUME, TokenKind::RETURN, TokenKind::CONTINUE, TokenKind::BREAK, TokenKind::FOR, TokenKind::WHILE, TokenKind::DO, TokenKind::SPAWN, TokenKind::SYNCHRONIZED, TokenKind::LPAREN, TokenKind::LCURL, TokenKind::LSQUARE, TokenKind::THIS, TokenKind::SUPER, TokenKind::IDENTIFIER, TokenKind::UNSAFE, TokenKind::WILDCARD, TokenKind::VARRAY, }; if (SeeingAny(exprFirstToken)) { return true; } return SeeingLiteral() || SeeingPrimitiveTypeAndLParen() || SeeingPrimitiveTypeAndDot() || SeeingMacroCall() || SeeingBuiltinAnnotation() || SeeingSoftKeyword(); } bool ParserImpl::SeeingCombinator(const std::vector& kinds) { if (Seeing(kinds, false)) { std::vector toks = {lookahead}; auto ahead = lexer->LookAheadSkipNL(kinds.size() - 1); toks.insert(toks.end(), ahead.begin(), ahead.end()); for (size_t i = 1; i < kinds.size(); ++i) { if (toks[i].Begin().line != toks[i - 1].Begin().line || toks[i].Begin().column != toks[i - 1].Begin().column + 1) { return false; } } return true; } return false; } bool ParserImpl::SeeingTokenAndCombinator(TokenKind kind, const std::vector& cmb) { if (!Seeing(kind)) { return false; } auto ahead = lexer->LookAheadSkipNL(cmb.size()); std::vector toks{ahead.begin(), ahead.end()}; if (cmb.size() != toks.size()) { return false; } CJC_ASSERT(cmb.size() > 1); for (size_t i = 0; i < cmb.size(); ++i) { if (toks[i].kind != cmb[i]) { return false; } } for (size_t i = 1; i < toks.size(); ++i) { if (toks[i].Begin().line != toks[i - 1].Begin().line || toks[i].Begin().column != toks[i - 1].Begin().column + 1) { return false; } } return true; } void ParserImpl::SkipCombinator(const std::vector& kinds) { if (SeeingCombinator(kinds)) { auto i = kinds.size(); while (i-- > 0) { Next(); } } } bool ParserImpl::SkipAmbiguousToken() { for (const auto &[tar, comb, val] : ambiguoussCombinedTokens) { if (SeeingCombinator(comb)) { size_t i = comb.size(); while (i-- != 0) { Next(); } lastToken = Token(tar, val, lookahead.Begin() - (val.size() - 1), lookahead.Begin() - 1); return true; } } return false; } bool ParserImpl::SkipCombinedDoubleArrow() { if (SeeingCombinator(combinedDoubleArrow)) { auto i = combinedDoubleArrow.size(); while (i-- > 0) { Next(); } lastToken = Token(TokenKind::DOUBLE_ARROW, "=>", lastToken.Begin(), lastToken.Begin() + std::string_view{"=>"}.size()); return true; } return false; } bool ParserImpl::Skip(TokenKind kind) { if (Peek().kind == kind) { Next(); return true; } else { return false; } } void ParserImpl::ParseOneOrMoreWithSeparator( TokenKind separator, std::vector& positions, const std::function& parseElement) { parseElement(); while (Skip(separator)) { positions.push_back(lastToken.Begin()); parseElement(); } } void ParserImpl::ParseOneOrMoreWithSeparator( TokenKind separator, const std::function& storeSeparator, const std::function& parseElement) { parseElement(); while (Skip(separator)) { storeSeparator(lastToken.Begin()); parseElement(); } } void ParserImpl::ParseOneOrMoreSepTrailing(std::function&& storeSeparator, std::function&& parseElement, TokenKind end, TokenKind separator) { do { parseElement(); if (Skip(separator)) { storeSeparator(lastToken.Begin()); } else { break; } } while (!Seeing(end)); } void ParserImpl::ParseZeroOrMoreSepTrailing(std::function&& storeSeparator, std::function&& parseElement, TokenKind end, TokenKind separator) { while (!Seeing(end)) { parseElement(); if (Skip(separator)) { storeSeparator(lastToken.Begin()); } else { break; } } } void ParserImpl::ParseZeroOrMoreWithSeparator(TokenKind separator, const std::function& storeSeparator, const std::function& parseElement, TokenKind terminator) { if (!Seeing(terminator)) { ParseOneOrMoreWithSeparator(separator, storeSeparator, parseElement); } } bool ParserImpl::CanMatchBracketInStack() { auto matchingBracket = MATCHING_OPENING_BRACKET_OF.find(lookahead.kind); if (matchingBracket != MATCHING_OPENING_BRACKET_OF.end()) { auto it = std::find(bracketsStack.begin(), bracketsStack.end(), matchingBracket->second); if (it == bracketsStack.end()) { return false; } else { bracketsStack.erase(it, bracketsStack.end()); } return true; } return false; } void ParserImpl::SpreadAttrAndConsume(Ptr source, Ptr target, std::vector&& kind) { if (source->TestAttr(AST::Attribute::IS_BROKEN)) { target->EnableAttr(AST::Attribute::IS_BROKEN); TryConsumeUntilAny(std::move(kind)); } } void ParserImpl::SkipPairedBrackets() { if (Seeing(TokenKind::LPAREN)) { Next(); ConsumeUntil(TokenKind::RPAREN, false); } else if (Seeing(TokenKind::LCURL)) { Next(); ConsumeUntil(TokenKind::RCURL, false); } else if (Seeing(TokenKind::LSQUARE)) { Next(); ConsumeUntil(TokenKind::RSQUARE, false); } } /* * Consume tokens until start of declaration. */ void ParserImpl::ConsumeUntilDecl(TokenKind kind) { while (!SeeingDecl() && !SeeingMacroCallDecl() && !Seeing(kind)) { if (Seeing(TokenKind::END)) { break; } SkipPairedBrackets(); Next(); } } bool ParserImpl::ConsumeCommon(bool& flag) { if (Seeing(TokenKind::NL)) { flag = true; } if (Seeing(TokenKind::END)) { return false; } SkipPairedBrackets(); skipNL = false; Next(); return true; } /* * Consume tokens until start of declaration. */ void ParserImpl::ConsumeUntilDeclOrNL(TokenKind kind) { skipNL = false; bool flag = false; while (!Seeing(TokenKind::NL) && !SeeingDecl() && !SeeingMacroCallDecl() && !Seeing(kind)) { if (!ConsumeCommon(flag)) { break; } } if (flag) { newlineSkipped = true; } skipNL = true; } /* * Consume tokens until target token, and if newline consumed the skipNL will be true. */ void ParserImpl::ConsumeUntil(TokenKind kind, bool targetTokenConsumed) { skipNL = false; bool flag = false; while (!Seeing(kind)) { if (!ConsumeCommon(flag)) { break; } } if (targetTokenConsumed) { if (Seeing(TokenKind::NL)) { flag = true; } Next(); Peek(); } if (flag) { newlineSkipped = true; } skipNL = true; } // Try to consume target tokens, will only lookahead 3 tokens (ignore NL) by default. // If found target tokens, consume until it, otherwise do nothing. bool ParserImpl::TryConsumeUntilAny(std::vector tokens) { const size_t len = 3; Peek(); std::vector predictToken = {lookahead}; auto ahead = lexer->LookAheadSkipNL(len - 1); predictToken.insert(predictToken.end(), ahead.begin(), ahead.end()); auto iter = std::find_if(predictToken.begin(), predictToken.end(), [&tokens](auto& token) { return std::any_of(tokens.begin(), tokens.end(), [&token](TokenKind kind) { return token.kind == kind; }); }); if (iter == predictToken.end()) { return false; } ptrdiff_t loc = iter - predictToken.begin(); while (loc-- > 0) { Next(); Peek(); } return true; } void ParserImpl::TargetTokenConsumedControl(bool& flag, bool targetTokenConsumed) { if (Seeing(TokenKind::NL)) { flag = true; Next(); } else if (targetTokenConsumed) { Next(); } if (flag) { newlineSkipped = true; } skipNL = true; } /* * Consume tokens until target tokens, and if newline consumed the skipNL will be true. * If token NL is among target tokens, it will be always consumed. */ void ParserImpl::ConsumeUntilAny(std::vector&& tokens, bool targetTokenConsumed) { skipNL = false; bool flag = false; while (std::none_of(tokens.begin(), tokens.end(), [this](TokenKind kind) { return kind == Peek().kind; })) { if (!ConsumeCommon(flag)) { break; } } TargetTokenConsumedControl(flag, targetTokenConsumed); } void ParserImpl::ConsumeUntilAny(const std::function& functor, bool targetTokenConsumed) { skipNL = false; bool flag = false; while (!functor()) { if (!ConsumeCommon(flag)) { break; } } TargetTokenConsumedControl(flag, targetTokenConsumed); } /* case1: LevenshteinDistance("lassB", "classB") => 1 case2: LevenshteinDistance("main", "intmian") => 5 */ unsigned LevenshteinDistance(const std::string& source, const std::string& target) { unsigned m = static_cast(source.length()); unsigned n = static_cast(target.length()); if (n == 0) { return n; } unsigned* dp = new unsigned[n + 1]; unsigned result; // although using std::min on unsigned is effectively noexcept, string::operator[] may throws an exception when // index out of range, so a try block is required #ifndef CANGJIE_ENABLE_GCOV try { #endif for (unsigned j = 0; j < n + 1; j++) { dp[j] = j; } for (unsigned x = 1; x <= m; x++) { dp[0] = x; unsigned upperLeft = x - 1; for (unsigned y = 1; y <= n; y++) { unsigned previousRowDpY = dp[y]; dp[y] = std::min(upperLeft + (source[x - 1] == target[y - 1] ? 0 : 1), std::min(dp[y], dp[y - 1]) + 1); upperLeft = previousRowDpY; } } result = dp[n]; #ifndef CANGJIE_ENABLE_GCOV } catch (...) { delete[] dp; throw; } #endif delete[] dp; return result; } void ParserImpl::ImplementConsumeStrategy(ScopeKind sc) { auto normalDeclConsume = [this]() -> bool { return SeeingDecl() || Seeing(TokenKind::RCURL); }; auto normalFuncBodyConsume = [this]() -> bool { return SeeingDecl() || SeeingExpr() || Seeing(TokenKind::RCURL); }; auto normalEnumBodyConsume = [this]() -> bool { return SeeingDecl() || Seeing(TokenKind::BITOR) || Seeing(TokenKind::IDENTIFIER) || Seeing(TokenKind::RCURL); }; switch (sc) { case ScopeKind::TOPLEVEL: { ConsumeUntilDecl(); ConsumeUntilAny(normalFuncBodyConsume, false); break; } case ScopeKind::EXTEND_BODY: case ScopeKind::STRUCT_BODY: case ScopeKind::INTERFACE_BODY: case ScopeKind::CLASS_BODY: { ConsumeUntilAny(normalDeclConsume, false); break; } case ScopeKind::FUNC_BODY: case ScopeKind::MACRO_BODY: case ScopeKind::PRIMARY_CONSTRUCTOR_BODY_FOR_CLASS: case ScopeKind::PRIMARY_CONSTRUCTOR_BODY_FOR_STRUCT: case ScopeKind::PROP_MEMBER_GETTER_BODY: case ScopeKind::PROP_MEMBER_SETTER_BODY: { ConsumeUntilAny(normalFuncBodyConsume, false); break; } case ScopeKind::ENUM_BODY: { ConsumeUntilAny(normalEnumBodyConsume, false); break; } default: break; } } bool ParserImpl::SeeingPrimaryIdentifer() { return SeeingIdentifierAndTargetOp({TokenKind::LT, TokenKind::LPAREN, TokenKind::LSQUARE, TokenKind::LCURL}) && LevenshteinDistance(lookahead.Value(), GetPrimaryDeclIdentRawValue()) <= 1; } bool ParserImpl::IsRawIdentifier(const std::string& identifier) const { if (identifier.size() <= std::string("``").size()) { return false; } return identifier.at(0) == '`'; } std::string ParserImpl::ParseNameFromRawIdentifier(const std::string& rawIdentifier) const { return rawIdentifier.substr(1, rawIdentifier.size() - std::string("``").size()); } SrcIdentifier ParserImpl::ParseIdentifierFromToken(const Token& token) const { size_t len{static_cast(static_cast((token.End() - token.Begin()).column))}; return ParseIdentifierFromName(token.Value(), token.Begin(), token.End(), len); } SrcIdentifier ParserImpl::ParseIdentifierFromName( const std::string& identifier, const Position& tkPos, const Position& end, size_t len) const { bool isRaw = IsRawIdentifier(identifier); const std::string& name = isRaw ? ParseNameFromRawIdentifier(identifier) : identifier; Position pos = isRaw ? (tkPos + 1) : tkPos; Position endPos = isRaw ? (end - 1) : end; (void)len; return {name, pos, endPos, isRaw}; } SrcIdentifier ParserImpl::ExpectIdentifierWithPos(AST::Node& node) { Position tkPos{INVALID_POSITION}; if (node.astKind == AST::ASTKind::FUNC_DECL && Skip(TokenKind::MAIN)) { return ParseIdentifierFromToken(lastToken); } if (Skip(TokenKind::IDENTIFIER) || SkipKeyWordIdentifier()) { return ParseIdentifierFromToken(lastToken); } if (!node.TestAttr(AST::Attribute::HAS_BROKEN)) { tkPos = lastToken.Begin(); DiagExpectedIdentifierWithNode(&node); } return {INVALID_IDENTIFIER, tkPos, tkPos, false}; } SrcIdentifier ParserImpl::ExpectPackageIdentWithPos(AST::Node& node) { Position tkPos{INVALID_POSITION}; if (Skip(TokenKind::IDENTIFIER) || Skip(TokenKind::PACKAGE_IDENTIFIER) || SkipKeyWordIdentifier() || Skip(TokenKind::COMMON)) { return ParseIdentifierFromToken(lastToken); } if (!node.TestAttr(AST::Attribute::HAS_BROKEN)) { tkPos = lastToken.End(); DiagExpectedIdentifierWithNode(&node); } return {INVALID_IDENTIFIER, tkPos, tkPos, false}; } void ParserImpl::SkipBlank(TokenKind blank0, TokenKind blank1) { while (Seeing(blank0) || Seeing(blank1)) { Next(); } } bool ParserImpl::DetectPrematureEnd() { if (deadlocked || Seeing(TokenKind::END)) { Next(); return true; } else { deadlocked = true; return false; } } bool ParserImpl::SeeingKeywordAndOperater() { if (SeeingContextualKeyword()) { auto tokens = lexer->LookAheadSkipNL(1); // Destructor function and MacroCall function will not be identified as a keyword identifier. if (tokens.begin()->kind == TokenKind::BITNOT || tokens.begin()->kind == TokenKind::AT) { return false; } if (tokens.begin()->kind < TokenKind::WILDCARD) { return true; } } return false; } bool ParserImpl::SeeingKeywordWithDecl() { std::vector tokenKindVec = {TokenKind::STRUCT, TokenKind::ENUM, TokenKind::PACKAGE, TokenKind::IMPORT, TokenKind::CLASS, TokenKind::INTERFACE, TokenKind::FUNC, TokenKind::MACRO, TokenKind::TYPE, TokenKind::LET, TokenKind::VAR, TokenKind::EXTEND, TokenKind::MAIN}; if (SeeingContextualKeyword()) { auto tokens = lexer->LookAheadSkipNL(1); if (std::find(tokenKindVec.begin(), tokenKindVec.end(), tokens.begin()->kind) != tokenKindVec.end()) { return true; } } return false; } bool ParserImpl::SeeingNamedFuncArgs() { if (SeeingContextualKeyword() || Seeing(TokenKind::IDENTIFIER)) { return lexer->Seeing({TokenKind::COLON}, true); } return false; } bool ParserImpl::SeeingIdentifierAndTargetOp(const std::vector& tokenKinds) { if (SeeingContextualKeyword() || Seeing(TokenKind::IDENTIFIER)) { auto tokens = lexer->LookAheadSkipNL(1); for (auto& kind : tokenKinds) { if (tokens.begin()->kind == kind) { return true; } } } return false; } bool ParserImpl::SeeingInvaildParamListInLambdaExpr() { const std::vector vaildKinds{TokenKind::COMMA, TokenKind::COLON, TokenKind::DOUBLE_ARROW}; if (SeeingContextualKeyword() || Seeing(TokenKind::IDENTIFIER) || Seeing(TokenKind::WILDCARD)) { const size_t combinedDoubleArrowSize = 2; auto tokens = lexer->LookAheadSkipNL(combinedDoubleArrowSize); CJC_ASSERT(!tokens.empty()); for (auto& kinds : vaildKinds) { if (tokens.begin()->kind == kinds) { return false; } } CJC_ASSERT(combinedDoubleArrow.size() == combinedDoubleArrowSize); if (tokens.size() == combinedDoubleArrow.size()) { auto first = tokens.begin(); if (first->kind != combinedDoubleArrow[0]) { return true; } auto second = ++tokens.begin(); if (second->kind != combinedDoubleArrow[1]) { return true; } if (second->Begin().line == first->Begin().line && second->Begin().column == first->Begin().column + 1) { return false; } } } return true; } bool ParserImpl::SeeingInvaildOperaterInLambdaExpr() { const std::vector tokenKinds{TokenKind::COMMA, TokenKind::COLON, TokenKind::DOUBLE_ARROW}; if (SeeingContextualKeyword() || Seeing(TokenKind::IDENTIFIER)) { const size_t combinedDoubleArrowSize = 2; const size_t lookNum = combinedDoubleArrowSize + 1; auto tokens = lexer->LookAheadSkipNL(lookNum); CJC_ASSERT(!tokens.empty()); if (tokens.front().kind != TokenKind::NOT) { return false; } auto secondTk = ++tokens.begin(); if (secondTk == tokens.end()) { return false; } if (std::find(tokenKinds.begin(), tokenKinds.end(), secondTk->kind) != tokenKinds.end()) { return true; } if (tokens.size() < lookNum) { return false; } auto thirdTk = tokens.back(); CJC_ASSERT(combinedDoubleArrow.size() == combinedDoubleArrowSize); if (secondTk->kind != combinedDoubleArrow[0] || thirdTk.kind != combinedDoubleArrow[1]) { return false; } if (thirdTk.Begin().line == secondTk->Begin().line && thirdTk.Begin().column == secondTk->Begin().column + 1) { return true; } } return false; } bool ParserImpl::SeeingAnnotationTrailingClosure(const std::vector& tokenKinds) { if (!SeeingAny({TokenKind::AT, TokenKind::AT_EXCL})) { return false; } auto tokens = lexer->LookAheadSkipNL(tokenKinds.size() + 1); auto& contextual = GetContextualKeyword(); if (tokens.front().kind == TokenKind::IDENTIFIER || std::binary_search(contextual.begin(), contextual.end(), tokens.front().kind)) { return tokens.back().kind == TokenKind::LSQUARE; } return false; } std::size_t ParserImpl::GetProcessedTokens() const { auto lexerTokensConsumed = this->lexer->GetCurrentToken(); auto lookAheadKind = this->lookahead.kind; if (lookAheadKind != TokenKind::SENTINEL && lookAheadKind != TokenKind::END) { lexerTokensConsumed--; } return lexerTokensConsumed; } }; // namespace Cangjie cangjie_compiler-1.0.7/src/Sema/000077500000000000000000000000001510705540100164635ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/Assumption.cpp000066400000000000000000000121671510705540100213400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file defines functions for collecting type constraints recursively. This process * is called assumption. */ #include "TypeCheckerImpl.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" using namespace Cangjie; using namespace AST; namespace { /** Add the subtype relation subTy <: upperBoundTy to the type constraint collection @p typeConstraintCollection. */ void AddConstraint(TyVarEnv& typeConstraintCollection, Ty& subTy, Ty& upperBoundTy) { if (!subTy.IsGeneric()) { return; } auto subGen = RawStaticCast(&subTy); subGen->upperBounds.insert(&upperBoundTy); auto found = typeConstraintCollection.find(subGen); if (found == typeConstraintCollection.end()) { typeConstraintCollection.emplace(subGen, std::set>{&upperBoundTy}); } else { found->second.insert(&upperBoundTy); } } /** Check whether the type constraint collection @p typeConstraintCollection has subtype relashion: subTy <: baseTy. */ bool LookUpConstraintCollection(Ty& subTy, Ty& baseTy, const TyVarEnv& typeConstraintCollection) { if (&subTy == &baseTy) { return true; } if (!subTy.IsGeneric()) { return false; } auto found = typeConstraintCollection.find(StaticCast(&subTy)); // If the subTy cannot be found in typeConstraintCollection, return false. if (found == typeConstraintCollection.end()) { return false; } return found->second.find(&baseTy) != found->second.end(); } /** * Checking Whether the Upper Bound of Generics Has InvalidTy. */ bool IsUpperBoundsValid(const std::vector>& upperBounds) { for (auto& upper : upperBounds) { if (!Ty::IsTyCorrect(upper->ty)) { return false; } } return true; } } // namespace void TypeChecker::TypeCheckerImpl::PerformAssumeReferenceTypeUpperBound(TyVarUB& typeConstraintCollection, GCBlames& blames, const AST::Type& referenceTypeUpperBound, const TypeSubst& typeMapping) { auto upperBoundTy = referenceTypeUpperBound.ty; Ptr baseDecl = Ty::GetDeclPtrOfTy(upperBoundTy); // If the upperBound is a generic Type and has associate declaration, perform assumption recursively. if (baseDecl != nullptr && Ty::IsTyCorrect(upperBoundTy) && upperBoundTy->HasGeneric()) { // 1. Create substitute Map between generic tys of upperBound's decl and current uppBound's tys which // can be generic or not. TypeSubst substituteMap = typeManager.GetSubstituteMapping(*upperBoundTy, typeMapping); // 2. Perform assumption recursively. Assumption(typeConstraintCollection, blames, *baseDecl, substituteMap); } } void TypeChecker::TypeCheckerImpl::AssumeOneUpperBound( TyVarUB& typeConstraintCollection, GCBlames& blames, const AST::Type& upperBound, const TypeSubst& typeMapping) { switch (upperBound.astKind) { case ASTKind::REF_TYPE: case ASTKind::QUALIFIED_TYPE: { PerformAssumeReferenceTypeUpperBound(typeConstraintCollection, blames, upperBound, typeMapping); break; } default: break; } } void TypeChecker::TypeCheckerImpl::PerformAssumptionForOneGenericConstraint( TyVarUB& typeConstraintCollection, GCBlames& blames, const GenericConstraint& gc, const TypeSubst& typeMapping) { auto subTypeTy = gc.type->ty; if (!Ty::IsTyCorrect(subTypeTy)) { return; } auto subTy = typeManager.GetInstantiatedTy(subTypeTy, typeMapping); for (const auto& upperBound : gc.upperBounds) { if (!upperBound) { continue; } auto upperBoundTy = upperBound->ty; if (!Ty::IsTyCorrect(upperBoundTy)) { continue; } auto baseTy = typeManager.GetInstantiatedTy(upperBoundTy, typeMapping); // If the constraint is already exist in typeConstraintCollection, no need to do assumption recursively. if (!subTy->IsGeneric() || LookUpConstraintCollection(*subTy, *baseTy, typeConstraintCollection)) { continue; } // Add the constraint to the typeConstraintCollection. AddConstraint(typeConstraintCollection, *subTy, *baseTy); blames[subTy][baseTy].emplace(&gc); AssumeOneUpperBound(typeConstraintCollection, blames, *upperBound, typeMapping); } } void TypeChecker::TypeCheckerImpl::Assumption( TyVarUB& typeConstraintCollection, GCBlames& blames, const AST::Decl& decl, const TypeSubst& typeMapping) { Ptr generic = decl.GetGeneric(); if (generic == nullptr) { return; } for (auto& gc : generic->genericConstraints) { bool shouldCheckUpperBounds = gc && gc->type && !gc->upperBounds.empty() && IsUpperBoundsValid(gc->upperBounds); if (shouldCheckUpperBounds) { PerformAssumptionForOneGenericConstraint(typeConstraintCollection, blames, *gc, typeMapping); } } } cangjie_compiler-1.0.7/src/Sema/BuiltInOperatorUtil.cpp000066400000000000000000000243461510705540100231200ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the builtin operator's type helper functions for TypeCheck. */ #include "TypeCheckUtil.h" #define GENERAL_ARITHMIC_INT_TYPE_MAP \ {TypeKind::TYPE_INT8, TypeKind::TYPE_INT8}, {TypeKind::TYPE_INT16, TypeKind::TYPE_INT16}, \ {TypeKind::TYPE_INT32, TypeKind::TYPE_INT32}, \ {TypeKind::TYPE_INT64, TypeKind::TYPE_INT64}, \ {TypeKind::TYPE_INT_NATIVE, TypeKind::TYPE_INT_NATIVE}, \ {TypeKind::TYPE_UINT8, TypeKind::TYPE_UINT8}, \ {TypeKind::TYPE_UINT16, TypeKind::TYPE_UINT16}, \ {TypeKind::TYPE_UINT32, TypeKind::TYPE_UINT32}, \ {TypeKind::TYPE_UINT64, TypeKind::TYPE_UINT64}, \ {TypeKind::TYPE_UINT_NATIVE, TypeKind::TYPE_UINT_NATIVE}, \ {TypeKind::TYPE_IDEAL_INT, TypeKind::TYPE_IDEAL_INT} #define GENERAL_ARITHMIC_TYPE_MAP \ GENERAL_ARITHMIC_INT_TYPE_MAP, \ {TypeKind::TYPE_FLOAT16, TypeKind::TYPE_FLOAT16}, \ {TypeKind::TYPE_FLOAT32, TypeKind::TYPE_FLOAT32}, \ {TypeKind::TYPE_FLOAT64, TypeKind::TYPE_FLOAT64}, \ {TypeKind::TYPE_IDEAL_FLOAT, TypeKind::TYPE_IDEAL_FLOAT} #define GENERAL_RELATION_NUMERIC_TYPE_MAP \ {TypeKind::TYPE_INT8, TypeKind::TYPE_BOOLEAN}, \ {TypeKind::TYPE_INT16, TypeKind::TYPE_BOOLEAN}, \ {TypeKind::TYPE_INT32, TypeKind::TYPE_BOOLEAN}, \ {TypeKind::TYPE_INT64, TypeKind::TYPE_BOOLEAN}, \ {TypeKind::TYPE_INT_NATIVE, TypeKind::TYPE_BOOLEAN}, \ {TypeKind::TYPE_UINT8, TypeKind::TYPE_BOOLEAN}, \ {TypeKind::TYPE_UINT16, TypeKind::TYPE_BOOLEAN}, \ {TypeKind::TYPE_UINT32, TypeKind::TYPE_BOOLEAN}, \ {TypeKind::TYPE_UINT64, TypeKind::TYPE_BOOLEAN}, \ {TypeKind::TYPE_UINT_NATIVE, TypeKind::TYPE_BOOLEAN}, \ {TypeKind::TYPE_FLOAT16, TypeKind::TYPE_BOOLEAN}, \ {TypeKind::TYPE_FLOAT32, TypeKind::TYPE_BOOLEAN}, \ {TypeKind::TYPE_FLOAT64, TypeKind::TYPE_BOOLEAN}, \ {TypeKind::TYPE_IDEAL_INT, TypeKind::TYPE_BOOLEAN}, \ {TypeKind::TYPE_IDEAL_FLOAT, TypeKind::TYPE_BOOLEAN}, \ {TypeKind::TYPE_RUNE, TypeKind::TYPE_BOOLEAN} namespace Cangjie::TypeCheckUtil { using namespace AST; namespace { /** * This table encodes the types of the operator and result, for every unary operator. */ static const std::map> UNARY_EXPR_TYPE_MAP = { {TokenKind::SUB, {GENERAL_ARITHMIC_TYPE_MAP}}, {TokenKind::NOT, {GENERAL_ARITHMIC_INT_TYPE_MAP, {TypeKind::TYPE_BOOLEAN, TypeKind::TYPE_BOOLEAN}}}, {TokenKind::INCR, {GENERAL_ARITHMIC_INT_TYPE_MAP}}, {TokenKind::DECR, {GENERAL_ARITHMIC_INT_TYPE_MAP}}, }; /** * This table encodes the types of the operator and result, for every binary operator. */ static const std::map> BINARY_EXPR_TYPE_MAP = { {TokenKind::ADD, {GENERAL_ARITHMIC_TYPE_MAP}}, {TokenKind::SUB, {GENERAL_ARITHMIC_TYPE_MAP}}, {TokenKind::MUL, {GENERAL_ARITHMIC_TYPE_MAP}}, {TokenKind::DIV, {GENERAL_ARITHMIC_TYPE_MAP}}, {TokenKind::MOD, {GENERAL_ARITHMIC_INT_TYPE_MAP}}, {TokenKind::EXP, {{TypeKind::TYPE_INT64, TypeKind::TYPE_INT64}, {TypeKind::TYPE_FLOAT64, TypeKind::TYPE_FLOAT64}, {TypeKind::TYPE_IDEAL_INT, TypeKind::TYPE_IDEAL_INT}, {TypeKind::TYPE_IDEAL_FLOAT, TypeKind::TYPE_IDEAL_FLOAT}}}, {TokenKind::EQUAL, {GENERAL_RELATION_NUMERIC_TYPE_MAP, {TypeKind::TYPE_BOOLEAN, TypeKind::TYPE_BOOLEAN}, {TypeKind::TYPE_UNIT, TypeKind::TYPE_BOOLEAN}}}, {TokenKind::NOTEQ, {GENERAL_RELATION_NUMERIC_TYPE_MAP, {TypeKind::TYPE_BOOLEAN, TypeKind::TYPE_BOOLEAN}, {TypeKind::TYPE_UNIT, TypeKind::TYPE_BOOLEAN}}}, {TokenKind::LT, {GENERAL_RELATION_NUMERIC_TYPE_MAP}}, {TokenKind::LE, {GENERAL_RELATION_NUMERIC_TYPE_MAP}}, {TokenKind::GT, {GENERAL_RELATION_NUMERIC_TYPE_MAP}}, {TokenKind::GE, {GENERAL_RELATION_NUMERIC_TYPE_MAP}}, {TokenKind::LSHIFT, {GENERAL_ARITHMIC_INT_TYPE_MAP}}, {TokenKind::RSHIFT, {GENERAL_ARITHMIC_INT_TYPE_MAP}}, {TokenKind::BITAND, {GENERAL_ARITHMIC_INT_TYPE_MAP}}, {TokenKind::BITXOR, {GENERAL_ARITHMIC_INT_TYPE_MAP}}, {TokenKind::BITOR, {GENERAL_ARITHMIC_INT_TYPE_MAP}}, {TokenKind::AND, {{TypeKind::TYPE_BOOLEAN, TypeKind::TYPE_BOOLEAN}}}, {TokenKind::OR, {{TypeKind::TYPE_BOOLEAN, TypeKind::TYPE_BOOLEAN}}}, }; const std::map EMPTY_KIND_MAP; // '**' only support builtin as Int64 ** UInt64 -> return Int64 or // Float64 ** Int64/Float64 -> return Float64. const std::map> SEMA_EXP_TYPES = { {TypeKind::TYPE_INT64, {TypeKind::TYPE_UINT64}}, {TypeKind::TYPE_FLOAT64, {TypeKind::TYPE_FLOAT64, TypeKind::TYPE_INT64}}, }; } // namespace bool IsUnaryOperator(TokenKind op) { return UNARY_EXPR_TYPE_MAP.find(op) != UNARY_EXPR_TYPE_MAP.end(); } bool IsBinaryOperator(TokenKind op) { return BINARY_EXPR_TYPE_MAP.find(op) != BINARY_EXPR_TYPE_MAP.end(); } // Find builtin function for unary expressions from operand Ty. bool IsBuiltinUnaryExpr(TokenKind op, const Ty& ty) { auto candidateMap = UNARY_EXPR_TYPE_MAP.find(op); if (candidateMap == UNARY_EXPR_TYPE_MAP.end()) { return false; } std::map typeCandidate = candidateMap->second; if (Ty::IsTyCorrect(&ty)) { if (typeCandidate.find(ty.kind) != typeCandidate.end()) { return true; } } return false; } // Find builtin function for binary expressions from left and right operand Ty. bool IsBuiltinBinaryExpr(TokenKind op, Ty& leftTy, Ty& rightTy) { if (!Ty::AreTysCorrect(std::set{Ptr(&leftTy), Ptr(&rightTy)})) { return false; } bool ret = false; // [] default check. if (op == TokenKind::LSQUARE) { return leftTy.kind == TypeKind::TYPE_ARRAY && rightTy.kind == TypeKind::TYPE_INT64; } else if (op == TokenKind::EXP) { auto found = SEMA_EXP_TYPES.find(leftTy.kind); return found != SEMA_EXP_TYPES.end() ? found->second.count(rightTy.kind) != 0 : false; } auto candidateMap = BINARY_EXPR_TYPE_MAP.find(op); if (candidateMap == BINARY_EXPR_TYPE_MAP.end()) { return false; } auto typeCandidate = candidateMap->second; if (op == TokenKind::LSHIFT || op == TokenKind::RSHIFT) { // For these two operators, the type of left expression is okay to not equal to the type of right expression. std::map::const_iterator search1 = typeCandidate.find(leftTy.kind); std::map::const_iterator search2 = typeCandidate.find(rightTy.kind); if (search1 != typeCandidate.end() && search2 != typeCandidate.end()) { ret = true; } } else if (&leftTy == &rightTy) { if (leftTy.kind == TypeKind::TYPE_UNIT && (op == TokenKind::EQUAL || op == TokenKind::NOTEQ)) { ret = true; } std::map::const_iterator search = typeCandidate.find(leftTy.kind); if (search != typeCandidate.end()) { ret = true; } } return ret; } TypeKind GetBuiltinBinaryExprReturnKind(TokenKind op, TypeKind leftOpType) { if (op == TokenKind::EXP) { // Return kind of exp is same as leftTy kind. auto found = SEMA_EXP_TYPES.find(leftOpType); return found != SEMA_EXP_TYPES.end() ? leftOpType : TypeKind::TYPE_INVALID; } auto candidateMap = BINARY_EXPR_TYPE_MAP.find(op); if (candidateMap != BINARY_EXPR_TYPE_MAP.end()) { auto found = candidateMap->second.find(leftOpType); if (found != candidateMap->second.end()) { return found->second; } } return TypeKind::TYPE_INVALID; } TypeKind GetBuiltinUnaryOpReturnKind(TokenKind op, TypeKind opType) { auto found = UNARY_EXPR_TYPE_MAP.find(op); if (found == UNARY_EXPR_TYPE_MAP.end()) { return TypeKind::TYPE_INVALID; } auto findType = found->second.find(opType); return findType == found->second.end() ? TypeKind::TYPE_INVALID : findType->second; } const std::map& GetBinaryOpTypeCandidates(TokenKind op) { auto found = BINARY_EXPR_TYPE_MAP.find(op); return found == BINARY_EXPR_TYPE_MAP.end() ? EMPTY_KIND_MAP : found->second; } const std::map& GetUnaryOpTypeCandidates(TokenKind op) { auto found = UNARY_EXPR_TYPE_MAP.find(op); return found == UNARY_EXPR_TYPE_MAP.end() ? EMPTY_KIND_MAP : found->second; } } // namespace Cangjie::TypeCheckUtil cangjie_compiler-1.0.7/src/Sema/BuiltInOperatorUtil.h000066400000000000000000000022611510705540100225550ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the builtin operator's type helper functions for TypeCheck. */ #ifndef CANGJIE_SEMA_BUILTIN_OPERATOR_UTIL_H #define CANGJIE_SEMA_BUILTIN_OPERATOR_UTIL_H #include #include "cangjie/AST/Node.h" namespace Cangjie::TypeCheckUtil { /* Check whether given type(s) and operator kind is builtin operation. */ bool IsBuiltinUnaryExpr(TokenKind op, const AST::Ty& ty); bool IsBuiltinBinaryExpr(TokenKind op, AST::Ty& leftTy, AST::Ty& rightTy); bool IsUnaryOperator(TokenKind op); bool IsBinaryOperator(TokenKind op); AST::TypeKind GetBuiltinBinaryExprReturnKind(TokenKind op, AST::TypeKind leftOpType); AST::TypeKind GetBuiltinUnaryOpReturnKind(TokenKind op, AST::TypeKind opType); const std::map& GetBinaryOpTypeCandidates(TokenKind op); const std::map& GetUnaryOpTypeCandidates(TokenKind op); } // namespace Cangjie::TypeCheckUtil #endif cangjie_compiler-1.0.7/src/Sema/CJMP/000077500000000000000000000000001510705540100172145ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/CJMP/CMakeLists.txt000066400000000000000000000005141510705540100217540ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB CJMP_SRC *.cpp) set(SEMA_SRC ${SEMA_SRC} ${CJMP_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/CJMP/CheckCJMP.cpp000066400000000000000000001014721510705540100214140ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the sema rules of CJMP feature. */ #include "MPTypeCheckerImpl.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Walker.h" #include "cangjie/AST/Clone.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; MPTypeCheckerImpl::MPTypeCheckerImpl(const CompilerInstance& ci) : typeManager(*ci.typeManager), diag(ci.diag), compileCommon(ci.invocation.globalOptions.outputMode == GlobalOptions::OutputMode::CHIR), compilePlatform(ci.invocation.globalOptions.commonPartCjo != std::nullopt) { } namespace { std::string GetExtendedTypeName(const ExtendDecl& ed) { auto& extendedType = ed.extendedType; if (Ty::IsTyCorrect(extendedType->ty.get())) { return extendedType->ty->IsPrimitive() ? extendedType->ty->String() : extendedType->ty->name; } else { return extendedType->ToString(); } } // Diag report void DiagNotMatchedDecl(DiagnosticEngine &diag, const AST::Decl& decl, const std::string& p0, const std::string& p2) { std::string info; if (decl.astKind == ASTKind::FUNC_DECL && decl.TestAttr(Attribute::CONSTRUCTOR)) { info = "constructor"; } else if (decl.TestAttr(Attribute::ENUM_CONSTRUCTOR)) { if (decl.outerDecl) { info = "enum '" + decl.outerDecl->identifier.GetRawText() + "' constructor" + " '" + decl.identifier.GetRawText() + "'"; } } else { if (decl.astKind == ASTKind::VAR_WITH_PATTERN_DECL) { info = "variable with pattern"; } else if (decl.astKind == ASTKind::EXTEND_DECL) { info = "extend '" + GetExtendedTypeName(StaticCast(decl)) + "'"; } else { info = DeclKindToString(decl) + " '" + decl.identifier.GetRawText() + "'"; } } diag.DiagnoseRefactor(DiagKindRefactor::sema_not_matched, decl, p0, info, p2); } inline void DiagNotMatchedCommonDecl(DiagnosticEngine &diag, const AST::Decl& decl) { DiagNotMatchedDecl(diag, decl, "platform", "common"); } inline void DiagNotMatchedPlatformDecl(DiagnosticEngine &diag, const AST::Decl& decl) { DiagNotMatchedDecl(diag, decl, "common", "platform"); } inline void DiagNotMatchedSuperType(DiagnosticEngine &diag, const AST::Decl& decl) { diag.DiagnoseRefactor(DiagKindRefactor::sema_platform_has_different_super_type, decl, DeclKindToString(decl)); } // Match nominative decl. bool MatchNominativeDecl(DiagnosticEngine &diag, Decl &commonDecl, Decl& platformDecl) { if (commonDecl.astKind != platformDecl.astKind) { diag.DiagnoseRefactor(DiagKindRefactor::platform_has_different_kind, platformDecl, DeclKindToString(platformDecl), DeclKindToString(commonDecl)); return false; } if (auto commonEnumDecl = DynamicCast(&commonDecl)) { auto platformEnumDecl = DynamicCast(&platformDecl); CJC_NULLPTR_CHECK(platformEnumDecl); if (commonEnumDecl->hasEllipsis) { platformDecl.EnableAttr(Attribute::COMMON_NON_EXHAUSTIVE); } else if (platformEnumDecl->hasEllipsis) { diag.DiagnoseRefactor(DiagKindRefactor::common_non_exaustive_platfrom_exaustive_mismatch, platformDecl, DeclKindToString(commonDecl), DeclKindToString(platformDecl)); } } return true; } // Update the dependencies: common -> platform one. void UpdateVarDependencies(const Decl& decl) { for (auto& dep : decl.dependencies) { if (dep->platformImplementation) { dep = dep->platformImplementation; } } } // Check common instance member without initializer not match with platform one. void Check4CommonInstanceVar(DiagnosticEngine& diag, const Decl& platformDecl) { auto platformDecls = platformDecl.GetMemberDeclPtrs(); for (auto decl : platformDecls) { if (decl->astKind == ASTKind::VAR_DECL && !decl->IsStaticOrGlobal() && decl->TestAttr(Attribute::COMMON)) { if (!decl->TestAttr(Attribute::COMMON_WITH_DEFAULT) && decl->platformImplementation == nullptr) { DiagNotMatchedPlatformDecl(diag, *decl); } } } } // Merge common nominative decl into platform one, do some match for fields. void MergeCommonIntoPlatform(DiagnosticEngine& diag, Decl& commonDecl, Decl& platformDecl) { CJC_ASSERT(commonDecl.TestAttr(AST::Attribute::COMMON)); CJC_ASSERT(platformDecl.TestAttr(AST::Attribute::PLATFORM)); if (!MatchNominativeDecl(diag, commonDecl, platformDecl)) { return; } auto& commonDecls = commonDecl.GetMemberDecls(); auto& platformDecls = platformDecl.GetMemberDecls(); std::vector> mergedDecls; mergedDecls.reserve(commonDecls.size() + platformDecls.size()); // Common instance member vars (including common member params) std::unordered_map commonVariablesIds; // General member instance member vars from member params std::unordered_map memberParamIds; // Collect candidates to be matched in common decl for (auto& commonDeclT : commonDecls) { auto newDecl = std::move(commonDeclT); newDecl->outerDecl = &platformDecl; newDecl->doNotExport = false; auto id = mergedDecls.size(); if (newDecl->astKind == ASTKind::VAR_DECL && !newDecl->IsStaticOrGlobal()) { auto varDecl = StaticCast(newDecl.get()); if (varDecl->TestAttr(Attribute::COMMON)) { commonVariablesIds.emplace(varDecl->identifier, id); } else if (varDecl->isMemberParam) { memberParamIds.emplace(varDecl->identifier, id); } } mergedDecls.emplace_back(std::move(newDecl)); } // Match instance member and merge into platform decl. for (auto& platformDeclT : platformDecls) { if (platformDeclT->astKind == ASTKind::VAR_DECL && !platformDeclT->IsStaticOrGlobal()) { auto varDecl = StaticCast(platformDeclT.get()); auto id = varDecl->identifier; if (platformDeclT->TestAttr(Attribute::PLATFORM)) { auto commonDeclIt = commonVariablesIds.find(id); if (commonDeclIt != commonVariablesIds.end()) { // match auto& commonDeclT = mergedDecls[commonDeclIt->second]; commonDeclT->platformImplementation = platformDeclT; std::swap(platformDeclT, commonDeclT); continue; } else { DiagNotMatchedCommonDecl(diag, *varDecl); } } else if (varDecl->isMemberParam) { if (memberParamIds.find(id) != memberParamIds.end()) { // General platform member params will merge into common if exist. continue; } } } // Merge platform members. mergedDecls.emplace_back(std::move(platformDeclT)); } std::swap(platformDecls, mergedDecls); // all the rest declarations need to be saved, because of, at least initializers of common // variables need to be analyzed. commonDecls.clear(); for (auto& decl : mergedDecls) { if (decl) { commonDecls.emplace_back(std::move(decl)); } } for (auto& decl : platformDecls) { UpdateVarDependencies(*decl); } // Check common member without initializer not match with platform one. Check4CommonInstanceVar(diag, platformDecl); commonDecl.doNotExport = true; commonDecl.platformImplementation = &platformDecl; } } // PrepareTypeCheck for CJMP void MPTypeCheckerImpl::PrepareTypeCheck4CJMP(Package& pkg) { if (!compilePlatform) { return; } // platform package part MergeCJMPNominals(pkg); } void MPTypeCheckerImpl::MergeCJMPNominals(Package& pkg) { std::unordered_map> matchedDecls; Walker walkerPackage(&pkg, [this, &matchedDecls](const Ptr& node) -> VisitAction { if (!node->IsDecl()) { return VisitAction::WALK_CHILDREN; } auto decl = StaticCast(node); if (decl->IsNominalDecl()) { auto key = DeclKindToString(*decl); if (decl->astKind == ASTKind::EXTEND_DECL) { key += GetExtendedTypeName(*StaticCast(decl)); std::set inheritedTypesName; for (auto& inheritedType : StaticCast(decl)->inheritedTypes) { inheritedTypesName.insert(inheritedType->ToString()); } std::for_each(inheritedTypesName.begin(), inheritedTypesName.end(), [&key](const std::string& name) { key += name; }); } else { key += decl->identifier; } if (auto it = matchedDecls.find(key); it != matchedDecls.end()) { auto matchedDecl = it->second; if (decl->TestAttr(Attribute::PLATFORM) && matchedDecl->TestAttr(Attribute::COMMON)) { MergeCommonIntoPlatform(diag, *matchedDecl, *decl); } else if (decl->TestAttr(Attribute::COMMON) && matchedDecl->TestAttr(Attribute::PLATFORM)) { MergeCommonIntoPlatform(diag, *decl, *matchedDecl); } } else if (decl->TestAnyAttr(Attribute::COMMON, Attribute::PLATFORM)) { matchedDecls.emplace(key, decl); } } return VisitAction::SKIP_CHILDREN; }); walkerPackage.Walk(); } namespace { // Check whether cls has general sub class. bool HasGeneralSubClass(const AST::ClassDecl& cls) { const AST::ClassDecl* cur = &cls; while (!cur->subDecls.empty()) { cur = StaticCast(*(cur->subDecls.begin())); if (!cur->TestAttr(Attribute::COMMON)) { return true; } } return false; } } // Precheck for CJMP void MPTypeCheckerImpl::PreCheck4CJMP(const Package& pkg) { if (!compileCommon) { return; } // common package part IterateToplevelDecls(pkg, [this](auto& decl) { if (decl->astKind == ASTKind::CLASS_DECL) { // Precheck for class PreCheckCJMPClass(*StaticCast(decl.get())); } }); } // Precheck for class void MPTypeCheckerImpl::PreCheckCJMPClass(const ClassDecl& cls) { // Report error for common open | abstract class without init inherited by general class in common part. if (cls.TestAttr(Attribute::COMMON) && cls.TestAnyAttr(Attribute::OPEN, Attribute::ABSTRACT)) { const auto& decls = cls.GetMemberDeclPtrs(); bool hasInit = std::any_of(decls.cbegin(), decls.cend(), [](const Ptr& decl) { return decl->TestAttr(Attribute::CONSTRUCTOR); }); if (!hasInit && HasGeneralSubClass(cls)) { // report error: please implement the constructor explicitly for common open class 'xxx' diag.DiagnoseRefactor(DiagKindRefactor::sema_common_open_class_no_init, cls, cls.identifier.Val()); } } } void MPTypeCheckerImpl::FilterOutCommonCandidatesIfPlatformExist( std::map>>& candidates) { for (auto& [names, funcs] : candidates) { bool hasPlatformCandidates = false; for (auto& func : funcs) { if (func->TestAttr(Attribute::PLATFORM)) { hasPlatformCandidates = true; break; } } if (hasPlatformCandidates) { funcs.erase( std::remove_if(funcs.begin(), funcs.end(), [](const Ptr decl) { return decl->TestAttr(Attribute::COMMON); }), funcs.end()); } } } // TypeCheck for CJMP void MPTypeCheckerImpl::RemoveCommonCandidatesIfHasPlatform(std::vector>& candidates) const { bool hasPlatformCandidate = std::find_if( candidates.begin(), candidates.end(), [](const Ptr decl) { return decl->TestAttr(Attribute::PLATFORM); } ) != candidates.end(); if (hasPlatformCandidate) { Utils::EraseIf(candidates, [](const Ptr decl) { return decl->TestAttr(Attribute::COMMON); }); } } namespace { // Collect common or platform decl. void CollectDecl( Ptr decl, std::vector>& commonDecls, std::vector>& platformDecls) { if (decl->TestAttr(Attribute::COMMON)) { commonDecls.emplace_back(decl); } else if (decl->TestAttr(Attribute::PLATFORM)) { platformDecls.emplace_back(decl); } } // Collect common and platform decls. void CollectCJMPDecls(Package& pkg, std::vector>& commonDecls, std::vector>& platformDecls) { std::function)> visitor = [&commonDecls, &platformDecls](const Ptr &node) { if (node->IsDecl() && node->astKind != ASTKind::PRIMARY_CTOR_DECL) { CollectDecl(StaticAs(node), commonDecls, platformDecls); } if (node->astKind == ASTKind::PACKAGE || node->astKind == ASTKind::FILE || node->IsNominalDecl() || node->IsNominalDeclBody()) { return VisitAction::WALK_CHILDREN; } return VisitAction::SKIP_CHILDREN; }; Walker walker(&pkg, visitor); walker.Walk(); } // Check whether the common decl must be matched with paltform decl. bool MustMatchWithPlatform(const Decl& decl) { CJC_ASSERT(decl.TestAttr(Attribute::COMMON)); if (decl.platformImplementation) { return false; } // var/func with default implementation if (decl.TestAttr(Attribute::COMMON_WITH_DEFAULT)) { return false; } // common member in interface allow no platform member, maybe use abstract attr. if (decl.outerDecl && decl.outerDecl->astKind == ASTKind::INTERFACE_DECL) { return false; } // var already initialized if (decl.astKind == ASTKind::VAR_DECL && decl.TestAttr(Attribute::INITIALIZED)) { return false; } // local var member if (decl.astKind == ASTKind::VAR_DECL && !decl.IsStaticOrGlobal() && decl.outerDecl->astKind != ASTKind::ENUM_DECL) { return false; } return true; } } bool NeedToReportMissingBody(const Decl& common, const Decl& platform) { if (common.outerDecl && common.TestAttr(Attribute::COMMON_WITH_DEFAULT) && !common.TestAttr(Attribute::ABSTRACT) && platform.TestAttr(Attribute::ABSTRACT)) { return true; } return false; } // PostTypeCheck for CJMP bool MPTypeCheckerImpl::MatchCJMPDeclAttrs( const std::vector& attrs, const Decl& common, const Decl& platform) const { for (auto attr : attrs) { if (common.TestAttr(attr) != platform.TestAttr(attr)) { if ((attr == Attribute::ABSTRACT || attr == Attribute::OPEN)) { // Error `sema_platform_has_different_modifier` will be reported if common have body, but platform not. // Diagnostic abort wrong modifiers is confusing. if (NeedToReportMissingBody(common, platform)) { continue; } if (platform.TestAttr(Attribute::ABSTRACT) && common.TestAttr(Attribute::OPEN)) { auto kindStr = common.astKind == ASTKind::FUNC_DECL ? "function" : "property"; diag.DiagnoseRefactor(DiagKindRefactor::sema_open_abstract_platform_can_not_replace_open_common, platform, kindStr, kindStr); } // ABSTRACT member can be replaced with OPEN if (common.TestAttr(Attribute::ABSTRACT) && platform.TestAttr(Attribute::OPEN)) { continue; } // Same as previous check, however static functions has no OPEN modifier if (common.TestAttr(Attribute::ABSTRACT) && common.TestAttr(Attribute::STATIC) && platform.TestAttr(Attribute::STATIC)) { continue; } } if (common.astKind == ASTKind::PROP_DECL && attr == Attribute::MUT) { if (common.TestAttr(attr)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_property_have_same_declaration_in_inherit_mut, platform, platform.identifier.Val()); } else { diag.DiagnoseRefactor(DiagKindRefactor::sema_property_have_same_declaration_in_inherit_immut, common, common.identifier.Val()); } } else if (common.astKind != ASTKind::FUNC_DECL) { // Keep silent due to overloaded common funcs. diag.DiagnoseRefactor( DiagKindRefactor::sema_platform_has_different_modifier, platform, DeclKindToString(platform)); } return false; } } return true; } bool MPTypeCheckerImpl::MatchCJMPDeclAnnotations( const std::vector& annotations, const AST::Decl& common, const AST::Decl& platform) const { for (auto anno : annotations) { if (common.HasAnno(anno) != platform.HasAnno(anno)) { // Keep silent due to overloaded common funcs. if (common.astKind != ASTKind::FUNC_DECL) { diag.DiagnoseRefactor( DiagKindRefactor::sema_platform_has_different_annotation, platform, DeclKindToString(platform)); } return false; } } return true; } // Match common nominal decl with platform for details. bool MPTypeCheckerImpl::MatchCommonNominalDeclWithPlatform(const InheritableDecl& commonDecl) { auto platformDecl = commonDecl.platformImplementation; if (platformDecl == nullptr) { DiagNotMatchedPlatformDecl(diag, commonDecl); return false; } // Match attributes (modifiers). std::vector matchedAttr = { Attribute::ABSTRACT, Attribute::PUBLIC, Attribute::OPEN, Attribute::PROTECTED, Attribute::C, Attribute::SEALED}; if (!MatchCJMPDeclAttrs(matchedAttr, commonDecl, *platformDecl)) { return false; } // Match annotations (built-in). if (!MatchCJMPDeclAnnotations({AnnotationKind::DEPRECATED}, commonDecl, *platformDecl)) { return false; } // Match super types. auto comSupInters = commonDecl.GetSuperInterfaceTys(); auto platSupInters = StaticCast(platformDecl)->GetSuperInterfaceTys(); if (comSupInters.size() != platSupInters.size()) { DiagNotMatchedSuperType(diag, *platformDecl); return false; } bool match = false; for (auto& comSupInter : comSupInters) { for (auto& platSupInter : platSupInters) { if (typeManager.IsTyEqual(comSupInter, platSupInter)) { match = true; break; } } if (!match) { DiagNotMatchedSuperType(diag, *platformDecl); return false; } } // Match super class if need. if (commonDecl.astKind == ASTKind::CLASS_DECL) { auto comSupClass = StaticCast(&commonDecl)->GetSuperClassDecl(); auto platSupIClass = StaticCast(platformDecl)->GetSuperClassDecl(); if (!typeManager.IsTyEqual(comSupClass->ty, platSupIClass->ty)) { DiagNotMatchedSuperType(diag, *platformDecl); return false; } } return true; } bool MPTypeCheckerImpl::IsCJMPDeclMatchable(const Decl& lhsDecl, const Decl& rhsDecl) const { bool isLeftCommon = lhsDecl.TestAttr(Attribute::COMMON); const Decl& commonDecl = isLeftCommon ? lhsDecl : rhsDecl; const Decl& platformDecl = isLeftCommon ? rhsDecl : lhsDecl; if (commonDecl.identifier.GetRawText() != platformDecl.identifier.GetRawText()) { return false; } if (platformDecl.IsMemberDecl() != commonDecl.IsMemberDecl()) { return false; } if (platformDecl.IsMemberDecl()) { CJC_NULLPTR_CHECK(platformDecl.outerDecl); CJC_NULLPTR_CHECK(commonDecl.outerDecl); if (platformDecl.outerDecl->rawMangleName != commonDecl.outerDecl->rawMangleName) { return false; } } // need check Attribute::ABSTRACT for abstract class? std::vector matchedAttrs = { Attribute::STATIC, Attribute::MUT, Attribute::PRIVATE, Attribute::PUBLIC, Attribute::PROTECTED, Attribute::FOREIGN, Attribute::UNSAFE, Attribute::C, Attribute::OPEN, Attribute::ABSTRACT}; return MatchCJMPDeclAttrs(matchedAttrs, commonDecl, platformDecl) && MatchCJMPDeclAnnotations({AnnotationKind::DEPRECATED, AnnotationKind::FROZEN}, commonDecl, platformDecl); } bool MPTypeCheckerImpl::TrySetPlatformImpl(Decl& platformDecl, Decl& commonDecl, const std::string& kind) { if (commonDecl.platformImplementation) { diag.DiagnoseRefactor(DiagKindRefactor::sema_multiple_common_implementations, commonDecl, kind); return false; } // common with default but platform without default if (commonDecl.outerDecl && commonDecl.TestAttr(Attribute::COMMON_WITH_DEFAULT) && platformDecl.TestAttr(Attribute::ABSTRACT)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_platform_member_must_have_implementation, platformDecl, platformDecl.identifier.Val(), commonDecl.outerDecl->identifier.Val()); return false; } commonDecl.platformImplementation = &platformDecl; commonDecl.doNotExport = true; return true; } bool MPTypeCheckerImpl::MatchCJMPFunction(FuncDecl& platformFunc, FuncDecl& commonFunc) { if (!IsCJMPDeclMatchable(platformFunc, commonFunc)) { return false; } if (!typeManager.IsFuncDeclSubType(platformFunc, commonFunc)) { return false; } auto& commonParams = commonFunc.funcBody->paramLists[0]->params; auto& platformParams = platformFunc.funcBody->paramLists[0]->params; for (size_t i = 0; i < commonFunc.funcBody->paramLists[0]->params.size(); i++) { if (commonParams[i]->isNamedParam != platformParams[i]->isNamedParam) { diag.DiagnoseRefactor(DiagKindRefactor::sema_platform_has_different_parameter, *platformParams[i]); return false; } if (commonParams[i]->isNamedParam && platformParams[i]->isNamedParam) { if (commonParams[i]->identifier.GetRawText() != platformParams[i]->identifier.GetRawText()) { diag.DiagnoseRefactor(DiagKindRefactor::sema_platform_has_different_parameter, *platformParams[i]); return false; } } // desugar platform default value, desugarDecl export all the time, assignment only export const value if (commonParams[i]->desugarDecl && !platformParams[i]->desugarDecl) { platformParams[i]->assignment = ASTCloner::Clone(commonParams[i]->assignment.get()); platformParams[i]->desugarDecl = ASTCloner::Clone(commonParams[i]->desugarDecl.get()); platformParams[i]->desugarDecl->outerDecl = platformFunc.outerDecl; platformParams[i]->EnableAttr(Attribute::HAS_INITIAL); } } // For init or primary constructor if (platformFunc.TestAttr(AST::Attribute::CONSTRUCTOR) || commonFunc.TestAttr(AST::Attribute::CONSTRUCTOR)) { if (!platformFunc.TestAttr(AST::Attribute::PRIMARY_CONSTRUCTOR) && commonFunc.TestAttr(AST::Attribute::PRIMARY_CONSTRUCTOR)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_platform_init_common_primary_constructor, commonFunc); return false; } for (size_t i = 0; i < platformParams.size(); ++i) { if (commonParams[i]->isMemberParam && !platformParams[i]->isMemberParam) { diag.DiagnoseRefactor(DiagKindRefactor::sema_platform_primary_unmatched_var_decl, *platformParams[i]); return false; } } } return TrySetPlatformImpl(platformFunc, commonFunc, "function"); } bool MPTypeCheckerImpl::MatchCJMPProp(PropDecl& platformProp, PropDecl& commonProp) { if (!IsCJMPDeclMatchable(platformProp, commonProp)) { return false; } if (!typeManager.IsTyEqual(platformProp.ty, commonProp.ty)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_platform_has_different_type, platformProp, "property"); } bool ret = TrySetPlatformImpl(platformProp, commonProp, "property " + platformProp.identifier); if (!platformProp.getters.empty() && !commonProp.getters.empty()) { ret &= TrySetPlatformImpl(*platformProp.getters[0], *commonProp.getters[0], "property getter " + platformProp.identifier); } if (!platformProp.setters.empty() && !commonProp.setters.empty()) { ret &= TrySetPlatformImpl(*platformProp.setters[0], *commonProp.setters[0], "property setter " + platformProp.identifier); } return ret; } bool MPTypeCheckerImpl::MatchCJMPEnumConstructor(Decl& platformDecl, Decl& commonDecl) { if (!IsCJMPDeclMatchable(platformDecl, commonDecl)) { return false; } if (platformDecl.astKind == ASTKind::FUNC_DECL) { auto platformFunc = StaticAs(&platformDecl); auto commonFunc = StaticAs(&commonDecl); if (!typeManager.IsFuncDeclEqualType(*platformFunc, *commonFunc)) { return false; } } auto enumName = platformDecl.outerDecl->identifier.GetRawText(); return TrySetPlatformImpl(platformDecl, commonDecl, "enum '" + enumName + "' constructor"); } bool MPTypeCheckerImpl::MatchCJMPVar(VarDecl& platformVar, VarDecl& commonVar) { if (!IsCJMPDeclMatchable(platformVar, commonVar)) { return false; } auto cType = commonVar.ty; auto pType = platformVar.ty; if (!typeManager.IsTyEqual(cType, pType)) { auto platformKind = platformVar.isVar ? "var" : "let"; diag.DiagnoseRefactor(DiagKindRefactor::sema_platform_has_different_type, platformVar, platformKind); } if (platformVar.isVar != commonVar.isVar) { auto platformKind = platformVar.isVar ? "var" : "let"; auto commonKind = commonVar.isVar ? "var" : "let"; diag.DiagnoseRefactor(DiagKindRefactor::sema_platform_var_not_match_let, platformVar, platformKind, commonKind); } if (platformVar.IsStaticOrGlobal()) { commonVar.platformImplementation = &platformVar; commonVar.doNotExport = true; } // Instance variables must already be matched CJC_ASSERT(commonVar.platformImplementation == &platformVar); return true; } bool MPTypeCheckerImpl::TryMatchVarWithPatternWithVarDecls( AST::VarWithPatternDecl& platformDecl, const std::vector>& commonDecls) { if (platformDecl.irrefutablePattern->astKind != ASTKind::TUPLE_PATTERN) { return false; } auto platformTuplePattern = StaticCast(platformDecl.irrefutablePattern.get()); bool matchedAll = true; for (auto& pattern : platformTuplePattern->patterns) { if (pattern->astKind != ASTKind::VAR_PATTERN) { matchedAll = false; break; } auto patternDecl = StaticCast(pattern.get()); if (!MatchPlatformDeclWithCommonDecls(*patternDecl->varDecl, commonDecls)) { matchedAll = false; } } return matchedAll; } bool MPTypeCheckerImpl::MatchPlatformDeclWithCommonDecls( AST::Decl& platformDecl, const std::vector>& commonDecls) { bool matched = false; bool isEnumConstructor = platformDecl.TestAttr(Attribute::ENUM_CONSTRUCTOR); auto kind = platformDecl.astKind; for (auto& commonDecl : commonDecls) { if (matched) { break; } if (commonDecl->astKind != kind) { continue; } if (isEnumConstructor && commonDecl->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { matched = MatchCJMPEnumConstructor(platformDecl, *commonDecl) || matched; } else if (kind == ASTKind::FUNC_DECL) { matched = MatchCJMPFunction(*StaticCast(&platformDecl), *StaticCast(commonDecl)) || matched; } else if (kind == ASTKind::PROP_DECL) { matched = MatchCJMPProp(*StaticCast(&platformDecl), *StaticCast(commonDecl)) || matched; } else if (kind == ASTKind::VAR_DECL) { matched = MatchCJMPVar(*StaticCast(&platformDecl), *StaticCast(commonDecl)) || matched; } } // VarWithPattern can match several decls from common part if (kind == ASTKind::VAR_WITH_PATTERN_DECL) { matched = TryMatchVarWithPatternWithVarDecls(*StaticCast(&platformDecl), commonDecls); } // For enum constructor if (!matched) { if (platformDecl.outerDecl && platformDecl.outerDecl->TestAttr(Attribute::COMMON_NON_EXHAUSTIVE)) { return false; } DiagNotMatchedCommonDecl(diag, platformDecl); } return matched; } // When there are several common extend without interfaces declared, // if there are same-named private functions declared, a clash is reported void MPTypeCheckerImpl::CheckCommonExtensions(std::vector>& commonDecls) { std::map, std::set> privateFunctionsOfExtensions; for (auto decl : commonDecls) { if (decl->astKind != ASTKind::EXTEND_DECL) { continue; } auto extendDecl = StaticCast(decl); if (!extendDecl->GetSuperInterfaceTys().empty()) { continue; } auto& privateFunctions = privateFunctionsOfExtensions[extendDecl->extendedType->ty]; for (auto& memberDecl : extendDecl->GetMemberDecls()) { if (!memberDecl->IsFuncOrProp() || !memberDecl->TestAttr(Attribute::PRIVATE)) { continue; } if (privateFunctions.find(memberDecl->rawMangleName) != privateFunctions.end()) { diag.DiagnoseRefactor(DiagKindRefactor::sema_common_direct_extension_has_duplicate_private_members, *memberDecl, extendDecl->extendedType->ToString(), memberDecl->IsFunc() ? "function" : "property", memberDecl->identifier.GetRawText()); } else { privateFunctions.emplace(memberDecl->rawMangleName); } } } } // A common declaration may have one or more matching specific declarations // in descending source sets (at most one specific per source set) void MPTypeCheckerImpl::CheckSpecificExtensions(std::vector>& platformDecls) { std::map, std::set>> superInterfaceTysOfExtensions; for (auto decl : platformDecls) { if (decl->astKind != ASTKind::EXTEND_DECL) { continue; } auto extendDecl = StaticCast(decl); auto& extendDeclsCache = superInterfaceTysOfExtensions[extendDecl->extendedType->ty]; for (auto item : extendDeclsCache) { if (extendDecl->GetSuperInterfaceTys() == item->GetSuperInterfaceTys()) { diag.DiagnoseRefactor(DiagKindRefactor::sema_platform_has_duplicate_extensions, *extendDecl, extendDecl->extendedType->ToString()); } } extendDeclsCache.emplace(extendDecl); } } void MPTypeCheckerImpl::MatchCJMPDecls(std::vector>& commonDecls, std::vector>& platformDecls) { for (auto& platformDecl : platformDecls) { CJC_ASSERT(platformDecl->TestAttr(Attribute::PLATFORM) && !platformDecl->TestAttr(Attribute::COMMON)); if (platformDecl->TestAttr(Attribute::IS_BROKEN) || platformDecl->IsNominalDecl()) { continue; } MatchPlatformDeclWithCommonDecls(*platformDecl, commonDecls); } std::unordered_set matchedIds; // Report error for common decl having no matched platform decl. for (auto& decl : commonDecls) { if (decl->IsNominalDecl() && MatchCommonNominalDeclWithPlatform(*StaticCast(decl))) { matchedIds.insert(decl->platformImplementation->identifier.Val()); } if (!MustMatchWithPlatform(*decl)) { continue; } DiagNotMatchedPlatformDecl(diag, *decl); } // Report error for platform nominal decl having no matched common decl. for (auto &decl : platformDecls) { if (decl->IsNominalDecl() && matchedIds.find(decl->identifier.Val()) == matchedIds.end()) { DiagNotMatchedCommonDecl(diag, *decl); } } } void MPTypeCheckerImpl::MatchPlatformWithCommon(Package& pkg) { std::vector> commonDecls; std::vector> platformDecls; CollectCJMPDecls(pkg, commonDecls, platformDecls); if (compileCommon) { // check common extensions CheckCommonExtensions(commonDecls); } else if (compilePlatform) { // match common decls and platform decls CheckSpecificExtensions(platformDecls); MatchCJMPDecls(commonDecls, platformDecls); } } cangjie_compiler-1.0.7/src/Sema/CJMP/MPTypeCheckerImpl.h000066400000000000000000000061071510705540100226560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the MPTypeCheckerImpl related classes, which provides typecheck capabilities for CJMP. */ #ifndef CANGJIE_SEMA_MPTYPECHECKER_IMPL_H #define CANGJIE_SEMA_MPTYPECHECKER_IMPL_H #include "cangjie/Sema/TypeManager.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Frontend/CompilerInstance.h" namespace Cangjie { class MPTypeCheckerImpl { public: #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND explicit MPTypeCheckerImpl(const CompilerInstance& ci); // PrepareTypeCheck for CJMP void PrepareTypeCheck4CJMP(AST::Package& pkg); // Precheck for CJMP void PreCheck4CJMP(const AST::Package& pkg); // TypeCheck for CJMP void RemoveCommonCandidatesIfHasPlatform(std::vector>& candidates) const; void FilterOutCommonCandidatesIfPlatformExist(std::map>>& candidates); void MatchPlatformWithCommon(AST::Package& pkg); #endif #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND private: // PrepareTypeCheck for CJMP void MergeCJMPNominals(AST::Package& pkg); // Precheck for CJMP void PreCheckCJMPClass(const AST::ClassDecl& cls); // PostTypeCheck for CJMP void CheckCommonExtensions(std::vector>& commonDecls); void CheckSpecificExtensions(std::vector>& platformDecls); void MatchCJMPDecls(std::vector>& commonDecls, std::vector>& platformDecls); bool MatchPlatformDeclWithCommonDecls(AST::Decl& platformDecl, const std::vector>& commonDecls); bool MatchCJMPEnumConstructor(AST::Decl& platformDecl, AST::Decl& commonDecl); bool MatchCJMPFunction(AST::FuncDecl& platformFunc, AST::FuncDecl& commonFunc); bool MatchCJMPProp(AST::PropDecl& platformProp, AST::PropDecl& commonProp); bool MatchCJMPVar(AST::VarDecl& platformVar, AST::VarDecl& commonVar); bool TryMatchVarWithPatternWithVarDecls(AST::VarWithPatternDecl& platformDecl, const std::vector>& commonDecls); bool IsCJMPDeclMatchable(const AST::Decl& lhsDecl, const AST::Decl& rhsDecl) const; bool MatchCJMPDeclAttrs( const std::vector& attrs, const AST::Decl& common, const AST::Decl& platform) const; bool MatchCJMPDeclAnnotations( const std::vector& annotations, const AST::Decl& common, const AST::Decl& platform) const; bool TrySetPlatformImpl(AST::Decl& platformDecl, AST::Decl& commonDecl, const std::string& kind); bool MatchCommonNominalDeclWithPlatform(const AST::InheritableDecl& commonDecl); private: TypeManager& typeManager; DiagnosticEngine& diag; bool compileCommon{false}; // true if compiling common part bool compilePlatform{false}; // true if compiling platform part #endif }; } // namespace Cangjie #endif // CANGJIE_SEMA_MPTYPECHECKER_IMPL_H cangjie_compiler-1.0.7/src/Sema/CMakeLists.txt000066400000000000000000000013041510705540100212210ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB SEMA_SRC *.cpp) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) add_subdirectory(Desugar) add_subdirectory(FFI) add_subdirectory(NativeFFI) add_subdirectory(GenericInstantiation) add_subdirectory(InheritanceChecker) add_subdirectory(LegalityOfUsage) add_subdirectory(Test) add_subdirectory(TypeCheckExpr) add_subdirectory(CJMP) add_library(CangjieSema OBJECT ${SEMA_SRC}) target_compile_options(CangjieSema PRIVATE ${CJC_EXTRA_WARNINGS}) cangjie_compiler-1.0.7/src/Sema/CalcConstExpr.cpp000066400000000000000000000103731510705540100217030ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements apis for the calculation of const expression. */ #include "TypeCheckerImpl.h" #include #include #include "Diags.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Utils/FloatFormat.h" using namespace Cangjie; using namespace AST; using namespace Sema; bool TypeChecker::TypeCheckerImpl::ChkFloatTypeOverflow(const LitConstExpr& lce) { auto info = GetFloatTypeInfoByKind(lce.ty->kind); switch (lce.constNumValue.asFloat.flowStatus) { case Expr::FlowStatus::OVER: { (void)diag.DiagnoseRefactor( DiagKindRefactor::sema_float_literal_too_large, lce, lce.ty->String(), info.max); return false; } case Expr::FlowStatus::UNDER: { (void)diag.DiagnoseRefactor( DiagKindRefactor::sema_float_literal_too_small, lce, lce.ty->String(), info.min); return false; } default: break; } uint64_t value = 0; switch (lce.ty->kind) { case TypeKind::TYPE_FLOAT16: { float f32 = static_cast(lce.constNumValue.asFloat.value); value = static_cast(FloatFormat::Float32ToFloat16(f32) << 1); // 1: remove the sign bit break; } case TypeKind::TYPE_FLOAT32: { float f32 = static_cast(lce.constNumValue.asFloat.value); // We need the original bit field of float, thus we can not use `static_cast` here. value = (*reinterpret_cast(&f32)) << 1; // 1: remove the sign bit break; } case TypeKind::TYPE_FLOAT64: { double f64 = static_cast(lce.constNumValue.asFloat.value); // We need the original bit field of double, thus we can not use `static_cast` here. value = (*reinterpret_cast(&f64)) << 1; // 1: remove the sign bit break; } // If the idea float value overflows, an error will be reported before this stage. default: { return true; } } #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wfloat-equal" #endif if (lce.constNumValue.asFloat.value != 0 && value == 0) { // round to zero #if defined(__clang__) #pragma clang diagnostic pop #endif (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_float_literal_too_small, lce, lce.ty->String(), info.min); return false; } else if (value == info.inf) { // match the infinity bits (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_float_literal_too_large, lce, lce.ty->String(), info.max); return false; } return true; } bool TypeChecker::TypeCheckerImpl::ChkLitConstExprRange(LitConstExpr& lce) { if (!Ty::IsTyCorrect(lce.ty)) { return false; } InitializeLitConstValue(lce); // Ty::IsTyCorrect(lce.ty) is checked by the caller. if (lce.ty->IsInteger()) { lce.constNumValue.asInt.SetOutOfRange(lce.ty); if (lce.constNumValue.asInt.IsOutOfRange()) { std::string typeName = lce.ty->String(); if (lce.ty->IsIdeal()) { typeName += "64"; } (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_exceed_num_value_range, lce, lce.stringValue, typeName); lce.ty = TypeManager::GetInvalidTy(); return false; } } else if (lce.ty->IsFloating()) { // Check whether floating-point literal exceeds the value range of target float type. (void)ChkFloatTypeOverflow(lce); } return true; } bool TypeChecker::TypeCheckerImpl::ReplaceIdealTy(Node& node) { if (!Ty::IsTyCorrect(node.ty)) { return false; } typeManager.ReplaceIdealTy(&node.ty); if (node.astKind == ASTKind::LIT_CONST_EXPR) { return ChkLitConstExprRange(*StaticAs(&node)); } return true; } cangjie_compiler-1.0.7/src/Sema/CheckAPILevel.cpp000066400000000000000000000531631510705540100215360ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file provides the function of checking APILevel customized macros. */ #include "CheckAPILevel.h" #include #include #include #include #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/StringConvertor.h" #include "cangjie/Utils/CastingTemplate.h" #include "cangjie/Utils/SafePointer.h" #include "cangjie/Utils/StdUtils.h" using namespace Cangjie; using namespace AST; using namespace APILevelCheck; namespace { constexpr std::string_view PKG_NAME_WHERE_APILEVEL_AT = "ohos.labels"; constexpr std::string_view APILEVEL_ANNO_NAME = "APILevel"; constexpr std::string_view SINCE_IDENTGIFIER = "since"; constexpr std::string_view LEVEL_IDENTGIFIER = "level"; constexpr std::string_view SYSCAP_IDENTGIFIER = "syscap"; constexpr std::string_view CFG_PARAM_LEVEL_NAME = "APILevel_level"; constexpr std::string_view CFG_PARAM_SYSCAP_NAME = "APILevel_syscap"; // For level check: const LevelType IFAVAIALBEL_LOWER_LIMITLEVEL = 19; LevelType Str2LevelType(std::string s) { return static_cast(Stoull(s).value_or(0)); } void ParseLevel(const Expr& e, APILevelAnnoInfo& apilevel, DiagnosticEngine& diag) { Ptr lce = nullptr; if (e.astKind == ASTKind::BINARY_EXPR) { auto be = StaticCast(&e); CJC_NULLPTR_CHECK(be->rightExpr); lce = DynamicCast(be->rightExpr.get()); } else if (e.astKind == ASTKind::LIT_CONST_EXPR) { lce = StaticCast(&e); } if (!lce || lce->kind != LitConstKind::INTEGER) { diag.DiagnoseRefactor(DiagKindRefactor::sema_only_literal_support, e, "integer"); return; } auto newLevel = Str2LevelType(lce->stringValue); apilevel.since = apilevel.since == 0 ? newLevel : std::min(newLevel, apilevel.since); } void ParseSince(const Expr& e, APILevelAnnoInfo& apilevel, DiagnosticEngine& diag) { Ptr lce = nullptr; if (e.astKind == ASTKind::BINARY_EXPR) { auto be = StaticCast(&e); CJC_NULLPTR_CHECK(be->rightExpr); lce = DynamicCast(be->rightExpr.get()); } else if (e.astKind == ASTKind::LIT_CONST_EXPR) { lce = StaticCast(&e); } if (!lce || lce->kind != LitConstKind::STRING) { diag.DiagnoseRefactor(DiagKindRefactor::sema_only_literal_support, e, "string"); return; } auto newLevel = Str2LevelType(lce->stringValue); apilevel.since = apilevel.since == 0 ? newLevel : std::min(newLevel, apilevel.since); } void ParseSysCap(const Expr& e, APILevelAnnoInfo& apilevel, DiagnosticEngine& diag) { Ptr lce = nullptr; if (e.astKind == ASTKind::CALL_EXPR) { auto ce = StaticCast(&e); CJC_ASSERT(ce->args.size() == 1 && ce->args[0]->expr); lce = DynamicCast(ce->args[0]->expr.get()); } else if (e.astKind == ASTKind::LIT_CONST_EXPR) { lce = StaticCast(&e); } if (!lce || lce->kind != LitConstKind::STRING) { diag.DiagnoseRefactor(DiagKindRefactor::sema_only_literal_support, e, "string"); return; } apilevel.syscap = lce->stringValue; } using ParseNameParamFunc = std::function; std::unordered_map parseNameParam = { {SINCE_IDENTGIFIER, ParseSince}, {LEVEL_IDENTGIFIER, ParseLevel}, {SYSCAP_IDENTGIFIER, ParseSysCap}, }; struct JsonObject; struct JsonPair { std::string key; std::vector valueStr; std::vector> valueObj; std::vector valueNum; }; struct JsonObject { std::vector> pairs; }; enum class StringMod { KEY, VALUE, }; std::string ParseJsonString(size_t& pos, const std::vector& in) { std::stringstream str; if (in[pos] == '"') { ++pos; while (pos < in.size() && in[pos] != '"') { str << in[pos]; ++pos; } } return str.str(); } uint64_t ParseJsonNumber(size_t& pos, const std::vector& in) { if (in[pos] < '0' || in[pos] > '9') { return 0; } std::stringstream num; while (pos < in.size() && in[pos] >= '0' && in[pos] <= '9') { num << in[pos]; ++pos; } if (num.str().size()) { --pos; } return Stoull(num.str()).value_or(0); } OwnedPtr ParseJsonObject(size_t& pos, const std::vector& in); void ParseJsonArray(size_t& pos, const std::vector& in, Ptr value) { if (in[pos] != '[') { return; } ++pos; while (pos < in.size()) { if (in[pos] == ' ' || in[pos] == '\n') { ++pos; continue; } if (in[pos] == '"') { value->valueStr.emplace_back(ParseJsonString(pos, in)); } if (in[pos] == '{') { value->valueObj.emplace_back(ParseJsonObject(pos, in)); } if (in[pos] == ']') { return; } ++pos; } } OwnedPtr ParseJsonObject(size_t& pos, const std::vector& in) { if (in[pos] != '{') { return nullptr; } ++pos; auto ret = MakeOwned(); auto mod = StringMod::KEY; while (pos < in.size()) { if (in[pos] == ' ' || in[pos] == '\n') { ++pos; continue; } if (in[pos] == '}') { return ret; } if (in[pos] == ':') { mod = StringMod::VALUE; } if (in[pos] == ',') { mod = StringMod::KEY; } if (in[pos] == '"') { if (mod == StringMod::KEY) { auto newData = MakeOwned(); newData->key = ParseJsonString(pos, in); ret->pairs.emplace_back(std::move(newData)); } else { ret->pairs.back()->valueStr.emplace_back(ParseJsonString(pos, in)); } } if (in[pos] >= '0' && in[pos] <= '9') { ret->pairs.back()->valueNum.emplace_back(ParseJsonNumber(pos, in)); } if (in[pos] == '{') { // The pos will be updated to the pos of matched '}'. ret->pairs.back()->valueObj.emplace_back(ParseJsonObject(pos, in)); } if (in[pos] == '[') { // The pos will be updated to the pos of matched ']'. ParseJsonArray(pos, in, ret->pairs.back().get()); } ++pos; } return ret; } std::vector GetJsonString(Ptr root, const std::string& key) { for (auto& v : root->pairs) { if (v->key == key) { return v->valueStr; } for (auto& o : v->valueObj) { auto ret = GetJsonString(o.get(), key); if (!ret.empty()) { return ret; } } } return {}; } Ptr GetJsonObject(Ptr root, const std::string& key, const size_t index) { for (auto& v : root->pairs) { if (v->key == key && v->valueObj.size() > index) { return v->valueObj[index].get(); } for (auto& o : v->valueObj) { auto ret = GetJsonObject(o.get(), key, index); if (ret) { return ret; } } } return nullptr; } void ClearAnnoInfoOfDepPkg(ImportManager& importManager) { auto clearAnno = [](Ptr node) { auto decl = DynamicCast(node); if (!decl) { return VisitAction::WALK_CHILDREN; } auto isCustomAnno = [](auto& a) { return a->kind == AnnotationKind::CUSTOM; }; decl->annotations.erase( std::remove_if(decl->annotations.begin(), decl->annotations.end(), isCustomAnno), decl->annotations.end()); return VisitAction::WALK_CHILDREN; }; auto cjdPaths = importManager.GetDepPkgCjdPaths(); for (auto& cjdInfo : cjdPaths) { auto depPkg = importManager.GetPackage(cjdInfo.first); if (!depPkg) { continue; } Walker(depPkg, clearAnno).Walk(); } } void MarkTargetAsExternalWeak(Ptr node) { if (!node) { return; } Ptr target = nullptr; if (node->GetTarget()) { target = node->GetTarget(); } else if (auto ce = DynamicCast(node); ce && ce->resolvedFunction) { target = ce->resolvedFunction; } if (!target) { return; } target->linkage = Linkage::EXTERNAL_WEAK; if (auto fd = DynamicCast(target)) { for (auto& param : fd->funcBody->paramLists[0]->params) { if (param->desugarDecl) { param->desugarDecl->linkage = Linkage::EXTERNAL_WEAK; } } if (fd->propDecl) { fd->propDecl->linkage = Linkage::EXTERNAL_WEAK; } } else if (auto md = DynamicCast(target)) { if (md->desugarDecl) { md->desugarDecl->linkage = Linkage::EXTERNAL_WEAK; } } else if (auto pd = DynamicCast(target)) { for (auto& getter : pd->getters) { if (!getter) { continue; } getter->linkage = Linkage::EXTERNAL_WEAK; } for (auto& setter : pd->setters) { if (!setter) { continue; } setter->linkage = Linkage::EXTERNAL_WEAK; } } if (target->outerDecl && target->outerDecl->IsNominalDecl()) { target->outerDecl->linkage = Linkage::EXTERNAL_WEAK; MarkTargetAsExternalWeak(target->outerDecl); } } } // namespace void APILevelAnnoChecker::ParseJsonFile(const std::vector& in) noexcept { size_t startPos = static_cast(std::find(in.begin(), in.end(), '{') - in.begin()); auto root = ParseJsonObject(startPos, in); auto deviceSysCapObj = GetJsonObject(root, "deviceSysCap", 0); std::map dev2SyscapsMap; for (auto& subObj : deviceSysCapObj->pairs) { SysCapSet syscapsOneDev; for (auto path : subObj->valueStr) { std::vector buffer; std::string failedReason; FileUtil::ReadBinaryFileToBuffer(path, buffer, failedReason); if (!failedReason.empty()) { diag.DiagnoseRefactor( DiagKindRefactor::module_read_file_to_buffer_failed, DEFAULT_POSITION, path, failedReason); return; } startPos = static_cast(std::find(buffer.begin(), buffer.end(), '{') - buffer.begin()); auto rootOneDevice = ParseJsonObject(startPos, buffer); auto curSyscaps = GetJsonString(rootOneDevice, "SysCaps"); for (auto syscap : curSyscaps) { if (Utils::NotIn(syscap, syscapsOneDev)) { syscapsOneDev.emplace_back(syscap); } } } dev2SyscapsMap.emplace(subObj->key, syscapsOneDev); } std::optional lastSyscap = std::nullopt; for (auto& dev2Syscaps : dev2SyscapsMap) { SysCapSet& curSyscaps = dev2Syscaps.second; std::sort(curSyscaps.begin(), curSyscaps.end()); SysCapSet intersection; if (lastSyscap.has_value()) { std::set_intersection(lastSyscap.value().begin(), lastSyscap.value().end(), curSyscaps.begin(), curSyscaps.end(), std::back_inserter(intersection)); } else { intersection = curSyscaps; } lastSyscap = intersection; for (auto syscap : curSyscaps) { if (Utils::NotIn(syscap, unionSet)) { unionSet.emplace_back(syscap); } } } intersectionSet = lastSyscap.value(); } void APILevelAnnoChecker::ParseOption() noexcept { auto& option = ci.invocation.globalOptions; auto found = option.passedWhenKeyValue.find(std::string(CFG_PARAM_LEVEL_NAME)); if (found != option.passedWhenKeyValue.end()) { globalLevel = Str2LevelType(found->second); optionWithLevel = true; } found = option.passedWhenKeyValue.find(std::string(CFG_PARAM_SYSCAP_NAME)); if (found != option.passedWhenKeyValue.end()) { auto syscapsCfgPath = found->second; std::vector jsonContent; std::string failedReason; FileUtil::ReadBinaryFileToBuffer(syscapsCfgPath, jsonContent, failedReason); if (!failedReason.empty()) { diag.DiagnoseRefactor( DiagKindRefactor::module_read_file_to_buffer_failed, DEFAULT_POSITION, syscapsCfgPath, failedReason); return; } ParseJsonFile(jsonContent); optionWithSyscap = true; } } bool APILevelAnnoChecker::IsAnnoAPILevel(Ptr anno, [[maybe_unused]] const Decl& decl) { if (ctx && ctx->curPackage && ctx->curPackage->fullPackageName == PKG_NAME_WHERE_APILEVEL_AT) { return anno->identifier == APILEVEL_ANNO_NAME; } if (!anno || anno->identifier != APILEVEL_ANNO_NAME) { return false; } auto target = anno->baseExpr ? anno->baseExpr->GetTarget() : nullptr; if (target && target->curFile && target->curFile->curPackage && target->curFile->curPackage->fullPackageName != PKG_NAME_WHERE_APILEVEL_AT) { return false; } return true; } APILevelAnnoInfo APILevelAnnoChecker::Parse(const Decl& decl) { if (decl.annotations.empty()) { return APILevelAnnoInfo(); } if (auto found = levelCache.find(&decl); found != levelCache.end()) { return found->second; } APILevelAnnoInfo ret; for (auto& anno : decl.annotations) { if (!anno || !IsAnnoAPILevel(anno.get(), decl)) { continue; } for (size_t i = 0; i < anno->args.size(); ++i) { std::string argName = anno->args[i]->name.Val(); if (parseNameParam.count(argName) <= 0) { continue; } std::string preSyscap = ret.syscap; parseNameParam[argName](*anno->args[i]->expr.get(), ret, diag); if (!preSyscap.empty() && preSyscap != ret.syscap) { diag.DiagnoseRefactor(DiagKindRefactor::sema_apilevel_multi_diff_syscap, decl); } } // In the APILevel definition, only "since" does not provide a default value. Here, the alert indicates that // there is an issue with the APILevel annotation, which may originnate from the cj.d file. if (ret.since == 0) { diag.DiagnoseRefactor(DiagKindRefactor::sema_apilevel_missing_arg, anno->begin, "since!: String"); } } levelCache[&decl] = ret; return ret; } bool APILevelAnnoChecker::CheckLevel( const Node& node, const Decl& target, const APILevelAnnoInfo& scopeAPILevel, bool reportDiag) { if (!optionWithLevel) { return true; } LevelType scopeLevel = scopeAPILevel.since != 0 ? scopeAPILevel.since : globalLevel; auto targetAPILevel = Parse(target); if (targetAPILevel.since > scopeLevel && !node.begin.IsZero()) { if (reportDiag) { diag.DiagnoseRefactor(DiagKindRefactor::sema_apilevel_ref_higher, node, target.identifier.Val(), std::to_string(targetAPILevel.since), std::to_string(scopeLevel)); } return false; } return true; } bool APILevelAnnoChecker::CheckSyscap( const Node& node, const Decl& target, const APILevelAnnoInfo& scopeAPILevel, bool reportDiag) { if (!optionWithSyscap) { return true; } SysCapSet scopeSyscaps = unionSet; if (!scopeAPILevel.syscap.empty()) { scopeSyscaps.emplace_back(scopeAPILevel.syscap); } auto targetAPILevel = Parse(target); std::string targetLevel = targetAPILevel.syscap; if (targetLevel.empty()) { return true; } auto diagForSyscap = [this, &scopeSyscaps, &node, &targetLevel](DiagKindRefactor kind) { auto builder = diag.DiagnoseRefactor(kind, node, targetLevel); std::stringstream scopeSyscapsStr; // 3 is maximum number of syscap limit. for (size_t i = 0; i < std::min(scopeSyscaps.size(), static_cast(3)); ++i) { std::string split = scopeSyscaps[i] == scopeSyscaps.back() ? "" : ", "; scopeSyscapsStr << scopeSyscaps[i] << split; } if (scopeSyscaps.size() > 3) { scopeSyscapsStr << "..."; } builder.AddNote("the following syscaps are supported: " + scopeSyscapsStr.str()); }; auto found = std::find(scopeSyscaps.begin(), scopeSyscaps.end(), targetLevel); if (found == scopeSyscaps.end() && !node.begin.IsZero()) { if (reportDiag) { diagForSyscap(DiagKindRefactor::sema_apilevel_syscap_error); } return false; } scopeSyscaps = intersectionSet; if (!scopeAPILevel.syscap.empty()) { scopeSyscaps.emplace_back(scopeAPILevel.syscap); } found = std::find(scopeSyscaps.begin(), scopeSyscaps.end(), targetLevel); if (found == scopeSyscaps.end() && !node.begin.IsZero()) { if (reportDiag) { diagForSyscap(DiagKindRefactor::sema_apilevel_syscap_warning); } return false; } return true; } bool APILevelAnnoChecker::CheckNode(Ptr node, APILevelAnnoInfo& scopeAPILevel, bool reportDiag) { if (!node) { return true; } auto target = node->GetTarget(); if (auto ce = DynamicCast(node); ce && ce->resolvedFunction) { target = ce->resolvedFunction; } if (!target) { return true; } bool ret = true; if (target->TestAttr(Attribute::CONSTRUCTOR) && target->outerDecl) { ret = CheckLevel(*node, *target->outerDecl, scopeAPILevel, reportDiag) && ret; ret = CheckSyscap(*node, *target->outerDecl, scopeAPILevel, reportDiag) && ret; if (!ret) { return false; } } ret = CheckLevel(*node, *target, scopeAPILevel, reportDiag) && ret; ret = CheckSyscap(*node, *target, scopeAPILevel, reportDiag) && ret; return ret; } void APILevelAnnoChecker::CheckIfAvailableExpr(IfAvailableExpr& iae, APILevelAnnoInfo& scopeAPILevel) { if (!iae.desugarExpr || iae.desugarExpr->astKind != ASTKind::IF_EXPR) { return; } auto ifExpr = StaticCast(iae.desugarExpr.get()); Ptr arg = iae.GetArg(); if (parseNameParam.count(arg->name.Val()) <= 0) { return; } auto ifScopeAPILevel = APILevelAnnoInfo(); parseNameParam[arg->name.Val()](*ifExpr->condExpr, ifScopeAPILevel, diag); if (ifScopeAPILevel.since != 0 && ifScopeAPILevel.since < IFAVAIALBEL_LOWER_LIMITLEVEL) { diag.DiagnoseRefactor(DiagKindRefactor::sema_ifavailable_level_limit, *arg); return; } // if branch. auto checkerIf = [this, &ifScopeAPILevel, &scopeAPILevel](Ptr node) -> VisitAction { if (auto e = DynamicCast(node)) { CheckIfAvailableExpr(*e, ifScopeAPILevel); return VisitAction::SKIP_CHILDREN; } // If the reference meets the 'IfAvaliable' condition but does not meet the global APILevel configuration, set // linkage to 'EXTERNAL_WEAK'. auto ret = CheckNode(node, ifScopeAPILevel); if (ret && !CheckNode(node, scopeAPILevel, false)) { MarkTargetAsExternalWeak(node); } if (!ret) { return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; Walker(ifExpr->thenBody.get(), checkerIf).Walk(); // else branch. auto checkerElse = [this, &scopeAPILevel](Ptr node) -> VisitAction { if (auto e = DynamicCast(node)) { CheckIfAvailableExpr(*e, scopeAPILevel); return VisitAction::SKIP_CHILDREN; } if (!CheckNode(node, scopeAPILevel)) { return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; Walker(ifExpr->elseBody.get(), checkerElse).Walk(); } void APILevelAnnoChecker::Check(Package& pkg) { ctx = ci.GetASTContextByPackage(&pkg); std::vector> scopeDecl; auto checker = [this, &scopeDecl](Ptr node) -> VisitAction { if (auto decl = DynamicCast(node)) { scopeDecl.emplace_back(decl); return VisitAction::WALK_CHILDREN; } auto scopeAPILevel = APILevelAnnoInfo(); for (auto it = scopeDecl.rbegin(); it != scopeDecl.rend(); ++it) { scopeAPILevel = Parse(**it); if (scopeAPILevel.since != 0) { break; } } if (auto iae = DynamicCast(node)) { scopeAPILevel.since = scopeAPILevel.since == 0 ? globalLevel : scopeAPILevel.since; CheckIfAvailableExpr(*iae, scopeAPILevel); return VisitAction::SKIP_CHILDREN; } if (!CheckNode(node, scopeAPILevel)) { return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; auto popScope = [&scopeDecl](Ptr node) -> VisitAction { if (!scopeDecl.empty() && scopeDecl.back() == node) { scopeDecl.pop_back(); } return VisitAction::WALK_CHILDREN; }; Walker(&pkg, checker, popScope).Walk(); // Clear the annotation information of the dependency package to avoid chir failure. // In the LSP scenario, annotation information still needs to be saved after SEMA. if (!ci.invocation.globalOptions.enableMacroInLSP) { ClearAnnoInfoOfDepPkg(importManager); } } cangjie_compiler-1.0.7/src/Sema/CheckAPILevel.h000066400000000000000000000067021510705540100212000ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares APILevelAnnoChecker class and APILevel information. */ #ifndef CHECK_APILEVEL_H #define CHECK_APILEVEL_H #include #include #include #include "cangjie/AST/Node.h" #include "cangjie/AST/NodeX.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Option/Option.h" #include "cangjie/Sema/TypeManager.h" namespace Cangjie { namespace APILevelCheck { /** * It should same as cangjie code follow: * ``` * @Annotation * public class APILevel { * // since * public let since: String * public let atomicservice: Bool * public let crossplatform: Bool * public let deprecated: ?String * public let form: Bool * public let permission: ?PermissionValue * public let syscap: String * public let throwexception: Bool * public let workerthread: Bool * public let systemapi: Bool * public const init(since!: String, atomicservice!: Bool = false, crossplatform!: Bool = false, * deprecated!: ?String = 0, form!: Bool = false, permission!: ?PermissionValue = None, * syscap!: String = "", throwexception!: Bool = false, workerthread!: Bool = false, systemapi!: Bool = false) { * this.since = since * this.atomicservice = atomicservice * this.crossplatform = crossplatform * this.deprecated = deprecated * this.form = form * this.permission = permission * this.syscap = syscap * this.throwexception = throwexception * this.workerthread = workerthread * this.systemapi = systemapi * } * } * ``` */ using LevelType = uint32_t; struct APILevelAnnoInfo { LevelType since{0}; std::string syscap{""}; }; using SysCapSet = std::vector; class APILevelAnnoChecker { public: APILevelAnnoChecker(CompilerInstance& ci, DiagnosticEngine& diag, ImportManager& importManager) : ci(ci), diag(diag), importManager(importManager) { ParseOption(); } APILevelAnnoInfo Parse(const AST::Decl& decl); void Check(AST::Package& pkg); private: void ParseOption() noexcept; void ParseJsonFile(const std::vector& in) noexcept; bool CheckLevel( const AST::Node& node, const AST::Decl& target, const APILevelAnnoInfo& scopeAPILevel, bool reportDiag); bool CheckSyscap( const AST::Node& node, const AST::Decl& target, const APILevelAnnoInfo& scopeAPILevel, bool reportDiag); bool CheckNode(Ptr node, APILevelAnnoInfo& scopeAPILevel, bool reportDiag = true); void CheckIfAvailableExpr(AST::IfAvailableExpr& iae, APILevelAnnoInfo& scopeAPILevel); bool IsAnnoAPILevel(Ptr anno, const AST::Decl& decl); private: CompilerInstance& ci; DiagnosticEngine& diag; ImportManager& importManager; LevelType globalLevel{0}; SysCapSet intersectionSet; SysCapSet unionSet; std::unordered_map, APILevelAnnoInfo> levelCache; Ptr ctx; bool optionWithLevel{false}; bool optionWithSyscap{false}; }; } // namespace APILevelCheck } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Sema/CheckFuncInline.cpp000066400000000000000000000217221510705540100221630ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the function is inline. */ #include "TypeCheckerImpl.h" #include #include #include #include "TypeCheckUtil.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Utils.h" #include "cangjie/Utils/CheckUtils.h" using namespace Cangjie; using namespace AST; using namespace std::placeholders; namespace { constexpr size_t MAX_NODE_NUMBER = 32; bool CanExportForInline(const FuncDecl& fd) { bool ret = true; if (fd.outerDecl) { if (!fd.outerDecl->IsExportedDecl()) { ret = false; } } auto checkFd = fd.ownerFunc ? fd.ownerFunc.get() : &fd; return checkFd->IsExportedDecl() && ret; } bool CanCountedRefExprOrMemberExpr(const Expr& expr) { Ptr target = expr.GetTarget(); if (target == nullptr) { return true; } if (target->astKind == ASTKind::VAR_DECL) { // If variable is a global definition, it must be external. // If variable is a static member decl, it must be public or protected member in exported decl. // NOTE: instance member variable can always be accessed from object no matter is public or private. if (target->TestAttr(Attribute::GLOBAL) && !target->IsExportedDecl()) { return false; } else if (target->TestAttr(Attribute::STATIC) && target->outerDecl && target->outerDecl->IsNominalDecl() && (!target->outerDecl->IsExportedDecl() || !target->IsExportedDecl())) { return false; } } else if (target->astKind == ASTKind::FUNC_DECL) { auto funcDecl = RawStaticCast(target); if (funcDecl->TestAttr(Attribute::INTRINSIC)) { return true; } if (funcDecl->TestAnyAttr(Attribute::FOREIGN, Attribute::C) || !CanExportForInline(*funcDecl)) { return false; } } return true; } bool ContainsInternalType(const Ptr& ty) { if (!Ty::IsTyCorrect(ty)) { return true; } // If decl is not external and not generic, it is internal used type decl. if (auto decl = Ty::GetDeclPtrOfTy(ty); decl && !decl->IsExportedDecl() && !decl->TestAttr(Attribute::GENERIC)) { return true; } for (auto it : ty->typeArgs) { if (ContainsInternalType(it)) { return true; } } return false; } VisitAction CountNodeNumber(const Expr& expr, bool& result, size_t& nodeNum) { if (nodeNum >= MAX_NODE_NUMBER) { result = false; return VisitAction::STOP_NOW; } if (expr.desugarExpr != nullptr) { return VisitAction::WALK_CHILDREN; } if (ContainsInternalType(expr.ty)) { result = false; return VisitAction::STOP_NOW; } switch (expr.astKind) { case ASTKind::BLOCK: return VisitAction::WALK_CHILDREN; case ASTKind::LAMBDA_EXPR: { result = false; return VisitAction::STOP_NOW; } case ASTKind::REF_EXPR: case ASTKind::MEMBER_ACCESS: { if (!CanCountedRefExprOrMemberExpr(expr)) { result = false; return VisitAction::STOP_NOW; } nodeNum++; return VisitAction::WALK_CHILDREN; } default: nodeNum++; return VisitAction::WALK_CHILDREN; } } bool IsInlineFunction(const FuncDecl& fd) { if (!CanExportForInline(fd)) { return false; } // Enum constructor not have function body, do not export. // The constructor source code does not contain the default value. if (fd.TestAnyAttr(Attribute::ENUM_CONSTRUCTOR, Attribute::CONSTRUCTOR)) { return false; } if (auto pd = fd.propDecl; pd && !pd->HasAnno(AnnotationKind::FROZEN) && !pd->isConst) { return false; } else if (auto of = fd.ownerFunc; of && !of->HasAnno(AnnotationKind::FROZEN) && !of->isConst) { return false; } else if (!fd.isFrozen && !fd.isConst) { return false; } if (fd.ownerFunc && fd.ownerFunc->TestAttr(Attribute::CONSTRUCTOR)) { return false; } if (fd.TestAnyAttr(Attribute::C, Attribute::FOREIGN)) { return false; } if (fd.TestAttr(Attribute::OPEN)) { return false; } if (fd.TestAttr(Attribute::INTRINSIC)) { return false; } if (fd.TestAttr(Attribute::ABSTRACT) && (fd.isGetter || fd.isSetter)) { return false; } if (fd.TestAttr(AST::Attribute::MAIN_ENTRY) || fd.identifier == MAIN_INVOKE || fd.identifier == TEST_ENTRY_NAME) { return false; } if (fd.outerDecl != nullptr && fd.outerDecl->identifier == CPOINTER_NAME) { return false; } bool result = true; size_t nodeNum = 0; auto walkFunc = [&result, &nodeNum](Ptr node) -> VisitAction { if (Is(node) && node->TestAttr(Attribute::HAS_INITIAL)) { return VisitAction::SKIP_CHILDREN; } if (auto ex = DynamicCast(node); ex) { return CountNodeNumber(*ex, result, nodeNum); } else if (node->astKind == ASTKind::FUNC_DECL && !node->TestAttr(Attribute::HAS_INITIAL)) { // NOTE: parameter default function is not treated as nested function. result = false; return VisitAction::STOP_NOW; } else { return VisitAction::WALK_CHILDREN; } }; Walker walker(fd.funcBody.get(), walkFunc); walker.Walk(); return result; } void CheckDefaultParameterFunctionIsInline(const FuncDecl& fd) { auto& params = fd.funcBody->paramLists[0]->params; for (auto& param : params) { if (param->desugarDecl != nullptr) { param->desugarDecl->isInline = param->desugarDecl->ownerFunc->isInline; } } } void ProcessFuncDeclWithInline(FuncDecl& fd) { if (fd.TestAttr(Attribute::GENERIC) || fd.TestAttr(Attribute::MACRO_FUNC)) { return; } auto fdIsInline = IsInlineFunction(fd); fd.isInline = fdIsInline; if (CanExportForInline(fd) && !fd.TestAttr(Attribute::GENERIC) && !fd.TestAttr(Attribute::COMPILER_ADD)) { CheckDefaultParameterFunctionIsInline(fd); } } void CheckFuncDeclIsInline(Package& pkg) { // Using ordered set to keep functions in definition order for bep. auto walkFunc = [](Ptr node) -> VisitAction { switch (node->astKind) { case ASTKind::FUNC_DECL: { auto fd = RawStaticCast(node); ProcessFuncDeclWithInline(*fd); return VisitAction::SKIP_CHILDREN; } case ASTKind::VAR_DECL: case ASTKind::INTERFACE_DECL: return VisitAction::SKIP_CHILDREN; default: return VisitAction::WALK_CHILDREN; } }; Walker walker(&pkg, walkFunc); walker.Walk(); } } // namespace void TypeChecker::TypeCheckerImpl::CheckInlineFunctions(const std::vector>& pkgs) const { std::vector> allImportInlineFunctions; for (auto& pkg : pkgs) { for (auto it = pkg->srcImportedNonGenericDecls.begin(); it != pkg->srcImportedNonGenericDecls.end();) { if ((*it)->astKind != ASTKind::FUNC_DECL) { ++it; continue; } auto func = StaticCast(*it); allImportInlineFunctions.emplace_back(func); it = pkg->srcImportedNonGenericDecls.erase(it); } } auto opts = ci->invocation.globalOptions; if (!opts.chirLLVM || opts.enableCompileTest || opts.enableHotReload || opts.mock == MockMode::ON) { return; // If current compilation is not supporting inlining, quit here. } // 2. Collect all inline functions defined in source package and all called imported inline functions. for (auto pkg : pkgs) { if (pkg->TestAttr(Attribute::IMPORTED) || pkg->isMacroPackage) { continue; } // Add flags to functions that match being inlined CheckFuncDeclIsInline(*pkg); } // 3. Copy inline functions back to the 'srcImportedNonGenericDecls' which is used for genericInstantiation. for (auto pkg : pkgs) { // Sort final 'inlineFuncDecls' for bep. std::sort(pkg->inlineFuncDecls.begin(), pkg->inlineFuncDecls.end(), CompNodeByPos); // Only needs to copy collected inline functions for source package. if (!pkg->TestAttr(Attribute::IMPORTED)) { pkg->inlineFuncDecls.insert( pkg->inlineFuncDecls.end(), allImportInlineFunctions.begin(), allImportInlineFunctions.end()); std::copy_if(pkg->inlineFuncDecls.begin(), pkg->inlineFuncDecls.end(), std::back_inserter(pkg->srcImportedNonGenericDecls), [](auto it) { return it->TestAttr(Attribute::IMPORTED); }); } } } cangjie_compiler-1.0.7/src/Sema/CheckFunctionLinkage.cpp000066400000000000000000000543321510705540100232140ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements setting linkages for functions. */ #include "TypeCheckUtil.h" #include "TypeCheckerImpl.h" #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" #include "cangjie/Driver/StdlibMap.h" using namespace Cangjie; using namespace AST; namespace { bool IsSpecialFunction(const FuncDecl& fd) { if (fd.TestAnyAttr(Attribute::IMPORTED, Attribute::FOREIGN)) { return true; } auto isGetCommandLineArgsFunc = fd.fullPackageName == CORE_PACKAGE_NAME && fd.identifier == GET_COMMAND_LINE_ARGS; auto isImplictUsed = Utils::In(fd.fullPackageName, {CORE_PACKAGE_NAME, AST_PACKAGE_NAME}) && fd.TestAttr(Attribute::IMPLICIT_USED); auto isToAny = fd.identifier == TO_ANY; auto isMainInvoke = fd.identifier == MAIN_INVOKE; return isGetCommandLineArgsFunc || isImplictUsed || isToAny || isMainInvoke; } void MarkFunctionLinkage(FuncDecl& fd, const Linkage linkage) { fd.linkage = linkage; // Set linkage for the funcDecl obtained by desugaring parameters with default values if (fd.funcBody && !fd.funcBody->paramLists.empty()) { auto& funcParams = fd.funcBody->paramLists[0]->params; for (auto& param : funcParams) { if (param->desugarDecl) { param->desugarDecl->linkage = linkage; } } } if (fd.propDecl) { fd.propDecl->linkage = linkage; } } void MarkFunctionAsInternalLinkage(FuncDecl& fd) { MarkFunctionLinkage(fd, Linkage::INTERNAL); } void MarkFunctionAsExternalLinkage(FuncDecl& fd) { if (auto id = DynamicCast(fd.outerDecl); id && id->linkage != Linkage::EXTERNAL) { id->linkage = Linkage::EXTERNAL; } if (fd.outerDecl) { if (fd.outerDecl->IsFunc()) { return; // Nested fuction should always be internal. } } else { if (!fd.TestAttr(Attribute::GLOBAL)) { return; // Non-toplevel/non-member function should always be internal. } } MarkFunctionLinkage(fd, Linkage::EXTERNAL); } inline void MarkVarDeclAbstractLinkage(VarDeclAbstract& vda) { if (vda.IsExportedDecl() || vda.TestAttr(Attribute::IMPLICIT_USED)) { vda.linkage = Cangjie::Linkage::EXTERNAL; } else { vda.linkage = Cangjie::Linkage::INTERNAL; } } inline void MarkTypeAliasDeclLinkage(TypeAliasDecl& tad) { if (tad.IsExportedDecl() || tad.TestAttr(Attribute::IMPLICIT_USED)) { tad.linkage = Cangjie::Linkage::EXTERNAL; } else { tad.linkage = Cangjie::Linkage::INTERNAL; } } /** * Some functions and classes in the core package are used by the compiler directly. * We filter them by their files. */ bool IsIntrinsicFile(const File& file) { if (file.curPackage->fullPackageName != CORE_PACKAGE_NAME) { return false; } static const std::set intrinsicFileNames{ "future.cj", "runtime_call_throw_exception.cj", }; return intrinsicFileNames.count(file.fileName) > 0; } inline bool IsInternalSrcExportedFunction(const FuncDecl& fd) { return !fd.IsExportedDecl() && CanBeSrcExported(fd); } /** * Auxiliary functions to iterate all functions in a nominal declaration. */ void IterateAllFunctionInStruct(InheritableDecl& inheritableDecl, const std::function action) { for (auto& decl : inheritableDecl.GetMemberDecls()) { if (decl->astKind == ASTKind::FUNC_DECL) { auto funcDecl = StaticAs(decl.get()); action(*funcDecl); } if (decl->astKind == ASTKind::PROP_DECL) { auto propDecl = StaticAs(decl.get()); for (OwnedPtr& funcDecl : propDecl->getters) { action(*funcDecl.get()); } for (OwnedPtr& funcDecl : propDecl->setters) { action(*funcDecl.get()); } } } } /** * Auxiliary functions to iterate all variables and properties in a nominal declaration. */ void IterateAllVariableInStruct(InheritableDecl& inheritableDecl, const std::function action) { for (auto& decl : inheritableDecl.GetMemberDecls()) { if (auto vd = DynamicCast(decl.get())) { action(*vd); } } } /** * Auxiliary functions to iterate all functions in an package. */ void IterateAllMembersInStruct(InheritableDecl& inheritableDecl, const std::function action) { for (auto& decl : inheritableDecl.GetMemberDecls()) { if (decl->astKind == ASTKind::PROP_DECL) { auto propDecl = StaticAs(decl.get()); for (OwnedPtr& funcDecl : propDecl->getters) { action(*funcDecl); } for (OwnedPtr& funcDecl : propDecl->setters) { action(*funcDecl); } } else if (decl->astKind != ASTKind::PRIMARY_CTOR_DECL) { action(*decl); } } } void AnalyzeMemberDeclAsInternalLinkageInStruct(InheritableDecl& id) { if (!id.IsExportedDecl() && !id.TestAttr(Attribute::C)) { id.linkage = Linkage::INTERNAL; } IterateAllFunctionInStruct(id, [](auto& fd) { if (!IsSpecialFunction(fd) && !fd.IsExportedDecl()) { MarkFunctionAsInternalLinkage(fd); } }); IterateAllVariableInStruct(id, MarkVarDeclAbstractLinkage); } void AnalyzeFunctionAsInternalLinkageInExtend(ExtendDecl& ed, bool exportForTest) { bool isExportedExtend = exportForTest || ed.IsExportedDecl(); ed.linkage = isExportedExtend ? Linkage::EXTERNAL : Linkage::INTERNAL; IterateAllFunctionInStruct(ed, [isExportedExtend](auto& fd) { // Ignore whether extend is public, to HACK function's linkage for decls used in src exported internal decls. bool publicInstance = fd.TestAttr(Attribute::PUBLIC) && !fd.TestAttr(Attribute::STATIC); if (!IsSpecialFunction(fd) && (!fd.IsExportedDecl() || (!isExportedExtend && !publicInstance))) { MarkFunctionAsInternalLinkage(fd); } }); } /** * Iterate all functions, * for each *internal* function, set it with the internal linkage. */ void AnalyzeLinkageBasedOnModifier(Package& pkg, const GlobalOptions& opt) { for (auto& file : pkg.files) { if (IsIntrinsicFile(*file)) { continue; } for (auto& decl : file->decls) { if (auto vda = DynamicCast(decl.get())) { MarkVarDeclAbstractLinkage(*vda); } else if (auto fd = DynamicCast(decl.get()); fd && !IsSpecialFunction(*fd) && !fd->IsExportedDecl()) { MarkFunctionAsInternalLinkage(*fd); } else if (auto ed = DynamicCast(decl.get()); ed) { AnalyzeFunctionAsInternalLinkageInExtend(*ed, opt.exportForTest); } else if (auto id = DynamicCast(decl.get()); id) { AnalyzeMemberDeclAsInternalLinkageInStruct(*id); } else if (auto tad = DynamicCast(decl.get()); tad) { MarkTypeAliasDeclLinkage(*tad); } } } } class ExternalLinkageAnalyzer { public: explicit ExternalLinkageAnalyzer(const Package& pkg) : pkg(pkg) { } void Run() { IterateToplevelDecls(pkg, [this](auto& decl) { PerformPublicType(decl); }); while (!srcExportedDecls.empty() || !exportedTys.empty()) { AnalyzeExternalLinkageBySrcExportedDecl(); AnalyzeExternalLinkageByExportedTy(); } } private: void PerformPublicType(const OwnedPtr& decl); void HandleMemberDeclInTopLevelDecl(Decl& decl); void AnalyzeExternalLinkageBySrcExportedDecl(); void AnalyzeExternalLinkageByExportedTy(); void HandleMemberDeclsByTy(const InheritableDecl& id); // Target of a reference node will not be propDecl, // since all propDecl accesses will be rearraged to getter/setter function which belongs to the propDecl. void SetTargetLinkage(Ptr target) { if (auto fd = DynamicCast(target)) { SetFuncTargetLinkage(*fd); } else if (auto vd = DynamicCast(target)) { SetVarTargetLinkage(*vd); } else if (auto tad = DynamicCast(target)) { tad->linkage = Linkage::EXTERNAL; } else { AddExportedTy(target->ty); } } void SetFuncTargetLinkage(FuncDecl& fd, bool byTy = false); void SetPropTargetLinkage(PropDecl& pd, bool byTy = false); void SetVarTargetLinkage(VarDecl& vd, bool byTy = false); void AddSrcExportedDecl(Ptr decl) { if (visitedSrcExportedDecls.count(decl) == 0) { srcExportedDecls.emplace(decl); } } void AddExportedTy(Ptr ty) { if (visitedExportedTys.count(ty) == 0) { exportedTys.emplace(ty); } } const Package& pkg; std::unordered_set> srcExportedDecls; // Include FuncDecl and VarDecl. std::unordered_set> exportedTys; std::unordered_set> visitedSrcExportedDecls; std::unordered_set> visitedExportedTys; }; /** * Because instance member variables determine the memory layout of a type, all of its instance member variables * should be stored in cjo as long as the type is externally visible. * So we should treat the type of the member variable as externally visible and store the latter in the cjo. */ inline bool IsMemberInMemLayout(const Decl& member) { CJC_ASSERT(member.outerDecl); const auto& id = *member.outerDecl; return (id.IsStructOrClassDecl() && member.astKind == ASTKind::VAR_DECL && !member.TestAttr(Attribute::STATIC)) || member.TestAttr(Attribute::ENUM_CONSTRUCTOR); } void ExternalLinkageAnalyzer::PerformPublicType(const OwnedPtr& decl) { if (!decl->IsExportedDecl() || (decl->astKind == ASTKind::FUNC_DECL && decl->TestAttr(Attribute::FOREIGN))) { return; } decl->linkage = Linkage::EXTERNAL; if (auto vd = DynamicCast(decl.get()); vd && vd->isConst) { AddSrcExportedDecl(vd); return; } if (auto fd = DynamicCast(decl.get()); fd && CanBeSrcExported(*fd)) { AddSrcExportedDecl(fd); return; } auto id = DynamicCast(decl.get()); if (!id) { return; } for (auto& super : id->GetAllSuperDecls()) { AddExportedTy(super->ty); } IterateAllMembersInStruct(*id, [this](Decl& decl) { HandleMemberDeclInTopLevelDecl(decl); }); } void ExternalLinkageAnalyzer::HandleMemberDeclInTopLevelDecl(Decl& decl) { if (decl.astKind == ASTKind::PRIMARY_CTOR_DECL) { return; } if (auto fd = DynamicCast(&decl)) { // The finalizer of a type is special, which determines whether CHIR appends an additional bool member // `hasInit` to the type to indicate whether the type has been initialized, // so as long as the type is externally visible, the finalizer must be exported. if (fd->TestAttr(Attribute::FINALIZER)) { MarkFunctionAsExternalLinkage(*fd); return; } if (fd->IsExportedDecl()) { AddExportedTy(fd->ty); if (CanBeSrcExported(*fd)) { AddSrcExportedDecl(fd); } } return; } auto vd = DynamicCast(&decl); if (!vd) { return; } if (IsMemberInMemLayout(*vd)) { AddExportedTy(vd->ty); } if (IsInstMemberVarInGenericDecl(*vd)) { AddSrcExportedDecl(vd); } if (!vd->IsExportedDecl()) { return; } AddExportedTy(vd->ty); if (auto pd = DynamicCast(vd); pd && (pd->isConst || pd->HasAnno(AnnotationKind::FROZEN))) { for (auto& getter : std::as_const(pd->getters)) { AddSrcExportedDecl(getter.get()); } for (auto& setter : std::as_const(pd->setters)) { AddSrcExportedDecl(setter.get()); } } } void ExternalLinkageAnalyzer::AnalyzeExternalLinkageBySrcExportedDecl() { while (!srcExportedDecls.empty()) { auto decl = *srcExportedDecls.begin(); visitedSrcExportedDecls.emplace(decl); srcExportedDecls.erase(decl); auto id = Walker::GetNextWalkerID(); std::function)> visitor = [this](Ptr n) { if (auto fd = DynamicCast(n)) { bool shouldMarkExternal = !IsInternalSrcExportedFunction(*fd); if (shouldMarkExternal) { MarkFunctionAsExternalLinkage(*fd); } if (CanBeSrcExported(*fd)) { AddSrcExportedDecl(fd); } return VisitAction::WALK_CHILDREN; } auto target = TypeCheckUtil::GetRealTarget(n->GetTarget()); if (target == nullptr) { return VisitAction::WALK_CHILDREN; } // If target is TypeAliasDecl, it should be exported too. SetTargetLinkage(n->GetTarget()); SetTargetLinkage(target); return VisitAction::WALK_CHILDREN; }; Walker walker(decl, id, visitor); walker.Walk(); } } void ExternalLinkageAnalyzer::AnalyzeExternalLinkageByExportedTy() { while (!exportedTys.empty()) { auto ty = *exportedTys.begin(); visitedExportedTys.emplace(ty); exportedTys.erase(ty); if (!Ty::IsTyCorrect(ty)) { continue; } if (ty->IsFunc()) { auto funcTy = StaticCast(ty); for (auto paramTy : std::as_const(funcTy->paramTys)) { AddExportedTy(paramTy); } AddExportedTy(funcTy->retTy); } else if (ty->IsGeneric()) { auto genTy = StaticCast(ty); for (auto up : std::as_const(genTy->upperBounds)) { AddExportedTy(up); } } else { for (auto tyArg : std::as_const(ty->typeArgs)) { AddExportedTy(tyArg); } } auto decl = Ty::GetDeclPtrOfTy(ty); if (!decl) { continue; } decl->linkage = Linkage::EXTERNAL; if (auto id = DynamicCast(decl)) { HandleMemberDeclsByTy(*id); } if (auto ed = DynamicCast(decl)) { for (auto& ctor : std::as_const(ed->constructors)) { if (auto fd = DynamicCast(ctor.get())) { SetFuncTargetLinkage(*fd); } else if (auto vd = DynamicCast(ctor.get())) { SetVarTargetLinkage(*vd, true); } } } } } void ExternalLinkageAnalyzer::HandleMemberDeclsByTy(const InheritableDecl& id) { for (auto& member : id.GetMemberDecls()) { const bool isFuncOrProp = member->astKind == ASTKind::FUNC_DECL || member->astKind == ASTKind::PROP_DECL; const bool isInstMemberInVTable = isFuncOrProp && member->TestAnyAttr(Attribute::PUBLIC, Attribute::PROTECTED); const bool isStaticMemberInVTable = isFuncOrProp && !member->TestAttr(Attribute::PRIVATE) && member->TestAttr(Attribute::STATIC); const bool isMemberInMemLayout = IsMemberInMemLayout(*member); if (!isInstMemberInVTable && !isStaticMemberInVTable && !isMemberInMemLayout && !member->IsExportedDecl()) { continue; } if (auto fd = DynamicCast(member.get())) { // The finalizer of a type is special, which determines whether CHIR appends an additional bool member // `hasInit` to the type to indicate whether the type has been initialized, // so as long as the type is externally visible, the finalizer must be exported. if (fd->TestAttr(Attribute::FINALIZER)) { MarkFunctionAsExternalLinkage(*fd); } else { SetFuncTargetLinkage(*fd, true); } } else if (auto pd = DynamicCast(member.get())) { SetPropTargetLinkage(*pd, true); } else if (auto vd = DynamicCast(member.get())) { SetVarTargetLinkage(*vd, true); } } } void ExternalLinkageAnalyzer::SetFuncTargetLinkage(FuncDecl& fd, [[maybe_unused]] bool byTy) { if (!IsGlobalOrMember(fd)) { return; } MarkFunctionLinkage(fd, Linkage::EXTERNAL); if (CanBeSrcExported(fd)) { AddSrcExportedDecl(&fd); } AddExportedTy(fd.ty); if (fd.outerDecl) { fd.outerDecl->linkage = Linkage::EXTERNAL; if (auto id = DynamicCast(fd.outerDecl)) { for (auto& type : std::as_const(id->inheritedTypes)) { AddExportedTy(type->ty); } } } if (auto pd = fd.propDecl) { SetPropTargetLinkage(*pd); } } void ExternalLinkageAnalyzer::SetPropTargetLinkage(PropDecl& pd, bool byTy) { SetVarTargetLinkage(pd, byTy); bool canBeSrcExported = pd.isConst || pd.HasAnno(AnnotationKind::FROZEN) || IsInDeclWithAttribute(pd, Attribute::GENERIC); for (auto& getter : std::as_const(pd.getters)) { MarkFunctionLinkage(*getter, Linkage::EXTERNAL); if (canBeSrcExported && visitedSrcExportedDecls.count(getter.get()) == 0) { AddSrcExportedDecl(getter.get()); } } for (auto& setter : std::as_const(pd.setters)) { MarkFunctionLinkage(*setter, Linkage::EXTERNAL); if (canBeSrcExported && visitedSrcExportedDecls.count(setter.get()) == 0) { AddSrcExportedDecl(setter.get()); } } } void ExternalLinkageAnalyzer::SetVarTargetLinkage(VarDecl& vd, bool byTy) { if (!IsGlobalOrMember(vd) || (byTy && vd.astKind == ASTKind::VAR_DECL && vd.TestAttr(Attribute::PRIVATE, Attribute::STATIC))) { return; } vd.linkage = Linkage::EXTERNAL; if (vd.isConst || vd.HasAnno(AnnotationKind::FROZEN) || IsInstMemberVarInGenericDecl(vd) || IsMemberVarShouldBeSrcExported(vd)) { AddSrcExportedDecl(&vd); } AddExportedTy(vd.ty); if (vd.outerDecl) { vd.outerDecl->linkage = Linkage::EXTERNAL; if (auto id = DynamicCast(vd.outerDecl)) { for (auto& type : std::as_const(id->inheritedTypes)) { AddExportedTy(type->ty); } } } } } // namespace /** * The term `internal/external` is used at two levels, we should tell the different first to avoid abusing them: * - visibility at the Cangjie level * - linkage at the binary level. * The visibility controls whether a package can import an item from another package; * while the linkage decides whether symbols in an object file (compiled from a package) can be linked by other files. * An item (e.g. function) with external visibility is always with external linkage; * however, items with internal visibility may be also with external linkage. * In summary, * A function can be set as internal (linkage) if it meets one of the following conditions: * 1. a top-level function and not marked as `external` (visibility) * 2. a method (no matter it's public or private) of an internal (visibility) class or struct * 3. a private method of a class/struct (no matter the class/struct is external or not) * Note that there exist some corner cases where functions should be *external* (linkage): * 1. a function is marked as intrinsic, it's never internal (linkage) * because the compiler may call it directly. * 2. a function used by an external (linkage) generic function. * Though the function's visibility is internal, its linkage should be external. * 3. Also, some special functions should be external (linkage), e.g., rt$CreateOverflowException_msg. * ===== * The analysis process: * 1. mark all functions (satisfying the above conditions) as internal linkage (including generic and normal functions) * 2. for each external (linkage) generic functions marked by the first step, * mark all functions used in the function body as external (linkage). * ===== * Example: * func foo() {} * external func bar() { foo() } * `foo` should be internal visibility but external linkage. * This example also shows the difference between visibility and linkage. * The former means whether a function can be imported and used (from the CangjieLang level); * while the latter means whether a function can be linked (from the binary level). */ void TypeChecker::TypeCheckerImpl::AnalyzeFunctionLinkage(Package& pkg) const { AnalyzeLinkageBasedOnModifier(pkg, ci->invocation.globalOptions); ExternalLinkageAnalyzer(pkg).Run(); IterateToplevelDecls(pkg, [](auto& decl) { if (auto id = DynamicCast(decl.get()); id && id->linkage == Linkage::INTERNAL) { IterateAllFunctionInStruct(*id, MarkFunctionAsInternalLinkage); IterateAllMembersInStruct(*id, [](Decl& decl) { if (auto vd = DynamicCast(&decl)) { vd->linkage = Linkage::INTERNAL; } }); } }); // If has same name private decls are non-internal in one package diag error. // NOTE: It should be remove when bug of PrivateDecl.ti is fixed. // The identifier for PrivateDecl.ti does not use the file name for differentiation. // The above modifications are incompatible, and the action is postponed. std::unordered_map> privateDeclMap; IterateToplevelDecls(pkg, [this, &privateDeclMap](OwnedPtr& decl) { if (decl->IsNominalDecl() && decl->TestAttr(Attribute::PRIVATE) && decl->linkage != Linkage::INTERNAL) { auto ret = privateDeclMap.emplace(decl->identifier.Val(), decl.get()); if (!ret.second) { auto builder = diag.DiagnoseRefactor( DiagKindRefactor::sema_export_same_private_decl, MakeRange(ret.first->second->identifier)); builder.AddNote(MakeRange(decl->identifier), "same with private declaration"); } } }); } cangjie_compiler-1.0.7/src/Sema/CheckTypeCompatible.cpp000066400000000000000000000025031510705540100230460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements checking of substitutability of two types. */ #include "TypeCheckUtil.h" namespace Cangjie::TypeCheckUtil { using namespace AST; ComparisonRes CompareIntAndFloat(const Ty& left, const Ty& right) { if (left.kind == right.kind) { return ComparisonRes::EQ; } if (left.IsInteger()) { if (right.IsInteger()) { if (left.kind == TypeKind::TYPE_INT64) { return ComparisonRes::LT; } if (right.kind == TypeKind::TYPE_INT64) { return ComparisonRes::GT; } } else { // right is float. return ComparisonRes::LT; } } else { // left is float. if (right.IsInteger()) { return ComparisonRes::GT; } if (left.kind == TypeKind::TYPE_FLOAT64) { return ComparisonRes::LT; } if (right.kind == TypeKind::TYPE_FLOAT64) { return ComparisonRes::GT; } } return ComparisonRes::EQ; } } // namespace Cangjie::TypeCheckUtil cangjie_compiler-1.0.7/src/Sema/CheckUnusedImportImpl.cpp000066400000000000000000000215221510705540100234070ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file defines functions for check unused import. */ #include "TypeCheckerImpl.h" using namespace Cangjie; using namespace AST; namespace { class CheckUnusedImportImpl { public: CheckUnusedImportImpl(Package& pkg, DiagnosticEngine& diag, ImportManager& importManager) : pkg(pkg), diag(diag), importManager(importManager) { } ~CheckUnusedImportImpl() = default; void Check(); private: void CollectUsedPackages(Node& node); void CollectNeedCheckImports(); bool IsImportContentUsed(AST::ImportSpec& importSpec); bool IsImportContentUsedInMacro(AST::ImportSpec& importSpec); void AddUsedExtendDeclTarget( const Ptr ed, std::map>>& usedMap) const; void AddUsedTarget(Node& node, Ptr target); void AddUsedPackage(Node& node); void ReportUnusedImports(); std::map>> usedPackageInAST; std::map, std::map>>> usedPackageInFile; std::map>> cacheUsedPackageInAST; std::map, std::map>>> cacheUsedPackageInFile; std::vector> needCheckImport; Package& pkg; DiagnosticEngine& diag; ImportManager& importManager; }; } // namespace void CheckUnusedImportImpl::AddUsedExtendDeclTarget( const Ptr ed, std::map>>& usedMap) const { Ptr target = nullptr; for (auto& type : ed->inheritedTypes) { if (target = type->GetTarget(); target) { usedMap[target->GetFullPackageName()].emplace(target); } } if (ed->extendedType && ed->extendedType->GetTarget()) { target = ed->extendedType->GetTarget(); usedMap[target->GetFullPackageName()].emplace(target); } if (!ed->generic) { return; } for (auto& gc : ed->generic->genericConstraints) { for (auto& ub : gc->upperBounds) { if (target = ub->GetTarget(); target) { usedMap[target->GetFullPackageName()].emplace(target); } } } } void CheckUnusedImportImpl::AddUsedTarget(Node& node, Ptr target) { auto& foundInAST = usedPackageInAST[target->fullPackageName]; foundInAST.emplace(target); if (target->outerDecl != nullptr) { foundInAST.emplace(target->outerDecl); if (auto ed = DynamicCast(target->outerDecl); ed != nullptr) { AddUsedExtendDeclTarget(ed, usedPackageInAST); } } if (!node.curFile) { return; } auto& fileUsed = usedPackageInFile[node.curFile]; auto& foundInFile = fileUsed[target->fullPackageName]; foundInFile.emplace(target); if (target->outerDecl != nullptr) { foundInFile.emplace(target->outerDecl); if (auto ed = DynamicCast(target->outerDecl); ed != nullptr) { AddUsedExtendDeclTarget(ed, fileUsed); } } } void CheckUnusedImportImpl::AddUsedPackage(Node& node) { auto target = node.GetTarget(); if (target == nullptr) { return; } AddUsedTarget(node, target); auto targets = node.GetTargets(); for (auto decl : targets) { AddUsedTarget(node, decl); } } void CheckUnusedImportImpl::CollectNeedCheckImports() { for (auto& file : pkg.files) { for (auto& import : file->imports) { // IMPLICIT_ADD(std.core) or cjmp scenes does not need to be checked. if (import->TestAttr(AST::Attribute::IMPLICIT_ADD) || import->begin.IsZero() || import->end.IsZero()) { continue; } // multi-import or reExport scenes does not need to be checked. auto modifier = import->modifier ? import->modifier->modifier : TokenKind::PRIVATE; if (modifier == TokenKind::PUBLIC || modifier == TokenKind::PROTECTED || import->IsImportMulti()) { continue; } bool isInternalNeedExport = import->curFile && import->curFile->curPackage && !import->curFile->curPackage->noSubPkg; if (modifier == TokenKind::INTERNAL && isInternalNeedExport) { continue; } needCheckImport.emplace_back(import.get()); } } } void CheckUnusedImportImpl::CollectUsedPackages(Node& node) { Walker walker(&node, nullptr, [this](Ptr node) -> VisitAction { if (node->astKind == ASTKind::IMPORT_SPEC) { return VisitAction::SKIP_CHILDREN; } AddUsedPackage(*node); return VisitAction::WALK_CHILDREN; }); walker.Walk(); } bool CheckUnusedImportImpl::IsImportContentUsedInMacro(AST::ImportSpec& importSpec) { CJC_ASSERT(importSpec.curFile); auto cjoManager = importManager.GetCjoManager(); std::string packageName = cjoManager->GetPackageNameByImport(importSpec); auto usedMacroInFile = importManager.GetUsedMacroDecls(*importSpec.curFile); auto declsMap = cjoManager->GetPackageMembers(packageName); if (importSpec.IsImportAll() || !importSpec.content.isDecl) { if (!usedMacroInFile[packageName].empty()) { return true; } for (auto [_, decls] : declsMap) { for (auto decl : decls) { if (usedMacroInFile[decl->fullPackageName].count(decl) > 0) { cacheUsedPackageInFile[importSpec.curFile][packageName].emplace(decl); return true; } } } } else { auto decls = declsMap[importSpec.content.identifier]; for (auto decl : decls) { if (usedMacroInFile[decl->fullPackageName].count(decl) > 0) { cacheUsedPackageInFile[importSpec.curFile][packageName].emplace(decl); return true; } } } return false; } bool CheckUnusedImportImpl::IsImportContentUsed(ImportSpec& importSpec) { auto cjoManager = importManager.GetCjoManager(); std::string packageName = cjoManager->GetPackageNameByImport(importSpec); std::map>>& usedPackage = (importSpec.IsPrivateImport() && importSpec.curFile) ? usedPackageInFile[importSpec.curFile] : usedPackageInAST; std::set> usedDecls = usedPackage[packageName]; if (!usedDecls.empty() && (importSpec.IsImportAll() || !importSpec.content.isDecl)) { return true; } std::map>>& cacheUsedPackage = (importSpec.IsPrivateImport() && importSpec.curFile) ? cacheUsedPackageInFile[importSpec.curFile] : cacheUsedPackageInAST; auto declsMap = cjoManager->GetPackageMembers(packageName); if (importSpec.IsImportAll() || !importSpec.content.isDecl) { if (!cacheUsedPackage[packageName].empty()) { return true; } for (auto [_, decls] : declsMap) { for (auto decl : decls) { usedDecls = usedPackage[decl->fullPackageName]; if (usedDecls.find(decl) != usedDecls.end()) { cacheUsedPackageInAST[packageName].emplace(decl); cacheUsedPackageInFile[importSpec.curFile][packageName].emplace(decl); return true; } } } } else { auto decls = declsMap[importSpec.content.identifier]; for (auto decl : decls) { if (cacheUsedPackage[packageName].find(decl) != cacheUsedPackage[packageName].end()) { return true; } usedDecls = usedPackage[decl->fullPackageName]; if (usedDecls.find(decl) != usedDecls.end()) { cacheUsedPackageInAST[packageName].emplace(decl); cacheUsedPackageInFile[importSpec.curFile][packageName].emplace(decl); return true; } } } auto package = cjoManager->GetPackage(packageName); if (package && package->isMacroPackage) { return IsImportContentUsedInMacro(importSpec); } return false; } void CheckUnusedImportImpl::ReportUnusedImports() { for (auto& importSpec : needCheckImport) { if (IsImportContentUsed(*importSpec)) { continue; } diag.DiagnoseRefactor(DiagKindRefactor::sema_unused_import, MakeRange(importSpec->begin, importSpec->end), importSpec->content.ToString()); } } void CheckUnusedImportImpl::Check() { CollectUsedPackages(pkg); CollectNeedCheckImports(); ReportUnusedImports(); } void TypeChecker::TypeCheckerImpl::CheckUnusedImportSpec(Package& pkg) { CheckUnusedImportImpl(pkg, diag, importManager).Check(); }cangjie_compiler-1.0.7/src/Sema/Collector.cpp000066400000000000000000001523421510705540100211240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Collector. */ #include "Collector.h" #include #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Utils/FileUtil.h" #include "TypeCheckUtil.h" using namespace Cangjie; using namespace AST; namespace { void WalkMacroCall(Node& macrocall, const OwnedPtr& decl) { // No error will be reported during semantic analysis of code in macrocall. Just for lsp. auto visitPre = [¯ocall](Ptr curNode) -> VisitAction { curNode->isInMacroCall = true; curNode->curMacroCall = ¯ocall; return VisitAction::WALK_CHILDREN; }; Walker macroCallWalker(decl.get(), visitPre); macroCallWalker.Walk(); } } // namespace void Collector::AddSymbol(ASTContext& ctx, const NodeInfo& nodeInfo, bool buildTrie) { auto& node = nodeInfo.node; uint64_t fileHash = 0; if (node.curFile) { fileHash = node.curFile->fileHash; } // Only 'Collector' is 'Symbol''s friend so using 'new' before 'std::unique_ptr'. auto sym = new Symbol(fileHash, nodeInfo.name, node, nodeInfo.scopeLevel, nodeInfo.scopeName); node.scopeName = nodeInfo.scopeName; node.scopeLevel = nodeInfo.scopeLevel; node.symbol = sym; ctx.symbolTable.push_back(std::unique_ptr(sym)); ctx.invertedIndex.Index(ctx.symbolTable.back().get(), buildTrie); } void Collector::CollectPackageNode(ASTContext& ctx, Package& package, bool buildTrie) { scopeManager.Reset(); auto nodeInfo = NodeInfo(package, package.fullPackageName, ctx.currentScopeLevel, TOPLEVEL_SCOPE_NAME); AddSymbol(ctx, nodeInfo, buildTrie); for (auto& file : package.files) { BuildSymbolTable(ctx, file.get(), buildTrie); } } void Collector::CollectFileNode(ASTContext& ctx, File& file, bool buildTrie) { AddCurFile(file); SymbolApi::ResetID(); auto nodeInfo = NodeInfo(file, FileUtil::GetShortHash(file.fileHash), ctx.currentScopeLevel, TOPLEVEL_SCOPE_NAME); AddSymbol(ctx, nodeInfo, buildTrie); for (auto& import : file.imports) { BuildSymbolTable(ctx, import.get(), buildTrie); } // Must use three-stage for loop instead of iterator-style for loop because that function "__maininvoke" will be // generated during function declaration symbol collection. for (size_t index = 0; index < file.decls.size(); index++) { auto decl = file.decls[index].get(); CJC_NULLPTR_CHECK(decl); BuildSymbolTable(ctx, decl, buildTrie); } if (enableMacroInLsp) { // Collect all node symbol in macroCall, for lsp. scopeManager.InitializeScope(ctx); for (auto& macroCall : file.originalMacroCallNodes) { BuildSymbolTable(ctx, macroCall.get(), buildTrie); AddCurFile(*macroCall, &file); } scopeManager.FinalizeScope(ctx); } } void Collector::CollectAnnotations( ASTContext& ctx, const std::vector>& annotations, bool buildTrie) { for (auto& it : annotations) { BuildSymbolTable(ctx, it.get(), buildTrie); } } void Collector::CollectGeneric(ASTContext& ctx, Decl& outerDecl, const Generic& generic, bool buildTrie) { for (auto& it : generic.typeParameters) { it->outerDecl = &outerDecl; BuildSymbolTable(ctx, it.get(), buildTrie); } for (auto& it : generic.genericConstraints) { BuildSymbolTable(ctx, it.get(), buildTrie); } } void Collector::CollectClassDecl(ASTContext& ctx, ClassDecl& cd, bool buildTrie) { CJC_NULLPTR_CHECK(cd.body); if ((!cd.outerDecl || !cd.outerDecl->TestAttr(Attribute::TOOL_ADD)) && ctx.currentScopeLevel != 0) { ctx.diag.Diagnose(cd, DiagKind::sema_type_must_toplevel, "class"); } CollectAnnotations(ctx, cd.annotations, buildTrie); auto nodeInfo = NodeInfo(cd, cd.identifier, ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); scopeManager.InitializeScope(ctx); if (cd.generic) { CollectGeneric(ctx, cd, *cd.generic, buildTrie); } for (auto& type : cd.inheritedTypes) { BuildSymbolTable(ctx, type.get(), buildTrie); } auto bodyNodeInfo = NodeInfo(*cd.body, "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, bodyNodeInfo, buildTrie); BuildSymbolTable(ctx, cd.body.get(), buildTrie); scopeManager.FinalizeScope(ctx); } void Collector::CollectClassBody(ASTContext& ctx, const ClassBody& cb, bool buildTrie) { scopeManager.InitializeScope(ctx); for (auto& bodyDecl : cb.decls) { bodyDecl->EnableAttr(Attribute::IN_CLASSLIKE); BuildSymbolTable(ctx, bodyDecl.get(), buildTrie); } scopeManager.FinalizeScope(ctx); } void Collector::CollectInterfaceDecl(ASTContext& ctx, InterfaceDecl& id, bool buildTrie) { CJC_NULLPTR_CHECK(id.body); if ((!id.outerDecl || !id.outerDecl->TestAttr(Attribute::TOOL_ADD)) && ctx.currentScopeLevel != 0) { ctx.diag.Diagnose(id, DiagKind::sema_type_must_toplevel, "interface"); } CollectAnnotations(ctx, id.annotations, buildTrie); auto nodeInfo = NodeInfo(id, id.identifier, ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); scopeManager.InitializeScope(ctx); if (id.generic) { CollectGeneric(ctx, id, *id.generic, buildTrie); } for (auto& type : id.inheritedTypes) { BuildSymbolTable(ctx, type.get(), buildTrie); } auto bodyNodeInfo = NodeInfo(*id.body, "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, bodyNodeInfo, buildTrie); BuildSymbolTable(ctx, id.body.get(), buildTrie); scopeManager.FinalizeScope(ctx); } void Collector::CollectInterfaceBody(ASTContext& ctx, const InterfaceBody& ib, bool buildTrie) { scopeManager.InitializeScope(ctx); for (auto& decl : ib.decls) { BuildSymbolTable(ctx, decl.get(), buildTrie); } scopeManager.FinalizeScope(ctx); } void Collector::CollectEnumDecl(ASTContext& ctx, EnumDecl& ed, bool buildTrie) { if (ctx.currentScopeLevel != 0) { ctx.diag.Diagnose(ed, DiagKind::sema_type_must_toplevel, "enum"); } CollectAnnotations(ctx, ed.annotations, buildTrie); auto nodeInfo = NodeInfo(ed, ed.identifier, ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); scopeManager.InitializeScope(ctx); if (ed.generic) { CollectGeneric(ctx, ed, *ed.generic, buildTrie); } for (auto& type : ed.inheritedTypes) { BuildSymbolTable(ctx, type.get(), buildTrie); } CJC_ASSERT(ed.bodyScope); auto bodyNodeInfo = NodeInfo(*ed.bodyScope, "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, bodyNodeInfo, buildTrie); scopeManager.InitializeScope(ctx); // Open for enum body scope. for (auto& ctor : ed.constructors) { if (auto fd = DynamicCast(ctor.get()); fd) { fd->funcBody->parentEnum = &ed; } BuildSymbolTable(ctx, ctor.get(), buildTrie); } for (auto& it : ed.members) { BuildSymbolTable(ctx, it.get(), buildTrie); if (it->astKind != ASTKind::FUNC_DECL) { continue; } auto fd = StaticAs(it.get()); fd->funcBody->parentEnum = &ed; } scopeManager.FinalizeScope(ctx); // End enum body scope. scopeManager.FinalizeScope(ctx); } void Collector::CollectStructDecl(ASTContext& ctx, StructDecl& sd, bool buildTrie) { CJC_NULLPTR_CHECK(sd.body); if (ctx.currentScopeLevel != 0) { ctx.diag.Diagnose(sd, DiagKind::sema_type_must_toplevel, "struct"); } CollectAnnotations(ctx, sd.annotations, buildTrie); auto nodeInfo = NodeInfo(sd, sd.identifier, ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); scopeManager.InitializeScope(ctx); if (sd.generic) { CollectGeneric(ctx, sd, *sd.generic, buildTrie); } for (auto& type : sd.inheritedTypes) { BuildSymbolTable(ctx, type.get(), buildTrie); } if (sd.body == nullptr) { scopeManager.FinalizeScope(ctx); return; } auto bodyNodeInfo = NodeInfo(*sd.body, "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, bodyNodeInfo, buildTrie); BuildSymbolTable(ctx, sd.body.get(), buildTrie); scopeManager.FinalizeScope(ctx); } void Collector::CollectStructBody(ASTContext& ctx, const StructBody& rb, bool buildTrie) { scopeManager.InitializeScope(ctx); for (auto& decl : rb.decls) { BuildSymbolTable(ctx, decl.get(), buildTrie); } scopeManager.FinalizeScope(ctx); } void Collector::CollectExtendDecl(ASTContext& ctx, ExtendDecl& ed, bool buildTrie) { if (ctx.currentScopeLevel != 0) { ctx.diag.Diagnose(ed, DiagKind::sema_type_must_toplevel, "extend"); } CollectAnnotations(ctx, ed.annotations, buildTrie); auto nodeInfo = NodeInfo(ed, ed.identifier, ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); scopeManager.InitializeScope(ctx); if (ed.generic) { CollectGeneric(ctx, ed, *ed.generic, buildTrie); } if (ed.extendedType) { BuildSymbolTable(ctx, ed.extendedType.get(), buildTrie); } for (auto& i : ed.inheritedTypes) { BuildSymbolTable(ctx, i.get(), buildTrie); } CJC_ASSERT(ed.bodyScope); auto bodyNodeInfo = NodeInfo(*ed.bodyScope, "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, bodyNodeInfo, buildTrie); scopeManager.InitializeScope(ctx); // Open for extend body scope. for (auto& i : ed.members) { BuildSymbolTable(ctx, i.get(), buildTrie); } scopeManager.FinalizeScope(ctx); // End extend body scope. scopeManager.FinalizeScope(ctx); } void Collector::CollectMainDecl(ASTContext& ctx, MainDecl& md, bool buildTrie) { if (md.desugarDecl) { BuildSymbolTable(ctx, md.desugarDecl.get(), buildTrie); } else { CollectAnnotations(ctx, md.annotations, buildTrie); auto nodeInfo = NodeInfo(md, md.identifier, ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); if (md.funcBody) { scopeManager.InitializeScope(ctx); BuildSymbolTable(ctx, md.funcBody.get(), buildTrie); scopeManager.FinalizeScope(ctx); } } } void Collector::CollectFuncDecl(ASTContext& ctx, FuncDecl& fd, bool buildTrie) { CollectAnnotations(ctx, fd.annotations, buildTrie); auto nodeInfo = NodeInfo(fd, fd.identifier, ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); if (fd.funcBody) { scopeManager.InitializeScope(ctx); fd.funcBody->funcDecl = &fd; BuildSymbolTable(ctx, fd.funcBody.get(), buildTrie); scopeManager.FinalizeScope(ctx); } auto pkg = ctx.curPackage; static const std::unordered_set intrinsicPkgs{CORE_PACKAGE_NAME, SYNC_PACKAGE_NAME, MATH_PACKAGE_NAME, OVERFLOW_PACKAGE_NAME, RUNTIME_PACKAGE_NAME, NET_PACKAGE_NAME, REFLECT_PACKAGE_NAME, UNITTEST_MOCK_INTERNAL_PACKAGE_NAME, EFFECT_PACKAGE_NAME, INTEROP_PACKAGE_NAME}; static const std::unordered_set headlessIntrinsics{GET_TYPE_FOR_TYPE_PARAMETER_FUNC_NAME, IS_SUBTYPE_TYPES_FUNC_NAME}; if (Utils::In(fd.identifier.Val(), headlessIntrinsics)) { return; } if (fd.TestAttr(Attribute::INTRINSIC) && pkg != nullptr && !Utils::In(pkg->fullPackageName, intrinsicPkgs)) { ctx.diag.Diagnose(fd, DiagKind::sema_invalid_intrinsic_decl, fd.identifier.Val(), pkg->fullPackageName); } } void Collector::CollectFuncBody(ASTContext& ctx, FuncBody& fb, bool buildTrie) { auto bodyNodeInfo = NodeInfo(fb, "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, bodyNodeInfo, buildTrie); // Go inside func body and initialize a new scope. if (fb.generic && fb.funcDecl) { CollectGeneric(ctx, *fb.funcDecl, *fb.generic, buildTrie); } scopeManager.InitializeScope(ctx); for (auto& funcParamList : fb.paramLists) { BuildSymbolTable(ctx, funcParamList.get(), buildTrie); } BuildSymbolTable(ctx, fb.retType.get(), buildTrie); if (fb.body != nullptr) { for (auto& n : fb.body->body) { BuildSymbolTable(ctx, n.get(), buildTrie); } } scopeManager.FinalizeScope(ctx); } void Collector::CollectPropDecl(ASTContext& ctx, PropDecl& pd, bool buildTrie) { CollectAnnotations(ctx, pd.annotations, buildTrie); auto nodeInfo = NodeInfo(pd, pd.identifier, ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, pd.type.get(), buildTrie); for (auto& propMemberdecl : pd.setters) { propMemberdecl->identifierForLsp = propMemberdecl->identifier; if (propMemberdecl->identifier == "set") { propMemberdecl->identifier = "$" + pd.identifier + propMemberdecl->identifier; } propMemberdecl->propDecl = &pd; propMemberdecl->outerDecl = pd.outerDecl; BuildSymbolTable(ctx, propMemberdecl.get(), buildTrie); } for (auto& propMemberdecl : pd.getters) { propMemberdecl->identifierForLsp = propMemberdecl->identifier; if (propMemberdecl->identifier == "get") { propMemberdecl->identifier = "$" + pd.identifier + propMemberdecl->identifier; } propMemberdecl->propDecl = &pd; propMemberdecl->outerDecl = pd.outerDecl; BuildSymbolTable(ctx, propMemberdecl.get(), buildTrie); } } void Collector::CollectVarWithPatternDecl(ASTContext& ctx, VarWithPatternDecl& vpd, bool buildTrie) { auto nodeInfo = NodeInfo(vpd, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, vpd.irrefutablePattern.get(), buildTrie); BuildSymbolTable(ctx, vpd.type.get(), buildTrie); BuildSymbolTable(ctx, vpd.initializer.get(), buildTrie); } void Collector::CollectVarDecl(ASTContext& ctx, VarDecl& vd, bool buildTrie) { CollectAnnotations(ctx, vd.annotations, buildTrie); auto nodeInfo = NodeInfo(vd, vd.identifier, ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, vd.type.get(), buildTrie); BuildSymbolTable(ctx, vd.initializer.get(), buildTrie); } void Collector::CollectTypeAliasDecl(ASTContext& ctx, TypeAliasDecl& tad, bool buildTrie) { auto nodeInfo = NodeInfo(tad, tad.identifier, ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); scopeManager.InitializeScope(ctx); if (tad.generic) { CollectGeneric(ctx, tad, *tad.generic, buildTrie); } BuildSymbolTable(ctx, tad.type.get(), buildTrie); scopeManager.FinalizeScope(ctx); } void Collector::CollectMacroExpandDecl(ASTContext& ctx, MacroExpandDecl& med, bool buildTrie) { CollectAnnotations(ctx, med.annotations, buildTrie); auto nodeInfo = NodeInfo(med, med.invocation.fullName, ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); if (med.invocation.decl) { WalkMacroCall(med, med.invocation.decl); scopeManager.InitializeScope(ctx); BuildSymbolTable(ctx, med.invocation.decl.get(), buildTrie); scopeManager.FinalizeScope(ctx); } } void Collector::CollectMacroExpandExpr(ASTContext& ctx, MacroExpandExpr& mee, bool buildTrie) { auto nodeInfo = NodeInfo(mee, mee.invocation.fullName, ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); if (mee.invocation.decl) { WalkMacroCall(mee, mee.invocation.decl); scopeManager.InitializeScope(ctx); BuildSymbolTable(ctx, mee.invocation.decl.get(), buildTrie); scopeManager.FinalizeScope(ctx); } } void Collector::CollectMacroExpandParam(ASTContext& ctx, MacroExpandParam& mep, bool buildTrie) { auto nodeInfo = NodeInfo(mep, mep.invocation.fullName, ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); if (mep.invocation.decl) { WalkMacroCall(mep, mep.invocation.decl); scopeManager.InitializeScope(ctx); BuildSymbolTable(ctx, mep.invocation.decl.get(), buildTrie); scopeManager.FinalizeScope(ctx); } } void Collector::CollectIfExpr(ASTContext& ctx, IfExpr& ie, bool buildTrie) { auto nodeInfo = NodeInfo(ie, "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); // There are two kinds of if expressions. // 1. The scope of `if (let p <- i) { t }` is shown as follows: // { // i // { p, t } // end of p-t // } // end of if-then // 2. The scope of `if (c) { t }` is shown as follows: // { // c, t // } // end of if-then scopeManager.InitializeScope(ctx); // start of condition and then-body CollectCondition(ctx, *ie.condExpr, buildTrie); if (ie.thenBody) { // `ie.thenBody` has the same scope as condition. for (auto& n : ie.thenBody->body) { BuildSymbolTable(ctx, n.get(), buildTrie); } } scopeManager.FinalizeScope(ctx); // Scope of else-body. if (ie.hasElse && ie.elseBody) { BuildSymbolTable(ctx, ie.elseBody.get(), buildTrie); } } void Collector::CollectCondition(ASTContext& ctx, AST::Expr& e, bool buildTrie) { if (auto letPattern = DynamicCast(&e)) { BuildSymbolTable(ctx, letPattern->initializer.get(), buildTrie); NodeInfo patternNodeInfo{*letPattern, "", ctx.currentScopeLevel, ctx.currentScopeName}; AddSymbol(ctx, patternNodeInfo, buildTrie); for (auto& p : letPattern->patterns) { BuildSymbolTable(ctx, p.get(), buildTrie); } return; } if (auto paren = DynamicCast(&e)) { return CollectCondition(ctx, *paren->expr, buildTrie); } if (auto binop = DynamicCast(&e)) { if (binop->op == TokenKind::AND || binop->op == TokenKind::OR) { CollectCondition(ctx, *binop->leftExpr, buildTrie); return CollectCondition(ctx, *binop->rightExpr, buildTrie); } } // normal expr BuildSymbolTable(ctx, &e, buildTrie); return; } void Collector::CollectWhileExpr(ASTContext& ctx, WhileExpr& we, bool buildTrie) { auto nodeInfo = NodeInfo(we, "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); // There are two kinds of while expressions. // 1. The scope of `while (let p <- i) { t }` is shown as follows: // { // i // { p, t } // end of p-t // } // end of while // 2. The scope of `while (c) { t }` is shown as follows: // { // c, t // } // end of while scopeManager.InitializeScope(ctx); // start of while CollectCondition(ctx, *we.condExpr, buildTrie); if (we.body != nullptr) { for (auto& n : we.body->body) { BuildSymbolTable(ctx, n.get(), buildTrie); } } scopeManager.FinalizeScope(ctx); // end of while } void Collector::CollectQuoteExpr(ASTContext& ctx, QuoteExpr& qe, bool buildTrie) { auto nodeInfo = NodeInfo(qe, "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); for (const auto& it : qe.exprs) { if (it->astKind != ASTKind::TOKEN_PART) { BuildSymbolTable(ctx, it.get(), buildTrie); } } } void Collector::CollectLitConstExpr(ASTContext& ctx, LitConstExpr& lce, bool buildTrie) { std::unordered_map stringKindToRef = { {LitConstKind::STRING, "String"}, {LitConstKind::JSTRING, "JString"}, }; if (stringKindToRef.find(lce.kind) != stringKindToRef.end() && !lce.ref) { auto tmp = MakeOwned(); tmp->begin = lce.begin; tmp->ref.identifier = stringKindToRef[lce.kind]; tmp->EnableAttr(Attribute::COMPILER_ADD, Attribute::IN_CORE); lce.ref = std::move(tmp); BuildSymbolTable(ctx, lce.ref.get(), buildTrie); } if (lce.siExpr) { CollectStrInterpolationExpr(ctx, *lce.siExpr.get(), buildTrie); } auto nodeInfo = NodeInfo(lce, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); } void Collector::CollectStrInterpolationExpr(ASTContext& ctx, StrInterpolationExpr& sie, bool buildTrie) { for (auto& expr : sie.strPartExprs) { BuildSymbolTable(ctx, expr.get(), buildTrie); } auto nodeInfo = NodeInfo(sie, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); } void Collector::CollectInterpolationExpr(ASTContext& ctx, InterpolationExpr& ie, bool buildTrie) { BuildSymbolTable(ctx, ie.block.get(), buildTrie); auto nodeInfo = NodeInfo(ie, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); } void Collector::CollectTryExpr(ASTContext& ctx, TryExpr& te, bool buildTrie) { auto nodeInfo = NodeInfo(te, "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); scopeManager.InitializeScope(ctx); // start of try for (auto& re : te.resourceSpec) { BuildSymbolTable(ctx, re.get(), buildTrie); } if (te.tryBlock) { for (auto& n : te.tryBlock->body) { BuildSymbolTable(ctx, n.get(), buildTrie); } } scopeManager.FinalizeScope(ctx); // end of try if (!te.handlers.empty()) { BuildSymbolTable(ctx, te.tryLambda.get(), buildTrie); BuildSymbolTable(ctx, te.finallyLambda.get(), buildTrie); } for (size_t cnt = 0; cnt < te.catchPatterns.size(); cnt++) { CJC_ASSERT(cnt < te.catchBlocks.size()); auto catchBlockNodeInfo = NodeInfo(*te.catchBlocks[cnt], "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, catchBlockNodeInfo, buildTrie); scopeManager.InitializeScope(ctx); BuildSymbolTable(ctx, te.catchPatterns[cnt].get(), buildTrie); // catch block has the same scope as catch pattern. for (auto& n : te.catchBlocks[cnt]->body) { BuildSymbolTable(ctx, n.get(), buildTrie); } scopeManager.FinalizeScope(ctx); } for (const auto& handler : te.handlers) { auto handleBlockNodeInfo = NodeInfo(*handler.block, "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, handleBlockNodeInfo, buildTrie); scopeManager.InitializeScope(ctx); BuildSymbolTable(ctx, handler.commandPattern.get(), buildTrie); // handle block has the same scope as effect and resumption patterns. for (auto& n : handler.block->body) { BuildSymbolTable(ctx, n.get(), buildTrie); } scopeManager.FinalizeScope(ctx); if (handler.desugaredLambda) { BuildSymbolTable(ctx, handler.desugaredLambda, buildTrie); } } if (te.finallyBlock) { BuildSymbolTable(ctx, te.finallyBlock.get(), buildTrie); } } void Collector::CollectSpawnExpr(ASTContext& ctx, SpawnExpr& se, bool buildTrie) { auto nodeInfo = NodeInfo(se, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, se.task.get(), buildTrie); BuildSymbolTable(ctx, se.arg.get(), buildTrie); } void Collector::BuildSymbolTable(ASTContext& ctx, Ptr node, bool buildTrie) { if (!node) { return; } if (auto decl = DynamicCast(node); decl && decl->fullPackageName.empty()) { // Imported decl has fullPackageName. decl->fullPackageName = ctx.fullPackageName; } // For-In expr and Synchronized expr need to be collected in new scope, and will be processed later. if (auto expr = DynamicCast(node); expr && expr->astKind != ASTKind::FOR_IN_EXPR && expr->astKind != ASTKind::SYNCHRONIZED_EXPR) { BuildSymbolTable(ctx, expr->desugarExpr.get(), buildTrie); } switch (node->astKind) { // ----------- Packages ------------------- case ASTKind::PACKAGE: { auto package = StaticAs(node); CollectPackageNode(ctx, *package, buildTrie); break; } // ----------- Files ---------------------- case ASTKind::FILE: { auto file = StaticAs(node); CollectFileNode(ctx, *file, buildTrie); break; } case ASTKind::ANNOTATION: { auto anno = StaticAs(node); BuildSymbolTable(ctx, anno->baseExpr.get(), buildTrie); for (auto& funcArg : anno->args) { BuildSymbolTable(ctx, funcArg.get(), buildTrie); } break; } // ----------- Decls ---------------------- case ASTKind::GENERIC_PARAM_DECL: { auto gpd = StaticAs(node); auto nodeInfo = NodeInfo(*gpd, gpd->identifier, ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); break; } case ASTKind::PRIMARY_CTOR_DECL: { auto pcd = StaticAs(node); CollectAnnotations(ctx, pcd->annotations, buildTrie); auto nodeInfo = NodeInfo(*pcd, pcd->identifier, ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); scopeManager.InitializeScope(ctx); BuildSymbolTable(ctx, pcd->funcBody.get(), buildTrie); scopeManager.FinalizeScope(ctx); break; } case ASTKind::BUILTIN_DECL: { auto bid = StaticAs(node); auto nodeInfo = NodeInfo(*bid, bid->identifier, ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); if (bid->generic) { scopeManager.InitializeScope(ctx); CollectGeneric(ctx, *bid, *bid->generic, buildTrie); scopeManager.FinalizeScope(ctx); } break; } case ASTKind::CLASS_DECL: { auto cd = StaticAs(node); CollectClassDecl(ctx, *cd, buildTrie); break; } case ASTKind::CLASS_BODY: { auto cb = StaticAs(node); CollectClassBody(ctx, *cb, buildTrie); break; } case ASTKind::INTERFACE_DECL: { auto id = StaticAs(node); CollectInterfaceDecl(ctx, *id, buildTrie); break; } case ASTKind::INTERFACE_BODY: { auto ib = StaticAs(node); CollectInterfaceBody(ctx, *ib, buildTrie); break; } case ASTKind::ENUM_DECL: { auto ed = StaticAs(node); CollectEnumDecl(ctx, *ed, buildTrie); break; } case ASTKind::STRUCT_DECL: { auto sd = StaticAs(node); CollectStructDecl(ctx, *sd, buildTrie); break; } case ASTKind::STRUCT_BODY: { auto rb = StaticAs(node); CollectStructBody(ctx, *rb, buildTrie); break; } case ASTKind::EXTEND_DECL: { auto ed = StaticAs(node); CollectExtendDecl(ctx, *ed, buildTrie); break; } case ASTKind::MAIN_DECL: { auto md = StaticAs(node); CollectMainDecl(ctx, *md, buildTrie); break; } case ASTKind::FUNC_DECL: { auto fd = StaticAs(node); CollectFuncDecl(ctx, *fd, buildTrie); break; } case ASTKind::FUNC_BODY: { auto fb = StaticAs(node); CollectFuncBody(ctx, *fb, buildTrie); break; } case ASTKind::MACRO_DECL: { auto md = StaticAs(node); if (md->desugarDecl) { CollectFuncDecl(ctx, *md->desugarDecl, buildTrie); } break; } case ASTKind::FUNC_PARAM_LIST: { auto fpl = StaticAs(node); for (auto& param : fpl->params) { BuildSymbolTable(ctx, param.get(), buildTrie); } break; } case ASTKind::FUNC_PARAM: { auto fp = StaticAs(node); // Process identifier : type. if (!fp->identifier.Empty()) { auto nodeInfo = NodeInfo(*fp, fp->identifier, ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); } CollectAnnotations(ctx, fp->annotations, buildTrie); BuildSymbolTable(ctx, fp->type.get(), buildTrie); BuildSymbolTable(ctx, fp->assignment.get(), buildTrie); BuildSymbolTable(ctx, fp->desugarDecl.get(), buildTrie); break; } case ASTKind::MACRO_EXPAND_PARAM: { auto mep = StaticAs(node); CollectMacroExpandParam(ctx, *mep, buildTrie); break; } case ASTKind::GENERIC_CONSTRAINT: { auto gc = StaticAs(node); BuildSymbolTable(ctx, gc->type.get(), buildTrie); for (const auto& upperBound : gc->upperBounds) { BuildSymbolTable(ctx, upperBound.get(), buildTrie); } break; } case ASTKind::PROP_DECL: { auto pd = StaticAs(node); CollectPropDecl(ctx, *pd, buildTrie); break; } case ASTKind::VAR_WITH_PATTERN_DECL: { auto vpd = StaticAs(node); CollectVarWithPatternDecl(ctx, *vpd, buildTrie); break; } case ASTKind::VAR_DECL: { auto vd = StaticAs(node); CollectVarDecl(ctx, *vd, buildTrie); break; } case ASTKind::TYPE_ALIAS_DECL: { auto tad = StaticAs(node); CollectTypeAliasDecl(ctx, *tad, buildTrie); break; } case ASTKind::MACRO_EXPAND_DECL: { auto med = StaticAs(node); CollectMacroExpandDecl(ctx, *med, buildTrie); break; } // ----------- Expressions ---------------------- case ASTKind::MACRO_EXPAND_EXPR: { auto me = StaticAs(node); CollectMacroExpandExpr(ctx, *me, buildTrie); break; } case ASTKind::CALL_EXPR: { auto ce = StaticAs(node); std::string name; if (auto re = DynamicCast(ce->baseFunc.get()); re) { name = re->ref.identifier; } if (auto ma = DynamicCast(ce->baseFunc.get()); ma) { name = ma->field; } CJC_NULLPTR_CHECK(ce->baseFunc); TypeCheckUtil::SetIsNotAlone(*ce->baseFunc); auto nodeInfo = NodeInfo(*ce, name, ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, ce->baseFunc.get(), buildTrie); for (auto& funcArg : ce->args) { BuildSymbolTable(ctx, funcArg.get(), buildTrie); } break; } case ASTKind::MEMBER_ACCESS: { auto ma = StaticAs(node); CJC_NULLPTR_CHECK(ma->baseExpr); TypeCheckUtil::SetIsNotAlone(*ma->baseExpr); auto nodeInfo = NodeInfo(*ma, ma->field, ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, ma->baseExpr.get(), buildTrie); for (auto& typeArg : ma->typeArguments) { BuildSymbolTable(ctx, typeArg.get(), buildTrie); } break; } case ASTKind::SUBSCRIPT_EXPR: { auto se = StaticAs(node); CJC_NULLPTR_CHECK(se->baseExpr); TypeCheckUtil::SetIsNotAlone(*se->baseExpr); auto nodeInfo = NodeInfo(*se, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, se->baseExpr.get(), buildTrie); for (auto& expr : se->indexExprs) { BuildSymbolTable(ctx, expr.get(), buildTrie); } break; } case ASTKind::FUNC_ARG: { auto fa = StaticAs(node); if (!fa->name.Empty()) { auto nodeInfo = NodeInfo(*fa, fa->name, ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); } BuildSymbolTable(ctx, fa->expr.get(), buildTrie); break; } case ASTKind::REF_EXPR: { auto re = StaticAs(node); auto nodeInfo = NodeInfo(*re, re->ref.identifier, ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); for (auto& typeArg : re->typeArguments) { BuildSymbolTable(ctx, typeArg.get(), buildTrie); } break; } case ASTKind::LAMBDA_EXPR: { auto le = StaticAs(node); auto nodeInfo = NodeInfo(*le, "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); scopeManager.InitializeScope(ctx); BuildSymbolTable(ctx, le->funcBody.get(), buildTrie); scopeManager.FinalizeScope(ctx); break; } case ASTKind::ARRAY_LIT: { auto al = StaticAs(node); auto nodeInfo = NodeInfo(*al, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); for (auto& child : al->children) { BuildSymbolTable(ctx, child.get(), buildTrie); } break; } case ASTKind::ARRAY_EXPR: { auto asl = StaticAs(node); auto nodeInfo = NodeInfo(*asl, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, asl->type.get(), buildTrie); for (auto& it : asl->args) { BuildSymbolTable(ctx, it.get(), buildTrie); } break; } case ASTKind::POINTER_EXPR: { auto cpe = StaticAs(node); auto nodeInfo = NodeInfo(*cpe, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, cpe->type.get(), buildTrie); BuildSymbolTable(ctx, cpe->arg.get(), buildTrie); break; } case ASTKind::TUPLE_LIT: { auto tl = StaticAs(node); auto nodeInfo = NodeInfo(*tl, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); for (auto& child : tl->children) { BuildSymbolTable(ctx, child.get(), buildTrie); } break; } case ASTKind::FOR_IN_EXPR: { auto fie = StaticAs(node); BuildSymbolTable(ctx, fie->inExpression.get(), buildTrie); auto nodeInfo = NodeInfo(*fie, "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); scopeManager.InitializeScope(ctx); BuildSymbolTable(ctx, fie->pattern.get(), buildTrie); BuildSymbolTable(ctx, fie->patternGuard.get(), buildTrie); if (fie->body != nullptr) { for (auto& n : fie->body->body) { BuildSymbolTable(ctx, n.get(), buildTrie); } } scopeManager.FinalizeScope(ctx); break; } case ASTKind::BLOCK: { auto b = StaticAs(node); auto nodeInfo = NodeInfo(*b, "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); scopeManager.InitializeScope(ctx); for (auto& n : b->body) { BuildSymbolTable(ctx, n.get(), buildTrie); } scopeManager.FinalizeScope(ctx); break; } case ASTKind::IF_EXPR: { auto ie = StaticAs(node); CollectIfExpr(ctx, *ie, buildTrie); break; } case ASTKind::PAREN_EXPR: { auto pe = StaticAs(node); BuildSymbolTable(ctx, pe->expr.get(), buildTrie); break; } case ASTKind::QUOTE_EXPR: { auto qe = StaticAs(node); CollectQuoteExpr(ctx, *qe, buildTrie); break; } case ASTKind::WHILE_EXPR: { auto we = StaticAs(node); CollectWhileExpr(ctx, *we, buildTrie); break; } case ASTKind::DO_WHILE_EXPR: { auto dwe = StaticAs(node); auto nodeInfo = NodeInfo(*dwe, "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); scopeManager.InitializeScope(ctx); BuildSymbolTable(ctx, dwe->condExpr.get(), buildTrie); BuildSymbolTable(ctx, dwe->body.get(), buildTrie); scopeManager.FinalizeScope(ctx); break; } case ASTKind::ASSIGN_EXPR: { auto ae = StaticAs(node); if (ae->desugarExpr) { BuildSymbolTable(ctx, ae->leftValue.get(), buildTrie); BuildSymbolTable(ctx, ae->rightExpr.get(), buildTrie); BuildSymbolTable(ctx, ae->desugarExpr.get(), buildTrie); } else { auto nodeInfo = NodeInfo(*ae, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); if (ae->leftValue) { ae->leftValue->EnableAttr(Attribute::LEFT_VALUE); BuildSymbolTable(ctx, ae->leftValue.get(), buildTrie); } BuildSymbolTable(ctx, ae->rightExpr.get(), buildTrie); } break; } case ASTKind::INC_OR_DEC_EXPR: { auto ide = StaticAs(node); auto nodeInfo = NodeInfo(*ide, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, ide->expr.get(), buildTrie); break; } case ASTKind::BINARY_EXPR: { auto be = StaticAs(node); auto nodeInfo = NodeInfo(*be, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, be->leftExpr.get(), buildTrie); BuildSymbolTable(ctx, be->rightExpr.get(), buildTrie); break; } case ASTKind::UNARY_EXPR: { auto ue = StaticAs(node); auto nodeInfo = NodeInfo(*ue, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, ue->expr.get(), buildTrie); break; } case ASTKind::RANGE_EXPR: { auto re = StaticAs(node); BuildSymbolTable(ctx, re->startExpr.get(), buildTrie); BuildSymbolTable(ctx, re->stopExpr.get(), buildTrie); BuildSymbolTable(ctx, re->stepExpr.get(), buildTrie); break; } case ASTKind::LIT_CONST_EXPR: { auto lce = StaticAs(node); CollectLitConstExpr(ctx, *lce, buildTrie); break; } case ASTKind::INTERPOLATION_EXPR: { auto ie = StaticAs(node); CollectInterpolationExpr(ctx, *ie, buildTrie); break; } case ASTKind::RETURN_EXPR: { auto re = StaticAs(node); auto nodeInfo = NodeInfo(*re, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, re->expr.get(), buildTrie); break; } case ASTKind::MATCH_EXPR: { auto me = StaticAs(node); auto nodeInfo = NodeInfo(*me, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, me->selector.get(), buildTrie); for (auto& matchCase : me->matchCases) { BuildSymbolTable(ctx, matchCase.get(), buildTrie); } for (auto& matchCaseOther : me->matchCaseOthers) { BuildSymbolTable(ctx, matchCaseOther.get(), buildTrie); } break; } case ASTKind::MATCH_CASE: { auto mc = StaticAs(node); auto nodeInfo = NodeInfo(*mc, "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); // Go inside match case and initialize a new scope. scopeManager.InitializeScope(ctx); for (auto& pattern : mc->patterns) { BuildSymbolTable(ctx, pattern.get(), buildTrie); } BuildSymbolTable(ctx, mc->patternGuard.get(), buildTrie); if (mc->exprOrDecls != nullptr) { // `mc->exprOrDecls` has the same scope as pattern. for (auto& n : mc->exprOrDecls->body) { BuildSymbolTable(ctx, n.get(), buildTrie); } } scopeManager.FinalizeScope(ctx); break; } case ASTKind::MATCH_CASE_OTHER: { auto mco = StaticAs(node); BuildSymbolTable(ctx, mco->matchExpr.get(), buildTrie); BuildSymbolTable(ctx, mco->exprOrDecls.get(), buildTrie); break; } case ASTKind::JUMP_EXPR: { auto je = StaticAs(node); auto nodeInfo = NodeInfo(*je, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); break; } case ASTKind::TYPE_CONV_EXPR: { auto tce = StaticAs(node); auto nodeInfo = NodeInfo(*tce, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, tce->expr.get(), buildTrie); if (tce->type->astKind == ASTKind::FUNC_TYPE) { BuildSymbolTable(ctx, tce->type.get(), buildTrie); } break; } case ASTKind::IF_AVAILABLE_EXPR: { auto ie = StaticCast(node); NodeInfo info{*ie, "", ctx.currentScopeLevel, ctx.currentScopeName}; AddSymbol(ctx, info, buildTrie); BuildSymbolTable(ctx, ie->GetArg(), buildTrie); BuildSymbolTable(ctx, ie->GetLambda1(), buildTrie); BuildSymbolTable(ctx, ie->GetLambda2(), buildTrie); break; } case ASTKind::THROW_EXPR: { auto te = StaticAs(node); auto nodeInfo = NodeInfo(*te, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, te->expr.get(), buildTrie); break; } case ASTKind::PERFORM_EXPR: { auto pe = StaticAs(node); auto nodeInfo = NodeInfo(*pe, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, pe->expr.get(), buildTrie); break; } case ASTKind::RESUME_EXPR: { auto re = StaticAs(node); auto nodeInfo = NodeInfo(*re, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, re->withExpr.get(), buildTrie); BuildSymbolTable(ctx, re->throwingExpr.get(), buildTrie); break; } case ASTKind::TRY_EXPR: { auto te = StaticAs(node); CollectTryExpr(ctx, *te, buildTrie); break; } case ASTKind::SPAWN_EXPR: { auto se = StaticAs(node); CollectSpawnExpr(ctx, *se, buildTrie); break; } case ASTKind::SYNCHRONIZED_EXPR: { auto se = StaticAs(node); auto nodeInfo = NodeInfo(*se, "", ctx.currentScopeLevel, scopeManager.CalcScopeGateName(ctx)); AddSymbol(ctx, nodeInfo, buildTrie); scopeManager.InitializeScope(ctx); BuildSymbolTable(ctx, se->mutex.get(), buildTrie); BuildSymbolTable(ctx, se->desugarExpr.get(), buildTrie); scopeManager.FinalizeScope(ctx); break; } case ASTKind::IS_EXPR: { auto ie = StaticAs(node); auto nodeInfo = NodeInfo(*ie, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, ie->leftExpr.get(), buildTrie); BuildSymbolTable(ctx, ie->isType.get(), buildTrie); break; } case ASTKind::AS_EXPR: { auto ae = StaticAs(node); auto nodeInfo = NodeInfo(*ae, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, ae->leftExpr.get(), buildTrie); BuildSymbolTable(ctx, ae->asType.get(), buildTrie); break; } // ----------- Patterns ---------------------- case ASTKind::CONST_PATTERN: { auto cp = StaticAs(node); auto nodeInfo = NodeInfo(*cp, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, cp->literal.get(), buildTrie); break; } case ASTKind::VAR_PATTERN: { auto vp = StaticAs(node); auto nodeInfo = NodeInfo(*vp, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, vp->varDecl.get(), buildTrie); break; } case ASTKind::TUPLE_PATTERN: { auto tp = StaticAs(node); auto nodeInfo = NodeInfo(*tp, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); for (auto& pattern : tp->patterns) { BuildSymbolTable(ctx, pattern.get(), buildTrie); } break; } case ASTKind::ENUM_PATTERN: { auto ep = StaticAs(node); auto nodeInfo = NodeInfo(*ep, ep->GetIdentifier(), ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, ep->constructor.get(), buildTrie); for (auto& pattern : ep->patterns) { BuildSymbolTable(ctx, pattern.get(), buildTrie); } break; } case AST::ASTKind::VAR_OR_ENUM_PATTERN: { auto vep = StaticAs(node); auto nodeInfo = NodeInfo(*vep, vep->identifier, ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); break; } case ASTKind::TYPE_PATTERN: { auto tp = StaticAs(node); auto nodeInfo = NodeInfo(*tp, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, tp->pattern.get(), buildTrie); BuildSymbolTable(ctx, tp->type.get(), buildTrie); break; } case ASTKind::WILDCARD_PATTERN: { auto wp = StaticAs(node); auto nodeInfo = NodeInfo(*wp, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); break; } case ASTKind::EXCEPT_TYPE_PATTERN: { auto etp = StaticAs(node); BuildSymbolTable(ctx, etp->pattern.get(), buildTrie); for (auto& type : etp->types) { BuildSymbolTable(ctx, type.get(), buildTrie); } break; } case ASTKind::COMMAND_TYPE_PATTERN: { auto etp = StaticAs(node); BuildSymbolTable(ctx, etp->pattern.get(), buildTrie); for (auto& type : etp->types) { BuildSymbolTable(ctx, type.get(), buildTrie); } break; } // ----------- Types ---------------------- case ASTKind::REF_TYPE: { auto rt = StaticAs(node); auto nodeInfo = NodeInfo(*rt, rt->ref.identifier, ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); for (auto& type : rt->typeArguments) { BuildSymbolTable(ctx, type.get(), buildTrie); } break; } case ASTKind::CONSTANT_TYPE: { auto ct = StaticAs(node); auto nodeInfo = NodeInfo(*ct, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, ct->constantExpr.get(), buildTrie); break; } case ASTKind::VARRAY_TYPE: { auto vt = StaticAs(node); auto nodeInfo = NodeInfo(*vt, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, vt->typeArgument.get(), buildTrie); BuildSymbolTable(ctx, vt->constantType.get(), buildTrie); break; } case ASTKind::PRIMITIVE_TYPE: { auto pt = StaticAs(node); auto nodeInfo = NodeInfo(*pt, pt->str, ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); break; } case ASTKind::PAREN_TYPE: { auto pt = StaticAs(node); auto nodeInfo = NodeInfo(*pt, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, pt->type.get(), buildTrie); break; } case ASTKind::QUALIFIED_TYPE: { auto qt = StaticAs(node); auto nodeInfo = NodeInfo(*qt, qt->field, ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); BuildSymbolTable(ctx, qt->baseType.get(), buildTrie); for (auto& type : qt->typeArguments) { BuildSymbolTable(ctx, type.get(), buildTrie); } break; } case ASTKind::FUNC_TYPE: { auto ft = StaticAs(node); auto nodeInfo = NodeInfo(*ft, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); for (auto& paramType : ft->paramTypes) { BuildSymbolTable(ctx, paramType.get(), buildTrie); } BuildSymbolTable(ctx, ft->retType.get(), buildTrie); break; } case ASTKind::TUPLE_TYPE: { auto tp = StaticAs(node); auto nodeInfo = NodeInfo(*tp, "", ctx.currentScopeLevel, ctx.currentScopeName); AddSymbol(ctx, nodeInfo, buildTrie); for (auto& type : tp->fieldTypes) { BuildSymbolTable(ctx, type.get(), buildTrie); } break; } case ASTKind::OPTION_TYPE: { auto ot = StaticAs(node); BuildSymbolTable(ctx, ot->componentType.get(), buildTrie); BuildSymbolTable(ctx, ot->desugarType.get(), buildTrie); break; } default: break; } } cangjie_compiler-1.0.7/src/Sema/Collector.h000066400000000000000000000105541510705540100205670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Collector, which collects almost all AST Node to build SymbolTable and reverse indexes. */ #ifndef CANGJIE_SEMA_COLLECTOR_H #define CANGJIE_SEMA_COLLECTOR_H #include #include "ScopeManager.h" #include "cangjie/AST/ASTContext.h" namespace Cangjie { struct NodeInfo { AST::Node& node; std::string name; uint32_t scopeLevel; std::string scopeName; NodeInfo(AST::Node& node, std::string name, uint32_t scopeLevel, std::string scopeName) : node(node), name(std::move(name)), scopeLevel(scopeLevel), scopeName(std::move(scopeName)) { } }; class Collector { public: /** * Build the SymbolTable from @p root. * @param root The root ast Node. */ void BuildSymbolTable(ASTContext& ctx, Ptr node, bool buildTrie = true); explicit Collector(ScopeManager& manager) : scopeManager(manager) { } Collector(ScopeManager& manager, bool enableMacroInLsp) : scopeManager(manager), enableMacroInLsp(enableMacroInLsp) { } /** Add a symbol with info @p nodeInfo to the symbolTable in @p ctx. The @p buildTrie is a flag to control whether * to build a group of trie trees to accelerate search. */ static void AddSymbol(ASTContext& ctx, const NodeInfo& nodeInfo, bool buildTrie = true); private: ScopeManager& scopeManager; bool enableMacroInLsp{false}; /** A set of functions to collect symbols of corresponding AST node. */ void CollectPackageNode(ASTContext& ctx, AST::Package& package, bool buildTrie); void CollectFileNode(ASTContext& ctx, AST::File& file, bool buildTrie); void CollectAnnotations( ASTContext& ctx, const std::vector>& annotations, bool buildTrie); void CollectGeneric(ASTContext& ctx, AST::Decl& outerDecl, const AST::Generic& generic, bool buildTrie); void CollectClassDecl(ASTContext& ctx, AST::ClassDecl& cd, bool buildTrie); void CollectClassBody(ASTContext& ctx, const AST::ClassBody& cb, bool buildTrie); void CollectInterfaceDecl(ASTContext& ctx, AST::InterfaceDecl& id, bool buildTrie); void CollectInterfaceBody(ASTContext& ctx, const AST::InterfaceBody& ib, bool buildTrie); void CollectEnumDecl(ASTContext& ctx, AST::EnumDecl& ed, bool buildTrie); void CollectStructDecl(ASTContext& ctx, AST::StructDecl& sd, bool buildTrie); void CollectStructBody(ASTContext& ctx, const AST::StructBody& rb, bool buildTrie); void CollectExtendDecl(ASTContext& ctx, AST::ExtendDecl& ed, bool buildTrie); void CollectMainDecl(ASTContext& ctx, AST::MainDecl& md, bool buildTrie); void CollectFuncDecl(ASTContext& ctx, AST::FuncDecl& fd, bool buildTrie); void CollectFuncBody(ASTContext& ctx, AST::FuncBody& fb, bool buildTrie); void CollectPropDecl(ASTContext& ctx, AST::PropDecl& pd, bool buildTrie); void CollectVarWithPatternDecl(ASTContext& ctx, AST::VarWithPatternDecl& vpd, bool buildTrie); void CollectVarDecl(ASTContext& ctx, AST::VarDecl& vd, bool buildTrie); void CollectTypeAliasDecl(ASTContext& ctx, AST::TypeAliasDecl& tad, bool buildTrie); void CollectIfExpr(ASTContext& ctx, AST::IfExpr& ie, bool buildTrie); void CollectCondition(ASTContext& ctx, AST::Expr& e, bool buildTrie); void CollectWhileExpr(ASTContext& ctx, AST::WhileExpr& we, bool buildTrie); void CollectLitConstExpr(ASTContext& ctx, AST::LitConstExpr& lce, bool buildTrie); void CollectStrInterpolationExpr(ASTContext& ctx, AST::StrInterpolationExpr& sie, bool buildTrie); void CollectInterpolationExpr(ASTContext& ctx, AST::InterpolationExpr& ie, bool buildTrie); void CollectTryExpr(ASTContext& ctx, AST::TryExpr& te, bool buildTrie); void CollectSpawnExpr(ASTContext& ctx, AST::SpawnExpr& se, bool buildTrie); void CollectMacroExpandDecl(ASTContext& ctx, AST::MacroExpandDecl& med, bool buildTrie); void CollectQuoteExpr(ASTContext& ctx, AST::QuoteExpr& qe, bool buildTrie); void CollectMacroExpandExpr(ASTContext& ctx, AST::MacroExpandExpr& mee, bool buildTrie); void CollectMacroExpandParam(ASTContext& ctx, AST::MacroExpandParam& mep, bool buildTrie); }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Sema/ConstEvaluationChecker.cpp000066400000000000000000001047401510705540100236000ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "Diags.h" #include "TypeCheckUtil.h" using namespace Cangjie; using namespace AST; using namespace Sema; namespace Cangjie { namespace { void DiagExpectConstFunc(DiagnosticEngine& diag, const FuncDecl& fd) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_expect_const, fd, MakeRange(fd.identifier), "function"); builder.AddMainHintArguments(""); } void DiagExpectConstExpr(DiagnosticEngine& diag, const Expr& expr, bool isWeak) { std::string constKind = isWeak ? "expression" : "expression guaranteed to be evaluated at compile time"; auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_expect_const, expr, constKind); std::string mainHint = ""; if (expr.astKind == ASTKind::RETURN_EXPR) { builder.AddNote("cannot jump to non-constant context from a constant context"); } else if (auto tryExpr = DynamicCast(&expr); tryExpr && tryExpr->isDesugaredFromTryWithResources) { builder.AddNote("try-with-resources expressions are not constant"); } else if (Ty::IsTyCorrect(expr.ty) && expr.ty->IsStructArray() && (expr.astKind == ASTKind::LIT_CONST_EXPR || expr.astKind == ASTKind::ARRAY_LIT)) { CJC_ASSERT(!expr.ty->typeArgs.empty() && expr.ty->typeArgs.front()); mainHint = "expressions of type 'Array' are not constant"; if (expr.astKind == ASTKind::ARRAY_LIT) { auto expected = "VArray<" + expr.ty->typeArgs.front()->String() + ", $" + std::to_string(StaticCast(expr).children.size()) + ">"; builder.AddNote("consider add type annotation '" + expected + "' to use value array"); } } builder.AddMainHintArguments(mainHint); } void DiagDefineVarInConstFunction(DiagnosticEngine& diag, const VarDeclAbstract& vda) { auto range = MakeRange(vda.identifier); diag.DiagnoseRefactor(DiagKindRefactor::sema_cannot_define_var_in_const_funciton, vda, range); } void DiagNoConstInit(DiagnosticEngine& diag, const FuncDecl& fd) { auto range = MakeRange(fd.identifier); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_no_const_init, fd, range); Ptr outerDecl = fd.outerDecl; if (outerDecl == nullptr) { return; } if (outerDecl->astKind == ASTKind::EXTEND_DECL) { const auto& ed = StaticCast(*outerDecl); CJC_NULLPTR_CHECK(ed.extendedType); outerDecl = Ty::GetDeclPtrOfTy(ed.extendedType->ty); } if (outerDecl == nullptr || outerDecl->TestAttr(Attribute::IMPORTED)) { return; } auto outerDeclRange = MakeRange(outerDecl->identifier); builder.AddNote(*outerDecl, outerDeclRange, "consider add a 'const' constructor to this " + TypeCheckUtil::DeclKindToString(*outerDecl)); } void DiagCannotDefineConstInit( DiagnosticEngine& diag, const std::vector>& constInits, const std::vector>& vars) { CJC_ASSERT(!constInits.empty()); auto iter = constInits.cbegin(); CJC_NULLPTR_CHECK(*iter); auto range = MakeRange((*iter)->identifier); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_class_const_init_with_var, **iter, range); ++iter; while (iter != constInits.cend()) { CJC_NULLPTR_CHECK(*iter); auto initRange = MakeRange((*iter)->identifier); builder.AddNote(**iter, initRange, "cannot define 'const' constructor"); ++iter; } for (auto var : vars) { CJC_NULLPTR_CHECK(var); auto varRange = MakeRange(var->identifier); builder.AddNote(*var, varRange, "member variable declared with 'var'"); } } bool HasAnnotationDeclTarget(const Annotation& anno) { if (anno.kind != AnnotationKind::ANNOTATION || anno.args.size() != 1) { return false; } auto& arg = anno.args.front(); CJC_NULLPTR_CHECK(arg); CJC_NULLPTR_CHECK(arg->expr); return arg->name == "target" && arg->expr->astKind == ASTKind::ARRAY_LIT; } // String operations are handled specially, // because the implementations in the core library rely on `unsafe` and loop, // which are not permitted in a `const` operator. bool IsStringBinaryExpr(const Expr& expr) { CJC_NULLPTR_CHECK(expr.desugarExpr); if (!Ty::IsTyCorrect(expr.ty) || expr.astKind != ASTKind::BINARY_EXPR || expr.desugarExpr->astKind != ASTKind::CALL_EXPR) { return false; } CallExpr& ce = StaticCast(*expr.desugarExpr); CJC_NULLPTR_CHECK(ce.baseFunc); if (ce.baseFunc->astKind != ASTKind::MEMBER_ACCESS) { return false; } auto& baseExpr = StaticCast(*ce.baseFunc).baseExpr; CJC_NULLPTR_CHECK(baseExpr); if (!Ty::IsTyCorrect(baseExpr->ty) || !baseExpr->ty->IsString()) { return false; } auto baseFuncTarget = ce.baseFunc->GetTarget(); if (baseFuncTarget == nullptr || baseFuncTarget->fullPackageName != CORE_PACKAGE_NAME || ce.args.empty()) { return false; } return true; } std::unordered_set> CollectMembers(const std::vector>& decls) { std::unordered_set> res; for (const auto& decl : decls) { if (decl->astKind == ASTKind::VAR_DECL && !decl->TestAttr(Attribute::STATIC)) { (void)res.emplace(decl.get()); } } return res; } // Checks whether `StructDecl` or `ClassDecl` has `const init`. bool HasConstInit(const std::vector>& decls) { return Utils::In(decls, [](auto& decl) { CJC_NULLPTR_CHECK(decl); return decl->IsConst() && decl->TestAttr(Attribute::CONSTRUCTOR); }); } // Checks whether a decl has been marked as "const safe" - and should be skipped by the // constant evaluation checker. Only usable in the standard library. bool IsConstSafe(Decl& decl) { auto isConstSafe = [](const OwnedPtr& a) { return a->kind == AnnotationKind::CONSTSAFE; }; auto& annos = decl.annotations; return std::find_if(annos.begin(), annos.end(), isConstSafe) != annos.end(); } }; // namespace class ConstEvaluationChecker { public: ConstEvaluationChecker(DiagnosticEngine& diag, Package& pkg) : diag(diag), pkg(pkg) { } ~ConstEvaluationChecker() = default; void Check() { (void)ChkAllDeclsByWalk(pkg); } private: // Walk through the `node` and do `const` checking for declarations. bool ChkAllDeclsByWalk(Node& node) { bool res = true; Walker(&node, [this, &res](auto node) { if (node->TestAnyAttr(Attribute::IMPORTED, Attribute::IS_BROKEN, Attribute::HAS_BROKEN)) { return VisitAction::SKIP_CHILDREN; } if (auto decl = DynamicCast(node); decl) { // Arguments of annotations must be `const` expressions. res = ChkAnnotations(*decl) && res; // Const-safe decls don't need to be checked. if (IsConstSafe(*decl)) { return VisitAction::SKIP_CHILDREN; } // There are two kinds of rules to check for the `decl`: // 1. If `isConst` is set, the `decl` must be a `VarDecl` or `VarWithPatternDecl` or `FuncDecl`. // We'll check rules related to `const` expressions. if (decl->IsConst()) { res = ChkConstDecl(*decl) && res; return VisitAction::SKIP_CHILDREN; } // 2. If it is a `StructDecl` or `ClassDecl` or `ExtendDecl`, // inheritance rules such as `const init` will be checked. switch (decl->astKind) { case ASTKind::STRUCT_DECL: res = ChkStructDecl(StaticCast(*decl)) && res; return VisitAction::SKIP_CHILDREN; case ASTKind::CLASS_DECL: res = ChkClassDecl(StaticCast(*decl)) && res; return VisitAction::SKIP_CHILDREN; case ASTKind::EXTEND_DECL: res = ChkExtendDecl(StaticCast(*decl)) && res; return VisitAction::SKIP_CHILDREN; default: return VisitAction::WALK_CHILDREN; } } return VisitAction::WALK_CHILDREN; }).Walk(); return res; } bool ChkAnnotations(const Decl& decl) { bool res = true; for (auto& anno : decl.annotations) { CJC_NULLPTR_CHECK(anno); if (HasAnnotationDeclTarget(*anno)) { CJC_ASSERT(anno->args.size() == 1 && anno->args.front() && anno->args.front()->expr && anno->args.front()->expr->astKind == ASTKind::ARRAY_LIT); res = ChkChildren(StaticCast(*anno->args.front()->expr), true) && res; } } if (decl.annotationsArray != nullptr) { res = ChkChildren(*decl.annotationsArray, true) && res; } return res; } // Call this function to check `Decl` if the `decl` is assumed to be `const`. // This function will do `const` checking even if the `Decl` is not marked as `const`. // For example, the `let` declarations in `const` functions // will be checked and marked as `const` by this function. bool ChkConstDecl(Decl& decl) { if (decl.TestAnyAttr(Attribute::IS_BROKEN, Attribute::HAS_BROKEN)) { return false; } switch (decl.astKind) { case ASTKind::VAR_DECL: return ChkVarDecl(StaticCast(decl)); case ASTKind::VAR_WITH_PATTERN_DECL: return ChkVarWithPatternDecl(StaticCast(decl)); case ASTKind::FUNC_DECL: return ChkFuncDecl(StaticCast(decl)); default: return false; } } bool ChkVarDeclAbstract(VarDeclAbstract& vd) { if (vd.isConst) { ConstWalker(&vd, [this](auto node) { if (Utils::In(node.get(), inConstContext)) { return VisitAction::SKIP_CHILDREN; } else if (auto varDecl = DynamicCast(node); varDecl && varDecl->isConst) { inConstContext.emplace(node.get()); } else if (node->astKind == ASTKind::FUNC_BODY || node->astKind == ASTKind::RETURN_EXPR) { inConstContext.emplace(node.get()); } return VisitAction::WALK_CHILDREN; }).Walk(); } if (vd.isVar) { DiagDefineVarInConstFunction(diag, vd); // Set `isConst` to be `true` to avoid chain of errors. vd.isConst = true; return false; } if (vd.initializer == nullptr) { return true; } return ChkExpr(*vd.initializer, !vd.isConst); } bool ChkVarDecl(VarDecl& vd) { return ChkVarDeclAbstract(vd); } bool ChkVarWithPatternDecl(VarWithPatternDecl& vpd) { // only if vpd is const need the inner vars marked const // let vpd in const func are not const variables if (vpd.IsConst()) { Walker(vpd.irrefutablePattern.get(), [](auto node) { if (node->astKind == ASTKind::VAR_DECL) { StaticCast(*node).isConst = true; } return VisitAction::WALK_CHILDREN; }).Walk(); } return ChkVarDeclAbstract(vpd); } bool ChkFuncBody(const FuncBody& fb) { if (fb.TestAnyAttr(Attribute::IS_BROKEN, Attribute::HAS_BROKEN)) { return false; } // The `funcDecl` of a lambda expression is null. if (fb.funcDecl == nullptr || fb.funcDecl->IsConst()) { ConstWalker(&fb, [this](auto node) { if (auto decl = DynamicCast(node)) { inConstFunc.emplace(decl); } else if (auto re = DynamicCast(node); re && re->GetTarget()) { auto target = re->GetTarget(); CJC_NULLPTR_CHECK(target); if (!IsClassOrEnumConstructor(*target) && (re->isThis || re->isSuper || IsInstanceMember(*target))) { constThisOrSuper.emplace(re); } } return VisitAction::WALK_CHILDREN; }).Walk(); } bool res = true; CJC_ASSERT(fb.paramLists.size() == 1 && fb.paramLists.front()); for (auto& param : fb.paramLists.front()->params) { if (param->assignment && param->desugarDecl) { // Default parameters should be `const`. bool isParamConst = ChkExpr(*param->assignment, true); param->desugarDecl->isConst = isParamConst; res = isParamConst && res; } } // The `body` of imported `FuncBody`s is null. // The correctness of the imported function was checked in the package that defines the function. if (fb.body) { res = ChkBlock(*fb.body, true) && res; } return res; } bool ChkFuncDecl(FuncDecl& fd) { if (!fd.isConst) { DiagExpectConstFunc(diag, fd); // Set `isConst` to be `true` to avoid chain of errors. fd.isConst = true; return false; } if (fd.TestAttr(Attribute::CONSTRUCTOR)) { inInit = true; } auto res = ChkFuncBody(*fd.funcBody); if (fd.TestAttr(Attribute::CONSTRUCTOR)) { inInit = false; } return res; } bool ChkDeclHasConstAllConstInitializers(const std::vector>& decls) { bool hasStaticConstInit = false; bool hasNonStaticConstInit = false; for (auto& decl : decls) { if (decl->IsConst() && decl->TestAttr(Attribute::CONSTRUCTOR)) { if (decl->TestAttr(Attribute::STATIC)) { hasStaticConstInit = true; } else { hasNonStaticConstInit = true; } } } auto res = true; for (auto& decl : decls) { if (auto vda = DynamicCast(decl.get()); vda && vda->astKind != ASTKind::PROP_DECL && vda->initializer && ((hasStaticConstInit && vda->TestAttr(Attribute::STATIC)) || (hasNonStaticConstInit && !vda->TestAttr(Attribute::STATIC)))) { res = ChkExpr(*vda->initializer, false) && res; } } return res; } // Cannot define `const` function without `const init`. bool ChkDeclNoConstInitNoConstFunc(const std::vector>& decls) { auto res = true; for (auto& decl : decls) { CJC_NULLPTR_CHECK(decl); // Cannot define `const` instance member function without `const init`. // But `const` static member function can be defined without `const init`. if (decl->IsConst() && decl->IsFunc() && !decl->TestAttr(Attribute::STATIC)) { DiagNoConstInit(diag, StaticCast(*decl)); res = false; } } return res; } bool ChkStructDecl(const StructDecl& sd) { CJC_NULLPTR_CHECK(sd.body); const auto& decls = sd.body->decls; structOrClassMembers = CollectMembers(decls); bool res = true; for (auto& decl : decls) { CJC_NULLPTR_CHECK(decl); res = ChkAllDeclsByWalk(*decl) && res; } if (HasConstInit(decls)) { res = ChkDeclHasConstAllConstInitializers(decls) && res; } else { res = ChkDeclNoConstInitNoConstFunc(decls) && res; } structOrClassMembers.clear(); return res; } bool ChkClassDeclHasConstInitNoVarMember(const ClassDecl& cd) { CJC_NULLPTR_CHECK(cd.body); std::vector> vars; // variables declared with `var`. for (auto& decl : cd.body->decls) { // Cannot define 'var' variables together with `const init`. // But `mut prop` can be defined with `const init`. if (auto vda = DynamicCast(decl.get()); vda && vda->isVar && vda->astKind != ASTKind::PROP_DECL) { vars.emplace_back(vda); } } if (vars.empty()) { return true; } std::vector> constInits; for (auto& decl : cd.body->decls) { CJC_NULLPTR_CHECK(decl); // The constant constructor marked with `PRIMARY_CONSTRUCTOR` is compiler added. // We don't need them since the primary constructors should have been collected. if (decl->IsConst() && decl->TestAttr(Attribute::CONSTRUCTOR) && !decl->TestAttr(Attribute::PRIMARY_CONSTRUCTOR)) { constInits.emplace_back(decl.get()); } } DiagCannotDefineConstInit(diag, constInits, vars); return false; } bool ChkClassDecl(const ClassDecl& cd) { CJC_NULLPTR_CHECK(cd.body); const auto& decls = cd.body->decls; structOrClassMembers = CollectMembers(decls); bool res = true; for (auto& decl : decls) { CJC_NULLPTR_CHECK(decl); res = ChkAllDeclsByWalk(*decl) && res; } if (HasConstInit(decls)) { res = ChkClassDeclHasConstInitNoVarMember(cd) && res; res = ChkDeclHasConstAllConstInitializers(decls) && res; } else { res = ChkDeclNoConstInitNoConstFunc(decls) && res; } structOrClassMembers.clear(); return res; } bool ChkExtendDecl(const ExtendDecl& ed) { auto res = true; CJC_NULLPTR_CHECK(ed.extendedType); for (auto& decl : ed.members) { CJC_NULLPTR_CHECK(decl); res = ChkAllDeclsByWalk(*decl) && res; } bool hasConstInit = true; Ptr target = Ty::GetDeclPtrOfTy(ed.extendedType->ty); if (target == nullptr) { // Do nothing.. } else if (target->astKind == ASTKind::STRUCT_DECL) { const auto& sd = StaticCast(*target); CJC_NULLPTR_CHECK(sd.body); hasConstInit = HasConstInit(sd.body->decls); } else if (target->astKind == ASTKind::CLASS_DECL) { const auto& cd = StaticCast(*target); CJC_NULLPTR_CHECK(cd.body); hasConstInit = HasConstInit(cd.body->decls); } if (!hasConstInit) { res = ChkDeclNoConstInitNoConstFunc(ed.members) && res; } return res; } bool ChkExpr(const Expr& expr, bool isWeak) { if (expr.TestAnyAttr(Attribute::IS_BROKEN, Attribute::HAS_BROKEN)) { return false; } if (expr.desugarExpr) { return ChkDesugarExpr(expr, isWeak); } auto iter = dispatchTable.find(expr.astKind); if (iter == dispatchTable.cend()) { DiagExpectConstExpr(diag, expr, isWeak); return false; } return iter->second(expr, isWeak); } bool ChkDesugarExpr(const Expr& expr, bool isWeak) { CJC_NULLPTR_CHECK(expr.desugarExpr); if (IsStringBinaryExpr(expr)) { const CallExpr& ce = StaticCast(*expr.desugarExpr); const MemberAccess& ma = StaticCast(*ce.baseFunc); CJC_NULLPTR_CHECK(ma.baseExpr); bool res = ChkExpr(*ma.baseExpr, isWeak); for (auto& arg : ce.args) { CJC_ASSERT(arg && arg->expr); res = ChkExpr(*arg->expr, isWeak) && res; } return res; } if (expr.astKind == ASTKind::INC_OR_DEC_EXPR || expr.astKind == ASTKind::TRAIL_CLOSURE_EXPR) { return ChkExpr(*expr.desugarExpr, isWeak); } // 1) array[0]++ is desugared to call // 2) binary/unary operator call is desuguared to call if (expr.desugarExpr->astKind == ASTKind::CALL_EXPR) { return ChkCallExpr(StaticCast(*expr.desugarExpr), isWeak); } // multiple assignement expr is expanded into a block of multiple expressions if (expr.astKind == ASTKind::ASSIGN_EXPR) { return CheckDesugaredMultipleAssignment(StaticCast(*expr.desugarExpr), isWeak); } // Not all syntax sugars are valid constant expressions. DiagExpectConstExpr(diag, expr, isWeak); return false; } bool CheckDesugaredMultipleAssignment(const Block& e, bool isWeak) { bool res = true; for (auto& node : e.body) { if (auto decl = DynamicCast(node.get())) { if (!ChkVarDecl(*decl)) { res = false; } } else if (auto expr = DynamicCast(node.get())) { if (!ChkExpr(*expr, isWeak)) { res = false; } } } return res; } bool ChkLitConstExpr(const LitConstExpr& le) { if (!Ty::IsTyCorrect(le.ty)) { return false; } if (le.ty->IsStructArray()) { DiagExpectConstExpr(diag, le, true); return false; } if (le.siExpr) { DiagExpectConstExpr(diag, le, false); return false; } return true; } // Check the inner `expr` for `ReturnExpr`, `ParenExpr`, `UnaryExpr`. template bool ChkInnerExpr(const T& expr, bool isWeak) { CJC_NULLPTR_CHECK(expr.expr); return ChkExpr(*expr.expr, isWeak); } // Check the `leftExpr` for `IsExpr`, `AsExpr`. template bool ChkLeftExpr(const T& expr, bool isWeak) { CJC_NULLPTR_CHECK(expr.leftExpr); return ChkExpr(*expr.leftExpr, isWeak); } // Check the `children` for `ArrayLit` or `TupleLit`. template bool ChkChildren(const T& expr, bool isWeak) { const std::vector>& children = expr.children; bool res = true; for (auto& child : children) { CJC_NULLPTR_CHECK(child); if (!ChkExpr(*child, isWeak)) { res = false; } } return res; } bool ChkReturnExpr(const ReturnExpr& ret, bool isWeak) { if (Utils::In(Ptr(&StaticCast(ret)), inConstContext) && !Utils::In(Ptr(StaticCast(ret.refFuncBody.get())), inConstContext)) { DiagExpectConstExpr(diag, ret, isWeak); return false; } return ChkInnerExpr(ret, isWeak); } bool ChkArrayLit(const ArrayLit& al, bool isWeak) { if (!Ty::IsTyCorrect(al.ty)) { return false; } if (al.ty->IsStructArray()) { DiagExpectConstExpr(diag, al, isWeak); return false; } return ChkChildren(al, isWeak); } bool ChkRefExpr(const RefExpr& re, bool isWeak) { Ptr target = re.GetTarget(); if (target == nullptr) { // There are semantic errors, and these errors should have been reported. Just return false. return false; } // Target of expression 'this()' or 'super()' is a constructor. if (!IsClassOrEnumConstructor(*target) && (re.isThis || re.isSuper || IsInstanceMember(*target)) && !Utils::In(&re, constThisOrSuper)) { DiagExpectConstExpr(diag, re, isWeak); return false; } // Target of a single 'this' or 'super' expression is a type declaration. if ((re.isThis || re.isSuper) && target->IsNominalDecl()) { return true; } // `enum` constructors, `const` variables are constant expressions. if (target->TestAttr(Attribute::ENUM_CONSTRUCTOR) || target->IsConst()) { return true; } // Member variable `x` is the syntax sugar of `this.x`. // Since `this` is a constant expression and member accesses of constant expressions are constant expressions. // Therefore, `this.x` is a constant expression. Thus `x` is a constant expression. if (Utils::In(target, structOrClassMembers)) { return true; } if (isWeak && Utils::In(target, inConstFunc)) { // Parameters and non-constant local variables of a constant function are "weak constant". return true; } DiagExpectConstExpr(diag, re, isWeak); return false; } bool ChkCallExpr(const CallExpr& ce, bool isWeak) { CJC_NULLPTR_CHECK(ce.baseFunc); if (!ChkExpr(*ce.baseFunc, isWeak)) { return false; } bool res = true; for (auto& arg : ce.args) { CJC_NULLPTR_CHECK(arg); CJC_NULLPTR_CHECK(arg->expr); res = ChkExpr(*arg->expr, isWeak) && res; } return res; } bool ChkLambdaExpr(const LambdaExpr& le) { CJC_NULLPTR_CHECK(le.funcBody); return ChkFuncBody(*le.funcBody); } bool ChkBinaryExpr(const BinaryExpr& be, bool isWeak) { CJC_NULLPTR_CHECK(be.leftExpr); CJC_NULLPTR_CHECK(be.rightExpr); bool res = ChkExpr(*be.leftExpr, isWeak); res = ChkExpr(*be.rightExpr, isWeak) && res; return res; } bool ChkIfExpr(const IfExpr& ie, bool isWeak) { CJC_NULLPTR_CHECK(ie.condExpr); CJC_NULLPTR_CHECK(ie.thenBody); bool res = ChkExpr(*ie.condExpr, isWeak); res = ChkBlock(*ie.thenBody, isWeak) && res; if (ie.elseBody) { res = ChkExpr(*ie.elseBody, isWeak) && res; } return res; } bool ChkMatchCase(const MatchCase& mc, bool isWeak) { if (mc.TestAnyAttr(Attribute::IS_BROKEN, Attribute::HAS_BROKEN)) { return false; } bool res = true; if (mc.patternGuard) { res = ChkExpr(*mc.patternGuard, isWeak) && res; } CJC_NULLPTR_CHECK(mc.exprOrDecls); res = ChkBlock(*mc.exprOrDecls, isWeak) && res; return res; } bool ChkMatchCaseOther(const MatchCaseOther& mco, bool isWeak) { if (mco.TestAnyAttr(Attribute::IS_BROKEN, Attribute::HAS_BROKEN)) { return false; } CJC_NULLPTR_CHECK(mco.matchExpr); CJC_NULLPTR_CHECK(mco.exprOrDecls); bool res = true; if (auto expr = DynamicCast(mco.matchExpr.get()); expr) { res = ChkExpr(*expr, isWeak) && res; } res = ChkBlock(*mco.exprOrDecls, isWeak) && res; return res; } // `match` expression with or without `selector` are both checked by this function. // We don't have to distinguish the semantics for `const` checking. bool ChkMatchExpr(const MatchExpr& me, bool isWeak) { bool res = true; if (me.selector) { res = ChkExpr(*me.selector, isWeak) && res; } for (auto& mc : me.matchCases) { CJC_NULLPTR_CHECK(mc); res = ChkMatchCase(*mc, isWeak) && res; } for (auto& mco : me.matchCaseOthers) { CJC_NULLPTR_CHECK(mco); res = ChkMatchCaseOther(*mco, isWeak) && res; } return res; } bool ChkTryExpr(const TryExpr& te, bool isWeak) { if (te.isDesugaredFromTryWithResources) { DiagExpectConstExpr(diag, te, isWeak); return false; } CJC_NULLPTR_CHECK(te.tryBlock); bool res = ChkBlock(*te.tryBlock, isWeak); for (auto& cb : te.catchBlocks) { CJC_NULLPTR_CHECK(cb); res = ChkBlock(*cb, isWeak) && res; } if (te.finallyBlock) { res = ChkBlock(*te.finallyBlock, isWeak) && res; } return res; } bool ChkMemberAccess(const MemberAccess& ma, bool isWeak) { CJC_NULLPTR_CHECK(ma.baseExpr); Ptr target = ma.GetTarget(); if (target == nullptr) { // There are semantic errors, and these errors should have been reported. Just return false. return false; } Ptr baseTarget = ma.baseExpr->GetTarget(); // Global/static `const` variables, and `enum` constructors are constant. if (baseTarget && (baseTarget->astKind == ASTKind::PACKAGE_DECL || baseTarget->IsTypeDecl()) && ((target->IsConst() && (IsInstanceConstructor(*target) || target->IsStaticOrGlobal())) || target->TestAttr(Attribute::ENUM_CONSTRUCTOR))) { return true; } if (ChkExpr(*ma.baseExpr, isWeak)) { // Member variables and constant member functions of constant expressions are constant. if (target->TestAttr(Attribute::ENUM_CONSTRUCTOR) || target->astKind == ASTKind::VAR_DECL || (target->astKind == ASTKind::FUNC_DECL && target->IsConst())) { return true; } } DiagExpectConstExpr(diag, ma, isWeak); return false; } bool ChkSubscriptExpr(const SubscriptExpr& se, bool isWeak) { CJC_NULLPTR_CHECK(se.baseExpr); if (!Ty::IsTyCorrect(se.baseExpr->ty)) { // There are semantic errors, and these errors should have been reported. Just return false. return false; } if (!se.baseExpr->ty->IsTuple() && !Is(se.baseExpr->ty)) { DiagExpectConstExpr(diag, se, isWeak); return false; } if (!ChkExpr(*se.baseExpr, isWeak)) { return false; } bool res = true; for (auto& idx : se.indexExprs) { CJC_NULLPTR_CHECK(idx); res = ChkExpr(*idx, isWeak) && res; } return res; } bool ChkBlock(const Block& block, bool isWeak) { if (block.TestAttr(Attribute::UNSAFE)) { DiagExpectConstExpr(diag, block, isWeak); return false; } bool res = true; for (auto& node : block.body) { CJC_NULLPTR_CHECK(node); if (auto decl = DynamicCast(node.get()); decl) { res = ChkConstDecl(*decl) && res; } else if (auto expr = DynamicCast(node.get()); expr) { res = ChkExpr(*expr, isWeak) && res; } else { CJC_ABORT(); } } return res; } bool ChkAssignExpr(const AssignExpr& ae, bool isWeak) { CJC_NULLPTR_CHECK(ae.leftValue); CJC_NULLPTR_CHECK(ae.rightExpr); if (inInit && Utils::In>(ae.leftValue->GetTarget(), structOrClassMembers)) { return ChkExpr(*ae.rightExpr, isWeak); } DiagExpectConstExpr(diag, ae, isWeak); return false; } bool ChkIncOrDecExpr(const IncOrDecExpr& ide) { CJC_NULLPTR_CHECK(ide.expr); if (inInit && Utils::In>(ide.expr->GetTarget(), structOrClassMembers)) { return true; } DiagExpectConstExpr(diag, ide, true); return false; } template std::function Proxy(bool (ConstEvaluationChecker::*chk)(const T&, bool)) { return [this, chk](const Expr& expr, bool isWeak) { return (this->*chk)(StaticCast(expr), isWeak); }; } template std::function Proxy(bool (ConstEvaluationChecker::*chk)(const T&)) { return [this, chk](const Expr& expr, bool) { return (this->*chk)(StaticCast(expr)); }; } std::unordered_map> dispatchTable = { {ASTKind::WILDCARD_EXPR, [](const Expr&, bool) { return true; }}, {ASTKind::LIT_CONST_EXPR, Proxy(&ConstEvaluationChecker::ChkLitConstExpr)}, {ASTKind::RETURN_EXPR, Proxy(&ConstEvaluationChecker::ChkReturnExpr)}, {ASTKind::THROW_EXPR, Proxy(&ConstEvaluationChecker::ChkInnerExpr)}, {ASTKind::PERFORM_EXPR, Proxy(&ConstEvaluationChecker::ChkInnerExpr)}, {ASTKind::PAREN_EXPR, Proxy(&ConstEvaluationChecker::ChkInnerExpr)}, {ASTKind::UNARY_EXPR, Proxy(&ConstEvaluationChecker::ChkInnerExpr)}, {ASTKind::IS_EXPR, Proxy(&ConstEvaluationChecker::ChkLeftExpr)}, {ASTKind::AS_EXPR, Proxy(&ConstEvaluationChecker::ChkLeftExpr)}, {ASTKind::ARRAY_LIT, Proxy(&ConstEvaluationChecker::ChkArrayLit)}, {ASTKind::TUPLE_LIT, Proxy(&ConstEvaluationChecker::ChkChildren)}, {ASTKind::REF_EXPR, Proxy(&ConstEvaluationChecker::ChkRefExpr)}, {ASTKind::CALL_EXPR, Proxy(&ConstEvaluationChecker::ChkCallExpr)}, {ASTKind::BINARY_EXPR, Proxy(&ConstEvaluationChecker::ChkBinaryExpr)}, {ASTKind::IF_EXPR, Proxy(&ConstEvaluationChecker::ChkIfExpr)}, {ASTKind::MATCH_EXPR, Proxy(&ConstEvaluationChecker::ChkMatchExpr)}, {ASTKind::TRY_EXPR, Proxy(&ConstEvaluationChecker::ChkTryExpr)}, {ASTKind::MEMBER_ACCESS, Proxy(&ConstEvaluationChecker::ChkMemberAccess)}, {ASTKind::SUBSCRIPT_EXPR, Proxy(&ConstEvaluationChecker::ChkSubscriptExpr)}, {ASTKind::LAMBDA_EXPR, Proxy(&ConstEvaluationChecker::ChkLambdaExpr)}, {ASTKind::BLOCK, Proxy(&ConstEvaluationChecker::ChkBlock)}, {ASTKind::ASSIGN_EXPR, Proxy(&ConstEvaluationChecker::ChkAssignExpr)}, {ASTKind::INC_OR_DEC_EXPR, Proxy(&ConstEvaluationChecker::ChkIncOrDecExpr)}, }; DiagnosticEngine& diag; Package& pkg; // Context states: std::unordered_set> inConstContext; // nodes in const contexts, contains `FuncBody`, `ReturnExpr` and `VarDeclAbstract` std::unordered_set> inConstFunc; // local variables of const function std::unordered_set> constThisOrSuper; // `this` and `super` that are `const` std::unordered_set> structOrClassMembers; // member variables of a struct/class bool inInit = false; // inside a constructor }; void TypeChecker::TypeCheckerImpl::CheckConstEvaluation(Package& pkg) { ConstEvaluationChecker(diag, pkg).Check(); } } // namespace Cangjie cangjie_compiler-1.0.7/src/Sema/DeclAttributeChecker.cpp000066400000000000000000000420261510705540100232130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the functions to check decl attributes. */ #include "TypeCheckerImpl.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Basic/Match.h" #include "cangjie/Utils/CheckUtils.h" using namespace Cangjie; using namespace AST; using namespace Meta; namespace { class DeclAttributeChecker { public: DeclAttributeChecker(const GlobalOptions& options, DiagnosticEngine& diag, Decl& decl) : opts{options}, diag(diag), decl_(decl) { } ~DeclAttributeChecker() = default; /** Dispatch checking function for decl to be checked. */ void Check() const; private: void SetCommonAttributes(Decl& d) const; void CheckClassAttribute(ClassDecl& cd) const; void CheckInterfaceAttribute(InterfaceDecl& id) const; void CheckEnumAttribute(EnumDecl& ed) const; void CheckStructAttribute(StructDecl& sd) const; void CheckExtendAttribute(ExtendDecl& ed) const; void CheckPropDeclAttributes(const PropDecl& pd) const; void CheckGenericFuncDeclAttributes(const FuncDecl& fd) const; void CheckAttributesForPropAndFuncDeclInClass(const ClassDecl& cd, Decl& member) const; void CheckCJMPAttributesForPropAndFuncDeclInClass(const ClassDecl& cd, Decl& member) const; static void SetContextAttribute(const Decl& decl, Decl& memberDecl) { auto found = ctxAttrMap.find(decl.astKind); if (found != ctxAttrMap.end()) { memberDecl.EnableAttr(found->second); } } const GlobalOptions& opts; DiagnosticEngine& diag; Decl& decl_; inline static std::unordered_map ctxAttrMap = { {ASTKind::INTERFACE_DECL, Attribute::IN_CLASSLIKE}, {ASTKind::CLASS_DECL, Attribute::IN_CLASSLIKE}, {ASTKind::ENUM_DECL, Attribute::IN_ENUM}, {ASTKind::STRUCT_DECL, Attribute::IN_STRUCT}, {ASTKind::EXTEND_DECL, Attribute::IN_EXTEND}, }; }; void SetParentDecl(Decl& structDecl, const FuncDecl& fd) { if (!fd.funcBody) { return; } auto outerDecl = &structDecl; if (auto ed = DynamicCast(outerDecl); ed && ed->extendedType) { auto extendedDecl = Ty::GetDeclPtrOfTy(ed->extendedType->ty); if (!extendedDecl) { return; } outerDecl = extendedDecl; } if (auto cld = DynamicCast(outerDecl); cld) { fd.funcBody->parentClassLike = cld; } else if (auto sd = DynamicCast(outerDecl); sd) { fd.funcBody->parentStruct = sd; } else if (auto ed = DynamicCast(outerDecl); ed) { fd.funcBody->parentEnum = ed; } } void SetContextDecl(Decl& structDecl, Decl& member) { member.outerDecl = &structDecl; if (auto pd = DynamicCast(&member); pd) { auto setDecl = [&structDecl](auto& it) { it->outerDecl = &structDecl; SetParentDecl(structDecl, *it); }; std::for_each(pd->getters.begin(), pd->getters.end(), setDecl); std::for_each(pd->setters.begin(), pd->setters.end(), setDecl); } else if (auto fd = DynamicCast(&member); fd) { SetParentDecl(structDecl, *fd); if (fd->funcBody->paramLists.empty()) { return; } for (auto& param : fd->funcBody->paramLists[0]->params) { if (param->desugarDecl) { SetParentDecl(structDecl, *param->desugarDecl); } } } } /** Mark static attribute and parent decl recursively. */ void InheritMemberAttrRecursively(Decl& memberDecl, bool isStatic) { auto visitor = [isStatic](Ptr node) -> VisitAction { if (node->IsNominalDecl()) { return VisitAction::SKIP_CHILDREN; // Ignore for invalid nesting type decl. } switch (node->astKind) { case ASTKind::FUNC_BODY: { if (isStatic) { node->EnableAttr(Attribute::STATIC); } break; } case ASTKind::FUNC_DECL: { auto fd = RawStaticCast(node); if (isStatic) { fd->EnableAttr(Attribute::STATIC); } if (fd->propDecl && !fd->TestAttr(Attribute::IMPLICIT_ADD)) { fd->CloneAttrs(*fd->propDecl); fd->DisableAttr(Attribute::MUT); } break; } default: break; } return VisitAction::WALK_CHILDREN; }; Walker walker(&memberDecl, visitor); walker.Walk(); } void SetDeclInternal(Decl& d) { if (d.TestAnyAttr(Attribute::PROTECTED, Attribute::PRIVATE, Attribute::PUBLIC) || d.astKind == ASTKind::EXTEND_DECL) { return; } d.EnableAttr(Attribute::INTERNAL); } } // namespace void DeclAttributeChecker::SetCommonAttributes(Decl& d) const { CJC_ASSERT(d.IsNominalDecl()); SetDeclInternal(d); for (auto memberDecl : d.GetMemberDeclPtrs()) { CJC_ASSERT(memberDecl); if (d.TestAttr(Attribute::FOREIGN)) { memberDecl->EnableAttr(Attribute::FOREIGN); } SetDeclInternal(*memberDecl); SetContextDecl(d, *memberDecl); SetContextAttribute(d, *memberDecl); InheritMemberAttrRecursively(*memberDecl, memberDecl->TestAttr(Attribute::STATIC)); } } void DeclAttributeChecker::CheckInterfaceAttribute(InterfaceDecl& id) const { CJC_NULLPTR_CHECK(id.body); if (id.TestAttr(Attribute::SEALED)) { id.EnableAttr(Attribute::OPEN, Attribute::PUBLIC); } for (auto& member : id.body->decls) { CJC_ASSERT(member); member->EnableAttr(Attribute::PUBLIC); // All members in interface are public. if (!member->TestAttr(Attribute::STATIC)) { // Instance member func and prop in interface are open. member->EnableAttr(Attribute::OPEN); } if (auto pd = DynamicCast(member.get())) { auto setMut = [](auto& it) { if (it && !it->TestAttr(Attribute::STATIC)) { it->EnableAttr(Attribute::MUT); } }; std::for_each(pd->setters.begin(), pd->setters.end(), setMut); } } SetCommonAttributes(id); } void DeclAttributeChecker::CheckEnumAttribute(EnumDecl& ed) const { for (auto& constructor : ed.constructors) { constructor->EnableAttr(Attribute::PUBLIC); } SetCommonAttributes(ed); } void DeclAttributeChecker::CheckStructAttribute(StructDecl& sd) const { if (!sd.body) { return; } SetCommonAttributes(sd); auto setMut = [](auto& it) { if (it && !it->TestAttr(Attribute::STATIC)) { it->EnableAttr(Attribute::MUT); } }; for (auto& member : sd.body->decls) { CJC_ASSERT(member); if (member->astKind == ASTKind::PRIMARY_CTOR_DECL) { continue; // PrimaryCtor was desugared as normal func decl before checking process. } if (auto pd = DynamicCast(member.get()); pd) { std::for_each(pd->setters.begin(), pd->setters.end(), setMut); } } } void DeclAttributeChecker::CheckExtendAttribute(ExtendDecl& ed) const { SetCommonAttributes(ed); for (auto& member : ed.members) { CJC_ASSERT(member); const auto& extendedDecl = Ty::GetDeclPtrOfTy(ed.ty); Ptr mutDecl = nullptr; for (auto& modifier : member->modifiers) { if (modifier.modifier == TokenKind::MUT) { mutDecl = &modifier; } } if (member.get()->TestAttr(Attribute::MUT) && mutDecl) { if (extendedDecl && extendedDecl->astKind != ASTKind::STRUCT_DECL && member->astKind != ASTKind::PROP_DECL) { diag.DiagnoseRefactor( DiagKindRefactor::sema_invalid_mut_modifier_extend_of_struct, *mutDecl, extendedDecl->identifier); } else if (ed.ty->IsPrimitive()) { diag.DiagnoseRefactor( DiagKindRefactor::sema_invalid_mut_modifier_extend_of_struct, *mutDecl, ed.ty->String()); } } if (auto pd = DynamicCast(member.get()); pd) { bool needMut = !pd->TestAttr(Attribute::STATIC) && ed.ty && ed.ty->IsStruct(); auto setMut = [needMut](auto& it) { if (it && needMut) { it->EnableAttr(Attribute::MUT); } }; std::for_each(pd->setters.begin(), pd->setters.end(), setMut); } } } void DeclAttributeChecker::CheckClassAttribute(ClassDecl& cd) const { CJC_NULLPTR_CHECK(cd.body); if (cd.TestAttr(Attribute::SEALED)) { cd.EnableAttr(Attribute::OPEN, Attribute::PUBLIC); if (!cd.TestAttr(Attribute::ABSTRACT)) { diag.DiagnoseRefactor( DiagKindRefactor::sema_non_abstract_class_cannot_be_sealed, cd, MakeRange(cd.identifier)); } } SetCommonAttributes(cd); for (auto& memberDecl : cd.body->decls) { CJC_ASSERT(memberDecl); if (memberDecl->IsFuncOrProp()) { CheckAttributesForPropAndFuncDeclInClass(cd, *memberDecl); } } } void DeclAttributeChecker::CheckCJMPAttributesForPropAndFuncDeclInClass(const ClassDecl& cd, Decl& member) const { bool inCJMP = cd.TestAttr(Attribute::COMMON) || cd.TestAttr(Attribute::PLATFORM); bool inAbstractCJMP = cd.TestAttr(Attribute::ABSTRACT) && inCJMP; bool isCJMP = member.TestAttr(Attribute::COMMON) || member.TestAttr(Attribute::PLATFORM); bool isAbstract = member.TestAttr(Attribute::ABSTRACT); bool compilerAddedOrConstructor = member.TestAttr(Attribute::COMPILER_ADD) || member.TestAttr(Attribute::CONSTRUCTOR); bool hasBody = false; std::string memberKind; if (member.astKind == ASTKind::FUNC_DECL) { memberKind = "function"; auto func = StaticCast(&member); if (func->funcBody && func->funcBody->body) { hasBody = true; } } else { memberKind = "property"; auto prop = StaticCast(&member); // dummy getters added in case of actually abstract property if (!prop->getters.empty() && !prop->getters[0]->TestAttr(Attribute::COMPILER_ADD)) { hasBody = true; } } // It can be checked only at parsed, because now no deffirence between modifier and attriubute if (hasBody && inCJMP && isAbstract) { diag.DiagnoseRefactor(DiagKindRefactor::sema_explicitly_abstract_can_not_have_body, member, memberKind); } bool checkedBefore = member.TestAttr(Attribute::FROM_COMMON_PART); // member of `common abstract` must be explicitly marked with common or abstract or have body if (inAbstractCJMP && !isCJMP && !isAbstract && !compilerAddedOrConstructor && !hasBody && !checkedBefore) { auto cjmpKind = cd.TestAttr(Attribute::COMMON) ? "common" : "platform"; diag.DiagnoseRefactor(DiagKindRefactor::sema_cjmp_abstract_class_member_has_no_explicit_modifier, member, MakeRange(member.identifier), cjmpKind, memberKind, cjmpKind); } } void DeclAttributeChecker::CheckAttributesForPropAndFuncDeclInClass(const ClassDecl& cd, Decl& member) const { if (member.astKind != ASTKind::PROP_DECL && member.astKind != ASTKind::FUNC_DECL) { return; } if (opts.compileCjd) { return; } auto type = (member.astKind == ASTKind::PROP_DECL) ? "property" : "function"; // Member decl cannot be abstract when: // 1. member decl is static and not foreign; // 2. class decl is not abstract and not foreign; // 3. member decl can not be modified with 'open' in a non-inheritable object. // 4. member decl is not in a mirror bool invalidAbstract = member.TestAttr(Attribute::ABSTRACT) && ((member.TestAttr(Attribute::STATIC) && !member.TestAttr(Attribute::FOREIGN)) || (!cd.TestAttr(Attribute::ABSTRACT) && !cd.TestAttr(Attribute::FOREIGN))) && !member.TestAttr(Attribute::JAVA_MIRROR) && !cd.TestAttr(Attribute::OBJ_C_MIRROR); if (invalidAbstract && !member.TestAttr(Attribute::COMMON_WITH_DEFAULT)) { diag.Diagnose(member, DiagKind::sema_missing_func_body, type, member.identifier.Val()); } if (member.TestAttr(Attribute::ABSTRACT) && cd.TestAttr(Attribute::ABSTRACT)) { if (!member.TestAttr(Attribute::PUBLIC) && !member.TestAttr(Attribute::PROTECTED)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_invalid_member_visibility_in_class, member, MakeRange(member.identifier), "abstract", type); } } CheckCJMPAttributesForPropAndFuncDeclInClass(cd, member); bool isInheritableClass = cd.TestAttr(Attribute::OPEN) || cd.TestAttr(Attribute::ABSTRACT); if (member.TestAttr(Attribute::OPEN)) { if (!isInheritableClass) { diag.Diagnose(member, DiagKind::sema_ignore_open); } else if (!member.TestAttr(Attribute::PUBLIC) && !member.TestAttr(Attribute::PROTECTED)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_invalid_member_visibility_in_class, member, MakeRange(member.identifier), "open", type); } else if (auto pd = DynamicCast(&member)) { // Inheriting open attribute to getter/setter to guarantee the virtual call of propDecl. auto setOpen = [](auto& it) { CJC_ASSERT(it); it->EnableAttr(Attribute::OPEN); }; std::for_each(pd->setters.begin(), pd->setters.end(), setOpen); std::for_each(pd->getters.begin(), pd->getters.end(), setOpen); } } if (auto fd = DynamicCast(&member); fd && fd->IsFinalizer() && isInheritableClass) { diag.Diagnose(*fd, DiagKind::sema_finalizer_forbidden_in_class, cd.identifier.Val(), cd.TestAttr(Attribute::OPEN) ? "open" : "abstract"); } } void DeclAttributeChecker::CheckPropDeclAttributes(const PropDecl& pd) const { if (pd.outerDecl && pd.outerDecl->TestAttr(Attribute::OBJ_C_MIRROR)) { return; } if (pd.TestAnyAttr(Attribute::ABSTRACT, Attribute::JAVA_MIRROR) || opts.compileCjd) { return; } if (IsCommonWithoutDefault(pd)) { return; } if (pd.isVar) { if ((((!pd.TestAttr(Attribute::STATIC) && !pd.TestAttr(Attribute::OVERRIDE)) || (pd.TestAttr(Attribute::STATIC) && !pd.TestAttr(Attribute::REDEF))) && (pd.setters.empty() || pd.getters.empty())) || (!pd.TestAttr(Attribute::ABSTRACT) && pd.setters.empty() && pd.getters.empty())) { diag.DiagnoseRefactor(DiagKindRefactor::sema_property_must_have_accessors, pd); } } else { if (!pd.setters.empty()) { diag.DiagnoseRefactor(DiagKindRefactor::sema_immutable_property_with_setter, pd); } if (pd.getters.empty()) { diag.DiagnoseRefactor(DiagKindRefactor::sema_property_must_have_accessors, pd); } } } /** * Report error for generic function in interfaces and abstract classes. * Report error for generic functions with open modifier in classes. * Report error for generic operator overloading functions. * Report error for generic functions with override modifier. * @param fd the function declaration to be checked. */ #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void DeclAttributeChecker::CheckGenericFuncDeclAttributes(const FuncDecl& fd) const { if (fd.funcBody == nullptr || fd.funcBody->generic == nullptr) { return; } if (fd.TestAttr(Attribute::OPERATOR)) { diag.Diagnose(fd, DiagKind::sema_generic_in_operator_overload); } } #endif void DeclAttributeChecker::Check() const { match(decl_)([this](ClassDecl& cd) { CheckClassAttribute(cd); }, [this](InterfaceDecl& id) { CheckInterfaceAttribute(id); }, [this](EnumDecl& ed) { CheckEnumAttribute(ed); }, [this](StructDecl& sd) { CheckStructAttribute(sd); }, [this](ExtendDecl& ed) { CheckExtendAttribute(ed); }, [this](const PropDecl& pd) { CheckPropDeclAttributes(pd); }, [this](const FuncDecl& fd) { CheckGenericFuncDeclAttributes(fd); }, []() {}); if (decl_.TestAttr(Attribute::GLOBAL)) { SetDeclInternal(decl_); } } /** * Check and set attributes for decls. * NOTE: should be called after decl type resolved. */ void TypeChecker::TypeCheckerImpl::CheckAllDeclAttributes(const ASTContext& ctx) { std::vector syms = GetAllDecls(ctx); for (auto& sym : syms) { if (auto decl = AST::As(sym->node)) { CJC_ASSERT(!decl->TestAttr(Attribute::IMPORTED) || decl->TestAttr(Attribute::TOOL_ADD) || decl->TestAttr(Attribute::FROM_COMMON_PART) || decl->TestAttr(Attribute::COMMON)); DeclAttributeChecker(ci->invocation.globalOptions, diag, *decl).Check(); } } } cangjie_compiler-1.0.7/src/Sema/Desugar/000077500000000000000000000000001510705540100200555ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck.cpp000066400000000000000000000577101510705540100234340ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Desugar functions used after typecheck step. */ #include #include #include #include #include "Desugar/AfterTypeCheck.h" #include "AutoBoxing.h" #include "NativeFFI/Java/AfterTypeCheck/JavaInteropManager.h" #include "NativeFFI/ObjC/AfterTypeCheck/Desugar.h" #include "ExtraScopes.h" #include "TypeCheckUtil.h" #include "TypeCheckerImpl.h" #include "cangjie/AST/ASTCasting.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Types.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Driver/StdlibMap.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; using namespace Sema::Desugar::AfterTypeCheck; namespace { OwnedPtr CreateConvInt64(VarDecl& vd, Ty& ty) { auto convType = MakeOwnedNode(); convType->kind = TypeKind::TYPE_INT64; convType->ty = &ty; auto numConv = MakeOwnedNode(); numConv->type = std::move(convType); numConv->expr = CreateRefExpr(vd); numConv->expr->ty = &ty; numConv->ty = &ty; return numConv; } OwnedPtr CreateMainInvokeDecl(OwnedPtr&& funcBody, const FuncDecl& mainFunc, Ty& funcTy) { auto mainInvokeFunc = MakeOwnedNode(); funcBody->funcDecl = mainInvokeFunc.get(); mainInvokeFunc->toBeCompiled = mainFunc.toBeCompiled; mainInvokeFunc->ty = &funcTy; mainInvokeFunc->identifier = MAIN_INVOKE; mainInvokeFunc->funcBody = std::move(funcBody); mainInvokeFunc->moduleName = mainFunc.moduleName; mainInvokeFunc->fullPackageName = mainFunc.fullPackageName; mainInvokeFunc->EnableAttr(Attribute::GLOBAL); AddCurFile(*mainInvokeFunc, mainFunc.curFile); if (mainFunc.TestAttr(Attribute::INCRE_COMPILE)) { mainInvokeFunc->EnableAttr(Attribute::INCRE_COMPILE); } return mainInvokeFunc; } /** * Non-primitive type property compound assignment will be desugared for operator overloading first. * eg: a.b += c where b is Non-primitive type is desugared to a.b = a.b.+(c) * And the 'a.b' part of 'a.b.+' should already have 'mapExpr'. * Above action is done before desugar of property accessing. * So we add 'mapExpr' to the base of property accessing for compound assignment of property in this function. */ void TrySetMappedExprForProp(Expr& expr, Ptr mapExpr) { if (expr.astKind != ASTKind::CALL_EXPR) { return; } auto ce = StaticAs(&expr); // Checking for 'mapExpr' on the 'a.b' of 'a.b.+(c)'. if (auto ma = DynamicCast(ce->baseFunc.get()); ma && ma->baseExpr && ma->baseExpr->mapExpr) { // Adding 'mapExpr' on the 'a' of 'a.b'. if (auto baseMa = DynamicCast(ma->baseExpr.get()); baseMa && baseMa->baseExpr) { baseMa->baseExpr->mapExpr = mapExpr; ma->baseExpr->mapExpr = nullptr; // Reset old 'mapExpr' } } } std::vector> GetFuncArgsForDesugaredPropDecl( TypeManager& tyMgr, Expr& expr, Ty& propTy, Ptr mapExpr, FuncDecl& getFunc) { std::vector> args; TokenKind op = TokenKind::ADD; OwnedPtr rightExpr; if (expr.astKind == ASTKind::ASSIGN_EXPR) { auto& ae = static_cast(expr); auto argExpr = ASTCloner::Clone(ae.rightExpr.get()); // Compound Assignment Operator if (ae.isCompound) { if (COMPOUND_ASSIGN_EXPR_MAP.find(ae.op) != COMPOUND_ASSIGN_EXPR_MAP.end()) { op = COMPOUND_ASSIGN_EXPR_MAP.at(ae.op); } rightExpr = std::move(argExpr); } else { // If the assignment is not compund, try to set 'mapExpr' on the right part and return it as argument. TrySetMappedExprForProp(*argExpr, mapExpr); args.emplace_back(CreateFuncArg(std::move(argExpr))); return args; } } else if (expr.astKind == ASTKind::INC_OR_DEC_EXPR) { // IncOrDecExpr only support primitive types, so this will never have operator overloading. auto& ide = static_cast(expr); op = ide.op == TokenKind::INCR ? TokenKind::ADD : TokenKind::SUB; rightExpr = CreateLitConstExpr(LitConstKind::INTEGER, "1", &propTy); } else { // Caller guarantees the expr's kind only be ASSIGN_EXPR or INC_OR_DEC_EXPR. CJC_ABORT(); } // Create argument part as "expr.propGet() + value". // NOTE: the expression here must not have operator overloading with binary operator // since that case has already desugared during typechecking. // For memberAccess case, the baseExpr of memberAccess should be mapped to one expression to avoid side effect. auto leftBaseExpr = mapExpr ? CreateMemberAccess(ASTCloner::Clone(mapExpr), getFunc) : OwnedPtr(CreateRefExpr(getFunc).release()); // 'getFunc' may be generic type, which need to be update to real used type. leftBaseExpr->ty = tyMgr.GetFunctionTy({}, &propTy); auto isMemberAccessSuperCall = false; if (auto ma = DynamicCast(leftBaseExpr.get()); ma) { CJC_NULLPTR_CHECK(ma->baseExpr); if (auto ref = DynamicCast(ma->baseExpr.get()); ref) { isMemberAccessSuperCall = ref->isSuper; } if (auto target = ma->baseExpr->GetTarget(); !target || !target->IsNominalDecl()) { ma->baseExpr->mapExpr = mapExpr; } } CopyBasicInfo(&expr, leftBaseExpr.get()); auto leftExpr = CreateCallExpr(std::move(leftBaseExpr), {}); leftExpr->callKind = isMemberAccessSuperCall ? CallKind::CALL_SUPER_FUNCTION : CallKind::CALL_DECLARED_FUNCTION; leftExpr->resolvedFunction = &getFunc; leftExpr->ty = &propTy; auto binaryExpr = CreateBinaryExpr(std::move(leftExpr), std::move(rightExpr), op); CopyBasicInfo(&expr, binaryExpr.get()); args.emplace_back(CreateFuncArg(std::move(binaryExpr))); return args; } /** * Desugar of propDecl get is happened after sema well-typed. */ void DesugarGetForPropDecl(TypeManager& tyMgr, Expr& expr) { // 1. Do not desugar for expression which already has desugared result. // 2. Do not desugar as property get for left value. // 3. Do not desugar for unreadchable node. // 4. Do not desugar for non-reference node. bool ignored = expr.desugarExpr || !Ty::IsTyCorrect(expr.ty) || expr.TestAttr(Attribute::LEFT_VALUE) || expr.TestAttr(Attribute::UNREACHABLE) || !expr.IsReferenceExpr(); if (ignored) { return; } auto target = expr.GetTarget(); if (!target || target->astKind != ASTKind::PROP_DECL) { return; } auto propDecl = StaticAs(target); auto getFunc = GetUsedMemberDecl(*propDecl, true); if (getFunc->astKind != ASTKind::FUNC_DECL) { return; } OwnedPtr baseExpr; bool isMemberAccessSuperCall = false; if (auto ma = DynamicCast(&expr); ma) { if (!ma->baseExpr) { return; // Current node may be desugared by other process. } if (auto ref = DynamicCast(ma->baseExpr.get()); ref) { isMemberAccessSuperCall = ref->isSuper; } baseExpr = CreateMemberAccess(ASTCloner::Clone(static_cast(expr).baseExpr.get()), *getFunc); } else { baseExpr = CreateRefExpr(*getFunc); } // 'getFunc' may be generic type, which need to be update to real used type. baseExpr->ty = tyMgr.GetFunctionTy({}, expr.ty); CopyBasicInfo(&expr, baseExpr.get()); auto lastCallExpr = CreateCallExpr(std::move(baseExpr), {}); lastCallExpr->callKind = isMemberAccessSuperCall ? CallKind::CALL_SUPER_FUNCTION : CallKind::CALL_DECLARED_FUNCTION; lastCallExpr->resolvedFunction = StaticCast(getFunc); lastCallExpr->ty = expr.ty; CopyBasicInfo(&expr, lastCallExpr.get()); AddCurFile(*lastCallExpr, expr.curFile); expr.desugarExpr = std::move(lastCallExpr); } /** * Desugar of propDecl set is happened after sema well-typed. */ void DesugarSetForPropDecl(TypeManager& tyMgr, Expr& expr) { if (expr.desugarExpr || !Ty::IsTyCorrect(expr.ty)) { return; } Ptr subExpr = nullptr; bool isCompound = false; if (expr.astKind == AST::ASTKind::INC_OR_DEC_EXPR) { subExpr = static_cast(expr).expr.get(); isCompound = true; } else if (expr.astKind == AST::ASTKind::ASSIGN_EXPR) { subExpr = static_cast(expr).leftValue.get(); // If desugar of compound assignExpr was performed during typecheck, // we need to move 'SIDE_EFFECT' from assignExpr to new desugared 'CallExpr'. // eg: a.v += "s" ====> a.v = a.v + "s" ===> a.v$set(a.v$get().+("s")) isCompound = static_cast(expr).isCompound || expr.TestAttr(Attribute::SIDE_EFFECT); expr.DisableAttr(Attribute::SIDE_EFFECT); } if (!subExpr || !Ty::IsTyCorrect(subExpr->ty)) { return; } auto propDecl = DynamicCast(subExpr->GetTarget()); if (propDecl == nullptr || !propDecl->isVar) { return; } auto [getFunc, setFunc] = GetUsableGetterSetterForProperty(*propDecl); if (!getFunc || !setFunc) { return; } // Original 'subExpr' should not be checked for further. subExpr->EnableAttr(Attribute::UNREACHABLE); // Since current desugar happens inside sema stage, we cannot use 'move' to create new node. auto baseExpr = subExpr->astKind == ASTKind::MEMBER_ACCESS ? CreateMemberAccess(ASTCloner::Clone(RawStaticCast(subExpr)->baseExpr.get()), *setFunc) : OwnedPtr(CreateRefExpr(*setFunc).release()); // 'setFunc' may be generic type, which need to be update to real used type. baseExpr->ty = tyMgr.GetFunctionTy({subExpr->ty}, TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)); CopyBasicInfo(subExpr, baseExpr.get()); Ptr basePtr = nullptr; // For refExpr case, 'a += b' is desugared to aSet(aGet() + b), there is no side effect to be handle. // For memberAccess case, the baseExpr of memberAccess should be mapped to one expression to avoid side effect. // eg: a.b += c ===> a.bSet(a.bGet() + c) // the 'a' part should only be generated once. This is guaranteed by setting both 'a' 's 'mapExpr' to same 'a', // then CHIR will only generate 'a' once. bool isMemberAccessSuperCall = false; if (auto ma = DynamicCast(baseExpr.get())) { CJC_NULLPTR_CHECK(ma->baseExpr); basePtr = ma->baseExpr.get(); if (auto ref = DynamicCast(basePtr); ref) { isMemberAccessSuperCall = ref->isSuper; } if (auto target = basePtr->GetTarget(); !target || !target->IsNominalDecl()) { ma->baseExpr->mapExpr = basePtr; } } auto args = GetFuncArgsForDesugaredPropDecl(tyMgr, expr, *subExpr->ty, basePtr, *getFunc); auto lastCallExpr = CreateCallExpr(std::move(baseExpr), std::move(args)); CopyBasicInfo(subExpr, lastCallExpr.get()); lastCallExpr->callKind = isMemberAccessSuperCall ? CallKind::CALL_SUPER_FUNCTION : CallKind::CALL_DECLARED_FUNCTION; lastCallExpr->resolvedFunction = setFunc; // Setter of propDecl must be unit type. lastCallExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); AddCurFile(*lastCallExpr, expr.curFile); if (isCompound) { lastCallExpr->EnableAttr(Attribute::SIDE_EFFECT); } expr.desugarExpr = std::move(lastCallExpr); } } // namespace void TypeChecker::PerformDesugarAfterSema(const std::vector>& pkgs) const { impl->PerformDesugarAfterSema(pkgs); } /** @p pkgs is set of source packages, desugar only needs to be done on source package. */ void TypeChecker::TypeCheckerImpl::PerformDesugarAfterSema(const std::vector>& pkgs) { TyVarScope ts(typeManager); for (auto& pkg : pkgs) { PerformDesugarAfterTypeCheck(*ci->pkgCtxMap[pkg], *pkg); TryDesugarForCoalescing(*pkg); AutoBoxing autoBox(typeManager); autoBox.AddOptionBox(*pkg); } if (ci->invocation.globalOptions.enIncrementalCompilation) { ci->CacheSemaUsage(GetSemanticUsage(typeManager, pkgs)); } GenerateMainInvoke(); // Inline checking needs to process source package and imported packages which has source imported decls. CheckInlineFunctions(ci->GetPackages()); // Since the inlined function will affect decls' visibility, analyze linkage after inline decl checking. for (auto& pkg : pkgs) { AnalyzeFunctionLinkage(*pkg); } // If compiled with the `-g` or '--coverage', files should be saved with absolute paths. // When compiling stdlib without options '--coverage', do not save file with abs path. // Then can not debugging stdlib with abs path. bool saveFileWithAbsPath = ci->invocation.globalOptions.enableCompileDebug || ci->invocation.globalOptions.enableCoverage; for (auto& pkg : pkgs) { bool saveAbsPath = STANDARD_LIBS.find(pkg->fullPackageName) != STANDARD_LIBS.end() ? ci->invocation.globalOptions.enableCoverage : saveFileWithAbsPath; importManager.ExportDeclsWithContent(saveAbsPath, *pkg); } } void TypeChecker::TypeCheckerImpl::GenerateMainInvoke() { if (mainFunctionMap.size() != 1 || mainFunctionMap.begin()->second.size() != 1) { return; } // 'main' must be checked before insert to 'mainFunctionMap' in the function 'CheckEntryFunc'. // Get the package that contains main function. If the package is imported, get one from source packages. auto mainFunc = RawStaticCast(*mainFunctionMap.begin()->second.begin()); CJC_ASSERT(mainFunc && mainFunc->curFile && mainFunc->curFile->curPackage); auto packageHasMain = mainFunc->curFile->curPackage; auto funcTy = DynamicCast(mainFunc->ty); // Do not generate mainInvoke if any of the following condition satisfies: // 1. target is a library, not an executable (-c or --output-type=staticlib or --output-type=dylib) // 2. main function is in imported package. (can be remove when strategy of main importation is updated). // 3. current process is in incremental compiling strategy // 4. main function is invalid. // 5. when compiling chir we only depend on whether we have main func or not bool outputCHIR = ci->invocation.globalOptions.outputMode == GlobalOptions::OutputMode::CHIR; if ((!ci->invocation.globalOptions.CompileExecutable() && !outputCHIR) || packageHasMain->TestAttr(Attribute::IMPORTED) || ci->compileStrategy->type == StrategyType::INCREMENTAL_COMPILE || !funcTy || !Ty::IsTyCorrect(funcTy->retTy) || !mainFunc->funcBody || mainFunc->funcBody->paramLists.empty()) { return; } auto mainTy = funcTy->retTy; auto int64Ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64); auto mainInvokeTy = typeManager.GetFunctionTy(funcTy->paramTys, int64Ty); // Creat mainInvoke function: "func $mainInvoke(v:Array)" or "func $mainInvoke()". OwnedPtr funcBody = MakeOwnedNode(); funcBody->ty = mainInvokeTy; funcBody->body = MakeOwnedNode(); funcBody->body->ty = mainInvokeTy; auto funcParamList = MakeOwnedNode(); Ptr argPtr = nullptr; if (!mainFunc->funcBody->paramLists[0]->params.empty()) { auto param = CreateFuncParam("v", ASTCloner::Clone(mainFunc->funcBody->paramLists[0]->params[0]->type.get())); argPtr = param.get(); funcParamList->params.emplace_back(std::move(param)); } funcBody->paramLists.push_back(std::move(funcParamList)); // Creat "retVal = main(v) or main()" std::vector> args; if (argPtr) { args.emplace_back(CreateFuncArg(CreateRefExpr(*argPtr), "", argPtr->ty)); } auto callMain = CreateCallExpr(CreateRefExpr(*mainFunc), std::move(args), mainFunc, mainTy); auto retVal = CreateVarDecl("retVal", std::move(callMain)); // Create "return Int64(retVal)" or "return 0". OwnedPtr retExpr = mainTy->IsInteger() ? CreateReturnExpr(CreateConvInt64(*retVal, *int64Ty), funcBody.get()) : CreateReturnExpr(CreateLitConstExpr(LitConstKind::INTEGER, "0", int64Ty), funcBody.get()); retExpr->ty = TypeManager::GetNothingTy(); funcBody->body->body.emplace_back(std::move(retVal)); funcBody->body->body.emplace_back(std::move(retExpr)); mainFunc->curFile->decls.emplace_back(CreateMainInvokeDecl(std::move(funcBody), *mainFunc, *mainInvokeTy)); } // Perform desugar after typecheck before generic instantiation. void TypeChecker::TypeCheckerImpl::DesugarForPropDecl(Node& pkg) { Walker(&pkg, [this](Ptr node) -> VisitAction { if (node->TestAnyAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN)) { return VisitAction::SKIP_CHILDREN; } switch (node->astKind) { case ASTKind::REF_EXPR: case ASTKind::MEMBER_ACCESS: DesugarGetForPropDecl(typeManager, *StaticAs(node)); break; case ASTKind::ASSIGN_EXPR: case ASTKind::INC_OR_DEC_EXPR: DesugarSetForPropDecl(typeManager, *StaticAs(node)); break; default: break; } return VisitAction::WALK_CHILDREN; }).Walk(); } // Perform desugar after typecheck before generic instantiation. void TypeChecker::TypeCheckerImpl::PerformDesugarAfterTypeCheck(ASTContext& ctx, Package& pkg) { Interop::Java::JavaInteropManager jim(importManager, typeManager, diag, *ci->mangler, ci->invocation.globalOptions.outputJavaGenDir, ci->invocation.globalOptions.output, ci->invocation.globalOptions.enableInteropCJMapping); jim.CheckImplRedefinition(pkg); for (auto& file : pkg.files) { jim.CheckTypes(*file); } jim.DesugarPackage(pkg); Interop::ObjC::Desugar(Interop::ObjC::InteropContext(pkg, typeManager, importManager, diag, *ci->mangler, ci->invocation.globalOptions.output)); DesugarDeclsForPackage(pkg, ci->invocation.globalOptions.enableCoverage); std::function)> preVisit = [this, &ctx](Ptr node) -> VisitAction { switch (node->astKind) { case ASTKind::FOR_IN_EXPR: { auto fie = StaticAs(node); if (fie->inExpression) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND ReArrangeForInExpr(ctx, *fie); #endif } break; } case ASTKind::LIT_CONST_EXPR: { auto lce = StaticAs(node); if (lce->siExpr && Ty::IsTyCorrect(lce->ty)) { DesugarStrInterpolationExpr(ctx, *lce); } break; } case ASTKind::FUNC_PARAM: PostProcessFuncParam(*StaticAs(node), ci->invocation.globalOptions); break; case ASTKind::RANGE_EXPR: DesugarRangeExpr(typeManager, *StaticAs(node)); break; case ASTKind::CALL_EXPR: DesugarIntrinsicCallExpr(*StaticAs(node)); DesugarTokenCallExpr(ctx, *StaticAs(node)); break; case ASTKind::BINARY_EXPR: DesugarBinaryExpr(StaticCast(*node)); break; case ASTKind::IS_EXPR: DesugarIsExpr(typeManager, *StaticAs(node)); break; case ASTKind::AS_EXPR: DesugarAsExpr(typeManager, *StaticAs(node)); break; case ASTKind::ARRAY_LIT: AddArrayLitConstructor(*StaticAs(node)); break; case ASTKind::IF_EXPR: DesugarIfExpr(typeManager, *StaticAs(node)); break; case ASTKind::SPAWN_EXPR: DesugarSpawnExpr(ctx, *StaticAs(node)); break; case ASTKind::TRY_EXPR: DesugarTryWithResourcesExpr(ctx, *StaticCast(node)); DesugarTryToFrame(ctx, *StaticCast(node)); break; case ASTKind::PERFORM_EXPR: DesugarPerform(ctx, *StaticCast(node)); break; case ASTKind::RESUME_EXPR: DesugarResume(ctx, *StaticCast(node)); break; default: break; } return VisitAction::WALK_CHILDREN; }; Walker(&pkg, preVisit).Walk(); } Ptr TypeChecker::TypeCheckerImpl::SynthesizeWithoutRecover(ASTContext& ctx, Ptr node) { CJC_NULLPTR_CHECK(node); ctx.ClearTypeCheckCache(*node); // ensure newly created nodes have no related cache ctx.SkipSynForCorrectTyRec(*node); // avoid possible recovery during synthesize for already checked node auto ret = Synthesize(ctx, node); DesugarForPropDecl(*node); // seems that the above still aren't enough... return ret; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // public func toAny(): Any { return this } OwnedPtr TypeChecker::TypeCheckerImpl::CreateToAny(AST::Decl& outerDecl) { OwnedPtr funcBody = MakeOwned(); funcBody->body = MakeOwned(); funcBody->body->ty = typeManager.GetAnyTy(); auto anyDecl = Ty::GetDeclPtrOfTy(typeManager.GetAnyTy()); CJC_NULLPTR_CHECK(anyDecl); funcBody->retType = CreateRefType(*anyDecl); funcBody->ty = typeManager.GetFunctionTy({}, typeManager.GetAnyTy()); auto fromTy = outerDecl.ty; auto funcParamList = MakeOwned(); funcBody->paramLists.push_back(std::move(funcParamList)); auto xRef = CreateRefExpr("this"); xRef->ref.target = &outerDecl; xRef->ty = fromTy; xRef->EnableAttr(Attribute::NO_REFLECT_INFO); auto returnExpr = CreateReturnExpr(std::move(xRef), funcBody.get()); returnExpr->ty = TypeManager::GetNothingTy(); funcBody->body->body.push_back(std::move(returnExpr)); auto toAnyFunc = CreateFuncDecl(std::string(TO_ANY), std::move(funcBody)); toAnyFunc->fullPackageName = outerDecl.fullPackageName; toAnyFunc->EnableAttr(Attribute::COMPILER_ADD, Attribute::PUBLIC); toAnyFunc->outerDecl = &outerDecl; if (outerDecl.TestAttr(Attribute::GENERIC_INSTANTIATED)) { toAnyFunc->linkage = Linkage::INTERNAL; } else { toAnyFunc->linkage = Linkage::WEAK_ODR; } toAnyFunc->doNotExport = true; AddCurFile(*toAnyFunc, outerDecl.curFile); return toAnyFunc; } void TypeChecker::TypeCheckerImpl::PerformToAnyInsertion(AST::Package& pkg) { if (ci->invocation.globalOptions.disableInstantiation) { return; } auto addToAny = [this](const OwnedPtr& decl) { if (decl->astKind == ASTKind::STRUCT_DECL && !decl->TestAttr(Attribute::GENERIC)) { auto sd = RawStaticCast(decl.get()); auto fd = CreateToAny(*sd); fd->EnableAttr(Attribute::IN_STRUCT); fd->funcBody->parentStruct = sd; fd->toBeCompiled = sd->toBeCompiled; (void)sd->body->decls.emplace_back(std::move(fd)); } }; IterateToplevelDecls(pkg, addToAny); (void)std::for_each(pkg.genericInstantiatedDecls.begin(), pkg.genericInstantiatedDecls.end(), [&addToAny](auto& it) { addToAny(it); }); } #endif cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck.h000066400000000000000000000124531510705540100230740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares private functions of desugar after type check. */ #ifndef CANGJIE_SEMA_DESUGAR_AFTER_TYPECHECK_H #define CANGJIE_SEMA_DESUGAR_AFTER_TYPECHECK_H #include "cangjie/AST/Node.h" #include "cangjie/IncrementalCompilation/IncrementalScopeAnalysis.h" #include "cangjie/Sema/TypeManager.h" namespace Cangjie::Sema::Desugar::AfterTypeCheck { using namespace Cangjie; using namespace AST; constexpr int8_t G_TOKEN_ARG_NUM = 2; constexpr int8_t G_DIAG_REPORT_ARG_NUM = 4; static const std::unordered_map semaCoreIntrinsicMap = { {"Int64Less", TokenKind::LT}, {"Int64Greater", TokenKind::GT}, {"Int64LessOrEqual", TokenKind::LE}, {"Int64GreaterOrEqual", TokenKind::GE}, {"Int64Equal", TokenKind::EQUAL}, {"Int64NotEqual", TokenKind::NOTEQ}, {"Int32Less", TokenKind::LT}, {"Int32Greater", TokenKind::GT}, {"Int32LessOrEqual", TokenKind::LE}, {"Int32GreaterOrEqual", TokenKind::GE}, {"Int32Equal", TokenKind::EQUAL}, {"Int32NotEqual", TokenKind::NOTEQ}, {"Int16Less", TokenKind::LT}, {"Int16Greater", TokenKind::GT}, {"Int16LessOrEqual", TokenKind::LE}, {"Int16GreaterOrEqual", TokenKind::GE}, {"Int16Equal", TokenKind::EQUAL}, {"Int16NotEqual", TokenKind::NOTEQ}, {"Int8Less", TokenKind::LT}, {"Int8Greater", TokenKind::GT}, {"Int8LessOrEqual", TokenKind::LE}, {"Int8GreaterOrEqual", TokenKind::GE}, {"Int8Equal", TokenKind::EQUAL}, {"Int8NotEqual", TokenKind::NOTEQ}, {"UInt64Less", TokenKind::LT}, {"UInt64Greater", TokenKind::GT}, {"UInt64LessOrEqual", TokenKind::LE}, {"UInt64GreaterOrEqual", TokenKind::GE}, {"UInt64Equal", TokenKind::EQUAL}, {"UInt64NotEqual", TokenKind::NOTEQ}, {"UInt32Less", TokenKind::LT}, {"UInt32Greater", TokenKind::GT}, {"UInt32LessOrEqual", TokenKind::LE}, {"UInt32GreaterOrEqual", TokenKind::GE}, {"UInt32Equal", TokenKind::EQUAL}, {"UInt32NotEqual", TokenKind::NOTEQ}, {"UInt16Less", TokenKind::LT}, {"UInt16Greater", TokenKind::GT}, {"UInt16LessOrEqual", TokenKind::LE}, {"UInt16GreaterOrEqual", TokenKind::GE}, {"UInt16Equal", TokenKind::EQUAL}, {"UInt16NotEqual", TokenKind::NOTEQ}, {"UInt8Less", TokenKind::LT}, {"UInt8Greater", TokenKind::GT}, {"UInt8LessOrEqual", TokenKind::LE}, {"UInt8GreaterOrEqual", TokenKind::GE}, {"UInt8Equal", TokenKind::EQUAL}, {"UInt8NotEqual", TokenKind::NOTEQ}, {"Float16Less", TokenKind::LT}, {"Float16Greater", TokenKind::GT}, {"Float16LessOrEqual", TokenKind::LE}, {"Float16GreaterOrEqual", TokenKind::GE}, {"Float16Equal", TokenKind::EQUAL}, {"Float16NotEqual", TokenKind::NOTEQ}, {"Float32Less", TokenKind::LT}, {"Float32Greater", TokenKind::GT}, {"Float32LessOrEqual", TokenKind::LE}, {"Float32GreaterOrEqual", TokenKind::GE}, {"Float32Equal", TokenKind::EQUAL}, {"Float32NotEqual", TokenKind::NOTEQ}, {"Float64Less", TokenKind::LT}, {"Float64Greater", TokenKind::GT}, {"Float64LessOrEqual", TokenKind::LE}, {"Float64GreaterOrEqual", TokenKind::GE}, {"Float64Equal", TokenKind::EQUAL}, {"Float64NotEqual", TokenKind::NOTEQ}, }; const static std::unordered_map> semaPackageMap = { {CORE_PACKAGE_NAME, semaCoreIntrinsicMap}, }; OwnedPtr CreateRuntimePreparedTypePattern( TypeManager& typeManager, OwnedPtr pattern, OwnedPtr type, Expr& selector); Ptr LookupEnumMember(Ptr decl, const std::string& identifier); void UnitifyBlock(const Expr& posSrc, Block& b, Ty& unitTy); void RearrangeRefLoop(const Expr& src, Expr& dst, Ptr loopBody); void PostProcessFuncParam(const FuncParam& fp, const GlobalOptions& options); void DesugarDeclsForPackage(Package& pkg, bool enableCoverage); void DesugarBinaryExpr(BinaryExpr& be); void DesugarIsExpr(TypeManager& typeManager, IsExpr& ie); void DesugarAsExpr(TypeManager& typeManager, AsExpr& ae); /// Insert Unit if needed. No desugaring requried for if-let expressions. void DesugarIfExpr(TypeManager& typeManager, IfExpr& ifExpr); void DesugarRangeExpr(TypeManager& typeManager, RangeExpr& re); void DesugarIntrinsicCallExpr(AST::CallExpr& expr); /** * Collect semantic usages for incremental complations. Performed before instantiation step. */ SemanticInfo GetSemanticUsage(TypeManager& typeManager, const std::vector>& pkgs); } // namespace Cangjie::Sema::Desugar::AfterTypeCheck #endif cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/000077500000000000000000000000001510705540100227165ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/AsExpr.cpp000066400000000000000000000073501510705540100246310ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Desugar/AfterTypeCheck.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Utils.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; namespace { OwnedPtr CreateAsExprSomeCall( FuncDecl& someDecl, Type& theAsType, VarDecl& varDecl, FuncTy& someCtorTy) { CJC_ASSERT(someCtorTy.paramTys.size() == 1); auto some = MakeOwnedNode(); auto someRef = CreateRefExpr(someDecl); auto theAsTy = someCtorTy.paramTys[0]; auto optionTy = someCtorTy.retTy; someRef->typeArguments.emplace_back(ASTCloner::Clone(Ptr(&theAsType))); someRef->instTys.emplace_back(theAsTy); someRef->ref.targets.emplace_back(&someDecl); someRef->isAlone = false; someRef->ty = &someCtorTy; someRef->callOrPattern = some.get(); auto newVarRef = CreateRefExpr(varDecl); newVarRef->ty = theAsTy; auto someArg = MakeOwnedNode(); someArg->expr = std::move(newVarRef); someArg->ty = theAsTy; some->baseFunc = std::move(someRef); some->args.emplace_back(std::move(someArg)); some->resolvedFunction = &someDecl; some->callKind = CallKind::CALL_DECLARED_FUNCTION; some->ty = optionTy; return some; } } // namespace namespace Cangjie::Sema::Desugar::AfterTypeCheck { /** * Desugar AsExpr to TypePattern of MatchExpr. * *************** before desugar **************** * e as T * *************** after desugar **************** * match (e) { * case newVar : T => Some(newVar) * case _ => None * } * */ void DesugarAsExpr(TypeManager& typeManager, AsExpr& ae) { if (!Ty::IsTyCorrect(ae.ty) || !ae.ty->IsCoreOptionType() || ae.desugarExpr) { return; } CJC_NULLPTR_CHECK(ae.leftExpr); CJC_NULLPTR_CHECK(ae.asType); CJC_NULLPTR_CHECK(ae.leftExpr->ty); CJC_NULLPTR_CHECK(ae.asType->ty); auto optionTy = ae.ty; auto selectorTy = ae.leftExpr->ty; auto theAsType = ASTCloner::Clone(ae.asType.get()); auto theAsTy = ae.asType->ty; auto optionDecl = StaticCast(optionTy)->decl; CJC_NULLPTR_CHECK(optionDecl); auto someDecl = StaticCast(LookupEnumMember(optionDecl, OPTION_VALUE_CTOR)); CJC_NULLPTR_CHECK(someDecl); std::vector> matchCases; auto varPattern = CreateVarPattern(V_COMPILER, theAsTy); varPattern->begin = ae.asPos; varPattern->end = ae.asPos; auto varDecl = varPattern->varDecl.get(); varDecl->fullPackageName = ae.GetFullPackageName(); matchCases.emplace_back( CreateMatchCase( CreateRuntimePreparedTypePattern(typeManager, std::move(varPattern), std::move(ae.asType), *ae.leftExpr), CreateAsExprSomeCall(*someDecl, *theAsType, *varDecl, *typeManager.GetFunctionTy({theAsTy}, optionTy)))); auto wildcard = MakeOwnedNode(); wildcard->ty = selectorTy; auto noneDecl = LookupEnumMember(optionDecl, OPTION_NONE_CTOR); CJC_NULLPTR_CHECK(noneDecl); auto none = CreateRefExpr(*noneDecl); CopyBasicInfo(&ae, none.get()); none->typeArguments.emplace_back(std::move(theAsType)); none->instTys.emplace_back(theAsTy); none->ty = optionTy; matchCases.emplace_back(CreateMatchCase(std::move(wildcard), std::move(none))); ae.desugarExpr = CreateMatchExpr(std::move(ae.leftExpr), std::move(matchCases), optionTy, Expr::SugarKind::AS); CopyBasicInfo(&ae, ae.desugarExpr.get()); AddCurFile(*ae.desugarExpr, ae.curFile); } } // namespace Cangjie::Sema::Desugar::AfterTypeCheck cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/BinaryExpr.cpp000066400000000000000000000040611510705540100255060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Desugar/AfterTypeCheck.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Utils.h" using namespace Cangjie; using namespace AST; namespace { /** * *************** before desugar **************** * e1 |> e2 * *************** after desugar **************** * e2(e1) * *************** after blockify **************** * { * let v = e1 * e2(v) * } */ void BlockifyFlowExpr(BinaryExpr& be) { CJC_ASSERT(be.op == TokenKind::PIPELINE && be.desugarExpr); // Get the inner most `desugarExpr`. Ptr innerCe = StaticCast(be.desugarExpr.get()); while (innerCe->desugarExpr) { innerCe = StaticCast(innerCe->desugarExpr.get()); } CJC_ASSERT(innerCe->args.size() == 1 && innerCe->args.front() != nullptr); // Create `let v = e1`. auto vd = CreateVarDecl(V_COMPILER, std::move(innerCe->args.front()->expr)); vd->fullPackageName = be.GetFullPackageName(); CopyBasicInfo(vd->initializer.get(), vd.get()); // Create the reference `v` and replace the argument with this `RefExpr`. auto re = CreateRefExpr(*vd); CopyBasicInfo(vd->initializer.get(), re.get()); innerCe->args.front()->expr = std::move(re); // Create the block. std::vector> nodes; nodes.emplace_back(std::move(vd)); nodes.emplace_back(std::move(be.desugarExpr)); auto block = CreateBlock(std::move(nodes), be.ty); CopyBasicInfo(&be, block.get()); AddCurFile(*block, be.curFile); be.desugarExpr = std::move(block); } } // namespace namespace Cangjie::Sema::Desugar::AfterTypeCheck { void DesugarBinaryExpr(BinaryExpr& be) { if (!Ty::IsTyCorrect(be.ty)) { return; } if (be.op == TokenKind::PIPELINE) { BlockifyFlowExpr(be); } } } // namespace Cangjie::Sema::Desugar::AfterTypeCheck cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/CMakeLists.txt000066400000000000000000000006121510705540100254550ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB SEMA_DESUGAR_AFTERTYPECHECK_SRC *.cpp) set(SEMA_DESUGAR_SRC ${SEMA_DESUGAR_SRC} ${SEMA_DESUGAR_AFTERTYPECHECK_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/CallExpr.cpp000066400000000000000000000106771510705540100251470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Desugar/AfterTypeCheck.h" #include "TypeCheckUtil.h" #include "TypeCheckerImpl.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Utils.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; using namespace Sema::Desugar::AfterTypeCheck; void TypeChecker::TypeCheckerImpl::DesugarTokenCallExpr(ASTContext& ctx, CallExpr& ce) { if (!Ty::IsTyCorrect(ce.ty) || ce.desugarExpr != nullptr || ce.sugarKind == Expr::SugarKind::TOKEN_CALL) { return; } if (!ce.baseFunc || ce.baseFunc->astKind != ASTKind::REF_EXPR) { return; } auto re = StaticCast(ce.baseFunc.get()); if (re->ref.identifier != "Token" || ce.args.empty() || ce.args.size() > G_TOKEN_ARG_NUM || re->ref.targets.empty() || re->ref.targets[0]->fullPackageName != "std.ast") { // If Token() args num is greater than 2, the token already has a position. return; } std::vector> args; auto uint32Ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UINT32); auto int32Ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT32); auto fileID = CreateLitConstExpr(LitConstKind::INTEGER, std::to_string(ce.begin.fileID), uint32Ty); // the `sugarKind` is also cloned, to prevent infinite loop in Walker // COMPILE_ADD attribute is not sufficent in this scenario auto newCe = CreateCallExpr(std::move(ce.baseFunc), std::move(ce.args), std::move(ce.resolvedFunction), ce.ty, ce.callKind); newCe->sugarKind = Expr::SugarKind::TOKEN_CALL; args.emplace_back(CreateFuncArg(std::move(newCe))); args.emplace_back(CreateFuncArg(std::move(fileID))); auto line = CreateLitConstExpr(LitConstKind::INTEGER, std::to_string(ce.begin.line), int32Ty); args.emplace_back(CreateFuncArg(std::move(line))); auto column = CreateLitConstExpr(LitConstKind::INTEGER, std::to_string(ce.begin.column), int32Ty); args.emplace_back(CreateFuncArg(std::move(column))); auto refreshExpr = CreateRefExprInAST("refreshPos"); refreshExpr->begin = ce.begin; refreshExpr->end = ce.end; auto callExpr = CreateCallExpr(std::move(refreshExpr), std::move(args)); CopyBasicInfo(&ce, callExpr.get()); AddCurFile(*callExpr, ce.curFile); auto updateTy = SynthesizeWithoutRecover(ctx, callExpr.get()); // Need syn to desugar. CJC_ASSERT(updateTy && updateTy->kind != TypeKind::TYPE_INVALID); ce.desugarExpr = std::move(callExpr); } namespace Cangjie::Sema::Desugar::AfterTypeCheck { void DesugarComparableIntrinsic(AST::CallExpr& expr, TokenKind op) { CJC_ASSERT(expr.desugarExpr == nullptr && expr.args.size() == 2); // compare intrinsic has exactly 2 args auto binExpr = CreateBinaryExpr(std::move(expr.args[0]->expr), std::move(expr.args[1]->expr), op); binExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); CopyBasicInfo(&expr, binExpr); expr.desugarExpr = std::move(binExpr); } void DesugarIntrinsicCallExpr(AST::CallExpr& expr) { // Check whether the call is an intrinsic function call. if (expr.callKind != AST::CallKind::CALL_INTRINSIC_FUNCTION) { return; } auto target = expr.baseFunc->GetTarget(); CJC_NULLPTR_CHECK(target); // Obtains the packageName where the intrinsic function definition is located. std::string packageName{}; if (target->genericDecl) { packageName = target->genericDecl->fullPackageName; } else if (target->outerDecl && target->outerDecl->genericDecl) { packageName = target->outerDecl->genericDecl->fullPackageName; } else { packageName = target->fullPackageName; } // Find tokenkind type of the intrinsic function by package name and function name of the intrinsic function. std::string identfifier = target->identifier; TokenKind op = TokenKind::ILLEGAL; auto it = semaPackageMap.find(packageName); if (it == semaPackageMap.end() || it->second.find(identfifier) == it->second.end()) { return; } op = it->second.at(identfifier); CJC_ASSERT(op != TokenKind::ILLEGAL); // Desugar the intrinsic function call expr. eg: Float64Less(x, y) => x < y. DesugarComparableIntrinsic(expr, op); } } // namespace Cangjie::Sema::Desugar::AfterTypeCheckcangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/Coalescing.cpp000066400000000000000000000133331510705540100254740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Utils.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; namespace { OwnedPtr GetValueMatchCase( OwnedPtr ctor, OwnedPtr someExpr, RefExpr& someVar, Expr& selector) { auto valMatchCase = MakeOwnedNode(); CJC_ASSERT(selector.ty->IsCoreOptionType()); // Caller guarantees. auto innerTy = selector.ty->typeArgs[0]; CopyBasicInfo(&selector, valMatchCase.get()); // 'x' in 'Some(x)'. auto someArg = MakeOwned(someVar.ref.identifier, INVALID_POSITION); someArg->ty = innerTy; someArg->varDecl->ty = innerTy; someArg->EnableAttr(Attribute::COMPILER_ADD); // 'x' in '=> x'. someVar.ref.target = someArg->varDecl.get(); someVar.ty = someVar.ref.target->ty; // Enum pattern 'Some(x)'. auto enumPattern = MakeOwnedNode(); CopyBasicInfo(someExpr.get(), enumPattern.get()); enumPattern->constructor = std::move(ctor); enumPattern->patterns.emplace_back(std::move(someArg)); enumPattern->ty = selector.ty; // Case body of '=> x'. someExpr->ty = innerTy; someExpr->curFile = selector.curFile; // Entire case expression 'case Some(x) => x'. valMatchCase->patterns.emplace_back(std::move(enumPattern)); valMatchCase->SetCtxExprForPatterns(&selector); valMatchCase->patternGuard = nullptr; valMatchCase->exprOrDecls = std::move(someExpr); valMatchCase->ty = valMatchCase->exprOrDecls->ty; return valMatchCase; } } /** * Given selector A, SomeExpr B, OtherExpr C. Ref var x. Only support for 'Option'. * Construct as bellow, * match (A) { * case CTOR(x) => B * case _: => C * } * NOTE: this happens before generic instantiation. */ OwnedPtr TypeChecker::TypeCheckerImpl::ConstructOptionMatch(OwnedPtr selector, OwnedPtr someExpr, OwnedPtr otherExpr, RefExpr& someVar, Ptr someTy) const { Ptr ctorDecl = nullptr; // Caller guarantees seletor is enum option type. auto enumTy = StaticCast(selector->ty); for (auto& it : enumTy->declPtr->constructors) { if (it->identifier == OPTION_VALUE_CTOR) { ctorDecl = StaticCast(it.get()); break; } } if (ctorDecl == nullptr) { return nullptr; } auto matchExpr = MakeOwnedNode(); matchExpr->matchMode = true; matchExpr->sugarKind = Expr::SugarKind::QUEST; matchExpr->selector = std::move(selector); auto valueRef = CreateRefExpr({OPTION_VALUE_CTOR, DEFAULT_POSITION, DEFAULT_POSITION, false}, someTy); valueRef->ref.target = ctorDecl; auto valMatchCase = GetValueMatchCase(std::move(valueRef), std::move(someExpr), someVar, *matchExpr->selector); matchExpr->matchCases.emplace_back(std::move(valMatchCase)); // Wild case body 'case _ => expr. otherExpr->curFile = matchExpr->selector->curFile; auto wildMatchCase = MakeOwnedNode(); wildMatchCase->patterns.emplace_back(MakeOwnedNode()); wildMatchCase->SetCtxExprForPatterns(matchExpr->selector.get()); wildMatchCase->patternGuard = nullptr; wildMatchCase->exprOrDecls = std::move(otherExpr); wildMatchCase->ty = wildMatchCase->exprOrDecls->ty; CopyBasicInfo(matchExpr->selector.get(), wildMatchCase.get()); matchExpr->matchCases.emplace_back(std::move(wildMatchCase)); return matchExpr; } /** * Desugar for Binary expression for ??(coalescing). * Only support 'Option' in core package. * *************** before desugar **************** * var option = Option.Some(1) * var val0 : Int32 = option ?? 11 * *************** after desugar ***************** * var option = Option.Some(1) * var val0 : Int32 = match (option) { * case Some(x) => x * case $None => 11 * } */ void TypeChecker::TypeCheckerImpl::DesugarForCoalescing(BinaryExpr& binaryExpr) const { // Caller guarantees the 'binaryExpr.desugarExpr' is not existed. CJC_ASSERT(binaryExpr.rightExpr && binaryExpr.leftExpr); auto leftTy = binaryExpr.leftExpr->ty; if (!leftTy->IsCoreOptionType()) { return; } // Case body of 'Some(x) => x'. auto expr = CreateRefExpr("x"); auto& refExpr = *expr; auto caseBody = MakeOwnedNode(); caseBody->body.emplace_back(std::move(expr)); // Case body 'case _ => rightExpr of binaryExpr' auto wildBody = MakeOwnedNode(); auto rightTy = binaryExpr.rightExpr->ty; (void)wildBody->body.emplace_back(std::move(binaryExpr.rightExpr)); wildBody->ty = rightTy; auto someTy = typeManager.GetFunctionTy({leftTy->typeArgs[0]}, leftTy); auto desugarExpr = ConstructOptionMatch( std::move(binaryExpr.leftExpr), std::move(caseBody), std::move(wildBody), refExpr, someTy); if (desugarExpr != nullptr) { desugarExpr->ty = binaryExpr.ty; binaryExpr.desugarExpr = std::move(desugarExpr); AddCurFile(*binaryExpr.desugarExpr, binaryExpr.curFile); } } void TypeChecker::TypeCheckerImpl::TryDesugarForCoalescing(Node& root) const { std::function)> visitBe = [this](Ptr node) -> VisitAction { if (auto be = DynamicCast(node); be && be->op == TokenKind::COALESCING && !be->desugarExpr) { DesugarForCoalescing(*be); } return VisitAction::WALK_CHILDREN; }; Walker walker(&root, visitBe); walker.Walk(); } cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/Create.cpp000066400000000000000000000020211510705540100246200ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Desugar/AfterTypeCheck.h" #include "cangjie/AST/Create.h" #include "TypeCheckUtil.h" using namespace Cangjie; using namespace TypeCheckUtil; namespace Cangjie::Sema::Desugar::AfterTypeCheck { OwnedPtr CreateRuntimePreparedTypePattern( TypeManager& typeManager, OwnedPtr pattern, OwnedPtr type, Expr& selector) { auto typePattern = CreateTypePattern(std::move(pattern), std::move(type), selector); typePattern->matchBeforeRuntime = typeManager.IsSubtype(selector.ty, typePattern->ty, true, false); typePattern->needRuntimeTypeCheck = !typePattern->matchBeforeRuntime && IsNeedRuntimeCheck(typeManager, *selector.ty, *typePattern->ty); return typePattern; } } // namespace Cangjie::Sema::Desugar::AfterTypeCheck cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/EffectHandlers.cpp000066400000000000000000001353731510705540100263130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "Desugar/AfterTypeCheck.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Utils.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; using namespace Sema::Desugar::AfterTypeCheck; namespace { // Create `stdx.effect.ImmediateHandlerReturn.Result` from the type // (Any) -> ImmediateHandlerReturn OwnedPtr CreateRefValue(FuncTy& funcTy) { CJC_ASSERT(Ty::IsTyCorrect(funcTy.retTy) && funcTy.retTy->IsEnum()); EnumTy& enumTy = StaticCast(*funcTy.retTy); auto decl = LookupEnumMember(enumTy.declPtr, "Result"); CJC_NULLPTR_CHECK(decl); auto refExpr = CreateRefExpr(*decl); (void)refExpr->ref.targets.emplace_back(decl); // for `GetFuncTargets` refExpr->ty = &funcTy; refExpr->instTys = enumTy.typeArgs; return refExpr; } // Create `stdx.effect.ImmediateHandlerReturn.Exc` from the type // (Exception) -> ImmediateHandlerReturn OwnedPtr CreateRefExc(FuncTy& funcTy) { CJC_ASSERT(Ty::IsTyCorrect(funcTy.retTy) && funcTy.retTy->IsEnum()); EnumTy& enumTy = StaticCast(*funcTy.retTy); auto decl = LookupEnumMember(enumTy.declPtr, "Exc"); CJC_NULLPTR_CHECK(decl); auto refExpr = CreateRefExpr(*decl); (void)refExpr->ref.targets.emplace_back(decl); // for `GetFuncTargets` refExpr->ty = &funcTy; refExpr->instTys = enumTy.typeArgs; return refExpr; } // Create `stdx.effect.ImmediateHandlerReturn.Err` from the type // (Error) -> ImmediateHandlerReturn OwnedPtr CreateRefErr(FuncTy& funcTy) { CJC_ASSERT(Ty::IsTyCorrect(funcTy.retTy) && funcTy.retTy->IsEnum()); EnumTy& enumTy = StaticCast(*funcTy.retTy); auto decl = LookupEnumMember(enumTy.declPtr, "Err"); CJC_NULLPTR_CHECK(decl); auto refExpr = CreateRefExpr(*decl); (void)refExpr->ref.targets.emplace_back(decl); // for `GetFuncTargets` refExpr->ty = &funcTy; refExpr->instTys = enumTy.typeArgs; return refExpr; } /** * Get the declaration of the default initializer of a ClassDecl * * Note: It's assumed there's only one initializer without arguments */ Ptr GetDefaultInitDecl(ClassDecl& classDecl) { std::vector> inits; for (auto& decl : classDecl.GetMemberDecls()) { CJC_NULLPTR_CHECK(decl); if (decl->astKind == ASTKind::FUNC_DECL && decl.get()->TestAttr(Attribute::CONSTRUCTOR)) { auto funcDecl = StaticCast(decl.get()); if (funcDecl->funcBody->paramLists.size() == 1 && funcDecl->funcBody->paramLists[0]->params.size() == 0) { inits.emplace_back(funcDecl); } } } CJC_ASSERT(inits.size() == 1); // `init(fn: ()->T)` is the only constructor return inits.front(); } } // namespace /** * Enclose the tryLambda of a try-handle expression to handle some special * exceptions. * * From: * { () => } * * To: * { () => * try { * * } catch (e: ImmediateFrameExceptionWrapper) { * if (HandlerFrame.getActiveFrame() == v.frame) { * throw v.exception * } else { * throw v * } * } catch (e: ImmediateFrameErrorWrapper) { * if (HandlerFrame.getActiveFrame() == v.frame) { * throw v.error * } else { * throw v * } * } catch (e: ImmediateEarlyReturn) { * if (HandlerFrame.getActiveFrame() == e.frame) { * // This is similar to a desugared `(e.result as T).getOrThrow` * match (e.result) { * case newVar : T => return newVar * case _ => throw Exception() * } * } else { * throw e * } * } * } */ void TypeChecker::TypeCheckerImpl::EncloseTryLambda(ASTContext& ctx, OwnedPtr& tryLambda) { OwnedPtr innerBlock = std::move(tryLambda->funcBody->body); // `try { ... } auto tryExpr = MakeOwnedNode(); CopyBasicInfo(innerBlock, tryExpr); tryExpr->tryBlock = std::move(innerBlock); tryExpr->ty = tryLambda->funcBody->retType->ty.get(); // Import declarations and types // stdx.effect.ImmediateFrameExceptionWrapper auto exceptionWrapperDecl = importManager.GetImportedDecl(EFFECT_INTERNALS_PACKAGE_NAME, CLASS_FRAME_EXCEPTION_WRAPPER); CJC_NULLPTR_CHECK(exceptionWrapperDecl); auto exceptionWrapperTy = typeManager.GetClassTy(*exceptionWrapperDecl, {}); // stdx.effect.ImmediateFrameErrorWrapper auto errorWrapperDecl = importManager.GetImportedDecl(EFFECT_INTERNALS_PACKAGE_NAME, CLASS_FRAME_ERROR_WRAPPER); CJC_NULLPTR_CHECK(errorWrapperDecl); auto errorWrapperTy = typeManager.GetClassTy(*errorWrapperDecl, {}); // stdx.effect.ImmediateEarlyReturn auto earlyReturnDecl = importManager.GetImportedDecl(EFFECT_INTERNALS_PACKAGE_NAME, CLASS_EARLY_RETURN); CJC_NULLPTR_CHECK(earlyReturnDecl); auto earlyReturnTy = typeManager.GetClassTy(*earlyReturnDecl, {}); { // (v: ImmediateFrameExceptionWrapper) auto vp = CreateVarPattern(V_COMPILER, exceptionWrapperTy); AST::CopyNodeScopeInfo(vp->varDecl, tryExpr); // v.frame auto vFrameRef = CreateRefExpr(*vp->varDecl); AST::CopyNodeScopeInfo(vFrameRef, tryExpr.get()); auto frameAccess = CreateMemberAccess(std::move(vFrameRef), "frame"); // HandlerFrame.getActiveFrame().getOrThrow().parent auto getActiveFrame = GetHelperFrameMethod(*tryExpr, "getActiveFrame", {}); auto getActiveFrameCall = CreateCallExpr(std::move(getActiveFrame), {}); auto getOrThrowAccess = CreateMemberAccess(std::move(getActiveFrameCall), "getOrThrow"); auto getOrThrowCall = CreateCallExpr(std::move(getOrThrowAccess), {}); auto parentAccess = CreateMemberAccess(std::move(getOrThrowCall), "parent"); SynthesizeWithoutRecover(ctx, parentAccess.get()); // `(HandlerFrame.getActiveFrame() == v.frame) auto binExpr = CreateBinaryExpr(std::move(parentAccess), std::move(frameAccess), TokenKind::EQUAL); binExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); CopyBasicInfo(tryExpr, binExpr); // v.exception auto vExcRef = CreateRefExpr(*vp->varDecl); AST::CopyNodeScopeInfo(vExcRef, tryExpr); auto excAccess = CreateMemberAccess(std::move(vExcRef), "exception"); // throw v.exception auto throwExcExpr = MakeOwned(); throwExcExpr->expr = std::move(excAccess); throwExcExpr->ty = TypeManager::GetNothingTy(); // v auto vRef = CreateRefExpr(*vp->varDecl); AST::CopyNodeScopeInfo(vRef, tryExpr); // throw v auto throwExpr = MakeOwned(); throwExpr->expr = std::move(vRef); throwExpr->ty = TypeManager::GetNothingTy(); // `if (HandlerFrame.getActiveFrame() == v.frame) { // ` throw v.exception // `} else { // ` throw v // `} std::vector> thenBlockNodes; (void)thenBlockNodes.emplace_back(std::move(throwExcExpr)); auto thenBlock = CreateBlock(std::move(thenBlockNodes)); thenBlock->ty = TypeManager::GetNothingTy(); std::vector> elseBlockNodes; (void)elseBlockNodes.emplace_back(std::move(throwExpr)); auto elseBlock = CreateBlock(std::move(elseBlockNodes)); elseBlock->ty = TypeManager::GetNothingTy(); auto ifExpr = CreateIfExpr(std::move(binExpr), std::move(thenBlock), std::move(elseBlock)); // `catch (v: ImmediateFrameExceptionWrapper) auto exceptTypePattern = MakeOwnedNode(); exceptTypePattern->pattern = std::move(vp); auto excWrapperRefTy = CreateRefType(*exceptionWrapperDecl); AST::CopyNodeScopeInfo(excWrapperRefTy, tryExpr); excWrapperRefTy->EnableAttr(Attribute::TOOL_ADD); (void)exceptTypePattern->types.emplace_back(std::move(excWrapperRefTy)); exceptTypePattern->ty = exceptionWrapperTy; // `catch (v: ImmediateFrameExceptionWrapper) { // ` if (HandlerFrame.getActiveFrame() == v.frame) { // ` ... // ` } else { // ` throw v // ` } // `} std::vector> catchBlockNodes; (void)catchBlockNodes.emplace_back(std::move(ifExpr)); auto catchBlock = CreateBlock(std::move(catchBlockNodes)); catchBlock->ty = TypeManager::GetNothingTy(); // Added to the tryExpr (void)tryExpr->catchPatterns.emplace_back(std::move(exceptTypePattern)); (void)tryExpr->catchBlocks.emplace_back(std::move(catchBlock)); } { // (v: ImmediateFrameErrorWrapper) auto vp = CreateVarPattern(V_COMPILER, errorWrapperTy); AST::CopyNodeScopeInfo(vp->varDecl, tryExpr); // v.frame auto vFrameRef = CreateRefExpr(*vp->varDecl); AST::CopyNodeScopeInfo(vFrameRef, tryExpr.get()); auto frameAccess = CreateMemberAccess(std::move(vFrameRef), "frame"); // HandlerFrame.getActiveFrame().getOrThrow().parent auto getActiveFrame = GetHelperFrameMethod(*tryExpr, "getActiveFrame", {}); auto getActiveFrameCall = CreateCallExpr(std::move(getActiveFrame), {}); auto getOrThrowAccess = CreateMemberAccess(std::move(getActiveFrameCall), "getOrThrow"); auto getOrThrowCall = CreateCallExpr(std::move(getOrThrowAccess), {}); auto parentAccess = CreateMemberAccess(std::move(getOrThrowCall), "parent"); SynthesizeWithoutRecover(ctx, parentAccess.get()); // `(HandlerFrame.getActiveFrame() == v.frame) auto binExpr = CreateBinaryExpr(std::move(parentAccess), std::move(frameAccess), TokenKind::EQUAL); binExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); CopyBasicInfo(tryExpr, binExpr); // v.error auto vExcRef = CreateRefExpr(*vp->varDecl); AST::CopyNodeScopeInfo(vExcRef, tryExpr); auto excAccess = CreateMemberAccess(std::move(vExcRef), "error"); // throw v.error auto throwExcExpr = MakeOwned(); throwExcExpr->expr = std::move(excAccess); throwExcExpr->ty = TypeManager::GetNothingTy(); // v auto vRef = CreateRefExpr(*vp->varDecl); AST::CopyNodeScopeInfo(vRef, tryExpr); // throw v auto throwExpr = MakeOwned(); throwExpr->expr = std::move(vRef); throwExpr->ty = TypeManager::GetNothingTy(); // `if (HandlerFrame.getActiveFrame() == v.frame) { // ` throw v.error // `} else { // ` throw v // `} std::vector> thenBlockNodes; (void)thenBlockNodes.emplace_back(std::move(throwExcExpr)); auto thenBlock = CreateBlock(std::move(thenBlockNodes)); thenBlock->ty = TypeManager::GetNothingTy(); std::vector> elseBlockNodes; (void)elseBlockNodes.emplace_back(std::move(throwExpr)); auto elseBlock = CreateBlock(std::move(elseBlockNodes)); elseBlock->ty = TypeManager::GetNothingTy(); auto ifExpr = CreateIfExpr(std::move(binExpr), std::move(thenBlock), std::move(elseBlock)); // `catch (v: ImmediateFrameErrorWrapper) auto exceptTypePattern = MakeOwnedNode(); exceptTypePattern->pattern = std::move(vp); auto excWrapperRefTy = CreateRefType(*errorWrapperDecl); AST::CopyNodeScopeInfo(excWrapperRefTy, tryExpr); excWrapperRefTy->EnableAttr(Attribute::TOOL_ADD); (void)exceptTypePattern->types.emplace_back(std::move(excWrapperRefTy)); exceptTypePattern->ty = errorWrapperTy; // `catch (v: ImmediateFrameExceptionWrapper) { // ` if (HandlerFrame.getActiveFrame() == v.frame) { // ` ... // ` } else { // ` throw v // ` } // `} std::vector> catchBlockNodes; (void)catchBlockNodes.emplace_back(std::move(ifExpr)); auto catchBlock = CreateBlock(std::move(catchBlockNodes)); catchBlock->ty = TypeManager::GetNothingTy(); // Added to the tryExpr (void)tryExpr->catchPatterns.emplace_back(std::move(exceptTypePattern)); (void)tryExpr->catchBlocks.emplace_back(std::move(catchBlock)); } { std::vector> matchCases; // (v: ImmediateEarlyReturn) auto vp = CreateVarPattern(V_COMPILER, earlyReturnTy); AST::CopyNodeScopeInfo(vp->varDecl, tryExpr); // v.frame auto vFrameRef = CreateRefExpr(*vp->varDecl); AST::CopyNodeScopeInfo(vFrameRef, tryExpr.get()); auto frameAccess = CreateMemberAccess(std::move(vFrameRef), "frame"); // HandlerFrame.getActiveFrame().getOrThrow().parent auto getActiveFrame = GetHelperFrameMethod(*tryExpr, "getActiveFrame", {}); auto getActiveFrameCall = CreateCallExpr(std::move(getActiveFrame), {}); auto getOrThrowAccess = CreateMemberAccess(std::move(getActiveFrameCall), "getOrThrow"); auto getOrThrowCall = CreateCallExpr(std::move(getOrThrowAccess), {}); auto parentAccess = CreateMemberAccess(std::move(getOrThrowCall), "parent"); SynthesizeWithoutRecover(ctx, parentAccess.get()); // `(HandlerFrame.getActiveFrame() == v.frame) auto binExpr = CreateBinaryExpr(std::move(parentAccess), std::move(frameAccess), TokenKind::EQUAL); binExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); CopyBasicInfo(tryExpr, binExpr); // v.result auto vpRef = CreateRefExpr(*vp->varDecl); AST::CopyNodeScopeInfo(vpRef, tryExpr); auto excAccess = CreateMemberAccess(std::move(vpRef), "result"); // case newVar: T auto varPattern = CreateVarPattern(V_COMPILER, tryLambda->funcBody->retType->ty.get()); AST::CopyNodeScopeInfo(varPattern->varDecl, tryExpr); auto vpRef2 = CreateRefExpr(*varPattern->varDecl); AST::CopyNodeScopeInfo(vpRef2, innerBlock); // return newVar auto returnExpr = MakeOwnedNode(); returnExpr->expr = std::move(vpRef2); returnExpr->refFuncBody = tryLambda->funcBody; returnExpr->ty = TypeManager::GetNothingTy(); CopyNodeScopeInfo(returnExpr, tryExpr); // case newVar: T => return newVar matchCases.emplace_back(CreateMatchCase(CreateRuntimePreparedTypePattern(typeManager, std::move(varPattern), ASTCloner::Clone(tryLambda->funcBody->retType.get()), *excAccess), std::move(returnExpr))); // case _ auto wildcard = MakeOwnedNode(); wildcard->ty = excAccess->ty; // Exception() auto exceptionDecl = importManager.GetCoreDecl(CLASS_EXCEPTION); CJC_NULLPTR_CHECK(exceptionDecl); auto exceptionType = exceptionDecl->ty; auto excInitDecl = GetDefaultInitDecl(*exceptionDecl); OwnedPtr re23 = CreateRefExpr("Exception"); CopyBasicInfo(innerBlock, re23.get()); re23->isAlone = false; re23->ref.target = excInitDecl; re23->ty = exceptionType; auto earlyCreation = AST::CreateCallExpr(std::move(re23), {}, excInitDecl, exceptionType, CallKind::CALL_OBJECT_CREATION); CopyBasicInfo(innerBlock, earlyCreation); // throw Exception() auto throwExpr = MakeOwned(); throwExpr->expr = std::move(earlyCreation); throwExpr->ty = TypeManager::GetNothingTy(); // case _ => throw Exception() matchCases.emplace_back(CreateMatchCase(std::move(wildcard), std::move(throwExpr))); // match (e.result) { // case newVar : T => return newVar // case _ => throw Exception() // } auto matchExpr = CreateMatchExpr( std::move(excAccess), std::move(matchCases), TypeManager::GetInvalidTy(), Expr::SugarKind::AS); matchExpr->ty = TypeManager::GetNothingTy(); // v auto vRef = CreateRefExpr(*vp->varDecl); AST::CopyNodeScopeInfo(vRef, tryExpr); // throw v auto reThrowExpr = MakeOwned(); reThrowExpr->expr = std::move(vRef); reThrowExpr->ty = TypeManager::GetNothingTy(); // `if (HandlerFrame.getActiveFrame() == v.frame) { // ` match (e.result) { // ` case newVar : T => return newVar // ` case _ => throw Exception() // ` } // `} else { // ` throw v // `} std::vector> thenBlockNodes; (void)thenBlockNodes.emplace_back(std::move(matchExpr)); auto thenBlock = CreateBlock(std::move(thenBlockNodes)); thenBlock->ty = TypeManager::GetNothingTy(); std::vector> elseBlockNodes; (void)elseBlockNodes.emplace_back(std::move(reThrowExpr)); auto elseBlock = CreateBlock(std::move(elseBlockNodes)); elseBlock->ty = TypeManager::GetNothingTy(); auto ifExpr = CreateIfExpr(std::move(binExpr), std::move(thenBlock), std::move(elseBlock)); // `catch (v: ImmediateEarlyReturn) auto exceptTypePattern = MakeOwnedNode(); exceptTypePattern->pattern = std::move(vp); auto excWrapperRefTy = CreateRefType(*earlyReturnDecl); AST::CopyNodeScopeInfo(excWrapperRefTy, tryExpr); excWrapperRefTy->EnableAttr(Attribute::TOOL_ADD); (void)exceptTypePattern->types.emplace_back(std::move(excWrapperRefTy)); exceptTypePattern->ty = earlyReturnTy; // `catch (e: ImmediateEarlyReturn) { // ` match (e.result) { // ` case newVar : T => return newVar // ` case _ => throw Exception() // ` } // `} std::vector> catchBlockNodes; (void)catchBlockNodes.emplace_back(std::move(ifExpr)); auto catchBlock = CreateBlock(std::move(catchBlockNodes)); catchBlock->ty = TypeManager::GetNothingTy(); // Added to the tryExpr (void)tryExpr->catchPatterns.emplace_back(std::move(exceptTypePattern)); (void)tryExpr->catchBlocks.emplace_back(std::move(catchBlock)); } // Since we're modifying the top block of the lambda expression, it must // finish with an explicit return expression auto returnExpr = MakeOwnedNode(); returnExpr->expr = std::move(tryExpr); returnExpr->refFuncBody = tryLambda->funcBody; CopyNodeScopeInfo(returnExpr, tryExpr); std::vector> nodes; nodes.emplace_back(std::move(returnExpr)); auto resultBlock = CreateBlock(std::move(nodes)); resultBlock->ty = TypeManager::GetNothingTy(); tryLambda->funcBody->body = std::move(resultBlock); } /* `let frame = Frame({=> ` try { ` println("trying...") ` let a = Frame.perf(Comm("foo")) ` println("tried a = ${a}") ` } catch (e: Exception){ ` println("exception") ` } `}) */ VarDecl& TypeChecker::TypeCheckerImpl::CreateFrame(ASTContext& ctx, TryExpr& te, std::vector>& block) { auto frameClassName = CLASS_IMMEDIATE_FRAME; // To: Frame({=> try{...} catch{...}}) auto lambdaTy = DynamicCast(te.tryLambda->ty); CJC_NULLPTR_CHECK(lambdaTy); // create Frame's reference auto re = CreateRefExpr(frameClassName); AST::CopyNodeScopeInfo(re, te.tryLambda); re->isAlone = false; // create function call: Frame(...) auto frameDecl = importManager.GetImportedDecl(EFFECT_INTERNALS_PACKAGE_NAME, frameClassName); CJC_NULLPTR_CHECK(frameDecl); re->SetTarget(frameDecl); auto tryExprTy = lambdaTy->retTy; auto frameClassTy = typeManager.GetClassTy(*frameDecl, {tryExprTy}); re->ty = frameClassTy; re->instTys.push_back(tryExprTy); auto frameInit = CreateMemberAccess(std::move(re), "init"); CJC_ASSERT(frameInit->target); frameInit->targets.emplace_back(StaticCast(frameInit->target)); EncloseTryLambda(ctx, te.tryLambda); // We cannot just pass the lambda directly to the constructor, because then the call // to ChkCallExpr would delete the desugaring information, so we bind the lambda to // a variable before calling the constructor auto lambdaVarDecl = CreateVarDecl("$frameLambda", std::move(te.tryLambda)); AST::CopyNodeScopeInfo(re, lambdaVarDecl); auto lambdaVar = CreateRefExpr(*lambdaVarDecl); AST::CopyNodeScopeInfo(re, lambdaVar); std::vector> args; args.emplace_back(CreateFuncArg(std::move(lambdaVar))); auto frameCreation = AST::CreateCallExpr( std::move(frameInit), std::move(args), nullptr, frameClassTy, CallKind::CALL_OBJECT_CREATION); frameCreation->desugarArgs = {}; auto typecheckOk = ChkCallExpr(ctx, frameClassTy, *frameCreation); CJC_ASSERT(typecheckOk); OwnedPtr frame = CreateVarDecl("frame", std::move(frameCreation)); frame->ty = frameClassTy; auto& frameRef = *frame; block.emplace_back(std::move(lambdaVarDecl)); block.emplace_back(std::move(frame)); return frameRef; } void TypeChecker::TypeCheckerImpl::CreateSetFinally( ASTContext& ctx, TryExpr& te, VarDecl& frame, std::vector>& block) { auto target = CreateRefExpr(frame); AST::CopyNodeScopeInfo(target, &te); auto setFinally = CreateMemberAccess(std::move(target), "setFinally"); setFinally->begin = te.begin; setFinally->end = te.end; InferMemberAccess(ctx, *setFinally); std::vector> args; args.emplace_back(CreateFuncArg(std::move(te.finallyLambda))); auto setFinallyCall = CreateCallExpr(std::move(setFinally), std::move(args)); setFinallyCall->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); setFinallyCall->callKind = AST::CallKind::CALL_DECLARED_FUNCTION; block.emplace_back(std::move(setFinallyCall)); } /* frame.setHandler({effect: Command, resumption: Resumption => println("handling") resumption.proceed(Foo()) }) */ void TypeChecker::TypeCheckerImpl::CreateSetHandler( ASTContext& ctx, TryExpr& te, VarDecl& frame, std::vector>& block) { auto unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); // each effect will have its handler, so there will be multipl setHandler call for (auto& handler : te.handlers) { CommandTypePattern* commandPattern = RawStaticCast(handler.commandPattern.get()); for (uint64_t j = 0; j < commandPattern->types.size(); j++) { // To: create memberAccess auto re = CreateRefExpr(frame); AST::CopyNodeScopeInfo(re, handler.block); OwnedPtr ma; ma = CreateMemberAccess(std::move(re), "setImmediateHandler"); // Function `CreateMemberAccess` will not initialise `ma.targets`, // so we need to assign `targets` by calling `ChkCallExpr` later OwnedPtr effectTypeArgument = ASTCloner::Clone(commandPattern->types[j].get()); ma->typeArguments.emplace_back(std::move(effectTypeArgument)); ma->instTys.emplace_back(commandPattern->types[j]->ty); OwnedPtr resumptionTypeArgument = MakeOwned(); CJC_ASSERT(handler.commandResultTy); resumptionTypeArgument->ty = handler.commandResultTy; ma->typeArguments.emplace_back(std::move(resumptionTypeArgument)); ma->instTys.emplace_back(handler.commandResultTy); // setImmediateHandler lives in HandlerFrame, which is not parameterized // by return type, so it takes the return type as an extra type parameter OwnedPtr resultTypeArgument = MakeOwned(); resultTypeArgument->ty = te.ty; ma->typeArguments.emplace_back(std::move(resultTypeArgument)); ma->instTys.emplace_back(te.ty); // We need to set the targets by hand, because the call to ChkCallExpr // below expects targets to be nonempty CJC_ASSERT(ma->target); ma->targets.emplace_back(StaticCast(ma->target)); OwnedPtr handlerLambda = std::move(handler.desugaredLambda); /** * When a handler is immediate we need to treat exceptions in the handler * and results of the desugaredLambda differently, since the stack of the * try expression needs to be unwinded. In here we modify the desugaredLambda * to achieve that * * From: * {(e:Eff) => * * } * * To: * {(e: Eff) => * let result = try { * * } catch (x: Exception) { * throw ImmediateFrameExceptionWrapper(HandlerFrame.getActiveFrame(), x) * } catch (x: Error) { * throw ImmediateFrameErrorWrapper(HandlerFrame.getActiveFrame(), x) * } * throw ImmediateFrameEarlyReturn(HandlerFrame.getActiveFrame(), result) * } */ // Get the inner block OwnedPtr innerBlock = std::move(handlerLambda->funcBody->body); // remove return expr from inner block since we wan't to treat it as // an exception now if (auto lastExpr = std::move(innerBlock->body.back())) { auto returnExpr = StaticCast(lastExpr.get()); CJC_NULLPTR_CHECK(returnExpr); innerBlock->body.back() = std::move(returnExpr->expr); } innerBlock->ty = Ty::GetInitialTy(); // Get all declarations and types needed // std.core.Exception auto exceptionDecl = importManager.GetCoreDecl(CLASS_EXCEPTION); CJC_NULLPTR_CHECK(exceptionDecl); auto exceptionType = exceptionDecl->ty; // std.core.Error auto errorDecl = importManager.GetCoreDecl(CLASS_ERROR); CJC_NULLPTR_CHECK(errorDecl); auto errorType = errorDecl->ty; // stdx.effect.ImmediateFrameExceptionWrapper auto exceptionWrapperDecl = importManager.GetImportedDecl( EFFECT_INTERNALS_PACKAGE_NAME, CLASS_FRAME_EXCEPTION_WRAPPER); CJC_NULLPTR_CHECK(exceptionWrapperDecl); auto exceptionWrapperTy = typeManager.GetClassTy(*exceptionWrapperDecl, {}); // stdx.effect.ImmediateFrameErrorWrapper auto errorWrapperDecl = importManager.GetImportedDecl(EFFECT_INTERNALS_PACKAGE_NAME, CLASS_FRAME_ERROR_WRAPPER); CJC_NULLPTR_CHECK(errorWrapperDecl); auto errorWrapperTy = typeManager.GetClassTy(*errorWrapperDecl, {}); // stdx.effect.ImmediateEarlyReturn auto earlyReturnDecl = importManager.GetImportedDecl(EFFECT_INTERNALS_PACKAGE_NAME, CLASS_EARLY_RETURN); CJC_NULLPTR_CHECK(earlyReturnDecl); auto earlyReturnTy = typeManager.GetClassTy(*earlyReturnDecl, {}); // `try { ... } auto tryExpr = MakeOwnedNode(); CopyBasicInfo(innerBlock, tryExpr); { // (v: Exception) auto vp = CreateVarPattern(V_COMPILER, exceptionType); AST::CopyNodeScopeInfo(vp->varDecl, innerBlock); // ImmediateFrameExceptionWrapper( ... ) std::vector> inits; for (auto& decl : exceptionWrapperDecl->GetMemberDecls()) { CJC_NULLPTR_CHECK(decl); if (decl->astKind == ASTKind::FUNC_DECL && decl.get()->TestAttr(Attribute::CONSTRUCTOR)) { inits.emplace_back(StaticCast(decl.get())); } } CJC_ASSERT(inits.size() == 1); auto initDecl = inits.front(); CJC_ASSERT(Ty::IsTyCorrect(initDecl->ty) && initDecl->ty->IsFunc()); OwnedPtr re2 = CreateRefExpr(CLASS_FRAME_EXCEPTION_WRAPPER); re2->isAlone = false; re2->ref.target = initDecl; re2->ty = initDecl->ty; CopyBasicInfo(innerBlock, re2.get()); // (v) std::vector> args; // HandlerFrame.getActiveFrame() auto getActiveFrame = GetHelperFrameMethod(*innerBlock, "getActiveFrame", {}); auto getActiveFrameCall = CreateCallExpr(std::move(getActiveFrame), {}); SynthesizeWithoutRecover(ctx, getActiveFrameCall.get()); auto vpRef = CreateRefExpr(*vp->varDecl); AST::CopyNodeScopeInfo(vpRef, innerBlock); // ImmediateFrameExceptionWrapper(HandlerFrame.getActiveFrame(), v) args.emplace_back(CreateFuncArg(std::move(getActiveFrameCall))); args.emplace_back(CreateFuncArg(std::move(vpRef))); auto wrapperCreation = AST::CreateCallExpr( std::move(re2), std::move(args), initDecl, exceptionWrapperTy, CallKind::CALL_OBJECT_CREATION); CopyBasicInfo(innerBlock, wrapperCreation); wrapperCreation->desugarArgs = {}; // throw ImmediateFrameExceptionWrapper(...) auto throwExpr = MakeOwned(); throwExpr->expr = std::move(wrapperCreation); throwExpr->ty = TypeManager::GetNothingTy(); // { // throw ImmediateFrameExceptionWrapper(...) // } std::vector> catchBlockNodes; (void)catchBlockNodes.emplace_back(std::move(throwExpr)); auto catchBlock = CreateBlock(std::move(catchBlockNodes)); // (v: Exception) auto exceptTypePattern = MakeOwnedNode(); exceptTypePattern->pattern = std::move(vp); (void)exceptTypePattern->types.emplace_back(CreateRefType(*exceptionDecl)); exceptTypePattern->ty = exceptionType; // `catch (v: Exception) { // ` throw ImmediateFrameExceptionWrapper(...) // `} (void)tryExpr->catchBlocks.emplace_back(std::move(catchBlock)); (void)tryExpr->catchPatterns.emplace_back(std::move(exceptTypePattern)); } { // (v: Error) auto vp = CreateVarPattern(V_COMPILER, errorType); AST::CopyNodeScopeInfo(vp->varDecl, innerBlock); // ImmediateFrameErrorWrapper( ... ) std::vector> inits; for (auto& decl : errorWrapperDecl->GetMemberDecls()) { CJC_NULLPTR_CHECK(decl); if (decl->astKind == ASTKind::FUNC_DECL && decl.get()->TestAttr(Attribute::CONSTRUCTOR)) { inits.emplace_back(StaticCast(decl.get())); } } CJC_ASSERT(inits.size() == 1); auto initDecl = inits.front(); CJC_ASSERT(Ty::IsTyCorrect(initDecl->ty) && initDecl->ty->IsFunc()); OwnedPtr re2 = CreateRefExpr(CLASS_FRAME_ERROR_WRAPPER); re2->isAlone = false; re2->ref.target = initDecl; re2->ty = initDecl->ty; CopyBasicInfo(innerBlock, re2.get()); // (v) std::vector> args; // HandlerFrame.getActiveFrame() auto getActiveFrame = GetHelperFrameMethod(*innerBlock, "getActiveFrame", {}); auto getActiveFrameCall = CreateCallExpr(std::move(getActiveFrame), {}); SynthesizeWithoutRecover(ctx, getActiveFrameCall.get()); auto vpRef = CreateRefExpr(*vp->varDecl); AST::CopyNodeScopeInfo(vpRef, innerBlock); // ImmediateFrameErrorWrapper(HandlerFrame.getActiveFrame(), v) args.emplace_back(CreateFuncArg(std::move(getActiveFrameCall))); args.emplace_back(CreateFuncArg(std::move(vpRef))); auto wrapperCreation = AST::CreateCallExpr( std::move(re2), std::move(args), initDecl, errorWrapperTy, CallKind::CALL_OBJECT_CREATION); CopyBasicInfo(innerBlock, wrapperCreation); wrapperCreation->desugarArgs = {}; // throw ImmediateFrameErrorWrapper(...) auto throwExpr = MakeOwned(); throwExpr->expr = std::move(wrapperCreation); throwExpr->ty = TypeManager::GetNothingTy(); // { // throw ImmediateFrameErrorWrapper(...) // } std::vector> catchBlockNodes; (void)catchBlockNodes.emplace_back(std::move(throwExpr)); auto catchBlock = CreateBlock(std::move(catchBlockNodes)); // (v: Error) auto exceptTypePattern = MakeOwnedNode(); exceptTypePattern->pattern = std::move(vp); (void)exceptTypePattern->types.emplace_back(CreateRefType(*errorDecl)); exceptTypePattern->ty = errorType; // `catch (v: Error) { // ` throw ImmediateFrameErrorWrapper(...) // `} (void)tryExpr->catchBlocks.emplace_back(std::move(catchBlock)); (void)tryExpr->catchPatterns.emplace_back(std::move(exceptTypePattern)); } tryExpr->tryBlock = std::move(innerBlock); // Recover all types in the try expression SynthesizeWithoutRecover(ctx, tryExpr); std::vector> nodes; { // let result = try { ... } catch { ... } auto tryResult = CreateVarDecl(V_COMPILER, std::move(tryExpr)); AST::CopyNodeScopeInfo(tryResult, tryResult); auto resRef = CreateRefExpr(*tryResult); CopyNodeScopeInfo(resRef, tryResult); std::vector> inits; for (auto& decl : earlyReturnDecl->GetMemberDecls()) { CJC_NULLPTR_CHECK(decl); if (decl->astKind == ASTKind::FUNC_DECL && decl.get()->TestAttr(Attribute::CONSTRUCTOR)) { inits.emplace_back(StaticCast(decl.get())); } } CJC_ASSERT(inits.size() == 1); auto initDecl = inits.front(); CJC_ASSERT(Ty::IsTyCorrect(initDecl->ty) && initDecl->ty->IsFunc()); // HandlerFrame.getActiveFrame() auto getActiveFrame = GetHelperFrameMethod(*tryResult, "getActiveFrame", {}); auto getActiveFrameCall = CreateCallExpr(std::move(getActiveFrame), {}); SynthesizeWithoutRecover(ctx, getActiveFrameCall.get()); // ImmediateEarlyReturn(...) OwnedPtr re2 = CreateRefExpr(CLASS_EARLY_RETURN); re2->isAlone = false; re2->ref.target = initDecl; re2->ty = earlyReturnTy; CopyBasicInfo(tryResult, re2.get()); // ImmediateEarlyReturn(HandlerFrame.getActiveFrame(), result) std::vector> args2; args2.emplace_back(CreateFuncArg(std::move(getActiveFrameCall))); args2.emplace_back(CreateFuncArg(std::move(resRef))); auto earlyCreation = AST::CreateCallExpr( std::move(re2), std::move(args2), initDecl, earlyReturnTy, CallKind::CALL_OBJECT_CREATION); CopyBasicInfo(tryResult, earlyCreation); earlyCreation->desugarArgs = {}; // throw ImmediateEarlyReturn(...) auto throwExpr2 = MakeOwned(); throwExpr2->expr = std::move(earlyCreation); throwExpr2->ty = TypeManager::GetNothingTy(); nodes.emplace_back(std::move(tryResult)); nodes.emplace_back(std::move(throwExpr2)); } // `let result = try { // ` // `} catch (x: Exception) { // ` throw ImmediateFrameExceptionWrapper(...) // `} // `throw ImmediateFrameEarlyReturn(...) auto resultBlock = CreateBlock(std::move(nodes)); // Recover the types of the whole block SynthesizeWithoutRecover(ctx, resultBlock); handlerLambda->funcBody->body = std::move(resultBlock); // Finally, we need to change the type of the try lambda from T // to ImmediateHandlerReturn auto resultDecl = importManager.GetImportedDecl(EFFECT_INTERNALS_PACKAGE_NAME, "ImmediateHandlerReturn"); CJC_NULLPTR_CHECK(resultDecl); auto resultDeclTy = typeManager.GetEnumTy(*resultDecl, {handler.commandResultTy}); handlerLambda->ty = typeManager.GetFunctionTy(DynamicCast(handlerLambda->ty)->paramTys, resultDeclTy); handlerLambda->funcBody->ty = typeManager.GetFunctionTy(DynamicCast(handlerLambda->ty)->paramTys, resultDeclTy); handlerLambda->funcBody->retType = CreateRefType(*resultDeclTy->decl); // Like in createFrame, we can't just pass the lambda to `setHandler` OwnedPtr handlerLambdaVarDecl = CreateVarDecl("$handlerLambda", std::move(handlerLambda)); AST::CopyNodeScopeInfo(re, handlerLambdaVarDecl); auto handlerLambdaVar = CreateRefExpr(*handlerLambdaVarDecl); AST::CopyNodeScopeInfo(re, handlerLambdaVar); std::vector> args; args.emplace_back(CreateFuncArg(std::move(handlerLambdaVar))); auto setHandlerCall = AST::CreateCallExpr(std::move(ma), std::move(args)); setHandlerCall->callKind = CallKind::CALL_DECLARED_FUNCTION; ChkCallExpr(ctx, unitTy, *setHandlerCall); block.emplace_back(std::move(handlerLambdaVarDecl)); block.emplace_back(std::move(setHandlerCall)); } } } /* let result = frame.start() */ void TypeChecker::TypeCheckerImpl::CreateResult( ASTContext& ctx, const TryExpr& te, VarDecl& frame, std::vector>& block) { // To: create memberAccess auto re = CreateRefExpr(frame); re->begin = te.begin; re->end = te.end; AST::CopyNodeScopeInfo(re, &frame); OwnedPtr ma = CreateMemberAccess(std::move(re), "start"); CJC_ASSERT(ma->target); ma->targets.emplace_back(StaticCast(ma->target)); // To: frame.start() auto startCall = AST::CreateCallExpr(std::move(ma), {}); auto typecheckOk = ChkCallExpr(ctx, te.ty, *startCall); CJC_ASSERT(typecheckOk); auto tryTy = te.ty; std::vector> matchCases; auto varPattern = CreateVarPattern(V_COMPILER, tryTy); auto varDecl = varPattern->varDecl.get(); varDecl->curFile = te.curFile; // This is a hack. We need an AST Type object to create the pattern, but we // don't have access to one, since tryTy was obtained by synthesis during sema, so // we create a fake one and bind it to the correct sema type auto type = CreateRefType(V_COMPILER); type->ty = tryTy; matchCases.emplace_back(CreateMatchCase( CreateRuntimePreparedTypePattern(typeManager, std::move(varPattern), std::move(type), *startCall), CreateRefExpr(*varDecl))); auto match = CreateMatchExpr(std::move(startCall), std::move(matchCases), tryTy); block.emplace_back(std::move(match)); } /* * *************** before desugar *************** try { println("trying...") let a = perform Comm("foo") println("tried") } catch (e: Exception){ println("exception") } handle (e: Comm, res: Resumption) { println("handling") resume res with Foo() } println("no effects any more") * *************** after desugar **************** let frame = Frame({=> try { println("trying...") let a = Frame.perf(Comm("foo")) println("tried a = ${a}") } catch (e: Exception){ println("exception") } }) frame.setHandler({e: Comm, res: Resumption => println("handling") res.proceed(Foo()) }) let result = frame.start() */ void TypeChecker::TypeCheckerImpl::DesugarTryToFrame(ASTContext& ctx, TryExpr& te) { if (te.handlers.empty() || !Ty::IsTyCorrect(te.ty)) { return; } std::vector> nodes; auto& frame = CreateFrame(ctx, te, nodes); CreateSetHandler(ctx, te, frame, nodes); if (te.finallyLambda) { CreateSetFinally(ctx, te, frame, nodes); } CreateResult(ctx, te, frame, nodes); OwnedPtr dummyBlock = CreateBlock(std::move(nodes), te.ty); AST::CopyNodeScopeInfo(dummyBlock, &te); te.desugarExpr = std::move(dummyBlock); } OwnedPtr TypeChecker::TypeCheckerImpl::GetHelperFrameMethod( AST::Node& base, const std::string& methodName, std::vector> typeArgs) { auto frameDecl = importManager.GetImportedDecl(EFFECT_INTERNALS_PACKAGE_NAME, CLASS_HANDLER_FRAME); CJC_NULLPTR_CHECK(frameDecl); auto frameClassTy = typeManager.GetClassTy(*frameDecl, {}); auto re = CreateRefExpr(CLASS_HANDLER_FRAME); re->ref.target = frameDecl; AST::CopyNodeScopeInfo(re, &base); re->ty = frameClassTy; auto memberFunc = CreateMemberAccess(std::move(re), methodName); memberFunc->instTys = std::move(typeArgs); CJC_ASSERT(memberFunc->target); memberFunc->targets.emplace_back(StaticCast(memberFunc->target)); return memberFunc; } /* From: perform Comm("foo") To: Frame.perf(Comm("foo")) */ void TypeChecker::TypeCheckerImpl::DesugarPerform(ASTContext& ctx, AST::PerformExpr& pe) { if (!Ty::IsTyCorrect(pe.ty)) { // Sometimes the Desugar function is called on "junk" code that has not been // through the typechecker. For example, code in primary constructors gets // desugared early on; the pre-desugaring code is never typechecked but for // some reason desugaring still happens inside it; this desugaring needs to // have access to types, therefore we can skip it entirely if the typechecker // has not been run return; } auto cmdTy = pe.expr->ty; auto resultTy = pe.ty; auto perfMethod = GetHelperFrameMethod(pe, "perf", {cmdTy, resultTy}); std::vector> args; args.emplace_back(CreateFuncArg(std::move(pe.expr))); auto perfCall = CreateCallExpr(std::move(perfMethod), std::move(args)); auto typecheckOk = ChkCallExpr(ctx, pe.ty, *perfCall); CJC_ASSERT(typecheckOk); // Checking for a call expr may get rid of some desugarings DesugarForPropDecl(*perfCall); pe.desugarExpr = std::move(perfCall); } void TypeChecker::TypeCheckerImpl::DesugarResume(ASTContext& ctx, AST::ResumeExpr& re) { if (!Ty::IsTyCorrect(re.ty)) { // Sometimes the Desugar function is called on "junk" code that has not been // through the typechecker. For example, code in primary constructors gets // desugared early on; the pre-desugaring code is never typechecked but for // some reason desugaring still happens inside it; this desugaring needs to // have access to types, therefore we can skip it entirely if the typechecker // has not been run return; } DesugarImmediateResume(ctx, re); } /** * From: resume with val * To: ImmediateHandlerReturn.Result(val) * * From: resume throwing exc * To: ImmediateHandlerReturn.Exc(exc) */ void TypeChecker::TypeCheckerImpl::DesugarImmediateResume(ASTContext& ctx, AST::ResumeExpr& re) { OwnedPtr desugared; auto resultDecl = importManager.GetImportedDecl(EFFECT_INTERNALS_PACKAGE_NAME, "ImmediateHandlerReturn"); CJC_NULLPTR_CHECK(resultDecl); CJC_ASSERT(re.enclosing.has_value()); auto resultDeclTy = typeManager.GetEnumTy(*resultDecl, {re.enclosing.value()->commandResultTy}); CJC_NULLPTR_CHECK(resultDeclTy); if (re.throwingExpr) { auto exceptionDecl = importManager.GetCoreDecl(CLASS_EXCEPTION); CJC_NULLPTR_CHECK(exceptionDecl); auto errorDecl = importManager.GetCoreDecl(CLASS_ERROR); CJC_NULLPTR_CHECK(errorDecl); OwnedPtr wrapperFnRef; if (typeManager.IsSubtype(re.throwingExpr->ty, exceptionDecl->ty)) { auto excTy = typeManager.GetFunctionTy({exceptionDecl->ty}, resultDeclTy); wrapperFnRef = CreateRefExc(*excTy); } else if (typeManager.IsSubtype(re.throwingExpr->ty, errorDecl->ty)) { auto excTy = typeManager.GetFunctionTy({errorDecl->ty}, resultDeclTy); wrapperFnRef = CreateRefErr(*excTy); } CJC_NULLPTR_CHECK(wrapperFnRef); std::vector> args; (void)args.emplace_back(CreateFuncArg(std::move(re.throwingExpr))); auto wrappedExc = CreateCallExpr(std::move(wrapperFnRef), std::move(args)); ChkCallExpr(ctx, resultDeclTy, *wrappedExc); // Checking for a call expr may get rid of some desugarings DesugarForPropDecl(*wrappedExc); auto returnExpr = MakeOwnedNode(); returnExpr->expr = std::move(wrappedExc); desugared = std::move(returnExpr); } else { auto valTy = typeManager.GetFunctionTy({re.enclosing.value()->commandResultTy}, resultDeclTy); std::vector> args; if (re.withExpr) { (void)args.emplace_back(CreateFuncArg(std::move(re.withExpr))); } else { (void)args.emplace_back( CreateFuncArg(AST::CreateUnitExpr(typeManager.GetPrimitiveTy(TypeKind::TYPE_UNIT)))); } auto wrappedValue = CreateCallExpr(CreateRefValue(*valTy), std::move(args)); ChkCallExpr(ctx, resultDeclTy, *wrappedValue); // Checking for a call expr may get rid of some desugarings DesugarForPropDecl(*wrappedValue); auto returnExpr = MakeOwnedNode(); returnExpr->expr = std::move(wrappedValue); desugared = std::move(returnExpr); } desugared->ty = TypeManager::GetNothingTy(); re.desugarExpr = std::move(desugared); } cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/ForInExpr.cpp000066400000000000000000001202031510705540100252740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Desugar/AfterTypeCheck.h" #include "cangjie/Driver/StdlibMap.h" #include "TypeCheckUtil.h" #include "TypeCheckerImpl.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Utils.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; using namespace Sema::Desugar::AfterTypeCheck; namespace { OwnedPtr CreateIndexVar(const ForInExpr& forInExpr, const RangeExpr& rangeExpr) { auto index = MakeOwnedNode(); index->EnableAttr(Attribute::IMPLICIT_ADD); index->ty = rangeExpr.startExpr->ty; index->initializer = ASTCloner::Clone(rangeExpr.startExpr.get()); if (auto vp = DynamicCast(forInExpr.pattern.get()); vp) { // var pattern index->identifier = "$iter-" + vp->varDecl->identifier; index->isVar = true; index->assignPos = index->begin; index->curFile = forInExpr.curFile; } else { // wildcard pattern index->identifier = "$index-compiler"; index->isVar = true; index->assignPos = forInExpr.pattern->begin; index->curFile = forInExpr.curFile; } index->begin = forInExpr.pattern->begin; index->end = forInExpr.pattern->end; return index; } OwnedPtr GetVarRef(VarDecl& var, const Node& pos) { auto r = CreateRefExpr(var.identifier); r->curFile = var.curFile; r->ref.target = &var; r->ty = var.ty; r->begin = pos.begin; r->end = pos.end; return r; } OwnedPtr GetVarRef(VarDecl& var) { auto r = CreateRefExpr(var.identifier); r->curFile = var.curFile; r->ref.target = &var; r->ty = var.ty; return r; } // 'curFile' value will be added at the end of all ast node creation. OwnedPtr CreateStopVar(const RangeExpr& rangeExpr) { auto rangeStop = CreateVarDecl("$stop-compiler", ASTCloner::Clone(rangeExpr.stopExpr.get())); rangeStop->begin = rangeExpr.stopExpr->begin; rangeStop->end = rangeExpr.stopExpr->end; return rangeStop; } OwnedPtr CreateFirstFlag(const RangeExpr& rangeExpr) { auto boolTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); auto firstFlag = CreateVarDecl("$isFirst-compiler", CreateLitConstExpr(LitConstKind::BOOL, "true", boolTy)); firstFlag->isVar = true; firstFlag->begin = rangeExpr.begin; firstFlag->end = rangeExpr.end; return firstFlag; } OwnedPtr CreateRangeCond( const ForInExpr& forInExpr, VarDecl& index, VarDecl& rangeStop, const TokenKind& opToken) { auto rangeCond = CreateBinaryExpr(GetVarRef(index, rangeStop), GetVarRef(rangeStop, rangeStop), opToken); rangeCond->curFile = forInExpr.curFile; rangeCond->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); rangeCond->begin = forInExpr.inExpression->begin; rangeCond->end = forInExpr.inExpression->end; return rangeCond; } OwnedPtr CreateGuard(ForInExpr& forInExpr, BinaryExpr& rangeCond, const bool isClose) { auto condBody = MakeOwnedNode(); condBody->sugarKind = Expr::SugarKind::FOR_IN_EXPR; condBody->curFile = forInExpr.curFile; condBody->begin = rangeCond.begin; condBody->end = rangeCond.end; if (forInExpr.patternGuard) { if (isClose) { auto andCond = CreateBinaryExpr(ASTCloner::Clone(Ptr(&rangeCond)), std::move(forInExpr.patternGuard), TokenKind::AND); andCond->curFile = forInExpr.curFile; andCond->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); andCond->begin = andCond->rightExpr->begin; andCond->end = andCond->rightExpr->end; condBody->condExpr = std::move(andCond); } else { condBody->condExpr = std::move(forInExpr.patternGuard); } } else { if (isClose) { condBody->condExpr = ASTCloner::Clone(Ptr(&rangeCond)); } else { condBody->condExpr = MakeOwned(LitConstKind::BOOL, "true"); condBody->condExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); condBody->condExpr->begin = rangeCond.begin; condBody->condExpr->end = rangeCond.end; condBody->condExpr->EnableAttr(Attribute::COMPILER_ADD); } } condBody->thenBody = std::move(forInExpr.body); condBody->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); condBody->EnableAttr(Attribute::IMPLICIT_ADD); return condBody; } OwnedPtr CreateUpdate(const ForInExpr& forInExpr, RangeExpr& rangeExpr, VarDecl& index) { auto update = MakeOwnedNode(); update->curFile = forInExpr.curFile; update->leftValue = GetVarRef(index, rangeExpr); if (rangeExpr.stepExpr) { update->rightExpr = std::move(rangeExpr.stepExpr); CJC_ASSERT(update->rightExpr->ty); } else { update->rightExpr = MakeOwned(LitConstKind::INTEGER, "1"); update->rightExpr->ty = index.ty; } update->begin = rangeExpr.begin; update->end = rangeExpr.end; update->op = TokenKind::ADD_ASSIGN; update->isCompound = true; update->leftValue->ty = index.ty; return update; } OwnedPtr CreateFirstIf(const ForInExpr& forInExpr, VarDecl& firstFlag, OwnedPtr&& update) { auto firstRun = MakeOwnedNode(); firstRun->hasElse = true; firstRun->curFile = forInExpr.curFile; firstRun->condExpr = GetVarRef(firstFlag, *forInExpr.inExpression); constexpr int inLen{2}; firstRun->condExpr->begin = firstRun->begin = forInExpr.inPos; firstRun->condExpr->end = firstRun->end = forInExpr.inPos + inLen; firstRun->thenBody = MakeOwnedNode(); firstRun->thenBody->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); firstRun->thenBody->begin = forInExpr.body->begin; firstRun->thenBody->end = forInExpr.body->end; // first = false auto assignFalse = MakeOwnedNode(); assignFalse->op = TokenKind::ASSIGN; assignFalse->curFile = forInExpr.curFile; assignFalse->leftValue = GetVarRef(firstFlag, *forInExpr.inExpression); assignFalse->leftValue->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); assignFalse->rightExpr = MakeOwned(LitConstKind::BOOL, "false"); assignFalse->rightExpr->curFile = forInExpr.curFile; assignFalse->rightExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); assignFalse->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); assignFalse->begin = forInExpr.inPos; assignFalse->end = forInExpr.inPos + inLen; firstRun->thenBody->body.push_back(std::move(assignFalse)); auto elseBody = MakeOwnedNode(); elseBody->body.push_back(std::move(update)); elseBody->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); firstRun->begin = elseBody->begin = forInExpr.body->begin; firstRun->end = elseBody->end = forInExpr.body->end; firstRun->elseBody = std::move(elseBody); firstRun->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); return firstRun; } OwnedPtr CreateLetIter(ASTContext& ctx, VarPattern& vp, ForInExpr& forInExpr, VarDecl& index) { auto letValue = MakeOwnedNode(); // To: let i = iter-i letValue = std::move(vp.varDecl); letValue->parentPattern = nullptr; auto initRef = GetVarRef(index, *letValue); letValue->initializer = std::move(initRef); ctx.DeleteInvertedIndexes(forInExpr.pattern.get()); forInExpr.pattern = nullptr; return letValue; } OwnedPtr CreateNoneCaseForForInIter(Ptr optionDecl, Expr& refLoop, Ptr caseTy) { auto caseNone = MakeOwnedNode(); auto nonePattern = MakeOwnedNode(); nonePattern->ty = caseTy; auto noneRef = MakeOwnedNode(); noneRef->ref.identifier = OPTION_NONE_CTOR; noneRef->ref.target = LookupEnumMember(optionDecl, OPTION_NONE_CTOR); noneRef->ty = nonePattern->ty; noneRef->instTys = nonePattern->ty->typeArgs; auto breakExpr = MakeOwnedNode(); breakExpr->isBreak = true; breakExpr->refLoop = &refLoop; breakExpr->ty = TypeManager::GetNothingTy(); nonePattern->constructor = std::move(noneRef); caseNone->patterns.emplace_back(std::move(nonePattern)); caseNone->exprOrDecls = MakeOwnedNode(); caseNone->exprOrDecls->body.push_back(std::move(breakExpr)); caseNone->exprOrDecls->ty = TypeManager::GetNothingTy(); caseNone->ty = TypeManager::GetNothingTy(); return caseNone; } OwnedPtr CreateSomeCaseForForInIter( Ptr optionDecl, Expr& refLoop, Ptr someRefTy, ForInExpr& forInExpr) { Ptr patternTy = forInExpr.pattern->ty; CJC_NULLPTR_CHECK(patternTy); // To : case Some(v) auto caseSome = MakeOwnedNode(); auto somePattern = MakeOwnedNode(); somePattern->ty = someRefTy->retTy; auto someRef = MakeOwnedNode(); someRef->ref.identifier = OPTION_VALUE_CTOR; someRef->ref.target = LookupEnumMember(optionDecl, OPTION_VALUE_CTOR); someRef->ty = someRefTy; somePattern->constructor = std::move(someRef); auto vPattern = CreateVarPattern(V_COMPILER, patternTy); auto vDecl = vPattern->varDecl.get(); somePattern->patterns.emplace_back(std::move(vPattern)); caseSome->patterns.emplace_back(std::move(somePattern)); // To : match v { case pat if e2 => b; case _ => continue} auto insideMatch = MakeOwnedNode(); insideMatch->matchMode = true; insideMatch->selector = CreateRefExpr(*vDecl); // To : case pat if e2 => b auto casePat = MakeOwnedNode(); casePat->patterns.emplace_back(std::move(forInExpr.pattern)); forInExpr.patternInDesugarExpr = casePat->patterns.front().get(); casePat->patternGuard = std::move(forInExpr.patternGuard); casePat->exprOrDecls = std::move(forInExpr.body); casePat->exprOrDecls->begin.Mark(PositionStatus::IGNORE); casePat->exprOrDecls->end.Mark(PositionStatus::IGNORE); RearrangeRefLoop(forInExpr, refLoop, casePat->exprOrDecls.get()); insideMatch->ty = casePat->exprOrDecls->ty; casePat->ty = casePat->exprOrDecls->ty; insideMatch->matchCases.push_back(std::move(casePat)); // To : case _ => continue auto wildCase = MakeOwnedNode(); auto wildcard = MakeOwnedNode(); wildcard->ty = patternTy; wildCase->patterns.emplace_back(std::move(wildcard)); auto continueExpr = MakeOwnedNode(); continueExpr->refLoop = &refLoop; continueExpr->isBreak = false; continueExpr->ty = TypeManager::GetNothingTy(); wildCase->exprOrDecls = MakeOwnedNode(); wildCase->exprOrDecls->body.push_back(std::move(continueExpr)); wildCase->exprOrDecls->ty = TypeManager::GetNothingTy(); wildCase->ty = TypeManager::GetNothingTy(); insideMatch->matchCases.push_back(std::move(wildCase)); caseSome->exprOrDecls = MakeOwnedNode(); caseSome->exprOrDecls->body.push_back(std::move(insideMatch)); caseSome->exprOrDecls->ty = TypeManager::GetNothingTy(); caseSome->ty = TypeManager::GetNothingTy(); return caseSome; } } // namespace /** * Desugar ForInExpr of close range to while expr. for example: * *************** before desugar **************** * for (int in range: step where guard) { * body * } * *************** after desugar **************** * var iter = range.start * let end: T = range.end * var first: Bool = true * while (iter < end || first) { * if (first) { * first = false * } else { * iter += step // step defaults to 1 * // note that a non-constant step will result desugaring to ForInIter * } * let i = iter // For user declared 'i', which should be immutable if been captured. * if (iter <= end && guard) { * body * } * } * ************** please note the following case: * for (i in 0..10) { * print(i.toString()) * continue * } */ void TypeChecker::TypeCheckerImpl::DesugarForInCloseRange(ASTContext& ctx, AST::ForInExpr& forInExpr) { CJC_ASSERT(forInExpr.inExpression->astKind == ASTKind::RANGE_EXPR); auto rangeExpr = StaticCast(forInExpr.inExpression.get()); // To : var iter-i = range.start auto index = CreateIndexVar(forInExpr, *rangeExpr); // To: let end = range.stop auto rangeStop = CreateStopVar(*rangeExpr); // To : var first: Bool = true auto firstFlag = CreateFirstFlag(*rangeExpr); bool increasing = !rangeExpr->stepExpr || !rangeExpr->stepExpr->constNumValue.asInt.IsNegativeNum(); auto whileExpr = MakeOwnedNode(); whileExpr->sugarKind = Expr::SugarKind::FOR_IN_EXPR; CopyBasicInfo(&forInExpr, whileExpr.get()); // To : while (i < end || first) auto firstFlagRef = GetVarRef(*firstFlag, *rangeStop); // Notice: need use non-close compare here. auto rangeCondExpr = CreateRangeCond(forInExpr, *index, *rangeStop, increasing ? TokenKind::LT : TokenKind::GT); rangeCondExpr->begin = firstFlagRef->begin = rangeExpr->begin; rangeCondExpr->end = firstFlagRef->end = rangeExpr->end; whileExpr->condExpr = CreateBinaryExpr( std::move(rangeCondExpr), std::move(firstFlagRef), TokenKind::OR); CopyBasicInfo(&forInExpr, whileExpr->condExpr.get()); whileExpr->body = MakeOwnedNode(); CopyBasicInfo(&forInExpr, whileExpr->body.get()); // For update the iter 'iter += step'. auto update = CreateUpdate(forInExpr, *rangeExpr, *index); AddCurFile(*update, forInExpr.curFile); Ptr updateTy = SynthesizeWithoutRecover(ctx, update.get()); // Need syn to desugar. CJC_ASSERT(updateTy && updateTy->kind != TypeKind::TYPE_INVALID); // Create first if-else part in while body. auto firstRun = CreateFirstIf(forInExpr, *firstFlag, std::move(update)); whileExpr->body->body.push_back(std::move(firstRun)); // For 'let i = iter'. if (auto vp = DynamicCast(forInExpr.pattern.get()); vp) { auto letValue = CreateLetIter(ctx, *vp, forInExpr, *index); // Put let value declaration between. whileExpr->body->body.push_back(std::move(letValue)); } // To: if (iter <= end && guard) { body } auto rangeCond = CreateRangeCond(forInExpr, *index, *rangeStop, increasing ? TokenKind::LE : TokenKind::GE); auto condBody = CreateGuard(forInExpr, *rangeCond, true); whileExpr->body->body.push_back(std::move(condBody)); whileExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); whileExpr->body->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); auto refWhile = whileExpr.get(); auto blockExpr = MakeOwnedNode(); CopyBasicInfo(&forInExpr, blockExpr.get()); blockExpr->body.push_back(std::move(index)); blockExpr->body.push_back(std::move(rangeStop)); blockExpr->body.push_back(std::move(firstFlag)); blockExpr->begin = whileExpr->begin; blockExpr->end = whileExpr->end; blockExpr->body.push_back(std::move(whileExpr)); AddCurFile(*blockExpr, forInExpr.curFile); Ptr blockExprTy = SynthesizeWithoutRecover(ctx, blockExpr.get()); CJC_ASSERT(blockExprTy && blockExprTy->kind != TypeKind::TYPE_INVALID); /* must do after synthesize */ RearrangeRefLoop(forInExpr, *refWhile, refWhile->body.get()); // Add file for all created ast. forInExpr.desugarExpr = std::move(blockExpr); ctx.DeleteInvertedIndexes(forInExpr.inExpression.get()); forInExpr.inExpression = nullptr; } /** * Desugar ForInExpr of non-close range to while expr. for example: * *************** before desugar **************** * for (int in range: step where guard) { * body * } * *************** after desugar **************** * var iter = range.start * let end: T = range.end * while (iter < end) { * let i = iter // For user declared 'i', which should be immutable if been captured. * iter += step * if (guard) { * body * } * } * ************** please note the following case: * for (i in 0..10) { * print(i.toString()) * continue * } */ void TypeChecker::TypeCheckerImpl::DesugarForInNonCloseRange(ASTContext& ctx, AST::ForInExpr& forInExpr) { CJC_ASSERT(forInExpr.inExpression->astKind == ASTKind::RANGE_EXPR); auto rangeExpr = StaticCast(forInExpr.inExpression.get()); // To : var iter-i = range.start auto index = CreateIndexVar(forInExpr, *rangeExpr); // To: let end = range.stop auto rangeStop = CreateStopVar(*rangeExpr); // To : where (i < end) bool increasing = !rangeExpr->stepExpr || !rangeExpr->stepExpr->constNumValue.asInt.IsNegativeNum(); auto rangeCond = CreateRangeCond(forInExpr, *index, *rangeStop, increasing ? TokenKind::LT : TokenKind::GT); // To: if (guard) { body } auto condBody = CreateGuard(forInExpr, *rangeCond, false); auto whileExpr = MakeOwnedNode(); whileExpr->sugarKind = Expr::SugarKind::FOR_IN_EXPR; CopyBasicInfo(&forInExpr, whileExpr.get()); whileExpr->condExpr = std::move(rangeCond); whileExpr->body = MakeOwnedNode(); CopyBasicInfo(&forInExpr, whileExpr->body.get()); // To : 'let i = iter' if (auto vp = DynamicCast(forInExpr.pattern.get()); vp) { auto letValue = CreateLetIter(ctx, *vp, forInExpr, *index); // Put let value declaration between. whileExpr->body->body.push_back(std::move(letValue)); } // To: 'iter += step' auto update = CreateUpdate(forInExpr, *rangeExpr, *index); AddCurFile(*update, forInExpr.curFile); Ptr updateTy = SynthesizeWithoutRecover(ctx, update.get()); CJC_ASSERT(updateTy && updateTy->kind != TypeKind::TYPE_INVALID); whileExpr->body->body.push_back(std::move(update)); CopyBasicInfo(condBody->thenBody.get(), whileExpr->body.get()); whileExpr->body->body.push_back(std::move(condBody)); whileExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); whileExpr->body->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); auto refWhile = whileExpr.get(); auto blockExpr = MakeOwnedNode(); CopyBasicInfo(&forInExpr, blockExpr.get()); blockExpr->body.push_back(std::move(index)); blockExpr->body.push_back(std::move(rangeStop)); blockExpr->body.push_back(std::move(whileExpr)); AddCurFile(*blockExpr, forInExpr.curFile); Ptr blockExprTy = SynthesizeWithoutRecover(ctx, blockExpr.get()); /* must do after synthesize */ RearrangeRefLoop(forInExpr, *refWhile, refWhile->body.get()); CJC_ASSERT(blockExprTy && blockExprTy->kind != TypeKind::TYPE_INVALID); // Add file for all created ast. forInExpr.desugarExpr = std::move(blockExpr); ctx.DeleteInvertedIndexes(forInExpr.inExpression.get()); forInExpr.inExpression = nullptr; } void TypeChecker::TypeCheckerImpl::ReArrangeForInRangeExpr(ASTContext& ctx, ForInExpr& forInExpr) { CJC_ASSERT(forInExpr.inExpression->astKind == ASTKind::RANGE_EXPR); auto re = DynamicCast(forInExpr.inExpression.get()); auto rangeExpr = StaticCast(forInExpr.inExpression.get()); // To : var iter-i = range.start auto index = CreateIndexVar(forInExpr, *rangeExpr); // To: let end = range.stop auto rangeStop = CreateStopVar(*rangeExpr); // To : where (i < end) bool increasing = !rangeExpr->stepExpr || !rangeExpr->stepExpr->constNumValue.asInt.IsNegativeNum(); auto rangeCond = CreateRangeCond(forInExpr, *index, *rangeStop, increasing ? (re->isClosed ? TokenKind::LE : TokenKind::LT) : (re->isClosed ? TokenKind::GE : TokenKind::GT)); // To : 'let i = iter' if (auto vp = DynamicCast(forInExpr.pattern.get()); vp) { auto letValue = MakeOwnedNode(); CopyBasicInfo(forInExpr.pattern, letValue.get()); AddCurFile(*letValue, forInExpr.curFile); // To: let i = iter-i letValue = std::move(vp->varDecl); letValue->parentPattern = nullptr; auto initRef = GetVarRef(*index, *index); initRef->begin = index->begin; initRef->end = index->end; letValue->initializer = std::move(initRef); vp->varDecl = std::move(letValue); } auto update = CreateUpdate(forInExpr, *rangeExpr, *index); CopyBasicInfo(forInExpr.inExpression, update.get()); AddCurFile(*update, forInExpr.curFile); Ptr updateTy = SynthesizeWithoutRecover(ctx, update.get()); CJC_ASSERT(updateTy && updateTy->kind != TypeKind::TYPE_INVALID); // composite block structure auto blockExpr = MakeOwnedNode(); CopyBasicInfo(forInExpr.inExpression, blockExpr.get()); AddCurFile(*blockExpr, forInExpr.curFile); blockExpr->body.push_back(std::move(index)); blockExpr->body.push_back(std::move(rangeStop)); blockExpr->body.push_back(std::move(update)); blockExpr->body.push_back(std::move(rangeCond)); blockExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); forInExpr.inExpression = std::move(blockExpr); forInExpr.forInKind = ForInKind::FORIN_RANGE; /* setup flag for jumpExpr: continue/break */ RearrangeRefLoop(forInExpr, forInExpr, forInExpr.body.get()); } void TypeChecker::TypeCheckerImpl::ReArrangeForInStringExpr(ASTContext& ctx, ForInExpr& forInExpr) { // To : var iter = 0 auto litZero = CreateLitConstExpr(LitConstKind::INTEGER, "0", TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64)); auto index = CreateVarDecl(ITER_COMPILER, std::move(litZero)); ctx.AddDeclName(std::make_pair(ITER_COMPILER, forInExpr.scopeName), *index); CopyBasicInfo(forInExpr.pattern, index.get()); index->ty = index->initializer->ty; index->isVar = true; index->begin = forInExpr.pattern->begin; index->end = forInExpr.pattern->end; // To : var tmp = e1 auto tmp = CreateTmpVarDecl(); tmp->initializer = std::move(forInExpr.inExpression); CopyBasicInfo(tmp->initializer.get(), tmp.get()); tmp->ty = tmp->initializer->ty; tmp->isVar = false; // To: let end = tmp.size auto end = CreateTmpVarDecl(); auto sizeDecl = FieldLookup(ctx, Ty::GetDeclPtrOfTy(tmp->ty), "size", {.lookupExtend = false}); CJC_ASSERT(!sizeDecl.empty()); auto getter = StaticCast(GetUsedMemberDecl(*sizeDecl[0], true)); CJC_NULLPTR_CHECK(getter); end->initializer = CreateCallExpr(CreateMemberAccess(CreateRefExpr(*tmp), *getter), {}, getter, sizeDecl[0]->ty); end->initializer->begin = tmp->begin; end->initializer->end = tmp->end; CopyBasicInfo(&forInExpr, end.get()); end->begin = tmp->begin; end->end = tmp->end; AddCurFile(*end->initializer, forInExpr.curFile); end->ty = SynthesizeWithoutRecover(ctx, end->initializer.get()); CJC_ASSERT(end->ty && end->ty->kind != TypeKind::TYPE_INVALID); // To : 'let pat = tmp[iter]' if (auto vp = DynamicCast(forInExpr.pattern.get())) { auto base = CreateMemberAccess(CreateRefExpr(*tmp, *end), "[]"); base->ty = Ty::GetInitialTy(); CopyBasicInfo(end, base->baseExpr.get()); std::vector> args; args.emplace_back(CreateFuncArg(CreateRefExpr(*index, *end))); CopyBasicInfo(end, args[0]->expr.get()); auto ce = CreateCallExpr(std::move(base), std::move(args)); CopyBasicInfo(end, ce.get()); AddCurFile(*ce, forInExpr.curFile); auto ceTy = SynthesizeWithoutRecover(ctx, ce.get()); CJC_ASSERT(ceTy && ceTy->kind != TypeKind::TYPE_INVALID); vp->varDecl->initializer = std::move(ce); } auto blockExpr = MakeOwnedNode(); AddCurFile(*blockExpr, forInExpr.curFile); CopyBasicInfo(tmp->initializer, blockExpr.get()); blockExpr->body.push_back(std::move(index)); blockExpr->body.push_back(std::move(tmp)); blockExpr->body.push_back(std::move(end)); blockExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); forInExpr.inExpression = std::move(blockExpr); forInExpr.forInKind = ForInKind::FORIN_STRING; /* setup flag for jumpExpr: continue/break */ RearrangeRefLoop(forInExpr, forInExpr, forInExpr.body.get()); } void TypeChecker::TypeCheckerImpl::ReArrangeForInIterExpr(ASTContext& ctx, ForInExpr& forInExpr) { // To : let iter = e.iterator() auto init = MakeOwnedNode(); auto inExprBegin = forInExpr.inExpression->GetBegin(); auto inExprEnd = forInExpr.inExpression->GetEnd(); auto initBase = MakeOwnedNode(); initBase->baseExpr = std::move(forInExpr.inExpression); initBase->field = "iterator"; init->baseFunc = std::move(initBase); AddCurFile(*init, forInExpr.curFile); SynthesizeWithoutRecover(ctx, init.get()); RawStaticCast(init->baseFunc.get())->target = init->resolvedFunction; auto iterator = CreateVarDecl(ITER_COMPILER, std::move(init)); iterator->begin = forInExpr.pattern->begin; iterator->end = forInExpr.pattern->end; auto matchExpr = MakeOwnedNode(); matchExpr->matchMode = true; matchExpr->begin = iterator->begin; matchExpr->end = iterator->end; // To : iter.next() { auto next = MakeOwnedNode(); auto nextBase = MakeOwnedNode(); nextBase->baseExpr = CreateRefExpr(*iterator); nextBase->field = "next"; next->begin = nextBase->begin = init->begin; next->end = nextBase->end = init->end; next->baseFunc = std::move(nextBase); AddCurFile(*next, forInExpr.curFile); SynthesizeWithoutRecover(ctx, next.get()); RawStaticCast(next->baseFunc.get())->target = next->resolvedFunction; matchExpr->selector = std::move(next); } matchExpr->ty = matchExpr->selector->ty; auto optionDecl = importManager.GetCoreDecl(STD_LIB_OPTION); auto somePattern = MakeOwnedNode(); AddCurFile(*somePattern, forInExpr.curFile); CopyBasicInfo(&forInExpr, somePattern.get()); somePattern->begin = inExprBegin; somePattern->end = inExprEnd; somePattern->ty = matchExpr->selector->ty; auto someRef = MakeOwnedNode(); someRef->ref.identifier = OPTION_VALUE_CTOR; someRef->ref.target = LookupEnumMember(optionDecl, OPTION_VALUE_CTOR); someRef->ty = typeManager.GetFunctionTy(somePattern->ty->typeArgs, somePattern->ty); somePattern->constructor = std::move(someRef); somePattern->patterns.emplace_back(std::move(forInExpr.pattern)); forInExpr.pattern = std::move(somePattern); auto blockExpr = MakeOwnedNode(); AddCurFile(*blockExpr, forInExpr.curFile); CopyBasicInfo(&forInExpr, blockExpr.get()); blockExpr->begin = inExprBegin; blockExpr->end = inExprEnd; blockExpr->body.push_back(std::move(iterator)); blockExpr->body.push_back(std::move(matchExpr)); blockExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); forInExpr.inExpression = std::move(blockExpr); forInExpr.forInKind = ForInKind::FORIN_ITER; /* setup flag for jumpExpr: continue/break */ RearrangeRefLoop(forInExpr, forInExpr, forInExpr.body.get()); } /** * Desugar ForInExpr to while expr. for example: * *************** before desugar **************** * for (pat in e1 where e2) { * b * } * *************** after desugar **************** * let iter = e1.iterator() * while (true) { * match (iter.next()) { * case None => break * case Some(v) => match v { * case pat where e2 => b * case _ => continue * } * } * } */ void TypeChecker::TypeCheckerImpl::DesugarForInIter(ASTContext& ctx, AST::ForInExpr& forInExpr) { // To : let iter = e.iterator() auto init = MakeOwnedNode(); auto initBase = MakeOwnedNode(); auto inExprBegin = forInExpr.inExpression->begin; auto inExprEnd = forInExpr.inExpression->end; initBase->baseExpr = std::move(forInExpr.inExpression); initBase->field = "iterator"; initBase->begin = init->begin = inExprBegin; initBase->end = init->end = inExprEnd; init->baseFunc = std::move(initBase); AddCurFile(*init, forInExpr.curFile); SynthesizeWithoutRecover(ctx, init.get()); RawStaticCast(init->baseFunc.get())->target = init->resolvedFunction; auto iterator = CreateVarDecl(ITER_COMPILER, std::move(init)); iterator->begin = forInExpr.pattern->begin; iterator->end = forInExpr.pattern->end; // To : while true auto whileExpr = MakeOwnedNode(); whileExpr->sugarKind = Expr::SugarKind::FOR_IN_EXPR; whileExpr->begin = forInExpr.body->begin; whileExpr->end = forInExpr.body->end; whileExpr->condExpr = MakeOwned(LitConstKind::BOOL, "true"); whileExpr->condExpr->begin = inExprBegin; whileExpr->condExpr->end = inExprEnd; whileExpr->condExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); whileExpr->EnableAttr(Attribute::IMPLICIT_ADD); auto matchExpr = MakeOwnedNode(); CJC_ASSERT(forInExpr.body); matchExpr->ty = forInExpr.body->ty; matchExpr->matchMode = true; matchExpr->begin = inExprBegin; matchExpr->end = inExprEnd; // To : iter.next() { auto next = MakeOwnedNode(); next->begin = forInExpr.begin; auto nextBase = MakeOwnedNode(); nextBase->baseExpr = CreateRefExpr(*iterator); nextBase->begin = inExprBegin; nextBase->end = inExprEnd; nextBase->field = "next"; next->baseFunc = std::move(nextBase); AddCurFile(*next, forInExpr.curFile); SynthesizeWithoutRecover(ctx, next.get()); RawStaticCast(next->baseFunc.get())->target = next->resolvedFunction; matchExpr->selector = std::move(next); } auto optionDecl = importManager.GetCoreDecl(STD_LIB_OPTION); // To : case None => break matchExpr->matchCases.push_back(CreateNoneCaseForForInIter(optionDecl, *whileExpr, matchExpr->selector->ty)); // To : case Some(v) => match (v) { case pat if e2 => b; case _ => continue} auto caseSome = CreateSomeCaseForForInIter(optionDecl, *whileExpr, typeManager.GetFunctionTy(matchExpr->selector->ty->typeArgs, matchExpr->selector->ty), forInExpr); matchExpr->matchCases.push_back(std::move(caseSome)); whileExpr->body = MakeOwnedNode(); whileExpr->body->body.push_back(std::move(matchExpr)); whileExpr->body->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); whileExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); // Connect vardecl and while expr. auto blockExpr = MakeOwnedNode(); blockExpr->body.push_back(std::move(iterator)); blockExpr->body.push_back(std::move(whileExpr)); CopyBasicInfo(&forInExpr, blockExpr.get()); blockExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); AddCurFile(*blockExpr, forInExpr.curFile); forInExpr.desugarExpr = std::move(blockExpr); } /** * Desugar ForInExpr to while expr. for example: * *************** before desugar **************** * for (pat in e1 where e2) { * b * } * *************** after desugar **************** * var iter = 0 * let tmp = e1 * let end: T = tmp.size * while (iter < end) { * pat = tmp[iter]; // For user declared 'pat', which should be immutable if been captured. * iter += 1 * if (guard) { * body * } * } */ void TypeChecker::TypeCheckerImpl::DesugarForInString(ASTContext& ctx, AST::ForInExpr& forInExpr) { // To : var iter = 0 auto litZero = CreateLitConstExpr(LitConstKind::INTEGER, "0", TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64)); auto index = CreateVarDecl(ITER_COMPILER, std::move(litZero)); ctx.AddDeclName(std::make_pair(ITER_COMPILER, forInExpr.scopeName), *index); CopyBasicInfo(&forInExpr, index.get()); index->ty = index->initializer->ty; index->isVar = true; // To : var tmp = e1 auto tmp = CreateTmpVarDecl(); tmp->initializer = std::move(forInExpr.inExpression); CopyBasicInfo(tmp->initializer.get(), tmp.get()); tmp->ty = tmp->initializer->ty; tmp->isVar = false; // To: let end = tmp.size auto end = CreateTmpVarDecl(); auto sizeDecl = FieldLookup(ctx, Ty::GetDeclPtrOfTy(tmp->ty), "size", {.lookupExtend = false}); CJC_ASSERT(!sizeDecl.empty()); auto getter = StaticCast(GetUsedMemberDecl(*sizeDecl[0], true)); CJC_NULLPTR_CHECK(getter); end->initializer = CreateCallExpr(CreateMemberAccess(CreateRefExpr(*tmp), *getter), {}, getter, sizeDecl[0]->ty); CopyBasicInfo(&forInExpr, end.get()); AddCurFile(*end->initializer, forInExpr.curFile); end->ty = SynthesizeWithoutRecover(ctx, end->initializer.get()); CJC_ASSERT(end->ty && end->ty->kind != TypeKind::TYPE_INVALID); // To : where (iter < end) auto condition = CreateBinaryExpr(GetVarRef(*index), GetVarRef(*end), TokenKind::LT); condition->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); // To: if (guard) { body } auto condBody = CreateGuard(forInExpr, *condition, false); auto whileExpr = MakeOwnedNode(); whileExpr->sugarKind = Expr::SugarKind::FOR_IN_EXPR; CopyBasicInfo(&forInExpr, whileExpr.get()); whileExpr->condExpr = std::move(condition); whileExpr->body = MakeOwnedNode(); CopyBasicInfo(&forInExpr, whileExpr->body.get()); // To : 'let pat = tmp[iter]' if (auto vp = DynamicCast(forInExpr.pattern.get())) { auto base = CreateMemberAccess(CreateRefExpr(*tmp), "[]"); base->ty = Ty::GetInitialTy(); CopyBasicInfo(&forInExpr, base->baseExpr.get()); std::vector> args; args.emplace_back(CreateFuncArg(CreateRefExpr(*index))); CopyBasicInfo(&forInExpr, args[0]->expr.get()); auto ce = CreateCallExpr(std::move(base), std::move(args)); CopyBasicInfo(&forInExpr, ce.get()); AddCurFile(*ce, forInExpr.curFile); auto ceTy = SynthesizeWithoutRecover(ctx, ce.get()); CJC_ASSERT(ceTy && ceTy->kind != TypeKind::TYPE_INVALID); auto letValue = std::move(vp->varDecl); forInExpr.pattern.reset(); letValue->initializer = std::move(ce); // Put let value declaration between. whileExpr->body->body.push_back(std::move(letValue)); } // To: 'iter += 1' auto update = MakeOwnedNode(); update->leftValue = GetVarRef(*index); update->rightExpr = CreateLitConstExpr(LitConstKind::INTEGER, "1", TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64)); update->op = TokenKind::ADD_ASSIGN; update->isCompound = true; AddCurFile(*update, forInExpr.curFile); Ty* updateTy = SynthesizeWithoutRecover(ctx, update.get()); CJC_ASSERT(updateTy && updateTy->kind != TypeKind::TYPE_INVALID); whileExpr->body->body.push_back(std::move(update)); whileExpr->body->body.push_back(std::move(condBody)); whileExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); whileExpr->body->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); auto refWhile = whileExpr.get(); auto blockExpr = MakeOwnedNode(); CopyBasicInfo(&forInExpr, blockExpr.get()); blockExpr->body.push_back(std::move(index)); blockExpr->body.push_back(std::move(tmp)); blockExpr->body.push_back(std::move(end)); blockExpr->body.push_back(std::move(whileExpr)); AddCurFile(*blockExpr, forInExpr.curFile); Ty* blockExprTy = SynthesizeWithoutRecover(ctx, blockExpr.get()); /* must do after synthesize */ RearrangeRefLoop(forInExpr, *refWhile, refWhile->body.get()); CJC_ASSERT(blockExprTy && blockExprTy->kind != TypeKind::TYPE_INVALID); // Add file for all created ast. forInExpr.desugarExpr = std::move(blockExpr); } /// Returns true if range expr \ref expr has step ±1 and types of both start and stop exprs are integer. /// A range expr without step expression equals step 1. static bool IsStepOne(const RangeExpr& expr) { if (expr.startExpr->ty->IsInvalid() || !expr.startExpr->ty->IsInteger()) { return false; } if (expr.stopExpr->ty->IsInvalid() || !expr.stopExpr->ty->IsInteger()) { return false; } if (!expr.stepExpr) { return true; } return expr.stepExpr->isConst && (expr.stepExpr->constNumValue.asInt.Int64() == 1 || expr.stepExpr->constNumValue.asInt.Int64() == -1); } void TypeChecker::TypeCheckerImpl::DesugarForInExpr(ASTContext& ctx, ForInExpr& forInExpr) { if (!Ty::IsTyCorrect(forInExpr.ty) || diag.GetErrorCount() != 0 || forInExpr.desugarExpr != nullptr) { return; } auto ty = forInExpr.inExpression->ty; // only support RangeExpr here, because we want to determine at compile time: // 1. whether it is closed range // 2. whether it has custom step expression // 3. whether that custom step expression is constant if (ty && ty->IsRange() && ty->typeArgs[0] && ty->typeArgs[0]->IsInteger()) { if (auto re = DynamicCast(forInExpr.inExpression.get()); re && IsStepOne(*re)) { return re->isClosed ? DesugarForInCloseRange(ctx, forInExpr) : DesugarForInNonCloseRange(ctx, forInExpr); } } if (ty->IsString()) { DesugarForInString(ctx, forInExpr); } else { DesugarForInIter(ctx, forInExpr); } } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND static bool IsCallRangeConstructor(const CallExpr& call) { if (call.callKind != CallKind::CALL_OBJECT_CREATION) { return false; } auto callee = DynamicCast(&*call.baseFunc); if (!callee) { return false; } auto calledFunc = DynamicCast(callee->ref.target); if (!calledFunc) { return false; } auto& params = calledFunc->funcBody->paramLists[0]->params; constexpr size_t rangeConstructorParamSize{6}; if (params.size() != rangeConstructorParamSize) { return false; } auto b = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); constexpr size_t rangeConstructorIntLength{3}; for (size_t i{0}; i < rangeConstructorIntLength; ++i) { if (!call.args[i]->ty->IsInteger()) { return false; } } if (auto step = DynamicCast(&*call.args[rangeConstructorIntLength - 1]->expr)) { if (auto cvalue = step->constNumValue.asInt.Int64(); cvalue != 1 && cvalue != -1) { return false; } } else { return false; } for (size_t i{rangeConstructorIntLength}; i < rangeConstructorParamSize; ++i) { if (call.args[i]->ty != b) { return false; } } if (auto isClosed = DynamicCast(&*call.args[rangeConstructorParamSize - 1]->expr)) { return isClosed->constNumValue.asBoolean; } else { return false; } } bool ForInExpr::IsClosedRangeOne() const { auto range = DynamicCast(inExpression.get()); if (!range) { if (auto call = DynamicCast(&*inExpression)) { return IsCallRangeConstructor(*call); } return false; } if (range->desugarExpr) { if (auto call = DynamicCast(&*range->desugarExpr)) { return IsCallRangeConstructor(*call); } return false; } if (range->isClosed && IsStepOne(*range)) { return true; } return false; } void TypeChecker::TypeCheckerImpl::ReArrangeForInExpr(ASTContext& ctx, ForInExpr& forInExpr) { if (!Ty::IsTyCorrect(forInExpr.ty) || diag.GetErrorCount() != 0 || forInExpr.desugarExpr != nullptr) { return; } auto ty = forInExpr.inExpression->ty; // only support RangeExpr here, because we want to determine at compile time: // 1. whether it is closed range // 2. whether it has custom step expression // 3. whether that custom step expression is constant if (forInExpr.IsClosedRangeOne()) { // translate to CHIR directly, no desugaring return; } if (ty && ty->IsRange() && ty->typeArgs[0] && ty->typeArgs[0]->IsInteger()) { if (auto re = DynamicCast(forInExpr.inExpression.get()); re && IsStepOne(*re)) { return ReArrangeForInRangeExpr(ctx, forInExpr); } } if (ty->IsString()) { ReArrangeForInStringExpr(ctx, forInExpr); } else { // rollback to old desugar for performance issue DesugarForInExpr(ctx, forInExpr); } } #endif cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/FuncParam.cpp000066400000000000000000000134161510705540100253030ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Desugar/AfterTypeCheck.h" #include "cangjie/AST/ASTCasting.h" using namespace Cangjie; using namespace AST; namespace { void RemoveSubScriptTypeLineInfo(const Expr& assignmentExpr) { if (assignmentExpr.desugarExpr == nullptr || assignmentExpr.desugarExpr->astKind != Cangjie::AST::ASTKind::CALL_EXPR || RawStaticCast(assignmentExpr.desugarExpr.get())->args.empty()) { return; } auto arrayCallExpr = RawStaticCast(assignmentExpr.desugarExpr.get()); assignmentExpr.desugarExpr->begin.Mark(PositionStatus::IGNORE); auto arrayArg = arrayCallExpr->args[0].get(); bool isRefLiteral = Utils::In(arrayArg->expr->astKind, {AST::ASTKind::LIT_CONST_EXPR, AST::ASTKind::REF_EXPR, AST::ASTKind::ARRAY_LIT, AST::ASTKind::TUPLE_LIT, AST::ASTKind::SUBSCRIPT_EXPR}); if (isRefLiteral) { arrayArg->expr->begin.Mark(PositionStatus::IGNORE); arrayArg->begin.Mark(PositionStatus::IGNORE); if (arrayArg->expr->astKind == AST::ASTKind::SUBSCRIPT_EXPR) { RemoveSubScriptTypeLineInfo(*arrayArg->expr); } } } void CleanExprLineInfo(Expr& expr); void CleanCallExprLineInfo(CallExpr& ce) { bool isEnumCtor = ce.resolvedFunction && ce.resolvedFunction->TestAttr(Attribute::ENUM_CONSTRUCTOR); auto bid = DynamicCast(ce.baseFunc ? ce.baseFunc->GetTarget() : nullptr); bool isCStrCtor = bid && bid->type == BuiltInType::CSTRING; if (isCStrCtor) { ce.begin.Mark(PositionStatus::IGNORE); } if (isEnumCtor || isCStrCtor) { for (auto& arg : ce.args) { CleanExprLineInfo(*arg->expr); } } } void CleanExprLineInfo(Expr& expr) { bool isRefLiteral = Utils::In(expr.astKind, {AST::ASTKind::LIT_CONST_EXPR, AST::ASTKind::REF_EXPR, AST::ASTKind::RANGE_EXPR}); bool isArrayLit = expr.astKind == AST::ASTKind::ARRAY_LIT; bool isTupleLit = expr.astKind == AST::ASTKind::TUPLE_LIT; bool isUnsafeBlock = expr.astKind == ASTKind::BLOCK && expr.TestAttr(Attribute::UNSAFE); if (isRefLiteral) { expr.begin.Mark(PositionStatus::IGNORE); if (expr.desugarExpr) { expr.desugarExpr->begin.Mark(PositionStatus::IGNORE); } } else if (isArrayLit) { auto& arrayExpr = static_cast(expr); expr.begin.Mark(PositionStatus::IGNORE); for (auto& child : arrayExpr.children) { CleanExprLineInfo(*child.get()); } } else if (isTupleLit) { auto& tupleExpr = static_cast(expr); expr.begin.Mark(PositionStatus::IGNORE); for (auto& child : tupleExpr.children) { CleanExprLineInfo(*child.get()); } } else if (isUnsafeBlock) { auto& blk = static_cast(expr); expr.begin.Mark(PositionStatus::IGNORE); blk.unsafePos.Mark(PositionStatus::IGNORE); for (auto& body : blk.body) { if (auto be = DynamicCast(body.get()); be) { CleanExprLineInfo(*be); } } } else if (auto ce = DynamicCast(&expr)) { CleanCallExprLineInfo(*ce); } else if (expr.astKind == AST::ASTKind::SUBSCRIPT_EXPR && expr.desugarExpr) { // if an expr is subscript like array[0], remove the line number of index. // the type of index should be in the filter set, too. expr.begin.Mark(PositionStatus::IGNORE); expr.desugarExpr->begin.Mark(PositionStatus::IGNORE); if (expr.desugarExpr->astKind != AST::ASTKind::CALL_EXPR) { return; } auto refExpr = RawStaticCast(expr.desugarExpr.get()); if (refExpr->baseFunc->astKind != AST::ASTKind::MEMBER_ACCESS) { return; } auto ma = RawStaticCast(refExpr->baseFunc.get()); if (!ma->baseExpr->ty->IsStructArray()) { return; } ma->baseExpr->begin.Mark(PositionStatus::IGNORE); RemoveSubScriptTypeLineInfo(expr); } } void ProcessDefaultParamLineInfo(FuncDecl& fd) { // Every default parameter only have one element in their funcBody. if (!fd.funcBody || !fd.funcBody->body || fd.funcBody->body->body.empty() || fd.funcBody->body->body[0]->astKind != AST::ASTKind::RETURN_EXPR) { return; } auto paramAssignment = RawStaticCast(fd.funcBody->body->body[0].get()); bool isRefLiteral = Utils::In(paramAssignment->expr->astKind, {AST::ASTKind::LIT_CONST_EXPR, AST::ASTKind::REF_EXPR, AST::ASTKind::ARRAY_LIT, AST::ASTKind::TUPLE_LIT, AST::ASTKind::SUBSCRIPT_EXPR, AST::ASTKind::RANGE_EXPR}); bool isUnsafeBlock = paramAssignment->expr->astKind == ASTKind::BLOCK && paramAssignment->expr->TestAttr(Attribute::UNSAFE); if (isRefLiteral || isUnsafeBlock) { paramAssignment->begin.Mark(PositionStatus::IGNORE); } fd.begin.Mark(PositionStatus::IGNORE); CleanExprLineInfo(*paramAssignment->expr); } } // namespace namespace Cangjie::Sema::Desugar::AfterTypeCheck { /** * For the default parameter, if assigned by a LIT_CONST_EXPR type, Ref type, or Subscript type. * which means it does not need line number for debug. * So, remove the assignment line number from desugared function. */ void PostProcessFuncParam(const FuncParam& fp, const GlobalOptions& options) { if (!fp.desugarDecl || !fp.desugarDecl->funcBody->body) { return; } if (!options.enableCoverage) { ProcessDefaultParamLineInfo(*fp.desugarDecl); } } } // namespace Cangjie::Sema::Desugar::AfterTypeCheck cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/IfExpr.cpp000066400000000000000000000041761510705540100246270ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Desugar/AfterTypeCheck.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; using namespace Sema::Desugar::AfterTypeCheck; namespace { /** * Insert unitExpr if the type of 'ifExpr' @p ie is unit type but the 'thenBody' is not the type of unit. * Also complete the 'elseBody' of the 'ifExpr' if it was not existed. */ void InsertUnitForIfExpr(TypeManager& tyMgr, IfExpr& ie) { if (ie.desugarExpr) { return; // Ignore desugared expression. } // All expression after typecheck must be welltyped. CJC_NULLPTR_CHECK(ie.ty); CJC_NULLPTR_CHECK(ie.thenBody); // If the 'ifExpr' is not unit typed or the type of then body is the subtype of unit type, then quit process. auto skip = !ie.ty->IsUnit() || tyMgr.IsSubtype(ie.thenBody->ty, ie.ty); if (skip) { return; } // If the type of 'then' is not unit, then the block must not be empty. CJC_ASSERT(!ie.thenBody->body.empty()); auto unitExpr = CreateUnitExpr(ie.ty); CopyBasicInfo(ie.thenBody->body.back().get(), unitExpr.get()); ie.thenBody->body.push_back(std::move(unitExpr)); ie.thenBody->ty = ie.ty; // If current ifExpr dose not have elseBody, create for it. if (!ie.elseBody) { // Added 'else' does not need position. auto elseBody = MakeOwnedNode(); elseBody->body.push_back(CreateUnitExpr(ie.ty)); elseBody->ty = ie.ty; ie.elseBody = std::move(elseBody); ie.hasElse = true; AddCurFile(*ie.elseBody, ie.curFile); } } } // namespace namespace Cangjie::Sema::Desugar::AfterTypeCheck { void DesugarIfExpr(TypeManager& typeManager, IfExpr& ifExpr) { InsertUnitForIfExpr(typeManager, ifExpr); } } // namespace Cangjie::Sema::Desugar::AfterTypeCheck cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/IsExpr.cpp000066400000000000000000000036461510705540100246450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Desugar/AfterTypeCheck.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Utils.h" using namespace Cangjie; using namespace TypeCheckUtil; namespace Cangjie::Sema::Desugar::AfterTypeCheck { /** * Desugar IsExpr to TypePattern of MatchExpr. * *************** before desugar **************** * e is T * *************** after desugar **************** * match (e) { * case _: T => true * case _ => false * } * */ void DesugarIsExpr(TypeManager& typeManager, IsExpr& ie) { if (!Ty::IsTyCorrect(ie.ty) || !ie.ty->IsBoolean() || ie.desugarExpr) { return; } CJC_NULLPTR_CHECK(ie.leftExpr); CJC_NULLPTR_CHECK(ie.isType); CJC_NULLPTR_CHECK(ie.leftExpr->ty); CJC_NULLPTR_CHECK(ie.isType->ty); auto boolTy = ie.ty; std::vector> matchCases; auto trueExpr = CreateLitConstExpr(LitConstKind::BOOL, "true", boolTy); auto falseExpr = CreateLitConstExpr(LitConstKind::BOOL, "false", boolTy); trueExpr->begin = ie.isPos; falseExpr->begin = ie.isPos; matchCases.emplace_back(CreateMatchCase( CreateRuntimePreparedTypePattern( typeManager, MakeOwnedNode(), std::move(ie.isType), *ie.leftExpr ), std::move(trueExpr))); auto wildcard = MakeOwnedNode(); wildcard->ty = ie.leftExpr->ty; matchCases.emplace_back( CreateMatchCase(std::move(wildcard), std::move(falseExpr))); ie.desugarExpr = CreateMatchExpr(std::move(ie.leftExpr), std::move(matchCases), boolTy, Expr::SugarKind::IS); AddCurFile(*ie.desugarExpr, ie.curFile); } } // namespace Cangjie::Sema::Desugar::AfterTypeCheck cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/Package.cpp000066400000000000000000000206471510705540100247660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Desugar/AfterTypeCheck.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/AST/ASTCasting.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; namespace { bool ShouldInsertReturnUnit(const FuncBody& fb) { if (!fb.body) { return false; // Invalid node. } // An empty function body {} has type Unit. We change {} to { return () }. if (fb.body->body.empty()) { return true; } // When a function body's last expression is a declaration, insert a return () expression after it. if (fb.body->body.back()->IsDecl()) { return true; } // Adding return () for constructors for the sake of backend compatibility, currently. if (fb.funcDecl) { if (fb.funcDecl->TestAnyAttr(Attribute::PRIMARY_CONSTRUCTOR, Attribute::CONSTRUCTOR)) { return true; } } // If a function or lambda's return type is ANNOTATED as Unit, then insert return (). // A trick: after type checking, if no error is reported, then e : Unit is a fact. So the check here is not needed. if (fb.retType && fb.retType->ty && fb.retType->ty->IsUnit()) { Ptr last = fb.body->body.back().get(); while (last && last->astKind == ASTKind::PAREN_EXPR) { last = StaticAs(last)->expr.get(); } // Should not insert unit if the last expression is return or is of type `Unit`, // instead we should `MakeLastNodeReturn`. if (last == nullptr || last->astKind == ASTKind::RETURN_EXPR || (last->astKind == ASTKind::LIT_CONST_EXPR && StaticAs(last)->kind == LitConstKind::UNIT)) { return false; } return true; } return false; } /** * Add return e when current function body's last expression isn't 'return e' or * current funcbody need to add' return ()'. */ void InsertUnitForFuncBody(FuncBody& fb, bool enableCoverage) { if (fb.body == nullptr) { return; } auto ue = CreateUnitExpr(TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)); ue->curFile = fb.curFile; auto re = CreateReturnExpr(std::move(ue)); re->curFile = fb.curFile; re->ty = TypeManager::GetNothingTy(); re->refFuncBody = &fb; bool notHaveRightCurlPos = fb.funcDecl && fb.body->rightCurlPos.IsZero(); // If compiled with `--coverage`, the inserted `return ()` should contain the line no of funcDecl, // thus, it will not result in redundant coverage reports. if (enableCoverage) { re->begin = fb.funcDecl ? fb.funcDecl->begin : fb.body->leftCurlPos; } else { /** * If the function has right curly brackets, like: * * func foo { * } // setting the return expression line info * * or * * class A { * init() { * } // setting the return expression line info * } * * If the function does not have right curly brackets, like the following init function: * * class A { // setting the return expression line info * var a = 10 * } * */ re->begin = notHaveRightCurlPos ? fb.funcDecl->end : fb.body->rightCurlPos; } fb.body->body.emplace_back(std::move(re)); } void MakeLastNodeReturn(FuncBody& funcBody) { if (!funcBody.body || funcBody.body->body.empty()) { return; } auto lastNode = funcBody.body->body.rbegin(); if (auto e = DynamicCast(lastNode->get()); e && e->astKind != ASTKind::RETURN_EXPR) { auto lastExpr = OwnedPtr(StaticAs(lastNode->release())); auto re = CreateReturnExpr(std::move(lastExpr)); CopyBasicInfo(e, re.get()); re->ty = TypeManager::GetNothingTy(); funcBody.body->ty = TypeManager::GetNothingTy(); re->refFuncBody = &funcBody; AddCurFile(*re, funcBody.curFile); *lastNode = std::move(re); } } inline void AddReturnExprForFuncBody(FuncBody& fb, bool enableCoverage) { if (ShouldInsertReturnUnit(fb)) { InsertUnitForFuncBody(fb, enableCoverage); } else if (Ty::IsTyCorrect(fb.ty)) { MakeLastNodeReturn(fb); } } void InsertStaticInitCall(InheritableDecl& decl, FuncDecl& staticInit) { if (staticInit.TestAttr(Attribute::FROM_COMMON_PART)) { // Static init was assigned as initializer in common part. return; } // Create and insert static initializing as the static member "let $init = static_init()" into typedecl. auto unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); std::vector> args; auto initializer = CreateCallExpr(CreateRefExpr(staticInit), std::move(args), &staticInit, unitTy); initializer->begin = decl.begin; auto initVar = CreateVarDecl(STATIC_INIT_VAR, std::move(initializer)); initVar->fullPackageName = decl.fullPackageName; initVar->outerDecl = &decl; initVar->EnableAttr(Attribute::STATIC, Attribute::PRIVATE); initVar->toBeCompiled = staticInit.toBeCompiled; initVar->fullPackageName = decl.fullPackageName; // Caller guarantees the 'decl' is class or struct. decl.IsClassLikeDecl() ? initVar->EnableAttr(Attribute::IN_CLASSLIKE) : initVar->EnableAttr(Attribute::IN_STRUCT); AddCurFile(*initVar, decl.curFile); (void)decl.GetMemberDecls().emplace_back(std::move(initVar)); } /** * Adding calling point of user defined static init as static member, eg: "private static let $init = static_init()" * eg: class A { * static var a : String * static init() { a = "str" } * } * to * class A { * static var a : String * static init() { a = "str" } * private static let $init = static_init() * } */ void AddStaticInitForTypeDecl(InheritableDecl& decl) { Ptr staticInitfb = nullptr; for (auto& member : decl.GetMemberDecls()) { CJC_ASSERT(member); if (!member->TestAttr(Attribute::STATIC)) { continue; } if (member->astKind == ASTKind::FUNC_DECL && member->TestAttr(Attribute::CONSTRUCTOR)) { // Valid static constructor must be funcDecl. CJC_ASSERT(member->astKind == ASTKind::FUNC_DECL); auto fd = StaticAs(member.get()); staticInitfb = fd->funcBody.get(); } } if (staticInitfb == nullptr) { return; // Current type decl does not need to modify static init. } CJC_ASSERT(staticInitfb->funcDecl); // Update name of 'static init', this function cannot be called by user, so user will not perceive changes. staticInitfb->funcDecl->identifier = STATIC_INIT_FUNC; InsertStaticInitCall(decl, *staticInitfb->funcDecl); } inline void CopyNecessaryAnnoForPropDecl(PropDecl& pd) { const bool hasFrozen = pd.HasAnno(AnnotationKind::FROZEN); auto clonePropAnno = [&pd, hasFrozen](OwnedPtr& fd) { auto clonedAnnos = ASTCloner::CloneVector(pd.annotations); std::move(clonedAnnos.begin(), clonedAnnos.end(), std::back_inserter(fd->annotations)); fd->isFrozen = hasFrozen; }; std::for_each(pd.getters.begin(), pd.getters.end(), clonePropAnno); std::for_each(pd.setters.begin(), pd.setters.end(), clonePropAnno); } } // namespace namespace Cangjie::Sema::Desugar::AfterTypeCheck { void DesugarDeclsForPackage(Package& pkg, bool enableCoverage) { Walker(&pkg, [enableCoverage](auto node) { CJC_ASSERT(node); if (node->astKind == ASTKind::CLASS_DECL || node->astKind == ASTKind::STRUCT_DECL) { AddStaticInitForTypeDecl(*StaticCast(node)); } else if (node->astKind == ASTKind::FUNC_BODY) { AddReturnExprForFuncBody(*StaticAs(node), enableCoverage); } else if (node->astKind == ASTKind::PROP_DECL) { CopyNecessaryAnnoForPropDecl(*StaticAs(node)); } return VisitAction::WALK_CHILDREN; }).Walk(); } } // namespace Cangjie::Sema::Desugar::AfterTypeCheck cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/RangeExpr.cpp000066400000000000000000000104141510705540100253150ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Desugar/AfterTypeCheck.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; namespace { std::vector> CreateRangeExprArgs(const RangeExpr& re) { std::vector> args; if (re.startExpr != nullptr) { args.push_back(CreateFuncArg(ASTCloner::Clone(re.startExpr.get()))); } else { // If startExpr does not exist, set LitConst "0" as default value. auto startExpr = CreateLitConstExpr(LitConstKind::INTEGER, "0", re.ty->typeArgs[0]); args.push_back(CreateFuncArg(std::move(startExpr))); } if (re.stopExpr != nullptr) { args.push_back(CreateFuncArg(ASTCloner::Clone(re.stopExpr.get()))); } else { // If stopExpr does not exist, set LitConst "0" as default value. auto stopExpr = CreateLitConstExpr( LitConstKind::INTEGER, std::to_string(std::numeric_limits::max()), re.ty->typeArgs[0]); args.push_back(CreateFuncArg(std::move(stopExpr))); } if (re.stepExpr != nullptr) { args.push_back(CreateFuncArg(ASTCloner::Clone(re.stepExpr.get()))); } else { // If stepExpr does not exist, set LitConst "1" as default value. auto stepExpr = CreateLitConstExpr(LitConstKind::INTEGER, "1", TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64)); args.push_back(CreateFuncArg(std::move(stepExpr))); } std::string hasStart = re.startExpr ? "true" : "false"; std::string hasStop = re.stopExpr ? "true" : "false"; std::string isClosed = re.isClosed ? "true" : "false"; auto hasStartExpr = CreateLitConstExpr(LitConstKind::BOOL, hasStart, TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN)); auto hasStopExpr = CreateLitConstExpr(LitConstKind::BOOL, hasStop, TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN)); auto isClosedExpr = CreateLitConstExpr(LitConstKind::BOOL, isClosed, TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN)); args.push_back(CreateFuncArg(std::move(hasStartExpr))); args.push_back(CreateFuncArg(std::move(hasStopExpr))); args.push_back(CreateFuncArg(std::move(isClosedExpr))); return args; } } // namespace namespace Cangjie::Sema::Desugar::AfterTypeCheck { void DesugarRangeExpr(TypeManager& typeManager, RangeExpr& re) { // Desugar of RangeExpr is done after typeCheck. if (!re.decl) { // RangeExpr in for-in expr does not have decl set. return; } CJC_NULLPTR_CHECK(re.decl->generic); CJC_NULLPTR_CHECK(re.ty); if (re.desugarExpr) { return; } if (re.ty->typeArgs.empty() || re.ty->typeArgs.size() != re.decl->generic->typeParameters.size() || !Ty::IsTyCorrect(re.decl->generic->typeParameters[0]->ty)) { return; } auto rangeFunc = CreateRefExpr(re.decl->identifier); CopyBasicInfo(&re, rangeFunc.get()); (void)rangeFunc->instTys.emplace_back(re.ty->typeArgs[0]); TypeSubst typeMapping; typeMapping.emplace(StaticCast(re.decl->generic->typeParameters[0]->ty), re.ty->typeArgs[0]); std::vector> args = CreateRangeExprArgs(re); auto ce = CreateCallExpr(std::move(rangeFunc), std::move(args)); ce->ty = re.ty; for (auto& initFn : re.decl->body->decls) { if (auto fd = AST::As(initFn.get()); fd && IsInstanceConstructor(*fd)) { if (auto refExpr = AST::As(ce->baseFunc.get()); refExpr) { ReplaceTarget(refExpr, fd, false); CJC_NULLPTR_CHECK(fd->ty); refExpr->ty = typeManager.GetInstantiatedTy(fd->ty, typeMapping); ce->resolvedFunction = fd; ce->callKind = CallKind::CALL_OBJECT_CREATION; } break; } } CopyBasicInfo(&re, ce.get()); AddCurFile(*ce, re.curFile); re.desugarExpr = std::move(ce); } } // namespace Cangjie::Sema::Desugar::AfterTypeCheck cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/SemanticUsageCollector.cpp000066400000000000000000000371671510705540100300370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Desugar/AfterTypeCheck.h" #include "ExtendBoxMarker.h" #include "TypeCheckUtil.h" #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Utils.h" #include "cangjie/IncrementalCompilation/IncrementalScopeAnalysis.h" #include "cangjie/Mangle/ASTMangler.h" #include "cangjie/Sema/IncrementalUtils.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; // Incremental compilation only enable in cjnative backend for now. #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND namespace { class SemanticUsageCollector { public: explicit SemanticUsageCollector(TypeManager& typeManager, const std::vector>& pkgs) : tyMgr(typeManager), pkgs(pkgs) { tyMgr.ClearRecordUsedExtends(); // Unset previous collection status. } ~SemanticUsageCollector() { currentTypeDecl = nullptr; } SemanticInfo CollectInfoUsages() { for (auto pkg : pkgs) { IterateToplevelDecls(*pkg, [this](auto& decl) { CollectForDecl(*decl); }); } return info; } private: void CollectForDecl(const Decl& decl) { if (decl.TestAnyAttr(Attribute::MACRO_INVOKE_FUNC, Attribute::IMPLICIT_ADD, Attribute::ENUM_CONSTRUCTOR)) { // 1. ignore macro invoke function and implicit added decls. // 2. Enum ctor does not have rawMangledName, it is considered as part of EnumDecl. return; } if (decl.TestAttr(Attribute::INCRE_COMPILE)) { info.usages.emplace(&decl, SemaUsage{}); // If decl is unchanged, only create empty entry. return; } if (auto vd = DynamicCast(&decl)) { CollectAPIUsage(*vd); CollectNameUsage(*vd); } else if (auto md = DynamicCast(&decl); md && md->desugarDecl) { CollectForDecl(*md->desugarDecl); } else if (auto fd = DynamicCast(&decl)) { CollectAPIUsage(*fd); CollectNameUsage(*fd); } else if (auto id = DynamicCast(&decl)) { currentTypeDecl = id; CollectAPIUsage(*id); CollectAnnotationUsage(*id, info.usages[id]); CollectRelation(*id); for (auto& member : id->GetMemberDeclPtrs()) { if (auto pd = DynamicCast(member)) { CollectAPIUsage(*pd); std::for_each(pd->getters.begin(), pd->getters.end(), [this](auto& it) { CollectForDecl(*it); }); std::for_each(pd->setters.begin(), pd->setters.end(), [this](auto& it) { CollectForDecl(*it); }); } else { // NOTE: PrimaryCtorDecl will be ignored by default. CollectForDecl(*member); } } currentTypeDecl = nullptr; } } static std::string GetName(const Node& node) { if (auto rt = DynamicCast(&node)) { return rt->ref.identifier; } else if (auto re = DynamicCast(&node)) { return re->ref.identifier; } else if (auto qt = DynamicCast(&node)) { return qt->field; } else if (auto ma = DynamicCast(&node)) { return ma->field; } return ""; } void CollectUseOfParentByTy(const Ty& ty, UseInfo& usage, NameUsage& nameUsage) const { // When accessed field is member of a type, we need to collect the accessed type. if (auto typeDecl = Ty::GetDeclPtrOfTy(&ty)) { (void)nameUsage.parentDecls.emplace(typeDecl->rawMangleName); // Only need to collect type of node when it is a baseExpr of memberAccess. (void)usage.usedDecls.emplace(typeDecl->rawMangleName); } else if (Ty::IsTyCorrect(&ty)) { (void)nameUsage.parentDecls.emplace(ASTMangler::MangleBuiltinType(Ty::KindName(ty.kind))); } } void CollectUseOfUnqualifedMember(const Decl& decl, UseInfo& usage) const { if (decl.IsTypeDecl() || !decl.outerDecl || !decl.outerDecl->IsNominalDecl() || decl.TestAttr(Attribute::CONSTRUCTOR)) { return; } // Collect real parent of accessd parent. CJC_NULLPTR_CHECK(decl.outerDecl->ty); auto& nameUsage = usage.usedNames[decl.identifier]; CollectUseOfParentByTy(*decl.outerDecl->ty, usage, nameUsage); if (currentTypeDecl && Ty::IsTyCorrect(currentTypeDecl->ty)) { // Collect current 'this' parent decl. CollectUseOfParentByTy(*currentTypeDecl->ty, usage, nameUsage); } } void CollectUseOfQualifedMember( const MemberAccess& ma, const Decl& target, UseInfo& usage, NameUsage& nameUsage) const { CJC_NULLPTR_CHECK(target.outerDecl); auto accessedTy = ma.isExposedAccess ? target.outerDecl->ty : ma.baseExpr->ty; if (Ty::IsTyCorrect(accessedTy)) { CollectUseOfParentByTy(*accessedTy, usage, nameUsage); } } void CollectForEnumAndStructTypeUse(const Node& node, UseInfo& usage) const { if (!Ty::IsTyCorrect(node.ty) || (!node.ty->IsEnum() && !node.ty->IsStruct())) { return; } auto ed = Ty::GetDeclPtrOfTy(node.ty); CJC_NULLPTR_CHECK(ed); usage.usedDecls.emplace(ed->rawMangleName); } VisitAction CollectUseInfo(const Node& node, UseInfo& usage) const { CollectForEnumAndStructTypeUse(node, usage); auto target = Is(node) ? Ty::GetDeclPtrOfTy(node.ty) : node.GetTarget(); if (target == nullptr || target->IsBuiltIn() || target->astKind == ASTKind::PACKAGE_DECL) { return VisitAction::WALK_CHILDREN; } // Ignore decl usage for compiler added decl which does not have raw mangled name. if (!target->rawMangleName.empty()) { usage.usedDecls.emplace(target->rawMangleName); } // Also record parent decl's usage when 'target' is constructor. if (IsClassOrEnumConstructor(*target)) { (void)usage.usedDecls.emplace(target->outerDecl->rawMangleName); usage.usedDecls.emplace(target->outerDecl->rawMangleName); // When 'target' is compiler added constructor, collect usage of constructed mangledName. if (target->TestAttr(Attribute::IMPLICIT_ADD)) { usage.usedDecls.emplace(target->outerDecl->rawMangleName + ""); } } // We need to collect reference's real name as used name (because the name may be alias). auto name = GetName(node); if (name.empty()) { return VisitAction::WALK_CHILDREN; } auto& nameUsage = usage.usedNames[name]; if (node.astKind == ASTKind::REF_TYPE || node.astKind == ASTKind::REF_EXPR) { if (target->TestAttr(Attribute::IMPORTED)) { nameUsage.hasUnqualifiedUsageOfImported = true; } else { nameUsage.hasUnqualifiedUsage = true; } CollectUseOfUnqualifedMember(*target, usage); } else if (auto qt = DynamicCast(&node)) { auto qualifier = ASTContext::GetPackageName(qt->baseType.get()); nameUsage.packageQualifiers.emplace(qualifier); return VisitAction::SKIP_CHILDREN; } else if (auto ma = DynamicCast(&node)) { CJC_NULLPTR_CHECK(ma->baseExpr); if (target->TestAttr(Attribute::GLOBAL)) { // Only collect qualifier when target is gloabl decl. auto qualifier = ASTContext::GetPackageName(ma->baseExpr.get()); nameUsage.packageQualifiers.emplace(qualifier); return VisitAction::SKIP_CHILDREN; } CollectUseOfQualifedMember(*ma, *target, usage, nameUsage); } return VisitAction::WALK_CHILDREN; } void CollectAPIUsage(const VarDecl& vd) { if (vd.type) { CJC_ASSERT(!vd.rawMangleName.empty()); CollectAPIUsage(*vd.type, info.usages[&vd].apiUsages); } } void CollectAPIUsage(Type& type, UseInfo& usage) const { Walker(&type, [this, &usage](auto node) { // Ignore implicit added nodes, these will base on user defined code and core package. if (node->TestAttr(Attribute::IMPLICIT_ADD)) { return VisitAction::SKIP_CHILDREN; } return CollectUseInfo(*node, usage); }).Walk(); } void CollectGenericUsage(const Generic& generic, UseInfo& usage) const { for (auto& gc : generic.genericConstraints) { for (const auto& upperBound : gc->upperBounds) { auto decl = Ty::GetDeclPtrOfTy(upperBound->ty); if (decl && !decl->rawMangleName.empty()) { usage.usedDecls.emplace(decl->rawMangleName); } } } } void CollectAPIUsage(const FuncDecl& fd) { CJC_NULLPTR_CHECK(fd.funcBody); CJC_ASSERT(fd.funcBody->paramLists.size() == 1 && !fd.rawMangleName.empty()); auto& usage = info.usages[&fd].apiUsages; for (auto& param : fd.funcBody->paramLists[0]->params) { CJC_NULLPTR_CHECK(param->type); CollectAPIUsage(*param->type, usage); } if (fd.funcBody->retType && !fd.funcBody->retType->TestAttr(Attribute::COMPILER_ADD)) { CollectAPIUsage(*fd.funcBody->retType, usage); } if (fd.funcBody->generic) { CollectGenericUsage(*fd.funcBody->generic, usage); } } void CollectAPIUsage(const InheritableDecl& id) { CJC_ASSERT(!id.rawMangleName.empty()); auto& usage = info.usages[&id].apiUsages; for (auto& type : id.inheritedTypes) { CollectAPIUsage(*type, usage); } if (id.generic) { CollectGenericUsage(*id.generic, usage); } auto ed = DynamicCast(&id); if (!ed) { return; } for (auto& ctor : ed->constructors) { auto fd = DynamicCast(ctor.get()); if (!fd) { continue; } CJC_ASSERT(fd->funcBody->paramLists.size() == 1); for (auto& param : fd->funcBody->paramLists[0]->params) { CJC_NULLPTR_CHECK(param->type); CollectAPIUsage(*param->type, usage); } } } void CollectRelation(const InheritableDecl& id) { // Ignore for non-extend decls which does not have 'inheritedTypes'. if (id.inheritedTypes.empty() && id.astKind != ASTKind::EXTEND_DECL) { return; } CJC_ASSERT(!id.rawMangleName.empty()); CJC_NULLPTR_CHECK(id.ty); auto decl = Ty::GetDeclPtrOfTy(id.ty); CJC_ASSERT(!decl || !decl->rawMangleName.empty()); auto& relation = decl ? info.relations[decl->rawMangleName] : info.builtInTypeRelations[ASTMangler::MangleBuiltinType(Ty::KindName(id.ty->kind))]; bool isExtend = false; if (Is(&id)) { relation.extends.emplace(id.rawMangleName); isExtend = true; } auto& inherited = isExtend ? relation.extendedInterfaces : relation.inherits; for (auto& type : id.inheritedTypes) { auto target = Ty::GetDeclPtrOfTy(type->ty); // Type without target will never be valid inherited type. CJC_ASSERT(target && !target->rawMangleName.empty()); inherited.emplace(target->rawMangleName); } } void CollectAnnotationUsage(const Decl& decl, SemaUsage& usage) const { if (decl.annotationsArray) { for (auto& it : decl.annotationsArray->children) { CollectNameUsage(*it, usage); } } } void CollectNameUsage(const VarDecl& vd) { auto& usage = info.usages[&vd]; if (vd.initializer) { // NOTE: To collect boxing relation correctly, we need pass varDecl itself. CollectNameUsage(const_cast(vd), usage); } CollectAnnotationUsage(vd, usage); } void CollectNameUsage(const FuncDecl& fd) { CJC_NULLPTR_CHECK(fd.funcBody); CJC_ASSERT(fd.funcBody->paramLists.size() == 1 && !fd.rawMangleName.empty()); auto& usage = info.usages[&fd]; for (auto& param : fd.funcBody->paramLists[0]->params) { CJC_NULLPTR_CHECK(param); if (param->assignment) { CollectNameUsage(*param->assignment, usage); } CollectAnnotationUsage(*param, usage); } // Abstract functions may omit func body. if (fd.funcBody->body) { CollectNameUsage(*fd.funcBody->body, usage); } if (fd.TestAttr(Attribute::CONSTRUCTOR)) { AddUsedBySpecificMemberVars(fd, fd.TestAttr(Attribute::STATIC)); } CollectAnnotationUsage(fd.propDecl ? *StaticCast(fd.propDecl) : StaticCast(fd), usage); } void CollectNameUsage(Node& node, SemaUsage& usage) const { { std::lock_guard lockGuard{ExtendBoxMarker::mtx}; Walker(&node, ExtendBoxMarker::GetMarkExtendBoxFunc(tyMgr)).Walk(); } auto boxedTys = tyMgr.GetAllBoxedTys(); tyMgr.ClearRecordUsedExtends(); // Unset collection status. for (auto ty : boxedTys) { usage.boxedTypes.emplace(Sema::GetTypeRawMangleName(*ty)); } auto nodePtr = &node; if (node.astKind == ASTKind::VAR_DECL) { // After collecting usage of boxing, change node from vardecl to its initializer. nodePtr = StaticCast(node).initializer.get(); } Walker(nodePtr, [this, &usage](auto n) { // Ignore implicit added default argument. if (n->TestAttr(Attribute::HAS_INITIAL) && n->astKind == ASTKind::FUNC_ARG) { return VisitAction::SKIP_CHILDREN; } return CollectUseInfo(*n, usage.bodyUsages); }).Walk(); } void AddUsedBySpecificMemberVars(const FuncDecl& ctor, bool isStatic) { CJC_NULLPTR_CHECK(ctor.outerDecl); auto& members = ctor.outerDecl->GetMemberDecls(); for (auto& member : members) { CJC_NULLPTR_CHECK(member); if (member->astKind != ASTKind::VAR_DECL || member->TestAttr(Attribute::STATIC) != isStatic) { continue; } auto vd = StaticCast(member.get()); if (!vd->isMemberParam && !vd->initializer) { // Static/non-static variable without initializer should be considered // as implicitly using static initializer/instance constructor. auto& usage = info.usages[vd]; usage.bodyUsages.usedDecls.emplace(ctor.rawMangleName); } } } TypeManager& tyMgr; const std::vector>& pkgs; SemanticInfo info; Ptr currentTypeDecl{nullptr}; }; } // namespace #endif namespace Cangjie::Sema::Desugar::AfterTypeCheck { SemanticInfo GetSemanticUsage(TypeManager& typeManager, const std::vector>& pkgs) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND SemanticUsageCollector collector(typeManager, pkgs); return collector.CollectInfoUsages(); #endif } } // namespace Cangjie::Sema::Desugar::AfterTypeCheck cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/SpawnExpr.cpp000066400000000000000000000101441510705540100253510ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Utils.h" using namespace Cangjie; using namespace AST; // The desugar of a `SpawnArg` is the form `arg.getSchedulerHandle()` void TypeChecker::TypeCheckerImpl::DesugarSpawnArgExpr(const ASTContext& ctx, const AST::SpawnExpr& se) { if (!Ty::IsTyCorrect(se.ty) || se.arg->desugarExpr) { return; } Ptr arg = se.arg.get(); CJC_ASSERT(arg && Ty::IsTyCorrect(arg->ty) && arg->ty->IsClassLike()); // Get `getSchedulerHandle` method from spawn argument, whose signature is `()->CPointer`. auto classLikeTy = StaticCast(arg->ty); auto retTy = typeManager.GetPointerTy(TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)); auto funcTy = typeManager.GetFunctionTy({}, retTy); const auto fieldName = "getSchedulerHandle"; auto decls = FieldLookup(ctx, classLikeTy->commonDecl, fieldName, {.file = se.curFile}); CJC_ASSERT(decls.size() == 1); // `getSchedulerHandle(): CPointer` is the private method. auto decl = decls.front(); CJC_ASSERT(Ty::IsTyCorrect(decl->ty) && decl->ty->IsFunc() && typeManager.IsSubtype(decl->ty, funcTy)); auto ma = CreateMemberAccess(ASTCloner::Clone(arg), fieldName); CopyBasicInfo(arg, ma.get()); auto ce = CreateCallExpr(std::move(ma), {}); CopyBasicInfo(arg, ce.get()); ce->callKind = CallKind::CALL_DECLARED_FUNCTION; ce->resolvedFunction = StaticCast(decl); ce->ty = retTy; AddCurFile(*ce, se.curFile); arg->desugarExpr = std::move(ce); } // The `futureObj` of a `SpawnExpr` is `VarDecl` of the form `let futureObj = Future(task)` // NOTE: This syntax sugar is stored in `futureObj` rather than `desugarExpr`. void TypeChecker::TypeCheckerImpl::DesugarSpawnExpr(const ASTContext& ctx, AST::SpawnExpr& se) { if (!Ty::IsTyCorrect(se.ty) || se.futureObj) { return; } Ptr task = se.task.get(); CJC_ASSERT(task && Ty::IsTyCorrect(task->ty) && task->ty->IsFunc()); Ptr taskTy = RawStaticCast(task->ty); CJC_ASSERT(se.ty->IsClass()); Ptr futureClass = RawStaticCast(se.ty)->declPtr; CJC_ASSERT(Ty::IsTyCorrect(futureClass->ty) && futureClass->ty->typeArgs.size() == 1); std::vector> inits; for (auto& decl : futureClass->GetMemberDecls()) { CJC_NULLPTR_CHECK(decl); if (decl->astKind == ASTKind::FUNC_DECL && decl.get()->TestAttr(Attribute::CONSTRUCTOR)) { inits.emplace_back(StaticCast(decl.get())); } } CJC_ASSERT(inits.size() == 1); // `init(fn: ()->T)` is the only constructor for `Future` auto initDecl = inits.front(); CJC_ASSERT(Ty::IsTyCorrect(initDecl->ty) && initDecl->ty->IsFunc()); // Prepare the `baseFunc` of the `Future` function call. auto re = CreateRefExprInCore("Future"); re->isAlone = false; re->ref.target = initDecl; re->instTys.emplace_back(taskTy->retTy); re->ty = typeManager.GetInstantiatedTy( initDecl->ty, {{StaticCast(futureClass->ty->typeArgs.front()), taskTy->retTy}}); CopyBasicInfo(task, re.get()); // Prepare the arguments of the `CallExpr`. std::vector> callArgs; auto fa = CreateFuncArg(std::move(se.task)); CopyBasicInfo(task, fa.get()); fa->ty = task->ty; callArgs.emplace_back(std::move(fa)); // Create the `CallExpr`. auto ce = CreateCallExpr(std::move(re), std::move(callArgs)); CopyBasicInfo(task, ce.get()); ce->callKind = CallKind::CALL_OBJECT_CREATION; ce->resolvedFunction = initDecl; ce->ty = se.ty; auto futureObj = CreateVarDecl("futureObj", std::move(ce)); CopyBasicInfo(task, futureObj.get()); AddCurFile(*futureObj, se.curFile); se.futureObj = std::move(futureObj); if (!se.arg) { return; } DesugarSpawnArgExpr(ctx, se); } cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/StrInterpolationExpr.cpp000066400000000000000000000215371510705540100276110ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "cangjie/AST/Create.h" using namespace Cangjie; using namespace AST; namespace { std::vector> EstimatingLengthOfString(const StrInterpolationExpr& sie) { size_t size = 0; for (auto& expr : sie.strPartExprs) { if (auto ie = DynamicCast(expr.get())) { if (ie->block->ty->IsPrimitive()) { // 10 is maximum number of digits for Int64. This is an estimate. size += 10; } else { // 32 is default characters length of toString return. This is an estimate. size += 32; } } else if (auto lce = DynamicCast(expr.get())) { size += lce->stringValue.size(); } } auto int64Ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64); auto ctorArg = CreateLitConstExpr(LitConstKind::INTEGER, std::to_string(size), int64Ty); std::vector> args; args.emplace_back(CreateFuncArg(std::move(ctorArg))); return args; } Ptr MatchAppendFuncByParamTy(std::vector> candidate, const Ty& paramTy) { auto matchedAppend = std::find_if(candidate.begin(), candidate.end(), [¶mTy](Ptr d) { // StringBuilder's append function only accept one parameter. if (auto funcTy = DynamicCast(d->ty); funcTy && funcTy->paramTys.size() == 1) { return funcTy->paramTys[0].get() == ¶mTy; } else { return false; } }); return matchedAppend != candidate.end() ? *matchedAppend : nullptr; } } // namespace std::vector> TypeChecker::TypeCheckerImpl::MatchToStringImpl(const ASTContext& ctx, const File& file, Ty& ty) { std::vector> found; if (ty.IsBuiltin()) { found = ExtendFieldLookup(ctx, file, &ty, "toString"); } else { auto decl = Ty::GetDeclPtrOfTy(&ty); found = FieldLookup(ctx, decl, "toString", {.file = &file}); } for (auto it = found.begin(); it != found.end();) { auto stringDecl = importManager.GetCoreDecl(STD_LIB_STRING); CJC_NULLPTR_CHECK(stringDecl); auto funcTy = DynamicCast((*it)->ty); CJC_NULLPTR_CHECK(funcTy); // toString function have no parameter and return type is Struct-String. if (!funcTy->paramTys.empty() || funcTy->retTy != stringDecl->ty) { it = found.erase(it); } else { ++it; } } return found; } OwnedPtr TypeChecker::TypeCheckerImpl::DesugarStrPartExpr( const ASTContext& ctx, Expr& expr, const std::vector> appendDecls, VarDecl& sbItem) { // Create append's argument, must be a primitive type or a subclass of ToString. std::vector> appendArgs; Ptr appendDecl = nullptr; CJC_NULLPTR_CHECK(expr.curFile); if (auto ie = DynamicCast(&expr)) { // For interpolation part. CJC_ASSERT(ie->block && Ty::IsTyCorrect(ie->block->ty)); // Find an 'append' function that matches with ie->block->ty. auto matchedAppend = MatchAppendFuncByParamTy(appendDecls, *ie->block->ty); if (matchedAppend) { appendArgs.emplace_back(CreateFuncArg(std::move(ie->block))); // Select append of the corresponding parameter type version: $tmp.append({...}). appendDecl = matchedAppend; } else { // Find toString function target, if can't find by interpolation expr type, use the version in the ToString // interface. Only one candidate should be eligible. auto found = MatchToStringImpl(ctx, *expr.curFile, *ie->block->ty); if (found.empty()) { auto toStringInterface = importManager.GetCoreDecl(TOSTRING_NAME); found = FieldLookup(ctx, toStringInterface, "toString"); } CJC_ASSERT(found.size() == 1); auto toStringFunc = CreateMemberAccess(std::move(ie->block), *found[0]); CopyBasicInfo(toStringFunc->baseExpr.get(), toStringFunc.get()); auto toStringCall = CreateCallExpr(std::move(toStringFunc), {}); auto funcTy = StaticCast(found[0]->ty); toStringCall->ty = funcTy->retTy; toStringCall->resolvedFunction = DynamicCast(found[0]); appendArgs.emplace_back(CreateFuncArg(std::move(toStringCall))); // Select append of with Struct-String type version, and add toString call: $tmp.append({...}.toString()). appendDecl = MatchAppendFuncByParamTy(appendDecls, *funcTy->retTy); } } else if (auto lce = DynamicCast(&expr)) { // There is always an extra empty string at the end of the interpolation string, and there is no need to // generate an empty string. if (lce->rawString.empty()) { return nullptr; } // For String Literal. appendDecl = MatchAppendFuncByParamTy(appendDecls, *expr.ty); appendArgs.emplace_back(CreateFuncArg(ASTCloner::Clone(Ptr(&expr)))); } CJC_ASSERT(appendDecl); auto appendFunc = CreateMemberAccess(CreateRefExpr(sbItem), *appendDecl); CopyBasicInfo(&expr, appendFunc->baseExpr.get()); CopyBasicInfo(&expr, appendFunc.get()); // sb.append() return type is unit. auto appendCall = CreateCallExpr( std::move(appendFunc), std::move(appendArgs), nullptr, TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)); appendCall->resolvedFunction = DynamicCast(appendDecl); return appendCall; } /** * Desugar InterpolationExpr to Stringbuilder * *************** before desugar **************** * var count: Int64 = 10 * var obj: ToStringInstance = ToStringInstance() * let interps = "There are ${count * count} ${obj}." * *************** after desugar **************** * let interps = { * var $tmpN = StringBuilder() * $tmpN.append("There are ") * $tmpN.append(count * count) * $tmpN.append(" ") * $tmpN.append(obj.toString()) * $tmpN.append(".") * $tmpN.toString() * } * */ void TypeChecker::TypeCheckerImpl::DesugarStrInterpolationExpr(ASTContext& ctx, LitConstExpr& litConstExpr) { CJC_NULLPTR_CHECK(litConstExpr.curFile); auto siexpr = litConstExpr.siExpr.get(); auto blk = MakeOwned(); CopyBasicInfo(siexpr, blk.get()); blk->EnableAttr(Attribute::COMPILER_ADD); blk->ty = siexpr->ty; // Create StringBuilder constructor call. auto sbDecl = importManager.GetCoreDecl("StringBuilder"); CJC_NULLPTR_CHECK(sbDecl); // Roughly estimate the length of the interpolated string, which is used to initialize the StringBuilder capacity. auto sbCtorCall = CreateCallExpr(CreateRefExpr(*sbDecl), EstimatingLengthOfString(*siexpr)); sbCtorCall->baseFunc->EnableAttr(Attribute::IMPLICIT_ADD); CopyBasicInfo(siexpr, sbCtorCall.get()); blk->body.emplace_back(CreateTmpVarDecl(nullptr, sbCtorCall.get())); // Get '$tmpN' var declaration, and synthesize it's ty by initializer expression. auto sbItem = StaticCast(blk->body.front().get()); CopyNodeScopeInfo(siexpr, sbItem); // Local variable names do not require mangling. sbItem->EnableAttr(Attribute::NO_MANGLE); sbItem->ty = SynthesizeWithoutRecover(ctx, sbItem->initializer.get()); // Get all StringBuilder append candidate functions. LookupInfo info{.file = litConstExpr.curFile, .lookupExtend = false}; std::vector> appendDecls = FieldLookup(ctx, sbDecl, "append", info); // Generate append call for every string part expr. for (auto& expr : siexpr->strPartExprs) { // Create '$tmpN.append' call and insert into block. auto appendCall = DesugarStrPartExpr(ctx, *expr, appendDecls, *sbItem); if (appendCall == nullptr) { continue; } blk->body.emplace_back(std::move(appendCall)); } auto field = MatchToStringImpl(ctx, *litConstExpr.curFile, *sbDecl->ty); CJC_ASSERT(field.size() == 1); auto toStringFunc = CreateMemberAccess(CreateRefExpr(*sbItem), *field[0]); CopyBasicInfo(sbCtorCall.get(), toStringFunc->baseExpr.get()); CopyBasicInfo(sbCtorCall.get(), toStringFunc.get()); auto toStringCall = CreateCallExpr(std::move(toStringFunc), {}); toStringCall->ty = blk->ty; // toStringCall ty is Struct-String. toStringCall->resolvedFunction = DynamicCast(field[0]); CopyBasicInfo(sbCtorCall.get(), toStringCall.get()); blk->body.emplace_back(std::move(toStringCall)); AddCurFile(*blk, litConstExpr.curFile); litConstExpr.desugarExpr = std::move(blk); } cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/TryExpr.cpp000066400000000000000000000264151510705540100250470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "Desugar/AfterTypeCheck.h" #include "JoinAndMeet.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Utils.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Utils/CheckUtils.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; using namespace Sema::Desugar::AfterTypeCheck; namespace { // Create `Some`. OwnedPtr CreateRefSome(FuncTy& funcTy) { CJC_ASSERT(Ty::IsTyCorrect(funcTy.retTy) && funcTy.retTy->IsEnum()); EnumTy& enumTy = StaticCast(*funcTy.retTy); auto decl = LookupEnumMember(enumTy.declPtr, OPTION_VALUE_CTOR); CJC_NULLPTR_CHECK(decl); auto refExpr = CreateRefExpr(*decl); (void)refExpr->ref.targets.emplace_back(decl); // for `GetFuncTargets` refExpr->ty = &funcTy; refExpr->instTys = enumTy.typeArgs; return refExpr; } // Create `None`. OwnedPtr CreateRefNone(EnumTy& enumTy) { auto decl = LookupEnumMember(enumTy.declPtr, OPTION_NONE_CTOR); CJC_NULLPTR_CHECK(decl); auto refExpr = CreateRefExpr(*decl); refExpr->ty = &enumTy; refExpr->instTys = enumTy.typeArgs; return refExpr; } // Create `throw v`. OwnedPtr CreateThrowExpr(const VarPattern& vp) { auto ret = MakeOwnedNode(); ret->expr = CreateRefExpr(*vp.varDecl); return ret; } /** Create the following match expression. * match (freshExc) { * case Some(v) => throw v * case None => noneBody * } */ OwnedPtr CreateMatchExpr(FuncTy& someTy, VarDecl& freshExc, OwnedPtr noneBody) { // `case Some(v) => throw v` CJC_ASSERT(Ty::IsTyCorrect(freshExc.ty) && freshExc.ty->IsEnum() && freshExc.ty->typeArgs.size() == 1); auto vp = CreateVarPattern(V_COMPILER, freshExc.ty->typeArgs.front()); auto throwExpr = CreateThrowExpr(*vp); auto somePattern = MakeOwnedNode(); somePattern->constructor = CreateRefSome(someTy); (void)somePattern->patterns.emplace_back(std::move(vp)); auto caseSome = CreateMatchCase(std::move(somePattern), std::move(throwExpr)); // `case None => noneBody` auto nonePattern = MakeOwnedNode(); nonePattern->constructor = CreateRefNone(StaticCast(*freshExc.ty)); auto caseNone = CreateMatchCase(std::move(nonePattern), std::move(noneBody)); // `match` std::vector> matchCases; (void)matchCases.emplace_back(std::move(caseSome)); (void)matchCases.emplace_back(std::move(caseNone)); return CreateMatchExpr(CreateRefExpr(freshExc), std::move(matchCases), nullptr); } // Create `x.isClosed()` or `x.close()`. OwnedPtr CreateCallExpr(VarDecl& vd, const std::string& func) { auto baseFunc = CreateMemberAccess(CreateRefExpr(vd), func); if (baseFunc->target != nullptr && baseFunc->target->astKind == ASTKind::FUNC_DECL) { (void)baseFunc->targets.emplace_back(StaticCast(baseFunc->target)); // for `GetFuncTargets` } CopyBasicInfo(vd.initializer.get(), baseFunc->baseExpr.get()); CopyBasicInfo(vd.initializer.get(), baseFunc.get()); auto callExpr = CreateCallExpr(std::move(baseFunc), {}); CopyBasicInfo(&vd, callExpr.get()); return callExpr; } /** Create the following try-catch expression. * try { * if (!x.isClosed()) { * x.close() * } * } catch (v: Exeption) { * match (freshExc) { * case Some(v) => throw v * case None => throw v * } * } */ OwnedPtr CreateTryInFinally(ClassDecl& exceptionDecl, FuncTy& someTy, VarDecl& x, VarDecl& freshExc) { CJC_ASSERT(Ty::IsTyCorrect(freshExc.ty) && freshExc.ty->typeArgs.size() == 1); auto vp = CreateVarPattern(V_COMPILER, freshExc.ty->typeArgs.front()); // `if` std::vector> thenBlockNodes; (void)thenBlockNodes.emplace_back(CreateCallExpr(x, "close")); auto ifExpr = CreateIfExpr( CreateUnaryExpr(CreateCallExpr(x, "isClosed"), TokenKind::NOT), CreateBlock(std::move(thenBlockNodes))); // `match` auto matchExp = CreateMatchExpr(someTy, freshExc, CreateThrowExpr(*vp)); // `v: Exception` auto exceptTypePattern = MakeOwnedNode(); exceptTypePattern->pattern = std::move(vp); (void)exceptTypePattern->types.emplace_back(CreateRefType(exceptionDecl)); // `try-catch` std::vector> tryBlockNodes; (void)tryBlockNodes.emplace_back(std::move(ifExpr)); std::vector> catchBlockNodes; (void)catchBlockNodes.emplace_back(std::move(matchExp)); auto tryExpr = MakeOwnedNode(); tryExpr->tryBlock = CreateBlock(std::move(tryBlockNodes)); (void)tryExpr->catchPatterns.emplace_back(std::move(exceptTypePattern)); (void)tryExpr->catchBlocks.emplace_back(CreateBlock(std::move(catchBlockNodes))); return tryExpr; } /** Create the following try-catch-finally expression. * try { * tryBlock * } catch (v: Exception) { * freshExc = Some(v) * } finally { * try { * if (!x.isClosed()) { * x.close() * } * } catch (v: Exception) { * match (freshExc) { * case Some(v) => throw v * case None => throw v * } * } * match (freshExc) { * case Some(v) => throw v * case None => () * } * } */ OwnedPtr CreateTryCatchFinally( ClassDecl& exceptionDecl, FuncTy& someTy, VarDecl& x, VarDecl& freshExc, OwnedPtr tryBlock) { CJC_ASSERT(Ty::IsTyCorrect(freshExc.ty) && freshExc.ty->IsEnum() && freshExc.ty->typeArgs.size() == 1); auto vp = CreateVarPattern(V_COMPILER, freshExc.ty->typeArgs.front()); std::vector> args; (void)args.emplace_back(CreateFuncArg(CreateRefExpr(*vp->varDecl))); auto assignExpr = CreateAssignExpr(CreateRefExpr(freshExc), CreateCallExpr(CreateRefSome(someTy), std::move(args))); auto exceptTypePattern = MakeOwnedNode(); exceptTypePattern->pattern = std::move(vp); (void)exceptTypePattern->types.emplace_back(CreateRefType(exceptionDecl)); // `try` std::vector> catchBlockNodes; (void)catchBlockNodes.emplace_back(std::move(assignExpr)); std::vector> finallyBlockNodes; (void)finallyBlockNodes.emplace_back(CreateTryInFinally(exceptionDecl, someTy, x, freshExc)); (void)finallyBlockNodes.emplace_back(CreateMatchExpr(someTy, freshExc, CreateUnitExpr())); auto tryExpr = MakeOwnedNode(); CopyBasicInfo(tryBlock.get(), tryExpr.get()); tryExpr->tryBlock = std::move(tryBlock); (void)tryExpr->catchPatterns.emplace_back(std::move(exceptTypePattern)); (void)tryExpr->catchBlocks.emplace_back(CreateBlock(std::move(catchBlockNodes))); tryExpr->finallyBlock = CreateBlock(std::move(finallyBlockNodes)); return tryExpr; } // Create the outermost try-block. OwnedPtr CreateOuterTryBlock(ClassDecl& exceptionDecl, FuncTy& someTy, EnumTy& noneTy, std::vector>& resourceSpec, OwnedPtr block) { auto tryBlock = std::move(block); for (auto iter = resourceSpec.rbegin(); iter != resourceSpec.rend(); ++iter) { auto noneRef = CreateRefNone(noneTy); auto freshExc = CreateVarDecl(RESOURCE_NAME, std::move(noneRef)); freshExc->isVar = true; auto x = std::move(*iter); auto tryCatchFinally = CreateTryCatchFinally(exceptionDecl, someTy, *x, *freshExc, std::move(tryBlock)); std::vector> tryBlockNodes; (void)tryBlockNodes.emplace_back(std::move(freshExc)); (void)tryBlockNodes.emplace_back(std::move(x)); (void)tryBlockNodes.emplace_back(std::move(tryCatchFinally)); tryBlock = CreateBlock(std::move(tryBlockNodes)); } resourceSpec.clear(); return tryBlock; } } // namespace /** * *************** before desugar *************** * try (x = A()) { * println(x) * } catch (_: Exception) { * println("catch exception") * } finally { * println("finally") * } * *************** after desugar **************** * try { * var freshExc = None * let x = A() * try { * println(x) * } catch (v: Exception) { * freshExc = Some(v) * } finally { * try { * if (!x.isClosed()) { * x.close() * } * } catch (v: Exception) { * match (freshExc) { * case Some(v) => throw v * case None => throw v * } * } * match (freshExc) { * case Some(v) => throw v * case None => () * } * } * } catch (_: Exception) { * println("catch exception") * } finally { * println("finally") * } */ void TypeChecker::TypeCheckerImpl::DesugarTryWithResourcesExpr(ASTContext& ctx, TryExpr& te) { if (te.resourceSpec.empty() || !Ty::IsTyCorrect(te.ty)) { return; } auto exceptionDecl = importManager.GetCoreDecl(CLASS_EXCEPTION); CJC_NULLPTR_CHECK(exceptionDecl); auto optionDecl = StaticCast(importManager.GetCoreDecl("Option")); CJC_NULLPTR_CHECK(optionDecl); auto noneTy = typeManager.GetEnumTy(*optionDecl, {exceptionDecl->ty}); CJC_NULLPTR_CHECK(noneTy); auto someTy = typeManager.GetFunctionTy({exceptionDecl->ty}, noneTy); auto unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); // try-with-resources expression is of type Unit. if (!typeManager.IsSubtype(te.tryBlock->ty, unitTy)) { (void)te.tryBlock->body.emplace_back(CreateUnitExpr(unitTy)); te.tryBlock->ty = unitTy; } auto tryBlock = CreateOuterTryBlock(*exceptionDecl, *someTy, *noneTy, te.resourceSpec, std::move(te.tryBlock)); // Only try-block needs `Synthesize`. `catch` and `finally` were checked by `SynTryWithResourcesExpr`. // And they will be moved to the desugar try expression. SynthesizeWithoutRecover(ctx, tryBlock.get()); CJC_ASSERT(Ty::IsTyCorrect(tryBlock->ty) && tryBlock->ty->IsUnit()); for (auto& block : te.catchBlocks) { CJC_NULLPTR_CHECK(block); if (!typeManager.IsSubtype(block->ty, unitTy)) { (void)block->body.emplace_back(CreateUnitExpr(unitTy)); block->ty = unitTy; } } auto emptyCatchAndFinally = te.catchBlocks.empty() && te.finallyBlock == nullptr; auto finallyBlock = CreateBlock(std::vector>{}, TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)); auto tryExpr = MakeOwnedNode(); tryExpr->tryBlock = std::move(tryBlock); tryExpr->catchPatterns = std::move(te.catchPatterns); tryExpr->catchBlocks = std::move(te.catchBlocks); tryExpr->finallyBlock = emptyCatchAndFinally ? std::move(finallyBlock) : std::move(te.finallyBlock); tryExpr->ty = unitTy; CopyBasicInfo(&te, tryExpr.get()); AddCurFile(*tryExpr, te.curFile); te.desugarExpr = std::move(tryExpr); } cangjie_compiler-1.0.7/src/Sema/Desugar/AfterTypeCheck/Utils.cpp000066400000000000000000000035171510705540100245300ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Desugar/AfterTypeCheck.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Walker.h" #include "cangjie/AST/ASTCasting.h" namespace Cangjie::Sema::Desugar::AfterTypeCheck { Ptr LookupEnumMember(Ptr decl, const std::string& identifier) { if (decl == nullptr || decl->astKind != ASTKind::ENUM_DECL) { return nullptr; } auto enumDecl = RawStaticCast(decl); for (auto& member : enumDecl->constructors) { if (member->identifier == identifier) { return member.get(); } } return nullptr; } void UnitifyBlock(const Expr& posSrc, Block& b, Ty& unitTy) { auto unitExpr = CreateUnitExpr(); unitExpr->begin = posSrc.begin; unitExpr->begin.Mark(PositionStatus::IGNORE); unitExpr->end = posSrc.end; unitExpr->ty = &unitTy; b.body.push_back(std::move(unitExpr)); b.ty = &unitTy; } void RearrangeRefLoop(const Expr& src, Expr& dst, Ptr loopBody) { if (loopBody == nullptr) { return; } std::function)> visitFunc = [&src, &dst](Ptr node) { if (auto je = DynamicCast(node); je) { if (je->refLoop == &src) { je->refLoop = &dst; } } // skip the nested loop structure and lambda if (node->astKind == ASTKind::FUNC_DECL || node->astKind == ASTKind::LAMBDA_EXPR) { return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }; Walker(loopBody, visitFunc).Walk(); } } // namespace Cangjie::Sema::Desugar::AfterTypeCheck cangjie_compiler-1.0.7/src/Sema/Desugar/AutoBoxing.h000066400000000000000000000034321510705540100223070ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares class for auto boxing extend and option. */ #ifndef CANGJIE_SEMA_AUTO_BOX_H #define CANGJIE_SEMA_AUTO_BOX_H #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Walker.h" #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Modules/ImportManager.h" namespace Cangjie { class AutoBoxing { public: explicit AutoBoxing(TypeManager& typeManager) : typeManager(typeManager) { } ~AutoBoxing() = default; void AddOptionBox(AST::Package& pkg); private: /** Boxing functions for option box. */ void TryOptionBox(AST::EnumTy& target, AST::Expr& expr); bool NeedBoxOption(AST::Ty& child, AST::Ty& target); AST::VisitAction AddOptionBoxHandleReturnExpr(const AST::ReturnExpr& re); AST::VisitAction AddOptionBoxHandleVarDecl(const AST::VarDecl& vd); AST::VisitAction AddOptionBoxHandleAssignExpr(const AST::AssignExpr& ae); AST::VisitAction AddOptionBoxHandleCallExpr(AST::CallExpr& ce); AST::VisitAction AddOptionBoxHandleIfExpr(const AST::IfExpr& ie); AST::VisitAction AddOptionBoxHandleTryExpr(AST::TryExpr& te); AST::VisitAction AddOptionBoxHandleArrayLit(AST::ArrayLit& lit); AST::VisitAction AddOptionBoxHandleMatchExpr(AST::MatchExpr& me); AST::VisitAction AddOptionBoxHandleArrayExpr(AST::ArrayExpr& ae); AST::VisitAction AddOptionBoxHandleTupleList(const AST::TupleLit& tl); void AddOptionBoxHandleBlock(AST::Block& block, AST::Ty& ty); TypeManager& typeManager; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Sema/Desugar/CMakeLists.txt000066400000000000000000000005751510705540100226240ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB SEMA_DESUGAR_SRC *.cpp) add_subdirectory(AfterTypeCheck) set(SEMA_SRC ${SEMA_SRC} ${SEMA_DESUGAR_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/Desugar/DesugarAfterInstantiation.cpp000066400000000000000000000336231510705540100257110ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Desugar functions used after instantiation step. */ #include "DesugarInTypeCheck.h" #include #include #include #include #include #include #include "AutoBoxing.h" #include "ExtendBoxMarker.h" #include "TypeCheckUtil.h" #include "TypeCheckerImpl.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Types.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/Match.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace AST; using namespace Meta; using namespace TypeCheckUtil; // Perform desugar after generic instantiation. void TypeChecker::PerformDesugarAfterInstantiation(ASTContext& ctx, Package& pkg) const { impl->PerformDesugarAfterInstantiation(ctx, pkg); } namespace { void UpdateDeclAttributes(Package& pkg, bool exportForTest) { Walker(&pkg, [&exportForTest](auto node) { if (auto vd = DynamicCast(node); vd && vd->initializer) { vd->EnableAttr(Attribute::DEFAULT); } if (exportForTest) { if (auto fd = DynamicCast(node); fd && !fd->TestAttr(Attribute::PRIVATE)) { auto isExtend = Is(fd->outerDecl); auto isForeignFunc = fd->TestAttr(Attribute::FOREIGN); if (!isExtend && !isForeignFunc) { return VisitAction::WALK_CHILDREN; } // Skip declarations added by the compiler because they wouldn't be accessible in tests anyway if (isExtend && fd->outerDecl->TestAttr(Attribute::COMPILER_ADD)) { return VisitAction::WALK_CHILDREN; } fd->linkage = Linkage::EXTERNAL; if (fd->propDecl) { fd->propDecl->linkage = Linkage::EXTERNAL; } if (isExtend) { fd->outerDecl->EnableAttr(Attribute::FOR_TEST); } else { fd->EnableAttr(Attribute::FOR_TEST); } } } return VisitAction::WALK_CHILDREN; }).Walk(); } /** * For compiled with the `--coverage` option, * Clear line info for: * - Compiler add by generic instantiation. */ void ClearLineInfoAfterSema(Package& pkg) { std::function)> clearGenericInst = [](Ptr node) -> VisitAction { node->begin.line = 0; node->begin.column = 0; node->begin.Mark(PositionStatus::IGNORE); return VisitAction::WALK_CHILDREN; }; for (auto& decl : pkg.genericInstantiatedDecls) { Walker(decl, clearGenericInst).Walk(); } } } // namespace void TypeChecker::TypeCheckerImpl::PerformDesugarAfterInstantiation([[maybe_unused]] ASTContext& ctx, Package& pkg) { if (pkg.files.empty()) { return; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (!ci->invocation.globalOptions.disableReflection) { PerformToAnyInsertion(pkg); } #endif PerformRecursiveTypesElimination(); UpdateDeclAttributes(pkg, ci->invocation.globalOptions.exportForTest); if (ci->invocation.globalOptions.enableCoverage) { ClearLineInfoAfterSema(pkg); } } bool AutoBoxing::NeedBoxOption(Ty& child, Ty& target) { if (Ty::IsInitialTy(&child) || Ty::IsInitialTy(&target) || (typeManager.CheckTypeCompatibility(&child, &target, false, target.IsGeneric()) != TypeCompatibility::INCOMPATIBLE) || child.kind == TypeKind::TYPE_NOTHING || target.kind != TypeKind::TYPE_ENUM) { return false; } auto lCnt = CountOptionNestedLevel(child); auto rCnt = CountOptionNestedLevel(target); // If type contains generic ty, current is node inside @Java class. Otherwise, incompatible types need to be boxed. if (lCnt == rCnt && child.HasGeneric()) { return false; } auto enumTy = RawStaticCast(&target); if (enumTy->declPtr->fullPackageName != CORE_PACKAGE_NAME || enumTy->declPtr->identifier != STD_LIB_OPTION) { return false; } return true; } // Option Box happens twice before and after instantiation, and must before extend box. void AutoBoxing::TryOptionBox(EnumTy& target, Expr& expr) { if (expr.ty && target.typeArgs[0] && NeedBoxOption(*expr.ty, *target.typeArgs[0])) { TryOptionBox(*StaticCast(target.typeArgs[0]), expr); } auto ed = target.decl; Ptr optionDecl = nullptr; for (auto& it : ed->constructors) { if (it->identifier == OPTION_VALUE_CTOR) { optionDecl = StaticCast(it.get()); break; } } if (optionDecl == nullptr) { return; } auto baseFunc = CreateRefExpr(OPTION_VALUE_CTOR); baseFunc->EnableAttr(Attribute::IMPLICIT_ADD); baseFunc->ref.target = optionDecl; baseFunc->ty = typeManager.GetInstantiatedTy(optionDecl->ty, GenerateTypeMapping(*ed, target.typeArgs)); std::vector> arg; if (expr.desugarExpr) { arg.emplace_back(CreateFuncArg(std::move(expr.desugarExpr))); } else { arg.emplace_back(CreateFuncArg(ASTCloner::Clone(Ptr(&expr)))); } auto ce = CreateCallExpr(std::move(baseFunc), std::move(arg)); ce->callKind = AST::CallKind::CALL_DECLARED_FUNCTION; ce->ty = ⌖ ce->resolvedFunction = optionDecl; if (expr.astKind == ASTKind::BLOCK) { // For correct deserialization, we need to keep type of block. auto b = MakeOwnedNode(); b->ty = ce->ty; b->body.emplace_back(std::move(ce)); expr.desugarExpr = std::move(b); } else { expr.desugarExpr = std::move(ce); } AddCurFile(*expr.desugarExpr, expr.curFile); expr.ty = expr.desugarExpr->ty; } /** * Option Box happens before type check finished with no errors. * All nodes and sema types should be valid. */ void AutoBoxing::AddOptionBox(Package& pkg) { std::function)> preVisit = [this](Ptr node) -> VisitAction { return match(*node)([this](const VarDecl& vd) { return AddOptionBoxHandleVarDecl(vd); }, [this](const AssignExpr& ae) { return AddOptionBoxHandleAssignExpr(ae); }, [this](CallExpr& ce) { return AddOptionBoxHandleCallExpr(ce); }, [this](const IfExpr& ie) { return AddOptionBoxHandleIfExpr(ie); }, [this](TryExpr& te) { return AddOptionBoxHandleTryExpr(te); }, [this](const ReturnExpr& re) { return AddOptionBoxHandleReturnExpr(re); }, [this](ArrayLit& lit) { return AddOptionBoxHandleArrayLit(lit); }, [this](MatchExpr& me) { return AddOptionBoxHandleMatchExpr(me); }, [this](const TupleLit& tl) { return AddOptionBoxHandleTupleList(tl); }, [this](ArrayExpr& ae) { return AddOptionBoxHandleArrayExpr(ae); }, []() { return VisitAction::WALK_CHILDREN; }); }; Walker walker(&pkg, preVisit); walker.Walk(); } VisitAction AutoBoxing::AddOptionBoxHandleTupleList(const TupleLit& tl) { // Tuple literal allows element been boxed. auto tupleTy = DynamicCast(tl.ty); if (tupleTy == nullptr) { return VisitAction::WALK_CHILDREN; } auto typeArgs = tupleTy->typeArgs; for (size_t i = 0; i < typeArgs.size(); ++i) { if (tl.children[i]->ty && typeArgs[i] && NeedBoxOption(*tl.children[i]->ty, *typeArgs[i])) { TryOptionBox(*StaticCast(typeArgs[i]), *tl.children[i]); } } return VisitAction::WALK_CHILDREN; } VisitAction AutoBoxing::AddOptionBoxHandleMatchExpr(MatchExpr& me) { for (auto& single : me.matchCases) { CJC_ASSERT(me.ty && single->exprOrDecls); AddOptionBoxHandleBlock(*single->exprOrDecls, *me.ty); } for (auto& caseOther : me.matchCaseOthers) { CJC_ASSERT(me.ty && caseOther->exprOrDecls); AddOptionBoxHandleBlock(*caseOther->exprOrDecls, *me.ty); } return VisitAction::WALK_CHILDREN; } VisitAction AutoBoxing::AddOptionBoxHandleArrayLit(ArrayLit& lit) { if (Ty::IsInitialTy(lit.ty) || !lit.ty->IsStructArray()) { return VisitAction::WALK_CHILDREN; } if (lit.ty->typeArgs.size() == 1) { auto targetTy = lit.ty->typeArgs[0]; CJC_NULLPTR_CHECK(targetTy); for (auto& child : lit.children) { if (child->ty && NeedBoxOption(*child->ty, *targetTy)) { TryOptionBox(*StaticCast(targetTy), *child); } } } return VisitAction::WALK_CHILDREN; } VisitAction AutoBoxing::AddOptionBoxHandleIfExpr(const IfExpr& ie) { if (!Ty::IsTyCorrect(ie.ty) || ie.ty->IsUnitOrNothing() || !ie.thenBody) { return VisitAction::WALK_CHILDREN; } AddOptionBoxHandleBlock(*ie.thenBody, *ie.ty); if (ie.hasElse && ie.elseBody) { if (auto block = DynamicCast(ie.elseBody.get()); block) { AddOptionBoxHandleBlock(*block, *ie.ty); } } return VisitAction::WALK_CHILDREN; } void AutoBoxing::AddOptionBoxHandleBlock(Block& block, Ty& ty) { // If the block is empty or end with declaration, the last type is 'Unit', // otherwise the last type is the type of last expression. auto lastExprOrDecl = block.GetLastExprOrDecl(); Ptr lastTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); if (auto expr = DynamicCast(lastExprOrDecl)) { lastTy = expr->ty; } if (!lastTy || !NeedBoxOption(*lastTy, ty)) { return; } // If the block is empty or end with declaration, we need to insert a unitExpr for box. if (Is(lastExprOrDecl) || block.body.empty()) { auto unitExpr = CreateUnitExpr(TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)); unitExpr->curFile = block.curFile; lastExprOrDecl = unitExpr.get(); block.body.emplace_back(std::move(unitExpr)); } if (auto lastExpr = DynamicCast(lastExprOrDecl)) { TryOptionBox(StaticCast(ty), *lastExpr); block.ty = lastExpr->ty; } } VisitAction AutoBoxing::AddOptionBoxHandleTryExpr(TryExpr& te) { if (!Ty::IsTyCorrect(te.ty)) { return VisitAction::WALK_CHILDREN; } if (te.tryBlock) { AddOptionBoxHandleBlock(*te.tryBlock, *te.ty); } for (auto& ce : te.catchBlocks) { AddOptionBoxHandleBlock(*ce, *te.ty); } return VisitAction::WALK_CHILDREN; } VisitAction AutoBoxing::AddOptionBoxHandleArrayExpr(ArrayExpr& ae) { bool ignore = !Ty::IsTyCorrect(ae.ty) || ae.initFunc || ae.args.size() < 1; if (ignore) { return VisitAction::WALK_CHILDREN; } auto targetTy = typeManager.GetTypeArgs(*ae.ty)[0]; Ptr arg = nullptr; if (ae.isValueArray) { // For VArray only one argument, and it need option box. arg = ae.args[0].get(); } else if (ae.args.size() > 1) { // For RawArray(size, item:T) boxing argIndex is 1, only this case may need option box. arg = ae.args[1].get(); } if (arg && arg->expr && arg->expr->ty && targetTy && NeedBoxOption(*arg->expr->ty, *targetTy)) { TryOptionBox(*StaticCast(targetTy), *arg->expr); arg->ty = arg->expr->ty; } return VisitAction::WALK_CHILDREN; } VisitAction AutoBoxing::AddOptionBoxHandleCallExpr(CallExpr& ce) { bool ignored = !ce.baseFunc || !ce.baseFunc->ty || ce.baseFunc->ty->kind != TypeKind::TYPE_FUNC; if (ignored) { return VisitAction::WALK_CHILDREN; } auto funcTy = RawStaticCast(ce.baseFunc->ty); unsigned count = 0; auto callCheck = [&count, &funcTy, this](auto begin, auto end) { for (auto it = begin; it != end; ++it) { if (count >= funcTy->paramTys.size()) { break; } auto paramTy = funcTy->paramTys[count]; // It's possible that childs have different box type, so does not break after match. if ((*it)->expr && (*it)->expr->ty && paramTy && NeedBoxOption(*(*it)->expr->ty, *paramTy)) { TryOptionBox(*StaticCast(paramTy), *(*it)->expr); (*it)->ty = (*it)->expr->ty; } ++count; } }; if (ce.desugarArgs.has_value()) { callCheck(ce.desugarArgs->begin(), ce.desugarArgs->end()); } else { callCheck(ce.args.begin(), ce.args.end()); } return VisitAction::WALK_CHILDREN; } VisitAction AutoBoxing::AddOptionBoxHandleAssignExpr(const AssignExpr& ae) { if (ae.desugarExpr) { return VisitAction::WALK_CHILDREN; } if (ae.rightExpr->ty && ae.leftValue->ty && NeedBoxOption(*ae.rightExpr->ty, *ae.leftValue->ty)) { TryOptionBox(*StaticCast(ae.leftValue->ty), *ae.rightExpr); } return VisitAction::WALK_CHILDREN; } VisitAction AutoBoxing::AddOptionBoxHandleVarDecl(const VarDecl& vd) { if (vd.initializer && vd.initializer->ty && vd.ty && NeedBoxOption(*vd.initializer->ty, *vd.ty)) { TryOptionBox(*StaticCast(vd.ty), *vd.initializer); } return VisitAction::WALK_CHILDREN; } VisitAction AutoBoxing::AddOptionBoxHandleReturnExpr(const ReturnExpr& re) { if (re.expr && re.refFuncBody && re.refFuncBody->ty && re.refFuncBody->ty->kind == TypeKind::TYPE_FUNC) { auto funcTy = RawStaticCast(re.refFuncBody->ty); if (re.expr->ty && funcTy->retTy && NeedBoxOption(*re.expr->ty, *funcTy->retTy)) { auto expr = re.desugarExpr ? re.desugarExpr.get() : re.expr.get(); TryOptionBox(*StaticCast(funcTy->retTy), *expr); } } return VisitAction::WALK_CHILDREN; } cangjie_compiler-1.0.7/src/Sema/Desugar/DesugarBeforeTypeCheck.cpp000066400000000000000000000753311510705540100251070ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Desugar functions used before typecheck step. */ #include "cangjie/Sema/Desugar.h" #include "DesugarInTypeCheck.h" #include "DesugarMacro.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Types.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/Utils.h" namespace Cangjie { using namespace AST; using namespace TypeCheckUtil; namespace { /** * Desugar the SynchronizedExpr as a tryExpr. Add `lock()` call to the beginning of try expression. * Add `unlock()` call to the finally block. */ void DesugarSynchronizedExpr(SynchronizedExpr& se) { if (se.TestAttr(Attribute::IS_BROKEN) || se.desugarExpr != nullptr) { return; } CJC_ASSERT(se.mutex); if (se.mutex->IsInvalid()) { return; } OwnedPtr block = MakeOwned(); auto mtxVar = CreateVarDecl(".mtx", ASTCloner::Clone(se.mutex.get())); block->body.push_back(std::move(mtxVar)); OwnedPtr tryExpr = MakeOwned(); tryExpr->tryPos = se.begin; tryExpr->begin = se.begin; tryExpr->finallyBlock = MakeOwned(); tryExpr->finallyPos = se.end; // Add `lock` call. auto mtxRef = CreateRefExpr(".mtx"); mtxRef->EnableAttr(Attribute::COMPILER_ADD); auto mtxLockAccess = CreateMemberAccess(std::move(mtxRef), "lock"); CopyBasicInfo(se.mutex.get(), mtxLockAccess.get()); auto lockCall = CreateCallExpr(std::move(mtxLockAccess), {}); block->body.push_back(std::move(lockCall)); // Move synchronized expression's body. tryExpr->tryBlock = std::move(se.body); // Add `unlock` call to the `finally` block. auto mtxRef2 = CreateRefExpr(".mtx"); mtxRef2->EnableAttr(Attribute::COMPILER_ADD); auto mtxUnlockAccess = CreateMemberAccess(std::move(mtxRef2), "unlock"); CopyBasicInfo(se.mutex.get(), mtxUnlockAccess.get()); auto unlockCall = CreateCallExpr(std::move(mtxUnlockAccess), {}); tryExpr->finallyBlock->body.push_back(std::move(unlockCall)); tryExpr->isDesugaredFromSyncBlock = true; block->body.push_back(std::move(tryExpr)); AddCurFile(*block, se.curFile); se.desugarExpr = std::move(block); } // Find the OptionalExpr in the left field of expr. // For example, the GetOptionalExpr(`a?.b.c?.d`) = `a?.b.c?`. // The selector of the desugared MatchExpr is based on it. Ptr GetOptionalExpr(Expr& expr) { switch (expr.astKind) { case ASTKind::OPTIONAL_EXPR: { return StaticAs(&expr); } case ASTKind::MEMBER_ACCESS: { auto ma = StaticAs(&expr); CJC_NULLPTR_CHECK(ma->baseExpr); return GetOptionalExpr(*ma->baseExpr); } case ASTKind::CALL_EXPR: { auto ce = StaticAs(&expr); CJC_NULLPTR_CHECK(ce->baseFunc); return GetOptionalExpr(*ce->baseFunc); } case ASTKind::TRAIL_CLOSURE_EXPR: { auto tce = StaticAs(&expr); CJC_NULLPTR_CHECK(tce->expr); return GetOptionalExpr(*tce->expr); } case ASTKind::SUBSCRIPT_EXPR: { auto se = StaticAs(&expr); CJC_NULLPTR_CHECK(se->baseExpr); return GetOptionalExpr(*se->baseExpr); } case ASTKind::ASSIGN_EXPR: { auto ae = StaticAs(&expr); CJC_NULLPTR_CHECK(ae->leftValue); return GetOptionalExpr(*ae->leftValue); } case ASTKind::INC_OR_DEC_EXPR: { auto ide = StaticAs(&expr); CJC_NULLPTR_CHECK(ide->expr); return GetOptionalExpr(*ide->expr); } default: { return nullptr; } } } // Replace the OptionalExpr with a RefExpr `v`. // For example, CreateSelector(`a?.b.c?.d`) = `v.d`, CreateSelector(`a?.b.c`) = `v.b.c`. // This function creates the selectors of nested MatchExprs and the innermost exprOrDecls of case Some. OwnedPtr CreateSelector(OwnedPtr&& expr) { CJC_NULLPTR_CHECK(expr); switch (expr->astKind) { case ASTKind::OPTIONAL_EXPR: { auto oe = StaticAs(expr.get()); auto re = CreateRefExpr(V_COMPILER); CopyBasicInfo(oe->baseExpr.get(), re.get()); re->begin = oe->questPos; re->end = oe->questPos + 1; return re; } case ASTKind::MEMBER_ACCESS: { auto ma = StaticAs(expr.get()); ma->baseExpr = CreateSelector(std::move(ma->baseExpr)); return std::move(expr); } case ASTKind::CALL_EXPR: { auto ce = StaticAs(expr.get()); ce->baseFunc = CreateSelector(std::move(ce->baseFunc)); return std::move(expr); } case ASTKind::TRAIL_CLOSURE_EXPR: { auto tce = StaticAs(expr.get()); tce->expr = CreateSelector(std::move(tce->expr)); return std::move(expr); } case ASTKind::SUBSCRIPT_EXPR: { auto se = StaticAs(expr.get()); se->baseExpr = CreateSelector(std::move(se->baseExpr)); return std::move(expr); } case ASTKind::ASSIGN_EXPR: { auto ae = StaticAs(expr.get()); ae->leftValue = CreateSelector(std::move(ae->leftValue)); return std::move(expr); } case ASTKind::INC_OR_DEC_EXPR: { auto ide = StaticAs(expr.get()); ide->expr = CreateSelector(std::move(ide->expr)); return std::move(expr); } default: { return std::move(expr); } } } /** * Create a MatchExpr and desugar recursively. * * @param expr is the `Expr` to be desugared. * @param caseExpr is the accumulator of the recursion, i.e., the `exprOrDecls` of the `Some` case * in the desugared `MatchExpr`. In the base case of the recursion, it is returned * directly as the desugared `Expr`. * @param isAssign indicates whether the optional chain is an `AssignExpr'. If it is true, the desugar * result is of type `Unit`, i.e., a `MatchExpr` whose `None` case is `()`. Otherwise, * the desugar result is of type `Option`, i.e., a `MatchExpr` whose `None` case is * `None`. */ OwnedPtr DesugarOptionalChainWithMatchCase(Expr& expr, OwnedPtr&& caseExpr, bool isAssign) { Ptr opt = GetOptionalExpr(expr); if (opt == nullptr) { return std::move(caseExpr); } else { CJC_NULLPTR_CHECK(opt->baseExpr); // Create a `MatchExpr`: // match (CreateSelector(opt->baseExpr)) { // case Some(v) => caseExpr // case None => caseNoneExpr // } // where `caseNoneExpr` is `()` if `isAssign` is true, otherwise, it is `None`. std::vector> matchCases; auto varPattern = CreateVarPattern(V_COMPILER); CopyBasicInfo(opt->baseExpr.get(), varPattern->varDecl.get()); auto somePattern = MakeOwnedNode(); somePattern->constructor = CreateRefExprInCore(OPTION_VALUE_CTOR); somePattern->patterns.emplace_back(std::move(varPattern)); matchCases.emplace_back(CreateMatchCase(std::move(somePattern), std::move(caseExpr))); auto nonePattern = MakeOwnedNode(); nonePattern->constructor = CreateRefExprInCore(OPTION_NONE_CTOR); auto caseNoneExpr = isAssign ? CreateUnitExpr() : CreateRefExprInCore(OPTION_NONE_CTOR); matchCases.emplace_back(CreateMatchCase(std::move(nonePattern), std::move(caseNoneExpr))); auto matchExpr = CreateMatchExpr(CreateSelector(ASTCloner::Clone(opt->baseExpr.get(), SetIsClonedSourceCode)), std::move(matchCases), Ty::GetInitialTy(), Expr::SugarKind::QUEST); CopyBasicInfo(opt->baseExpr.get(), matchExpr.get()); matchExpr->EnableAttr(Attribute::IS_CLONED_SOURCE_CODE); return DesugarOptionalChainWithMatchCase(*opt->baseExpr, std::move(matchExpr), isAssign); } } OwnedPtr CreateOptionalChainCallSome(OwnedPtr&& expr) { OwnedPtr someRef = CreateRefExprInCore(OPTION_VALUE_CTOR); CopyBasicInfo(expr.get(), someRef.get()); std::vector> args; args.emplace_back(CreateFuncArg(CreateSelector(std::move(expr)))); OwnedPtr callSome = CreateCallExpr(std::move(someRef), std::move(args)); CopyBasicInfo(callSome->args[0].get(), callSome.get()); return callSome; } /** * Desugar OptionalChainExpr to MatchExpr * *************** before desugar **************** * a?.b.c?.d * *************** after desugar **************** * match (a) { * case Some(v) => match (v.b.c) { * case Some(v) => Some(v.d) * case None => None * } * case None => None * } * *************** before desugar **************** * a?.b.c?.d = x * *************** after desugar **************** * match (a) { * case Some(v) => match (v.b.c) { * case Some(v) => v.d = x * case None => () * } * case None => () * } * */ void DesugarOptionalChainExpr(OptionalChainExpr& oce) { if (oce.desugarExpr != nullptr) { return; } CJC_NULLPTR_CHECK(oce.expr); auto expr = ASTCloner::Clone(oce.expr.get(), SetIsClonedSourceCode); oce.desugarExpr = expr->astKind == ASTKind::ASSIGN_EXPR || expr->astKind == ASTKind::INC_OR_DEC_EXPR ? DesugarOptionalChainWithMatchCase(*expr, CreateSelector(std::move(oce.expr)), true) : DesugarOptionalChainWithMatchCase(*expr, CreateOptionalChainCallSome(std::move(oce.expr)), false); CopyBasicInfo(expr.get(), oce.desugarExpr.get()); AddCurFile(*oce.desugarExpr, oce.curFile); } // Get OptionalChainExpr appearing in leftTupleLit. Ptr GetOptionalChainExprForLeftVale(Expr& leftValue) { Ptr expr = &leftValue; while (expr->astKind == ASTKind::PAREN_EXPR) { CJC_NULLPTR_CHECK(StaticAs(expr)->expr.get()); expr = StaticAs(expr)->expr.get(); } if (expr->astKind == ASTKind::OPTIONAL_CHAIN_EXPR) { return StaticAs(expr); } return nullptr; } OwnedPtr CreateCompoundAssignExpr( OwnedPtr left, OwnedPtr right, TokenKind op) { auto ae = CreateAssignExpr(std::move(left), std::move(right)); ae->op = op; ae->isCompound = true; return ae; } /** * *************** before desugar **************** * a++ * *************** after desugar **************** * a += 1 * */ void DesugarIncOrDecExpr(IncOrDecExpr& ide) { if (ide.desugarExpr != nullptr) { return; } CJC_NULLPTR_CHECK(ide.expr); auto one = CreateLitConstExpr(LitConstKind::INTEGER, "1", Ty::GetInitialTy()); CopyBasicInfo(&ide, one.get()); auto ae = CreateCompoundAssignExpr( std::move(ide.expr), std::move(one), ide.op == TokenKind::INCR ? TokenKind::ADD_ASSIGN : TokenKind::SUB_ASSIGN); CopyBasicInfo(&ide, ae.get()); ide.desugarExpr = std::move(ae); } void DesugarAssignExprRecursively( const TupleLit& leftValues, Expr& rightExprs, std::vector>& nodes) { uint64_t indexOfRightExpr = 0; auto varDecl = CreateTmpVarDecl(nullptr, &rightExprs); std::string identifier = varDecl->identifier; nodes.emplace_back(std::move(varDecl)); for (auto& leftValue : leftValues.children) { auto tempRefExpr = CreateRefExpr(identifier); tempRefExpr->EnableAttr(Attribute::COMPILER_ADD); auto rightExpr = MakeOwned(); rightExpr->baseExpr = std::move(tempRefExpr); rightExpr->indexExprs.emplace_back( MakeOwned(LitConstKind::INTEGER, std::to_string(indexOfRightExpr))); rightExpr->begin = rightExprs.begin; rightExpr->end = rightExprs.end; rightExpr->EnableAttr(Attribute::COMPILER_ADD, Attribute::IMPLICIT_ADD); rightExpr->sourceExpr = &rightExprs; if (leftValue->astKind == ASTKind::TUPLE_LIT) { DesugarAssignExprRecursively(*StaticAs(leftValue.get()), *rightExpr, nodes); } else if (Ptr leftIsOptionalChain = GetOptionalChainExprForLeftVale(*leftValue)) { auto optionalChainExpr = MakeOwned(); optionalChainExpr->EnableAttr(Attribute::COMPILER_ADD); optionalChainExpr->expr = CreateAssignExpr( ASTCloner::Clone(leftIsOptionalChain->expr.get(), SetIsClonedSourceCode), std::move(rightExpr)); CopyBasicInfo(leftIsOptionalChain, optionalChainExpr->expr.get()); nodes.emplace_back(std::move(optionalChainExpr)); } else { auto tmpAssignExpr = CreateAssignExpr(ASTCloner::Clone(leftValue.get(), SetIsClonedSourceCode), std::move(rightExpr)); tmpAssignExpr->begin = leftValue->begin; tmpAssignExpr->end = leftValue->end; nodes.emplace_back(std::move(tmpAssignExpr)); } ++indexOfRightExpr; } } /** * Desugar multiple assignment expression to a series of single assignment expressions. * *************** before desugar **************** * var (a, b, c, d) = (0, 0, 0, 0) * var f = { => ((1, 2), (3, 4))} * ((a, b), (c, d)) = f() * *************** after desugar **************** * { * var $tmp1 = f() * var $tmp2 = $tmp1[0] * a = $tmp2[0] * b = $tmp2[1] * var $tmp3 = $tmp0[1] * c = $tmp3[0] * d = $tmp3[1] * } // wrap with a block. */ void DesugarAssignExpr(AssignExpr& ae) { CJC_ASSERT(ae.leftValue); if (ae.leftValue->astKind != ASTKind::TUPLE_LIT) { return; } std::vector> nodes; DesugarAssignExprRecursively(*StaticAs(ae.leftValue.get()), *ae.rightExpr, nodes); ae.desugarExpr = CreateBlock(std::move(nodes)); } void DesugarOptionType(OptionType& optionType) { unsigned int questNum = optionType.questNum; // Create new RefType whose 'name' is 'Option' and 'typeArgs' is the 'componentType' of the optionType auto refType = CreateRefTypeInCore(STD_LIB_OPTION); std::vector> typeArgs; typeArgs.push_back(ASTCloner::Clone(optionType.componentType.get())); refType->typeArguments = std::move(typeArgs); // Handle nested option types for (unsigned int i = 1; i < questNum; i++) { std::vector> tempTypeArgs; tempTypeArgs.push_back(ASTCloner::Clone(refType.get())); refType->typeArguments = std::move(tempTypeArgs); } CopyBasicInfo(&optionType, refType.get()); // Set the desugarType of the optionType optionType.desugarType = std::move(refType); } void DesugarMainDecl(MainDecl& mainDecl) { if (mainDecl.desugarDecl) { return; // NOTE: During incremental compilation, this may be set. } auto funcDecl = MakeOwnedNode(); funcDecl->curFile = mainDecl.curFile; funcDecl->begin = mainDecl.begin; funcDecl->keywordPos = mainDecl.keywordPos; funcDecl->identifier = mainDecl.identifier; funcDecl->CloneAttrs(mainDecl); funcDecl->overflowStrategy = mainDecl.overflowStrategy; funcDecl->funcBody = std::move(mainDecl.funcBody); funcDecl->end = funcDecl->funcBody->end; funcDecl->toBeCompiled = mainDecl.toBeCompiled; funcDecl->comments = std::move(mainDecl.comments); funcDecl->rawMangleName = std::move(mainDecl.rawMangleName); funcDecl->hash = mainDecl.hash; funcDecl->EnableAttr(Attribute::MAIN_ENTRY); auto cl = ASTCloner(); for (auto& anno : mainDecl.annotations) { funcDecl->annotations.push_back(cl.Clone(anno.get())); } mainDecl.desugarDecl = std::move(funcDecl); } OwnedPtr DesugarTrailClosureAsCall(TrailingClosureExpr& trailingClosure) { // Desugar trailing closure by move. std::vector> parameters; parameters.emplace_back(CreateFuncArg(std::move(trailingClosure.lambda))); parameters[0]->EnableAttr(Attribute::IMPLICIT_ADD); auto callExpr = CreateCallExpr(std::move(trailingClosure.expr), std::move(parameters)); CopyBasicInfo(&trailingClosure, callExpr.get()); return callExpr; } /** * Desugar TrailingClosureExpr to CallExpr. For example: * *************** before desugar **************** * func f(a : Int64, b : Int64, g : (Int64, Int64) -> Int64){ * return g(a, b) * } * var t = f(1,2){x, y => x + 2 * y} * *************** after desugar **************** * var t = f(1,2,{x, y => x + 2 * y}) or var t = f(1,2)({x, y => x + 2 * y}) * */ void DesugarTrailingClosureExpr(TrailingClosureExpr& trailingClosure) { if (trailingClosure.desugarExpr || !trailingClosure.lambda) { return; } if (auto ae = AST::As(trailingClosure.expr.get()); ae) { // If baseExpr of trailing closure is arrayExpr, move lambda as last argument of arrayExpr. (void)ae->args.emplace_back(CreateFuncArg(std::move(trailingClosure.lambda))); trailingClosure.desugarExpr = std::move(trailingClosure.expr); } else if (auto ce = AST::As(trailingClosure.expr.get()); ce) { // If baseExpr of trailing closure is call, the closure is passed as base call expression's last argument. ce->args.emplace_back(CreateFuncArg(std::move(trailingClosure.lambda))); ce->args.back()->EnableAttr(Attribute::IMPLICIT_ADD); trailingClosure.desugarExpr = std::move(trailingClosure.expr); } else { trailingClosure.desugarExpr = DesugarTrailClosureAsCall(trailingClosure); } } const std::string LEVEL_IDENTGIFIER = "level"; const std::string SYSCAP_IDENTGIFIER = "syscap"; // For level check: const std::string DEVICE_INFO = "DeviceInfo"; const std::string SDK_API_VERSION = "sdkApiVersion"; // For syscap check: const std::string CANIUSE_IDENTIFIER = "canIUse"; // Before desugar: `@IfAvaliable(level: 11, {=>...}, {=>...})` // Desugar as: `if (DeviceInfo.sdkApiVersion >= 11) {...} else {...}` OwnedPtr DesugarIfAvailableLevelCondition(IfAvailableExpr& iae) { auto me = CreateMemberAccess(CreateRefExpr(SrcIdentifier(DEVICE_INFO)), SDK_API_VERSION); auto condition = CreateBinaryExpr(std::move(me), std::move(iae.GetArg()->expr), TokenKind::GE); AddCurFile(*condition, iae.curFile); CopyBasicInfo(&iae, condition.get()); return std::move(condition); } // Before desugar: `@IfAvaliable(syscap: "xxx", {=>...}, {=>...})` // Desugar as: `if (canIUse("xxx")) {...} else {...}` OwnedPtr DesugarIfAvailableSyscapCondition(IfAvailableExpr& iae) { auto canIUseRef = CreateRefExpr(SrcIdentifier(CANIUSE_IDENTIFIER)); std::vector> argList; argList.emplace_back(CreateFuncArg(std::move(iae.GetArg()->expr))); auto condition = CreateCallExpr(CreateRefExpr(SrcIdentifier(CANIUSE_IDENTIFIER)), std::move(argList)); AddCurFile(*condition, iae.curFile); CopyBasicInfo(&iae, condition.get()); return std::move(condition); } OwnedPtr DesugarIfAvailableCondition(IfAvailableExpr& iae) { if (iae.GetArg()->name == LEVEL_IDENTGIFIER) { return DesugarIfAvailableLevelCondition(iae); } else if (iae.GetArg()->name == SYSCAP_IDENTGIFIER) { return DesugarIfAvailableSyscapCondition(iae); } else { return MakeOwned(); } } /// @IfAvailable(namedArg, lambda1, lambda2) is parsed as a MacroExpandExpr and desguared into an IfAvailableExpr here void DesugarIfAvailableExpr(IfAvailableExpr& iae) { if (iae.desugarExpr) { return; } // Create condition. OwnedPtr condition = DesugarIfAvailableCondition(iae); auto ifBlock = ASTCloner::Clone(iae.GetLambda1()->funcBody->body.get()); auto elseBlock = ASTCloner::Clone(iae.GetLambda2()->funcBody->body.get()); auto ifExpr = CreateIfExpr(std::move(condition), std::move(ifBlock), std::move(elseBlock), iae.ty); ifExpr->sourceExpr = &iae; CopyBasicInfo(&iae, ifExpr); iae.desugarExpr = std::move(ifExpr); } struct VisitContext { void Push(bool isDiscarded, Ptr parent) { isDiscardedStack.push_back(isDiscarded); parentStack.push_back(parent); } void Pop(Ptr expected) { CJC_ASSERT((!parentStack.empty()) && (parentStack.back() == expected)); isDiscardedStack.pop_back(); parentStack.pop_back(); } /* Whether the current context does not require a return value of the expr */ std::vector isDiscardedStack; std::vector> parentStack; }; struct DiscardedHelper { void PushCtxt(bool isDiscarded, Ptr parent) { ctxt.Push(isDiscarded, parent); } void PopCtxt(Ptr expected) { ctxt.Pop(expected); } bool IsNodeDiscarded(const Node& n) const { return !ctxt.parentStack.empty() && IsDiscarded(n, *(ctxt.parentStack.back()), ctxt.isDiscardedStack.back()); } /* * Try to find branch expressions to desuger in a block. * Branch expressions include: IfExpr, TryExpr, MatchExpr. * If the return value of the entire exprssion is not used, * add () to the end of each branch to skip lowest common * parent type check. * An example: * ************** before desugar **************** if (true) { 1 } else { 1.0 } // Fail. Can't find a return type. * *************** after desugar **************** if (true) { 1 () } else { 1.0 () } // Succeed. Return type is Unit. * ********************************************** */ static void DesugarBrExpr(Node& node) { auto isUnitExpr = [](Node& n) -> bool { return n.astKind == ASTKind::LIT_CONST_EXPR && StaticAs(&n)->kind == LitConstKind::UNIT; }; auto isNothingExpr = [](const Node& n) -> bool { return std::set{ASTKind::JUMP_EXPR, ASTKind::THROW_EXPR, ASTKind::RETURN_EXPR}.count(n.astKind) > 0; }; auto unitifyBlock = [&isUnitExpr, &isNothingExpr](Block& b) -> void { if (b.body.empty() || (b.body.back() && (!isUnitExpr(*b.body.back())) && (!isNothingExpr(*b.body.back())))) { b.body.push_back(CreateUnitExpr()); } }; auto unitifyIf = [&unitifyBlock](const IfExpr& ie) -> void { if (ie.thenBody && ie.elseBody) { unitifyBlock(*StaticAs(ie.thenBody.get())); if (ie.elseBody->astKind == ASTKind::BLOCK) { unitifyBlock(*StaticAs(ie.elseBody.get())); } } }; auto unitifyTry = [&unitifyBlock](TryExpr& te) { if (te.tryLambda) { unitifyBlock(*(te.tryLambda->funcBody->body)); } else { unitifyBlock(*(te.tryBlock)); } for (auto& cb : te.catchBlocks) { unitifyBlock(*cb); } for (auto& h : te.handlers) { if (h.desugaredLambda) { unitifyBlock(*h.desugaredLambda->funcBody->body); } CJC_NULLPTR_CHECK(h.block); unitifyBlock(*h.block); } }; auto unitifyMatch = [&unitifyBlock](MatchExpr& me) { for (auto& mc : me.matchCases) { unitifyBlock(*(mc->exprOrDecls)); } for (auto& mc : me.matchCaseOthers) { unitifyBlock(*(mc->exprOrDecls)); } }; if (node.astKind == ASTKind::IF_EXPR) { unitifyIf(*StaticAs(&node)); } else if (node.astKind == ASTKind::TRY_EXPR) { unitifyTry(*StaticAs(&node)); } else if (node.astKind == ASTKind::MATCH_EXPR) { unitifyMatch(*StaticAs(&node)); } } private: VisitContext ctxt; /* * If the return value of the child is only used as a candidate of the parent's return value, * the discarded property is transitive from parent to child. */ static bool IsDiscardTransitive(const Node& node, Node& parent) { static const std::set caseKinds = {ASTKind::MATCH_CASE, ASTKind::MATCH_CASE_OTHER}; // if expr to then blk and else blk bool ifBody = parent.astKind == ASTKind::IF_EXPR && (&node == StaticAs(&parent)->thenBody.get() || &node == StaticAs(&parent)->elseBody.get()); // try expr to try blk and catch blk bool tryBody = parent.astKind == ASTKind::TRY_EXPR && node.astKind == ASTKind::BLOCK; // match expr to all cases bool matchCase = parent.astKind == ASTKind::MATCH_EXPR && (caseKinds.count(node.astKind) > 0); // match case to its body bool matchCaseBody = (caseKinds.count(parent.astKind) > 0) && node.astKind == ASTKind::BLOCK; // synchronized to its body bool syncBody = parent.astKind == ASTKind::SYNCHRONIZED_EXPR && node.astKind == ASTKind::BLOCK; // parentheses to their inner exprssion bool parentheses = parent.astKind == ASTKind::PAREN_EXPR; // info of FuncDecl has to pass down through FuncBody node bool funcBody = parent.astKind == ASTKind::FUNC_BODY; return ifBody || tryBody || matchCase || matchCaseBody || syncBody || parentheses || funcBody ; }; /* Some expressions will ignore a child block's return value. */ static bool IsConstValBlk(const Node& node, Node& parent) { static const std::set unitTypeExpr = { ASTKind::WHILE_EXPR, ASTKind::DO_WHILE_EXPR, ASTKind::FOR_IN_EXPR}; // loops always return Unit bool constBlock = unitTypeExpr.count(parent.astKind) > 0; // finally is always ignored bool finallyBlock = parent.astKind == ASTKind::TRY_EXPR && &node == StaticAs(&parent)->finallyBlock.get(); // func with ret type Unit always return Unit bool funcWithUnitRet = false; if (auto parentFB = DynamicCast(&parent); parentFB && parentFB->retType) { funcWithUnitRet = parentFB->retType->astKind == ASTKind::PRIMITIVE_TYPE && StaticAs(parentFB->retType.get())->kind == TypeKind::TYPE_UNIT; } // constructors don't have return value bool constructor = node.astKind == ASTKind::FUNC_BODY && parent.TestAnyAttr(Attribute::CONSTRUCTOR, Attribute::PRIMARY_CONSTRUCTOR); // if without else always return Unit bool ifNoElse = parent.astKind == ASTKind::IF_EXPR && StaticAs(&parent)->elseBody == nullptr; return constBlock || finallyBlock || funcWithUnitRet || constructor || ifNoElse; }; /* Whether the current node is an discarded-value expression */ static bool IsDiscarded(const Node& node, Node& parent, bool isParentDiscarded) { // Transitively discarded bool flagTransitive = isParentDiscarded && IsDiscardTransitive(node, parent); // Child's ret is ignored bool flagConst = (node.astKind == ASTKind::BLOCK || node.astKind == ASTKind::FUNC_BODY) && IsConstValBlk(node, parent); // Immediate child of a Block bool flagBlock = parent.astKind == ASTKind::BLOCK && (isParentDiscarded || (&node != StaticAs(&parent)->body.back().get())); return flagTransitive || flagConst || flagBlock; } }; } // namespace void PerformDesugarBeforeTypeCheck(Node& root, bool desugarMacrocall) { DiscardedHelper dHelper; std::function)> visitorPost = [&dHelper](Ptr node) -> VisitAction { dHelper.PopCtxt(node); return VisitAction::KEEP_DECISION; }; std::function)> visitor = [&visitor, &visitorPost, &dHelper, &desugarMacrocall](Ptr node) -> VisitAction { if (node->TestAttr(Attribute::IS_BROKEN)) { // must push before return to pair with visitorPost dHelper.PushCtxt(false, node); return VisitAction::SKIP_CHILDREN; } // Add all desugar branches here. if (node->astKind == ASTKind::FILE) { auto file = StaticAs(node); if (desugarMacrocall) { // Walk nodes in macrocall to find references, for lsp. for (auto& it : file->originalMacroCallNodes) { Walker(it.get(), visitor, visitorPost).Walk(); } } DesugarMacroDecl(*file); } else if (node->astKind == ASTKind::MAIN_DECL) { DesugarMainDecl(*StaticAs(node)); } else if (node->astKind == ASTKind::QUOTE_EXPR) { DesugarQuoteExpr(*StaticAs(node)); } else if (node->astKind == ASTKind::OPTION_TYPE) { DesugarOptionType(*StaticAs(node)); } else if (node->astKind == ASTKind::TRAIL_CLOSURE_EXPR) { DesugarTrailingClosureExpr(*StaticAs(node)); } else if (node->astKind == ASTKind::SYNCHRONIZED_EXPR) { DesugarSynchronizedExpr(*StaticAs(node)); } else if (node->astKind == ASTKind::OPTIONAL_CHAIN_EXPR) { DesugarOptionalChainExpr(*StaticAs(node)); } else if (node->astKind == ASTKind::INC_OR_DEC_EXPR) { DesugarIncOrDecExpr(StaticCast(*node)); } else if (node->astKind == ASTKind::ASSIGN_EXPR) { DesugarAssignExpr(*StaticAs(node)); } else if (node->astKind == ASTKind::IF_AVAILABLE_EXPR) { DesugarIfAvailableExpr(*StaticAs(node)); } if (dHelper.IsNodeDiscarded(*node)) { dHelper.PushCtxt(true, node); DiscardedHelper::DesugarBrExpr(*node); } else { dHelper.PushCtxt(false, node); } return VisitAction::WALK_CHILDREN; }; Walker(&root, visitor, visitorPost).Walk(); } } // namespace Cangjie cangjie_compiler-1.0.7/src/Sema/Desugar/DesugarInTypeCheck.cpp000066400000000000000000000676241510705540100242610ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Desugar functions used during typecheck step. */ #include "DesugarInTypeCheck.h" #include #include #include #include "TypeCheckUtil.h" #include "TypeCheckerImpl.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Types.h" #include "cangjie/AST/Utils.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Utils/CheckUtils.h" namespace Cangjie { using namespace AST; using namespace TypeCheckUtil; namespace { OwnedPtr TryDesugarFunctionCallExpr(OwnedPtr base) { if (!Ty::IsInitialTy(base->ty) && base->ty->kind != TypeKind::TYPE_FUNC) { // Try to desugar base as base.operator(). // NOTE: 'curFile' and 'curMacroCall' will be set from caller of current method. auto newBase = MakeOwnedNode(); newBase->field = "()"; newBase->scopeName = base->scopeName; newBase->begin = base->begin; newBase->end = base->end; newBase->baseExpr = std::move(base); return newBase; } return base; } void DesugarPipelineExpr(ASTContext& ctx, BinaryExpr& be) { auto callExpr = MakeOwnedNode(); CopyBasicInfo(&be, callExpr.get()); ctx.RemoveTypeCheckCache(*callExpr); callExpr->baseFunc = TryDesugarFunctionCallExpr(std::move(be.rightExpr)); ctx.RemoveTypeCheckCache(*(callExpr->baseFunc)); auto funcArg = MakeOwned(); CopyBasicInfo(be.leftExpr.get(), funcArg.get()); ctx.RemoveTypeCheckCache(*funcArg); funcArg->expr = std::move(be.leftExpr); callExpr->args.push_back(std::move(funcArg)); callExpr->sourceExpr = &be; be.desugarExpr = std::move(callExpr); if (be.TestAttr(Attribute::UNSAFE)) { be.desugarExpr->EnableAttr(Attribute::UNSAFE); } AddCurFile(be, be.curFile); } void DesugarCompositionExpr(ASTContext& ctx, BinaryExpr& be) { auto callExpr = MakeOwnedNode(); CopyBasicInfo(&be, callExpr.get()); ctx.RemoveTypeCheckCache(*callExpr); auto refExpr = CreateRefExprInCore("composition"); CopyBasicInfo(&be, refExpr.get()); ctx.RemoveTypeCheckCache(*refExpr); callExpr->baseFunc = std::move(refExpr); auto arg1 = MakeOwned(); CopyBasicInfo(be.leftExpr.get(), arg1.get()); ctx.RemoveTypeCheckCache(*arg1); arg1->expr = TryDesugarFunctionCallExpr(std::move(be.leftExpr)); arg1->expr->isInFlowExpr = true; ctx.RemoveTypeCheckCache(*(arg1->expr)); auto arg2 = MakeOwned(); CopyBasicInfo(be.rightExpr.get(), arg2.get()); ctx.RemoveTypeCheckCache(*arg2); arg2->expr = TryDesugarFunctionCallExpr(std::move(be.rightExpr)); arg2->expr->isInFlowExpr = true; ctx.RemoveTypeCheckCache(*(arg2->expr)); callExpr->args.push_back(std::move(arg1)); callExpr->args.push_back(std::move(arg2)); callExpr->sourceExpr = &be; be.desugarExpr = std::move(callExpr); if (be.TestAttr(Attribute::UNSAFE)) { be.desugarExpr->EnableAttr(Attribute::UNSAFE); } AddCurFile(be, be.curFile); } // Ignore empty intersection ty added by typealias substitution. void IgnoreEmptyIntersection(std::vector>& baseArgs) { for (auto it = baseArgs.begin(); it != baseArgs.end();) { CJC_ASSERT(*it != nullptr); auto iTy = DynamicCast((*it)->ty); bool isEmpty = iTy && iTy->tys.empty(); it = isEmpty ? baseArgs.erase(it) : it + 1; } } OwnedPtr CreateArgumentForCompoundAssignOverload( AssignExpr& ae, CallExpr& createdCall, Ptr basePtr, const std::vector>& indexPtrs) { // Compound assign will be desugared as a[i] = x ==> a.[](i, a.[](i) + x). // The arg 'createdCall' is outer call of desugared result and this function will create the inner callExpr. auto binaryExpr = MakeOwnedNode(); CopyBasicInfo(&ae, binaryExpr.get()); binaryExpr->leftExpr = ASTCloner::Clone(Ptr(&createdCall)); // Set mapped expr node for later process for side effect. auto ce = RawStaticCast(binaryExpr->leftExpr.get()); auto base = StaticCast(ce->baseFunc.get()); base->baseExpr->mapExpr = basePtr; for (size_t i = 0; i < indexPtrs.size(); ++i) { ce->args[i]->expr->mapExpr = indexPtrs[i]; } binaryExpr->leftExpr->sourceExpr = &ae; binaryExpr->rightExpr = std::move(ae.rightExpr); if (COMPOUND_ASSIGN_EXPR_MAP.find(ae.op) != COMPOUND_ASSIGN_EXPR_MAP.end()) { binaryExpr->op = COMPOUND_ASSIGN_EXPR_MAP.at(ae.op); } binaryExpr->operatorPos = ae.assignPos; return binaryExpr; } void DesugarPrimaryCtorHandleSuper(const Decl& decl, const PrimaryCtorDecl& fd, const OwnedPtr& funcBody, std::vector>::iterator& it) { if (decl.astKind != ASTKind::CLASS_DECL || !fd.funcBody->body || fd.funcBody->body->body.empty()) { return; } if (auto ce = DynamicCast(fd.funcBody->body->body.front().get()); ce) { if (auto re = DynamicCast(ce->baseFunc.get()); re && re->isSuper) { funcBody->body->body.push_back( ASTCloner::Clone(fd.funcBody->body->body.front().get(), SetIsClonedSourceCode)); ++it; } } } void DesugarPrimaryCtorHandleParamSetEachParam( Decl& decl, const PrimaryCtorDecl& fd, const OwnedPtr& funcBody) { if (fd.funcBody->paramLists.empty()) { return; } for (auto& param : fd.funcBody->paramLists[0]->params) { if (!param->isMemberParam) { continue; } if (auto vd = DynamicCast(param.get()); vd && !vd->TestAttr(Attribute::STATIC)) { // Create declare variables. OwnedPtr varDecl = MakeOwned(); CopyBasicInfo(vd, varDecl.get()); varDecl->annotations = ASTCloner::CloneVector(vd->annotations); varDecl->annotationsArray = ASTCloner::Clone(vd->annotationsArray.get()); varDecl->isVar = vd->isVar; varDecl->modifiers.insert(vd->modifiers.begin(), vd->modifiers.end()); varDecl->CloneAttrs(*vd); varDecl->keywordPos = vd->begin; varDecl->colonPos = vd->colonPos; varDecl->type = ASTCloner::Clone(vd->type.get()); varDecl->identifier = vd->identifier; varDecl->isMemberParam = true; // Lsp will use this to found related funcparam. varDecl->rawMangleName = vd->rawMangleName; varDecl->toBeCompiled = fd.toBeCompiled; varDecl->outerDecl = &decl; varDecl->EnableAttr(Attribute::COMPILER_ADD, Attribute::IS_CLONED_SOURCE_CODE); if (decl.astKind == ASTKind::CLASS_DECL) { StaticAs(&decl)->body->decls.push_back(std::move(varDecl)); } else if (decl.astKind == ASTKind::STRUCT_DECL) { StaticAs(&decl)->body->decls.push_back(std::move(varDecl)); } // Create initialize expression. auto thisExpr = CreateRefExpr("this"); thisExpr->isThis = true; thisExpr->EnableAttr(Attribute::IMPLICIT_ADD); // Requried for LSP usage. CopyBasicInfo(vd, thisExpr.get()); thisExpr->ref.identifier.SetFileID(fd.funcBody->begin.fileID); auto left = CreateMemberAccess(std::move(thisExpr), vd->identifier); left->EnableAttr(Attribute::IMPLICIT_ADD); // Requried for LSP usage. CopyBasicInfo(vd, left.get()); auto right = CreateRefExpr(vd->identifier); right->EnableAttr(Attribute::IMPLICIT_ADD); // Requried for LSP usage. CopyBasicInfo(vd->type.get(), right.get()); // The right position must be later than left. right->sourceExpr = left.get(); // Used to mark the 'right' is desugar created and should not diag. right->ref.identifier.SetFileID(fd.funcBody->begin.fileID); auto assignment = MakeOwned(); CopyBasicInfo(vd, assignment.get()); assignment->EnableAttr(Attribute::COMPILER_ADD); assignment->leftValue = std::move(left); assignment->rightExpr = std::move(right); assignment->op = TokenKind::ASSIGN; funcBody->body->body.push_back(std::move(assignment)); } } } void DesugarPrimaryCtorHandleParam(Decl& decl, const PrimaryCtorDecl& fd, const OwnedPtr& funcBody, const OwnedPtr& funcParamList) { if (fd.funcBody->paramLists.empty()) { return; } CopyBasicInfo(fd.funcBody->paramLists[0].get(), funcParamList.get()); for (auto& p : fd.funcBody->paramLists[0]->params) { // FundParam is cloned from user written source code, which should be used to highlight in LSP. auto param = CreateFuncParam(p->identifier, ASTCloner::Clone(p->type.get(), SetIsClonedSourceCode), ASTCloner::Clone(p->assignment.get(), SetIsClonedSourceCode)); param->EnableAttr(Attribute::IS_CLONED_SOURCE_CODE); // Original user written source code should be ignored for LSP. p->EnableAttr(Attribute::COMPILER_ADD); CopyBasicInfo(p.get(), param.get()); param->identifier.SetPos(p->identifier.Begin(), p->identifier.End()); param->identifier.SetRaw(p->identifier.IsRaw()); param->isNamedParam = p->isNamedParam; param->isMemberParam = p->isMemberParam; // Lsp will use this to found related member variable. for (auto& anno : p->annotations) { if (anno->kind == AnnotationKind::DEPRECATED || anno->kind == AnnotationKind::CUSTOM) { param->annotations.emplace_back(ASTCloner::Clone(anno.get(), SetIsClonedSourceCode)); } } funcParamList->params.push_back(std::move(param)); } // Member variable param. DesugarPrimaryCtorHandleParamSetEachParam(decl, fd, funcBody); } void DesugarPrimaryCtorSetPrimaryFunc(Decl& decl, PrimaryCtorDecl& fd, OwnedPtr& funcBody) { OwnedPtr primaryFunc = MakeOwned(); CopyBasicInfo(&fd, primaryFunc.get()); primaryFunc->toBeCompiled = fd.toBeCompiled; primaryFunc->funcBody = std::move(funcBody); primaryFunc->funcBody->funcDecl = primaryFunc.get(); primaryFunc->identifier = "init"; primaryFunc->identifierForLsp = fd.identifier; primaryFunc->identifier.SetPos(fd.identifier.Begin(), fd.identifier.End()); primaryFunc->modifiers.insert(fd.modifiers.begin(), fd.modifiers.end()); primaryFunc->constructorCall = ConstructorCall::NONE; for (const auto& anno : fd.annotations) { // Annotation is cloned from user written source code, which should be used to highlight in LSP. (void)primaryFunc->annotations.emplace_back(ASTCloner::Clone(anno.get(), SetIsClonedSourceCode)); } primaryFunc->CloneAttrs(fd); primaryFunc->EnableAttr(Attribute::COMPILER_ADD, Attribute::IS_CLONED_SOURCE_CODE); primaryFunc->EnableAttr(Attribute::CONSTRUCTOR, Attribute::PRIMARY_CONSTRUCTOR); primaryFunc->annotations = std::move(fd.annotations); primaryFunc->overflowStrategy = fd.overflowStrategy; primaryFunc->isFastNative = fd.isFastNative; primaryFunc->isConst = fd.isConst; primaryFunc->isFrozen = primaryFunc->HasAnno(AnnotationKind::FROZEN); primaryFunc->rawMangleName = fd.rawMangleName; if (decl.astKind == ASTKind::CLASS_DECL) { auto classDecl = As(&decl); CJC_ASSERT(classDecl); primaryFunc->funcBody->parentClassLike = classDecl; primaryFunc->outerDecl = classDecl; primaryFunc->EnableAttr(Attribute::IN_CLASSLIKE); classDecl->body->decls.push_back(std::move(primaryFunc)); } else if (decl.astKind == ASTKind::STRUCT_DECL) { auto structDecl = As(&decl); CJC_ASSERT(structDecl); primaryFunc->funcBody->parentStruct = structDecl; primaryFunc->outerDecl = structDecl; primaryFunc->EnableAttr(Attribute::IN_STRUCT); structDecl->body->decls.push_back(std::move(primaryFunc)); } } } // namespace void DesugarFlowExpr(ASTContext& ctx, BinaryExpr& fe) { if (fe.desugarExpr != nullptr) { return; // Avoid re-enter. } if (fe.op == TokenKind::PIPELINE) { DesugarPipelineExpr(ctx, fe); } else if (fe.op == TokenKind::COMPOSITION) { DesugarCompositionExpr(ctx, fe); } } void DesugarOperatorOverloadExpr(ASTContext& ctx, BinaryExpr& be) { if (be.desugarExpr != nullptr || !be.leftExpr || !be.rightExpr) { return; } auto callExpr = MakeOwnedNode(); CopyBasicInfo(&be, callExpr.get()); ctx.RemoveTypeCheckCache(*callExpr); auto callBase = MakeOwnedNode(); CopyBasicInfo(&be, callBase.get()); ctx.RemoveTypeCheckCache(*callBase); callBase->baseExpr = std::move(be.leftExpr); callBase->field = SrcIdentifier{TOKENS[static_cast(be.op)]}; callBase->field.SetPos(be.operatorPos, be.operatorPos + strlen(TOKENS[static_cast(be.op)])); callExpr->baseFunc = std::move(callBase); auto funcArg = MakeOwned(); CopyBasicInfo(be.rightExpr.get(), funcArg.get()); ctx.RemoveTypeCheckCache(*funcArg); funcArg->expr = std::move(be.rightExpr); funcArg->ty = funcArg->expr->ty; callExpr->args.push_back(std::move(funcArg)); callExpr->sourceExpr = &be; be.desugarExpr = std::move(callExpr); if (be.TestAttr(Attribute::UNSAFE)) { be.desugarExpr->EnableAttr(Attribute::UNSAFE); } AddCurFile(be, be.curFile); } void DesugarOperatorOverloadExpr(ASTContext& ctx, UnaryExpr& ue) { if (ue.desugarExpr != nullptr) { return; // Avoid re-enter. } auto callExpr = MakeOwnedNode(); ctx.RemoveTypeCheckCache(*callExpr); auto callBase = MakeOwnedNode(); CopyBasicInfo(&ue, callBase.get()); ctx.RemoveTypeCheckCache(*callBase); callBase->baseExpr = std::move(ue.expr); callBase->field = SrcIdentifier{TOKENS[static_cast(ue.op)]}; callBase->field.SetPos(ue.operatorPos, ue.operatorPos + strlen(TOKENS[static_cast(ue.op)])); // Requried for LSP usage. callExpr->baseFunc = std::move(callBase); callExpr->sourceExpr = &ue; callExpr->begin = ue.begin; callExpr->end = ue.end; if (ue.TestAttr(Attribute::UNSAFE)) { callExpr->EnableAttr(Attribute::UNSAFE); } ue.desugarExpr = std::move(callExpr); AddCurFile(ue, ue.curFile); } void DesugarOperatorOverloadExpr(ASTContext& ctx, SubscriptExpr& se) { if (se.desugarExpr != nullptr) { return; // Avoid re-enter. } auto callExpr = MakeOwnedNode(); CopyBasicInfo(&se, callExpr.get()); ctx.RemoveTypeCheckCache(*callExpr); auto callBase = MakeOwnedNode(); CopyBasicInfo(&se, callBase.get()); ctx.RemoveTypeCheckCache(*callBase); callBase->baseExpr = std::move(se.baseExpr); callBase->field = "[]"; callBase->field.SetPos(se.leftParenPos, se.leftParenPos + std::string_view{"[]"}.size()); // Requried for LSP usage. callExpr->baseFunc = std::move(callBase); for (auto& expr : se.indexExprs) { auto funcArg = MakeOwned(); CopyBasicInfo(expr.get(), funcArg.get()); ctx.RemoveTypeCheckCache(*funcArg); funcArg->expr = std::move(expr); callExpr->args.push_back(std::move(funcArg)); } callExpr->sourceExpr = &se; if (se.TestAttr(Attribute::UNSAFE)) { callExpr->EnableAttr(Attribute::UNSAFE); } se.desugarExpr = std::move(callExpr); AddCurFile(se, se.curFile); } void DesugarSubscriptOverloadExpr(ASTContext& ctx, AssignExpr& ae) { if (ae.leftValue == nullptr || ae.rightExpr == nullptr) { return; } if (ae.leftValue->astKind != ASTKind::SUBSCRIPT_EXPR) { return; } auto subscript = StaticAs(ae.leftValue.get()); auto callExpr = MakeOwnedNode(); auto basePtr = subscript->baseExpr.get(); std::vector> indexPtrs{}; for (const auto& expr : subscript->indexExprs) { indexPtrs.push_back(expr.get()); } CopyBasicInfo(&ae, callExpr.get()); ctx.RemoveTypeCheckCache(*callExpr); auto callBase = MakeOwnedNode(); CopyBasicInfo(&ae, callBase.get()); ctx.RemoveTypeCheckCache(*callBase); callBase->baseExpr = std::move(subscript->baseExpr); if (ae.isCompound) { // Store mapped expr node for later process for side effect. callBase->baseExpr->mapExpr = basePtr; } callBase->field = "[]"; callBase->field.SetPos(subscript->leftParenPos, subscript->leftParenPos + std::string_view("[]").size()); // Requried for LSP usage. callExpr->baseFunc = std::move(callBase); callExpr->sourceExpr = &ae; for (size_t i = 0; i < subscript->indexExprs.size(); ++i) { auto funcArg1 = MakeOwned(); CopyBasicInfo(subscript->indexExprs[i].get(), funcArg1.get()); ctx.RemoveTypeCheckCache(*funcArg1); funcArg1->expr = std::move(subscript->indexExprs[i]); if (ae.isCompound) { // Store mapped expr node for later process for side effect. funcArg1->expr->mapExpr = indexPtrs[i]; } funcArg1->scopeName = funcArg1->expr->scopeName; callExpr->args.push_back(std::move(funcArg1)); } auto funcArg2 = MakeOwned(); ctx.RemoveTypeCheckCache(*funcArg2); funcArg2->name = "value"; // NOTE: This desugar will cause side effect, so need special handling in codeGen. funcArg2->expr = ae.isCompound ? CreateArgumentForCompoundAssignOverload(ae, *callExpr, basePtr, indexPtrs) : std::move(ae.rightExpr); CopyBasicInfo(funcArg2->expr.get(), funcArg2.get()); ctx.ClearTypeCheckCache(*(funcArg2->expr)); callExpr->args.push_back(std::move(funcArg2)); if (ae.TestAttr(Attribute::UNSAFE)) { callExpr->EnableAttr(Attribute::UNSAFE); } if (ae.isCompound) { callExpr->EnableAttr(Attribute::SIDE_EFFECT); } ae.desugarExpr = std::move(callExpr); AddCurFile(ae, ae.curFile); } void DesugarOperatorOverloadExpr(ASTContext& ctx, AssignExpr& ae) { if (ae.leftValue == nullptr || ae.rightExpr == nullptr) { return; } auto assignExpr = MakeOwnedNode(); CopyBasicInfo(&ae, assignExpr.get()); ctx.RemoveTypeCheckCache(*assignExpr); assignExpr->sourceExpr = &ae; assignExpr->op = TokenKind::ASSIGN; assignExpr->leftValue = std::move(ae.leftValue); assignExpr->leftValue->mapExpr = assignExpr->leftValue.get(); auto callBase = MakeOwnedNode(); CopyBasicInfo(&ae, callBase.get()); ctx.RemoveTypeCheckCache(*callBase); callBase->baseExpr = ASTCloner::Clone(assignExpr->leftValue.get()); ctx.ClearTypeCheckCache(*(callBase->baseExpr)); // Created 'callBase' is not left value, should remove attr. callBase->baseExpr->DisableAttr(Attribute::LEFT_VALUE); callBase->baseExpr->mapExpr = assignExpr->leftValue.get(); callBase->field = std::string(TOKENS[static_cast(COMPOUND_ASSIGN_EXPR_MAP.find(ae.op)->second)]); auto funcArg = MakeOwned(); CopyBasicInfo(&ae, funcArg.get()); ctx.RemoveTypeCheckCache(*funcArg); funcArg->expr = std::move(ae.rightExpr); auto callExpr = MakeOwnedNode(); CopyBasicInfo(&ae, callExpr.get()); ctx.RemoveTypeCheckCache(*callExpr); callExpr->sourceExpr = &ae; callExpr->baseFunc = std::move(callBase); callExpr->args.push_back(std::move(funcArg)); if (ae.TestAttr(Attribute::UNSAFE)) { callExpr->EnableAttr(Attribute::UNSAFE); } assignExpr->rightExpr = std::move(callExpr); assignExpr->EnableAttr(Attribute::SIDE_EFFECT); ae.desugarExpr = std::move(assignExpr); AddCurFile(ae, ae.curFile); } void DesugarCallExpr(ASTContext& ctx, CallExpr& ce) { if (ce.desugarExpr != nullptr || ce.baseFunc == nullptr) { return; } auto callExpr = MakeOwnedNode(); ctx.RemoveTypeCheckCache(*callExpr); callExpr->sourceExpr = &ce; callExpr->begin = ce.begin; callExpr->end = ce.end; auto ma = MakeOwnedNode(); ctx.RemoveTypeCheckCache(*ma); ma->scopeName = ce.baseFunc->scopeName; ma->field = "()"; ma->field.SetPos(ce.leftParenPos, ce.leftParenPos + std::string_view{"()"}.size()); // Requried for LSP usage. ma->begin = ce.baseFunc->begin; ma->end = ce.baseFunc->end; ma->baseExpr = std::move(ce.baseFunc); callExpr->baseFunc = std::move(ma); callExpr->args = std::move(ce.args); if (ce.TestAttr(Attribute::UNSAFE)) { callExpr->EnableAttr(Attribute::UNSAFE); } ce.desugarExpr = std::move(callExpr); // Contains 'AddMacroAttr' for diag after macro expansion. AddCurFile(ce, ce.curFile); } void DesugarVariadicCallExpr(ASTContext& ctx, CallExpr& ce, size_t fixedPositionalArity) { if (ce.desugarExpr != nullptr || ce.baseFunc == nullptr) { return; } auto callExpr = MakeOwnedNode(); ctx.RemoveTypeCheckCache(*callExpr); callExpr->callKind = ce.callKind; ce.callKind = CallKind::CALL_VARIADIC_FUNCTION; callExpr->sourceExpr = &ce; CopyBasicInfo(&ce, callExpr.get()); callExpr->baseFunc = std::move(ce.baseFunc); CJC_ASSERT(ce.args.size() >= fixedPositionalArity); size_t idx = 0; // Fixed positional arguments. for (; idx < fixedPositionalArity; idx++) { CJC_ASSERT(ce.args[idx]->name.Empty()); callExpr->args.emplace_back(std::move(ce.args[idx])); } // Variadic arguments. auto arrayLit = MakeOwned(); // The position of `arrayLit` should not be used to report diagnostics. // We set them here to avoid zero position error while checking candidates. CopyBasicInfo(&ce, arrayLit.get()); if (idx >= ce.args.size()) { // no vararg, it desugars to an empty array arrayLit->begin = arrayLit->end = ce.rightParenPos; } else { arrayLit->begin = ce.args[idx]->begin; arrayLit->end = ce.args.back()->end; } ctx.RemoveTypeCheckCache(*arrayLit); for (; idx < ce.args.size(); idx++) { if (!ce.args[idx]->name.Empty()) { break; } arrayLit->children.emplace_back(std::move(ce.args[idx]->expr)); ctx.RemoveTypeCheckCache(*ce.args[idx]); } callExpr->args.emplace_back(CreateFuncArg(std::move(arrayLit))); ctx.RemoveTypeCheckCache(*(callExpr->args.back())); // Named arguments. for (; idx < ce.args.size(); idx++) { callExpr->args.emplace_back(std::move(ce.args[idx])); } if (ce.TestAttr(Attribute::UNSAFE)) { callExpr->EnableAttr(Attribute::UNSAFE); } ce.desugarExpr = std::move(callExpr); AddCurFile(ce, ce.curFile); } void TypeChecker::TypeCheckerImpl::DesugarPointerCall(ASTContext& ctx, CallExpr& ce) { auto pointerExpr = MakeOwnedNode(); pointerExpr->type = MakeOwnedNode(); ctx.RemoveTypeCheckCache(*pointerExpr); ctx.RemoveTypeCheckCache(*(pointerExpr->type)); auto baseFuncTy = ce.baseFunc->ty ? SubstituteTypeAliasInTy(*ce.baseFunc->ty) : TypeManager::GetInvalidTy(); CJC_ASSERT(baseFuncTy); auto typeArgs = baseFuncTy->typeArgs; auto baseArgs = ce.baseFunc->GetTypeArgs(); IgnoreEmptyIntersection(baseArgs); // Eg: type A = Pointer; A(v), type arg of this A is not useful. bool usefulTypeArg = !typeArgs.empty() && (!typeArgs[0]->IsGeneric() || !baseArgs.empty()); Ptr argTy = nullptr; if (usefulTypeArg) { argTy = typeArgs[0]; } else if (!baseArgs.empty()) { argTy = baseArgs[0]->ty; } pointerExpr->type->ty = typeManager.GetPointerTy(argTy); if (!ce.args.empty()) { pointerExpr->arg = std::move(ce.args[0]); } if (ce.args.size() > 1) { diag.Diagnose(ce, DiagKind::sema_pointer_too_much_argument); } ce.args.clear(); pointerExpr->scopeName = ce.scopeName; pointerExpr->sourceExpr = &ce; pointerExpr->begin = ce.begin; pointerExpr->end = ce.end; ce.desugarExpr = std::move(pointerExpr); AddCurFile(ce, ce.curFile); } void TypeChecker::TypeCheckerImpl::DesugarArrayCall(ASTContext& ctx, CallExpr& ce) { auto arrayExpr = MakeOwnedNode(); arrayExpr->type = MakeOwnedNode(); ctx.RemoveTypeCheckCache(*arrayExpr); ctx.RemoveTypeCheckCache(*(arrayExpr->type)); auto baseFuncTy = ce.baseFunc->ty ? SubstituteTypeAliasInTy(*ce.baseFunc->ty) : TypeManager::GetInvalidTy(); CJC_ASSERT(baseFuncTy); auto typeArgs = typeManager.GetTypeArgs(*baseFuncTy); // ArrayTy has 'dims', must using function to get arguments. auto baseArgs = ce.baseFunc->GetTypeArgs(); IgnoreEmptyIntersection(baseArgs); if (auto varrTy = DynamicCast(baseFuncTy); varrTy) { Ptr argTy = TypeManager::GetInvalidTy(); if (!typeArgs.empty()) { argTy = typeArgs[0]; } if (typeArgs[0]->IsGeneric() && !baseArgs.empty()) { argTy = baseArgs[0]->ty; } arrayExpr->type->ty = typeManager.GetVArrayTy(*argTy, varrTy->size); arrayExpr->isValueArray = true; } else { // Eg: type A = Array; A(size, v), type args of this A is not useful. bool usefulTypeArg = !typeArgs.empty() && (!typeArgs[0]->IsGeneric() || !baseArgs.empty()); if (usefulTypeArg) { arrayExpr->type->ty = typeManager.GetArrayTy(typeArgs[0], 1); } else { Ptr argTy = TypeManager::GetInvalidTy(); if (!baseArgs.empty()) { argTy = baseArgs[0]->ty; } arrayExpr->type->ty = typeManager.GetArrayTy(argTy, 1); } } for (auto& it : ce.args) { (void)arrayExpr->args.emplace_back(std::move(it)); } ce.args.clear(); arrayExpr->scopeName = ce.scopeName; arrayExpr->sourceExpr = &ce; arrayExpr->begin = ce.begin; arrayExpr->end = ce.end; ce.desugarExpr = std::move(arrayExpr); AddCurFile(ce, ce.curFile); } void DesugarPrimaryCtor(Decl& decl, PrimaryCtorDecl& fd) { if (decl.astKind != ASTKind::CLASS_DECL && decl.astKind != ASTKind::STRUCT_DECL) { return; } if (fd.funcBody == nullptr) { return; } OwnedPtr funcBody = MakeOwned(); CopyBasicInfo(fd.funcBody.get(), funcBody.get()); funcBody->body = MakeOwned(); CopyBasicInfo(fd.funcBody.get(), funcBody->body.get()); // RetType is cloned from user written source code, which should be used to highlight in LSP. funcBody->retType = ASTCloner::Clone(fd.funcBody->retType.get(), SetIsClonedSourceCode); if (funcBody->retType) { // Despite that the whole tree is cloned, the retType node is explicitly given by the user. funcBody->retType->DisableAttr(Attribute::COMPILER_ADD); } auto funcParamList = MakeOwned(); CopyFileID(funcParamList.get(), &fd); std::vector>::iterator it; if (fd.funcBody->body) { it = fd.funcBody->body->body.begin(); } // If existing super() for class decl. DesugarPrimaryCtorHandleSuper(decl, fd, funcBody, it); DesugarPrimaryCtorHandleParam(decl, fd, funcBody, funcParamList); if (fd.funcBody->body) { // Add primary func other body. while (it != fd.funcBody->body->body.end()) { // Primary func's other body is cloned from user written source code, which should be used to highlight in // LSP. funcBody->body->body.push_back(ASTCloner::Clone(it->get(), SetIsClonedSourceCode)); ++it; } } else if (fd.TestAttr(Attribute::COMMON)) { funcBody->body = nullptr; } funcBody->paramLists.push_back(std::move(funcParamList)); DesugarPrimaryCtorSetPrimaryFunc(decl, fd, funcBody); } } // namespace Cangjie cangjie_compiler-1.0.7/src/Sema/Desugar/DesugarInTypeCheck.h000066400000000000000000000027151510705540100237140ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares private functions of desugar. */ #ifndef CANGJIE_SEMA_DESUGAR_IN_TYPECHECK_H #define CANGJIE_SEMA_DESUGAR_IN_TYPECHECK_H #include "cangjie/AST/Node.h" #include "cangjie/AST/ASTContext.h" namespace Cangjie { using namespace AST; void DesugarOperatorOverloadExpr(ASTContext& ctx, BinaryExpr& be); void DesugarOperatorOverloadExpr(ASTContext& ctx, UnaryExpr& ue); void DesugarOperatorOverloadExpr(ASTContext& ctx, SubscriptExpr& se); void DesugarOperatorOverloadExpr(ASTContext& ctx, AssignExpr& ae); void DesugarSubscriptOverloadExpr(ASTContext& ctx, AssignExpr& ae); void DesugarFlowExpr(ASTContext& ctx, BinaryExpr& fe); void DesugarCallExpr(ASTContext& ctx, CallExpr& ce); /** * Desugar variable-length arguments to array literal. For example: * *************** before desugar **************** * f(true, 1, 2, 3) * *************** after desugar **************** * f(true, [1, 2, 3]) // Suppose the `fixedPositionalArity` is 1. * */ void DesugarVariadicCallExpr(ASTContext& ctx, CallExpr& ce, size_t fixedPositionalArity); /** Add primary constructor for class or struct. */ void DesugarPrimaryCtor(AST::Decl& decl, AST::PrimaryCtorDecl& fd); } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Sema/Desugar/DesugarMacro.cpp000066400000000000000000000740021510705540100231400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Desugar macro. */ #include "DesugarMacro.h" #include #include #include #include #include #include "TypeCheckUtil.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Types.h" #include "cangjie/AST/Walker.h" #include "cangjie/Macro/TokenSerialization.h" #include "cangjie/Utils/Utils.h" #include "cangjie/Basic/StringConvertor.h" namespace Cangjie { using namespace AST; using namespace TypeCheckUtil; namespace { void DesugarTokensNormalizeString(std::vector& tokens) { for (unsigned long i = 0; i < tokens.size(); i++) { if (tokens[i].kind == TokenKind::MULTILINE_RAW_STRING) { tokens[i].SetValue(StringConvertor::Normalize(tokens[i].Value(), true)); } } } /* * Encode the token into bytes and then bytes will be stored in ArrayLit's children * For example, quote(0) will be desugared to: * Tokens([1,0,0,0,134,0,1,0,0,0,48,1,0,0,0,3,0,0,0,24,0,0,0]) */ OwnedPtr DesugarTokensToArrayLiteral(std::vector& tokens) { DesugarTokensNormalizeString(tokens); std::vector buffers = TokenSerialization::GetTokensBytes(tokens); OwnedPtr arrayLit = MakeOwned(); arrayLit->children.reserve(buffers.size()); std::transform(buffers.begin(), buffers.end(), std::back_inserter(arrayLit->children), [](auto& byte) { return CreateLitConstExpr(LitConstKind::INTEGER, std::to_string(byte), nullptr); }); return arrayLit; } OwnedPtr CreateQuoteDesugarExpr(const QuoteExpr& qexpr) { auto tksRefExpr = CreateRefExprInAST("Tokens"); tksRefExpr->begin = qexpr.begin; tksRefExpr->end = qexpr.end; auto tokensExpr = CreateCallExpr(std::move(tksRefExpr), {}); tokensExpr->EnableAttr(Attribute::COMPILER_ADD); tokensExpr->begin = qexpr.begin; tokensExpr->end = qexpr.end; return tokensExpr; } OwnedPtr CreateReadingForInExpr(const std::tuple& declArgs) { auto [declName, argPtr, argSize] = declArgs; auto varPattern = CreateVarPattern("i"); auto start = CreateLitConstExpr(LitConstKind::INTEGER, "0", nullptr); auto end = CreateRefExpr(argSize); auto rangeExpr = MakeOwned(); rangeExpr->startExpr = std::move(start); rangeExpr->stopExpr = std::move(end); rangeExpr->isClosed = false; rangeExpr->EnableAttr(Attribute::COMPILER_ADD); auto callBase = MakeOwnedNode(); callBase->baseExpr = CreateRefExpr(declName); callBase->field = "[]"; auto funcAgr1 = CreateFuncArg(CreateRefExpr("i")); auto readCallBase = MakeOwnedNode(); readCallBase->baseExpr = CreateRefExpr(argPtr); readCallBase->field = "read"; std::vector> readVector; readVector.emplace_back(CreateFuncArg(CreateRefExpr("i"))); auto readCall = CreateCallExpr(std::move(readCallBase), std::move(readVector)); readCall->EnableAttr(Attribute::UNSAFE); std::vector> funcVector; funcVector.emplace_back(std::move(funcAgr1)); funcVector.emplace_back(CreateFuncArg(std::move(readCall), "value")); auto callExpr = CreateCallExpr(std::move(callBase), std::move(funcVector)); std::vector> nodes; nodes.emplace_back(std::move(callExpr)); auto forInBody = CreateBlock(std::move(nodes)); return CreateForInExpr(std::move(varPattern), std::move(rangeExpr), std::move(forInBody)); } /** * Create var decl, like : let bufParam: RefArray = Array(paramSize, repeat: 0). * @param newPos * @param declArgs * @return */ OwnedPtr CreateReadingVarDecl( const Position& newPos, const std::pair& declArgs) { auto [declName, argSize] = declArgs; auto typeArgs = MakeOwned(); typeArgs->str = "UInt8"; typeArgs->kind = TypeKind::TYPE_UINT8; std::string v = "Array"; auto arrType = CreateRefType(v, {typeArgs.get()}); arrType->EnableAttr(Attribute::IN_CORE); auto baseF = CreateRefExpr({v, newPos, newPos, false}, nullptr, newPos, {typeArgs.get()}); baseF->EnableAttr(Attribute::IN_CORE); auto funcArg1 = CreateFuncArg(CreateRefExpr(argSize)); auto litConst = CreateLitConstExpr(LitConstKind::INTEGER, "0", nullptr); std::vector> funcVector; funcVector.emplace_back(std::move(funcArg1)); funcVector.emplace_back(CreateFuncArg(std::move(litConst), "repeat")); auto callExpr = CreateCallExpr(std::move(baseF), std::move(funcVector)); return CreateVarDecl(declName, std::move(callExpr), arrType.get()); } /** * Create var decl, like : let attr: Tokens = Tokens(). * @param declArgs * @return */ OwnedPtr CreateTokensParamDecl(const std::string argName, const Position& pos) { auto refExpr = CreateRefExprInAST("Tokens"); refExpr->begin = pos; refExpr->end = pos; auto tokensAttrCall = CreateCallExpr(std::move(refExpr), {}); return CreateVarDecl(argName, std::move(tokensAttrCall)); } /** * Create var decl, like : var tok : Tokens = Tokens() + Token(TokenKind.ILLEGAL) * @param declArgs * @return */ OwnedPtr CreateIllegalTokensDecl(const std::string argName, const Position& pos) { // Create assign expr, like: ret = ret + Token(TokenKind.ILLEGAL) auto tokenKind = CreateRefExprInAST("TokenKind"); auto tok = CreateFuncArg(CreateMemberAccess(std::move(tokenKind), "ILLEGAL")); tok->begin = pos; tok->end = pos; std::vector> tokensAttrArgs; tokensAttrArgs.emplace_back(std::move(tok)); auto fileID = CreateFuncArg(CreateLitConstExpr( LitConstKind::INTEGER, std::to_string(pos.fileID), TypeManager::GetPrimitiveTy(TypeKind::TYPE_UINT32))); auto line = CreateFuncArg(CreateLitConstExpr( LitConstKind::INTEGER, std::to_string(pos.line), TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT32))); auto column = CreateFuncArg(CreateLitConstExpr( LitConstKind::INTEGER, std::to_string(pos.column), TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT32))); auto refExpr = CreateRefExprInAST("Token"); refExpr->begin = pos; refExpr->end = pos; auto baseExpr = CreateCallExpr(std::move(refExpr), std::move(tokensAttrArgs)); std::vector> arguments; arguments.emplace_back(CreateFuncArg(std::move(baseExpr))); arguments.emplace_back(std::move(fileID)); arguments.emplace_back(std::move(line)); arguments.emplace_back(std::move(column)); auto refreshExpr = CreateRefExprInAST("refreshPos"); refreshExpr->begin = pos; refreshExpr->end = pos; auto callExpr = CreateCallExpr(std::move(refreshExpr), std::move(arguments)); auto binExpr = CreateBinaryExpr(CreateRefExpr(argName), std::move(callExpr), TokenKind::ADD); auto assignExpr = CreateAssignExpr(CreateRefExpr(argName), std::move(binExpr)); return assignExpr; } /** * Create attribute macro call, like : ident(attr, params) * @param ident * @return */ OwnedPtr CreateAttrCall(const std::string& ident, const Position& pos) { auto attr = CreateFuncArg(CreateRefExpr("attr", pos)); auto param = CreateFuncArg(CreateRefExpr("params", pos)); std::vector> retAttrArgs; (void)retAttrArgs.emplace_back(std::move(attr)); (void)retAttrArgs.emplace_back(std::move(param)); auto baseExpr = CreateRefExpr(ident, pos); auto ret = CreateCallExpr(std::move(baseExpr), std::move(retAttrArgs)); return ret; } /** * Create common macro call, like : ident(params) * @param ident * @return */ OwnedPtr CreateCommonCall(const std::string& ident, const Position& pos) { auto param = CreateFuncArg(CreateRefExpr("params", pos)); std::vector> retCommonArgs; (void)retCommonArgs.emplace_back(std::move(param)); auto baseExpr = CreateRefExpr(ident, pos); auto ret = CreateCallExpr(std::move(baseExpr), std::move(retCommonArgs)); return ret; } /** * Create var decl, like : let tBuffer = ret.toBytes() * @return */ OwnedPtr CreateToBytesVar(const std::string& refName, const Position& pos) { auto toBytesCall = CreateCallExpr(CreateMemberAccess(CreateRefExpr(refName, pos), "toBytes"), {}); return CreateVarDecl("tBuffer", std::move(toBytesCall)); } /** * Create return expression, like : return unsafePointerCastFromUint8Array(tBuffer). * @return */ OwnedPtr CreateReturnExpr(const Position& pos) { auto tBuffer = CreateFuncArg(CreateRefExpr("tBuffer", pos)); std::vector> retExprArgs; retExprArgs.emplace_back(std::move(tBuffer)); auto baseExpr = CreateRefExprInAST("unsafePointerCastFromUint8Array"); baseExpr->begin = pos; baseExpr->end = pos; auto ret = CreateCallExpr(std::move(baseExpr), std::move(retExprArgs)); return ret; } /** * Create call param, like : * (attrPtr: CPointer, attrSize: Int64, paramPtr: CPointer, * paramSize: Int64, callMacroCallPtr: CPointer). * @return */ using ParamPtr = OwnedPtr; std::tuple CreateCallParam( const Position& pos, RefType& unsafePtrType, RefType& callBackPtrType) { auto int64Type = MakeOwned(); int64Type->kind = TypeKind::TYPE_INT64; int64Type->str = "Int64"; // Create func param and Bind pos in it, for refExpr's lookup. auto attrPtrParam = CreateFuncParam("attrPtr", ASTCloner::Clone(Ptr(&unsafePtrType))); attrPtrParam->begin = pos; attrPtrParam->end = pos; auto attrSizeParam = CreateFuncParam("attrSize", ASTCloner::Clone(int64Type.get())); attrSizeParam->begin = pos; attrSizeParam->end = pos; auto paramPtrParam = CreateFuncParam("paramPtr", ASTCloner::Clone(Ptr(&unsafePtrType))); paramPtrParam->begin = pos; paramPtrParam->end = pos; auto paramSizeParam = CreateFuncParam("paramSize", std::move(int64Type)); paramSizeParam->begin = pos; paramSizeParam->end = pos; auto callMacroCallPtr = CreateFuncParam("callMacroCallPtr", ASTCloner::Clone(Ptr(&callBackPtrType))); return {std::move(attrPtrParam), std::move(attrSizeParam), std::move(paramPtrParam), std::move(paramSizeParam), std::move(callMacroCallPtr)}; } /** * Create the wrapper function: external func macroCall_a_ident(attrPtr: CPointer, attrSize: Int64, * paramPtr: CPointer, paramSize: Int64, callMacroCallPtr: CPointer): CPointer. * @param funcName * @param pos * @param body * @param params * @param unsafePtrType * @return */ OwnedPtr CreateWrapperFuncDecl(const std::string& funcName, const Position& pos, OwnedPtr body, std::vector>& params, OwnedPtr unsafePtrType) { // Create func param list, func body and func decl. auto paramList = CreateFuncParamList(std::move(params)); std::vector> funcParamLists; funcParamLists.emplace_back(std::move(paramList)); auto funcBody = CreateFuncBody(std::move(funcParamLists), std::move(unsafePtrType), std::move(body)); funcBody->EnableAttr(Attribute::C, Attribute::MACRO_INVOKE_BODY, Attribute::PUBLIC); auto funcDecl = CreateFuncDecl(funcName, std::move(funcBody)); funcDecl->toBeCompiled = true; // For incremental compilation. funcDecl->identifier.SetPos(pos, pos); funcDecl->begin = pos; funcDecl->end = pos; std::set modifiers; modifiers.emplace(TokenKind::PUBLIC, pos); funcDecl->modifiers.insert(modifiers.begin(), modifiers.end()); funcDecl->EnableAttr(Attribute::NO_MANGLE, Attribute::C, Attribute::MACRO_INVOKE_FUNC, Attribute::PUBLIC); return funcDecl; } /** * Create return expression, like : return unsafePointerCastFromUint8Array(@{}). * @return */ OwnedPtr CreateCatchReturnExpr(const Position& pos) { auto arg = CreateFuncArg(CreateRefExpr("tBuffer", pos)); std::vector> retExprArgs; retExprArgs.emplace_back(std::move(arg)); auto baseExpr = CreateRefExprInAST("unsafePointerCastFromUint8Array"); baseExpr->begin = pos; baseExpr->end = pos; return CreateCallExpr(std::move(baseExpr), std::move(retExprArgs)); } /** * Create catch block, like * { * var tok = Tokens() * tok = tok + Token(TokenKind::ILLEGAL) * return unsafePointerCastFromUint8Array(@{}) * }. * @return */ void CreateCatchBlock(TryExpr& tryExpr, const Position& pos, bool printStack = false) { // To create ExceptTypePattern e: Exception std::vector> nodes; // Create print stack, like : e.printStack(). if (printStack) { auto printStackNode = CreateCallExpr(CreateMemberAccess(CreateRefExpr("e"), "printStackTrace"), {}); nodes.emplace_back(std::move(printStackNode)); } auto tokVar = CreateTokensParamDecl("tokVar", pos); tokVar->isVar = true; nodes.emplace_back(std::move(tokVar)); auto assignExpr = CreateIllegalTokensDecl("tokVar", pos); nodes.emplace_back(std::move(assignExpr)); auto tBufferVar = CreateToBytesVar("tokVar", pos); nodes.emplace_back(std::move(tBufferVar)); auto retExpr = CreateCatchReturnExpr(pos); nodes.emplace_back(std::move(retExpr)); auto catchBlock = CreateBlock(std::move(nodes)); tryExpr.catchPosVector.emplace_back(INVALID_POSITION); tryExpr.catchBlocks.push_back(std::move(catchBlock)); } /** * Create ExceptTypePattern, like : 'catch : (e: Exception)' or 'catch : (e: MacroWithContextException)' * @return */ void CreateCatchPattern(TryExpr& tryExpr, const std::string& exceptionTypeStr) { auto exceptTypePattern = MakeOwnedNode(); auto varPattern = MakeOwned(); varPattern->varDecl = CreateVarDecl("e"); varPattern->varDecl->parentPattern = varPattern.get(); OwnedPtr exceptionType; if (exceptionTypeStr == CLASS_EXCEPTION) { exceptionType = CreateRefTypeInCore(CLASS_EXCEPTION); } else if (exceptionTypeStr == CLASS_ERROR) { exceptionType = CreateRefTypeInCore(CLASS_ERROR); } else { exceptionType = MakeOwned(); exceptionType->ref.identifier = exceptionTypeStr; exceptionType->EnableAttr(Attribute::IN_MACRO); } exceptionType->begin = tryExpr.tryBlock->begin; exceptionType->end = tryExpr.tryBlock->begin; exceptionType->EnableAttr(Attribute::COMPILER_ADD); exceptTypePattern->types.push_back(std::move(exceptionType)); exceptTypePattern->pattern = std::move(varPattern); tryExpr.catchPatterns.push_back(std::move(exceptTypePattern)); } // Create thread_local assign expr, like : MACRO_OBJECT.set(XXX). static OwnedPtr CreateTLAssignExpr(const std::string& refName, const Position& newPos) { auto tlBase = MakeOwnedNode(); tlBase->baseExpr = CreateRefExpr(MACRO_OBJECT_NAME); tlBase->baseExpr->EnableAttr(Attribute::IN_MACRO); tlBase->baseExpr->begin = newPos; tlBase->baseExpr->end = newPos; tlBase->field = "set"; std::vector> funcArgVector; funcArgVector.emplace_back(CreateFuncArg(CreateRefExpr(refName))); auto assignCall = CreateCallExpr(std::move(tlBase), std::move(funcArgVector)); assignCall->EnableAttr(Attribute::COMPILER_ADD); assignCall->begin = newPos; assignCall->end = newPos; return assignCall; } // Create finally block, like : { MACRO_OBJECT.set(None) } static void CreateFinallyBlock(TryExpr& tryExpr, const Position& pos) { std::vector> nodes; auto tlAssignCall = CreateTLAssignExpr("None", pos); nodes.emplace_back(std::move(tlAssignCall)); tryExpr.finallyBlock = CreateBlock(std::move(nodes)); } /** * Create try expression, like * try { * MACRO_OBJECT.set(callMacroCallPtr) // CJNATIVE * let bufParam: Array = Array(paramSize, repeat: 0) * for (i in 0..paramSize) { * bufParam[i] = unsafe{ paramPtr.read(i) } * } * let params: Tokens = Tokens(bufParam) * let ret: Tokens = ident(params) // CJNATIVE * let tBuffer = ret.toBytes() * return unsafePointerCastFromUint8Array(tBuffer) * } * catch (e: MacroWithContextException) { * var tok = Tokens() * tok = tok + Token(TokenKind::ILLEGAL) * return unsafePointerCastFromUint8Array(tok) * } * catch (e: Exception) { * var tok = Tokens() * tok = tok + Token(TokenKind::ILLEGAL) * return unsafePointerCastFromUint8Array(tok) * } * catch (e: Error) { * var tok = Tokens() * tok = tok + Token(TokenKind::ILLEGAL) * return unsafePointerCastFromUint8Array(tok) * } finally { * MACRO_OBJECT.set(None) // CJNATIVE only * } * @return */ OwnedPtr CreateWrapperTryExpr(OwnedPtr tryBlock) { auto tryExpr = MakeOwned(); tryExpr->tryBlock = std::move(tryBlock); CreateCatchPattern(*tryExpr, MC_EXCEPTION); CreateCatchBlock(*tryExpr, tryExpr->tryBlock->begin); CreateCatchPattern(*tryExpr, CLASS_EXCEPTION); CreateCatchBlock(*tryExpr, tryExpr->tryBlock->begin, true); // For oom, catch error is required because oom inherits from error rather than exception. CreateCatchPattern(*tryExpr, CLASS_ERROR); CreateCatchBlock(*tryExpr, tryExpr->tryBlock->begin, true); CreateFinallyBlock(*tryExpr, tryExpr->tryBlock->begin); return tryExpr; } /** * Create if expression, like * if (paramSize > 0) { * params = Tokens(bufParam) * } * @return */ OwnedPtr CreateWrapperIfExpr( const std::tuple& declArgs, const Position& pos) { // Create condition expr, like: paramSize > 0 auto [argName, argBuf, argSize] = declArgs; auto argSizeExpr = CreateRefExpr(argSize); auto litConst = CreateLitConstExpr(LitConstKind::INTEGER, "0", nullptr); auto cond = CreateBinaryExpr(std::move(argSizeExpr), std::move(litConst), TokenKind::GT); // Create assign expr, like: params = Tokens(bufParam) auto thenExprBlock = MakeOwnedNode(); auto bufAttr = CreateFuncArg(CreateRefExpr(argBuf)); std::vector> tokensAttrArgs; tokensAttrArgs.emplace_back(std::move(bufAttr)); auto refExpr = CreateRefExprInAST("Tokens"); refExpr->begin = pos; refExpr->end = pos; auto callExpr = CreateCallExpr(std::move(refExpr), std::move(tokensAttrArgs)); auto assignExpr = CreateAssignExpr(CreateRefExpr(argName), std::move(callExpr)); thenExprBlock->body.push_back(std::move(assignExpr)); auto elseExprBlock = MakeOwnedNode(); auto ifExpr = CreateIfExpr(std::move(cond), std::move(thenExprBlock), std::move(elseExprBlock)); ifExpr->hasElse = false; ifExpr->EnableAttr(Attribute::COMPILER_ADD); return ifExpr; } /** * Create block expression, like * { * MACRO_OBJECT.set(callMacroCallPtr) // CJNATIVE * let bufParam: Array = Array(paramSize, repeat: 0) * for (i in 0..paramSize) { * bufParam[i] = unsafe{ paramPtr.read(i) } * } * var params: Tokens = Tokens() * if (paramSize > 0) { * params = Tokens(bufParam) * } * let ret: Tokens = ident(params) // CJNATIVE * let tBuffer = ret.toBytes() * return unsafePointerCastFromUint8Array(tBuffer) * } * @return */ OwnedPtr CreateWrapperTryBlock(const Position& pos, const std::string& ident, bool isAttr) { // Pos after 'pos' to lookup field. auto newPos = pos + BEGIN_POSITION; auto tlAssignCall = CreateTLAssignExpr("callMacroCallPtr", newPos); // Create var decl, like : let bufParam: RefArray = Array(paramSize, repeat: 0). auto bufAttrVar = CreateReadingVarDecl(newPos, {"bufAttr", "attrSize"}); // Create forIn expression. auto bufAttrForIn = CreateReadingForInExpr({"bufAttr", "attrPtr", "attrSize"}); auto bufParamVar = CreateReadingVarDecl(newPos, {"bufParam", "paramSize"}); auto bufParamForIn = CreateReadingForInExpr({"bufParam", "paramPtr", "paramSize"}); auto attrVar = CreateTokensParamDecl("attr", pos); attrVar->isVar = true; auto attrIfExpr = CreateWrapperIfExpr({"attr", "bufAttr", "attrSize"}, pos); auto paramsVar = CreateTokensParamDecl("params", pos); paramsVar->isVar = true; auto argsIfExpr = CreateWrapperIfExpr({"params", "bufParam", "paramSize"}, pos); auto callExpr = isAttr ? CreateAttrCall(ident, newPos) : CreateCommonCall(ident, newPos); callExpr->baseFunc->EnableAttr(Attribute::MACRO_INVOKE_BODY); callExpr->EnableAttr(Attribute::MACRO_INVOKE_BODY); auto retVar = CreateVarDecl("ret", std::move(callExpr)); retVar->isVar = true; auto tBufferVar = CreateToBytesVar("ret", newPos); auto retExpr = CreateReturnExpr(newPos); std::vector> nodes; nodes.emplace_back(std::move(tlAssignCall)); // Add attr var decl. if (isAttr) { nodes.emplace_back(std::move(bufAttrVar)); nodes.emplace_back(std::move(bufAttrForIn)); nodes.emplace_back(std::move(attrVar)); nodes.emplace_back(std::move(attrIfExpr)); } nodes.emplace_back(std::move(bufParamVar)); nodes.emplace_back(std::move(bufParamForIn)); nodes.emplace_back(std::move(paramsVar)); nodes.emplace_back(std::move(argsIfExpr)); nodes.emplace_back(std::move(retVar)); nodes.emplace_back(std::move(tBufferVar)); nodes.emplace_back(std::move(retExpr)); return CreateBlock(std::move(nodes)); } void AddMacroFuncDecl(File& curFile, const Position& pos, const std::string& ident, bool isAttr) { auto tryBlock = CreateWrapperTryBlock(pos, ident, isAttr); tryBlock->begin = pos; tryBlock->end = pos; auto tryExpr = CreateWrapperTryExpr(std::move(tryBlock)); tryExpr->EnableAttr(Attribute::MACRO_INVOKE_BODY); auto body = MakeOwned(); body->begin = pos; body->end = pos; body->body.emplace_back(std::move(tryExpr)); auto callBackPtrType = MakeOwned(); callBackPtrType->begin = pos; callBackPtrType->ref.identifier = "CPointer"; auto callBackPrimitiveType = MakeOwned(); callBackPrimitiveType->kind = TypeKind::TYPE_UNIT; callBackPtrType->typeArguments.push_back(std::move(callBackPrimitiveType)); callBackPtrType->end = pos; // Create Types, like 'CPointer' : let attrPtr: CPointer. auto unsafePtrType = MakeOwned(); unsafePtrType->begin = pos; unsafePtrType->ref.identifier = "CPointer"; auto unsafePrimitiveType = MakeOwned(); unsafePrimitiveType->kind = TypeKind::TYPE_UINT8; unsafePtrType->typeArguments.push_back(std::move(unsafePrimitiveType)); auto [attrPtrParam, attrSizeParam, paramPtrParam, paramSizeParam, callMacroCallPtr] = CreateCallParam(pos, *unsafePtrType, *callBackPtrType); std::vector> params = { attrPtrParam.get(), attrSizeParam.get(), paramPtrParam.get(), paramSizeParam.get(), callMacroCallPtr.get()}; // If is not attr macro, should remove attr func arg. if (!isAttr) { // The attr macro has 2 more arguments than the normal version. // So if it is not attr, we should delete 2 of them. params.erase(params.begin(), params.begin() + 2); } std::string packageName = ""; if (curFile.curPackage != nullptr) { packageName = curFile.curPackage->fullPackageName; } auto funcName = Utils::GetMacroFuncName(packageName, isAttr, ident); auto funcDecl = CreateWrapperFuncDecl(funcName, pos, std::move(body), params, std::move(unsafePtrType)); funcDecl->EnableAttr(Attribute::GLOBAL); curFile.decls.emplace_back(std::move(funcDecl)); } } // namespace OwnedPtr CreateToTokensMethod(const OwnedPtr& expr) { // For case: quote($(Token(RPAREN))), need to add position. auto desugarTokenInQuote = [](Ptr curNode) -> VisitAction { if (curNode->astKind != ASTKind::CALL_EXPR) { return VisitAction::WALK_CHILDREN; } auto ce = StaticAs(curNode); if (ce->baseFunc && ce->baseFunc->astKind == ASTKind::REF_EXPR) { auto re = StaticAs(ce->baseFunc.get()); if (re->ref.identifier == "Token") { auto uint32Ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UINT32); auto int32Ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT32); std::vector> args; args.emplace_back(CreateFuncArg(ASTCloner::Clone(Ptr(ce)))); args.emplace_back(CreateFuncArg( CreateLitConstExpr(LitConstKind::INTEGER, std::to_string(ce->begin.fileID), uint32Ty))); args.emplace_back(CreateFuncArg( CreateLitConstExpr(LitConstKind::INTEGER, std::to_string(ce->begin.line), int32Ty))); args.emplace_back(CreateFuncArg( CreateLitConstExpr(LitConstKind::INTEGER, std::to_string(ce->begin.column), int32Ty))); auto refreshExpr = CreateRefExprInAST("refreshPos"); auto callExpr = CreateCallExpr(std::move(refreshExpr), std::move(args)); ce->desugarExpr = std::move(callExpr); return VisitAction::SKIP_CHILDREN; } } return VisitAction::WALK_CHILDREN; }; Walker walker(expr.get(), desugarTokenInQuote); walker.Walk(); if (expr->desugarExpr) { return CreateMemberAccess(std::move(expr->desugarExpr), "toTokens"); } return CreateMemberAccess(ASTCloner::Clone(expr.get(), SetIsClonedSourceCode), "toTokens"); } void DesugarQuoteExpr(QuoteExpr& qe) { if (qe.exprs.empty()) { qe.desugarExpr = CreateQuoteDesugarExpr(qe); } for (auto& expr : qe.exprs) { OwnedPtr tokensExpr; if (expr->astKind == ASTKind::TOKEN_PART) { // Tokens(ArrayLiteral(...)). auto tokenPart = StaticAs(expr.get()); auto arrayLit = DesugarTokensToArrayLiteral(tokenPart->tokens); CopyBasicInfo(expr.get(), arrayLit.get()); std::vector> args; (void)args.emplace_back(CreateFuncArg(std::move(arrayLit))); auto tksRefExpr = CreateRefExprInAST("Tokens"); tksRefExpr->begin = expr->begin; tksRefExpr->end = expr->end; tokensExpr = CreateCallExpr(std::move(tksRefExpr), std::move(args)); std::vector> args0; (void)args0.emplace_back(CreateFuncArg(std::move(tokensExpr))); auto refreshExpr = CreateRefExprInAST("refreshTokensPosition"); refreshExpr->begin = expr->begin; refreshExpr->end = expr->end; tokensExpr = CreateCallExpr(std::move(refreshExpr), std::move(args0)); } else if (expr->astKind == ASTKind::QUOTE_EXPR) { // dollarExpr is quoteExpr. auto quoteExpr = StaticAs(expr.get()); DesugarQuoteExpr(*quoteExpr); tokensExpr = std::move(quoteExpr->desugarExpr); } else { // a.toTokens(). auto ma = CreateToTokensMethod(expr); auto ce = CreateCallExpr(std::move(ma), {}); ce->needCheckToTokens = true; tokensExpr = std::move(ce); } tokensExpr->EnableAttr(Attribute::COMPILER_ADD); CopyBasicInfo(expr.get(), tokensExpr.get()); // Tokens1 + Tokens2 => Tokens1.concat(Tokens2). if (qe.desugarExpr) { auto tokens1 = CreateMemberAccess(ASTCloner::Clone(qe.desugarExpr.get()), "concat"); tokens1->begin = expr->begin; tokens1->end = expr->end; std::vector> funcArgs; funcArgs.emplace_back(CreateFuncArg(std::move(tokensExpr))); qe.desugarExpr = CreateCallExpr(std::move(tokens1), std::move(funcArgs)); qe.desugarExpr->EnableAttr(Attribute::COMPILER_ADD); } else { qe.desugarExpr = std::move(tokensExpr); } } auto curFile = qe.curFile; Walker assignCurFile(qe.desugarExpr.get(), [&curFile](Ptr curNode) -> VisitAction { curNode->curFile = curFile; return VisitAction::WALK_CHILDREN; }); assignCurFile.Walk(); } void DesugarMacroDecl(File& file) { auto size = file.decls.size(); for (size_t i = 0; i < size; ++i) { if (file.decls[i]->astKind == ASTKind::MACRO_DECL && !file.decls[i]->TestAttr(Attribute::HAS_BROKEN)) { auto macroDecl = RawStaticCast(file.decls[i].get()); if (macroDecl->desugarDecl) { continue; // NOTE: During incremental compilation, this may be set. } auto funcDecl = MakeOwnedNode(); funcDecl->curFile = macroDecl->curFile; funcDecl->fullPackageName = macroDecl->curFile->curPackage->fullPackageName; funcDecl->begin = macroDecl->begin; funcDecl->end = macroDecl->end; funcDecl->identifier = macroDecl->identifier; funcDecl->modifiers.insert(macroDecl->modifiers.begin(), macroDecl->modifiers.end()); funcDecl->CloneAttrs(*macroDecl); funcDecl->rawMangleName = macroDecl->rawMangleName; funcDecl->funcBody = std::move(macroDecl->funcBody); funcDecl->end = funcDecl->funcBody->end; funcDecl->toBeCompiled = macroDecl->toBeCompiled; // For incremental compilation. funcDecl->comments = std::move(macroDecl->comments); auto isAttr = funcDecl->funcBody->paramLists.front()->params.size() == 2; for (auto& anno : macroDecl->annotations) { if (anno->kind == AnnotationKind::DEPRECATED) { funcDecl->annotations.emplace_back(ASTCloner::Clone(anno.get(), SetIsClonedSourceCode)); } } macroDecl->desugarDecl = std::move(funcDecl); AddMacroFuncDecl(file, macroDecl->end, macroDecl->identifier, isAttr); } } } } // namespace Cangjie cangjie_compiler-1.0.7/src/Sema/Desugar/DesugarMacro.h000066400000000000000000000055451510705540100226130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares private functions of desugar macro. */ #ifndef CANGJIE_SEMA_DESUGAR_MACRO_H #define CANGJIE_SEMA_DESUGAR_MACRO_H #include "cangjie/AST/Node.h" namespace Cangjie { using namespace AST; /** * Create a wrapper func decl like this * * Attr macro wrapper func * """ func macroCall_a_ident (attrPtr: CPointer, attrSize: Int64, * paramPtr: CPointer, paramSize: Int64): CPointer { * try { * let bufAttr: Array = Array(attrSize, repeat: 0) * for (i in 0..attrSize) { * bufAttr[i] = unsafe{ attrPtr.read(i) } * } * let bufParam: Array = Array(paramSize, repeat: 0) * for (i in 0..paramSize) { * bufParam[i] = unsafe{ paramPtr.read(i) } * } * let attr: Tokens = Tokens(bufAttr) * let params: Tokens = Tokens(bufParam) * let ret: Tokens = ident(attr, params) * let tBuffer = ret.toBytes() * return unsafePointerCastFromUint8Array(tBuffer) * } * catch (e: Exception) { * e.printStackTrace() * return unsafePointerCastFromUint8Array(@{}) * } * } * """ * * Common macro wrapper func * """ func macroCall_c_ident (paramPtr: CPointer, paramSize: Int64): CPointer { * try { * let bufParam: Array = Array(paramSize, repeat: 0) * for (i in 0..paramSize) { * bufParam[i] = unsafe{ paramPtr.read(i) } * } * let params: Tokens = Tokens(bufParam) * let ret: Tokens = ident(params) * let tBuffer = ret.toBytes() * return unsafePointerCastFromUint8Array(tBuffer) * } * catch (e: Exception) { * e.printStackTrace() * return unsafePointerCastFromUint8Array(@{}) * } * } * """ * * @param curFile : CurFile node to add wrapper node. * @param pos : Macro decl's end pos. * @param ident : Macro decl's ident. * @param isAttr : True if is a attr macro. */ void DesugarMacroDecl(File& file); /** * Desugar quote expression. * * Before desugar: quote (1 + $(a) + 3) * * After desugar: Tokens([Token(1), Token(+)]) + a.ToTokens() + Tokens([Token(+), Token(3)]) * * @param qe : QuoteExpr desugared. */ void DesugarQuoteExpr(QuoteExpr& qe); void AddMacroContextInfo(FuncBody& funcBody); } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Sema/DiagSuppressor.cpp000066400000000000000000000016371510705540100221500ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements diagnostic suppressor class for semantic check. */ #include "DiagSuppressor.h" using namespace Cangjie; using namespace std; std::vector DiagSuppressor::GetSuppressedDiag() { return diag.ConsumeStoredDiags(); } void DiagSuppressor::ReportDiag() { auto diags = GetSuppressedDiag(); diag.EnableDiagnose(originDiagVec); for (auto& d : diags) { diag.Diagnose(d); } originDiagVec = diag.DisableDiagnose(); } bool DiagSuppressor::HasError() const { return Utils::In(diag.GetStoredDiags(), [](const Diagnostic& d) { return d.diagSeverity == DiagSeverity::DS_ERROR; }); } cangjie_compiler-1.0.7/src/Sema/DiagSuppressor.h000066400000000000000000000017021510705540100216060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements diagnostic suppressor class for semantic check. */ #ifndef CANGJIE_DIAGSUPPRESSOR_H #define CANGJIE_DIAGSUPPRESSOR_H #include "cangjie/Basic/DiagnosticEngine.h" namespace Cangjie { class DiagSuppressor { public: explicit DiagSuppressor(DiagnosticEngine& diag) : diag(diag) { originDiagVec = diag.DisableDiagnose(); } ~DiagSuppressor() { diag.EnableDiagnose(originDiagVec); } std::vector GetSuppressedDiag(); void ReportDiag(); bool HasError() const; private: DiagnosticEngine& diag; std::vector originDiagVec; }; } // namespace Cangjie #endif // CANGJIE_DIAGSUPPRESSOR_H cangjie_compiler-1.0.7/src/Sema/Diags.cpp000066400000000000000000000674031510705540100202300ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements diagnostics for type checker. */ #include "Diags.h" #include #include #include #include #include "TypeCheckUtil.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; namespace { std::string GetParamsType(const std::vector>& typeList) { std::string result = "("; for (auto& ty : typeList) { std::string tyName = ty != nullptr ? ty->String() : "UnknownType"; result += tyName + ", "; } if (!typeList.empty()) { result.erase(result.size() - 2, 2); // Erase last extra ", " which length is 2. } result += ")"; return result; } void DiagWrongNumberOfArgumentsCommon( DiagnosticEngine& diag, const CallExpr& ce, const std::vector>& paramTys, Ptr fd) { auto expectedNum = paramTys.size(); auto foundNum = ce.args.size(); CJC_ASSERT(expectedNum != foundNum); std::string symptom; if (expectedNum > foundNum) { symptom = expectedNum - foundNum == 1 ? "missing argument" : "missing arguments"; } else { symptom = foundNum - expectedNum == 1 ? "extra argument given" : "extra arguments given"; } std::string lst = GetParamsType(paramTys); std::string expected = std::to_string(expectedNum) + (expectedNum == 1 ? " argument" : " arguments"); // If the position of "()" exists, using paren positions' range, otherwise using the range of 'ce' begin to end. bool parenPosExist = !ce.leftParenPos.IsZero() && !ce.rightParenPos.IsZero(); auto beginPos = parenPosExist ? ce.leftParenPos : ce.begin; // For trailing lambda, the last argument is after 'rightParenPos', using the end of lambda as the range end. bool lastArgAfterParen = !ce.args.empty() && ce.args.back() && ce.args.back()->end > ce.rightParenPos; auto endPos = parenPosExist ? (lastArgAfterParen ? ce.args.back()->end : ce.rightParenPos + 1) : ce.end; auto builder = diag.DiagnoseRefactor( DiagKindRefactor::sema_wrong_number_of_arguments, ce, MakeRange(beginPos, endPos), symptom, lst); builder.AddMainHintArguments(expected, std::to_string(foundNum)); if (fd != nullptr && fd->ShouldDiagnose()) { builder.AddNote(*fd, MakeRange(fd->identifier), "found candidate"); } else if (fd == nullptr) { Ptr target = ce.baseFunc->GetTarget(); if (target == nullptr) { return; } else if (auto vd = DynamicCast(target); vd && vd->ShouldDiagnose()) { builder.AddNote(*vd, MakeRange(vd->identifier), "found candidate"); } } } std::set GetRestNamedParams(const std::vector>& params, size_t offset) { std::set uncheckedNamedParams{}; for (size_t i = offset; i < params.size(); ++i) { if (params[i]->isNamedParam) { uncheckedNamedParams.insert(i); } } return uncheckedNamedParams; } std::set GetRestArgNames( const FuncDecl& fd, const std::vector>& args, size_t offset) { std::set uncheckedArgNames{}; for (size_t i = offset; i < args.size(); ++i) { CJC_NULLPTR_CHECK(args[i]); std::string name = GetArgName(fd, *args[i]); if (!name.empty()) { uncheckedArgNames.emplace(name); } } return uncheckedArgNames; } } // namespace namespace Cangjie::Sema { Range MakeRangeForDeclIdentifier(const Decl& decl) { // Optimize range for `init`. if (decl.astKind == ASTKind::FUNC_DECL && decl.TestAttr(Attribute::CONSTRUCTOR)) { CJC_NULLPTR_CHECK(decl.outerDecl); const Decl& outerDecl = *decl.outerDecl; if (decl.identifier.ZeroPos()) { // Compiler added default `init`. return MakeRange(outerDecl.identifier); } else if (decl.TestAttr(Attribute::PRIMARY_CONSTRUCTOR)) { // Primary constructor's user visible identifier is the type name. return MakeRange(decl.identifier.Begin(), outerDecl.identifier); } else { return MakeRange(decl.identifier); } } else if (auto ed = DynamicCast(&decl); ed && ed->extendedType) { // Recheck for decls from common part, we use the position of the decl due to no other available positions. if (ed->TestAttr(AST::Attribute::FROM_COMMON_PART)) { return MakeRange(ed->begin, ed->end); } return MakeRange(ed->extendedType->begin, ed->extendedType->end); } else if (auto vpd = DynamicCast(&decl); vpd && vpd->irrefutablePattern) { return MakeRange(vpd->irrefutablePattern->begin, vpd->irrefutablePattern->end); } if (decl.identifier.ZeroPos()) { return MakeRange(decl.begin, decl.end); } else if (auto fd = DynamicCast(&decl); fd && (fd->isGetter || fd->isSetter)) { return MakeRange(decl.identifier.Begin(), decl.identifier.Begin() + 3); // `get` and `set` are 3 letter words } else { return MakeRange(decl.identifier); } } void DiagRedefinitionWithFoundNode(DiagnosticEngine& diag, const Decl& current, const Decl& previous) { if (current.TestAttr(Attribute::IS_BROKEN)) { return; } CJC_ASSERT(current.identifier == previous.identifier); const Decl* declUp = &previous; const Decl* declDown = ¤t; auto rangeUp = MakeRangeForDeclIdentifier(previous); auto rangeDown = MakeRangeForDeclIdentifier(current); // NOTE: when one is private global, we need report private decl conflict against non-private version. if (rangeUp.begin > rangeDown.begin || (previous.TestAttr(Attribute::GLOBAL, Attribute::PRIVATE) && current.curFile != previous.curFile)) { const Decl* tmpDecl = declUp; declUp = declDown; declDown = tmpDecl; auto tmpRange = rangeUp; rangeUp = rangeDown; rangeDown = tmpRange; } auto builder = diag.DiagnoseRefactor( DiagKindRefactor::sema_redefinition, *declDown, rangeDown, declDown->identifier); builder.AddNote(*declUp, rangeUp, "'" + declDown->identifier + "' is previously declared here"); } void DiagOverloadConflict(DiagnosticEngine& diag, const std::vector>& sameSigFuncs) { // If the 'sameSigFuncs' has size 1, then the function is conflicting with imported declaration, // and the error for imported declaration will be generated later. CJC_ASSERT(sameSigFuncs.size() >= 1); auto baseFd = sameSigFuncs.front(); auto getIdentifier = [](auto fd) { return fd->identifierForLsp.empty() ? fd->identifier.Val() : fd->identifierForLsp; }; auto identifier = getIdentifier(baseFd); std::string kind = "function"; if (baseFd->TestAttr(Attribute::MACRO_FUNC)) { kind = "macro"; } else if (IsClassOrEnumConstructor(*baseFd)) { kind = "constructor"; } else if (IsStaticInitializer(*baseFd)) { kind = "static constructor"; } auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_overload_conflicts, *baseFd, MakeRange(baseFd->identifier.Begin(), identifier), kind, identifier); auto hasConstraints = [](auto fd) { auto generic = fd->GetGeneric(); return generic && !generic->genericConstraints.empty(); }; if (std::any_of(sameSigFuncs.begin(), sameSigFuncs.end(), hasConstraints)) { builder.AddNote("generic constraints are not involved in the overloading"); } for (auto it = sameSigFuncs.begin() + 1; it != sameSigFuncs.end(); ++it) { auto fd = *it; builder.AddNote(*fd, MakeRange(fd->identifier.Begin(), getIdentifier(fd)), "conflict with the declaration"); } } void DiagMismatchedTypesWithFoundTy(DiagnosticEngine& diag, const Node& node, const std::string& expected, const std::string& found, const std::string& note) { if (!node.ShouldDiagnose(true)) { return; } CJC_ASSERT(expected != found); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_mismatched_types, node); builder.AddMainHintArguments(expected, found); if (!note.empty()) { builder.AddNote(note); } } void DiagMismatchedTypesWithFoundTy( DiagnosticEngine& diag, const Node& node, const Ty& expected, const Ty& found, const std::string& note) { if (!node.ShouldDiagnose(true)) { return; } CJC_ASSERT(Ty::IsTyCorrect(&expected)); CJC_ASSERT(Ty::IsTyCorrect(&found)); std::string expectedStr = expected.String(); std::string foundStr = found.String(); if (expectedStr == foundStr) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_mismatched_types, node); Ptr expectedDecl = Ty::GetDeclPtrOfTy(&expected); if (expectedDecl) { expectedStr += "' in '" + expectedDecl->fullPackageName; builder.AddNote(*expectedDecl, MakeRangeForDeclIdentifier(*expectedDecl), "'" + expectedStr + "'"); } Ptr foundDecl = Ty::GetDeclPtrOfTy(&found); if (foundDecl) { foundStr += "' in '" + foundDecl->fullPackageName; builder.AddNote(*foundDecl, MakeRangeForDeclIdentifier(*foundDecl), "'" + foundStr + "'"); } builder.AddMainHintArguments(expectedStr, foundStr); if (!note.empty()) { builder.AddNote(note); } } else { DiagMismatchedTypesWithFoundTy(diag, node, expectedStr, foundStr, note); } } void DiagMismatchedTypes(DiagnosticEngine& diag, const Node& node, const Ty& type, const std::string& note) { if (!Ty::IsTyCorrect(node.ty)) { return; // Should have been diagnosed before. } DiagMismatchedTypesWithFoundTy(diag, node, type, *node.ty, note); } void DiagMismatchedTypes(DiagnosticEngine& diag, const Node& node, const Node& type, const std::string& because) { if (!Ty::IsTyCorrect(node.ty)) { return; // Should have been diagnosed before. } CJC_ASSERT(Ty::IsTyCorrect(type.ty)); if (type.ShouldDiagnose() && !because.empty()) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_mismatched_types_because, node); auto tyStr = node.ty->String(); if (auto thisTy = DynamicCast(node.ty); thisTy) { tyStr = ClassTy(thisTy->name, *thisTy->declPtr, thisTy->typeArgs).String(); } builder.AddMainHintArguments(type.ty->String(), tyStr); builder.AddHint(type, type.ty->String(), because); } else { DiagMismatchedTypesWithFoundTy(diag, node, *type.ty, *node.ty); } } void DiagUnableToInferReturnType(DiagnosticEngine& diag, const FuncDecl& fd) { if (Ty::IsTyCorrect(fd.ty)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_unable_to_infer_return_type, fd, MakeRange(fd.identifier)); } } void DiagUnableToInferReturnType(DiagnosticEngine& diag, const FuncDecl& fd, const Expr& expr) { if (Ty::IsTyCorrect(fd.ty)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_unable_to_infer_return_type, fd, MakeRange(fd.identifier)) .AddNote(expr, MakeRange(expr.begin, expr.end), "with recursive usage from"); } } void DiagUnableToInferReturnType(DiagnosticEngine& diag, const FuncBody& fb) { if (fb.funcDecl == nullptr) { diag.DiagnoseRefactor(DiagKindRefactor::sema_unable_to_infer_return_type, fb); } else { DiagUnableToInferReturnType(diag, *fb.funcDecl); } } void DiagWrongNumberOfArguments(DiagnosticEngine& diag, const CallExpr& ce, const std::vector>& paramTys) { if (!ce.ShouldDiagnose(true)) { return; } DiagWrongNumberOfArgumentsCommon(diag, ce, paramTys, nullptr); } void DiagWrongNumberOfArguments(DiagnosticEngine& diag, const CallExpr& ce, const FuncDecl& fd) { if (!ce.ShouldDiagnose(true)) { return; } CJC_NULLPTR_CHECK(fd.funcBody); CJC_ASSERT(!fd.funcBody->paramLists.empty()); CJC_NULLPTR_CHECK(fd.funcBody->paramLists.front()); auto& params = fd.funcBody->paramLists.front()->params; std::vector> paramTys; std::transform(params.cbegin(), params.cend(), std::back_inserter(paramTys), [](auto& param) { return param->type == nullptr ? param->ty : param->type->ty; }); DiagWrongNumberOfArgumentsCommon(diag, ce, paramTys, &fd); } void DiagGenericFuncWithoutTypeArg(DiagnosticEngine& diag, const Expr& expr) { if (!expr.ShouldDiagnose()) { return; } auto range = MakeRange(expr.begin, expr.end); if (auto ma = DynamicCast(&expr); ma) { range = MakeRange(ma->field); } auto name = expr.symbol == nullptr ? "" : " '" + expr.symbol->name + "'"; diag.DiagnoseRefactor(DiagKindRefactor::sema_generic_func_without_type_arg, expr, range, name); } void DiagStaticAndNonStaticOverload(DiagnosticEngine& diag, const FuncDecl& fd, const FuncDecl& firstNonStatic) { if (!fd.ShouldDiagnose()) { return; } auto builder = diag.DiagnoseRefactor( DiagKindRefactor::sema_static_function_overload_conflicts, fd, MakeRange(fd.identifier), fd.identifier); builder.AddNote(firstNonStatic, MakeRange(firstNonStatic.identifier), "non-static function is here"); } void DiagImmutableAccessMutableFunc(DiagnosticEngine& diag, const MemberAccess& outerMa, const MemberAccess& ma) { CJC_NULLPTR_CHECK(ma.baseExpr); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_immutable_access_mutable_func, *ma.baseExpr); auto mutFuncPos = outerMa.GetFieldPos(); if (!mutFuncPos.IsZero() && !IsFieldOperator(ma.field)) { builder.AddHint(MakeRange(mutFuncPos, outerMa.field), "is a mutable function"); } if (auto vd = DynamicCast(ma.baseExpr->GetTarget())) { if (!vd->isVar && !vd->identifier.ZeroPos()) { auto letOrConst = vd->IsConst() ? "const" : "let"; builder.AddNote(*vd, MakeRange(vd->identifier), "'" + vd->identifier + "' is a variable declared with '" + letOrConst + "'"); } else if (auto pd = DynamicCast(vd); pd && pd->getters.size() == 1 && pd->getters.front() && !pd->getters.front()->begin.IsZero()) { auto& get = *pd->getters.front(); builder.AddNote(get, MakeRangeForDeclIdentifier(get), "property getter returns immutable value"); } } } // `ae` can be `AssignExpr` or `IncOrDecExpr`. void DiagCannotAssignToImmutable(DiagnosticEngine& diag, const Expr& ae, const Expr& perpetrator) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_cannot_assign_to_immutable, ae); auto target = perpetrator.GetTarget(); if (target != nullptr && !target->identifier.ZeroPos()) { builder.AddNote(*target, MakeRange(target->identifier), DeclKindToString(*target) + " '" + target->identifier + "' is immutable"); } else if (perpetrator.astKind == ASTKind::CALL_EXPR) { builder.AddNote( perpetrator, MakeRange(perpetrator.begin, perpetrator.end), "function call returns immutable value"); } } void DiagCannotOverride(DiagnosticEngine& diag, const Decl& child, const Decl& parent) { std::string kind = DeclKindToString(child); std::string name = child.identifier; auto childRange = child.identifier.ZeroPos() ? MakeRange(child.begin, child.end) : MakeRange(child.identifier); auto parentRange = parent.identifier.ZeroPos() ? MakeRange(parent.begin, parent.end) : MakeRange(parent.identifier.Begin(), name); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_cannot_override, child, childRange, kind, name); builder.AddNote(parent, parentRange, "the parent " + kind + " is not modified by 'open'"); } void DiagCannotHaveDefaultParam(DiagnosticEngine& diag, const FuncDecl& fd, const FuncParam& fp) { if (fp.TestAttr(Attribute::IS_BROKEN)) { return; } std::string funcType; if (fd.op != TokenKind::ILLEGAL) { funcType = "operator overloading"; } else if (fd.TestAttr(Attribute::OPEN)) { funcType = "'open'"; } else { funcType = "abstract"; } (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_cannot_have_default_param, fp, funcType); } void DiagCannotInheritSealed(DiagnosticEngine& diag, const Decl& child, const Type& sealed, const bool& isCommon) { Ptr target = sealed.GetTarget(); CJC_NULLPTR_CHECK(target); bool isImplement = child.astKind == ASTKind::CLASS_DECL && target->astKind == ASTKind::INTERFACE_DECL; auto range = MakeRange(child.identifier); if (child.astKind == ASTKind::EXTEND_DECL) { const ExtendDecl& ed = StaticCast(child); if (ed.extendedType) { Ptr extendedDecl = ed.extendedType->GetTarget(); if (extendedDecl && extendedDecl->astKind != ASTKind::INTERFACE_DECL) { isImplement = true; } range = MakeRange(ed.extendedType->begin, ed.extendedType->end); } } const std::string inheritOrImplement = isImplement ? "implement" : "inherit"; const std::string importedOrCommon = isCommon ? "common-defined" : "imported"; const std::string classOrInterface = target->astKind == ASTKind::CLASS_DECL ? "class" : "interface"; auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_cannot_inherit_sealed, range, inheritOrImplement, importedOrCommon, classOrInterface, target->identifier); const std::string declaredPos = isCommon ? "common package part" : ("package '" + target->fullPackageName + "'"); builder.AddHint(sealed, "sealed " + classOrInterface + " declared in " + declaredPos); } void DiagPackageMemberNotFound( DiagnosticEngine& diag, const ImportManager& importManager, const MemberAccess& ma, const PackageDecl& pd) { auto range = ma.field.ZeroPos() ? MakeRange(ma.begin, ma.end) : MakeRange(ma.field); auto decls = importManager.GetPackageMembersByName(*pd.srcPackage, ma.field); if (decls.empty()) { diag.DiagnoseRefactor( DiagKindRefactor::sema_not_member_of, ma, range, ma.field, "package", pd.identifier); } else { diag.DiagnoseRefactor( DiagKindRefactor::sema_member_not_imported, ma, range, pd.identifier + "." + ma.field); } } void DiagAmbiguousUse( DiagnosticEngine& diag, const Node& node, const std::string& name, std::vector>& targets, const ImportManager& importManager) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_ambiguous_use, node, name); std::sort(targets.begin(), targets.end(), CompNodeByPos); CJC_ASSERT(targets.size() > 1); std::vector> importedTargets; for (auto target : targets) { CJC_NULLPTR_CHECK(target); if (target->TestAttr(Attribute::IMPORTED)) { importedTargets.emplace_back(target); } else { builder.AddNote(*target, MakeRangeForDeclIdentifier(*target), "found candidate"); } } CJC_ASSERT(node.curFile && node.curFile->curPackage); AddDiagNotesForImportedDecls( builder, importManager.GetImportsOfDecl(node.curFile->curPackage->fullPackageName), importedTargets); } namespace { std::string GetNoteMessageForMemberDecl(const Decl& target) { std::string ret = "found "; CJC_NULLPTR_CHECK(target.outerDecl); if (target.outerDecl->astKind == ASTKind::EXTEND_DECL) { ret += "extended "; } ret += DeclKindToString(target) + " member of '" + target.outerDecl->ty->String() + "'"; return ret; } } // namespace void AddDiagNotesForImportedDecls(DiagnosticBuilder& builder, const ImportManager::DeclImportsMap& declToImports, const std::vector>& targets) { std::set, CmpNodeByPos> imports; std::set implicitImports; for (auto target : targets) { CJC_NULLPTR_CHECK(target); if (!target->TestAttr(Attribute::GLOBAL) && target->astKind != ASTKind::PACKAGE_DECL) { // Imported non-global decl must be member decl. CJC_ASSERT(Is(target->outerDecl)); builder.AddNote(*target, MakeRangeForDeclIdentifier(*target), GetNoteMessageForMemberDecl(*target)); } auto found = declToImports.find(target); if (found == declToImports.end()) { continue; } for (auto import : found->second) { if (import->TestAttr(Attribute::IMPLICIT_ADD)) { implicitImports.emplace(target->fullPackageName); } else { imports.emplace(import); } } } for (auto import : imports) { builder.AddNote(*import, MakeRange(import->begin, import->end), "found imported candidate"); } for (auto implicitPkg : implicitImports) { builder.AddNote("found candidate from implicit imported pacakge '" + implicitPkg + "'"); } } void DiagInvalidAssign(DiagnosticEngine& diag, const Node& node, const std::string& name) { auto range = MakeRange(node.begin, node.end); (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_unqualified_left_value_assigned, node, range, name); } void DiagLowerAccessLevelTypesUse(DiagnosticEngine& diag, const Decl& outDecl, const std::vector>& limitedDecls, const std::vector>& hintDecls) { bool noHint = hintDecls.empty(); if (limitedDecls.empty() && noHint) { return; } auto kind = noHint ? DiagKindRefactor::sema_accessibility : DiagKindRefactor::sema_accessibility_with_main_hint; auto range = MakeRangeForDeclIdentifier(outDecl); std::string lowerLevelStr = limitedDecls.size() + hintDecls.size() != 1 ? "lower access level" : GetAccessLevelStr(limitedDecls.empty() ? *hintDecls.front().get() : limitedDecls.front().second, "'"); auto builder = diag.DiagnoseRefactor(kind, outDecl, range, GetAccessLevelStr(outDecl), lowerLevelStr); if (!noHint && Ty::IsTyCorrect(outDecl.ty)) { builder.AddMainHintArguments("inferred type", outDecl.ty->IsFunc() ? Ty::ToString(StaticCast(outDecl.ty)->retTy) : Ty::ToString(outDecl.ty), lowerLevelStr); for (const auto& hintDecl : hintDecls) { CJC_ASSERT(hintDecl); auto inDeclRange = MakeRangeForDeclIdentifier(*hintDecl); builder.AddNote(*hintDecl, inDeclRange, "the " + GetAccessLevelStr(*hintDecl, "'") + " type is '" + Ty::ToString(hintDecl->ty) + "'"); } } for (const auto& [node, decl] : limitedDecls) { auto usedVisibility = GetAccessLevelStr(decl, "'"); if (!node.begin.IsZero() && !node.end.IsZero()) { auto typeRange = MakeRange(node.begin, node.end); builder.AddNote( node, typeRange, "type '" + Ty::ToString(node.ty) + "' contains " + usedVisibility + " type"); } auto inDeclRange = MakeRangeForDeclIdentifier(decl); builder.AddNote( decl, inDeclRange, "the " + usedVisibility + " type is '" + Ty::ToString(decl.ty) + "'"); } } void DiagPatternInternalTypesUse(DiagnosticEngine& diag, const std::vector>& inDecls) { for (const auto& inDeclPair : inDecls) { auto& node = inDeclPair.first; auto& used = inDeclPair.second; if (!node.begin.IsZero() && !node.end.IsZero()) { auto usedVisibility = GetAccessLevelStr(used, "'"); auto range = MakeRange(node.begin, node.end); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_accessibility_with_main_hint, node, range, GetAccessLevelStr(node), usedVisibility); builder.AddMainHintArguments("inferred type", Ty::ToString(node.ty), usedVisibility); auto inDeclRange = MakeRangeForDeclIdentifier(used); builder.AddNote(used, inDeclRange, "the " + usedVisibility + " type is '" + Ty::ToString(used.ty) + "'"); } } } void DiagAmbiguousUpperBoundTargets(DiagnosticEngine& diag, const MemberAccess& ma, const OrderedDeclSet& targets) { auto diagBuilder = diag.DiagnoseRefactor(DiagKindRefactor::sema_ambiguous_use, ma, ma.field); for (auto it : targets) { CJC_NULLPTR_CHECK(it); CJC_ASSERT(Is(it->outerDecl)); std::string message = GetNoteMessageForMemberDecl(*it); diagBuilder.AddNote(*it, MakeRangeForDeclIdentifier(*it), message); } } void DiagUseClosureCaptureVarAlone(DiagnosticEngine& diag, const Expr& expr) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_use_func_capture_var_alone, expr, expr.astKind == ASTKind::LAMBDA_EXPR ? "lambda" : "function"); Ptr fb = nullptr; if (auto le = DynamicCast(&expr)) { fb = le->funcBody.get(); } else if (auto re = DynamicCast(&expr)) { fb = StaticCast(re->GetTarget())->funcBody.get(); } CJC_NULLPTR_CHECK(fb); std::set, CmpNodeByPos> capturedVars( fb->capturedVars.cbegin(), fb->capturedVars.cend()); for (auto varRe : capturedVars) { CJC_ASSERT(varRe && varRe->GetTarget()); auto& vd = *varRe->GetTarget(); builder.AddNote(*varRe, MakeRange(varRe->begin, varRe->end), "'" + vd.identifier + "' is mutable"); } } void DiagNeedNamedArgument( DiagnosticEngine& diag, const CallExpr& ce, const FuncDecl& fd, size_t paramPos, size_t argPos) { CJC_ASSERT(argPos < ce.args.size()); if (argPos >= ce.args.size()) { return; } CJC_ASSERT(fd.funcBody && !fd.funcBody->paramLists.empty()); std::set restParams = GetRestNamedParams(fd.funcBody->paramLists[0]->params, paramPos); std::set restArgNames = GetRestArgNames(fd, ce.args, argPos + 1); std::string expectedPrefix; bool first = true; for (auto n : restParams) { if (restArgNames.count(fd.funcBody->paramLists[0]->params[n]->identifier) == 1) { continue; } if (fd.funcBody->paramLists[0]->params[n]->identifier.Empty()) { continue; } expectedPrefix += (first ? "'" : " or '") + fd.funcBody->paramLists[0]->params[n]->identifier.GetRawText() + ":'"; first = false; } diag.DiagnoseRefactor(DiagKindRefactor::sema_need_named_argument, *ce.args[argPos], expectedPrefix); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void DiagForStaticVariableDependsGeneric(DiagnosticEngine& diag, const Node& node, const std::set>& targetTys) { if (targetTys.empty()) { return; } for (auto id : targetTys) { if (!id) { continue; } auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_static_variable_use_generic_parameter, node, id->String()); if (auto gt = DynamicCast(id); gt && gt->decl && !gt->decl->begin.IsZero()) { builder.AddNote(*gt->decl, "generic argument declaration here"); } } }; #endif void RecommendImportForMemberAccess(TypeManager& typeManager, const ImportManager& importManager, const MemberAccess& ma, const Ptr builder) { if (ma.baseExpr == nullptr || !Ty::IsTyCorrect(ma.baseExpr->ty)) { return; } CJC_ASSERT(ma.curFile && ma.curFile->curPackage); std::vector, Ptr>> recommendations; auto extends = typeManager.GetAllExtendsByTy(*ma.baseExpr->ty); for (auto& ed : extends) { for (auto& decl : ed->members) { if (decl->identifier == ma.field && ma.curFile) { importManager.IsExtendMemberAccessible(*ma.curFile, *decl, *ma.baseExpr->ty, builder); } } } } } // namespace Cangjie::Sema cangjie_compiler-1.0.7/src/Sema/Diags.h000066400000000000000000000124321510705540100176650ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares diagnostic related functions for Sema. */ #ifndef CANGJIE_SEMA_DIAGS_H #define CANGJIE_SEMA_DIAGS_H #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Modules/ImportManager.h" #include namespace Cangjie::Sema { using namespace AST; Range MakeRangeForDeclIdentifier(const AST::Decl& decl); void DiagRedefinitionWithFoundNode(DiagnosticEngine& diag, const Decl& current, const Decl& previous); void DiagOverloadConflict(DiagnosticEngine& diag, const std::vector>& sameSigFuncs); void DiagMismatchedTypesWithFoundTy(DiagnosticEngine& diag, const Node& node, const std::string& expected, const std::string& found, const std::string& note = ""); void DiagMismatchedTypesWithFoundTy( DiagnosticEngine& diag, const Node& node, const Ty& expected, const Ty& found, const std::string& note = ""); void DiagMismatchedTypes(DiagnosticEngine& diag, const Node& node, const Ty& type, const std::string& note = ""); void DiagMismatchedTypes(DiagnosticEngine& diag, const Node& node, const Node& type, const std::string& because = ""); void DiagInvalidMultipleAssignExpr( DiagnosticEngine& diag, const Node& leftNode, const Expr& rightExpr, const std::string& because = ""); void DiagInvalidBinaryExpr(DiagnosticEngine& diag, const BinaryExpr& be); void DiagInvalidUnaryExpr(DiagnosticEngine& diag, const UnaryExpr& ue); void DiagInvalidUnaryExprWithTarget(DiagnosticEngine& diag, const UnaryExpr& ue, Ty& target); void DiagInvalidSubscriptExpr( DiagnosticEngine& diag, const SubscriptExpr& se, const Ty& baseTy, const std::vector>& indexTys); void DiagUnableToInferExpr(DiagnosticEngine& diag, const Expr& expr); void DiagUnableToInferReturnType(DiagnosticEngine& diag, const FuncDecl& fd); void DiagUnableToInferReturnType(DiagnosticEngine& diag, const FuncBody& fb); void DiagUnableToInferReturnType(DiagnosticEngine& diag, const FuncDecl& fd, const Expr& expr); void DiagWrongNumberOfArguments(DiagnosticEngine& diag, const CallExpr& ce, const std::vector>& paramTys); void DiagWrongNumberOfArguments(DiagnosticEngine& diag, const CallExpr& ce, const FuncDecl& fd); void DiagGenericFuncWithoutTypeArg(DiagnosticEngine& diag, const Expr& expr); void DiagStaticAndNonStaticOverload(DiagnosticEngine& diag, const FuncDecl& fd, const FuncDecl& firstNonStatic); void DiagImmutableAccessMutableFunc(DiagnosticEngine& diag, const MemberAccess& outerMa, const MemberAccess& ma); void DiagCannotAssignToImmutable(DiagnosticEngine& diag, const Expr& ae, const Expr& perpetrator); void DiagCannotOverride(DiagnosticEngine& diag, const Decl& child, const Decl& parent); void DiagCannotHaveDefaultParam(DiagnosticEngine& diag, const FuncDecl& fd, const FuncParam& fp); void DiagCannotInheritSealed( DiagnosticEngine& diag, const Decl& child, const Type& sealed, const bool& isCommon = false); void DiagInvalidAssign(DiagnosticEngine& diag, const Node& node, const std::string& name); void DiagLowerAccessLevelTypesUse(DiagnosticEngine& diag, const Decl& outDecl, const std::vector>& limitedDecls, const std::vector>& hintDecls = {}); void DiagPatternInternalTypesUse(DiagnosticEngine& diag, const std::vector>& inDecls); void DiagAmbiguousUse( DiagnosticEngine& diag, const Node& node, const std::string& name, std::vector>& targets, const ImportManager& importManager); void AddDiagNotesForImportedDecls(DiagnosticBuilder& builder, const ImportManager::DeclImportsMap& declToImports, const std::vector>& targets); void DiagAmbiguousUpperBoundTargets(DiagnosticEngine& diag, const MemberAccess& ma, const OrderedDeclSet& targets); void DiagPackageMemberNotFound( DiagnosticEngine& diag, const ImportManager& importManager, const MemberAccess& ma, const PackageDecl& pd); void DiagUseClosureCaptureVarAlone(DiagnosticEngine& diag, const Expr& expr); void DiagNeedNamedArgument( DiagnosticEngine& diag, const CallExpr& ce, const FuncDecl& fd, size_t paramPos, size_t argPos); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void DiagForStaticVariableDependsGeneric(DiagnosticEngine& diag, const Node& node, const std::set>& targetTys); #endif /** * @brief Returns a vector of recommendations. * @details Users have to import the interfaces in order to use the methods defined in the extensions. * The first of the pair is the function/property declaration in the extension. * The second of the pair is the interface declaration. * @param typeManager Reference to the TypeManager, used for type checking and management. * @param importManager Reference to the ImportManager, used for managing imported modules and symbols. * @param ma Object of MemberAccess, representing the member access needed in the code. * @param builder Smart pointer to the DiagnosticBuilder, used for building and reporting diagnostic information. */ void RecommendImportForMemberAccess(TypeManager& typeManager, const ImportManager& importManager, const MemberAccess& ma, const Ptr builder); } // namespace Cangjie::Sema #endif cangjie_compiler-1.0.7/src/Sema/EliminateRecursiveTypes.cpp000066400000000000000000000267021510705540100240220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the elimination of recursive types. */ #include "TypeCheckerImpl.h" using namespace Cangjie; using namespace AST; namespace { struct CmpNodeByPackagePos { bool operator()(Ptr n1, Ptr n2) const { if (n1 && n1->curFile && n1->curFile->curPackage && n2 && n2->curFile && n2->curFile->curPackage) { if (int cp1 = n1->curFile->curPackage->fullPackageName.compare(n2->curFile->curPackage->fullPackageName); cp1 != 0) { return cp1 < 0; } } return CompNodeByPos(n1, n2); } }; // A dependency graph among value types. // There are two kinds of vertices in the graph: `StructDecl` and `EnumDecl`. // There is an arc from `u` to `v`, if `v` is a member variable of a `struct` `u`, // or `v` is a parameter of a constructor of an `enum` `u`, class Graph { public: explicit Graph(const std::vector>& pkgs) { for (auto pkg : pkgs) { CJC_NULLPTR_CHECK(pkg); AddArcs(*pkg->srcPackage); } } const std::set, CmpNodeByPackagePos>& InEdges(const EnumDecl& ed) const { return enumInEdges.at(&ed); } std::vector StronglyConnectedComponents() const { TarjanContext ctx; for (auto u : vertices) { CJC_NULLPTR_CHECK(u); if (!Utils::InKeys(u, ctx.indices)) { TarjanSCC(ctx, *u); } } return std::move(ctx.sccs); } // Peek the `enum` with the maximum in-degree to box. // Will peek the foremost one if there are more than one maximums. // Returns `nullptr` if there are no `enum`s in the graph or the SCC is trivial. Ptr PeekEnumToBox() const { Ptr candidate = nullptr; size_t maxInDegree = 0; for (auto& [ed, inEdges] : enumInEdges) { size_t inDegree = inEdges.size(); if (inDegree > maxInDegree) { candidate = ed; maxInDegree = inDegree; } } return candidate; } void RemoveVertex(const EnumDecl& ed) { vertices.erase(&ed); for (auto u : enumInEdges.at(&ed)) { outEdges.at(u).erase(&ed); } outEdges.erase(&ed); enumInEdges.erase(&ed); } private: Graph() { } void AddVertex(const Decl& v) { if (!Ty::IsTyCorrect(v.ty) || v.ty->HasGeneric()) { return; } vertices.emplace(&v); if (!Utils::InKeys(Ptr(&v), outEdges)) { outEdges[&v] = {}; } if (v.astKind == ASTKind::ENUM_DECL) { auto& ed = StaticCast(v); if (!Utils::InKeys(Ptr(&ed), enumInEdges)) { enumInEdges[&ed] = {}; } } } void AddArc(const Decl& u, const Decl& v) { if (!Ty::IsTyCorrect(u.ty) || u.ty->HasGeneric() || !Ty::IsTyCorrect(v.ty) || v.ty->HasGeneric()) { return; } AddVertex(u); AddVertex(v); outEdges[&u].emplace(&v); if (v.astKind == ASTKind::ENUM_DECL) { auto& ed = StaticCast(v); enumInEdges[&ed].emplace(&u); } } void AddArcs(const Decl& decl, const Ty& fieldTy) { if (!Ty::IsTyCorrect(&fieldTy)) { return; } else if ((fieldTy.IsStruct() || fieldTy.IsEnum()) && Ty::GetDeclOfTy(&fieldTy)) { AddArc(decl, *Ty::GetDeclOfTy(&fieldTy)); } else if (fieldTy.IsTuple()) { for (auto typeArg : fieldTy.typeArgs) { if (Ty::IsTyCorrect(typeArg)) { AddArcs(decl, *typeArg); } } } } void AddArcs(const StructDecl& sd) { CJC_NULLPTR_CHECK(sd.body); for (auto& d : sd.body->decls) { CJC_NULLPTR_CHECK(d); if (d->astKind != ASTKind::VAR_DECL || d->TestAttr(Attribute::STATIC) || !Ty::IsTyCorrect(d->ty)) { continue; } AddArcs(sd, *d->ty); } } void AddArcs(const EnumDecl& ed) { for (auto& ctor : ed.constructors) { CJC_NULLPTR_CHECK(ctor); if (auto fd = DynamicCast(ctor.get())) { CJC_ASSERT(fd->funcBody && fd->funcBody->paramLists.size() == 1 && fd->funcBody->paramLists.front()); for (auto& param : fd->funcBody->paramLists.front()->params) { CJC_NULLPTR_CHECK(param); if (Ty::IsTyCorrect(param->ty)) { AddArcs(ed, *param->ty); } } } } } void AddArcs(const Decl& decl) { if (decl.astKind == ASTKind::STRUCT_DECL) { AddArcs(StaticCast(decl)); } else if (decl.astKind == ASTKind::ENUM_DECL) { AddArcs(StaticCast(decl)); } } void AddArcs(const Package& pkg) { IterateToplevelDecls(pkg, [this](auto& decl) { CJC_NULLPTR_CHECK(decl); AddArcs(*decl); }); for (auto& decl : pkg.genericInstantiatedDecls) { CJC_NULLPTR_CHECK(decl); AddArcs(*decl); } } // Get the vertex-induced sub-graph. Graph SubGraph(const std::unordered_set>& subVertices) const { Graph subGraph; for (auto& [u, uOutEdges] : outEdges) { CJC_NULLPTR_CHECK(u); if (!Utils::In(u, subVertices)) { continue; } subGraph.AddVertex(*u); for (auto v : uOutEdges) { if (Utils::In(v, subVertices)) { CJC_ASSERT(u && v); subGraph.AddArc(*u, *v); } } } return subGraph; } struct TarjanContext { size_t index = 0; std::vector> stack; std::unordered_map, size_t> indices; /**< The discovered order of vertices in a DFS. */ std::unordered_map, size_t> lowlinks; /**< The smallest index reachable from the vertex. */ std::unordered_map, bool> onStack; /**< Indicate whether the vertex is on stack. */ std::vector sccs; /**< Strongly connected components. */ }; void TarjanSCC(TarjanContext& ctx, const Decl& u) const { ctx.indices[&u] = ctx.index; ctx.lowlinks[&u] = ctx.index; ++ctx.index; ctx.stack.emplace_back(&u); ctx.onStack[&u] = true; CJC_ASSERT(Utils::InKeys(Ptr(&u), outEdges)); for (auto v : outEdges.at(&u)) { CJC_NULLPTR_CHECK(v); if (!Utils::InKeys(v, ctx.indices)) { TarjanSCC(ctx, *v); ctx.lowlinks[&u] = std::min(ctx.lowlinks[&u], ctx.lowlinks[v]); } else if (ctx.onStack[v]) { ctx.lowlinks[&u] = std::min(ctx.lowlinks[&u], ctx.indices[v]); } } if (ctx.lowlinks[&u] == ctx.indices[&u]) { std::unordered_set> subVertices; Ptr w = nullptr; do { w = ctx.stack.back(); ctx.stack.pop_back(); ctx.onStack[w] = false; subVertices.emplace(w); } while (w != &u); ctx.sccs.emplace_back(SubGraph(subVertices)); } } std::set, CmpNodeByPackagePos> vertices; std::map, std::set, CmpNodeByPackagePos>, CmpNodeByPackagePos> outEdges; // We don't care about the in-edges of `struct`s std::map, std::set, CmpNodeByPackagePos>, CmpNodeByPackagePos> enumInEdges; }; void CheckAndUpdateDeclTyWithNewTy(Decl& decl, const EnumTy& specifiedTy, TypeManager& typeManager) { if (decl.ty->IsTuple()) { auto tupleTy = RawStaticCast(decl.ty); bool hasSpecifiedTy = std::find_if(tupleTy->typeArgs.begin(), tupleTy->typeArgs.end(), [&specifiedTy](auto& typeArg) { return typeArg == &specifiedTy; }) != tupleTy->typeArgs.end(); if (!hasSpecifiedTy) { return; } std::vector> vec; for (auto elemTy : tupleTy->typeArgs) { if (elemTy == &specifiedTy) { auto enumTy = RawStaticCast(elemTy); auto newTy = typeManager.GetRefEnumTy(*enumTy->declPtr, enumTy->typeArgs); enumTy->hasCorrespondRefEnumTy = true; newTy->decl = enumTy->decl; vec.emplace_back(newTy); } else { vec.emplace_back(elemTy); } } decl.ty = typeManager.GetTupleTy(vec); } else if (decl.ty == &specifiedTy) { auto enumTy = RawStaticCast(decl.ty); auto newTy = typeManager.GetRefEnumTy(*enumTy->declPtr, enumTy->typeArgs); enumTy->hasCorrespondRefEnumTy = true; newTy->decl = enumTy->decl; decl.ty = newTy; } } } // namespace void TypeChecker::TypeCheckerImpl::UpdateMemberVariableTy(const Decl& decl, const EnumTy& eTy) { switch (decl.astKind) { case ASTKind::STRUCT_DECL: { auto rd = StaticCast(&decl); for (auto& d : rd->body->decls) { if (!d || d->astKind != ASTKind::VAR_DECL || d->TestAttr(Attribute::STATIC)) { continue; } CheckAndUpdateDeclTyWithNewTy(*d, eTy, typeManager); } break; } case ASTKind::ENUM_DECL: { auto ed = StaticCast(&decl); for (auto& ctor : ed->constructors) { if (ctor->astKind != ASTKind::FUNC_DECL) { continue; } auto fd = StaticCast(ctor.get()); for (auto& d : fd->funcBody->paramLists[0]->params) { CheckAndUpdateDeclTyWithNewTy(*d, eTy, typeManager); } } break; } default: CJC_ASSERT(false && "wrong branch"); break; } } void TypeChecker::TypeCheckerImpl::PerformRecursiveTypesElimination() { // Since `RefEnumTy` and `EnumTy` cannot be distinguished in cjo, we have to check all the imported packages. // NOTE: This api will also contains current source package. Graph graph(importManager.GetAllImportedPackages()); std::vector sccs = graph.StronglyConnectedComponents(); while (!sccs.empty()) { Graph scc = std::move(sccs.back()); sccs.pop_back(); auto ed = scc.PeekEnumToBox(); if (ed == nullptr) { continue; } auto& inEdges = scc.InEdges(*ed); for (auto u : inEdges) { CJC_NULLPTR_CHECK(u); UpdateMemberVariableTy(*u, *StaticCast(ed->ty)); } scc.RemoveVertex(*ed); auto subSCCs = scc.StronglyConnectedComponents(); std::copy(std::move_iterator{subSCCs.cbegin()}, std::move_iterator{subSCCs.cend()}, std::back_inserter(sccs)); } } cangjie_compiler-1.0.7/src/Sema/EnumSugarChecker.cpp000066400000000000000000000100171510705540100223610ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the EnumSugarChecker class. */ #include "EnumSugarChecker.h" #include "Diags.h" #include "TypeCheckUtil.h" using namespace Cangjie; using namespace TypeCheckUtil; using namespace AST; using namespace Sema; bool TypeChecker::EnumSugarChecker::CheckVarDeclTargets() { // Type for FuncDecls are inferred and decided later. // Enum member without parameter is not supported to do type inference. std::vector> varDeclTargets; for (auto& target : enumSugarTargets) { if (target->astKind == ASTKind::VAR_DECL && !target->TestAttr(Attribute::COMMON)) { varDeclTargets.push_back(target); } } if (varDeclTargets.size() > 1) { DiagnosticBuilder diagBuilder = ctx.diag.Diagnose( refExpr, DiagKind::sema_multiple_constructor_in_enum, varDeclTargets[0]->identifier.Val()); std::sort(varDeclTargets.begin(), varDeclTargets.end(), [](Ptr a, Ptr b) { return a->begin < b->begin; }); for (auto& it : varDeclTargets) { diagBuilder.AddNote(*it, DiagKind::sema_found_candidate_decl); } refExpr.ty = TypeManager::GetInvalidTy(); return false; } return true; } void TypeChecker::EnumSugarChecker::CheckGenericEnumSugarWithTypeArgs(Ptr ed) { if (!ed) { enumSugarTargets.clear(); return; } UpdateInstTysWithTypeArgs(refExpr); // Check generic constraint. if (!typeChecker.CheckGenericDeclInstantiation(ed, refExpr.GetTypeArgs(), refExpr)) { enumSugarTargets.clear(); return; } // Build generic type mapping. TypeSubst typeMapping; // ed->generic is guaranteed to be not null because of CheckGenericDeclInstantiation invoked before. if (ed->generic->typeParameters.size() != refExpr.typeArguments.size()) { return; } for (size_t i = 0; i < refExpr.typeArguments.size(); ++i) { typeMapping[StaticCast(ed->generic->typeParameters[i]->ty)] = refExpr.typeArguments[i]->ty; } refExpr.ty = typeChecker.typeManager.GetInstantiatedTy(refExpr.ty, typeMapping); } void TypeChecker::EnumSugarChecker::CheckGenericEnumSugarWithoutTypeArgs(Ptr ed) { auto argSize = refExpr.OuterArgSize(); auto foundTargetType = ctx.targetTypeMap.find(&refExpr); bool referenceNeedTypeInfer = (foundTargetType == ctx.targetTypeMap.end() || foundTargetType->second == nullptr) && !refExpr.isInFlowExpr; if (ed && ed->generic && Is(enumSugarTargets[0]) && referenceNeedTypeInfer && argSize == 0) { ctx.diag.Diagnose(refExpr, DiagKind::sema_generic_type_without_type_argument); enumSugarTargets.clear(); } } Ptr TypeChecker::EnumSugarChecker::CheckEnumSugarTargets() { auto it = std::find_if(enumSugarTargets.cbegin(), enumSugarTargets.cend(), [](Ptr decl) { return decl->astKind == ASTKind::VAR_DECL; }); Ptr target = it != enumSugarTargets.cend() ? *it : enumSugarTargets.front(); refExpr.ty = target->ty; // Handle generic enum field sugar like: .century. auto ed = DynamicCast(target->outerDecl); if (refExpr.typeArguments.empty()) { CheckGenericEnumSugarWithoutTypeArgs(ed); } else { CheckGenericEnumSugarWithTypeArgs(ed); } return target; } std::pair>> TypeChecker::EnumSugarChecker::Resolve() { enumSugarTargets = enumSugarTargetsFinder.FindEnumSugarTargets(); if (enumSugarTargets.empty()) { return {false, {}}; } else if (!CheckVarDeclTargets()) { return {true, {}}; } Ptr target = CheckEnumSugarTargets(); ModifyTargetOfRef(refExpr, target, enumSugarTargets); return {true, enumSugarTargets}; } cangjie_compiler-1.0.7/src/Sema/EnumSugarChecker.h000066400000000000000000000030551510705540100220320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the EnumSugarChecker class. */ #ifndef CANGJIE_SEMA_ENUMSUGARCHECKER_H #define CANGJIE_SEMA_ENUMSUGARCHECKER_H #include #include "EnumSugarTargetsFinder.h" #include "TypeCheckerImpl.h" #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Node.h" namespace Cangjie { class TypeChecker::EnumSugarChecker { public: EnumSugarChecker(TypeCheckerImpl& typeChecker, ASTContext& ctx, AST::RefExpr& re) : typeChecker(typeChecker), ctx(ctx), refExpr(re), enumSugarTargetsFinder(typeChecker.typeManager, ctx, re) { } /** * According to found targets, try to resolve enum sugar related targets. * @return the first of pair is true when error detected, and the second of pair is resolved targets. */ std::pair>> Resolve(); private: void CheckGenericEnumSugarWithTypeArgs(Ptr ed); void CheckGenericEnumSugarWithoutTypeArgs(Ptr ed); Ptr CheckEnumSugarTargets(); bool CheckVarDeclTargets(); std::vector> enumSugarTargets; TypeCheckerImpl& typeChecker; ASTContext& ctx; AST::RefExpr& refExpr; EnumSugarTargetsFinder enumSugarTargetsFinder; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Sema/EnumSugarTargetsFinder.cpp000066400000000000000000000126411510705540100235630ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the EnumSugarTargetFiner class. */ #include "EnumSugarTargetsFinder.h" #include #include "TypeCheckUtil.h" #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Utils.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; std::vector> EnumSugarTargetsFinder::FindEnumSugarTargets() { if (refExpr.TestAttr(Attribute::MACRO_INVOKE_BODY)) { return {}; } // 'Lookup' is only able to found target from exactly one enum decl. if (IsAllFuncDecl(enumSugarTargets)) { // No shadow happened if targets are all funcDecl, need to re-find all target globally. enumSugarTargets.clear(); } else { RefineTargets(); } if (enumSugarTargets.empty()) { size_t argSize = refExpr.OuterArgSize(); auto decls = ctx.FindEnumConstructor(refExpr.ref.identifier, argSize); if (argSize != 0) { // for `()` operator overloading auto& varDecls = ctx.FindEnumConstructor(refExpr.ref.identifier, 0); decls.insert(decls.end(), varDecls.cbegin(), varDecls.cend()); } Utils::EraseIf(decls, [this](auto decl) { CJC_NULLPTR_CHECK(decl); auto& ed = *StaticCast(decl->outerDecl); // Filter out the `enum`s that mismatch the number of type arguments. return !refExpr.typeArguments.empty() && (ed.generic == nullptr || (refExpr.typeArguments.size() != ed.generic->typeParameters.size())); }); if (refExpr.TestAttr(Attribute::IN_CORE)) { std::copy_if(decls.cbegin(), decls.cend(), std::back_inserter(enumSugarTargets), [](auto decl) { return decl->fullPackageName == CORE_PACKAGE_NAME; }); } else { // Only keep toplevel `enum`s if `refExpr` doesn't have target ty. std::copy_if(decls.cbegin(), decls.cend(), std::back_inserter(enumSugarTargets), [this](auto decl) { return ctx.HasTargetTy(&refExpr) || decl->IsSamePackage(refExpr); }); // If toplevel is empty, keep the imported `enum`s. if (enumSugarTargets.empty()) { enumSugarTargets = decls; } } RefineTargets(); } auto it = std::unique(enumSugarTargets.begin(), enumSugarTargets.end()); enumSugarTargets.resize(static_cast(std::distance(enumSugarTargets.begin(), it))); std::sort(enumSugarTargets.begin(), enumSugarTargets.end(), CmpNodeByPos()); return enumSugarTargets; } void EnumSugarTargetsFinder::RefineTargets() { if (!ctx.HasTargetTy(&refExpr) || refExpr.callOrPattern != nullptr) { return; } std::vector> inCandidates = enumSugarTargets; for (auto it = enumSugarTargets.begin(); it != enumSugarTargets.end();) { auto targetTy = ctx.targetTypeMap[&refExpr]; if (auto refinedTargetTy = RefineTargetTy(tyMgr, targetTy, *it)) { ctx.targetTypeMap[&refExpr] = *refinedTargetTy; ++it; } else { it = enumSugarTargets.erase(it); } } // If there is no target left after refining, restore targets in current package, // OR if targets are all imported, retore all of them. if (enumSugarTargets.empty()) { bool hasTargetInCurrentPkg = Utils::In(inCandidates, [](auto it) { return it && !it->TestAttr(Attribute::IMPORTED); }); if (hasTargetInCurrentPkg) { std::copy_if(inCandidates.begin(), inCandidates.end(), std::back_inserter(enumSugarTargets), [](auto it) { return it && !it->TestAttr(Attribute::IMPORTED); }); } else { enumSugarTargets = inCandidates; } } } // Get real enum type of the given target. Only return value with valid type. std::optional> EnumSugarTargetsFinder::RefineTargetTy( TypeManager& typeManager, Ptr targetTy, Ptr target) { if (!target || !targetTy) { return {}; } Ptr currentTy = targetTy; while (currentTy != nullptr && currentTy->kind == TypeKind::TYPE_ENUM) { auto targetEnumTy = RawStaticCast(currentTy); if (targetEnumTy->declPtr == target->outerDecl) { return currentTy; } // Option type allow type auto box. if (targetEnumTy->IsCoreOptionType()) { currentTy = targetEnumTy->typeArgs[0]; } else { return {}; } } CJC_ASSERT(target->outerDecl); // When target type is enum implemented interface type, directly return current enum type. if (auto currentInterfaceTy = DynamicCast(currentTy); currentInterfaceTy && target->outerDecl->ty) { auto allInterfaceTys = typeManager.GetAllSuperTys(*target->outerDecl->ty); if (allInterfaceTys.count(currentInterfaceTy) > 0) { return target->outerDecl->ty; } for (auto ty : allInterfaceTys) { if (auto iTy = DynamicCast(ty); iTy && iTy->declPtr == currentInterfaceTy->declPtr) { return target->outerDecl->ty; } } } return {}; } cangjie_compiler-1.0.7/src/Sema/EnumSugarTargetsFinder.h000066400000000000000000000022441510705540100232260ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the EnumSugarTargetFiner class. */ #ifndef CANGJIE_SEMA_ENUMSUGARTARGETSFINDER_H #define CANGJIE_SEMA_ENUMSUGARTARGETSFINDER_H #include #include "TypeCheckerImpl.h" #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Node.h" #include "cangjie/Modules/ImportManager.h" namespace Cangjie { class EnumSugarTargetsFinder { public: EnumSugarTargetsFinder(TypeManager& typeManager, ASTContext& ctx, AST::RefExpr& re) : tyMgr(typeManager), ctx(ctx), refExpr(re) { } std::vector> FindEnumSugarTargets(); static std::optional> RefineTargetTy( TypeManager& typeManager, Ptr targetTy, Ptr target); private: void RefineTargets(); TypeManager& tyMgr; ASTContext& ctx; AST::RefExpr& refExpr; std::vector> enumSugarTargets; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Sema/ExtendBoxMarker.cpp000066400000000000000000000351521510705540100222370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * file implements functions to mark extend box attribute for ast. */ #include "ExtendBoxMarker.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Walker.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace AST; TypeManager* ExtendBoxMarker::typeManager = nullptr; std::mutex ExtendBoxMarker::mtx; std::function)> ExtendBoxMarker::GetMarkExtendBoxFunc(TypeManager& typeMgr) { typeManager = &typeMgr; std::function)> markerFunc = [](auto node) -> VisitAction { CJC_ASSERT(node); switch (node->astKind) { case ASTKind::VAR_DECL: return MarkBoxPointHandleVarDecl(*RawStaticCast(node)); case ASTKind::ASSIGN_EXPR: return MarkBoxPointHandleAssignExpr(*RawStaticCast(node)); case ASTKind::CALL_EXPR: return MarkBoxPointHandleCallExpr(*RawStaticCast(node)); case ASTKind::IF_EXPR: return MarkBoxPointHandleIfExpr(*RawStaticCast(node)); case ASTKind::RETURN_EXPR: return MarkBoxPointHandleReturnExpr(*RawStaticCast(node)); case ASTKind::MATCH_EXPR: return MarkBoxPointHandleMatchExpr(*RawStaticCast(node)); case ASTKind::TRY_EXPR: return MarkBoxPointHandleTryExpr(*RawStaticCast(node)); case ASTKind::ARRAY_EXPR: return MarkBoxPointHandleArrayExpr(*RawStaticCast(node)); case ASTKind::ARRAY_LIT: return MarkBoxPointHandleArrayLit(*RawStaticCast(node)); case ASTKind::TUPLE_LIT: return MarkBoxPointHandleTupleLit(*RawStaticCast(node)); case ASTKind::WHILE_EXPR: return MarkBoxPointHandleWhileExpr(StaticCast(*node)); default: return VisitAction::WALK_CHILDREN; } }; return markerFunc; } bool ExtendBoxMarker::NeedAutoBox(Ptr child, Ptr interface, bool isUpcast) { CJC_ASSERT(typeManager); CJC_NULLPTR_CHECK(child); CJC_NULLPTR_CHECK(interface); auto target = interface; // If the 'target' has more option box than child, then get it's typeArg for box checking. // NOTE: The 'while' should only be entered during instantiation checking. // After instantiation, the option box is happened before extend box. // So this 'while' should not be entered during extend box 'AutoBoxing' step. while (TypeCheckUtil::CountOptionNestedLevel(*child) < TypeCheckUtil::CountOptionNestedLevel(*target)) { target = target->typeArgs[0]; } bool isExtended = target && typeManager->HasExtensionRelation(*child, *target); if (isExtended && isUpcast) { typeManager->RecordUsedExtend(*child, *target); } return isExtended; } void ExtendBoxMarker::CheckBlockNeedBox(const Block& block, Ty& ty, Node& nodeToCheck) { auto lastExpr = block.GetLastExprOrDecl(); Ptr lastTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); if (auto expr = DynamicCast(lastExpr); expr) { lastTy = expr->ty; } if (NeedAutoBox(lastTy, &ty)) { nodeToCheck.EnableAttr(Attribute::NEED_AUTO_BOX); } } VisitAction ExtendBoxMarker::MarkBoxPointHandleArrayExpr(ArrayExpr& ae) { CJC_ASSERT(typeManager); bool ignored = !Ty::IsTyCorrect(ae.ty) || ae.args.empty() || (ae.initFunc && ae.initFunc->identifier != "arrayInitByCollection"); if (ignored) { return VisitAction::WALK_CHILDREN; } // For 'VArray<...>(repeat: T)' constructor may need auto box. if (ae.isValueArray) { if (NeedAutoBox(ae.args[0]->ty, typeManager->GetTypeArgs(*ae.ty)[0])) { ae.EnableAttr(Attribute::NEED_AUTO_BOX); } return VisitAction::WALK_CHILDREN; } // For 'RawArray(size, item: T)' constructor may need auto box. if (!ae.initFunc) { if (NeedAutoBox(ae.args[1]->ty, typeManager->GetTypeArgs(*ae.ty)[0])) { ae.EnableAttr(Attribute::NEED_AUTO_BOX); } return VisitAction::WALK_CHILDREN; } // For 'RawArray(Collection)' constructor may need auto box. // 'initFunc' may be generic, we need to instantiated it's type before boxing check. auto initFuncTy = DynamicCast(ae.initFunc->ty); auto generic = ae.initFunc->GetGeneric(); auto instTys = typeManager->GetTypeArgs(*ae.ty); bool invalid = !initFuncTy || initFuncTy->paramTys.size() != 2 || // 'arrayInitByCollection' has 2 parameters. !generic || generic->typeParameters.size() != instTys.size(); if (invalid) { return VisitAction::WALK_CHILDREN; } TypeSubst typeMapping = TypeCheckUtil::GenerateTypeMapping(*ae.initFunc, instTys); initFuncTy = RawStaticCast(typeManager->GetInstantiatedTy(initFuncTy, typeMapping)); if (NeedAutoBox(ae.args[0]->ty, initFuncTy->paramTys[1])) { ae.EnableAttr(Attribute::NEED_AUTO_BOX); } return VisitAction::WALK_CHILDREN; } /* In TryExpr: * If the context does explicitly require a certain type, the tryBlock and * each of the catchBlocks (if present) are required to be a subtype of the required type. * If the context does not explicitly require a certain type, the tryBlock and * all catchBlocks(if present) are required to have their least common super type, * which is also the type of the entire tryExpr. * When the entire tryExpr is an Interface type, and one of the tryBlock or catchBlocks is * an type which extend the Interface, or non-class type which implements the Interface, * we need to box it. * */ VisitAction ExtendBoxMarker::MarkBoxPointHandleTryExpr(TryExpr& te) { if (!Ty::IsTyCorrect(te.ty)) { return VisitAction::WALK_CHILDREN; } if (te.tryBlock) { CheckBlockNeedBox(*te.tryBlock, *te.ty, te); } for (auto& cb : te.catchBlocks) { CJC_NULLPTR_CHECK(cb); CheckBlockNeedBox(*cb, *te.ty, te); } return VisitAction::WALK_CHILDREN; } bool ExtendBoxMarker::IsTypePatternNeedBox(Ptr pattern, Ty& selectorTy) { if (selectorTy.IsNothing() || pattern == nullptr) { return false; } // NOTE: we need to collect all boxed types, so DO NOT interrupt loop early. bool boxOrUnbox = false; switch (pattern->astKind) { case ASTKind::TYPE_PATTERN: { auto typePattern = RawStaticCast(pattern); CJC_ASSERT(typePattern->type && typePattern->ty && typePattern->type->ty == typePattern->ty); // Downcast or Upcast. bool cond = NeedAutoBox(typePattern->ty, &selectorTy, false) || NeedAutoBox(&selectorTy, typePattern->ty); boxOrUnbox = cond || MustUnboxDownCast(selectorTy, *typePattern->ty); break; } case ASTKind::TUPLE_PATTERN: { auto tuplePattern = StaticCast(pattern); auto tupleTy = StaticCast(&selectorTy); for (size_t i = 0; i < tuplePattern->patterns.size(); i++) { CJC_ASSERT(tupleTy->typeArgs[i]); bool cond = IsTypePatternNeedBox(tuplePattern->patterns[i].get(), *tupleTy->typeArgs[i]); boxOrUnbox = boxOrUnbox || cond; } break; } case ASTKind::ENUM_PATTERN: { auto enumPattern = StaticCast(pattern); CJC_ASSERT(enumPattern->constructor && enumPattern->constructor->ty); auto constructorTy = DynamicCast(enumPattern->constructor->ty); if (!constructorTy) { // Enum pattern may without param. break; } for (size_t i = 0; i < enumPattern->patterns.size(); i++) { auto paramTy = constructorTy->paramTys[i]; CJC_ASSERT(paramTy); bool cond = IsTypePatternNeedBox(enumPattern->patterns[i].get(), *paramTy); boxOrUnbox = boxOrUnbox || cond; } break; } default: break; } return boxOrUnbox; } void ExtendBoxMarker::MarkBoxPointHandleCondition(Expr& e) { // record outermost condition to mark as box auto& target = e; std::stack st; st.push(&e); while (!st.empty()) { auto expr = st.top(); st.pop(); if (!expr || !Ty::IsTyCorrect(expr->ty)) { continue; } if (auto let = DynamicCast(expr)) { if (!let->initializer || !Ty::IsTyCorrect(let->initializer->ty)) { continue; } for (auto& pat : std::as_const(let->patterns)) { if (IsTypePatternNeedBox(pat.get(), *let->initializer->ty)) { target.EnableAttr(Attribute::NEED_AUTO_BOX); pat->EnableAttr(Attribute::NEED_AUTO_BOX); } } } if (auto par = DynamicCast(expr)) { st.push(&*par->expr); } if (auto bin = DynamicCast(expr); bin && (bin->op == TokenKind::AND || bin->op == TokenKind::OR)) { st.push(&*bin->leftExpr); st.push(&*bin->rightExpr); } } } VisitAction ExtendBoxMarker::MarkBoxPointHandleMatchExpr(MatchExpr& me) { if (!Ty::IsTyCorrect(me.ty)) { return VisitAction::SKIP_CHILDREN; } // NOTE: we also need to collect all boxed types, so do not interrupt loop early. for (auto& matchCase : me.matchCases) { // Primary constructors and their desugared components (such as default params) are not type checked and do // not have a correct semantic type field. if (!me.selector || !Ty::IsTyCorrect(me.selector->ty)) { continue; } for (auto& pattern : matchCase->patterns) { if (IsTypePatternNeedBox(pattern.get(), *me.selector->ty)) { // It's possible that childs have different box type, so does not break after match. // auto box or unbox. me.EnableAttr(Attribute::NEED_AUTO_BOX); } } if (matchCase->exprOrDecls) { CheckBlockNeedBox(*matchCase->exprOrDecls, *me.ty, me); } } for (auto& matchCase : me.matchCaseOthers) { if (matchCase->exprOrDecls) { CheckBlockNeedBox(*matchCase->exprOrDecls, *me.ty, me); } } return VisitAction::WALK_CHILDREN; } VisitAction ExtendBoxMarker::MarkBoxPointHandleArrayLit(ArrayLit& lit) { if (Ty::IsTyCorrect(lit.ty) && lit.ty->typeArgs.size() == 1) { for (auto& child : lit.children) { // It's possible that child 0 and child 1 needs different box type, so does not break after match. if (child->ty && NeedAutoBox(child->ty, lit.ty->typeArgs[0])) { lit.EnableAttr(Attribute::NEED_AUTO_BOX); } } } return VisitAction::WALK_CHILDREN; } VisitAction ExtendBoxMarker::MarkBoxPointHandleReturnExpr(ReturnExpr& re) { if (re.expr && re.refFuncBody && re.refFuncBody->ty && re.refFuncBody->ty->kind == TypeKind::TYPE_FUNC) { auto funcTy = RawStaticCast(re.refFuncBody->ty); if (NeedAutoBox(re.expr->ty, funcTy->retTy)) { re.EnableAttr(Attribute::NEED_AUTO_BOX); } } return VisitAction::WALK_CHILDREN; } VisitAction ExtendBoxMarker::MarkBoxPointHandleIfExpr(IfExpr& ie) { if (Ty::IsTyCorrect(ie.ty) && (ie.condExpr && Ty::IsTyCorrect(ie.condExpr->ty))) { MarkBoxPointHandleCondition(*ie.condExpr); } if (Ty::IsTyCorrect(ie.ty) && !ie.ty->IsUnitOrNothing()) { if (ie.thenBody) { CheckBlockNeedBox(*ie.thenBody, *ie.ty, ie); } if (!ie.hasElse || !ie.elseBody) { return VisitAction::WALK_CHILDREN; } if (auto block = DynamicCast(ie.elseBody.get()); block) { CheckBlockNeedBox(*block, *ie.ty, ie); } } return VisitAction::WALK_CHILDREN; } VisitAction ExtendBoxMarker::MarkBoxPointHandleWhileExpr(const AST::WhileExpr& we) { if (Ty::IsTyCorrect(we.ty) && we.condExpr && Ty::IsTyCorrect(we.condExpr->ty)) { MarkBoxPointHandleCondition(*we.condExpr); } return VisitAction::WALK_CHILDREN; } VisitAction ExtendBoxMarker::MarkBoxPointHandleCallExpr(CallExpr& ce) { if (!ce.baseFunc || !ce.baseFunc->ty || ce.baseFunc->ty->kind != TypeKind::TYPE_FUNC) { return VisitAction::WALK_CHILDREN; } auto funcTy = RawStaticCast(ce.baseFunc->ty); unsigned count = 0; auto callCheck = [&count, &funcTy, &ce](auto begin, auto end) { for (auto it = begin; it != end; ++it) { if (count >= funcTy->paramTys.size()) { break; } auto& paramTy = funcTy->paramTys[count]; // It's possible that childs have different box type, so does not break after match. if ((*it)->expr && NeedAutoBox((*it)->expr->ty, paramTy)) { ce.EnableAttr(Attribute::NEED_AUTO_BOX); } count = count + 1; } }; if (ce.desugarArgs.has_value()) { callCheck(ce.desugarArgs->begin(), ce.desugarArgs->end()); } else { callCheck(ce.args.begin(), ce.args.end()); } return VisitAction::WALK_CHILDREN; } VisitAction ExtendBoxMarker::MarkBoxPointHandleAssignExpr(AssignExpr& ae) { // Desugared assign expression will be skipped. if (!ae.desugarExpr && ae.rightExpr && ae.leftValue && NeedAutoBox(ae.rightExpr->ty, ae.leftValue->ty)) { ae.EnableAttr(Attribute::NEED_AUTO_BOX); } return VisitAction::WALK_CHILDREN; } VisitAction ExtendBoxMarker::MarkBoxPointHandleVarDecl(VarDecl& vd) { if (vd.initializer && NeedAutoBox(vd.initializer->ty, vd.ty)) { vd.EnableAttr(Attribute::NEED_AUTO_BOX); } return VisitAction::WALK_CHILDREN; } VisitAction ExtendBoxMarker::MarkBoxPointHandleTupleLit(TupleLit& tl) { // Tuple literal allows element been boxed. auto tupleTy = DynamicCast(tl.ty); if (tupleTy == nullptr) { return VisitAction::WALK_CHILDREN; } auto typeArgs = tupleTy->typeArgs; for (size_t i = 0; i < typeArgs.size(); ++i) { if (NeedAutoBox(tl.children[i]->ty, typeArgs[i])) { tl.EnableAttr(Attribute::NEED_AUTO_BOX); } } return VisitAction::WALK_CHILDREN; } cangjie_compiler-1.0.7/src/Sema/ExtendBoxMarker.h000066400000000000000000000062631510705540100217050ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares a class for extend box marker. */ #ifndef CANGJIE_SEMA_EXTEND_BOX_MARKER_H #define CANGJIE_SEMA_EXTEND_BOX_MARKER_H #include #include #include "cangjie/AST/Node.h" #include "cangjie/AST/Walker.h" #include "cangjie/Sema/TypeManager.h" namespace Cangjie { class ExtendBoxMarker { public: static std::function)> GetMarkExtendBoxFunc(TypeManager& typeMgr); /** * Return true if @p selectorTy and @p patternTy must be treated as unboxing downcast. * NOTE: for special case: * interface I {}; class A <: I {} * func f(any: Any) { * let i : I = (any as I).getOrThrow() * let v = i as A * } * main() { * f(A()) * } * If do not downcast with box, the conversion 'i as A' will be failed since the instance is actually 'BOX_A'. * So, when converting interface to class, we always need to check both boxed condition and non-box condition. * And for another case: * If selector's type is interface and the pattern's type is class, * the pattern must be boxed even extension relation checking failed, * since the instance may be subclass of pattern's type which has extend relation with the interface. * eg. open class A {}; class B <: A {} * interface I {}; extend B <: I {} * let ins : I = B() * then 'ins is A' should be true. */ static bool MustUnboxDownCast(const AST::Ty& selectorTy, const AST::Ty& patternTy) { return selectorTy.IsInterface() && patternTy.IsClass(); } static std::mutex mtx; // Used to keep marker working as thread-safe. private: static AST::VisitAction MarkBoxPointHandleVarDecl(AST::VarDecl& vd); static AST::VisitAction MarkBoxPointHandleAssignExpr(AST::AssignExpr& ae); static AST::VisitAction MarkBoxPointHandleCallExpr(AST::CallExpr& ce); static AST::VisitAction MarkBoxPointHandleIfExpr(AST::IfExpr& ie); static AST::VisitAction MarkBoxPointHandleWhileExpr(const AST::WhileExpr& we); static void MarkBoxPointHandleCondition(AST::Expr& e); static AST::VisitAction MarkBoxPointHandleReturnExpr(AST::ReturnExpr& re); static AST::VisitAction MarkBoxPointHandleArrayLit(AST::ArrayLit& lit); static AST::VisitAction MarkBoxPointHandleTryExpr(AST::TryExpr& te); static AST::VisitAction MarkBoxPointHandleMatchExpr(AST::MatchExpr& me); static AST::VisitAction MarkBoxPointHandleArrayExpr(AST::ArrayExpr& ae); static AST::VisitAction MarkBoxPointHandleTupleLit(AST::TupleLit& tl); static bool IsTypePatternNeedBox(Ptr pattern, AST::Ty& selectorTy); static bool NeedAutoBox(Ptr child, Ptr interface, bool isUpcast = true); static void CheckBlockNeedBox(const AST::Block& block, AST::Ty& ty, AST::Node& nodeToCheck); static TypeManager* typeManager; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Sema/ExtraScopes.cpp000066400000000000000000000317251510705540100214370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file manages type check information that should be controlled by scope. */ #include "ExtraScopes.h" #include "TypeCheckUtil.h" #include "Promotion.h" namespace Cangjie { using namespace AST; using namespace TypeCheckUtil; namespace { inline bool IsCurrentGeneric(const FuncDecl& fd, const CallExpr& ce) { return GetCurrentGeneric(fd, ce) != nullptr; } bool IsBaseTypeOmittedTypeArgs(const MemberAccess& ma) { // Caller guarantees 'ma.baseExpr' and 'ma.baseExpr->ty' valid. // For the case like 'A.add()' or 'pkg.A.add()' that type 'A' is generic type without type argument. auto baseTarget = TypeCheckUtil::GetRealTarget(ma.baseExpr->GetTarget()); bool isGenericType = baseTarget && baseTarget->IsTypeDecl() && baseTarget->TestAttr(Attribute::GENERIC); if (!isGenericType) { return false; } if (IsThisOrSuper(*ma.baseExpr)) { return false; // Return false is base expr is 'this' or 'super'. } auto nre = DynamicCast(ma.baseExpr.get()); return nre && nre->instTys.empty() && NeedFurtherInstantiation(ma.baseExpr->GetTypeArgs()); } } TyVarScope::TyVarScope(TypeManager& tyMgr) : tyMgr(tyMgr) { tyMgr.tyVarScopes.push_back(this); } TyVarScope::~TyVarScope() { for (auto tyVar: tyVars) { tyMgr.ReleaseTyVar(tyVar); } tyMgr.tyVarScopes.pop_back(); } void TyVarScope::AddTyVar(Ptr tyVar) { tyVars.push_back(tyVar); } InstCtxScope::InstCtxScope(TypeChecker::TypeCheckerImpl& typeChecker) :tyMgr(typeChecker.typeManager), diag(typeChecker.diag), typeChecker(typeChecker) { tyMgr.instCtxScopes.push_back(this); } InstCtxScope::~InstCtxScope() { tyMgr.instCtxScopes.pop_back(); } void InstCtxScope::SetRefDecl(const AST::Decl& decl, Ptr instTy) { if (!decl.generic) { return; } auto& genParams = decl.generic->typeParameters; CJC_ASSERT(genParams.size() == instTy->typeArgs.size()); refMaps = {}; for (size_t i = 0; i < instTy->typeArgs.size(); i++) { auto itv = tyMgr.AllocTyVar(); refMaps.u2i[StaticCast(genParams[i]->ty)] = itv; refMaps.inst[itv].insert(instTy->typeArgs[i]); } maps = curMaps; MergeSubstPack(maps, refMaps); } bool InstCtxScope::GenerateTypeMappingByCallContext( const ASTContext& ctx, const FuncDecl& fd, const CallExpr& ce, SubstPack& typeMapping) { CJC_NULLPTR_CHECK(ce.baseFunc); CJC_ASSERT(ce.baseFunc->astKind == ASTKind::REF_EXPR); bool invalid = !fd.outerDecl || !Ty::IsTyCorrect(fd.outerDecl->ty); if (invalid) { return false; } auto structDecl = fd.outerDecl; if (!structDecl->TestAttr(Attribute::GENERIC)) { return true; // If current outer nominal decl is not generic, ignore. } auto sym = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, ce.scopeName); if (!sym || !sym->node || !sym->node->ty) { return false; } auto prRes = Promotion(tyMgr).Promote(*sym->node->ty, *structDecl->ty); bool generated = false; for (auto promoteTy : prRes) { if (!Ty::IsTyCorrect(promoteTy)) { continue; } // If the current candidate is a generic function inside generic structure, // just check the constraint of generic structure without diagnose (using function in typeManger) // otherwise check the constraint and report error. bool isConstraintFit = IsCurrentGeneric(fd, ce) ? tyMgr.CheckGenericDeclInstantiation(structDecl, promoteTy->typeArgs) : typeChecker.CheckGenericDeclInstantiation(structDecl, promoteTy->typeArgs, ce); bool invalidArgSize = structDecl->ty->typeArgs.size() != promoteTy->typeArgs.size(); if (!isConstraintFit || invalidArgSize) { continue; } GenerateTypeMapping(tyMgr, typeMapping, *structDecl, promoteTy->typeArgs); generated = true; } return generated; } // Caller guarantees 'fd.outerDecl' is extendDecl. bool InstCtxScope::GenerateExtendGenericTypeMapping( const ASTContext& ctx, const FuncDecl& fd, const CallExpr& ce, SubstPack& typeMapping) { auto extend = RawStaticCast(fd.outerDecl); if (!extend->extendedType || !Ty::IsTyCorrect(extend->extendedType->ty)) { return false; } if (!IsCurrentGeneric(fd, ce) && extend->TestAttr(Attribute::GENERIC) && !ce.baseFunc->GetTypeArgs().empty()) { diag.Diagnose(ce, DiagKind::sema_non_generic_function_with_type_argument); return false; } if (ce.baseFunc->astKind == ASTKind::REF_EXPR) { // Base expr is 'RefExpr' and parent of candidate function may be generic decl. if (!GenerateTypeMappingByCallContext(ctx, fd, ce, typeMapping)) { return false; } } if (ce.baseFunc->astKind != ASTKind::MEMBER_ACCESS) { return true; } auto ma = StaticAs(ce.baseFunc.get()); if (!ma->baseExpr || !Ty::IsTyCorrect(ma->baseExpr->ty)) { return false; } if (extend->generic) { // in case base expr missing ty arg, the placeholder will be generated here // But the placeholders are only for extend's generic args. // The baseExpr's ty args will be inferred in FillTypeArgumentsTy. for (auto& extGenParam : extend->generic->typeParameters) { tyMgr.MakeInstTyVar(typeMapping, *StaticCast(extGenParam->ty)); } } if (IsBaseTypeOmittedTypeArgs(*ma)) { return true; } // Since base expression is member access, // member access's type should be able to promote a valid type with extended type, // and the extended type must have same number of type arguments with the promoted type. auto prTys = Promotion(tyMgr).Promote( *ma->baseExpr->ty, *tyMgr.GetInstantiatedTy(extend->extendedType->ty, typeMapping.u2i)); auto promotedTy = prTys.empty() ? TypeManager::GetInvalidTy() : *prTys.begin(); if (!Ty::IsTyCorrect(promotedTy)) { return false; } auto baseArgs = tyMgr.GetTypeArgs(*promotedTy); if (extend->TestAttr(Attribute::GENERIC) && !typeChecker.CheckGenericDeclInstantiation(fd.outerDecl, baseArgs, ce)) { return false; } GenerateTypeMapping(tyMgr, typeMapping, *extend, baseArgs); RelayMappingFromExtendToExtended(tyMgr, typeMapping, *extend); return true; } void InstCtxScope::GenerateSubstPackByTyArgs( SubstPack& tmaps, const std::vector>& typeArgs, const Generic& generic) const { // Add u2i mapping anyway // Add inst mapping when we can get the type parameters and type arguments. auto tyParamSize = generic.typeParameters.size(); // Error case. Maybe shouldn't reach here. if (tyParamSize < typeArgs.size()) { return; } for (size_t i = 0; i < tyParamSize; ++i) { if (!generic.typeParameters[i]) { continue; } auto uTy = generic.typeParameters[i]->ty; if (Ty::IsTyCorrect(uTy)) { auto uGenTy = StaticCast(uTy); if (tmaps.u2i.count(uGenTy) == 0) { auto iGenTy = tyMgr.AllocTyVar(); tmaps.u2i.emplace(uGenTy, iGenTy); } if (i >= typeArgs.size()) { continue; } if (typeArgs[i] && Ty::IsTyCorrect(typeArgs[i]->ty) && !typeArgs[i]->ty->HasIntersectionTy()) { tmaps.inst[StaticCast(tmaps.u2i[uGenTy])] = {typeArgs[i]->ty}; } } } } // When the baseExpr of other expr is a memberAccess, // we could build the type mapping by the memberAccess's baseExpr's type. void TypeChecker::TypeCheckerImpl::GenerateTypeMappingForBaseExpr(const Expr& baseExpr, SubstPack& typeMapping) { if (baseExpr.astKind != ASTKind::MEMBER_ACCESS) { return; } auto& ma = static_cast(baseExpr); if (!Ty::IsTyCorrect(ma.baseExpr->ty)) { return; } CJC_ASSERT(!ma.baseExpr->ty->HasIntersectionTy()); if (IsThisOrSuper(*ma.baseExpr)) { typeManager.GenerateGenericMapping(typeMapping, *ma.baseExpr->ty); return; } auto directBase = ma.baseExpr->GetTarget(); auto realBase = GetRealTarget(ma.baseExpr.get(), ma.baseExpr->GetTarget()); SubstPack directMapping; if (directBase && (directBase != realBase)) { // in case of alias, where directBase is the alias decl, realBase is the real type decl typeManager.MakeInstTyVar(directMapping, *directBase); } if (realBase && realBase->astKind == ASTKind::PACKAGE_DECL) { return; } auto maTarget = ma.GetTarget(); // NOTE: member access of enum constructor is also considered as type decl's member access for inference. bool typeDeclMemberAccess = realBase && maTarget && maTarget->outerDecl && (realBase->IsTypeDecl() || realBase->TestAttr(Attribute::ENUM_CONSTRUCTOR)); if (typeDeclMemberAccess) { CJC_NULLPTR_CHECK(maTarget->outerDecl->ty); auto instBaseTy = typeManager.GetInstantiatedTy(ma.baseExpr->ty, directMapping.u2i); auto promoteMapping = promotion.GetPromoteTypeMapping(*instBaseTy, *maTarget->outerDecl->ty); if (realBase->ty != instBaseTy) { typeManager.PackMapping(typeMapping, GenerateTypeMapping(*realBase, instBaseTy->typeArgs)); } auto baseTypeArgs = ma.baseExpr->GetTypeArgs(); std::unordered_set> baseTyArgs; std::for_each( baseTypeArgs.begin(), baseTypeArgs.end(), [&baseTyArgs](auto type) { baseTyArgs.emplace(type->ty); }); auto genericTys = GetAllGenericTys(realBase->ty); for (auto it = promoteMapping.begin(); it != promoteMapping.end(); ++it) { // If mapped 'ty' exists in 'realBase' generic types // and is not found in user defined type args, remove it from mapping. Utils::EraseIf(it->second, [&genericTys, &baseTyArgs](auto ty) { return genericTys.count(ty) != 0 && baseTyArgs.count(ty) == 0; }); } auto genericTysInst = GetAllGenericTys(ma.baseExpr->ty); /* in case the used type is alias, the ty vars to be solved are type parameters of the alias decl * but in case the type args are already given by users, they don't need to be solved */ Utils::EraseIf(directMapping.u2i, [&genericTysInst](auto it) { return genericTysInst.count(it.first) == 0; }); MergeSubstPack(typeMapping, directMapping); typeManager.PackMapping(typeMapping, promoteMapping); } else { typeManager.GenerateGenericMapping(typeMapping, *ma.baseExpr->ty); } } void InstCtxScope::SetRefDeclSimple(const AST::FuncDecl& fd, const AST::CallExpr& ce) { for (auto tv : GetTyVars(fd, ce, true)) { refMaps.u2i.emplace(tv, tyMgr.AllocTyVar()); } maps = curMaps; MergeSubstPack(maps, refMaps); } bool InstCtxScope::SetRefDecl(ASTContext& ctx, AST::FuncDecl& fd, CallExpr& ce) { refMaps = {}; std::vector> typeArgs = ce.baseFunc->GetTypeArgs(); bool isInStructDecl = fd.outerDecl && fd.outerDecl->IsNominalDecl() && !IsTypeObjectCreation(fd, ce); if (IsGenericUpperBoundCall(*ce.baseFunc, fd)) { // Handle upperbound function. tyMgr.GenerateTypeMappingForUpperBounds( refMaps, *RawStaticCast(ce.baseFunc.get()), fd); } else if (isInStructDecl && fd.outerDecl->astKind == ASTKind::EXTEND_DECL) { // Handle extend function. if (!GenerateExtendGenericTypeMapping(ctx, fd, ce, refMaps)) { return false; } } else { if (!IsCurrentGeneric(fd, ce) && !typeArgs.empty()) { diag.Diagnose(ce, DiagKind::sema_non_generic_function_with_type_argument); return false; } // Base expr is RefExpr, and the context of reference may be generic. if (ce.baseFunc->astKind == ASTKind::REF_EXPR && isInStructDecl) { // When the callee and caller are in different structure decl and there are generic decl between them, // check the inheritance relationship and generate typeMapping. // NOTE: It is possible that reference context and the candidate function are both generic. if (!GenerateTypeMappingByCallContext(ctx, fd, ce, refMaps)) { return false; } } else if (ce.baseFunc->astKind == ASTKind::MEMBER_ACCESS) { ReplaceTarget(ce.baseFunc.get(), &fd); // Update target for typeMapping generation. // Check the base of memberAccess and generate typeMapping by base. typeChecker.GenerateTypeMappingForBaseExpr(*ce.baseFunc, refMaps); } } if (auto generic = GetCurrentGeneric(fd, ce)) { GenerateSubstPackByTyArgs(refMaps, typeArgs, *generic); } maps = curMaps; MergeSubstPack(maps, refMaps); return true; } } cangjie_compiler-1.0.7/src/Sema/ExtraScopes.h000066400000000000000000000051741510705540100211030ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file manages type check information that should be controlled by scope. */ #ifndef CANGJIE_SEMA_EXTRASCOPES_H #define CANGJIE_SEMA_EXTRASCOPES_H #include "cangjie/Sema/TypeManager.h" #include "cangjie/AST/ASTContext.h" #include "TypeCheckerImpl.h" #include "Diags.h" namespace Cangjie { // scope of introducing placeholder type var class TyVarScope { public: explicit TyVarScope(TypeManager& tyMgr); ~TyVarScope(); TyVarScope(const TyVarScope& other) = delete; TyVarScope& operator =(const TyVarScope& other) = delete; private: friend class TypeManager; void AddTyVar(Ptr tyVar); std::vector> tyVars; TypeManager& tyMgr; std::string scope; }; // scope of type instantiation context class InstCtxScope { public: explicit InstCtxScope(TypeChecker::TypeCheckerImpl& typeChecker); ~InstCtxScope(); /** * If we are using FuncA within FuncB, then current decl is FuncB, * referenced decl is FuncA. */ // generate mapping between decl and its instantiated ty void SetRefDecl(const AST::Decl& decl, Ptr instTy); // generate all needed mappings with all available info for a CallExpr, // ty vars remaining to be solved are not mapped in inst map bool SetRefDecl(ASTContext& ctx, AST::FuncDecl& fd, AST::CallExpr& ce); // simply generate u2i mapping for all universal ty vars used for a CallExpr void SetRefDeclSimple(const AST::FuncDecl& fd, const AST::CallExpr& ce); InstCtxScope(const InstCtxScope& other) = delete; InstCtxScope& operator =(const InstCtxScope& other) = delete; private: friend class TypeManager; SubstPack curMaps; // mapping only from current decl SubstPack refMaps; // mapping only from referenced decl SubstPack maps; // merged mapping, users should use this TypeManager& tyMgr; DiagnosticEngine& diag; TypeChecker::TypeCheckerImpl& typeChecker; bool GenerateExtendGenericTypeMapping( const ASTContext& ctx, const AST::FuncDecl& fd, const AST::CallExpr& ce, SubstPack& typeMapping); bool GenerateTypeMappingByCallContext( const ASTContext& ctx, const AST::FuncDecl& fd, const AST::CallExpr& ce, SubstPack& typeMapping); void GenerateSubstPackByTyArgs( SubstPack& tmaps, const std::vector>& typeArgs, const AST::Generic& generic) const; }; } #endif cangjie_compiler-1.0.7/src/Sema/FFI/000077500000000000000000000000001510705540100170675ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/FFI/CFFICheck.cpp000066400000000000000000000345701510705540100212510ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements CFFI typecheck apis. */ #include "TypeCheckerImpl.h" #include "TypeCheckUtil.h" using namespace Cangjie; using namespace Cangjie::AST; using namespace TypeCheckUtil; namespace { void CheckAnnoC(const Decl& decl, const Annotation& anno, DiagnosticEngine& diag) { if (!anno.args.empty()) { diag.Diagnose(*anno.args[0], DiagKind::sema_annotation_error_arg_num, "@C", "no"); } auto generic = decl.GetGeneric(); if (decl.astKind == ASTKind::FUNC_DECL && generic) { diag.Diagnose(*generic, generic->leftAnglePos, DiagKind::sema_cffi_cannot_have_type_param, std::string{CFUNC_NAME}); } if (Utils::NotIn(decl.astKind, {ASTKind::FUNC_DECL, ASTKind::STRUCT_DECL})) { diag.Diagnose(anno, DiagKind::sema_illegal_use_of_annotation, DeclKindToString(decl), "@C"); } else if (decl.outerDecl != nullptr || decl.scopeLevel > 0) { diag.Diagnose(anno, DiagKind::sema_illegal_scope_use_of_annotation, "@C"); } } void CheckAnnoCallingConv(Decl& decl, const Annotation& anno, DiagnosticEngine& diag) { if (decl.outerDecl != nullptr || decl.scopeLevel > 0) { diag.Diagnose(anno, DiagKind::sema_illegal_scope_use_of_annotation, "@CallingConv"); return; } if (decl.astKind != ASTKind::FUNC_DECL || (!decl.TestAttr(Attribute::FOREIGN) && !decl.TestAttr(Attribute::C))) { // Will be merged with sema_unsupported_annotation_in_js_interop. // Will be replaced with anno->identifier. diag.Diagnose(anno, DiagKind::sema_only_cfunc_can_use_annotation, "@CallingConv"); return; } if (anno.args.size() != 1) { diag.Diagnose(anno, DiagKind::sema_annotation_error_arg_num, "@CallingConv", "one"); return; } if (auto refExpr = DynamicCast(anno.args.front()->expr.get()); refExpr) { std::unordered_map callingConvMap = { {"CDECL", Attribute::C}, {"STDCALL", Attribute::STD_CALL}, }; if (callingConvMap.find(refExpr->ref.identifier) == callingConvMap.end()) { // Will be merged with sema_unsupported_annotation_in_js_interop. diag.Diagnose(anno, DiagKind::sema_annotation_calling_conv_not_support, refExpr->ref.identifier.Val()); return; } decl.EnableAttr(callingConvMap[refExpr->ref.identifier]); } else { diag.Diagnose(anno, DiagKind::sema_annotation_invalid_args_type, "@CallingConv"); } } void CheckAnnoFastNative(const Decl& decl, const Annotation& anno, DiagnosticEngine& diag) { if (!anno.args.empty()) { diag.Diagnose(*anno.args[0], DiagKind::sema_annotation_error_arg_num, "@FastNative", "no"); } if (decl.astKind != ASTKind::FUNC_DECL || !decl.TestAttr(Attribute::FOREIGN)) { // Will be merged with sema_illegal_scope_use_of_annotation. std::string prefix = decl.astKind == ASTKind::FUNC_DECL ? "non-foreign " : ""; diag.Diagnose(anno, DiagKind::sema_illegal_use_of_annotation, prefix + DeclKindToString(decl), "@FastNative"); return; } if (decl.outerDecl != nullptr || decl.scopeLevel > 0) { diag.Diagnose(anno, DiagKind::sema_illegal_scope_use_of_annotation, "@FastNative"); } } void CheckAnnoFrozen(const Decl& decl, const Annotation& anno, DiagnosticEngine& diag) { if (!anno.args.empty()) { diag.Diagnose(*anno.args[0], DiagKind::sema_annotation_error_arg_num, "@Frozen", "no"); } if (decl.astKind != ASTKind::FUNC_DECL && decl.astKind != ASTKind::PROP_DECL) { // Will be merged with sema_illegal_scope_use_of_annotation. diag.Diagnose(anno, DiagKind::sema_illegal_use_of_annotation, DeclKindToString(decl), "@Frozen"); return; } if (decl.outerDecl != nullptr && decl.outerDecl->astKind == ASTKind::FUNC_DECL) { diag.Diagnose(anno, DiagKind::sema_illegal_use_of_annotation, "local function", "@Frozen"); } } using AnnoCheckMap = std::unordered_map>; AnnoCheckMap& GetAnnoCheckMap() { static AnnoCheckMap annoCheckMap = { {AnnotationKind::C, CheckAnnoC}, {AnnotationKind::CALLING_CONV, CheckAnnoCallingConv}, {AnnotationKind::FASTNATIVE, CheckAnnoFastNative}, {AnnotationKind::FROZEN, CheckAnnoFrozen}, }; return annoCheckMap; } bool IsZeroSizedTy(const Ty& ty) { std::unordered_set traversesCache; std::function isZeroSizedTy = [&isZeroSizedTy, &traversesCache](const Ty& ty) { if (ty.IsUnitOrNothing()) { return true; } if (!ty.IsStruct()) { return false; } if (traversesCache.find(&ty) != traversesCache.end()) { return false; } traversesCache.emplace(&ty); auto decl = Ty::GetDeclPtrOfTy(&ty); CJC_ASSERT(decl != nullptr && decl->astKind == ASTKind::STRUCT_DECL); auto sd = StaticCast(decl); auto& body = sd->body; for (auto& member : body->decls) { CJC_NULLPTR_CHECK(member); if (member->astKind != ASTKind::VAR_DECL || member->TestAttr(Attribute::STATIC)) { continue; } CJC_ASSERT(Ty::IsTyCorrect(member->ty)); if (!isZeroSizedTy(*member->ty)) { return false; } } return true; }; return isZeroSizedTy(ty); } inline Range GetFuncBodyRange(const FuncBody& fb, const Type& type) { if (auto begin = type.GetBegin(); !begin.IsZero()) { return MakeRange(begin, type.GetEnd()); } auto fd = fb.funcDecl; CJC_NULLPTR_CHECK(fd); return MakeRange(fd->identifier); } } // namespace Attribute TypeChecker::TypeCheckerImpl::GetDefaultABI() { // This should be consistent with BackendType. All BackendType should have a default ABI. static std::unordered_map defaultABI = { {Triple::BackendType::CJNATIVE, Attribute::C}}; if (defaultABI.find(backendType) != defaultABI.end()) { return defaultABI[backendType]; } // Will only appear when the definitions between defaultABI and Triple::BackendType are inconsistent. return Attribute::C; } void TypeChecker::TypeCheckerImpl::SetForeignABIAttr(Decl& decl) { // Will unify the judgment of foreign, foreign, C, and backend. // Update W/R in class Attribute. if (HasModifier(decl.modifiers, TokenKind::FOREIGN)) { auto defaultABI = GetDefaultABI(); for (auto& anno : decl.annotations) { if (anno->kind == AnnotationKind::C) { defaultABI = Attribute::C; } } decl.EnableAttr(defaultABI); } } void TypeChecker::TypeCheckerImpl::CheckInvalidRefInCFunc( const ASTContext& ctx, const AST::RefExpr& re, const AST::Decl& target) const { if (!IsInCFunc(ctx, re) || target.TestAnyAttr(Attribute::CONSTRUCTOR, Attribute::ENUM_CONSTRUCTOR)) { return; } bool isNonStaticMember = !target.TestAttr(Attribute::STATIC) && target.TestAnyAttr(Attribute::IN_CLASSLIKE, Attribute::IN_STRUCT, Attribute::IN_ENUM, Attribute::IN_EXTEND); if (isNonStaticMember) { diag.Diagnose(re, DiagKind::sema_cfunc_cannot_capture_this, "this"); } } void TypeChecker::TypeCheckerImpl::CheckInvalidRefInCFunc(const ASTContext& ctx, const AST::RefExpr& re) const { if (!IsInCFunc(ctx, re)) { return; } if (re.isThis) { diag.Diagnose(re, DiagKind::sema_cfunc_cannot_capture_this, "this"); } else if (re.isSuper) { diag.Diagnose(re, DiagKind::sema_cfunc_cannot_capture_this, "super"); } } bool TypeChecker::TypeCheckerImpl::IsInCFunc(const ASTContext& ctx, const AST::RefExpr& re) const { Symbol* curFuncSym = ScopeManager::GetCurSymbolByKind(SymbolKind::FUNC_LIKE, ctx, re.scopeName); while (curFuncSym && curFuncSym->scopeName.length() > 0) { if (auto le = DynamicCast(curFuncSym->node); le && Ty::IsTyCorrect(le->ty) && le->ty->IsCFunc()) { return true; } curFuncSym = ScopeManager::GetCurSymbolByKind(SymbolKind::FUNC_LIKE, ctx, curFuncSym->scopeName); } return false; } void TypeChecker::TypeCheckerImpl::CheckCTypeMember(const Decl& decl) { if (decl.astKind != ASTKind::VAR_DECL || Ty::IsInitialTy(decl.ty) || decl.outerDecl == nullptr || !Ty::IsTyCorrect(decl.outerDecl->ty)) { return; } if (Ty::IsCStructType(*decl.outerDecl->ty)) { const auto& vd = StaticCast(decl); if (vd.ty->IsUnit()) { diag.Diagnose(vd.identifier.Begin(), vd.type ? vd.type->end : vd.identifier.End(), DiagKind::sema_cstruct_cannot_have_unit_fields, vd.identifier.Val()); return; } if (Ty::IsMetCType(*vd.ty)) { return; } diag.Diagnose(vd.identifier.Begin(), vd.type ? vd.type->end : vd.identifier.End(), DiagKind::sema_illegal_member_of_cstruct, vd.identifier.Val(), vd.outerDecl->identifier.Val()); } } void TypeChecker::TypeCheckerImpl::CheckUnsafeInvoke(const CallExpr& ce) { if (ce.resolvedFunction == nullptr || !Ty::IsTyCorrect(ce.resolvedFunction->ty) || !IsUnsafeBackend(backendType)) { return; } auto funcTy = StaticCast(ce.resolvedFunction->ty); bool isUnsafe = ce.resolvedFunction->TestAttr(Attribute::UNSAFE) || ce.resolvedFunction->TestAttr(Attribute::FOREIGN) || funcTy->isC; if (!isUnsafe) { return; } if (!ce.TestAttr(Attribute::UNSAFE)) { diag.Diagnose(ce, DiagKind::sema_unsafe_function_invoke_failed); } } void TypeChecker::TypeCheckerImpl::CheckLegalityOfUnsafeAndInout(Node& root) { Walker(&root, [this](auto node) { CJC_ASSERT(node); if (node->astKind == ASTKind::CALL_EXPR) { CheckUnsafeInvoke(static_cast(*node)); } else if (node->astKind == ASTKind::FUNC_ARG) { const auto& fa = static_cast(*node); // If the object modified by inout isn't a RefExpr or MemberAccess node or doesn't meet CType constraint, // it's wrong and will be diagnosed in the previous stage. if (fa.withInout && Ty::IsTyCorrect(fa.expr->ty) && Utils::In(fa.expr->astKind, {ASTKind::REF_EXPR, ASTKind::MEMBER_ACCESS}) && Ty::IsMetCType(*fa.expr->ty) && IsZeroSizedTy(*fa.expr->ty)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_modify_cstring_or_zerosized, fa, "zero-sized type"); } } return VisitAction::WALK_CHILDREN; }).Walk(); } void TypeChecker::TypeCheckerImpl::UnsafeCheck(const FuncBody& fb) { if (fb.funcDecl && (fb.funcDecl->TestAttr(Attribute::FOREIGN) || fb.funcDecl->TestAttr(Attribute::C))) { CJC_ASSERT(!fb.paramLists.empty() && "function body's param list cannot be empty!"); for (auto& arg : fb.paramLists[0]->params) { CheckCFuncParam(*arg); } CJC_NULLPTR_CHECK(fb.retType); if (!Ty::IsTyCorrect(fb.retType->ty)) { return; } if (!Ty::IsMetCType(*fb.retType->ty)) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_invalid_cfunc_return_type, *fb.retType, GetFuncBodyRange(fb, *fb.retType)); builder.AddNote("return type is " + fb.retType->ty->String()); } else if (Is(fb.retType->ty)) { // VArray not allowed as CFunc return type. diag.DiagnoseRefactor( DiagKindRefactor::sema_varray_in_cfunc, *fb.retType, GetFuncBodyRange(fb, *fb.retType)); } } } void TypeChecker::TypeCheckerImpl::CheckCFuncParam(const AST::FuncParam& fp) { if (!Ty::IsTyCorrect(fp.ty)) { return; } if (fp.isNamedParam) { diag.Diagnose(fp, DiagKind::sema_cfunc_cannot_have_named_args); } if (fp.ty->IsUnit()) { diag.Diagnose(fp.identifier.Begin(), fp.type->end, DiagKind::sema_cfunc_cannot_have_unit_args); } else if (!Ty::IsMetCType(*fp.ty)) { diag.Diagnose(fp.identifier.Begin(), fp.type->end, DiagKind::sema_invalid_cfunc_arg_type); } } void TypeChecker::TypeCheckerImpl::CheckCFuncParamType(const AST::Type& type) { if (type.ty->IsUnit()) { diag.Diagnose(type.begin, type.end, DiagKind::sema_cfunc_cannot_have_unit_args); } else if (!Ty::IsMetCType(*type.ty)) { diag.Diagnose(type.begin, type.end, DiagKind::sema_invalid_cfunc_arg_type); } } void TypeChecker::TypeCheckerImpl::PreCheckAnnoForCFFI(Decl& decl) { if (HasModifier(decl.modifiers, TokenKind::FOREIGN) && !decl.HasAnno(AnnotationKind::C)) { // Check node. auto modifier = decl.modifiers.crbegin(); if (decl.astKind == ASTKind::CLASS_DECL) { diag.Diagnose(*modifier, DiagKind::sema_unexpected_wrapper, "class declaration"); } if (decl.astKind == ASTKind::INTERFACE_DECL) { diag.Diagnose(*modifier, DiagKind::sema_unexpected_wrapper, "interface declaration"); } // Will be merged with sema_unexpected_wrapper. if (decl.astKind == ASTKind::VAR_DECL) { diag.Diagnose(decl, DiagKind::sema_native_var_error); } } auto isAnnoC = [](const OwnedPtr& anno) { return anno->kind == AnnotationKind::C; }; auto found = std::find_if(decl.annotations.begin(), decl.annotations.end(), isAnnoC); if (found != decl.annotations.end()) { decl.EnableAttr(Attribute::C); if (auto fd = DynamicCast(&decl); fd && fd->funcBody) { fd->funcBody->EnableAttr(Attribute::C); } } for (auto& anno : decl.annotations) { auto annoCheckMap = GetAnnoCheckMap(); auto checkerIt = annoCheckMap.find(anno->kind); if (checkerIt!= annoCheckMap.end()) { checkerIt->second(decl, *anno, diag); } } if (decl.astKind == ASTKind::FUNC_DECL) { if (decl.TestAttr(AST::Attribute::FOREIGN)) { decl.EnableAttr(Attribute::NO_MANGLE); } if (auto fd = StaticCast(&decl); fd && fd->TestAttr(Attribute::FOREIGN)) { fd->linkage = Linkage::INTERNAL; } } } cangjie_compiler-1.0.7/src/Sema/FFI/CMakeLists.txt000066400000000000000000000005121510705540100216250ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB FFI_SRC *.cpp) set(SEMA_SRC ${SEMA_SRC} ${FFI_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/FFI/FFICheck.cpp000066400000000000000000000017041510705540100211370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements FFI typecheck apis. */ #include "TypeCheckerImpl.h" #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" using namespace Cangjie; using namespace Cangjie::AST; void TypeChecker::TypeCheckerImpl::PreCheckAnnoForFFI(Node& root) { if (root.TestAttr(Attribute::IMPORTED)) { return; } Walker walker(&root, [this](const Ptr node) -> VisitAction { if (node && node->IsDecl()) { auto decl = StaticCast(node); SetForeignABIAttr(*decl); PreCheckAnnoForCFFI(*decl); } return VisitAction::WALK_CHILDREN; }); walker.Walk(); } cangjie_compiler-1.0.7/src/Sema/GenericInstantiation/000077500000000000000000000000001510705540100226045ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/GenericInstantiation/CMakeLists.txt000066400000000000000000000005541510705540100253500ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB GENERICINSTANTIATION_SRC *.cpp) set(SEMA_SRC ${SEMA_SRC} ${GENERICINSTANTIATION_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/GenericInstantiation/GenericInstantiationContext.cpp000066400000000000000000000627401510705540100310070ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * Implements GenericInstantiation related methods. */ #include "GenericInstantiationManagerImpl.h" #include #include "TypeCheckUtil.h" #include "TypeCheckerImpl.h" #include "ImplUtils.h" #include "PartialInstantiation.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Sema/TypeManager.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; using GIM = GenericInstantiationManager; void GIM::GenericInstantiationManagerImpl::ClearCache() { structContext.clear(); extendGenerated.clear(); intersectionTyStatus.clear(); declInstantiationByTypeMap.clear(); instantiatedDeclsMap.clear(); membersIndexMap.clear(); } void GIM::GenericInstantiationManagerImpl::WalkImportedInstantiations( const std::function& processFunc, const std::function& skipChecker) const { for (auto pd : importManager.GetAllImportedPackages(true)) { CJC_ASSERT(pd && pd->srcPackage); auto srcPkg = pd->srcPackage; if (skipChecker(*srcPkg)) { continue; } for (auto& decl : srcPkg->genericInstantiatedDecls) { CJC_NULLPTR_CHECK(decl); if (decl->genericDecl && importManager.IsMacroRelatedPackageName(decl->genericDecl->fullPackageName)) { continue; } processFunc(*decl); } } } void GIM::GenericInstantiationManagerImpl::RebuildGenericInstantiationManager() { WalkImportedInstantiations( [this](Decl& decl) { // Do not building cache for extend decl when current is not incremental compilation. auto genericDecl = decl.genericDecl; if (!genericDecl || decl.astKind == ASTKind::EXTEND_DECL) { return; } GenericInfo genericInfo(genericDecl, BuildTypeMapping(decl)); declInstantiationByTypeMap.emplace(genericInfo, &decl); instantiatedDeclsMap[genericDecl].emplace(&decl); }, [this](auto& pkg) { // 1. When backend is llvm, do not rebuild cache. // 2. For other backend, filter out macro-related package. // Ensure that macro-related packages are used only in macro expansion. // NOTE: the incremental compilation does not support macro package. return backend == Triple::BackendType::CJNATIVE || importManager.IsMacroRelatedPackageName(pkg.fullPackageName); }); } void GIM::GenericInstantiationManagerImpl::RestoreInstantiatedDeclTy() const { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND WalkImportedInstantiations([this](Decl& decl) { RestoreInstantiatedDeclTy(decl); }, [this](auto& pkg) { return importManager.IsMacroRelatedPackageName(pkg.fullPackageName); }); #endif } /** * All sema types created in sema stage are only point to generic decls. * So after generic instantiation, we need to update sema ty's decl from generic decl to instantiated decl. * Since instantiated decls are not shared between irrelevant packages, * an instantiated decl can be restored to ty only if it's generated in relevant packages * or all of instantiated decls of same ty are generated in irrelevant package. */ void GIM::GenericInstantiationManagerImpl::RestoreInstantiatedDeclTy(Decl& decl) const { bool ignore = Ty::IsInitialTy(decl.ty) || !decl.IsNominalDecl() || decl.astKind == ASTKind::EXTEND_DECL; if (ignore) { return; } if (!IsDeclCanRestoredForTy(decl)) { return; } switch (decl.ty->kind) { case TypeKind::TYPE_CLASS: { auto ty = RawStaticCast(decl.ty); ty->decl = StaticAs(&decl); ty->commonDecl = StaticAs(&decl); auto thisTy = typeManager.GetClassThisTy(*ty->declPtr, ty->typeArgs); thisTy->decl = StaticAs(&decl); thisTy->commonDecl = StaticAs(&decl); break; } case TypeKind::TYPE_INTERFACE: { auto ty = RawStaticCast(decl.ty); ty->decl = StaticAs(&decl); ty->commonDecl = StaticAs(&decl); break; } case TypeKind::TYPE_STRUCT: { auto ty = RawStaticCast(decl.ty); ty->decl = StaticAs(&decl); break; } case TypeKind::TYPE_ENUM: { auto ty = RawStaticCast(decl.ty); ty->decl = StaticAs(&decl); break; } default: break; } } bool GIM::GenericInstantiationManagerImpl::IsDeclCanRestoredForTy(const Decl& decl) const { if (!curPkg) { return true; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (decl.fullPackageName == curPkg->fullPackageName) { return true; // If decl is in current package, the 'decl' can be set as ty's decl. } // If there is an instantiated version in current package, the 'decl' cannot be set as ty's decl. GenericInfo genericInfo(decl.genericDecl, BuildTypeMapping(decl)); auto decls = declInstantiationByTypeMap.equal_range(genericInfo); for (auto it = decls.first; it != decls.second; ++it) { auto instantiatedDecl = it->second; if (instantiatedDecl->fullPackageName == curPkg->fullPackageName) { return false; } } return true; #endif } TypeSubst GIM::GenericInstantiationManagerImpl::BuildTypeMapping( const Decl& instantiatedDecl) const { auto genericDecl = instantiatedDecl.genericDecl; if (genericDecl == nullptr) { return {}; } TypeSubst typeMapping = typeManager.GenerateGenericMappingFromGeneric(*genericDecl, instantiatedDecl); return typeMapping; } void GIM::GenericInstantiationManagerImpl::CollectDeclMemberFuncs( Decl& decl, std::unordered_set>& funcs) const { for (auto& member : decl.GetMemberDecls()) { // Only collect non-private non-generic member which may implement interface member. bool ignored = member == nullptr || member->astKind == AST::ASTKind::VAR_DECL || member->TestAnyAttr(Attribute::PRIVATE, Attribute::CONSTRUCTOR); if (ignored) { continue; } WorkForMembers(*member, [&funcs](auto& it) { if (it.astKind == AST::ASTKind::FUNC_DECL) { funcs.emplace(StaticCast(&it)); } }); } } std::unordered_set> GIM::GenericInstantiationManagerImpl::MergeMemberFuncs( Ty& ty, Decl& decl, const std::unordered_set>& inheritedMembers) { std::unordered_set> memberFuncs; CollectDeclMemberFuncs(decl, memberFuncs); std::unordered_set> dropped; for (auto member : memberFuncs) { std::vector> overloads; std::copy_if(inheritedMembers.begin(), inheritedMembers.end(), std::back_inserter(overloads), [member](auto it) { return it->identifier == member->identifier; }); for (auto func : overloads) { // If current member func is implementation of inherited function, drop inherited function. // Otherwise store inherited function. if (IsImplementationFunc(ty, *func, *member)) { dropped.emplace(func); } } } // Add inherited members to current collection, except dropped functions. std::copy_if(inheritedMembers.begin(), inheritedMembers.end(), std::inserter(memberFuncs, memberFuncs.end()), [&dropped](auto decl) { return dropped.count(decl) == 0; }); return memberFuncs; } std::unordered_set> GIM::GenericInstantiationManagerImpl::CollectInheritedMembers( Ty& ty, Decl& decl) { std::set, Ptr>> visited; return CollectInheritedMembersVisit(ty, decl, visited); } std::unordered_set> GIM::GenericInstantiationManagerImpl::CollectInheritedMembersVisit( Ty& ty, Decl& decl, std::set, Ptr>>& visited) { auto visitedKey = std::make_pair(&ty, &decl); if (visited.count(visitedKey) != 0) { // avoid repeatedly visiting same super-type when there are multiple paths return {}; } visited.insert(visitedKey); std::unordered_set> inheritedFuncs; // Decl is guaranteed as InheritableDecl. auto& inheritDecl = static_cast(decl); for (auto& type : inheritDecl.inheritedTypes) { Ptr baseDecl = Ty::GetDeclPtrOfTy(type->ty); if (!baseDecl) { continue; } // If no inherited function exist, directly collection members, // Otherwise merge inherited functions with current type decl's member functions. // If two parents have functions with same signature, current decl must override them, // that we can choose any of them here. inheritedFuncs = inheritedFuncs.empty() ? CollectInheritedMembersVisit(*type->ty, *baseDecl, visited) : MergeMemberFuncs(*type->ty, *baseDecl, inheritedFuncs); } return MergeMemberFuncs(ty, decl, inheritedFuncs); } namespace { std::set> GetAllRelatedExtendsByTy(TypeManager& typeManager, Ty& ty) { auto baseDecl = Ty::GetDeclPtrOfTy(&ty); if (baseDecl) { return CollectAllRelatedExtends(typeManager, *baseDecl); } else { return typeManager.GetAllExtendsByTy(ty); } } } // namespace std::unordered_set> GIM::GenericInstantiationManagerImpl::GetInheritedMemberFuncs( Ty& ty) { std::unordered_set> funcs; auto baseDecl = Ty::GetDeclPtrOfTy(&ty); if (baseDecl) { // Collect inherited function if sema's InheritedMembers not done. funcs = CollectInheritedMembers(ty, *baseDecl); } // Collect func which declared with related extends. auto extendDecls = GetAllRelatedExtendsByTy(typeManager, ty); for (auto ed : extendDecls) { if (!ed || ed->TestAttr(Attribute::GENERIC_INSTANTIATED)) { continue; } CollectDeclMemberFuncs(*ed, funcs); } return funcs; } std::unordered_set> GIM::GenericInstantiationManagerImpl::GetInheritedInterfaces( Ty& ty) { std::unordered_set> inheritableType; // Collect interfaces that are explict implemented by the 'decl'. auto collectInterfaces = [&inheritableType](const InheritableDecl& decl) { auto superITys = decl.GetSuperInterfaceTys(); std::for_each(superITys.begin(), superITys.end(), [&inheritableType](auto ty) { if (auto id = DynamicCast(Ty::GetDeclPtrOfTy(ty)); id) { inheritableType.insert(id); } }); }; std::function collectInheritedInterfaces = [&collectInheritedInterfaces, &inheritableType, &collectInterfaces](InheritableDecl& decl) { if (auto cd = DynamicCast(&decl); cd) { auto super = cd->GetSuperClassDecl(); if (super) { (void)inheritableType.emplace(super); // Collect ineriable class decl. collectInheritedInterfaces(*super); } } collectInterfaces(decl); }; // Collect inheritableType which declared with extend. std::set> extendDecls = GetAllRelatedExtendsByTy(typeManager, ty); for (auto& ed : extendDecls) { if (ed->TestAttr(Attribute::GENERIC_INSTANTIATED)) { continue; } collectInterfaces(*RawStaticCast(ed)); } // Collect inheritableType which declare in class, struct, enum (interface decl is not extendable). auto decl = Ty::GetDeclPtrOfTy(&ty); if (auto inheritDecl = DynamicCast(decl); inheritDecl) { collectInheritedInterfaces(*inheritDecl); } // Collect inheritableType which declare in super. std::unordered_set> allInterfaceTys; for (auto i : inheritableType) { if (i->ty) { allInterfaceTys.merge(typeManager.GetAllSuperTys(*i->ty)); } } allInterfaceTys.erase(&ty); for (auto iTy : allInterfaceTys) { if (auto id = DynamicCast(Ty::GetDeclPtrOfTy(iTy)); id) { inheritableType.insert(id); } } return inheritableType; } void GIM::GenericInstantiationManagerImpl::MapFuncWithDecl(Ty& ty, FuncDecl& interfaceFunc, const FuncDecl& target) { CJC_ASSERT(target.outerDecl); auto decls = GetRealIndexingMembers(target.outerDecl->GetMemberDecls(), target.outerDecl->TestAttr(Attribute::GENERIC)); auto realDecl = target.propDecl ? RawStaticCast(target.propDecl) : ⌖ for (auto it = decls.begin(); it != decls.end(); ++it) { if (*it == realDecl) { auto keyPair = std::make_pair(&ty, &interfaceFunc); auto valuePair = std::make_pair(target.outerDecl, std::distance(decls.begin(), it)); abstractFuncToDeclMap[keyPair].emplace(valuePair); break; } } } MultiTypeSubst GIM::GenericInstantiationManagerImpl::GetTypeMapping(Ptr& baseTy, Ty& interfaceTy) { MultiTypeSubst typeMapping; if (!baseTy) { return typeMapping; } typeMapping = promotion.GetPromoteTypeMapping(*baseTy, interfaceTy); if (!typeMapping.empty()) { return typeMapping; } // For a generic type which extends interface, // only sema type created in extend decl can generate typeMapping with the extended interface type. std::set> extendDecls = GetAllRelatedExtendsByTy(typeManager, *baseTy); for (auto& ed : extendDecls) { if (ed->TestAttr(Attribute::GENERIC_INSTANTIATED)) { continue; } baseTy = ed->extendedType->ty; MultiTypeSubst prRes; if (baseTy) { prRes = promotion.GetPromoteTypeMapping(*baseTy, interfaceTy); } std::for_each(prRes.begin(), prRes.end(), [&typeMapping](auto it) { typeMapping[it.first].merge(it.second); }); } return typeMapping; } /** * Check whether two functions which exist in given type's whole inheritance relationship has * implementation/override relation. * eg. interface I1 { * func foo():Int64 // f1 * } * class A { * func foo():Int64{0} // f2 * } * extend A <: I1{} * In this case, sema type of 'A', interface function 'f1' and function 'f2' are checked, * result will be 'f2' implements 'f1', function returns true. * Example also for generic declarations. */ bool GIM::GenericInstantiationManagerImpl::IsImplementationFunc( Ty& ty, const FuncDecl& interfaceFunc, const FuncDecl& fd) { CJC_ASSERT(fd.outerDecl && interfaceFunc.outerDecl); // If function's static or generic status not equal, the will not have relation of implementation. bool noRelation = interfaceFunc.TestAttr(Attribute::STATIC) != fd.TestAttr(Attribute::STATIC) || interfaceFunc.TestAttr(Attribute::GENERIC) != fd.TestAttr(Attribute::GENERIC); if (noRelation) { return false; } if (interfaceFunc.outerDecl->astKind == fd.outerDecl->astKind) { // If both functions are declared in same kind of type decls, directly return if 'fd' is abstract. if (fd.TestAttr(Attribute::ABSTRACT)) { return false; } } else { // If functions are declared in different kind of type decls, directly return if 'fd' is in interface. if (fd.outerDecl->astKind == ASTKind::INTERFACE_DECL) { return false; } } auto baseTy = Ptr(&ty); auto structDecl = RawStaticCast(fd.outerDecl); // Update base ty to extendDecl's ty when the extended type is current given 'ty'. // For primitive and builtin types, the declPtr is always nullptr that always update baseTy to extendDecl ty. // For user defined types only update baseTy when declPtr is same. bool shouldUpdateImplementTy = true; if (auto ed = DynamicCast(structDecl); ed && Ty::GetDeclPtrOfTy(ed->extendedType->ty) == Ty::GetDeclPtrOfTy(baseTy)) { baseTy = ed->extendedType->ty; shouldUpdateImplementTy = false; } // 1. Substitute interface func's type to baseTy. NOTE: May update baseTy's value. auto interfaceTy = interfaceFunc.outerDecl->ty; auto typeMappings = GetTypeMapping(baseTy, *interfaceTy); for (auto& type : structDecl->inheritedTypes) { // Eg: generate mapping for extend Int16 <: Number and interface Number. if (type && type->ty && typeManager.IsSubtype(Ty::GetGenericTyOfInsTy(*type->ty), interfaceTy)) { typeManager.GenerateGenericMapping(typeMappings, *type->ty); break; } } auto iFuncTy = interfaceFunc.ty; // 1.5 Mapping function generic from 'interfaceFunc' to 'fd'. // Previously checked 'interfaceFunc' and 'fd' have same generic status. if (fd.TestAttr(Attribute::GENERIC)) { TypeSubst typeMapping = typeManager.GenerateGenericMappingFromGeneric(interfaceFunc, fd); iFuncTy = typeManager.GetInstantiatedTy(iFuncTy, typeMapping); } // For 'interface I { func foo(a: T) {} }; class C <: I & I', // function 'foo' may have multiple instantiation type. auto interfaceFuncTys = typeManager.GetInstantiatedTys(iFuncTy, typeMappings); // 2. Substitute implemented func's type to baseTy. auto implementedFuncTy = RawStaticCast(fd.ty); if (shouldUpdateImplementTy) { auto structTy = structDecl->ty; if (baseTy && structTy) { typeMappings = promotion.GetPromoteTypeMapping(*baseTy, *structTy); } else { typeMappings = {}; } implementedFuncTy = RawStaticCast(typeManager.GetBestInstantiatedTy(fd.ty, typeMappings)); } // 3. compare two func's signature. structure declaration mapping relation if signatures are same. // when function have multiple instantiated types, only need one of them passed. // Actual type will be decided in rearrange stage. return std::any_of(interfaceFuncTys.begin(), interfaceFuncTys.end(), [this, implementedFuncTy](auto ty) { return implementedFuncTy && ty && typeManager.IsFuncParameterTypesIdentical(*implementedFuncTy, *RawStaticCast(ty)); }); } void GIM::GenericInstantiationManagerImpl::BuildAbstractFuncMapHelper(Ty& ty) { std::unordered_set> funcs = GetInheritedMemberFuncs(ty); std::unordered_set> interfaces = GetInheritedInterfaces(ty); auto dealWithFunDecl = [this, &funcs, &ty](FuncDecl& realMember) { // Currently not adaptable for property decl. for (auto& fd : funcs) { if (fd->identifier != realMember.identifier) { continue; } if (IsImplementationFunc(ty, realMember, *fd)) { MapFuncWithDecl(ty, realMember, *fd); } } }; for (auto& id : interfaces) { for (auto realMember : GetInheritedMemberFuncs(*id->ty)) { dealWithFunDecl(*realMember); } } } /** * Build abstract function map for all type which inherited interface. * This map helps target rearrange of interface call in generic function which has interface upper bound. * eg. func test(ins: T) where T <: I { * ins.interfaceFunc * } * target of ins.interfaceFunc, must be rearranged to ins.instanceFunc if: * 1. interfaceFunc is a static interface function. * 2. type 'T' extends interface 'I' */ void GIM::GenericInstantiationManagerImpl::BuildAbstractFuncMap() { Utils::ProfileRecorder::Start("BuildAbstractFuncMap", "primitive types"); abstractFuncToDeclMap.clear(); // For primitive types. for (auto& it : typeManager.builtinTyToExtendMap) { BuildAbstractFuncMapHelper(*it.first); } Utils::ProfileRecorder::Stop("BuildAbstractFuncMap", "primitive types"); Utils::ProfileRecorder::Start("BuildAbstractFuncMap", "class/struct/enum/interface type"); // For all class/struct/enum/interface type. std::unordered_set> inheritableDecls; std::unordered_set> genericDecls; auto collectDecls = [&inheritableDecls, &genericDecls](const OwnedPtr& decl) { if (!decl || !GetDeclTy(*decl) || !decl->IsNominalDecl()) { return; } if (decl->astKind != ASTKind::EXTEND_DECL) { inheritableDecls.emplace(decl.get()); } if (decl->generic) { genericDecls.emplace(decl.get()); } }; auto pkgs = importManager.GetAllImportedPackages(); for (auto& pkg : pkgs) { CJC_NULLPTR_CHECK(pkg->srcPackage); IterateToplevelDecls(*pkg->srcPackage, collectDecls); } Utils::ProfileRecorder::Stop("BuildAbstractFuncMap", "class/struct/enum/interface type"); Utils::ProfileRecorder::Start("BuildAbstractFuncMap", "inheritableDecls"); for (auto& decl : inheritableDecls) { BuildAbstractFuncMapHelper(*decl->ty); } Utils::ProfileRecorder::Stop("BuildAbstractFuncMap", "inheritableDecls"); Utils::ProfileRecorder recorder("BuildAbstractFuncMap", " Build index"); // Build index for members of generic decl. for (auto decl : genericDecls) { size_t i = 0; for (auto& member : decl->GetMemberDecls()) { // NOTE: Ignore primaryCtorDecl from indexing map. // The primaryCtorDecl will be removed from instantiated version. if (member->astKind == ASTKind::PRIMARY_CTOR_DECL) { continue; } membersIndexMap.emplace(member.get(), i++); } if (decl->astKind != ASTKind::ENUM_DECL) { continue; } auto ed = RawStaticCast(decl); for (size_t idx = 0; idx < ed->constructors.size(); ++idx) { membersIndexMap.emplace(ed->constructors[idx].get(), idx); } } } static void AppendGenericFuncMap( const AST::FuncDecl& genericDecl, const std::unordered_set>& insFuncDecls, Generic2InsMap& result) { result.emplace(&genericDecl, insFuncDecls); for (size_t i = 0; i < genericDecl.funcBody->paramLists[0]->params.size(); ++i) { auto& genericParam = genericDecl.funcBody->paramLists[0]->params[i]; if (genericParam->desugarDecl == nullptr) { continue; } std::unordered_set> insParamDecls; for (auto decl : insFuncDecls) { auto& insParam = StaticCast(decl)->funcBody->paramLists[0]->params[i]; CJC_NULLPTR_CHECK(insParam->desugarDecl); insParamDecls.emplace(insParam->desugarDecl.get()); } result.emplace(genericParam->desugarDecl.get(), insParamDecls); } } static void AppendGenericPropMap( const AST::PropDecl& propDecl, std::unordered_set>& insPropDecls, Generic2InsMap& result) { for (size_t i = 0; i < propDecl.getters.size(); ++i) { std::unordered_set> insGetterDecls; auto genericGetter = propDecl.getters[i].get(); for (auto insProp : insPropDecls) { insGetterDecls.emplace(StaticCast(insProp)->getters[i].get()); } AppendGenericFuncMap(*genericGetter, insGetterDecls, result); } for (size_t i = 0; i < propDecl.setters.size(); ++i) { std::unordered_set> insSetterDecls; auto genericSetter = propDecl.setters[i].get(); for (auto insProp : insPropDecls) { insSetterDecls.emplace(StaticCast(insProp)->setters[i].get()); } AppendGenericFuncMap(*genericSetter, insSetterDecls, result); } } void GIM::GenericInstantiationManagerImpl::AppendGenericMemberMap(const AST::Decl& genericDecl, const std::unordered_set>& insNominalDecls, Generic2InsMap& result) const { result.emplace(&genericDecl, insNominalDecls); for (auto& genericMember : genericDecl.GetMemberDecls()) { if (genericMember->astKind != AST::ASTKind::FUNC_DECL && genericMember->astKind != AST::ASTKind::PROP_DECL) { continue; } auto insMemberDecls = PartialInstantiation::GetInstantiatedDecl(*genericMember); if (insMemberDecls.empty()) { continue; } if (auto genericMemberFunc = DynamicCast(genericMember.get()); genericMemberFunc) { AppendGenericFuncMap(*genericMemberFunc, insMemberDecls, result); } else { AppendGenericPropMap(*StaticCast(genericMember.get()), insMemberDecls, result); } } } Generic2InsMap GIM::GenericInstantiationManagerImpl::GetAllGenericToInsDecls() const { Generic2InsMap result; for (auto& mapIt : instantiatedDeclsMap) { if (mapIt.first->IsNominalDecl()) { AppendGenericMemberMap(*mapIt.first, mapIt.second, result); } else if (mapIt.first->astKind == AST::ASTKind::FUNC_DECL) { AppendGenericFuncMap(*StaticCast(mapIt.first), mapIt.second, result); } } return result; } cangjie_compiler-1.0.7/src/Sema/GenericInstantiation/GenericInstantiationManager.cpp000066400000000000000000000027541510705540100307340ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * Implements GenericInstantiationManager API calls. * Real implementation functions in 'GenericInstantiationManagerImpl'. */ #include "GenericInstantiationManagerImpl.h" #include "cangjie/Frontend/CompilerInstance.h" using namespace Cangjie; GenericInstantiationManager::GenericInstantiationManager(CompilerInstance& ci) { impl = std::make_unique(ci); } GenericInstantiationManager::~GenericInstantiationManager() { } void GenericInstantiationManager::GenericInstantiatePackage(AST::Package& pkg) const { impl->GenericInstantiatePackage(pkg); } Ptr GenericInstantiationManager::GetInstantiatedDeclWithGenericInfo( const GenericInfo& genericInfo, AST::Package& pkg) const { return impl->GetInstantiatedDeclWithGenericInfo(genericInfo, pkg); } std::unordered_set> GenericInstantiationManager::GetInstantiatedDecls( const AST::Decl& genericDecl) const { return impl->GetInstantiatedDecls(genericDecl); } void GenericInstantiationManager::ResetGenericInstantiationStage() const { impl->ResetGenericInstantiationStage(); } Generic2InsMap GenericInstantiationManager::GetAllGenericToInsDecls() const { return impl->GetAllGenericToInsDecls(); }cangjie_compiler-1.0.7/src/Sema/GenericInstantiation/GenericInstantiationManagerImpl.cpp000066400000000000000000002042471510705540100315570ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * Implements GenericInstantiationManager related methods. Contains: * 1. Instantiate all referenced generic decls in non-generic/instantiated decls. * - Copy generic decl and substitute generic types with instantiated types. * - Imported inline functions' content should only be walked if the function is used in source package. * 2. Rearrange generic references to instantiated decls. * - Update reference decl pointer from original generic ast to instantiated decl's pointer. * eg: func test(a: T) { // xxx} * var a = test * After typecheck, the RefExpr ('test')'s member ref.target was pointer to generic version of 'test'. * the rearrange is to update 'refExpr.ref.target' from generic version to instantiated decl created in step 1. * - Update 'typePattern''s compile-time matching status with instantiated types. * 3. Some 'Ty' has 'decl' and 'declPtr' members. * We need to update type's corresponding 'decl' memebr to newly instantiated decl. * For cjnative backend, clear imported instantiated decl which has same type with decls instantiate in current * package. * 4. Remove unused instantiated decls when compiling toy executable with 'O2' option. * 5. Validate all used nodes have non-generic sema type in DEBUG version. */ #include "GenericInstantiationManagerImpl.h" #include "BuiltInOperatorUtil.h" #include "ImplUtils.h" #include "InstantiatedExtendRecorder.h" #include "TypeCheckUtil.h" #include "PartialInstantiation.h" #include "cangjie/AST/ASTTypeValidator.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/RecoverDesugar.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Sema/Desugar.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Utils/ProfileRecorder.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; using GIM = GenericInstantiationManager; GIM::GenericInstantiationManagerImpl::GenericInstantiationManagerImpl(CompilerInstance& ci) : diag(ci.diag), importManager(ci.importManager), typeManager(*ci.typeManager), testManager(ci.testManager), promotion(*ci.typeManager), instantiationWalkerID(AST::Walker::GetNextWalkerID()), rearrangeWalkerID(AST::Walker::GetNextWalkerID()), backend(ci.invocation.globalOptions.backend) { instantiator = [this](auto node) { return CheckNodeInstantiation(*node); }; rearranger = [this](auto node) { return RearrangeReferencePtr(*node); }; contextReset = [this](auto node) { if (auto decl = DynamicCast(node); decl && !structContext.empty()) { // Pop context if current is structure declaration or generic decl inside generic structure declaration. if (NeedSwitchContext(*decl) && decl == structContext.back()) { structContext.pop_back(); } } return VisitAction::WALK_CHILDREN; }; SetOptLevel(ci.invocation.globalOptions); } namespace { std::unordered_map, std::vector> g_skippedMemberOffsets = {}; void UpdateInstantiatedDeclsLinkage(const Package& pkg) { // All instantiated decls should be marked as internal for cjnative backend. // For other backend, only mark extend as internal since extend will be instantiated in used package. for (auto& decl : pkg.genericInstantiatedDecls) { Walker(decl.get(), [](auto node) { if (auto fd = DynamicCast(node); fd) { fd->linkage = Linkage::INTERNAL; } return VisitAction::WALK_CHILDREN; }).Walk(); } } inline void UnsetBoxStatus(Package& pkg) { Walker(&pkg, [](auto node) { node->DisableAttr(Attribute::NEED_AUTO_BOX); return VisitAction::WALK_CHILDREN; }).Walk(); } void ClearUnusedSrcImportedDecls(Package& pkg, const std::function&)>& checkUnvisited) { for (auto it = pkg.srcImportedNonGenericDecls.begin(); it != pkg.srcImportedNonGenericDecls.end();) { if (checkUnvisited(*it)) { // Unset 'SRC_IMPORTED' for unused imported func decls. (*it)->DisableAttr(Attribute::SRC_IMPORTED); it = pkg.srcImportedNonGenericDecls.erase(it); } else { ++it; } } Utils::EraseIf(pkg.inlineFuncDecls, checkUnvisited); } void ClearInstTysIsNeeded(Node& node) { if (Utils::NotIn(node.astKind, {ASTKind::REF_EXPR, ASTKind::MEMBER_ACCESS, ASTKind::CALL_EXPR})) { return; } if (auto ce = DynamicCast(&node); ce && ce->desugarExpr && !ce->desugarExpr->ty->HasGeneric() && Is(ce->baseFunc.get())) { StaticCast(ce->baseFunc.get())->instTys.clear(); return; } if (auto re = DynamicCast(&node)) { if (auto target = re->ref.target; target && !target->ty->HasGeneric() && !target->TestAnyAttr(Attribute::INTRINSIC, Attribute::GENERIC)) { re->instTys.clear(); if (target->astKind != ASTKind::TYPE_ALIAS_DECL) { re->ty = target->ty; } } return; } if (auto ma = DynamicCast(&node); ma && ma->target && !ma->target->ty->HasGeneric() && !ma->target->TestAnyAttr(Attribute::INTRINSIC, Attribute::GENERIC)) { ma->instTys.clear(); if (ma->target->astKind != ASTKind::TYPE_ALIAS_DECL && !HasJavaAttr(*ma->target)) { ma->ty = ma->target->ty; } } } Ptr GetOriginalTy(Ty& ty, const TypeSubst& g2gTyMap, TypeManager& typeManager) { if (!ty.IsGeneric() && !ty.HasGeneric()) { return &ty; } if (auto gTy = DynamicCast(&ty)) { if (auto found = g2gTyMap.find(gTy); found != g2gTyMap.end()) { return found->second; } } if (!ty.IsFunc()) { std::vector> tyArgs; tyArgs.reserve(ty.typeArgs.size()); for (auto tyArg : std::as_const(ty.typeArgs)) { tyArgs.emplace_back(GetOriginalTy(*tyArg, g2gTyMap, typeManager)); } if (ty.IsTuple()) { return typeManager.GetTupleTy(tyArgs); } auto decl = Ty::GetDeclPtrOfTy(&ty); if (!decl) { return &ty; } if (ty.IsClass()) { return Is(ty) ? typeManager.GetClassThisTy(*StaticCast(decl), tyArgs) : typeManager.GetClassTy(*StaticCast(decl), tyArgs); } if (ty.IsInterface()) { return typeManager.GetInterfaceTy(*StaticCast(decl), tyArgs); } if (ty.IsEnum()) { return typeManager.GetEnumTy(*StaticCast(decl), tyArgs); } if (ty.IsStruct()) { return typeManager.GetStructTy(*StaticCast(decl), tyArgs); } return &ty; } auto& funcTy = StaticCast(ty); std::vector> paramTys; paramTys.reserve(funcTy.paramTys.size()); for (auto paramTy : funcTy.paramTys) { paramTys.emplace_back(GetOriginalTy(*paramTy, g2gTyMap, typeManager)); } return typeManager.GetFunctionTy(paramTys, GetOriginalTy(*funcTy.retTy, g2gTyMap, typeManager)); } inline ReversedTypeSubst GetReversedTypeSubst(const TypeSubst& typeMapping) { ReversedTypeSubst rts; for (auto [k, v] : typeMapping) { rts.emplace(v, k); } return rts; } inline bool IsGenericFuncWithDefaultParam(const Decl& decl) { if (auto fd = DynamicCast(&decl); fd && fd->GetGeneric() && fd->funcBody && !fd->funcBody->paramLists.empty()) { for (const auto& param : fd->funcBody->paramLists[0]->params) { if (param->desugarDecl) { return true; } } } return false; } void BuildGenericsTyMap(const FuncDecl& fd, TypeSubst& g2gMap) { auto generic = fd.GetGeneric(); CJC_NULLPTR_CHECK(generic); const auto& typeParams = generic->typeParameters; for (const auto& param : fd.funcBody->paramLists[0]->params) { if (const auto& dd = param->desugarDecl) { auto ddGeneric = dd->GetGeneric(); CJC_ASSERT(ddGeneric && ddGeneric->typeParameters.size() == typeParams.size()); const auto& ddTypeParams = ddGeneric->typeParameters; for (size_t i = 0; i < ddTypeParams.size(); ++i) { CJC_ASSERT(Ty::IsTyCorrect(ddTypeParams[i]->ty) && Ty::IsTyCorrect(typeParams[i]->ty)); g2gMap.emplace(StaticCast(ddTypeParams[i]->ty), typeParams[i]->ty); } } } } size_t CountSkippedMembersBefore(const Decl& decl, size_t offset) { auto found = g_skippedMemberOffsets.find(&decl); if (found == g_skippedMemberOffsets.end()) { std::vector offsets; auto members = GetRealIndexingMembers(decl.GetMemberDecls(), decl.TestAttr(Attribute::GENERIC)); for (auto it = members.begin(); it != members.end(); ++it) { size_t off = static_cast(std::distance(members.begin(), it)); if (IsVirtualMember(**it) || IsStaticVar(**it)) { offsets.emplace_back(off); } } found = g_skippedMemberOffsets.emplace(&decl, std::move(offsets)).first; } size_t count = 0; for (size_t off : found->second) { if (off < offset) { count++; } else if (off == offset) { return std::numeric_limits::max(); } else { break; } } return count; } Ptr GetMemberByOffset(const Decl& decl, size_t offset) { auto members = GetRealIndexingMembers(decl.GetMemberDecls(), decl.TestAttr(Attribute::GENERIC)); auto implMemberIt = members.begin(); if (!decl.TestAttr(Attribute::GENERIC_INSTANTIATED)) { CJC_ASSERT(offset < members.size()); std::advance(implMemberIt, offset); } else { CJC_NULLPTR_CHECK(decl.genericDecl); size_t count = CountSkippedMembersBefore(*decl.genericDecl, offset); if (count == std::numeric_limits::max()) { members = GetRealIndexingMembers( decl.genericDecl->GetMemberDecls(), decl.genericDecl->TestAttr(Attribute::GENERIC)); implMemberIt = members.begin(); CJC_ASSERT(offset < members.size()); std::advance(implMemberIt, offset); return implMemberIt->get(); } CJC_ASSERT(count <= offset && offset - count < members.size()); std::advance(implMemberIt, offset - count); } return implMemberIt->get(); } } // namespace void GIM::GenericInstantiationManagerImpl::GenericInstantiatePackage(Package& pkg) { this->curPkg = &pkg; Utils::ProfileRecorder::Start("GenericInstantiatePackage", "instantiate"); // Collect extend decls by usage. RecordExtend(*curPkg); if (curPkg->TestAttr(Attribute::INCRE_COMPILE)) { InstantiateForIncrementalPackage(); } else { // When `GenericInstantiatePackage` is invoked for multiple times, ensure that global data is clean. PartialInstantiation::ResetGlobalMap(); // Only walk non-generic or instantiated decl's to perform instantiation. Walker(curPkg, instantiationWalkerID, instantiator, contextReset).Walk(); } Utils::ProfileRecorder::Stop("GenericInstantiatePackage", "instantiate"); Utils::ProfileRecorder::Start("GenericInstantiatePackage", "testManager"); testManager->PreparePackageForTestIfNeeded(*curPkg); Utils::ProfileRecorder::Stop("GenericInstantiatePackage", "testManager"); // Do not perform rearrange, validation and deletion if errors generated. if (diag.GetErrorCount() != 0) { return; } Utils::ProfileRecorder::Start("GenericInstantiatePackage", "rearrange"); // Erase un-visited src imported functions during instantiation step. ClearUnusedSrcImportedDecls( *curPkg, [this](const Ptr& it) { return !it->IsConst() && usedSrcImportedDecls.count(it) == 0; }); // After the instantiation finished and there are instantiatedDecls generated in current package, // rearrange the ptr of outer references' target to the instantiated decl. // NOTE: Walker will also walk 'genericInstantiatedDecls' in package node. Walker(curPkg, rearrangeWalkerID, rearranger, contextReset).Walk(); RecoverDesugarForBuiltIn(); Utils::ProfileRecorder::Stop("GenericInstantiatePackage", "rearrange"); // Do not perform validation and deletion if errors generated. if (diag.GetErrorCount() != 0) { return; } Utils::ProfileRecorder recorder("GenericInstantiatePackage", "cleanup"); UpdateInstantiatedExtendMap(); UpdateInstantiatedDeclsLinkage(pkg); ClearImportedUnusedInstantiatedDecls(); ValidateUsedNodes(diag, pkg); UnsetBoxStatus(pkg); } void GIM::GenericInstantiationManagerImpl::RecordExtend(AST::Node& node) { InstantiatedExtendRecorder(*this, typeManager)(node); } void GIM::GenericInstantiationManagerImpl::InstantiateForIncrementalPackage() { // Instantiate decls which is marked as to be compiled and collect unchanged decls. std::vector> unchanged; IterateToplevelDecls(*curPkg, [this, &unchanged](auto& decl) { if (decl->TestAttr(Attribute::GENERIC)) { return; } if (decl->toBeCompiled) { Walker(decl.get(), instantiationWalkerID, instantiator, contextReset).Walk(); return; } unchanged.emplace_back(decl.get()); if (!decl->IsNominalDecl()) { return; } // Enum constructor is part of whole enum decl, so it will not be separately marked as 'toBeCompiled'. // Only need using 'GetMemberDecls' here. structContext.push_back(decl.get()); for (auto& it : decl->GetMemberDecls()) { if (it->TestAttr(Attribute::GENERIC)) { continue; } WorkForMembers(*it, [this, &unchanged](auto& member) { if (member.toBeCompiled) { Walker(&member, instantiationWalkerID, instantiator, contextReset).Walk(); } else { unchanged.emplace_back(&member); } }); } structContext.pop_back(); }); // Set 'needCompile' to mark instantiations as not to be compiled, // then walk and instantiate toplevel decls for full signature. needCompile = false; for (auto it : unchanged) { if (it->outerDecl) { structContext.push_back(it->outerDecl); Walker(it, instantiationWalkerID, instantiator, contextReset).Walk(); structContext.pop_back(); } else { Walker(it, instantiationWalkerID, instantiator, contextReset).Walk(); } } needCompile = true; } void GIM::GenericInstantiationManagerImpl::UpdateInstantiatedExtendMap() { // Update instantiated extend decl map after instantiation and re-arrange finished. for (auto& it : curPkg->genericInstantiatedDecls) { if (it->astKind != ASTKind::EXTEND_DECL) { continue; } if (it->ty->IsBuiltin()) { typeManager.UpdateBuiltInTyExtendDecl(*it->ty, static_cast(*it)); } else if (auto extendedDecl = DynamicCast(Ty::GetDeclOfTy(it->ty)); extendedDecl) { typeManager.declToExtendMap[extendedDecl].emplace(RawStaticCast(it.get())); } } } void GIM::GenericInstantiationManagerImpl::ClearImportedUnusedInstantiatedDecls() { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::unordered_set> instantiatedTys; std::unordered_set> removedDecls; for (auto& decl : curPkg->genericInstantiatedDecls) { if (decl->IsNominalDecl()) { // Update ty's instantiated decl to make current package's decl as the first choice of type. RestoreInstantiatedDeclTy(*decl); instantiatedTys.emplace(decl->ty); } } // Record erasable decls which have same ty with instantiated decl in current package. auto recordEraseNominal = [&removedDecls, &instantiatedTys](auto& it) { if (instantiatedTys.find(it->ty) != instantiatedTys.end()) { removedDecls.emplace(it.get()); } }; // Instantiated decls in imported package will not be referenced by any other place. // The only 'ty->decl' references are already been replaced in previous step. for (const auto& it : importManager.GetAllImportedPackages()) { bool ignore = it->srcPackage == this->curPkg || !it->srcPackage->TestAttr(Attribute::IMPORTED); if (ignore) { continue; } // Collect and record all erasable decls which have same instantiation types. auto importPkg = it->srcPackage; std::for_each( importPkg->genericInstantiatedDecls.begin(), importPkg->genericInstantiatedDecls.end(), recordEraseNominal); // Collect all decls which are inside removed decls. std::unordered_set> removedOtherDecls; for (auto& decl : importPkg->genericInstantiatedDecls) { Ptr outermostDecl = decl->outerDecl; while (outermostDecl != nullptr && outermostDecl->outerDecl != nullptr) { outermostDecl = outermostDecl->outerDecl; } if (removedDecls.find(outermostDecl) != removedDecls.end()) { removedOtherDecls.emplace(decl.get()); } } // Remove corresponding extend type map. std::for_each(removedDecls.begin(), removedDecls.end(), [this](auto it) { if (it->astKind == ASTKind::EXTEND_DECL) { typeManager.RemoveExtendFromMap(*StaticAs(it)); } }); Utils::EraseIf(importPkg->srcImportedNonGenericDecls, [&removedDecls](auto it) { return it && removedDecls.find(it->outerDecl) != removedDecls.end(); }); // Remove all struct decls. Utils::EraseIf(importPkg->genericInstantiatedDecls, [&removedDecls](auto& it) { return removedDecls.find(it.get()) != removedDecls.end(); }); // Remove all inner decls. Utils::EraseIf(importPkg->genericInstantiatedDecls, [&removedOtherDecls](auto& it) { return removedOtherDecls.find(it.get()) != removedOtherDecls.end(); }); removedDecls.clear(); // Reset collected removed decls for next pacakge. } // Since the 'ty->decl' may be changed after above process, update extends again. UpdateInstantiatedExtendMap(); #endif } /** * Use Types::Type to establish the map which binds the relationships between the generic parameter type to * semantic funcType parameters. */ GenericInfo GIM::GenericInstantiationManagerImpl::ConstructGenericInfo( Decl& decl, const std::vector>& instTys) const { CJC_ASSERT(decl.TestAttr(Attribute::GENERIC)); TypeSubst gTyToTyMap = GenerateTypeMapping(decl, instTys); CJC_ASSERT(!gTyToTyMap.empty()); GenericInfo genericInfo(&decl, gTyToTyMap); return genericInfo; } void GIM::GenericInstantiationManagerImpl::PerformTyInstantiationDuringClone( const Node& genericNode, Node& clonedNode, const GenericInfo& info, const TypeSubst& g2gTyMap) { // Instantiate the generic ty to instantiated type. if (Ty::IsTyCorrect(genericNode.ty)) { clonedNode.ty = typeManager.GetInstantiatedTy(GetOriginalTy(*genericNode.ty, g2gTyMap, typeManager), info.gTyToTyMap); } if (auto ref = DynamicCast(&clonedNode); ref) { if (ref->matchedParentTy) { ref->matchedParentTy = typeManager.GetInstantiatedTy( GetOriginalTy(*ref->matchedParentTy, g2gTyMap, typeManager), info.gTyToTyMap); } for (auto& instTy : ref->instTys) { instTy = typeManager.GetInstantiatedTy(GetOriginalTy(*instTy, g2gTyMap, typeManager), info.gTyToTyMap); } } } void GIM::GenericInstantiationManagerImpl::PerformUpdateAttrDuringClone(Node& genericNode, Node& clonedNode) const { auto clonedDecl = As(&clonedNode); if (clonedDecl == nullptr) { return; } auto genericDecl = RawStaticCast(&genericNode); CJC_ASSERT(curPkg != nullptr); clonedDecl->moduleName = Utils::GetRootPackageName(curPkg->fullPackageName); clonedDecl->fullPackageName = curPkg->fullPackageName; clonedDecl->curFile = curPkg->files[0]; clonedDecl->toBeCompiled = needCompile || genericDecl->toBeCompiled; // Mark for incremental compilation. clonedDecl->DisableAttr(Attribute::IMPORTED); // The generic static function in instantiated structDecl has attributes GENERIC and GENERIC_INSTANTIATED. Ptr declGeneric = clonedDecl->GetGeneric(); if (declGeneric == nullptr) { return; } bool result = false; for (auto& typeParameter : declGeneric->typeParameters) { result = result || typeParameter->ty->IsGeneric(); } if (result && clonedNode.TestAttr(Attribute::GENERIC)) { if (clonedDecl->outerDecl && clonedDecl->outerDecl->IsFunc()) { return; // Nested generic function do not record original generic decl. } clonedNode.EnableAttr(Attribute::GENERIC_INSTANTIATED); // This may be accessed by AST2CHIR. clonedDecl->genericDecl = genericDecl; } else if (!result && clonedNode.TestAttr(Attribute::GENERIC)) { clonedNode.DisableAttr(Attribute::GENERIC); clonedNode.EnableAttr(Attribute::GENERIC_INSTANTIATED); } } Ptr GIM::GenericInstantiationManagerImpl::GetInstantiatedDeclWithGenericInfo(const GenericInfo& genericInfo) { auto genericDecl = genericInfo.decl; Ptr instantiatedDecl = FindInCache(genericInfo); // Check if the generic function is already instantiated. if (instantiatedDecl) { // 'toBeCompiled' need be updated, since unchanged cache also may be created during incremental stage. instantiatedDecl->toBeCompiled = instantiatedDecl->toBeCompiled || needCompile; return instantiatedDecl; } TypeSubst g2gTyMap = {}; if (IsGenericFuncWithDefaultParam(*genericDecl)) { BuildGenericsTyMap(*StaticCast(genericDecl), g2gTyMap); } // Clone a generic instantiated funcDecl by changing its generic types to instantiated types. // This is a callback function which perform generic type instantiation during the clone period. VisitFunc instantiateType = [this, &genericInfo, &g2gTyMap](Node& genericNode, Node& clonedNode) { PerformTyInstantiationDuringClone(genericNode, clonedNode, genericInfo, g2gTyMap); PerformUpdateAttrDuringClone(genericNode, clonedNode); }; auto clonedDecl = PartialInstantiation::Instantiate(genericDecl, instantiateType); if (clonedDecl == nullptr) { return nullptr; } if (auto fd = DynamicCast(clonedDecl.get()); fd && fd->funcBody && !fd->funcBody->paramLists.empty()) { for (auto& param : std::as_const(fd->funcBody->paramLists[0]->params)) { if (param->desugarDecl) { param->desugarDecl->DisableAttr(Attribute::GENERIC); } } } clonedDecl->EnableAttr(Attribute::GENERIC_INSTANTIATED); clonedDecl->EnableAttr(Attribute::NO_REFLECT_INFO); clonedDecl->DisableAttr(Attribute::GENERIC, Attribute::IMPORTED); // NOTE: Remove primary ctor decl from cloned decl to get stable member index for rearrangement. // This is related to the usage of 'membersIndexMap'. auto& members = clonedDecl->GetMemberDecls(); Utils::EraseIf(members, [](auto& it) { return it->astKind == ASTKind::PRIMARY_CTOR_DECL; }); for (auto& ptr : members) { ptr->EnableAttr(Attribute::NO_REFLECT_INFO); if (ptr->astKind == ASTKind::PROP_DECL) { for (auto& g : StaticCast(ptr.get())->getters) { g->EnableAttr(Attribute::NO_REFLECT_INFO); } for (auto& s : StaticCast(ptr.get())->setters) { s->EnableAttr(Attribute::NO_REFLECT_INFO); } } } instantiatedDecl = clonedDecl.get(); CJC_ASSERT(curPkg != nullptr); (void)curPkg->genericInstantiatedDecls.emplace_back(std::move(clonedDecl)); instantiatedDecl->genericDecl = genericDecl; // Record this instantiated result to maps. declInstantiationByTypeMap.emplace(genericInfo, instantiatedDecl); instantiatedDeclsMap[genericDecl].insert(instantiatedDecl); if (curPkg->TestAttr(Attribute::TOOL_ADD)) { return instantiatedDecl; // When the package is create by cjogen, the sub nodes do not need to be walked. } // Collect extend decls by usage. RecordExtend(*instantiatedDecl); Walker(instantiatedDecl, instantiationWalkerID, instantiator, contextReset).Walk(); return instantiatedDecl; } Ptr GIM::GenericInstantiationManagerImpl::GetInstantiatedDeclWithGenericInfo( const GenericInfo& genericInfo, Package& pkg) { curPkg = &pkg; auto instantiatedDecl = GetInstantiatedDeclWithGenericInfo(genericInfo); Walker(instantiatedDecl, rearrangeWalkerID, rearranger, contextReset).Walk(); return instantiatedDecl; } Ptr GIM::GenericInstantiationManagerImpl::FindInCache(const GenericInfo& info) { auto found = declInstantiationByTypeMap.equal_range(info); // Only using instantiated decl in current package. // If the first and the second of the range is not equal, it means the target GI can be found in cache. for (auto it = found.first; it != found.second; ++it) { auto instantiatedDecl = it->second; CJC_ASSERT(instantiatedDecl != nullptr); if (instantiatedDecl->fullPackageName != curPkg->fullPackageName) { continue; } return instantiatedDecl; } return nullptr; } void GIM::GenericInstantiationManagerImpl::WalkNonGenericExtendedType() { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto extends = typeManager.GetBoxUsedExtends(); for (auto extend : extends) { // Should have same ignore condition with 'CollectExtendedInterfaceHelper'. // UG fix here do not box decls any more. bool ignore = extend->TestAttr(Attribute::GENERIC) || extend->fullPackageName != curPkg->fullPackageName; if (ignore) { continue; } for (auto& type : extend->inheritedTypes) { Walker(type.get(), instantiationWalkerID, instantiator, contextReset).Walk(); } } auto decls = typeManager.GetBoxedNonGenericDecls(); for (auto id : decls) { for (auto& type : id->inheritedTypes) { Walker(type.get(), instantiationWalkerID, instantiator, contextReset).Walk(); } } #endif } void GIM::GenericInstantiationManagerImpl::InstantiateGenericDeclWithInstTys( Decl& decl, const std::vector>& instTys) { // Don't instantiate for type alias decl and builtin decl. if (decl.astKind == ASTKind::TYPE_ALIAS_DECL || decl.IsBuiltIn()) { return; } // Target of self call may be rearrange to wrong decl during instantiation. eg: // func foo () { // foo() // } auto generalDecl = GetGeneralDecl(decl); auto genericTy = GetDeclTy(*generalDecl); if (!Ty::IsTyCorrect(genericTy)) { InternalError("generic instantiation failed"); return; } if (!generalDecl->GetGeneric() || !RequireInstantiation(*generalDecl)) { if (!decl.TestAnyAttr(Attribute::IMPORTED, Attribute::GENERATED_TO_MOCK)) { return; // If decl is in current package and not mocked, it must be walked by instantiator. } bool canSkip = curPkg->TestAttr(Attribute::INCRE_COMPILE) && !decl.toBeCompiled && needCompile; // If 'canSkip' is true, all decls used by current decl must be unchanged, // so the related instantiations does not need be recompiled. IncrementalContext changingContext(needCompile, canSkip); // If the given decl is not generic decl, checking for following cases: // 1. If the 'generalDecl' is existed in 'srcImportedNonGenericDecls', also checking the instantiation for it // (including default param function). // 2. Otherwise only walk class/interface decls' inherited types. if (auto fd = As(generalDecl); fd && fd->funcBody && !fd->funcBody->paramLists.empty()) { for (auto& param : fd->funcBody->paramLists[0]->params) { Ptr func = param->desugarDecl.get(); if (func && func->TestAttr(Attribute::SRC_IMPORTED)) { usedSrcImportedDecls.emplace(func); Walker(func, instantiationWalkerID, instantiator, contextReset).Walk(); } } } // Used imported source decl need to be checked for instantiation // since this kind of decl will be re-generated in current package. if (generalDecl->TestAttr(Attribute::SRC_IMPORTED)) { usedSrcImportedDecls.emplace(generalDecl); // Walk inside decl to instantiate all used generics. Walker(&decl, instantiationWalkerID, instantiator, contextReset).Walk(); } return; } // 2. If the type arguments is empty, can not trigger generic instantiation. // If type arguments still has generic parameter, need to wait for the instantiation of the generic parameters. if (instTys.empty() || Ty::ExistGeneric(instTys)) { return; } // Create struct GenericInfo contains the information of the raw generic function. // Do instantiation. (void)GetInstantiatedDeclWithGenericInfo(ConstructGenericInfo(*generalDecl, instTys)); } Ptr GIM::GenericInstantiationManagerImpl::GetGeneralDecl(Decl& decl, bool getOriginal) const { auto target = GetRealTarget(&decl); // If decl is toplevel or nested function or haven't been instantiated, directly returns generic decl or itself. if (!target->outerDecl || !target->outerDecl->genericDecl || target->outerDecl->IsFunc()) { return target->genericDecl ? target->genericDecl : target; } // If decl is a generic member, return itself. if (target->TestAttr(Attribute::GENERIC) && !getOriginal) { return target; } bool isEnumCtor = target->TestAttr(Attribute::ENUM_CONSTRUCTOR); // If getOriginal is on, we found original generic version of the target. // Else: For generic member, we need use current outerDecl to find generic version of 'target'. auto typeDecl = target->outerDecl; if ((getOriginal && target->outerDecl->genericDecl) || isEnumCtor || !target->GetGeneric()) { typeDecl = target->outerDecl->genericDecl; } return PartialInstantiation::GetGeneralDecl(decl); /* auto& targetMembers = isEnumCtor ? RawStaticCast(typeDecl)->constructors : typeDecl->GetMemberDecls(); auto& srcMembers = isEnumCtor ? RawStaticCast(target->outerDecl)->constructors : target->outerDecl->GetMemberDecls(); // NOTE: Filter primary constructors and members generated by the compiler for test purposes, to get fixed members. std::vector> srcDecls = GetRealIndexingMembers(srcMembers); std::vector> targetDecls = GetRealIndexingMembers(targetMembers); CJC_ASSERT(srcDecls.size() == targetDecls.size()); // Found current decls's index in its owner structure declaration. // Get it's origin version by add offset to generic structure declaration's members. auto [isGetter, realDecl] = GetRealMemberDecl(*target); realDecl = realDecl->genericDecl ? realDecl->genericDecl : realDecl; for (auto it = srcDecls.begin(); it != srcDecls.end(); ++it) { if (*it == realDecl) { auto matchDecl = *(targetDecls.begin() + std::distance(srcDecls.begin(), it)); return GetUsedMemberDecl(*matchDecl, isGetter); } } return target; */ } /** * 'candidates' contains the parent decl and member offset of interface's implementation function. * Returns the valid instantiated implementation member. */ Ptr GIM::GenericInstantiationManagerImpl::SelectTypeMatchedImplMember( Ty& ty, const FuncDecl& interfaceFunc, std::vector, size_t>>& candidates, Ty& targetBaseTy) { Ptr member = nullptr; for (auto [baseDecl, offset] : candidates) { auto structDecl = baseDecl; auto instantiatedDecls = GetInstantiatedDecls(*baseDecl); for (auto decl : instantiatedDecls) { if (typeManager.IsSubtype(&ty, GetDeclTy(*decl), false)) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND structDecl = decl; #endif break; } } auto implMember = GetMemberByOffset(*structDecl, offset); if (implMember->astKind == ASTKind::PROP_DECL || IsVirtualMember(*implMember)) { member = implMember; break; } auto iFuncTy = StaticCast(interfaceFunc.ty); if (interfaceFunc.TestAttr(Attribute::GENERIC)) { TypeSubst typeMapping = typeManager.GenerateGenericMappingFromGeneric(interfaceFunc, *implMember); iFuncTy = StaticCast(typeManager.GetInstantiatedTy(iFuncTy, typeMapping)); } else if (interfaceFunc.outerDecl && interfaceFunc.outerDecl->TestAttr(Attribute::GENERIC)) { TypeSubst typeMapping = GenerateTypeMappingByTy(interfaceFunc.outerDecl->ty, &targetBaseTy); if (auto outerDecl = implMember->outerDecl; outerDecl && outerDecl->TestAttr(Attribute::GENERIC)) { auto tmpMapping = GenerateTypeMappingByTy(implMember->outerDecl->ty, &ty); auto rts = GetReversedTypeSubst(tmpMapping); TyGeneralizer tg(typeManager, rts); for (auto it = typeMapping.begin(); it != typeMapping.end(); ++it) { it->second = tg.Generalize(it->second); } } iFuncTy = StaticCast(typeManager.GetInstantiatedTy(iFuncTy, typeMapping)); } // Found related instantiated decls, if not exist, using the found member. auto usableDecls = GetInstantiatedDecls(*implMember); if (usableDecls.empty()) { usableDecls.emplace(implMember); } auto found = std::find_if(usableDecls.begin(), usableDecls.end(), [this, iFuncTy](auto it) { auto fty = DynamicCast(it->ty); return fty && iFuncTy && typeManager.IsFuncParameterTypesIdentical(*fty, *iFuncTy); }); if (found != usableDecls.end()) { member = *found; break; } } return member; } Ptr GIM::GenericInstantiationManagerImpl::FindImplFuncForAbstractFunc(Ty& ty, FuncDecl& fd, Ty& targetBaseTy) { // Rearrange for interface or open class call. Ignore function with following conditions: // 1. non-member function; // 2. non-static function which is accessed with interface type. // 3. Neither interface member nor open class's static member. bool shouldIgnore = !fd.outerDecl || (ty.IsInterface() && !fd.TestAttr(Attribute::STATIC)) || (fd.outerDecl->astKind != ASTKind::INTERFACE_DECL && (!IsInheritableClass(*fd.outerDecl) || !fd.TestAttr(Attribute::STATIC))); if (shouldIgnore) { return &fd; } // Get base ty: primitive ty, non-generic ty or generic ty of instantiated ty. auto baseTy = typeManager.GetTyForExtendMap(ty); auto genericFd = StaticCast(GetGeneralDecl(fd, true)); if (genericFd->genericDecl) { genericFd = StaticCast(genericFd->genericDecl); } auto keyPair = std::make_pair(baseTy, genericFd); auto foundAbstFunc = abstractFuncToDeclMap.find(keyPair); if (foundAbstFunc == abstractFuncToDeclMap.end()) { return &fd; } std::vector, size_t>> candidates; bool accessByInterface = ty.IsInterface(); std::copy_if(foundAbstFunc->second.begin(), foundAbstFunc->second.end(), std::back_inserter(candidates), [this, accessByInterface, &fd](auto& it) { bool notInSameDecl = it.first != GetGeneralDecl(*fd.outerDecl, true); bool instInInterface = it.first->astKind == ASTKind::INTERFACE_DECL; // Member access through the interface, looking up only the implementation from the interface. if (accessByInterface) { return notInSameDecl && instInInterface; } return notInSameDecl; }); Ptr member = SelectTypeMatchedImplMember(ty, fd, candidates, targetBaseTy); if (!member) { return &fd; } return As(GetUsedMemberDecl(*member, fd.isGetter)); } Ptr GIM::GenericInstantiationManagerImpl::ReinstantiatedPartialMemberDecl( const GenericInfo& genericInfo, Decl& structDecl, Decl& genericMember, size_t memberIndex) { // Re-instantiate generic member and replace the version stored in 'structDecl'. // Since partially instantiated generic member must never be referenced, the replacement is allowed. VisitFunc instantiateType = [this, &genericInfo, &structDecl](const Node& genericNode, Node& clonedNode) { PerformTyInstantiationDuringClone(genericNode, clonedNode, genericInfo, {}); if (auto decl = DynamicCast(&clonedNode); decl && decl->outerDecl == genericInfo.decl) { decl->outerDecl = &structDecl; // Update outerDecl from generic to instantiated. } }; auto clonedDecl = PartialInstantiation::Instantiate(&genericMember, instantiateType); if (!clonedDecl) { InternalError("generic instantiation failed"); return &genericMember; } clonedDecl->EnableAttr(Attribute::GENERIC_INSTANTIATED); auto partialDecl = clonedDecl.get(); auto& members = structDecl.GetMemberDecls(); members[memberIndex] = std::move(clonedDecl); return partialDecl; } Ptr GIM::GenericInstantiationManagerImpl::GetInstantiatedMemberTarget(Ty& baseTy, Decl& target, bool inRearrange) { auto declTy = GetDeclTy(*target.outerDecl); bool ignore = !target.outerDecl->generic || !target.outerDecl->TestAttr(Attribute::GENERIC) || !Ty::IsTyCorrect(declTy); if (ignore) { // Same non-generic judging condition with 'BuildAbstractFuncMap'. return ⌖ } auto promotedTys = promotion.Promote(baseTy, *declTy); auto realStructTy = promotedTys.empty() ? TypeManager::GetInvalidTy() : *promotedTys.begin(); if (realStructTy->HasInvalidTy() || realStructTy->HasGeneric()) { // Shall we really return here if realStructTy->HasGeneric()? return ⌖ } auto genericInfo = ConstructGenericInfo(*target.outerDecl, typeManager.GetTypeArgs(*realStructTy)); auto structDecl = FindInCache(genericInfo); // If current in instantiation step, instantiate structure declaration if not found. if (!structDecl && curTriggerNode && curTriggerNode->visitedByWalkerID == instantiationWalkerID) { structDecl = GetInstantiatedDeclWithGenericInfo(genericInfo); } auto [isGetter, targetToFind] = GetRealMemberDecl(target); auto foundIndex = membersIndexMap.find(targetToFind); // Function's outerDecl must been instantiated. if (!structDecl || foundIndex == membersIndexMap.end()) { if (!inRearrange) { InternalError("generic instantiation failed"); } return ⌖ } auto& members = target.TestAttr(Attribute::ENUM_CONSTRUCTOR) ? RawStaticCast(structDecl)->constructors : structDecl->GetMemberDecls(); auto tmpTarget = targetToFind.get(); auto found = std::find_if(members.begin(), members.end(), [tmpTarget](auto& member) { return PartialInstantiation::GetGeneralDecl(*member) == tmpTarget; }); if (found == members.end()) { return ⌖ } Ptr resultDecl = found->get(); CJC_ASSERT(resultDecl); // NOTE: If member decl has both 'GENERIC', 'GENERIC_INSTANTIATED' an 'UNREACHABLE' attributes, // means current is imported partially instantiated. It needs to be replaced by re-instantiated version. if (resultDecl->TestAttr(Attribute::GENERIC, Attribute::GENERIC_INSTANTIATED, Attribute::UNREACHABLE)) { resultDecl = ReinstantiatedPartialMemberDecl(genericInfo, *structDecl, *targetToFind, foundIndex->second); } resultDecl = GetUsedMemberDecl(*resultDecl, isGetter); return resultDecl; } void GIM::GenericInstantiationManagerImpl::GenericMemberAccessInstantiate(MemberAccess& ma) { for (auto& it : ma.typeArguments) { Walker walkArg(it.get(), instantiationWalkerID, instantiator, contextReset); walkArg.Walk(); } Walker walkBase(ma.baseExpr.get(), instantiationWalkerID, instantiator, contextReset); walkBase.Walk(); auto invalid = !ma.target || !ma.baseExpr || !Ty::IsTyCorrect(ma.ty); if (invalid || ma.target->astKind == ASTKind::PACKAGE_DECL || TestManager::IsMockAccessor(*ma.target)) { return; } auto target = GetRealTarget(ma.target); if (target->IsBuiltIn() || !RequireInstantiation(*target)) { return; } auto instTys = ma.instTys; // If target is constructor, instantiate outer structure declaration. if (IsClassOrEnumConstructor(*target)) { target = target->outerDecl; CJC_ASSERT(target != nullptr); auto baseTy = ma.ty->IsFunc() ? RawStaticCast(ma.ty)->retTy : ma.ty; instTys = typeManager.GetTypeArgs(*baseTy); } // For following conditions, we need to instantiate parent decl first. // 1. For function in generic structure declaration. Found partially instantiated decl. // 2. For member call of generic decl's member. bool needInstantiateParent = target->outerDecl && target->outerDecl->IsNominalDecl() && ma.baseExpr->ty && (IsGenericInGenericStruct(*target) || target->outerDecl->TestAttr(Attribute::GENERIC)); if (needInstantiateParent) { auto baseTy = Ty::IsTyCorrect(ma.matchedParentTy) ? ma.matchedParentTy : ma.baseExpr->ty; target = GetInstantiatedMemberTarget(*baseTy, *target); } InstantiateGenericDeclWithInstTys(*target, instTys); if (!Ty::IsTyCorrect(ma.baseExpr->ty)) { return; } if (target->IsFunc() && target->TestAttr(Attribute::GENERIC)) { // Also try to instantiate implementation function of generic interface function. target = FindImplFuncForAbstractFunc(*ma.baseExpr->ty, *StaticCast(target), *ma.baseExpr->ty); InstantiateGenericDeclWithInstTys(*target, instTys); } else if (!IsInDeclWithAttribute(*target, Attribute::GENERIC) && ma.isExposedAccess && target->IsFunc()) { // Searching for upper bound call of non-generic access to collect used inline functions earlier. target = FindImplFuncForAbstractFunc(*ma.baseExpr->ty, *StaticCast(target), *ma.baseExpr->ty); InstantiateGenericDeclWithInstTys(*target, instTys); } } void GIM::GenericInstantiationManagerImpl::GenericRefExprInstantiate(RefExpr& re) { for (auto& it : re.typeArguments) { Walker walkArg(it.get(), instantiationWalkerID, instantiator, contextReset); walkArg.Walk(); } // Generic type decleration do not need to be instantiated. if (!re.ref.target || re.ref.target->astKind == ASTKind::PACKAGE_DECL || !Ty::IsTyCorrect(re.ty) || re.ref.target->astKind == ASTKind::GENERIC_PARAM_DECL) { return; } auto target = GetRealTarget(re.ref.target); if (target->IsBuiltIn() || !RequireInstantiation(*target, IsInOpenContext(structContext))) { return; } auto instTys = re.instTys; // If target is constructor, instantiate outer structure declaration. if (IsClassOrEnumConstructor(*target)) { target = re.ref.target->outerDecl; CJC_ASSERT(target != nullptr); auto baseTy = re.ty->IsFunc() ? RawStaticCast(re.ty)->retTy : re.ty; instTys = typeManager.GetTypeArgs(*baseTy); } // For function in generic structure declaration, // found decl inside the instantiated outerDecl which is not current context decl. bool needInstantiateParent = target->outerDecl && target->outerDecl->IsNominalDecl() && (IsGenericInGenericStruct(*target) || target->outerDecl->TestAttr(Attribute::GENERIC)); auto structDecl = structContext.empty() ? target->outerDecl : GetStructDeclByContext(); Ptr baseTy = nullptr; if (structDecl != target->outerDecl) { baseTy = GetDeclTy(*structDecl); CJC_NULLPTR_CHECK(baseTy); } if (needInstantiateParent && baseTy != nullptr) { target = GetInstantiatedMemberTarget(*baseTy, *target); } InstantiateGenericDeclWithInstTys(*target, instTys); if (baseTy != nullptr && target->IsFunc() && target->TestAttr(Attribute::GENERIC)) { // Also try to instantiate implementation function of generic interface function. target = FindImplFuncForAbstractFunc(*baseTy, *StaticCast(target), *baseTy); InstantiateGenericDeclWithInstTys(*target, instTys); } } void GIM::GenericInstantiationManagerImpl::GenericTyExtendInstantiate(Ty& ty) { if (ty.HasGeneric()) { return; } auto decl = Ty::GetDeclPtrOfTy(&ty); if (decl) { InstantiateGenericDeclWithInstTys(*decl, ty.typeArgs); } auto extends = typeManager.GetTyUsedExtends(&ty); if (extends.empty()) { return; } for (auto it : extends) { if (!RequireInstantiation(*it)) { continue; } auto extendTy = GetDeclTy(*it); auto promoteRes = promotion.Promote(ty, *extendTy); CJC_ASSERT(!promoteRes.empty()); // The 'ty' must have promoted result with extended type. auto promotedTy = *promoteRes.begin(); bool ignored = !extendTy || !typeManager.CheckExtendWithConstraint(ty, it); if (ignored) { continue; } InstantiateGenericDeclWithInstTys(*it, typeManager.GetTypeArgs(*promotedTy)); } } void GIM::GenericInstantiationManagerImpl::InstantiateGenericTysForMemoryLayout(const Ty& ty) { if (ty.HasGeneric()) { return; } for (auto it : ty.typeArgs) { CJC_NULLPTR_CHECK(it); if (!it->typeArgs.empty()) { InstantiateGenericTysForMemoryLayout(*it); } } auto decl = Ty::GetDeclPtrOfTy(&ty); if (decl) { InstantiateGenericDeclWithInstTys(*decl, ty.typeArgs); } } void GIM::GenericInstantiationManagerImpl::GenericTypeInstantiate(const Type& type) { // Ignore invalid type node & partially typealias node. // If the type contains generic ty, current type node may be typealias substituted node. if (!Ty::IsTyCorrect(type.ty) || HasIntersectionTy(*type.ty) || type.ty->HasGeneric()) { return; } // Instantiated type only by sema type (also for typeAlias substituted cases). std::function)> instantiateType = [this, &instantiateType](auto ty) { for (auto tyArg : ty->typeArgs) { instantiateType(tyArg); } auto target = Ty::GetDeclPtrOfTy(ty); if (!target || !RequireInstantiation(*target)) { return; } InstantiateGenericDeclWithInstTys(*target, ty->typeArgs); }; instantiateType(type.ty); } void GIM::GenericInstantiationManagerImpl::GenericArrayExprInstantiate(const ArrayExpr& ae) { // VArray does not have initFunc and does not need to be instantiated. if (Ty::IsTyCorrect(ae.ty) && ae.ty->kind != TypeKind::TYPE_ARRAY) { return; } auto arrayTy = RawStaticCast(ae.ty); if (ae.initFunc != nullptr) { auto typeArgs = typeManager.GetTypeArgs(*arrayTy); InstantiateGenericDeclWithInstTys(*ae.initFunc, typeArgs); } } void GIM::GenericInstantiationManagerImpl::GenericArrayLitInstantiate(ArrayLit& al) { for (auto& it : al.children) { Walker walkElement(it.get(), instantiationWalkerID, instantiator, contextReset); walkElement.Walk(); } auto target = Ty::GetDeclPtrOfTy(al.ty); if (!target) { return; } InstantiateGenericDeclWithInstTys(*target, al.ty->typeArgs); } VisitAction GIM::GenericInstantiationManagerImpl::CheckVisitedNode(Ptr node, bool checkGeneric) { if (diag.GetErrorCount() != 0) { // Error happens, stop walker. return VisitAction::STOP_NOW; } if (!node || (!checkGeneric && node->TestAttr(Attribute::GENERIC))) { // We should ignore current node if it: // - is invalid, // - is generic and we won't check generic. return VisitAction::SKIP_CHILDREN; } if (ignoreKinds.count(node->astKind) > 0) { // Current not should be ignored. return VisitAction::SKIP_CHILDREN; } // Ignore compiler added placeholder argument for function's default parameter. // NOTE: This should not be translated by CHIR, and never been used. if (node->astKind == ASTKind::FUNC_ARG && node->TestAttr(Attribute::HAS_INITIAL)) { return VisitAction::SKIP_CHILDREN; } if (auto decl = DynamicCast(node); decl) { if (decl->astKind == ASTKind::CLASS_DECL && decl->identifier.Val().find(BOX_DECL_PREFIX) == 0) { // Current decl is a boxed decl, should be skipped. return VisitAction::SKIP_CHILDREN; } // If walk into a structure declaration or generic decl inside generic structure declaration. Update context. if (NeedSwitchContext(*decl)) { structContext.push_back(decl); } } return VisitAction::WALK_CHILDREN; } VisitAction GIM::GenericInstantiationManagerImpl::CheckNodeInstantiation(Node& node) { // Should not walk generated instantiated decls and source imported decls. if (auto pkg = DynamicCast(&node); pkg) { for (auto& it : pkg->files) { Walker(it.get(), instantiationWalkerID, instantiator, contextReset).Walk(); } return VisitAction::SKIP_CHILDREN; } // Validate input node, quit if invalid. VisitAction action = CheckVisitedNode(&node); if (action != VisitAction::WALK_CHILDREN) { return action; } if (auto expr = DynamicCast(&node); expr && expr->desugarExpr) { Walker(expr->desugarExpr.get(), instantiationWalkerID, instantiator, contextReset).Walk(); return VisitAction::SKIP_CHILDREN; } curTriggerNode = &node; if (Ty::IsTyCorrect(node.ty) && !HasIntersectionTy(*node.ty)) { // For memory layout, instantiated all used generics in node's ty. InstantiateGenericTysForMemoryLayout(*node.ty); // Extends of type should be instantied by usage. GenericTyExtendInstantiate(*node.ty); } switch (node.astKind) { case ASTKind::REF_EXPR: { GenericRefExprInstantiate(*StaticAs(&node)); return VisitAction::SKIP_CHILDREN; } case ASTKind::MEMBER_ACCESS: { GenericMemberAccessInstantiate(*StaticAs(&node)); return VisitAction::SKIP_CHILDREN; } case ASTKind::REF_TYPE: case ASTKind::QUALIFIED_TYPE: { GenericTypeInstantiate(*StaticAs(&node)); return VisitAction::SKIP_CHILDREN; } case ASTKind::ARRAY_EXPR: { GenericArrayExprInstantiate(*StaticAs(&node)); return VisitAction::WALK_CHILDREN; } case ASTKind::ARRAY_LIT: { GenericArrayLitInstantiate(*StaticAs(&node)); return VisitAction::SKIP_CHILDREN; } default: return VisitAction::WALK_CHILDREN; } } VisitAction GIM::GenericInstantiationManagerImpl::RearrangeReferencePtr(Node& node) { VisitAction action = CheckVisitedNode(&node, true); if (action != VisitAction::WALK_CHILDREN) { return action; } if (auto expr = DynamicCast(&node); expr && expr->desugarExpr) { Walker(expr->desugarExpr.get(), rearrangeWalkerID, rearranger, contextReset).Walk(); ClearInstTysIsNeeded(node); return VisitAction::SKIP_CHILDREN; } curTriggerNode = &node; switch (node.astKind) { case ASTKind::CALL_EXPR: // Rearrange call expr. RearrangeCallExprReference(*StaticAs(&node)); return VisitAction::WALK_CHILDREN; case ASTKind::REF_EXPR: // Rearrange ref expr. RearrangeRefExprReference(*StaticAs(&node)); return VisitAction::WALK_CHILDREN; case ASTKind::MEMBER_ACCESS: RearrangeMemberAccessReference(*StaticAs(&node)); return VisitAction::WALK_CHILDREN; case ASTKind::REF_TYPE: case ASTKind::QUALIFIED_TYPE: RearrangeTypeReference(*StaticAs(&node)); return VisitAction::SKIP_CHILDREN; case ASTKind::ARRAY_EXPR: RearrangeArrayExprReference(*StaticAs(&node)); return VisitAction::WALK_CHILDREN; case ASTKind::ARRAY_LIT: RearrangeArrayLitReference(*StaticAs(&node)); return VisitAction::WALK_CHILDREN; case ASTKind::FUNC_BODY: RearrangeFuncBodyReference(*StaticAs(&node)); return VisitAction::WALK_CHILDREN; case ASTKind::TYPE_PATTERN: case ASTKind::TUPLE_PATTERN: case ASTKind::ENUM_PATTERN: UpdateTypePatternMatchResult(*StaticAs(&node)); return VisitAction::WALK_CHILDREN; default: return VisitAction::WALK_CHILDREN; } } void GIM::GenericInstantiationManagerImpl::RearrangeCallExprReference(CallExpr& ce) { if (!ce.resolvedFunction || !ce.baseFunc || !ce.baseFunc->IsReferenceExpr()) { return; } Walker(ce.baseFunc.get(), rearrangeWalkerID, rearranger, contextReset).Walk(); Ptr target = ce.baseFunc->GetTarget(); // Get re-arranged target. // Sema guarantees: base's target not null when 'resolvedFunction' is not null and they are pointing to same decl. CJC_NULLPTR_CHECK(target); ce.resolvedFunction = StaticCast(target); if (target->ty->HasGeneric() || target->ty->IsGeneric()) { return; } if (!HasJavaAttr(*target)) { ce.ty = StaticCast(target->ty)->retTy; } // Deal for dynamic 'This' binding call. // eg: tests/LLT/Sema/class/ThisType/class_generic_dynamic_binding_thistype_ok_2.cj if (auto thisTy = DynamicCast(ce.ty); thisTy && ce.baseFunc->astKind == ASTKind::MEMBER_ACCESS) { ce.ty = StaticAs(ce.baseFunc.get())->baseExpr->ty; } } Ptr GIM::GenericInstantiationManagerImpl::GetInstantiatedTarget( Ty& baseTy, Decl& target, const std::vector>& instTys, Ptr upperTy) { auto genericDecl = GetGeneralDecl(target); if (genericDecl->IsBuiltIn()) { return ⌖ } // Rearrange toplevel decls or nested function decls. if (genericDecl->IsNominalDecl() || !genericDecl->outerDecl || !genericDecl->outerDecl->IsNominalDecl()) { if (!genericDecl->GetGeneric() || !genericDecl->TestAttr(Attribute::GENERIC)) { // Target is not a generic decl. return genericDecl; } Ptr instantiatedDecl = FindInCache(ConstructGenericInfo(*genericDecl, instTys)); if (!instantiatedDecl) { return ⌖ } return instantiatedDecl; } auto targetBase = Ty::IsTyCorrect(upperTy) && !upperTy->HasGeneric() ? upperTy : Ptr(&baseTy); auto decl = GetInstantiatedMemberTarget(*targetBase, *genericDecl, true); if (decl && decl->TestAttr(Attribute::GENERIC)) { decl = FindInCache(ConstructGenericInfo(*decl, instTys)); } // Decl may be an interface function which should be rearranged to implementation decl. if (decl && decl->IsFunc()) { decl = FindImplFuncForAbstractFunc(baseTy, *RawStaticCast(decl), *targetBase); CJC_NULLPTR_CHECK(decl); // Get instantiated version if decl has been updated to implented generic function. if (decl->TestAttr(Attribute::GENERIC)) { decl = FindInCache(ConstructGenericInfo(*decl, instTys)); } } if (!decl) { return ⌖ } return decl; } void GIM::GenericInstantiationManagerImpl::RearrangeRefExprReference(RefExpr& re) { // Generic type decleration do not need to be instantiated. if (!Ty::IsTyCorrect(re.ty) || !re.ref.target || re.ref.target->IsBuiltIn() || re.ref.target->astKind == ASTKind::GENERIC_PARAM_DECL || IsInOpenContext(structContext)) { return; } auto baseTy = re.ty; auto instTys = (re.isThis || re.isSuper) ? typeManager.GetTypeArgs(*re.ty) : re.instTys; if (IsClassOrEnumConstructor(*re.ref.target)) { baseTy = baseTy->IsFunc() ? RawStaticCast(baseTy)->retTy : baseTy; instTys = typeManager.GetTypeArgs(*baseTy); } else if (!structContext.empty()) { auto structDecl = GetStructDeclByContext(); // For member used inside owner decl, the base type is type of current structure declaration. baseTy = GetDeclTy(*structDecl); } re.ref.target = GetInstantiatedTarget(*baseTy, *re.ref.target, instTys, re.matchedParentTy); // If the target of RefExpr has been instantiated, we should clear the field instTys of the latter. ClearInstTysIsNeeded(re); } void GIM::GenericInstantiationManagerImpl::RearrangeMemberAccessReference(MemberAccess& ma) { Walker(ma.baseExpr.get(), rearrangeWalkerID, rearranger, contextReset).Walk(); // BaseExpr of member access may be package decl which does no have sema type. if (!ma.target || !ma.baseExpr || Ty::IsInitialTy(ma.ty) || ma.target->IsBuiltIn()) { return; } // MemberAccess's base may be package decl which does not have sema type. auto baseTy = Ty::IsTyCorrect(ma.baseExpr->ty) ? ma.baseExpr->ty : ma.ty; auto instTys = ma.instTys; if (IsClassOrEnumConstructor(*ma.target)) { baseTy = baseTy->IsFunc() ? RawStaticCast(baseTy)->retTy : baseTy; instTys = typeManager.GetTypeArgs(*baseTy); } ma.target = GetInstantiatedTarget(*baseTy, *ma.target, instTys, ma.matchedParentTy); /** * For such case: * @Java * class Sample { * var t: T * init(t: T) { this.t = t } * } * * @Java * class A { * var i: Int32 * init(i: Int32) { this.i = i } * } * * main() { * var a = A(1) * var s = Sample(a) * println(s.t.i) <============== here * } * Because `Sample` has been generic erase to `Sample_JObject`, the target of `s.t` is type of `JObject`, * but actually it should be `A` here, so we don't update its `ty` to keep it `A`. */ ClearInstTysIsNeeded(ma); } void GIM::GenericInstantiationManagerImpl::RearrangeTypeReference(Type& type) { // Ignore invalid type node & partially typealias node. // If the type contains generic ty, current type node may be typealias substituted node. if (!Ty::IsTyCorrect(type.ty) || HasIntersectionTy(*type.ty) || type.ty->HasGeneric()) { return; } auto target = Ty::GetDeclPtrOfTy(type.ty); if (!target) { return; } auto instantiatedDecl = GetInstantiatedTarget(*type.ty, *target, type.ty->typeArgs); // Type only will be RefType or QualifiedType, guaranteed by caller. if (type.astKind == ASTKind::REF_TYPE) { auto& rt = static_cast(type); rt.ref.target = instantiatedDecl; } else { auto& qt = static_cast(type); qt.target = instantiatedDecl; } } void GIM::GenericInstantiationManagerImpl::RearrangeArrayExprReference(ArrayExpr& ae) { if (!ae.initFunc || !Ty::IsTyCorrect(ae.ty)) { return; } auto decl = FindInCache(ConstructGenericInfo(*ae.initFunc, typeManager.GetTypeArgs(*ae.ty))); if (!decl) { return; } ae.initFunc = RawStaticCast(decl); } void GIM::GenericInstantiationManagerImpl::RearrangeArrayLitReference(ArrayLit& al) { if (al.initFunc && Ty::IsTyCorrect(al.ty)) { al.initFunc = RawStaticCast(GetInstantiatedTarget(*al.ty, *al.initFunc, al.ty->typeArgs)); } } void GIM::GenericInstantiationManagerImpl::RearrangeFuncBodyReference(FuncBody& fb) { if (structContext.empty()) { return; } auto structDecl = GetStructDeclByContext(); auto baseTy = GetDeclTy(*structDecl); if (fb.parentClassLike) { fb.parentClassLike = RawStaticCast(GetInstantiatedTarget(*baseTy, *fb.parentClassLike, baseTy->typeArgs)); } else if (fb.parentStruct) { fb.parentStruct = RawStaticCast(GetInstantiatedTarget(*baseTy, *fb.parentStruct, baseTy->typeArgs)); } else if (fb.parentEnum) { fb.parentEnum = RawStaticCast(GetInstantiatedTarget(*baseTy, *fb.parentEnum, baseTy->typeArgs)); } } void GIM::GenericInstantiationManagerImpl::UpdateTypePatternMatchResult(Pattern& pattern) { if (!Ty::IsTyCorrect(pattern.ty)) { return; } // Define the process of updating match result of typePattern. auto update = [this](auto targetTy, auto pattern) { auto tp = As(pattern); if (!tp || tp->matchBeforeRuntime) { return; } tp->matchBeforeRuntime = typeManager.IsSubtype(targetTy, tp->type->ty, true, false); // Only set 'needRuntimeTypeCheck' true if both selector type and pattern type are classlike type. tp->needRuntimeTypeCheck = !tp->matchBeforeRuntime && tp->type->ty && IsNeedRuntimeCheck(typeManager, *targetTy, *tp->type->ty); }; // Define the process of checking child patterns of current pattern level. auto checkChildren = [&update](auto tys, auto& patterns) { CJC_ASSERT(tys.size() == patterns.size()); for (size_t i = 0; i < patterns.size(); ++i) { update(tys[i], patterns[i].get()); } }; // Perform updating for current pattern level. More nesting pattern will be checked during rearrange process later. if (auto tuplePattern = DynamicCast(&pattern); tuplePattern) { auto tupleTy = DynamicCast(tuplePattern->ty); if (tupleTy) { checkChildren(tupleTy->typeArgs, tuplePattern->patterns); } } else if (auto enumPattern = DynamicCast(&pattern); enumPattern) { CJC_NULLPTR_CHECK(enumPattern->constructor); auto funcTy = DynamicCast(enumPattern->constructor->ty); if (funcTy) { checkChildren(funcTy->paramTys, enumPattern->patterns); } } else if (pattern.ctxExpr) { update(pattern.ctxExpr->ty, &pattern); } } /** * Remove built-in binary/unary operation's desugar after instantiation and type rearranged. * eg: func test(a: T, b: T) where T <: Equatable { * a == b * } * 'a == b' will be desugared as call expr after sema check. But if we have 'test' then * 'a == b' is actually 'Int64' comparing with 'Int64' which can be directly generated as binary expr. * so, recover this kind of call expr to original binary/unary expr in this function. */ void GIM::GenericInstantiationManagerImpl::RecoverDesugarForBuiltIn() const { for (auto& it : curPkg->genericInstantiatedDecls) { // the 'ce' should not be used after recover, so walking in postorder. Walker(it.get(), nullptr, [](auto node) { auto ce = DynamicCast(node); bool mayBeBuiltIn = ce && ce->resolvedFunction && ce->resolvedFunction->outerDecl; if (!mayBeBuiltIn) { return VisitAction::WALK_CHILDREN; } auto fd = ce->resolvedFunction; Ty* thisTy = fd->outerDecl->ty; FuncTy* funcTy = DynamicCast(fd->ty); if (ce->baseFunc && ce->baseFunc->astKind == ASTKind::MEMBER_ACCESS) { thisTy = StaticCast(ce->baseFunc.get())->baseExpr->ty; CJC_ASSERT(ce->baseFunc->ty->IsFunc()); funcTy = StaticCast(ce->baseFunc->ty); } if (!funcTy || !thisTy) { return VisitAction::WALK_CHILDREN; } // For desugared callExpr in source package we directly recover to original expression. // For imported callExpr, we desugar it to binary/unary expression. if (funcTy->paramTys.size() == 1 && funcTy->paramTys[0] && TypeCheckUtil::IsBuiltinBinaryExpr(fd->op, *thisTy, *funcTy->paramTys[0])) { if (ce->sourceExpr) { if (auto bin = DynamicCast(ce->sourceExpr)) { RecoverToBinaryExpr(*bin); } // otherwise sourceExpr can be a compound assignment expr, e.g. v |= v, where v is of ty Int64, and // | is overloaded operator from an interface that Int64 extends } else { auto be = MakeOwnedNode(); be->leftExpr = std::move(StaticCast(ce->baseFunc.get())->baseExpr); be->rightExpr = std::move(ce->args[0]->expr); be->op = fd->op; be->ty = ce->ty; CopyBasicInfo(ce, be.get()); AddCurFile(*be, ce->curFile); ce->desugarExpr = std::move(be); } } else if (funcTy->paramTys.empty() && TypeCheckUtil::IsBuiltinUnaryExpr(fd->op, *thisTy)) { if (ce->sourceExpr) { if (auto bin = DynamicCast(ce->sourceExpr)) { RecoverToUnaryExpr(*bin); } } else { auto ue = MakeOwnedNode(); ue->expr = std::move(StaticCast(ce->baseFunc.get())->baseExpr); ue->op = fd->op; ue->ty = ce->ty; CopyBasicInfo(ce, ue.get()); AddCurFile(*ue, ce->curFile); ce->desugarExpr = std::move(ue); } } return VisitAction::SKIP_CHILDREN; }).Walk(); } } cangjie_compiler-1.0.7/src/Sema/GenericInstantiation/GenericInstantiationManagerImpl.h000066400000000000000000000374051510705540100312240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * GenericInstantiationManager is the global manager to maintain the generic information. */ #ifndef CANGJIE_SEMA_GENERIC_INSTANTIATION_MANAGER_IMPL_H #define CANGJIE_SEMA_GENERIC_INSTANTIATION_MANAGER_IMPL_H #include "Promotion.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Sema/CommonTypeAlias.h" #include "cangjie/Sema/GenericInstantiationManager.h" #include "cangjie/Sema/TestManager.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Utils/ProfileRecorder.h" namespace Cangjie { /** GenericInfoHash is the hash to indicate the uniqueness of a GenericInfo. */ struct GenericInfoHash { size_t operator()(const GenericInfo& info) const { size_t ret = 0; ret = hash_combine>(ret, info.decl); for (auto n : info.gTyToTyMap) { ret = hash_combine>(ret, n.first); ret = hash_combine>(ret, n.second); } return ret; } }; /** GenericInfoEqual is used to distinguish two GenericInfo when hash conflict encountered. */ struct GenericInfoEqual { bool operator()(const GenericInfo& lhs, const GenericInfo& rhs) const { if ((lhs.decl != rhs.decl) || (lhs.gTyToTyMap.size() != rhs.gTyToTyMap.size())) { return false; } for (auto it1 : lhs.gTyToTyMap) { auto it2 = rhs.gTyToTyMap.find(it1.first); if (it2 == rhs.gTyToTyMap.end()) { return false; } else { if (it1.second != it2->second) { return false; } } } return true; } }; /** * The class of generic instantiation manager is a global manager that * maintains cache of generic and instantiated decls' information. */ using Generic2InsMap = std::unordered_map, std::unordered_set>>; class GenericInstantiationManager::GenericInstantiationManagerImpl { public: explicit GenericInstantiationManagerImpl(CompilerInstance& ci); ~GenericInstantiationManagerImpl() { curPkg = nullptr; curTriggerNode = nullptr; testManager = nullptr; } /** Generic instantiation package entrance. */ void GenericInstantiatePackage(AST::Package& pkg); /** * Get the instantiated decl corresponding to the genericInfo: * @param genericInfo [in] generic decl instantiation parameters. * @param pkg [in] current processing package. MUST given, if call this api outside genericInstantiation step. */ Ptr GetInstantiatedDeclWithGenericInfo(const GenericInfo& genericInfo, AST::Package& pkg); /** Get set of instantiated decl of given generic decl */ std::unordered_set> GetInstantiatedDecls(const AST::Decl& genericDecl) const { auto found = instantiatedDeclsMap.find(&genericDecl); return found == instantiatedDeclsMap.end() ? std::unordered_set>{} : found->second; } /** Prepare for generic instantiation processing: * 1. clear all cache generated before. * 2. pre-build context cache. */ void ResetGenericInstantiationStage() { ClearCache(); // Build generic instantiateManager to rebuild instantiated cache and genericNodeToInstantiatedNodeMap. RebuildGenericInstantiationManager(); Utils::ProfileRecorder recorder("ResetGenericInstantiationStage", "BuildAbstractFuncMap"); // Build abstract function map for all type which inherited interface. BuildAbstractFuncMap(); } Generic2InsMap GetAllGenericToInsDecls() const; friend class InstantiatedExtendRecorder; friend class MockUtils; private: Ptr curPkg{nullptr}; DiagnosticEngine& diag; ImportManager& importManager; TypeManager& typeManager; TestManager* testManager{nullptr}; Promotion promotion; /** Unified walker ID for instantiation. */ unsigned instantiationWalkerID; /** Unified walker ID for rearrange. */ unsigned rearrangeWalkerID; /** Current compiling backend. */ Triple::BackendType backend; /** The node which triggered current instantiation. */ Ptr curTriggerNode{nullptr}; /** Lambda function for instantiation walker. */ std::function)> instantiator; /** Lambda function for rearrangement walker. */ std::function)> rearranger; std::function)> contextReset; /** A map stores the original generic decl and all its instantiated decls. */ Generic2InsMap instantiatedDeclsMap; /** Key: generic decl & instantiated types. Value: instantiated decl. */ std::unordered_multimap, GenericInfoHash, GenericInfoEqual> declInstantiationByTypeMap; /** * This map saves the information of the function in which structure declaration implements the abstract function in * interface. * The key is pair of type and the abstract function in interface. * The value is a set that contains pair of: * 1. the structure declaration which contains the function which implements abstract function. * 2. the index of the implementation function in structure declaration. * */ std::unordered_map, Ptr>, std::unordered_set, size_t>, HashPair>, HashPair> abstractFuncToDeclMap; std::unordered_map, size_t> membersIndexMap; /** Node kinds which should be ignored in walker. */ inline static const std::unordered_set ignoreKinds = { AST::ASTKind::GENERIC_PARAM_DECL, AST::ASTKind::GENERIC_CONSTRAINT, AST::ASTKind::PRIMARY_CTOR_DECL, AST::ASTKind::MODIFIER, }; std::vector> structContext; /** Key: sema type; Value: whether contains intersection ty. */ std::unordered_map, bool> intersectionTyStatus; /** Mark whether current type has instantiated related extends for package in gim. */ std::unordered_set, std::string>, HashPair> extendGenerated; /** Store the source imported decls which are checked with instantiation status. */ std::unordered_set> usedSrcImportedDecls; /** Used for incremental compilation, decide whether new created instantiation need to be compiled. */ bool needCompile = true; /** Implement working flow for incremental compiling package. */ void InstantiateForIncrementalPackage(); /** Rebuild type stored decl pointer for imported instantiated decls. */ void RestoreInstantiatedDeclTy() const; void RestoreInstantiatedDeclTy(AST::Decl& decl) const; void RebuildGenericInstantiationManager(); void WalkImportedInstantiations(const std::function& processFunc, const std::function& skipChecker) const; void UpdateInstantiatedExtendMap(); void ClearCache(); void RecordExtend(AST::Node& node); /** * Since cjnative backend only generate instantiated decls as local symbols, * we need to remove decls in other package which have same type of instantiation in current package. */ void ClearImportedUnusedInstantiatedDecls(); void RecoverDesugarForBuiltIn() const; /** Build type mapping from instantiated decl. */ TypeSubst BuildTypeMapping(const AST::Decl& instantiatedDecl) const; /** * Check whether the genericInfo is found in the declInstantiationByTypeMap * and the decl is instantiated in dependent package of current package. */ Ptr FindInCache(const GenericInfo& info); /** Construct GenericInfo. */ GenericInfo ConstructGenericInfo(AST::Decl& decl, const std::vector>& instTys) const; void AppendGenericMemberMap(const AST::Decl& genericDecl, const std::unordered_set>& insNominalDecls, Generic2InsMap& result) const; /** * Get the instantiated decl corresponding to the genericInfo: * 1. Find in the declInstantiationByTypeMap, if found, there is no need to instantiate again. * 2. If not found in declInstantiationByTypeMap, perform instantiation according to the genericInfo. * 3. After the generic decl is instantiated, all its extend decls need to be instantiate, too. * @param genericInfo [in] generic decl instantiation parameters. */ Ptr GetInstantiatedDeclWithGenericInfo(const GenericInfo& genericInfo); /** * Instantiate a generic decl @p genericDecl with type arguments @p instTys. */ void InstantiateGenericDeclWithInstTys(AST::Decl& decl, const std::vector>& instTys); /** * Walk inherited types which are used by non-generic be boxed extend decls. * Used to guarantee all related generic types are instantiated. */ void WalkNonGenericExtendedType(); /** Helper functions during clone instantiated decl. */ void PerformTyInstantiationDuringClone( const AST::Node& genericNode, AST::Node& clonedNode, const GenericInfo& info, const TypeSubst& g2gTyMap); void PerformUpdateAttrDuringClone(AST::Node& genericNode, AST::Node& clonedNode) const; /** Find implemented version function of abstract function @p interfaceFunc in the decl of Ty @p ty. */ Ptr FindImplFuncForAbstractFunc(AST::Ty& ty, AST::FuncDecl& fd, AST::Ty& targetBaseTy); /** Find matched implementation function of abstract function @p fd from candidates */ Ptr SelectTypeMatchedImplMember(AST::Ty& ty, const AST::FuncDecl& interfaceFunc, std::vector, size_t>>& candidates, AST::Ty& targetBaseTy); /** Walker function for node instantiation. */ AST::VisitAction CheckNodeInstantiation(AST::Node& node); /** Walker function for reference pointer rearrangement. */ AST::VisitAction RearrangeReferencePtr(AST::Node& node); AST::VisitAction CheckVisitedNode(Ptr node, bool checkGeneric = false); /** Instantiate generic MemberAccess @p ma. */ void GenericMemberAccessInstantiate(AST::MemberAccess& ma); /** Instantiate generic RefExpr @p re. */ void GenericRefExprInstantiate(AST::RefExpr& re); /** Instantiate generic RefType or QualifiedType @p type. */ void GenericTypeInstantiate(const AST::Type& type); /** Instantiate generic ArrayExpr @p ae. */ void GenericArrayExprInstantiate(const AST::ArrayExpr& ae); /** Instantiate generic ArrayList @p al which has Struct-Array type. */ void GenericArrayLitInstantiate(AST::ArrayLit& al); /** Instantiate extend of generic sema type @p ty 's extends. */ void GenericTyExtendInstantiate(AST::Ty& ty); void InstantiateGenericTysForMemoryLayout(const AST::Ty& ty); /** Instantiate imported partial instantiated member decl. */ Ptr ReinstantiatedPartialMemberDecl( const GenericInfo& genericInfo, AST::Decl& structDecl, AST::Decl& genericMember, size_t memberIndex); /** Rearrange the ptr of outer references' target to the instantiated decl. */ void RearrangeTypeReference(AST::Type& type); void RearrangeCallExprReference(AST::CallExpr& ce); void RearrangeRefExprReference(AST::RefExpr& re); void RearrangeMemberAccessReference(AST::MemberAccess& ma); void RearrangeArrayExprReference(AST::ArrayExpr& ae); void RearrangeArrayLitReference(AST::ArrayLit& al); void RearrangeFuncBodyReference(AST::FuncBody& fb); void UpdateTypePatternMatchResult(AST::Pattern& pattern); Ptr GetInstantiatedTarget( AST::Ty& baseTy, AST::Decl& target, const std::vector>& instTys, Ptr upperTy = nullptr); Ptr GetInstantiatedMemberTarget(AST::Ty& baseTy, AST::Decl& target, bool inRearrange = false); /** Build interface function to implemented function map for all type decls */ void BuildAbstractFuncMap(); void BuildAbstractFuncMapHelper(AST::Ty& ty); bool IsImplementationFunc(AST::Ty& ty, const AST::FuncDecl& interfaceFunc, const AST::FuncDecl& fd); MultiTypeSubst GetTypeMapping(Ptr& baseTy, AST::Ty& interfaceTy); void MapFuncWithDecl(AST::Ty& ty, AST::FuncDecl& interfaceFunc, const AST::FuncDecl& target); void CollectDeclMemberFuncs(AST::Decl& decl, std::unordered_set>& funcs) const; std::unordered_set> GetInheritedMemberFuncs(AST::Ty& ty); std::unordered_set> GetInheritedInterfaces(AST::Ty& ty); /** Collect inherited members for imported decls which is not check during sema stage. */ std::unordered_set> MergeMemberFuncs( AST::Ty& ty, AST::Decl& decl, const std::unordered_set>& inheritedMembers); std::unordered_set> CollectInheritedMembers(AST::Ty& ty, AST::Decl& decl); std::unordered_set> CollectInheritedMembersVisit( AST::Ty& ty, AST::Decl& decl, std::set, Ptr>>& visited); /** * Get general version of given @p decl. If getOriginal is on: * Get original generic version of the 'decl'. * Else: * 1. if decl is toplevel decl or nested function, return it's original decl that input may be instantiated. * 2. if decl is a generic member decl, return itself since decl may be partial instantiated. * 3. if decl is a non-generic member, but outerDecl is instantiated decl, find origin one in generic outerDecl. */ Ptr GetGeneralDecl(AST::Decl& decl, bool getOriginal = false) const; Ptr GetStructDeclByContext() { // Caller guarantees 'structContext' not empty. auto decl = structContext.back(); while (decl && !decl->IsNominalDecl()) { decl = decl->outerDecl; } return decl; } bool HasIntersectionTy(AST::Ty& ty) { auto found = intersectionTyStatus.find(&ty); if (found != intersectionTyStatus.end()) { return found->second; } if (ty.IsIntersection()) { intersectionTyStatus.emplace(&ty, true); return true; } for (auto typeArg : ty.typeArgs) { if (typeArg && HasIntersectionTy(*typeArg)) { intersectionTyStatus.emplace(&ty, true); return true; } } intersectionTyStatus.emplace(&ty, false); return false; } void RemoveFromCache(AST::Decl& decl) { auto genericDecl = decl.genericDecl; instantiatedDeclsMap[genericDecl].erase(&decl); GenericInfo genericInfo(genericDecl, BuildTypeMapping(decl)); auto decls = declInstantiationByTypeMap.equal_range(genericInfo); for (auto it = decls.first; it != decls.second; ++it) { if (it->second == &decl) { declInstantiationByTypeMap.erase(it); break; } } } bool IsDeclCanRestoredForTy(const AST::Decl& decl) const; class IncrementalContext { public: explicit IncrementalContext(bool& reCompile, bool unchanged) : needReCompile(reCompile), prevStatus(reCompile) { needReCompile = unchanged ? false : needReCompile; } ~IncrementalContext() { needReCompile = prevStatus; } private: bool& needReCompile; bool prevStatus; }; }; } // namespace Cangjie #endif // CANGJIE_SEMA_GENERIC_INSTANTIATION_MANAGER_IMPL_H cangjie_compiler-1.0.7/src/Sema/GenericInstantiation/ImplUtils.h000066400000000000000000000052401510705540100247000ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * Define utils methods for generic instantiation. */ #ifndef CANGJIE_SEMA_GENERIC_INSTANTIATION_UTILS_H #define CANGJIE_SEMA_GENERIC_INSTANTIATION_UTILS_H #include #include "cangjie/AST/Node.h" #include "cangjie/Sema/TestManager.h" namespace Cangjie { /** Get @p decl 's sema type. If decl is extend decl, get it's extended sema type. */ inline Ptr GetDeclTy(const AST::Decl& decl) { auto ty = decl.ty; if (decl.astKind == AST::ASTKind::EXTEND_DECL) { ty = static_cast(decl).extendedType->ty; } return ty; } inline Ptr GetOuterStructDecl(const AST::Decl& decl) { auto outerDecl = decl.outerDecl; while (outerDecl && !outerDecl->IsNominalDecl()) { outerDecl = outerDecl->outerDecl; } return outerDecl; } /** Check if given @p decl is generic decl in generic structure declaration. */ inline bool IsGenericInGenericStruct(const AST::Decl& decl) { auto outerDecl = GetOuterStructDecl(decl); return outerDecl && outerDecl->generic && outerDecl->IsNominalDecl() && decl.GetGeneric(); } inline std::vector> GetRealIndexingMembers( const std::vector>& decls, bool inGenericDecl = true) { // NOTE: Filter primary constructors and members generated for test purpuses, for generic decls, // to get fixed members for indexing usage. std::vector> ret; for (auto& member : decls) { if ((inGenericDecl && member->astKind == AST::ASTKind::PRIMARY_CTOR_DECL) || TestManager::IsDeclGeneratedForTest(*member) ) { continue; } (void)ret.emplace_back(member.get()); } return ret; } inline void WorkForMembers(AST::Decl& decl, const std::function& worker) { if (decl.astKind == AST::ASTKind::PROP_DECL) { auto& pd = static_cast(decl); std::for_each(pd.getters.begin(), pd.getters.end(), [&worker](auto& fd) { worker(*fd); }); std::for_each(pd.setters.begin(), pd.setters.end(), [&worker](auto& fd) { worker(*fd); }); } else { worker(decl); } } inline bool NeedSwitchContext(const AST::Decl& decl) { auto outerDecl = GetOuterStructDecl(decl); return decl.IsNominalDecl() || (outerDecl && outerDecl->IsNominalDecl() && (decl.GetGeneric() || decl.TestAttr(AST::Attribute::IMPORTED))); } } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Sema/GenericInstantiation/InstantiatedExtendRecorder.cpp000066400000000000000000000150711510705540100306010ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * InstantiatedExtendRecorder is the class to check used extend decl for each sema type. * NOTE: this should be used before instantiated pointer rearrange. */ #include "InstantiatedExtendRecorder.h" #include "ExtendBoxMarker.h" #include "ImplUtils.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Walker.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace AST; GenericInstantiationManager::InstantiatedExtendRecorder::InstantiatedExtendRecorder( GenericInstantiationManagerImpl& gim, TypeManager& tyMgr) : gim(gim), typeManager(tyMgr), promotion(tyMgr), recorderId(AST::Walker::GetNextWalkerID()) { extendRecorder = [this](auto node) { return node ? RecordUsedExtendDecl(*node) : VisitAction::SKIP_CHILDREN; }; } void GenericInstantiationManager::InstantiatedExtendRecorder::operator()(AST::Node& node) { auto process = [this](auto node) { // Do not consider boxed extends. // Walk node to record used extends as function call. // eg: 1. 'obj.extendFunction' -> collect extend decl which defined the 'extendFunction'. // 2. func test(a: T) where T <: I { a.interfaceFunction } // collect extend decl of type T <: I which implement the 'interfaceFunction'. Walker(node, recorderId, extendRecorder, gim.contextReset).Walk(); }; if (auto pkg = DynamicCast(&node); pkg) { for (auto& it : pkg->files) { process(it.get()); } for (auto srcFunc : pkg->srcImportedNonGenericDecls) { process(srcFunc); } } else { process(&node); } } VisitAction GenericInstantiationManager::InstantiatedExtendRecorder::RecordUsedExtendDecl(Node& node) { VisitAction action = gim.CheckVisitedNode(&node); if (action != VisitAction::WALK_CHILDREN) { return action; } if (auto expr = DynamicCast(&node); expr && expr->desugarExpr) { Walker(expr->desugarExpr.get(), recorderId, extendRecorder, gim.contextReset).Walk(); return VisitAction::SKIP_CHILDREN; } switch (node.astKind) { case ASTKind::REF_EXPR: RecordExtendForRefExpr(*StaticAs(&node)); break; case ASTKind::MEMBER_ACCESS: RecordExtendForMemberAccess(*StaticAs(&node)); break; default: break; } return VisitAction::WALK_CHILDREN; } /** * For the case: 'extendFunc()' directly called in structDecl, record extendDecl which defined extendFunc. */ void GenericInstantiationManager::InstantiatedExtendRecorder::RecordExtendForRefExpr(const RefExpr& re) { bool ignored = !Ty::IsTyCorrect(re.ty) || !re.ref.target || re.ref.target->IsBuiltIn() || !re.ref.target->TestAttr(Attribute::IN_EXTEND); if (ignored || gim.structContext.empty()) { return; } auto extend = re.ref.target->outerDecl; CJC_ASSERT(extend && extend->astKind == ASTKind::EXTEND_DECL); auto structDecl = gim.GetStructDeclByContext(); CJC_ASSERT(structDecl); auto baseTy = GetDeclTy(*structDecl); bool invalid = !Ty::IsTyCorrect(baseTy) || !Ty::IsTyCorrect(extend->ty) || baseTy->HasGeneric(); if (invalid) { return; } auto promoteRes = promotion.Promote(*baseTy, *extend->ty); if (promoteRes.empty()) { InternalError("generic instantiation failed"); return; } auto promotedTy = *promoteRes.begin(); typeManager.RecordUsedGenericExtend(*promotedTy, RawStaticCast(extend)); } /** * For cases: * 1. 'obj.extendFunc', record extendDecl which defined extendFunc. * 2. 'obj.interfaceFunc' which belong to a generic definition, eg: * func test(a: T) where T <: ToString { a.toString() } * 'toString' may be rearraged to extend function after rearrange step in generic instantiation. */ void GenericInstantiationManager::InstantiatedExtendRecorder::RecordExtendForMemberAccess(const MemberAccess& ma) { auto ignored = !ma.target || !ma.baseExpr || !Ty::IsTyCorrect(ma.baseExpr->ty) || ma.baseExpr->ty->HasGeneric() || !ma.target->outerDecl; if (ignored) { return; } auto outerDecl = ma.target->outerDecl; if (auto fd = DynamicCast(ma.target); fd && outerDecl->astKind == ASTKind::INTERFACE_DECL) { RecordImplExtendDecl(*ma.baseExpr->ty, *fd, ma.matchedParentTy); } else if (outerDecl->astKind == ASTKind::EXTEND_DECL) { auto promoteRes = promotion.Promote(*ma.baseExpr->ty, *outerDecl->ty); if (promoteRes.empty()) { InternalError("generic instantiation failed"); return; } auto promotedTy = *promoteRes.begin(); typeManager.RecordUsedGenericExtend(*promotedTy, RawStaticCast(outerDecl)); } } void GenericInstantiationManager::InstantiatedExtendRecorder::RecordImplExtendDecl( Ty& ty, FuncDecl& fd, Ptr upperTy) { auto baseTy = typeManager.GetTyForExtendMap(ty); auto genericFd = RawStaticCast(gim.GetGeneralDecl(fd)); CJC_ASSERT(genericFd->astKind == AST::ASTKind::FUNC_DECL); auto declMap = gim.abstractFuncToDeclMap.find(std::make_pair(baseTy, genericFd)); if (declMap == gim.abstractFuncToDeclMap.end()) { return; } Ptr extend = nullptr; // All candidates have satisfied functions, choose most matched decl. for (auto [baseDecl, _] : declMap->second) { CJC_ASSERT(baseDecl); if (baseDecl->astKind != ASTKind::EXTEND_DECL) { continue; // Function implemented in origin decl, ignored. } if (Ty::IsTyCorrect(upperTy)) { auto& inheritedTys = RawStaticCast(baseDecl)->inheritedTypes; bool isImplemented = std::any_of(inheritedTys.begin(), inheritedTys.end(), [this, upperTy](auto& type) { return typeManager.IsSubtype(type->ty, upperTy); }); if (isImplemented) { // If interface implementation check passed, store and break; extend = baseDecl; break; } } // If 'upperTy' check ignored or failed, set decl and continue if exist next match. extend = baseDecl; } if (extend) { typeManager.RecordUsedGenericExtend(ty, RawStaticCast(extend)); } } cangjie_compiler-1.0.7/src/Sema/GenericInstantiation/InstantiatedExtendRecorder.h000066400000000000000000000027311510705540100302450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * InstantiatedExtendRecorder is the class to check used extend decl for each sema type. * NOTE: this should be used before instantiated pointer rearrange. */ #ifndef CANGJIE_SEMA_INSTANTIATED_EXTEND_RECORDER_H #define CANGJIE_SEMA_INSTANTIATED_EXTEND_RECORDER_H #include "GenericInstantiationManagerImpl.h" #include "Promotion.h" #include "cangjie/AST/Node.h" #include "cangjie/Sema/TypeManager.h" namespace Cangjie { class GenericInstantiationManager::InstantiatedExtendRecorder { public: InstantiatedExtendRecorder(GenericInstantiationManagerImpl& gim, TypeManager& tyMgr); ~InstantiatedExtendRecorder() = default; void operator()(AST::Node& node); private: /** Walker function to record extend. */ AST::VisitAction RecordUsedExtendDecl(AST::Node& node); void RecordExtendForRefExpr(const AST::RefExpr& re); void RecordExtendForMemberAccess(const AST::MemberAccess& ma); void RecordImplExtendDecl(AST::Ty& ty, AST::FuncDecl& fd, Ptr upperTy); GenericInstantiationManagerImpl& gim; TypeManager& typeManager; Promotion promotion; unsigned recorderId; std::function)> extendRecorder; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Sema/GenericInstantiation/PartialInstantiation.cpp000066400000000000000000002316711510705540100274630ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * PartialInstantiation is the class to partial instantiation. */ #include "PartialInstantiation.h" #include "ImplUtils.h" #include "cangjie/AST/Create.h" #include "cangjie/Basic/Match.h" #include "cangjie/Utils/Casting.h" namespace Cangjie { using namespace Cangjie::AST; using namespace Cangjie::Meta; namespace { auto g_optLevel = GlobalOptions::OptimizationLevel::O0; inline bool IsCPointerFrozenMember(const Decl& decl) { if (decl.fullPackageName != CORE_PACKAGE_NAME || decl.astKind != ASTKind::FUNC_DECL || !decl.HasAnno(AnnotationKind::FROZEN) || !decl.outerDecl || decl.outerDecl->astKind != ASTKind::EXTEND_DECL) { return false; } return StaticCast(decl.outerDecl)->extendedType->ty->IsPointer(); } } // namespace void SetOptLevel(const Cangjie::GlobalOptions& opts) { g_optLevel = opts.optimizationLevel; } GlobalOptions::OptimizationLevel GetOptLevel() { return g_optLevel; } bool IsOpenDecl(const Decl& decl) { if (!decl.IsClassLikeDecl()) { return false; } // Note: Non-open class will mark as 'OPEN' and 'OPEN_TO_MOCK' when mock is on. if (decl.TestAttr(Attribute::OPEN_TO_MOCK)) { return false; } return decl.TestAnyAttr(AST::Attribute::ABSTRACT, AST::Attribute::OPEN) || decl.astKind == ASTKind::INTERFACE_DECL; }; /** * Check whether the location where the instantiation is triggered is in the context with Open semantics. * Return true if expr is in a member of an open class or interface. */ bool IsInOpenContext(const std::vector>& contextDecl) { bool isInOpenContext = !contextDecl.empty(); if (isInOpenContext) { auto toplevelDecl = contextDecl.front(); if (toplevelDecl->IsNominalDecl()) { isInOpenContext = IsOpenDecl(*toplevelDecl); } else { // In global function if outer is null, and global function context can be instantated. auto outer = GetOuterStructDecl(*toplevelDecl); isInOpenContext = outer == nullptr || IsOpenDecl(*outer); } } return isInOpenContext; } bool RequireInstantiation(const Decl& decl, bool isInOpenContext) { if (IsCPointerFrozenMember(decl)) { return true; } if (decl.astKind == ASTKind::INTERFACE_DECL) { return false; } if (decl.IsNominalDecl()) { if (g_optLevel >= GlobalOptions::OptimizationLevel::O2 && !decl.TestAttr(Attribute::IMPORTED)) { return true; } if (decl.TestAttr(Attribute::GENERIC)) { auto& members = decl.GetMemberDecls(); return std::any_of(members.begin(), members.end(), [&isInOpenContext](auto& member) { return RequireInstantiation(*member, isInOpenContext); }); } return false; } // If the current reference comes from an open context, that context may be inherited into a subtype, and the // declaration cannot be instantiated as a unique solution. // For example: // interface I { // static func foo(): Int64 // func call() { foo() } // } // class A <: I { static func foo(): Int64 {1} } // class B <: I { static func foo(): Int64 {2} } // CallExpr `foo()` cannot pointer to any instantation version, it must be a static-invoke. if (IsVirtualMember(decl) || isInOpenContext) { return false; } if (decl.IsConst()) { return true; } if (decl.TestAttr(Attribute::CONTAINS_MOCK_CREATION_CALL) && decl.HasAnno(AnnotationKind::FROZEN)) { return true; } if (g_optLevel < GlobalOptions::OptimizationLevel::O2) { return false; } return !IsInDeclWithAttribute(decl, Attribute::IMPORTED) || decl.HasAnno(AnnotationKind::FROZEN); } void DefaultVisitFunc(const Node& /* source */, const Node& /* target */) { } OwnedPtr InstantiateGeneric(const Generic& generic, const VisitFunc& visitor) { auto ret = MakeOwned(); for (auto& it : generic.typeParameters) { auto gpd = MakeOwned(); auto d = PartialInstantiation::Instantiate(it.get(), visitor); gpd.reset(As(d.release())); ret->typeParameters.push_back(std::move(gpd)); } for (auto& it : generic.genericConstraints) { ret->genericConstraints.push_back(PartialInstantiation::Instantiate(it.get(), visitor)); } ret->leftAnglePos = generic.leftAnglePos; ret->rightAnglePos = generic.rightAnglePos; return ret; } MacroInvocation InstantiateMacroInvocation(const MacroInvocation& me) { MacroInvocation mi; mi.fullName = me.fullName; mi.fullNameDotPos = me.fullNameDotPos; mi.identifier = me.identifier; mi.identifierPos = me.identifierPos; mi.leftSquarePos = me.leftSquarePos; mi.attrs = me.attrs; mi.rightSquarePos = me.rightSquarePos; mi.leftParenPos = me.leftParenPos; mi.args = me.args; mi.rightParenPos = me.rightParenPos; mi.newTokens = me.newTokens; mi.newTokensStr = me.newTokensStr; mi.parent = me.parent; mi.scope = me.scope; return mi; } namespace { // This function should be called in each node which inherit Node directly. void CopyNodeField(Ptr ret, const Node& e) { ret->begin = e.begin; ret->end = e.end; ret->ty = e.ty; ret->curMacroCall = e.curMacroCall; ret->isInMacroCall = e.isInMacroCall; CopyNodeScopeInfo(ret, &e); ret->CopyAttrs(e.GetAttrs()); } } // namespace std::unordered_map, Ptr> PartialInstantiation::ins2generic = {}; std::unordered_map, std::unordered_set>> PartialInstantiation::generic2ins = {}; // Instantiate nodes which inherit Node indirectly, should only be call by clone function which inherit Node directly. OwnedPtr PartialInstantiation::InstantiateGenericParamDecl(const GenericParamDecl& gpd) { auto ret = MakeOwned(); ret->outerDecl = gpd.outerDecl; ret->commaPos = gpd.commaPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateFuncParam(const FuncParam& fp, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->colonPos = fp.colonPos; ret->assignment = InstantiateExpr(fp.assignment.get(), visitor); if (fp.desugarDecl) { auto decl = InstantiateDecl(fp.desugarDecl.get(), visitor); decl->genericDecl = fp.desugarDecl.get(); ret->desugarDecl.reset(RawStaticCast(decl.release())); } ret->isNamedParam = fp.isNamedParam; ret->isMemberParam = fp.isMemberParam; ret->commaPos = fp.commaPos; ret->notMarkPos = fp.notMarkPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateVarWithPatternDecl( const VarWithPatternDecl& vwpd, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->type = InstantiateType(vwpd.type.get(), visitor); ret->assignPos = vwpd.assignPos; ret->colonPos = vwpd.colonPos; ret->isVar = vwpd.isVar; ret->isConst = vwpd.isConst; ret->irrefutablePattern = InstantiatePattern(vwpd.irrefutablePattern.get(), visitor); ret->initializer = InstantiateExpr(vwpd.initializer.get(), visitor); return ret; } OwnedPtr PartialInstantiation::InstantiateVarDecl(const VarDecl& vd, const VisitFunc& visitor) { auto ret = match(vd)([&visitor](const FuncParam& e) { return InstantiateFuncParam(e, visitor); }, []() { return MakeOwned(); }); // Instantiate field in VarDecl. ret->type = InstantiateType(vd.type.get(), visitor); ret->colonPos = vd.colonPos; ret->initializer = InstantiateExpr(vd.initializer.get(), visitor); ret->assignPos = vd.assignPos; ret->isVar = vd.isVar; ret->isConst = vd.isConst; ret->isResourceVar = vd.isResourceVar; ret->isIdentifierCompilerAdd = vd.isIdentifierCompilerAdd; ret->parentPattern = vd.parentPattern; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateFuncDecl(const FuncDecl& fd, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->leftParenPos = fd.leftParenPos; ret->rightParenPos = fd.rightParenPos; CJC_NULLPTR_CHECK(fd.funcBody); ret->funcBody = InstantiateNode(fd.funcBody.get(), visitor); ret->funcBody->funcDecl = ret.get(); // Reset funcDecl of funcBody to new function. ret->propDecl = fd.propDecl; ret->constructorCall = fd.constructorCall; ret->op = fd.op; ret->ownerFunc = fd.ownerFunc; ret->overflowStrategy = fd.overflowStrategy; ret->isFastNative = fd.isFastNative; ret->isGetter = fd.isGetter; ret->isSetter = fd.isSetter; ret->isInline = fd.isInline; ret->isConst = fd.isConst; ret->isFrozen = fd.isFrozen; ret->variadicArgIndex = fd.variadicArgIndex; ret->hasVariableLenArg = fd.hasVariableLenArg; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiatePrimaryCtorDecl(const PrimaryCtorDecl& pcd, const VisitFunc& visitor) { auto ret = MakeOwned(); CJC_NULLPTR_CHECK(pcd.funcBody); ret->funcBody = InstantiateNode(pcd.funcBody.get(), visitor); ret->overflowStrategy = pcd.overflowStrategy; ret->isFastNative = pcd.isFastNative; ret->isConst = pcd.isConst; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiatePropDecl(const PropDecl& pd, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->type = InstantiateType(pd.type.get(), visitor); ret->colonPos = pd.colonPos; ret->leftCurlPos = pd.leftCurlPos; ret->rightCurlPos = pd.rightCurlPos; ret->isVar = pd.isVar; ret->isConst = pd.isConst; for (auto& func : pd.setters) { auto decl = InstantiateDecl(func.get(), visitor); auto fd = MakeOwned(); fd.reset(As(decl.release())); fd->EnableAttr(Attribute::COMPILER_ADD); ret->setters.push_back(std::move(fd)); } for (auto& func : pd.getters) { auto decl = InstantiateDecl(func.get(), visitor); auto fd = MakeOwned(); fd.reset(As(decl.release())); fd->EnableAttr(Attribute::COMPILER_ADD); ret->getters.push_back(std::move(fd)); } ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateRefType(const RefType& type, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), type); ret->ref = type.ref; ret->leftAnglePos = type.leftAnglePos; ret->typeArguments = std::vector>(); for (auto& it : type.typeArguments) { ret->typeArguments.push_back(InstantiateType(it.get(), visitor)); } ret->rightAnglePos = type.rightAnglePos; ret->commaPos = type.commaPos; ret->bitAndPos = type.bitAndPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateExtendDecl(const ExtendDecl& ed, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->extendedType = InstantiateType(ed.extendedType.get(), visitor); for (auto& interface : ed.inheritedTypes) { ret->inheritedTypes.push_back(InstantiateType(interface.get(), visitor)); } for (auto& member : ed.members) { if (RequireInstantiation(*member)) { ret->members.push_back(InstantiateDecl(member.get(), visitor)); } } if (ed.generic) { ret->generic = InstantiateGeneric(*ed.generic, visitor); } if (ed.bodyScope) { ret->bodyScope = MakeOwned(); CopyNodeScopeInfo(ret->bodyScope.get(), ed.bodyScope.get()); } ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateMacroExpandDecl(const MacroExpandDecl& med) { auto ret = MakeOwned(); ret->invocation = InstantiateMacroInvocation(med.invocation); ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateStructDecl(const StructDecl& sd, const VisitFunc& visitor) { auto ret = MakeOwned(); for (auto& it : sd.inheritedTypes) { ret->inheritedTypes.push_back(InstantiateType(it.get(), visitor)); } ret->body = InstantiateNode(sd.body.get(), visitor); if (sd.generic) { ret->generic = InstantiateGeneric(*sd.generic, visitor); } ret->upperBoundPos = sd.upperBoundPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateClassDecl(const ClassDecl& cd, const VisitFunc& visitor) { auto ret = MakeOwned(); for (auto& it : cd.inheritedTypes) { ret->inheritedTypes.push_back(InstantiateType(it.get(), visitor)); } ret->body = InstantiateNode(cd.body.get(), visitor); if (cd.generic) { ret->generic = InstantiateGeneric(*cd.generic, visitor); } ret->upperBoundPos = cd.upperBoundPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateInterfaceDecl(const InterfaceDecl& id, const VisitFunc& visitor) { auto ret = MakeOwned(); for (auto& it : id.inheritedTypes) { ret->inheritedTypes.push_back(InstantiateType(it.get(), visitor)); } ret->body = InstantiateNode(id.body.get(), visitor); if (id.generic) { ret->generic = InstantiateGeneric(*id.generic, visitor); } ret->upperBoundPos = id.upperBoundPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateEnumDecl(const EnumDecl& ed, const VisitFunc& visitor) { auto ret = MakeOwned(); for (auto& it : ed.inheritedTypes) { ret->inheritedTypes.push_back(InstantiateType(it.get(), visitor)); } for (auto& it : ed.constructors) { ret->constructors.push_back(InstantiateDecl(it.get(), visitor)); } for (auto& func : ed.members) { if (!RequireInstantiation(*func)) { continue; } ret->members.push_back(InstantiateDecl(func.get(), visitor)); } if (ed.generic) { ret->generic = InstantiateGeneric(*ed.generic, visitor); } ret->hasArguments = ed.hasArguments; if (ed.bodyScope) { ret->bodyScope = MakeOwned(); CopyNodeScopeInfo(ret->bodyScope.get(), ed.bodyScope.get()); } ret->leftCurlPos = ed.leftCurlPos; ret->bitOrPosVector = ed.bitOrPosVector; ret->rightCurlPos = ed.rightCurlPos; ret->upperBoundPos = ed.upperBoundPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateTypeAliasDecl(const TypeAliasDecl& tad, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->assignPos = tad.assignPos; ret->type = InstantiateType(tad.type.get(), visitor); if (tad.generic != nullptr) { ret->generic = InstantiateGeneric(*tad.generic, visitor); } ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateQualifiedType( const QualifiedType& node, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->baseType = InstantiateType(node.baseType.get(), visitor); ret->field = node.field; ret->target = node.target; for (auto& it : node.typeArguments) { ret->typeArguments.push_back(InstantiateType(it.get(), visitor)); } return ret; } OwnedPtr PartialInstantiation::InstantiateParenType(const ParenType& node, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->type = InstantiateType(node.type.get(), visitor); ret->leftParenPos = node.leftParenPos; ret->rightParenPos = node.rightParenPos; return ret; } OwnedPtr PartialInstantiation::InstantiateOptionType(const OptionType& node, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->componentType = InstantiateType(node.componentType.get(), visitor); ret->questNum = node.questNum; ret->questVector = node.questVector; if (node.desugarType != nullptr) { ret->desugarType = Instantiate(node.desugarType.get(), visitor); } return ret; } OwnedPtr PartialInstantiation::InstantiateFuncType(const FuncType& node, const VisitFunc& visitor) { auto ret = MakeOwned(); for (auto& paramType : node.paramTypes) { ret->paramTypes.emplace_back(InstantiateType(paramType.get(), visitor)); } ret->retType = InstantiateType(node.retType.get(), visitor); ret->isC = node.isC; ret->leftParenPos = node.leftParenPos; ret->rightParenPos = node.rightParenPos; ret->arrowPos = node.arrowPos; return ret; } OwnedPtr PartialInstantiation::InstantiateTupleType(const TupleType& node, const VisitFunc& visitor) { auto ret = MakeOwned(); for (auto& it : node.fieldTypes) { ret->fieldTypes.push_back(InstantiateType(it.get(), visitor)); } ret->leftParenPos = node.leftParenPos; ret->rightParenPos = node.rightParenPos; ret->commaPosVector = node.commaPosVector; return ret; } OwnedPtr PartialInstantiation::InstantiateConstantType(const ConstantType& node, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->constantExpr = InstantiateExpr(node.constantExpr.get(), visitor); ret->dollarPos = node.dollarPos; return ret; } OwnedPtr PartialInstantiation::InstantiateVArrayType(const VArrayType& node, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->leftAnglePos = node.leftAnglePos; ret->typeArgument = InstantiateType(node.typeArgument.get(), visitor); ret->constantType = InstantiateType(node.constantType.get(), visitor); ret->rightAnglePos = node.rightAnglePos; return ret; } // Instantiate nodes which inherit Node directly. OwnedPtr PartialInstantiation::InstantiateType(Ptr type, const VisitFunc& visitor) { if (!type) { return OwnedPtr(); } auto ret = match(*type)([&visitor](const RefType& e) { return OwnedPtr(InstantiateRefType(e, visitor)); }, [](const PrimitiveType& e) { return OwnedPtr(MakeOwned(e)); }, [&visitor](const ParenType& e) { return OwnedPtr(InstantiateParenType(e, visitor)); }, [&visitor](const QualifiedType& e) { return OwnedPtr(InstantiateQualifiedType(e, visitor)); }, [&visitor](const OptionType& e) { return OwnedPtr(InstantiateOptionType(e, visitor)); }, [&visitor](const FuncType& e) { return OwnedPtr(InstantiateFuncType(e, visitor)); }, [&visitor](const TupleType& e) { return OwnedPtr(InstantiateTupleType(e, visitor)); }, [&visitor](const ConstantType& e) { return OwnedPtr(InstantiateConstantType(e, visitor)); }, [&visitor](const VArrayType& e) { return OwnedPtr(InstantiateVArrayType(e, visitor)); }, [](const ThisType& e) { return OwnedPtr(MakeOwned(e)); }, [](const InvalidType& e) { return OwnedPtr(MakeOwned(e)); }, [](const Type& e) { return OwnedPtr(MakeOwned(e)); }, []() { // Invalid case. return MakeOwned(); }); CJC_ASSERT(ret && ret->astKind == type->astKind); ret->commaPos = type->commaPos; ret->bitAndPos = type->bitAndPos; ret->typeParameterName = type->typeParameterName; ret->typeParameterNameIsRawId = type->typeParameterNameIsRawId; ret->typePos = type->typePos; ret->colonPos = type->colonPos; CopyNodeField(ret.get(), *type); visitor(*type, *ret); ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateMacroExpandExpr( const MacroExpandExpr& mee, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->identifier = mee.identifier; expr->invocation = InstantiateMacroInvocation(mee.invocation); for (auto& anno : mee.annotations) { expr->annotations.emplace_back(InstantiateNode(anno.get(), visitor)); } expr->modifiers.insert(mee.modifiers.begin(), mee.modifiers.end()); return expr; } OwnedPtr PartialInstantiation::InstantiateTokenPart(const TokenPart& tp, const VisitFunc& /* visitor */) { auto expr = MakeOwned(); expr->tokens.assign(tp.tokens.begin(), tp.tokens.end()); return expr; } OwnedPtr PartialInstantiation::InstantiateQuoteExpr(const QuoteExpr& qe, const VisitFunc& visitor) { auto expr = MakeOwned(); for (auto& i : qe.exprs) { expr->exprs.push_back(InstantiateExpr(i.get(), visitor)); } expr->quotePos = qe.quotePos; expr->leftParenPos = qe.leftParenPos; expr->rightParenPos = qe.rightParenPos; return expr; } OwnedPtr PartialInstantiation::InstantiateIfExpr(const IfExpr& ie, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->ifPos = ie.ifPos; expr->leftParenPos = ie.leftParenPos; expr->condExpr = InstantiateExpr(ie.condExpr.get(), visitor); expr->rightParenPos = ie.rightParenPos; auto n = InstantiateExpr(ie.thenBody.get(), visitor); expr->thenBody.reset(As(n.release())); expr->hasElse = ie.hasElse; expr->isElseIf = ie.isElseIf; expr->elsePos = ie.elsePos; expr->elseBody = InstantiateExpr(ie.elseBody.get(), visitor); return expr; } OwnedPtr PartialInstantiation::InstantiateTryExpr(const TryExpr& te, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->tryPos = te.tryPos; expr->lParen = te.lParen; for (auto& resource : te.resourceSpec) { expr->resourceSpec.push_back( OwnedPtr(As(InstantiateDecl(resource.get(), visitor).release()))); } expr->isDesugaredFromSyncBlock = te.isDesugaredFromSyncBlock; expr->isDesugaredFromTryWithResources = te.isDesugaredFromTryWithResources; expr->isIllegalResourceSpec = te.isIllegalResourceSpec; expr->tryBlock = InstantiateExpr(te.tryBlock.get(), visitor); for (size_t i = 0; i < te.catchPosVector.size(); i++) { expr->catchPosVector.push_back(te.catchPosVector[i]); } CJC_ASSERT(te.catchBlocks.size() == te.catchPatterns.size()); for (size_t i = 0; i < te.catchBlocks.size(); i++) { expr->catchBlocks.push_back(InstantiateExpr(te.catchBlocks[i].get(), visitor)); expr->catchPatterns.push_back(InstantiatePattern(te.catchPatterns[i].get(), visitor)); } expr->finallyPos = te.finallyPos; expr->finallyBlock = InstantiateExpr(te.finallyBlock.get(), visitor); expr->resourceSpecCommaPos = te.resourceSpecCommaPos; expr->rParen = te.rParen; expr->catchLParenPosVector = te.catchLParenPosVector; expr->catchRParenPosVector = te.catchRParenPosVector; return expr; } OwnedPtr PartialInstantiation::InstantiateThrowExpr(const ThrowExpr& te, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->throwPos = te.throwPos; expr->expr = InstantiateExpr(te.expr.get(), visitor); return expr; } OwnedPtr PartialInstantiation::InstantiateReturnExpr(const ReturnExpr& re, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->returnPos = re.returnPos; expr->expr = InstantiateExpr(re.expr.get(), visitor); expr->refFuncBody = re.refFuncBody; return expr; } OwnedPtr PartialInstantiation::InstantiateWhileExpr(const WhileExpr& we, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->whilePos = we.whilePos; expr->leftParenPos = we.leftParenPos; expr->condExpr = InstantiateExpr(we.condExpr.get(), visitor); expr->body = InstantiateExpr(we.body.get(), visitor); expr->rightParenPos = we.rightParenPos; return expr; } OwnedPtr PartialInstantiation::InstantiateDoWhileExpr(const DoWhileExpr& dwe, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->doPos = dwe.doPos; expr->body = InstantiateExpr(dwe.body.get(), visitor); expr->whilePos = dwe.whilePos; expr->leftParenPos = dwe.leftParenPos; expr->condExpr = InstantiateExpr(dwe.condExpr.get(), visitor); expr->rightParenPos = dwe.rightParenPos; return expr; } OwnedPtr PartialInstantiation::InstantiateAssignExpr(const AssignExpr& ae, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->leftValue = InstantiateExpr(ae.leftValue.get(), visitor); expr->op = ae.op; expr->rightExpr = InstantiateExpr(ae.rightExpr.get(), visitor); expr->isCompound = ae.isCompound; expr->assignPos = ae.assignPos; return expr; } OwnedPtr PartialInstantiation::InstantiateIncOrDecExpr(const IncOrDecExpr& ide, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->op = ide.op; expr->operatorPos = ide.operatorPos; expr->expr = InstantiateExpr(ide.expr.get(), visitor); return expr; } OwnedPtr PartialInstantiation::InstantiateUnaryExpr(const UnaryExpr& ue, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->op = ue.op; expr->expr = InstantiateExpr(ue.expr.get(), visitor); expr->operatorPos = ue.operatorPos; return expr; } OwnedPtr PartialInstantiation::InstantiateBinaryExpr(const BinaryExpr& be, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->op = be.op; expr->leftExpr = InstantiateExpr(be.leftExpr.get(), visitor); expr->rightExpr = InstantiateExpr(be.rightExpr.get(), visitor); expr->operatorPos = be.operatorPos; return expr; } OwnedPtr PartialInstantiation::InstantiateRangeExpr(const RangeExpr& re, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->startExpr = InstantiateExpr(re.startExpr.get(), visitor); expr->rangePos = re.rangePos; expr->stopExpr = InstantiateExpr(re.stopExpr.get(), visitor); expr->colonPos = re.colonPos; expr->stepExpr = InstantiateExpr(re.stepExpr.get(), visitor); expr->isClosed = re.isClosed; expr->decl = re.decl; return expr; } OwnedPtr PartialInstantiation::InstantiateSubscriptExpr( const SubscriptExpr& se, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->baseExpr = InstantiateExpr(se.baseExpr.get(), visitor); expr->leftParenPos = se.leftParenPos; for (auto& it : se.indexExprs) { expr->indexExprs.push_back(InstantiateExpr(it.get(), visitor)); } expr->commaPos = se.commaPos; expr->rightParenPos = se.rightParenPos; expr->isTupleAccess = se.isTupleAccess; return expr; } OwnedPtr PartialInstantiation::InstantiateMemberAccess(const MemberAccess& ma, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->baseExpr = InstantiateExpr(ma.baseExpr.get(), visitor); expr->dotPos = ma.dotPos; expr->field = ma.field; expr->target = ma.target; expr->targets = ma.targets; expr->isPattern = ma.isPattern; expr->isAlone = ma.isAlone; expr->instTys = ma.instTys; expr->matchedParentTy = ma.matchedParentTy; expr->callOrPattern = ma.callOrPattern; expr->foundUpperBoundMap = ma.foundUpperBoundMap; expr->isExposedAccess = ma.isExposedAccess; for (auto& it : ma.typeArguments) { expr->typeArguments.push_back(InstantiateType(it.get(), visitor)); } return expr; } OwnedPtr PartialInstantiation::InstantiateCallExpr(const CallExpr& ce, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->baseFunc = InstantiateExpr(ce.baseFunc.get(), visitor); expr->leftParenPos = ce.leftParenPos; expr->needCheckToTokens = ce.needCheckToTokens; std::unordered_map, Ptr> cloneTable; // For replace desugarArgs for (auto& it : ce.args) { expr->args.push_back(InstantiateNode(it.get(), visitor)); cloneTable[it.get()] = expr->args.back().get(); } if (ce.desugarArgs.has_value()) { expr->defaultArgs = std::vector>(); for (auto& it : ce.defaultArgs) { expr->defaultArgs.push_back(InstantiateNode(it.get(), visitor)); cloneTable[it.get()] = expr->defaultArgs.back().get(); } expr->desugarArgs = std::vector>(); for (auto& it : ce.desugarArgs.value()) { expr->desugarArgs.value().push_back(cloneTable[it]); } } expr->rightParenPos = ce.rightParenPos; expr->resolvedFunction = ce.resolvedFunction; expr->callKind = ce.callKind; expr->sugarKind = ce.sugarKind; if (auto ma = DynamicCast(expr->baseFunc.get()); ma) { ma->callOrPattern = expr.get(); } return expr; } OwnedPtr PartialInstantiation::InstantiateParenExpr(const ParenExpr& pe, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->leftParenPos = pe.leftParenPos; expr->expr = InstantiateExpr(pe.expr.get(), visitor); expr->rightParenPos = pe.rightParenPos; return expr; } OwnedPtr PartialInstantiation::InstantiateLambdaExpr(const LambdaExpr& le, const VisitFunc& visitor) { auto expr = MakeOwned(InstantiateNode(le.funcBody.get(), visitor)); if (le.TestAttr(Attribute::MOCK_SUPPORTED)) { expr->EnableAttr(Attribute::MOCK_SUPPORTED); } return expr; } OwnedPtr PartialInstantiation::InstantiateLitConstExpr(const LitConstExpr& lce, const VisitFunc& visitor) { auto expr = MakeOwned(lce.kind, lce.stringValue); expr->codepoint = lce.codepoint; expr->delimiterNum = lce.delimiterNum; expr->stringKind = lce.stringKind; expr->rawString = lce.rawString; if (lce.ref) { expr->ref = InstantiateRefType(*lce.ref, visitor); visitor(*lce.ref, *expr->ref); } if (lce.siExpr) { expr->siExpr = InstantiateExpr(lce.siExpr.get(), visitor); } return expr; } OwnedPtr PartialInstantiation::InstantiateInterpolationExpr( const InterpolationExpr& ie, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->rawString = ie.rawString; expr->dollarPos = ie.dollarPos; expr->block = InstantiateExpr(ie.block.get(), visitor); return expr; } OwnedPtr PartialInstantiation::InstantiateStrInterpolationExpr( const StrInterpolationExpr& sie, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->rawString = sie.rawString; expr->strParts = sie.strParts; for (auto& it : sie.strPartExprs) { expr->strPartExprs.push_back(InstantiateExpr(it.get(), visitor)); } return expr; } OwnedPtr PartialInstantiation::InstantiateArrayLit(const ArrayLit& al, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->leftSquarePos = al.leftSquarePos; for (auto& it : al.children) { expr->children.push_back(InstantiateExpr(it.get(), visitor)); } expr->commaPosVector = al.commaPosVector; expr->rightSquarePos = al.rightSquarePos; expr->initFunc = al.initFunc; return expr; } OwnedPtr PartialInstantiation::InstantiateArrayExpr(const ArrayExpr& ae, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->type = InstantiateType(ae.type.get(), visitor); expr->leftParenPos = ae.leftParenPos; expr->args.resize(ae.args.size()); for (size_t i = 0; i < ae.args.size(); ++i) { expr->args[i] = InstantiateNode(ae.args[i].get(), visitor); } expr->commaPosVector = ae.commaPosVector; expr->rightParenPos = ae.rightParenPos; expr->initFunc = ae.initFunc; expr->isValueArray = ae.isValueArray; return expr; } OwnedPtr PartialInstantiation::InstantiatePointerExpr(const PointerExpr& ptre, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->type = InstantiateType(ptre.type.get(), visitor); expr->arg = InstantiateNode(ptre.arg.get(), visitor); return expr; } OwnedPtr PartialInstantiation::InstantiateTupleLit(const TupleLit& tl, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->leftParenPos = tl.leftParenPos; for (auto& it : tl.children) { expr->children.push_back(InstantiateExpr(it.get(), visitor)); } expr->commaPosVector = tl.commaPosVector; expr->rightParenPos = tl.rightParenPos; return expr; } OwnedPtr PartialInstantiation::InstantiateRefExpr(const RefExpr& re, const VisitFunc& visitor) { auto expr = CreateRefExpr(re.ref.identifier); expr->leftAnglePos = re.leftAnglePos; expr->ref.target = re.ref.target; expr->ref.targets = re.ref.targets; for (auto& it : re.typeArguments) { expr->typeArguments.push_back(InstantiateType(it.get(), visitor)); } expr->rightAnglePos = re.rightAnglePos; expr->isSuper = re.isSuper; expr->isThis = re.isThis; expr->isAlone = re.isAlone; expr->instTys = re.instTys; expr->matchedParentTy = re.matchedParentTy; expr->callOrPattern = re.callOrPattern; return expr; } OwnedPtr PartialInstantiation::InstantiateForInExpr(const ForInExpr& fie, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->leftParenPos = fie.leftParenPos; expr->rightParenPos = fie.rightParenPos; expr->pattern = InstantiatePattern(fie.pattern.get(), visitor); expr->inPos = fie.inPos; expr->inExpression = InstantiateExpr(fie.inExpression.get(), visitor); expr->wherePos = fie.wherePos; expr->patternGuard = InstantiateExpr(fie.patternGuard.get(), visitor); expr->patternInDesugarExpr = fie.patternInDesugarExpr; expr->body = InstantiateExpr(fie.body.get(), visitor); expr->forInKind = fie.forInKind; return expr; } OwnedPtr PartialInstantiation::InstantiateMatchExpr(const MatchExpr& me, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->matchMode = me.matchMode; expr->sugarKind = me.sugarKind; expr->selector = InstantiateExpr(me.selector.get(), visitor); for (auto& i : me.matchCases) { expr->matchCases.push_back(InstantiateNode(i.get(), visitor)); } for (auto& i : me.matchCaseOthers) { expr->matchCaseOthers.push_back(InstantiateNode(i.get(), visitor)); } expr->leftParenPos = me.leftParenPos; expr->rightParenPos = me.rightParenPos; expr->leftCurlPos = me.leftCurlPos; expr->rightCurlPos = me.rightCurlPos; return expr; } OwnedPtr PartialInstantiation::InstantiateJumpExpr(const JumpExpr& je) { auto expr = MakeOwned(); expr->isBreak = je.isBreak; expr->refLoop = je.refLoop; return expr; } OwnedPtr PartialInstantiation::InstantiateTypeConvExpr(const TypeConvExpr& tce, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->type = OwnedPtr(InstantiateType(tce.type.get(), visitor).release()); expr->expr = InstantiateExpr(tce.expr.get(), visitor); expr->leftParenPos = tce.leftParenPos; expr->rightParenPos = tce.rightParenPos; return expr; } OwnedPtr PartialInstantiation::InstantiateSpawnExpr(const SpawnExpr& se, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->task = InstantiateExpr(se.task.get(), visitor); expr->arg = InstantiateExpr(se.arg.get(), visitor); if (se.futureObj) { auto obj = InstantiateDecl(se.futureObj.get(), visitor); expr->futureObj = OwnedPtr(As(obj.release())); } expr->spawnPos = se.spawnPos; expr->leftParenPos = se.leftParenPos; expr->rightParenPos = se.rightParenPos; return expr; } OwnedPtr PartialInstantiation::InstantiateSynchronizedExpr( const SynchronizedExpr& se, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->mutex = InstantiateExpr(se.mutex.get(), visitor); expr->body = InstantiateExpr(se.body.get(), visitor); expr->syncPos = se.syncPos; expr->leftParenPos = se.leftParenPos; expr->rightParenPos = se.rightParenPos; return expr; } OwnedPtr PartialInstantiation::InstantiateInvalidExpr(const InvalidExpr& ie) { auto expr = MakeOwned(ie.begin); expr->value = ie.value; return expr; } OwnedPtr PartialInstantiation::InstantiateTrailingClosureExpr( const TrailingClosureExpr& tc, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->leftLambda = tc.leftLambda; expr->rightLambda = tc.rightLambda; if (tc.expr) { expr->expr = InstantiateExpr(tc.expr.get(), visitor); } if (tc.lambda) { expr->lambda = InstantiateExpr(tc.lambda.get(), visitor); } return expr; } OwnedPtr PartialInstantiation::InstantiateIsExpr(const IsExpr& ie, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->leftExpr = InstantiateExpr(ie.leftExpr.get(), visitor); expr->isType = InstantiateType(ie.isType.get(), visitor); expr->isPos = ie.isPos; return expr; } OwnedPtr PartialInstantiation::InstantiateAsExpr(const AsExpr& ae, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->leftExpr = InstantiateExpr(ae.leftExpr.get(), visitor); expr->asType = InstantiateType(ae.asType.get(), visitor); expr->asPos = ae.asPos; return expr; } OwnedPtr PartialInstantiation::InstantiateOptionalExpr(const OptionalExpr& oe, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->baseExpr = InstantiateExpr(oe.baseExpr.get(), visitor); expr->questPos = oe.questPos; return expr; } OwnedPtr PartialInstantiation::InstantiateOptionalChainExpr( const OptionalChainExpr& oce, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->expr = InstantiateExpr(oce.expr.get(), visitor); return expr; } OwnedPtr PartialInstantiation::InstantiateLetPatternDestructor( const LetPatternDestructor& ldp, const VisitFunc& visitor) { auto expr = MakeOwned(); expr->backarrowPos = ldp.backarrowPos; for (auto& p : ldp.patterns) { expr->patterns.push_back(InstantiatePattern(p.get(), visitor)); } expr->orPos = ldp.orPos; expr->initializer = InstantiateExpr(ldp.initializer.get(), visitor); return expr; } /** * NOTE: To guarantee the members of Expr is copied, the sub method should not be called outside 'InstantiateExpr'. */ template OwnedPtr PartialInstantiation::InstantiateExpr(Ptr expr, const VisitFunc& visitor) { if (!expr) { return OwnedPtr(); } auto clonedExpr = match(*expr)( // PrimitiveExpr, AdjointExpr are ignored. [&visitor](const IfExpr& ie) { return OwnedPtr(InstantiateIfExpr(ie, visitor)); }, [](const PrimitiveTypeExpr& pte) { return OwnedPtr(MakeOwned(pte.typeKind)); }, [&visitor](const MacroExpandExpr& mee) { return OwnedPtr(InstantiateMacroExpandExpr(mee, visitor)); }, [&visitor](const TokenPart& tp) { return OwnedPtr(InstantiateTokenPart(tp, visitor)); }, [&visitor](const QuoteExpr& qe) { return OwnedPtr(InstantiateQuoteExpr(qe, visitor)); }, [&visitor](const TryExpr& te) { return OwnedPtr(InstantiateTryExpr(te, visitor)); }, [&visitor](const ThrowExpr& te) { return OwnedPtr(InstantiateThrowExpr(te, visitor)); }, [&visitor](const ReturnExpr& re) { return OwnedPtr(InstantiateReturnExpr(re, visitor)); }, [&visitor](const WhileExpr& we) { return OwnedPtr(InstantiateWhileExpr(we, visitor)); }, [&visitor](const DoWhileExpr& dwe) { return OwnedPtr(InstantiateDoWhileExpr(dwe, visitor)); }, [&visitor](const AssignExpr& ae) { return OwnedPtr(InstantiateAssignExpr(ae, visitor)); }, [&visitor](const IncOrDecExpr& ide) { return OwnedPtr(InstantiateIncOrDecExpr(ide, visitor)); }, [&visitor](const UnaryExpr& ue) { return OwnedPtr(InstantiateUnaryExpr(ue, visitor)); }, [&visitor](const BinaryExpr& be) { return OwnedPtr(InstantiateBinaryExpr(be, visitor)); }, [&visitor](const RangeExpr& re) { return OwnedPtr(InstantiateRangeExpr(re, visitor)); }, [&visitor](const SubscriptExpr& se) { return OwnedPtr(InstantiateSubscriptExpr(se, visitor)); }, [&visitor](const MemberAccess& ma) { return OwnedPtr(InstantiateMemberAccess(ma, visitor)); }, [&visitor](const CallExpr& ce) { return OwnedPtr(InstantiateCallExpr(ce, visitor)); }, [&visitor](const ParenExpr& pe) { return OwnedPtr(InstantiateParenExpr(pe, visitor)); }, [&visitor](const LambdaExpr& le) { return OwnedPtr(InstantiateLambdaExpr(le, visitor)); }, [&visitor](const LitConstExpr& lce) { return OwnedPtr(InstantiateLitConstExpr(lce, visitor)); }, [&visitor](const ArrayLit& al) { return OwnedPtr(InstantiateArrayLit(al, visitor)); }, [&visitor](const ArrayExpr& asl) { return OwnedPtr(InstantiateArrayExpr(asl, visitor)); }, [&visitor](const PointerExpr& ptre) { return OwnedPtr(InstantiatePointerExpr(ptre, visitor)); }, [&visitor](const TupleLit& tl) { return OwnedPtr(InstantiateTupleLit(tl, visitor)); }, [&visitor](const RefExpr& re) { return OwnedPtr(InstantiateRefExpr(re, visitor)); }, [&visitor](const ForInExpr& fie) { return OwnedPtr(InstantiateForInExpr(fie, visitor)); }, [&visitor](const MatchExpr& me) { return OwnedPtr(InstantiateMatchExpr(me, visitor)); }, [](const JumpExpr& je) { return OwnedPtr(InstantiateJumpExpr(je)); }, [&visitor](const TypeConvExpr& e) { return OwnedPtr(InstantiateTypeConvExpr(e, visitor)); }, [&visitor](const SpawnExpr& se) { return OwnedPtr(InstantiateSpawnExpr(se, visitor)); }, [&visitor](const SynchronizedExpr& se) { return OwnedPtr(InstantiateSynchronizedExpr(se, visitor)); }, [](const InvalidExpr& ie) { return OwnedPtr(InstantiateInvalidExpr(ie)); }, [&visitor](const Block& b) { return OwnedPtr(InstantiateBlock(b, visitor)); }, [&visitor](const InterpolationExpr& ie) { return OwnedPtr(InstantiateInterpolationExpr(ie, visitor)); }, [&visitor]( const StrInterpolationExpr& sie) { return OwnedPtr(InstantiateStrInterpolationExpr(sie, visitor)); }, [&visitor]( const TrailingClosureExpr& tc) { return OwnedPtr(InstantiateTrailingClosureExpr(tc, visitor)); }, [&visitor](const IsExpr& ie) { return OwnedPtr(InstantiateIsExpr(ie, visitor)); }, [&visitor](const AsExpr& ae) { return OwnedPtr(InstantiateAsExpr(ae, visitor)); }, [&visitor](const OptionalExpr& oe) { return OwnedPtr(InstantiateOptionalExpr(oe, visitor)); }, [&visitor](const OptionalChainExpr& oe) { return OwnedPtr(InstantiateOptionalChainExpr(oe, visitor)); }, [](const WildcardExpr& /* we */) { return OwnedPtr(MakeOwned()); }, [&visitor]( const LetPatternDestructor& ld) { return OwnedPtr(InstantiateLetPatternDestructor(ld, visitor)); }, [&expr]() { // Invalid and ignored cases. auto invalidExpr = MakeOwned(expr->begin); return OwnedPtr(invalidExpr.release()); }); CJC_ASSERT(clonedExpr); if (clonedExpr->astKind != ASTKind::INVALID_EXPR) { CopyNodeField(clonedExpr.get(), *expr); // Instantiate field in Expr. clonedExpr->isConst = expr->isConst; clonedExpr->isBaseFunc = expr->isBaseFunc; clonedExpr->isInFlowExpr = expr->isInFlowExpr; clonedExpr->constNumValue = expr->constNumValue; clonedExpr->hasSemi = expr->hasSemi; clonedExpr->mapExpr = (expr->mapExpr == expr) ? clonedExpr.get() : expr->mapExpr; clonedExpr->sourceExpr = expr->sourceExpr; clonedExpr->overflowStrategy = expr->overflowStrategy; if (expr->desugarExpr && !clonedExpr->desugarExpr) { clonedExpr->desugarExpr = InstantiateExpr(expr->desugarExpr.get(), visitor); } clonedExpr->EnableAttr(Attribute::COMPILER_ADD); visitor(*expr, *clonedExpr); } auto result = DynamicCast(clonedExpr.release()); CJC_NULLPTR_CHECK(result); return OwnedPtr(result); } /** * NOTE: To guarantee the members of Decl is copied, the sub method should not be called outside 'InstantiateDecl'. */ OwnedPtr PartialInstantiation::InstantiateDecl(Ptr decl, const VisitFunc& visitor) { if (!decl) { return OwnedPtr(); } auto ret = match(*decl)([](const GenericParamDecl& e) { return InstantiateGenericParamDecl(e); }, [&visitor](const PropDecl& e) { return InstantiatePropDecl(e, visitor); }, [&visitor](const VarDecl& e) { return InstantiateVarDecl(e, visitor); }, [&visitor](const FuncDecl& e) { return InstantiateFuncDecl(e, visitor); }, [&visitor](const StructDecl& e) { return InstantiateStructDecl(e, visitor); }, [&visitor](const ClassDecl& e) { return InstantiateClassDecl(e, visitor); }, [&visitor](const InterfaceDecl& e) { return InstantiateInterfaceDecl(e, visitor); }, [&visitor](const EnumDecl& e) { return InstantiateEnumDecl(e, visitor); }, [&visitor](const PrimaryCtorDecl& e) { return InstantiatePrimaryCtorDecl(e, visitor); }, [&visitor](const ExtendDecl& e) { return InstantiateExtendDecl(e, visitor); }, [](const MacroExpandDecl& e) { return InstantiateMacroExpandDecl(e); }, [&visitor](const VarWithPatternDecl& e) { return InstantiateVarWithPatternDecl(e, visitor); }, [&visitor](const TypeAliasDecl& e) { return InstantiateTypeAliasDecl(e, visitor); }, // MainDecl and MacroDecl are ignored. Since they will be desugared before semantic typecheck. [](const MacroDecl& e) { return OwnedPtr(MakeOwned(e.begin).release()); }, [](const MainDecl& e) { return OwnedPtr(MakeOwned(e.begin).release()); }, [](const InvalidDecl& e) { return OwnedPtr(MakeOwned(e.begin).release()); }, [&decl]() { // Invalid and ignored cases. auto invalidDecl = MakeOwned(decl->begin); return OwnedPtr(invalidDecl.release()); }); CJC_ASSERT(ret); CopyNodeField(ret.get(), *decl); // Instantiate field in Decl. ret->modifiers.insert(decl->modifiers.begin(), decl->modifiers.end()); ret->identifier = decl->identifier; ret->identifierForLsp = decl->identifierForLsp; ret->keywordPos = decl->keywordPos; ret->moduleName = decl->moduleName; ret->fullPackageName = decl->fullPackageName; ret->outerDecl = decl->outerDecl; ret->checkFlag = decl->checkFlag; ret->captureIndex = decl->captureIndex; ret->linkage = decl->linkage; for (auto& anno : decl->annotations) { ret->annotations.emplace_back(InstantiateNode(anno.get(), visitor)); } ret->annotationsArray = InstantiateNode(decl->annotationsArray.get(), visitor); visitor(*decl, *ret); return ret; } OwnedPtr PartialInstantiation::InstantiateConstPattern(const ConstPattern& cp, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->literal = InstantiateExpr(cp.literal.get(), visitor); ret->operatorCallExpr = InstantiateExpr(cp.operatorCallExpr.get(), visitor); return ret; } OwnedPtr PartialInstantiation::InstantiateVarPattern(const VarPattern& vp, const VisitFunc& visitor) { OwnedPtr ret; if (vp.varDecl) { ret = MakeOwned(vp.varDecl->identifier, vp.begin); auto d = InstantiateDecl(vp.varDecl.get(), visitor); ret->varDecl.reset(As(d.release())); } else { ret = MakeOwned(); } ret->desugarExpr = InstantiateExpr(vp.desugarExpr.get(), visitor); return ret; } OwnedPtr PartialInstantiation::InstantiateTuplePattern(const TuplePattern& tp, const VisitFunc& visitor) { auto ret = MakeOwned(); for (auto& i : tp.patterns) { ret->patterns.push_back(InstantiatePattern(i.get(), visitor)); } ret->leftBracePos = tp.leftBracePos; ret->rightBracePos = tp.rightBracePos; ret->commaPosVector = tp.commaPosVector; return ret; } OwnedPtr PartialInstantiation::InstantiateTypePattern(const TypePattern& tp, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->pattern = InstantiatePattern(tp.pattern.get(), visitor); ret->type = InstantiateType(tp.type.get(), visitor); ret->colonPos = tp.colonPos; if (tp.desugarExpr != nullptr) { ret->desugarExpr = InstantiateExpr(tp.desugarExpr.get(), visitor); } if (tp.desugarVarPattern != nullptr) { ret->desugarVarPattern = PartialInstantiation::Instantiate(tp.desugarVarPattern.get(), visitor); } ret->needRuntimeTypeCheck = tp.needRuntimeTypeCheck; ret->matchBeforeRuntime = tp.matchBeforeRuntime; return ret; } OwnedPtr PartialInstantiation::InstantiateEnumPattern(const EnumPattern& ep, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->constructor = InstantiateExpr(ep.constructor.get(), visitor); for (auto& p : ep.patterns) { ret->patterns.emplace_back(InstantiatePattern(p.get(), visitor)); } ret->leftParenPos = ep.leftParenPos; ret->rightParenPos = ep.rightParenPos; ret->commaPosVector = ep.commaPosVector; return ret; } OwnedPtr PartialInstantiation::InstantiateExceptTypePattern( const ExceptTypePattern& etp, const VisitFunc& visitor) { auto ret = MakeOwned(); ret->pattern = InstantiatePattern(etp.pattern.get(), visitor); for (auto& i : etp.types) { ret->types.push_back(InstantiateType(i.get(), visitor)); } ret->patternPos = etp.patternPos; ret->colonPos = etp.colonPos; ret->bitOrPosVector = etp.bitOrPosVector; return ret; } OwnedPtr PartialInstantiation::InstantiateVarOrEnumPattern( const VarOrEnumPattern& vep, const VisitFunc& visitor) { auto ret = MakeOwned(vep.identifier); if (vep.pattern) { ret->pattern = InstantiatePattern(vep.pattern.get(), visitor); } return ret; } /** * NOTE: To guarantee the members of Pattern is copied, the sub method should not be called outside * 'InstantiatePattern'. */ OwnedPtr PartialInstantiation::InstantiatePattern(Ptr pattern, const VisitFunc& visitor) { if (!pattern) { return OwnedPtr(); } auto ret = match(*pattern)( [&visitor](const ConstPattern& e) { return OwnedPtr(InstantiateConstPattern(e, visitor)); }, [](const WildcardPattern& e) { return OwnedPtr(MakeOwned(e)); }, [&visitor](const VarPattern& e) { return OwnedPtr(InstantiateVarPattern(e, visitor)); }, [&visitor](const TuplePattern& e) { return OwnedPtr(InstantiateTuplePattern(e, visitor)); }, [&visitor](const TypePattern& e) { return OwnedPtr(InstantiateTypePattern(e, visitor)); }, [&visitor](const EnumPattern& e) { return OwnedPtr(InstantiateEnumPattern(e, visitor)); }, [&visitor](const ExceptTypePattern& e) { return OwnedPtr(InstantiateExceptTypePattern(e, visitor)); }, [&visitor](const VarOrEnumPattern& e) { return OwnedPtr(InstantiateVarOrEnumPattern(e, visitor)); }, [](const InvalidPattern& e) { return OwnedPtr(MakeOwned(e)); }, []() { return OwnedPtr(MakeOwned()); }); CJC_ASSERT(ret && ret->astKind == pattern->astKind); CopyNodeField(ret.get(), *pattern); // Instantiate field in Pattern. ret->ctxExpr = pattern->ctxExpr; ret->EnableAttr(Attribute::COMPILER_ADD); visitor(*pattern, *ret); return ret; } OwnedPtr PartialInstantiation::InstantiateBlock(const Block& block, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), block); // Instantiate field in Block. ret->unsafePos = block.unsafePos; ret->leftCurlPos = block.leftCurlPos; for (auto& it : block.body) { ret->body.push_back(InstantiateNode(it.get(), visitor)); } ret->rightCurlPos = block.rightCurlPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateClassBody(const ClassBody& cb, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), cb); // Instantiate field in ClassBody. ret->leftCurlPos = cb.leftCurlPos; for (auto& it : cb.decls) { // not need to be instantiated: // 1. prop decl and primary ctor decl are desugard as function decl // 2. function decl need be instantiated by requirement // 3. static member var can't be instantiated, it should be regarded as global var decl // 4. static init func can't be instantiated, only call once in template // need to be instantiated: // 1. function decl which marked with @Frozen // 2. member var decl, maybe we need instantiated class decl's memory info if (it->astKind == ASTKind::PRIMARY_CTOR_DECL || (it->astKind != ASTKind::VAR_DECL && !RequireInstantiation(*it)) || IsStaticVar(*it) || IsStaticInitializer(*it)) { continue; } ret->decls.push_back(InstantiateDecl(it.get(), visitor)); } ret->rightCurlPos = cb.rightCurlPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateStructBody(const StructBody& sb, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), sb); ret->leftCurlPos = sb.leftCurlPos; for (auto& it : sb.decls) { if (it->astKind == ASTKind::PRIMARY_CTOR_DECL || (it->astKind != ASTKind::VAR_DECL && !RequireInstantiation(*it)) || IsStaticVar(*it)) { continue; } ret->decls.push_back(InstantiateDecl(it.get(), visitor)); } ret->rightCurlPos = sb.rightCurlPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateInterfaceBody( const InterfaceBody& ib, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), ib); // Instantiate field in InterfaceBody. ret->leftCurlPos = ib.leftCurlPos; for (auto& it : ib.decls) { if (it->astKind == ASTKind::PROP_DECL || (it->astKind == ASTKind::FUNC_DECL && !RequireInstantiation(*it))) { continue; } ret->decls.push_back(InstantiateDecl(it.get(), visitor)); } ret->rightCurlPos = ib.rightCurlPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateGenericConstraint( const GenericConstraint& gc, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), gc); // Instantiate field in GenericConstraint. ret->type.reset(As(InstantiateType(gc.type.get(), visitor).release())); for (auto& upperBound : gc.upperBounds) { ret->upperBounds.push_back(InstantiateType(upperBound.get(), visitor)); } ret->wherePos = gc.wherePos; ret->operatorPos = gc.operatorPos; ret->bitAndPos = gc.bitAndPos; ret->commaPos = gc.commaPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateFuncBody(const FuncBody& fb, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), fb); // Instantiate field in FuncBody. for (auto& it : fb.paramLists) { ret->paramLists.push_back(InstantiateNode(it.get(), visitor)); } ret->doubleArrowPos = fb.doubleArrowPos; ret->colonPos = fb.colonPos; ret->retType = InstantiateType(fb.retType.get(), visitor); ret->body = InstantiateExpr(fb.body.get(), visitor); if (fb.generic) { ret->generic = InstantiateGeneric(*fb.generic, visitor); } for (auto& it : fb.capturedVars) { ret->capturedVars.insert(it); } ret->captureKind = fb.captureKind; ret->outerFunc = fb.outerFunc; ret->parentClassLike = fb.parentClassLike; ret->parentStruct = fb.parentStruct; ret->parentEnum = fb.parentEnum; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateFuncParamList( const FuncParamList& fpl, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), fpl); // Instantiate field in FuncParamList. ret->leftParenPos = fpl.leftParenPos; for (auto& it : fpl.params) { auto funcParam = MakeOwned(); auto d = InstantiateDecl(it.get(), visitor); funcParam.reset(As(d.release())); ret->params.push_back(std::move(funcParam)); } ret->variadicArgIndex = fpl.variadicArgIndex; ret->hasVariableLenArg = fpl.hasVariableLenArg; ret->rightParenPos = fpl.rightParenPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateFuncArg(const FuncArg& fa, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), fa); // Instantiate field in FuncArg. ret->name = fa.name; ret->withInout = fa.withInout; ret->colonPos = fa.colonPos; ret->expr = InstantiateExpr(fa.expr.get(), visitor); ret->commaPos = fa.commaPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } OwnedPtr PartialInstantiation::InstantiateAnnotation(const Annotation& annotation, const VisitFunc& visitor) { auto ret = MakeOwned(annotation.identifier, annotation.kind, annotation.begin); CopyNodeField(ret.get(), annotation); ret->kind = annotation.kind; ret->definedPackage = annotation.definedPackage; ret->identifier = annotation.identifier; ret->attrs = annotation.attrs; ret->attrCommas = annotation.attrCommas; ret->adAnnotation = annotation.adAnnotation; ret->rsquarePos = annotation.rsquarePos; ret->lsquarePos = annotation.lsquarePos; for (auto& arg : annotation.args) { ret->args.emplace_back(InstantiateNode(arg.get(), visitor)); } ret->condExpr = InstantiateExpr(annotation.condExpr.get()); ret->baseExpr = InstantiateExpr(annotation.baseExpr.get()); return ret; } OwnedPtr PartialInstantiation::InstantiateImportSpec(const ImportSpec& is, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), is); if (is.modifier) { // Instantiate Modifier ret->modifier = MakeOwned(is.modifier->modifier, is.modifier->begin); CopyNodeField(ret->modifier.get(), *is.modifier); ret->modifier->isExplicit = is.modifier->isExplicit; } std::function cloneContent = [&cloneContent](const ImportContent& src, ImportContent& dst) { CopyNodeField(&dst, src); dst.kind = src.kind; dst.prefixPaths = src.prefixPaths; dst.prefixPoses = src.prefixPoses; dst.prefixDotPoses = src.prefixDotPoses; dst.identifier = src.identifier; dst.asPos = src.asPos; dst.aliasName = src.aliasName; dst.leftCurlPos = src.leftCurlPos; if (!src.items.empty()) { dst.items.resize(src.items.size()); for (size_t i = 0; i < src.items.size(); ++i) { cloneContent(src.items[i], dst.items[i]); } } dst.commaPoses = src.commaPoses; dst.rightCurlPos = src.rightCurlPos; }; cloneContent(is.content, ret->content); for (auto& it : is.annotations) { ret->annotations.emplace_back(InstantiateNode(it.get(), visitor)); } return ret; } OwnedPtr PartialInstantiation::InstantiateMatchCase(const MatchCase& mc, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), mc); // Instantiate field in MatchCase. for (auto& pattern : mc.patterns) { ret->patterns.emplace_back(InstantiatePattern(pattern.get(), visitor)); } ret->patternGuard = InstantiateExpr(mc.patternGuard.get(), visitor); ret->exprOrDecls = InstantiateExpr(mc.exprOrDecls.get(), visitor); ret->wherePos = mc.wherePos; ret->arrowPos = mc.arrowPos; ret->EnableAttr(Attribute::COMPILER_ADD); ret->bitOrPosVector = mc.bitOrPosVector; return ret; } OwnedPtr PartialInstantiation::InstantiateMatchCaseOther( const MatchCaseOther& mco, const VisitFunc& visitor) { auto ret = MakeOwned(); CopyNodeField(ret.get(), mco); // Instantiate field in MatchCaseOther. ret->matchExpr = InstantiateExpr(mco.matchExpr.get(), visitor); ret->exprOrDecls = InstantiateExpr(mco.exprOrDecls.get(), visitor); ret->arrowPos = mco.arrowPos; ret->EnableAttr(Attribute::COMPILER_ADD); return ret; } // Instantiate Node. template OwnedPtr PartialInstantiation::InstantiateNode(Ptr node, const VisitFunc& visitor) { if (!node) { return OwnedPtr(); } auto clonedNode = match(*node)( // NOTE: Package, File, PackageSpec, ImportSpec, Modifier are ignored. // EnumBody struct is empty, no need to be cloned [&visitor](Type& type) { return OwnedPtr(InstantiateType(Ptr(&type), visitor)); }, [&visitor](Expr& expr) { return OwnedPtr(InstantiateExpr(Ptr(&expr), visitor)); }, [&visitor](Decl& decl) { return OwnedPtr(InstantiateDecl(Ptr(&decl), visitor)); }, [&visitor](Pattern& pattern) { return OwnedPtr(InstantiatePattern(Ptr(&pattern), visitor)); }, [&visitor](const ClassBody& cb) { return OwnedPtr(InstantiateClassBody(cb, visitor)); }, [&visitor](const StructBody& rb) { return OwnedPtr(InstantiateStructBody(rb, visitor)); }, [&visitor](const InterfaceBody& ib) { return OwnedPtr(InstantiateInterfaceBody(ib, visitor)); }, [&visitor](const GenericConstraint& gc) { return OwnedPtr(InstantiateGenericConstraint(gc, visitor)); }, [&visitor](const FuncBody& fb) { return OwnedPtr(InstantiateFuncBody(fb, visitor)); }, [&visitor](const FuncParamList& fpl) { return OwnedPtr(InstantiateFuncParamList(fpl, visitor)); }, [&visitor](const FuncArg& fa) { return OwnedPtr(InstantiateFuncArg(fa, visitor)); }, [&visitor](const MatchCase& mc) { return OwnedPtr(InstantiateMatchCase(mc, visitor)); }, [&visitor](const MatchCaseOther& mco) { return OwnedPtr(InstantiateMatchCaseOther(mco, visitor)); }, [&visitor](const Annotation& ann) { return OwnedPtr(InstantiateAnnotation(ann, visitor)); }, [](const DummyBody&) { return OwnedPtr(OwnedPtr()); }, [&visitor](const ImportSpec& is) { return OwnedPtr(InstantiateImportSpec(is, visitor)); }, []() { // Invalid cases. return OwnedPtr(MakeOwned()); }); CJC_ASSERT(clonedNode); visitor(*node, *clonedNode); clonedNode->EnableAttr(Attribute::COMPILER_ADD); auto result = DynamicCast(clonedNode.release()); return OwnedPtr(result); } OwnedPtr PartialInstantiation::InstantiateWithRearrange(Ptr node, const VisitFunc& visitor) { VisitFunc collectMap = [this, visitor](Node& from, Node& target) { // Collect decl to decl map. if (auto decl = DynamicCast(&from); decl) { source2cloned[decl] = ⌖ ins2generic[StaticCast(&target)] = decl; if (auto found = generic2ins.find(decl); found != generic2ins.end()) { found->second.emplace(StaticCast(&target)); } else { generic2ins[decl] = {StaticCast(&target)}; } auto& targetDecl = static_cast(target); TargetAddrMapInsert(decl->outerDecl, targetDecl.outerDecl); TargetAddrMapInsert(decl->genericDecl, targetDecl.genericDecl); // unorder_set> users field in Decl is ignored. } if (auto expr = DynamicCast(&from); expr) { source2cloned[expr] = ⌖ auto& targetExpr = static_cast(target); TargetAddrMapInsert(expr->sourceExpr, targetExpr.sourceExpr); TargetAddrMapInsert(expr->mapExpr, targetExpr.mapExpr); } if (auto funcBody = DynamicCast(&from); funcBody) { source2cloned[funcBody] = ⌖ auto& targetFuncBody = static_cast(target); TargetAddrMapInsert(funcBody->funcDecl, targetFuncBody.funcDecl); TargetAddrMapInsert(funcBody->outerFunc, targetFuncBody.outerFunc); TargetAddrMapInsert(funcBody->parentClassLike, targetFuncBody.parentClassLike); TargetAddrMapInsert(funcBody->parentStruct, targetFuncBody.parentStruct); TargetAddrMapInsert(funcBody->parentEnum, targetFuncBody.parentEnum); } if (auto pattern = DynamicCast(&from); pattern) { source2cloned[pattern] = ⌖ auto& targetPattern = static_cast(target); TargetAddrMapInsert(pattern->ctxExpr, targetPattern.ctxExpr); } // Package and File are ignored since usually we do not need to clone them. // Collect target variable address to target variable address map. // For sub-class of Expr, there is only 1 layer under it, we just enumerate them. // For sub-class of Decl, there are 2 layers under it. FuncParam extends Vardecl // which extends Decl. // Macro related ASTs also are ignored switch (from.astKind) { // sub-class of Expr struct case ASTKind::REF_EXPR: { auto& reFrom = static_cast(from); auto& reTarget = static_cast(target); /** * Whether a static member function uses virtual calls depends on the called expression. * If the invoked function is not in a type with `open` semantics, instantiation can be directly invoked * through static type invoking. * For example, `class A { static func foo() {...} }` * Instantated to `class A { static func foo() {...} }` * `A.foo()` will pointer to `foo` in `A`. * But if `A` is modified by `open`, `foo` will point to function in `A`. */ bool isCallStaticMemberByVirtual = reFrom.ref.target && reFrom.ref.target->IsFunc() && reFrom.ref.target->TestAttr(Attribute::STATIC) && reFrom.ref.target->outerDecl && IsOpenDecl(*reFrom.ref.target->outerDecl); if (!isCallStaticMemberByVirtual) { TargetAddrMapInsert(reFrom.ref.target, reTarget.ref.target); } TargetAddrMapInsert(reFrom.callOrPattern, reTarget.callOrPattern); break; } case ASTKind::ARRAY_EXPR: { auto& aeFrom = static_cast(from); auto& aeTarget = static_cast(target); TargetAddrMapInsert(aeFrom.initFunc, aeTarget.initFunc); break; } case ASTKind::ARRAY_LIT: { auto& aeFrom = static_cast(from); auto& aeTarget = static_cast(target); TargetAddrMapInsert(aeFrom.initFunc, aeTarget.initFunc); break; } case ASTKind::MEMBER_ACCESS: { auto& maFrom = static_cast(from); if (auto base = DynamicCast(maFrom.baseExpr.get()); !base || !base->isThis) { break; // Do not rearrange target if current is not accessing 'this'. } auto& maTarget = static_cast(target); TargetAddrMapInsert(maFrom.target, maTarget.target); TargetAddrMapInsert(maFrom.callOrPattern, maTarget.callOrPattern); for (size_t i = 0; i < maFrom.targets.size(); i++) { TargetAddrMapInsert(maFrom.targets[i], maTarget.targets[i]); } // NOTE: foundUpperBoundMap is ignored break; } case ASTKind::FOR_IN_EXPR: { auto& fiFrom = static_cast(from); auto& fiTarget = static_cast(target); TargetAddrMapInsert(fiFrom.patternInDesugarExpr, fiTarget.patternInDesugarExpr); break; } case ASTKind::CALL_EXPR: { auto& ceFrom = static_cast(from); if (auto ma = DynamicCast(ceFrom.baseFunc.get())) { if (auto base = DynamicCast(ma->baseExpr.get()); !base || !base->isThis) { // Do not rearrange target if current is not call of refExpr or memberAccess of 'this'. break; } } auto& ceTarget = static_cast(target); TargetAddrMapInsert(ceFrom.resolvedFunction, ceTarget.resolvedFunction); break; } case ASTKind::RETURN_EXPR: { auto& rtFrom = static_cast(from); auto& rtTarget = static_cast(target); TargetAddrMapInsert(rtFrom.refFuncBody, rtTarget.refFuncBody); break; } case ASTKind::JUMP_EXPR: { auto& jeFrom = static_cast(from); auto& jeTarget = static_cast(target); TargetAddrMapInsert(jeFrom.refLoop, jeTarget.refLoop); break; } case ASTKind::REF_TYPE: { auto& reFrom = static_cast(from); auto& reTarget = static_cast(target); TargetAddrMapInsert(reFrom.ref.target, reTarget.ref.target); break; } case ASTKind::QUALIFIED_TYPE: { auto& qtFrom = static_cast(from); auto& qtTarget = static_cast(target); TargetAddrMapInsert(qtFrom.target, qtTarget.target); break; } // case ASTKind::FUNC_BODY handled in if case case ASTKind::FUNC_DECL: { auto& fdFrom = static_cast(from); auto& fdTarget = static_cast(target); TargetAddrMapInsert(fdFrom.ownerFunc, fdTarget.ownerFunc); TargetAddrMapInsert(fdFrom.propDecl, fdTarget.propDecl); break; } case ASTKind::VAR_DECL: { auto& vdFrom = static_cast(from); auto& vdTarget = static_cast(target); TargetAddrMapInsert(vdFrom.parentPattern, vdTarget.parentPattern); break; } default: break; } visitor(from, target); }; OwnedPtr targetNode = InstantiateNode(node, collectMap); // Rearrange pointer to node pointer's target from source node pointer to cloned node pointer. for (auto& [s, t] : targetAddr2targetAddr) { if (source2cloned.find(*s) != source2cloned.end()) { *t = source2cloned[*s]; } } return targetNode; } Ptr TyGeneralizer::Generalize(Ty& ty) { if (typeMapping.empty()) { return &ty; } if (auto found = typeMapping.find(&ty); found != typeMapping.end()) { return found->second; } switch (ty.kind) { case TypeKind::TYPE_FUNC: { std::vector> paramTys; auto& funcTy = static_cast(ty); for (auto& it : funcTy.paramTys) { paramTys.push_back(Generalize(it)); } auto retType = Generalize(funcTy.retTy); Ptr ret = tyMgr.GetFunctionTy( paramTys, retType, {funcTy.IsCFunc(), funcTy.isClosureTy, funcTy.hasVariableLenArg}); return ret; } case TypeKind::TYPE_TUPLE: { std::vector> typeArgs; std::transform(ty.typeArgs.begin(), ty.typeArgs.end(), std::back_inserter(typeArgs), [this](auto it) { return Generalize(it); }); return tyMgr.GetTupleTy(typeArgs, static_cast(ty).isClosureTy); } case TypeKind::TYPE_ARRAY: return GetGeneralizedArrayTy(static_cast(ty)); case TypeKind::TYPE_POINTER: return GetGeneralizedPointerTy(static_cast(ty)); case TypeKind::TYPE_STRUCT: return GetGeneralizedStructTy(static_cast(ty)); case TypeKind::TYPE_CLASS: return GetGeneralizedClassTy(static_cast(ty)); case TypeKind::TYPE_INTERFACE: return GetGeneralizedInterfaceTy(static_cast(ty)); case TypeKind::TYPE_ENUM: return GetGeneralizedEnumTy(static_cast(ty)); case TypeKind::TYPE: { std::vector> typeArgs; for (auto& it : ty.typeArgs) { typeArgs.push_back(Generalize(it)); } return tyMgr.GetTypeAliasTy(*static_cast(ty).declPtr, typeArgs); } case TypeKind::TYPE_INTERSECTION: return GetGeneralizedSetTy(static_cast(ty)); case TypeKind::TYPE_UNION: return GetGeneralizedSetTy(static_cast(ty)); default:; } return &ty; } Ptr TyGeneralizer::GetGeneralizedStructTy(StructTy& structTy) { // If is a struct without generic parameter, no need do instantiation. if (!structTy.declPtr || !structTy.declPtr->generic) { return &structTy; } std::vector> typeArgs; // Build type arguments. for (auto& it : structTy.typeArgs) { typeArgs.push_back(Generalize(it)); } auto recTy = tyMgr.GetStructTy(*structTy.declPtr, typeArgs); return recTy; } Ptr TyGeneralizer::GetGeneralizedClassTy(ClassTy& classTy) { if (!classTy.declPtr || !classTy.declPtr->generic) { return &classTy; } std::vector> typeArgs; for (auto& it : classTy.typeArgs) { typeArgs.push_back(Generalize(it)); } Ptr insTy = nullptr; if (Is(classTy)) { insTy = tyMgr.GetClassThisTy(*classTy.declPtr, typeArgs); } else { insTy = tyMgr.GetClassTy(*classTy.declPtr, typeArgs); } return insTy; } Ptr TyGeneralizer::GetGeneralizedInterfaceTy(InterfaceTy& interfaceTy) { if (!interfaceTy.declPtr || !interfaceTy.declPtr->generic) { return &interfaceTy; } std::vector> typeArgs; for (auto& it : interfaceTy.typeArgs) { typeArgs.push_back(Generalize(it)); } auto insTy = tyMgr.GetInterfaceTy(*interfaceTy.declPtr, typeArgs); return insTy; } Ptr TyGeneralizer::GetGeneralizedEnumTy(EnumTy& enumTy) { // If is an enum without generic parameter, no need to do instantiation. if (!enumTy.declPtr || !enumTy.declPtr->generic) { return &enumTy; } std::vector> typeArgs; // Build type arguments. for (auto& it : enumTy.typeArgs) { typeArgs.push_back(Generalize(it)); } if (Is(enumTy)) { return tyMgr.GetRefEnumTy(*enumTy.declPtr, typeArgs); } auto tmp = tyMgr.GetEnumTy(*enumTy.declPtr, typeArgs); tmp->hasCorrespondRefEnumTy = enumTy.hasCorrespondRefEnumTy; return tmp; } Ptr TyGeneralizer::GetGeneralizedArrayTy(ArrayTy& arrayTy) { if (arrayTy.typeArgs.empty()) { return &arrayTy; } auto elemTy = Generalize(arrayTy.typeArgs[0]); auto dims = arrayTy.dims; return tyMgr.GetArrayTy(elemTy, dims); } Ptr TyGeneralizer::GetGeneralizedPointerTy(PointerTy& cptrTy) { if (cptrTy.typeArgs.empty()) { return &cptrTy; } auto elemTy = Generalize(cptrTy.typeArgs[0]); return tyMgr.GetPointerTy(elemTy); } // Get instantiated ty of set type 'IntersectionTy' and 'UnionTy'. template Ptr TyGeneralizer::GetGeneralizedSetTy(SetTy& ty) { std::set> tys; for (auto it : ty.tys) { tys.emplace(Generalize(it)); } return tyMgr.GetTypeTy(tys); } } // namespace Cangjie cangjie_compiler-1.0.7/src/Sema/GenericInstantiation/PartialInstantiation.h000066400000000000000000000337141510705540100271260ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * PartialInstantiation is the class to partial instantiation. */ #ifndef CANGJIE_SEMA_PARTIAL_INSTANTIATION_H #define CANGJIE_SEMA_PARTIAL_INSTANTIATION_H #include #include #include "cangjie/AST/Node.h" #include "cangjie/Option/Option.h" #include "cangjie/Sema/CommonTypeAlias.h" #include "cangjie/Utils/SafePointer.h" namespace Cangjie { void SetOptLevel(const GlobalOptions& opts); GlobalOptions::OptimizationLevel GetOptLevel(); /** * Check whether the location where the instantiation is triggered is in the context with Open semantics. * Return true if expr is in a member of an open class or interface. */ bool IsInOpenContext(const std::vector>& contextDecl); bool RequireInstantiation(const AST::Decl& decl, bool isInOpenContext = false); using VisitFunc = std::function; void DefaultVisitFunc(const AST::Node& source, const AST::Node& target); AST::MacroInvocation InstantiateMacroInvocation(const AST::MacroInvocation& me); OwnedPtr InstantiateGeneric(const AST::Generic& generic, const VisitFunc& visitor); class PartialInstantiation { public: template static OwnedPtr Instantiate(Ptr node, const VisitFunc& visitFunc) { OwnedPtr clonedNode = PartialInstantiation().InstantiateWithRearrange(node, visitFunc); return OwnedPtr(static_cast(clonedNode.release())); } static Ptr GetGeneralDecl(AST::Decl& clonedDecl) { if (clonedDecl.genericDecl) { return clonedDecl.genericDecl; } else if (clonedDecl.TestAttr(AST::Attribute::GENERATED_TO_MOCK)) { return &clonedDecl; } else { return ins2generic.at(&clonedDecl); } } static std::unordered_set> GetInstantiatedDecl(const AST::Decl& genericDecl) { if (auto found = generic2ins.find(&genericDecl); found != generic2ins.end()) { return found->second; } return {}; } static void ResetGlobalMap() { generic2ins.clear(); ins2generic.clear(); } private: /** Map between 'pointer to source node pointer' to 'pointer to cloned node pointer'. */ std::unordered_map*, Ptr*> targetAddr2targetAddr; /** Map bewteen 'source node pointer' to 'cloned node pointer'. */ std::unordered_map, Ptr> source2cloned; static std::unordered_map, Ptr> ins2generic; static std::unordered_map, std::unordered_set>> generic2ins; template void TargetAddrMapInsert(Ptr& from, Ptr& target) { if (from == nullptr) { return; } targetAddr2targetAddr[reinterpret_cast*>(&from)] = reinterpret_cast*>(&target); } OwnedPtr InstantiateWithRearrange(Ptr node, const VisitFunc& visitor); template static OwnedPtr InstantiateNode(Ptr node, const VisitFunc& visitor); static OwnedPtr InstantiateType(Ptr type, const VisitFunc& visitor); template static OwnedPtr InstantiateExpr(Ptr expr, const VisitFunc& visitor = DefaultVisitFunc); static OwnedPtr InstantiateDecl(Ptr decl, const VisitFunc& visitor); static OwnedPtr InstantiatePattern(Ptr pattern, const VisitFunc& visitor); static OwnedPtr InstantiateQualifiedType( const AST::QualifiedType& node, const VisitFunc& visitor); static OwnedPtr InstantiateParenType(const AST::ParenType& node, const VisitFunc& visitor); static OwnedPtr InstantiateOptionType(const AST::OptionType& node, const VisitFunc& visitor); static OwnedPtr InstantiateFuncType(const AST::FuncType& node, const VisitFunc& visitor); static OwnedPtr InstantiateTupleType(const AST::TupleType& node, const VisitFunc& visitor); static OwnedPtr InstantiateConstantType(const AST::ConstantType& node, const VisitFunc& visitor); static OwnedPtr InstantiateVArrayType(const AST::VArrayType& node, const VisitFunc& visitor); static OwnedPtr InstantiateRefType(const AST::RefType& type, const VisitFunc& visitor); static OwnedPtr InstantiateMacroExpandExpr( const AST::MacroExpandExpr& mee, const VisitFunc& visitor); static OwnedPtr InstantiateTokenPart(const AST::TokenPart& tp, const VisitFunc& visitor); static OwnedPtr InstantiateQuoteExpr(const AST::QuoteExpr& qe, const VisitFunc& visitor); static OwnedPtr InstantiateIfExpr(const AST::IfExpr& ie, const VisitFunc& visitor); static OwnedPtr InstantiateTryExpr(const AST::TryExpr& te, const VisitFunc& visitor); static OwnedPtr InstantiateThrowExpr(const AST::ThrowExpr& te, const VisitFunc& visitor); static OwnedPtr InstantiateReturnExpr(const AST::ReturnExpr& re, const VisitFunc& visitor); static OwnedPtr InstantiateWhileExpr(const AST::WhileExpr& we, const VisitFunc& visitor); static OwnedPtr InstantiateDoWhileExpr(const AST::DoWhileExpr& dwe, const VisitFunc& visitor); static OwnedPtr InstantiateAssignExpr(const AST::AssignExpr& ae, const VisitFunc& visitor); static OwnedPtr InstantiateIncOrDecExpr(const AST::IncOrDecExpr& ide, const VisitFunc& visitor); static OwnedPtr InstantiateUnaryExpr(const AST::UnaryExpr& ue, const VisitFunc& visitor); static OwnedPtr InstantiateBinaryExpr(const AST::BinaryExpr& be, const VisitFunc& visitor); static OwnedPtr InstantiateRangeExpr(const AST::RangeExpr& re, const VisitFunc& visitor); static OwnedPtr InstantiateSubscriptExpr( const AST::SubscriptExpr& se, const VisitFunc& visitor); static OwnedPtr InstantiateMemberAccess(const AST::MemberAccess& ma, const VisitFunc& visitor); static OwnedPtr InstantiateCallExpr(const AST::CallExpr& ce, const VisitFunc& visitor); static OwnedPtr InstantiateParenExpr(const AST::ParenExpr& pe, const VisitFunc& visitor); static OwnedPtr InstantiateLambdaExpr(const AST::LambdaExpr& le, const VisitFunc& visitor); static OwnedPtr InstantiateLitConstExpr(const AST::LitConstExpr& lce, const VisitFunc& visitor); static OwnedPtr InstantiateArrayLit(const AST::ArrayLit& al, const VisitFunc& visitor); static OwnedPtr InstantiateArrayExpr(const AST::ArrayExpr& ae, const VisitFunc& visitor); static OwnedPtr InstantiatePointerExpr(const AST::PointerExpr& ptre, const VisitFunc& visitor); static OwnedPtr InstantiateTupleLit(const AST::TupleLit& tl, const VisitFunc& visitor); static OwnedPtr InstantiateRefExpr(const AST::RefExpr& re, const VisitFunc& visitor); static OwnedPtr InstantiateForInExpr(const AST::ForInExpr& fie, const VisitFunc& visitor); static OwnedPtr InstantiateMatchExpr(const AST::MatchExpr& me, const VisitFunc& visitor); static OwnedPtr InstantiateJumpExpr(const AST::JumpExpr& je); static OwnedPtr InstantiateTypeConvExpr(const AST::TypeConvExpr& tce, const VisitFunc& visitor); static OwnedPtr InstantiateSpawnExpr(const AST::SpawnExpr& se, const VisitFunc& visitor); static OwnedPtr InstantiateSynchronizedExpr( const AST::SynchronizedExpr& se, const VisitFunc& visitor); static OwnedPtr InstantiateInvalidExpr(const AST::InvalidExpr& ie); static OwnedPtr InstantiateInterpolationExpr( const AST::InterpolationExpr& ie, const VisitFunc& visitor); static OwnedPtr InstantiateStrInterpolationExpr( const AST::StrInterpolationExpr& sie, const VisitFunc& visitor); static OwnedPtr InstantiateTrailingClosureExpr( const AST::TrailingClosureExpr& tc, const VisitFunc& visitor); static OwnedPtr InstantiateIsExpr(const AST::IsExpr& ie, const VisitFunc& visitor); static OwnedPtr InstantiateAsExpr(const AST::AsExpr& ae, const VisitFunc& visitor); static OwnedPtr InstantiateOptionalExpr(const AST::OptionalExpr& oe, const VisitFunc& visitor); static OwnedPtr InstantiateOptionalChainExpr( const AST::OptionalChainExpr& oce, const VisitFunc& visitor); static OwnedPtr InstantiateLetPatternDestructor( const AST::LetPatternDestructor& ldp, const VisitFunc& visitor); static OwnedPtr InstantiateConstPattern(const AST::ConstPattern& cp, const VisitFunc& visitor); static OwnedPtr InstantiateVarPattern(const AST::VarPattern& vp, const VisitFunc& visitor); static OwnedPtr InstantiateTuplePattern(const AST::TuplePattern& tp, const VisitFunc& visitor); static OwnedPtr InstantiateTypePattern(const AST::TypePattern& tp, const VisitFunc& visitor); static OwnedPtr InstantiateEnumPattern(const AST::EnumPattern& ep, const VisitFunc& visitor); static OwnedPtr InstantiateExceptTypePattern( const AST::ExceptTypePattern& etp, const VisitFunc& visitor); static OwnedPtr InstantiateVarOrEnumPattern( const AST::VarOrEnumPattern& vep, const VisitFunc& visitor); static OwnedPtr InstantiateBlock(const AST::Block& block, const VisitFunc& visitor); static OwnedPtr InstantiateClassBody(const AST::ClassBody& cb, const VisitFunc& visitor); static OwnedPtr InstantiateStructBody(const AST::StructBody& sb, const VisitFunc& visitor); static OwnedPtr InstantiateInterfaceBody( const AST::InterfaceBody& ib, const VisitFunc& visitor); static OwnedPtr InstantiateGenericConstraint( const AST::GenericConstraint& gc, const VisitFunc& visitor); static OwnedPtr InstantiateFuncBody(const AST::FuncBody& fb, const VisitFunc& visitor); static OwnedPtr InstantiateFuncParam(const AST::FuncParam& fp, const VisitFunc& visitor); static OwnedPtr InstantiateFuncParamList( const AST::FuncParamList& fpl, const VisitFunc& visitor); static OwnedPtr InstantiateFuncArg(const AST::FuncArg& fa, const VisitFunc& visitor); static OwnedPtr InstantiateAnnotation(const AST::Annotation& annotation, const VisitFunc& visitor); static OwnedPtr InstantiateImportSpec(const AST::ImportSpec& is, const VisitFunc& visitor); static OwnedPtr InstantiateMatchCase(const AST::MatchCase& mc, const VisitFunc& visitor); static OwnedPtr InstantiateMatchCaseOther( const AST::MatchCaseOther& mco, const VisitFunc& visitor); static OwnedPtr InstantiateGenericParamDecl(const AST::GenericParamDecl& gpd); static OwnedPtr InstantiateVarWithPatternDecl( const AST::VarWithPatternDecl& vwpd, const VisitFunc& visitor); static OwnedPtr InstantiateVarDecl(const AST::VarDecl& vd, const VisitFunc& visitor); static OwnedPtr InstantiateFuncDecl(const AST::FuncDecl& fd, const VisitFunc& visitor); static OwnedPtr InstantiatePrimaryCtorDecl(const AST::PrimaryCtorDecl& pcd, const VisitFunc& visitor); static OwnedPtr InstantiatePropDecl(const AST::PropDecl& pd, const VisitFunc& visitor); static OwnedPtr InstantiateExtendDecl(const AST::ExtendDecl& ed, const VisitFunc& visitor); static OwnedPtr InstantiateMacroExpandDecl(const AST::MacroExpandDecl& med); static OwnedPtr InstantiateStructDecl(const AST::StructDecl& sd, const VisitFunc& visitor); static OwnedPtr InstantiateClassDecl(const AST::ClassDecl& cd, const VisitFunc& visitor); static OwnedPtr InstantiateInterfaceDecl(const AST::InterfaceDecl& id, const VisitFunc& visitor); static OwnedPtr InstantiateEnumDecl(const AST::EnumDecl& ed, const VisitFunc& visitor); static OwnedPtr InstantiateTypeAliasDecl(const AST::TypeAliasDecl& tad, const VisitFunc& visitor); }; class TypeManager; using ReversedTypeSubst = std::map, Ptr>; class TyGeneralizer { public: TyGeneralizer(TypeManager& tyMgr, const ReversedTypeSubst& mapping) : tyMgr(tyMgr), typeMapping(mapping) { } ~TyGeneralizer() = default; inline Ptr Generalize(Ptr ty) { return AST::Ty::IsTyCorrect(ty) ? Generalize(*ty) : ty; } private: Ptr Generalize(AST::Ty& ty); Ptr GetGeneralizedStructTy(AST::StructTy& structTy); Ptr GetGeneralizedClassTy(AST::ClassTy& classTy); Ptr GetGeneralizedInterfaceTy(AST::InterfaceTy& interfaceTy); Ptr GetGeneralizedEnumTy(AST::EnumTy& enumTy); Ptr GetGeneralizedArrayTy(AST::ArrayTy& arrayTy); Ptr GetGeneralizedPointerTy(AST::PointerTy& cptrTy); // Get instantiated ty of set type 'IntersectionTy' and 'UnionTy'. template Ptr GetGeneralizedSetTy(SetTy& ty); TypeManager& tyMgr; const ReversedTypeSubst& typeMapping; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Sema/IncrementalUtils.cpp000066400000000000000000000325171510705540100224610ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the helper functions used for incremental semantic checking. */ #include "cangjie/Sema/IncrementalUtils.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/ASTCasting.h" #include "cangjie/Mangle/ASTMangler.h" #include "Desugar/DesugarInTypeCheck.h" #include "cangjie/IncrementalCompilation/IncrementalCompilationLogger.h" namespace Cangjie::Sema { using namespace Cangjie::AST; namespace { inline bool IsInstanceMemberVar(const Decl& decl) { return decl.outerDecl && decl.astKind == ASTKind::VAR_DECL && !decl.TestAnyAttr(Attribute::STATIC, Attribute::ENUM_CONSTRUCTOR); } inline bool IsStaticMemberVar(const Decl& decl) { return decl.outerDecl && decl.astKind == ASTKind::VAR_DECL && decl.TestAttr(Attribute::STATIC); } } // namespace void MarkIncrementalCheckForCtor(const std::unordered_set>& declsToBeReCompiled) { std::unordered_set> typeOfModifiedCtors; std::unordered_set> typeOfModifiedStaticInits; auto collectTypes = [&typeOfModifiedCtors, &typeOfModifiedStaticInits](auto decl) { // 1. If a constructor needs to be compiled, all instance member variables also need to be compiled. // 2. If any of the instance member variable needs to be compiled, // all other instance member variables also need to be compiled. if (IsInstanceConstructor(*decl) || IsInstanceMemberVar(*decl)) { CJC_NULLPTR_CHECK(decl->outerDecl); typeOfModifiedCtors.emplace(decl->outerDecl); } else if (IsStaticInitializer(*decl) || IsStaticMemberVar(*decl)) { // If any static initializer or static variables has been changed, // the static initializer and all static variables are need to be compiled. CJC_NULLPTR_CHECK(decl->outerDecl); typeOfModifiedStaticInits.emplace(decl->outerDecl); } else if (decl->astKind == ASTKind::CLASS_DECL) { // If classDecl is to be compiled, mark it's instance constructor also needs to be recompiled, // since its inherited super may changed (add/remove/modify super call). typeOfModifiedCtors.emplace(decl); } }; std::for_each(declsToBeReCompiled.cbegin(), declsToBeReCompiled.cend(), collectTypes); for (auto& it : typeOfModifiedCtors) { for (auto& member : it->GetMemberDecls()) { if (IsInstanceConstructor(*member) || IsInstanceMemberVar(*member)) { member->toBeCompiled = true; } } } for (auto& it : typeOfModifiedStaticInits) { for (auto& member : it->GetMemberDecls()) { if (IsStaticInitializer(*member) || IsStaticMemberVar(*member)) { member->toBeCompiled = true; } } } } /** * Collect changed struct types which need to be regenerated in CodeGen. */ std::unordered_set> CollectChangedStructTypes( const Package& pkg, const std::unordered_set>& declsToBeReCompiled) { std::unordered_set> tys; for (auto decl : declsToBeReCompiled) { if (decl->astKind != ASTKind::STRUCT_DECL || decl->TestAttr(Attribute::GENERIC)) { continue; } if (auto structTy = DynamicCast(decl->ty)) { (void)tys.emplace(structTy); } } for (auto& it : pkg.genericInstantiatedDecls) { if (it->toBeCompiled && it->astKind == ASTKind::STRUCT_DECL) { (void)tys.emplace(StaticCast(it->ty)); } } return tys; } bool IsNeedRecompileCtor(Decl& decl) { if (!decl.TestAttr(AST::Attribute::CONSTRUCTOR)) { return false; } auto initFunc = DynamicCast(&decl); if (!initFunc) { return false; } // We can't reset the right body for unchanged init decl, so recompile it when it need body if (initFunc->isConst || initFunc->isFrozen) { return true; } return false; } /** * Desugar for unchanged primary ctor and update member variables into 'mangledName2DeclMap'. * NOTE: this only for incremental compilation. */ void HandleCtorForIncr( const AST::Package& pkg, std::map>& mangledName2DeclMap, SemanticInfo& usageCache) { IterateToplevelDecls(pkg, [&mangledName2DeclMap, &usageCache](auto& decl) { if (decl->astKind != ASTKind::CLASS_DECL && decl->astKind != ASTKind::STRUCT_DECL) { return; } auto& members = decl->GetMemberDecls(); Ptr primaryCtor = nullptr; size_t index = members.size(); for (auto& it : members) { if (auto pd = DynamicCast(it.get()); pd && !pd->toBeCompiled) { if (pd->isConst || pd->HasAnno(AnnotationKind::FROZEN)) { // We can't desugar and reset the right body for unchanged primary constructor, so recompile it when // it need body pd->toBeCompiled = true; } else { primaryCtor = pd; } } else if (!it->toBeCompiled && IsNeedRecompileCtor(*it)) { it->toBeCompiled = true; } } if (primaryCtor) { DesugarPrimaryCtor(*decl, *primaryCtor); // Desugar of primary ctor may insert new member variables into type decl. size_t i = index; CJC_ASSERT(i < members.size()); for (; i < members.size(); ++i) { auto originDecl = mangledName2DeclMap[members[i]->rawMangleName]; mangledName2DeclMap[members[i]->rawMangleName] = members[i].get(); // Since decl has been desugared, we need to update sema cache to new decl. auto found = usageCache.usages.find(originDecl); if (found != usageCache.usages.end()) { usageCache.usages.emplace(members[i].get(), found->second); usageCache.usages.erase(originDecl); } } } }); } std::string GetTypeRawMangleName(const Ty& ty) { auto boxDecl = Ty::GetDeclPtrOfTy(&ty); if (boxDecl) { return boxDecl->rawMangleName; } // NOTE: extend of function & tuple type is not supported now. CJC_ASSERT(ty.IsBuiltin()); return ASTMangler::MangleBuiltinType(Ty::KindName(ty.kind)); } std::string GetRawMangleOfBoxedType(const InheritableDecl& cd) { CJC_ASSERT(cd.TestAttr(Attribute::OPEN) && cd.identifier.Val().find(BOX_DECL_PREFIX) != std::string::npos); for (auto& member : cd.GetMemberDecls()) { if (auto vd = DynamicCast(member.get())) { CJC_ASSERT(vd->identifier == "$value"); return GetTypeRawMangleName(*vd->ty); } } InternalError("Found incorrect base box class: " + cd.identifier); return ""; } void CollectImplicitAddedMembers(const AST::InheritableDecl& id, SemanticInfo& usageCache) { auto rawMangleName = id.rawMangleName; if (auto cd = DynamicCast(&id); cd && cd->TestAttr(Attribute::IMPLICIT_ADD)) { // collect cd to baseBoxType if (cd->TestAttr(Attribute::OPEN)) { // Base box class rawMangleName = GetRawMangleOfBoxedType(*cd); } else { // box class with interfaces. auto sd = cd->GetSuperClassDecl(); CJC_NULLPTR_CHECK(sd); rawMangleName = GetRawMangleOfBoxedType(*sd); } CJC_ASSERT(!rawMangleName.empty()); usageCache.compilerAddedUsages[rawMangleName].emplace(cd->mangledName); } else { CJC_ASSERT(!rawMangleName.empty()); } for (auto& member : id.GetMemberDecls()) { // collect compiler inserted static.init, normal init, copied default implementation, $toAny, // and members in boxed decl. if (!member->TestAttr(Attribute::IMPLICIT_ADD)) { continue; } if (auto pd = DynamicCast(member.get())) { auto collectFunc = [&usageCache, &rawMangleName](auto& it) { usageCache.compilerAddedUsages[rawMangleName].emplace(it->mangledName); }; std::for_each(pd->getters.cbegin(), pd->getters.cend(), collectFunc); std::for_each(pd->setters.cbegin(), pd->setters.cend(), collectFunc); } else { usageCache.compilerAddedUsages[rawMangleName].emplace(member->mangledName); } } } namespace { void CollectAllNoninstantiatedDeclsOfTypeArgs( const Cangjie::AST::Decl& genericDecl, std::set>& nonInsTypeDecls) { if (genericDecl.ty == nullptr) { return; } for (auto& tyArg : genericDecl.ty->typeArgs) { auto tyDecl = Ty::GetDeclOfTy(tyArg); if (tyDecl == nullptr) { continue; } if (tyDecl->TestAttr(Attribute::GENERIC_INSTANTIATED)) { CollectAllNoninstantiatedDeclsOfTypeArgs(*tyDecl, nonInsTypeDecls); } nonInsTypeDecls.emplace(tyDecl); } } } void CollectCompilerAddedDeclUsage(const AST::Package& pkg, SemanticInfo& usageCache) { IterateToplevelDecls(pkg, [&usageCache](auto& decl) { if (auto md = DynamicCast(decl.get()); md && md->desugarDecl) { // original param 2 + compiler inserted 1 param == 3. auto isAttr = md->desugarDecl->funcBody->paramLists.front()->params.size() == MACRO_ATTR_ARGS; auto invokeFuncName = Utils::GetMacroFuncName(md->fullPackageName, isAttr, md->identifier); usageCache.compilerAddedUsages[md->rawMangleName].emplace(invokeFuncName); } else if (auto id = DynamicCast(decl.get())) { CollectImplicitAddedMembers(*id, usageCache); } }); for (auto it : pkg.srcImportedNonGenericDecls) { auto rawMangleName = it->rawMangleName; if (auto fd = DynamicCast(it); fd && fd->ownerFunc) { rawMangleName = fd->ownerFunc->rawMangleName; } if (rawMangleName.empty()) { continue; } usageCache.compilerAddedUsages[rawMangleName].emplace(it->mangledName); } for (const auto &it : pkg.genericInstantiatedDecls) { if (it->mangledName.empty()) { continue; } // collocet all decl (non-Instantiated) of type args of generic instantiated decl std::set> nonInsDeclsOfTypeArgs; CollectAllNoninstantiatedDeclsOfTypeArgs(*it, nonInsDeclsOfTypeArgs); for (auto& decl : nonInsDeclsOfTypeArgs) { if (decl->rawMangleName.empty()) { continue; } usageCache.compilerAddedUsages[decl->rawMangleName].emplace(it->mangledName); } } } void CollectRemovedMangles( const std::string& removed, SemanticInfo& semaInfo, std::unordered_set& removedMangles) { if (auto found = semaInfo.compilerAddedUsages.find(removed); found != semaInfo.compilerAddedUsages.end()) { auto& logger = IncrementalCompilationLogger::GetInstance(); if (logger.IsEnable()) { logger.LogLn("remove for: " + removed); } for (auto it : found->second) { if (it.find("") == std::string::npos) { removedMangles.insert(it); if (logger.IsEnable()) { logger.LogLn("removed mangled: " + it); } } } semaInfo.compilerAddedUsages.erase(found); } } static std::optional CollectDefaultCtorIfNeeded(const std::string& typeMangle, SemanticInfo& semaInfo, bool needRemoveCompilerAddCtor) { auto found = semaInfo.compilerAddedUsages.find(typeMangle); if (found == semaInfo.compilerAddedUsages.end()) { return {}; } // Every type decl must have at most one default constructor. auto foundCtor = std::find_if(found->second.cbegin(), found->second.cend(), [](auto it) { return it.find("") != std::string::npos; }); std::optional ret; if (foundCtor != found->second.cend()) { if (needRemoveCompilerAddCtor) { ret = *foundCtor; } found->second.erase(foundCtor); } return ret; } void CollectRemovedManglesForReCompile( const Decl& changed, SemanticInfo& semaInfo, std::unordered_set& removedMangles) { if (changed.astKind == ASTKind::CLASS_DECL || changed.astKind == ASTKind::STRUCT_DECL) { auto& members = changed.GetMemberDecls(); bool needRemoveCompilerAddCtor = std::any_of(members.cbegin(), members.cend(), [](auto& it) { return IsInstanceConstructor(*it) || (it->toBeCompiled && it->astKind == ASTKind::VAR_DECL && !it->TestAttr(Attribute::STATIC)); }); auto& typeMangle = changed.rawMangleName; if (auto ctorMangle = CollectDefaultCtorIfNeeded(typeMangle, semaInfo, needRemoveCompilerAddCtor)) { IncrementalCompilationLogger::GetInstance().LogLn( "remove default ctor '" + *ctorMangle + "' for '" + typeMangle + "'"); removedMangles.emplace(ctorMangle.value()); } } CollectRemovedMangles(changed.rawMangleName, semaInfo, removedMangles); } } // namespace Cangjie::Sema cangjie_compiler-1.0.7/src/Sema/InheritanceChecker/000077500000000000000000000000001510705540100222015ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/InheritanceChecker/BuiltInInheritanceHelper.cpp000066400000000000000000000146401510705540100275720ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements inheritance checking of structure declarations. */ #include "StructInheritanceChecker.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Types.h" #include "cangjie/AST/Utils.h" #include "cangjie/Sema/TypeManager.h" #include "BuiltInOperatorUtil.h" #include "TypeCheckUtil.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; bool StructInheritanceChecker::IsBuiltInOperatorFuncInExtend( const MemberSignature& member, const Decl& structDecl) const { if (structDecl.astKind != ASTKind::EXTEND_DECL || !member.decl->IsFunc() || !Ty::IsTyCorrect(member.decl->ty) || !member.decl->TestAttr(Attribute::ABSTRACT, Attribute::OPERATOR)) { return false; } auto ed = RawStaticCast(&structDecl); auto fd = RawStaticCast(member.decl); auto funcTy = RawStaticCast(member.ty); auto iFuncRetTy = funcTy->retTy; const std::vector>& paramTys = funcTy->paramTys; Ptr thisTy = ed->extendedType->ty; if (paramTys.size() == 1 && thisTy && paramTys[0] && IsBuiltinBinaryExpr(fd->op, *thisTy, *paramTys[0])) { TypeKind returnTyKind = GetBuiltinBinaryExprReturnKind(fd->op, thisTy->kind); auto expectedRetTy = TypeManager::GetPrimitiveTy(returnTyKind); if (expectedRetTy == iFuncRetTy) { CreateBuiltInBinaryOperatorFunc(fd->op, paramTys[0], *const_cast(ed), returnTyKind); } else { diag.DiagnoseRefactor(DiagKindRefactor::sema_return_type_incompatible, structDecl, fd->identifier); } return true; } else if (paramTys.empty() && thisTy && IsBuiltinUnaryExpr(fd->op, *thisTy)) { TypeKind returnTyKind = GetBuiltinUnaryOpReturnKind(fd->op, ed->ty->kind); auto expectedRetTy = TypeManager::GetPrimitiveTy(returnTyKind); if (expectedRetTy == iFuncRetTy) { CreateBuiltInUnaryOperatorFunc(fd->op, *const_cast(ed)); } else { diag.DiagnoseRefactor(DiagKindRefactor::sema_return_type_incompatible, structDecl, fd->identifier); } return true; } return false; } void StructInheritanceChecker::CreateBuiltInUnaryOperatorFunc(TokenKind op, ExtendDecl& ed) const { TypeKind returnTyKind = GetBuiltinUnaryOpReturnKind(op, ed.ty->kind); auto returnTy = TypeManager::GetPrimitiveTy(returnTyKind); auto nothingTy = TypeManager::GetNothingTy(); auto fd = MakeOwnedNode(); fd->toBeCompiled = true; // For incremental compilation. fd->EnableAttr(Attribute::IN_EXTEND, Attribute::PUBLIC, Attribute::OPERATOR, Attribute::IMPLICIT_ADD); CopyBasicInfo(&ed, fd.get()); fd->moduleName = ed.moduleName; fd->fullPackageName = ed.fullPackageName; fd->op = op; fd->identifier = SrcIdentifier{TOKENS[static_cast(op)]}; fd->ty = typeManager.GetFunctionTy({}, returnTy); fd->outerDecl = &ed; auto funcBody = MakeOwnedNode(); funcBody->paramLists.emplace_back(MakeOwnedNode()); funcBody->funcDecl = fd.get(); funcBody->ty = fd->ty; auto retType = MakeOwnedNode(); retType->kind = returnTyKind; retType->ty = returnTy; funcBody->retType = std::move(retType); auto block = MakeOwnedNode(); block->ty = nothingTy; auto thisExpr = MakeOwnedNode(); thisExpr->isThis = true; thisExpr->ref.identifier = "this"; thisExpr->ty = ed.ty; auto ue = MakeOwnedNode(); ue->op = op; ue->expr = std::move(thisExpr); ue->ty = returnTy; auto returnExpr = MakeOwnedNode(); returnExpr->expr = std::move(ue); returnExpr->ty = nothingTy; returnExpr->refFuncBody = funcBody.get(); block->body.emplace_back(std::move(returnExpr)); funcBody->body = std::move(block); fd->funcBody = std::move(funcBody); AddCurFile(*fd, ed.curFile); ed.members.push_back(std::move(fd)); } void StructInheritanceChecker::CreateBuiltInBinaryOperatorFunc( TokenKind op, Ptr rightTy, ExtendDecl& ed, TypeKind returnTyKind) const { auto retTy = TypeManager::GetPrimitiveTy(returnTyKind); auto nothingTy = TypeManager::GetNothingTy(); auto fd = MakeOwnedNode(); fd->toBeCompiled = true; // For incremental compilation. fd->EnableAttr(Attribute::IN_EXTEND, Attribute::PUBLIC, Attribute::OPERATOR, Attribute::IMPLICIT_ADD); CopyBasicInfo(&ed, fd.get()); fd->moduleName = ed.moduleName; fd->fullPackageName = ed.fullPackageName; fd->op = op; fd->identifier = SrcIdentifier{TOKENS[static_cast(op)]}; fd->ty = typeManager.GetFunctionTy({rightTy}, retTy); fd->outerDecl = &ed; auto funcBody = MakeOwnedNode(); funcBody->ty = fd->ty; funcBody->funcDecl = fd.get(); auto retType = MakeOwnedNode(); retType->kind = returnTyKind; retType->ty = retTy; funcBody->retType = std::move(retType); auto rightParam = MakeOwnedNode(); rightParam->ty = rightTy; rightParam->identifier = "right"; auto block = MakeOwnedNode(); block->ty = nothingTy; auto leftExpr = MakeOwnedNode(); leftExpr->isThis = true; leftExpr->ref.identifier = "this"; leftExpr->ty = ed.ty; auto rightExpr = MakeOwnedNode(); rightExpr->ref.identifier = "right"; rightExpr->ref.target = rightParam.get(); rightExpr->ty = rightTy; auto be = MakeOwnedNode(); be->op = op; be->leftExpr = std::move(leftExpr); be->rightExpr = std::move(rightExpr); be->ty = retTy; auto returnExpr = MakeOwnedNode(); returnExpr->expr = std::move(be); returnExpr->ty = nothingTy; returnExpr->refFuncBody = funcBody.get(); auto paramList = MakeOwnedNode(); paramList->params.emplace_back(std::move(rightParam)); funcBody->paramLists.emplace_back(std::move(paramList)); block->body.emplace_back(std::move(returnExpr)); funcBody->body = std::move(block); fd->funcBody = std::move(funcBody); AddCurFile(*fd, ed.curFile); ed.members.push_back(std::move(fd)); } cangjie_compiler-1.0.7/src/Sema/InheritanceChecker/CMakeLists.txt000066400000000000000000000005501510705540100247410ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB INHERITANCECHECKER_SRC *.cpp) set(SEMA_SRC ${SEMA_SRC} ${INHERITANCECHECKER_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/InheritanceChecker/GenericInheritanceChecker.cpp000066400000000000000000000073131510705540100277240ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements confliction checking of generic upper bounds. */ #include "StructInheritanceChecker.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Types.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Sema/TypeManager.h" #include "TypeCheckUtil.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; namespace { Ptr GetSmallestClassTy(TypeManager& tyMgr, const std::set>& uppers) { Ptr smallest = nullptr; for (auto it : uppers) { if (!it->IsClass()) { continue; } if (!Ty::IsTyCorrect(smallest) || tyMgr.IsSubtype(it, smallest)) { smallest = it; } } return smallest; } } // namespace void StructInheritanceChecker::CheckAllUpperBoundsConfliction() { std::vector> genericsWithConstraint; Walker(&pkg, [&genericsWithConstraint](auto node) { // Only incremental compilation case will meet generic instantiated decl. Ignore them all. if (node->TestAttr(Attribute::GENERIC_INSTANTIATED)) { return VisitAction::SKIP_CHILDREN; } if (auto generic = DynamicCast(node); generic && !generic->genericConstraints.empty()) { (void)genericsWithConstraint.emplace_back(generic); return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }).Walk(); for (auto generic : std::as_const(genericsWithConstraint)) { CheckUpperBoundsConfliction(*generic); } } void StructInheritanceChecker::CheckUpperBoundsConfliction(const Generic& generic) { for (auto& gc : generic.genericConstraints) { CJC_ASSERT(gc && gc->type); auto gTy = DynamicCast(gc->type->ty); if (gTy == nullptr || gTy->decl->TestAttr(Attribute::IN_REFERENCE_CYCLE)) { continue; // Ignore invalid generic types. } // 'upperbounds' contains directly and indirectly defined non-generic upper bounds. auto uppers = gTy->upperBounds; if (uppers.empty()) { continue; } auto classTy = GetSmallestClassTy(typeManager, uppers); // 1. Erase non-interface from upperbounds. Utils::EraseIf(uppers, [](auto ty) { return !ty->IsInterface(); }); MemberMap inheritedMembers; // 2. Merge members of interface upper bounds. for (auto iTy : uppers) { auto interfaceDecl = Ty::GetDeclPtrOfTy(iTy); if (auto found = structInheritedMembers.find(interfaceDecl); found != structInheritedMembers.end()) { MergeInheritedMembers(inheritedMembers, found->second, *iTy, true); } } // 3. Update member if valid class upperBound existed. if (auto cd = Ty::GetDeclPtrOfTy(classTy)) { auto members = GetInheritedSuperMembers(*cd, *classTy, *generic.curFile); // Since tys are upperbounds of generic, treat them as same inherited types to update 'inconstent' types. MergeInheritedMembers(inheritedMembers, members, *classTy, true); } // 4. Report for members which have conflict upperbounds. for (auto& [_, member] : std::as_const(inheritedMembers)) { if (member.inconsistentTypes.empty()) { continue; } DiagnoseInheritedInsconsistType(member, *gc); } } } cangjie_compiler-1.0.7/src/Sema/InheritanceChecker/InstantiatedChecker.cpp000066400000000000000000000434011510705540100266230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements inheritance checking of structure declarations. */ #include "StructInheritanceChecker.h" #include #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Types.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Sema/TypeManager.h" #include "Diags.h" #include "TypeCheckUtil.h" using namespace Cangjie; using namespace AST; using namespace Sema; using namespace TypeCheckUtil; namespace { void MarkUpperBoundIgnored(const Node& node, Generic& generic) { for (const auto& constraint : generic.genericConstraints) { for (const auto& upperBound : constraint->upperBounds) { if (upperBound->ty->HasGeneric()) { // If the upper bound has generic param, must not check it to prevent infinite recursion. upperBound->visitedByWalkerID = node.visitedByWalkerID; } } } } std::pair, Ptr> GetRealReferenceType(const Node& node) { auto target = GetRealTarget(node.GetTarget()); auto baseTy = node.ty; // Ty is guaranteed by caller. // If target is constructor, instantiate outer structure declaration. if (target && IsClassOrEnumConstructor(*target)) { target = target->outerDecl; CJC_ASSERT(target != nullptr); if (node.ty->IsFunc()) { baseTy = RawStaticCast(node.ty)->retTy; } } return {baseTy, target}; } } // namespace void StructInheritanceChecker::CheckInstMemberSignatures( const InheritableDecl& decl, const std::vector>& instTys) { if (instTriggerInfos.empty()) { return; } auto searchKey = std::make_pair(&decl, instTys); if (instantiatedDecls.count(searchKey) != 0) { for (auto& [parent, member] : std::as_const(genericMembersForInstantiatedDecl[searchKey])) { DiagnoseForInstantiatedMember(parent, member); } return; } auto typeMapping = GenerateTypeMapping(decl, instTys); CheckMembersWithInheritedDecls(decl); auto members = structInheritedMembers[&decl]; for (auto& member : decl.GetMemberDecls()) { CJC_NULLPTR_CHECK(member); if (!Ty::IsTyCorrect(member->ty) || !member->outerDecl) { continue; } // Add constructors and private functions again for instantiated member's checking. if (IsInstanceConstructor(*member) || member->TestAttr(Attribute::PRIVATE)) { (void)UpdateInheritedMemberIfNeeded(members, MemberSignature{member, member->ty, decl.ty}); } } MemberMap genericTyMembers; for (auto it = members.begin(); it != members.end();) { // Ignore generic member and non-function which will not causing collision after instantiation. if (it->second.decl->TestAttr(Attribute::GENERIC) || it->second.decl->astKind != ASTKind::FUNC_DECL) { it = members.erase(it); } else if (it->second.ty->HasGeneric()) { // Move generic ty member to other container. genericTyMembers.emplace(it->first, it->second); it = members.erase(it); } else { ++it; } } auto& cache = instantiatedTyCache[searchKey]; auto getAndUpdateCache = [this, &cache, &typeMapping](auto ty) { auto found = cache.find(ty); if (found != cache.end()) { return found->second; } else { auto instTy = typeManager.GetInstantiatedTy(ty, typeMapping); cache[ty] = instTy; return instTy; } }; for (auto [identifier, memberSig] : std::as_const(genericTyMembers)) { memberSig.ty = getAndUpdateCache(memberSig.ty); memberSig.structTy = getAndUpdateCache(memberSig.structTy); auto matches = members.equal_range(identifier); for (auto it = matches.first; it != matches.second; ++it) { auto parent = it->second; parent.structTy = getAndUpdateCache(parent.structTy); // Check & Diagnose for instantiated collision. DiagnoseForInstantiatedMember(parent, memberSig); genericMembersForInstantiatedDecl[searchKey].emplace_back(std::make_pair(parent, memberSig)); } members.emplace(identifier, memberSig); } } std::set, CmpNodeByPos> StructInheritanceChecker::GetVisibleExtendsForInstantiation( const Decl& decl, const std::vector>& instTys) { if (decl.astKind == ASTKind::EXTEND_DECL || !Ty::IsTyCorrect(decl.ty)) { return {}; } std::set, CmpNodeByPos> orderedVisibleExtends; auto extends = typeManager.GetAllExtendsByTy(*decl.ty); for (auto& extend : extends) { // Check for extend decl if it is defined in current package or it has external attribute, // and also the instantiation meets extend constraints. bool visibleExtend = (IsExtendVisibleInCurpkg(*extend)) && extend->TestAttr(Attribute::GENERIC) && typeManager.CheckGenericDeclInstantiation(extend, instTys); if (visibleExtend) { orderedVisibleExtends.emplace(extend); } } return orderedVisibleExtends; } bool StructInheritanceChecker::WillCauseInfiniteInstantiation( const Node& triggerNode, const Decl& decl, const std::vector>& instTys) { auto generic = decl.GetGeneric(); // Relation between 'generic' and 'instTys' already been checked in previous step in 'CheckInstDupFuncsRecursively'. CJC_ASSERT(generic && generic->typeParameters.size() == instTys.size()); bool triggeredInside = false; TypeSubst typeMapping = institutionMaps.empty() ? TypeSubst{} : institutionMaps.top(); if (HaveCyclicSubstitution(typeManager, GenerateTypeMapping(decl, instTys)) || HaveCyclicSubstitution(typeManager, typeMapping)) { diag.Diagnose(triggerNode, DiagKind::sema_generic_infinite_instantiation); infiniteInstantiationOccured = true; return true; } if (!instTriggerInfos.empty()) { // For indirect instantiated case like: // class A { // func test(a: B>) {} // } // class B { // func test(a: A<(T,T)>) {} <== detect infinite instantiation here. // } // A() <== triggered from here. // Index 1 of 'instTriggerInfo' value is the decl which previously triggered instantiation. triggeredInside = &decl == std::get<1>(instTriggerInfos.back()); } Ptr genericTy = TypeManager::GetInvalidTy(); auto checkRecursion = [&genericTy, triggeredInside, typeMapping, this](auto ty) -> bool { CJC_ASSERT(ty); auto substitutedTy = typeManager.GetInstantiatedTy(ty, typeMapping); bool hasRecursion = ty->Contains(genericTy) || substitutedTy->Contains(genericTy); if (hasRecursion) { return true; // Directly and indirectly cause generic infinite instantiation. } // For indirect instantiated case. auto typeArg = Ty::GetGenericTyOfInsTy(*ty); bool noRecursion = !triggeredInside || (typeArg && typeArg->Contains(genericTy)); if (noRecursion) { return false; } substitutedTy = Ty::GetGenericTyOfInsTy(*substitutedTy); return substitutedTy && substitutedTy->Contains(genericTy); }; bool hasSelfRecursion = false; for (size_t i = 0; i < instTys.size(); ++i) { genericTy = generic->typeParameters[i]->ty; // Update ty which captured by lambda; hasSelfRecursion = std::any_of(instTys[i]->typeArgs.begin(), instTys[i]->typeArgs.end(), checkRecursion); if (hasSelfRecursion) { break; } } if (hasSelfRecursion) { diag.Diagnose(triggerNode, DiagKind::sema_generic_infinite_instantiation); infiniteInstantiationOccured = true; } return hasSelfRecursion; } VisitAction StructInheritanceChecker::CheckInstDupFuncsRecursively(Node& node) { if (infiniteInstantiationOccured) { return VisitAction::STOP_NOW; } if (node.astKind == ASTKind::GENERIC) { MarkUpperBoundIgnored(node, static_cast(node)); } if (!Ty::IsTyCorrect(node.ty) || node.TestAttr(Attribute::HAS_BROKEN)) { return VisitAction::WALK_CHILDREN; } auto [baseTy, target] = GetRealReferenceType(node); if (!target || target->TestAttr(Attribute::IN_REFERENCE_CYCLE) || !Ty::IsTyCorrect(baseTy)) { return VisitAction::WALK_CHILDREN; } if (target->IsNominalDecl() && target->TestAttr(Attribute::GENERIC)) { // NOTE: We must guarantee 'target' decl does not have 'generic infinite instantiation' status // before checking for its instantiated status, and also avoid re-entry. if (auto [_, success] = instantiatedDecls.insert(std::make_pair(target, std::vector>{})); success) { Walker(target, [this](auto node) { return CheckInstDupFuncsRecursively(*node); }).Walk(); } } CheckInstWithCStructTypeArg(node); auto ref = DynamicCast(&node); std::vector> instTys = ref ? ref->instTys : typeManager.GetTypeArgs(*baseTy); TypeSubst typeMapping = GenerateTypeMapping(*target, instTys); // If failed to generate new typeMapping with current target, return now. if (typeMapping.empty()) { return VisitAction::WALK_CHILDREN; } // If current instantiation will cause infinite instantiation, return now. if (WillCauseInfiniteInstantiation(node, *target, instTys)) { return VisitAction::STOP_NOW; } if (!institutionMaps.empty()) { // Update instTys by current typeMapping. for (auto& ty : instTys) { ty = typeManager.GetInstantiatedTy(ty, institutionMaps.top()); } } // Update instantiation trigger node if current node's ty is fully instantiated or current is outermost checking. bool updateTriggerInfo = !baseTy->HasGeneric() || institutionMaps.empty(); auto instantiateCtx = InstantiatedContext(*this, &node, target, instTys, updateTriggerInfo); if (target->IsNominalDecl()) { // Check for function signatures. CheckInstMemberSignatures(*RawStaticCast(target), instTys); } auto extends = GetVisibleExtendsForInstantiation(*target, instTys); for (auto extend : extends) { // Check for function signatures of related extend decls. CheckInstMemberSignatures(*extend, instTys); // Extend will not appeared as referenced target, so we need cache it here. instantiatedDecls.insert(std::make_pair(target, instTys)); } // Walk if current instantiation of target has not been checked. if (auto [_, success] = instantiatedDecls.insert(std::make_pair(target, instTys)); success) { CheckInstantiatedDecl(*target, instTys); // NOTE: since we instantiate extends on use, do not check for extend recursively here. } return VisitAction::WALK_CHILDREN; } void StructInheritanceChecker::CheckInstantiatedDecl(Decl& decl, const std::vector>& instTys) { if (decl.IsBuiltIn()) { return; // Do not check for builtin decl. } TypeSubst typeMapping; if (auto ed = DynamicCast(&decl); ed && ed->ty && ed->ty->IsNominal()) { // For extend decl of a user defined type, treat the extend decl members' type as inside origin type decl. auto target = Ty::GetDeclPtrOfTy(ed->ty); if (target == nullptr) { return; } typeMapping = GenerateTypeMapping(*target, instTys); typeMapping.merge(GenerateTypeMapping(decl, ed->ty->typeArgs)); } else { typeMapping = GenerateTypeMapping(decl, instTys); } institutionMaps.push(typeMapping); Walker(&decl, [this](auto node) { return CheckInstDupFuncsRecursively(*node); }).Walk(); institutionMaps.pop(); } void StructInheritanceChecker::CheckInstDupFuncsInNominalDecls() { for (auto& file : pkg.files) { for (auto& decl : file->decls) { // NOTE: We must guarantee 'target' decl does not have 'generic infinite instantiation' status // before checking for its instantiated status, and also avoid re-entry. if (auto [_, success] = instantiatedDecls.insert(std::make_pair(decl.get(), std::vector>{})); success) { Walker(decl.get(), [this](auto node) { return CheckInstDupFuncsRecursively(*node); }).Walk(); } } } } void StructInheritanceChecker::CheckInstWithCStructTypeArg(const Node& node) { if (!Utils::In(node.astKind, {ASTKind::REF_TYPE, ASTKind::REF_EXPR, ASTKind::MEMBER_ACCESS})) { return; } if (auto rt = DynamicCast(&node); rt) { if (!rt->typeArguments.empty()) { CheckCStructArguments(*rt, *rt->ty, rt->leftAnglePos, rt->GetTypeArgs()); } return; } // For the following case: // @C // struct Data {} // enum TimeUnit { // | Year(T1) // | Month(T1, T2) // } // var t = TimeUnit.Year(Data()) // To avoid repeated error reporting, we check the RefExpr `TimeUnit`, // instead of MemberAccess `TimeUnit.Year`. auto target = node.GetTarget(); if (!target) { return; } if (node.astKind == ASTKind::MEMBER_ACCESS && target->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { return; } // The 'node' will only be 'RefExpr' or 'MemberAccess' here, which is must the type of NameReferenceExpr. auto& ref = static_cast(node); if (ref.instTys.empty()) { return; } auto typeArgs = ref.GetTypeArgs(); if (ref.instTys.size() == typeArgs.size()) { auto ty = ref.ty; if (ref.astKind == ASTKind::REF_EXPR && target->TestAttr(Attribute::CONSTRUCTOR)) { CJC_NULLPTR_CHECK(target->outerDecl); ty = target->outerDecl->ty; } Position leftAnglePos = ref.astKind == ASTKind::REF_EXPR ? static_cast(ref).leftAnglePos : static_cast(ref).leftAnglePos; CheckCStructArguments(ref, *ty, leftAnglePos, typeArgs); return; } } void StructInheritanceChecker::CheckCStructArguments(const Node& node, const AST::Ty& ty, const Position& leftAnglePos, const std::vector>& typeArgs) { if (typeArgs.empty()) { return; } if (Ty::IsCStructType(ty)) { (void)diag.Diagnose(node, leftAnglePos, DiagKind::sema_cffi_cannot_have_type_param, "struct with @C"); } for (auto& type : typeArgs) { CheckCStructArgument(ty, *type); } } void StructInheritanceChecker::CheckCStructArgument(const Ty& ty, const Type& typeArg) { if (!Ty::IsTyCorrect(typeArg.ty)) { return; } // Transitional state, only one will be retained in the future. if (ty.IsPointer()) { // The type arg of CPointer constraint: // 1. CPointer, defined in core/CPointer.cj // 2. CPointer<`CType`> if (!typeArg.ty->IsGeneric() && !Ty::IsMetCType(*typeArg.ty)) { diag.Diagnose(typeArg, DiagKind::sema_illegal_cpointer_generic_type); } return; } // Only Array, Array is allowed. Other CType argument is not. if (ty.IsStructArray() && (typeArg.ty->IsCString() || typeArg.ty->IsPointer())) { return; } CheckCStruct(*typeArg.ty, typeArg); } void StructInheritanceChecker::CheckCStruct(const Ty& ty, const Type& typeArg) { if (!Ty::IsTyCorrect(&ty)) { return; } // String and CPointer type is special, we should solve this after. if (ty.IsPointer()) { // The type arg of CPointer constraint: // 1. CPointer, defined in core/CPointer.cj // 2. CPointer<`CType`> if (!ty.typeArgs.empty() && ty.typeArgs[0] && !ty.typeArgs[0]->IsGeneric() && !Ty::IsMetCType(*ty.typeArgs[0])) { diag.Diagnose(typeArg, DiagKind::sema_illegal_cpointer_generic_type); } return; } if (auto fty = DynamicCast(&ty); fty) { for (auto it : fty->typeArgs) { CheckCStruct(*it, typeArg); } return; } for (auto it : ty.typeArgs) { auto sty = DynamicCast(it); if (sty == nullptr) { continue; } CheckCStruct(*it, typeArg); } } void StructInheritanceChecker::DiagnoseForInstantiatedMember( const MemberSignature& parent, const MemberSignature& child) const { if (instTriggerInfos.empty()) { return; } auto parentFuncTy = DynamicCast(parent.ty); auto childFuncTy = DynamicCast(child.ty); bool sameStatus = parent.decl->TestAttr(Attribute::STATIC) == child.decl->TestAttr(Attribute::STATIC); bool isConflicted = sameStatus && parentFuncTy && childFuncTy && typeManager.IsFuncParameterTypesIdentical(*parentFuncTy, *childFuncTy); if (isConflicted) { std::string functionName = child.decl->identifier; OrderedDeclSet candidates = {parent.decl, child.decl}; auto [triggerNode, triggerDecl, instTys] = instTriggerInfos.back(); std::string triggeredDeclType = triggerDecl->identifier.Val() + '<' + Ty::GetTypesToStr(instTys, ", ") + ">"; auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_generic_instantiation_causes_ambiguous_functions, *triggerNode, triggeredDeclType, functionName); for (auto& candidate : candidates) { builder.AddNote(*candidate, MakeRangeForDeclIdentifier(*candidate), "found candidate"); } } } cangjie_compiler-1.0.7/src/Sema/InheritanceChecker/MergeInheritedMemberHelper.cpp000066400000000000000000000256511510705540100301010ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements how to merge inherited parent member to child member. */ #include "StructInheritanceChecker.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Types.h" #include "cangjie/AST/Utils.h" #include "cangjie/Sema/TypeManager.h" #include "TypeCheckUtil.h" #include "NativeFFI/Java/TypeCheck/InheritanceChecker.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; namespace { void UpdateInconsistentTypes( MemberSignature& member, const MemberSignature& parent, const std::vector>& inconsistentTypes) { member.inconsistentTypes.insert(parent.inconsistentTypes.begin(), parent.inconsistentTypes.end()); member.inconsistentTypes.insert(inconsistentTypes.begin(), inconsistentTypes.end()); } std::vector>> UpdateUpperBoundsSet( TypeManager& tyMgr, const std::vector>>& upperBounds, const TypeSubst& typeMapping) { if (typeMapping.empty() || std::all_of(upperBounds.begin(), upperBounds.end(), [](auto it) { return it.empty(); })) { // If typeMapping is empty or 'upperBounds' is empty or // All elements in 'upperBounds' are empty, quick quit. return upperBounds; } std::vector>> allUpperBounds; for (const auto& uppers : std::as_const(upperBounds)) { std::unordered_set> newUppers; for (auto it : uppers) { newUppers.emplace(tyMgr.GetInstantiatedTy(it, typeMapping)); } allUpperBounds.emplace_back(newUppers); } return allUpperBounds; } // Caller guarantees 'src' and 'other' are both generic function. void MergeUpperBounds(TypeManager& tyMgr, MemberSignature& src, const MemberSignature& other, bool shouldMerge) { bool ignored = !shouldMerge || src.decl == other.decl || src.upperBounds.empty() || src.upperBounds.size() != other.upperBounds.size(); if (ignored) { return; } TypeSubst typeMapping = tyMgr.GenerateGenericMappingFromGeneric( *RawStaticCast(other.decl), *RawStaticCast(src.decl)); for (size_t i = 0; i < other.upperBounds.size(); ++i) { for (auto upper : other.upperBounds[i]) { src.upperBounds[i].emplace(tyMgr.GetInstantiatedTy(upper, typeMapping)); } } } void SetPossibleInconsistentType( const MemberSignature& target, const MemberSignature& src, std::vector>& inconsistentTypes) { auto getTy = [](auto member) { if (auto ty = DynamicCast(member.ty)) { return ty->retTy; } else { return member.ty; } }; inconsistentTypes.emplace_back(getTy(target)); inconsistentTypes.emplace_back(getTy(src)); } } // namespace /** * Compute whether 'parent' and 'child' contains inconsistent return type. If so, update 'inconsistentTypes' * Update 'updated' result's type if 'parent' and 'child' are both from inherited interfaces. * and return type of 'parent' is subtype of return type of 'child'. * @p status means {parent and child have same signature, parent and child are both from inherited interfaces}. */ bool StructInheritanceChecker::ComputeInconsistentTypes(const MemberSignature& child, const MemberSignature& parent, MemberSignature& updated, const std::pair& status, std::vector>& inconsistentTypes) const { auto parentTy = DynamicCast(parent.ty); auto childTy = DynamicCast(child.ty); if (!Ty::IsTyCorrect(parentTy) || !Ty::IsTyCorrect(childTy)) { return true; } bool hasConsistentReturnTy = true; if (status.first) { // If child's ret type is subtype of parent, and parent is abstract or both not abstract, return type is valid. bool isValidOverride = parent.decl->TestAttr(Attribute::ABSTRACT) || !child.decl->TestAttr(Attribute::ABSTRACT); hasConsistentReturnTy = isValidOverride && AreReturnTypesCompatible(*parentTy, *childTy); // If two decls are both interface members that are inherited from current type // and parent's return type is subtype of child, using parentTy. // The overridden return type is consistent. bool bothInterfaceMember = status.second && child.decl->outerDecl->astKind == ASTKind::INTERFACE_DECL && parent.decl->outerDecl->astKind == ASTKind::INTERFACE_DECL; bool needUpdateTy = bothInterfaceMember && AreReturnTypesCompatible(*childTy, *parentTy); hasConsistentReturnTy = hasConsistentReturnTy || needUpdateTy; if (needUpdateTy) { updated.ty = parentTy; } } if (!hasConsistentReturnTy) { SetPossibleInconsistentType(parent, child, inconsistentTypes); } return hasConsistentReturnTy; } void StructInheritanceChecker::UpdateOverriddenFuncDeclCache(Ptr child, Ptr parent) { if (checkingDecls.empty()) { return; } if (child->outerDecl == checkingDecls.back()) { typeManager.UpdateTopOverriddenFuncDeclCache(child, parent); } } /** * Update @p child value into the @p members * NOTE: Do not report error here. * @param inheritedInterfaces Indicates parent and child are members of interfaces that inherited by same type. * This will also be true if parent and child's parent types are from generic upper bounds. */ MemberSignature StructInheritanceChecker::UpdateInheritedMemberIfNeeded( MemberMap& inheritedMembers, const MemberSignature& child, bool inheritedInterfaces) { MemberSignature updated = child; bool needImplement = child.decl->outerDecl->astKind == ASTKind::INTERFACE_DECL || inheritedInterfaces; auto foundMembers = inheritedMembers.equal_range(child.decl->identifier); std::vector> inconsistentTypes; for (auto it = foundMembers.first; it != foundMembers.second; ++it) { auto parent = it->second; if (parent.decl->astKind != child.decl->astKind) { continue; } bool shouldUpdate = true; bool inconsistentType = needImplement && parent.ty != child.ty; if (parent.decl->IsFunc()) { if (child.decl->TestAttr(Attribute::GENERIC) && parent.decl->TestAttr(Attribute::GENERIC)) { TypeSubst typeMapping = typeManager.GenerateGenericMappingFromGeneric( *RawStaticCast(parent.decl), *RawStaticCast(child.decl)); parent.ty = typeManager.GetInstantiatedTy(parent.ty, typeMapping); } auto parentFuncTy = DynamicCast(parent.ty); auto childFuncTy = DynamicCast(child.ty); bool sameStatus = parent.decl->TestAttr(Attribute::STATIC) == child.decl->TestAttr(Attribute::STATIC) && child.decl->TestAttr(Attribute::GENERIC) == parent.decl->TestAttr(Attribute::GENERIC); shouldUpdate = sameStatus && parentFuncTy && childFuncTy && typeManager.IsFuncParameterTypesIdentical(*parentFuncTy, *childFuncTy); auto consistent = ComputeInconsistentTypes( child, parent, updated, {shouldUpdate, inheritedInterfaces}, inconsistentTypes); inconsistentType = inconsistentType && !consistent; } else if (inconsistentType) { SetPossibleInconsistentType(parent, child, inconsistentTypes); } CheckNativeFFI(parent, child); if (shouldUpdate) { if (parent.decl->TestAttr(Attribute::ABSTRACT) || !child.decl->TestAttr(Attribute::ABSTRACT)) { UpdateOverriddenFuncDeclCache(child.decl, parent.decl); it->second = updated; // Override stored value to child. } it->second.replaceOther = true; if (needImplement && (!parent.inconsistentTypes.empty() || inconsistentType)) { UpdateInconsistentTypes(it->second, parent, inconsistentTypes); } MergeUpperBounds(typeManager, it->second, parent, inheritedInterfaces); MergeUpperBounds(typeManager, it->second, child, inheritedInterfaces); if (!parent.extendDecl || !child.extendDecl) { it->second.extendDecl = nullptr; } it->second.isInheritedInterface = child.isInheritedInterface && inheritedInterfaces; // Update status. // If default interface member merged to other default interface member, and: // 1. current is merging members of type inherited interfaces, or // 2. current is merging extend visible members in other extend // eg: interface I1 { func foo(): Unit {} }; interface I2 <: I1 { func foo(): Unit {} }; // open class C1 <: I1; class C2 <: C1 & I1; is allowed to ignore 'foo' in C2. // class A <: I1 & I2; the 'foo' must be implemented. // extend A <: I1 {}; extend A <: I2; the 'foo' does not have to be implemented. // interface I3 { func foo(): Unit {} }; extend A <: I1 {}; extend A <: I3; the 'foo' must be // implemented. bool isRelationExtend = true; if (!checkingDecls.empty() && checkingDecls[0]->astKind == ASTKind::EXTEND_DECL && child.extendDecl) { auto curExtend = StaticCast(checkingDecls[0].get()); isRelationExtend = typeManager.IsExtendInheritRelation(*curExtend, *child.extendDecl).first; } it->second.shouldBeImplemented = (it->second.isInheritedInterface || !isRelationExtend) && child.decl->TestAttr(Attribute::DEFAULT) && parent.decl->TestAttr(Attribute::DEFAULT); return it->second; } } inheritedMembers.emplace(child.decl->identifier, updated); return updated; } /** * Merge @p otherMembers to @p members * If member of the otherMembers implements member in the 'members', replace the member in 'members'. * NOTE: Do not report error here. */ void StructInheritanceChecker::MergeInheritedMembers( MemberMap& members, const MemberMap& otherMembers, Ty& structTy, bool inheritedInterfaces) { MultiTypeSubst mts; typeManager.GenerateGenericMapping(mts, structTy); auto typeMapping = MultiTypeSubstToTypeSubst(mts); for (auto& member : otherMembers) { auto memberSig = member.second; if (inheritedInterfaces) { memberSig.isInheritedInterface = true; } memberSig.ty = typeManager.GetInstantiatedTy(memberSig.ty, typeMapping); memberSig.structTy = typeManager.GetInstantiatedTy(memberSig.structTy, typeMapping); memberSig.upperBounds = UpdateUpperBoundsSet(typeManager, memberSig.upperBounds, typeMapping); UpdateInheritedMemberIfNeeded(members, memberSig, inheritedInterfaces); } } cangjie_compiler-1.0.7/src/Sema/InheritanceChecker/StructInheritanceChecker.cpp000066400000000000000000002020761510705540100276370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements inheritance checking of structure declarations. */ #include "StructInheritanceChecker.h" #include #include #include #include #include #include #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Types.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Modules/ModulesUtils.h" #include "cangjie/Sema/TestManager.h" #include "cangjie/Sema/TypeManager.h" #include "../NativeFFI/Java/AfterTypeCheck/Utils.h" #include "Diags.h" #include "TypeCheckUtil.h" #include "TypeCheckerImpl.h" #include "NativeFFI/Java/TypeCheck/InheritanceChecker.h" using namespace Cangjie; using namespace AST; using namespace Sema; using namespace TypeCheckUtil; namespace { /** Remove members which cannot be inherited. */ void RemoveMembersShouldNotInherit(MemberMap& members) { for (auto it = members.begin(); it != members.end();) { it = it->second.decl->TestAttr(Attribute::PRIVATE) ? members.erase(it) : ++it; } } bool IsInvisibleMember(const Decl& member, const std::string& currentPkg) { if (member.TestAttr(Attribute::PRIVATE)) { return true; } auto relation = Modules::GetPackageRelation(currentPkg, member.fullPackageName); // 'IsVisible' is used for toplevel decl, member decl with 'PROTECTED' is also visible in children. return !Modules::IsVisible(member, relation) && !member.TestAttr(Attribute::PROTECTED); } /** Remove members which is only visible in defined package. */ void RemoveInvisibleMember(MemberMap& members, const std::string& currentPkg) { for (auto it = members.cbegin(); it != members.cend();) { CJC_NULLPTR_CHECK(it->second.decl); it = IsInvisibleMember(*it->second.decl, currentPkg) ? members.erase(it) : ++it; } } bool AreFuncParameterNameIdentical(const FuncDecl& parent, const FuncDecl& child) { CJC_NULLPTR_CHECK(parent.funcBody->paramLists[0]); CJC_NULLPTR_CHECK(child.funcBody->paramLists[0]); auto& parentParams = parent.funcBody->paramLists[0]->params; auto& childParams = child.funcBody->paramLists[0]->params; CJC_ASSERT(parentParams.size() == childParams.size()); for (size_t i = 0; i < parentParams.size(); ++i) { auto& parentParam = parentParams[i]; auto& childParam = childParams[i]; CJC_NULLPTR_CHECK(parentParam); CJC_NULLPTR_CHECK(childParam); if (parentParam->isNamedParam != childParam->isNamedParam) { return false; } if (parentParam->isNamedParam && parentParam->identifier != childParam->identifier) { return false; } } return true; } void DiagnoseParameterName(DiagnosticEngine& diag, const FuncDecl& parent, const FuncDecl& child) { // The caller should guarantee the pointers are non-null and valid! auto diagBuilder = diag.DiagnoseRefactor(DiagKindRefactor::sema_param_named_mismatched, child, MakeRange(child.identifier)); auto& parentParams = parent.funcBody->paramLists[0]->params; auto& childParams = child.funcBody->paramLists[0]->params; CJC_ASSERT(parentParams.size() == childParams.size()); for (size_t i = 0; i < parentParams.size(); ++i) { auto& parentParam = parentParams[i]; auto& childParam = childParams[i]; if (!parentParam->isNamedParam && childParam->isNamedParam) { diagBuilder.AddHint(MakeRange(childParam->GetIdentifierPos(), childParam->identifier), "this named parameter conflicts with the corresponding positional parameter in the supertype"); } else if (parentParam->isNamedParam && !childParam->isNamedParam) { diagBuilder.AddHint(MakeRange(childParam->GetIdentifierPos(), childParam->identifier), "this positional parameter conflicts with the corresponding named parameter in the supertype"); } else if (parentParam->isNamedParam && childParam->isNamedParam && parentParam->identifier != childParam->identifier) { diagBuilder.AddHint(MakeRange(childParam->GetIdentifierPos(), childParam->identifier), "the corresponding parameter in the supertype is named '" + parentParam->identifier + "'"); } } auto subDiag = SubDiagnostic( MakeRange(parent.GetIdentifierPos(), parent.identifier), "the corresponding function of the supertype is"); diagBuilder.AddNote(subDiag); } std::string StringifyInconsistentTypes( const std::unordered_set>& inconsistentTypes, Ptr childTy) { std::set, CmpTyByName> sortedTys(inconsistentTypes.cbegin(), inconsistentTypes.cend()); sortedTys.erase(childTy); return "'" + Ty::GetTypesToStr(sortedTys, "', '") + "'"; } std::vector>> GetAllGenericUpperBounds(TypeManager& tyMgr, const Decl& decl) { auto generic = decl.GetGeneric(); if (!generic) { return {}; } std::vector>> allUpperBounds; for (auto& type : generic->typeParameters) { auto genericTy = DynamicCast(type->ty); if (!genericTy) { continue; } // NOTE: Since 'upperBounds' contains all direct and transitive non-generic upperbounds, // we also need to collect generic upperBounds to check generic constraints correctly. std::set> tys(genericTy->upperBounds.begin(), genericTy->upperBounds.end()); std::queue> q; q.push(genericTy); std::unordered_set> traversedTy = {}; while (!q.empty()) { auto gTy = q.front(); q.pop(); if (auto [_, success] = traversedTy.emplace(gTy); !success) { continue; } for (auto upper : gTy->upperBounds) { if (upper->IsGeneric()) { q.push(RawStaticCast(upper)); tys.emplace(upper); } } } if (tys.size() > 1) { allUpperBounds.emplace_back(std::unordered_set>{tyMgr.GetIntersectionTy(tys)}); } else { allUpperBounds.emplace_back(tys.begin(), tys.end()); } } return allUpperBounds; } // Caller guarantees the given 'index' has corresponding constraint node. Ptr GetConstraintNodeByIndex(size_t index, Generic& generic) { CJC_ASSERT(index < generic.typeParameters.size()); CJC_NULLPTR_CHECK(generic.typeParameters[index]); auto gTy = generic.typeParameters[index]->ty; for (auto& constraint : generic.genericConstraints) { CJC_NULLPTR_CHECK(constraint->type); if (constraint->type->ty == gTy) { return constraint.get(); } } // Should not reach here. CJC_ABORT(); return generic.typeParameters[index].get(); } void DiagWeakVisibility(DiagnosticEngine& diag, const Decl& parent, const Decl& child, const Decl& diagNode) { auto visibilityToString = [](const Decl& decl) -> std::string { if (decl.TestAttr(Attribute::PUBLIC)) { return "public"; } else if (decl.TestAttr(Attribute::PROTECTED)) { return "protected"; } else if (decl.TestAttr(Attribute::PRIVATE)) { return "private"; } else { return "internal"; } }; auto parentRange = MakeRangeForDeclIdentifier(parent); auto childRange = MakeRangeForDeclIdentifier(child); std::string childHint = "the visibility of the deriving '" + child.identifier + "' is '" + visibilityToString(child) + "'"; auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_weak_visibility, MakeRangeForDeclIdentifier(diagNode)); if (&child == &diagNode) { builder.AddMainHintArguments(childHint); } else { // child is inherited from other parent builder.AddMainHintArguments("'" + child.identifier + "' is inherited"); SubDiagnostic childNote(childRange, "the deriving '" + child.identifier + "' is defined here"); childNote.AddMainHint(childRange, childHint); builder.AddNote(childNote); } SubDiagnostic parentNote( parentRange, "the visibility of the base '" + parent.identifier + "' is '" + visibilityToString(parent) + "'"); if (parent.outerDecl->astKind == ASTKind::INTERFACE_DECL) { parentNote.AddMainHint(parentRange, "the base '" + parent.identifier + "' is in 'interface'"); } builder.AddNote(parentNote); } // If node is from common part, check for decls extend or implement common decl. bool NeedRecheck(InheritableDecl& id) { auto superDecls = id.GetAllSuperDecls(); std::vector> extendDecls; if (id.astKind == ASTKind::EXTEND_DECL) { auto ed = RawStaticCast(&id); if (auto decl = Ty::GetDeclPtrOfTy(ed->extendedType->ty); decl && decl->IsNominalDecl()) { // extend in common part for platform decl. if (decl->TestAttr(Attribute::PLATFORM)) { return true; } extendDecls = RawStaticCast(decl)->GetAllSuperDecls(); } } auto checkCommon = [](Ptr it) { return it->TestAttr(Attribute::COMMON); }; return std::any_of(superDecls.cbegin(), superDecls.cend(), checkCommon) || std::any_of(extendDecls.cbegin(), extendDecls.cend(), checkCommon); } // Check whether inheritance rules need to be checked. bool NeedCheck(Node& node) { if (!node.IsNominalDecl()) { return false; } // Only incremental compilation case will meet generic instantiated decl. // Decl from common part do not check again. if (node.TestAttr(Attribute::GENERIC_INSTANTIATED)) { return false; } // If node is from common part, check for decls extend or implement common decl. if (node.TestAttr(Attribute::FROM_COMMON_PART)) { // Except common decls. if (node.TestAttr(Attribute::COMMON)) { return false; } return NeedRecheck(*RawStaticCast(&node)); } return true; } bool CompMemberSignatureByPosAndTy(Ptr m1, Ptr m2) { if (m1->decl != m2->decl) { return CompNodeByPos(m1->decl, m2->decl); } return CompTyByNames(m1->ty, m2->ty); } } // namespace void TypeChecker::TypeCheckerImpl::CheckInheritance(Package& pkg) { StructInheritanceChecker checker(diag, typeManager, pkg, importManager, ci->invocation.globalOptions); checker.Check(); } void StructInheritanceChecker::Check() { if (pkg.TestAnyAttr(Attribute::IMPORTED, Attribute::TOOL_ADD)) { return; } std::vector> structDecls; std::vector> extendDecls; Walker(&pkg, [&structDecls, &extendDecls](auto node) { if (NeedCheck(*node)) { if (node->astKind == ASTKind::EXTEND_DECL) { extendDecls.emplace_back(RawStaticCast(node)); } else { structDecls.emplace_back(RawStaticCast(node)); } return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }).Walk(); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto sortedExtends = GetAllNeedCheckExtended(); extendDecls.insert(extendDecls.end(), sortedExtends.cbegin(), sortedExtends.cend()); #endif for (auto decl : structDecls) { if (decl->TestAttr(Attribute::IS_BROKEN) || !Ty::IsTyCorrect(decl->ty)) { continue; // Do not check for incorrect structure declaration. } CheckMembersWithInheritedDecls(*decl); } for (auto decl : extendDecls) { if (decl->TestAttr(Attribute::IS_BROKEN) || !Ty::IsTyCorrect(decl->ty) || !IsExtendVisibleInCurpkg(*decl)) { continue; // Do not check for incorrect structure declaration. } CheckMembersWithInheritedDecls(*decl); } // Check generic upper bounds member confliction. CheckAllUpperBoundsConfliction(); // Check function confliction for instantiated decl. CheckInstDupFuncsInNominalDecls(); } void StructInheritanceChecker::CheckMembersWithInheritedDecls(const InheritableDecl& decl) { if (structInheritedMembers.count(&decl) > 0) { return; } checkingDecls.push_back(&decl); MemberMap interfaceMembers = GetAndCheckInheritedInterfaces(decl); MemberMap instanceMembers = GetAndCheckInheritedMembers(decl); auto [visibleExtendMembers, invisibleMembers] = GetVisibleExtendMembersForExtend(decl); // 0. Merge inherited members for extend decl. Must merge 'instanceMembers' to 'visibleExtendMembers'. // 'instanceMembers' will replace any interface members in 'visibleExtendMembers'. MergeInheritedMembers(visibleExtendMembers, instanceMembers, *decl.ty); // Spec check: "The exported extension cannot indirectly export the functions of the non-exported extension." for (auto& interface : interfaceMembers) { CheckExtendExportDependence(decl, interface.second, visibleExtendMembers); } // 1. Merge & check members inherited in from super class or extended type of extend decl first. for (auto& member : decl.GetMemberDecls()) { if (!Ty::IsTyCorrect(member->ty) || !member->outerDecl || member->TestAttr(Attribute::CONSTRUCTOR)) { continue; } std::pair inherited = visibleExtendMembers.equal_range(member->identifier); MemberSignature memberSig{member, member->ty, decl.ty, nullptr, GetAllGenericUpperBounds(typeManager, *member)}; for (auto it = inherited.first; it != inherited.second; ++it) { DiagnoseForInheritedMember(it->second, memberSig); } std::pair invisibleMatches = invisibleMembers.equal_range(member->identifier); for (auto it = invisibleMatches.first; it != invisibleMatches.second; ++it) { DiagnoseForInheritedMember(it->second, memberSig); } memberSig = UpdateInheritedMemberIfNeeded(visibleExtendMembers, memberSig); } // When processing extend decl, do not report error for interface implementation. // 2. Check whether every interface is implemented. for (auto& interface : interfaceMembers) { interface.second.isInheritedInterface = true; DiagnoseForInheritedInterfaces(interface.second, visibleExtendMembers); } // 3. Merge final inherited members. 'visibleExtendMembers' is merged to 'interfaceMembers'. MergeInheritedMembers(interfaceMembers, visibleExtendMembers, *decl.ty); for (const auto& memberSig : std::as_const(interfaceMembers)) { DiagnoseForOverriddenMember(memberSig.second); } // 4. Check unimplemented interface function. DiagnoseForUnimplementedInterfaces(interfaceMembers, decl); RemoveMembersShouldNotInherit(interfaceMembers); checkingDecls.pop_back(); // Unset local checking status. for (auto& member : interfaceMembers) { member.second.isInheritedInterface = false; member.second.extendDecl = nullptr; } structInheritedMembers.emplace(&decl, interfaceMembers); } /** * Return inherited interfaces' members's map with instantiated type declared in inheritance. * eg: class A <: I1 & I2 -> will return members with ty instantiated as I I2. */ MemberMap StructInheritanceChecker::GetAndCheckInheritedInterfaces(const InheritableDecl& decl) { std::set, CmpTyByName> interfaceTys; for (auto& type : decl.inheritedTypes) { auto baseDecl = Ty::GetDeclPtrOfTy(type->ty); if (!baseDecl || baseDecl->astKind != ASTKind::INTERFACE_DECL || baseDecl->TestAttr(Attribute::IN_REFERENCE_CYCLE)) { continue; } CheckMembersWithInheritedDecls(*baseDecl); interfaceTys.emplace(type->ty); } MemberMap members; // Merge inherited interfaces' members. for (auto iTy : interfaceTys) { auto interfaceDecl = Ty::GetDeclPtrOfTy(iTy); auto interfaceMembers = structInheritedMembers[interfaceDecl]; MergeInheritedMembers(members, interfaceMembers, *iTy, true); } DiagnoseForConflictInheritance(decl, members); return members; } /** * Get given decl's members' map with extended members of geiven decl. * eg: class A <: B -> will return members in B and in extend decls of B. * extend A -> will return members in A. */ MemberMap StructInheritanceChecker::GetInheritedSuperMembers( const InheritableDecl& decl, Ty& baseTy, const File& curFile, bool ignoreExtends) { MemberMap members; // Merge inherited class members. MergeInheritedMembers(members, structInheritedMembers[&decl], baseTy); RemoveInvisibleMember(members, curFile.curPackage->fullPackageName); if (ignoreExtends) { return members; } // Merge extend decls' members of given class. auto extends = typeManager.GetDeclExtends(decl); std::set, CmpNodeByPos> ordered(extends.begin(), extends.end()); for (auto extend : ordered) { if (!extend->extendedType || !Ty::IsTyCorrect(extend->extendedType->ty)) { continue; } if (!importManager.IsExtendAccessible(curFile, *extend)) { continue; } CheckMembersWithInheritedDecls(*extend); auto extendMembers = structInheritedMembers[extend]; RemoveInvisibleMember(members, curFile.curPackage->fullPackageName); MergeInheritedMembers(members, extendMembers, baseTy); } return members; } /** * Return inherited members' map with instantiated type declared in class inheritance. * Or inherited members of extended type of extend decl. * eg: class A <: B -> will return members in B and in extend decls of B. * extend A -> will return members in A. */ MemberMap StructInheritanceChecker::GetAndCheckInheritedMembers(const InheritableDecl& decl) { if (!Ty::IsTyCorrect(decl.ty)) { return {}; } Ptr baseDecl = nullptr; Ptr baseTy = nullptr; if (decl.astKind == ASTKind::CLASS_DECL) { auto& cd = static_cast(decl); baseDecl = cd.GetSuperClassDecl(); baseTy = RawStaticCast(cd.ty)->GetSuperClassTy(); } else if (decl.astKind == ASTKind::EXTEND_DECL) { auto& ed = static_cast(decl); if (ed.extendedType) { baseDecl = Ty::GetDeclPtrOfTy(ed.extendedType->ty); baseTy = ed.extendedType->ty; } } if (!baseDecl || !Ty::IsTyCorrect(baseTy) || baseDecl->TestAttr(Attribute::IN_REFERENCE_CYCLE)) { return {}; } // If common decl is with platform implementation, using platform one. if (baseDecl->platformImplementation) { baseDecl = RawStaticCast(baseDecl->platformImplementation); } CheckMembersWithInheritedDecls(*baseDecl); if (decl.curFile) { return GetInheritedSuperMembers(*baseDecl, *baseTy, *decl.curFile, decl.astKind == ASTKind::EXTEND_DECL); } return {}; } /** * Spec: If the parent and child interfaces are implemented separately for extensions of the same type within the same * package, then the compiler will first check for extensions that implement the parent interface and then for * extensions that implement the child interface. * * So when checking extend declarations, only the parent interface extension of the current extension implementing * interface are collected. * If extended interface have no parent-child relationship, both extensions are collected. * If the interfaces of two extension implementations are cross-inherited, an error is reported. * * eg: interface I1 {} * interface I2 <: I1 {} * class A {} * extend A <: I1 {} * extend A <: I2 {} * when check 'extend A <: I1', member of 'extend A <: I2' will not be collected. * when check 'extend A <: I2', both will be collected. */ void StructInheritanceChecker::CollectExtendByInterfaceInherit(const std::set>& otherExtends, const ExtendDecl& curDecl, std::set, CmpNodeByPos>& ordered) { auto extendedDecl = Ty::GetDeclPtrOfTy(curDecl.ty); if (auto rt = DynamicCast(curDecl.extendedType.get()); rt && rt->GetTarget() && !extendedDecl) { // For Built-in generic type 'CPointer'. extendedDecl = rt->GetTarget(); } for (auto ed : std::as_const(otherExtends)) { if (ed == &curDecl) { continue; } // In the following scenarios, need to check whether to skip extension based on the inheritance relationship. // 1.both extensions are from the same package // 2.both extensions are from different packages, but both are visible in current package // If the two expansions are from different packages and ed is accessible in curDecl's package, need to collect. bool areAllFromOtherPkg = ed->fullPackageName != pkg.fullPackageName && curDecl.fullPackageName != pkg.fullPackageName && IsExtendVisibleInCurpkg(*ed); if (ed->fullPackageName != curDecl.fullPackageName && !areAllFromOtherPkg) { if (importManager.IsExtendAccessible(*curDecl.curFile, *ed)) { ordered.emplace(ed); } continue; } std::optional skipExtend = DeterminingSkipExtendByInheritanceRelationship(curDecl, *ed, extendedDecl); // If 'skipExtend' is nullopt, the two extensions are not sequence-sensitive. if (!skipExtend.has_value() || !skipExtend.value()) { ordered.emplace(ed); } } } std::optional StructInheritanceChecker::DeterminingSkipExtendByInheritanceRelationship( const ExtendDecl& curDecl, const ExtendDecl& ed, const Ptr& extendedDecl) { std::optional skipExtend = std::nullopt; std::pair, Ptr> lastInherTy; auto mappingOfExtended2Ed = extendedDecl ? GenerateTypeMapping(*extendedDecl, ed.extendedType->ty->typeArgs) : TypeSubst(); auto mappingOfExtended2CurExtend = extendedDecl ? GenerateTypeMapping(*extendedDecl, curDecl.extendedType->ty->typeArgs) : TypeSubst(); for (auto& curDeclSuper : std::as_const(curDecl.inheritedTypes)) { Ptr hasSubImpl = nullptr; Ptr hasSuperImpl = nullptr; Ptr curDeclSuperInsTy = curDeclSuper->ty; auto typeArgs = extendedDecl ? extendedDecl->ty->typeArgs : std::vector>(); for (auto typeArg : typeArgs) { auto tyArgGen = StaticCast(typeArg); auto mappingOfCurExtend2Extend = GenerateTypeMappingByTy(mappingOfExtended2CurExtend[tyArgGen], mappingOfExtended2Ed[tyArgGen]); curDeclSuperInsTy = typeManager.GetInstantiatedTy(curDeclSuperInsTy, mappingOfCurExtend2Extend); } for (auto& edSuper : std::as_const(ed.inheritedTypes)) { if (edSuper->ty == curDeclSuperInsTy) { continue; } // Whether at least one extension's interface is the current sub-interface. if (typeManager.IsSubtype(edSuper->ty, curDeclSuperInsTy)) { hasSubImpl = edSuper->ty; } // Whether at least one extension's interface is the current super-interface. if (typeManager.IsSubtype(curDeclSuperInsTy, edSuper->ty)) { hasSuperImpl = edSuper->ty; } } // Another extension implement sub-interface and super-interface of current extension interface at same // time, unable to decide which extension happens first. if (hasSubImpl && hasSuperImpl) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_extend_check_sequence_cannot_decide, curDecl); std::string inherChainStr = hasSubImpl->String() + " <: " + curDeclSuperInsTy->String() + " <: " + hasSuperImpl->String(); builder.AddNote( MakeRange(ed.identifier), "conflict with this extension, beacase of '" + inherChainStr + "'"); return {}; } else if (!hasSubImpl && !hasSuperImpl) { continue; } bool shouldDiag = skipExtend.has_value() && skipExtend.value() != (hasSubImpl != nullptr); if (shouldDiag) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_extend_check_sequence_cannot_decide, curDecl); std::string lastInherStr = lastInherTy.first->String() + " <: " + lastInherTy.second->String(); std::string curInherStr = hasSubImpl ? hasSubImpl->String() + " <: " + curDeclSuperInsTy->String() : curDeclSuperInsTy->String() + " <: " + hasSuperImpl->String(); builder.AddNote(MakeRange(ed.identifier), "conflict with this extension, beacase of '" + lastInherStr + "' and '" + curInherStr + "'"); return {}; } skipExtend = hasSubImpl != nullptr; lastInherTy = skipExtend.value() ? std::make_pair(hasSubImpl, curDeclSuperInsTy) : std::make_pair(curDeclSuperInsTy, hasSuperImpl); } return skipExtend; } void StructInheritanceChecker::CheckExtendExportDependence( const InheritableDecl& curDecl, const MemberSignature& interface, const MemberMap& implDecls) { auto curExtend = DynamicCast(&curDecl); if (!curExtend || !curDecl.IsExportedDecl()) { return; } auto identifier = interface.decl->identifier; auto foundMembers = implDecls.equal_range(identifier); for (auto it = foundMembers.first; it != foundMembers.second; ++it) { auto child = it->second; if (child.decl->astKind != interface.decl->astKind) { continue; } bool isImplFuncOrProp = interface.decl->astKind == ASTKind::PROP_DECL || (interface.decl->IsFunc() && CheckImplementationRelation(interface, child)); auto parentOuter = interface.decl->outerDecl; auto childOuter = child.decl->outerDecl; CJC_NULLPTR_CHECK(childOuter); CJC_NULLPTR_CHECK(parentOuter); if (isImplFuncOrProp && parentOuter->IsExportedDecl() && !childOuter->IsExportedDecl()) { auto builder = diag.DiagnoseRefactor( DiagKindRefactor::sema_export_extend_depend_non_export_extend, curDecl, interface.decl->identifier); builder.AddNote(MakeRange(child.decl->outerDecl->begin, child.decl->outerDecl->end), "following extension cannot be exported"); } } } std::pair StructInheritanceChecker::GetVisibleExtendMembersForExtend(const InheritableDecl& decl) { if (decl.astKind != ASTKind::EXTEND_DECL) { return {}; } auto ed = RawStaticCast(&decl); if (!ed->extendedType || !Ty::IsTyCorrect(ed->extendedType->ty)) { return {}; } MemberMap interfaceMembers; MemberMap extendMembers; MemberMap invisibleInterfaceMembers; MemberMap invisibleExtendMembers; MultiTypeSubst mts; typeManager.GenerateGenericMapping(mts, *ed->extendedType->ty); auto extends = typeManager.GetAllExtendsByTy(*ed->extendedType->ty); std::set, CmpNodeByPos> ordered; CollectExtendByInterfaceInherit(extends, *ed, ordered); for (auto extend : ordered) { if (!extend->extendedType || !Ty::IsTyCorrect(extend->extendedType->ty) || extend == ed) { continue; } if (decl.fullPackageName != extend->fullPackageName && !extend->TestAttr(Attribute::PUBLIC)) { continue; } // Check if the 'extend' is visible from current decl. Store visible and invisble members in different maps. bool visibleExtend = typeManager.CheckGenericDeclInstantiation(extend, ed->extendedType->ty->typeArgs); if (!visibleExtend) { // Invisible extend's MultiTypeSubst will not be generated by 'GenerateGenericMapping'. Generate here. typeManager.GenerateStructDeclGenericMapping(mts, *extend, *ed->extendedType->ty); } auto& interfaceMap = visibleExtend ? interfaceMembers : invisibleInterfaceMembers; auto& extendMap = visibleExtend ? extendMembers : invisibleExtendMembers; auto curInterfaceMap = GetAndCheckInheritedInterfaces(*extend); std::for_each( curInterfaceMap.begin(), curInterfaceMap.end(), [&extend](auto& m) { m.second.extendDecl = extend; }); MergeInheritedMembers(interfaceMap, curInterfaceMap, *ed->extendedType->ty); auto typeMapping = MultiTypeSubstToTypeSubst(mts); for (auto& edMember : extend->GetMemberDecls()) { if (!Ty::IsTyCorrect(edMember->ty) || IsInvisibleMember(*edMember, decl.fullPackageName)) { continue; } auto memberTy = typeManager.GetInstantiatedTy(edMember->ty, typeMapping); auto structTy = typeManager.GetInstantiatedTy(extend->ty, typeMapping); MemberSignature sig{edMember, memberTy, structTy, extend, GetAllGenericUpperBounds(typeManager, *edMember)}; (void)UpdateInheritedMemberIfNeeded(extendMap, sig); } } MergeInheritedMembers(interfaceMembers, extendMembers, *ed->extendedType->ty); MergeInheritedMembers(invisibleInterfaceMembers, invisibleExtendMembers, *ed->extendedType->ty); // Remove if member is abstract; auto isAbstract = [](auto it) { return it.second.decl->TestAttr(Attribute::ABSTRACT); }; Utils::EraseIf(interfaceMembers, isAbstract); Utils::EraseIf(invisibleInterfaceMembers, isAbstract); return {interfaceMembers, invisibleInterfaceMembers}; } // Check & report error with inherited members. void StructInheritanceChecker::DiagnoseForInheritedMember( const MemberSignature& parent, const MemberSignature& child) const { if (parent.decl->TestAttr(Attribute::ENUM_CONSTRUCTOR) && child.decl->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { return; // Do not check between enum constructors. } if (parent.decl->outerDecl == child.decl->outerDecl) { return; // Do not check functions in same decl. } CheckSameNameInheritanceInfo(parent, *child.decl); if (child.decl->astKind != parent.decl->astKind) { return; } if (parent.decl->astKind == ASTKind::PROP_DECL) { CheckInheritanceAttributes(parent, *child.decl); CheckPropertyInheritance(parent, *child.decl); } else if (parent.decl->IsFunc()) { if (CheckImplementationRelation(parent, child)) { CheckInheritanceAttributes(parent, *child.decl); } } CheckNativeFFI(parent, child); } void StructInheritanceChecker::DiagnoseInheritedInsconsistType(const MemberSignature& member, const Node& node) const { CJC_ASSERT(!member.inconsistentTypes.empty() && member.decl->IsFuncOrProp()); std::string typeName = member.decl->astKind == ASTKind::PROP_DECL ? "type" : "return type"; auto range = MakeRange(node.begin, node.end); if (auto decl = DynamicCast(&node)) { range = MakeRange(decl->identifier); } auto diagBuilder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inherit_member_type_inconsistent, node, range, typeName, DeclKindToString(*member.decl), member.decl->identifier.Val()); std::string message = StringifyInconsistentTypes(member.inconsistentTypes, TypeManager::GetInvalidTy()); diagBuilder.AddNote("conflict types are " + message); } void StructInheritanceChecker::DiagnoseForConflictInheritance( const InheritableDecl& decl, const MemberMap& members) const { std::set names; for (auto& it : std::as_const(members)) { names.emplace(it.first); } for (auto it = names.begin(); it != names.end();) { std::string identifier = *it; auto found = members.equal_range(identifier); auto count = static_cast(std::distance(found.first, found.second)); // Only function allows overloading. bool allFunc = std::all_of(found.first, found.second, [](auto& it) { return it.second.decl->IsFunc(); }); if (count > 1 && !allFunc) { auto diagBuilder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inherit_super_member_kind_inconsistent, decl, MakeRange(decl.identifier), identifier); OrderedDeclSet sorted; for (auto mIt = found.first; mIt != found.second; ++mIt) { sorted.emplace(mIt->second.decl); } for (auto member : sorted) { auto message = DeclKindToString(*member) + " member"; diagBuilder.AddNote(*member, MakeRange(member->identifier), message); } // If astKind already conflicted, do not report further inherited types inconsistent error. it = names.erase(it); } else { ++it; } } // Report error when inherited property members types are inconsistent. for (const auto& identifier : std::as_const(names)) { auto found = members.equal_range(identifier); for (auto it = found.first; it != found.second; ++it) { auto member = it->second; if (!member.inconsistentTypes.empty() && member.decl->astKind == ASTKind::PROP_DECL) { DiagnoseInheritedInsconsistType(member, decl); } } } } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND /** * Check whether the member in the parent class extension can completely overwrite the member in the interface. * eg: * interface I1 { * func test(): Unit {} * } * interface I2 {} * class A {} * extend A where T <: I2 { * public func test(): Unit {} * } * class B <: A & I1 {} * * The generic constraint of `extend A` is inconsistent with `class B <: A`, function `test` cannot override or * implement it in interface `I1`. */ void StructInheritanceChecker::CheckIncompleteOverrideOrImplOfExtend( const MemberSignature& interface, const MemberSignature& child) const { auto childOuter = child.decl->outerDecl; if (!childOuter || childOuter->astKind != ASTKind::EXTEND_DECL) { return; } auto childOuterEd = StaticCast(childOuter); auto childOuterDecl = childOuterEd->extendedType->GetTarget(); auto isChildInCheckingDecl = [this, &childOuterDecl]() { if (checkingDecls.empty()) { return false; } if (checkingDecls[0] == childOuterDecl) { return true; } auto allChildExtends = typeManager.GetAllExtendsByTy(*childOuterDecl->ty); for (auto extend : allChildExtends) { if (extend == checkingDecls[0]) { return true; } } return false; }; if (!childOuterDecl || !childOuterDecl->ty->HasGeneric() || isChildInCheckingDecl()) { return; } auto curClassTy = DynamicCast(checkingDecls[0]->ty); if (!curClassTy || childOuterDecl->astKind != ASTKind::CLASS_DECL) { return; } MultiTypeSubst m; typeManager.GenerateGenericMapping(m, *curClassTy); auto instSupers = typeManager.GetInstantiatedTys(childOuterDecl->ty, m); auto diagForIncompleteOverrideOrImplement = [this, &interface, &child, &childOuterDecl]() { std::string funcOrProp = child.decl->astKind == ASTKind::FUNC_DECL ? "function" : "prop"; std::string memberStr = funcOrProp + " '" + child.decl->identifier.Val() + "' in '" + childOuterDecl->identifier.Val() + "''s extension"; std::string interfaceBeOverrided = "interface '" + interface.decl->outerDecl->identifier + "'"; if (interface.decl->TestAttr(Attribute::DEFAULT)) { auto builder = diag.DiagnoseRefactor( DiagKindRefactor::sema_cannot_override, *checkingDecls[0], funcOrProp, child.decl->identifier); builder.AddNote(MakeRange(child.decl->identifier), "member " + memberStr + " conflict with that in " + interfaceBeOverrided); } else { std::string prefix = checkingDecls[0]->astKind == ASTKind::EXTEND_DECL ? "extend " : ""; std::string classNameOverride = prefix + (checkingDecls[0]->ty->IsNominal() ? checkingDecls[0]->ty->name : checkingDecls[0]->ty->String()); auto builder = diag.Diagnose(*checkingDecls[0], DiagKind::sema_interface_member_must_be_implemented_in_struct, funcOrProp, child.decl->identifier.Val(), classNameOverride); builder.AddNote(MakeRange(child.decl->identifier), "member " + memberStr + " may not be visible to '" + classNameOverride + "'"); } }; for (auto inst : instSupers) { if (!typeManager.CheckGenericDeclInstantiation(childOuterEd, inst->typeArgs)) { diagForIncompleteOverrideOrImplement(); return; } } } #endif void StructInheritanceChecker::DiagnoseForInheritedInterfaces( const MemberSignature& interface, const MemberMap& implDecls) const { auto identifier = interface.decl->identifier; auto foundMembers = implDecls.equal_range(identifier); for (auto it = foundMembers.first; it != foundMembers.second; ++it) { auto child = it->second; CheckSameNameInheritanceInfo(interface, *child.decl); if (child.decl->astKind != interface.decl->astKind) { continue; } if (interface.decl->astKind == ASTKind::PROP_DECL) { CheckInheritanceForInterface(interface, child); CheckPropertyInheritance(interface, *child.decl); } else if (interface.decl->IsFunc()) { if (CheckImplementationRelation(interface, child)) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND CheckIncompleteOverrideOrImplOfExtend(interface, child); #endif CheckInheritanceForInterface(interface, child); CheckMutModifierCompatible(interface, *child.decl); } } CheckNativeFFI(interface, child); } if (interface.decl->TestAttr(Attribute::DEFAULT)) { interface.decl->EnableAttr(Attribute::INTERFACE_IMPL); } } void StructInheritanceChecker::DiagnoseForUnimplementedInterfaces(const MemberMap& members, const Decl& structDecl) { // Do not check unimplemented function for: // 1. Foreign struct. // 2. Mirror struct. if (structDecl.TestAttr(Attribute::FOREIGN) || structDecl.TestAnyAttr(Attribute::OBJ_C_MIRROR)) { return; } std::string prefix = structDecl.astKind == ASTKind::EXTEND_DECL ? "extend " : ""; std::string structName = prefix + (structDecl.ty->IsNominal() ? structDecl.ty->name : structDecl.ty->String()); std::multimap, const MemberSignature*> unImplementedDecl; std::vector> unImplementedMembers; for (auto& [identifier, member] : members) { bool notInheritable = member.decl->astKind != ASTKind::PROP_DECL && !member.decl->IsFunc(); bool ignored = notInheritable || member.extendDecl || opts.compileCjd; if (ignored) { continue; // Do not report error for not inheritable member or member in other extends. } if (IsBuiltInOperatorFuncInExtend(member, structDecl)) { continue; } // common inherit decl may not impletement all interface members. if (structDecl.TestAttr(Attribute::COMMON) && member.isInheritedInterface) { // impletement but no body if (IsCommonWithoutDefault(*member.decl) && member.decl->outerDecl == &structDecl) { continue; } } // Unimplemented decls can be ignored if: // 1. decl is defined in foreign struct; // 2. abstract decl in abstract class && current is extend decl. // 3. abstract decl in interface inherited by extended type decl. bool inForeignType = member.decl->outerDecl->TestAttr(Attribute::FOREIGN); bool extendInheritedAbstract = member.decl->outerDecl->TestAttr(Attribute::ABSTRACT) && member.decl->TestAttr(Attribute::ABSTRACT) && structDecl.astKind == ASTKind::EXTEND_DECL; bool extendAbsInheritedInterface = structDecl.astKind == ASTKind::EXTEND_DECL && member.decl->TestAttr(Attribute::ABSTRACT) && !member.isInheritedInterface; if (inForeignType || extendInheritedAbstract || extendAbsInheritedInterface) { continue; } std::string type = DeclKindToString(*member.decl); bool isAbstractClass = structDecl.TestAttr(Attribute::ABSTRACT) && structDecl.astKind == ASTKind::CLASS_DECL; bool isAbstractDecl = isAbstractClass || structDecl.astKind == ASTKind::INTERFACE_DECL; bool isStaticAbsMember = member.decl->TestAttr(Attribute::STATIC, Attribute::ABSTRACT); if (member.decl->TestAttr(Attribute::ABSTRACT) && !isAbstractDecl) { unImplementedDecl.emplace(member.decl, &member); unImplementedMembers.emplace_back(&member); } else if (member.shouldBeImplemented) { diag.Diagnose( structDecl, DiagKind::sema_interface_member_must_be_implemented, type, identifier, structName); } else if (member.decl->outerDecl != &structDecl) { if (isStaticAbsMember && isAbstractClass) { auto structNameRange = MakeRange(structDecl.identifier); (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_inherit_abstract_class_static_unimplement_func, structDecl, structNameRange, structName, type, identifier); } else if (!member.inconsistentTypes.empty()) { // When inherit functions from interfaces which has inconsistent types, we need report error here. DiagnoseInheritedInsconsistType(member, structDecl); } } } if (!unImplementedMembers.empty()) { DiagKindRefactor kind; if (structDecl.IsClassLikeDecl()) { kind = DiagKindRefactor::sema_class_need_abstract_modifier_or_func_need_impl; } else { kind = DiagKindRefactor::sema_need_member_implementation; } DiagnosticBuilder builder = diag.DiagnoseRefactor(kind, structDecl.begin, structName); std::stable_sort(unImplementedMembers.begin(), unImplementedMembers.end(), CompMemberSignatureByPosAndTy); for (auto member : unImplementedMembers) { std::string identifierName; if (unImplementedDecl.count(member->decl) > 1) { identifierName = member->decl->identifier.Val() + ", of type: " + member->ty->String(); } else { identifierName = member->decl->identifier.Val(); } std::string abstractType = member->decl->outerDecl->astKind == ASTKind::CLASS_DECL ? "abstract" : "interface"; std::string note = "unimplemented " + abstractType + " " + DeclKindToString(*member->decl) + " " + identifierName; builder.AddNote(member->decl->GetBegin(), note); } } } /** * Check if child overrides the extended parent with default implementation in interface extension. */ bool StructInheritanceChecker::IsExtendedDefaultImpl(const MemberSignature& parent, const Decl& child) const { if (Utils::NotIn(parent.decl->astKind, {ASTKind::FUNC_DECL, ASTKind::PROP_DECL}) || !parent.decl->TestAttr(Attribute::DEFAULT) || parent.decl->outerDecl->astKind != ASTKind::INTERFACE_DECL || child.outerDecl->astKind == ASTKind::EXTEND_DECL) { return false; } return typeManager.HasExtensionRelation(*child.outerDecl->ty, *parent.decl->outerDecl->ty); } /** * Check if: * 1. member in extend is not shadow parent members, * 2. member in class is not shadow extend members of parent decl. * return true if not shadow, return false if shadowed. */ bool StructInheritanceChecker::CheckExtendMemberValid(const MemberSignature& parent, const Decl& child) const { auto childRange = child.identifier.Empty() ? MakeRange(child.begin, child.end) : MakeRange(child.identifier); if (parent.extendDecl) { // When parent is default implement and has been copyed by other extend, or parent is defined in other extend, // it should be check shadow between parent and child which is in extend too. if (!parent.decl->TestAnyAttr(Attribute::DEFAULT, Attribute::IN_EXTEND) && child.TestAttr(Attribute::IN_EXTEND)) { return true; } auto structTy = child.outerDecl->ty; std::string typeName = "extend " + (structTy->IsNominal() ? structTy->name : structTy->String()); diag.DiagnoseRefactor( DiagKindRefactor::sema_extend_member_cannot_shadow, child, childRange, child.identifier, typeName); } else if (auto isExtended = IsExtendedDefaultImpl(parent, child); isExtended || parent.decl->outerDecl->astKind == ASTKind::EXTEND_DECL) { std::string type = DeclKindToString(*parent.decl); auto diagnose = diag.DiagnoseRefactor( DiagKindRefactor::sema_extend_function_cannot_overridden, child, childRange, type, child.identifier); if (!isExtended) { auto parentRange = parent.decl->identifier.Empty() ? MakeRange(parent.decl->begin, parent.decl->end) : MakeRange(parent.decl->identifier); diagnose.AddNote(*parent.decl, parentRange, "overridden definition of '" + child.identifier + "' is here"); } } else if (child.outerDecl->astKind == ASTKind::EXTEND_DECL) { auto parentRange = parent.decl->identifier.Empty() ? MakeRange(parent.decl->begin, parent.decl->end) : MakeRange(parent.decl->identifier); auto ed = RawStaticCast(child.outerDecl); auto diagnose = diag.DiagnoseRefactor( DiagKindRefactor::sema_extend_member_cannot_shadow, child, childRange, child.identifier, ed->ty->String()); diagnose.AddNote(*parent.decl, parentRange, "shadowed definition of '" + child.identifier + "' is here"); } else { return true; } return false; } /** * @p parent and @p child are considered as inherited members in structure declaration not interfaces' members. * Report errors when: * 1. parent and child have different 'static' status. * 2. child conflict with parent when two have different node kind. * 3. child shadows parent when two have same node kind, but parent is not opened. */ void StructInheritanceChecker::CheckSameNameInheritanceInfo(const MemberSignature& parent, const Decl& child) const { std::string name = child.identifier; auto parentDecl = parent.decl; std::string childStatic = child.TestAttr(Attribute::STATIC) ? "static" : "non-static"; std::string parentStatic = parentDecl->TestAttr(Attribute::STATIC) ? "static" : "non-static"; std::string inheritance = checkingDecls[0]->astKind == ASTKind::EXTEND_DECL ? "extended type" : "parent class or interfaces"; if (child.TestAttr(Attribute::STATIC) != parentDecl->TestAttr(Attribute::STATIC)) { diag.Diagnose(child, DiagKind::sema_static_and_non_static_member_cannot_have_same_name, childStatic, name, parentStatic, inheritance); return; } if (child.astKind != parentDecl->astKind) { if (parent.extendDecl || parent.isInheritedInterface || CheckExtendMemberValid(parent, child)) { auto build = diag.DiagnoseRefactor(DiagKindRefactor::sema_inherit_member_kind_inconsistent, child, MakeRange(child.identifier), DeclKindToString(child), name, DeclKindToString(*parentDecl), inheritance); build.AddNote(*parentDecl, MakeRange(parentDecl->identifier), "found conflict here"); } } else if (parentDecl->astKind == ASTKind::VAR_DECL) { diag.Diagnose(child, DiagKind::sema_member_variable_can_not_shadow, name); } } void StructInheritanceChecker::CheckInheritanceAttributes(const MemberSignature& parent, const Decl& child) const { auto parentDecl = parent.decl; if (child.astKind != parentDecl->astKind || (parentDecl->astKind != ASTKind::PROP_DECL && !parentDecl->IsFunc())) { return; } std::string type = DeclKindToString(*parentDecl); std::string name = child.identifier; if (!CheckExtendMemberValid(parent, child) || checkingDecls[0]->astKind == ASTKind::EXTEND_DECL) { return; } if (!parentDecl->TestAttr(Attribute::STATIC) && child.TestAttr(Attribute::REDEF)) { diag.Diagnose(child, DiagKind::sema_func_no_override_or_redefine_modifier, "redef", type, name); } else if (!parentDecl->TestAttr(Attribute::ABSTRACT) && !parentDecl->TestAttr(Attribute::STATIC)) { bool notInInterface = parentDecl->outerDecl->astKind != ASTKind::INTERFACE_DECL; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if ((!parentDecl->TestAttr(Attribute::OPEN) && notInInterface) || TestManager::IsDeclOpenToMock(*parentDecl)) { DiagCannotOverride(diag, child, *parentDecl); #endif } else if (child.TestAttr(Attribute::ABSTRACT) && notInInterface) { diag.Diagnose(child, DiagKind::sema_invalid_override_member_in_class, type, child.identifier.Val(), type); } } } void StructInheritanceChecker::CheckInheritanceForInterface( const MemberSignature& interface, const MemberSignature& child) const { auto interfaceMember = interface.decl; auto childDecl = child.decl; if (childDecl->astKind != interfaceMember->astKind || (interfaceMember->astKind != ASTKind::PROP_DECL && !interfaceMember->IsFunc())) { return; } std::string type = DeclKindToString(*interfaceMember); if (childDecl->TestAttr(Attribute::ABSTRACT) && !interfaceMember->TestAttr(Attribute::ABSTRACT)) { diag.Diagnose(*childDecl, DiagKind::sema_invalid_override_or_redefine_member_in_interface, type, childDecl->identifier.Val(), type); } // Mark child member implment abstract function of inherted interface. Used to determine whether child needs to // be exported. childDecl->EnableAttr(Attribute::INTERFACE_IMPL); auto setInterfaceImplAttr = [](const OwnedPtr& fd) { fd->EnableAttr(Attribute::INTERFACE_IMPL); }; if (auto pd = DynamicCast(childDecl)) { std::for_each(pd->getters.begin(), pd->getters.end(), setInterfaceImplAttr); std::for_each(pd->setters.begin(), pd->setters.end(), setInterfaceImplAttr); } } /** * Report error when no inherited which has same identifier existing for current member. */ void StructInheritanceChecker::DiagnoseForOverriddenMember(const MemberSignature& child) const { if (child.replaceOther) { return; } auto childDecl = child.decl; std::string type = DeclKindToString(*childDecl); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (childDecl->TestAttr(Attribute::OVERRIDE)) { #endif diag.Diagnose(*childDecl, DiagKind::sema_missing_overridden_func, type, childDecl->identifier.Val(), type); } else if (childDecl->TestAttr(Attribute::REDEF) && childDecl->TestAttr(Attribute::STATIC)) { diag.Diagnose(*childDecl, DiagKind::sema_missing_redefined_func, type, childDecl->identifier.Val(), type); } } void StructInheritanceChecker::CheckAccessVisibility(const Decl& parent, const Decl& child, const Decl& diagNode) const { CJC_NULLPTR_CHECK(parent.outerDecl); // Do not report error if the `parent` cannot be implemented. bool canBeImplemented = parent.TestAnyAttr(Attribute::ABSTRACT, Attribute::OPEN, Attribute::STATIC) || parent.outerDecl->astKind == ASTKind::INTERFACE_DECL; if (!canBeImplemented) { return; } // |--------------|--------|-----------|---------|---------| // | parent\child | public | protected | default | private | // |--------------|--------|-----------|---------|---------| // | public | ok | case 1 | case 1 | case 1 | // | protected | ok | ok | case 2 | case 2 | // | default | ok | ok | ok | case 3 | // | private | ok | ok | ok | ok | // |--------------|--------|-----------|---------|---------| bool weakVisibility = // case 1 (parent.TestAttr(Attribute::PUBLIC) && !child.TestAttr(Attribute::PUBLIC)) || // case 2 (parent.TestAttr(Attribute::PROTECTED) && !child.TestAnyAttr(Attribute::PUBLIC, Attribute::PROTECTED)) || // case 3 (!parent.TestAnyAttr(Attribute::PUBLIC, Attribute::PROTECTED, Attribute::PRIVATE) && child.TestAttr(Attribute::PRIVATE)); if (weakVisibility) { DiagWeakVisibility(diag, parent, child, diagNode); } } void StructInheritanceChecker::CheckGenericTypeArgInfo( const MemberSignature& parent, const MemberSignature& child) const { auto parentGeneric = parent.decl->GetGeneric(); auto childGeneric = child.decl->GetGeneric(); bool diffStatus = parent.decl->TestAttr(Attribute::STATIC) != child.decl->TestAttr(Attribute::STATIC); if (!parentGeneric || !childGeneric || diffStatus) { return; } if (parentGeneric->typeParameters.size() != childGeneric->typeParameters.size()) { diag.Diagnose(*child.decl, DiagKind::sema_generic_member_type_argument_different, child.decl->identifier.Val()); return; } TypeSubst typeMapping = typeManager.GenerateGenericMappingFromGeneric(*parent.decl, *child.decl); CJC_ASSERT(parent.upperBounds.size() == child.upperBounds.size()); // Child's constraint should be looser or same with the parent's constraint; for (size_t i = 0; i < parent.upperBounds.size(); ++i) { auto childUppers = child.upperBounds[i]; (void)childUppers.erase(typeManager.GetAnyTy()); // Remove upper bound of type 'Any'. if (childUppers.empty()) { continue; // Empty upperBounds is always looser. } auto& parentUppers = parent.upperBounds[i]; std::unordered_set> instUppers; for (auto it : parentUppers) { instUppers.emplace(typeManager.GetInstantiatedTy(it, typeMapping)); } bool childLooser = true; for (auto upper : childUppers) { // At least one parent upperBounds is the subtype of child upperBound. childLooser = std::any_of(instUppers.begin(), instUppers.end(), [this, &upper](auto it) { return typeManager.IsSubtype(it, upper); }); if (!childLooser) { break; } } for (auto upper : instUppers) { if (!childLooser) { break; } // Cannot exist any child upperBound that is the subtype of parent upperBound. childLooser = !std::any_of(childUppers.begin(), childUppers.end(), [this, &upper](auto it) { return !typeManager.IsTyEqual(it, upper) && typeManager.IsSubtype(it, upper); }); } if (!childLooser) { auto parentConstraints = instUppers.empty() ? std::string("empty") : "'" + Ty::GetTypesToStableStr(instUppers, "', '") + "'"; auto gc = GetConstraintNodeByIndex(i, *childGeneric); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_generic_constraint_not_looser, *gc); builder.AddNote("parent constraint is " + parentConstraints); } } } void StructInheritanceChecker::CheckPropertyInheritance(const MemberSignature& parent, const Decl& child) const { auto parentDecl = parent.decl; // Caller guarantees parent and child have same astKind. if (parentDecl->astKind != ASTKind::PROP_DECL) { return; } if (parent.ty != child.ty || !parent.inconsistentTypes.empty()) { diag.Diagnose(child, DiagKind::sema_property_override_implement_type_diff); } auto parentProp = RawStaticCast(parentDecl); auto childProp = RawStaticCast(&child); if (childProp->isVar != parentProp->isVar) { if (parentProp->isVar) { diag.DiagnoseRefactor(DiagKindRefactor::sema_property_have_same_declaration_in_inherit_mut, *childProp, childProp->identifier.Val()); } else { diag.DiagnoseRefactor(DiagKindRefactor::sema_property_have_same_declaration_in_inherit_immut, *childProp, childProp->identifier.Val()); } } else if (childProp->isVar && (childProp->getters.empty() || childProp->setters.empty()) && parentProp->TestAnyAttr(Attribute::DEFAULT, Attribute::ABSTRACT)) { // Currently, implemenation of default interface property must implement both getter/setter. diag.DiagnoseRefactor( DiagKindRefactor::sema_property_must_implement_both, *childProp, childProp->identifier.Val()); } CheckAccessVisibility(*parentDecl, child, child); } bool StructInheritanceChecker::CheckReturnOverrideByGeneric(const FuncTy& parentTy, const FuncTy& childTy) const { if (!Ty::IsTyCorrect(childTy.retTy) || !Ty::IsTyCorrect(parentTy.retTy)) { return true; } if (!childTy.retTy->IsGeneric() || parentTy.retTy == childTy.retTy) { return true; } // 'func foo(): I1' cannot be overridden by a function 'func foo(): T' that return generic type 'T' and 'T''s // upperbound is empty or all upperbounds are interface. auto genericTy = StaticCast(childTy.retTy); if (genericTy->upperBounds.empty()) { return false; } for (auto up : genericTy->upperBounds) { if (!up->IsInterface() && !up->IsGeneric()) { return true; } } return false; } bool StructInheritanceChecker::CheckImplementationRelation( const MemberSignature& parent, const MemberSignature& child) const { auto parentDecl = parent.decl; // Caller guarantees parent and child have same astKind. if (!parentDecl->IsFunc()) { return false; } auto parentTy = parent.ty; auto parentFunc = RawStaticCast(parent.decl); auto childFunc = RawStaticCast(child.decl); if (childFunc->TestAttr(Attribute::GENERIC) && parentDecl->TestAttr(Attribute::GENERIC)) { TypeSubst typeMapping = typeManager.GenerateGenericMappingFromGeneric(*parentFunc, *childFunc); parentTy = typeManager.GetInstantiatedTy(parentTy, typeMapping); } auto parentFuncTy = DynamicCast(parentTy); auto childFuncTy = DynamicCast(child.ty); bool sameSignature = parentFuncTy && childFuncTy && typeManager.IsFuncParameterTypesIdentical(*parentFuncTy, *childFuncTy); if (sameSignature) { CJC_ASSERT(parentFunc->funcBody && childFunc->funcBody); bool isParamListCorrect = !parentFunc->funcBody->paramLists.empty() && !childFunc->funcBody->paramLists.empty(); bool inheritedByExtension = childFuncTy->retTy && parentFuncTy->retTy && typeManager.HasExtensionRelation(*childFuncTy->retTy, *parentFuncTy->retTy); if (isParamListCorrect && !AreFuncParameterNameIdentical(*parentFunc, *childFunc)) { DiagnoseParameterName(diag, *parentFunc, *childFunc); } else if (!CheckThisTypeCompatibility(*parentFunc, *childFunc)) { auto diagBuilder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inherit_not_return_this, MakeRange(childFunc->identifier)); diagBuilder.AddNote(MakeRange(parentFunc->identifier), "the overriden function"); } else if (AreReturnTypesCompatible(*parentFuncTy, *childFuncTy, parent.inconsistentTypes)) { const Decl& diagNode = childFunc->outerDecl == checkingDecls.front() ? *childFunc : *checkingDecls.front(); CheckAccessVisibility(*parentFunc, *childFunc, diagNode); CheckGenericTypeArgInfo(parent, child); } else if (inheritedByExtension || !CheckReturnOverrideByGeneric(*parentFuncTy, *childFuncTy)) { // A type that does not meet the type variance relationship cannot be used as the basis for subtypes when // override occurs. auto builder = diag.Diagnose(*childFunc, DiagKind::sema_return_type_invariance, childFunc->identifier.Val(), Ty::ToString(parentFuncTy->retTy)); builder.AddNote(*parentFunc, "cannot override/implement the following function"); // If `diagDecl == nullptr`, both child and parent are imported which is orphan extension. // And error should have been reported. } else { Ptr targetFuncDecl = parentFunc; std::string inconsistentTypesForDiag; if (!parent.inconsistentTypes.empty()) { inconsistentTypesForDiag = StringifyInconsistentTypes(parent.inconsistentTypes, childFuncTy->retTy); targetFuncDecl = childFunc; } else { inconsistentTypesForDiag = "'" + parentFuncTy->retTy->String() + "'"; } auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_return_type_incompatible, MakeRangeForDeclIdentifier(*childFunc), childFunc->identifier.Val()); builder.AddNote(MakeRangeForDeclIdentifier(*targetFuncDecl), "'" + childFuncTy->retTy->String() + "' is not a subtype of " + inconsistentTypesForDiag); } if (!childFunc->IsConst() && parentDecl->IsConst()) { std::string inheritance = checkingDecls[0]->astKind == ASTKind::EXTEND_DECL ? "extended type" : "parent class or interfaces"; auto build = diag.DiagnoseRefactor(DiagKindRefactor::sema_inherit_member_kind_inconsistent, *childFunc, MakeRange(childFunc->identifier), "non-constant function", childFunc->identifier.Val(), "'const' function", inheritance); build.AddNote(*parentDecl, MakeRange(parentDecl->identifier), "found 'const' function here"); } } return sameSignature; } // Struct/extend types must maintain the same mut modifier when implementing functions of the interface void StructInheritanceChecker::CheckMutModifierCompatible(const MemberSignature& parent, const Decl& child) const { auto parentDecl = parent.decl; // Caller guarantees parent and child have same astKind. if (!parentDecl->IsFunc()) { return; } if (parentDecl->outerDecl->astKind != ASTKind::INTERFACE_DECL) { return; } // Only function in structure declaration or extend of struct needs to checking 'mut' attribute. if (auto ed = DynamicCast(child.outerDecl); ed) { if (!ed->ty->IsStruct()) { return; } } else if (child.outerDecl->astKind != ASTKind::STRUCT_DECL) { return; } if (parentDecl->TestAttr(Attribute::MUT) != child.TestAttr(Attribute::MUT)) { auto& decl = child.outerDecl == checkingDecls[0] ? child : *checkingDecls[0]; diag.Diagnose(decl, DiagKind::sema_incompatible_mut_modifier_between_struct_and_interface, child.identifier.Val(), parentDecl->outerDecl->identifier.Val()); } } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::vector> StructInheritanceChecker::GetAllNeedCheckExtended() { std::vector> needCheckExtendDecls = {}; // If the extend decls are all imported from same package, do not check their inheritance again. auto filter = [&needCheckExtendDecls](const std::set> extends) -> void { std::unordered_set pkgNames; if (std::all_of(extends.cbegin(), extends.cend(), [&pkgNames](auto it) { pkgNames.emplace(it->fullPackageName); return it->TestAttr(Attribute::IMPORTED) && pkgNames.size() == 1; })) { return; } std::set, CmpNodeByPos> sortedExtends; std::copy_if(extends.begin(), extends.end(), std::inserter(sortedExtends, sortedExtends.end()), [](auto it) { return it->TestAttr(Attribute::PUBLIC); }); needCheckExtendDecls.insert(needCheckExtendDecls.end(), sortedExtends.cbegin(), sortedExtends.cend()); }; // Collect all imported extend decls. Used for checking confliction between imported extends. auto extendedDecls = typeManager.GetAllExtendedDecls(); // Use ordered set to diagnose in consistent order. std::set, CmpNodeByPos> sorted(extendedDecls.cbegin(), extendedDecls.cend()); for (auto extendedDecl : sorted) { if (extendedDecl->TestAttr(Attribute::IMPORTED, Attribute::PUBLIC)) { auto extends = typeManager.GetDeclExtends(*extendedDecl); filter(extends); } } // Collect all extended builtIn. Used for checking confliction between imported extends. auto extendedBuiltInTys = typeManager.GetAllExtendedBuiltIn(); // Use ordered set to diagnose in consistent order. std::set, CmpTyByName> sortedBuiltInTys(extendedBuiltInTys.cbegin(), extendedBuiltInTys.cend()); for (auto extendedBuiltInTy : sortedBuiltInTys) { auto extends = typeManager.GetBuiltinTyExtends(*extendedBuiltInTy); filter(extends); } return needCheckExtendDecls; } #endif void StructInheritanceChecker::CheckNativeFFI( [[maybe_unused]] const MemberSignature& parent, [[maybe_unused]] const MemberSignature& child) const { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (checkingDecls.size() > 0 && checkingDecls.back()) { Interop::Java::CheckForeignName(diag, typeManager, parent, child, *checkingDecls.back()); } #endif } cangjie_compiler-1.0.7/src/Sema/InheritanceChecker/StructInheritanceChecker.h000066400000000000000000000247231510705540100273050ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares class for inheritance checking of structure declarations. */ #ifndef CANGJIE_SEMA_INHERITANCE_CHECKER_H #define CANGJIE_SEMA_INHERITANCE_CHECKER_H #include "cangjie/AST/Node.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Modules/ImportManager.h" namespace Cangjie { using namespace AST; struct MemberSignature { Ptr decl = nullptr; Ptr ty = nullptr; Ptr structTy = nullptr; Ptr extendDecl = nullptr; // If the member is a member of another visible extension, it points to the // extension declaration. Otherwise, it is null. std::vector>> upperBounds; std::unordered_set> inconsistentTypes; // List of the corresponding types came from super-types which // are inconsistent bool shouldBeImplemented = false; // True: if this member has multiple default implementation. bool replaceOther = false; // True: if this member override others. bool isInheritedInterface = false; // True: if current member is implementing inherited interface decl. }; using MemberMap = std::multimap; class StructInheritanceChecker { public: StructInheritanceChecker(DiagnosticEngine& diag, TypeManager& manger, Package& pkg, ImportManager& importManager, const GlobalOptions& options) : diag(diag), typeManager(manger), pkg(pkg), importManager(importManager), opts{options} { } ~StructInheritanceChecker() = default; /** * Collect and check decls declared inside given pkg node tree when node is not imported. */ void Check(); private: void CheckMembersWithInheritedDecls(const InheritableDecl& decl); MemberMap GetAndCheckInheritedInterfaces(const InheritableDecl& decl); MemberMap GetInheritedSuperMembers( const InheritableDecl& decl, Ty& baseTy, const AST::File& curFile, bool ignoreExtends = false); MemberMap GetAndCheckInheritedMembers(const InheritableDecl& decl); void CollectExtendByInterfaceInherit(const std::set>& otherExtends, const ExtendDecl& curDecl, std::set, CmpNodeByPos>& ordered); std::optional DeterminingSkipExtendByInheritanceRelationship( const AST::ExtendDecl& curDecl, const AST::ExtendDecl& ed, const Ptr& extendedDecl); std::pair GetVisibleExtendMembersForExtend(const InheritableDecl& decl); void CheckExtendExportDependence( const InheritableDecl& curExtend, const MemberSignature& interface, const MemberMap& implDecl); void UpdateOverriddenFuncDeclCache(Ptr child, Ptr parent); MemberSignature UpdateInheritedMemberIfNeeded( MemberMap& inheritedMembers, const MemberSignature& child, bool inheritedInterfaces = false); bool ComputeInconsistentTypes(const MemberSignature& child, const MemberSignature& parent, MemberSignature& updated, const std::pair& status, std::vector>& inconsistentTypes) const; void MergeInheritedMembers( MemberMap& members, const MemberMap& otherMembers, Ty& structTy, bool inheritedInterfaces = false); void DiagnoseForOverriddenMember(const MemberSignature& child) const; void DiagnoseForInheritedMember(const MemberSignature& parent, const MemberSignature& child) const; void DiagnoseForInheritedInterfaces(const MemberSignature& interface, const MemberMap& implDecls) const; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void CheckIncompleteOverrideOrImplOfExtend(const MemberSignature& interface, const MemberSignature& child) const; #endif void DiagnoseForUnimplementedInterfaces(const MemberMap& members, const Decl& structDecl); void DiagnoseForConflictInheritance(const InheritableDecl& decl, const MemberMap& members) const; void DiagnoseInheritedInsconsistType(const MemberSignature& member, const Node& node) const; void CheckSameNameInheritanceInfo(const MemberSignature& parent, const Decl& child) const; void CheckInheritanceAttributes(const MemberSignature& parent, const Decl& child) const; void CheckPropertyInheritance(const MemberSignature& parent, const Decl& child) const; void CheckGenericTypeArgInfo(const MemberSignature& parent, const MemberSignature& child) const; void CheckInheritanceForInterface(const MemberSignature& interface, const MemberSignature& child) const; bool CheckImplementationRelation(const MemberSignature& parent, const MemberSignature& child) const; void CheckMutModifierCompatible(const MemberSignature& parent, const Decl& child) const; void CheckAccessVisibility(const Decl& parent, const Decl& child, const Decl& diagNode) const; bool IsExtendedDefaultImpl(const MemberSignature& parent, const Decl& child) const; bool CheckExtendMemberValid(const MemberSignature& parent, const Decl& child) const; bool IsBuiltInOperatorFuncInExtend(const MemberSignature& member, const Decl& structDecl) const; bool CheckReturnOverrideByGeneric(const FuncTy& parentTy, const FuncTy& childTy) const; bool AreReturnTypesCompatible(const FuncTy& parentTy, const FuncTy& childTy, const std::unordered_set> inconsistentTypes = {}) const { bool isSubOfAll = std::all_of(inconsistentTypes.begin(), inconsistentTypes.end(), [&childTy, this](auto ty) { return typeManager.IsSubtype(childTy.retTy, const_cast(ty.get()), false); }); if (!CheckReturnOverrideByGeneric(parentTy, childTy)) { return false; } // Return types are the same or return types have subtype relations. return isSubOfAll && typeManager.IsSubtype(childTy.retTy, parentTy.retTy, false); } void CheckAllUpperBoundsConfliction(); void CheckUpperBoundsConfliction(const Generic& generic); /** Checks related to NativeFFI */ void CheckNativeFFI(const MemberSignature& parent, const MemberSignature& child) const; /** * Generates the built-in operator function and adds to ed.members. * e.g. operator func -(): Int64 { return -this } */ void CreateBuiltInUnaryOperatorFunc(TokenKind op, ExtendDecl& ed) const; /** * Generates the built-in operator function and adds to ed.members. * e.g. operator func +(right: Int64): Int64 { return this + right } */ void CreateBuiltInBinaryOperatorFunc(TokenKind op, Ptr rightTy, ExtendDecl& ed, TypeKind returnTyKind) const; /** * Walk source package and checking inside instantiated decls to find whether * there conflict members existing in instantiated nominal decls. */ void CheckInstDupFuncsInNominalDecls(); VisitAction CheckInstDupFuncsRecursively(Node& node); void CheckInstMemberSignatures(const InheritableDecl& decl, const std::vector>& instTys); void CheckInstantiatedDecl(Decl& decl, const std::vector>& instTys); /** * Get visible extend decls in stable order for given @p decl with @p instTys . */ std::set, CmpNodeByPos> GetVisibleExtendsForInstantiation( const Decl& decl, const std::vector>& instTys); /** * Check whether the given decl instantiation info may cause infinite instantiation. */ bool WillCauseInfiniteInstantiation(const Node& triggerNode, const Decl& decl, const std::vector>& instTys); void DiagnoseForInstantiatedMember(const MemberSignature& parent, const MemberSignature& child) const; /** CStruct checking methods. */ void CheckInstWithCStructTypeArg(const Node& node); void CheckCStruct(const Ty& ty, const Type& typeArg); void CheckCStructArguments(const Node& node, const AST::Ty& ty, const Position& leftAnglePos, const std::vector>& typeArgs); void CheckCStructArgument(const Ty& ty, const Type& typeArg); bool IsExtendVisibleInCurpkg(const ExtendDecl& ed) { return !pkg.files.empty() && *pkg.files.begin() && importManager.IsExtendAccessible(**pkg.files.begin(), ed); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::vector> GetAllNeedCheckExtended(); #endif DiagnosticEngine& diag; TypeManager& typeManager; Package& pkg; ImportManager& importManager; // Used to check whether the extend declaration is imported. const GlobalOptions& opts; std::unordered_map, MemberMap> structInheritedMembers; std::vector> checkingDecls; /** Following members are used to cache instantiation checking status. */ std::set, const std::vector>>> instantiatedDecls; std::map, const std::vector>>, std::unordered_map, Ptr>> instantiatedTyCache; /** * Used to cache instantiation checking status which need to report diagnoses. * key: generic decl with instantiated type, value: pair of member signatures to report diagnose. */ std::map, const std::vector>>, std::vector>> genericMembersForInstantiatedDecl; std::vector, Ptr, const std::vector>>> instTriggerInfos; std::stack institutionMaps; bool infiniteInstantiationOccured{false}; /** * Used for store & re-store context info for checking declaration status in instantiated status. */ class InstantiatedContext { public: InstantiatedContext(StructInheritanceChecker& checker, Ptr node, Ptr target, const std::vector>& instTys, bool updateTrigger) : checker(checker), needUpdate(updateTrigger) { if (needUpdate) { checker.instTriggerInfos.emplace_back(std::make_tuple(node, target, instTys)); } } ~InstantiatedContext() { if (needUpdate) { checker.instTriggerInfos.pop_back(); } } private: StructInheritanceChecker& checker; bool needUpdate; }; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Sema/JoinAndMeet.cpp000066400000000000000000000337041510705540100213330ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements utility functions for JoinAndMeet. */ #include "JoinAndMeet.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Types.h" #include "cangjie/Utils/CheckUtils.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; using ErrMsg = std::stack; using ErrOrTy = std::variant>; namespace { enum class Uniformity { UNIFORMED, MIXED, ALL_IRRELEVANT }; Uniformity CheckFuncUniformity(const std::set>& tys) { size_t paramCnt = 0; bool anyFuncTy = false; bool anyNonFuncTy = false; for (auto& ty : tys) { if (auto funcTy = DynamicCast(ty)) { size_t curCnt = funcTy->paramTys.size(); if (!anyFuncTy) { paramCnt = curCnt; } else if (paramCnt != curCnt) { return Uniformity::MIXED; } anyFuncTy = true; } else { anyNonFuncTy = true; } if (anyFuncTy && anyNonFuncTy) { return Uniformity::MIXED; } } if (!anyFuncTy) { return Uniformity::ALL_IRRELEVANT; } return Uniformity::UNIFORMED; } Uniformity CheckTupleUniformity(const std::set>& tys) { size_t argCnt = 0; bool anyTupleTy = false; bool anyNonTupleTy = false; for (auto& ty : tys) { if (auto tupleTy = DynamicCast(ty)) { size_t curCnt = tupleTy->typeArgs.size(); if (!anyTupleTy) { argCnt = curCnt; } else if (argCnt != curCnt) { return Uniformity::MIXED; } anyTupleTy = true; } else { anyNonTupleTy = true; } if (anyTupleTy && anyNonTupleTy) { return Uniformity::MIXED; } } if (!anyTupleTy) { return Uniformity::ALL_IRRELEVANT; } return Uniformity::UNIFORMED; } } // namespace ErrOrTy JoinAndMeet::Join(bool sprsErr) { // Hot fix. if (!IsInputValid()) { return {TypeManager::GetInvalidTy()}; } auto jTy = BatchJoin(tySet); if (sprsErr || errMsg.empty()) { return {jTy}; } else { return {errMsg}; } } ErrOrTy JoinAndMeet::JoinAsVisibleTy() { // Hot fix. if (!IsInputValid()) { return {TypeManager::GetInvalidTy()}; } auto jTy = BatchJoin(tySet); this->isForcedToUserVisible = true; jTy = ToUserVisibleTy(jTy); AddFinalErrMsgs(*jTy, true); if (errMsg.empty()) { return {jTy}; } else { return {errMsg}; } } Ptr JoinAndMeet::BatchJoin(const std::set>& tys) { auto isSubtype = [this](Ptr ty1, Ptr ty2) { return tyMgr.IsSubtype(ty1, ty2); }; auto isSupertype = [this](Ptr ty1, Ptr ty2) { return tyMgr.IsSubtype(ty2, ty1); }; auto doBatchJoin = [this](const std::set>& tys) {return BatchJoin(tys);}; auto doBatchMeet = [this](const std::set>& tys) {return BatchMeet(tys);}; DualMode joinMode = {.bound = tyMgr.GetAnyTy(), .coFunc = doBatchJoin, .contraFunc = doBatchMeet, .coSubtyFunc = isSubtype}; std::set> realTys; std::function)> insertRealTy = [this, &realTys, &insertRealTy](Ptr ty) { if (auto tyVar = DynamicCast(ty); (tyVar && Utils::In(tyVar, ignoredTyVars)) || ty->IsNothing()) { return; } if (auto unionTy = DynamicCast(ty)) { for (auto uty : unionTy->tys) { insertRealTy(uty); } } else { realTys.insert(ty); } }; for (auto ty : tys) { insertRealTy(ty); } PData::CommitScope cs(tyMgr.constraints); if (auto ret = FindSmallestTy(realTys, isSupertype); ret && !ret->IsInvalid()) { return ret; } PData::Reset(tyMgr.constraints); if (auto funcTyJoin = JoinOrMeetFuncTy(joinMode, realTys)) { return funcTyJoin; } PData::Reset(tyMgr.constraints); if (auto tupleTyJoin = JoinOrMeetTupleTy(joinMode, realTys)) { return tupleTyJoin; } PData::Reset(tyMgr.constraints); auto common = tyMgr.GetAllCommonSuperTys(std::unordered_set>(realTys.begin(), realTys.end())); if (curFile) { Utils::EraseIf(common, [this](Ptr ty) { return !impMgr->IsTyAccessible(*curFile, *ty); }); } if (common.empty()) { return tyMgr.GetAnyTy(); } auto ret = FindSmallestTy(std::set>(common.begin(), common.end()), isSubtype); // reset unnecessary constaints from finding possible supertypes (e.g. those claimed by conditional extensions), // and re-enforce necessary constraints by judging the common supertype again PData::Reset(tyMgr.constraints); if (ret->IsInvalid() || !LessThanAll(ret, realTys, isSupertype)) { PData::Reset(tyMgr.constraints); } return ret; } /** * returns: * - the joined/met FuncTy if all tys are FuncTy and the LUB/GLB exists * - AnyTy/Nothing if there exists any FuncTy but the LUB/GLB doesn't exist * - nullptr if there is no FuncTy in tys */ Ptr JoinAndMeet::JoinOrMeetFuncTy(const DualMode& mode, const std::set>& tys) { auto uniformity = CheckFuncUniformity(tys); switch (uniformity) { case Uniformity::ALL_IRRELEVANT: return nullptr; case Uniformity::MIXED: return mode.bound; default: break; } size_t paramCnt = RawStaticCast(*tys.begin())->paramTys.size(); std::vector> paramTys(paramCnt); for (size_t i = 0; i < paramCnt; i++) { std::set> operandParamTys; for (auto ty : tys) { operandParamTys.insert(RawStaticCast(ty)->paramTys[i]); } paramTys[i] = mode.contraFunc(operandParamTys); } std::set> operandRetTys; for (auto ty : tys) { operandRetTys.insert(RawStaticCast(ty)->retTy); } auto retTy = mode.coFunc(operandRetTys); if (Ty::AreTysCorrect(paramTys) && Ty::IsTyCorrect(retTy)) { auto resultTy = tyMgr.GetFunctionTy(paramTys, retTy); CJC_NULLPTR_CHECK(resultTy); for (auto ty : tys) { if (!mode.coSubtyFunc(ty, resultTy)) { return mode.bound; } } return resultTy; } return mode.bound; } /** * returns: * - the joined/met TupleTy if all tys are TupleTy and the LUB/GLB exists * - AnyTy/Nothing if there exists any TupleTy but the LUB/GLB doesn't exist * - nullptr if there is no TupleTy in tys */ Ptr JoinAndMeet::JoinOrMeetTupleTy(const DualMode& mode, const std::set>& tys) { auto uniformity = CheckTupleUniformity(tys); switch (uniformity) { case Uniformity::ALL_IRRELEVANT: return nullptr; case Uniformity::MIXED: return mode.bound; default: break; } size_t argCnt = RawStaticCast(*tys.begin())->typeArgs.size(); std::vector> typeArgs(argCnt); for (size_t i = 0; i < argCnt; i++) { std::set> operandTyArgs; for (auto& ty : tys) { operandTyArgs.insert(RawStaticCast(ty)->typeArgs[i]); } typeArgs[i] = mode.coFunc(operandTyArgs); } if (Ty::AreTysCorrect(typeArgs)) { auto resultTy = tyMgr.GetTupleTy(typeArgs); for (auto ty : tys) { if (!mode.coSubtyFunc(ty, resultTy)) { return mode.bound; } } return resultTy; } return mode.bound; } ErrOrTy JoinAndMeet::Meet(bool sprsErr) { // Hot fix. if (!IsInputValid()) { return {TypeManager::GetInvalidTy()}; } auto mTy = BatchMeet(tySet); if (sprsErr || errMsg.empty()) { return {mTy}; } else { return {errMsg}; } } ErrOrTy JoinAndMeet::MeetAsVisibleTy() { // Hot fix. if (!IsInputValid()) { return {TypeManager::GetInvalidTy()}; } auto mTy = BatchMeet(tySet); this->isForcedToUserVisible = true; mTy = ToUserVisibleTy(mTy); AddFinalErrMsgs(*mTy, false); if (errMsg.empty()) { return {mTy}; } else { return {errMsg}; } } Ptr JoinAndMeet::BatchMeet(const std::set>& tys) { auto isSubtype = [this](Ptr ty1, Ptr ty2) { return tyMgr.IsSubtype(ty1, ty2); }; auto isSupertype = [this](Ptr ty1, Ptr ty2) { return tyMgr.IsSubtype(ty2, ty1); }; auto doBatchJoin = [this](const std::set>& tys) {return BatchJoin(tys);}; auto doBatchMeet = [this](const std::set>& tys) {return BatchMeet(tys);}; DualMode meetMode = {.bound = TypeManager::GetInvalidTy(), .coFunc = doBatchMeet, .contraFunc = doBatchJoin, .coSubtyFunc = isSupertype}; std::set> realTys; std::function)> insertRealTy = [this, &realTys, &insertRealTy](Ptr ty) { if (auto tyVar = DynamicCast(ty); tyVar && Utils::In(tyVar, ignoredTyVars)) { return; } if (auto unionTy = DynamicCast(ty)) { insertRealTy(BatchJoin(unionTy->tys)); } if (auto itsTy = DynamicCast(ty)) { for (auto ity : itsTy->tys) { insertRealTy(ity); } } else { realTys.insert(ty); } }; for (auto ty : tys) { insertRealTy(ty); } PData::CommitScope cs(tyMgr.constraints); if (auto ret = FindSmallestTy(realTys, isSubtype); ret && !ret->IsInvalid()) { return ret; } PData::Reset(tyMgr.constraints); if (auto funcTyJoin = JoinOrMeetFuncTy(meetMode, realTys)) { return funcTyJoin; } PData::Reset(tyMgr.constraints); if (auto tupleTyJoin = JoinOrMeetTupleTy(meetMode, realTys)) { return tupleTyJoin; } PData::Reset(tyMgr.constraints); return TypeManager::GetInvalidTy(); } std::string JoinAndMeet::CombineErrMsg(ErrMsg& msgs) { std::string res{}; res.append("Traces:\n"); while (!msgs.empty()) { res.append(msgs.top() + "\n"); msgs.pop(); } return res; } Ptr JoinAndMeet::ToUserVisibleTy(Ptr ty) { CJC_NULLPTR_CHECK(ty); if (ty->IsIntersection()) { auto iSecTy = RawStaticCast(ty); auto isSubtype = [this](Ptr ty1, Ptr ty2) { return tyMgr.IsSubtype(ty1, ty2); }; Ptr res = ToUserVisibleTy(FindSmallestTy(iSecTy->tys, isSubtype)); // Given C1 <: I1 & I2 and C2 <: I1 & I2, then Join(C1, C2) gives I1 & I2. // Meet(I1, I2) gives Nothing but the result for the original Join. return res->IsNothing() ? tyMgr.GetAnyTy() : res; } else if (auto unionTy = DynamicCast(ty)) { std::set> uTys; for (auto& t : unionTy->tys) { uTys.insert(ToUserVisibleTy(t)); } auto res = BatchJoin(uTys); // Dual of the above comments. return res->IsAny() ? TypeManager::GetNothingTy() : res; } else if (ty->IsFunc()) { auto funcTy = RawStaticCast(ty); auto retTy = ToUserVisibleTy(funcTy->retTy); auto paramTys = funcTy->paramTys; std::transform(funcTy->paramTys.begin(), funcTy->paramTys.end(), paramTys.begin(), [this](Ptr typ) { return ToUserVisibleTy(typ); }); if (Ty::AreTysCorrect(paramTys) && Ty::IsTyCorrect(retTy)) { return tyMgr.GetFunctionTy(paramTys, retTy, {funcTy->isC, funcTy->isClosureTy, funcTy->hasVariableLenArg}); } else { return TypeManager::GetInvalidTy(); } } else if (ty->IsTuple()) { auto tupleTy = RawStaticCast(ty); auto elemTys = tupleTy->typeArgs; std::transform(tupleTy->typeArgs.begin(), tupleTy->typeArgs.end(), elemTys.begin(), [this](Ptr typ) { return ToUserVisibleTy(typ); }); if (Ty::AreTysCorrect(elemTys)) { return tyMgr.GetTupleTy(elemTys); } else { return TypeManager::GetInvalidTy(); } } else { return ty; } } void JoinAndMeet::AddFinalErrMsgs(const Ty& ty, bool isJoin) { auto getTysStr = [this]() { auto tyVec = Utils::SetToVec>(tySet); std::sort(tyVec.begin(), tyVec.end(), CompTyByNames); std::string tysStr; for (auto it = tyVec.begin(); it != std::prev(tyVec.end()); ++it) { tysStr += (it == tyVec.begin() ? std::string() : ", ") + "'" + Ty::ToString(*it) + "'"; } if (tyVec.size() > 1) { CJC_NULLPTR_CHECK(tyVec.back()); tysStr += " and '" + tyVec.back()->String() + "'"; } return tysStr; }; if (ty.IsInvalid()) { CJC_ASSERT(!tySet.empty()); std::string newErrMsg = "The types " + getTysStr() + " do not have "; newErrMsg += isJoin ? "the smallest common supertype" : "the greatest common subtype"; errMsg.push(newErrMsg); return; } } std::optional JoinAndMeet::SetJoinedType( Ptr& ty, std::variant, Ptr>& joinRes) { if (std::get_if>(&joinRes)) { ty = std::get>(joinRes); return {}; } else { ty = TypeManager::GetInvalidTy(); auto errMsgs = std::get>(joinRes); return {JoinAndMeet::CombineErrMsg(errMsgs)}; } } std::optional JoinAndMeet::SetMetType(Ptr& ty, std::variant, Ptr>& metRes) { return SetJoinedType(ty, metRes); } bool JoinAndMeet::IsInputValid() const { bool isValid = true; if (tySet.empty()) { isValid = false; } if (!Ty::AreTysCorrect(tySet)) { isValid = false; } return isValid; } cangjie_compiler-1.0.7/src/Sema/JoinAndMeet.h000066400000000000000000000076671510705540100210110ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the class for calculating the smallest common supertype (or join, or least upper bound) and the * greatest common subtype (or meet, or greatest lower bound) of the given set of types. */ #ifndef CANGJIE_SEMA_JOINANDMEET_H #define CANGJIE_SEMA_JOINANDMEET_H #include #include #include "cangjie/AST/Types.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Modules/ImportManager.h" namespace Cangjie { struct DualMode { Ptr bound; // Any for join, Nothing for meet std::function(const std::set>&)> coFunc; // join for join, meet for meet std::function(const std::set>&)> contraFunc; // meet for join, join for meet std::function, Ptr)> coSubtyFunc; // is-subtype for join, is-supertype for meet }; class JoinAndMeet { using ErrMsg = std::stack; using ErrOrTy = std::variant>; public: // if curFile is given, impMgr must also be given JoinAndMeet(TypeManager& tyMgr, const std::initializer_list> tySet, const std::initializer_list> ignoredTyVars = {}, Ptr impMgr = nullptr, Ptr curFile = nullptr) : tyMgr(tyMgr), tySet(tySet), ignoredTyVars(ignoredTyVars), impMgr(impMgr), curFile(curFile) { } // if curFile is given, impMgr must also be given JoinAndMeet(TypeManager& tyMgr, const std::set> tySet, const std::set> ignoredTyVars = {}, Ptr impMgr = nullptr, Ptr curFile = nullptr) : tyMgr(tyMgr), tySet(tySet), ignoredTyVars(ignoredTyVars), impMgr(impMgr), curFile(curFile) { } /** * Calculate the join (i.e. least upper bound) of two types. * sprsErr: suppress error messages. We opt in reporting the summary of errors after the join (meet) finishes and * the error messages produced along the calculation are regarded as logs for debuging. * Turn the sprsErr from true to false when debuging this module. */ ErrOrTy Join(bool sprsErr = true); ErrOrTy JoinAsVisibleTy(); /** * Calculate the meet (i.e. greatest lower bound) of two types. */ ErrOrTy Meet(bool sprsErr = true); ErrOrTy MeetAsVisibleTy(); static std::string CombineErrMsg(ErrMsg& msgs); // Caution! The serial of functions modifies the first argument. // The first argument is guaranteed to be not null after the invocation. static std::optional SetJoinedType( Ptr& ty, std::variant, Ptr>& joinRes); static std::optional SetMetType( Ptr& ty, std::variant, Ptr>& metRes); // Convert the input type to a user-visible one by eliminating intersection and union types. // Use a boolean value isJoin to distinguish the join and meet mode. Ptr ToUserVisibleTy(Ptr ty); private: TypeManager& tyMgr; const std::set> tySet; const TyVars ignoredTyVars; Ptr impMgr; Ptr curFile; ErrMsg errMsg; bool isForcedToUserVisible = false; Ptr BatchJoin(const std::set>& tys); Ptr BatchMeet(const std::set>& tys); Ptr JoinOrMeetFuncTy(const DualMode& mode, const std::set>& tys); Ptr JoinOrMeetTupleTy(const DualMode& mode, const std::set>& tys); void AddFinalErrMsgs(const AST::Ty& ty, bool isJoin); bool IsInputValid() const; }; } // namespace Cangjie #endif // CANGJIE_SEMA_JOINANDMEET_H cangjie_compiler-1.0.7/src/Sema/LegalityOfUsage/000077500000000000000000000000001510705540100215075ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/LegalityOfUsage/CMakeLists.txt000066400000000000000000000005241510705540100242500ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB LEGALITY_SRC *.cpp) set(SEMA_SRC ${SEMA_SRC} ${LEGALITY_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/LegalityOfUsage/CheckInternalTypeUse.cpp000066400000000000000000000150131510705540100262440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements internal type use in public decl. */ #include "TypeCheckerImpl.h" #include "Diags.h" #include "TypeCheckUtil.h" namespace Cangjie { using namespace AST; using namespace Sema; using namespace TypeCheckUtil; namespace { std::pair, bool> IsAccessible(Ptr type, AccessLevel srcLevel) { if (!Ty::IsTyCorrect(type)) { return {nullptr, true}; } for (const auto& ty : std::as_const(type->typeArgs)) { if (!Ty::IsTyCorrect(ty)) { continue; } if (auto [decl, accessible] = IsAccessible(ty, srcLevel); !accessible) { return {decl, false}; } } if (type->IsNominal()) { auto decl = Ty::GetDeclPtrOfTy(type); if (decl && !IsCompatibleAccessLevel(srcLevel, GetAccessLevel(*decl))) { return {decl, false}; } } return {nullptr, true}; } void CollectGenericTyAccessibility(const AST::Decl& decl, std::vector>& limitedDecls) { auto generic = decl.GetGeneric(); if (!generic) { return; } auto declLevel = GetAccessLevel(decl); for (auto& it : generic->genericConstraints) { for (auto& upperBound : it->upperBounds) { if (!upperBound->ty) { continue; } if (auto [ubDecl, accessible] = IsAccessible(upperBound->ty, declLevel); !accessible) { (void)limitedDecls.emplace_back(*upperBound, *ubDecl); } } } } } // namespace void TypeChecker::TypeCheckerImpl::CheckAccessLevelValidity(Package& package) { for (auto& file : package.files) { for (auto& decl : file->decls) { CJC_ASSERT(decl); if (decl->TestAttr(Attribute::PRIVATE)) { continue; } if (decl->TestAttr(Attribute::FROM_COMMON_PART)) { continue; } CheckNonPrivateDeclAccessLevelValidity(*decl); } } } void TypeChecker::TypeCheckerImpl::CheckNonPrivateDeclAccessLevelValidity(Decl& decl) { if (!Ty::IsTyCorrect(decl.ty)) { return; } if (auto id = DynamicCast(&decl)) { CheckNominalDeclAccessLevelValidity(*id); } else if (auto fd = DynamicCast(&decl)) { CheckFuncAccessLevelValidity(*fd); } else if (auto vpd = DynamicCast(&decl)) { CheckPatternVarAccessLevelValidity(*vpd->irrefutablePattern); } else if (auto tad = DynamicCast(&decl)) { std::vector> limitedDecls; CJC_NULLPTR_CHECK(tad->type); if (auto [inDecl, accessible] = IsAccessible(tad->type->ty, GetAccessLevel(*tad)); !accessible) { (void)limitedDecls.emplace_back(*tad->type, *inDecl); } CollectGenericTyAccessibility(*tad, limitedDecls); DiagLowerAccessLevelTypesUse(diag, *tad, limitedDecls); } else if (auto pd = DynamicCast(&decl)) { CJC_NULLPTR_CHECK(pd->type); if (auto [inDecl, accessible] = IsAccessible(pd->ty, GetAccessLevel(*pd)); !accessible) { std::vector> limitedDecls; (void)limitedDecls.emplace_back(*pd->type, *inDecl); DiagLowerAccessLevelTypesUse(diag, *pd, limitedDecls); } } else if (auto vd = DynamicCast(&decl)) { auto [inDecl, accessible] = IsAccessible(vd->ty, GetAccessLevel(*vd)); if (accessible) { return; } std::vector> limitedDecls; if (vd->type) { (void)limitedDecls.emplace_back(*vd->type, *inDecl); DiagLowerAccessLevelTypesUse(diag, *vd, limitedDecls); } else { // The type of variable is obtained by inference. DiagLowerAccessLevelTypesUse(diag, *vd, limitedDecls, {inDecl}); } } } void TypeChecker::TypeCheckerImpl::CheckNominalDeclAccessLevelValidity(const InheritableDecl& id) { if (id.astKind == AST::ASTKind::EXTEND_DECL) { return; } std::vector> limitedDecls; CollectGenericTyAccessibility(id, limitedDecls); DiagLowerAccessLevelTypesUse(diag, id, limitedDecls); for (auto& it : id.GetMemberDeclPtrs()) { CJC_NULLPTR_CHECK(it); if (!(it->TestAttr(Attribute::PRIVATE)) && !(it->TestAttr(Attribute::FROM_COMMON_PART))) { CheckNonPrivateDeclAccessLevelValidity(*it); } } } void TypeChecker::TypeCheckerImpl::CheckFuncAccessLevelValidity(const FuncDecl& fd) { CJC_NULLPTR_CHECK(fd.funcBody); std::vector> limitedDecls; std::vector> hintDecls; if (fd.funcBody->retType) { if (auto [decl, accessible] = IsAccessible(fd.funcBody->retType->ty, GetAccessLevel(fd)); !accessible) { if (!fd.funcBody->retType->TestAttr(Attribute::COMPILER_ADD)) { (void)limitedDecls.emplace_back(*fd.funcBody->retType, *decl); } else { // The type of function return type is obtained by inference. (void)hintDecls.emplace_back(decl); } } } for (auto& param : (*fd.funcBody->paramLists[0]).params) { if (fd.TestAttr(Attribute::FROM_COMMON_PART)) { continue; } CJC_ASSERT(param && param->type); if (auto [inDecl, accessible] = IsAccessible(param->ty, GetAccessLevel(fd)); !accessible) { (void)limitedDecls.emplace_back(*param->type, *inDecl); } } CollectGenericTyAccessibility(fd, limitedDecls); DiagLowerAccessLevelTypesUse(diag, fd, limitedDecls, hintDecls); } void TypeChecker::TypeCheckerImpl::CheckPatternVarAccessLevelValidity(AST::Pattern& pattern) { std::vector> limitedDecls; Walker(&pattern, [&limitedDecls](Ptr node) -> VisitAction { if (auto vd = DynamicCast(node)) { if (auto [inDecl, accessible] = IsAccessible(vd->ty, GetAccessLevel(*vd)); !accessible) { (void)limitedDecls.emplace_back(*vd, *inDecl); } return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }).Walk(); DiagPatternInternalTypesUse(diag, limitedDecls); } } // namespace Cangjie cangjie_compiler-1.0.7/src/Sema/LegalityOfUsage/GlobalVarChecker.cpp000066400000000000000000000575751510705540100253740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements initialization checking for global variables. */ #include "TypeCheckerImpl.h" #include #include #include "TypeCheckUtil.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/Match.h" namespace Cangjie { using namespace AST; using namespace Meta; using namespace TypeCheckUtil; namespace { /** * For each global variable `g`, we wrap it in a `DefNode` * and collect other global variables *MAY* be use by `g` during its initialization. * See @DoCollect for more details. * For example, * * ``` * let g1 = foo() // g1 may use { g2 } * let g2 = 3 // g2 may use { } * let g3 = bar() // g2 may use { g1, g2 } * func foo() { g2 = 4 } * func bar() { g1 = g2 } * ``` * * We build a def-use graph for all global variables, and do two checking phrases: * 1. For global variables in a same file, * we check whether they are initialized in (their declaration) order. * In the above example, `g2` is used before being initialized. * * 2. For global variables from different files, the checking becomes tricky: * because we can reorder the initialization order of variables from different files. * I.e., the checking is valid as long as we can find a valid initialization order. * To enforce this checking, we attempt to find a topological order between variables. * * * Data structure for the def-use graph. */ struct DefNode; struct UseEdge; /** * Auxiliary functions */ bool IsGlobalOrStaticOrConstVar(const Decl& decl) { if ((decl.astKind == ASTKind::VAR_DECL || decl.astKind == ASTKind::VAR_WITH_PATTERN_DECL) && (decl.TestAnyAttr(Attribute::GLOBAL, Attribute::STATIC) || decl.IsConst())) { return true; } return false; } bool IsRefGlobalOrStaticVar(const RefExpr& re) { CJC_ASSERT(re.ref.target); return IsGlobalOrStaticOrConstVar(*re.ref.target); } Ptr GetTargetIfShouldCollect(const Expr& expr) { auto target = expr.GetTarget(); if (target == nullptr) { return target; } // Get real used target. eg: if target is property, get real used getter or setter which is decided by expr. target = GetUsedMemberDecl(*target, !expr.TestAttr(Attribute::LEFT_VALUE)); // The target only need be collected when it is funcDecl. return target->astKind == ASTKind::FUNC_DECL ? target : nullptr; } inline bool CanBeIgnored(const Decl& decl) { return decl.TestAnyAttr(Attribute::UNSAFE, Attribute::IMPORTED, Attribute::FOREIGN); } bool IsInSameFile(const Node& n1, const Node& n2) { if (n1.astKind == ASTKind::FILE || n2.astKind == ASTKind::FILE) { return false; } return n1.curFile && n2.curFile && n1.curFile->fileHash == n2.curFile->fileHash; } /** * A wrapper of `DefNode` with `RefExpr`. * The `RefExpr` records the position where the `DefNode` is used * and it's used for a better error reporting. */ struct UseEdge { const Node& refNode; const Position refPos; std::string refName; DefNode& node; explicit UseEdge(DefNode& n); UseEdge(const Node& node, const Position& pos, const std::string& name, DefNode& n) : refNode(node), refPos(pos), refName(name), node(n) { } }; /** * The concept of tri-color marking is borrowed from tracing garbage collection. * The white set is the set of objects that are candidates for having their memory recycled. * The black set is the set of objects that can be shown to have no outgoing references to objects in the white set, * and to be reachable from the roots. Objects in the black set are not candidates for collection. * The grey set contains all objects reachable from the roots but yet to be scanned for references to "white" objects. * Since they are known to be reachable from the roots, they cannot be garbage-collected and will end up * in the black set after being scanned. */ enum class Color { WHITE, BLACK, GRAY, }; /** * A `DefNode` is a wrapper of `VarDecl` with `UseEdge`s. * The `visitOrder` field ensures that: * if two variable `a` and `b` are defined in the same file and `a` is defined first; * then, `a.visitOrder` < `b.visitOrder`. */ struct DefNode { const Decl& var; std::vector usage; // Current variable uses other variables. std::vector usedBy; // Current variable is used by other variables. int visitOrder = -1; // This field reflects the declaration order of current variable in the file Color color{Color::WHITE}; explicit DefNode(const Decl& v) : var(v) { } }; UseEdge::UseEdge(DefNode& n) : refNode(n.var), node(n) { } class VarWithPatternDeclMap { public: void Add(const VarWithPatternDecl& vpd) { if (!Utils::In(&vpd, cachedVPDs)) { ConstWalker(&vpd, [this, &vpd](auto node) { if (node->astKind == ASTKind::VAR_DECL) { outerVPDMap.emplace(StaticCast(node), &vpd); return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }).Walk(); cachedVPDs.emplace(&vpd); } } Ptr GetOuterVPD(const VarDecl& vd) const { auto iter = outerVPDMap.find(&vd); if (iter == outerVPDMap.cend()) { return nullptr; } return iter->second; } private: std::unordered_set> cachedVPDs; std::unordered_map, Ptr> outerVPDMap; }; /** * The def-use graph */ struct DefUseGraph { DefNode& GetOrBuildNode(const Decl& var) { Ptr vda = &var; if (auto vpd = DynamicCast(vda)) { vpdMap.Add(*vpd); } else if (auto vd = DynamicCast(vda)) { auto outerVPD = vpdMap.GetOuterVPD(*vd); if (outerVPD != nullptr) { vda = outerVPD; } } auto it = nodes.find(vda); if (it != nodes.cend()) { return *it->second; } auto node = MakeOwned(*vda); auto rawPtr = node.get(); nodes[vda] = std::move(node); return *rawPtr; } void AddEdge(const Decl& user, const VarDeclAbstract& used, const Expr& refExpr) { auto& userNode = GetOrBuildNode(user); auto& usedNode = GetOrBuildNode(used); Position pos; std::string refName; if (auto re = DynamicCast(&refExpr); re) { pos = re->begin; refName = re->ref.identifier; } else if (auto ma = DynamicCast(&refExpr); ma) { pos = ma->field.Begin(); refName = ma->field; } else { CJC_ABORT(); } userNode.usage.emplace_back(refExpr, pos, refName, usedNode); usedNode.usedBy.emplace_back(refExpr, pos, refName, userNode); } void AddEdge(const Decl& user, const VarDeclAbstract& used) { auto& userNode = GetOrBuildNode(user); auto& usedNode = GetOrBuildNode(used); userNode.usage.emplace_back(usedNode); usedNode.usedBy.emplace_back(userNode); } std::map, OwnedPtr, CmpNodeByPos> nodes; VarWithPatternDeclMap vpdMap; }; class GlobalVarChecker { public: explicit GlobalVarChecker(DiagnosticEngine& d) : diag(d) { } void DoCollect(const Package& package); void DoCheck(const Package& package); ~GlobalVarChecker() { currentNode = nullptr; } private: template void CollectForStaticVar(const Decl& structDecl); void CollectForVar(const Decl& decl); void CollectVarUsageBFS(const Node& node); void CollectVarUsageBFSImpl(const Node& node, std::queue>& worklist); void CollectForStaticInit(const FuncDecl& staticInit, const std::unordered_set>& staticVars); std::vector> GetDefaultInitVariables(Decl& outerDecl) const; template std::vector> GetDefaultInitVariablesInStruct(Decl& structDecl) const; void CollectInRefExpr(const AST::RefExpr& re); void CollectInMemberAccessExpr(const MemberAccess& ma); bool CheckInSameFile() const; bool CheckVarUsageForDefNode(const DefNode& defNode) const; void CheckCrossFile(const Package& package); void AddInitializationOrderEdge(const File& file); void CheckByToposort(); bool ToposortDFS(DefNode& node); DiagnosticEngine& diag; DefUseGraph graph; Ptr currentNode{nullptr}; int visitOrder = 0; }; void GlobalVarChecker::DoCollect(const Package& package) { for (auto& file : package.files) { if (file == nullptr) { continue; } // For each global or static variable, // we collect dependent variables it may use during its initialization. for (auto& decl : file->decls) { CJC_ASSERT(decl); if (IsGlobalOrStaticOrConstVar(*decl)) { CollectForVar(*decl); } else if (decl->astKind == ASTKind::CLASS_DECL) { CollectForStaticVar(*decl); } else if (decl->astKind == ASTKind::STRUCT_DECL) { CollectForStaticVar(*decl); } } } } /** * For each global variable, we collect all other global variables it MAY use during the initialization. * With a conservative strategy, the collecting phrase analyzes recursively all functions it refers, * i.e., it conducts a context-insensitive reachability analysis. * * ``` * let g1 = foo() // g1 may use { g2 } though it will not use g2 actually. * let g2 = 3 // g2 may use { } * func foo() { let _ = bar // NO calling } * func bar() { g2 = 4 } */ void GlobalVarChecker::CollectForVar(const Decl& decl) { auto& varDecl = StaticCast(decl); CJC_ASSERT(varDecl.TestAnyAttr(Attribute::GLOBAL, Attribute::STATIC) || varDecl.IsConst()); currentNode = &graph.GetOrBuildNode(varDecl); currentNode->visitOrder = visitOrder++; CollectVarUsageBFS(varDecl); } void GlobalVarChecker::CollectForStaticInit(const FuncDecl& staticInit, const std::unordered_set>& staticVars) { std::unordered_set> visited; std::function)> preVisit = [this, &preVisit, &visited, &staticVars](auto node) { if (auto expr = DynamicCast(node); expr && expr->desugarExpr) { Walker(expr->desugarExpr.get(), preVisit).Walk(); return VisitAction::SKIP_CHILDREN; } if (auto ae = DynamicCast(node); ae && ae->leftValue && ae->rightExpr) { auto target = DynamicCast(ae->leftValue->GetTarget()); if (target && staticVars.count(target) != 0 && visited.count(target) == 0) { target->EnableAttr(AST::Attribute::INITIALIZED); bool isLet = !target->isVar; bool isCommon = target->TestAttr(AST::Attribute::COMMON); bool isStatic = target->TestAttr(AST::Attribute::STATIC); if (isLet && isCommon && isStatic) { diag.DiagnoseRefactor(DiagKindRefactor::sema_common_static_let_cant_be_initialized_in_static_init, *node, target->identifier.Val()); } currentNode = &graph.GetOrBuildNode(*target); // Update visitOrder when initializing static members inside static init. currentNode->visitOrder = visitOrder++; CollectVarUsageBFS(*ae->rightExpr); visited.emplace(target); return VisitAction::SKIP_CHILDREN; } } return VisitAction::WALK_CHILDREN; }; Walker(staticInit.funcBody.get(), preVisit).Walk(); currentNode = &graph.GetOrBuildNode(staticInit); currentNode->visitOrder = visitOrder++; CollectVarUsageBFS(staticInit); } /** * Collect all static variables in the class or struct. * DeclType will be `ClassDecl` or `StructDecl`. */ template void GlobalVarChecker::CollectForStaticVar(const Decl& structDecl) { auto* outerDecl = StaticCast(&structDecl); CJC_NULLPTR_CHECK(outerDecl->body); std::unordered_set> uninitStaticVars; Ptr staticInit = nullptr; for (auto& decl : outerDecl->body->decls) { if (IsGlobalOrStaticOrConstVar(*decl)) { CollectForVar(*decl); if (auto vd = DynamicCast(decl.get()); vd && !vd->initializer) { uninitStaticVars.emplace(decl.get()); } } else if (auto fd = DynamicCast(decl.get()); fd && IsStaticInitializer(*fd)) { staticInit = fd; } } if (staticInit != nullptr) { // Static constructor is used to initialize static member variables, // we should collect usage inside 'static init' after all static member has been collected. CollectForStaticInit(*staticInit, uninitStaticVars); } } template std::vector> GlobalVarChecker::GetDefaultInitVariablesInStruct(Decl& structDecl) const { std::vector> result; auto outerDecl = StaticAs(&structDecl); CJC_NULLPTR_CHECK(outerDecl->body); for (auto& decl : outerDecl->body->decls) { if (decl && decl->astKind == ASTKind::VAR_DECL && !decl->TestAttr(Attribute::STATIC)) { result.push_back(StaticAs(decl.get())); } } return result; } std::vector> GlobalVarChecker::GetDefaultInitVariables(Decl& outerDecl) const { if (outerDecl.astKind == ASTKind::CLASS_DECL) { return GetDefaultInitVariablesInStruct(outerDecl); } else if (outerDecl.astKind == ASTKind::STRUCT_DECL) { return GetDefaultInitVariablesInStruct(outerDecl); } return std::vector>(); } void GlobalVarChecker::CollectVarUsageBFS(const Node& node) { std::queue> worklist; std::set> visited; worklist.push(&node); while (!worklist.empty()) { auto currNode = worklist.front(); worklist.pop(); if (visited.count(currNode) > 0) { continue; } visited.insert(currNode); CollectVarUsageBFSImpl(*currNode, worklist); } } void GlobalVarChecker::CollectVarUsageBFSImpl(const Node& node, std::queue>& worklist) { std::function)> visitor = [this, &worklist, &visitor](Ptr n) { if (auto expr = DynamicCast(n); expr && expr->desugarExpr) { Walker(expr->desugarExpr.get(), visitor).Walk(); return VisitAction::SKIP_CHILDREN; } switch (n->astKind) { case ASTKind::FUNC_DECL: { const auto& fd = StaticCast(*n); if (CanBeIgnored(fd)) { return VisitAction::SKIP_CHILDREN; } else if (!fd.outerDecl || !IsInstanceConstructor(fd)) { return VisitAction::WALK_CHILDREN; } // If the function is a constructor, we add member variables with // default initializations into the worklist because // initializers will not be inlined into the constructor (in the AST). // For the example code: `var g = 3; def-class A { let a = g; init(){} } ` // When the function is `init`, we should add `a = g` into the worklist as well. for (auto varDecl : GetDefaultInitVariables(*fd.outerDecl)) { worklist.push(varDecl); } return VisitAction::WALK_CHILDREN; } // Since a lambda expression may capture a global/static variable but not called immediately, // there is no need to analyze the lambda expr. // E.g., if a lambda expr is assigned: `a = { global_x }`, // we should analyze it only when `a()` where `a` ref to the lambda expr. // However, this pattern can only be guranteed for VarDecl and AssignExpr. // If a lambda is passed as an argument, it's hard to analyze whether it is called or not, // so we treat lambda exprs (as arguments) called conservatively. case ASTKind::VAR_DECL: { const auto& varDecl = StaticCast(*n); if (varDecl.initializer && varDecl.initializer->astKind != ASTKind::LAMBDA_EXPR) { worklist.push(varDecl.initializer.get()); } return VisitAction::SKIP_CHILDREN; } case ASTKind::VAR_WITH_PATTERN_DECL: { const auto& vpd = StaticCast(*n); if (vpd.initializer) { worklist.emplace(vpd.initializer.get()); } return VisitAction::SKIP_CHILDREN; } case ASTKind::ASSIGN_EXPR: { const auto& ae = StaticCast(*n); if (ae.rightExpr && ae.rightExpr->astKind != ASTKind::LAMBDA_EXPR) { worklist.push(ae.rightExpr.get()); } // Left value of assignExpr may be property which should be collected. if (auto target = ae.leftValue ? GetTargetIfShouldCollect(*ae.leftValue) : nullptr) { worklist.push(target); } return VisitAction::SKIP_CHILDREN; } case ASTKind::MEMBER_ACCESS: { const auto& ma = StaticCast(*n); if (auto target = GetTargetIfShouldCollect(ma)) { worklist.push(target); } CollectInMemberAccessExpr(ma); return VisitAction::WALK_CHILDREN; } case ASTKind::REF_EXPR: { const auto& re = StaticCast(*n); CollectInRefExpr(re); // If referring a function or property, collect recursively. if (auto target = GetTargetIfShouldCollect(re)) { worklist.push(target); } return VisitAction::SKIP_CHILDREN; } default: return VisitAction::WALK_CHILDREN; } }; ConstWalker walker(&node, visitor); walker.Walk(); } void GlobalVarChecker::CollectInRefExpr(const RefExpr& re) { if (re.ref.target == nullptr || re.ref.target->astKind == ASTKind::GENERIC_PARAM_DECL || re.isThis || re.ref.target->TestAnyAttr(Attribute::IMPORTED, Attribute::FOREIGN, Attribute::ENUM_CONSTRUCTOR)) { return; } // If referring a global or static variable, add it in the graph. if (IsRefGlobalOrStaticVar(re)) { auto targetVarDecl = StaticAs(re.ref.target); CJC_ASSERT(targetVarDecl); graph.AddEdge(currentNode->var, *targetVarDecl, re); } return; } void GlobalVarChecker::CollectInMemberAccessExpr(const MemberAccess& ma) { if (!ma.target) { return; } // If referring a global or static variable via a member access, // e.g., SomeClass.some_static_variable, // add it in the graph. if (IsGlobalOrStaticOrConstVar(*ma.target)) { auto varDecl = StaticAs(ma.target); CJC_ASSERT(varDecl); graph.AddEdge(currentNode->var, *varDecl, ma); } return; } /** * @Return value: * true -> Checking success * false -> Checking failure and detecting issues */ bool GlobalVarChecker::CheckInSameFile() const { bool result = true; for (auto& it : graph.nodes) { auto& defNode = it.second; if (!CheckVarUsageForDefNode(*defNode)) { result = false; } } return result; } /** * @Return value: * true -> Checking success * false -> The variable wrapped in the `defNode` uses an uninitialized variable, * and these two variables are in the same file. */ bool GlobalVarChecker::CheckVarUsageForDefNode(const DefNode& defNode) const { for (auto usage : defNode.usage) { auto& usedNode = usage.node; if (!IsInSameFile(defNode.var, usedNode.var)) { continue; } if (usedNode.visitOrder >= defNode.visitOrder) { std::string identifierPrefix = (defNode.var.astKind == ASTKind::FUNC_DECL && defNode.var.outerDecl) ? (defNode.var.outerDecl->identifier.Val() + ".") : ""; std::string identifier = (defNode.var.astKind != ASTKind::VAR_WITH_PATTERN_DECL) ? (identifierPrefix + defNode.var.identifier.Val()) : usage.refNode.GetTarget()->identifier.Val(); diag.Diagnose(usage.refNode, usage.refPos, DiagKind::sema_global_var_used_before_initialization, usage.refName, identifier); return false; } } return true; } /** * Though we can reorder the initialization order of global variables from different files, * we must obey that variables in a same file are initialized in their definition order. * Thus, we add a fake def-use edge between variables in the same file. * ``` * let g = bar() * let h = baz() * let u = baz() * ``` * We add fake def-use edges: h <- g, u <- g, u <- h; * thus, we ensure that `g` will be initialized first; then, `h` and `u` are initialized. */ void GlobalVarChecker::CheckCrossFile(const Package& package) { for (auto& file : package.files) { if (file == nullptr) { continue; } AddInitializationOrderEdge(*file); } CheckByToposort(); } void GlobalVarChecker::AddInitializationOrderEdge(const File& file) { Ptr prevDecl = nullptr; for (auto& decl : file.decls) { if (decl && decl->astKind == ASTKind::VAR_DECL) { auto* varDecl = StaticAs(decl.get()); if (prevDecl) { // A fake def-use edge: current variable uses previous defined variable. graph.AddEdge(*varDecl, *prevDecl); } prevDecl = varDecl; } } } void GlobalVarChecker::CheckByToposort() { for (auto& it : graph.nodes) { auto& defNode = it.second; if (defNode->color == Color::WHITE) { // If a cycle is detected, early return. if (!ToposortDFS(*defNode)) { return; } } } } /** * @Return value: * true -> toposort success * false -> a cycle detected */ bool GlobalVarChecker::ToposortDFS(DefNode& node) { if (node.color == Color::BLACK) { return true; } // Detect a cycle if (node.color == Color::GRAY) { return false; } node.color = Color::GRAY; for (auto& usage : node.usage) { if (!ToposortDFS(usage.node)) { if (usage.node.color == Color::GRAY) { diag.Diagnose(usage.refNode, usage.refPos, DiagKind::sema_used_before_initialization, usage.refName); } // Also set black for early quit. Used to distinguish the situation from detecting cycle. node.color = Color::BLACK; return false; } } node.color = Color::BLACK; return true; } void GlobalVarChecker::DoCheck(const Package& package) { bool result = CheckInSameFile(); // If checking in the same file fails, there is no need to check cross files. if (!result) { return; } CheckCrossFile(package); } } // namespace void TypeChecker::TypeCheckerImpl::CheckGlobalVarInitialization([[maybe_unused]]ASTContext& ctx, const Package& package) { GlobalVarChecker checker(diag); checker.DoCollect(package); checker.DoCheck(package); } } // namespace Cangjie cangjie_compiler-1.0.7/src/Sema/LegalityOfUsage/InitializationChecker.cpp000066400000000000000000002143241510705540100264750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements initialization checking. */ #include "InitializationChecker.h" #include #include #include "Diags.h" #include "TypeCheckUtil.h" #include "TypeCheckerImpl.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/Print.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace AST; using namespace Sema; using namespace TypeCheckUtil; namespace { inline bool IsFuncOrProp(const Node& node) { return node.astKind == ASTKind::FUNC_DECL || node.astKind == ASTKind::PROP_DECL; } bool IsInitialized(MemberAccess& ma) { bool init = false; auto visit = [&init](Ptr node) { if (auto target = node->GetTarget(); target && target->TestAttr(Attribute::INITIALIZED)) { init = true; return VisitAction::STOP_NOW; } return VisitAction::WALK_CHILDREN; }; Walker(&ma, visit).Walk(); return init; } inline bool IsInStructDecl(const VarDecl& vd) { return vd.outerDecl && vd.outerDecl->IsNominalDecl(); } inline bool IsDeclaredInSameFile(const Decl& d1, const Decl& d2) { return d1.curFile && d2.curFile && d1.curFile->fileHash == d2.curFile->fileHash; } inline bool IsInitializedButUnReachable(const VarDecl& vd) { return vd.TestAttr(Attribute::INITIALIZED) && vd.initializer && vd.initializer->TestAttr(Attribute::UNREACHABLE); } bool IsAllInitialized(const Node& node) { if (node.astKind == ASTKind::CLASS_DECL || node.astKind == ASTKind::STRUCT_DECL) { auto decls = RawStaticCast(&node)->GetMemberDeclPtrs(); for (auto& it : decls) { if (it != nullptr && it->astKind == ASTKind::VAR_DECL && !it->TestAttr(Attribute::INITIALIZED)) { return false; } } } return true; } bool IsRelatedTypeDecl(const Decl& curComposite, const Decl& outerDeclOfTarget) { if (curComposite.astKind == ASTKind::CLASS_DECL) { auto currentClass = StaticCast(&curComposite); while (currentClass != nullptr) { if (currentClass == &outerDeclOfTarget) { return true; } currentClass = currentClass->GetSuperClassDecl(); } } return &curComposite == &outerDeclOfTarget; } bool IsUsedInInitFunction(const ASTContext& ctx, const Expr& expr) { Ptr decl = expr.GetTarget(); bool inInitFunction = false; if (decl != nullptr && decl->TestAnyAttr(Attribute::IN_STRUCT, Attribute::IN_CLASSLIKE)) { Symbol* symOfVdStruct = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, decl->scopeName); Symbol* symOfExprStruct = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, expr.scopeName); Symbol* symOfExprFunc = ScopeManager::GetOutMostSymbol(ctx, SymbolKind::FUNC, expr.scopeName); if (symOfExprStruct != nullptr && symOfVdStruct != nullptr && symOfExprFunc != nullptr) { auto structOfExpr = symOfExprStruct->node; auto structOfVd = symOfVdStruct->node; CJC_NULLPTR_CHECK(symOfExprFunc->node); auto currentFunc = symOfExprFunc->node; bool sameStaticStatus = currentFunc->TestAttr(Attribute::STATIC) == decl->TestAttr(Attribute::STATIC); if (currentFunc->TestAttr(Attribute::CONSTRUCTOR) && sameStaticStatus && IsRelatedTypeDecl(*StaticCast(structOfExpr), *StaticCast(structOfVd))) { inInitFunction = true; } } } return inInitFunction; } void GetInitsInExpr(Ptr node, std::unordered_set>& initedNodes) { if (!node) { return; } auto visitor = [&initedNodes](Ptr n) { auto target = n->GetTarget(); if (auto expr = DynamicCast(n); expr && expr->IsReferenceExpr() && target && target->TestAttr(Attribute::INITIALIZED)) { initedNodes.insert(target); } return VisitAction::WALK_CHILDREN; }; Walker walker(node, visitor); walker.Walk(); } void GetUnInitsInExpr(Ptr node, std::unordered_set>& unInitedNodes) { auto visitor = [&unInitedNodes](Ptr n) { auto target = n->GetTarget(); if (auto expr = DynamicCast(n); expr && expr->IsReferenceExpr() && target && !target->TestAttr(Attribute::INITIALIZED)) { unInitedNodes.insert(target); } return VisitAction::WALK_CHILDREN; }; Walker walker(node, visitor); walker.Walk(); } void GetLocalUnInitsInExpr(const Ptr& node, std::unordered_set>& localUnInits) { Walker(node, [&localUnInits](auto node) { if (auto vda = DynamicCast(node); vda && !vda->TestAttr(Attribute::INITIALIZED)) { localUnInits.emplace(vda); } return VisitAction::WALK_CHILDREN; }).Walk(); } inline bool NotAssignableVariable(const VarDecl& vd, bool inInitFunction) { return !vd.isVar && (vd.TestAnyAttr(Attribute::GLOBAL, Attribute::INITIALIZED, Attribute::ENUM_CONSTRUCTOR) || (vd.TestAnyAttr(Attribute::IN_STRUCT, Attribute::IN_CLASSLIKE) && !inInitFunction)); } inline bool CanSkipInitCheck(const Node& node) { return node.TestAnyAttr(Attribute::IMPORTED, Attribute::FOREIGN, Attribute::ENUM_CONSTRUCTOR); } // Check whether member variable is used in member function/property except constructor. bool IsMemberUseOutsideCtor(const ASTContext& ctx, const Expr& expr, const Decl& decl) { bool isNotMemberVar = !decl.outerDecl || !decl.outerDecl->IsNominalDecl() || decl.astKind != ASTKind::VAR_DECL; if (isNotMemberVar) { return false; } auto outerMostSym = ScopeManager::GetOutMostSymbol(ctx, SymbolKind::FUNC, expr.scopeName); auto outerMostFunc = outerMostSym ? StaticCast(outerMostSym->node) : nullptr; return outerMostFunc && !outerMostFunc->TestAttr(Attribute::CONSTRUCTOR); } bool IsInDifferentFunction(const ASTContext& ctx, const Expr& usage, const Decl& target) { auto usageSym = ScopeManager::GetCurSymbolByKind(SymbolKind::FUNC_LIKE, ctx, usage.scopeName); if (usageSym == nullptr || IsGlobalOrStaticVar(target)) { return false; } auto declSym = ScopeManager::GetCurSymbolByKind(SymbolKind::FUNC_LIKE, ctx, target.scopeName); if (declSym != nullptr) { // Local variable usage. return usageSym != declSym; } if (target.outerDecl == nullptr) { // Local variable declared in the initializer of a global variable. return true; } if (auto usageDecl = DynamicCast(usageSym->node); usageDecl && !usageDecl->TestAttr(Attribute::CONSTRUCTOR)) { // Member variable usage. return target.outerDecl != usageDecl->outerDecl && usageSym != declSym; } // Used in lambda which means capture the member variable. return usageSym->node->astKind == ASTKind::LAMBDA_EXPR; } bool IsAssignLetDefinedOuterLoop(const ASTContext& ctx, const VarDecl& vd, const Expr& curExpr) { if (vd.isVar) { return false; } // When 'vd' is not initialized and the 'curExpr' is compound assignment // or target decl is also referenced in the right hand, // the error of used before intialization has higher priority than assign immutable, return false here. auto ae = DynamicCast(&curExpr); if (ae && !vd.TestAttr(Attribute::INITIALIZED)) { if (ae->isCompound) { return false; } bool occurredInRight = false; Walker(ae->rightExpr.get(), [&occurredInRight, &vd](auto sub) { if (sub->GetTarget() == &vd) { occurredInRight = true; return VisitAction::STOP_NOW; } return VisitAction::WALK_CHILDREN; }).Walk(); if (occurredInRight) { return false; } } auto outerLoop = ScopeManager::GetRefLoopSymbol(ctx, curExpr); if (outerLoop && outerLoop->node->begin > vd.begin) { return true; } return false; } void CollectDeclsFromPropDecl(const OwnedPtr& decl, const PropDecl& pd, std::vector>& funcDecls, std::vector>& staticFuncDecls) { if (!pd.getters.empty()) { for (auto& getter : pd.getters) { if (decl->TestAttr(Attribute::STATIC)) { staticFuncDecls.emplace_back(getter.get()); } else { funcDecls.emplace_back(getter.get()); } } } if (!pd.setters.empty()) { for (auto& setter : pd.setters) { if (decl->TestAttr(Attribute::STATIC)) { staticFuncDecls.emplace_back(setter.get()); } else { funcDecls.emplace_back(setter.get()); } } } } void UpdateContextVaraiables(std::unordered_set>& contextVariables, const Decl& d) { if (d.TestAnyAttr(Attribute::GLOBAL, Attribute::STATIC)) { return; } if (auto vd = DynamicCast(&d)) { contextVariables.emplace(vd); } else if (auto vpd = DynamicCast(&d)) { for (auto it : FlattenVarWithPatternDecl(*vpd)) { if (auto vp = DynamicCast(it)) { contextVariables.emplace(vp->varDecl.get()); } } } } bool MayBeStructTy(const VarDecl& target) { if (!Ty::IsTyCorrect(target.ty)) { return false; } if (target.ty->IsStruct()) { return true; } if (target.ty->IsGeneric()) { auto gTy = RawStaticCast(target.ty); for (auto& ub : std::as_const(gTy->upperBounds)) { // The upper bounds of GenericsTy can only be Classes or Interfaces. CJC_ASSERT(ub->IsClassLike()); if (ub->IsInterface()) { return true; } } } return false; } } // namespace // Only update scope is terminated for control flow expr. void InitializationChecker::UpdateScopeStatus(const Node& node) { CJC_ASSERT(Utils::In(node.astKind, {ASTKind::RETURN_EXPR, ASTKind::JUMP_EXPR, ASTKind::THROW_EXPR})); auto scopeGate = ScopeManagerApi::GetScopeGateName(node.scopeName); bool mayNotGoOn = optionalCtxDepth != 0 && (node.astKind != ASTKind::THROW_EXPR || tryDepth == 0); if (scopeGate.empty() || mayNotGoOn) { return; } // Only update scope info when encountered the control flow expr in same scope for the first time. if (auto [it, success] = variablesBeforeTeminatedScope.try_emplace(scopeGate, std::unordered_set>{}); success) { it->second.reserve(contextVariables.size()); for (auto& [_, vars] : contextVariables) { it->second.insert(vars.cbegin(), vars.cend()); } scopeTerminationKinds[scopeGate] = node.astKind; } // When meeting return expr inside constructor, we need to record current uninitialized member variables. if (node.astKind == ASTKind::RETURN_EXPR) { Symbol* funcSym = ScopeManager::GetCurSymbolByKind(SymbolKind::FUNC_LIKE, ctx, node.scopeName); if (!funcSym || !IsInstanceConstructor(*funcSym->node)) { return; } auto ctor = StaticCast(funcSym->node); if (auto [it, success] = ctorUninitVarsMap.try_emplace(ctor, std::unordered_set>{}); success) { auto typeDecl = ctor->outerDecl; CJC_NULLPTR_CHECK(typeDecl); for (auto& d : typeDecl->GetMemberDecls()) { if (d->astKind == ASTKind::VAR_DECL && !d->TestAttr(Attribute::STATIC) && !d->TestAttr(Attribute::INITIALIZED)) { it->second.emplace(d.get()); } } } } } void InitializationChecker::UpdateInitializationStatus(const AST::AssignExpr& assign, Decl& decl) { // Only set initialized when the accessed decl is not defined before termination of current scope. // Special case: When terminated expr is throwExpr, we only skip the initialization set when assignment is in try. auto searchingScope = ScopeManagerApi::GetScopeGateName(assign.scopeName); auto found = variablesBeforeTeminatedScope.find(searchingScope); if (found != variablesBeforeTeminatedScope.end() && found->second.count(&decl) != 0) { if (scopeTerminationKinds[searchingScope] != ASTKind::THROW_EXPR || tryDepth != 0) { initVarsAfterTerminator[searchingScope].emplace(&decl); return; } } decl.EnableAttr(Attribute::INITIALIZED); } // For cjmp check common class/struct member instance variable init void InitializationChecker::CheckNonCommonVariablesInitInCommonDecl(const InheritableDecl& id) { if (!id.TestAttr(Attribute::COMMON)) { return; } // collect member decls CollectDeclsInfo info = CollectDecls(id); if (!info.initFuncDecls.empty()) { return; } // if there is no initFuncDecls, common decl no-common member variable init check here for (auto decl : info.nonFuncDecls) { if (auto vd = DynamicCast(decl); vd) { if (!vd->TestAttr(Attribute::COMMON) && vd->initializer == nullptr) { diag.Diagnose(*decl, DiagKind::sema_class_uninitialized_field, decl->identifier.Val()); } } } } void InitializationChecker::CheckInitialization(Ptr n) { if (!n || n->TestAttr(Attribute::HAS_BROKEN)) { return; } if (n->TestAttr(Attribute::GLOBAL, Attribute::INITIALIZED)) { return; } auto preVisiter = [this](Ptr n) { if (n->astKind == AST::ASTKind::MACRO_EXPAND_DECL || n->TestAttr(Attribute::INITIALIZATION_CHECKED)) { return VisitAction::SKIP_CHILDREN; } n->EnableAttr(Attribute::INITIALIZATION_CHECKED); if (auto decl = DynamicCast(n)) { UpdateContextVaraiables(contextVariables[ScopeManagerApi::GetScopeGateName(decl->scopeName)], *decl); } switch (n->astKind) { case ASTKind::FUNC_PARAM: n->EnableAttr(Attribute::INITIALIZED); CheckInitInExpr(StaticCast(n)->assignment.get()); return VisitAction::SKIP_CHILDREN; case ASTKind::VAR_DECL: CheckInitInVarDecl(StaticCast(*n)); return VisitAction::SKIP_CHILDREN; case ASTKind::VAR_WITH_PATTERN_DECL: CheckInitInVarWithPatternDecl(StaticCast(*n)); return VisitAction::SKIP_CHILDREN; case ASTKind::FUNC_DECL: { auto& fd = StaticCast(*n); if (fd.TestAttr(Attribute::HAS_INITIAL) && fd.ownerFunc) { // Do not check initialization status for compiler add default param function. // Initialization status will be checked in original function param assignment. return VisitAction::SKIP_CHILDREN; } if (fd.funcBody) { fd.EnableAttr(Attribute::INITIALIZED); CheckInitInFuncBody(*fd.funcBody); } CheckInitInExpr(fd.annotationsArray); return VisitAction::SKIP_CHILDREN; } case ASTKind::EXTEND_DECL: CheckInitInExtendDecl(StaticCast(*n)); StaticCast(n)->EnableAttr(Attribute::INITIALIZED); return VisitAction::SKIP_CHILDREN; case ASTKind::INTERFACE_DECL: { auto& id = StaticCast(*n); for (auto& si : id.inheritedTypes) { CheckInitialization(si->GetTarget()); } CheckInitInTypeDecl(id); id.EnableAttr(Attribute::INITIALIZED); return VisitAction::SKIP_CHILDREN; } case ASTKind::CLASS_DECL: { auto& cd = StaticCast(*n); if (cd.TestAttr(Attribute::IMPORTED)) { return VisitAction::SKIP_CHILDREN; } CheckInitInClassDecl(cd); CheckNonCommonVariablesInitInCommonDecl(cd); cd.EnableAttr(Attribute::INITIALIZED); return VisitAction::SKIP_CHILDREN; } case ASTKind::ENUM_DECL: { auto& ed = StaticCast(*n); for (auto& si : ed.inheritedTypes) { CheckInitialization(si->GetTarget()); } std::for_each(ed.constructors.begin(), ed.constructors.end(), [](auto& it) { it->EnableAttr(Attribute::INITIALIZED); }); CheckInitInTypeDecl(ed); ed.EnableAttr(Attribute::INITIALIZED); return VisitAction::SKIP_CHILDREN; } case ASTKind::STRUCT_DECL: { auto& sd = StaticCast(*n); CheckStaticInitForTypeDecl(sd); CheckInitInTypeDecl(sd); CheckNonCommonVariablesInitInCommonDecl(sd); sd.EnableAttr(Attribute::INITIALIZED); return VisitAction::SKIP_CHILDREN; } case ASTKind::BLOCK: for (auto& node : StaticCast(n)->body) { CheckInitialization(node.get()); } return VisitAction::SKIP_CHILDREN; case ASTKind::PACKAGE: for (auto& file : StaticCast(n)->files) { if (file == nullptr) { continue; } for (auto& decl : file->decls) { CheckInitialization(decl.get()); } } return VisitAction::SKIP_CHILDREN; default: if (auto expr = DynamicCast(n); expr) { if (expr->desugarExpr) { CheckInitInExpr(expr->desugarExpr.get()); } else { CheckInitInExpr(expr); } return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; } }; auto postVisiter = [this](Ptr n) { // Current node's 'scopeName' is a scopeGate which means the boundary of scope. if (n->scopeName.find("_") != std::string::npos) { // When quit the node, clear the context info. ClearScopeStatus(n->scopeName); } return VisitAction::WALK_CHILDREN; }; Walker(n, preVisiter, postVisiter).Walk(); } void InitializationChecker::CheckStaticInitForTypeDecl(const InheritableDecl& id) { Ptr staticInit = nullptr; std::vector> staticMembers; for (auto& member : id.GetMemberDeclPtrs()) { if (!member->TestAttr(Attribute::STATIC)) { continue; } if (member->astKind == ASTKind::VAR_DECL) { CheckInitialization(member); (void)staticMembers.emplace_back(member); } else if (auto fd = DynamicCast(member); fd && IsStaticInitializer(*fd)) { staticInit = fd; } } if (staticInit) { CheckInitialization(staticInit); if (staticInit->funcBody == nullptr) { return; // If the function is broken, we can quit now. } // If the static initializer is terminated by 'ThrowExpr' directly, // the initialization check can be ignored, since the global initialization will fail. auto found = scopeTerminationKinds.find(staticInit->funcBody->scopeName); if (found != scopeTerminationKinds.end() && found->second == ASTKind::THROW_EXPR) { return; } } for (auto decl : staticMembers) { if (decl->TestAnyAttr(Attribute::INITIALIZED, Attribute::TOOL_ADD, Attribute::COMMON) || opts.compileCjd) { continue; } auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_type_uninitialized_static_field, *decl, decl->identifier); std::string note = "you may add an initializer inline or "; if (staticInit) { builder.AddNote(note + "initialize the variable in the 'static init'"); builder.AddNote(*staticInit, MakeRange(staticInit->identifier), "static init defined here"); } else { builder.AddNote(note + "define a 'static init' to perform initialization"); } } } void InitializationChecker::CheckInitInClassDecl(const ClassDecl& cd) { std::vector> superClassNonFuncDecls; std::set> superClasses; for (auto& it : cd.inheritedTypes) { CJC_NULLPTR_CHECK(it); if (auto superClass = DynamicCast(it->GetTarget()); superClass) { if (IsDeclaredInSameFile(cd, *superClass)) { // Since static variables in same file are initialized in the definition order, // if the super class is defined in same file, // static init of current class should be checked before checking super class. CheckStaticInitForTypeDecl(cd); } CheckInitialization(superClass); GetNonFuncDeclsInSuperClass(*superClass, superClassNonFuncDecls, superClasses); } if (auto superInterface = DynamicCast(it->GetTarget()); superInterface) { CheckInitialization(superInterface); } } if (cd.body) { CheckStaticInitForTypeDecl(cd); CheckInitInTypeDecl(cd, superClassNonFuncDecls); } } void InitializationChecker::CheckInitInExtendDecl(const ExtendDecl& ed) { CJC_NULLPTR_CHECK(ed.extendedType); if (!Ty::IsTyCorrect(ed.extendedType->ty)) { return; } // Get the not declared-initialized member variables. std::vector> unInitNonFuncDecls; if (auto typeDecl = Ty::GetDeclPtrOfTy(ed.extendedType->ty); typeDecl && typeDecl->IsStructOrClassDecl()) { for (auto& decl : typeDecl->GetMemberDecls()) { CJC_NULLPTR_CHECK(decl); if (decl->astKind == ASTKind::VAR_DECL && !decl->TestAttr(Attribute::INITIALIZED)) { unInitNonFuncDecls.emplace_back(decl.get()); decl->EnableAttr(Attribute::INITIALIZED); } } } CheckInitInTypeDecl(ed); std::for_each(unInitNonFuncDecls.begin(), unInitNonFuncDecls.end(), [](Ptr decl) { decl->DisableAttr(Attribute::INITIALIZED); }); } void InitializationChecker::CheckInitInFuncBody(const FuncBody& fb) { if (fb.generic) { for (auto& it : fb.generic->typeParameters) { CheckInitialization(it.get()); } for (auto& it : fb.generic->genericConstraints) { CheckInitialization(it.get()); } } for (auto& paramList : fb.paramLists) { CheckInitialization(paramList.get()); } bool needCheckInLoop = !fb.funcDecl || (fb.funcDecl->symbol && ScopeManager::GetCurSymbolByKind(SymbolKind::FUNC_LIKE, ctx, fb.funcDecl->scopeName)); if (needCheckInLoop) { CheckInitInLoop(fb.body.get()); } else { CheckInitialization(fb.body.get()); } } void InitializationChecker::CheckInitInVarWithPatternDecl(VarWithPatternDecl& vpd) { if (vpd.initializer) { CheckInitInExpr(vpd.initializer.get()); vpd.EnableAttr(Attribute::INITIALIZED); } if (vpd.irrefutablePattern) { CheckInitInExpr(vpd.irrefutablePattern.get()); Walker(vpd.irrefutablePattern, [&vpd](auto node) { if (auto vd = DynamicCast(node)) { if (vpd.initializer == nullptr) { vd->DisableAttr(Attribute::INITIALIZED); } vd->EnableAttr(Attribute::INITIALIZATION_CHECKED); } return VisitAction::WALK_CHILDREN; }).Walk(); } CheckInitInExpr(vpd.annotationsArray); } void InitializationChecker::CheckInitInVarDecl(VarDecl& vd) { if (vd.initializer) { if (IsInstanceMember(vd)) { CJC_ASSERT(!currentInitializingVarDependencies); currentInitializingVarDependencies = std::unordered_set>{}; } CheckInitInExpr(vd.initializer.get()); vd.EnableAttr(Attribute::INITIALIZED); // Save recored dependencies to variable decl if relevant if (currentInitializingVarDependencies) { auto& deps = *currentInitializingVarDependencies; std::move(deps.begin(), deps.end(), std::back_inserter(vd.dependencies)); currentInitializingVarDependencies = std::nullopt; } } else if (vd.TestAttr(Attribute::ENUM_CONSTRUCTOR)) { vd.EnableAttr(Attribute::INITIALIZED); } CheckInitInExpr(vd.annotationsArray); } void InitializationChecker::CheckLetFlag(const Expr& ae, const Expr& expr) { bool inInitFunction = IsUsedInInitFunction(ctx, expr); switch (expr.astKind) { case ASTKind::REF_EXPR: if (auto vd = DynamicCast(expr.GetTarget()); vd && (NotAssignableVariable(*vd, inInitFunction) || IsAssignLetDefinedOuterLoop(ctx, *vd, ae))) { DiagCannotAssignToImmutable(diag, ae, expr); } break; case ASTKind::MEMBER_ACCESS: CheckLetFlagInMemberAccess(ae, StaticCast(expr), inInitFunction); break; case ASTKind::SUBSCRIPT_EXPR: { auto& se = StaticCast(expr); if (!se.baseExpr || !Is(se.baseExpr->ty)) { return; } // Multi-level VArray nested assignment expressions cannot be modified. Because the return value of // subscripted accessor function can't be modified. if (se.baseExpr->astKind == ASTKind::SUBSCRIPT_EXPR || se.baseExpr->astKind == ASTKind::CALL_EXPR) { DiagCannotAssignToImmutable(diag, ae, *se.baseExpr); } else { CheckLetFlag(ae, *se.baseExpr); } } default: break; } } void InitializationChecker::CheckLetFlagInMemberAccess(const Expr& ae, const MemberAccess& ma, bool inInitFunction) { if (ma.baseExpr == nullptr) { return; } if (auto vd = DynamicCast(ma.target); vd) { auto& realBaseExpr = ma.baseExpr->desugarExpr ? ma.baseExpr->desugarExpr : ma.baseExpr; if (realBaseExpr == nullptr) { return; } bool isStructBase = realBaseExpr->ty && realBaseExpr->ty->IsStruct(); if (NotAssignableVariable(*vd, inInitFunction)) { DiagCannotAssignToImmutable(diag, ae, ma); } if (Utils::In(realBaseExpr->astKind, {ASTKind::CALL_EXPR, ASTKind::SUBSCRIPT_EXPR}) && isStructBase) { DiagCannotAssignToImmutable(diag, ae, *realBaseExpr); } if (auto bma = DynamicCast(ma.baseExpr.get()); bma) { bool invalidPropAccess = bma->target && bma->target->astKind == ASTKind::PROP_DECL && isStructBase; if (invalidPropAccess) { DiagCannotAssignToImmutable(diag, ae, *bma); } } } // Do not allow assignments of fields of 'let' struct instance if (auto re = DynamicCast(ma.baseExpr.get()); re) { if (auto vd = DynamicCast(re->ref.target); vd && !vd->isVar && MayBeStructTy(*vd)) { DiagCannotAssignToImmutable(diag, ae, *re); } } if (auto m = DynamicCast(ma.baseExpr.get()); m) { if (auto vd = DynamicCast(m->target); vd && MayBeStructTy(*vd)) { if (!vd->isVar) { DiagCannotAssignToImmutable(diag, ae, ma); } else { CheckLetFlag(ae, *ma.baseExpr); } } } } bool InitializationChecker::CheckInitInRefExpr(const RefExpr& re) { auto target = TypeCheckUtil::GetRealTarget(re.ref.target); if (target == nullptr || target->astKind == ASTKind::GENERIC_PARAM_DECL) { return true; } bool invalidUseOfThis = re.isThis && re.isAlone && target->IsStructOrClassDecl() && !IsAllInitialized(*target); if (invalidUseOfThis) { diag.Diagnose(re, DiagKind::sema_illegal_usage_of_member, "this"); return false; } RecordInstanceVariableUsage(*target); if (CanSkipInitCheck(*target) || !IsOrderRelated(re, *target, target->IsNominalDecl())) { return true; } Symbol* toplevelSymOfTarget = ScopeManager::GetCurSymbolByKind(SymbolKind::TOPLEVEL, ctx, target->scopeName); if (toplevelSymOfTarget != nullptr && toplevelSymOfTarget->node != nullptr) { if (CanSkipInitCheck(*toplevelSymOfTarget->node)) { return true; } Symbol* toplevelSymOfRe = ScopeManager::GetCurSymbolByKind(SymbolKind::TOPLEVEL, ctx, re.scopeName); if (toplevelSymOfRe != nullptr && toplevelSymOfRe->node != nullptr) { // If accessing non-static instance member inside nominal struct declaration, check for legality. bool referenceInside = toplevelSymOfRe->node->IsStructOrClassDecl() && target->outerDecl && target->outerDecl->IsNominalDecl() && !target->TestAttr(Attribute::CONSTRUCTOR) && !target->TestAttr(Attribute::STATIC); if (referenceInside && !CheckIllegalRefExprAccess(re, *toplevelSymOfRe, *toplevelSymOfTarget)) { return false; } bool inDiffScope = toplevelSymOfRe != toplevelSymOfTarget && !target->TestAttr(Attribute::STATIC); if (inDiffScope) { return true; } } } // Do not report use before initialization when: // 1. target is funcparam; // 2. target is function or property; // 3. target is instance or static member variable used outside constructor, // which will be checked separately in constructor. if (Is(target) || IsFuncOrProp(*target) || IsMemberUseOutsideCtor(ctx, re, *target)) { return true; } if (re.ShouldDiagnose(true)) { if (target->TestAttr(Attribute::GLOBAL) && target->begin.fileID == re.begin.fileID && re.begin < target->begin) { diag.DiagnoseRefactor(DiagKindRefactor::sema_undefined_variable, re, re.ref.identifier.Val()); return false; } else if (!target->TestAttr(Attribute::INITIALIZED)) { if (initVarsAfterTerminator.count(ScopeManagerApi::GetScopeGateName(re.scopeName)) != 0) { return true; } else if (IsInDifferentFunction(ctx, re, *target)) { // Definition and usage is not in same declaration (usage is not in constructor/member decl). diag.DiagnoseRefactor( DiagKindRefactor::sema_capture_before_initialization, re, target->identifier.Val()); } else if (!re.TestAttr(Attribute::LEFT_VALUE)) { diag.Diagnose(re, DiagKind::sema_used_before_initialization, re.ref.identifier.Val()); } return false; } } return target->TestAttr(Attribute::INITIALIZED); } bool InitializationChecker::CheckInitInMemberAccess(MemberAccess& ma) { bool res = CheckInitInExpr(ma.baseExpr.get()); if (!ma.target) { return res; } RecordInstanceVariableUsage(*ma.target); auto curStructOfMemberAccess = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, ma.scopeName); if (auto re = DynamicCast(ma.baseExpr.get()); re && ma.target->outerDecl && !ma.target->TestAttr(Attribute::STATIC) && curStructOfMemberAccess && curStructOfMemberAccess->node->IsStructOrClassDecl()) { // Check illegal `this.member` access if (re->isThis && re->ref.target && re->ref.target->IsStructOrClassDecl()) { if (!CheckIllegalMemberAccess(ma, *ma.target, *re->ref.target)) { return false; } } // Check illegal `super.memberFunc` access if (re->isSuper && IsFuncOrProp(*ma.target)) { CJC_NULLPTR_CHECK(curStructOfMemberAccess->node); // Symbol must have non-null 'node'. if (!CheckIllegalMemberAccess(ma, *ma.target, *curStructOfMemberAccess->node)) { return false; } } } // Do not report use before initialization when: // 1. target is function or property; // 2. target is instance or static member variable which will be checked separately in constructor. if (IsFuncOrProp(*ma.target) || IsMemberUseOutsideCtor(ctx, ma, *ma.target)) { return res; } bool isUndefinedVar = ma.target->TestAttr(Attribute::GLOBAL) && ma.target->begin.fileID == ma.begin.fileID && ma.begin < ma.target->begin; if (isUndefinedVar) { (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_undefined_variable, ma, ma.target->identifier.Val()); return false; } auto curStructOfTarget = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, ma.target->scopeName); bool isInSameDecl = curStructOfMemberAccess && curStructOfMemberAccess == curStructOfTarget; if (isInSameDecl && !IsInitialized(ma)) { (void)diag.Diagnose(ma, DiagKind::sema_used_before_initialization, ma.target->identifier.Val()); return false; } return true; } bool InitializationChecker::CheckInitInAssignExpr(const AssignExpr& ae) { if (ae.leftValue == nullptr || ae.rightExpr == nullptr) { return false; // Ignore invalid node. } // Check usage of leftValue and rightExpr. CheckLetFlag(ae, *ae.leftValue); if (ae.isCompound) { ae.leftValue->DisableAttr(Attribute::LEFT_VALUE); (void)CheckInitInExpr(ae.leftValue.get()); ae.leftValue->EnableAttr(Attribute::LEFT_VALUE); } (void)CheckInitInExpr(ae.rightExpr.get()); // Check for possible un-initialized left target. auto target = ae.leftValue->GetTarget(); // If leftvalue is not initialized, then initialize. leftvalue is reference expression. if (target) { if (ae.leftValue->astKind == ASTKind::MEMBER_ACCESS) { auto& ma = StaticCast(*ae.leftValue); CheckInitInExpr(ma.baseExpr.get()); } // 1. Top-level/non-variable cannot be accessed before all members initialized in decl, // so if the target is not variable, we should check initialization status here. // 2. For reference of local variable, we need to gurantee that it is initialized in same function scope. bool needCheck = target->scopeLevel == 0 || (target->astKind != ASTKind::VAR_DECL && target->outerDecl && target->outerDecl->IsNominalDecl()) || (ae.leftValue->astKind == ASTKind::REF_EXPR && !target->TestAttr(Attribute::INITIALIZED)); if (!ae.isCompound && needCheck) { (void)CheckInitInExpr(ae.leftValue.get()); } // AssignExpr cannot inialize captured variable. if (target->TestAttr(Attribute::INITIALIZATION_CHECKED) && !IsInDifferentFunction(ctx, ae, *target)) { UpdateInitializationStatus(ae, *target); } return true; } return true; } bool InitializationChecker::CheckInitInExpr(Ptr node) { if (!node) { return true; } if (auto expr = DynamicCast(node); expr) { if (expr->desugarExpr) { return CheckInitInExpr(expr->desugarExpr.get()); } } switch (node->astKind) { case ASTKind::REF_EXPR: return CheckInitInRefExpr(StaticCast(*node)); case ASTKind::MEMBER_ACCESS: return CheckInitInMemberAccess(StaticCast(*node)); case ASTKind::ASSIGN_EXPR: return CheckInitInAssignExpr(StaticCast(*node)); case ASTKind::RETURN_EXPR: { auto ret = CheckInitInExpr(StaticCast(node)->expr.get()); UpdateScopeStatus(*node); return ret; } case ASTKind::TRY_EXPR: return CheckInitInTryExpr(StaticCast(*node)); case ASTKind::MATCH_EXPR: return CheckInitInMatchExpr(StaticCast(*node)); case ASTKind::FOR_IN_EXPR: return CheckInitInForInExpr(StaticCast(*node)); case ASTKind::IF_EXPR: return CheckInitInIfExpr(StaticCast(*node)); case ASTKind::WHILE_EXPR: return CheckInitInWhileExpr(StaticCast(*node)); case ASTKind::DO_WHILE_EXPR: { auto& dwe = StaticCast(*node); // do { // if (true) { // break // } // a = 1 // } while (a == 0) // in this case, we don't check 'a=1' and 'a==0', CHIR will do CheckInitInLoop(dwe.body.get(), false); // Do-while's body is always executed. return CheckInitInExpr(dwe.condExpr.get()); } case ASTKind::INC_OR_DEC_EXPR: { auto& ide = StaticCast(*node); CheckLetFlag(ide, *ide.expr); return CheckInitInExpr(ide.expr.get()); } case ASTKind::UNARY_EXPR: return CheckInitInExpr(StaticCast(node)->expr.get()); case ASTKind::BINARY_EXPR: return CheckInitInBinaryExpr(StaticCast(*node)); case ASTKind::RANGE_EXPR: { auto& re = StaticCast(*node); bool result = CheckInitInExpr(re.startExpr.get()); result = result && CheckInitInExpr(re.stopExpr.get()); result = result && CheckInitInExpr(re.stepExpr.get()); return result; } case ASTKind::SUBSCRIPT_EXPR: { auto& se = StaticCast(*node); bool result = CheckInitInExpr(se.baseExpr.get()); for (auto& expr : se.indexExprs) { result = result && CheckInitInExpr(expr.get()); } return result; } case ASTKind::CALL_EXPR: { auto& ce = StaticCast(*node); bool result = CheckInitInExpr(ce.baseFunc.get()); if (auto& args = ce.desugarArgs) { for (auto arg : *args) { result = result && CheckInitInExpr(arg->expr.get()); } } else { for (auto& arg : ce.args) { result = result && CheckInitInExpr(arg->expr.get()); } } return result; } case ASTKind::PAREN_EXPR: return CheckInitInExpr(StaticCast(node)->expr.get()); case ASTKind::LAMBDA_EXPR: CheckInitialization(StaticCast(node)->funcBody.get()); return true; case ASTKind::ARRAY_LIT: return std::all_of(StaticCast(node)->children.begin(), StaticCast(node)->children.end(), [this](auto& expr) { return CheckInitInExpr(expr.get()); }); case ASTKind::ARRAY_EXPR: { bool result = true; for (auto& it : StaticCast(node)->args) { result = CheckInitInExpr(it->expr.get()) && result; } return result; } case ASTKind::POINTER_EXPR: { auto& cptrExpr = StaticCast(*node); if (cptrExpr.arg) { return CheckInitInExpr(cptrExpr.arg->expr.get()); } return true; } case ASTKind::TUPLE_LIT: return std::all_of(StaticCast(node)->children.begin(), StaticCast(node)->children.end(), [this](auto& expr) { return CheckInitInExpr(expr.get()); }); case ASTKind::TYPE_CONV_EXPR: return CheckInitInExpr(StaticCast(node)->expr.get()); case ASTKind::IF_AVAILABLE_EXPR: { auto ie = StaticCast(node); bool res = true; res = CheckInitInExpr(ie->GetArg()) && res; res = CheckInitInExpr(ie->GetLambda1()) && res; res = CheckInitInExpr(ie->GetLambda2()) && res; return res; } case ASTKind::LIT_CONST_EXPR: { auto& lce = StaticCast(*node); if (!lce.siExpr) { return true; } bool result = true; for (auto& spe : lce.siExpr->strPartExprs) { if (auto ie = DynamicCast(spe.get())) { result = result && CheckInitInExpr(ie->block.get()); } } return result; } case ASTKind::THROW_EXPR: { auto ret = CheckInitInExpr(StaticCast(node)->expr.get()); UpdateScopeStatus(*node); return ret; } case ASTKind::JUMP_EXPR: UpdateScopeStatus(*node); return true; case ASTKind::PERFORM_EXPR: { auto& pe = StaticCast(*node); return CheckInitInExpr(pe.expr); } case ASTKind::RESUME_EXPR: { auto& re = StaticCast(*node); bool result = true; if (re.withExpr) { result = result && CheckInitInExpr(re.withExpr); } if (re.throwingExpr) { result = result && CheckInitInExpr(re.throwingExpr); } return result; } case ASTKind::LET_PATTERN_DESTRUCTOR: { auto& lpd = StaticCast(*node); return std::all_of(lpd.patterns.cbegin(), lpd.patterns.cend(), [this](const OwnedPtr& p) { return CheckInitInExpr(p.get()); }) && CheckInitInExpr(lpd.initializer.get()); } case ASTKind::VAR_PATTERN: StaticCast(node)->varDecl->EnableAttr(Attribute::INITIALIZED); return true; case ASTKind::TUPLE_PATTERN: { bool result = true; for (auto& pattern : StaticCast(node)->patterns) { result = result && CheckInitInExpr(pattern.get()); } return result; } case ASTKind::ENUM_PATTERN: { bool result = true; for (auto& pattern : StaticCast(node)->patterns) { result = result && CheckInitInExpr(pattern.get()); } return result; } case ASTKind::VAR_OR_ENUM_PATTERN: return CheckInitInExpr(StaticCast(node)->pattern.get()); case ASTKind::TYPE_PATTERN: return CheckInitInExpr(StaticCast(node)->pattern.get()); case ASTKind::EXCEPT_TYPE_PATTERN: return CheckInitInExpr(StaticCast(node)->pattern.get()); case ASTKind::BLOCK: CheckInitialization(node); return true; case ASTKind::SPAWN_EXPR: { bool result = CheckInitInExpr(StaticCast(node)->task.get()); result = CheckInitInExpr(StaticCast(node)->arg.get()) && result; return result; } case ASTKind::SYNCHRONIZED_EXPR: { bool result = CheckInitInExpr(StaticCast(node)->mutex.get()); result = CheckInitInExpr(StaticCast(node)->body.get()) && result; return result; } case ASTKind::IS_EXPR: return CheckInitInExpr(StaticCast(node)->leftExpr.get()); case ASTKind::AS_EXPR: return CheckInitInExpr(StaticCast(node)->leftExpr.get()); default: return true; } } bool InitializationChecker::CheckInitInWhileExpr(const WhileExpr& we) { bool result = CheckInitInCondition(*we.condExpr); CJC_NULLPTR_CHECK(we.condExpr); CheckInitInLoop(we.body.get()); return result; } bool InitializationChecker::CheckInitInForInExpr(const ForInExpr& fie) { bool result = CheckInitInExpr(fie.pattern.get()); result = result && CheckInitInExpr(fie.inExpression.get()); result = result && CheckInitInExpr(fie.patternGuard.get()); CheckInitInLoop(fie.body.get()); return result; } bool InitializationChecker::CheckInitInTryExpr(const TryExpr& te) { bool result = true; if (!te.resourceSpec.empty()) { for (auto& re : te.resourceSpec) { CheckInitialization(re.get()); } } ++tryDepth; if (te.tryLambda) { CheckInitialization(te.tryLambda.get()); } else { CheckInitialization(te.tryBlock.get()); } --tryDepth; if (!te.catchBlocks.empty()) { for (auto& catchPattern : te.catchPatterns) { result = result && CheckInitInExpr(catchPattern.get()); } for (auto& catchBlock : te.catchBlocks) { CheckInitialization(catchBlock.get()); } } if (!te.handlers.empty()) { // If there are handlers, we need to check them for (const auto& handler : te.handlers) { result = result && CheckInitInExpr(handler.commandPattern.get()); CheckInitialization(handler.desugaredLambda.get()); } } if (te.finallyBlock) { CheckInitialization(te.finallyBlock.get()); } return result; } namespace { /** * Check whether there is a control-transfer expression (throw, return, break and continue) in the @p node * @return Return true when there is a control-transfer expression in @p node */ bool MetControlTransferExpr(const Node& node) { if (Utils::In(node.astKind, {ASTKind::RETURN_EXPR, ASTKind::JUMP_EXPR, ASTKind::THROW_EXPR})) { return true; } else if (node.astKind == ASTKind::BLOCK) { auto& block = StaticCast(node); return std::any_of( block.body.begin(), block.body.end(), [](auto& n) { return n && MetControlTransferExpr(*n); }); } else if (auto ae = DynamicCast(&node); ae && ae->rightExpr) { return MetControlTransferExpr(*ae->rightExpr); } else if (auto dwe = DynamicCast(&node); dwe && dwe->body) { return MetControlTransferExpr(*dwe->body); } else { return false; } } class GetPatternVarsImpl { std::vector res; void Visit(const LetPatternDestructor& let) { for (auto& p : let.patterns) { Visit(*p); } } void Visit(const Expr& expr) { if (auto let = DynamicCast(&expr)) { for (auto& p : let->patterns) { Visit(*p); } } if (auto bin = DynamicCast(&expr); bin && IsCondition(*bin) && bin->op == TokenKind::AND) { Visit(*bin->leftExpr); Visit(*bin->rightExpr); } if (auto paren = DynamicCast(&expr)) { Visit(*paren->expr); } } void Visit(const Pattern& p) { if (auto tuple = DynamicCast(&p)) { return Visit(*tuple); } if (auto enm = DynamicCast(&p)) { return Visit(*enm); } if (auto var = DynamicCast(&p)) { if (var->pattern) { return Visit(*var->pattern); } } if (auto var = DynamicCast(&p)) { if (var->varDecl) { res.push_back(var->varDecl.get()); } } if (auto ty = DynamicCast(&p)) { if (ty->desugarVarPattern) { return Visit(*ty->desugarVarPattern); } return Visit(*ty->pattern); } } void Visit(const TuplePattern& p) { for (auto& v : p.patterns) { Visit(*v); } } void Visit(const EnumPattern& p) { for (auto& v : p.patterns) { Visit(*v); } } public: std::vector GetDefinedVars(const Expr& e) && { Visit(e); return std::move(res); } }; } // namespace std::unordered_set> InitializationChecker::CheckAndGetConditionalInitDecls( Expr& expr, const std::unordered_set>& uninitsDecls) { CheckInitInCondition(expr); auto inited = std::set{}; for (auto var : GetPatternVarsImpl{}.GetDefinedVars(expr)) { inited.insert(var); } std::unordered_set> conditionalInitialized; GetInitsInExpr(&expr, conditionalInitialized); // Reset status as before. for (auto decl : uninitsDecls) { if (conditionalInitialized.find(decl) != conditionalInitialized.end() && inited.count(decl) == 0) { decl->DisableAttr(Attribute::INITIALIZED); } } return conditionalInitialized; } void InitializationChecker::CheckInitInCondBlock(Expr& expr, const std::unordered_set>& uninitsDecls, std::unordered_set>& commonInitedDeclsOfBranches, bool& firstInitBranchInited) { std::unordered_set> initedInIfBlock = CheckAndGetConditionalInitDecls(expr, uninitsDecls); // If the checked scope is terminated by controflow expression, // the update of common inited decls should be ignored for strict checking. if (MetControlTransferExpr(expr)) { return; } if (!firstInitBranchInited) { commonInitedDeclsOfBranches = initedInIfBlock; firstInitBranchInited = true; } else { // calculate the intersection std::unordered_set> tmpInitedInIfBlock; for (auto decl : initedInIfBlock) { if (commonInitedDeclsOfBranches.find(decl) != commonInitedDeclsOfBranches.end()) { tmpInitedInIfBlock.insert(decl); } } commonInitedDeclsOfBranches = tmpInitedInIfBlock; } } namespace { inline void UpdateInitCondition( const std::unordered_set>& commonInitedDecls, const std::unordered_set>& uninitsDecls) { for (auto decl : commonInitedDecls) { if (uninitsDecls.find(decl) != uninitsDecls.end()) { decl->EnableAttr(Attribute::INITIALIZED); } } } std::unordered_set> GetReferencedDeclsBeforeJumpExpr(const Block& b) { std::unordered_set> decls; ConstWalker(&b, [&decls](auto n) { auto target = n->GetTarget(); if (Is(n) || Is(n) || Is(n) || Is(n)) { return VisitAction::SKIP_CHILDREN; } else if (Is(n)) { return VisitAction::STOP_NOW; } else if (auto expr = DynamicCast(n); expr && expr->IsReferenceExpr() && target) { decls.insert(target); } return VisitAction::WALK_CHILDREN; }).Walk(); return decls; } } // namespace bool InitializationChecker::CheckInitInBinaryExpr(const BinaryExpr& be) { if (be.desugarExpr) { return CheckInitInExpr(be.desugarExpr.get()); } if (be.op != TokenKind::AND && be.op != TokenKind::OR && be.op != TokenKind::COALESCING) { return CheckInitInExpr(be.leftExpr.get()) && CheckInitInExpr(be.rightExpr.get()); } std::unordered_set> leftUninitsDecls; GetUnInitsInExpr(be.leftExpr, leftUninitsDecls); std::unordered_set> rightUninitsDecls; GetUnInitsInExpr(be.rightExpr, rightUninitsDecls); auto res = CheckInitInExpr(be.leftExpr.get()); ++optionalCtxDepth; res = res && CheckInitInExpr(be.rightExpr.get()); --optionalCtxDepth; // Because of short-circuit, variables in rightExpr is not guaranteed to be initialized. for (auto decl : rightUninitsDecls) { if (!Utils::In(decl, leftUninitsDecls)) { decl->DisableAttr(Attribute::INITIALIZED); } } return res; } bool InitializationChecker::CheckInitInCondition(Expr& e) { if (!IsCondition(e)) { return CheckInitInExpr(&e); } if (auto let = DynamicCast(&e)) { auto res = CheckInitInExpr(let->initializer); for (auto var : GetPatternVarsImpl{}.GetDefinedVars(*let)) { var->EnableAttr(Attribute::INITIALIZED); } return res; } if (auto bin = DynamicCast(&e)) { if (bin->op == TokenKind::AND) { bool res = CheckInitInCondition(*bin->leftExpr); res = CheckInitInCondition(*bin->rightExpr) && res; for (auto var : GetPatternVarsImpl{}.GetDefinedVars(*bin)) { var->EnableAttr(Attribute::INITIALIZED); } return res; } } if (auto paren = DynamicCast(&e)) { return CheckInitInCondition(*paren->expr); } return CheckInitInExpr(&e); } bool InitializationChecker::CheckInitInMatchExpr(MatchExpr& me) { bool result = CheckInitInExpr(me.selector.get()); // get all uninitialized decls in the match case std::unordered_set> uninitsDecls; GetUnInitsInExpr(&me, uninitsDecls); std::unordered_set> commonInitedDeclsOfBranches; bool firstInitBranchInited{false}; for (auto& mc : me.matchCases) { if (mc == nullptr || mc->TestAttr(Attribute::UNREACHABLE)) { continue; } for (auto& pattern : mc->patterns) { result = result && CheckInitInExpr(pattern.get()); } result = result && CheckInitInExpr(mc->patternGuard.get()); if (result && mc->exprOrDecls) { CheckInitInCondBlock(*mc->exprOrDecls, uninitsDecls, commonInitedDeclsOfBranches, firstInitBranchInited); } } for (auto& mco : me.matchCaseOthers) { if (mco == nullptr || mco->TestAttr(Attribute::UNREACHABLE)) { continue; } result = result && CheckInitInExpr(mco->matchExpr.get()); if (result && mco->exprOrDecls) { CheckInitInCondBlock(*mco->exprOrDecls, uninitsDecls, commonInitedDeclsOfBranches, firstInitBranchInited); } } UpdateInitCondition(commonInitedDeclsOfBranches, uninitsDecls); return result; } bool InitializationChecker::CheckInitInIfExpr(IfExpr& ie) { // The first if condition must be evaluated. bool result = CheckInitInCondition(*ie.condExpr); std::unordered_set> uninitsDecls; GetUnInitsInExpr(&ie, uninitsDecls); bool firstInitBranchInited{false}; std::unordered_set> commonInitedDeclsOfBranches; Ptr visitingIe = &ie; auto curUninits = uninitsDecls; while (visitingIe->thenBody != nullptr) { CJC_NULLPTR_CHECK(visitingIe->condExpr); if (visitingIe != &ie) { auto initedInCondition = CheckAndGetConditionalInitDecls(*visitingIe->condExpr, curUninits); Utils::EraseIf(curUninits, [&initedInCondition](auto it) { return initedInCondition.count(it) != 0; }); } CheckInitInCondBlock(*visitingIe->thenBody, curUninits, commonInitedDeclsOfBranches, firstInitBranchInited); if (!visitingIe->hasElse) { commonInitedDeclsOfBranches.clear(); break; } // Get the next else body if (auto eif = DynamicCast(visitingIe->elseBody.get()); eif) { visitingIe = eif; } else { // last else block without condition if (auto elseBlock = DynamicCast(visitingIe->elseBody.get()); elseBlock) { CheckInitInCondBlock(*elseBlock, curUninits, commonInitedDeclsOfBranches, firstInitBranchInited); } break; } } UpdateInitCondition(commonInitedDeclsOfBranches, uninitsDecls); return result; } void InitializationChecker::CheckInitInLoop(Ptr block, bool shouldUnset) { if (!block) { return; } std::unordered_set> uninitedDecls; for (auto& n : block->body) { GetUnInitsInExpr(n.get(), uninitedDecls); } std::unordered_set> uninitedLocalDecls; GetLocalUnInitsInExpr(block, uninitedLocalDecls); CheckInitialization(block); std::unordered_set> initedDecls; for (auto& n : block->body) { GetInitsInExpr(n.get(), initedDecls); } if (shouldUnset) { for (auto decl : uninitedDecls) { if (initedDecls.count(decl) != 0) { decl->DisableAttr(Attribute::INITIALIZED); } } } else { auto declsBeforeJump = GetReferencedDeclsBeforeJumpExpr(*block); for (auto decl : uninitedDecls) { if (declsBeforeJump.count(decl) == 0) { decl->DisableAttr(Attribute::INITIALIZED); } } } } bool InitializationChecker::CheckIllegalMemberAccess(const Expr& expr, const Decl& target, const Node& targetStruct) { // This check for the rule that before all member variables have been initialized: // 1. 'this' cannot be captured by nested function or lambda; // 2. member function or property is not allowed to be used. CJC_NULLPTR_CHECK(expr.curFile); auto curFuncBody = GetCurFuncBody(ctx, expr.scopeName); if (curFuncBody == nullptr) { if (IsFuncOrProp(target)) { // Member function or property is not allowed to be used inside other member variable's initializer. diag.Diagnose(expr, DiagKind::sema_illegal_usage_of_member, target.identifier.Val()); return false; } return true; } auto outerMostFuncSym = ScopeManager::GetOutMostSymbol(ctx, SymbolKind::FUNC, expr.scopeName); auto outerMostFunc = outerMostFuncSym ? StaticCast(outerMostFuncSym->node) : nullptr; bool notInMemberFunc = !outerMostFunc || !outerMostFunc->outerDecl || !outerMostFunc->outerDecl->IsNominalDecl(); if (notInMemberFunc) { // Member is captured by lambda or function which defined inside other member variable's initializer. diag.Diagnose(expr, DiagKind::sema_illegal_usage_of_member, target.identifier.Val()); return false; } bool leftVaraible = expr.TestAttr(Attribute::LEFT_VALUE) && target.astKind == ASTKind::VAR_DECL; if (IsAllInitialized(targetStruct) || leftVaraible) { return true; } // Not all member variables have been initialized, check for forbidden usages. // Since ''symOfExprCurFunc' not null, outer most func must exist. if (!IsInstanceConstructor(*outerMostFunc)) { // When using member in normal instance function/property, // the object must be created that all members should be initialized. return true; } // 1. member function or property is not allowed to be used. // 2. when current function is not the outer most function, it indicates 'this' has been captured. if (IsFuncOrProp(target) || curFuncBody->funcDecl != outerMostFunc) { diag.Diagnose(expr, DiagKind::sema_illegal_usage_of_member, target.identifier.Val()); return false; } return true; } bool InitializationChecker::CheckIllegalRefExprAccess( const RefExpr& re, const Symbol& toplevelSymOfRe, const Symbol& toplevelSymOfTarget) { CJC_NULLPTR_CHECK(toplevelSymOfRe.node); CJC_NULLPTR_CHECK(toplevelSymOfTarget.node); if (&toplevelSymOfRe == &toplevelSymOfTarget) { // Check current class/struct member access CJC_NULLPTR_CHECK(re.ref.target); return CheckIllegalMemberAccess(re, *re.ref.target, *toplevelSymOfRe.node); } // Check super class/interface member access Symbol* symOfExprFunc = ScopeManager::GetOutMostSymbol(ctx, SymbolKind::FUNC_LIKE, re.scopeName); if (re.ref.target != nullptr && re.ref.target->astKind == ASTKind::VAR_DECL) { if (symOfExprFunc == nullptr || symOfExprFunc->astKind != AST::ASTKind::FUNC_DECL || (symOfExprFunc->astKind == AST::ASTKind::FUNC_DECL && !Is(StaticAs(symOfExprFunc->node)->outerDecl))) { diag.Diagnose(re, DiagKind::sema_illegal_usage_of_super_member, re.ref.identifier.Val()); return false; } } else if (re.ref.target && IsFuncOrProp(*re.ref.target) && !CheckIllegalMemberAccess(re, *re.ref.target, *toplevelSymOfRe.node)) { return false; } return true; } void InitializationChecker::CheckInitInTypeDecl( const Decl& inheritDecl, const std::vector>& superClassNonFuncDecls) { CollectDeclsInfo info = CollectDecls(inheritDecl); // 1. non funcs for (auto nfd : info.nonFuncDecls) { CheckInitialization(nfd); } // get the not declared-initialized member variables std::vector> unInitNonFuncDecls; for (auto decl : info.nonFuncDecls) { if (auto vd = DynamicCast(decl); vd && IsInitializedButUnReachable(*vd)) { unInitNonFuncDecls.emplace_back(decl); } else if (!decl->TestAttr(Attribute::INITIALIZED)) { unInitNonFuncDecls.emplace_back(decl); // class.uninits decl->EnableAttr(Attribute::INITIALIZED); } } // get the not declared-initialized member variables in super class for (auto decl : superClassNonFuncDecls) { if (!decl->TestAttr(Attribute::INITIALIZED)) { unInitNonFuncDecls.emplace_back(decl); decl->EnableAttr(Attribute::INITIALIZED); } } // 2. static functions (include static properties) for (auto sfd : info.staticFuncDecls) { CheckInitialization(sfd); } // 3. constructors for (auto fd : info.initFuncDecls) { if (fd == nullptr || fd->TestAttr(Attribute::INITIALIZATION_CHECKED) || (fd->TestAttr(Attribute::COMMON) && !fd->TestAttr(Attribute::COMMON_WITH_DEFAULT))) { continue; } CheckInitInConstructors(*fd, unInitNonFuncDecls); ctorUninitVarsMap.erase(fd); } // 4. non-static functions (include no-static properties) for (auto fd : info.funcDecls) { CheckInitialization(fd); } for (auto annoArray : info.annotations) { CheckInitInExpr(annoArray); } } void InitializationChecker::CheckInitInConstructors(FuncDecl& fd, const std::vector>& unInitNonFuncDecls) { if (fd.constructorCall != ConstructorCall::OTHER_INIT) { std::for_each(unInitNonFuncDecls.begin(), unInitNonFuncDecls.end(), [](Ptr decl) { decl->DisableAttr(Attribute::INITIALIZED); }); } CheckInitialization(&fd); if (fd.funcBody == nullptr || opts.compileCjd) { return; // If the function is broken, we can quit now. } if (fd.constructorCall == ConstructorCall::OTHER_INIT || (fd.funcBody->parentClassLike && (fd.funcBody->parentClassLike->TestAttr(Attribute::FOREIGN) || HasJavaAttr(*fd.funcBody->parentClassLike)))) { for (auto decl : unInitNonFuncDecls) { decl->EnableAttr(Attribute::INITIALIZED); } return; } if (auto classDecl = As(fd.funcBody->parentClassLike)) { if (auto superClass = classDecl->GetSuperClassDecl(); superClass && superClass->TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE)) { for (auto decl : unInitNonFuncDecls) { if (decl->astKind != ASTKind::VAR_DECL || !decl->TestAttr(Attribute::COMPILER_ADD)) { continue; } if (decl->identifier != Interop::Java::JAVA_REF_FIELD_NAME) { continue; } /* Only `javaref` field in mirrors and impls initialization has to be skipped. This field is initialized in JavaInterop desugar stage */ decl->EnableAttr(Attribute::INITIALIZED); } } } // If the constructor is terminated by 'ThrowExpr' directly, // the initialization check can be ignored, since the object will not be created. auto found = scopeTerminationKinds.find(fd.funcBody->scopeName); if (found != scopeTerminationKinds.end() && found->second == ASTKind::THROW_EXPR) { return; } auto definitelyUninitVars = ctorUninitVarsMap[&fd]; for (auto decl : unInitNonFuncDecls) { // Skip report error when the decl is common member for CJMP. if (decl->TestAnyAttr(Attribute::INITIALIZED, Attribute::COMMON) && definitelyUninitVars.count(decl) == 0) { continue; } if (fd.TestAttr(Attribute::COMPILER_ADD, Attribute::PRIMARY_CONSTRUCTOR)) { diag.Diagnose(fd, DiagKind::sema_class_uninitialized_field, decl->identifier.Val()); } else { diag.Diagnose(fd.TestAttr(Attribute::COMPILER_ADD) ? *decl : fd, DiagKind::sema_class_uninitialized_field, decl->identifier.Val()); } } } void InitializationChecker::CollectToDeclsInfo(const OwnedPtr& decl, CollectDeclsInfo& info) { if (decl->astKind == AST::ASTKind::PRIMARY_CTOR_DECL || decl->astKind == AST::ASTKind::MACRO_EXPAND_DECL) { return; } if (auto fd = DynamicCast(decl.get()); fd) { fd->EnableAttr(Attribute::INITIALIZED); if (fd->TestAttr(Attribute::STATIC)) { info.staticFuncDecls.emplace_back(fd); } else { if (fd->TestAttr(Attribute::CONSTRUCTOR)) { info.initFuncDecls.emplace_back(fd); } else { info.funcDecls.emplace_back(fd); } } } else if (auto pd = DynamicCast(decl.get()); pd) { pd->EnableAttr(Attribute::INITIALIZED); CollectDeclsFromPropDecl(decl, *pd, info.funcDecls, info.staticFuncDecls); } else { if (decl->TestAttr(Attribute::STATIC)) { CheckInitialization(decl.get()); } else { // Either invalid declaration or instance variable, that will be collected separately CJC_ASSERT(decl->TestAttr(AST::Attribute::IS_BROKEN) || decl->TestAttr(AST::Attribute::HAS_BROKEN) || decl->astKind == ASTKind::VAR_WITH_PATTERN_DECL || (decl->astKind == ASTKind::VAR_DECL && !decl->IsStaticOrGlobal())); } } if (decl->annotationsArray) { info.annotations.push_back(decl->annotationsArray); } } CollectDeclsInfo InitializationChecker::CollectDecls(const Decl& decl) { CollectDeclsInfo info; if (decl.annotationsArray) { info.annotations.push_back(decl.annotationsArray); } if (auto enumDecl = DynamicCast(&decl)) { for (auto& ctor: enumDecl->constructors) { if (ctor->annotationsArray) { info.annotations.push_back(ctor->annotationsArray); } } } for (auto& member : decl.GetMemberDecls()) { CollectToDeclsInfo(member, info); } auto initOrder = GetVarsInitializationOrderWithPositions(decl); for (auto& member : initOrder) { info.nonFuncDecls.emplace_back(member.decl); } return info; } void InitializationChecker::GetNonFuncDeclsInSuperClass( const ClassDecl& cd, std::vector>& superClassNonFuncDecls, std::set>& superClasses) { if (cd.body) { for (auto& decl : cd.body->decls) { if (auto vd = DynamicCast(decl.get()); vd && !vd->TestAttr(Attribute::PRIVATE) && vd->astKind != ASTKind::PROP_DECL) { superClassNonFuncDecls.emplace_back(decl.get()); } } } for (auto& it : cd.inheritedTypes) { if (auto superClass = DynamicCast(it->GetTarget()); superClass) { if (superClasses.find(superClass) != superClasses.end()) { superClasses.insert(superClass); GetNonFuncDeclsInSuperClass(*superClass, superClassNonFuncDecls, superClasses); } else { return; } } } } bool InitializationChecker::IsVarUsedBeforeDefinition(const Node& checkNode, Node& targetNode) const { bool validNodes = checkNode.begin != INVALID_POSITION && targetNode.begin != INVALID_POSITION && checkNode.curFile && targetNode.curFile; if (validNodes) { auto isSameFile = checkNode.astKind != ASTKind::FILE && targetNode.astKind == ASTKind::VAR_DECL && targetNode.curFile->fileHash == checkNode.curFile->fileHash; if (!isSameFile) { return false; } if (checkNode.IsStaticOrGlobal() && targetNode.IsStaticOrGlobal() && checkNode.begin < targetNode.begin) { return true; } if (!IsNode1ScopeVisibleForNode2(targetNode, checkNode)) { return false; } auto isConflictInSameScope = checkNode.scopeLevel == targetNode.scopeLevel && checkNode.begin < targetNode.begin; if (isConflictInSameScope) { return true; } auto isInDiffScope = checkNode.scopeLevel > targetNode.scopeLevel && !IsInStructDecl(*StaticAs(&targetNode)); if (!isInDiffScope) { return false; } auto outerScope = ScopeManager::GetCurOuterDeclOfScopeLevelX(ctx, checkNode, targetNode.scopeLevel); auto outerTry = outerScope ? As(outerScope->node) : nullptr; auto isCompilerAddTryExpr = outerTry && outerTry->isDesugaredFromTryWithResources; // See CreateInnerFinallyBlock in the desugar.cpp for the reasons. if (outerScope && outerScope->node->astKind == ASTKind::BLOCK) { auto b = StaticAs(outerScope->node); if (b->body.size() > 1) { auto innerTry = As(b->body.at(0).get()); isCompilerAddTryExpr = isCompilerAddTryExpr || (innerTry && innerTry->isDesugaredFromTryWithResources); } } auto isConflictInDiffScope = outerScope && outerScope->node->begin < targetNode.begin; return isConflictInDiffScope && !isCompilerAddTryExpr; } return false; } bool InitializationChecker::IsOrderRelated(const Node& checkNode, Node& targetNode, bool isClassLikeOrStruct) const { if (IsVarUsedBeforeDefinition(checkNode, targetNode)) { return true; } // Toplevel(except for var to var) or static decls are not order related. return !(targetNode.scopeLevel == 0 || isClassLikeOrStruct); } void InitializationChecker::RecordInstanceVariableUsage(const AST::Decl& target) { if (!currentInitializingVarDependencies) { return; } auto varDecl = DynamicCast(&target); if (!varDecl || !IsInstanceMember(*varDecl)) { return; } currentInitializingVarDependencies->emplace(Ptr(&target)); } cangjie_compiler-1.0.7/src/Sema/LegalityOfUsage/InitializationChecker.h000066400000000000000000000210461510705540100261370ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares a class to check initilization of variables. */ #ifndef CANGJIE_SEMA_LEGALITY_OF_USAGE_INITIALIZATION_CHECKER_H #define CANGJIE_SEMA_LEGALITY_OF_USAGE_INITIALIZATION_CHECKER_H #include "TypeCheckerImpl.h" #include #include #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Node.h" #include "cangjie/Frontend/CompilerInstance.h" namespace Cangjie { class InitializationChecker { public: static void Check(CompilerInstance& compilerInstance, const ASTContext& ctx, Ptr n) { InitializationChecker checker(compilerInstance, ctx); checker.CheckInitialization(n); } private: explicit InitializationChecker(CompilerInstance& ci, const ASTContext& ctx) : ctx(ctx), diag(ci.diag), opts{ci.invocation.globalOptions} { } void CheckInitialization(Ptr n); /** * Check whether @p ref has let flag. if @p ref has let flag and is in a constructor, report error. * * @param ref The expression should be checked. */ void CheckLetFlag(const AST::Expr& ae, const AST::Expr& expr); /** * Check whether the refExpr used in expr is initialized. * @return Return true if the expr is initialized. */ bool CheckInitInExpr(Ptr node); bool CheckInitInRefExpr(const AST::RefExpr& re); bool CheckInitInMemberAccess(AST::MemberAccess& ma); bool CheckInitInAssignExpr(const AST::AssignExpr& ae); bool CheckInitInTryExpr(const AST::TryExpr& te); bool CheckInitInBinaryExpr(const AST::BinaryExpr& be); bool CheckInitInCondition(AST::Expr& e); bool CheckInitInForInExpr(const AST::ForInExpr& fie); bool CheckInitInWhileExpr(const AST::WhileExpr& we); void CheckInitInVarDecl(AST::VarDecl& vd); void CheckInitInVarWithPatternDecl(AST::VarWithPatternDecl& vpd); void CheckInitInFuncBody(const AST::FuncBody& fb); void CheckInitInExtendDecl(const AST::ExtendDecl& ed); void CheckInitInClassDecl(const AST::ClassDecl& cd); void CheckLetFlagInMemberAccess( const AST::Expr& ae, const AST::MemberAccess& ma, bool inInitFunction); CollectDeclsInfo CollectDecls(const AST::Decl& decl); void CollectToDeclsInfo(const OwnedPtr& decl, CollectDeclsInfo& info); void CheckInitInConstructors( AST::FuncDecl& fd, const std::vector>& unInitNonFuncDecls); void CheckStaticInitForTypeDecl(const AST::InheritableDecl& id); /** * Check the initialization status in a constructor * @param decls the decls in the body of a class or struct */ void CheckInitInTypeDecl(const AST::Decl& inheritDecl, const std::vector>& superClassNonFuncDecls = std::vector>()); void GetNonFuncDeclsInSuperClass(const AST::ClassDecl& cd, std::vector>& superClassNonFuncDecls, std::set>& superClasses); /** * Check the initialization status in the Loop, * and unset the initialization to the variables out of the loop if @p shouldUnset is set. * @param block The block of a loop */ void CheckInitInLoop(Ptr block, bool shouldUnset = true); /** * Check the initialization status of the match expression @p me , * an uninitialized variable can get initialized in the match expression, * only if it is initialized in each match-case block */ bool CheckInitInMatchExpr(AST::MatchExpr& me); /** * Check the initialization status of the if expression @p ie , * an uninitialized variable can get initialized in the 'if' expression, * only if the if expression has the else block and the variable is initialized in each match-case block. */ bool CheckInitInIfExpr(AST::IfExpr& ie); /** * Check the initialization status in the given conditional reached @p expr of the condition expr */ void CheckInitInCondBlock(AST::Expr& expr, const std::unordered_set>& uninitsDecls, std::unordered_set>& commonInitedDeclsOfBranches, bool& firstInitBranchInited); std::unordered_set> CheckAndGetConditionalInitDecls( AST::Expr& expr, const std::unordered_set>& uninitsDecls); bool IsOrderRelated(const AST::Node& checkNode, AST::Node& targetNode, bool isClassLikeOrStruct) const; bool IsVarUsedBeforeDefinition(const AST::Node& checkNode, AST::Node& targetNode) const; bool CheckIllegalMemberAccess(const AST::Expr& expr, const AST::Decl& target, const AST::Node& targetStruct); bool CheckIllegalRefExprAccess( const AST::RefExpr& re, const AST::Symbol& toplevelSymOfRe, const AST::Symbol& toplevelSymOfTarget); void UpdateScopeStatus(const AST::Node& node); void UpdateInitializationStatus(const AST::AssignExpr& assign, AST::Decl& decl); void ClearScopeStatus(const std::string& scopeName) { contextVariables.erase(scopeName); variablesBeforeTeminatedScope.erase(scopeName); initVarsAfterTerminator.erase(scopeName); } void CheckNonCommonVariablesInitInCommonDecl(const AST::InheritableDecl& id); void RecordInstanceVariableUsage(const AST::Decl& target); const ASTContext& ctx; DiagnosticEngine& diag; const GlobalOptions& opts; /** * Map of the variables defined in visible scopes. * eg: func foo() { * let x : Int64 // map: s0: x * if (condition) { * let y = 2 // map: s0: x; s1: y * } * var z = 1 // map: s0: x, z * } */ std::unordered_map>> contextVariables; /** * Map of varaibles declared before the specific specific is terminated by 'throw', 'return', 'break' or 'continue' * eg: func foo() { * let x : Int64 * if (condition) { * let y = 2 * return 1 <--- this line terminated the scope of if-then branch, the collected decls are 'x' and 'y'. * let a = 1 * return a * } * } */ std::unordered_map>> variablesBeforeTeminatedScope; /** * Map of constructor to uninitialized member variables when meeting return expression inside ctor. * eg: class A { * init(a: Int64, c: Bool) { * let b = 1 * if (c) { * return <-- map will be updated here: 'init(a: Int64, c: Bool)' -> 'x' * } * x = a * println(x) <-- should not report unitialized * } * public var x: Int64 * } */ std::unordered_map, std::unordered_set>> ctorUninitVarsMap; /** * Map of first terminating kind of the specific specific. * eg: func foo() { * throw Exception() <-- only record the kind of throw, ignore later kind 'return' * retrun 1 * } */ std::unordered_map scopeTerminationKinds; /** * Map of initiliazed variables after terminate expression in the specific scope. * eg: func foo() { * var a : Int64 // s0 * if (condition) { // s1 * throw Exception() <-- terminate expresson * a = 1 <-- map updated: 's1' -> 'a' * println(a) <-- privious initialization is reachable for current line, will not report error * } else { a = 2 } * println(a) <-- initialization in if-then body is unreachable, will report 'used before initialization' * } */ std::unordered_map>> initVarsAfterTerminator; /** * Holds a dependencies to other member variables when analyzing initializer of meber variable * eg: let a = b + 1 // { b } */ std::optional>> currentInitializingVarDependencies; /** * When current is in the context that may not run termination as normal, 'optionalCtxDepth' plus 1. * eg: at the right hand side of 'coalescing', 'and', 'or' expressions. */ size_t optionalCtxDepth{0}; /** When current is inside the tryBlock, 'tryDepth' plus 1. */ size_t tryDepth{0}; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Sema/LegalityOfUsage/LegalityOfUsage.cpp000066400000000000000000000273471510705540100252540ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements legality of usage checking after sema type completed. */ #include "TypeCheckerImpl.h" #include "Diags.h" #include "InitializationChecker.h" namespace Cangjie { using namespace AST; namespace { void SetFuncBodyCaptureKind(FuncBody& fb) { if (!fb.body) { return; } // Collect mutable variables declared in the function `fb`. std::unordered_set> mutVars; Walker(fb.body.get(), [&mutVars](Ptr n) { if (auto varDecl = DynamicCast(n); varDecl && varDecl->isVar) { mutVars.emplace(varDecl); } return VisitAction::WALK_CHILDREN; }).Walk(); auto visitor = [&fb, &mutVars](Ptr n) { if (n->astKind != ASTKind::CALL_EXPR) { return VisitAction::WALK_CHILDREN; } // In the function body `fb`, if it calls some function that has captured some mutable variables, // we store the called function's body in `targetFuncBody`. auto& ce = static_cast(*n); Ptr targetFuncBody = nullptr; if (auto refExpr = DynamicCast(ce.baseFunc.get()); refExpr) { if (auto target = DynamicCast(refExpr->ref.target); target && target->funcBody && target->funcBody->captureKind != CaptureKind::NO_CAPTURE) { targetFuncBody = target->funcBody.get(); } } else if (auto lambdaExpr = DynamicCast(ce.baseFunc.get()); lambdaExpr) { CJC_ASSERT(lambdaExpr->funcBody); // Parser guarantees. if (lambdaExpr->funcBody->captureKind != CaptureKind::NO_CAPTURE) { targetFuncBody = lambdaExpr->funcBody.get(); } } // If the called function captures mutable variables, we check whether the variables are defined inside `fb`. if (targetFuncBody) { std::copy_if(targetFuncBody->capturedVars.begin(), targetFuncBody->capturedVars.end(), std::inserter(fb.capturedVars, fb.capturedVars.begin()), [&mutVars]( Ptr varRe) { return mutVars.find(varRe->GetTarget()) == mutVars.cend(); }); if (!fb.capturedVars.empty()) { fb.captureKind = CaptureKind::TRANSITIVE_CAPTURE; return VisitAction::STOP_NOW; } } return VisitAction::WALK_CHILDREN; }; Walker walker(fb.body.get(), visitor); walker.Walk(); } } // namespace void TypeChecker::TypeCheckerImpl::CheckLegalityOfUsage(ASTContext& ctx, AST::Package& pkg) { // Check whether value type decl contains value type recursive dependency. CheckValueTypeRecursive(pkg); // Check legality of reference usage. CheckLegalityOfReference(ctx, pkg); CheckStaticMembersWithGeneric(pkg); CheckUsageOfDeprecated(pkg); // Check initialization. if (!ci->invocation.globalOptions.disableSemaVic) { InitializationChecker::Check(*ci, ctx, &pkg); } CheckGlobalVarInitialization(ctx, pkg); // CFunc must be called in an unsafe block. CheckLegalityOfUnsafeAndInout(pkg); // Check structure declaration inheritance. CheckInheritance(pkg); CheckClosures(ctx, pkg); CheckAccessLevelValidity(pkg); CheckAllInvocationHasImpl(ctx, pkg); CheckSubscriptLegality(pkg); } void TypeChecker::TypeCheckerImpl::CheckStaticMemberWithGeneric( Decl& member, const std::vector>& outerGenericTys) { // Static member variables, properties and init func can't depend on generic parameters of outer type. if (!member.TestAttr(Attribute::STATIC) || outerGenericTys.empty()) { return; } if (member.astKind != ASTKind::VAR_DECL && member.astKind != ASTKind::PROP_DECL && !IsStaticInitializer(member)) { return; } auto preVisitor = [this, outerGenericTys](Ptr node) { if (node == nullptr || (!Is(node) && !Is(node))) { return VisitAction::WALK_CHILDREN; } // Because static member var and prop cannot themselves declare generic parameters, static member var and // prop cannot contain any outside generic types. if (node->ty && node->ty->HasGeneric() && !node->begin.IsZero()) { std::set> targetTys; for (auto& usedTy : node->ty->GetGenericTyArgs()) { if (usedTy && Utils::In(usedTy, outerGenericTys)) { targetTys.emplace(usedTy); break; } } Sema::DiagForStaticVariableDependsGeneric(diag, *node, targetTys); return VisitAction::SKIP_CHILDREN; } // If the static var/let's initialization expression contains a static member function call of a generic class, // it's also not legal. // eg: class A { static func foo() {1}; static var a = foo(); } // 'static var a = foo()' will be same as 'static var a = A.foo()'. auto ref = node->astKind == ASTKind::REF_EXPR ? StaticCast(node)->ref : StaticCast(node)->ref; auto needDiag = ref.target && (ref.target->astKind == ASTKind::FUNC_DECL || ref.target->astKind == ASTKind::PROP_DECL) && ref.target->TestAttr(Attribute::STATIC) && ref.target->outerDecl && ref.target->outerDecl->ty && ref.target->outerDecl->ty->HasGeneric(); if (needDiag) { Sema::DiagForStaticVariableDependsGeneric(diag, *node, ref.target->outerDecl->ty->GetGenericTyArgs()); } return VisitAction::SKIP_CHILDREN; }; Walker(&member, preVisitor).Walk(); } void TypeChecker::TypeCheckerImpl::CheckStaticMembersWithGeneric(const Package& pkg) { IterateToplevelDecls(pkg, [this](auto& decl) { if (!decl || !decl->generic || decl->generic->typeParameters.empty()) { return; } for (auto& member : decl->GetMemberDecls()) { std::vector> outersideGenericParamTys; for (auto& tp : decl->generic->typeParameters) { CJC_ASSERT(tp && Ty::IsTyCorrect(tp->ty)); outersideGenericParamTys.emplace_back(tp->ty); } CheckStaticMemberWithGeneric(*member, outersideGenericParamTys); } }); } void TypeChecker::TypeCheckerImpl::CheckValueTypeRecursive(const Package& pkg) { IterateToplevelDecls(pkg, [this](auto& decl) { if (decl->astKind == ASTKind::ENUM_DECL || decl->astKind == ASTKind::STRUCT_DECL) { CheckValueTypeRecursiveDFS(decl.get()); } }); for (auto& instantiatedDecl : pkg.genericInstantiatedDecls) { if (instantiatedDecl->astKind == ASTKind::ENUM_DECL || instantiatedDecl->astKind == ASTKind::STRUCT_DECL) { CheckValueTypeRecursiveDFS(instantiatedDecl.get()); } } } void TypeChecker::TypeCheckerImpl::CheckClosures(const ASTContext& ctx, Node& node) const { // 1. mark all reference capture status. Walker(&node, [&ctx, this](auto node) { if (auto ma = DynamicCast(node); ma && ma->baseExpr && IsThisOrSuper(*ma->baseExpr)) { MarkAndCheckRefExprVarCaptureStatus(ctx, *ma); } else if (auto re = DynamicCast(node); re) { MarkAndCheckRefExprVarCaptureStatus(ctx, *re); } return VisitAction::WALK_CHILDREN; }).Walk(); // 2. set all funcBody capture status. Walker(&node, nullptr, [](auto node) { if (auto fb = DynamicCast(node); fb) { SetFuncBodyCaptureKind(*fb); } return VisitAction::WALK_CHILDREN; }).Walk(); // 3. diagnose for invalid capture. Walker(&node, [&ctx, this](Ptr n) { CheckLegalUseOfClosure(ctx, *n); return VisitAction::WALK_CHILDREN; }).Walk(); } void TypeChecker::TypeCheckerImpl::CheckSubscriptLegality(Node& node) { auto postVisit = [this](Ptr node) -> VisitAction { if (node->astKind != ASTKind::SUBSCRIPT_EXPR) { return VisitAction::WALK_CHILDREN; } auto se = StaticCast(node); // Checking the Validity of VArray Subscript Access if (!se->baseExpr || !Ty::IsTyCorrect(se->baseExpr->ty) || !Is(se->baseExpr->ty)) { return VisitAction::WALK_CHILDREN; } auto varrTy = StaticCast(se->baseExpr->ty); CJC_ASSERT(!se->indexExprs.empty()); if (se->indexExprs[0]->isConst) { auto index = se->indexExprs[0]->constNumValue.asInt; if (index.IsOutOfRange()) { return VisitAction::SKIP_CHILDREN; } if (index.Uint64() >= static_cast(varrTy->size) && se->ShouldDiagnose(true)) { auto builder = diag.Diagnose(*se, DiagKind::sema_builtin_index_in_bound, VARRAY_NAME); std::string hint; if (index.Int64() < 0) { hint = "'VArray' index can not be negative"; } else { hint = "'VArray' index " + index.GetValue() + " is past the end of 'VArray' (which contains " + std::to_string(varrTy->size) + " elements)"; } builder.AddHint(*se->indexExprs[0], hint); return VisitAction::SKIP_CHILDREN; } } return VisitAction::WALK_CHILDREN; }; Walker walker(&node, nullptr, postVisit); walker.Walk(); } void TypeChecker::TypeCheckerImpl::CheckAllInvocationHasImpl(const ASTContext& ctx, Node& node) { auto preVisit = [this, &ctx](Ptr node) -> VisitAction { if (node->astKind != ASTKind::MEMBER_ACCESS) { return VisitAction::WALK_CHILDREN; } auto& ma = *RawStaticCast(node); auto target = ma.GetTarget(); if (!target) { return VisitAction::WALK_CHILDREN; } bool isInterfaceAccess = ma.baseExpr && ma.baseExpr->ty && ma.baseExpr->ty->IsInterface(); bool isStaticFuncOrProp = target->TestAttr(Attribute::STATIC) && target->IsFuncOrProp() && target->outerDecl; if (isInterfaceAccess && isStaticFuncOrProp) { std::unordered_set> traversedDecls = {}; MultiTypeSubst typeMapping; if (ma.matchedParentTy && target->outerDecl->ty) { typeMapping = promotion.GetPromoteTypeMapping(*ma.matchedParentTy, *target->outerDecl->ty); } auto baseDecl = Ty::GetDeclPtrOfTy(ma.baseExpr->ty); MultiTypeSubst typeMapping1; if (baseDecl->ty) { typeMapping1 = promotion.GetPromoteTypeMapping(*ma.baseExpr->ty, *baseDecl->ty); } typeMapping.merge(typeMapping1); auto ret = CheckInvokeTargetHasImpl(ctx, *ma.baseExpr->ty, *target, typeMapping, traversedDecls); if (ret.first && ret.second) { auto retTarget = ret.second->GetTarget(); std::string strType = retTarget->IsFunc() ? "function" : "property"; std::string strNote = "indirect use of unimplemented " + strType + " reference here"; auto builder = diag.DiagnoseRefactor( DiagKindRefactor::sema_interface_call_with_unimplemented_call, ma, strType, retTarget->identifier); builder.AddNote(MakeRange(ret.second->begin, ret.second->end), strNote); return VisitAction::SKIP_CHILDREN; } } return VisitAction::WALK_CHILDREN; }; Walker walker(&node, preVisit); walker.Walk(); } } // namespace Cangjie cangjie_compiler-1.0.7/src/Sema/LocalTypeArgumentSynthesis.cpp000066400000000000000000001323051510705540100245040ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * Local type argument synthesis. */ #include "LocalTypeArgumentSynthesis.h" #include "TypeCheckerImpl.h" #include #include #include #include "JoinAndMeet.h" #include "Promotion.h" #include "TyVarConstraintGraph.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Node.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/AST/ASTCasting.h" using namespace Cangjie; using namespace AST; namespace { std::vector GetOrderedCheckingIndexes(const std::vector>& tys) { // Ordering index of types to make index of non-ideal types precedes the index of ideal types. // Synthesis non-ideal types first to restrict possible range of ideal type. // Since cj allows auto box option type, we should place option type before non-option type to allows // 'A'& 'Option' & 'Equatable' results in upper bound 'Equatable>'. std::vector ideals; std::vector options; std::vector others; for (size_t i = 0; i < tys.size(); ++i) { if (Ty::IsTyCorrect(tys[i])) { if (tys[i]->IsIdeal()) { (void)ideals.emplace_back(i); } else if (tys[i]->IsCoreOptionType()) { (void)options.emplace_back(i); } else { (void)others.emplace_back(i); } } else { (void)others.emplace_back(i); } } std::stable_sort(options.begin(), options.end(), [&tys](auto l, auto r) { return TypeCheckUtil::CountOptionNestedLevel(*tys[l]) > TypeCheckUtil::CountOptionNestedLevel(*tys[r]); }); options.insert(options.end(), others.begin(), others.end()); options.insert(options.end(), ideals.begin(), ideals.end()); return options; } } // namespace LocalTypeArgumentSynthesis::~LocalTypeArgumentSynthesis() { curTyVar = nullptr; } std::optional LocalTypeArgumentSynthesis::SynthesizeTypeArguments(bool allowPartial) { static std::mutex lock; std::lock_guard guard(lock); CJC_ASSERT(argPack.argTys.size() == argPack.argBlames.size()); CJC_ASSERT(argPack.argTys.size() == argPack.paramTys.size()); for (auto tv : argPack.tyVarsToSolve) { CJC_ASSERT(tv->isPlaceholder); } CopyUpperbound(); cms = ConstraintWithMemos{{InitConstraints(argPack.tyVarsToSolve), {}, false, false}}; std::vector orderedIndexes = GetOrderedCheckingIndexes(argPack.argTys); for (auto i : orderedIndexes) { if (!Ty::IsTyCorrect(argPack.argTys[i]) || !Ty::IsTyCorrect(argPack.paramTys[i])) { return {}; } if (needDiagMsg && errMsg.style == SolvingErrStyle::DEFAULT) { auto [tmpCms, tmpMsg] = Unify( cms, {*argPack.argTys[i], {argPack.argBlames[i]}}, {*argPack.paramTys[i], {argPack.argBlames[i]}}); cms = tmpCms; errMsg = tmpMsg; } else { cms = Unify(cms, {*argPack.argTys[i], {argPack.argBlames[i]}}, {*argPack.paramTys[i], {argPack.argBlames[i]}}) .first; } if (cms.empty()) { MaybeSetErrMsg(MakeMsgMismatchedArg(argPack.argBlames[i])); return {}; } } if (Ty::IsTyCorrect(argPack.funcRetTy) && argPack.funcRetTy->HasGeneric() && Ty::IsTyCorrect(argPack.retTyUB)) { // Only consider function's return type when the return type contains generic type. // Add a constraint that the function's return type should be smaller than the type required by the context. if (needDiagMsg && errMsg.style == SolvingErrStyle::DEFAULT) { auto [tmpCms, tmpMsg] = Unify(cms, {*argPack.funcRetTy, {argPack.retBlame}}, {*argPack.retTyUB, {argPack.retBlame}}); cms = tmpCms; errMsg = tmpMsg; } else { cms = Unify(cms, {*argPack.funcRetTy, {argPack.retBlame}}, {*argPack.retTyUB, {argPack.retBlame}}).first; } } if (cms.empty()) { MaybeSetErrMsg(MakeMsgMismatchedRet(argPack.retBlame)); return {}; } if (!allowPartial && !needDiagMsg) { Utils::EraseIf(cms, [this](const ConstraintWithMemo& cm) { return !DoesCSCoverAllTyVars(cm.constraint); }); } if (auto optSubst = SolveConstraints(allowPartial)) { auto subst = ResetIdealTypesInSubst(*optSubst); return {subst}; } else { return {}; } } // copy and instantiate generic upperbound from universal ty var to instance ty var void LocalTypeArgumentSynthesis::CopyUpperbound() { for (auto& [univ, inst] : tyMgr.GetInstMapping().u2i) { auto instTv = RawStaticCast(inst); if (!Utils::In(instTv, argPack.tyVarsToSolve)) { continue; } for (auto upper : univ->upperBounds) { CJC_NULLPTR_CHECK(upper); instTv->upperBounds.emplace(tyMgr.InstOf(upper)); if (gcBlames.count(univ) > 0 && gcBlames.at(univ).count(upper) > 0) { gcBlamesInst[instTv][tyMgr.InstOf(upper)] = gcBlames.at(univ).at(upper); } } } } Constraint LocalTypeArgumentSynthesis::InitConstraints(const TyVars& tyVarsToSolve) { Constraint res; for (auto& tyVar : tyVarsToSolve) { // Type variables must be of generic types by definition. if (tyVar == nullptr) { res = {}; break; } if (tyVar->IsGeneric()) { auto ubs = RawStaticCast(tyVar)->upperBounds; TyVarBounds bounds; for (auto ub : ubs) { bounds.ubs.insert(ub); for (auto node : gcBlamesInst[tyVar][ub]) { bounds.ub2Blames[ub].insert({.src = node, .style = BlameStyle::CONSTRAINT}); } } InsertConstraint(res, *tyVar, bounds); } } return res; } void LocalTypeArgumentSynthesis::InsertConstraint(Constraint& c, TyVar& tyVar, TyVarBounds& tvb) const { auto found = c.find(&tyVar); if (found == c.end()) { c.emplace(&tyVar, tvb); } else { TyVarBounds& old = found->second; for (auto lb : tvb.lbs) { old.lbs.insert(lb); auto& lb2Blames = tvb.lb2Blames[lb]; old.lb2Blames[lb].insert(lb2Blames.begin(), lb2Blames.end()); } for (auto ub : tvb.ubs) { old.ubs.insert(ub); auto& ub2Blames = tvb.ub2Blames[ub]; old.ub2Blames[ub].insert(ub2Blames.begin(), ub2Blames.end()); } } } std::pair LocalTypeArgumentSynthesis::Unify( const ConstraintWithMemos& newCMS, const Tracked& argTTy, const Tracked& paramTTy) { LocTyArgSynArgPack dummyArgPack = { argPack.tyVarsToSolve, {}, {}, {}, TypeManager::GetInvalidTy(), TypeManager::GetInvalidTy(), Blame()}; ConstraintWithMemos res; SolvingErrInfo msg; std::for_each(newCMS.cbegin(), newCMS.cend(), [this, &msg, &res, &argTTy, ¶mTTy, &dummyArgPack](auto& cm) { auto newSynIns = LocalTypeArgumentSynthesis(tyMgr, dummyArgPack, {}, needDiagMsg); newSynIns.cms = {cm}; newSynIns.deterministic = deterministic; if (newSynIns.UnifyOne(argTTy, paramTTy)) { // The result of correct unification will be kept. // If there are any errors during the unification, the result will not be recorded into the res variable. res.insert(res.end(), newSynIns.cms.begin(), newSynIns.cms.end()); } else if (msg.style == SolvingErrStyle::DEFAULT) { msg = newSynIns.errMsg; } }); return {res, msg}; } bool LocalTypeArgumentSynthesis::UnifyAndTrim( const ConstraintWithMemos& curCMS, const Tracked& argTTy, const Tracked& paramTTy) { auto [newCMS, msg] = Unify(curCMS, argTTy, paramTTy); MaybeSetErrMsg(msg); return VerifyAndSetCMS(newCMS); } bool LocalTypeArgumentSynthesis::VerifyAndSetCMS(const LocalTypeArgumentSynthesis::ConstraintWithMemos& newCMS) { if (!cms.empty() && newCMS.empty()) { cms = {}; return false; } else { cms = newCMS; return true; } } bool LocalTypeArgumentSynthesis::UnifyOne(const Tracked& argTTy, const Tracked& paramTTy) { auto& argTy = argTTy.ty; auto& paramTy = paramTTy.ty; // Handle the base case. if (cms.size() != 1) { return false; } if (&argTy == ¶mTy) { return true; } if (argTy.IsQuest() || paramTy.IsQuest()) { return true; } if (paramTy.IsIntersection()) { return UnifyParamIntersectionTy(argTTy, {static_cast(paramTy), paramTTy.blames}); } else if (argTy.IsIntersection()) { return UnifyArgIntersectionTy({static_cast(argTy), argTTy.blames}, paramTTy); } else if (argTy.IsUnion()) { return UnifyArgUnionTy({static_cast(argTy), argTTy.blames}, paramTTy); } else if (paramTy.IsUnion()) { return UnifyParamUnionTy(argTTy, {static_cast(paramTy), paramTTy.blames}); } MemoForUnifiedTys& memo = cms.front().memo; auto inProcessing = std::pair, Ptr>(&argTy, ¶mTy); if (Utils::In(inProcessing, memo)) { return true; } if ((paramTy.IsGeneric() && StaticCast(¶mTy)->isPlaceholder) || (argTy.IsGeneric() && StaticCast(&argTy)->isPlaceholder)) { memo.insert(inProcessing); return UnifyTyVar(argTTy, paramTTy); } // When the 'paramTy' has more option nesting level than the 'argTy': // If both the 'argTy' and the 'paramTy' are Option type, unify their typeArgs. // Otherwise If the 'paramTy' is Option type, unify the 'paramTy''s type argument with the 'argTy'. if (TypeCheckUtil::CountOptionNestedLevel(paramTy) > TypeCheckUtil::CountOptionNestedLevel(argTy)) { CJC_ASSERT(paramTy.typeArgs.size() == 1 && paramTy.typeArgs.front() != nullptr); if (argTy.IsCoreOptionType() && paramTy.IsCoreOptionType()) { memo.insert(inProcessing); CJC_ASSERT(argTy.typeArgs.size() == 1 && argTy.typeArgs.front() != nullptr); return UnifyOne({*argTy.typeArgs[0], argTTy.blames}, {*paramTy.typeArgs[0], paramTTy.blames}); } memo.insert(inProcessing); return UnifyOne(argTTy, {*paramTy.typeArgs[0], paramTTy.blames}); } // Context type variables are first promoted if (argTy.IsGeneric() || paramTy.IsGeneric()) { return UnifyContextTyVar(argTTy, paramTTy); } else if (paramTy.IsNominal() && argTy.IsNominal()) { return UnifyNominal(argTTy, paramTTy); } else if (argTy.IsBuiltin() && paramTy.IsInterface()) { return UnifyBuiltInExtension(argTTy, {static_cast(paramTy), paramTTy.blames}); } else if (paramTy.IsPrimitive() && argTy.IsPrimitive()) { return UnifyPrimitiveTy(static_cast(argTy), static_cast(paramTy)); } else if ((argTy.IsArray() && paramTy.IsArray()) || (argTy.IsPointer() && paramTy.IsPointer())) { return UnifyBuiltInTy(argTTy, paramTTy); } else if (argTy.IsFunc() && paramTy.IsFunc()) { return UnifyFuncTy( {static_cast(argTy), argTTy.blames}, {static_cast(paramTy), paramTTy.blames}); } else if (argTy.IsTuple() && paramTy.IsTuple()) { return UnifyTupleTy( {static_cast(argTy), argTTy.blames}, {static_cast(paramTy), paramTTy.blames}); } else { return tyMgr.IsSubtype(&argTy, ¶mTy); } } bool LocalTypeArgumentSynthesis::UnifyTyVar(const Tracked& argTTy, const Tracked& paramTTy) { CJC_ASSERT(cms.size() == 1); auto& argTy = argTTy.ty; auto& paramTy = paramTTy.ty; // If paramTy is the generic parameter to be solved. Ptr tyVar = nullptr; Ptr lb = TypeManager::GetInvalidTy(); Ptr ub = TypeManager::GetInvalidTy(); std::set lbBlames; std::set ubBlames; auto unifyBound = [&tyVar, this](Ptr& one, Ptr& other, const Tracked& bound, bool isUb) { one = &bound.ty; if (one->IsNothing()) { cms.front().hasNothingTy = true; } else if (one->IsAny()) { cms.front().hasAnyTy = true; } else if (auto ctt = DynamicCast(one)) { // For inferred type, the class this type should be substituted as original class type. one = tyMgr.GetClassTy(*ctt->declPtr, ctt->typeArgs); } if (deterministic && IsGreedySolution(*tyVar, *one, isUb)) { other = one; auto& eq = cms.front().constraint[tyVar].eq; if (eq.empty()) { eq.insert(one); } } }; if (auto genParam = DynamicCast(¶mTy); genParam && genParam->isPlaceholder) { // case T = X. tyVar = genParam; lbBlames = argTTy.blames; unifyBound(lb, ub, argTTy, false); } else if (auto genArg = DynamicCast(&argTy); genArg && genArg->isPlaceholder) { // case X = T. tyVar = genArg; ubBlames = paramTTy.blames; unifyBound(ub, lb, paramTTy, true); } else { return false; } this->curTyVar = tyVar; // Recursively constrain existing bounds with the newly added one. return UnifyTyVarCollectConstraints(*tyVar, {*lb, lbBlames}, {*ub, ubBlames}); } bool LocalTypeArgumentSynthesis::UnifyTyVarCollectConstraints( TyVar& tyVar, const Tracked& lbTTy, const Tracked& ubTTy) { if (cms.size() != 1) { return false; } auto& lb = lbTTy.ty; auto& ub = ubTTy.ty; Constraint& c = cms.front().constraint; if (Ty::IsTyCorrect(&lb)) { TyVarBounds bounds; bounds.lbs.insert(&lb); bounds.lb2Blames[&lb] = lbTTy.blames; InsertConstraint(c, tyVar, bounds); } if (Ty::IsTyCorrect(&ub)) { TyVarBounds bounds; bounds.ubs.insert(&ub); bounds.ub2Blames[&ub] = ubTTy.blames; InsertConstraint(c, tyVar, bounds); } if (Ty::IsTyCorrect(&lb)) { // the reference to `c` may be invalidated in the following loop; must copy here auto ubs = c[&tyVar].ubs; auto ub2Blames = c[&tyVar].ub2Blames; // lb and ub are initialized to INVALID_TY and thus are not null. // Elements in ubs are tested to be not null in UnifyTyVarCollectNewConstraints. // The join adds option box to lb if option-boxed lb already exists in the type var's lbs. Ptr lbTy{}; if (deterministic) { lbTy = &lb; } else { auto joinRes = JoinAndMeet(tyMgr, c[&tyVar].lbs, argPack.tyVarsToSolve).JoinAsVisibleTy(); if (JoinAndMeet::SetJoinedType(lbTy, joinRes).has_value() || lbTy->IsAny()) { lbTy = &lb; } } std::optional st; auto [tyL, tyR] = GetMaybeStableIters(ubs, st); for (auto ub0 = tyL; ub0 != tyR; ++ub0) { if (!UnifyAndTrim(cms, {*lbTy, lbTTy.blames}, {**ub0, ub2Blames[*ub0]})) { MaybeSetErrMsg(MakeMsgConflictingConstraints(tyVar, {lbTTy}, {{**ub0, ub2Blames[*ub0]}})); return false; } } // with known sum, but the sum doesn't include lb if (deterministic && !tyMgr.TyVarHasNoSum(tyVar)) { auto& sum = c[&tyVar].sum; if (!lb.IsNothing() && sum.count(&lb) == 0) { return false; } } } if (Ty::IsTyCorrect(&ub)) { // the reference to `c` may be invalidated in the following loop; must copy here auto lbs = c[&tyVar].lbs; auto lb2Blames = c[&tyVar].lb2Blames; std::optional st; auto [tyL, tyR] = GetMaybeStableIters(lbs, st); for (auto lb0 = tyL; lb0 != tyR; ++lb0) { if (!UnifyAndTrim(cms, {**lb0, lb2Blames[*lb0]}, ubTTy)) { MaybeSetErrMsg(MakeMsgConflictingConstraints(tyVar, {{**lb0, lb2Blames[*lb0]}}, {ubTTy})); return false; } } } // with known sum, but the sum doesn't include eq if (deterministic && !tyMgr.TyVarHasNoSum(tyVar)) { auto& sum = c[&tyVar].sum; auto& eq = c[&tyVar].eq; if (!eq.empty() && !(*eq.begin())->IsNothing() && sum.count(*eq.begin()) == 0) { return false; } } return true; } // Unify type variables not those to be solved. bool LocalTypeArgumentSynthesis::UnifyContextTyVar(const Tracked& argTTy, const Tracked& paramTTy) { auto& argTy = argTTy.ty; auto& paramTy = paramTTy.ty; if (&argTy == ¶mTy) { return true; } if (auto gTy = DynamicCast(&argTy); gTy && !gTy->isPlaceholder) { // Need to check argTy <: argUBound <: paramTy. // Hotfix, should be changed later. // Unify empty intersection ty as argument is same with unify with type of 'Any'. auto& ubs = gTy->upperBounds; if (ubs.empty()) { return UnifyOne({*tyMgr.GetAnyTy(), {}}, paramTTy); } auto ubTy = ubs.size() == 1 ? *ubs.begin() : tyMgr.GetIntersectionTy(ubs); return UnifyOne({*ubTy, argTTy.blames}, paramTTy); } if (paramTy.IsGeneric() && !StaticCast(¶mTy)->isPlaceholder) { // Need to check argTy <: paramLBound <: paramTy. However, currently T === Nothing return false; } return false; } bool LocalTypeArgumentSynthesis::UnifyFuncTy(const Tracked& argTTy, const Tracked& paramTTy) { auto& argTy = argTTy.ty; auto& paramTy = paramTTy.ty; if (argTy.paramTys.size() != paramTy.paramTys.size()) { return false; } for (size_t i = 0; i < paramTy.paramTys.size(); ++i) { if (!paramTy.paramTys[i] || !argTy.paramTys[i]) { return false; } if (!UnifyAndTrim(cms, {*paramTy.paramTys[i], paramTTy.blames}, {*argTy.paramTys[i], argTTy.blames})) { return false; } } if (!argTy.retTy || !paramTy.retTy) { return false; } if (!UnifyAndTrim(cms, {*argTy.retTy, argTTy.blames}, {*paramTy.retTy, paramTTy.blames})) { return false; } return true; } bool LocalTypeArgumentSynthesis::UnifyTupleTy(const Tracked& argTTy, const Tracked& paramTTy) { auto& argTy = argTTy.ty; auto& paramTy = paramTTy.ty; if (argTy.typeArgs.size() != paramTy.typeArgs.size()) { return false; } for (size_t i = 0; i < paramTy.typeArgs.size(); ++i) { if (!argTy.typeArgs[i] || !paramTy.typeArgs[i]) { return false; } if (!UnifyOne({*argTy.typeArgs[i], argTTy.blames}, {*paramTy.typeArgs[i], paramTTy.blames})) { return false; } } return true; } bool LocalTypeArgumentSynthesis::UnifyNominal(const Tracked& argTTy, const Tracked& paramTTy) { auto& argTy = argTTy.ty; auto& paramTy = paramTTy.ty; auto prTys = std::make_unique(tyMgr)->Promote(argTy, paramTy); if (prTys.empty()) { return false; } ConstraintWithMemos res; std::optional st; auto [tyL, tyR] = GetMaybeStableIters(prTys, st); for (auto ty = tyL; ty != tyR; ++ty) { auto prTy = *ty; if (!Ty::IsTyCorrect(prTy)) { continue; } if (!Ty::IsTyArgsSizeEqual(paramTy, *prTy)) { continue; } auto currentCms = cms; for (size_t i = 0; i < prTy->typeArgs.size(); ++i) { if (!prTy->typeArgs[i] || !paramTy.typeArgs[i]) { continue; } // For nominal types, I1 <: I2 iff A <: B and B <: A. if (needDiagMsg && errMsg.style == SolvingErrStyle::DEFAULT) { auto [tmpCms, tmpMsg] = Unify(currentCms, {*prTy->typeArgs[i], argTTy.blames}, {*paramTy.typeArgs[i], paramTTy.blames}); errMsg = tmpMsg; auto [tmpCms2, tmpMsg2] = Unify(tmpCms, {*paramTy.typeArgs[i], paramTTy.blames}, {*prTy->typeArgs[i], argTTy.blames}); currentCms = tmpCms2; MaybeSetErrMsg(tmpMsg2); } else { currentCms = Unify(currentCms, {*prTy->typeArgs[i], argTTy.blames}, {*paramTy.typeArgs[i], paramTTy.blames}) .first; currentCms = Unify(currentCms, {*paramTy.typeArgs[i], paramTTy.blames}, {*prTy->typeArgs[i], argTTy.blames}) .first; } } res.insert(res.end(), currentCms.begin(), currentCms.end()); if (deterministic && !res.empty()) { break; } } if (!res.empty()) { cms = res; errMsg = {}; } return this->cms.empty() || !res.empty(); } bool LocalTypeArgumentSynthesis::UnifyBuiltInExtension(const Tracked& argTTy, const Tracked& paramTTy) { return UnifyNominal(argTTy, {paramTTy.ty, paramTTy.blames}); } bool LocalTypeArgumentSynthesis::UnifyPrimitiveTy(PrimitiveTy& argTy, PrimitiveTy& paramTy) { if (!tyMgr.IsSubtype(&argTy, ¶mTy)) { return false; } if (argTy.IsIdeal() && !paramTy.IsIdeal()) { UpdateIdealTysInConstraints(paramTy); } else if (paramTy.IsIdeal() && !argTy.IsIdeal()) { UpdateIdealTysInConstraints(argTy); } return true; } void LocalTypeArgumentSynthesis::UpdateIdealTysInConstraints(PrimitiveTy& tgtTy) { if (cms.size() != 1) { return; } Constraint& c = cms.front().constraint; if (!Ty::IsTyCorrect(this->curTyVar)) { return; } Ptr idealInt = TypeManager::GetPrimitiveTy(TypeKind::TYPE_IDEAL_INT); Ptr idealFloat = TypeManager::GetPrimitiveTy(TypeKind::TYPE_IDEAL_FLOAT); auto& lbs = c[this->curTyVar].lbs; // Actually only one of the contains is true otherwise errors will be reported beforehand when checking // argTy <: paramTy and the program will not run up to here. if (Utils::In(idealInt, lbs)) { lbs.erase(idealInt); lbs.insert(&tgtTy); } else if (Utils::In(idealFloat, lbs)) { lbs.erase(idealFloat); lbs.insert(&tgtTy); } auto& ubs = c[this->curTyVar].ubs; if (Utils::In(idealInt, ubs)) { ubs.erase(idealInt); ubs.insert(&tgtTy); } else if (Utils::In(idealFloat, ubs)) { ubs.erase(idealFloat); ubs.insert(&tgtTy); } } bool LocalTypeArgumentSynthesis::UnifyBuiltInTy(const Tracked& argTTy, const Tracked& paramTTy) { // Array/CPointer type must have exactly one type argument by definition. // TypeArgument of these built-in type are invariant. if (argTTy.ty.IsTyArgsSingleton() && paramTTy.ty.IsTyArgsSingleton()) { return UnifyOne({*argTTy.ty.typeArgs[0], argTTy.blames}, {*paramTTy.ty.typeArgs[0], paramTTy.blames}) && UnifyOne({*paramTTy.ty.typeArgs[0], paramTTy.blames}, {*argTTy.ty.typeArgs[0], argTTy.blames}); } else { return false; } } bool LocalTypeArgumentSynthesis::UnifyParamIntersectionTy( const Tracked& argTTy, const Tracked& paramTTy) { std::optional st; auto [tyL, tyR] = GetMaybeStableIters(paramTTy.ty.tys, st); // A <: B & C holds if A <: B AND A <: C holds for (auto ty = tyL; ty != tyR; ++ty) { if (!UnifyAndTrim(cms, argTTy, {**ty, paramTTy.blames})) { return false; } } return true; } bool LocalTypeArgumentSynthesis::UnifyArgIntersectionTy( const Tracked& argTTy, const Tracked& paramTTy) { auto& argTy = argTTy.ty; if (argTy.tys.empty()) { return UnifyOne({*tyMgr.GetAnyTy(), {}}, paramTTy); } else if (argTy.tys.size() == 1) { return UnifyOne({**argTy.tys.begin(), argTTy.blames}, paramTTy); } // else: see below // A & B <: C holds if either A <: C OR B <: C. ConstraintWithMemos res; std::optional st; auto [tyL, tyR] = GetMaybeStableIters(argTy.tys, st); for (auto ty = tyL; ty != tyR; ++ty) { auto [newCMS, msg] = Unify(cms, {**ty, argTTy.blames}, paramTTy); MaybeSetErrMsg(msg); res.insert(res.end(), newCMS.begin(), newCMS.end()); if (deterministic && !res.empty()) { break; } } return VerifyAndSetCMS(res); } bool LocalTypeArgumentSynthesis::UnifyParamUnionTy(const Tracked& argTTy, const Tracked& paramTTy) { auto& argTy = argTTy.ty; auto& paramTy = paramTTy.ty; // A <: B V C holds if A <: B OR A <: C holds if (paramTy.tys.empty()) { return argTy.IsNothing(); } else if (paramTy.tys.size() == 1) { return UnifyOne(argTTy, {**paramTy.tys.begin(), paramTTy.blames}); } // else: see below ConstraintWithMemos res; std::optional st; auto [tyL, tyR] = GetMaybeStableIters(paramTy.tys, st); for (auto ty = tyL; ty != tyR; ++ty) { auto [newCMS, msg] = Unify(cms, argTTy, {**ty, paramTTy.blames}); MaybeSetErrMsg(msg); res.insert(res.end(), newCMS.begin(), newCMS.end()); if (deterministic && !res.empty()) { break; } } return VerifyAndSetCMS(res); } bool LocalTypeArgumentSynthesis::UnifyArgUnionTy(const Tracked& argTTy, const Tracked& paramTTy) { std::optional st; auto [tyL, tyR] = GetMaybeStableIters(argTTy.ty.tys, st); // A V B <: C holds if A <: C AND B <: C holds for (auto ty = tyL; ty != tyR; ++ty) { if (!UnifyAndTrim(cms, {**ty, argTTy.blames}, paramTTy)) { return false; } } return true; } std::optional LocalTypeArgumentSynthesis::SolveConstraints(bool allowPartial) { TypeSubsts substs; std::for_each(cms.begin(), cms.end(), [this, &substs, &allowPartial](const ConstraintWithMemo& cm) { TypeSubst subst; // Build a type variables' dependency graph for topological sorting from the constraints. auto tyVarConstraintGraph = TyVarConstraintGraph(cm.constraint, argPack.tyVarsToSolve, tyMgr); while (true) { auto thisM = tyVarConstraintGraph.TopoOnce(cm.constraint); if (thisM.empty()) { break; } // Update 'lbs' and 'ubs' in constraints with solved typeMapping. thisM = ApplyTypeSubstForCS(subst, thisM); if (auto optThisSubst = FindSolution(thisM, cm.hasNothingTy, cm.hasAnyTy)) { // Substitute the graph with the newly solved ones. tyVarConstraintGraph.ApplyTypeSubst(*optThisSubst); subst.merge(*optThisSubst); } else { return; } } if (allowPartial || !HasUnsolvedTyVars(subst)) { substs.insert(subst); } }); if (!substs.empty()) { errMsg = {}; } return GetBestSolution(substs, allowPartial); } namespace { Ptr MeetUpperBounds(TypeManager& tyMgr, Ptr tyVar, const UpperBounds& ubs, const TyVars& ignoredTyVars) { // Classify the upperbound into tys which is a generic type with 'tyVar' in its typeArgs and other tys. // eg: T <: Interface // First calculate meet result with ty without tyVars. If there exists valid result 'tyM', // than instantiating 'tysWithTyVar' with the mapping "tyVar -> tyM", and calculate final meet result // using substituted tys and 'tyM'. Ptr tyM = nullptr; // Must set by 'SetMetType'. std::set> tysWithoutTyVar; std::set> tysWithTyVar; // Step 1, classify tys. std::for_each(ubs.begin(), ubs.end(), [&](auto ty) { ty->Contains(tyVar) ? tysWithTyVar.emplace(ty) : tysWithoutTyVar.emplace(ty); }); auto meetRes = JoinAndMeet(tyMgr, tysWithoutTyVar, ignoredTyVars).MeetAsVisibleTy(); JoinAndMeet::SetMetType(tyM, meetRes); if (Ty::IsTyCorrect(tyM) && !tysWithTyVar.empty()) { tysWithoutTyVar.clear(); // Step 2, substitute tys with the 'tyVar'. for (auto& it : tysWithTyVar) { tysWithoutTyVar.emplace(tyMgr.GetInstantiatedTy(it, {std::make_pair(tyVar, tyM)})); } tysWithoutTyVar.emplace(tyM); // Step 3, meet the final result. // For the case 'T <: Interface', the valid meet result will only be the given 'tyM', // the result will never be any of the instantiated ty substituted in step 2. meetRes = JoinAndMeet(tyMgr, tysWithoutTyVar, ignoredTyVars).MeetAsVisibleTy(); JoinAndMeet::SetMetType(tyM, meetRes); } return tyM; } } // namespace std::optional LocalTypeArgumentSynthesis::FindSolution( Constraint& thisM, const bool hasNothingTy, const bool hasAnyTy) { TypeSubst thisSubst; bool newInfo; SolvingErrInfo msg; do { newInfo = false; TyVars tyVarsOfThisM = Utils::GetKeys(thisM); std::optional st; auto [tyL, tyR] = GetMaybeStableIters(tyVarsOfThisM, st); for (auto it = tyL; it != tyR; ++it) { Ptr tyVar = *it; if (needDiagMsg && thisM[tyVar].lbs.empty() && thisM[tyVar].ubs.empty()) { msg = MakeMsgNoConstraint(*tyVar); break; } Ptr tyJ{}; auto joinRes = JoinAndMeet(tyMgr, thisM[tyVar].lbs, tyVarsOfThisM).JoinAsVisibleTy(); JoinAndMeet::SetJoinedType(tyJ, joinRes); Ptr tyM = MeetUpperBounds(tyMgr, tyVar, thisM[tyVar].ubs, tyVarsOfThisM); bool validAnyTy = hasAnyTy || (deterministic && thisM[tyVar].ubs.count(tyMgr.GetAnyTy()) > 0); bool validNothingTy = hasNothingTy || (deterministic && thisM[tyVar].lbs.count(TypeManager::GetNothingTy()) > 0); if (IsValidSolution(*tyJ, validNothingTy, validAnyTy)) { thisSubst.emplace(std::make_pair(tyVar, tyJ)); newInfo = true; thisM.erase(tyVar); } else if (tyJ->HasIdealTy() && !tyM->IsNumeric()) { tyMgr.ReplaceIdealTy(&tyJ); thisSubst.emplace(std::make_pair(tyVar, tyJ)); newInfo = true; thisM.erase(tyVar); } else if (IsValidSolution(*tyM, validNothingTy, validAnyTy)) { thisSubst.emplace(std::make_pair(tyVar, tyM)); newInfo = true; thisM.erase(tyVar); } else if (tyM->HasIdealTy()) { tyMgr.ReplaceIdealTy(&tyM); thisSubst.emplace(std::make_pair(tyVar, tyM)); newInfo = true; thisM.erase(tyVar); } else if (needDiagMsg) { StableTys lbSt(thisM[tyVar].lbs.begin(), thisM[tyVar].lbs.end()); StableTys ubSt(thisM[tyVar].ubs.begin(), thisM[tyVar].ubs.end()); std::vector> lbs; for (auto lb : lbSt) { lbs.push_back({*lb, thisM[tyVar].lb2Blames[lb]}); } std::vector> ubs; for (auto ub : ubSt) { ubs.push_back({*ub, thisM[tyVar].ub2Blames[ub]}); } msg = MakeMsgConflictingConstraints(*tyVar, lbs, ubs); } } auto newThisM = ApplyTypeSubstForCS(thisSubst, thisM); thisM = newThisM; } while (newInfo); if (errMsg.style == SolvingErrStyle::DEFAULT) { errMsg = msg; } return {thisSubst}; } bool LocalTypeArgumentSynthesis::IsValidSolution(const Ty& ty, const bool hasNothingTy, const bool hasAnyTy) const { bool solution = !ty.HasInvalidTy() && !ty.IsNothing() && !ty.IsAny() && !ty.HasIdealTy() && !ty.IsCType(); solution = solution || (hasNothingTy && ty.IsNothing()); solution = solution || (hasAnyTy && ty.IsAny()); return solution; } bool LocalTypeArgumentSynthesis::HasUnsolvedTyVars(const TypeSubst& subst) { auto tyVars = argPack.tyVarsToSolve; // A valid solution should constain substitution for all of type variables // and each substituted type should not contain any of type variable. return std::any_of(tyVars.begin(), tyVars.end(), [&subst](auto& tyVar) { return !Utils::InKeys(tyVar, subst) || std::any_of(subst.begin(), subst.end(), [tyVar](auto it) { return it.second->Contains(tyVar); }); }); } size_t LocalTypeArgumentSynthesis::CountUnsolvedTyVars(const TypeSubst& subst) { auto tyVars = argPack.tyVarsToSolve; size_t counter = 0; for (auto& tyVar: tyVars) { if (!Utils::InKeys(tyVar, subst) || std::any_of(subst.begin(), subst.end(), [tyVar](auto it) { return it.second->Contains(tyVar); })) { counter++; } }; return counter; } bool LocalTypeArgumentSynthesis::DoesCSCoverAllTyVars(const Constraint& m) { auto tyVars = argPack.tyVarsToSolve; return std::all_of(tyVars.begin(), tyVars.end(), [&m](auto tyVar) { return Utils::InKeys(tyVar, m) && (!m.at(tyVar).lbs.empty() || !m.at(tyVar).ubs.empty()); }); } std::optional LocalTypeArgumentSynthesis::GetBestSolution(const TypeSubsts& substs, bool allowPartial) { // Here requires a function which compares the input substitutions and select the best one (if exists). // The best one is the one in which the instantiated types are subtypes of other solutions. // For example: given D <: C, then [X |-> D] is better than [X |-> C]. A counter example: given // [X |-> D, Y |-> C] and [X |-> C, Y |-> D], no one is better than the other; hence there is no best solution. if (substs.empty() || argPack.tyVarsToSolve.empty()) { return {}; } if (substs.size() == 1) { return {*substs.begin()}; } // Caller guarantees all elements in 'substs' have all tyVarsToSolve. std::vector candidates(substs.begin(), substs.end()); std::vector maximals(candidates.size(), true); if (allowPartial) { std::vector unsolvedCount; for (auto& tySub: candidates) { unsolvedCount.push_back(CountUnsolvedTyVars(tySub)); } auto minCount = *std::min_element(unsolvedCount.begin(), unsolvedCount.end()); for (size_t i = 0; i < unsolvedCount.size(); i++) { if (unsolvedCount[i] > minCount) { maximals[i] = false; } } } for (auto tyVar : argPack.tyVarsToSolve) { CompareCandidates(tyVar, candidates, maximals); } if (auto idx = GetBestIndex(maximals)) { return {candidates[*idx]}; } else { return {}; } } void LocalTypeArgumentSynthesis::CompareCandidates( Ptr tyVar, const std::vector& candidates, std::vector& maximals) { auto checkForNumeric = [&maximals](auto& tyI, auto& tyJ, size_t i, size_t j) { auto res = TypeCheckUtil::CompareIntAndFloat(tyI, tyJ); if (res == TypeCheckUtil::ComparisonRes::GT) { maximals[i] = false; } else if (res == TypeCheckUtil::ComparisonRes::LT) { maximals[j] = false; } }; for (size_t i = 0; i < candidates.size(); ++i) { if (!maximals[i]) { continue; } auto tyI = tyMgr.GetInstantiatedTy(tyVar, candidates[i]); CJC_NULLPTR_CHECK(tyI); for (size_t j = i + 1; j < candidates.size(); ++j) { if (!maximals[j]) { continue; } auto tyJ = tyMgr.GetInstantiatedTy(tyVar, candidates[j]); CJC_NULLPTR_CHECK(tyJ); if (tyI->IsNumeric() && tyJ->IsNumeric()) { // If candidates are numberic types, comparing them with built-in comparator. checkForNumeric(*tyI, *tyJ, i, j); } else if (!tyMgr.IsSubtype(tyI, tyJ)) { maximals[i] = false; } else if (!tyMgr.IsSubtype(tyJ, tyI)) { maximals[j] = false; } if (!maximals[i]) { break; } } } } std::optional LocalTypeArgumentSynthesis::GetBestIndex(const std::vector& maximals) const { std::vector res; for (size_t i = 0; i < maximals.size(); i++) { if (maximals.at(i)) { res.push_back(i); } } if (res.size() == 1) { return {res.front()}; } else { return {}; } } void TyVarConstraintGraph::PreProcessConstraintGraph(const Constraint& m, const TyVars& mayUsedTyVars) { for (const auto& constraint : m) { if (mayUsedTyVars.count(constraint.first) == 0) { continue; } usedTyVars.emplace(constraint.first); for (const auto& lb : constraint.second.lbs) { for (auto& lbGen : lb->GetGenericTyArgs(mayUsedTyVars)) { usedTyVars.emplace(lbGen); if (edges[lbGen].count(constraint.first) == 0) { indegree[constraint.first]++; edges[lbGen].emplace(constraint.first); } } } for (const auto& ub : constraint.second.ubs) { for (auto& ubGen : ub->GetGenericTyArgs(mayUsedTyVars)) { usedTyVars.emplace(ubGen); if (edges[ubGen].count(constraint.first) == 0) { indegree[constraint.first]++; edges[ubGen].emplace(constraint.first); } } } } for (const auto& usedKey : usedTyVars) { if (indegree.count(usedKey) == 0) { indegree[usedKey] = 0; } isVisited[usedKey] = false; } } Constraint TyVarConstraintGraph::TopoOnce(const Constraint& m) { if (!hasNext) { return Constraint(); } Constraint solvedConstraints; for (const auto& ty : std::as_const(indegree)) { if (ty.second == 0 && solvedTyVars.count(ty.first) == 0) { solvedTyVars.emplace(ty.first); if (auto found = m.find(ty.first); found != m.cend()) { solvedConstraints[ty.first] = found->second; } isVisited[ty.first] = true; } } if (solvedTyVars.size() == usedTyVars.size()) { // all constraints are solved. hasNext = false; return solvedConstraints; } if (solvedConstraints.empty()) { // contains loop. for (const auto& ty : std::as_const(indegree)) { if (ty.second != 1) { continue; } FindLoopConstraints(m, ty.first, solvedConstraints); if (!solvedConstraints.empty()) { break; } } } for (const auto& solvedConstraint : std::as_const(solvedConstraints)) { for (const auto& e : edges[solvedConstraint.first]) { indegree[e]--; } } return solvedConstraints; } void TyVarConstraintGraph::FindLoopConstraints(const Constraint& m, Ptr start, Constraint& tyVarsInLoop) { std::stack> loopPath; if (HasLoop(start, loopPath)) { while (!loopPath.empty()) { solvedTyVars.emplace(loopPath.top()); tyVarsInLoop[loopPath.top()] = m.at(loopPath.top()); loopPath.pop(); } } } bool TyVarConstraintGraph::HasLoop(Ptr start, std::stack>& loopPath) { if (isVisited[start]) { return true; } loopPath.push(start); isVisited[start] = true; for (auto const& out : edges[start]) { if (HasLoop(out, loopPath)) { return true; } } isVisited[start] = false; loopPath.pop(); return false; } TypeSubst LocalTypeArgumentSynthesis::ResetIdealTypesInSubst(TypeSubst& m) { TypeSubst res; for (const auto& pair : std::as_const(m)) { Ptr tyVar = pair.first; Ptr instTy = pair.second; tyMgr.ReplaceIdealTy(&instTy); res.emplace(tyVar, instTy); } return res; } Constraint LocalTypeArgumentSynthesis::ApplyTypeSubstForCS(const TypeSubst& subst, const Constraint& cs) { Constraint res; for (auto& it : cs) { Ptr tyVar = it.first; TyVarBounds newBounds; for (auto lb : it.second.lbs) { auto newLb = tyMgr.GetInstantiatedTy(lb, subst); newBounds.lbs.insert(newLb); if (it.second.lb2Blames.count(lb) > 0) { newBounds.lb2Blames[newLb] = it.second.lb2Blames.at(lb); } } for (auto ub : it.second.ubs) { auto newUb = tyMgr.GetInstantiatedTy(ub, subst); newBounds.ubs.insert(newUb); if (it.second.ub2Blames.count(ub) > 0) { newBounds.ub2Blames[newUb] = it.second.ub2Blames.at(ub); } } res.emplace(tyVar, newBounds); } return res; } SolvingErrInfo LocalTypeArgumentSynthesis::GetErrInfo() { /* Recover names as context ty vars. */ if (errMsg.tyVar) { errMsg.tyVar = StaticCast(tyMgr.RecoverUnivTyVar(errMsg.tyVar)); } for (auto& ty: errMsg.lbs) { ty = tyMgr.RecoverUnivTyVar(ty); } for (auto& ty: errMsg.ubs) { ty = tyMgr.RecoverUnivTyVar(ty); } return errMsg; } std::pair>::iterator, std::set>::iterator> LocalTypeArgumentSynthesis::GetMaybeStableIters( const std::set>& s, std::optional& ss) const { auto tyL = s.cbegin(); auto tyR = s.cend(); if (needDiagMsg) { ss = {StableTys(tyL, tyR)}; tyL = ss.value().cbegin(); tyR = ss.value().cend(); } return {tyL, tyR}; } std::pair LocalTypeArgumentSynthesis::GetMaybeStableIters( const TyVars& s, std::optional& ss) const { auto tyL = s.cbegin(); auto tyR = s.cend(); if (needDiagMsg) { ss = {StableTyVars(tyL, tyR)}; tyL = ss.value().cbegin(); tyR = ss.value().cend(); } return {tyL, tyR}; } SolvingErrInfo LocalTypeArgumentSynthesis::MakeMsgConflictingConstraints( TyVar& v, const std::vector>& lbTTys, const std::vector>& ubTTys) const { auto ret = SolvingErrInfo{.style = SolvingErrStyle::CONFLICTING_CONSTRAINTS, .tyVar = &v}; for (auto tty : lbTTys) { ret.lbs.emplace_back(&tty.ty); ret.blames.push_back(tty.blames); } for (auto tty : ubTTys) { ret.ubs.emplace_back(&tty.ty); ret.blames.push_back(tty.blames); } return ret; } SolvingErrInfo LocalTypeArgumentSynthesis::MakeMsgNoConstraint(TyVar& v) const { return { .style = SolvingErrStyle::NO_CONSTRAINT, .tyVar = &v }; } SolvingErrInfo LocalTypeArgumentSynthesis::MakeMsgMismatchedArg(const Blame& blame) const { return { .style = SolvingErrStyle::ARG_MISMATCH, .blames = {{blame}} }; } SolvingErrInfo LocalTypeArgumentSynthesis::MakeMsgMismatchedRet(const Blame& blame) const { return { .style = SolvingErrStyle::RET_MISMATCH, .blames = {{blame}} }; } void LocalTypeArgumentSynthesis::MaybeSetErrMsg(const SolvingErrInfo& s) { if (needDiagMsg && errMsg.style == SolvingErrStyle::DEFAULT) { errMsg = s; } } bool TypeChecker::TypeCheckerImpl::Unify(Constraint& cst, AST::Ty& argTy, AST::Ty& paramTy) { return LocalTypeArgumentSynthesis::Unify(typeManager, cst, argTy, paramTy); } std::optional TypeChecker::TypeCheckerImpl::SolveConstraints(const Constraint& cst) { return LocalTypeArgumentSynthesis::SolveConstraints(typeManager, cst); } bool LocalTypeArgumentSynthesis::Unify( TypeManager& tyMgr, Constraint& cst, AST::Ty& argTy, AST::Ty& paramTy) { LocTyArgSynArgPack dummyArgPack = { {}, {}, {}, {}, TypeManager::GetInvalidTy(), TypeManager::GetInvalidTy(), Blame()}; auto synIns = LocalTypeArgumentSynthesis(tyMgr, dummyArgPack, {}, false); synIns.cms = {{cst}}; synIns.deterministic = true; if (synIns.UnifyOne({argTy, {}}, {paramTy, {}})) { CJC_ASSERT(synIns.cms.size() > 0); cst = synIns.cms[0].constraint; return true; } return false; } std::optional LocalTypeArgumentSynthesis::SolveConstraints(TypeManager& tyMgr, const Constraint& cst) { LocTyArgSynArgPack dummyArgPack = { tyMgr.GetUnsolvedTyVars(), {}, {}, {}, TypeManager::GetInvalidTy(), TypeManager::GetInvalidTy(), Blame()}; auto synIns = LocalTypeArgumentSynthesis(tyMgr, dummyArgPack, {}, false); synIns.cms = {{cst}}; synIns.deterministic = true; return synIns.SolveConstraints(true); } bool LocalTypeArgumentSynthesis::IsGreedySolution(const TyVar& tv, const Ty& bound, bool isUpperbound) { // the bound is universal ty var bool tyParam = bound.IsGeneric() && !bound.IsPlaceholder(); // the bound is placeholder ty var, and depth is no deeper than this one // NOTE: if the bound's ty var is introduced in a deeper scope, it will leak out of its scope if used as a solution bool outerTyVar = bound.IsPlaceholder() && (tyMgr.ScopeDepthOfTyVar(StaticCast(bound)) <= tyMgr.ScopeDepthOfTyVar(tv)); // the bound doesn't have inheritance bool finalType = (isUpperbound && bound.IsClass() && !IsInheritableClass(*StaticCast(bound).decl)) || (!bound.IsGeneric() && !bound.IsClassLike() && !bound.IsAny() && !bound.IsNothing()); // the solution must be Any/Nothing bool anyOrNothing = (bound.IsAny() && !isUpperbound) || (bound.IsNothing() && isUpperbound); return tyParam || outerTyVar || finalType || anyOrNothing; } cangjie_compiler-1.0.7/src/Sema/LocalTypeArgumentSynthesis.h000066400000000000000000000175721510705540100241610ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares a class for calculating type arguments for function calls. */ #ifndef CANGJIE_SEMA_LOCALTYPEARGUMENTSYNTHESIS_H #define CANGJIE_SEMA_LOCALTYPEARGUMENTSYNTHESIS_H #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Types.h" #include "cangjie/Sema/CommonTypeAlias.h" #include "cangjie/Sema/TypeManager.h" namespace Cangjie { struct LocTyArgSynArgPack { TyVars tyVarsToSolve; std::vector> argTys; std::vector> paramTys; std::vector argBlames; Ptr funcRetTy = nullptr; // Nullable Ptr retTyUB = nullptr; // Nullable Blame retBlame; }; using MemoForUnifiedTys = std::set, Ptr>>; class LocalTypeArgumentSynthesis { struct ConstraintWithMemo { Constraint constraint; MemoForUnifiedTys memo; bool hasNothingTy; bool hasAnyTy; }; using ConstraintWithMemos = std::vector; template struct Tracked { T& ty; std::set blames; }; public: /* If needDiagMsg is true, blames have to be provided in arg pack. The solving will also use a less efficient but * stable version that guarantees a stable error message if type args can't be solved. */ LocalTypeArgumentSynthesis( TypeManager& tyMgr, const LocTyArgSynArgPack& argPack, const GCBlames& gcBlames, bool needDiagMsg = false) : tyMgr(tyMgr), argPack(argPack), gcBlames(gcBlames), needDiagMsg(needDiagMsg) { if (!needDiagMsg) { this->argPack.argBlames = std::vector(argPack.argTys.size(), Blame()); } } ~LocalTypeArgumentSynthesis(); // The main function that synthesize type arguments to a generic function call. // allowPartial: whether partial solutions should be returnd. By default no. std::optional SynthesizeTypeArguments(bool allowPartial = false); bool HasUnsolvedTyVars(const TypeSubst& subst); size_t CountUnsolvedTyVars(const TypeSubst& subst); SolvingErrInfo GetErrInfo(); // there's also a wrapper in TypeCheckerImpl. that one is recommended static bool Unify(TypeManager& tyMgr, Constraint& cst, AST::Ty& argTy, AST::Ty& paramTy); static std::optional SolveConstraints(TypeManager& tyMgr, const Constraint& cst); private: TypeManager& tyMgr; LocTyArgSynArgPack argPack; ConstraintWithMemos cms{}; // The current type variable for which we are establishing constraints. Ptr curTyVar = nullptr; const GCBlames& gcBlames; // reference to context, for initializing upperbounds from generic constraints GCBlames gcBlamesInst{}; // same as above, but with unsolved ty vars substituted by the instantiated version SolvingErrInfo errMsg; // final error message bool needDiagMsg; bool deterministic = false; Constraint InitConstraints(const TyVars& tyVarsToSolve); void InsertConstraint(Constraint& c, TyVar& tyVar, TyVarBounds& tvb) const; // Unify two types by imposing subtyping relation argTy <: paramTy and generate corresponding constraints. // The function accept constraints and returns new ones without modifying the existing ones, which eases the work // of the caller, since in certain situations, the caller needs to try to unify different pairs of argTy and paramTy // from the same state (i.e., cms) and only preserves valid ones. // The string part in return value is potential error message. std::pair Unify( const ConstraintWithMemos& newCMS, const Tracked& argTTy, const Tracked& paramTTy); // Directly merge result of sub-Unify with cms & errMsg of parent instance. Used in cases where failure of // a sub-Unify also indicates the failure of the parent Unify. bool UnifyAndTrim( const ConstraintWithMemos& curCMS, const Tracked& argTTy, const Tracked& paramTTy); // Return the **best** type substitution regarding the subtyping relation if it exists. std::optional SolveConstraints(bool allowPartial = false); // The UnifyXXX series have SIDE EFFECT that the Constraints m and the UnifiedTys memo will be updated. // Constraints m: generated constraints, // UnifiedTys memo: memoization of already unified types, // Ty argTy and Ty paramTy: two types to be unified. // The series of functions return false if obvious errors are detected. bool UnifyOne(const Tracked& argTTy, const Tracked& paramTTy); bool UnifyTyVar(const Tracked& argTTy, const Tracked& paramTTy); bool UnifyTyVarCollectConstraints(TyVar& tyVar, const Tracked& lbTTy, const Tracked& ubTTy); bool UnifyContextTyVar(const Tracked& argTTy, const Tracked& paramTTy); bool UnifyBuiltInTy(const Tracked& argTTy, const Tracked& paramTTy); // Nominal types are types that have names, defined by class, interface, enum, and struct. bool UnifyNominal(const Tracked& argTTy, const Tracked& paramTTy); // Primitive/Array types can only subtype interface types (by extensions). bool UnifyBuiltInExtension(const Tracked& argTTy, const Tracked& paramTTy); bool UnifyTupleTy(const Tracked& argTTy, const Tracked& paramTTy); bool UnifyFuncTy(const Tracked& argTTy, const Tracked& paramTTy); bool UnifyPrimitiveTy(AST::PrimitiveTy& argTy, AST::PrimitiveTy& paramTy); bool UnifyParamIntersectionTy(const Tracked& argTTy, const Tracked& paramTTy); bool UnifyArgIntersectionTy(const Tracked& argTTy, const Tracked& paramTTy); bool UnifyParamUnionTy(const Tracked& argTTy, const Tracked& paramTTy); bool UnifyArgUnionTy(const Tracked& argTTy, const Tracked& paramTTy); void UpdateIdealTysInConstraints(AST::PrimitiveTy& tgtTy); std::optional FindSolution(Constraint& thisM, const bool hasNothingTy, const bool hasAnyTy); bool IsValidSolution(const AST::Ty& ty, const bool hasNothingTy, const bool hasAnyTy) const; bool DoesCSCoverAllTyVars(const Constraint& m); TypeSubst ResetIdealTypesInSubst(TypeSubst& m); Constraint ApplyTypeSubstForCS(const TypeSubst& subst, const Constraint& cs); std::optional GetBestSolution(const TypeSubsts& substs, bool allowPartial = false); std::optional GetBestIndex(const std::vector& maximals) const; void CompareCandidates(Ptr tyVar, const std::vector& candidates, std::vector& maximals); std::pair>::iterator, std::set>::iterator> GetMaybeStableIters( const std::set>& s, std::optional& ss) const; std::pair GetMaybeStableIters( const TyVars& s, std::optional& ss) const; SolvingErrInfo MakeMsgNoConstraint(TyVar& v) const; SolvingErrInfo MakeMsgConflictingConstraints( TyVar& v, const std::vector>& lbTTys, const std::vector>& ubTTys) const; SolvingErrInfo MakeMsgMismatchedArg(const Blame& blame) const; SolvingErrInfo MakeMsgMismatchedRet(const Blame& blame) const; void MaybeSetErrMsg(const SolvingErrInfo& s); void CopyUpperbound(); bool IsGreedySolution(const TyVar& tv, const AST::Ty& bound, bool isUpperbound); bool VerifyAndSetCMS(const ConstraintWithMemos& newCMS); }; } // namespace Cangjie #endif // CANGJIE_SEMA_LOCALTYPEARGUMENTSYNTHESIS_H cangjie_compiler-1.0.7/src/Sema/LookUpImpl.cpp000066400000000000000000000703241510705540100212300ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file defines functions for looking up symbol. */ #include "TypeCheckUtil.h" #include "TypeCheckerImpl.h" #include "cangjie/AST/ScopeManagerApi.h" #include "cangjie/AST/Utils.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; namespace { class LookUpImpl { public: LookUpImpl(const ASTContext& ctx, DiagnosticEngine& diag, TypeManager& manger, ImportManager& importManager) : ctx(ctx), diag(diag), typeManager(manger), importManager(importManager) { } ~LookUpImpl() = default; /** * Search target by namespace(like class, interface, struct, enum) and field name. * NOTE: decl which has 'IN_REFERENCE_CYCLE' should only be intercepted during checking 'inheritedTypes'. */ std::vector> FieldLookup(Ptr decl, const std::string& fieldName, const LookupInfo& info = {}); std::vector> Lookup(const std::string& name, const std::string& scopeName, const Node& node, bool onlyLookUpTopLevel = false, bool isSetter = false); void FieldLookupExtend( Ty& ty, const std::string& fieldName, std::vector>& results, const LookupInfo& info); private: void AddMemberIfValidForLookup(std::vector>& results, Ty& baseTy, bool isSetter, Decl& decl); /** * @brief Determine whether @param decl forms an override or implementation with decl in @param results. * If decl in @param results override the @param decl, do nothing. * If @param decl override the decl in @param results, replace the match item in @param results. * @param results the results that have been collected. * @param decl currently processed declaration. * @param baseTy type of MemberAccess' baseExpr. * @param parentTy inherited instantiated types. eg: * 'I1 <: I2', parentTy should be 'I2'. * 'I1 <: I2', parentTy should be 'I2'. */ void ResolveOverrideOrShadow( std::vector>& results, Decl& decl, Ptr baseTy, Ptr parentTy); void FieldLookup(const ClassDecl& cd, const std::string& fieldName, std::vector>& results, const LookupInfo& info); void FieldLookup( InterfaceTy& idTy, const std::string& fieldName, std::vector>& results, const LookupInfo& info); std::vector> FieldLookup(const EnumDecl& ed, const std::string& fieldName, const LookupInfo& info); std::vector> FieldLookup(const StructDecl& sd, const std::string& fieldName, const LookupInfo& info); std::vector> FieldLookup(const PackageDecl& pd, const std::string& fieldName); std::vector> StdLibFieldLookup(const Node& node, const std::string& fieldName); void ProcessStructDeclBody( const std::string& name, const std::string& scopeName, const Node& node, std::vector>& results); bool LookupImpl(const std::string& name, std::string scopeName, const Node& node, bool onlyLookUpTopLevel, bool isSetter, std::vector>& results); bool FindRealResult(const Node& node, bool isSetter, std::vector>& results, std::multimap>& resultsMap, bool isInDeclBody); const ASTContext& ctx; DiagnosticEngine& diag; TypeManager& typeManager; ImportManager& importManager; }; bool IgnoredMember(const Decl& decl) { // The cjdb expression calculation may involve calling the A().init() function within a member function. if (decl.TestAttr(Attribute::TOOL_ADD)) { return false; } // Constructor, static init, primary ctor and main entry cannot be used by user, just ignore it in field lookup. return decl.TestAnyAttr(Attribute::CONSTRUCTOR, Attribute::MAIN_ENTRY) || decl.astKind == ASTKind::PRIMARY_CTOR_DECL; } void UpdatePropOverriddenCache( TypeManager& typeManager, const PropDecl& src, std::vector>& results, Ptr baseTy) { for (auto it = results.begin(); it != results.end(); ++it) { if (auto fd2 = DynamicCast(*it)) { IsOverrideOrShadow(typeManager, *fd2, src, baseTy); } } } template inline Ptr GetPlatformDecl(Ptr decl) { CJC_ASSERT(decl); return StaticCast(decl->platformImplementation == nullptr ? decl : decl->platformImplementation); } } // namespace void LookUpImpl::AddMemberIfValidForLookup(std::vector>& results, Ty& baseTy, bool isSetter, Decl& decl) { bool covered{false}; if (auto pd = DynamicCast(&decl); pd) { if (!isSetter) { if (pd->getters.empty()) { return; } } else { if (pd->isVar && pd->setters.empty()) { return; } } } if (decl.astKind != ASTKind::FUNC_DECL) { if (auto prop = DynamicCast(&decl)) { UpdatePropOverriddenCache(typeManager, *prop, results, &baseTy); } results.push_back(&decl); return; } for (auto it = results.begin(); it != results.end();) { if (auto fd2 = DynamicCast(*it); fd2 && IsOverrideOrShadow(typeManager, *fd2, static_cast(decl), &baseTy)) { // if fd2 is abstract and fd1 is the implementation, then we remove fd2 from results and push // fd1 into results if (fd2->TestAttr(Attribute::ABSTRACT) && !decl.TestAttr(Attribute::ABSTRACT)) { it = results.erase(it); results.push_back(&decl); } covered = true; break; } else { ++it; } } if (!covered) { results.push_back(&decl); } } void LookUpImpl::FieldLookupExtend( Ty& ty, const std::string& fieldName, std::vector>& results, const LookupInfo& info) { CJC_NULLPTR_CHECK(info.file); // NOTE: decl which has 'IN_REFERENCE_CYCLE' should only be intercepted during checking 'inheritedTypes'. OrderedDeclSet extendFuncs; // Ordered set for diagnostic consistency. auto extends = typeManager.GetAllExtendsByTy(ty); std::set, CmpNodeByPos> orderExtends(extends.begin(), extends.end()); for (auto& extend : orderExtends) { CJC_NULLPTR_CHECK(extend); if (!importManager.IsExtendAccessible(*info.file, *extend)) { continue; } for (auto& it : extend->members) { if (it->identifier == fieldName) { extendFuncs.emplace(it.get()); } } } results.insert(results.end(), extendFuncs.begin(), extendFuncs.end()); // For interface functions found in different extend's inherited interfaces, add them to results when: // 1. interface function is not shadowed by already found instance functions. // 2. interface functions belong to different extends are all needed to be added to the 'results' // since they will not shadow each other in extend. (Collision will be reported later when checking extend) for (auto& extend : orderExtends) { CJC_NULLPTR_CHECK(extend); if (!importManager.IsExtendAccessible(*info.file, *extend)) { continue; } for (auto& it : extend->inheritedTypes) { if (it == nullptr) { continue; } if (auto interfaceTy = DynamicCast(it->ty); interfaceTy && interfaceTy->decl && !interfaceTy->decl->TestAttr(Attribute::IN_REFERENCE_CYCLE)) { FieldLookup(*interfaceTy, fieldName, results, info); } } } } void LookUpImpl::ResolveOverrideOrShadow( std::vector>& results, Decl& decl, Ptr baseTy, Ptr parentTy) { if (decl.astKind == ASTKind::PROP_DECL) { if (!decl.TestAttr(Attribute::ABSTRACT)) { Utils::EraseIf(results, [](auto it) { return it->TestAttr(Attribute::ABSTRACT); }); } } for (auto resIter = results.begin(); resIter != results.end(); ++resIter) { // Caller guarantees 'decl' must be func or prop. if ((*resIter) == nullptr || ((*resIter)->astKind != ASTKind::FUNC_DECL && (*resIter)->astKind != ASTKind::PROP_DECL)) { continue; } auto decl1 = RawStaticCast(&decl); auto decl2 = RawStaticCast(*resIter); if (!typeManager.PairIsOverrideOrImpl(*decl2, *decl1, baseTy, parentTy) && !typeManager.PairIsOverrideOrImpl(*decl1, *decl2, baseTy)) { continue; } auto isSub = [this](const Ptr leaf, const Ptr root) { auto tys = Promotion(typeManager).Promote(*leaf, *root); bool ret = false; for (auto ty : tys) { ret = typeManager.IsSubtype(leaf, ty) || ret; } return ret; }; if (isSub(decl1->outerDecl->ty, decl2->outerDecl->ty) || (decl2->TestAttr(Attribute::ABSTRACT) && !decl1->TestAttr(Attribute::ABSTRACT))) { // If decl1 override or shadow the decl2, only reserved decl1. results.erase(resIter); results.emplace_back(&decl); } else if (isSub(decl2->outerDecl->ty, decl1->outerDecl->ty) || (decl1->TestAttr(Attribute::ABSTRACT) && !decl2->TestAttr(Attribute::ABSTRACT))) { // Do nothing. Reserved decl2. } else { // If the relationship is not override or shadow, both are saved. results.emplace_back(&decl); } // Otherwise, do not insert current candidate into 'results'. return; } results.emplace_back(&decl); } void LookUpImpl::FieldLookup( const ClassDecl& cd, const std::string& fieldName, std::vector>& results, const LookupInfo& info) { // NOTE: decl which has 'IN_REFERENCE_CYCLE' should only be intercepted during checking 'inheritedTypes'. if (!cd.body) { return; } std::vector> decls; for (auto& it : cd.body->decls) { if (!it || IgnoredMember(*it)) { continue; } decls.push_back(it.get()); } for (auto& decl : decls) { auto notValid = decl == nullptr || decl->identifier != fieldName; if (notValid) { continue; } auto staticAndNotClassLike = !decl->IsClassLikeDecl() && decl->TestAttr(Attribute::STATIC); auto foundTy = info.baseTy; if (staticAndNotClassLike) { auto genericTy = foundTy ? Ty::GetGenericTyOfInsTy(*foundTy) : nullptr; if (genericTy) { foundTy = genericTy; } } AddMemberIfValidForLookup(results, *TypeManager::GetNonNullTy(foundTy), info.isSetter, *decl); } if (!info.lookupInherit) { return; } // Lookup field in super class and its extend or super interfaces. for (auto& it : cd.inheritedTypes) { auto super = it ? Ty::GetDeclPtrOfTy(it->ty) : nullptr; if (!super || super->TestAttr(Attribute::IN_REFERENCE_CYCLE)) { continue; } if (auto superClass = DynamicCast(super)) { auto superInfo = info; superInfo.lookupExtend = true; FieldLookup(*superClass, fieldName, results, superInfo); } else if (auto superInterface = DynamicCast(super)) { auto parentInstTys = Promotion(typeManager).Promote(*cd.ty, *superInterface->ty); for (auto parentInstTy : parentInstTys) { FieldLookup(*StaticCast(parentInstTy), fieldName, results, {info.baseTy}); } } } if (info.lookupExtend) { FieldLookupExtend(*cd.ty, fieldName, results, info); } } void LookUpImpl::FieldLookup( InterfaceTy& idTy, const std::string& fieldName, std::vector>& results, const LookupInfo& info) { auto id = GetPlatformDecl(idTy.declPtr); // NOTE: decl which has 'IN_REFERENCE_CYCLE' should only be intercepted during checking 'inheritedTypes'. if (!id->body) { return; } for (auto& decl : id->body->decls) { if (decl == nullptr || decl->identifier != fieldName) { continue; } // static member can also be inherited auto foundTy = info.baseTy; if (!decl->IsClassLikeDecl() && decl.get()->TestAttr(Attribute::STATIC)) { auto genericTy = foundTy ? Ty::GetGenericTyOfInsTy(*foundTy) : nullptr; if (genericTy) { foundTy = genericTy; } } if (decl->IsFuncOrProp()) { ResolveOverrideOrShadow(results, *decl, foundTy, &idTy); } else { (void)results.emplace_back(decl.get()); } } if (!info.lookupInherit) { return; } // Lookup field in super interfaces. for (auto& it : id->inheritedTypes) { if (!it) { continue; } if (auto interfaceTy = DynamicCast(it->ty); interfaceTy && interfaceTy->decl && !interfaceTy->decl->TestAttr(Attribute::IN_REFERENCE_CYCLE)) { auto promTys = Promotion(typeManager).Promote(idTy, *interfaceTy); for (auto promTy : promTys) { FieldLookup(*StaticCast(promTy), fieldName, results, {info.baseTy}); } } } } std::vector> LookUpImpl::FieldLookup(const EnumDecl& ed, const std::string& fieldName, const LookupInfo& info) { // NOTE: decl which has 'IN_REFERENCE_CYCLE' should only be intercepted during checking 'inheritedTypes'. std::vector> results; for (auto& ctor : ed.constructors) { if (ctor->identifier == fieldName) { results.push_back(ctor.get()); } } for (auto& func : ed.members) { if (func->identifier == fieldName) { results.push_back(func.get()); } } for (auto& it : ed.inheritedTypes) { if (auto interfaceTy = DynamicCast(it->ty); interfaceTy && interfaceTy->decl && !interfaceTy->decl->TestAttr(Attribute::IN_REFERENCE_CYCLE)) { FieldLookup(*interfaceTy, fieldName, results, info); } } if (info.lookupExtend) { FieldLookupExtend(*ed.ty, fieldName, results, info); } return results; } std::vector> LookUpImpl::FieldLookup( const StructDecl& sd, const std::string& fieldName, const LookupInfo& info) { // NOTE: decl which has 'IN_REFERENCE_CYCLE' should only be intercepted during checking 'inheritedTypes'. std::vector> results; auto bodySetter = [&fieldName, &results](auto& decl) { if (decl != nullptr && decl->identifier == fieldName && !IgnoredMember(*decl)) { results.push_back(decl.get()); } }; auto inheritedTypesSetter = [&fieldName, &results, &info, this](auto& it) { if (it == nullptr) { return; } if (auto interfaceTy = DynamicCast(it->ty); interfaceTy && interfaceTy->decl && !interfaceTy->decl->TestAttr(Attribute::IN_REFERENCE_CYCLE)) { FieldLookup(*interfaceTy, fieldName, results, info); } }; std::for_each(sd.body->decls.begin(), sd.body->decls.end(), bodySetter); std::for_each(sd.inheritedTypes.begin(), sd.inheritedTypes.end(), inheritedTypesSetter); if (info.lookupExtend) { FieldLookupExtend(*sd.ty, fieldName, results, info); } return results; } std::vector> LookUpImpl::FieldLookup(const PackageDecl& pd, const std::string& fieldName) { // Must be imported package decl, decls in source package cannot be accessed by package name. CJC_ASSERT(pd.TestAttr(Attribute::IMPORTED)); auto decls = importManager.GetPackageMembersByName(*pd.srcPackage, fieldName); // Main entry cannot be referenced. Utils::EraseIf(decls, [](auto decl) { return decl->TestAttr(Attribute::MAIN_ENTRY); }); return Utils::SetToVec>(decls); } std::vector> LookUpImpl::FieldLookup(Ptr decl, const std::string& fieldName, const LookupInfo& info) { std::vector> results; if (!decl) { return results; } // All method from common type are moved to platform one // So looking up method in platform type decl = GetPlatformDecl(decl); if (auto cd = DynamicCast(decl)) { FieldLookup(*cd, fieldName, results, info); return results; } if (auto id = DynamicCast(decl); id && Ty::IsTyCorrect(id->ty)) { CJC_ASSERT(id->ty->kind == TypeKind::TYPE_INTERFACE); FieldLookup(*StaticCast(id->ty), fieldName, results, info); return results; } if (auto ed = DynamicCast(decl)) { return FieldLookup(*ed, fieldName, info); } if (auto sd = DynamicCast(decl)) { return FieldLookup(*sd, fieldName, info); } if (auto pd = DynamicCast(decl)) { // Lookup package decl. return FieldLookup(*pd, fieldName); } return results; } std::vector> LookUpImpl::StdLibFieldLookup(const Node& node, const std::string& fieldName) { std::vector> results; Ptr target = nullptr; if (node.TestAttr(Attribute::IN_CORE)) { target = importManager.GetCoreDecl(fieldName); } else if (node.TestAttr(Attribute::IN_MACRO)) { target = importManager.GetAstDecl(fieldName); } if (target) { results.emplace_back(target); } return results; } namespace { /** Check whether the @p target is defined after @p ref node. */ inline bool IsDefinedAfter(const Decl& target, const Node& ref) { return target.begin > ref.begin; } bool IsNodeInVarDecl(const ASTContext& ctx, const Node& node, VarDecl& vd) { bool found = false; auto& nodeToSearch = ctx.GetOuterVarDeclAbstract(vd); Walker walker(&nodeToSearch, [&node, &found](Ptr n) -> VisitAction { if (n == &node) { found = true; return VisitAction::STOP_NOW; } return VisitAction::WALK_CHILDREN; }); walker.Walk(); return found; } bool IsNodeInDestructed(const Node& node, VarDecl& vd) { if (!vd.parentPattern || !vd.parentPattern->ctxExpr) { return false; } bool found = false; Walker walker(vd.parentPattern->ctxExpr, [&node, &found](Ptr n) -> VisitAction { if (n == &node) { found = true; return VisitAction::STOP_NOW; } return VisitAction::WALK_CHILDREN; }); walker.Walk(); return found; } bool IsNodeInTypeAliasDecl(const Node& node, const TypeAliasDecl& tad) { bool found = false; Walker walker(tad.type.get(), [&node, &found](Ptr n) -> VisitAction { if (n == &node) { found = true; return VisitAction::STOP_NOW; } return VisitAction::WALK_CHILDREN; }); walker.Walk(); return found; } bool IsTargetVisibleToNode(const Decl& target, const Node& node) { // In the LSP, the 'node' may be a new ast node, 'curFile' pointer consistency cannot be ensured. return !target.TestAttr(Attribute::PRIVATE) || (target.curFile && node.curFile && *target.curFile == *node.curFile); } } // namespace bool LookUpImpl::FindRealResult(const Node& node, bool isSetter, std::vector>& results, std::multimap>& resultsMap, bool isInDeclBody) { // If previous found targets are not empty and not all function decls, no need to find more from parent scope. bool wasAllFunction = !results.empty() && IsAllFuncDecl(results); if (!results.empty() && !wasAllFunction) { return true; } for (auto it : std::as_const(resultsMap)) { auto targetDecl = it.second; // Compiler added init FuncParam, and it's RHS expr of Assignment. bool initFuncParam = targetDecl->begin == INVALID_POSITION && node.begin == INVALID_POSITION; // Compiler added init LHS expr of Assignment. bool initAssignLHS = node.begin == INVALID_POSITION && node.scopeLevel > targetDecl->scopeLevel; if (targetDecl->astKind == ASTKind::VAR_DECL) { auto vd = RawStaticCast(targetDecl); initAssignLHS = initAssignLHS || vd->isResourceVar; } // Toplevel decls, static decls, compiler added init parameter and left expression are not order related. bool orderRelated = !initFuncParam && !initAssignLHS && !(targetDecl->scopeLevel == 0 || isInDeclBody); if ((orderRelated && IsDefinedAfter(*targetDecl, node)) || IgnoredMember(*targetDecl)) { continue; // Ignore target defined after reference node. } // If found targets in parent scope are all function decls, // stop finding other result from current scope when any non-function is found. if ((wasAllFunction && !targetDecl->IsFunc())) { return true; } if (targetDecl->astKind == ASTKind::TYPE_ALIAS_DECL) { // Should not put the target type alias which the initializer include this node. auto tad = RawStaticCast(targetDecl); if (IsNodeInTypeAliasDecl(node, *tad)) { continue; } } if (!Is(targetDecl)) { results.emplace_back(RawStaticCast(targetDecl)); continue; } // Should not put the target varDecl which the initializer include this node. auto vd = RawStaticCast(targetDecl); if (targetDecl->astKind != ASTKind::PROP_DECL && (IsNodeInVarDecl(ctx, node, *vd) || IsNodeInDestructed(node, *vd))) { continue; } if (targetDecl->astKind == ASTKind::PROP_DECL) { auto pd = RawStaticCast(targetDecl); auto needContinue = (isSetter && pd->isVar && pd->setters.empty()) || (!isSetter && pd->getters.empty()); if (needContinue) { continue; } } results.emplace_back(vd); // If the node is a RefExpr of CallExpr, we should continue to collect all candidate Decls. if (auto refExpr = DynamicCast(&node); refExpr && refExpr->callOrPattern) { continue; } // Otherwise, we only collect one VarDecl by shadow rules. return true; } return false; } void LookUpImpl::ProcessStructDeclBody( const std::string& name, const std::string& scopeName, const Node& node, std::vector>& results) { // Lookup for inherited members, eg: // 1. from subclass body finding any member from parent class // 2. from extend body finding any member from the extened type decl. auto parentScopeName = ScopeManagerApi::GetParentScopeName(scopeName); auto parentSopeGateName = ScopeManagerApi::GetScopeGateName(parentScopeName); auto parentScopeGateSym = ScopeManagerApi::GetScopeGate(ctx, parentSopeGateName); if (parentScopeGateSym == nullptr) { return; } CJC_NULLPTR_CHECK(node.curFile); auto currentDecl = StaticCast(parentScopeGateSym->node); LookupInfo info{ .baseTy = currentDecl->ty, .file = node.curFile, .lookupExtend = currentDecl->astKind == ASTKind::EXTEND_DECL}; auto typeDecl = Ty::GetDeclPtrOfTy(currentDecl->ty); if (!typeDecl) { // Lookup for extend of builtin type. FieldLookupExtend(*currentDecl->ty, name, results, info); return; } auto fields = FieldLookup(typeDecl, name, info); for (auto it : fields) { if (auto vd = DynamicCast(it); vd && it->astKind != ASTKind::PROP_DECL && IsNodeInVarDecl(ctx, node, *vd)) { continue; } else { results.emplace_back(it); } } } bool LookUpImpl::LookupImpl(const std::string& name, std::string scopeName, const Node& node, bool onlyLookUpTopLevel, bool isSetter, std::vector>& results) { do { auto targetDecls = ctx.GetDeclsByName({name, scopeName}); std::multimap> resultsMap; for (auto decl : targetDecls) { CJC_NULLPTR_CHECK(decl); if (IsTargetVisibleToNode(*decl, node)) { resultsMap.emplace(decl->begin, decl); } } auto scopeGateName = ScopeManagerApi::GetScopeGateName(scopeName); auto scopeGateSym = ScopeManagerApi::GetScopeGate(ctx, scopeGateName); bool isInDeclBody = scopeGateSym != nullptr && scopeGateSym->node != nullptr && (scopeGateSym->node->IsNominalDeclBody() || scopeGateSym->node->TestAttr(Attribute::IN_EXTEND) || scopeGateSym->node->astKind == ASTKind::ENUM_DECL); if (FindRealResult(node, isSetter, results, resultsMap, isInDeclBody)) { return true; } // onlyLookUpTopLevel is a flag to mark that the LookUp is invoked at resolve decls stage of PreCheck, the // reference type must be at top-level. if (scopeGateSym && scopeGateSym->node && scopeGateSym->node->IsNominalDeclBody() && !onlyLookUpTopLevel) { ProcessStructDeclBody(name, scopeName, node, results); } scopeName = ScopeManagerApi::GetParentScopeName(scopeName); if (!results.empty() && Is(results[0])) { // For var, only find the nearest targets. return true; } } while (!scopeName.empty()); return false; } std::vector> LookUpImpl::Lookup( const std::string& name, const std::string& scopeName, const Node& node, bool onlyLookUpTopLevel, bool isSetter) { std::vector> results = StdLibFieldLookup(node, name); if (!results.empty()) { return results; } if (name == INVALID_IDENTIFIER) { return results; } if (scopeName.empty()) { diag.Diagnose(node, DiagKind::sema_symbol_not_collected, name); return results; } if (LookupImpl(name, scopeName, node, onlyLookUpTopLevel, isSetter, results)) { return results; } // If the targets is not empty and the target is not function but other decls, no need to search in imported // decl collections. if (!results.empty() && !IsAllFuncDecl(results)) { return results; } else { // Insert import symbols (already sorted by API). auto importDecls = importManager.GetImportedDeclsByName(*node.curFile, name); results.insert(results.end(), importDecls.begin(), importDecls.end()); } // Remove duplicate function declarations. for (auto it = results.begin(); it != results.end(); ++it) { bool self = true; for (auto i = it; i != results.end();) { if (self) { self = false; } else if (*it == *i) { i = results.erase(i); continue; } ++i; } } return results; } std::vector> TypeChecker::TypeCheckerImpl::FieldLookup( const ASTContext& ctx, Ptr decl, const std::string& fieldName, const LookupInfo& info) { LookUpImpl lookUpImpl(ctx, diag, typeManager, importManager); return lookUpImpl.FieldLookup(decl, fieldName, info); } std::vector> TypeChecker::TypeCheckerImpl::Lookup( const ASTContext& ctx, const std::string& name, const std::string& scopeName, const Node& node, bool isSetter) { LookUpImpl lookUpImpl(ctx, diag, typeManager, importManager); return lookUpImpl.Lookup(name, scopeName, node, false, isSetter); } std::vector> TypeChecker::TypeCheckerImpl::LookupTopLevel( const ASTContext& ctx, const std::string& name, const std::string& scopeName, const Node& node, bool isSetter) { LookUpImpl lookUpImpl(ctx, diag, typeManager, importManager); return lookUpImpl.Lookup(name, scopeName, node, true, isSetter); } std::vector> TypeChecker::TypeCheckerImpl::ExtendFieldLookup( const ASTContext& ctx, const File& file, Ptr ty, const std::string& fieldName) { LookUpImpl lookUpImpl(ctx, diag, typeManager, importManager); std::vector> results = {}; if (Ty::IsTyCorrect(ty)) { LookupInfo info{ty, &file}; lookUpImpl.FieldLookupExtend(*ty, fieldName, results, info); } return results; } cangjie_compiler-1.0.7/src/Sema/MultiTypeSubstUtils.cpp000066400000000000000000000357051510705540100231770ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the utility functions for manipulating 'MultiTypeSubst' */ #include "TypeCheckUtil.h" namespace Cangjie::TypeCheckUtil { using namespace AST; namespace { std::set> GetDirectMappingTys( Ptr const tyVar, const MultiTypeSubst& mts) { std::vector> stack{tyVar}; std::set> res; auto mapping = mts; while (!stack.empty()) { auto curTy = stack.back(); stack.pop_back(); MultiTypeSubst::const_iterator found = mapping.find(curTy); if (found == mapping.cend()) { continue; } auto targetTys = found->second; mapping.erase(found->first); // Erase current substitution from mapping, avoid circular substitution. targetTys.erase(curTy); // Ignore self mapping. if (targetTys.empty()) { continue; } for (auto ty : targetTys) { if (auto genTy = DynamicCast(ty)) { stack.emplace_back(genTy); } } // Update result set. res.erase(curTy); res.insert(targetTys.begin(), targetTys.end()); } return res; } // remove mappings not useful(either directly or transitively used) in tys SubstPack FilterUnusedMapping(const SubstPack& mapping, const std::set>& tys) { std::set> allu; std::set> alli; std::set> reachable; std::queue> worklist; // collect all tyvars for (auto [tvu, tvi] : mapping.u2i) { allu.insert(tvu); alli.emplace(StaticCast(tvi)); } // collect inst tyvars directly used for (auto ty : tys) { for (auto tvu : ty->GetGenericTyArgs(allu)) { reachable.emplace(StaticCast(mapping.u2i.at(tvu))); } } for (auto tvi : reachable) { worklist.push(tvi); } // collect inst tyvars indirected used while (!worklist.empty()) { auto tvi = worklist.front(); worklist.pop(); if (mapping.inst.count(tvi) > 0) { for (auto instTy : mapping.inst.at(tvi)) { reachable.merge(instTy->GetGenericTyArgs(alli)); } } } // return useful mappings SubstPack ret; for (auto [tvu, tvi] : mapping.u2i) { if (reachable.count(StaticCast(tvi)) > 0) { ret.u2i[tvu] = tvi; } } for (auto [tvi, instTys] : mapping.inst) { if (reachable.count(tvi) > 0) { ret.inst[tvi] = instTys; } } return ret; } MultiTypeSubst FilterUnusedMapping(const MultiTypeSubst& mapping, const std::set>& tys) { std::set> all = Utils::GetKeys(mapping); std::set> reachable; std::queue> worklist; // collect tyvars directly used for (auto ty : tys) { reachable.merge(ty->GetGenericTyArgs(all)); } for (auto tv : reachable) { worklist.push(tv); } // collect inst tyvars indirected used while (!worklist.empty()) { auto tv = worklist.front(); worklist.pop(); if (mapping.count(tv) > 0) { for (auto instTy : mapping.at(tv)) { reachable.merge(instTy->GetGenericTyArgs(all)); } } } // return useful mappings MultiTypeSubst ret; for (auto [tv, instTys] : mapping) { if (reachable.count(tv) > 0) { ret[tv] = instTys; } } return ret; } std::set ExpandFilteredMultiTypeSubst(const MultiTypeSubst& mts) { if (mts.empty()) { return {{}}; // If TypeSubst is empty, must return set with empty TypeSubst. } std::set res; std::vector> keys; std::for_each(mts.begin(), mts.end(), [&keys](auto& it) { keys.emplace_back(it.first); }); size_t numKeys = keys.size(); // Expand every type mapping possibilities. std::function expand = [&expand, &mts, &numKeys, &keys, &res](TypeSubst& mapping) { size_t cur = mapping.size(); auto key = keys[cur]; for (auto& it : mts.at(key)) { mapping[key] = it; if (cur + 1 == numKeys) { res.emplace(mapping); } else { expand(mapping); } mapping.erase(key); } }; TypeSubst mapping; expand(mapping); return res; } } // namespace TypeSubst MultiTypeSubstToTypeSubst(const MultiTypeSubst& mts) { TypeSubst m; std::for_each(mts.cbegin(), mts.cend(), [&m](auto& kv) { auto values = kv.second; if (values.size() > 1) { values.erase(kv.first); // Avoid choosing self mapping when there are more than one candidates. } m[kv.first] = *values.begin(); }); return m; } std::vector> GetDeclTypeParams(const Decl& decl) { if (decl.astKind == ASTKind::EXTEND_DECL) { CJC_NULLPTR_CHECK(decl.ty); return decl.ty->typeArgs; } std::vector> ret; auto generic = decl.GetGeneric(); if (!generic) { return ret; } for (auto& it : generic->typeParameters) { ret.emplace_back(it->ty); } return ret; } std::unordered_set> GetAllGenericTys(Ptr const ty) { std::unordered_set> res; std::unordered_set> visited; std::queue> q; q.emplace(ty); while (!q.empty()) { auto curTy = q.front(); q.pop(); if (auto [_, succ] = visited.emplace(curTy); !succ) { continue; } if (curTy->IsGeneric()) { res.emplace(curTy); continue; } for (auto it : curTy->typeArgs) { q.emplace(it); } } return res; } MultiTypeSubst ReduceMultiTypeSubst(TypeManager& tyMgr, const TyVars& tyVars, const MultiTypeSubst& mts) { if (tyVars.empty()) { return {}; } auto mapping = mts; // Erase self-reference mappings. std::unordered_set> visited; for (auto& p : mts) { for (Ptr ty : p.second) { if (auto [_, succ] = visited.emplace(ty); !succ) { continue; } auto gtys = GetAllGenericTys(ty); if (gtys.find(p.first) != gtys.cend()) { mapping.erase(p.first); } } } MultiTypeSubst res; for (auto tyVar : tyVars) { auto targetTys = GetDirectMappingTys(tyVar, mts); mapping.erase(tyVar); // Erase current substitution from mapping. if (!targetTys.empty()) { res.emplace(tyVar, targetTys); } } for (auto& it : res) { std::set> targetRes; for (auto& ty : it.second) { targetRes.merge(tyMgr.GetInstantiatedTys(ty, mapping)); } it.second = targetRes; } return res; } std::vector ExpandMultiTypeSubst(const SubstPack& maps, const std::set>& usefulTys) { std::vector ret; auto filtered = FilterUnusedMapping(maps, usefulTys); for (auto m : ExpandFilteredMultiTypeSubst(filtered.inst)) { SubstPack mp; mp.u2i = filtered.u2i; MergeTypeSubstToMultiTypeSubst(mp.inst, m); ret.push_back(mp); } return ret; } std::set ExpandMultiTypeSubst(const MultiTypeSubst& mts, const std::set>& usefulTys) { auto filtered = FilterUnusedMapping(mts, usefulTys); return ExpandFilteredMultiTypeSubst(filtered); } Ptr GetMappedTy(const MultiTypeSubst& mts, TyVar* tyVar) { auto found = mts.find(tyVar); if (found != mts.end()) { for (auto ty : found->second) { if (ty != tyVar) { return ty; } } } return tyVar; } Ptr GetMappedTy(const TypeSubst& typeMapping, TyVar* tyVar) { auto found = typeMapping.find(tyVar); if (found != typeMapping.end()) { return found->second; } return tyVar; } namespace { void InsertInstMapping(TypeManager& tyMgr, SubstPack& m, GenericsTy& genTy, Ty& instTy) { CJC_ASSERT(!genTy.isPlaceholder); if (m.u2i.count(&genTy) == 0) { m.u2i[&genTy] = tyMgr.AllocTyVar(); } m.inst[StaticCast(m.u2i[&genTy])].emplace(&instTy); } TypeSubst GenerateTypeMappingByArgs(const std::vector>& srcArgs, const std::vector>& instantiateArgs) { if (srcArgs.size() != instantiateArgs.size()) { return {}; } TypeSubst typeMapping; for (size_t i = 0; i < srcArgs.size(); ++i) { if (auto genTy = DynamicCast(srcArgs[i])) { typeMapping.emplace(genTy, instantiateArgs[i]); } else { if (srcArgs[i]->kind != instantiateArgs[i]->kind || Ty::GetDeclPtrOfTy(srcArgs[i]) != Ty::GetDeclPtrOfTy(instantiateArgs[i])) { continue; } typeMapping.merge(GenerateTypeMappingByArgs(srcArgs[i]->typeArgs, instantiateArgs[i]->typeArgs)); } } return typeMapping; } /** * Find mappings from partially instantiated ty args to fully instantiated ty args, recursively * e.g. * class C {} * extend C> {} * let c = C>() * * for `C>` against the extension, the inputs are: * srcArgs: [R1, Array] * instantiateArgs: [String, Array] * The resulting maps: * u2i: [R1 |-> R1', R2 |-> R2'] * inst: [R1' |-> String, R2' |-> Int] */ void GenerateTypeMappingByArgs( TypeManager& tyMgr, SubstPack& m, const std::vector>& srcArgs, const std::vector>& instantiateArgs) { if (srcArgs.size() != instantiateArgs.size()) { return; } TypeSubst typeMapping; for (size_t i = 0; i < srcArgs.size(); ++i) { if (auto genTy = DynamicCast(srcArgs[i])) { InsertInstMapping(tyMgr, m, *genTy, *instantiateArgs[i]); } else { if (srcArgs[i]->kind != instantiateArgs[i]->kind || Ty::GetDeclPtrOfTy(srcArgs[i]) != Ty::GetDeclPtrOfTy(instantiateArgs[i])) { continue; } GenerateTypeMappingByArgs(tyMgr, m, srcArgs[i]->typeArgs, instantiateArgs[i]->typeArgs); } } } } // namespace TypeSubst GenerateTypeMappingByTy(const Ptr genericTy, const Ptr instantTy) { if (!genericTy || !instantTy) { return {}; } if (!genericTy->IsGeneric() && (genericTy->kind != instantTy->kind || Ty::GetDeclPtrOfTy(genericTy) != Ty::GetDeclPtrOfTy(instantTy))) { return {}; } return GenerateTypeMappingByArgs({genericTy}, {instantTy}); } /** * Generate type mapping **directly** from the decl to the given type args. * If the decl is an extension, then it's the mapping from the extended type to the type args. * It doesn't include mapping from the decl of the extended type to the extension, or mapping * for the entire inheritance chain. * See `GenerateTypeMappingByArgs` for an example. */ void GenerateTypeMapping(TypeManager& tyMgr, SubstPack& m, const Decl& decl, const std::vector>& typeArgs) { auto generic = decl.GetGeneric(); if (!generic) { return; } if (decl.astKind == ASTKind::EXTEND_DECL) { auto extendedTypeArgs = StaticCast(decl).extendedType->ty->typeArgs; GenerateTypeMappingByArgs(tyMgr, m, extendedTypeArgs, typeArgs); return; } if (generic->typeParameters.size() == typeArgs.size()) { for (size_t i = 0; i < typeArgs.size(); ++i) { if (Ty::IsTyCorrect(generic->typeParameters[i]->ty) && Ty::IsTyCorrect(typeArgs[i])) { auto genTy = StaticCast(generic->typeParameters[i]->ty); InsertInstMapping(tyMgr, m, *genTy, *typeArgs[i]); } } } } /** * Given an extend whose generic parameter's mapping to instantiated types already exists in `m`, * generated type mapping from the original type decl to this extend, using the inst ty vars for the extendedType. * * E.g, given: * class A {} * extend A> {} * * Will newly generate: * u2i: [T |-> T'] * inst: [T' |-> Option] */ void RelayMappingFromExtendToExtended(TypeManager& tyMgr, SubstPack& m, const ExtendDecl& decl) { auto target = decl.extendedType->GetTarget(); if (!target) { return; } auto originalTypeArgs = GetRealTarget(target)->ty->typeArgs; auto extendedTypeArgsInst = decl.extendedType->ty->typeArgs; for (auto& ty : extendedTypeArgsInst) { ty = tyMgr.GetInstantiatedTy(ty, m.u2i); } GenerateTypeMappingByArgs(tyMgr, m, originalTypeArgs, extendedTypeArgsInst); } TypeSubst GenerateTypeMapping(const Decl& decl, const std::vector>& typeArgs) { TypeSubst substituteMapping; auto generic = decl.GetGeneric(); if (!generic) { return substituteMapping; } if (decl.astKind == ASTKind::EXTEND_DECL) { auto extendedTypeArgs = StaticCast(decl).extendedType->ty->typeArgs; return GenerateTypeMappingByArgs(extendedTypeArgs, typeArgs); } if (generic->typeParameters.size() == typeArgs.size()) { for (size_t i = 0; i < typeArgs.size(); ++i) { if (Ty::IsTyCorrect(generic->typeParameters[i]->ty) && Ty::IsTyCorrect(typeArgs[i])) { // could be used by instantiated decl, therefore need to check if (auto declGenParam = DynamicCast(generic->typeParameters[i]->ty)) { substituteMapping[declGenParam] = typeArgs[i]; } } } } return substituteMapping; } TypeSubst InverseMapping(const TypeSubst& typeMapping) { TypeSubst inversed; for (auto [from, to] : typeMapping) { if (auto genTo = DynamicCast(to)) { inversed.emplace(genTo, from); } } return inversed; } void MergeTypeSubstToMultiTypeSubst(MultiTypeSubst& mts, const TypeSubst& typeMapping) { for (auto it : typeMapping) { mts[it.first].emplace(it.second); } } void MergeMultiTypeSubsts(MultiTypeSubst& target, const MultiTypeSubst& src) { for (auto it : src) { if (it.second.empty()) { continue; } target[it.first].merge(it.second); } } bool HaveCyclicSubstitution(TypeManager& tyMgr, const TypeSubst& typeMapping) { auto keys = Utils::GetKeys(typeMapping); for (auto tyVar : keys) { auto mapping = typeMapping; auto target = mapping[tyVar]; (void)mapping.erase(tyVar); auto substitutedTy = tyMgr.GetInstantiatedTy(target, mapping); // eg: {X->Y, Y->E} will generate 'X->E' which may cause infinite substitution. bool recursived = substitutedTy != tyVar && substitutedTy->Contains(tyVar); if (recursived) { return true; } } return false; } } // namespace Cangjie::TypeCheckUtil cangjie_compiler-1.0.7/src/Sema/NativeFFI/000077500000000000000000000000001510705540100202365ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/NativeFFI/CMakeLists.txt000066400000000000000000000006201510705540100227740ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB SEMA_NATIVE_FFI_SRC *.cpp) add_subdirectory(Java) add_subdirectory(ObjC) set(SEMA_SRC ${SEMA_SRC} ${SEMA_NATIVE_FFI_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/000077500000000000000000000000001510705540100211175ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/AfterTypeCheck/000077500000000000000000000000001510705540100237605ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/AfterTypeCheck/CMakeLists.txt000066400000000000000000000006541510705540100265250ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB SEMA_NATIVE_FFI_JAVA_AFTER_TYPECHECK_SRC *.cpp) set(SEMA_NATIVE_FFI_JAVA_SRC ${SEMA_NATIVE_FFI_JAVA_SRC} ${SEMA_NATIVE_FFI_JAVA_AFTER_TYPECHECK_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/AfterTypeCheck/DesugarCJMapping.cpp000066400000000000000000000154511510705540100276150ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "JavaDesugarManager.h" #include "NativeFFI/Java/JavaCodeGenerator/JavaSourceCodeGenerator.h" #include "Utils.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" namespace Cangjie::Interop::Java { using namespace Cangjie::Native::FFI; // Support Struct decl and Enum decl for now. OwnedPtr JavaDesugarManager::GenerateCJMappingNativeDeleteCjObjectFunc(Decl& decl) { std::vector> params; FuncParam* jniEnvPtrParam = nullptr; OwnedPtr selfParamRef; GenerateFuncParamsForNativeDeleteCjObject(decl, params, jniEnvPtrParam, selfParamRef); auto removeFromRegistryCall = lib.CreateRemoveFromRegistryCall(std::move(selfParamRef)); auto wrappedNodesLambda = WrapReturningLambdaExpr(typeManager, Nodes(std::move(removeFromRegistryCall))); Ptr unitTy = typeManager.GetPrimitiveTy(TypeKind::TYPE_UNIT).get(); auto funcName = GetJniDeleteCjObjectFuncName(decl); std::vector> paramLists; paramLists.push_back(CreateFuncParamList(std::move(params))); return GenerateNativeFuncDeclBylambda(decl, wrappedNodesLambda, paramLists, *jniEnvPtrParam, unitTy, funcName); } void JavaDesugarManager::GenerateForCJStructMapping(AST::StructDecl* structDecl) { CJC_ASSERT(structDecl && IsCJMapping(*structDecl)); std::vector generatedCtors; for (auto& member : structDecl->GetMemberDecls()) { if (member->TestAnyAttr(Attribute::IS_BROKEN, Attribute::PRIVATE, Attribute::PROTECTED, Attribute::INTERNAL)) { continue; } if (auto fd = As(member.get())) { if (fd->TestAttr(Attribute::CONSTRUCTOR)) { generatedCtors.push_back(fd); } else { auto nativeMethod = GenerateNativeMethod(*fd, *structDecl); if (nativeMethod != nullptr) { generatedDecls.push_back(std::move(nativeMethod)); } } } } if (!generatedCtors.empty()) { generatedDecls.push_back(GenerateCJMappingNativeDeleteCjObjectFunc(*structDecl)); for (auto generatedCtor : generatedCtors) { generatedDecls.push_back(GenerateNativeInitCjObjectFunc(*generatedCtor, false)); } } } OwnedPtr JavaDesugarManager::GenerateNativeInitCjObjectFuncForEnumCtorNoParams( AST::EnumDecl& enumDecl, AST::VarDecl& ctor) { // Empty params to build constructor from VarDecl. std::vector> params; std::vector> ctorCallArgs; PushEnvParams(params, "env"); auto curFile = ctor.curFile; CJC_NULLPTR_CHECK(curFile); auto& jniEnvPtrParam = *(params[0]); std::vector> paramLists; paramLists.push_back(CreateFuncParamList(std::move(params))); auto enumRef = WithinFile(CreateRefExpr(enumDecl), curFile); auto objectCtorCall = CreateMemberAccess(std::move(enumRef), ctor.identifier); auto putToRegistryCall = lib.CreatePutToRegistryCall(std::move(objectCtorCall)); auto bodyLambda = WrapReturningLambdaExpr(typeManager, Nodes(std::move(putToRegistryCall))); auto jlongTy = lib.GetJlongTy(); auto funcName = GetJniInitCjObjectFuncNameForVarDecl(ctor); return GenerateNativeFuncDeclBylambda(ctor, bodyLambda, paramLists, jniEnvPtrParam, jlongTy, funcName); } void JavaDesugarManager::GenerateNativeInitCJObjectEnumCtor(AST::EnumDecl& enumDecl) { auto nativeMethod = MakeOwned(); for (auto& ctor : enumDecl.constructors) { if (ctor->astKind == ASTKind::FUNC_DECL) { auto fd = As(ctor.get()); CJC_NULLPTR_CHECK(fd); nativeMethod = GenerateNativeInitCjObjectFunc(*fd, false); } else if (ctor->astKind == ASTKind::VAR_DECL) { auto varDecl = As(ctor.get()); CJC_NULLPTR_CHECK(varDecl); nativeMethod = GenerateNativeInitCjObjectFuncForEnumCtorNoParams(enumDecl, *varDecl); } generatedDecls.push_back(std::move(nativeMethod)); } } void JavaDesugarManager::GenerateForCJEnumMapping(AST::EnumDecl& enumDecl) { CJC_ASSERT(IsCJMapping(enumDecl)); GenerateNativeInitCJObjectEnumCtor(enumDecl); for (auto& member : enumDecl.GetMemberDecls()) { if (member->TestAttr(Attribute::IS_BROKEN) || !member->TestAttr(Attribute::PUBLIC)) { continue; } if (auto fd = As(member.get())) { generatedDecls.push_back(GenerateNativeMethod(*fd, enumDecl)); } else if (member->astKind == ASTKind::PROP_DECL && !member->TestAttr(Attribute::COMPILER_ADD)) { const PropDecl& propDecl = *StaticAs(member.get()); const OwnedPtr& funcDecl = propDecl.getters[0]; auto getSignature = GetJniMethodNameForProp(propDecl, false); auto nativeMethod = GenerateNativeMethod(*funcDecl.get(), enumDecl); if (nativeMethod != nullptr) { nativeMethod->identifier = getSignature; generatedDecls.push_back(std::move(nativeMethod)); } } } generatedDecls.push_back(GenerateCJMappingNativeDeleteCjObjectFunc(enumDecl)); } void JavaDesugarManager::GenerateInCJMapping(File& file) { for (auto& decl : file.decls) { if (!decl.get()->TestAttr(Attribute::PUBLIC) || decl.get()->TestAttr(Attribute::IS_BROKEN)) { continue; } auto astDecl = As(decl.get()); if (astDecl && astDecl->TestAttr(Attribute::IS_BROKEN)) { continue; } auto structDecl = As(decl.get()); if (structDecl && IsCJMapping(*structDecl)) { GenerateForCJStructMapping(structDecl); } auto enumDecl = As(decl.get()); if (enumDecl && IsCJMapping(*enumDecl)) { GenerateForCJEnumMapping(*enumDecl); } } } void JavaDesugarManager::DesugarInCJMapping(File& file) { for (auto& decl : file.decls) { if (!decl.get()->TestAttr(Attribute::PUBLIC) || decl.get()->TestAttr(Attribute::IS_BROKEN) || !JavaSourceCodeGenerator::IsDeclAppropriateForGeneration(*decl.get()) || !IsCJMapping(*decl.get())) { continue; } const std::string fileJ = decl.get()->identifier.Val() + ".java"; auto codegen = JavaSourceCodeGenerator(decl.get(), mangler, javaCodeGenPath, fileJ, GetCangjieLibName(outputLibPath, decl.get()->GetFullPackageName())); codegen.Generate(); } } } // namespace Cangjie::Interop::Java cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/AfterTypeCheck/DesugarCommonAPI.cpp000066400000000000000000000257141510705540100275720ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "JavaDesugarManager.h" #include "NativeFFI/Java/JavaCodeGenerator/JavaSourceCodeGenerator.h" #include "Utils.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" namespace Cangjie::Interop::Java { using namespace Cangjie::Native::FFI; inline void JavaDesugarManager::PushEnvParams(std::vector>& params, std::string name) { auto jniEnvPtrDecl = lib.GetJniEnvPtrDecl(); CJC_NULLPTR_CHECK(jniEnvPtrDecl); params.push_back(CreateFuncParam(name, ASTCloner::Clone(jniEnvPtrDecl->type.get()), nullptr, lib.GetJNIEnvPtrTy())); } inline void JavaDesugarManager::PushObjParams(std::vector>& params, std::string name) { params.push_back(CreateFuncParam(name, lib.CreateJobjectType(), nullptr, lib.GetJobjectTy())); } inline void JavaDesugarManager::PushSelfParams(std::vector>& params, std::string name) { params.push_back(CreateFuncParam(name, lib.CreateJlongType(), nullptr, lib.GetJlongTy())); } bool JavaDesugarManager::FillMethodParamsByArg(std::vector>& params, std::vector>& callArgs, FuncDecl& funcDecl, OwnedPtr& arg, FuncParam& jniEnvPtrParam) { CJC_NULLPTR_CHECK(funcDecl.curFile); auto jniArgTy = GetJNITy(arg->ty); OwnedPtr param = CreateFuncParam(arg->identifier.GetRawText(), nullptr, nullptr, jniArgTy); auto classLikeTy = DynamicCast(arg->ty); if (classLikeTy && !classLikeTy->commonDecl) { return false; } auto outerDecl = funcDecl.outerDecl; auto paramRef = WithinFile(CreateRefExpr(*param), funcDecl.curFile); OwnedPtr methodArg; if (IsMirror(*arg->ty)) { auto entity = lib.CreateJavaEntityJobjectCall(std::move(paramRef)); methodArg = CreateFuncArg(CreateMirrorConstructorCall(importManager, std::move(entity), arg->ty)); } else if (IsImpl(*arg->ty)) { auto entity = lib.CreateJavaEntityJobjectCall(std::move(paramRef)); methodArg = CreateFuncArg(lib.UnwrapJavaEntity(std::move(entity), arg->ty, *outerDecl)); } else if (arg->ty->IsCoreOptionType() && IsMirror(*arg->ty->typeArgs[0])) { // funcDecl(Java_CFFI_JavaEntity(arg)) // if arg is null (as jobject == 0) -> java entity will preserve it auto entity = lib.CreateJavaEntityJobjectCall(std::move(paramRef)); methodArg = CreateFuncArg(lib.UnwrapJavaEntity(std::move(entity), arg->ty, *outerDecl)); } else if (arg->ty->IsCoreOptionType() && IsImpl(*arg->ty->typeArgs[0])) { auto entity = lib.CreateJavaEntityJobjectCall(std::move(paramRef)); methodArg = CreateFuncArg(lib.UnwrapJavaEntity(std::move(entity), arg->ty, *outerDecl)); } else if (IsCJMapping(*arg->ty)) { auto entity = lib.CreateGetFromRegistryCall( WithinFile(CreateRefExpr(jniEnvPtrParam), funcDecl.curFile), std::move(paramRef), arg->ty); methodArg = CreateFuncArg(WithinFile(std::move(entity), funcDecl.curFile)); } else { methodArg = CreateFuncArg(std::move(paramRef)); } params.push_back(std::move(param)); callArgs.push_back(std::move(methodArg)); return true; } OwnedPtr JavaDesugarManager::GenerateNativeMethod(FuncDecl& sampleMethod, Decl& decl) { auto curFile = sampleMethod.curFile; CJC_NULLPTR_CHECK(curFile); auto retTy = StaticCast(sampleMethod.ty)->retTy; std::vector> params; PushEnvParams(params); // jobject or jclass PushObjParams(params, "_"); auto& jniEnvPtrParam = *params[0]; if (!sampleMethod.TestAttr(Attribute::STATIC)) { PushSelfParams(params); } auto& selfParam = *params.back(); std::vector> methodCallArgs; for (auto& arg : sampleMethod.funcBody->paramLists[0]->params) { if (!FillMethodParamsByArg(params, methodCallArgs, sampleMethod, arg, jniEnvPtrParam)) { return nullptr; } } OwnedPtr methodAccess; if (sampleMethod.TestAttr(Attribute::STATIC)) { methodAccess = CreateMemberAccess(WithinFile(CreateRefExpr(decl), curFile), sampleMethod); } else { auto reg = lib.CreateGetFromRegistryCall( WithinFile(CreateRefExpr(jniEnvPtrParam), curFile), WithinFile(CreateRefExpr(selfParam), curFile), decl.ty); methodAccess = CreateMemberAccess(std::move(reg), sampleMethod); } methodAccess->curFile = curFile; auto methodCall = CreateCallExpr(std::move(methodAccess), std::move(methodCallArgs), Ptr(&sampleMethod), retTy, CallKind::CALL_DECLARED_FUNCTION); auto methodCallRes = CreateTmpVarDecl(nullptr, std::move(methodCall)); methodCallRes->ty = retTy; OwnedPtr retExpr; auto createCJMappingCall = [&library = this->lib, &jniEnvPtrParam, &curFile, &methodCallRes, &retExpr]( std::string& clazzName, bool needCtorArgs) { std::replace(clazzName.begin(), clazzName.end(), '.', '/'); auto entity = library.CreateCFFINewJavaCFFINewJavaProxyObjectForCJMappingCall( WithinFile(CreateRefExpr(jniEnvPtrParam), curFile), WithinFile(CreateRefExpr(*methodCallRes), curFile), clazzName, needCtorArgs); retExpr = WithinFile(std::move(entity), curFile); }; if (retTy->IsPrimitive()) { retExpr = WithinFile(CreateRefExpr(*methodCallRes), curFile); } else if (IsCJMapping(*retTy)) { if (auto retStructTy = DynamicCast(retTy)) { std::string clazzName = retStructTy->decl->fullPackageName + "." + retTy->name; createCJMappingCall(clazzName, true); } else if (auto retEnumTy = DynamicCast(retTy)) { std::string clazzName = retEnumTy->decl->fullPackageName + "." + retTy->name; createCJMappingCall(clazzName, false); } } else { OwnedPtr methodResRef = WithinFile(CreateRefExpr(*methodCallRes), curFile); auto entity = lib.WrapJavaEntity(std::move(methodResRef)); retExpr = lib.UnwrapJavaEntity(std::move(entity), retTy, *(sampleMethod.outerDecl), true); } auto wrappedNodesLambda = WrapReturningLambdaExpr(typeManager, Nodes(std::move(methodCallRes), std::move(retExpr))); std::string funcName = GetJniMethodName(sampleMethod); std::vector> paramLists; paramLists.push_back(CreateFuncParamList(std::move(params))); return GenerateNativeFuncDeclBylambda(decl, wrappedNodesLambda, paramLists, jniEnvPtrParam, retTy, funcName); } void JavaDesugarManager::GenerateFuncParamsForNativeDeleteCjObject( Decl& decl, std::vector>& params, FuncParam*& jniEnv, OwnedPtr& selfRef) { PushEnvParams(params); PushObjParams(params); PushSelfParams(params); jniEnv = &(*params[0]); CJC_NULLPTR_CHECK(decl.curFile); constexpr int SELF_REF_INDEX = 2; selfRef = WithinFile(CreateRefExpr(*params[SELF_REF_INDEX]), decl.curFile); } OwnedPtr JavaDesugarManager::GenerateNativeFuncDeclBylambda(Decl& decl, OwnedPtr& wrappedNodesLambda, std::vector>& paramLists, FuncParam& jniEnvPtrParam, Ptr& retTy, std::string funcName) { CJC_NULLPTR_CHECK(decl.curFile); auto catchingCall = lib.WrapExceptionHandling( WithinFile(CreateRefExpr(jniEnvPtrParam), decl.curFile), std::move(wrappedNodesLambda)); // For ty is CJMapping: // when ty is ArgsTy, we could use the Java_CFFI_getFromRegistry with [id: jlong] to get the cangjie side // struct/class. when ty is RetTy, just use [jobjectTy] for we need JNI to construct the ret object. auto jniRetTy = IsCJMapping(*retTy) ? lib.GetJobjectTy() : GetJNITy(retTy); auto block = CreateBlock(Nodes(std::move(catchingCall)), jniRetTy); auto funcBody = CreateFuncBody(std::move(paramLists), nullptr, std::move(block), jniRetTy); std::vector> funcTyParams; for (auto& param : funcBody->paramLists[0]->params) { funcTyParams.push_back(param->ty); } auto funcTy = typeManager.GetFunctionTy(funcTyParams, jniRetTy, {.isC = true}); auto fdecl = CreateFuncDecl(funcName, std::move(funcBody), funcTy); fdecl->funcBody->funcDecl = fdecl.get(); fdecl->EnableAttr(Attribute::C); fdecl->EnableAttr(Attribute::GLOBAL); fdecl->EnableAttr(Attribute::PUBLIC); fdecl->EnableAttr(Attribute::NO_MANGLE); fdecl->curFile = decl.curFile; fdecl->moduleName = decl.moduleName; fdecl->fullPackageName = decl.fullPackageName; return std::move(fdecl); } /** * when isClassLikeDecl is true: argument ctor: generated constructor mapped with Java_ClassName_initCJObject func * when isClassLikeDecl is false: argument ctor: origin constructor mapped with Java_ClassName_initCJObject func */ OwnedPtr JavaDesugarManager::GenerateNativeInitCjObjectFunc(FuncDecl& ctor, bool isClassLikeDecl) { if (isClassLikeDecl) { CJC_ASSERT(!ctor.funcBody->paramLists[0]->params.empty()); // it contains obj: JavaEntity as minimum } // func decl arguments construction std::vector> params; std::vector> ctorCallArgs; PushEnvParams(params); PushObjParams(params); auto curFile = ctor.curFile; CJC_NULLPTR_CHECK(curFile); auto& jniEnvPtrParam = *(params[0]); auto objParamRef = WithinFile(CreateRefExpr(*params[1]), curFile); if (isClassLikeDecl) { auto objAsEntity = lib.CreateJavaEntityJobjectCall(std::move(objParamRef)); auto objWeakRef = lib.CreateNewGlobalRefCall( WithinFile(CreateRefExpr(jniEnvPtrParam), curFile), std::move(objAsEntity), true); ctorCallArgs.push_back(CreateFuncArg(std::move(objWeakRef))); } for (size_t argIdx = 0; argIdx < ctor.funcBody->paramLists[0]->params.size(); ++argIdx) { auto& arg = ctor.funcBody->paramLists[0]->params[argIdx]; if (isClassLikeDecl && argIdx == 0) { continue; } if (!FillMethodParamsByArg(params, ctorCallArgs, ctor, arg, jniEnvPtrParam)) { return nullptr; } } std::vector> paramLists; paramLists.push_back(CreateFuncParamList(std::move(params))); auto ctorRef = WithinFile(CreateRefExpr(ctor), curFile); auto objectCtorCall = CreateCallExpr( std::move(ctorRef), std::move(ctorCallArgs), Ptr(&ctor), ctor.outerDecl->ty, CallKind::CALL_OBJECT_CREATION); auto putToRegistryCall = lib.CreatePutToRegistryCall(std::move(objectCtorCall)); auto bodyLambda = WrapReturningLambdaExpr(typeManager, Nodes(std::move(putToRegistryCall))); auto jlongTy = lib.GetJlongTy(); auto funcName = GetJniInitCjObjectFuncName(ctor, isClassLikeDecl); return GenerateNativeFuncDeclBylambda(ctor, bodyLambda, paramLists, jniEnvPtrParam, jlongTy, funcName); } } // namespace Cangjie::Interop::Javacangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/AfterTypeCheck/DesugarJavaImpl.cpp000066400000000000000000001177361510705540100275210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "JavaDesugarManager.h" #include "NativeFFI/Java/JavaCodeGenerator/JavaSourceCodeGenerator.h" #include "NativeFFI/Utils.h" #include "Utils.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Walker.h" namespace Cangjie::Interop::Java { using namespace Cangjie::Native::FFI; namespace { // Some helper functions. inline Ptr TryGetSuperCall(const FuncDecl& ctor) { if (!ctor.funcBody) { return nullptr; } if (!ctor.funcBody->body) { return nullptr; } if (ctor.funcBody->body->body.empty()) { return nullptr; } auto firstNode = ctor.funcBody->body->body[0].get(); if (auto callExpr = As(firstNode); callExpr && callExpr->callKind == CallKind::CALL_SUPER_FUNCTION) { return callExpr; } return nullptr; } inline Ptr GetNativeFuncTy(const std::vector>& params, const std::function(const FuncParam&)>& toJni, Ptr jniRet, TypeManager& typeManager) { std::vector> funcTyParams; for (auto& param : params) { funcTyParams.push_back(toJni(*param)); } return typeManager.GetFunctionTy(funcTyParams, jniRet, {.isC = true}); } size_t GetCtorId(const FuncDecl& ctor) { auto decl = As(ctor.outerDecl); CJC_NULLPTR_CHECK(decl); auto& members = decl->GetMemberDecls(); for (size_t i = 0; i < members.size(); i++) { if (&ctor == members[i].get()) { return i; } } return 0; } // Check whether decl is instance member. inline bool IsInstMember(const Decl& decl) { return decl.IsMemberDecl() && !decl.TestAnyAttr(Attribute::STATIC, Attribute::CONSTRUCTOR); } // Check whether the decl is static member. inline bool IsStaticMember(const Decl& decl) { return decl.IsMemberDecl() && decl.TestAttr(Attribute::STATIC); } // Check whether the decl is private static member. inline bool IsPrivateStaticMember(const Ptr decl) { return decl && IsStaticMember(*decl) && decl->TestAttr(Attribute::PRIVATE); } /** * Collecting parameters used by expressions and encoding the ids of using parameter. * * @param expr the expression to be checked * @param params the original parameters of constructor * @param usingParams the result of using params expr * * @return {usingThis, paramIds} whether "this" is used and the paramIds encoding string. */ std::pair CollectParams( Ptr expr, const std::vector>& params, std::vector>& usingParams) { bool usingThis = false; std::set> refParams; Walker(expr, [&usingThis, &refParams](auto node) { auto ref = As(node); if (!ref) { return VisitAction::WALK_CHILDREN; } auto target = ref->ref.target; CJC_NULLPTR_CHECK(target); // explicit and implicit using this if (ref->isThis || IsInstMember(*target)) { usingThis = true; } else if (target->astKind == ASTKind::FUNC_PARAM) { // using parameter refParams.insert(ref->ref.target); } return VisitAction::WALK_CHILDREN; }).Walk(); std::vector ids; for (size_t pi = 0; pi < params.size(); pi++) { if (refParams.find(params[pi].get()) != refParams.end()) { ids.push_back(std::to_string(pi)); usingParams.push_back(params[pi].get()); } } std::string paramIds = ""; if (!ids.empty()) { const std::string psep = "P"; paramIds = psep + Cangjie::Utils::JoinStrings(ids, psep); } return {usingThis, paramIds}; } /** * Collecting using static refs including fields and funcs and check whether using private static members. * * @param expr the expression to be checked * @param staticRefs the result of using static ref exprs * * @return usingPrivate whether using private static members. */ bool CollectStaticRefs(Ptr expr, std::vector>& staticRefs) { bool usingPrivate = false; Walker(expr, [&usingPrivate, &staticRefs](auto node) { if (auto ref = As(node)) { if (ref->desugarExpr) { return VisitAction::WALK_CHILDREN; } auto target = ref->ref.target; CJC_NULLPTR_CHECK(target); if (!IsStaticMember(*target)) { return VisitAction::SKIP_CHILDREN; } if (target->TestAttr(Attribute::PRIVATE)) { usingPrivate = true; } staticRefs.push_back(ref); } else if (auto ma = As(node)) { if (IsPrivateStaticMember(ma->target)) { usingPrivate = true; } } return VisitAction::WALK_CHILDREN; }).Walk(); return usingPrivate; } } /** * Unwrap a refExpr of native func param into Cangjie type. * 1. Primitive Type * 2. JavaMirror/JavaImpl: jobject -> JavaEntity -> @JavaMirror class M / @JavaImpl class A * * For example: * pa: jobject -> A(Java_CFFI_JavaEntityJobject(pa)): A */ OwnedPtr JavaDesugarManager::UnwrapRefExpr(OwnedPtr ref, Ptr targetTy, const ClassLikeDecl& decl) { // Do not wrap for primitive type. if (targetTy->IsPrimitive()) { return std::move(ref); } // Mirror or Impl or Option or other? // jobject -> JavaEntity auto entity = lib.CreateJavaEntityJobjectCall(std::move(ref)); // JavaEntity -> Cangjie Type return lib.UnwrapJavaEntity(std::move(entity), targetTy, decl); } /** * Wrap the cangjie expr into native type. * 1. Primitive Type * 2. JavaMirror/JavaImpl: @JavaMirror class M / @JavaImpl class A -> JavaEntity -> jobject * * For example: foo(m, a) with type M * withExceptionHandling(env, { => * let tmp = foo(m, a) * return Java_CFFI_unwrapJavaEntityAsValue(tmp.javaref) * }) */ OwnedPtr JavaDesugarManager::WrapExprWithExceptionHandling( std::vector>&& nodes, OwnedPtr expr, FuncParam& env, const ClassLikeDecl& decl) { auto curFile = expr->curFile; auto retTy = expr->ty; auto res = CreateTmpVarDecl(nullptr, std::move(expr)); OwnedPtr ret = WithinFile(CreateRefExpr(*res), curFile); if (retTy->IsPrimitive()) { ret = WithinFile(CreateRefExpr(*res), curFile); } else { // Cangjie Type -> JavaEntity -> jobject ret = lib.UnwrapJavaEntity(lib.WrapJavaEntity(WithinFile(CreateRefExpr(*res), curFile)), retTy, decl, true); } nodes.push_back(std::move(res)); nodes.push_back(std::move(ret)); return lib.WrapExceptionHandling(WithinFile(CreateRefExpr(env), curFile), WrapReturningLambdaExpr(typeManager, std::move(nodes))); } OwnedPtr JavaDesugarManager::CreateNativeFunc(const std::string& funcName, std::vector> params, Ptr retTy, std::vector> nodes) { auto jniRetTy = GetJNITy(retTy); auto funcTy = GetNativeFuncTy(params, [this](const FuncParam& node) { return GetJNITy(node.ty);}, jniRetTy, typeManager); auto block = CreateBlock(std::move(nodes), jniRetTy); std::vector> paramLists; paramLists.push_back(CreateFuncParamList(std::move(params))); auto funcBody = CreateFuncBody(std::move(paramLists), CreateType(jniRetTy), std::move(block), funcTy); auto fdecl = CreateFuncDecl(funcName, std::move(funcBody), funcTy); fdecl->funcBody->funcDecl = fdecl.get(); fdecl->EnableAttr(Attribute::GLOBAL, Attribute::PUBLIC, Attribute::C); return fdecl; } OwnedPtr JavaDesugarManager::GenerateNativeFunc4Argument(const FuncArg& arg, const std::vector>& params, ClassLikeDecl& decl, size_t ctorId, size_t argId) { std::vector> usingParams; auto [usingThis, paramIds] = CollectParams(arg.expr.get(), params, usingParams); if (usingThis) { // Report error for using this in super call. diag.DiagnoseRefactor(DiagKindRefactor::sema_java_interop_not_supported, arg, "using this in super call"); return nullptr; } std::vector> staticRefs; auto usingPrivate = CollectStaticRefs(arg.expr.get(), staticRefs); if (usingPrivate) { // Report error for using private static member in super call. diag.DiagnoseRefactor( DiagKindRefactor::sema_java_interop_not_supported, arg, "using private static member in super call"); return nullptr; } // The encoding rules of super call id: C{ctorId}A{argId}{usingParamIds: P{paramId}P{paramId}...} // {ctorId} is the index of the constructor containing the super call in the class body. // {argId} is the index of the func argument in super call. // {usingParamIds} is the indexes of the func argument using parameters of the constructor. std::string id = "C" + std::to_string(ctorId) + "A" + std::to_string(argId); std::string funcName = GetJniSuperArgFuncName(decl, id) + paramIds; std::vector> funcParams; funcParams.push_back(lib.CreateEnvFuncParam()); // jobject or jclass funcParams.push_back(lib.CreateJClassOrJObjectFuncParam()); // Body std::vector> nodes; // collect temp vars std::map, Ptr> source2cloned; for (size_t pi = 0; pi < usingParams.size(); pi++) { // Create func param for native func auto funcParam = CreateFuncParam( usingParams[pi]->identifier.Val(), nullptr, nullptr, GetJNITy(usingParams[pi]->ty)); auto ref = WithinFile(CreateRefExpr(*funcParam), decl.curFile); funcParams.push_back(std::move(funcParam)); // Create temp var wrapper for using param: `let tv = unwrap(p0)`. OwnedPtr initializer = UnwrapRefExpr(std::move(ref), usingParams[pi]->ty, decl); auto tv = CreateTmpVarDecl(nullptr, std::move(initializer)); source2cloned[usingParams[pi]] = tv.get(); nodes.push_back(std::move(tv)); } for (auto sf : staticRefs) { // Create temp var for static member ref: `let tv = A.sf`. auto outerDecl = sf->ref.target->outerDecl; CJC_NULLPTR_CHECK(outerDecl); OwnedPtr initializer = CreateMemberAccess(CreateRefExpr(*outerDecl), *sf->ref.target); auto tv = CreateTmpVarDecl(nullptr, std::move(initializer)); source2cloned[sf->ref.target] = tv.get(); nodes.push_back(std::move(tv)); } // For `foo(x, y + z)` the parameters x, y and implicit static field z need be replaced by temp vars. // ----------------- // let tmp0 = unwrap(x) // let tmp1 = unwrap(y) // let tmp2 = A.z // foo(tmp0, tmp1 + tmp2) auto replaceTarget = [&mp = std::as_const(source2cloned)](Node& source, Node& target) { if (auto srcRef = As(&source)) { // replace ref by temp var auto it = mp.find(srcRef->ref.target); if (it != mp.end()) { auto dstRef = StaticAs(&target); dstRef->ref.identifier = it->second->identifier; dstRef->ref.target = it->second; } } else if (auto call = As(&source)) { if (call->baseFunc->astKind != ASTKind::REF_EXPR) { return; } auto refExpr = As(call->baseFunc); auto it = mp.find(refExpr->ref.target); // update call member access by call temp var if (it != mp.end()) { auto targetCall = As(&target); targetCall->resolvedFunction = nullptr; targetCall->callKind = CallKind::CALL_FUNCTION_PTR; } } }; auto clonedExpr = ASTCloner::Clone(arg.expr.get(), replaceTarget); auto wrapper = WrapExprWithExceptionHandling(std::move(nodes), std::move(clonedExpr), *funcParams[0], decl); OwnedPtr nativeFn = CreateNativeFunc( funcName, std::move(funcParams), arg.expr->ty, Nodes(std::move(wrapper))); nativeFn->moduleName = decl.moduleName; nativeFn->fullPackageName = decl.fullPackageName; // update curFile info. AddCurFile(*nativeFn, decl.curFile); return nativeFn; } OwnedPtr JavaDesugarManager::DesugarJavaImplSuperCall(const FuncDecl& ctor, Decl& jniEnvVar) { auto& paramList = *ctor.funcBody->paramLists[0]; auto decl = As(ctor.outerDecl); // super call auto superCall = TryGetSuperCall(ctor); CJC_NULLPTR_CHECK(superCall); size_t ctorId = GetCtorId(ctor); auto needGen = [](const Expr& expr) { if (expr.astKind == ASTKind::LIT_CONST_EXPR) { return false; } if (expr.astKind != ASTKind::REF_EXPR) { return true; } auto refTarget = StaticAs(&expr)->ref.target; CJC_NULLPTR_CHECK(refTarget); return refTarget->astKind != ASTKind::FUNC_PARAM; }; auto& args = superCall->args; for (size_t index = 0; index < args.size(); index++) { // generate @C func on demand if (!needGen(*args[index]->expr)) { continue; } if (auto argFn = GenerateNativeFunc4Argument(*args[index], paramList.params, *decl, ctorId, index); argFn) { // record desugared function args[index]->expr->desugarExpr = CreateRefExpr(*argFn); generatedDecls.push_back(std::move(argFn)); } } // Java_CFFI_newJavaObject_raw call return lib.CreateCFFINewJavaObjectCall(WithinFile(CreateRefExpr(jniEnvVar), ctor.curFile), utils.GetJavaClassNormalizeSignature(*decl->ty), paramList, false, *ctor.curFile); } void JavaDesugarManager::DesugarJavaImplConstructor(FuncDecl& ctor, FuncDecl& parentCtor) { auto curFile = ctor.curFile; CJC_ASSERT(ctor.TestAttr(Attribute::CONSTRUCTOR)); auto classLikeDecl = As(ctor.outerDecl); CJC_NULLPTR_CHECK(classLikeDecl); ctor.constructorCall = ConstructorCall::SUPER; auto jniEnvCall = lib.CreateGetJniEnvCall(curFile); if (!jniEnvCall) { ctor.EnableAttr(Attribute::IS_BROKEN); return; } auto jniEnvPtrDecl = lib.GetJniEnvPtrDecl(); if (!jniEnvPtrDecl) { ctor.EnableAttr(Attribute::IS_BROKEN); return; } ctor.EnableAttr(Attribute::UNSAFE); auto jniEnvVar = CreateTmpVarDecl(jniEnvPtrDecl->type, jniEnvCall); CJC_ASSERT(ctor.funcBody); CJC_ASSERT(ctor.funcBody->paramLists.size() == 1); if (auto newObjCall = DesugarJavaImplSuperCall(ctor, *jniEnvVar)) { auto jniEnvRef = WithinFile(CreateRefExpr(*jniEnvVar), curFile); if (auto newWeakRefCall = lib.CreateNewGlobalRefCall(std::move(jniEnvRef), std::move(newObjCall), true)) { std::vector> nodes; nodes.push_back(std::move(jniEnvVar)); nodes.push_back(std::move(newWeakRefCall)); auto lambdaCall = WrapReturningLambdaCall(typeManager, std::move(nodes)); auto superCall = CreateSuperCall(*ctor.outerDecl, parentCtor, parentCtor.ty); superCall->args.insert(superCall->args.begin(), CreateFuncArg(std::move(lambdaCall))); if (!ctor.funcBody->body->body.empty()) { auto firstNode = ctor.funcBody->body->body[0].get(); if (auto callExpr = As(firstNode); callExpr && (callExpr->callKind == CallKind::CALL_SUPER_FUNCTION)) { // This super call `callExpr` will be removed in `JavaSourceCodeGenerator` callExpr->EnableAttr(Attribute::JAVA_MIRROR, Attribute::UNREACHABLE); } } auto putToRegistryCall = lib.CreatePutToRegistrySelfInitCall( lib.CreateGetJniEnvCall(curFile), CreateJavaRefCall(*classLikeDecl, curFile), CreateThisRef(Ptr(classLikeDecl), classLikeDecl->ty, curFile)); ctor.funcBody->body->body.insert(ctor.funcBody->body->body.begin(), std::move(putToRegistryCall)); // insert generated super call at the beginning of the constructor ctor.funcBody->body->body.insert(ctor.funcBody->body->body.begin(), std::move(superCall)); /* We can't remove existing user-defined super call on this stage because it will be used for javacode generation */ } else { ctor.EnableAttr(Attribute::IS_BROKEN); } } else { ctor.EnableAttr(Attribute::IS_BROKEN); } } OwnedPtr JavaDesugarManager::GenerateJavaImplConstructor(FuncDecl& sampleCtor, ClassLikeDecl& parent) { auto ctor = ASTCloner::Clone(Ptr(&sampleCtor)); // obj: Java_CFFI_JavaEntity auto javaEntityDecl = lib.GetJavaEntityDecl(); if (!javaEntityDecl) { return nullptr; } auto entityParam = CreateFuncParam( JAVA_IMPL_ENTITY_ARG_NAME_IN_GENERATED_CTOR, CreateRefType(*javaEntityDecl), nullptr, javaEntityDecl->ty); auto entityParamRef = WithinFile(CreateRefExpr(*entityParam), sampleCtor.curFile); ctor->funcBody->paramLists[0]->params.insert(ctor->funcBody->paramLists[0]->params.begin(), std::move(entityParam)); Ptr parentCtor = GetGeneratedJavaMirrorConstructor(parent); CJC_ASSERT(parentCtor); CJC_ASSERT(parentCtor->funcBody->paramLists[0]->params.size() == 1); // Java_CFFI_JavaEntity std::vector> paramTys; paramTys.push_back(javaEntityDecl->ty); for (auto paramTy : StaticCast(sampleCtor.ty.get())->paramTys) { paramTys.push_back(paramTy); } auto ctorTy = typeManager.GetFunctionTy(paramTys, StaticCast(sampleCtor.ty.get())->retTy); auto superCall = CreateSuperCall(*sampleCtor.outerDecl, *parentCtor, parentCtor->ty); superCall->args.push_back(CreateFuncArg(std::move(entityParamRef))); auto& block = ctor->funcBody->body; block->body.erase(std::remove_if(block->body.begin(), block->body.end(), [](auto& node) { if (auto call = As(node.get())) { return call->callKind == CallKind::CALL_SUPER_FUNCTION; } return false; }), block->body.end()); block->body.insert(block->body.begin(), std::move(superCall)); ctor->funcBody->ty = ctorTy; ctor->ty = ctorTy; ctor->funcBody->funcDecl = ctor.get(); ctor->constructorCall = ConstructorCall::SUPER; ctor->DisableAttr(Attribute::PRIMARY_CONSTRUCTOR); ctor->EnableAttr(Attribute::JAVA_MIRROR_SUBTYPE); ctor->EnableAttr(Attribute::UNSAFE); return std::move(ctor); } OwnedPtr JavaDesugarManager::CreateUnitType() { auto type = MakeOwned(); type->str = "Unit"; type->kind = TypeKind::TYPE_UNIT; type->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); return type; } Ptr JavaDesugarManager::GetJNITy(Ptr ty) { static auto jobjectTy = lib.GetJobjectTy(); static auto jlongTy = lib.GetJlongTy(); if (!ty) { return nullptr; } if (ty->IsCoreOptionType()) { return jobjectTy; } if (IsMirror(*ty) || IsImpl(*ty)) { return jobjectTy; } if (IsCJMapping(*ty)) { return jlongTy; } CJC_ASSERT(ty->IsBuiltin()); return ty; } std::string JavaDesugarManager::GetJniMethodName(const FuncDecl& method) { auto sampleJavaName = GetJavaMemberName(method); std::string fqname = GetJavaFQName(*(method.outerDecl)); MangleJNIName(fqname); auto mangledFuncName = GetMangledMethodName(mangler, method.funcBody->paramLists[0]->params, sampleJavaName); MangleJNIName(mangledFuncName); return "Java_" + fqname + "_" + mangledFuncName; } std::string JavaDesugarManager::GetJniMethodNameForProp(const PropDecl& propDecl, bool isSet) const { std::string varDecl = GetJavaMemberName(propDecl); std::string varDeclSuffix = varDecl; varDeclSuffix[0] = static_cast(toupper(varDeclSuffix[0])); std::string fqname = GetJavaFQName(*(propDecl.outerDecl)); MangleJNIName(fqname); return "Java_" + fqname + (isSet ? "_set" : "_get") + varDeclSuffix + "Impl"; ; } inline std::string JavaDesugarManager::GetJniSuperArgFuncName(const ClassLikeDecl& outer, const std::string& id) const { std::string fqname = GetJavaFQName(outer); MangleJNIName(fqname); return "Java_" + fqname + "_super" + id; } std::string JavaDesugarManager::GetJniInitCjObjectFuncName(const FuncDecl& ctor, bool isGeneratedCtor) { std::string fqname = GetJavaFQName(*(ctor.outerDecl)); MangleJNIName(fqname); auto mangledFuncName = GetMangledJniInitCjObjectFuncName(mangler, ctor.funcBody->paramLists[0]->params, isGeneratedCtor); MangleJNIName(mangledFuncName); if (auto enumDecl = As(ctor.outerDecl)) { mangledFuncName = ctor.identifier + mangledFuncName; } return "Java_" + fqname + "_" + mangledFuncName; } std::string JavaDesugarManager::GetJniInitCjObjectFuncNameForVarDecl(const AST::VarDecl& ctor) const { std::string fqname = GetJavaFQName(*(ctor.outerDecl)); MangleJNIName(fqname); auto mangledFuncName = ctor.identifier.Val(); MangleJNIName(mangledFuncName); return "Java_" + fqname + "_" + mangledFuncName + "initCJObject"; } std::string JavaDesugarManager::GetJniDeleteCjObjectFuncName(const Decl& decl) const { std::string fqname = GetJavaFQName(decl); MangleJNIName(fqname); return "Java_" + fqname + "_deleteCJObject"; } OwnedPtr JavaDesugarManager::GenerateNativeDeleteCjObjectFunc(ClassLikeDecl& javaImpl, VarDecl& javaWeakRefField) { auto curFile = javaImpl.curFile; CJC_NULLPTR_CHECK(javaImpl.curFile); std::vector> params; FuncParam* jniEnvPtrParam = nullptr; OwnedPtr selfParamRef; GenerateFuncParamsForNativeDeleteCjObject(javaImpl, params, jniEnvPtrParam, selfParamRef); /* lambda: { objToDelete: => return objToDelete.javaref } */ std::vector> lambdaParams; lambdaParams.push_back(CreateFuncParam("objToDelete", CreateRefType(javaImpl), nullptr, javaImpl.ty)); std::vector> lambdaParamLists; lambdaParamLists.push_back(CreateFuncParamList(std::move(lambdaParams))); auto retExpr = CreateReturnExpr(CreateMemberAccess( WithinFile(CreateRefExpr(*lambdaParamLists[0]->params[0]), curFile), javaWeakRefField)); retExpr->ty = TypeManager::GetNothingTy(); auto javaEntityDecl = lib.GetJavaEntityDecl(); if (!javaEntityDecl) { return nullptr; } auto javaEntityType = CreateRefType(*javaEntityDecl); auto lambda = CreateLambdaExpr(CreateFuncBody( std::move(lambdaParamLists), std::move(javaEntityType), CreateBlock({}, javaEntityDecl->ty), javaEntityDecl->ty )); retExpr->refFuncBody = lambda->funcBody.get(); lambda->funcBody->body->body.push_back(std::move(retExpr)); lambda->curFile = curFile; std::vector> lambdaParamTys; lambdaParamTys.push_back(javaImpl.ty); lambda->ty = typeManager.GetFunctionTy(lambdaParamTys, javaEntityDecl->ty); auto deleteCjObjCall = lib.CreateDeleteCJObjectCall( WithinFile(CreateRefExpr(*jniEnvPtrParam), curFile), std::move(selfParamRef), std::move(lambda), javaImpl.ty); auto wrappedNodesLambda = WrapReturningLambdaExpr(typeManager, Nodes(std::move(deleteCjObjCall))); Ptr unitTy = typeManager.GetPrimitiveTy(TypeKind::TYPE_UNIT).get(); auto funcName = GetJniDeleteCjObjectFuncName(javaImpl); std::vector> paramLists; paramLists.push_back(CreateFuncParamList(std::move(params))); return GenerateNativeFuncDeclBylambda(javaImpl, wrappedNodesLambda, paramLists, *jniEnvPtrParam, unitTy, funcName); } void JavaDesugarManager::DesugarJavaImpl(ClassDecl& jimpl) { auto parentMirror = jimpl.GetSuperClassDecl(); CJC_NULLPTR_CHECK(parentMirror); CJC_ASSERT(IsMirror(*parentMirror)); for (auto& member : jimpl.GetMemberDecls()) { if (member->TestAttr(Attribute::IS_BROKEN) || member->astKind == ASTKind::PRIMARY_CTOR_DECL) { continue; } Walker(member, [this, &jimpl](auto node) { auto call = As(node); if (!call) { return VisitAction::WALK_CHILDREN; } auto ma = As(call->baseFunc.get()); if (!ma || !ma->baseExpr) { return VisitAction::WALK_CHILDREN; } auto ref = As(ma->baseExpr); if (!ref || !ref->isSuper) { return VisitAction::WALK_CHILDREN; } DesugarSuperMethodCall(*call, *StaticAs(&jimpl)); return VisitAction::WALK_CHILDREN; }).Walk(); if (auto fd = As(member.get())) { if (!fd->TestAttr(Attribute::CONSTRUCTOR) || IsGeneratedJavaImplConstructor(*fd)) { continue; } DesugarJavaImplConstructor(*fd, *GetGeneratedJavaMirrorConstructor(*parentMirror)); continue; } } } void JavaDesugarManager::DesugarInJavaImpls(File& file) { for (auto& decl : file.decls) { if (auto cdecl = As(decl.get())) { if (cdecl->TestAttr(Attribute::IS_BROKEN) || !IsImpl(*cdecl)) { continue; } DesugarJavaImpl(*cdecl); if (JavaSourceCodeGenerator::IsDeclAppropriateForGeneration(*cdecl)) { const std::string fileJ = cdecl->identifier.Val() + ".java"; auto codegen = JavaSourceCodeGenerator(cdecl, mangler, javaCodeGenPath, fileJ, GetCangjieLibName(outputLibPath, cdecl->GetFullPackageName())); codegen.Generate(); } } } } OwnedPtr JavaDesugarManager::CreateIsInstanceCall(Ptr jObjectVar, Ptr classTy, Ptr curFile) { auto isInstanceOfDecl = lib.GetIsInstanceOf(); auto jniEnvCall = lib.CreateGetJniEnvCall(curFile); auto javaRefExpr = CreateJavaRefCall(WithinFile(CreateRefExpr(*jObjectVar), curFile)); auto nameLit = CreateLitConstExpr( LitConstKind::STRING, utils.GetJavaTypeSignature(*classTy), isInstanceOfDecl->funcBody->paramLists[0]->params[2]->ty); return CreateCall(isInstanceOfDecl, curFile, std::move(jniEnvCall), std::move(javaRefExpr), std::move(nameLit)); } void JavaDesugarManager::DesugarIsExpression(IsExpr& ie) { auto curFile = ie.curFile; CJC_NULLPTR_CHECK(curFile); static const auto BOOL_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); CJC_ASSERT(!ie.desugarExpr); auto castTy = ie.isType->ty; auto jObjectDecl = utils.GetJObjectDecl(); CJC_ASSERT(jObjectDecl); // match (x) std::vector> matchCases; // case x : JObject => IsInstance(..) auto jObjVarPattern = WithinFile(CreateVarPattern(V_COMPILER, jObjectDecl->ty), curFile); jObjVarPattern->varDecl->curFile = curFile; auto isInstanceCall = CreateIsInstanceCall(jObjVarPattern->varDecl, castTy, curFile); auto jObjectType = CreateType(jObjectDecl->ty); auto typePattern = CreateTypePattern(std::move(jObjVarPattern), std::move(jObjectType), *ie.leftExpr); matchCases.emplace_back(CreateMatchCase(std::move(typePattern), std::move(isInstanceCall))); // case _ => false auto falseLit = CreateLitConstExpr(LitConstKind::BOOL, "false", BOOL_TY); matchCases.emplace_back(CreateMatchCase(MakeOwned(), std::move(falseLit))); ie.desugarExpr = WithinFile(CreateMatchExpr(std::move(ie.leftExpr), std::move(matchCases), BOOL_TY), curFile); } OwnedPtr JavaDesugarManager::CreateJObjectCast(Ptr jObjectVar, Ptr castDecl, Ptr curFile) { auto castTy = castDecl->ty; // match (IsInstance(obj.javaref, ...)) auto isInstanceCall = CreateIsInstanceCall(jObjectVar, castTy, curFile); auto javarefExpr = CreateJavaRefCall(WithinFile(CreateRefExpr(*jObjectVar), curFile)); // cast true => ... OwnedPtr trueBranch; if (castDecl->TestAttr(Attribute::JAVA_MIRROR)) { // Some(T(obj.javaref)) auto ctorCall = CreateMirrorConstructorCall(importManager, std::move(javarefExpr), castTy); trueBranch = utils.CreateOptionSomeCall(std::move(ctorCall), castTy); } else { // getFromRegistry(..) CJC_ASSERT(castDecl->TestAttr(Attribute::JAVA_MIRROR_SUBTYPE)); bool retAsOption = true; trueBranch = lib.CreateGetFromRegistryByEntityCall(lib.CreateGetJniEnvCall(curFile), std::move(javarefExpr), castTy, retAsOption); } // case false => None OwnedPtr falseBranch = utils.CreateOptionNoneRef(castTy); return CreateBoolMatch( std::move(isInstanceCall), std::move(trueBranch), std::move(falseBranch), utils.GetOptionTy(castTy)); } void JavaDesugarManager::DesugarAsExpression(AsExpr& ae) { auto curFile = ae.curFile; CJC_NULLPTR_CHECK(curFile); CJC_ASSERT(!ae.desugarExpr); auto castTy = ae.asType->ty; auto castDecl = DynamicCast(Ty::GetDeclOfTy(castTy)); auto jObjectDecl = utils.GetJObjectDecl(); auto castResultTy = utils.GetOptionTy(castTy); // match (obj) std::vector> typeMatchCases; // case obj : JObject => match ... auto jObjVarPattern = WithinFile(CreateVarPattern(V_COMPILER, jObjectDecl->ty), curFile); jObjVarPattern->varDecl->curFile = curFile; auto jObjectType = CreateType(jObjectDecl->ty); auto isInstanceMatch = CreateJObjectCast(jObjVarPattern->varDecl, castDecl, curFile); auto typePattern = CreateTypePattern(std::move(jObjVarPattern), std::move(jObjectType), *ae.leftExpr); typeMatchCases.emplace_back(CreateMatchCase(std::move(typePattern), std::move(isInstanceMatch))); // case _ => None auto noneRef = utils.CreateOptionNoneRef(castTy); typeMatchCases.emplace_back(CreateMatchCase(MakeOwned(), std::move(noneRef))); ae.desugarExpr = WithinFile( CreateMatchExpr(std::move(ae.leftExpr), std::move(typeMatchCases), castResultTy), curFile); } namespace { bool ShouldDesugarTypecheck(Ptr type, Ptr expr) { // When obj is not class or interface or type is not of Java class // then will be desugared as regular as auto castDecl = DynamicCast(Ty::GetDeclOfTy(type->ty)); auto objDecl = DynamicCast(Ty::GetDeclOfTy(expr->ty)); if (!objDecl || !castDecl || !castDecl->TestAnyAttr(Attribute::JAVA_MIRROR_SUBTYPE, Attribute::JAVA_MIRROR)) { return false; } return true; } std::vector> CollectTypePatternsWithJavaClass(Ptr pat) { std::vector> res; Walker(pat, [&res](auto node) { CJC_ASSERT(node); if (auto tpat = As(node.get())) { auto decl = Ty::GetDeclOfTy(tpat->type->ty); // Saving for all Java classes, except JObject which is JAVA_MIRROR and // not JAVA_MIRROR_SUBTYPE if (decl && decl->TestAttr(Attribute::JAVA_MIRROR_SUBTYPE)) { res.push_back(tpat); } } return VisitAction::WALK_CHILDREN; }).Walk(); return res; } std::vector> CollectTypePatternsWithJavaClass(const std::vector>& patterns) { std::vector> res; for (auto& pat : patterns) { auto pats = CollectTypePatternsWithJavaClass(pat.get()); std::move(pats.begin(), pats.end(), std::back_inserter(res)); } return res; } OwnedPtr CreateTmpVarPattern(Ptr ty) { auto var = CreateTmpVarDecl(); auto varPat = MakeOwned(); var->parentPattern = varPat; var->ty = ty; varPat->ty = ty; varPat->varDecl = std::move(var); return varPat; } } // namespace void JavaDesugarManager::DesugarMatchCase(MatchCase& matchCase) { auto jObjectDecl = utils.GetJObjectDecl(); std::vector> typePatterns = CollectTypePatternsWithJavaClass(matchCase.patterns); if (typePatterns.empty()) { return; } std::vector> isInstanceGuards; std::vector, Ptr>> patternVars; for (auto pat : typePatterns) { if (DynamicCast(pat->pattern.get())) { pat->pattern = CreateTmpVarPattern(pat->type->ty); pat->pattern->curFile = matchCase.curFile; } auto varPat = DynamicCast(pat->pattern.get()); // Pattern under type pattern is always either wildcard or var CJC_ASSERT(varPat); auto originalTy = varPat->ty; pat->type = CreateType(jObjectDecl->ty); varPat->ty = jObjectDecl->ty; varPat->varDecl->ty = jObjectDecl->ty; patternVars.emplace_back(varPat->varDecl, originalTy); isInstanceGuards.emplace_back(CreateIsInstanceCall(varPat->varDecl, originalTy, matchCase.curFile)); } OwnedPtr guard; if (!matchCase.patternGuard) { guard = std::move(isInstanceGuards.back()); isInstanceGuards.pop_back(); } else { OwnedPtr guardVarsBlock = CastAndSubstituteVars(*matchCase.patternGuard, patternVars); guard = ASTCloner::Clone(matchCase.patternGuard.get()); guardVarsBlock->body.emplace_back(std::move(matchCase.patternGuard)); guardVarsBlock->ty = guard->ty; guard->desugarExpr = std::move(guardVarsBlock); } while (!isInstanceGuards.empty()) { guard = CreateBinaryExpr(std::move(isInstanceGuards.back()), std::move(guard), TokenKind::AND); isInstanceGuards.pop_back(); } matchCase.patternGuard = std::move(guard); auto bodyVarsBlock = CastAndSubstituteVars(*matchCase.exprOrDecls, patternVars); bodyVarsBlock->ty = matchCase.exprOrDecls->ty; std::move(matchCase.exprOrDecls->body.begin(), matchCase.exprOrDecls->body.end(), std::back_inserter(bodyVarsBlock->body)); matchCase.exprOrDecls = std::move(bodyVarsBlock); } OwnedPtr JavaDesugarManager::CastAndSubstituteVars( Expr& expr, const std::vector, Ptr>>& patternVars) { auto curFile = expr.curFile; auto varsBlock = WithinFile(MakeOwned(), curFile); std::unordered_map, Ptr> varsMapping; for (auto [varDecl, castTy] : patternVars) { auto castDecl = DynamicCast(Ty::GetDeclOfTy(castTy)); CJC_ASSERT(castDecl); auto javarefExpr = CreateJavaRefCall(WithinFile(CreateRefExpr(*varDecl), curFile)); OwnedPtr initializer = lib.UnwrapJavaEntity(std::move(javarefExpr), castDecl->ty, *castDecl); auto castedVar = WithinFile(CreateTmpVarDecl(CreateType(castDecl->ty), std::move(initializer)), curFile); varsMapping[varDecl] = castedVar; varsBlock->body.emplace_back(std::move(castedVar)); } Walker(&expr, [&varsMapping](Ptr node) { CJC_ASSERT(node); if (auto refExpr = DynamicCast(node.get())) { auto target = refExpr->ref.target; if (auto castedVarIt = varsMapping.find(target); castedVarIt != varsMapping.end()) { refExpr->ref.identifier = castedVarIt->second->identifier; refExpr->ref.target = castedVarIt->second; } } return VisitAction::WALK_CHILDREN; }).Walk(); return varsBlock; } void JavaDesugarManager::DesugarLetPattern(LetPatternDestructor& letPat) { std::vector> typePatterns = CollectTypePatternsWithJavaClass(letPat.patterns); if (typePatterns.empty()) { return; } for (auto typePat : typePatterns) { diag.DiagnoseRefactor(DiagKindRefactor::sema_java_interop_not_supported, *typePat, "let type patterns with JavaImpl or JavaMirror types"); } } void JavaDesugarManager::DesugarTypechecks(File& file) { Walker(&file, [this](auto node) { CJC_ASSERT(node); if (auto ie = As(node.get())) { if (!ShouldDesugarTypecheck(ie->isType, ie->leftExpr)) { return VisitAction::WALK_CHILDREN; } DesugarIsExpression(*ie); } else if (auto ae = As(node.get())) { if (!ShouldDesugarTypecheck(ae->asType, ae->leftExpr)) { return VisitAction::WALK_CHILDREN; } DesugarAsExpression(*ae); } else if (auto mc = As(node.get())) { DesugarMatchCase(*mc); } else if (auto lpd = As(node.get())) { DesugarLetPattern(*lpd); } return VisitAction::WALK_CHILDREN; }).Walk(); } void JavaDesugarManager::DesugarSuperMethodCall(CallExpr& call, ClassDecl& impl) { CJC_ASSERT(call.baseFunc && call.baseFunc->astKind == ASTKind::MEMBER_ACCESS); auto& ma = *StaticAs(call.baseFunc.get()); CJC_ASSERT(ma.baseExpr && ma.baseExpr->astKind == ASTKind::REF_EXPR); auto& ref = *StaticAs(ma.baseExpr.get().get()); CJC_ASSERT(ref.isSuper); CJC_ASSERT(call.resolvedFunction && call.resolvedFunction->outerDecl); auto& outerDecl = *call.resolvedFunction->outerDecl; CJC_ASSERT(IsMirror(outerDecl)); auto parent = As(&outerDecl); auto curFile = call.curFile; std::vector> args; for (auto& arg : call.args) { auto desugaredArg = lib.WrapJavaEntity(ASTCloner::Clone(arg->expr.get())); args.emplace_back(std::move(desugaredArg)); } auto desugaredCall = lib.CreateCallMethodCall( lib.CreateGetJniEnvCall(curFile), CreateJavaRefCall(impl, curFile), MemberJNISignature(utils, *call.resolvedFunction, parent), std::move(args), *curFile, false); desugaredCall->desugarArgs = std::nullopt; call.desugarExpr = call.ty->IsUnit() ? std::move(desugaredCall) : lib.UnwrapJavaEntity(std::move(desugaredCall), call.ty, impl); } void JavaDesugarManager::GenerateInJavaImpl(AST::ClassDecl* classDecl) { CJC_ASSERT(classDecl && IsImpl(*classDecl)); auto& jimpl = *classDecl; auto parentMirror = jimpl.GetSuperClassDecl(); CJC_NULLPTR_CHECK(parentMirror); std::vector> generatedCtors; for (auto& member : jimpl.GetMemberDecls()) { if (member->TestAnyAttr(Attribute::IS_BROKEN, Attribute::PRIVATE)) { // private members are not callable from java continue; } if (auto fd = As(member.get())) { if (fd->TestAttr(Attribute::CONSTRUCTOR)) { generatedCtors.push_back(GenerateJavaImplConstructor(*fd, *parentMirror)); continue; } else { generatedDecls.push_back(GenerateNativeMethod(*fd, jimpl)); } } } if (!generatedCtors.empty()) { auto& javaWeakRefField = *GetJavaRefField(*parentMirror); generatedDecls.push_back(GenerateNativeDeleteCjObjectFunc(jimpl, javaWeakRefField)); for (auto& generatedCtor : generatedCtors) { generatedDecls.push_back(GenerateNativeInitCjObjectFunc(*generatedCtor, true)); if (auto cd = As(&jimpl)) { cd->body->decls.push_back(std::move(generatedCtor)); } } } } void JavaDesugarManager::GenerateInJavaImpls(File& file) { for (auto& decl : file.decls) { auto astDecl = As(decl.get()); if (astDecl && astDecl->TestAttr(Attribute::IS_BROKEN)) { continue; } auto classDecl = As(decl.get()); if (classDecl && IsImpl(*classDecl)) { GenerateInJavaImpl(classDecl); } } } } // namespace Cangjie::Interop::Java cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/AfterTypeCheck/DesugarJavaMirror.cpp000066400000000000000000000665111510705540100300640ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckUtil.h" #include "JavaDesugarManager.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Walker.h" #include "cangjie/AST/Match.h" #include "cangjie/Utils/ConstantsUtils.h" #include "Utils.h" #include "NativeFFI/Utils.h" #include "cangjie/AST/Utils.h" namespace Cangjie::Interop::Java { using namespace Cangjie::Native::FFI; // For an array of object-based type we add additional `get` method to perform unwrapping further, // at call site with a concrete type void JavaDesugarManager::InsertArrayJavaEntityGet(ClassDecl& decl) { if (!decl.body) { return; } Ptr getOperationDecl; for (auto& member : decl.body->decls) { auto funcDecl = As(member); if (!funcDecl || !funcDecl->funcBody || funcDecl->funcBody->paramLists.empty()) { continue; } if (funcDecl && funcDecl->identifier == "[]" && funcDecl->funcBody->paramLists[0]->params.size() == 1) { getOperationDecl = As(member); break; } } if (!getOperationDecl) { return; } auto javaEntityGetDecl = ASTCloner::Clone(getOperationDecl); javaEntityGetDecl->identifier = JAVA_ARRAY_GET_FOR_REF_TYPES; javaEntityGetDecl->ty = typeManager.GetFunctionTy( {TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT32)}, lib.GetJavaEntityTy()); auto javaEntity = lib.GetJavaEntityTy(); if (!javaEntity || !javaEntityGetDecl->funcBody->retType) { return; } javaEntityGetDecl->funcBody->retType->ty = javaEntity; decl.body->decls.push_back(std::move(javaEntityGetDecl)); } // For an array of object-based type we add additional `set` method to perform unwrapping further, // at call site with a concrete type void JavaDesugarManager::InsertArrayJavaEntitySet(ClassDecl& decl) { if (!decl.body) { return; } Ptr setOperationDecl; for (auto& member : decl.body->decls) { auto funcDecl = As(member); if (!funcDecl || !funcDecl->funcBody || funcDecl->funcBody->paramLists.empty()) { continue; } if (funcDecl && funcDecl->identifier == "[]" && funcDecl->funcBody->paramLists[0]->params.size() == 2) { setOperationDecl = As(member); break; } } if (!setOperationDecl) { return; } auto javaEntity = lib.GetJavaEntityTy(); auto javaEntitySetDecl = ASTCloner::Clone(setOperationDecl); if (!javaEntity || !javaEntitySetDecl->funcBody->retType) { return; } auto unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); javaEntitySetDecl->identifier = JAVA_ARRAY_SET_FOR_REF_TYPES; javaEntitySetDecl->funcBody->paramLists[0]->params[1]->ty = javaEntity; javaEntitySetDecl->ty = typeManager.GetFunctionTy({TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT32), javaEntity}, unitTy); javaEntitySetDecl->funcBody->retType->ty = unitTy; decl.body->decls.push_back(std::move(javaEntitySetDecl)); } void JavaDesugarManager::InsertJavaRefVarDecl(ClassDecl& decl) { auto& javaEntityDecl = *lib.GetJavaEntityDecl(); auto javaref = CreateVarDecl(JAVA_REF_FIELD_NAME, nullptr, CreateRefType(javaEntityDecl)); javaref->ty = javaEntityDecl.ty; javaref->EnableAttr(Attribute::JAVA_MIRROR); javaref->begin = decl.body ? decl.body->begin : decl.begin; javaref->curFile = decl.curFile; Modifier protectedMod = Modifier(TokenKind::PUBLIC, javaref->begin); protectedMod.curFile = decl.curFile; javaref->modifiers.emplace(std::move(protectedMod)); javaref->isVar = false; javaref->outerDecl = Ptr(&decl); javaref->fullPackageName = decl.fullPackageName; decl.body->decls.push_back(std::move(javaref)); } void JavaDesugarManager::InsertJavaMirrorCtor(ClassDecl& decl, bool doStub) { auto curFile = decl.curFile; auto isJObject = IsJObject(decl); auto& javaEntityDecl = *lib.GetJavaEntityDecl(); auto param = CreateFuncParam("$ref", CreateRefType(javaEntityDecl), nullptr, javaEntityDecl.ty); std::vector> ctorNodes; if (isJObject) { // for JObject, body can be created and filled in one step if (!doStub) { return; } auto lhsRef = WithinFile(CreateRefExpr(*GetJavaRefField(decl)), curFile); auto rhsRef = WithinFile(CreateRefExpr(*param), curFile); auto unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); auto refAssignment = CreateAssignExpr(std::move(lhsRef), std::move(rhsRef), unitTy); ctorNodes.push_back(lib.CreateEnsureNotNullCall(WithinFile(CreateRefExpr(*param), curFile))); ctorNodes.push_back(std::move(refAssignment)); } else if (!doStub) { auto ctor = GetGeneratedJavaMirrorConstructor(decl); CJC_NULLPTR_CHECK(ctor); auto actualParam = CreateFuncArg(WithinFile(CreateRefExpr(*ctor->funcBody->paramLists[0]->params[0]), curFile)); auto& superCtor = *GetGeneratedJavaMirrorConstructor(*decl.GetSuperClassDecl()); auto superCall = CreateSuperCall(decl, superCtor, superCtor.ty); superCall->args.emplace_back(std::move(actualParam)); ctor->funcBody->body->body.emplace_back(std::move(superCall)); return; } std::vector> ctorFuncParamTys; ctorFuncParamTys.push_back(param->ty); auto ctorFuncTy = typeManager.GetFunctionTy(std::move(ctorFuncParamTys), decl.ty); std::vector> ctorParams; ctorParams.push_back(std::move(param)); auto paramList = CreateFuncParamList(std::move(ctorParams)); std::vector> paramLists; paramLists.push_back(std::move(paramList)); auto ctorFuncBody = CreateFuncBody( std::move(paramLists), CreateRefType(decl), CreateBlock(std::move(ctorNodes), decl.ty), decl.ty); auto fd = CreateFuncDecl("init", std::move(ctorFuncBody), ctorFuncTy); fd->fullPackageName = decl.fullPackageName; fd->outerDecl = &decl; fd->funcBody->funcDecl = fd.get(); fd->constructorCall = ConstructorCall::SUPER; fd->EnableAttr( Attribute::PUBLIC, Attribute::JAVA_MIRROR, Attribute::IN_CLASSLIKE, Attribute::CONSTRUCTOR); fd->funcBody->parentClassLike = &decl; fd->begin = decl.begin; fd->curFile = decl.curFile; fd->fullPackageName = decl.fullPackageName; decl.body->decls.emplace_back(std::move(fd)); } void JavaDesugarManager::InsertAbstractJavaRefGetter(ClassLikeDecl& decl) { auto& javaEntityDecl = *lib.GetJavaEntityDecl(); std::vector> callParams; std::vector> paramLists; paramLists.emplace_back(CreateFuncParamList(std::move(callParams))); auto funcBody = CreateFuncBody( std::move(paramLists), CreateRefType(javaEntityDecl), nullptr, TypeManager::GetNothingTy()); funcBody->parentClassLike = &decl; std::vector> funcParamTys; Ptr funcTy = typeManager.GetFunctionTy(std::move(funcParamTys), javaEntityDecl.ty); auto fd = CreateFuncDecl(JAVA_REF_GETTER_FUNC_NAME, std::move(funcBody), funcTy); fd->EnableAttr(Attribute::PUBLIC, Attribute::IN_CLASSLIKE, Attribute::ABSTRACT); fd->funcBody->funcDecl = fd.get(); fd->fullPackageName = decl.fullPackageName; fd->outerDecl = Ptr(&decl); if (auto iDecl = As(&decl)) { iDecl->body->decls.emplace_back(std::move(fd)); } else if (auto clDecl = As(&decl)) { clDecl->body->decls.emplace_back(std::move(fd)); } } void JavaDesugarManager::InsertJavaRefGetterWithBody(ClassDecl& decl) { auto iter = decl.GetMemberDecls().begin(); for (auto& member : decl.GetMemberDecls()) { if (auto fd = DynamicCast(member.get())) { if (IsJavaRefGetter(*fd)) { // remove stub from parser stage decl.GetMemberDecls().erase(iter); break; } } iter++; } auto javaEntityDecl = lib.GetJavaEntityDecl(); std::vector> callParams; std::vector> paramLists; paramLists.push_back(CreateFuncParamList(std::move(callParams))); auto javaRef = GetJavaRefField(decl); auto ref = CreateRefExpr(*javaRef); auto ret = CreateReturnExpr(std::move(ref)); ret->ty = TypeManager::GetNothingTy(); std::vector> nodes; nodes.emplace_back(std::move(ret)); auto block = CreateBlock(std::move(nodes), javaEntityDecl->ty); auto funcBody = CreateFuncBody( std::move(paramLists), CreateRefType(*javaEntityDecl), std::move(block), TypeManager::GetNothingTy()); std::vector> funcParamTys; Ptr funcTy = typeManager.GetFunctionTy(std::move(funcParamTys), javaEntityDecl->ty); auto fd = CreateFuncDecl(JAVA_REF_GETTER_FUNC_NAME, std::move(funcBody), funcTy); fd->EnableAttr(Attribute::PUBLIC, Attribute::IN_CLASSLIKE, Attribute::INITIALIZED, Attribute::IS_CHECK_VISITED); fd->fullPackageName = decl.fullPackageName; fd->funcBody->funcDecl = fd.get(); fd->funcBody->parentClassLike = &decl; fd->outerDecl = &decl; decl.body->decls.emplace_back(std::move(fd)); } void JavaDesugarManager::DesugarJavaMirrorConstructor(FuncDecl& ctor, FuncDecl& generatedCtor) { auto curFile = ctor.curFile; CJC_ASSERT(ctor.TestAttr(Attribute::CONSTRUCTOR) && ctor.TestAttr(Attribute::JAVA_MIRROR)); ctor.constructorCall = ConstructorCall::OTHER_INIT; auto thisCall = CreateThisCall(*ctor.outerDecl, generatedCtor, generatedCtor.ty, curFile); CJC_ASSERT(ctor.funcBody); CJC_ASSERT(ctor.funcBody->paramLists.size() == 1); auto jniEnvCall = lib.CreateGetJniEnvCall(curFile); if (!jniEnvCall) { ctor.EnableAttr(Attribute::IS_BROKEN); return; } auto jniEnvPtrDecl = lib.GetJniEnvPtrDecl(); if (!jniEnvPtrDecl) { ctor.EnableAttr(Attribute::IS_BROKEN); return; } auto jniEnvVar = CreateTmpVarDecl(jniEnvPtrDecl->type, jniEnvCall); OwnedPtr newObjectCall; if (IsJArray(*ctor.outerDecl)) { auto genericParam = ctor.outerDecl->generic->typeParameters[0].get(); newObjectCall = lib.CreateCFFINewJavaArrayCall( WithinFile(CreateRefExpr(*jniEnvVar), curFile), *ctor.funcBody->paramLists[0], genericParam); } else { newObjectCall = lib.CreateCFFINewJavaObjectCall( WithinFile(CreateRefExpr(*jniEnvVar), curFile), utils.GetJavaClassNormalizeSignature(*ctor.outerDecl->ty), *ctor.funcBody->paramLists[0], true, *curFile); } if (newObjectCall) { std::vector> nodes; nodes.push_back(std::move(jniEnvVar)); nodes.push_back(std::move(newObjectCall)); thisCall->args.push_back(CreateFuncArg(WrapReturningLambdaCall(typeManager, std::move(nodes)))); ctor.funcBody->body->body.push_back(std::move(thisCall)); } else { ctor.EnableAttr(Attribute::IS_BROKEN); } } void JavaDesugarManager::AddJavaMirrorMethodBody(const ClassLikeDecl& mirror, FuncDecl& fun, OwnedPtr javaRefCall) { if (fun.TestAttr(Attribute::ABSTRACT) && !IsSynthetic(mirror)) { return; } auto curFile = fun.curFile; CJC_NULLPTR_CHECK(curFile); OwnedPtr jniEnvCall = lib.CreateGetJniEnvCall(curFile); if (!jniEnvCall) { fun.EnableAttr(Attribute::IS_BROKEN); return; } OwnedPtr methodCall; auto& paramList = *fun.funcBody->paramLists[0].get(); if (fun.TestAttr(Attribute::STATIC)) { methodCall = lib.CreateCFFICallStaticMethodCall( std::move(jniEnvCall), MemberJNISignature(utils, fun), paramList, *curFile); } else { if (IsJArray(mirror)) { auto genericParam = mirror.generic->typeParameters[0].get(); CJC_ASSERT(genericParam); methodCall = lib.CreateCFFICallArrayMethodCall( std::move(jniEnvCall), std::move(javaRefCall), paramList, genericParam, GetArrayOperationKind(fun)); } else { methodCall = lib.CreateCFFICallMethodCall( std::move(jniEnvCall), std::move(javaRefCall), MemberJNISignature(utils, fun), paramList, *fun.curFile); } } if (!methodCall) { fun.EnableAttr(Attribute::IS_BROKEN); return; } auto methodCallRes = CreateTmpVarDecl(nullptr, std::move(methodCall)); methodCallRes->ty = methodCallRes->initializer->ty; CopyBasicInfo(methodCallRes->initializer.get(), methodCallRes.get()); methodCallRes->begin = fun.begin; methodCallRes->curFile = fun.curFile; auto callResRef = CreateRefExpr(*methodCallRes); CopyBasicInfo(methodCallRes.get(), callResRef.get()); auto unwrapJavaEntityCall = lib.UnwrapJavaEntity(std::move(callResRef), fun.funcBody->retType->ty, mirror); if (!unwrapJavaEntityCall) { fun.EnableAttr(Attribute::IS_BROKEN); return; } std::vector> blockNodes; auto retExpr = CreateReturnExpr(std::move(unwrapJavaEntityCall), fun.funcBody.get()); retExpr->ty = TypeManager::GetNothingTy(); retExpr->refFuncBody = fun.funcBody.get(); blockNodes.push_back(std::move(methodCallRes)); blockNodes.push_back(std::move(retExpr)); // Return type has to be specified, so it's safe to use it below fun.funcBody->body = CreateBlock(std::move(blockNodes), fun.funcBody->retType->ty); fun.funcBody->ty = TypeManager::GetNothingTy(); if (IsSynthetic(mirror)) { fun.DisableAttr(Attribute::ABSTRACT); } } void JavaDesugarManager::DesugarJavaMirrorMethod(FuncDecl& fun, ClassLikeDecl& mirror) { CJC_ASSERT(!fun.TestAttr(Attribute::CONSTRUCTOR) && fun.TestAttr(Attribute::JAVA_MIRROR)); AddJavaMirrorMethodBody(mirror, fun, CreateJavaRefCall(mirror, mirror.curFile)); } void JavaDesugarManager::InsertJavaMirrorPropGetter(PropDecl& prop) { auto& mirror = *StaticAs(prop.outerDecl); auto curFile = prop.curFile; OwnedPtr jniGetterCall; auto isArrayGetLength = IsJArray(*prop.outerDecl) && GetArrayOperationKind(prop) == ArrayOperationKind::GET_LENGTH; if (isArrayGetLength) { jniGetterCall = lib.CreateCFFIArrayLengthGetCall(CreateJavaRefCall(mirror, curFile), curFile); } else if (prop.TestAttr(Attribute::STATIC)) { jniGetterCall = lib.CreateGetStaticFieldCall( lib.CreateGetJniEnvCall(curFile), utils.GetJavaClassNormalizeSignature(*mirror.ty), GetJavaMemberName(prop), utils.GetJavaTypeSignature(*prop.ty)); } else { jniGetterCall = lib.CreateGetFieldCall( lib.CreateGetJniEnvCall(curFile), CreateJavaRefCall(mirror, curFile), utils.GetJavaClassNormalizeSignature(*prop.outerDecl->ty), GetJavaMemberName(prop), utils.GetJavaTypeSignature(*prop.ty)); } auto jniGetterCallRes = CreateTmpVarDecl(nullptr, std::move(jniGetterCall)); jniGetterCallRes->ty = jniGetterCallRes->initializer->ty; CopyBasicInfo(jniGetterCallRes->initializer.get(), jniGetterCallRes.get()); auto callResRef = CreateRefExpr(*jniGetterCallRes); CopyBasicInfo(jniGetterCallRes.get(), callResRef.get()); auto outerDecl = As(prop.outerDecl); OwnedPtr unwrapJavaEntityCall = isArrayGetLength || !outerDecl ? std::move(callResRef) : lib.UnwrapJavaEntity(std::move(callResRef), prop.ty, *outerDecl); if (!unwrapJavaEntityCall) { prop.EnableAttr(Attribute::IS_BROKEN); prop.outerDecl->EnableAttr(Attribute::HAS_BROKEN); return; } auto& getter = *prop.getters.begin(); auto& getterBody = getter->funcBody; getterBody->body = CreateBlock({}, prop.ty); getterBody->ty = prop.ty; getterBody->body->body.emplace_back(std::move(jniGetterCallRes)); getterBody->body->body.emplace_back(CreateReturnExpr(std::move(unwrapJavaEntityCall), getterBody.get())); } void JavaDesugarManager::InsertJavaMirrorPropSetter(PropDecl& prop) { auto& mirror = *StaticAs(prop.outerDecl); auto curFile = prop.curFile; OwnedPtr jniSetterCall; auto paramRef = WithinFile(CreateRefExpr(*prop.setters[0]->funcBody->paramLists[0]->params[0]), curFile); if (prop.TestAttr(Attribute::STATIC)) { jniSetterCall = lib.CreateSetStaticFieldCall( lib.CreateGetJniEnvCall(curFile), utils.GetJavaClassNormalizeSignature(*mirror.ty), GetJavaMemberName(prop), utils.GetJavaTypeSignature(*prop.ty), std::move(paramRef)); } else { jniSetterCall = lib.CreateSetFieldCall( lib.CreateGetJniEnvCall(curFile), CreateJavaRefCall(mirror, curFile), MemberJNISignature(utils, prop), std::move(paramRef)); } auto block = CreateBlock({}, prop.ty); auto unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); auto& setter = *prop.setters.begin(); auto& setterBody = setter->funcBody; setterBody->body = std::move(block); setterBody->ty = unitTy; setterBody->body->body.emplace_back(std::move(jniSetterCall)); } void JavaDesugarManager::DesugarJavaMirrorProp(PropDecl& prop) { CJC_ASSERT(prop.outerDecl); InsertJavaMirrorPropGetter(prop); if (prop.isVar) { InsertJavaMirrorPropSetter(prop); } } void JavaDesugarManager::DesugarJavaMirror(ClassDecl& mirror) { if (IsSynthetic(mirror)) { mirror.DisableAttr(Attribute::ABSTRACT); } Ptr generatedCtor = GetGeneratedJavaMirrorConstructor(mirror); for (auto& decl : mirror.GetMemberDecls()) { if (auto fd = As(decl.get())) { if (fd->TestAttr(Attribute::IS_BROKEN)) { continue; } if (fd->TestAttr(Attribute::CONSTRUCTOR)) { if (!IsGeneratedJavaMirrorConstructor(*fd)) { DesugarJavaMirrorConstructor(*fd, *generatedCtor); } } else if (fd->TestAttr(Attribute::JAVA_MIRROR)) { DesugarJavaMirrorMethod(*fd, mirror); } } else if (auto prop = As(decl.get())) { DesugarJavaMirrorProp(*prop); } } } void JavaDesugarManager::DesugarJavaMirror(ClassLikeDecl& mirror) { for (auto& decl : mirror.GetMemberDecls()) { if (FuncDecl* fd = As(decl.get()); fd && !fd->TestAttr(Attribute::CONSTRUCTOR)) { if (!fd->TestAttr(Attribute::IS_BROKEN) && fd->TestAttr(Attribute::JAVA_MIRROR, Attribute::STATIC)) { DesugarJavaMirrorMethod(*fd, mirror); } } else if (auto prop = As(decl.get())) { // not supported yet auto message = (mirror.astKind == ASTKind::CLASS_DECL) ? "property in abstract class" : "property in interface"; diag.DiagnoseRefactor(DiagKindRefactor::sema_java_interop_not_supported, *prop, message); } } } void JavaDesugarManager::GenerateInMirror(ClassDecl& classDecl, bool doStub) { // Note: on [doStub] = true, no javaref field references may exist if (IsJObject(classDecl) && doStub) { // Generate java reference field for JObject. This field is accessible inside predecessors InsertJavaRefVarDecl(classDecl); } InsertJavaMirrorCtor(classDecl, doStub); if (&classDecl == utils.GetJStringDecl()) { InsertJStringOfStringCtor(classDecl, doStub); } if (!doStub && IsJArray(classDecl)) { InsertArrayJavaEntityGet(classDecl); InsertArrayJavaEntitySet(classDecl); } if (!doStub && IsJObject(classDecl)) { InsertJavaRefGetterWithBody(classDecl); } } void JavaDesugarManager::ReplaceCallsWithArrayJavaEntityGet(File& file) { Walker(&file, Walker::GetNextWalkerID(), [this, &file](auto node) { if (!node->IsSamePackage(*file.curPackage)) { return VisitAction::WALK_CHILDREN; } Ptr callExpr = As(node); if (!callExpr) { return VisitAction::WALK_CHILDREN; } auto funcDecl = callExpr->resolvedFunction; if (!funcDecl || !funcDecl->outerDecl || !IsJArray(*funcDecl->outerDecl) || funcDecl->identifier != "[]" || callExpr->args.size() != 1 ) { return VisitAction::WALK_CHILDREN; } auto ma = As(callExpr->baseFunc); auto arrayElementType = ma->baseExpr->ty->typeArgs[0]; if (!arrayElementType->IsClass() && !arrayElementType->IsCoreOptionType()) { return VisitAction::WALK_CHILDREN; } static auto arrayJavaEntityGetDecl = lib.FindArrayJavaEntityGetDecl( *As(funcDecl->outerDecl)); CJC_ASSERT(arrayJavaEntityGetDecl); auto newCallExpr = ASTCloner::Clone(callExpr); newCallExpr->resolvedFunction = arrayJavaEntityGetDecl; auto base = As(newCallExpr->baseFunc); base->target = arrayJavaEntityGetDecl; base->ty = arrayJavaEntityGetDecl->ty; callExpr->desugarExpr = lib.UnwrapJavaEntity( std::move(newCallExpr), arrayElementType, *As(funcDecl->outerDecl)); callExpr->desugarArgs = std::nullopt; return VisitAction::WALK_CHILDREN; }).Walk(); } void JavaDesugarManager::ReplaceCallsWithArrayJavaEntitySet(File& file) { Walker(&file, Walker::GetNextWalkerID(), [this, &file](auto node) { if (!node->IsSamePackage(*file.curPackage)) { return VisitAction::WALK_CHILDREN; } Ptr callExpr = As(node); if (!callExpr) { return VisitAction::WALK_CHILDREN; } auto funcDecl = callExpr->resolvedFunction; if (!funcDecl || !funcDecl->outerDecl || !IsJArray(*funcDecl->outerDecl) || funcDecl->identifier != "[]" || callExpr->args.size() != 2 ) { return VisitAction::WALK_CHILDREN; } auto ma = As(callExpr->baseFunc); auto arrayElementType = ma->baseExpr->ty->typeArgs[0]; if (!arrayElementType->IsClass() && !arrayElementType->IsCoreOptionType()) { return VisitAction::WALK_CHILDREN; } static auto arrayJavaEntitySetDecl = lib.FindArrayJavaEntitySetDecl( *As(funcDecl->outerDecl)); CJC_ASSERT(arrayJavaEntitySetDecl); auto newCallExpr = ASTCloner::Clone(callExpr); newCallExpr->resolvedFunction = arrayJavaEntitySetDecl; newCallExpr->args[1] = ASTCloner::Clone(newCallExpr->args[1].get()); newCallExpr->args[1]->expr = lib.WrapJavaEntity(std::move(newCallExpr->args[1]->expr)); newCallExpr->desugarArgs = std::nullopt; callExpr->desugarArgs = std::nullopt; auto base = As(newCallExpr->baseFunc); base->target = arrayJavaEntitySetDecl; base->ty = arrayJavaEntitySetDecl->ty; callExpr->args[1] = ASTCloner::Clone(newCallExpr->args[1].get()); callExpr->baseFunc = ASTCloner::Clone(newCallExpr->baseFunc.get()); callExpr->resolvedFunction = newCallExpr->resolvedFunction; callExpr->desugarExpr = std::move(newCallExpr); return VisitAction::WALK_CHILDREN; }).Walk(); } void JavaDesugarManager::GenerateInMirrors(File& file, bool doStub) { auto pkg = file.curPackage; if (!doStub) { std::once_flag flag; std::call_once(flag, [this, &pkg]() { TypeCheckUtil::GenerateGetTypeForTypeParamIntrinsic( *pkg, typeManager, importManager.GetCoreDecl(STD_LIB_STRING)->ty); }); } for (auto& decl : file.decls) { if (auto cldecl = As(decl.get())) { if (!cldecl->TestAttr(Attribute::JAVA_MIRROR)) { continue; } if (cldecl->TestAttr(Attribute::IS_BROKEN)) { return; } if (auto classDecl = As(cldecl)) { GenerateInMirror(*classDecl, doStub); } if (doStub && cldecl->astKind == ASTKind::INTERFACE_DECL) { InsertAbstractJavaRefGetter(*cldecl); } } } if (!doStub) { ReplaceCallsWithArrayJavaEntityGet(file); ReplaceCallsWithArrayJavaEntitySet(file); } } void JavaDesugarManager::DesugarMirrors(File& file) { for (auto& decl : file.decls) { if (auto cldecl = As(decl.get())) { if (cldecl->TestAttr(Attribute::JAVA_MIRROR)) { if (cldecl->TestAttr(Attribute::IS_BROKEN)) { return; } if (auto cd = DynamicCast(cldecl)) { DesugarJavaMirror(*cd); } else if (auto iDecl = DynamicCast(cldecl)) { DesugarJavaMirror(*iDecl); } } } } } void JavaDesugarManager::InsertJStringOfStringCtor(ClassDecl& decl, bool doStub) { static const std::string STRING_PARAM_NAME = "s"; static Ptr generatedCtor; CJC_ASSERT(&decl == utils.GetJStringDecl()); if (!doStub) { auto curFile = decl.curFile; // After constructor stub insertion, its body is filled on doStub = false CJC_NULLPTR_CHECK(generatedCtor); auto& param = generatedCtor->funcBody->paramLists[0]->params[0]; auto jObjectCtor = GetGeneratedJavaMirrorConstructor(decl); auto convertCall = CreateCall(lib.GetCangjieStringToJava(), curFile, lib.CreateGetJniEnvCall(curFile), WithinFile(CreateRefExpr(*param), curFile)); auto superCall = CreateSuperCall(decl, *jObjectCtor, jObjectCtor->ty); superCall->args.emplace_back(CreateFuncArg(std::move(convertCall))); generatedCtor->funcBody->body->body.emplace_back(std::move(superCall)); return; } // Initially, on doStub = true, it inserts constructor stub auto& stringDecl = utils.GetStringDecl(); auto param = CreateFuncParam(STRING_PARAM_NAME, CreateRefType(stringDecl), nullptr, stringDecl.ty); auto ctorFuncTy = typeManager.GetFunctionTy({param->ty}, decl.ty); std::vector> ctorParams; ctorParams.emplace_back(std::move(param)); std::vector> paramLists; paramLists.emplace_back(CreateFuncParamList(std::move(ctorParams))); auto ctorFuncBody = CreateFuncBody( std::move(paramLists), CreateRefType(decl), CreateBlock({}, decl.ty), decl.ty); auto fd = CreateFuncDecl("init", std::move(ctorFuncBody), ctorFuncTy); fd->fullPackageName = decl.fullPackageName; fd->outerDecl = &decl; fd->funcBody->funcDecl = fd.get(); fd->constructorCall = ConstructorCall::SUPER; fd->funcBody->parentClassLike = &decl; fd->EnableAttr( Attribute::PUBLIC, Attribute::JAVA_MIRROR, Attribute::CONSTRUCTOR, Attribute::IN_CLASSLIKE); generatedCtor = fd.get(); decl.body->decls.emplace_back(std::move(fd)); } } // namespace Cangjie::Interop::Java cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/AfterTypeCheck/DesugarPackage.cpp000066400000000000000000000067311510705540100273410ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "JavaDesugarManager.h" #include "JavaInteropManager.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Walker.h" #include "cangjie/Utils/ConstantsUtils.h" namespace Cangjie::Interop::Java { void JavaDesugarManager::ProcessJavaMirrorImplStage(DesugarJavaMirrorImplStage stage, File& file) { switch (stage) { case DesugarJavaMirrorImplStage::MIRROR_GENERATE_STUB: GenerateInMirrors(file, true); break; case DesugarJavaMirrorImplStage::MIRROR_GENERATE: GenerateInMirrors(file, false); break; case DesugarJavaMirrorImplStage::IMPL_GENERATE: GenerateInJavaImpls(file); break; case DesugarJavaMirrorImplStage::MIRROR_DESUGAR: DesugarMirrors(file); break; case DesugarJavaMirrorImplStage::IMPL_DESUGAR: DesugarInJavaImpls(file); break; case DesugarJavaMirrorImplStage::TYPECHECKS: DesugarTypechecks(file); break; default: CJC_ABORT(); // unreachable state } std::move(generatedDecls.begin(), generatedDecls.end(), std::back_inserter(file.decls)); generatedDecls.clear(); } void JavaDesugarManager::ProcessCJImplStage(DesugarCJImplStage stage, File& file) { switch (stage) { case DesugarCJImplStage::IMPL_GENERATE: GenerateInCJMapping(file); break; case DesugarCJImplStage::IMPL_DESUGAR: DesugarInCJMapping(file); break; case DesugarCJImplStage::TYPECHECKS: DesugarTypechecks(file); break; default: CJC_ABORT(); // unreachable state } std::move(generatedDecls.begin(), generatedDecls.end(), std::back_inserter(file.decls)); generatedDecls.clear(); } void JavaInteropManager::DesugarPackage(Package& pkg) { if (!(hasMirrorOrImpl || enableInteropCJMapping)) { return; } JavaDesugarManager desugarer{importManager, typeManager, diag, mangler, javagenOutputPath, outputPath}; if (hasMirrorOrImpl) { auto nbegin = static_cast(DesugarJavaMirrorImplStage::BEGIN); auto nend = static_cast(DesugarJavaMirrorImplStage::END); for (uint8_t nstage = nbegin; nstage != nend; nstage++) { auto stage = static_cast(nstage); if (stage == DesugarJavaMirrorImplStage::BEGIN) { continue; } for (auto& file : pkg.files) { desugarer.ProcessJavaMirrorImplStage(stage, *file); } } } // Currently CJMapping is enable by compile config --enable-interop-cjmapping if (enableInteropCJMapping) { auto nbegin = static_cast(DesugarCJImplStage::BEGIN); auto nend = static_cast(DesugarCJImplStage::END); for (uint8_t nstage = nbegin; nstage != nend; nstage++) { auto stage = static_cast(nstage); if (stage == DesugarCJImplStage::BEGIN) { continue; } for (auto& file : pkg.files) { desugarer.ProcessCJImplStage(stage, *file); } } } } } // namespace Cangjie::Interop::Java cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/AfterTypeCheck/DiagsInterop.cpp000066400000000000000000000062311510705540100270560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "DiagsInterop.h" #include "Diags.h" #include "Utils.h" namespace Cangjie::Interop::Java { using namespace Sema; namespace { Range MakeJavaImplJavaNameRange(const ClassLikeDecl& decl) { if (!HasPredefinedJavaName(decl)) { return MakeRangeForDeclIdentifier(decl); } for (auto& anno : decl.annotations) { if (anno->kind != AnnotationKind::JAVA_IMPL) { continue; } return MakeRange(anno->GetBegin(), decl.identifier.End()); } return MakeRange(decl.identifier.Begin(), decl.identifier.End()); } } // namespace void DiagJavaImplRedefinitionInJava(DiagnosticEngine& diag, const ClassLikeDecl& decl, const ClassLikeDecl& prevDecl) { if (decl.TestAttr(Attribute::IS_BROKEN)) { return; } auto prevDeclFqName = GetJavaFQSourceCodeName(prevDecl); CJC_ASSERT(GetJavaFQSourceCodeName(decl) == prevDeclFqName); auto rangePrev = MakeJavaImplJavaNameRange(prevDecl); auto rangeNext = MakeJavaImplJavaNameRange(decl); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_java_impl_redefinition, prevDecl, rangeNext, prevDeclFqName); builder.AddNote(decl, rangePrev, "'" + prevDeclFqName + "' is previously declared here"); } void DiagJavaMirrorChildMustBeAnnotated(DiagnosticEngine& diag, const ClassLikeDecl& decl) { SrcIdentifier parentId; for (auto& parentType: decl.inheritedTypes) { auto pty = parentType->ty; if (auto parent = DynamicCast(pty)) { parentId = parent->decl->identifier; break; } } diag.DiagnoseRefactor(DiagKindRefactor::sema_java_mirror_subtype_must_be_annotated, decl, parentId); } void DiagJavaDeclCannotInheritPureCangjieType(DiagnosticEngine& diag, ClassLikeDecl& decl) { CJC_ASSERT(IsMirror(decl) || IsImpl(decl)); auto kind = IsMirror(decl) ? DiagKindRefactor::sema_java_mirror_cannot_inherit_pure_cangjie_type : DiagKindRefactor::sema_java_impl_cannot_inherit_pure_cangjie_type; auto builder = diag.DiagnoseRefactor(kind, decl); for (const auto& superType : decl.inheritedTypes) { auto superDecl = Ty::GetDeclOfTy(superType->ty); CJC_ASSERT(superDecl); if (!IsMirror(*superDecl) && !IsImpl(*superDecl) && !superDecl->ty->IsObject() && !superDecl->ty->IsAny()) { builder.AddNote(*superType, "'" + superType->ToString() + "'" + " is not a java-compatible type"); } } } void DiagJavaDeclCannotBeExtendedWithInterface(DiagnosticEngine& diag, ExtendDecl& decl) { auto& ty = *decl.extendedType->ty; CJC_ASSERT(IsMirror(ty) || IsImpl(ty)); CJC_ASSERT(!decl.inheritedTypes.empty()); auto kind = IsMirror(ty) ? DiagKindRefactor::sema_java_mirror_cannot_be_extended_with_interface : DiagKindRefactor::sema_java_impl_cannot_be_extended_with_interface; diag.DiagnoseRefactor(kind, decl); } } // namespace cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/AfterTypeCheck/DiagsInterop.h000066400000000000000000000016231510705540100265230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_SEMA_NATIVE_FFI_JAVA_DIAGS #define CANGJIE_SEMA_NATIVE_FFI_JAVA_DIAGS #include "cangjie/Basic/DiagnosticEngine.h" namespace Cangjie::Interop::Java { using namespace Cangjie; using namespace AST; void DiagJavaImplRedefinitionInJava(DiagnosticEngine& diag, const ClassLikeDecl& decl, const ClassLikeDecl& prevDecl); void DiagJavaMirrorChildMustBeAnnotated(DiagnosticEngine& diag, const ClassLikeDecl& decl); void DiagJavaDeclCannotInheritPureCangjieType(DiagnosticEngine& diag, ClassLikeDecl& decl); void DiagJavaDeclCannotBeExtendedWithInterface(DiagnosticEngine& diag, ExtendDecl& decl); } #endif // CANGJIE_SEMA_NATIVE_FFI_JAVA_DIAGScangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/AfterTypeCheck/InteropLibBridge.cpp000066400000000000000000001602661510705540100276630ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "InteropLibBridge.h" #include "Desugar/AfterTypeCheck.h" #include "JavaDesugarManager.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Create.h" namespace { // vars constexpr auto INTEROPLIB_VERSION_FIELD_ID = "INTEROPLIB_VERSION"; constexpr auto JAVA_CONSTRUCTOR = ""; // types constexpr auto INTEROPLIB_JNI_ENV_PTR_ID = "JNIEnv_ptr"; constexpr auto INTEROPLIB_JNI_JLONG_ID = "jlong"; constexpr auto INTEROPLIB_JNI_JOBJECT_ID = "jobject"; constexpr auto INTEROPLIB_JAVA_ENTITY_KIND = "Java_CFFI_JavaEntityKind"; constexpr auto INTEROPLIB_JAVA_ENTITY_KIND_JOBJECT = "JOBJECT"; constexpr auto INTEROPLIB_JAVA_ENTITY_KIND_JBYTE = "JBYTE"; constexpr auto INTEROPLIB_JAVA_ENTITY_KIND_JSHORT = "JSHORT"; constexpr auto INTEROPLIB_JAVA_ENTITY_KIND_JCHAR = "JCHAR"; constexpr auto INTEROPLIB_JAVA_ENTITY_KIND_JINT = "JINT"; constexpr auto INTEROPLIB_JAVA_ENTITY_KIND_JLONG = "JLONG"; constexpr auto INTEROPLIB_JAVA_ENTITY_KIND_JFLOAT = "JFLOAT"; constexpr auto INTEROPLIB_JAVA_ENTITY_KIND_JDOUBLE = "JDOUBLE"; constexpr auto INTEROPLIB_JAVA_ENTITY_KIND_JBOOLEAN = "JBOOLEAN"; // funcs constexpr auto INTEROPLIB_JNI_GET_ENV_ID = "Java_CFFI_get_env"; constexpr auto INTEROPLIB_CFFI_NEW_GLOBAL_REF_ID = "Java_CFFI_newGlobalReference"; constexpr auto INTEROPLIB_CFFI_NEW_JAVA_OBJECT_ID = "Java_CFFI_newJavaObject"; constexpr auto INTEROPLIB_CFFI_NEW_JAVA_ARRAY_ID = "Java_CFFI_newJavaArray"; constexpr auto INTEROPLIB_CFFI_NEW_JAVA_PROXY_OBJECT_FOR_CJMAPPING_ID = "Java_CFFI_newJavaProxyObjectForCJMapping"; constexpr auto INTEROPLIB_CFFI_JAVA_ARRAY_GET_ID = "Java_CFFI_arrayGet"; constexpr auto INTEROPLIB_CFFI_JAVA_ARRAY_SET_ID = "Java_CFFI_arraySet"; constexpr auto INTEROPLIB_CFFI_JAVA_ARRAY_GET_LENGTH = "Java_CFFI_arrayGetLength"; constexpr auto INTEROPLIB_CFFI_JAVA_ENTITY_JOBJECT_ID = "Java_CFFI_JavaEntityJobject"; constexpr auto INTEROPLIB_CFFI_JAVA_ENTITY_NULL_ID = "Java_CFFI_JavaEntityJobjectNull"; constexpr auto INTEROPLIB_CFFI_JAVA_ENTITY_IS_NULL_ID = "isNull"; constexpr auto INTEROPLIB_JNI_PUT_TO_REGISTRY_DECL_ID = "Java_CFFI_put_to_registry_1"; constexpr auto INTEROPLIB_JNI_PUT_TO_REGISTRY_SELF_INIT_DECL_ID = "Java_CFFI_putToRegistry"; constexpr auto INTEROPLIB_JNI_DELETE_CJ_OBJECT_DECL_ID = "Java_CFFI_deleteCJObject"; constexpr auto INTEROPLIB_JNI_REMOVE_FROM_REGISTRY_DECL_ID = "Java_CFFI_removeFromRegistry"; constexpr auto INTEROPLIB_CFFI_CALL_METHOD_DECL_ID = "Java_CFFI_callVirtualMethod"; constexpr auto INTEROPLIB_CFFI_NON_VIRT_CALL_METHOD_DECL_ID = "Java_CFFI_callMethod"; constexpr auto INTEROPLIB_CFFI_CALL_STATIC_METHOD_DECL_ID = "Java_CFFI_callStaticMethod"; constexpr auto INTEROPLIB_CFFI_UNWRAP_JAVA_ENTITY_METHOD_DECL_ID = "Java_CFFI_unwrapJavaEntityAsValue"; constexpr auto INTEROPLIB_CFFI_UNWRAP_JAVA_MIRROR_METHOD_DECL_ID = "Java_CFFI_unwrapJavaMirror"; constexpr auto INTEROPLIB_CFFI_GET_FROM_REGISTRY_METHOD_DECL_ID = "Java_CFFI_getFromRegistry"; constexpr auto INTEROPLIB_CFFI_GET_FROM_REGISTRY_OPTION_METHOD_DECL_ID = "Java_CFFI_getFromRegistryOption"; constexpr auto INTEROPLIB_CFFI_GET_FIELD_METHOD_DECL_ID = "Java_CFFI_getField"; constexpr auto INTEROPLIB_CFFI_GET_STATIC_FIELD_METHOD_DECL_ID = "Java_CFFI_getStaticField"; constexpr auto INTEROPLIB_CFFI_SET_FIELD_METHOD_DECL_ID = "Java_CFFI_setField"; constexpr auto INTEROPLIB_CFFI_SET_STATIC_FIELD_METHOD_DECL_ID = "Java_CFFI_setStaticField"; constexpr auto INTEROPLIB_CFFI_ENSURE_NOT_NULL_METHOD_DECL_ID = "Java_CFFI_ensure_not_null"; constexpr auto INTEROPLIB_CFFI_GET_JAVA_ENTITY_OR_NULL_METHOD_DECL_ID = "Java_CFFI_getJavaEntityOrNull"; constexpr auto INTEROPLIB_CFFI_IS_INSTANCE_OF_DECL_ID = "Java_CFFI_isInstanceOf"; constexpr auto INTEROPLIB_CFFI_GET_FROM_REGISTRY_BY_ENTITY_OPTION_DECL_ID = "Java_CFFI_getFromRegistryByObjOption"; constexpr auto INTEROPLIB_CFFI_GET_FROM_REGISTRY_BY_ENTITY_DECL_ID = "Java_CFFI_getFromRegistryByObj"; constexpr auto INTEROPLIB_CFFI_JAVA_STRING_TO_CANGJIE = "Java_CFFI_JavaStringToCangjie"; constexpr auto INTEROPLIB_CFFI_CANGJIE_STRING_TO_JAVA = "Java_CFFI_CangjieStringToJava"; constexpr auto INTEROPLIB_CFFI_WITH_EXCEPTION_HANDLING_ID = "withExceptionHandling"; constexpr auto INTEROPLIB_CFFI_JAVA_CFFI_CLASS_ID = "Java_CFFI_ClassInit"; constexpr auto INTEROPLIB_CFFI_PARSE_METHOD_SIGNATURE_ID = "Java_CFFI_parseMethodSignature"; constexpr auto INTEROPLIB_CFFI_PARSE_COMPONENT_SIGNATURE_ID = "Java_CFFI_parseComponentSignature"; constexpr auto INTEROPLIB_CFFI_JAVA_CALLNEST_ID = "Java_CFFI_JavaCallNestInit"; constexpr auto INTEROPLIB_CFFI_JAVA_METHODID_CONSTR_ID = "Java_CFFI_MethodIDConstr"; constexpr auto INTEROPLIB_CFFI_JAVA_METHODID_CONSTR_STATIC_ID = "Java_CFFI_MethodIDConstrStatic"; constexpr auto INTEROPLIB_CFFI_JAVA_FIELDID_CONSTR_ID = "Java_CFFI_FieldIDConstr"; constexpr auto INTEROPLIB_CFFI_JAVA_FIELDID_CONSTR_STATIC_ID = "Java_CFFI_FieldIDConstrStatic"; } // namespace using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; using namespace Sema::Desugar::AfterTypeCheck; namespace Cangjie::Interop::Java { // declarations Ptr InteropLibBridge::GetJobjectDecl() { return GetInteropLibDecl(INTEROPLIB_JNI_JOBJECT_ID); } Ptr InteropLibBridge::GetJavaEntityDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_JAVA_ENTITY); } Ptr InteropLibBridge::GetJniEnvPtrDecl() { return GetInteropLibDecl(INTEROPLIB_JNI_ENV_PTR_ID); } Ptr InteropLibBridge::GetJavaEntityKindDecl() { return GetInteropLibDecl(INTEROPLIB_JAVA_ENTITY_KIND); } Decl& InteropLibBridge::GetJavaEntityKindJObject() { auto entityKindDecl = GetJavaEntityKindDecl(); return *LookupEnumMember(entityKindDecl, INTEROPLIB_JAVA_ENTITY_KIND_JOBJECT); } Ptr InteropLibBridge::GetNewGlobalRefDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_NEW_GLOBAL_REF_ID); } Ptr InteropLibBridge::GetGetJniEnvDecl() { return GetInteropLibDecl(INTEROPLIB_JNI_GET_ENV_ID); } Ptr InteropLibBridge::GetCreateJavaEntityJobjectDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_JAVA_ENTITY_JOBJECT_ID); } Ptr InteropLibBridge::GetCreateJavaEntityNullDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_JAVA_ENTITY_NULL_ID); } Ptr InteropLibBridge::GetNewJavaObjectDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_NEW_JAVA_OBJECT_ID); } Ptr InteropLibBridge::GetNewJavaArrayDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_NEW_JAVA_ARRAY_ID); } Ptr InteropLibBridge::GetNewJavaProxyObjectForCJMappingDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_NEW_JAVA_PROXY_OBJECT_FOR_CJMAPPING_ID); } Ptr InteropLibBridge::GetJavaArrayGetDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_JAVA_ARRAY_GET_ID); } Ptr InteropLibBridge::GetJavaArraySetDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_JAVA_ARRAY_SET_ID); } Ptr InteropLibBridge::GetJavaArrayGetLengthDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_JAVA_ARRAY_GET_LENGTH); } Ptr InteropLibBridge::GetCallMethodDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_CALL_METHOD_DECL_ID); } Ptr InteropLibBridge::GetNonVirtualCallMethodDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_NON_VIRT_CALL_METHOD_DECL_ID); } Ptr InteropLibBridge::GetCallStaticMethodDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_CALL_STATIC_METHOD_DECL_ID); } Ptr InteropLibBridge::GetDeleteCJObjectDecl() { return GetInteropLibDecl(INTEROPLIB_JNI_DELETE_CJ_OBJECT_DECL_ID); } Ptr InteropLibBridge::GetRemoveFromRegistryDecl() { return GetInteropLibDecl(INTEROPLIB_JNI_REMOVE_FROM_REGISTRY_DECL_ID); } Ptr InteropLibBridge::GetPutToRegistryDecl() { return GetInteropLibDecl(INTEROPLIB_JNI_PUT_TO_REGISTRY_DECL_ID); } Ptr InteropLibBridge::GetPutToRegistrySelfInitDecl() { return GetInteropLibDecl(INTEROPLIB_JNI_PUT_TO_REGISTRY_SELF_INIT_DECL_ID); } Ptr InteropLibBridge::GetUnwrapJavaEntityDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_UNWRAP_JAVA_ENTITY_METHOD_DECL_ID); } Ptr InteropLibBridge::GetUnwrapJavaMirrorDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_UNWRAP_JAVA_MIRROR_METHOD_DECL_ID); } Ptr InteropLibBridge::GetGetFromRegistryByEntityOptionDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_GET_FROM_REGISTRY_BY_ENTITY_OPTION_DECL_ID); } Ptr InteropLibBridge::GetGetFromRegistryByEntityDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_GET_FROM_REGISTRY_BY_ENTITY_DECL_ID); } Ptr InteropLibBridge::GetJavaStringToCangjie() { return GetInteropLibDecl(INTEROPLIB_CFFI_JAVA_STRING_TO_CANGJIE); } Ptr InteropLibBridge::GetCangjieStringToJava() { return GetInteropLibDecl(INTEROPLIB_CFFI_CANGJIE_STRING_TO_JAVA); } Ptr InteropLibBridge::GetGetFromRegistryDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_GET_FROM_REGISTRY_METHOD_DECL_ID); } Ptr InteropLibBridge::GetGetFromRegistryOptionDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_GET_FROM_REGISTRY_OPTION_METHOD_DECL_ID); } Ptr InteropLibBridge::GetGetFieldDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_GET_FIELD_METHOD_DECL_ID); } Ptr InteropLibBridge::GetGetStaticFieldDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_GET_STATIC_FIELD_METHOD_DECL_ID); } Ptr InteropLibBridge::GetSetFieldDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_SET_FIELD_METHOD_DECL_ID); } Ptr InteropLibBridge::GetSetStaticFieldDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_SET_STATIC_FIELD_METHOD_DECL_ID); } Ptr InteropLibBridge::GetInteropLibVersionVarDecl() { return GetInteropLibDecl(INTEROPLIB_VERSION_FIELD_ID, true); } Ptr InteropLibBridge::GetEnsureNotNullDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_ENSURE_NOT_NULL_METHOD_DECL_ID); } Ptr InteropLibBridge::GetGetJavaEntityOrNullDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_GET_JAVA_ENTITY_OR_NULL_METHOD_DECL_ID); } Ptr InteropLibBridge::GetIsInstanceOf() { return GetInteropLibDecl(INTEROPLIB_CFFI_IS_INSTANCE_OF_DECL_ID); } Ptr InteropLibBridge::GetWithExceptionHandlingDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_WITH_EXCEPTION_HANDLING_ID); } Ptr InteropLibBridge::GetJClassDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_JAVA_CFFI_CLASS_ID); } Ptr InteropLibBridge::GetParseMethodSignatureDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_PARSE_METHOD_SIGNATURE_ID); } Ptr InteropLibBridge::GetParseComponentSignatureDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_PARSE_COMPONENT_SIGNATURE_ID); } Ptr InteropLibBridge::GetCallNestDecl() { return GetInteropLibDecl(INTEROPLIB_CFFI_JAVA_CALLNEST_ID); } Ptr InteropLibBridge::GetMethodIdConstr() { return GetInteropLibDecl(INTEROPLIB_CFFI_JAVA_METHODID_CONSTR_ID); } Ptr InteropLibBridge::GetMethodIdConstrStatic() { return GetInteropLibDecl(INTEROPLIB_CFFI_JAVA_METHODID_CONSTR_STATIC_ID); } Ptr InteropLibBridge::GetFieldIdConstr() { return GetInteropLibDecl(INTEROPLIB_CFFI_JAVA_FIELDID_CONSTR_ID); } Ptr InteropLibBridge::GetFieldIdConstrStatic() { return GetInteropLibDecl(INTEROPLIB_CFFI_JAVA_FIELDID_CONSTR_STATIC_ID); } // ty Ptr InteropLibBridge::GetJNIEnvPtrTy() { auto decl = GetJniEnvPtrDecl(); if (!decl) { return nullptr; } return decl->type->ty; } Ptr InteropLibBridge::GetJavaEntityTy() { auto decl = GetJavaEntityDecl(); if (!decl) { return nullptr; } return decl->ty; } Ptr InteropLibBridge::GetJobjectTy() { return typeManager.GetPointerTy(typeManager.GetPrimitiveTy(TypeKind::TYPE_UNIT)); } OwnedPtr InteropLibBridge::CreateJobjectNull() { auto pointerExpr = MakeOwnedNode(); pointerExpr->type = MakeOwnedNode(); pointerExpr->type->ty = GetJobjectTy(); pointerExpr->ty = pointerExpr->type->ty; return pointerExpr; } OwnedPtr InteropLibBridge::CreateEnvFuncParam() { auto decl = GetJniEnvPtrDecl(); CJC_NULLPTR_CHECK(decl); CJC_NULLPTR_CHECK(decl->type); const std::string name = "env"; return CreateFuncParam(name, ASTCloner::Clone(decl->type.get()), nullptr, decl->type->ty); } OwnedPtr InteropLibBridge::CreateJClassOrJObjectFuncParam(const std::string& name) { return CreateFuncParam(name, CreateJobjectType(), nullptr, GetJobjectTy()); } OwnedPtr InteropLibBridge::CreateSelfFuncParam() { const std::string name = "self"; return CreateFuncParam(name, CreateJlongType(), nullptr, GetJlongTy()); } Ptr InteropLibBridge::GetJlongTy() { return typeManager.GetPrimitiveTy(TypeKind::TYPE_INT64); } // type OwnedPtr InteropLibBridge::CreateJobjectType() { Ptr jobjectDecl = GetInteropLibDecl(INTEROPLIB_JNI_JOBJECT_ID); if (!jobjectDecl) { return nullptr; } return ASTCloner::Clone(Ptr(StaticAs(jobjectDecl)->type.get())); } OwnedPtr InteropLibBridge::CreateJlongType() { Ptr jlongDecl = GetInteropLibDecl(INTEROPLIB_JNI_JLONG_ID); if (!jlongDecl) { return nullptr; } return ASTCloner::Clone(Ptr(StaticAs(jlongDecl)->type.get())); } // applications OwnedPtr InteropLibBridge::CreateGetJniEnvCall(Ptr curFile) { return CreateCall(GetGetJniEnvDecl(), curFile); } OwnedPtr InteropLibBridge::CreateJavaEntityCall(Ptr file) { auto javaEntityDecl = GetJavaEntityDecl(); if (!javaEntityDecl) { return nullptr; } Ptr suitableCtor; for (auto& decl : javaEntityDecl->body->decls) { if (auto ctor = As(decl.get())) { if (ctor->funcBody->paramLists[0]->params.empty()) { suitableCtor = ctor; break; } } } if (!suitableCtor) { diag.DiagnoseRefactor( DiagKindRefactor::sema_member_not_imported, DEFAULT_POSITION, INTEROPLIB_CFFI_JAVA_ENTITY); } auto call = CreateCall(suitableCtor, file); if (call) { call->callKind = CallKind::CALL_STRUCT_CREATION; } return call; } OwnedPtr InteropLibBridge::CreateJavaEntityJobjectCall(OwnedPtr arg) { auto curFile = arg->curFile; return CreateCall(GetCreateJavaEntityJobjectDecl(), curFile, std::move(arg)); } OwnedPtr InteropLibBridge::CreateJavaEntityNullCall(Ptr curFile) { return CreateCall(GetCreateJavaEntityNullDecl(), curFile); } OwnedPtr InteropLibBridge::CreateJavaEntityFromOptionMirror(OwnedPtr option, ClassLikeDecl& mirror) { auto curFile = option->curFile; CJC_NULLPTR_CHECK(curFile); auto mirrorTy = option->ty->typeArgs[0]; // `case Some(argv) => argv.javaref` auto vp = CreateVarPattern(V_COMPILER, mirrorTy); vp->curFile = curFile; vp->varDecl->curFile = curFile; auto javarefAccess = CreateJavaRefCall(WithinFile(CreateRefExpr(*vp->varDecl), curFile), mirror); auto somePattern = MakeOwnedNode(); somePattern->ty = utils.GetOptionTy(mirrorTy); somePattern->constructor = utils.CreateOptionSomeRef(mirrorTy); somePattern->patterns.emplace_back(std::move(vp)); somePattern->curFile = curFile; auto caseSome = CreateMatchCase(std::move(somePattern), std::move(javarefAccess)); // `case None => Java_CFFI_JavaEntityJobjectNull()` auto nonePattern = MakeOwnedNode(); nonePattern->constructor = utils.CreateOptionNoneRef(mirrorTy); nonePattern->ty = nonePattern->constructor->ty; nonePattern->curFile = curFile; auto caseNone = CreateMatchCase(std::move(nonePattern), CreateJavaEntityNullCall(curFile)); // `match` std::vector> matchCases; matchCases.emplace_back(std::move(caseSome)); matchCases.emplace_back(std::move(caseNone)); return WithinFile(CreateMatchExpr(std::move(option), std::move(matchCases), GetJavaEntityTy()), curFile); } OwnedPtr InteropLibBridge::CreateJavaEntityCall(OwnedPtr arg) { auto javaEntityDecl = GetJavaEntityDecl(); if (!javaEntityDecl) { return nullptr; } if (auto classLTy = DynamicCast(arg->ty)) { if (auto decl = classLTy->commonDecl; decl && decl->TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE)) { return CreateJavaRefCall(std::move(arg)); } } else if (arg->ty->IsCoreOptionType()) { if (auto classALTy = DynamicCast(arg->ty->typeArgs[0])) { if (auto decl = classALTy->commonDecl; decl && decl->TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE)) { return CreateJavaEntityFromOptionMirror(std::move(arg), *decl); } } } Ptr suitableCtor; for (auto& decl : javaEntityDecl->body->decls) { if (auto ctor = As(decl.get()); ctor && ctor->TestAttr(Attribute::CONSTRUCTOR)) { if (ctor->funcBody->paramLists[0]->params.size() != 1) { continue; } if (typeManager.IsTyEqual(arg->ty, ctor->funcBody->paramLists[0]->params[0]->ty)) { suitableCtor = ctor; } } } if (!suitableCtor) { diag.DiagnoseRefactor(DiagKindRefactor::sema_java_interop_not_supported, arg->begin, "Type " + arg->ty->name); return nullptr; } auto curFile = arg->curFile; auto call = CreateCall(suitableCtor, curFile, std::move(arg)); if (call) { call->callKind = CallKind::CALL_STRUCT_CREATION; } return call; } OwnedPtr InteropLibBridge::WrapJavaEntity(OwnedPtr cjExpr) { CJC_NULLPTR_CHECK(cjExpr->curFile); if (cjExpr->ty->kind == TypeKind::TYPE_UNIT) { return CreateJavaEntityCall(cjExpr->curFile); } return CreateJavaEntityCall(std::move(cjExpr)); } OwnedPtr InteropLibBridge::CreateNewJavaObjectCall(OwnedPtr env, const std::string& classTypeSignature, const std::string& constructorSignature, std::vector> args) { auto curFile = env->curFile; auto funcDecl = GetNewJavaObjectDecl(); auto jclassInitFuncDecl = GetJClassDecl(); auto parseSignatureFuncDecl = GetParseMethodSignatureDecl(); auto callNestFuncDecl = GetCallNestDecl(); auto methodIDFuncDecl = GetMethodIdConstr(); if (!funcDecl || !jclassInitFuncDecl || !parseSignatureFuncDecl || !callNestFuncDecl || !methodIDFuncDecl) { return nullptr; } CJC_ASSERT(jclassInitFuncDecl->funcBody->paramLists[0] && jclassInitFuncDecl->funcBody->paramLists[0]->params[1]); CJC_ASSERT(callNestFuncDecl->funcBody->paramLists[0] && callNestFuncDecl->funcBody->paramLists[0]->params[0]); auto strTy = jclassInitFuncDecl->funcBody->paramLists[0]->params[1]->ty; auto intTy = callNestFuncDecl->funcBody->paramLists[0]->params[0]->ty; auto lclassName = CreateLitConstExpr(LitConstKind::STRING, classTypeSignature, strTy); lclassName->curFile = curFile; auto lConstructorSignature = CreateLitConstExpr(LitConstKind::STRING, constructorSignature, strTy); lConstructorSignature->curFile = curFile; auto lmethodName = CreateLitConstExpr(LitConstKind::STRING, JAVA_CONSTRUCTOR, strTy); lmethodName->curFile = curFile; auto jclass = CreateCall(jclassInitFuncDecl, curFile, CreateGetJniEnvCall(curFile), std::move(lclassName)); auto methodSignature = CreateCall(parseSignatureFuncDecl, curFile, std::move(lConstructorSignature)); auto methodID = CreateCall(methodIDFuncDecl, curFile, CreateGetJniEnvCall(curFile), ASTCloner::Clone(jclass.get()), std::move(lmethodName), std::move(methodSignature)); auto arrayStruct = importManager.GetCoreDecl(STD_LIB_ARRAY); if (!arrayStruct) { return nullptr; } std::vector> arrParamsTy; arrParamsTy.push_back(GetJavaEntityTy()); auto arrayTy = typeManager.GetStructTy(*arrayStruct, arrParamsTy); auto argsSize = CreateLitConstExpr(LitConstKind::INTEGER, std::to_string(args.size()), intTy); auto argsAsArray = WithinFile(CreateArrayLit(std::move(args), arrayTy), curFile); auto callNest = CreateCall(callNestFuncDecl, curFile, std::move(argsSize)); return CreateCall(funcDecl, curFile, std::move(env), std::move(jclass), std::move(methodID), std::move(argsAsArray), std::move(callNest)); } OwnedPtr InteropLibBridge::SelectJSigByTypeKind([[maybe_unused]] TypeKind kind, Ptr ty) { static auto strTy = utils.GetStringDecl().ty; return CreateLitConstExpr(LitConstKind::STRING, utils.GetJavaTypeSignature(*ty), strTy); } OwnedPtr InteropLibBridge::SelectJPrimitiveNameByTypeKind(TypeKind kind, [[maybe_unused]] Ptr ty) { static auto javaEntityKindDecl = GetJavaEntityKindDecl(); std::string typeName; switch (kind) { case TypeKind::TYPE_INT8: typeName = INTEROPLIB_JAVA_ENTITY_KIND_JBYTE; break; case TypeKind::TYPE_INT16: typeName = INTEROPLIB_JAVA_ENTITY_KIND_JSHORT; break; case TypeKind::TYPE_UINT16: typeName = INTEROPLIB_JAVA_ENTITY_KIND_JCHAR; break; case TypeKind::TYPE_INT32: typeName = INTEROPLIB_JAVA_ENTITY_KIND_JINT; break; case TypeKind::TYPE_INT64: typeName = INTEROPLIB_JAVA_ENTITY_KIND_JLONG; break; case TypeKind::TYPE_FLOAT32: typeName = INTEROPLIB_JAVA_ENTITY_KIND_JFLOAT; break; case TypeKind::TYPE_FLOAT64: typeName = INTEROPLIB_JAVA_ENTITY_KIND_JDOUBLE; break; case TypeKind::TYPE_BOOLEAN: typeName = INTEROPLIB_JAVA_ENTITY_KIND_JBOOLEAN; break; case TypeKind::TYPE_ENUM: typeName = INTEROPLIB_JAVA_ENTITY_KIND_JOBJECT; break; default: CJC_ABORT(); break; } return CreateRefExpr(*LookupEnumMember(javaEntityKindDecl, typeName)); } OwnedPtr InteropLibBridge::SelectEntityWrapperByTypeKind([[maybe_unused]] TypeKind kind, [[maybe_unused]] Ptr ty, Ptr param, Ptr file) { auto paramRef = CreateRefExpr(*param); paramRef->curFile = file; return WrapJavaEntity(CreateMatchWithTypeCast(std::move(paramRef), ty)); } OwnedPtr InteropLibBridge::SelectEntityUnwrapperByTypeKind([[maybe_unused]] TypeKind kind, [[maybe_unused]] Ptr ty, Ptr entity, const ClassLikeDecl& mirror) { auto cEntity = ASTCloner::Clone(entity); cEntity->curFile = entity->curFile; return CreateMatchWithTypeCast(UnwrapJavaEntity(std::move(cEntity), ty, mirror), ty); } std::map> InteropLibBridge::GenerateTypeMappingWithSelector( std::function(TypeKind, Ptr)> selector) { std::map> typeMapping; for (auto& type : supportedArrayPrimitiveElementType) { auto ty = TypeManager::GetPrimitiveTy(type); typeMapping.insert(std::make_pair(Ty::ToString(ty), selector(type, ty))); } return typeMapping; } OwnedPtr InteropLibBridge::CreateCFFIArrayLengthGetCall(OwnedPtr javarefExpr, Ptr curFile) { return CreateCall(GetJavaArrayGetLengthDecl(), curFile, CreateGetJniEnvCall(curFile), std::move(javarefExpr)); } OwnedPtr InteropLibBridge::CreateCFFINewJavaArrayCall( OwnedPtr jniEnv, FuncParamList& params, const Ptr genericParam) { auto curFile = genericParam->curFile; static auto funcDecl = GetNewJavaArrayDecl(); if (!funcDecl) { return nullptr; } static auto strTy = utils.GetStringDecl().ty; auto defaultTypeOption = CreateLitConstExpr(LitConstKind::STRING, utils.GetJavaTypeSignature(*utils.GetJObjectDecl()->ty), strTy); defaultTypeOption->curFile = curFile; auto typeMatch = CreateMatchByTypeArgument(genericParam, GenerateTypeMappingWithSelector([this](TypeKind kind, Ptr ty) { return SelectJSigByTypeKind(kind, ty); }), strTy, std::move(defaultTypeOption)); auto sizeParam = WithinFile(CreateRefExpr(*params.params[0]), curFile); return CreateCall(funcDecl, curFile, std::move(jniEnv), std::move(typeMatch), std::move(sizeParam)); } OwnedPtr InteropLibBridge::CreateCFFINewJavaCFFINewJavaProxyObjectForCJMappingCall( OwnedPtr jniEnv, OwnedPtr entity, std::string name, bool withMarkerParam) { auto curFile = entity->curFile; static auto funcDecl = GetNewJavaProxyObjectForCJMappingDecl(); if (!funcDecl) { return nullptr; } static auto retTy = utils.GetStringDecl().ty; auto expr = CreateLitConstExpr(LitConstKind::STRING, name, retTy); std::string hasArg = withMarkerParam ? "true" : "false"; static const auto BOOL_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); auto literal = CreateLitConstExpr(LitConstKind::BOOL, hasArg, BOOL_TY); return CreateCall(funcDecl, curFile, std::move(jniEnv), std::move(entity), std::move(expr), std::move(literal)); } Ptr InteropLibBridge::FindArrayJavaEntityGetDecl(ClassDecl& jArrayDecl) const { for (auto& member : jArrayDecl.body->decls) { if (member->identifier == JAVA_ARRAY_GET_FOR_REF_TYPES) { return As(member); } } return nullptr; } Ptr InteropLibBridge::FindArrayJavaEntitySetDecl(ClassDecl& jArrayDecl) const { for (auto& member : jArrayDecl.body->decls) { if (member->identifier == JAVA_ARRAY_SET_FOR_REF_TYPES) { return As(member); } } return nullptr; } OwnedPtr InteropLibBridge::CreateCFFINewJavaObjectCall( OwnedPtr jniEnv, std::string classTypeSignature, FuncParamList& params, bool isMirror, File& curFile) { static auto markerClassDecl = CreateConstructorMarkerClassDecl(); std::vector> cffiCtorFuncArgs; for (auto& param : params.params) { cffiCtorFuncArgs.push_back(WrapJavaEntity(WithinFile(CreateRefExpr(*param), Ptr(&curFile)))); } std::vector> paramTys = Native::FFI::GetParamTys(params); if (!isMirror) { // in java, the constructor with additional fake null argument // of special marker types is created. cffiCtorFuncArgs.push_back(CreateJavaEntityNullCall(&curFile)); paramTys.push_back(typeManager.GetClassTy(*markerClassDecl, {})); } return CreateNewJavaObjectCall(std::move(jniEnv), classTypeSignature, utils.GetJavaTypeSignature(*TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT), paramTys), std::move(cffiCtorFuncArgs)); } OwnedPtr InteropLibBridge::CreateNewGlobalRefCall(OwnedPtr env, OwnedPtr obj, bool isWeak) { auto curFile = obj->curFile; auto isWeakBoolValue = CreateBoolLit(isWeak); isWeakBoolValue->curFile = curFile; isWeakBoolValue->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); return CreateCall(GetNewGlobalRefDecl(), curFile, std::move(env), std::move(obj), std::move(isWeakBoolValue)); } OwnedPtr InteropLibBridge::CreateCallMethodCall(OwnedPtr env, OwnedPtr obj, const MemberJNISignature& signature, std::vector> args, File& curFile, bool virt) { auto pcurFile = Ptr(&curFile); auto funcDecl = virt ? GetCallMethodDecl() : GetNonVirtualCallMethodDecl(); auto jclassInitFuncDecl = GetJClassDecl(); auto parseSignatureFuncDecl = GetParseMethodSignatureDecl(); auto callNestFuncDecl = GetCallNestDecl(); auto methodIDFuncDecl = GetMethodIdConstr(); if (!funcDecl || !jclassInitFuncDecl || !parseSignatureFuncDecl || !callNestFuncDecl || !methodIDFuncDecl) { return nullptr; } CJC_ASSERT(jclassInitFuncDecl->funcBody->paramLists[0] && jclassInitFuncDecl->funcBody->paramLists[0]->params[1]); CJC_ASSERT(callNestFuncDecl->funcBody->paramLists[0] && callNestFuncDecl->funcBody->paramLists[0]->params[0]); auto strTy = jclassInitFuncDecl->funcBody->paramLists[0]->params[1]->ty; auto intTy = callNestFuncDecl->funcBody->paramLists[0]->params[0]->ty; auto lclassName = CreateLitConstExpr(LitConstKind::STRING, signature.classTypeSignature, strTy); lclassName->curFile = pcurFile; auto lmethodName = CreateLitConstExpr(LitConstKind::STRING, signature.name, strTy); lmethodName->curFile = pcurFile; auto lsignature = CreateLitConstExpr(LitConstKind::STRING, signature.signature, strTy); lsignature->curFile = pcurFile; auto jclass = CreateCall(jclassInitFuncDecl, pcurFile, CreateGetJniEnvCall(pcurFile), std::move(lclassName)); auto methodSignature = CreateCall(parseSignatureFuncDecl, pcurFile, std::move(lsignature)); auto methodID = CreateCall(methodIDFuncDecl, pcurFile, CreateGetJniEnvCall(pcurFile), std::move(jclass), std::move(lmethodName), std::move(methodSignature)); auto arrayStruct = importManager.GetCoreDecl(STD_LIB_ARRAY); if (!arrayStruct) { return nullptr; } auto entityTy = GetJavaEntityTy(); if (!entityTy) { return nullptr; } std::vector> arrParamsTy; arrParamsTy.push_back(entityTy); auto arrayTy = typeManager.GetStructTy(*arrayStruct, arrParamsTy); auto argsSize = CreateLitConstExpr(LitConstKind::INTEGER, std::to_string(args.size()), intTy); auto callNest = CreateCall(callNestFuncDecl, pcurFile, std::move(argsSize)); return CreateCall(funcDecl, pcurFile, std::move(env), std::move(obj), std::move(methodID), CreateArrayLit(std::move(args), arrayTy), std::move(callNest)); } OwnedPtr InteropLibBridge::CreateCallStaticMethodCall( OwnedPtr env, const MemberJNISignature& signature, std::vector> args, File& curFile) { auto pcurFile = Ptr(&curFile); auto funcDecl = GetCallStaticMethodDecl(); auto jclassInitFuncDecl = GetJClassDecl(); auto parseSignatureFuncDecl = GetParseMethodSignatureDecl(); auto callNestFuncDecl = GetCallNestDecl(); auto methodIDFuncDecl = GetMethodIdConstrStatic(); if (!funcDecl || !jclassInitFuncDecl || !parseSignatureFuncDecl || !callNestFuncDecl || !methodIDFuncDecl) { return nullptr; } CJC_ASSERT(jclassInitFuncDecl->funcBody->paramLists[0] && jclassInitFuncDecl->funcBody->paramLists[0]->params[1]); CJC_ASSERT(callNestFuncDecl->funcBody->paramLists[0] && callNestFuncDecl->funcBody->paramLists[0]->params[0]); auto strTy = jclassInitFuncDecl->funcBody->paramLists[0]->params[1]->ty; auto intTy = callNestFuncDecl->funcBody->paramLists[0]->params[0]->ty; auto lclassName = CreateLitConstExpr(LitConstKind::STRING, signature.classTypeSignature, strTy); lclassName->curFile = pcurFile; auto lmethodName = CreateLitConstExpr(LitConstKind::STRING, signature.name, strTy); lmethodName->curFile = pcurFile; auto lsignature = CreateLitConstExpr(LitConstKind::STRING, signature.signature, strTy); lsignature->curFile = pcurFile; auto jclass = CreateCall(jclassInitFuncDecl, pcurFile, CreateGetJniEnvCall(pcurFile), std::move(lclassName)); auto methodSignature = CreateCall(parseSignatureFuncDecl, pcurFile, std::move(lsignature)); auto methodID = CreateCall(methodIDFuncDecl, pcurFile, CreateGetJniEnvCall(pcurFile), ASTCloner::Clone(jclass.get()), std::move(lmethodName), std::move(methodSignature)); auto arrayStruct = importManager.GetCoreDecl(STD_LIB_ARRAY); if (!arrayStruct) { return nullptr; } auto entityTy = GetJavaEntityTy(); if (!entityTy) { return nullptr; } std::vector> arrParamsTy; arrParamsTy.push_back(entityTy); auto arrayTy = typeManager.GetStructTy(*arrayStruct, arrParamsTy); auto argsSize = CreateLitConstExpr(LitConstKind::INTEGER, std::to_string(args.size()), intTy); auto callNest = CreateCall(callNestFuncDecl, pcurFile, std::move(argsSize)); return CreateCall(funcDecl, pcurFile, std::move(env), std::move(jclass), std::move(methodID), CreateArrayLit(std::move(args), arrayTy), std::move(callNest)); } OwnedPtr InteropLibBridge::CreateMatchWithTypeCast(OwnedPtr exprToCast, Ptr castTy) { static auto exceptionDecl = importManager.GetCoreDecl(CLASS_EXCEPTION); if (!exceptionDecl) { return nullptr; } auto castType = MakeOwned(); castType->ty = castTy; auto varPattern = CreateVarPattern(V_COMPILER, castType->ty); auto curFile = exprToCast->curFile; CJC_NULLPTR_CHECK(curFile); varPattern->curFile = curFile; varPattern->varDecl->curFile = curFile; auto varPatternRef = WithinFile(CreateRefExpr(*(varPattern->varDecl)), curFile); std::vector> matchCases; auto typePattern = CreateTypePattern(std::move(varPattern), std::move(castType), *exprToCast); typePattern->matchBeforeRuntime = false; std::vector> exceptionCallArgs; exceptionCallArgs.emplace_back(CreateLitConstExpr(LitConstKind::STRING, "internal error: CreateMatchWithTypeCast(" + Ty::KindName(exprToCast->ty->kind) + " -> " + Ty::KindName(castTy->kind) + ")", utils.GetStringDecl().ty)); exceptionCallArgs.back()->curFile = curFile; matchCases.emplace_back(CreateMatchCase(std::move(typePattern), std::move(varPatternRef))); matchCases[0]->curFile = curFile; matchCases.emplace_back(CreateMatchCase(MakeOwned(), CreateThrowException(*exceptionDecl, std::move(exceptionCallArgs), *exprToCast->curFile, typeManager))); matchCases[1]->curFile = curFile; return WithinFile(CreateMatchExpr(std::move(exprToCast), std::move(matchCases), castTy), curFile); } OwnedPtr InteropLibBridge::CreateCFFICallArrayMethodCall(OwnedPtr jniEnv, OwnedPtr obj, FuncParamList& params, const Ptr genericParam, ArrayOperationKind kind) { CJC_ASSERT(kind == ArrayOperationKind::GET || kind == ArrayOperationKind::SET); static auto javaEntityKindDecl = GetJavaEntityKindDecl(); static auto jObject = utils.GetJObjectDecl(); static auto javaRef = GetJavaRefField(*jObject); CJC_ASSERT(javaRef); auto curFile = genericParam->curFile; auto funcDecl = kind == ArrayOperationKind::GET ? GetJavaArrayGetDecl() : GetJavaArraySetDecl(); auto indexArg = params.params[0].get(); auto matchWithJPrimitive = CreateMatchByTypeArgument(genericParam, GenerateTypeMappingWithSelector( [this](TypeKind kind, Ptr ty) { return SelectJPrimitiveNameByTypeKind(kind, ty); }), javaEntityKindDecl->ty, WithinFile(CreateRefExpr(GetJavaEntityKindJObject()), curFile)); if (kind == ArrayOperationKind::GET) { return CreateCall(funcDecl, curFile, std::move(jniEnv), std::move(matchWithJPrimitive), std::move(obj), WithinFile(CreateRefExpr(*indexArg), curFile)); } auto valueArg = params.params[1].get(); auto paramRef = WithinFile(CreateRefExpr(*valueArg), curFile); OwnedPtr valueEntity; if (valueArg->ty->name == INTEROPLIB_CFFI_JAVA_ENTITY) { // for generated function - value argument was replaced with java entity valueEntity = std::move(paramRef); } else { valueEntity = CreateMatchByTypeArgument(genericParam, GenerateTypeMappingWithSelector([this, &valueArg, &curFile](TypeKind kind, Ptr ty) { return SelectEntityWrapperByTypeKind(kind, ty, valueArg, curFile); }), GetJavaEntityDecl()->ty, CreateMemberAccess(CreateMatchWithTypeCast(std::move(paramRef), jObject->ty), *javaRef)); } return CreateCall(funcDecl, curFile, std::move(jniEnv), std::move(matchWithJPrimitive), std::move(obj), WithinFile(CreateRefExpr(*indexArg), curFile), std::move(valueEntity)); } OwnedPtr InteropLibBridge::CreateCFFICallMethodCall(OwnedPtr jniEnv, OwnedPtr obj, const MemberJNISignature& signature, FuncParamList& params, File& curFile) { std::vector> cffiMethodArgs; CJC_NULLPTR_CHECK(obj->curFile); for (auto& param : params.params) { auto paramRef = WithinFile(CreateRefExpr(*param), Ptr(&curFile)); paramRef->curFile = obj->curFile; cffiMethodArgs.push_back(WrapJavaEntity(std::move(paramRef))); } return CreateCallMethodCall(std::move(jniEnv), std::move(obj), signature, std::move(cffiMethodArgs), curFile); } OwnedPtr InteropLibBridge::CreateCFFICallStaticMethodCall( OwnedPtr jniEnv, const MemberJNISignature& signature, FuncParamList& params, File& curFile) { std::vector> cffiMethodArgs; for (auto& param : params.params) { cffiMethodArgs.push_back(WrapJavaEntity(WithinFile(CreateRefExpr(*param), Ptr(&curFile)))); } return CreateCallStaticMethodCall(std::move(jniEnv), signature, std::move(cffiMethodArgs), curFile); } OwnedPtr InteropLibBridge::CreateDeleteCJObjectCall( OwnedPtr env, OwnedPtr self, OwnedPtr getWeakRef, Ptr cjTy) { auto curFile = self->curFile; auto funcDecl = GetDeleteCJObjectDecl(); if (!funcDecl) { return nullptr; } std::vector> callArgs; callArgs.push_back(CreateFuncArg(std::move(env))); callArgs.push_back(CreateFuncArg(std::move(self))); callArgs.push_back(CreateFuncArg(std::move(getWeakRef))); auto unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); auto fdRef = WithinFile(CreateRefExpr(*funcDecl), curFile); fdRef->instTys.push_back(cjTy); fdRef->ty = typeManager.GetInstantiatedTy(funcDecl->ty, GenerateTypeMapping(*funcDecl, fdRef->instTys)); return CreateCallExpr(std::move(fdRef), std::move(callArgs), funcDecl, unitTy, CallKind::CALL_DECLARED_FUNCTION); } OwnedPtr InteropLibBridge::CreateRemoveFromRegistryCall(OwnedPtr self) { auto curFile = self->curFile; return CreateCall(GetRemoveFromRegistryDecl(), curFile, std::move(self)); } OwnedPtr InteropLibBridge::CreatePutToRegistryCall(OwnedPtr obj) { auto curFile = obj->curFile; return CreateCall(GetPutToRegistryDecl(), curFile, std::move(obj)); } OwnedPtr InteropLibBridge::CreatePutToRegistrySelfInitCall( OwnedPtr env, OwnedPtr entity, OwnedPtr obj) { auto curFile = entity->curFile; return CreateCall(GetPutToRegistrySelfInitDecl(), curFile, std::move(env), std::move(entity), std::move(obj)); } OwnedPtr InteropLibBridge::CreateGetFromRegistryByEntityCall( OwnedPtr env, OwnedPtr obj, Ptr ty, bool retAsOption) { auto curFile = obj->curFile; auto funcDecl = retAsOption ? GetGetFromRegistryByEntityOptionDecl() : GetGetFromRegistryByEntityDecl(); if (!funcDecl) { return nullptr; } std::vector> callArgs; callArgs.push_back(CreateFuncArg(std::move(env))); callArgs.push_back(CreateFuncArg(std::move(obj))); auto fdRef = WithinFile(CreateRefExpr(*funcDecl), curFile); fdRef->instTys.push_back(ty); fdRef->ty = typeManager.GetInstantiatedTy(funcDecl->ty, GenerateTypeMapping(*funcDecl, fdRef->instTys)); auto retTy = retAsOption ? utils.GetOptionTy(ty) : ty; return CreateCallExpr(std::move(fdRef), std::move(callArgs), funcDecl, retTy, CallKind::CALL_DECLARED_FUNCTION); } OwnedPtr InteropLibBridge::CreateJavaStringToCangjieCall(OwnedPtr env, OwnedPtr jstring) { auto curFile = jstring->curFile; auto funcDecl = GetJavaStringToCangjie(); if (!funcDecl) { return nullptr; } return CreateCall(funcDecl, curFile, std::move(env), std::move(jstring)); } OwnedPtr InteropLibBridge::CreateGetFromRegistryCall(OwnedPtr env, OwnedPtr self, Ptr ty) { auto curFile = self->curFile; auto funcDecl = GetGetFromRegistryDecl(); if (!funcDecl) { return nullptr; } std::vector> callArgs; callArgs.push_back(CreateFuncArg(std::move(env))); callArgs.push_back(CreateFuncArg(std::move(self))); auto fdRef = WithinFile(CreateRefExpr(*funcDecl), curFile); fdRef->instTys.push_back(ty); fdRef->ty = typeManager.GetInstantiatedTy(funcDecl->ty, GenerateTypeMapping(*funcDecl, fdRef->instTys)); return CreateCallExpr(std::move(fdRef), std::move(callArgs), funcDecl, ty, CallKind::CALL_DECLARED_FUNCTION); } OwnedPtr InteropLibBridge::CreateGetFromRegistryOptionCall(OwnedPtr self, Ptr ty) { auto curFile = self->curFile; CJC_ASSERT(ty->IsCoreOptionType()); auto funcDecl = GetGetFromRegistryOptionDecl(); if (!funcDecl) { return nullptr; } std::vector> callArgs; callArgs.push_back(CreateFuncArg(std::move(self))); auto fdRef = WithinFile(CreateRefExpr(*funcDecl), curFile); fdRef->instTys.push_back(ty->typeArgs[0]); fdRef->ty = typeManager.GetInstantiatedTy(funcDecl->ty, GenerateTypeMapping(*funcDecl, fdRef->instTys)); return CreateCallExpr(std::move(fdRef), std::move(callArgs), funcDecl, ty, CallKind::CALL_DECLARED_FUNCTION); } OwnedPtr InteropLibBridge::CreateGetFieldCall(OwnedPtr env, OwnedPtr obj, std::string typeSignature, std::string fieldName, std::string fieldSignature) { auto curFile = obj->curFile; auto funcDecl = GetGetFieldDecl(); auto jclassInitFuncDecl = GetJClassDecl(); auto parseSignatureFuncDecl = GetParseComponentSignatureDecl(); auto fieldIDFuncDecl = GetFieldIdConstr(); if (!funcDecl || !jclassInitFuncDecl || !parseSignatureFuncDecl || !fieldIDFuncDecl) { return nullptr; } CJC_ASSERT(jclassInitFuncDecl->funcBody->paramLists[0] && jclassInitFuncDecl->funcBody->paramLists[0]->params[1]); auto strTy = jclassInitFuncDecl->funcBody->paramLists[0]->params[1]->ty; auto tsigExpr = CreateLitConstExpr(LitConstKind::STRING, typeSignature, strTy); auto fieldNameExpr = CreateLitConstExpr(LitConstKind::STRING, fieldName, strTy); auto fsigExpr = CreateLitConstExpr(LitConstKind::STRING, fieldSignature, strTy); auto jclass = CreateCall(jclassInitFuncDecl, curFile, CreateGetJniEnvCall(curFile), std::move(tsigExpr)); auto filedSignature = CreateCall(parseSignatureFuncDecl, curFile, std::move(fsigExpr)); auto fieldID = CreateCall(fieldIDFuncDecl, curFile, CreateGetJniEnvCall(curFile), std::move(jclass), std::move(fieldNameExpr), std::move(filedSignature)); return CreateCall(funcDecl, curFile, std::move(env), std::move(obj), std::move(fieldID)); } OwnedPtr InteropLibBridge::CreateGetStaticFieldCall( OwnedPtr env, std::string typeSignature, std::string fieldName, std::string fieldSignature) { auto curFile = env->curFile; auto funcDecl = GetGetStaticFieldDecl(); auto jclassInitFuncDecl = GetJClassDecl(); auto parseSignatureFuncDecl = GetParseComponentSignatureDecl(); auto fieldIDFuncDecl = GetFieldIdConstrStatic(); if (!funcDecl || !jclassInitFuncDecl || !parseSignatureFuncDecl || !fieldIDFuncDecl) { return nullptr; } auto entityTy = GetJavaEntityTy(); if (!entityTy) { return nullptr; } CJC_ASSERT(jclassInitFuncDecl->funcBody->paramLists[0] && jclassInitFuncDecl->funcBody->paramLists[0]->params[1]); auto strTy = jclassInitFuncDecl->funcBody->paramLists[0]->params[1]->ty; auto tsigExpr = CreateLitConstExpr(LitConstKind::STRING, typeSignature, strTy); auto fieldNameExpr = CreateLitConstExpr(LitConstKind::STRING, fieldName, strTy); auto fsigExpr = CreateLitConstExpr(LitConstKind::STRING, fieldSignature, strTy); auto jclass = CreateCall(jclassInitFuncDecl, curFile, CreateGetJniEnvCall(curFile), std::move(tsigExpr)); auto filedSignature = CreateCall(parseSignatureFuncDecl, curFile, std::move(fsigExpr)); auto fieldID = CreateCall(fieldIDFuncDecl, curFile, CreateGetJniEnvCall(curFile), ASTCloner::Clone(jclass.get()), std::move(fieldNameExpr), std::move(filedSignature)); return CreateCall(funcDecl, curFile, std::move(env), std::move(jclass), std::move(fieldID)); } OwnedPtr InteropLibBridge::CreateSetFieldCall( OwnedPtr env, OwnedPtr obj, const MemberJNISignature& signature, OwnedPtr value) { auto curFile = value->curFile; auto funcDecl = GetSetFieldDecl(); auto jclassInitFuncDecl = GetJClassDecl(); auto parseSignatureFuncDecl = GetParseComponentSignatureDecl(); auto fieldIDFuncDecl = GetFieldIdConstr(); if (!funcDecl || !jclassInitFuncDecl || !parseSignatureFuncDecl || !fieldIDFuncDecl) { return nullptr; } CJC_ASSERT(jclassInitFuncDecl->funcBody->paramLists[0] && jclassInitFuncDecl->funcBody->paramLists[0]->params[1]); auto strTy = jclassInitFuncDecl->funcBody->paramLists[0]->params[1]->ty; auto tsigExpr = CreateLitConstExpr(LitConstKind::STRING, signature.classTypeSignature, strTy); tsigExpr->curFile = curFile; auto fieldNameExpr = CreateLitConstExpr(LitConstKind::STRING, signature.name, strTy); fieldNameExpr->curFile = curFile; auto fsigExpr = CreateLitConstExpr(LitConstKind::STRING, signature.signature, strTy); fsigExpr->curFile = curFile; auto jclass = CreateCall(jclassInitFuncDecl, curFile, CreateGetJniEnvCall(curFile), std::move(tsigExpr)); auto filedSignature = CreateCall(parseSignatureFuncDecl, curFile, std::move(fsigExpr)); auto fieldID = CreateCall(fieldIDFuncDecl, curFile, CreateGetJniEnvCall(curFile), std::move(jclass), std::move(fieldNameExpr), std::move(filedSignature)); return CreateCall( funcDecl, curFile, std::move(env), std::move(obj), std::move(fieldID), WrapJavaEntity(std::move(value))); } OwnedPtr InteropLibBridge::CreateSetStaticFieldCall(OwnedPtr env, std::string typeSignature, std::string fieldName, std::string fieldSignature, OwnedPtr value) { auto curFile = value->curFile; auto funcDecl = GetSetStaticFieldDecl(); auto jclassInitFuncDecl = GetJClassDecl(); auto parseSignatureFuncDecl = GetParseComponentSignatureDecl(); auto fieldIDFuncDecl = GetFieldIdConstrStatic(); if (!funcDecl || !jclassInitFuncDecl || !parseSignatureFuncDecl || !fieldIDFuncDecl) { return nullptr; } CJC_ASSERT(jclassInitFuncDecl->funcBody->paramLists[0] && jclassInitFuncDecl->funcBody->paramLists[0]->params[1]); auto strTy = jclassInitFuncDecl->funcBody->paramLists[0]->params[1]->ty; auto tsigExpr = CreateLitConstExpr(LitConstKind::STRING, typeSignature, strTy); tsigExpr->curFile = curFile; auto fieldNameExpr = CreateLitConstExpr(LitConstKind::STRING, fieldName, strTy); fieldNameExpr->curFile = curFile; auto fsigExpr = CreateLitConstExpr(LitConstKind::STRING, fieldSignature, strTy); fsigExpr->curFile = curFile; auto jclass = CreateCall(jclassInitFuncDecl, curFile, CreateGetJniEnvCall(curFile), std::move(tsigExpr)); auto filedSignature = CreateCall(parseSignatureFuncDecl, curFile, std::move(fsigExpr)); auto fieldID = CreateCall(fieldIDFuncDecl, curFile, CreateGetJniEnvCall(curFile), ASTCloner::Clone(jclass.get()), std::move(fieldNameExpr), std::move(filedSignature)); return CreateCall( funcDecl, curFile, std::move(env), std::move(jclass), std::move(fieldID), WrapJavaEntity(std::move(value))); } OwnedPtr InteropLibBridge::WrapExceptionHandling(OwnedPtr env, OwnedPtr action) { auto curFile = env->curFile; auto retTy = action->funcBody->ty; auto fd = GetWithExceptionHandlingDecl(); auto fdRef = WithinFile(CreateRefExpr(*fd), curFile); fdRef->instTys.push_back(retTy); fdRef->ty = typeManager.GetInstantiatedTy(fd->ty, GenerateTypeMapping(*fd, fdRef->instTys)); std::vector> args; args.emplace_back(CreateFuncArg(std::move(env))); args.emplace_back(CreateFuncArg(std::move(action))); return CreateCallExpr(std::move(fdRef), std::move(args), nullptr, retTy, CallKind::CALL_DECLARED_FUNCTION); } namespace { Ptr GetJavaEntityIsNullCall(StructDecl& entityDecl) { for (auto& member : entityDecl.GetMemberDecls()) { if (auto isNullField = As(member.get())) { if (isNullField->identifier == INTEROPLIB_CFFI_JAVA_ENTITY_IS_NULL_ID) { return isNullField; } } } return nullptr; } [[maybe_unused]] OwnedPtr CreateJavaEntityIsNullExpr(OwnedPtr entity, Ptr entityDecl) { auto isNullProp = GetJavaEntityIsNullCall(*entityDecl); if (!isNullProp) { return nullptr; } auto access = CreateMemberAccess(std::move(entity), *isNullProp->getters[0]); CopyBasicInfo(access->baseExpr.get(), access.get()); auto call = CreateCallExpr(std::move(access), {}, isNullProp->getters[0], isNullProp->ty, CallKind::CALL_DECLARED_FUNCTION); CopyBasicInfo(call->baseFunc.get(), call.get()); return call; } } // namespace OwnedPtr InteropLibBridge::UnwrapJavaMirrorOption( OwnedPtr entity, Ptr ty, const ClassLikeDecl& mirror, bool toRaw) { CJC_ASSERT(ty->IsCoreOptionType()); auto curFile = entity->curFile; CJC_NULLPTR_CHECK(curFile); auto declTy = ty->typeArgs[0]; auto decl = Ty::GetDeclOfTy(declTy); CJC_ASSERT(IsMirror(*decl) || declTy->IsString() || (toRaw && IsImpl(*decl))); auto actualTy = toRaw ? GetJobjectTy() : ty; return utils.CreateOptionMatch( CreateGetJavaEntityOrNullCall(std::move(entity)), [this, curFile, declTy, &mirror, toRaw](VarDecl& e) { auto unwrapped = UnwrapJavaEntity(WithinFile(CreateRefExpr(e), curFile), declTy, mirror, toRaw); return unwrapped; }, [this, ty, toRaw]() { return toRaw ? CreateJobjectNull() : utils.CreateOptionNoneRef(ty->typeArgs[0]); }, actualTy); } OwnedPtr InteropLibBridge::UnwrapJavaImplOption( OwnedPtr env, OwnedPtr entityOption, Ptr ty, const ClassLikeDecl& mirror, bool toRaw) { auto actualTy = ty->typeArgs[0]; auto regCall = CreateGetFromRegistryByEntityCall(std::move(env), std::move(entityOption), actualTy, true); if (!toRaw) { return regCall; } return UnwrapJavaMirrorOption(WrapJavaEntity(std::move(regCall)), ty, mirror, toRaw); } OwnedPtr InteropLibBridge::CreateEnsureNotNullCall(OwnedPtr entity) { auto curFile = entity->curFile; return CreateCall(GetEnsureNotNullDecl(), curFile, std::move(entity)); } OwnedPtr InteropLibBridge::CreateGetJavaEntityOrNullCall(OwnedPtr entity) { auto curFile = entity->curFile; return CreateCall(GetGetJavaEntityOrNullDecl(), curFile, std::move(entity)); } OwnedPtr InteropLibBridge::UnwrapJavaArrayEntity(OwnedPtr entity, Ptr ty, const ClassLikeDecl& mirror) { auto isSetOperation = ty == TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); if (isSetOperation) { return CreateUnitExpr(); } auto castType = MakeOwned(); castType->ty = ty; auto varPattern = CreateVarPattern(V_COMPILER, ty); auto curFile = entity->curFile; CJC_NULLPTR_CHECK(curFile); varPattern->curFile = curFile; varPattern->varDecl->curFile = curFile; auto varPatternRef = WithinFile(CreateRefExpr(*(varPattern->varDecl)), curFile); auto genericParam = mirror.generic->typeParameters[0].get(); auto entityPtr = entity.get(); return CreateMatchByTypeArgument(genericParam, GenerateTypeMappingWithSelector([this, &entityPtr, &mirror](TypeKind kind, Ptr ty) { return SelectEntityUnwrapperByTypeKind(kind, ty, entityPtr, mirror); }), ty, CreateMatchWithTypeCast(std::move(entity), ty)); } OwnedPtr InteropLibBridge::UnwrapJavaPrimitiveEntity(OwnedPtr entity, Ptr ty) { auto funcDecl = GetUnwrapJavaEntityDecl(); if (!funcDecl) { return nullptr; } auto isPrimitive = ty->IsPrimitive(); auto unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); auto actualEntityTy = isPrimitive ? ty : typeManager.GetPointerTy(unitTy); auto curFile = entity->curFile; std::vector> callArgs; callArgs.emplace_back(CreateFuncArg(std::move(entity))); auto fdRef = WithinFile(CreateRefExpr(*funcDecl), curFile); fdRef->instTys.push_back(actualEntityTy); fdRef->ty = typeManager.GetInstantiatedTy(funcDecl->ty, GenerateTypeMapping(*funcDecl, fdRef->instTys)); auto callExpr = CreateCallExpr( std::move(fdRef), std::move(callArgs), funcDecl, actualEntityTy, CallKind::CALL_DECLARED_FUNCTION); callExpr->curFile = curFile; return std::move(callExpr); } OwnedPtr InteropLibBridge::UnwrapJavaEntity(OwnedPtr entity, Ptr ty, const Decl& outerDecl, bool toRaw) { auto curFile = entity->curFile; if (!entity || !ty) { return nullptr; } auto isPrimitive = ty->IsPrimitive(); if (isPrimitive || (toRaw && !ty->IsCoreOptionType())) { return UnwrapJavaPrimitiveEntity(std::move(entity), ty); } if (IsJArray(outerDecl) && ty->IsGeneric()) { return UnwrapJavaArrayEntity(std::move(entity), ty, static_cast(outerDecl)); } else if (ty == GetJavaEntityTy()) { return std::move(entity); } if (ty->IsString()) { return CreateJavaStringToCangjieCall(CreateGetJniEnvCall(curFile), std::move(entity)); } if (ty->IsCoreOptionType()) { auto classLikeDecl = DynamicCast(&outerDecl); CJC_NULLPTR_CHECK(classLikeDecl); auto actualTy = ty->typeArgs[0]; auto actualDecl = Ty::GetDeclOfTy(actualTy); if (IsMirror(*actualDecl) || actualTy->IsString()) { return UnwrapJavaMirrorOption(std::move(entity), ty, *classLikeDecl, toRaw); } return UnwrapJavaImplOption(CreateGetJniEnvCall(curFile), std::move(entity), ty, *classLikeDecl, toRaw); } auto classLikeTy = DynamicCast(ty.get()); if (!classLikeTy) { diag.DiagnoseRefactor(DiagKindRefactor::sema_java_interop_not_supported, DEFAULT_POSITION, "type " + ty->name); return nullptr; } auto decl = classLikeTy->commonDecl; if (!decl) { diag.DiagnoseRefactor( DiagKindRefactor::sema_java_interop_not_supported, DEFAULT_POSITION, "unknown decl " + ty->name); return nullptr; } if (decl->TestAttr(Attribute::JAVA_MIRROR)) { return CreateMirrorConstructorCall(importManager, std::move(entity), ty); } else { return CreateGetFromRegistryByEntityCall(CreateGetJniEnvCall(curFile), std::move(entity), ty, false); } } void InteropLibBridge::CheckInteropLibVersion() { auto versionDecl = GetInteropLibVersionVarDecl(); if (!versionDecl || !versionDecl->initializer || versionDecl->initializer->astKind != ASTKind::LIT_CONST_EXPR || versionDecl->initializer->ty->kind != TypeKind::TYPE_INT64) { diag.DiagnoseRefactor(DiagKindRefactor::sema_java_interoplib_version_too_old, DEFAULT_POSITION, std::to_string(INTEROPLIB_VERSION)); return; } auto libVersionLit = StaticAs(versionDecl->initializer.get()); auto libVersion = std::stoi(libVersionLit->stringValue); if (libVersion != INTEROPLIB_VERSION) { diag.DiagnoseRefactor(DiagKindRefactor::sema_java_interoplib_version_mismatch, DEFAULT_POSITION, libVersionLit->stringValue, std::to_string(INTEROPLIB_VERSION)); } } } // namespace Cangjie::Interop::Java cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/AfterTypeCheck/InteropLibBridge.h000066400000000000000000000463121510705540100273230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_SEMA_NATIVE_FFI_JAVA_INTEROPLIB_BRIDGE #define CANGJIE_SEMA_NATIVE_FFI_JAVA_INTEROPLIB_BRIDGE #include "Utils.h" #include "NativeFFI/Utils.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/AST/Match.h" namespace Cangjie::Interop::Java { using namespace AST; /** * method or field jni signature */ struct MemberJNISignature { std::string classTypeSignature; std::string name; std::string signature; MemberJNISignature(std::string classTypeSignature, std::string name, std::string signature) : classTypeSignature(classTypeSignature), name(name), signature(signature) {} MemberJNISignature(Utils& utils, FuncDecl& member) : MemberJNISignature(utils, member, StaticAs(member.outerDecl)) { auto& retTy = *member.funcBody->retType->ty; std::vector> paramTys = Native::FFI::GetParamTys(*member.funcBody->paramLists[0]); signature = utils.GetJavaTypeSignature(retTy, paramTys); } MemberJNISignature(Utils& utils, PropDecl& member) : MemberJNISignature(utils, member, StaticAs(member.outerDecl)) { signature = utils.GetJavaTypeSignature(*member.ty); } MemberJNISignature(Utils& utils, Decl& member, Ptr jobject) { CJC_ASSERT(jobject); Ptr ty = jobject->ty; if (IsSynthetic(*jobject)) { if (jobject->inheritedTypes.size() > 1) { ty = jobject->inheritedTypes[1]->ty; // take interface ty } else { ty = jobject->inheritedTypes[0]->ty; // take superclass ty } } classTypeSignature = utils.GetJavaClassNormalizeSignature(*ty); name = GetJavaMemberName(member); CJC_ASSERT(member.astKind == ASTKind::FUNC_DECL || member.astKind == ASTKind::PROP_DECL); signature = utils.GetJavaTypeSignature(*member.ty); } }; class InteropLibBridge { public: InteropLibBridge( ImportManager& importManager, TypeManager& typeManager, DiagnosticEngine& diag, Utils& utils) : importManager(importManager), typeManager(typeManager), diag(diag), utils(utils) { } /** * jobject */ Ptr GetJobjectDecl(); /** * Java_CFFI_JavaEntity */ Ptr GetJavaEntityDecl(); /** * Java_CFFI_JavaEntityKind */ Ptr GetJavaEntityKindDecl(); /** * Java_CFFI_JavaEntityKind.JOBJECT */ Decl& GetJavaEntityKindJObject(); /** * Java_CFFI_newGlobalReference */ Ptr GetNewGlobalRefDecl(); /** * JNIEnv_ptr */ Ptr GetJniEnvPtrDecl(); /** * Java_CFFI_get_env */ Ptr GetGetJniEnvDecl(); /** * Java_CFFI_JavaEntityJobject */ Ptr GetCreateJavaEntityJobjectDecl(); /** * Java_CFFI_JavaEntityJobjectNull */ Ptr GetCreateJavaEntityNullDecl(); /** * Java_CFFI_newJavaObject */ Ptr GetNewJavaObjectDecl(); /** * Java_CFFI_newJavaArray */ Ptr GetNewJavaArrayDecl(); /** * Java_CFFI_newJavaProxyObjectForCJMapping */ Ptr GetNewJavaProxyObjectForCJMappingDecl(); /** * Java_CFFI_arrayGet */ Ptr GetJavaArrayGetDecl(); /** * Java_CFFI_arraySet */ Ptr GetJavaArraySetDecl(); /** * Java_CFFI_arrayGetLength */ Ptr GetJavaArrayGetLengthDecl(); /** * Java_CFFI_callVirtualMethod_raw */ Ptr GetCallMethodDecl(); /** * Java_CFFI_callNonVirtualMethod_raw */ Ptr GetNonVirtualCallMethodDecl(); /** * Java_CFFI_callStaticMethod_raw */ Ptr GetCallStaticMethodDecl(); /** * Java_CFFI_deleteCJObject */ Ptr GetDeleteCJObjectDecl(); /** * Java_CFFI_removeFromRegistry */ Ptr GetRemoveFromRegistryDecl(); /** * Java_CFFI_put_to_registry_1 */ Ptr GetPutToRegistryDecl(); /** * Java_CFFI_put_to_registry */ Ptr GetPutToRegistrySelfInitDecl(); /** * Java_CFFI_unwrapJavaEntityAsValue */ Ptr GetUnwrapJavaEntityDecl(); /** * Java_CFFI_unwrapJavaMirror */ Ptr GetUnwrapJavaMirrorDecl(); /** * Java_CFFI_getFromRegistryByEntityOption */ Ptr GetGetFromRegistryByEntityOptionDecl(); /** * Java_CFFI_getFromRegistryByEntity */ Ptr GetGetFromRegistryByEntityDecl(); /** * Java_CFFI_JavaStringToCangjie */ Ptr GetJavaStringToCangjie(); /** * Java_CFFI_CangjieStringToJava */ Ptr GetCangjieStringToJava(); /** * Java_CFFI_getFromRegistry */ Ptr GetGetFromRegistryDecl(); /** * Java_CFFI_getFromRegistryOption */ Ptr GetGetFromRegistryOptionDecl(); /** * Java_CFFI_getField_raw */ Ptr GetGetFieldDecl(); /** * Java_CFFI_setField_raw */ Ptr GetSetFieldDecl(); /** * Java_CFFI_getStaticField_raw */ Ptr GetGetStaticFieldDecl(); /** * Java_CFFI_setStaticField_raw */ Ptr GetSetStaticFieldDecl(); /** * INTEROPLIB_VERSION */ Ptr GetInteropLibVersionVarDecl(); /** * Java_CFFI_ensure_not_null */ Ptr GetEnsureNotNullDecl(); /** * Java_CFFI_getOrNull */ Ptr GetGetJavaEntityOrNullDecl(); /** * Java_CFFI_isInstanceOf */ Ptr GetIsInstanceOf(); /** * withExceptionHandling */ Ptr GetWithExceptionHandlingDecl(); Ptr GetJClassDecl(); Ptr GetParseMethodSignatureDecl(); Ptr GetParseComponentSignatureDecl(); Ptr GetCallNestDecl(); Ptr GetMethodIdConstr(); Ptr GetMethodIdConstrStatic(); Ptr GetFieldIdConstr(); Ptr GetFieldIdConstrStatic(); /** * JNIEnv_ptr ty */ Ptr GetJNIEnvPtrTy(); /** * Java_CFFI_JavaEntity ty */ Ptr GetJavaEntityTy(); /** * jobject ty */ Ptr GetJobjectTy(); /** * jlong */ Ptr GetJlongTy(); /** * jobject type */ OwnedPtr CreateJobjectType(); /** * jlong */ OwnedPtr CreateJlongType(); /** * Returns cjExpr wrapped into java entity: * * Java_CFFI_JavaEntity(cjExpr) */ OwnedPtr WrapJavaEntity(OwnedPtr cjExpr); /** * interoplib.interop.Java_CFFI_JavaEntityJobject(jobject: CPointer) */ OwnedPtr CreateJavaEntityJobjectCall(OwnedPtr arg); /** * Java_CFFI_JavaEntityJobjectNull() */ OwnedPtr CreateJavaEntityNullCall(Ptr curFile); /** * match (arg) { * case Some(argv) => argv.javaref * case None => Java_CFFI_JavaEntityJobjectNull() * } */ OwnedPtr CreateJavaEntityFromOptionMirror(OwnedPtr option, ClassLikeDecl& mirror); /** * Java_CFFI_JavaEntity() // Unit */ OwnedPtr CreateJavaEntityCall(Ptr file); /** * Java_CFFI_JavaEntity(arg) */ OwnedPtr CreateJavaEntityCall(OwnedPtr arg); /** * Java_CFFI_get_env() */ OwnedPtr CreateGetJniEnvCall(Ptr curFile); /** CFFI object creation call: * Java_CFFI_newJavaObject(jniEnv, classTypeSignature, "()V", cffiCtorFuncArgs) */ OwnedPtr CreateCFFINewJavaObjectCall(OwnedPtr jniEnv, std::string classTypeSignature, FuncParamList& params, bool isMirror, File& curFile); /** * Java_CFFI_newJavaObject(env, classTypeSignature, constructorSignature, [args]) */ OwnedPtr CreateNewJavaObjectCall( OwnedPtr env, const std::string& classTypeSignature, const std::string& constructorSignature, std::vector> args); /** * Java_CFFI_newJavaArray(env, signature, [args]) */ OwnedPtr CreateCFFINewJavaArrayCall( OwnedPtr jniEnv, FuncParamList& params, const Ptr genericParam); /** * Java_CFFI_newJavaProxyObjectForCJMapping(env, entity, name, withMarkerParam) * For StrcutTy, withMarkerParam is true; for EnumTy, withMarkerParam is false. */ OwnedPtr CreateCFFINewJavaCFFINewJavaProxyObjectForCJMappingCall( OwnedPtr jniEnv, OwnedPtr entity, std::string name, bool withMarkerParam); /** * Java_CFFI_newGlobalReference(env, obj, isWeak) */ OwnedPtr CreateNewGlobalRefCall(OwnedPtr env, OwnedPtr obj, bool isWeak); /** * CFFI method call: * Java_CFFI_callVirtualMethod_raw(jniEnv, obj, typeSignature, methodName, "()ReTy", cffiMethodArgs) */ OwnedPtr CreateCFFICallMethodCall(OwnedPtr jniEnv, OwnedPtr obj, const MemberJNISignature& signature, FuncParamList& params, File& curFile); /** * Java_CFFI_arrayGetLength(env) */ OwnedPtr CreateCFFIArrayLengthGetCall(OwnedPtr javarefExpr, Ptr curFile); /** * CFFI method call: * Java_CFFI_arrayGet(jniEnv, obj, typeSignature, cffiMethodArgs) * Java_CFFI_arraySet(jniEnv, obj, typeSignature, cffiMethodArgs) */ OwnedPtr CreateCFFICallArrayMethodCall(OwnedPtr jniEnv, OwnedPtr obj, AST::FuncParamList& params, const Ptr genericParam, ArrayOperationKind kind); /** * CFFI static method call: * Java_CFFI_callStaticMethod_raw( * jniEnv, signature.classTypeSignature, signature.name, "()ReTy", cffiMethodArgs) */ OwnedPtr CreateCFFICallStaticMethodCall( OwnedPtr jniEnv, const MemberJNISignature& signature, FuncParamList& params, File& curFile); /** * (env, obj, signature.classTypeSignature, signature.name, signature.signature, [args]) * where is Java_CFFI_callVirtualMethod_raw or Java_CFFI_callNonVirtualMethod_raw * * @param virt should the call be virtual or not */ OwnedPtr CreateCallMethodCall(OwnedPtr env, OwnedPtr obj, const MemberJNISignature& signature, std::vector> args, File& curFile, bool virt = true); /** * Java_CFFI_callStaticMethod_raw(env, signature.classTypeSignature, signature.name, signature.signature, [args]) */ OwnedPtr CreateCallStaticMethodCall(OwnedPtr env, const MemberJNISignature& signature, std::vector> args, File& curFile); /** * Java_CFFI_deleteCJObject(env, obj, self, getWeakRef) */ OwnedPtr CreateDeleteCJObjectCall(OwnedPtr env, OwnedPtr self, OwnedPtr getWeakRef, Ptr cjTy /* generic param ty */); /** * Java_CFFI_removeFromRegistry(self) */ OwnedPtr CreateRemoveFromRegistryCall(OwnedPtr self); /** * Java_CFFI_put_to_registry_1(obj) */ OwnedPtr CreatePutToRegistryCall(OwnedPtr obj); OwnedPtr CreatePutToRegistrySelfInitCall(OwnedPtr env, OwnedPtr entity, OwnedPtr obj); /** * Java_CFFI_getFromRegistryByObj{Option}(env, obj) */ OwnedPtr CreateGetFromRegistryByEntityCall(OwnedPtr env, OwnedPtr obj, Ptr ty, bool retAsOption); /** * Java_CFFI_JavaStringToCangjie(env, jstring) */ OwnedPtr CreateJavaStringToCangjieCall(OwnedPtr env, OwnedPtr jstring); /** * Java_CFFI_getFromRegistry(env, self) */ OwnedPtr CreateGetFromRegistryCall(OwnedPtr env, OwnedPtr self, Ptr ty); /** * Java_CFFI_getFromRegistryOption(self) */ OwnedPtr CreateGetFromRegistryOptionCall(OwnedPtr self, Ptr ty); /** * Java_CFFI_getField_raw(env, obj, typeSignature, fieldName, fieldSignature) */ OwnedPtr CreateGetFieldCall(OwnedPtr env, OwnedPtr obj, std::string typeSignature, std::string fieldName, std::string fieldSignature); /** * Java_CFFI_getStaticField_raw(env, typeSignature, fieldName, fieldSignature) */ OwnedPtr CreateGetStaticFieldCall(OwnedPtr env, std::string typeSignature, std::string fieldName, std::string fieldSignature); /** * Java_CFFI_setField_raw(env, obj, signature.classTypeSignature, * signature.name, signature.signature, Java_CFFI_JavaEntity(value)) */ OwnedPtr CreateSetFieldCall(OwnedPtr env, OwnedPtr obj, const MemberJNISignature& signature, OwnedPtr value); /** * Java_CFFI_setStaticField_raw(env, typeSignature, fieldName, fieldSignature, Java_CFFI_JavaEntity(value)) */ OwnedPtr CreateSetStaticFieldCall(OwnedPtr env, std::string typeSignature, std::string fieldName, std::string fieldSignature, OwnedPtr value); /** * Java_CFFI_ensure_not_null(entity) */ OwnedPtr CreateEnsureNotNullCall(OwnedPtr entity); /** * Java_CFFI_getJavaEntityOrNull(entity) */ OwnedPtr CreateGetJavaEntityOrNullCall(OwnedPtr entity); OwnedPtr WrapExceptionHandling(OwnedPtr env, OwnedPtr action); /** * CPointer() */ OwnedPtr CreateJobjectNull(); /** * env: JNIEnv_Ptr */ OwnedPtr CreateEnvFuncParam(); /** * _: jobject or jclass, default name is '_' */ OwnedPtr CreateJClassOrJObjectFuncParam(const std::string& name = "_"); /** * self: jlong */ OwnedPtr CreateSelfFuncParam(); /** * // entityOption: Java_CFFI_JavaEntity * match (entityOption.isNull) { * case true => None * case false => Some(ty(entityOption)) * } * If [toRaw] = `true`, then it returns java reference as CPointer instead of an instance */ OwnedPtr UnwrapJavaMirrorOption( OwnedPtr entityOption, Ptr ty, const ClassLikeDecl& mirror, bool toRaw = false); OwnedPtr UnwrapJavaImplOption(OwnedPtr env, OwnedPtr entityOption, Ptr ty, const ClassLikeDecl& mirror, bool toRaw = false); /** * Creates unwrap call Java_CFFI_JavaEntity [entity] and returns the value it stores as [ty] if [toRaw] is `false`: * Java_CFFI_unwrapJavaEntityAsValue(entity) * * If ty of decl T is @JavaMirror: * T(entity) * * If ty of decl T is @JavaImpl: * Java_CFFI_getFromRegistry(entity). * Else, if [toRaw] is `true` and T is @JavaMirror or @JavaImpl class, then it returns its CPointer java ref */ OwnedPtr UnwrapJavaEntity(OwnedPtr entity, Ptr ty, const Decl& outerDecl, bool toRaw = false); OwnedPtr CreateMatchByTypeArgument( const Ptr genericParam, std::map> typeToCaseMap, Ptr retTy, OwnedPtr defaultCase); OwnedPtr CreateMatchWithTypeCast(OwnedPtr exprToCast, Ptr castTy); OwnedPtr CreateGetTypeForTypeParameterCall(const Ptr genericParam) const; Ptr FindGetTypeForTypeParamDecl(File& file) const; Ptr FindCStringToStringDecl(); Ptr FindStringEqualsDecl(); Ptr FindStringStartsWithDecl(); Ptr FindArrayJavaEntityGetDecl(ClassDecl& jArrayDecl) const; Ptr FindArrayJavaEntitySetDecl(ClassDecl& jArrayDecl) const; OwnedPtr SelectJSigByTypeKind(TypeKind kind, Ptr ty); OwnedPtr SelectJPrimitiveNameByTypeKind(TypeKind kind, Ptr ty); OwnedPtr SelectEntityWrapperByTypeKind(TypeKind kind, Ptr ty, Ptr param, Ptr file); OwnedPtr SelectEntityUnwrapperByTypeKind( TypeKind kind, Ptr ty, Ptr entity, const ClassLikeDecl& mirror); std::map> GenerateTypeMappingWithSelector( std::function(TypeKind, Ptr)> selector ); void CheckInteropLibVersion(); private: static constexpr auto INTEROPLIB_VERSION = 9; static constexpr auto INTEROPLIB_PACKAGE_NAME = "interoplib.interop"; const std::vector supportedArrayPrimitiveElementType = { TypeKind::TYPE_BOOLEAN, TypeKind::TYPE_INT8, TypeKind::TYPE_UINT16, TypeKind::TYPE_INT16, TypeKind::TYPE_INT32, TypeKind::TYPE_INT64, TypeKind::TYPE_FLOAT32, TypeKind::TYPE_FLOAT64 }; OwnedPtr CreateMirrorContructorCall( Ptr mirror, OwnedPtr javaEntity, Ptr expectedTy) const; OwnedPtr UnwrapJavaArrayEntity(OwnedPtr entity, Ptr ty, const ClassLikeDecl& mirror); OwnedPtr UnwrapJavaPrimitiveEntity(OwnedPtr entity, Ptr ty); template inline auto ImportDecl(const std::string& package, const std::string& declname, bool silent = false) { auto decl = importManager.GetImportedDecl(package, declname); if (!decl) { if (!silent) { diag.DiagnoseRefactor(DiagKindRefactor::sema_member_not_imported, DEFAULT_POSITION, package + "." + declname); } return Ptr(As(nullptr)); } CJC_ASSERT(decl && decl->astKind == K); return Ptr(StaticAs(decl)); } template inline auto GetInteropLibDecl(const std::string& declname, bool silent = false) { return ImportDecl(INTEROPLIB_PACKAGE_NAME, declname, silent); } template inline auto GetJavaLangDecl(const std::string& declname) { return ImportDecl(INTEROP_JAVA_LANG_PACKAGE, declname); } ImportManager& importManager; TypeManager& typeManager; DiagnosticEngine& diag; Utils& utils; }; } #endif // CANGJIE_SEMA_NATIVE_FFI_JAVA_INTEROPLIB_BRIDGE cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/AfterTypeCheck/JavaDesugarManager.h000066400000000000000000000425721510705540100276320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares core support for java mirror and mirror subtype */ #ifndef CANGJIE_SEMA_NATIVE_FFI_JAVA_DESUGAR_MANAGER #define CANGJIE_SEMA_NATIVE_FFI_JAVA_DESUGAR_MANAGER #include "InteropLibBridge.h" #include "Utils.h" #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Sema/TypeManager.h" namespace Cangjie::Interop::Java { using namespace AST; const std::string JAVA_ARRAY_GET_FOR_REF_TYPES = "$javaarrayget"; const std::string JAVA_ARRAY_SET_FOR_REF_TYPES = "$javaarrayset"; const std::string JAVA_IMPL_ENTITY_ARG_NAME_IN_GENERATED_CTOR = "$obj"; enum class DesugarJavaMirrorImplStage : uint8_t { BEGIN, MIRROR_GENERATE_STUB, MIRROR_GENERATE, IMPL_GENERATE, MIRROR_DESUGAR, IMPL_DESUGAR, TYPECHECKS, END }; enum class DesugarCJImplStage : uint8_t { BEGIN, IMPL_GENERATE, IMPL_DESUGAR, TYPECHECKS, END }; class JavaDesugarManager { public: JavaDesugarManager(ImportManager& importManager, TypeManager& typeManager, DiagnosticEngine& diag, const BaseMangler& mangler, const std::optional& javaCodeGenPath, const std::string& outputLibPath) : importManager(importManager), typeManager(typeManager), utils(importManager, typeManager), diag(diag), mangler(mangler), lib(importManager, typeManager, diag, utils), javaCodeGenPath(javaCodeGenPath), outputLibPath(outputLibPath) { lib.CheckInteropLibVersion(); } /** * Stage 1: for each mirror interface and abstract class, generate a class wich implements * this interface/abstract class. * Such classes are used as wrappers when interface or abstract class type object is returned from java */ void GenerateSyntheticClass(File& file); /** * Stage 2-3: constructors generation and javaref field insertion. * The first step: generate members in `JObject` and insert empty constructor in other mirrors. ([doStub] = `false`) * The second step: fill pregenerated bodies ([doStub] = `false`) */ void GenerateInMirrors(File& file, bool doStub); void GenerateInMirror(ClassDecl& classDecl, bool doStub); /** * Stage 4: desugar constructors, methods, etc */ void DesugarMirrors(File& file); /** * Stage 5: generate constructors and native init/deinit/method call functions (callable from java) for @JavaImpl */ void GenerateInJavaImpls(File& file); /** * Stage 6: desugar in @JavaImpl */ void DesugarInJavaImpls(File& file); /** * Stage 7: desugar `as`, `is` where type operand is Java class */ void DesugarTypechecks(File& file); void DesugarJavaMirror(ClassDecl& mirror); /** * for interfaces and abstract classes */ void DesugarJavaMirror(ClassLikeDecl& mirror); void DesugarJavaImpl(ClassDecl& jimpl); void ProcessJavaMirrorImplStage(DesugarJavaMirrorImplStage stage, File& file); void ProcessCJImplStage(DesugarCJImplStage stage, File& file); /** * Stage 1: generate constructors and native init/deinit/method call functions (callable from java) for CJMapping * data structure */ void GenerateInCJMapping(File& file); /** * Stage 2: desugar in CJMapping data structure */ void DesugarInCJMapping(File& file); private: /** * Inserts javaref decl to the class decl: * * let javaref: Java_CFFI_JavaEntity */ void InsertJavaRefVarDecl(ClassDecl& decl); void InsertArrayJavaEntityGet(ClassDecl& decl); void InsertArrayJavaEntitySet(ClassDecl& decl); /** * Generates and inserts constructor in java mirror: * * public init($ref: Java_CFFI_JavaEntity) { * this.javaref = ref // for JObject * // super($ref) // for other mirrors * } */ void InsertJavaMirrorCtor(ClassDecl& decl, bool doStub); /** * Generates and inserts javaref getter as javaref field will be in synthetic class * that implements current interface. * * abstract getter * public func $getJavaRef(): Java_CFFI_JavaEntity */ void InsertAbstractJavaRefGetter(ClassLikeDecl& decl); /** * public override func $getJavaRef(): Java_CFFI_JavaEntity { * return $javaref * } */ void InsertJavaRefGetterWithBody(ClassDecl& decl); /** * before [ctor]: * init(a1: A, a2: B, ..., an: N) { // empty body generated on the previous compilation steps * } * -------- * after: * init(a1: A, a2: B, ..., an: N) { * this({ * let jniEnv = Java_CFFI_get_env() * Java_CFFI_newJavaObject(jniEnv, typeSignature, "()V", [ * Java_CFFI_JavaEntity(a1), * Java_CFFI_JavaEntity(a2), * ..., * Java_CFFI_JavaEntity(an)]) * }) * } */ void DesugarJavaMirrorConstructor(FuncDecl& ctor, FuncDecl& generatedCtor); /** * This constructor could be called from cangjie * before [ctor]: * init(a1: A, a2: B, ..., an: N) { * // optional * ...body... * } * -------------------------------- * after: * init(a1: A, a2: B, ..., an: N) { * super({ * let jniEnv = Java_CFFI_get_env() * Java_CFFI_newGlobalReference( * jniEnv, * Java_CFFI_newJavaObject(jniEnv, typeSignature, "()V", [ * Java_CFFI_JavaEntity(a1), * Java_CFFI_JavaEntity(a2), * ..., * Java_CFFI_JavaEntity(an), * Java_CFFI_JavaEntity(null) // mark argument for generated java constructor * // of Type $$NativeConstructorMarker * ]), * true // isWeak * ) * }) // @JavaMirror constructor call * // optional <- will be removed after java code generation * ...body... * } */ void DesugarJavaImplConstructor(FuncDecl& ctor, FuncDecl& parentCtor); /** * Create native func * public @C func ${funcName} (${params}): retTy { * ${nodes} * } */ OwnedPtr CreateNativeFunc(const std::string& funcName, std::vector> params, Ptr retTy, std::vector> nodes); /** * Unwrap a refExpr of native func param into Cangjie type. * ref: jobject -> @JavaMirror/@JavaImpl */ OwnedPtr UnwrapRefExpr(OwnedPtr ref, Ptr targetTy, const ClassLikeDecl& decl); /** * Wrap the cangjie expr into native type. * expr: @JavaMirror/@JavaImpl -> jobject */ OwnedPtr WrapExprWithExceptionHandling( std::vector>&& nodes, OwnedPtr expr, FuncParam& env, const ClassLikeDecl& decl); /** * Generate native for arg: * public @C func Java_xxx_superC${ctorId}A${argId}P${paramIds} (${usingParams}) { * return withExceptionHandling(env, { => * let tmp0 = wrap($usingParams) * let tmp1 = ${clonedExpr($arg)} * return unwrap(tmp) * }) * } */ OwnedPtr GenerateNativeFunc4Argument(const FuncArg& arg, const std::vector>& params, ClassLikeDecl& decl, size_t ctorId, size_t argId); // Desugar super call in constructor OwnedPtr DesugarJavaImplSuperCall(const FuncDecl& ctor, Decl& jniEnvVar); /** * This contructor could be called from java * for constructor [sampleCtor]: * init(a1: A, a2: B, ..., an: N) { // or primary constructor * // optional * ...body... * } * * the following will be generated: * init(obj: Java_CFFI_JavaEntity, a1: A, a2: B, ..., an: N) { * super(obj) * ...body... * } */ OwnedPtr GenerateJavaImplConstructor(FuncDecl& sampleCtor, ClassLikeDecl& parent); /** * This generated method could be called from java * for method [sampleMethod] of class package.I: * * func foo(a1: A, a2: B, ..., an: N): Ret { * ...body... * } * * the following native function will be generated: * * @C * func java_package_I_fooImpl{mangling}(env: JNIEnv_ptr, _: jobject, self: jlong, a1: A, a2: B, ..., an: N): Ret { * *WrapJavaEntity*(Java_CFFI_getFromRegistry(env, self).foo(a1, a2, ..., an)) * } */ void PushEnvParams(std::vector>& params, std::string name = "env"); void PushObjParams(std::vector>& params, std::string name = "obj"); void PushSelfParams(std::vector>& params, std::string name = "self"); bool FillMethodParamsByArg(std::vector>& params, std::vector>& callArgs, FuncDecl& funcDecl, OwnedPtr& arg, FuncParam& jniEnvPtrParam); OwnedPtr GenerateNativeMethod(FuncDecl& sampleMethod, Decl& decl); void GenerateFuncParamsForNativeDeleteCjObject( Decl& decl, std::vector>& params, FuncParam*& jniEnv, OwnedPtr& selfRef); OwnedPtr GenerateNativeFuncDeclBylambda(Decl& decl, OwnedPtr& wrappedNodesLambda, std::vector>& paramLists, FuncParam& jniEnvPtrParam, Ptr& retTy, std::string funcName); std::string GetJniMethodName(const FuncDecl& method); std::string GetJniMethodNameForProp(const PropDecl& propDecl, bool isSet) const; std::string GetJniSuperArgFuncName(const ClassLikeDecl& outer, const std::string& id) const; std::string GetJniInitCjObjectFuncName(const FuncDecl& ctor, bool isGeneratedCtor); std::string GetJniInitCjObjectFuncNameForVarDecl(const VarDecl& ctor) const; std::string GetJniDeleteCjObjectFuncName(const Decl& decl) const; /** * @C public func Java__deleteCJObject(env: JNIEnv_ptr, obj: jobject, self: jlong): Unit { * Java_CFFI_deleteCJObject(env, self, { objToDelete: => objToDelete.javaref }) * } */ OwnedPtr GenerateNativeDeleteCjObjectFunc(ClassLikeDecl& javaImpl, VarDecl& javaWeakRefField); /** * @C public func Java_Vector_deleteCJObject(env: JNIEnv_ptr, clazz: jclass, self: jlong): Unit { * withExceptionHandling(env) { * Java_CFFI_removeFromRegistry(self) * } * } */ OwnedPtr GenerateCJMappingNativeDeleteCjObjectFunc(Decl& decl); // A helper function to for the ctor of Enum. void GenerateNativeInitCJObjectEnumCtor(AST::EnumDecl& enumDecl); /** * @C public func Java__initCJObject(env: JNIEnv_ptr, obj: jobject): jlong { return withExceptionHandling(env, { => return Java_CFFI_put_to_registry_1(Enum.VarDecl) }) * } */ OwnedPtr GenerateNativeInitCjObjectFuncForEnumCtorNoParams(AST::EnumDecl& enumDecl, AST::VarDecl& ctor); /** * when arg ClassLikeDecl is true(e.g. calss decl):: * * @C public func Java__initCJObject{mangled}(env: JNIEnv_ptr, obj: jobject, args...): jlong { * registry.put( * (Java_CFFI_newGlobalReference(env, Java_CFFI_JavaEntity(JOBJECT, obj), true), args...) * ) * } * when arg ClassLikeDecl is false (e.g. struct decl): * * @C public func Java__initCJObject{mangled}(env: JNIEnv_ptr, obj: jobject, args...): jlong { * registry.put( * (args...) * ) * } */ OwnedPtr GenerateNativeInitCjObjectFunc(FuncDecl& ctor, bool isClassLikeDecl); /** * for func [fun]: * func foo(args): Ret * * the following will be generated: * func foo(args): Ret { * *UnwrapJavaEntity*( * Java_CFFI_callMethod_raw( * Java_CFFI_get_env(), * this.javaref, // or getJavaref if mirror is an interface * typeSignature, "foo", "()Ret", * [Java_CFFI_JavaEntity(args[0]), ... Java_CFFI_JavaEntity(args[n])] * ) * } * * where *UnwrapJavaEntity* - generated unwrapper for Ret type value. */ void DesugarJavaMirrorMethod(FuncDecl& fun, ClassLikeDecl& mirror); /** * used in DesugarJavaMirrorMethod for method's body generation * */ void AddJavaMirrorMethodBody(const ClassLikeDecl& mirror, FuncDecl& fun, OwnedPtr javaRefCall); /** * for prop [prop]: * mut prop p: Ret * * the following will be generated: * mut prop p: Ret { * get() { * *UnwrapJavaEntity*( * Java_CFFI_getField_raw( * Java_CFFI_get_env(), this.javaref, typeSignature, "p", "Ret" * )) * } * set(v) { * Java_CFFI_setField_raw( * Java_CFFI_get_env(), * this.javaref, typeSignature, "p", "Ret", Java_CFFI_JavaEntity(v) * ) * } * } */ void DesugarJavaMirrorProp(PropDecl& prop); void InsertJavaMirrorPropGetter(PropDecl& prop); void InsertJavaMirrorPropSetter(PropDecl& prop); /** * For CType ty, ty is returned. For mirrors, impls and CJMapping jobject is returned */ Ptr GetJNITy(Ptr ty); OwnedPtr CreateIsInstanceCall(Ptr jObjectVar, Ptr classTy, Ptr curFile); /** * Transforms `x is T` expressions, where T is Java class into and x is class or interface: * * match (x) { * case x : JObject => * Java_CFFI_isInstanceOf(Java_CFFI_get_env(), x.javaref, [Name of T Java class]) * case _ => false * } */ void DesugarIsExpression(IsExpr& ie); OwnedPtr CreateJObjectCast(Ptr jObjectVar, Ptr castDecl, Ptr curFile); /** * Transforms `x as T` expressions, similarly to `is` above: * * In case of T is JavaMirror: * match (x) { * case x : JObject => * match (Java_CFFI_isInstanceOf(Java_CFFI_get_env(), x.javaref, [Name of T Java class])) { * case true => * (When T is JavaMirror) Some(T(x.javaref)) * (When T is JavaImpl) Java_CFFI_getFromRegistryByObj(Java_CFFI_get_env(), x.javaref) * case flase => None * } * case _ => None * } * } */ void DesugarAsExpression(AsExpr& ae); /** * Transforms case arm where Java class is used in type patterns * * `case (.. (xi : Ti) ..) where guard => ...` * where Ti is Java class. Gets replaced with: * case (.. (xi : JObject) ..) where IsInstanceOf(xi.javaref, Ti) && .. && guard => { * (.. let xi$Casted = T(x.javaref) ..) * // All references to xi are replaces with references to xi$Casted * ... * } * * Guard also might use xi variables, so casted variables are added to guard too and * all references are replaced */ void DesugarMatchCase(MatchCase& matchCase); OwnedPtr CastAndSubstituteVars( Expr& expr, const std::vector, Ptr>>& patternVars); /** * Transforms let pattern (let Some(a) = b), where type pattern with Java class is used * * let (... (xi : Ti) ...) = ... * where Ti is Java class. Gets replaced with: * let ( ... (xi : JObject) ... ) = ... && IsInstanceOf(xi, Ti) && ... */ void DesugarLetPattern(LetPatternDestructor& letPat); /** * Inserts constructor of form `JString(String)`. * The operation consists of two steps: * 1) Insert constructor stub (constructor with empty body): [doStub] = `true` * 2) Fills generated constructor with actual body: [doStub] = `false` * * public init(s: String) { * super(Java_CFFI_CangjieStringToJava(env, s)) * } */ void InsertJStringOfStringCtor(ClassDecl& decl, bool doStub); void ReplaceCallsWithArrayJavaEntityGet(File& file); void ReplaceCallsWithArrayJavaEntitySet(File& file); void DesugarSuperMethodCall(CallExpr& call, ClassDecl& impl); void GenerateInJavaImpl(AST::ClassDecl* classDecl); void GenerateForCJStructMapping(AST::StructDecl* structDecl); void GenerateForCJEnumMapping(AST::EnumDecl& enumDecl); OwnedPtr CreateUnitType(); ImportManager& importManager; TypeManager& typeManager; Utils utils; DiagnosticEngine& diag; const BaseMangler& mangler; InteropLibBridge lib; const std::optional& javaCodeGenPath; const std::string& outputLibPath; /** * Top-level declarations generated during desugaring. Should be added at the end of file desugaring */ std::vector> generatedDecls; }; } // namespace Cangjie::Interop::Java #endif // CANGJIE_SEMA_NATIVE_FFI_JAVA_DESUGAR_MANAGER cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/AfterTypeCheck/JavaInteropManager.h000066400000000000000000000046021510705540100276500ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares core support for java mirror and mirror subtype */ #ifndef CANGJIE_SEMA_NATIVE_FFI_JAVA_DESUGAR_INTEROP_MANAGER #define CANGJIE_SEMA_NATIVE_FFI_JAVA_DESUGAR_INTEROP_MANAGER #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Sema/TypeManager.h" namespace Cangjie::Interop::Java { using namespace AST; class JavaInteropManager { public: JavaInteropManager(ImportManager& importManager, TypeManager& typeManager, DiagnosticEngine& diag, const BaseMangler& mangler, const std::optional& javagenOutputPath, const std::string outputPath, bool enableInteropCJMapping = false) : importManager(importManager), typeManager(typeManager), diag(diag), mangler(mangler), javagenOutputPath(javagenOutputPath), outputPath(outputPath), enableInteropCJMapping(enableInteropCJMapping) { } void CheckImplRedefinition(Package& package); void CheckInheritance(ClassLikeDecl& decl) const; void CheckTypes(File& file); void CheckTypes(ClassLikeDecl& classLikeDecl); void CheckJavaMirrorTypes(ClassLikeDecl& decl); void CheckJavaImplTypes(ClassLikeDecl& decl); void CheckCJMappingType(Decl& decl); void CheckCJMappingDeclSupportRange(Decl& decl); void DesugarPackage(Package& pkg); private: void CheckNonJavaSuperType(ClassLikeDecl& decl) const; void CheckJavaMirrorSubtypeAttrClassLikeDecl(ClassLikeDecl& decl) const; void CheckExtendDecl(ExtendDecl& decl) const; ImportManager& importManager; TypeManager& typeManager; DiagnosticEngine& diag; const BaseMangler& mangler; const std::optional& javagenOutputPath; /** * Name of output cangjie library */ const std::string outputPath; /** * Flag that informs on presence of any @JavaMirror- or @JavaImpl-annotated entities in the compilation package */ bool hasMirrorOrImpl = false; bool enableInteropCJMapping = false; }; } // namespace Cangjie::Interop::Java #endif // CANGJIE_SEMA_NATIVE_FFI_JAVA_DESUGAR_INTEROP_MANAGER cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/AfterTypeCheck/TypeCheckJavaInterop.cpp000066400000000000000000000360661510705540100305210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "JavaInteropManager.h" #include "JavaDesugarManager.h" #include "DiagsInterop.h" #include "TypeCheckUtil.h" #include "TypeCheckerImpl.h" #include "Utils.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace TypeCheckUtil; using namespace AST; using namespace Cangjie::Interop::Java; namespace { /* Represents a position of value where specific type is allowed to be * - OUT -- return values * - IN -- methods parameters * - BOTH -- OUT and IN */ enum class Position { OUT, IN, BOTH }; inline bool IsJavaMirror(const Decl& decl) { return decl.TestAttr(Attribute::JAVA_MIRROR); } /* * Checks if [ty] is direct or indirect child of @JavaMirror-annotated decl. */ inline bool IsJavaMirrorSubtype(const Ty& ty) { if (auto classTy = DynamicCast(&ty); classTy && classTy->GetSuperClassTy() && !classTy->GetSuperClassTy()->IsObject() ) { if (!IsJavaMirrorSubtype(*classTy->GetSuperClassTy()) && (!classTy->GetSuperClassTy()->decl || !IsJavaMirror(*classTy->GetSuperClassTy()->decl))) { return false; } return true; } if (auto ity = DynamicCast(&ty)) { if (ity->GetSuperInterfaceTys().empty()) { return false; } for (auto parent : ity->GetSuperInterfaceTys()) { if (IsJavaMirror(*parent->decl)) { return true; } } } return false; } struct JavaInteropTypeChecker { bool isImpl; bool isCJMappingTypeCheck = false; DiagnosticEngine& diag; auto GetJavaClassKind() const { if (isCJMappingTypeCheck) { return "CJMapping"; } if (isImpl) { return "@JavaImpl"; } else { return "@JavaMirror"; } } /* * Checks if [ty] is supported in signature. * - @JavaMirror class types * - Some built-in supported types * - String type in return position in Mirror class */ inline bool IsSupported(const Ty& ty, Position pos) { auto areJavaMirrorTypes = [this](const std::vector>& typeArgs, Position pos) { return std::all_of(typeArgs.begin(), typeArgs.end(), [this, pos](const auto& argTy) { return IsSupported(*argTy, pos); }); }; // Use IsCJMapping to check a ty is supported or not. if (isCJMappingTypeCheck && IsCJMapping(ty)) { return true; } switch (ty.kind) { case TypeKind::TYPE_UNIT: case TypeKind::TYPE_BOOLEAN: case TypeKind::TYPE_INT8: case TypeKind::TYPE_UINT16: case TypeKind::TYPE_INT16: case TypeKind::TYPE_INT32: case TypeKind::TYPE_INT64: case TypeKind::TYPE_FLOAT32: case TypeKind::TYPE_FLOAT64: return true; case TypeKind::TYPE_ENUM: if (!ty.IsCoreOptionType() || ty.typeArgs[0]->IsCoreOptionType()) { return false; }; return !ty.typeArgs[0]->IsPrimitive() && areJavaMirrorTypes(ty.typeArgs, pos); case TypeKind::TYPE_CLASS: case TypeKind::TYPE_INTERFACE: if (ty.IsObject()) { return true; } return IsJavaMirror(*StaticCast(ty).commonDecl); case TypeKind::TYPE_STRUCT: if (ty.name == INTEROPLIB_CFFI_JAVA_ENTITY) { // for javaref getter stub wich is generated on Parser stage return true; } if (!isImpl && pos == Position::OUT && ty.IsString()) { return true; } return false; default: return false; } } /* * Checks if [ty] is one of: * of @JavaMirror-annotated decl * supported built-in type * direct or indirect successor of @JavaMirror-annotated decl (with already enabled JAVA_MIRROR_SUBTYPE attribute) */ inline bool IsJavaCompatible(const Ty& ty, Position pos = Position::BOTH) { if (IsSupported(ty, pos)) { if (auto classLikeTy = DynamicCast(&ty); classLikeTy && classLikeTy->commonDecl && IsJArray(*classLikeTy->commonDecl) ) { return IsJavaCompatible(*ty.typeArgs[0]); } return true; } if (ty.IsCoreOptionType() && !ty.typeArgs[0]->IsCoreOptionType() && !ty.typeArgs[0]->IsPrimitive()) { return IsJavaCompatible(*ty.typeArgs[0]); } if (auto classLikeTy = DynamicCast(&ty); classLikeTy && classLikeTy->commonDecl) { return classLikeTy->commonDecl->TestAttr(Attribute::JAVA_MIRROR_SUBTYPE); } return false; } inline void CheckJavaCompatibleParamTypes(FuncDecl& fdecl, DiagKindRefactor errkind) { if (!fdecl.funcBody) { return; } auto isJavaArray = IsJArray(*fdecl.outerDecl); for (auto& paramList : fdecl.funcBody->paramLists) { for (auto ¶m : paramList->params) { if (!IsJavaCompatible(*param->ty) && (!isJavaArray || !param->ty->IsGeneric())) { diag.DiagnoseRefactor(errkind, *param); fdecl.EnableAttr(Attribute::IS_BROKEN); fdecl.outerDecl->EnableAttr(Attribute::HAS_BROKEN); fdecl.outerDecl->EnableAttr(Attribute::IS_BROKEN); } } } } inline void CheckJavaMirrorMethodTypes(FuncDecl& fd) { if (fd.funcBody && fd.funcBody->retType && !IsJavaCompatible(*fd.funcBody->retType->ty, Position::OUT) && (!IsJArray(*fd.outerDecl) || !fd.funcBody->retType->ty->IsGeneric()) ) { Ptr node; if (!fd.funcBody->retType->begin.IsZero()) { node = fd.funcBody->retType; } else { // Type wasn't explicitly written, so report on whole declaration node = &fd; } diag.DiagnoseRefactor( DiagKindRefactor::sema_java_mirror_method_ret_unsupported, *node, Ty::ToString(fd.funcBody->retType->ty), GetJavaClassKind()); fd.EnableAttr(Attribute::IS_BROKEN); } CheckJavaCompatibleParamTypes(fd, DiagKindRefactor::sema_java_mirror_method_arg_must_be_java_mirror); } inline void CheckJavaMirrorConstructorTypes(FuncDecl& ctor) { CheckJavaCompatibleParamTypes(ctor, DiagKindRefactor::sema_java_mirror_ctor_arg_must_be_java_mirror); } inline void CheckJavaMirrorPropType(PropDecl& prop) { if (!IsJavaCompatible(*prop.ty)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_java_mirror_prop_must_be_java_mirror, *prop.type); prop.EnableAttr(Attribute::IS_BROKEN); } } inline void CheckJavaCompatibleDeclSignature(ClassLikeDecl& decl) { for (auto& member : decl.GetMemberDecls()) { switch (member->astKind) { case ASTKind::FUNC_DECL: if (member->TestAttr(Attribute::CONSTRUCTOR)) { CheckJavaMirrorConstructorTypes(*StaticAs(member.get())); } else { if (isImpl && member->TestAttr(Attribute::PRIVATE)) { continue; } CheckJavaMirrorMethodTypes(*StaticAs(member.get())); } break; case ASTKind::PROP_DECL: CheckJavaMirrorPropType(*StaticAs(member.get())); break; default: continue; } } } inline void CheckCJMappingMethodTypes(FuncDecl& fd) { if (fd.funcBody && fd.funcBody->retType && (fd.funcBody->retType->ty->IsCoreOptionType() || !IsJavaCompatible(*fd.funcBody->retType->ty, Position::OUT))) { Ptr node; if (!fd.funcBody->retType->begin.IsZero()) { node = fd.funcBody->retType; } else { node = &fd; } diag.DiagnoseRefactor(DiagKindRefactor::sema_cjmapping_method_ret_unsupported, *node, Ty::ToString(fd.funcBody->retType->ty), GetJavaClassKind()); fd.EnableAttr(Attribute::IS_BROKEN); } CheckCJMappingCompatibleParamTypes(fd); } inline void CheckCJMappingCompatibleParamTypes(FuncDecl& fdecl) { if (!fdecl.funcBody) { return; } for (auto& paramList : fdecl.funcBody->paramLists) { for (auto& param : paramList->params) { bool isOptionArg = (*param->ty).IsCoreOptionType(); if (isOptionArg || !IsJavaCompatible(*param->ty)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_cjmapping_method_arg_not_supported, *param); fdecl.EnableAttr(Attribute::IS_BROKEN); fdecl.outerDecl->EnableAttr(Attribute::HAS_BROKEN); fdecl.outerDecl->EnableAttr(Attribute::IS_BROKEN); } } } } }; } // namespace namespace Cangjie::Interop::Java { void JavaInteropManager::CheckInheritance(ClassLikeDecl& decl) const { CheckJavaMirrorSubtypeAttrClassLikeDecl(decl); if (IsMirror(decl) || IsImpl(decl)) { CheckNonJavaSuperType(decl); } } void JavaInteropManager::CheckNonJavaSuperType(ClassLikeDecl& decl) const { CJC_ASSERT(IsMirror(decl) || IsImpl(decl)); auto superDecls = decl.GetAllSuperDecls(); for (const auto superDecl : superDecls) { if (!IsMirror(*superDecl) && !IsImpl(*superDecl) && !superDecl->ty->IsObject() && !superDecl->ty->IsAny()) { DiagJavaDeclCannotInheritPureCangjieType(diag, decl); decl.EnableAttr(Attribute::IS_BROKEN); return; } } if (decl.HasAnno(AnnotationKind::JAVA_IMPL) && !IsJavaMirrorSubtype(*decl.ty)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_java_mirror_subtype_anno_must_inherit_mirror, decl); decl.EnableAttr(Attribute::IS_BROKEN); } } void JavaInteropManager::CheckJavaMirrorSubtypeAttrClassLikeDecl(ClassLikeDecl& decl) const { if (IsJavaMirrorSubtype(*decl.ty)) { if (!decl.TestAnyAttr(Attribute::JAVA_MIRROR_SUBTYPE, Attribute::JAVA_MIRROR)) { DiagJavaMirrorChildMustBeAnnotated(diag, decl); decl.EnableAttr(Attribute::IS_BROKEN); } } } void JavaInteropManager::CheckExtendDecl(ExtendDecl& decl) const { auto& ty = *decl.extendedType->ty; if (!IsMirror(ty) && !IsImpl(ty)) { return; } if (!decl.inheritedTypes.empty()) { DiagJavaDeclCannotBeExtendedWithInterface(diag, decl); decl.EnableAttr(Attribute::IS_BROKEN); } } void JavaInteropManager::CheckJavaMirrorTypes(ClassLikeDecl& decl) { JavaInteropTypeChecker checker{ .isImpl = false, .diag = diag, }; checker.CheckJavaCompatibleDeclSignature(decl); } void JavaInteropManager::CheckJavaImplTypes(ClassLikeDecl& decl) { JavaInteropTypeChecker checker{ .isImpl = true, .diag = diag, }; checker.CheckJavaCompatibleDeclSignature(decl); } void JavaInteropManager::CheckImplRedefinition(Package& package) { std::unordered_map javaNameToDecl; for (auto& file : package.files) { for (auto& decl : file->decls) { auto classLikeDecl = As(decl); if (!classLikeDecl || classLikeDecl->TestAttr(Attribute::IS_BROKEN)) { continue; } if (!IsImpl(*classLikeDecl)) { continue; } auto name = GetJavaFQName(*classLikeDecl); if (javaNameToDecl.find(name) != javaNameToDecl.end()) { DiagJavaImplRedefinitionInJava(diag, *classLikeDecl, *(javaNameToDecl[name])); classLikeDecl->EnableAttr(Attribute::IS_BROKEN); continue; } javaNameToDecl[name] = classLikeDecl; } } } void JavaInteropManager::CheckTypes(File& file) { for (auto& decl : file.decls) { if (auto classLikeDecl = As(decl)) { CheckInheritance(*classLikeDecl); CheckTypes(*classLikeDecl); } else if (auto extendDecl = As(decl)) { CheckExtendDecl(*extendDecl); } CheckCJMappingType(*decl); } } void JavaInteropManager::CheckTypes(ClassLikeDecl& classLikeDecl) { if (!classLikeDecl.TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE)) { return; } hasMirrorOrImpl = true; if (auto id = As(&classLikeDecl); id && id->TestAttr(Attribute::JAVA_MIRROR_SUBTYPE) && !id->TestAttr(Attribute::JAVA_MIRROR)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_java_interop_not_supported, *id, "@JavaImpl interface"); classLikeDecl.EnableAttr(Attribute::IS_BROKEN); return; } if (auto cd = As(&classLikeDecl); cd && cd->TestAttr(Attribute::ABSTRACT) && IsImpl(*cd)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_java_interop_not_supported, *cd, "@JavaImpl abstract"); classLikeDecl.EnableAttr(Attribute::IS_BROKEN); return; } if (classLikeDecl.TestAttr(Attribute::JAVA_MIRROR)) { CheckJavaMirrorTypes(classLikeDecl); } else if (classLikeDecl.TestAttr(Attribute::JAVA_MIRROR_SUBTYPE)) { CheckJavaImplTypes(classLikeDecl); } } void JavaInteropManager::CheckCJMappingDeclSupportRange(Decl& decl) { switch (decl.astKind) { case ASTKind::STRUCT_DECL: case ASTKind::ENUM_DECL: break; default: diag.DiagnoseRefactor(DiagKindRefactor::sema_cjmapping_decl_not_supported, MakeRange(decl.identifier), std::string{decl.identifier}); decl.EnableAttr(Attribute::IS_BROKEN); } } void JavaInteropManager::CheckCJMappingType(Decl& decl) { if (!IsCJMapping(decl)) { return; } CheckCJMappingDeclSupportRange(decl); JavaInteropTypeChecker checker{ .isImpl = false, .isCJMappingTypeCheck = true, .diag = diag, }; for (auto& member : decl.GetMemberDecls()) { switch (member->astKind) { case ASTKind::FUNC_DECL: if (!member->TestAttr(Attribute::PUBLIC)) { continue; } checker.CheckCJMappingMethodTypes(*StaticAs(member.get())); break; default: continue; } } } } cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/AfterTypeCheck/Utils.cpp000066400000000000000000001042701510705540100255700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Utils.h" #include "NativeFFI/Utils.h" #include "TypeCheckUtil.h" #include "Desugar/AfterTypeCheck.h" #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/AST/Match.h" #include "cangjie/Utils/ConstantsUtils.h" #include "JavaDesugarManager.h" #include "../../../InheritanceChecker/StructInheritanceChecker.h" #include "cangjie/AST/Utils.h" namespace { using namespace Cangjie; std::string NormalizeJavaSignature(const std::string& sig) { std::string normalized = sig; std::replace(normalized.begin(), normalized.end(), '.', '/'); return normalized; } /** * @brief Generates a synthetic function stub based on an existing function declaration. * * This function creates a clone of the provided function declaration (fd), * replaces its outerDecl to synthetic class, and then inserts the * modified function declaration into the specified synthetic class declaration. * * @param synthetic The class declaration where the cloned function stub will be inserted. * @param fd The original function declaration that will be cloned and modified. */ void GenerateSyntheticClassFuncStub(ClassDecl& synthetic, FuncDecl& fd) { OwnedPtr abstractFdStub = ASTCloner::Clone(Ptr(&fd)); // remove foreign anno from cloned func decl for (auto it = abstractFdStub->annotations.begin(); it != abstractFdStub->annotations.end(); ++it) { if ((*it)->kind == AnnotationKind::FOREIGN_NAME) { abstractFdStub->annotations.erase(it); break; } } abstractFdStub->outerDecl = Ptr(&synthetic); synthetic.body->decls.emplace_back(std::move(abstractFdStub)); } void GenerateSyntheticClassPropStub(ClassDecl& synthetic, PropDecl& fd) { CJC_ASSERT(&synthetic); CJC_ASSERT(&fd); // TODO: } void GenerateSyntheticClassAbstractMemberImplStubs(ClassDecl& synthetic, const MemberMap& members) { for (const auto& idMemberSignature : members) { auto& signature = idMemberSignature.second; if (!signature.decl->TestAttr(Attribute::ABSTRACT)) { continue; } switch (signature.decl->astKind) { case ASTKind::FUNC_DECL: GenerateSyntheticClassFuncStub(synthetic, *StaticAs(signature.decl)); break; case ASTKind::PROP_DECL: GenerateSyntheticClassPropStub(synthetic, *StaticAs(signature.decl)); break; default: continue; } } } } namespace Cangjie::Interop::Java { using namespace TypeCheckUtil; using namespace Cangjie::Native::FFI; Utils::Utils(ImportManager& importManager, TypeManager& typeManager) : importManager(importManager), typeManager(typeManager) {} Ptr Utils::GetOptionTy(Ptr ty) { return typeManager.GetEnumTy(*GetOptionDecl(), {ty}); } Ptr Utils::GetOptionDecl() { static auto decl = importManager.GetCoreDecl(STD_LIB_OPTION); return decl; } Ptr Utils::GetOptionSomeDecl() { static auto someDecl = Sema::Desugar::AfterTypeCheck::LookupEnumMember(GetOptionDecl(), OPTION_VALUE_CTOR); return someDecl; } Ptr Utils::GetOptionNoneDecl() { static auto noneDecl = Sema::Desugar::AfterTypeCheck::LookupEnumMember(GetOptionDecl(), OPTION_NONE_CTOR); return noneDecl; } OwnedPtr Utils::CreateOptionSomeRef(Ptr ty) { auto someDeclRef = CreateRefExpr(*GetOptionSomeDecl()); auto optionActualTy = GetOptionTy(ty); someDeclRef->ty = typeManager.GetFunctionTy({ty}, optionActualTy); return someDeclRef; } OwnedPtr Utils::CreateOptionNoneRef(Ptr ty) { auto noneDeclRef = CreateRefExpr(*GetOptionNoneDecl()); auto optionActualTy = GetOptionTy(ty); noneDeclRef->ty = optionActualTy; return noneDeclRef; } OwnedPtr Utils::CreateOptionSomeCall(OwnedPtr expr, Ptr ty) { std::vector> someDeclCallArgs {}; someDeclCallArgs.emplace_back(CreateFuncArg(std::move(expr))); auto someDeclCall = CreateCallExpr(CreateOptionSomeRef(ty), std::move(someDeclCallArgs)); someDeclCall->ty = GetOptionTy(ty); someDeclCall->resolvedFunction = As(GetOptionSomeDecl()); someDeclCall->callKind = CallKind::CALL_DECLARED_FUNCTION; return someDeclCall; } Ptr Utils::GetJObjectDecl() { return GetJavaLangDecl(INTEROP_JOBJECT_NAME); } Ptr Utils::GetJStringDecl() { return GetJavaLangDecl(INTEROP_JSTRING_NAME); } Ptr Utils::GetJavaLangDecl(const std::string& identifier) { auto decl = DynamicCast(importManager.GetImportedDecl(INTEROP_JAVA_LANG_PACKAGE, identifier)); if (decl == nullptr) { importManager.GetDiagnosticEngine().DiagnoseRefactor( DiagKindRefactor::sema_member_not_imported, DEFAULT_POSITION, INTEROP_JAVA_LANG_PACKAGE + "." + identifier); return Ptr(nullptr); } return decl; } StructDecl& Utils::GetStringDecl() { return Native::FFI::GetStringDecl(importManager); } Ptr GetJavaRefField(ClassDecl& mirrorLike) { if (mirrorLike.TestAttr(Attribute::JAVA_MIRROR_SUBTYPE)) { if (auto superClass = mirrorLike.GetSuperClassDecl(); superClass && !superClass->ty->IsObject() && superClass->TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE)) { return GetJavaRefField(*superClass); } auto superClass = mirrorLike.GetSuperClassDecl(); if (!superClass || !superClass->TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE)) { CJC_ABORT(); // neither mirror or mirror subtype are super type of [mirror] } return GetJavaRefField(*superClass); } CJC_ASSERT(mirrorLike.TestAttr(Attribute::JAVA_MIRROR)); CJC_ASSERT(IsJObject(mirrorLike)); CJC_ASSERT(mirrorLike.body); for (auto& member : mirrorLike.body->decls) { if (auto varDecl = As(member); varDecl && varDecl->identifier == JAVA_REF_FIELD_NAME) { return varDecl; } } CJC_ABORT(); // Internal error: mirror is not @JavaMirror or weak reference field was not generated! return nullptr; } // will be removed or changed when interface for JavaImpl will be supported // now it's kind of stub Ptr GetJavaRefField(ClassLikeDecl& mirror) { if (mirror.astKind == AST::ASTKind::CLASS_DECL) { if (auto mirrorClass = DynamicCast(&mirror)) { return GetJavaRefField(*mirrorClass); } } CJC_ABORT(); // Internal error: mirror is not @JavaMirror or weak reference field was not generated! return nullptr; } bool IsJavaRefGetter(const Decl& fd) { return fd.astKind == ASTKind::FUNC_DECL && fd.TestAttr(Attribute::COMPILER_ADD) && fd.identifier.Val() == JAVA_REF_GETTER_FUNC_NAME; } Ptr GetJavaRefGetter(ClassLikeDecl& mirror) { CJC_ASSERT(mirror.TestAnyAttr(Attribute::JAVA_MIRROR_SUBTYPE, Attribute::JAVA_MIRROR)); const std::function& isDeclJavaRefGetterFunc = [](const Decl& d) { return IsJavaRefGetter(d); }; if (auto cd = DynamicCast(&mirror)) { if (!IsJObject(mirror)) { return GetJavaRefGetter(*cd->GetSuperClassDecl()); } else { return FindFirstMemberDecl(mirror, isDeclJavaRefGetterFunc); } } else { return FindFirstMemberDecl(mirror, isDeclJavaRefGetterFunc); } } OwnedPtr CreateJavaRefCall(ClassLikeDecl& mirrorLike, FuncDecl& javaRefGetter, Ptr curFile) { CJC_ASSERT(mirrorLike.TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE)); auto thisRef = CreateThisRef(&mirrorLike, mirrorLike.ty, curFile); return CreateJavaRefCall(std::move(thisRef), javaRefGetter); } OwnedPtr CreateJavaRefCall(ClassLikeDecl& mirrorLike, VarDecl& javaref, Ptr curFile) { CJC_ASSERT(mirrorLike.astKind == ASTKind::CLASS_DECL); CJC_ASSERT(mirrorLike.TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE)); auto thisRef = CreateThisRef(&mirrorLike, mirrorLike.ty, curFile); return CreateJavaRefCall(std::move(thisRef), javaref); } OwnedPtr CreateJavaRefCall(OwnedPtr expr, FuncDecl& javaRefGetter) { // expr ty decl and javaRef outerDecl must be the same CJC_ASSERT(expr->ty->IsClassLike()); CJC_ASSERT(StaticCast(expr->ty)->commonDecl->TestAnyAttr( Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE)); auto curFile = expr->curFile; CJC_NULLPTR_CHECK(curFile); return CreateCallExpr( WithinFile(CreateMemberAccess(std::move(expr), javaRefGetter), curFile), {}, &javaRefGetter, StaticCast(javaRefGetter.ty)->retTy, CallKind::CALL_DECLARED_FUNCTION); } OwnedPtr CreateJavaRefCall(OwnedPtr expr, VarDecl& javaref) { // expr ty decl and javaref outerDecl must be the same CJC_ASSERT(expr->ty->IsClassLike()); CJC_ASSERT(StaticCast(expr->ty)->commonDecl->TestAnyAttr( Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE)); auto curFile = expr->curFile; CJC_NULLPTR_CHECK(curFile); return WithinFile(CreateMemberAccess(std::move(expr), javaref), curFile); } OwnedPtr CreateJavaRefCall(OwnedPtr expr, ClassLikeDecl& mirrorLike) { CJC_ASSERT(mirrorLike.TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE)); if (auto mirrorLikeClass = As(&mirrorLike)) { return CreateJavaRefCall(std::move(expr), *GetJavaRefField(*mirrorLikeClass)); } // for an interface return CreateJavaRefCall(std::move(expr), *GetJavaRefGetter(mirrorLike)); } OwnedPtr CreateJavaRefCall(ClassLikeDecl& mirrorLike, Ptr curFile) { auto thisExpr = CreateThisRef(Ptr(&mirrorLike), mirrorLike.ty, curFile); return CreateJavaRefCall(std::move(thisExpr), mirrorLike); } OwnedPtr CreateJavaRefCall(OwnedPtr expr) { CJC_ASSERT(expr->ty->IsClassLike()); auto classLikeTy = StaticCast(expr->ty); CJC_ASSERT(classLikeTy->commonDecl); return CreateJavaRefCall(std::move(expr), *classLikeTy->commonDecl); } bool IsGeneratedJavaMirrorConstructor(const FuncDecl& ctor) { return ctor.TestAttr(Attribute::JAVA_MIRROR) && ctor.TestAttr(Attribute::CONSTRUCTOR) && ctor.TestAttr(Attribute::COMPILER_ADD); } Ptr GetGeneratedConstructorInMirror(ClassDecl& mirror) { CJC_ASSERT(mirror.TestAttr(Attribute::JAVA_MIRROR)); Ptr generatedCtor; for (auto& member : mirror.GetMemberDecls()) { if (auto fd = As(member); fd && IsGeneratedJavaMirrorConstructor(*fd)) { generatedCtor = fd; break; } } return generatedCtor; } Ptr GetGeneratedJavaMirrorConstructor(ClassLikeDecl& mirror) { CJC_ASSERT(mirror.astKind == AST::ASTKind::CLASS_DECL); if (mirror.TestAttr(Attribute::JAVA_MIRROR_SUBTYPE) && !mirror.TestAttr(Attribute::JAVA_MIRROR)) { for (auto& superType : mirror.inheritedTypes) { if (superType->ty->kind != TypeKind::TYPE_CLASS) { continue; } auto superTy = static_cast(superType->ty.get()); if (superTy->commonDecl->TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE)) { return GetGeneratedJavaMirrorConstructor(*superTy->commonDecl); } } CJC_ABORT(); // impl class must have mirror parent } CJC_ASSERT(mirror.TestAttr(Attribute::JAVA_MIRROR)); if (auto mirrorClass = DynamicCast(&mirror)) { auto curCtor = GetGeneratedConstructorInMirror(*mirrorClass); CJC_ASSERT(curCtor); return curCtor; } CJC_ABORT(); return nullptr; } bool IsGeneratedJavaImplConstructor(const FuncDecl& ctor) { if (!ctor.TestAttr(Attribute::CONSTRUCTOR)) { return false; } auto& plists = ctor.funcBody->paramLists; if (plists.empty() || plists[0]->params.empty()) { return false; } return plists[0]->params[0]->identifier == JAVA_IMPL_ENTITY_ARG_NAME_IN_GENERATED_CTOR; } std::string GetJavaMemberName(const Decl& decl) { for (auto& anno : decl.annotations) { if (anno->kind != AnnotationKind::FOREIGN_NAME) { continue; } CJC_ASSERT(anno->args.size() == 1); auto litExpr = DynamicCast(anno->args[0]->expr.get()); CJC_ASSERT(litExpr); return litExpr->stringValue; } return decl.identifier; } bool HasPredefinedJavaName(const ClassLikeDecl& decl) { for (auto& anno : decl.annotations) { if (anno->kind != AnnotationKind::JAVA_MIRROR && anno->kind != AnnotationKind::JAVA_IMPL) { continue; } return !anno->args.empty(); } return false; } Ptr GetJavaMirrorAnnoAttr(const ClassLikeDecl& decl) { for (auto& anno : decl.annotations) { if (anno->kind != AnnotationKind::JAVA_MIRROR && anno->kind != AnnotationKind::JAVA_IMPL) { continue; } CJC_ASSERT(anno->args.size() < 2); // It is empty if < 2 if (anno->args.empty()) { break; } CJC_ASSERT(anno->args[0]->expr->astKind == ASTKind::LIT_CONST_EXPR); auto lce = As(anno->args[0]->expr.get()); CJC_ASSERT(lce); return &lce->stringValue; } return nullptr; } namespace { template std::string StringJoin(I begin, I end, std::string_view separator) { std::string res; for (auto it = begin; it != end; ++it) { if (it != begin) { res += separator; } res += *it; } return res; } std::vector GetFQNameParts(std::string_view name) { std::vector res; res.push_back(""); bool wasBackslash = false; for (std::size_t i = 0; i < name.length(); ++i) { switch (name[i]) { case '\\': if (wasBackslash) { res.back() += '\\'; } wasBackslash = true; continue; case '$': if (wasBackslash) { res.back() += '$'; } else { res.push_back(""); } wasBackslash = false; continue; default: if (wasBackslash) { res.back() += '\\'; } res.back() += name[i]; wasBackslash = false; continue; } } if (wasBackslash) { res.back() += '\\'; } return res; } std::string GetFQNameJoinBy(const std::string& name, std::string_view separator) { auto parts = GetFQNameParts(name); return StringJoin(parts.begin(), parts.end(), separator); } } std::string GetJavaFQName(const Decl& decl) { if (auto classlikeDecl = DynamicCast(&decl)) { auto attr = GetJavaMirrorAnnoAttr(*classlikeDecl); if (attr) { return GetFQNameJoinBy(*attr, "$"); } } return decl.GetFullPackageName() + "." + decl.identifier; } std::string GetJavaFQSourceCodeName(const ClassLikeDecl& decl) { auto attr = GetJavaMirrorAnnoAttr(decl); return attr ? GetFQNameJoinBy(*attr, ".") : (decl.GetFullPackageName() + "." + decl.identifier); } DestructedJavaClassName DestructJavaClassName(const ClassLikeDecl& decl) { auto attr = GetJavaMirrorAnnoAttr(decl); if (!attr) { return {decl.GetFullPackageName(), decl.identifier, decl.identifier}; } auto parts = GetFQNameParts(*attr); CJC_ASSERT(parts.size() > 0); auto ind = parts[0].find_last_of('.'); if (ind == std::string::npos) { return { .packageName=std::nullopt, .topLevelClassName=parts[0], .fullClassName=StringJoin(parts.begin(), parts.end(), ".") }; } auto package = parts[0].substr(0, ind); parts[0] = parts[0].substr(ind + 1); return { .packageName=package, .topLevelClassName=parts[0], .fullClassName=StringJoin(parts.begin(), parts.end(), ".") }; } ArrayOperationKind GetArrayOperationKind(Decl& decl) { CJC_ASSERT(IsJArray(*decl.outerDecl)); if (Is(decl) && decl.identifier == "length") { return ArrayOperationKind::GET_LENGTH; } if (auto funcDecl = As(&decl); funcDecl && funcDecl->identifier == "[]") { auto paramsNumber = funcDecl->funcBody->paramLists[0]->params.size(); if (paramsNumber == 1) { return ArrayOperationKind::GET; } else if (paramsNumber == 2) { return ArrayOperationKind::SET; } } if (auto funcDecl = As(&decl)) { if (funcDecl->identifier == JAVA_ARRAY_GET_FOR_REF_TYPES) { // Special version of get operation for object-based types return ArrayOperationKind::GET; } if (funcDecl->identifier == JAVA_ARRAY_SET_FOR_REF_TYPES) { // Special version of set operation for object-based types return ArrayOperationKind::SET; } } CJC_ABORT(); return ArrayOperationKind::GET; } std::string GetJavaPackage(const Decl& decl) { for (auto& anno : decl.annotations) { if (anno->kind != AnnotationKind::JAVA_MIRROR && anno->kind != AnnotationKind::JAVA_IMPL) { continue; } CJC_ASSERT(anno->args.size() < 2); if (anno->args.empty()) { break; } CJC_ASSERT(anno->args[0]->expr->astKind == ASTKind::LIT_CONST_EXPR); auto lce = As(anno->args[0]->expr.get()); CJC_ASSERT(lce); std::string fqname = lce->stringValue; auto beforeClassNamePos = fqname.rfind("."); if (beforeClassNamePos != std::string::npos) { fqname.erase(beforeClassNamePos); } else { return ""; } return fqname; } return decl.GetFullPackageName(); } void MangleJNIName(std::string& name) { size_t start_pos = 0; while ((start_pos = name.find("_", start_pos)) != std::string::npos) { name.replace(start_pos, 1, "_1"); start_pos += 2; } std::replace(name.begin(), name.end(), '.', '_'); } std::string Utils::GetJavaObjectTypeName(const Ty& ty) { if (ty.IsCoreOptionType()) { return GetJavaObjectTypeName(*ty.typeArgs[0]); } if (ty.kind == TypeKind::TYPE_BOOLEAN) { return "java.lang.Boolean"; } if (ty.kind == TypeKind::TYPE_INT8) { return "java.lang.Byte"; } if (ty.kind == TypeKind::TYPE_UINT16) { return "java.lang.Character"; } if (ty.kind == TypeKind::TYPE_INT16) { return "java.lang.Short"; } if (ty.kind == TypeKind::TYPE_INT32) { return "java.lang.Integer"; } if (ty.kind == TypeKind::TYPE_INT64) { return "java.lang.Long"; } if (ty.kind == TypeKind::TYPE_FLOAT32) { return "java.lang.Float"; } if (ty.kind == TypeKind::TYPE_FLOAT64) { return "java.lang.Double"; } if (ty.kind == TypeKind::TYPE_CLASS || ty.kind == TypeKind::TYPE_INTERFACE) { auto& cldecl = *StaticCast(ty).commonDecl; if (IsJArray(cldecl)) { return GetJavaObjectTypeName(*ty.typeArgs[0]) + "[]"; } return GetJavaFQName(cldecl); } if (ty.IsString()) { return GetJavaFQName(*GetJStringDecl()); } return "unknown type"; } /* * Call only when the type is a class or interface. * such as: turn class 'Integer' to 'java/lang/Integer' */ std::string Utils::GetJavaClassNormalizeSignature(const Ty& cjtype) const { CJC_ASSERT(!IsJArray(cjtype)); return NormalizeJavaSignature(GetJavaFQName(*StaticCast(cjtype).commonDecl)); } /* * Should be called only on java compatible type or a function type over java compatible types */ std::string Utils::GetJavaTypeSignature(const Ty& cjtype) { std::string jsig; switch (cjtype.kind) { case TypeKind::TYPE_UNIT: jsig = "V"; break; case TypeKind::TYPE_BOOLEAN: jsig = "Z"; break; case TypeKind::TYPE_INT8: jsig = "B"; break; case TypeKind::TYPE_UINT16: jsig = "C"; break; case TypeKind::TYPE_INT16: jsig = "S"; break; case TypeKind::TYPE_INT32: jsig = "I"; break; case TypeKind::TYPE_INT64: jsig = "J"; break; case TypeKind::TYPE_FLOAT32: jsig = "F"; break; case TypeKind::TYPE_FLOAT64: jsig = "D"; break; case TypeKind::TYPE_STRUCT: { if (cjtype.IsString()) { jsig = "L" + NormalizeJavaSignature(GetJavaFQName(*GetJStringDecl())) + ";"; break; } if (!cjtype.IsStructArray()) { break; } [[fallthrough]]; // for Array - fallback } case TypeKind::TYPE_ARRAY: jsig = "[" + GetJavaTypeSignature(*cjtype.typeArgs[0]); break; case TypeKind::TYPE_ENUM: { if (!cjtype.IsCoreOptionType()) { break; }; auto& argTy = *cjtype.typeArgs[0]; if (IsJArray(argTy)) { jsig = GetJavaTypeSignature(argTy); } else { jsig = "L" + NormalizeJavaSignature(GetJavaObjectTypeName(argTy)) + ";"; } break; } case TypeKind::TYPE_CLASS: case TypeKind::TYPE_INTERFACE: if (IsJArray(*StaticCast(cjtype).commonDecl)) { jsig = "[" + GetJavaTypeSignature(*cjtype.typeArgs[0]); } else { jsig = "L" + NormalizeJavaSignature( GetJavaFQName(*StaticCast(cjtype).commonDecl) ) + ";"; } break; case TypeKind::TYPE_FUNC: { auto& funcTy = *StaticCast(&cjtype); jsig = "("; for (auto paramTy : funcTy.paramTys) { jsig.append(GetJavaTypeSignature(*paramTy)); } jsig.append(")"); jsig.append(GetJavaTypeSignature(*funcTy.retTy)); break; } default: CJC_ABORT(); break; // method must be called only on java-compatible types } return jsig; } std::string Utils::GetJavaTypeSignature(Ty& retTy, const std::vector>& params) { return GetJavaTypeSignature(*typeManager.GetFunctionTy(params, &retTy)); } std::string GetMangledJniInitCjObjectFuncName(const BaseMangler& mangler, const std::vector>& params, bool isGeneratedCtor) { std::string name("initCJObject"); // the first parameter is added in generated constructor, it should be skipped in mangling size_t toSkip = isGeneratedCtor ? 1 : 0; for (auto& param : params) { if (toSkip > 0) { toSkip--; continue; } auto mangledParam = mangler.MangleType(*param->ty); std::replace(mangledParam.begin(), mangledParam.end(), '.', '_'); name += mangledParam; } return name; } std::string GetMangledJniInitCjObjectFuncNameForEnum( const BaseMangler& mangler, const std::vector>& params, const std::string funcName) { std::string name = funcName + "initCJObject"; // the first parameter is added in generated constructor, it should be skipped in mangling for (auto& param : params) { auto mangledParam = mangler.MangleType(*param->ty); std::replace(mangledParam.begin(), mangledParam.end(), '.', '_'); name += mangledParam; } return name; } bool IsMirror(const Ty& ty) { auto classLikeTy = DynamicCast(&ty); return classLikeTy && classLikeTy->commonDecl && IsMirror(*classLikeTy->commonDecl); } bool IsImpl(const Ty& ty) { auto classLikeTy = DynamicCast(&ty); return classLikeTy && classLikeTy->commonDecl && IsImpl(*classLikeTy->commonDecl); } bool IsCJMapping(const Ty& ty) { // currently only support struct type and enum type if (auto structTy = DynamicCast(&ty)) { return structTy->decl && IsCJMapping(*structTy->decl); } else if (auto enumTy = DynamicCast(&ty)) { return enumTy->decl && IsCJMapping(*enumTy->decl); } return false; } const Ptr GetSyntheticClass(const ImportManager& importManager, const ClassLikeDecl& cld) { ClassDecl* synthetic = importManager.GetImportedDecl( cld.fullPackageName, GetSyntheticNameFromClassLike(cld)); CJC_NULLPTR_CHECK(synthetic); return Ptr(synthetic); } OwnedPtr CreateMirrorConstructorCall( const ImportManager& importManager, OwnedPtr entity, Ptr mirrorTy) { auto curFile = entity->curFile; auto classLikeTy = DynamicCast(mirrorTy.get()); if (!classLikeTy) { return nullptr; } if (auto decl = classLikeTy->commonDecl; decl && decl->TestAttr(Attribute::JAVA_MIRROR)) { Ptr mirrorCtor; if (decl->astKind == ASTKind::INTERFACE_DECL || (decl->astKind == ASTKind::CLASS_DECL && decl->TestAttr(Attribute::ABSTRACT))) { Ptr synthetic = GetSyntheticClass(importManager, *decl); mirrorCtor = GetGeneratedConstructorInMirror(*synthetic); CJC_ASSERT(mirrorCtor); } else if (decl->astKind == AST::ASTKind::CLASS_DECL) { auto cld = As(decl); CJC_ASSERT(cld); mirrorCtor = GetGeneratedJavaMirrorConstructor(*cld); } else { CJC_ABORT(); } auto ctorCall = CreateCall(mirrorCtor, curFile, std::move(entity)); if (ctorCall) { ctorCall->callKind = CallKind::CALL_OBJECT_CREATION; ctorCall->ty = mirrorTy; } return ctorCall; } CJC_ABORT(); return nullptr; } bool IsSynthetic(const Node& node) { return node.astKind == ASTKind::CLASS_DECL && node.TestAttr(Attribute::COMPILER_ADD); } OwnedPtr Utils::CreateOptionMatch( OwnedPtr selector, std::function(VarDecl&)> someBranch, std::function()> noneBranch, Ptr ty) { auto curFile = selector->curFile; CJC_NULLPTR_CHECK(curFile); auto& optTy = *selector->ty; CJC_ASSERT(optTy.IsCoreOptionType()); auto optArgTy = optTy.typeArgs[0]; auto vp = CreateVarPattern(V_COMPILER, optArgTy); vp->curFile = curFile; vp->varDecl->curFile = curFile; auto& someArgVar = *vp->varDecl; auto somePattern = MakeOwnedNode(); somePattern->ty = selector->ty; somePattern->constructor = CreateOptionSomeRef(optArgTy); somePattern->patterns.emplace_back(std::move(vp)); somePattern->curFile = curFile; auto caseSome = CreateMatchCase(std::move(somePattern), someBranch(someArgVar)); // `case None => Java_CFFI_JavaEntityJobjectNull()` auto nonePattern = MakeOwnedNode(); nonePattern->constructor = CreateOptionNoneRef(optArgTy); nonePattern->ty = nonePattern->constructor->ty; nonePattern->curFile = curFile; auto caseNone = CreateMatchCase(std::move(nonePattern), noneBranch()); return WithinFile(CreateMatchExpr( std::move(selector), Nodes(std::move(caseSome), std::move(caseNone)), ty), curFile); } bool IsJArray(const Decl& decl) { if (!Is(decl)) { return false; } auto classLikeDecl = StaticCast(&decl); if (!decl.TestAttr(Attribute::JAVA_MIRROR)) { return false; } const auto packageName = INTEROP_JAVA_LANG_PACKAGE; if (decl.identifier.Val() != INTEROP_JARRAY_NAME || decl.fullPackageName != packageName) { return false; } auto attr = GetJavaMirrorAnnoAttr(*classLikeDecl); if (!attr) { return false; } return *attr == "[]"; } bool IsJArray(const Ty& ty) { auto classLikeTy = DynamicCast(&ty); if (!classLikeTy || !classLikeTy->commonDecl) { return false; } return IsJArray(*classLikeTy->commonDecl); } Ptr InteropLibBridge::FindGetTypeForTypeParamDecl(File& file) const { for (auto& decl : file.curPackage->files[0]->decls) { if (decl->identifier.Val() == GET_TYPE_FOR_TYPE_PARAMETER_FUNC_NAME) { return As(decl.get()); } } CJC_ABORT(); return nullptr; } Ptr InteropLibBridge::FindCStringToStringDecl() { for (auto& extend : typeManager.GetAllExtendsByTy(*TypeManager::GetCStringTy())) { for (auto& ex : extend->GetMemberDecls()) { if (ex->identifier == "toString") { return As(ex.get()); } } } CJC_ABORT(); return nullptr; } Ptr InteropLibBridge::FindStringEqualsDecl() { static auto& strDecl = utils.GetStringDecl(); for (auto& decl : strDecl.GetMemberDeclPtrs()) { if (decl->identifier == "==") { return As(decl.get()); } } CJC_ABORT(); return nullptr; } Ptr InteropLibBridge::FindStringStartsWithDecl() { static auto& strDecl = utils.GetStringDecl(); for (auto& decl : strDecl.GetMemberDeclPtrs()) { if (decl->identifier == "startsWith") { return As(decl.get()); } } CJC_ABORT(); return nullptr; } OwnedPtr InteropLibBridge::CreateGetTypeForTypeParameterCall(const Ptr genericParam) const { static auto getTypeForTypeParamDecl = FindGetTypeForTypeParamDecl(*genericParam->curFile); std::vector> args; auto refExpr = WithinFile(CreateRefExpr(*getTypeForTypeParamDecl), genericParam->curFile); refExpr->instTys.push_back(genericParam->ty); auto callExpr = CreateCallExpr(std::move(refExpr), std::move(args), getTypeForTypeParamDecl, getTypeForTypeParamDecl->funcBody->retType->ty, CallKind::CALL_INTRINSIC_FUNCTION); callExpr->curFile = genericParam->curFile; return callExpr; } OwnedPtr InteropLibBridge::CreateMatchByTypeArgument( const Ptr genericParam, std::map> typeToCaseMap, Ptr retTy, OwnedPtr defaultCase) { static auto strTy = utils.GetStringDecl().ty; static auto cStrToStringDecl = FindCStringToStringDecl(); static auto strEqualsDecl = FindStringEqualsDecl(); static auto strStartsWithDecl = FindStringStartsWithDecl(); static auto boolTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); auto file = genericParam->curFile; auto cStrToStringCall = MakeOwned(); auto cStrToStringMa = CreateMemberAccess(CreateGetTypeForTypeParameterCall(genericParam), "toString"); cStrToStringMa->ty = cStrToStringDecl->ty; cStrToStringMa->target = cStrToStringDecl; cStrToStringMa->callOrPattern = cStrToStringCall; std::vector> cStrToStringCallArgs; cStrToStringCall->ty = strTy; cStrToStringCall->resolvedFunction = cStrToStringDecl; cStrToStringCall->baseFunc = std::move(cStrToStringMa); cStrToStringCall->args = std::move(cStrToStringCallArgs); cStrToStringCall->callKind = CallKind::CALL_DECLARED_FUNCTION; cStrToStringCall->curFile = file; std::vector> cases; for (auto & [typeDesc, expr] : typeToCaseMap) { auto isOption = typeDesc.rfind(std::string(CORE_PACKAGE_NAME) + ":" + OPTION_NAME, 0) == 0; // starts_with actually auto caseCall = MakeOwned(); auto caseMa = CreateMemberAccess(ASTCloner::Clone(cStrToStringCall.get()), isOption ? "startsWith" : "=="); caseMa->ty = isOption ? strStartsWithDecl->ty : strEqualsDecl->ty; caseMa->target = isOption ? strStartsWithDecl : strEqualsDecl; caseMa->callOrPattern = caseCall; std::vector> caseCallArgs; caseCallArgs.emplace_back(CreateFuncArg(CreateLitConstExpr(LitConstKind::STRING, typeDesc, strTy))); caseCall->ty = boolTy; caseCall->resolvedFunction = isOption ? strStartsWithDecl : strEqualsDecl; caseCall->baseFunc = std::move(caseMa); caseCall->args = std::move(caseCallArgs); caseCall->callKind = CallKind::CALL_DECLARED_FUNCTION; caseCall->curFile = file; OwnedPtr patternForType = MakeOwned(); patternForType->ty = strTy; patternForType->literal = CreateLitConstExpr(LitConstKind::STRING, typeDesc, strTy); patternForType->operatorCallExpr = std::move(caseCall); cases.emplace_back(CreateMatchCase(std::move(patternForType), std::move(expr))); } cases.emplace_back(CreateMatchCase(MakeOwned(), std::move(defaultCase))); auto matchExpr = CreateMatchExpr(std::move(cStrToStringCall), std::move(cases), retTy); matchExpr->curFile = genericParam->curFile; return std::move(matchExpr); } void GenerateSyntheticClassMemberStubs( ClassDecl& synthetic, const MemberMap& interfaceMembers, const MemberMap& instanceMembers) { GenerateSyntheticClassAbstractMemberImplStubs(synthetic, interfaceMembers); GenerateSyntheticClassAbstractMemberImplStubs(synthetic, instanceMembers); } namespace { constexpr auto NATIVE_CONSTRUCTOR_MARKER_CLASS_NAME = "$$NativeConstructorMarker"; constexpr auto NATIVE_CONSTRUCTOR_MARKER_PACKAGE_NAME = "cangjie.lang.internal"; } // namespace std::string GetConstructorMarkerFQName() { std::string res; res += NATIVE_CONSTRUCTOR_MARKER_PACKAGE_NAME; res += "."; res += NATIVE_CONSTRUCTOR_MARKER_CLASS_NAME; return res; } std::string GetConstructorMarkerClassName() { return NATIVE_CONSTRUCTOR_MARKER_CLASS_NAME; } OwnedPtr CreateConstructorMarkerClassDecl() { OwnedPtr markerClassDecl = MakeOwned(); markerClassDecl->identifier = std::string(NATIVE_CONSTRUCTOR_MARKER_CLASS_NAME); markerClassDecl->fullPackageName = NATIVE_CONSTRUCTOR_MARKER_PACKAGE_NAME; return markerClassDecl; } } cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/AfterTypeCheck/Utils.h000066400000000000000000000213361510705540100252360ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares auxiliary methods for java interop implementation */ #ifndef CANGJIE_SEMA_NATIVE_FFI_JAVA_AFTER_TYPE_CHECK_UTILS #define CANGJIE_SEMA_NATIVE_FFI_JAVA_AFTER_TYPE_CHECK_UTILS #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" #include "../../../InheritanceChecker/StructInheritanceChecker.h" namespace Cangjie::Interop::Java { using namespace AST; enum class ArrayOperationKind: uint8_t { CREATE, GET, SET, GET_LENGTH }; class Utils final { public: Utils(ImportManager& importManager, TypeManager& typeManager); // Ty of `Option` Ptr GetOptionTy(Ptr ty); Ptr GetOptionDecl(); // `Option.None` OwnedPtr CreateOptionNoneRef(Ptr ty); // `Option.Some(expr)` OwnedPtr CreateOptionSomeCall(OwnedPtr expr, Ptr ty); // `Option.Some` OwnedPtr CreateOptionSomeRef(Ptr ty); // Decl of `java.lang.JObject` Ptr GetJObjectDecl(); // Decl of `java.lang.JString` Ptr GetJStringDecl(); // Decl of String StructDecl& GetStringDecl(); std::string GetJavaClassNormalizeSignature(const Ty& cjtype) const; std::string GetJavaTypeSignature(const Ty& cjtype); std::string GetJavaTypeSignature(Ty& retTy, const std::vector>& args); std::string GetJavaObjectTypeName(const Ty& ty); OwnedPtr CreateOptionMatch( OwnedPtr selector, std::function(VarDecl&)> someBranch, std::function()> noneBranch, Ptr ty); private: Ptr GetOptionSomeDecl(); Ptr GetOptionNoneDecl(); Ptr GetJavaLangDecl(const std::string& identifier); private: ImportManager& importManager; TypeManager& typeManager; }; /** * Returns javaref field of java mirror for the passed @JavaMirror/@JavaImpl declaration */ Ptr GetJavaRefField(ClassLikeDecl& mirror); /** * expr.javaRefGetter() */ OwnedPtr CreateJavaRefCall(OwnedPtr expr, FuncDecl& javaRefGetter); /** * expr.javaref */ OwnedPtr CreateJavaRefCall(OwnedPtr expr, VarDecl& javaref); /** * this.javaRefGetter(), where this is expr on mirrorLike */ OwnedPtr CreateJavaRefCall(ClassLikeDecl& mirrorLike, FuncDecl& javaRefGetter, Ptr curFile); /** * this.javaref, where this is expr on mirrorLike */ OwnedPtr CreateJavaRefCall(ClassLikeDecl& mirrorLike, VarDecl& javaref, Ptr curFile); /** * if mirrorLike is abstract class or interface, then: * expr.getJavaRef() * else: * expr.javaref */ OwnedPtr CreateJavaRefCall(OwnedPtr expr, ClassLikeDecl& mirrorLike); /** * if mirrorLike is abstract class or interface, then: * this.getJavaRef() * else: * this.javaref, * where this is on mirrorLike */ OwnedPtr CreateJavaRefCall(ClassLikeDecl& mirrorLike, Ptr curFile); /** * Acts like `CreateJavaRefCall(expr, *StaticCast(expr->ty))->commonDecl` */ OwnedPtr CreateJavaRefCall(OwnedPtr expr); /** * Is generated constructor of java mirror of kind: init(Java_CFFI_JavaEntity) */ bool IsGeneratedJavaMirrorConstructor(const FuncDecl& ctor); bool IsGeneratedJavaImplConstructor(const FuncDecl& ctor); /** * Recursively searches generated constructor of @JavaMirror in passed @JavaMirror/@JavaImpl `mirror` */ Ptr GetGeneratedJavaMirrorConstructor(ClassLikeDecl& mirror); /** * Searches generated constructor of current @JavaMirror */ Ptr GetGeneratedConstructorInMirror(ClassDecl& mirror); /** * Returns name of corresponding Java method or field with respect to @ForeignName annotation */ std::string GetJavaMemberName(const Decl& decl); /** * Returns true if has java fully-qualified name defined in annotation as a string literal */ bool HasPredefinedJavaName(const ClassLikeDecl& decl); /** * Returns fully-qualified name of the decl or fq-name specified in @JavaMirror as attribute, * which is suitable for specifying in JNI calls */ std::string GetJavaFQName(const Decl& decl); /** * Returns fully-qualified name of the decl or fq-name specified in @JavaMirror as attribute, * which is suitable for using Java source code: * - For specifying nested class '.' is used */ std::string GetJavaFQSourceCodeName(const ClassLikeDecl& decl); struct DestructedJavaClassName { /// Full package name std::optional packageName; /// Name of top level class std::string topLevelClassName; /// Full name of the class, starting from top level std::string fullClassName; }; /** * Returns parts of java class name */ DestructedJavaClassName DestructJavaClassName(const ClassLikeDecl& decl); /** * Returns package of the decl or package specified in @JavaMirror as attribute (omitting class name) */ std::string GetJavaPackage(const Decl& decl); /** * Mangles java name with JNI correspondence */ void MangleJNIName(std::string& name); /** * Performs mangling of `javaTy` with `mangler`. If `javaTy` is a mirrror or impl, then it returns `jobjectTy` */ std::string GetMangledJniInitCjObjectFuncName(const BaseMangler& mangler, const std::vector>& params, bool isGeneratedCtor); std::string GetMangledJniInitCjObjectFuncNameForEnum( const BaseMangler& mangler, const std::vector>& params, const std::string funcName); const Ptr GetSyntheticClass(const ImportManager& importManager, const ClassLikeDecl& cld); /** * Creates call of generated constructor (accepting java entity) * mirrorTy(entity) */ OwnedPtr CreateMirrorConstructorCall( const ImportManager& importManager, OwnedPtr entity, Ptr mirrorTy); bool IsJArray(const Decl& decl); bool IsJArray(const Ty& ty); bool IsMirror(const Ty& ty); bool IsImpl(const Decl& decl); bool IsImpl(const Ty& ty); bool IsCJMapping(const Ty& ty); ArrayOperationKind GetArrayOperationKind(Decl& decl); template std::vector> Nodes(OwnedPtr&&... args) { std::vector> nodes; (nodes.push_back(std::forward>(args)), ...); return nodes; } namespace { template void WrapArg(std::vector>* funcArgs, OwnedPtr&& e) { CJC_ASSERT(e); if (auto ptr = As(e.get())) { funcArgs->emplace_back(ptr); } else { funcArgs->push_back(CreateFuncArg(std::forward>(e))); } } } template OwnedPtr WithinFile(OwnedPtr node, Ptr curFile) { CJC_NULLPTR_CHECK(curFile); node->curFile = curFile; return node; } template OwnedPtr CreateCall(Ptr fd, Ptr curFile, OwnedPtr&&... args) { if (!fd) { return nullptr; } std::vector> funcArgs; (WrapArg(&funcArgs, std::forward>(args)), ...); auto funcTy = StaticCast(fd->ty); return CreateCallExpr(WithinFile(CreateRefExpr(*fd), curFile), std::move(funcArgs), fd, funcTy->retTy, CallKind::CALL_DECLARED_FUNCTION); } Ptr GetJavaRefField(ClassDecl& mirrorLike); /** * for interfaces and abstract classes */ Ptr GetJavaRefGetter(ClassLikeDecl& mirror); /** * For interface and abstract class mirror the synthetic class is generated to store $javaref field. */ bool IsSynthetic(const Node& node); bool IsJavaRefGetter(const Decl& fd); template Ptr FindFirstMemberDecl( const Decl& sourceDecl, const std::function& memberPredicate ) { for (auto& member : sourceDecl.GetMemberDecls()) { if (memberPredicate(*member)) { auto ptr = As(member); CJC_ASSERT(ptr); return Ptr(ptr); } } CJC_ABORT(); return nullptr; } void GenerateSyntheticClassMemberStubs( ClassDecl& synthetic, const MemberMap& interfaceMembers, const MemberMap& instanceMembers); /** * Returns FQ name of marker class for Cangjie side constructor of Java class */ std::string GetConstructorMarkerFQName(); std::string GetConstructorMarkerClassName(); OwnedPtr CreateConstructorMarkerClassDecl(); } // namespace Cangjie::Interop::Java #endif // CANGJIE_SEMA_NATIVE_FFI_JAVA_AFTER_TYPE_CHECK_UTILS cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/BeforeTypeCheck/000077500000000000000000000000001510705540100241215ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/BeforeTypeCheck/CMakeLists.txt000066400000000000000000000006561510705540100266700ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB SEMA_NATIVE_FFI_JAVA_BEFORE_TYPECHECK_SRC *.cpp) set(SEMA_NATIVE_FFI_JAVA_SRC ${SEMA_NATIVE_FFI_JAVA_SRC} ${SEMA_NATIVE_FFI_JAVA_BEFORE_TYPECHECK_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/BeforeTypeCheck/GenerateJavaMirror.cpp000066400000000000000000000020511510705540100303520ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "GenerateJavaMirror.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/ASTCasting.h" #include "cangjie/Utils/CastingTemplate.h" namespace Cangjie::Interop::Java { void PrepareTypeCheck(Package& pkg) { for (auto& file : pkg.files) { for (auto& decl : file->decls) { if (IsJObject(*decl, pkg.fullPackageName)) { if (auto cd = DynamicCast(decl.get())) { InsertJavaRefGetterStubWithBody(*cd); } } if (IsMirror(*decl)) { if (auto cd = DynamicCast(decl.get())) { InsertMirrorVarProp(*cd, Attribute::JAVA_MIRROR); } } } } } } cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/BeforeTypeCheck/GenerateJavaMirror.h000066400000000000000000000010431510705540100300170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_SEMA_NATIVE_FFI_JAVA_BEFORE_TYPECHECK_GENERATE_JAVA_MIRROR #define CANGJIE_SEMA_NATIVE_FFI_JAVA_BEFORE_TYPECHECK_GENERATE_JAVA_MIRROR #include "cangjie/AST/Node.h" namespace Cangjie::Interop::Java { using namespace AST; void PrepareTypeCheck(Package& pkg); } #endifcangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/CMakeLists.txt000066400000000000000000000010061510705540100236540ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB SEMA_NATIVE_FFI_JAVA_SRC *.cpp) add_subdirectory(BeforeTypeCheck) add_subdirectory(TypeCheck) add_subdirectory(AfterTypeCheck) add_subdirectory(JavaCodeGenerator) set(SEMA_NATIVE_FFI_SRC ${SEMA_NATIVE_FFI_SRC} ${SEMA_NATIVE_FFI_JAVA_SRC} PARENT_SCOPE) cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/JavaCodeGenerator/000077500000000000000000000000001510705540100244425ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/JavaCodeGenerator/AbstractSourceCodeGenerator.cpp000066400000000000000000000024641510705540100325420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements Java class generation. */ #include "AbstractSourceCodeGenerator.h" namespace Cangjie::Interop { AbstractSourceCodeGenerator::AbstractSourceCodeGenerator(const std::string& outputFilePath) : outputFilePath(outputFilePath) { } AbstractSourceCodeGenerator::AbstractSourceCodeGenerator( const std::string& outputFolderPath, const std::string& outputFileName) : outputFilePath(FileUtil::JoinPath(outputFolderPath, outputFileName)) { } void AbstractSourceCodeGenerator::Generate() { ConstructResult(); WriteToOutputFile(); } bool AbstractSourceCodeGenerator::WriteToOutputFile() { return FileUtil::WriteToFile(outputFilePath, res); } void AbstractSourceCodeGenerator::AddWithIndent(const std::string& indent, const std::string& s) { res += indent; res += s; res += "\n"; } const std::string AbstractSourceCodeGenerator::TAB = " "; const std::string AbstractSourceCodeGenerator::TAB2 = AbstractSourceCodeGenerator::TAB + AbstractSourceCodeGenerator::TAB; } // namespace Cangjie::Interopcangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/JavaCodeGenerator/AbstractSourceCodeGenerator.h000066400000000000000000000037631510705540100322120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares class for java code generation. */ #ifndef CANGJIE_SEMA_JAVA_ABSTRACT_GENERATOR #define CANGJIE_SEMA_JAVA_ABSTRACT_GENERATOR #include #include #include "cangjie/AST/Types.h" #include "cangjie/Utils/FileUtil.h" namespace Cangjie::Interop { using namespace Cangjie::AST; class AbstractSourceCodeGenerator { public: explicit AbstractSourceCodeGenerator(const std::string& outputFilePath); AbstractSourceCodeGenerator(const std::string& outputFolderPath, const std::string& outputFileName); virtual ~AbstractSourceCodeGenerator() = default; void Generate(); protected: std::string res; static const std::string TAB; static const std::string TAB2; template static std::string Join(const Container& container, const std::string& delimiter, const std::function& transformer) { using E = std::decay_t; using ValueType = std::decay_t; static_assert( std::is_same_v, "Transformer argument must have the same type as container's element."); std::string result = ""; bool isFirst = true; for (const auto& elem : container) { if (!isFirst) { result += delimiter; } isFirst = false; result += transformer(elem); } return result; } virtual void ConstructResult() = 0; void AddWithIndent(const std::string& indent, const std::string& s); private: const std::string outputFilePath; bool WriteToOutputFile(); }; } // namespace Cangjie::Interop #endif // CANGJIE_SEMA_JAVA_ABSTRACT_GENERATOR cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/JavaCodeGenerator/CMakeLists.txt000066400000000000000000000006521510705540100272050ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB SEMA_NATIVE_FFI_JAVA_CODE_GENERATOR_SRC *.cpp) set(SEMA_NATIVE_FFI_JAVA_SRC ${SEMA_NATIVE_FFI_JAVA_SRC} ${SEMA_NATIVE_FFI_JAVA_CODE_GENERATOR_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/JavaCodeGenerator/JavaSourceCodeGenerator.cpp000066400000000000000000000661641510705540100316670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements Java class generation. */ #include #include "cangjie/AST/Match.h" #include "cangjie/Utils/FileUtil.h" #include "NativeFFI/Java/AfterTypeCheck/JavaDesugarManager.h" #include "NativeFFI/Java/AfterTypeCheck/Utils.h" #include "cangjie/Utils/StdUtils.h" #include "JavaSourceCodeGenerator.h" namespace { using namespace Cangjie::AST; using namespace Cangjie::Interop::Java; using namespace Cangjie::Native::FFI; using TokenKind = Cangjie::TokenKind; constexpr auto JAVA_PACKAGE = "package"; constexpr auto JAVA_PUBLIC = "public"; constexpr auto JAVA_PRIVATE = "private"; constexpr auto JAVA_SEMICOLON = ";"; constexpr auto JAVA_COMMA = ","; constexpr auto JAVA_WHITESPACE = " "; constexpr auto JAVA_BOOLEAN = "boolean"; constexpr auto JAVA_SPECIAL_PARAM_NAME = "__init__"; bool IsFuncDeclAndNotConstructor(OwnedPtr& declPtr) { return declPtr->astKind == ASTKind::FUNC_DECL && !declPtr->TestAttr(Attribute::CONSTRUCTOR); } std::string GetModifier(Decl* decl) { if (decl->TestAttr(Attribute::PUBLIC)) { return "public "; } else { return ""; } } bool NeedExtraFinalModifier(const Decl& declArg) { if (declArg.TestAttr(Attribute::JAVA_CJ_MAPPING) && declArg.astKind == ASTKind::STRUCT_DECL) { return true; } return false; } } // namespace namespace Cangjie::Interop::Java { JavaSourceCodeGenerator::JavaSourceCodeGenerator( Decl* decl, const BaseMangler& mangler, const std::string& outputFilePath, std::string cjLibName) : AbstractSourceCodeGenerator(outputFilePath), decl(decl), cjLibName(std::move(cjLibName)), mangler(mangler) { } JavaSourceCodeGenerator::JavaSourceCodeGenerator(Decl* decl, const BaseMangler& mangler, const std::optional& outputFolderPath, const std::string& outputFileName, std::string cjLibName) : AbstractSourceCodeGenerator( outputFolderPath.value_or(JavaSourceCodeGenerator::DEFAULT_OUTPUT_DIR), outputFileName), decl(decl), cjLibName(std::move(cjLibName)), mangler(mangler) { } bool JavaSourceCodeGenerator::IsDeclAppropriateForGeneration(const Decl& declArg) { return (IsImpl(declArg) && (declArg.astKind == ASTKind::CLASS_DECL || declArg.astKind == ASTKind::INTERFACE_DECL)) || IsCJMapping(declArg); } void JavaSourceCodeGenerator::ConstructResult() { if (decl->TestAnyAttr(Attribute::IS_BROKEN, Attribute::HAS_BROKEN)) { return; } AddClassDeclaration(); AddLoadLibrary(); AddSelfIdField(); AddProperties(); AddConstructors(); AddMethods(); AddNativeDeleteCJObject(); AddFinalize(); AddEndClassParenthesis(); AddHeader(); // after all because during AST traverse the 'imports' field is accumulating import declarations } void JavaSourceCodeGenerator::AddHeader() { std::string curPackageName = GetJavaPackage(*decl); std::string header; if (!curPackageName.empty()) { header += JAVA_PACKAGE; header += JAVA_WHITESPACE; header += curPackageName; header += JAVA_SEMICOLON; header += "\n\n"; } const std::function& mapper = [](const std::string& s) { return "import " + s + ";"; }; header += Join(imports, "\n", mapper) + "\n"; header += "import static cangjie.lang.LibraryLoader.loadLibrary;\n"; header += "import " + GetConstructorMarkerFQName() + ";\n"; header += "\n"; res = std::move(header) + res; } std::string JavaSourceCodeGenerator::MapCJTypeToJavaType( const Ptr ty, std::set* javaImports, const std::string* curPackageName, bool isNativeMethod) { if (ty->IsCoreOptionType()) { return MapCJTypeToJavaType(ty->typeArgs[0], javaImports, curPackageName, isNativeMethod); } std::string javaType = ""; auto declTy = Ty::GetDeclOfTy(ty); switch (ty->kind) { case TypeKind::TYPE_FLOAT64: javaType = "double"; break; case TypeKind::TYPE_FLOAT32: javaType = "float"; break; case TypeKind::TYPE_INT64: javaType = "long"; break; case TypeKind::TYPE_INT32: javaType = "int"; break; case TypeKind::TYPE_INT16: javaType = "short"; break; case TypeKind::TYPE_UINT16: javaType = "char"; break; case TypeKind::TYPE_INT8: javaType = "byte"; break; case TypeKind::TYPE_BOOLEAN: javaType = JAVA_BOOLEAN; break; case TypeKind::TYPE_UNIT: javaType = "void"; break; case TypeKind::TYPE_INTERFACE: case TypeKind::TYPE_CLASS: if (IsJArray(*declTy)) { return MapCJTypeToJavaType(ty->typeArgs[0], javaImports, curPackageName) + "[]"; } else if (isNativeMethod && declTy && IsCJMapping(*declTy)) { return "long"; } javaType = AddImport(ty, javaImports, curPackageName); break; case TypeKind::TYPE_STRUCT: case TypeKind::TYPE_ENUM: if (isNativeMethod && declTy && IsCJMapping(*declTy)) { return "long"; } javaType = ty->name; break; default: if (ty->name == "String") { javaType = "String"; } } CJC_ASSERT(!javaType.empty()); return javaType; } std::string JavaSourceCodeGenerator::MapCJTypeToJavaType(const OwnedPtr& type, std::set* javaImports, const std::string* curPackageName, bool isNativeMethod) { CJC_ASSERT(type && type->ty); return MapCJTypeToJavaType(type->ty, javaImports, curPackageName, isNativeMethod); } std::string JavaSourceCodeGenerator::MapCJTypeToJavaType(const OwnedPtr& param, std::set* javaImports, const std::string* curPackageName, bool isNativeMethod) { CJC_ASSERT(param && param->type && param->type->ty); return MapCJTypeToJavaType(param->type->ty, javaImports, curPackageName, isNativeMethod); } void JavaSourceCodeGenerator::AddClassDeclaration() { std::string modifier = NeedExtraFinalModifier(*decl) ? "final " : ""; modifier += GetModifier(decl); res += modifier; res += "class " + decl->identifier.Val(); if (auto classDecl = As(decl)) { Ptr superClassPtr = classDecl->GetSuperClassDecl(); bool isClassInheritedFromClass = !IsJObject(*superClassPtr); std::set> implementedInterfacesPtrs = classDecl->GetSuperInterfaceTys(); size_t implementedInterfacesCnt = implementedInterfacesPtrs.size(); if (isClassInheritedFromClass) { res += " extends "; res += MapCJTypeToJavaType(superClassPtr->ty, &imports, &classDecl->fullPackageName); } if (implementedInterfacesCnt > 0) { res += " implements "; std::set* imp = &imports; const std::string* package = &classDecl->fullPackageName; const std::function)>& transformer = [imp, package](Ptr p) { return MapCJTypeToJavaType(p, imp, package); }; res += Join(implementedInterfacesPtrs, ", ", transformer); } } res += " {\n"; } std::string JavaSourceCodeGenerator::AddImport( Ptr ty, std::set* javaImports, const std::string* curPackageName) { std::string javaType = ""; if (auto classlikety = DynamicCast(ty)) { if (auto clDecl = As(classlikety->commonDecl)) { auto [package, topLevelClassName, fullClassName] = Interop::Java::DestructJavaClassName(*clDecl); if (!package) { return fullClassName; } if (*package != *curPackageName && *package != IGNORE_IMPORT) { javaImports->insert(*package + "." + topLevelClassName); } javaType = fullClassName; } } return javaType; } void JavaSourceCodeGenerator::AddLoadLibrary() { AddWithIndent(TAB, "static {"); AddWithIndent(TAB2, "loadLibrary(\"" + cjLibName + "\");"); AddWithIndent(TAB, "}\n"); } void JavaSourceCodeGenerator::AddSelfIdField() { AddWithIndent(TAB, "long self;\n"); } void JavaSourceCodeGenerator::AddProperties() { for (OwnedPtr& declPtr : decl->GetMemberDecls()) { if (declPtr->astKind == ASTKind::PROP_DECL && !declPtr->TestAttr(Attribute::COMPILER_ADD)) { const PropDecl& propDecl = *StaticAs(declPtr.get()); const OwnedPtr& funcDecl = propDecl.getters[0]; const std::string type = MapCJTypeToJavaType(funcDecl->funcBody->retType, &imports, &decl->fullPackageName, false); std::string varDecl = GetJavaMemberName(propDecl); std::string varDeclSuffix = varDecl; varDeclSuffix[0] = static_cast(toupper(varDeclSuffix[0])); std::string getSignature = "get" + varDeclSuffix; // add getter AddWithIndent(TAB, "public " + type + " " + getSignature + "() {"); AddWithIndent(TAB2, "return " + getSignature + "Impl(this.self);"); AddWithIndent(TAB, "}\n"); AddWithIndent(TAB, "public native " + type + " " + getSignature + "Impl(long self);\n"); // add setter if (!propDecl.setters.empty()) { std::string setSignature = "set" + varDeclSuffix; AddWithIndent(TAB, "public void " + setSignature + "(" + type + " " + varDecl + ") {"); AddWithIndent(TAB2, setSignature + "Impl(this.self, " + varDecl + ");"); AddWithIndent(TAB, "}\n"); AddWithIndent( TAB, "public native void " + setSignature + "Impl(long self, " + type + " " + varDecl + ");\n"); } } } } std::string JavaSourceCodeGenerator::GenerateFuncParams( const std::vector>& params, bool isNativeMethod) { std::set* imp = &imports; const std::string* curPackage = &decl->fullPackageName; std::function& ptr)> mapper = [imp, curPackage, isNativeMethod]( const OwnedPtr& cur) { return MapCJTypeToJavaType(cur, imp, curPackage, isNativeMethod) + " " + cur->identifier.Val(); }; return GenerateParams(params, mapper); } std::string JavaSourceCodeGenerator::GenerateFuncParamLists( const std::vector>& paramLists, bool isNativeMethod) { std::set* imp = &imports; const std::string* curPackage = &decl->fullPackageName; std::function& ptr)> mapper = [imp, curPackage, isNativeMethod]( const OwnedPtr& cur) { CJC_ASSERT(cur && cur->type && cur->type->ty); std::string res = MapCJTypeToJavaType(cur, imp, curPackage, isNativeMethod) + " " + cur->identifier.Val(); if (auto ty = Ty::GetDeclOfTy(cur->type->ty)) { bool castToId = isNativeMethod && IsCJMapping(*ty); res += castToId ? "Id" : ""; } return res; }; return GenerateParamLists(paramLists, mapper); } std::string JavaSourceCodeGenerator::GenerateConstructorForEnumDecl(const OwnedPtr& ctor) { std::string declaration; declaration.append(JAVA_PUBLIC); declaration.append(JAVA_WHITESPACE); declaration.append("static"); declaration.append(JAVA_WHITESPACE); declaration.append(ctor.get()->outerDecl->identifier.Val()); declaration.append(JAVA_WHITESPACE); std::string params; auto func = As(ctor.get()); if (func && func->funcBody) { declaration.append(ctor.get()->identifier.Val() + "("); params += GenerateFuncParamLists(func->funcBody->paramLists); declaration += params; declaration += ") {"; } else { declaration.append(ctor.get()->identifier.Val()); } return declaration; } std::string JavaSourceCodeGenerator::GenerateConstructorDecl(const FuncDecl& func, bool isForCangjie) { std::string declaration; if (!isForCangjie && func.TestAttr(Attribute::PUBLIC)) { declaration += JAVA_PUBLIC; declaration += JAVA_WHITESPACE; } else if (isForCangjie) { declaration += JAVA_PRIVATE; declaration += JAVA_WHITESPACE; } declaration.append(decl->identifier.Val() + "("); std::string params; if (func.funcBody) { params += GenerateFuncParamLists(func.funcBody->paramLists); if (isForCangjie) { if (!params.empty()) { params += JAVA_COMMA; params += JAVA_WHITESPACE; } params += GetConstructorMarkerClassName(); params += JAVA_WHITESPACE; params += JAVA_SPECIAL_PARAM_NAME; } } declaration += params; declaration += ")"; return declaration; } std::string JavaSourceCodeGenerator::GenerateParams(const std::vector>& params, const std::function& ptr)>& transform) { return Join(params, ", ", transform); } std::string JavaSourceCodeGenerator::GenerateParamLists(const std::vector>& paramLists, const std::function& ptr)>& transform) { if (!paramLists.empty() && paramLists[0]) { return GenerateParams(paramLists[0]->params, transform); } return ""; } /** * The important function to generate super call argument and native declaration. * * @param arg is the super argument with desugared native func * @param params is original constructor parameters */ std::pair JavaSourceCodeGenerator::GenNativeSuperArgCall( const FuncArg& arg, const std::vector>& params) { CJC_ASSERT(arg.expr->desugarExpr->astKind == ASTKind::REF_EXPR); auto ref = StaticCast(arg.expr->desugarExpr.get()); CJC_NULLPTR_CHECK(ref->ref.target); CJC_ASSERT(ref->ref.target->astKind == ASTKind::FUNC_DECL); auto nativeFn = StaticCast(ref->ref.target); std::string id = nativeFn->identifier.Val(); const std::string keyword = "_super"; size_t pos = id.rfind(keyword); CJC_ASSERT(pos != std::string::npos); // The native id should be `superC0A0...` id = id.substr(pos + 1); // Decode using parameter ids const std::string sep = "P"; std::vector parts = Cangjie::Utils::SplitString(id.substr(keyword.length()), sep); std::vector args; std::vector nativeParams; auto mpTy = [this](const Ptr ty) { return MapCJTypeToJavaType(ty, &imports, &decl->fullPackageName); }; // Skip first part. for (size_t i = 1; i < parts.size(); i++) { auto pid = Cangjie::Stoi(parts[i]); CJC_ASSERT(pid.has_value()); auto index = pid.value(); auto& pname = params[index]->identifier.Val(); args.push_back(pname); nativeParams.push_back(mpTy(params[index]->ty) + " " + pname); } std::string superCall = id + "(" + Cangjie::Utils::JoinStrings(args, ", ") + ")"; std::string nativeFnDecl = "public static native " + mpTy(arg.ty) + " " + id + "(" + Cangjie::Utils::JoinStrings(nativeParams, ", ") + ");"; return std::make_pair(superCall, nativeFnDecl); } // Generate super call and collection native func declaration. std::string JavaSourceCodeGenerator::GenerateSuperCall( const CallExpr& call, const std::vector>& params, std::vector& nativeArgs) { // generate logic std::vector args; for (auto& arg : call.args) { if (arg->expr->desugarExpr) { auto [superCall, superNative] = GenNativeSuperArgCall(*arg, params); args.push_back(superCall); nativeArgs.push_back(superNative); } else { args.push_back(arg->expr->ToString()); } } return "super(" + Cangjie::Utils::JoinStrings(args, ", ") + ");"; } std::string JavaSourceCodeGenerator::GenerateConstructorSuperCall( const FuncBody& body, std::vector& nativeArgs) { if (!body.body) { return ""; } std::string superCall; auto& params = body.paramLists[0]->params; // Generate super call auto& block = body.body->body; for (auto it = block.begin(); it != block.end(); it++) { if ((*it)->astKind != ASTKind::CALL_EXPR) { continue; } auto call = StaticCast(it->get()); if (call->callKind == CallKind::CALL_SUPER_FUNCTION && call->TestAttr(Attribute::UNREACHABLE, Attribute::JAVA_MIRROR)) { superCall = GenerateSuperCall(*call, params, nativeArgs); // remove after generating java code. block.erase(it); break; } } return superCall; } void JavaSourceCodeGenerator::AddConstructor(const FuncDecl& ctor, const std::string& superCall, bool isForCangjie) { AddWithIndent(TAB, GenerateConstructorDecl(ctor, isForCangjie) + " {"); // super call if (!superCall.empty()) { AddWithIndent(TAB2, superCall); } if (!isForCangjie) { // initCJObject auto& params = ctor.funcBody->paramLists[0]->params; std::string selfInit = "self = " + GetMangledJniInitCjObjectFuncName(mangler, params, false) + "("; if (ctor.funcBody) { selfInit += GenerateParamLists( ctor.funcBody->paramLists, [](const OwnedPtr& p) { return p->identifier.Val(); }); } selfInit += ");"; AddWithIndent(TAB2, selfInit); } AddWithIndent(TAB, "}\n"); } void JavaSourceCodeGenerator::AddConstructor(const FuncDecl& ctor) { std::vector nativeArgs; std::string superCall = GenerateConstructorSuperCall(*ctor.funcBody, nativeArgs); auto& params = ctor.funcBody->paramLists[0]->params; // Java side constructor AddConstructor(ctor, superCall, false); // For Cangjie side constructor with marker. AddConstructor(ctor, superCall, true); // Add native init AddNativeInitCJObject(params); // Add native super args for (auto& fn : nativeArgs) { AddWithIndent(TAB, fn); } } void JavaSourceCodeGenerator::AddAllCtorsForCJMappingEnum(const EnumDecl& enumDecl) { const std::string enumName = enumDecl.identifier; for (auto& constructor : enumDecl.constructors) { std::string declaration = GenerateConstructorForEnumDecl(constructor); if (constructor->astKind == ASTKind::FUNC_DECL) { auto funcDecl = As(constructor.get()); CJC_NULLPTR_CHECK(funcDecl); auto funcName = funcDecl->identifier; auto& params = funcDecl->funcBody->paramLists[0]->params; AddWithIndent(TAB, declaration); auto nativeFuncName = GetMangledJniInitCjObjectFuncNameForEnum(mangler, params, funcName); std::string selfInit = "return new " + enumName + "(" + nativeFuncName + "("; if (funcDecl->funcBody) { selfInit += GenerateParamLists(funcDecl->funcBody->paramLists, [](const OwnedPtr& p) { std::string res = p->identifier.Val(); if (auto ty = Ty::GetDeclOfTy(p->type->ty)) { res += IsCJMapping(*ty) ? ".self" : ""; } return res; }); } selfInit += "));"; AddWithIndent(TAB2, selfInit); AddWithIndent(TAB, "}\n"); // Add Native function auto strParams = GenerateFuncParams(params, true); std::string signature = "private static native long " + nativeFuncName + "(" + strParams + ");\n"; AddWithIndent(TAB, signature); } else if (constructor->astKind == ASTKind::VAR_DECL) { auto varDecl = As(constructor.get()); CJC_NULLPTR_CHECK(varDecl); auto varName = varDecl->identifier; auto nativeFuncName = varName + "initCJObject"; auto selfInit = declaration + " = new " + enumName + "(" + nativeFuncName + "());\n"; AddWithIndent(TAB, selfInit); std::string signature = "private static native long " + nativeFuncName + "();\n"; AddWithIndent(TAB, signature); } } } void JavaSourceCodeGenerator::AddConstructors() { for (OwnedPtr& declPtr : decl->GetMemberDecls()) { auto fd = As(declPtr.get()); if (!fd || fd->TestAttr(Attribute::PRIVATE) || !fd->TestAttr(Attribute::CONSTRUCTOR)) { continue; } if (IsCJMapping(*decl) && !fd->TestAttr(Attribute::PUBLIC)) { continue; } const auto& funcDecl = *fd; if (IsGeneratedJavaImplConstructor(funcDecl)) { continue; } AddConstructor(*fd); } if (IsCJMapping(*decl)) { if (auto enumDecl = As(decl)) { AddPrivateCtorForCJMappringEnum(); AddAllCtorsForCJMappingEnum(*enumDecl); } else { AddPrivateCtorForCJMappring(); } } } void JavaSourceCodeGenerator::AddInstanceMethod(const FuncDecl& funcDecl) { auto& params = funcDecl.funcBody->paramLists[0]->params; auto funcIdentifier = GetJavaMemberName(funcDecl); auto mangledNativeName = GetMangledMethodName(mangler, params, funcIdentifier); if (funcDecl.TestAttr(Attribute::OVERRIDE)) { AddWithIndent(TAB, "@Override"); } const std::string retType = MapCJTypeToJavaType(funcDecl.funcBody->retType, &imports, &decl->fullPackageName); std::string methodSignature = "public " + retType + " "; methodSignature += funcIdentifier + "("; std::string argsWithTypes = GenerateFuncParamLists(funcDecl.funcBody->paramLists, false); methodSignature += argsWithTypes; methodSignature += ") {"; AddWithIndent(TAB, methodSignature); std::string args = "this.self"; const std::function&)>& mapper = [](const OwnedPtr& p) { CJC_ASSERT(p && p->type && p->type->ty); std::string res = p->identifier.Val(); if (auto ty = Ty::GetDeclOfTy(p->type->ty)) { res += IsCJMapping(*ty) ? ".self" : ""; } return res; }; std::string paramList = Join(params, ", ", mapper); if (!paramList.empty()) { args += ", "; args += paramList; } auto funcCall = mangledNativeName + "(" + args + ");"; if (retType != "void") { funcCall = "return " + funcCall; } AddWithIndent(TAB2, funcCall); AddWithIndent(TAB, "}\n"); argsWithTypes = GenerateFuncParamLists(funcDecl.funcBody->paramLists, true); auto comma = (argsWithTypes.empty()) ? "" : ", "; AddWithIndent(TAB, "public native " + retType + " " + mangledNativeName + "(long self" + comma + argsWithTypes + ");\n"); } void JavaSourceCodeGenerator::AddStaticMethod(const FuncDecl& funcDecl) { auto& params = funcDecl.funcBody->paramLists[0]->params; auto funcIdentifier = GetJavaMemberName(funcDecl); auto mangledNativeName = GetMangledMethodName(mangler, params, funcIdentifier); const std::string retType = MapCJTypeToJavaType(funcDecl.funcBody->retType, &imports, &decl->fullPackageName); std::string argsWithTypes = GenerateFuncParamLists(funcDecl.funcBody->paramLists, false); const std::function&)>& mapper = [](const OwnedPtr& p) { CJC_ASSERT(p && p->type && p->type->ty); std::string res = p->identifier.Val(); if (auto ty = Ty::GetDeclOfTy(p->type->ty)) { res += IsCJMapping(*ty) ? ".self" : ""; } return res; }; std::string paramList = Join(params, ", ", mapper); if (mangledNativeName != funcIdentifier) { AddWithIndent(TAB, "public static " + retType + " " + funcIdentifier + "(" + argsWithTypes + ") {"); auto funcCall = mangledNativeName + "(" + paramList + ");"; if (retType != "void") { funcCall = "return " + funcCall; } AddWithIndent(TAB2, funcCall); AddWithIndent(TAB, "}\n"); } argsWithTypes = GenerateFuncParamLists(funcDecl.funcBody->paramLists, true); std::string methodSignature = "public static native " + retType + " "; methodSignature += mangledNativeName + "("; methodSignature += argsWithTypes; methodSignature += ");\n"; AddWithIndent(TAB, methodSignature); } void JavaSourceCodeGenerator::AddMethods() { for (OwnedPtr& declPtr : decl->GetMemberDecls()) { if (IsCJMapping(*decl) && !declPtr->TestAttr(Attribute::PUBLIC)) { continue; } if (!declPtr->TestAttr(Attribute::PRIVATE) && IsFuncDeclAndNotConstructor(declPtr)) { const FuncDecl& funcDecl = *StaticAs(declPtr.get()); if (funcDecl.funcBody && funcDecl.funcBody->retType) { if (funcDecl.TestAttr(Attribute::STATIC)) { AddStaticMethod(funcDecl); } else { AddInstanceMethod(funcDecl); } } } } } void JavaSourceCodeGenerator::AddEndClassParenthesis() { res += "}\n"; } /** * @param params is original constructor parameters */ void JavaSourceCodeGenerator::AddNativeInitCJObject(const std::vector> ¶ms) { auto name = GetMangledJniInitCjObjectFuncName(mangler, params, false); auto strParams = GenerateFuncParams(params, true); std::string signature = "public native long " + name + "(" + strParams + ");\n"; AddWithIndent(TAB, signature); } void JavaSourceCodeGenerator::AddNativeDeleteCJObject() { AddWithIndent(TAB, "public native void deleteCJObject(long self);\n"); } void JavaSourceCodeGenerator::AddFinalize() { AddWithIndent(TAB, "@Override"); AddWithIndent(TAB, "public void finalize() {"); AddWithIndent(TAB2, "deleteCJObject(this.self);"); AddWithIndent(TAB, "}"); } void JavaSourceCodeGenerator::AddPrivateCtorForCJMappring() { std::string signature = "private " + decl->identifier.Val() + " (long id, " + GetConstructorMarkerClassName() + " __init__) {"; AddWithIndent(TAB, signature); AddWithIndent(TAB2, "self = id;"); AddWithIndent(TAB, "}\n"); } void JavaSourceCodeGenerator::AddPrivateCtorForCJMappringEnum() { std::string signature = "private " + decl->identifier.Val() + " (long id) {"; AddWithIndent(TAB, signature); AddWithIndent(TAB2, "self = id;"); AddWithIndent(TAB, "}\n"); } const std::string JavaSourceCodeGenerator::DEFAULT_OUTPUT_DIR = "java-gen"; const std::string JavaSourceCodeGenerator::IGNORE_IMPORT = "java.lang"; } // namespace Cangjie::Interop::Java cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/JavaCodeGenerator/JavaSourceCodeGenerator.h000066400000000000000000000102351510705540100313200ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares class for java code generation. */ #ifndef CANGJIE_SEMA_JAVA_CODE_GENERATOR #define CANGJIE_SEMA_JAVA_CODE_GENERATOR #include #include "AbstractSourceCodeGenerator.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Types.h" #include "cangjie/Mangle/BaseMangler.h" namespace Cangjie::Interop::Java { using namespace AST; class JavaSourceCodeGenerator : public AbstractSourceCodeGenerator { public: JavaSourceCodeGenerator( Decl* decl, const BaseMangler& mangler, const std::string& outputFilePath, std::string cjLibName); JavaSourceCodeGenerator(Decl* decl, const BaseMangler& mangler, const std::optional& folderPath, const std::string& outputFileName, std::string cjLibName); static bool IsDeclAppropriateForGeneration(const Decl& declArg); private: static const std::string DEFAULT_OUTPUT_DIR; static const std::string IGNORE_IMPORT; static std::string AddImport(Ptr ty, std::set* javaImports, const std::string* curPackageName); static std::string MapCJTypeToJavaType(const Ptr ty, std::set* javaImports, const std::string* curPackageName, bool isNativeMethod = false); static std::string MapCJTypeToJavaType(const OwnedPtr& type, std::set* javaImports, const std::string* curPackageName, bool isNativeMethod = false); static std::string MapCJTypeToJavaType(const OwnedPtr& param, std::set* javaImports, const std::string* curPackageName, bool isNativeMethod = false); static std::string GenerateParams(const std::vector>& params, const std::function& ptr)>& transform); static std::string GenerateParamLists(const std::vector>& paramLists, const std::function& ptr)>& transform); Decl* decl; std::set imports; const std::string cjLibName; const BaseMangler& mangler; std::string GenerateFuncParams(const std::vector>& params, bool isNativeMethod = false); std::string GenerateFuncParamLists( const std::vector>& paramLists, bool isNativeMethod = false); void ConstructResult() override; void AddClassDeclaration(); void AddLoadLibrary(); void AddSelfIdField(); void AddProperties(); std::string GenerateConstructorDecl(const FuncDecl& func, bool isForCangjie); // Generate all constructors for each ctor in Enum. std::string GenerateConstructorForEnumDecl(const OwnedPtr& ctor); // Generate super call argument and native declaration. std::pair GenNativeSuperArgCall( const FuncArg& arg, const std::vector>& params); // Generate super call and collection native func declaration. std::string GenerateSuperCall( const CallExpr& call, const std::vector>& params, std::vector& nativeArgs); std::string GenerateConstructorSuperCall(const FuncBody& body, std::vector& nativeArgs); void AddConstructor(const FuncDecl& ctor, const std::string& superCall, bool isForCangjie); // Generate constructors and native funcs. void AddConstructor(const FuncDecl& ctor); void AddConstructors(); void AddAllCtorsForCJMappingEnum(const EnumDecl& enumDecl); void AddInstanceMethod(const FuncDecl& funcDecl); void AddStaticMethod(const FuncDecl& funcDecl); void AddMethods(); void AddEndClassParenthesis(); void AddNativeInitCJObject(const std::vector> ¶ms); void AddNativeDeleteCJObject(); void AddFinalize(); void AddHeader(); void AddPrivateCtorForCJMappring(); void AddPrivateCtorForCJMappringEnum(); }; } // namespace Cangjie::Interop::Java #endif // CANGJIE_SEMA_JAVA_CODE_GENERATOR cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/TypeCheck/000077500000000000000000000000001510705540100227765ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/TypeCheck/CMakeLists.txt000066400000000000000000000006411510705540100255370ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB SEMA_NATIVE_FFI_JAVA_TYPECHECK_SRC *.cpp) set(SEMA_NATIVE_FFI_JAVA_SRC ${SEMA_NATIVE_FFI_JAVA_SRC} ${SEMA_NATIVE_FFI_JAVA_TYPECHECK_SRC} PARENT_SCOPE) cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/TypeCheck/InheritanceChecker.cpp000066400000000000000000000131261510705540100272230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "InheritanceChecker.h" #include "NativeFFI/Java/AfterTypeCheck/Utils.h" #include "cangjie/AST/Clone.h" namespace Cangjie::Interop::Java { namespace { Ptr GetForeignNameAnnotation(const Decl& decl) { auto it = std::find_if(decl.annotations.begin(), decl.annotations.end(), [](const auto& anno) { return anno->kind == AnnotationKind::FOREIGN_NAME; }); return it != decl.annotations.end() ? it->get() : nullptr; } std::string GetAnnoValue(Ptr anno) { CJC_ASSERT(anno); CJC_ASSERT(anno->args.size() == 1); auto litExpr = DynamicCast(anno->args[0]->expr.get()); CJC_ASSERT(litExpr); return litExpr->stringValue; } void DiagConflictingForeignName( DiagnosticEngine& diag, const Decl& declWithAnno, const Decl& otherDecl, const Decl& checkingDecl) { auto anno = GetForeignNameAnnotation(declWithAnno); CJC_ASSERT(anno); auto builder = [&diag, &anno, &declWithAnno]() { if (!anno->TestAttr(Attribute::COMPILER_ADD)) { auto declWithAnnoRange = MakeRange(anno->GetBegin(), declWithAnno.identifier.End()); return diag.DiagnoseRefactor(DiagKindRefactor::sema_foreign_name_conflicting_annotation, declWithAnno, declWithAnnoRange, declWithAnno.identifier); } else { return diag.DiagnoseRefactor(DiagKindRefactor::sema_foreign_name_conflicting_derived_annotation, declWithAnno, MakeRange(declWithAnno.identifier), declWithAnno.identifier, GetAnnoValue(anno)); } }(); auto otherAnno = GetForeignNameAnnotation(otherDecl); if (otherAnno && !otherAnno->TestAttr(Attribute::COMPILER_ADD)) { auto otherDeclRange = MakeRange(otherAnno->GetBegin(), otherDecl.identifier.End()); builder.AddNote( otherDecl, otherDeclRange, "Other declaration '" + otherDecl.identifier + "' has a different @ForeignName"); } else if (otherAnno) { builder.AddNote(otherDecl, MakeRange(otherDecl.identifier), "Other declaration '" + otherDecl.identifier + "' has a different derived @ForeignName '" + GetAnnoValue(otherAnno) + "'"); } else { auto otherDeclRange = MakeRange(otherDecl.identifier); builder.AddNote( otherDecl, otherDeclRange, "Other declaration '" + otherDecl.identifier + "' doesn't have a @ForeignName"); } builder.AddNote(checkingDecl, MakeRange(checkingDecl.identifier), "While checking declaration '" + checkingDecl.identifier + "'"); } bool NeedCheck(const MemberSignature& parent, const MemberSignature& child) { if (child.decl->outerDecl->TestAttr(Attribute::IMPORTED)) { return false; } if (!parent.decl->IsFuncOrProp()) { return false; } CJC_ASSERT(child.decl->IsFuncOrProp()); if (!parent.decl->outerDecl->TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE)) { return false; } if (!child.decl->outerDecl->TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE)) { // @JavaMirror anottation might be missing here, will report it later return false; } if (parent.decl->outerDecl == child.decl->outerDecl) { return false; } return true; } } // namespace void CheckForeignName(DiagnosticEngine& diag, TypeManager& typeManager, const MemberSignature& parent, const MemberSignature& child, const Decl& checkingDecl) { if (!NeedCheck(parent, child)) { return; } auto childAnno = GetForeignNameAnnotation(*child.decl); auto parentAnno = GetForeignNameAnnotation(*parent.decl); if (!childAnno && !parentAnno) { return; } if (!typeManager.IsSubtype(child.structTy, parent.structTy)) { if (!childAnno && parentAnno) { DiagConflictingForeignName(diag, *parent.decl, *child.decl, checkingDecl); } else if (!parentAnno && childAnno) { DiagConflictingForeignName(diag, *child.decl, *parent.decl, checkingDecl); } else if (GetAnnoValue(childAnno) != GetAnnoValue(parentAnno)) { DiagConflictingForeignName(diag, *parent.decl, *child.decl, checkingDecl); } return; } if (childAnno && !childAnno->TestAttr(Attribute::COMPILER_ADD)) { auto range = MakeRange(childAnno->GetBegin(), child.decl->identifier.End()); diag.DiagnoseRefactor(DiagKindRefactor::sema_foreign_name_appeared_in_child, *child.decl, range); } else if (childAnno && !parentAnno) { DiagConflictingForeignName(diag, *child.decl, *parent.decl, checkingDecl); } else if (!childAnno && parentAnno && child.replaceOther) { // NOTE: When replaceOther is true, then this method is overriding some other parent one // And if there is no ForeignName, then that parent also hadn't ForeignName. But current // parent do have it DiagConflictingForeignName(diag, *parent.decl, *child.decl, checkingDecl); } else if (!childAnno && parentAnno) { auto clonedAnno = ASTCloner::Clone(parentAnno); clonedAnno->EnableAttr(Attribute::COMPILER_ADD); CopyBasicInfo(child.decl, clonedAnno.get()); child.decl->annotations.emplace_back(std::move(clonedAnno)); } else if (GetAnnoValue(childAnno) != GetAnnoValue(parentAnno)) { DiagConflictingForeignName(diag, *parent.decl, *child.decl, checkingDecl); } } } // namespace Cangjie::Interop::Java cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/TypeCheck/InheritanceChecker.h000066400000000000000000000020151510705540100266630ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares inheritance related checks for JavaFFI features */ #ifndef CANGJIE_SEMA_NATIVE_FFI_JAVA_TYPE_CHECK_INHERITANCE_CHECKER_H #define CANGJIE_SEMA_NATIVE_FFI_JAVA_TYPE_CHECK_INHERITANCE_CHECKER_H #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/AST/Node.h" #include "cangjie/Sema/TypeManager.h" #include "InheritanceChecker/StructInheritanceChecker.h" namespace Cangjie::Interop::Java { /** Checks @ForeignName annotation usage and propagates it */ void CheckForeignName(DiagnosticEngine& diag, TypeManager& typeManager, const MemberSignature& parent, const MemberSignature& child, const Decl& checkingDecl); } // namespace Cangjie::Interop::Java #endif // CANGJIE_SEMA_NATIVE_FFI_JAVA_TYPE_CHECK_INHERITANCE_CHECKER_H cangjie_compiler-1.0.7/src/Sema/NativeFFI/Java/readme.md000066400000000000000000000003331510705540100226750ustar00rootroot00000000000000# Native FFI with Java This directory includes semantics logics related to Cangjie **Native** FFI with Java. It declares desugaring for mirrors (foreign types), impls (export types), java source code generation, etc. cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/000077500000000000000000000000001510705540100210535ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/000077500000000000000000000000001510705540100237145ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/CMakeLists.txt000066400000000000000000000007121510705540100264540ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB SEMA_NATIVE_FFI_OBJ_C_AFTER_TYPECHECK_SRC *.cpp) add_subdirectory(Interop) set(SEMA_NATIVE_FFI_OBJ_C_SRC ${SEMA_NATIVE_FFI_OBJ_C_SRC} ${SEMA_NATIVE_FFI_OBJ_C_AFTER_TYPECHECK_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Desugar.cpp000066400000000000000000000021551510705540100260150ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the entrypoint to handle Objective-C mirrors and theirs subtypes desugaring. */ #include "Desugar.h" #include "Interop/Handlers.h" void Cangjie::Interop::ObjC::Desugar(InteropContext&& ctx) { HandlerFactory::Start() .Use() .Use() .Use() .Use() .Use() .Use() .Use() .Use() .Use() .Use() .Use() .Use() .Use() .Use() .Use() .Handle(ctx); } cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Desugar.h000066400000000000000000000012541510705540100254610ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the entrypoint to handle Objective-C mirrors and theirs subtypes desugaring. */ #ifndef CANGJIE_SEMA_DESUGAR_OBJ_C_INTEROP_DESUGAR_H #define CANGJIE_SEMA_DESUGAR_OBJ_C_INTEROP_DESUGAR_H #include "Interop/Context.h" namespace Cangjie::Interop::ObjC { void Desugar(InteropContext&& ctx); } // namespace Cangjie::Interop::ObjC #endif // CANGJIE_SEMA_DESUGAR_OBJ_C_INTEROP_DESUGAR_Hcangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop/000077500000000000000000000000001510705540100253345ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop/CMakeLists.txt000066400000000000000000000007411510705540100300760ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB SEMA_NATIVE_FFI_OBJ_C_AFTER_TYPECHECK_INTEROP_SRC *.cpp) set(SEMA_NATIVE_FFI_OBJ_C_AFTER_TYPECHECK_SRC ${SEMA_NATIVE_FFI_OBJ_C_AFTER_TYPECHECK_SRC} ${SEMA_NATIVE_FFI_OBJ_C_AFTER_TYPECHECK_INTEROP_SRC} PARENT_SCOPE) cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop/CheckImplTypes.cpp000066400000000000000000000016761510705540100307360ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements typechecking pipeline for Objective-C mirror subtypes. */ #include "NativeFFI/ObjC/TypeCheck/Handlers.h" #include "Handlers.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; void CheckImplTypes::HandleImpl(InteropContext& ctx) { auto checker = HandlerFactory::Start() .Use() .Use() .Use(); for (auto& impl : ctx.impls) { auto typeCheckCtx = TypeCheckContext(*impl, ctx.diag, ctx.typeMapper); checker.Handle(typeCheckCtx); } }cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop/CheckMirrorTypes.cpp000066400000000000000000000016661510705540100313060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements typechecking pipeline for Objective-C mirrors. */ #include "NativeFFI/ObjC/TypeCheck/Handlers.h" #include "Handlers.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; void CheckMirrorTypes::HandleImpl(InteropContext& ctx) { auto checker = HandlerFactory::Start() .Use() .Use() .Use(); for (auto& mirror : ctx.mirrors) { auto typeCheckCtx = TypeCheckContext(*mirror, ctx.diag, ctx.typeMapper); checker.Handle(typeCheckCtx); } }CheckObjCPointerTypeArguments.cpp000066400000000000000000000026561510705540100336360ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements checks of types used with ObjCPointer */ #include "NativeFFI/Utils.h" #include "NativeFFI/ObjC/Utils/Common.h" #include "cangjie/AST/Walker.h" #include "Handlers.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; void CheckObjCPointerTypeArguments::HandleImpl(InteropContext& ctx) { for (auto& file : ctx.pkg.files) { Walker(file, Walker::GetNextWalkerID(), [&file, &ctx](auto node) { if (!node->IsSamePackage(*file->curPackage)) { return VisitAction::WALK_CHILDREN; } Ptr typeUsage = As(node); if (typeUsage && typeUsage->GetTypeArgs().size() == 1 && ctx.typeMapper.IsObjCPointer(*typeUsage->ty) && !ctx.typeMapper.IsObjCCompatible(*typeUsage->ty->typeArgs[0])) { ctx.diag.DiagnoseRefactor( DiagKindRefactor::sema_objc_pointer_argument_must_be_objc_compatible, *typeUsage); typeUsage->EnableAttr(Attribute::IS_BROKEN); } return VisitAction::WALK_CHILDREN; }).Walk(); } } cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop/Context.h000066400000000000000000000040001510705540100271230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares core context for the core handlers of Cangjie <-> Objective-C interopability. */ #ifndef CANGJIE_SEMA_DESUGAR_OBJ_C_INTEROP_INTEROP_CONTEXT #define CANGJIE_SEMA_DESUGAR_OBJ_C_INTEROP_INTEROP_CONTEXT #include "cangjie/AST/Node.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Sema/TypeManager.h" #include "NativeFFI/ObjC/Utils/ASTFactory.h" #include "NativeFFI/ObjC/Utils/InteropLibBridge.h" #include "NativeFFI/ObjC/Utils/NameGenerator.h" #include "NativeFFI/ObjC/Utils/TypeMapper.h" namespace Cangjie::Interop::ObjC { struct InteropContext { explicit InteropContext( AST::Package& pkg, TypeManager& typeManager, ImportManager& importManager, DiagnosticEngine& diag, const BaseMangler& mangler, const std::string& cjLibOutputPath) : pkg(pkg), diag(diag), typeManager(typeManager), importManager(importManager), bridge(importManager, diag), typeMapper(bridge, typeManager), mangler(mangler), nameGenerator(mangler), factory(bridge, typeManager, nameGenerator, typeMapper, importManager), cjLibOutputPath(cjLibOutputPath) { } AST::Package& pkg; std::vector> mirrors; std::vector> impls; std::vector> genDecls; DiagnosticEngine& diag; TypeManager& typeManager; ImportManager& importManager; InteropLibBridge bridge; TypeMapper typeMapper; const BaseMangler& mangler; NameGenerator nameGenerator; ASTFactory factory; const std::string& cjLibOutputPath; }; } // namespace Cangjie::Interop::ObjC #endif // CANGJIE_SEMA_DESUGAR_OBJ_C_INTEROP_INTEROP_CONTEXT cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop/DesugarImpls.cpp000066400000000000000000000046551510705540100304510ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements desugaring of @ObjCImpl. */ #include "Handlers.h" #include "NativeFFI/ObjC/Utils/Common.h" #include "NativeFFI/Utils.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Walker.h" using namespace Cangjie::AST; using namespace Cangjie::Native::FFI; using namespace Cangjie::Interop::ObjC; void DesugarImpls::HandleImpl(InteropContext& ctx) { for (auto& impl : ctx.impls) { if (impl->TestAttr(Attribute::IS_BROKEN)) { continue; } for (auto& memberDecl : impl->GetMemberDeclPtrs()) { if (memberDecl->TestAttr(Attribute::IS_BROKEN)) { continue; } switch (memberDecl->astKind) { case ASTKind::FUNC_DECL: { auto& fd = *StaticAs(memberDecl); if (fd.TestAttr(Attribute::CONSTRUCTOR)) { DesugarCtor(ctx, *impl, fd); } else { DesugarMethod(ctx, *impl, fd); } break; } case ASTKind::PROP_DECL: { Desugar(ctx, *impl, *StaticAs(memberDecl)); break; } default: break; } } } } void DesugarImpls::DesugarMethod( [[maybe_unused]] InteropContext& ctx, [[maybe_unused]] ClassDecl& impl, [[maybe_unused]] FuncDecl& method) { } void DesugarImpls::DesugarCtor([[maybe_unused]] InteropContext& ctx, [[maybe_unused]] ClassDecl& impl, FuncDecl& ctor) { if (!ctx.factory.IsGeneratedCtor(ctor)) { // Call this([[ImplCls alloc] init]) ctor.funcBody->body = CreateBlock(Nodes(ctx.factory.CreateThrowUnreachableCodeExpr(*ctor.curFile)), TypeManager::GetPrimitiveTy(TypeKind::TYPE_NOTHING)); return; } } void DesugarImpls::Desugar(InteropContext& ctx, ClassDecl& impl, PropDecl& prop) { for (auto& getter : prop.getters) { DesugarMethod(ctx, impl, *getter.get()); } for (auto& setter : prop.setters) { DesugarMethod(ctx, impl, *setter.get()); } } cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop/DesugarMirrors.cpp000066400000000000000000000172241510705540100310160ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements desugaring of Objective-C mirror declarations. */ #include "Handlers.h" #include "NativeFFI/Utils.h" #include "cangjie/AST/Create.h" #include "cangjie/Utils/CheckUtils.h" #include using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; using namespace Cangjie::Native::FFI; void DesugarMirrors::HandleImpl(InteropContext& ctx) { for (auto& mirror : ctx.mirrors) { if (mirror->TestAttr(Attribute::IS_BROKEN)) { continue; } for (auto& memberDecl : mirror->GetMemberDeclPtrs()) { if (memberDecl->TestAttr(Attribute::IS_BROKEN)) { continue; } if (ctx.factory.IsGeneratedMember(*memberDecl)) { continue; } switch (memberDecl->astKind) { case ASTKind::FUNC_DECL: { auto& fd = *StaticAs(memberDecl); if (fd.TestAttr(Attribute::CONSTRUCTOR)) { DesugarCtor(ctx, *mirror, fd); } else { DesugarMethod(ctx, *mirror, fd); } break; } case ASTKind::PROP_DECL: { auto& pd = *StaticAs(memberDecl); if (memberDecl->TestAttr(Attribute::DESUGARED_MIRROR_FIELD)) { DesugarField(ctx, *mirror, pd); } else { DesugarProp(ctx, *mirror, pd); } break; } case ASTKind::VAR_DECL: // Unreachable, because all @ObjCMirror fields are converted to props on previous stages. CJC_ABORT(); break; default: break; } } } } void DesugarMirrors::DesugarCtor(InteropContext& ctx, ClassLikeDecl& mirror, FuncDecl& ctor) { auto curFile = ctor.curFile; CJC_NULLPTR_CHECK(ctor.funcBody); CJC_ASSERT(!ctor.funcBody->paramLists.empty()); auto& params = ctor.funcBody->paramLists[0]->params; auto& generatedCtor = *ctx.factory.GetGeneratedMirrorCtor(mirror); auto thisCall = CreateThisCall(mirror, generatedCtor, generatedCtor.ty, curFile); auto allocCall = ctx.factory.CreateAllocCall(*StaticAs(&mirror), curFile); std::vector> initArgs; std::transform(params.begin(), params.end(), std::back_inserter(initArgs), [&ctx, curFile](auto& param) { auto unwrapped = ctx.factory.UnwrapEntity(WithinFile(CreateRefExpr(*param), curFile)); return unwrapped; }); auto initCall = ctx.factory.CreateMethodCallViaMsgSend(ctor, std::move(allocCall), std::move(initArgs)); thisCall->args.emplace_back(CreateFuncArg(std::move(initCall))); ctor.constructorCall = ConstructorCall::OTHER_INIT; ctor.funcBody->body->body.emplace_back(std::move(thisCall)); } void DesugarMirrors::DesugarMethod(InteropContext& ctx, ClassLikeDecl& mirror, FuncDecl& method) { auto methodTy = StaticCast(method.ty); auto curFile = method.curFile; auto nativeHandle = ctx.factory.CreateNativeHandleExpr(mirror, method.TestAttr(Attribute::STATIC), curFile); std::vector> msgSendArgs; auto& params = method.funcBody->paramLists[0]->params; std::transform(params.begin(), params.end(), std::back_inserter(msgSendArgs), [&ctx, curFile](auto& param) { return ctx.factory.UnwrapEntity(WithinFile(CreateRefExpr(*param), curFile)); }); auto arpScopeCall = ctx.factory.CreateAutoreleasePoolScope(methodTy->retTy, Nodes(ctx.factory.CreateMethodCallViaMsgSend(method, std::move(nativeHandle), std::move(msgSendArgs)))); method.funcBody->body = CreateBlock({}, methodTy->retTy); method.funcBody->body->body.emplace_back(ctx.factory.WrapEntity(std::move(arpScopeCall), *methodTy->retTy)); } namespace { void DesugarGetter(InteropContext& ctx, ClassLikeDecl& mirror, PropDecl& prop) { CJC_ASSERT(!prop.getters.empty()); auto& getter = prop.getters[0]; auto curFile = prop.curFile; auto nativeHandle = ctx.factory.CreateNativeHandleExpr(mirror, prop.TestAttr(Attribute::STATIC), curFile); auto arpScopeCall = ctx.factory.CreateAutoreleasePoolScope( prop.ty, Nodes(ctx.factory.CreatePropGetterCallViaMsgSend(prop, std::move(nativeHandle)))); getter->funcBody->body = CreateBlock({}, prop.ty); getter->funcBody->body->body.emplace_back(ctx.factory.WrapEntity(std::move(arpScopeCall), *prop.ty)); } void DesugarSetter(InteropContext& ctx, ClassLikeDecl& mirror, PropDecl& prop) { CJC_ASSERT(prop.TestAttr(Attribute::MUT)); CJC_ASSERT(!prop.setters.empty()); auto& setter = prop.setters[0]; auto curFile = prop.curFile; auto unitTy = ctx.typeManager.GetPrimitiveTy(TypeKind::TYPE_UNIT); setter->funcBody->body = CreateBlock({}, unitTy); auto nativeHandle = ctx.factory.CreateNativeHandleExpr(mirror, prop.TestAttr(Attribute::STATIC), curFile); auto paramRef = WithinFile(CreateRefExpr(*setter->funcBody->paramLists[0]->params[0]), curFile); auto arg = ctx.factory.UnwrapEntity(std::move(paramRef)); auto arpScopeCall = ctx.factory.CreateAutoreleasePoolScope( unitTy, Nodes(ctx.factory.CreatePropSetterCallViaMsgSend(prop, std::move(nativeHandle), std::move(arg)))); setter->funcBody->body->body.emplace_back(std::move(arpScopeCall)); } void DesugarFieldGetter(InteropContext& ctx, ClassLikeDecl& mirror, PropDecl& field) { CJC_ASSERT(!field.getters.empty()); auto& getter = field.getters[0]; auto curFile = field.curFile; getter->funcBody->body = CreateBlock({}, field.ty); CJC_ASSERT(!field.TestAttr(Attribute::STATIC)); auto nativeHandle = ctx.factory.CreateNativeHandleExpr(mirror, false, curFile); auto getInstanceVariableCall = ctx.factory.CreateGetInstanceVariableCall(field, std::move(nativeHandle)); getter->funcBody->body->body.emplace_back(ctx.factory.WrapEntity(std::move(getInstanceVariableCall), *field.ty)); } void DesugarFieldSetter(InteropContext& ctx, ClassLikeDecl& mirror, PropDecl& field) { CJC_ASSERT(field.TestAttr(Attribute::MUT)); CJC_ASSERT(!field.setters.empty()); auto& setter = field.setters[0]; auto curFile = field.curFile; auto unitTy = ctx.typeManager.GetPrimitiveTy(TypeKind::TYPE_UNIT); setter->funcBody->body = CreateBlock({}, unitTy); CJC_ASSERT(!field.TestAttr(Attribute::STATIC)); auto nativeHandle = ctx.factory.CreateNativeHandleExpr(mirror, false, curFile); auto paramRef = WithinFile(CreateRefExpr(*setter->funcBody->paramLists[0]->params[0]), curFile); auto arg = ctx.factory.UnwrapEntity(std::move(paramRef)); auto setInstanceVariableCall = ctx.factory.CreateObjCRuntimeSetInstanceVariableCall(field, std::move(nativeHandle), std::move(arg)); setter->funcBody->body->body.emplace_back(std::move(setInstanceVariableCall)); } } // namespace void DesugarMirrors::DesugarProp(InteropContext& ctx, ClassLikeDecl& mirror, PropDecl& prop) { DesugarGetter(ctx, mirror, prop); if (prop.TestAttr(Attribute::MUT)) { DesugarSetter(ctx, mirror, prop); } } void DesugarMirrors::DesugarField(InteropContext& ctx, ClassLikeDecl& mirror, PropDecl& field) { DesugarFieldGetter(ctx, mirror, field); if (field.TestAttr(Attribute::MUT)) { DesugarFieldSetter(ctx, mirror, field); } } cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop/DrainGeneratedDecls.cpp000066400000000000000000000012651510705540100316730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements draining generated top-level declarations to its files. */ #include "Handlers.h" using namespace Cangjie::Interop::ObjC; void DrainGeneratedDecls::HandleImpl(InteropContext& ctx) { for (auto pdecl = ctx.genDecls.begin(); pdecl < ctx.genDecls.end(); pdecl++) { auto& file = (*pdecl)->curFile; file->decls.push_back(std::move(*pdecl)); } ctx.genDecls.clear(); }cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop/FindMirrors.cpp000066400000000000000000000024051510705540100302770ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements searching for Objective-C mirror declarations and theirs subtypes. */ #include "Handlers.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; void FindMirrors::HandleImpl(InteropContext& ctx) { for (auto& file : ctx.pkg.files) { for (auto& decl : file->decls) { if (auto classLikeDecl = As(decl); classLikeDecl && ctx.typeMapper.IsObjCMirror(*classLikeDecl)) { ctx.mirrors.emplace_back(classLikeDecl); } if (auto classDecl = As(decl); classDecl && (ctx.typeMapper.IsObjCImpl(*classDecl) || (ctx.typeMapper.IsObjCMirrorSubtype(*classDecl) && !ctx.typeMapper.IsObjCImpl(*classDecl) && !ctx.typeMapper.IsObjCMirror(*classDecl)))) { ctx.impls.emplace_back(classDecl); } } } }GenerateDeleteCJObjectMethod.cpp000066400000000000000000000015061510705540100333450ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements generating delete Cangjie object method for Objective-C mirror subtypes. */ #include "Handlers.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; void GenerateDeleteCJObjectMethod::HandleImpl(InteropContext& ctx) { for (auto& impl : ctx.impls) { if (impl->TestAttr(Attribute::IS_BROKEN)) { continue; } auto deleteCjObject = ctx.factory.CreateDeleteCjObject(*impl); CJC_ASSERT(deleteCjObject); ctx.genDecls.emplace_back(std::move(deleteCjObject)); } }cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop/GenerateGlueCode.cpp000066400000000000000000000015271510705540100312070ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements generating Objective-C glue code. */ #include "NativeFFI/ObjC/ObjCCodeGenerator/ObjCGenerator.h" #include "Handlers.h" #include "cangjie/Mangle/BaseMangler.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; void GenerateGlueCode::HandleImpl(InteropContext& ctx) { for (auto& impl : ctx.impls) { if (impl->TestAnyAttr(Attribute::IS_BROKEN, Attribute::HAS_BROKEN)) { continue; } auto codegen = ObjCGenerator(ctx, impl, "objc-gen", ctx.cjLibOutputPath); codegen.Generate(); } } GenerateInitCJObjectMethods.cpp000066400000000000000000000027261510705540100332360ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements generating init Cangjie object method for @ObjCImpls. */ #include "Handlers.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; void GenerateInitCJObjectMethods::HandleImpl(InteropContext& ctx) { for (auto& impl : ctx.impls) { if (impl->TestAttr(Attribute::IS_BROKEN)) { continue; } for (auto& memberDecl : impl->GetMemberDeclPtrs()) { if (memberDecl->TestAttr(Attribute::IS_BROKEN)) { continue; } if (!memberDecl->TestAttr(Attribute::CONSTRUCTOR)) { continue; } if (memberDecl->astKind != ASTKind::FUNC_DECL) { // skip primary ctor, as it is desugared to init already continue; } auto& ctorDecl = *StaticAs(memberDecl); // skip original ctors if (!ctx.factory.IsGeneratedCtor(ctorDecl)) { continue; } auto initCjObject = ctx.factory.CreateInitCjObject(*impl, ctorDecl); CJC_ASSERT(initCjObject); ctx.genDecls.emplace_back(std::move(initCjObject)); } } }cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop/GenerateObjCImplMembers.cpp000066400000000000000000000026371510705540100324750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements generating in @ObjCImpl declarations. */ #include "Handlers.h" #include "cangjie/AST/Create.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; void GenerateObjCImplMembers::HandleImpl(InteropContext& ctx) { for (auto& impl : ctx.impls) { if (impl->TestAttr(Attribute::IS_BROKEN)) { continue; } for (auto& memberDecl : impl->GetMemberDeclPtrs()) { if (memberDecl->TestAttr(Attribute::IS_BROKEN)) { continue; } switch (memberDecl->astKind) { case ASTKind::FUNC_DECL: if (memberDecl->TestAttr(Attribute::CONSTRUCTOR)) { GenerateCtor(ctx, *impl, *StaticAs(memberDecl)); } break; default: break; } } } } void GenerateObjCImplMembers::GenerateCtor(InteropContext& ctx, ClassDecl& target, FuncDecl& from) { auto ctor = ctx.factory.CreateImplCtor(target, from); CJC_ASSERT(ctor); target.body->decls.emplace_back(std::move(ctor)); } cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop/GenerateWrappers.cpp000066400000000000000000000057231510705540100313250ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements desugaring of Objective-C mirror subtypes. */ #include "NativeFFI/ObjC/Utils/Common.h" #include "Handlers.h" #include "cangjie/AST/Match.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; void GenerateWrappers::HandleImpl(InteropContext& ctx) { for (auto& impl : ctx.impls) { if (impl->TestAttr(Attribute::IS_BROKEN)) { continue; } for (auto& memberDecl : impl->GetMemberDeclPtrs()) { if (memberDecl->TestAnyAttr(Attribute::IS_BROKEN, Attribute::CONSTRUCTOR)) { continue; } if (!memberDecl->TestAnyAttr(Attribute::PUBLIC)) { continue; } if (ctx.factory.IsGeneratedMember(*memberDecl)) { continue; } switch (memberDecl->astKind) { case ASTKind::FUNC_DECL: GenerateWrapper(ctx, *StaticAs(memberDecl)); break; case ASTKind::PROP_DECL: GenerateWrapper(ctx, *StaticAs(memberDecl)); break; case ASTKind::VAR_DECL: GenerateWrapper(ctx, *StaticAs(memberDecl)); break; default: break; } } } } void GenerateWrappers::GenerateWrapper(InteropContext& ctx, FuncDecl& method) { auto wrapper = ctx.factory.CreateMethodWrapper(method); CJC_NULLPTR_CHECK(wrapper); ctx.genDecls.emplace_back(std::move(wrapper)); } void GenerateWrappers::GenerateWrapper(InteropContext& ctx, PropDecl& prop) { auto wrapper = ctx.factory.CreateGetterWrapper(prop); CJC_NULLPTR_CHECK(wrapper); ctx.genDecls.emplace_back(std::move(wrapper)); if (prop.isVar) { GenerateSetterWrapper(ctx, prop); } } void GenerateWrappers::GenerateSetterWrapper(InteropContext& ctx, PropDecl& prop) { auto wrapper = ctx.factory.CreateSetterWrapper(prop); CJC_NULLPTR_CHECK(wrapper); ctx.genDecls.emplace_back(std::move(wrapper)); } void GenerateWrappers::GenerateWrapper(InteropContext& ctx, VarDecl& field) { if (ctx.factory.IsGeneratedNativeHandleField(field)) { return; } auto wrapper = ctx.factory.CreateGetterWrapper(field); CJC_NULLPTR_CHECK(wrapper); ctx.genDecls.emplace_back(std::move(wrapper)); if (field.isVar) { GenerateSetterWrapper(ctx, field); } } void GenerateWrappers::GenerateSetterWrapper(InteropContext& ctx, VarDecl& field) { auto wrapper = ctx.factory.CreateSetterWrapper(field); CJC_NULLPTR_CHECK(wrapper); ctx.genDecls.emplace_back(std::move(wrapper)); } cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop/Handlers.h000066400000000000000000000222471510705540100272540ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares core handlers for implementation of Cangjie <-> Objective-C interopability. */ #ifndef CANGJIE_SEMA_DESUGAR_OBJ_C_INTEROP_INTEROP_HANDLERS #define CANGJIE_SEMA_DESUGAR_OBJ_C_INTEROP_INTEROP_HANDLERS #include "Context.h" #include "NativeFFI/ObjC/Utils/Handler.h" namespace Cangjie::Interop::ObjC { /** * Finds all @ObjCMirror and @ObjCImpl declarations. */ class FindMirrors : public Handler { public: void HandleImpl(InteropContext& ctx); }; /** * Performs all necessary syntax and semantic checks on @ObjCMirror declarations. */ class CheckMirrorTypes : public Handler { public: void HandleImpl(InteropContext& ctx); }; /** * Performs all necessary syntax and semantic checks on @ObjCImpl declarations. */ class CheckImplTypes : public Handler { public: void HandleImpl(InteropContext& ctx); }; /** * Creates and inserts field of type `NativeObjCId` in all hierarchy root @ObjCMirror declarations. * * `public var $obj: NativeObjCId` */ class InsertNativeHandleField : public Handler { public: void HandleImpl(InteropContext& ctx); }; /** * Creates and inserts constructors skeletons for each @ObjCMirror class: * * `public init($obj: NativeObjCId)` */ class InsertMirrorCtorDecl : public Handler { public: void HandleImpl(InteropContext& ctx); }; /** * Creates and inserts constructors bodies for each @ObjCMirror class: * * ```cangjie * public init($obj: NativeObjCId) { // decl was inserted in `InsertMirrorCtorDecl` * this.$obj = $obj * } * ``` * or * ```cangjie * public init($obj: NativeObjCId) { * super($obj) * } * ``` */ class InsertMirrorCtorBody : public Handler { public: void HandleImpl(InteropContext& ctx); }; /** * Generates top-level `CJImpl_ObjC_{ForeignName}_deleteCJObject($registryId: RegistryId): Unit` * method for each @ObjCImpl declaration. */ class GenerateDeleteCJObjectMethod : public Handler { public: void HandleImpl(InteropContext& ctx); }; /** * Desugars all members of every @ObjCMirror declarations. * Methods are desugared as follows: * 1. If method returns @ObjCMirror/@ObjCImpl value: * 1.1 Generates proper ObjCRuntime.msgSend call, e.g. ```unsafe { CFunc<(NativeObjCId, NativeObjCSel, ...ArgTypes) -> * NativeObjCId>(ObjCRuntime.msgSend)($obj, ObjCRuntime.registerName("${methodForeignName}"), ...args) }```. 1.2 Wraps * it with ObjCRuntime.withAutoreleasePoolObj call, e.g. ```ObjCRuntime.withAutoreleasePool{ => // msgSend call })```. * 1.3 Wraps it with ctor call of return value type, e.g. ```M(// ObjCRuntime.withAutoreleasePool call)```. * * 2. Else if method returns Objective-C compatible primitive type value: * 2.1 Generates proper ObjCRuntime.msgSend call, e.g. ```unsafe { CFunc<(NativeObjCId, NativeObjCSel, ...ArgTypes) -> * RetType>(ObjCRuntime.msgSend)($obj, ObjCRuntime.registerName("${methodForeignName}"), ...args) }``` * * 3. Body is inserted in the ctor declaration generated on previous steps. * * Prop getters are desugared as follows: * 1. If prop has @ObjCMirror/@ObjCImpl value: * 1.1 Generates proper ObjCRuntime.msgSend call, e.g. ```unsafe { CFunc<(ObjCId, ObjCSel) -> * ObjCId>(ObjCRuntime.msgSend)($obj, ObjCRuntime.registerName("foo")) }```. 1.2 Wraps it with * ObjCRuntime.withAutoreleasePoolObj call, e.g. ```ObjCRuntime.withAutoreleasePool{ => // msgSend call })```. 1.3 Wraps * it with ObjCRuntime.getFromRegistry call, e.g. ```ObjCRuntime.getFromRegistry(...// ObjCRuntime.withAutoreleasePool * call)```. * * 2. Else if prop has Objective-C compatible primitive type value: * 2.1 Generates proper ObjCRuntime.msgSend call, e.g. ```unsafe { CFunc<(ObjCId, ObjCSel) -> * RetType>(ObjCRuntime.msgSend)($obj, ObjCRuntime.registerName("foo"), ...args) }``` * * 3. Accessor body is inserted in the corresponding declaration generated on previous steps. * * Prop setters are desugared as follows: * Generates proper ObjCRuntime.msgSend call, e.g. ```unsafe { CFunc<(ObjCId, ObjCSel, ObjCId) -> * Unit>(ObjCRuntime.msgSend)($obj, ObjCRuntime.registerName("setFoo:"), value.$obj) }```. * * Fields are desugared as follows: * 1. On parse stage, all fields are replaced with corresponding prop declaration. * 2. Prop getter is essentially ObjCRuntime.getInstanceVariable call (getInstanceVariableObj for object types wrapped * with getFromRegistryByHandle call). * 3. Prop setter (@ObjCMirror field is always var) is essentially ObjCRuntime.setInstanceVariable * (setInstanceVariableObj for object type) call. */ class DesugarMirrors : public Handler { public: void HandleImpl(InteropContext& ctx); private: void DesugarMethod(InteropContext& ctx, AST::ClassLikeDecl& mirror, AST::FuncDecl& method); void DesugarCtor(InteropContext& ctx, AST::ClassLikeDecl& mirror, AST::FuncDecl& ctor); void DesugarProp(InteropContext& ctx, AST::ClassLikeDecl& mirror, AST::PropDecl& prop); void DesugarField(InteropContext& ctx, AST::ClassLikeDecl& mirror, AST::PropDecl& field); }; /** * Does generation inside impl body. Inserts: * - A constructor (with extra handle parameter) for each user-defined constructor. It is callable from Objective-C */ class GenerateObjCImplMembers : public Handler { public: void HandleImpl(InteropContext& ctx); private: void GenerateCtor(InteropContext& ctx, AST::ClassDecl& target, AST::FuncDecl& from); }; /** * Generates top-level `CJImpl_ObjC_{ForeignName}($obj: NativeObjCId, ...): RegistryId` methods * for each @ObjCImpl ctor declaration (with first $obj param, as they have proper types for interop). */ class GenerateInitCJObjectMethods : public Handler { public: void HandleImpl(InteropContext& ctx); }; /** * Desugars all members of every @ObjCImpl declaration. * Common steps are: * 1. All method/prop accessors bodies wrapped with `ObjC_ISL_withMethodEnv_ret`/`ObjC_ISL_withMethodEnv_retObj` * method call. * 2. All expressions with Objective-C mirror/mirror subtype declarations involved are desugared to * `objc_msgSend`/`objc_msgSendSuper` calls. * 3. Desugared bodies are placed in the corresponding mirrors ($objC prefixed) members. * 4. Original members bodies are replaced with throwing `ObjC_ISL_UnreachableCodeException`. */ class DesugarImpls : public Handler { public: void HandleImpl(InteropContext& ctx); private: void DesugarMethod(InteropContext& ctx, AST::ClassDecl& impl, AST::FuncDecl& method); void DesugarCtor(InteropContext& ctx, AST::ClassDecl& impl, AST::FuncDecl& ctor); void Desugar(InteropContext& ctx, AST::ClassDecl& impl, AST::PropDecl& prop); }; /** * Generates top-level function wrappers for: * 1. Methods (static included) * 2. Props (getter and *setter static included) * 3. Fields (getter and *setter static included) * * for each @ObjCImpl declaration. * * It has to be done because we need a way to call them from Objective-C runtime. * * NOTE: setters are generated if and only if the prop/field is mutable. */ class GenerateWrappers : public Handler { public: void HandleImpl(InteropContext& ctx); private: void GenerateWrapper(InteropContext& ctx, AST::FuncDecl& method); // Generic methods for prop and field with SFINAE? void GenerateWrapper(InteropContext& ctx, AST::PropDecl& prop); void GenerateSetterWrapper(InteropContext& ctx, AST::PropDecl& prop); void GenerateWrapper(InteropContext& ctx, AST::VarDecl& field); void GenerateSetterWrapper(InteropContext& ctx, AST::VarDecl& field); }; /** * Generates Objective-C glue code for all @ObjCImpl declarations. * For each valid @ObjCImpl would be generated the following: * 1. Header file ({ForeignName}.h) * 2. Implementation file ({ForeignName}.cpp) */ class GenerateGlueCode : public Handler { public: void HandleImpl(InteropContext& ctx); }; /** * Report errors on usage of ObjCPointer with ObjC-incompatible types */ class CheckObjCPointerTypeArguments : public Handler { public: void HandleImpl(InteropContext& ctx); }; /** * Rewrite pointer access performed by ObjCPointer methods to proper FFI calls */ class RewriteObjCPointerAccess : public Handler { public: void HandleImpl(InteropContext& ctx); }; /** * Drains all declarations generated on the previous step to their corresponding files which finishes the desugaring. */ class DrainGeneratedDecls : public Handler { public: void HandleImpl(InteropContext& ctx); }; } // namespace Cangjie::Interop::ObjC #endif // CANGJIE_SEMA_DESUGAR_OBJ_C_INTEROP_INTEROP_HANDLERS cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop/InsertMirrorCtorBody.cpp000066400000000000000000000037501510705540100321520ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements generating and inserting a constructor of handle to each objective-c mirror */ #include "NativeFFI/Utils.h" #include "NativeFFI/ObjC/Utils/Common.h" #include "cangjie/AST/Create.h" #include "Handlers.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; using namespace Cangjie::Native::FFI; void InsertMirrorCtorBody::HandleImpl(InteropContext& ctx) { for (auto& mirror : ctx.mirrors) { if (mirror->TestAttr(Attribute::IS_BROKEN)) { continue; } auto mirrorClass = As(mirror); if (!mirrorClass) { continue; } auto ctor = ctx.factory.GetGeneratedMirrorCtor(*mirrorClass); CJC_NULLPTR_CHECK(ctor); auto curFile = ctor->curFile; auto handleParam = WithinFile(CreateRefExpr(*ctor->funcBody->paramLists[0]->params[0]), curFile); if (HasMirrorSuperClass(*mirrorClass)) { auto superCtor = ctx.factory.GetGeneratedMirrorCtor(*mirrorClass->GetSuperClassDecl()); auto superCall = CreateSuperCall(*mirrorClass, *superCtor, superCtor->ty); superCall->args.emplace_back(CreateFuncArg(std::move(handleParam))); ctor->funcBody->body->body.emplace_back(std::move(superCall)); } else { auto lhs = CreateMemberAccess(CreateThisRef(mirrorClass, mirrorClass->ty, curFile), ASTFactory::NATIVE_HANDLE_IDENT); static auto unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); auto nativeHandleAssignExpr = CreateAssignExpr(std::move(lhs), std::move(handleParam), unitTy); ctor->funcBody->body->body.emplace_back(std::move(nativeHandleAssignExpr)); } } } cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop/InsertMirrorCtorDecl.cpp000066400000000000000000000020221510705540100321130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements generating and inserting a constructor declaration of native handle to each @ObjCMirror */ #include "Handlers.h" #include "NativeFFI/ObjC/Utils/Common.h" #include "NativeFFI/Utils.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; using namespace Cangjie::Native::FFI; void InsertMirrorCtorDecl::HandleImpl(InteropContext& ctx) { for (auto& mirror : ctx.mirrors) { if (mirror->TestAttr(Attribute::IS_BROKEN)) { continue; } auto mirrorClass = As(mirror); if (!mirrorClass) { continue; } auto ctor = ctx.factory.CreateMirrorCtorDecl(*mirrorClass); mirrorClass->body->decls.emplace_back(std::move(ctor)); } }cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop/InsertNativeHandleField.cpp000066400000000000000000000017611510705540100325400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements inserting NativeObjCId field in Objective-C mirrors */ #include "NativeFFI/ObjC/Utils/Common.h" #include "Handlers.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; void InsertNativeHandleField::HandleImpl(InteropContext& ctx) { for (auto& mirror : ctx.mirrors) { if (mirror->TestAttr(Attribute::IS_BROKEN)) { continue; } auto mirrorClass = As(mirror); if (!mirrorClass || HasMirrorSuperClass(*mirrorClass)) { continue; } auto nativeObjCIdField = ctx.factory.CreateNativeHandleField(*mirrorClass); mirrorClass->body->decls.emplace_back(std::move(nativeObjCIdField)); } } cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/AfterTypeCheck/Interop/RewriteObjCPointerAccess.cpp000066400000000000000000000166061510705540100327130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements desugaring of ObjCPointer struct accessors */ #include "NativeFFI/Utils.h" #include "NativeFFI/ObjC/Utils/Common.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Walker.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Clone.h" #include "Handlers.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; using namespace Cangjie::Native::FFI; using namespace Cangjie; namespace { constexpr auto READ_POINTER_INTRINSIC = "readPointer"; constexpr auto WRITE_POINTER_INTRINSIC = "writePointer"; constexpr auto OBJCPOINTER_READ_METHOD = "read"; constexpr auto OBJCPOINTER_WRITE_METHOD = "write"; OwnedPtr CreateZero(Ptr ty) { return CreateLitConstExpr(LitConstKind::INTEGER, "0", ty); } /** * if T is CType * PTR.read() -> readPointer(CPointer(PTR.ptr)) * * if T is itself ObjCPointer * PTR.read() -> ObjCPointer(readPointer>(CPointer>(PTR.ptr))) * * if T is @ObjCImpl/@ObjCMirror class * PTR.read() -> T(readPointer(CPointer(PTR.ptr))) * * if T is @ObjCImpl/@ObjCMirror interface -> (not implemented yet) * PTR.read() -> T$WrapperClass(readPointer(CPointer(PTR.ptr))) * * All these cases can be unified as * WRAP(readPointer(CPointer(PTR.ptr))) * where NATIVE_C_TYPE is the C type representation of T * WRAP(expr) is the construction procedure of T from NATIVE_C_TYPE */ void HandleObjCPointerRead(InteropContext& ctx, CallExpr& callExpr) { auto ma = As(callExpr.baseFunc); CJC_NULLPTR_CHECK(ma); auto receiver = ASTCloner::Clone(ma->baseExpr); auto elementType = receiver->ty->typeArgs[0]; auto rawCType = ctx.typeMapper.Cj2CType(elementType); Ptr pointerType = ctx.typeManager.GetPointerTy(rawCType); auto ptrFieldDecl = ctx.factory.GetObjCPointerPointerField(); CJC_ASSERT(ptrFieldDecl); Ptr int64Type = ctx.typeManager.GetPrimitiveTy(TypeKind::TYPE_INT64); auto readPointerFunc = ctx.importManager.GetCoreDecl(READ_POINTER_INTRINSIC); CJC_NULLPTR_CHECK(readPointerFunc); auto readPointerRef = CreateRefExpr(*readPointerFunc, callExpr); readPointerRef->typeArguments.push_back(CreateType(rawCType)); readPointerRef->ty = ctx.typeManager.GetFunctionTy(std::vector { pointerType, int64Type }, rawCType); auto ptrExpr = ctx.factory.CreateUnsafePointerCast( CreateMemberAccess(std::move(receiver), *ptrFieldDecl), rawCType); CopyBasicInfo(&callExpr, ptrExpr); auto callArgs = std::vector> {}; callArgs.emplace_back(CreateFuncArg(std::move(ptrExpr))); callArgs.emplace_back(CreateFuncArg(CreateZero(int64Type))); auto call = CreateCallExpr( std::move(readPointerRef), std::move(callArgs), readPointerFunc, rawCType, CallKind::CALL_INTRINSIC_FUNCTION); CopyBasicInfo(&callExpr, call); auto wrapExpr = ctx.factory.WrapEntity(std::move(call), *elementType); wrapExpr->sourceExpr = &callExpr; callExpr.desugarExpr = std::move(wrapExpr); callExpr.desugarArgs = std::nullopt; } /** * if T is CType * PTR.write(V) -> writePointer(CPointer(PTR.ptr), V) * * if T is itself ObjCPointer * PTR.write(V) -> writePointer>(CPointer>(PTR.ptr), V.ptr) * * if T is @ObjCImpl/@ObjCMirror class * PTR.write(V) -> writePointer(CPointer(PTR.ptr), V.$obj) * * if T is @ObjCImpl/@ObjCMirror interface -> (not implemented yet) * PTR.write(V) -> writePointer(CPointer(PTR.ptr)), V.$getObj()) * * All these cases can be unified as * writePointer(CPointer(PTR.ptr), UNWRAP(V)) * where NATIVE_C_TYPE is the C type representation of T * UNWRAP(expr) is the extraction procedure of NATIVE_C_TYPE from T */ void HandleObjCPointerWrite(InteropContext& ctx, CallExpr& callExpr) { auto ma = As(callExpr.baseFunc); CJC_NULLPTR_CHECK(ma); auto receiver = ASTCloner::Clone(ma->baseExpr); CJC_ASSERT(callExpr.args.size() == 1); auto valueArg = ASTCloner::Clone(callExpr.args[0]->expr); auto elementType = receiver->ty->typeArgs[0]; auto rawCType = ctx.typeMapper.Cj2CType(elementType); Ptr pointerType = ctx.typeManager.GetPointerTy(rawCType); auto ptrFieldDecl = ctx.factory.GetObjCPointerPointerField(); CJC_ASSERT(ptrFieldDecl); Ptr int64Type = ctx.typeManager.GetPrimitiveTy(TypeKind::TYPE_INT64); Ptr unitType = ctx.typeManager.GetPrimitiveTy(TypeKind::TYPE_UNIT); auto writePointerFunc = ctx.importManager.GetCoreDecl(WRITE_POINTER_INTRINSIC); CJC_NULLPTR_CHECK(writePointerFunc); auto writePointerRef = CreateRefExpr(*writePointerFunc, callExpr); writePointerRef->typeArguments.push_back(CreateType(rawCType)); writePointerRef->ty = ctx.typeManager.GetFunctionTy({ pointerType, int64Type, rawCType }, unitType); auto ptrExpr = ctx.factory.CreateUnsafePointerCast( CreateMemberAccess(std::move(receiver), *ptrFieldDecl), rawCType); CopyBasicInfo(&callExpr, ptrExpr); auto callArgs = std::vector> {}; callArgs.emplace_back(CreateFuncArg(std::move(ptrExpr))); callArgs.emplace_back(CreateFuncArg(CreateZero(int64Type))); callArgs.emplace_back(CreateFuncArg(ctx.factory.UnwrapEntity(std::move(valueArg)))); auto call = CreateCallExpr( std::move(writePointerRef), std::move(callArgs), writePointerFunc, unitType, CallKind::CALL_INTRINSIC_FUNCTION); CopyBasicInfo(&callExpr, call); call->sourceExpr = &callExpr; callExpr.desugarExpr = std::move(call); callExpr.desugarArgs = std::nullopt; } } void RewriteObjCPointerAccess::HandleImpl(InteropContext& ctx) { for (auto& file : ctx.pkg.files) { Walker(file, Walker::GetNextWalkerID(), [&file, &ctx](auto node) { if (!node->IsSamePackage(*file->curPackage)) { return VisitAction::WALK_CHILDREN; } Ptr callExpr = As(node); if (!callExpr) { return VisitAction::WALK_CHILDREN; } auto funcDecl = callExpr->resolvedFunction; if (!funcDecl || !funcDecl->outerDecl || !ctx.typeMapper.IsObjCPointer(*funcDecl->outerDecl)) { return VisitAction::WALK_CHILDREN; } if (funcDecl->identifier == OBJCPOINTER_READ_METHOD && callExpr->args.size() == 0 && ctx.typeMapper.IsObjCCompatible(*callExpr->ty)) { HandleObjCPointerRead(ctx, *callExpr); return VisitAction::WALK_CHILDREN; } if (funcDecl->identifier == OBJCPOINTER_WRITE_METHOD && callExpr->args.size() == 1 && ctx.typeMapper.IsObjCCompatible(*callExpr->args[0]->ty)) { HandleObjCPointerWrite(ctx, *callExpr); return VisitAction::WALK_CHILDREN; } return VisitAction::WALK_CHILDREN; }).Walk(); } } cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/BeforeTypeCheck/000077500000000000000000000000001510705540100240555ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/BeforeTypeCheck/CMakeLists.txt000066400000000000000000000006621510705540100266210ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB SEMA_NATIVE_FFI_OBJ_C_BEFORE_TYPECHECK_SRC *.cpp) set(SEMA_NATIVE_FFI_OBJ_C_SRC ${SEMA_NATIVE_FFI_OBJ_C_SRC} ${SEMA_NATIVE_FFI_OBJ_C_BEFORE_TYPECHECK_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/BeforeTypeCheck/Desugar.cpp000066400000000000000000000015121510705540100261520ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Desugar.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/ASTCasting.h" #include "cangjie/Utils/CastingTemplate.h" namespace Cangjie::Interop::ObjC { void PrepareTypeCheck(Package& pkg) { for (auto& file : pkg.files) { for (auto& decl : file->decls) { if (decl->TestAttr(Attribute::OBJ_C_MIRROR)) { if (auto cd = DynamicCast(decl.get())) { InsertMirrorVarProp(*cd, Attribute::OBJ_C_MIRROR); } } } } } } cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/BeforeTypeCheck/Desugar.h000066400000000000000000000011051510705540100256150ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_SEMA_NATIVE_FFI_OBJ_C_BEFORE_TYPECHECK_DESUGAR #define CANGJIE_SEMA_NATIVE_FFI_OBJ_C_BEFORE_TYPECHECK_DESUGAR #include "cangjie/AST/Node.h" namespace Cangjie::Interop::ObjC { using namespace AST; void PrepareTypeCheck(Package& pkg); } #endif // CANGJIE_SEMA_NATIVE_FFI_OBJ_C_BEFORE_TYPECHECK_DESUGARcangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/CMakeLists.txt000066400000000000000000000010371510705540100236140ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB SEMA_NATIVE_FFI_OBJ_C_SRC *.cpp) add_subdirectory(AfterTypeCheck) add_subdirectory(BeforeTypeCheck) add_subdirectory(ObjCCodeGenerator) add_subdirectory(TypeCheck) add_subdirectory(Utils) set(SEMA_NATIVE_FFI_SRC ${SEMA_NATIVE_FFI_SRC} ${SEMA_NATIVE_FFI_OBJ_C_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/ObjCCodeGenerator/000077500000000000000000000000001510705540100243325ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/ObjCCodeGenerator/CMakeLists.txt000066400000000000000000000006561510705540100271010ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB SEMA_NATIVE_FFI_OBJ_C_CODE_GENERATOR_SRC *.cpp) set(SEMA_NATIVE_FFI_OBJ_C_SRC ${SEMA_NATIVE_FFI_OBJ_C_SRC} ${SEMA_NATIVE_FFI_OBJ_C_CODE_GENERATOR_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/ObjCCodeGenerator/ObjCGenerator.cpp000066400000000000000000000742411510705540100275320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements class for objc code generation. */ #include #include "NativeFFI/ObjC/Utils/ASTFactory.h" #include "NativeFFI/Utils.h" #include "cangjie/Utils/FileUtil.h" #include "NativeFFI/ObjC/Utils/Common.h" #include "cangjie/Sema/TypeManager.h" #include "ObjCGenerator.h" namespace { constexpr auto INTERFACE_KEYWORD = "@interface"; constexpr auto IMPORT_KEYWORD = "#import"; constexpr auto PROPERTY_KEYWORD = "@property"; constexpr auto END_KEYWORD = "@end"; constexpr auto STRUCT_KEYWORD = "struct"; constexpr auto RTIME_PARAM_KEYWORD = "RuntimeParam"; constexpr auto IMPL_KEYWORD = "@implementation"; constexpr auto RETURN_KEYWORD = "return"; constexpr auto SUPER_KEYWORD = "super"; constexpr auto CLASS_KEYWORD = "class"; constexpr auto SETTER_KEYWORD = "setter="; constexpr auto GETTER_KEYWORD = "getter="; constexpr auto FOUNDATION_IMPORT = ""; constexpr auto STDDEF_IMPORT = ""; constexpr auto CJ_IMPORT = "\"Cangjie.h\""; constexpr auto DLFCN_IMPORT = ""; constexpr auto STDLIB_IMPORT = ""; constexpr auto READWRITE_MODIFIER = "readwrite"; constexpr auto READONLY_MODIFIER = "readonly"; constexpr auto STATIC_FUNC_MODIFIER = "+"; constexpr auto INSTANCE_MODIFIER = "-"; constexpr auto STATIC_MODIFIER = "static"; constexpr auto VOID_TYPE = "void"; constexpr auto VOID_POINTER_TYPE = "void*"; constexpr auto ID_TYPE = "id"; constexpr auto UNSUPPORTED_TYPE = "UNSUPPORTED_TYPE"; constexpr auto INT64_T = "int64_t"; constexpr auto SELF_WEAKLINK_NAME = "$registryId"; constexpr auto SELF_NAME = "self"; constexpr auto SETTER_PARAM_NAME = "value"; constexpr auto INITIALIZE_FUNC_NAME = "initialize"; constexpr auto INIT_FUNC_NAME = "init"; constexpr auto DELETE_FUNC_NAME = "deleteCJObject"; constexpr auto DEALLOC_FUNC_NAME = "dealloc"; constexpr auto INIT_CJ_RUNTIME_NAME = "InitCJRuntime"; constexpr auto NSLOG_FUNC_NAME = "NSLog"; constexpr auto EXIT_FUNC_NAME = "exit"; constexpr auto DLOPEN_FUNC_NAME = "dlopen"; constexpr auto DLERROR_FUNC_NAME = "dlerror"; constexpr auto DLSYM_FUNC_NAME = "dlsym"; constexpr auto LOAD_LIB_FUNC_NAME = "LoadCJLibraryWithInit"; constexpr auto CAST_TO_VOID_PTR = "(__bridge void*)"; constexpr auto CJ_DLL_HANLDE = "CJWorldDLHandle"; constexpr auto CJ_RTIME_PARAMS = "defaultCJRuntimeParams"; constexpr auto EQ_OP = "="; constexpr auto EQ_CHECK_OP = "=="; constexpr auto NOT_EQ_OP = "!="; constexpr auto E_OK = "E_OK"; constexpr auto RTLD_LAZY = "RTLD_LAZY"; constexpr auto NULL_KW = "NULL"; constexpr auto FAIL_INIT_CJ_RT_MSG = "@\"ERROR: Failed to initialize Cangjie runtime\""; constexpr auto FAIL_INIT_CJ_LIB = "@\"ERROR: Failed to init cjworld library \""; constexpr auto FAIL_OPEN_CJ_LIB = "@\"ERROR: Failed to open cjworld library \""; constexpr auto FAIL_FIND_SYM_1 = "@\"ERROR: Failed to find "; constexpr auto FAIL_FIND_SYM_2 = " symbol in cjworld\""; constexpr auto FAIL_CALL_UNINIT_CTOR = "[self doesNotRecognizeSelector:_cmd];"; constexpr auto STRING_FORMAT = "@\"%s\""; constexpr auto INDENT_SIZE = 4; } // namespace namespace Cangjie::Interop::ObjC { using namespace Cangjie; using namespace AST; using namespace Native::FFI; using std::string; std::string ToDecl(ObjCFunctionType fType) { if (fType == ObjCFunctionType::STATIC) { return STATIC_FUNC_MODIFIER; } return INSTANCE_MODIFIER; } ObjCGenerator::ObjCGenerator( InteropContext& ctx, Ptr decl, const std::string& outputFilePath, const std::string& cjLibOutputPath) : outputFilePath(outputFilePath), cjLibOutputPath(cjLibOutputPath), classDecl(decl), ctx(ctx) { } /* Main access point to translation of CJ class to Objective C. Generates two files - .h and .m Example of header, .h: #import ... @interface A : M + (void)initialize; - (id)init; @property (readwrite) UIntNative obj; - (void)goo; ... @end Example of source, .m: #import "A.h" ... static void (*CJImpl_ObjC_A_goo)(size_t) = NULL; ... @implementation A + (void)initialize { ... } ... - (void)goo { CJImpl_ObjC_A_goo(self.obj); } ... @end */ void ObjCGenerator::Generate() { const auto objCDeclName = ctx.nameGenerator.GetObjCDeclName(*classDecl); AddWithIndent(GenerateImport(FOUNDATION_IMPORT), GenerationTarget::HEADER); AddWithIndent(GenerateImport(STDDEF_IMPORT), GenerationTarget::HEADER); AddWithIndent(GenerateImport("\"" + objCDeclName + ".h\""), GenerationTarget::SOURCE); AddWithIndent(GenerateImport(CJ_IMPORT), GenerationTarget::SOURCE); AddWithIndent(GenerateImport(DLFCN_IMPORT), GenerationTarget::SOURCE); AddWithIndent(GenerateImport(STDLIB_IMPORT), GenerationTarget::SOURCE); GenerateStaticFunctionsReferences(); AddWithIndent(GenerateStaticReference(CJ_DLL_HANLDE, string(VOID_TYPE) + "*", NULL_KW), GenerationTarget::SOURCE); AddWithIndent(GenerateStaticReference(CJ_RTIME_PARAMS, string(STRUCT_KEYWORD) + " " + RTIME_PARAM_KEYWORD, "{0}"), GenerationTarget::SOURCE); GenerateInterfaceDecl(); AddConstructors(); AddWithIndent(GenerateFunctionDeclaration( ObjCFunctionType::STATIC, VOID_TYPE, INITIALIZE_FUNC_NAME), GenerationTarget::BOTH, OptionalBlockOp::OPEN); AddWithIndent(GenerateIfStatement(SELF_NAME, GenerateObjCCall(objCDeclName, CLASS_KEYWORD), EQ_CHECK_OP), GenerationTarget::SOURCE, OptionalBlockOp::OPEN); AddWithIndent(GenerateIfStatement(GenerateCCall(INIT_CJ_RUNTIME_NAME, std::vector{"&" + string(CJ_RTIME_PARAMS)}), E_OK, NOT_EQ_OP), GenerationTarget::SOURCE, OptionalBlockOp::OPEN); AddWithIndent(GenerateCCall(NSLOG_FUNC_NAME, std::vector{FAIL_INIT_CJ_RT_MSG}) + ";", GenerationTarget::SOURCE); AddWithIndent(GenerateCCall(EXIT_FUNC_NAME, std::vector{"1"}) + ";", GenerationTarget::SOURCE); CloseBlock(false, true); auto cjLibName = Native::FFI::GetCangjieLibName(cjLibOutputPath, classDecl->fullPackageName, false); // Crutch that solves issue when cjLibOutputPath is dir or not provided. if (cjLibName.find("lib", 0) != 0) { cjLibName = "lib" + cjLibName; } size_t extIdx = cjLibName.find_last_of("."); if (extIdx == std::string::npos || cjLibName.substr(extIdx) != ".dylib") { cjLibName += ".dylib"; // what if we compile on linux target? } cjLibName = "\"" + cjLibName + "\""; AddWithIndent(GenerateIfStatement(GenerateCCall(LOAD_LIB_FUNC_NAME, std::vector{cjLibName}), E_OK, NOT_EQ_OP), GenerationTarget::SOURCE, OptionalBlockOp::OPEN); AddWithIndent(GenerateCCall(NSLOG_FUNC_NAME, std::vector{FAIL_INIT_CJ_LIB}) + ";", GenerationTarget::SOURCE); AddWithIndent(GenerateCCall(EXIT_FUNC_NAME, std::vector{"1"}) + ";", GenerationTarget::SOURCE); CloseBlock(false, true); AddWithIndent( GenerateIfStatement( "(" + GenerateAssignment( CJ_DLL_HANLDE, GenerateCCall( DLOPEN_FUNC_NAME, std::vector{cjLibName, RTLD_LAZY})) + ")", NULL_KW, EQ_CHECK_OP), GenerationTarget::SOURCE, OptionalBlockOp::OPEN); AddWithIndent(GenerateCCall(NSLOG_FUNC_NAME, std::vector{FAIL_OPEN_CJ_LIB}) + ";", GenerationTarget::SOURCE); AddWithIndent(GenerateCCall(NSLOG_FUNC_NAME, std::vector{ STRING_FORMAT, GenerateCCall(DLERROR_FUNC_NAME)}) + ";", GenerationTarget::SOURCE); AddWithIndent(GenerateCCall(EXIT_FUNC_NAME, std::vector{"1"}) + ";", GenerationTarget::SOURCE); CloseBlock(false, true); GenerateFunctionSymbolsInitialization(); CloseBlock(false, true); CloseBlock(false, true); AddProperties(); AddMethods(); AddWithIndent(GenerateFunctionDeclaration(ObjCFunctionType::INSTANCE, VOID_TYPE, DELETE_FUNC_NAME), GenerationTarget::BOTH, OptionalBlockOp::OPEN); AddWithIndent(GenerateDefaultFunctionImplementation(ctx.nameGenerator.GenerateDeleteCjObjectName(*classDecl), *ctx.typeManager.GetPrimitiveTy(TypeKind::TYPE_UNIT), {std::pair( INT64_T, string(SELF_NAME) + "." + SELF_WEAKLINK_NAME)}), GenerationTarget::SOURCE, OptionalBlockOp::CLOSE); AddWithIndent(GenerateFunctionDeclaration( ObjCFunctionType::INSTANCE, VOID_TYPE, DEALLOC_FUNC_NAME), GenerationTarget::BOTH, OptionalBlockOp::OPEN); AddWithIndent(GenerateObjCCall(SELF_NAME, DELETE_FUNC_NAME) + ";", GenerationTarget::SOURCE, OptionalBlockOp::CLOSE); AddWithIndent(END_KEYWORD, GenerationTarget::BOTH); WriteToFile(); } /* ------------------indent handling-------------------- */ void ObjCGenerator::OpenBlock() { // needed only in source file generation resSource += " {"; currentBlockIndent += INDENT_SIZE; } void ObjCGenerator::CloseBlock(bool newLineBefore = false, bool newLineAfter = false) { currentBlockIndent -= INDENT_SIZE; resSource += (newLineBefore ? "\n" : "") + std::string(currentBlockIndent, ' ') + "}" + (newLineAfter ? "\n" : ""); } void ObjCGenerator::AddWithIndent(const std::string& s, const GenerationTarget target, const OptionalBlockOp bOp) { if (target == GenerationTarget::HEADER || target == GenerationTarget::BOTH) { res += s; if (bOp == OptionalBlockOp::OPEN) { res += ";"; } res += "\n"; } if (target == GenerationTarget::SOURCE || target == GenerationTarget::BOTH) { resSource += std::string(currentBlockIndent, ' '); resSource += s; if (bOp == OptionalBlockOp::OPEN) { OpenBlock(); } if (bOp == OptionalBlockOp::CLOSE) { CloseBlock(true); } resSource += "\n"; } } /* ------------------------------------------------------ */ /* * return statement; */ std::string ObjCGenerator::GenerateReturn(const std::string& statement) const { return string(RETURN_KEYWORD) + " " + statement + ";"; } /* * lhs = rhs */ std::string ObjCGenerator::GenerateAssignment(const std::string& lhs, const std::string& rhs) const { return lhs + " = " + rhs; } /* * if (lhs op rhs) */ std::string ObjCGenerator::GenerateIfStatement(const std::string& lhs, const std::string& rhs, const std::string& op) const { return "if (" + lhs + " " + op + " " + rhs + ")"; } /* * objective c style function call * [lhs rhs] */ std::string ObjCGenerator::GenerateObjCCall(const std::string& lhs, const std::string& rhs) const { return "[" + lhs + " " + rhs + "]"; } /* * funcName(arg1, arg2, ... argN) */ std::string ObjCGenerator::GenerateCCall(const std::string& funcName, const std::vector args) const { std::string result = funcName + "("; if (args.size() != 0) { for (size_t i = 0; i < args.size(); i++) { result += args.at(i); if (i != args.size() - 1) { result += ", "; } } } result += ")"; return result; } /* * return CJImpl_ObjC_cjworld_A_goo(arg1, arg2, ... arg3); */ std::string ObjCGenerator::GenerateDefaultFunctionImplementation( const std::string& name, const Ty& retTy, const ArgsList args, const ObjCFunctionType type) const { std::string result = retTy.IsUnit() ? "" : RETURN_KEYWORD; result += " "; if (ctx.typeMapper.IsValidObjCMirror(retTy) || ctx.typeMapper.IsObjCImpl(retTy)) { result += "(__bridge " + ctx.typeMapper.Cj2ObjCForObjC(retTy) + ")"; } result += name + "("; if (type != ObjCFunctionType::INSTANCE) { result += ");"; return result; } if (args.size() != 0) { for (size_t i = 0; i < args.size(); i++) { result += args.at(i).second; if (i != args.size() - 1) { result += ", "; } } } else { result += string(SELF_NAME) + "." + SELF_WEAKLINK_NAME; } result += ");"; return result; } /* * - (returnType)name */ std::string ObjCGenerator::GenerateFunctionDeclaration( const ObjCFunctionType type, const std::string& returnType, const std::string& name) const { std::string result = ""; result += ToDecl(type) + " "; result += "(" + returnType + ")"; result += name; return result; } /** * @property (, readwrite, getter=name, ) type name; * Note: <...> are optional. */ std::string ObjCGenerator::GeneratePropertyDeclaration( const ObjCFunctionType staticType, const std::string& mode, const std::string& type, const std::string& name) const { std::string result = ""; if (name == "" || type == "") { return result; } result += PROPERTY_KEYWORD; result += " ("; if (staticType == ObjCFunctionType::STATIC) { result += CLASS_KEYWORD; result += ", "; } if (mode != "") { result += mode; } if (name != SELF_WEAKLINK_NAME) { result += ", "; result += GETTER_KEYWORD + name; if (mode == READWRITE_MODIFIER) { std::string capitalizedName = name; std::transform(capitalizedName.begin(), capitalizedName.begin() + 1, capitalizedName.begin(), [](unsigned char c) { return std::toupper(c); }); const auto& setterName = "set" + capitalizedName + ":"; result += ", "; result += SETTER_KEYWORD + setterName; } } result += ") "; result += type + " "; result += name + ";"; return result; } /* * #import name */ std::string ObjCGenerator::GenerateImport(const std::string& name) { return string(IMPORT_KEYWORD) + " " + name; } void ObjCGenerator::GenerateStaticFunctionsReferences() { for (auto& declPtr : ctx.genDecls) { // Must be filtered out earlier if (declPtr->curFile != classDecl->curFile) { continue; } if (declPtr->astKind == ASTKind::FUNC_DECL) { const auto& funcDecl = *StaticAs(declPtr.get()); auto& retTy = *funcDecl.funcBody->retType->ty; const auto retType = ctx.typeMapper.IsValidObjCMirror(retTy) || ctx.typeMapper.IsObjCImpl(retTy) ? VOID_POINTER_TYPE : ctx.typeMapper.Cj2ObjCForObjC(retTy); std::string argTypes = ""; for (auto& arg: funcDecl.funcBody->paramLists[0]->params) { argTypes += ctx.typeMapper.Cj2ObjCForObjC(*arg->ty) + ","; } // trim unnecessary "," argTypes.erase(std::find_if(argTypes.rbegin(), argTypes.rend(), [](auto c) { return c != ','; }).base(), argTypes.end()); AddWithIndent( GenerateStaticFunctionReference(funcDecl.identifier, retType, argTypes), GenerationTarget::SOURCE); } } } /* * static type (*CJImpl_ObjC_A_funcName)(arg1Type, ... argNType) = NULL; */ std::string ObjCGenerator::GenerateStaticFunctionReference( const std::string& funcName, const std::string& retType, const std::string& argTypes) const { std::string result = STATIC_MODIFIER; result += " "; result += retType + " (*"; result += funcName + ")"; result += "(" + argTypes + ")"; result += " = " + string(NULL_KW) + ";"; return result; } /* * static type name = defaultValue; */ std::string ObjCGenerator::GenerateStaticReference( const std::string& name, const std::string& type, const std::string& defaultValue) const { std::string result = STATIC_MODIFIER; result += " "; result += type + " "; result += name; if (defaultValue != "") { result += " = " + defaultValue + ";"; } return result; } /* * Generate code to load baked and dynamic functions from dylib */ void ObjCGenerator::GenerateFunctionSymbolsInitialization() { for (OwnedPtr& declPtr : ctx.genDecls) { if (declPtr->astKind == ASTKind::FUNC_DECL) { const FuncDecl& funcDecl = *StaticAs(declPtr.get()); GenerateFunctionSymInit(funcDecl.identifier); } } } /* * if ((CJImpl_ObjC_A_name = dlsym(CJWorldDLHandle, "CJImpl_ObjC_A_name")) == NULL) { * NSLog(@"ERROR: Failed to find CJImpl_ObjC_A_name symbol in cjworld"); * exit(1); * } */ void ObjCGenerator::GenerateFunctionSymInit(const std::string& fName) { AddWithIndent(GenerateIfStatement( "(" + GenerateAssignment(fName, GenerateCCall(DLSYM_FUNC_NAME, std::vector{CJ_DLL_HANLDE, "\"" + fName + "\""})) + ")", NULL_KW, EQ_CHECK_OP), GenerationTarget::SOURCE, OptionalBlockOp::OPEN); AddWithIndent( GenerateCCall(NSLOG_FUNC_NAME, std::vector{FAIL_FIND_SYM_1 + fName + FAIL_FIND_SYM_2}) + ";", GenerationTarget::SOURCE); AddWithIndent(GenerateCCall(EXIT_FUNC_NAME, std::vector{"1"}) + ";", GenerationTarget::SOURCE); CloseBlock(false, true); } /* * HEADER: @interface A : M * SOURCE: @implementation A */ void ObjCGenerator::GenerateInterfaceDecl() { std::string resultH = ""; std::string resultS = ""; Ptr superClassPtr = classDecl->GetSuperClassDecl(); bool isClassInheritedFromClass = superClassPtr && superClassPtr->identifier.Val() != Cangjie::OBJECT_NAME; if (isClassInheritedFromClass) { AddWithIndent(GenerateImport("\"" + superClassPtr->identifier.Val() + ".h\""), GenerationTarget::HEADER); } auto objCDeclName = ctx.nameGenerator.GetObjCDeclName(*classDecl); resultH += INTERFACE_KEYWORD; resultH += " "; resultH += objCDeclName; if (isClassInheritedFromClass) { resultH += " : "; resultH += superClassPtr->identifier.Val(); } AddWithIndent(resultH, GenerationTarget::HEADER); resultS += IMPL_KEYWORD; resultS += " "; resultS += objCDeclName; AddWithIndent(resultS, GenerationTarget::SOURCE); } /* * DECLARATION: :(arg1Type)arg1:(arg2Type)arg2:...(argNType)argN * STATIC_REF: (arg1Type, arg2Type, ...argNType) */ std::string ObjCGenerator::GenerateFuncParamLists( const std::vector>& paramLists, FunctionListFormat format, const ObjCFunctionType type) { std::string genParams = format == FunctionListFormat::DECLARATION ? "" : "("; if (paramLists.empty() || !paramLists[0]) { return ""; } for (size_t i = 0; i < paramLists[0]->params.size(); i++) { OwnedPtr& cur = paramLists[0]->params[i]; switch (format) { case FunctionListFormat::DECLARATION: if (i != 0) { genParams += cur->identifier.Val() + ":"; // label } genParams += "(" + MapCJTypeToObjCType(cur) + ")"; genParams += cur->identifier.Val(); if (i != paramLists[0]->params.size() - 1) { genParams += " "; } break; case FunctionListFormat::STATIC_REF: if (paramLists[0]->params.size() == 0 && type != ObjCFunctionType::STATIC) { genParams += VOID_POINTER_TYPE; } genParams += ")"; break; case FunctionListFormat::CANGJIE_DECL: genParams += cur->identifier.Val() + ": " + Ty::ToString(cur->type->ty); if (i != paramLists[0]->params.size() - 1) { genParams += ", "; } break; default: break; } } if (format == FunctionListFormat::STATIC_REF || format == FunctionListFormat::CANGJIE_DECL) { if (paramLists[0]->params.size() == 0 && type != ObjCFunctionType::STATIC) { genParams += VOID_POINTER_TYPE; } genParams += ")"; } return genParams; } std::string ObjCGenerator::GenerateSetterParamLists(const std::string& type) const { return ":(" + type + ")" + SETTER_PARAM_NAME; } /* * Iterate over properties and generate declarations */ void ObjCGenerator::AddProperties() { auto registryIdType = CreateType(TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64)); AddWithIndent( GeneratePropertyDeclaration(ObjCFunctionType::INSTANCE, READWRITE_MODIFIER, INT64_T, SELF_WEAKLINK_NAME) ); for (OwnedPtr& declPtr : classDecl->body->decls) { if (declPtr->astKind != ASTKind::VAR_DECL && declPtr->astKind != ASTKind::PROP_DECL) { continue; } if (!declPtr->TestAttr(Attribute::PUBLIC)) { continue; } if (ctx.factory.IsGeneratedNativeHandleField(*declPtr)) { continue; } const auto& varDecl = *As(declPtr.get()); const auto& staticType = varDecl.TestAttr(Attribute::STATIC) ? ObjCFunctionType::STATIC : ObjCFunctionType::INSTANCE; const std::string& type = ctx.typeMapper.Cj2ObjCForObjC(*varDecl.ty); const auto modeModifier = varDecl.isVar ? READWRITE_MODIFIER : READONLY_MODIFIER; const auto name = ctx.nameGenerator.GetObjCDeclName(varDecl); AddWithIndent(GeneratePropertyDeclaration(staticType, modeModifier, type, name)); std::string getterResult = ""; getterResult += GenerateFunctionDeclaration(staticType, type, name); AddWithIndent(getterResult, GenerationTarget::BOTH, OptionalBlockOp::OPEN); AddWithIndent( GenerateDefaultFunctionImplementation(ctx.nameGenerator.GetFieldGetterWrapperName(varDecl), *varDecl.ty, {std::pair(INT64_T, string(SELF_NAME) + "." + SELF_WEAKLINK_NAME)}, staticType), GenerationTarget::SOURCE, OptionalBlockOp::CLOSE); if (!varDecl.isVar) { continue; } std::string setterName = name; std::transform(setterName.begin(), setterName.begin() + 1, setterName.begin(), [](unsigned char c) { return std::toupper(c); }); setterName = "set" + setterName; ArgsList setterArgsList = ArgsList(); setterArgsList.emplace_back( std::pair(INT64_T, string(SELF_NAME) + "." + SELF_WEAKLINK_NAME)); const auto setterArg = ctx.typeMapper.IsValidObjCMirror(*varDecl.ty) || ctx.typeMapper.IsObjCImpl(*varDecl.ty) ? std::string(CAST_TO_VOID_PTR) + SETTER_PARAM_NAME : SETTER_PARAM_NAME; setterArgsList.emplace_back(std::pair(type, setterArg)); std::string setterResult = ""; setterResult += GenerateFunctionDeclaration(staticType, VOID_TYPE, setterName); setterResult += GenerateSetterParamLists(type); AddWithIndent(setterResult, GenerationTarget::BOTH, OptionalBlockOp::OPEN); AddWithIndent( GenerateDefaultFunctionImplementation( ctx.nameGenerator.GetFieldSetterWrapperName(varDecl), *ctx.typeManager.GetPrimitiveTy(TypeKind::TYPE_UNIT), setterArgsList, staticType), GenerationTarget::SOURCE, OptionalBlockOp::CLOSE); } } void ObjCGenerator::AddConstructors() { std::set generatedCtors = {}; for (OwnedPtr& declPtr : classDecl->body->decls) { if (!declPtr->TestAttr(Attribute::PUBLIC)) { continue; } if (ctx.factory.IsGeneratedMember(*declPtr.get())) { continue; } if (!declPtr->TestAttr(Attribute::CONSTRUCTOR)) { continue; } if (declPtr->astKind != ASTKind::FUNC_DECL) { // skip primary ctor, as it is desugared to init already continue; } const FuncDecl& funcDecl = *StaticAs(declPtr.get()); if (!funcDecl.funcBody) { continue; } const auto generatedCtor = ctx.factory.GetGeneratedImplCtor(*classDecl, funcDecl); CJC_ASSERT(generatedCtor); const std::string& retType = ID_TYPE; const auto objCName = ctx.nameGenerator.GetObjCDeclName(funcDecl); // wrapper name MUST use generated ctor const auto cjWrapperName = ctx.nameGenerator.GenerateInitCjObjectName(*generatedCtor); const auto staticType = ObjCFunctionType::INSTANCE; std::string result = ""; result += GenerateFunctionDeclaration(staticType, retType, objCName); result += GenerateFuncParamLists(funcDecl.funcBody->paramLists, FunctionListFormat::DECLARATION, staticType); AddWithIndent(result, GenerationTarget::BOTH, OptionalBlockOp::OPEN); AddWithIndent(GenerateIfStatement(SELF_NAME, GenerateObjCCall(SUPER_KEYWORD, INIT_FUNC_NAME), EQ_OP), GenerationTarget::SOURCE, OptionalBlockOp::OPEN); AddWithIndent(GenerateAssignment(string(SELF_NAME) + "." + SELF_WEAKLINK_NAME, GenerateCCall(cjWrapperName, ConvertParamsListToCallableParamsString(funcDecl.funcBody->paramLists, true))) + ";", GenerationTarget::SOURCE, OptionalBlockOp::CLOSE); AddWithIndent(GenerateReturn(SELF_NAME), GenerationTarget::SOURCE, OptionalBlockOp::CLOSE); generatedCtors.insert(objCName); } auto parentCtors = ctx.factory.GetAllParentCtors(*classDecl); std::set processedCtors = {}; for (auto ctor : parentCtors) { if (ctor->funcBody->paramLists[0]->params.empty()) { continue; } auto mangledName = ctx.nameGenerator.GetObjCDeclName(*ctor); if (std::find(generatedCtors.begin(), generatedCtors.end(), mangledName) != generatedCtors.end()) { continue; } if (std::find(processedCtors.begin(), processedCtors.end(), mangledName) != processedCtors.end()) { continue; } auto generatedName = ctx.nameGenerator.GetObjCDeclName(*ctor); std::string result = ""; result += GenerateFunctionDeclaration(ObjCFunctionType::INSTANCE, ID_TYPE, generatedName); result += GenerateFuncParamLists(ctor->funcBody->paramLists, FunctionListFormat::DECLARATION, ObjCFunctionType::INSTANCE); AddWithIndent(result, GenerationTarget::BOTH, OptionalBlockOp::OPEN); AddWithIndent(FAIL_CALL_UNINIT_CTOR, GenerationTarget::SOURCE, OptionalBlockOp::CLOSE); processedCtors.insert(mangledName); } } /* * Iterate over methods and generate default implementations */ void ObjCGenerator::AddMethods() { for (OwnedPtr& declPtr : classDecl->body->decls) { if (ctx.factory.IsGeneratedMember(*declPtr.get())) { continue; } if (!declPtr->TestAttr(Attribute::PUBLIC)) { continue; } if (declPtr->astKind == ASTKind::FUNC_DECL && !declPtr->TestAnyAttr(Attribute::CONSTRUCTOR, Attribute::FINALIZER)) { const FuncDecl& funcDecl = *StaticAs(declPtr.get()); if (funcDecl.funcBody && funcDecl.funcBody->retType) { auto staticType = funcDecl.TestAttr(Attribute::STATIC) ? ObjCFunctionType::STATIC : ObjCFunctionType::INSTANCE; auto retTy = funcDecl.funcBody->retType->ty; const std::string& retType = MapCJTypeToObjCType(funcDecl.funcBody->retType); const auto objCName = ctx.nameGenerator.GetObjCDeclName(funcDecl); std::string result = ""; result += GenerateFunctionDeclaration(staticType, retType, objCName); result += GenerateFuncParamLists(funcDecl.funcBody->paramLists, FunctionListFormat::DECLARATION, staticType); AddWithIndent(result, GenerationTarget::BOTH, OptionalBlockOp::OPEN); AddWithIndent( GenerateDefaultFunctionImplementation(ctx.nameGenerator.GenerateMethodWrapperName(funcDecl), *retTy, ConvertParamsListToArgsList(funcDecl.funcBody->paramLists, true), staticType), GenerationTarget::SOURCE, OptionalBlockOp::CLOSE); } } } } ArgsList ObjCGenerator::ConvertParamsListToArgsList( const std::vector>& paramLists, bool withRegistryId) { ArgsList result = ArgsList(); if (withRegistryId) { result.emplace_back(std::pair(INT64_T, string(SELF_NAME) + "." + SELF_WEAKLINK_NAME)); } if (!paramLists.empty() && paramLists[0]) { for (size_t i = 0; i < paramLists[0]->params.size(); i++) { OwnedPtr& cur = paramLists[0]->params[i]; result.push_back(std::pair(MapCJTypeToObjCType(cur), cur->identifier.Val())); } } return result; } std::vector ObjCGenerator::ConvertParamsListToCallableParamsString( std::vector>& paramLists, bool withSelf) const { std::vector result = {}; if (withSelf) { result.emplace_back(std::string(CAST_TO_VOID_PTR) + SELF_NAME); } if (!paramLists.empty() && paramLists[0]) { for (size_t i = 0; i < paramLists[0]->params.size(); i++) { OwnedPtr& cur = paramLists[0]->params[i]; result.push_back(cur->identifier.Val()); } } return result; } void ObjCGenerator::WriteToFile() { auto objCDeclName = ctx.nameGenerator.GetObjCDeclName(*classDecl); auto headerPath = outputFilePath + "/" + objCDeclName + ".h"; FileUtil::WriteToFile(headerPath, res); auto sourcePath = outputFilePath + "/" + objCDeclName + ".m"; FileUtil::WriteToFile(sourcePath, resSource); } std::string ObjCGenerator::MapCJTypeToObjCType(const OwnedPtr& type) { if (!type) { return UNSUPPORTED_TYPE; } return ctx.typeMapper.Cj2ObjCForObjC(*type->ty); } std::string ObjCGenerator::MapCJTypeToObjCType(const OwnedPtr& param) { if (!param) { return UNSUPPORTED_TYPE; } return ctx.typeMapper.Cj2ObjCForObjC(*param->type->ty); } } // namespace Cangjie::Interop::ObjC cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/ObjCCodeGenerator/ObjCGenerator.h000066400000000000000000000077551510705540100272050ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares class for Objective-C code generation. */ #ifndef CANGJIE_SEMA_OBJC_GENERATOR_H #define CANGJIE_SEMA_OBJC_GENERATOR_H #include #include #include "cangjie/AST/Match.h" #include "cangjie/AST/Types.h" #include "NativeFFI/ObjC/AfterTypeCheck/Interop/Context.h" namespace Cangjie::Interop::ObjC { using namespace AST; enum class ObjCFunctionType { STATIC, INSTANCE }; enum class GenerationTarget { HEADER, SOURCE, BOTH }; enum class FunctionListFormat { DECLARATION, STATIC_REF, CANGJIE_DECL }; enum class OptionalBlockOp { OPEN, CLOSE, NONE }; using ArgsList = std::vector>; class ObjCGenerator { public: ObjCGenerator(InteropContext& ctx, Ptr decl, const std::string& outputFilePath, const std::string& cjLibOutputPath); void Generate(); private: std::string res; std::string resSource; const std::string& outputFilePath; const std::string& cjLibOutputPath; size_t currentBlockIndent = 0; Ptr classDecl; InteropContext& ctx; void OpenBlock(); void CloseBlock(bool newLineBefore, bool newLineAfter); void AddWithIndent(const std::string& s, const GenerationTarget target = GenerationTarget::HEADER, const OptionalBlockOp bOp = OptionalBlockOp::NONE); std::string GenerateReturn(const std::string& statement) const; std::string GenerateAssignment(const std::string& lhs, const std::string& rhs) const; std::string GenerateIfStatement(const std::string& lhs, const std::string& rhs, const std::string& op) const; std::string GenerateObjCCall(const std::string& lhs, const std::string& rhs) const; std::string GenerateCCall( const std::string& funcName, const std::vector args = std::vector()) const; std::string GenerateDefaultFunctionImplementation(const std::string& name, const Ty& retTy, const ArgsList args = ArgsList(), const ObjCFunctionType = ObjCFunctionType::INSTANCE) const; std::string GenerateFunctionDeclaration( const ObjCFunctionType type, const std::string& returnType, const std::string& name) const; std::string GeneratePropertyDeclaration(const ObjCFunctionType staticType, const std::string& mode, const std::string& type, const std::string& name) const; std::string GenerateImport(const std::string& name); std::string GenerateStaticFunctionReference( const std::string& funcName, const std::string& retType, const std::string& argTypes) const; std::string GenerateStaticReference(const std::string& name, const std::string& type, const std::string& defaultValue) const; std::string GenerateFuncParamLists(const std::vector>& paramLists, FunctionListFormat format = FunctionListFormat::DECLARATION, const ObjCFunctionType type = ObjCFunctionType::INSTANCE); std::string MapCJTypeToObjCType(const OwnedPtr& type); std::string MapCJTypeToObjCType(const OwnedPtr& param); ArgsList ConvertParamsListToArgsList( const std::vector>& paramLists, bool withRegistryId); std::vector ConvertParamsListToCallableParamsString( std::vector>& paramLists, bool withSelf) const; std::string GenerateSetterParamLists(const std::string& type) const; void GenerateStaticFunctionsReferences(); void GenerateFunctionSymbolsInitialization(); void GenerateFunctionSymInit(const std::string& fName); void GenerateInterfaceDecl(); void AddProperties(); void AddConstructors(); void AddMethods(); void WriteToFile(); }; } // namespace Cangjie::Interop::ObjC #endif // CANGJIE_SEMA_OBJC_GENERATOR_H cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/TypeCheck/000077500000000000000000000000001510705540100227325ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/TypeCheck/CMakeLists.txt000066400000000000000000000006451510705540100254770ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB SEMA_NATIVE_FFI_OBJ_C_TYPECHECK_SRC *.cpp) set(SEMA_NATIVE_FFI_OBJ_C_SRC ${SEMA_NATIVE_FFI_OBJ_C_SRC} ${SEMA_NATIVE_FFI_OBJ_C_TYPECHECK_SRC} PARENT_SCOPE) cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/TypeCheck/CheckAbstractClass.cpp000066400000000000000000000015131510705540100271250ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements check that Objective-C mirror declaration is not an abstract class, as it is not supported yet. */ #include "Handlers.h" #include "cangjie/AST/Match.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; void CheckAbstractClass::HandleImpl(TypeCheckContext& ctx) { if (auto cd = As(&ctx.target); cd && cd->TestAttr(Attribute::ABSTRACT)) { ctx.diag.DiagnoseRefactor(DiagKindRefactor::sema_objc_interop_not_supported, *cd, "abstract"); cd->EnableAttr(Attribute::IS_BROKEN); } }cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/TypeCheck/CheckImplInheritMirror.cpp000066400000000000000000000015771510705540100300250ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements check that @ObjCImpl annotated declaration inherits an @ObjCMirror declaration. */ #include "Handlers.h" #include "cangjie/AST/Match.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; void CheckImplInheritMirror::HandleImpl(TypeCheckContext& ctx) { if (!ctx.typeMapper.IsObjCImpl(*ctx.target.ty)) { return; } if (ctx.typeMapper.IsObjCMirrorSubtype(*ctx.target.ty)) { return; } ctx.diag.DiagnoseRefactor(DiagKindRefactor::sema_objc_mirror_subtype_must_inherit_mirror, ctx.target); ctx.target.EnableAttr(Attribute::IS_BROKEN); }cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/TypeCheck/CheckInterface.cpp000066400000000000000000000014551510705540100263010ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements check that Objective-C mirror declaration is not an interface, as it is not supported yet. */ #include "Handlers.h" #include "cangjie/AST/Match.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; void CheckInterface::HandleImpl(TypeCheckContext& ctx) { if (auto id = As(&ctx.target); id) { ctx.diag.DiagnoseRefactor(DiagKindRefactor::sema_objc_interop_not_supported, id->keywordPos, "interface"); id->EnableAttr(Attribute::IS_BROKEN); } }cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/TypeCheck/CheckMemberTypes.cpp000066400000000000000000000063271510705540100266400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements checks for Objective-C mirror/subtype member declarations. */ #include "Handlers.h" #include "cangjie/AST/Match.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; void CheckMemberTypes::HandleImpl(TypeCheckContext& ctx) { auto isMirrorSubtype = ctx.typeMapper.IsObjCMirrorSubtype(ctx.target); for (auto& decl : ctx.target.GetMemberDeclPtrs()) { // Only public members of exported declarations must be checked. if (isMirrorSubtype && !decl->TestAttr(Attribute::PUBLIC)) { continue; } switch (decl->astKind) { case ASTKind::FUNC_DECL: CheckFuncTypes(*StaticAs(decl), ctx); break; case ASTKind::PROP_DECL: CheckPropTypes(*StaticAs(decl), ctx); break; case ASTKind::VAR_DECL: CheckVarTypes(*StaticAs(decl), ctx); break; default: break; } } } void CheckMemberTypes::CheckPropTypes(PropDecl& pd, TypeCheckContext& ctx) { if (ctx.typeMapper.IsObjCCompatible(*pd.ty)) { return; } ctx.diag.DiagnoseRefactor(DiagKindRefactor::sema_objc_mirror_prop_must_be_objc_compatible, *pd.type); pd.EnableAttr(Attribute::IS_BROKEN); } void CheckMemberTypes::CheckVarTypes(VarDecl& vd, TypeCheckContext& ctx) { if (ctx.typeMapper.IsObjCCompatible(*vd.ty)) { return; } ctx.diag.DiagnoseRefactor(DiagKindRefactor::sema_objc_mirror_field_must_be_objc_compatible, *vd.type); vd.EnableAttr(Attribute::IS_BROKEN); } void CheckMemberTypes::CheckFuncTypes(FuncDecl& fd, TypeCheckContext& ctx) { if (!fd.funcBody) { return; } if (!fd.TestAttr(Attribute::CONSTRUCTOR)) { CheckFuncRetType(fd, ctx); } CheckFuncParamTypes(fd, ctx); } void CheckMemberTypes::CheckFuncRetType(FuncDecl& fd, TypeCheckContext& ctx) { if (fd.funcBody->retType && !ctx.typeMapper.IsObjCCompatible(*fd.funcBody->retType->ty)) { ctx.diag.DiagnoseRefactor( DiagKindRefactor::sema_objc_mirror_method_ret_must_be_objc_compatible, *fd.funcBody->retType); fd.EnableAttr(Attribute::IS_BROKEN); } } void CheckMemberTypes::CheckFuncParamTypes(FuncDecl& fd, TypeCheckContext& ctx) { auto errKind = fd.TestAttr(Attribute::CONSTRUCTOR) ? DiagKindRefactor::sema_objc_mirror_ctor_param_must_be_objc_compatible : DiagKindRefactor::sema_objc_mirror_method_param_must_be_objc_compatible; for (auto& paramList : fd.funcBody->paramLists) { for (auto& param : paramList->params) { if (ctx.typeMapper.IsObjCCompatible(*param->ty)) { continue; } fd.EnableAttr(Attribute::IS_BROKEN); fd.outerDecl->EnableAttr(Attribute::HAS_BROKEN, Attribute::IS_BROKEN); ctx.diag.DiagnoseRefactor(errKind, *param); } } } cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/TypeCheck/CheckMirrorInheritMirror.cpp000066400000000000000000000014771510705540100303750ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements check that @ObjCMirror annotated declaration inherits an @ObjCMirror declaration or none of * them. */ #include "Handlers.h" #include "cangjie/AST/Match.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; void CheckMirrorInheritMirror::HandleImpl(TypeCheckContext& ctx) { if (ctx.typeMapper.IsValidObjCMirror(*ctx.target.ty)) { return; } ctx.diag.DiagnoseRefactor(DiagKindRefactor::sema_objc_mirror_must_inherit_mirror, ctx.target); ctx.target.EnableAttr(Attribute::IS_BROKEN); }cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/TypeCheck/CheckMirrorSubtypeAttr.cpp000066400000000000000000000020321510705540100300520ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements check that Objective-C mirror subtypes declaration MUST be annotated * either with @ObjCImpl or with @ObjCImpl (which leads to have an OBJ_C_MIRROR_SUBTYPE attribute, enabled by Parser). */ #include "Handlers.h" #include "cangjie/AST/Match.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; void CheckMirrorSubtypeAttr::HandleImpl(TypeCheckContext& ctx) { auto& ty = *ctx.target.ty; if (!ctx.typeMapper.IsObjCMirrorSubtype(ty)) { return; } if ((ctx.typeMapper.IsValidObjCMirror(ty) || ctx.typeMapper.IsObjCImpl(ty))) { return; } ctx.diag.DiagnoseRefactor(DiagKindRefactor::sema_objc_mirror_subtype_must_be_annotated, ctx.target); ctx.target.EnableAttr(Attribute::IS_BROKEN); }cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/TypeCheck/CheckMultipleInherit.cpp000066400000000000000000000017471510705540100275230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements check that Objective-C mirror subtype declaration has at most one supertype (except an Object). */ #include "Handlers.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; void CheckMultipleInherit::HandleImpl(TypeCheckContext& ctx) { auto superTypesCount = 0; for (auto& parent : ctx.target.inheritedTypes) { if (parent->ty && parent->ty->IsObject()) { continue; } superTypesCount++; if (superTypesCount > 1) { ctx.diag.DiagnoseRefactor(DiagKindRefactor::sema_objc_mirror_subtype_cannot_multiple_inherit, *parent); ctx.target.EnableAttr(Attribute::IS_BROKEN); return; } } }cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/TypeCheck/Context.h000066400000000000000000000017651510705540100245400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares context for typechecking pipeline of Objective-C mirror/subtype declarations. */ #ifndef CANGJIE_SEMA_OBJ_C_TYPECHECK_CONTEXT #define CANGJIE_SEMA_OBJ_C_TYPECHECK_CONTEXT #include "NativeFFI/ObjC/Utils/TypeMapper.h" #include "cangjie/AST/Node.h" #include "cangjie/Basic/DiagnosticEngine.h" namespace Cangjie::Interop::ObjC { struct TypeCheckContext { explicit TypeCheckContext(AST::ClassLikeDecl& target, DiagnosticEngine& diag, TypeMapper& typeMapper) : target(target), diag(diag), typeMapper(typeMapper) { } AST::ClassLikeDecl& target; DiagnosticEngine& diag; TypeMapper& typeMapper; }; } // namespace Cangjie::Interop::ObjC #endif // CANGJIE_SEMA_OBJ_C_TYPECHECK_CONTEXT cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/TypeCheck/Handlers.h000066400000000000000000000036701510705540100246510ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares typecheck handlers for Objective-C mirror declarations. */ #ifndef CANGJIE_SEMA_OBJ_C_TYPECHECK_HANDLERS #define CANGJIE_SEMA_OBJ_C_TYPECHECK_HANDLERS #include "NativeFFI/ObjC/Utils/Handler.h" #include "Context.h" namespace Cangjie::Interop::ObjC { class CheckInterface : public Handler { public: void HandleImpl(TypeCheckContext& ctx); }; class CheckAbstractClass : public Handler { public: void HandleImpl(TypeCheckContext& ctx); }; class CheckMultipleInherit : public Handler { public: void HandleImpl(TypeCheckContext& ctx); }; class CheckMirrorSubtypeAttr : public Handler { public: void HandleImpl(TypeCheckContext& ctx); }; class CheckMirrorInheritMirror : public Handler { public: void HandleImpl(TypeCheckContext& ctx); }; class CheckImplInheritMirror : public Handler { public: void HandleImpl(TypeCheckContext& ctx); }; class CheckMemberTypes : public Handler { public: void HandleImpl(TypeCheckContext& ctx); private: void CheckFuncTypes(AST::FuncDecl& fd, TypeCheckContext& ctx); void CheckFuncParamTypes(AST::FuncDecl& fd, TypeCheckContext& ctx); void CheckFuncRetType(AST::FuncDecl& fd, TypeCheckContext& ctx); void CheckPropTypes(AST::PropDecl& pd, TypeCheckContext& ctx); void CheckVarTypes(AST::VarDecl& vd, TypeCheckContext& ctx); }; } // namespace Cangjie::Interop::ObjC #endif // CANGJIE_SEMA_OBJ_C_TYPECHECK_HANDLERScangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/Utils/000077500000000000000000000000001510705540100221535ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/Utils/ASTFactory.cpp000066400000000000000000001262061510705540100246450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements factory class for creating AST nodes. */ #include "ASTFactory.h" #include "NativeFFI/ObjC/Utils/Common.h" #include "NativeFFI/Utils.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Types.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/SafePointer.h" using namespace Cangjie::AST; using namespace Cangjie::TypeCheckUtil; using namespace Cangjie::Interop::ObjC; using namespace Cangjie::Native::FFI; namespace { constexpr auto VALUE_IDENT = "value"; constexpr auto INIT_IDENT = "init"; } // namespace OwnedPtr ASTFactory::CreateNativeHandleExpr(OwnedPtr entity) { CJC_ASSERT(typeMapper.IsValidObjCMirror(*entity->ty) || typeMapper.IsObjCImpl(*entity->ty)); auto curFile = entity->curFile; return WithinFile(CreateMemberAccess(std::move(entity), NATIVE_HANDLE_IDENT), curFile); } OwnedPtr ASTFactory::CreateNativeHandleExpr(ClassLikeTy& ty, Ptr curFile) { CJC_ASSERT(typeMapper.IsValidObjCMirror(ty) || typeMapper.IsObjCImpl(ty)); auto thisRef = CreateThisRef(ty.commonDecl, &ty, curFile); return CreateNativeHandleExpr(std::move(thisRef)); } OwnedPtr ASTFactory::CreateNativeHandleExpr(ClassLikeDecl& decl, bool isStatic, Ptr curFile) { auto& ty = *StaticCast(decl.ty); auto handle = isStatic ? CreateGetClassCall(ty, curFile) : UnwrapEntity(Native::FFI::CreateThisRef(&decl, &ty, curFile)); return handle; } OwnedPtr ASTFactory::UnwrapEntity(OwnedPtr expr) { if (typeMapper.IsValidObjCMirror(*expr->ty) || typeMapper.IsObjCImpl(*expr->ty)) { return CreateNativeHandleExpr(std::move(expr)); } if (expr->ty->IsCoreOptionType()) { CJC_ABORT(); // Option type is not supported } if (typeMapper.IsObjCPointer(*expr->ty)) { CJC_ASSERT(expr->ty->typeArgs.size() == 1); auto elementType = expr->ty->typeArgs[0]; auto field = GetObjCPointerPointerField(); auto fieldRef = CreateRefExpr(*field, *expr); return CreateUnsafePointerCast( CreateMemberAccess(std::move(expr), *field), typeMapper.Cj2CType(elementType) ); } CJC_ASSERT(expr->ty->IsPrimitive()); return expr; } OwnedPtr ASTFactory::WrapEntity(OwnedPtr expr, Ty& wrapTy) { if (typeMapper.IsValidObjCMirror(wrapTy)) { CJC_ASSERT(expr->ty->IsPointer()); auto classLikeTy = StaticCast(&wrapTy); auto mirror = As(classLikeTy->commonDecl); if (!mirror) { CJC_ABORT(); // mirror interface is not supported } auto ctor = GetGeneratedMirrorCtor(*mirror); return CreateCallExpr(CreateRefExpr(*ctor), Nodes(CreateFuncArg(std::move(expr))), ctor, classLikeTy, CallKind::CALL_OBJECT_CREATION); } if (typeMapper.IsObjCImpl(wrapTy)) { CJC_ASSERT(expr->ty->IsPointer()); auto& classLikeTy = *StaticCast(&wrapTy); auto& impl = *classLikeTy.commonDecl; return CreateGetFromRegistryByNativeHandleCall(std::move(expr), CreateRefType(impl)); } if (typeMapper.IsObjCPointer(wrapTy)) { CJC_ASSERT(expr->ty->IsPointer()); CJC_ASSERT(wrapTy.typeArgs.size() == 1); auto ctor = GetObjCPointerConstructor(); CJC_ASSERT(ctor); auto ctorRef = CreateRefExpr(*ctor, *expr); ctorRef->typeArguments.emplace_back(CreateType(wrapTy.typeArgs[0])); auto unitPtrExpr = CreateUnsafePointerCast( std::move(expr), typeManager.GetPrimitiveTy(TypeKind::TYPE_UNIT)); return CreateCallExpr(std::move(ctorRef), Nodes(CreateFuncArg(std::move(unitPtrExpr))), ctor, &wrapTy, CallKind::CALL_STRUCT_CREATION); } if (wrapTy.IsCoreOptionType()) { CJC_ABORT(); // Option type is not supported } CJC_ASSERT(expr->ty->IsPrimitive()); CJC_ASSERT(wrapTy.IsPrimitive()); return expr; } OwnedPtr ASTFactory::CreateNativeHandleField(ClassDecl& target) { auto nativeHandleTy = bridge.GetNativeObjCIdTy(); auto nativeHandleField = CreateVarDecl(NATIVE_HANDLE_IDENT, nullptr, CreateType(nativeHandleTy)); nativeHandleField->ty = nativeHandleTy; nativeHandleField->EnableAttr(Attribute::PUBLIC); PutDeclToClassBody(*nativeHandleField, target); return nativeHandleField; } OwnedPtr ASTFactory::CreateNativeHandleInit(FuncDecl& ctor) { auto& impl = *As(ctor.outerDecl); auto& implTy = *StaticCast(impl.ty); auto& param = *ctor.funcBody->paramLists[0]->params.back(); auto lhs = CreateNativeHandleExpr(implTy, impl.curFile); // this.$obj auto rhs = CreateRefExpr(param); auto unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); auto nativeObjHandleAssignExpr = CreateAssignExpr(std::move(lhs), std::move(rhs), unitTy); return nativeObjHandleAssignExpr; } OwnedPtr ASTFactory::CreateInitCjObject(const ClassDecl& target, FuncDecl& ctor) { auto curFile = ctor.curFile; auto registryIdTy = bridge.GetRegistryIdTy(); auto wrapperParamList = MakeOwned(); auto& wrapperParams = wrapperParamList->params; auto& ctorParams = ctor.funcBody->paramLists[0]->params; std::transform(ctorParams.begin(), ctorParams.end(), std::back_inserter(wrapperParams), [this](auto& p) { return CreateFuncParam(p->identifier.GetRawText(), nullptr, nullptr, typeMapper.Cj2CType(p->ty)); }); auto objParamRef = CreateRefExpr(*wrapperParams[0]); std::vector> wrapperParamTys; std::transform( wrapperParams.begin(), wrapperParams.end(), std::back_inserter(wrapperParamTys), [](auto& p) { return p->ty; }); auto wrapperTy = typeManager.GetFunctionTy(wrapperParamTys, registryIdTy, {.isC = true}); std::vector> wrapperParamLists; wrapperParamLists.emplace_back(std::move(wrapperParamList)); std::vector> ctorCallArgs; ctorCallArgs.emplace_back(CreateFuncArg(std::move(objParamRef))); // skip first param, as it is needed only for restore @ObjCImpl instance. for (size_t argIdx = 1; argIdx < ctorParams.size(); ++argIdx) { auto& wrapperParam = wrapperParams[argIdx]; auto paramRef = WithinFile(CreateRefExpr(*wrapperParam), curFile); OwnedPtr ctorCallArg = CreateFuncArg(WrapEntity(std::move(paramRef), *ctorParams[argIdx]->ty)); ctorCallArgs.emplace_back(std::move(ctorCallArg)); } auto ctorCall = CreateCallExpr( CreateRefExpr(ctor), std::move(ctorCallArgs), Ptr(&ctor), target.ty, CallKind::CALL_OBJECT_CREATION); auto putToRegistryCall = CreatePutToRegistryCall(std::move(ctorCall)); auto wrapperBody = CreateFuncBody(std::move(wrapperParamLists), CreateType(registryIdTy), CreateBlock(Nodes(std::move(putToRegistryCall)), registryIdTy), wrapperTy); auto wrapperName = nameGenerator.GenerateInitCjObjectName(ctor); auto wrapper = CreateFuncDecl(wrapperName, std::move(wrapperBody), wrapperTy); wrapper->EnableAttr(Attribute::C, Attribute::GLOBAL, Attribute::PUBLIC, Attribute::NO_MANGLE); wrapper->funcBody->funcDecl = wrapper.get(); PutDeclToFile(*wrapper, *ctor.curFile); return wrapper; } OwnedPtr ASTFactory::CreateDeleteCjObject(ClassDecl& target) { auto registryIdTy = bridge.GetRegistryIdTy(); auto param = CreateFuncParam(REGISTRY_ID_IDENT, CreateType(registryIdTy), nullptr, registryIdTy); auto unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); auto paramRef = CreateRefExpr(*param); std::vector> funcParamTys; funcParamTys.emplace_back(param->ty); auto funcTy = typeManager.GetFunctionTy(std::move(funcParamTys), unitTy, {.isC = true}); std::vector> funcParams; funcParams.emplace_back(std::move(param)); auto paramList = CreateFuncParamList(std::move(funcParams)); std::vector> funcNodes; auto getFromRegistryCall = CreateGetFromRegistryByIdCall(ASTCloner::Clone(paramRef.get()), CreateRefType(target)); auto removeFromRegistryCall = CreateRemoveFromRegistryCall(std::move(paramRef)); auto objTmpVarDecl = CreateTmpVarDecl(CreateRefType(target), std::move(getFromRegistryCall)); auto nativeHandleExpr = CreateMemberAccess(CreateRefExpr(*objTmpVarDecl), NATIVE_HANDLE_IDENT); auto releaseCall = CreateObjCRuntimeReleaseCall(std::move(nativeHandleExpr)); funcNodes.emplace_back(std::move(objTmpVarDecl)); funcNodes.emplace_back(std::move(removeFromRegistryCall)); funcNodes.emplace_back(std::move(releaseCall)); std::vector> paramLists; paramLists.emplace_back(std::move(paramList)); auto funcBody = CreateFuncBody( std::move(paramLists), Native::FFI::CreateUnitType(target.curFile), CreateBlock(std::move(funcNodes), unitTy), funcTy); auto funcName = nameGenerator.GenerateDeleteCjObjectName(target); auto ret = CreateFuncDecl(funcName, std::move(funcBody), funcTy); ret->EnableAttr(Attribute::C, Attribute::GLOBAL, Attribute::PUBLIC, Attribute::NO_MANGLE); ret->funcBody->funcDecl = ret.get(); PutDeclToFile(*ret, *target.curFile); return ret; } OwnedPtr ASTFactory::CreateMethodWrapper(FuncDecl& method) { auto registryIdTy = bridge.GetRegistryIdTy(); auto registryIdParam = CreateFuncParam(REGISTRY_ID_IDENT, CreateType(registryIdTy), nullptr, registryIdTy); auto registryIdParamRef = CreateRefExpr(*registryIdParam); auto outerDecl = StaticAs(method.outerDecl); auto wrapperParamList = MakeOwned(); auto& wrapperParams = wrapperParamList->params; wrapperParams.emplace_back(std::move(registryIdParam)); auto& originParams = method.funcBody->paramLists[0]->params; std::transform(originParams.begin(), originParams.end(), std::back_inserter(wrapperParams), [this](auto& p) { auto convertedParamTy = typeMapper.Cj2CType(p->ty); return CreateFuncParam(p->identifier.GetRawText(), CreateType(convertedParamTy), nullptr, convertedParamTy); }); std::vector> wrapperParamTys; std::transform( wrapperParams.begin(), wrapperParams.end(), std::back_inserter(wrapperParamTys), [](auto& p) { return p->ty; }); auto wrapperTy = typeManager.GetFunctionTy(wrapperParamTys, typeMapper.Cj2CType(method.funcBody->retType->ty), {.isC = true}); std::vector> wrapperParamLists; wrapperParamLists.emplace_back(std::move(wrapperParamList)); std::vector> wrapperNodes; auto getFromRegistryCall = CreateGetFromRegistryByIdCall(std::move(registryIdParamRef), CreateRefType(*outerDecl)); auto objTmpVarDecl = CreateTmpVarDecl(CreateRefType(*outerDecl), std::move(getFromRegistryCall)); auto methodExpr = CreateMemberAccess(CreateRefExpr(*objTmpVarDecl), method); methodExpr->curFile = method.curFile; methodExpr->begin = method.GetBegin(); methodExpr->end = method.GetEnd(); std::vector> methodArgs; // skip first param, as it is needed only for restore @ObjCImpl instance. for (size_t i = 1; i < wrapperParams.size(); ++i) { auto wrapperParam = wrapperParams[i].get(); auto originParam = originParams[i - 1].get(); auto paramRef = CreateRefExpr(*wrapperParam); auto wrappedParamRef = WrapEntity(std::move(paramRef), *originParam->ty); auto arg = CreateFuncArg(std::move(wrappedParamRef), wrapperParam->identifier, originParam->ty); methodArgs.emplace_back(std::move(arg)); } auto methodCall = CreateCallExpr(std::move(methodExpr), std::move(methodArgs), Ptr(&method), method.funcBody->retType->ty, CallKind::CALL_DECLARED_FUNCTION); wrapperNodes.emplace_back(std::move(objTmpVarDecl)); wrapperNodes.emplace_back(UnwrapEntity(std::move(methodCall))); auto wrapperBody = CreateFuncBody(std::move(wrapperParamLists), CreateType(wrapperTy->retTy), CreateBlock(std::move(wrapperNodes), wrapperTy->retTy), wrapperTy); auto wrapperName = nameGenerator.GenerateMethodWrapperName(method); auto wrapper = CreateFuncDecl(wrapperName, std::move(wrapperBody), wrapperTy); wrapper->EnableAttr(Attribute::C, Attribute::GLOBAL, Attribute::PUBLIC, Attribute::NO_MANGLE); wrapper->funcBody->funcDecl = wrapper.get(); PutDeclToFile(*wrapper, *method.curFile); return wrapper; } OwnedPtr ASTFactory::CreateGetterWrapper(PropDecl& prop) { auto registryIdTy = bridge.GetRegistryIdTy(); auto registryIdParam = CreateFuncParam(REGISTRY_ID_IDENT, CreateType(registryIdTy), nullptr, registryIdTy); auto registryIdParamRef = CreateRefExpr(*registryIdParam); auto outerDecl = StaticAs(prop.outerDecl); auto wrapperParamList = MakeOwned(); auto& wrapperParams = wrapperParamList->params; wrapperParams.emplace_back(std::move(registryIdParam)); std::vector> wrapperParamTys; std::transform( wrapperParams.begin(), wrapperParams.end(), std::back_inserter(wrapperParamTys), [](auto& p) { return p->ty; }); auto wrapperTy = typeManager.GetFunctionTy(wrapperParamTys, typeMapper.Cj2CType(prop.ty), {.isC = true}); std::vector> wrapperParamLists; wrapperParamLists.emplace_back(std::move(wrapperParamList)); std::vector> wrapperNodes; auto getFromRegistryCall = CreateGetFromRegistryByIdCall(std::move(registryIdParamRef), CreateRefType(*outerDecl)); auto objTmpVarDecl = CreateTmpVarDecl(CreateRefType(*outerDecl), std::move(getFromRegistryCall)); // Not sure if accessing the first getter is good auto propGetterExpr = CreateMemberAccess(CreateRefExpr(*objTmpVarDecl), *prop.getters[0].get()); propGetterExpr->curFile = prop.curFile; propGetterExpr->begin = prop.GetBegin(); propGetterExpr->end = prop.GetEnd(); auto propGetterCall = CreateCallExpr( std::move(propGetterExpr), {}, Ptr(prop.getters[0].get()), prop.ty, CallKind::CALL_DECLARED_FUNCTION); wrapperNodes.emplace_back(std::move(objTmpVarDecl)); wrapperNodes.emplace_back(UnwrapEntity(std::move(propGetterCall))); auto wrapperBody = CreateFuncBody(std::move(wrapperParamLists), ASTCloner::Clone(prop.type.get()), CreateBlock(std::move(wrapperNodes), wrapperTy->retTy), wrapperTy); auto wrapperName = nameGenerator.GeneratePropGetterWrapperName(prop); auto wrapper = CreateFuncDecl(wrapperName, std::move(wrapperBody), wrapperTy); wrapper->EnableAttr(Attribute::C, Attribute::GLOBAL, Attribute::PUBLIC, Attribute::NO_MANGLE); wrapper->funcBody->funcDecl = wrapper.get(); PutDeclToFile(*wrapper, *prop.curFile); return wrapper; } OwnedPtr ASTFactory::CreateSetterWrapper(PropDecl& prop) { auto registryIdTy = bridge.GetRegistryIdTy(); auto unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); auto registryIdParam = CreateFuncParam(REGISTRY_ID_IDENT, CreateType(registryIdTy), nullptr, registryIdTy); auto registryIdParamRef = CreateRefExpr(*registryIdParam); auto convertedPropTy = typeMapper.Cj2CType(prop.ty); auto setterParam = CreateFuncParam(VALUE_IDENT, CreateType(convertedPropTy), nullptr, convertedPropTy); auto setterParamRef = CreateRefExpr(*setterParam); auto outerDecl = StaticAs(prop.outerDecl); auto wrapperParamList = MakeOwned(); auto& wrapperParams = wrapperParamList->params; wrapperParams.emplace_back(std::move(registryIdParam)); wrapperParams.emplace_back(std::move(setterParam)); std::vector> wrapperParamTys; std::transform( wrapperParams.begin(), wrapperParams.end(), std::back_inserter(wrapperParamTys), [](auto& p) { return p->ty; }); auto wrapperTy = typeManager.GetFunctionTy(wrapperParamTys, unitTy, {.isC = true}); std::vector> wrapperParamLists; wrapperParamLists.emplace_back(std::move(wrapperParamList)); std::vector> wrapperNodes; auto getFromRegistryCall = CreateGetFromRegistryByIdCall(std::move(registryIdParamRef), CreateRefType(*outerDecl)); auto objTmpVarDecl = CreateTmpVarDecl(CreateRefType(*outerDecl), std::move(getFromRegistryCall)); // Not sure if accessing the first setter is good auto& setter = *prop.setters[0].get(); auto& originParams = setter.funcBody->paramLists[0]->params; auto propSetterExpr = CreateMemberAccess(CreateRefExpr(*objTmpVarDecl), setter); propSetterExpr->curFile = prop.curFile; propSetterExpr->begin = prop.GetBegin(); propSetterExpr->end = prop.GetEnd(); std::vector> propSetterArgs; // skip first param, as it is needed only for restore @ObjCImpl instance. for (size_t i = 1; i < wrapperParams.size(); ++i) { auto wrapperParam = wrapperParams[i].get(); auto originParam = originParams[i - 1].get(); auto paramRef = CreateRefExpr(*wrapperParam); auto wrappedParamRef = WrapEntity(std::move(paramRef), *originParam->ty); auto arg = CreateFuncArg(std::move(wrappedParamRef), wrapperParam->identifier, originParam->ty); propSetterArgs.emplace_back(std::move(arg)); } auto propSetterCall = CreateCallExpr( std::move(propSetterExpr), std::move(propSetterArgs), Ptr(&setter), unitTy, CallKind::CALL_DECLARED_FUNCTION); wrapperNodes.emplace_back(std::move(objTmpVarDecl)); wrapperNodes.emplace_back(std::move(propSetterCall)); auto wrapperBody = CreateFuncBody(std::move(wrapperParamLists), CreateUnitType(prop.curFile), CreateBlock(std::move(wrapperNodes), wrapperTy->retTy), wrapperTy); auto wrapperName = nameGenerator.GetPropSetterWrapperName(prop); auto wrapper = CreateFuncDecl(wrapperName, std::move(wrapperBody), wrapperTy); wrapper->EnableAttr(Attribute::C, Attribute::GLOBAL, Attribute::PUBLIC, Attribute::NO_MANGLE); wrapper->funcBody->funcDecl = wrapper.get(); PutDeclToFile(*wrapper, *prop.curFile); return wrapper; } OwnedPtr ASTFactory::CreateGetterWrapper(VarDecl& field) { auto registryIdTy = bridge.GetRegistryIdTy(); auto registryIdParam = CreateFuncParam(REGISTRY_ID_IDENT, CreateType(registryIdTy), nullptr, registryIdTy); auto registryIdParamRef = CreateRefExpr(*registryIdParam); auto outerDecl = StaticAs(field.outerDecl); auto wrapperParamList = MakeOwned(); auto& wrapperParams = wrapperParamList->params; wrapperParams.emplace_back(std::move(registryIdParam)); std::vector> wrapperParamTys; std::transform( wrapperParams.begin(), wrapperParams.end(), std::back_inserter(wrapperParamTys), [](auto& p) { return p->ty; }); auto convertedFieldTy = typeMapper.Cj2CType(field.ty); auto wrapperTy = typeManager.GetFunctionTy(wrapperParamTys, convertedFieldTy, {.isC = true}); std::vector> wrapperParamLists; wrapperParamLists.emplace_back(std::move(wrapperParamList)); std::vector> wrapperNodes; auto getFromRegistryCall = CreateGetFromRegistryByIdCall(std::move(registryIdParamRef), CreateRefType(*outerDecl)); auto objTmpVarDecl = CreateTmpVarDecl(CreateRefType(*outerDecl), std::move(getFromRegistryCall)); auto fieldExpr = CreateMemberAccess(CreateRefExpr(*objTmpVarDecl), field); fieldExpr->curFile = field.curFile; fieldExpr->begin = field.GetBegin(); fieldExpr->end = field.GetEnd(); wrapperNodes.emplace_back(std::move(objTmpVarDecl)); wrapperNodes.emplace_back(UnwrapEntity(std::move(fieldExpr))); auto wrapperBody = CreateFuncBody(std::move(wrapperParamLists), ASTCloner::Clone(field.type.get()), CreateBlock(std::move(wrapperNodes), wrapperTy->retTy), wrapperTy); // Generate wrapper name from ORIGIN field, not a mirror one. auto wrapperName = nameGenerator.GetFieldGetterWrapperName(field); auto wrapper = CreateFuncDecl(wrapperName, std::move(wrapperBody), wrapperTy); wrapper->EnableAttr(Attribute::C, Attribute::GLOBAL, Attribute::PUBLIC, Attribute::NO_MANGLE); wrapper->funcBody->funcDecl = wrapper.get(); PutDeclToFile(*wrapper, *field.curFile); return wrapper; } OwnedPtr ASTFactory::CreateSetterWrapper(VarDecl& field) { auto registryIdTy = bridge.GetRegistryIdTy(); auto unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); auto registryIdParam = CreateFuncParam(REGISTRY_ID_IDENT, CreateType(registryIdTy), nullptr, registryIdTy); auto registryIdParamRef = CreateRefExpr(*registryIdParam); auto convertedFieldTy = typeMapper.Cj2CType(field.ty); auto setterParam = CreateFuncParam(VALUE_IDENT, CreateType(convertedFieldTy), nullptr, convertedFieldTy); auto setterParamRef = CreateRefExpr(*setterParam); auto outerDecl = StaticAs(field.outerDecl); auto wrapperParamList = MakeOwned(); auto& wrapperParams = wrapperParamList->params; wrapperParams.emplace_back(std::move(registryIdParam)); wrapperParams.emplace_back(std::move(setterParam)); std::vector> wrapperParamTys; std::transform( wrapperParams.begin(), wrapperParams.end(), std::back_inserter(wrapperParamTys), [](auto& p) { return p->ty; }); auto wrapperTy = typeManager.GetFunctionTy(wrapperParamTys, unitTy, {.isC = true}); std::vector> wrapperParamLists; wrapperParamLists.emplace_back(std::move(wrapperParamList)); std::vector> wrapperNodes; auto getFromRegistryCall = CreateGetFromRegistryByIdCall(std::move(registryIdParamRef), CreateRefType(*outerDecl)); auto objTmpVarDecl = CreateTmpVarDecl(CreateRefType(*outerDecl), std::move(getFromRegistryCall)); auto lhs = CreateMemberAccess(CreateRefExpr(*objTmpVarDecl), field); auto assignFieldExpr = CreateAssignExpr(std::move(lhs), WrapEntity(std::move(setterParamRef), *field.ty), unitTy); assignFieldExpr->curFile = field.curFile; assignFieldExpr->begin = field.GetBegin(); assignFieldExpr->end = field.GetEnd(); wrapperNodes.emplace_back(std::move(objTmpVarDecl)); wrapperNodes.emplace_back(std::move(assignFieldExpr)); auto wrapperBody = CreateFuncBody(std::move(wrapperParamLists), CreateUnitType(field.curFile), CreateBlock(std::move(wrapperNodes), wrapperTy->retTy), wrapperTy); // Generate wrapper name from ORIGIN field, not a mirror one. auto wrapperName = nameGenerator.GetFieldSetterWrapperName(field); auto wrapper = CreateFuncDecl(wrapperName, std::move(wrapperBody), wrapperTy); wrapper->EnableAttr(Attribute::C, Attribute::GLOBAL, Attribute::PUBLIC, Attribute::NO_MANGLE); wrapper->funcBody->funcDecl = wrapper.get(); PutDeclToFile(*wrapper, *field.curFile); return wrapper; } OwnedPtr ASTFactory::CreateThrowUnreachableCodeExpr(File& file) { auto exceptionDecl = bridge.GetObjCUnreachableCodeExceptionDecl(); CJC_NULLPTR_CHECK(exceptionDecl); return CreateThrowException(*exceptionDecl, {}, file, typeManager); } std::set> ASTFactory::GetAllParentCtors(ClassDecl& target) { std::set> result = {}; for (auto& it : target.GetAllSuperDecls()) { for (OwnedPtr& declPtr : it->GetMemberDecls()) { if (IsGeneratedMember(*declPtr.get())) { continue; } if (!declPtr->TestAttr(Attribute::CONSTRUCTOR)) { continue; } if (declPtr->astKind != ASTKind::FUNC_DECL) { // skip primary ctor, as it is desugared to init already continue; } auto funcDecl = StaticAs(declPtr.get()); if (!funcDecl->funcBody) { continue; } result.insert(funcDecl); } } return result; } OwnedPtr ASTFactory::CreateMirrorCtorDecl(ClassDecl& target) { auto nativeObjCIdTy = bridge.GetNativeObjCIdTy(); auto param = CreateFuncParam(NATIVE_HANDLE_IDENT, CreateType(nativeObjCIdTy), nullptr, nativeObjCIdTy); std::vector> ctorFuncParamTys; ctorFuncParamTys.emplace_back(param->ty); auto ctorFuncTy = typeManager.GetFunctionTy(std::move(ctorFuncParamTys), target.ty); std::vector> ctorParams; ctorParams.emplace_back(std::move(param)); auto paramList = CreateFuncParamList(std::move(ctorParams)); std::vector> ctorNodes; std::vector> paramLists; paramLists.emplace_back(std::move(paramList)); auto ctorFuncBody = CreateFuncBody( std::move(paramLists), CreateRefType(target), CreateBlock(std::move(ctorNodes), target.ty), ctorFuncTy); auto ctor = CreateFuncDecl(INIT_IDENT, std::move(ctorFuncBody), ctorFuncTy); ctor->funcBody->funcDecl = ctor.get(); ctor->constructorCall = ConstructorCall::NONE; ctor->funcBody->parentClassLike = ⌖ ctor->EnableAttr(Attribute::PUBLIC, Attribute::CONSTRUCTOR); PutDeclToClassBody(*ctor, target); return ctor; } OwnedPtr ASTFactory::CreateImplCtor(ClassDecl& impl, FuncDecl& from) { auto nativeHandleTy = bridge.GetNativeObjCIdTy(); auto ctor = ASTCloner::Clone(Ptr(&from)); auto& implCtorParams = ctor->funcBody->paramLists[0]->params; implCtorParams.insert(implCtorParams.begin(), CreateFuncParam(NATIVE_HANDLE_IDENT, CreateType(nativeHandleTy), nullptr, nativeHandleTy)); auto& nativeHandleParam = *implCtorParams.front(); std::vector> implCtorParamTys; std::transform(implCtorParams.begin(), implCtorParams.end(), std::back_inserter(implCtorParamTys), [](auto& p) { return p->type->ty; }); ctor->ty = typeManager.GetFunctionTy(implCtorParamTys, ctor->funcBody->retType->ty); ctor->funcBody->ty = ctor->ty; ctor->funcBody->funcDecl = ctor.get(); ctor->constructorCall = ConstructorCall::SUPER; CJC_NULLPTR_CHECK(impl.GetSuperClassDecl()); auto parentCtor = GetGeneratedMirrorCtor(*impl.GetSuperClassDecl()); auto superCall = CreateSuperCall(*impl.GetSuperClassDecl(), *parentCtor, parentCtor->ty); superCall->args.emplace_back(CreateFuncArg(CreateRefExpr(nativeHandleParam))); auto& body = ctor->funcBody->body->body; body.erase(std::remove_if(body.begin(), body.end(), [](auto& node) { if (auto call = As(node.get())) { return call->callKind == CallKind::CALL_SUPER_FUNCTION; } return false; }), body.end()); body.insert(body.begin(), std::move(superCall)); return ctor; } bool ASTFactory::IsGeneratedMember(const Decl& decl) { return IsGeneratedNativeHandleField(decl) || IsGeneratedCtor(decl); } bool ASTFactory::IsGeneratedNativeHandleField(const Decl& decl) { return decl.identifier.Val() == NATIVE_HANDLE_IDENT; } bool ASTFactory::IsGeneratedCtor(const Decl& decl) { auto fd = DynamicCast(&decl); if (!fd || !fd->TestAttr(Attribute::CONSTRUCTOR) || !fd->funcBody) { return false; } auto& paramLists = fd->funcBody->paramLists; if (paramLists.empty()) { return false; } // taking first param list probably is not the best idea auto& params = paramLists[0]->params; if (params.empty()) { return false; } return params[0]->identifier == NATIVE_HANDLE_IDENT; } Ptr ASTFactory::GetGeneratedMirrorCtor(Decl& decl) { CJC_ASSERT(decl.astKind == ASTKind::CLASS_DECL); CJC_ASSERT(TypeMapper::IsValidObjCMirror(*decl.ty)); auto& classDecl = *StaticAs(&decl); for (auto& member : classDecl.GetMemberDeclPtrs()) { if (auto fd = As(member); fd && IsGeneratedCtor(*fd)) { return fd; } } CJC_ABORT(); return nullptr; } Ptr ASTFactory::GetGeneratedImplCtor(const ClassDecl& impl, const FuncDecl& origin) { CJC_ASSERT(origin.TestAttr(Attribute::CONSTRUCTOR)); CJC_NULLPTR_CHECK(origin.funcBody); const auto& originParamLists = origin.funcBody->paramLists; CJC_ASSERT(!originParamLists.empty()); // taking first param list probably is not the best idea const auto& originParams = originParamLists[0]->params; for (auto& member: impl.GetMemberDeclPtrs()) { if (auto fd = As(member); fd && IsGeneratedCtor(*fd)) { CJC_NULLPTR_CHECK(fd->funcBody); const auto& fdParamLists = fd->funcBody->paramLists; CJC_ASSERT(!fdParamLists.empty()); // taking first param list probably is not the best idea const auto& fdParams = fdParamLists[0]->params; if (originParams.size() + 1 != fdParams.size()) { continue; } auto matched = true; // assume that first fd param if $obj: NativeObjCId for (size_t i = 1; i < fdParams.size(); ++i) { const auto fdParam = fdParams[i].get(); const auto originParam = originParams[i - 1].get(); if (fdParam->identifier != originParam->identifier || fdParam->ty != originParam->ty) { matched = false; break; } } if (matched) { return fd; } } } return Ptr(); } void ASTFactory::PutDeclToClassBody(Decl& decl, ClassDecl& target) { decl.begin = target.body->end; decl.end = target.body->end; decl.outerDecl = ⌖ decl.curFile = target.curFile; decl.EnableAttr(Attribute::IN_CLASSLIKE); } void ASTFactory::PutDeclToFile(Decl& decl, File& target) { decl.curFile = ⌖ decl.begin = target.end; decl.end = target.end; } OwnedPtr ASTFactory::CreatePutToRegistryCall(OwnedPtr nativeHandle) { auto putToRegistryDecl = bridge.GetPutToRegistryDecl(); auto putToRegistryExpr = CreateRefExpr(*putToRegistryDecl); std::vector> args; args.emplace_back(CreateFuncArg(std::move(nativeHandle))); return CreateCallExpr(std::move(putToRegistryExpr), std::move(args), putToRegistryDecl, putToRegistryDecl->funcBody->retType->ty, CallKind::CALL_DECLARED_FUNCTION); } OwnedPtr ASTFactory::CreateGetFromRegistryByNativeHandleCall( OwnedPtr nativeHandle, OwnedPtr typeArg) { CJC_ASSERT(nativeHandle->ty->IsPointer()); auto getFromRegistryByNativeHandleDecl = bridge.GetGetFromRegistryByNativeHandleDecl(); auto getFromRegistryNativeHandleExpr = CreateRefExpr(*getFromRegistryByNativeHandleDecl); auto ty = typeArg->ty; CJC_ASSERT(TypeMapper::IsObjCImpl(*ty)); std::vector> args; args.emplace_back(CreateFuncArg(std::move(nativeHandle))); getFromRegistryNativeHandleExpr->instTys.emplace_back(ty); getFromRegistryNativeHandleExpr->ty = typeManager.GetInstantiatedTy(getFromRegistryByNativeHandleDecl->ty, GenerateTypeMapping(*getFromRegistryByNativeHandleDecl, getFromRegistryNativeHandleExpr->instTys)); getFromRegistryNativeHandleExpr->typeArguments.emplace_back(std::move(typeArg)); return CreateCallExpr(std::move(getFromRegistryNativeHandleExpr), std::move(args), getFromRegistryByNativeHandleDecl, ty, CallKind::CALL_DECLARED_FUNCTION); } OwnedPtr ASTFactory::CreateGetFromRegistryByIdCall(OwnedPtr registryId, OwnedPtr typeArg) { auto getFromRegistryByIdDecl = bridge.GetGetFromRegistryByIdDecl(); auto getFromRegistryByIdExpr = CreateRefExpr(*getFromRegistryByIdDecl); auto ty = typeArg->ty; CJC_ASSERT(TypeMapper::IsObjCImpl(*ty)); std::vector> args; args.emplace_back(CreateFuncArg(std::move(registryId))); getFromRegistryByIdExpr->instTys.emplace_back(ty); getFromRegistryByIdExpr->ty = typeManager.GetInstantiatedTy( getFromRegistryByIdDecl->ty, GenerateTypeMapping(*getFromRegistryByIdDecl, getFromRegistryByIdExpr->instTys)); getFromRegistryByIdExpr->typeArguments.emplace_back(std::move(typeArg)); return CreateCallExpr(std::move(getFromRegistryByIdExpr), std::move(args), getFromRegistryByIdDecl, ty, CallKind::CALL_DECLARED_FUNCTION); } OwnedPtr ASTFactory::CreateRemoveFromRegistryCall(OwnedPtr registryId) { auto removeFromRegistryDecl = bridge.GetRemoveFromRegistryDecl(); auto removeFromRegistryExpr = CreateRefExpr(*removeFromRegistryDecl); std::vector> args; args.emplace_back(CreateFuncArg(std::move(registryId))); return CreateCallExpr(std::move(removeFromRegistryExpr), std::move(args), removeFromRegistryDecl, removeFromRegistryDecl->funcBody->retType->ty, CallKind::CALL_DECLARED_FUNCTION); } OwnedPtr ASTFactory::CreateObjCRuntimeReleaseCall(OwnedPtr nativeHandle) { auto releaseExpr = bridge.CreateObjCRuntimeReleaseExpr(); std::vector> args; args.emplace_back(CreateFuncArg(std::move(nativeHandle))); return CreateCallExpr(std::move(releaseExpr), std::move(args), nullptr, TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT), CallKind::CALL_FUNCTION_PTR); } OwnedPtr ASTFactory::CreateObjCRuntimeMsgSendCall( Ptr ty, OwnedPtr funcType, std::vector> funcArgs) { auto msgSendExpr = bridge.CreateObjCRuntimeMsgSendExpr(); auto retType = funcType->retType.get(); auto cFuncDecl = importManager.GetCoreDecl(std::string(CFUNC_NAME)); auto cFuncRefExpr = CreateRefExpr(*cFuncDecl); cFuncRefExpr->ty = ty; cFuncRefExpr->typeArguments.emplace_back(std::move(funcType)); // CFunc<...>(msgSend) auto cFuncCallExpr = CreateCallExpr(std::move(cFuncRefExpr), Nodes(CreateFuncArg(std::move(msgSendExpr))), nullptr, ty, CallKind::CALL_FUNCTION_PTR); std::vector> msgSendCallArgs; std::transform(funcArgs.begin(), funcArgs.end(), std::back_inserter(msgSendCallArgs), [](auto&& argExpr) { return CreateFuncArg(std::move(argExpr)); }); // CFunc<...>(msgSend)(...) return CreateCallExpr( std::move(cFuncCallExpr), std::move(msgSendCallArgs), nullptr, retType->ty, CallKind::CALL_FUNCTION_PTR); } OwnedPtr ASTFactory::CreateObjCRuntimeMsgSendCall( OwnedPtr nativeHandle, const std::string& selector, Ptr retTy, std::vector> args) { auto selectorCall = CreateRegisterNameCall(selector, nativeHandle->curFile); auto ft = MakeOwned(); ft->isC = true; ft->retType = CreateType(retTy); args.insert(args.begin(), std::move(selectorCall)); args.insert(args.begin(), std::move(nativeHandle)); std::vector> paramTys; for (auto& param : args) { ft->paramTypes.emplace_back(CreateType(param->ty)); paramTys.emplace_back(param->ty); } auto fty = typeManager.GetFunctionTy(paramTys, retTy, {.isC = true}); ft->ty = fty; return CreateObjCRuntimeMsgSendCall(fty, std::move(ft), std::move(args)); } OwnedPtr ASTFactory::CreateGetClassCall(ClassLikeTy& ty, Ptr curFile) { auto getClassFuncDecl = bridge.GetGetClassDecl(); auto getClassExpr = CreateRefExpr(*getClassFuncDecl); auto cnameAsLit = CreateLitConstExpr(LitConstKind::STRING, ty.name, GetStringDecl(importManager).ty); return CreateCall(getClassFuncDecl, curFile, std::move(cnameAsLit)); } OwnedPtr ASTFactory::CreateRegisterNameCall(OwnedPtr selectorExpr) { auto registerNameDecl = bridge.GetRegisterNameDecl(); auto registerNameExpr = CreateRefExpr(*registerNameDecl); auto curFile = selectorExpr->curFile; return CreateCall(registerNameDecl, curFile, std::move(selectorExpr)); } OwnedPtr ASTFactory::CreateRegisterNameCall(const std::string& selector, Ptr curFile) { auto strTy = GetStringDecl(importManager).ty; auto selectorAsLit = CreateLitConstExpr(LitConstKind::STRING, selector, strTy); selectorAsLit->curFile = curFile; return CreateRegisterNameCall(std::move(selectorAsLit)); } OwnedPtr ASTFactory::CreateAllocCall(OwnedPtr className) { auto allocDecl = bridge.GetAllocDecl(); auto allocExpr = CreateRefExpr(*allocDecl); auto curFile = className->curFile; return CreateCall(allocDecl, curFile, std::move(className)); } OwnedPtr ASTFactory::CreateAllocCall(ClassDecl& decl, Ptr curFile) { auto objcname = nameGenerator.GetObjCDeclName(decl); auto classNameExpr = WithinFile(CreateLitConstExpr(LitConstKind::STRING, objcname, GetStringDecl(importManager).ty), curFile); return CreateAllocCall(std::move(classNameExpr)); } OwnedPtr ASTFactory::CreateMethodCallViaMsgSend( FuncDecl& fd, OwnedPtr nativeHandle, std::vector> rawArgs) { auto objcname = nameGenerator.GetObjCDeclName(fd); return CreateObjCRuntimeMsgSendCall( std::move(nativeHandle), objcname, typeMapper.Cj2CType(StaticCast(fd.ty)->retTy), std::move(rawArgs)); } OwnedPtr ASTFactory::CreatePropGetterCallViaMsgSend(PropDecl& pd, OwnedPtr nativeHandle) { auto objcname = nameGenerator.GetObjCDeclName(pd); return CreateObjCRuntimeMsgSendCall(std::move(nativeHandle), objcname, typeMapper.Cj2CType(pd.ty), {}); } OwnedPtr ASTFactory::CreatePropSetterCallViaMsgSend(PropDecl& pd, OwnedPtr nativeHandle, OwnedPtr arg) { auto objcname = nameGenerator.GetObjCDeclName(pd); std::transform( objcname.begin(), objcname.begin() + 1, objcname.begin(), [](unsigned char c) { return std::toupper(c); }); objcname = "set" + objcname + ":"; return CreateObjCRuntimeMsgSendCall( std::move(nativeHandle), objcname, typeMapper.Cj2CType(pd.ty), Nodes(std::move(arg))); } OwnedPtr ASTFactory::CreateAutoreleasePoolScope(Ptr ty, std::vector> actions) { CJC_ASSERT(typeMapper.IsObjCCompatible(*ty)); CJC_ASSERT(!actions.empty()); Ptr arpdecl; OwnedPtr arpref; if (ty->IsPrimitive() || typeMapper.IsObjCPointer(*ty)) { arpdecl = bridge.GetWithAutoreleasePoolDecl(); auto unwrappedTy = typeMapper.Cj2CType(ty); arpref = CreateRefExpr(*arpdecl); arpref->instTys.emplace_back(unwrappedTy); arpref->ty = typeManager.GetInstantiatedTy(arpdecl->ty, GenerateTypeMapping(*arpdecl, arpref->instTys)); arpref->typeArguments.emplace_back(CreateType(unwrappedTy)); } else { arpdecl = bridge.GetWithAutoreleasePoolObjDecl(); arpref = CreateRefExpr(*arpdecl); } auto args = Nodes(CreateFuncArg(WrapReturningLambdaExpr(typeManager, std::move(actions)))); auto retTy = StaticCast(arpref->ty)->retTy; return CreateCallExpr(std::move(arpref), std::move(args), arpdecl, retTy, CallKind::CALL_DECLARED_FUNCTION); } OwnedPtr ASTFactory::CreateGetInstanceVariableCall( const AST::PropDecl& field, OwnedPtr nativeHandle) { Ptr getInstVarDecl; OwnedPtr getInstVarRef; if (field.ty->IsPrimitive()) { getInstVarDecl = bridge.GetGetInstanceVariableDecl(); getInstVarRef = CreateRefExpr(*getInstVarDecl); getInstVarRef->instTys.emplace_back(field.ty); getInstVarRef->ty = typeManager.GetInstantiatedTy( getInstVarDecl->ty, GenerateTypeMapping(*getInstVarDecl, getInstVarRef->instTys) ); getInstVarRef->typeArguments.emplace_back(CreateType(field.ty)); } else { getInstVarDecl = bridge.GetGetInstanceVariableObjDecl(); getInstVarRef = CreateRefExpr(*getInstVarDecl); } auto objcname = nameGenerator.GetObjCDeclName(field); auto nameExpr = WithinFile( CreateLitConstExpr( LitConstKind::STRING, objcname, GetStringDecl(importManager).ty ), field.curFile); auto args = Nodes( CreateFuncArg(std::move(nativeHandle)), CreateFuncArg(std::move(nameExpr)) ); auto retTy = StaticCast(getInstVarRef->ty)->retTy; return CreateCallExpr( std::move(getInstVarRef), std::move(args), getInstVarDecl, retTy, CallKind::CALL_DECLARED_FUNCTION); } OwnedPtr ASTFactory::CreateObjCRuntimeSetInstanceVariableCall( const PropDecl& field, OwnedPtr nativeHandle, OwnedPtr value) { Ptr setInstVarDecl; OwnedPtr setInstVarRef; if (field.ty->IsPrimitive()) { setInstVarDecl = bridge.GetSetInstanceVariableDecl(); setInstVarRef = CreateRefExpr(*setInstVarDecl); setInstVarRef->instTys.emplace_back(field.ty); setInstVarRef->ty = typeManager.GetInstantiatedTy( setInstVarDecl->ty, GenerateTypeMapping(*setInstVarDecl, setInstVarRef->instTys) ); setInstVarRef->typeArguments.emplace_back(CreateType(field.ty)); } else { setInstVarDecl = bridge.GetSetInstanceVariableObjDecl(); setInstVarRef = CreateRefExpr(*setInstVarDecl); } auto objcname = nameGenerator.GetObjCDeclName(field); auto nameExpr = WithinFile( CreateLitConstExpr(LitConstKind::STRING, objcname, GetStringDecl(importManager).ty ), field.curFile); auto args = Nodes( CreateFuncArg(std::move(nativeHandle)), CreateFuncArg(std::move(nameExpr)), CreateFuncArg(std::move(value)) ); return CreateCallExpr( std::move(setInstVarRef), std::move(args), setInstVarDecl, TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT), CallKind::CALL_DECLARED_FUNCTION); } OwnedPtr ASTFactory::CreateUnsafePointerCast(OwnedPtr expr, Ptr elementType) { CJC_ASSERT(expr->ty->IsPointer()); CJC_ASSERT(Ty::IsMetCType(*elementType)); auto ptrExpr = MakeOwned(); auto pointerType = typeManager.GetPointerTy(elementType); CopyBasicInfo(expr, ptrExpr); ptrExpr->arg = CreateFuncArg(std::move(expr)); ptrExpr->ty = pointerType; ptrExpr->type = CreateType(ptrExpr->ty); ptrExpr->EnableAttr(Attribute::COMPILER_ADD); return ptrExpr; } Ptr ASTFactory::GetObjCPointerConstructor() { Ptr result = nullptr; auto outer = bridge.GetObjCPointerDecl(); for (auto& member : outer->body->decls) { if (auto funcDecl = DynamicCast(member.get())) { if (funcDecl->TestAttr(Attribute::CONSTRUCTOR) && funcDecl->funcBody && funcDecl->funcBody->paramLists[0] && funcDecl->funcBody->paramLists[0]->params.size() == 1) { result = funcDecl; } } } return result; } Ptr ASTFactory::GetObjCPointerPointerField() { Ptr result = nullptr; auto outer = bridge.GetObjCPointerDecl(); for (auto& member : outer->body->decls) { if (auto fieldDecl = DynamicCast(member.get())) { if (fieldDecl->ty->IsPointer()) { result = fieldDecl; } } } return result; } cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/Utils/ASTFactory.h000066400000000000000000000160401510705540100243040ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares a factory class for creating AST nodes. */ #ifndef CANGJIE_SEMA_OBJ_C_UTILS_AST_FACTORY_H #define CANGJIE_SEMA_OBJ_C_UTILS_AST_FACTORY_H #include "InteropLibBridge.h" #include "NameGenerator.h" #include "TypeMapper.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Types.h" #include "cangjie/Utils/SafePointer.h" namespace Cangjie::Interop::ObjC { class ASTFactory { public: explicit ASTFactory(InteropLibBridge& bridge, TypeManager& typeManager, NameGenerator& nameGenerator, TypeMapper& typeMapper, ImportManager& importManager) : bridge(bridge), typeManager(typeManager), nameGenerator(nameGenerator), typeMapper(typeMapper), importManager(importManager) { } /** * For obj-c compatible expression `expr`, it returns corresponding expr over CType: * - for mirror/impl: `$expr.$obj` * - for primitive value: the value itself */ OwnedPtr UnwrapEntity(OwnedPtr expr); /** * Returns obj-c compatible expression over `expr` of CType: * - for primitive value: the value itself * - for CPointer (mirror M): constructor call `M(expr)` * - for CPointer (impl I): call to retrieve corresponding instance from regirstry for `expr` */ OwnedPtr WrapEntity(OwnedPtr expr, AST::Ty& wrapTy); /** * Returns native handle for decl mirror/impl type. * If `isStatic`, then returns handle related to class. (objc_getClass) * If not `isStatic`, then returns handle related to instance (this.$obj) */ OwnedPtr CreateNativeHandleExpr(AST::ClassLikeDecl& decl, bool isStatic, Ptr curFile); /** * For mirror/impl `entity`, it returns pointer on obj-c object: `$entity.$obj` */ OwnedPtr CreateNativeHandleExpr(OwnedPtr entity); /** * For mirror/impl type `ty`, it returns this.$obj, where `this` is a reference on `ty` */ OwnedPtr CreateNativeHandleExpr(AST::ClassLikeTy& ty, Ptr curFile); OwnedPtr CreateNativeHandleField(AST::ClassDecl& target); OwnedPtr CreateNativeHandleInit(AST::FuncDecl& ctor); OwnedPtr CreateInitCjObject(const AST::ClassDecl& target, AST::FuncDecl& ctor); OwnedPtr CreateDeleteCjObject(AST::ClassDecl& target); /** * Returns generated top-level @C function (callable from obj-c) that calls @ObjCImpl `originMethod`. */ OwnedPtr CreateMethodWrapper(AST::FuncDecl& method); OwnedPtr CreateGetterWrapper(AST::PropDecl& prop); OwnedPtr CreateSetterWrapper(AST::PropDecl& prop); OwnedPtr CreateGetterWrapper(AST::VarDecl& field); OwnedPtr CreateSetterWrapper(AST::VarDecl& field); OwnedPtr CreateThrowUnreachableCodeExpr(AST::File& file); std::set> GetAllParentCtors(AST::ClassDecl& target); OwnedPtr CreateImplCtor(AST::ClassDecl& target, AST::FuncDecl& from); OwnedPtr CreateMirrorCtorDecl(AST::ClassDecl& target); bool IsGeneratedMember(const AST::Decl& decl); bool IsGeneratedNativeHandleField(const AST::Decl& decl); bool IsGeneratedCtor(const AST::Decl& decl); Ptr GetGeneratedMirrorCtor(AST::Decl& decl); Ptr GetGeneratedImplCtor(const AST::ClassDecl& impl, const AST::FuncDecl& origin); OwnedPtr CreateObjCRuntimeMsgSendCall( OwnedPtr nativeHandle, const std::string& selector, Ptr retTy, std::vector> args ); OwnedPtr CreateObjCRuntimeMsgSendCall( Ptr ty, OwnedPtr funcType, std::vector> funcArgs ); OwnedPtr CreateGetInstanceVariableCall( const AST::PropDecl& field, OwnedPtr nativeHandle ); OwnedPtr CreateObjCRuntimeSetInstanceVariableCall( const AST::PropDecl& field, OwnedPtr nativeHandle, OwnedPtr value ); OwnedPtr CreateRegisterNameCall(OwnedPtr selectorExpr); OwnedPtr CreateRegisterNameCall(const std::string& selector, Ptr curFile); OwnedPtr CreateGetClassCall(AST::ClassLikeTy& ty, Ptr curFile); /** * ObjCRuntime.alloc($classHandle) */ OwnedPtr CreateAllocCall(OwnedPtr className); /** * ObjCRuntime.alloc(decl) */ OwnedPtr CreateAllocCall(AST::ClassDecl& decl, Ptr curFile); OwnedPtr CreateMethodCallViaMsgSend( AST::FuncDecl& fd, OwnedPtr nativeHandle, std::vector> rawArgs ); OwnedPtr CreatePropGetterCallViaMsgSend( AST::PropDecl& pd, OwnedPtr nativeHandle ); OwnedPtr CreatePropSetterCallViaMsgSend( AST::PropDecl& pd, OwnedPtr nativeHandle, OwnedPtr arg ); OwnedPtr CreateAutoreleasePoolScope(Ptr ty, std::vector> actions); OwnedPtr CreateUnsafePointerCast(OwnedPtr expr, Ptr elementType); Ptr GetObjCPointerConstructor(); Ptr GetObjCPointerPointerField(); static constexpr auto NATIVE_HANDLE_IDENT = "$obj"; static constexpr auto REGISTRY_ID_IDENT = "$registryId"; private: void PutDeclToClassBody(AST::Decl& decl, AST::ClassDecl& target); void PutDeclToFile(AST::Decl& decl, AST::File& target); /** * ObjCRuntime.putToRegistry($obj) */ OwnedPtr CreatePutToRegistryCall(OwnedPtr nativeHandle); /** * ObjCRuntime.getFromRegistry(registryId) */ OwnedPtr CreateGetFromRegistryByIdCall( OwnedPtr registryId, OwnedPtr typeArg); OwnedPtr CreateGetFromRegistryByNativeHandleCall( OwnedPtr nativeHandle, OwnedPtr typeArg); /** * ObjCRuntime.removeFromRegistry(registryId) */ OwnedPtr CreateRemoveFromRegistryCall(OwnedPtr registryId); /** * ObjCRuntime.release(obj) */ OwnedPtr CreateObjCRuntimeReleaseCall(OwnedPtr nativeHandle); InteropLibBridge& bridge; TypeManager& typeManager; NameGenerator& nameGenerator; TypeMapper& typeMapper; ImportManager& importManager; }; } // namespace Cangjie::Interop::ObjC #endif // CANGJIE_SEMA_OBJ_C_UTILS_AST_FACTORY_H cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/Utils/CMakeLists.txt000066400000000000000000000006341510705540100247160ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB SEMA_NATIVE_FFI_OBJ_C_UTILS_SRC *.cpp) set(SEMA_NATIVE_FFI_OBJ_C_SRC ${SEMA_NATIVE_FFI_OBJ_C_SRC} ${SEMA_NATIVE_FFI_OBJ_C_UTILS_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/Utils/Common.cpp000066400000000000000000000033461510705540100241150ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements common utils for Cangjie <-> Objective-C interop. */ #include "Common.h" #include "TypeMapper.h" #include "ASTFactory.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; Ptr Cangjie::Interop::ObjC::GetMirrorSuperClass(const ClassLikeDecl& target) { if (auto classDecl = DynamicCast(&target)) { auto superClass = classDecl->GetSuperClassDecl(); if (superClass && TypeMapper::IsValidObjCMirror(*superClass->ty)) { return superClass; } } return nullptr; } bool Cangjie::Interop::ObjC::HasMirrorSuperClass(const ClassLikeDecl& target) { return GetMirrorSuperClass(target) != nullptr; } Ptr Cangjie::Interop::ObjC::FindNativeVarHandle(const AST::ClassLikeDecl& target) { CJC_ASSERT(TypeMapper::IsValidObjCMirror(*target.ty) || TypeMapper::IsObjCImpl(*target.ty)); auto mirrorSuperClass = GetMirrorSuperClass(target); if (mirrorSuperClass != nullptr) { return FindNativeVarHandle(*mirrorSuperClass); } return As(FindMirrorMember(ASTFactory::NATIVE_HANDLE_IDENT, target)); } Ptr Cangjie::Interop::ObjC::FindMirrorMember(const std::string_view& mirrorMemberIdent, const InheritableDecl& target) { for (auto& memberDecl: target.GetMemberDeclPtrs()) { if (memberDecl->identifier == mirrorMemberIdent) { return memberDecl; } } return Ptr(nullptr); } cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/Utils/Common.h000066400000000000000000000021041510705540100235510ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares common utils for Cangjie <-> Objective-C interop. * Needs to be structured properly in the future. */ #ifndef CANGJIE_SEMA_OBJ_C_UTILS_COMMON_H #define CANGJIE_SEMA_OBJ_C_UTILS_COMMON_H #include "cangjie/AST/Node.h" #include "cangjie/Utils/SafePointer.h" namespace Cangjie::Interop::ObjC { /** * Returns native handle field declaration for `target` mirror/impl declaration * */ Ptr FindNativeVarHandle(const AST::ClassLikeDecl& target); Ptr FindMirrorMember(const std::string_view& mirrorMemberIdent, const AST::InheritableDecl& target); Ptr GetMirrorSuperClass(const AST::ClassLikeDecl& target); bool HasMirrorSuperClass(const AST::ClassLikeDecl& target); } // namespace Cangjie::Interop::ObjC #endif // CANGJIE_SEMA_OBJ_C_UTILS_COMMON_H cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/Utils/Handler.h000066400000000000000000000047501510705540100237070ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares utility classes for implementing a chain of responsibility. */ #ifndef CANGJIE_SEMA_OBJ_C_UTILS_HANDLER_H #define CANGJIE_SEMA_OBJ_C_UTILS_HANDLER_H #include #include namespace Cangjie::Interop::ObjC { /** * Base CRTP-class for handlers. * Every descendant should implement `void HandleImpl(ContextT& ctx)` method. */ template class Handler { public: void Handle(ContextT& ctx) { static_cast(*this).HandleImpl(ctx); } private: Handler() { } friend DerivedT; }; /** * Implements simple static Chain of responsibility pattern for Handlers. */ template class ChainedHandlers { public: explicit ChainedHandlers(HandlersT&&... hs) : handlers(std::forward(hs)...) { } /** * Creates `NextHandlerT` with provided `NextHandlerArgsT` and adds it to the end of the chain. */ template auto Use(NextHandlerArgsT&&... args) { return ChainedHandlers(std::move(std::get(handlers))..., std::move(NextHandlerT(std::forward(args)...))); } /** * Recursively calls `Handle(ctx)` method on chained `HandlersT` */ template void Handle(ContextT& ctx) { if constexpr (I < sizeof...(HandlersT)) { std::get(handlers).Handle(ctx); Handle(ctx); } } private: std::tuple handlers; }; /** * Factory class to start `ChainedHandlers` for specific `ContextT`. */ template class HandlerFactory { public: /** * Creates `FirstHandlerT` with provided `FirstHandlerArgsT` and starts a new chain of handlers (`ChainedHandlers`) * with `FirstHandlerT` at the beginning. */ template static auto Start(FirstHandlerArgsT&&... args) { return ChainedHandlers(FirstHandlerT(std::forward(args)...)); } }; } // namespace Cangjie::Interop::ObjC #endif // CANGJIE_SEMA_OBJ_C_UTILS_HANDLER_Hcangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/Utils/InteropLibBridge.cpp000066400000000000000000000141231510705540100260440ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements a thin bridge to interoplib.objc library */ #include "InteropLibBridge.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Node.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; namespace { constexpr auto INTEROPLIB_NATIVE_OBJ_C_ID = "NativeObjCId"; constexpr auto INTEROPLIB_NATIVE_OBJ_C_SEL = "NativeObjCSel"; constexpr auto INTEROPLIB_NATIVE_OBJ_C_SUPER_PTR = "NativeObjCSuperPtr"; constexpr auto INTEROPLIB_REGISTRY_ID = "RegistryId"; constexpr auto INTEROPLIB_OBJ_C_RUNTIME = "ObjCRuntime"; constexpr auto INTEROPLIB_OBJ_C_UNREACHABLE_CODE_EXCEPTION = "ObjCUnreachableCodeException"; constexpr auto INTEROPLIB_OBJ_C_GET_FROM_REGISTRY_BY_NATIVE_HANDLE = "getFromRegistryByHandle"; constexpr auto INTEROPLIB_OBJ_C_GET_FROM_REGISTRY_BY_ID = "getFromRegistryById"; constexpr auto INTEROPLIB_OBJ_C_PUT_TO_REGISTRY = "putToRegistry"; constexpr auto INTEROPLIB_OBJ_C_REMOVE_FROM_REGISTRY = "removeFromRegistry"; constexpr auto INTEROPLIB_OBJ_C_ALLOC = "alloc"; constexpr auto INTEROPLIB_OBJ_C_WITH_AUTORELEASE_POOL = "withAutoreleasePool"; constexpr auto INTEROPLIB_OBJ_C_WITH_AUTORELEASE_POOL_OBJ = "withAutoreleasePoolObj"; constexpr auto INTEROPLIB_OBJ_C_REGISTER_NAME = "registerName"; constexpr auto INTEROPLIB_OBJ_C_GET_INSTANCE_VARIABLE_OBJ = "getInstanceVariableObj"; constexpr auto INTEROPLIB_OBJ_C_SET_INSTANCE_VARIABLE_OBJ = "setInstanceVariableObj"; constexpr auto INTEROPLIB_OBJ_C_GET_INSTANCE_VARIABLE = "getInstanceVariable"; constexpr auto INTEROPLIB_OBJ_C_SET_INSTANCE_VARIABLE = "setInstanceVariable"; constexpr auto INTEROPLIB_OBJ_C_GET_CLASS = "getClass"; constexpr auto OBJ_C_RUNTIME_MSG_SEND = "msgSend"; constexpr auto OBJ_C_RUNTIME_RELEASE = "release"; } // namespace Ptr InteropLibBridge::GetNativeObjCIdDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_NATIVE_OBJ_C_ID); return decl; } Ptr InteropLibBridge::GetNativeObjCIdTy() { return GetNativeObjCIdDecl()->type->ty; } Ptr InteropLibBridge::GetNativeObjCSelDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_NATIVE_OBJ_C_SEL); return decl; } Ptr InteropLibBridge::GetNativeObjCSuperPtrDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_NATIVE_OBJ_C_SUPER_PTR); return decl; } Ptr InteropLibBridge::GetRegistryIdDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_REGISTRY_ID); return decl; } Ptr InteropLibBridge::GetRegistryIdTy() { return GetRegistryIdDecl()->type->ty; } Ptr InteropLibBridge::GetObjCUnreachableCodeExceptionDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_OBJ_C_UNREACHABLE_CODE_EXCEPTION); return decl; } Ptr InteropLibBridge::GetObjCRuntimeDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_OBJ_C_RUNTIME); return decl; } Ptr InteropLibBridge::GetGetFromRegistryByNativeHandleDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_OBJ_C_GET_FROM_REGISTRY_BY_NATIVE_HANDLE); return decl; } Ptr InteropLibBridge::GetGetFromRegistryByIdDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_OBJ_C_GET_FROM_REGISTRY_BY_ID); return decl; } Ptr InteropLibBridge::GetPutToRegistryDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_OBJ_C_PUT_TO_REGISTRY); return decl; } Ptr InteropLibBridge::GetRemoveFromRegistryDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_OBJ_C_REMOVE_FROM_REGISTRY); return decl; } Ptr InteropLibBridge::GetAllocDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_OBJ_C_ALLOC); return decl; } Ptr InteropLibBridge::GetWithAutoreleasePoolDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_OBJ_C_WITH_AUTORELEASE_POOL); return decl; } Ptr InteropLibBridge::GetWithAutoreleasePoolObjDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_OBJ_C_WITH_AUTORELEASE_POOL_OBJ); return decl; } Ptr InteropLibBridge::GetRegisterNameDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_OBJ_C_REGISTER_NAME); return decl; } Ptr InteropLibBridge::GetGetInstanceVariableObjDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_OBJ_C_GET_INSTANCE_VARIABLE_OBJ); return decl; } Ptr InteropLibBridge::GetSetInstanceVariableObjDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_OBJ_C_SET_INSTANCE_VARIABLE_OBJ); return decl; } Ptr InteropLibBridge::GetGetInstanceVariableDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_OBJ_C_GET_INSTANCE_VARIABLE); return decl; } Ptr InteropLibBridge::GetSetInstanceVariableDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_OBJ_C_SET_INSTANCE_VARIABLE); return decl; } Ptr InteropLibBridge::GetGetClassDecl() { static auto decl = GetInteropLibDecl(INTEROPLIB_OBJ_C_GET_CLASS); return decl; } OwnedPtr InteropLibBridge::CreateObjCRuntimeMsgSendExpr() { return CreateMemberAccess(CreateObjCRuntimeRefExpr(), OBJ_C_RUNTIME_MSG_SEND); } OwnedPtr InteropLibBridge::CreateObjCRuntimeReleaseExpr() { return CreateMemberAccess(CreateObjCRuntimeRefExpr(), OBJ_C_RUNTIME_RELEASE); } OwnedPtr InteropLibBridge::CreateObjCRuntimeRefExpr() { return CreateRefExpr(*GetObjCRuntimeDecl()); } Ptr InteropLibBridge::GetObjCPointerDecl() { return GetObjCLangDecl("ObjCPointer"); } cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/Utils/InteropLibBridge.h000066400000000000000000000101751510705540100255140ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares a thin bridge to interoplib.objc library */ #ifndef CANGJIE_SEMA_OBJ_C_UTILS_INTEROPLIB_BRIDGE_H #define CANGJIE_SEMA_OBJ_C_UTILS_INTEROPLIB_BRIDGE_H #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Utils/SafePointer.h" namespace Cangjie::Interop::ObjC { class InteropLibBridge { public: explicit InteropLibBridge(ImportManager& importManager, DiagnosticEngine& diag) : importManager(importManager), diag(diag) { } /** * Gets NativeObjCId declaration. * id or Instance Method Pointer type. */ Ptr GetNativeObjCIdDecl(); /** * Gets NativeObjCId semantic type. */ Ptr GetNativeObjCIdTy(); /** * Gets NativeObjCSel declaration. * SEL. */ Ptr GetNativeObjCSelDecl(); /** * Gets NativeObjCSuperPtr (CPointer) declaration. * objc_super*. */ Ptr GetNativeObjCSuperPtrDecl(); /** * Gets RegistryId declaration. * An opaque identifier for Cangjie mirror objects. */ Ptr GetRegistryIdDecl(); /** * Gets RegistryId semantic type. */ Ptr GetRegistryIdTy(); /** * Gets ObjCUnreachableCodeException declaration. * An exception that has to be used to mark an unreachable code (e.g instantiation @ObjCImpl objects from Cangjie * side). */ Ptr GetObjCUnreachableCodeExceptionDecl(); Ptr GetGetFromRegistryByNativeHandleDecl(); Ptr GetGetFromRegistryByIdDecl(); Ptr GetPutToRegistryDecl(); Ptr GetRemoveFromRegistryDecl(); Ptr GetAllocDecl(); Ptr GetWithAutoreleasePoolDecl(); Ptr GetWithAutoreleasePoolObjDecl(); Ptr GetRegisterNameDecl(); Ptr GetGetInstanceVariableObjDecl(); Ptr GetSetInstanceVariableObjDecl(); Ptr GetGetInstanceVariableDecl(); Ptr GetSetInstanceVariableDecl(); Ptr GetGetClassDecl(); /** * Gets ObjCRuntime declaration. * This struct exports interface of an Objective-C runtime. */ Ptr GetObjCRuntimeDecl(); OwnedPtr CreateObjCRuntimeMsgSendExpr(); OwnedPtr CreateObjCRuntimeReleaseExpr(); /** * Get objc.lang.ObjCPointer declaration */ Ptr GetObjCPointerDecl(); private: OwnedPtr CreateObjCRuntimeRefExpr(); template auto GetInteropLibDecl(const std::string& ident) { const auto interoplibObjCPackageName = "interoplib.objc"; auto decl = importManager.GetImportedDecl(interoplibObjCPackageName, ident); if (!decl) { diag.DiagnoseRefactor(DiagKindRefactor::sema_member_not_imported, DEFAULT_POSITION, ident); return Ptr(AST::As(nullptr)); } CJC_ASSERT(decl && decl->astKind == K); return Ptr(AST::StaticAs(decl)); } template auto GetObjCLangDecl(const std::string& ident) { const auto objcLangPackageName = "objc.lang"; auto decl = importManager.GetImportedDecl(objcLangPackageName, ident); if (!decl) { diag.DiagnoseRefactor(DiagKindRefactor::sema_member_not_imported, DEFAULT_POSITION, ident); return Ptr(AST::As(nullptr)); } CJC_ASSERT(decl && decl->astKind == K); return Ptr(AST::StaticAs(decl)); } ImportManager& importManager; DiagnosticEngine& diag; }; } // namespace Cangjie::Interop::ObjC #endif // CANGJIE_SEMA_OBJ_C_UTILS_INTEROPLIB_BRIDGE_H cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/Utils/NameGenerator.cpp000066400000000000000000000116331510705540100254120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements factory class for names of different Objective-C interop entities. */ #include "ASTFactory.h" #include "NameGenerator.h" #include "cangjie/AST/Match.h" #include "NativeFFI/Utils.h" using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; using namespace Cangjie::Native::FFI; namespace { constexpr auto WRAPPER_PREFIX = "CJImpl_ObjC_"; constexpr auto DELETE_CJ_OBJECT_SUFFIX = "_deleteCJObject"; constexpr auto WRAPPER_GETTER_SUFFIX = "_get"; constexpr auto WRAPPER_SETTER_SUFFIX = "_set"; } // namespace NameGenerator::NameGenerator(const BaseMangler& mangler) : mangler(mangler) { } std::string NameGenerator::GenerateInitCjObjectName(const FuncDecl& target) { auto& params = target.funcBody->paramLists[0]->params; auto ctorName = GetObjCDeclName(target); auto mangledCtorName = GetMangledMethodName(mangler, params, ctorName); auto name = GetObjCFullDeclName(*target.outerDecl) + "_" + mangledCtorName; std::replace(name.begin(), name.end(), '.', '_'); std::replace(name.begin(), name.end(), ':', '_'); return WRAPPER_PREFIX + name; } std::string NameGenerator::GenerateDeleteCjObjectName(const ClassDecl& target) { auto name = GetObjCFullDeclName(target); std::replace(name.begin(), name.end(), '.', '_'); std::replace(name.begin(), name.end(), ':', '_'); return WRAPPER_PREFIX + name + DELETE_CJ_OBJECT_SUFFIX; } std::string NameGenerator::GenerateMethodWrapperName(const FuncDecl& target) { auto& params = target.funcBody->paramLists[0]->params; auto methodName = GetObjCDeclName(target); auto mangledMethodName = GetMangledMethodName(mangler, params, methodName); auto outerDeclName = GetObjCFullDeclName(*target.outerDecl); auto name = outerDeclName + "." + mangledMethodName; std::replace(name.begin(), name.end(), '.', '_'); std::replace(name.begin(), name.end(), ':', '_'); return WRAPPER_PREFIX + name; } std::string NameGenerator::GeneratePropGetterWrapperName(const PropDecl& target) { CJC_NULLPTR_CHECK(target.outerDecl); auto outerDeclName = GetObjCFullDeclName(*target.outerDecl); auto name = outerDeclName + "." + GetObjCDeclName(target); std::replace(name.begin(), name.end(), '.', '_'); std::replace(name.begin(), name.end(), ':', '_'); return WRAPPER_PREFIX + name + WRAPPER_GETTER_SUFFIX; } std::string NameGenerator::GetPropSetterWrapperName(const PropDecl& target) { CJC_NULLPTR_CHECK(target.outerDecl); auto outerDeclName = GetObjCFullDeclName(*target.outerDecl); auto name = outerDeclName + "." + GetObjCDeclName(target); std::replace(name.begin(), name.end(), '.', '_'); std::replace(name.begin(), name.end(), ':', '_'); return WRAPPER_PREFIX + name + WRAPPER_SETTER_SUFFIX; } std::string NameGenerator::GetFieldGetterWrapperName(const VarDecl& target) { CJC_NULLPTR_CHECK(target.outerDecl); auto outerDeclName = GetObjCFullDeclName(*target.outerDecl); auto name = outerDeclName + "." + GetObjCDeclName(target); std::replace(name.begin(), name.end(), '.', '_'); std::replace(name.begin(), name.end(), ':', '_'); return WRAPPER_PREFIX + name + WRAPPER_GETTER_SUFFIX; } std::string NameGenerator::GetFieldSetterWrapperName(const VarDecl& target) { CJC_NULLPTR_CHECK(target.outerDecl); auto outerDeclName = GetObjCFullDeclName(*target.outerDecl); auto name = outerDeclName + "." + GetObjCDeclName(target); std::replace(name.begin(), name.end(), '.', '_'); std::replace(name.begin(), name.end(), ':', '_'); return WRAPPER_PREFIX + name + WRAPPER_SETTER_SUFFIX; } Ptr NameGenerator::GetUserDefinedObjCName(const Decl& target) { for (auto& anno : target.annotations) { if (anno->kind != AnnotationKind::OBJ_C_MIRROR && anno->kind != AnnotationKind::OBJ_C_IMPL && anno->kind != AnnotationKind::FOREIGN_NAME) { continue; } CJC_ASSERT(anno->args.size() < 2); if (anno->args.empty()) { break; } CJC_ASSERT(anno->args[0]->expr->astKind == ASTKind::LIT_CONST_EXPR); auto lce = As(anno->args[0]->expr.get()); CJC_ASSERT(lce); return &lce->stringValue; } return nullptr; } std::string NameGenerator::GetObjCDeclName(const Decl& target) { auto foreignName = GetUserDefinedObjCName(target); if (foreignName) { return *foreignName; } return target.identifier; } std::string NameGenerator::GetObjCFullDeclName(const Decl& target) { auto name = GetUserDefinedObjCName(target); if (name) { return *name; } auto ret = target.fullPackageName + "." + target.identifier; return ret; } cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/Utils/NameGenerator.h000066400000000000000000000033421510705540100250550ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares factory class for names of different Objective-C interop entities. */ #ifndef CANGJIE_SEMA_OBJ_C_UTILS_NAME_GENERATOR_H #define CANGJIE_SEMA_OBJ_C_UTILS_NAME_GENERATOR_H #include "cangjie/AST/Node.h" #include "cangjie/Mangle/BaseMangler.h" namespace Cangjie::Interop::ObjC { class NameGenerator { public: explicit NameGenerator(const BaseMangler& mangler); std::string GenerateInitCjObjectName(const AST::FuncDecl& target); std::string GenerateDeleteCjObjectName(const AST::ClassDecl& target); std::string GenerateMethodWrapperName(const AST::FuncDecl& target); std::string GeneratePropGetterWrapperName(const AST::PropDecl& target); std::string GetPropSetterWrapperName(const AST::PropDecl& target); std::string GetFieldGetterWrapperName(const AST::VarDecl& target); std::string GetFieldSetterWrapperName(const AST::VarDecl& target); /** * Returns name declared in @ForeignName or target.identifier if no foreign name specified */ std::string GetObjCDeclName(const AST::Decl& target); /** * Returns name declared in @ObjC attribute or {target.fullPackageName}.{target.identifier} * if no name in attribute specified */ std::string GetObjCFullDeclName(const AST::Decl& target); private: Ptr GetUserDefinedObjCName(const AST::Decl& target); const BaseMangler& mangler; }; } // namespace Cangjie::Interop::ObjC #endif // CANGJIE_SEMA_OBJ_C_UTILS_NAME_GENERATOR_H cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/Utils/TypeMapper.cpp000066400000000000000000000171131510705540100247500ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeMapper.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/ASTCasting.h" using namespace Cangjie; using namespace Cangjie::AST; using namespace Cangjie::Interop::ObjC; namespace { static constexpr auto VOID_TYPE = "void"; static constexpr auto UNSUPPORTED_TYPE = "UNSUPPORTED_TYPE"; static constexpr auto INT8_TYPE = "int8_t"; static constexpr auto UINT8_TYPE = "uint8_t"; static constexpr auto INT16_TYPE = "int16_t"; static constexpr auto UINT16_TYPE = "uint16_t"; static constexpr auto INT32_TYPE = "int32_t"; static constexpr auto UINT32_TYPE = "uint32_t"; static constexpr auto INT64_TYPE = "int64_t"; static constexpr auto UINT64_TYPE = "uint64_t"; static constexpr auto NATIVE_INT_TYPE = "ssize_t"; static constexpr auto NATIVE_UINT_TYPE = "size_t"; static constexpr auto FLOAT_TYPE = "float"; static constexpr auto DOUBLE_TYPE = "double"; static constexpr auto BOOL_TYPE = "BOOL"; static constexpr auto OBJC_LANG_PACKAGE = "objc.lang"; static constexpr auto OBJC_POINTER_TYPE = "ObjCPointer"; } // namespace Ptr TypeMapper::Cj2CType(Ptr cjty) const { CJC_NULLPTR_CHECK(cjty); if (cjty->IsCoreOptionType()) { CJC_ABORT(); return nullptr; } if (IsValidObjCMirror(*cjty) || IsObjCImpl(*cjty)) { return bridge.GetNativeObjCIdTy(); } if (IsObjCPointer(*cjty)) { CJC_ASSERT(cjty->typeArgs.size() == 1); return typeManager.GetPointerTy(Cj2CType(cjty->typeArgs[0])); } CJC_ASSERT(cjty->IsBuiltin()); return cjty; } std::string TypeMapper::Cj2ObjCForObjC(const Ty& from) const { switch (from.kind) { case TypeKind::TYPE_UNIT: return VOID_TYPE; case TypeKind::TYPE_INT8: return INT8_TYPE; case TypeKind::TYPE_INT16: return INT16_TYPE; case TypeKind::TYPE_INT32: return INT32_TYPE; case TypeKind::TYPE_INT64: case TypeKind::TYPE_IDEAL_INT: // alias for int64 return INT64_TYPE; case TypeKind::TYPE_INT_NATIVE: return NATIVE_INT_TYPE; case TypeKind::TYPE_UINT8: return UINT8_TYPE; case TypeKind::TYPE_UINT16: return UINT16_TYPE; case TypeKind::TYPE_UINT32: return UINT32_TYPE; case TypeKind::TYPE_UINT64: return UINT64_TYPE; case TypeKind::TYPE_UINT_NATIVE: return NATIVE_UINT_TYPE; case TypeKind::TYPE_FLOAT32: return FLOAT_TYPE; case TypeKind::TYPE_FLOAT64: case TypeKind::TYPE_IDEAL_FLOAT: return DOUBLE_TYPE; case TypeKind::TYPE_BOOLEAN: return BOOL_TYPE; case TypeKind::TYPE_STRUCT: if (IsObjCPointer(from)) { return Cj2ObjCForObjC(*from.typeArgs[0]) + "*"; } /* continue to next case */ case TypeKind::TYPE_CLASS: case TypeKind::TYPE_INTERFACE: if (IsValidObjCMirror(from) || IsObjCImpl(from)) { return from.name + "*"; } return UNSUPPORTED_TYPE; case TypeKind::TYPE_POINTER: return Cj2ObjCForObjC(*from.typeArgs[0]) + "*"; default: CJC_ABORT(); return UNSUPPORTED_TYPE; } } bool TypeMapper::IsObjCCompatible(const Ty& ty) { switch (ty.kind) { case TypeKind::TYPE_UNIT: case TypeKind::TYPE_INT8: case TypeKind::TYPE_INT16: case TypeKind::TYPE_INT32: case TypeKind::TYPE_INT64: case TypeKind::TYPE_INT_NATIVE: case TypeKind::TYPE_IDEAL_INT: case TypeKind::TYPE_UINT8: case TypeKind::TYPE_UINT16: case TypeKind::TYPE_UINT32: case TypeKind::TYPE_UINT64: case TypeKind::TYPE_UINT_NATIVE: case TypeKind::TYPE_FLOAT32: case TypeKind::TYPE_FLOAT64: case TypeKind::TYPE_IDEAL_FLOAT: case TypeKind::TYPE_BOOLEAN: return true; case TypeKind::TYPE_STRUCT: if (IsObjCPointer(ty)) { CJC_ASSERT(ty.typeArgs.size() == 1); return IsObjCCompatible(*ty.typeArgs[0]); } /* fall through to next case */ case TypeKind::TYPE_CLASS: case TypeKind::TYPE_INTERFACE: if (IsValidObjCMirror(ty) || IsObjCImpl(ty)) { return true; } default: return false; } } bool TypeMapper::IsObjCMirror(const Decl& decl) { return decl.TestAttr(Attribute::OBJ_C_MIRROR); } bool TypeMapper::IsObjCMirrorSubtype(const Decl& decl) { return IsObjCMirrorSubtype(*decl.ty); } bool TypeMapper::IsObjCImpl(const Decl& decl) { if (!decl.TestAttr(Attribute::OBJ_C_MIRROR_SUBTYPE) || decl.TestAttr(Attribute::OBJ_C_MIRROR)) { return false; } return decl.HasAnno(AnnotationKind::OBJ_C_IMPL); } bool TypeMapper::IsValidObjCMirror(const Ty& ty) { auto classLikeTy = DynamicCast(&ty); if (!classLikeTy) { return false; } auto hasAttr = classLikeTy->commonDecl && IsObjCMirror(*classLikeTy->commonDecl); if (!hasAttr) { return false; } // all super interfaces must be @ObjCMirror for (auto parent : classLikeTy->GetSuperInterfaceTys()) { if (!IsObjCMirror(*parent->decl)) { return false; } } // superclass must be @ObjCMirror if (auto classTy = DynamicCast(&ty); classTy) { // Hierarchy root @ObjCMirror class if (!classTy->GetSuperClassTy() || classTy->GetSuperClassTy()->IsObject()) { return true; } return IsValidObjCMirror(*classTy->GetSuperClassTy()); } return false; } bool TypeMapper::IsObjCMirrorSubtype(const Ty& ty) { if (auto classTy = DynamicCast(&ty); classTy && classTy->GetSuperClassTy() && !classTy->GetSuperClassTy()->IsObject() ) { if (!IsObjCMirrorSubtype(*classTy->GetSuperClassTy()) && (!classTy->GetSuperClassTy()->decl || !IsObjCMirror(*classTy->GetSuperClassTy()->decl))) { return false; } return true; } if (auto ity = DynamicCast(&ty)) { if (ity->GetSuperInterfaceTys().empty()) { return false; } for (auto parent : ity->GetSuperInterfaceTys()) { if (IsObjCMirror(*parent->decl)) { return true; } } } return false; } bool TypeMapper::IsObjCImpl(const Ty& ty) { auto classLikeTy = DynamicCast(&ty); return classLikeTy && classLikeTy->commonDecl && IsObjCImpl(*classLikeTy->commonDecl); } bool TypeMapper::IsObjCMirror(const Ty& ty) { auto classLikeTy = DynamicCast(&ty); return classLikeTy && classLikeTy->commonDecl && IsObjCMirror(*classLikeTy->commonDecl); } namespace { bool IsObjCPointerImpl(const StructDecl& structDecl) { if (structDecl.fullPackageName != OBJC_LANG_PACKAGE) { return false; } if (structDecl.identifier.Val() != OBJC_POINTER_TYPE) { return false; } return true; } } bool TypeMapper::IsObjCPointer(const Decl& decl) { if (auto structDecl = DynamicCast(&decl)) { return IsObjCPointerImpl(*structDecl); } return false; } bool TypeMapper::IsObjCPointer(const Ty& ty) { if (auto structTy = DynamicCast(&ty)) { return IsObjCPointerImpl(*structTy->decl); } return false; } cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/Utils/TypeMapper.h000066400000000000000000000030621510705540100244130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares type mappings Cangjie <-> Objective-C helper */ #ifndef CANGJIE_SEMA_OBJ_C_UTILS_TYPE_MAPPER_H #define CANGJIE_SEMA_OBJ_C_UTILS_TYPE_MAPPER_H #include "cangjie/AST/Node.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Utils/SafePointer.h" #include "InteropLibBridge.h" namespace Cangjie::Interop::ObjC { class TypeMapper { public: explicit TypeMapper(InteropLibBridge& bridge, TypeManager& typeManager) : bridge(bridge), typeManager(typeManager) { } std::string Cj2ObjCForObjC(const AST::Ty& from) const; Ptr Cj2CType(Ptr cjty) const; static bool IsObjCCompatible(const AST::Ty& ty); static bool IsObjCMirror(const AST::Decl& decl); static bool IsObjCMirrorSubtype(const AST::Decl& decl); static bool IsObjCImpl(const AST::Decl& decl); static bool IsValidObjCMirror(const AST::Ty& ty); static bool IsObjCMirrorSubtype(const AST::Ty& ty); static bool IsObjCImpl(const AST::Ty& ty); static bool IsObjCMirror(const AST::Ty& ty); static bool IsObjCPointer(const AST::Decl& decl); static bool IsObjCPointer(const AST::Ty& ty); private: InteropLibBridge& bridge; TypeManager& typeManager; }; } // namespace Cangjie::Interop::ObjC #endif // CANGJIE_SEMA_OBJ_C_UTILS_TYPE_MAPPER_H cangjie_compiler-1.0.7/src/Sema/NativeFFI/ObjC/readme.md000066400000000000000000000003601510705540100226310ustar00rootroot00000000000000# Native FFI with Objective-C This directory includes semantics logics related to Cangjie **Native** FFI with Objective-C. It declares desugaring for mirrors (foreign types), impls (export types), Objective-C source code generation, etc. cangjie_compiler-1.0.7/src/Sema/NativeFFI/Utils.cpp000066400000000000000000000142541510705540100220500ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Utils.h" #include "TypeCheckUtil.h" #include "Desugar/AfterTypeCheck.h" #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/AST/Match.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/ConstantsUtils.h" namespace Cangjie::Native::FFI { using namespace TypeCheckUtil; OwnedPtr CreateThisRef(Ptr target, Ptr ty, Ptr curFile) { auto thisRef = MakeOwned(); thisRef->isThis = true; thisRef->ty = ty; thisRef->ref.identifier = SrcIdentifier("this"); thisRef->ref.target = target; thisRef->curFile = curFile; return thisRef; } OwnedPtr CreateThisCall(Decl& target, FuncDecl& baseTarget, Ptr funcTy, Ptr curFile) { auto call = CreateCallExpr(CreateThisRef(Ptr(&baseTarget), funcTy, curFile), {}); call->callKind = CallKind::CALL_OBJECT_CREATION; call->ty = target.ty; call->resolvedFunction = Ptr(&baseTarget); return call; } OwnedPtr CreateUnitType(Ptr curFile) { auto ret = MakeOwned(); ret->str = "Unit"; ret->kind = TypeKind::TYPE_UNIT; ret->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); ret->curFile = curFile; return ret; } std::vector> GetParamTys(FuncParamList& params) { std::vector> paramTys; for (auto& param : params.params) { paramTys.push_back(param->ty); } return paramTys; } OwnedPtr CreateSuperRef(Ptr target, Ptr ty) { auto superRef = MakeOwned(); superRef->isSuper = true; superRef->ty = ty; superRef->ref.identifier = SrcIdentifier("super"); superRef->ref.target = target; return superRef; } OwnedPtr CreateSuperCall(Decl& target, FuncDecl& baseTarget, Ptr funcTy) { auto call = CreateCallExpr(CreateSuperRef(Ptr(&baseTarget), funcTy), {}); call->callKind = CallKind::CALL_SUPER_FUNCTION; call->ty = target.ty; call->resolvedFunction = Ptr(&baseTarget); return call; } OwnedPtr CreateType(Ptr ty) { auto res = MakeOwned(); res->ty = ty; return res; } OwnedPtr CreateFuncType(Ptr ty) { auto res = MakeOwned(); res->ty = ty; for (auto param : ty->paramTys) { res->paramTypes.push_back(CreateType(param)); } return res; } OwnedPtr CreateBoolMatch( OwnedPtr selector, OwnedPtr trueBranch, OwnedPtr falseBranch, Ptr ty) { static const auto BOOL_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); OwnedPtr truePattern = MakeOwned(); truePattern->literal = CreateLitConstExpr(LitConstKind::BOOL, "true", BOOL_TY); truePattern->ty = BOOL_TY; OwnedPtr falsePattern = MakeOwned(); falsePattern->literal = CreateLitConstExpr(LitConstKind::BOOL, "false", BOOL_TY); falsePattern->ty = BOOL_TY; auto caseTrue = CreateMatchCase(std::move(truePattern), std::move(trueBranch)); auto caseFalse = CreateMatchCase(std::move(falsePattern), std::move(falseBranch)); std::vector> matchCases; matchCases.emplace_back(std::move(caseTrue)); matchCases.emplace_back(std::move(caseFalse)); auto curFile = selector->curFile; return WithinFile(CreateMatchExpr(std::move(selector), std::move(matchCases), ty), curFile); } StructDecl& GetStringDecl(ImportManager& importManager) { static auto decl = importManager.GetCoreDecl(STD_LIB_STRING); CJC_NULLPTR_CHECK(decl); return *decl; } OwnedPtr WrapReturningLambdaCall(TypeManager& typeManager, std::vector> nodes) { auto retTy = nodes.back()->ty; auto lambda = WrapReturningLambdaExpr(typeManager, std::move(nodes)); return CreateCallExpr(std::move(lambda), {}, nullptr, retTy); } OwnedPtr WrapReturningLambdaExpr(TypeManager& typeManager, std::vector> nodes) { auto curFile = nodes[0]->curFile; CJC_ASSERT(!nodes.empty()); std::vector> lambdaCallParams; std::vector> paramLists; paramLists.push_back(CreateFuncParamList(std::move(lambdaCallParams))); auto retTy = nodes.back()->ty; auto retExpr = CreateReturnExpr(ASTCloner::Clone(Ptr(As(nodes.back().get())))); retExpr->ty = TypeManager::GetNothingTy(); nodes.pop_back(); auto lambda = CreateLambdaExpr( CreateFuncBody( std::move(paramLists), nullptr, CreateBlock(std::move(nodes), retTy), retTy)); retExpr->refFuncBody = lambda->funcBody.get(); lambda->funcBody->body->body.push_back(std::move(retExpr)); lambda->curFile = curFile; lambda->ty = typeManager.GetFunctionTy({}, retTy); return lambda; } std::string GetCangjieLibName(const std::string& outputLibPath, const std::string& fullPackageName, bool trimmed) { if (FileUtil::IsDir(outputLibPath)) { return fullPackageName; } auto outputFileName = FileUtil::GetFileName(outputLibPath); constexpr std::string_view libPrefix = "lib"; // check if [outputLibPath] starts with [LIB_PREFIX] if (outputFileName.rfind(libPrefix, 0) == 0) { if (!trimmed) { return outputFileName; } size_t extIdx = outputFileName.find_last_of("."); if (extIdx == std::string::npos) { return fullPackageName; } return outputFileName.substr(libPrefix.size(), extIdx - libPrefix.size()); } return fullPackageName; } std::string GetMangledMethodName(const BaseMangler& mangler, const std::vector>& params, const std::string& methodName) { std::string name(methodName); for (auto& param : params) { auto mangledParam = mangler.MangleType(*param->ty); std::replace(mangledParam.begin(), mangledParam.end(), '.', '_'); name += mangledParam; } return name; } } cangjie_compiler-1.0.7/src/Sema/NativeFFI/Utils.h000066400000000000000000000074451510705540100215210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares auxiliary methods for Cangjie Native FFI implementation with different targets */ #ifndef CANGJIE_SEMA_NATIVE_FFI_UTILS #define CANGJIE_SEMA_NATIVE_FFI_UTILS #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" namespace Cangjie::Native::FFI { using namespace AST; enum class ArrayOperationKind: uint8_t { CREATE, GET, SET, GET_LENGTH }; OwnedPtr CreateThisRef(Ptr target, Ptr ty, Ptr curFile); OwnedPtr CreateThisCall(Decl& target, FuncDecl& baseTarget, Ptr funcTy, Ptr curFile); OwnedPtr CreateUnitType(Ptr curFile); std::vector> GetParamTys(FuncParamList& params); OwnedPtr CreateSuperRef(Ptr target, Ptr ty); OwnedPtr CreateSuperCall(Decl& target, FuncDecl& baseTarget, Ptr funcTy); ArrayOperationKind GetArrayOperationKind(Decl& decl); template std::vector> Nodes(OwnedPtr&&... args) { std::vector> nodes; (nodes.push_back(std::forward>(args)), ...); return nodes; } namespace details { template void WrapArg(std::vector>* funcArgs, OwnedPtr&& e) { CJC_ASSERT(e); if (auto ptr = As(e.get())) { funcArgs->emplace_back(ptr); } else { funcArgs->push_back(CreateFuncArg(std::forward>(e))); } } } // namespace details template OwnedPtr WithinFile(OwnedPtr node, Ptr curFile) { CJC_NULLPTR_CHECK(curFile); node->curFile = curFile; return node; } template OwnedPtr CreateCall(Ptr fd, Ptr curFile, OwnedPtr&&... args) { if (!fd) { return nullptr; } std::vector> funcArgs; (details::WrapArg(&funcArgs, std::forward>(args)), ...); auto funcTy = StaticCast(fd->ty); return CreateCallExpr(WithinFile(CreateRefExpr(*fd), curFile), std::move(funcArgs), fd, funcTy->retTy, CallKind::CALL_DECLARED_FUNCTION); } OwnedPtr CreateType(Ptr ty); OwnedPtr CreateFuncType(Ptr ty); OwnedPtr CreateBoolMatch(OwnedPtr selector, OwnedPtr trueBranch, OwnedPtr falseBranch, Ptr ty); StructDecl& GetStringDecl(ImportManager& importManager); /** * Returns synthetic lambda call that includes nodes. The result of the call expr is the last node: * * { * node1; * node2; * ... * return noden; * }() */ OwnedPtr WrapReturningLambdaCall(TypeManager& typeManager, std::vector> nodes); OwnedPtr WrapReturningLambdaExpr(TypeManager& typeManager, std::vector> nodes); /** * Returns trimmed cangjie library name. * For a filename in [outputLibPath] matched to "lib{libname}.{ext}" it returns {libname} if [trimmed] = `true` * and "lib{libname}.{ext}" if [trimmed] = `false`. * For other cases, it returns [fullPackageName] */ std::string GetCangjieLibName(const std::string& outputLibPath, const std::string& fullPackageName, bool trimmed = true); std::string GetMangledMethodName(const BaseMangler& mangler, const std::vector>& params, const std::string& methodName); } // namespace Cangjie::Interop::Java #endif // CANGJIE_SEMA_NATIVE_FFI_UTILS cangjie_compiler-1.0.7/src/Sema/NativeFFI/readme.md000066400000000000000000000002151510705540100220130ustar00rootroot00000000000000# Native FFI This directory includes semantics logics related to Cangjie **Native** FFI with other languages such as: - Java - Objective-c cangjie_compiler-1.0.7/src/Sema/PatternUsefulness.cpp000066400000000000000000001166141510705540100226720ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the Usefulness checking related classes. * * We adopt the algorithm described in Luc Maranget's paper, warnings for pattern matching, to do exhaustiveness * and reachable checking. And some ideas are from Rust's usefulness checking module. * * The algorithm should be called after type checking/inferring, and it requires the type information to work. * The core of the algorithm is the concept of "usefulness". Informally, the term usefulness can be viewed as * the reachable. Please refer to the paper if you want a formal definition. The exhaustive and reachable * checking can be solved by usefulness checking. And we stick to use the term "usefulness", which is consistent * with the paper. * * Suppose that p_1, p_2, ..., p_n are all the match arms given by a user. * match (x) { * case p_1 => {} * case p_2 => {} * case ... => {} * case p_n => {} * } * 1. Reachable checking: In order to check if p_i (1 <= i <= n) is reachable, suppose that p_1, ..., p_{i-1} have * been checked and they are all reachable. If q is useful, which means q is reachable, so far so good; * On the other hand, if q is not useful, the compiler should report that q is unreachable. * 2. Exhaustiveness checking: We append a wildcard to the end and check the usefulness of the wildcard. * match (x) { * case p_1 => {} * case p_2 => {} * case ... => {} * case p_n => {} * case _ => {} // The wildcard is inserted virtually * } * If the wildcard is useful, there must be cases not covered by p_1, p_2, ..., p_n, and the match expression is * nonexhaustive. A diagnose message should be reported. * Otherwise, the match expression is exhaustive. * 3. Report missed patterns: p_i (1 <= i <= n) is useful, if and only if there are cases that p_i could match but * p_1, ..., p_{i-1} couldn't. These cases are the "witnesses" of the usefulness of p_i. * In order to report missed patterns, we find the witnesses of the virtually inserted wildcard described before. * They are the uncovered cases. * In fact, we use FindWitnesses() to check the usefulness: a match arm is useful iff it has witnesses. */ #include "PatternUsefulness.h" #include #include #include #include #include #include #include #include "TypeCheckUtil.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Types.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/AST/ASTCasting.h" #include "cangjie/Utils/CheckUtils.h" namespace Cangjie { using namespace AST; namespace { bool IsValidTy(Ty& ty) { if (!Ty::IsTyCorrect(&ty)) { return false; } if (ty.IsEnum()) { auto& enumTy = static_cast(ty); if (!enumTy.decl) { return false; } for (auto& ctor : enumTy.decl->constructors) { if (!ctor || !Ty::IsTyCorrect(ctor->ty)) { return false; } } } return true; } inline bool IsSuperTypeForFFI(const Ty& ty) { return ty.IsCType(); } template bool IsSealedLikeCommon(const T& ty) { CJC_NULLPTR_CHECK(ty.declPtr); const auto& decl = *ty.declPtr; if (decl.TestAttr(Attribute::SEALED) || decl.TestAttr(Attribute::PRIVATE)) { return true; } return false; } bool HasVisibleInit(const ClassDecl& cd) { CJC_NULLPTR_CHECK(cd.body); return Utils::In(cd.body->decls, [](auto& decl) { CJC_NULLPTR_CHECK(decl); return decl->TestAttr(Attribute::CONSTRUCTOR) && decl->TestAnyAttr(Attribute::PUBLIC, Attribute::PROTECTED); }); } bool IsSealedLike(const Ty& ty) { if (ty.IsClass()) { auto& classTy = StaticCast(ty); CJC_NULLPTR_CHECK(classTy.declPtr); return IsSealedLikeCommon(classTy) || !HasVisibleInit(*classTy.declPtr); } if (ty.IsInterface()) { return IsSealedLikeCommon(StaticCast(ty)); } // Other types cannot be inherited, they are all sealed-like. return true; } Ptr AsSealedLikeClassLikeTy(Ty& ty) { if (!ty.IsClassLike()) { return nullptr; } ClassLikeTy& clt = StaticCast(ty); // Generic types are not supported so far. if (!clt.typeArgs.empty()) { return nullptr; } if (IsSealedLike(clt)) { return &clt; } return nullptr; } bool LitConstExprEq(const LitConstExpr& left, const LitConstExpr& right) { if (left.kind != right.kind) { return false; } switch (left.kind) { case LitConstKind::UNIT: case LitConstKind::NONE: { return true; } case LitConstKind::BOOL: { return left.constNumValue.asBoolean == right.constNumValue.asBoolean; } case LitConstKind::INTEGER: case LitConstKind::RUNE_BYTE: { return left.constNumValue.asInt.Uint64() == right.constNumValue.asInt.Uint64(); } #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wfloat-equal" #endif case LitConstKind::FLOAT: { return left.constNumValue.asFloat.value - right.constNumValue.asFloat.value == 0; } #if defined(__clang__) #pragma clang diagnostic pop #endif case LitConstKind::RUNE: case LitConstKind::STRING: case LitConstKind::JSTRING: { return left.stringValue == right.stringValue; } default: CJC_ASSERT(false); return false; } } enum class ConstructorKind { UNIT, BOOLEAN, // (bool) NON_EXHAUSTIVE_LITERAL, // (const LitConstExpr&), i.e., String, Rune, Float, ... ENUM, // (const std::string&, const Ty&), the constructor of enum NON_EXHAUSTIVE_ENUM, TUPLE, TYPE, // (Ty&) WILDCARD, MISSING, // A special wildcard to cover non-exhaustive and type patterns OR, // patterns separated by vertical bars INVALID, }; struct EnumConstructor { const std::string& identifier; const Ty& ty; EnumConstructor(const std::string& identifier, const Ty& ty) : identifier(identifier), ty(ty) { } }; using ConstructorUnion = std::variant, std::reference_wrapper, std::reference_wrapper, EnumConstructor>; class Constructor { public: static Constructor Unit() { return Constructor(ConstructorKind::UNIT, 0); } static Constructor Boolean(bool boolCtor) { return Constructor(ConstructorKind::BOOLEAN, 0, boolCtor); } static Constructor NonExhaustive(const LitConstExpr& litCtor) { return Constructor(ConstructorKind::NON_EXHAUSTIVE_LITERAL, 0, litCtor); } static Constructor NonExhaustiveEnum() { return Constructor(ConstructorKind::NON_EXHAUSTIVE_ENUM, 0); } static Constructor Tuple(size_t numArgs) { return Constructor(ConstructorKind::TUPLE, numArgs); } static Constructor Enum(const Decl& decl, const Ty& ty) { if (!Ty::IsTyCorrect(&ty)) { return Invalid(); } size_t numArgs = 0; if (decl.astKind == ASTKind::FUNC_DECL) { numArgs = static_cast(decl).funcBody->paramLists[0]->params.size(); } return Constructor(ConstructorKind::ENUM, numArgs, EnumConstructor(decl.identifier, ty)); } static Constructor Type(Ty& ty) { return Constructor(ConstructorKind::TYPE, 0, ty); } static Constructor Wildcard() { return Constructor(ConstructorKind::WILDCARD, 0); } static Constructor Or(size_t numArgs) { return Constructor(ConstructorKind::OR, numArgs); } static Constructor Missing() { return Constructor(ConstructorKind::MISSING, 0); } static Constructor Invalid() { return Constructor(ConstructorKind::INVALID, 0); } static Constructor FromLiteral(const Expr& expr) { if (expr.astKind != ASTKind::LIT_CONST_EXPR) { CJC_ABORT(); return Constructor::Invalid(); } return FromLitConstExpr(static_cast(expr)); } std::string ToString() const { switch (kind) { case ConstructorKind::UNIT: { return "()"; } case ConstructorKind::BOOLEAN: { return std::get(ctor) ? "true" : "false"; } case ConstructorKind::ENUM: { return std::get(ctor).identifier; } case ConstructorKind::WILDCARD: case ConstructorKind::MISSING: { return std::string(TypeCheckUtil::WILDCARD_CHAR); } case ConstructorKind::NON_EXHAUSTIVE_LITERAL: { return std::get>(ctor).get().stringValue; } case ConstructorKind::NON_EXHAUSTIVE_ENUM: return "_"; case ConstructorKind::TYPE: { return std::get>(ctor).get().String(); } default: { return "invalid"; } } } bool IsCoveredBy(TypeManager& typeManager, const Constructor& other) const { if (other.kind == ConstructorKind::WILDCARD || other.kind == ConstructorKind::MISSING) { return true; } switch (kind) { case ConstructorKind::WILDCARD: case ConstructorKind::MISSING: { return false; } case ConstructorKind::UNIT: { return other.kind == ConstructorKind::UNIT; } case ConstructorKind::BOOLEAN: { return other.kind == ConstructorKind::BOOLEAN && std::get(ctor) == std::get(other.ctor); } case ConstructorKind::NON_EXHAUSTIVE_LITERAL: { return other.kind == ConstructorKind::NON_EXHAUSTIVE_LITERAL && LitConstExprEq(std::get>(ctor), std::get>(other.ctor)); } case ConstructorKind::NON_EXHAUSTIVE_ENUM: return other.kind == ConstructorKind::WILDCARD || other.kind == ConstructorKind::NON_EXHAUSTIVE_ENUM; case ConstructorKind::ENUM: { // We don't have to worry about different enums with the same identifier, i.e., `A.E` and `B.E`. // The type checker guarantees their types must be the same enum. return other.kind == ConstructorKind::ENUM && std::get(ctor).identifier == std::get(other.ctor).identifier && std::get(ctor).ty.IsEnum() == std::get(other.ctor).ty.IsEnum() && std::get(ctor).ty.typeArgs.size() == std::get(other.ctor).ty.typeArgs.size(); } case ConstructorKind::TUPLE: { return other.kind == ConstructorKind::TUPLE; } case ConstructorKind::TYPE: { return other.kind == ConstructorKind::TYPE && typeManager.IsSubtype(&std::get>(ctor).get(), &std::get>(other.ctor).get(), true, false); } default: { return false; } } } ConstructorKind Kind() const { return kind; } const ConstructorUnion& Ctor() const { return ctor; } size_t Arity() const { return arity; } private: static Constructor FromLitConstExpr(const LitConstExpr& lit) { switch (lit.kind) { case LitConstKind::UNIT: { return Constructor::Unit(); } case LitConstKind::BOOL: { return Constructor::Boolean(lit.stringValue == "true"); } default: { return Constructor::NonExhaustive(lit); } } } Constructor(ConstructorKind kind, size_t arity) : kind(kind), arity(arity) { } Constructor(ConstructorKind kind, size_t arity, const ConstructorUnion& ctor) : kind(kind), ctor(ctor), arity(arity) { } ConstructorKind kind; ConstructorUnion ctor; size_t arity = 0; // number of arguments the constructor applys to }; class DestructedPattern { public: static DestructedPattern FromPattern(TypeManager& typeManager, Ty& goalTy, Pattern& pattern) { if (!Ty::IsTyCorrect(pattern.ty) || !IsValidTy(*pattern.ty)) { return {Constructor::Invalid(), {}, *TypeManager::GetInvalidTy(), &pattern}; } // Now we are 100% sure that `pattern.ty` contains a type. switch (pattern.astKind) { case ASTKind::CONST_PATTERN: { return FromConstPattern(static_cast(pattern)); } case ASTKind::TUPLE_PATTERN: { return FromTuplePattern(typeManager, static_cast(pattern)); } case ASTKind::ENUM_PATTERN: { return FromEnumPattern(typeManager, static_cast(pattern)); } case ASTKind::TYPE_PATTERN: { return FromTypePattern(typeManager, goalTy, static_cast(pattern)); } // Variable pattern behaves the same as a wildcard in the usefulness checking problem. // And we don't care about the name for binding. case ASTKind::WILDCARD_PATTERN: case ASTKind::VAR_PATTERN: { return {Constructor::Wildcard(), {}, *pattern.ty, &pattern}; } case ASTKind::VAR_OR_ENUM_PATTERN: { return FromPattern(typeManager, goalTy, *static_cast(pattern).pattern); } default: { CJC_ABORT(); // unreachable return {Constructor::Invalid(), {}, *TypeManager::GetInvalidTy(), &pattern}; } } } static DestructedPattern FromPatterns( TypeManager& typeManager, Ty& goalTy, std::vector>& patterns) { CJC_ASSERT(!patterns.empty()); OwnedPtr& firstPattern = patterns.front(); if (std::any_of(patterns.cbegin(), patterns.cend(), [](auto& pattern) { CJC_NULLPTR_CHECK(pattern); return !Ty::IsTyCorrect(pattern->ty) || !IsValidTy(*pattern->ty); })) { return {Constructor::Invalid(), {}, *TypeManager::GetInvalidTy(), firstPattern.get()}; } if (patterns.size() == 1) { return FromPattern(typeManager, goalTy, *firstPattern); } else { std::vector subPatterns; std::transform(patterns.begin(), patterns.end(), std::back_inserter(subPatterns), [&typeManager, &goalTy](const OwnedPtr& pattern) { CJC_NULLPTR_CHECK(pattern); return FromPattern(typeManager, goalTy, *pattern); }); return {Constructor::Or(subPatterns.size()), subPatterns, goalTy, firstPattern.get()}; } } DestructedPattern(Constructor ctor, std::vector subPatterns, Ty& goalTy, Ptr pattern) : ctor_(std::move(ctor)), subPatterns_(std::move(subPatterns)), goalTy_(goalTy), pattern_(pattern) { } std::string ToString() const { if (ctor_.Kind() == ConstructorKind::TUPLE) { return SubPatternsToString(subPatterns_); } if (ctor_.Kind() == ConstructorKind::ENUM) { if (ctor_.Arity() > 0) { // Enum constructor with fields. return ctor_.ToString() + SubPatternsToString(subPatterns_); } // Enum constructor without associated type. return ctor_.ToString(); } return ctor_.ToString(); } bool IsUnreachableTypePattern(TypeManager& typeManager) const { if (ctor_.Kind() != ConstructorKind::TYPE) { return false; } AST::Ty& patternTy = std::get>(ctor_.Ctor()).get(); // Nothing type is always unreachable. if (patternTy.IsNothing()) { return true; } // Usually, a type pattern is unreachable, // if the type pattern does not have subtyping relationships with its goal type, // For example, // match (x) { // x: Int64 // case _: Bool => ... // unreachable // } // But there are exceptions: // 1. Generic types may be reachable after instantiated. // func f(x: T) { // match (x) { // case _: Int64 => ... // not (T <: Int64 or Int64 <: T), but is reachable in `f(0)` // } // } // 2. Non-sealed like types may be reachable // package pkg // public interface I {} // public open class A {} // // import pkg.* // class B <: A & I {} // func f(x: I) { // match (x) { // case _: A => ... // not (I <: A or A <: I), but is reachable // case _: I => ... // } // } if (goalTy_.HasGeneric() || patternTy.HasGeneric() || !IsSealedLike(goalTy_) || !IsSealedLike(patternTy)) { return false; } bool goalIsSubtype = typeManager.IsSubtype(&goalTy_, &patternTy, true, false); bool patternIsSubtype = typeManager.IsSubtype(&patternTy, &goalTy_, true, false); return !goalIsSubtype && !patternIsSubtype; } bool AllWildcard() const { return ctor_.Kind() == ConstructorKind::WILDCARD || (ctor_.Kind() == ConstructorKind::TUPLE && std::all_of(subPatterns_.cbegin(), subPatterns_.cend(), [](const DestructedPattern& subPattern) { return subPattern.AllWildcard(); })); } /** * The subroutine to check the patterns recursively. * * @param otherCtor must Intersects() with the Head(). */ std::vector Specialize(const Constructor& otherCtor) const { if (ctor_.Kind() == ConstructorKind::WILDCARD) { return SpecializeWildcard(goalTy_, otherCtor); } return subPatterns_; } const Constructor& Ctor() const { return ctor_; } const std::vector& SubPatterns() const { return subPatterns_; } AST::Ty& GoalTy() { return goalTy_; } Ptr Node() { return pattern_; } private: static DestructedPattern FromConstPattern(ConstPattern& constPattern) { CJC_ASSERT(Ty::IsTyCorrect(constPattern.ty)); return {Constructor::FromLiteral(*constPattern.literal), {}, *constPattern.ty, &constPattern}; } static std::vector SubPatternsFromPatterns(TypeManager& typeManager, const std::vector>& goalTys, const std::vector>& patterns) { std::vector subPatterns; CJC_ASSERT(goalTys.size() == patterns.size()); for (size_t i = 0; i < goalTys.size(); i++) { CJC_ASSERT(Ty::IsTyCorrect(goalTys[i])); CJC_NULLPTR_CHECK(patterns[i]); (void)subPatterns.emplace_back(DestructedPattern::FromPattern(typeManager, *goalTys[i], *patterns[i])); } return subPatterns; } static DestructedPattern FromTuplePattern(TypeManager& typeManager, TuplePattern& tuplePattern) { CJC_ASSERT(Ty::IsTyCorrect(tuplePattern.ty) && tuplePattern.ty->IsTuple()); const TupleTy& tupleTy = StaticCast(*tuplePattern.ty); std::vector subPatterns = SubPatternsFromPatterns(typeManager, tupleTy.typeArgs, tuplePattern.patterns); return {Constructor::Tuple(subPatterns.size()), subPatterns, *tuplePattern.ty, &tuplePattern}; } static DestructedPattern FromEnumPattern(TypeManager& typeManager, EnumPattern& enumPattern) { CJC_NULLPTR_CHECK(enumPattern.constructor); Ptr target = enumPattern.constructor->GetTarget(); if (!target || !Ty::IsTyCorrect(enumPattern.ty)) { return {Constructor::Invalid(), {}, *TypeManager::GetInvalidTy(), &enumPattern}; } MultiTypeSubst m; typeManager.GenerateGenericMapping(m, *enumPattern.ty); Ptr instTy = typeManager.GetBestInstantiatedTy(target->ty, m); CJC_ASSERT(Ty::IsTyCorrect(instTy)); Constructor ctor = Constructor::Enum(*target, *instTy); if (instTy->IsFunc()) { const FuncTy& funcTy = StaticCast(*instTy); return {ctor, SubPatternsFromPatterns(typeManager, funcTy.paramTys, enumPattern.patterns), *enumPattern.ty, &enumPattern}; } return {ctor, {}, *enumPattern.ty, &enumPattern}; } static DestructedPattern FromTypePattern(TypeManager& typeManager, AST::Ty& goalTy, TypePattern& typePattern) { CJC_ASSERT(Ty::IsTyCorrect(typePattern.ty)); // If `goalTy <: typePattern.ty`, the type pattern can always be matched. // For example: In `match (x) { case _: ToString => ... }`, where `x: Int64`, // the type pattern is equivalent to a wildcard. // An exception is that `Nothing` is always unreachable, it will be handled by `IsUnreachableTypePattern`. if (!typePattern.ty->IsNothing() && typeManager.IsSubtype(&goalTy, typePattern.ty, true, false)) { return {Constructor::Wildcard(), {}, goalTy, &typePattern}; } return {Constructor::Type(*typePattern.ty), {}, goalTy, &typePattern}; } static std::string SubPatternsToString(const std::vector& subPatterns) { if (subPatterns.empty()) { return "()"; } auto subPatternIter = subPatterns.cbegin(); std::string result = "(" + (*subPatternIter).ToString(); ++subPatternIter; while (subPatternIter != subPatterns.cend()) { result += ", " + (*subPatternIter).ToString(); ++subPatternIter; } return result + ")"; } static std::vector SpecializeWildcard(AST::Ty& ty, const Constructor& ctor) { std::vector subPatterns; if (ctor.Kind() == ConstructorKind::TUPLE) { // Expand the wildcard, i.e., _ => (_, _, ..., _) CJC_ASSERT(AST::Ty::IsTyCorrect(&ty)); CJC_ASSERT(ty.IsTuple()); CJC_ASSERT(ctor.Arity() == ty.typeArgs.size()); for (size_t i = 0; i < ctor.Arity(); i++) { Ptr argTy = ty.typeArgs[i]; CJC_ASSERT(Ty::IsTyCorrect(argTy)); subPatterns.emplace_back(DestructedPattern(Constructor::Wildcard(), {}, *argTy, nullptr)); } return subPatterns; } if (ctor.Kind() == ConstructorKind::ENUM) { const AST::Ty& ctorTy = std::get(ctor.Ctor()).ty; if (ctorTy.IsEnum()) { // `ctor` doesn't have fields, e.g., `None`. The transform will not happen. // An empty `subPatterns` will be returned as we need. return subPatterns; } // Otherwise, `ctor` is a enum constructor with fields. CJC_ASSERT(ctorTy.IsFunc()); const AST::FuncTy& ctorFuncTy = static_cast(ctorTy); std::transform(ctorFuncTy.paramTys.cbegin(), ctorFuncTy.paramTys.cend(), std::back_inserter(subPatterns), [](auto paramTy) { CJC_ASSERT(Ty::IsTyCorrect(paramTy)); return DestructedPattern(Constructor::Wildcard(), {}, *paramTy, nullptr); }); return subPatterns; } return subPatterns; } Constructor ctor_; std::vector subPatterns_; Ty& goalTy_; Ptr pattern_; }; class PatternStack { public: PatternStack() { } explicit PatternStack(const DestructedPattern& pattern) { stack.emplace_back(pattern); } bool IsEmpty() const { return stack.empty(); } bool AllWildcard() const { return std::all_of( stack.cbegin(), stack.cend(), [](const DestructedPattern& pattern) { return pattern.AllWildcard(); }); } size_t Size() const { return stack.size(); } DestructedPattern& Head() { return stack.back(); } PatternStack Specialize(const Constructor& ctor) { std::vector newStack; // Copies the `stack` except the head (the last element) to `newStack`. std::copy(stack.cbegin(), stack.cend() - 1, std::back_inserter(newStack)); std::vector headSubPatterns = Head().Specialize(ctor); std::copy(headSubPatterns.crbegin(), headSubPatterns.crend(), std::back_inserter(newStack)); return PatternStack(std::move(newStack)); } /** * Apply the constructor on the PatternStack. * * This function is designed to recover the original patterns. For example, after applying the enum constructor * `A(Int64, Int64)` on a PatternStack `[1, 2, 3]`, we get a new PatternStack `[A(1, 2), 3]`. */ PatternStack Apply(const Constructor& ctor, Ty& ty) const { std::vector subPatterns; CJC_ASSERT(Size() >= ctor.Arity()); std::copy_n(stack.crbegin(), ctor.Arity(), std::back_inserter(subPatterns)); std::vector newStack; std::copy_n(stack.cbegin(), stack.size() - ctor.Arity(), std::back_inserter(newStack)); newStack.emplace_back(DestructedPattern(ctor, subPatterns, ty, nullptr)); return PatternStack(std::move(newStack)); } /** * Expand the or-pattern. * * Returns a vector of the expanded PatternStacks. For example, `[true | false, 1, 2, 3]` will be expanded to: * [ * [true, 1, 2, 3], * [false, 1, 2, 3] * ] */ std::vector ExpandOr() { CJC_ASSERT(!stack.empty()); CJC_ASSERT(Head().Ctor().Kind() == ConstructorKind::OR); std::vector results; std::transform(Head().SubPatterns().cbegin(), Head().SubPatterns().cend(), std::back_inserter(results), [this](const DestructedPattern& newHead) { std::vector newStack; std::copy(this->stack.cbegin(), this->stack.cend() - 1, std::back_inserter(newStack)); newStack.emplace_back(newHead); return PatternStack(std::move(newStack)); }); return results; } private: explicit PatternStack(std::vector&& stack) : stack(stack) { } std::vector stack; // Stores `DestructedPattern`s reversely, i.e., the `Head()` is the last item. }; class Matrix { public: bool IsEmpty() const { return rows.empty(); } bool HasSingleRowAndAllWildcard() const { return rows.size() == 1 && rows.front().AllWildcard(); } bool MissingAll() { return Utils::All(rows, [](const PatternStack& row) { return row.IsEmpty(); }); } bool HeadsAllWildcard() { return std::all_of(rows.begin(), rows.end(), [](PatternStack& row) { if (row.IsEmpty()) { return false; } return row.Head().AllWildcard(); }); } Matrix Specialize(TypeManager& typeManager, const Constructor& ctor) { Matrix newMatrix; for (auto& row : rows) { CJC_ASSERT(!row.IsEmpty()); if (ctor.IsCoveredBy(typeManager, row.Head().Ctor())) { PatternStack newRow = row.Specialize(ctor); newMatrix.rows.emplace_back(newRow); } } return newMatrix; } void AppendRows(std::vector&& newRows) { if (rows.empty()) { rows = std::move(newRows); } else { rows.reserve(rows.size() + newRows.size()); std::move(std::begin(newRows), std::end(newRows), std::back_inserter(rows)); newRows.clear(); } } private: std::vector rows; }; class UsefulnessChecker { public: UsefulnessChecker(DiagnosticEngine& diag, TypeManager& typeManager) : diag(diag), typeManager_(typeManager) { } UsefulnessChecker(DiagnosticEngine& diag, TypeManager& typeManager, Matrix&& matrix) : diag(diag), typeManager_(typeManager), matrix_(matrix) { } /** * Find the witnesses of usefulness. * * There is an invariant in this function: the size of each returned PatternStacks is equal to the size of @param * vec. */ std::vector FindWitnesses(PatternStack& vec) { // Base case: if (vec.IsEmpty()) { if (matrix_.IsEmpty()) { return {PatternStack()}; } return {}; } if (matrix_.HasSingleRowAndAllWildcard()) { return {}; } // Induction: DestructedPattern& head = vec.Head(); if (head.IsUnreachableTypePattern(typeManager_)) { return {}; } if (head.Ctor().Kind() == ConstructorKind::WILDCARD) { return FindWitnessesForWildcard(vec); } if (head.Ctor().Kind() == ConstructorKind::OR) { return FindWitnessesForExpandedOr(vec.ExpandOr()); } if (head.Ctor().Kind() == ConstructorKind::TYPE) { PatternStack wildcard = PatternStack(DestructedPattern(Constructor::Wildcard(), {}, head.GoalTy(), nullptr)); if (FindWitnessesForWildcard(wildcard).empty()) { return {}; } } return FindWitnessesBySpecialization(vec, head.Ctor()); } void AddWitnesses(std::vector&& witnesses) { matrix_.AppendRows(std::move(witnesses)); } private: /** * Split the wildcard into a vector of possible constructors. * * Returns a single wildcard if the matrix is empty, otherwise, returns all the possible constructors. For * example: match (Some(true)) { case None => {} * } * The missing cases are `Some(true)` and `Some(false)`, but `Some(_)` is preferred. */ static std::vector SplitWildcard(TypeManager& typeManager, Ty& ty, Matrix& matrix, bool isABIStable) { if (matrix.MissingAll()) { return {Constructor::Wildcard()}; } if (auto sealedTy = AsSealedLikeClassLikeTy(ty); sealedTy && !IsSuperTypeForFFI(ty) && !isABIStable) { std::vector ctors; SplitWildcardForSealed(ctors, *sealedTy); return ctors; } if (!Ty::IsTyCorrect(&ty)) { return {Constructor::Missing()}; } if (ty.IsUnit()) { return {Constructor::Unit()}; } if (ty.IsBoolean()) { return {Constructor::Boolean(false), Constructor::Boolean(true)}; } if (ty.IsTuple()) { return {Constructor::Tuple(ty.typeArgs.size())}; } if (ty.IsEnum()) { MultiTypeSubst m; typeManager.GenerateGenericMapping(m, ty); std::vector ctors; auto& enumTy = StaticCast(ty); for (const auto& decl : enumTy.declPtr->constructors) { if (auto varDecl = DynamicCast(decl.get()); varDecl) { ctors.emplace_back(Constructor::Enum(*varDecl, ty)); } else if (auto funcDecl = DynamicCast(decl.get()); funcDecl) { ctors.emplace_back( Constructor::Enum(*funcDecl, *typeManager.GetBestInstantiatedTy(funcDecl->ty, m))); } } if (enumTy.IsNonExhaustive()) { ctors.push_back(Constructor::NonExhaustiveEnum()); } return ctors; } return {Constructor::Missing()}; } static void SplitWildcardForSealed(std::vector& ctors, ClassLikeTy& sealedTy) { std::set, CmpTyByName> directSubtypes(sealedTy.directSubtypes.cbegin(), sealedTy.directSubtypes.cend()); for (Ptr subTy : directSubtypes) { CJC_ASSERT(Ty::IsTyCorrect(subTy)); if (auto subSealed = AsSealedLikeClassLikeTy(*subTy); subSealed && !IsSuperTypeForFFI(*subTy)) { SplitWildcardForSealed(ctors, *subSealed); } else { (void)ctors.emplace_back(Constructor::Type(*subTy)); } } if (sealedTy.IsClass()) { auto& ct = StaticCast(sealedTy); CJC_NULLPTR_CHECK(ct.declPtr); if (!ct.declPtr->TestAttr(Attribute::ABSTRACT)) { ctors.emplace_back(Constructor::Type(sealedTy)); } } } std::vector FindWitnessesForWildcard(PatternStack& vec) { DestructedPattern& head = vec.Head(); CJC_ASSERT(head.Ctor().Kind() == ConstructorKind::WILDCARD); if (matrix_.HeadsAllWildcard()) { // Avoid inspecting the sub-patterns if the matrix' heads are all wildcards. return FindWitnessesBySpecialization(vec, head.Ctor()); } Ty& goalTy = head.GoalTy(); if (goalTy.IsTuple()) { // Expand the wildcard into a tuple of wildcards, i.e., _ => (_, _, ..., _), then call FindWitnesses for // the rewritted wildcards. Constructor tuple = Constructor::Tuple(goalTy.typeArgs.size()); auto stack = vec.Specialize(tuple).Apply(tuple, goalTy); return FindWitnesses(stack); } // Split the wildcard into a series of constructors, call FindWitnesses for each constructor and concatenate // the witnesses together. std::vector witnesses; auto ctors = SplitWildcard(typeManager_, goalTy, matrix_, keepABIStable); for (const Constructor& ctor : ctors) { for (const PatternStack& witness : FindWitnessesBySpecialization(vec, ctor)) { witnesses.emplace_back(witness); } } return witnesses; } std::vector FindWitnessesBySpecialization(PatternStack& vec, const Constructor& ctor) { CJC_ASSERT(!vec.IsEmpty()); std::vector witnesses; UsefulnessChecker newChecker(diag, typeManager_, matrix_.Specialize(typeManager_, ctor)); auto stack = vec.Specialize(ctor); for (const PatternStack& witness : newChecker.FindWitnesses(stack)) { witnesses.emplace_back(witness.Apply(ctor, vec.Head().GoalTy())); } return witnesses; } std::vector FindWitnessesForExpandedOr(std::vector&& vecs) const { UsefulnessChecker newChecker(diag, typeManager_, Matrix(matrix_)); std::vector> unreachables; std::vector totalWitnesses; for (PatternStack& vec : vecs) { std::vector witnesses = newChecker.FindWitnesses(vec); if (witnesses.empty()) { unreachables.emplace_back(vec.Head().Node()); } else { std::copy(witnesses.cbegin(), witnesses.cend(), std::back_inserter(totalWitnesses)); newChecker.AddWitnesses(std::move(witnesses)); } } if (!totalWitnesses.empty()) { // If the or-pattern has witnesses (the match case is reachable), some of the sub-patterns can still be // unreachable. For example, `case true | true | false`, the `true` in the middle is unreachable. // We set each sub-pattern to be UNREACHABLE and report diagnoses. for (Ptr unreachable : unreachables) { CJC_NULLPTR_CHECK(unreachable); unreachable->EnableAttr(Attribute::UNREACHABLE); diag.DiagnoseRefactor(DiagKindRefactor::sema_unreachable_pattern, *unreachable); } } // Otherwise, every sub-pattern is unreachable and the or-pattern itself is unreachable. // There is no need to report unreachable for each sub-pattern. return totalWitnesses; } DiagnosticEngine& diag; TypeManager& typeManager_; Matrix matrix_; bool keepABIStable{true}; }; } // namespace namespace PatternUsefulness { bool CheckMatchExprHasSelectorExhaustivenessAndReachability( DiagnosticEngine& diag, TypeManager& typeManager, const MatchExpr& me) { if (!me.selector || !me.selector->ty || !IsValidTy(*me.selector->ty)) { // Avoid exhaustive & reachable checking if fatal errors appeared. return true; } UsefulnessChecker checker(diag, typeManager); for (auto& mc : me.matchCases) { if (mc->patterns.empty()) { CJC_ABORT(); continue; } for (auto& pattern : mc->patterns) { if (Ty::IsInitialTy(pattern->ty)) { pattern->ty = typeManager.TryGreedySubst(me.selector->ty); } } // The PatternStack vec contains only one item in the beginning. PatternStack vec( DestructedPattern::FromPatterns(typeManager, *typeManager.TryGreedySubst(me.selector->ty), mc->patterns)); std::vector witnesses = checker.FindWitnesses(vec); if (!witnesses.empty()) { // Add the witnesses to the matrix only if the match case doesn't have guard. // Note that we should use `FindWitnesses` to check reachability even if it has a pattern guard. if (!mc->patternGuard) { checker.AddWitnesses(std::move(witnesses)); } } else { mc->EnableAttr(Attribute::UNREACHABLE); diag.DiagnoseRefactor(DiagKindRefactor::sema_unreachable_pattern, *mc, MakeRange(mc->patterns.front()->begin, mc->patterns.back()->end)); } } auto stack = PatternStack( DestructedPattern(Constructor::Wildcard(), {}, *typeManager.TryGreedySubst(me.selector->ty), nullptr)); std::vector witnesses = checker.FindWitnesses(stack); if (witnesses.empty()) { return true; } DiagnosticBuilder diagBuilder = diag.DiagnoseRefactor(DiagKindRefactor::sema_nonexhuastive_patterns, *me.selector); diagBuilder.AddMainHintArguments(me.selector->ty->String()); for (PatternStack& witness : witnesses) { // Every witness has only one item in the final result of FindWitnesses() CJC_ASSERT(witness.Size() == 1); diagBuilder.AddNote(witness.Head().ToString() + " is not covered"); } return false; } } // namespace PatternUsefulness } // namespace Cangjie cangjie_compiler-1.0.7/src/Sema/PatternUsefulness.h000066400000000000000000000014541510705540100223320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Usefulness Checking related classes, which provides * exhaustiveness and reachability checking capabilities for pattern matching. */ #ifndef CANGJIE_SEMA_USEFULNESS_H #define CANGJIE_SEMA_USEFULNESS_H #include "cangjie/AST/Node.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Sema/TypeManager.h" namespace Cangjie::PatternUsefulness { bool CheckMatchExprHasSelectorExhaustivenessAndReachability( DiagnosticEngine& diag, TypeManager& typeManager, const AST::MatchExpr& me); } #endif cangjie_compiler-1.0.7/src/Sema/PreCheck.cpp000066400000000000000000002270671510705540100206710ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the TypeChecker related classes. */ #include "Diags.h" #include "JoinAndMeet.h" #include "TypeCheckUtil.h" #include "TypeCheckerImpl.h" #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Types.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace TypeCheckUtil; using namespace AST; using namespace Sema; namespace { std::string ConstructDiagInfoForCycle(const Decl& root, const std::deque>& path) { bool cycle{false}; std::string str; for (auto& i : path) { CJC_NULLPTR_CHECK(i); if (i == &root) { cycle = true; } if (!cycle) { continue; } i->EnableAttr(Attribute::IN_REFERENCE_CYCLE); str += i->identifier + "->"; } str += root.identifier; return str; } void MarkInvalidInheritanceForNonClassLike(InheritableDecl& id) { CJC_ASSERT(!id.IsClassLikeDecl()); for (auto& type : id.inheritedTypes) { if (auto decl = Ty::GetDeclPtrOfTy(type->ty); decl && !decl->IsClassLikeDecl()) { // Non-classlike decl cannot inherit non-classlike decl, add mark avoid invalid type substitution. id.EnableAttr(Attribute::IN_REFERENCE_CYCLE); } } } TypeSubst GetSubstituteMap(const TypeManager& tyMgr, const FuncDecl& decl1, const FuncDecl& decl2) { // Generate typeMapping from the 'decl1' to the 'decl2'. TypeSubst substituteMap = tyMgr.GenerateGenericMappingFromGeneric(decl1, decl2); // Notice: these two functions must be in the same composite type or its extend, should make sure by call side. if (decl1.outerDecl && decl2.outerDecl) { // Generate typeMapping from the outerDecl of 'decl1' to the outerDecl of 'decl2'. auto parentMap = tyMgr.GenerateGenericMappingFromGeneric(*decl1.outerDecl, *decl2.outerDecl); substituteMap.merge(parentMap); } return substituteMap; } /** * Find whether the declMap in current context @p ctx has declaration of @p names and astKind @p target. * Note that names is a pair of type , whose first element is the declaration name and the second * element is the name of the scope it locates, which is the key of declMap. */ bool FindASTKindInDeclMap(const ASTContext& ctx, const Names& names, const ASTKind target) { auto decls = ctx.GetDeclsByName(names); for (auto decl : decls) { if (decl->astKind == target) { return true; } } return false; } /** * Check recursively in upper bounds when the typeArg is another generic parameter. * NOTE: Only generic param decls which defined for same declaration may be used recursively. * For example: error should be report when T < Option, U <: T since Option is not class/interface. */ bool IsGenericParamExistInUpperBounds(GenericsTy& gTy, Ty& upper) { // If exist any invalid ty, we can skip checking. if (!Ty::IsTyCorrect(&upper)) { return false; } CJC_NULLPTR_CHECK(gTy.decl); std::unordered_set> visited{&gTy}; std::queue> q; q.push(&upper); while (!q.empty()) { auto curTy = q.front(); q.pop(); if (curTy == &gTy) { return true; } if (auto [_, success] = visited.emplace(curTy); !success) { continue; } for (auto& arg : curTy->typeArgs) { CJC_ASSERT(arg); if (!arg->IsGeneric()) { q.push(arg); continue; } auto genericTy = RawStaticCast(arg); if (genericTy == &gTy) { return true; } // If genericTys are not belong to same declaration, the self recursion must not exists. CJC_NULLPTR_CHECK(genericTy->decl); if (genericTy->decl->outerDecl != gTy.decl->outerDecl) { continue; } for (auto& it : genericTy->upperBounds) { CJC_NULLPTR_CHECK(it); q.push(it); } } } return false; } bool AreUpperBoundsDirectlyRecursive(std::set> visitedGenerics, Ty& upperBound) { if (!upperBound.IsGeneric()) { return false; } auto genericTy = RawStaticCast(&upperBound); if (visitedGenerics.find(genericTy) != visitedGenerics.end()) { return true; } visitedGenerics.insert(genericTy); for (auto& it : genericTy->upperBounds) { CJC_NULLPTR_CHECK(it); if (AreUpperBoundsDirectlyRecursive(visitedGenerics, *it)) { return true; } } return false; } void CreateGenericConstraints(Generic& generic) { for (auto& param : generic.typeParameters) { auto genericTy = DynamicCast(param->ty); if (genericTy == nullptr) { continue; } generic.assumptionCollection.emplace(genericTy, genericTy->upperBounds); auto gc = MakeOwnedNode(); CopyBasicInfo(&generic, gc.get()); // Sort upper bound tys. To ensure the compiler added upperBounds are in stable order. std::set, CmpTyByName> sortedUpperTys; sortedUpperTys.insert(genericTy->upperBounds.begin(), genericTy->upperBounds.end()); for (auto& it : sortedUpperTys) { auto sub = MakeOwnedNode(); sub->ty = genericTy; CopyBasicInfo(&generic, sub.get()); auto upper = MakeOwnedNode(); upper->ty = it; CopyBasicInfo(&generic, upper.get()); gc->type = std::move(sub); gc->upperBounds.push_back(std::move(upper)); } if (!gc->upperBounds.empty()) { generic.genericConstraints.push_back(std::move(gc)); } } } } // namespace void TypeChecker::TypeCheckerImpl::CheckRedefinition(ASTContext& ctx) { std::vector syms; auto enableMacroInLSP = ci->invocation.globalOptions.enableMacroInLSP; std::function)> collector = [&syms, &collector, &enableMacroInLSP](auto node) { // Collect all decls with symbol, except decls that do not have name. static std::vector ignoredKinds = { ASTKind::PRIMARY_CTOR_DECL, ASTKind::EXTEND_DECL, ASTKind::VAR_WITH_PATTERN_DECL}; if (auto decl = DynamicCast(node); decl && !decl->TestAttr(Attribute::IS_BROKEN) && decl->symbol && !Utils::In(decl->astKind, ignoredKinds) && decl->identifier != WILDCARD_CHAR) { syms.emplace_back(decl->symbol); } if (enableMacroInLSP && node->astKind == ASTKind::FILE) { auto file = StaticAs(node); // Walk decls in macrocall to find references, for lsp. for (auto& it : file->originalMacroCallNodes) { Walker(it.get(), collector).Walk(); } } return VisitAction::WALK_CHILDREN; }; Walker(ctx.curPackage, collector).Walk(); CheckRedefinitionInDeclHelper(ctx, syms); CheckConflictDeclWithSubPackage(*ctx.curPackage); } void TypeChecker::TypeCheckerImpl::CollectDeclMapAndCheckRedefinitionForOneSymbol( ASTContext& ctx, const Symbol& sym, const Names& names) { CJC_NULLPTR_CHECK(sym.node); // 1. Duplicate decl with wildcard pattern is allowed. bool isWildCard = false; if (auto vwp = AST::As(sym.node); vwp && vwp->irrefutablePattern != nullptr) { isWildCard = vwp->irrefutablePattern->astKind == ASTKind::WILDCARD_PATTERN; } // 2. Function redefinition will not be checked in this phase. bool funcOverloading = sym.astKind == ASTKind::FUNC_DECL && (FindASTKindInDeclMap(ctx, names, ASTKind::FUNC_DECL)); // 3. Macro function redefinition will not be checked in this phase bool macroOverloading = sym.node->IsMacroCallNode() || (sym.astKind == ASTKind::MACRO_DECL && (FindASTKindInDeclMap(ctx, names, ASTKind::MACRO_DECL))); auto currDecl = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, sym.scopeName); // 4. VarDecl and FuncDecl as constructor in enumDecl can overload, other's not. bool isEnumConstructor = currDecl && currDecl->astKind == ASTKind::ENUM_DECL && sym.node->TestAttr(Attribute::ENUM_CONSTRUCTOR) && !FindASTKindInDeclMap(ctx, names, sym.astKind); // 5. Constructor and main entry will not conflict with none function decls. // Function confliction will be checked later. bool ignoredHere = isWildCard || funcOverloading || macroOverloading || isEnumConstructor || sym.node->TestAnyAttr(Attribute::CONSTRUCTOR, Attribute::MAIN_ENTRY); if (ignoredHere) { ctx.AddDeclName(names, StaticCast(*sym.node)); return; } auto found = ctx.GetDeclsByName(names); if (!found.empty()) { bool privateGlobalInDifferentFile = sym.node->TestAttr(Attribute::GLOBAL, Attribute::PRIVATE) && found.front()->TestAttr(Attribute::GLOBAL, Attribute::PRIVATE) && sym.node->curFile != found.front()->curFile; bool multiPlat = sym.node->TestAttr(Cangjie::AST::Attribute::COMMON) && found.front()->TestAttr(Attribute::PLATFORM); multiPlat = multiPlat || (sym.node->TestAttr(Attribute::PLATFORM) && found.front()->TestAttr(Attribute::COMMON)); if (!privateGlobalInDifferentFile && !multiPlat) { DiagRedefinitionWithFoundNode(diag, StaticCast(*sym.node), *found.front()); } } ctx.AddDeclName(names, StaticCast(*sym.node)); } void TypeChecker::TypeCheckerImpl::CheckRedefinitionInDeclHelper(ASTContext& ctx, std::vector& syms) { auto ignoredMacroDecl = [](const Node& node) { return node.astKind == ASTKind::MACRO_EXPAND_DECL || node.TestAttr(Attribute::MACRO_INVOKE_FUNC); }; for (auto sym : syms) { // macro expanded decls are not added into declMap. // macro invoke func can NOT be seen by developer so should not be added. if (ignoredMacroDecl(*sym->node)) { continue; } std::string scopeName = ScopeManagerApi::GetScopeNameWithoutTail(sym->scopeName); auto names = std::make_pair(sym->name, scopeName); if (ctx.curPackage->TestAttr(Attribute::TOOL_ADD)) { ctx.AddDeclName(names, StaticCast(*sym->node)); continue; // Ignore redefinition checking for cjogen package. } CollectDeclMapAndCheckRedefinitionForOneSymbol(ctx, *sym, names); } } void TypeChecker::TypeCheckerImpl::CheckConflictDeclWithSubPackage(const Package& pkg) { if (pkg.files.empty()) { return; } std::unordered_set subPkgNames; auto subDirs = FileUtil::GetDirectories(FileUtil::GetDirPath(pkg.files[0]->filePath)); for (auto& it : subDirs) { subPkgNames.emplace(it.name); } std::vector> toplevelDecls; IterateToplevelDecls(pkg, [&toplevelDecls](auto& decl) { if (decl->TestAttr(Attribute::IS_BROKEN)) { return; } if (auto vpd = DynamicCast(decl.get())) { for (auto it : FlattenVarWithPatternDecl(*vpd)) { if (auto vp = DynamicCast(it)) { toplevelDecls.emplace_back(vp->varDecl.get()); } } } else { toplevelDecls.emplace_back(decl.get()); } }); for (auto decl : toplevelDecls) { if (subPkgNames.count(decl->identifier) != 0) { auto fullSubName = pkg.fullPackageName + "." + decl->identifier; diag.DiagnoseRefactor(DiagKindRefactor::sema_conflict_with_sub_package, *decl, MakeRange(decl->identifier), decl->identifier.Val(), fullSubName); } } } Ptr TypeChecker::TypeCheckerImpl::GetTyFromASTType(ASTContext& ctx, Ptr type) { if (type == nullptr) { return TypeManager::GetInvalidTy(); } // If ty is not nullptr, it means this type's ty is already set, no matter whether this type is legal or not. if (!Ty::IsInitialTy(type->ty)) { return type->ty; } switch (type->astKind) { case ASTKind::PRIMITIVE_TYPE: { auto primitiveType = StaticAs(type); primitiveType->ty = TypeManager::GetPrimitiveTy(primitiveType->kind); return primitiveType->ty; } case ASTKind::REF_TYPE: { auto refType = StaticAs(type); refType->ty = GetTyFromASTType(ctx, *refType); return refType->ty; } case ASTKind::QUALIFIED_TYPE: { auto qualifiedType = StaticAs(type); qualifiedType->ty = GetTyFromASTType(ctx, *qualifiedType); return qualifiedType->ty; } case ASTKind::VARRAY_TYPE: { auto varrayType = StaticAs(type); varrayType->ty = GetTyFromASTType(ctx, *varrayType); return varrayType->ty; } case ASTKind::TUPLE_TYPE: { auto tupleType = StaticAs(type); tupleType->ty = GetTyFromASTType(ctx, *tupleType); return tupleType->ty; } case ASTKind::PAREN_TYPE: { auto parenType = StaticAs(type); parenType->ty = GetTyFromASTType(ctx, parenType->type.get()); return parenType->ty; } case ASTKind::FUNC_TYPE: { auto funcType = StaticAs(type); funcType->ty = GetTyFromASTType(ctx, *funcType); return funcType->ty; } case ASTKind::OPTION_TYPE: { auto optionType = StaticAs(type); optionType->ty = GetTyFromASTType(ctx, *optionType); return optionType->ty; } case ASTKind::INVALID_TYPE: { auto invalidType = StaticAs(type); invalidType->ty = RawStaticCast(TypeManager::GetInvalidTy()); return invalidType->ty; } case ASTKind::THIS_TYPE: CJC_ABORT(); // ThisType should not enter current func. // Fall-through default: { return TypeManager::GetInvalidTy(); } } } Ptr TypeChecker::TypeCheckerImpl::GetTyFromASTType(ASTContext& ctx, RefType& rt) { // If rt.ty is nullptr but rt.ref.target is packageDecl, rt is the base of another qualified type and is already // resolved. if ((rt.ref.target && rt.ref.target->astKind == ASTKind::PACKAGE_DECL) || !Ty::IsInitialTy(rt.ty)) { return rt.ty; } // Get scope target. auto targets = LookupTopLevel(ctx, rt.ref.identifier, rt.scopeName, rt); // A copy of scopeTargets for better error messages. // If non types are defined, error message will be 'xxx' is not a type // If cannot find any declarations, error message will be undeclared type name 'xxx' auto allTargets = std::vector>(targets); // Remove all non-type decls. Utils::EraseIf(targets, [](Ptr d) { return !d || !d->IsTypeDecl(); }); std::sort(targets.begin(), targets.end(), [](Ptr d1, Ptr d2) { if (d1->scopeLevel > d2->scopeLevel) { return true; } else if (d1->scopeLevel == d2->scopeLevel) { // Ranking overloads also by platform > common if (d1->TestAttr(Attribute::PLATFORM)) { return true; } } return false; }); Ptr target{nullptr}; if (targets.empty()) { if (!ctx.packageDecls.empty()) { // a.b.c, ref a not found, but packagedecl of [a a.b] may exist. return TypeManager::GetInvalidTy(); } if (allTargets.empty()) { diag.Diagnose(rt, DiagKind::sema_undeclared_type_name, rt.ref.identifier.Val()); } else { diag.Diagnose(rt, DiagKind::sema_not_a_type, rt.ref.identifier.Val()); } return TypeManager::GetInvalidTy(); } if (targets.size() == 1 || // The other case must be scopeTargets.size() > 1, so no need to check // this condition. scopeTargets is sorted, so only need to check whether // scope levels are equal or not. If they are equal then rt is ambiguous. (targets[0]->scopeLevel > targets[1]->scopeLevel) || // With equal scope levels platform declaration are prefered over the common ones (targets[0]->TestAttr(Attribute::PLATFORM))) { target = targets[0]; if (auto builtin = DynamicCast(target); builtin && builtin->type == BuiltInType::CFUNC) { auto ty = GetTyFromASTCFuncType(ctx, rt); rt.ref.target = target; return ty; } ReplaceTarget(&rt, target); // Get semaTy only by name, no need to check inside. auto typeArgs = GetTyFromASTType(ctx, rt.typeArguments); rt.ty = GetTyFromASTType(*target, typeArgs); return rt.ty; } // If there are more than one targets and the scope are the same then report ambiguous error. DiagAmbiguousUse(diag, rt, rt.ref.identifier, targets, importManager); return TypeManager::GetInvalidTy(); } Ptr TypeChecker::TypeCheckerImpl::GetTyFromASTCFuncType(ASTContext& ctx, RefType& rt) { if (rt.typeArguments.size() != 1) { diag.DiagnoseRefactor( DiagKindRefactor::sema_generic_argument_no_match, rt, MakeRange(rt.GetBegin(), rt.GetEnd())); return TypeManager::GetInvalidTy(); } auto funcType = DynamicCast(&*rt.typeArguments[0]); if (!funcType) { diag.Diagnose(rt.typeArguments[0]->GetBegin(), rt.typeArguments[0]->GetEnd(), DiagKind::sema_cfunc_type); return TypeManager::GetInvalidTy(); } std::vector> paramTys; for (size_t i{0}; i < funcType->paramTypes.size(); ++i) { auto& param = funcType->paramTypes[i]; param->ty = GetTyFromASTType(ctx, &*param); paramTys.push_back(param->ty); } Ptr retTy = funcType->retType->ty = GetTyFromASTType(ctx, funcType->retType.get()); funcType->ty = GetTyFromASTType(ctx, funcType); return typeManager.GetFunctionTy(std::move(paramTys), retTy, {.isC = true}); } Ptr TypeChecker::TypeCheckerImpl::GetTyFromASTType(ASTContext& ctx, QualifiedType& qt) { // If qt.ty is nullptr but qt.target is packageDecl, qt is the base of another qualified type and is already // resolved. bool earlyQuit = (qt.target && qt.target->astKind == ASTKind::PACKAGE_DECL) || !Ty::IsInitialTy(qt.ty); if (earlyQuit) { return qt.ty; } Ptr baseType = qt.baseType.get(); CJC_ASSERT(baseType != nullptr); if (baseType->astKind == ASTKind::INVALID_TYPE) { return TypeManager::GetInvalidTy(); } std::string packageName = ASTContext::GetPackageName(baseType); auto [packageDecl, isConflicted] = importManager.GetImportedPackageDecl(&qt, packageName); if (isConflicted) { diag.Diagnose(qt, DiagKind::sema_package_name_conflict, packageName); return TypeManager::GetInvalidTy(); } else if (packageDecl) { ReplaceTarget(baseType, packageDecl); // Base node of current package qualifier node is useless. // So set all parent nodes as type of invalid to avoid checking their types. auto curType = &qt; do { CJC_NULLPTR_CHECK(curType->baseType); curType->baseType->ty = TypeManager::GetInvalidTy(); curType = DynamicCast(curType->baseType.get()); } while (curType != nullptr); } else { baseType->ty = GetTyFromASTType(ctx, baseType); } Ptr targetOfBaseType = baseType->GetTarget(); // Check targetOfBaseType. std::vector> targetsOfField; if (targetOfBaseType != nullptr) { targetsOfField = FieldLookup(ctx, targetOfBaseType, qt.field, {.lookupInherit = false, .lookupExtend = false}); } if (targetsOfField.empty()) { diag.Diagnose(qt, qt.field.Begin(), DiagKind::sema_undeclared_type_name, qt.field.Val()); return TypeManager::GetInvalidTy(); } auto target = targetsOfField.front(); CJC_NULLPTR_CHECK(target); if (!target->IsTypeDecl()) { diag.Diagnose(qt, qt.field.Begin(), DiagKind::sema_not_a_type, qt.field.Val()); return TypeManager::GetInvalidTy(); } // Bind target-user relationship. ReplaceTarget(&qt, target); // Get semaTy only by name, no need to check inside. auto typeArgs = GetTyFromASTType(ctx, qt.typeArguments); qt.ty = GetTyFromASTType(*target, typeArgs); return qt.ty; } Ptr TypeChecker::TypeCheckerImpl::GetTyFromASTType(ASTContext& ctx, VArrayType& varrayType) { varrayType.typeArgument->ty = GetTyFromASTType(ctx, varrayType.typeArgument.get()); auto expr = StaticAs(varrayType.constantType.get())->constantExpr.get(); CJC_ASSERT(expr && expr->astKind == ASTKind::LIT_CONST_EXPR); auto le = StaticAs(expr); #if CANGJIE_CODEGEN_CJNATIVE_BACKEND auto lengthLimitKind = TypeKind::TYPE_INT64; #endif le->constNumValue.asInt.InitIntLiteral(le->stringValue, lengthLimitKind); le->constNumValue.asInt.SetOutOfRange(Cangjie::TypeManager::GetPrimitiveTy(lengthLimitKind)); if (le->constNumValue.asInt.IsOutOfRange()) { (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_exceed_num_value_range, *le, le->stringValue, #if CANGJIE_CODEGEN_CJNATIVE_BACKEND "Int64"); #endif return TypeManager::GetInvalidTy(); } varrayType.ty = typeManager.GetVArrayTy(*varrayType.typeArgument->ty, le->constNumValue.asInt.Int64()); return varrayType.ty; } Ptr TypeChecker::TypeCheckerImpl::GetTyFromASTType(ASTContext& ctx, TupleType& tupleType) { if (!Ty::IsInitialTy(tupleType.ty)) { return tupleType.ty; } std::vector> elemTy; for (auto& fieldType : tupleType.fieldTypes) { if (!fieldType) { return TypeManager::GetInvalidTy(); } fieldType->ty = GetTyFromASTType(ctx, fieldType.get()); if (Ty::IsInitialTy(fieldType->ty)) { return TypeManager::GetInvalidTy(); } elemTy.push_back(fieldType->ty); } tupleType.ty = typeManager.GetTupleTy(elemTy); return tupleType.ty; } Ptr TypeChecker::TypeCheckerImpl::GetTyFromASTType(ASTContext& ctx, FuncType& funcType) { if (!Ty::IsInitialTy(funcType.ty)) { return funcType.ty; } std::vector> paramTys; for (auto& paramType : funcType.paramTypes) { if (!paramType) { return TypeManager::GetInvalidTy(); } paramType->ty = GetTyFromASTType(ctx, paramType.get()); if (Ty::IsInitialTy(paramType->ty)) { return TypeManager::GetInvalidTy(); } paramTys.push_back(paramType->ty); } if (!funcType.retType) { return TypeManager::GetInvalidTy(); } funcType.retType->ty = GetTyFromASTType(ctx, funcType.retType.get()); if (!funcType.retType->ty) { return TypeManager::GetInvalidTy(); } funcType.ty = typeManager.GetFunctionTy(paramTys, funcType.retType->ty, {funcType.isC}); return funcType.ty; } Ptr TypeChecker::TypeCheckerImpl::GetTyFromASTType(ASTContext& ctx, OptionType& optionType) { if (optionType.componentType) { optionType.componentType->ty = GetTyFromASTType(ctx, optionType.componentType.get()); } if (optionType.desugarType) { optionType.ty = GetTyFromASTType(ctx, *optionType.desugarType); } else { optionType.ty = TypeManager::GetInvalidTy(); } return optionType.ty; } Ptr TypeChecker::TypeCheckerImpl::GetTyFromASTType(Decl& decl, const std::vector>& typeArgs) { switch (decl.astKind) { case ASTKind::CLASS_DECL: { auto cd = StaticAs(&decl); return typeManager.GetClassTy(*cd, typeArgs); } case ASTKind::INTERFACE_DECL: { auto id = StaticAs(&decl); return typeManager.GetInterfaceTy(*id, typeArgs); } case ASTKind::STRUCT_DECL: { auto sd = StaticAs(&decl); return typeManager.GetStructTy(*sd, typeArgs); } case ASTKind::ENUM_DECL: { auto ed = StaticAs(&decl); return typeManager.GetEnumTy(*ed, typeArgs); } case ASTKind::TYPE_ALIAS_DECL: { auto tad = StaticAs(&decl); return typeManager.GetTypeAliasTy(*tad, typeArgs); } case ASTKind::GENERIC_PARAM_DECL: { auto gpd = StaticAs(&decl); return typeManager.GetGenericsTy(*gpd); } case ASTKind::BUILTIN_DECL: { auto bid = StaticAs(&decl); return GetTyFromBuiltinDecl(*bid, typeArgs); } default: return decl.ty; } } Ptr TypeChecker::TypeCheckerImpl::GetTyFromBuiltinDecl(const BuiltInDecl& bid, const std::vector>& typeArgs) { switch (bid.type) { case BuiltInType::ARRAY: { return GetBuiltInArrayType(typeArgs); } case BuiltInType::POINTER: { return GetBuiltInPointerType(typeArgs); } case BuiltInType::CSTRING: { return TypeManager::GetCStringTy(); } case BuiltInType::VARRAY: { return GetBuiltInVArrayType(typeArgs); } case BuiltInType::CFUNC: { return GetBuiltinCFuncType(typeArgs); } default: return TypeManager::GetInvalidTy(); } } Ptr TypeChecker::TypeCheckerImpl::GetBuiltInPointerType(const std::vector>& typeArgs) { auto elemTy = typeArgs.empty() ? TypeManager::GetInvalidTy() : typeArgs[0]; return typeManager.GetPointerTy(elemTy); } Ptr TypeChecker::TypeCheckerImpl::GetBuiltInArrayType(const std::vector>& typeArgs) { if (typeArgs.size() > 1) { return TypeManager::GetInvalidTy(); } auto elemTy = typeArgs.empty() ? TypeManager::GetInvalidTy() : typeArgs[0]; return typeManager.GetArrayTy(elemTy, 1); } Ptr TypeChecker::TypeCheckerImpl::GetBuiltInVArrayType(const std::vector>& typeArgs) { CJC_ASSERT(typeArgs.size() == 1); auto elemTy = typeArgs.empty() ? TypeManager::GetInvalidTy() : typeArgs[0]; return typeManager.GetVArrayTy(*elemTy, 0); } Ptr TypeChecker::TypeCheckerImpl::GetBuiltinCFuncType(const std::vector>& typeArgs) { // the return type is CFunc CJC_ASSERT(typeArgs.size() == 1 && Ty::IsTyCorrect(typeArgs[0])); return typeManager.GetFunctionTy( typeArgs, typeArgs[0], {.isC = true, .isClosureTy = false, .hasVariableLenArg = false, .noCast = false}); } std::vector> TypeChecker::TypeCheckerImpl::GetTyFromASTType( const std::vector>& typeParameters) { std::vector> typeArgs; for (auto& gpd : typeParameters) { if (Ty::IsInitialTy(gpd->ty)) { gpd->ty = GetTyFromASTType(*gpd, {}); } typeArgs.push_back(gpd->ty); } return typeArgs; } std::vector> TypeChecker::TypeCheckerImpl::GetTyFromASTType( ASTContext& ctx, std::vector>& typeArguments) { std::vector> typeArgs; for (auto& arg : typeArguments) { if (Ty::IsInitialTy(arg->ty)) { arg->ty = GetTyFromASTType(ctx, arg.get()); } typeArgs.push_back(arg->ty); } return typeArgs; } void TypeChecker::TypeCheckerImpl::SetDeclTy(Decl& decl) { std::vector> typeArgs; auto generic = decl.GetGeneric(); if (generic) { typeArgs = GetTyFromASTType(generic->typeParameters); } decl.ty = GetTyFromASTType(decl, typeArgs); } void TypeChecker::TypeCheckerImpl::SetTypeAliasDeclTy(ASTContext& ctx, TypeAliasDecl& tad) { SetDeclTy(tad); if (tad.type == nullptr) { return; } tad.type->ty = GetTyFromASTType(ctx, tad.type.get()); if (!Ty::IsTyCorrect(tad.type->ty)) { std::string name; if (auto rt = DynamicCast(tad.type.get()); rt) { name = rt->ref.identifier; } else if (auto qt = DynamicCast(tad.type.get()); qt) { name = qt->field; } else { name = "Unknown type"; } diag.Diagnose(*tad.type, DiagKind::sema_not_a_type, name); } } void TypeChecker::TypeCheckerImpl::ResolveOneDecl(ASTContext& ctx, Decl& decl) { switch (decl.astKind) { case ASTKind::CLASS_DECL: if (decl.HasAnno(AnnotationKind::ANNOTATION)) { decl.EnableAttr(Attribute::IS_ANNOTATION); } [[fallthrough]]; case ASTKind::STRUCT_DECL: case ASTKind::INTERFACE_DECL: case ASTKind::ENUM_DECL: { auto id = RawStaticCast(&decl); SetDeclTy(*id); if (decl.TestAttr(Attribute::C) && !id->inheritedTypes.empty()) { diag.Diagnose(decl, DiagKind::sema_c_type_cannot_implement_interface, decl.identifier.Val()); } for (auto& superType : id->inheritedTypes) { superType->ty = GetTyFromASTType(ctx, superType.get()); if (auto clt = DynamicCast(superType->ty); clt && Ty::IsTyCorrect(id->ty)) { (void)clt->directSubtypes.emplace(id->ty); } } break; } case ASTKind::FUNC_DECL: case ASTKind::GENERIC_PARAM_DECL: case ASTKind::BUILTIN_DECL: case ASTKind::EXTEND_DECL: SetDeclTy(decl); break; case ASTKind::VAR_WITH_PATTERN_DECL: { auto& vpd = StaticCast(decl); Walker(vpd.irrefutablePattern.get(), [&ctx, &vpd](auto node) { if (auto vd = DynamicCast(node)) { // Collect mapping from VarDecl to the outer VarWithPatternDecl. ctx.StoreOuterVarWithPatternDecl(*vd, vpd); } return VisitAction::WALK_CHILDREN; }).Walk(); break; } default: break; } } void TypeChecker::TypeCheckerImpl::ResolveDecls(ASTContext& ctx) { std::vector syms = GetAllDecls(ctx); for (auto& sym : syms) { if (sym->node->astKind == ASTKind::VAR_DECL || sym->node->astKind == ASTKind::FUNC_DECL) { SetOuterFunctionDecl(*StaticCast(sym->node)); } } for (auto& sym : syms) { if (auto decl = AST::As(sym->node); decl) { ResolveOneDecl(ctx, *decl); } } } void TypeChecker::TypeCheckerImpl::ResolveTypeAlias(const std::vector>& contexts) { auto setTypAliasFunc = [this](ASTContext& ctx, TypeAliasDecl& tad) { return SetTypeAliasDeclTy(ctx, tad); }; auto substituteFunc = [this](ASTContext&, TypeAliasDecl& tad) { return SubstituteTypeAliasForAlias(tad); }; // Resolve all targets for typeAlias decls. for (auto ctx : contexts) { IterateToplevelDecls(*ctx->curPackage, [&ctx, &setTypAliasFunc](const OwnedPtr& decl) { if (decl->astKind == ASTKind::TYPE_ALIAS_DECL) { setTypAliasFunc(*ctx, *StaticAs(decl.get())); } }); } for (auto ctx : contexts) { // TypeAlias circle check before substitution. TypeAliasCircleCheck(*ctx); // Substitute typealiased decl types. IterateToplevelDecls(*ctx->curPackage, [&ctx, &substituteFunc](const OwnedPtr& decl) { if (decl->astKind == ASTKind::TYPE_ALIAS_DECL) { substituteFunc(*ctx, *StaticAs(decl.get())); } }); } } void TypeChecker::TypeCheckerImpl::SetTypeTy(ASTContext& ctx, Type& type) { type.ty = GetTyFromASTType(ctx, &type); type.ty = SubstituteTypeAliasInTy(*type.ty); } void TypeChecker::TypeCheckerImpl::SubstituteTypeAliasForAlias(TypeAliasDecl& tad) { auto resolveTypes = [this](Ptr node) -> VisitAction { switch (node->astKind) { case ASTKind::PRIMARY_CTOR_DECL: return VisitAction::SKIP_CHILDREN; case ASTKind::PRIMITIVE_TYPE: case ASTKind::REF_TYPE: case ASTKind::QUALIFIED_TYPE: case ASTKind::TUPLE_TYPE: case ASTKind::PAREN_TYPE: case ASTKind::FUNC_TYPE: case ASTKind::OPTION_TYPE: { auto type = StaticAs(node); CJC_ASSERT(type->ty != nullptr); type->ty = SubstituteTypeAliasInTy(*type->ty); return VisitAction::WALK_CHILDREN; } default: break; } return VisitAction::WALK_CHILDREN; }; Walker(&tad, resolveTypes).Walk(); } void TypeChecker::TypeCheckerImpl::ResolveNames(ASTContext& ctx) { auto id = Walker::GetNextWalkerID(); // NOTE: ThisType should be resolved with function and it's parent. auto resolveSingleType = [&ctx, id, this](Ptr node) -> VisitAction { switch (node->astKind) { case ASTKind::REF_TYPE: case ASTKind::PRIMITIVE_TYPE: case ASTKind::QUALIFIED_TYPE: case ASTKind::VARRAY_TYPE: case ASTKind::TUPLE_TYPE: case ASTKind::PAREN_TYPE: case ASTKind::FUNC_TYPE: case ASTKind::OPTION_TYPE: { auto type = StaticAs(node); SetTypeTy(ctx, *type); if (auto qt = DynamicCast(node); qt && qt->TestAttr(Attribute::IS_CHECK_VISITED)) { // Update id to ignore baseType for unchanged type in incremental compilation. CJC_NULLPTR_CHECK(qt->baseType); qt->baseType->visitedByWalkerID = id; } return VisitAction::WALK_CHILDREN; } default: break; } return VisitAction::WALK_CHILDREN; }; std::vector syms = GetToplevelDecls(ctx); for (auto& sym : syms) { CJC_NULLPTR_CHECK(sym); Walker(sym->node, id, resolveSingleType).Walk(); } if (ci->invocation.globalOptions.enableMacroInLSP) { for (auto& file : ctx.curPackage->files) { for (auto& it : file->originalMacroCallNodes) { Walker(it.get(), id, resolveSingleType).Walk(); } } } } bool TypeChecker::TypeCheckerImpl::CheckAndReduceUpperBounds( GenericsTy& genericTy, const std::set>& upperBounds) { if (!Ty::AreTysCorrect(upperBounds)) { return false; } if (upperBounds.size() <= 1) { return true; } auto joinAndMeet = JoinAndMeet(typeManager, upperBounds); auto meetRes = joinAndMeet.MeetAsVisibleTy(); if (std::get_if>(&meetRes)) { auto maxCommonChildTy = std::get>(meetRes); for (auto& it : upperBounds) { if (it != maxCommonChildTy) { genericTy.upperBounds.erase(it); } } return true; } else { genericTy.isUpperBoundLegal = false; genericTy.upperBounds.clear(); return false; } } bool TypeChecker::TypeCheckerImpl::ValidRecursiveConstraintCheck(const Generic& generic) { for (auto& it : generic.typeParameters) { CJC_NULLPTR_CHECK(it); auto genericTy = DynamicCast(it->ty); if (!genericTy) { continue; } for (auto& upper : genericTy->upperBounds) { CJC_NULLPTR_CHECK(upper); if (upper->IsGeneric() && AreUpperBoundsDirectlyRecursive(std::set>(), *upper)) { auto upperGenericTy = DynamicCast(upper); CJC_NULLPTR_CHECK(upperGenericTy); upperGenericTy->isUpperBoundLegal = false; CJC_NULLPTR_CHECK(genericTy->decl); diag.DiagnoseRefactor(DiagKindRefactor::sema_generic_param_directly_recursive, *genericTy->decl, genericTy->decl->identifier, upperGenericTy->String()); return false; } if (!upper->IsClassLike() && IsGenericParamExistInUpperBounds(*genericTy, *upper)) { genericTy->isUpperBoundLegal = false; CJC_NULLPTR_CHECK(genericTy->decl); diag.DiagnoseRefactor( DiagKindRefactor::sema_generic_param_exist_in_class_irrelevant_upperbound_recursively, *genericTy->decl, genericTy->decl->identifier, upper->String()); return false; } } } return true; } bool TypeChecker::TypeCheckerImpl::CheckUpperBoundsLegality(const Generic& generic) { for (auto& gp : generic.typeParameters) { CJC_NULLPTR_CHECK(gp); auto genericsTy = DynamicCast(gp->ty); if (!genericsTy) { return false; } for (auto& upper : genericsTy->upperBounds) { CJC_NULLPTR_CHECK(upper); if (!CheckUpperBoundsLegalityRecursively(*upper)) { return false; } } } return true; } bool TypeChecker::TypeCheckerImpl::CheckUpperBoundsLegalityRecursively(const Ty& upper) { auto decl = Ty::GetDeclPtrOfTy(&upper); if (!decl) { return true; // If upper is not user defined type decl, just treat current checking as passed. } auto typeArgs = upper.typeArgs; if (!typeManager.CheckGenericDeclInstantiation(decl, typeArgs)) { return false; } for (auto& it : typeArgs) { if (!CheckUpperBoundsLegalityRecursively(*it)) { return false; } } return true; } void TypeChecker::TypeCheckerImpl::SanityCheckForClassUpperBounds( GenericsTy& genericTy, const std::set>& classUpperBounds) { if (!CheckAndReduceUpperBounds(genericTy, classUpperBounds)) { auto diagInfo = Ty::GetTypesToStableStr(classUpperBounds, ", "); CJC_NULLPTR_CHECK(genericTy.decl); diag.DiagnoseRefactor( DiagKindRefactor::sema_multiple_class_upperbounds, *genericTy.decl, genericTy.decl->identifier, diagInfo); // NOTE: Use 'IN_REFERENCE_CYCLE' attribute to mark invalid upperbounds for genericParamDecl. genericTy.decl->EnableAttr(Attribute::IN_REFERENCE_CYCLE); } } void TypeChecker::TypeCheckerImpl::SanityCheckForOneGenericTy(GenericsTy& genericTy) { std::set> classUpperBounds; bool invalidUpperbound = false; std::set, CmpTyByName> sortedUpperTys; sortedUpperTys.insert(genericTy.upperBounds.begin(), genericTy.upperBounds.end()); for (auto& upper : sortedUpperTys) { CJC_NULLPTR_CHECK(upper); if (upper->IsAny() || upper->IsCType()) { continue; // Ignore for top type and ctype constraint. } if (upper->kind == TypeKind::TYPE_CLASS) { classUpperBounds.emplace(upper); } else if (upper->kind != TypeKind::TYPE_INTERFACE) { // Rule 1: There can only be interface or class bounds. diag.DiagnoseRefactor(DiagKindRefactor::sema_upper_bound_must_be_class_or_interface, *genericTy.decl, upper->String(), genericTy.String()); invalidUpperbound = true; continue; } } if (invalidUpperbound) { return; } // Rule 2: If there are multiple classes, they must be in one inheritance chain. SanityCheckForClassUpperBounds(genericTy, classUpperBounds); } void TypeChecker::TypeCheckerImpl::AssumptionSanityCheck(const Generic& generic) { // Rule 1: There can be no recursive constraint if the upper bound of a type argument is a class irrelevant type. if (!ValidRecursiveConstraintCheck(generic)) { return; } // Do sanity check recursively according to rule 2-5. for (auto& it : generic.typeParameters) { auto genericTy = DynamicCast(it->ty); if (!genericTy) { continue; } SanityCheckForOneGenericTy(*genericTy); } } void TypeChecker::TypeCheckerImpl::CollectAssumption(ASTContext& ctx, const Decl& decl) { switch (decl.astKind) { case ASTKind::FUNC_DECL: case ASTKind::BUILTIN_DECL: case ASTKind::CLASS_DECL: case ASTKind::INTERFACE_DECL: case ASTKind::STRUCT_DECL: case ASTKind::ENUM_DECL: case ASTKind::EXTEND_DECL: { auto generic = decl.GetGeneric(); if (generic != nullptr) { Assumption(generic->assumptionCollection, ctx.gcBlames, decl); } break; } default: break; } } void TypeChecker::TypeCheckerImpl::ExposeGenericUpperBounds(ASTContext& ctx, const Generic& generic) const { for (auto& it : generic.typeParameters) { CJC_NULLPTR_CHECK(it); auto genericTy = DynamicCast(it->ty); if (!genericTy) { continue; } std::set> exposedUpperBounds(genericTy->upperBounds.begin(), genericTy->upperBounds.end()); std::queue> q; q.push(genericTy); std::unordered_set> visited = {}; while (!q.empty()) { auto gTy = q.front(); q.pop(); if (auto [_, success] = visited.emplace(gTy); !success) { continue; } for (auto upper : gTy->upperBounds) { if (upper->IsGeneric()) { q.push(RawStaticCast(upper)); } else { (void)exposedUpperBounds.emplace(upper); auto& srcBlames = ctx.gcBlames[gTy][upper]; ctx.gcBlames[genericTy][upper].insert(srcBlames.begin(), srcBlames.end()); } } } (void)exposedUpperBounds.erase(genericTy); // Erase itself to avoid invalid looping. genericTy->upperBounds = std::move(exposedUpperBounds); } } void TypeChecker::TypeCheckerImpl::CheckAssumption(ASTContext& ctx, const Decl& decl) { auto generic = decl.GetGeneric(); if (generic == nullptr) { return; } if (decl.TestAttr(Attribute::TOOL_ADD)) { ExposeGenericUpperBounds(ctx, *generic); return; } CheckGenericConstraints(ctx, *generic); if (!CheckUpperBoundsLegality(*generic)) { diag.Diagnose(decl, DiagKind::sema_generic_type_argument_not_match_constraint, decl.ty->String()); } ExposeGenericUpperBounds(ctx, *generic); AssumptionSanityCheck(*generic); } void TypeChecker::TypeCheckerImpl::AddSuperClassObjectForClassDecl(ASTContext& ctx) { std::vector syms = GetSymsByASTKind(ctx, ASTKind::CLASS_DECL, Sort::posAsc); for (auto& sym : syms) { CJC_ASSERT(sym && sym->node && sym->node->astKind == ASTKind::CLASS_DECL); auto cd = StaticAs(sym->node); if (HasSuperClass(*cd)) { continue; } if (cd->TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE)) { if (!AddJObjectSuperClassJavaInterop(ctx, *cd)) { AddObjectSuperClass(ctx, *cd); } } else { AddObjectSuperClass(ctx, *cd); } } } void TypeChecker::TypeCheckerImpl::CollectAndCheckAssumption(ASTContext& ctx) { std::vector syms = GetGenericCandidates(ctx); for (auto& sym : syms) { if (auto decl = AST::As(sym->node); decl) { CollectAssumption(ctx, *decl); } } IgnoreAssumptionForTypeAliasDecls(ctx); AddAssumptionForExtendDecls(ctx); for (auto& sym : syms) { if (auto decl = AST::As(sym->node); decl) { CheckAssumption(ctx, *decl); } } } void TypeChecker::TypeCheckerImpl::TypeAliasCircleCheck(const ASTContext& ctx) { if (ctx.curPackage->TestAttr(Attribute::IMPORTED)) { return; // Alias dependency check can be ignored for imported package. } std::vector syms = GetSymsByASTKind(ctx, ASTKind::TYPE_ALIAS_DECL, Sort::posAsc); for (auto& sym : syms) { if (auto tad = AST::As(sym->node); tad) { std::deque> path; CheckTypeAliasCycleForOneDecl(*tad, path); } } } void TypeChecker::TypeCheckerImpl::CheckTypeAliasCycleForOneType(Type& type, std::deque>& path) { switch (type.astKind) { case ASTKind::REF_TYPE: { auto rt = StaticAs(&type); if (rt->ref.target && rt->ref.target->astKind == ASTKind::TYPE_ALIAS_DECL) { CheckTypeAliasCycleForOneDecl(*StaticAs(rt->ref.target), path); } break; } case ASTKind::QUALIFIED_TYPE: { auto qt = StaticAs(&type); if (qt->target && qt->target->astKind == ASTKind::TYPE_ALIAS_DECL) { CheckTypeAliasCycleForOneDecl(*StaticAs(qt->target), path); } break; } default: break; } } void TypeChecker::TypeCheckerImpl::CheckTypeAliasCycleForTypeArgsRecursively( std::vector>& typeArgs, std::deque>& path) { for (auto type : typeArgs) { if (type == nullptr) { continue; } CheckTypeAliasCycleForOneType(*type, path); } } void TypeChecker::TypeCheckerImpl::CheckTypeAliasCycleForOneDecl(TypeAliasDecl& tad, std::deque>& path) { if (tad.checkFlag == InheritanceVisitStatus::VISITING) { // Found a cycle; mark every decl in this cycle. auto diagInfo = ConstructDiagInfoForCycle(tad, path); diag.Diagnose(tad, DiagKind::sema_typealias_cycle, diagInfo.c_str()); return; } tad.checkFlag = InheritanceVisitStatus::VISITING; path.push_back(&tad); std::vector> typeArgs; GetTypeArgsOfType(tad.type.get(), typeArgs); CheckTypeAliasCycleForTypeArgsRecursively(typeArgs, path); path.pop_back(); tad.checkFlag = InheritanceVisitStatus::VISITED; } void TypeChecker::TypeCheckerImpl::StructDeclCircleOrDupCheckForOneSymbol(ASTContext& ctx, const Symbol& sym) { CJC_NULLPTR_CHECK(sym.node); switch (sym.node->astKind) { case ASTKind::CLASS_DECL: { auto cd = StaticAs(sym.node); // Do duplicate interfaces implementation check. CheckDupInterfaceInStructDecl(*cd); // Check if there is inheritance cycle. std::deque> path{}; CheckInheritanceCycleDFS(ctx, *cd, path); break; } case ASTKind::INTERFACE_DECL: { auto id = StaticAs(sym.node); // Do duplicate interfaces implementation check. CheckDupInterfaceInStructDecl(*id); // Check if there is inheritance cycle. std::deque> path{}; CheckInheritanceCycleDFS(ctx, *id, path); break; } case ASTKind::STRUCT_DECL: case ASTKind::ENUM_DECL: { auto d = RawStaticCast(sym.node); CheckDupInterfaceInStructDecl(*d); MarkInvalidInheritanceForNonClassLike(*d); break; } default: break; } } void TypeChecker::TypeCheckerImpl::StructDeclCircleOrDupCheck(ASTContext& ctx) { std::vector syms = GetAllStructDecls(ctx); for (auto& sym : syms) { StructDeclCircleOrDupCheckForOneSymbol(ctx, *sym); } } void TypeChecker::TypeCheckerImpl::CheckDupInterfaceInStructDecl(InheritableDecl& decl) { if (!Ty::IsTyCorrect(decl.ty)) { return; } // Do not check for decl's related extend decls in precheck step. CheckInstDupSuperInterfaces(decl, decl, {}, false); } template void TypeChecker::TypeCheckerImpl::CheckInheritanceCycleHelper( ASTContext& ctx, const Decl& decl, const Type& te, std::deque>& path, Ptr extendDecl) { if (te.astKind == ASTKind::REF_TYPE || te.astKind == ASTKind::QUALIFIED_TYPE) { auto target = te.GetTarget(); // If type's target is typeAliasDecl, recursively finding the real used type. if (auto tad = DynamicCast(target); tad && tad->type) { CheckInheritanceCycleHelper(ctx, decl, *tad->type, path, extendDecl); } else if (target && target->checkFlag != InheritanceVisitStatus::VISITED) { CheckInheritanceCycleDFS(ctx, *target, path, extendDecl); } } else { diag.Diagnose(decl, DiagKind::sema_inheritance_non_ref_type, decl.identifier.Val()); } } void TypeChecker::TypeCheckerImpl::CheckInheritanceCycleWithExtend( ASTContext& ctx, const InheritableDecl& decl, std::deque>& path) { std::set> extends = typeManager.GetDeclExtends(decl); for (auto& extend : extends) { for (auto& it : extend->inheritedTypes) { it->ty = GetTyFromASTType(ctx, it.get()); auto target = Ty::GetDeclPtrOfTy(it->ty); if (auto id = DynamicCast(target); id && id->checkFlag != InheritanceVisitStatus::VISITED) { CheckInheritanceCycleDFS(ctx, *id, path, extend); } else { CheckInheritanceCycleHelper>(ctx, decl, *it, path, extend); } } } } void TypeChecker::TypeCheckerImpl::CheckInheritanceCycleDFS( ASTContext& ctx, Decl& decl, std::deque>& path, Ptr extendDecl) { if (decl.checkFlag == InheritanceVisitStatus::VISITING) { CheckInheritanceCycleDFSHandleVisiting(decl, path, extendDecl); return; } decl.checkFlag = InheritanceVisitStatus::VISITING; path.push_back(&decl); if (auto cld = DynamicCast(&decl); cld) { for (auto& it : cld->inheritedTypes) { auto target = Ty::GetDeclPtrOfTy(it->ty); if (target && target->IsNominalDecl() && target->checkFlag != InheritanceVisitStatus::VISITED) { CheckInheritanceCycleDFS(ctx, *target, path, extendDecl); } else { CheckInheritanceCycleHelper>(ctx, decl, *it, path, extendDecl); } } CheckInheritanceCycleWithExtend(ctx, *cld, path); } path.pop_back(); decl.checkFlag = InheritanceVisitStatus::VISITED; } void TypeChecker::TypeCheckerImpl::CheckInheritanceCycleDFSHandleVisiting( const Decl& decl, const std::deque>& path, Ptr extendDecl) { // Found a cycle; mark every decl in this cycle. auto str = ConstructDiagInfoForCycle(decl, path); diag.Diagnose((extendDecl == nullptr) ? decl : *extendDecl, DiagKind::sema_inheritance_cycle, str); } void TypeChecker::TypeCheckerImpl::IgnoreAssumptionForTypeAliasDecls(const ASTContext& ctx) const { std::vector syms = GetSymsByASTKind(ctx, ASTKind::TYPE_ALIAS_DECL, Sort::posAsc); for (auto& sym : syms) { if (auto tad = AST::As(sym->node); tad && tad->type && tad->generic) { for (auto& tp : tad->generic->typeParameters) { if (auto genTy = DynamicCast(tp->ty)) { genTy->isAliasParam = true; } } } } } void TypeChecker::TypeCheckerImpl::AddAssumptionForExtendDecls(ASTContext& ctx) { std::vector syms = GetSymsByASTKind(ctx, ASTKind::EXTEND_DECL, Sort::posAsc); for (auto& sym : syms) { if (auto ed = AST::As(sym->node); ed) { if (ed->extendedType && ed->generic) { AddAssumptionForType(ctx, *ed->extendedType, *ed->generic); } } } } void TypeChecker::TypeCheckerImpl::AddUpperBoundOnTypeParameters( ASTContext& ctx, const Generic& generic, const Decl& typeTarget, MultiTypeSubst& revTypeMapping, TyVarUB& allAssumptionMap, const TypeSubst& typeArgAppliedMap) { for (auto& typeParameter : generic.typeParameters) { auto genericTy = DynamicCast(typeParameter->ty); if (genericTy == nullptr) { continue; } auto aliasedTypeGeneric = typeTarget.GetGeneric(); if (aliasedTypeGeneric == nullptr) { continue; } auto assumedUpperBounds = aliasedTypeGeneric->assumptionCollection; GetAllAssumptions(assumedUpperBounds, allAssumptionMap); auto constraintTys = revTypeMapping[StaticCast(typeParameter->ty)]; UpperBounds constraints; for (auto it : constraintTys) { auto uppers = allAssumptionMap[StaticCast(it)]; constraints.merge(uppers); } std::set> upperBoundsForParam; std::for_each(constraints.begin(), constraints.end(), [this, &ctx, &constraintTys, &genericTy, &typeArgAppliedMap, &upperBoundsForParam](auto ty) { Ptr instTy = typeManager.GetInstantiatedTy(ty, typeArgAppliedMap); if (Ty::IsTyCorrect(instTy)) { upperBoundsForParam.insert(instTy); for (auto it : constraintTys) { auto srcBlames = ctx.gcBlames[it][ty]; ctx.gcBlames[genericTy][instTy].merge(srcBlames); } } }); genericTy->upperBounds.insert(upperBoundsForParam.begin(), upperBoundsForParam.end()); } } void TypeChecker::TypeCheckerImpl::AddAssumptionForType(ASTContext& ctx, const Type& type, Generic& generic) { if (type.astKind != ASTKind::REF_TYPE && type.astKind != ASTKind::QUALIFIED_TYPE) { return; } auto aliasedTypeTarget = type.GetTarget(); if (!aliasedTypeTarget || !type.ty || type.ty->typeArgs.empty()) { return; } MultiTypeSubst revTypeMapping; TyVarUB allAssumptionMap; GetRevTypeMapping(aliasedTypeTarget->ty->typeArgs, type.ty->typeArgs, revTypeMapping); auto typeArgAppliedMap = GetGenericTysToInstTysMapping(*aliasedTypeTarget->ty, *type.ty); AddUpperBoundOnTypeParameters( ctx, generic, *aliasedTypeTarget, revTypeMapping, allAssumptionMap, typeArgAppliedMap); CreateGenericConstraints(generic); } void TypeChecker::TypeCheckerImpl::PreCheckMacroRedefinition(const std::vector>& funcs) const { std::vector> macroVec; std::for_each(funcs.begin(), funcs.end(), [¯oVec](auto func) { if (func->TestAttr(Attribute::MACRO_FUNC) && !func->TestAttr(Attribute::COMPILER_ADD)) { macroVec.emplace_back(func); } }); if (macroVec.size() <= 1) { return; } bool invalid = std::any_of( macroVec.begin(), macroVec.end(), [](auto fd) { return !fd->funcBody || fd->funcBody->paramLists.empty(); }); if (invalid) { return; } size_t cnt0 = macroVec[0]->funcBody->paramLists.front().get()->params.size(); size_t cnt1 = macroVec[1]->funcBody->paramLists.front().get()->params.size(); // Allow one attribute macro and one non-attribute macro. // 2 means a threshold if ((macroVec.size() == 2 && cnt0 == cnt1) || macroVec.size() > 2) { for (auto md : macroVec) { (void)ci->diag.Diagnose(*md, DiagKind::sema_expand_macro_redefinition, md->identifier.Val().c_str()); } } } void TypeChecker::TypeCheckerImpl::PreCheckFuncRedefinitionForEnum(const std::vector>& funcs) { CJC_ASSERT(!funcs.empty()); // Caller guarantees. if (funcs[0]->outerDecl == nullptr || funcs[0]->outerDecl->astKind != ASTKind::ENUM_DECL) { return; } auto ed = StaticAs(funcs[0]->outerDecl); std::unordered_set paramNum; std::unordered_map> ctorNames; std::for_each(ed->constructors.begin(), ed->constructors.end(), [&ctorNames](auto& decl) { ctorNames.emplace(std::make_pair(decl->identifier, decl.get())); }); for (auto func : funcs) { if (!func->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { // Member functions in enum cannot have same name with enum constructor. auto iter = ctorNames.find(func->identifier); if (iter != ctorNames.cend()) { DiagRedefinitionWithFoundNode(diag, *func, *iter->second); } continue; } // Function redefinition check is moved to precheck when the ty of function is not set, because the // return type need to be inferred in type check stage. auto funcTy = DynamicCast(func->ty); auto paramTys = funcTy ? funcTy->paramTys : GetParamTys(*func); if (paramNum.find(paramTys.size()) != paramNum.end()) { diag.Diagnose(*func, DiagKind::sema_duplicated_item_in_enum, func->identifier.Val().c_str(), ed->identifier.Val().c_str()); } else { paramNum.insert(paramTys.size()); } } } void TypeChecker::TypeCheckerImpl::PreCheckFuncRedefinitionForFFI(const std::vector>& funcs) { std::vector> nativeDuplicates; std::copy_if(funcs.begin(), funcs.end(), std::back_inserter(nativeDuplicates), [](Ptr fd) { return fd->TestAnyAttr(Attribute::FOREIGN, Attribute::C); }); if (nativeDuplicates.size() <= 1) { return; } for (size_t i = 1; i < nativeDuplicates.size(); ++i) { auto fd = nativeDuplicates[i]; if (fd->ShouldDiagnose() && !fd->TestAttr(Attribute::CONSTRUCTOR)) { DiagRedefinitionWithFoundNode(diag, *fd, *nativeDuplicates[0]); } } } void TypeChecker::TypeCheckerImpl::PreCheckFuncStaticConflict(const std::vector>& funcs) { std::vector> staticFuncs; std::copy_if(funcs.begin(), funcs.end(), std::back_inserter(staticFuncs), [](Ptr fd) { return fd->TestAttr(Attribute::STATIC) && !fd->TestAttr(Attribute::CONSTRUCTOR); }); Ptr firstNonStatic = nullptr; for (const auto& func : funcs) { if (!func->TestAttr(Attribute::STATIC) && !func->TestAttr(Attribute::CONSTRUCTOR)) { firstNonStatic = func; break; } } if (!staticFuncs.empty() && firstNonStatic) { for (const auto& func : staticFuncs) { DiagStaticAndNonStaticOverload(diag, *func, *firstNonStatic); } } } bool TypeChecker::TypeCheckerImpl::PreCheckFuncRedefinitionWithSameSignature( std::vector> funcs, bool needReportErr) { bool ret = false; while (funcs.size() > 1) { auto it = funcs.begin(); auto it1 = std::partition(it, funcs.end(), [&it, this](Ptr fd) { // Function redefinition check is moved to PreCheck when the ty of function is not set, because the // return type need to be inferred in type check stage. auto funcTy1 = DynamicCast((*it)->ty); auto funcTy2 = DynamicCast(fd->ty); auto paramTys1 = funcTy1 ? funcTy1->paramTys : GetParamTys(**it); auto paramTys2 = funcTy2 ? funcTy2->paramTys : GetParamTys(*fd); // Get substituteMap S = [X11 -> X21, ..., X1n -> X2m]. from '*it' to 'fd' if exists. TypeSubst substituteMap = GetSubstituteMap(typeManager, **it, *fd); bool visibilityMatch = true; // Private functions in different files are not considered redefinitions. bool bothTopLevel = !fd->IsMemberDecl() && !(*it)->IsMemberDecl(); bool bothPrivate = fd->TestAttr(Attribute::PRIVATE) && (*it)->TestAttr(Attribute::PRIVATE); if (bothTopLevel && bothPrivate) { CJC_NULLPTR_CHECK(fd->curFile); CJC_NULLPTR_CHECK((*it)->curFile); if (*(fd->curFile) != *((*it)->curFile)) { visibilityMatch = false; } } return typeManager.IsFuncParameterTypesIdentical(paramTys1, paramTys2, substituteMap) && fd->TestAttr(Attribute::STATIC) == (*it)->TestAttr(Attribute::STATIC) && fd->TestAttr(Attribute::CONSTRUCTOR) == (*it)->TestAttr(Attribute::CONSTRUCTOR) && fd->TestAttr(Attribute::MAIN_ENTRY) == (*it)->TestAttr(Attribute::MAIN_ENTRY) && visibilityMatch; }); std::vector> sameSigFuncs; std::copy(funcs.begin(), it1, std::back_inserter(sameSigFuncs)); if (sameSigFuncs.size() > 1) { if (needReportErr) { DiagOverloadConflict(diag, sameSigFuncs); } ret = true; } // All processed. if (it1 == funcs.end()) { break; } // No ty case, force push. if (sameSigFuncs.empty()) { sameSigFuncs.emplace_back(*it1++); } std::copy(it1, funcs.end(), funcs.begin()); funcs.resize(funcs.size() - sameSigFuncs.size()); } return ret; } void TypeChecker::TypeCheckerImpl::PreCheckFuncRedefinition(const ASTContext& ctx) { // Redefinition check can be ignored for imported package or package generate by cjogen. if (ctx.curPackage->TestAnyAttr(Attribute::IMPORTED, Attribute::TOOL_ADD)) { return; // Redefinition check can be ignored for imported package. } auto syms = GetSymsByASTKind(ctx, ASTKind::FUNC_DECL); // Redefinition candidates. std::map>> candidates; for (auto sym : syms) { if (!sym->node || sym->node->TestAttr(Attribute::MACRO_INVOKE_FUNC)) { continue; // Do not collect invalid/macro expanded node. } std::string scopeName = ScopeManagerApi::GetScopeNameWithoutTail(sym->scopeName); auto names = std::make_pair(sym->name, scopeName); auto fd = StaticAs(sym->node); if (fd->propDecl) { continue; // Do not check for property's getter/setter. } if (candidates.find(names) != candidates.end()) { candidates[names].emplace_back(fd); } else { candidates[names] = {fd}; } } mpImpl->FilterOutCommonCandidatesIfPlatformExist(candidates); auto candidatesForImport = candidates; for (const auto& [names, funcs] : std::as_const(candidates)) { PreCheckMacroRedefinition(funcs); // Check Functions in the Enum. PreCheckFuncRedefinitionForEnum(funcs); // Check FFI function duplications. PreCheckFuncRedefinitionForFFI(funcs); // Check static and non-static duplications. PreCheckFuncStaticConflict(funcs); // Check same signature for both non-generic and generic functions. PreCheckFuncRedefinitionWithSameSignature(funcs); } } namespace { Ptr GetDanglingReturn(const FuncParam& fp) { Ptr ret = nullptr; Walker(fp.assignment.get(), [&ret](Ptr node) { if (node->astKind == ASTKind::FUNC_BODY) { // We donot check recursively here, since the caller guarantees all the parameters are checked. return VisitAction::SKIP_CHILDREN; } else if (node->astKind == ASTKind::RETURN_EXPR) { ret = RawStaticCast(node); return VisitAction::STOP_NOW; } else { return VisitAction::WALK_CHILDREN; } }).Walk(); return ret; } }; // namespace void TypeChecker::TypeCheckerImpl::CheckReturnAndJump(const ASTContext& ctx) { auto syms = GetSymsByASTKind(ctx, ASTKind::RETURN_EXPR); for (auto sym : syms) { CJC_ASSERT(sym && sym->node); auto re = StaticCast(sym->node); auto refFuncBody = GetCurFuncBody(ctx, sym->scopeName); if (refFuncBody) { if (refFuncBody->funcDecl && IsStaticInitializer(*refFuncBody->funcDecl)) { re->ty = TypeManager::GetInvalidTy(); (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_invalid_return_in_static_init, *sym->node); } else { re->refFuncBody = refFuncBody; } } else { re->ty = TypeManager::GetInvalidTy(); diag.DiagnoseRefactor(DiagKindRefactor::sema_invalid_return, *sym->node); } } auto paramSyms = GetSymsByASTKind(ctx, ASTKind::FUNC_PARAM); for (auto sym : paramSyms) { CJC_ASSERT(sym && sym->node); FuncParam& param = *StaticCast(sym->node); Ptr re = GetDanglingReturn(param); if (re != nullptr) { re->refFuncBody = nullptr; re->ty = TypeManager::GetInvalidTy(); diag.DiagnoseRefactor(DiagKindRefactor::sema_invalid_return, *re); } } syms = GetSymsByASTKind(ctx, ASTKind::JUMP_EXPR); for (auto sym : syms) { CJC_ASSERT(sym && sym->node); if (!ScopeManager::GetRefLoopSymbol(ctx, *sym->node)) { diag.Diagnose(*sym->node, DiagKind::sema_invalid_loop_control); sym->node->ty = TypeManager::GetInvalidTy(); } } } void TypeChecker::TypeCheckerImpl::ReplaceThisTypeInFunc(const AST::FuncDecl& funcDecl) { CJC_ASSERT(funcDecl.funcBody); // Parser guarantees 'This' only exist on the return type of function decl inside classBody. auto cd = DynamicCast(funcDecl.outerDecl); if (cd == nullptr || !Ty::IsTyCorrect(cd->ty)) { return; } // Replace ThisType to RefType. if (auto thisType = DynamicCast(funcDecl.funcBody->retType.get())) { auto rt = MakeOwned(); rt->ref.target = cd; rt->ref.identifier = "This"; rt->ref.identifier.SetPos(thisType->begin, thisType->begin + std::string_view{"This"}.size()); CopyBasicInfo(thisType, rt.get()); rt->ty = typeManager.GetClassThisTy(*cd, cd->ty->typeArgs); funcDecl.funcBody->retType = std::move(rt); } } void TypeChecker::TypeCheckerImpl::PreSetDeclType(const ASTContext& ctx) { // 1. Set user defined enum constructor's type. auto syms = GetSymsByASTKind(ctx, ASTKind::ENUM_DECL); for (auto sym : syms) { CJC_ASSERT(sym && sym->node); auto ed = StaticCast(sym->node); for (auto& constructor : ed->constructors) { SetEnumEleTy(*constructor); } } // 2. Set user defined varDecl and propDecl's type. syms = GetSymsByASTKind(ctx, ASTKind::VAR_DECL); auto propSyms = GetSymsByASTKind(ctx, ASTKind::PROP_DECL); syms.insert(syms.end(), propSyms.begin(), propSyms.end()); for (auto sym : syms) { CJC_ASSERT(sym && sym->node); auto vd = StaticCast(sym->node); if (vd->type && Ty::IsTyCorrect(vd->type->ty)) { vd->ty = vd->type->ty; } } // 3. Set user defined function parameter's types (must exist), and function return type if written. syms = GetSymsByASTKind(ctx, ASTKind::FUNC_DECL); for (auto sym : syms) { CJC_ASSERT(sym && sym->node); auto fd = StaticCast(sym->node); CJC_NULLPTR_CHECK(fd->funcBody); ReplaceThisTypeInFunc(*fd); // Always need to update node of ThisType. if (Ty::IsTyCorrect(fd->ty)) { continue; // Do not replace valid ty. } auto paramTys = GetFuncBodyParamTys(*fd->funcBody); Ptr retTy = TypeManager::GetQuestTy(); if (fd->TestAttr(Attribute::CONSTRUCTOR)) { // Static init has return type of unit. Instance init has return type of current typeDecl. retTy = fd->TestAttr(Attribute::STATIC) ? TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT) : (fd->outerDecl && fd->outerDecl->IsNominalDecl() ? fd->outerDecl->ty : TypeManager::GetInvalidTy()); fd->ty = typeManager.GetFunctionTy(paramTys, retTy); continue; } if (fd->funcBody->retType) { retTy = fd->funcBody->retType->ty; } else if (fd->funcBody->body && fd->funcBody->body->body.empty()) { retTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); } bool isCFunc = fd->funcBody->TestAttr(Attribute::C) || (fd->TestAttr(Attribute::FOREIGN) && IsUnsafeBackend(backendType)); bool hasVariableLenArg = fd->hasVariableLenArg || (!fd->funcBody->paramLists.empty() && fd->funcBody->paramLists[0]->hasVariableLenArg); fd->ty = typeManager.GetFunctionTy(paramTys, retTy, {isCFunc, false, hasVariableLenArg}); if (fd->TestAttr(Attribute::IS_CHECK_VISITED)) { fd->funcBody->ty = fd->ty; } } } void TypeChecker::TypeCheckerImpl::PreCheckUsage(ASTContext& ctx, const Package& pkg) { // NOTE: processing order is important. // Build enum constructors map. BuildImportedEnumConstructorMap(ctx); BuildEnumConstructorMap(ctx); // Resolve all type names. ResolveNames(ctx); // Build and check extend decls for each type. BuildExtendMap(ctx); // Check duplicate interface inheritance for nominal decls. NOTE: Should resolve typeAlias first. StructDeclCircleOrDupCheck(ctx); // Check and set member's basic attributes. CheckAllDeclAttributes(ctx); // Add Object as super class for class declaration. AddSuperClassObjectForClassDecl(ctx); // Collector assumption collection of generic declaration. CollectAndCheckAssumption(ctx); // Check extend generic param & reset inheritance checking flag. CheckExtendRules(ctx); // Check Function Redefinition. PreCheckFuncRedefinition(ctx); // Check return and jump expressions. CheckReturnAndJump(ctx); // Preset user defined types of decls, must before sema check to reduce circular synthesis. PreSetDeclType(ctx); // Check invalid inherit of class, struct, enum, interface. PreCheckInvalidInherit(ctx, pkg); // Check for CJMP decls mpImpl->PreCheck4CJMP(pkg); } void TypeChecker::TypeCheckerImpl::PreCheckInvalidInherit(const ASTContext& ctx, const AST::Package& pkg) { if (pkg.TestAttr(Attribute::IMPORTED)) { return; } auto inheritableDecls = GetAllStructDecls(ctx); for (auto sym : inheritableDecls) { CJC_NULLPTR_CHECK(sym); CJC_NULLPTR_CHECK(sym->node); auto id = DynamicCast(sym->node); if (id == nullptr) { continue; } // For ExtendDecl, we check it in `CheckExtendInterfaces`, so we dont't check it here. if (id->astKind == ASTKind::EXTEND_DECL) { continue; } DiagKind kind = id->astKind == ASTKind::INTERFACE_DECL ? DiagKind::sema_interface_is_not_inheritable : DiagKind::sema_interface_is_not_implementable; for (auto& it : id->inheritedTypes) { if (it->ty->IsCType()) { diag.Diagnose(*it, kind, CTYPE_NAME); } } } } namespace { Ptr GetTypeDecl(TypeManager& tyMgr, Ptr d) { if (auto ed = DynamicCast(d)) { auto target = ed->extendedType->GetTarget(); if (!target) { return tyMgr.GetDummyBuiltInDecl(ed->extendedType->ty); } return GetRealTarget(target); } else { return d; } } Ptr GetTypeDeclOfMember(TypeManager& tyMgr, const Decl& d) { return GetTypeDecl(tyMgr, d.outerDecl); } MemSig MemDecl2Sig(Decl& d) { if (auto fd = DynamicCast(&d)) { return MemSig{fd->identifier, false, fd->funcBody->paramLists[0]->params.size(), fd->generic ? fd->generic->typeParameters.size() : 0}; } else { return MemSig{d.identifier, true}; } } } // namespace void TypeChecker::TypeCheckerImpl::CollectDeclsWithMember(Ptr pkg, ASTContext& ctx) { const std::set TYPE_DECLS{ASTKind::CLASS_DECL, ASTKind::STRUCT_DECL, ASTKind::ENUM_DECL, ASTKind::INTERFACE_DECL, ASTKind::EXTEND_DECL, ASTKind::BUILTIN_DECL}; const std::set MEMBER_DECLS{ASTKind::FUNC_DECL, ASTKind::PROP_DECL, ASTKind::VAR_DECL}; std::map, std::unordered_set> decl2Mems; std::set> allTops; // includes extendDecl needed for finding super types auto mapDecl = [this, &ctx, &decl2Mems, &MEMBER_DECLS](Ptr d) { if (MEMBER_DECLS.count(d->astKind) == 0) { return; } auto sig = MemDecl2Sig(*d); auto top = GetTypeDeclOfMember(typeManager, *d); ctx.mem2Decls[sig].insert(top); decl2Mems[top].insert(sig); if (sig.genArity > 0) { sig.genArity = 0; ctx.mem2Decls[sig].insert(top); decl2Mems[top].insert(sig); } }; auto collectDecl = [&allTops, &TYPE_DECLS, &mapDecl](Ptr top) { if (TYPE_DECLS.count(top->astKind) > 0) { allTops.emplace(top); } for (auto& d : top->GetMemberDecls()) { mapDecl(d); } }; // collect this package std::vector syms = GetAllDecls(ctx); for (auto& sym : syms) { collectDecl(StaticCast(sym->node)); } // collect imported for (auto& file : pkg->files) { auto imported = importManager.GetImportedDecls(*file); for (auto& [_, tops] : imported) { for (auto& top : tops) { collectDecl(top); } } } // collect builtin extends, which somehow is not included in imported for (auto& [_, tops] : typeManager.builtinTyToExtendMap) { for (auto& top : tops) { collectDecl(top); } } // Members are not propagated to inherited types here, since it may take // too much time & memory; only remember accesible subtypes here. // Inherited members are computed on-demand. for (auto& top : allTops) { auto realTop = GetTypeDecl(typeManager, top); if (auto inheritable = DynamicCast(top)) { for (auto sup : inheritable->GetAllSuperDecls()) { ctx.subtypeDeclsMap[sup].insert(realTop); } } } } void TypeChecker::TypeCheckerImpl::PreCheck(const std::vector>& contexts) { // NOTE: PreCheck should not contains any 'Synthesize' call. for (auto& ctx : contexts) { // Stage 1. Check redefinition except functions and build all pkgs declMap to accelerate look up. CheckRedefinition(*ctx); } for (auto& ctx : contexts) { // Stage 2: Set decl sema type without checking inside the decl body. ResolveDecls(*ctx); } // Resolve type alias separately after all user-defined type resolved. ResolveTypeAlias(contexts); // Clear extend related data before iteration of all packages. typeManager.ClearMapCache(); // First part does not need sema check. // Collect all imported non-source extends before checking each package. BuildImportedExtendMap(); for (auto& ctx : contexts) { // Phase: check annotation for FFI. PreCheckAnnoForFFI(*ctx->curPackage); PreCheckUsage(*ctx, *ctx->curPackage); } PreCheckAllExtendInterface(); // CHIR's closure conversion cannot be used without 'Object' class. // If '-no-prelude' is given, should check dependencies of class. if (ci->invocation.globalOptions.chirCC && !ci->invocation.globalOptions.implicitPrelude) { CheckCHIRClassDependencies(); } // Hotfix. Update 'anyTy' in typeManger to real interface 'Any' type. UpdateAnyTy(); UpdateCTypeTy(); } cangjie_compiler-1.0.7/src/Sema/Promotion.cpp000066400000000000000000000151041510705540100211560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the header file Promotion.h */ #include "Promotion.h" #include "cangjie/AST/Types.h" #include "cangjie/AST/ASTCasting.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace AST; // Given C <: D, Promote(C, D) will establish a substitution [X |-> [Bool]]. // Given C <: D & C <: D, Promote(C, D) will establish a substitution [X |-> [Bool, Int64]]. MultiTypeSubst Promotion::GetPromoteTypeMapping(Ty& from, Ty& target) { if (!Ty::IsTyCorrect(&from) || !Ty::IsTyCorrect(&target)) { return {}; } auto prRes = Promote(from, target); if (prRes.empty()) { return {}; } MultiTypeSubst mts; Utils::EraseIf(prRes, [&target](auto& e) { return e->typeArgs.size() != target.typeArgs.size(); }); std::for_each(prRes.begin(), prRes.end(), [&target, &mts](auto prResI) { for (size_t i = 0; i < target.typeArgs.size(); i++) { if (auto targetGenParam = DynamicCast(target.typeArgs[i])) { mts[targetGenParam].emplace(prResI->typeArgs[i]); } } }); return mts; } MultiTypeSubst Promotion::GetDowngradeTypeMapping(Ty& target, Ty& upfrom) { if (!Ty::IsTyCorrect(&target) || !Ty::IsTyCorrect(&upfrom)) { return {}; } auto prRes = Promote(target, upfrom); if (prRes.empty()) { return {}; } MultiTypeSubst mts; Utils::EraseIf(prRes, [&upfrom](auto& e) { return e->typeArgs.size() != upfrom.typeArgs.size(); }); std::for_each(prRes.begin(), prRes.end(), [&upfrom, &mts](auto prResI) { for (size_t i = 0; i < prResI->typeArgs.size(); i++) { if (auto tv = DynamicCast(prResI->typeArgs[i])) { mts[tv].emplace(upfrom.typeArgs[i]); } } }); return mts; } // Given C <: D, Promote(C, D<_>) will give a promoted singleton set {D}. // Given C <: D & C <: D, Promote(C, D<_>) will give a promoted set {D, D} std::set> Promotion::Promote(Ty& from, Ty& target) { if (!Ty::IsTyCorrect(&from) || !Ty::IsTyCorrect(&target)) { return {}; } // Promoting to 'target' type when any of the following conditions is met; // 1. 'from' is type of 'Nothing'; // 2. 'target' is type of 'Any'; // 3. 'from' is a type which meets CType constraints and 'target' is 'CType'; // 4. 'from' is a type which meets JType constraints and 'target' is 'JType'. bool useTarget = from.IsNothing() || target.IsAny() || (target.IsCType() && Ty::IsMetCType(from)); if (useTarget) { return {&target}; } if (from.IsPrimitive() && target.IsPrimitive()) { return PromoteHandleIdealTys(from, target); } if (from.IsFunc() && target.IsFunc()) { return PromoteHandleFunc(from, target); } if (from.IsTuple() && target.IsTuple()) { return PromoteHandleTuple(from, target); } if (auto res = PromoteHandleTyVar(from, target); !res.empty()) { return res; } return PromoteHandleNominal(from, target); } std::set> Promotion::Downgrade(AST::Ty& target, AST::Ty& upfrom) { auto dMaps = GetDowngradeTypeMapping(target, upfrom); if (dMaps.size() < target.typeArgs.size()) { return {}; } else { return tyMgr.GetInstantiatedTys(&target, dMaps); } } std::set> Promotion::PromoteHandleTyVar(Ty& from, Ty& target) { if (!from.IsGeneric()) { return {}; } auto genericTy = RawStaticCast(&from); for (auto& ty : genericTy->upperBounds) { if (auto res = Promote(*ty, target); !res.empty()) { return res; } } return {}; } std::set> Promotion::PromoteHandleIdealTys(Ty& from, Ty& target) const { // Caller guarantees the 'from' and 'target' are primitive type. if (from.kind == target.kind) { return {&from}; } // Very ad hoc fix. Should be improved in the future. if (from.kind == TypeKind::TYPE_IDEAL_INT && target.IsInteger()) { return {&target}; } else if (from.kind == TypeKind::TYPE_IDEAL_FLOAT && target.IsFloating()) { return {&target}; } else { return {}; } } std::set> Promotion::PromoteHandleFunc(Ty& from, Ty& target) { if (tyMgr.IsTyEqual(&from, &target)) { return {&from}; } else { return {}; } } std::set> Promotion::PromoteHandleTuple(Ty& from, Ty& target) { if (tyMgr.IsTyEqual(&from, &target)) { return {&from}; } else { return {}; } } std::set> Promotion::PromoteHandleNominal(Ty& from, const Ty& target) { if (Ty::GetDeclPtrOfTy(&from) == Ty::GetDeclPtrOfTy(&target)) { return {&from}; } TypeSubst typeMapping; auto emplaceElem = [&from, &typeMapping](const std::vector>& tyVars) { if (tyVars.size() != from.typeArgs.size()) { return; } for (size_t i = 0; i < tyVars.size(); i++) { typeMapping.emplace(StaticCast(tyVars[i]), from.typeArgs[i]); } }; if (from.IsClass()) { auto classTy = RawStaticCast(&from); CJC_ASSERT(classTy->decl); if (classTy->decl->ty) { emplaceElem(classTy->declPtr->ty->typeArgs); } } else if (from.IsInterface()) { auto interfaceTy = RawStaticCast(&from); CJC_ASSERT(interfaceTy->decl); if (interfaceTy->decl->ty) { emplaceElem(interfaceTy->declPtr->ty->typeArgs); } } else if (from.IsStruct()) { auto structTy = RawStaticCast(&from); CJC_ASSERT(structTy->decl); if (structTy->decl->ty) { emplaceElem(structTy->declPtr->ty->typeArgs); } } else if (from.IsEnum()) { auto enumTy = RawStaticCast(&from); CJC_ASSERT(enumTy->decl); if (enumTy->decl->ty) { emplaceElem(enumTy->declPtr->ty->typeArgs); } } std::set> res; auto supTys = tyMgr.GetAllSuperTys(from, typeMapping); for (auto ty : supTys) { auto cly = DynamicCast(ty); if (cly && Ty::GetDeclPtrOfTy(cly) == Ty::GetDeclPtrOfTy(&target)) { res.insert(cly); } } return res; } cangjie_compiler-1.0.7/src/Sema/Promotion.h000066400000000000000000000031731510705540100206260ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares a class providing functions for promoting a subtype to a designated supertype if possible. * It also provides utility functions that handle type substitutions. */ #ifndef CANGJIE_SEMA_PROMOTION_H #define CANGJIE_SEMA_PROMOTION_H #include "cangjie/AST/Types.h" #include "cangjie/Sema/TypeManager.h" namespace Cangjie { class Promotion { public: explicit Promotion(TypeManager& tyMgr) : tyMgr(tyMgr) { } MultiTypeSubst GetPromoteTypeMapping(AST::Ty& from, AST::Ty& target); MultiTypeSubst GetDowngradeTypeMapping(AST::Ty& target, AST::Ty& upfrom); std::set> Promote(AST::Ty& from, AST::Ty& target); // will return empty if any type arg of target (the subtype) unused in upfrom (the supertype) // e.g. downgrading to Future from Any std::set> Downgrade(AST::Ty& target, AST::Ty& upfrom); private: TypeManager& tyMgr; std::set> PromoteHandleIdealTys(AST::Ty& from, AST::Ty& target) const; std::set> PromoteHandleFunc(AST::Ty& from, AST::Ty& target); std::set> PromoteHandleTuple(AST::Ty& from, AST::Ty& target); std::set> PromoteHandleTyVar(AST::Ty& from, AST::Ty& target); std::set> PromoteHandleNominal(AST::Ty& from, const AST::Ty& target); }; } // namespace Cangjie #endif // CANGJIE_SEMA_PROMOTION_H cangjie_compiler-1.0.7/src/Sema/ScopeManager.cpp000066400000000000000000000224641510705540100215430ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the ScopeManager related classes. */ #include "ScopeManager.h" #include #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/ScopeManagerApi.h" #include "cangjie/AST/Symbol.h" #include "cangjie/AST/Walker.h" #include "cangjie/Utils/Utils.h" #include "cangjie/AST/ASTCasting.h" using namespace Cangjie; using namespace AST; using namespace std::placeholders; namespace { bool IsRefLoop(const Symbol& sym, const Node& self) { Ptr condExpr = nullptr; Ptr guardExpr = nullptr; // As for while (true) { while (break) {...} }, the break binds to the outer while loop. if (sym.node->astKind == ASTKind::WHILE_EXPR) { condExpr = RawStaticCast(sym.node)->condExpr.get(); } else if (sym.node->astKind == ASTKind::DO_WHILE_EXPR) { condExpr = RawStaticCast(sym.node)->condExpr.get(); } else if (sym.node->astKind == ASTKind::FOR_IN_EXPR) { condExpr = RawStaticCast(sym.node)->inExpression.get(); guardExpr = RawStaticCast(sym.node)->patternGuard.get(); } else { return sym.node->IsLoopExpr(); } bool isInLoopCond = false; auto markIsInLoopCond = [&isInLoopCond, &self](Ptr node) -> VisitAction { switch (node->astKind) { case ASTKind::JUMP_EXPR: if (&self == node) { isInLoopCond = true; } return VisitAction::SKIP_CHILDREN; case ASTKind::WHILE_EXPR: case ASTKind::DO_WHILE_EXPR: case ASTKind::FOR_IN_EXPR: case ASTKind::FUNC_DECL: case ASTKind::LAMBDA_EXPR: return VisitAction::SKIP_CHILDREN; default: return VisitAction::WALK_CHILDREN; } }; Walker w(condExpr, nullptr, markIsInLoopCond); w.Walk(); if (guardExpr) { Walker w2(guardExpr, nullptr, markIsInLoopCond); w2.Walk(); } return !isInLoopCond; } } // namespace AST::Symbol* ScopeManager::GetRefLoopSymbol(const ASTContext& ctx, const Node& self) { return GetCurSatisfiedSymbol(ctx, self.scopeName, [&self](auto& sym) { return IsRefLoop(sym, self); }, [](auto& sym) { return sym.node->astKind == ASTKind::FUNC_BODY; }); } void ScopeManager::InitializeScope(ASTContext& ctx) { ctx.currentScopeLevel += 1; if (ctx.currentScopeLevel > ctx.currentMaxDepth) { ctx.currentMaxDepth = ctx.currentScopeLevel; } // Init scope level is 0, but size of (char indexes: {0}) is 1 // so we need charIndexes.size() - 1 to keep them in the same start line. if (ctx.currentScopeLevel > charIndexes.size() - 1) { // Add a new and first scope. charIndexes.push_back(0); ctx.currentScopeName.push_back(ScopeManagerApi::scopeNameSplit); ctx.currentScopeName.push_back('a'); } else { // Increment index. charIndexes[ctx.currentScopeLevel] += 1; if (ctx.currentScopeLevel > 1 && charIndexes[ctx.currentScopeLevel] == 0) { // When currentScopeLevel > 1, we check whether the index has been cleared to -1(according to // FinalizeScope), we need to restart encoding from a. ctx.currentScopeName = ctx.currentScopeName + ScopeManagerApi::scopeNameSplit + 'a'; } else { ctx.currentScopeName += GetLayerName(charIndexes[ctx.currentScopeLevel]); } } ctx.invertedIndex.scopeNameTrie->Insert(ctx.currentScopeName); } std::string ScopeManager::CalcScopeGateName(const ASTContext& ctx) { unsigned scopeLevel = ctx.currentScopeLevel + 1; if (scopeLevel > charIndexes.size() - 1) { return ctx.currentScopeName + ScopeManagerApi::childScopeNameSplit + 'a'; } if (scopeLevel > 1 && charIndexes[scopeLevel] == -1) { // When scopeLevel > 1, we check whether the index has been cleared to -1(according to // FinalizeScope), we need to restart encoding from a. return ctx.currentScopeName + ScopeManagerApi::childScopeNameSplit + 'a'; } return ctx.currentScopeName + GetLayerName(charIndexes[scopeLevel] + 1, ScopeManagerApi::childScopeNameSplit); } Symbol* ScopeManager::GetOutMostSymbol(const ASTContext& ctx, SymbolKind symbolKind, const std::string& scopeName) { Symbol* curFuncSym = GetCurSymbolByKind(symbolKind, ctx, scopeName); Symbol* insideFuncSym = curFuncSym; while (curFuncSym && curFuncSym->scopeName.length() > 0) { insideFuncSym = curFuncSym; curFuncSym = GetCurSymbolByKind(symbolKind, ctx, curFuncSym->scopeName); } return insideFuncSym; } Symbol* ScopeManager::GetCurSatisfiedSymbol(const ASTContext& ctx, const std::string& scopeName, const std::function& satisfy, const std::function& fail) { std::string scopeGateName = ScopeManagerApi::GetScopeGateName(scopeName); while (true) { auto sym = ScopeManagerApi::GetScopeGate(ctx, scopeGateName); std::string tmpScopeName; if (sym) { tmpScopeName = sym->scopeName; if (fail(*sym)) { return nullptr; } if (satisfy(*sym)) { return sym; } } else { if (scopeGateName.empty()) { // Reach top but not found. return nullptr; } tmpScopeName = ScopeManagerApi::GetParentScopeName(scopeGateName); } scopeGateName = ScopeManagerApi::GetScopeGateName(tmpScopeName); } } Symbol* ScopeManager::GetCurSatisfiedSymbolUntilTopLevel(const ASTContext& ctx, const std::string& scopeName, const std::function& satisfy) { return GetCurSatisfiedSymbol(ctx, scopeName, satisfy, [](const Symbol& /* sym */) { return false; }); } Symbol* ScopeManager::GetCurOuterDeclOfScopeLevelX( const ASTContext& ctx, const Node& checkNode, uint32_t scopeLevel) { if (checkNode.scopeLevel < scopeLevel) { return nullptr; } std::string scopeGateName = ScopeManagerApi::GetScopeGateName(checkNode.scopeName); while (true) { auto sym = ScopeManagerApi::GetScopeGate(ctx, scopeGateName); if (!sym) { return nullptr; } if (sym->scopeLevel == scopeLevel) { return sym; } scopeGateName = ScopeManagerApi::GetScopeGateName(sym->scopeName); } } void ScopeManager::FinalizeScope(ASTContext& ctx) { // Calc length of last layer's name in whole scope, 1 is SPLIT length. unsigned backLength = GetLayerNameLength(charIndexes[ctx.currentScopeLevel]) + 1; ctx.currentScopeName.erase(ctx.currentScopeName.size() - backLength); ctx.currentScopeLevel -= 1; // Reset finalized scope char indexes when finish a toplevel decl. if (ctx.currentScopeLevel == 0) { // Clear from scope level 2. // func a { // // scope_name: a0a && scope_level: 1 // func aa { // // scope_name: a0a0a && scope_level: 2 // } // } // // func b { // // scope_name: a0b && scope_level: 1 // func ba { // // scope_name: a0b0a && scope_level: 2 // } // } // Scope in aa and ba will be cleared. for (size_t i = 2; i <= ctx.currentMaxDepth; i++) { charIndexes[i] = -1; } } } // Get layer name of current index with given separator. std::string ScopeManager::GetLayerName(int layerIndex, char split) { if (layerIndex == 0) { return TOPLEVEL_SCOPE_NAME; } unsigned length = GetLayerNameLength(layerIndex); std::vector y(length + 1); std::vector n(length + 1); n[0] = static_cast(layerIndex); for (unsigned i = 1; i <= length; ++i) { y[i - 1] = n[i - 1] % numChar; n[i] = n[i - 1] / numChar; } std::string s; for (unsigned j = 0; j < length; ++j) { s += chars[y[j]]; } std::reverse(s.begin(), s.end()); return split + s; } // Calculate number of char which is needed to present given layer's name. // Base is 52 (number of capital and small letter). unsigned ScopeManager::GetLayerNameLength(int layerIndex) { if (layerIndex == 0) { return 1; } return static_cast(floor(log(layerIndex) / log(numChar))) + 1; } Symbol* ScopeManager::GetCurSymbolByKind( const SymbolKind symbolKind, const ASTContext& ctx, const std::string& scopeName) { std::function finder; switch (symbolKind) { case SymbolKind::STRUCT: finder = [](const Symbol& sym) { return sym.node->IsNominalDecl(); }; break; case SymbolKind::FUNC: finder = [](const Symbol& sym) { return sym.node->IsFunc(); }; break; case SymbolKind::FUNC_LIKE: finder = [](const Symbol& sym) { return sym.node->IsFuncLike(); }; break; case SymbolKind::TOPLEVEL: finder = [](const Symbol& sym) { return sym.scopeLevel == 0; }; break; } return GetCurSatisfiedSymbolUntilTopLevel(ctx, scopeName, finder); } cangjie_compiler-1.0.7/src/Sema/ScopeManager.h000066400000000000000000000073411510705540100212050ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the ScopeManager related classes. */ #ifndef CANGJIE_AST_SCOPEMANAGER_H #define CANGJIE_AST_SCOPEMANAGER_H #include #include #include #include #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/ScopeManagerApi.h" #include "cangjie/AST/Symbol.h" namespace Cangjie { /** * Current symbol's ASTKind. */ enum class SymbolKind { FUNC, /**< FUNC_DECL || LAMBDA_EXPR. */ FUNC_LIKE, /**< FUNC_DECL || LAMBDA_EXPR || PRIMARY_CTOR || MACRO_DECL */ STRUCT, /**< CLASS_DECL || INTERFACE_DECL || RECORD_DECL || ENUM_DECL || EXTEND_DECL. */ TOPLEVEL, /**< All toplevel decls. */ }; /** Manage @c scopeName, @c scopeLevel, provide Symbol query from @c scopeName. */ class ScopeManager { public: ScopeManager() = default; ~ScopeManager() = default; /** * When entering a block, we need initialize the scope, we do two things: * 1. Increment @p ctx.currentScopeLevel * 2. Modify @p ctx.currentScopeName */ void InitializeScope(ASTContext& ctx); /** * Calc scope gate name of @p ctx.currentScopeName, only used in collect * phase. * * @return Scope gate name, when @p ctx.currentScopeName is a0ab, the scope * gate name maybe a0ab_c. */ std::string CalcScopeGateName(const ASTContext& ctx); /** * When leave a block, we need finalize the scope, we do two things: * 1. Decrement @p ctx.currentScopeLevel * 2. Modify @p ctx.currentScopeName */ void FinalizeScope(ASTContext& ctx); /** Get the out most symbol of given kind from the @p scopeName. */ static AST::Symbol* GetOutMostSymbol(const ASTContext& ctx, SymbolKind symbolKind, const std::string& scopeName); static AST::Symbol* GetRefLoopSymbol(const ASTContext& ctx, const AST::Node& self); static AST::Symbol* GetCurSymbolByKind( const SymbolKind symbolKind, const ASTContext& ctx, const std::string& scopeName); /** * Satisfy and Fail are two predicates. * When Fail(sym) is true, the loop stops and returns nullptr; * When Satisfy(sym) is true, the loop stops and returns the satisfied sym. * Otherwise, the loop continues to find the desired symbol (in the outside scope) */ static AST::Symbol* GetCurSatisfiedSymbol(const ASTContext& ctx, const std::string& scopeName, const std::function& satisfy, const std::function& fail); static AST::Symbol* GetCurSatisfiedSymbolUntilTopLevel(const ASTContext& ctx, const std::string& scopeName, const std::function& satisfy); static AST::Symbol* GetCurOuterDeclOfScopeLevelX( const ASTContext& ctx, const AST::Node& checkNode, uint32_t scopeLevel); void Reset() { charIndexes = {0}; } private: static std::string GetLayerName(int layerIndex, char split = ScopeManagerApi::scopeNameSplit); static unsigned GetLayerNameLength(int layerIndex); // a-z A-Z constexpr static int numChar = 52; constexpr static std::array chars{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; // Index is the number of the \c scopeName, start from 0. std::vector charIndexes{0}; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Sema/Search.cpp000066400000000000000000000076041510705540100204030ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements Search apis for TypeChecker. */ #include #include "TypeCheckerImpl.h" #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Query.h" #include "cangjie/AST/ScopeManagerApi.h" #include "cangjie/AST/Searcher.h" #include "cangjie/AST/Symbol.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace AST; namespace { constexpr unsigned int CORES_REQUIRED_WARMUP = 8; } void TypeChecker::TypeCheckerImpl::WarmupCache(const ASTContext& ctx) const { auto numProcessors = std::thread::hardware_concurrency(); if (numProcessors < CORES_REQUIRED_WARMUP) { return; } auto scopeNames = ctx.searcher->GetScopeNamesByPrefix(ctx, TOPLEVEL_SCOPE_NAME); auto numScopeNames = scopeNames.size(); if (numProcessors < numScopeNames) { return; } std::unordered_map> cache; std::vector threads(numScopeNames); std::vector> searchers(numScopeNames); for (size_t i = 0; i < numScopeNames; i++) { searchers[i] = std::make_unique(); } for (size_t i = 0; i < numScopeNames; i++) { std::string scopeName = scopeNames[i]; Searcher* s = searchers[i].get(); threads[i] = std::thread([scopeName, s, &ctx]() { Query q(Operator::NOT); q.left = std::make_unique(Operator::AND); q.left->left = std::make_unique("scope_name", scopeName); q.left->right = std::make_unique(Operator::OR); q.left->right->left = std::make_unique("ast_kind", "decl"); q.left->right->left->matchKind = MatchKind::SUFFIX; q.left->right->right = std::make_unique("ast_kind", "func_param"); q.right = std::make_unique("ast_kind", "extend_decl"); s->Search(ctx, &q); }); } for (auto& t : threads) { if (t.joinable()) { t.join(); } } for (auto& searcher : searchers) { cache.merge(searcher->GetCache()); } ctx.searcher->SetCache(cache); } std::vector TypeChecker::TypeCheckerImpl::GetToplevelDecls(const ASTContext& ctx) const { // "scope_level:0 && ast_kind: *decl" Query q(Operator::AND); q.left = std::make_unique("scope_level", "0"); q.right = std::make_unique("ast_kind", "decl"); q.right->matchKind = MatchKind::SUFFIX; return ctx.searcher->Search(ctx, &q, Sort::posAsc); } std::vector TypeChecker::TypeCheckerImpl::GetAllDecls(const ASTContext& ctx) const { Query q("ast_kind", "decl", MatchKind::SUFFIX); return ctx.searcher->Search(ctx, &q, Sort::posAsc); } std::vector TypeChecker::TypeCheckerImpl::GetGenericCandidates(const ASTContext& ctx) const { return ctx.searcher->Search(ctx, "ast_kind : class_decl || ast_kind : interface_decl || ast_kind : struct_decl || ast_kind : enum_decl || " "ast_kind : func_decl || ast_kind : extend_decl || ast_kind : builtin_decl", Sort::posAsc); } std::vector TypeChecker::TypeCheckerImpl::GetAllStructDecls(const ASTContext& ctx) const { return ctx.searcher->Search(ctx, "ast_kind : class_decl || ast_kind : interface_decl || ast_kind : struct_decl || ast_kind : enum_decl || " "ast_kind : extend_decl", Sort::posAsc); } std::vector TypeChecker::TypeCheckerImpl::GetSymsByASTKind( const ASTContext& ctx, ASTKind astKind, const Order& order) const { Query q = Query("ast_kind", ASTKIND_TO_STRING_MAP[astKind]); return ctx.searcher->Search(ctx, &q, order); } cangjie_compiler-1.0.7/src/Sema/Test/000077500000000000000000000000001510705540100174025ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/Test/CMakeLists.txt000066400000000000000000000005141510705540100221420ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB TEST_SRC *.cpp) set(SEMA_SRC ${SEMA_SRC} ${TEST_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/Test/MockManager.cpp000066400000000000000000002207041510705540100222770ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "MockManager.h" #include "TypeCheckUtil.h" #include "Desugar/AfterTypeCheck.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/Sema/TestManager.h" namespace Cangjie { namespace { using namespace AST; using namespace TypeCheckUtil; /* * Below are specified names to interfact with intermediate mocking framework API, * all declarations with these names are a part of a contract between the compiler and mocking framework, * they should be located in std.unittest.mock package. * All these declarations are given in std/unittest/mock/mock.cj */ static const std::string CREATE_MOCK_FUNC_NAME = "createMock"; static const std::string CREATE_SPY_FUNC_NAME = "createSpy"; static const std::string CALL_HANDLER_INTERFACE_NAME = "CallHandler"; static const std::string CURRENT_STATIC_FUNC_NAME = "currentStatic"; static const std::string MOCKED_INTERFACE_NAME = "Mocked"; static const std::string GET_HANDLER_METHOD_NAME = "getHandler"; static const std::string REQUIRE_MOCK_OBJ_NAME = "requireMockObject"; static const std::string CALL_STRUCT_NAME = "Call"; static const std::string FUNC_INFO_STRUCT_NAME = "FuncInfo"; static const std::string DECL_ID_STRUCT_NAME = "DeclId"; static const std::string PARAMETER_INFO_STRUCT_NAME = "ParameterInfo"; static const std::string ON_CALL_ENUM_NAME = "OnCall"; static const std::string DECL_KIND_ENUM_NAME = "DeclKind"; static const std::string HAS_DEFAULT_VALUE_FOR_STUB_INTERFACE_NAME = "HasDefaultValueForStub"; static const std::string DEFAULT_VALUE_FOR_STUB_METHOD_NAME = "defaultValueForStub"; static const std::string ON_CALL_METHOD_NAME = "onCall"; // OnCall enum entry names static const std::string RETURN_ZERO_ENTRY = "ReturnZero"; static const std::string RETURN_ENTRY = "Return"; static const std::string THROW_ENTRY = "Throw"; static const std::string CALL_BASE_ENTRY = "CallBase"; static const std::string RETURN_DEFAULT_ENTRY = "ReturnDefault"; // Exceptions related to framework-compiler interaction static const std::string ILLEGAL_MOCK_CALL_EXCEPTION_NAME = "IllegalMockCallException"; static const std::string NO_DEFAULT_VALUE_FOR_MOCK_EXCEPTION_NAME = "NoDefaultValueForMockException"; static const std::string MOCK_RETURN_VALUE_TYPE_MISMATCH_EXCEPTION_NAME = "MockReturnValueTypeMismatchException"; static const std::string MOCK_ZERO_VALUE_NAME = "MockZeroValue"; /* * Below are internal names not related to intermediate mocking framework API */ static const std::string MOCKED_DECL_SUFFIX = "$Mocked"; static const std::string FIELD_NAME_TO_STORE_HANDLER = "handler"; static const std::string FIELD_NAME_TO_STORE_OBJ_TO_SPY = "objectToSpyOn"; static const std::string PARAMETER_TO_MATCH_RETURN_ENTRY = "value"; static const std::string PARAMETER_TO_MATCH_THROW_ENTRY = "e"; // This map describes the correspondance between accessor kind in the compiler code // and accessor kind in the unittest library code static const std::map ACCESSOR_KINDS = { { AccessorKind::METHOD, "Method" }, // Technically, accessors for properies themselves are also generated, // it isn't used in the mocking framework intermediate API, so it's just delegated to the propery getter { AccessorKind::PROP, "PropertyGetter" }, { AccessorKind::PROP_GETTER, "PropertyGetter" }, { AccessorKind::PROP_SETTER, "PropertySetter" }, { AccessorKind::FIELD_GETTER, "FieldGetter" }, { AccessorKind::FIELD_SETTER, "FieldSetter" }, { AccessorKind::TOP_LEVEL_FUNCTION, "TopLevelFunction" }, { AccessorKind::STATIC_METHOD, "StaticMethod" }, { AccessorKind::STATIC_PROP_GETTER, "StaticPropertyGetter" }, { AccessorKind::STATIC_PROP_SETTER, "StaticPropertySetter" }, { AccessorKind::STATIC_FIELD_GETTER, "StaticFieldGetter" }, { AccessorKind::STATIC_FIELD_SETTER, "StaticFieldSetter" }, { AccessorKind::TOP_LEVEL_VARIABLE_GETTER, "TopLevelVariableGetter" }, { AccessorKind::TOP_LEVEL_VARIABLE_SETTER, "TopLevelVariableSetter" } }; template T* GetDeclFromMockPackage(const ImportManager& importManager, const std::string& identifier) { Ptr decl = importManager.GetImportedDecl(UNITTEST_MOCK_INTERNAL_PACKAGE_NAME, identifier); return DynamicCast(decl); } } // namespace // Note that this function doesn't care of private and protected members inside the current of inherited class/interface // We don't support officially mocking private members or protected ones outside the declaring package bool IsDeclAccessible(const Package& currentPackage, const Decl& decl) { if (decl.TestAttr(Attribute::PUBLIC)) { return true; } if (decl.TestAttr(Attribute::PRIVATE)) { return false; } return decl.fullPackageName == currentPackage.fullPackageName; } MockManager::MockManager(ImportManager& importManager, TypeManager& typeManager, const Ptr mockUtils) : typeManager(typeManager), importManager(importManager), mockUtils(mockUtils), // Core used declarations objectDecl(importManager.GetCoreDecl(OBJECT_NAME)), // Mocking specific used declarations callHandlerDecl(GetDeclFromMockPackage(importManager, CALL_HANDLER_INTERFACE_NAME)), mockedInterfaceDecl(GetDeclFromMockPackage(importManager, MOCKED_INTERFACE_NAME)), parameterInfoDecl(GetDeclFromMockPackage(importManager, PARAMETER_INFO_STRUCT_NAME)), declIdDecl(GetDeclFromMockPackage(importManager, DECL_ID_STRUCT_NAME)), funcInfoDecl(GetDeclFromMockPackage(importManager, FUNC_INFO_STRUCT_NAME)), callDecl(GetDeclFromMockPackage(importManager, CALL_STRUCT_NAME)), onCallEnumDecl(GetDeclFromMockPackage(importManager, ON_CALL_ENUM_NAME)), declKindEnumDecl(GetDeclFromMockPackage(importManager, DECL_KIND_ENUM_NAME)), hasDefaultValueForStubDecl( GetDeclFromMockPackage(importManager, HAS_DEFAULT_VALUE_FOR_STUB_INTERFACE_NAME)), noDefaultValueForMockException( GetDeclFromMockPackage(importManager, NO_DEFAULT_VALUE_FOR_MOCK_EXCEPTION_NAME)), mockReturnValueTypeMismatchException( GetDeclFromMockPackage(importManager, MOCK_RETURN_VALUE_TYPE_MISMATCH_EXCEPTION_NAME)), requireMockObject( GetDeclFromMockPackage(importManager, REQUIRE_MOCK_OBJ_NAME)), mockZeroValueDecl(GetDeclFromMockPackage(importManager, MOCK_ZERO_VALUE_NAME)) {} bool MockManager::IsMockClass(const Decl& decl) { return decl.astKind == ASTKind::CLASS_DECL && decl.TestAttr(Attribute::GENERATED_TO_MOCK); } void MockManager::AddObjectSuperTypeIfNeeded( const ClassLikeDecl& originalDecl, ClassDecl& mockedDecl) const { if (!originalDecl.ty->IsInterface()) { return; } auto objectRef = MakeOwned(); objectRef->ref.identifier = OBJECT_NAME; objectRef->ref.target = objectDecl; objectRef->ty = objectDecl->ty; mockedDecl.inheritedTypes.insert(mockedDecl.inheritedTypes.begin(), std::move(objectRef)); } MockManager::GeneratedClassResult MockManager::GenerateMockClassIfNeededAndGet( ClassLikeDecl& originalDecl, Package& curPkg, MockKind mockKind) { CJC_ASSERT(originalDecl.astKind == ASTKind::CLASS_DECL || originalDecl.astKind == ASTKind::INTERFACE_DECL); auto originalMangledName = mockUtils->Mangle(originalDecl); if (mockedClassDecls.find(originalMangledName) != mockedClassDecls.end()) { return { .classDecl=mockedClassDecls[originalMangledName], .generated=false, }; } if (IS_GENERIC_INSTANTIATION_ENABLED && !originalDecl.TestAttr(Attribute::GENERIC_INSTANTIATED) && originalDecl.generic ) { return { .classDecl=nullptr, .generated=false, }; } // No matter in which file of the package mocked decl would be generated, so take the first file of the package auto curFile = Ptr(curPkg.files[0].get()); auto mockedDecl = MakeOwned(); if (!IS_GENERIC_INSTANTIATION_ENABLED) { mockUtils->AddGenericIfNeeded(originalDecl, *mockedDecl); } if (IS_GENERIC_INSTANTIATION_ENABLED || !mockedDecl->generic) { mockedDecl->ty = typeManager.GetClassTy(*mockedDecl, {}); } mockedDecl->moduleName = Utils::GetRootPackageName(curPkg.fullPackageName); mockedDecl->curFile = curFile; mockedDecl->fullPackageName = curPkg.fullPackageName; mockedDecl->identifier = originalDecl.identifier + MOCKED_DECL_SUFFIX + std::to_string(ComputeInstantiationNumberToMangleMockedDecl(originalDecl)); mockedDecl->EnableAttr(Attribute::GENERATED_TO_MOCK); mockedDecl->body = MakeOwned(); mockedDecl->linkage = Linkage::INTERNAL; mockedDecl->EnableAttr(GetAttrByAccessLevel(GetAccessLevel(originalDecl))); mockedDecl->EnableAttr(Attribute::OPEN); auto originalDeclRef = CreateRefType(originalDecl); Ptr substitutedOriginalTy; if (!IS_GENERIC_INSTANTIATION_ENABLED) { substitutedOriginalTy = typeManager.GetInstantiatedTy( originalDeclRef->ty, GenerateTypeMapping(originalDecl, mockedDecl->ty->typeArgs)); originalDeclRef->ty = substitutedOriginalTy; } else { substitutedOriginalTy = originalDecl.ty; } mockedDecl->inheritedTypes.emplace_back(std::move(originalDeclRef)); originalDecl.subDecls.insert(mockedDecl.get()); auto genericSubsts = MockUtils::BuildGenericSubsts(mockedDecl); auto handlerField = CreateFieldDecl(*mockedDecl, FIELD_NAME_TO_STORE_HANDLER, callHandlerDecl->ty, curPkg); AddMockedInterface(*mockedDecl, *handlerField); mockedDecl->body->decls.emplace_back(std::move(handlerField)); mockedDecl->body->decls.emplace_back( CreateFieldDecl(*mockedDecl, FIELD_NAME_TO_STORE_OBJ_TO_SPY, substitutedOriginalTy, curPkg)); mockedDecl->body->decls.emplace_back(CreateConstructorDecl( *mockedDecl, { FIELD_NAME_TO_STORE_HANDLER, FIELD_NAME_TO_STORE_OBJ_TO_SPY }, mockKind, *curFile)); mockedDecl->body->decls.emplace_back(CreateConstructorDecl( *mockedDecl, { FIELD_NAME_TO_STORE_HANDLER }, mockKind, *curFile)); AddObjectSuperTypeIfNeeded(originalDecl, *mockedDecl); AddMockedMembers(originalDecl, *mockedDecl, genericSubsts); AddCurFile(*mockedDecl, curFile); auto mockClassPtr = mockedDecl.get(); mockedClassDecls[originalMangledName] = std::move(mockedDecl); return { .classDecl=mockClassPtr, .generated=true, }; } void MockManager::AddMockedMembers( ClassLikeDecl& originalDecl, ClassDecl& mockedDecl, std::vector& classGenericSubsts) { std::set> foundOverrides; // originalDecl itself is also included here for (auto& superDecl : originalDecl.GetAllSuperDecls()) { for (auto& member : superDecl->GetMemberDecls()) { if (foundOverrides.find(member.get()) != foundOverrides.end()) { continue; } FindOverridesInSuperDecl(*superDecl, *member, foundOverrides); AddMockedMemberIfNeeded(mockedDecl, *member, classGenericSubsts); } } } void MockManager::WrapWithRequireMockObject(Expr& receiverExpr) { std::vector> callArgs; callArgs.emplace_back(CreateFuncArg(ASTCloner::Clone(Ptr(&receiverExpr)))); auto requireMockObjCall = MakeOwned(); requireMockObjCall->baseFunc = mockUtils->CreateDeclBasedReferenceExpr( *requireMockObject, { receiverExpr.ty }, REQUIRE_MOCK_OBJ_NAME, *receiverExpr.curFile); requireMockObjCall->ty = receiverExpr.ty; requireMockObjCall->callKind = CallKind::CALL_DECLARED_FUNCTION; requireMockObjCall->resolvedFunction = requireMockObject; requireMockObjCall->curFile = receiverExpr.curFile; requireMockObjCall->args = std::move(callArgs); if (IS_GENERIC_INSTANTIATION_ENABLED) { mockUtils->Instantiate(*requireMockObjCall); } receiverExpr.desugarExpr = std::move(requireMockObjCall); } void MockManager::AddMockedInterface(ClassDecl& mockedDecl, VarDecl& handlerFieldDecl) { static const auto NOTHING_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_NOTHING); mockedDecl.inheritedTypes.emplace_back(CreateRefType(*mockedInterfaceDecl)); mockedInterfaceDecl->subDecls.emplace(&mockedDecl); // Mocked interface contains exactly one func `getHandler` auto& interfaceMembers = mockedInterfaceDecl->GetMemberDecls(); CJC_ASSERT(interfaceMembers.size() == 1); Ptr getHandlerMethodDecl = As(interfaceMembers[0]); CJC_NULLPTR_CHECK(getHandlerMethodDecl); CJC_ASSERT(getHandlerMethodDecl->identifier == GET_HANDLER_METHOD_NAME); auto getHandlerMethod = ASTCloner::Clone(getHandlerMethodDecl); auto getHandlerMethodBody = MakeOwned(); auto returnExpr = CreateReturnExpr(CreateRefExpr(handlerFieldDecl), getHandlerMethodBody); returnExpr->ty = NOTHING_TY; auto getHandlerMethodBlock = MakeOwned(); getHandlerMethodBlock->ty = NOTHING_TY; getHandlerMethodBlock->body.emplace_back(std::move(returnExpr)); getHandlerMethodBody->ty = getHandlerMethod->ty; getHandlerMethodBody->funcDecl = getHandlerMethod; getHandlerMethodBody->retType = CreateRefType(*callHandlerDecl); getHandlerMethodBody->paramLists.emplace_back(CreateFuncParamList(std::vector>{})); getHandlerMethodBody->parentClassLike = &mockedDecl; getHandlerMethodBody->body = std::move(getHandlerMethodBlock); getHandlerMethod->outerDecl = &mockedDecl; getHandlerMethod->curFile = mockedDecl.curFile; getHandlerMethod->moduleName = mockedDecl.moduleName; getHandlerMethod->fullPackageName = mockedDecl.fullPackageName; getHandlerMethod->EnableAttr(Attribute::OVERRIDE); getHandlerMethod->DisableAttr(Attribute::ABSTRACT); getHandlerMethod->EnableAttr(Attribute::IN_CLASSLIKE); getHandlerMethod->linkage = Linkage::EXTERNAL; getHandlerMethod->funcBody = std::move(getHandlerMethodBody); mockedDecl.body->decls.emplace_back(std::move(getHandlerMethod)); } void MockManager::FindOverridesInSuperDecl( ClassLikeDecl& superDecl, const Decl& member, std::set>& foundOverrides) const { for (auto& anotherSuperDecl : superDecl.GetAllSuperDecls()) { if (anotherSuperDecl == &superDecl) { continue; } for (auto& superMember : anotherSuperDecl->GetMemberDecls()) { if (typeManager.PairIsOverrideOrImpl(member, *superMember)) { foundOverrides.insert(superMember); break; // In one super decl, only one member can be overriden } } } } void MockManager::WriteGeneratedClasses() { for (auto& [mn, mockedClass] : mockedClassDecls) { mockUtils->Instantiate(*mockedClass); mockedClass->curFile->decls.emplace_back(std::move(mockedClass)); } mockedClassDecls.clear(); } bool MockManager::IsMockCall(const CallExpr& ce) { return GetMockKind(ce) != MockKind::NOT_MOCK; } MockKind MockManager::GetMockKind(const CallExpr& ce) { if (ce.callKind != CallKind::CALL_INTRINSIC_FUNCTION || !ce.resolvedFunction) { return MockKind::NOT_MOCK; } if (ce.resolvedFunction->fullPackageName != UNITTEST_MOCK_INTERNAL_PACKAGE_NAME) { return MockKind::NOT_MOCK; } if (ce.resolvedFunction->identifier == CREATE_MOCK_FUNC_NAME) { return MockKind::PLAIN_MOCK; } else if (ce.resolvedFunction->identifier == CREATE_SPY_FUNC_NAME) { return MockKind::SPY; } else { return MockKind::NOT_MOCK; } } Ptr MockManager::FindMockedClassOf(const ClassLikeDecl& decl) { auto mockClassIterator = std::find_if( decl.subDecls.begin(), decl.subDecls.end(), [] (const Ptr decl) { return IsMockClass(*decl); }); if (mockClassIterator == decl.subDecls.end()) { return nullptr; } return As(*mockClassIterator); } int MockManager::ComputeInstantiationNumberToMangleMockedDecl(const ClassLikeDecl& declToMock) { auto genericDecl = As(declToMock.genericDecl); if (!genericDecl) { return 0; } if (instantiationCounters.count(genericDecl) > 0) { instantiationCounters[genericDecl]++; } else { instantiationCounters[genericDecl] = 1; } return instantiationCounters[genericDecl]; } OwnedPtr MockManager::CreateInitCallOfMockClass( ClassDecl& mockClass, std::vector>& mockCallArgs, TypeManager& typeManager, const std::vector> instTys, const std::vector> valueParamTys) { std::vector> initArgs; std::transform( mockCallArgs.begin(), mockCallArgs.end(), std::back_inserter(initArgs), [] (auto & arg) { return ASTCloner::Clone(arg->expr.get()); } ); return CreateInitCall( FindInitDecl(mockClass, typeManager, valueParamTys, instTys).value(), initArgs, *mockClass.curFile, instTys); } OwnedPtr MockManager::CreateIllegalMockCallException( File& curFile, TypeManager& typeMgr, ImportManager& importMgr) { CJC_ASSERT(GetDeclFromMockPackage(importMgr, ILLEGAL_MOCK_CALL_EXCEPTION_NAME)); return CreateThrowException( *GetDeclFromMockPackage(importMgr, ILLEGAL_MOCK_CALL_EXCEPTION_NAME), {}, curFile, typeMgr); } OwnedPtr MockManager::CreateTypeCastForOnCallReturnValue( OwnedPtr exprToCast, const Ptr castTy) const { auto castType = MockUtils::CreateType(castTy); auto varPattern = CreateVarPattern(V_COMPILER, castTy); auto varPatternRef = CreateRefExpr(*(varPattern->varDecl)); std::vector> matchCases; auto typePattern = CreateTypePattern(std::move(varPattern), std::move(castType), *exprToCast); typePattern->matchBeforeRuntime = false; matchCases.emplace_back(CreateMatchCase(std::move(typePattern), std::move(varPatternRef))); std::vector> exceptionCallArgs; exceptionCallArgs.emplace_back( CreateLitConstExpr(LitConstKind::STRING, Ty::ToString(castTy), mockUtils->stringDecl->ty, true)); matchCases.emplace_back( CreateMatchCase(MakeOwned(), CreateThrowException(*mockReturnValueTypeMismatchException, std::move(exceptionCallArgs), *exprToCast->curFile, typeManager))); return CreateMatchExpr(std::move(exprToCast), std::move(matchCases), castTy); } OwnedPtr MockManager::CreateMemberAssignment( VarDecl& member, OwnedPtr rhsExpr) const { static const auto UNIT_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); auto decl = RawStaticCast(member.outerDecl); auto thisExpr = CreateRefExpr( {"this", DEFAULT_POSITION, DEFAULT_POSITION, false}, typeManager.GetClassThisTy(*decl, decl->ty->typeArgs)); thisExpr->ref.target = decl; return CreateAssignExpr(CreateMemberAccess(std::move(thisExpr), member), std::move(rhsExpr), UNIT_TY); } OwnedPtr MockManager::CreateOnCallReturnZeroMatchCase( const FuncDecl& originalMethod, const Ptr zeroValueTy, Decl& enumConstructor) const { auto pattern = MakeOwned(); pattern->ty = enumConstructor.ty->IsFunc() ? StaticCast(enumConstructor.ty)->retTy : enumConstructor.ty; pattern->constructor = mockUtils->CreateRefExprWithInstTys( enumConstructor, enumConstructor.ty->typeArgs, RETURN_ZERO_ENTRY, *(originalMethod.curFile)); return CreateMatchCase( std::move(pattern), mockUtils->CreateZeroValue(zeroValueTy, *(originalMethod.curFile))); } OwnedPtr MockManager::CreateOnCallReturnMatchCase( const FuncDecl& originalMethod, const Ptr retTy, Decl& enumConstructor) const { auto arg = MakeOwned(SrcIdentifier{ PARAMETER_TO_MATCH_RETURN_ENTRY, INVALID_POSITION, INVALID_POSITION, false}); arg->ty = typeManager.GetAnyTy(); arg->varDecl->ty = typeManager.GetAnyTy(); auto argVarDecl = arg->varDecl.get(); auto curFile = originalMethod.curFile; std::vector> patternExprArgs; patternExprArgs.emplace_back(std::move(arg)); auto pattern = MakeOwned(); pattern->ty = enumConstructor.ty->IsFunc() ? StaticCast(enumConstructor.ty)->retTy : enumConstructor.ty; pattern->constructor = mockUtils->CreateRefExprWithInstTys( enumConstructor, enumConstructor.ty->typeArgs, RETURN_ENTRY, *curFile); pattern->patterns = std::move(patternExprArgs); auto refExpr = CreateRefExpr(*argVarDecl); refExpr->curFile = originalMethod.curFile; return CreateMatchCase(std::move(pattern), CreateTypeCastForOnCallReturnValue(std::move(refExpr), retTy)); } OwnedPtr MockManager::CreateOnCallThrowMatchCase( const FuncDecl& originalMethod, Decl& enumConstructor) const { auto arg = MakeOwned( SrcIdentifier{PARAMETER_TO_MATCH_THROW_ENTRY, INVALID_POSITION, INVALID_POSITION, false}); arg->ty = mockUtils->exceptionClassDecl->ty; arg->varDecl->ty = mockUtils->exceptionClassDecl->ty; auto throwExpr = CreateThrowExpr(*(arg->varDecl.get())); throwExpr->ty = TypeManager::GetNothingTy(); throwExpr->expr->ty = mockUtils->exceptionClassDecl->ty; std::vector> patternExprArgs; patternExprArgs.emplace_back(std::move(arg)); auto pattern = MakeOwned(); pattern->ty = enumConstructor.ty->IsFunc() ? StaticCast(enumConstructor.ty)->retTy : enumConstructor.ty; pattern->patterns = std::move(patternExprArgs); pattern->constructor = mockUtils->CreateRefExprWithInstTys( enumConstructor, enumConstructor.ty->typeArgs, THROW_ENTRY, *(originalMethod.curFile)); return CreateMatchCase(std::move(pattern), std::move(throwExpr)); } OwnedPtr MockManager::CreateOnCallCallBaseMatchCase( FuncDecl& originalFunc, const FuncDecl& mockedFunc, Decl& enumConstructor) const { auto mockedClass = As(mockedFunc.outerDecl); CJC_NULLPTR_CHECK(mockedClass); auto objToSpyDecl = GetMemberDecl(*mockedClass, FIELD_NAME_TO_STORE_OBJ_TO_SPY, {}, typeManager); CJC_ASSERT(objToSpyDecl); std::vector> callBaseArgs; for (auto& param : mockedFunc.funcBody->paramLists[0]->params) { callBaseArgs.emplace_back(CreateFuncArg(CreateRefExpr(*param))); } auto memberAccessFuncBaseExpr = CreateMemberAccess(CreateRefExpr(*objToSpyDecl), originalFunc); memberAccessFuncBaseExpr->ty = mockedFunc.ty; memberAccessFuncBaseExpr->EnableAttr(Attribute::GENERATED_TO_MOCK); if (auto& genericInfo = mockedFunc.funcBody->generic; genericInfo) { for (auto& typeParam : genericInfo->typeParameters) { memberAccessFuncBaseExpr->instTys.emplace_back(typeParam->ty); } } auto retTy = mockedFunc.funcBody->retType->ty; auto callBaseFunc = CreateCallExpr( std::move(memberAccessFuncBaseExpr), std::move(callBaseArgs), &originalFunc, retTy, CallKind::CALL_DECLARED_FUNCTION); callBaseFunc->EnableAttr(Attribute::GENERATED_TO_MOCK); auto accessorKind = MockUtils::ComputeAccessorKind(originalFunc); if (accessorKind == AccessorKind::METHOD || accessorKind == AccessorKind::PROP_GETTER || accessorKind == AccessorKind::PROP_SETTER ) { callBaseFunc->EnableAttr(Attribute::MOCK_SUPPORTED); } auto pattern = MakeOwned(); pattern->ty = enumConstructor.ty->IsFunc() ? StaticCast(enumConstructor.ty)->retTy : enumConstructor.ty; pattern->constructor = mockUtils->CreateRefExprWithInstTys( enumConstructor, enumConstructor.ty->typeArgs, CALL_BASE_ENTRY, *(originalFunc.curFile)); auto mangledName = mockUtils->Mangle(*originalFunc.outerDecl); auto spiedInstanceDecl = MockUtils::FindMockGlobalDecl( *mockedClass, MockUtils::spyObjVarName + "$" + mangledName); if (!spiedInstanceDecl || !originalFunc.funcBody->body) { return CreateMatchCase(std::move(pattern), std::move(callBaseFunc)); } static const auto UNIT_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); static const auto BOOL_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); auto thisRef = CreateRefExpr( SrcIdentifier{"this"}, typeManager.GetClassThisTy(*mockedClass, mockedClass->ty->typeArgs)); thisRef->ref.target = mockedClass; auto enumMember = mockUtils->GetInstantiatedDecl( mockUtils->optionDecl, {typeManager.GetAnyTy()}, IS_GENERIC_INSTANTIATION_ENABLED); auto someOuterDecl = Sema::Desugar::AfterTypeCheck::LookupEnumMember(enumMember, OPTION_VALUE_CTOR); auto someOuterDeclRef = CreateRefExpr(*someOuterDecl); auto optionOuterDeclTy = typeManager.GetEnumTy(*mockUtils->optionDecl, {typeManager.GetAnyTy()}); someOuterDeclRef->ty = typeManager.GetFunctionTy({ typeManager.GetAnyTy() }, optionOuterDeclTy); std::vector> someOuterDeclCallArgs {}; someOuterDeclCallArgs.emplace_back(CreateFuncArg(std::move(thisRef))); auto someOuterDeclCall = CreateCallExpr(std::move(someOuterDeclRef), std::move(someOuterDeclCallArgs)); someOuterDeclCall->ty = optionOuterDeclTy; someOuterDeclCall->resolvedFunction = As(someOuterDecl); someOuterDeclCall->callKind = CallKind::CALL_DECLARED_FUNCTION; auto optionOuterDeclMember = mockUtils->GetInstantiatedDecl( optionOuterDeclTy->decl, {mockedClass->ty}, IS_GENERIC_INSTANTIATION_ENABLED); auto noneRef = CreateRefExpr( *Sema::Desugar::AfterTypeCheck::LookupEnumMember(optionOuterDeclMember, OPTION_NONE_CTOR)); noneRef->ty = optionOuterDeclTy; auto callBaseMatch = CreateMatchCase( std::move(pattern), CreateAssignExpr(CreateRefExpr(*spiedInstanceDecl), std::move(someOuterDeclCall), UNIT_TY)); auto callBaseResult = CreateVarDecl( "callBaseResult" + MockUtils::mockAccessorSuffix, std::move(callBaseFunc), nullptr); callBaseResult->curFile = mockedClass->curFile; callBaseResult->fullPackageName = mockedClass->fullPackageName; auto callBaseResultRef = CreateRefExpr(*callBaseResult); auto trueLit = CreateLitConstExpr(LitConstKind::BOOL, "true", BOOL_TY); trueLit->curFile = mockedClass->curFile; auto trueSpyCallMarkerAssign = CreateAssignExpr( CreateRefExpr(*MockUtils::FindMockGlobalDecl(*mockedClass, MockUtils::spyCallMarkerVarName)), std::move(trueLit), UNIT_TY); callBaseMatch->exprOrDecls->body.emplace_back(std::move(trueSpyCallMarkerAssign)); callBaseMatch->exprOrDecls->body.emplace_back(std::move(callBaseResult)); callBaseMatch->exprOrDecls->body.emplace_back( CreateAssignExpr(CreateRefExpr(*spiedInstanceDecl), std::move(noneRef), UNIT_TY)); callBaseMatch->exprOrDecls->body.emplace_back(std::move(callBaseResultRef)); callBaseMatch->exprOrDecls->ty = retTy; return std::move(callBaseMatch); } Ptr MockManager::FindDefaultValueForStubMethod(const Ptr retTy) const { for (auto& extend : typeManager.GetAllExtendsByTy(*retTy)) { auto extendedTy = extend->extendedType->ty; auto foundHasDefaultValueForStub = false; for (auto& iTy : extend->GetSuperInterfaceTys()) { if (auto iTyDecl = As(Ty::GetDeclOfTy(iTy)); iTyDecl && (iTyDecl == hasDefaultValueForStubDecl || iTyDecl->genericDecl == hasDefaultValueForStubDecl) && iTy->typeArgs[0] == extendedTy ) { foundHasDefaultValueForStub = true; break; } } if (!foundHasDefaultValueForStub) { continue; } for (auto& extendDecl : extend->GetMemberDeclPtrs()) { auto extendFuncDecl = As(extendDecl); if (!extendFuncDecl || extendDecl->identifier != DEFAULT_VALUE_FOR_STUB_METHOD_NAME) { continue; } auto fb = extendFuncDecl->funcBody.get(); if (fb->paramLists[0]->params.empty()) { return extendFuncDecl; } } } return nullptr; } OwnedPtr MockManager::CreateOnCallReturnDefaultMatchCase( const FuncDecl& originalMethod, const Ptr retTy, Decl& enumConstructor) { OwnedPtr rhsExpr; if (auto defaultValueProviderDecl = FindDefaultValueForStubMethod(retTy); defaultValueProviderDecl) { defaultForTypePresence[&originalMethod] = true; auto defaultValueProviderRef = MakeOwned(); defaultValueProviderRef->ref = Reference(Ty::ToString(retTy)); defaultValueProviderRef->ref.target = Ty::GetDeclOfTy(retTy); defaultValueProviderRef->instTys = retTy->typeArgs; defaultValueProviderRef->ty = retTy; auto defaultValueProviderMemberAccess = CreateMemberAccess(std::move(defaultValueProviderRef), DEFAULT_VALUE_FOR_STUB_METHOD_NAME); defaultValueProviderMemberAccess->ty = typeManager.GetFunctionTy({}, retTy); defaultValueProviderMemberAccess->target = defaultValueProviderDecl; auto defaultValueProviderCall = MakeOwned(); defaultValueProviderCall->ty = retTy; defaultValueProviderCall->resolvedFunction = defaultValueProviderDecl; defaultValueProviderCall->baseFunc = std::move(defaultValueProviderMemberAccess); defaultValueProviderCall->callKind = CallKind::CALL_DECLARED_FUNCTION; defaultValueProviderCall->curFile = originalMethod.curFile; rhsExpr = std::move(defaultValueProviderCall); } else { defaultForTypePresence[&originalMethod] = false; std::vector> exceptionCallArgs; exceptionCallArgs.emplace_back( CreateLitConstExpr(LitConstKind::STRING, Ty::ToString(retTy), mockUtils->stringDecl->ty, true)); exceptionCallArgs.emplace_back( CreateLitConstExpr( LitConstKind::STRING, HAS_DEFAULT_VALUE_FOR_STUB_INTERFACE_NAME, mockUtils->stringDecl->ty, true)); rhsExpr = CreateThrowException( *noDefaultValueForMockException, std::move(exceptionCallArgs), *originalMethod.curFile, typeManager); } auto pattern = MakeOwned(); pattern->ty = enumConstructor.ty->IsFunc() ? StaticCast(enumConstructor.ty)->retTy : enumConstructor.ty; pattern->constructor = mockUtils->CreateRefExprWithInstTys( enumConstructor, enumConstructor.ty->typeArgs, RETURN_DEFAULT_ENTRY, *(originalMethod.curFile)); return CreateMatchCase(std::move(pattern), std::move(rhsExpr)); } OwnedPtr MockManager::CreateOnCallResultMatchCase( FuncDecl& originalFunc, const Ptr mockedFunc, Decl& enumConstructor) { auto id = enumConstructor.identifier; auto retTy = mockedFunc ? mockedFunc->funcBody->retType->ty : typeManager.GetAnyTy(); if (id == RETURN_ZERO_ENTRY) { auto zeroValueTy = retTy->IsAny() ? mockZeroValueDecl->ty : retTy; return CreateOnCallReturnZeroMatchCase(originalFunc, zeroValueTy, enumConstructor); } else if (id == RETURN_ENTRY) { return CreateOnCallReturnMatchCase(originalFunc, retTy, enumConstructor); } else if (id == THROW_ENTRY) { return CreateOnCallThrowMatchCase(originalFunc, enumConstructor); } else if (id == CALL_BASE_ENTRY && mockedFunc) { return CreateOnCallCallBaseMatchCase(originalFunc, *mockedFunc, enumConstructor); } else if (id == RETURN_DEFAULT_ENTRY) { return CreateOnCallReturnDefaultMatchCase(originalFunc, retTy, enumConstructor); } else { return nullptr; } } OwnedPtr MockManager::CreateEmptyConstructorDecl( ClassDecl& mockedClass, std::vector> params, File& curFile) const { auto constructorDecl = MakeOwned(); constructorDecl->identifier = "init"; constructorDecl->curFile = &curFile; constructorDecl->moduleName = Utils::GetRootPackageName(curFile.curPackage->fullPackageName); constructorDecl->fullPackageName = curFile.curPackage->fullPackageName; constructorDecl->outerDecl = &mockedClass; constructorDecl->EnableAttr(Attribute::CONSTRUCTOR); std::vector> paramTys; std::for_each(params.begin(), params.end(), [¶mTys](const OwnedPtr& param) { paramTys.emplace_back(param->ty); }); constructorDecl->ty = typeManager.GetFunctionTy(paramTys, mockedClass.ty); constructorDecl->EnableAttr(Attribute::IN_CLASSLIKE); std::vector> constructorParamLists; constructorParamLists.emplace_back(CreateFuncParamList(std::move(params))); constructorDecl->funcBody = MakeOwned(); constructorDecl->funcBody->ty = constructorDecl->ty; constructorDecl->funcBody->parentClassLike = &mockedClass; constructorDecl->funcBody->paramLists = std::move(constructorParamLists); constructorDecl->funcBody->funcDecl = constructorDecl.get(); return constructorDecl; } OwnedPtr MockManager::CreateConstructorDecl( ClassDecl& mockedClass, std::set membersToAssign, MockKind mockKind, File& curFile) const { std::vector> constructorBody; std::vector> constructorParams; for (auto& member : mockedClass.GetMemberDecls()) { auto memberDecl = As(member.get()); if (!memberDecl) { continue; } OwnedPtr rhsExpr; if (membersToAssign.find(member->identifier) != membersToAssign.end()) { auto constructorParam = CreateFuncParam(member->identifier, nullptr, nullptr, memberDecl->ty); rhsExpr = CreateRefExpr(*constructorParam); constructorParams.emplace_back(std::move(constructorParam)); } else { rhsExpr = mockUtils->CreateZeroValue(memberDecl->ty, *(mockedClass.curFile)); } constructorBody.emplace_back(CreateMemberAssignment(*memberDecl, std::move(rhsExpr))); } auto constructorDecl = CreateEmptyConstructorDecl(mockedClass, std::move(constructorParams), curFile); constructorDecl->funcBody->body = CreateBlock(std::move(constructorBody), mockedClass.ty); if (mockKind == MockKind::SPY) { AddAssignmentsForSuperFields(*constructorDecl); } // Add return at the end of creation. constructorDecl->funcBody->body->body.emplace_back(CreateReturnExpr( CreateUnitExpr(TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)), constructorDecl->funcBody.get())); return constructorDecl; } void MockManager::AddAssignmentsForSuperFields(const FuncDecl& constructorOfMockedDecl) const { auto decl = As(constructorOfMockedDecl.outerDecl); CJC_ASSERT(decl); Ptr objToSpyOnDecl = GetMemberDecl(*decl, FIELD_NAME_TO_STORE_OBJ_TO_SPY, {}, typeManager); CJC_ASSERT(objToSpyOnDecl); for (auto& superDecl : decl->GetAllSuperDecls()) { if (superDecl == decl) { continue; } for (auto& member : superDecl->GetMemberDecls()) { auto fieldDecl = As(member.get()); if (!fieldDecl || Is(*fieldDecl)) { continue; } auto objToSpyOnRef = MakeOwned(); objToSpyOnRef->ref = Reference(FIELD_NAME_TO_STORE_OBJ_TO_SPY); objToSpyOnRef->ref.target = objToSpyOnDecl; objToSpyOnRef->ty = objToSpyOnDecl->ty; auto rhsExpr = CreateMemberAccess(std::move(objToSpyOnRef), fieldDecl->identifier); constructorOfMockedDecl.funcBody->body->body.emplace_back( CreateMemberAssignment(*fieldDecl, std::move(rhsExpr))); } } } OwnedPtr MockManager::CreateFieldDecl( ClassLikeDecl& decl, const std::string& identifier, const Ptr ty, const Package& curPkg) { auto fieldDecl = MakeOwned(); fieldDecl->identifier = identifier; fieldDecl->ty = ty; fieldDecl->isMemberParam = false; fieldDecl->outerDecl = &decl; fieldDecl->curFile = decl.curFile; fieldDecl->moduleName = Utils::GetRootPackageName(curPkg.fullPackageName); fieldDecl->fullPackageName = curPkg.fullPackageName; return fieldDecl; } OwnedPtr MockManager::CreateDeclId(const Decl& decl, File& curFile) const { std::vector> valueArgs; auto mangledName = mockUtils->Mangle(*mockUtils->GetGenericDecl(Ptr(&decl))); valueArgs.emplace_back( CreateLitConstExpr( LitConstKind::STRING, mangledName, mockUtils->stringDecl->ty, true)); valueArgs.emplace_back( CreateLitConstExpr( LitConstKind::STRING, mockUtils->GetOriginalIdentifierOfMockAccessor(decl), mockUtils->stringDecl->ty, true)); return CreateInitCall(FindInitDecl(*declIdDecl, typeManager, valueArgs).value(), valueArgs, curFile); } OwnedPtr MockManager::CreateParamInfo(const FuncParam& param, int position, File& curFile) const { static const auto BOOL_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); static const auto INT_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64); std::vector> valueArgs; valueArgs.emplace_back(CreateLitConstExpr(LitConstKind::STRING, param.identifier, mockUtils->stringDecl->ty, true)); valueArgs.emplace_back(CreateLitConstExpr(LitConstKind::INTEGER, std::to_string(position), INT_TY, true)); valueArgs.emplace_back( CreateLitConstExpr(LitConstKind::BOOL, param.isNamedParam ? "true" : "false", BOOL_TY, true)); valueArgs.emplace_back( CreateLitConstExpr(LitConstKind::BOOL, param.assignment != nullptr ? "true" : "false", BOOL_TY, true)); return CreateInitCall( FindInitDecl(*parameterInfoDecl, typeManager, valueArgs).value(), valueArgs, curFile); } OwnedPtr MockManager::CreateParamsInfo(const FuncDecl& decl, File& curFile) const { std::vector> paramInfoArgs {}; int paramNumber = 1; for (auto& param : decl.funcBody->paramLists[0]->params) { paramInfoArgs.emplace_back(CreateParamInfo(*param, paramNumber, curFile)); paramNumber++; } auto paramInfoArray = CreateArrayLit( std::move(paramInfoArgs), typeManager.GetStructTy(*mockUtils->arrayDecl, { parameterInfoDecl->ty })); AddArrayLitConstructor(*paramInfoArray); paramInfoArray->curFile = &curFile; return paramInfoArray; } OwnedPtr MockManager::CreateTypeParamsInfo(const FuncDecl& decl, File& curFile) const { std::vector> typeParamInfoArgs {}; if (decl.funcBody->generic) { for (auto& typeParam : decl.funcBody->generic->typeParameters) { typeParamInfoArgs.emplace_back( CreateLitConstExpr(LitConstKind::STRING, typeParam->identifier, mockUtils->stringDecl->ty, true) ); } } auto typeParamInfoArray = CreateArrayLit( std::move(typeParamInfoArgs), typeManager.GetStructTy(*mockUtils->arrayDecl, { mockUtils->stringDecl->ty })); AddArrayLitConstructor(*typeParamInfoArray); typeParamInfoArray->curFile = &curFile; return typeParamInfoArray; } OwnedPtr MockManager::CreateFuncInfo(FuncDecl& funcDecl, File& curFile) { static const auto INT_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64); static const auto BOOL_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); std::vector> positionInfo; positionInfo.emplace_back( CreateLitConstExpr(LitConstKind::STRING, funcDecl.curFile->fileName, mockUtils->stringDecl->ty, true)); positionInfo.emplace_back( CreateLitConstExpr(LitConstKind::INTEGER, std::to_string(funcDecl.identifier.Begin().line), INT_TY, true)); positionInfo.emplace_back( CreateLitConstExpr(LitConstKind::INTEGER, std::to_string(funcDecl.identifier.Begin().column), INT_TY, true)); std::vector> funcIntoItems; funcIntoItems.emplace_back(CreateDeclId(funcDecl, curFile)); funcIntoItems.emplace_back(CreateParamsInfo(funcDecl, curFile)); funcIntoItems.emplace_back(CreateTypeParamsInfo(funcDecl, curFile)); funcIntoItems.emplace_back( CreateTupleLit(std::move(positionInfo), typeManager.GetTupleTy({ mockUtils->stringDecl->ty, INT_TY, INT_TY }))); funcIntoItems.emplace_back( CreateLitConstExpr(LitConstKind::BOOL, funcDecl.funcBody->body != nullptr ? "true" : "false", BOOL_TY, true)); OwnedPtr outerDeclExpr; auto optionDecl = mockUtils->optionDecl; auto optionOuterIdDeclTy = typeManager.GetEnumTy(*optionDecl, { declIdDecl->ty }); if (funcDecl.outerDecl) { auto someOuterDeclIdDecl = Sema::Desugar::AfterTypeCheck::LookupEnumMember(optionDecl, OPTION_VALUE_CTOR); auto someInstanceRef = CreateRefExpr(*someOuterDeclIdDecl); someInstanceRef->ty = typeManager.GetFunctionTy({declIdDecl->ty}, optionOuterIdDeclTy); Ptr outerDecl = funcDecl.outerDecl; if (funcDecl.TestAttr(Attribute::IN_EXTEND)) { outerDecl = mockUtils->GetExtendedClassDecl(funcDecl); } auto outerDeclId = CreateDeclId(*outerDecl, curFile); std::vector> someOuterDeclIdCallArgs {}; someOuterDeclIdCallArgs.emplace_back(CreateFuncArg(std::move(outerDeclId))); auto someOuterDeclIdCall = CreateCallExpr(std::move(someInstanceRef), std::move(someOuterDeclIdCallArgs)); someOuterDeclIdCall->ty = optionOuterIdDeclTy; someOuterDeclIdCall->resolvedFunction = As(someOuterDeclIdDecl); someOuterDeclIdCall->callKind = CallKind::CALL_DECLARED_FUNCTION; outerDeclExpr = std::move(someOuterDeclIdCall); } else { outerDeclExpr = CreateRefExpr(*Sema::Desugar::AfterTypeCheck::LookupEnumMember(optionDecl, OPTION_NONE_CTOR)); } outerDeclExpr->ty = optionOuterIdDeclTy; funcIntoItems.emplace_back(std::move(outerDeclExpr)); funcIntoItems.emplace_back( CreateLitConstExpr(LitConstKind::BOOL, defaultForTypePresence[&funcDecl] ? "true" : "false", BOOL_TY, true)); funcIntoItems.emplace_back( CreateLitConstExpr( LitConstKind::STRING, Ty::ToString(RawStaticCast(funcDecl.ty)->retTy), mockUtils->stringDecl->ty, true)); funcIntoItems.emplace_back( CreateLitConstExpr( LitConstKind::BOOL, IsDeclAccessible(*curFile.curPackage, funcDecl) ? "true" : "false", BOOL_TY, true)); funcIntoItems.emplace_back(CreateDeclKind(funcDecl)); return CreateInitCall( FindInitDecl(*funcInfoDecl, typeManager, funcIntoItems).value(), funcIntoItems, curFile); } OwnedPtr MockManager::CreateDeclKind(const FuncDecl& decl) const { static const auto BOOL_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); std::vector> declKindArgs; declKindArgs.emplace_back( CreateFuncArg( CreateLitConstExpr( LitConstKind::STRING, mockUtils->GetOriginalIdentifierOfAccessor(decl), mockUtils->stringDecl->ty, true))); auto accessorKind = MockUtils::ComputeAccessorKind(decl); auto libAccessorKind = ACCESSOR_KINDS.at(accessorKind); auto needToRecordSetterPresence = false; auto hasSetter = false; if (accessorKind == AccessorKind::FIELD_GETTER) { needToRecordSetterPresence = true; hasSetter = MockUtils::IsGetterForMutField(decl); } else if (accessorKind == AccessorKind::PROP_GETTER) { needToRecordSetterPresence = true; hasSetter = decl.propDecl->isVar; } if (needToRecordSetterPresence) { declKindArgs.emplace_back( CreateFuncArg(CreateLitConstExpr(LitConstKind::BOOL, hasSetter ? "true" : "false", BOOL_TY, true))); } for (auto& enumEntry : declKindEnumDecl->constructors) { if (enumEntry->identifier != libAccessorKind) { continue; } auto entryRetType = RawStaticCast(enumEntry->ty)->retTy; auto enumEntryRef = MakeOwned(); enumEntryRef->ref = Reference(libAccessorKind); enumEntryRef->ref.target = enumEntry; enumEntryRef->ty = entryRetType; auto enumEntryBase = CreateMemberAccess(std::move(enumEntryRef), libAccessorKind); auto enumEntryCallExpr = MakeOwned(); enumEntryCallExpr->ty = entryRetType; enumEntryCallExpr->resolvedFunction = RawStaticCast(enumEntryBase->target); enumEntryCallExpr->baseFunc = std::move(enumEntryBase); enumEntryCallExpr->args = std::move(declKindArgs); enumEntryCallExpr->callKind = CallKind::CALL_DECLARED_FUNCTION; enumEntryCallExpr->curFile = decl.curFile; return enumEntryCallExpr; } return nullptr; } OwnedPtr MockManager::CreateCallInfo( FuncDecl& originalFunction, OwnedPtr mockedArgsArray, OwnedPtr typeArgsArray, const Ptr curFile, OwnedPtr objRef) { OwnedPtr instanceExpr; auto optionObjectTy = typeManager.GetEnumTy(*mockUtils->optionDecl, { objectDecl->ty }); if (objRef) { auto someInstanceDecl = Sema::Desugar::AfterTypeCheck::LookupEnumMember( mockUtils->optionDecl, OPTION_VALUE_CTOR); auto someInstanceRef = CreateRefExpr(*someInstanceDecl); someInstanceRef->ty = typeManager.GetFunctionTy({objectDecl->ty}, optionObjectTy); std::vector> someInstancCallArgs {}; someInstancCallArgs.emplace_back(CreateFuncArg(std::move(objRef))); auto instanceCall = CreateCallExpr(std::move(someInstanceRef), std::move(someInstancCallArgs)); instanceCall->ty = optionObjectTy; instanceCall->resolvedFunction = As(someInstanceDecl); instanceCall->callKind = CallKind::CALL_DECLARED_FUNCTION; instanceExpr = std::move(instanceCall); } else { instanceExpr = CreateRefExpr( *Sema::Desugar::AfterTypeCheck::LookupEnumMember(mockUtils->optionDecl, OPTION_NONE_CTOR)); } instanceExpr->ty = optionObjectTy; std::vector> callInfoItems; callInfoItems.emplace_back(std::move(instanceExpr)); callInfoItems.emplace_back(std::move(typeArgsArray)); callInfoItems.emplace_back(std::move(mockedArgsArray)); callInfoItems.emplace_back(CreateFuncInfo(originalFunction, *curFile)); return CreateInitCall( FindInitDecl(*callDecl, typeManager, callInfoItems).value(), callInfoItems, *curFile); } OwnedPtr MockManager::GetHandlerRefFromClass(const Ptr decl) { Ptr callHandler = GetMemberDecl(*decl, FIELD_NAME_TO_STORE_HANDLER, {}, typeManager); CJC_ASSERT(callHandler); auto callHandlerRef = MakeOwned(); callHandlerRef->ref = Reference(FIELD_NAME_TO_STORE_HANDLER); callHandlerRef->ref.target = callHandler; callHandlerRef->ty = callHandler->ty; callHandlerRef->curFile = decl->curFile; return callHandlerRef; } OwnedPtr MockManager::CreateOnCallInvocation( OwnedPtr mockedArgsArray, OwnedPtr typeArgsArray, FuncDecl& originalFunc, OwnedPtr objRef, OwnedPtr handler ) { auto curFile = handler->curFile; std::vector> onCallArgs; onCallArgs.emplace_back( CreateFuncArg( CreateCallInfo( originalFunc, std::move(mockedArgsArray), std::move(typeArgsArray), curFile, std::move(objRef)))); auto onCallBaseFunc = CreateMemberAccess(std::move(handler), ON_CALL_METHOD_NAME); auto onCallFunc = MakeOwned(); onCallFunc->ty = RawStaticCast(onCallBaseFunc->ty)->retTy; onCallFunc->resolvedFunction = RawStaticCast(onCallBaseFunc->target); onCallFunc->baseFunc = std::move(onCallBaseFunc); onCallFunc->args = std::move(onCallArgs); onCallFunc->callKind = CallKind::CALL_DECLARED_FUNCTION; onCallFunc->curFile = curFile; return onCallFunc; } OwnedPtr MockManager::CreateStaticMethodStub( FuncDecl& originalMethod, ClassDecl& mockedDecl, std::vector& classGenericSubsts) { OwnedPtr mockedMethod = CreateMockedMethodWithoutBody(originalMethod, mockedDecl, classGenericSubsts); auto retTy = mockedMethod->funcBody->retType->ty; // Static methods in mocked classes should never be called, there is no way to do it from user's code std::vector> nodes; nodes.emplace_back(CreateIllegalMockCallException(*originalMethod.curFile, typeManager, importManager)); mockedMethod->funcBody->body = CreateBlock(std::move(nodes)); mockedMethod->funcBody->body->ty = retTy; return mockedMethod; } OwnedPtr MockManager::CreateMockedMethod( FuncDecl& originalMethod, ClassDecl& mockedDecl, std::vector& classGenericSubsts) { // Generate mocked methods only for previously generated accessors or for an original method, // if it doesn't have accessor if (MockUtils::IsMockAccessorRequired(originalMethod)) { return nullptr; } OwnedPtr mockedMethod = CreateMockedMethodWithoutBody(originalMethod, mockedDecl, classGenericSubsts); auto retTy = mockedMethod->funcBody->retType->ty; auto thisRef = CreateRefExpr( SrcIdentifier{"this"}, typeManager.GetClassThisTy(mockedDecl, mockedDecl.ty->typeArgs)); thisRef->ref.target = &mockedDecl; std::vector> matchCasesForOnCallReturnedValue; for (auto& constructor : onCallEnumDecl->constructors) { if (auto matchCase = CreateOnCallResultMatchCase(originalMethod, mockedMethod, *constructor); matchCase) { matchCasesForOnCallReturnedValue.emplace_back(std::move(matchCase)); } } std::vector> mockedMethodBodyNodes; mockedMethodBodyNodes.emplace_back( CreateReturnExpr( CreateMatchExpr( CreateOnCallInvocation( mockUtils->WrapCallArgsIntoArray(*mockedMethod), mockUtils->WrapCallTypeArgsIntoArray(*mockedMethod), originalMethod, std::move(thisRef), GetHandlerRefFromClass(&mockedDecl)), std::move(matchCasesForOnCallReturnedValue), retTy), mockedMethod->funcBody.get())); mockedMethod->funcBody->body = CreateBlock(std::move(mockedMethodBodyNodes)); mockedMethod->funcBody->body->ty = retTy; mockedMethod->linkage = Linkage::INTERNAL; return mockedMethod; } OwnedPtr MockManager::CreateMockedMethodWithoutBody( FuncDecl& originalMethod, ClassDecl& mockedDecl, std::vector& classGenericSubsts) { OwnedPtr mockedMethod = ASTCloner::Clone(Ptr(&originalMethod)); if (!IS_GENERIC_INSTANTIATION_ENABLED) { mockUtils->AddGenericIfNeeded(originalMethod, *mockedMethod); MockUtils::PrependFuncGenericSubst( originalMethod.funcBody->generic, mockedMethod->funcBody->generic, classGenericSubsts); } auto funcTy = mockUtils->GetInstantiatedTy(originalMethod.ty, classGenericSubsts); auto retTy = mockUtils->GetInstantiatedTy(originalMethod.funcBody->retType->ty, classGenericSubsts); auto mockedMethodBody = MakeOwned(); mockedMethodBody->generic = std::move(mockedMethod->funcBody->generic); mockedMethodBody->parentClassLike = &mockedDecl; mockedMethodBody->funcDecl = mockedMethod.get(); mockedMethodBody->ty = funcTy; mockedMethodBody->retType = std::move(mockedMethod->funcBody->retType); mockedMethodBody->retType->ty = retTy; std::vector> mockedMethodParams {}; for (auto& param : originalMethod.funcBody->paramLists[0]->params) { auto funcParam = CreateFuncParam( param->identifier, nullptr, nullptr, mockUtils->GetInstantiatedTy(param->ty, classGenericSubsts)); funcParam->outerDecl = mockedMethod.get(); funcParam->curFile = mockedDecl.curFile; mockedMethodParams.emplace_back(std::move(funcParam)); } std::vector> mockedMethodParamLists {}; mockedMethodParamLists.emplace_back(CreateFuncParamList(std::move(mockedMethodParams))); mockedMethodBody->paramLists = std::move(mockedMethodParamLists); mockedMethod->outerDecl = &mockedDecl; mockedMethod->ty = funcTy; mockedMethod->linkage = Linkage::EXTERNAL; mockedMethod->EnableAttr(Attribute::OVERRIDE); mockedMethod->DisableAttr(Attribute::ABSTRACT); mockedMethod->EnableAttr(Attribute::IN_CLASSLIKE); mockedMethod->EnableAttr(Attribute::OPEN); mockedMethod->curFile = mockedDecl.curFile; mockedMethod->funcBody = std::move(mockedMethodBody); mockedMethod->mangledName = mockUtils->Mangle(*mockedMethod); return mockedMethod; } OwnedPtr MockManager::CreateMockedProp( PropDecl& originalProp, ClassDecl& mockedDecl, std::vector& classGenericSubsts) { if (MockUtils::IsMockAccessorRequired(originalProp)) { return nullptr; } auto mockedProp = ASTCloner::Clone(Ptr(&originalProp)); mockedProp->EnableAttr(Attribute::OVERRIDE); mockedProp->DisableAttr(Attribute::ABSTRACT); mockedProp->EnableAttr(Attribute::IN_CLASSLIKE); std::vector> mockedGetters; std::vector> mockedSetters; for (auto& getter : originalProp.getters) { auto mockedGetter = CreateMockedMethod(*getter, mockedDecl, classGenericSubsts); if (mockedGetter) { mockedGetters.emplace_back(std::move(mockedGetter)); } } for (auto& setter : originalProp.setters) { auto mockedSetter = CreateMockedMethod(*setter, mockedDecl, classGenericSubsts); if (mockedSetter) { mockedSetters.emplace_back(std::move(mockedSetter)); } } mockedProp->getters = std::move(mockedGetters); mockedProp->setters = std::move(mockedSetters); mockedProp->linkage = Linkage::INTERNAL; return mockedProp; } void MockManager::AddMockedMemberIfNeeded(ClassDecl& mockedDecl, Decl& member, std::vector& classGenericSubsts) { if (member.TestAttr(Attribute::CONSTRUCTOR)) { return; } if (member.TestAttr(Attribute::STATIC)) { auto originalMethod = As(&member); if (!originalMethod || originalMethod->funcBody->body != nullptr) { return; } // For static methods without default implementation creating stub implementation if (auto mockedMethod = CreateStaticMethodStub(*originalMethod, mockedDecl, classGenericSubsts)) { mockedDecl.body->decls.emplace_back(std::move(mockedMethod)); } return; } if (auto originalProp = As(&member); originalProp) { if (auto mockedProp = CreateMockedProp(*originalProp, mockedDecl, classGenericSubsts); mockedProp) { mockedDecl.body->decls.emplace_back(std::move(mockedProp)); } } if (auto originalMethod = As(&member); originalMethod) { if (IS_GENERIC_INSTANTIATION_ENABLED && (originalMethod->genericDecl != nullptr || originalMethod->TestAttr(Attribute::GENERIC) || originalMethod->ty->HasGeneric()) ) { return; } if (auto mockedMethod = CreateMockedMethod(*originalMethod, mockedDecl, classGenericSubsts); mockedMethod) { mockedDecl.body->decls.emplace_back(std::move(mockedMethod)); } } } std::vector> MockManager::GenerateCallHandlerCases(FuncDecl& staticDecl, const Expr& injectTo) { std::vector> matchCasesForOnCallReturnedValue; auto handlerRetTy = typeManager.GetAnyTy(); auto optionFuncRetTy = typeManager.GetEnumTy(*mockUtils->optionDecl, { handlerRetTy }); auto optionFuncRet = optionFuncRetTy->decl; for (auto& constructor : onCallEnumDecl->constructors) { auto matchCase = CreateOnCallResultMatchCase(staticDecl, nullptr, *constructor); if (matchCase) { auto someCaseDecl = Sema::Desugar::AfterTypeCheck::LookupEnumMember(optionFuncRet, OPTION_VALUE_CTOR); auto someCaseRef = CreateRefExpr(*someCaseDecl); someCaseRef->ty = typeManager.GetFunctionTy({handlerRetTy}, optionFuncRetTy); std::vector> someCaseCallArgs {}; auto firstExpr = As(matchCase->exprOrDecls->body[0]); CJC_NULLPTR_CHECK(firstExpr); someCaseCallArgs.emplace_back(CreateFuncArg(ASTCloner::Clone(Ptr(firstExpr)))); auto someCaseCall = CreateCallExpr(std::move(someCaseRef), std::move(someCaseCallArgs)); someCaseCall->resolvedFunction = As(someCaseDecl); someCaseCall->callKind = CallKind::CALL_DECLARED_FUNCTION; someCaseCall->ty = optionFuncRetTy; firstExpr->desugarExpr = std::move(someCaseCall); matchCasesForOnCallReturnedValue.emplace_back(std::move(matchCase)); } else { auto pattern = MakeOwned(); pattern->ty = constructor->ty->IsFunc() ? StaticCast(constructor->ty)->retTy : constructor->ty; pattern->constructor = mockUtils->CreateRefExprWithInstTys( *constructor, constructor->ty->typeArgs, RETURN_ZERO_ENTRY, *(injectTo.curFile)); auto none = Sema::Desugar::AfterTypeCheck::LookupEnumMember(optionFuncRet, OPTION_NONE_CTOR); matchCase = CreateMatchCase(std::move(pattern), mockUtils->CreateRefExprWithInstTys(*none, {handlerRetTy}, OPTION_NONE_CTOR, *(injectTo.curFile))); matchCasesForOnCallReturnedValue.emplace_back(std::move(matchCase)); } } return matchCasesForOnCallReturnedValue; } OwnedPtr MockManager::GetMockedObjectHandler(OwnedPtr objRef, const Ptr curFile) { static const auto NOTHING_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_NOTHING); auto handlerRetTy = typeManager.GetAnyTy(); auto optionFuncRetTy = typeManager.GetEnumTy(*mockUtils->optionDecl, { handlerRetTy }); auto noneRef = CreateRefExpr( *Sema::Desugar::AfterTypeCheck::LookupEnumMember(mockUtils->optionDecl, OPTION_NONE_CTOR)); noneRef->ty = optionFuncRetTy; auto returnNoneExpr = CreateReturnExpr(std::move(noneRef)); returnNoneExpr->ty = NOTHING_TY; auto mockedObjVarPattern = CreateVarPattern(V_COMPILER, mockedInterfaceDecl->ty); auto mockedObjRef = CreateRefExpr(*mockedObjVarPattern->varDecl); mockedObjRef->ty = mockedInterfaceDecl->ty; Ptr getHandlerMethodDecl = GetMemberDecl( *mockedInterfaceDecl, GET_HANDLER_METHOD_NAME, {}, typeManager); CJC_NULLPTR_CHECK(getHandlerMethodDecl); auto getHandlerMa = CreateMemberAccess(ASTCloner::Clone(mockedObjRef.get()), *getHandlerMethodDecl); auto getHandlerCall = MakeOwned(); getHandlerCall->ty = callHandlerDecl->ty; getHandlerCall->resolvedFunction = RawStaticCast(getHandlerMa->target); CJC_NULLPTR_CHECK(getHandlerCall->resolvedFunction); getHandlerCall->baseFunc = std::move(getHandlerMa); getHandlerCall->args = std::vector>{}; getHandlerCall->callKind = CallKind::CALL_DECLARED_FUNCTION; getHandlerCall->curFile = curFile; auto mockedObjTypePattern = CreateTypePattern( std::move(mockedObjVarPattern), CreateRefType(*mockedInterfaceDecl), *objRef); mockedObjTypePattern->matchBeforeRuntime = false; mockedObjTypePattern->needRuntimeTypeCheck = true; std::vector> mockedCastMatchCases; mockedCastMatchCases.emplace_back(CreateMatchCase(std::move(mockedObjTypePattern), std::move(getHandlerCall))); mockedCastMatchCases.emplace_back(CreateMatchCase(MakeOwned(), std::move(returnNoneExpr))); auto mockedObjMatch = CreateMatchExpr(std::move(objRef), std::move(mockedCastMatchCases), callHandlerDecl->ty); mockedObjMatch->curFile = curFile; return mockedObjMatch; } OwnedPtr MockManager::GetCurrentStaticHandler(const Ptr curFile) { auto currentStaticMa = CreateMemberAccess(CreateRefExpr(*callHandlerDecl), CURRENT_STATIC_FUNC_NAME); currentStaticMa->ty = typeManager.GetFunctionTy({}, callHandlerDecl->ty); auto target = currentStaticMa->target; auto currentStaticCall = CreateCallExpr(std::move(currentStaticMa), {}); currentStaticCall->resolvedFunction = As(target); currentStaticCall->callKind = CallKind::CALL_DECLARED_FUNCTION; currentStaticCall->ty = callHandlerDecl->ty; currentStaticCall->curFile = curFile; return currentStaticCall; } OwnedPtr MockManager::GenerateCallHandlerLambda( FuncDecl& decl, const Expr& injectTo, Ptr declForInfo) { static const std::string TYPE_PARAMS_PARAM_NAME_FOR_HANDLER_LAMBDA = "typeParams"; static const std::string PARAMS_PARAM_NAME_FOR_HANDLER_LAMBDA = "args"; static const std::string OBJECT_PARAM_NAME_FOR_HANDLER_LAMBDA = "obj"; static const auto NOTHING_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_NOTHING); auto isMethod = !decl.IsStaticOrGlobal(); auto handlerRetTy = typeManager.GetAnyTy(); auto optionFuncRetTy = typeManager.GetEnumTy(*mockUtils->optionDecl, { handlerRetTy }); auto arrayTy = typeManager.GetStructTy(*mockUtils->arrayDecl, { typeManager.GetAnyTy() }); auto toStrArrayTy = typeManager.GetStructTy(*mockUtils->arrayDecl, { mockUtils->toStringDecl->ty }); auto objectTy = typeManager.GetClassTy(*mockUtils->objectDecl, {}); auto funcTy = isMethod ? typeManager.GetFunctionTy({objectTy, arrayTy, toStrArrayTy}, optionFuncRetTy) : typeManager.GetFunctionTy({arrayTy, toStrArrayTy}, optionFuncRetTy); auto funcParamObj = CreateFuncParam(OBJECT_PARAM_NAME_FOR_HANDLER_LAMBDA, nullptr, nullptr, objectTy); auto funcParamObjRef = MakeOwned(); funcParamObjRef->ref = Reference(OBJECT_PARAM_NAME_FOR_HANDLER_LAMBDA); funcParamObjRef->ref.target = funcParamObj.get(); funcParamObjRef->ty = funcParamObj->ty; funcParamObjRef->curFile = injectTo.curFile; auto funcParam = CreateFuncParam(PARAMS_PARAM_NAME_FOR_HANDLER_LAMBDA, nullptr, nullptr, arrayTy); auto funcParamRef = MakeOwned(); funcParamRef->ref = Reference(PARAMS_PARAM_NAME_FOR_HANDLER_LAMBDA); funcParamRef->ref.target = funcParam.get(); funcParamRef->ty = funcParam->ty; funcParamRef->curFile = injectTo.curFile; auto funcTypeParam = CreateFuncParam(TYPE_PARAMS_PARAM_NAME_FOR_HANDLER_LAMBDA, nullptr, nullptr, toStrArrayTy); auto funcTypeParamRef = MakeOwned(); funcTypeParamRef->ref = Reference(TYPE_PARAMS_PARAM_NAME_FOR_HANDLER_LAMBDA); funcTypeParamRef->ref.target = funcTypeParam.get(); funcTypeParamRef->ty = funcTypeParam->ty; funcTypeParamRef->curFile = injectTo.curFile; std::vector> handlerCallParams {}; if (isMethod) { handlerCallParams.emplace_back(std::move(funcParamObj)); } handlerCallParams.emplace_back(std::move(funcParam)); handlerCallParams.emplace_back(std::move(funcTypeParam)); std::vector> handlerCallParamLists {}; handlerCallParamLists.emplace_back(CreateFuncParamList(std::move(handlerCallParams))); OwnedPtr handler = nullptr; if (isMethod) { handler = GetMockedObjectHandler(std::move(funcParamObjRef), injectTo.curFile); } else { handler = GetCurrentStaticHandler(injectTo.curFile); } auto onCallDecl = declForInfo ? declForInfo.get() : &decl; auto onCallInvocation = CreateOnCallInvocation( std::move(funcParamRef), std::move(funcTypeParamRef), *onCallDecl, nullptr, std::move(handler)); auto matchExpr = CreateMatchExpr( std::move(onCallInvocation), GenerateCallHandlerCases(decl, injectTo), optionFuncRetTy); std::vector> handlerBody {}; auto handlerBodyReturn = CreateReturnExpr(std::move(matchExpr), nullptr); handlerBodyReturn->ty = NOTHING_TY; handlerBody.emplace_back(std::move(handlerBodyReturn)); return CreateLambdaExpr( CreateFuncBody( std::move(handlerCallParamLists), MockUtils::CreateType(optionFuncRetTy), CreateBlock(std::move(handlerBody), NOTHING_TY), funcTy) ); } std::tuple, Ptr> MockManager::FindDefaultAccessorInterfaceAndFunction( Ptr original, Ptr baseFunc) { auto interfaceDecl = DynamicCast(original->outerDecl); if (!interfaceDecl) { return {nullptr, nullptr}; } if (!original->TestAttr(Attribute::DEFAULT)) { return {nullptr, nullptr}; } Ptr accessorInterfaceDecl = MockUtils::FindGlobalDecl( interfaceDecl->curFile, interfaceDecl->identifier + MockUtils::defaultAccessorSuffix); if (!accessorInterfaceDecl) { return {nullptr, nullptr}; } auto extendDecl = typeManager.GetExtendDeclByInterface(*baseFunc->baseExpr->ty, *accessorInterfaceDecl->ty); if (!extendDecl) { return {nullptr, nullptr}; } Ptr accessorImplDecl = MockUtils::FindMemberDecl( *extendDecl.value(), mockUtils->Mangle(*original) + MockUtils::defaultAccessorSuffix); return {accessorInterfaceDecl, accessorImplDecl}; } void MockManager::GenerateCallHandlerForMethodWithDefault(CallExpr& callExpr) { auto maExpr = DynamicCast(callExpr.baseFunc.get()); CJC_ASSERT(maExpr); auto funcDecl = callExpr.resolvedFunction; CJC_ASSERT(funcDecl); auto [accessorInterfaceDecl, accessorDecl] = FindDefaultAccessorInterfaceAndFunction(funcDecl, maExpr); if (!accessorInterfaceDecl) { if (funcDecl->TestAttr(Attribute::STATIC) || (funcDecl->outerDecl && funcDecl->outerDecl->astKind == ASTKind::EXTEND_DECL)) { GenerateCallHandlerForStaticDecl(*funcDecl, callExpr); } return; } CJC_ASSERT(accessorDecl); auto accessorMa = CreateMemberAccess(ASTCloner::Clone(maExpr->baseExpr.get()), *accessorDecl); accessorMa->ty = typeManager.GetInstantiatedTy( accessorDecl->ty, GenerateTypeMapping(*accessorDecl, maExpr->instTys)); accessorMa->instTys = maExpr->instTys; auto call = ASTCloner::Clone(Ptr(&callExpr)); call->baseFunc = std::move(accessorMa); call->resolvedFunction = accessorDecl; OwnedPtr selector; if (callExpr.resolvedFunction->TestAttr(Attribute::STATIC)) { GenerateCallHandlerForStaticDecl(*accessorDecl, *call); } else { GenerateCallHandlerForStaticDecl(*accessorDecl, *call, callExpr.resolvedFunction); } callExpr.desugarExpr = std::move(call); return; } void MockManager::GenerateCallHandlerForStaticDecl(FuncDecl& decl, Expr& injectTo, Ptr declForInfo) { static const auto UNIT_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); if (!callHandlerDecl) { // no unittest dependency, then don't try to generate call handler return; } auto isMethod = !decl.IsStaticOrGlobal(); auto handlerRetTy = typeManager.GetAnyTy(); auto optionFuncRetTy = typeManager.GetEnumTy(*mockUtils->optionDecl, { handlerRetTy }); auto arrayTy = typeManager.GetStructTy(*mockUtils->arrayDecl, { typeManager.GetAnyTy() }); auto toStrArrayTy = typeManager.GetStructTy(*mockUtils->arrayDecl, { mockUtils->toStringDecl->ty }); auto objectTy = typeManager.GetClassTy(*mockUtils->objectDecl, {}); auto funcTy = isMethod ? typeManager.GetFunctionTy({objectTy, arrayTy, toStrArrayTy}, optionFuncRetTy) : typeManager.GetFunctionTy({arrayTy, toStrArrayTy}, optionFuncRetTy); auto optionFuncTy = typeManager.GetEnumTy(*mockUtils->optionDecl, { funcTy }); auto optionFunc = mockUtils->GetInstantiatedDecl( optionFuncTy->decl, {optionFuncTy}, IS_GENERIC_INSTANTIATION_ENABLED); auto handlerSomeDecl = Sema::Desugar::AfterTypeCheck::LookupEnumMember(optionFunc, OPTION_VALUE_CTOR); auto handlerSomeRef = CreateRefExpr(*handlerSomeDecl); handlerSomeRef->ty = typeManager.GetFunctionTy({funcTy}, optionFuncTy); std::vector> handlerSomeCallArgs {}; handlerSomeCallArgs.emplace_back(CreateFuncArg(GenerateCallHandlerLambda(decl, injectTo, declForInfo))); auto handlerSomeCall = CreateCallExpr(std::move(handlerSomeRef), std::move(handlerSomeCallArgs)); handlerSomeCall->resolvedFunction = As(handlerSomeDecl); handlerSomeCall->callKind = CallKind::CALL_DECLARED_FUNCTION; handlerSomeCall->ty = optionFuncTy; auto genericDecl = mockUtils->GetGenericDecl(Ptr(&decl)); auto mangledName = mockUtils->Mangle(*genericDecl); Ptr handlerDecl = As( MockUtils::FindMockGlobalDecl(*genericDecl, mangledName)); CJC_NULLPTR_CHECK(handlerDecl); auto handlerRef = CreateRefExpr(*handlerDecl); handlerRef->curFile = injectTo.curFile; auto handlerAssignExpr = CreateAssignExpr(std::move(handlerRef), std::move(handlerSomeCall), UNIT_TY); handlerAssignExpr->curFile = injectTo.curFile; handlerAssignExpr->EnableAttr(Attribute::IMPLICIT_ADD); std::vector> replacedExprs {}; replacedExprs.emplace_back(std::move(handlerAssignExpr)); replacedExprs.emplace_back(ASTCloner::Clone(Ptr(&injectTo))); injectTo.desugarExpr = CreateBlock(std::move(replacedExprs), injectTo.ty); mockUtils->Instantiate(*injectTo.desugarExpr); } void MockManager::HandleMockAnnotatedLambdaValue(Expr& expr) { if (auto callExpr = As(&expr); callExpr) { HandleMockAnnotatedLambdaWithCall(*callExpr); } else if (auto ma = As(&expr); ma) { HandleMockAnnotatedLambdaWithMemberAccess(*ma, *ma); } else if (auto refExpr = As(&expr); refExpr) { HandleMockAnnotatedLambdaWithRefExpr(*refExpr, *refExpr); } else if (auto assignExpr = As(&expr)) { HandleMockAnnotatedLambdaWithAssignExpr(*assignExpr); } } void MockManager::HandleMockAnnotatedLambdaWithCall(CallExpr& callExpr) { auto target = callExpr.resolvedFunction; bool isInInterfaceWithDefault = target->outerDecl && target->outerDecl->astKind == ASTKind::INTERFACE_DECL && target->TestAttr(Attribute::DEFAULT); CJC_ASSERT(isInInterfaceWithDefault || target->TestAttr(Attribute::IN_EXTEND) || target->IsStaticOrGlobal()); Ptr funcDecl = mockUtils->GetInstantiatedDecl( target, StaticCast(callExpr.baseFunc.get())->instTys, IS_GENERIC_INSTANTIATION_ENABLED); if (isInInterfaceWithDefault) { GenerateCallHandlerForMethodWithDefault(callExpr); return; } GenerateCallHandlerForStaticDecl(*funcDecl, callExpr); } void MockManager::HandleMockAnnotatedLambdaWithMemberAccess(MemberAccess& ma, Expr& injectTo) { Ptr getterDecl; auto target = StaticCast(ma.target); CJC_ASSERT(target->IsStaticOrGlobal()); if (auto propDecl = As(target); propDecl) { getterDecl = GetUsableGetterForProperty(*propDecl); } else { getterDecl = mockUtils->FindAccessor(&ma, target, AccessorKind::FIELD_GETTER); } GenerateCallHandlerForStaticDecl(*getterDecl, injectTo); if (target->isVar) { Ptr setter; if (auto propDecl = As(ma.target); propDecl) { setter = GetUsableSetterForProperty(*As(ma.target)); } else { setter = mockUtils->FindAccessor(&ma, ma.target, AccessorKind::FIELD_SETTER); } GenerateCallHandlerForStaticDecl(*setter, injectTo); } } void MockManager::HandleMockAnnotatedLambdaWithRefExpr(const RefExpr& refExpr, Expr& injectTo) { Ptr getterDecl; auto target = As(refExpr.GetTarget()); CJC_NULLPTR_CHECK(target); CJC_ASSERT(target->IsStaticOrGlobal()); getterDecl = mockUtils->FindAccessor(nullptr, target, AccessorKind::TOP_LEVEL_VARIABLE_GETTER); CJC_NULLPTR_CHECK(getterDecl); GenerateCallHandlerForStaticDecl(*getterDecl, injectTo); if (target->isVar) { Ptr setter = mockUtils->FindAccessor(nullptr, target, AccessorKind::TOP_LEVEL_VARIABLE_SETTER); CJC_NULLPTR_CHECK(setter); GenerateCallHandlerForStaticDecl(*setter, injectTo); } } void MockManager::HandleMockAnnotatedLambdaWithAssignExpr(AssignExpr& assignExpr) { if (auto refExpr = As(assignExpr.leftValue)) { HandleMockAnnotatedLambdaWithRefExpr(*refExpr, assignExpr); } else if (auto maExpr = As(assignExpr.leftValue)) { HandleMockAnnotatedLambdaWithMemberAccess(*maExpr, assignExpr); } } } cangjie_compiler-1.0.7/src/Sema/Test/MockManager.h000066400000000000000000000205451510705540100217450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * MockManager is the global manager to generate the mocked version of a class or interface * which is passed to createMock/createSpy calls. */ #ifndef CANGJIE_SEMA_MOCK_MANAGER_H #define CANGJIE_SEMA_MOCK_MANAGER_H #include "cangjie/Sema/TypeManager.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Mangle/BaseMangler.h" #include "MockUtils.h" namespace Cangjie { enum class MockKind : uint8_t; class MockManager { public: explicit MockManager(ImportManager& importManager, TypeManager& typeManager, const Ptr mockUtils); struct GeneratedClassResult { Ptr classDecl; bool generated; }; GeneratedClassResult GenerateMockClassIfNeededAndGet( AST::ClassLikeDecl& originalDecl, AST::Package& curPkg, MockKind mockKind); void WriteGeneratedClasses(); void HandleMockAnnotatedLambdaValue(AST::Expr& expr); void WrapWithRequireMockObject(AST::Expr& receiverExpr); static OwnedPtr CreateIllegalMockCallException( AST::File& curFile, TypeManager& typeMgr, ImportManager& importMgr); static OwnedPtr CreateInitCallOfMockClass( AST::ClassDecl& mockClass, std::vector>& mockCallArgs, TypeManager& typeManager, const std::vector> instTys, const std::vector> valueParamTys); static bool IsMockCall(const AST::CallExpr& ce); static bool IsMockClass(const AST::Decl& decl); static MockKind GetMockKind(const AST::CallExpr& ce); static Ptr FindMockedClassOf(const AST::ClassLikeDecl& decl); private: static OwnedPtr CreateFieldDecl( AST::ClassLikeDecl& decl, const std::string& identifier, const Ptr ty, const AST::Package& curPkg ); TypeManager& typeManager; ImportManager& importManager; Ptr mockUtils; std::map> mockedClassDecls; std::map, int> instantiationCounters; std::map, bool> defaultForTypePresence; Ptr objectDecl; Ptr callHandlerDecl; Ptr mockedInterfaceDecl; Ptr parameterInfoDecl; Ptr declIdDecl; Ptr funcInfoDecl; Ptr callDecl; Ptr onCallEnumDecl; Ptr declKindEnumDecl; Ptr hasDefaultValueForStubDecl; Ptr noDefaultValueForMockException; Ptr mockReturnValueTypeMismatchException; Ptr requireMockObject; Ptr mockZeroValueDecl; int ComputeInstantiationNumberToMangleMockedDecl(const AST::ClassLikeDecl& declToMock); void FindOverridesInSuperDecl( AST::ClassLikeDecl& superDecl, const AST::Decl& member, std::set>& foundOverrides ) const; void AddObjectSuperTypeIfNeeded( const AST::ClassLikeDecl& originalDecl, AST::ClassDecl& mockedDecl ) const; void AddMockedMembers(AST::ClassLikeDecl& originalDecl, AST::ClassDecl& mockedDecl, std::vector& classGenericSubsts); void AddMockedInterface(AST::ClassDecl& mockedDecl, AST::VarDecl& handlerFieldDecl); OwnedPtr CreateTypeCastForOnCallReturnValue( OwnedPtr exprToCast, const Ptr castTy ) const; OwnedPtr CreateMemberAssignment( AST::VarDecl& member, OwnedPtr rhsExpr ) const; OwnedPtr CreateOnCallReturnZeroMatchCase( const AST::FuncDecl& originalFunc, const Ptr zeroValueTy, AST::Decl& enumConstructor) const; OwnedPtr CreateOnCallReturnMatchCase( const AST::FuncDecl& originalFunc, const Ptr retTy, AST::Decl& enumConstructor) const; OwnedPtr CreateOnCallThrowMatchCase( const AST::FuncDecl& originalFunc, AST::Decl& enumConstructor) const; OwnedPtr CreateOnCallCallBaseMatchCase( AST::FuncDecl& originalFunc, const AST::FuncDecl& mockedFunc, AST::Decl& enumConstructor) const; OwnedPtr CreateOnCallReturnDefaultMatchCase( const AST::FuncDecl& originalFunc, const Ptr retTy, AST::Decl& enumConstructor); Ptr FindDefaultValueForStubMethod(const Ptr retTy) const; OwnedPtr CreateOnCallResultMatchCase( AST::FuncDecl& originalFunc, const Ptr mockedFunc, AST::Decl& enumConstructor); OwnedPtr CreateEmptyConstructorDecl( AST::ClassDecl& mockedClass, std::vector> params, AST::File& curFile) const; OwnedPtr CreateConstructorDecl( AST::ClassDecl& mockedClass, std::set membersToAssign, MockKind mockKind, AST::File& curFile ) const; void AddAssignmentsForSuperFields(const AST::FuncDecl& constructorOfMockedDecl) const; OwnedPtr CreateDeclId(const AST::Decl& decl, AST::File& curFile) const; OwnedPtr CreateParamInfo(const AST::FuncParam& param, int position, AST::File& curFile ) const; OwnedPtr CreateParamsInfo(const AST::FuncDecl& decl, AST::File& curFile) const; OwnedPtr CreateTypeParamsInfo(const AST::FuncDecl& decl, AST::File& curFile) const; OwnedPtr CreateFuncInfo(AST::FuncDecl& funcDecl, AST::File& curFile); OwnedPtr CreateDeclKind(const AST::FuncDecl& decl) const; OwnedPtr CreateCallInfo( AST::FuncDecl& originalFunction, OwnedPtr mockedArgsArray, OwnedPtr typeArgsArray, const Ptr curFile, OwnedPtr objRef ); OwnedPtr CreateOnCallInvocation( OwnedPtr mockedArgsArray, OwnedPtr typeArgsArray, AST::FuncDecl& originalFunc, OwnedPtr objRef, OwnedPtr handler ); OwnedPtr CreateStaticMethodStub( AST::FuncDecl& originalMethod, AST::ClassDecl& mockedDecl, std::vector& classGenericSubsts); OwnedPtr CreateMockedMethod( AST::FuncDecl& originalMethod, AST::ClassDecl& mockedDecl, std::vector& classGenericSubsts); OwnedPtr CreateMockedMethodWithoutBody( AST::FuncDecl& originalMethod, AST::ClassDecl& mockedDecl, std::vector& classGenericSubsts); OwnedPtr CreateMockedProp( AST::PropDecl& originalProp, AST::ClassDecl& mockedDecl, std::vector& classGenericSubsts); void AddMockedMemberIfNeeded( AST::ClassDecl& mockedDecl, AST::Decl& member, std::vector& classGenericSubsts); OwnedPtr GetHandlerRefFromClass(const Ptr decl); void GenerateCallHandlerForStaticDecl( AST::FuncDecl& decl, AST::Expr& injectTo, Ptr declForInfo = nullptr); void GenerateCallHandlerForMethodWithDefault(AST::CallExpr& callExpr); void HandleMockAnnotatedLambdaWithCall(AST::CallExpr& callExpr); void HandleMockAnnotatedLambdaWithMemberAccess(AST::MemberAccess& ma, AST::Expr& injectTo); void HandleMockAnnotatedLambdaWithRefExpr(const AST::RefExpr& refExpr, AST::Expr& injectTo); void HandleMockAnnotatedLambdaWithAssignExpr(AST::AssignExpr& assignExpr); OwnedPtr GetCurrentStaticHandler(const Ptr curFile); OwnedPtr GetMockedObjectHandler(OwnedPtr objRef, const Ptr curFile); OwnedPtr GenerateCallHandlerLambda( AST::FuncDecl& decl, const AST::Expr& injectTo, Ptr declForInfo = nullptr); std::vector> GenerateCallHandlerCases( AST::FuncDecl& staticDecl, const AST::Expr& injectTo); std::tuple, Ptr> FindDefaultAccessorInterfaceAndFunction( Ptr original, Ptr baseFunc); }; } // namespace Cangjie #endif // CANGJIE_SEMA_MOCK_MANAGER_H cangjie_compiler-1.0.7/src/Sema/Test/MockSupportManager.cpp000066400000000000000000002460611510705540100237000ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "MockSupportManager.h" #include "TypeCheckUtil.h" #include "Desugar/AfterTypeCheck.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/Sema/TestManager.h" namespace Cangjie { using namespace AST; using namespace TypeCheckUtil; using namespace Cangjie::Sema::Desugar::AfterTypeCheck; OwnedPtr CreateThisRef(ClassLikeDecl& targetClass) { auto thisRef = CreateRefExpr(SrcIdentifier{"this"}, targetClass.ty); thisRef->ref.target = &targetClass; return thisRef; } bool IsFieldOrVariable(AccessorKind kind) { return kind == AccessorKind::FIELD_GETTER || kind == AccessorKind::FIELD_SETTER || kind == AccessorKind::STATIC_FIELD_GETTER || kind == AccessorKind::STATIC_FIELD_SETTER || kind == AccessorKind::TOP_LEVEL_VARIABLE_GETTER || kind == AccessorKind::TOP_LEVEL_VARIABLE_SETTER; } bool IsStaticField(AccessorKind kind) { return kind == AccessorKind::STATIC_FIELD_GETTER || kind == AccessorKind::STATIC_FIELD_SETTER; } bool IsTopLevelField(AccessorKind kind) { return kind == AccessorKind::TOP_LEVEL_VARIABLE_GETTER || kind == AccessorKind::TOP_LEVEL_VARIABLE_SETTER; } MockSupportManager::MockSupportManager(TypeManager& typeManager, const Ptr mockUtils) : typeManager(typeManager), mockUtils(mockUtils) {} Ptr ExtractLastDesugaredExpr(Expr& expr) { auto lastDesugaredExpr = &expr; while (lastDesugaredExpr->desugarExpr != nullptr) { lastDesugaredExpr = lastDesugaredExpr->desugarExpr.get(); } return lastDesugaredExpr; } bool MockSupportManager::IsDeclOpenToMock(const Decl& decl) { return decl.TestAttr(Attribute::OPEN) && decl.TestAttr(Attribute::OPEN_TO_MOCK); } bool MockSupportManager::DoesClassLikeSupportMocking(ClassLikeDecl& classLikeToCheck) { if (Is(classLikeToCheck) || classLikeToCheck.TestAttr(Attribute::MOCK_SUPPORTED)) { return true; } if (!classLikeToCheck.TestAttr(Attribute::OPEN) && !classLikeToCheck.TestAttr(Attribute::ABSTRACT)) { return false; } for (auto& superDecl : classLikeToCheck.GetAllSuperDecls()) { for (auto& member : superDecl->GetMemberDecls()) { if (member->TestAttr(Attribute::CONSTRUCTOR)) { continue; } if (!member->TestAttr(Attribute::OPEN) && !member->TestAttr(Attribute::ABSTRACT)) { return false; } } } return true; } void MockSupportManager::MakeOpenToMockIfNeeded(Decl& decl) { if (!decl.TestAttr(Attribute::OPEN) && !decl.TestAttr(Attribute::ABSTRACT)) { decl.EnableAttr(Attribute::OPEN); decl.EnableAttr(Attribute::OPEN_TO_MOCK); } } namespace { bool IsMemberOfGenericDecl(const Decl& decl) { return decl.outerDecl && (decl.outerDecl->TestAttr(Attribute::GENERIC) || decl.outerDecl->genericDecl); } void MarkFuncMockSupportedIfNeeded(FuncDecl& decl) { if (decl.IsStaticOrGlobal() && decl.TestAttr(AST::Attribute::GENERIC)) { if (!IsMemberOfGenericDecl(decl)) { decl.EnableAttr(Attribute::MOCK_SUPPORTED); } } else if (decl.TestAttr(AST::Attribute::IN_EXTEND) && decl.TestAttr(AST::Attribute::GENERIC)) { if (!IsMemberOfGenericDecl(decl)) { decl.EnableAttr(Attribute::MOCK_SUPPORTED); } } } } // namespace void MockSupportManager::MarkNodeMockSupportedIfNeeded(Node& node) { auto decl = As(&node); if (!decl) { return; } if (Is(decl)) { decl->EnableAttr(Attribute::MOCK_SUPPORTED); MakeOpenToMockIfNeeded(*decl); } else if ((Is(decl) || Is(decl)) && !MockUtils::IsMockAccessorRequired(*decl) && !decl->IsStaticOrGlobal() && !decl->TestAttr(Attribute::CONSTRUCTOR) ) { decl->EnableAttr(Attribute::MOCK_SUPPORTED); MakeOpenToMockIfNeeded(*decl); if (auto propMember = As(decl); propMember) { decl->EnableAttr(Attribute::MOCK_SUPPORTED); MakeOpenToMockIfNeeded(*GetUsableGetterForProperty(*propMember)); if (propMember->isVar) { decl->EnableAttr(Attribute::MOCK_SUPPORTED); MakeOpenToMockIfNeeded(*GetUsableSetterForProperty(*propMember)); } } } else if (auto funcDecl = As(decl)) { MarkFuncMockSupportedIfNeeded(*funcDecl); } } void MockSupportManager::MarkMockAccessorWithAttributes(Decl& decl, AccessLevel accessLevel) { decl.DisableAttr(Attribute::GENERIC_INSTANTIATED); decl.EnableAttr(Attribute::OPEN); decl.DisableAttr(Attribute::PRIVATE, Attribute::PROTECTED, Attribute::PUBLIC, Attribute::INTERNAL); decl.EnableAttr(GetAttrByAccessLevel(accessLevel)); decl.EnableAttr(Attribute::GENERATED_TO_MOCK); decl.EnableAttr(Attribute::COMPILER_ADD); decl.EnableAttr(Attribute::IN_CLASSLIKE); } void MockSupportManager::PrepareDecls(DeclsToPrepare&& decls) { for (auto decl : decls.interfacesWithDefaults) { PrepareInterfaceDecl(*decl); } for (auto decl : decls.properties) { PrepareStaticDecl(*GetUsableGetterForProperty(*decl)); if (decl->isVar) { PrepareStaticDecl(*GetUsableSetterForProperty(*decl)); } decl->EnableAttr(Attribute::MOCK_SUPPORTED); } for (auto [classDecl, interfaceDecl] : decls.classWithInterfaceDefaults) { PrepareClassWithDefaults(*classDecl, *interfaceDecl); } for (auto decl : decls.functions) { if (decl->TestAttr(Attribute::FOREIGN)) { auto wrapperDecl = CreateForeignFunctionAccessorDecl(*decl); PrepareStaticDecl(*wrapperDecl); generatedMockDecls.emplace(std::move(wrapperDecl)); } else { if (decl->outerDecl && (decl->outerDecl->TestAttr(Attribute::GENERIC) || decl->outerDecl->genericDecl)) { continue; } PrepareStaticDecl(*decl); if (auto instantiatedDecls = mockUtils->TryGetInstantiatedDecls(*decl)) { for (auto& iDecl : *instantiatedDecls) { PrepareStaticDecl(*iDecl); } } } } } namespace { bool HasDefaults(Ptr decl) { for (auto& memberDecl : decl->GetMemberDecls()) { auto funcDecl = As(memberDecl); if (!funcDecl || !funcDecl->TestAttr(Attribute::DEFAULT)) { continue; } return true; } return false; } std::vector> FindInterfacesWithDefaults(Ptr decl) { std::vector> res; for (auto& inherited : decl->inheritedTypes) { auto typeDecl = Ty::GetDeclOfTy(inherited->ty); auto interfaceDecl = As(typeDecl); if (!interfaceDecl) { continue; } if (HasDefaults(interfaceDecl)) { res.push_back(interfaceDecl); } } return res; } } // namespace void MockSupportManager::CollectDeclsToPrepare(Decl& decl, DeclsToPrepare& decls) { for (auto& member : decl.GetMemberDecls()) { if (auto propDecl = As(member); propDecl) { decls.properties.emplace_back(propDecl); } else { CollectDeclsToPrepare(*member, decls); } } if (auto interfaceDecl = As(&decl)) { if (HasDefaults(interfaceDecl)) { decls.interfacesWithDefaults.emplace_back(interfaceDecl); } return; } if (decl.astKind == ASTKind::CLASS_DECL || decl.astKind == ASTKind::EXTEND_DECL) { Ptr classDecl = DynamicCast(&decl); if (!classDecl) { auto extendDecl = As(&decl); CJC_ASSERT(extendDecl); classDecl = As(Ty::GetDeclOfTy(extendDecl->extendedType->ty)); } if (!classDecl) { return; } } if (auto classDecl = As(&decl)) { for (auto interfaceDecl : FindInterfacesWithDefaults(classDecl)) { decls.classWithInterfaceDefaults.emplace_back(classDecl, interfaceDecl); } return; } if (auto extendDecl = As(&decl)) { auto classDecl = As(Ty::GetDeclOfTy(extendDecl->extendedType->ty)); if (!classDecl) { return; } for (auto interfaceDecl : FindInterfacesWithDefaults(extendDecl)) { decls.classWithInterfaceDefaults.emplace_back(classDecl, interfaceDecl); } return; } auto funcDecl = As(&decl); if (!funcDecl) { return; } if (!decl.IsStaticOrGlobal() && !funcDecl->TestAttr(Attribute::IN_EXTEND)) { return; } if (decl.TestAttr(Attribute::PRIVATE) || decl.TestAttr(Attribute::MAIN_ENTRY)) { return; } decls.functions.emplace_back(funcDecl); } std::vector> MockSupportManager::GenerateHandlerMatchCases( const FuncDecl& funcDecl, OwnedPtr optionFuncTyPattern, OwnedPtr handlerCallExpr) { static const auto NOTHING_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_NOTHING); static const auto UNIT_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); auto arrayLitOfGetTypeCalls = mockUtils->WrapCallTypeArgsIntoArray(funcDecl); auto handlerRetTy = typeManager.GetAnyTy(); auto optionFuncRetTy = typeManager.GetEnumTy(*mockUtils->optionDecl, { handlerRetTy }); std::vector> handlerResultCases; auto handlerResultPattern = MakeOwned(); auto handlerResultPatternConstructor = LookupEnumMember( mockUtils->GetInstantiatedDecl(optionFuncRetTy->decl, {handlerRetTy}, IS_GENERIC_INSTANTIATION_ENABLED), OPTION_VALUE_CTOR); handlerResultPattern->ty = RawStaticCast(handlerResultPatternConstructor->ty)->retTy; handlerResultPattern->constructor = mockUtils->CreateRefExprWithInstTys( *handlerResultPatternConstructor, {handlerRetTy}, OPTION_VALUE_CTOR, *(funcDecl.curFile)); auto handlerResultVarPattern = CreateVarPattern(V_COMPILER, handlerRetTy); auto handlerResultRef = CreateRefExpr(*(handlerResultVarPattern->varDecl)); handlerResultRef->ty = handlerRetTy; handlerResultPattern->patterns.emplace_back(std::move(handlerResultVarPattern)); auto castTy = RawStaticCast(funcDecl.ty)->retTy; auto castType = MockUtils::CreateType(castTy); auto varPatternForTypeCast = CreateVarPattern(V_COMPILER, castTy); auto varPatternForTypeCastRef = CreateRefExpr(*(varPatternForTypeCast->varDecl)); varPatternForTypeCastRef->ty = castTy; varPatternForTypeCastRef->instTys.emplace_back(castTy); std::vector> matchCasesTypeCast; auto retExprWithCastedType = CreateReturnExpr(std::move(varPatternForTypeCastRef)); retExprWithCastedType->ty = NOTHING_TY; auto typePattern = CreateTypePattern(std::move(varPatternForTypeCast), std::move(castType), *handlerResultRef); typePattern->curFile = funcDecl.curFile; auto typeCastMatchCase = CreateMatchCase(std::move(typePattern), std::move(retExprWithCastedType)); auto zeroValueRet = CreateReturnExpr(mockUtils->CreateZeroValue(castTy, *funcDecl.curFile)); zeroValueRet->ty = NOTHING_TY; if (!castTy->IsNothing()) { // There is no valid cast from Any to Nothing matchCasesTypeCast.emplace_back(std::move(typeCastMatchCase)); } matchCasesTypeCast.emplace_back(CreateMatchCase(MakeOwned(), std::move(zeroValueRet))); auto retExpr = CreateMatchExpr(std::move(handlerResultRef), std::move(matchCasesTypeCast), NOTHING_TY); handlerResultCases.emplace_back(CreateMatchCase(std::move(handlerResultPattern), std::move(retExpr))); handlerResultCases.emplace_back(CreateMatchCase(MakeOwned(), CreateUnitExpr(UNIT_TY))); std::vector> handlerCases; handlerCases.emplace_back( CreateMatchCase( std::move(optionFuncTyPattern), CreateMatchExpr(std::move(handlerCallExpr), std::move(handlerResultCases), UNIT_TY))); handlerCases.emplace_back(CreateMatchCase(MakeOwned(), CreateUnitExpr(UNIT_TY))); return handlerCases; } void MockSupportManager::PrepareStaticDecl(Decl& decl) { static const auto UNIT_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); auto funcDecl = As(&decl); if (!funcDecl) { return; } // Do not generate mock var for $test.entry function // Because it breaks cjvm if (funcDecl->identifier == TEST_ENTRY_NAME) { return; } auto body = funcDecl->funcBody->body.get(); if (!body) { return; } auto isMethod = !decl.IsStaticOrGlobal(); auto handlerRetTy = typeManager.GetAnyTy(); auto optionFuncRetTy = typeManager.GetEnumTy(*mockUtils->optionDecl, { handlerRetTy }); auto arrayTy = typeManager.GetStructTy(*mockUtils->arrayDecl, { typeManager.GetAnyTy() }); auto toStrArrayTy = typeManager.GetStructTy(*mockUtils->arrayDecl, { mockUtils->toStringDecl->ty }); auto objectTy = typeManager.GetClassTy(*mockUtils->objectDecl, {}); auto funcTy = isMethod ? typeManager.GetFunctionTy({objectTy, arrayTy, toStrArrayTy}, optionFuncRetTy) : typeManager.GetFunctionTy({arrayTy, toStrArrayTy}, optionFuncRetTy); auto optionFuncTy = typeManager.GetEnumTy(*mockUtils->optionDecl, { funcTy }); auto optionFunc = mockUtils->GetInstantiatedDecl(optionFuncTy->decl, {funcTy}, IS_GENERIC_INSTANTIATION_ENABLED); auto noneFuncTy = CreateRefExpr(*LookupEnumMember(optionFunc, OPTION_NONE_CTOR)); noneFuncTy->ty = optionFuncTy; Ptr varDecl = nullptr; if (funcDecl->genericDecl) { auto& genericDecl = *funcDecl->genericDecl; if (auto it = genericMockVarsDecls.find(&genericDecl); it != genericMockVarsDecls.end()) { // The var was already generated, but not yet written to file varDecl = it->second; } else { varDecl = As( mockUtils->FindMockGlobalDecl(genericDecl, mockUtils->Mangle(genericDecl))); } CJC_ASSERT(varDecl); } else { auto varMangledName = mockUtils->Mangle(decl); auto newVarDecl = CreateVarDecl(varMangledName + MockUtils::mockAccessorSuffix, std::move(noneFuncTy), nullptr); newVarDecl->curFile = decl.curFile; newVarDecl->isVar = true; newVarDecl->EnableAttr(Attribute::PUBLIC); newVarDecl->EnableAttr(Attribute::GLOBAL); newVarDecl->fullPackageName = decl.fullPackageName; varDecl = newVarDecl.get(); generatedMockDecls.insert(std::move(newVarDecl)); genericMockVarsDecls.emplace(&decl, varDecl); } if (IS_GENERIC_INSTANTIATION_ENABLED && funcDecl->TestAttr(Attribute::GENERIC)) { return; } auto varDeclRef = CreateRefExpr(*varDecl); varDeclRef->ty = optionFuncTy; auto optionFuncTyPattern = MakeOwned(); auto optionFuncTyPatternConstructor = LookupEnumMember(optionFunc, OPTION_VALUE_CTOR); optionFuncTyPattern->ty = RawStaticCast(optionFuncTyPatternConstructor->ty)->retTy; optionFuncTyPattern->constructor = mockUtils->CreateRefExprWithInstTys( *optionFuncTyPatternConstructor, {funcTy}, OPTION_VALUE_CTOR, *(decl.curFile)); auto optionFuncTyVarPattern = CreateVarPattern(V_COMPILER, funcTy); auto varPatternRef = CreateRefExpr(*optionFuncTyVarPattern->varDecl.get()); varPatternRef->ty = funcTy; optionFuncTyPattern->patterns.emplace_back(std::move(optionFuncTyVarPattern)); std::vector> handlerCallArgs {}; if (isMethod) { handlerCallArgs.emplace_back(CreateFuncArg(CreateThisRef(*objectTy->decl))); } handlerCallArgs.emplace_back(CreateFuncArg(mockUtils->WrapCallArgsIntoArray(*funcDecl))); handlerCallArgs.emplace_back(CreateFuncArg(mockUtils->WrapCallTypeArgsIntoArray(*funcDecl))); auto handlerCallExpr = CreateCallExpr( std::move(varPatternRef), std::move(handlerCallArgs), nullptr, optionFuncRetTy); handlerCallExpr->callKind = CallKind::CALL_FUNCTION_PTR; auto handlerCases = GenerateHandlerMatchCases( *funcDecl, std::move(optionFuncTyPattern), std::move(handlerCallExpr)); auto handlerMatch = CreateMatchExpr(std::move(varDeclRef), std::move(handlerCases), UNIT_TY); handlerMatch->curFile = funcDecl->curFile; mockUtils->Instantiate(*handlerMatch); body->body.push_back(std::move(handlerMatch)); std::rotate(body->body.rbegin(), body->body.rbegin() + 1, body->body.rend()); decl.EnableAttr(Attribute::MOCK_SUPPORTED); } void MockSupportManager::GenerateVarDeclAccessors(VarDecl& fieldDecl, AccessorKind getterKind, AccessorKind setterKind) { generatedMockDecls.insert(GenerateVarDeclAccessor(fieldDecl, getterKind)); if (fieldDecl.isVar) { generatedMockDecls.insert(GenerateVarDeclAccessor(fieldDecl, setterKind)); } fieldDecl.EnableAttr(Attribute::MOCK_SUPPORTED); } void MockSupportManager::GenerateSpyCallMarker(Package& package) { if (package.files.size() == 0) { return; } static const auto BOOL_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); auto type = MockUtils::CreateType(BOOL_TY); type->kind = TypeKind::TYPE_BOOLEAN; type->str = BOOL_TY->String(); auto varDecl = CreateVarDecl( MockUtils::spyCallMarkerVarName + MockUtils::mockAccessorSuffix, CreateLitConstExpr(LitConstKind::BOOL, "false", BOOL_TY, true), std::move(type)); varDecl->curFile = package.files[0].get(); varDecl->isVar = true; varDecl->EnableAttr(Attribute::PUBLIC); varDecl->EnableAttr(Attribute::GLOBAL); varDecl->fullPackageName = package.fullPackageName; varDecl->TestAttr(Attribute::GENERATED_TO_MOCK); generatedMockDecls.insert(std::move(varDecl)); } Ptr MockSupportManager::GenerateSpiedObjectVar(const Decl& decl) { auto declTy = typeManager.GetAnyTy(); auto mangledName = mockUtils->Mangle(decl); auto optionDeclTy = typeManager.GetEnumTy(*mockUtils->optionDecl, { declTy }); auto noneRef = CreateRefExpr( *LookupEnumMember( mockUtils->GetInstantiatedDecl(optionDeclTy->decl, {declTy}, IS_GENERIC_INSTANTIATION_ENABLED), OPTION_NONE_CTOR)); noneRef->ty = optionDeclTy; auto varDecl = CreateVarDecl( MockUtils::spyObjVarName + "$" + mangledName + MockUtils::mockAccessorSuffix, std::move(noneRef)); varDecl->curFile = decl.curFile; varDecl->isVar = true; varDecl->EnableAttr(Attribute::PUBLIC); varDecl->EnableAttr(Attribute::GLOBAL); varDecl->fullPackageName = decl.fullPackageName; varDecl->TestAttr(Attribute::GENERATED_TO_MOCK); auto varRef = varDecl.get(); generatedMockDecls.insert(std::move(varDecl)); return varRef; } void MockSupportManager::GenerateSpyCallHandler(FuncDecl& funcDecl, Decl& spiedObjectDecl) { static const auto BOOL_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); static const auto UNIT_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); if (funcDecl.TestAttr(Attribute::CONSTRUCTOR) || funcDecl.TestAttr(Attribute::STATIC)) { return; } auto body = funcDecl.funcBody->body.get(); if (!body || MockUtils::IsMockAccessor(funcDecl)) { return; } auto spiedObjOptionTy = StaticCast(spiedObjectDecl.ty); auto spiedObjTy = spiedObjOptionTy->typeArgs[0]; auto optionSpiedObjTyPattern = MakeOwned(); auto optionSpiedObjPatternConstructor = LookupEnumMember( mockUtils->GetInstantiatedDecl(spiedObjOptionTy->decl, {spiedObjTy}, IS_GENERIC_INSTANTIATION_ENABLED), OPTION_VALUE_CTOR); optionSpiedObjTyPattern->ty = RawStaticCast(optionSpiedObjPatternConstructor->ty)->retTy; optionSpiedObjTyPattern->constructor = mockUtils->CreateRefExprWithInstTys( *optionSpiedObjPatternConstructor, {spiedObjTy}, OPTION_VALUE_CTOR, *(funcDecl.curFile)); Ptr spyCallMarker = nullptr; for (auto& mockDecl : generatedMockDecls) { if (mockDecl->identifier == MockUtils::spyCallMarkerVarName + MockUtils::mockAccessorSuffix) { spyCallMarker = mockDecl; break; } } if (!spyCallMarker) { return; } std::vector> callBaseArgs; for (auto& param : funcDecl.funcBody->paramLists[0]->params) { callBaseArgs.emplace_back(CreateFuncArg(CreateRefExpr(*param))); } auto castTy = funcDecl.outerDecl->ty; auto castType = MockUtils::CreateType(castTy); auto varPatternForTypeCast = CreateVarPattern(V_COMPILER, castTy); auto varPatternForTypeCastRef = CreateRefExpr(*(varPatternForTypeCast->varDecl)); varPatternForTypeCastRef->ty = castTy; varPatternForTypeCastRef->instTys.emplace_back(castTy); auto memberAccessFuncBaseExpr = CreateMemberAccess(std::move(varPatternForTypeCastRef), funcDecl); memberAccessFuncBaseExpr->EnableAttr(Attribute::GENERATED_TO_MOCK); if (auto& genericInfo = funcDecl.funcBody->generic; genericInfo) { for (auto& typeParam : genericInfo->typeParameters) { memberAccessFuncBaseExpr->instTys.emplace_back(typeParam->ty); } } auto callMockedMember = MakeOwned(); callMockedMember->ty = RawStaticCast(memberAccessFuncBaseExpr->ty)->retTy; callMockedMember->resolvedFunction = RawStaticCast(memberAccessFuncBaseExpr->target); callMockedMember->baseFunc = std::move(memberAccessFuncBaseExpr); callMockedMember->args = std::move(callBaseArgs); callMockedMember->callKind = CallKind::CALL_DECLARED_FUNCTION; callMockedMember->curFile = funcDecl.curFile; callMockedMember->EnableAttr(Attribute::GENERATED_TO_MOCK); std::vector> nodes {}; auto trueLit = CreateLitConstExpr(LitConstKind::BOOL, "true", BOOL_TY); trueLit->curFile = funcDecl.curFile; auto trueSpyCallMarkerAssign = CreateAssignExpr(CreateRefExpr(*spyCallMarker), std::move(trueLit), UNIT_TY); trueSpyCallMarkerAssign->curFile = funcDecl.curFile; trueSpyCallMarkerAssign->EnableAttr(Attribute::GENERATED_TO_MOCK); auto falseLit = CreateLitConstExpr(LitConstKind::BOOL, "false", BOOL_TY); falseLit->curFile = funcDecl.curFile; auto falseSpyCallMarkerAssign = CreateAssignExpr(CreateRefExpr(*spyCallMarker), std::move(falseLit), UNIT_TY); falseSpyCallMarkerAssign->curFile = funcDecl.curFile; falseSpyCallMarkerAssign->EnableAttr(Attribute::GENERATED_TO_MOCK); auto spiedObjVarPattern = CreateVarPattern(V_COMPILER, spiedObjTy); auto spiedObjVarRef = CreateRefExpr(*spiedObjVarPattern->varDecl.get()); optionSpiedObjTyPattern->patterns.emplace_back(std::move(spiedObjVarPattern)); auto callMockedMemberResult = CreateVarDecl( "callBaseResult" + MockUtils::mockAccessorSuffix, std::move(callMockedMember), nullptr); callMockedMemberResult->curFile = funcDecl.curFile; callMockedMemberResult->fullPackageName = funcDecl.fullPackageName; auto callMockedMemberResultRef = CreateRefExpr(*callMockedMemberResult); std::vector> matchCasesTypeCast; auto typePattern = CreateTypePattern(std::move(varPatternForTypeCast), std::move(castType), *spiedObjVarRef); typePattern->curFile = funcDecl.curFile; auto typeCastMatchCase = CreateMatchCase(std::move(typePattern), std::move(trueSpyCallMarkerAssign)); typeCastMatchCase->exprOrDecls->body.emplace_back(std::move(callMockedMemberResult)); typeCastMatchCase->exprOrDecls->body.emplace_back(std::move(falseSpyCallMarkerAssign)); typeCastMatchCase->exprOrDecls->body.emplace_back( CreateReturnExpr(std::move(callMockedMemberResultRef), funcDecl.funcBody)); matchCasesTypeCast.emplace_back(std::move(typeCastMatchCase)); matchCasesTypeCast.emplace_back(CreateMatchCase(MakeOwned(), CreateUnitExpr(UNIT_TY))); auto typeCastMatch = CreateMatchExpr(std::move(spiedObjVarRef), std::move(matchCasesTypeCast), UNIT_TY); std::vector> handlerCases; handlerCases.emplace_back(CreateMatchCase(std::move(optionSpiedObjTyPattern), std::move(typeCastMatch))); handlerCases.emplace_back(CreateMatchCase(MakeOwned(), CreateUnitExpr(UNIT_TY))); auto handlerMatch = CreateMatchExpr(CreateRefExpr(spiedObjectDecl), std::move(handlerCases), UNIT_TY); handlerMatch->curFile = funcDecl.curFile; auto falseLitBackCall = CreateLitConstExpr(LitConstKind::BOOL, "false", BOOL_TY); falseLitBackCall->curFile = funcDecl.curFile; auto falseSpyCallMarkerAssignBackCall = CreateAssignExpr( CreateRefExpr(*spyCallMarker), std::move(falseLitBackCall), UNIT_TY); falseSpyCallMarkerAssignBackCall->curFile = funcDecl.curFile; falseSpyCallMarkerAssignBackCall->EnableAttr(Attribute::GENERATED_TO_MOCK); std::vector> callMarkerCases; OwnedPtr truePattern = MakeOwned(); truePattern->literal = CreateLitConstExpr(LitConstKind::BOOL, "true", BOOL_TY, true); truePattern->ty = BOOL_TY; callMarkerCases.emplace_back(CreateMatchCase( std::move(truePattern), std::move(falseSpyCallMarkerAssignBackCall))); OwnedPtr falsePattern = MakeOwned(); falsePattern->ty = BOOL_TY; falsePattern->literal = CreateLitConstExpr(LitConstKind::BOOL, "false", BOOL_TY, true); callMarkerCases.emplace_back(CreateMatchCase( std::move(falsePattern), std::move(handlerMatch))); auto spyCallMarkerMatch = CreateMatchExpr(CreateRefExpr(*spyCallMarker), std::move(callMarkerCases), UNIT_TY); mockUtils->Instantiate(*spyCallMarkerMatch); body->body.push_back(std::move(spyCallMarkerMatch)); std::rotate(body->body.rbegin(), body->body.rbegin() + 1, body->body.rend()); } void MockSupportManager::PrepareToSpy(Decl& decl) { auto classLikeDecl = As(&decl); if (!classLikeDecl || (!classLikeDecl->TestAttr(Attribute::MOCK_SUPPORTED) && !Is(classLikeDecl))) { return; } auto spiedObjectDecl = GenerateSpiedObjectVar(decl); for (auto& member : classLikeDecl->GetMemberDecls()) { if (auto funcDecl = As(member); funcDecl && !funcDecl->IsFinalizer()) { GenerateSpyCallHandler(*funcDecl, *spiedObjectDecl); } } } void MockSupportManager::GenerateAccessors(Decl& decl) { if (auto varDecl = As(&decl); varDecl && varDecl->TestAttr(Attribute::GLOBAL)) { GenerateVarDeclAccessors( *varDecl, AccessorKind::TOP_LEVEL_VARIABLE_GETTER, AccessorKind::TOP_LEVEL_VARIABLE_SETTER); return; } auto classDecl = As(&decl); if (!classDecl) { return; } for (auto& member : classDecl->GetMemberDecls()) { if (member->TestAttr(Attribute::CONSTRUCTOR)) { continue; } if (member->TestAttr(Attribute::STATIC)) { if (auto fieldDecl = As(member.get()); fieldDecl && !Is(member.get())) { GenerateVarDeclAccessors( *fieldDecl, AccessorKind::STATIC_FIELD_GETTER, AccessorKind::STATIC_FIELD_SETTER); } continue; } if (auto funcDecl = As(member); funcDecl && funcDecl->IsFinalizer()) { continue; } if (!MockUtils::IsMockAccessorRequired(*member)) { continue; } if (auto propDecl = As(member.get()); propDecl) { generatedMockDecls.insert(GeneratePropAccessor(*propDecl)); } else if (auto methodDecl = As(member.get()); methodDecl) { if (!IS_GENERIC_INSTANTIATION_ENABLED) { generatedMockDecls.insert(GenerateFuncAccessor(*RawStaticCast(methodDecl))); } if (auto instantiatedDecls = mockUtils->TryGetInstantiatedDecls(*methodDecl)) { for (auto& instantiatedDecl : *instantiatedDecls) { generatedMockDecls.insert(GenerateFuncAccessor(*RawStaticCast(instantiatedDecl))); } } else if (IS_GENERIC_INSTANTIATION_ENABLED) { generatedMockDecls.insert(GenerateFuncAccessor(*RawStaticCast(methodDecl))); } } else if (auto fieldDecl = As(member.get()); fieldDecl) { GenerateVarDeclAccessors(*fieldDecl, AccessorKind::FIELD_GETTER, AccessorKind::FIELD_SETTER); } } } bool MockSupportManager::NeedEraseAccessorTypes(AST::Decl& decl) const { if (decl.outerDecl) { if (decl.outerDecl->linkage == Linkage::INTERNAL) { return false; } } return mockUtils->MayContainInternalTypes(decl.ty); } OwnedPtr MockSupportManager::GenerateErasedFuncAccessor(FuncDecl& methodDecl) const { auto outerClassDecl = As(methodDecl.outerDecl); CJC_ASSERT(outerClassDecl); OwnedPtr methodAccessor = ASTCloner::Clone(Ptr(&methodDecl)); mockUtils->AddGenericIfNeeded(methodDecl, *methodAccessor); std::vector> typeParamTys; auto memberAccessOriginal = CreateRefExpr(methodDecl); if (auto& generic = methodAccessor->funcBody->generic; generic) { generic->genericConstraints.clear(); for (auto& typeParam : generic->typeParameters) { typeParam->outerDecl = methodAccessor; typeParamTys.emplace_back(typeParam->ty); memberAccessOriginal->instTys.emplace_back(typeParam->ty); } } TypeSubst typeSubst = GenerateTypeMapping(methodDecl, typeParamTys); auto originalFuncTy = StaticCast(typeManager.GetInstantiatedTy(methodDecl.ty, typeSubst)); methodAccessor->ty = mockUtils->EraseFuncTypes(StaticCast(methodDecl.ty)); memberAccessOriginal->ty = originalFuncTy; std::vector> mockedMethodArgRefs {}; for (auto& param : methodAccessor->funcBody->paramLists[0]->params) { auto originalTy = typeManager.GetInstantiatedTy(param->ty, typeSubst); param->ty = typeManager.GetAnyTy(); param->outerDecl = methodAccessor.get(); auto refExpr = CreateRefExpr(*param); refExpr->curFile = param->curFile; auto arg = mockUtils->CreateTypeCastOrThrow(std::move(refExpr), originalTy, "internal error"); mockedMethodArgRefs.emplace_back(CreateFuncArg(std::move(arg))); } auto callOriginalMethod = MakeOwned(); callOriginalMethod->ty = originalFuncTy->retTy; callOriginalMethod->resolvedFunction = &methodDecl; callOriginalMethod->baseFunc = std::move(memberAccessOriginal); callOriginalMethod->args = std::move(mockedMethodArgRefs); callOriginalMethod->callKind = CallKind::CALL_DECLARED_FUNCTION; callOriginalMethod->curFile = methodDecl.curFile; std::vector> mockedMethodBodyNodes; mockedMethodBodyNodes.emplace_back(CreateReturnExpr(std::move(callOriginalMethod), methodAccessor->funcBody.get())); methodAccessor->funcBody->body->body = std::move(mockedMethodBodyNodes); methodAccessor->funcBody->funcDecl = methodAccessor.get(); methodAccessor->funcBody->ty = methodAccessor->ty; methodAccessor->funcBody->body->ty = methodAccessor->ty; methodAccessor->funcBody->retType->ty = typeManager.GetAnyTy(); methodAccessor->propDecl = nullptr; methodAccessor->isSetter = false; methodAccessor->isGetter = false; bool includeArgumentTypes = true; methodAccessor->identifier = mockUtils->BuildMockAccessorIdentifier( methodDecl, AccessorKind::METHOD, includeArgumentTypes); methodAccessor->mangledName = mockUtils->Mangle(*methodAccessor); MarkMockAccessorWithAttributes(*methodAccessor, AccessLevel::PUBLIC); methodAccessor->linkage = Linkage::EXTERNAL; return methodAccessor; } OwnedPtr MockSupportManager::GenerateFuncAccessor(FuncDecl& methodDecl) { bool needEraseTypes = NeedEraseAccessorTypes(methodDecl); auto outerClassDecl = As(methodDecl.outerDecl); CJC_ASSERT(outerClassDecl); OwnedPtr methodAccessor = ASTCloner::Clone(Ptr(&methodDecl)); mockUtils->AddGenericIfNeeded(methodDecl, *methodAccessor); OwnedPtr erasedAccessor = needEraseTypes ? GenerateErasedFuncAccessor(methodDecl) : nullptr; std::vector> typeParamTys; OwnedPtr memberAccessOriginal = needEraseTypes ? CreateRefExpr(*erasedAccessor) : CreateRefExpr(methodDecl); if (auto& generic = methodAccessor->funcBody->generic; generic) { for (auto& typeParam : generic->typeParameters) { typeParam->outerDecl = methodAccessor; typeParamTys.emplace_back(typeParam->ty); memberAccessOriginal->instTys.emplace_back(typeParam->ty); } } TypeSubst typeSubst = GenerateTypeMapping(methodDecl, typeParamTys); auto originalFuncTy = StaticCast(typeManager.GetInstantiatedTy(methodDecl.ty, typeSubst)); methodAccessor->ty = originalFuncTy; memberAccessOriginal->ty = needEraseTypes ? erasedAccessor->ty : originalFuncTy; std::vector> mockedMethodArgRefs {}; for (auto& param : methodAccessor->funcBody->paramLists[0]->params) { param->ty = typeManager.GetInstantiatedTy(param->ty, typeSubst); param->outerDecl = methodAccessor.get(); auto refExpr = CreateRefExpr(*param); refExpr->curFile = param->curFile; mockedMethodArgRefs.emplace_back(CreateFuncArg(std::move(refExpr))); } auto callOriginalMethod = MakeOwned(); if (needEraseTypes) { callOriginalMethod->ty = typeManager.GetAnyTy(); callOriginalMethod->resolvedFunction = erasedAccessor; } else { callOriginalMethod->ty = originalFuncTy->retTy; callOriginalMethod->resolvedFunction = &methodDecl; } callOriginalMethod->baseFunc = std::move(memberAccessOriginal); callOriginalMethod->args = std::move(mockedMethodArgRefs); callOriginalMethod->callKind = CallKind::CALL_DECLARED_FUNCTION; callOriginalMethod->curFile = methodDecl.curFile; OwnedPtr retValueExpr = std::move(callOriginalMethod); if (needEraseTypes) { retValueExpr = mockUtils->CreateTypeCastOrZeroValue(std::move(retValueExpr), originalFuncTy->retTy); } std::vector> mockedMethodBodyNodes; mockedMethodBodyNodes.emplace_back(CreateReturnExpr(std::move(retValueExpr), methodAccessor->funcBody.get())); methodAccessor->funcBody->body->body = std::move(mockedMethodBodyNodes); methodAccessor->funcBody->funcDecl = methodAccessor.get(); methodAccessor->funcBody->ty = originalFuncTy; methodAccessor->funcBody->body->ty = originalFuncTy; methodAccessor->funcBody->retType->ty = originalFuncTy->retTy; methodAccessor->identifier = mockUtils->BuildMockAccessorIdentifier(methodDecl, AccessorKind::METHOD); methodAccessor->mangledName = mockUtils->Mangle(*methodAccessor); if (needEraseTypes) { MarkMockAccessorWithAttributes(*methodAccessor, GetAccessLevel(methodDecl)); methodAccessor->linkage = methodDecl.linkage; } else { MarkMockAccessorWithAttributes(*methodAccessor, AccessLevel::PUBLIC); methodAccessor->linkage = Linkage::EXTERNAL; methodDecl.linkage = Linkage::EXTERNAL; } if (needEraseTypes) { generatedMockDecls.insert(std::move(erasedAccessor)); } return methodAccessor; } OwnedPtr MockSupportManager::GeneratePropAccessor(PropDecl& propDecl) { OwnedPtr propAccessor = ASTCloner::Clone(Ptr(&propDecl)); auto outerClassDecl = As(propDecl.outerDecl); CJC_ASSERT(outerClassDecl); std::vector> accessorForGetters; std::vector> accessorForSetters; auto propGetter = GenerateFuncAccessor(*GetUsableGetterForProperty(propDecl)); propGetter->propDecl = propAccessor.get(); accessorForGetters.emplace_back(std::move(propGetter)); if (propDecl.isVar) { auto propSetter = GenerateFuncAccessor(*GetUsableSetterForProperty(propDecl)); propSetter->propDecl = propAccessor.get(); accessorForSetters.emplace_back(std::move(propSetter)); } propAccessor->getters = std::move(accessorForGetters); propAccessor->setters = std::move(accessorForSetters); propAccessor->identifier = mockUtils->BuildMockAccessorIdentifier(propDecl, AccessorKind::PROP); propAccessor->mangledName = mockUtils->Mangle(*propAccessor); if (NeedEraseAccessorTypes(propDecl)) { MarkMockAccessorWithAttributes(*propAccessor, GetAccessLevel(propDecl)); propAccessor->linkage = propDecl.linkage; } else { MarkMockAccessorWithAttributes(*propAccessor, AccessLevel::PUBLIC); propAccessor->linkage = Linkage::EXTERNAL; } return propAccessor; } namespace { OwnedPtr CreateRefForFieldAccess(Ptr outerDecl, FuncBody& funcBody, AccessorKind kind) { auto outerClassDecl = As(outerDecl); CJC_ASSERT(outerClassDecl); if (kind == AccessorKind::STATIC_FIELD_GETTER || kind == AccessorKind::STATIC_FIELD_SETTER) { auto ref = CreateRefExpr(*outerClassDecl); if (funcBody.generic) { for (auto& param : funcBody.generic->typeParameters) { ref->instTys.emplace_back(param->ty); } } return ref; } else { return CreateThisRef(*outerClassDecl); } } } // namespace std::vector> MockSupportManager::GenerateFieldGetterAccessorBody( VarDecl& fieldDecl, FuncBody& funcBody, AccessorKind kind) const { OwnedPtr retExpr; if (kind == AccessorKind::TOP_LEVEL_VARIABLE_GETTER) { retExpr = CreateRefExpr(fieldDecl); } else { auto ref = CreateRefForFieldAccess(fieldDecl.outerDecl, funcBody, kind); retExpr = CreateMemberAccess(std::move(ref), fieldDecl.identifier); } std::vector> bodyNodes; bodyNodes.emplace_back(CreateReturnExpr(std::move(retExpr), &funcBody)); return bodyNodes; } std::vector> MockSupportManager::GenerateFieldSetterAccessorBody( VarDecl& fieldDecl, FuncParam& setterParam, FuncBody& funcBody, AccessorKind kind) const { static const auto UNIT_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); OwnedPtr retExpr; if (kind == AccessorKind::TOP_LEVEL_VARIABLE_SETTER) { retExpr = CreateRefExpr(fieldDecl); } else { auto ref = CreateRefForFieldAccess(fieldDecl.outerDecl, funcBody, kind); retExpr = CreateMemberAccess(std::move(ref), fieldDecl.identifier); } std::vector> bodyNodes; bodyNodes.emplace_back( CreateReturnExpr(CreateAssignExpr(std::move(retExpr), CreateRefExpr(setterParam), UNIT_TY), &funcBody)); return bodyNodes; } OwnedPtr MockSupportManager::CreateFieldAccessorDecl( const VarDecl &fieldDecl, FuncTy *accessorTy, AccessorKind kind) const { OwnedPtr accessorDecl = MakeOwned(); accessorDecl->curFile = fieldDecl.curFile; accessorDecl->keywordPos = fieldDecl.keywordPos; accessorDecl->identifier.SetPos(fieldDecl.identifier.Begin(), fieldDecl.identifier.End()); accessorDecl->moduleName = fieldDecl.moduleName; accessorDecl->fullPackageName = fieldDecl.fullPackageName; accessorDecl->outerDecl = As(fieldDecl.outerDecl); accessorDecl->ty = accessorTy; accessorDecl->identifier = mockUtils->BuildMockAccessorIdentifier(fieldDecl, kind); if (fieldDecl.IsStaticOrGlobal()) { MarkMockAccessorWithAttributes(*accessorDecl, GetAccessLevel(fieldDecl)); accessorDecl->linkage = fieldDecl.linkage; } else { MarkMockAccessorWithAttributes(*accessorDecl, AccessLevel::PUBLIC); accessorDecl->linkage = Linkage::EXTERNAL; } return accessorDecl; } OwnedPtr MockSupportManager::CreateForeignFunctionAccessorDecl(FuncDecl& funcDecl) const { static const auto NOTHING_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_NOTHING); CJC_ASSERT(funcDecl.TestAttr(Attribute::FOREIGN)); const auto& funcBody = funcDecl.funcBody; CJC_ASSERT(funcDecl.ty->kind == TypeKind::TYPE_FUNC); auto funcTy = Ptr(StaticCast(funcDecl.ty)); std::vector> accessorFuncParamLists; for (const auto& paramList : funcBody->paramLists) { std::vector> accessorParamList; for (const auto& param : paramList->params) { auto paramDecl = As(param); CJC_ASSERT(paramDecl); auto accessorParamDecl = CreateFuncParam( paramDecl->identifier, ASTCloner::Clone(paramDecl->type.get()), nullptr, paramDecl->ty); accessorParamList.emplace_back(std::move(accessorParamDecl)); } accessorFuncParamLists.emplace_back( CreateFuncParamList(std::move(accessorParamList))); } std::vector> args; for (const auto& paramList : accessorFuncParamLists) { for (const auto& param : paramList->params) { args.emplace_back(CreateFuncArg(CreateRefExpr(*param))); } } auto accessorFuncRetStmt = CreateReturnExpr( CreateCallExpr(CreateRefExpr(funcDecl), std::move(args), nullptr, funcTy->retTy)); accessorFuncRetStmt->ty = NOTHING_TY; std::vector> accessorFuncBodyStmts; accessorFuncBodyStmts.emplace_back(std::move(accessorFuncRetStmt)); auto accessorFuncBodyBlock = CreateBlock(std::move(accessorFuncBodyStmts), NOTHING_TY); auto accessorFuncBody = CreateFuncBody( std::move(accessorFuncParamLists), ASTCloner::Clone(funcBody->retType.get()), std::move(accessorFuncBodyBlock), funcTy); auto accessorName = MockUtils::GetForeignAccessorName(funcDecl) + MockUtils::mockAccessorSuffix; auto accessorDecl = CreateFuncDecl(accessorName, std::move(accessorFuncBody), funcTy); accessorDecl->curFile = funcDecl.curFile; accessorDecl->fullPackageName = funcDecl.fullPackageName; accessorDecl->moduleName = funcDecl.moduleName; accessorDecl->EnableAttr(Attribute::PUBLIC); accessorDecl->EnableAttr(Attribute::GLOBAL); accessorDecl->EnableAttr(Attribute::UNSAFE); accessorDecl->EnableAttr(Attribute::GENERATED_TO_MOCK); accessorDecl->EnableAttr(Attribute::NO_MANGLE); return accessorDecl; } OwnedPtr MockSupportManager::GenerateVarDeclAccessor(VarDecl& fieldDecl, AccessorKind kind) { CJC_ASSERT(IsFieldOrVariable(kind)); auto isGetter = mockUtils->IsGeneratedGetter(kind); FuncTy* accessorTy = isGetter ? typeManager.GetFunctionTy({}, fieldDecl.ty) : typeManager.GetFunctionTy({fieldDecl.ty}, TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)); std::vector> body; std::vector> accessorParams {}; OwnedPtr fieldType = fieldDecl.type ? ASTCloner::Clone(fieldDecl.type.get()) : MockUtils::CreateType(fieldDecl.ty); auto accessorDecl = CreateFieldAccessorDecl(fieldDecl, accessorTy, kind); accessorDecl->funcBody = MakeOwned(); if (fieldDecl.IsStaticOrGlobal() && fieldDecl.outerDecl && fieldDecl.outerDecl->generic) { accessorDecl->funcBody->generic = MakeOwned(); CopyBasicInfo(fieldDecl.outerDecl->generic, accessorDecl->funcBody->generic); for (auto& param : fieldDecl.outerDecl->generic->typeParameters) { accessorDecl->funcBody->generic->typeParameters.emplace_back( CreateGenericParamDecl(*accessorDecl, param->identifier, typeManager)); } } if (isGetter) { body = GenerateFieldGetterAccessorBody(fieldDecl, *accessorDecl->funcBody, kind); } else { auto setterParam = CreateFuncParam("newValue", std::move(fieldType), nullptr, fieldDecl.ty); setterParam->outerDecl = accessorDecl.get(); setterParam->moduleName = fieldDecl.moduleName; setterParam->fullPackageName = fieldDecl.fullPackageName; setterParam->curFile = fieldDecl.curFile; body = GenerateFieldSetterAccessorBody(fieldDecl, *setterParam, *accessorDecl->funcBody, kind); accessorParams.emplace_back(std::move(setterParam)); } std::vector> accessorParamLists {}; accessorParamLists.emplace_back(CreateFuncParamList(std::move(accessorParams))); OwnedPtr retType = isGetter ? std::move(fieldType) : MockUtils::CreateType(TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)); if (IsStaticField(kind)) { accessorDecl->EnableAttr(Attribute::STATIC); } if (IsTopLevelField(kind)) { accessorDecl->EnableAttr(Attribute::GLOBAL); } accessorDecl->funcBody->ty = accessorTy; accessorDecl->funcBody->parentClassLike = As(fieldDecl.outerDecl); accessorDecl->funcBody->funcDecl = accessorDecl.get(); accessorDecl->funcBody->paramLists = std::move(accessorParamLists); accessorDecl->funcBody->body = CreateBlock(std::move(body), accessorTy); accessorDecl->funcBody->retType = std::move(retType); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (fieldDecl.ty->IsStruct() && kind == AccessorKind::FIELD_SETTER) { accessorDecl->EnableAttr(Attribute::MUT); } #endif accessorDecl->mangledName = mockUtils->Mangle(*accessorDecl); return accessorDecl; } bool MockSupportManager::NeedToSearchCallsToReplaceWithAccessors(Node& node) { // Accessors only contain a call of their original declaration which shouldn't be replaced if (auto decl = As(&node); decl && MockUtils::IsMockAccessor(*decl)) { return false; } return true; } void MockSupportManager::WriteGeneratedMockDecls() { while (!generatedMockDecls.empty()) { auto accessorDecl = std::move(generatedMockDecls.extract(generatedMockDecls.begin()).value()); mockUtils->Instantiate(*accessorDecl); if (auto outerDecl = As(accessorDecl->outerDecl); outerDecl) { outerDecl->body->decls.emplace_back(std::move(accessorDecl)); } else if (accessorDecl->curFile) { auto file = accessorDecl->curFile; file->decls.emplace_back(std::move(accessorDecl)); std::rotate(file->decls.rbegin(), file->decls.rbegin() + 1, file->decls.rend()); } } generatedMockDecls.clear(); genericMockVarsDecls.clear(); } bool MockSupportManager::IsMemberAccessOnThis(const MemberAccess& memberAccess) const { if (!memberAccess.baseExpr) { return false; } auto refBaseExpr = As(memberAccess.baseExpr); if (!refBaseExpr) { return false; } return refBaseExpr->isThis; } /* * For calls involving mut operations on structs, * we cannot just replace intermediate member access expression (like field access) with an accessor call, * because it causes mutability rules violation. * Foe example, `foo.myStructField.mutY(newY)` -> `foo.myStructField$get().mutY(newY)` <--- this is wrong replacement * Instead, we extract that intermediate expression into a mutable variable, * then substitute it instead of original expression. * And, finally invoke a setter accessor to pass the mutated struct back. * * Example of desugaring: * myClass.myStruct.mutSomeField() * => * { * var $tmp1 = myClass.myStruct$get$ToMock() * let $tmp2 = $tmp1.mutSomeField() * myClass.myStruct$set$ToMock($tmp1) * $tmp2 * } */ void MockSupportManager::TransformAccessorCallForMutOperation( NameReferenceExpr& originalNre, Expr& replacedNre, Expr& topLevelExpr) { CJC_ASSERT(Is(topLevelExpr) || (Is(topLevelExpr) && DynamicCast(&topLevelExpr)->resolvedFunction->TestAttr(Attribute::MUT))); auto tmpVarDecl = CreateTmpVarDecl( MockUtils::CreateType(replacedNre.ty), ASTCloner::Clone(Ptr(&replacedNre))); tmpVarDecl->isVar = true; auto tmpVarRefToMutate = CreateRefExpr(*tmpVarDecl); tmpVarRefToMutate->ref.identifier = tmpVarDecl->identifier; tmpVarRefToMutate->curFile = replacedNre.curFile; tmpVarRefToMutate->ty = originalNre.ty; OwnedPtr newTopLevelExpr = ASTCloner::Clone(Ptr(&topLevelExpr)); Ptr mutBaseExpr; if (auto callExpr = As(newTopLevelExpr)) { mutBaseExpr = callExpr->baseFunc; } else if (auto assignExpr = As(newTopLevelExpr)) { mutBaseExpr = assignExpr->leftValue; } else { CJC_ABORT(); } auto tmpVarRefToAssign = ASTCloner::Clone(tmpVarRefToMutate.get()); CJC_ASSERT(mutBaseExpr->astKind == ASTKind::MEMBER_ACCESS); As(mutBaseExpr)->baseExpr = std::move(tmpVarRefToMutate); auto mutResultVarDecl = CreateTmpVarDecl( MockUtils::CreateType(newTopLevelExpr->ty), std::move(newTopLevelExpr)); auto mutResultVarRef = CreateRefExpr(*mutResultVarDecl); mutResultVarRef->ref.identifier = mutResultVarDecl->identifier; mutResultVarRef->curFile = replacedNre.curFile; auto ty = topLevelExpr.ty; auto newOriginalMa = ASTCloner::Clone(Ptr(&originalNre)); newOriginalMa->desugarExpr = nullptr; auto backAssignExpr = CreateAssignExpr(std::move(newOriginalMa), std::move(tmpVarRefToAssign)); backAssignExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); ReplaceFieldSetWithAccessor(*backAssignExpr, false); backAssignExpr->EnableAttr(Attribute::GENERATED_TO_MOCK); std::vector> nodes {}; nodes.emplace_back(std::move(tmpVarDecl)); nodes.emplace_back(std::move(mutResultVarDecl)); nodes.emplace_back(std::move(backAssignExpr)); nodes.emplace_back(CreateReturnExpr(std::move(mutResultVarRef), nullptr)); std::vector> paramLists {}; paramLists.emplace_back(CreateFuncParamList(std::vector> {})); auto lambda = CreateLambdaExpr( CreateFuncBody( std::move(paramLists), MockUtils::CreateType(replacedNre.ty), CreateBlock(std::move(nodes), ty), ty) ); lambda->ty = typeManager.GetFunctionTy({}, ty); lambda->funcBody->ty = lambda->ty; topLevelExpr.desugarExpr = CreateCallExpr(std::move(lambda), {}, nullptr, ty); } void MockSupportManager::ReplaceSubMemberAccessWithAccessor( const MemberAccess& memberAccess, bool isInConstructor, const Ptr topLevelMutExpr) { if (auto nre = DynamicCast(ExtractLastDesugaredExpr(*memberAccess.baseExpr)); nre) { auto replacedNre = ReplaceExprWithAccessor(*nre, isInConstructor, true); if (topLevelMutExpr && replacedNre) { TransformAccessorCallForMutOperation(*nre, *replacedNre, *topLevelMutExpr); } } } Ptr MockSupportManager::ReplaceExprWithAccessor(Expr& originalExpr, bool isInConstructor, bool isSubMemberAccess) { auto expr = ExtractLastDesugaredExpr(originalExpr); if (auto fieldMemberAccess = As(expr); fieldMemberAccess && fieldMemberAccess->target && fieldMemberAccess->target->astKind == ASTKind::VAR_DECL && fieldMemberAccess->target->astKind != ASTKind::PROP_DECL ) { // Left values of an assignment are handled below, by `ReplaceFieldSetWithAccessor` if (fieldMemberAccess->TestAttr(Attribute::LEFT_VALUE) || (!fieldMemberAccess->isAlone && !isSubMemberAccess)) { return nullptr; } return ReplaceFieldGetWithAccessor(*fieldMemberAccess, isInConstructor); } else if (auto assignment = As(expr); assignment && !assignment->TestAttr(Attribute::GENERATED_TO_MOCK) ) { // Some left value expressions don't have `LEFT_VALUE` attribute, // set the attribute to definitely skip left value expressions themthelves to generate accessor calls // as they are handled within whole assign expressions assignment->leftValue->EnableAttr(Attribute::LEFT_VALUE); // Support all compound assignments if (!assignment->isCompound) { return ReplaceFieldSetWithAccessor(*assignment, isInConstructor); } else { return nullptr; } } else if (auto memberAccess = As(expr); memberAccess && memberAccess->target) { return ReplaceMemberAccessWithAccessor(*memberAccess, isInConstructor); } else if (auto refExpr = As(expr); refExpr && refExpr->GetTarget() && !refExpr->TestAttr(Attribute::LEFT_VALUE) ) { auto target = refExpr->GetTarget(); if (target->astKind == ASTKind::VAR_DECL && target->TestAttr(Attribute::GLOBAL)) { return ReplaceTopLevelVariableGetWithAccessor(*refExpr); } return nullptr; } else { return nullptr; } } Ptr MockSupportManager::ReplaceMemberAccessWithAccessor(MemberAccess& memberAccess, bool isInConstructor) { Ptr parentMutExpr = nullptr; if (auto ce = DynamicCast(memberAccess.callOrPattern)) { if (auto resolvedFunction = ce->resolvedFunction; resolvedFunction && resolvedFunction->TestAttr(Attribute::MUT) && Is(resolvedFunction->outerDecl)) { parentMutExpr = ce; } } ReplaceSubMemberAccessWithAccessor(memberAccess, isInConstructor, parentMutExpr); if (isInConstructor && IsMemberAccessOnThis(memberAccess)) { return nullptr; } if (auto funcDecl = As(memberAccess.target); funcDecl && funcDecl->propDecl) { auto propDeclToMock = As( mockUtils->FindAccessorForMemberAccess(memberAccess, funcDecl->propDecl, {}, AccessorKind::METHOD)); if (!propDeclToMock) { return nullptr; } if (funcDecl->isGetter) { memberAccess.target = GetUsableGetterForProperty(*propDeclToMock); } else if (funcDecl->isSetter) { memberAccess.target = GetUsableSetterForProperty(*propDeclToMock); } } else if (auto funcDeclToMock = mockUtils->FindAccessorForMemberAccess( memberAccess, memberAccess.target, memberAccess.instTys, AccessorKind::METHOD); funcDeclToMock) { memberAccess.target = funcDeclToMock; if (auto callExpr = DynamicCast(memberAccess.callOrPattern)) { callExpr->resolvedFunction = StaticCast(funcDeclToMock); } } // No desugared expr generated here (instead, target is replaced) so return original member access return &memberAccess; } Ptr MockSupportManager::ReplaceFieldGetWithAccessor(MemberAccess& memberAccess, bool isInConstructor) { ReplaceSubMemberAccessWithAccessor(memberAccess, isInConstructor); if (!memberAccess.target || (isInConstructor && IsMemberAccessOnThis(memberAccess))) { return nullptr; } if (auto accessorCall = GenerateAccessorCallForField(memberAccess, AccessorKind::FIELD_GETTER); accessorCall) { accessorCall->sourceExpr = Ptr(&memberAccess); memberAccess.desugarExpr = std::move(accessorCall); return memberAccess.desugarExpr; } return nullptr; } Ptr MockSupportManager::ReplaceTopLevelVariableGetWithAccessor(RefExpr& refExpr) { if (!refExpr.GetTarget()) { return nullptr; } if (auto accessorCall = GenerateAccessorCallForTopLevelVariable( refExpr, AccessorKind::TOP_LEVEL_VARIABLE_GETTER); accessorCall ) { accessorCall->sourceExpr = Ptr(&refExpr); refExpr.desugarExpr = std::move(accessorCall); return refExpr.desugarExpr; } return nullptr; } Ptr MockSupportManager::ReplaceFieldSetWithAccessor(AssignExpr& assignExpr, bool isInConstructor) { auto leftValue = assignExpr.leftValue.get(); OwnedPtr accessorCall; if (auto refExpr = As(leftValue); refExpr) { if (!refExpr->GetTarget()) { return nullptr; } accessorCall = GenerateAccessorCallForTopLevelVariable(*refExpr, AccessorKind::TOP_LEVEL_VARIABLE_SETTER); if (!accessorCall) { return nullptr; } } else if (auto memberAccess = As(leftValue); memberAccess) { if (!memberAccess->target || (isInConstructor && IsMemberAccessOnThis(*memberAccess))) { return nullptr; } accessorCall = GenerateAccessorCallForField(*memberAccess, AccessorKind::FIELD_SETTER); if (!accessorCall) { ReplaceSubMemberAccessWithAccessor( *memberAccess, isInConstructor, Is(memberAccess->target->outerDecl) ? &assignExpr : nullptr); return &assignExpr; } } else if (leftValue->astKind == ASTKind::WILDCARD_EXPR) { return nullptr; } CJC_ASSERT(accessorCall); accessorCall->args.emplace_back(CreateFuncArg(ASTCloner::Clone(assignExpr.rightExpr.get()))); accessorCall->sourceExpr = Ptr(&assignExpr); assignExpr.desugarExpr = std::move(accessorCall); return assignExpr.desugarExpr; } OwnedPtr MockSupportManager::GenerateAccessorCallForTopLevelVariable( const RefExpr& refExpr, AccessorKind kind) { auto accessorDecl = mockUtils->FindTopLevelAccessor(refExpr.GetTarget(), kind); if (!accessorDecl) { return nullptr; } auto accessorCall = MakeOwned(); accessorCall->ty = RawStaticCast(accessorDecl->ty)->retTy; accessorCall->resolvedFunction = accessorDecl; accessorCall->baseFunc = CreateRefExpr(*accessorDecl); std::vector> mockedMethodArgRefs {}; accessorCall->args = std::move(mockedMethodArgRefs); accessorCall->callKind = CallKind::CALL_DECLARED_FUNCTION; accessorCall->curFile = refExpr.curFile; return accessorCall; } OwnedPtr MockSupportManager::GenerateAccessorCallForField( const MemberAccess& memberAccess, AccessorKind kind) { Ptr accessorDecl = As( mockUtils->FindAccessorForMemberAccess(memberAccess, memberAccess.target, memberAccess.instTys, kind)); if (!accessorDecl) { return nullptr; } auto maTy = memberAccess.ty; auto accessorCall = MakeOwned(); auto accessorMemberAccess = CreateMemberAccess( ASTCloner::Clone(memberAccess.baseExpr.get()), accessorDecl->identifier); accessorMemberAccess->curFile = memberAccess.curFile; accessorMemberAccess->target = accessorDecl; accessorMemberAccess->callOrPattern = accessorCall.get(); if (kind == AccessorKind::FIELD_GETTER) { accessorMemberAccess->ty = typeManager.GetFunctionTy({}, maTy); accessorCall->ty = maTy; } else { accessorMemberAccess->ty = typeManager.GetFunctionTy({maTy}, TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)); accessorCall->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); } accessorCall->resolvedFunction = accessorDecl; accessorCall->baseFunc = std::move(accessorMemberAccess); std::vector> mockedMethodArgRefs {}; accessorCall->args = std::move(mockedMethodArgRefs); accessorCall->callKind = CallKind::CALL_DECLARED_FUNCTION; accessorCall->curFile = memberAccess.curFile; return accessorCall; } std::vector> MockSupportManager::CloneFuncDecl(Ptr fromDecl, Ptr toDecl) { CopyBasicInfo(fromDecl, toDecl); toDecl->CloneAttrs(*fromDecl); toDecl->linkage = fromDecl->linkage; toDecl->fullPackageName = fromDecl->fullPackageName; toDecl->identifier = fromDecl->identifier; toDecl->outerDecl = fromDecl->outerDecl; toDecl->funcBody = MakeOwned(); auto genericTys = mockUtils->AddGenericIfNeeded(*fromDecl, *toDecl); auto typeSubsts = GenerateTypeMapping(*fromDecl, genericTys); toDecl->ty = typeManager.GetInstantiatedTy(fromDecl->ty, typeSubsts); toDecl->funcBody->ty = toDecl->ty; toDecl->funcBody->retType = MockUtils::CreateType(StaticCast(toDecl->ty)->retTy); toDecl->funcBody->funcDecl = toDecl; toDecl->funcBody->parentClassLike = toDecl->funcBody->parentClassLike; auto paramList = MakeOwned(); CopyBasicInfo(fromDecl->funcBody->paramLists[0], paramList); for (auto& param : fromDecl->funcBody->paramLists[0]->params) { auto clonedParam = MakeOwned(); CopyBasicInfo(param, clonedParam); clonedParam->CloneAttrs(*param); clonedParam->ty = typeManager.GetInstantiatedTy(param->ty, typeSubsts); clonedParam->type = MockUtils::CreateType(clonedParam->ty); clonedParam->outerDecl = toDecl; clonedParam->identifier = param->identifier; paramList->params.emplace_back(std::move(clonedParam)); } toDecl->funcBody->paramLists.emplace_back(std::move(paramList)); return genericTys; } void MockSupportManager::PrepareInterfaceDecl(InterfaceDecl& interfaceDecl) { if (IS_GENERIC_INSTANTIATION_ENABLED) { return; } if (interfaceDecl.TestAttr(Attribute::GENERIC)) { // Generic interface not yet supported return; } std::vector> funcDeclsWithDefault; for (auto& decl : interfaceDecl.GetMemberDecls()) { auto funcDecl = DynamicCast(decl.get()); if (!funcDecl || !funcDecl->TestAttr(Attribute::DEFAULT)) { continue; } funcDeclsWithDefault.push_back(funcDecl); } if (funcDeclsWithDefault.empty()) { return; } auto accessorInterface = MakeOwned(); CopyBasicInfo(&interfaceDecl, accessorInterface); accessorInterface->CloneAttrs(interfaceDecl); accessorInterface->identifier = interfaceDecl.identifier + MockUtils::defaultAccessorSuffix; accessorInterface->inheritedTypes.emplace_back(MockUtils::CreateType(interfaceDecl.ty)); accessorInterface->ty = typeManager.GetInterfaceTy(*accessorInterface, {}); accessorInterface->fullPackageName = interfaceDecl.fullPackageName; accessorInterface->linkage = interfaceDecl.linkage; accessorInterface->body = MakeOwned(); CopyBasicInfo(interfaceDecl.body, accessorInterface->body); for (auto funcDecl : funcDeclsWithDefault) { auto accessorDecl = MakeOwned(); CloneFuncDecl(funcDecl, accessorDecl); accessorDecl->outerDecl = accessorInterface; accessorDecl->funcBody->parentClassLike = accessorInterface; accessorDecl->identifier = mockUtils->Mangle(*funcDecl) + MockUtils::defaultAccessorSuffix; CJC_ASSERT(accessorDecl->TestAttr(Attribute::DEFAULT)); accessorDecl->DisableAttr(Attribute::DEFAULT); accessorDecl->EnableAttr(Attribute::ABSTRACT); accessorDecl->EnableAttr(Attribute::GENERATED_TO_MOCK); accessorInterface->body->decls.emplace_back(std::move(accessorDecl)); } interfaceDecl.EnableAttr(Attribute::MOCK_SUPPORTED); generatedMockDecls.emplace(std::move(accessorInterface)); } template Ptr MockSupportManager::FindGeneratedGlobalDecl(Ptr file, const std::string& identifier) { if (auto decl = MockUtils::FindGlobalDecl(file, identifier)) { return decl; } for (auto& decl : generatedMockDecls) { if (decl->identifier == identifier) { return DynamicCast(decl.get()); } } return nullptr; } void MockSupportManager::PrepareClassWithDefaults(ClassDecl& classDecl, InterfaceDecl& interfaceDecl) { Ptr accessorInterfaceDecl = FindGeneratedGlobalDecl( interfaceDecl.curFile, interfaceDecl.identifier + MockUtils::defaultAccessorSuffix); if (!accessorInterfaceDecl) { // Interface is in package, which was compiled without mocking support return; } auto extendDecl = MakeOwned(); CopyBasicInfo(&classDecl, extendDecl); extendDecl->extendedType = MockUtils::CreateType(classDecl.ty); extendDecl->inheritedTypes.emplace_back(MockUtils::CreateType(accessorInterfaceDecl->ty)); extendDecl->ty = classDecl.ty; extendDecl->linkage = accessorInterfaceDecl->linkage; extendDecl->fullPackageName = classDecl.fullPackageName; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto manglerCtxIt = mockUtils->mangler.manglerCtxTable.find( ManglerContext::ReduceUnitTestPackageName(extendDecl->fullPackageName)); CJC_ASSERT(manglerCtxIt != mockUtils->mangler.manglerCtxTable.end()); manglerCtxIt->second->SaveExtend2CurFile(extendDecl->curFile, extendDecl); #endif // Declarations with default in original interface are in the same order // as in accessor interface auto& accessorDecls = accessorInterfaceDecl->GetMemberDecls(); auto& originalDecls = interfaceDecl.GetMemberDecls(); auto accessorDeclIt = accessorDecls.begin(); auto originalDeclIt = originalDecls.begin(); while (accessorDeclIt != accessorDecls.end()) { CJC_ASSERT(originalDeclIt != originalDecls.end()); auto originalFunc = DynamicCast(originalDeclIt->get()); if (!originalFunc || !originalFunc->TestAttr(Attribute::DEFAULT)) { originalDeclIt++; continue; } auto accessorDecl = As(*accessorDeclIt); CJC_ASSERT(accessorDecl); CJC_ASSERT(accessorDecl->identifier == mockUtils->Mangle(*originalFunc) + MockUtils::defaultAccessorSuffix); auto accessorImplDecl = MakeOwned(); auto implTypeParamsTy = CloneFuncDecl(accessorDecl, accessorImplDecl); accessorImplDecl->identifier = accessorDecl->identifier; accessorImplDecl->outerDecl = extendDecl; accessorImplDecl->curFile = extendDecl->curFile; CJC_ASSERT(accessorImplDecl->TestAttr(Attribute::ABSTRACT)); accessorImplDecl->DisableAttr(Attribute::ABSTRACT); accessorImplDecl->EnableAttr(Attribute::GENERATED_TO_MOCK); std::vector> args; for (auto& param : accessorImplDecl->funcBody->paramLists[0]->params) { args.emplace_back(CreateFuncArg(CreateRefExpr(*param))); } OwnedPtr receiver; if (accessorDecl->TestAttr(Attribute::STATIC)) { receiver = CreateRefExpr(classDecl); } else { receiver = CreateThisRef(classDecl); } auto memberAccess = CreateMemberAccess(std::move(receiver), *originalFunc); memberAccess->ty = typeManager.GetInstantiatedTy( originalFunc->ty, GenerateTypeMapping(*originalFunc, implTypeParamsTy)); for (auto& tyParam : implTypeParamsTy) { memberAccess->instTys.emplace_back(tyParam); } memberAccess->EnableAttr(Attribute::GENERATED_TO_MOCK); auto callExpr = CreateCallExpr( std::move(memberAccess), std::move(args), originalFunc, accessorImplDecl->funcBody->retType->ty, CallKind::CALL_DECLARED_FUNCTION); callExpr->EnableAttr(Attribute::GENERATED_TO_MOCK); accessorImplDecl->funcBody->body = MakeOwned(); accessorImplDecl->funcBody->body->ty = accessorImplDecl->funcBody->retType->ty; accessorImplDecl->funcBody->body->body.emplace_back(CreateReturnExpr(std::move(callExpr))); PrepareStaticDecl(*accessorImplDecl); extendDecl->members.emplace_back(std::move(accessorImplDecl)); accessorDeclIt++; originalDeclIt++; } typeManager.declToExtendMap[&classDecl].insert(extendDecl.get()); defaultInterfaceAccessorExtends[classDecl.ty].insert(accessorInterfaceDecl->ty); generatedMockDecls.emplace(std::move(extendDecl)); } namespace { OwnedPtr CreateBoolMatch( OwnedPtr selector, OwnedPtr trueBranch, OwnedPtr falseBranch, Ptr ty) { static const auto BOOL_TY = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); OwnedPtr truePattern = MakeOwned(); truePattern->literal = CreateLitConstExpr(LitConstKind::BOOL, "true", BOOL_TY); truePattern->ty = BOOL_TY; OwnedPtr falsePattern = MakeOwned(); falsePattern->literal = CreateLitConstExpr(LitConstKind::BOOL, "false", BOOL_TY); falsePattern->ty = BOOL_TY; auto caseTrue = CreateMatchCase(std::move(truePattern), std::move(trueBranch)); auto caseFalse = CreateMatchCase(std::move(falsePattern), std::move(falseBranch)); std::vector> matchCases; matchCases.emplace_back(std::move(caseTrue)); matchCases.emplace_back(std::move(caseFalse)); return CreateMatchExpr(std::move(selector), std::move(matchCases), ty); } } // namespace std::tuple, Ptr> MockSupportManager::FindDefaultAccessorInterfaceAndFunction( Ptr original) { auto interfaceDecl = DynamicCast(original->outerDecl); if (!interfaceDecl) { return {nullptr, nullptr}; } if (!original->TestAttr(Attribute::DEFAULT)) { return {nullptr, nullptr}; } Ptr accessorInterfaceDecl = FindGeneratedGlobalDecl( interfaceDecl->curFile, interfaceDecl->identifier + MockUtils::defaultAccessorSuffix); if (!accessorInterfaceDecl) { return {nullptr, nullptr}; } Ptr accessorDecl = MockUtils::FindMemberDecl( *accessorInterfaceDecl, mockUtils->Mangle(*original) + MockUtils::defaultAccessorSuffix); return {accessorInterfaceDecl, accessorDecl}; } void MockSupportManager::ReplaceInterfaceDefaultFunc( AST::Expr& originalExpr, Ptr outerClassLike, bool isInMockAnnotatedLambda) { auto expr = ExtractLastDesugaredExpr(originalExpr); if (expr->TestAttr(Attribute::GENERATED_TO_MOCK)) { return; } auto nameRefExpr = DynamicCast(expr); if (!nameRefExpr) { return; } if (nameRefExpr->callOrPattern) { ReplaceInterfaceDefaultFuncInCall( *nameRefExpr->callOrPattern, outerClassLike, isInMockAnnotatedLambda); return; } auto target = nameRefExpr->GetTarget(); if (!target) { return; } auto funcDecl = DynamicCast(target); if (!funcDecl || !funcDecl->outerDecl) { return; } CJC_ASSERT(!isInMockAnnotatedLambda); auto [buddyInterfaceDecl, buddyFuncDecl] = FindDefaultAccessorInterfaceAndFunction(funcDecl); if (!buddyInterfaceDecl || !buddyFuncDecl) { return; } if (auto maExpr = DynamicCast(nameRefExpr)) { if (funcDecl->TestAttr(Attribute::STATIC)) { if (auto genericTy = DynamicCast(maExpr->baseExpr->ty)) { // T.foo |-> match (IsSubtypeTypes()) { // case true => T.foo$Buddy // case false => T.foo // } genericTy->upperBounds.insert(buddyInterfaceDecl->ty); auto selector = mockUtils->CreateIsSubtypeTypesCall(maExpr->baseExpr->ty, buddyInterfaceDecl->ty); auto buddyMa = CreateMemberAccess(ASTCloner::Clone(Ptr(maExpr->baseExpr.get())), *buddyFuncDecl); CopyBasicInfo(maExpr, buddyMa); buddyMa->EnableAttr(Attribute::GENERATED_TO_MOCK); buddyMa->ty = typeManager.GetInstantiatedTy( buddyFuncDecl->ty, GenerateTypeMapping(*buddyFuncDecl, maExpr->instTys)); buddyMa->instTys = maExpr->instTys; auto originalMa = ASTCloner::Clone(Ptr(maExpr)); originalMa->EnableAttr(Attribute::GENERATED_TO_MOCK); auto matchExpr = CreateBoolMatch( std::move(selector), std::move(buddyMa), std::move(originalMa), maExpr->ty); maExpr->desugarExpr = std::move(matchExpr); } else if (HasDefaultInterfaceAccessor(maExpr->baseExpr->ty, buddyInterfaceDecl->ty)) { // C.foo |-> C.foo$Buddy auto buddyMa = CreateMemberAccess(ASTCloner::Clone(Ptr(maExpr->baseExpr.get())), *buddyFuncDecl); CopyBasicInfo(maExpr, buddyMa); buddyMa->EnableAttr(Attribute::GENERATED_TO_MOCK); buddyMa->ty = typeManager.GetInstantiatedTy( buddyFuncDecl->ty, GenerateTypeMapping(*buddyFuncDecl, maExpr->instTys)); buddyMa->instTys = maExpr->instTys; maExpr->desugarExpr = std::move(buddyMa); } } else { // a.foo |-> // let tmp = a // match (a) { // case v : I$Buddy => v.foo$Buddy // case _ => tmp.foo // } auto baseExprVar = CreateTmpVarDecl( MockUtils::CreateType(maExpr->baseExpr->ty), ASTCloner::Clone(maExpr->baseExpr.get())); auto createBuddyMa = [this, maExpr, &funcDecl = *buddyFuncDecl](Ptr castedExpr) { auto ma = CreateMemberAccess(CreateRefExpr(*castedExpr), funcDecl); CopyBasicInfo(maExpr, ma); ma->ty = typeManager.GetInstantiatedTy(funcDecl.ty, GenerateTypeMapping(funcDecl, maExpr->instTys)); ma->instTys = maExpr->instTys; return ma; }; auto originalMa = ASTCloner::Clone(Ptr(maExpr)); originalMa->EnableAttr(Attribute::GENERATED_TO_MOCK); auto matchExpr = MockUtils::CreateTypeCast( CreateRefExpr(*baseExprVar), buddyInterfaceDecl->ty, std::move(createBuddyMa), std::move(originalMa), maExpr->ty); auto blockExpr = CreateBlock({}, maExpr->ty); blockExpr->body.emplace_back(std::move(baseExprVar)); blockExpr->body.emplace_back(std::move(matchExpr)); maExpr->desugarExpr = std::move(blockExpr); } return; } else if (auto refExpr = DynamicCast(expr)) { if (funcDecl->TestAttr(Attribute::STATIC)) { if (!outerClassLike || HasDefaultInterfaceAccessor(outerClassLike->ty, buddyInterfaceDecl->ty)) { // foo |-> foo$Buddy auto buddyFuncRef = CreateRefExpr(*buddyFuncDecl); CopyBasicInfo(refExpr, buddyFuncRef); buddyFuncRef->ty = typeManager.GetInstantiatedTy( buddyFuncDecl->ty, GenerateTypeMapping(*buddyFuncDecl, refExpr->instTys)); buddyFuncRef->instTys = refExpr->instTys; refExpr->desugarExpr = std::move(buddyFuncRef); } } else { // foo |-> match (this) { // case v : I$Buddy => v.foo$Buddy // case _ => foo // } auto thisExpr = CreateThisRef(*funcDecl->funcBody->parentClassLike); CopyBasicInfo(refExpr, thisExpr); thisExpr->EnableAttr(Attribute::GENERATED_TO_MOCK); auto createBuddyMa = [this, refExpr, &funcDecl = *buddyFuncDecl](Ptr castedExpr) { auto ma = CreateMemberAccess(CreateRefExpr(*castedExpr), funcDecl); CopyBasicInfo(refExpr, ma); ma->ty = typeManager.GetInstantiatedTy( funcDecl.ty, GenerateTypeMapping(funcDecl, refExpr->instTys)); ma->instTys = refExpr->instTys; return ma; }; auto originalRef = ASTCloner::Clone(Ptr(refExpr)); originalRef->EnableAttr(Attribute::GENERATED_TO_MOCK); auto matchExpr = MockUtils::CreateTypeCast( std::move(thisExpr), buddyInterfaceDecl->ty, std::move(createBuddyMa), std::move(originalRef), refExpr->ty); refExpr->desugarExpr = std::move(matchExpr); } } } void MockSupportManager::ReplaceInterfaceDefaultFuncInCall( AST::Node& node, Ptr outerClassLike, bool isInMockAnnotatedLambda) { if (isInMockAnnotatedLambda) { return; } auto originalExpr = DynamicCast(&node); if (!originalExpr) { return; } auto expr = ExtractLastDesugaredExpr(*originalExpr); if (expr->TestAttr(Attribute::GENERATED_TO_MOCK)) { return; } auto callExpr = DynamicCast(expr); if (!callExpr || !callExpr->resolvedFunction) { return; } auto [buddyInterfaceDecl, buddyFuncDecl] = FindDefaultAccessorInterfaceAndFunction(callExpr->resolvedFunction); if (!buddyInterfaceDecl || !buddyFuncDecl) { return; } if (auto maExpr = DynamicCast(callExpr->baseFunc.get())) { if (callExpr->resolvedFunction->TestAttr(Attribute::STATIC)) { if (auto genericTy = DynamicCast(maExpr->baseExpr->ty)) { // T.foo() |-> match (IsSubtypeTypes()) { // case true => T.foo$Buddy() // case false => T.foo() // } genericTy->upperBounds.insert(buddyInterfaceDecl->ty); auto selector = mockUtils->CreateIsSubtypeTypesCall(maExpr->baseExpr->ty, buddyInterfaceDecl->ty); auto buddyMa = CreateMemberAccess(ASTCloner::Clone(Ptr(maExpr->baseExpr.get())), *buddyFuncDecl); CopyBasicInfo(maExpr, buddyMa); buddyMa->EnableAttr(Attribute::GENERATED_TO_MOCK); buddyMa->ty = typeManager.GetInstantiatedTy( buddyFuncDecl->ty, GenerateTypeMapping(*buddyFuncDecl, maExpr->instTys)); buddyMa->instTys = maExpr->instTys; auto buddyCall = ASTCloner::Clone(Ptr(callExpr)); buddyCall->baseFunc = std::move(buddyMa); buddyCall->resolvedFunction = buddyFuncDecl; auto originalCall = ASTCloner::Clone(Ptr(callExpr)); originalCall->EnableAttr(Attribute::GENERATED_TO_MOCK); auto retTy = StaticCast(maExpr->ty)->retTy; auto matchExpr = CreateBoolMatch( std::move(selector), std::move(buddyCall), std::move(originalCall), retTy); callExpr->desugarExpr = std::move(matchExpr); } else if (HasDefaultInterfaceAccessor(maExpr->baseExpr->ty, buddyInterfaceDecl->ty)) { // C.foo() |-> C.foo$Buddy() auto buddyMa = CreateMemberAccess(ASTCloner::Clone(Ptr(maExpr->baseExpr.get())), *buddyFuncDecl); CopyBasicInfo(maExpr, buddyMa); buddyMa->EnableAttr(Attribute::GENERATED_TO_MOCK); buddyMa->ty = typeManager.GetInstantiatedTy( buddyFuncDecl->ty, GenerateTypeMapping(*buddyFuncDecl, maExpr->instTys)); buddyMa->instTys = maExpr->instTys; auto buddyCall = ASTCloner::Clone(Ptr(callExpr)); buddyCall->baseFunc = std::move(buddyMa); buddyCall->resolvedFunction = buddyFuncDecl; callExpr->desugarExpr = std::move(buddyCall); } } else { // a.foo() |-> // let tmp = a // match (a) { // case v : I$Buddy => v.foo$Buddy() // case _ => tmp.foo() // } auto baseExprVar = CreateTmpVarDecl( MockUtils::CreateType(maExpr->baseExpr->ty), ASTCloner::Clone(maExpr->baseExpr.get())); auto createBuddyCall = [this, &maExpr, &funcDecl = *buddyFuncDecl, callExpr](Ptr castedExpr) { auto ma = CreateMemberAccess(CreateRefExpr(*castedExpr), funcDecl); ma->ty = typeManager.GetInstantiatedTy(funcDecl.ty, GenerateTypeMapping(funcDecl, maExpr->instTys)); ma->instTys = maExpr->instTys; auto call = ASTCloner::Clone(Ptr(callExpr)); call->baseFunc = std::move(ma); call->resolvedFunction = &funcDecl; return call; }; auto originalCall = ASTCloner::Clone(Ptr(callExpr)); originalCall->EnableAttr(Attribute::GENERATED_TO_MOCK); auto retTy = StaticCast(maExpr->ty)->retTy; auto matchExpr = MockUtils::CreateTypeCast( CreateRefExpr(*baseExprVar), buddyInterfaceDecl->ty, std::move(createBuddyCall), std::move(originalCall), retTy); auto blockExpr = CreateBlock({}, callExpr->ty); blockExpr->body.emplace_back(std::move(baseExprVar)); blockExpr->body.emplace_back(std::move(matchExpr)); callExpr->desugarExpr = std::move(blockExpr); } return; } else if (auto refExpr = DynamicCast(callExpr->baseFunc.get())) { if (callExpr->resolvedFunction->TestAttr(Attribute::STATIC)) { if (!outerClassLike || HasDefaultInterfaceAccessor(outerClassLike->ty, buddyInterfaceDecl->ty)) { // foo() |-> foo$Buddy() auto buddyFuncRef = CreateRefExpr(*buddyFuncDecl); CopyBasicInfo(refExpr, buddyFuncRef); buddyFuncRef->ty = typeManager.GetInstantiatedTy( buddyFuncDecl->ty, GenerateTypeMapping(*buddyFuncDecl, refExpr->instTys)); buddyFuncRef->instTys = refExpr->instTys; auto buddyCallExpr = ASTCloner::Clone(Ptr(callExpr)); buddyCallExpr->baseFunc = std::move(buddyFuncRef); buddyCallExpr->resolvedFunction = buddyFuncDecl; callExpr->desugarExpr = std::move(buddyCallExpr); } } else { // foo() |-> match (this) { // case v : I$Buddy => v.foo$Buddy() // case _ => foo() // } auto thisExpr = CreateThisRef(*callExpr->resolvedFunction->funcBody->parentClassLike); CopyBasicInfo(refExpr, thisExpr); thisExpr->EnableAttr(Attribute::GENERATED_TO_MOCK); auto createBuddyCall = [this, refExpr, &funcDecl = *buddyFuncDecl, callExpr](Ptr castedExpr) { auto ma = CreateMemberAccess(CreateRefExpr(*castedExpr), funcDecl); ma->ty = typeManager.GetInstantiatedTy( funcDecl.ty, GenerateTypeMapping(funcDecl, refExpr->instTys)); ma->instTys = refExpr->instTys; auto call = ASTCloner::Clone(Ptr(callExpr)); call->baseFunc = std::move(ma); call->resolvedFunction = &funcDecl; return call; }; auto originalCall = ASTCloner::Clone(Ptr(callExpr)); originalCall->EnableAttr(Attribute::GENERATED_TO_MOCK); auto retTy = StaticCast(refExpr->ty)->retTy; auto matchExpr = MockUtils::CreateTypeCast( std::move(thisExpr), buddyInterfaceDecl->ty, std::move(createBuddyCall), std::move(originalCall), retTy); callExpr->desugarExpr = std::move(matchExpr); } return; } } bool MockSupportManager::HasDefaultInterfaceAccessor(Ptr declTy, Ptr accessorInterfaceDeclTy) { if (typeManager.IsSubtype(declTy, accessorInterfaceDeclTy)) { return true; } return defaultInterfaceAccessorExtends[declTy].count(accessorInterfaceDeclTy) != 0; } } cangjie_compiler-1.0.7/src/Sema/Test/MockSupportManager.h000066400000000000000000000136061510705540100233420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * MockSupportManager is the global manager to prepare classes for further possible mocking. */ #ifndef CANGJIE_SEMA_MOCK_SUPPORT_MANAGER_H #define CANGJIE_SEMA_MOCK_SUPPORT_MANAGER_H #include "cangjie/Sema/TypeManager.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Mangle/BaseMangler.h" #include "MockUtils.h" namespace Cangjie { class MockSupportManager { public: explicit MockSupportManager(TypeManager& typeManager, const Ptr mockUtils); static bool IsDeclOpenToMock(const AST::Decl& decl); static bool DoesClassLikeSupportMocking(AST::ClassLikeDecl& classLikeToCheck); static bool NeedToSearchCallsToReplaceWithAccessors(AST::Node& node); void GenerateSpyCallMarker(AST::Package& package); void GenerateAccessors(AST::Decl& decl); Ptr ReplaceExprWithAccessor( AST::Expr& originalExpr, bool isInConstructor, bool isSubMemberAccess = false); void ReplaceInterfaceDefaultFunc( AST::Expr& originalExpr, Ptr outerClassLike, bool isInMockAnnotatedLambda); void ReplaceInterfaceDefaultFuncInCall( AST::Node& node, Ptr outerClassLike, bool isInMockAnnotatedLambda); static void MarkNodeMockSupportedIfNeeded(AST::Node& node); void WriteGeneratedMockDecls(); void PrepareToSpy(AST::Decl& decl); struct DeclsToPrepare { // - Toplevel, static functions // - Instance functions from extends std::vector> functions; // Static properties std::vector> properties; // Interfaces contains any methods with default implementation std::vector> interfacesWithDefaults; // Classes along with the interfaces that it implements (directly or through extend) // with methods with default implementation std::vector, Ptr>> classWithInterfaceDefaults; }; void PrepareDecls(DeclsToPrepare&& decls); void CollectDeclsToPrepare(AST::Decl& decl, DeclsToPrepare& decls); void PrepareClassWithDefaults(AST::ClassDecl& classDecl, AST::InterfaceDecl& interfaceDecl); private: TypeManager& typeManager; Ptr mockUtils; std::set> generatedMockDecls; std::unordered_map, Ptr> genericMockVarsDecls; std::unordered_map, std::unordered_set>> defaultInterfaceAccessorExtends; bool HasDefaultInterfaceAccessor(Ptr declTy, Ptr accessorInterfaceDeclTy); static void MakeOpenToMockIfNeeded(AST::Decl& decl); static void MarkMockAccessorWithAttributes(AST::Decl& decl, AST::AccessLevel accessLevel); bool IsMemberAccessOnThis(const AST::MemberAccess& memberAccess) const; OwnedPtr GenerateErasedFuncAccessor(AST::FuncDecl& methodDecl) const; OwnedPtr GenerateFuncAccessor(AST::FuncDecl& methodDecl); OwnedPtr GeneratePropAccessor(AST::PropDecl& propDecl); std::vector> GenerateFieldGetterAccessorBody( AST::VarDecl& fieldDecl, AST::FuncBody& funcBody, AccessorKind kind) const; std::vector> GenerateFieldSetterAccessorBody( AST::VarDecl& fieldDecl, AST::FuncParam& setterParam, AST::FuncBody& funcBody, AccessorKind kind) const; OwnedPtr CreateFieldAccessorDecl( const AST::VarDecl& fieldDecl, AST::FuncTy* accessorTy, AccessorKind kind) const; OwnedPtr CreateForeignFunctionAccessorDecl(AST::FuncDecl& funcDecl) const; OwnedPtr GenerateVarDeclAccessor(AST::VarDecl& fieldDecl, AccessorKind kind); OwnedPtr GenerateAccessorCallForField(const AST::MemberAccess& memberAccess, AccessorKind kind); Ptr ReplaceFieldGetWithAccessor(AST::MemberAccess& memberAccess, bool isInConstructor); Ptr ReplaceFieldSetWithAccessor(AST::AssignExpr& assignExpr, bool isInConstructor); Ptr ReplaceMemberAccessWithAccessor(AST::MemberAccess& memberAccess, bool isInConstructor); template Ptr FindGeneratedGlobalDecl(Ptr file, const std::string& identifier); std::tuple, Ptr> FindDefaultAccessorInterfaceAndFunction( Ptr original); void TransformAccessorCallForMutOperation( AST::NameReferenceExpr& originalNre, AST::Expr& replacedNre, AST::Expr& topLevelExpr); void ReplaceSubMemberAccessWithAccessor( const AST::MemberAccess& memberAccess, bool isInConstructor, const Ptr topLevelMutExpr = nullptr); Ptr ReplaceTopLevelVariableGetWithAccessor(AST::RefExpr& refExpr); OwnedPtr GenerateAccessorCallForTopLevelVariable( const AST::RefExpr& refExpr, AccessorKind kind); void GenerateVarDeclAccessors(AST::VarDecl& fieldDecl, AccessorKind getterKind, AccessorKind setterKind); void PrepareStaticDecl(AST::Decl& decl); std::vector> GenerateHandlerMatchCases( const AST::FuncDecl& funcDecl, OwnedPtr optionFuncTyPattern, OwnedPtr handlerCallExpr); Ptr GenerateSpiedObjectVar(const AST::Decl& decl); std::vector> CloneFuncDecl(Ptr fromDecl, Ptr toDecl); void GenerateSpyCallHandler(AST::FuncDecl& funcDecl, AST::Decl& spiedObjectDecl); void PrepareInterfaceDecl(AST::InterfaceDecl& interfaceDecl); bool NeedEraseAccessorTypes(AST::Decl& decl) const; }; } // namespace Cangjie #endif // CANGJIE_SEMA_MOCK_SUPPORT_MANAGER_H cangjie_compiler-1.0.7/src/Sema/Test/MockUtils.cpp000066400000000000000000000745331510705540100220340ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * MockUtils contains helper functions for MockManager, MockSupportManager and TestManager */ #include "MockUtils.h" #include "cangjie/Sema/TestManager.h" #include "cangjie/Utils/ConstantsUtils.h" #include #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND #include "GenericInstantiation/PartialInstantiation.h" #endif #include "TypeCheckUtil.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Sema/GenericInstantiationManager.h" #include "GenericInstantiation/GenericInstantiationManagerImpl.h" namespace Cangjie { using namespace AST; using namespace TypeCheckUtil; namespace { static const std::string MOCKED_ACCESSOR_SUFFIX = "$ToMock"; static const std::string GETTER_SUFFIX = "$get"; static const std::string SETTER_SUFFIX = "$set"; static constexpr std::string_view ZERO_VALUE_INTRINSIC_NAME = "zeroValue"; } // namespace MockUtils::MockUtils(ImportManager& importManager, TypeManager& typeManager, GenericInstantiationManager* gim) : importManager(importManager), typeManager(typeManager), gim(gim), arrayDecl(importManager.GetCoreDecl(STD_LIB_ARRAY)), stringDecl(importManager.GetCoreDecl(STD_LIB_STRING)), optionDecl(importManager.GetCoreDecl(STD_LIB_OPTION)), toStringDecl(importManager.GetCoreDecl(TOSTRING_NAME)), objectDecl(importManager.GetCoreDecl(OBJECT_NAME)), zeroValueDecl(importManager.GetCoreDecl(std::string(ZERO_VALUE_INTRINSIC_NAME))), exceptionClassDecl(importManager.GetCoreDecl(CLASS_EXCEPTION)) {} std::string MockUtils::mockAccessorSuffix = MOCKED_ACCESSOR_SUFFIX; std::string MockUtils::spyObjVarName = "spiedObjectRef"; std::string MockUtils::spyCallMarkerVarName = "shouldReturnZeroForSpy"; std::string MockUtils::defaultAccessorSuffix = "$Buddy"; void MockUtils::Instantiate(Node& node) const { if (!IS_GENERIC_INSTANTIATION_ENABLED) { return; } gim->impl->RecordExtend(node); gim->impl->WalkNonGenericExtendedType(); if (Is(&node)) { gim->impl->CheckNodeInstantiation(node); } else { Walker( &node, gim->impl->instantiationWalkerID, [this](auto node) { return gim->impl->CheckNodeInstantiation(*node); }).Walk(); } } std::optional>> MockUtils::TryGetInstantiatedDecls(Decl& decl) const { if (!decl.TestAttr(AST::Attribute::GENERIC) && !decl.ty->HasGeneric()) { return std::nullopt; } return gim->impl->GetInstantiatedDecls(decl); } Ptr MockUtils::GetInstantiatedMemberTarget(Ty& baseTy, Decl& target) const { return gim->impl->GetInstantiatedMemberTarget(baseTy, target); } Ptr MockUtils::GetInstantiatedDeclWithGenericInfo(Decl& decl, const std::vector>& instTys) const { return gim->impl->GetInstantiatedDeclWithGenericInfo(gim->impl->ConstructGenericInfo(decl, instTys)); } Ptr MockUtils::GetInstantiatedDeclInCurrentPackage(const Ptr classLikeToMockTy) { if (!IS_GENERIC_INSTANTIATION_ENABLED) { return RawStaticCast(Ty::GetDeclOfTy(classLikeToMockTy)); } auto classLikeToMock = DynamicCast(classLikeToMockTy->commonDecl.get()); CJC_NULLPTR_CHECK(classLikeToMock); Ptr genericDecl; if (classLikeToMock->TestAttr(Attribute::GENERIC)) { genericDecl = classLikeToMock; } else if (classLikeToMock->genericDecl) { genericDecl = classLikeToMock->genericDecl; } else { return classLikeToMock; } auto genericInfo = gim->impl->ConstructGenericInfo(*genericDecl, typeManager.GetTypeArgs(*classLikeToMockTy)); return RawStaticCast(gim->impl->GetInstantiatedDeclWithGenericInfo(genericInfo)); } std::string MockUtils::Mangle(const Decl& decl) const { return mangler.Mangle(decl); } OwnedPtr MockUtils::WrapCallArgsIntoArray(const FuncDecl& mockedFunc) { std::vector> mockedMethodArgRefs {}; for (auto const& param : mockedFunc.funcBody->paramLists[0]->params) { auto paramRef = MakeOwned(); paramRef->ref = Reference(param->identifier); paramRef->ref.target = param.get(); paramRef->ty = param->ty; paramRef->curFile = mockedFunc.curFile; mockedMethodArgRefs.emplace_back(std::move(paramRef)); } auto baseTy = typeManager.GetStructTy(*arrayDecl, { typeManager.GetAnyTy() }); auto argRefsArray = CreateArrayLit(std::move(mockedMethodArgRefs), baseTy); AddArrayLitConstructor(*argRefsArray); argRefsArray->curFile = mockedFunc.curFile; return argRefsArray; } bool MockUtils::IsMockAccessor(const Decl& decl) { if (!Is(decl) && !Is(decl)) { return false; } if (decl.astKind == ASTKind::VAR_DECL && !decl.outerDecl) { return false; } return decl.TestAttr(Attribute::GENERATED_TO_MOCK); } bool MockUtils::IsMockAccessorRequired(const Decl& decl) { if (decl.astKind == ASTKind::VAR_DECL && decl.outerDecl) { return true; } if (decl.astKind != ASTKind::FUNC_DECL && decl.astKind != ASTKind::PROP_DECL) { return false; } if (decl.TestAttr(Attribute::OPEN) || decl.TestAttr(Attribute::ABSTRACT) || decl.TestAttr(Attribute::CONSTRUCTOR) || decl.TestAttr(Attribute::STATIC) || (decl.outerDecl && decl.outerDecl->astKind == ASTKind::INTERFACE_DECL) ) { return false; } if (IS_GENERIC_INSTANTIATION_ENABLED && (decl.TestAttr(Attribute::GENERIC_INSTANTIATED) || decl.TestAttr(Attribute::GENERIC)) ) { return true; } if (decl.TestAttr(Attribute::PUBLIC) || decl.TestAttr(Attribute::PROTECTED)) { return false; } return true; } Ptr MockUtils::FindAccessorForMemberAccess( const MemberAccess& memberAccess, const Ptr resolvedMember, const std::vector>& instTys, AccessorKind kind) const { if (!resolvedMember || !IsMockAccessorRequired(*resolvedMember)) { return nullptr; } if (IS_GENERIC_INSTANTIATION_ENABLED) { for (auto tyVar : memberAccess.instTys) { if (tyVar->HasGeneric()) { return nullptr; } } } auto baseTy = memberAccess.baseExpr->ty; auto baseDecl = Ty::GetDeclOfTy(baseTy); if (!baseDecl || !Is(baseDecl) || (IS_GENERIC_INSTANTIATION_ENABLED && baseTy->HasGeneric()) || !baseDecl->TestAttr(Attribute::MOCK_SUPPORTED) ) { return nullptr; } Ptr baseClass = As(baseDecl); Ptr outerClass; if (baseClass->TestAttr(Attribute::GENERIC)) { outerClass = GetInstantiatedDecl(baseClass, baseTy->typeArgs, IS_GENERIC_INSTANTIATION_ENABLED); } else if (baseClass->genericDecl != nullptr) { // Initially instantiated generic base type points to its original package's type // But we need to get the current package's version of that instantiated type outerClass = GetInstantiatedDecl( Ptr(As(baseClass->genericDecl)), baseTy->typeArgs, IS_GENERIC_INSTANTIATION_ENABLED); } else { outerClass = baseClass; } return FindAccessor(*outerClass, resolvedMember, instTys, kind); } Ptr MockUtils::FindTopLevelAccessor(Ptr member, AccessorKind kind) const { for (auto& decl : member->curFile->decls) { if (!IsMockAccessor(*decl) || !Is(member)) { continue; } auto originalIdentifier = GetOriginalIdentifierOfMockAccessor(*decl); if (kind == AccessorKind::TOP_LEVEL_VARIABLE_GETTER && originalIdentifier == member->identifier + GETTER_SUFFIX) { return As(decl.get()); } else if (kind == AccessorKind::TOP_LEVEL_VARIABLE_SETTER && originalIdentifier == member->identifier + SETTER_SUFFIX) { return As(decl.get()); } } return nullptr; } Ptr MockUtils::FindAccessor(Ptr ma, Ptr target, AccessorKind kind) const { Ptr accessor; if (kind == AccessorKind::FIELD_GETTER || kind == AccessorKind::FIELD_SETTER) { CJC_ASSERT(ma); accessor = FindAccessorForMemberAccess(*ma, target, {}, kind); } else if (kind == AccessorKind::TOP_LEVEL_VARIABLE_GETTER || kind == AccessorKind::TOP_LEVEL_VARIABLE_SETTER ) { accessor = FindTopLevelAccessor(target, kind); } else { CJC_ABORT(); } return As(accessor); } Ptr MockUtils::FindAccessor( ClassDecl& outerClass, const Ptr member, const std::vector>& instTys, AccessorKind kind) const { for (auto& superDecl : outerClass.GetAllSuperDecls()) { // Accessors are generated only for classes if (superDecl->astKind != ASTKind::CLASS_DECL) { continue; } for (auto& decl : superDecl->GetMemberDecls()) { if (!IsMockAccessor(*decl)) { continue; } auto originalIdentifier = GetOriginalIdentifierOfMockAccessor(*decl); if (Is(member) && originalIdentifier == member->identifier) { return decl.get(); } else if (Is(member)) { if (kind == AccessorKind::FIELD_GETTER && originalIdentifier == member->identifier + GETTER_SUFFIX) { return decl.get(); } else if (kind == AccessorKind::FIELD_SETTER && originalIdentifier == member->identifier + SETTER_SUFFIX) { return decl.get(); } } else { // For functions we search accessors by using mangled names due to overloading auto instantiatedDecl = GetInstantiatedDecl( member, instTys, IS_GENERIC_INSTANTIATION_ENABLED, outerClass.ty); auto originalMangledName = Mangle(*instantiatedDecl); auto originalMangledNameGivenFromAccessor = MangleUtils::ComputeMangledNameWithCustomIdentifier( *decl, originalIdentifier); if (originalMangledName == originalMangledNameGivenFromAccessor) { return decl.get(); } } } } return nullptr; } AccessorKind MockUtils::ComputeAccessorKind(const FuncDecl& accessorDecl) { if (accessorDecl.propDecl) { if (&accessorDecl == GetUsableGetterForProperty(*(accessorDecl.propDecl))) { if (accessorDecl.TestAttr(Attribute::STATIC)) { return AccessorKind::STATIC_PROP_GETTER; } return AccessorKind::PROP_GETTER; } else if (accessorDecl.propDecl->isVar && &accessorDecl == GetUsableSetterForProperty(*(accessorDecl.propDecl)) ) { if (accessorDecl.TestAttr(Attribute::STATIC)) { return AccessorKind::STATIC_PROP_SETTER; } return AccessorKind::PROP_SETTER; } CJC_ABORT(); } static const std::regex FIELD_GETTER_PATTERN( "^.*?\\" + GETTER_SUFFIX + "\\" + MOCKED_ACCESSOR_SUFFIX + ".*?$"); if (std::regex_match(accessorDecl.identifier.Val().c_str(), FIELD_GETTER_PATTERN)) { if (accessorDecl.TestAttr(Attribute::STATIC)) { return AccessorKind::STATIC_FIELD_GETTER; } if (accessorDecl.TestAttr(Attribute::GLOBAL)) { return AccessorKind::TOP_LEVEL_VARIABLE_GETTER; } return AccessorKind::FIELD_GETTER; } static const std::regex FIELD_SETTER_PATTERN( "^.*?\\" + SETTER_SUFFIX + "\\" + MOCKED_ACCESSOR_SUFFIX + ".*?$"); if (std::regex_match(accessorDecl.identifier.Val().c_str(), FIELD_SETTER_PATTERN)) { if (accessorDecl.TestAttr(Attribute::STATIC)) { return AccessorKind::STATIC_FIELD_SETTER; } if (accessorDecl.TestAttr(Attribute::GLOBAL)) { return AccessorKind::TOP_LEVEL_VARIABLE_SETTER; } return AccessorKind::FIELD_SETTER; } if (accessorDecl.TestAttr(Attribute::GLOBAL)) { return AccessorKind::TOP_LEVEL_FUNCTION; } if (accessorDecl.TestAttr(Attribute::STATIC)) { return AccessorKind::STATIC_METHOD; } return AccessorKind::METHOD; } bool MockUtils::IsGetterForMutField(const FuncDecl& accessorDecl) { auto accessorKind = ComputeAccessorKind(accessorDecl); if (accessorKind != AccessorKind::FIELD_GETTER && accessorKind != AccessorKind::STATIC_FIELD_GETTER && accessorKind != AccessorKind::TOP_LEVEL_VARIABLE_GETTER ) { return false; } static const auto FIELD_GETTER_REGEX = std::regex("\\" + GETTER_SUFFIX + "\\" + MOCKED_ACCESSOR_SUFFIX); auto getterDeclId = accessorDecl.identifier.Val(); auto setterDeclId = std::regex_replace( getterDeclId, FIELD_GETTER_REGEX, SETTER_SUFFIX + MOCKED_ACCESSOR_SUFFIX); if (getterDeclId == setterDeclId) { return false; } if (accessorDecl.outerDecl) { for (auto& member : accessorDecl.outerDecl->GetMemberDecls()) { if (member->identifier == setterDeclId) { return true; } } } else { for (auto& member : accessorDecl.curFile->decls) { if (member->identifier == setterDeclId) { return true; } } } return false; } std::string MockUtils::GetOriginalIdentifierOfAccessor(const FuncDecl& decl) const { auto mockSuffixTrimmedId = GetOriginalIdentifierOfMockAccessor( *(decl.propDecl ? RawStaticCast>(decl.propDecl) : Ptr(&decl))); static const auto FIELD_ACCESSOR_REGEX = std::regex("^(.*?)(\\" + GETTER_SUFFIX + "|\\" + SETTER_SUFFIX + ")?$"); return std::regex_replace(mockSuffixTrimmedId, FIELD_ACCESSOR_REGEX, "$01"); } std::string MockUtils::GetOriginalIdentifierOfMockAccessor(const Decl& decl) const { if (!IsMockAccessor(decl)) { return decl.identifier; } auto outerDeclSuffix = decl.outerDecl ? "_" + decl.outerDecl->identifier.Val() : ""; auto identifier = decl.identifier.Val().substr( 0, decl.identifier.Val().size() - (MOCKED_ACCESSOR_SUFFIX + outerDeclSuffix).length()); if (IS_GENERIC_INSTANTIATION_ENABLED && decl.astKind == ASTKind::FUNC_DECL) { auto& fd = static_cast(decl); if (fd.funcBody->generic) { identifier = identifier.substr(0, identifier.size() - BuildTypeArgumentList(decl).length()); } } return identifier; } std::string MockUtils::BuildArgumentList(const AST::Decl& decl) const { if (decl.genericDecl) { return BuildArgumentList(*decl.genericDecl); } auto funcDecl = DynamicCast(&decl); if (!funcDecl) { return ""; } auto it = funcDecl->funcBody->paramLists[0]->params.begin(); auto end = funcDecl->funcBody->paramLists[0]->params.end(); std::stringstream result; result << "("; while (it != end) { auto paramTy = (*it)->ty; if (auto paramTyDecl = Ty::GetDeclOfTy(paramTy)) { if (paramTyDecl->genericDecl) { paramTyDecl = paramTyDecl->genericDecl; } paramTy = paramTyDecl->ty; } result << Ty::ToString(paramTy); it++; if (it != end) { result << ","; } } result << ")"; return result.str(); } std::string MockUtils::BuildTypeArgumentList(const Decl& decl) { if (!IS_GENERIC_INSTANTIATION_ENABLED || decl.astKind != ASTKind::FUNC_DECL) { return ""; } auto& fd = static_cast(decl); if (!fd.funcBody->generic) { return ""; } std::string typeArgs = ""; typeArgs += "<"; size_t i = 0; for (auto& arg : fd.funcBody->generic->typeParameters) { typeArgs += Ty::ToString(arg->ty); if (i != fd.funcBody->generic->typeParameters.size() - 1) { typeArgs += ","; } i++; } typeArgs += ">"; return typeArgs; } std::string MockUtils::BuildMockAccessorIdentifier( const Decl& originalDecl, AccessorKind kind, bool includeArgumentTypes) const { std::string additionalSuffix; switch (kind) { case AccessorKind::FIELD_GETTER: case AccessorKind::STATIC_FIELD_GETTER: case AccessorKind::TOP_LEVEL_VARIABLE_GETTER: additionalSuffix = GETTER_SUFFIX; break; case AccessorKind::FIELD_SETTER: case AccessorKind::STATIC_FIELD_SETTER: case AccessorKind::TOP_LEVEL_VARIABLE_SETTER: additionalSuffix = SETTER_SUFFIX; break; default: additionalSuffix = ""; break; } auto mangledName = Mangle(originalDecl); auto outerDeclSuffix = originalDecl.outerDecl ? "_" + originalDecl.outerDecl->identifier.Val() : ""; auto argumentSuffix = includeArgumentTypes ? BuildArgumentList(originalDecl) : ""; return originalDecl.identifier + argumentSuffix + BuildTypeArgumentList(originalDecl) + additionalSuffix + MOCKED_ACCESSOR_SUFFIX + outerDeclSuffix; } bool MockUtils::IsGeneratedGetter(AccessorKind kind) { return kind == AccessorKind::FIELD_GETTER || kind == AccessorKind::STATIC_FIELD_GETTER || kind == AccessorKind::TOP_LEVEL_VARIABLE_GETTER; } Ptr MockUtils::FindMockGlobalDecl(const Decl& decl, const std::string& name) { for (auto& varDecl : decl.curFile->decls) { if (varDecl->identifier == name + MockUtils::mockAccessorSuffix) { return varDecl; } } return nullptr; } std::string MockUtils::GetForeignAccessorName(const FuncDecl& decl) { return decl.fullPackageName + "$" + decl.identifier; } OwnedPtr MockUtils::CreateGetTypeForTypeParameterCall(const Ptr genericParam) { auto funcTy = Ptr(StaticCast(getTypeForTypeParamDecl->ty)); std::vector> args; auto refExpr = CreateRefExpr(*getTypeForTypeParamDecl); refExpr->instTys.push_back(genericParam->ty); refExpr->curFile = genericParam->curFile; auto res = CreateCallExpr(std::move(refExpr), std::move(args), getTypeForTypeParamDecl, funcTy->retTy, CallKind::CALL_INTRINSIC_FUNCTION); res->curFile = genericParam->curFile; return res; } OwnedPtr MockUtils::CreateIsSubtypeTypesCall(Ptr tyToCheck, Ptr ty) { auto funcTy = Ptr(StaticCast(isSubtypeTypesDecl->ty)); std::vector> args; auto refExpr = CreateRefExpr(*isSubtypeTypesDecl); refExpr->instTys.push_back(tyToCheck); refExpr->instTys.push_back(ty); auto res = CreateCallExpr(std::move(refExpr), std::move(args), isSubtypeTypesDecl, funcTy->retTy, CallKind::CALL_INTRINSIC_FUNCTION); return res; } OwnedPtr MockUtils::WrapCallTypeArgsIntoArray(const Decl& decl) { std::vector> getTypeCalls; if (auto outerDecl = decl.outerDecl; outerDecl) { if (auto generic = outerDecl->GetGeneric()) { for (auto& genericParam : generic->typeParameters) { getTypeCalls.emplace_back(CreateGetTypeForTypeParameterCall(genericParam)); } } } if (auto generic = decl.GetGeneric(); generic) { for (auto& genericParam : generic->typeParameters) { getTypeCalls.emplace_back(CreateGetTypeForTypeParameterCall(genericParam)); } } auto baseTy = typeManager.GetStructTy(*arrayDecl, { toStringDecl->ty }); auto arrayLitOfGetTypeCalls = CreateArrayLit(std::move(getTypeCalls), baseTy); AddArrayLitConstructor(*arrayLitOfGetTypeCalls); arrayLitOfGetTypeCalls->curFile = decl.curFile; return arrayLitOfGetTypeCalls; } Ptr MockUtils::GetExtendedClassDecl(FuncDecl& decl) const { CJC_ASSERT(decl.TestAttr(Attribute::IN_EXTEND)); auto outerDecl = decl.outerDecl; CJC_NULLPTR_CHECK(outerDecl); Ptr extendDecl = As(outerDecl); CJC_NULLPTR_CHECK(extendDecl); Ptr extendedRefType = As(extendDecl->extendedType); CJC_NULLPTR_CHECK(extendedRefType); CJC_NULLPTR_CHECK(extendedRefType->ref.target); Ptr classLikeDecl = As(extendedRefType->ref.target); CJC_NULLPTR_CHECK(classLikeDecl); return classLikeDecl; } void MockUtils::PrependFuncGenericSubst( const Ptr originalGeneric, const Ptr mockedGeneric, std::vector& classSubsts) { if (!originalGeneric || !mockedGeneric) { return; } CJC_ASSERT(originalGeneric->typeParameters.size() == mockedGeneric->typeParameters.size()); if (originalGeneric->typeParameters.empty() && classSubsts.empty()) { return; } TypeSubst subst; std::vector>::size_type i = 0; for (auto& typeParam : originalGeneric->typeParameters) { subst[DynamicCast(typeParam->ty)] = mockedGeneric->typeParameters[i]->ty; i++; } classSubsts.push_back(subst); std::rotate(classSubsts.rbegin(), classSubsts.rbegin() + 1, classSubsts.rend()); } Ptr MockUtils::GetInstantiatedTy(const Ptr ty, std::vector& typeSubsts) { auto substitutedTy = ty; for (auto typeSubst : typeSubsts) { substitutedTy = typeManager.GetInstantiatedTy(substitutedTy, typeSubst); } return substitutedTy; } std::vector MockUtils::BuildGenericSubsts(const Ptr decl) { if (IS_GENERIC_INSTANTIATION_ENABLED) { return {}; } std::vector genericSubsts; std::queue> workList; workList.push(decl); while (!workList.empty()) { auto curDecl = workList.front(); workList.pop(); for (auto& inheritedType : curDecl->inheritedTypes) { if (inheritedType->ty == curDecl->ty || !inheritedType->ty->HasGeneric()) { continue; } if (auto inheritedDecl = DynamicCast(Ty::GetDeclPtrOfTy(inheritedType->ty)); inheritedDecl ) { genericSubsts.emplace_back(GenerateTypeMapping(*inheritedDecl, inheritedType->ty->typeArgs)); workList.emplace(inheritedDecl); } } } std::reverse(genericSubsts.begin(), genericSubsts.end()); return genericSubsts; } int MockUtils::GetIndexOfGenericTypeParam(Ptr ty, Ptr generic) const { int i = 0; for (auto& typeParam : generic->typeParameters) { if (typeParam->ty == ty) { return i; } i++; } return -1; } void MockUtils::UpdateRefTypesTarget(Ptr type, Ptr oldGeneric, Ptr newGeneric) const { auto refType = As(type); if (!refType) { return; } if (auto genericTy = DynamicCast(refType->ty); genericTy) { auto typeParamIndex = GetIndexOfGenericTypeParam(genericTy, oldGeneric); if (typeParamIndex != -1) { refType->ref.target = newGeneric->typeParameters[typeParamIndex].get(); } } for (auto& typeArg : refType->typeArguments) { UpdateRefTypesTarget(typeArg.get(), oldGeneric, newGeneric); } } std::vector> MockUtils::AddGenericIfNeeded(Decl& originalDecl, Decl& mockedDecl) const { if (!originalDecl.TestAttr(Attribute::GENERIC)) { return {}; } mockedDecl.EnableAttr(Attribute::GENERIC); auto originalFuncDecl = As(&originalDecl); Ptr generic; if (originalFuncDecl) { generic = originalFuncDecl->funcBody->generic.get(); } else { generic = originalDecl.generic.get(); } std::vector> typeParamTys {}; auto newGeneric = CloneGeneric(*generic); for (auto& typeParam : newGeneric->typeParameters) { typeParam->outerDecl = &mockedDecl; typeParam->ty = typeManager.GetGenericsTy(*typeParam); typeParamTys.emplace_back(typeParam->ty); } if (originalFuncDecl) { auto mockedFuncDecl = As(&mockedDecl); CJC_ASSERT(mockedFuncDecl && mockedFuncDecl->funcBody); UpdateRefTypesTarget(mockedFuncDecl->funcBody->retType.get(), generic.get(), newGeneric.get()); mockedFuncDecl->funcBody->generic = std::move(newGeneric); } else { mockedDecl.generic = std::move(newGeneric); if (auto classDecl = As(&mockedDecl); classDecl) { mockedDecl.ty = typeManager.GetClassTy(*classDecl, std::move(typeParamTys)); } else if (auto interfaceDecl = As(&mockedDecl); interfaceDecl) { mockedDecl.ty = typeManager.GetInterfaceTy(*interfaceDecl, std::move(typeParamTys)); } } return typeParamTys; } void MockUtils::SetGetTypeForTypeParamDecl(Package& pkg) { getTypeForTypeParamDecl = GenerateGetTypeForTypeParamIntrinsic(pkg, typeManager, stringDecl->ty); } void MockUtils::SetIsSubtypeTypes(Package& pkg) { isSubtypeTypesDecl = GenerateIsSubtypeTypesIntrinsic(pkg, typeManager); } OwnedPtr MockUtils::CreateZeroValue(Ptr ty, File& curFile) const { auto zeroValueCall = MakeOwned(); zeroValueCall->baseFunc = CreateDeclBasedReferenceExpr( *zeroValueDecl, { ty }, std::string(ZERO_VALUE_INTRINSIC_NAME), curFile); zeroValueCall->ty = ty; zeroValueCall->callKind = CallKind::CALL_INTRINSIC_FUNCTION; zeroValueCall->resolvedFunction = zeroValueDecl; zeroValueCall->curFile = &curFile; return zeroValueCall; } OwnedPtr MockUtils::CreateRefExprWithInstTys( Decl& target, const std::vector>& instTys, const std::string& refName, File& curFile) const { auto refExpr = CreateRefExpr(target); refExpr->ref.identifier = refName; refExpr->curFile = &curFile; refExpr->ty = typeManager.GetInstantiatedTy(target.ty, GenerateTypeMapping(target, instTys)); refExpr->instTys = instTys; return refExpr; } OwnedPtr MockUtils::CreateDeclBasedReferenceExpr( Decl& target, const std::vector>& instTys, const std::string& refName, File& curFile) const { Ptr ty = nullptr; switch (target.astKind) { case ASTKind::FUNC_DECL: case ASTKind::VAR_DECL: { ty = typeManager.GetFunctionTy(std::vector>(instTys.cbegin(), instTys.cend() - 1), instTys.back()); break; } case ASTKind::CLASS_DECL: { ty = typeManager.GetClassTy(*StaticAs(&target), instTys); break; } default: break; } return CreateRefExprWithInstTys(target, ty->typeArgs, refName, curFile); } OwnedPtr MockUtils::CreateThrowExpr(const std::string& message, Ptr curFile) { std::vector> exceptionCallArgs; exceptionCallArgs.emplace_back(CreateLitConstExpr(LitConstKind::STRING, message, stringDecl->ty)); exceptionCallArgs.back()->curFile = curFile; return CreateThrowException(*exceptionClassDecl, std::move(exceptionCallArgs), *curFile, typeManager); } OwnedPtr MockUtils::CreateTypeCast( OwnedPtr selector, Ptr castTy, std::function(Ptr)> createMatchedBranch, OwnedPtr otherwiseBranch, Ptr ty) { auto castType = MockUtils::CreateType(castTy); auto varPatternForTypeCast = CreateVarPattern(V_COMPILER, castTy); auto matchedBranch = createMatchedBranch(varPatternForTypeCast->varDecl); std::vector> matchCasesTypeCast; auto typePattern = CreateTypePattern(std::move(varPatternForTypeCast), std::move(castType), *selector); typePattern->curFile = selector->curFile; typePattern->matchBeforeRuntime = false; matchCasesTypeCast.emplace_back(CreateMatchCase(std::move(typePattern), std::move(matchedBranch))); matchCasesTypeCast.emplace_back(CreateMatchCase(MakeOwned(), std::move(otherwiseBranch))); return CreateMatchExpr(std::move(selector), std::move(matchCasesTypeCast), ty); } OwnedPtr MockUtils::CreateTypeCastOrThrow( OwnedPtr selector, Ptr castTy, const std::string& message) { auto createValueBranch = [](Ptr varDecl) { return CreateRefExpr(*varDecl); }; auto throwExpression = CreateThrowExpr(message, selector->curFile); return CreateTypeCast( std::move(selector), castTy, std::move(createValueBranch), std::move(throwExpression), castTy); } OwnedPtr MockUtils::CreateTypeCastOrZeroValue(OwnedPtr selector, Ptr castTy) const { auto createValueBranch = [](Ptr varDecl) { return CreateRefExpr(*varDecl); }; auto throwExpression = CreateZeroValue(castTy, *selector->curFile); return CreateTypeCast( std::move(selector), castTy, std::move(createValueBranch), std::move(throwExpression), castTy); } Ptr MockUtils::EraseFuncTypes(Ptr funcTy) { std::vector> paramTys; for ([[maybe_unused]] auto& paramTy : funcTy->paramTys) { paramTys.push_back(typeManager.GetAnyTy()); } return typeManager.GetFunctionTy(paramTys, typeManager.GetAnyTy()); } namespace { struct InternalTypesChecker { bool Check(Ptr ty) { if (visitedGenerics.count(ty) > 0) { return false; } if (auto decl = Ty::GetDeclOfTy(ty)) { if (decl->linkage == Linkage::INTERNAL) { return true; } if (decl->outerDecl && Check(decl->outerDecl->ty)) { return true; } } for (auto paramTy : ty->typeArgs) { if (Check(paramTy)) { return true; } } if (auto genericTy = DynamicCast(ty)) { visitedGenerics.insert(ty); for (auto upperBound : genericTy->upperBounds) { if (Check(upperBound)) { return true; } } } return false; } std::unordered_set> visitedGenerics; }; } // namespace bool MockUtils::MayContainInternalTypes(Ptr ty) const { return InternalTypesChecker{}.Check(ty); } } // namespace Cangjie cangjie_compiler-1.0.7/src/Sema/Test/MockUtils.h000066400000000000000000000210111510705540100214600ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the TypeManager related classes, which manages all types. */ #ifndef CANGJIE_SEMA_MOCK_UTILS_H #define CANGJIE_SEMA_MOCK_UTILS_H #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Mangle/BaseMangler.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Sema/GenericInstantiationManager.h" namespace Cangjie { enum class AccessorKind : uint8_t { FIELD_GETTER, FIELD_SETTER, PROP, PROP_GETTER, PROP_SETTER, METHOD, TOP_LEVEL_FUNCTION, STATIC_METHOD, STATIC_PROP_GETTER, STATIC_PROP_SETTER, STATIC_FIELD_GETTER, STATIC_FIELD_SETTER, TOP_LEVEL_VARIABLE_GETTER, TOP_LEVEL_VARIABLE_SETTER }; class MockUtils { public: explicit MockUtils(ImportManager& importManager, TypeManager& typeManager, GenericInstantiationManager* gim); static bool IsMockAccessor(const AST::Decl& decl); template static OwnedPtr CreateType(const Ptr ty) { auto type = MakeOwned(); type->ty = ty; return type; } static std::string mockAccessorSuffix; static std::string spyObjVarName; static std::string spyCallMarkerVarName; static std::string defaultAccessorSuffix; template static Ptr FindGlobalDecl(Ptr file, const std::string& identifier) { for (auto& decl : file->decls) { if (decl->identifier == identifier) { return DynamicCast(decl.get()); } } return nullptr; } template static Ptr FindMemberDecl(AST::Decl& decl, const std::string& identifier) { for (auto& member : decl.GetMemberDecls()) { if (member->identifier == identifier) { return DynamicCast(member.get()); } } return nullptr; } /** * throw Exception([message]) **/ OwnedPtr CreateThrowExpr(const std::string& message, Ptr curFile); /** * match ([selector]) { * case v : [castTy] => [createMatchedBranch($v)] * case _ => [otherwiseBranch] * } */ static OwnedPtr CreateTypeCast( OwnedPtr selector, Ptr castTy, std::function(Ptr)> createMatchedBranch, OwnedPtr otherwiseBranch, Ptr ty); /** * match ([selector]) { * case v : [castTy] => v * case _ => throw Exception([message]) * } */ OwnedPtr CreateTypeCastOrThrow( OwnedPtr selector, Ptr castTy, const std::string& message); /** * match ([selector]) { * case v : [castTy] => v * case _ => zerValue<[castTy]>() * } */ OwnedPtr CreateTypeCastOrZeroValue(OwnedPtr selector, Ptr castTy) const; /** * Replaces all argument's types and return type with Any */ Ptr EraseFuncTypes(Ptr funcTy); std::string BuildMockAccessorIdentifier( const AST::Decl& originalDecl, AccessorKind kind, bool includeArgumentTypes = false) const; std::string BuildArgumentList(const AST::Decl& decl) const; std::string GetOriginalIdentifierOfAccessor(const AST::FuncDecl& decl) const; std::string GetOriginalIdentifierOfMockAccessor(const AST::Decl& decl) const; bool MayContainInternalTypes(Ptr ty) const; private: ImportManager& importManager; TypeManager& typeManager; GenericInstantiationManager* gim {nullptr}; BaseMangler mangler; Ptr getTypeForTypeParamDecl = nullptr; Ptr isSubtypeTypesDecl = nullptr; Ptr arrayDecl; Ptr stringDecl; Ptr optionDecl; Ptr toStringDecl; Ptr objectDecl; Ptr zeroValueDecl; Ptr exceptionClassDecl; static bool IsMockAccessorRequired(const AST::Decl& decl); static std::string BuildTypeArgumentList(const AST::Decl& decl); static AccessorKind ComputeAccessorKind(const AST::FuncDecl& accessorDecl); static bool IsGetterForMutField(const AST::FuncDecl& accessorDecl); static Ptr FindMockGlobalDecl(const AST::Decl& decl, const std::string& name); static void PrependFuncGenericSubst( const Ptr originalGeneric, const Ptr mockedGeneric, std::vector& classSubsts); static std::vector BuildGenericSubsts(const Ptr decl); static std::string GetForeignAccessorName(const AST::FuncDecl& decl); Ptr FindAccessor(AST::ClassDecl& outerClass, const Ptr member, const std::vector>& instTys, AccessorKind kind) const; Ptr FindAccessorForMemberAccess(const AST::MemberAccess& memberAccess, const Ptr resolvedMember, const std::vector>& instTys, AccessorKind kind) const; Ptr FindTopLevelAccessor(Ptr member, AccessorKind kind) const; OwnedPtr WrapCallTypeArgsIntoArray(const AST::Decl& decl); bool IsGeneratedGetter(AccessorKind kind); Ptr FindAccessor(Ptr ma, Ptr target, AccessorKind kind) const; std::vector> AddGenericIfNeeded(AST::Decl& originalDecl, AST::Decl& mockedDecl) const; OwnedPtr WrapCallArgsIntoArray(const AST::FuncDecl& mockedFunc); Ptr GetInstantiatedTy(const Ptr ty, std::vector& typeSubsts); void SetGetTypeForTypeParamDecl(AST::Package& pkg); void SetIsSubtypeTypes(AST::Package& pkg); OwnedPtr CreateGetTypeForTypeParameterCall(const Ptr genericParam); OwnedPtr CreateIsSubtypeTypesCall(Ptr tyToCheck, Ptr ty); std::string Mangle(const AST::Decl& decl) const; OwnedPtr CreateRefExprWithInstTys( AST::Decl& target, const std::vector>& instTys, const std::string& refName, AST::File& curFile) const; OwnedPtr CreateDeclBasedReferenceExpr( AST::Decl& target, const std::vector>& instTys, const std::string& refName, AST::File& curFile ) const; OwnedPtr CreateZeroValue(Ptr ty, AST::File& curFile) const; template Ptr GetGenericDecl(Ptr decl) const { if (decl->genericDecl) { return StaticCast(decl->genericDecl); } return decl; } // Type instantiation helpers void Instantiate(AST::Node& node) const; Ptr GetInstantiatedDeclInCurrentPackage(const Ptr classLikeToMockTy); std::optional>> TryGetInstantiatedDecls(AST::Decl& decl) const; Ptr GetInstantiatedMemberTarget(AST::Ty& baseTy, AST::Decl& target) const; Ptr GetInstantiatedDeclWithGenericInfo(AST::Decl& decl, const std::vector>& instTys) const; template Ptr GetInstantiatedDecl( Ptr decl, const std::vector>& instTys, bool isInstEnabled, Ptr baseTy = nullptr) const { if (!isInstEnabled || (!decl->ty->HasGeneric() && !decl->TestAttr(AST::Attribute::GENERIC))) { return decl; } auto memberDeclInInstantiatedClass = baseTy ? GetInstantiatedMemberTarget(*baseTy, *decl) : decl; if (!memberDeclInInstantiatedClass->TestAttr(AST::Attribute::GENERIC)) { return Ptr(RawStaticCast(memberDeclInInstantiatedClass)); } return Ptr(RawStaticCast(GetInstantiatedDeclWithGenericInfo(*memberDeclInInstantiatedClass, instTys))); } Ptr GetExtendedClassDecl(AST::FuncDecl& decl) const; void UpdateRefTypesTarget( Ptr type, Ptr oldGeneric, Ptr newGeneric) const; int GetIndexOfGenericTypeParam(Ptr ty, Ptr generic) const; friend class TestManager; friend class MockManager; friend class MockSupportManager; }; } #endif // CANGJIE_SEMA_MOCK_UTILS_H cangjie_compiler-1.0.7/src/Sema/Test/TestManager.cpp000066400000000000000000000660711510705540100223320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the TypeManager related classes. */ #include "cangjie/Sema/TestManager.h" #include #include "Desugar/AfterTypeCheck.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Match.h" #include "cangjie/Sema/GenericInstantiationManager.h" #include "GenericInstantiation/GenericInstantiationManagerImpl.h" #include "GenericInstantiation/PartialInstantiation.h" #include "cangjie/AST/Clone.h" #include "cangjie/Driver/DriverOptions.h" #include "MockManager.h" #include "MockSupportManager.h" #include "MockUtils.h" namespace Cangjie { using namespace AST; using namespace TypeCheckUtil; static const std::string MOCK_ON_COMPILATION_OPTION = "--mock=on"; static const std::string TEST_COMPILATION_OPTION = "--test"; Ptr DeparenthesizeExpr(Ptr expr) { if (auto parenExpr = As(expr); parenExpr) { return DeparenthesizeExpr(parenExpr->expr); } else { return expr; } } bool IsAnyTypeParamUsedInTypeArgs( std::vector>& typeParams, std::vector>& typeArgs) { for (auto& typeParam : typeParams) { for (auto& typeArg : std::as_const(typeArgs)) { if (typeArg->ty->Contains(typeParam->ty)) { return true; } } } return false; } TestManager::TestManager( ImportManager& im, TypeManager& tm, DiagnosticEngine& diag, const GlobalOptions& compilationOptions) : importManager(im), typeManager(tm), diag(diag), testEnabled(compilationOptions.enableCompileTest), mockMode(compilationOptions.mock), mockCompatibleIfNeeded(testEnabled && mockMode == MockMode::DEFAULT), mockCompatible(mockCompatibleIfNeeded || mockMode == MockMode::ON) #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND , exportForTest(compilationOptions.exportForTest) #endif {} void TestManager::ReportDoesntSupportMocking( const Expr& reportOn, const std::string& name, const std::string& package) { diag.DiagnoseRefactor( DiagKindRefactor::sema_mock_doesnt_support_mocking, reportOn, name, package, MOCK_ON_COMPILATION_OPTION); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void TestManager::ReportFrozenRequired(const FuncDecl& reportOn) { diag.DiagnoseRefactor(DiagKindRefactor::sema_mock_frozen_required, reportOn, reportOn.identifier); } #endif void TestManager::ReportUnsupportedType(const Expr& reportOn) { diag.DiagnoseRefactor(DiagKindRefactor::sema_mock_unsupported_type, reportOn); } void TestManager::ReportNotInTestMode(const Expr& reportOn) { diag.DiagnoseRefactor(DiagKindRefactor::sema_mock_not_in_test_mode, reportOn, TEST_COMPILATION_OPTION); } void TestManager::ReportMockDisabled(const Expr& reportOn) { diag.DiagnoseRefactor(DiagKindRefactor::sema_mock_disabled, reportOn, MOCK_ON_COMPILATION_OPTION); } void TestManager::ReportWrongStaticDecl(const Expr& reportOn) { diag.DiagnoseRefactor( DiagKindRefactor::sema_mock_wrong_static_decl, reportOn); } bool TestManager::IsDeclOpenToMock(const Decl& decl) { return MockSupportManager::IsDeclOpenToMock(decl); } bool TestManager::IsDeclGeneratedForTest(const Decl& decl) { return MockUtils::IsMockAccessor(decl) || MockManager::IsMockClass(decl); } bool TestManager::IsMockAccessor(const Decl& decl) { return MockUtils::IsMockAccessor(decl); } bool IsLocalDecl(const Decl& decl) { return (decl.outerDecl == nullptr || !decl.outerDecl->IsNominalDecl()) && !decl.TestAttr(Attribute::GLOBAL); } VisitAction TestManager::HandleCreateMockCall(CallExpr& callExpr, Package& pkg) { bool isMockCall = MockManager::IsMockCall(callExpr); if (!isMockCall || (callExpr.ty && callExpr.ty->HasGeneric())) { if (isMockCall) { callExpr.desugarExpr = MockManager::CreateIllegalMockCallException(*callExpr.curFile, typeManager, importManager); } return VisitAction::WALK_CHILDREN; } // The first type argument is a declaration to mock auto typeArgument = callExpr.baseFunc->ty->typeArgs[1]; if (!typeArgument->IsClass() && !typeArgument->IsInterface()) { ReportUnsupportedType(callExpr); return VisitAction::SKIP_CHILDREN; } if (mockCompatible && testEnabled) { auto mockClass = GenerateMockClassIfNeededAndGet(callExpr, pkg); if (!mockClass) { return VisitAction::WALK_CHILDREN; } std::vector> valueParamTys; valueParamTys.emplace_back(callExpr.args[0]->ty); if (MockManager::GetMockKind(callExpr) == MockKind::SPY) { valueParamTys.emplace_back(mockClass->ty); } callExpr.desugarExpr = MockManager::CreateInitCallOfMockClass( *mockClass, callExpr.args, typeManager, typeArgument->typeArgs, valueParamTys); } else if (mockMode == MockMode::RUNTIME_ERROR && testEnabled) { callExpr.desugarExpr = MockManager::CreateIllegalMockCallException( *callExpr.curFile, typeManager, importManager); } else if (!testEnabled) { ReportNotInTestMode(callExpr); } else { ReportMockDisabled(callExpr); } return VisitAction::WALK_CHILDREN; } namespace { bool ShouldHandleMockAnnotatedLambdaValue(Ptr target) { bool isInExtend = target->TestAttr(Attribute::IN_EXTEND); bool isInInterfaceWithDefault = target->outerDecl && target->outerDecl->astKind == ASTKind::INTERFACE_DECL && target->TestAttr(Attribute::DEFAULT); return isInExtend || isInInterfaceWithDefault || target->IsStaticOrGlobal(); } } // namespace void TestManager::WrapWithRequireMockObjectIfNeeded(Ptr expr, Ptr target) { // For non-static/non-global decls, generate an assertion that their receiver is a real mock object if (!target->IsStaticOrGlobal()) { auto callExpr = As(expr->desugarExpr ? expr->desugarExpr : expr); // After preparing decls and calls in MockSupportManager, // all exprs inside @EnsurePreparedToMock-marked lambda // should be represented as a call expr (either direct calling or through func accessor) CJC_ASSERT(callExpr); auto ma = As(callExpr->baseFunc); // After desugaring, baseFunc for member decls should be always member access expression CJC_ASSERT(ma); mockManager->WrapWithRequireMockObject(*ma->baseExpr.get()); } } VisitAction TestManager::HandleMockAnnotatedLambda(const LambdaExpr& lambda) { if (!lambda.TestAttr(Attribute::MOCK_SUPPORTED) || (mockMode == MockMode::RUNTIME_ERROR && testEnabled)) { return VisitAction::WALK_CHILDREN; } if (!testEnabled) { ReportNotInTestMode(lambda); return VisitAction::WALK_CHILDREN; } else if (!mockCompatible) { ReportMockDisabled(lambda); return VisitAction::WALK_CHILDREN; } auto lastExpr = As(lambda.funcBody->body->GetLastExprOrDecl()); if (!lastExpr) { return VisitAction::WALK_CHILDREN; } auto expr = DeparenthesizeExpr(lastExpr->expr); Ptr lastExprTarget = nullptr; if (auto assignExpr = As(expr); assignExpr) { lastExprTarget = assignExpr->leftValue->GetTarget(); } else if (auto callExpr = As(expr); callExpr) { lastExprTarget = callExpr->resolvedFunction; } else { lastExprTarget = expr->GetTarget(); } if (!lastExprTarget) { return VisitAction::WALK_CHILDREN; } if (lastExprTarget->TestAnyAttr(Attribute::PRIVATE, Attribute::CONSTRUCTOR) || IsLocalDecl(*lastExprTarget) || lastExprTarget->IsConst() || (lastExprTarget->outerDecl && lastExprTarget->outerDecl->TestAttr(Attribute::PRIVATE)) ) { ReportWrongStaticDecl(lambda); return VisitAction::WALK_CHILDREN; } WrapWithRequireMockObjectIfNeeded(expr, lastExprTarget); if (!ShouldHandleMockAnnotatedLambdaValue(lastExprTarget)) { return VisitAction::WALK_CHILDREN; } if (!lastExprTarget->TestAttr(Attribute::MOCK_SUPPORTED)) { ReportDoesntSupportMocking(*expr, lastExprTarget->identifier, lastExprTarget->fullPackageName); return VisitAction::SKIP_CHILDREN; } mockManager->HandleMockAnnotatedLambdaValue(*expr); return VisitAction::WALK_CHILDREN; } void TestManager::HandleMockCalls(Package& pkg) { Walker(&pkg, Walker::GetNextWalkerID(), [this, &pkg](auto node) { if (!node->IsSamePackage(pkg)) { return VisitAction::WALK_CHILDREN; } if (auto callExpr = As(node); callExpr) { return HandleCreateMockCall(*callExpr, pkg); } else if (auto lambda = As(node); lambda) { return HandleMockAnnotatedLambda(*lambda); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (auto funcDecl = As(node); funcDecl && funcDecl->TestAttr(Attribute::CONTAINS_MOCK_CREATION_CALL) && funcDecl->funcBody && funcDecl->funcBody->generic && !funcDecl->HasAnno(AnnotationKind::FROZEN)) { ReportFrozenRequired(*funcDecl); } #endif return VisitAction::WALK_CHILDREN; }).Walk(); if (mockCompatible && testEnabled) { mockManager->WriteGeneratedClasses(); } } Ptr TestManager::GenerateMockClassIfNeededAndGet(const CallExpr& callExpr, Package& pkg) { auto typeArgument = callExpr.baseFunc->ty->typeArgs[1]; if (!typeArgument->IsClass() && !typeArgument->IsInterface()) { diag.DiagnoseRefactor(DiagKindRefactor::sema_mock_unsupported_type, callExpr); return nullptr; } auto declToMock = mockUtils->GetInstantiatedDeclInCurrentPackage( DynamicCast(typeArgument.get())); if (MockSupportManager::DoesClassLikeSupportMocking(*declToMock)) { auto [classDecl, generated] = mockManager->GenerateMockClassIfNeededAndGet( *declToMock, pkg, MockManager::GetMockKind(callExpr)); if (generated) { CJC_ASSERT(classDecl); if (auto ifaceDecl = DynamicCast(declToMock)) { mockSupportManager->PrepareClassWithDefaults(*classDecl, *ifaceDecl); mockSupportManager->WriteGeneratedMockDecls(); } } return classDecl; } else { auto packageName = declToMock->genericDecl ? declToMock->genericDecl->fullPackageName : declToMock->fullPackageName; ReportDoesntSupportMocking(callExpr, Ty::ToString(typeArgument), packageName); return nullptr; } } namespace { bool ShouldPrepareDecl(Node& node, const Package& pkg) { if (!node.curFile) { return false; } if (node.curFile->curPackage != &pkg) { if (auto decl = As(&node); decl && decl->genericDecl && decl->genericDecl->TestAttr(Attribute::MOCK_SUPPORTED)) { return true; } return false; } else { if (auto decl = As(&node); decl && decl->genericDecl) { // Not preparing instantiated decls from current package // Will prepare them when encounter their generic decl return false; } } return true; } } void TestManager::PrepareDecls(Package& pkg) { CJC_ASSERT(mockSupportManager); MockSupportManager::DeclsToPrepare decls; Walker(&pkg, Walker::GetNextWalkerID(), [this, &pkg, &decls](auto node) { if (!node->curFile) { return VisitAction::WALK_CHILDREN; } if (!ShouldPrepareDecl(*node, pkg)) { return VisitAction::SKIP_CHILDREN; } if (auto decl = As(node); decl) { mockSupportManager->CollectDeclsToPrepare(*decl, decls); return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }).Walk(); mockSupportManager->PrepareDecls(std::move(decls)); mockSupportManager->WriteGeneratedMockDecls(); } void TestManager::GenerateAccessors(Package& pkg) { CJC_ASSERT(mockSupportManager); Walker(&pkg, Walker::GetNextWalkerID(), [this, &pkg](auto node) { if (!node->IsSamePackage(pkg) || Is(node)) { return VisitAction::SKIP_CHILDREN; } auto decl = As(node); if (!decl) { return VisitAction::WALK_CHILDREN; } /* Don't generate accessors for instantiated versions * if the original generic declaration wasn't compiled with mocking support * othewise we cannot guarantee that all other instantiated versions * including from other packages' ones would be compatible with this declaration with accessors */ if (decl->genericDecl && !decl->genericDecl->TestAttr(Attribute::MOCK_SUPPORTED)) { return VisitAction::SKIP_CHILDREN; } // Don't generate accessors for generics themthelves, do it for instantiated versions if (IS_GENERIC_INSTANTIATION_ENABLED && decl->TestAttr(Attribute::GENERIC)) { return VisitAction::SKIP_CHILDREN; } mockSupportManager->GenerateAccessors(*decl); return VisitAction::SKIP_CHILDREN; }).Walk(); mockSupportManager->WriteGeneratedMockDecls(); } void TestManager::PrepareToSpy(Package& pkg) { CJC_ASSERT(mockSupportManager); mockSupportManager->GenerateSpyCallMarker(pkg); Walker(&pkg, Walker::GetNextWalkerID(), [this, &pkg](auto node) { if (!node->IsSamePackage(pkg) || Is(node)) { return VisitAction::SKIP_CHILDREN; } auto decl = As(node); if (!decl) { return VisitAction::WALK_CHILDREN; } if (decl->genericDecl && !decl->genericDecl->TestAttr(Attribute::MOCK_SUPPORTED)) { return VisitAction::SKIP_CHILDREN; } if (IS_GENERIC_INSTANTIATION_ENABLED && decl->TestAttr(Attribute::GENERIC)) { return VisitAction::SKIP_CHILDREN; } if (decl->curFile && decl->curFile->curPackage->fullPackageName == pkg.fullPackageName) { mockSupportManager->PrepareToSpy(*decl); } return VisitAction::SKIP_CHILDREN; }).Walk(); mockSupportManager->WriteGeneratedMockDecls(); } void TestManager::ReplaceCallsToForeignFunctions(Package& pkg) { CJC_ASSERT(mockSupportManager); Walker(&pkg, Walker::GetNextWalkerID(), [](const Ptr node) { auto declNode = As(node.get()); if (declNode && declNode->TestAttr(Attribute::GENERATED_TO_MOCK)) { return VisitAction::SKIP_CHILDREN; } Ptr refNode = As(node.get()); if (!refNode) { return VisitAction::WALK_CHILDREN; } auto target = refNode->ref.target; Ptr funcDecl = As(target); if (!funcDecl || !funcDecl->TestAttr(Attribute::FOREIGN)) { return VisitAction::SKIP_CHILDREN; } Ptr accessorDecl = MockUtils::FindMockGlobalDecl(*funcDecl, MockUtils::GetForeignAccessorName(*funcDecl)); if (!accessorDecl) { return VisitAction::SKIP_CHILDREN; } CJC_ASSERT(Is(accessorDecl)); refNode->ref = Reference(accessorDecl->identifier); refNode->ref.target = accessorDecl; if (Ptr callNode = As(refNode->callOrPattern)) { callNode->resolvedFunction = StaticCast(accessorDecl); } return VisitAction::SKIP_CHILDREN; }).Walk(); } namespace { bool IsMockAnnotedLambda(Ptr node) { return node->astKind == ASTKind::LAMBDA_EXPR && node->TestAttr(Attribute::MOCK_SUPPORTED); } } // namespace void TestManager::ReplaceCallsWithAccessors(Package& pkg) { CJC_ASSERT(mockSupportManager); bool isInConstructor = false; bool isInMockAnnotatedLambda = false; Ptr outerClassLike; Walker(&pkg, Walker::GetNextWalkerID(), [this, &isInConstructor, &isInMockAnnotatedLambda, &outerClassLike, &pkg](const Ptr node) { if (node->astKind == ASTKind::PRIMARY_CTOR_DECL) { // Primary init has been already desugared to regular init return VisitAction::SKIP_CHILDREN; } if (IsMockAnnotedLambda(node)) { isInMockAnnotatedLambda = true; } if (auto classLikeDecl = DynamicCast(node)) { CJC_ASSERT(!outerClassLike); outerClassLike = classLikeDecl; } if ((node->curFile && !node->IsSamePackage(pkg))) { return VisitAction::SKIP_CHILDREN; } if (IS_GENERIC_INSTANTIATION_ENABLED && (node->TestAttr(Attribute::GENERIC) || (node->ty && node->ty->HasGeneric()))) { return VisitAction::SKIP_CHILDREN; } if (node->TestAttr(Attribute::CONSTRUCTOR)) { isInConstructor = true; return VisitAction::WALK_CHILDREN; } if (!MockSupportManager::NeedToSearchCallsToReplaceWithAccessors(*node)) { return VisitAction::SKIP_CHILDREN; } if (auto expr = As(node); expr) { mockSupportManager->ReplaceExprWithAccessor(*expr, isInConstructor); mockSupportManager->ReplaceInterfaceDefaultFunc(*expr, outerClassLike, isInMockAnnotatedLambda); } return VisitAction::WALK_CHILDREN; }, [&isInConstructor, &isInMockAnnotatedLambda, &outerClassLike](const Ptr node) { if (node->TestAttr(Attribute::CONSTRUCTOR)) { isInConstructor = false; } if (IsMockAnnotedLambda(node)) { isInMockAnnotatedLambda = false; } if (auto classLikeDecl = DynamicCast(node)) { CJC_ASSERT(outerClassLike == classLikeDecl); outerClassLike = nullptr; } return VisitAction::KEEP_DECISION; }).Walk(); } bool TestManager::ArePackagesMockSupportConsistent( const Package& currentPackage, const Package& importedPackage) { auto isCurrentSupportMock = currentPackage.TestAttr(Attribute::MOCK_SUPPORTED); auto isImportedSupportMock = importedPackage.TestAttr(Attribute::MOCK_SUPPORTED); if (!isImportedSupportMock) { // It's ok to have mock-incompatible dependencies, // the error would be reported in the actual case of attempt to mock something from such dependency return true; } return isCurrentSupportMock && isImportedSupportMock; } void TestManager::CheckIfNoMockSupportDependencies(const Package& curPkg) { for (auto pkg: importManager.GetAllImportedPackages(true)) { if (&curPkg != pkg->srcPackage && !ArePackagesMockSupportConsistent(curPkg, *(pkg->srcPackage))) { diag.DiagnoseRefactor( DiagKindRefactor::package_mocking_support_inconsistency, DEFAULT_POSITION, pkg->srcPackage->fullPackageName, MOCK_ON_COMPILATION_OPTION); } } } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND /* * It marks all generic functions which call createMock/createSpy with their generic parameters, * or call other such functions. * Further those marks are used: * 1) to validate that createMock / createSpy calls are used in the "frozen context" * which means all generic functions in the chain of generic calls should be frozen * 2) to force type instantiation if the marked function also has @Frozen anno */ void TestManager::MarkMockCreationContainingGenericFuncs(Package& pkg) const { bool hasDeclsToCheckUsages = true; while (hasDeclsToCheckUsages) { hasDeclsToCheckUsages = false; Ptr enclosingGenericFunc = nullptr; Walker(&pkg, Walker::GetNextWalkerID(), [this, &enclosingGenericFunc, &hasDeclsToCheckUsages](auto node) { if (auto funcDecl = As(node); funcDecl && funcDecl->funcBody && funcDecl->funcBody->generic ) { if (funcDecl->TestAttr(Attribute::CONTAINS_MOCK_CREATION_CALL)) { return VisitAction::SKIP_CHILDREN; } enclosingGenericFunc = funcDecl; } if (auto callExpr = As(node); callExpr && enclosingGenericFunc && ShouldBeMarkedAsContainingMockCreationCall(*callExpr, enclosingGenericFunc) ) { enclosingGenericFunc->EnableAttr(Attribute::CONTAINS_MOCK_CREATION_CALL); hasDeclsToCheckUsages = true; } return VisitAction::WALK_CHILDREN; }, [&enclosingGenericFunc](const Ptr node) { if (auto funcDecl = As(node); funcDecl && funcDecl->funcBody && funcDecl->funcBody->generic ) { enclosingGenericFunc = nullptr; } return VisitAction::KEEP_DECISION; }).Walk(); } } void TestManager::HandleDeclsToExportForTest(std::vector> pkgs) const { if (!exportForTest) { return; } for (auto& pkg : pkgs) { BaseMangler mangler; auto manglerCtx = mangler.PrepareContextForPackage(pkg); auto isInExtend = false; Walker(pkg, Walker::GetNextWalkerID(), [&mangler, &manglerCtx, &isInExtend](auto node) { if (auto ed = As(node); ed && !ed->TestAttr(Attribute::IMPORTED)) { manglerCtx->SaveExtend2CurFile(ed->curFile, ed); isInExtend = true; } if (auto d = As(node); d && !d->TestAttr(Attribute::PRIVATE) && (d->IsFuncOrProp() || Is(d) || Is(d)) && (isInExtend || d->TestAttr(Attribute::FOREIGN)) ) { d->mangledName = mangler.Mangle(*d); } return VisitAction::WALK_CHILDREN; }, [&isInExtend](const Ptr node) { if (Is(node) && !node->TestAttr(Attribute::IMPORTED)) { isInExtend = false; } return VisitAction::KEEP_DECISION; }).Walk(); if (manglerCtx) { manglerCtx.reset(); } } } bool TestManager::ShouldBeMarkedAsContainingMockCreationCall( const CallExpr& callExpr, const Ptr enclosingFunc) const { auto resolvedFunc = callExpr.resolvedFunction; if (!resolvedFunc || !resolvedFunc->funcBody || !resolvedFunc->funcBody->generic || !enclosingFunc->funcBody || !enclosingFunc->funcBody->generic ) { return false; // outside generics, mock creation calls (createMock / createSpy) can be used without restrictions } if (!MockManager::IsMockCall(callExpr) && !resolvedFunc->TestAttr(Attribute::CONTAINS_MOCK_CREATION_CALL)) { return false; } if (auto nre = DynamicCast(callExpr.baseFunc.get())) { return IsAnyTypeParamUsedInTypeArgs(enclosingFunc->funcBody->generic->typeParameters, nre->typeArguments); } else { return false; } } #endif void TestManager::MarkDeclsForTestIfNeeded(std::vector> pkgs) const { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND HandleDeclsToExportForTest(pkgs); #endif for (auto& pkg : pkgs) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND MarkMockCreationContainingGenericFuncs(*pkg); if (mockMode != MockMode::ON && (!mockCompatibleIfNeeded || !IsThereMockUsage(*pkg))) { continue; } #endif Walker(pkg, Walker::GetNextWalkerID(), [](auto node) { MockSupportManager::MarkNodeMockSupportedIfNeeded(*node); return VisitAction::WALK_CHILDREN; }).Walk(); } } bool TestManager::IsThereMockUsage(Package& pkg) const { bool mockUsageFound = false; Walker(&pkg, Walker::GetNextWalkerID(), [&pkg, &mockUsageFound](auto node) { if (auto callExpr = As(node); callExpr && (!IS_GENERIC_INSTANTIATION_ENABLED || !callExpr->ty->HasGeneric()) && callExpr->IsSamePackage(pkg) ) { auto resolvedFunc = callExpr->resolvedFunction; if (MockManager::IsMockCall(*callExpr) || (!IS_GENERIC_INSTANTIATION_ENABLED && resolvedFunc && resolvedFunc->TestAttr(Attribute::CONTAINS_MOCK_CREATION_CALL)) ) { mockUsageFound = true; return VisitAction::STOP_NOW; } } if (auto lambdaExpr = As(node); lambdaExpr && lambdaExpr->IsSamePackage(pkg) && lambdaExpr->TestAttr(Attribute::MOCK_SUPPORTED) ) { mockUsageFound = true; return VisitAction::STOP_NOW; } return VisitAction::WALK_CHILDREN; }).Walk(); if (mockUsageFound) { return true; } for (auto importedPkg: importManager.GetAllImportedPackages(true)) { if (&pkg != importedPkg->srcPackage && importedPkg->srcPackage->TestAttr(Attribute::MOCK_SUPPORTED)) { return true; } } return false; } namespace { struct ManglerCtxGuard final { public: ManglerCtxGuard(BaseMangler& mangler, Package& pkg) : mangler(mangler), pkg(pkg) { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND manglerCtx = mangler.PrepareContextForPackage(&pkg); #endif } ~ManglerCtxGuard() { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND mangler.manglerCtxTable.erase( ManglerContext::ReduceUnitTestPackageName(pkg.fullPackageName)); #endif } private: [[maybe_unused]] BaseMangler& mangler; [[maybe_unused]] Package& pkg; #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::unique_ptr manglerCtx; #endif }; } // namespace void TestManager::PreparePackageForTestIfNeeded(Package& pkg) { if (pkg.files.empty()) { return; } std::optional manglerCtxGuard; if (mockMode == MockMode::ON || (mockCompatibleIfNeeded && IsThereMockUsage(pkg))) { manglerCtxGuard.emplace(mockUtils->mangler, pkg); mockUtils->SetGetTypeForTypeParamDecl(pkg); mockUtils->SetIsSubtypeTypes(pkg); GenerateAccessors(pkg); PrepareToSpy(pkg); PrepareDecls(pkg); ReplaceCallsWithAccessors(pkg); ReplaceCallsToForeignFunctions(pkg); } else { CheckIfNoMockSupportDependencies(pkg); } HandleMockCalls(pkg); } void TestManager::Init(GenericInstantiationManager* instantiationManager) { if (!mockCompatible) { return; } mockUtils = new MockUtils(importManager, typeManager, instantiationManager); mockSupportManager = MakeOwned(typeManager, mockUtils); if (mockCompatible && testEnabled) { mockManager = MakeOwned(importManager, typeManager, mockUtils); } } TestManager::~TestManager() { if (mockUtils != nullptr) { delete mockUtils; mockUtils = nullptr; } } } // namespace Cangjie cangjie_compiler-1.0.7/src/Sema/TyVarConstraintGraph.h000066400000000000000000000061531510705540100227350ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the TyVar Constraints Solving class, which provides topological algorithm to process TyVar * Constraints Solving. */ #ifndef CANGJIE_SEMA_TYVARCONSTRAINTGRAPH_H #define CANGJIE_SEMA_TYVARCONSTRAINTGRAPH_H #include "cangjie/Utils/Casting.h" #include "cangjie/AST/Types.h" #include "cangjie/Sema/TypeManager.h" namespace Cangjie { /* * A graph built from constraints (type variables and their lower and upper bounds). * The graph records and analyses the dependency of type variables by a topological sorting. */ class TyVarConstraintGraph { // A map mapping type variables to their constraints. public: TyVarConstraintGraph(const Constraint& m, const TyVars& mayUsedTyVars, TypeManager& tyMgr) : tyMgr(tyMgr) { PreProcessConstraintGraph(m, mayUsedTyVars); } // Used to build the graph. void PreProcessConstraintGraph(const Constraint& m, const TyVars& mayUsedTyVars); // The method TopoOnce tries to get a set of type variables that are most independent and can be solved first. Constraint TopoOnce(const Constraint& m); // Substitute some type variables in the graph with their instantiated types. void ApplyTypeSubst(const TypeSubst& subst) { std::map, int> newIndegree{}; for (auto pair : std::as_const(indegree)) { if (auto tv = DynamicCast(tyMgr.GetInstantiatedTy(pair.first, subst))) { newIndegree.emplace(tv, pair.second); } } this->indegree = newIndegree; std::map, TyVars> newEdges{}; for (auto pair : std::as_const(edges)) { if (auto tv = DynamicCast(tyMgr.GetInstantiatedTy(pair.first, subst))) { newEdges.emplace(tv, StaticToTyVars(tyMgr.ApplyTypeSubstForTys(subst, pair.second))); } } this->edges = newEdges; TyVars newUsedTyVars{}; for (auto tv : usedTyVars) { if (auto tv2 = DynamicCast(tyMgr.GetInstantiatedTy(tv, subst))) { newUsedTyVars.emplace(tv2); } } this->usedTyVars = newUsedTyVars; TyVars newSolvedTyVars{}; for (auto tv : solvedTyVars) { if (auto tv2 = DynamicCast(tyMgr.GetInstantiatedTy(tv, subst))) { newSolvedTyVars.emplace(tv2); } } this->solvedTyVars = newSolvedTyVars; } private: void FindLoopConstraints(const Constraint& m, Ptr start, Constraint& tyVarsInLoop); bool HasLoop(Ptr start, std::stack>& loopPath); std::map, int> indegree; std::map, TyVars> edges; std::map, bool> isVisited; TyVars solvedTyVars; TyVars usedTyVars; bool hasNext{true}; TypeManager& tyMgr; }; } // namespace Cangjie #endif // CANGJIE_SEMA_TYVARCONSTRAINTGRAPH_H cangjie_compiler-1.0.7/src/Sema/TypeArgumentInference.cpp000066400000000000000000000560371510705540100234450ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements type argument inference. */ #include "cangjie/AST/Match.h" #include "TypeCheckerImpl.h" #include "TypeCheckUtil.h" #include "ExtraScopes.h" #include "LocalTypeArgumentSynthesis.h" #include "DiagSuppressor.h" #include "Diags.h" using namespace Cangjie; using namespace AST; using namespace Sema; using namespace TypeCheckUtil; namespace { // for diagnose TyVars GetUnivTyVarsToSolve(const SubstPack& maps) { TyVars ret; for (auto [k, v] : maps.u2i) { ret.insert(k); } Utils::EraseIf( ret, [&maps](auto tv) { return Utils::InKeys(Ptr(StaticCast(maps.u2i.at(tv))), maps.inst); }); return ret; } // should be enough to replace only immediate ideal types Ptr TryReplaceIdeal(const Ptr ty) { if (!ty) { return ty; } else if (ty->kind == TypeKind::TYPE_IDEAL_INT) { return TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64); } else if (ty->kind == TypeKind::TYPE_IDEAL_FLOAT) { return TypeManager::GetPrimitiveTy(TypeKind::TYPE_FLOAT64); } else { return ty; } } std::string MakeOneBlameHint(const Blame& blame) { auto lb = TryReplaceIdeal(blame.lb); auto ub = TryReplaceIdeal(blame.ub); switch (blame.style) { case BlameStyle::ARGUMENT: return "of type '" + lb->String() + "', should match parameter type '" + ub->String() + "'"; case BlameStyle::RETURN: return "of return type '" + lb->String() + "', should match expected type '" + ub->String() + "'"; case BlameStyle::CONSTRAINT: return ""; } } void MakeBlameMsg(DiagnosticBuilder& builder, const SolvingErrInfo& diagInfo) { switch (diagInfo.style) { case SolvingErrStyle::NO_CONSTRAINT: { auto tyVar = StaticCast(diagInfo.tyVar); builder.AddNote(*tyVar->decl, MakeRangeForDeclIdentifier(*tyVar->decl), "no information known for type variable '" + tyVar->String() + "'"); break; } case SolvingErrStyle::ARG_MISMATCH: { auto& blame = *diagInfo.blames[0].begin(); auto lb = TryReplaceIdeal(blame.lb); auto ub = TryReplaceIdeal(blame.ub); auto subDiag = SubDiagnostic("mismatched types"); subDiag.AddMainHint(MakeRange(blame.src->GetBegin(), blame.src->GetEnd()), "of type '" + lb->String() + "', impossible to match parameter type '" + ub->String() + "'"); builder.AddNote(subDiag); break; } case SolvingErrStyle::RET_MISMATCH: { auto& blame = *diagInfo.blames[0].begin(); auto lb = TryReplaceIdeal(blame.lb); auto ub = TryReplaceIdeal(blame.ub); auto subDiag = SubDiagnostic("mismatched types"); subDiag.AddMainHint(MakeRange(blame.src->GetBegin(), blame.src->GetEnd()), "of return type '" + lb->String() + "', impossible to match expected type '" + ub->String() + "'"); builder.AddNote(subDiag); break; } case SolvingErrStyle::CONFLICTING_CONSTRAINTS: { auto tyVar = StaticCast(diagInfo.tyVar); std::string constraintsMsg; std::string indentStr = "\n "; for (auto lb0 : diagInfo.lbs) { auto lb = TryReplaceIdeal(lb0); constraintsMsg += indentStr + "'" + lb->String() + " <: " + tyVar->String() + "'"; } for (auto ub0 : diagInfo.ubs) { auto ub = TryReplaceIdeal(ub0); constraintsMsg += indentStr + "'" + tyVar->String() + " <: " + ub->String() + "'"; } builder.AddNote(*tyVar->decl, MakeRangeForDeclIdentifier(*tyVar->decl), "following constraints for type variable '" + tyVar->String() + "' cannot be solved:" + constraintsMsg); size_t blameId = 0; for (auto lb0 : diagInfo.lbs) { auto lb = TryReplaceIdeal(lb0); std::string msg = "constraint '" + lb->String() + " <: " + tyVar->String() + "' may come from:"; for (auto blame : diagInfo.blames[blameId]) { auto subDiag = SubDiagnostic(msg); subDiag.AddMainHint( MakeRange(blame.src->GetBegin(), blame.src->GetEnd()), MakeOneBlameHint(blame)); builder.AddNote(subDiag); } blameId++; } for (auto ub0 : diagInfo.ubs) { auto ub = TryReplaceIdeal(ub0); std::string msg = "constraint '" + tyVar->String() + " <: " + ub->String() + "' may come from:"; for (auto blame : diagInfo.blames[blameId]) { auto subDiag = SubDiagnostic(msg); subDiag.AddMainHint( MakeRange(blame.src->GetBegin(), blame.src->GetEnd()), MakeOneBlameHint(blame)); builder.AddNote(subDiag); } blameId++; } break; } case SolvingErrStyle::DEFAULT: break; } } void DiagnoseForCallInference(DiagnosticEngine& diag, const CallExpr& ce, const FuncDecl& fd, const SubstPack& maps, const SolvingErrInfo& diagInfo) { CJC_ASSERT(fd.ty); CJC_NULLPTR_CHECK(ce.baseFunc); auto tyVars = GetUnivTyVarsToSolve(maps); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_unable_to_infer_generic_func, *ce.baseFunc); std::string typeStr = "'" + fd.ty->String() + "'"; if (auto ma = DynamicCast(ce.baseFunc.get()); ma && ma->baseExpr && Ty::IsTyCorrect(ma->baseExpr->ty)) { auto reBase = As(ma->baseExpr.get()); bool isNormalRef = !reBase || (!reBase->isThis && !reBase->isSuper); // Base is not this or super. if (isNormalRef) { typeStr += " in context type '" + ma->baseExpr->ty->String() + "'"; } } TyVars fdVars; auto fdGeneric = fd.GetGeneric(); if (fdGeneric) { for (auto& tyParam : fdGeneric->typeParameters) { fdVars.emplace(StaticCast(tyParam->ty)); } } std::string constraintStr; std::string fdConstraints; for (auto ty : tyVars) { if (auto gty = DynamicCast(ty); gty && !gty->upperBounds.empty()) { std::string str = gty->String() + " <: " + Ty::GetTypesToStableStr(gty->upperBounds, " & "); if (fdVars.find(ty) != fdVars.end()) { fdConstraints += str + ", "; } else { constraintStr += str + ", "; } } } // Concat context constraints and function constraints in order. constraintStr += fdConstraints; if (!constraintStr.empty()) { typeStr += " with constraint '" + constraintStr.substr(0, constraintStr.find_last_of(',')) + "'"; } if (fd.ShouldDiagnose()) { // too much redundant information in these cases if (diagInfo.style != SolvingErrStyle::CONFLICTING_CONSTRAINTS && diagInfo.style != SolvingErrStyle::NO_CONSTRAINT) { builder.AddNote(fd, MakeRangeForDeclIdentifier(fd), "for function type " + typeStr); } MakeBlameMsg(builder, diagInfo); } } bool IsConcrete(const TyVars& tyVarsToSolve, AST::Ty& ty) { if (!Ty::IsTyCorrect(&ty)) { return false; } auto allTyVars = ty.GetGenericTyArgs(); return std::all_of(allTyVars.begin(), allTyVars.end(), [&tyVarsToSolve](Ptr ty) { return tyVarsToSolve.count(RawStaticCast(ty)) == 0; }); } /** * Turn unsolved TyVars in a Ty into QuestTy. * Will only do so if QuestTy at the position can be supported. * Otherwise a nullopt will be returned. */ std::optional> UnsolvedAsQuest(TypeManager& tyMgr, const TyVars& tyVarsToSolve, AST::Ty& ty) { if (!Ty::IsTyCorrect(&ty)) { return &ty; } if (auto funcTy = DynamicCast(&ty)) { std::vector> paramTys; for (auto& it : funcTy->paramTys) { if (!IsConcrete(tyVarsToSolve, *it)) { return std::nullopt; } paramTys.push_back(it); } if (auto retType = UnsolvedAsQuest(tyMgr, tyVarsToSolve, *funcTy->retTy)) { Ptr fin = tyMgr.GetFunctionTy(paramTys, *retType, {funcTy->IsCFunc(), funcTy->isClosureTy, funcTy->hasVariableLenArg}); return fin; } else { return std::nullopt; } } else { if (IsConcrete(tyVarsToSolve, ty)) { return &ty; } else { return TypeManager::GetQuestTy(); } } } struct TyArgSynState { TyVars tyVarsToSolve; Ptr retTarget; std::vector> argTys; std::unordered_set ignoredEnumCtor; // bitmap for arugments failed to be synthesized std::vector failSet; // param tys with quest type, used as check target for args in failSet std::vector> questParamTys; // number of type vars not yet solved size_t unsolvedCount; // any new information derived from this iteration bool newInfo = true; // if iterative solving fails, and any arg is lambda, could try to infer the lambda's param types from body bool lastResortUnused = true; // the solution std::optional solution = {}; }; bool MayNeedNextIteration(TyArgSynState& stat, size_t newUnsolvedCount) { if (!stat.solution || newUnsolvedCount == 0) { // final fail or success stat.unsolvedCount = newUnsolvedCount; return false; } else if (newUnsolvedCount >= stat.unsolvedCount) { // no new solved ty var return false; } else { stat.unsolvedCount = newUnsolvedCount; return true; } } bool PrepareQuestParamTy(TypeManager& tyMgr, const std::vector>& paramsTy, TyArgSynState& stat) { // prepare param ty with new info bool anyNewTarget = false; for (size_t i = 0; i < paramsTy.size(); ++i) { stat.questParamTys[i] = nullptr; if (stat.failSet[i] && stat.ignoredEnumCtor.count(i) == 0) { // paramTy where the solution is substituted. Some placeholder type vars may not be solved. auto paramTyPartial = tyMgr.GetInstantiatedTy(paramsTy[i], *stat.solution); // paramTy where the unsolved type vars are replaced by QuestTy auto paramTyQuest = UnsolvedAsQuest(tyMgr, stat.tyVarsToSolve, *paramTyPartial); if (paramTyQuest) { anyNewTarget = true; stat.questParamTys[i] = *paramTyQuest; } } } return anyNewTarget; } std::vector> ValidateArgTys(CallExpr& ce, TyArgSynState& stat) { std::vector> validArgTys; stat.ignoredEnumCtor.clear(); for (size_t i = 0; i < ce.args.size(); ++i) { // For code 'Some(None)' the sema type of 'None' needs to be inferred, // so it should be ignored for function type inference. if (ce.args[i]->expr && IsEnumCtorWithoutTypeArgs(*ce.args[i]->expr, ce.args[i]->expr->GetTarget())) { stat.failSet[i] = true; stat.ignoredEnumCtor.emplace(i); } else if (Ty::IsTyCorrect(stat.argTys[i])) { stat.failSet[i] = false; validArgTys.emplace_back(stat.argTys[i]); } } return validArgTys; } std::vector MakeArgBlames(const TyArgSynState& stat, const CallExpr& ce, const std::vector>& argTys, const std::vector>& paramTys, TypeManager& tyMgr) { CJC_ASSERT(argTys.size() == paramTys.size()); std::vector blames; size_t j = 0; for (size_t i = 0; i < ce.args.size(); i++) { if (stat.ignoredEnumCtor.count(i) == 0 && Ty::IsTyCorrect(stat.argTys[i])) { CJC_ASSERT(j < argTys.size()); // should be guaranteed by ValidateArgTys blames.push_back({ .src = ce.args[i].get(), .lb = argTys[j], .ub = tyMgr.RecoverUnivTyVar(paramTys[j]), .style = BlameStyle::ARGUMENT}); j++; } } return blames; } void DisableBodyInferForArgs(ASTContext& ctx, CallExpr& ce) { for (auto& arg : ce.args) { Walker(arg.get(), [&ctx](Ptr node) { if (auto lam = DynamicCast(node)) { ctx.funcArgReachable.emplace(lam); return VisitAction::SKIP_CHILDREN; } if (IsQuestableNode(*node)) { return VisitAction::WALK_CHILDREN; } else { return VisitAction::SKIP_CHILDREN; } }).Walk(); } } bool NeedLastTry(CallExpr& ce) { bool ret = false; for (auto& arg : ce.args) { Walker(arg.get(), [&ret](Ptr node) { if (Is(node)) { ret = true; return VisitAction::STOP_NOW; } if (IsQuestableNode(*node)) { return VisitAction::WALK_CHILDREN; } else { return VisitAction::SKIP_CHILDREN; } }).Walk(); } return ret; } void TryUpdateEnumTyByTarget(const CallExpr& ce, const FuncDecl& fd, Ptr retTarget) { if (auto ma = DynamicCast(ce.baseFunc.get()); ma && ma->baseExpr) { // To handle omiting the generic parameter but it can be defined by target ty. // If target type of enum ctor is given, must not synthesize later. Example: var b: E = E.e(1) bool updateEnumTy = IsEnumCtorWithoutTypeArgs(*ma, &fd) && Ty::GetDeclPtrOfTy(retTarget) == fd.outerDecl; if (updateEnumTy) { ma->baseExpr->ty = retTarget; } } } void ConstrainByUpperbound(TypeManager& tyMgr) { for (auto& [univ, inst] : tyMgr.GetInstMapping().u2i) { auto instTv = RawStaticCast(inst); auto& toSolve = tyMgr.GetUnsolvedTyVars(); if (!Utils::In(instTv, toSolve)) { continue; } for (auto upper : univ->upperBounds) { CJC_NULLPTR_CHECK(upper); tyMgr.constraints[instTv].ubs.insert(tyMgr.InstOf(upper)); } } } } // namespace std::optional TypeChecker::TypeCheckerImpl::PropagatePlaceholderAndSolve( ASTContext& ctx, CallExpr& ce, const std::vector>& paramTys, const Ptr retTy, const Ptr retTyUB) { if (!NeedLastTry(ce)) { return {}; } DiagSuppressor ds(diag); for (auto tv : GetTyVarsToSolve(typeManager.GetInstMapping())) { typeManager.MarkAsUnsolvedTyVar(*tv); } ConstrainByUpperbound(typeManager); if (retTyUB && !typeManager.IsSubtype(retTy, retTyUB)) { return {}; } for (size_t i = 0; i < ce.args.size(); ++i) { if (!CheckWithCache(ctx, paramTys[i], ce.args[i].get())) { return {}; } } return SolveConstraints(typeManager.constraints); } ErrOrSubst TypeChecker::TypeCheckerImpl::PrepareTyArgsSynthesis( ASTContext& ctx, const FunctionCandidate& candidate, Ptr const retTyUB) { auto& [fd, ce, argCombinations, _] = candidate; // Guarantees the RawStaticCast below. CJC_ASSERT(Ty::IsTyCorrect(fd.ty) && fd.ty->IsFunc()); DisableBodyInferForArgs(ctx, ce); TyArgSynState stat; // If args not empty, get argTys. Index 0 is set as current args' types combination. stat.argTys = argCombinations.empty() ? std::vector>{} : argCombinations[0]; if (stat.argTys.size() != ce.args.size()) { return SolvingErrInfo{}; } if (NeedSynOnUsed(fd)) { Synthesize(ctx, &fd); } stat.failSet = std::vector(stat.argTys.size(), true); stat.questParamTys = std::vector>(stat.argTys.size(), nullptr); stat.tyVarsToSolve = GetTyVarsToSolve(typeManager.GetInstMapping()); stat.unsolvedCount = stat.tyVarsToSolve.size() + 1; // Suppressing errors generated in 'Synthesize', only report them when type inference failed. // If type inference succeed, all real errors in argument expression will be thrown during parameter checking. auto ds = DiagSuppressor(diag); // no need to consider any constraint generated here, since they are all tentative or local // any information worth considering will be re-considered during re-check of arguments auto cs = PData::CommitScope(typeManager.constraints); for (size_t i = 0; i < stat.argTys.size(); ++i) { // Must not re-synthesize already checked argument here. if (Ty::IsTyCorrect(stat.argTys[i])) { stat.failSet[i] = false; } } stat.retTarget = retTyUB; auto paramsTyInOrder = GetParamTysInArgsOrder(typeManager, ce, fd); for (size_t i = 0; i < paramsTyInOrder.size(); ++i) { paramsTyInOrder[i] = typeManager.InstOf(paramsTyInOrder[i]); } Ptr funcRetTy = typeManager.InstOf(RawStaticCast(fd.ty)->retTy); LocTyArgSynArgPack argPack; while (stat.newInfo) { // 1. synthesize/check func args for (size_t i = 0; i < stat.argTys.size(); ++i) { if (stat.failSet[i] && stat.questParamTys[i]) { // never report error from speculative check with questParamTy auto ds2 = DiagSuppressor(diag); ce.args[i]->Clear(); CheckWithCache(ctx, stat.questParamTys[i], ce.args[i].get()); stat.argTys[i] = ce.args[i]->ty; } else if (Ty::IsInitialTy(stat.argTys[i])) { // Initially, every arg goes into this branch. If the inference eventually fails, // errors from this synthesize should be reported stat.argTys[i] = SynthesizeWithCache(ctx, ce.args[i].get()); } else if (stat.failSet[i] && !stat.lastResortUnused) { // If some arg is a lambda, try to infer its param type from its body, // as a last resort in case no enough contextual info is provided. auto ds3 = DiagSuppressor(diag); stat.argTys[i] = Synthesize(ctx, ce.args[i].get()); } } // 2. collect valid arg tys & update failSet std::vector> validArgTys = ValidateArgTys(ce, stat); std::vector> paramTys; for (size_t i = 0; i < paramsTyInOrder.size(); ++i) { if (!stat.failSet[i]) { paramTys.push_back(paramsTyInOrder[i]); } } // 3. prepare input pack & infer type args auto retBlame = Blame{ .src = &ce, .lb = typeManager.RecoverUnivTyVar(funcRetTy), .ub = retTyUB, .style = BlameStyle::RETURN}; argPack = {stat.tyVarsToSolve, validArgTys, paramTys, MakeArgBlames(stat, ce, validArgTys, paramTys, typeManager), funcRetTy, stat.retTarget, retBlame}; auto synCtx = LocalTypeArgumentSynthesis(typeManager, argPack, ctx.gcBlames, false); stat.solution = synCtx.SynthesizeTypeArguments(true); size_t newUnsolvedCount = stat.solution ? synCtx.CountUnsolvedTyVars(*stat.solution) : 0; // 4. check if there is new info & prepare next iteration stat.newInfo = MayNeedNextIteration(stat, newUnsolvedCount); if (stat.newInfo) { stat.newInfo = PrepareQuestParamTy(typeManager, paramsTyInOrder, stat); } if (!stat.newInfo && stat.unsolvedCount > 0 && stat.lastResortUnused) { stat.lastResortUnused = false; auto sol = PropagatePlaceholderAndSolve(ctx, ce, paramsTyInOrder, funcRetTy, retTyUB); if (sol && !HasUnsolvedTyVars(*sol, stat.tyVarsToSolve)) { stat.solution = sol; stat.unsolvedCount = 0; } } } if (!stat.solution || stat.unsolvedCount > 0) { stat.solution = {}; // clear partially solved solution for (auto idx : stat.ignoredEnumCtor) { diag.Diagnose(*ce.args[idx]->expr, DiagKind::sema_generic_type_without_type_argument); } // inference failure will be reported later ds.ReportDiag(); auto diagSyn = LocalTypeArgumentSynthesis(typeManager, argPack, ctx.gcBlames, true); (void)diagSyn.SynthesizeTypeArguments(false); return diagSyn.GetErrInfo(); } PData::Reset(typeManager.constraints); return *stat.solution; } // Generate the type mapping table if the current call involves generic types. Before overload resolution, the fd may // not be the right target, so the generation of typeMapping may fail as well. This function will return empty in this // case. std::vector TypeChecker::TypeCheckerImpl::GenerateTypeMappingForCall( ASTContext& ctx, FunctionCandidate& candidate, Ptr retTarget) { auto& ce = candidate.ce; auto& fd = candidate.fd; InstCtxScope ic(*this); TryUpdateEnumTyByTarget(ce, fd, retTarget); if (!ic.SetRefDecl(ctx, fd, ce)) { return {}; } if (HasTyVarsToSolve(typeManager.GetInstMapping())) { return GenerateTypeMappingByInference(ctx, candidate, retTarget); } return {typeManager.GetInstMapping()}; } std::vector TypeChecker::TypeCheckerImpl::GenerateTypeMappingByInference( ASTContext& ctx, const FunctionCandidate& candidate, Ptr retTarget) { auto& ce = candidate.ce; auto& fd = candidate.fd; if (!Ty::IsTyCorrect(fd.ty) || !fd.ty->IsFunc()) { return {}; } std::vector typeMappings; size_t combinationSize = candidate.argCombinations.size(); size_t cbIndex = 0; auto curCandidate = candidate; SolvingErrInfo diagInfo; do { // Solve type mappings with all possible arguments combinations. curCandidate.argCombinations[0] = candidate.argCombinations[cbIndex]; cbIndex++; auto errOrSubst = PrepareTyArgsSynthesis(ctx, curCandidate, retTarget); if (std::holds_alternative(errOrSubst)) { // Copy context mapping and merge with solution to get complete type mapping. auto currentSubst = typeManager.GetInstMapping(); auto newSubst = std::get(errOrSubst); typeManager.PackMapping(currentSubst, newSubst); typeMappings.emplace_back(currentSubst); } else { diagInfo = std::get(errOrSubst); } } while (cbIndex < combinationSize); if (typeMappings.empty() && !Utils::In(ce.args, [](const auto& arg) { return !Ty::IsTyCorrect(arg->ty); })) { DiagnoseForCallInference(diag, ce, fd, typeManager.GetInstMapping(), diagInfo); } return typeMappings; } cangjie_compiler-1.0.7/src/Sema/TypeCheckAccess.cpp000066400000000000000000000463761510705540100222100ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements access control check apis. */ #include "TypeCheckerImpl.h" #include #include "Diags.h" #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Modules/ModulesUtils.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Utils/CheckUtils.h" using namespace Cangjie; using namespace AST; using namespace Sema; namespace { bool SearchTargetDeclForProtectMember(const Node& curComposite, const Decl& outerDeclOfTarget, TypeManager& typeManager) { auto typeDecl = &curComposite; // For protected members, need check the extended type. if (curComposite.astKind == ASTKind::EXTEND_DECL) { auto outerED = RawStaticCast(&curComposite); auto decl = Ty::GetDeclPtrOfTy(outerED->extendedType->ty); if (decl == &outerDeclOfTarget) { return true; } if (decl) { typeDecl = decl; // Let the BFS search start from the extended declaration. } } // For protected members, need check superclass. if (typeDecl->IsClassLikeDecl()) { auto outerCLD = RawStaticCast(typeDecl); std::queue> res; res.push(outerCLD); while (!res.empty()) { auto iter = res.front(); if (iter == &outerDeclOfTarget) { return true; } for (const auto& extend : typeManager.GetDeclExtends(*iter)) { res.push(extend); } if (auto cd = DynamicCast(iter); cd && cd->GetSuperClassDecl()) { auto superClass = cd->GetSuperClassDecl(); res.push(superClass); } res.pop(); } } return false; } bool MaybeStruct(const Ty& ty) { if (!Ty::IsTyCorrect(&ty)) { return false; } else if (ty.IsStruct()) { return true; } else if (!ty.IsGeneric()) { return false; } const GenericsTy& gty = static_cast(ty); for (Ptr upperBound : gty.upperBounds) { if (upperBound && MaybeStruct(*upperBound)) { return true; } } // It there are non-interface types (e.g., classes) in the upper bounds, `ty` cannot be struct. return !Utils::In(gty.upperBounds, [](Ptr ty) { CJC_NULLPTR_CHECK(ty); return !ty->IsInterface(); }); }; void CheckMutationInStructNonMut(DiagnosticEngine& diag, const StructDecl& sd, const Expr& expr) { CJC_NULLPTR_CHECK(sd.body); // Collect the value type instance members of the `struct`, // these `varDecls` are not allowed to be assigned in non-mut function. std::unordered_set> varDecls; for (auto& decl : sd.body->decls) { if (auto vd = DynamicCast(decl.get()); vd && !vd->TestAttr(Attribute::STATIC) && Ty::IsTyCorrect(vd->ty) && !vd->ty->IsArray()) { varDecls.emplace(vd); } } // Get the `baseExpr` of the form `x` (`RefExpr`) or `this.x.*` (`MemberAccess`). // Then check whether the target of the `x` is in the forbidden `varDecls`. Ptr baseExpr = &expr; while (baseExpr != nullptr) { auto target = baseExpr->GetTarget(); if (Is(baseExpr)) { if (varDecls.find(target) != varDecls.cend()) { diag.Diagnose(expr, DiagKind::sema_cannot_modify_var, target->identifier.Val()); } break; } else if (auto ma = DynamicCast(baseExpr); ma) { if (ma->baseExpr->ty->IsClassLike()) { // don't check member access of field of class like type, unless it is a member access of this // e.g. let T be a class type with field v // this.a = T() // this is a mutation to this // this.a.v = T() // this is not a mutation, because a is of class type break; } if (auto re = DynamicCast(ma->baseExpr.get()); re && re->isThis && varDecls.find(target) != varDecls.cend()) { diag.Diagnose(expr, DiagKind::sema_cannot_modify_var, target->identifier.Val()); break; } baseExpr = ma->baseExpr.get(); } else { break; } } } inline bool IsNormalCtorRef(const AST::Node& node) { auto re = DynamicCast(&node); return !re || (!re->isThis && !re->isSuper); } } // namespace bool TypeChecker::TypeCheckerImpl::IsLegalAccess(Symbol* curComposite, const Decl& d, const AST::Node& node) const { auto vd = DynamicCast(&d); auto fd = DynamicCast(&d); auto cld = DynamicCast(&d); if (!vd && !fd && !cld) { // There are four kinds of members in class, VarDecl, funcDecl, classDecl and interfaceDecl. If decl is not one // of these, then there is no need to check visibility. return true; } // Public & external decls are always accessible. But public in extend may not export. // The node with 'IN_CORE' or 'IN_MACRO' attribute is created by compiler, // allowing access any kind of decl for special. if (node.TestAnyAttr(Attribute::IN_CORE, Attribute::IN_MACRO)) { return true; } if (!node.IsSamePackage(d) && node.curFile && node.astKind == ASTKind::MEMBER_ACCESS) { auto ma = StaticCast(&node); if (ma->baseExpr && ma->baseExpr->ty && !importManager.IsExtendMemberAccessible(*node.curFile, d, *ma->baseExpr->ty)) { return false; } } if (d.TestAttr(Attribute::PUBLIC)) { return true; } CJC_ASSERT(node.curFile && node.curFile->curPackage); auto relation = Modules::GetPackageRelation(node.curFile->curPackage->fullPackageName, d.GetFullPackageName()); // `flag` indicates whether the type name is used to construct an object outside a type declaration // or in a type without inheritance relationship. bool flag = IsClassOrEnumConstructor(d) && !d.TestAttr(Attribute::PRIVATE) && IsNormalCtorRef(node) && (!curComposite || !SearchTargetDeclForProtectMember(*curComposite->node, *d.outerDecl, typeManager)); if (d.TestAttr(Attribute::GLOBAL) || flag) { // When decl is private it can only be accessed in same file, if (d.TestAttr(Attribute::PRIVATE)) { // In the LSP, the 'node' may be a new ast node, 'curFile' pointer consistency cannot be ensured. return node.curFile && d.curFile && *node.curFile == *d.curFile; } return Modules::IsVisible(d, relation); } Ptr outerDeclOfTarget = d.outerDecl; if (outerDeclOfTarget == nullptr || !outerDeclOfTarget->IsNominalDecl()) { return true; // Access local decls, must be valid (outerDecl is empty or non-nominal decl). } CJC_ASSERT(!curComposite || curComposite->node); // 1. When decl is internal, checking the package relation. if (d.TestAttr(Attribute::INTERNAL)) { return Modules::IsVisible(d, relation); } else if (d.TestAttr(Attribute::PROTECTED)) { // 2. when decl is protected, checking the package relation, if relation is none, checking composite relation. // 3. Access decl in same composite decl is always allowed. return Modules::IsVisible(d, relation) || (curComposite && (curComposite->node == outerDeclOfTarget || SearchTargetDeclForProtectMember(*curComposite->node, *outerDeclOfTarget, typeManager))); } // 4. otherwise accessing private decl must inside same decl. auto expectedOuter = outerDeclOfTarget; if (expectedOuter->platformImplementation) { expectedOuter = expectedOuter->platformImplementation; } return curComposite && curComposite->node == expectedOuter; } std::vector> TypeChecker::TypeCheckerImpl::GetAccessibleDecls( const ASTContext& ctx, const Expr& e, const std::vector>& targets) const { std::vector> res; for (auto t : targets) { if (t == nullptr) { continue; } Symbol* sym = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, e.scopeName); bool ret = IsLegalAccess(sym, *t, e); if (ret) { res.emplace_back(t); } } return res; } Ptr TypeChecker::TypeCheckerImpl::GetAccessibleDecl( const ASTContext& ctx, const Expr& e, const std::vector>& targets) const { auto res = GetAccessibleDecls(ctx, e, targets); return res.empty() ? nullptr : res[0]; } Ptr TypeChecker::TypeCheckerImpl::CheckFuncAccessControl( const ASTContext& ctx, const Expr& e, const std::vector>& targets) const { auto accessibleDecl = GetAccessibleDecl(ctx, e, targets); bool invalidFuncAccess = accessibleDecl == nullptr && !targets.empty() && targets[0] && targets[0]->astKind == ASTKind::FUNC_DECL; if (invalidFuncAccess) { diag.Diagnose(e, DiagKind::sema_invalid_access_function, targets[0]->identifier.Val()); } return accessibleDecl; } bool TypeChecker::TypeCheckerImpl::CheckNonFuncAccessControl( const ASTContext& ctx, const Expr& e, const Decl& target) const { Symbol* sym = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, e.scopeName); bool ret = IsLegalAccess(sym, target, e); if (!ret) { ctx.diag.Diagnose(e, DiagKind::sema_invalid_access_control, target.identifier.Val()); } return ret; } void TypeChecker::TypeCheckerImpl::CheckMemberAccessInCtorParamOrCtorArg(const ASTContext& ctx, const FuncDecl& fd) { if (fd.funcBody == nullptr) { return; } for (auto& paramList : fd.funcBody->paramLists) { for (auto& fp : paramList->params) { if (fp->assignment) { CheckIllegalMemberWalker(ctx, fp->assignment.get(), true, "default parameter value of the constructor"); } } } if (fd.constructorCall != ConstructorCall::NONE && !fd.funcBody->body->body.empty()) { if (auto ce = DynamicCast(fd.funcBody->body->body.begin()->get()); ce) { auto refExpr = As(ce->baseFunc.get()); if (!refExpr || (!refExpr->isThis && !refExpr->isSuper)) { return; } for (auto& i : ce->args) { CheckIllegalMemberWalker(ctx, i.get(), refExpr->isThis, "arguments of constructor call"); } } } } void TypeChecker::TypeCheckerImpl::CheckIllegalMemberWalker( const ASTContext& ctx, Ptr node, bool reportThis, const std::string& errorStr) { Walker walker(node, [this, &ctx, reportThis, errorStr](Ptr node) -> VisitAction { if (auto re = DynamicCast(node); re) { if (reportThis && re->isThis && re->isAlone) { diag.Diagnose(*re, DiagKind::sema_assignment_of_member_variable_cannot_use_this_or_super, re->ref.identifier.Val(), errorStr); } CheckIllegalMemberHelper(ctx, reportThis, errorStr, *re); return VisitAction::WALK_CHILDREN; } else if (auto ma = DynamicCast(node); ma) { auto refExpr = As(ma->baseExpr.get()); // call member this.xx/super.xx in struct or class constructor if (refExpr && ((refExpr->isThis && reportThis) || refExpr->isSuper)) { diag.Diagnose(*ma, DiagKind::sema_assignment_of_member_variable_cannot_use_this_or_super, refExpr->ref.identifier.Val(), errorStr); return VisitAction::SKIP_CHILDREN; } if (refExpr && (refExpr->isThis || refExpr->isSuper)) { CheckIllegalMemberHelper(ctx, reportThis, errorStr, *refExpr); } return VisitAction::SKIP_CHILDREN; } else { return VisitAction::WALK_CHILDREN; } }); walker.Walk(); } void TypeChecker::TypeCheckerImpl::CheckIllegalMemberHelper( const ASTContext& ctx, bool reportThis, const std::string& errorStr, const NameReferenceExpr& nre) { auto target = nre.GetTarget(); bool isInstanceMember = target != nullptr && target->outerDecl && target->outerDecl->IsNominalDecl() && !target->TestAnyAttr(Attribute::CONSTRUCTOR, Attribute::ENUM_CONSTRUCTOR, Attribute::STATIC); if (isInstanceMember) { Symbol* symOfExprStruct = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, nre.scopeName); if (symOfExprStruct == nullptr) { return; // If the reference is not inside any structure decl, quit now. } // The target has been checked that is defined inside nominal declaration. Symbol* symOfDeclStruct = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, target->scopeName); CJC_NULLPTR_CHECK(symOfDeclStruct); CJC_NULLPTR_CHECK(symOfDeclStruct->node); CJC_NULLPTR_CHECK(symOfExprStruct->node); bool inSameDecl = symOfExprStruct == symOfDeclStruct; std::string identifier = nre.astKind == ASTKind::REF_EXPR ? StaticCast(&nre)->ref.identifier.Val() : StaticCast(&nre)->field.Val(); Position pos = nre.astKind == ASTKind::REF_EXPR ? nre.GetBegin() : StaticCast(&nre)->field.Begin(); if (inSameDecl && reportThis) { // Report error when this reference and declaration are in same decl. diag.Diagnose( pos, DiagKind::sema_assignment_of_member_variable_cannot_use_this_or_super, identifier, errorStr); } else if (!inSameDecl && typeManager.IsSubtype(symOfExprStruct->node->ty, symOfExprStruct->node->ty)) { // Report error when this reference and declaration are in decls which have inheritance relation. diag.Diagnose( pos, DiagKind::sema_assignment_of_member_variable_cannot_use_this_or_super, identifier, errorStr); } } } void TypeChecker::TypeCheckerImpl::CheckMutationInStruct(const ASTContext& ctx, const Expr& expr) const { auto target = expr.GetTarget(); if (!target || target->astKind != ASTKind::VAR_DECL) { return; } auto vd = StaticAs(target); // `static` variables are checked by other functions. if (vd->TestAttr(Attribute::STATIC)) { return; } Symbol* outFunc = ScopeManager::GetOutMostSymbol(ctx, SymbolKind::FUNC_LIKE, expr.scopeName); if (!outFunc || !outFunc->node || outFunc->node->TestAnyAttr(Attribute::CONSTRUCTOR, Attribute::MUT)) { return; } Ptr sd = nullptr; Symbol* outDecl = ScopeManager::GetCurOuterDeclOfScopeLevelX(ctx, expr, 0); // The `expr` may be nested in a `struct` or an `extend` of `struct`, and we use `StructTy` to get the `struct`. if (outDecl && Ty::IsTyCorrect(outDecl->node->ty) && outDecl->node->ty->IsStruct()) { sd = RawStaticCast(outDecl->node->ty)->decl; } if (!sd) { return; } CheckMutationInStructNonMut(diag, *sd, expr); } bool TypeChecker::TypeCheckerImpl::CheckIfUseInout(const FuncDecl& decl) { if (auto found = inoutCache.find(&decl); found != inoutCache.end()) { return found->second; } bool res = false; auto preVisit = [&res, &decl](Ptr node) -> VisitAction { auto ce = DynamicCast(node); if (!ce || ce->desugarExpr) { return VisitAction::WALK_CHILDREN; } for (const auto& arg : ce->args) { if (!arg->withInout) { continue; } auto tempExpr = arg->expr.get(); while (tempExpr) { if (auto target = tempExpr->GetTarget(); target && target->outerDecl == decl.outerDecl) { res = true; return VisitAction::STOP_NOW; } if (auto re = DynamicCast(tempExpr); re) { res = re->isThis; return res ? VisitAction::STOP_NOW : VisitAction::SKIP_CHILDREN; } if (auto ma = DynamicCast(tempExpr); ma) { tempExpr = ma->baseExpr.get(); } } } return VisitAction::WALK_CHILDREN; }; Walker(decl.funcBody.get(), preVisit).Walk(); return inoutCache.emplace(&decl, res).first->second; } // let instance of struct cannot access mut function. void TypeChecker::TypeCheckerImpl::CheckLetInstanceAccessMutableFunc( const ASTContext& ctx, const MemberAccess& ma, const Decl& target) { CJC_NULLPTR_CHECK(ma.baseExpr); if (!target.TestAttr(Attribute::MUT) || target.astKind != ASTKind::FUNC_DECL || !ma.baseExpr->ty || !MaybeStruct(*ma.baseExpr->ty)) { return; } bool useInout = CheckIfUseInout(static_cast(target)); Ptr tempMa = &ma; while (tempMa->baseExpr != nullptr) { auto baseExpr = tempMa->baseExpr.get(); // Get the real `baseExpr` by diving into the parentheses. while (baseExpr != nullptr && baseExpr->astKind == ASTKind::PAREN_EXPR) { baseExpr = StaticAs(baseExpr)->expr.get(); } bool inoutHeapAddr = useInout && baseExpr->ty->IsClassLike(); if (inoutHeapAddr) { diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_modify_heap_variable, *baseExpr); } // If the target of the `baseExpr` is a property or a `VarDecl` introduced by `let`, and it is of value type, // it cannot access mutable function. auto vd = DynamicCast(baseExpr->GetTarget()); bool immutableAccessMutableFunc = vd && (vd->astKind == ASTKind::PROP_DECL || !vd->isVar) && Ty::IsTyCorrect(vd->ty) && !vd->ty->IsClassLike(); if (immutableAccessMutableFunc) { DiagImmutableAccessMutableFunc(diag, ma, *tempMa); return; } if (baseExpr->astKind == ASTKind::MEMBER_ACCESS) { tempMa = StaticAs(baseExpr); } else if (baseExpr->astKind == ASTKind::REF_EXPR) { inoutHeapAddr = useInout && vd && vd->outerDecl && vd->outerDecl->ty->IsClassLike(); if (inoutHeapAddr) { diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_modify_heap_variable, *baseExpr); } break; } else if (Ty::IsTyCorrect(baseExpr->ty) && !baseExpr->ty->IsClassLike()) { DiagImmutableAccessMutableFunc(diag, ma, *tempMa); return; } else { break; } } if (ma.callOrPattern == nullptr) { auto range = ma.field.ZeroPos() ? MakeRange(ma.begin, ma.end) : MakeRange(ma.field); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_use_mutable_func_alone, ma, range, ma.field.Val()); builder.AddNote( target, MakeRangeForDeclIdentifier(target), "'" + target.identifier + "' is a mutable funciton"); } else { CheckMutationInStruct(ctx, *ma.baseExpr); } } cangjie_compiler-1.0.7/src/Sema/TypeCheckAnnotation.cpp000066400000000000000000000166241510705540100231120ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements typecheck apis for annotations. */ #include "TypeCheckerImpl.h" #include "Diags.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/RecoverDesugar.h" using namespace Cangjie; using namespace AST; using namespace Sema; namespace { void DiagAnnotationArgTarget(DiagnosticEngine& diag, const Node& node) { diag.DiagnoseRefactor(DiagKindRefactor::sema_annotation_arg_target, node); } OwnedPtr DesugarCustomAnnotation(Annotation& ann) { if (!ann.baseExpr) { return nullptr; } auto callExpr = CreateCallExpr(std::move(ann.baseExpr), std::move(ann.args)); callExpr->callKind = CallKind::CALL_ANNOTATION; CopyBasicInfo(&ann, callExpr.get()); AddCurFile(*callExpr, ann.curFile); return callExpr; } void RecoverToCustomAnnotation(Annotation& ann, CallExpr& callExpr) { RecoverToCallExpr(callExpr); if (callExpr.baseFunc->astKind == ASTKind::REF_EXPR) { auto re = StaticAs(callExpr.baseFunc.get()); if (re->callOrPattern == &callExpr) { re->callOrPattern = nullptr; } } ann.baseExpr = std::move(callExpr.baseFunc); ann.args = std::move(callExpr.args); } void DesugarAnnotationsArray(ImportManager& importManager, TypeManager& typeManager, Decl& decl, const std::vector>& annotations, std::vector>&& annotationsArray) { if (!annotationsArray.empty()) { auto objectClass = importManager.GetCoreDecl(OBJECT_NAME); auto arrayStruct = importManager.GetCoreDecl(STD_LIB_ARRAY); if (objectClass != nullptr && arrayStruct != nullptr) { auto arrayTy = typeManager.GetStructTy(*arrayStruct, {objectClass->ty}); decl.annotationsArray = CreateArrayLit(std::move(annotationsArray), arrayTy); decl.annotationsArray->EnableAttr(Attribute::IS_ANNOTATION); } else { CJC_ASSERT(annotations.size() == annotationsArray.size()); for (size_t i = 0; i < annotations.size(); ++i) { RecoverToCustomAnnotation(annotations[i], StaticCast(*annotationsArray[i])); } } } } bool CheckCustomAnnotationPlace(DiagnosticEngine& diag, const Decl& decl, const Annotation& ann) { auto declsInType = [](const Decl& d) { return d.TestAnyAttr(Attribute::IN_CLASSLIKE, Attribute::IN_STRUCT, Attribute::IN_ENUM, Attribute::IN_EXTEND); }; bool validCustomAnnotaionPlace = decl.IsNominalDecl() || IsInstanceConstructor(decl) || decl.TestAttr(Attribute::ENUM_CONSTRUCTOR) || declsInType(decl) || (decl.astKind == ASTKind::FUNC_PARAM && decl.outerDecl) || (decl.TestAttr(Attribute::GLOBAL) && (Is(decl) || Is(decl))); if (!validCustomAnnotaionPlace) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_annotation_custom_place, MakeRangeForDeclIdentifier(decl)); builder.AddHint(ann); builder.AddNote("custom annotations can only be used on non-local declarations"); } return validCustomAnnotaionPlace; } } // namespace void TypeChecker::TypeCheckerImpl::CheckAnnotationDecl(ASTContext& ctx, Annotation& ann) { if (ann.TestAttr(Attribute::TOOL_ADD)) { return; } CJC_ASSERT(ann.kind == AnnotationKind::ANNOTATION); if (ann.args.empty()) { return; } if (ann.args.size() > 1) { DiagAnnotationArgTarget(diag, ann); return; } CJC_NULLPTR_CHECK(ann.args.front()); auto& arg = *ann.args.front(); if (arg.name != "target") { DiagAnnotationArgTarget(diag, arg); return; } CJC_NULLPTR_CHECK(arg.expr); if (arg.expr->astKind != ASTKind::ARRAY_LIT) { diag.DiagnoseRefactor(DiagKindRefactor::sema_annotation_arg_target_array_lit, *arg.expr); return; } // Check arg.ty <: Array auto arrayStruct = importManager.GetCoreDecl("Array"); auto annotationKindEnum = importManager.GetCoreDecl("AnnotationKind"); if (arrayStruct == nullptr || annotationKindEnum == nullptr) { // Errors should have been reported if core package is not imported correctly. return; } auto targetTy = typeManager.GetStructTy(*arrayStruct, {typeManager.GetEnumTy(*annotationKindEnum)}); (void)Check(ctx, targetTy, ann.args.front().get()); } OwnedPtr TypeChecker::TypeCheckerImpl::CheckCustomAnnotation( ASTContext& ctx, const Decl& decl, Annotation& ann) { CJC_ASSERT(ann.kind == AnnotationKind::CUSTOM); auto callExpr = DesugarCustomAnnotation(ann); if (!callExpr) { return nullptr; } if (Ty::IsTyCorrect(Synthesize(ctx, callExpr.get())) && CheckCustomAnnotationPlace(diag, decl, ann)) { CJC_ASSERT(callExpr->ty->IsClass()); // The args information needs to be save into cjo. The original node need to be recover. std::vector> args = {}; for (auto& arg : callExpr->args) { args.emplace_back(ASTCloner::Clone(arg.get())); } ann.baseExpr = ASTCloner::Clone(callExpr->baseFunc.get()); ann.args = std::move(args); return callExpr; } RecoverToCustomAnnotation(ann, *callExpr); return nullptr; } void TypeChecker::TypeCheckerImpl::CheckAnnotations(ASTContext& ctx, Decl& decl) { std::vector> annotations; std::vector> annotationsArray; for (auto& anno : decl.annotations) { CJC_NULLPTR_CHECK(anno); switch (anno->kind) { case AnnotationKind::ANNOTATION: { CheckAnnotationDecl(ctx, *anno); break; } case AnnotationKind::CUSTOM: { auto callExpr = CheckCustomAnnotation(ctx, decl, *anno); if (!callExpr) { break; } CJC_ASSERT(callExpr && Ty::IsTyCorrect(callExpr->ty)); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND if (!anno->isCompileTimeVisible) { // this special attribute is to tell CHIR that this attr isCompileTimeVisible when computing // annotations not visible during compile time // it should have name NO_COMPILE_TIME_INFO, but there is no such attribute. Virtually any // attribute that cannot be used on const eval expr works here. callExpr->EnableAttr(Attribute::NO_REFLECT_INFO); } #endif annotationsArray.emplace_back(std::move(callExpr)); annotations.emplace_back(*anno); break; } case AnnotationKind::C: case AnnotationKind::CALLING_CONV: case AnnotationKind::FASTNATIVE: case AnnotationKind::FROZEN: { break; } default: { for (auto& arg : anno->args) { (void)Synthesize(ctx, arg.get()); } } } } DesugarAnnotationsArray(importManager, typeManager, decl, annotations, std::move(annotationsArray)); } cangjie_compiler-1.0.7/src/Sema/TypeCheckBuiltinExpr.cpp000066400000000000000000000720341510705540100232420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements typecheck apis for array exprs. */ #include "TypeCheckerImpl.h" #include #include #include "DiagSuppressor.h" #include "Diags.h" #include "JoinAndMeet.h" #include "TypeCheckUtil.h" #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/RecoverDesugar.h" #include "cangjie/AST/Symbol.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/Match.h" #include "cangjie/Sema/TypeManager.h" using namespace Cangjie; using namespace Sema; namespace { Ptr GetArrayElementTy(TypeManager& typeManager, ArrayTy& arrayTy) { // Array type parser guarantees 'dims' value at least 1. if (arrayTy.dims == 1) { return arrayTy.typeArgs[0]; } return typeManager.GetArrayTy(arrayTy.typeArgs[0], arrayTy.dims - 1); } bool HasInheritGivenDecl(const std::unordered_set>& superTys, const ClassLikeDecl& decl) { for (auto ty : superTys) { CJC_NULLPTR_CHECK(ty); if (ty->kind == TypeKind::TYPE_INTERFACE) { if (RawStaticCast(ty)->declPtr == &decl) { return true; } } else { if (RawStaticCast(ty)->declPtr == &decl) { return true; } } } return false; } } // namespace Ptr TypeChecker::TypeCheckerImpl::GetArrayTypeByInterface(Ty& interfaceTy) { Ptr invalid = TypeManager::GetInvalidTy(); auto arrayStruct = importManager.GetCoreDecl("Array"); if (!arrayStruct) { return invalid; } auto arrTys = promotion.Downgrade(*arrayStruct->ty, interfaceTy); if (arrTys.empty()) { return invalid; } else { return *arrTys.begin(); } } bool TypeChecker::TypeCheckerImpl::ChkArrayLit(ASTContext& ctx, Ty& target, ArrayLit& al) { auto targetTy = TypeCheckUtil::UnboxOptionType(&target); // Set type first, if check succeed, type will be updated. al.ty = TypeManager::GetInvalidTy(); if (targetTy->IsInterface()) { targetTy = GetArrayTypeByInterface(*targetTy); if (targetTy->IsInvalid()) { // If failed to get valid array type, there are two cases: // 1. array lit is empty, type is unable to be inferred. // 2. array lit is not empty, synthesize arrayLit 's type and check with target. if (al.children.empty()) { diag.Diagnose(al, DiagKind::sema_empty_arrayLit_type_undefined); return false; } auto arrayLitTy = SynArrayLit(ctx, al); if (typeManager.IsSubtype(arrayLitTy, &target)) { return true; } if (Ty::IsTyCorrect(arrayLitTy)) { DiagMismatchedTypes(diag, al, target); } return false; } } else if (!Ty::IsTyCorrect(targetTy) || (!targetTy->IsStructArray() && !Is(targetTy))) { DiagMismatchedTypesWithFoundTy(diag, al, targetTy->String(), "Array"); return false; } if (targetTy->typeArgs.empty()) { return false; } bool matched = true; auto vt = DynamicCast(targetTy); if (vt && vt->size != static_cast(al.children.size())) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_varray_size_match, al); builder.AddMainHintArguments(std::to_string(vt->size), std::to_string(al.children.size())); matched = false; } Ptr arrayElemTy = targetTy->typeArgs[0]; for (auto& child : al.children) { if (!Check(ctx, arrayElemTy, child.get())) { matched = false; break; } } if (matched) { al.ty = targetTy; } return matched; } Ptr TypeChecker::TypeCheckerImpl::SynArrayLit(ASTContext& ctx, ArrayLit& al) { if (al.children.empty()) { diag.Diagnose(al, DiagKind::sema_empty_arrayLit_type_undefined); al.ty = TypeManager::GetInvalidTy(); return al.ty; } std::set> arrayElemTys; bool hasInvalidElemTy = false; for (auto& child : al.children) { if (Synthesize(ctx, child.get()) && !ReplaceIdealTy(*child)) { hasInvalidElemTy = true; } arrayElemTys.insert(child->ty); } auto arrayStruct = importManager.GetCoreDecl("Array"); if (hasInvalidElemTy || arrayStruct == nullptr) { // If there exists invalid element ty or 'core' package is not imported correctly, // error will be throwed in other process. al.ty = TypeManager::GetInvalidTy(); return al.ty; } auto joinRes = JoinAndMeet(typeManager, arrayElemTys, {}, &importManager, al.curFile).JoinAsVisibleTy(); if (auto ty = std::get_if>(&joinRes)) { al.ty = typeManager.GetStructTy(*arrayStruct, {*ty}); } else { al.ty = TypeManager::GetInvalidTy(); auto errMsg = JoinAndMeet::CombineErrMsg(std::get>(joinRes)); diag.Diagnose(al, DiagKind::sema_inconsistency_elemType, "array").AddNote(errMsg); } return al.ty; } // Caller guarantees ae. Array has 2 args. bool TypeChecker::TypeCheckerImpl::ChkSizedArrayElement(ASTContext& ctx, Ty& elemTargetTy, ArrayExpr& ae) { PData::CommitScope(typeManager.constraints); { // Create a scope for DiagSuppressor. auto ds = DiagSuppressor(diag); // Support for old array constructor, Array(size, element: T). if (Check(ctx, &elemTargetTy, ae.args[1].get())) { ds.ReportDiag(); ae.initFunc = nullptr; // When initialize array with given elememt, init function is needless. return true; } } PData::Reset(typeManager.constraints); // For new array constructor, Array(size, element: (Int64)->T). auto sizeType = TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64); auto expectedExprTy = typeManager.GetFunctionTy({sizeType}, &elemTargetTy); if (Check(ctx, expectedExprTy, ae.args[1].get())) { return true; } diag.Diagnose(*ae.args[1], DiagKind::sema_array_expression_type_error); return false; } // Caller guarantees array type without component type 'RawArray(size, expr)' bool TypeChecker::TypeCheckerImpl::ChkSizedArrayWithoutElemTy(ASTContext& ctx, Ty& target, ArrayExpr& ae) { auto arrayTy = RawStaticCast(ae.type->ty); auto sizeType = TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64); // There are two possible constructors: // 1. RawArray(size, (Int64)->T) // 2. RawArray(size, item: T) // When second argument has argument name, check ArrayExpr as case 2, and check name's legality later. // Otherwise check ArrayExpr as case 1. CJC_ASSERT(ae.args.size() >= 1); bool isArgLambda = ae.args[1]->name.Empty(); if (!isArgLambda) { ae.initFunc = nullptr; // When initialize array with given element, init function is needless. } // Array as 'RawArray(size, expr)', need type inference. Type should match RawArray(size, element: (Int64)->T). if (target.IsInvalid() || target.IsInterface()) { // No target type, synthesis expression type. Type must be (Int64)->T. (void)Synthesize(ctx, ae.args[1].get()); ReplaceIdealTy(*ae.args[1]); ReplaceIdealTy(*ae.args[1]->expr); auto exprTy = ae.args[1]->ty; if (!Ty::IsTyCorrect(exprTy) || (isArgLambda && exprTy->kind != TypeKind::TYPE_FUNC)) { diag.Diagnose(*ae.args[0], DiagKind::sema_array_expression_type_error); return false; } auto elementTy = exprTy; if (isArgLambda) { // Constructor is RawArray(Int64, (Int64)->T), so array type can be inferred by funcTy. auto funcTy = RawStaticCast(exprTy); if (funcTy->paramTys.size() != 1 || !typeManager.IsSubtype(funcTy->paramTys[0], sizeType)) { diag.Diagnose(*ae.args[0], DiagKind::sema_array_expression_param_type_error); return false; } elementTy = funcTy->retTy; } ae.ty = typeManager.GetArrayTy(elementTy, arrayTy->dims); return target.IsInvalid() || typeManager.IsSubtype(ae.ty, &target); } // Target type is given, check with expression. auto elementTy = GetArrayElementTy(typeManager, static_cast(target)); auto expectedExprTy = isArgLambda ? typeManager.GetFunctionTy({sizeType}, elementTy) : elementTy; if (!Check(ctx, expectedExprTy, ae.args[1].get())) { diag.Diagnose(*ae.args[1], DiagKind::sema_array_element_type_error); return false; } ae.ty = ⌖ return true; } // Caller guarantees ae. Array has 2 args and target is arrayType or interfaceType. bool TypeChecker::TypeCheckerImpl::ChkSizedArrayExpr(ASTContext& ctx, Ty& target, ArrayExpr& ae) { auto arrayTy = RawStaticCast(ae.type->ty); auto sizeType = TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64); if (!Check(ctx, sizeType, ae.args[0].get())) { diag.Diagnose(*ae.args[0], DiagKind::sema_array_size_type_error); return false; } if (ae.args[1] == nullptr) { return false; } ae.initFunc = importManager.GetCoreDecl("arrayInitByFunction"); CJC_NULLPTR_CHECK(ae.initFunc); Synthesize(ctx, ae.initFunc); // Non-public decl should synthesize manually. CJC_ASSERT(arrayTy && !arrayTy->typeArgs.empty()); if (arrayTy->typeArgs[0]->IsInvalid()) { return ChkSizedArrayWithoutElemTy(ctx, target, ae); } // Check array elements with declared array type. Ptr elemTy = GetArrayElementTy(typeManager, *arrayTy); bool ret = ChkSizedArrayElement(ctx, *elemTy, ae); if (!target.IsInvalid() && ret) { // Check declared array type with targetTy.Not support option box type. ret = typeManager.IsSubtype(arrayTy, &target, true, false); if (ret && target.IsArray()) { ae.ty = ⌖ } } return ret; } // Caller guarantees array has one arg and 'List/Collection' exists. bool TypeChecker::TypeCheckerImpl::ChkSingeArgArrayWithoutElemTy(ASTContext& ctx, Ty& target, ArrayExpr& ae) { auto collectionDecl = importManager.GetCoreDecl("Collection"); CJC_NULLPTR_CHECK(collectionDecl); auto arrayTy = RawStaticCast(ae.type->ty); // Array(List/Collection), need type inference. CJC_ASSERT(!ae.args.empty()); auto exprTy = Synthesize(ctx, ae.args[0].get()); if (!Ty::IsTyCorrect(exprTy)) { diag.Diagnose(*ae.args[0], DiagKind::sema_array_single_element_type_error); return false; } auto superTys = typeManager.GetAllSuperTys(*exprTy); if (HasInheritGivenDecl(superTys, *collectionDecl)) { CJC_NULLPTR_CHECK(collectionDecl->ty); auto promotedTys = promotion.Promote(*exprTy, *collectionDecl->ty); auto collectionTy = *promotedTys.begin(); // Previous check guarantees 'promotedTys' has at least one value. ae.ty = typeManager.GetArrayTy(collectionTy->typeArgs[0], arrayTy->dims); ae.initFunc = importManager.GetCoreDecl("arrayInitByCollection"); CJC_NULLPTR_CHECK(ae.initFunc); Synthesize(ctx, ae.initFunc); // Non-public decl should synthesize manually. } else { diag.Diagnose(*ae.args[0], DiagKind::sema_array_single_element_type_error); return false; } return target.IsInvalid() || typeManager.IsSubtype(ae.ty, &target); } bool TypeChecker::TypeCheckerImpl::ChkSingeArgArrayExpr(ASTContext& ctx, Ty& target, ArrayExpr& ae) { auto collectionDecl = importManager.GetCoreDecl("Collection"); if (collectionDecl == nullptr) { return false; } auto arrayTy = RawStaticCast(ae.type->ty); if (arrayTy->typeArgs[0]->IsInvalid()) { return ChkSingeArgArrayWithoutElemTy(ctx, target, ae); } auto elemTy = (arrayTy->dims > 1) ? typeManager.GetArrayTy(arrayTy->typeArgs[0], arrayTy->dims - 1) : arrayTy->typeArgs[0]; // Check for Array(Collection). auto collectionTy = typeManager.GetInterfaceTy(*collectionDecl, {elemTy}); if (Check(ctx, collectionTy, ae.args[0].get())) { ae.initFunc = importManager.GetCoreDecl("arrayInitByCollection"); CJC_NULLPTR_CHECK(ae.initFunc); Synthesize(ctx, ae.initFunc); // Non-public decl should synthesize manually. return target.IsInvalid() || typeManager.IsSubtype(ae.ty, &target); } diag.Diagnose(*ae.args[0], DiagKind::sema_array_single_element_type_error); return false; } bool TypeChecker::TypeCheckerImpl::ChkArrayExpr(ASTContext& ctx, Ty& target, ArrayExpr& ae) { CJC_NULLPTR_CHECK(ae.type); Ptr targetTy = TypeCheckUtil::UnboxOptionType(&target); // Target type must be array/interface type or option boxed array/interface type. bool isWellTyped = Ty::IsTyCorrect(targetTy) && (targetTy->IsArray() || targetTy->IsInterface()); if (!isWellTyped) { diag.DiagnoseRefactor(DiagKindRefactor::sema_mismatched_types, ae) .AddMainHintArguments(targetTy->String(), "Array"); ae.ty = TypeManager::GetNonNullTy(ae.ty); return false; } ae.type->ty = Synthesize(ctx, ae.type.get()); if (ae.type->ty == nullptr || !ae.type->ty->IsArray()) { ae.ty = TypeManager::GetNonNullTy(ae.ty); return false; } ae.ty = ae.type->ty; auto arrayTy = RawStaticCast(ae.type->ty); bool ret = true; if (ae.args.empty()) { if (!arrayTy->typeArgs[0]->IsInvalid()) { ret = typeManager.IsSubtype(ae.ty, targetTy, true, false); } ae.ty = target.IsInterface() ? arrayTy : targetTy; } else if (ae.args.size() == 1) { ret = ChkSingeArgArrayExpr(ctx, *targetTy, ae); } else if (ae.args.size() == 2) { // Array init with 2 elements, size & initExpr. ret = ChkSizedArrayExpr(ctx, *targetTy, ae); } else { diag.Diagnose(ae, DiagKind::sema_array_too_much_argument); ret = false; } if (Ty::IsTyCorrect(ae.ty) && !Ty::IsTyCorrect(ae.type->ty)) { ae.type->ty = ae.ty; // Update array type ty, overwrite Array. } ChkArrayArgs(ae); return ret; } bool TypeChecker::TypeCheckerImpl::ChkVArrayArg(ASTContext& ctx, ArrayExpr& ve) { // check arg. if (ve.args.size() != 1) { diag.DiagnoseRefactor( DiagKindRefactor::sema_varray_args_number_mismatch, ve, MakeRange(ve.leftParenPos, ve.rightParenPos + 1)); return false; } bool ret = false; if (ve.args[0]->name.Empty()) { // For Lambda. auto sizeType = TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64); auto expectedExprTy = typeManager.GetFunctionTy({sizeType}, ve.type->ty->typeArgs[0]); ret = Check(ctx, expectedExprTy, ve.args[0].get()); } else { if (ve.args[0]->name != "repeat") { auto builder = diag.Diagnose(*ve.args[0], DiagKind::sema_unknown_named_argument, ve.args[0]->name.Val()); builder.AddNote("expect the name of the named parameter is 'item'"); return false; } // For item: T auto expectedItemTy = ve.type->ty->typeArgs[0]; ret = Check(ctx, expectedItemTy, ve.args[0].get()); } if (Ty::IsTyCorrect(ve.ty) && !Ty::IsTyCorrect(ve.type->ty)) { ve.type->ty = ve.ty; // Update array type ty, overwrite Array. } return ret; } bool TypeChecker::TypeCheckerImpl::ChkVArrayExpr(ASTContext& ctx, Ty& target, ArrayExpr& ve) { CJC_NULLPTR_CHECK(ve.type); Ptr targetTy = TypeCheckUtil::UnboxOptionType(&target); ve.type->ty = Synthesize(ctx, ve.type.get()); if (!Ty::IsTyCorrect(ve.type->ty) || !Is(ve.type->ty)) { ve.ty = TypeManager::GetInvalidTy(); return false; } if (!ChkVArrayArg(ctx, ve)) { ve.ty = TypeManager::GetInvalidTy(); return false; } // check T and size. if (!typeManager.IsSubtype(ve.type->ty, targetTy)) { DiagMismatchedTypesWithFoundTy(diag, ve, targetTy->String(), ve.type->ty->String()); ve.ty = TypeManager::GetInvalidTy(); return false; } ve.ty = ve.type->ty; return true; } Ptr TypeChecker::TypeCheckerImpl::SynArrayExpr(ASTContext& ctx, ArrayExpr& ae) { CJC_NULLPTR_CHECK(ae.type); ae.type->ty = Synthesize(ctx, ae.type.get()); if (ae.type->ty == nullptr || !ae.type->ty->IsArray()) { ae.ty = TypeManager::GetInvalidTy(); return ae.ty; } ae.ty = ae.type->ty; auto arrayTy = RawStaticCast(ae.type->ty); bool checkRet = true; if (ae.args.empty()) { if (arrayTy->typeArgs[0]->IsInvalid()) { diag.Diagnose(ae, DiagKind::sema_empty_arrayLit_type_undefined); checkRet = false; } } else if (ae.args.size() == 1) { checkRet = ChkSingeArgArrayExpr(ctx, *TypeManager::GetInvalidTy(), ae); } else if (ae.args.size() == 2) { // Array init with 2 elements, size & initExpr. checkRet = ChkSizedArrayExpr(ctx, *TypeManager::GetInvalidTy(), ae); } else { diag.Diagnose(ae, DiagKind::sema_array_too_much_argument); checkRet = false; } if (!checkRet || !ChkArrayArgs(ae)) { ae.ty = TypeManager::GetInvalidTy(); } else { ae.type->ty = ae.ty; // Update array type ty, overwrite Array. } return ae.ty; } Ptr TypeChecker::TypeCheckerImpl::SynVArrayExpr(ASTContext& ctx, ArrayExpr& ve) { CJC_NULLPTR_CHECK(ve.type); ve.type->ty = Synthesize(ctx, ve.type.get()); if (!Ty::IsTyCorrect(ve.type->ty) || !Is(ve.type->ty)) { ve.ty = TypeManager::GetInvalidTy(); return ve.ty; } ve.ty = ve.type->ty; if (!ChkVArrayArg(ctx, ve)) { ve.ty = TypeManager::GetInvalidTy(); return ve.ty; } return ve.ty; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool TypeChecker::TypeCheckerImpl::ChkPointerExpr(ASTContext& ctx, Ty& target, PointerExpr& cpe) { CJC_ASSERT(cpe.type && cpe.type->ty); bool ret = true; // 'var a: CPointer = CPointer()': Generic types should need to be derived. Ptr targetTy = TypeCheckUtil::UnboxOptionType(&target); if (!targetTy->typeArgs.empty() && Ty::IsTyCorrect(targetTy->typeArgs[0]) && (!Ty::IsTyCorrect(cpe.type->ty->typeArgs[0]) || cpe.type->ty->HasGeneric())) { cpe.type->ty = targetTy; } if (Ty::IsTyCorrect(targetTy)) { ret = Check(ctx, targetTy, cpe.type.get()); } else { // 'var a = CPointer()': Type derivation does not depend on target information. cpe.type->ty = Synthesize(ctx, cpe.type.get()); } cpe.ty = cpe.type->ty; // 'var a = CPointer()': Generic type cannot be derived. if (!Ty::IsTyCorrect(cpe.ty)) { diag.Diagnose(cpe, DiagKind::sema_pointer_unknow_generic_type); return false; } else if (!ret) { DiagMismatchedTypesWithFoundTy(diag, *cpe.sourceExpr, *targetTy, *cpe.ty); } // One arg. if (cpe.arg) { auto argTy = Synthesize(ctx, cpe.arg.get()); if (!Ty::IsTyCorrect(argTy) || !(argTy->IsPointer() || argTy->IsCFunc())) { if (!TypeCheckUtil::CanSkipDiag(*cpe.arg)) { diag.Diagnose(cpe, DiagKind::sema_pointer_single_element_type_error); } return false; } if (!cpe.arg->name.Empty()) { diag.Diagnose(cpe.arg->name.Begin(), cpe.arg->name.End(), DiagKind::sema_unknown_named_argument, cpe.arg->name.Val()); return false; } } if (Ty::IsTyCorrect(targetTy) && !typeManager.IsSubtype(cpe.ty, targetTy)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_mismatched_types, cpe) .AddMainHintArguments(target.String(), Ty::ToString(cpe.ty)); return false; } return ret; } #endif Ptr TypeChecker::TypeCheckerImpl::SynPointerExpr(ASTContext& ctx, PointerExpr& cptrExpr) { if (!ChkPointerExpr(ctx, *TypeManager::GetInvalidTy(), cptrExpr)) { return TypeManager::GetInvalidTy(); } return cptrExpr.ty; } bool TypeChecker::TypeCheckerImpl::SynCFuncCall(ASTContext& ctx, CallExpr& ce) { auto callee = DynamicCast(&*ce.baseFunc); if (!callee) { ce.ty = TypeManager::GetInvalidTy(); return false; } ce.ty = Synthesize(ctx, ce.baseFunc.get()); if (!Ty::IsTyCorrect(ce.baseFunc->ty) || !Ty::IsTyCorrect(ce.ty)) { ce.ty = TypeManager::GetInvalidTy(); return false; } if (ce.ty->IsCFunc()) { if (ce.args.size() != 1) { diag.Diagnose(*ce.baseFunc, DiagKind::sema_cfunc_too_many_arguments); ce.ty = TypeManager::GetInvalidTy(); return false; } Synthesize(ctx, ce.args[0]); if (!Ty::IsTyCorrect(ce.args[0]->ty)) { ce.ty = TypeManager::GetInvalidTy(); return false; } bool res{true}; if (!ce.args[0]->name.Empty()) { diag.Diagnose(ce.args[0]->name.Begin(), ce.args[0]->name.End(), DiagKind::sema_unknown_named_argument, ce.args[0]->name.Val()); // the program is invalid, yet the type check can still pass if all other checks hold, do not goto INVALID // here res = false; } // only CFunc<...>(CPointer(...)) is valid if (ce.args[0]->ty->IsPointer()) { ce.ty = ce.baseFunc->ty; return res; } } // Otherwise, report error and return a invalid ty. diag.Diagnose(*ce.args[0], DiagKind::sema_cfunc_ctor_must_be_cpointer); ce.ty = TypeManager::GetInvalidTy(); return false; } bool TypeChecker::TypeCheckerImpl::ChkBuiltinCall(ASTContext& ctx, Ty& target, CallExpr& ce) { static bool (TypeCheckerImpl::*const(CHK_FUNCS[]))(ASTContext&, AST::Ty&, AST::CallExpr&) = { &TypeCheckerImpl::ChkPointerCall, &TypeCheckerImpl::ChkCStringCall, &TypeCheckerImpl::ChkArrayCall, &TypeCheckerImpl::ChkVArrayCall, &TypeCheckerImpl::ChkCFuncCall, }; for (auto& chkFunc : CHK_FUNCS) { if ((this->*chkFunc)(ctx, target, ce)) { return true; } } return false; } bool TypeChecker::TypeCheckerImpl::IsCallOfBuiltInType(const CallExpr& ce, const AST::TypeKind kind) const { if (!ce.baseFunc) { return false; } auto baseTarget = ce.baseFunc->GetTarget(); if (!baseTarget) { return false; } auto isTypeAlias = IsBuiltinTypeAlias(*baseTarget, kind); BuiltInType builtInType; switch (kind) { case AST::TypeKind::TYPE_POINTER: builtInType = BuiltInType::POINTER; break; case AST::TypeKind::TYPE_CSTRING: builtInType = BuiltInType::CSTRING; break; case AST::TypeKind::TYPE_ARRAY: builtInType = BuiltInType::ARRAY; break; case AST::TypeKind::TYPE_VARRAY: builtInType = BuiltInType::VARRAY; break; default: return false; } auto isSpecifyBuiltinType = baseTarget->IsBuiltIn() && RawStaticCast(baseTarget)->IsType(builtInType); return isTypeAlias || isSpecifyBuiltinType; } bool TypeChecker::TypeCheckerImpl::ChkPointerCall(ASTContext& ctx, Ty& target, CallExpr& ce) { if (IsCallOfBuiltInType(ce, AST::TypeKind::TYPE_POINTER)) { bool ret = false; Ptr resultTy = TypeManager::GetInvalidTy(); DesugarPointerCall(ctx, ce); auto pointerExpr = StaticAs(ce.desugarExpr.get()); if (target.IsInvalid()) { resultTy = SynPointerExpr(ctx, *pointerExpr); ret = Ty::IsTyCorrect(resultTy); } else { ret = ChkPointerExpr(ctx, target, *pointerExpr); resultTy = pointerExpr->ty; } if (!ret) { if (pointerExpr->arg) { (void)ce.args.emplace_back(std::move(pointerExpr->arg)); } ce.desugarExpr = nullptr; } ce.ty = resultTy; return ret; } return false; } bool TypeChecker::TypeCheckerImpl::ChkCStringCall(ASTContext& ctx, Ty& target, CallExpr& ce) { if (IsCallOfBuiltInType(ce, AST::TypeKind::TYPE_CSTRING)) { // Check arg. auto cptrTy = typeManager.GetPointerTy(TypeManager::GetPrimitiveTy(TypeKind::TYPE_UINT8)); if (ce.args.size() != 1) { DiagWrongNumberOfArguments(diag, ce, {cptrTy}); ce.ty = TypeManager::GetInvalidTy(); return false; } if (!Check(ctx, cptrTy, ce.args[0].get())) { ce.ty = TypeManager::GetInvalidTy(); return false; } // Check whether target is CString or CType. #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND Ptr targetTy = TypeCheckUtil::UnboxOptionType(&target); if (Ty::IsTyCorrect(targetTy) && !targetTy->IsCString() && !targetTy->IsCType()) { DiagMismatchedTypesWithFoundTy(diag, ce, target.String(), std::string{CSTRING_NAME}); ce.ty = TypeManager::GetInvalidTy(); return false; } #endif ce.ty = TypeManager::GetCStringTy(); return true; } return false; } bool TypeChecker::TypeCheckerImpl::ChkArrayCall(ASTContext& ctx, Ty& target, CallExpr& ce) { // Check TypeAlias or constructor call of 'Array'. if (IsCallOfBuiltInType(ce, AST::TypeKind::TYPE_ARRAY)) { bool ret = false; Ptr resultTy = TypeManager::GetInvalidTy(); DesugarArrayCall(ctx, ce); auto arrayExpr = StaticAs(ce.desugarExpr.get()); if (target.IsInvalid()) { resultTy = SynArrayExpr(ctx, *arrayExpr); ret = Ty::IsTyCorrect(resultTy); } else { ret = ChkArrayExpr(ctx, target, *arrayExpr); resultTy = arrayExpr->ty; } if (!ret) { RecoverCallFromArrayExpr(ce); } ce.ty = resultTy; return ret; } ce.ty = TypeManager::GetNonNullTy(ce.ty); return false; } bool TypeChecker::TypeCheckerImpl::ChkVArrayCall(ASTContext& ctx, Ty& target, CallExpr& ce) { if (IsCallOfBuiltInType(ce, AST::TypeKind::TYPE_VARRAY)) { bool ret = false; Ptr resultTy = TypeManager::GetInvalidTy(); DesugarArrayCall(ctx, ce); auto arrayExpr = StaticAs(ce.desugarExpr.get()); if (target.IsInvalid()) { resultTy = SynVArrayExpr(ctx, *arrayExpr); ret = Ty::IsTyCorrect(resultTy); } else { ret = ChkVArrayExpr(ctx, target, *arrayExpr); resultTy = arrayExpr->ty; } if (!ret) { RecoverCallFromArrayExpr(ce); } ce.ty = resultTy; return ret; } return false; } bool TypeChecker::TypeCheckerImpl::ChkCFuncCall([[maybe_unused]] ASTContext& ctx, AST::Ty& target, AST::CallExpr& ce) { if (!ce.baseFunc) { return false; } if (!ce.baseFunc->GetTarget()) { return false; } Ptr realTarget = GetRealTarget(ce.baseFunc, ce.baseFunc->GetTarget()); if (!realTarget->IsBuiltIn() || !RawStaticCast(realTarget)->IsType(BuiltInType::CFUNC)) { return false; } if (!target.IsInvalid() && !target.IsCFunc()) { ce.ty = TypeManager::GetInvalidTy(); return false; } ce.baseFunc->SetTarget(realTarget); bool ret = SynCFuncCall(ctx, ce); if (!ret) { ce.ty = TypeManager::GetInvalidTy(); } return ret; } bool TypeChecker::TypeCheckerImpl::IsBuiltinTypeAlias(const Decl& decl, const AST::TypeKind kind) const { if (decl.astKind != ASTKind::TYPE_ALIAS_DECL) { return false; } auto& typeAliasDecl = static_cast(decl); // If typealias' base type is array, expr is not a normal call base. Ptr origTy = typeAliasDecl.type->ty; if (origTy) { if (kind == AST::TypeKind::TYPE_ANY) { return (origTy->kind == TypeKind::TYPE_ARRAY || origTy->kind == TypeKind::TYPE_POINTER || origTy->kind == TypeKind::TYPE_CSTRING || origTy->kind == TypeKind::TYPE_VARRAY); } else { return origTy->kind == kind; } } return false; } bool TypeChecker::TypeCheckerImpl::ChkArrayArgs(ArrayExpr& ae) { if (ae.args.empty()) { return true; } if (ae.args[0] && !ae.args[0]->name.Empty()) { diag.Diagnose(*ae.args[0], DiagKind::sema_array_first_arg_cannot_be_named); return false; } // Other valid array constructors must have 2 arguments. if (ae.args.size() != 2 || !ae.args[1]) { return true; } if (ae.initFunc) { if (!ae.args[1]->name.Empty()) { diag.Diagnose(*ae.args[1], DiagKind::sema_array_second_arg_cannot_be_named); return false; } } else { if (ae.args[1]->name != "repeat") { diag.Diagnose(*ae.args[1], DiagKind::sema_array_second_wrong_named_arg); return false; } } return true; } cangjie_compiler-1.0.7/src/Sema/TypeCheckCall.cpp000066400000000000000000004037171510705540100216560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements typecheck apis for function CallExpr. */ #include "TypeCheckerImpl.h" #include #include #include "Desugar/DesugarInTypeCheck.h" #include "DiagSuppressor.h" #include "Diags.h" #include "JoinAndMeet.h" #include "LocalTypeArgumentSynthesis.h" #include "TypeCheckUtil.h" #include "ExtraScopes.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/RecoverDesugar.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Basic/Match.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace Sema; using namespace TypeCheckUtil; using namespace AST; namespace { Ptr GetInoutTargetTy(const OwnedPtr& arg) { if (Ty::IsTyCorrect(arg->expr->ty)) { return arg->expr->ty; } CJC_ASSERT(arg->ty->IsPointer() && arg->ty->typeArgs.size() == 1); return arg->ty->typeArgs[0]; } // Parser guarantees CallExpr's args not contains nullptr. // NOTE: 'arg.name' may be named for trailing closure, so we need using calculated 'argName'. std::optional CheckNameArgInParams(DiagnosticEngine& diag, const FuncArg& arg, const std::string& argName, const FuncParamList& list, const std::vector& marks) { // Size of list.params and marks are guaranteed by caller that will not out of bound here. // Traverse the parameters, find the parameter has same identifier with the named argument. for (size_t j = 0; j < list.params.size(); ++j) { CJC_NULLPTR_CHECK(list.params[j]); if (list.params[j]->identifier != argName) { continue; } // Named argument must passed to named parameter. if (!list.params[j]->isNamedParam) { diag.Diagnose(arg, DiagKind::sema_invalid_named_arguments, argName); return {}; } if (marks[j]) { // When mark[j] is true, current arg's type has been added to argsTy already. A parameter // cannot be assigned two values. So return false. diag.Diagnose(arg, DiagKind::sema_multiple_named_argument, argName); return {}; } return {j}; } diag.Diagnose(arg, DiagKind::sema_unknown_named_argument, argName); return {}; } void SortCallArgumentByParamOrder(const FuncDecl& fd, CallExpr& ce, std::vector>& args) { bool namedArgFound = false; size_t pos = 0; for (auto& arg : ce.args) { CJC_NULLPTR_CHECK(arg->expr); arg->expr->ty = arg->withInout ? GetInoutTargetTy(arg) : arg->ty; auto argName = GetArgName(fd, *arg); if (argName.empty()) { if (namedArgFound) { // Positional argument can not appear after named argument. break; } if (pos < args.size()) { args[pos] = arg.get(); } else { // For varArg func. args.push_back(arg.get()); } ++pos; continue; } namedArgFound = true; // Find the parameters whose name is the same as arguments. Then get the correct position where the // argument's type should be. for (size_t j = 0; j < fd.funcBody->paramLists[0]->params.size(); ++j) { if (fd.funcBody->paramLists[0]->params[j]->identifier == argName) { args[j] = arg.get(); break; } } } } /** * Update callExpr's baseExpr's target and binding named funcArg with funcParam for LSP. * NOTE: This function should only be used if the 'fd' matches the callExpr. */ void UpdateCallTargetsForLSP(const CallExpr& ce, FuncDecl& fd) { // Set callExpr's baseExpr decl (Caller guarantees only RefExpr or MemberAccess is possible here). ReplaceTarget(ce.baseFunc.get(), &fd); // Caller guarantees. CJC_ASSERT(fd.funcBody && !fd.funcBody->paramLists.empty()); std::unordered_map> namedParams; for (auto& param : fd.funcBody->paramLists[0]->params) { if (param->isNamedParam) { namedParams.emplace(param->identifier, param.get()); } } for (auto& arg : ce.args) { if (!arg->name.Empty() && namedParams.count(arg->name) != 0) { ReplaceTarget(arg.get(), namedParams[arg->name]); } } } // Because two candidate types of different PrimitiveTy means argument is ideal type, // change primitive arg type to ideal type. void ProcessParamToArg(TypeManager& tyMgr, ArgumentTypeUnit& atu) { size_t argSize = atu.argTys.size(); // atu.argTys and atu.tysInArgOrder has same length. for (size_t t = 0; t < argSize; ++t) { auto& argTy = atu.argTys[t]; auto& paramTy = atu.tysInArgOrder[t]; CJC_ASSERT(argTy && paramTy); paramTy = tyMgr.InstOf(paramTy); if (argTy->IsInteger() && paramTy->IsNumeric()) { argTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_IDEAL_INT); } else if (argTy->IsFloating() && paramTy->IsNumeric()) { argTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_IDEAL_FLOAT); } } } // Caller guarantees call's baseFunc, and baseFunc's node members not null. bool IsSuperCall(const CallExpr& ce) { if (ce.baseFunc->astKind == ASTKind::REF_EXPR) { auto re = StaticAs(ce.baseFunc.get()); return re->isSuper; } else if (ce.baseFunc->astKind == ASTKind::MEMBER_ACCESS) { auto ma = StaticAs(ce.baseFunc.get()); if (ma->baseExpr == nullptr || ma->baseExpr->astKind != ASTKind::REF_EXPR) { return false; } auto reBase = StaticAs(ma->baseExpr.get()); return reBase->isSuper; } return false; } bool IsPossibleEnumConstructor(const std::vector>& candidates, const Expr& baseExpr) { if (baseExpr.astKind != ASTKind::REF_EXPR) { return false; } auto& re = static_cast(baseExpr); if (!candidates.empty() && Is(candidates.front()->outerDecl)) { for (auto& target : re.ref.targets) { if (target->ty == nullptr || target->ty->kind != TypeKind::TYPE_FUNC) { return true; } } } return false; } bool IsInGenericStructDecl(const FuncDecl& fd) { return fd.outerDecl && fd.outerDecl->IsNominalDecl() && fd.outerDecl->generic; } // Get the generic types in candidate. std::vector> GetAllGenericTys(const Expr& expr, const FuncDecl& fd) { std::vector> result; std::vector> generics; if (fd.funcBody->generic) { // Get the candidate's generic types. generics.push_back(fd.funcBody->generic.get()); } if (IsInGenericStructDecl(fd)) { // 1. Collect if the callee is memberAccess of decl's static member. // 2. Collect if candidate is constructor of struct/class/enum. bool isStaticMemberAccess = (expr.astKind == ASTKind::MEMBER_ACCESS) && fd.TestAttr(Attribute::STATIC); if (isStaticMemberAccess || IsClassOrEnumConstructor(fd)) { generics.push_back(fd.outerDecl->generic.get()); } } for (auto& generic : generics) { for (auto& it : generic->typeParameters) { result.push_back(it->ty); } } return result; } /** Calculate enum target's scope level when using. */ int64_t CalculateEnumCtorScopeLevel(const ASTContext& ctx, const CallExpr& ce, const FuncDecl& fd) { if (fd.TestAttr(Attribute::IMPORTED)) { return 0; // Imported enum constructor is treated as lifted to toplevel. } // If current context inside enum decl, return origin scope level. if (IsNode1ScopeVisibleForNode2(fd, ce)) { return static_cast(fd.scopeLevel); } // Else if current context inside enum decl's extend decl, return origin scope level. Symbol* symOfCurStruct = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, ce.symbol->scopeName); if (symOfCurStruct && symOfCurStruct->node && symOfCurStruct->node->astKind == ASTKind::EXTEND_DECL) { auto ed = RawStaticCast(symOfCurStruct->node); if (ed->extendedType && fd.outerDecl && Ty::GetDeclPtrOfTy(ed->extendedType->ty) == fd.outerDecl) { return static_cast(fd.scopeLevel); } } // Otherwise return global scope level. return 0; } // Check whether given i types can passed to j types (T_i <: T_j). bool CompareParamTys(TypeManager& tyMgr, const std::vector>& iTys, const std::vector>& jTys) { if (iTys.size() != jTys.size()) { return false; } size_t argSize = iTys.size(); for (size_t t = 0; t < argSize; ++t) { if (iTys[t]->IsNumeric() && jTys[t]->IsNumeric()) { ComparisonRes res = CompareIntAndFloat(*iTys[t], *jTys[t]); if (res == ComparisonRes::GT) { return false; } else { continue; } } if (!tyMgr.IsSubtype(iTys[t], jTys[t])) { return false; } } return true; } std::vector ResolveTypeMappings( TypeManager& tyMgr, const std::vector& typeMappings, const FuncDecl& fd) { CJC_ASSERT(fd.ty && fd.ty->kind == TypeKind::TYPE_FUNC); auto mappingSize = typeMappings.size(); std::vector matchMark(mappingSize, true); // When getting false, the type mapping is excluded. for (size_t i = 0; i < mappingSize; ++i) { if (!matchMark[i]) { continue; } for (size_t j = i + 1; j < mappingSize; ++j) { if (!matchMark[j]) { continue; } auto iTy = DynamicCast(tyMgr.ApplySubstPack(fd.ty, typeMappings[i])); auto jTy = DynamicCast(tyMgr.ApplySubstPack(fd.ty, typeMappings[j])); if (iTy == jTy) { matchMark[j] = false; continue; } CJC_NULLPTR_CHECK(iTy); CJC_NULLPTR_CHECK(jTy); auto iToJ = CompareParamTys(tyMgr, iTy->paramTys, jTy->paramTys); auto jToI = CompareParamTys(tyMgr, jTy->paramTys, iTy->paramTys); if (iToJ && !jToI) { matchMark[j] = false; } else if (!iToJ && jToI) { matchMark[i] = false; break; } } } std::vector resMappings; for (size_t i = 0; i < matchMark.size(); ++i) { if (matchMark[i]) { resMappings.push_back(typeMappings[i]); } } return resMappings; } void DiagnoseForMultiMapping(DiagnosticEngine& diag, const CallExpr& ce, const FuncDecl& fd, const std::vector& typeMappings, const std::vector& resMappings) { // Do not know how to skip the whole type checking process at some earlier stage for function calls. Patch here. bool shouldDiag = std::none_of(ce.args.cbegin(), ce.args.cend(), [](const OwnedPtr& farg) { return farg && farg->expr && farg->expr->ty && farg->expr->ty->IsInvalid(); }); if (!shouldDiag) { return; } if (resMappings.empty()) { diag.Diagnose(ce, DiagKind::sema_parameters_and_arguments_mismatch); } else if (resMappings.size() > typeMappings.size()) { diag.Diagnose(ce, DiagKind::sema_generic_ambiguous_method_match_in_upper_bounds, fd.identifier.Val()); } else { diag.Diagnose(ce, DiagKind::sema_ambiguous_arg_type); } } bool IsInterfaceFuncWithSameSignature(TypeManager& tyMgr, const FunctionMatchingUnit& i, const FunctionMatchingUnit& j) { // Check whether both candidates are abstract interface function with same signature. bool bothInInterface = i.fd.outerDecl && i.fd.outerDecl->astKind == ASTKind::INTERFACE_DECL && j.fd.outerDecl && j.fd.outerDecl->astKind == ASTKind::INTERFACE_DECL && i.fd.TestAttr(Attribute::ABSTRACT) && j.fd.TestAttr(Attribute::ABSTRACT); if (bothInInterface) { auto iTy = tyMgr.ApplySubstPack(i.fd.ty, i.typeMapping); auto jTy = tyMgr.ApplySubstPack(j.fd.ty, j.typeMapping); return iTy == jTy; } return false; } bool IsFuncDeclPossibleVariadic(const FuncDecl& fd) { // enum constructors cannot be variadic. if (fd.TestAttr(Attribute::ENUM_CONSTRUCTOR)) { return false; } // Operator overloading except for `()`, `[]` cannot be variadic. if (fd.identifier != "()" && fd.identifier != "[]" && IsFieldOperator(fd.identifier)) { return false; } CJC_ASSERT(fd.funcBody && !fd.funcBody->paramLists.empty()); auto& params = fd.funcBody->paramLists.front()->params; auto iter = std::find_if(params.crbegin(), params.crend(), [](auto& param) { CJC_NULLPTR_CHECK(param); return !param->isNamedParam; }); return iter != params.crend() && Ty::IsTyCorrect((*iter)->ty) && (*iter)->ty->IsStructArray(); } size_t GetPositionalParamSize(const FuncDecl& fd) { CJC_ASSERT(fd.funcBody && !fd.funcBody->paramLists.empty()); auto& params = fd.funcBody->paramLists.front()->params; size_t namedSize = 0; for (auto iter = params.crbegin(); iter < params.crend(); ++iter) { CJC_NULLPTR_CHECK(*iter); if ((*iter)->isNamedParam) { namedSize++; } else { break; } } return params.size() - namedSize; } size_t GetPositionalArgSize(const CallExpr& ce) { size_t count = 0; for (auto& arg : ce.args) { if (!arg->name.Empty()) { break; } count++; } return count; } // Caller guarantees the fd is not a function with variable length argument. bool HasSamePositionalArgsSize(const FuncDecl& fd, const CallExpr& ce) { size_t positionalParamSize = GetPositionalParamSize(fd); size_t positionalArgSize = GetPositionalArgSize(ce); if (!ce.args.empty() && ce.args.back()->TestAttr(Attribute::IMPLICIT_ADD) && !fd.funcBody->paramLists[0]->params.empty() && fd.funcBody->paramLists[0]->params.back()->isNamedParam && positionalArgSize == ce.args.size()) { // For trailing closure argument, if the original func param is named and no other named argument existed, // it should be treated as named argument. --positionalArgSize; } return positionalParamSize == positionalArgSize; } bool IsCallSourceExprVariadic(const CallExpr& ce) { // A desugarred variadic function call cannot be variadic. return ce.sourceExpr && ce.sourceExpr->astKind == ASTKind::CALL_EXPR && StaticAs(ce.sourceExpr)->callKind == CallKind::CALL_VARIADIC_FUNCTION; } // Let `v` be the size of the varargs in `ce`, we have `positionalArgSize = positionalParamSize - 1 + v`. // If it is compatible, then `v >= 0`, i.e. `positionalArgSize + 1 - positionalParamSize >= 0`. // This is the so called "variadic inequality". bool AreFuncDeclAndCallExprFullfillVariadicInequality(const FuncDecl& fd, const CallExpr& ce) { size_t positionalArgSize = GetPositionalArgSize(ce); size_t positionalParamSize = GetPositionalParamSize(fd); return positionalArgSize + 1 >= positionalParamSize; } bool IsPossibleVariadicFunction(const FuncDecl& fd, const CallExpr& ce) { return !IsCallSourceExprVariadic(ce) && IsFuncDeclPossibleVariadic(fd) && AreFuncDeclAndCallExprFullfillVariadicInequality(fd, ce); } bool IsPossibleVariadicFunction(const std::vector>& candidates, const CallExpr& ce) { return Utils::In(candidates, [&ce](Ptr fd) { return fd != nullptr && !IsCallSourceExprVariadic(ce) && IsFuncDeclPossibleVariadic(*fd) && AreFuncDeclAndCallExprFullfillVariadicInequality(*fd, ce); }); } // This function is used to check whether a function typed expression could be variadic. bool IsPossibleVariadicFunction(const FuncTy& funcTy, const CallExpr& ce) { // `hasVariableLenArg` indicates the `funcTy` is a variadic C FFI function, // it cannot be a Cangjie variadic function at the same time. if (funcTy.hasVariableLenArg || funcTy.paramTys.empty() || IsCallSourceExprVariadic(ce) || GetPositionalArgSize(ce) + 1 < funcTy.paramTys.size()) { return false; } return Ty::IsTyCorrect(funcTy.paramTys.back()) && funcTy.paramTys.back()->IsStructArray(); } inline Ptr GetRealNonOptionTy(Ptr ty) { if (ty->IsCoreOptionType()) { return GetRealNonOptionTy(ty->typeArgs[0]); } return ty; } std::vector> SyntaxFilterCandidates(const CallExpr& ce, const std::vector>& candidates) { std::vector> definitelyMismatched; std::vector> mismatched; std::vector> matched; // Filter the candidates by lambda arguments and associated parameter types. for (auto fd : candidates) { bool mismatch = false; bool definitelyMismatch = false; auto paramTys = GetParamTys(*fd); for (size_t i = 0; i < ce.args.size(); i++) { auto lamArg = DynamicCast(ce.args[i]->expr.get()); if (!lamArg) { continue; } else if (ce.args[i]->TestAttr(Attribute::IMPLICIT_ADD)) { // For trailing closure, checking the argument with last parameter type, // ignore the mismatched size of arguments. definitelyMismatch = paramTys.empty() || !paramTys.back()->IsFunc(); break; } else if (ce.args[i]->name.Empty() && paramTys.size() <= i) { definitelyMismatch = true; break; } Ptr paramTy = nullptr; if (ce.args[i]->name.Empty()) { paramTy = GetRealNonOptionTy(paramTys[i]); } else if (auto res = GetParamTyAccordingToArgName(*fd, ce.args[i]->name)) { paramTy = GetRealNonOptionTy(res.value().first); } // Do not use size of lambda's parameters as filter condition, since it may be better matched than others. if (!paramTy || !Utils::In(paramTy->kind, {TypeKind::TYPE_FUNC, TypeKind::TYPE_ANY, TypeKind::TYPE_GENERICS})) { mismatch = true; } } definitelyMismatch ? definitelyMismatched.emplace_back(fd) : (mismatch ? mismatched.emplace_back(fd) : matched.emplace_back(fd)); } // Should not return empty vector for future diagnoses. return matched.empty() ? (mismatched.empty() ? definitelyMismatched : mismatched) : matched; } bool ChkArgsOrder(DiagnosticEngine& diag, const CallExpr& ce) { bool foundNamedArg = false; for (auto& arg : ce.args) { if (foundNamedArg) { if (arg->name.Empty()) { diag.DiagnoseRefactor(DiagKindRefactor::sema_unordered_arguments, *arg); return false; } } else if (!arg->name.Empty()) { foundNamedArg = true; } } return true; } std::map>> FuncDeclsGroupByFixedPositionalArity( const std::vector>& candidates, const CallExpr& ce) { std::map>> fixedPositionalArityToGroup; for (auto fd : candidates) { CJC_NULLPTR_CHECK(fd); if (!fd || !IsPossibleVariadicFunction(*fd, ce)) { continue; } size_t fixedPositionalArity = GetPositionalParamSize(*fd) - 1; const auto iter = fixedPositionalArityToGroup.find(fixedPositionalArity); if (iter != fixedPositionalArityToGroup.cend()) { iter->second.emplace_back(fd); } else { std::vector> group = {fd}; fixedPositionalArityToGroup.emplace(fixedPositionalArity, group); } } return fixedPositionalArityToGroup; } inline bool IsSearchTargetInMemberAccessUpperBound(const CallExpr& ce) { if (auto ma = DynamicCast(ce.baseFunc.get())) { return ma->isExposedAccess; } return false; } std::pair CheckAndObtainTypeMappingBetweenInterfaceAndExtend( const ExtendDecl& ed, const InterfaceDecl& id) { bool flag = false; TypeSubst typeMapping; for (auto& super : ed.inheritedTypes) { if (!super->ty->IsInterface()) { continue; } auto superTy = RawStaticCast(super->ty); if (&id != Ty::GetDeclPtrOfTy(superTy)) { continue; } typeMapping = TypeCheckUtil::GenerateTypeMapping(id, superTy->typeArgs); if (typeMapping.empty()) { continue; // Failed to generate typeMapping. } flag = true; break; } return {flag, typeMapping}; } void RemoveNonAnnotationCandidates(std::vector>& candidates) { for (auto iter = candidates.begin(); iter != candidates.end();) { CJC_NULLPTR_CHECK(*iter); bool isCustomAnnotation = IsInstanceConstructor(**iter) && (*iter)->TestAttr(Attribute::IN_CLASSLIKE) && (*iter)->outerDecl && (*iter)->outerDecl->TestAttr(Attribute::IS_ANNOTATION); if (isCustomAnnotation) { ++iter; } else { iter = candidates.erase(iter); } } } inline bool IsFunctionalType(const Ty& ty) { if (auto genericTy = DynamicCast(&ty)) { return std::any_of( genericTy->upperBounds.cbegin(), genericTy->upperBounds.cend(), [](auto ty) { return ty->IsFunc(); }); } return ty.IsFunc(); } bool CheckUseTrailingClosureWithFunctionType( DiagnosticEngine& diag, FuncArg& arg, const Ty& originalTy, const FuncDecl& candidate) { // Implicit added func argument is added by trailing closure. if (!arg.TestAttr(Attribute::IMPLICIT_ADD) || IsFunctionalType(originalTy)) { return true; } arg.ty = TypeManager::GetInvalidTy(); CJC_NULLPTR_CHECK(arg.expr); arg.expr->ty = TypeManager::GetInvalidTy(); std::string message = originalTy.IsGeneric() ? "generic type without upper bound of function type" : "non-function type"; auto diagBuilder = diag.DiagnoseRefactor( DiagKindRefactor::sema_trailing_lambda_cannot_used_for_non_function, arg, message); diagBuilder.AddMainHintArguments(originalTy.String()); diagBuilder.AddNote(candidate, MakeRangeForDeclIdentifier(candidate), "found candidate"); return false; } } // namespace bool TypeChecker::TypeCheckerImpl::CheckArgsWithParamName(const CallExpr& ce, const FuncDecl& fd) { // Record the type of the default parameter first. std::vector hasTy; CJC_ASSERT(fd.funcBody && !fd.funcBody->paramLists.empty()); for (auto& param : fd.funcBody->paramLists[0]->params) { hasTy.push_back(param->TestAttr(Attribute::HAS_INITIAL)); } size_t funcDeclParamListLen = fd.funcBody->paramLists[0]->params.size(); // Help to mark the arguments whose type have been checked as valid. std::vector marks(funcDeclParamListLen, false); // Help to record whether named argument has been appear. bool namedArgFound = false; size_t pos = 0; for (size_t i = 0; i < ce.args.size(); ++i) { CJC_NULLPTR_CHECK(ce.args[i]); // Parser guarantees CallExpr's args not contains nullptr. size_t index = 0; std::string argName = GetArgName(fd, *ce.args[i]); if (argName.empty()) { // Positional argument can not appear after named argument. if (namedArgFound) { diag.DiagnoseRefactor(DiagKindRefactor::sema_unordered_arguments, *ce.args[i]); return false; } // Named parameter must be passed with named argument. if (pos < funcDeclParamListLen && fd.funcBody->paramLists[0]->params[pos]->isNamedParam) { DiagNeedNamedArgument(diag, ce, fd, pos, i); return false; } index = pos; ++pos; } else { auto res = CheckNameArgInParams(diag, *ce.args[i], argName, *fd.funcBody->paramLists[0], marks); if (res.has_value()) { index = res.value(); } else { return false; } namedArgFound = true; } if (index < funcDeclParamListLen) { marks[index] = true; hasTy[index] = true; } } for (auto it : std::as_const(hasTy)) { if (it) { continue; } if (ce.sourceExpr == nullptr) { DiagWrongNumberOfArguments(diag, ce, fd); } return false; } return true; } // Caller guarantees call's baseFunc, and baseFunc's node members not null. bool TypeChecker::TypeCheckerImpl::IsGenericCall(const ASTContext& ctx, const CallExpr& ce, const FuncDecl& fd) const { // 1. If fd has generic types, return true. bool hasGeneric = fd.TestAttr(Attribute::GENERIC) || (fd.ty && fd.ty->HasGeneric()); if (hasGeneric) { return true; } // 2. If the caller has type arguments, return true. if (!ce.baseFunc->GetTypeArgs().empty()) { return true; } // 3. Generic class's constructor may not be a generic function with type parameters. So we should check its outer // class when call type is super call. if (IsSuperCall(ce) && fd.funcBody->parentClassLike && fd.funcBody->parentClassLike->generic) { return true; } // 4. If the call's baseFunc is a member access, and the base's type has generic, return true; if (ce.baseFunc->astKind == ASTKind::MEMBER_ACCESS) { auto ma = StaticAs(ce.baseFunc.get()); auto baseType = ma->baseExpr ? ma->baseExpr->ty : nullptr; if (!Ty::IsTyCorrect(baseType)) { return false; } auto decl = Ty::GetDeclPtrOfTy(baseType); // Check whether decl is generic if (decl && decl->TestAttr(Attribute::GENERIC) && (decl->astKind != ASTKind::ENUM_DECL || fd.TestAttr(Attribute::ENUM_CONSTRUCTOR) || fd.TestAttr(Attribute::STATIC))) { return true; } // 5. If the target function is a member of generic structure decl, and the base's extends/implements the // decl, return true. if (IsInGenericStructDecl(fd)) { return true; } } // 6. If current callExpr is in a classlike decl, and the target function is decl's super classlike, and the super // classlike is a generic decl. if (ce.baseFunc->astKind == ASTKind::REF_EXPR) { auto sym1 = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, ce.scopeName); auto sym2 = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, fd.scopeName); if (sym1 != sym2 && sym2 != nullptr && Ty::IsTyCorrect(sym2->node->ty) && !sym2->node->ty->typeArgs.empty()) { return true; } } return false; } // Pass parameters of candidate i to candidate j as arguments. Compare two candidates. bool TypeChecker::TypeCheckerImpl::CompareFuncCandidates( FunctionMatchingUnit& i, FunctionMatchingUnit& j, const CallExpr& ce) { size_t argSize = i.tysInArgOrder.size(); // Only resolve for generic when candidate is generic related and its parameter's types contains generic type. bool isGeneric = GetCurrentGeneric(j.fd, ce) != nullptr; if (isGeneric && Utils::In(j.tysInArgOrder, [](auto ty) { return ty->HasGeneric(); })) { InstCtxScope ic(*this); ic.SetRefDeclSimple(j.fd, ce); ArgumentTypeUnit atu(i.tysInArgOrder, j.tysInArgOrder, {}); ProcessParamToArg(typeManager, atu); auto argPack = LocTyArgSynArgPack{GetTyVarsToSolve(typeManager.GetInstMapping()), atu.argTys, atu.tysInArgOrder, {}, TypeManager::GetInvalidTy(), TypeManager::GetInvalidTy(), {}}; auto optSubst = LocalTypeArgumentSynthesis(typeManager, argPack, {}).SynthesizeTypeArguments(); if (!optSubst.has_value()) { return false; } for (size_t t = 0; t < argSize; ++t) { // Int64 has priority than float and other int. if (!i.tysInArgOrder[t]->IsNumeric() || !j.tysInArgOrder[t]->IsNumeric()) { continue; } ComparisonRes res = CompareIntAndFloat(*i.tysInArgOrder[t], *j.tysInArgOrder[t]); if (res == ComparisonRes::GT) { return false; } } return true; } if (i.fd.outerDecl && j.fd.outerDecl) { // Since interface allows multiple inheritance, and have default functions, we need to resolve shadow case. // When both candidates are member functions and they have the same size of parameters and function signatures, // resolve them by deciding whether one is implementing the other. bool sameNumberOfParams = j.fd.ty->typeArgs.size() == i.fd.ty->typeArgs.size(); if (sameNumberOfParams && typeManager.IsFuncParameterTypesIdentical(i.tysInArgOrder, j.tysInArgOrder)) { bool isJImplementable = j.fd.outerDecl->astKind == ASTKind::INTERFACE_DECL || j.fd.TestAttr(Attribute::ABSTRACT); bool isITheImplementation = i.fd.outerDecl->astKind != ASTKind::INTERFACE_DECL && !i.fd.TestAttr(Attribute::ABSTRACT); return isJImplementable && isITheImplementation; } } // Check whether T_i <: T_j return CompareParamTys(typeManager, i.tysInArgOrder, j.tysInArgOrder); } std::vector TypeChecker::TypeCheckerImpl::ResolveOverload( std::vector>& candidates, const CallExpr& ce) { auto targetNum = candidates.size(); std::vector targetMark(targetNum, true); // When get false, the target is excluded. for (size_t i = 0; i < targetNum; ++i) { if (!targetMark[i]) { continue; } for (size_t j = i + 1; j < targetNum; ++j) { if (!targetMark[j]) { continue; } auto iToJ = CompareFuncCandidates(*candidates[i], *candidates[j], ce); auto jToI = CompareFuncCandidates(*candidates[j], *candidates[i], ce); if (iToJ && !jToI) { targetMark[j] = false; } else if (!iToJ && jToI) { targetMark[i] = false; break; } if (IsInterfaceFuncWithSameSignature(typeManager, *candidates[i], *candidates[j])) { // When two interface candidates have same signature, disable one of them. targetMark[i] = false; break; } } } std::vector results; for (size_t i = 0; i < targetMark.size(); ++i) { if (targetMark[i]) { results.push_back(i); } } return results; } void TypeChecker::TypeCheckerImpl::InstantiatePartOfTheGenericParameters( std::vector>& candidates) { for (auto& candidate : candidates) { if (candidate->typeMapping.u2i.empty()) { // If the typeMapping is empty, current candidate has no generic type parameter. No need to do // instantiation. continue; } if (IsClassOrEnumConstructor(candidate->fd)) { continue; } SubstPack outerDeclTypeMapping = candidate->typeMapping; CJC_NULLPTR_CHECK(candidate->fd.funcBody); auto generic = candidate->fd.funcBody->generic.get(); if (generic) { // Delete the generic types that introduced from current candidate. Only type parameters introduced by the // outer decls are retained. for (auto& it : generic->typeParameters) { outerDeclTypeMapping.u2i.erase(StaticCast(it->ty)); } if (outerDeclTypeMapping.u2i.empty()) { // If the candidate does not introduce type parameters from the outer layer, there is no need to // instantiate the parameters. continue; } } // Do instantiation. for (auto& it : candidate->tysInArgOrder) { it = typeManager.ApplySubstPack(it, outerDeclTypeMapping); } } } // When the baseExpr of other expr is a memberAccess, // we could build the type mapping by the memberAccess's baseExpr's type. void TypeChecker::TypeCheckerImpl::GenerateTypeMappingForBaseExpr(const Expr& baseExpr, MultiTypeSubst& typeMapping) { if (baseExpr.astKind != ASTKind::MEMBER_ACCESS) { return; } auto& ma = static_cast(baseExpr); if (!Ty::IsTyCorrect(ma.baseExpr->ty)) { return; } CJC_ASSERT(!ma.baseExpr->ty->HasIntersectionTy()); if (IsThisOrSuper(*ma.baseExpr)) { typeManager.GenerateGenericMapping(typeMapping, *ma.baseExpr->ty); return; } auto baseDecl = GetRealTarget(ma.baseExpr.get(), ma.baseExpr->GetTarget()); if (baseDecl && baseDecl->astKind == ASTKind::PACKAGE_DECL) { return; } auto maTarget = ma.GetTarget(); // NOTE: member access of enum constructor is also considered as type decl's member access for inference. bool typeDeclMemberAccess = baseDecl && maTarget && maTarget->outerDecl && (baseDecl->IsTypeDecl() || baseDecl->TestAttr(Attribute::ENUM_CONSTRUCTOR)); if (typeDeclMemberAccess) { CJC_NULLPTR_CHECK(maTarget->outerDecl->ty); auto promoteMapping = promotion.GetPromoteTypeMapping(*ma.baseExpr->ty, *maTarget->outerDecl->ty); auto baseTypeArgs = ma.baseExpr->GetTypeArgs(); std::unordered_set> definedTys; std::for_each( baseTypeArgs.begin(), baseTypeArgs.end(), [&definedTys](auto type) { definedTys.emplace(type->ty); }); auto genericTys = GetAllGenericTys(baseDecl->ty); for (auto it = promoteMapping.begin(); it != promoteMapping.end(); ++it) { // If mapped 'ty' is existed in 'baseDecl' generic types // and is not found in user defined type args, remove it from mapping. Utils::EraseIf(it->second, [&genericTys, &definedTys](auto ty) { return genericTys.count(ty) != 0 && definedTys.count(ty) == 0; }); } MergeMultiTypeSubsts(typeMapping, promoteMapping); } else { typeManager.GenerateGenericMapping(typeMapping, *ma.baseExpr->ty); } } // NOTE: all sema tys of 'Type' & 'TypeDecl' are set in 'PreCheck' step. TypeSubst TypeChecker::TypeCheckerImpl::GenerateTypeMappingByTyArgs( const std::vector>& typeArgs, const Generic& generic) const { TypeSubst typeMapping; // Add the mapping when we can get the type parameters and type arguments. auto typeArgsSize = typeArgs.size(); // Get the type arguments size and compare it with type parameters' size. They should be equal. if (generic.typeParameters.size() != typeArgsSize) { return typeMapping; } // If callExpr gives type arguments and fd has type parameters as well, generate the typeMapping directly. for (size_t i = 0; i < typeArgsSize; ++i) { if (!generic.typeParameters[i]) { continue; } Ptr typeArgTy = TypeManager::GetInvalidTy(); if (auto type = typeArgs[i]; type) { if (type->ty && type->ty->HasIntersectionTy()) { continue; } typeArgTy = type->ty; } if (Ty::IsTyCorrect(generic.typeParameters[i]->ty) && Ty::IsTyCorrect(typeArgTy)) { typeMapping.emplace(StaticCast(generic.typeParameters[i]->ty), typeArgTy); } } return typeMapping; } namespace { // apply all indirect substituions in inst map, // so that equivalent substituions will appear the same void ReduceSubstPack(SubstPack& maps, TypeManager& tyMgr) { for (auto& [tv, insts] : maps.inst) { std::set> instsOneStep = insts; std::set> instsFinal; for (auto ty : instsOneStep) { instsFinal.merge(tyMgr.GetInstantiatedTys(ty, maps.inst)); } insts = instsFinal; } } void FilterAndReduceSubstPacks(std::vector& mapsList, TypeManager& tyMgr) { std::vector filtered; for (auto& maps : mapsList) { if (!HasTyVarsToSolve(maps)) { filtered.push_back(maps); } } for (auto& maps : filtered) { ReduceSubstPack(maps, tyMgr); } mapsList = filtered; } } void TypeChecker::TypeCheckerImpl::FilterTypeMappings( const Expr& expr, FuncDecl& fd, std::vector& typeMappings) { // Check all type parameter included in typeMapping to ensure that each generic has a instantiation type, otherwise // this target is illegal. auto genericParams = StaticToTyVars(GetAllGenericTys(expr, fd)); for (auto curTyParam : genericParams) { for (auto it = typeMappings.begin(); it != typeMappings.end();) { auto mapping = *it; if (curTyParam->IsGeneric() && mapping.find(curTyParam) == mapping.end()) { it = typeMappings.erase(it); } else { ++it; } } } // Reduce type mapping to direct mapping for used generic sema types. auto tyVars = ::GetAllGenericTys(fd.ty); CJC_ASSERT(fd.funcBody); if (fd.funcBody->generic) { // Since 'func test(a: Int64):Unit' function type can be used without type parameters' sema ty, // we need to add type parameters' sema ty manually to ensure keeping type parameters' mapping. for (auto& it : fd.funcBody->generic->typeParameters) { tyVars.emplace(it->ty); } } // If candidate returns 'This' type, need to keep type mapping for current decl. if (IsFuncReturnThisType(fd)) { auto declOfThisType = GetDeclOfThisType(expr); if (declOfThisType) { tyVars.merge(::GetAllGenericTys(declOfThisType->ty)); } } else if (auto ma = DynamicCast(&expr); ma && ma->baseExpr) { // 1. Store upperBounds' parent generic ty, if current is generic upper bound function call. // 2. If function contains quest ty, also collect typeMapping of outerDecl for possible generic ty. if (fd.outerDecl && (IsGenericUpperBoundCall(*ma, fd) || fd.ty->HasQuestTy())) { tyVars.merge(::GetAllGenericTys(fd.outerDecl->ty)); } auto baseTarget = TypeCheckUtil::GetRealTarget(ma->baseExpr->GetTarget()); // When type of baseExpr should be inferred, we need to keep tyVars. Such as A.add() or pkg.A.add(). bool isNameAccessNeedInfer = baseTarget && baseTarget->IsTypeDecl() && ma->baseExpr->GetTypeArgs().empty(); if (isNameAccessNeedInfer) { tyVars.merge(::GetAllGenericTys(baseTarget->ty)); } } // Erase context type vars from the type mapping, the context tyVar should be substituted. // eg: class A { func test() { A(); }}; The 'T' should not be considered as substitutable. for (auto& mapping : typeMappings) { mapping = ReduceMultiTypeSubst(typeManager, StaticToTyVars(tyVars), mapping); } if (typeMappings.empty()) { // Should always leave at least one empty element in the 'typeMappings'. typeMappings.emplace_back(MultiTypeSubst{}); } } bool TypeChecker::TypeCheckerImpl::CheckGenericCallCompatible( ASTContext& ctx, FunctionCandidate& candidate, SubstPack& typeMapping, Ptr targetRet) { auto& ce = candidate.ce; auto& fd = candidate.fd; // Generate the type mapping first. The constraints should be checked in the process. If the mapping of a // generic type is not unique, the generation fails and the function is not matched. True is returned if the // mapping meets the constraints. auto typeMappings = GenerateTypeMappingForCall(ctx, candidate, targetRet); FilterAndReduceSubstPacks(typeMappings, typeManager); if (typeMappings.empty()) { return false; } CJC_ASSERT(ce.baseFunc); // After generating type mapping, check the compatibility of arguments and parameters. std::vector> paramTysInArgOrder = GetParamTysInArgsOrder(typeManager, ce, fd); if (paramTysInArgOrder.size() != ce.args.size()) { return false; } std::vector resMappings; for (const auto& mts : typeMappings) { std::set> usedTys = {fd.ty}; usedTys.merge(GetGenericParamsForCall(ce, fd)); auto mappings = ExpandMultiTypeSubst(mts, usedTys); resMappings.insert(resMappings.end(), mappings.begin(), mappings.end()); } size_t matchedCnt = 0; for (auto mapping = resMappings.begin(); mapping != resMappings.end();) { auto ds = DiagSuppressor(diag); bool matched = true; size_t currentCnt = 0; for (size_t i = 0; i < paramTysInArgOrder.size(); ++i) { auto paramTy = paramTysInArgOrder[i]; if (paramTy && paramTy->HasGeneric()) { // If the parameter has generic type, get the instantiation type. paramTy = typeManager.ApplySubstPack(paramTy, *mapping); } if (!CheckWithCache(ctx, paramTy, ce.args[i].get())) { matched = false; break; } if (!CheckUseTrailingClosureWithFunctionType(diag, *ce.args[i], *paramTysInArgOrder[i], fd)) { matched = false; break; } currentCnt++; } matchedCnt = currentCnt > matchedCnt ? currentCnt : matchedCnt; if (matched || resMappings.size() == 1) { ds.ReportDiag(); // Report when matched or current is last candidate. } mapping = matched ? mapping + 1 : resMappings.erase(mapping); } candidate.stat.matchedArgs = static_cast(matchedCnt); resMappings = ResolveTypeMappings(typeManager, resMappings, fd); if (resMappings.size() == 1) { typeMapping = resMappings.front(); return CheckCandidateConstrains(ce, fd, typeMapping); } DiagnoseForMultiMapping(diag, ce, fd, typeMappings, resMappings); return false; } bool TypeChecker::TypeCheckerImpl::CheckAndGetMappingForTypeDecl( const Expr& baseExpr, const Decl& typeDecl, const Decl& targetDecl, const SubstPack& typeMapping) { // Generate mapping from 'structDecl' generics to 'typeDecl' generics. // eg: open class I1{ static func f3(a: F, b: H) {} } // class I2 <: I1 {} // I2.f3(1, Int32(1)) // typeMapping will contains F->Int64, H->Int32. // But we need further mapping from K -> H, to get final I2's type I2 // Also for mapping extend ty to type decl ty. MultiTypeSubst mts; MultiTypeSubst mapping; if (baseExpr.ty && targetDecl.ty) { mapping = promotion.GetPromoteTypeMapping(*baseExpr.ty, *targetDecl.ty); } for (auto it : std::as_const(mapping)) { if (!it.first->IsGeneric()) { continue; } for (auto ty : it.second) { if (ty->IsGeneric()) { mts[RawStaticCast(ty)].emplace(typeManager.ApplySubstPack(it.first, typeMapping)); } } } // Diagnose for inconsistent types, eg: // open class I1 { static func f3(a: F, b: K) {} } // class I2 <: I1 {} // I2.f3(Int64(1), Int32(1)) // Should report error since we got F->Int64, H->Int32 and K->F, K->H. The type substitution does not consistent. bool valid = true; std::string diagMessage; for (auto it : std::as_const(mts)) { if (it.second.size() > 1) { diagMessage += (diagMessage.empty() ? "" : std::string(", ")) + "'" + it.first->String() + "' to '" + Ty::GetTypesToStr(it.second, "', '") + "'"; valid = false; } } if (!valid) { auto diagBuilder = diag.DiagnoseRefactor(DiagKindRefactor::sema_generic_type_inconsistent, baseExpr, MakeRange(baseExpr.begin, baseExpr.end), typeDecl.ty->String()); diagBuilder.AddNote(diagMessage); } return valid; } bool TypeChecker::TypeCheckerImpl::CheckCandidateConstrains( const CallExpr& ce, const FuncDecl& fd, const SubstPack& typeMapping) { auto userDefinedTypeArgs = ce.baseFunc->GetTypeArgs(); if (!NeedFurtherInstantiation(userDefinedTypeArgs) && fd.TestAttr(Attribute::GENERIC) && !CheckGenericDeclInstantiation(&fd, userDefinedTypeArgs, *ce.baseFunc)) { return false; } bool ignored = !fd.outerDecl || !fd.outerDecl->IsNominalDecl() || !Ty::IsTyCorrect(fd.outerDecl->ty); if (ignored) { return true; } Ptr structDecl = fd.outerDecl; if (auto ma = DynamicCast(ce.baseFunc.get()); ma && ma->baseExpr && Ty::IsTyCorrect(ma->baseExpr->ty)) { Ptr baseTy = ma->baseExpr->ty; if (auto res = typeManager.GetExtendDeclByInterface(*baseTy, *structDecl->ty)) { structDecl = *res; } if (!structDecl->GetGeneric()) { return true; } auto typeDecl = Ty::GetDeclPtrOfTy(baseTy); auto typeArgs = ma->baseExpr->GetTypeArgs(); if (!NeedFurtherInstantiation(typeArgs) && structDecl == typeDecl) { // If function's outerDecl and maBase's type decl is same, check constrains with user defined typeArgs. return CheckGenericDeclInstantiation(structDecl, typeArgs, *ma->baseExpr); } bool needCheckTypeInconsistency = typeDecl && typeDecl->TestAttr(Attribute::GENERIC) && typeDecl != structDecl; if (needCheckTypeInconsistency) { if (!CheckAndGetMappingForTypeDecl(*ma->baseExpr, *typeDecl, *structDecl, typeMapping)) { return false; } } // If function is in extend decl, we need to replace any generic ty in type decl to generic ty in extend. if (structDecl->astKind == ASTKind::EXTEND_DECL && typeDecl) { baseTy = ReplaceWithGenericTyInInheritableDecl(baseTy, *structDecl, *typeDecl); } auto prRes = promotion.Promote(*baseTy, *structDecl->ty); if (prRes.empty()) { return true; } auto instTy = typeManager.ApplySubstPack(*prRes.begin(), typeMapping); return Ty::IsTyCorrect(instTy) && CheckGenericDeclInstantiation(structDecl, instTy->typeArgs, *ma->baseExpr); } else if (IsClassOrEnumConstructor(fd)) { // If typeArgs are fully instantiated, check with decl constraint. if (!NeedFurtherInstantiation(userDefinedTypeArgs)) { return CheckGenericDeclInstantiation(structDecl, userDefinedTypeArgs, *ce.baseFunc); } } return true; } bool TypeChecker::TypeCheckerImpl::CheckCallCompatible(ASTContext& ctx, FunctionCandidate& candidate) { auto& ce = candidate.ce; auto& fd = candidate.fd; std::vector> paramTysInArgsOrder = GetParamTysInArgsOrder(typeManager, ce, fd); if (paramTysInArgsOrder.size() != ce.args.size()) { return false; } for (size_t i = 0; i < paramTysInArgsOrder.size(); ++i) { ce.args[i]->Clear(); if (!CheckWithCache(ctx, paramTysInArgsOrder[i], ce.args[i].get())) { if (ce.args[i]->ShouldDiagnose() && !CanSkipDiag(*ce.args[i])) { DiagMismatchedTypes(diag, *ce.args[i], *paramTysInArgsOrder[i]); } return false; } if (!CheckUseTrailingClosureWithFunctionType(diag, *ce.args[i], *paramTysInArgsOrder[i], fd)) { return false; } if (fd.TestAttr(Attribute::C) && ce.args[i]->ty && ce.args[i]->ty->IsUnit()) { diag.Diagnose(*ce.args[i], DiagKind::sema_unit_cannot_as_cfunc_arg); return false; } candidate.stat.matchedArgs++; if (fd.TestAttr(Attribute::FOREIGN) && fd.hasVariableLenArg && i >= fd.funcBody->paramLists[0]->params.size()) { continue; } if (auto st = DynamicCast(ce.args[i]->ty); st && st->decl->TestAttr(Attribute::C) && st != paramTysInArgsOrder[i]) { diag.Diagnose(*ce.args[i], DiagKind::sema_cstruct_cannot_autobox, paramTysInArgsOrder[i]->String()); return false; } } return true; } void TypeChecker::TypeCheckerImpl::FillEnumTypeArgumentsTy( const Decl& ctorDecl, const SubstPack& typeMapping, MemberAccess& ma) { auto ed = As(ctorDecl.outerDecl); if (!ed || !ed->generic) { return; } if (auto ref = DynamicCast(ma.baseExpr.get())) { if (ref->typeArguments.empty()) { ref->instTys.clear(); } if (ref->instTys.empty()) { for (auto& typeParam : ed->generic->typeParameters) { (void)ref->instTys.emplace_back( typeManager.ApplySubstPack(StaticCast(typeParam->ty), typeMapping)); } } } ma.baseExpr->ty = typeManager.ApplySubstPack(ed->ty, typeMapping); auto baseTypeArgs = ma.baseExpr->GetTypeArgs(); bool enumCallNoTyArgs = ma.typeArguments.empty() && baseTypeArgs.empty(); if (enumCallNoTyArgs) { ma.ty = typeManager.ApplySubstPack(ma.ty, typeMapping); } } void TypeChecker::TypeCheckerImpl::FillTypeArgumentsTy(const FuncDecl& fd, const CallExpr& ce, SubstPack& typeMapping) { auto generic = GetCurrentGeneric(fd, ce); if (auto ref = DynamicCast(ce.baseFunc.get()); ref && generic) { if (ref->typeArguments.empty()) { ref->instTys.clear(); } if (ref->instTys.empty()) { for (auto& typeParam : generic->typeParameters) { // Store the instantiate type. this is more convenient for later instantiation. (void)ref->instTys.emplace_back( typeManager.ApplySubstPack(StaticCast(typeParam->ty), typeMapping)); } } } auto ma = As(ce.baseFunc.get()); // Return here for non-member access expression OR not structure's member decl. if (!ma || !fd.outerDecl) { return; } if (fd.TestAttr(Attribute::ENUM_CONSTRUCTOR)) { FillEnumTypeArgumentsTy(fd, typeMapping, *ma); return; } /* for cases like enum A{ a public func b(x:T){0} } let v = A.a.b(1) // <-- inference here */ if (auto ma2 = DynamicCast(ma->baseExpr.get()); ma2 && ma2->target && ma2->target->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { FillEnumTypeArgumentsTy(*ma2->target, typeMapping, *ma2); } auto baseDecl = TypeCheckUtil::GetRealTarget(ma->baseExpr->GetTarget()); auto ref = DynamicCast(ma->baseExpr.get()); // A expr which has 'baseDecl' must also be 'NameReferenceExpr'. auto baseGeneric = baseDecl ? baseDecl->GetGeneric() : nullptr; bool noNeedArguments = !ref || !baseDecl || !baseGeneric; if (noNeedArguments) { return; } // Check whether is accessing member using generic type name without typeArguments. // NOTE: It is possible to access a member of generic type A with non-generic type name B that B <: A. bool typeAccessOmitTypeArgs = (fd.TestAttr(Attribute::STATIC) || baseDecl->TestAttr(Attribute::ENUM_CONSTRUCTOR)) && ref->instTys.empty(); if (!typeAccessOmitTypeArgs) { return; } if (baseDecl != fd.outerDecl) { // Generate mapping from 'fd.outerDecl' generics to real maBase's generics. // 1. eg: open class I1{ static func f3(a: F, b: H) {} } // class I2 <: I1 {} // I2.f3(1, Int32(1)) // typeMapping will contains F->Int64, H->Int32. // But we need further mapping from K -> H, to get final I2's type I2 // Also for mapping extend ty to type decl ty. // 2. public enum E { // EE // func test(a: T) {} // } // EE.test(1) <- T of E should be able to inferred. // This part may not be necessary. Could be treated as an unsolved ty var in the future. TypeSubst mapping; auto outerDeclTy = typeManager.GetInstantiatedTy(fd.outerDecl->ty, typeMapping.u2i); if (baseDecl->ty && fd.outerDecl->ty) { mapping = MultiTypeSubstToTypeSubst(promotion.GetDowngradeTypeMapping(*baseDecl->ty, *outerDeclTy)); } typeManager.PackMapping(typeMapping, mapping); } // If memberAccess's base expression has no explicit type arguments, we should add instantiated ty to it. for (auto& typeParameter : baseGeneric->typeParameters) { (void)ref->instTys.emplace_back( typeManager.ApplySubstPack(StaticCast(typeParameter->ty), typeMapping)); } // Update the ma's base expression's ty. ma->baseExpr->ty = typeManager.ApplySubstPack(ma->baseExpr->ty, typeMapping); } // Caller guarantees fmu.fd not null and has function type. std::vector> TypeChecker::TypeCheckerImpl::UpdateFuncGenericType( ASTContext& ctx, FunctionMatchingUnit& fmu, CallExpr& ce) { auto funcTy = RawStaticCast(fmu.fd.ty); ReplaceIdealTypeInSubstPack(fmu.typeMapping); // Caller guarantees 'desugarArgs' has value. if (!ce.desugarArgs.has_value()) { return {}; } for (size_t i = 0; i < ce.desugarArgs.value().size(); ++i) { auto& arg = ce.desugarArgs.value()[i]; if (!arg->expr || !arg->expr->ty) { continue; } if (arg->expr->ty->HasIdealTy()) { return {}; } } // The callExpr's type should be the instantiated type, not generic type. if (auto ref = DynamicCast(ce.baseFunc.get())) { ref->matchedParentTy = typeManager.ApplySubstPack(ref->matchedParentTy, fmu.typeMapping); } if (auto ma = DynamicCast(ce.baseFunc.get()); ma && ma->matchedParentTy) { // 'matchedParentTy' may be generic ty that can be promoted further. if (ma->matchedParentTy->HasGeneric() && !ma->baseExpr->ty->IsGeneric()) { auto prTys = promotion.Promote(*ma->baseExpr->ty, *ma->matchedParentTy); auto updatedTy = prTys.empty() ? TypeManager::GetInvalidTy() : *prTys.begin(); if (Ty::IsTyCorrect(updatedTy)) { ma->matchedParentTy = updatedTy; } } } ce.baseFunc->ty = typeManager.ApplySubstPack(funcTy, fmu.typeMapping); auto rawTy = GetCallTy(ctx, ce, fmu.fd); ce.ty = typeManager.ApplySubstPack(rawTy, fmu.typeMapping); FillTypeArgumentsTy(fmu.fd, ce, fmu.typeMapping); return {&fmu.fd}; } // Convert the type IDEAL_INT to INT64 and IDEAL_FLOAT to FLOAT64 in typeMapping. void TypeChecker::TypeCheckerImpl::ReplaceIdealTypeInSubstPack(SubstPack& maps) { for (auto& [tv, tys] : maps.inst) { auto tys0 = tys; tys.clear(); for (auto& ty0 : tys0) { // std::set always has const iterator, can't replace in-place Ptr ty1 = ty0; typeManager.ReplaceIdealTy(&ty1); tys.insert(ty1); } } } OwnedPtr TypeChecker::TypeCheckerImpl::CheckCandidate( ASTContext& ctx, FunctionCandidate& candidate, Ptr targetRet, SubstPack& typeMapping) { auto& ce = candidate.ce; auto& fd = candidate.fd; bool wrongNormalArgSize = !fd.hasVariableLenArg && fd.funcBody->paramLists.front()->params.size() != ce.args.size() && !HasSamePositionalArgsSize(fd, ce); if (wrongNormalArgSize) { DiagWrongNumberOfArguments(diag, ce, fd); return nullptr; } candidate.stat.argNameValid = CheckArgsWithParamName(ce, fd); std::vector> paramTyInArgOrder = GetParamTysInArgsOrder(typeManager, ce, fd); if (paramTyInArgOrder.size() != ce.args.size()) { return nullptr; } // Check whether the target function's params type is compatible with arguments. if (IsGenericCall(ctx, candidate.ce, candidate.fd)) { if (!CheckGenericCallCompatible(ctx, candidate, typeMapping, targetRet)) { return nullptr; } } else if (!CheckCallCompatible(ctx, candidate)) { return nullptr; } if (NeedSynOnUsed(fd)) { Synthesize(ctx, &fd); } if (!Ty::IsTyCorrect(fd.ty) || !fd.ty->IsFunc()) { return nullptr; } if (targetRet) { // Check if the function's return type is the subtype of the target type that the callExpr should be. auto retTy = GetCallTy(ctx, candidate.ce, fd); DynamicBindingThisType(*ce.baseFunc, fd, typeMapping); auto instRet = typeManager.ApplySubstPack(retTy, typeMapping); // Should delete ideal type in the future. if (!instRet->HasIdealTy() && !typeManager.IsSubtype(instRet, targetRet)) { if (!ce.sourceExpr) { DiagMismatchedTypesWithFoundTy(diag, ce, *targetRet, *instRet); } return nullptr; } } return candidate.stat.argNameValid ? MakeOwned(fd, paramTyInArgOrder, typeMapping) : nullptr; } std::vector> TypeChecker::TypeCheckerImpl::ReorderCallArgument( ASTContext& ctx, FunctionMatchingUnit& fmu, CallExpr& ce) { std::vector> args; auto defaultArgs = std::vector>(); // Record the default parameters' type first. Non-default parameters' type is set nullptr firstly. for (auto& param : fmu.fd.funcBody->paramLists[0]->params) { if (param->TestAttr(Attribute::HAS_INITIAL)) { auto arg = CreateFuncArgForOptional(*param); if (!fmu.fd.TestAttr(Attribute::TOOL_ADD)) { arg->EnableAttr(Attribute::HAS_INITIAL); } else if (Ty::IsTyCorrect(arg->ty) && !Ty::IsTyCorrect(arg->expr->ty)) { // When default parameters are created by cjogen, the arg->expr type info is required, // but in Cangjie is used only as a placeholder. auto ret = Check(ctx, arg->ty, arg->expr.get()); CJC_ASSERT(ret); } SpreadInstantiationTy(*arg, fmu.typeMapping); args.push_back(arg.get()); defaultArgs.push_back(std::move(arg)); } else { args.push_back(nullptr); } } SortCallArgumentByParamOrder(fmu.fd, ce, args); ce.desugarArgs = args; // could be re-checked, must release after references in desugarArgs have changed ce.defaultArgs = std::move(defaultArgs); defaultArgs.clear(); // Caller guarantees fd not null and type must be function type. auto funcTy = RawStaticCast(fmu.fd.ty); // When the funcDecl is a generic function, we should do instantiation and assign the real type to callExpr. if (IsGenericCall(ctx, ce, fmu.fd)) { return UpdateFuncGenericType(ctx, fmu, ce); } // Infer the ideal type, if infer fails, return a nullptr and there is no match function for the callExpr. for (size_t i = 0; i < funcTy->paramTys.size(); ++i) { auto& arg = ce.desugarArgs.value()[i]; if (arg->TestAttr(Attribute::HAS_INITIAL)) { continue; } if (arg->ty && arg->ty->IsIdeal()) { return {}; } } ce.baseFunc->ty = funcTy; ce.ty = GetCallTy(ctx, ce, fmu.fd); return {&fmu.fd}; } /// Get return type from a function call. This translates the returned This ty to its underlying type if necessary. /// Otherwise it returns the original function return type. Ty* TypeChecker::TypeCheckerImpl::GetCallTy(ASTContext& ctx, const CallExpr& ce, const FuncDecl& target) const { auto targetRetTy = StaticCast(target.ty)->retTy; auto ret = targetRetTy; if (Is(targetRetTy)) { if (auto ma = DynamicCast(&*ce.baseFunc)) { // SomeClass.StaticFunc() -> SomeClass if (target.TestAttr(Attribute::STATIC)) { return target.outerDecl->ty; } // instance.Func() -> instance type return ma->baseExpr->ty; } auto sym = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, ce.scopeName); CJC_ASSERT(sym->node); // return ThisTy if symbol target is ClassTy/ClassThisTy if (auto classTy = DynamicCast(sym->node->ty)) { return typeManager.GetClassThisTy(*classTy->decl, classTy->typeArgs); } return sym->node->ty; } return ret; } // Collect valid function types (non-generic function types & instantiated function types), // and erase invalid targets from 'funcs' vector. Making candidateTys' order corresponding to 'funcs'. // return true if there is not instantiated candidate exist during collection. // NOTE: we only synthesize candidate function when following condition fits: // 1. when the 'targetTy' is given, the function's parameter types should be subtype of the 'targetTy'. // 2. the function is imported so it will not have circular dependency when synthesizing. // 3. the function does not have user written return type. // NOTE: return type is pair of (ignoreGeneric, (funcDecl, ty, typeMapping)) TypeChecker::TypeCheckerImpl::FuncTyPair TypeChecker::TypeCheckerImpl::CollectValidFuncTys( ASTContext& ctx, std::vector>& funcs, Expr& expr, Ptr targetTy) { FilterShadowedFunc(funcs); // Filter invalid and shadowed function. std::vector, Ptr, TypeSubst>> candidates; auto typeArgs = expr.GetTypeArgs(); bool genericIgnored = false; auto ds = DiagSuppressor(diag); for (auto fd : funcs) { if (fd == nullptr || !Ty::IsTyCorrect(fd->ty) || !fd->ty->IsFunc()) { continue; } SubstPack mts; // Instantiate for all candidate target generic functions. if (auto generic = fd->GetGeneric()) { // If re has no type arguments, generic candidates should be ignored. genericIgnored = genericIgnored || typeArgs.empty(); if (typeArgs.empty() || !CheckGenericDeclInstantiation(fd, typeArgs, expr)) { continue; } typeManager.PackMapping(mts, GenerateTypeMappingByTyArgs(typeArgs, *generic)); } ReplaceTarget(&expr, fd); // Update target for typeMapping generation. // Instantiate ty if call base is member access and member base is an instantiated node. MergeSubstPack(mts, GenerateGenericTypeMapping(ctx, expr)); // Zip is just a compatibility patch. Change all following to use SubstPack in the future. std::vector typeMappings{typeManager.ZipSubstPack(mts)}; // Filter typeMapping with generic parameters, it will keep the size of 'typeMappings' to be at least one. FilterTypeMappings(expr, *fd, typeMappings); CJC_ASSERT(!typeMappings.empty()); std::set> usedTys = {fd->ty}; usedTys.merge(GetGenericParamsForDecl(*fd)); std::set resMappings = ExpandMultiTypeSubst(typeMappings[0], usedTys); auto thisCd = DynamicCast(GetDeclOfThisType(expr)); bool hasThisRet = thisCd && Ty::IsTyCorrect(thisCd->ty) && IsFuncReturnThisType(*fd); auto genericParams = GetAllGenericTys(expr, *fd); // Process for all possible type mappings. for (auto& mapping : resMappings) { // If generic typeParameter not empty but mapping is empty, set 'genericIgnored' true; // NOTE: As long as any one of mapping is empty, it means there is no valid mapping candidate. if (!genericParams.empty() && mapping.empty()) { genericIgnored = true; break; } auto instTy = typeManager.GetInstantiatedTy(fd->ty, mapping); // If parameters' types already incompatible, ignore current candidate. if (targetTy && !typeManager.IsFuncParametersSubtype(*RawStaticCast(instTy), *targetTy)) { continue; } if (NeedSynOnUsed(*fd) && Synthesize(ctx, fd) && (!Ty::IsTyCorrect(fd->ty) || fd->ty->HasQuestTy())) { DiagUnableToInferReturnType(diag, *fd, expr); break; // If the function's type still contains quest type, ignore current candidate. } // Re-instantiated function's type after function has been synthesize. instTy = typeManager.GetInstantiatedTy(fd->ty, mapping); CJC_ASSERT(instTy->IsFunc()); // Process dynamic binding this type. if (hasThisRet) { // Since 'CollectValidFuncTys' is only used for reference node, we do not need to keep 'This' ty here. auto realThisTy = typeManager.GetInstantiatedTy(thisCd->ty, mapping); instTy = typeManager.GetFunctionTy(RawStaticCast(instTy)->paramTys, realThisTy); } candidates.emplace_back(std::make_tuple(fd, instTy, mapping)); } } return {genericIgnored, candidates}; } bool TypeChecker::TypeCheckerImpl::HasBaseOfPlaceholderTy(ASTContext& ctx, Ptr n) { if (!n || typeManager.GetUnsolvedTyVars().empty()) { return false; } if (auto ma = DynamicCast(n)) { SetIsNotAlone(*ma->baseExpr); if (HasBaseOfPlaceholderTy(ctx, ma->baseExpr)) { return true; } } bool wasOK = Ty::IsTyCorrect(n->ty); if (Ty::IsInitialTy(n->ty)) { DiagSuppressor ds(diag); ctx.targetTypeMap[n] = TypeManager::GetQuestTy(); SynthesizeWithCache(ctx, n); ctx.targetTypeMap[n] = nullptr; } if (n->ty->IsPlaceholder()) { return true; } if (!wasOK) { n->Clear(); } return false; } std::vector>> TypeChecker::TypeCheckerImpl::GetArgTyPossibilities(ASTContext& ctx, CallExpr& ce) { std::vector>> ceArgs; for (auto& arg : ce.args) { std::vector> argCandidates; if (!arg->expr) { continue; } // Check non-reference expressions type later with function parameter type as upper-bound. if (!arg->expr->IsReferenceExpr() || HasBaseOfPlaceholderTy(ctx, arg->expr)) { argCandidates.emplace_back(nullptr); ceArgs.emplace_back(Utils::VecToSet(argCandidates)); continue; } // Set dummy target type for synthesize target, this will only used for skip filter function, // since ambiguous target of reference is acceptable as func arg, which is knonw from existence of target Ty. // Avoid error report by 'Synthesize' in current and later steps. ctx.targetTypeMap[arg->expr.get()] = TypeManager::GetQuestTy(); SynthesizeWithCache(ctx, arg.get()); ctx.targetTypeMap[arg->expr.get()] = nullptr; // Get all possible definition for each function argument. auto targets = GetFuncTargets(*arg->expr); // Only refExpr/memberAccess base with function target may have different types for a call, // So record and skip function check for other cases. if (!Ty::IsTyCorrect(arg->ty) || targets.empty()) { argCandidates.emplace_back(arg->ty); ceArgs.emplace_back(Utils::VecToSet(argCandidates)); continue; } auto [genericIgnored, candidates] = CollectValidFuncTys(ctx, targets, *arg->expr); for (auto [_, ty, __] : candidates) { argCandidates.emplace_back(ty); } if (argCandidates.empty()) { if (genericIgnored) { diag.Diagnose(*arg, DiagKind::sema_generic_type_without_type_argument); return {}; } argCandidates.emplace_back(arg->ty); } ceArgs.emplace_back(Utils::VecToSet(argCandidates)); } return ceArgs; } std::vector>> TypeChecker::TypeCheckerImpl::GetArgsCombination(ASTContext& ctx, CallExpr& ce) { std::vector>> ceArgs = GetArgTyPossibilities(ctx, ce); if (ceArgs.empty()) { return {}; } std::vector>> combinations; std::function>&)> fillCombination; size_t numArgs = ceArgs.size(); // Combine every arguments possibilities. fillCombination = [&fillCombination, &ceArgs, &numArgs, &combinations](std::vector>& argSet) { size_t cur = argSet.size(); for (auto& it : ceArgs[cur]) { argSet.emplace_back(it); if (cur + 1 == numArgs) { combinations.emplace_back(argSet); } else { fillCombination(argSet); } argSet.pop_back(); } }; std::vector> argSet; fillCombination(argSet); return combinations; } std::vector> TypeChecker::TypeCheckerImpl::CheckFunctionMatch( ASTContext& ctx, FunctionCandidate& candidate, Ptr target, SubstPack& typeMapping) { auto matched = CheckCandidate(ctx, candidate, target, typeMapping); if (!matched) { return {}; } if (candidate.fd.outerDecl && candidate.fd.outerDecl->astKind == ASTKind::INTERFACE_DECL) { if (auto ref = DynamicCast(candidate.ce.baseFunc.get())) { ref->matchedParentTy = candidate.fd.outerDecl->ty; } } // Infer the arguments' type again, to update ideal type and reference's targets which may overload. ReInferCallArgs(ctx, candidate.ce, *matched); return ReorderCallArgument(ctx, *matched, candidate.ce); } void TypeChecker::TypeCheckerImpl::RemoveShadowedFunc( const FuncDecl& fd, int64_t currentLevel, int64_t targetLevel, std::vector>& funcs) { if (targetLevel >= currentLevel) { return; } auto funcTy = DynamicCast(fd.ty); auto paramTys = funcTy ? funcTy->paramTys : GetParamTys(fd); auto isFuncSignatureSame = [this, &fd, ¶mTys](auto& target) -> bool { if (fd.TestAttr(Attribute::GENERIC) != target->TestAttr(Attribute::GENERIC)) { return false; } auto targetTy = DynamicCast(target->ty); auto targetParamTys = targetTy ? targetTy->paramTys : GetParamTys(*target); return typeManager.IsFuncParameterTypesIdentical(targetParamTys, paramTys); }; funcs.erase(std::remove_if(funcs.begin(), funcs.end(), isFuncSignatureSame), funcs.end()); } void TypeChecker::TypeCheckerImpl::FilterShadowedFunc(std::vector>& candidates) { auto calScopeLevel = [](auto fd) { // Caller guarantees fd not null. // Imported gloabl function's scope level is -1. return fd->TestAttr(Attribute::IMPORTED) && fd->TestAttr(Attribute::GLOBAL) ? -1 : (fd->TestAttr(Attribute::ENUM_CONSTRUCTOR) ? 0 : static_cast(fd->scopeLevel)); }; std::unordered_map>> levelMap; auto checkPriority = [&levelMap, &calScopeLevel](auto& fd) { if (fd == nullptr) { return; } int64_t scopeLevel = calScopeLevel(fd); auto found = levelMap.find(scopeLevel); if (found == levelMap.end()) { std::vector> set{fd}; levelMap.emplace(std::make_pair(scopeLevel, set)); } else { found->second.emplace_back(fd); } }; std::for_each(candidates.begin(), candidates.end(), checkPriority); for (auto& [i, fdSet] : levelMap) { int64_t level = i; // For every function decl in each scope level, // check and remove functions in outer scopes which has same function signature. for (auto& fd : fdSet) { for (auto& it : levelMap) { auto scopeLevel = it.first; auto& funcs = it.second; RemoveShadowedFunc(*fd, level, scopeLevel, funcs); } } } // Remove shadowed function from candidates in place. // Need keep null for re-export case. At least one valid target before null. Utils::EraseIf( candidates, [&levelMap, &calScopeLevel](auto fd) { return fd && !Utils::In(fd, levelMap[calScopeLevel(fd)]); }); } void TypeChecker::TypeCheckerImpl::FilterExtendImplAbstractFunc(std::vector>& candidates) { std::set> implementedOrOverridenFuncs; for (auto& fd1 : candidates) { if (!(fd1->TestAttr(Attribute::ABSTRACT) && Ty::IsTyCorrect(fd1->ty) && fd1->outerDecl && fd1->outerDecl->astKind == ASTKind::INTERFACE_DECL)) { continue; } auto id = StaticAs(fd1->outerDecl); for (auto it2 = candidates.begin(); it2 != candidates.end(); ++it2) { auto fd2 = *it2; if (!(Ty::IsTyCorrect(fd2->ty) && fd2->outerDecl && fd2->outerDecl->astKind == ASTKind::EXTEND_DECL)) { continue; } auto ed = StaticAs(fd2->outerDecl); auto [flag, typeMapping] = CheckAndObtainTypeMappingBetweenInterfaceAndExtend(*ed, *id); if (!flag) { continue; } auto funcTy1 = StaticCast(typeManager.GetInstantiatedTy(fd1->ty, typeMapping)); auto funcTy2 = StaticCast(fd2->ty); if (typeManager.IsFuncParameterTypesIdentical(funcTy1->paramTys, funcTy2->paramTys) && typeManager.IsSubtype(funcTy2->retTy, funcTy1->retTy)) { implementedOrOverridenFuncs.insert(fd1); } } } if (implementedOrOverridenFuncs.empty()) { return; } for (auto it = candidates.begin(); it != candidates.end();) { if (implementedOrOverridenFuncs.find(*it) != implementedOrOverridenFuncs.end()) { it = candidates.erase(it); } else { ++it; } } } void TypeChecker::TypeCheckerImpl::FilterIncompatibleCandidatesForCall( const CallExpr& ce, std::vector>& candidates) { if (candidates.empty()) { return; } Ptr badFd = candidates.front(); // Param lists are decided before type synthesis which can be used to filter candidates earlier. auto notMatch = [this, &badFd, &ce](const Ptr fd) { CJC_ASSERT(fd && fd->funcBody && !fd->funcBody->paramLists.empty()); // If the function is variadic function, this function is always valid with arg size. if (fd->hasVariableLenArg) { return false; } if (IsPossibleVariadicFunction(*fd, ce)) { return false; } auto ds = DiagSuppressor(diag); if (HasSamePositionalArgsSize(*fd, ce) && CheckArgsWithParamName(ce, *fd)) { return false; } badFd = fd; return true; }; // Filter by lambda and trailing closure first. candidates = SyntaxFilterCandidates(ce, candidates); candidates.erase(std::remove_if(candidates.begin(), candidates.end(), notMatch), candidates.end()); if (!candidates.empty()) { return; } if (badFd->funcBody->paramLists.front()->params.size() != ce.args.size()) { DiagWrongNumberOfArguments(diag, ce, *badFd); } else { CheckArgsWithParamName(ce, *badFd); } } std::vector> TypeChecker::TypeCheckerImpl::FilterCandidates( const ASTContext& ctx, const std::vector>& inCandidates, const CallExpr& ce) { if (inCandidates.empty()) { return inCandidates; } std::vector> candidates = inCandidates; // The trait function of 'This' type cannot be called. auto isInvalidAndInvisible = [this, &ctx, &ce](Ptr fd) { bool invalid = !fd || !fd->funcBody || fd->funcBody->paramLists.empty(); if (invalid) { return true; } // Check candidate's accessibility, filter invisible candidate. Symbol* sym = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, ce.baseFunc->scopeName); return !IsLegalAccess(sym, *fd, *ce.baseFunc); }; candidates.erase(std::remove_if(candidates.begin(), candidates.end(), isInvalidAndInvisible), candidates.end()); FilterIncompatibleCandidatesForCall(ce, candidates); auto paramInvalid = [](Ptr fd) { auto funcTy = DynamicCast(fd->ty); // 'IsTyCorrect' check will confirm all typeArgs are valid. if (!Ty::IsTyCorrect(funcTy)) { return true; } if (funcTy->paramTys.size() != fd->funcBody->paramLists[0]->params.size()) { return true; } return false; }; candidates.erase(std::remove_if(candidates.begin(), candidates.end(), paramInvalid), candidates.end()); FilterShadowedFunc(candidates); FilterExtendImplAbstractFunc(candidates); return candidates; } std::vector> TypeChecker::TypeCheckerImpl::FilterCandidatesWithReExport( const ASTContext& ctx, const std::vector>& inCandidates, const CallExpr& ce) { if (inCandidates.empty()) { return {}; } auto first = inCandidates.begin(); while (first != inCandidates.end()) { if (*first == nullptr) { break; } ++first; } if (first == inCandidates.end()) { return FilterCandidates(ctx, inCandidates, ce); } std::vector> reExportFuncTargets; std::vector> importedFuncTargets; std::copy(inCandidates.begin(), first, std::back_inserter(reExportFuncTargets)); std::copy(++first, inCandidates.end(), std::back_inserter(importedFuncTargets)); importedFuncTargets = FilterCandidates(ctx, importedFuncTargets, ce); reExportFuncTargets = FilterCandidates(ctx, reExportFuncTargets, ce); if (!(importedFuncTargets.empty() && reExportFuncTargets.empty())) { std::copy(importedFuncTargets.begin(), importedFuncTargets.end(), std::back_inserter(reExportFuncTargets)); return reExportFuncTargets; } if (importedFuncTargets.empty()) { return reExportFuncTargets; } return importedFuncTargets; } using FuncCompT = std::function const, Ptr const)>; std::vector> TypeChecker::TypeCheckerImpl::GetOrderedCandidates(const ASTContext& ctx, const CallExpr& ce, std::vector>& candidates, std::unordered_map, int64_t>& fdScopeLevelMap) const { std::vector> reExportAndCurrentPkgFuncTargets; std::vector> importedFuncTargets; for (Ptr funcDecl : candidates) { CJC_NULLPTR_CHECK(funcDecl); if (funcDecl->isReExportedOrRenamed) { reExportAndCurrentPkgFuncTargets.emplace_back(funcDecl); } else { importedFuncTargets.emplace_back(funcDecl); } } // Enum constructors are treated as global decls. // Define a compare function to help sorting scope in descending order. bool reExport = true; FuncCompT scopeLevelCompare = [&ctx, &ce, &reExport, &fdScopeLevelMap](auto fd1, auto fd2) -> bool { auto scopeCal = [&ctx, &ce, &reExport](const FuncDecl& fd) -> int64_t { // Imported toplevel function's scope level is treated as lowest. // Member functions except enum constructor should be treated as original scope level. if (!fd.IsSamePackage(ce) && fd.TestAttr(Attribute::GLOBAL) && (!fd.outerDecl || fd.TestAttr(Attribute::ENUM_CONSTRUCTOR))) { return reExport ? 0 : -1; } if (fd.TestAttr(Attribute::ENUM_CONSTRUCTOR)) { return CalculateEnumCtorScopeLevel(ctx, ce, fd); } return static_cast(fd.scopeLevel); }; int64_t scopeLevel1 = scopeCal(*fd1); fdScopeLevelMap[fd1] = scopeLevel1; int64_t scopeLevel2 = scopeCal(*fd2); fdScopeLevelMap[fd2] = scopeLevel2; // Greater scope level, smaller file id, smaller position. if (scopeLevel1 != scopeLevel2) { return scopeLevel1 > scopeLevel2; } else if (fd1->begin.fileID != fd2->begin.fileID) { return fd1->begin.fileID < fd2->begin.fileID; } else { return fd1->begin < fd2->begin; } }; // Sort the candidates according to the scope level. This ensures that the check starts with the maximum scope // level. size_t reExportSize = reExportAndCurrentPkgFuncTargets.size(); std::sort(reExportAndCurrentPkgFuncTargets.begin(), reExportAndCurrentPkgFuncTargets.end(), scopeLevelCompare); reExport = false; if (importedFuncTargets.size() != 1) { std::sort(importedFuncTargets.begin(), importedFuncTargets.end(), scopeLevelCompare); } else { fdScopeLevelMap[importedFuncTargets[0]] = importedFuncTargets[0]->TestAttr(Attribute::ENUM_CONSTRUCTOR) ? 0 : -1; } std::copy( importedFuncTargets.begin(), importedFuncTargets.end(), std::back_inserter(reExportAndCurrentPkgFuncTargets)); FuncCompT cmpScope = [&fdScopeLevelMap](auto fd1, auto fd2) { return fdScopeLevelMap[fd1] > fdScopeLevelMap[fd2]; }; std::inplace_merge(reExportAndCurrentPkgFuncTargets.begin(), std::next(reExportAndCurrentPkgFuncTargets.begin(), static_cast(reExportSize)), reExportAndCurrentPkgFuncTargets.end(), cmpScope); return reExportAndCurrentPkgFuncTargets; } std::vector> TypeChecker::TypeCheckerImpl::MatchFunctionForCall( ASTContext& ctx, std::vector>& candidates, CallExpr& ce, Ptr target, SubstPack& typeMapping) { TyVarScope ts(typeManager); std::vector>> argCombinations = GetArgsCombination(ctx, ce); if (!ce.args.empty() && argCombinations.empty()) { return {}; } auto filterSum = [this, &ce](std::vector> ret) { if (auto ma = DynamicCast(ce.baseFunc.get())) { if (ma->baseExpr->ty->IsPlaceholder() && ret.size() == 1) { FilterSumUpperbound(*ma, *RawStaticCast(ma->baseExpr->ty), *ret[0]); } } return ret; }; if (candidates.size() == 1) { FunctionCandidate candidate{*candidates[0], ce, argCombinations}; return filterSum(CheckFunctionMatch(ctx, candidate, target, typeMapping)); } std::unordered_map, int64_t> fdScopeLevelMap; auto orderedCandidates = GetOrderedCandidates(ctx, ce, candidates, fdScopeLevelMap); // Record the index of compatible target in targets and its coherent argument type pattern. std::vector> legals; std::vector illegals; // Record the scopeLevel. int64_t preScopeLevel = fdScopeLevelMap[orderedCandidates[0]]; // set checkpoint for resetting side effects auto cs = PData::CommitScope(typeManager.constraints); // Loop through each candidate, check whether the arguments and parameters are compatible, and find out whether the // candidate function can be called. We only check the compatible candidates in the largest scopeLevel. for (size_t i = 0; i < orderedCandidates.size(); ++i) { auto ds = DiagSuppressor(diag); auto curScopeLevel = fdScopeLevelMap[orderedCandidates[i]]; if (curScopeLevel != preScopeLevel && !legals.empty()) { // We only check the candidates in largest scope level. break; } FunctionCandidate candidate{*orderedCandidates[i], ce, argCombinations}; auto ret = CheckCandidate(ctx, candidate, target, typeMapping); // Check result & If current function declaration is already matched, do not add again. if (!ret || (!legals.empty() && legals.back()->id == static_cast(i))) { illegals.emplace_back(*orderedCandidates[i], std::vector>(), SubstPack()); illegals.back().diags = std::make_pair(ds.GetSuppressedDiag(), candidate.stat); PData::Reset(typeManager.constraints); continue; } ret->ver = PData::Stash(typeManager.constraints); ret->diags = std::make_pair(ds.GetSuppressedDiag(), candidate.stat); ret->id = static_cast(i); preScopeLevel = curScopeLevel; legals.emplace_back(std::move(ret)); } return filterSum(CheckMatchResult(ctx, ce, legals, illegals)); } void TypeChecker::TypeCheckerImpl::ReInferCallArgs( ASTContext& ctx, const CallExpr& ce, const FunctionMatchingUnit& legal) { for (size_t i = 0; i < legal.tysInArgOrder.size(); ++i) { auto instTy = typeManager.ApplySubstPack(legal.tysInArgOrder[i], legal.typeMapping); // Since the call has been checked, return value can be ignored. (void)CheckWithEffectiveCache(ctx, instTy, ce.args[i].get(), false); } } void TypeChecker::TypeCheckerImpl::RecoverCallArgs( ASTContext& ctx, const CallExpr& ce, const std::vector>& argsTys) { // Recover arguments' types if current check failed but previous check passed (argsTys not empty). if (argsTys.empty() || argsTys.size() != ce.args.size()) { return; } if (ce.resolvedFunction) { UpdateCallTargetsForLSP(ce, *ce.resolvedFunction); } for (size_t i = 0; i < argsTys.size(); ++i) { (void)CheckWithEffectiveCache(ctx, argsTys[i], ce.args[i].get(), false); } } namespace { /** * Choose the reasonable set of diagnoses to report again when their is not matching function. * @param ce The callExpr need to be resolved. * @param diagnoses The vector of diagnostics of all function candidates and their count of matched arguments. */ void CheckEmptyMatchResult(DiagnosticEngine& diag, CallExpr& ce, std::vector& illegals) { if (illegals.empty()) { return; } // Get diagnoses which have most matching arguments. std::vector> diags; uint32_t maxMatched = 0; // Used to mark whether collected diagnoses context has valid argument names. bool hasValid = false; for (size_t i = 0; i < illegals.size(); ++i) { auto [diagInfo, stat] = illegals[i].diags; if (stat.matchedArgs > maxMatched) { maxMatched = stat.matchedArgs; diags.clear(); hasValid = stat.argNameValid; diags.emplace_back(diagInfo); } else if (stat.matchedArgs == maxMatched) { // If argument valid status changed from invalid to valid, drop all collected diagnoses with invalid args. if (stat.argNameValid && !hasValid) { hasValid = true; diags.clear(); } // Only collect diagnoses when the current status is the same with the stored status. if (stat.argNameValid == hasValid) { diags.emplace_back(diagInfo); } } } // Choose a set of diagnoses that contains error happens outside callExpr to ensure throwing the useful diagnoses. auto hasCallIrrelatedDiag = [&ce](const std::vector& diags) { return std::any_of(diags.begin(), diags.end(), [&ce](const auto& info) { return info.start != ce.begin && std::all_of(ce.args.begin(), ce.args.end(), [&info](const auto& arg) { // Check 'start' for old diag, 'mainHint.range.begin' for refactored diag. auto pos = info.start.IsZero() ? info.mainHint.range.begin : info.start; return !pos.IsZero() && pos != arg->begin; }); }); }; size_t idx = 0; for (size_t i = 0; i < diags.size(); ++i) { if (hasCallIrrelatedDiag(diags[i])) { idx = i; break; } } std::for_each(diags[idx].begin(), diags[idx].end(), [&diag](auto info) { diag.Diagnose(info); }); } } // namespace std::vector> TypeChecker::TypeCheckerImpl::CheckMatchResult(ASTContext& ctx, CallExpr& ce, std::vector>& legals, std::vector& illegals) { // No matching function. if (legals.empty()) { CheckEmptyMatchResult(diag, ce, illegals); return {}; } // Only one legal target. uint64_t id; if (legals.size() == 1) { id = 0; } else { std::vector> result; std::for_each(legals.begin(), legals.end(), [&result](auto& it) { result.emplace_back(&it->fd); }); auto hasEnum = std::find_if( result.begin(), result.end(), [](auto& fd) { return fd->TestAttr(Attribute::ENUM_CONSTRUCTOR); }); if (hasEnum != result.end()) { return result; // Find at least one enum candidates in all. } InstantiatePartOfTheGenericParameters(legals); auto indexRes = ResolveOverload(legals, ce); if (indexRes.size() == 1) { id = indexRes[0]; } else { return (indexRes.size() > 1) ? result : std::vector>(); } } // Check and report suppressed diagnoses for matched function call. bool funcHasError = false; PData::Apply(typeManager.constraints, legals[id]->ver); for (const Diagnostic& funcDiag : legals[id]->diags.first) { if (funcDiag.diagSeverity == DiagSeverity::DS_ERROR) { funcHasError = true; } diag.Diagnose(funcDiag); } if (funcHasError) { return {}; } if (legals[id]->fd.outerDecl && legals[id]->fd.outerDecl->astKind == ASTKind::INTERFACE_DECL) { if (auto ref = DynamicCast(ce.baseFunc.get())) { ref->matchedParentTy = legals[id]->fd.outerDecl->ty; } } // Infer the arguments' type again, to update ideal type and reference's targets which may overload. ReInferCallArgs(ctx, ce, *legals[id]); auto ret = ReorderCallArgument(ctx, *legals[id], ce); return ret; } static std::optional>> CheckCallArgs( DiagnosticEngine& diag, TypeManager& tyMgr, CallExpr& ce, FuncTy& funcTy) { auto paramTys = funcTy.paramTys; if (funcTy.hasVariableLenArg && ce.args.size() > funcTy.paramTys.size()) { size_t varargsSize = ce.args.size() - funcTy.paramTys.size(); for (size_t i = 0; i < varargsSize; i++) { paramTys.push_back(funcTy.isC ? tyMgr.GetCTypeTy() : tyMgr.GetAnyTy()); } } if (paramTys.size() != ce.args.size()) { DiagWrongNumberOfArguments(diag, ce, paramTys); return std::nullopt; } for (auto& arg : ce.args) { if (!arg->name.Empty()) { diag.Diagnose(*arg, DiagKind::sema_unsupport_named_argument); } } return paramTys; } bool TypeChecker::TypeCheckerImpl::CheckFuncPtrCall(ASTContext& ctx, Ptr target, CallExpr& ce, FuncTy& funcTy) { // no need to check the arguments of CFunc call again if (IsValidCFuncConstructorCall(ce)) { return true; } CJC_NULLPTR_CHECK(ce.baseFunc); auto baseTarget = ce.baseFunc->GetTarget(); if (baseTarget && baseTarget->astKind == ASTKind::TYPE_ALIAS_DECL) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_invalid_called_object, *ce.baseFunc); builder.AddNote("cannot call a type alias for a function type"); return false; } auto paramTysOption = CheckCallArgs(diag, typeManager, ce, funcTy); if (!paramTysOption.has_value()) { return false; } auto paramTys = paramTysOption.value(); SubstPack typeMapping; if (funcTy.HasGeneric()) { // Generate the type mapping for generic call first. typeMapping = GenerateGenericTypeMapping(ctx, *ce.baseFunc); } for (size_t i = 0; i < paramTys.size(); ++i) { // If is not generic call, type instantiation will have no effect. auto param = typeManager.ApplySubstPack(paramTys[i], typeMapping); if (!param) { return false; } if (!Check(ctx, param, ce.args[i].get())) { return false; } } // Infer the ideal type. Previous success indicates ce.args' member not null. for (size_t i = 0; i < paramTys.size(); ++i) { auto param = typeManager.ApplySubstPack(paramTys[i], typeMapping); if (!ce.args[i]->expr || !ce.args[i]->ty->HasIdealTy()) { continue; } (void)Check(ctx, param, ce.args[i]->expr.get()); if (ce.args[i]->expr->ty->HasIdealTy()) { if (ce.ShouldDiagnose()) { diag.Diagnose(ce, DiagKind::sema_parameters_and_arguments_mismatch); } return false; } ce.args[i]->ty = ce.args[i]->expr->ty; } ce.ty = typeManager.ApplySubstPack(funcTy.retTy, typeMapping); if (target && !typeManager.IsSubtype(ce.ty, target)) { DiagMismatchedTypes(diag, ce, *target); return false; } else { ce.callKind = CallKind::CALL_FUNCTION_PTR; // Only set 'callKind' when check succeed (allow re-enter). return true; } } Ptr TypeChecker::TypeCheckerImpl::SynCallExpr(ASTContext& ctx, CallExpr& ce) { if (!ChkCallExpr(ctx, nullptr, ce)) { return TypeManager::GetInvalidTy(); } return ce.ty; } Ptr TypeChecker::TypeCheckerImpl::SynTrailingClosure(ASTContext& ctx, TrailingClosureExpr& tc) { // TrailingClosureExpr will be desugared if ast node is valid. if (tc.desugarExpr) { tc.ty = Synthesize(ctx, tc.desugarExpr.get()); } else { tc.ty = TypeManager::GetInvalidTy(); } return tc.ty; } bool TypeChecker::TypeCheckerImpl::ChkTrailingClosureExpr(ASTContext& ctx, Ty& target, TrailingClosureExpr& tc) { // TrailingClosureExpr will be desugared if ast node is valid. if (tc.desugarExpr) { if (Check(ctx, &target, tc.desugarExpr.get())) { tc.ty = tc.desugarExpr->ty; return true; } tc.desugarExpr->ty = TypeManager::GetInvalidTy(); } tc.ty = TypeManager::GetInvalidTy(); return false; } bool TypeChecker::TypeCheckerImpl::GetCallBaseCandidates( const ASTContext& ctx, const CallExpr& ce, Expr& expr, Ptr& target, std::vector>& candidates) { target = GetRealTarget(&expr, expr.GetTarget()); if (target->TestAttr(Attribute::COMMON) && target->platformImplementation) { target = target->platformImplementation; } CJC_NULLPTR_CHECK(target); if (IsBuiltinTypeAlias(*target) || target->IsBuiltIn()) { return false; } bool isFunctionCall = true; if (IsInstanceConstructor(*target) && target->outerDecl) { target = target->outerDecl; } if (target->IsStructOrClassDecl()) { // Get constructors for current decl. for (auto& member : target->GetMemberDeclPtrs()) { CJC_NULLPTR_CHECK(member); bool originalCtor = IsInstanceConstructor(*member) && member->astKind == ASTKind::FUNC_DECL && member->identifier == "init"; if (originalCtor) { (void)candidates.emplace_back(StaticCast(member)); } } isFunctionCall = false; } else { candidates = GetFuncTargets(expr); } RemoveDuplicateElements(candidates); if (ce.callKind == CallKind::CALL_ANNOTATION) { RemoveNonAnnotationCandidates(candidates); } auto ds = DiagSuppressor(diag); candidates = FilterCandidatesWithReExport(ctx, candidates, ce); if (expr.astKind == ASTKind::REF_EXPR && candidates.empty() && target->astKind == ASTKind::PACKAGE_DECL) { diag.DiagnoseRefactor(DiagKindRefactor::sema_cannot_ref_to_pkg_name, expr); } // Add for cjmp mpImpl->RemoveCommonCandidatesIfHasPlatform(candidates); if (ds.HasError()) { ds.ReportDiag(); return false; } ds.ReportDiag(); // Report warnings. if (!candidates.empty() && isFunctionCall) { target = candidates[0]; } return true; } bool TypeChecker::TypeCheckerImpl::CheckRefConstructor(const ASTContext& ctx, const CallExpr& ce, const RefExpr& re) { // Check if init() and super() is invoked outside constructor if (re.isSuper || re.isThis) { auto curFuncBody = GetCurFuncBody(ctx, re.scopeName); bool insideCtor{false}; if (curFuncBody && curFuncBody->funcDecl && curFuncBody->funcDecl->TestAttr(Attribute::CONSTRUCTOR)) { insideCtor = true; } if (!insideCtor) { diag.Diagnose(ce, DiagKind::sema_invalid_this_call_outside_ctor, re.ref.identifier.Val()); return false; } } return true; } bool TypeChecker::TypeCheckerImpl::ChkCallBaseRefExpr( ASTContext& ctx, CallExpr& ce, Ptr& target, std::vector>& candidates) { CJC_NULLPTR_CHECK(ce.baseFunc); CJC_ASSERT(ce.baseFunc->astKind == ASTKind::REF_EXPR); auto re = StaticAs(ce.baseFunc.get()); re->isAlone = false; re->callOrPattern = &ce; ctx.targetTypeMap[re] = ctx.targetTypeMap[&ce]; // For enum sugar type infer. Synthesize(ctx, re); if (auto builtin = DynamicCast(re->ref.target); builtin && builtin->type == BuiltInType::CFUNC) { return SynCFuncCall(ctx, ce); } if (!re->ref.target && re->ref.targets.empty()) { return false; } else { return CheckRefConstructor(ctx, ce, *re) && GetCallBaseCandidates(ctx, ce, *re, target, candidates); } } bool TypeChecker::TypeCheckerImpl::ChkCallBaseMemberAccess( ASTContext& ctx, CallExpr& ce, Ptr& target, std::vector>& candidates) { auto ma = StaticAs(ce.baseFunc.get()); ma->callOrPattern = &ce; ma->isAlone = false; // in case base is enum, its type arg may be inferred from func call, so disable error report here ctx.targetTypeMap[ma->baseExpr] = TypeManager::GetQuestTy(); Synthesize(ctx, ma); ctx.targetTypeMap[ma->baseExpr] = nullptr; if (ma->ty && ma->ty->IsNothing()) { return true; } else if (!ma->target && ma->targets.empty()) { // 'varr' is VArray type, 'varr.size()' is illegitimate, 'size' of VArray is a property. if (Is(ma->baseExpr->ty) && ma->field == "size") { diag.Diagnose(ce, DiagKind::sema_no_match_operator_function_call); ma->ty = TypeManager::GetInvalidTy(); } return false; } else { return GetCallBaseCandidates(ctx, ce, *ma, target, candidates); } } namespace { bool CanReachFuncCallByQuestable(Node& root) { bool canReachFuncCall = false; Walker(&root, [&canReachFuncCall](Ptr n) { if (Is(n) || Is(n)) { canReachFuncCall = true; return VisitAction::STOP_NOW; } if (!n || !IsQuestableNode(*n)) { return VisitAction::SKIP_CHILDREN; } else { return VisitAction::WALK_CHILDREN; } }).Walk(); return canReachFuncCall; } } // namespace /* * Used to help type argument inference for curried function call, * where the args are continuously given in a call chain like f(1)(2)(3). * Expected return type for the entire call chain and the last argument's type * are piggybacked to targetForBase. * This procedure could be reached recursively if the baseFunc is yet another function call * but not the first one. * * For example: * Check(`f(1)(2)(3)`, Int64`) * -> Check(`f(1)(2)`, (Int64)->Int64) * -> Check(`f(1)`, (Int64)->(Int64)->Int64) * * If the target type or any argument's type is unknown, it is replaced by QuestTy. * Since targetForBase is an overestimation with noCast set and possibly QuestTy, * it can only pass down questable nodes to ensure it's not misused as the ty of any Node. */ bool TypeChecker::TypeCheckerImpl::ChkCurryCallBase(ASTContext& ctx, CallExpr& ce, Ptr& targetRet) { std::vector> paramTys; Ptr retTy = targetRet ? targetRet : TypeManager::GetQuestTy(); { auto ds = DiagSuppressor(diag); for (auto& arg: ce.args) { if (Ty::IsInitialTy(arg->ty)) { SynthesizeWithCache(ctx, arg); } if (Ty::IsTyCorrect(arg->ty)) { paramTys.push_back(arg->ty); } else { paramTys.push_back(TypeManager::GetQuestTy()); } } } auto targetForBase = typeManager.GetFunctionTy(paramTys, retTy, {false, false, false, true}); return Check(ctx, targetForBase, ce.baseFunc.get()); } bool TypeChecker::TypeCheckerImpl::ChkCallBaseExpr( ASTContext& ctx, CallExpr& ce, Ptr& targetDecl, Ptr& targetRet, std::vector>& candidates) { auto setFuncArgsInvalidTy = [&ce]() { ce.ty = TypeManager::GetInvalidTy(); // Skip the check for function's arguments. std::for_each(ce.args.begin(), ce.args.end(), [](const OwnedPtr& fa) { CJC_NULLPTR_CHECK(fa->expr); if (!Ty::IsTyCorrect(fa->expr->ty)) { fa->expr->ty = TypeManager::GetInvalidTy(); } fa->ty = TypeManager::GetInvalidTy(); }); }; if (ce.baseFunc->desugarExpr != nullptr && !Is(ce.baseFunc.get())) { if (!Ty::IsTyCorrect(Synthesize(ctx, ce.baseFunc.get()))) { setFuncArgsInvalidTy(); return false; } return true; } bool isWellTyped = true; auto cs = PData::CommitScope(typeManager.constraints); switch (ce.baseFunc->astKind) { case ASTKind::REF_EXPR: isWellTyped = ChkCallBaseRefExpr(ctx, ce, targetDecl, candidates); break; case ASTKind::MEMBER_ACCESS: isWellTyped = ChkCallBaseMemberAccess(ctx, ce, targetDecl, candidates); break; default: bool mayCurry = false; if (CanReachFuncCallByQuestable(*ce.baseFunc)) { auto ds = DiagSuppressor(diag); mayCurry = ChkCurryCallBase(ctx, ce, targetRet); } if (!mayCurry) { PData::Reset(typeManager.constraints); ce.baseFunc->Clear(); Synthesize(ctx, ce.baseFunc.get()); } break; } if (!isWellTyped && !Ty::IsTyCorrect(ce.baseFunc->ty)) { setFuncArgsInvalidTy(); } return isWellTyped; } void TypeChecker::TypeCheckerImpl::CheckMacroCall(ASTContext& ctx, Node& macroNode) { if (macroNode.TestAttr(Attribute::IS_CHECK_VISITED)) { return; } // Mark the current macroNode is checked. macroNode.EnableAttr(Attribute::IS_CHECK_VISITED); // Note: The MacroCall Check here is designed to be used for IDE LSP, because normally macroCalls // do not exist after expansion. Now we do keep orginal macroCall AST, and put it under the // its current File Node(managed by unique_ptr) in case any toolchain(LSP, etc) wants to access // the original Nodes before macro expansion. // But the following implementation is out-of-date and should be rewritten. See issue 175. auto targetHandler = [&ctx, ¯oNode, this](const auto& kind) -> void { auto me = static_cast*>(¯oNode); if (me->invocation.target != nullptr) { return; } // For lsp hover, No error is reported during semantic analysis of code in macrocall(through judging by // isInMacroCall field of the node which is diagnosed). if (ci->invocation.globalOptions.enableMacroInLSP && me->invocation.decl && !me->invocation.decl->TestAttr(Attribute::IS_CHECK_VISITED)) { Synthesize(ctx, me->invocation.decl.get()); } auto decls = importManager.GetImportedDeclsByName(*me->curFile, me->identifier); if (decls.empty()) { return; } Utils::EraseIf(decls, [](Ptr d) -> bool { return !d || !d->TestAttr(Attribute::MACRO_FUNC); }); for (const auto& it : decls) { Ptr funcDecl{nullptr}; if (it->astKind == ASTKind::MACRO_DECL) { auto macroDecl = RawStaticCast(it); funcDecl = macroDecl->desugarDecl.get(); } else { funcDecl = StaticAs(it); } if (!funcDecl || !funcDecl->funcBody) { continue; } if (funcDecl->funcBody && funcDecl->funcBody->paramLists.empty()) { continue; } auto isCommonMacroCall = !me->invocation.HasAttr(); auto isCommonMacroDecl = funcDecl->funcBody->paramLists.front()->params.size() == MACRO_COMMON_ARGS; if (isCommonMacroCall == isCommonMacroDecl) { ReplaceTarget(me, funcDecl); } } }; if (macroNode.astKind == ASTKind::MACRO_EXPAND_EXPR) { targetHandler(MacroExpandExpr()); } else if (macroNode.astKind == ASTKind::MACRO_EXPAND_DECL) { targetHandler(MacroExpandDecl()); } else if (macroNode.astKind == ASTKind::MACRO_EXPAND_PARAM) { targetHandler(MacroExpandParam()); } } bool TypeChecker::TypeCheckerImpl::CheckCallKind(const CallExpr& ce, Ptr decl, CallKind& type) { if (!decl) { return false; } switch (decl->astKind) { case ASTKind::INTERFACE_DECL: diag.Diagnose(ce, DiagKind::sema_interface_can_not_be_instantiated, decl->identifier.Val()); break; case ASTKind::CLASS_DECL: // Fall-through. case ASTKind::STRUCT_DECL: { // Call of type name for constructor, eg. class A {} -> A(), struct R{} -> R(). // Call of 'this()' is treated as normal declared function call. auto re = As(ce.baseFunc.get()); bool isSuper = re && re->isSuper; bool isThis = re && re->isThis; auto cd = As(decl); if (cd && cd->TestAttr(Attribute::ABSTRACT) && !isSuper && !isThis) { diag.Diagnose(ce, DiagKind::sema_abstract_class_can_not_be_instantiated, cd->identifier.Val()); break; } if (isThis) { type = CallKind::CALL_DECLARED_FUNCTION; } else if (decl->astKind == ASTKind::CLASS_DECL) { type = isSuper ? CallKind::CALL_SUPER_FUNCTION : CallKind::CALL_OBJECT_CREATION; } else { type = CallKind::CALL_STRUCT_CREATION; } break; } case ASTKind::FUNC_DECL: { auto ma = As(ce.baseFunc.get()); auto reBase = ma ? As(ma->baseExpr.get()) : nullptr; if (reBase && reBase->isSuper) { type = CallKind::CALL_SUPER_FUNCTION; } else { type = decl->TestAttr(Attribute::INTRINSIC) ? CallKind::CALL_INTRINSIC_FUNCTION : CallKind::CALL_DECLARED_FUNCTION; } break; } default: return false; } return true; } DiagKind TypeChecker::TypeCheckerImpl::GetErrorKindForCall(const std::vector>& candidatesBeforeCheck, const std::vector>& candidatesAfterCheck, const CallExpr& ce) const { bool hasEmpty = candidatesBeforeCheck.empty() || candidatesAfterCheck.empty(); auto target = ce.baseFunc ? ce.baseFunc->GetTarget() : nullptr; if (hasEmpty) { if (IsSearchTargetInMemberAccessUpperBound(ce)) { return DiagKind::sema_generic_no_method_match_in_upper_bounds; } else if (target && target->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { return DiagKind::sema_enum_constructor_type_not_match; } else if (ce.callKind == CallKind::CALL_OBJECT_CREATION || ce.callKind == CallKind::CALL_STRUCT_CREATION) { return DiagKind::sema_no_match_constructor; } else { return DiagKind::sema_no_match_function_declaration_for_call; } } else { if (IsSearchTargetInMemberAccessUpperBound(ce)) { return DiagKind::sema_generic_ambiguous_method_match_in_upper_bounds; } else if (target && target->TestAttr(Attribute::ENUM_CONSTRUCTOR)) { return DiagKind::sema_multiple_constructor_in_enum; } else if (ce.callKind == CallKind::CALL_OBJECT_CREATION || ce.callKind == CallKind::CALL_STRUCT_CREATION) { return DiagKind::sema_ambiguous_constructor_match; } else { return DiagKind::sema_ambiguous_match; } } } void TypeChecker::TypeCheckerImpl::DiagnoseForCall(const std::vector>& candidatesBeforeCheck, const std::vector>& candidatesAfterCheck, CallExpr& ce, const Decl& decl) { // Ignore desugared and macro added callExpr. if (ce.sourceExpr || ce.baseFunc->TestAttr(Attribute::MACRO_INVOKE_BODY)) { return; } // If ce contains invalid type, diagnostics are reported before. bool invalid = !candidatesBeforeCheck.empty() && candidatesAfterCheck.empty() && Ty::IsTyCorrect(ce.baseFunc->ty) && Utils::In(ce.args, [](const auto& arg) { return !Ty::IsTyCorrect(arg->ty); }); if (invalid) { return; } bool isSuperCreation = ce.callKind == CallKind::CALL_SUPER_FUNCTION && decl.astKind != ASTKind::FUNC_DECL; std::string identifier = isSuperCreation ? "super" : decl.identifier.Val(); Position identifierPos{ce.begin}; if (auto re = DynamicCast(ce.baseFunc.get()); re) { identifier = re->ref.identifier; // We should diagnose with alias rather than real name. } else if (auto memberAccess = DynamicCast(ce.baseFunc.get()); memberAccess && !memberAccess->field.ZeroPos()) { identifierPos = memberAccess->field.Begin(); } DiagKind errorKind = GetErrorKindForCall(candidatesBeforeCheck, candidatesAfterCheck, ce); if (candidatesBeforeCheck.empty()) { ce.callKind = CallKind::CALL_INVALID; // When 'candidatesBeforeCheck' is empty, reset call kind. auto builder = diag.Diagnose(ce, identifierPos, errorKind, identifier); if (auto ma = DynamicCast(ce.baseFunc.get())) { RecommendImportForMemberAccess(typeManager, importManager, *ma, &builder); } return; } else if (candidatesBeforeCheck.size() == 1) { // Errors should be reported before, optimizing the only 'no matching' error here. return; } bool hasEmpty = candidatesBeforeCheck.empty() || candidatesAfterCheck.empty(); std::vector> candidates = hasEmpty ? candidatesBeforeCheck : candidatesAfterCheck; DiagKind noteKind = hasEmpty ? DiagKind::sema_found_possible_candidate_decl : DiagKind::sema_found_candidate_decl; // Print error. std::vector> importedTargets; DiagnosticBuilder diagInfo = diag.Diagnose(ce, identifierPos, errorKind, identifier); // Print all candidates. for (auto& it : candidates) { CJC_NULLPTR_CHECK(it); if (it->TestAttr(Attribute::IMPORTED)) { importedTargets.emplace_back(it); } else { diagInfo.AddNote(*it, it->identifier.Begin(), noteKind); } } AddDiagNotesForImportedDecls( diagInfo, importManager.GetImportsOfDecl(ce.curFile->curPackage->fullPackageName), importedTargets); } Ptr TypeChecker::TypeCheckerImpl::GetDeclOfThisType(const Expr& expr) const { Ptr decl = nullptr; if (auto ma = DynamicCast(&expr); ma && ma->baseExpr) { if (auto ct = DynamicCast(ma->baseExpr->ty); ct) { decl = ct->declPtr; } } else if (auto re = DynamicCast(&expr); re) { Ptr baseFunc = re->GetTarget(); if (auto vd = DynamicCast(baseFunc); vd && vd->initializer != nullptr && vd->initializer->astKind == ASTKind::MEMBER_ACCESS) { // get decl of This type from non normal call Ptr maInitializer = RawStaticCast(vd->initializer.get()); Ptr baseTarget = maInitializer->baseExpr->GetTarget(); if (baseTarget != nullptr && baseTarget->ty != nullptr && baseTarget->ty->IsClass()) { decl = RawStaticCast(baseTarget->ty)->declPtr; } } else { // get decl of This type from normal call CJC_NULLPTR_CHECK(expr.curFile); auto ctx = ci->GetASTContextByPackage(expr.curFile->curPackage); CJC_NULLPTR_CHECK(ctx); auto outMostFunc = ScopeManager::GetOutMostSymbol(*ctx, SymbolKind::FUNC, expr.scopeName); if (outMostFunc && StaticCast(outMostFunc->node)->funcBody->parentClassLike) { decl = RawStaticCast(outMostFunc->node)->funcBody->parentClassLike; } } } return decl; } std::optional> TypeChecker::TypeCheckerImpl::DynamicBindingThisType( Expr& baseExpr, const FuncDecl& fd, const SubstPack& typeMapping) { if (!IsFuncReturnThisType(fd)) { return {}; } auto funcTy = DynamicCast(Ty::IsTyCorrect(baseExpr.ty) && baseExpr.ty->IsFunc() ? baseExpr.ty : fd.ty); if (funcTy == nullptr) { return {}; } auto declOfThisType = GetDeclOfThisType(baseExpr); if (auto cd = DynamicCast(declOfThisType); cd && Ty::IsTyCorrect(cd->ty)) { auto instTy = typeManager.ApplySubstPack(typeManager.GetClassThisTy(*cd, cd->ty->typeArgs), typeMapping); baseExpr.ty = typeManager.GetFunctionTy(funcTy->paramTys, instTy); return instTy; } else if (auto ma = DynamicCast(&baseExpr); ma && ma->baseExpr) { // If the baseExpr's type is generic type, set the function call's return type as 'gty'. if (auto gty = DynamicCast(ma->baseExpr->ty); gty) { baseExpr.ty = typeManager.GetFunctionTy(funcTy->paramTys, gty); return gty; } } return {}; } FunctionMatchingUnit* TypeChecker::TypeCheckerImpl::FindFuncWithMaxChildRetTy( const CallExpr& ce, std::vector>& candidates) { if (candidates.empty()) { return nullptr; } FunctionMatchingUnit* maxChildFmu = candidates[0].get(); for (size_t i = 1; i < candidates.size(); i++) { auto ty1 = typeManager.ApplySubstPack(maxChildFmu->fd.ty, maxChildFmu->typeMapping); auto ty2 = typeManager.ApplySubstPack(candidates[i]->fd.ty, candidates[i]->typeMapping); // Errors which cause the types to be invalid should have been reported before. if (ty1->IsInvalid() || ty2->IsInvalid()) { continue; } Ptr maxChildRetTy{}; auto meteRes = JoinAndMeet(typeManager, {ty1, ty2}).MeetAsVisibleTy(); if (auto optErrs = JoinAndMeet::SetMetType(maxChildRetTy, meteRes)) { (void)diag.Diagnose(ce, DiagKind::sema_diag_report_note_message, *optErrs); }; if (maxChildRetTy->IsInvalid()) { return nullptr; } else if (maxChildRetTy == ty2 && ty1 != ty2) { maxChildFmu = candidates[i].get(); } else { continue; } } return maxChildFmu; } bool TypeChecker::TypeCheckerImpl::PostCheckCallExpr( const ASTContext& ctx, CallExpr& ce, FuncDecl& func, const SubstPack& typeMapping) { if (!func.TestAttr(Attribute::CONSTRUCTOR) && !CheckStaticCallNonStatic(ctx, ce, func)) { // Reset status for re-enter callCheck. ce.ty = TypeManager::GetInvalidTy(); ce.callKind = CallKind::CALL_INVALID; return false; } // Check whether the instantiated type arguments still has unimplemented static functions. if (func.TestAttr(Attribute::GENERIC)) { auto typeArgs = ce.baseFunc->GetTypeArgs(); auto ref = DynamicCast(ce.baseFunc.get()); // Currently, only name reference node can have typeArgument. auto isGenericLegal = !(typeArgs.empty() && ref && ref->instTys.empty()) && CheckCallGenericDeclInstantiation(&func, typeArgs, *ce.baseFunc); if (!isGenericLegal) { ce.ty = TypeManager::GetInvalidTy(); ce.callKind = CallKind::CALL_INVALID; return false; } } ce.resolvedFunction = &func; UpdateCallTargetsForLSP(ce, func); DynamicBindingThisType(*ce.baseFunc, func, typeMapping); if (!func.TestAttr(Attribute::FOREIGN) && !func.TestAttr(Attribute::C)) { bool valid = true; for (auto& fa : ce.args) { if (fa->withInout) { diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_can_only_used_in_cfunc_calling, *fa); valid = false; } } if (!valid) { return false; } } else { // VArray must modify by 'inout' in CFunc call. auto fa = std::find_if( ce.args.begin(), ce.args.end(), [](auto& fa) { return Is(fa->ty) && !fa->withInout; }); if (fa != ce.args.end()) { (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_mismatch, *fa->get(), VARRAY_NAME); return false; } } if (func.ty->HasQuestTy()) { DiagUnableToInferReturnType(diag, func, ce); ce.ty = TypeManager::GetInvalidTy(); ce.callKind = CallKind::CALL_INVALID; return false; } return true; } namespace { Ptr MakePlaceholderFuncTy(size_t arity, TypeManager& tyMgr) { std::vector> paramTys; Ptr retTy = tyMgr.AllocTyVar(); for (size_t i = 0; i < arity; i++) { paramTys.push_back(tyMgr.AllocTyVar()); } return tyMgr.GetFunctionTy(paramTys, retTy); } Ptr TryCastingToFuncTy(Ptr ty, size_t arity, TypeManager& tyMgr) { if (auto funcTy = DynamicCast(ty); funcTy) { return funcTy; } if (auto genTy = DynamicCast(ty); genTy) { if (genTy->isPlaceholder) { if (auto placeholderFuncTy = tyMgr.ConstrainByCtor(*genTy, *MakePlaceholderFuncTy(arity, tyMgr))) { return StaticCast(placeholderFuncTy); } else { return nullptr; } } auto allUpperBounds = genTy->upperBounds; for (auto& upperBound : allUpperBounds) { if (auto funcTy = DynamicCast(upperBound); funcTy) { return funcTy; } } } return nullptr; }; } bool TypeChecker::TypeCheckerImpl::CheckNonNormalCall(ASTContext& ctx, Ptr target, CallExpr& ce) { CJC_NULLPTR_CHECK(ce.baseFunc); bool res = false; if (auto funcTy = TryCastingToFuncTy(ce.baseFunc->ty, ce.args.size(), typeManager)) { auto maybeVariadic = IsPossibleVariadicFunction(*funcTy, ce); std::vector diagnostics; if (maybeVariadic) { auto ds = DiagSuppressor(diag); res = CheckFuncPtrCall(ctx, target, ce, *funcTy); diagnostics = ds.GetSuppressedDiag(); } else { res = CheckFuncPtrCall(ctx, target, ce, *funcTy); } if (!res && maybeVariadic) { DesugarVariadicCallExpr(ctx, ce, funcTy->paramTys.size() - 1); bool success = false; { auto ds = DiagSuppressor(diag); success = CheckFuncPtrCall(ctx, target, *StaticAs(ce.desugarExpr.get()), *funcTy); if (diagnostics.empty()) { diagnostics = ds.GetSuppressedDiag(); } } if (success) { ce.ty = ce.desugarExpr->ty; res = true; } else { RecoverFromVariadicCallExpr(ce); std::for_each(diagnostics.cbegin(), diagnostics.cend(), [this](auto info) { diag.Diagnose(info); }); ce.ty = TypeManager::GetInvalidTy(); res = false; } } // VArray must modify by 'inout' in CFunc lambda call. if (funcTy->IsCFunc()) { auto fa = std::find_if( ce.args.begin(), ce.args.end(), [](auto& fa) { return Is(fa->ty) && !fa->withInout; }); if (fa != ce.args.end()) { (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_mismatch, *fa->get(), VARRAY_NAME); res = false; } } if (funcTy->isC && !ce.TestAttr(Attribute::UNSAFE) && !IsValidCFuncConstructorCall(ce)) { diag.Diagnose(ce, DiagKind::sema_unsafe_function_invoke_failed); res = false; } } else if (ChkFunctionCallExpr(ctx, target, ce)) { res = true; } if (!res) { ce.ty = TypeManager::GetInvalidTy(); } return res; } void TypeChecker::TypeCheckerImpl::PostProcessForLSP(CallExpr& ce, const std::vector>& result) const { if (result.size() > 1) { // According to demand from LSP, a target should be bind when conflict happens. // Caller guarantees no nullptr existing in the 'result'. UpdateCallTargetsForLSP(ce, *result[0]); ce.resolvedFunction = result[0]; } else { ReplaceTarget(ce.baseFunc.get(), nullptr); ce.baseFunc->ty = TypeManager::GetInvalidTy(); } } // The type of CallExpr depends on the type of its baseExpr, which can be refExpr or memberAccess. bool TypeChecker::TypeCheckerImpl::ChkCallExpr(ASTContext& ctx, Ptr target, CallExpr& ce) { if (ce.desugarExpr) { return ChkDesugarExprOfCallExpr(ctx, target, ce); } if (!ce.baseFunc) { return false; } bool prevCheckTrue = ce.callKind != CallKind::CALL_INVALID && Ty::IsTyCorrect(ce.ty) && typeManager.GetUnsolvedTyVars().empty(); std::vector> argsTys; if (prevCheckTrue) { std::transform(ce.args.begin(), ce.args.end(), std::back_inserter(argsTys), [](auto& arg) { return arg->ty; }); } // Set type as not null before starting type check. ce.ty = TypeManager::GetNonNullTy(ce.ty); // Step 1: Check call expression's base expression, get valid function candidates & decl. // Decl only be set when call base is valid RefExpr or MemberAccess. Ptr decl{nullptr}; std::vector> candidates; if (!ChkCallBaseExpr(ctx, ce, decl, target, candidates)) { // If no call base exist, expr may be array or pointer builtin api call. return ChkBuiltinCall(ctx, *TypeManager::GetNonNullTy(target), ce); } if (!Ty::IsTyCorrect(ce.baseFunc->ty)) { return false; } if (ce.baseFunc->ty->IsNothing()) { return SynArgsOfNothingBaseExpr(ctx, ce); } // Check ToTokens interface implementation. if (ce.needCheckToTokens) { CheckToTokensImpCallExpr(ce); } // Step 2: Check & set callKind. // Function pointer call, operator () function call and non-valid call base, return false. if (!CheckCallKind(ce, decl, ce.callKind)) { return CheckNonNormalCall(ctx, target, ce); } if (ce.callKind == CallKind::CALL_INVALID) { return false; } if (candidates.empty()) { DiagnoseForCall(candidates, candidates, ce, *decl); return false; } bool maybeEnumOverloadOP = IsPossibleEnumConstructor(candidates, *ce.baseFunc); bool maybeVariadicFunction = IsPossibleVariadicFunction(candidates, ce); bool maybeEnumOrVariadic = maybeEnumOverloadOP || maybeVariadicFunction; // Do not diagnose when candidates may be enum constructor or operator(). // Step 3: Check whether function candidates matched. SubstPack typeMapping; std::vector> result; std::vector diagnostics; PData::CommitScope cs(typeManager.constraints); if (maybeEnumOrVariadic) { auto ds = DiagSuppressor(diag); result = MatchFunctionForCall(ctx, candidates, ce, target, typeMapping); diagnostics = ds.GetSuppressedDiag(); } else { result = MatchFunctionForCall(ctx, candidates, ce, target, typeMapping); } ce.ty = TypeManager::GetNonNullTy(ce.ty); if (result.size() == 1) { return PostCheckCallExpr(ctx, ce, *result[0], typeMapping); } PData::Reset(typeManager.constraints); // If candidates may be enum constructor or operator(), clear baseFunc's ty when constructor mismatched. ce.baseFunc->ty = maybeEnumOrVariadic ? TypeManager::GetInvalidTy() : ce.baseFunc->ty; auto ret = (maybeEnumOverloadOP && ChkFunctionCallExpr(ctx, target, ce)) || (maybeVariadicFunction && result.empty() && ChkVariadicCallExpr(ctx, target, ce, candidates, diagnostics)); if (ret) { return true; } // If no matching or having multiple matching candidates, generate diagnosis. if (diagnostics.empty()) { DiagnoseForCall(candidates, result, ce, *decl); } // Recover arguments' types if current check failed but previous check passed. RecoverCallArgs(ctx, ce, argsTys); if (diag.GetDiagnoseStatus()) { PostProcessForLSP(ce, result); } else if (!prevCheckTrue) { ce.callKind = CallKind::CALL_INVALID; } return false; } bool TypeChecker::TypeCheckerImpl::ChkDesugarExprOfCallExpr(ASTContext& ctx, Ptr target, CallExpr& ce) { if (Ty::IsTyCorrect(ce.desugarExpr->ty)) { ce.ty = ce.desugarExpr->ty; return !target || typeManager.IsSubtype(ce.desugarExpr->ty, target); } bool isWellTyped; // If `ce` is a builtin call and it has been cleared, we should recheck the `desugarExpr`. if (Ty::IsTyCorrect(target)) { isWellTyped = Check(ctx, target, ce.desugarExpr.get()); } else { isWellTyped = Ty::IsTyCorrect(Synthesize(ctx, ce.desugarExpr.get())); } ce.ty = ce.desugarExpr->ty; return isWellTyped && Ty::IsTyCorrect(ce.ty); } bool TypeChecker::TypeCheckerImpl::SynArgsOfNothingBaseExpr(ASTContext& ctx, CallExpr& ce) { bool isWellTyped = true; std::for_each(ce.args.cbegin(), ce.args.cend(), [this, &ctx, &isWellTyped](auto& arg) { isWellTyped = isWellTyped && Ty::IsTyCorrect(Synthesize(ctx, arg.get())); ReplaceIdealTy(*arg); ReplaceIdealTy(*arg->expr); }); ce.ty = isWellTyped ? RawStaticCast(TypeManager::GetNothingTy()) : TypeManager::GetInvalidTy(); return isWellTyped; } bool TypeChecker::TypeCheckerImpl::ChkFunctionCallExpr(ASTContext& ctx, Ptr target, CallExpr& ce) { DesugarCallExpr(ctx, ce); auto desugarCallExpr = StaticAs(ce.desugarExpr.get()); if (!ChkCallExpr(ctx, target, *desugarCallExpr)) { RecoverToCallExpr(ce); diag.Diagnose(ce, DiagKind::sema_no_match_operator_function_call); ce.ty = TypeManager::GetInvalidTy(); return false; } ce.callKind = desugarCallExpr->callKind; ce.ty = desugarCallExpr->ty; return true; } bool TypeChecker::TypeCheckerImpl::ChkVariadicCallExpr(ASTContext& ctx, Ptr target, CallExpr& ce, const std::vector>& candidates, std::vector& diagnostics) { if (!ChkArgsOrder(diag, ce)) { return false; } std::vector> results; std::map>> fixedPositionalArityToGroup = FuncDeclsGroupByFixedPositionalArity(candidates, ce); // The `CallExpr` is desugarred based on the fixed positional arity. We group the candidates based on // the fixed positional arity and use `MatchFunctionForCall` to try the desugarred call. for (auto& [fixedPositionalArity, group] : fixedPositionalArityToGroup) { DesugarVariadicCallExpr(ctx, ce, fixedPositionalArity); auto ds = DiagSuppressor(diag); SubstPack typeMapping; std::vector> matchResults = MatchFunctionForCall(ctx, group, *StaticAs(ce.desugarExpr.get()), target, typeMapping); if (matchResults.size() == 1) { results.emplace_back(matchResults.front()); } if (diagnostics.empty()) { // The first diagnostic will be reported if the variadic call checked failed. diagnostics = ds.GetSuppressedDiag(); } RecoverFromVariadicCallExpr(ce); } if (results.size() == 1) { size_t fixedPositionalArity = GetPositionalParamSize(*results.front()) - 1; DesugarVariadicCallExpr(ctx, ce, fixedPositionalArity); SubstPack typeMapping; std::vector> matchResults = MatchFunctionForCall( ctx, results, *StaticAs(ce.desugarExpr.get()), target, typeMapping); CJC_ASSERT(matchResults.size() == 1); bool ret = PostCheckCallExpr( ctx, *StaticAs(ce.desugarExpr.get()), *matchResults.front(), typeMapping); if (ret) { ce.ty = ce.desugarExpr->ty; ce.resolvedFunction = StaticAs(ce.desugarExpr.get())->resolvedFunction; } else { RecoverFromVariadicCallExpr(ce); } return ret; } else if (results.empty()) { // If no matching functions are found, report the first diagnostic. std::for_each(diagnostics.cbegin(), diagnostics.cend(), [this](auto info) { diag.Diagnose(info); }); } else { // Multiple matching functions found, report ambiguous function call. Ptr decl = results.front(); CJC_NULLPTR_CHECK(decl); if (decl->TestAttr(Attribute::CONSTRUCTOR) && decl->outerDecl) { decl = decl->outerDecl; } DiagnoseForCall(candidates, results, ce, *decl); } ce.callKind = CallKind::CALL_INVALID; return false; } void TypeChecker::TypeCheckerImpl::CheckToTokensImpCallExpr(const CallExpr& ce) { auto toTokensDecl = importManager.GetAstDecl("ToTokens"); // ce has the form: a.obj.ToTokens() if (ce.astKind != ASTKind::CALL_EXPR || !toTokensDecl) { return; } auto ma = StaticAs(ce.baseFunc.get()); auto be = ma->baseExpr.get(); if (be) { auto prTys = promotion.Promote(*be->ty, *toTokensDecl->ty); auto ty = prTys.empty() ? TypeManager::GetInvalidTy() : *prTys.begin(); if (ty != toTokensDecl->ty) { diag.Diagnose(ce, DiagKind::sema_invalid_tokens_implementation, be->ty->String()); } } } bool TypeChecker::TypeCheckerImpl::CheckStaticCallNonStatic( const ASTContext& ctx, const CallExpr& ce, const FuncDecl& result) { // Return true if result is not a structure decl's method. if (!result.outerDecl || !result.outerDecl->IsNominalDecl()) { return true; } if (auto re = DynamicCast(ce.baseFunc.get()); re) { return IsLegalAccessFromStaticFunc(ctx, *re, result); } return true; } void TypeChecker::TypeCheckerImpl::SpreadInstantiationTy(Node& node, const SubstPack& typeMapping) { if (typeMapping.u2i.empty() || !Ty::IsTyCorrect(node.ty)) { return; } Walker walkerExpr(&node, [this, &typeMapping](Ptr node) -> VisitAction { if (Ty::IsTyCorrect(node->ty)) { node->ty = typeManager.ApplySubstPack(node->ty, typeMapping); return VisitAction::WALK_CHILDREN; } return VisitAction::WALK_CHILDREN; }); walkerExpr.Walk(); } cangjie_compiler-1.0.7/src/Sema/TypeCheckClassLike.cpp000066400000000000000000000225101510705540100226410ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements typecheck apis for class and interface. */ #include "TypeCheckerImpl.h" #include #include #include #include "Diags.h" #include "TypeCheckUtil.h" #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Sema/TestManager.h" using namespace Cangjie; using namespace AST; using namespace Sema; using namespace TypeCheckUtil; namespace { void CheckAnnotationHasConstInit(DiagnosticEngine& diag, const ClassDecl& cd) { CJC_NULLPTR_CHECK(cd.body); if (!Utils::In(cd.body->decls, [](const auto& decl) { CJC_NULLPTR_CHECK(decl); return IsInstanceConstructor(*decl) && decl->IsConst(); })) { (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_annotation_no_const_init, MakeRangeForDeclIdentifier(cd)); } } } // namespace void TypeChecker::TypeCheckerImpl::CheckSealedInheritance(const Decl& child, const Type& parent) { Ptr target = parent.GetTarget(); if (target == nullptr) { return; } if (target->TestAttr(Attribute::SEALED)) { if (target->TestAttr(Attribute::IMPORTED) && !child.TestAttr(Attribute::IMPORTED)) { // Parent is imported, and child is defined in current package. DiagCannotInheritSealed(diag, child, parent); } // Otherwise, both parent and child are imported, // which is orphan extension and the error is reported by `CheckExtendOrphanRule`. if (target->TestAttr(Attribute::PLATFORM) && !child.TestAttr(Attribute::FROM_COMMON_PART)) { // Parent is imported, and child is defined in current package. DiagCannotInheritSealed(diag, child, parent, true); } } } /** * Check some constraints for decls that inherit `ThreadContext` from package `core`. * eg: Should not inherit or implement `ThreadContext` if decls are not within the whitelist. */ void TypeChecker::TypeCheckerImpl::CheckThreadContextInheritance(const Decl& decl, const Type& parent) { static std::unordered_map whitelist = { {"ThreadContext", "std.core"}, {"MainThreadContext", "ohos.base"} }; // Manage all derived type of `ThreadContext` or `SchedulerNativeHandle` which from package `core` with whitelist. auto target = parent.GetTarget(); if (target == nullptr || target->fullPackageName != "std.core" || target->identifier != "ThreadContext") { return; } // Decls cannot be modified with 'open' when inherit 'ThreadContext' if (decl.TestAttr(Attribute::OPEN)) { (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_inherit_thread_context_not_open, decl, decl.identifier); return; } auto found = whitelist.find(decl.identifier); if (found != whitelist.end() && found->second == decl.curFile->curPackage->fullPackageName) { return; } (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_inherit_thread_context_invalid, decl, decl.identifier); } void TypeChecker::TypeCheckerImpl::CheckClassDecl(ASTContext& ctx, ClassDecl& cd) { if (cd.TestAttr(Attribute::IS_CHECK_VISITED)) { return; } // Mark the current class declaration is checked. cd.EnableAttr(Attribute::IS_CHECK_VISITED); if (!cd.TestAttr(Attribute::PUBLIC)) { for (auto& ann : cd.annotations) { CJC_NULLPTR_CHECK(ann); if (ann->kind == AnnotationKind::ANNOTATION) { (void)diag.DiagnoseRefactor( DiagKindRefactor::sema_annotation_non_public, MakeRangeForDeclIdentifier(cd)); break; } } } if (cd.TestAttr(Attribute::IS_ANNOTATION)) { CheckAnnotationHasConstInit(diag, cd); } // Do type check for all implemented interfaces. for (auto& it : cd.inheritedTypes) { CJC_NULLPTR_CHECK(it); Synthesize(ctx, it.get()); if (auto rt = DynamicCast(it.get()); rt && rt->ref.target) { CheckSealedInheritance(cd, *rt); CheckThreadContextInheritance(cd, *rt); } } TypeCheckCompositeBody(ctx, cd, cd.body->decls); CheckRecursiveConstructorCall(cd.body->decls); if (cd.TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE)) { CheckJavaInteropLibImport(cd); } if (cd.TestAnyAttr(Attribute::OBJ_C_MIRROR, Attribute::OBJ_C_MIRROR_SUBTYPE)) { CheckObjCInteropLibImport(cd); } } void TypeChecker::TypeCheckerImpl::CheckAndAddSubDecls( const Type& type, ClassDecl& cd, bool& hasSuperClass, int& superClassLikeNum) const { auto target = Ty::IsTyCorrect(type.ty) ? Ty::GetDeclPtrOfTy(type.ty) : type.GetTarget(); if (auto id = AST::As(target); id) { id->subDecls.insert(&cd); } else if (auto superClass = AST::As(target); superClass) { if (hasSuperClass) { diag.Diagnose(type, DiagKind::sema_illegal_multi_inheritance, cd.identifier.Val()); } else { if (superClassLikeNum != 0) { diag.Diagnose(type, DiagKind::sema_superclass_must_be_placed_at_first, superClass->identifier.Val()); } } // Check at the first. superClass->subDecls.insert(&cd); if ((!superClass->TestAttr(Attribute::ABSTRACT) && !superClass->TestAttr(Attribute::OPEN)) || TestManager::IsDeclOpenToMock(*superClass) ) { diag.Diagnose(type, DiagKind::sema_non_inheritable_super_class, superClass->identifier.Val()); } hasSuperClass = true; } else if (auto tad = AST::As(target); tad) { CheckAndAddSubDecls(*tad->type, cd, hasSuperClass, superClassLikeNum); } else { // Can only implement class or interface. diag.Diagnose(type, DiagKind::sema_class_inherit_non_class_nor_interface, cd.identifier.Val()); return; } superClassLikeNum++; } bool TypeChecker::TypeCheckerImpl::HasSuperClass(ClassDecl& cd) const { int count = 0; bool hasSuperClass = false; for (auto& it : cd.inheritedTypes) { CheckAndAddSubDecls(*it, cd, hasSuperClass, count); } return hasSuperClass; } void TypeChecker::TypeCheckerImpl::AddObjectSuperClass(ASTContext& ctx, ClassDecl& cd) { auto tmp = MakeOwned(); tmp->curFile = cd.curFile; tmp->ref.identifier = OBJECT_NAME; tmp->EnableAttr(Attribute::COMPILER_ADD); if (ctx.fullPackageName == CORE_PACKAGE_NAME && cd.identifier == OBJECT_NAME) { return; // Do not add 'Object' for itself. } else if (auto objectDecl = importManager.GetCoreDecl(OBJECT_NAME)) { tmp->ref.target = objectDecl; tmp->ty = tmp->ref.target->ty; cd.inheritedTypes.insert(cd.inheritedTypes.begin(), std::move(tmp)); } else { ctx.diag.Diagnose(cd, DiagKind::sema_no_core_object); } } bool TypeChecker::TypeCheckerImpl::AddJObjectSuperClassJavaInterop(ASTContext& ctx, ClassDecl& cd) { using namespace Interop::Java; if (ctx.fullPackageName == INTEROP_JAVA_LANG_PACKAGE && cd.identifier == INTEROP_JOBJECT_NAME) { return false; } if (auto objectDecl = importManager.GetImportedDecl( INTEROP_JAVA_LANG_PACKAGE, INTEROP_JOBJECT_NAME)) { CJC_ASSERT(objectDecl->astKind == ASTKind::CLASS_DECL); auto tmp = MakeOwned(); tmp->EnableAttr(AST::Attribute::COMPILER_ADD); tmp->curFile = cd.curFile; tmp->ref.identifier = SrcIdentifier{INTEROP_JOBJECT_NAME}; tmp->ref.target = objectDecl; tmp->ty = tmp->ref.target->ty; cd.inheritedTypes.insert(cd.inheritedTypes.begin(), std::move(tmp)); cd.EnableAttr(Attribute::JAVA_MIRROR_SUBTYPE); } else { ctx.diag.DiagnoseRefactor(DiagKindRefactor::sema_member_not_imported, cd.identifier.Begin(), INTEROP_JAVA_LANG_PACKAGE + "." + INTEROP_JOBJECT_NAME); } return true; } void TypeChecker::TypeCheckerImpl::CheckInterfaceDecl(ASTContext& ctx, InterfaceDecl& id) { if (id.TestAttr(Attribute::IS_CHECK_VISITED)) { return; } // Mark the current declaration is checked. id.EnableAttr(Attribute::IS_CHECK_VISITED); // Do type check for all implemented interfaces. for (auto& interfaceType : id.inheritedTypes) { Synthesize(ctx, interfaceType.get()); if (auto it = DynamicCast(interfaceType->ty); it) { it->decl->subDecls.insert(&id); CheckSealedInheritance(id, *interfaceType); CheckThreadContextInheritance(id, *interfaceType); } else { // Can only implement interface. diag.Diagnose(id, DiagKind::sema_interface_inherit_non_interface, id.identifier.Val()); } } TypeCheckCompositeBody(ctx, id, id.body->decls); if (id.TestAnyAttr(Attribute::JAVA_MIRROR, Attribute::JAVA_MIRROR_SUBTYPE)) { CheckJavaInteropLibImport(id); } if (id.TestAnyAttr(Attribute::OBJ_C_MIRROR, Attribute::OBJ_C_MIRROR_SUBTYPE)) { CheckObjCInteropLibImport(id); } } cangjie_compiler-1.0.7/src/Sema/TypeCheckDecl.cpp000066400000000000000000000744301510705540100216460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements typecheck apis for decls. */ #include "TypeCheckerImpl.h" #include #include #include "Diags.h" #include "BuiltInOperatorUtil.h" #include "TypeCheckUtil.h" #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/Position.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Utils/CheckUtils.h" using namespace Cangjie; using namespace AST; using namespace Sema; using namespace TypeCheckUtil; namespace { void InsertEnumConstructors(ASTContext& ctx, const EnumDecl& ed, bool enableMacroInLsp) { for (auto& ctor : ed.constructors) { CJC_NULLPTR_CHECK(ctor); if (ctor->astKind == ASTKind::VAR_DECL) { ctx.InsertEnumConstructor(ctor->identifier, 0, *ctor, enableMacroInLsp); } else if (ctor->astKind == ASTKind::FUNC_DECL) { auto& fd = StaticCast(*ctor); CJC_ASSERT(fd.funcBody && fd.funcBody->paramLists.size() == 1 && fd.funcBody->paramLists.front()); ctx.InsertEnumConstructor( fd.identifier, fd.funcBody->paramLists.front()->params.size(), fd, enableMacroInLsp); } } } inline void DiagUnableToInferDecl(DiagnosticEngine& diag, const Decl& decl) { diag.DiagnoseRefactor(DiagKindRefactor::sema_unable_to_infer_decl, MakeRangeForDeclIdentifier(decl)); } } // namespace void TypeChecker::TypeCheckerImpl::CheckFuncDecl(ASTContext& ctx, FuncDecl& fd) { CJC_NULLPTR_CHECK(fd.funcBody); if (fd.TestAttr(Attribute::IS_CHECK_VISITED)) { return; } fd.EnableAttr(Attribute::IS_CHECK_VISITED); // NOTE: Property decl's getter/setter function should be ignored from 'redef' checking. auto redefModifier = TypeCheckUtil::FindModifier(fd, TokenKind::REDEF); auto staticModifier = TypeCheckUtil::FindModifier(fd, TokenKind::STATIC); if (redefModifier && staticModifier == nullptr && fd.propDecl == nullptr) { diag.Diagnose(*redefModifier, DiagKind::sema_redef_modify_static_func, "function"); } fd.funcBody->funcDecl = &fd; (void)CheckFuncBody(ctx, *fd.funcBody); if (Ty::IsTyCorrect(fd.ty) && fd.ty->HasQuestTy()) { CJC_ASSERT(fd.ty->IsFunc()); // NOTE: Error's for synthesized quest ty must be reported in 'CheckBodyRetType', // otherwise it means funcBody contains broken nodes. // Update return type to invalid, keep 'fd''s type in funcTy format. fd.ty = typeManager.GetFunctionTy(RawStaticCast(fd.ty)->paramTys, TypeManager::GetInvalidTy()); } // NOTE: 'fd''s type should only be updated inside 'CheckFuncBody' not here. if (fd.TestAttr(AST::Attribute::MAIN_ENTRY)) { CheckEntryFunc(fd); } else if (fd.TestAttr(Attribute::OPERATOR)) { CheckOperatorOverloadFunc(fd); } } void TypeChecker::TypeCheckerImpl::CheckStaticVarAccessNonStatic(const VarDecl& vd) { // Only check for static variable. if (!vd.TestAttr(Attribute::STATIC)) { return; } Walker walkDecl(vd.initializer.get(), [this, &vd](Ptr node) -> VisitAction { if (auto re = DynamicCast(node); re) { auto target = re->GetTarget(); if (!target || target->astKind == ASTKind::FUNC_DECL) { return VisitAction::SKIP_CHILDREN; } // If vardecl is static, the initializer cannot contain non-static member. bool invalidAccess = !re->TestAttr(Attribute::COMPILER_ADD) && target != nullptr && !target->IsStaticOrGlobal() && !target->IsTypeDecl() && !target->TestAttr(Attribute::CONSTRUCTOR) && !target->TestAttr(Attribute::ENUM_CONSTRUCTOR) && target->outerDecl; if (invalidAccess) { diag.Diagnose( vd, DiagKind::sema_static_variable_cannot_access_non_static_member, re->ref.identifier.Val()); } return VisitAction::WALK_CHILDREN; } else if (node->astKind == ASTKind::MEMBER_ACCESS || node->astKind == ASTKind::FUNC_BODY) { return VisitAction::SKIP_CHILDREN; } else { return VisitAction::WALK_CHILDREN; } }); walkDecl.Walk(); } void TypeChecker::TypeCheckerImpl::CheckEntryFunc(FuncDecl& fd) { bool invalid = !fd.curFile || !fd.curFile->curPackage; if (invalid || !fd.TestAttr(Attribute::GLOBAL)) { return; // Do not need diagnose. } if (fd.curFile->isCommon) { diag.DiagnoseRefactor(DiagKindRefactor::sema_common_package_has_main, fd); } if (!mainFunctionMap.empty() && (mainFunctionMap.find(fd.curFile) == mainFunctionMap.end() || mainFunctionMap[fd.curFile].find(&fd) == mainFunctionMap[fd.curFile].end())) { (void)diag.Diagnose(fd, DiagKind::sema_redefinition_entry); } if (fd.funcBody->retType && fd.funcBody->retType->ty) { auto retTy = fd.funcBody->retType->ty; if (Ty::IsTyCorrect(retTy) && !(retTy->IsInteger() || retTy->IsUnit())) { (void)diag.Diagnose(fd, DiagKind::sema_unexpected_return_type_for_entry); } } bool noParamList = !fd.funcBody || fd.funcBody->paramLists.empty(); if (noParamList) { return; // Invalid main function, error messages should be reported before. } bool invalidParamTy = std::any_of(fd.funcBody->paramLists[0]->params.begin(), fd.funcBody->paramLists[0]->params.end(), [](const OwnedPtr& fp) { bool isArrayTy = fp->ty && fp->ty->IsStructArray() && !fp->ty->typeArgs.empty(); bool isArrayStringTy = isArrayTy && fp->ty->typeArgs[0] && fp->ty->typeArgs[0]->IsString(); if (isArrayStringTy) { return false; } return true; }); if (invalidParamTy || fd.funcBody->paramLists[0]->params.size() > 1) { (void)diag.Diagnose(fd, DiagKind::sema_unexpected_param_for_entry); } (void)mainFunctionMap[fd.curFile].emplace(&fd); } void TypeChecker::TypeCheckerImpl::CheckOperatorOverloadFunc(const FuncDecl& fd) { auto funcTy = DynamicCast(fd.ty); if (!Ty::IsTyCorrect(funcTy) || fd.op == TokenKind::ILLEGAL || fd.op == TokenKind::LPAREN) { return; } Ptr baseTy = (fd.outerDecl != nullptr) ? fd.outerDecl->ty : nullptr; if (fd.op == TokenKind::LSQUARE) { return HandIndexOperatorOverload(fd, *funcTy); } const std::vector>& paramTys = funcTy->paramTys; switch (paramTys.size()) { case 0: { // If the size of paramsTy is 0. // Unary operators. if (IsUnaryOperator(fd.op)) { if (baseTy && IsBuiltinUnaryExpr(fd.op, *baseTy)) { (void)diag.Diagnose(fd, DiagKind::sema_operator_overload_built_in_unary_operator, fd.identifier.Val(), baseTy->String()); } } else { // If fd.op is not a unary operator. diag.Diagnose(fd, DiagKind::sema_operator_overload_invalid_num_parameter, fd.identifier.Val()); } break; } case 1: { // If the size of paramsTy is 1. // Binary operators. // Allow 'Intrinsic' function to overload. if (!IsBinaryOperator(fd.op)) { // If fd.op is not a binary operator. (void)diag.Diagnose(fd, DiagKind::sema_operator_overload_invalid_num_parameter, fd.identifier.Val()); } else if (fd.fullPackageName != CORE_PACKAGE_NAME && baseTy && paramTys[0] && IsBuiltinBinaryExpr(fd.op, *baseTy, *paramTys[0])) { (void)diag.Diagnose(fd, DiagKind::sema_operator_overload_built_in_binary_operator, fd.identifier.Val(), baseTy->String(), paramTys[0]->String()); } break; } default: (void)diag.Diagnose(fd, DiagKind::sema_operator_overload_invalid_num_parameter, fd.identifier.Val()); } } void TypeChecker::TypeCheckerImpl::HandIndexOperatorOverload(const FuncDecl& fd, const FuncTy& funcTy) { CJC_ASSERT(!fd.funcBody->paramLists.empty()); const auto& params = fd.funcBody->paramLists[0]->params; if (params.empty()) { (void)diag.Diagnose(fd, DiagKind::sema_operator_overload_invalid_num_parameter, fd.identifier.Val()); return; } // Index operator overload function can have many parameters but at most one named parameter. auto lastIndex = params.size() - 1; if (!params[lastIndex]->isNamedParam) { // It is getter. return; } std::vector> invalidNamedParams; for (auto& param : params) { if (param->isNamedParam && param->identifier != "value") { (void)invalidNamedParams.emplace_back(*param); } } if (!invalidNamedParams.empty()) { auto& firstParam = invalidNamedParams.front(); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_invalid_subscript_assign_parameter, firstParam, MakeRange(firstParam.get().identifier)); for (auto iter = invalidNamedParams.cbegin() + 1; iter != invalidNamedParams.cend(); ++iter) { builder.AddHint(MakeRange(iter->get().identifier)); } } if (params.size() <= 1) { (void)diag.DiagnoseRefactor( DiagKindRefactor::sema_invalid_subscript_assign_parameter_num, fd, MakeRange(fd.identifier)); return; } if (invalidNamedParams.empty() && funcTy.retTy != TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)) { auto range = MakeRange(fd.identifier); if (fd.funcBody && fd.funcBody->retType && !fd.funcBody->retType->begin.IsZero()) { range = MakeRange(fd.funcBody->retType->begin, fd.funcBody->retType->end); } (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_invalid_subscript_assign_return, fd, range); } } void TypeChecker::TypeCheckerImpl::CheckVarDecl(ASTContext& ctx, VarDecl& vd) { if (vd.TestAttr(Attribute::IS_CHECK_VISITED)) { // Unable to infer mutually recursive variables. if (IsGlobalOrMember(vd) && Ty::IsInitialTy(vd.ty)) { DiagUnableToInferDecl(diag, vd); vd.ty = TypeManager::GetInvalidTy(); } return; } // Mark the declaration is checked. vd.EnableAttr(Attribute::IS_CHECK_VISITED); SynchronizeTypeAndInitializer(ctx, vd); if (vd.initializer != nullptr && Ty::IsInitialTy(vd.initializer->ty)) { // Already generate diagnostics before, quit here. return; } CheckStaticVarAccessNonStatic(vd); } void TypeChecker::TypeCheckerImpl::CheckVarWithPatternDecl(ASTContext& ctx, VarWithPatternDecl& vpd) { if (vpd.TestAttr(Attribute::IS_CHECK_VISITED)) { // Unable to infer mutually recursive top level variables. if (vpd.TestAttr(Attribute::GLOBAL) && Ty::IsInitialTy(vpd.ty)) { DiagUnableToInferDecl(diag, vpd); vpd.ty = TypeManager::GetInvalidTy(); } return; } // Mark the current declaration checked. vpd.EnableAttr(Attribute::IS_CHECK_VISITED); SynchronizeTypeAndInitializer(ctx, vpd); if ((vpd.initializer != nullptr && !Ty::IsTyCorrect(vpd.initializer->ty)) || !vpd.irrefutablePattern) { // Already generate diagnostics before, quit here. return; } if (Ty::IsTyCorrect(vpd.ty) && !ChkPattern(ctx, *vpd.ty, *vpd.irrefutablePattern, false)) { diag.Diagnose(*vpd.irrefutablePattern, DiagKind::sema_mismatched_type_for_pattern_in_vardecl); } if (!IsIrrefutablePattern(*vpd.irrefutablePattern)) { diag.Diagnose(*vpd.irrefutablePattern, DiagKind::sema_pattern_can_not_be_assigned); } // Set VarDecl's initializer of VarPattern in TuplePattern by VarWithPatternDecl's initializer. auto tp = DynamicCast(vpd.irrefutablePattern.get()); auto tl = DynamicCast(vpd.initializer.get()); if (vpd.isConst && tp && tl) { if (tp->patterns.size() != tl->children.size()) { return; } for (size_t i = 0; i < tp->patterns.size(); ++i) { if (auto vp = DynamicCast(tp->patterns[i].get()); vp && vp->varDecl) { vp->varDecl->initializer = ASTCloner::Clone(tl->children[i].get()); } } } } template void TypeChecker::TypeCheckerImpl::SynchronizeTypeAndInitializer(ASTContext& ctx, T& vd) { if (vd.type != nullptr && vd.initializer == nullptr) { Synthesize(ctx, vd.type.get()); vd.ty = vd.type->ty; } else if (vd.type != nullptr && vd.initializer != nullptr) { // Always set vd.ty to the one the user gives. Synthesize(ctx, vd.type.get()); if (AST::Ty::IsTyCorrect(vd.type->ty)) { // User has defined vardecl's type. Should not be set to invalid even if initializer is incompatible. vd.ty = vd.type->ty; if (vd.type->ty->IsRune() && IsSingleRuneStringLiteral(*vd.initializer)) { vd.initializer->ty = vd.type->ty; } else if (vd.type->ty->kind == TypeKind::TYPE_UINT8 && IsSingleByteStringLiteral(*vd.initializer)) { vd.initializer->ty = vd.type->ty; ChkLitConstExprRange(StaticCast(*vd.initializer)); } else { bool isWellTyped = Check(ctx, vd.type->ty, vd.initializer.get()); // Unset 'checked' attribute for local variables when there exists any error. if (!isWellTyped && !IsGlobalOrMember(vd)) { vd.DisableAttr(Attribute::IS_CHECK_VISITED); } } } else { // VarDecl's user defined type is invalid, should not update 'vd.ty' with initializer's type. vd.ty = TypeManager::GetInvalidTy(); (void)Synthesize(ctx, vd.initializer.get()); } // Should not report here. Any error should be reported during 'Check' or 'Synthesize' step. } else if (vd.type == nullptr && vd.initializer != nullptr) { bool isWellTyped = SynthesizeAndReplaceIdealTy(ctx, *vd.initializer); // Unset 'checked' attribute for local variables when there exists any error. if (!isWellTyped && !IsGlobalOrMember(vd)) { vd.DisableAttr(Attribute::IS_CHECK_VISITED); } // Set VarDecl's type by its initializer's type. // when the initializer is 'this', the vd's ty will be inferred to the corresponding ClassTy (NOT // ClassThisTy) if (auto ctt = DynamicCast(vd.initializer->ty); ctt && ctt->decl) { vd.ty = typeManager.GetClassTy(*ctt->decl, ctt->typeArgs); } else if (auto fty = DynamicCast(vd.initializer->ty); fty && fty->isC && fty->hasVariableLenArg) { vd.ty = TypeManager::GetInvalidTy(); bool shouldDiag = vd.ShouldDiagnose() && !CanSkipDiag(*vd.initializer); if (shouldDiag) { diag.Diagnose(*vd.initializer, DiagKind::sema_cfunc_var_cannot_have_var_param); } } else { vd.ty = vd.initializer->ty; } } } void TypeChecker::TypeCheckerImpl::CheckPropDecl(ASTContext& ctx, PropDecl& pd) { if (pd.TestAttr(Attribute::IS_CHECK_VISITED)) { return; } // Mark the current declaration is checked. pd.EnableAttr(Attribute::IS_CHECK_VISITED); auto redefModifier = TypeCheckUtil::FindModifier(pd, TokenKind::REDEF); auto staticModifier = TypeCheckUtil::FindModifier(pd, TokenKind::STATIC); if (redefModifier && staticModifier == nullptr) { (void)diag.Diagnose(*redefModifier, DiagKind::sema_redef_modify_static_func, "property"); pd.ty = TypeManager::GetInvalidTy(); } CJC_NULLPTR_CHECK(pd.type); (void)Synthesize(ctx, pd.type.get()); pd.ty = pd.type->ty; for (auto& pmd : pd.getters) { if (pmd->funcBody && !pmd->funcBody->paramLists.empty()) { if (!pmd->funcBody->paramLists[0]->params.empty()) { (void)diag.Diagnose(*pmd, DiagKind::sema_cannot_have_parameter, "getter"); } if (pmd->funcBody->paramLists.size() > 1) { (void)diag.Diagnose(*pmd, DiagKind::sema_cannot_currying, "getter"); } } (void)Synthesize(ctx, pmd.get()); } for (auto& pmd : pd.setters) { if (pmd->funcBody && pmd->funcBody->paramLists.size() > 1) { (void)diag.Diagnose(*pmd, DiagKind::sema_cannot_currying, "setter"); } (void)Synthesize(ctx, pmd.get()); } } void TypeChecker::TypeCheckerImpl::BuildImportedEnumConstructorMap(ASTContext& ctx) { CJC_NULLPTR_CHECK(ctx.curPackage); std::unordered_set visited; for (auto& file : ctx.curPackage->files) { for (auto& [_, decls] : importManager.GetImportedDecls(*file)) { for (auto decl : decls) { auto ed = DynamicCast(decl); if (!ed || visited.count(ed) != 0) { continue; } InsertEnumConstructors(ctx, *ed, ci->invocation.globalOptions.enableMacroInLSP); } } } } void TypeChecker::TypeCheckerImpl::BuildEnumConstructorMap(ASTContext& ctx) const { auto syms = GetSymsByASTKind(ctx, ASTKind::ENUM_DECL); for (auto sym : syms) { if (auto ed = DynamicCast(sym->node)) { InsertEnumConstructors(ctx, *ed, ci->invocation.globalOptions.enableMacroInLSP); } } } void TypeChecker::TypeCheckerImpl::CheckEnumDecl(ASTContext& ctx, EnumDecl& ed) { if (ed.TestAttr(Attribute::IS_CHECK_VISITED)) { return; } // Mark the current declaration is checked. ed.EnableAttr(Attribute::IS_CHECK_VISITED); // Do type check for all implemented interfaces. for (auto& interfaceType : ed.inheritedTypes) { (void)Synthesize(ctx, interfaceType.get()); if (auto id = AST::As(Ty::GetDeclPtrOfTy(interfaceType->ty)); id) { CheckSealedInheritance(ed, *interfaceType); (void)id->subDecls.insert(&ed); } else { // Can only implement interface. Set type to invalid (avoid invalid reference). interfaceType->ty = TypeManager::GetInvalidTy(); (void)diag.Diagnose(ed, DiagKind::sema_type_implement_non_interface, "enum", ed.identifier.Val()); } } // Check constructors. for (auto& it : ed.constructors) { CJC_NULLPTR_CHECK(it); CheckAnnotations(ctx, *it); Walker(it.get(), [this, &ctx](Ptr node) { if (auto type = DynamicCast(node); type) { Synthesize(ctx, type); return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }).Walk(); } // Check each function. for (auto& it : ed.members) { if (it->astKind == ASTKind::FUNC_DECL) { auto fd = StaticAs(it.get()); (void)Synthesize(ctx, fd); } else if (it->astKind == ASTKind::PROP_DECL) { auto pd = StaticAs(it.get()); (void)Synthesize(ctx, pd); if (pd->isVar) { auto mutDecl = TypeCheckUtil::FindModifier(*pd, TokenKind::MUT); CJC_ASSERT(mutDecl); if (mutDecl) { (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_immutable_type_illegal_property, *mutDecl); } } } } } void TypeChecker::TypeCheckerImpl::SetEnumEleTy(Decl& constructor) { // Imported enum decl's constructor may have valid type. if (!constructor.TestAttr(Attribute::ENUM_CONSTRUCTOR) || Ty::IsTyCorrect(constructor.ty)) { return; } if (constructor.astKind == ASTKind::VAR_DECL) { if (constructor.outerDecl != nullptr) { constructor.ty = constructor.outerDecl->ty; } } else if (constructor.astKind == ASTKind::FUNC_DECL) { SetEnumEleTyHandleFuncDecl(*StaticAs(&constructor)); } else { (void)diag.Diagnose(constructor, DiagKind::sema_invalid_constructor_in_enum); constructor.ty = TypeManager::GetInvalidTy(); } } void TypeChecker::TypeCheckerImpl::CheckEnumFuncDeclIsCStructParam(const FuncDecl& funcDecl) { bool invalid = !funcDecl.funcBody || funcDecl.funcBody->paramLists.empty(); if (invalid) { return; } for (auto& it : funcDecl.funcBody->paramLists[0]->params) { // String type is special, we should solve this after. if (it->ty && Ty::IsCStructType(*it->ty)) { auto structTy = RawStaticCast(it->ty); CJC_ASSERT(structTy && structTy->declPtr); if (structTy->declPtr->identifier != "String" && funcDecl.outerDecl) { (void)diag.Diagnose(funcDecl, DiagKind::sema_enum_pattern_func_param_cty_error, funcDecl.identifier.Val(), funcDecl.outerDecl->identifier.Val()); } } } } void TypeChecker::TypeCheckerImpl::SetEnumEleTyHandleFuncDecl(FuncDecl& funcDecl) { bool invalid = !funcDecl.outerDecl || !funcDecl.funcBody || funcDecl.funcBody->paramLists.empty(); if (invalid) { funcDecl.ty = TypeManager::GetInvalidTy(); return; } // EnumDecl's func constructor must be 'CtorName(Type1, Type2...)'. // The target and type of parameters should be checked in 'ResolveName' stage. std::vector> paramTys; for (auto& param : funcDecl.funcBody->paramLists[0]->params) { if (!param->type) { continue; } auto ty = param->type->ty; param->ty = ty; paramTys.emplace_back(ty); } auto ctorTy = typeManager.GetFunctionTy(paramTys, funcDecl.outerDecl->ty); funcDecl.funcBody->ty = ctorTy; funcDecl.ty = ctorTy; funcDecl.funcBody->retType = MakeOwned(); funcDecl.funcBody->retType->ty = ctorTy->retTy; funcDecl.funcBody->retType->begin = funcDecl.begin; funcDecl.funcBody->retType->end = funcDecl.end; // Check c ffi type usage. if (funcDecl.outerDecl->TestAttr(Attribute::C)) { diag.Diagnose(funcDecl, DiagKind::sema_enum_pattern_func_cty_error, funcDecl.identifier.Val(), funcDecl.outerDecl->identifier.Val()); } else { CheckEnumFuncDeclIsCStructParam(funcDecl); } } void TypeChecker::TypeCheckerImpl::CheckStructDecl(ASTContext& ctx, StructDecl& sd) { if (sd.TestAttr(Attribute::IS_CHECK_VISITED)) { return; } // Mark the current declaration is checked. sd.EnableAttr(Attribute::IS_CHECK_VISITED); // Do type check for all implemented interfaces. for (auto& interfaceType : sd.inheritedTypes) { (void)Synthesize(ctx, interfaceType.get()); if (auto id = AST::As(Ty::GetDeclPtrOfTy(interfaceType->ty)); id) { CheckSealedInheritance(sd, *interfaceType); (void)id->subDecls.insert(&sd); } else { // Can only implement interface. Set type to invalid (avoid invalid reference). interfaceType->ty = TypeManager::GetInvalidTy(); (void)diag.Diagnose(sd, DiagKind::sema_type_implement_non_interface, "struct", sd.identifier.Val()); } } if (sd.ty && Ty::IsCStructType(*sd.ty)) { if (sd.generic) { (void)diag.Diagnose( *sd.generic, sd.generic->leftAnglePos, DiagKind::sema_cffi_cannot_have_type_param, "struct with @C"); } if (!sd.inheritedTypes.empty()) { diag.DiagnoseRefactor(DiagKindRefactor::sema_cstruct_cannot_impl_interfaces, MakeRange(sd.identifier)); } } CJC_NULLPTR_CHECK(sd.body); TypeCheckCompositeBody(ctx, sd, sd.body->decls); CheckRecursiveConstructorCall(sd.body->decls); if (sd.TestAnyAttr(Attribute::JAVA_CJ_MAPPING)) { CheckJavaInteropLibImport(sd); if (sd.generic) { diag.DiagnoseRefactor(DiagKindRefactor::sema_cjmapping_struct_generic_not_supported, MakeRange(sd.generic->leftAnglePos, sd.generic->rightAnglePos), std::string{sd.identifier}); } if (!sd.inheritedTypes.empty()) { diag.DiagnoseRefactor( DiagKindRefactor::sema_cjmapping_struct_inheritance_interface_not_supported, MakeRange(sd.identifier)); } } } void TypeChecker::TypeCheckerImpl::GetRevTypeMapping( std::vector>& params, std::vector>& args, MultiTypeSubst& revTyMap) { if (args.size() != params.size()) { return; } for (size_t i = 0; i < args.size(); ++i) { if (args[i] == nullptr) { continue; } else if (args[i]->kind == TypeKind::TYPE_GENERICS) { revTyMap[RawStaticCast(args[i])].emplace(params[i]); } else if (auto decl = Ty::GetDeclPtrOfTy(args[i]); decl && Ty::IsTyCorrect(decl->ty)) { GetRevTypeMapping(decl->ty->typeArgs, args[i]->typeArgs, revTyMap); } } } TypeSubst TypeChecker::TypeCheckerImpl::GetGenericTysToInstTysMapping(Ty& genericTy, Ty& instTy) const { TypeSubst map; if (genericTy.typeArgs.size() > instTy.typeArgs.size()) { return map; } for (size_t i = 0; i < genericTy.typeArgs.size(); ++i) { map.emplace(StaticCast(genericTy.typeArgs[i]), instTy.typeArgs[i]); } return map; } void TypeChecker::TypeCheckerImpl::GetAllAssumptions(TyVarUB& source, TyVarUB& newMap) { for (const auto& kv : std::as_const(source)) { if (newMap.find(kv.first) != newMap.cend()) { continue; } else { newMap.emplace(kv.first, kv.second); } for (const auto ty : kv.second) { auto decl = Ty::GetDeclPtrOfTy(ty); if (!decl) { continue; } auto genericDecl = decl->GetGeneric(); if (!genericDecl) { continue; } auto n = genericDecl->assumptionCollection; GetAllAssumptions(n, newMap); } } } void TypeChecker::TypeCheckerImpl::CheckTypeAliasAccess(const TypeAliasDecl& tad) { if (tad.TestAttr(Attribute::PRIVATE)) { return; } std::vector> typeArgs{}; GetTypeArgsOfType(tad.type.get(), typeArgs); const AccessLevel tadLevel = GetAccessLevel(tad); const std::string tadLevelStr = GetAccessLevelStr(tad); for (auto type : typeArgs) { if (type->astKind != ASTKind::REF_TYPE) { continue; } auto rt = StaticAs(type); Ptr decl = rt->ref.target; if (!decl) { continue; } if (decl->astKind != ASTKind::GENERIC_PARAM_DECL && !IsCompatibleAccessLevel(tadLevel, GetAccessLevel(*decl))) { (void)diag.Diagnose(tad, DiagKind::sema_typealias_external_refer_internal, tadLevelStr, tad.identifier.Val(), GetAccessLevelStr(*decl), decl->identifier.Val()); } } } namespace { std::unordered_set> GetTyArgsRecursive(Ty& ty) { std::unordered_set> tyArgSet; if (!Ty::IsTyCorrect(&ty)) { return tyArgSet; } for (auto& arg : ty.typeArgs) { tyArgSet.insert(arg); tyArgSet.merge(GetTyArgsRecursive(*arg)); } return tyArgSet; } } // namespace std::vector> TypeChecker::TypeCheckerImpl::GetUnusedTysInTypeAlias(const TypeAliasDecl& tad) const { std::vector> diffs; if (!tad.generic || !tad.type || Ty::IsInitialTy(tad.type->ty)) { return diffs; } std::vector> declTys; // Use vector to keep defined order. for (auto& param : tad.generic->typeParameters) { declTys.emplace_back(param->ty); } auto usedTys = GetTyArgsRecursive(*tad.type->ty); for (auto ty : declTys) { if (usedTys.find(ty) == usedTys.end()) { diffs.emplace_back(ty); } } return diffs; } void TypeChecker::TypeCheckerImpl::CheckTypeAlias(ASTContext& ctx, TypeAliasDecl& tad) { if (tad.TestAttr(Attribute::IS_CHECK_VISITED)) { return; } // Mark the current declaration is checked. tad.EnableAttr(Attribute::IS_CHECK_VISITED); if (!tad.type || !Ty::IsTyCorrect(tad.type->ty)) { return; } CheckTypeAliasAccess(tad); // Check type parameters which are not used if (tad.generic) { std::vector> diffs = GetUnusedTysInTypeAlias(tad); if (!diffs.empty()) { diag.Diagnose(tad, DiagKind::typealias_unused_type_parameters, Ty::GetTypesToStr(diffs, ",")); } } // NOTE: for incremental compile, the type may be marked as 'IS_CHECK_VISITED'. if (!tad.type->TestAttr(Attribute::IS_CHECK_VISITED)) { CheckReferenceTypeLegality(ctx, *tad.type); } } Ptr TypeChecker::TypeCheckerImpl::SynFuncParam(ASTContext& ctx, FuncParam& fp) { if (!fp.type && !Ty::IsTyCorrect(fp.ty)) { return TypeManager::GetInvalidTy(); } if (fp.type) { (void)Synthesize(ctx, fp.type.get()); if (!Ty::IsTyCorrect(fp.type->ty)) { (void)Synthesize(ctx, fp.assignment.get()); // If fp has assignment, synthesize to report error. return TypeManager::GetInvalidTy(); } fp.ty = fp.type->ty; } if (fp.assignment) { if (!Check(ctx, fp.ty, fp.assignment.get())) { return TypeManager::GetInvalidTy(); } (void)Synthesize(ctx, fp.desugarDecl.get()); } return fp.ty; } bool TypeChecker::TypeCheckerImpl::ChkFuncParam(ASTContext& ctx, Ty& target, FuncParam& fp) { if (fp.ty && fp.ty == &target) { return true; } if (fp.type) { (void)Synthesize(ctx, fp.type.get()); if (!Ty::IsTyCorrect(fp.type->ty)) { return false; } fp.ty = fp.type->ty; // Check type compatibility. if (!typeManager.IsSubtype(&target, fp.type->ty)) { DiagMismatchedTypes(diag, fp, target); return false; } if (fp.assignment) { if (!Check(ctx, fp.ty, fp.assignment.get())) { return false; } (void)Synthesize(ctx, fp.desugarDecl.get()); } } else { fp.ty = ⌖ } return true; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr.cpp000066400000000000000000000422661510705540100217170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements typecheck apis for exprs. */ #include "TypeCheckerImpl.h" #include "TypeCheckUtil.h" using namespace Cangjie; using namespace AST; using namespace TypeCheckUtil; namespace { bool IsLocalDeclOutOfFuncBody(const Decl& decl, const FuncBody& curFuncBody) { // Return false if the decl is member variable. if (decl.outerDecl && decl.outerDecl->IsNominalDecl()) { return false; } // Return true if the decl is not toplevel (which scope level is 0) and not in funcBody. return decl.scopeLevel > 0 && decl.scopeLevel < curFuncBody.scopeLevel; } } // namespace void TypeChecker::TypeCheckerImpl::UpdateAnyTy() { auto anyDecl = importManager.GetCoreDecl("Any"); if (anyDecl && Ty::IsTyCorrect(anyDecl->ty)) { typeManager.SetSemaAnyTy(anyDecl->ty); } } void TypeChecker::TypeCheckerImpl::UpdateCTypeTy() { if (!ci->invocation.globalOptions.implicitPrelude) { return; } auto ctypeDecl = importManager.GetCoreDecl(CTYPE_NAME); if (ctypeDecl && Ty::IsTyCorrect(ctypeDecl->ty)) { typeManager.SetSemaCTypeTy(ctypeDecl->ty); } } bool TypeChecker::TypeCheckerImpl::IsLegalAccessFromStaticFunc( const ASTContext& ctx, const RefExpr& re, const Decl& decl) { auto curFuncBody = GetCurFuncBody(ctx, re.scopeName); // First make sure we are in a static function, and target is not constructor function. if (!curFuncBody || !curFuncBody->TestAttr(AST::Attribute::STATIC) || decl.TestAttr(AST::Attribute::STATIC) || IsClassOrEnumConstructor(decl)) { return true; } // Check if the reference is a non-static member of current structure declaration. Symbol* symOfCurStruct = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, re.scopeName); CJC_NULLPTR_CHECK(symOfCurStruct); CJC_NULLPTR_CHECK(re.curFile); std::vector> decls; if (auto typeDecl = Ty::GetDeclPtrOfTy(symOfCurStruct->node->ty)) { decls = FieldLookup(ctx, typeDecl, decl.identifier, {.baseTy = typeDecl->ty, .file = re.curFile}); } else { decls = ExtendFieldLookup(ctx, *re.curFile, symOfCurStruct->node->ty, decl.identifier); } auto diagForInvalidAccess = [this](const RefExpr& re, const Decl& decl, const FuncBody& curFuncBody) { if (curFuncBody.funcDecl) { std::string identifier = curFuncBody.funcDecl->TestAttr(AST::Attribute::COMPILER_ADD) && curFuncBody.funcDecl->ownerFunc ? curFuncBody.funcDecl->ownerFunc->identifier : curFuncBody.funcDecl->identifier; if (curFuncBody.funcDecl->propDecl) { // set or get in property std::string propDeclName = "*et"; identifier = curFuncBody.funcDecl->identifier.Val().substr(identifier.size() - propDeclName.size()); } diag.Diagnose( re, DiagKind::sema_static_function_cannot_access_non_static_member, decl.identifier.Val(), identifier); } else { diag.Diagnose(re, DiagKind::sema_static_lambdaExpr_cannot_access_non_static, decl.identifier.Val()); } }; for (auto it : decls) { if (it == &decl) { // This function guarantees the curFuncBody is not nullptr. diagForInvalidAccess(re, decl, *curFuncBody); return false; } } return true; } void TypeChecker::TypeCheckerImpl::SetCaptureKind( const ASTContext& ctx, const NameReferenceExpr& nre, FuncBody& curFuncBody) const { auto decl = nre.GetTarget(); CJC_NULLPTR_CHECK(decl); auto targetFB = GetCurFuncBody(ctx, decl->scopeName); bool isLocal = !decl->TestAttr(Attribute::GLOBAL) && !decl->outerDecl; // Not gloabl and not member variable. if (targetFB != nullptr || isLocal) { // Capture a decl in funcBody. if (targetFB == &curFuncBody) { return; } if (auto varDecl = DynamicCast(decl); varDecl) { varDecl->EnableAttr(AST::Attribute::IS_CAPTURE); if (varDecl->isVar) { curFuncBody.capturedVars.emplace(&nre); curFuncBody.captureKind = CaptureKind::CAPTURE_VAR; } } else if (Is(decl)) { decl->EnableAttr(AST::Attribute::IS_CAPTURE); } return; } // Capture a decl in toplevel block. if (IsLocalDeclOutOfFuncBody(*decl, curFuncBody)) { if (auto varDecl = DynamicCast(decl); varDecl) { if (varDecl->isVar) { curFuncBody.captureKind = CaptureKind::CAPTURE_VAR; } if (!varDecl->TestAttr(AST::Attribute::GLOBAL) && !varDecl->TestAttr(AST::Attribute::IS_CAPTURE) && varDecl->outerDecl == nullptr) { varDecl->EnableAttr(AST::Attribute::IS_CAPTURE); } } else if (Is(decl) && !decl->TestAttr(AST::Attribute::GLOBAL) && decl->outerDecl == nullptr) { decl->EnableAttr(AST::Attribute::IS_CAPTURE); } } } void TypeChecker::TypeCheckerImpl::CanTargetOfRefBeCaptured( const ASTContext& ctx, const NameReferenceExpr& nre, const Decl& decl, const FuncBody& curFuncBody) const { CanTargetOfRefBeCapturedCaseNominalDecl(ctx, nre, decl, curFuncBody); CanTargetOfRefBeCapturedCaseMutFunc(ctx, nre, decl, curFuncBody); } void TypeChecker::TypeCheckerImpl::CanTargetOfRefBeCapturedCaseNominalDecl( const ASTContext& ctx, const NameReferenceExpr& nre, const Decl& decl, const FuncBody& curFuncBody) const { auto funcSrc = ScopeManager::GetOutMostSymbol(ctx, SymbolKind::FUNC, nre.scopeName); if (!funcSrc) { return; } CJC_NULLPTR_CHECK(funcSrc->node); auto fd = StaticCast(funcSrc->node); if (GetCurFuncBody(ctx, decl.scopeName)) { return; } // Member variables cannot be accessed in nested function/lambda of struct constructor. if (!curFuncBody.funcDecl || !curFuncBody.funcDecl->TestAttr(Attribute::CONSTRUCTOR)) { if (decl.TestAnyAttr(Attribute::CONSTRUCTOR, Attribute::STATIC) || !fd->TestAttr(Attribute::CONSTRUCTOR)) { return; } CJC_NULLPTR_CHECK(fd->outerDecl); bool needCheck = decl.TestAttr(Attribute::IN_STRUCT) || (fd->outerDecl->astKind == ASTKind::CLASS_DECL && (fd->outerDecl->TestAnyAttr(Attribute::ABSTRACT, Attribute::OPEN))); if (needCheck && fd->outerDecl == decl.outerDecl) { diag.Diagnose(nre, DiagKind::sema_illegal_capture_this, decl.TestAttr(Attribute::IN_STRUCT) ? "struct" : "inheritable class"); } } } void TypeChecker::TypeCheckerImpl::CanTargetOfRefBeCapturedCaseMutFunc( const ASTContext& ctx, const NameReferenceExpr& nre, const Decl& decl, const FuncBody& curFuncBody) const { auto funcSrc = ScopeManager::GetOutMostSymbol(ctx, SymbolKind::FUNC, nre.scopeName); if (funcSrc && Is(funcSrc->node)) { auto fd = RawStaticCast(funcSrc->node); // the fields of the instance cannot be captured by lambda or internal function in a mut function. if (fd->TestAttr(AST::Attribute::MUT) && curFuncBody.funcDecl != fd && decl.TestAttr(AST::Attribute::IN_STRUCT) && decl.astKind == ASTKind::VAR_DECL) { diag.Diagnose(nre, DiagKind::sema_capture_this_or_instance_field_in_func, decl.identifier.Val(), "mutable function '" + fd->identifier + "'"); } if (fd->IsFinalizer() && decl.TestAnyAttr(AST::Attribute::IN_CLASSLIKE, AST::Attribute::IN_EXTEND) && !decl.TestAttr(AST::Attribute::CONSTRUCTOR) && (decl.astKind == ASTKind::FUNC_DECL || decl.astKind == ASTKind::PROP_DECL) && !decl.TestAttr(AST::Attribute::STATIC)) { diag.Diagnose( nre, DiagKind::sema_capture_this_or_instance_field_in_func, decl.identifier.Val(), "finalizer"); } } } // Immutable function cannot access mutable function. void TypeChecker::TypeCheckerImpl::CheckImmutableFuncAccessMutableFunc( const Position& pos, const Node& srcNode, const Decl& destNode, bool isLeftStructValue) const { // Only need to check mutable accessing for struct 's var property. bool accessMutableTarget = (destNode.astKind == ASTKind::FUNC_DECL && destNode.TestAttr(AST::Attribute::MUT)) || (destNode.astKind == ASTKind::PROP_DECL && isLeftStructValue && static_cast(destNode).isVar); bool bothInstance = !srcNode.TestAttr(AST::Attribute::STATIC) && !destNode.TestAttr(AST::Attribute::STATIC); if (auto fdSrc = DynamicCast(&srcNode); fdSrc && !fdSrc->TestAttr(AST::Attribute::MUT) && fdSrc->outerDecl != nullptr && bothInstance && !fdSrc->TestAttr(AST::Attribute::CONSTRUCTOR) && !fdSrc->TestAttr(AST::Attribute::PRIMARY_CONSTRUCTOR) && accessMutableTarget) { std::string srcName = fdSrc->isGetter ? "get" : fdSrc->identifier.Val(); std::string dstName = destNode.astKind == ASTKind::PROP_DECL ? "set" : destNode.identifier.Val(); diag.Diagnose(srcNode, pos, DiagKind::sema_immutable_function_cannot_access_mutable_function, srcName, dstName); } } void TypeChecker::TypeCheckerImpl::CheckForbiddenFuncReferenceAccess( const Position& pos, const FuncDecl& fd, const Decl& decl) const { if (!fd.outerDecl || !decl.outerDecl || !decl.IsFuncOrProp() || decl.TestAnyAttr(Attribute::CONSTRUCTOR, Attribute::STATIC)) { return; // Only check for instance function and property, except constructor and static member. } // Calling any member function or property in constructor of an inheritable class is forbidden. auto inOpenClassCtor = fd.outerDecl->astKind == ASTKind::CLASS_DECL && fd.TestAttr(AST::Attribute::CONSTRUCTOR) && fd.outerDecl->TestAnyAttr(AST::Attribute::OPEN, AST::Attribute::ABSTRACT); bool useMemberInCtor = inOpenClassCtor && decl.TestAnyAttr(AST::Attribute::IN_CLASSLIKE, AST::Attribute::IN_EXTEND); if (useMemberInCtor) { diag.Diagnose(fd, pos, DiagKind::sema_illegal_member_used_in_open_constructor, DeclKindToString(decl), decl.identifier.Val(), fd.outerDecl->identifier.Val()); } // Finalizer is only allowed in class. // spec rule: this.xx, super.xx or current member (function or property) is forbidden in class finalizer. if (fd.IsFinalizer() && typeManager.IsSubtype(fd.outerDecl->ty, decl.outerDecl->ty)) { std::string type = decl.astKind == ASTKind::PROP_DECL ? "property" : "function"; diag.DiagnoseRefactor(DiagKindRefactor::sema_instance_func_cannot_be_used_in_finalizer, pos, type); } } void TypeChecker::TypeCheckerImpl::MarkAndCheckRefExprVarCaptureStatus( const ASTContext& ctx, const NameReferenceExpr& nre) const { auto target = nre.GetTarget(); if (!target || target->IsTypeDecl()) { return; } auto curFuncBody = GetCurFuncBody(ctx, nre.scopeName); if (!curFuncBody) { return; } // Global or static variable decl is not treated as capture. bool canBeCaptured = !target->TestAnyAttr(Attribute::STATIC, Attribute::GLOBAL); if (canBeCaptured) { CanTargetOfRefBeCaptured(ctx, nre, *target, *curFuncBody); SetCaptureKind(ctx, nre, *curFuncBody); } if (nre.astKind == ASTKind::REF_EXPR) { CheckWarningOfCaptureVariable(ctx, StaticCast(nre)); } } Ptr TypeChecker::TypeCheckerImpl::GetRealTarget(Ptr const node, Ptr const target) { std::vector> targets = {target}; HandleAlias(node, targets); return targets[0]; } void TypeChecker::TypeCheckerImpl::SubstituteTypeForTypeAliasTypeMapping( const TypeAliasDecl& tad, const std::vector>& typeArgs, TypeSubst& typeMapping) const { if (!tad.generic || tad.generic->typeParameters.size() != typeArgs.size()) { return; } auto argsNum = tad.generic->typeParameters.size(); for (auto& it : typeMapping) { for (size_t i = 0; i < argsNum; ++i) { if (it.second == tad.generic->typeParameters[i]->ty) { it.second = typeArgs[i]; break; } } } } /** * Typealias's may be used recursively, so generate a typeMapping from used typealias decl to inner most typealias. * type A = T0*T0 * type A1 = A * Generate T0->T1. * type B = A * type C = B * Generate T0->Rune for B and C. */ TypeSubst TypeChecker::TypeCheckerImpl::GenerateTypeMappingForTypeAliasDecl(const TypeAliasDecl& tad) const { std::unordered_set> visited; return GenerateTypeMappingForTypeAliasDeclVisit(tad, visited); } TypeSubst TypeChecker::TypeCheckerImpl::GenerateTypeMappingForTypeAliasDeclVisit( const TypeAliasDecl& tad, std::unordered_set>& visited) const { TypeSubst typeMapping; if (!tad.type) { return typeMapping; } if (visited.count(&tad) > 0) { return typeMapping; } else { visited.emplace(&tad); } auto target = tad.type->GetTarget(); if (!target || !Ty::IsTyCorrect(tad.type->ty)) { return typeMapping; } if (target->astKind != ASTKind::TYPE_ALIAS_DECL) { // For target which is not typealias decl, generate typeMapping from used genericTy to itself. for (auto ty : tad.type->ty->typeArgs) { if (ty->IsGeneric()) { typeMapping[StaticCast(ty)] = ty; } } return typeMapping; } auto targetTad = RawStaticCast(target); typeMapping = GenerateTypeMappingForTypeAliasDeclVisit(*targetTad, visited); // Get used typeArguments of current typealias decl. // eg. have 'type A1 = Type' & 'type B1 = A1' // current tad is B1, target is A1. We need to collect X here. // or have 'type A2 = Type' & 'type B2 = A2' // current tad is B2, target is A1. We need to collect 'Int64 & X' here. std::vector> typeArgs; for (auto& it : tad.type->GetTypeArgs()) { typeArgs.push_back(it->ty); } SubstituteTypeForTypeAliasTypeMapping(*targetTad, typeArgs, typeMapping); return typeMapping; } void TypeChecker::TypeCheckerImpl::HandleAlias(Ptr expr, std::vector>& targets) { for (auto& target : targets) { if (!target || target->astKind != ASTKind::TYPE_ALIAS_DECL) { continue; } auto aliasDecl = StaticCast(target); if (aliasDecl->type == nullptr) { continue; } auto innerTypeAliasTarget = GetLastTypeAliasTarget(*aliasDecl); if (auto realTarget = innerTypeAliasTarget->type->GetTarget(); realTarget) { target = realTarget; if (auto ref = DynamicCast(expr)) { auto wasEmpty = ref->typeArguments.empty(); auto typeMapping = GenerateTypeMappingForTypeAliasUse(*aliasDecl, *ref); SubstituteTypeArguments(*innerTypeAliasTarget, ref->typeArguments, typeMapping); // Try to insert new typeArguments to ref's instTys. UpdateInstTysWithTypeArgs(*ref); if (wasEmpty && !ref->typeArguments.empty()) { ref->compilerAddedTyArgs = true; } } } } } void TypeChecker::TypeCheckerImpl::CheckWarningOfCaptureVariable(const ASTContext& ctx, const RefExpr& re) const { auto target = re.GetTarget(); if (!Is(target) || Is(target)) { return; } auto funcSym = ScopeManager::GetCurSymbolByKind(SymbolKind::FUNC_LIKE, ctx, re.scopeName); while (target != nullptr && funcSym != nullptr && funcSym->scopeLevel > target->scopeLevel) { auto funcScope = funcSym->scopeName.substr(0, funcSym->scopeName.find('_')); // If has another a same name decl in inter scope, should report a warning. auto decls = ctx.GetDeclsByName({target->identifier, funcScope}); for (auto decl : decls) { if (!Is(decl) || decl->identifier != target->identifier) { continue; } diag.Diagnose( re, DiagKind::sema_capture_has_shadow_variable, target->identifier.Val(), target->begin, decl->begin); return; } funcSym = ScopeManager::GetCurSymbolByKind(SymbolKind::FUNC_LIKE, ctx, funcSym->scopeName); } } bool TypeChecker::TypeCheckerImpl::CheckOptionBox(Ty& target, Ty& ty) { if (typeManager.IsSubtype(&ty, &target)) { return true; } if (!Ty::IsTyCorrect(&target) || !target.IsCoreOptionType()) { return false; } if (typeManager.IsTyEqual(&ty, &target)) { return true; } auto curTarget = ⌖ while (Ty::IsTyCorrect(curTarget) && curTarget->IsCoreOptionType()) { CJC_ASSERT(curTarget->typeArgs.size() == 1); curTarget = curTarget->typeArgs[0]; if (typeManager.IsTyEqual(&ty, curTarget)) { return true; } } return false; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/000077500000000000000000000000001510705540100212015ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/AssignExpr.cpp000066400000000000000000000473561510705540100240070ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "BuiltInOperatorUtil.h" #include "Desugar/DesugarInTypeCheck.h" #include "DiagSuppressor.h" #include "Diags.h" #include "TypeCheckUtil.h" #include "cangjie/AST/RecoverDesugar.h" using namespace Cangjie; using namespace Sema; using namespace TypeCheckUtil; namespace { const std::vector SHIFT_ASSIGN_OPERATOR = {TokenKind::LSHIFT_ASSIGN, TokenKind::RSHIFT_ASSIGN}; /** * This table encodes the types of the operator and result, for every assignment operator, where key is assignment * operator kind, and value is supported type for corresponding kind of assignment. */ #define ASSIGNABLE_INTEGER_TYPE \ TypeKind::TYPE_INT8, TypeKind::TYPE_INT16, TypeKind::TYPE_INT32, TypeKind::TYPE_INT64, TypeKind::TYPE_INT_NATIVE, \ TypeKind::TYPE_UINT8, TypeKind::TYPE_UINT16, TypeKind::TYPE_UINT32, TypeKind::TYPE_UINT64, \ TypeKind::TYPE_UINT_NATIVE, TypeKind::TYPE_IDEAL_INT #define ASSIGNABLE_NUMERIC_TYPE \ ASSIGNABLE_INTEGER_TYPE, TypeKind::TYPE_FLOAT16, TypeKind::TYPE_FLOAT32, TypeKind::TYPE_FLOAT64, \ TypeKind::TYPE_IDEAL_FLOAT const std::map> COMPOUND_ASSIGN_TYPE_MAP = { {TokenKind::ADD_ASSIGN, {ASSIGNABLE_NUMERIC_TYPE}}, {TokenKind::SUB_ASSIGN, {ASSIGNABLE_NUMERIC_TYPE}}, {TokenKind::MUL_ASSIGN, {ASSIGNABLE_NUMERIC_TYPE}}, {TokenKind::EXP_ASSIGN, {ASSIGNABLE_NUMERIC_TYPE}}, {TokenKind::DIV_ASSIGN, {ASSIGNABLE_NUMERIC_TYPE}}, {TokenKind::MOD_ASSIGN, {ASSIGNABLE_INTEGER_TYPE}}, {TokenKind::AND_ASSIGN, {TypeKind::TYPE_BOOLEAN}}, {TokenKind::OR_ASSIGN, {TypeKind::TYPE_BOOLEAN}}, {TokenKind::BITAND_ASSIGN, {ASSIGNABLE_INTEGER_TYPE}}, {TokenKind::BITOR_ASSIGN, {ASSIGNABLE_INTEGER_TYPE}}, {TokenKind::BITXOR_ASSIGN, {ASSIGNABLE_INTEGER_TYPE}}, {TokenKind::LSHIFT_ASSIGN, {ASSIGNABLE_INTEGER_TYPE}}, {TokenKind::RSHIFT_ASSIGN, {ASSIGNABLE_INTEGER_TYPE}}}; void DiagCannotAssignToSubscript(DiagnosticEngine& diag, const SubscriptExpr& se, const Expr& rightValue) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_cannot_assign_to_subscript, se); CJC_ASSERT(se.baseExpr && Ty::IsTyCorrect(se.baseExpr->ty)); if (se.baseExpr->ty->IsExtendable()) { std::string indexParams; for (size_t i = 0; i < se.indexExprs.size(); ++i) { CJC_ASSERT(se.indexExprs[i] && Ty::IsTyCorrect(se.indexExprs[i]->ty)); indexParams += "index" + std::to_string(i) + ": " + se.indexExprs[i]->ty->String() + ", "; } builder.AddNote("you may want to implement 'operator func[](" + indexParams + "value!: " + rightValue.ty->String() + ")' for type '" + se.baseExpr->ty->String() + "'"); } } bool MaySubscriptAssignOnlyBeOverload(const Expr& baseExpr) { return Ty::IsTyCorrect(baseExpr.ty) && !baseExpr.ty->IsBuiltin() && !baseExpr.ty->IsRange() && !baseExpr.ty->IsTuple(); } // Check whether ScriptExpr can be modified and diagnose for immutable subscript expression. bool IsAssignableSubScriptExpr( SubscriptExpr& se, bool isCompound, const std::vector& diags, DiagnosticEngine& diag) { bool isOperatorOverload = se.desugarExpr != nullptr; if (se.desugarExpr) { RecoverToSubscriptExpr(se); } if (se.isTupleAccess) { (void)std::for_each(diags.begin(), diags.end(), [&diag](auto info) { (void)diag.Diagnose(info); }); DiagInvalidAssign(diag, se, "tuple element"); return false; } CJC_ASSERT(se.baseExpr && !se.indexExprs.empty()); DiagKind diagKind = isCompound ? DiagKind::sema_subscript_get_set_not_supported : DiagKind::sema_subscript_set_not_supported; auto indexTy = TypeManager::GetNonNullTy(se.indexExprs[0]->ty); auto baseTy = TypeManager::GetNonNullTy(se.baseExpr->ty); if ((!baseTy->IsArray() && !Is(baseTy)) || (isOperatorOverload && !indexTy->IsInteger())) { (void)std::for_each(diags.begin(), diags.end(), [&diag](auto info) { (void)diag.Diagnose(info); }); (void)diag.Diagnose(se, diagKind, baseTy->String(), indexTy->String()); return false; } return true; } bool CheckMatchOfDimensionAndTypes(DiagnosticEngine& diag, TypeManager& typeManager, std::pair>&, uint64_t&> tysOfExprAndItsIndex, const std::pair& tupleLitAndTy, const Expr& rightExpr) { auto [tupleLit, tupleTy] = tupleLitAndTy; auto& [tysOfExpr, indexOfExprTys] = tysOfExprAndItsIndex; if (tupleLit.children.size() != tupleTy.typeArgs.size()) { DiagInvalidMultipleAssignExpr(diag, tupleLit, rightExpr, "the tuple has " + std::to_string(tupleLit.children.size()) + "-elements, found one with " + std::to_string(tupleTy.typeArgs.size()) + "-elements"); return false; } uint64_t indexOfRightTy = 0; for (auto& leftValue : tupleLit.children) { CJC_NULLPTR_CHECK(leftValue); auto rightTy = tupleTy.typeArgs[indexOfRightTy]; CJC_NULLPTR_CHECK(rightTy); indexOfRightTy++; const std::string leftDiagInfo = Ty::IsTyCorrect(leftValue->ty) && !leftValue->IsInvalid() ? ("'" + leftValue->ty->String() + "'") : "it"; if (leftValue->astKind == ASTKind::TUPLE_LIT) { if (!rightTy->IsTuple()) { DiagInvalidMultipleAssignExpr(diag, *leftValue, rightExpr, "can not assign " + leftDiagInfo + " with '" + rightExpr.ty->String() + "'"); return false; } auto tupleLitAndTupleTy = std::pair( *StaticCast(leftValue.get()), *RawStaticCast(rightTy)); bool success = CheckMatchOfDimensionAndTypes(diag, typeManager, tysOfExprAndItsIndex, tupleLitAndTupleTy, rightExpr); if (!success) { return false; } continue; } CJC_ASSERT(indexOfExprTys < tysOfExpr.size()); bool typeMatch = (Ty::IsTyCorrect(leftValue->ty) && typeManager.IsSubtype(rightTy, leftValue->ty)) || Ty::IsTyCorrect(tysOfExpr[indexOfExprTys]); indexOfExprTys++; if (!typeMatch) { DiagInvalidMultipleAssignExpr( diag, *leftValue, rightExpr, "can not assign " + leftDiagInfo + " with '" + rightTy->String() + "'"); return false; } } return true; } void CheckMultipleAssignExpr(DiagnosticEngine& diag, TypeManager& typeManager, AssignExpr& assignExpr) { CJC_ASSERT(assignExpr.leftValue->astKind == ASTKind::TUPLE_LIT); auto& tupleLit = *StaticCast(assignExpr.leftValue.get()); auto& rightExpr = *assignExpr.rightExpr; CJC_ASSERT(Ty::IsTyCorrect(rightExpr.ty)); // Collect tys of desugarExpr(except VarDecl) to help decide whether type match on corresponding single assign // expression. std::vector> tysOfDesugarExpr; // Index of tysOfDesugarExprs, increase when travel a non-tupleLit of leftValue. uint64_t indexOfDesugaredTys = 0; CJC_ASSERT(assignExpr.desugarExpr->astKind == ASTKind::BLOCK); for (auto& node : StaticCast(assignExpr.desugarExpr.get())->body) { if (node->astKind == ASTKind::VAR_DECL) { continue; } (void)tysOfDesugarExpr.emplace_back(node->ty); } if (!rightExpr.ty->IsTuple()) { const std::string leftDiagInfo = Ty::IsTyCorrect(tupleLit.ty) && !tupleLit.IsInvalid() ? ("'" + tupleLit.ty->String() + "'") : "it"; DiagInvalidMultipleAssignExpr( diag, tupleLit, rightExpr, "can not assign " + leftDiagInfo + " with '" + rightExpr.ty->String() + "'"); assignExpr.ty = TypeManager::GetInvalidTy(); return; } auto success = CheckMatchOfDimensionAndTypes(diag, typeManager, {tysOfDesugarExpr, indexOfDesugaredTys}, {tupleLit, *RawStaticCast(rightExpr.ty)}, rightExpr); if (!success) { assignExpr.ty = TypeManager::GetInvalidTy(); } } } // namespace bool TypeChecker::TypeCheckerImpl::IsAssignable(Expr& e, bool isCompound, const std::vector& diags) const { auto getTargetName = [](const Decl& decl) { return decl.TestAttr(AST::Attribute::MACRO_FUNC) ? "macro" : (decl.TestAttr(AST::Attribute::ENUM_CONSTRUCTOR) ? "enum constructor" : "func"); }; if (e.astKind == ASTKind::REF_EXPR) { auto& leftRef = static_cast(e); if (leftRef.ref.target && leftRef.ref.target->astKind == ASTKind::FUNC_DECL) { (void)std::for_each(diags.begin(), diags.end(), [this](auto info) { (void)diag.Diagnose(info); }); DiagInvalidAssign(diag, e, getTargetName(*leftRef.ref.target)); return false; } if (leftRef.isThis && leftRef.isAlone) { (void)std::for_each(diags.begin(), diags.end(), [this](auto info) { (void)diag.Diagnose(info); }); (void)diag.Diagnose(e, DiagKind::sema_invalid_assignment_to_this_expr); return false; } } else if (e.astKind == ASTKind::MEMBER_ACCESS) { auto& ma = static_cast(e); if (ma.target && ma.target->astKind == ASTKind::FUNC_DECL) { (void)std::for_each(diags.begin(), diags.end(), [this](auto info) { (void)diag.Diagnose(info); }); DiagInvalidAssign(diag, e, getTargetName(*ma.target)); return false; } bool isVArraySize = ma.baseExpr && Ty::IsTyCorrect(ma.baseExpr->ty) && Is(ma.baseExpr->ty) && ma.desugarExpr && ma.desugarExpr->astKind == ASTKind::LIT_CONST_EXPR; if (isVArraySize) { (void)std::for_each(diags.begin(), diags.end(), [this](auto info) { (void)diag.Diagnose(info); }); DiagCannotAssignToImmutable(diag, ma, ma); return false; } } else if (e.astKind == ASTKind::SUBSCRIPT_EXPR) { auto& se = static_cast(e); if (!IsAssignableSubScriptExpr(se, isCompound, diags, diag)) { return false; } } return true; } void TypeChecker::TypeCheckerImpl::DiagnoseForSubscriptAssignExpr( ASTContext& ctx, const AssignExpr& ae, std::vector& diags) { CJC_NULLPTR_CHECK(ae.leftValue); CJC_NULLPTR_CHECK(ae.rightExpr); SubscriptExpr& se = StaticCast(*ae.leftValue); auto baseTarget = se.baseExpr->GetTarget(); // Try to report optimized diagnostics. if (Utils::NotIn(diags, [](const Diagnostic& d) { return d.diagSeverity == DiagSeverity::DS_ERROR; }) || baseTarget == nullptr || baseTarget->astKind == ASTKind::VAR_DECL) { // Check whether all the children (`se.indexExprs`, `ae.rightExpr`) are valid. bool areChildrenValid = true; for (auto& indexExpr : se.indexExprs) { CJC_NULLPTR_CHECK(indexExpr); areChildrenValid = Ty::IsTyCorrect(Synthesize(ctx, indexExpr.get())) && ReplaceIdealTy(*indexExpr) && areChildrenValid; } areChildrenValid = Ty::IsTyCorrect(Synthesize(ctx, ae.rightExpr.get())) && ReplaceIdealTy(*ae.rightExpr) && areChildrenValid; if (areChildrenValid) { // If all the children are valid, there must be operator overloading errors. // Firstly, report the warnings in children. // Then report the subscript assignment operator overloading error. DiagCannotAssignToSubscript(diag, se, *ae.rightExpr); } // Otherwise, some children are invalid, and diagnostics should have been reported // by `Synthesize` or `ReplaceIdealTy`. } else { // Report the stashed diagnostics. for (auto& d : diags) { (void)diag.Diagnose(d); } } } std::optional> TypeChecker::TypeCheckerImpl::InferAssignExprCheckCaseOverloading( ASTContext& ctx, AssignExpr& ae, std::vector& diags) { auto ds = DiagSuppressor(diag); if (!ae.desugarExpr && ae.leftValue && ae.leftValue->astKind == ASTKind::SUBSCRIPT_EXPR) { SubscriptExpr& se = StaticCast(*ae.leftValue); DesugarSubscriptOverloadExpr(ctx, ae); if (Ty::IsTyCorrect(Synthesize(ctx, ae.desugarExpr.get()))) { ds.ReportDiag(); CJC_NULLPTR_CHECK(ae.desugarExpr); ae.ty = ae.desugarExpr->ty; CJC_ASSERT(ae.desugarExpr->astKind == ASTKind::CALL_EXPR); ReplaceTarget(&ae, StaticCast(ae.desugarExpr.get())->resolvedFunction); return {ae.ty}; } RecoverToAssignExpr(ae); if (se.baseExpr && MaySubscriptAssignOnlyBeOverload(*se.baseExpr)) { auto stashedDiags = ds.GetSuppressedDiag(); DiagnoseForSubscriptAssignExpr(ctx, ae, stashedDiags); ds.ReportDiag(); return {ae.ty}; } ctx.DeleteDesugarExpr(ae.desugarExpr); } else if (ae.isCompound) { DesugarOperatorOverloadExpr(ctx, ae); if (Ty::IsTyCorrect(Synthesize(ctx, ae.desugarExpr.get()))) { ds.ReportDiag(); ae.ty = ae.desugarExpr->ty; CallExpr& ce = StaticCast(*StaticCast(*ae.desugarExpr).rightExpr); ReplaceTarget(&ae, ce.resolvedFunction); MemberAccess& ma = StaticCast(*ce.baseFunc); CJC_ASSERT(ma.baseExpr && ma.baseExpr->ty && !ce.args.empty() && ce.args.front()->ty); auto iter = COMPOUND_ASSIGN_EXPR_MAP.find(ae.op); CJC_ASSERT(iter != COMPOUND_ASSIGN_EXPR_MAP.cend()); if (IsBuiltinBinaryExpr(iter->second, *ma.baseExpr->ty, *ce.args.front()->ty)) { RecoverToAssignExpr(ae); } return {ae.ty}; } RecoverToAssignExpr(ae); ctx.DeleteDesugarExpr(ae.desugarExpr); } diags = ds.GetSuppressedDiag(); return {}; } bool TypeChecker::TypeCheckerImpl::ChkAssignExpr(ASTContext& ctx, Ty& target, AssignExpr& ae) { if (!Ty::IsTyCorrect(SynAssignExpr(ctx, ae))) { return false; } Ptr unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); if (typeManager.IsSubtype(ae.ty, &target)) { return true; } else { DiagMismatchedTypesWithFoundTy( diag, ae, target, *unitTy, "the type of an assignment expression is always 'Unit'"); ae.ty = TypeManager::GetInvalidTy(); return false; } } Ptr TypeChecker::TypeCheckerImpl::SynMultipleAssignExpr(ASTContext& ctx, AST::AssignExpr& ae) { (void)Synthesize(ctx, ae.desugarExpr.get()); CJC_NULLPTR_CHECK(ae.desugarExpr); ae.ty = ae.desugarExpr->ty; (void)Synthesize(ctx, ae.rightExpr.get()); CJC_NULLPTR_CHECK(ae.rightExpr); if (!Ty::IsTyCorrect(ae.rightExpr->ty)) { return ae.ty; } typeManager.ReplaceIdealTy(&ae.rightExpr->ty); { // Create a scope for DiagSuppressor. auto ds = DiagSuppressor(diag); (void)Synthesize(ctx, ae.leftValue.get()); } CheckMultipleAssignExpr(diag, typeManager, ae); return ae.ty; } Ptr TypeChecker::TypeCheckerImpl::SynAssignExpr(ASTContext& ctx, AssignExpr& ae) { if (ae.desugarExpr) { // It is a multiple assignment expression. if (ae.leftValue && ae.leftValue->astKind == ASTKind::TUPLE_LIT) { return SynMultipleAssignExpr(ctx, ae); } return ae.desugarExpr->ty; } std::vector diagsForOverload; // Check operator overloading for index accessing or compound assignment. if (auto ret = InferAssignExprCheckCaseOverloading(ctx, ae, diagsForOverload)) { return *ret; } CJC_ASSERT(ae.leftValue && ae.rightExpr); ae.ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); if (ae.leftValue->astKind == ASTKind::WILDCARD_EXPR) { if (Ty::IsTyCorrect(Synthesize(ctx, ae.rightExpr.get()))) { typeManager.ReplaceIdealTy(&ae.rightExpr->ty); } else { ae.ty = TypeManager::GetInvalidTy(); } ae.leftValue->ty = ae.rightExpr->ty; return ae.ty; } auto lTy = Synthesize(ctx, ae.leftValue.get()); if (lTy->IsInvalid()) { ae.ty = TypeManager::GetInvalidTy(); return TypeManager::GetInvalidTy(); } lTy = Ty::GetPrimitiveUpperBound(lTy); if (!IsAssignable(*ae.leftValue, ae.isCompound, diagsForOverload)) { return TypeManager::GetInvalidTy(); } // Additional checks for compound assignment expressions: // If the assignment operator is not overloaded, only built-in types support compound assign expressions. if (!PreCheckCompoundAssign(ctx, ae, *lTy, diagsForOverload)) { return TypeManager::GetInvalidTy(); } if (ae.op == TokenKind::EXP_ASSIGN) { // Quick fix. Rules for **= are different from all other compound assignment expressions. if (!CheckExponentByBaseTy(ctx, *lTy, *ae.leftValue, *ae.rightExpr)) { if (ae.ShouldDiagnose()) { (void)diag.Diagnose(ae, DiagKind::sema_type_incompatible, "assignment"); } ae.ty = TypeManager::GetInvalidTy(); } return ae.ty; } if (!ae.isCompound && ae.leftValue->ty->IsRune() && IsSingleRuneStringLiteral(*ae.rightExpr)) { ae.rightExpr->ty = ae.leftValue->ty; } else if (!ae.isCompound && ae.leftValue->ty->kind == TypeKind::TYPE_UINT8 && IsSingleByteStringLiteral(*ae.rightExpr)) { ae.rightExpr->ty = ae.leftValue->ty; ChkLitConstExprRange(StaticCast(*ae.rightExpr)); } else if (!Check(ctx, lTy, ae.rightExpr.get())) { if (ae.ShouldDiagnose() && !CanSkipDiag(*ae.rightExpr)) { (void)diag.Diagnose(ae, DiagKind::sema_type_incompatible, "assignment"); } return TypeManager::GetInvalidTy(); } // If the above check passes, ae.rightExpr and ae.rightExpr->ty must not be null. // Additional checks for shift assignment expressions: negative left value check and simple overflow check. if (!IsShiftAssignValid(ae)) { return TypeManager::GetInvalidTy(); } return ae.ty; } bool TypeChecker::TypeCheckerImpl::IsShiftAssignValid(const AssignExpr& ae) { if (Utils::In(ae.op, SHIFT_ASSIGN_OPERATOR) && ae.rightExpr->astKind == ASTKind::LIT_CONST_EXPR) { if (ae.rightExpr->constNumValue.asInt.IsNegativeNum()) { (void)diag.Diagnose(*ae.rightExpr, DiagKind::sema_negative_shift_count); return false; } if (Ty::IsTyCorrect(ae.leftValue->ty) && ae.leftValue->ty->IsInteger()) { if (ae.rightExpr->constNumValue.asInt.GreaterThanOrEqualBitLen(ae.leftValue->ty->kind)) { (void)diag.Diagnose(*ae.rightExpr, DiagKind::sema_shift_count_overflow); return false; } } } return true; } bool TypeChecker::TypeCheckerImpl::PreCheckCompoundAssign( ASTContext& ctx, const AssignExpr& ae, const Ty& lTy, const std::vector& diags) { if (ae.isCompound) { const auto& typeCandidates = COMPOUND_ASSIGN_TYPE_MAP.at(ae.op); if (Ty::IsInitialTy(&lTy) || !Utils::In(lTy.kind, typeCandidates)) { (void)Synthesize(ctx, ae.rightExpr.get()); if (ae.ShouldDiagnose()) { (void)std::for_each(diags.begin(), diags.end(), [this](auto info) { (void)diag.Diagnose(info); }); (void)diag.Diagnose(ae, DiagKind::sema_type_incompatible, "compound assignment expression"); } return false; } } return true; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/BinaryExpr.cpp000066400000000000000000001361521510705540100240000ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include #include "BuiltInOperatorUtil.h" #include "Desugar/DesugarInTypeCheck.h" #include "DiagSuppressor.h" #include "Diags.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/RecoverDesugar.h" using namespace Cangjie; using namespace AST; using namespace Sema; using namespace TypeCheckUtil; namespace { const std::vector ARITHMETIC_OPERATOR = {TokenKind::ADD, TokenKind::SUB, TokenKind::MUL, TokenKind::DIV, TokenKind::EXP, TokenKind::MOD, TokenKind::BITAND, TokenKind::BITOR, TokenKind::BITXOR}; const std::vector RELATIONAL_OPERATOR = { TokenKind::EQUAL, TokenKind::NOTEQ, TokenKind::GT, TokenKind::LT, TokenKind::GE, TokenKind::LE}; const std::vector LOGICAL_OPERATOR = {TokenKind::AND, TokenKind::OR}; const std::vector SHIFT_OPERATOR = {TokenKind::LSHIFT, TokenKind::RSHIFT}; const std::vector FLOW_OPERATOR = {TokenKind::PIPELINE, TokenKind::COMPOSITION}; Ptr GetChildBinaryExpr(Expr& child) { // If a `BinaryExpr` has `desugarExpr`, it is guaranteed to be correct and cannot be the pivot. if (child.astKind == ASTKind::BINARY_EXPR && child.desugarExpr == nullptr) { return StaticAs(&child); } if (child.astKind == ASTKind::PAREN_EXPR) { ParenExpr& pe = *StaticAs(&child); CJC_NULLPTR_CHECK(pe.expr); if (pe.expr->astKind == ASTKind::BINARY_EXPR && pe.expr->desugarExpr == nullptr) { return StaticAs(pe.expr.get()); } } return nullptr; } // This function is used to check whether binaryExpr contains a invalid child node which cannot be inferred. bool HasInvalidChild(BinaryExpr& be) { bool hasInvalid = false; auto id = Walker::GetNextWalkerID(); std::function)> containsInvalid = [id, &containsInvalid, &hasInvalid](auto node) { CJC_ASSERT(node); if (auto expr = DynamicCast(node); expr && expr->desugarExpr) { Walker(expr, id, containsInvalid).Walk(); return VisitAction::SKIP_CHILDREN; } if (node->TestAttr(AST::Attribute::IS_BROKEN) || node->TestAttr(AST::Attribute::HAS_BROKEN)) { hasInvalid = true; } else if (auto expr = DynamicCast(node); expr && expr->IsReferenceExpr() && !Ty::IsInitialTy(expr->ty)) { // If there exists node 'a.b' or 'a' which has been synthesized and type of expression is invalid, // the binaryExpr will definitely failed the typeCheck, so the overload checking can be skipped. hasInvalid = !expr->GetTarget() && !Ty::IsTyCorrect(expr->ty); return hasInvalid ? VisitAction::STOP_NOW : VisitAction::SKIP_CHILDREN; } return hasInvalid ? VisitAction::STOP_NOW : VisitAction::WALK_CHILDREN; }; Walker(&be, id, containsInvalid).Walk(); return hasInvalid; } bool RefExprTargetIsFuncDeclAndHasNamedParam(const RefExpr& re) { if (!re.ref.target || re.ref.target->astKind != ASTKind::FUNC_DECL) { return false; } const FuncDecl& funcDecl = *StaticCast(re.ref.target); CJC_NULLPTR_CHECK(funcDecl.funcBody); auto& params = funcDecl.funcBody->paramLists[0]->params; return std::any_of(params.cbegin(), params.cend(), [](const OwnedPtr& param) { return param && param->isNamedParam; }); } void MarkOutermostBinaryExpr(bool setOutermost, Node& node) { Walker(&node, [&setOutermost](auto n) { if (n->astKind == ASTKind::BINARY_EXPR) { if (setOutermost) { n->EnableAttr(Attribute::IS_OUTERMOST); } auto& be = StaticCast(*n); if (be.leftExpr) { MarkOutermostBinaryExpr(false, *be.leftExpr); } if (be.rightExpr) { MarkOutermostBinaryExpr(false, *be.rightExpr); } setOutermost = true; return VisitAction::SKIP_CHILDREN; } else if (n->astKind == ASTKind::PAREN_EXPR) { // Parentheses are considered as part of a binary expression. auto& pe = StaticCast(*n); if (pe.expr) { MarkOutermostBinaryExpr(setOutermost, *pe.expr); } return VisitAction::SKIP_CHILDREN; } else { // For binary expressions nested in other expressions, i.e., `1 + if (a < b) { 0 } else { 1 }`, // `a < b` is also outermost. setOutermost = true; } return VisitAction::WALK_CHILDREN; }).Walk(); } inline bool HasSideEffect(const Expr& expr) { return Utils::NotIn(expr.astKind, {ASTKind::TUPLE_LIT, ASTKind::REF_EXPR}); } // Get the element accesses of a tuple. // For tuple literal, copy the elements. // For other tuple type expression, create subscript expressions to access the elements. std::vector> GetTupleElements(Expr& expr) { CJC_ASSERT(Ty::IsTyCorrect(expr.ty) && expr.ty->IsTuple()); auto int64Ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64); std::vector> tupleElements; if (expr.astKind == ASTKind::TUPLE_LIT) { // Get children of a tuple literal. for (OwnedPtr& child : static_cast(expr).children) { tupleElements.push_back(ASTCloner::Clone(child.get())); } } else { const bool sideEffect = HasSideEffect(expr); Ptr mapExpr = nullptr; // Create TupleAccess node to get children. for (size_t i = 0; i < expr.ty->typeArgs.size(); i++) { auto tupleAccessExpr = MakeOwnedNode(); tupleAccessExpr->isTupleAccess = true; auto clonedExpr = ASTCloner::Clone(Ptr(&expr)); if (sideEffect) { if (!mapExpr) { mapExpr = clonedExpr.get(); } clonedExpr->mapExpr = mapExpr; } tupleAccessExpr->baseExpr = std::move(clonedExpr); tupleAccessExpr->indexExprs.push_back( CreateLitConstExpr(LitConstKind::INTEGER, std::to_string(i), int64Ty)); tupleAccessExpr->indexExprs[0]->ty = int64Ty; tupleAccessExpr->ty = expr.ty->typeArgs[i]; tupleAccessExpr->curFile = expr.curFile; tupleElements.push_back(std::move(tupleAccessExpr)); } } return tupleElements; } std::pair GetDesugaredBinaryExprArgTys(const BinaryExpr& be) { CJC_NULLPTR_CHECK(be.desugarExpr); CJC_ASSERT(be.desugarExpr->astKind == ASTKind::CALL_EXPR); auto callexpr = RawStaticCast(be.desugarExpr.get()); CJC_ASSERT(callexpr->baseFunc->astKind == ASTKind::MEMBER_ACCESS); auto ceBaseFunc = RawStaticCast(callexpr->baseFunc.get()); CJC_NULLPTR_CHECK(ceBaseFunc->baseExpr); CJC_ASSERT(Ty::IsTyCorrect(ceBaseFunc->baseExpr->ty)); CJC_ASSERT(callexpr->args.size() == 1); CJC_NULLPTR_CHECK(callexpr->args[0]->expr); CJC_ASSERT(Ty::IsTyCorrect(callexpr->args[0]->expr->ty)); return std::make_pair(ceBaseFunc->baseExpr->ty->String(), callexpr->args[0]->expr->ty->String()); } } // namespace void TypeChecker::TypeCheckerImpl::MarkOutermostBinaryExpressions(Package& pkg) const { MarkOutermostBinaryExpr(true, pkg); } bool TypeChecker::TypeCheckerImpl::ChkBinaryExpr(ASTContext& ctx, Ty& target, BinaryExpr& be) { auto cs = PData::CommitScope(typeManager.constraints); if (be.desugarExpr) { return typeManager.IsSubtype(be.desugarExpr->ty, &target); } bool invalid = !be.leftExpr || !be.rightExpr; if (invalid) { be.ty = TypeManager::GetNonNullTy(be.ty); return false; } // 1. Check built-in operator expr. { // Create a scope for DiagSuppressor. auto ds = DiagSuppressor(diag); if (auto optRes = CheckBinaryExprCaseBuiltIn(ctx, be, &target)) { ds.ReportDiag(); return *optRes; } PData::Reset(typeManager.constraints); } // Do not throw stored errors, re-generated below. if (TypeCheckUtil::IsOverloadableOperator(be.op)) { DesugarOperatorOverloadExpr(ctx, be); auto ds = DiagSuppressor(diag); if (Check(ctx, &target, be.desugarExpr.get())) { ds.ReportDiag(); CJC_NULLPTR_CHECK(be.desugarExpr); be.ty = be.desugarExpr->ty; CJC_ASSERT(be.desugarExpr->astKind == ASTKind::CALL_EXPR); ReplaceTarget(&be, StaticCast(be.desugarExpr.get())->resolvedFunction); return true; } else { RecoverToBinaryExpr(be); PData::Reset(typeManager.constraints); } } // Clear the node to `Synthesize` the literals and the corresponding expressions again. be.Clear(); if (Ty::IsTyCorrect(SynthesizeWithNegCache(ctx, &be))) { DiagMismatchedTypes(diag, be, target); RecoverToBinaryExpr(be); } if (be.TestAttr(Attribute::IS_OUTERMOST)) { SynBinaryLeafs(ctx, be); } be.ty = TypeManager::GetInvalidTy(); return false; } bool TypeChecker::TypeCheckerImpl::ChkArithmeticExpr(ASTContext& ctx, Ty& target, BinaryExpr& be) { if (!IsBinaryOperator(be.op)) { return false; } // Update the targetTy so that the binary expression is of a primitive type, which can be boxed properly // to an interface type later. Forced by the current box algorithm. Ptr optionUnboxTy = TypeCheckUtil::UnboxOptionType(&target); if (!Ty::IsTyCorrect(optionUnboxTy)) { return false; } // Exponentiation expressions have special rules and thus be handled differently. if (be.op == TokenKind::EXP) { return Ty::IsTyCorrect(&target) && ChkExpoExpr(ctx, target, be); } std::map typeCandidates = GetBinaryOpTypeCandidates(be.op); Utils::EraseIf(typeCandidates, [optionUnboxTy, this](auto& e) { auto primitiveTy = TypeManager::GetPrimitiveTy(e.second); return primitiveTy->IsIdeal() || !typeManager.IsSubtype(primitiveTy, optionUnboxTy); }); std::vector thisDiags; for (auto& type : std::as_const(typeCandidates)) { auto ds = DiagSuppressor(diag); auto tgtTy = TypeManager::GetPrimitiveTy(type.second); bool isWellTyped = CheckWithNegCache(ctx, tgtTy, be.leftExpr.get()); isWellTyped = CheckWithNegCache(ctx, tgtTy, be.rightExpr.get()) && isWellTyped; if (isWellTyped) { ds.ReportDiag(); be.ty = tgtTy; return true; } else { auto tmpDiag = ds.GetSuppressedDiag(); (void)thisDiags.insert(thisDiags.end(), tmpDiag.begin(), tmpDiag.end()); } } (void)std::for_each(thisDiags.begin(), thisDiags.end(), [this](auto& d) { (void)diag.Diagnose(d); }); be.ty = TypeManager::GetInvalidTy(); return false; } bool TypeChecker::TypeCheckerImpl::ChkExpoExpr(ASTContext& ctx, Ty& tgtTy, BinaryExpr& be) { Ptr i64 = TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64); Ptr f64 = TypeManager::GetPrimitiveTy(TypeKind::TYPE_FLOAT64); Ptr u64 = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UINT64); // Using the IsSubtype function to fit the ImplicitConvert interface in the future. bool isBaseWellTyped = false; bool isExponentWellTyped = false; bool i64Compatible = typeManager.IsSubtype(i64, &tgtTy); bool f64Compatible = typeManager.IsSubtype(f64, &tgtTy); bool maybef64 = f64Compatible; auto cs = PData::CommitScope(typeManager.constraints); if (i64Compatible) { auto ds = DiagSuppressor(diag); isBaseWellTyped = ChkExpoExprBase(ctx, be, *i64); std::vector> exponentTys = {u64}; isExponentWellTyped = ChkExpoExprExponent(ctx, *be.rightExpr, exponentTys); maybef64 = maybef64 && !(isBaseWellTyped && isExponentWellTyped); if (!maybef64) { ds.ReportDiag(); } else { PData::Reset(typeManager.constraints); } } if (maybef64) { isBaseWellTyped = ChkExpoExprBase(ctx, be, *f64); std::vector> exponentTys = {i64, f64}; isExponentWellTyped = ChkExpoExprExponent(ctx, *be.rightExpr, exponentTys); } if (!i64Compatible && !f64Compatible) { (void)diag.Diagnose(be, DiagKind::sema_incompatible_expo_target_type, tgtTy.String()); } if (isBaseWellTyped && isExponentWellTyped) { be.ty = be.leftExpr->ty; return true; } else { be.ty = TypeManager::GetInvalidTy(); return false; } } bool TypeChecker::TypeCheckerImpl::ChkExpoExprBase(ASTContext& ctx, const BinaryExpr& be, Ty& baseTy) { const std::string noteMsg = "the context requires this exponentiation expression to be of type '" + baseTy.String() + "'; the type of the left operand must also be '" + baseTy.String() + "' in this case"; if (!CheckWithNegCache(ctx, &baseTy, be.leftExpr.get())) { if (!CanSkipDiag(*be.leftExpr)) { DiagMismatchedTypes(diag, *be.leftExpr, baseTy, noteMsg); } return false; } else { return true; } } bool TypeChecker::TypeCheckerImpl::ChkExpoExprExponent(ASTContext& ctx, Expr& exponent, std::vector> exTys) { bool isWellTyped = true; std::vector pass; auto cs = PData::CommitScope(typeManager.constraints); (void)std::for_each(exTys.cbegin(), exTys.cend(), [this, &ctx, &exponent, &pass](Ptr ty) { auto ds = DiagSuppressor(diag); // Use check mode to let more cases such as a ** f() where f is overloaded pass type checking. (void)pass.emplace_back(CheckWithNegCache(ctx, ty, &exponent) ? 1 : 0); }); PData::Reset(typeManager.constraints); auto answers = std::accumulate(pass.begin(), pass.end(), decltype(pass)::value_type(0)); if (answers > 1) { (void)diag.Diagnose(exponent, DiagKind::sema_ambiguous_expo_right_operand_type); isWellTyped = false; } else if (answers == 1) { // Get the correct answer's position size_t i = 0; while (pass.at(i) != true) { i++; } auto ty = exTys.at(i); // Recheck to set correct semantic types isWellTyped = CheckWithNegCache(ctx, ty, &exponent); } else { // The nested if reflects the checking logic better. isWellTyped = false; if (CanSkipDiag(exponent)) { (void)SynthesizeWithNegCache(ctx, &exponent); // Synthesize 'exponent' to report errors. } else { std::string tgtTyStr; if (exTys.size() == 1) { tgtTyStr = exTys.front()->String(); } else { (void)std::for_each(exTys.begin(), std::prev(exTys.end()), [&tgtTyStr](const auto ty) { tgtTyStr += ty->String() + "' or '"; }); CJC_ASSERT(!exTys.empty() && exTys.back()); tgtTyStr += exTys.back()->String(); } DiagMismatchedTypesWithFoundTy(diag, exponent, tgtTyStr, exponent.ty->String(), "the type of the right operand must be " + tgtTyStr + " in this case"); } } return isWellTyped; } Ptr TypeChecker::TypeCheckerImpl::SynExpoExpr(ASTContext& ctx, BinaryExpr& be) { (void)SynthesizeWithNegCache(ctx, be.leftExpr.get()); ReplaceIdealTy(*be.leftExpr); auto& exponent = *be.rightExpr; Ptr baseTy = be.leftExpr->ty; CJC_NULLPTR_CHECK(baseTy); bool isExponentWellTyped = CheckExponentByBaseTy(ctx, *baseTy, *be.leftExpr, exponent); be.ty = Ty::IsTyCorrect(be.leftExpr->ty) && isExponentWellTyped ? be.leftExpr->ty : TypeManager::GetInvalidTy(); return be.ty; } bool TypeChecker::TypeCheckerImpl::CheckExponentByBaseTy(ASTContext& ctx, Ty& baseTy, const Expr& base, Expr& exponent) { Ptr i64 = TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64); Ptr u64 = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UINT64); Ptr f64 = TypeManager::GetPrimitiveTy(TypeKind::TYPE_FLOAT64); // The left operand of ** can only be of type Int64 or Float64. bool isExponentWellTyped = true; if (baseTy.IsPlaceholder()) { std::set, Ptr>> validCombos = {{i64, u64}, {f64, i64}, {f64, f64}}; SynthesizeWithNegCache(ctx, &exponent); ReplaceIdealTy(exponent); if (!Ty::IsTyCorrect(exponent.ty)) { isExponentWellTyped = false; } else { switch (PickConstaintFromTys(baseTy, *exponent.ty, validCombos, true)) { case MatchResult::UNIQUE: break; case MatchResult::AMBIGUOUS: case MatchResult::NONE: isExponentWellTyped = false; break; } } } if (isExponentWellTyped && typeManager.IsSubtype(&baseTy, i64)) { std::vector> exponentTys = {u64}; isExponentWellTyped = ChkExpoExprExponent(ctx, exponent, exponentTys); } else if (isExponentWellTyped && typeManager.IsSubtype(&baseTy, f64)) { std::vector> exponentTys = {i64, f64}; isExponentWellTyped = ChkExpoExprExponent(ctx, exponent, exponentTys); } else { isExponentWellTyped = false; if (Ty::IsTyCorrect(&baseTy)) { DiagMismatchedTypesWithFoundTy(diag, base, "Int64' or 'Float64", baseTy.String(), "the type of the left operand of an exponentiation expression must be either 'Int64' or 'Float64'"); } (void)SynthesizeWithNegCache(ctx, &exponent); } return isExponentWellTyped; } bool TypeChecker::TypeCheckerImpl::ChkRelationalExpr(ASTContext& ctx, Ty& target, BinaryExpr& be) { // Check target type is bool type. if (!target.IsBoolean()) { // Option type allow auto box. if (!typeManager.IsLitBoxableType(TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN), &target)) { return false; } } auto ty = SynLiteralInBinaryExpr(ctx, be); if (!Ty::IsTyCorrect(ty)) { return false; } // Check expression is numeric type. if (!IsBinaryOperator(be.op)) { return false; } const auto& typeCandidates = GetBinaryOpTypeCandidates(be.op); if (ty->IsNothing() || Utils::InKeys(ty->kind, typeCandidates)) { be.ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); return true; } be.ty = TypeManager::GetInvalidTy(); return false; } bool TypeChecker::TypeCheckerImpl::ChkLogicalExpr(ASTContext& ctx, Ty& target, BinaryExpr& be) { Ptr boolTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); // Check T is bool type. if (!target.IsBoolean()) { // Option type allow auto box. if (!typeManager.IsLitBoxableType(boolTy, &target)) { DiagMismatchedTypesWithFoundTy(diag, be, target, *boolTy); be.ty = TypeManager::GetInvalidTy(); return false; } } auto ret = true; if (!CheckWithNegCache(ctx, boolTy, be.leftExpr.get())) { ret = false; } if (!CheckWithNegCache(ctx, boolTy, be.rightExpr.get())) { ret = false; } if (ret) { be.ty = boolTy; return true; } be.ty = TypeManager::GetInvalidTy(); return false; } bool TypeChecker::TypeCheckerImpl::CheckTupleCanEqual(ASTContext& ctx, BinaryExpr& be) { CJC_ASSERT(be.leftExpr != nullptr && Ty::IsTyCorrect(be.leftExpr->ty) && be.leftExpr->ty->IsTuple()); CJC_ASSERT(be.rightExpr != nullptr && Ty::IsTyCorrect(be.rightExpr->ty) && be.rightExpr->ty->IsTuple()); TupleTy& leftTupleTy = *RawStaticCast(be.leftExpr->ty); TupleTy& rightTupleTy = *RawStaticCast(be.rightExpr->ty); if (leftTupleTy.typeArgs.size() != rightTupleTy.typeArgs.size()) { (void)diag.Diagnose(be, DiagKind::sema_tuple_cmp_not_supported, TOKENS[static_cast(be.op)], leftTupleTy.String(), rightTupleTy.String()); be.ty = TypeManager::GetInvalidTy(); return false; } TokenKind connectionOp = be.op == TokenKind::EQUAL ? TokenKind::AND : TokenKind::OR; auto boolTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); OwnedPtr tupleCmpExpr = CreateLitConstExpr(LitConstKind::BOOL, be.op == TokenKind::EQUAL ? "true" : "false", boolTy); std::vector> leftElements = GetTupleElements(*be.leftExpr); std::vector> rightElements = GetTupleElements(*be.rightExpr); /* Try to convert tupleA == (or !=) tupleB into * true && tupleA[0] == tupleB[0] && tupleA[1] == tupleB[1] && ... tupleA[n] == tupleB[n] (if op is ==) * false || tupleA[0] != tupleB[0] || tupleA[1] != tupleB[1] || ... tupleA[n] != tupleB[n] (if op is !=) * and store that expression in be.desugarExpr. */ for (size_t i = 0; i < leftElements.size(); i++) { CJC_ASSERT(i < rightElements.size()); auto elementCmpExpr = CreateBinaryExpr(std::move(leftElements[i]), std::move(rightElements[i]), be.op); elementCmpExpr->EnableAttr(Attribute::IMPLICIT_ADD); CopyBasicInfo(&be, elementCmpExpr.get()); Ptr returnTy = nullptr; { auto ds = DiagSuppressor(diag); returnTy = SynBinaryExpr(ctx, *elementCmpExpr); } if (!Ty::IsTyCorrect(returnTy)) { // These two elements do not support == (or !=). (void)diag.Diagnose(be, DiagKind::sema_tuple_cmp_not_supported, TOKENS[static_cast(be.op)], leftTupleTy.String(), rightTupleTy.String()); be.ty = TypeManager::GetInvalidTy(); return false; } if (!returnTy->IsBoolean()) { auto argTys = GetDesugaredBinaryExprArgTys(*elementCmpExpr); (void)diag.Diagnose(be, DiagKind::sema_tuple_element_cmp_not_bool, TOKENS[static_cast(be.op)], argTys.first, argTys.second); be.ty = TypeManager::GetInvalidTy(); return false; } auto connectionExpr = CreateBinaryExpr(std::move(tupleCmpExpr), std::move(elementCmpExpr), connectionOp); tupleCmpExpr = std::move(connectionExpr); } if (HasSideEffect(*be.leftExpr) || HasSideEffect(*be.rightExpr)) { tupleCmpExpr->EnableAttr(Attribute::SIDE_EFFECT); } be.ty = boolTy; // The built-in == (or !=) is always evaluated to a bool. be.desugarExpr = std::move(tupleCmpExpr); return true; } std::optional TypeChecker::TypeCheckerImpl::CheckBinaryExprCaseBuiltIn( ASTContext& ctx, BinaryExpr& be, Ptr target) { CJC_ASSERT(target); bool checkRet = false; if (Utils::In(be.op, ARITHMETIC_OPERATOR)) { checkRet = ChkArithmeticExpr(ctx, *target, be); } else if (Utils::In(be.op, RELATIONAL_OPERATOR)) { checkRet = ChkRelationalExpr(ctx, *target, be); if (checkRet) { return {true}; } // Tuple equality is handled specially. bool cannotBeTuple = (!Ty::IsTyCorrect(be.leftExpr->ty) && !Ty::IsInitialTy(be.leftExpr->ty)) || (!Ty::IsTyCorrect(be.rightExpr->ty) && !Ty::IsInitialTy(be.rightExpr->ty)) || (be.op != TokenKind::EQUAL && be.op != TokenKind::NOTEQ); if (cannotBeTuple) { return {}; } bool bothAreTuple = Ty::IsTyCorrect(SynthesizeWithNegCache(ctx, be.leftExpr.get())) && be.leftExpr->ty->IsTuple() && Ty::IsTyCorrect(SynthesizeWithNegCache(ctx, be.rightExpr.get())) && be.rightExpr->ty->IsTuple(); if (bothAreTuple) { if (!CheckTupleCanEqual(ctx, be)) { return {false}; } if (!typeManager.IsSubtype(be.ty, target)) { DiagMismatchedTypes(diag, be, *target); return {false}; } return {true}; } } else if (Utils::In(be.op, LOGICAL_OPERATOR)) { checkRet = ChkLogicalExpr(ctx, *target, be); } else if (Utils::In(be.op, SHIFT_OPERATOR)) { checkRet = ChkShiftExpr(ctx, *target, be); } else if (Utils::In(be.op, FLOW_OPERATOR)) { if (ChkFlowExpr(ctx, target, be)) { return {true}; } } else if (be.op == TokenKind::COALESCING) { return {ChkCoalescingExpr(ctx, target, be)}; } else { checkRet = ChkArithmeticExpr(ctx, *target, be); } if (checkRet) { return {true}; } return {}; } Ptr TypeChecker::TypeCheckerImpl::SynLiteralInBinaryExpr(ASTContext& ctx, BinaryExpr& be) { CJC_NULLPTR_CHECK(be.leftExpr); CJC_NULLPTR_CHECK(be.rightExpr); if (auto optTy = SynLiteralInBinaryExprFromRight(ctx, be)) { return *optTy; } be.Clear(); return SynLiteralInBinaryExprFromLeft(ctx, be); } std::optional> TypeChecker::TypeCheckerImpl::SynLiteralInBinaryExprFromRight(ASTContext& ctx, BinaryExpr& be) { CJC_NULLPTR_CHECK(be.rightExpr); CJC_NULLPTR_CHECK(be.leftExpr); { auto ds = DiagSuppressor(diag); bool isWellTyped = SynthesizeWithNegCache(ctx, be.rightExpr.get()) && ReplaceIdealTy(*be.rightExpr); if (!isWellTyped) { be.ty = TypeManager::GetInvalidTy(); return {}; } } auto targetRight = be.rightExpr->ty; // In this case, type variables' upper bounds must be primitive types. targetRight = Ty::GetPrimitiveUpperBound(targetRight); bool isWellTyped = Ty::IsTyCorrect(targetRight) && (targetRight->IsNothing() || targetRight->IsPrimitiveSubType() || targetRight->IsTuple() || targetRight->IsPlaceholder()); if (!isWellTyped) { // Bind target when error happens for LSP. SynthesizeWithNegCache(ctx, be.leftExpr.get()); be.ty = TypeManager::GetInvalidTy(); return {TypeManager::GetInvalidTy()}; } auto ds = DiagSuppressor(diag); be.leftExpr->Clear(); isWellTyped = CheckWithNegCache(ctx, targetRight, be.leftExpr.get()) && isWellTyped; if (isWellTyped) { ds.ReportDiag(); targetRight = typeManager.TryGreedySubst(targetRight); be.rightExpr->ty = targetRight; be.ty = targetRight; return {targetRight}; } else { be.ty = TypeManager::GetInvalidTy(); return {}; } } Ptr TypeChecker::TypeCheckerImpl::SynLiteralInBinaryExprFromLeft(ASTContext& ctx, BinaryExpr& be) { bool isWellTyped = SynthesizeWithNegCache(ctx, be.leftExpr.get()) && ReplaceIdealTy(*be.leftExpr); auto targetLeft = be.leftExpr->ty; isWellTyped = isWellTyped && (targetLeft->IsNothing() || targetLeft->IsPrimitiveSubType() || targetLeft->IsTuple() || targetLeft->IsPlaceholder()); if (!isWellTyped) { // Bind target when error happens for LSP. SynthesizeWithNegCache(ctx, be.rightExpr.get()); be.ty = TypeManager::GetInvalidTy(); return TypeManager::GetInvalidTy(); } auto ds = DiagSuppressor(diag); isWellTyped = CheckWithNegCache(ctx, targetLeft, be.rightExpr.get()) && isWellTyped; if (isWellTyped) { ds.ReportDiag(); be.ty = targetLeft; return targetLeft; } else { be.Clear(); return TypeManager::GetInvalidTy(); } } std::optional> TypeChecker::TypeCheckerImpl::SynArithmeticOrRelationalExpr( ASTContext& ctx, BinaryExpr& be, bool isArithmetic) { // Exponentiation expressions have special rules and thus be handled differently. if (isArithmetic && be.op == TokenKind::EXP) { return SynExpoExpr(ctx, be); } auto ty = SynLiteralInBinaryExpr(ctx, be); if (!Ty::IsTyCorrect(ty)) { return ty; } // Check expression is numeric type. if (!IsBinaryOperator(be.op)) { return TypeManager::GetInvalidTy(); } const auto& typeCandidates = GetBinaryOpTypeCandidates(be.op); ty = Ty::GetPrimitiveUpperBound(ty); // If the ty is generic, it must be subtype of primitive. // placeholder without any info if (auto tv = DynamicCast(ty); tv && tv->isPlaceholder) { switch (PickConstaintFromTys(*tv, TypeMapToTys(typeCandidates, true), true)) { case MatchResult::UNIQUE: be.ty = typeManager.TryGreedySubst(tv); return {be.ty}; case MatchResult::AMBIGUOUS: return {}; case MatchResult::NONE: break; } } if (!ty->IsNothing() && !Utils::InKeys(ty->kind, typeCandidates)) { return {TypeManager::GetInvalidTy()}; } be.ty = isArithmetic ? ty : TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); return {be.ty}; } Ptr TypeChecker::TypeCheckerImpl::SynLogicalExpr(ASTContext& ctx, BinaryExpr& be) { CJC_NULLPTR_CHECK(be.leftExpr); CJC_NULLPTR_CHECK(be.rightExpr); auto leftTy = SynthesizeWithNegCache(ctx, be.leftExpr.get()); auto rightTy = SynthesizeWithNegCache(ctx, be.rightExpr.get()); if (!Ty::IsTyCorrect(leftTy) || !Ty::IsTyCorrect(rightTy)) { return TypeManager::GetInvalidTy(); } auto boolTy = TypeManager::GetPrimitiveTy(AST::TypeKind::TYPE_BOOLEAN); if (typeManager.IsSubtype(leftTy, boolTy) && typeManager.IsSubtype(rightTy, boolTy)) { be.ty = boolTy; return be.ty; } else { return TypeManager::GetInvalidTy(); } } bool TypeChecker::TypeCheckerImpl::ChkShiftExpr(ASTContext& ctx, Ty& target, BinaryExpr& be) { be.ty = TypeManager::GetInvalidTy(); if (!be.leftExpr || !be.rightExpr) { return false; } auto isWellTyped = CheckWithNegCache(ctx, &target, be.leftExpr.get()); auto leftTy = be.leftExpr->ty; SynthesizeWithNegCache(ctx, be.rightExpr.get()); ReplaceIdealTy(*be.rightExpr); auto rightTy = be.rightExpr->ty; isWellTyped = Ty::IsTyCorrect(rightTy) && isWellTyped; isWellTyped = IsBinaryOperator(be.op) && isWellTyped; if (!isWellTyped) { return false; } const auto& typeCandidates = GetBinaryOpTypeCandidates(be.op); isWellTyped = (leftTy->IsNothing() || Utils::InKeys(leftTy->kind, typeCandidates)) && isWellTyped; isWellTyped = (rightTy->IsNothing() || Utils::InKeys(rightTy->kind, typeCandidates)) && isWellTyped; if (isWellTyped) { be.ty = be.leftExpr->ty; } return isWellTyped; } Ptr TypeChecker::TypeCheckerImpl::SynShiftExpr(ASTContext& ctx, BinaryExpr& be) { CJC_NULLPTR_CHECK(be.leftExpr); CJC_NULLPTR_CHECK(be.rightExpr); auto leftTy = SynthesizeWithNegCache(ctx, be.leftExpr.get()); auto rightTy = SynthesizeWithNegCache(ctx, be.rightExpr.get()); if (!Ty::IsTyCorrect(leftTy) || !Ty::IsTyCorrect(rightTy)) { return TypeManager::GetInvalidTy(); } // Check expression is numeric type. if (!IsBinaryOperator(be.op)) { return TypeManager::GetInvalidTy(); } const auto& typeCandidates = GetBinaryOpTypeCandidates(be.op); leftTy = Ty::GetPrimitiveUpperBound(leftTy); // If the ty is generic, it must be subtype of primitive. rightTy = Ty::GetPrimitiveUpperBound(rightTy); // If the ty is generic, it must be subtype of primitive. if (!leftTy->IsNothing() && !Utils::InKeys(leftTy->kind, typeCandidates)) { return TypeManager::GetInvalidTy(); } if (!rightTy->IsNothing() && !Utils::InKeys(rightTy->kind, typeCandidates)) { return TypeManager::GetInvalidTy(); } ReplaceIdealTy(*be.leftExpr); ReplaceIdealTy(*be.rightExpr); be.ty = leftTy; ReplaceIdealTy(be); return be.ty; } void TypeChecker::TypeCheckerImpl::DiagnoseForBinaryExpr(ASTContext& ctx, BinaryExpr& be) { // `pivot` is responsible for the invalid type. std::vector> pivotStack = {&be}; while (true) { Ptr pivot = pivotStack.back(); CJC_NULLPTR_CHECK(pivot); if (Ty::IsTyCorrect(pivot->ty) || !pivot->ShouldDiagnose(true)) { return; } if (Ty::IsInitialTy(pivot->leftExpr->ty)) { auto ds = DiagSuppressor(diag); SynthesizeWithNegCache(ctx, pivot->leftExpr); // in case the node was cleared and then skipped } if (!Ty::IsTyCorrect(pivot->leftExpr->ty)) { auto newPivot = GetChildBinaryExpr(*pivot->leftExpr); if (newPivot) { pivotStack.emplace_back(newPivot); continue; } if (!Ty::IsTyCorrect(SynthesizeWithNegCache(ctx, pivot->leftExpr.get()))) { // `leftExpr` is the pivot, and the `Synthesize` should have diagnosed errors in `leftExpr`. // Now we report diagnostics in the `rightExpr`, SynthesizeWithNegCache(ctx, pivot->rightExpr.get()); return; } } // So far, `leftExpr` is correct. if (Ty::IsInitialTy(pivot->rightExpr->ty)) { auto ds = DiagSuppressor(diag); SynthesizeWithNegCache(ctx, pivot->rightExpr); } if (!Ty::IsTyCorrect(pivot->rightExpr->ty)) { auto newPivot = GetChildBinaryExpr(*pivot->rightExpr); if (newPivot) { pivotStack.emplace_back(newPivot); continue; } if (!Ty::IsTyCorrect(SynthesizeWithNegCache(ctx, pivot->rightExpr.get()))) { // `rightExpr` is the pivot, and the `Synthesize` should have diagnosed. return; } } // So far, both `leftExpr` and `rightExpr` are valid, i.e., we've got the pivot. break; } while (pivotStack.size() > 1) { Ptr pivot = pivotStack.back(); pivotStack.pop_back(); if (!Ty::IsTyCorrect(SynthesizeWithNegCache(ctx, pivot))) { return; } } Ptr pivot = pivotStack.back(); SynthesizeWithNegCache(ctx, pivot->leftExpr.get()); SynthesizeWithNegCache(ctx, pivot->rightExpr.get()); if (ReplaceIdealTy(*pivot->leftExpr) && ReplaceIdealTy(*pivot->rightExpr)) { DiagInvalidBinaryExpr(diag, *pivot); } } Ptr TypeChecker::TypeCheckerImpl::SynBinaryExpr(ASTContext& ctx, BinaryExpr& be) { if (be.desugarExpr) { return be.desugarExpr->ty; } CJC_NULLPTR_CHECK(be.leftExpr); CJC_NULLPTR_CHECK(be.rightExpr); auto cs = PData::CommitScope(typeManager.constraints); { // Create a scope for DiagSuppressor. auto ds = DiagSuppressor(diag); // Infer builtin binary Expr. Ptr inferRet = TypeManager::GetInvalidTy(); if (auto optTy = InferBinaryExprCaseBuiltIn(ctx, be, inferRet)) { ds.ReportDiag(); return *optTy; } if (Ty::IsTyCorrect(inferRet)) { ds.ReportDiag(); return be.ty; } } PData::Reset(typeManager.constraints); if (TypeCheckUtil::IsOverloadableOperator(be.op)) { DesugarOperatorOverloadExpr(ctx, be); auto ds = DiagSuppressor(diag); if (Ty::IsTyCorrect(Synthesize(ctx, be.desugarExpr.get()))) { ds.ReportDiag(); // Desugar SubscriptOverloadExpr guarantees the deref and StaticCast. be.ty = be.desugarExpr->ty; ReplaceTarget(&be, StaticCast(be.desugarExpr.get())->resolvedFunction); } else { // Recover to BinaryExpr. RecoverToBinaryExpr(be); be.ty = TypeManager::GetInvalidTy(); PData::Reset(typeManager.constraints); } } else { be.ty = TypeManager::GetInvalidTy(); } if (be.TestAttr(Attribute::IS_OUTERMOST)) { SynBinaryLeafs(ctx, be); // NOTE: All used reference must be synthesized for lsp usage. } DiagnoseForBinaryExpr(ctx, be); return be.ty; } /** * Synthesize all reference leaf nodes when BinaryExpr is checked as invalid. */ void TypeChecker::TypeCheckerImpl::SynBinaryLeafs(ASTContext& ctx, BinaryExpr& be) { auto id = Walker::GetNextWalkerID(); std::function)> synLeafs = [this, id, &ctx, &synLeafs](auto node) { CJC_ASSERT(node); if (auto expr = DynamicCast(node); expr && expr->desugarExpr) { Walker(expr, id, synLeafs).Walk(); return VisitAction::SKIP_CHILDREN; } if (node->TestAnyAttr(AST::Attribute::IS_BROKEN, AST::Attribute::HAS_BROKEN) || Is(node)) { return VisitAction::SKIP_CHILDREN; } if (auto ce = DynamicCast(node); ce) { CJC_NULLPTR_CHECK(ce->baseFunc); SetIsNotAlone(*ce->baseFunc); if (auto bf = DynamicCast(ce->baseFunc.get())) { bf->callOrPattern = ce; } } else if (auto ma = DynamicCast(node); ma && !IsFieldOperator(ma->field)) { Synthesize(ctx, ma); return VisitAction::SKIP_CHILDREN; } else if (auto re = DynamicCast(node); re) { Synthesize(ctx, re); } return VisitAction::WALK_CHILDREN; }; Walker(&be, id, synLeafs).Walk(); } std::optional> TypeChecker::TypeCheckerImpl::InferBinaryExprCaseBuiltIn( ASTContext& ctx, BinaryExpr& be, Ptr& inferRet) { bool failNow = false; if (Utils::In(be.op, ARITHMETIC_OPERATOR)) { if (auto maybeRet = SynArithmeticOrRelationalExpr(ctx, be)) { inferRet = *maybeRet; } else { failNow = true; } } else if (Utils::In(be.op, RELATIONAL_OPERATOR)) { if (auto maybeRet = SynArithmeticOrRelationalExpr(ctx, be, false)) { inferRet = *maybeRet; } else { failNow = true; } if (Ty::IsInitialTy(be.leftExpr->ty)) { (void)SynthesizeWithNegCache(ctx, be.leftExpr.get()); } if (Ty::IsInitialTy(be.rightExpr->ty)) { (void)SynthesizeWithNegCache(ctx, be.rightExpr.get()); } auto leftTupleTy = DynamicCast(be.leftExpr->ty); auto rightTupleTy = DynamicCast(be.rightExpr->ty); if (Ty::IsTyCorrect(leftTupleTy) && Ty::IsTyCorrect(rightTupleTy) && (be.op == TokenKind::EQUAL || be.op == TokenKind::NOTEQ)) { if (!CheckTupleCanEqual(ctx, be)) { inferRet = TypeManager::GetInvalidTy(); } else { inferRet = be.ty; } } } else if (Utils::In(be.op, LOGICAL_OPERATOR)) { inferRet = SynLogicalExpr(ctx, be); } else if (Utils::In(be.op, SHIFT_OPERATOR)) { inferRet = SynShiftExpr(ctx, be); } else if (Utils::In(be.op, FLOW_OPERATOR)) { inferRet = SynFlowExpr(ctx, be); return {be.ty}; } else if (be.op == TokenKind::COALESCING) { return {SynCoalescingExpr(ctx, be)}; } else { if (auto maybeRet = SynArithmeticOrRelationalExpr(ctx, be)) { inferRet = *maybeRet; } else { failNow = true; } } // Only syn leaf nodes when current is the outermost binaryExpr. if (be.TestAttr(Attribute::IS_OUTERMOST)) { // Since check of 'CallExpr' will clear target of 'RefExpr' and 'MemberAccess', // we need to syn leaf before check invalid. SynBinaryLeafs(ctx, be); } if (HasInvalidChild(be)) { if (be.TestAttr(Attribute::IS_OUTERMOST)) { DiagnoseForBinaryExpr(ctx, be); // Only report error when current is outer most binary expr. } be.ty = TypeManager::GetInvalidTy(); return {be.ty}; } else if (failNow) { be.ty = TypeManager::GetInvalidTy(); return {be.ty}; } else { return {}; } } bool TypeChecker::TypeCheckerImpl::CheckFlowOperandsHaveNamedParam(const CallExpr& ce) { return std::any_of(ce.args.cbegin(), ce.args.cend(), [this](const OwnedPtr& arg) { CJC_NULLPTR_CHECK(arg); if (auto re = DynamicCast(arg->expr.get()); re && RefExprTargetIsFuncDeclAndHasNamedParam(*re)) { diag.Diagnose(*arg->expr, DiagKind::sema_fail_flow_expr_operand_has_named_param); return true; } return false; }); } bool TypeChecker::TypeCheckerImpl::ChkFlowExpr(ASTContext& ctx, Ptr target, BinaryExpr& be) { if (Ty::IsTyCorrect(be.ty)) { return !(target && typeManager.CheckTypeCompatibility(be.ty, target) == TypeCompatibility::INCOMPATIBLE); } auto& rightExpr = *be.rightExpr; auto& leftExpr = *be.leftExpr; rightExpr.isInFlowExpr = true; auto cs = PData::CommitScope(typeManager.constraints); { auto ds = DiagSuppressor(diag); (void)SynthesizeWithNegCache(ctx, &leftExpr); (void)SynthesizeWithNegCache(ctx, &rightExpr); if (ds.HasError()) { if (be.op == TokenKind::COMPOSITION) { be.ty = TypeManager::GetInvalidTy(); ds.ReportDiag(); return false; } else { leftExpr.Clear(); rightExpr.Clear(); PData::Reset(typeManager.constraints); } } } // Check if flow expr contains 'this' which is illegal. // 'super' is not allowed to be used alone and is checked in 'InferSuperExpr'. auto isThisExpr = [this](Expr& exp) { if (auto re = DynamicCast(&exp); re && re->isAlone && re->isThis) { diag.Diagnose(*re, DiagKind::sema_flow_expressions_use_this_or_super, re->ref.identifier.Val()); return false; } return true; }; // 'this' or 'super' is not allowed to be used in function part of flow expression. // NOT allowed: a |> this; a |> super; this ~> f; f ~> this; super ~> f; f ~> super. // Allowed: this |> f. if ((be.op == TokenKind::COMPOSITION && !isThisExpr(leftExpr)) || !isThisExpr(rightExpr)) { be.ty = TypeManager::GetInvalidTy(); return false; } // Desugar flow expr. DesugarFlowExpr(ctx, be); CJC_ASSERT(be.desugarExpr && be.desugarExpr->astKind == ASTKind::CALL_EXPR); CallExpr& beCallExpr = *StaticCast(be.desugarExpr.get()); { auto ds = DiagSuppressor(diag); if (!ChkCallExpr(ctx, target, beCallExpr)) { (void)ds.GetSuppressedDiag(); // Should recover the desugaredExpr to binaryExpr if failed. RecoverToBinaryExpr(be); be.ty = TypeManager::GetInvalidTy(); DiagnoseForBinaryExpr(ctx, be); ds.ReportDiag(); return false; } ds.ReportDiag(); // Report warnings. } if (CheckFlowOperandsHaveNamedParam(beCallExpr)) { // Should recover the desugaredExpr to binaryExpr if failed. RecoverToBinaryExpr(be); be.ty = TypeManager::GetInvalidTy(); return false; } be.ty = be.desugarExpr->ty; return true; } Ptr TypeChecker::TypeCheckerImpl::SynFlowExpr(ASTContext& ctx, BinaryExpr& be) { if (!ChkFlowExpr(ctx, nullptr, be)) { be.ty = TypeManager::GetInvalidTy(); } return be.ty; } bool TypeChecker::TypeCheckerImpl::IsCoalescingLeftTyValid(Ty& ty) const { Ptr realTy = &ty; if (ty.IsPlaceholder()) { auto optionDecl = importManager.GetCoreDecl("Option"); realTy = typeManager.ConstrainByCtor(StaticCast(ty), *optionDecl->ty); } if (!Ty::IsTyCorrect(realTy) || !realTy->IsEnum()) { return false; } auto enumTy = RawStaticCast(realTy); if (!enumTy->declPtr) { return false; } if (enumTy->declPtr->fullPackageName != CORE_PACKAGE_NAME || enumTy->typeArgs.size() != 1) { return false; } else if (enumTy->declPtr->identifier == STD_LIB_OPTION) { // Coalescing valid for enum 'Option' in core package. return true; } return false; } bool TypeChecker::TypeCheckerImpl::ChkCoalescingExpr(ASTContext& ctx, Ptr tgtTy, BinaryExpr& be) { auto leftTy = SynthesizeWithNegCache(ctx, be.leftExpr.get()); bool isLeftTyInvalid = !leftTy || !IsCoalescingLeftTyValid(*leftTy); be.leftExpr->ty = typeManager.TryGreedySubst(leftTy); leftTy = typeManager.TryGreedySubst(leftTy); isLeftTyInvalid = isLeftTyInvalid || leftTy->typeArgs[0] == nullptr; if (isLeftTyInvalid) { be.ty = TypeManager::GetInvalidTy(); if (!CanSkipDiag(*be.leftExpr)) { (void)diag.Diagnose(*be.leftExpr, DiagKind::sema_invalid_coalescing); } // Do not set (children of) be.rightExpr to InvalidTy due to the bugs of the type check process. // For example, Check(String, B??"") succeeds and Check(Int64, B??"") fails. Then the re-inference // Check(String, B??"") fails because the type of "" does not change; the change is prevented by the re-visit // detection in CheckReferenceTypeLegality invoked by SynLitConstStringExpr. return false; } CJC_ASSERT(leftTy); if (tgtTy != nullptr && !typeManager.IsSubtype(leftTy->typeArgs[0], tgtTy)) { // Did not report errors before. Raise a new error message. if (Ty::IsTyCorrect(leftTy->typeArgs[0])) { DiagMismatchedTypesWithFoundTy(diag, *be.leftExpr, *tgtTy, *(leftTy->typeArgs[0])); } be.ty = TypeManager::GetInvalidTy(); return false; } auto realTgtTy = (tgtTy != nullptr) ? tgtTy : leftTy->typeArgs[0]; if (!CheckWithNegCache(ctx, realTgtTy, be.rightExpr.get())) { be.ty = TypeManager::GetInvalidTy(); DiagMismatchedTypes(diag, *be.rightExpr, *realTgtTy); return false; } be.ty = realTgtTy; return true; } Ptr TypeChecker::TypeCheckerImpl::SynCoalescingExpr(ASTContext& ctx, BinaryExpr& be) { if (!ChkCoalescingExpr(ctx, nullptr, be)) { be.ty = TypeManager::GetInvalidTy(); } return be.ty; } MatchResult TypeChecker::TypeCheckerImpl::PickConstaintFromTys(TyVar& tv, std::set> tys, bool isUB) { PData::CommitScope cs(typeManager.constraints); size_t cnt = 0; Ptr target = nullptr; for (auto ty : tys) { if (isUB ? typeManager.IsSubtype(&tv, ty) : typeManager.IsSubtype(ty, &tv)) { cnt++; target = ty; } PData::Reset(typeManager.constraints); } if (cnt > 1) { return MatchResult::AMBIGUOUS; } else if (cnt == 1) { isUB ? typeManager.IsSubtype(&tv, target) : typeManager.IsSubtype(target, &tv); // only for adding constraint return MatchResult::UNIQUE; } else { return MatchResult::NONE; } } MatchResult TypeChecker::TypeCheckerImpl::PickConstaintFromTys( Ty& tv1, Ty& tv2, std::set, Ptr>> tys, bool isUB) { PData::CommitScope cs(typeManager.constraints); size_t cnt = 0; Ptr target1 = nullptr; Ptr target2 = nullptr; for (auto [ty1, ty2] : tys) { if (isUB ? typeManager.IsSubtype(&tv1, ty1) && typeManager.IsSubtype(&tv2, ty2) : typeManager.IsSubtype(ty1, &tv1) && typeManager.IsSubtype(ty2, &tv2)) { cnt++; target1 = ty1; target2 = ty2; } PData::Reset(typeManager.constraints); } if (cnt > 1) { return MatchResult::AMBIGUOUS; } else if (cnt == 1) { // only for adding constraint isUB ? typeManager.IsSubtype(&tv1, target1) && typeManager.IsSubtype(&tv2, target2) : typeManager.IsSubtype(target1, &tv1) && typeManager.IsSubtype(target2, &tv2); return MatchResult::UNIQUE; } else { return MatchResult::NONE; } } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/Block.cpp000066400000000000000000000072041510705540100227420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "DiagSuppressor.h" #include "Diags.h" using namespace Cangjie; using namespace Sema; bool TypeChecker::TypeCheckerImpl::SynthesizeAndReplaceIdealTy(ASTContext& ctx, Node& node) { // Call `Synthesize` on declares containing invalid types may return valid types. // Therefore, we need to know if there are any errors during the inference process. auto ds = DiagSuppressor(diag); bool valid = Ty::IsTyCorrect(Synthesize(ctx, &node)) && ReplaceIdealTy(node) && !ds.HasError(); ds.ReportDiag(); return valid; } Ptr TypeChecker::TypeCheckerImpl::SynBlock(ASTContext& ctx, Block& b) { if (b.body.empty()) { b.ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); } else { bool existInvalid = false; for (auto& node : b.body) { existInvalid = !SynthesizeAndReplaceIdealTy(ctx, *node) || existInvalid; } Ptr lastNode = b.body[b.body.size() - 1].get(); CJC_ASSERT(lastNode != nullptr); if (existInvalid) { b.ty = TypeManager::GetInvalidTy(); } else if (lastNode->IsDecl()) { b.ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); } else { b.ty = lastNode->ty; } } return b.ty; } bool TypeChecker::TypeCheckerImpl::ChkBlock(ASTContext& ctx, Ty& target, Block& b) { Ptr unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); if (b.body.empty()) { b.ty = unitTy; // NOTE: This function may return false, the caller should handle diagnostics. // Only unsafe block is allowed to exist on its own, and needs to diagnose here. auto ret = typeManager.IsSubtype(b.ty, &target); if (!ret && b.TestAttr(Attribute::UNSAFE)) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_mismatched_types, b); builder.AddMainHintArguments(target.String(), b.ty->String()); } return ret; } // Synthesize the first N - 1 nodes. bool isWellTyped = true; for (size_t i = 0; i < b.body.size() - 1; i++) { CJC_ASSERT(b.body[i]); isWellTyped = SynthesizeAndReplaceIdealTy(ctx, *b.body[i]) && isWellTyped; } Ptr lastNode = b.body[b.body.size() - 1].get(); CJC_ASSERT(lastNode != nullptr); // If lastNode is compiler added return, just check inner expr. if (!b.TestAttr(AST::Attribute::COMPILER_ADD) && lastNode->TestAttr(AST::Attribute::COMPILER_ADD) && lastNode->astKind == ASTKind::RETURN_EXPR) { lastNode = StaticCast(lastNode)->expr.get(); } if (lastNode->IsDecl()) { bool typeMatched = typeManager.IsSubtype(unitTy, &target); isWellTyped = SynthesizeAndReplaceIdealTy(ctx, *lastNode) && typeMatched && isWellTyped; if (isWellTyped) { b.ty = unitTy; return true; } else { b.ty = TypeManager::GetInvalidTy(); if (!typeMatched) { DiagMismatchedTypesWithFoundTy( diag, *lastNode, target, *unitTy, "definitions and declarations are always of type 'Unit'"); } return false; } } else { isWellTyped = Check(ctx, &target, lastNode) && isWellTyped; if (isWellTyped) { b.ty = lastNode->ty; return true; } b.ty = TypeManager::GetInvalidTy(); return false; } } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/CMakeLists.txt000066400000000000000000000005361510705540100237450ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. file(GLOB TYPECHECKEXPR_SRC *.cpp) set(SEMA_SRC ${SEMA_SRC} ${TYPECHECKEXPR_SRC} PARENT_SCOPE)cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/Diags.cpp000066400000000000000000000115351510705540100227410ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Diags.h" #include "TypeCheckUtil.h" #include "cangjie/Basic/DiagnosticEngine.h" using namespace Cangjie; using namespace AST; namespace Cangjie::Sema { void DiagInvalidMultipleAssignExpr( DiagnosticEngine& diag, const Node& leftNode, const Expr& rightExpr, const std::string& because) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_mismatched_types_multiple_assign, rightExpr); builder.AddMainHintArguments(rightExpr.ty->String()); builder.AddHint(leftNode, because); } void DiagInvalidBinaryExpr(DiagnosticEngine& diag, const BinaryExpr& be) { CJC_NULLPTR_CHECK(be.leftExpr); CJC_NULLPTR_CHECK(be.rightExpr); CJC_ASSERT(Ty::IsTyCorrect(be.leftExpr->ty)); CJC_ASSERT(Ty::IsTyCorrect(be.rightExpr->ty)); const std::string& opStr = TOKENS[static_cast(be.op)]; auto range = be.operatorPos.IsZero() ? MakeRange(be.begin, be.end) : MakeRange(be.operatorPos, opStr); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_invalid_binary_expr, be, range, opStr, be.leftExpr->ty->String(), be.rightExpr->ty->String()); if (be.leftExpr->ty->IsFunc() || be.leftExpr->ty->IsCFunc() || be.leftExpr->ty->IsTuple()) { // func and tuple type cannot be extended return; } if (TypeCheckUtil::IsOverloadableOperator(be.op)) { std::string note("you may want to implement 'operator func " + opStr + "(right: " + be.rightExpr->ty->String() + ")' for type '" + be.leftExpr->ty->String() + "'"); if (be.op == TokenKind::EXP) { if (be.leftExpr->ty->kind == TypeKind::TYPE_INT64) { note += ", or to provide a right operand of type 'UInt64'"; } else if (be.leftExpr->ty->kind == TypeKind::TYPE_FLOAT64) { note += ", or to provide a right operand of type 'Int64' or 'Float64'"; } else if (be.rightExpr->ty->kind == TypeKind::TYPE_INT64) { note += ", or to provide a left operand of type 'Float64'"; } else if (be.rightExpr->ty->kind == TypeKind::TYPE_FLOAT64) { note += ", or to provide a left operand of type 'Float64'"; } else if (be.rightExpr->ty->kind == TypeKind::TYPE_UINT64) { note += ", or to provide a left operand of type 'Int64'"; } } builder.AddNote(note); } } void DiagInvalidUnaryExpr(DiagnosticEngine& diag, const UnaryExpr& ue) { if (!ue.ShouldDiagnose()) { return; } CJC_NULLPTR_CHECK(ue.expr); CJC_ASSERT(Ty::IsTyCorrect(ue.expr->ty)); const std::string& opStr = TOKENS[static_cast(ue.op)]; auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_invalid_unary_expr, ue, opStr, ue.expr->ty->String()); if (ue.expr->ty->IsExtendable()) { builder.AddNote( "you may want to implement 'operator func " + opStr + "()' for type '" + ue.expr->ty->String() + "'"); } } void DiagInvalidUnaryExprWithTarget(DiagnosticEngine& diag, const UnaryExpr& ue, Ty& target) { if (!ue.ShouldDiagnose()) { return; } CJC_NULLPTR_CHECK(ue.expr); CJC_ASSERT(Ty::IsTyCorrect(ue.expr->ty)); CJC_ASSERT(Ty::IsTyCorrect(&target)); const std::string& opStr = TOKENS[static_cast(ue.op)]; auto builder = diag.DiagnoseRefactor( DiagKindRefactor::sema_invalid_unary_expr_with_target, ue, opStr, ue.expr->ty->String(), target.String()); } void DiagInvalidSubscriptExpr( DiagnosticEngine& diag, const SubscriptExpr& se, const Ty& baseTy, const std::vector>& indexTys) { if (!se.ShouldDiagnose()) { return; } CJC_ASSERT(!indexTys.empty()); CJC_ASSERT(Ty::IsTyCorrect(&baseTy)); CJC_ASSERT(Ty::AreTysCorrect(indexTys)); std::string indexPrefix = "type" + (indexTys.size() > 1 ? std::string("s ") : " "); std::string indexStr = indexPrefix + "'" + Ty::GetTypesToStr(indexTys, "', ") + "'"; auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_invalid_subscript_expr, se, baseTy.String(), indexStr); if (baseTy.IsExtendable()) { std::string indexParam; for (size_t i = 0; i < indexTys.size(); ++i) { indexParam += "index" + std::to_string(i) + ": " + indexTys[i]->String(); if (i != indexTys.size() - 1) { indexParam += ", "; } } builder.AddNote( "you may want to implement 'operator func [](" + indexParam + ")' for type '" + baseTy.String() + "'"); } } void DiagUnableToInferExpr(DiagnosticEngine& diag, const Expr& expr) { (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_unable_to_infer_expr, expr); } } // namespace Cangjie::Sema cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/IfAvailableExpr.cpp000066400000000000000000000104761510705540100247130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "Desugar/DesugarInTypeCheck.h" #include "Diags.h" #include "TypeCheckerImpl.h" using namespace Cangjie; using namespace AST; namespace { const std::string LEVEL_IDENTGIFIER = "level"; const std::string SYSCAP_IDENTGIFIER = "syscap"; const std::string DEVICE_INFO = "DeviceInfo"; // For level check: const std::string PKG_NAME_DEVICE_INFO_AT = "ohos.device_info"; // For syscap check: const std::string PKG_NAME_CANIUSE_AT = "ohos.base"; bool ChkIfImportDeviceInfo(DiagnosticEngine& diag, const ImportManager& im, const IfAvailableExpr& iae) { if (iae.GetFullPackageName() == PKG_NAME_DEVICE_INFO_AT) { return true; } auto importedPkgs = im.GetAllImportedPackages(); for (auto& importedPkg : importedPkgs) { if (importedPkg->srcPackage && importedPkg->srcPackage->fullPackageName == PKG_NAME_DEVICE_INFO_AT) { return true; } } auto builder = diag.DiagnoseRefactor( DiagKindRefactor::sema_use_expr_without_import, iae, PKG_NAME_DEVICE_INFO_AT, "IfAvailable"); builder.AddNote("depend on declaration 'DeviceInfo'"); return false; } bool ChkIfImportBase(DiagnosticEngine& diag, const ImportManager& im, const IfAvailableExpr& iae) { if (iae.GetFullPackageName() == PKG_NAME_CANIUSE_AT) { return true; } auto importedPkgs = im.GetAllImportedPackages(); for (auto& importedPkg : importedPkgs) { if (importedPkg->srcPackage && importedPkg->srcPackage->fullPackageName == PKG_NAME_CANIUSE_AT) { return true; } } auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_use_expr_without_import, iae, PKG_NAME_CANIUSE_AT, "IfAvailable"); builder.AddNote("depend on declaration 'canIUse'"); return false; } } // namespace bool TypeChecker::TypeCheckerImpl::ChkIfAvailableExpr(ASTContext& ctx, Ty& ty, IfAvailableExpr& ie) { auto exprTy = SynIfAvailableExpr(ctx, ie); if (!Ty::IsTyCorrect(exprTy)) { return false; } if (!typeManager.IsSubtype(exprTy, &ty)) { Sema::DiagMismatchedTypes(diag, ie, ty); return false; } return true; } Ptr TypeChecker::TypeCheckerImpl::SynIfAvailableExpr(ASTContext& ctx, IfAvailableExpr& iae) { // Desugar before type checker. auto ie = DynamicCast(iae.desugarExpr.get()); if (!ie) { return typeManager.GetInvalidTy(); } bool res{true}; auto argName = iae.GetArg()->name; if (argName.Empty()) { diag.DiagnoseRefactor(DiagKindRefactor::sema_ifavailable_arg_no_name, *iae.GetArg()); res = false; } if (argName == LEVEL_IDENTGIFIER && ie->condExpr) { res = ChkIfImportDeviceInfo(diag, importManager, iae) && res; CJC_ASSERT(ie->condExpr->astKind == ASTKind::BINARY_EXPR); auto argExpr = StaticCast(ie->condExpr.get())->rightExpr.get(); if (argExpr->astKind != ASTKind::LIT_CONST_EXPR) { diag.DiagnoseRefactor(DiagKindRefactor::sema_ifavailable_arg_not_literal, *iae.GetArg()); res = false; } } else if (argName == SYSCAP_IDENTGIFIER && ie->condExpr) { res = ChkIfImportBase(diag, importManager, iae) && res; CJC_ASSERT(ie->condExpr->astKind == ASTKind::CALL_EXPR); auto argExpr = StaticCast(ie->condExpr.get()); CJC_ASSERT(argExpr->args.size() == 1); if (argExpr->args[0]->expr->astKind != ASTKind::LIT_CONST_EXPR) { diag.DiagnoseRefactor(DiagKindRefactor::sema_ifavailable_arg_not_literal, *iae.GetArg()); res = false; } } else { diag.DiagnoseRefactor(DiagKindRefactor::sema_ifavailable_unknow_arg_name, MakeRange(iae.GetArg()->name), iae.GetArg()->name.Val()); res = false; } auto targetTy = typeManager.GetFunctionTy({}, typeManager.GetPrimitiveTy(TypeKind::TYPE_UNIT)); res = Check(ctx, targetTy, iae.GetLambda1()) && res; res = Check(ctx, targetTy, iae.GetLambda2()) && res; if (!res) { iae.ty = typeManager.GetInvalidTy(); return iae.ty; } iae.ty = Synthesize(ctx, iae.desugarExpr); return iae.ty; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/IfExpr.cpp000066400000000000000000000225331510705540100231070ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "Diags.h" #include "JoinAndMeet.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/RecoverDesugar.h" using namespace Cangjie; using namespace Sema; using namespace TypeCheckUtil; namespace { bool IsIfExprWithoutElse(const IfExpr& ie) { if (!ie.elseBody) { return true; } else if (auto elseIfBody = As(ie.elseBody.get())) { return IsIfExprWithoutElse(*elseIfBody); } return false; } } // namespace // Syntax : if t1 then t2 else t3. Ptr TypeChecker::TypeCheckerImpl::SynIfExpr(ASTContext& ctx, IfExpr& ie) { CJC_NULLPTR_CHECK(ie.condExpr); bool isWellTyped = CheckCondition(ctx, *ie.condExpr, false); isWellTyped = ie.thenBody && Ty::IsTyCorrect(Synthesize(ctx, ie.thenBody.get())) && isWellTyped; if (IsIfExprWithoutElse(ie)) { // For the case that if-elseif without ending 'else' branch. isWellTyped = (!ie.elseBody || Ty::IsTyCorrect(Synthesize(ctx, ie.elseBody.get()))) && isWellTyped; ie.ty = isWellTyped ? RawStaticCast(TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)) : TypeManager::GetInvalidTy(); return ie.ty; } isWellTyped = ie.elseBody && Ty::IsTyCorrect(Synthesize(ctx, ie.elseBody.get())) && isWellTyped; if (!isWellTyped) { ie.ty = TypeManager::GetInvalidTy(); return TypeManager::GetInvalidTy(); } ReplaceIdealTy(*ie.thenBody); ReplaceIdealTy(*ie.elseBody); ie.thenBody->ty = ReplaceThisTy(ie.thenBody->ty); ie.elseBody->ty = ReplaceThisTy(ie.elseBody->ty); auto thenTy = ie.thenBody->ty; auto elseTy = ie.elseBody->ty; if (Ty::IsTyCorrect(thenTy) && Ty::IsTyCorrect(elseTy)) { auto joinRes = JoinAndMeet(typeManager, {thenTy, elseTy}, {}, &importManager, ie.curFile).JoinAsVisibleTy(); if (auto optErrs = JoinAndMeet::SetJoinedType(ie.ty, joinRes)) { std::string errMsg = "types " + Ty::ToString(thenTy) + " and " + Ty::ToString(elseTy); errMsg = ie.sourceExpr && ie.sourceExpr->astKind == ASTKind::IF_AVAILABLE_EXPR ? errMsg + " of the two lambda of this '@IfAvailable' expression mismatch" : errMsg + " of the two branches of this 'if' expression mismatch"; diag.Diagnose(ie, DiagKind::sema_diag_report_error_message, errMsg).AddNote(*optErrs); } } return ie.ty; } bool TypeChecker::TypeCheckerImpl::ChkIfExpr(ASTContext& ctx, Ty& tgtTy, IfExpr& ie) { CJC_NULLPTR_CHECK(ie.condExpr); CJC_NULLPTR_CHECK(ie.thenBody); bool isWellTyped = CheckCondition(ctx, *ie.condExpr, false); if (IsIfExprWithoutElse(ie)) { isWellTyped = ChkIfExprNoElse(ctx, tgtTy, ie) && isWellTyped; } else { isWellTyped = ChkIfExprTwoBranches(ctx, tgtTy, ie) && isWellTyped; } if (!isWellTyped) { ie.ty = TypeManager::GetInvalidTy(); } return isWellTyped; } static std::vector> CollectVariables(const ASTContext& ctx, Node& n) { std::vector> varPatterns; Walker(&n, [&varPatterns, &ctx](Ptr node) { CJC_NULLPTR_CHECK(node); bool isVarPattern = node->astKind == ASTKind::VAR_PATTERN || (node->astKind == ASTKind::VAR_OR_ENUM_PATTERN && !ctx.IsEnumConstructor(StaticCast(*node).identifier)); if (isVarPattern) { varPatterns.emplace_back(node); return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }).Walk(); return varPatterns; } namespace { // any binding introduced by the pattern shouldn't be seen by the destructed expr // propagate this info for later lookup void PropagateCtxExpr(Pattern& pat, Expr& initializer) { Walker(&pat, [&initializer](Ptr n) { if (auto vp = DynamicCast(n)) { vp->ctxExpr = &initializer; } if (!Is(n)) { return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }).Walk(); } } bool TypeChecker::TypeCheckerImpl::SynLetPatternDestructor( ASTContext& ctx, LetPatternDestructor& lpd, bool suppressIntroducingVariableError) { CJC_NULLPTR_CHECK(lpd.initializer); Ptr boolTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); for (auto& p : lpd.patterns) { p->ctxExpr = lpd.initializer.get(); PropagateCtxExpr(*p, *p->ctxExpr); } Synthesize(ctx, lpd.initializer.get()); ReplaceIdealTy(*lpd.initializer); auto selectorTy = lpd.initializer->ty; // cannot have multiple pattern in one let with different astKind, e.g. let A|_ <- xxx // in this case, no need to check further if (!ChkPatternsSameASTKind(ctx, lpd.patterns)) { lpd.ty = TypeManager::GetInvalidTy(); return false; } // intended shortcut &&: no need to check var pattern if it has already been reported by parent AST bool good = !suppressIntroducingVariableError && ChkNoVarPatternInOrPattern(ctx, lpd.patterns); bool subpatternsGood{true}; for (auto& p : lpd.patterns) { subpatternsGood = ChkPattern(ctx, *selectorTy, *p) && subpatternsGood; } if (Ty::IsTyCorrect(selectorTy) && subpatternsGood) { lpd.ty = boolTy; return good; } else { lpd.ty = TypeManager::GetInvalidTy(); return false; } } bool TypeChecker::TypeCheckerImpl::CheckCondition(ASTContext& ctx, Expr& e, bool suppressIntroducingVariableError) { if (e.astKind == ASTKind::LET_PATTERN_DESTRUCTOR) { return SynLetPatternDestructor(ctx, StaticCast(e), suppressIntroducingVariableError); } if (auto bin = DynamicCast(&e)) { return CheckBinaryCondition(ctx, *bin, suppressIntroducingVariableError); } if (auto paren = DynamicCast(&e)) { bool res = CheckCondition(ctx, *paren->expr, suppressIntroducingVariableError); paren->ty = paren->expr->ty; return res; } Ptr boolTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); if (Check(ctx, boolTy, &e)) { return true; } auto shouldDiag = e.ShouldDiagnose() && !CanSkipDiag(e); if (shouldDiag) { DiagMismatchedTypes(diag, e, *boolTy); } return false; } bool TypeChecker::TypeCheckerImpl::CheckBinaryCondition( ASTContext& ctx, BinaryExpr& e, bool suppressIntroducingVariableError) { Ptr boolTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); if (!IsCondition(e)) { return Check(ctx, boolTy, &e); } if (!suppressIntroducingVariableError && e.op == TokenKind::OR) { auto vars = CollectVariables(ctx, e); if (!vars.empty()) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_var_in_or_condition, *vars.front()); auto iter = vars.cbegin() + 1; // Skip the first var as it has been reported in main hint. while (iter != vars.cend()) { builder.AddHint(**iter++); } suppressIntroducingVariableError = true; } } auto leftGood = CheckCondition(ctx, *e.leftExpr, suppressIntroducingVariableError); auto rightGood = CheckCondition(ctx, *e.rightExpr, suppressIntroducingVariableError); auto res = leftGood && rightGood; if (res) { e.ty = boolTy; } else { if (e.ShouldDiagnose() && !CanSkipDiag(e)) { DiagMismatchedTypes(diag, e, *boolTy); } e.ty = TypeManager::GetInvalidTy(); } res = res && !suppressIntroducingVariableError; return res; } bool TypeChecker::TypeCheckerImpl::ChkIfExprNoElse(ASTContext& ctx, Ty& target, IfExpr& ie) { Ptr unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); ie.ty = unitTy; Synthesize(ctx, ie.thenBody.get()); Synthesize(ctx, ie.elseBody.get()); // The ifExpr may only have 'then' branch or as the case that if-elseif without ending 'else' branch. bool isWellTyped = Ty::IsTyCorrect(ie.thenBody->ty) && (!ie.elseBody || Ty::IsTyCorrect(ie.elseBody->ty)); bool isTargetMatched = typeManager.IsSubtype(unitTy, &target); if (isWellTyped && !isTargetMatched) { DiagMismatchedTypesWithFoundTy( diag, ie, target, *unitTy, "the type of an 'if' expression without an 'else' branch is always 'Unit'"); } return isWellTyped && isTargetMatched; } bool TypeChecker::TypeCheckerImpl::ChkIfExprTwoBranches(ASTContext& ctx, Ty& target, IfExpr& ie) { // Now both thenBody and elseBody are guaranteed to be non-nullable. if (!Check(ctx, &target, ie.thenBody.get())) { if (ie.ShouldDiagnose() && !CanSkipDiag(*ie.thenBody) && !typeManager.IsSubtype(ie.thenBody->ty, &target)) { DiagMismatchedTypes(diag, *ie.thenBody, target); } } if (!Check(ctx, &target, ie.elseBody.get())) { if (ie.ShouldDiagnose() && !CanSkipDiag(*ie.elseBody) && !typeManager.IsSubtype(ie.elseBody->ty, &target)) { DiagMismatchedTypes(diag, *ie.elseBody, target); } } if (!Ty::IsTyCorrect(ie.thenBody->ty) || !Ty::IsTyCorrect(ie.elseBody->ty)) { return false; } ie.ty = ⌖ return true; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/IncOrDecExpr.cpp000066400000000000000000000041061510705540100241730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "DiagSuppressor.h" #include "Diags.h" #include "cangjie/AST/RecoverDesugar.h" using namespace Cangjie; using namespace AST; using namespace Sema; bool TypeChecker::TypeCheckerImpl::ChkIncOrDecExpr(ASTContext& ctx, Ty& target, IncOrDecExpr& ide) { if (!Ty::IsTyCorrect(SynIncOrDecExpr(ctx, ide))) { return false; } if (typeManager.IsSubtype(ide.ty, &target)) { return true; } DiagMismatchedTypesWithFoundTy(diag, ide, target, *ide.ty, "the type of an assignment expression is always 'Unit'"); ide.ty = TypeManager::GetInvalidTy(); return false; } Ptr TypeChecker::TypeCheckerImpl::SynIncOrDecExpr(ASTContext& ctx, IncOrDecExpr& ide) { if (ide.desugarExpr == nullptr) { // `ide` or parent of `ide` is broken. return TypeManager::GetInvalidTy(); } auto& ae = *StaticCast(ide.desugarExpr.get()); auto leftTy = Synthesize(ctx, ae.leftValue.get()); if (!Ty::IsTyCorrect(leftTy)) { ide.ty = TypeManager::GetInvalidTy(); } else if (leftTy->IsNothing()) { CJC_NULLPTR_CHECK(ae.rightExpr); ae.rightExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64); ae.ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); ide.ty = ae.ty; } else if (!leftTy->IsInteger()) { DiagMismatchedTypesWithFoundTy(diag, *ae.leftValue, "integer type", leftTy->String(), "the base of increment or decrement expressions should be of integer type"); ide.ty = TypeManager::GetInvalidTy(); } else { if (ae.leftValue->astKind == ASTKind::SUBSCRIPT_EXPR && ae.leftValue->desugarExpr != nullptr) { RecoverToSubscriptExpr(StaticCast(*ae.leftValue)); } ide.ty = Synthesize(ctx, ide.desugarExpr.get()); } return ide.ty; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/IsAsExprs.cpp000066400000000000000000000044741510705540100235770ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "Diags.h" using namespace Cangjie; using namespace Sema; Ptr TypeChecker::TypeCheckerImpl::SynIsExpr(ASTContext& ctx, IsExpr& ie) { if (Ty::IsTyCorrect(Synthesize(ctx, ie.leftExpr.get())) && Ty::IsTyCorrect(Synthesize(ctx, ie.isType.get())) && ReplaceIdealTy(*ie.leftExpr)) { ie.ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); } else { ie.ty = TypeManager::GetInvalidTy(); } return ie.ty; } bool TypeChecker::TypeCheckerImpl::ChkIsExpr(ASTContext& ctx, Ty& target, IsExpr& ie) { // Always type checking the expression even if the target type mismatches. auto ty = SynIsExpr(ctx, ie); if (!Ty::IsTyCorrect(ty)) { return false; } bool isWellTyped = ty->IsBoolean(); auto boolTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); if (!typeManager.IsLitBoxableType(boolTy, &target)) { DiagMismatchedTypesWithFoundTy(diag, ie, target, *boolTy); isWellTyped = false; } ie.ty = isWellTyped ? ie.ty : TypeManager::GetInvalidTy(); return isWellTyped; } Ptr TypeChecker::TypeCheckerImpl::SynAsExpr(ASTContext& ctx, AsExpr& ae) { if (Ty::IsTyCorrect(Synthesize(ctx, ae.leftExpr.get())) && Ty::IsTyCorrect(Synthesize(ctx, ae.asType.get())) && ReplaceIdealTy(*ae.leftExpr)) { auto optionDecl = RawStaticCast(importManager.GetCoreDecl("Option")); if (optionDecl) { ae.ty = typeManager.GetEnumTy(*optionDecl, {ae.asType->ty}); } else { diag.Diagnose(ae, DiagKind::sema_no_core_object); ae.ty = TypeManager::GetInvalidTy(); } } else { ae.ty = TypeManager::GetInvalidTy(); } return ae.ty; } bool TypeChecker::TypeCheckerImpl::ChkAsExpr(ASTContext& ctx, Ty& target, AsExpr& ae) { if (!Ty::IsTyCorrect(SynAsExpr(ctx, ae))) { return false; } if (!CheckOptionBox(target, *ae.ty)) { DiagMismatchedTypes(diag, ae, target); ae.ty = TypeManager::GetInvalidTy(); return false; } return true; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/JumpExpr.cpp000066400000000000000000000023001510705540100234520ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "cangjie/AST/ASTCasting.h" using namespace Cangjie; using namespace AST; namespace { // Find the closest loop expression where the jump expression locates. Ptr FindLoopExpr(const ASTContext& ctx, const JumpExpr& jumpExpr) { auto sym = ScopeManager::GetRefLoopSymbol(ctx, jumpExpr); return sym ? DynamicCast(sym->node) : nullptr; } } // namespace Ptr TypeChecker::TypeCheckerImpl::SynLoopControlExpr(const ASTContext& ctx, JumpExpr& je) const { je.refLoop = FindLoopExpr(ctx, je); // je.refLoop may be a null pointer, but the errors are already reported by CheckReturnAndJump in PreCheck je.ty = je.refLoop ? RawStaticCast(TypeManager::GetNothingTy()) : TypeManager::GetInvalidTy(); return je.ty; } bool TypeChecker::TypeCheckerImpl::ChkLoopControlExpr(const ASTContext& ctx, JumpExpr& je) const { SynLoopControlExpr(ctx, je); return je.refLoop != nullptr; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/LambdaExpr.cpp000066400000000000000000000414771510705540100237410ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "DiagSuppressor.h" #include "Diags.h" #include "TypeCheckUtil.h" #include "ExtraScopes.h" using namespace Cangjie; using namespace AST; using namespace Sema; using namespace TypeCheckUtil; using namespace Utils; namespace { // we should clear the nodes in the block and then recheck the body according to the new `target`. void ClearLambdaBodyForReCheck(const LambdaExpr& le) { Walker(le.funcBody->body.get(), [](Ptr node) { if (Is(node) || Is(node)) { // In the `PreCheck` stage: // The `Ty` of `Type` was set by `GetTyFromASTType`. // The `Ty` of `GenericParamDecl` was set by `SetDeclTy`. return VisitAction::SKIP_CHILDREN; } else if (Is(node) && node->TestAttr(AST::Attribute::HAS_INITIAL)) { // The sugar for default parameter was set by `AddDefaultFunction` in the `PrepareTypeCheck` stage. return VisitAction::SKIP_CHILDREN; } else { node->Clear(); return VisitAction::WALK_CHILDREN; } }).Walk(); } bool IsAnyParamTypeOmitted(const LambdaExpr& le) { CJC_ASSERT(le.funcBody && !le.funcBody->paramLists.empty()); auto& paramList = le.funcBody->paramLists[0]; return std::any_of(paramList->params.cbegin(), paramList->params.cend(), [](auto& param) { return param->type == nullptr; }); } void ClearCacheForNames(ASTContext& ctx, const LambdaExpr& le, const std::vector& paramNames) { std::vector> parentStack; std::unordered_set> clearedNodes; std::unordered_set aliases(paramNames.cbegin(), paramNames.cend()); // potential aliases of parameters std::unordered_map, std::vector> aliasDecl; std::unordered_map, std::vector> aliasShadow; // a simplistic version of symbol table auto shadowName = [&aliases, &aliasShadow](const LambdaExpr& le) { CJC_ASSERT(le.funcBody && !le.funcBody->paramLists.empty()); for (auto& param : le.funcBody->paramLists[0]->params) { CJC_NULLPTR_CHECK(param); auto id = param->identifier; if (aliases.count(id) > 0) { aliasShadow[&le].push_back(id); aliases.erase(id); } } }; auto preAction = [&ctx, &aliases, &clearedNodes, &parentStack, &shadowName](Ptr node) -> VisitAction { parentStack.push_back(node); if (auto re = DynamicCast(node); re && aliases.count(re->ref.identifier) > 0) { for (size_t i = parentStack.size(); i > 0; i--) { auto p = parentStack[i - 1]; if (clearedNodes.count(p) > 0) { break; } ctx.RemoveTypeCheckCache(*p); clearedNodes.insert(p); } } else if (auto lam = DynamicCast(node)) { shadowName(*lam); // shadow by local var is too complicated to track. skip here } return aliases.empty() ? VisitAction::SKIP_CHILDREN : VisitAction::WALK_CHILDREN; }; auto postAction = [&aliases, &aliasDecl, &aliasShadow, &parentStack, &clearedNodes]( Ptr /* node */) -> VisitAction { auto top = parentStack.back(); parentStack.pop_back(); if (auto decl = DynamicCast(top); decl && clearedNodes.count(decl) > 0) { aliases.insert(decl->identifier); // VarDecl is cleared ==> it contains alias ==> the var is a new alias aliasDecl[parentStack.back()].push_back(decl->identifier); } for (auto name : aliasShadow[top]) { aliases.insert(name); } for (auto name : aliasDecl[top]) { aliases.erase(name); } return VisitAction::WALK_CHILDREN; }; Walker(le.funcBody->body.get(), preAction, postAction).Walk(); } void ClearInvalidTypeCheckCache(ASTContext& ctx, const LambdaExpr& le, const Ty& target) { if ((ctx.lastTargetTypeMap.count(&le) == 0 || ctx.lastTargetTypeMap[&le] != &target) && IsAnyParamTypeOmitted(le)) { std::vector names; CJC_ASSERT(le.funcBody && !le.funcBody->paramLists.empty()); for (auto& param : le.funcBody->paramLists[0]->params) { CJC_NULLPTR_CHECK(param); if (!param->type) { names.push_back(param->identifier); } } ClearCacheForNames(ctx, le, names); } } // Return true if lambda has any mismatched parameter type and any of the parameter's type is omitted. bool IsLambdaIncompatible(const LambdaExpr& le, bool paramsMismatched) { return paramsMismatched && IsAnyParamTypeOmitted(le); } // return: true if error, false if no error bool DiagInferParamTyFail(DiagnosticEngine& diag, LambdaExpr& le) { for (auto& node : le.funcBody->paramLists[0]->params) { if (!node->type) { diag.DiagnoseRefactor(DiagKindRefactor::sema_lambdaExpr_must_have_type_annotation, *node); le.ty = TypeManager::GetInvalidTy(); return true; } } return false; } // if it's a member func call to a param, return the param's id and possible type decls // otherwise return empty string and empty set std::pair>> FindCandidatesFromCall( ASTContext& ctx, const CallExpr& ce, const std::map>& unsolvedParams) { if (auto ma = DynamicCast(ce.baseFunc.get())) { if (auto re = DynamicCast(ma->baseExpr.get())) { if (unsolvedParams.count(re->ref.identifier) > 0) { auto id = re->ref.identifier; auto sig = MemSig{ma->field, false, ce.args.size(), ma->typeArguments.size()}; return {id, ctx.Mem2Decls(sig)}; } } } return {"", {}}; } // if it's a member access to a param, return the param's id and possible type decls // otherwise return empty string and empty set std::pair>> FindCandidatesFromAccess( ASTContext& ctx, MemberAccess& ma, const std::map>& unsolvedParams) { if (auto re = DynamicCast(ma.baseExpr.get())) { if (unsolvedParams.count(re->ref.identifier) > 0) { auto id = re->ref.identifier; auto sig = MemSig{ma.field, true}; return {id, ctx.Mem2Decls(sig)}; } } return {"", {}}; } } // namespace void TypeChecker::TypeCheckerImpl::ResetLambdaForReinfer(ASTContext& ctx, const AST::LambdaExpr& le) { le.funcBody->retType.reset(nullptr); AddRetTypeNode(*le.funcBody); le.funcBody->Clear(); Walker(le.funcBody->body.get(), [](Ptr node) { if ((Is(node) || Is(node)) && !Ty::IsInitialTy(node->ty)) { node->Clear(); } return VisitAction::WALK_CHILDREN; }).Walk(); ctx.ClearTypeCheckCache(*le.funcBody); } bool TypeChecker::TypeCheckerImpl::SolveLamExprParamTys(ASTContext& ctx, AST::LambdaExpr& le) { auto tyVars = typeManager.GetInnermostUnsolvedTyVars(); auto sol = SolveConstraints(typeManager.constraints); bool successful = false; if (sol && !HasUnsolvedTyVars(*sol, tyVars)) { ResetLambdaForReinfer(ctx, le); for (auto& node : le.funcBody->paramLists[0]->params) { node->ty = typeManager.GetInstantiatedTy(node->ty, *sol); } if (Ty::IsTyCorrect(Synthesize(ctx, le.funcBody.get())) && le.funcBody->body && Ty::IsTyCorrect(le.funcBody->body->ty)) { le.ty = le.funcBody->ty; successful = true; } } if (!successful && DiagInferParamTyFail(diag, le)) { return false; } return true; } void TypeChecker::TypeCheckerImpl::TryInferFromSyntaxInfo(ASTContext& ctx, const AST::LambdaExpr& le) { if (typeManager.GetInnermostUnsolvedTyVars().empty()) { return; } std::map> unsolvedParams; std::map>> candidates; for (auto& param : le.funcBody->paramLists[0]->params) { if (param->ty->IsPlaceholder()) { unsolvedParams[param->identifier] = param; } } if (unsolvedParams.empty()) { return; } auto candiHandler = [&candidates](const std::pair>>& candi) { if (candi.first.empty()) { return false; } if (candidates.count(candi.first) == 0) { candidates[candi.first] = candi.second; } else { EraseIf(candidates[candi.first], [&candi](Ptr d) { return candi.second.count(d) == 0; }); } return true; }; std::function)> memberScanner = [&ctx, &unsolvedParams, &candiHandler, &memberScanner]( Ptr n) -> VisitAction { if (auto ce = DynamicCast(n)) { if (candiHandler(FindCandidatesFromCall(ctx, *ce, unsolvedParams))) { // need to skip baseFunc to avoid handling it again as an access to var/prop for (auto& arg : ce->args) { Walker(arg.get(), memberScanner).Walk(); } return VisitAction::SKIP_CHILDREN; } } else if (auto ma = DynamicCast(n)) { candiHandler(FindCandidatesFromAccess(ctx, *ma, unsolvedParams)); } return VisitAction::WALK_CHILDREN; }; Walker(le.funcBody.get(), memberScanner).Walk(); for (auto& [id, decls] : candidates) { TryEnforceCandidate(*StaticCast(unsolvedParams[id]->ty), decls, typeManager); } } Ptr TypeChecker::TypeCheckerImpl::SynLamExpr(ASTContext& ctx, LambdaExpr& le) { if (le.funcBody == nullptr || le.funcBody->paramLists.empty()) { return TypeManager::GetInvalidTy(); } // Lambda's return type is added by compiler, so reset it here to allow re-checking. le.funcBody->retType.reset(nullptr); AddRetTypeNode(*le.funcBody); le.funcBody->retType->begin = le.begin; le.funcBody->retType->end = le.end; CJC_ASSERT(le.funcBody && !le.funcBody->paramLists.empty()); TyVarScope sc(typeManager); for (auto& node : le.funcBody->paramLists[0]->params) { CJC_NULLPTR_CHECK(node); Ptr ty = Synthesize(ctx, node->type.get()); if (Ty::IsTyCorrect(ty)) { node->ty = ty; } else if (ctx.funcArgReachable.count(&le) == 0 && !node->type) { node->ty = typeManager.AllocTyVar("T-Lam", true); } else { diag.DiagnoseRefactor(DiagKindRefactor::sema_lambdaExpr_must_have_type_annotation, *node); le.ty = TypeManager::GetInvalidTy(); return le.ty; } } TryInferFromSyntaxInfo(ctx, le); bool skipSolving = typeManager.GetInnermostUnsolvedTyVars().empty(); Ptr leTy = nullptr; { DiagSuppressor ds(diag); leTy = Synthesize(ctx, le.funcBody.get()); if (skipSolving) { ds.ReportDiag(); } } // `funcBody` containing invalid expressions can have valid types. // We have to use `funcBody->body` to determine if there are errors. if (!Ty::IsTyCorrect(leTy) || !le.funcBody->body || !Ty::IsTyCorrect(le.funcBody->body->ty)) { le.ty = TypeManager::GetInvalidTy(); if (DiagInferParamTyFail(diag, le)) { return le.ty; } } else { if (skipSolving) { le.ty = le.funcBody->ty; } else { if (!SolveLamExprParamTys(ctx, le)) { return le.ty; } } } if (auto ft = DynamicCast(le.ty)) { le.funcBody->retType->ty = ft->retTy; } else { le.funcBody->retType->ty = TypeManager::GetInvalidTy(); } return le.ty; } bool TypeChecker::TypeCheckerImpl::ChkLamExpr(ASTContext& ctx, Ty& target, LambdaExpr& le) { CJC_ASSERT(le.funcBody != nullptr && !le.funcBody->paramLists.empty()); ClearInvalidTypeCheckCache(ctx, le, target); ctx.lastTargetTypeMap[&le] = ⌖ if (target.IsAny() || (le.TestAttr(AST::Attribute::C) && target.IsCType())) { le.ty = Synthesize(ctx, &le); (void)ReplaceIdealTy(le); return Ty::IsTyCorrect(le.ty); } Ptr targetTy = TypeCheckUtil::UnboxOptionType(&target); if (!Ty::IsTyCorrect(targetTy) || !targetTy->IsFunc()) { auto ds = DiagSuppressor(diag); auto synTy = Synthesize(ctx, &le); le.ty = TypeManager::GetInvalidTy(); if (!ds.HasError() && Ty::IsTyCorrect(synTy)) { // Only report type mismatch when no error happens. DiagMismatchedTypesWithFoundTy(diag, le, target, *synTy); } ds.ReportDiag(); return false; } { // Suppress errors if check lambda failed. Only report when type check succeed. auto ds = DiagSuppressor(diag); auto tgtTy = StaticCast(targetTy); std::vector> lamParamTys; // Check arguments' types against parameters' types. bool paramsMatched = ChkLamParamTys(ctx, le, tgtTy->paramTys, lamParamTys); // In the check mode, a lambda's return type is explicitly given. // To ease the following check, we create a retType node from the given target type. le.funcBody->retType = MakeOwned(); le.funcBody->retType->begin = le.begin; le.funcBody->retType->end = le.end; le.funcBody->retType->ty = tgtTy->retTy; le.funcBody->retType->EnableAttr(AST::Attribute::COMPILER_ADD); ClearLambdaBodyForReCheck(le); // Should not be short-circuited. if (ChkLamBody(ctx, *le.funcBody) && paramsMatched) { ds.ReportDiag(); // The call to GetFunctionTy is necessary to create (cached) CPointer types if necessary. le.funcBody->ty = typeManager.GetFunctionTy(lamParamTys, StaticCast(le.funcBody->ty)->retTy, {tgtTy->isC, tgtTy->isClosureTy, tgtTy->hasVariableLenArg}); le.ty = le.funcBody->ty; return true; } else if (IsLambdaIncompatible(le, !paramsMatched)) { // User omitted parameter's type. We should quit early. ds.ReportDiag(); return false; } } // In the LSP that uses macros, the ast before the macro expansion needs to save the complete ty information to // prevent 'Synthesize' skipping child nodes in the lambda body due to cache. if (!ci->invocation.globalOptions.enableMacroInLSP) { ClearLambdaBodyForReCheck(le); } le.funcBody->retType.reset(nullptr); auto bodyTy = Synthesize(ctx, le.funcBody.get()); if (Ty::IsTyCorrect(bodyTy) && !typeManager.IsSubtype(bodyTy, targetTy)) { DiagMismatchedTypesWithFoundTy(diag, le, *targetTy, *bodyTy); } le.funcBody->ty = TypeManager::GetInvalidTy(); le.ty = le.funcBody->ty; return false; } bool TypeChecker::TypeCheckerImpl::ChkLamParamTys( ASTContext& ctx, LambdaExpr& le, const std::vector>& tgtParamTys, std::vector>& lamParamTys) { CJC_ASSERT(le.funcBody && !le.funcBody->paramLists.empty()); if (le.funcBody->paramLists[0]->params.size() != tgtParamTys.size()) { auto& paramList = le.funcBody->paramLists[0]; auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_param_miss_match, *paramList); builder.AddMainHintArguments(std::to_string(tgtParamTys.size()), std::to_string(paramList->params.size())); return false; } size_t i = 0; for (auto& node : le.funcBody->paramLists[0]->params) { CJC_NULLPTR_CHECK(node); Ptr paramTy = tgtParamTys[i]; // Caller guarantees the 'paramTy' is valid. if (!Check(ctx, paramTy, node.get())) { le.ty = TypeManager::GetInvalidTy(); return false; } // Lambda's parameter type not support auto box. // NOTE: lambda should not report parameter type mismatch, only report lambda type mismatchs. if (!typeManager.IsSubtype(paramTy, node->ty, false, false)) { le.ty = TypeManager::GetInvalidTy(); return false; } lamParamTys.push_back(node->ty); i++; } return true; } bool TypeChecker::TypeCheckerImpl::ChkLamBody(ASTContext& ctx, FuncBody& lamFb) { if (CheckFuncBody(ctx, lamFb) && Ty::IsTyCorrect(lamFb.ty) && Ty::IsTyCorrect(lamFb.body->ty)) { return true; } // Since the return type of lambda body is added in 'ChkLamExpr', all type mismatching errors are reported before. lamFb.ty = TypeManager::GetInvalidTy(); return false; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/LitConstExpr.cpp000066400000000000000000000240731510705540100243110ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "Diags.h" #include "TypeCheckUtil.h" using namespace Cangjie; using namespace Sema; using namespace TypeCheckUtil; namespace { Ptr GetInnerNumericType(Ty& boxTy, Ty& basicTy) { // This function only used after type was checked by "IsLitBoxableType" for integer and float LitConst. // that guaranteed boxTy must be N-dims Option where T is numeric type. if (!boxTy.IsEnum() || boxTy.HasInvalidTy()) { return boxTy.IsNumeric() ? &boxTy : &basicTy; } auto enumTy = RawStaticCast(&boxTy); CJC_ASSERT(!enumTy->typeArgs.empty()); return GetInnerNumericType(*enumTy->typeArgs[0], basicTy); } } // namespace bool TypeChecker::TypeCheckerImpl::ChkLitConstExprOfTypeBool(Ty& target, LitConstExpr& lce) { if (target.IsBooleanSubType()) { lce.ty = ⌖ return true; } else if (typeManager.IsLitBoxableType(TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN), &target)) { lce.ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); return true; } else if (target.IsAny() || target.IsCType()) { lce.ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); return true; } else { diag.Diagnose(lce, DiagKind::sema_cannot_convert_literal, "a boolean", target.String()); lce.ty = TypeManager::GetNonNullTy(lce.ty); return false; } } bool TypeChecker::TypeCheckerImpl::ChkLitConstExprOfTypeUnit(Ty& target, LitConstExpr& lce) { if (target.IsAny() || target.IsUnit() || target.IsCType() || typeManager.IsLitBoxableType(TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT), &target)) { lce.ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); return true; } else { DiagMismatchedTypesWithFoundTy(diag, lce, target.String(), "Unit"); lce.ty = TypeManager::GetInvalidTy(); return false; } } bool TypeChecker::TypeCheckerImpl::ChkLitConstExprOfTypeInteger(Ty& target, LitConstExpr& lce) { TypeKind intSuffixTokenKind = lce.GetNumLitTypeKind(); TypeKind defaultIntTokenKind = intSuffixTokenKind == TypeKind::TYPE_IDEAL_INT ? TypeKind::TYPE_INT64 : intSuffixTokenKind; if (target.IsIntegerSubType()) { if (intSuffixTokenKind == target.kind || intSuffixTokenKind == TypeKind::TYPE_IDEAL_INT) { lce.ty = ⌖ return true; } else { diag.Diagnose(lce, DiagKind::sema_cannot_convert_literal, lce.stringValue, target.String()); return false; } } else if (typeManager.IsLitBoxableType(TypeManager::GetPrimitiveTy(defaultIntTokenKind), &target)) { // Check for extendable or option boxable type as int64. lce.ty = GetInnerNumericType(target, *TypeManager::GetPrimitiveTy(defaultIntTokenKind)); return true; } else if (target.IsAny() || target.IsCType()) { lce.ty = GetInnerNumericType(target, *TypeManager::GetPrimitiveTy(defaultIntTokenKind)); return true; } else if (!target.IsInvalid()) { diag.Diagnose(lce, DiagKind::sema_cannot_convert_literal, "an integer", target.String()); lce.ty = TypeManager::GetInvalidTy(); return false; } lce.ty = TypeManager::GetNonNullTy(lce.ty); return false; } bool TypeChecker::TypeCheckerImpl::ChkLitConstExprOfTypeFloat(Ty& targetTy, LitConstExpr& lce) { TypeKind intSuffixTokenKind = lce.GetNumLitTypeKind(); TypeKind defaultFloat64TokenKind = intSuffixTokenKind == TypeKind::TYPE_IDEAL_FLOAT ? TypeKind::TYPE_FLOAT64 : intSuffixTokenKind; if (targetTy.IsFloatingSubType()) { if (intSuffixTokenKind == targetTy.kind || intSuffixTokenKind == TypeKind::TYPE_IDEAL_FLOAT) { lce.ty = &targetTy; return true; } else { diag.Diagnose(lce, DiagKind::sema_cannot_convert_literal, lce.stringValue, targetTy.String()); return false; } } else if (typeManager.IsLitBoxableType(TypeManager::GetPrimitiveTy(defaultFloat64TokenKind), &targetTy)) { // Check for extendable or option boxable type as float64. lce.ty = GetInnerNumericType(targetTy, *TypeManager::GetPrimitiveTy(defaultFloat64TokenKind)); return true; } else if (targetTy.IsAny() || targetTy.IsCType()) { lce.ty = GetInnerNumericType(targetTy, *TypeManager::GetPrimitiveTy(defaultFloat64TokenKind)); return true; } else { diag.Diagnose(lce, DiagKind::sema_cannot_convert_literal, "a floating-point", targetTy.String()); lce.ty = TypeManager::GetNonNullTy(lce.ty); return false; } } bool TypeChecker::TypeCheckerImpl::ChkLitConstExprOfTypeChar(Ty& targetTy, LitConstExpr& lce) { if (&targetTy == TypeManager::GetPrimitiveTy(TypeKind::TYPE_RUNE)) { lce.ty = &targetTy; return true; } else if (typeManager.IsLitBoxableType(TypeManager::GetPrimitiveTy(TypeKind::TYPE_RUNE), &targetTy)) { lce.ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_RUNE); return true; } else if (targetTy.IsAny()) { lce.ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_RUNE); return true; } else { diag.Diagnose(lce, DiagKind::sema_cannot_convert_literal, "a character", targetTy.String()); lce.ty = TypeManager::GetNonNullTy(lce.ty); return false; } } Ptr TypeChecker::TypeCheckerImpl::SynLitConstStringExpr(ASTContext& ctx, LitConstExpr& lce) { // For string literal expr. if (!lce.siExpr) { lce.ty = Synthesize(ctx, lce.ref.get()); return lce.ty; } // For String Interpolation. // 1. Get Struct-String and Interface-ToString type. auto stringDecl = importManager.GetCoreDecl(STD_LIB_STRING); auto toStringInterface = importManager.GetCoreDecl(TOSTRING_NAME); if (!stringDecl || !toStringInterface) { lce.ty = TypeManager::GetInvalidTy(); return lce.ty; } // 2. Check all interpolated expressions. auto strExpr = lce.siExpr.get(); bool isWellTyped = true; for (auto& expr : strExpr->strPartExprs) { if (expr->astKind != ASTKind::INTERPOLATION_EXPR) { isWellTyped = Check(ctx, stringDecl->ty, expr.get()) && isWellTyped; continue; } auto ie = StaticCast(expr.get()); CJC_NULLPTR_CHECK(ie->block); ie->block->ty = Synthesize(ctx, ie->block.get()); if (!typeManager.IsSubtype(ie->block->ty, toStringInterface->ty)) { if (Ty::IsTyCorrect(ie->block->ty)) { diag.Diagnose(*ie->block, DiagKind::sema_invalid_string_implementation, ie->block->ty->String()); } isWellTyped = false; } else if (ie->block->ty->IsNothing()) { // After typechecker, it will desugar as 'expr.toString()', and Nothing type can't access member. diag.DiagnoseRefactor(DiagKindRefactor::sema_undeclared_identifier, *ie->block, "toString"); isWellTyped = false; } if (!isWellTyped) { ie->block->ty = TypeManager::GetInvalidTy(); } ie->ty = ie->block->ty; } // If not all interpolated expression check passed, directly quit current check. if (!isWellTyped) { lce.ty = TypeManager::GetInvalidTy(); return lce.ty; } lce.siExpr->ty = stringDecl->ty; lce.ty = stringDecl->ty; return lce.ty; } bool TypeChecker::TypeCheckerImpl::ChkLitConstExprOfTypeString(ASTContext& ctx, Ty& target, LitConstExpr& lce) { auto ty = SynLitConstStringExpr(ctx, lce); bool isWellTyped = typeManager.IsLitBoxableType(ty, &target) || target.IsAny(); if (!isWellTyped && !CanSkipDiag(lce)) { DiagMismatchedTypesWithFoundTy( diag, lce, target.String(), lce.stringKind == StringKind::JSTRING ? "JString" : "Struct-String"); } return isWellTyped; } bool TypeChecker::TypeCheckerImpl::ChkLitConstExpr(ASTContext& ctx, Ty& target, LitConstExpr& lce) { switch (lce.kind) { case LitConstKind::BOOL: return ChkLitConstExprOfTypeBool(target, lce); case LitConstKind::UNIT: return ChkLitConstExprOfTypeUnit(target, lce); case LitConstKind::INTEGER: case LitConstKind::RUNE_BYTE: return ChkLitConstExprOfTypeInteger(target, lce) && ChkLitConstExprRange(lce); case LitConstKind::FLOAT: return ChkLitConstExprOfTypeFloat(target, lce) && ChkLitConstExprRange(lce); case LitConstKind::RUNE: return ChkLitConstExprOfTypeChar(target, lce); case LitConstKind::STRING: case LitConstKind::JSTRING: return ChkLitConstExprOfTypeString(ctx, target, lce); default: lce.ty = TypeManager::GetInvalidTy(); return false; } } Ptr TypeChecker::TypeCheckerImpl::SynLitConstExpr(ASTContext& ctx, LitConstExpr& lce) { switch (lce.kind) { case LitConstKind::BOOL: lce.ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); break; case LitConstKind::UNIT: lce.ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); break; case LitConstKind::INTEGER: case LitConstKind::RUNE_BYTE: case LitConstKind::FLOAT: if (TypeKind kind = lce.GetNumLitTypeKind(); kind == TypeKind::TYPE_INVALID) { lce.ty = TypeManager::GetInvalidTy(); } else { lce.ty = TypeManager::GetPrimitiveTy(kind); } break; case LitConstKind::STRING: case LitConstKind::JSTRING: lce.ty = SynLitConstStringExpr(ctx, lce); break; case LitConstKind::RUNE: lce.ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_RUNE); break; case LitConstKind::NONE: lce.ty = TypeManager::GetInvalidTy(); break; } ChkLitConstExprRange(lce); return lce.ty; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/LoopExprs.cpp000066400000000000000000000121201510705540100236340ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "Diags.h" #include "TypeCheckUtil.h" using namespace Cangjie; using namespace Sema; using namespace TypeCheckUtil; namespace { Ptr GetIterableTy(TypeManager& tyMgr, ImportManager& importManager, Promotion& promotion, Ty& ty) { // Promote implemented iterable type except nothing type. if (ty.IsNothing()) { return TypeManager::GetInvalidTy(); } auto iterableInterface = importManager.GetCoreDecl("Iterable"); if (auto genTy = DynamicCast(&ty); genTy && genTy->isPlaceholder) { if (auto placeholderItTy = tyMgr.ConstrainByCtor(*genTy, *iterableInterface->ty)) { return placeholderItTy; } else { return TypeManager::GetInvalidTy(); } } if (iterableInterface) { auto prTys = promotion.Promote(ty, *iterableInterface->ty); CJC_ASSERT(prTys.size() <= 1); return prTys.empty() ? TypeManager::GetInvalidTy() : *prTys.begin(); } return TypeManager::GetInvalidTy(); } } // namespace bool TypeChecker::TypeCheckerImpl::ChkWhileExpr(ASTContext& ctx, Ty& target, WhileExpr& we) { Ptr unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); bool isWellTyped = typeManager.IsSubtype(unitTy, &target); if (!isWellTyped) { DiagMismatchedTypesWithFoundTy(diag, we, target, *unitTy); } isWellTyped = Ty::IsTyCorrect(SynWhileExpr(ctx, we)) && isWellTyped; return isWellTyped; } Ptr TypeChecker::TypeCheckerImpl::SynWhileExpr(ASTContext& ctx, WhileExpr& we) { bool isWellTyped = CheckCondition(ctx, *we.condExpr, false); isWellTyped = Ty::IsTyCorrect(Synthesize(ctx, we.body.get())) && isWellTyped; we.ty = isWellTyped ? StaticCast(TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)) : TypeManager::GetInvalidTy(); return we.ty; } bool TypeChecker::TypeCheckerImpl::ChkDoWhileExpr(ASTContext& ctx, Ty& target, DoWhileExpr& dwe) { Ptr unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); bool isWellTyped = typeManager.IsSubtype(unitTy, &target); if (!isWellTyped) { DiagMismatchedTypesWithFoundTy(diag, dwe, target, *unitTy); } isWellTyped = Ty::IsTyCorrect(SynDoWhileExpr(ctx, dwe)) && isWellTyped; return isWellTyped; } Ptr TypeChecker::TypeCheckerImpl::SynDoWhileExpr(ASTContext& ctx, DoWhileExpr& dwe) { bool isWellTyped = Ty::IsTyCorrect(Synthesize(ctx, dwe.body.get())); isWellTyped = CheckCondition(ctx, *dwe.condExpr, false) && isWellTyped; dwe.ty = isWellTyped ? StaticCast(TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)) : TypeManager::GetInvalidTy(); return dwe.ty; } bool TypeChecker::TypeCheckerImpl::ChkForInExpr(ASTContext& ctx, Ty& target, ForInExpr& fie) { Ptr unitTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); bool isWellTyped = typeManager.IsSubtype(unitTy, &target); if (!isWellTyped) { DiagMismatchedTypesWithFoundTy(diag, fie, target, *unitTy); } isWellTyped = Ty::IsTyCorrect(SynForInExpr(ctx, fie)) && isWellTyped; if (!isWellTyped) { fie.ty = TypeManager::GetInvalidTy(); } return isWellTyped; } Ptr TypeChecker::TypeCheckerImpl::SynForInExpr(ASTContext& ctx, ForInExpr& fie) { CJC_NULLPTR_CHECK(fie.inExpression); CJC_NULLPTR_CHECK(fie.pattern); bool isWellTyped = Synthesize(ctx, fie.inExpression.get()) && ReplaceIdealTy(*fie.inExpression); // Implemented iterable in stdlib. CJC_NULLPTR_CHECK(fie.inExpression->ty); Ptr iterableTy = GetIterableTy(typeManager, importManager, promotion, *fie.inExpression->ty); Ptr inPatternTy = TypeManager::GetInvalidTy(); if (Ty::IsTyCorrect(iterableTy)) { CJC_ASSERT(!iterableTy->typeArgs.empty()); inPatternTy = iterableTy->typeArgs[0]; } else { isWellTyped = false; if (!CanSkipDiag(*fie.inExpression)) { diag.Diagnose( *fie.inExpression, DiagKind::sema_expr_in_forin_must_has_iterator, Ty::ToString(fie.inExpression->ty)); } } isWellTyped = Check(ctx, inPatternTy, fie.pattern.get()) && isWellTyped; if (fie.patternGuard) { // PatternGuard's ty should be boolean. if (!Check(ctx, TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN), fie.patternGuard.get())) { isWellTyped = false; if (!CanSkipDiag(*fie.patternGuard)) { diag.Diagnose(*fie.patternGuard, DiagKind::sema_wrong_forin_guard); } } } isWellTyped = Ty::IsTyCorrect(Synthesize(ctx, fie.body.get())) && isWellTyped; if (!IsIrrefutablePattern(*fie.pattern)) { isWellTyped = false; diag.Diagnose(fie, DiagKind::sema_forin_pattern_must_be_irrefutable); } fie.ty = isWellTyped ? StaticCast(TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)) : TypeManager::GetInvalidTy(); return fie.ty; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/NameReferenceExpr.cpp000066400000000000000000001324411510705540100252500ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "DiagSuppressor.h" #include "Diags.h" #include "EnumSugarChecker.h" #include "EnumSugarTargetsFinder.h" #include "LocalTypeArgumentSynthesis.h" #include "TypeCheckUtil.h" #include "ExtraScopes.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/Basic/Match.h" #include "cangjie/Modules/ModulesUtils.h" #include "cangjie/Macro/TestEntryConstructor.h" using namespace Cangjie; using namespace Sema; using namespace TypeCheckUtil; using namespace Meta; namespace { bool IsEnumNeedSynthesis(Expr& expr, const Decl& target) { if (!target.TestAttr(AST::Attribute::ENUM_CONSTRUCTOR) || !target.GetGeneric()) { return false; } Ptr ref = &expr; if (auto ma = DynamicCast(&expr); ma) { ref = ma->baseExpr.get(); } return ref && NeedFurtherInstantiation(ref->GetTypeArgs()); } bool CheckInferrableEnumReference(DiagnosticEngine& diag, TypeManager& tyMgr, Expr& expr, Ty& target) { auto res = EnumSugarTargetsFinder::RefineTargetTy(tyMgr, &target, expr.GetTarget()); if (!res.has_value()) { diag.Diagnose(expr, DiagKind::sema_generic_type_without_type_argument); return false; } auto targetTy = *res; if (target.IsInterface()) { auto candiTys = Promotion(tyMgr).Downgrade(*targetTy, target); if (candiTys.empty()) { DiagUnableToInferExpr(diag, expr); return false; } targetTy = *candiTys.begin(); } expr.ty = targetTy; Ptr ref = &expr; if (auto ma = DynamicCast(&expr); ma) { ref = ma->baseExpr.get(); ref->ty = targetTy; // Type of enum base is same with the expression's ty. } if (auto reference = DynamicCast(ref)) { reference->instTys.clear(); for (auto it : targetTy->typeArgs) { (void)reference->instTys.emplace_back(it); } } return true; } bool CheckNonFunctionReference(DiagnosticEngine& diag, TypeManager& tyMgr, Ty& target, Expr& refNode) { auto refTarget = refNode.GetTarget(); bool checkEnumCtor = refTarget && refTarget->TestAttr(AST::Attribute::ENUM_CONSTRUCTOR); if (checkEnumCtor && IsEnumNeedSynthesis(refNode, *refTarget)) { // Check and update type for enum target without typeArgument. return CheckInferrableEnumReference(diag, tyMgr, refNode, target); } bool isWellTyped = tyMgr.IsSubtype(refNode.ty, &target); if (!isWellTyped) { DiagMismatchedTypes(diag, refNode, target); refNode.ty = TypeManager::GetInvalidTy(); } return isWellTyped; } ASTKind GetTargetsSameASTKind(const std::vector>& targets) { ASTKind ret = ASTKind::INVALID_DECL; for (auto& it : targets) { CJC_NULLPTR_CHECK(it); if (ret == ASTKind::INVALID_DECL && it->astKind != ASTKind::INVALID_DECL) { ret = it->astKind; } if (ret == it->astKind) { continue; } else { return ASTKind::INVALID_DECL; } } return ret; } ASTKind GetTargetsSameASTKind(std::unordered_map, std::vector>>& allTargets) { ASTKind ret = ASTKind::INVALID_DECL; for (auto& it : allTargets) { auto kind = GetTargetsSameASTKind(it.second); if (kind == ASTKind::INVALID_DECL || (ret != ASTKind::INVALID_DECL && ret != kind)) { return ASTKind::INVALID_DECL; } ret = kind; } return ret; } std::unordered_map, std::unordered_set>> GetUpperBoundTargetsWithGivenKind( const std::unordered_map, std::vector>>& allTargets, const std::unordered_set& kinds) { std::unordered_map, std::unordered_set>> results; for (auto [ty, targets] : allTargets) { for (auto target : targets) { if (Utils::In(target->astKind, kinds)) { results[target].emplace(ty); } } } return results; } bool IsCloserToImpl(const Decl& src, const Decl& target) { // If the current decl is not abstract and previous is abstract, replace previous one. bool updateAbstract = src.TestAttr(Attribute::ABSTRACT) && !target.TestAttr(Attribute::ABSTRACT); // If the current decl is not in interface, replace previous one. bool updateNonInterface = !src.TestAttr(Attribute::ABSTRACT) && !target.TestAttr(Attribute::ABSTRACT) && target.outerDecl && target.outerDecl->astKind != ASTKind::INTERFACE_DECL; return updateAbstract || updateNonInterface; } std::map, Ptr>::const_iterator FoundSameSignatureMember( TypeManager& tyMgr, const Decl& decl, FuncTy& funcTy, std::map, Ptr>& methodSigs) { auto curTy = &funcTy; std::map, Ptr>::const_iterator found = methodSigs.find(curTy); if (found != methodSigs.cend()) { return found; } if (!decl.TestAttr(Attribute::GENERIC)) { return methodSigs.cend(); } std::unordered_set> decls; for (auto method : std::as_const(methodSigs)) { if (method.second->TestAttr(Attribute::GENERIC)) { decls.emplace(method.second); } } for (auto it : decls) { // Substitute generic types for generic function. TypeSubst typeMapping = tyMgr.GenerateGenericMappingFromGeneric(decl, *it); // Checking whether substituted function signature is existed in map. curTy = StaticCast(tyMgr.GetInstantiatedTy(&funcTy, typeMapping)); found = methodSigs.find(curTy); if (found != methodSigs.cend()) { return found; } } return methodSigs.cend(); } FuncSig2Decl::const_iterator FoundSameSignatureMember( TypeManager& tyMgr, const Decl& decl, FuncTy& funcTy, FuncSig2Decl& methodSigs) { auto keyPair = std::make_pair(decl.identifier, funcTy.paramTys); FuncSig2Decl::const_iterator found = methodSigs.find(keyPair); if (found != methodSigs.cend()) { return found; } if (!decl.TestAttr(Attribute::GENERIC)) { return methodSigs.cend(); } std::unordered_set> decls; for (auto method : std::as_const(methodSigs)) { if (method.second->TestAttr(Attribute::GENERIC)) { decls.emplace(method.second); } } for (auto it : decls) { // Substitute generic types for generic function. TypeSubst typeMapping = tyMgr.GenerateGenericMappingFromGeneric(decl, *it); // Checking whether substituted function signature is existed in map. auto instTy = StaticCast(tyMgr.GetInstantiatedTy(&funcTy, typeMapping)); keyPair = std::make_pair(decl.identifier, instTy->paramTys); found = methodSigs.find(keyPair); if (found != methodSigs.cend()) { return found; } } return methodSigs.cend(); } std::vector> MergeFuncTargetsInUpperBounds(TypeManager& tyMgr, const MemberAccess& ma) { // We need to check upperbound members in a fixed order. OrderedDeclSet upperDecls; for (auto it : ma.foundUpperBoundMap) { upperDecls.emplace(it.first); } // Functions found in upperbounds which have same signature must have only one valid implementation. // So, classify functions by function signature. std::unordered_set> targets; FuncSig2Decl methodSigs; for (auto decl : upperDecls) { CJC_NULLPTR_CHECK(decl); if (decl->astKind != ASTKind::FUNC_DECL) { continue; } MultiTypeSubst mts; tyMgr.GenerateTypeMappingForUpperBounds(mts, ma, *decl); auto tys = tyMgr.GetInstantiatedTys(decl->ty, mts); for (auto ty : tys) { auto funcTy = DynamicCast(ty); if (!Ty::IsTyCorrect(funcTy)) { continue; } const auto found = FoundSameSignatureMember(tyMgr, *decl, *funcTy, methodSigs); if (found == methodSigs.cend()) { methodSigs.emplace(std::make_pair(decl->identifier, funcTy->paramTys), RawStaticCast(decl)); } else if (IsCloserToImpl(*found->second, *decl)) { // If the decl is generic, the paramsTys in the map key should also be updated, // so, just erase found result and emplace new result here. methodSigs.erase(found); methodSigs.emplace(std::make_pair(decl->identifier, funcTy->paramTys), RawStaticCast(decl)); } } } for (auto method : std::as_const(methodSigs)) { targets.emplace(method.second); } return Utils::SetToVec>(targets); } std::vector> MergeFuncTargetsInSum(TypeManager& tyMgr, const MemberAccess& ma) { // We need to check upperbound members in a fixed order. OrderedDeclSet upperDecls; for (auto it : ma.foundUpperBoundMap) { upperDecls.emplace(it.first); } // Functions found in upperbounds which have same signature must have only one valid implementation. // So, classify functions by function signature. std::unordered_set> targets; std::map, Ptr> methodSigs; for (auto decl : upperDecls) { CJC_NULLPTR_CHECK(decl); if (decl->astKind != ASTKind::FUNC_DECL) { continue; } MultiTypeSubst mts; tyMgr.GenerateTypeMappingForUpperBounds(mts, ma, *decl); auto tys = tyMgr.GetInstantiatedTys(decl->ty, mts); for (auto ty : tys) { auto funcTy = DynamicCast(ty); if (!Ty::IsTyCorrect(funcTy)) { continue; } const auto found = FoundSameSignatureMember(tyMgr, *decl, *funcTy, methodSigs); if (found == methodSigs.cend()) { methodSigs.emplace(funcTy, RawStaticCast(decl)); } else if (IsCloserToImpl(*found->second, *decl)) { // If the decl is generic, the paramsTys in the map key should also be updated, // so, just erase found result and emplace new result here. methodSigs.erase(found); methodSigs.emplace(funcTy, RawStaticCast(decl)); } } } for (auto method : std::as_const(methodSigs)) { targets.emplace(method.second); } return Utils::SetToVec>(targets); } void DiagForGenericParamMemberNotFound(DiagnosticEngine& diag, const MemberAccess& ma, const GenericParamDecl& gpd) { if (Ty::IsTyCorrect(gpd.ty) && gpd.ty->IsGeneric() && RawStaticCast(gpd.ty)->isUpperBoundLegal) { diag.Diagnose(*ma.baseExpr, DiagKind::sema_invalid_field_expose_access, ma.field.Val(), "exposed generic parameter", gpd.identifier.Val()); } } } // namespace void TypeChecker::TypeCheckerImpl::DiagMemberAccessNotFound(const MemberAccess& ma) { if (IsFieldOperator(ma.field)) { return; // Do not report error for operator overload access. } if (ci->invocation.globalOptions.compileTestsOnly && TestEntryConstructor::IsTestRegistrationFunction(ma.target) ) { /* * Allow loading not accessible test registration functions, * especially from other packages, to be able to execute tests from other packages */ return; } Ptr baseExpr = ma.baseExpr.get(); CJC_NULLPTR_CHECK(baseExpr); if (!Ty::IsTyCorrect(baseExpr->ty)) { return; // Do not report error for baseExpr with invlaid type. } auto getMemberRange = [&ma]() { return ma.field.ZeroPos() ? MakeRange(ma.begin, ma.end) : MakeRange(ma.field); }; if (ma.isExposedAccess) { (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_not_found_from_generic_upper_bounds, ma, getMemberRange(), ma.field.Val(), baseExpr->ty->name); } else if (baseExpr->ty->IsNominal()) { std::string kind = baseExpr->ty->Ty::String(); kind[0] = static_cast(std::tolower(kind[0])); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_not_member_of, ma, getMemberRange(), ma.field, kind, baseExpr->ty->name + baseExpr->ty->PrintTypeArgs()); RecommendImportForMemberAccess(typeManager, importManager, ma, &builder); } else if (ma.ShouldDiagnose(true)) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_undeclared_identifier, ma, getMemberRange(), ma.field); RecommendImportForMemberAccess(typeManager, importManager, ma, &builder); } } bool TypeChecker::TypeCheckerImpl::ChkRefExpr(ASTContext& ctx, Ty& target, NameReferenceExpr& refNode) { (void)Synthesize(ctx, &refNode); auto targets = GetFuncTargets(refNode); // None function target check. if (targets.empty()) { return CheckNonFunctionReference(diag, typeManager, target, refNode); } // Overloading check. uint32_t matched = 0; CJC_ASSERT(refNode.IsReferenceExpr()); RemoveDuplicateElements(targets); // Add for cjmp mpImpl->RemoveCommonCandidatesIfHasPlatform(targets); auto [genericIgnored, candidates] = CollectValidFuncTys(ctx, targets, refNode, DynamicCast(&target)); TypeSubst resultMapping; Ptr matchedFd = nullptr; Ptr validCandidateTy = nullptr; for (auto [fd, fdTy, mapping] : candidates) { if (!typeManager.IsSubtype(fdTy, &target)) { if (!validCandidateTy && Ty::IsTyCorrect(fdTy) && !StaticCast(fdTy)->retTy->IsQuest()) { validCandidateTy = fdTy; } continue; } if (matched != 0) { matched++; break; } matched++; ReplaceTarget(&refNode, fd); std::tie(matchedFd, refNode.ty, resultMapping) = {fd, fdTy, mapping}; } refNode.ty = TypeManager::GetNonNullTy(refNode.ty); if (refNode.ty->IsQuest()) { refNode.ty = TypeManager::GetInvalidTy(); } if (matched > 1) { diag.Diagnose(refNode, DiagKind::sema_ambiguous_func_ref, targets[0]->identifier.Val()); } else if (matched == 0) { if (Ty::IsTyCorrect(refNode.ty) && refNode.ty->HasQuestTy()) { CJC_ASSERT(refNode.GetTarget()); DiagUnableToInferReturnType(diag, *targets[0], refNode); } else if (genericIgnored) { diag.Diagnose(refNode, DiagKind::sema_generic_type_without_type_argument); } else if (validCandidateTy) { DiagMismatchedTypesWithFoundTy(diag, refNode, target, *validCandidateTy); } else { diag.Diagnose(refNode, DiagKind::sema_no_match_function_declaration_for_ref, targets[0]->identifier.Val()); } if (targets.size() > 1) { ReplaceTarget(&refNode, nullptr); // Only clear target when there are more than one candidate. } } else { if (IsGenericUpperBoundCall(refNode, *matchedFd)) { // Previous check guarantees 'refNode' is 'RefExpr' or 'MemberAccess' which must be 'NameReferenceExpr'. auto& ref = static_cast(refNode); ref.matchedParentTy = typeManager.GetInstantiatedTy(matchedFd->outerDecl->ty, resultMapping); } InstantiateReferenceType(ctx, refNode, resultMapping); } return matched != 0; } bool TypeChecker::TypeCheckerImpl::SynTargetOnUsed(ASTContext& ctx, const NameReferenceExpr& nre, Decl& target) { // Type decls are no need to be checked again recursively, because the ty is already set at PreCheck stage. if (NeedSynOnUsed(target)) { Ptr declToSyn = ⌖ if (auto vd = DynamicCast(&target)) { auto& vda = ctx.GetOuterVarDeclAbstract(*vd); declToSyn = vda.TestAttr(Attribute::GLOBAL) ? &vda : vd; } auto targetTy = Synthesize(ctx, declToSyn); if (auto fd = DynamicCast(declToSyn); fd && targetTy->HasQuestTy()) { DiagUnableToInferReturnType(diag, *fd, nre); return false; } } return true; } void TypeChecker::TypeCheckerImpl::InferRefExpr(ASTContext& ctx, RefExpr& re) { if (re.ref.target && Ty::IsTyCorrect(re.ty) && !re.ref.target->ty->IsPlaceholder()) { return; // If the target is already existed and type is valid, we can exit early. } bool isWellTyped = true; for (auto& type : re.typeArguments) { isWellTyped = Ty::IsTyCorrect(Synthesize(ctx, type.get())) && isWellTyped; } if (re.isThis || re.isSuper) { CheckThisOrSuper(ctx, re); return; } bool isCustomAnnotation = false; if (auto ce = DynamicCast(re.callOrPattern); ce && ce->callKind == CallKind::CALL_ANNOTATION) { isCustomAnnotation = true; } auto targets = Lookup(ctx, re.ref.identifier, isCustomAnnotation ? TOPLEVEL_SCOPE_NAME : re.scopeName, re, re.TestAttr(AST::Attribute::LEFT_VALUE)); if (!isCustomAnnotation && !re.TestAttr(Attribute::MACRO_INVOKE_BODY) && std::all_of(targets.cbegin(), targets.cend(), [](auto target) { CJC_NULLPTR_CHECK(target); return target->TestAttr(Attribute::MACRO_FUNC); })) { auto enumSugarChecker = std::make_unique(*this, ctx, re); auto res = enumSugarChecker->Resolve(); if (res.first) { if (res.second.empty()) { re.ty = TypeManager::GetInvalidTy(); } return; } targets = res.second; } if (!FilterAndCheckTargetsOfRef(ctx, re, targets) || !isWellTyped) { re.ty = TypeManager::GetInvalidTy(); return; } // 'targets' must not empty. auto decl = GetAccessibleDecl(ctx, re, targets); if (!decl) { decl = targets.front(); } ModifyTargetOfRef(re, decl, targets); // Legality of using refExpr will be checked after typecheck in 'CheckLegalityOfReference'. CJC_ASSERT(re.ref.target); // 're.ref.target' should be set in 'ModifyTargetOfRef'. if (auto target = re.ref.target; target->IsBuiltIn()) { if (auto cfunc = StaticCast(target); cfunc->type == BuiltInType::CFUNC) { return InferCFuncExpr(ctx, re); } } // If refExpr is base of call, decide real target & type in function call checking. // If refExpr is overloaded function reference with target type, decide real target & type in 'ChkRefExpr'. if (IsAllFuncDecl(targets) && targets.size() > 1 && (re.callOrPattern || ctx.HasTargetTy(&re))) { re.ty = TypeManager::GetQuestTy(); return; } if (!SynTargetOnUsed(ctx, re, *re.ref.target)) { re.ty = TypeManager::GetInvalidTy(); return; } re.ty = SubstituteTypeAliasInTy(*re.ref.target->ty); if (!decl->IsFunc() || re.isAlone) { // Only check non-function or non-call target. Functions will be check after function overload resolution. InstantiateReferenceType(ctx, re); } if (Ty::IsInitialTy(re.ty)) { re.ty = TypeManager::GetInvalidTy(); } } void TypeChecker::TypeCheckerImpl::InferCFuncExpr(ASTContext& ctx, RefExpr& re) { // check type arguments // infer type arguments of CFunc is currently disallowed if (re.typeArguments.size() != 1) { diag.DiagnoseRefactor( DiagKindRefactor::sema_generic_argument_no_match, re, MakeRange(re.GetBegin(), re.GetEnd())); re.ty = TypeManager::GetInvalidTy(); return; } auto funcType = DynamicCast(&*re.typeArguments[0]); if (!funcType) { diag.Diagnose(re.typeArguments[0]->GetBegin(), re.typeArguments[0]->GetEnd(), DiagKind::sema_cfunc_type); re.ty = TypeManager::GetInvalidTy(); return; } // synthesise type std::vector> paramTys; for (size_t i{0}; i < funcType->paramTypes.size(); ++i) { auto& param = funcType->paramTypes[i]; paramTys.push_back(param->ty = GetTyFromASTType(ctx, &*param)); } Ptr retTy = funcType->retType->ty = GetTyFromASTType(ctx, funcType->retType.get()); auto resTy = typeManager.GetFunctionTy(std::move(paramTys), retTy, {.isC = true}); re.ty = resTy; } void TypeChecker::TypeCheckerImpl::TryInitializeBaseSum(ASTContext& ctx, MemberAccess& ma) { if (!ma.baseExpr->ty->IsPlaceholder()) { return; } auto tv = RawStaticCast(ma.baseExpr->ty); auto& sum = typeManager.constraints[tv].sum; if (sum.size() != 1 || !(*sum.begin())->IsAny()) { return; } MemSig sig; if (auto ce = DynamicCast(ma.callOrPattern)) { sig = MemSig{ma.field, false, ce->args.size(), ma.typeArguments.size()}; } else { sig = MemSig{ma.field, true}; } TryEnforceCandidate(*tv, ctx.Mem2Decls(sig), typeManager); } void TypeChecker::TypeCheckerImpl::InferMemberAccess(ASTContext& ctx, MemberAccess& ma) { if (ma.target && Ty::IsTyCorrect(ma.ty)) { return; // If the target is already existed and type is valid, we can exit early. } bool isWellTyped = true; for (auto& type : ma.typeArguments) { isWellTyped = Ty::IsTyCorrect(Synthesize(ctx, type.get())) && isWellTyped; } Ptr baseExpr = ma.baseExpr.get(); if (!baseExpr || !isWellTyped) { ma.ty = TypeManager::GetInvalidTy(); return; } SetIsNotAlone(*ma.baseExpr); auto targetOfBase = GetBaseDeclInMemberAccess(ctx, ma); TryInitializeBaseSum(ctx, ma); // baseExpr is Synthesized by GetBaseDeclInMemberAccess. Refactor later. // Whether current is access member by type alias of primitive types. bool isPrimitiveTypeAlias = targetOfBase && targetOfBase->astKind == ASTKind::TYPE_ALIAS_DECL && baseExpr->ty && baseExpr->ty->IsPrimitive(); // Whether current is access member by real/generic type or package name. bool isStaticAccessByName = targetOfBase && !IsThisOrSuper(*baseExpr) && (targetOfBase->IsTypeDecl() || Is(targetOfBase)); bool isBuiltInStaticAccess = targetOfBase && targetOfBase->IsBuiltIn(); bool isPartialPackagePath = !targetOfBase && Ty::IsInitialTy(ma.baseExpr->ty) && (ma.isAlone || ma.callOrPattern); if (isBuiltInStaticAccess) { InferBuiltInStaticAccess(ctx, ma, *RawStaticCast(targetOfBase)); } else if (baseExpr->astKind == ASTKind::PRIMITIVE_TYPE_EXPR || isPrimitiveTypeAlias) { CheckExtendField(ctx, ma); } else if (isStaticAccessByName) { CJC_ASSERT(targetOfBase); InferStaticAccess(ctx, ma, *targetOfBase); } else if (isPartialPackagePath) { auto range = ma.field.ZeroPos() ? MakeRange(ma.begin, ma.end) : MakeRange(ma.field); (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_undeclared_identifier, ma, range, ma.field); } else { InferInstanceAccess(ctx, ma); } if (!ma.target) { return; } // Legality of using memberAccess will be checked after typecheck in 'CheckLegalityOfReference'. // If memberAccess is base of call, decide real target & type in function call checking. // If memberAccess is overloaded function reference with target type, decide real target & type in 'ChkRefExpr'. bool checkedLater = IsAllFuncDecl(ma.targets) && ma.targets.size() > 1 && (ma.callOrPattern || ctx.HasTargetTy(&ma)); if (checkedLater) { ma.ty = TypeManager::GetQuestTy(); return; } if (!SynTargetOnUsed(ctx, ma, *ma.target)) { ma.ty = TypeManager::GetInvalidTy(); return; } ma.ty = SubstituteTypeAliasInTy(*ma.target->ty); // Only instantiate ty for non-function or non-call. Function's will be done after overload resolution. if (!ma.target->IsFunc() || ma.isAlone) { InstantiateReferenceType(ctx, ma); } } Ptr TypeChecker::TypeCheckerImpl::GetBaseDeclInMemberAccess(ASTContext& ctx, const MemberAccess& ma) { if (ma.baseExpr == nullptr) { return nullptr; } Ptr baseExpr = ma.baseExpr.get(); // Synthesize baseExpr's ty, baseExpr maybe another MemberAccess like a.b or RefExpr a. // Could be from desugaring a binary expr, need to avoid exponential repetitive check with cache SynthesizeWithNegCache(ctx, baseExpr); return GetRealTarget(baseExpr, baseExpr->GetTarget()); } void TypeChecker::TypeCheckerImpl::InferBuiltInStaticAccess( const ASTContext& ctx, MemberAccess& ma, const BuiltInDecl& bid) { if (bid.IsType(BuiltInType::ARRAY)) { InferArrayStaticAccess(ctx, ma); } else if (bid.IsType(BuiltInType::POINTER) || bid.IsType(BuiltInType::CSTRING)) { CheckExtendField(ctx, ma); } // Leave for other builtin composite type. } void TypeChecker::TypeCheckerImpl::InferArrayStaticAccess(const ASTContext& ctx, MemberAccess& ma) { CJC_ASSERT(ma.baseExpr); CJC_NULLPTR_CHECK(ma.curFile); auto typeArgs = ma.baseExpr->GetTypeArgs(); if (typeArgs.empty()) { diag.Diagnose(ma, DiagKind::sema_generic_type_without_type_argument); ma.ty = TypeManager::GetInvalidTy(); return; } ma.baseExpr->ty = typeManager.GetArrayTy(typeArgs[0]->ty, 1); auto targets = ExtendFieldLookup(ctx, *ma.curFile, ma.baseExpr->ty, ma.field); if (!FilterAndCheckTargetsOfNameAccess(ctx, ma, targets)) { return; } auto target = GetAccessibleDecl(ctx, ma, targets); ReplaceTarget(&ma, target ? target : targets[0]); AddFuncTargetsForMemberAccess(ma, targets); } void TypeChecker::TypeCheckerImpl::InferStaticAccess(const ASTContext& ctx, MemberAccess& ma, Decl& targetOfBaseExpr) { ma.ty = TypeManager::GetInvalidTy(); // Ty will be set to valid if non-error happens. // Caller guarantees current is access member by real/generic type or package name. std::vector> targets; Ptr baseExpr = ma.baseExpr.get(); CJC_ASSERT(baseExpr); // Case for access member by real type. if (targetOfBaseExpr.astKind != ASTKind::GENERIC_PARAM_DECL) { // In this case, targetOfBaseExpr is ClassDecl, InterfaceDecl, StructDecl, EnumDecl or PackageDecl. CJC_NULLPTR_CHECK(ma.curFile); targets = FieldLookup(ctx, &targetOfBaseExpr, ma.field, {baseExpr->ty, ma.curFile, true, true, ma.TestAttr(AST::Attribute::LEFT_VALUE)}); // remove macro decl when it is not a @ call bool hasRemovedMacros{!targets.empty()}; bool isCustomAnnotation = false; if (auto ce = DynamicCast(ma.callOrPattern); ce && ce->callKind == CallKind::CALL_ANNOTATION) { isCustomAnnotation = true; } if (!isCustomAnnotation && Is(targetOfBaseExpr) && !ma.TestAttr(Attribute::MACRO_INVOKE_BODY)) { targets.erase(std::remove_if(targets.begin(), targets.end(), [](auto target) { return target->TestAttr(Attribute::MACRO_FUNC); }), targets.end()); if (!targets.empty()) { hasRemovedMacros = false; } } if (targets.empty()) { if (targetOfBaseExpr.astKind == ASTKind::PACKAGE_DECL) { if (hasRemovedMacros) { diag.DiagnoseRefactor(DiagKindRefactor::sema_undeclared_identifier, MakeRange(ma.field.Begin(), ma.field.End()), ma.field.Val()); } else { DiagPackageMemberNotFound(diag, importManager, ma, StaticCast(targetOfBaseExpr)); } } else { DiagMemberAccessNotFound(ma); } return; } if (!FilterAndCheckTargetsOfNameAccess(ctx, ma, targets)) { return; } // targets is guaranteed to be no empty after the check FilterAndCheckTargetsOfNameAccess is invoked. auto target = GetAccessibleDecl(ctx, ma, targets); ReplaceTarget(&ma, target ? target : targets[0]); AddFuncTargetsForMemberAccess(ma, targets); } else { // Case for access member by generic type. auto genericTy = DynamicCast(baseExpr->ty); if (!genericTy) { return; // When the target of base is generic param and 'ty' is not 'GenericsTy', errors happened before. } // 'GetMemberAccessExposedTarget' will put found targets into 'ma.targets' and report error if not found. auto target = GetMemberAccessExposedTarget(ctx, ma, *genericTy, true); if (!target) { return; } ReplaceTarget(&ma, target); if (target && target->astKind == ASTKind::FUNC_DECL) { targets = std::vector>(ma.targets.begin(), ma.targets.end()); } if (target && (target->astKind == ASTKind::VAR_DECL || target->astKind == ASTKind::PROP_DECL)) { targets = {target}; } } } void TypeChecker::TypeCheckerImpl::InferInstanceAccess(const ASTContext& ctx, MemberAccess& ma) { // In this case, targetOfBaseExpr is an object. Ptr baseExpr = ma.baseExpr.get(); if (!baseExpr || Ty::IsInitialTy(baseExpr->ty)) { return; // 'baseExpr' may be a part of package name such as 'pkga' in 'package pkga.pkgb.pkgc'. } ma.ty = TypeManager::GetInvalidTy(); // Ty will be set to valid if non-error happens. Ptr target = GetObjMemberAccessTarget(ctx, ma, *baseExpr->ty); if (!target) { return; } ReplaceTarget(&ma, target); } Ptr TypeChecker::TypeCheckerImpl::CheckUpperBoundTargetsCaseFuncCall( const ASTContext& ctx, MemberAccess& ma, const std::unordered_map, std::vector>>& allTargets) { ma.targets.clear(); // Need clear before insertion. ma.foundUpperBoundMap = GetUpperBoundTargetsWithGivenKind(allTargets, {ASTKind::FUNC_DECL}); std::vector> fdTargets = ma.baseExpr->ty->IsPlaceholder() ? MergeFuncTargetsInSum(typeManager, ma) : MergeFuncTargetsInUpperBounds(typeManager, ma); std::sort(fdTargets.begin(), fdTargets.end(), CompNodeByPos); auto target = ma.baseExpr->GetTarget(); if (target && target->astKind == ASTKind::GENERIC_PARAM_DECL) { FilterAndCheckTargetsOfNameAccess(ctx, ma, fdTargets); } else { FilterAndGetTargetsOfObjAccess(ctx, ma, fdTargets); } // The final target will be determined in match function period. if (!fdTargets.empty()) { std::for_each(fdTargets.begin(), fdTargets.end(), [&ma](auto it) { ma.targets.emplace_back(RawStaticCast(it)); }); ma.matchedParentTy = *ma.foundUpperBoundMap[fdTargets[0]].begin(); return fdTargets[0]; } else { return nullptr; } } Ptr TypeChecker::TypeCheckerImpl::CheckUpperBoundTargetsCaseOthers(const ASTContext& ctx, MemberAccess& ma, const std::unordered_map, std::vector>>& allTargets) { ma.foundUpperBoundMap = GetUpperBoundTargetsWithGivenKind(allTargets, {ASTKind::VAR_DECL, ASTKind::PROP_DECL}); std::vector> tempTargets; // We need to check upperbound members in a fixed order. OrderedDeclSet upperDecls; for (auto it : ma.foundUpperBoundMap) { upperDecls.emplace(it.first); } for (auto it : upperDecls) { // Decls found in upperbounds which have same type must have only one valid implementation. // So, classify decls by type. auto found = std::find_if(tempTargets.begin(), tempTargets.end(), [this, &it](auto decl) { return typeManager.IsTyEqual(decl->ty, it->ty); }); if (found == tempTargets.end()) { tempTargets.emplace_back(it); } else if (IsCloserToImpl(*(*found), *it)) { *found = it; } } if (auto genTy = DynamicCast(ma.baseExpr->ty); genTy && genTy->isPlaceholder) { FilterSumUpperbound(ctx, ma, *genTy, tempTargets, allTargets); } if (tempTargets.empty()) { diag.Diagnose(ma, ma.field.Begin(), DiagKind::sema_generic_no_member_match_in_upper_bounds); return nullptr; } else if (tempTargets.size() == 1 && ma.foundUpperBoundMap.find(tempTargets[0]) != ma.foundUpperBoundMap.end()) { // For non-function target, all types must be same, so just chose first one. ma.matchedParentTy = *ma.foundUpperBoundMap[tempTargets[0]].begin(); return As(tempTargets[0]); } else { OrderedDeclSet candidates(tempTargets.cbegin(), tempTargets.cend()); DiagAmbiguousUpperBoundTargets(diag, ma, candidates); return nullptr; } } void TypeChecker::TypeCheckerImpl::FilterSumUpperbound(const ASTContext& ctx, AST::MemberAccess& ma, AST::GenericsTy& tv, std::vector>& targets, const std::unordered_map, std::vector>>& allTargets) { auto& m = ctx.targetTypeMap; auto tgtTy = m.count(&ma) > 0 ? m.at(&ma) : nullptr; if (tgtTy && tgtTy->HasQuestTy()) { tgtTy = nullptr; } if (tgtTy) { std::map, Ptr> decl2Ub; for (auto& [ub, decls] : allTargets) { for (auto d : decls) { decl2Ub[d] = ub; } } std::vector> filteredTargets; Ptr validTy = nullptr; for (auto d : targets) { auto ub = decl2Ub[d]; // ub should never be filtered out if (!d->outerDecl || typeManager.constraints[&tv].ubs.count(ub) > 0) { filteredTargets.push_back(d); continue; // only filter out ub and targets from sum } InstCtxScope ic(*this); ic.SetRefDecl(*d->outerDecl, ub); auto instTy = typeManager.InstOf(d->ty); if (typeManager.IsSubtype(instTy, tgtTy)) { if (validTy && instTy != validTy) { return; // can't disambiguate anyway, just return } filteredTargets.push_back(d); validTy = instTy; } } targets = filteredTargets; } if (targets.size() == 1 && !FilterSumUpperbound(ma, tv, *targets[0])) { ma.ty = TypeManager::GetInvalidTy(); DiagMismatchedTypesWithFoundTy(diag, ma, *tgtTy, *ma.ty); } } bool TypeChecker::TypeCheckerImpl::FilterSumUpperbound(AST::MemberAccess& ma, AST::GenericsTy& tv, const AST::Decl& d) { CJC_ASSERT(tv.isPlaceholder); auto hasMemOfTargetSig = [this, &ma, &d](Ptr ty)->bool { for (auto& [d2, ub] : ma.foundUpperBoundMap) { if (ub.count(ty) > 0 && typeManager.IsTyEqual(d.ty, d2->ty)) { return true; } } return false; }; auto& cst = typeManager.constraints[&tv]; auto& sum = cst.sum; for (auto it = sum.begin(); it != sum.end();) { if (!hasMemOfTargetSig(*it)) { it = sum.erase(it); } else { it++; } } if (sum.size() == 1 && !(*sum.begin())->IsAny()) { auto eq = *sum.begin(); if (cst.eq.size() == 0) { cst.eq.insert(eq); } if (!typeManager.IsTyEqual(&tv, eq)) { return false; } } return true; } Ptr TypeChecker::TypeCheckerImpl::GetIdealTypeFuncTargetFromExtend( const ASTContext& ctx, MemberAccess& ma, const Ty& baseExprTy) { CJC_NULLPTR_CHECK(ma.curFile); std::unordered_set> candidates; std::vector> targetTys; for (auto& tyKind : GetIdealTypesByKind(baseExprTy.kind)) { auto targets = ExtendFieldLookup(ctx, *ma.curFile, TypeManager::GetPrimitiveTy(tyKind), ma.field); for (auto& target : targets) { if (!target->TestAttr(AST::Attribute::STATIC)) { candidates.insert(target); targetTys.push_back(TypeManager::GetPrimitiveTy(tyKind)); } } } if (candidates.size() > 1) { CJC_ASSERT(ma.baseExpr); ReplaceIdealTy(*ma.baseExpr); if (auto lce = DynamicCast(ma.baseExpr.get())) { InitializeLitConstValue(*lce); } Ptr target = nullptr; { auto disDiag = DiagSuppressor(diag); target = GetObjMemberAccessTarget(ctx, ma, *ma.baseExpr->ty); } if (Ty::IsTyCorrect(ma.baseExpr->ty) && target == nullptr) { std::string tyStr = Ty::GetTypesToStr(targetTys, " "); diag.DiagnoseRefactor(DiagKindRefactor::sema_ambiguous_match_primitive_extend, ma, ma.field, tyStr); } return target; } else if (candidates.size() == 1) { ma.baseExpr->ty = targetTys.back(); ma.target = *candidates.begin(); if (ma.target->astKind == ASTKind::FUNC_DECL) { ma.targets.push_back(StaticAs(ma.target)); } return ma.target; } else { DiagMemberAccessNotFound(ma); return nullptr; } } Ptr TypeChecker::TypeCheckerImpl::GetMemberAccessExposedTarget( const ASTContext& ctx, MemberAccess& ma, const GenericsTy& genericsTy, bool isStaticAccess) { ma.isExposedAccess = true; if (!genericsTy.isUpperBoundLegal) { return nullptr; // If not legal, errors should be reported before. } auto allUpperBounds = genericsTy.upperBounds; std::unordered_map, std::vector>> allTargets; for (auto& upperBound : allUpperBounds) { if (!Ty::IsTyCorrect(upperBound)) { continue; } auto targets = GetUpperBoundTargets(ctx, ma, *upperBound, isStaticAccess); if (!targets.empty()) { allTargets.emplace(std::make_pair(upperBound, targets)); } } if (allTargets.empty()) { isStaticAccess ? DiagForGenericParamMemberNotFound(diag, ma, *genericsTy.decl) : DiagMemberAccessNotFound(ma); return nullptr; } // Since members of user defined type cannot have same name, the found targets must have same astKind. // If not same, report error. auto kind = GetTargetsSameASTKind(allTargets); if (kind == ASTKind::INVALID_DECL) { OrderedDeclSet candidates; for (auto it : allTargets) { candidates.insert(it.second.begin(), it.second.end()); } DiagAmbiguousUpperBoundTargets(diag, ma, candidates); return nullptr; } // Will report error if return nullptr. if (kind == ASTKind::FUNC_DECL) { return CheckUpperBoundTargetsCaseFuncCall(ctx, ma, allTargets); } else { return CheckUpperBoundTargetsCaseOthers(ctx, ma, allTargets); } } Ptr TypeChecker::TypeCheckerImpl::GetObjMemberAccessTarget( const ASTContext& ctx, MemberAccess& ma, Ty& baseExprTy) { // If the member access is 'this.xxx' and in original type decl, the members in extend should be ignored. auto re = DynamicCast(ma.baseExpr.get()); auto outerDecl = GetCurInheritableDecl(ctx, ma.scopeName); bool searchExtend = !re || !re->isThis || !outerDecl || outerDecl->astKind == ASTKind::EXTEND_DECL; CJC_NULLPTR_CHECK(ma.curFile); return match(baseExprTy)( [this, &ctx, &ma, searchExtend](ClassTy& classTy) { LookupInfo info{&classTy, ma.curFile, true, searchExtend, ma.TestAttr(AST::Attribute::LEFT_VALUE)}; auto targets = FieldLookup(ctx, classTy.declPtr, ma.field, info); return FilterAndGetTargetsOfObjAccess(ctx, ma, targets); }, [this, &ctx, &ma](InterfaceTy& interfaceTy) { auto targets = FieldLookup(ctx, interfaceTy.declPtr, ma.field, {&interfaceTy, ma.curFile}); return FilterAndGetTargetsOfObjAccess(ctx, ma, targets); }, [this, &ctx, &ma, searchExtend](const StructTy& structTy) { auto targets = FieldLookup( ctx, structTy.declPtr, ma.field, {.file = ma.curFile, .lookupExtend = searchExtend}); return FilterAndGetTargetsOfObjAccess(ctx, ma, targets); }, [this, &ctx, &ma, searchExtend](const EnumTy& enumTy) { auto targets = FieldLookup( ctx, enumTy.declPtr, ma.field, {.file = ma.curFile, .lookupExtend = searchExtend}); return FilterAndGetTargetsOfObjAccess(ctx, ma, targets); }, [this, &ctx, &ma](ArrayTy& arrayTy) { auto targets = ExtendFieldLookup(ctx, *ma.curFile, &arrayTy, ma.field); return FilterAndGetTargetsOfObjAccess(ctx, ma, targets); }, [this, &ma](const VArrayTy& varrayTy) { // 'size' is the only member of VArray. if (ma.field == "size") { ma.ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64); auto literalExpr = CreateLitConstExpr(LitConstKind::INTEGER, std::to_string(varrayTy.size), ma.ty); ma.desugarExpr = std::move(literalExpr); ma.desugarExpr->ty = ma.ty; } else { DiagMemberAccessNotFound(ma); } return Ptr(); }, [this, &ctx, &ma](PointerTy& pointerTy) { auto targets = ExtendFieldLookup(ctx, *ma.curFile, &pointerTy, ma.field); return FilterAndGetTargetsOfObjAccess(ctx, ma, targets); }, [this, &ctx, &ma](GenericsTy& genericsTy) { if (genericsTy.isPlaceholder) { auto maybeSol = typeManager.TryGreedySubst(&genericsTy); if (!maybeSol->IsPlaceholder()) { ma.baseExpr->ty = maybeSol; return GetObjMemberAccessTarget(ctx, ma, *maybeSol); } auto& cst = typeManager.constraints[&genericsTy]; genericsTy.upperBounds.clear(); genericsTy.upperBounds.insert(cst.ubs.begin(), cst.ubs.end()); genericsTy.upperBounds.insert(cst.sum.begin(), cst.sum.end()); } // Diagnose inside callee. return GetMemberAccessExposedTarget(ctx, ma, genericsTy, false); }, [this, &ctx, &ma, &baseExprTy]() { if (baseExprTy.IsIdeal()) { return GetIdealTypeFuncTargetFromExtend(ctx, ma, baseExprTy); } auto targets = ExtendFieldLookup(ctx, *ma.curFile, &baseExprTy, ma.field); return FilterAndGetTargetsOfObjAccess(ctx, ma, targets); }); } std::vector> TypeChecker::TypeCheckerImpl::GetUpperBoundTargets( const ASTContext& ctx, const MemberAccess& ma, Ty& baseExprTy, const bool isStaticAccess) { CJC_NULLPTR_CHECK(ma.curFile); auto filterTargets = [&isStaticAccess](std::vector>& targets) -> void { for (auto it = targets.begin(); it != targets.end();) { // Objects cannot be used to access static members. if ((!isStaticAccess && (*it)->TestAttr(AST::Attribute::STATIC)) || (isStaticAccess && !(*it)->TestAttr(AST::Attribute::STATIC))) { it = targets.erase(it); } else { ++it; } } }; return match(baseExprTy)( [this, &ctx, &ma, &filterTargets](ClassTy& classTy) { auto targets = FieldLookup(ctx, classTy.decl, ma.field, {&classTy, ma.curFile}); filterTargets(targets); return targets; }, [this, &ctx, &ma, &filterTargets](InterfaceTy& interfaceTy) { auto targets = FieldLookup(ctx, interfaceTy.decl, ma.field, {&interfaceTy, ma.curFile}); filterTargets(targets); return targets; }, [this, &ctx, &ma, &filterTargets](const StructTy& structTy) { auto targets = FieldLookup(ctx, structTy.decl, ma.field, {.file = ma.curFile}); filterTargets(targets); return targets; }, [this, &ctx, &ma, &filterTargets](const EnumTy& enumTy) { auto targets = FieldLookup(ctx, enumTy.declPtr, ma.field, {.file = ma.curFile}); filterTargets(targets); return targets; }, [this, &ctx, &ma, &filterTargets](ArrayTy& arrayTy) { auto targets = ExtendFieldLookup(ctx, *ma.curFile, &arrayTy, ma.field); filterTargets(targets); return targets; }, [this, &ctx, &ma, &filterTargets](PrimitiveTy& ty) { auto targets = ExtendFieldLookup(ctx, *ma.curFile, &ty, ma.field); filterTargets(targets); return targets; }, []() { return std::vector>(); }); } void TypeChecker::TypeCheckerImpl::CheckForbiddenMemberAccess( const ASTContext& ctx, const MemberAccess& ma, const Decl& target) const { auto funcSrc = ScopeManager::GetOutMostSymbol(ctx, SymbolKind::FUNC, ma.scopeName); if (auto re = DynamicCast(ma.baseExpr.get()); re && funcSrc) { bool isThisOrSuper = re->ty && (re->isThis || re->isSuper); if (re->isThis) { bool accessStructLeftValue = ma.TestAttr(AST::Attribute::LEFT_VALUE) && re->ty && re->ty->IsStruct(); CheckImmutableFuncAccessMutableFunc(ma.begin, *funcSrc->node, target, accessStructLeftValue); } auto funcDecl = StaticCast(funcSrc->node); if (isThisOrSuper) { CheckForbiddenFuncReferenceAccess(ma.field.ZeroPos() ? ma.begin : ma.field.Begin(), *funcDecl, target); } } // NOT allowed to call enum constructor by object instance. if (target.TestAttr(AST::Attribute::ENUM_CONSTRUCTOR)) { ctx.diag.Diagnose(ma, DiagKind::sema_invalid_enum_member_access); } // Object member access does not allow type arguments appeared after var decl field. if (target.astKind == ASTKind::VAR_DECL && !ma.typeArguments.empty() && ma.isAlone) { diag.DiagnoseRefactor(DiagKindRefactor::sema_generic_argument_no_match, ma); } } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/OptionalChainExpr.cpp000066400000000000000000000020601510705540100252720ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "Diags.h" using namespace Cangjie; using namespace Sema; Ptr TypeChecker::TypeCheckerImpl::SynOptionalChainExpr(ASTContext& ctx, OptionalChainExpr& oce) { CJC_NULLPTR_CHECK(oce.desugarExpr); oce.ty = Synthesize(ctx, oce.desugarExpr.get()); return oce.ty; } bool TypeChecker::TypeCheckerImpl::ChkOptionalChainExpr(ASTContext& ctx, Ty& target, OptionalChainExpr& oce) { CJC_NULLPTR_CHECK(oce.desugarExpr); if (!Ty::IsTyCorrect(SynOptionalChainExpr(ctx, oce))) { return false; } if (!CheckOptionBox(target, *oce.desugarExpr->ty)) { DiagMismatchedTypes(diag, oce, target); oce.desugarExpr->ty = TypeManager::GetInvalidTy(); oce.ty = TypeManager::GetInvalidTy(); return false; } return true; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/ParenExpr.cpp000066400000000000000000000025111510705540100236100ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" using namespace Cangjie; using namespace AST; Ptr TypeChecker::TypeCheckerImpl::SynParenExpr(ASTContext& ctx, ParenExpr& pe) { (void)Synthesize(ctx, pe.expr.get()); if (!pe.expr || !Ty::IsTyCorrect(pe.expr->ty)) { pe.ty = TypeManager::GetInvalidTy(); return TypeManager::GetInvalidTy(); } if (pe.expr->ty->IsIdeal()) { ReplaceIdealTy(*pe.expr); } pe.ty = pe.expr->ty; if (pe.expr->isConst) { pe.isConst = true; pe.constNumValue = pe.expr->constNumValue; } return pe.ty; } bool TypeChecker::TypeCheckerImpl::ChkParenExpr(ASTContext& ctx, Ty& target, ParenExpr& pe) { if (Check(ctx, &target, pe.expr.get())) { CJC_NULLPTR_CHECK(pe.expr); // When the Check's result is true, pe.expr must not be nullptr. pe.ty = pe.expr->ty; if (pe.expr->isConst) { pe.isConst = true; pe.constNumValue = pe.expr->constNumValue; } return true; } else { pe.ty = TypeManager::GetInvalidTy(); return false; } } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/PerformExpr.cpp000066400000000000000000000015771510705540100241700ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "Diags.h" #include "JoinAndMeet.h" using namespace Cangjie; using namespace Sema; Ptr TypeChecker::TypeCheckerImpl::SynPerformExpr(ASTContext& ctx, PerformExpr& pe) { CJC_NULLPTR_CHECK(pe.expr); // Parser guarantees. auto exprTy = Synthesize(ctx, pe.expr.get()); if (!Ty::IsTyCorrect(exprTy)) { pe.ty = TypeManager::GetInvalidTy(); return pe.ty; } if (auto commandTy = PromoteToCommandTy(*pe.expr, *exprTy); commandTy) { pe.ty = (*commandTy)->typeArgs[0]; } else { pe.ty = TypeManager::GetInvalidTy(); } return pe.ty; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/QuoteExpr.cpp000066400000000000000000000033641510705540100236470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "Diags.h" using namespace Cangjie; using namespace Sema; namespace { void ChkIfImportLibAST(DiagnosticEngine& diag, const ImportManager& im, const QuoteExpr& qe) { if (qe.GetFullPackageName() == AST_PACKAGE_NAME) { return; } auto importedPkgs = im.GetAllImportedPackages(); for (auto& importedPkg : importedPkgs) { if (importedPkg->srcPackage && importedPkg->srcPackage->fullPackageName == AST_PACKAGE_NAME) { return; } } diag.DiagnoseRefactor(DiagKindRefactor::sema_use_expr_without_import, qe, "std.ast", "quote"); } } // namespace bool TypeChecker::TypeCheckerImpl::ChkQuoteExpr(ASTContext& ctx, Ty& target, QuoteExpr& qe) { ChkIfImportLibAST(diag, importManager, qe); if (!Ty::IsTyCorrect(Synthesize(ctx, &qe))) { return false; } if (!typeManager.IsSubtype(qe.ty, &target)) { DiagMismatchedTypes(diag, qe, target); qe.ty = TypeManager::GetInvalidTy(); return false; } if (qe.desugarExpr) { if (!Check(ctx, &target, qe.desugarExpr.get())) { qe.ty = TypeManager::GetInvalidTy(); return false; } } return true; } Ptr TypeChecker::TypeCheckerImpl::SynQuoteExpr(ASTContext& ctx, QuoteExpr& qe) { ChkIfImportLibAST(diag, importManager, qe); if (qe.desugarExpr) { qe.ty = Synthesize(ctx, qe.desugarExpr.get()); } else { qe.ty = TypeManager::GetInvalidTy(); } return qe.ty; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/RangeExpr.cpp000066400000000000000000000122501510705540100236000ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "Diags.h" #include "LocalTypeArgumentSynthesis.h" #include "TypeCheckUtil.h" using namespace Cangjie; using namespace Sema; using namespace TypeCheckUtil; bool TypeChecker::TypeCheckerImpl::CheckRangeElements(ASTContext& ctx, Ptr elemTy, const RangeExpr& re) { bool isWellTyped = !re.startExpr || Check(ctx, elemTy, re.startExpr.get()); isWellTyped = (!re.stopExpr || Check(ctx, elemTy, re.stopExpr.get())) && isWellTyped; // When target type of rangeExpr is not given, the elemTy is type of start or stop. bool inconsistent = !ctx.HasTargetTy(&re) && re.startExpr && re.stopExpr && !isWellTyped; if (inconsistent) { if (!CanSkipDiag(*re.startExpr) && !CanSkipDiag(*re.stopExpr)) { diag.Diagnose(re, DiagKind::sema_inconsistency_range_elemType); } } if (isWellTyped && !CheckGenericDeclInstantiation(re.decl, std::vector>{elemTy}, re)) { isWellTyped = false; } if (re.stepExpr) { if (!Check(ctx, TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64), re.stepExpr.get())) { if (!CanSkipDiag(*re.stepExpr)) { diag.Diagnose(re, DiagKind::sema_range_step_not_int64); } isWellTyped = false; } else if (re.stepExpr->isConst) { // Step expr cannot be zero. if (re.stepExpr->constNumValue.asInt.Uint64() == 0) { diag.Diagnose(*re.stepExpr, DiagKind::sema_step_non_zero_range); } } } return isWellTyped; } bool TypeChecker::TypeCheckerImpl::ChkRangeExpr(ASTContext& ctx, Ty& target, RangeExpr& re) { re.ty = TypeManager::GetInvalidTy(); // Set type invalid first, will be updated if check passed. re.decl = importManager.GetCoreDecl("Range"); if (!re.decl) { diag.Diagnose(re, DiagKind::sema_no_core_object); return false; } if (!Ty::IsTyCorrect(&target)) { return false; } auto synAndCheckRangeTy = [this, &ctx, &re, &target]() { re.ty = SynRangeExpr(ctx, re); bool isWellTyped = typeManager.IsSubtype(re.ty, &target); if (!isWellTyped && Ty::IsTyCorrect(re.ty)) { DiagMismatchedTypes(diag, re, target); } return isWellTyped; }; Ptr targetTy = TypeCheckUtil::UnboxOptionType(&target); bool isWellTyped = Ty::IsTyCorrect(targetTy) && (targetTy->IsStruct() || targetTy->IsInterface()); if (!isWellTyped) { (void)synAndCheckRangeTy(); re.ty = TypeManager::GetInvalidTy(); return false; } auto rangeTy = targetTy; if (targetTy->IsInterface()) { auto candiTys = promotion.Downgrade(*re.decl->ty, target); if (candiTys.empty()) { // If range type cannot be inferred from target type, syn range expr. if (synAndCheckRangeTy()) { return true; } DiagUnableToInferExpr(diag, re); return false; } rangeTy = *candiTys.begin(); } else if (!targetTy->IsRange() || rangeTy->typeArgs.size() != 1) { return synAndCheckRangeTy(); } if (!CheckRangeElements(ctx, rangeTy->typeArgs[0], re)) { return false; } re.ty = rangeTy; isWellTyped = typeManager.IsSubtype(re.ty, &target); if (!isWellTyped) { DiagMismatchedTypes(diag, re, target); } return isWellTyped; } Ptr TypeChecker::TypeCheckerImpl::SynRangeExpr(ASTContext& ctx, RangeExpr& re) { re.ty = TypeManager::GetInvalidTy(); // Set type invalid first, will be updated if check passed. re.decl = importManager.GetCoreDecl("Range"); if (!re.decl) { return re.ty; } Ptr elemTy = SynRangeExprInferElemTy(re, ctx); if (Ty::IsTyCorrect(elemTy) && elemTy->IsIdeal() && elemTy->IsInteger()) { elemTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64); } if (!CheckRangeElements(ctx, elemTy, re)) { return re.ty; } re.ty = typeManager.GetStructTy(*re.decl, {elemTy}); return re.ty; } Ptr TypeChecker::TypeCheckerImpl::SynRangeExprInferElemTy(const RangeExpr& re, ASTContext& ctx) { // If type of startExpr is Nothing or startExpr is litconst when only one of start/stop is litconst, // using type of stopExpr or default Int64 type. Ptr startTy = re.startExpr ? Synthesize(ctx, re.startExpr.get()) : TypeManager::GetInvalidTy(); bool useStartLit = !Is(re.startExpr.get()) || Is(re.stopExpr.get()); if (Ty::IsTyCorrect(startTy) && !startTy->IsNothing() && useStartLit) { return startTy; } Ptr stopTy = re.stopExpr ? Synthesize(ctx, re.stopExpr.get()) : TypeManager::GetInvalidTy(); if (Ty::IsTyCorrect(stopTy) && !stopTy->IsNothing()) { return stopTy; } else if (Ty::IsTyCorrect(startTy)) { return startTy; } // Element type is Int64 by default. return TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64); } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/ResumeExpr.cpp000066400000000000000000000035311510705540100240060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" using namespace Cangjie; using namespace AST; Ptr TypeChecker::TypeCheckerImpl::SynResumeExpr(ASTContext& ctx, ResumeExpr& re) { Ty* resumptionParamTy = typeManager.GetAnyTy(); if (re.enclosing) { resumptionParamTy = (*re.enclosing)->commandResultTy; re.ty = TypeManager::GetNothingTy(); } else { diag.DiagnoseRefactor(DiagKindRefactor::sema_implicit_resume_outside_handler, re); re.ty = TypeManager::GetInvalidTy(); } if (re.throwingExpr) { auto exception = importManager.GetCoreDecl(CLASS_EXCEPTION); auto error = importManager.GetCoreDecl(CLASS_ERROR); Synthesize(ctx, re.throwingExpr.get()); CJC_NULLPTR_CHECK(re.throwingExpr->ty); if (!typeManager.IsSubtype(re.throwingExpr->ty, exception->ty) && !typeManager.IsSubtype(re.throwingExpr->ty, error->ty)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_resume_throwing_mismatch_type, re); re.ty = TypeManager::GetInvalidTy(); } } else if (re.withExpr) { if (!Check(ctx, resumptionParamTy, re.withExpr)) { // `Check` produces the error message. re.ty = TypeManager::GetInvalidTy(); } } else { auto unitTy = typeManager.GetPrimitiveTy(TypeKind::TYPE_UNIT); if (!typeManager.IsSubtype(resumptionParamTy, unitTy)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_resume_no_with, re, Ty::ToString(resumptionParamTy)); re.ty = TypeManager::GetInvalidTy(); } } return re.ty; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/ReturnExpr.cpp000066400000000000000000000053041510705540100240250ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" using namespace Cangjie; using namespace AST; // The function does not need a target type since bottom type is a subtype of all (target) types. bool TypeChecker::TypeCheckerImpl::ChkReturnExpr(ASTContext& ctx, ReturnExpr& re) { return Ty::IsTyCorrect(SynReturnExpr(ctx, re)); } Ptr TypeChecker::TypeCheckerImpl::SynReturnExpr(ASTContext& ctx, ReturnExpr& re) { if (!re.refFuncBody || !re.refFuncBody->retType) { re.ty = TypeManager::GetInvalidTy(); return re.ty; } CJC_ASSERT(re.expr); bool isWellTyped = true; re.ty = TypeManager::GetInvalidTy(); // Analyse re.expr. auto retTy = re.refFuncBody->retType->ty; if (Ty::IsTyCorrect(retTy) && !retTy->IsQuest()) { bool isInConstructor = re.refFuncBody->funcDecl && IsInstanceConstructor(*re.refFuncBody->funcDecl); if (isInConstructor) { isWellTyped = CheckReturnInConstructors(ctx, re) && isWellTyped; } else { isWellTyped = Check(ctx, retTy, re.expr.get()); } if (isWellTyped) { ctx.targetTypeMap[re.expr.get()] = re.expr->ty; } } else { isWellTyped = Synthesize(ctx, re.expr.get()) && ReplaceIdealTy(*re.expr); } // Replace ClassThisTy to ClassTy when the function's outer declaration is not Class or Extend which extends class. if (!Is(re.refFuncBody->parentClassLike)) { if (auto ctt = DynamicCast(re.expr->ty); ctt && ctt->decl) { re.expr->ty = ctt->decl->ty; } } // Generic decls imported from foreign code and created by auto-sdk have no body, no need to check return. if (!isWellTyped && NeedCheckBodyReturn(*re.refFuncBody)) { re.ty = TypeManager::GetInvalidTy(); } else { re.ty = TypeManager::GetNothingTy(); } return re.ty; } bool TypeChecker::TypeCheckerImpl::CheckReturnInConstructors(ASTContext& ctx, const ReturnExpr& re) { CJC_NULLPTR_CHECK(re.expr); return Check(ctx, TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT), re.expr.get()); } bool TypeChecker::TypeCheckerImpl::NeedCheckBodyReturn(const FuncBody& fb) const { if (fb.parentClassLike && HasJavaAttr(*fb.parentClassLike) && fb.parentClassLike->TestAttr(Attribute::GENERIC, Attribute::IMPORTED)) { return false; } return !(fb.funcDecl && HasJavaAttr(*fb.funcDecl) && fb.funcDecl->TestAttr(Attribute::GENERIC, Attribute::IMPORTED)); } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/SpawnExpr.cpp000066400000000000000000000104131510705540100236330ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "Diags.h" using namespace Cangjie; using namespace Sema; Ptr TypeChecker::TypeCheckerImpl::SynSpawnExpr(ASTContext& ctx, SpawnExpr& se) { bool isWellTyped = !se.arg || (Ty::IsTyCorrect(Synthesize(ctx, se.arg.get())) && CheckSpawnArgValid(ctx, *se.arg)); CJC_NULLPTR_CHECK(se.task); isWellTyped = Ty::IsTyCorrect(Synthesize(ctx, se.task.get())) && isWellTyped; if (!isWellTyped) { se.ty = TypeManager::GetInvalidTy(); return se.ty; } auto futureClass = importManager.GetCoreDecl("Future"); if (futureClass == nullptr) { diag.Diagnose(se, DiagKind::sema_no_core_object); se.ty = TypeManager::GetInvalidTy(); return se.ty; } CJC_ASSERT(Ty::IsTyCorrect(se.task->ty) && se.task->ty->IsFunc()); CJC_ASSERT(!se.arg || (Ty::IsTyCorrect(se.arg->ty) && se.arg->ty->IsClassLike())); CJC_ASSERT(Ty::IsTyCorrect(futureClass->ty) && futureClass->ty->typeArgs.size() == 1); se.ty = typeManager.GetInstantiatedTy(futureClass->ty, {{StaticCast(futureClass->ty->typeArgs.front()), RawStaticCast(se.task->ty)->retTy}}); return se.ty; } bool TypeChecker::TypeCheckerImpl::CheckSpawnArgValid(const ASTContext& ctx, const Expr& arg) { CJC_ASSERT(arg.ty != nullptr); CJC_ASSERT(Ty::IsTyCorrect(arg.ty)); // Check the ty of spawn argument is `ThreadContext` which from package `core`. auto threadContextInterface = importManager.GetCoreDecl("ThreadContext"); if (threadContextInterface == nullptr) { (void)diag.Diagnose(arg, DiagKind::sema_no_core_object); return false; } if (arg.ty->IsNothing() || !arg.ty->IsClassLike() || !typeManager.IsSubtype(arg.ty, threadContextInterface->ty)) { DiagMismatchedTypes(diag, arg, *threadContextInterface->ty); return false; } // Check The spawn argument whether have `getSchedulerHandle` method, // whose signature is `()->CPointer`. If not, just prompts that the type is invalid. auto classLikeTy = StaticCast(arg.ty); auto retTy = typeManager.GetPointerTy(TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)); auto funcTy = typeManager.GetFunctionTy({}, retTy); CJC_NULLPTR_CHECK(arg.curFile); auto decls = FieldLookup(ctx, classLikeTy->commonDecl, "getSchedulerHandle", {.file = arg.curFile}); if (decls.size() != 1 || decls[0]->astKind != ASTKind::FUNC_DECL || !typeManager.IsSubtype(decls[0]->ty, funcTy)) { (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_spawn_arg_invalid, arg); return false; } return true; } bool TypeChecker::TypeCheckerImpl::ChkSpawnExprSimple(ASTContext& ctx, Ty& tgtTy, SpawnExpr& se) { if (Ty::IsTyCorrect(Synthesize(ctx, &se))) { if (typeManager.IsSubtype(se.ty, &tgtTy)) { return true; } else { DiagMismatchedTypes(diag, se, tgtTy); se.ty = TypeManager::GetInvalidTy(); return false; } } return false; } bool TypeChecker::TypeCheckerImpl::ChkSpawnExpr(ASTContext& ctx, Ty& tgtTy, SpawnExpr& se) { Ptr fuTy; if (TypeManager::IsCoreFutureType(tgtTy)) { fuTy = &tgtTy; } else { auto futureClass = importManager.GetCoreDecl("Future"); if (futureClass == nullptr) { diag.Diagnose(se, DiagKind::sema_no_core_object); return false; } auto fuTys = promotion.Downgrade(*futureClass->ty, tgtTy); if (fuTys.empty()) { return ChkSpawnExprSimple(ctx, tgtTy, se); } fuTy = *fuTys.begin(); } auto funcTy = typeManager.GetFunctionTy({}, fuTy->typeArgs.front()); bool isWellTyped = !se.arg || (Ty::IsTyCorrect(Synthesize(ctx, se.arg.get())) && CheckSpawnArgValid(ctx, *se.arg)); isWellTyped = Check(ctx, funcTy, se.task.get()) && isWellTyped; if (!isWellTyped) { se.ty = TypeManager::GetInvalidTy(); return false; } se.ty = fuTy; return true; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/SubscriptExpr.cpp000066400000000000000000000156611510705540100245330ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "Desugar/DesugarInTypeCheck.h" #include "DiagSuppressor.h" #include "Diags.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/RecoverDesugar.h" using namespace Cangjie; using namespace Sema; using namespace TypeCheckUtil; bool TypeChecker::TypeCheckerImpl::ChkSubscriptExpr(ASTContext& ctx, Ptr target, SubscriptExpr& se) { if (se.desugarExpr) { return typeManager.IsSubtype(se.desugarExpr->ty, target); } se.ty = TypeManager::GetInvalidTy(); // Set invalid ty at first, will be updated later. bool invalid = !se.baseExpr || se.indexExprs.empty(); if (invalid) { return false; } SetIsNotAlone(*se.baseExpr); Ptr baseTy = Synthesize(ctx, se.baseExpr.get()); std::vector> indexTys{}; for (auto& expr : se.indexExprs) { indexTys.push_back(Synthesize(ctx, expr.get())); } if (!Ty::IsTyCorrect(baseTy) || !Ty::AreTysCorrect(indexTys)) { return false; } // NOTE: Tuple and VArray type support built-in 'SubscriptExpr', others are all operator overload. if (auto tupleTy = DynamicCast(baseTy); tupleTy && se.indexExprs.size() == 1) { se.isTupleAccess = true; return ChkTupleAccess(ctx, target, se, *tupleTy); } if (auto varrTy = DynamicCast(baseTy); varrTy) { return ChkVArrayAccess(ctx, target, se, *varrTy); } auto ds = DiagSuppressor(diag); DesugarOperatorOverloadExpr(ctx, se); // Desugar to callExpr. // The type of baseExpr should not be inferred here! bool isWellTyped = target == nullptr ? Ty::IsTyCorrect(Synthesize(ctx, se.desugarExpr.get())) : Check(ctx, target, se.desugarExpr.get()); if (isWellTyped) { ds.ReportDiag(); se.ty = se.desugarExpr->ty; CJC_ASSERT(se.desugarExpr->astKind == ASTKind::CALL_EXPR); auto desugaredCE = StaticAs(se.desugarExpr.get()); CJC_ASSERT(desugaredCE->resolvedFunction != nullptr); ReplaceTarget(&se, desugaredCE->resolvedFunction); return true; } auto synTy = Synthesize(ctx, se.desugarExpr.get()); bool retTyMismatch = Ty::IsTyCorrect(target) && Ty::IsTyCorrect(synTy); RecoverToSubscriptExpr(se); // Also recover base and index's type. typeManager.ReplaceIdealTy(&baseTy); se.baseExpr->ty = se.baseExpr->ty ? se.baseExpr->ty : baseTy; for (size_t i = 0; i < se.indexExprs.size(); ++i) { typeManager.ReplaceIdealTy(&indexTys[i]); se.indexExprs[i]->ty = Ty::IsTyCorrect(se.indexExprs[i]->ty) ? se.indexExprs[i]->ty : indexTys[i]; } if (!ds.HasError() && se.ShouldDiagnose(true)) { // Only report subscript diagnoses if no error has beed reported. ds.ReportDiag(); // Report warnings. if (retTyMismatch) { CJC_NULLPTR_CHECK(target); DiagMismatchedTypesWithFoundTy(diag, se, *target, *synTy); } else { DiagInvalidSubscriptExpr(diag, se, *baseTy, indexTys); } } ds.ReportDiag(); return false; } bool TypeChecker::TypeCheckerImpl::ChkTupleAccess(ASTContext& ctx, Ptr target, SubscriptExpr& se, TupleTy& tupleTy) { if (se.baseExpr == nullptr || se.indexExprs.size() != 1) { return false; } if (se.indexExprs[0]->astKind != ASTKind::LIT_CONST_EXPR) { diag.Diagnose(*se.indexExprs[0], DiagKind::sema_builtin_invalid_index, "tuple"); return false; } if (!Check(ctx, TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64), se.indexExprs[0].get())) { diag.Diagnose(*se.indexExprs[0], DiagKind::sema_builtin_invalid_index, "tuple"); return false; } auto idxExpr = StaticAs(se.indexExprs[0].get()); uint64_t index = idxExpr->constNumValue.asInt.Uint64(); if (index >= tupleTy.typeArgs.size()) { if (se.ShouldDiagnose(true)) { diag.Diagnose(*se.indexExprs[0], DiagKind::sema_builtin_index_in_bound, "tuple"); } return false; } if (target == nullptr) { // Type inferring. se.ty = tupleTy.typeArgs[index]; return true; } if (auto tl = AST::As(se.baseExpr.get()); tl) { CJC_ASSERT(index < tl->children.size()); // Update tuple ty if baseExpr is a TupleLit whenever check succeed or failed, // because the check result will affect child expr of tuple lit which // may changing ideal ty to exact ty or changing valid ty to invalid ty. if (!Check(ctx, target, tl->children[index].get())) { // Reset the ty of tuple lit to allow re-synthesize of the tuple lit. tl->ty = TypeManager::GetInvalidTy(); return false; } std::vector> elemTy; for (auto& it : tl->children) { if (it != nullptr && ReplaceIdealTy(*it)) { elemTy.emplace_back(it->ty); } else { elemTy.emplace_back(TypeManager::GetInvalidTy()); } } tl->ty = typeManager.GetTupleTy(elemTy); se.ty = elemTy[index]; } else { if (!typeManager.IsSubtype(tupleTy.typeArgs[index], target)) { DiagMismatchedTypesWithFoundTy(diag, se, target->String(), tupleTy.typeArgs[index]->String()); return false; } se.ty = tupleTy.typeArgs[index]; } return true; } bool TypeChecker::TypeCheckerImpl::ChkVArrayAccess(ASTContext& ctx, Ptr target, SubscriptExpr& se, VArrayTy& varrTy) { if (se.indexExprs.size() != 1) { diag.DiagnoseRefactor(DiagKindRefactor::sema_varray_subscript_num, se); return false; } { DiagSuppressor ds(diag); // only for examing missing error report auto i64 = TypeManager::GetPrimitiveTy(TypeKind::TYPE_INT64); auto idxExpr = se.indexExprs[0].get(); if (!Check(ctx, i64, idxExpr)) { if (!ds.HasError()) { DiagMismatchedTypesWithFoundTy(diag, *idxExpr, i64->String(), idxExpr->ty->String()); } ds.ReportDiag(); return false; } ds.ReportDiag(); } CJC_ASSERT(!varrTy.typeArgs.empty() && varrTy.typeArgs[0]); if (target == nullptr) { // Type inferring. se.ty = varrTy.typeArgs[0]; return true; } // check subvalue type. if (!typeManager.IsSubtype(varrTy.typeArgs[0], target)) { DiagMismatchedTypesWithFoundTy(diag, se, target->String(), varrTy.typeArgs[0]->String()); return false; } se.ty = varrTy.typeArgs[0]; return true; } Ptr TypeChecker::TypeCheckerImpl::SynSubscriptExpr(ASTContext& ctx, SubscriptExpr& se) { ChkSubscriptExpr(ctx, nullptr, se); return se.ty; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/SynchronizedExpr.cpp000066400000000000000000000044101510705540100252220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "DiagSuppressor.h" using namespace Cangjie; using namespace AST; Ptr TypeChecker::TypeCheckerImpl::SynSyncExpr(ASTContext& ctx, SynchronizedExpr& se) { ChkSyncExpr(ctx, nullptr, se); return se.ty; } bool TypeChecker::TypeCheckerImpl::ChkSyncExpr(ASTContext& ctx, Ptr tgtTy, SynchronizedExpr& se) { bool isWellTyped = true; auto lockDecl = importManager.GetSyncDecl("Lock"); if (lockDecl) { isWellTyped = Check(ctx, lockDecl->ty, se.mutex.get()) && isWellTyped; } else { diag.DiagnoseRefactor(DiagKindRefactor::sema_use_expr_without_import, *se.mutex, "sync", "synchronized"); // Do not return false immediately so that more (and independent) error messages could be reported. } // Given sync (e) { b }, always check b if b exists (even if e is ill-typed). if (se.desugarExpr) { auto& b = RawStaticCast(se.desugarExpr.get())->body; // The desugared expression must have 3 children: a mutex declaration, mutex.lock() and a try expression. CJC_ASSERT(b.size() == 3); // Handle the mutex variable declaration. isWellTyped = Ty::IsTyCorrect(Synthesize(ctx, b.at(0).get())) && isWellTyped; // Handle the mutex.lock(). { // Create a scope for DiagSuppressor. Suppress errors raised by mutex.lock(). auto ds = DiagSuppressor(diag); if (Ty::IsTyCorrect(Synthesize(ctx, b.at(1).get()))) { ds.ReportDiag(); } else { isWellTyped = false; } } // The child at 2 is a try expression. auto te = RawStaticCast(b.at(2).get()); isWellTyped = (tgtTy ? ChkTryExpr(ctx, *tgtTy, *te) : Ty::IsTyCorrect(SynTryExpr(ctx, *te))) && isWellTyped; se.desugarExpr->ty = isWellTyped ? te->ty : TypeManager::GetInvalidTy(); se.ty = se.desugarExpr->ty; } else { isWellTyped = false; se.ty = TypeManager::GetInvalidTy(); } return isWellTyped; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/ThrowExpr.cpp000066400000000000000000000030421510705540100236460ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" using namespace Cangjie; using namespace AST; Ptr TypeChecker::TypeCheckerImpl::SynThrowExpr(ASTContext& ctx, ThrowExpr& te) { CJC_NULLPTR_CHECK(te.expr); // Parser guarantees. Synthesize(ctx, te.expr.get()); te.ty = TypeManager::GetNothingTy(); if (!Ty::IsTyCorrect(te.expr->ty)) { return TypeManager::GetInvalidTy(); } if (te.expr->ty->IsEnum()) { if (auto refExpr = DynamicCast(te.expr.get()); refExpr && refExpr->ref.identifier == RESOURCE_NAME) { return TypeManager::GetNothingTy(); } } else if (te.expr->ty->IsClass() || te.expr->ty->IsGeneric()) { // Check if the type of expression thrown is derived from `core.Exception` class // For class type and generic type. auto exception = importManager.GetCoreDecl(CLASS_EXCEPTION); auto error = importManager.GetCoreDecl(CLASS_ERROR); bool foundClass = exception && error; if (foundClass && (typeManager.IsSubtype(te.expr->ty, exception->ty) || typeManager.IsSubtype(te.expr->ty, error->ty))) { return TypeManager::GetNothingTy(); } } diag.Diagnose(te, DiagKind::sema_throw_expr_with_wrong_type); return TypeManager::GetInvalidTy(); } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/TryExpr.cpp000066400000000000000000000354571510705540100233400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "DiagSuppressor.h" #include "Diags.h" #include "JoinAndMeet.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Match.h" using namespace Cangjie; using namespace Sema; using namespace TypeCheckUtil; Ptr TypeChecker::TypeCheckerImpl::SynTryWithResourcesExpr(ASTContext& ctx, TryExpr& te) { auto resourceDecl = importManager.GetCoreDecl("Resource"); if (resourceDecl == nullptr) { te.ty = TypeManager::GetInvalidTy(); return te.ty; } Ptr resourceTy = resourceDecl->ty; bool isWellTyped = true; for (auto& vd : te.resourceSpec) { CJC_NULLPTR_CHECK(vd); if (!SynthesizeAndReplaceIdealTy(ctx, *vd)) { isWellTyped = false; vd->ty = TypeManager::GetInvalidTy(); // Avoid chaining errors. continue; } if (vd->ty->IsNothing() || !typeManager.IsSubtype(vd->ty, resourceTy)) { DiagMismatchedTypes( diag, *vd, *resourceTy, "the resource specification should implement interface 'Resource'"); vd->ty = TypeManager::GetInvalidTy(); // Avoid chaining errors. } } CJC_NULLPTR_CHECK(te.tryBlock); isWellTyped = SynthesizeAndReplaceIdealTy(ctx, *te.tryBlock) && isWellTyped; isWellTyped = ChkTryExprCatchPatterns(ctx, te) && isWellTyped; for (auto& catchBlock : te.catchBlocks) { CJC_NULLPTR_CHECK(catchBlock); isWellTyped = SynthesizeAndReplaceIdealTy(ctx, *catchBlock) && isWellTyped; } isWellTyped = ChkTryExprFinallyBlock(ctx, te) && isWellTyped; if (isWellTyped) { te.ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); } else { te.ty = TypeManager::GetInvalidTy(); } return te.ty; } Ptr TypeChecker::TypeCheckerImpl::SynTryExpr(ASTContext& ctx, TryExpr& te) { if (!te.resourceSpec.empty()) { return SynTryWithResourcesExpr(ctx, te); } bool isWellTyped; if (!te.handlers.empty() && te.tryLambda) { // For a try-handle expression, the try block has been replaced by a lambda, // but only if there were no syntax errors. isWellTyped = SynthesizeAndReplaceIdealTy(ctx, *te.tryLambda); } else { CJC_NULLPTR_CHECK(te.tryBlock); isWellTyped = SynthesizeAndReplaceIdealTy(ctx, *te.tryBlock); } auto optJTy = SynTryExprCatchesAndHandles(ctx, te); isWellTyped = optJTy.has_value() && isWellTyped; isWellTyped = ChkTryExprFinallyBlock(ctx, te) && isWellTyped; te.ty = isWellTyped ? *optJTy : TypeManager::GetInvalidTy(); return te.ty; } std::optional> TypeChecker::TypeCheckerImpl::SynTryExprCatchesAndHandles(ASTContext& ctx, TryExpr& te) { CJC_NULLPTR_CHECK(te.tryBlock); Ptr jTy = Ty::IsTyCorrect(te.tryBlock->ty) ? te.tryBlock->ty : TypeManager::GetNothingTy(); if (te.tryLambda && Ty::IsTyCorrect(te.tryLambda->ty)) { jTy = DynamicCast(te.tryLambda->ty)->retTy; } if ((te.catchPatterns.empty() || te.catchBlocks.empty()) && te.handlers.empty()) { return {jTy}; } bool isWellTyped = ChkTryExprCatchPatterns(ctx, te) && ChkTryExprHandlePatterns(ctx, te); if (!te.handlers.empty()) { isWellTyped = isWellTyped && ValidateBlockInTryHandle(*te.tryBlock); } for (auto& catchBlock : te.catchBlocks) { if (!SynthesizeAndReplaceIdealTy(ctx, *catchBlock) || (!te.handlers.empty() && !ValidateBlockInTryHandle(*catchBlock))) { isWellTyped = false; continue; } auto joinRes = JoinAndMeet( typeManager, std::initializer_list>{jTy, catchBlock->ty}, {}, &importManager, te.curFile) .JoinAsVisibleTy(); // Do not overwrite the previous jTy immediately for the sake of error reporting. Use a fresh type here. Ptr tmpJTy{}; if (auto optErrs = JoinAndMeet::SetJoinedType(tmpJTy, joinRes)) { isWellTyped = false; if (te.ShouldDiagnose()) { diag.Diagnose(*catchBlock, DiagKind::sema_diag_report_error_message, "The type of this catch block is '" + Ty::ToString(catchBlock->ty) + "', which mismatches the smallest common supertype '" + jTy->String() + "' of previous branches.") .AddNote(te, DiagKind::sema_diag_report_note_message, *optErrs); } } else { // Only overwrite jTy if the join operation succeeds. jTy = tmpJTy; } } for (auto& handler : te.handlers) { if (!SynHandler(ctx, handler, jTy, te)) { isWellTyped = false; } } return isWellTyped ? std::make_optional(jTy) : std::nullopt; } std::optional> TypeChecker::TypeCheckerImpl::PromoteToCommandTy(const AST::Node& cause, AST::Ty& cmdTy) { // Check if the type of expression performed is derived from `core.Command` interface // For class type and generic type. if (cmdTy.IsInvalid()) { // If the command type is erroneous, the error has already been reported before // calling this function, do not report again return std::nullopt; } auto cmdClassDecl = importManager.GetImportedDecl(EFFECT_PACKAGE_NAME, CLASS_COMMAND); if (!cmdClassDecl) { return std::nullopt; } auto promotedTys = promotion.Promote(cmdTy, *cmdClassDecl->ty); if (promotedTys.size() != 1) { diag.DiagnoseRefactor(DiagKindRefactor::sema_command_incompatible_type, cause, Ty::ToString(&cmdTy)); return std::nullopt; } return StaticCast(*promotedTys.begin()); } bool TypeChecker::TypeCheckerImpl::SynHandler(ASTContext& ctx, Handler& handler, Ptr tgtTy, TryExpr& te) { // We need to validate the handler before synthesizing since it's necessary for // resume expressions if (!ValidateHandler(handler)) { return false; } if (!SynthesizeAndReplaceIdealTy(ctx, *handler.block)) { return false; } auto joinRes = JoinAndMeet( typeManager, std::initializer_list>{tgtTy, handler.block->ty}, {}, &importManager, te.curFile) .JoinAsVisibleTy(); // Do not overwrite the previous jTy immediately for the sake of error reporting. Use a fresh type here. Ptr tmpJTy{}; if (auto optErrs = JoinAndMeet::SetJoinedType(tmpJTy, joinRes)) { if (te.ShouldDiagnose()) { diag.DiagnoseRefactor(DiagKindRefactor::sema_mismatching_handle_block, *handler.block, Ty::ToString(handler.block->ty), tgtTy->String()) .AddNote(te, DiagKind::sema_diag_report_note_message, *optErrs); } return false; } else { // Only overwrite jTy if the join operation succeeds. tgtTy = tmpJTy; } return ChkHandler(ctx, handler, *tgtTy); } bool TypeChecker::TypeCheckerImpl::ChkHandler(ASTContext& ctx, Handler& handler, Ty& tgtTy) { if (!handler.commandPattern || !handler.desugaredLambda) { // Parse error has already been reported. return false; } if (!ValidateHandler(handler)) { return false; } auto cmdTy = StaticAs(handler.commandPattern.get())->pattern->ty; if (cmdTy->IsInvalid()) { return false; } std::vector> args; args.emplace_back(cmdTy); Ptr handleLambdaTy = typeManager.GetFunctionTy(args, &tgtTy); if (!Check(ctx, handleLambdaTy, handler.desugaredLambda)) { DiagMismatchedTypes(diag, *handler.desugaredLambda->funcBody->body, tgtTy); return false; } return true; } bool TypeChecker::TypeCheckerImpl::ChkTryExpr(ASTContext& ctx, Ty& tgtTy, TryExpr& te) { if (!te.resourceSpec.empty()) { auto ty = SynTryWithResourcesExpr(ctx, te); if (!Ty::IsTyCorrect(ty)) { return false; } if (!typeManager.IsSubtype(ty, &tgtTy)) { DiagMismatchedTypes(diag, te, tgtTy, "try-with-resources expressions are of type 'Unit'"); return false; } return true; } CJC_NULLPTR_CHECK(te.tryBlock); bool isWellTyped = true; if (!te.handlers.empty()) { // Careful: if there are handlers, then the body of the try is empty because we turned // it into a lambda during parsing auto tryLambdaTy = typeManager.GetFunctionTy({}, &tgtTy); if (!te.tryLambda || !Check(ctx, tryLambdaTy, te.tryLambda)) { isWellTyped = false; if (!CanSkipDiag(*te.tryBlock) && !typeManager.IsSubtype(te.tryBlock->ty, &tgtTy)) { DiagMismatchedTypes(diag, *te.tryBlock, tgtTy); } } } else if (!Check(ctx, &tgtTy, te.tryBlock.get())) { isWellTyped = false; if (!CanSkipDiag(*te.tryBlock) && !typeManager.IsSubtype(te.tryBlock->ty, &tgtTy)) { DiagMismatchedTypes(diag, *te.tryBlock, tgtTy); } } isWellTyped = ChkTryExprCatchesAndHandles(ctx, tgtTy, te) && isWellTyped; isWellTyped = ChkTryExprFinallyBlock(ctx, te) && isWellTyped; te.ty = isWellTyped ? &tgtTy : TypeManager::GetInvalidTy(); return isWellTyped; } bool TypeChecker::TypeCheckerImpl::ChkTryExprCatchesAndHandles(ASTContext& ctx, Ty& tgtTy, TryExpr& te) { bool isWellTyped = ChkTryExprCatchPatterns(ctx, te) && ChkTryExprHandlePatterns(ctx, te); for (auto& catchBlock : te.catchBlocks) { if (Check(ctx, &tgtTy, catchBlock.get())) { continue; } isWellTyped = false; if (!CanSkipDiag(*catchBlock) && !typeManager.IsSubtype(catchBlock->ty, &tgtTy)) { DiagMismatchedTypes(diag, *catchBlock, tgtTy); // Do not return immediately. Report errors for each case. } } for (auto& handler : te.handlers) { if (!ChkHandler(ctx, handler, tgtTy)) { isWellTyped = false; } } return isWellTyped; } bool TypeChecker::TypeCheckerImpl::ChkTryExprFinallyBlock(ASTContext& ctx, const TryExpr& te) { if (!te.finallyBlock) { return true; } bool isWellTyped = true; if (te.isDesugaredFromSyncBlock) { // Suppress errors raised from the desugared mutex.unlock(), which should not be reported anyway. auto ds = DiagSuppressor(diag); if (Ty::IsTyCorrect(Synthesize(ctx, te.finallyBlock.get()))) { ds.ReportDiag(); } else { isWellTyped = false; } } else { isWellTyped = Ty::IsTyCorrect(Synthesize(ctx, te.finallyBlock.get())) && isWellTyped; if (!te.handlers.empty() && te.finallyLambda) { auto finallyLamTy = typeManager.GetFunctionTy({}, TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)); isWellTyped = Check(ctx, finallyLamTy, te.finallyLambda) && isWellTyped; } } te.finallyBlock->ty = isWellTyped ? StaticCast(TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT)) : TypeManager::GetInvalidTy(); return isWellTyped; } bool TypeChecker::TypeCheckerImpl::ChkTryExprCatchPatterns(ASTContext& ctx, TryExpr& te) { std::vector> included{}; auto exception = importManager.GetCoreDecl(CLASS_EXCEPTION); for (auto& pattern : te.catchPatterns) { CJC_NULLPTR_CHECK(pattern); if (pattern->astKind == ASTKind::WILDCARD_PATTERN) { if (exception == nullptr || !ChkTryWildcardPattern(exception->ty, *StaticAs(pattern.get()), included)) { return false; } included.push_back(exception->ty); } else if (pattern->astKind == ASTKind::EXCEPT_TYPE_PATTERN) { if (!ChkExceptTypePattern(ctx, *StaticAs(pattern.get()), included)) { return false; } } else { return false; } } return true; } bool TypeChecker::TypeCheckerImpl::ChkTryExprHandlePatterns(ASTContext& ctx, TryExpr& te) { std::vector> included{}; for (auto& handler : te.handlers) { if (handler.commandPattern.get()->astKind != ASTKind::COMMAND_TYPE_PATTERN || !handler.desugaredLambda) { // Parse error has already been reported. continue; } if (!ChkHandlePatterns(ctx, handler, included)) { return false; } } return true; } bool TypeChecker::TypeCheckerImpl::ValidateHandler(Handler& h) { bool valid = ValidateBlockInTryHandle(*h.block); std::vector> scopeChanges; if (valid) { // If the handler is immediate, then we need to walk the body of the handle blocks // and bind any implicit `resume` to it. if (!h.desugaredLambda) { // The AST was invalid, we do not bother to check (an error was already reported) return false; } std::function)> bind = [&h](Ptr node) -> VisitAction { switch (node->astKind) { case ASTKind::RESUME_EXPR: { auto re = StaticAs(node); re->enclosing = &h; return VisitAction::WALK_CHILDREN; } case ASTKind::FUNC_BODY: return VisitAction::SKIP_CHILDREN; default: return VisitAction::WALK_CHILDREN; } }; // It's important that we walk the body of the desugared lambda, since we're // binding the resumeExpr nodes contained within Walker(h.desugaredLambda->funcBody->body, bind).Walk(); // This is redundant but we need to bind the `resume`s inside the block so that // we can typecheck it Walker(h.block, bind).Walk(); } return valid; } bool TypeChecker::TypeCheckerImpl::ValidateBlockInTryHandle(Block& block) { bool hasReturns = false; const auto& findReturns = [this, &hasReturns](Ptr node) -> VisitAction { switch (node->astKind) { case ASTKind::FUNC_BODY: return VisitAction::SKIP_CHILDREN; case ASTKind::TRY_EXPR: if (StaticAs(node)->handlers.empty()) { return VisitAction::WALK_CHILDREN; } else { return VisitAction::SKIP_CHILDREN; } case ASTKind::RETURN_EXPR: diag.DiagnoseRefactor(DiagKindRefactor::sema_return_in_try_handle_block, *node); hasReturns = true; return VisitAction::STOP_NOW; default: return VisitAction::WALK_CHILDREN; } }; Walker(&block, findReturns).Walk(); return !hasReturns; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/TupleLit.cpp000066400000000000000000000051451510705540100234540ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "Diags.h" #include "TypeCheckUtil.h" using namespace Cangjie; using namespace Sema; using namespace TypeCheckUtil; bool TypeChecker::TypeCheckerImpl::ChkTupleLit(ASTContext& ctx, Ty& target, TupleLit& tl) { if (target.IsAny()) { tl.ty = Synthesize(ctx, &tl); ReplaceIdealTy(tl); return Ty::IsTyCorrect(tl.ty); } Ptr targetTy = UnboxOptionType(&target); if (!Ty::IsTyCorrect(targetTy) || !targetTy->IsTuple()) { DiagMismatchedTypesWithFoundTy(diag, tl, targetTy->String(), "Tuple"); tl.ty = TypeManager::GetNonNullTy(tl.ty); return false; } auto tupleTy = StaticCast(targetTy); auto typeArgs = tupleTy->typeArgs; if (typeArgs.size() != tl.children.size()) { tl.ty = Synthesize(ctx, &tl); ReplaceIdealTy(tl); DiagMismatchedTypes(diag, tl, *targetTy); return false; } // If the size of target elemTys and elements are equal, check one by one. std::vector> realElemTys; for (size_t i = 0; i < typeArgs.size(); ++i) { CJC_NULLPTR_CHECK(tl.children[i]); if (!Check(ctx, typeArgs[i], tl.children[i].get())) { if (Ty::IsTyCorrect(typeArgs[i]) && Ty::IsTyCorrect(tl.children[i]->ty)) { DiagMismatchedTypes(diag, *tl.children[i], *typeArgs[i]); } tl.ty = Synthesize(ctx, &tl); ReplaceIdealTy(tl); return false; } else { realElemTys.push_back(tl.children[i]->ty); } } // Should use SetTy(), but have bugs on ideal type, use Join() instead at current stage. // TupleLit allow elements been boxed by given target type. // Eg. Option*Option <=> (1,1) or I1*I1 <=> (1,1) where Int64 extends I1 allow box. tl.ty = targetTy; return true; } Ptr TypeChecker::TypeCheckerImpl::SynTupleLit(ASTContext& ctx, TupleLit& tl) { std::vector> elemTy; // Synthesize the type of each element. for (auto& it : tl.children) { if (!it) { tl.ty = TypeManager::GetInvalidTy(); return tl.ty; } if (!Ty::IsTyCorrect(it->ty)) { (void)Synthesize(ctx, it.get()); } ReplaceIdealTy(*it); elemTy.push_back(it->ty); } tl.ty = typeManager.GetTupleTy(elemTy); return tl.ty; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/TypeConvExpr.cpp000066400000000000000000000055241510705540100243210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "Diags.h" #include "TypeCheckUtil.h" #include "cangjie/AST/RecoverDesugar.h" using namespace Cangjie; using namespace Sema; using namespace TypeCheckUtil; Ptr TypeChecker::TypeCheckerImpl::SynTypeConvExpr(ASTContext& ctx, TypeConvExpr& tce) { CJC_NULLPTR_CHECK(tce.expr); CJC_NULLPTR_CHECK(tce.type); Synthesize(ctx, tce.expr.get()); ReplaceIdealTy(*tce.expr); if (tce.type->astKind == ASTKind::PRIMITIVE_TYPE) { return SynNumTypeConvExpr(tce); } // The TypeConvExpr supports conversion between primitive types and conversion from CPointer to CFunc. // Therefore, the function should be returned in either of the above two branches. // Otherwise, there must be errors reported by other modules or logic codes. tce.ty = TypeManager::GetInvalidTy(); return tce.ty; } Ptr TypeChecker::TypeCheckerImpl::SynNumTypeConvExpr(TypeConvExpr& tce) { tce.ty = TypeManager::GetPrimitiveTy(StaticCast(tce.type.get())->kind); if (!Ty::IsTyCorrect(tce.expr->ty) || !Ty::IsTyCorrect(tce.ty)) { tce.ty = TypeManager::GetInvalidTy(); return tce.ty; } // Case 0: expr is of Nothing type, e.g., `UInt32(return)` bool isExprNothing = (tce.ty->kind == TypeKind::TYPE_RUNE || tce.ty->IsNumeric()) && tce.expr->ty->IsNothing(); // Case 1: Rune to UInt32, e.g., `UInt32('a')` bool isRuneToUInt32 = tce.ty->kind == TypeKind::TYPE_UINT32 && tce.expr->ty->kind == TypeKind::TYPE_RUNE; // Case 2: Integer to Rune, e.g., `Rune(97)` bool isIntegerToChar = tce.ty->kind == TypeKind::TYPE_RUNE && tce.expr->ty->IsInteger(); // Case 3: convert between numeric types bool isBetweenNumeric = tce.ty->IsNumeric() && tce.expr->ty->IsNumeric(); if (isExprNothing || isRuneToUInt32 || isIntegerToChar || isBetweenNumeric) { return tce.ty; } // Otherwise, return false. if (!CanSkipDiag(*tce.expr)) { diag.Diagnose(*tce.expr, DiagKind::sema_numeric_convert_must_be_numeric); } tce.ty = TypeManager::GetInvalidTy(); return tce.ty; } bool TypeChecker::TypeCheckerImpl::ChkTypeConvExpr(ASTContext& ctx, Ty& targetTy, TypeConvExpr& tce) { // Additionally, given a context type T0 and an expression T1(t), since T1(t) : T1, we always require T1 <: T0. if (Ty::IsTyCorrect(SynTypeConvExpr(ctx, tce)) && typeManager.IsSubtype(tce.ty, &targetTy)) { return true; } else { if (!CanSkipDiag(tce)) { DiagMismatchedTypes(diag, tce, targetTy); } tce.ty = TypeManager::GetInvalidTy(); return false; } } cangjie_compiler-1.0.7/src/Sema/TypeCheckExpr/UnaryExpr.cpp000066400000000000000000000147171510705540100236540ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "TypeCheckerImpl.h" #include "BuiltInOperatorUtil.h" #include "Desugar/DesugarInTypeCheck.h" #include "Diags.h" #include "TypeCheckUtil.h" #include "cangjie/AST/RecoverDesugar.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Utils/CheckUtils.h" #include "DiagSuppressor.h" using namespace Cangjie; using namespace Sema; using namespace TypeCheckUtil; namespace { Expr& GetNonParenExpr(ParenExpr& pe) { Ptr parent = &pe; CJC_NULLPTR_CHECK(parent->expr); while (parent->expr->astKind == ASTKind::PAREN_EXPR) { parent = StaticCast(parent->expr.get()); CJC_NULLPTR_CHECK(parent->expr); } return *parent->expr; } UnaryExpr& GetLeafUnaryExpr(UnaryExpr& ue) { Ptr leaf = &ue; while (true) { CJC_NULLPTR_CHECK(leaf->expr); // If a `UnaryExpr` has `desugarExpr`, it is guaranteed to be correct and cannot be the leaf. if (leaf->expr->astKind == ASTKind::UNARY_EXPR && leaf->expr->desugarExpr == nullptr) { leaf = StaticCast(leaf->expr.get()); } else if (leaf->expr->astKind == ASTKind::PAREN_EXPR) { Expr& child = GetNonParenExpr(StaticCast(*leaf->expr)); if (child.astKind == ASTKind::UNARY_EXPR && child.desugarExpr == nullptr) { leaf = StaticCast(&child); } else { break; } } else { break; } CJC_NULLPTR_CHECK(leaf); } return *leaf; } } // namespace void TypeChecker::TypeCheckerImpl::DiagnoseForUnaryExprWithTarget(ASTContext& ctx, UnaryExpr& ue, Ty& target) { // `leaf` is responsible for the invalid type. UnaryExpr& leaf = GetLeafUnaryExpr(ue); if (Ty::IsTyCorrect(Synthesize(ctx, leaf.expr.get())) && ReplaceIdealTy(*leaf.expr)) { DiagInvalidUnaryExprWithTarget(diag, leaf, target); } } void TypeChecker::TypeCheckerImpl::DiagnoseForUnaryExpr(ASTContext& ctx, UnaryExpr& ue) { // `leaf` is responsible for the invalid type. UnaryExpr& leaf = GetLeafUnaryExpr(ue); if (Ty::IsTyCorrect(Synthesize(ctx, leaf.expr.get())) && ReplaceIdealTy(*leaf.expr)) { DiagInvalidUnaryExpr(diag, leaf); } } Ptr TypeChecker::TypeCheckerImpl::SynUnaryExpr(ASTContext& ctx, UnaryExpr& ue) { if (ue.desugarExpr) { return ue.desugarExpr->ty; } if (!Ty::IsTyCorrect(Synthesize(ctx, ue.expr.get()))) { ue.ty = TypeManager::GetInvalidTy(); return ue.ty; } if (Ty::IsTyCorrect(SynBuiltinUnaryExpr(ctx, ue))) { ReplaceIdealTy(*ue.expr); ue.ty = ue.expr->ty; return ue.ty; } DesugarOperatorOverloadExpr(ctx, ue); if (Ty::IsTyCorrect(Synthesize(ctx, ue.desugarExpr.get()))) { ue.ty = ue.desugarExpr->ty; ReplaceTarget(&ue, StaticCast(ue.desugarExpr.get())->resolvedFunction); } else { RecoverToUnaryExpr(ue); DiagnoseForUnaryExpr(ctx, ue); ue.ty = TypeManager::GetInvalidTy(); } return ue.ty; } Ptr TypeChecker::TypeCheckerImpl::SynBuiltinUnaryExpr(ASTContext& ctx, UnaryExpr& ue) { auto ty = Synthesize(ctx, ue.expr.get()); if (!Ty::IsTyCorrect(ty)) { return TypeManager::GetInvalidTy(); } // Check if type is available for the given operator. if (!IsUnaryOperator(ue.op)) { return TypeManager::GetInvalidTy(); } const auto& typeCandidate = GetUnaryOpTypeCandidates(ue.op); if (auto tv = DynamicCast(ty); tv && tv->isPlaceholder) { switch (PickConstaintFromTys(*tv, TypeMapToTys(typeCandidate, true), true)) { case MatchResult::UNIQUE: ue.ty = typeManager.TryGreedySubst(tv); return ue.ty; case MatchResult::AMBIGUOUS: case MatchResult::NONE: return TypeManager::GetInvalidTy(); } } for (auto& type : typeCandidate) { auto primitiveTy = TypeManager::GetPrimitiveTy(type.second); if (typeManager.IsSubtype(ty, primitiveTy)) { ue.ty = ty; return ty; } } return TypeManager::GetInvalidTy(); } bool TypeChecker::TypeCheckerImpl::ChkUnaryExpr(ASTContext& ctx, Ty& target, UnaryExpr& ue) { if (ue.desugarExpr) { return typeManager.IsSubtype(ue.desugarExpr->ty, &target); } bool isWellTyped = true; // If the 'target' is correct type, 'unboxedTy' must also be correct; auto unboxedTy = TypeCheckUtil::UnboxOptionType(&target); Ptr retTy = nullptr; // If 'unboxedTy' is builtin type, we need to use this type checking with expression for ideal literal, // otherwise only only check type relation here for possible type boxing relation. if (unboxedTy->IsBuiltin()) { DiagSuppressor ds(diag); // report error later isWellTyped = Check(ctx, unboxedTy, ue.expr.get()); ue.expr->ty = typeManager.TryGreedySubst(ue.expr->ty); retTy = SynBuiltinUnaryExpr(ctx, ue); isWellTyped = isWellTyped && Ty::IsTyCorrect(retTy) && Ty::IsTyCorrect(&target); } else { retTy = SynBuiltinUnaryExpr(ctx, ue); isWellTyped = Ty::IsTyCorrect(retTy) && Ty::IsTyCorrect(&target) && typeManager.IsSubtype(retTy, &target); } if (isWellTyped) { // If this is a built-in unary expr. ReplaceIdealTy(*ue.expr); ue.ty = ue.expr->ty; return true; } // Try operator overload. DesugarOperatorOverloadExpr(ctx, ue); if (Check(ctx, &target, ue.desugarExpr.get())) { ue.ty = ue.desugarExpr->ty; ReplaceTarget(&ue, StaticCast(ue.desugarExpr.get())->resolvedFunction); return true; } auto synTy = Synthesize(ctx, ue.desugarExpr.get()); RecoverToUnaryExpr(ue); ctx.DeleteDesugarExpr(ue.desugarExpr); // Report errors. typeManager.ReplaceIdealTy(&retTy); bool retTyMismatch = isWellTyped || (Ty::IsTyCorrect(&target) && Ty::IsTyCorrect(synTy)); if (retTyMismatch) { DiagMismatchedTypesWithFoundTy(diag, ue, target, isWellTyped ? *retTy : *synTy); } else if (Ty::IsTyCorrect(&target)) { DiagnoseForUnaryExprWithTarget(ctx, ue, target); } else { DiagnoseForUnaryExpr(ctx, ue); } ue.ty = TypeManager::GetInvalidTy(); return false; } cangjie_compiler-1.0.7/src/Sema/TypeCheckExtend.cpp000066400000000000000000000675551510705540100222400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements extend type check. */ #include "TypeCheckUtil.h" #include "TypeCheckerImpl.h" #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Utils/CheckUtils.h" using namespace Cangjie; using namespace TypeCheckUtil; using namespace AST; void TypeChecker::TypeCheckerImpl::CheckExtendGenerics(const ExtendDecl& ed) { if (!ed.generic || !Ty::IsTyCorrect(ed.extendedType->ty) || !ed.extendedType->ty->IsExtendable()) { return; } auto usedGenericTys = GetAllGenericTys(ed.extendedType->ty); std::vector unusedTypes; for (auto& it : ed.generic->typeParameters) { if (usedGenericTys.count(it->ty) == 0) { unusedTypes.emplace_back(it->identifier); } } if (!unusedTypes.empty()) { std::string typeStr = " '" + Utils::JoinStrings(unusedTypes, "', '") + "'"; if (unusedTypes.size() > 1) { typeStr = "s" + typeStr; // add suffix 's' for plural. } diag.DiagnoseRefactor(DiagKindRefactor::sema_extend_generic_must_be_used, *ed.extendedType, typeStr); } } void TypeChecker::TypeCheckerImpl::CheckExtendedTypeValidity(const Type& extendedType) { if (!Ty::IsTyCorrect(extendedType.ty) || extendedType.ty->IsExtendable()) { return; } // All other types are not allowed to be extended. diag.DiagnoseRefactor(DiagKindRefactor::sema_illegal_extended_type, extendedType, extendedType.ty->String()); } /** * Check procedure: * 1. Find same interface declaration for the extension. Follow case 'A' will be found. * 2. Check whether the default implementation does not depend on external generic parameters is included. * * interface A { * func foo(): Unit {} * } * class B {} * extend B <: A { * // func foo(): Unit {} * } * extend B <: A { * // func foo(): Unit {} // same signature, should be diag. * } */ void TypeChecker::TypeCheckerImpl::CheckExtendDupDefImplByDiffTypArgs( const std::set, CmpNodeByPos>& extendDecls, const ExtendDecl& extend) { std::set> directlyImplementedInterfaces; for (auto e : extendDecls) { if (e == &extend) { continue; } for (auto& inhert : e->inheritedTypes) { auto inhertDecl = Ty::GetDeclPtrOfTy(inhert->ty); directlyImplementedInterfaces.emplace(inhertDecl); } } for (auto& inhert : extend.inheritedTypes) { auto inhertDecl = Ty::GetDeclPtrOfTy(inhert->ty); auto found = directlyImplementedInterfaces.find(inhertDecl); if (found != directlyImplementedInterfaces.end()) { CheckDefImplWithoutOutsideGeneric(*inhertDecl, extend); } } } void TypeChecker::TypeCheckerImpl::CheckDefImplWithoutOutsideGeneric(Decl& inhertDecl, const ExtendDecl& extend) { auto id = DynamicCast(&inhertDecl); if (!id || !id->generic || id->generic->typeParameters.empty()) { return; } std::vector> outersideGenericParamTys; for (auto& tp : id->generic->typeParameters) { outersideGenericParamTys.emplace_back(tp->ty); } for (auto& member : id->GetMemberDecls()) { if (!member->TestAttr(Attribute::DEFAULT) || !Ty::IsTyCorrect(member->ty)) { continue; } auto usedGenericTypeParamTys = member->ty->GetGenericTyArgs(); bool usedOutersideGeneric = false; for (auto& outer : outersideGenericParamTys) { if (Utils::In(outer, usedGenericTypeParamTys)) { usedOutersideGeneric = true; break; } } if (!usedOutersideGeneric) { std::string typeName = "extend " + extend.ty->String(); auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_extend_member_cannot_shadow, MakeRange(member->identifier), member->identifier.Val(), typeName); } } } void TypeChecker::TypeCheckerImpl::CheckExtendInterfaces( Ty& ty, const std::set, CmpNodeByPos>& extendDecls) { std::unordered_set> inheritInterfaces; if (auto extendedDecl = Ty::GetDeclPtrOfTy(&ty); extendedDecl) { std::vector> allSuperTys{}; for (const auto& inheritedType : extendedDecl->inheritedTypes) { if (inheritedType->ty) { auto superTys = typeManager.GetAllSuperTys(*inheritedType->ty); allSuperTys.insert(allSuperTys.end(), superTys.begin(), superTys.end()); } } for (const auto& superTy : allSuperTys) { if (superTy && superTy->IsInterface()) { inheritInterfaces.emplace(RawStaticCast(superTy)); } } } std::unordered_map, std::set, CmpNodeByPos>> extendInterfaces; for (auto& extendDecl : extendDecls) { // Check all extend decls of it.first type. if (!extendDecl->extendedType) { continue; } MultiTypeSubst typeMapping; if (extendDecl->extendedType->ty) { typeMapping = promotion.GetPromoteTypeMapping(ty, *extendDecl->extendedType->ty); } // Check all implemented interfaces of this extend decl. for (const auto& interface : extendDecl->inheritedTypes) { auto instantiateTy = typeManager.GetBestInstantiatedTy(interface->ty, typeMapping); if (!instantiateTy->IsInterface()) { continue; } auto interfaceTy = RawStaticCast(instantiateTy); extendInterfaces[interfaceTy].insert(interface.get()); if (interfaceTy->decl && Utils::In(interfaceTy->decl->identifier.Val(), {std::string("Any"), CTYPE_NAME}) && interfaceTy->decl->fullPackageName == CORE_PACKAGE_NAME) { diag.DiagnoseRefactor( DiagKindRefactor::sema_interface_is_not_extendable, *interface, interfaceTy->decl->identifier); } } } std::set, CmpTyByName> keys; std::for_each(extendInterfaces.begin(), extendInterfaces.end(), [&keys](auto& it) { keys.emplace(it.first); }); for (auto interfaceTy : keys) { // Duplicated with extended type or duplicated with other extends. auto& typeSet = extendInterfaces[interfaceTy]; if (inheritInterfaces.count(interfaceTy) > 0 || typeSet.size() > 1) { // When multiple interfaces are implemented repeatedly, only diagnose at the last implementation position. // The pre-process ensures that the size of typeSet is at least 1. CJC_ASSERT(!typeSet.empty()); const auto type = *typeSet.crbegin(); if (!type->TestAttr(Attribute::IMPORTED)) { diag.DiagnoseRefactor( DiagKindRefactor::sema_extend_duplicate_interface, *type, interfaceTy->String(), ty.String()); } } } } // Do check interface implementation of the special extended version. // eg: Line 1: interface I {} // Line 2: class A {} // Line 3: extend A <: I {} // Line 4: extend A <: I {} // They might get the same instantiation version: 'A <: I', this is illeage in spec. void TypeChecker::TypeCheckerImpl::CheckSpecializationExtend(const InheritableDecl& extendedDecl, const ExtendDecl& extendDecl, const std::set, CmpNodeByPos> otherExtendDecls) { // Only check specialized versions of generic types. Only Line 3 will be checked. if (extendedDecl.ty && !extendedDecl.ty->HasGeneric()) { return; } // Get typemapping from 'class A'(in Line 2) to 'extend A'(in Line 3), it should return K |-> String. auto orig2specMapping = GenerateTypeMapping(extendedDecl, extendDecl.extendedType->ty->typeArgs); // Check if type definition is 'class A <: I'. The super interface will be instantiated as 'I'. CheckSpecializationExtendDupImstantation(*extendedDecl.ty, extendDecl, extendedDecl, orig2specMapping, true); // Check with other generic extend declarations. for (auto ed : otherExtendDecls) { // Exclude all non-generic declaration. if (!ed->ty || !ed->ty->HasGeneric()) { continue; } // Get typemapping from 'class A'(in Line 2) to 'extend A'(in line 4), it should return K |-> T. auto orig2genericMapping = GenerateTypeMapping(extendedDecl, ed->extendedType->ty->typeArgs); TypeSubst instantMapping; bool needCheck = true; for (auto typeArg : extendedDecl.ty->typeArgs) { auto tyArgGen = StaticCast(typeArg); auto mapping = GenerateTypeMappingByTy(orig2genericMapping[tyArgGen], orig2specMapping[tyArgGen]); for (auto m : mapping) { auto found = instantMapping.emplace(m); // If the same generic T maps to different types, the two extends must not conflict. // if 'T |-> TypeA' and 'T |-> TypeB', 'extend A' and 'extend A' must not conflict. if (!found.second && found.first->second != m.second) { needCheck = false; break; } } } if (needCheck) { CheckSpecializationExtendDupImstantation(*extendedDecl.ty, extendDecl, *ed, instantMapping); } } } void TypeChecker::TypeCheckerImpl::CheckSpecializationExtendDupImstantation(const Ty& extendedDeclTy, const ExtendDecl& compareExtend, const InheritableDecl& beComparedDecl, const TypeSubst& instantMapping, const bool checkParent) { // 'compareExtend' is Specialized Version, 'beComparedDecl' is Generic Version. for (auto& compareSuperInterface : compareExtend.inheritedTypes) { if (!compareSuperInterface || compareSuperInterface->ty->HasGeneric() || compareSuperInterface->ty->typeArgs.empty()) { continue; } std::unordered_set> allSuperTys{}; for (const auto& inheritedType : beComparedDecl.inheritedTypes) { if (!inheritedType->ty) { continue; } if (checkParent) { allSuperTys.merge(typeManager.GetAllSuperTys(*inheritedType->ty)); } else { allSuperTys.emplace(inheritedType->ty); } } for (auto& beComparedSuperInterfaceTy : allSuperTys) { if (!beComparedSuperInterfaceTy || beComparedSuperInterfaceTy->kind != TypeKind::TYPE_INTERFACE || !beComparedSuperInterfaceTy->HasGeneric()) { continue; } auto instantTy = typeManager.GetInstantiatedTy(beComparedSuperInterfaceTy, instantMapping); if (instantTy != compareSuperInterface->ty) { continue; } auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_extend_duplicate_interface, *compareSuperInterface, compareSuperInterface->ty->String(), extendedDeclTy.String()); builder.AddNote(MakeRange(beComparedDecl.begin, beComparedDecl.end), "it may be an instantiated version of the following declaration"); } } } void TypeChecker::TypeCheckerImpl::PreCheckAllExtendInterface() { // Check extend decls of primitive types in stable order. std::set, CmpTyByName> keys; std::for_each(typeManager.builtinTyToExtendMap.begin(), typeManager.builtinTyToExtendMap.end(), [&keys](auto& it) { keys.emplace(it.first); }); for (auto ty : keys) { CJC_ASSERT(ty); auto& extends = typeManager.builtinTyToExtendMap[ty]; std::set, CmpNodeByPos> ordered(extends.begin(), extends.end()); CheckExtendInterfaces(*ty, ordered); // The CPointer type is a generic type and can be extended by users. It need to be checked for duplicate // implementations of the specialized version. if (ty->IsPointer()) { auto extendDeclInCore = std::find_if(ordered.begin(), ordered.end(), [](const Ptr e) { return e->fullPackageName == "std.core"; }); for (auto ed : ordered) { if (extendDeclInCore == ordered.end() || ed == *extendDeclInCore) { continue; } CheckSpecializationExtend(**extendDeclInCore, *ed, ordered); } } } // Check extend decls of class/struct/enum in stable order. auto extendedDecls = typeManager.GetAllExtendedDecls(); std::set, CmpNodeByPos> declKeys(extendedDecls.begin(), extendedDecls.end()); for (auto decl : declKeys) { CJC_ASSERT(decl->ty); auto extends = typeManager.GetDeclExtends(*decl); std::set, CmpNodeByPos> ordered(extends.begin(), extends.end()); CheckExtendInterfaces(*decl->ty, ordered); for (auto ed : ordered) { CheckSpecializationExtend(*decl, *ed, ordered); } } } void TypeChecker::TypeCheckerImpl::PreCheckExtend(ASTContext& ctx, ExtendDecl& ed) { // Need put after TypeAlias circle check. if (ed.extendedType) { auto decl = Ty::GetDeclOfTy(ed.extendedType->ty); if (decl && decl->TestAttr(Attribute::C)) { diag.DiagnoseRefactor( DiagKindRefactor::sema_c_type_cannot_extend_interface, *ed.extendedType.get(), decl->identifier); } ed.ty = ed.extendedType->ty; } // Check implemented interfaces. for (auto it = ed.inheritedTypes.begin(); it != ed.inheritedTypes.end();) { if (!Ty::IsTyCorrect((*it)->ty) || (*it)->ty->kind != TypeKind::TYPE_INTERFACE) { diag.DiagnoseRefactor(DiagKindRefactor::sema_extend_not_interface, *(*it)); ctx.DeleteInvertedIndexes(it->get()); it->reset(); it = ed.inheritedTypes.erase(it); } else { ++it; } } SetExtendExternalAttr(ctx, ed); CheckExtendedTypeValidity(*ed.extendedType); } namespace { inline bool IsExtendedASTKind(const Type& ty) { return ty.astKind == ASTKind::REF_TYPE || ty.astKind == ASTKind::QUALIFIED_TYPE || ty.astKind == ASTKind::PRIMITIVE_TYPE || ty.astKind == ASTKind::OPTION_TYPE; } void UpdateExtendMap(TypeManager& typeManager, const std::unordered_set>& extends) { // Ensure two maps are cleared correctly: `builtinTyToExtendMap` and `declToExtendMap`. // For multi package compilation case: clear once for all the packages. // For single package or file compilation case: clear right before invocation of this function is OK. for (const auto& extendDecl : extends) { CJC_NULLPTR_CHECK(extendDecl); if (extendDecl->isInMacroCall || !extendDecl->extendedType || (!extendDecl->TestAttr(Attribute::IMPORTED) && extendDecl->TestAttr(Attribute::FROM_COMMON_PART) && extendDecl->TestAttr(Attribute::COMMON))) { // The extendDecl in macrocall is only for lsp, and does not need to be updated. continue; } auto extendTy = extendDecl->extendedType->ty; if (!Ty::IsTyCorrect(extendTy) || !extendTy->IsExtendable() || !IsExtendedASTKind(*extendDecl->extendedType)) { continue; } for (auto& interfaceType : extendDecl->inheritedTypes) { CJC_NULLPTR_CHECK(interfaceType); if (Ty::IsTyCorrect(interfaceType->ty) && interfaceType->ty->IsClassLike()) { StaticCast(*interfaceType->ty).directSubtypes.emplace(extendTy); } } // extendTy is a built-in type if (extendTy->IsBuiltin()) { extendTy = typeManager.GetTyForExtendMap(*extendTy); if (typeManager.builtinTyToExtendMap.find(extendTy) == typeManager.builtinTyToExtendMap.end()) { typeManager.builtinTyToExtendMap[extendTy] = {}; } typeManager.builtinTyToExtendMap[extendTy].emplace(extendDecl); continue; } // extendTy is not a built-in type auto decl = Ty::GetDeclPtrOfTy(extendTy); if (decl == nullptr) { continue; } if (typeManager.declToExtendMap.find(decl) == typeManager.declToExtendMap.end()) { typeManager.declToExtendMap[decl] = {}; } typeManager.declToExtendMap[decl].emplace(extendDecl); for (auto& interfaceType : extendDecl->inheritedTypes) { auto id = Ty::GetDeclPtrOfTy(interfaceType->ty); if (id == nullptr) { continue; } auto interface = RawStaticCast(id); interface->subDecls.emplace(decl); } } } } // namespace void TypeChecker::TypeCheckerImpl::BuildExtendMap(ASTContext& ctx) { std::unordered_set> allExtends; // Collect current package's source extends. auto syms = GetSymsByASTKind(ctx, ASTKind::EXTEND_DECL, Sort::posAsc); for (auto& sym : syms) { if (auto ed = AST::As(sym->node); ed) { allExtends.emplace(ed); PreCheckExtend(ctx, *ed); } } UpdateExtendMap(typeManager, allExtends); } void TypeChecker::TypeCheckerImpl::BuildImportedExtendMap() { std::unordered_set> allExtends; // Collect all imported extends. auto emplaceExtend = [&allExtends](const OwnedPtr& decl) { if (auto ed = DynamicCast(decl.get())) { allExtends.emplace(ed); } }; // Must collect all packages' extend, even invisible macro packages. auto pkgs = importManager.GetAllImportedPackages(true); for (auto& pkg : pkgs) { if (pkg->srcPackage->TestAttr(Attribute::IMPORTED)) { IterateToplevelDecls(*pkg->srcPackage, emplaceExtend); } } UpdateExtendMap(typeManager, allExtends); } void TypeChecker::TypeCheckerImpl::CheckExtendRules(const ASTContext& ctx) { if (ctx.curPackage->TestAttr(Attribute::IMPORTED)) { return; // OrphanRule and generic consistency check can be ignored for imported package. } std::vector syms = GetSymsByASTKind(ctx, ASTKind::EXTEND_DECL, Sort::posAsc); for (auto& sym : syms) { auto extendDecl = As(sym->node); bool invalid = extendDecl == nullptr || !extendDecl->extendedType || !Ty::IsTyCorrect(extendDecl->extendedType->ty); if (invalid) { continue; } CheckExtendOrphanRule(ctx, *extendDecl); CheckExtendGenerics(*extendDecl); } } void TypeChecker::TypeCheckerImpl::CheckImmutExtendInhertMutSuper(const Type& inheritedType, const ExtendDecl& ed) { if (!Ty::IsTyCorrect(ed.ty) || !ed.ty->IsImmutableType()) { return; } Ptr target = inheritedType.GetTarget(); if (!target || target->astKind != ASTKind::INTERFACE_DECL || target->TestAttr(Attribute::IN_REFERENCE_CYCLE)) { return; } auto id = StaticCast(target); for (auto& member : id->GetMemberDecls()) { if (member->astKind == ASTKind::PROP_DECL && member->TestAttr(Attribute::MUT)) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_interface_is_not_extendable, ed, id->identifier); builder.AddNote("interface '" + id->identifier + "' contains mutable member '" + member->identifier + "'"); return; } } for (auto& it : id->inheritedTypes) { CheckImmutExtendInhertMutSuper(*it, ed); } } void TypeChecker::TypeCheckerImpl::CheckExtendDecl(ASTContext& ctx, ExtendDecl& ed) { if (ed.TestAttr(Attribute::IS_CHECK_VISITED)) { return; } ed.EnableAttr(Attribute::IS_CHECK_VISITED); // Do type legality check for extend type. CJC_NULLPTR_CHECK(ed.extendedType); Synthesize(ctx, ed.extendedType.get()); Ptr extendedDecl = ed.extendedType->GetTarget(); // Check implemented interfaces. for (auto it = ed.inheritedTypes.begin(); it != ed.inheritedTypes.end();) { if (!Ty::IsTyCorrect(Synthesize(ctx, it->get()))) { ctx.DeleteInvertedIndexes(it->get()); it->reset(); it = ed.inheritedTypes.erase(it); } else { if (extendedDecl) { CheckSealedInheritance(*extendedDecl, **it); CheckThreadContextInheritance(*extendedDecl, **it); } CheckImmutExtendInhertMutSuper(**it, ed); ++it; } } for (auto& m : ed.members) { Synthesize(ctx, m.get()); if (ed.ty && ed.ty->IsImmutableType()) { if (auto fd = DynamicCast(m.get()); fd && !ed.ty->IsEnum() && IsIndexAssignmentOperator(*fd)) { auto fdIdRange = MakeRange(fd->identifier); diag.DiagnoseRefactor( DiagKindRefactor::sema_immutable_type_extend_assignment_index_operator, *fd, fdIdRange); } if (auto pd = DynamicCast(m.get()); pd && pd->isVar) { auto mutDecl = TypeCheckUtil::FindModifier(*pd, TokenKind::MUT); CJC_ASSERT(mutDecl); if (mutDecl) { (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_immutable_type_illegal_property, *mutDecl); } } } } if (auto inhertDecl = DynamicCast(extendedDecl)) { auto extends = typeManager.GetDeclExtends(*inhertDecl); std::set, CmpNodeByPos> ordered(extends.begin(), extends.end()); CheckExtendDupDefImplByDiffTypArgs(ordered, ed); } } void TypeChecker::TypeCheckerImpl::CheckExtendOrphanRule(const ASTContext& ctx, ExtendDecl& ed) { CJC_ASSERT(ed.extendedType && ed.extendedType->ty); // Collect interfaces which has already been extended in other packages. auto extendedTypeTarget = Ty::GetDeclPtrOfTy(ed.extendedType->ty); std::unordered_set> otherPackageExtendInterfaceTy{}; // 1. collect direct inherited interfaces of type decl. if (extendedTypeTarget && extendedTypeTarget->ty) { auto iTys = typeManager.GetAllSuperTys(*extendedTypeTarget->ty, {}, false); otherPackageExtendInterfaceTy.insert(iTys.begin(), iTys.end()); } // 2. collect direct and indirect extend decls. auto extends = extendedTypeTarget ? CollectAllRelatedExtends(typeManager, *extendedTypeTarget) : typeManager.GetAllExtendsByTy(*ed.extendedType->ty); for (const auto& extend : extends) { // Replace other extended interfaces with the current extended interface generics to ensure that the interface // generics are the same when ty is compared. If it cannot be replaced, an empty typeMapping is generated. TypeSubst typeMapping = InverseMapping(GenerateTypeMapping(ed, extend->ty->typeArgs)); if (extend->fullPackageName != ctx.fullPackageName) { for (const auto& inheritedType : extend->inheritedTypes) { if (!inheritedType->ty) { continue; } for (auto superTy : typeManager.GetAllSuperTys(*inheritedType->ty)) { otherPackageExtendInterfaceTy.insert(typeManager.GetInstantiatedTy(superTy, typeMapping)); } } } } // Check whether extended type is imported. bool isImportedExtendedType = (extendedTypeTarget && extendedTypeTarget->fullPackageName != ctx.fullPackageName) || ed.extendedType->ty->IsBuiltin(); // Check whether all extended interfaces are imported. std::set, CmpTyByName> externalDecls = {}; for (const auto& inheritedType : ed.inheritedTypes) { if (!inheritedType->ty) { continue; } auto allSuperTys = typeManager.GetAllSuperTys(*inheritedType->ty); for (auto& ty : allSuperTys) { auto decl = Ty::GetDeclPtrOfTy(ty); if (decl && decl->fullPackageName != ctx.fullPackageName && otherPackageExtendInterfaceTy.count(ty) == 0) { externalDecls.insert(ty); } } } // Report errors. if (isImportedExtendedType && !externalDecls.empty()) { std::string extendedType = extendedTypeTarget ? extendedTypeTarget->identifier.Val() : ed.extendedType->ty->String(); DiagnosticBuilder diagnose = diag.DiagnoseRefactor(DiagKindRefactor::sema_type_cannot_extend_imported_interface, *ed.extendedType, extendedTypeTarget ? "imported" : "primitive", extendedType); diagnose.AddNote("used external interface: " + Ty::GetTypesToStr(externalDecls, " ")); } } void TypeChecker::TypeCheckerImpl::SetExtendExternalAttr(const ASTContext& ctx, ExtendDecl& ed) { // Check import and export. Ptr extendedTypeTarget = Ty::GetDeclPtrOfTy(ed.extendedType->ty); if (!extendedTypeTarget) { extendedTypeTarget = TypeCheckUtil::GetRealTarget(ed.extendedType->GetTarget()); } if ((extendedTypeTarget == nullptr || extendedTypeTarget->TestAttr(Attribute::IMPORTED)) && ed.inheritedTypes.empty()) { // direct extend of built-in type or imported type is visible in defined package but not exported. // So do not add attribute for this kind of extend. return; } // If extension and extended type declare in same package, extension's external attr decided on both extended type // and generic upperbound type. // If not, extension's external attr decided on both of inhert interface and generic upperbound type. // NOTE: 'public' was added for decl with 'sealed' in 'DeclAttributeChecker'. auto accessLevel = extendedTypeTarget == nullptr ? AccessLevel::PUBLIC : GetAccessLevel(*extendedTypeTarget); if (ed.generic) { for (auto& tp : ed.generic->genericConstraints) { for (auto& up : tp->upperBounds) { Ptr decl = up->GetTarget(); if (decl == nullptr) { continue; } auto upperboundAccessLevel = GetAccessLevel(*decl); if (upperboundAccessLevel < accessLevel) { accessLevel = upperboundAccessLevel; } } } } if ((extendedTypeTarget != nullptr && extendedTypeTarget->fullPackageName != ctx.fullPackageName) || (extendedTypeTarget == nullptr && "std.core" != ctx.fullPackageName)) { auto interfaceAccessLevelFinal = AccessLevel::PRIVATE; // Different package. Extension visible as same as max of all inherited type. for (auto& interface : ed.inheritedTypes) { Ptr decl = interface->GetTarget(); if (decl == nullptr) { return; } auto interfaceAccessLevel = GetAccessLevel(*decl); if (interfaceAccessLevelFinal < interfaceAccessLevel) { interfaceAccessLevelFinal = interfaceAccessLevel; } } accessLevel = accessLevel < interfaceAccessLevelFinal ? accessLevel : interfaceAccessLevelFinal; } ed.EnableAttr(GetAttrByAccessLevel(accessLevel)); } void TypeChecker::TypeCheckerImpl::CheckExtendField(const ASTContext& ctx, MemberAccess& ma) { ma.ty = TypeManager::GetInvalidTy(); // Ty will be set to valid if non-error happens. CJC_NULLPTR_CHECK(ma.curFile); std::vector> targets = ExtendFieldLookup(ctx, *ma.curFile, ma.baseExpr->ty, ma.field); if (!FilterAndCheckTargetsOfNameAccess(ctx, ma, targets)) { return; } auto target = GetAccessibleDecl(ctx, ma, targets); ReplaceTarget(&ma, target ? target : targets[0]); AddFuncTargetsForMemberAccess(ma, targets); } cangjie_compiler-1.0.7/src/Sema/TypeCheckForLSP.cpp000066400000000000000000000707701510705540100221070ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the TypeChecker related api for lsp. */ #include "TypeCheckerImpl.h" #include #include "DiagSuppressor.h" #include "JoinAndMeet.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace AST; namespace Cangjie { template struct RetValue { using Type = T; T value; template explicit RetValue(Args&&... args) : value(std::forward(args)...) { } }; /** * A base class to restrict the valid type of nodes in expression chain which supporting dot completion of LSP. * Using all pure virtual function for AST nodes to restrict sub-class defining all related behavior. */ template class RefNodeWalker { public: typename RetT::Type operator()() { auto ret = VisitNode(expr); return ret.value; } virtual ~RefNodeWalker() = default; protected: explicit RefNodeWalker(Expr& expr) : expr(&expr) {}; virtual RetT Visit(RefExpr& re) = 0; virtual RetT Visit(MemberAccess& ma) = 0; virtual RetT Visit(PrimitiveTypeExpr& pte) = 0; virtual RetT Visit(LitConstExpr& lce) = 0; virtual RetT Visit(CallExpr& ce) = 0; virtual RetT Visit(SubscriptExpr& se) = 0; virtual RetT Visit(OptionalExpr& oe) = 0; virtual RetT Visit(TrailingClosureExpr& tce) = 0; virtual RetT Visit(ArrayLit& al) = 0; RetT Visit(const OptionalChainExpr& oce) { // 'OptionalChainExpr' is only a wrapper node, should not be implemented by sub-class. // NOTE: 'Synthesizer' is used for LSP dot completion, so for code like 'a?.' or 'a?b.' // the wrapped 'OptionalChainExpr' will not be counted as part of base expression. // This type of expression is actually should not existing. return VisitNode(oce.expr.get()); } RetT VisitNode(Ptr node) { ASTKind kind = node ? node->astKind : ASTKind::INVALID_EXPR; switch (kind) { case ASTKind::REF_EXPR: return Visit(*StaticAs(node)); case ASTKind::MEMBER_ACCESS: return Visit(*StaticAs(node)); case ASTKind::PRIMITIVE_TYPE_EXPR: return Visit(*StaticAs(node)); case ASTKind::LIT_CONST_EXPR: return Visit(*StaticAs(node)); case ASTKind::CALL_EXPR: return Visit(*StaticAs(node)); case ASTKind::SUBSCRIPT_EXPR: return Visit(*StaticAs(node)); case ASTKind::OPTIONAL_CHAIN_EXPR: return Visit(*StaticAs(node)); case ASTKind::OPTIONAL_EXPR: return Visit(*StaticAs(node)); case ASTKind::TRAIL_CLOSURE_EXPR: return Visit(*StaticAs(node)); case ASTKind::ARRAY_LIT: return Visit(*StaticAs(node)); default: return RetT(); } } Ptr const expr; }; /** * Get the base reference name of given expression. * eg: 'a.b.c' -- return 'a' * 'a().b.c' or 'a[0].b.c' -- return 'a' * NOTE: if the base expression is not name reference, return empty string. * eg: if (true) {1} else {0}.toString() -- return empty string. */ using BaseName = RetValue; class BaseNameFinder : public RefNodeWalker { public: explicit BaseNameFinder(Expr& expr) : RefNodeWalker(expr) { } ~BaseNameFinder() override = default; protected: BaseName Visit(RefExpr& re) override { return BaseName(re.ref.identifier); } BaseName Visit(MemberAccess& ma) override { return VisitNode(ma.baseExpr.get()); } BaseName Visit(PrimitiveTypeExpr& pte) override { // Just for placeholder, indicate current expr is also an alternative type of name reference. (void)pte; return BaseName("$type"); } BaseName Visit(LitConstExpr& lce) override { // Consistent with 'CollectLitConstExpr'. if (lce.kind == LitConstKind::STRING) { return BaseName("String"); } else if (lce.kind == LitConstKind::JSTRING) { return BaseName("JString"); } // Can't be empty. return BaseName(lce.stringValue); } BaseName Visit(CallExpr& ce) override { return VisitNode(ce.baseFunc.get()); } BaseName Visit(SubscriptExpr& se) override { return VisitNode(se.baseExpr.get()); } BaseName Visit(OptionalExpr& oe) override { return VisitNode(oe.baseExpr.get()); } BaseName Visit(TrailingClosureExpr& tce) override { return VisitNode(tce.expr.get()); } BaseName Visit(ArrayLit& al) override { std::stringstream arrayStr; arrayStr << "["; for (auto& child : al.children) { arrayStr << VisitNode(child.get()).value; if (child != al.children.back()) { arrayStr << ", "; } } arrayStr << "]"; return BaseName(arrayStr.str()); } }; /** * Set the scopeName for expressions in whitelist. */ using EmptyRet = RetValue; class ScopeNameSetter : public RefNodeWalker { public: ScopeNameSetter(Expr& expr, const std::string& scopeName) : RefNodeWalker(expr), scopeName(scopeName) { } ~ScopeNameSetter() override = default; protected: EmptyRet Visit(RefExpr& re) override { re.scopeName = scopeName; for (auto& type : re.typeArguments) { Walker(type.get(), [this](auto node) { node->scopeName = scopeName; return VisitAction::WALK_CHILDREN; }).Walk(); } return EmptyRet(); } EmptyRet Visit(MemberAccess& ma) override { ma.scopeName = scopeName; for (auto& type : ma.typeArguments) { Walker(type.get(), [this](auto node) { node->scopeName = scopeName; return VisitAction::WALK_CHILDREN; }).Walk(); } return VisitNode(ma.baseExpr.get()); } EmptyRet Visit(PrimitiveTypeExpr& pte) override { // ScopeName can be omitted for 'PrimitiveTypeExpr'. (void)pte; return EmptyRet(); } EmptyRet Visit(LitConstExpr& lce) override { // ScopeName can be omitted for 'LitConstExpr'. (void)lce; return EmptyRet(); } EmptyRet Visit(CallExpr& ce) override { ce.scopeName = scopeName; return VisitNode(ce.baseFunc.get()); } EmptyRet Visit(SubscriptExpr& se) override { se.scopeName = scopeName; return VisitNode(se.baseExpr.get()); } EmptyRet Visit(OptionalExpr& oe) override { // ScopeName can be omitted for 'OptionalExpr'. return VisitNode(oe.baseExpr.get()); } EmptyRet Visit(TrailingClosureExpr& tce) override { // ScopeName can be omitted for 'TrailingClosureExpr'. return VisitNode(tce.expr.get()); } EmptyRet Visit(ArrayLit& al) override { al.scopeName = scopeName; for (auto& child : al.children) { VisitNode(child.get()); } return EmptyRet(); } private: std::string scopeName; }; /** * Get the base reference name of given expression. * eg: 'a.b.c' -- return 'a' * 'a().b.c' or 'a[0].b.c' -- return 'a' * NOTE: if the base expression is not name reference, return empty string. * eg: if (true) {1} else {0}.toString() -- return empty string. * This should be a friend class of TypeChecker. */ using SynResult = RetValue; class TypeChecker::Synthesizer : public RefNodeWalker { public: Synthesizer(TypeChecker::TypeCheckerImpl& typeChecker, ASTContext& ctx, Expr& expr, const Position& pos) : RefNodeWalker(expr), checker(typeChecker), ctx(ctx), pos(pos) { } ~Synthesizer() override = default; protected: SynResult Visit(RefExpr& re) override { re.begin = pos; // Update position for declaration founding. // Reference checking requires the validation of 'typeArguments'. auto instTys = checker.GetTyFromASTType(ctx, re.typeArguments); if (ctxExprs.empty()) { checker.InferRefExpr(ctx, re); if (Ty::IsTyCorrect(re.ty) && (re.isThis || re.isSuper)) { std::unordered_set> tys{re.ty}; return SynResult(tys); } } else { auto parentExpr = ctxExprs.top(); re.isAlone = false; re.callOrPattern = DynamicCast(parentExpr); if (parentExpr->IsReferenceExpr()) { return SynResult(re.ref.targets); } checker.InferRefExpr(ctx, re); if (!instTys.empty() && !re.ref.targets.empty()) { std::unordered_set> tys; for (auto decl : re.ref.targets) { auto typeMapping = TypeCheckUtil::GenerateTypeMapping(*decl, instTys); tys.emplace(checker.typeManager.GetInstantiatedTy(decl->ty, typeMapping)); } // Update current decl status for parent expr when return type result. wasTypeCall = re.callOrPattern && re.ref.target && re.ref.target->IsTypeDecl(); return SynResult(tys); } } return SynResult(re.ref.targets); } // Convert decls to tys with type instantiation. std::unordered_set> GetCandidateTysForMemberAccess( Ty& baseTy, const std::vector>& typeArgs, const std::vector>& candidates) { MultiTypeSubst mts; checker.typeManager.GenerateGenericMapping(mts, baseTy); auto baseMapping = TypeCheckUtil::MultiTypeSubstToTypeSubst(mts); std::unordered_set> tys; for (auto decl : candidates) { auto typeMapping = TypeCheckUtil::GenerateTypeMapping(*decl, typeArgs); typeMapping.insert(baseMapping.begin(), baseMapping.end()); auto instTy = checker.typeManager.GetInstantiatedTy(decl->ty, typeMapping); if (auto fTy = DynamicCast(instTy); fTy && Is(fTy->retTy)) { instTy = checker.typeManager.GetFunctionTy(fTy->paramTys, &baseTy); } tys.emplace(instTy); } return tys; } Candidate GetCandidateMembers(const std::unordered_set>& tys, const std::string& field, Ptr curFile) { std::unordered_set> resultTys; auto ma = CreateMemberAccess(CreateRefExpr("$dummy"), field); // Avoid diagnostic engine to throw internal error. ma->begin = DEFAULT_POSITION; ma->end = DEFAULT_POSITION; AddCurFile(*ma, curFile); for (auto ty : tys) { std::vector> curTargets; ma->baseExpr->ty = ty; ma->targets.clear(); // Set dummy target type to skip inference checking. ctx.targetTypeMap[ma.get()] = ty; auto target = checker.GetObjMemberAccessTarget(ctx, *ma, *ty); target && ma->targets.empty() ? curTargets.insert(curTargets.end(), target) : curTargets.insert(curTargets.end(), ma->targets.begin(), ma->targets.end()); // Convert decls to tys for further non-reference synthesizing. auto foundTys = GetCandidateTysForMemberAccess(*ty, {}, curTargets); resultTys.merge(foundTys); } return Candidate(resultTys); } SynResult Visit(MemberAccess& ma) override { Ptr parentExpr = nullptr; if (!ctxExprs.empty()) { parentExpr = ctxExprs.top(); ma.isAlone = false; ma.callOrPattern = DynamicCast(parentExpr); } SynCtx synCtx(this, &ma); auto base = VisitNode(ma.baseExpr.get()); // Set type of 'typeArguments' if existed. std::vector> instTys = checker.GetTyFromASTType(ctx, ma.typeArguments); // If baseExpr is combined with pure references, decide whether syn current ma with parentExpr state. // a.b or a().b if (wasPureReference) { std::vector> candidates; // If parent expression is not reference expression, synthesize current expression's target. if (parentExpr == nullptr || !parentExpr->IsReferenceExpr()) { checker.InferMemberAccess(ctx, ma); } (ma.target && ma.targets.empty()) ? candidates.insert(candidates.end(), ma.target) : candidates.insert(candidates.end(), ma.targets.begin(), ma.targets.end()); CJC_NULLPTR_CHECK(ma.baseExpr); // If the 'parentExpr' existed and the 'candidates' is not empty, convert result to tys. if (parentExpr != nullptr && !candidates.empty() && ma.baseExpr->ty) { // Update current decl status for parent expr when return type result. wasTypeCall = ma.callOrPattern && ma.target && ma.target->IsTypeDecl(); auto tys = GetCandidateTysForMemberAccess(*ma.baseExpr->ty, instTys, candidates); return SynResult(tys); } return SynResult(candidates); } std::unordered_set> tys; if (base.value.hasDecl) { // Field accessing like 'call().a.b' for (auto it : base.value.decls) { if (Ty::IsTyCorrect(it->ty)) { tys.emplace(it->ty); } } } else { // Field accessing like 'call().a().b' tys = base.value.tys; } auto result = GetCandidateMembers(tys, ma.field, ma.curFile); return SynResult(result); } SynResult Visit(PrimitiveTypeExpr& pte) override { // Another kind of reference, only valid for base of MemberAccess. // If ctx is empty, return primitive type, otherwise nothing to do here. std::unordered_set> tys; if (ctxExprs.empty()) { tys.emplace(TypeManager::GetPrimitiveTy(pte.typeKind)); } return SynResult(tys); } SynResult Visit(LitConstExpr& lce) override { Ptr ty = TypeManager::GetInvalidTy(); if (lce.siExpr || lce.kind == LitConstKind::STRING) { auto stringDecl = checker.importManager.GetCoreDecl(STD_LIB_STRING); if (stringDecl) { ty = stringDecl->ty; } } else { ty = checker.SynLitConstExpr(ctx, lce); } std::unordered_set> tys; if (Ty::IsTyCorrect(ty)) { tys.emplace(ty); } return SynResult(tys); } std::unordered_set> GetCandidateCallResults(const std::unordered_set>& tys, const std::string& field, Ptr curFile, bool isInTrailingClosure = false) { std::unordered_set> results; auto candidates = GetCandidateMembers(tys, field, curFile); CJC_ASSERT(!candidates.hasDecl); for (auto ty : candidates.tys) { if (auto fTy = DynamicCast(ty); fTy && (!isInTrailingClosure || IsViableTypeForTrailingClosure(*fTy))) { results.emplace(fTy->retTy); } } return results; } SynResult Visit(CallExpr& ce) override { bool isInTrailingClosure = !ctxExprs.empty() && ctxExprs.top()->astKind == ASTKind::TRAIL_CLOSURE_EXPR; std::unordered_set> tys; SynCtx synCtx(this, &ce); auto base = VisitNode(ce.baseFunc.get()); // NOTE: will not filter candidate by arguments, since the user may not given correct arguments. if (base.value.hasDecl) { std::unordered_set> varTys; for (auto decl : base.value.decls) { if (!decl || !Ty::IsTyCorrect(decl->ty)) { continue; } if (decl->IsTypeDecl()) { // Handle case of constructor call. tys.emplace(decl->ty); } else if (auto fTy = DynamicCast(decl->ty)) { if (!isInTrailingClosure || IsViableDeclForTrailingClosure(*decl, *fTy)) { tys.emplace(fTy->retTy); } } else { varTys.emplace(decl->ty); } } // Handle case of operator '()' overloading. tys.merge(GetCandidateCallResults(varTys, "()", ce.curFile, isInTrailingClosure)); } else { // For 'this()', 'super()'. if (ce.baseFunc && IsThisOrSuper(*ce.baseFunc)) { return SynResult(base.value.tys); } // eg: funcCall()(), calling returned value of another function call. std::unordered_set> varTys; for (auto ty : base.value.tys) { if (wasTypeCall) { // Handle case of constructor call. tys.emplace(ty); wasTypeCall = false; } else if (auto fTy = DynamicCast(ty)) { if (!isInTrailingClosure || IsViableTypeForTrailingClosure(*fTy)) { tys.emplace(fTy->retTy); } } else { varTys.emplace(ty); } } // Handle case of operator '()' overloading. tys.merge(GetCandidateCallResults(varTys, "()", ce.curFile, isInTrailingClosure)); } return SynResult(tys); } std::unordered_set> GetTupleAccessTys(const std::unordered_set>& tupleTys, Expr& index) { // Tuple can only accessed by numeric literal. std::unordered_set> tys; if (auto lce = DynamicCast(&index); lce && lce->kind == LitConstKind::INTEGER) { auto indexTy = checker.SynLitConstExpr(ctx, *lce); if (!indexTy->IsInteger() || lce->constNumValue.asInt.IsOutOfRange() || lce->constNumValue.asInt.IsNegativeNum()) { return tys; } size_t idx = lce->constNumValue.asInt.Uint64(); for (auto ty : tupleTys) { if (idx < ty->typeArgs.size()) { tys.emplace(ty->typeArgs[idx]); } } } return tys; } SynResult Visit(SubscriptExpr& se) override { SynCtx synCtx(this, &se); auto base = VisitNode(se.baseExpr.get()); std::unordered_set> baseTys; std::unordered_set> tupleTys; if (base.value.hasDecl) { // eg: a[0] -- only can be tuple or user defined type vardecl. for (auto decl : base.value.decls) { if (decl && Ty::IsTyCorrect(decl->ty)) { decl->ty->IsTuple() ? tupleTys.emplace(decl->ty) : baseTys.emplace(decl->ty); } } } else { // eg: a[0][1] -- tuple or user defined type. for (auto ty : base.value.tys) { ty->IsTuple() ? tupleTys.emplace(ty) : baseTys.emplace(ty); } } auto tys = GetCandidateCallResults(baseTys, "[]", se.curFile); if (!se.indexExprs.empty() && se.indexExprs[0] != nullptr) { tys.merge(GetTupleAccessTys(tupleTys, *se.indexExprs[0])); } return SynResult(tys); } SynResult Visit(OptionalExpr& oe) override { std::unordered_set> tys; SynCtx synCtx(this, &oe); auto base = VisitNode(oe.baseExpr.get()); if (base.value.hasDecl) { for (auto decl : base.value.decls) { if (decl && Ty::IsTyCorrect(decl->ty)) { tys.emplace(decl->ty); } } } else { tys = base.value.tys; } // Unpack option type. std::unordered_set> results; for (auto ty : tys) { if (ty->IsCoreOptionType()) { results.emplace(ty->typeArgs[0]); } } return SynResult(results); } static bool IsViableDeclForTrailingClosure(const Decl& decl, const FuncTy& funcTy) { if (auto fd = DynamicCast(&decl)) { CJC_ASSERT(fd->funcBody && !fd->funcBody->paramLists.empty()); Ptr lastTrivialParam = nullptr; // Only function whose last unnamed parameter has a function type can be used for trailing closure. for (auto& param : fd->funcBody->paramLists[0]->params) { if (param->isNamedParam) { break; } lastTrivialParam = param.get(); CJC_NULLPTR_CHECK(lastTrivialParam->ty); } return lastTrivialParam && lastTrivialParam->ty->IsFunc(); } return IsViableTypeForTrailingClosure(funcTy); } static bool IsViableTypeForTrailingClosure(const FuncTy& funcTy) { // To be used in trailing closure, there must exist a function type of paramTy. // NOTE: Fuzzy checking, do not restrict the funcTy be last param type. return !funcTy.paramTys.empty() && Utils::In(funcTy.paramTys, [](auto& it) { return it->IsFunc(); }); } SynResult Visit(TrailingClosureExpr& tce) override { SynCtx synCtx(this, &tce); CJC_NULLPTR_CHECK(tce.expr); if (tce.expr->astKind != ASTKind::CALL_EXPR) { auto tmpCall = CreateCallExpr(std::move(tce.expr), {}); return VisitNode(tmpCall.get()); } return VisitNode(tce.expr.get()); } SynResult Visit(ArrayLit& al) override { std::unordered_set> tys; std::set> arrayElemTys; for (auto& child : al.children) { auto childResult = VisitNode(child.get()).value; Ptr childTy = TypeManager::GetInvalidTy(); if (childResult.hasDecl || !childResult.tys.empty()) { childTy = childResult.hasDecl ? childResult.decls[0]->ty : *childResult.tys.begin(); } if (!Ty::IsTyCorrect(childTy)) { tys.emplace(TypeManager::GetInvalidTy()); return SynResult(tys); } arrayElemTys.insert(childTy); } auto arrayStruct = checker.importManager.GetCoreDecl("Array"); if (arrayElemTys.empty() || arrayStruct == nullptr) { tys.emplace(TypeManager::GetInvalidTy()); return SynResult(tys); } auto joinRes = JoinAndMeet(checker.typeManager, arrayElemTys, {}, &checker.importManager, al.curFile).JoinAsVisibleTy(); if (auto ty = std::get_if>(&joinRes)) { al.ty = checker.typeManager.GetStructTy(*arrayStruct, {*ty}); } else { tys.emplace(TypeManager::GetInvalidTy()); return SynResult(tys); } tys.emplace(al.ty); return SynResult(tys); } class SynCtx { public: SynCtx(Synthesizer* syn, Ptr current) : synthesizer(syn) { synthesizer->ctxExprs.push(current); } ~SynCtx() noexcept { #ifndef CANGJIE_ENABLE_GCOV try { #endif if (synthesizer->wasPureReference) { synthesizer->wasPureReference = synthesizer->ctxExprs.top()->IsReferenceExpr(); } synthesizer->ctxExprs.pop(); synthesizer = nullptr; #ifndef CANGJIE_ENABLE_GCOV } catch (...) { // Avoid using exceptions in destructors. } #endif } private: Synthesizer* synthesizer; }; private: TypeChecker::TypeCheckerImpl& checker; ASTContext& ctx; Position pos; std::stack> ctxExprs; // Checking is performed from inner to outer, this status is also updated from child to parent. // Whether the previous children is in form "a" or "a.b" "a.b.c". bool wasPureReference = true; // Whether the previous checking base expression is a type identifier. bool wasTypeCall = false; }; } // namespace Cangjie namespace { /** * Get the relative postion of reference name @p baseName with given @p scopeName and the status indicating * whether the searching reference is existing as local declaration. * @return the relative postion. */ Position GetRelativePos( const ASTContext& ctx, const std::string& scopeName, const std::string& baseName, bool hasLocalDecl) { // Default position is at the line with the largest line number, which can accessing all of the global variables. Position pos = {0, std::numeric_limits::max(), 0}; if (hasLocalDecl) { // Search for "scope_name == 'scopeName' && name == 'reference' && // (ast_kind == 'var_decl' || ast_kind == 'func_decl')" // when 'hasLocalDecl' is true. Query q(Operator::AND); q.left = std::make_unique("scope_name", scopeName); q.right = std::make_unique(Operator::AND); q.right->left = std::make_unique("name", baseName); q.right->right = std::make_unique(Operator::OR); q.right->right->left = std::make_unique("ast_kind", "var_decl"); q.right->right->right = std::make_unique("ast_kind", "func_decl"); auto syms = ctx.searcher->Search(ctx, &q); if (!syms.empty()) { CJC_NULLPTR_CHECK(syms[0]->node); // Set searching position just behind local variable. pos = syms[0]->node->end + Position{0, 0, 1}; } } else { // If the scope gate can be found, set position just behind scope gate's begin position. auto sym = ScopeManagerApi::GetScopeGate(ctx, ScopeManagerApi::GetScopeGateName(scopeName)); if (sym && sym->node) { pos = sym->node->begin + Position{0, 0, 1}; } } return pos; } } // namespace /** * Verifying the 'expr' is only combined with named reference / call / subscript accessing. * If 'hasLocalDecl' is true, found local declaration of base name. * Searching for each expression hierarchy result and filter candidates by accessed member name. * * From inner most to outer, if previous level is name reference, do synthesizing * else filter by calling argument or subscript index, check whether they contain any expression * which is needing desugar before typechecking. * After filtration, if 'candidates' is empty, failed to found result. Otherwise continue searching. * * The return value will contains all possible candidate decls or types. */ Candidate TypeChecker::SynReferenceSeparately( ASTContext& ctx, const std::string& scopeName, Expr& expr, bool hasLocalDecl) const { return impl->SynReferenceSeparately(ctx, scopeName, expr, hasLocalDecl); } void TypeChecker::RemoveTargetNotMeetExtendConstraint(const Ptr baseTy, std::vector>& targets) { return impl->RemoveTargetNotMeetExtendConstraint(baseTy, targets); } Candidate TypeChecker::TypeCheckerImpl::SynReferenceSeparately( ASTContext& ctx, const std::string& scopeName, Expr& expr, bool hasLocalDecl) { if (ctx.curPackage->files.empty()) { return {}; } std::string baseName = BaseNameFinder(expr)(); if (baseName.empty()) { return {}; // If failed to get 'baseName', directly return -- no result found. } // Clone the expr using for 'Synthesizer', avoid direct modified the parameter. auto cloned = ASTCloner::Clone(&expr); // Ensure the expression has 'scopeName'. auto scope = scopeName.empty() ? TOPLEVEL_SCOPE_NAME : scopeName; ScopeNameSetter(*cloned, scope)(); auto ds = DiagSuppressor(diag); Position pos = GetRelativePos(ctx, scope, baseName, hasLocalDecl); // Previous checking guarantees the expresion only combined with name reference accessing/calling/subscripting. auto results = Synthesizer(*this, ctx, *cloned, pos)(); return results; } cangjie_compiler-1.0.7/src/Sema/TypeCheckGeneric.cpp000066400000000000000000000351321510705540100223470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements typecheck apis for Generic. */ #include "TypeCheckUtil.h" #include "TypeCheckerImpl.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace TypeCheckUtil; using namespace AST; void TypeChecker::TypeCheckerImpl::CheckUpperBound(ASTContext& ctx, const GenericConstraint& genericConstraint) { // The generic constraint is as: type <: upperBound. // The upperBound must be class like decl or primitive type. CheckReferenceTypeLegality(ctx, *genericConstraint.type); for (auto& upperBoundPtr : genericConstraint.upperBounds) { auto upperBound = upperBoundPtr.get(); if (upperBound == nullptr) { continue; } CheckReferenceTypeLegality(ctx, *upperBound); } } void TypeChecker::TypeCheckerImpl::CheckGenericConstraints(ASTContext& ctx, const Generic& generic) { if (generic.genericConstraints.empty()) { return; } for (auto& genericConstraint : generic.genericConstraints) { CheckUpperBound(ctx, *genericConstraint); // Check if left value of generic constraints is in the typeParameters. bool found = false; if (genericConstraint->type == nullptr) { continue; } for (auto& typeParameter : generic.typeParameters) { if (genericConstraint->type->ref.identifier.Val() == typeParameter->identifier.Val()) { found = true; break; } } if (!found) { diag.Diagnose(*genericConstraint->type, DiagKind::sema_generics_type_variable_not_defined, genericConstraint->type->ref.identifier.Val().c_str()); } } } bool TypeChecker::TypeCheckerImpl::HasIncompleteStaticFuncOrProp( const ASTContext& ctx, InheritableDecl& cd, std::vector>& staticMemberFuncsOrProps) { if (!cd.TestAttr(Attribute::ABSTRACT) && cd.astKind != ASTKind::INTERFACE_DECL) { return false; } for (auto& decl : staticMemberFuncsOrProps) { CJC_NULLPTR_CHECK(decl); auto candidates = FieldLookup(ctx, &cd, decl->identifier, {.file = cd.curFile}); auto found = std::find_if(candidates.begin(), candidates.end(), [this, decl](auto& candidate) -> bool { bool isImpl = false; if (auto srcFunc = DynamicCast(candidate); srcFunc && decl->IsFunc()) { auto fd = RawStaticCast(decl); isImpl = IsOverrideOrShadow(typeManager, *srcFunc, *fd) && !srcFunc->TestAttr(Attribute::ABSTRACT); } else if (auto srcProp = DynamicCast(candidate); srcProp && decl->astKind == ASTKind::PROP_DECL) { auto pd = RawStaticCast(decl); isImpl = IsOverrideOrShadow(typeManager, *srcProp, *pd) && !srcProp->TestAttr(Attribute::ABSTRACT); } return isImpl; }); if (found == candidates.end()) { return true; } } return false; } namespace { void CollectStaticMember(const InheritableDecl& id, std::vector>& ret) { for (auto& it : id.GetMemberDecls()) { if (it->IsFuncOrProp() && it->TestAttr(Attribute::STATIC)) { ret.push_back(it.get()); } } // Look up parent interface. for (auto& inheritedType : id.inheritedTypes) { auto inherDecl = Ty::GetDeclPtrOfTy(inheritedType->ty); if (auto interfaceDecl = DynamicCast(inherDecl); interfaceDecl) { CollectStaticMember(*interfaceDecl, ret); } } } inline std::unordered_map, size_t> GetTyArgsIndexMap(const std::vector>& tyArgs) { std::unordered_map, size_t> indexMap; for (size_t i = 0; i < tyArgs.size(); ++i) { indexMap[tyArgs[i]] = i; } return indexMap; } } // namespace bool TypeChecker::TypeCheckerImpl::CheckInstTyWithUpperbound( const ASTContext& ctx, TypeSubst& typeMapping, const NameReferenceExpr& expr) { if (typeMapping.empty()) { return true; // Errors must be reported before. } std::unordered_map, size_t> indexMap = GetTyArgsIndexMap(expr.instTys); auto typeArgs = expr.GetTypeArgs(); // If generic parameter is instantiated by a type that has unimplemented static function, including static funcion // in interface and abstract class. for (auto& it : typeMapping) { auto gTy = RawStaticCast(it.first); bool isInstSatisfyConstraints = Utils::All(gTy->upperBounds, [this, &it, &typeMapping](auto& ub) { auto ubInst = typeManager.GetInstantiatedTy(ub, typeMapping); return typeManager.IsSubtype(it.second, ubInst); }); if (!isInstSatisfyConstraints) { return true; } std::vector> staticMemberFuncsOrProps; // Collect all static functions in interface declarations. for (auto& upper : gTy->upperBounds) { if (auto decl = DynamicCast(Ty::GetDeclPtrOfTy(upper))) { CollectStaticMember(*decl, staticMemberFuncsOrProps); } } // If there is no static member, no further check is required. if (staticMemberFuncsOrProps.empty()) { continue; } // If the generic argument is instantiated as an interface and the upper bound of the generic constraint is an // interface that contains static members, an error is reported directly. // Need to be modified after the default implementation of static functions in the interface is supported. auto iTy = DynamicCast(it.second); auto isInstByInterface = iTy != nullptr; if (isInstByInterface) { std::vector> staticMembers = {}; CollectStaticMember(*iTy->decl, staticMembers); isInstByInterface = HasIncompleteStaticFuncOrProp(ctx, *iTy->decl, staticMembers); } auto isInstByNothing = it.second->IsNothing(); // The Nothing type does not have any members. if (isInstByInterface || isInstByNothing) { std::string typeString = isInstByNothing ? "'Nothing'" : "interface or abstract class '" + it.second->String() + "'"; auto builder = diag.Diagnose(typeArgs.empty() ? StaticCast(expr) : *typeArgs[indexMap[it.second]], DiagKind::sema_cannot_instantiated_by_incomplete_type, gTy->String(), typeString); if (isInstByNothing) { builder.AddNote("'Nothing' type has no members"); } return false; } } return true; } // This checking method is performed after sema type completed. bool TypeChecker::TypeCheckerImpl::CheckInstTypeCompleteness(const ASTContext& ctx, const NameReferenceExpr& expr) { auto target = TypeCheckUtil::GetRealTarget(expr.GetTarget()); auto genericDecl = target ? (target->TestAttr(Attribute::CONSTRUCTOR) ? target->outerDecl : target) : nullptr; if (!genericDecl) { return true; // Errors must be reported before. } TypeSubst typeMapping = GenerateTypeMapping(*genericDecl, expr.instTys); if (!CheckInstTyWithUpperbound(ctx, typeMapping, expr)) { return false; } auto extends = typeManager.GetAllExtendsByTy(*genericDecl->ty); for (auto extend : extends) { TypeSubst extendMapping = GenerateTypeMapping(*extend, expr.instTys); if (!CheckInstTyWithUpperbound(ctx, extendMapping, expr)) { return false; } } return true; } bool TypeChecker::TypeCheckerImpl::CheckCallGenericDeclInstantiation( Ptr d, const std::vector>& typeArgs, const Expr& checkNode) { if (!d) { return false; } std::vector> typeArgTys; Position diagPos; // RefExpr, MemberAccess 's typeArgTys maybe synthesized by type infer, which are saved in instTys of Expr Node. if (!typeArgs.empty()) { diagPos = (*typeArgs.begin())->begin; } else { diagPos = checkNode.begin; } typeArgTys = TypeCheckUtil::GetInstanationTys(checkNode); auto genericDecl = d->GetGeneric(); if (!genericDecl || genericDecl->typeParameters.size() != typeArgTys.size()) { diag.DiagnoseRefactor(DiagKindRefactor::sema_generic_argument_no_match, checkNode, diagPos); return false; } return true; } bool TypeChecker::TypeCheckerImpl::CheckGenericDeclInstantiation(Ptr d, const std::variant>, std::vector>>& arguments, const Node& checkNode) { size_t index = arguments.index(); if (!d || !Ty::IsTyCorrect(d->ty) || index == std::variant_npos) { return false; } std::vector> typeArgs; std::vector> typeNodes; bool isTypeNode = index == 0; if (isTypeNode) { typeNodes = std::get<0>(arguments); std::for_each(typeNodes.begin(), typeNodes.end(), [&typeArgs](auto it) { typeArgs.emplace_back(it->ty); }); } else { typeArgs = std::get<1>(arguments); } auto genericParams = GetDeclTypeParams(*d); bool invalid = typeArgs.empty() || genericParams.size() != typeArgs.size(); if (invalid) { auto range = MakeRange(checkNode.begin, checkNode.end.IsZero() ? checkNode.begin + 1 : checkNode.end); diag.DiagnoseRefactor(DiagKindRefactor::sema_generic_argument_no_match, checkNode, range); return false; } TypeSubst instantiateMap; if (auto ma = DynamicCast(&checkNode); ma && ma->baseExpr) { MultiTypeSubst instMap; // Collect typeMapping of baseExpr of member access, eg: A.foo. GenerateTypeMappingForBaseExpr(*ma, instMap); instantiateMap = MultiTypeSubstToTypeSubst(instMap); } // NOTE: member of extend with incompatible generic constraint will be filtered early by 'FilterTargetsInExtend'. auto genericDecl = d->GetGeneric(); if (!genericDecl) { return true; // Extend of instantiated type. } auto typeMapping = GenerateTypeMapping(*d, typeArgs); instantiateMap.merge(typeMapping); std::unordered_map, size_t> indexMap = GetTyArgsIndexMap(typeArgs); // Check generic constraints. for (auto& gc : genericDecl->genericConstraints) { auto instTy = typeManager.GetInstantiatedTy(gc->type->ty, instantiateMap); if (!Ty::IsTyCorrect(instTy)) { return false; } if (auto gty = DynamicCast(instTy); gty && !gty->isUpperBoundLegal) { continue; // If instantiated ty is generic type with invalid upper bounds, do not report error. } for (const auto& upperBound : gc->upperBounds) { auto upperBoundTy = typeManager.GetInstantiatedTy(upperBound->ty, instantiateMap); if (!Ty::IsTyCorrect(upperBoundTy)) { return false; } bool isSameTyButCType = instTy == upperBoundTy && instTy->IsCType(); if (!typeManager.IsSubtype(instTy, upperBoundTy, true, false) || isSameTyButCType) { auto& node = isTypeNode && !typeNodes[indexMap[instTy]]->TestAttr(Attribute::COMPILER_ADD) ? *typeNodes[indexMap[instTy]] : checkNode; diag.Diagnose(node, DiagKind::sema_generic_type_argument_not_match_constraint, d->ty->String()) .AddNote(*gc, DiagKind::sema_which_constraint_not_match, instTy->String(), "'" + upperBoundTy->String() + "'"); return false; } } } return true; } Ptr TypeChecker::TypeCheckerImpl::GetGenericType(Decl& d, const std::vector>& typeArgs) { // For GenericParam check typeArgs if (auto gp = DynamicCast(&d); gp) { if (!typeArgs.empty()) { diag.DiagnoseRefactor(DiagKindRefactor::sema_generic_argument_no_match, *typeArgs[0]); return d.ty; } } auto generic = d.GetGeneric(); if (!generic) { return d.ty; } if (typeArgs.size() != generic->typeParameters.size()) { diag.DiagnoseRefactor(DiagKindRefactor::sema_generic_argument_no_match, *typeArgs[0]); return d.ty; } // Build generic type mapping. TypeSubst typeMapping; for (size_t i = 0; i < typeArgs.size(); ++i) { typeMapping[StaticCast(generic->typeParameters[i]->ty)] = typeArgs[i]->ty; } // Instantiate the typeParameters of base function. return typeManager.GetInstantiatedTy(d.ty, typeMapping); } void TypeChecker::TypeCheckerImpl::CheckGenericExpr(Expr& expr) { auto exprTarget = expr.GetTarget(); auto realTarget = GetRealTarget(&expr, exprTarget); auto typeArgs = expr.GetTypeArgs(); if (!realTarget || (typeArgs.empty() && TypeCheckUtil::GetInstanationTys(expr).empty())) { return; } if (exprTarget->astKind == ASTKind::TYPE_ALIAS_DECL) { std::vector> diffs = GetUnusedTysInTypeAlias(*StaticAs(exprTarget)); Utils::EraseIf(typeArgs, [&diffs](auto type) { return Utils::In(type->ty, diffs); }); } expr.ty = GetGenericType(*realTarget, typeArgs); if (!CheckGenericDeclInstantiation(realTarget, typeArgs, expr)) { expr.ty = TypeManager::GetInvalidTy(); return; } } SubstPack TypeChecker::TypeCheckerImpl::GenerateGenericTypeMapping(const ASTContext& ctx, const Expr& expr) { SubstPack typeMapping; // Generate typeMapping by given expression node. if (auto ma = DynamicCast(&expr); ma) { if (auto target = ma->GetTarget(); target && ma->isExposedAccess && IsGenericUpperBoundCall(expr, *target)) { typeManager.GenerateTypeMappingForUpperBounds(typeMapping, *ma, *target); } else { GenerateTypeMappingForBaseExpr(*ma, typeMapping); } } if (auto re = DynamicCast(&expr); re) { auto sym = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, re->scopeName); if (sym && sym->node->IsNominalDecl()) { // Symbol guarantees sym->node not null. // Sema ty of structure declaration should be set in PreCheck stage. if (!Ty::IsTyCorrect(sym->node->ty)) { return typeMapping; } typeManager.GenerateGenericMapping(typeMapping, *sym->node->ty); } } return typeMapping; } cangjie_compiler-1.0.7/src/Sema/TypeCheckMatchExpr.cpp000066400000000000000000000445511510705540100226730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements typecheck apis for MatchExpr. */ #include #include #include #include #include #include "Diags.h" #include "JoinAndMeet.h" #include "PatternUsefulness.h" #include "TypeCheckerImpl.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace AST; using namespace Sema; namespace { const std::unordered_map PATTERN_ASTKIND_TO_STRING_MAP = { {ASTKind::CONST_PATTERN, "constant"}, {ASTKind::WILDCARD_PATTERN, "wildcard"}, {ASTKind::VAR_PATTERN, "binding"}, {ASTKind::TUPLE_PATTERN, "tuple"}, {ASTKind::TYPE_PATTERN, "type"}, {ASTKind::ENUM_PATTERN, "enum"}, }; void CheckMatchExprSetTy(MatchExpr& me, Ptr target, TypeManager& typeManager, ImportManager& impMgr, const std::set>& matchCaseTys) { // If any of branch equal to the target type, do not join branches' types (avoiding unexpected common supertype). if (matchCaseTys.find(target) != matchCaseTys.end()) { me.ty = target; return; } auto joinAndMeet = JoinAndMeet(typeManager, matchCaseTys, {}, &impMgr, me.curFile); auto joinRes = joinAndMeet.JoinAsVisibleTy(); me.ty = std::get_if>(&joinRes) ? std::get>(joinRes) : target; } } // namespace namespace Cangjie { Ptr TypeChecker::TypeCheckerImpl::SynMatchExpr(ASTContext& ctx, MatchExpr& me) { if (me.selector) { return SynMatchExprHasSelector(ctx, me); } return SynMatchExprNoSelector(ctx, me); } bool TypeChecker::TypeCheckerImpl::ChkMatchExpr(ASTContext& ctx, Ty& target, MatchExpr& me) { if (me.selector) { return ChkMatchExprHasSelector(ctx, target, me); } return ChkMatchExprNoSelector(ctx, target, me); } Ptr TypeChecker::TypeCheckerImpl::SynMatchExprHasSelector(ASTContext& ctx, MatchExpr& me) { // Synthesize selector's ty. CJC_NULLPTR_CHECK(me.selector); Synthesize(ctx, me.selector.get()); ReplaceIdealTy(*me.selector); // Check each case. if (me.sugarKind == Expr::SugarKind::QUEST) { return SynQuestSugarMatchCaseBody(ctx, me); } else { // NoSugar, As, Is return SynNormalMatchCaseBody(ctx, me); } } Ptr TypeChecker::TypeCheckerImpl::SynNormalMatchCaseBody(ASTContext& ctx, MatchExpr& me) { std::set> matchCaseTyVec; CJC_NULLPTR_CHECK(me.selector); auto selectorTy = me.selector->ty; bool isMatchCorrect = !me.matchCases.empty(); for (auto& mc : me.matchCases) { CJC_NULLPTR_CHECK(mc); CJC_ASSERT(!mc->patterns.empty()); mc->SetCtxExprForPatterns(me.selector.get()); if (Ty::IsTyCorrect(selectorTy)) { // Check whether pattern is compatible with selectorTy. bool isPatOK = ChkMatchCasePatterns(ctx, selectorTy, *mc); // Check the actions in a match case anyway. bool isGuardOK = ChkMatchCasePatGuard(ctx, *mc); bool areActionsOK = ChkMatchCaseActions(ctx, nullptr, *mc); if (isPatOK && isGuardOK && areActionsOK) { matchCaseTyVec.insert(mc->ty); } else { isMatchCorrect = false; } } } if (!isMatchCorrect) { me.ty = TypeManager::GetInvalidTy(); return me.ty; } if (me.selector->ty->HasPlaceholder()) { me.selector->ty = typeManager.TryGreedySubst(me.selector->ty); for (auto& mc : me.matchCases) { (void)ChkMatchCasePatterns(ctx, me.selector->ty, *mc); } } // Join match expr's ty. auto joinAndMeet = JoinAndMeet(typeManager, matchCaseTyVec, {}, &importManager, me.curFile); auto joinRes = joinAndMeet.JoinAsVisibleTy(); if (auto optErrs = JoinAndMeet::SetJoinedType(me.ty, joinRes)) { if (me.sugarKind == Expr::SugarKind::IF_LET) { auto builder = diag.Diagnose(me, DiagKind::sema_diag_report_error_message, "types " + Ty::ToString(me.matchCases[0]->ty) + " and " + Ty::ToString(me.matchCases[1]->ty) + " of the two branches of this 'if' expression mismatch"); builder.AddNote(*optErrs); } else { auto builder = diag.Diagnose(me, DiagKind::sema_type_incompatible, "MatchCase"); builder.AddNote(*optErrs); } } // Check pattern exhaustiveness and set unreachable attr of match cases. // Hotfix: ignore for desugared matchExpr. if (Ty::IsTyCorrect(me.ty) && me.sugarKind == Expr::SugarKind::NO_SUGAR && !PatternUsefulness::CheckMatchExprHasSelectorExhaustivenessAndReachability(ctx.diag, typeManager, me)) { me.ty = TypeManager::GetInvalidTy(); return me.ty; } return me.ty; } Ptr TypeChecker::TypeCheckerImpl::SynQuestSugarMatchCaseBody(ASTContext& ctx, MatchExpr& me) { auto selectorTy = me.selector->ty; if (!Ty::IsTyCorrect(selectorTy)) { me.ty = TypeManager::GetInvalidTy(); return me.ty; } else if (!selectorTy->IsCoreOptionType()) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_optional_chain_non_optional, me); builder.AddMainHintArguments(selectorTy->String()); me.ty = TypeManager::GetInvalidTy(); return me.ty; } // Match desugared from e? always have 2 cases. CJC_ASSERT(me.matchCases.size() == 2); auto& mc0 = me.matchCases[0]; // Match desugared from e? always have patterns. CJC_ASSERT(mc0->patterns.size() == 1); CJC_NULLPTR_CHECK(mc0->patterns.front()); mc0->SetCtxExprForPatterns(me.selector.get()); // Desugared patterns are always compatible with selectorTy. Therefore, no error will be raised if e is correct. (void)ChkMatchCasePatterns(ctx, selectorTy, *mc0); // Desugared patterns have no pattern guards and hence skip the invocation of ChkMatchCasePatGuard. (void)ChkMatchCaseActions(ctx, nullptr, *mc0); auto& mc1 = me.matchCases[1]; CJC_ASSERT(mc1->patterns.size() == 1); CJC_NULLPTR_CHECK(mc1->patterns.front()); mc1->SetCtxExprForPatterns(me.selector.get()); ChkMatchCasePatterns(ctx, selectorTy, *mc1); // Case 1 is merely a None constructor, whose type is the SAME as the type of CASE0. Return value can be ignored. (void)Check(ctx, mc0->ty, mc1->exprOrDecls.get()); mc1->ty = mc1->exprOrDecls->ty; me.ty = mc0->ty; return me.ty; } Ptr TypeChecker::TypeCheckerImpl::SynMatchExprNoSelector(ASTContext& ctx, MatchExpr& me) { std::set> matchCaseTyVec; bool hasInvalidCase = false; for (auto& mco : me.matchCaseOthers) { if (!Ty::IsTyCorrect(SynMatchCaseNoSelector(ctx, *mco))) { hasInvalidCase = true; } else { matchCaseTyVec.insert(mco->ty); } } if (hasInvalidCase) { me.ty = TypeManager::GetInvalidTy(); return me.ty; } // Join match expr's ty. auto joinAndMeet = JoinAndMeet(typeManager, matchCaseTyVec, {}, &importManager, me.curFile); auto joinRes = joinAndMeet.JoinAsVisibleTy(); if (auto optErrs = JoinAndMeet::SetJoinedType(me.ty, joinRes)) { auto builder = diag.Diagnose(me, DiagKind::sema_type_incompatible, "MatchCase"); builder.AddNote(*optErrs); } // Check exhaustiveness and set unreachable attr of match cases. if (!CheckMatchExprNoSelectorExhaustiveness(me, false)) { me.ty = TypeManager::GetInvalidTy(); return me.ty; } return me.ty; } bool TypeChecker::TypeCheckerImpl::ChkMatchExprHasSelector(ASTContext& ctx, AST::Ty& target, AST::MatchExpr& me) { CJC_NULLPTR_CHECK(me.selector); std::set> matchCaseTyVec; bool isMatchCorrect = Synthesize(ctx, me.selector.get()) && ReplaceIdealTy(*me.selector); auto selectorTy = me.selector->ty; for (auto& mc : me.matchCases) { CJC_NULLPTR_CHECK(mc); CJC_ASSERT(!mc->patterns.empty()); mc->SetCtxExprForPatterns(me.selector.get()); // Check whether pattern has the sameTy with selectorTy. if (Ty::IsTyCorrect(selectorTy)) { bool isPatOK = ChkMatchCasePatterns(ctx, selectorTy, *mc); bool isGuardOK = ChkMatchCasePatGuard(ctx, *mc); bool areActionsOK = ChkMatchCaseActions(ctx, &target, *mc); if (isPatOK && isGuardOK && areActionsOK) { matchCaseTyVec.insert(mc->ty); } else { isMatchCorrect = false; } } } if (isMatchCorrect) { CheckMatchExprSetTy(me, &target, typeManager, importManager, matchCaseTyVec); // Check pattern exhaustiveness and set unreachable attr of match cases. if (me.sugarKind == Expr::SugarKind::NO_SUGAR && !PatternUsefulness::CheckMatchExprHasSelectorExhaustivenessAndReachability(ctx.diag, typeManager, me)) { isMatchCorrect = false; } } me.ty = isMatchCorrect ? me.ty : TypeManager::GetInvalidTy(); return isMatchCorrect; } bool TypeChecker::TypeCheckerImpl::ChkMatchExprNoSelector(ASTContext& ctx, AST::Ty& target, AST::MatchExpr& me) { bool isWellTyped = true; std::set> matchCaseTyVec; for (auto& mco : me.matchCaseOthers) { if (!ChkMatchCaseNoSelector(ctx, target, *mco)) { isWellTyped = false; } else { matchCaseTyVec.insert(mco->ty); } } if (isWellTyped) { CheckMatchExprSetTy(me, &target, typeManager, importManager, matchCaseTyVec); // Check pattern exhaustiveness and set unreachable attr of match cases. if (!CheckMatchExprNoSelectorExhaustiveness(me, false)) { isWellTyped = false; } } me.ty = isWellTyped ? me.ty : TypeManager::GetInvalidTy(); return isWellTyped; } Ptr TypeChecker::TypeCheckerImpl::SynMatchCaseNoSelector(ASTContext& ctx, MatchCaseOther& mco) { // Type of patternGuard (matchExpr) is boolean. if (Is(mco.matchExpr.get())) { mco.matchExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); } else { if (!Check(ctx, TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN), mco.matchExpr.get())) { mco.matchExpr->ty = TypeManager::GetInvalidTy(); } } // Synthesize the ty of exprOrDecls of each case. mco.ty = Synthesize(ctx, mco.exprOrDecls.get()); return mco.ty; } bool TypeChecker::TypeCheckerImpl::ChkMatchCasePatGuard(ASTContext& ctx, const MatchCase& mc) { bool ret = true; if (mc.patternGuard) { Ptr boolTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); ret = Check(ctx, boolTy, mc.patternGuard.get()); } return ret; } bool TypeChecker::TypeCheckerImpl::ChkMatchCaseActions(ASTContext& ctx, Ptr target, MatchCase& mc) { bool ret = true; if (!mc.exprOrDecls) { mc.ty = TypeManager::GetInvalidTy(); return false; } if (!target) { // Synthesize the ty of exprOrDecls of each case. mc.exprOrDecls->ty = Synthesize(ctx, mc.exprOrDecls.get()); mc.ty = mc.exprOrDecls->ty; } else if (Check(ctx, target, mc.exprOrDecls.get())) { // Check whether exprOrDecls->ty has given target ty. mc.ty = mc.exprOrDecls->ty; } else { mc.ty = TypeManager::GetInvalidTy(); ret = false; } return ret; } bool TypeChecker::TypeCheckerImpl::ChkNoVarPatternInOrPattern( const ASTContext& ctx, const std::vector>& ps) { CJC_ASSERT(!ps.empty()); if (ps.size() == 1) { return true; } std::vector> varPatterns; for (auto& pattern : ps) { CJC_NULLPTR_CHECK(pattern); Walker(pattern.get(), [&varPatterns, &ctx](Ptr node) { CJC_NULLPTR_CHECK(node); bool isVarPattern = node->astKind == ASTKind::VAR_PATTERN || (node->astKind == ASTKind::VAR_OR_ENUM_PATTERN && !ctx.IsEnumConstructor(StaticCast(*node).identifier)); if (isVarPattern) { varPatterns.emplace_back(node); return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }).Walk(); } if (varPatterns.empty()) { return true; } auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_var_in_or_pattern, *varPatterns.front()); auto iter = varPatterns.cbegin() + 1; // Skip the first `Var` as it has been reported in main hint. while (iter != varPatterns.cend()) { builder.AddHint(**iter); ++iter; } return false; } bool TypeChecker::TypeCheckerImpl::ChkPatternsSameASTKind(const ASTContext& ctx, const std::vector>& patterns) { CJC_ASSERT(!patterns.empty()); if (patterns.size() == 1) { CJC_NULLPTR_CHECK(patterns.front()); return true; } if (std::all_of(patterns.cbegin(), patterns.cend(), [](const OwnedPtr& pattern) { CJC_NULLPTR_CHECK(pattern); return pattern->astKind == ASTKind::VAR_OR_ENUM_PATTERN || pattern->astKind == ASTKind::ENUM_PATTERN; })) { // We don't know the real type of the `VarOrEnumPattern` by far, and there are two possible cases: // 1. if it is a `VarPattern`, the `CheckMatchCaseNoVarPatternsInOr` will report errors latter. // 2. if it is an `EnumPattern`, it should be compiled correctly, e.g., `Some(_) | None`. return true; } auto patternToString = [&ctx](const Pattern& p) { auto kind = p.astKind; if (kind == ASTKind::VAR_OR_ENUM_PATTERN) { kind = ctx.IsEnumConstructor(StaticCast(p).identifier) ? ASTKind::ENUM_PATTERN : ASTKind::VAR_PATTERN; } auto iter = PATTERN_ASTKIND_TO_STRING_MAP.find(kind); std::string res = iter == PATTERN_ASTKIND_TO_STRING_MAP.cend() ? "invalid" : iter->second; return res + " pattern"; }; const OwnedPtr& front = patterns.front(); for (auto iter = patterns.cbegin() + 1; iter != patterns.cend(); ++iter) { CJC_NULLPTR_CHECK(*iter); if ((*iter)->astKind != front->astKind) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_different_or_pattern, *front); builder.AddMainHintArguments(patternToString(*front)); builder.AddHint(**iter, patternToString(**iter)); return false; } } return true; } bool TypeChecker::TypeCheckerImpl::ChkMatchCasePatterns(ASTContext& ctx, Ptr target, MatchCase& mc) { if (!ChkNoVarPatternInOrPattern(ctx, mc.patterns) || !ChkPatternsSameASTKind(ctx, mc.patterns)) { for (auto& pattern : mc.patterns) { CJC_NULLPTR_CHECK(pattern); pattern->ty = TypeManager::GetInvalidTy(); } return false; } bool ret = true; for (auto& pattern : mc.patterns) { auto safeTarget = target ? target : TypeManager::GetInvalidTy(); if (!ChkPattern(ctx, *safeTarget, *pattern)) { ret = false; } } return ret; } bool TypeChecker::TypeCheckerImpl::ChkMatchCaseNoSelector(ASTContext& ctx, Ty& target, MatchCaseOther& mco) { bool ret = true; // Type of patternGuard (matchExpr) is boolean. if (Is(mco.matchExpr.get())) { mco.matchExpr->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); } else { if (!Check(ctx, TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN), mco.matchExpr.get())) { mco.matchExpr->ty = TypeManager::GetInvalidTy(); ret = false; } } // Check whether exprOrDecls->ty has given target ty. if (Check(ctx, &target, mco.exprOrDecls.get())) { mco.ty = mco.exprOrDecls->ty; } else { mco.ty = TypeManager::GetInvalidTy(); ret = false; } return ret; } bool TypeChecker::TypeCheckerImpl::CheckMatchExprNoSelectorExhaustiveness(MatchExpr& me, bool hasDefault) { bool ret = true; size_t defaultCase{0}; for (size_t i = 0; i < me.matchCaseOthers.size(); ++i) { auto matchCaseOther = me.matchCaseOthers[i].get(); if (!hasDefault && Is(matchCaseOther->matchExpr.get())) { hasDefault = true; defaultCase = i; } if (Ty::IsInitialTy(matchCaseOther->ty)) { ret = false; diag.Diagnose(*matchCaseOther, DiagKind::sema_match_case_has_no_type); continue; } } if (!hasDefault) { ret = false; diag.Diagnose(me, DiagKind::sema_match_case_must_have_default); } // The cases after default should be set UNREACHABLE. for (size_t i = defaultCase + 1; i < me.matchCaseOthers.size(); i++) { me.matchCaseOthers[i]->EnableAttr(Attribute::UNREACHABLE); } return ret; } bool TypeChecker::TypeCheckerImpl::IsIrrefutablePattern(const Pattern& pattern) { switch (pattern.astKind) { case AST::ASTKind::INVALID_PATTERN: case AST::ASTKind::CONST_PATTERN: case AST::ASTKind::TYPE_PATTERN: return false; case AST::ASTKind::WILDCARD_PATTERN: case AST::ASTKind::VAR_PATTERN: return true; case AST::ASTKind::TUPLE_PATTERN: { auto& tuplePattern = static_cast(pattern); return std::all_of(tuplePattern.patterns.cbegin(), tuplePattern.patterns.cend(), [this](const OwnedPtr& p) { return IsIrrefutablePattern(*p); }); } case AST::ASTKind::ENUM_PATTERN: { if (!pattern.ty || !pattern.ty->IsEnum()) { return false; } auto& enumPattern = static_cast(pattern); auto enumTy = RawStaticCast(pattern.ty); return enumTy && enumTy->declPtr && enumTy->declPtr->constructors.size() == 1 && std::all_of(enumPattern.patterns.cbegin(), enumPattern.patterns.cend(), [this](const OwnedPtr& p) { return IsIrrefutablePattern(*p); }); } default: CJC_ABORT(); return false; } } }; // namespace Cangjie cangjie_compiler-1.0.7/src/Sema/TypeCheckOverflow.cpp000066400000000000000000000161721510705540100226010ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the integer overflow strategy. */ #include "TypeCheckerImpl.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/Frontend/CompilerInstance.h" using namespace Cangjie; using namespace AST; namespace { bool IsOverflowOp(TokenKind op) { switch (op) { case TokenKind::ADD: case TokenKind::SUB: case TokenKind::MUL: case TokenKind::DIV: case TokenKind::MOD: case TokenKind::EXP: case TokenKind::INCR: case TokenKind::DECR: case TokenKind::ADD_ASSIGN: case TokenKind::SUB_ASSIGN: case TokenKind::MUL_ASSIGN: case TokenKind::DIV_ASSIGN: case TokenKind::MOD_ASSIGN: case TokenKind::EXP_ASSIGN: return true; default: return false; } } void SetIncOrDecOverflowExpr(Node& node) { auto ide = As(&node); if (ide == nullptr || !IsOverflowOp(ide->op)) { return; } // Not Integer, no need to set overflow flag. if (ide->expr == nullptr || Ty::IsInitialTy(ide->expr->ty) || !ide->expr->ty->IsInteger()) { return; } // Implement overflow in codegen. ide->expr->EnableAttr(Attribute::NUMERIC_OVERFLOW); ide->EnableAttr(Attribute::NUMERIC_OVERFLOW); ide->expr->overflowStrategy = ide->overflowStrategy; return; } void SetAssignOverflowExpr(Node& node) { auto ae = As(&node); if (ae == nullptr || !IsOverflowOp(ae->op)) { return; } // Not Integer, no need to set overflow flag. if (ae->leftValue == nullptr || Ty::IsInitialTy(ae->leftValue->ty) || !ae->leftValue->ty->IsInteger()) { return; } // Implement overflow in codegen. ae->EnableAttr(Attribute::NUMERIC_OVERFLOW); return; } void SetUnaryOverflowExpr(Node& node) { auto ue = As(&node); if (ue == nullptr || !IsOverflowOp(ue->op)) { return; } // Not Integer, no need to set overflow flag. if (ue->expr == nullptr || Ty::IsInitialTy(ue->expr->ty) || !ue->expr->ty->IsInteger()) { return; } // Implement overflow in codegen. ue->EnableAttr(Attribute::NUMERIC_OVERFLOW); return; } void SetBinaryOverflowExpr(Node& node) { auto be = As(&node); if (be == nullptr || !IsOverflowOp(be->op)) { return; } // Not Integer or not Same no need to set overflow flag. if (be->leftExpr == nullptr || Ty::IsInitialTy(be->leftExpr->ty) || !be->leftExpr->ty->IsInteger() || be->rightExpr == nullptr || Ty::IsInitialTy(be->rightExpr->ty) || !be->rightExpr->ty->IsInteger()) { return; } // Implement overflow in codegen. be->EnableAttr(Attribute::NUMERIC_OVERFLOW); return; } void SetOverflowFlag(Node& node) { // Set integer overflow flag. if (node.astKind == ASTKind::INC_OR_DEC_EXPR) { SetIncOrDecOverflowExpr(node); return; } if (node.astKind == ASTKind::ASSIGN_EXPR) { SetAssignOverflowExpr(node); return; } if (Ty::IsInitialTy(node.ty) || !node.ty->IsInteger()) { return; } if (node.astKind == ASTKind::UNARY_EXPR) { SetUnaryOverflowExpr(node); return; } if (node.astKind == ASTKind::BINARY_EXPR) { SetBinaryOverflowExpr(node); return; } return; } void SetOverflowStrategyForPkg(Node& node) { Walker walkerAST(&node, [](Ptr curNode) -> VisitAction { switch (curNode->astKind) { case ASTKind::INC_OR_DEC_EXPR: case ASTKind::ASSIGN_EXPR: case ASTKind::UNARY_EXPR: case ASTKind::BINARY_EXPR: { SetOverflowFlag(*curNode); break; } default: break; } return VisitAction::WALK_CHILDREN; }); walkerAST.Walk(); } } // namespace // Set overflow strategy after typechecked. void TypeChecker::SetOverflowStrategy(const std::vector>& pkgs) const { // Update overflow strategy for desugared decls. impl->SetIntegerOverflowStrategy(); // Check integer overflow strategy. for (auto& pkg : pkgs) { SetOverflowStrategyForPkg(*pkg); } } namespace { void SetOverflowStrategy(Node& node, const OverflowStrategy overflowStrategy, const OverflowStrategy optionStrategy) { auto setOverflowStrategyInFuncBody = [optionStrategy](Node& curNode) -> void { if (curNode.astKind == ASTKind::FUNC_DECL) { auto& fd = StaticCast(curNode); if (fd.funcBody) { SetOverflowStrategy(*fd.funcBody, fd.overflowStrategy, optionStrategy); } } else if (curNode.astKind == ASTKind::LAMBDA_EXPR) { auto& le = StaticCast(curNode); if (le.funcBody) { SetOverflowStrategy(*le.funcBody, le.overflowStrategy, optionStrategy); } } }; auto preVisit = [overflowStrategy, optionStrategy, setOverflowStrategyInFuncBody]( Ptr curNode) -> VisitAction { switch (curNode->astKind) { case ASTKind::FUNC_DECL: case ASTKind::LAMBDA_EXPR: { if (curNode->TestAttr(Attribute::NUMERIC_OVERFLOW)) { setOverflowStrategyInFuncBody(*curNode); return VisitAction::SKIP_CHILDREN; } break; } case ASTKind::INC_OR_DEC_EXPR: case ASTKind::ASSIGN_EXPR: case ASTKind::UNARY_EXPR: case ASTKind::BINARY_EXPR: case ASTKind::TYPE_CONV_EXPR: { auto expr = RawStaticCast(curNode); if (overflowStrategy == OverflowStrategy::NA) { expr->overflowStrategy = optionStrategy; } else { expr->overflowStrategy = overflowStrategy; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // mark call to operator func with overflowStrategy, used in split operator if (expr->desugarExpr) { expr->desugarExpr->overflowStrategy = expr->overflowStrategy; } #endif break; } default: break; } return VisitAction::WALK_CHILDREN; }; Walker walkerAST(&node, preVisit); walkerAST.Walk(); } } // namespace // Set integer overflow strategy before sema typechecking. void TypeChecker::TypeCheckerImpl::SetIntegerOverflowStrategy() const { CJC_NULLPTR_CHECK(ci); if (ci->invocation.globalOptions.overflowStrategy == OverflowStrategy::NA) { return; } // Choose integer overflow strategy. for (auto& pkg : ci->GetSourcePackages()) { ::SetOverflowStrategy(*pkg, OverflowStrategy::NA, ci->invocation.globalOptions.overflowStrategy); } } cangjie_compiler-1.0.7/src/Sema/TypeCheckPattern.cpp000066400000000000000000000654771510705540100224270ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements typecheck apis for Pattern. */ #include "TypeCheckerImpl.h" #include #include #include #include #include #include #include #include "BuiltInOperatorUtil.h" #include "Collector.h" #include "DiagSuppressor.h" #include "Diags.h" #include "JoinAndMeet.h" #include "TypeCheckUtil.h" #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Types.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Basic/Print.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Sema/CommonTypeAlias.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace Sema; using namespace TypeCheckUtil; using namespace AST; namespace { void InvalidateNonTopLevelCache(const ASTContext& ctx) { std::string query = "scope_level=0"; ctx.searcher->InvalidateCacheBut(query); } // Caller guarantees not re-enter for same node. OwnedPtr VarOrEnumPatternToEnumPattern(ASTContext& ctx, const VarOrEnumPattern& vep, bool buildTrie) { OwnedPtr ep = MakeOwned(); CopyNodeWithFileID(ep.get(), &vep); ep->begin = vep.begin; ep->end = vep.end; ep->constructor = CreateRefExpr(vep.identifier, nullptr, vep.begin); CopyNodeWithFileID(ep->constructor.get(), &vep); ep->constructor->begin = vep.begin; ep->constructor->end = vep.end; // Set symbol for LSP. ep->constructor->DisableAttr(Attribute::COMPILER_ADD); ep->isInMacroCall = vep.isInMacroCall; if (vep.symbol) { auto nodeInfo = NodeInfo(*ep->constructor, vep.identifier, vep.symbol->scopeLevel, vep.symbol->scopeName); Collector::AddSymbol(ctx, nodeInfo, buildTrie); ctx.DeleteCurrentInvertedIndexes(&vep); // Preserve top-level symbol search cache to ensure faster next call to GetToplevelDecls. InvalidateNonTopLevelCache(ctx); } return ep; } // Caller guarantees not re-enter for same node. OwnedPtr VarOrEnumPatternToVarPattern(ASTContext& ctx, const VarOrEnumPattern& vep, bool buildTrie) { OwnedPtr vp = MakeOwned(vep.identifier, vep.begin); CopyNodeWithFileID(vp.get(), &vep); vp->begin = vep.begin; vp->end = vep.end; CopyNodeWithFileID(vp->varDecl.get(), &vep); vp->varDecl->begin = vep.begin; vp->varDecl->end = vep.end; vp->varDecl->fullPackageName = vep.GetFullPackageName(); vp->varDecl->identifier.SetPos(vep.identifier.Begin(), vep.identifier.End()); vp->varDecl->identifier.SetRaw(vep.identifier.IsRaw()); // Set symbol for LSP. if (vep.symbol) { auto nodeInfo = NodeInfo(*vp->varDecl, vep.identifier, vep.symbol->scopeLevel, vep.symbol->scopeName); Collector::AddSymbol(ctx, nodeInfo, buildTrie); ctx.DeleteCurrentInvertedIndexes(&vep); // Preserve top-level symbol search cache to ensure faster next call to GetToplevelDecls. InvalidateNonTopLevelCache(ctx); } return vp; } void FillEnumPatternMemberAccessTypeArgumentsTy(TypeManager& typeManager, EnumTy& targetTy, MemberAccess& ma) { Ptr ed = ma.GetTarget() ? As(ma.GetTarget()->outerDecl) : nullptr; if (!ed || !ed->generic || !ma.baseExpr) { return; } MultiTypeSubst typeMapping; if (auto ref = DynamicCast(ma.baseExpr.get()); ref && ref->instTys.empty()) { // instTys is empty, use targetTy to generate the typeMapping. typeManager.GenerateGenericMapping(typeMapping, targetTy); for (auto& typeParam : ed->generic->typeParameters) { ref->instTys.emplace_back(GetMappedTy(typeMapping, StaticCast(typeParam->ty))); } } else if (ma.baseExpr->ty) { // instTys is not empty, use ma.baseExpr->ty to generate the typeMapping. typeManager.GenerateGenericMapping(typeMapping, *ma.baseExpr->ty); } ma.baseExpr->ty = typeManager.GetBestInstantiatedTy(ed->ty, typeMapping); ma.ty = typeManager.GetBestInstantiatedTy(ma.ty, typeMapping); } void SetTyForEnumPatternConstructor(TypeManager& typeManager, EnumTy& targetTy, const EnumPattern& ep) { if (!ep.constructor) { return; } if (ep.constructor->astKind == ASTKind::MEMBER_ACCESS) { MemberAccess& ma = static_cast(*ep.constructor); if (!ma.baseExpr || !ma.baseExpr->ty || !ma.baseExpr->ty->IsEnum()) { return; } FillEnumPatternMemberAccessTypeArgumentsTy(typeManager, targetTy, ma); } else if (targetTy.GetGenericTy()) { CJC_ASSERT(ep.constructor->astKind == ASTKind::REF_EXPR); MultiTypeSubst typeMapping; typeManager.GenerateGenericMapping(typeMapping, targetTy); ep.constructor->ty = typeManager.GetBestInstantiatedTy(ep.constructor->ty, typeMapping); } } bool IsSubtypeBoxed(TypeManager& typeManager, Ty& leaf, Ty& root); bool IsTupleSubtypeBoxed(TypeManager& typeManager, const TupleTy& leaf, const TupleTy& root) { if (leaf.typeArgs.size() != root.typeArgs.size()) { return false; } for (size_t i = 0; i < leaf.typeArgs.size(); i++) { CJC_NULLPTR_CHECK(leaf.typeArgs[i]); CJC_NULLPTR_CHECK(root.typeArgs[i]); if (!IsSubtypeBoxed(typeManager, *leaf.typeArgs[i], *root.typeArgs[i])) { return false; } } return true; } bool IsFuncSubTypeBoxed(TypeManager& typeManager, const FuncTy& leaf, const FuncTy& root) { CJC_NULLPTR_CHECK(leaf.retTy); CJC_NULLPTR_CHECK(root.retTy); if (leaf.paramTys.size() != root.paramTys.size()) { return false; } for (size_t i = 0; i < leaf.paramTys.size(); i++) { CJC_NULLPTR_CHECK(root.paramTys[i]); CJC_NULLPTR_CHECK(leaf.paramTys[i]); if (!IsSubtypeBoxed(typeManager, *root.paramTys[i], *leaf.paramTys[i])) { return false; } } return leaf.isC == root.isC && leaf.hasVariableLenArg == root.hasVariableLenArg && IsSubtypeBoxed(typeManager, *leaf.retTy, *root.retTy); } // Check implicitly boxed subtype relationships. // For TupleTy and FuncTy, the covariant/contravariant relationships are checked recursively with implicit box. bool IsSubtypeBoxed(TypeManager& typeManager, Ty& leaf, Ty& root) { if (leaf.IsTuple() && root.IsTuple()) { return IsTupleSubtypeBoxed(typeManager, static_cast(leaf), static_cast(root)); } else if (leaf.IsFunc() && root.IsFunc()) { return IsFuncSubTypeBoxed(typeManager, static_cast(leaf), static_cast(root)); } else { return typeManager.IsSubtype(&leaf, &root, true, false); } } }; // namespace bool TypeChecker::TypeCheckerImpl::ChkPattern(ASTContext& ctx, Ty& target, Pattern& p, bool isPatternInMatch) { switch (p.astKind) { case ASTKind::WILDCARD_PATTERN: { auto wp = StaticAs(&p); return ChkWildcardPattern(target, *wp); } case ASTKind::CONST_PATTERN: { auto cp = StaticAs(&p); return ChkConstPattern(ctx, target, *cp); } case ASTKind::TYPE_PATTERN: { auto tp = StaticAs(&p); return ChkTypePattern(ctx, target, *tp); } case ASTKind::VAR_PATTERN: { auto vp = StaticAs(&p); return ChkVarPattern(ctx, target, *vp); } case ASTKind::ENUM_PATTERN: { auto ep = StaticAs(&p); return ChkEnumPattern(ctx, target, *ep); } case AST::ASTKind::VAR_OR_ENUM_PATTERN: { auto vep = StaticAs(&p); return ChkVarOrEnumPattern(ctx, target, *vep); } case ASTKind::TUPLE_PATTERN: { auto tp = StaticAs(&p); return ChkTuplePattern(ctx, target, *tp, isPatternInMatch); } // Handle invalid patterns explicitly. case ASTKind::INVALID_PATTERN: { p.ty = TypeManager::GetInvalidTy(); return false; } default: { Errorln("unhandled pattern"); p.ty = TypeManager::GetInvalidTy(); return false; } } } bool TypeChecker::TypeCheckerImpl::ChkWildcardPattern(Ty& target, WildcardPattern& p) const { p.ty = ⌖ return true; } bool TypeChecker::TypeCheckerImpl::ChkConstPattern(ASTContext& ctx, Ty& target, ConstPattern& p) { CJC_NULLPTR_CHECK(p.literal); // 1. Check the type of the literal in the constant pattern. if (target.IsRune() && IsSingleRuneStringLiteral(*p.literal)) { p.literal->ty = ⌖ p.ty = ⌖ } else if (target.kind == TypeKind::TYPE_UINT8 && IsSingleByteStringLiteral(*p.literal)) { p.literal->ty = ⌖ p.ty = ⌖ ChkLitConstExprRange(StaticCast(*p.literal)); } else if (!Check(ctx, &target, p.literal.get())) { p.ty = TypeManager::GetInvalidTy(); return false; } // 2. The literals are compared by their values, i.e., the types must be checked exactly equal, // auto-boxed Options are not allowed. if (!typeManager.IsTyEqual(p.literal->ty, &target)) { DiagMismatchedTypes(diag, *p.literal, target); p.ty = TypeManager::GetInvalidTy(); return false; } // 3. String interpolations are not allowed in constant patterns. if (auto lce = DynamicCast(p.literal.get()); lce && lce->siExpr) { diag.DiagnoseRefactor(DiagKindRefactor::sema_interpolation_in_const_pattern, p); p.ty = TypeManager::GetInvalidTy(); return false; } // 4. Check if the selector can be compared with the pattern. p.ty = typeManager.TryGreedySubst(&target); const auto& typeCandidates = GetBinaryOpTypeCandidates(TokenKind::EQUAL); if (Utils::InKeys(p.ty->kind, typeCandidates)) { return true; } // String literals are compared by the `==` method. return ChkOpOverloadForConstPattern(ctx, target, p); } bool TypeChecker::TypeCheckerImpl::ChkOpOverloadForConstPattern(ASTContext& ctx, Ty& target, ConstPattern& p) { auto boolTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); auto callExpr = MakeOwnedNode(); ctx.RemoveTypeCheckCache(*callExpr); auto callBase = MakeOwnedNode(); ctx.RemoveTypeCheckCache(*callBase); callBase->scopeName = p.scopeName; callBase->baseExpr = MakeOwnedNode(); callBase->baseExpr->ty = ⌖ // ensure synthesize skip the dummy node ctx.SkipSynForCorrectTy(*callBase->baseExpr); callBase->field = "=="; callExpr->baseFunc = std::move(callBase); CJC_NULLPTR_CHECK(p.literal); auto funcArg = AST::CreateFuncArg(ASTCloner::Clone(p.literal.get())); funcArg->scopeName = p.scopeName; callExpr->args.push_back(std::move(funcArg)); AddCurFile(*callExpr, p.curFile); { // Create a scope for DiagSuppressor. auto ds = DiagSuppressor(diag); if (Check(ctx, boolTy, callExpr.get())) { ds.ReportDiag(); p.operatorCallExpr = std::move(callExpr); return true; } } p.ty = TypeManager::GetNonNullTy(p.ty); diag.Diagnose(p, DiagKind::sema_not_overload_in_match); return false; } bool TypeChecker::TypeCheckerImpl::ChkTypePattern(ASTContext& ctx, Ty& target, TypePattern& p) { CJC_NULLPTR_CHECK(p.pattern); CJC_NULLPTR_CHECK(p.type); p.type->ty = Synthesize(ctx, p.type.get()); CJC_NULLPTR_CHECK(p.type->ty); if (typeManager.IsSubtype(&target, p.type->ty, true, false)) { p.needRuntimeTypeCheck = false; p.matchBeforeRuntime = true; } else { if (IsSubtypeBoxed(typeManager, target, *p.type->ty)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_unreachable_pattern, p) .AddNote("the selector is of type '" + target.String() + "', which is not a subtype of '" + p.type->ty->String()); } p.needRuntimeTypeCheck = IsNeedRuntimeCheck(typeManager, target, *p.type->ty); p.matchBeforeRuntime = false; } if (!ChkPattern(ctx, *p.type->ty, *p.pattern)) { p.ty = TypeManager::GetInvalidTy(); return false; } p.ty = p.type->ty; return true; } bool TypeChecker::TypeCheckerImpl::ChkVarPattern(const ASTContext& ctx, Ty& target, VarPattern& p) { if (p.varDecl->identifier != V_COMPILER) { auto decls = ctx.GetDeclsByName({p.varDecl->identifier, p.varDecl->scopeName}); for (const auto& decl : decls) { if (decl == p.varDecl.get()) { continue; } if (p.varDecl->TestAttr(Attribute::PLATFORM) && decl->TestAttr(Attribute::COMMON)) { // common variable can be matched with VarWithDeclPattern on platform continue; } // The variable has been defined in this MatchCase, e.g., `case (x, x) => {}`. // Or it conflicts with definition in while-let body, e.g., `while (let a <- 1) { let a = 1 }` DiagRedefinitionWithFoundNode(diag, *p.varDecl, *decl); p.ty = TypeManager::GetInvalidTy(); p.varDecl->ty = TypeManager::GetInvalidTy(); p.varDecl->fullPackageName = p.GetFullPackageName(); p.varDecl->EnableAttr(Attribute::IS_CHECK_VISITED); return false; } } p.ty = ⌖ p.varDecl->ty = p.ty; p.varDecl->EnableAttr(Attribute::IS_CHECK_VISITED); return true; } bool TypeChecker::TypeCheckerImpl::ChkEnumPattern(ASTContext& ctx, Ty& target, EnumPattern& p) { CJC_NULLPTR_CHECK(p.constructor); bool mayMatch = false; auto maybeInferred = typeManager.TryGreedySubst(&target); if (auto tempEnumTy = DynamicCast(maybeInferred); tempEnumTy) { FindEnumPatternTarget(ctx, tempEnumTy->decl, p); // If selector is a generic instantiated enum, set the pattern ty to instantiated ty. SetTyForEnumPatternConstructor(typeManager, *tempEnumTy, p); mayMatch = true; } else if (target.IsPlaceholder()) { // in case the select has placeholder ty var, only try to find target from pattern FindEnumPatternTarget(ctx, nullptr, p); auto enumTy = Is(p.constructor->ty) ? p.constructor->ty->typeArgs[1] : p.constructor->ty; auto placeholderEnumTy = typeManager.ConstrainByCtor(StaticCast(target), *enumTy); if (placeholderEnumTy) { SetTyForEnumPatternConstructor(typeManager, *StaticCast(placeholderEnumTy), p); mayMatch = true; } } if (mayMatch) { if (auto enumTy = DynamicCast(p.constructor->ty); enumTy && typeManager.IsTyEqual(p.constructor->ty, &target)) { p.ty = p.constructor->ty; return true; } else if (auto funcTy = DynamicCast(p.constructor->ty); funcTy) { if (!IsFuncTyEnumPatternMatched(ctx, target, *funcTy, p)) { p.ty = TypeManager::GetInvalidTy(); return false; } p.ty = funcTy->retTy; return true; } } diag.Diagnose(p, DiagKind::sema_pattern_not_match, "enum"); p.ty = TypeManager::GetInvalidTy(); return false; } bool TypeChecker::TypeCheckerImpl::IsFuncTyEnumPatternMatched( ASTContext& ctx, Ty& target, const FuncTy& funcTy, const EnumPattern& p) { if (funcTy.paramTys.size() != p.patterns.size()) { (void)diag.Diagnose(p, DiagKind::sema_pattern_not_match, "enum"); return false; } for (size_t i = 0; i < p.patterns.size(); i++) { CJC_NULLPTR_CHECK(p.patterns[i]); if (funcTy.paramTys[i] == nullptr || !ChkPattern(ctx, *funcTy.paramTys[i], *p.patterns[i])) { return false; } } if (!typeManager.IsTyEqual(funcTy.retTy, &target)) { (void)diag.Diagnose(p, DiagKind::sema_pattern_not_match, "enum"); return false; } return true; } bool TypeChecker::TypeCheckerImpl::ChkVarOrEnumPattern(ASTContext& ctx, Ty& target, VarOrEnumPattern& p) { if (p.pattern != nullptr) { // We have to ChkPattern again because of the LSPCompilerInstance. if (!ChkPattern(ctx, target, *p.pattern)) { p.ty = TypeManager::GetInvalidTy(); return false; } p.ty = ⌖ return true; } if (ctx.IsEnumConstructor(p.identifier)) { p.pattern = VarOrEnumPatternToEnumPattern(ctx, p, ci->buildTrie); if (!ChkPattern(ctx, target, *p.pattern)) { p.ty = TypeManager::GetInvalidTy(); return false; } } else { p.pattern = VarOrEnumPatternToVarPattern(ctx, p, ci->buildTrie); auto& decl = *StaticAs(p.pattern.get())->varDecl; auto name = std::make_pair(p.identifier, p.scopeName); ctx.AddDeclName(name, decl); if (!ChkPattern(ctx, target, *p.pattern)) { ctx.RemoveDeclByName(name, decl); p.ty = TypeManager::GetInvalidTy(); return false; } } if (auto ep = DynamicCast(p.pattern.get()); ep) { // In order to support the find-references feature for LSP, we have to insert the constructor to the `users` // of the `Decl`, by setting the third argument (`insertTarget`) as `true`. ReplaceTarget(ep->constructor.get(), ep->constructor->GetTarget(), true); } p.ty = ⌖ return true; } bool TypeChecker::TypeCheckerImpl::ChkTuplePattern(ASTContext& ctx, Ty& target, TuplePattern& p, bool isPatternInMatch) { if (auto tupleTy = DynamicCast(&target); tupleTy) { if (tupleTy->typeArgs.size() != p.patterns.size()) { diag.Diagnose(p, DiagKind::sema_tuple_pattern_with_correct_size_expected); p.ty = TypeManager::GetInvalidTy(); return false; } for (size_t i = 0; i < p.patterns.size(); i++) { if (!Check(ctx, tupleTy->typeArgs[i], p.patterns[i].get())) { p.ty = TypeManager::GetInvalidTy(); return false; } } } else { if (isPatternInMatch) { DiagMismatchedTypesWithFoundTy(diag, p, target.String(), "Tuple"); } else { diag.Diagnose(p, DiagKind::sema_tuple_pattern_not_match, "initializer"); } p.ty = TypeManager::GetInvalidTy(); return false; } p.ty = ⌖ return true; } std::vector> TypeChecker::TypeCheckerImpl::FindEnumPatternTargets( ASTContext& ctx, Ptr ed, EnumPattern& ep) { if (auto ma = DynamicCast(ep.constructor.get()); ma) { InferMemberAccess(ctx, *ma); if (!ma->target || !ma->target->outerDecl || ma->target->outerDecl->astKind != ASTKind::ENUM_DECL) { return {}; } std::vector> result{ma->target}; result.insert(result.end(), ma->targets.begin(), ma->targets.end()); return result; } else if (ed) { return FieldLookup(ctx, ed, ep.GetIdentifier(), {.lookupExtend = false}); } else if (auto re = DynamicCast(ep.constructor.get())) { // in case selector is placeholder re->callOrPattern = &ep; InferRefExpr(ctx, *re); if (!re->ref.target || !re->ref.target->outerDecl || re->ref.target->outerDecl->astKind != ASTKind::ENUM_DECL) { return {}; } std::vector> result{re->ref.target}; result.insert(result.end(), re->ref.targets.begin(), re->ref.targets.end()); return result; } return {}; } // Use enumType to determine enumPattern's target recursively. void TypeChecker::TypeCheckerImpl::FindEnumPatternTarget(ASTContext& ctx, Ptr ed, EnumPattern& ep) { CJC_NULLPTR_CHECK(ep.constructor); // Search the enum element in enumDecl. std::vector> result = FindEnumPatternTargets(ctx, ed, ep); if (result.empty()) { diag.Diagnose(ep, DiagKind::sema_undeclared_identifier, ep.GetIdentifier()); ep.constructor->ty = TypeManager::GetInvalidTy(); return; } // Clear any existing target since the result haven't been checked. ReplaceTarget(ep.constructor.get(), nullptr); // Search all the result to find the right one and set the target. for (auto& it : result) { if (ep.patterns.empty()) { // If 'EnumPattern' has no sub-pattern, the target is matched when the type of decl is EnumTy. if (it->ty && it->ty->kind == TypeKind::TYPE_ENUM) { ReplaceTarget(ep.constructor.get(), it, false); break; } } else { // If 'EnumPattern' has sub-patterns, // the target is matched when the type of decl is FuncTy with same number of parameters. if (auto funcTy = DynamicCast(it->ty); funcTy && ep.patterns.size() == funcTy->paramTys.size()) { CJC_ASSERT(it->astKind == ASTKind::FUNC_DECL); ReplaceTarget(ep.constructor.get(), it, false); break; } } } auto target = ep.constructor->GetTarget(); if (!target || !Ty::IsTyCorrect(target->ty)) { diag.Diagnose(ep, DiagKind::sema_enum_pattern_param_size_error); ep.constructor->ty = TypeManager::GetInvalidTy(); return; } ep.constructor->ty = target->ty; if (ep.patterns.empty()) { return; } auto fd = StaticAs(target); CJC_NULLPTR_CHECK(fd->funcBody); CJC_ASSERT(!fd->funcBody->paramLists.empty()); CJC_ASSERT(fd->funcBody->paramLists[0]->params.size() >= ep.patterns.size()); for (size_t i = 0; i < ep.patterns.size(); ++i) { auto subTarget = Ty::GetDeclPtrOfTy(fd->funcBody->paramLists[0]->params[i]->ty); bool hasSubEnumPattern = ep.patterns[i]->astKind == ASTKind::ENUM_PATTERN && subTarget && subTarget->astKind == ASTKind::ENUM_DECL; if (hasSubEnumPattern) { FindEnumPatternTarget( ctx, RawStaticCast(subTarget), *RawStaticCast(ep.patterns[i].get())); } } } bool TypeChecker::TypeCheckerImpl::ChkTryWildcardPattern( Ptr target, WildcardPattern& p, std::vector>& included) { p.ty = target; if (auto classTy = DynamicCast(target); classTy) { if (std::find(included.begin(), included.end(), classTy) != included.end()) { diag.Diagnose(p, DiagKind::sema_useless_exception_type); } } return true; } bool TypeChecker::TypeCheckerImpl::ChkExceptTypePattern( ASTContext& ctx, ExceptTypePattern& etp, std::vector>& included) { bool result = true; std::set> typeTys; auto exception = importManager.GetCoreDecl(CLASS_EXCEPTION); auto error = importManager.GetCoreDecl(CLASS_ERROR); bool foundClass = exception && error; for (auto& type : etp.types) { CJC_NULLPTR_CHECK(type); Synthesize(ctx, type.get()); CJC_NULLPTR_CHECK(type->ty); if (!foundClass || type->ty->IsNothing() || (!typeManager.IsSubtype(type->ty, exception->ty) && !typeManager.IsSubtype(type->ty, error->ty))) { diag.Diagnose(*type, DiagKind::sema_except_catch_type_error); result = false; continue; } if (Utils::In(included, [this, &type](Ptr ty) { return typeManager.IsSubtype(type->ty, ty); })) { diag.Diagnose(*type, DiagKind::sema_useless_exception_type); } else { included.emplace_back(type->ty); } typeTys.emplace(type->ty); } if (!result || typeTys.empty()) { etp.ty = TypeManager::GetInvalidTy(); return false; } auto joinRes = JoinAndMeet(typeManager, typeTys, {}, &importManager, etp.curFile).JoinAsVisibleTy(); auto optErrs = JoinAndMeet::SetJoinedType(etp.ty, joinRes); CJC_NULLPTR_CHECK(etp.ty); if (optErrs.has_value()) { diag.Diagnose(etp, DiagKind::sema_type_incompatible, "pattern").AddNote(*optErrs); return false; } return Check(ctx, etp.ty, etp.pattern.get()); } bool TypeChecker::TypeCheckerImpl::ChkHandlePatterns(ASTContext& ctx, Handler& h, std::vector>& included) { auto& ctp = *StaticAs(h.commandPattern.get()); CJC_ASSERT(ctp.types.size() >= 1); auto maybeCtpTyAsCommand = ChkCommandTypePattern(ctx, ctp, included); if (!maybeCtpTyAsCommand) { return false; } auto& ctpTyAsCommand = **maybeCtpTyAsCommand; h.commandResultTy = ctpTyAsCommand.typeArgs[0]; return Check(ctx, ctp.ty, ctp.pattern.get()); } std::optional> TypeChecker::TypeCheckerImpl::ChkCommandTypePattern( ASTContext& ctx, CommandTypePattern& ctp, std::vector>& included) { CJC_ASSERT(ctp.types.size() == 1); bool result = true; std::set> typeTys; auto command = importManager.GetImportedDecl(EFFECT_PACKAGE_NAME, CLASS_COMMAND); if (!command) { diag.DiagnoseRefactor(DiagKindRefactor::sema_command_handle_type_error, *ctp.types[0]); return {}; } std::optional> cmdTypeAsCommand; CJC_ASSERT(ctp.types.size() == 1); auto cmdTypePat = ctp.types[0].get(); CJC_NULLPTR_CHECK(cmdTypePat); Synthesize(ctx, cmdTypePat); CJC_NULLPTR_CHECK(cmdTypePat->ty); auto prCTys = promotion.Promote(*cmdTypePat->ty, *command->ty); if (cmdTypePat->ty->IsNothing() || prCTys.empty()) { diag.DiagnoseRefactor(DiagKindRefactor::sema_command_handle_type_error, *cmdTypePat); result = false; ctp.ty = TypeManager::GetInvalidTy(); return {}; } CJC_ASSERT(prCTys.size() == 1); if (Utils::In(included, [this, &cmdTypePat](Ty* ty) { return typeManager.IsSubtype(cmdTypePat->ty, ty); })) { diag.DiagnoseRefactor(DiagKindRefactor::sema_useless_command_type, *cmdTypePat); } else { included.emplace_back(cmdTypePat->ty); } typeTys.emplace(cmdTypePat->ty); cmdTypeAsCommand = *prCTys.begin(); if (!result || typeTys.empty()) { ctp.ty = TypeManager::GetInvalidTy(); return {}; } auto joinRes = JoinAndMeet(typeManager, typeTys).JoinAsVisibleTy(); auto optErrs = JoinAndMeet::SetJoinedType(ctp.ty, joinRes); CJC_NULLPTR_CHECK(ctp.ty); if (optErrs.has_value()) { diag.Diagnose(ctp, DiagKind::sema_type_incompatible, "pattern").AddNote(*optErrs); return {}; } return cmdTypeAsCommand; } cangjie_compiler-1.0.7/src/Sema/TypeCheckReference.cpp000066400000000000000000001525111510705540100226720ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements typecheck apis for reference exprs. */ #include "TypeCheckerImpl.h" #include "Diags.h" #include "ScopeManager.h" #include "TypeCheckUtil.h" #include "cangjie/AST/ASTContext.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Symbol.h" #include "cangjie/AST/Types.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Basic/Position.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Modules/ModulesUtils.h" #include "cangjie/Utils/CastingTemplate.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; using namespace Sema; using namespace AST; using namespace TypeCheckUtil; namespace { inline bool CheckTargetTypeArgs(Ptr const decl, size_t numTypeArgs) { Ptr generic = decl ? decl->GetGeneric() : nullptr; return generic != nullptr && generic->typeParameters.size() == numTypeArgs; } inline bool CheckForQuestFuncRetType(DiagnosticEngine& diag, Decl& target, const Expr& expr, bool hasTargetTy) { if (hasTargetTy) { return true; // Check after the return type later in 'ChkCallExpr' or 'ChkRefExpr'. } if (Ty::IsTyCorrect(target.ty) && target.ty->HasQuestTy() && target.astKind == ASTKind::FUNC_DECL) { DiagUnableToInferReturnType(diag, *StaticAs(&target), expr); return false; } return true; } } // namespace void TypeChecker::TypeCheckerImpl::CheckThisOrSuper(const ASTContext& ctx, RefExpr& re) { re.ty = TypeManager::GetInvalidTy(); auto sym = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, re.scopeName); if (!sym || !Is(sym->node)) { diag.Diagnose(re, DiagKind::sema_this_super_use_error_outside_class, re.ref.identifier.Val()); return; } auto decl = RawStaticCast(sym->node); // Set this/super's type and target first. Will not diagnose. if (re.isThis) { re.ty = InferTypeOfThis(re, *decl); } else { re.ty = InferTypeOfSuper(re, *decl); } // Legality of using this/super will be checked after typecheck in 'CheckLegalityOfReference'. } Ptr TypeChecker::TypeCheckerImpl::InferTypeOfThis(RefExpr& re, InheritableDecl& objDecl) { Ptr outerDecl = &objDecl; auto typeDecl = outerDecl; // Handle special case: this in ExtendDecl: if extended types is built-in type, directly return extended Type. if (auto ed = AST::As(typeDecl); ed && ed->extendedType && ed->extendedType->ty) { if (ed->extendedType->ty->IsBuiltin()) { return ed->extendedType->ty; } // Real type decl is decl of extened type. auto realTypeDecl = Ty::GetDeclPtrOfTy(ed->extendedType->ty); if (!Is(realTypeDecl)) { return TypeManager::GetInvalidTy(); } typeDecl = RawStaticCast(realTypeDecl); } ReplaceTarget(&re, typeDecl); // all this reference are checked as This type if possible (i.e. it is instance of class decl). auto ret = ReplaceWithGenericTyInInheritableDecl(outerDecl->ty, *outerDecl, *typeDecl); if (auto cd = DynamicCast(ret)) { ret = typeManager.GetClassThisTy(*cd->decl, cd->typeArgs); } return ret; } Ptr TypeChecker::TypeCheckerImpl::InferTypeOfSuper(RefExpr& re, const InheritableDecl& objDecl) { // Super can only used in classDecl, if not in class decl, super's ty is invalid. if (objDecl.astKind != ASTKind::CLASS_DECL) { return TypeManager::GetInvalidTy(); } auto cd = RawStaticCast(&objDecl); if (auto classTy = DynamicCast(cd->ty); classTy) { auto superClassTy = classTy->GetSuperClassTy(); if (superClassTy) { CJC_NULLPTR_CHECK(superClassTy->declPtr); ReplaceTarget(&re, superClassTy->declPtr); return ReplaceWithGenericTyInInheritableDecl(superClassTy->declPtr->ty, objDecl, *superClassTy->declPtr); } } return TypeManager::GetInvalidTy(); } Ptr TypeChecker::TypeCheckerImpl::ReplaceWithGenericTyInInheritableDecl( Ptr ty, const Decl& outerDecl, const InheritableDecl& id) { if (!ty || !ty->HasGeneric()) { return ty; } // Builds the generic mapping between typeArguments of the derived struc decl and the inherited decl, // such as: class decl & it's super class decl, the extend decl and the inheritable decl. // e.g. // class A { let item: T } // extend A { } // extend has an implicit generic, the above is in fact `extend A { }` // if we have // extend A { // func foo(): T1 { return this.item } // } // The type of this.item is T, which is not consistent with the return type T1, but T1 is equivalent to T here. // So we need to replace the T's to T1's. // currentType is the A (AST node) in class B <: A, or extend A // targetGeneric is the (AST node) at the declaration of A, e.g. class A Ptr currentTy = outerDecl.ty; MultiTypeSubst typeMapping; if (currentTy && id.ty) { typeMapping = promotion.GetPromoteTypeMapping(*currentTy, *id.ty); } if (typeMapping.empty()) { return ty; } return typeManager.GetBestInstantiatedTy(ty, typeMapping); } void TypeChecker::TypeCheckerImpl::CheckUsageOfThis(const ASTContext& ctx, const RefExpr& re) const { auto outerDecl = GetCurInheritableDecl(ctx, re.scopeName); if (outerDecl == nullptr) { return; } // 'curFuncBody' can belong to FuncDecl, LambdaExpr, MacroDecl. auto curFuncBody = GetCurFuncBody(ctx, re.scopeName); if (curFuncBody && curFuncBody->TestAttr(Attribute::STATIC)) { diag.Diagnose(re, DiagKind::sema_static_members_cannot_call_members, re.ref.identifier.Val()); } // Any use of 'this()' call in interface is invalid. if (Is(outerDecl) && re.callOrPattern) { diag.Diagnose(re, DiagKind::sema_illegal_this_in_interface); } auto outMostFuncSymbol = ScopeManager::GetOutMostSymbol(ctx, SymbolKind::FUNC_LIKE, re.scopeName); Ptr outMostFunc = outMostFuncSymbol ? DynamicCast(outMostFuncSymbol->node) : nullptr; bool insideConstructor = outMostFunc && IsInstanceConstructor(*outMostFunc); // When 'outMostFunc' is funcDecl, 'curFuncBody' must have valid value. if (outMostFunc) { // 'this' cannot be captured by lambda or internal function in a mut function. bool lambdaOrNestedFuncInMutFunc = outMostFunc->TestAttr(Attribute::MUT) && curFuncBody && (curFuncBody->funcDecl != outMostFunc); if (lambdaOrNestedFuncInMutFunc) { diag.Diagnose(re, DiagKind::sema_capture_this_or_instance_field_in_func, re.ref.identifier.Val(), "mutable function '" + outMostFunc->identifier + "'"); } // 'this' cannot be used as an expression in a mut function, constructor of inheritable class or finalizer. bool referenceNotAllowed = re.isAlone && (outMostFunc->TestAttr(Attribute::MUT) || outMostFunc->IsFinalizer() || (insideConstructor && IsInheritableClass(*outerDecl))); if (referenceNotAllowed) { std::string info = outMostFunc->TestAttr(Attribute::MUT) ? "mutable function '" + outMostFunc->identifier + "'" : (outMostFunc->IsFinalizer() ? "finalizer" : std::string("constructor of ") + (outerDecl->TestAttr(Attribute::OPEN) ? "open" : "abstract") + " class"); diag.Diagnose(re, DiagKind::sema_use_this_as_an_expression_in_func, info); } } // Check usage of this in 'struct' decl except call like 'this()'. // Member variables this.xx cannot be accessed in nested function/lambda of 'struct' constructor bool referenceInStruct = outerDecl->astKind == ASTKind::STRUCT_DECL && !re.callOrPattern; if (referenceInStruct) { bool notInConstructor = curFuncBody && (!curFuncBody->funcDecl || !curFuncBody->funcDecl->TestAttr(Attribute::CONSTRUCTOR)); if (notInConstructor && insideConstructor) { diag.Diagnose(re, DiagKind::sema_illegal_capture_this, "struct"); } bool inInvalidCtx = !curFuncBody || (outMostFuncSymbol && outMostFuncSymbol->astKind == ASTKind::LAMBDA_EXPR); if (inInvalidCtx) { diag.Diagnose(re, DiagKind::sema_illegal_this_outside_struct_constructor); } } bool referenceInInitializer = !curFuncBody && !ctx.currentCheckingNodes.empty(); if (referenceInInitializer) { CheckThisOrSuperInInitializer(*ctx.currentCheckingNodes.top(), re); } CheckInvalidRefInCFunc(ctx, re); } void TypeChecker::TypeCheckerImpl::CheckUsageOfSuper(const ASTContext& ctx, const RefExpr& re) const { auto outerDecl = GetCurInheritableDecl(ctx, re.scopeName); if (outerDecl == nullptr) { return; } auto curFuncBody = GetCurFuncBody(ctx, re.scopeName); if (curFuncBody && curFuncBody->TestAttr(Attribute::STATIC)) { diag.Diagnose(re, DiagKind::sema_static_members_cannot_call_members, re.ref.identifier.Val()); } // Any use of 'super' call in interface is invalid. if (Is(outerDecl)) { diag.Diagnose(re, DiagKind::sema_use_super_in_interface); } // 'super' is not allowed in extend decl or other non-class decl. if (outerDecl->astKind == ASTKind::EXTEND_DECL) { diag.DiagnoseRefactor(DiagKindRefactor::sema_extend_use_super, re); } else if (outerDecl->astKind != ASTKind::CLASS_DECL) { diag.Diagnose(re, DiagKind::sema_super_use_error_inside_non_class); } else { // It's illegal to use 'super' individually, e.g: var a = super if (re.isAlone) { diag.Diagnose(re, DiagKind::sema_illegal_super_alone); } } bool referenceInInitializer = !curFuncBody && !ctx.currentCheckingNodes.empty(); if (referenceInInitializer) { CheckThisOrSuperInInitializer(*ctx.currentCheckingNodes.top(), re); } CheckInvalidRefInCFunc(ctx, re); } void TypeChecker::TypeCheckerImpl::CheckThisOrSuperInInitializer(const Node& node, const RefExpr& re) const { if (node.astKind == ASTKind::VAR_DECL) { if (node.TestAttr(Attribute::STATIC)) { diag.Diagnose( re, DiagKind::sema_this_or_super_not_allowed_to_initialize_static_member, re.ref.identifier.Val()); } else if (re.isSuper || (re.isThis && re.isAlone)) { diag.Diagnose( re, DiagKind::sema_this_or_super_not_allowed_to_initialize_non_static_member, re.ref.identifier.Val()); } } } bool TypeChecker::TypeCheckerImpl::IsRefTypeArgSizeValid(const Expr& expr, std::vector>& targets) { auto typeArgs = expr.GetTypeArgs(); if (typeArgs.empty()) { return true; // Ignored for empty typeArguments. } bool isValid; // If re has type arguments, the number of type parameters of the target should match re. size_t numTypeArgs = typeArgs.size(); // If typeArguments is already substitutied for typealias expression, here need to check with real target. bool typeAliasSubstituted = typeArgs[0]->TestAttr(Attribute::COMPILER_ADD) && !targets.empty() && targets[0]->astKind == ASTKind::TYPE_ALIAS_DECL; if (typeAliasSubstituted) { isValid = CheckTargetTypeArgs(TypeCheckUtil::GetRealTarget(targets[0]), numTypeArgs); } else { for (auto it = targets.begin(); it != targets.end();) { (!CheckTargetTypeArgs(*it, numTypeArgs)) ? (it = targets.erase(it)) : (++it); } isValid = !targets.empty(); } if (!isValid) { diag.DiagnoseRefactor(DiagKindRefactor::sema_generic_argument_no_match, expr); } return isValid; } void TypeChecker::TypeCheckerImpl::FilterCandidatesForRef( const ASTContext& ctx, const RefExpr& re, std::vector>& targets) { auto curFuncBody = GetCurFuncBody(ctx, re.scopeName); // Filter macro func bcz macro can NOT be referred. Utils::EraseIf(targets, [curFuncBody](auto it) -> bool { if (!it) { return false; // Need keep null for re-export case. At least one valid target before null. } return (!curFuncBody && it->TestAttr(Attribute::MACRO_FUNC)) || (curFuncBody && !curFuncBody->TestAttr(Attribute::MACRO_INVOKE_BODY) && it->TestAttr(Attribute::MACRO_FUNC)); }); auto sym = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, re.scopeName); if (sym && Is(sym->node)) { FilterTargetsInExtend(re, sym->node->ty, targets); } // Filter incompatible and shadowed function if all candidates are functions. if (IsAllFuncDecl(targets)) { std::vector> funcs; std::for_each( targets.begin(), targets.end(), [&funcs](auto fd) { funcs.emplace_back(RawStaticCast(fd)); }); FilterShadowedFunc(funcs); targets.clear(); targets.insert(targets.begin(), funcs.begin(), funcs.end()); } } bool TypeChecker::TypeCheckerImpl::FilterInvalidEnumTargets( const NameReferenceExpr& nre, std::vector>& targets) { CJC_ASSERT(!targets.empty()); auto candidates = targets; auto ma = DynamicCast(&nre); // Filter enum constructor which is not matched. Utils::EraseIf(targets, [&nre, &ma](auto target) { return target && target->TestAttr(Attribute::ENUM_CONSTRUCTOR) && target->IsFunc() && !nre.callOrPattern && (!ma || !ma->isPattern); }); if (targets.empty()) { auto diagBuilder = diag.DiagnoseRefactor( DiagKindRefactor::sema_enum_constructor_with_param_must_have_args, nre, candidates[0]->identifier); for (auto& it : candidates) { diagBuilder.AddNote(*it, MakeRangeForDeclIdentifier(*it), "found candidate"); } return false; } return true; } bool TypeChecker::TypeCheckerImpl::FilterAndCheckTargetsOfRef( const ASTContext& ctx, RefExpr& re, std::vector>& targets) { RemoveDuplicateElements(targets); FilterCandidatesForRef(ctx, re, targets); if (targets.empty()) { diag.Diagnose(re, DiagKind::sema_undeclared_identifier, re.ref.identifier.Val()); return false; } if (targets.size() == 1 && targets[0]->astKind == ASTKind::PACKAGE_DECL && re.isAlone) { diag.DiagnoseRefactor(DiagKindRefactor::sema_cannot_ref_to_pkg_name, re); targets.clear(); return false; } // If source decls are not all functions, it will shadow imported decls. // If source decls not existed or there are all functions, // we need report error when there are more than one imported decls which are not all functions. auto sourceEndIt = std::stable_partition( targets.begin(), targets.end(), [](auto decl) { return !decl->TestAttr(Attribute::IMPORTED); }); if (std::all_of(targets.begin(), sourceEndIt, [](auto decl) { return decl->astKind == ASTKind::FUNC_DECL; }) && std::distance(sourceEndIt, targets.end()) > 1 && std::any_of(sourceEndIt, targets.end(), [](auto decl) { return decl->astKind != ASTKind::FUNC_DECL; })) { std::vector> imports(sourceEndIt, targets.end()); if (!std::all_of(imports.begin(), imports.end(), [](auto decl) { return decl->IsMemberDecl(); })) { DiagAmbiguousUse(diag, re, re.ref.identifier.Val(), imports, importManager); return false; } } if (!IsRefTypeArgSizeValid(re, targets) || !FilterInvalidEnumTargets(re, targets)) { return false; } // RefExpr in flow expr or func argument will be synthesized first and then checked, so ignored here. bool hasTarget = ctx.HasTargetTy(&re) || re.isInFlowExpr; bool referenceNeedTypeInfer = !hasTarget && re.isAlone; if (referenceNeedTypeInfer) { return FilterTargetsForFuncReference(re, targets); } hasTarget = hasTarget || re.callOrPattern != nullptr; return !targets.empty() ? CheckForQuestFuncRetType(diag, *targets[0], re, hasTarget) : false; } bool TypeChecker::TypeCheckerImpl::FilterTargetsForFuncReference(const Expr& expr, std::vector>& targets) { auto typeArgs = expr.GetTypeArgs(); // Reference without target type and used alone must be inferred. if (typeArgs.empty() && !targets.empty()) { // If reference has no type arguments and no target type, the target should not have type parameters. Utils::EraseIf(targets, [](auto it) { return it && it->IsFunc() && it->GetGeneric() != nullptr; }); if (targets.empty()) { DiagGenericFuncWithoutTypeArg(diag, expr); return false; } } // If this reference needs to be inferred and has multiple function targets, the target cannot be distinguished. if (targets.size() > 1 && IsAllFuncDecl(targets)) { DiagAmbiguousUse(diag, expr, targets.front()->identifier, targets, importManager); targets.clear(); return false; } return true; } void TypeChecker::TypeCheckerImpl::RemoveTargetNotMeetExtendConstraint( const Ptr baseTy, std::vector>& targets) { Utils::EraseIf(targets, [this, &baseTy](auto target) -> bool { bool ignored = !target || !Ty::IsTyCorrect(baseTy) || !Is(target->outerDecl) || !Ty::IsTyCorrect(target->outerDecl->ty); if (ignored) { return false; } // Check whether target in extend decl or inherited interface decl is accessible from member base type. auto prTys = promotion.Promote(*baseTy, *target->outerDecl->ty); if (prTys.empty()) { // No promoted type existed means the 'baseTy' not fit constraint of inheriting target's type. return true; } Ptr extend = nullptr; if (auto res = typeManager.GetExtendDeclByInterface(*baseTy, *target->outerDecl->ty)) { extend = *res; } else { extend = DynamicCast(target->outerDecl); } if (extend && extend->extendedType && extend->extendedType->ty) { prTys = promotion.Promote(*baseTy, *extend->extendedType->ty); auto promotedTy = prTys.empty() ? TypeManager::GetInvalidTy() : *prTys.begin(); if (Ty::IsTyCorrect(promotedTy)) { return !typeManager.CheckGenericDeclInstantiation(extend, promotedTy->typeArgs); } } return false; }); } bool TypeChecker::TypeCheckerImpl::FilterTargetsInExtend( const AST::NameReferenceExpr& nre, Ptr baseTy, std::vector>& targets) { if (auto ma = DynamicCast(&nre)) { CJC_NULLPTR_CHECK(ma->baseExpr); auto target = ma->baseExpr->GetTarget(); if (target && target->IsTypeDecl() && baseTy && !IsThisOrSuper(*ma->baseExpr)) { // If the baseExpr is typeDecl and the current memberAccess is the baseFunc of a call, // the typeArg is inferrable from callExpr, do not filter candidates with constraint for now. bool inferrable = !baseTy->typeArgs.empty() && ma->baseExpr->GetTypeArgs().empty() && ma->callOrPattern; if (inferrable) { return true; } } } RemoveTargetNotMeetExtendConstraint(baseTy, targets); return !targets.empty(); } bool TypeChecker::TypeCheckerImpl::FilterAndCheckTargetsOfNameAccess( const ASTContext& ctx, const MemberAccess& ma, std::vector>& targets) { if (targets.empty()) { DiagMemberAccessNotFound(ma); return false; } // Erase members which cannot accessed by package name or type name. Utils::EraseIf(targets, [](auto target) { return target && !target->TestAttr(Attribute::ENUM_CONSTRUCTOR) && !target->TestAttr(Attribute::STATIC) && !target->TestAttr(Attribute::GLOBAL); }); if (targets.empty()) { diag.Diagnose(ma, DiagKind::sema_illegal_access_non_static_member, ma.field.Val()); return false; } RemoveDuplicateElements(targets); if (!FilterTargetsInExtend(ma, ma.baseExpr->ty, targets)) { DiagMemberAccessNotFound(ma); return false; } else if (!IsRefTypeArgSizeValid(ma, targets) || !FilterInvalidEnumTargets(ma, targets)) { return false; } if (targets.empty()) { DiagMemberAccessNotFound(ma); return false; } // MemberAccess in func argument will be synthesized first and then checked, so ignored here. bool referenceNeedTypeInfer = ((!ctx.HasTargetTy(&ma) && (ma.isAlone || !ma.callOrPattern)) || (targets[0]->astKind != ASTKind::FUNC_DECL && !targets[0]->TestAttr(Attribute::ENUM_CONSTRUCTOR))) && !ma.isInFlowExpr && !ma.isPattern; if (referenceNeedTypeInfer) { CJC_ASSERT(ma.baseExpr); // If type argument is not given in base and can't be inferred, report error. auto targetOfBase = ma.baseExpr->GetTarget(); bool noTypeArgs = NeedFurtherInstantiation(ma.baseExpr->GetTypeArgs()); bool genericBaseWithoutTypeArg = targetOfBase && targetOfBase->IsTypeDecl() && targetOfBase->GetGeneric() && noTypeArgs; if (genericBaseWithoutTypeArg) { diag.Diagnose(*ma.baseExpr, DiagKind::sema_generic_type_without_type_argument); return false; } return FilterTargetsForFuncReference(ma, targets); } bool hasTarget = ctx.HasTargetTy(&ma) || ma.isInFlowExpr || ma.callOrPattern != nullptr; return !targets.empty() ? CheckForQuestFuncRetType(diag, *targets[0], ma, hasTarget) : false; } Ptr TypeChecker::TypeCheckerImpl::FilterAndGetTargetsOfObjAccess( const ASTContext& ctx, MemberAccess& ma, std::vector>& targets) { if (targets.empty()) { DiagMemberAccessNotFound(ma); return nullptr; } CJC_NULLPTR_CHECK(ma.baseExpr); if (!FilterTargetsInExtend(ma, ma.baseExpr->ty, targets)) { DiagMemberAccessNotFound(ma); return nullptr; } for (auto it = targets.begin(); it != targets.end();) { // Objects cannot be used to access static members. auto decl = *it; if (decl && decl->TestAttr(Attribute::STATIC)) { it = targets.erase(it); } else { if (decl && decl->astKind == ASTKind::FUNC_DECL) { ma.targets.push_back(StaticAs(decl)); } ++it; } } if (targets.empty()) { diag.Diagnose(ma, DiagKind::sema_object_cannot_access_static_member, ma.field.Val()); } bool hasTarget = ctx.HasTargetTy(&ma) || ma.isInFlowExpr; bool referenceNeedTypeInfer = !hasTarget && (ma.isAlone || !ma.callOrPattern); if (referenceNeedTypeInfer) { if (targets.empty()) { return nullptr; } auto accessibleDecls = GetAccessibleDecls(ctx, ma, targets); // If there are accessible decls, checking for function reference resovling. if (!accessibleDecls.empty() && !FilterTargetsForFuncReference(ma, accessibleDecls)) { return nullptr; } return accessibleDecls.empty() ? targets[0] : accessibleDecls[0]; } auto target = GetAccessibleDecl(ctx, ma, targets); if (!target) { if (targets.empty()) { return nullptr; } target = targets[0]; } hasTarget = hasTarget || ma.callOrPattern != nullptr; return target && CheckForQuestFuncRetType(diag, *target, ma, hasTarget) ? target : nullptr; } void TypeChecker::TypeCheckerImpl::InstantiateReferenceType( const ASTContext& ctx, NameReferenceExpr& expr, const TypeSubst& instantiateMap) { if (expr.compilerAddedTyArgs) { expr.typeArguments.clear(); } auto target = expr.GetTarget(); if (!expr.GetTypeArgs().empty()) { // For partial generic typealias case. if (target && target->astKind != ASTKind::TYPE_ALIAS_DECL) { UpdateInstTysWithTypeArgs(expr); } CheckGenericExpr(expr); } SubstPack typeMapping; if (instantiateMap.empty()) { typeMapping = GenerateGenericTypeMapping(ctx, expr); auto tys = typeManager.ApplySubstPackNonUniq(expr.ty, typeMapping, true); bool cannotInfer = tys.size() != 1 && Is(target) && !ctx.HasTargetTy(&expr); if (cannotInfer) { diag.Diagnose(expr, DiagKind::sema_ambiguous_func_ref, target->identifier.Val()); } expr.ty = *tys.begin(); } else { typeManager.PackMapping(typeMapping, instantiateMap); expr.ty = typeManager.GetInstantiatedTy(expr.ty, instantiateMap); } if (!Ty::IsTyCorrect(expr.ty)) { expr.ty = TypeManager::GetInvalidTy(); } else if (auto fd = DynamicCast(target); fd) { DynamicBindingThisType(expr, *fd, typeMapping); } } void TypeChecker::TypeCheckerImpl::CheckAccessLegalityOfRefExpr(const ASTContext& ctx, RefExpr& re) { // Get target from re.ref.target, if re.ref.target is null but re.ref.targets not empty, get first of targets. auto decl = (!re.ref.target && !re.ref.targets.empty()) ? re.ref.targets[0] : re.ref.target; if (!decl || decl->astKind == ASTKind::PACKAGE_DECL) { return; } if (auto fd = DynamicCast(decl); fd && fd->propDecl) { decl = fd->propDecl; // Property was desugar to get/set, resest checking decl to propDecl. } auto funcSrc = ScopeManager::GetOutMostSymbol(ctx, SymbolKind::FUNC, re.scopeName); if (funcSrc && Is(funcSrc->node)) { auto outerFd = RawStaticCast(funcSrc->node); // Checking with propDecl's funcDecl. bool accessStructLeftValue = re.TestAttr(Attribute::LEFT_VALUE) && outerFd->funcBody && outerFd->funcBody->parentStruct; CheckImmutableFuncAccessMutableFunc(re.begin, *outerFd, *decl, accessStructLeftValue); // Should use original decl here. CheckForbiddenFuncReferenceAccess(re.begin, *outerFd, *decl); } if (decl->IsFunc()) { if (re.isAlone) { (void)CheckFuncAccessControl(ctx, re, re.ref.targets); // The unsafe function can only be called rather than as name reference if (decl->TestAttr(Attribute::UNSAFE) && !re.isInFlowExpr) { diag.DiagnoseRefactor(DiagKindRefactor::sema_unsafe_func_can_only_be_called, re); } } } else { (void)CheckNonFuncAccessControl(ctx, re, *decl); } // We check if a static function will access non-static variables or non-static functions. // Note that, if we have multiple candidates here, we will not be able to implement the // check. In that case, we will leave the check to the place where we resolve the overload // issue. // NOTE: some desugared expressions only have 're.ref.target' with no targets, so checking with <= 1. if (re.ref.targets.size() <= 1) { (void)IsLegalAccessFromStaticFunc(ctx, re, *decl); } // When isAlone is false, refExpr is part of a rvalue expression, which can be callExpr or memberAccess (the // value of isAlone is set when checking these nodes). Since a composite type cannot appear alone as an rvalue, // it needs to be checked when isAlone is true. For example: let a = A is wrong when A is a class. The flag // isAlone is true in this case; if (re.isAlone && decl->IsTypeDecl()) { diag.Diagnose(re, DiagKind::sema_ref_not_be_type, re.ref.identifier.Val()) .AddNote(*decl, DiagKind::sema_found_candidate_decl); } CheckInvalidRefInCFunc(ctx, re, *decl); } void TypeChecker::TypeCheckerImpl::CheckAccessLegalityOfMemberAccess(const ASTContext& ctx, MemberAccess& ma) { // Get target from ma.target, if ma.target is null but ma.targets not empty, get first of targets. auto decl = (!ma.target && !ma.targets.empty()) ? ma.targets[0] : ma.target; if (!decl || decl->astKind == ASTKind::PACKAGE_DECL || !ma.baseExpr) { return; } if (auto fd = DynamicCast(decl); fd && fd->propDecl) { decl = fd->propDecl; // Property was desugar to get/set, resest checking decl to propDecl. } if (decl->IsFunc()) { if (ma.isAlone) { std::vector> targets{ma.target}; (void)CheckFuncAccessControl(ctx, ma, targets); // The unsafe function can only be called rather than as name reference if (decl->TestAttr(Attribute::UNSAFE) && !ma.isInFlowExpr) { diag.DiagnoseRefactor(DiagKindRefactor::sema_unsafe_func_can_only_be_called, ma); } } } else { (void)CheckNonFuncAccessControl(ctx, ma, *decl); } auto targetOfBase = ma.baseExpr->GetTarget(); // Whether current is access member by real/generic type. bool isStaticAccessByName = targetOfBase && targetOfBase->IsTypeDecl() && !IsThisOrSuper(*ma.baseExpr); if (decl->IsTypeDecl() || decl->TestAnyAttr(Attribute::GLOBAL, Attribute::STATIC) || isStaticAccessByName) { CheckStaticMemberAccessLegality(ma, *decl); } else { CheckInstanceMemberAccessLegality(ctx, ma, *decl); } } void TypeChecker::TypeCheckerImpl::CheckStaticMemberAccessLegality(const MemberAccess& ma, const Decl& target) { auto targetOfBase = ma.baseExpr->GetTarget(); if (targetOfBase) { IsNamespaceMemberAccessLegal(ma, *targetOfBase, target); } // When isAlone is false, reference is part of a rvalue expression, which can be callExpr or memberAccess (the // value of isAlone is set when checking these nodes). Since a composite type cannot appear alone as an rvalue, // it needs to be checked when isAlone is true. For example: let a = A is wrong when A is a class. The flag // isAlone is true in this case; if (ma.isAlone && target.IsTypeDecl()) { diag.Diagnose(ma, DiagKind::sema_ref_not_be_type, ma.field.Val()) .AddNote(target, DiagKind::sema_found_candidate_decl); } } void TypeChecker::TypeCheckerImpl::IsNamespaceMemberAccessLegal( const MemberAccess& ma, AST::Decl& targetOfBaseDecl, const Decl& target) { auto realTarget = TypeCheckUtil::GetRealTarget(&targetOfBaseDecl); // Check whether the accessed member is external if is accessed from another package. if (targetOfBaseDecl.astKind == ASTKind::PACKAGE_DECL && target.TestAttr(Attribute::GLOBAL)) { // Only external target can be accessed by other packages. std::string packageName = ma.curFile && ma.curFile->curPackage ? ma.curFile->curPackage->fullPackageName : ""; std::string targetPackage = target.fullPackageName; auto relation = Modules::GetPackageRelation(packageName, targetPackage); if (!Modules::IsVisible(target, relation)) { diag.Diagnose(ma, DiagKind::sema_package_internal_decl_obtain_illegal, GetAccessLevelStr(target), ma.field.Val(), targetPackage); } } else if (!ma.isPattern && ma.isAlone && ma.baseExpr->ty && ma.baseExpr->ty->kind == TypeKind::TYPE_ENUM && target.astKind == ASTKind::FUNC_DECL && target.TestAttr(Attribute::ENUM_CONSTRUCTOR)) { DiagMemberAccessNotFound(ma); } else if (realTarget->IsNominalDecl() && !realTarget->TestAttr(Attribute::FOREIGN) && target.TestAttr(Attribute::ABSTRACT)) { auto fieldRange = MakeRange(ma.field); std::string strType = target.astKind == ASTKind::FUNC_DECL ? "function" : "property"; diag.DiagnoseRefactor( DiagKindRefactor::sema_interface_call_with_unimplemented_call, ma, fieldRange, strType, ma.field.Val()); } // Handle generic enum var field like: TimeUnit.century. if (target.TestAttr(Attribute::ENUM_CONSTRUCTOR) && !ma.typeArguments.empty()) { // TimeUnit.century is not allowed. diag.Diagnose(ma, DiagKind::sema_invalid_type_param_of_enum_member_access, ma.field.Val(), targetOfBaseDecl.identifier.Val()); } } void TypeChecker::TypeCheckerImpl::CheckInstanceMemberAccessLegality( const ASTContext& ctx, const MemberAccess& ma, const Decl& target) { CheckForbiddenMemberAccess(ctx, ma, target); CheckLetInstanceAccessMutableFunc(ctx, ma, target); // Check for 'super.func()' calling abstract function. auto refExpr = As(ma.baseExpr.get()); bool isTargetAbstract = refExpr && refExpr->isSuper && target.TestAttr(Attribute::ABSTRACT); if (isTargetAbstract) { diag.Diagnose(ma, DiagKind::sema_abstract_method_cannot_be_accessed_directly, target.identifier.Val()); } } void TypeChecker::TypeCheckerImpl::CheckLegalityOfReference(ASTContext& ctx, Node& node) { unsigned id = Walker::GetNextWalkerID(); std::function)> postVisit = [this, &ctx](Ptr node) -> VisitAction { if (node->astKind == ASTKind::VAR_DECL) { ctx.currentCheckingNodes.pop(); } if (auto ma = DynamicCast(node); ma) { // Needs to check memberAccess's child node first, so added to post visit. CheckAccessLegalityOfMemberAccess(ctx, *ma); } return VisitAction::WALK_CHILDREN; }; std::function)> preVisit = [this, &ctx, &preVisit, &postVisit, id]( Ptr node) -> VisitAction { if (node->astKind == ASTKind::PRIMARY_CTOR_DECL) { return VisitAction::SKIP_CHILDREN; } else if (node->astKind == ASTKind::ANNOTATION) { return VisitAction::SKIP_CHILDREN; } else if (node->astKind == ASTKind::VAR_DECL) { ctx.currentCheckingNodes.push(node); } // If current node is desugared func argument, ignore the checking. if (node->astKind == ASTKind::FUNC_ARG && node->TestAttr(Attribute::HAS_INITIAL)) { return VisitAction::SKIP_CHILDREN; } if (auto expr = DynamicCast(node); expr) { if (expr->desugarExpr) { // Only check desugared nodes. Walker(expr->desugarExpr.get(), id, preVisit, postVisit).Walk(); return VisitAction::SKIP_CHILDREN; } else if (auto ref = DynamicCast(expr); ref && !ref->instTys.empty()) { CheckInstTypeCompleteness(ctx, *ref); } } if (auto re = DynamicCast(node)) { if (re->isThis) { CheckUsageOfThis(ctx, *re); } else if (re->isSuper) { CheckUsageOfSuper(ctx, *re); } else { CheckAccessLegalityOfRefExpr(ctx, *re); } } else if (auto fa = DynamicCast(node); fa && fa->withInout) { CheckMutationInStruct(ctx, *fa->expr); } else if (auto ae = DynamicCast(node)) { CheckMutationInStruct(ctx, *ae->leftValue); } else if (auto ide = DynamicCast(node)) { CheckMutationInStruct(ctx, *ide->expr); } else if (auto fd = DynamicCast(node); fd && IsInstanceConstructor(*fd)) { CheckMemberAccessInCtorParamOrCtorArg(ctx, *fd); } return VisitAction::WALK_CHILDREN; }; Walker(&node, id, preVisit, postVisit).Walk(); } std::string TypeChecker::TypeCheckerImpl::GetDiagnoseKindOfFuncDecl(const Ptr target) const { auto funcDecl = DynamicCast(target); CJC_ASSERT(funcDecl); if (target->TestAnyAttr(Attribute::CONSTRUCTOR)) { return "constructor"; } else if (target->TestAttr(AST::Attribute::ENUM_CONSTRUCTOR)) { if (funcDecl->outerDecl) { auto enumName = funcDecl->outerDecl->identifier.GetRawText(); return "enum '" + enumName + "' constructor"; } return "enum constructor"; } else if (funcDecl->op != TokenKind::ILLEGAL) { return "operator"; } return "function"; } std::optional TypeChecker::TypeCheckerImpl::GetDiagnoseKindOfFuncDecl( const Ptr usage, const Ptr target ) const { if (target->TestAnyAttr(Attribute::MACRO_FUNC)) { // deprecation of macroses checked in MACRO_EXPAND stage return std::nullopt; } auto funcDecl = DynamicCast(target); if (!funcDecl) { InternalError("decl with ASTKind::FuncDecl can not be casted to FuncDecl."); return std::nullopt; } if (target->TestAnyAttr(Attribute::ENUM_CONSTRUCTOR)) { if (usage->astKind == ASTKind::MEMBER_ACCESS) { auto ma = DynamicCast(usage); if (ma && ma->isPattern) { // enum constructor used as pattern matching // according to spec this case is not reported return std::nullopt; } } if (funcDecl->funcBody && funcDecl->funcBody->parentEnum) { std::string enumName = funcDecl->funcBody->parentEnum->identifier.GetRawText(); return "enum '" + enumName + "' constructor"; } else { InternalError("it's enum constructor with no parent class like decl:" + funcDecl->identifier.GetRawText()); } } else if (funcDecl->isSetter) { return "property setter of"; } return GetDiagnoseKindOfFuncDecl(target); } std::string TypeChecker::TypeCheckerImpl::GetDiagnoseKindOfVarDecl(const Ptr target) const { CJC_ASSERT(target->astKind == ASTKind::VAR_DECL); if (target->TestAttr(AST::Attribute::ENUM_CONSTRUCTOR)) { if (auto vd = StaticCast(target); vd) { if (vd->outerDecl) { return "enum '" + vd->outerDecl->identifier.GetRawText() + "' constructor"; } else { return "enum constructor"; } } } return "variable"; } std::optional TypeChecker::TypeCheckerImpl::GetKindfOfDeprecatedDeclaration( const Ptr target, const Ptr usage) const { switch (target->astKind) { case ASTKind::CLASS_DECL: case ASTKind::INTERFACE_DECL: case ASTKind::STRUCT_DECL: case ASTKind::ENUM_DECL: case ASTKind::PROP_DECL: case ASTKind::MACRO_DECL: case ASTKind::TYPE_ALIAS_DECL: { return DeclKindToString(*target); } case ASTKind::VAR_DECL: { return GetDiagnoseKindOfVarDecl(target); } case ASTKind::FUNC_DECL: { auto fdKind = GetDiagnoseKindOfFuncDecl(usage, target); if (fdKind.has_value()) { return fdKind.value(); } return std::nullopt; } case ASTKind::FUNC_PARAM: { if (target->TestAttr(Attribute::HAS_INITIAL)) { return "function parameter"; } break; } default: { InternalError("Unhanled @Deprecated declaration of kind " + ASTKIND_TO_STRING_MAP[target->astKind]); break; } } return std::nullopt; } bool TypeChecker::TypeCheckerImpl::ShouldSkipDeprecationDiagnostic(const Ptr target, bool strict) { if (strictDeprecatedContext && target->IsSamePackage(*strictDeprecatedContext)) { return true; } if (!strict && deprecatedContext && target->IsSamePackage(*deprecatedContext)) { return true; } return false; } void TypeChecker::TypeCheckerImpl::DiagnoseDeprecatedUsage( const Ptr usage, const Ptr target, const std::string& nameOfDiagnose ) { std::string name = nameOfDiagnose != "" ? nameOfDiagnose : target->identifier.GetRawText(); std::optional kind = GetKindfOfDeprecatedDeclaration(target, usage); if (!kind.has_value()) { return; } Ptr deprecated; for (auto& annotation : target->annotations) { if (annotation->kind == AnnotationKind::DEPRECATED) { deprecated = annotation.get(); break; } } InternalError("@Deprecated annotation not found in target for DiagnoseDeprecatedUsage" && deprecated); std::string message = ""; std::string since = "."; bool strict = false; AST::ExtractArgumentsOfDeprecatedAnno(deprecated, message, since, strict); auto range = MakeRange(usage->begin, usage->end); if (usage->astKind == ASTKind::MEMBER_ACCESS) { if (auto ma = StaticCast(usage); ma) { auto begin = ma->field.Begin(); if (!begin.IsZero()) { range.begin = begin; } auto end = ma->field.End(); if (!end.IsZero()) { range.end = end; } } } auto diagnoseKind = strict ? DiagKindRefactor::sema_deprecated_error : DiagKindRefactor::sema_deprecated_warning; if (kind.value() == "") { InternalError("Kind of deprecated declaration diagnostic must be defined."); } if (ShouldSkipDeprecationDiagnostic(target, strict)) { return; } diag.DiagnoseRefactor(diagnoseKind, *usage, range, kind.value(), name, since, message); } void TypeChecker::TypeCheckerImpl::CheckUsageOfDeprecatedParameters( const Ptr usage ) { auto ce = DynamicCast(usage); if (!ce) { return; } auto fd = ce->resolvedFunction; if (!fd) { return; } if (!fd->funcBody || fd->funcBody->paramLists.empty()) { return; } if (!ce->desugarArgs.has_value()) { return; } for (size_t i = 0; i < fd->funcBody->paramLists.size(); i++) { auto& params = fd->funcBody->paramLists[i]->params; auto& args = ce->desugarArgs.value(); if (params.size() != args.size()) { continue; } for (size_t j = 0; j < args.size(); j++) { auto& param = params[j]; auto& arg = args[j]; if (param->HasAnno(AnnotationKind::DEPRECATED) && !arg->TestAnyAttr(Attribute::COMPILER_ADD)) { DiagnoseDeprecatedUsage(arg, param); } } } } void TypeChecker::TypeCheckerImpl::CheckUsageOfDeprecatedNominative( const Ptr usage, const Ptr target ) { if (auto constructor = DynamicCast(target); constructor) { auto funcBody = constructor->funcBody.get(); if (funcBody) { Ptr parentDecl; if (auto pc = funcBody->parentClassLike; pc != nullptr) { parentDecl = pc; } else if (auto ps = funcBody->parentStruct; ps != nullptr) { parentDecl = ps; } else if (auto pe = funcBody->parentEnum; pe != nullptr) { parentDecl = pe; } if (parentDecl != nullptr && parentDecl->HasAnno(AnnotationKind::DEPRECATED)) { DiagnoseDeprecatedUsage(usage, parentDecl); } else if (auto refExpr = DynamicCast(usage); refExpr != nullptr && refExpr->aliasTarget != nullptr && refExpr->aliasTarget->HasAnno(AnnotationKind::DEPRECATED)) { // unlike the previous case, it's the typealias is deprecated // so we are pointing to it instead DiagnoseDeprecatedUsage(usage, refExpr->aliasTarget); } } } if (target->TestAttr(AST::Attribute::ENUM_CONSTRUCTOR)) { auto vd = DynamicCast(target); if (!vd) { return; } if (!vd->outerDecl) { return; } auto pe = vd->outerDecl; if (!pe || !pe->HasAnno(AnnotationKind::DEPRECATED)) { return; } DiagnoseDeprecatedUsage(usage, pe); } } void TypeChecker::TypeCheckerImpl::CheckOverridingOrRedefiningOfDeprecated( const Ptr overridden, const Ptr overriding, const std::string& declType ) { if (!overridden || !overridden->HasAnno(AnnotationKind::DEPRECATED)) { return; } auto isOverridenStrict = IsDeprecatedStrict(overridden); if (overriding->HasAnno(AnnotationKind::DEPRECATED)) { if (isOverridenStrict && !IsDeprecatedStrict(overriding)) { auto diagnoseKind = DiagKindRefactor::sema_deprecation_weakening; diag.DiagnoseRefactor(diagnoseKind, *overriding); } } else { bool isRedefined = overridden->TestAnyAttr(Attribute::STATIC); DiagKindRefactor diagnoseKind; if (isRedefined) { diagnoseKind = isOverridenStrict ? DiagKindRefactor::sema_deprecation_redef_error : DiagKindRefactor::sema_deprecation_redef_warning; } else { diagnoseKind = isOverridenStrict ? DiagKindRefactor::sema_deprecation_override_error : DiagKindRefactor::sema_deprecation_override_warning; } auto name = declType == "setter" ? "set" : overriding->identifier.GetRawText(); diag.DiagnoseRefactor( diagnoseKind, *overriding, declType, name); } } void TypeChecker::TypeCheckerImpl::CheckOverridingOrRedefinitionOfDeprecatedFunction( const Ptr cd, const Ptr member ) { if (!Is(member) || member->TestAttr(Attribute::GENERIC)) { return; } auto funcOverride = StaticCast(member.get()); for (auto& inheritedType : cd->inheritedTypes) { auto inheritedDecl = Ty::GetDeclPtrOfTy(inheritedType->ty); if (!inheritedDecl) { continue; } for (auto& memberDecl : inheritedDecl->GetMemberDecls()) { if (auto baseFuncDecl = As(memberDecl); baseFuncDecl) { if (Is(baseFuncDecl->ty) && Is(funcOverride->ty) && typeManager.IsFuncDeclSubType(*funcOverride, *baseFuncDecl) ) { CheckOverridingOrRedefiningOfDeprecated( baseFuncDecl, funcOverride, "function"); } } } } } void TypeChecker::TypeCheckerImpl::CheckOverridingOrRedefinitionOfDeprecatedProperty( const Ptr cd, const Ptr member ) { auto propOverride = DynamicCast(member.get()); if (!propOverride) { return; } // Checking overriding/redefinition of deprecated property auto getterOverride = GetUsableGetterForProperty(*propOverride); auto setterOverride = propOverride->isVar ? GetUsableSetterForProperty(*propOverride) : nullptr; for (auto& inheritedType : cd->inheritedTypes) { auto inheritedDecl = Ty::GetDeclPtrOfTy(inheritedType->ty); if (!inheritedDecl) { continue; } for (auto& memberDecl : inheritedDecl->GetMemberDecls()) { if (auto basePropDecl = As(memberDecl); basePropDecl) { if (basePropDecl->TestAttr(Attribute::GENERIC)) { return; } auto propGetter = GetUsableGetterForProperty(*basePropDecl); if (propGetter && getterOverride && Is(getterOverride->ty) && Is(propGetter->ty) && typeManager.IsFuncDeclSubType(*getterOverride, *propGetter)) { CheckOverridingOrRedefiningOfDeprecated( basePropDecl, propOverride, "property"); } auto propSetter = basePropDecl->isVar ? GetUsableSetterForProperty(*basePropDecl) : nullptr; if (propSetter && setterOverride && Is(setterOverride->ty) && Is(propSetter->ty) && typeManager.IsFuncDeclSubType(*setterOverride, *propSetter) ) { CheckOverridingOrRedefiningOfDeprecated( propSetter, setterOverride, "setter"); } } } } } void TypeChecker::TypeCheckerImpl::CheckOverridingOrRedefinitionOfDeprecated( const Ptr cd ) { for (auto& member : cd->body->decls) { CheckOverridingOrRedefinitionOfDeprecatedFunction(cd, member); CheckOverridingOrRedefinitionOfDeprecatedProperty(cd, member); } } void TypeChecker::TypeCheckerImpl::CheckUsageOfDeprecatedSetter( const Ptr usage ) { auto ae = DynamicCast(usage); if (!ae || !ae->leftValue) { return; } if (auto leftPartTarget = ae->leftValue->GetTarget(); leftPartTarget) { if (leftPartTarget->astKind != ASTKind::PROP_DECL) { return; } auto pd = StaticCast(leftPartTarget); if (!pd || !pd->isVar) { return; } auto setter = GetUsableSetterForProperty(*pd); if (setter && setter->HasAnno(AnnotationKind::DEPRECATED)) { DiagnoseDeprecatedUsage( ae->leftValue, setter, pd->identifier.GetRawText() ); } } } void TypeChecker::TypeCheckerImpl::CheckDeprecationLevelOnInheritors( const Ptr classLike ) { auto isParentStrict = IsDeprecatedStrict(classLike); OrderedDeclSet orderedSubDecl; orderedSubDecl.insert(classLike->subDecls.begin(), classLike->subDecls.end()); for (auto sub : orderedSubDecl) { if (sub->HasAnno(AnnotationKind::DEPRECATED)) { if (isParentStrict && !IsDeprecatedStrict(sub)) { auto diagnoseKind = DiagKindRefactor::sema_deprecation_weakening; diag.DiagnoseRefactor(diagnoseKind, *sub); } } else { auto diagnoseKind = isParentStrict ? DiagKindRefactor::sema_deprecation_override_error : DiagKindRefactor::sema_deprecation_override_warning; diag.DiagnoseRefactor( diagnoseKind, *sub, DeclKindToString(*sub), sub->identifier.GetRawText() ); } } } void TypeChecker::TypeCheckerImpl::CheckUsageOfDeprecatedWithTarget( const Ptr usage, const Ptr target ) { if (target->HasAnno(AnnotationKind::DEPRECATED)) { if (target->astKind == ASTKind::FUNC_PARAM) { // They are checked in CheckUsageOfDeprecatedParameters // Here skipping due to usage of function paremeters // inside block must not be diagnosed return; } if (target->astKind == ASTKind::VAR_DECL && usage->TestAnyAttr(Attribute::COMPILER_ADD)) { // Skip compiler generated assignment of member // desugared from let/var param of function return; } DiagnoseDeprecatedUsage(usage, target); return; } if (IsClassOrEnumConstructor(*target)) { if (usage->astKind == ASTKind::MEMBER_ACCESS) { // qualified enum constructor `En.EnumConstr` // e.g. skip reporting `EnumConstr` because diasgnostic for `En` is enough return; } if (!usage->TestAnyAttr(Attribute::COMPILER_ADD)) { CheckUsageOfDeprecatedNominative(usage, target); } } if (auto alias = As(target); alias) { auto aliased = Ty::GetDeclOfTy(alias->type->ty); if (!aliased) { return; } if (aliased->HasAnno(AnnotationKind::DEPRECATED)) { DiagnoseDeprecatedUsage(usage, aliased); } } } void TypeChecker::TypeCheckerImpl::CheckUsageOfDeprecated( Node& node ) { auto enterVisitor = [this](const Ptr usage) { if (usage->astKind == ASTKind::ENUM_PATTERN) { return VisitAction::SKIP_CHILDREN; } if (usage->IsDecl()) { auto decl = StaticCast(usage); if (decl && decl->HasAnno(AnnotationKind::DEPRECATED)) { if (IsDeprecatedStrict(decl) && !strictDeprecatedContext) { strictDeprecatedContext = usage; } else if (!deprecatedContext) { deprecatedContext = usage; } } if (auto cd = As(usage); cd && cd->body) { CheckOverridingOrRedefinitionOfDeprecated(cd); } if (decl && decl->HasAnno(AnnotationKind::DEPRECATED)) { if (auto cl = DynamicCast(decl); cl) { CheckDeprecationLevelOnInheritors(cl); } } return VisitAction::WALK_CHILDREN; } if (usage->astKind == ASTKind::CALL_EXPR) { CheckUsageOfDeprecatedParameters(usage); } else if (usage->astKind == ASTKind::ASSIGN_EXPR) { CheckUsageOfDeprecatedSetter(usage); } if (auto target = usage->GetTarget(); target && target != usage) { CheckUsageOfDeprecatedWithTarget(usage, target); } return VisitAction::WALK_CHILDREN; }; auto exitVisitor = [this](const Ptr usage) { if (deprecatedContext == usage) { deprecatedContext = nullptr; } if (strictDeprecatedContext == usage) { strictDeprecatedContext = nullptr; } return VisitAction::KEEP_DECISION; }; Walker(&node, enterVisitor, exitVisitor).Walk(); } cangjie_compiler-1.0.7/src/Sema/TypeCheckType.cpp000066400000000000000000000316031510705540100217130ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the TypeChecker related classes. */ #include "TypeCheckerImpl.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/Print.h" using namespace Cangjie; using namespace TypeCheckUtil; using namespace AST; void TypeChecker::TypeCheckerImpl::CheckReferenceTypeLegality(ASTContext& ctx, Type& t) { if (t.TestAttr(Attribute::TOOL_ADD)) { return; } auto typeArgs = t.GetTypeArgs(); for (auto& it : typeArgs) { CheckReferenceTypeLegality(ctx, *it); } switch (t.astKind) { case ASTKind::REF_TYPE: CheckRefType(ctx, *StaticAs(&t)); break; case ASTKind::QUALIFIED_TYPE: CheckQualifiedType(ctx, *StaticAs(&t)); break; case ASTKind::TUPLE_TYPE: CheckTupleType(ctx, *StaticAs(&t)); break; case ASTKind::FUNC_TYPE: CheckFuncType(ctx, *StaticAs(&t)); break; case ASTKind::PAREN_TYPE: CheckReferenceTypeLegality(ctx, *StaticAs(&t)->type); break; case ASTKind::OPTION_TYPE: CheckOptionType(ctx, *StaticAs(&t)); break; case ASTKind::VARRAY_TYPE: CheckVArrayType(ctx, *StaticAs(&t)); break; default: break; } // Check all related type's sema ty, udpate current node's ty to invalid, if any of them invalid. bool hasInvalidTy = !Ty::IsTyCorrect(t.ty) || std::any_of(typeArgs.begin(), typeArgs.end(), [](auto& it) { return !Ty::IsTyCorrect(it->ty); }); if (hasInvalidTy) { t.ty = TypeManager::GetInvalidTy(); } } void TypeChecker::TypeCheckerImpl::CheckOptionType(ASTContext& ctx, const OptionType& ot) { if (ot.desugarType) { CheckReferenceTypeLegality(ctx, *ot.desugarType); } } std::tuple TypeChecker::TypeCheckerImpl::CheckVArrayWithRefType( Ty& ty, std::unordered_set>& traversedTy) { if (ty.IsStructArray() || ty.IsClassLike() || ty.IsArray() || ty.IsEnum() || ty.IsGeneric() || (ty.IsFunc() && !ty.IsCFunc())) { return {true, Ty::ToString(&ty)}; } if (std::find(traversedTy.begin(), traversedTy.end(), Ptr(&ty)) != traversedTy.end()) { return {false, ""}; } (void)traversedTy.emplace(&ty); if (auto sd = DynamicCast(Ty::GetDeclPtrOfTy(&ty))) { auto typeMapping = promotion.GetPromoteTypeMapping(ty, *sd->ty); for (auto& decl : sd->GetMemberDecls()) { if (decl->TestAttr(Attribute::STATIC) || decl->astKind != ASTKind::VAR_DECL) { continue; } auto memberTy = typeManager.GetBestInstantiatedTy(decl->ty, typeMapping); auto [needReport, reportType] = CheckVArrayWithRefType(*memberTy, traversedTy); if (needReport) { return {needReport, reportType}; } } } else if (ty.IsTuple()) { for (auto typeArg : ty.typeArgs) { auto [needReport, reportType] = CheckVArrayWithRefType(*typeArg, traversedTy); if (needReport) { return {needReport, reportType}; } } } // CPointer and CString are always allowed. return {false, ""}; } void TypeChecker::TypeCheckerImpl::CheckVArrayType(ASTContext& ctx, const VArrayType& vt) { Synthesize(ctx, vt.typeArgument.get()); if (auto ct = DynamicCast(vt.constantType.get()); ct) { ct->ty = Synthesize(ctx, ct->constantExpr.get()); } // The runtime gc cannot manage data of type llvm::array, so it cannot use a reference type as its element type, // which is temporarily prohibited by semantics. auto typeArgTy = vt.typeArgument->ty; if (!Ty::IsTyCorrect(typeArgTy)) { return; } std::unordered_set> traversedTy = {}; auto [isReferenceType, type] = CheckVArrayWithRefType(*typeArgTy, traversedTy); if (isReferenceType) { auto builder = diag.DiagnoseRefactor( DiagKindRefactor::sema_varray_arg_type_with_reftype, *vt.typeArgument, Ty::ToString(typeArgTy)); builder.AddMainHintArguments(type); } } bool TypeChecker::TypeCheckerImpl::IsGenericTypeWithTypeArgs(AST::Type& type) const { if (auto rt = DynamicCast(&type); rt && rt->IsGenericThisType()) { return true; } auto target = type.GetTarget(); if (!target) { return true; // If current type do not have target, return true for ignore current check. } auto generic = target->GetGeneric(); // Type must have typeArguments when its target is a generic type declaration. return !(type.GetTypeArgs().empty() && generic != nullptr); } void TypeChecker::TypeCheckerImpl::HandleAliasForRefType(RefType& rt, Ptr& target) { if (!target || target->astKind != ASTKind::TYPE_ALIAS_DECL) { return; } auto aliasDecl = StaticCast(target); if (aliasDecl->type == nullptr) { return; } auto innerTypeAliasTarget = GetLastTypeAliasTarget(*aliasDecl); if (auto realTarget = innerTypeAliasTarget->type->GetTarget(); realTarget) { target = realTarget; auto typeMapping = GenerateTypeMappingForTypeAliasUse(*aliasDecl, rt); SubstituteTypeArguments(*innerTypeAliasTarget, rt.typeArguments, typeMapping); } } namespace { void BackupTypes(std::vector>& src, std::vector>& backup) { for (auto& it : src) { backup.push_back(std::move(it)); } src.clear(); for (auto& it : backup) { src.push_back(AST::ASTCloner::Clone(it.get())); src.back()->DisableAttr(Attribute::COMPILER_ADD); // to avoid being skipped in SubstituteTypeArguments } } void RestoreTypes(std::vector>& src, std::vector>& backup) { src.clear(); for (auto& it : backup) { src.push_back(std::move(it)); } backup.clear(); } } void TypeChecker::TypeCheckerImpl::CheckRefTypeWithRealTarget(RefType& rt) { std::vector> backup; BackupTypes(rt.typeArguments, backup); auto target = rt.ref.target; auto realTarget = target; HandleAliasForRefType(rt, realTarget); auto typeArgs = rt.GetTypeArgs(); if (!realTarget || typeArgs.empty()) { RestoreTypes(rt.typeArguments, backup); return; } if (auto aliasTarget = DynamicCast(target)) { std::vector> diffs = GetUnusedTysInTypeAlias(*aliasTarget); Utils::EraseIf(typeArgs, [&diffs](auto type) { return Utils::In(type->ty, diffs); }); } if (!CheckGenericDeclInstantiation(realTarget, typeArgs, rt)) { // Do not clear type target when constraints mismatched. // Guarantees constraint error can be thrown when re-checking. rt.ty = TypeManager::GetInvalidTy(); } RestoreTypes(rt.typeArguments, backup); } void TypeChecker::TypeCheckerImpl::CheckRefType(ASTContext& ctx, RefType& rt) { auto target = rt.ref.target; // If no target found in name resolution stage, return directly. if (!target) { return; // 'PreCheck' will not set non typeDecl to target, eg: refType may be name of package for pkg.Type. } // Get field target and check access legality. if (!CheckRefTypeCheckAccessLegality(ctx, rt, *target)) { return; } if (auto ref = DynamicCast(&*target); ref && ref->type == BuiltInType::CFUNC) { CheckCFuncType(ctx, rt); return; } // Type must have typeArguments when its target is a generic type declaration. if (!IsGenericTypeWithTypeArgs(rt)) { diag.Diagnose(rt, DiagKind::sema_generic_type_without_type_argument); // Unbind target-user relationship when setting type to invalid. rt.ty = TypeManager::GetInvalidTy(); return; } // Returns true if further checks can be omitted. if (CheckRefExprCheckTyArgs(rt, *target)) { return; } CheckRefTypeWithRealTarget(rt); } void TypeChecker::TypeCheckerImpl::CheckCFuncType(ASTContext& ctx, const RefType& rt) { (void)ctx; auto args = rt.GetTypeArgs(); // do not check invalid CFunc type. if (args.size() != 1) { return; } auto arg = DynamicCast(args[0]); if (!arg) { return; } for (auto& it : arg->paramTypes) { CheckCFuncParamType(*it); } if (!Ty::IsTyCorrect(arg->retType->ty)) { return; } CheckCFuncReturnType(*arg->retType); } bool TypeChecker::TypeCheckerImpl::CheckRefExprCheckTyArgs(const RefType& rt, const Decl& target) { bool skipFurther = false; if (rt.typeArguments.empty()) { skipFurther = true; } for (auto& type : rt.typeArguments) { if (!HasJavaAttr(target)) { continue; } if (auto prt = DynamicCast(type.get()); prt && prt->ref.target) { auto tg = TypeCheckUtil::GetRealTarget(prt->ref.target); if (tg && (HasJavaAttr(*tg) || tg->astKind == ASTKind::GENERIC_PARAM_DECL)) { continue; } } else if (auto qt = DynamicCast(type.get()); qt && qt->target) { auto tgt = TypeCheckUtil::GetRealTarget(qt->target); if (tgt && HasJavaAttr(*tgt)) { continue; } } skipFurther = true; break; } return skipFurther; } bool TypeChecker::TypeCheckerImpl::CheckRefTypeCheckAccessLegality( const ASTContext& ctx, RefType& rt, const Decl& target) { auto sym = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, rt.scopeName); if (!IsLegalAccess(sym, target, rt)) { diag.Diagnose(rt, DiagKind::sema_invalid_access_control, target.identifier.Val()); // Unbind target-user relationship when error happens. ReplaceTarget(&rt, nullptr); rt.ty = TypeManager::GetInvalidTy(); return false; } return true; } void TypeChecker::TypeCheckerImpl::CheckTupleType(ASTContext& ctx, TupleType& tt) { for (auto& it : tt.fieldTypes) { CJC_NULLPTR_CHECK(it); Synthesize(ctx, it.get()); if (it->ty && Ty::IsCTypeConstraint(*it->ty)) { diag.Diagnose(*it, DiagKind::sema_invalid_tuple_field_ctype); return; } } } void TypeChecker::TypeCheckerImpl::CheckFuncType(ASTContext& ctx, FuncType& ft) { for (auto& it : ft.paramTypes) { CheckReferenceTypeLegality(ctx, *it); } CJC_NULLPTR_CHECK(ft.retType); CheckReferenceTypeLegality(ctx, *ft.retType); if (!ft.isC) { return; } for (auto& it : ft.paramTypes) { CheckCFuncParamType(*it); } if (!Ty::IsTyCorrect(ft.retType->ty)) { return; } CheckCFuncReturnType(*ft.retType); } void TypeChecker::TypeCheckerImpl::CheckCFuncReturnType(const Type& type) { if (!Ty::IsMetCType(*type.ty)) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_invalid_cfunc_return_type, type); builder.AddNote("return type is " + type.ty->String()); } else if (Is(type.ty)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_varray_in_cfunc, type); } } void TypeChecker::TypeCheckerImpl::CheckQualifiedType(const ASTContext& ctx, QualifiedType& qt) { auto target = qt.target; // If no target found in name resolution stage, return directly. if (target == nullptr) { // 'PreCheck' will not set non typeDecl to target // eg: refType may be name of package for pkg.Type or pkga.pkgb.Type. return; } // Get field target and check access legality. auto sym = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, qt.scopeName); if (!IsLegalAccess(sym, *target, qt)) { diag.Diagnose(qt, DiagKind::sema_invalid_access_control, target->identifier.Val()); } // Type must have typeArguments when its target is a generic type declaration. if (!IsGenericTypeWithTypeArgs(qt)) { diag.Diagnose(qt, DiagKind::sema_generic_type_without_type_argument); // Unbind target-user relationship when setting type to invalid. ReplaceTarget(&qt, nullptr); qt.ty = TypeManager::GetInvalidTy(); return; } if (!Ty::IsTyCorrect(qt.ty) || qt.typeArguments.empty()) { return; } if (!CheckGenericDeclInstantiation(target, qt.GetTypeArgs(), qt)) { // Do not clear type target when constraints mismatched. // Guarantees constraint error can be thrown when re-checking. qt.ty = TypeManager::GetInvalidTy(); } } cangjie_compiler-1.0.7/src/Sema/TypeCheckUtil.cpp000066400000000000000000001212711510705540100217100ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the utility functions for TypeCheck. */ #include "TypeCheckUtil.h" #include "Promotion.h" #include #include #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/ScopeManagerApi.h" #include "cangjie/AST/Symbol.h" #include "cangjie/AST/Utils.h" #include "cangjie/AST/Walker.h" namespace Cangjie::TypeCheckUtil { using namespace AST; namespace { const std::set BUILTIN_OPERATORS = {"@", ".", "[]", "()", "++", "--", "?", "!", "-", "**", "*", "/", "%", "+", "<<", ">>", "<", "<=", ">", ">=", "is", "as", "==", "!=", "&", "^", "|", "..", "..=", "&&", "||", "??", "~>", "=", "**=", "*=", "/=", "%/", "+=", "-=", "<<=", ">>=", "&=", "^=", "|="}; } // namespace std::vector GetIdealTypesByKind(TypeKind type) { if (type == TypeKind::TYPE_IDEAL_INT) { return {TypeKind::TYPE_INT8, TypeKind::TYPE_INT16, TypeKind::TYPE_INT32, TypeKind::TYPE_INT_NATIVE, TypeKind::TYPE_INT64, TypeKind::TYPE_UINT8, TypeKind::TYPE_UINT16, TypeKind::TYPE_UINT32, TypeKind::TYPE_UINT64, TypeKind::TYPE_UINT_NATIVE}; } else if (type == TypeKind::TYPE_IDEAL_FLOAT) { return {TypeKind::TYPE_FLOAT16, TypeKind::TYPE_FLOAT32, TypeKind::TYPE_FLOAT64}; } return {}; } void UpdateInstTysWithTypeArgs(NameReferenceExpr& expr) { if (!expr.instTys.empty()) { return; } auto typeArgs = expr.GetTypeArgs(); // Do not update instTys for partial generic typealias case which has intersection type. if (HasIntersectionTy(typeArgs)) { return; } for (auto& typeArg : typeArgs) { (void)expr.instTys.emplace_back(typeArg->ty); } } void SetIsNotAlone(Expr& baseExpr) { if (auto nre = DynamicCast(&baseExpr); nre) { nre->isAlone = false; } } bool HasIntersectionTy(const std::vector>& types) { return std::any_of( types.begin(), types.end(), [](auto& type) { return type->ty && type->ty->HasIntersectionTy(); }); } bool NeedFurtherInstantiation(const std::vector>& types) { return types.empty() || HasIntersectionTy(types); } void ModifyTargetOfRef(RefExpr& re, Ptr decl, const std::vector>& targets) { ReplaceTarget(&re, decl); // If the target of refExpr is FuncDecl, it should not be the real target of RefExpr. // The real target will be determined by resolvedFunction in the typecheck of CallExpr. re.ref.targets.clear(); for (auto& it : targets) { re.ref.targets.push_back(it); } } void AddFuncTargetsForMemberAccess(MemberAccess& ma, const std::vector>& targets) { ma.targets.clear(); for (auto& decl : targets) { if (!decl || (!ma.isPattern && decl->astKind != ASTKind::FUNC_DECL)) { continue; } auto funcDecl = RawStaticCast(decl); ma.targets.push_back(funcDecl); } } void ReplaceTarget(Ptr node, Ptr target, bool insertTarget) { if (target == nullptr && (!node->ty || node->ty->IsNothing())) { node->ty = TypeManager::GetInvalidTy(); } auto aliasDecl = As(target); switch (node->astKind) { case ASTKind::REF_EXPR: { auto re = StaticAs(node); re->ref.target = target; // Update type alias decl or clear the target. if (aliasDecl || !target) { re->aliasTarget = aliasDecl; } break; } case ASTKind::MEMBER_ACCESS: { auto ma = StaticAs(node); ma->target = target; // Update type alias decl or clear the target. if (aliasDecl || !target) { ma->aliasTarget = aliasDecl; } break; } case ASTKind::REF_TYPE: { auto rt = StaticAs(node); rt->ref.target = target; break; } case ASTKind::QUALIFIED_TYPE: { auto qt = StaticAs(node); qt->target = target; break; } case ASTKind::MACRO_EXPAND_EXPR: { auto mee = RawStaticCast(node); mee->invocation.target = target; break; } case ASTKind::MACRO_EXPAND_DECL: { auto med = RawStaticCast(node); med->invocation.target = target; break; } case ASTKind::MACRO_EXPAND_PARAM: { auto mep = RawStaticCast(node); mep->invocation.target = target; break; } default: break; } if (node->symbol) { node->symbol->UnbindTarget(); if (insertTarget && node->begin.fileID != 0) { node->symbol->target = target; } } } bool IsFuncReturnThisType(const FuncDecl& fd) { return fd.funcBody && fd.funcBody->retType && fd.funcBody->retType->astKind == ASTKind::REF_TYPE && StaticAs(fd.funcBody->retType.get())->ref.identifier == "This"; } bool CheckThisTypeCompatibility(const FuncDecl& parentFunc, const FuncDecl& childFunc) { // In Class, when a function in child class has overridden relation with the function in parent class, // 1. If the return type of parent function is 'This', the return type of the child function must be 'This'; // 2. If the return type of parent function is not 'This', the return type of the child function can be // any other type which is the subtype of the return type of parent function. return !IsFuncReturnThisType(parentFunc) || IsFuncReturnThisType(childFunc); } bool HasMainDecl(Package& pkg) { bool hasMain = false; Walker(&pkg, [&hasMain](auto node) { if (auto decl = DynamicCast(node); decl) { if (decl->astKind == ASTKind::MAIN_DECL) { hasMain = true; return VisitAction::STOP_NOW; } return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }).Walk(); return hasMain; } void MarkParamWithInitialValue(Node& root) { auto setFunc = [](Ptr node) -> VisitAction { if (auto fp = DynamicCast(node); fp && fp->assignment) { node->EnableAttr(Attribute::HAS_INITIAL); // Set initial mark to param which has initial value. } return VisitAction::WALK_CHILDREN; }; Walker(&root, setFunc).Walk(); } bool IsOverloadableOperator(TokenKind op) { static const std::unordered_set overloadableOperators = { TokenKind::ADD, TokenKind::BITAND, TokenKind::BITOR, TokenKind::BITXOR, TokenKind::DIV, TokenKind::EQUAL, TokenKind::EXP, TokenKind::GE, TokenKind::GT, TokenKind::LE, TokenKind::LSHIFT, TokenKind::LSQUARE, TokenKind::LT, TokenKind::MOD, TokenKind::MUL, TokenKind::NOT, TokenKind::NOTEQ, TokenKind::RSHIFT, TokenKind::SUB, }; return overloadableOperators.find(op) != overloadableOperators.end(); } bool CanSkipDiag(const Node& node) { return !Ty::IsTyCorrect(node.ty); } bool IsFieldOperator(const std::string& field) { return Utils::In(field, BUILTIN_OPERATORS); } std::vector> GetParamTys(const FuncDecl& fd) { if (fd.TestAttr(Attribute::IMPORTED) && Ty::IsTyCorrect(fd.ty) && fd.ty->IsFunc()) { return RawStaticCast(fd.ty)->paramTys; } CJC_NULLPTR_CHECK(fd.funcBody); return GetFuncBodyParamTys(*fd.funcBody); } std::vector> GetFuncBodyParamTys(const FuncBody& fb) { if (fb.paramLists.empty()) { return {}; } std::vector> ret; for (auto& param : fb.paramLists[0].get()->params) { if (param->type) { param->ty = param->type->ty; } ret.emplace_back(param->ty ? param->ty : TypeManager::GetInvalidTy()); } return ret; } // Generate type mapping for src is an override or implement of target. MultiTypeSubst GenerateTypeMappingBetweenFuncs(TypeManager& typeManager, const FuncDecl& src, const FuncDecl& target) { MultiTypeSubst typeMapping; if (src.outerDecl && Ty::IsTyCorrect(src.outerDecl->ty)) { typeMapping = typeManager.GenerateStructDeclTypeMapping(*src.outerDecl); } if (target.TestAttr(Attribute::GENERIC) && src.TestAttr(Attribute::GENERIC)) { // Solve generic function (eg:`func foo(arr: A): Unit`) 's type identical check. MergeTypeSubstToMultiTypeSubst(typeMapping, typeManager.GenerateGenericMappingFromGeneric(target, src)); } return typeMapping; } // Check if src is an override or implement of target. DO NOT call 'Synthesize'. bool IsOverrideOrShadow(TypeManager& typeManager, const FuncDecl& src, const FuncDecl& target, const Ptr baseTy, const Ptr expectInstParent) { if (auto ret = typeManager.GetOverrideCache(&src, &target, baseTy, expectInstParent); ret.has_value()) { return ret.value(); } auto srcFt = DynamicCast(src.ty); auto targetFt = DynamicCast(target.ty); MultiTypeSubst mts; if (expectInstParent) { CJC_ASSERT(src.outerDecl && Is(src.outerDecl)); MergeTypeSubstToMultiTypeSubst(mts, GenerateTypeMappingByTy(target.outerDecl->ty, expectInstParent)); auto substituteToParent = Promotion(typeManager).Promote(*src.outerDecl->ty, *target.outerDecl->ty); for (auto p : substituteToParent) { MergeTypeSubstToMultiTypeSubst(mts, GenerateTypeMappingByTy(p, expectInstParent)); } if (target.TestAttr(Attribute::GENERIC) && src.TestAttr(Attribute::GENERIC)) { MergeTypeSubstToMultiTypeSubst(mts, typeManager.GenerateGenericMappingFromGeneric(target, src)); } } else { mts = GenerateTypeMappingBetweenFuncs(typeManager, src, target); } std::set typeMappings = ExpandMultiTypeSubst(mts, {srcFt, targetFt}); for (auto typeMapping : typeMappings) { auto srcParamTys = srcFt ? srcFt->paramTys : GetParamTys(src); auto targetParamTys = targetFt ? targetFt->paramTys : GetParamTys(target); if (srcParamTys.size() != targetParamTys.size()) { typeManager.AddOverrideCache(src, target, baseTy, expectInstParent, false); return false; } // Only generate typeMapping by base type, if functions' outerDecls are irrelevant. // eg: interface I1 { func foo(): Int64 }, interface I2 { func foo(): Int64 } // class/interface Type3 <: I1 & I2 { func foo() : Int64 {0}} if (Ty::IsTyCorrect(baseTy) && Is(src.outerDecl) && Is(target.outerDecl)) { auto parentStructTy = typeManager.GetInstantiatedTy(target.outerDecl->ty, typeMapping); auto childStructTy = typeManager.GetInstantiatedTy(src.outerDecl->ty, typeMapping); if (!typeManager.IsSubtype(childStructTy, parentStructTy)) { MultiTypeSubst m; typeManager.GenerateGenericMapping(m, *baseTy); typeMapping.merge(MultiTypeSubstToTypeSubst(m)); } } // Instantiated parameter's types. for (auto& it : srcParamTys) { it = typeManager.GetInstantiatedTy(it, typeMapping); } for (auto& it : targetParamTys) { it = typeManager.GetInstantiatedTy(it, typeMapping); } if (typeManager.IsFuncParameterTypesIdentical(srcParamTys, targetParamTys)) { bool isCrossPlatform = (src.TestAttr(AST::Attribute::COMMON) && target.TestAttr(AST::Attribute::PLATFORM)) || (src.TestAttr(AST::Attribute::PLATFORM) && target.TestAttr(AST::Attribute::COMMON)); if (isCrossPlatform) { continue; } typeManager.AddOverrideCache(src, target, baseTy, expectInstParent, true); return true; } } typeManager.AddOverrideCache(src, target, baseTy, expectInstParent, false); return false; } // Check if src is an override or implement of target. DO NOT call 'Synthesize'. bool IsOverrideOrShadow(TypeManager& typeManager, const PropDecl& src, const PropDecl& target, Ptr baseTy) { CJC_ASSERT(src.outerDecl); MultiTypeSubst mts = typeManager.GenerateStructDeclTypeMapping(*src.outerDecl); auto typeMapping = MultiTypeSubstToTypeSubst(mts); // Only generate typeMapping by base type, if functions' outerDecls are irrelevant. // eg: interface I1 { func foo() : Int64 }, interface I2 { func foo() : Int64} // class/interface Type3 <: I1&I2 { func foo() : Int64 {0}} if (Ty::IsTyCorrect(baseTy) && Is(src.outerDecl) && Is(target.outerDecl)) { auto parentStructTy = typeManager.GetInstantiatedTy(target.outerDecl->ty, typeMapping); auto childStructTy = typeManager.GetInstantiatedTy(src.outerDecl->ty, typeMapping); if (!typeManager.IsSubtype(childStructTy, parentStructTy)) { typeManager.GenerateGenericMapping(mts, *baseTy); typeMapping.merge(MultiTypeSubstToTypeSubst(mts)); } } auto srcTy = src.type ? src.type->ty : src.ty; auto targetTy = target.type ? target.type->ty : target.ty; bool ret = srcTy == typeManager.GetInstantiatedTy(targetTy, typeMapping); if (ret && src.outerDecl == Ty::GetDeclPtrOfTy(baseTy)) { typeManager.UpdateTopOverriddenFuncDeclCache(&src, &target); } return ret; } // Check where expr is memberAccess calling interface's member. bool IsGenericUpperBoundCall(const Expr& expr, Decl& target) { auto ma = DynamicCast(&expr); bool isNotGenericCall = !ma || !ma->baseExpr || !ma->baseExpr->ty || !ma->baseExpr->ty->IsGeneric() || !target.outerDecl; if (isNotGenericCall) { return false; } auto found = ma->foundUpperBoundMap.find(&target); return found != ma->foundUpperBoundMap.end() && !found->second.empty(); } bool IsNode1ScopeVisibleForNode2(const Node& node1, const Node& node2) { auto scopeName1 = ScopeManagerApi::GetScopeNameWithoutTail(node1.scopeName); auto scopeName2 = ScopeManagerApi::GetScopeNameWithoutTail(node2.scopeName); bool isScopeVisible = scopeName2.rfind(scopeName1, 0) == 0; return isScopeVisible; } Ptr GetRealTarget(Ptr decl) { auto target = decl; if (auto aliasDecl = DynamicCast(target); aliasDecl && !target->TestAttr(Attribute::IN_REFERENCE_CYCLE) && aliasDecl->type) { auto realTarget = aliasDecl->type->GetTarget(); // It is possible that existing empty realTarget, eg: typealias of primitive type. // And it's also possible to existing typealias of another aliasdecl. target = realTarget ? GetRealTarget(realTarget) : target; } return target; } std::pair> GetRealMemberDecl(Decl& decl) { if (auto fd = DynamicCast(&decl); fd && fd->propDecl) { return {fd->isGetter, fd->propDecl}; } return {false, &decl}; } Ptr GetUsedMemberDecl(Decl& decl, bool isGetter) { if (auto pd = DynamicCast(&decl); pd) { // If target is prop decl, return getter/setter func. auto& funcs = isGetter ? pd->getters : pd->setters; // Spec allows only implement prop's getter or setter // for the interface property which have default implementation. return funcs.empty() ? RawStaticCast(pd) : funcs[0].get(); } return &decl; } static const std::unordered_map DECL2STRMAP = { {ASTKind::CLASS_DECL, "class"}, {ASTKind::ENUM_DECL, "enum"}, {ASTKind::EXTEND_DECL, "extend"}, {ASTKind::FUNC_DECL, "function"}, {ASTKind::FUNC_PARAM, "parameter"}, {ASTKind::INTERFACE_DECL, "interface"}, {ASTKind::MACRO_DECL, "macro"}, {ASTKind::MAIN_DECL, "main"}, {ASTKind::PACKAGE_DECL, "package"}, {ASTKind::PRIMARY_CTOR_DECL, "primary constructor"}, {ASTKind::PROP_DECL, "property"}, {ASTKind::STRUCT_DECL, "struct"}, {ASTKind::TYPE_ALIAS_DECL, "type alias"}, {ASTKind::VAR_DECL, "variable"}, {ASTKind::VAR_WITH_PATTERN_DECL, "variable"}, }; std::string DeclKindToString(const Decl& decl) { auto it = DECL2STRMAP.find(decl.astKind); if (it == DECL2STRMAP.end()) { return decl.identifier; } return it->second; } std::string GetTypesStr(std::vector>& decls) { std::string res; std::unordered_set typeSetCache; for (auto it : decls) { if (it == nullptr) { continue; } auto str = AST::ASTKIND_TO_STRING_MAP[it->astKind]; if (typeSetCache.find(str) != typeSetCache.end()) { continue; } typeSetCache.emplace(str); res += str + " "; } return res; } namespace { Ptr FindValidPropAccessor(ClassDecl& cd, bool isGetter, const std::string& name) { auto curClass = &cd; while (curClass != nullptr) { for (auto& it : curClass->GetMemberDecls()) { CJC_ASSERT(it); if (it->identifier != name) { continue; } auto found = GetUsedMemberDecl(*it, isGetter); CJC_ASSERT(found); if (found->astKind == ASTKind::FUNC_DECL) { return RawStaticCast(found); } } curClass = curClass->GetSuperClassDecl(); }; return nullptr; } } // namespace /** * Since spec support 'var' propDecl to inherit parent's getter/setter separately that * child can only override one of getter/setter. * We need to find getter/setter from current class or parent class. * return (getter, setter) */ std::pair, Ptr> GetUsableGetterSetterForProperty(PropDecl& pd) { return std::make_pair(GetUsableGetterForProperty(pd), GetUsableSetterForProperty(pd)); } // Returns getter for property Ptr GetUsableGetterForProperty(PropDecl& pd) { Ptr getter = nullptr; auto cd = DynamicCast(pd.outerDecl); if (pd.getters.empty()) { if (cd) { getter = FindValidPropAccessor(*cd, true, pd.identifier); } } else { getter = pd.getters[0].get(); } return getter; } // Returns setter for mutable property Ptr GetUsableSetterForProperty(PropDecl& pd) { CJC_ASSERT(pd.isVar); Ptr setter = nullptr; auto cd = DynamicCast(pd.outerDecl); if (pd.setters.empty()) { if (cd) { setter = FindValidPropAccessor(*cd, false, pd.identifier); } } else { setter = pd.setters[0].get(); } return setter; } std::set> CollectAllRelatedExtends(TypeManager& tyMgr, InheritableDecl& boxedDecl) { if (boxedDecl.astKind != ASTKind::CLASS_DECL) { return tyMgr.GetDeclExtends(boxedDecl); } std::set> allExtends; auto curClass = StaticAs(&boxedDecl); do { allExtends.merge(tyMgr.GetDeclExtends(*curClass)); curClass = curClass->GetSuperClassDecl(); } while (curClass != nullptr); return allExtends; } size_t CountOptionNestedLevel(const Ty& ty) { size_t level = 0; Ptr currentTy = &ty; while (currentTy->IsCoreOptionType()) { CJC_ASSERT(currentTy->typeArgs.size() == 1); CJC_NULLPTR_CHECK(currentTy->typeArgs.front()); currentTy = currentTy->typeArgs.front(); level++; } return level; } Ptr UnboxOptionType(Ptr ty) { Ptr optionUnboxTy = ty; // Option type allow type auto box. while (Ty::IsTyCorrect(optionUnboxTy) && optionUnboxTy->IsCoreOptionType()) { // CoreOptionType test guarantees that typeArgs.size == 1. optionUnboxTy = optionUnboxTy->typeArgs[0]; } return optionUnboxTy; } std::string GetFullInheritedTy(ExtendDecl& extend) { std::string fullType = PosSearchApi::PosToStr(extend.begin); for (auto& interface : extend.inheritedTypes) { fullType += interface->ty->String(); } return fullType; } std::vector> GetFuncTargets(const Node& node) { switch (node.astKind) { case ASTKind::REF_EXPR: { std::vector> funcTargets; auto refTargets = StaticCast(node).ref.targets; for (auto& it : refTargets) { if (auto fd = DynamicCast(it)) { funcTargets.push_back(fd); } } return funcTargets; } case ASTKind::MEMBER_ACCESS: { return StaticCast(node).targets; } default: return {}; } } Ptr FindModifier(const Decl& d, TokenKind kind) { Ptr mod = nullptr; for (auto& modifier : d.modifiers) { if (modifier.modifier == kind) { mod = &modifier; } } return mod; } void AddArrayLitConstructor(ArrayLit& al) { auto decl = Ty::GetDeclPtrOfTy(al.ty); if (!decl) { return; } for (auto it : decl->GetMemberDeclPtrs()) { if (auto fd = DynamicCast(it); fd && IsInstanceConstructor(*fd) && fd->funcBody) { // Constructor used for 'ArrayLit' has 3 params. if (fd->funcBody->paramLists.empty() || fd->funcBody->paramLists[0]->params.size() != 3) { continue; } auto firstParamTy = fd->funcBody->paramLists[0]->params[0]->ty; if (Ty::IsTyCorrect(firstParamTy) && firstParamTy->IsArray()) { al.initFunc = fd; return; } } } } std::optional, size_t>> GetParamTyAccordingToArgName(const FuncDecl& fd, const std::string argName) { CJC_ASSERT(!argName.empty()); // Null test is done in the caller. auto& paramList = fd.funcBody->paramLists[0]; // Traverse the parameters, find the parameter has same identifier with the named argument. for (size_t j = 0; j < paramList->params.size(); ++j) { if (paramList->params[j] && paramList->params[j]->identifier == argName) { auto ty = paramList->params[j]->type ? paramList->params[j]->type->ty : paramList->params[j]->ty; return {std::make_pair(ty, j)}; } } return {}; } std::string GetArgName(const FuncDecl& fd, const FuncArg& arg) { if (arg.TestAttr(Attribute::IMPLICIT_ADD)) { // For trailing closure argument, its naming condition always follows the definition. if (!fd.funcBody->paramLists[0]->params.empty() && fd.funcBody->paramLists[0]->params.back()->isNamedParam) { return fd.funcBody->paramLists[0]->params.back()->identifier; } else { return ""; } } return arg.name; } Ptr GetCurrentGeneric(const FuncDecl& fd, const CallExpr& ce) { CJC_NULLPTR_CHECK(fd.funcBody); auto generic = fd.funcBody->generic.get(); if (generic == nullptr && fd.outerDecl && IsTypeObjectCreation(fd, ce)) { generic = fd.outerDecl->GetGeneric(); } return generic; } TyVars GetTyVars(const FuncDecl& fd, const CallExpr& ce, bool ignoreContext) { TyVars res; auto curGeneric = GetCurrentGeneric(fd, ce); if (curGeneric) { for (auto& tyParam : curGeneric->typeParameters) { res.emplace(StaticCast(tyParam->ty)); } } // A special case for static function calls or enum constructor. // Get the type variables from the class, interface, enum, struct, and // extend definitions. bool isMemberOfGenericType = false; if (auto ma = DynamicCast(ce.baseFunc.get())) { CJC_NULLPTR_CHECK(ma->baseExpr); auto baseTarget = ma->baseExpr->GetTarget(); isMemberOfGenericType = fd.TestAttr(Attribute::STATIC) && fd.outerDecl && fd.outerDecl->generic && fd.outerDecl->IsNominalDecl(); isMemberOfGenericType = isMemberOfGenericType || (baseTarget && baseTarget->GetGeneric() && baseTarget->TestAttr(Attribute::ENUM_CONSTRUCTOR)); } if (ignoreContext || !isMemberOfGenericType) { return res; } for (auto& tyParam : fd.outerDecl->generic->typeParameters) { res.emplace(StaticCast(tyParam->ty)); } return res; } bool HasTyVarsToSolve(const SubstPack& maps) { for (auto [k, v] : maps.u2i) { if (!Utils::InKeys(Ptr(StaticCast(v)), maps.inst)) { return true; } } return false; } bool HasUnsolvedTyVars(const TypeSubst& subst, const std::set>& tyVars) { // A valid solution should constain substitution for all of type variables // and each substituted type should not contain any of type variable. return std::any_of(tyVars.begin(), tyVars.end(), [&subst](auto& tyVar) { return !Utils::InKeys(tyVar, subst) || std::any_of(subst.begin(), subst.end(), [tyVar](auto it) { return it.second->Contains(tyVar); }); }); } TyVars GetTyVarsToSolve(const SubstPack& maps) { TyVars ret; for (auto [k, v] : maps.u2i) { ret.emplace(StaticCast(v)); } Utils::EraseIf(ret, [&maps](auto tv) { return Utils::InKeys(tv, maps.inst); }); return ret; } /** * This function is only used to collect param types in arguments order and will NOT report diagnostics. */ std::vector> GetParamTysInArgsOrder(TypeManager& tyMgr, const CallExpr& ce, const FuncDecl& fd) { if (!fd.funcBody || fd.funcBody->paramLists.empty()) { return {}; } auto& paramList = fd.funcBody->paramLists[0]; // Record whether the position of parameters has ty. std::vector paramHasTy(paramList->params.size(), false); // If a parameter has a default value, it is already specified as ty by default. for (size_t i = 0; i < paramList->params.size(); ++i) { paramHasTy[i] = paramList->params[i]->TestAttr(Attribute::HAS_INITIAL); } // Help to record whether named argument has been appear. bool namedArgFound = false; std::vector> tyInArgOrder; for (size_t i = 0; i < ce.args.size(); ++i) { std::string argName = GetArgName(fd, *ce.args[i]); if (argName.empty()) { // Unnamed argument can not appear after named argument. if (namedArgFound) { return {}; } // So the index of the unnamed parameter argument should match the index of the formal parameter. if (i < paramList->params.size()) { CJC_NULLPTR_CHECK(paramList->params[i]); tyInArgOrder.emplace_back(paramList->params[i]->ty); paramHasTy[i] = true; } else if (fd.TestAttr(Attribute::C)) { // For C FFI variable-length arguments. tyInArgOrder.emplace_back(tyMgr.GetCTypeTy()); } else if (HasJavaAttr(fd)) { // For JavaScript or Java FFI variable-length arguments. tyInArgOrder.emplace_back(tyMgr.GetAnyTy()); } else { // Cangjie's variable-length arguments are handled by `ChkVariadicCallExpr`. // The number of arguments mismatches the number of parameters here. return {}; } continue; } // Named argument. namedArgFound = true; if (auto res = GetParamTyAccordingToArgName(fd, argName)) { auto [ty, index] = res.value(); tyInArgOrder.emplace_back(ty); CJC_ASSERT(index < paramHasTy.size()); paramHasTy[index] = true; } else { return {}; } } for (auto it : std::as_const(paramHasTy)) { if (!it) { return {}; } } return tyInArgOrder; } bool IsEnumCtorWithoutTypeArgs(const Expr& expr, Ptr target) { if (!target || !target->TestAttr(Attribute::ENUM_CONSTRUCTOR) || !target->GetGeneric()) { return false; } if (expr.astKind == ASTKind::REF_EXPR) { // For enum like 'None'. return expr.GetTypeArgs().empty(); } else if (auto ma = DynamicCast(&expr); ma && ma->baseExpr && ma->baseExpr->IsReferenceExpr()) { auto baseTypeArgs = ma->baseExpr->GetTypeArgs(); auto baseDecl = ma->baseExpr->GetTarget(); CJC_NULLPTR_CHECK(baseDecl); // For enum like 'Option.None' or 'core.Option.None'. // 'NeedFurtherInstantiation' is checking for typealias accessing like: // enum E { EE(K) } // type X = E // X.EE(1) -- which also needs type inference. // type Y = E // Y.EE(1) -- which does not need type inference. return NeedFurtherInstantiation(baseTypeArgs) && expr.GetTypeArgs().empty(); } return false; } const std::unordered_set QUESTABLE_NODES{AST::ASTKind::FUNC_ARG, AST::ASTKind::PAREN_EXPR, AST::ASTKind::LAMBDA_EXPR, AST::ASTKind::CALL_EXPR, AST::ASTKind::TRAIL_CLOSURE_EXPR}; bool IsQuestableNode(const Node& n) { return QUESTABLE_NODES.count(n.astKind) != 0; } const std::unordered_set PLACEHOLDABLE_NODES{ASTKind::PATTERN, ASTKind::VAR_PATTERN, ASTKind::CONST_PATTERN, ASTKind::TUPLE_PATTERN, ASTKind::ENUM_PATTERN, ASTKind::VAR_OR_ENUM_PATTERN, ASTKind::TYPE_PATTERN, ASTKind::EXCEPT_TYPE_PATTERN, ASTKind::WILDCARD_PATTERN, ASTKind::CALL_EXPR, ASTKind::PAREN_EXPR, ASTKind::MEMBER_ACCESS, ASTKind::REF_EXPR, ASTKind::OPTIONAL_EXPR, ASTKind::OPTIONAL_CHAIN_EXPR, ASTKind::MATCH_EXPR, ASTKind::BLOCK, ASTKind::IF_EXPR, ASTKind::TRY_EXPR, ASTKind::LAMBDA_EXPR, ASTKind::TRAIL_CLOSURE_EXPR, ASTKind::SPAWN_EXPR, ASTKind::MATCH_CASE, ASTKind::MATCH_CASE_OTHER, ASTKind::FUNC_ARG, ASTKind::FUNC_BODY, ASTKind::FUNC_PARAM}; bool AcceptPlaceholderTarget(const AST::Node& n) { return PLACEHOLDABLE_NODES.count(n.astKind) != 0; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool IsNeedRuntimeCheck(TypeManager& typeManager, Ty& srcTy, Ty& targetTy) { return (srcTy.IsClassLike() && targetTy.IsClassLike()) || srcTy.IsGeneric() || targetTy.IsGeneric() || srcTy.HasGeneric() || targetTy.HasGeneric() || typeManager.IsSubtype(&srcTy, &targetTy, true, false) || typeManager.IsSubtype(&targetTy, &srcTy, true, false); } #endif namespace { Ptr GetLastTypeAliasTargetVisit( AST::TypeAliasDecl& decl, std::unordered_set>& visited) { auto target = &decl; if (visited.count(target) > 0) { return target; } else { visited.emplace(target); } if (auto aliasDecl = DynamicCast(target); aliasDecl && aliasDecl->type) { auto realTarget = aliasDecl->type->GetTarget(); if (auto innerAlias = DynamicCast(realTarget); innerAlias && innerAlias->type) { target = GetLastTypeAliasTargetVisit(*innerAlias, visited); } } return target; } } // namespace Ptr GetLastTypeAliasTarget(AST::TypeAliasDecl& decl) { std::unordered_set> visited; return GetLastTypeAliasTargetVisit(decl, visited); } void MergeSubstPack(SubstPack& target, const SubstPack& src) { for (auto [tvu, tvi] : src.u2i) { CJC_ASSERT(!(target.u2i.count(tvu) > 0 && target.u2i[tvu] != tvi)); target.u2i[tvu] = tvi; } MergeMultiTypeSubsts(target.inst, src.inst); } // decide if one type is subtype/supertype of all types. subtype or supertype is specified by lessThan bool LessThanAll(Ptr ty, const std::set>& tys, const std::function, Ptr)>& lessThan) { return std::all_of(tys.cbegin(), tys.cend(), [ty, &lessThan](Ptr element) { return lessThan(ty, element); }); } Ptr FindSmallestTy(const std::set>& tys, const std::function, Ptr)>& lessThan) { if (tys.empty()) { return TypeManager::GetNothingTy(); } Ptr bubble = nullptr; // bubble over one or two tys that are not min by each iteration for (Ptr ty : tys) { if (!bubble) { bubble = ty; } else { if (lessThan(bubble, ty)) { continue; } else if (lessThan(ty, bubble)) { bubble = ty; } else { bubble = nullptr; } } } // bubble is the only possible ty that is min, but not necessarily so, therefore need to verify if (bubble && LessThanAll(bubble, tys, lessThan)) { return bubble; } else { return TypeManager::GetInvalidTy(); } } void TryEnforceCandidate(TyVar& tv, const std::set>& candidates, TypeManager& tyMgr) { if (candidates.empty()) { return; } std::set> declTys; for (auto d : candidates) { declTys.emplace(d->ty); } auto pro = Promotion(tyMgr); auto isSuperDecl = [&pro](Ptr sup, Ptr sub) { return sub && sup && !pro.Promote(*sub, *sup).empty(); }; // try to find the most general type auto uniq = FindSmallestTy(declTys, isSuperDecl); if (Ty::IsTyCorrect(uniq)) { tyMgr.ConstrainByCtor(tv, *uniq); } else { // fill type arguments of found type with placeholder tyvars, in case it's generic // the tyvars may be solved later when inferring the lambda body std::vector> tyArgs; tyMgr.constraints[&tv].sum.clear(); for (auto ty : declTys) { tyMgr.AddSumByCtor(tv, *ty, tyArgs); } } } std::set> TypeMapToTys(const std::map& m, bool fromKey) { std::set> result; for (auto& [operandKind, retKind] : m) { result.insert(TypeManager::GetPrimitiveTy(fromKey ? operandKind : retKind)); } return result; } std::set> GetGenericParamsForDecl(const AST::Decl& decl) { std::set> ret; if (decl.generic) { for (auto& gp : decl.generic->typeParameters) { ret.insert(gp->ty); } } if (auto ed = DynamicCast(&decl)) { auto target = ed->extendedType->GetTarget(); if (target) { ret.merge(GetGenericParamsForDecl(*GetRealTarget(target))); } } else if (auto fd = DynamicCast(&decl); fd && fd->funcBody && fd->funcBody->generic) { for (auto& gp : fd->funcBody->generic->typeParameters) { ret.insert(gp->ty); } } if (auto outer = decl.outerDecl) { ret.merge(GetGenericParamsForDecl(*outer)); } return ret; } std::set> GetGenericParamsForTy(const AST::Ty& ty) { if (auto id = Ty::GetDeclPtrOfTy(&ty)) { return GetGenericParamsForDecl(*id); } return {}; } std::set> GetGenericParamsForCall(const AST::CallExpr& ce, const AST::FuncDecl& fd) { auto ret = GetGenericParamsForDecl(fd); auto base = ce.baseFunc.get(); while (base) { if (base->ty) { ret.merge(GetGenericParamsForTy(*base->ty)); } if (auto ma = DynamicCast(base)) { if (ma->target) { ret.merge(GetGenericParamsForDecl(*ma->target)); } base = ma->baseExpr; } else if (auto ce0 = DynamicCast(base)) { base = ce0->baseFunc; } else if (auto re = DynamicCast(base)) { if (re->ref.target) { ret.merge(GetGenericParamsForDecl(*re->ref.target)); } base = nullptr; } else { base = nullptr; } } return ret; } std::optional, Ptr>> FindInitDecl(InheritableDecl& decl, TypeManager& typeManager, std::vector>& valueArgs, const std::vector> instTys) { std::vector> valueParamTys; std::transform( valueArgs.begin(), valueArgs.end(), std::back_inserter(valueParamTys), [](auto& arg) { return arg->ty; }); return FindInitDecl(decl, typeManager, valueParamTys, instTys); } std::optional, Ptr>> FindInitDecl(InheritableDecl& decl, TypeManager& typeManager, const std::vector> valueParamTys, const std::vector> instTys) { auto initFuncDecl = GetMemberDecl(decl, "init", valueParamTys, typeManager); if (!initFuncDecl) { return std::nullopt; } return std::make_pair( initFuncDecl, typeManager.GetInstantiatedTy(initFuncDecl->ty, GenerateTypeMapping(decl, instTys))); } OwnedPtr CreateInitCall(const std::pair, Ptr> initDeclInfo, std::vector>& valueArgs, File& curFile, const std::vector> instTys) { std::vector> valueFuncArgs; std::transform(valueArgs.begin(), valueArgs.end(), std::back_inserter(valueFuncArgs), [](auto& arg) { return CreateFuncArg(std::move(arg)); }); auto call = MakeOwned(); auto initDecl = initDeclInfo.first; auto ty = initDeclInfo.second; auto refExpr = CreateRefExpr(*initDecl); refExpr->ty = ty; refExpr->curFile = &curFile; refExpr->instTys = instTys; call->baseFunc = std::move(refExpr); call->curFile = &curFile; call->resolvedFunction = initDecl; CJC_ASSERT(ty && ty->IsFunc()); call->ty = StaticCast(ty)->retTy; call->args = std::move(valueFuncArgs); return call; } OwnedPtr CreateThrowException( ClassDecl& exceptionDecl, std::vector> args, File& curFile, TypeManager& typeManager) { auto throwExpr = MakeOwned(); throwExpr->expr = CreateInitCall(FindInitDecl(exceptionDecl, typeManager, args).value(), args, curFile); throwExpr->ty = TypeManager::GetNothingTy(); throwExpr->curFile = &curFile; return throwExpr; } OwnedPtr CreateGenericParamDecl(Decl& decl, const std::string& name, TypeManager& typeManager) { auto typeParam = MakeOwned(); typeParam->identifier = name; typeParam->ty = typeManager.GetGenericsTy(*typeParam); typeParam->outerDecl = &decl; return typeParam; } OwnedPtr CreateGenericParamDecl(Decl& decl, TypeManager& typeManager) { return CreateGenericParamDecl(decl, "T", typeManager); } Ptr GenerateGetTypeForTypeParamIntrinsic(Package& pkg, TypeManager& typeManager, Ptr strTy) { auto file = pkg.files[0].get(); auto retTy = IS_GENERIC_INSTANTIATION_ENABLED ? strTy : typeManager.GetCStringTy(); auto funcTy = typeManager.GetFunctionTy({}, retTy); auto decl = MakeOwned(); auto funcBody = MakeOwned(); funcBody->paramLists.emplace_back(CreateFuncParamList(std::vector>{})); funcBody->retType = MakeOwned(); funcBody->retType->ty = retTy; funcBody->generic = MakeOwned(); funcBody->generic->typeParameters.emplace_back(CreateGenericParamDecl(*decl, typeManager)); funcBody->ty = funcTy; decl->curFile = file; decl->identifier = GET_TYPE_FOR_TYPE_PARAMETER_FUNC_NAME; decl->fullPackageName = pkg.fullPackageName; decl->ty = funcTy; decl->funcBody = std::move(funcBody); decl->EnableAttr(Attribute::INTRINSIC); decl->EnableAttr(Attribute::GENERIC); auto declPtr = decl.get(); file->decls.push_back(std::move(decl)); std::rotate(file->decls.rbegin(), file->decls.rbegin() + 1, file->decls.rend()); return declPtr; } Ptr GenerateIsSubtypeTypesIntrinsic(Package& pkg, TypeManager& typeManager) { auto file = pkg.files[0].get(); auto retTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_BOOLEAN); auto funcTy = typeManager.GetFunctionTy({}, retTy); auto decl = MakeOwned(); auto funcBody = MakeOwned(); funcBody->paramLists.emplace_back(CreateFuncParamList(std::vector>{})); funcBody->retType = MakeOwned(); funcBody->retType->ty = retTy; funcBody->generic = MakeOwned(); funcBody->generic->typeParameters.emplace_back(CreateGenericParamDecl(*decl, typeManager)); funcBody->generic->typeParameters.emplace_back(CreateGenericParamDecl(*decl, typeManager)); funcBody->ty = funcTy; AddCurFile(*decl, file); decl->identifier = IS_SUBTYPE_TYPES_FUNC_NAME; decl->fullPackageName = pkg.fullPackageName; decl->ty = funcTy; decl->funcBody = std::move(funcBody); decl->EnableAttr(Attribute::INTRINSIC); decl->EnableAttr(Attribute::GENERIC); decl->EnableAttr(Attribute::COMPILER_ADD); auto declPtr = decl.get(); file->decls.push_back(std::move(decl)); std::rotate(file->decls.rbegin(), file->decls.rbegin() + 1, file->decls.rend()); return declPtr; } } // namespace Cangjie::TypeCheckUtil cangjie_compiler-1.0.7/src/Sema/TypeCheckUtil.h000066400000000000000000000364371510705540100213660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the Utility functions for TypeCheck. */ #ifndef CANGJIE_SEMA_TYPECHECKUTIL_H #define CANGJIE_SEMA_TYPECHECKUTIL_H #include "ScopeManager.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Sema/TypeManager.h" namespace Cangjie::TypeCheckUtil { // The comparison result of two types. // If left < right, return LT. // If left > right, return GT. // If left == right, return EQ. enum class ComparisonRes { LT, GT, EQ }; /* * Questable nodes need to allow QuestTy in target type and * use synthesized type instead of target type as its own ty. */ bool IsQuestableNode(const AST::Node& n); bool AcceptPlaceholderTarget(const AST::Node& n); std::vector GetIdealTypesByKind(AST::TypeKind type); constexpr std::string_view WILDCARD_CHAR{"_"}; using FuncSig = std::pair>>; struct FuncSigCmp { bool operator()(const FuncSig& lhs, const FuncSig& rhs) const { if (lhs.first != rhs.first) { return lhs.first < rhs.first; } if (lhs.second.size() != rhs.second.size()) { return lhs.second.size() < rhs.second.size(); } // If 'lhs' and 'rhs' have same size of func parameter types, // compare each type's kind, type name, type definition position and ptr value in order. for (size_t i = 0; i < lhs.second.size(); ++i) { if (CompTyByNames(lhs.second[i], rhs.second[i])) { return true; } } return false; } }; using FuncSig2Decl = std::map, FuncSigCmp>; template inline OwnedPtr MakeOwnedNode() { auto ptr = MakeOwned(); ptr->EnableAttr(AST::Attribute::COMPILER_ADD); return ptr; } template inline OwnedPtr MakeOwnedNode(_Args&&... args) { auto ptr = MakeOwned(std::forward<_Args>(args)...); ptr->EnableAttr(AST::Attribute::COMPILER_ADD); return ptr; } template bool IsAllFuncDecl(T& results) { for (auto it : results) { if (it && it->astKind != AST::ASTKind::FUNC_DECL && it->astKind != AST::ASTKind::MACRO_DECL) { return false; } } return true; } inline std::vector> GetInstanationTys(const AST::Expr& expr) { auto ref = DynamicCast(&expr); return ref ? ref->instTys : std::vector>{}; } template void RemoveDuplicateElements(std::vector& candidates) { // Remove duplicate elements in vector. std::set uniqueCandidates; for (auto it = candidates.begin(); it != candidates.end();) { if (uniqueCandidates.find(*it) != uniqueCandidates.end()) { it = candidates.erase(it); } else { uniqueCandidates.insert(*it); ++it; } } } inline bool NeedSynOnUsed(const AST::Decl& target) { // Type decls, func param and other well-typed imported decls are no need to be checked again recursively, // because the ty is already set at PreCheck stage. // Source imported function will not been checked from toplevel, so we must synthesize it when used. return !target.IsTypeDecl() && target.astKind != AST::ASTKind::FUNC_PARAM && (!AST::Ty::IsTyCorrect(target.ty) || target.ty->HasQuestTy()); } std::string GetFullInheritedTy(AST::ExtendDecl& extend); void UpdateInstTysWithTypeArgs(AST::NameReferenceExpr& expr); void SetIsNotAlone(AST::Expr& baseExpr); void ModifyTargetOfRef(AST::RefExpr& re, Ptr decl, const std::vector>& targets); void AddFuncTargetsForMemberAccess(AST::MemberAccess& ma, const std::vector>& targets); void ReplaceTarget(Ptr node, Ptr target, bool insertTarget = true); void MarkParamWithInitialValue(AST::Node& root); bool HasIntersectionTy(const std::vector>& types); bool NeedFurtherInstantiation(const std::vector>& types); bool IsOverloadableOperator(TokenKind op); bool CanSkipDiag(const AST::Node& node); bool IsFieldOperator(const std::string& field); bool IsGenericUpperBoundCall(const AST::Expr& expr, AST::Decl& target); bool IsNode1ScopeVisibleForNode2(const AST::Node& node1, const AST::Node& node2); size_t CountOptionNestedLevel(const AST::Ty& ty); Ptr UnboxOptionType(Ptr ty); /** * Check if ThisType compatibility in class inheritance */ bool CheckThisTypeCompatibility(const AST::FuncDecl& parentFunc, const AST::FuncDecl& childFunc); bool IsFuncReturnThisType(const AST::FuncDecl& fd); /** Return true if @p pkg has main_decl. */ bool HasMainDecl(AST::Package& pkg); /** Compare numeric type by builtin strategy. */ ComparisonRes CompareIntAndFloat(const AST::Ty& left, const AST::Ty& right); /** * Util functions for manipulating 'MultiTypeSubst'. */ std::set ExpandMultiTypeSubst(const MultiTypeSubst& mts, const std::set>& usefulTys); std::vector ExpandMultiTypeSubst(const SubstPack& maps, const std::set>& usefulTys); /** * Reduce type mapping to only contains direct mapping from given generic ty vars to instantiated tys. */ MultiTypeSubst ReduceMultiTypeSubst(TypeManager& tyMgr, const TyVars& tyVars, const MultiTypeSubst& mts); TypeSubst MultiTypeSubstToTypeSubst(const MultiTypeSubst& mts); TypeSubst GenerateTypeMappingByTy(const Ptr genericTy, const Ptr instantTy); TypeSubst GenerateTypeMapping(const AST::Decl& decl, const std::vector>& typeArgs); void GenerateTypeMapping( TypeManager& tyMgr, SubstPack& m, const AST::Decl& decl, const std::vector>& typeArgs); void RelayMappingFromExtendToExtended(TypeManager& tyMgr, SubstPack& m, const AST::ExtendDecl& decl); TypeSubst InverseMapping(const TypeSubst& typeMapping); void MergeTypeSubstToMultiTypeSubst(MultiTypeSubst& mts, const TypeSubst& typeMapping); void MergeMultiTypeSubsts(MultiTypeSubst& target, const MultiTypeSubst& src); /* u2i map can't have conflict between the two */ void MergeSubstPack(SubstPack& target, const SubstPack& src); /** Get set of generic sema tys used in given @p ty */ std::unordered_set> GetAllGenericTys(Ptr const ty); std::vector> GetDeclTypeParams(const AST::Decl& decl); /** Get mapped type of given @p tyVar . If the tyVar is not mapped in TypeSubst/MultiTypeSubst, return itself. */ Ptr GetMappedTy(const MultiTypeSubst& mts, TyVar* tyVar); Ptr GetMappedTy(const TypeSubst& typeMapping, TyVar* tyVar); /** Occurs check for tyVars in @p typeMapping */ bool HaveCyclicSubstitution(TypeManager& tyMgr, const TypeSubst& typeMapping); /** * Get parameter tys of given function declaration @p fd. */ std::vector> GetParamTys(const AST::FuncDecl& fd); std::vector> GetFuncBodyParamTys(const AST::FuncBody& fb); /** * Check whether src is an override or implementation of target. */ bool IsOverrideOrShadow(TypeManager& typeManager, const AST::FuncDecl& src, const AST::FuncDecl& target, const Ptr baseTy = nullptr, const Ptr expectInstParent = nullptr); bool IsOverrideOrShadow( TypeManager& typeManager, const AST::PropDecl& src, const AST::PropDecl& target, Ptr baseTy = nullptr); MultiTypeSubst GenerateTypeMappingBetweenFuncs( TypeManager& typeManager, const AST::FuncDecl& src, const AST::FuncDecl& target); /** Get real target decl since given decl maybe typealias decl. */ Ptr GetRealTarget(Ptr decl); /** * Return function targets for a reference node. */ std::vector> GetFuncTargets(const AST::Node& node); /** * Given a referenced target @p decl from RefExpr or MemberAccess. * Return the pair of 'isGetter' status and the real member in nominal decl. */ std::pair> GetRealMemberDecl(AST::Decl& decl); /** * Given a member @p decl found in nominal decl and a possible getter status @p isGetter for propDecl. * Return the real used decl as a referenced target. * Return 'decl' it self for non-propDecl, return getter/setter function for propDecl. */ Ptr GetUsedMemberDecl(AST::Decl& decl, bool isGetter); std::string DeclKindToString(const AST::Decl& decl); /** * Get string of given decls' ast type. */ std::string GetTypesStr(std::vector>& decls); std::pair, Ptr> GetUsableGetterSetterForProperty(AST::PropDecl& pd); Ptr GetUsableGetterForProperty(AST::PropDecl& pd); Ptr GetUsableSetterForProperty(AST::PropDecl& pd); /** Collect all related extends of 'decl' and it's super classes' extends if exist. */ std::set> CollectAllRelatedExtends(TypeManager& tyMgr, AST::InheritableDecl& boxedDecl); std::unordered_set> GetContextGenericTys(const ASTContext& ctx, const AST::Expr& expr); OwnedPtr CreateDefaultCtor(AST::InheritableDecl& decl, bool isStatic = false); Ptr GetCurFuncBody(const ASTContext& ctx, const std::string& scopeName); /** Get the outer inheritable decl where the current context is. */ inline Ptr GetCurInheritableDecl(const ASTContext& ctx, const std::string& scopeName) { auto sym = ScopeManager::GetCurSymbolByKind(SymbolKind::STRUCT, ctx, scopeName); return sym ? DynamicCast(sym->node) : nullptr; } /* Utils for TypeCheckCall and TypeArgumentInference */ bool IsEnumCtorWithoutTypeArgs(const AST::Expr& expr, Ptr target); TyVars GetTyVars(const AST::FuncDecl& fd, const AST::CallExpr& ce, bool ignoreContext = false); TyVars GetTyVarsToSolve(const SubstPack& maps); bool HasTyVarsToSolve(const SubstPack& maps); bool HasUnsolvedTyVars(const TypeSubst& subst, const std::set>& tyVars); std::vector> GetParamTysInArgsOrder(TypeManager& tyMgr, const AST::CallExpr& ce, const AST::FuncDecl& fd); Ptr GetCurrentGeneric(const AST::FuncDecl& fd, const AST::CallExpr& ce); std::string GetArgName(const AST::FuncDecl& fd, const AST::FuncArg& arg); std::optional, size_t>> GetParamTyAccordingToArgName( const AST::FuncDecl& fd, const std::string argName); inline bool IsTypeObjectCreation(const AST::FuncDecl& fd, const AST::CallExpr& ce) { // Get type variables from the outer (class, struct, enum) declaration of a constructor function // when call by type name (enum constructor is alway treated as called by typename). bool isTypeNameCall = ce.callKind == AST::CallKind::CALL_OBJECT_CREATION || ce.callKind == AST::CallKind::CALL_STRUCT_CREATION; return (isTypeNameCall && fd.TestAttr(AST::Attribute::CONSTRUCTOR)) || fd.TestAttr(AST::Attribute::ENUM_CONSTRUCTOR); } /** * Get a specific modifier of a given declaration @p d. */ Ptr FindModifier(const AST::Decl& d, TokenKind kind); inline bool HasCFuncAttr(const AST::Decl& decl) { return decl.TestAnyAttr(AST::Attribute::C, AST::Attribute::FOREIGN); } void AddArrayLitConstructor(AST::ArrayLit& al); bool IsNeedRuntimeCheck(TypeManager& typeManager, AST::Ty& srcTy, AST::Ty& targetTy); Ptr GetLastTypeAliasTarget(AST::TypeAliasDecl& decl); // find the type that is subtype/supertype of all types. subtype or supertype is specified by lessThan Ptr FindSmallestTy( const std::set>& tys, const std::function, Ptr)>& lessThan); bool LessThanAll(Ptr ty, const std::set>& tys, const std::function, Ptr)>& lessThan); void TryEnforceCandidate(TyVar& tv, const std::set>& candidates, TypeManager& tyMgr); std::set> TypeMapToTys(const std::map& m, bool fromKey); // get generic params for the decl and outer decl(if there is) and extended decl(if there is) std::set> GetGenericParamsForDecl(const AST::Decl& decl); // get generic params for the decl of the type std::set> GetGenericParamsForTy(const AST::Ty& ty); // get generic params for all decls used in the call std::set> GetGenericParamsForCall(const AST::CallExpr& ce, const AST::FuncDecl& fd); OwnedPtr CreateThrowException( AST::ClassDecl& exceptionDecl, std::vector> args, AST::File& curFile, TypeManager& typeManager); std::optional, Ptr>> FindInitDecl( AST::InheritableDecl& decl, TypeManager& typeManager, std::vector>& valueArgs, const std::vector> instTys = {}); std::optional, Ptr>> FindInitDecl( AST::InheritableDecl& decl, TypeManager& typeManager, const std::vector> valueParamTys, const std::vector> instTys = {}); OwnedPtr CreateInitCall( const std::pair, Ptr> initDeclInfo, std::vector>& valueArgs, AST::File& curFile, const std::vector> instTys = {}); Ptr GenerateGetTypeForTypeParamIntrinsic( AST::Package& pkg, TypeManager& typeManager, Ptr strTy); // Generates declaration of intrinsic that is roughly eqivalent to expression `TypeLeft is TypeRight`, // where `TypeLeft` and `TypeRight` are types and the result is true iff TypeLeft is subtype of TypeRight: // func isSubtypeTypes() { // return T is E // } Ptr GenerateIsSubtypeTypesIntrinsic(AST::Package& pkg, TypeManager& typeManager); OwnedPtr CreateGenericParamDecl( AST::Decl& decl, const std::string& name, TypeManager& typeManager); OwnedPtr CreateGenericParamDecl(AST::Decl& decl, TypeManager& typeManager); static bool const IS_GENERIC_INSTANTIATION_ENABLED = #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND false; #endif template T* GetMemberDecl( AST::Decl& decl, const std::string& identifier, std::vector> paramTys, TypeManager& typeManager) { for (auto& member : decl.GetMemberDecls()) { if (member->identifier != identifier) { continue; } bool isSuitableDecl = true; if (auto funcMember = DynamicCast(member.get()); funcMember) { auto originalParamTys = RawStaticCast(funcMember->ty)->paramTys; if (originalParamTys.size() != paramTys.size()) { continue; } for (std::vector>::size_type i = 0; i < paramTys.size(); i++) { // Object super type is added later, at "desugar after type instantiation" stage, // so we check it here explicitly if (originalParamTys[i]->IsObject() && paramTys[i]->IsClass()) { continue; } if (!typeManager.IsSubtype(paramTys[i], originalParamTys[i])) { isSuitableDecl = false; } } } if (isSuitableDecl) { return DynamicCast(member.get()); } } return nullptr; } } // namespace Cangjie::TypeCheckUtil #endif cangjie_compiler-1.0.7/src/Sema/TypeChecker.cpp000066400000000000000000003412721510705540100214060ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the TypeChecker related classes. */ #include "TypeCheckerImpl.h" #include #include #include #include "CheckAPILevel.h" #include "Collector.h" #include "Desugar/DesugarInTypeCheck.h" #include "DiagSuppressor.h" #include "Diags.h" #include "ExtraScopes.h" #include "JoinAndMeet.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Node.h" #include "cangjie/AST/Utils.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Basic/Print.h" #include "cangjie/Frontend/CompilerInstance.h" #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/Utils.h" #include "NativeFFI/Java/BeforeTypeCheck/GenerateJavaMirror.h" #include "NativeFFI/ObjC/BeforeTypeCheck/Desugar.h" namespace Cangjie { using namespace Sema; using namespace TypeCheckUtil; using namespace AST; TypeChecker::TypeChecker(CompilerInstance* ci) { impl = std::make_unique(ci); } TypeChecker::~TypeChecker() { } TypeChecker::TypeCheckerImpl::TypeCheckerImpl(CompilerInstance* ci) : promotion(Promotion(*ci->typeManager)), typeManager(*ci->typeManager), ci(ci), diag(ci->diag), importManager(ci->importManager), backendType(ci->invocation.globalOptions.backend), mpImpl(new MPTypeCheckerImpl(*ci)) { } TypeChecker::TypeCheckerImpl::~TypeCheckerImpl() { if (mpImpl) { delete mpImpl; mpImpl = nullptr; } } bool TypeChecker::TypeCheckerImpl::CheckThisTypeOfFuncBody(const FuncBody& fb) const { CJC_ASSERT(fb.retType); bool returnThis = fb.retType->astKind == ASTKind::THIS_TYPE || (fb.retType->astKind == ASTKind::REF_TYPE && RawStaticCast(fb.retType.get())->ref.identifier == "This"); // STATIC function should not return 'This' type. if (returnThis && fb.funcDecl != nullptr && fb.funcDecl->TestAttr(Attribute::STATIC)) { return false; } // 'This' type used in non-class decl or nested function are reported by parser. Do not check again. return true; } bool TypeChecker::TypeCheckerImpl::IsIndexAssignmentOperator(const AST::FuncDecl& fd) const { return fd.op == TokenKind::LSQUARE && fd.funcBody != nullptr && !fd.funcBody->paramLists.empty() && !fd.funcBody->paramLists[0]->params.empty() && fd.funcBody->paramLists[0]->params.back()->isNamedParam; } bool TypeChecker::TypeCheckerImpl::CheckBodyRetType(ASTContext& ctx, FuncBody& fb) { CJC_ASSERT(fb.retType); CJC_ASSERT(fb.retType->ty); // Only set 'fb.retType->ty' to invalid when it's type was questTy. // Otherwise return without modifying the type of any node. if (fb.body == nullptr) { // Quick fix for foreign functions. I did not add error messages currently since it is a quick fix. if (fb.retType->ty->HasQuestTy()) { fb.retType->ty = TypeManager::GetInvalidTy(); return false; } return true; } // If the return type is not of QuestTy, then we use it to check the function's body. if (fb.retType->ty->IsQuest()) { // Semantic analysis for function body without given return type. auto ret = Synthesize(ctx, fb.body.get()); // In lambda, the return value type should be consistent with the body. // Otherwise, errors in the body that do not affect the return value type may fail to be reported. if (fb.funcDecl == nullptr && !Ty::IsTyCorrect(ret)) { fb.retType->ty = TypeManager::GetInvalidTy(); return false; } fb.retType->ty = CalcFuncRetTyFromBody(fb); bool isWellTyped = Ty::IsTyCorrect(fb.retType->ty); if (fb.retType->ty->HasQuestTy()) { isWellTyped = false; fb.retType->ty = TypeManager::GetInvalidTy(); if (!CanSkipDiag(fb)) { DiagUnableToInferReturnType(diag, fb); } } if (isWellTyped) { if (CheckReturnThisInFuncBody(fb)) { ReplaceFuncRetTyWithThis(fb, fb.retType->ty); } else if (auto ctt = DynamicCast(fb.retType->ty)) { // Replace ClassThisTy to ClassTy when the function cannot return 'This' anymore. fb.retType->ty = ctt->declPtr->ty; } } return isWellTyped; } else { bool isWellTyped = true; if (fb.retType->ty->IsUnit()) { // The body eventually will be appended a 'return ()' expression, so we switch to the Synthesize mode. // Errors should be already reported during the synthesis. isWellTyped = Ty::IsTyCorrect(Synthesize(ctx, fb.body.get())); } else if (NeedCheckBodyReturn(fb)) { isWellTyped = Check(ctx, fb.retType->ty, fb.body.get()); if (!isWellTyped && fb.body->body.empty()) { DiagMismatchedTypes(diag, *fb.body, *fb.retType, "return type"); } if (isWellTyped && Is(fb.retType->ty)) { auto node = fb.body->body.back().get(); while (true) { if (auto e = DynamicCast(node); e && e->desugarExpr) { node = e->desugarExpr.get(); } else { break; } } if (auto k = node->astKind; k != ASTKind::CALL_EXPR && k != ASTKind::MEMBER_ACCESS && k != ASTKind::REF_EXPR && k != ASTKind::RETURN_EXPR && k != ASTKind::PAREN_EXPR) { fb.body->ty = ReplaceThisTy(fb.body->ty); } if (!CheckReturnThisInFuncBody(fb) && !fb.retType->ty->IsNothing()) { DiagMismatchedTypes(diag, *fb.body, *fb.retType, "return type"); isWellTyped = false; } } } return isWellTyped; } } Ptr TypeChecker::TypeCheckerImpl::ReplaceThisTy(Ptr now) { if (!Ty::IsTyCorrect(now)) { return now; } if (auto thisTy = DynamicCast(now)) { return typeManager.GetClassTy(*thisTy->decl, thisTy->typeArgs); } return now; } void TypeChecker::TypeCheckerImpl::ReplaceFuncRetTyWithThis(FuncBody& fb, Ptr ty) { if (auto ct = DynamicCast(ty); ct) { auto rt = MakeOwned(); rt->curFile = fb.curFile; rt->ref.target = ct->decl; rt->ref.identifier = "This"; rt->ty = typeManager.GetClassThisTy(*ct->declPtr, ct->typeArgs); rt->EnableAttr(Attribute::COMPILER_ADD); if (fb.retType) { CJC_ASSERT(fb.curFile); fb.curFile->trashBin.emplace_back(std::move(fb.retType)); } fb.retType = std::move(rt); } } bool TypeChecker::TypeCheckerImpl::CheckFuncBody(ASTContext& ctx, FuncBody& fb) { if (fb.retType) { Synthesize(ctx, fb.retType.get()); } else { // The goal is that, after type checking, every function declaration has a correct return type, so that the back // ends do not need to test anymore. AddRetTypeNode(fb); } // The constructor logic is simpler and handled separately. if (fb.funcDecl && fb.funcDecl->TestAttr(Attribute::CONSTRUCTOR)) { CheckCtorFuncBody(ctx, fb); // Should not return true directly. Revise later. return true; } if (!CheckThisTypeOfFuncBody(fb)) { diag.Diagnose(*fb.retType, DiagKind::sema_invalid_position_of_this_type); } std::vector> paramTys; if (!CheckNormalFuncBody(ctx, fb, paramTys)) { return false; } return true; } void TypeChecker::TypeCheckerImpl::AddRetTypeNode(FuncBody& fb) const { // For incremental type check's compatibility. Incremental type check will erase retType->ty but not retType. // Adding retType node more than once leads to memory leak. if (!fb.retType) { fb.retType = MakeOwned(); } fb.retType->EnableAttr(Attribute::COMPILER_ADD); fb.retType->ty = TypeManager::GetQuestTy(); // Set compiler added retType as visited to avoid being checked by `CheckReferenceTypeLegality`. fb.retType->EnableAttr(Attribute::IS_CHECK_VISITED); } bool TypeChecker::TypeCheckerImpl::CheckNormalFuncBody(ASTContext& ctx, FuncBody& fb, std::vector>& paramTys) { if (fb.paramLists.empty()) { return false; } CheckFuncParamList(ctx, *fb.paramLists[0]); paramTys = GetFuncBodyParamTys(fb); bool isCFFIBackend = IsUnsafeBackend(backendType); bool isCFunc = fb.TestAttr(Attribute::C) || (fb.funcDecl && fb.funcDecl->TestAttr(Attribute::FOREIGN) && isCFFIBackend); bool hasVariableLenArg = (fb.funcDecl && fb.funcDecl->hasVariableLenArg) || fb.paramLists[0]->hasVariableLenArg; // Check and update return type for foreign functions. if (!Ty::IsTyCorrect(fb.retType->ty)) { if (!fb.TestAttr(Attribute::IS_CHECK_VISITED)) { fb.EnableAttr(Attribute::IS_CHECK_VISITED); // Avoid re-enter funcDecl check, when function is invalid. Synthesize(ctx, fb.body.get()); // Synthesize for other decl/expr in function body. } fb.ty = typeManager.GetFunctionTy(paramTys, fb.retType->ty, {isCFunc, false, hasVariableLenArg}); return false; } // Set funcTy before Synthesize body, avoid recursively call typecheck loop. auto funcTy = typeManager.GetFunctionTy(paramTys, fb.retType->ty, {isCFunc, false, hasVariableLenArg}); if (fb.funcDecl) { fb.funcDecl->ty = funcTy; } fb.ty = funcTy; if (!CheckBodyRetType(ctx, fb)) { // Update 'fb.ty' witch updated 'fb.retType->ty'. fb.ty = typeManager.GetFunctionTy(paramTys, fb.retType->ty, {isCFunc, false, hasVariableLenArg}); return false; } if (isCFFIBackend) { UnsafeCheck(fb); } funcTy = typeManager.GetFunctionTy(paramTys, fb.retType->ty, {isCFunc, false, hasVariableLenArg}); // Update funcDecl's type after body is checked. if (fb.funcDecl) { fb.funcDecl->ty = funcTy; } fb.ty = funcTy; return true; } namespace { /** * Add return type(Unit) for function */ void AddUnitType(const AST::FuncDecl& fd) { if (fd.funcBody == nullptr || fd.funcBody->retType) { return; } OwnedPtr type = MakeOwnedNode(); type->str = "Unit"; type->kind = TypeKind::TYPE_UNIT; if (!fd.funcBody->paramLists.empty()) { type->begin = fd.funcBody->paramLists[0]->end; type->end = fd.funcBody->paramLists[0]->end; } type->ty = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); fd.funcBody->retType = std::move(type); fd.funcBody->retType->EnableAttr(Attribute::COMPILER_ADD); } /** * Add return type for property member decl */ void AddReturnTypeForPropMemDecl(PropDecl& pd) { for (auto& getter : pd.getters) { CJC_NULLPTR_CHECK(getter); CJC_NULLPTR_CHECK(getter->funcBody); if (pd.type && !getter->funcBody->retType) { getter->funcBody->retType = ASTCloner::Clone(pd.type.get()); } } for (auto& setter : pd.setters) { if (!pd.type) { continue; } CJC_NULLPTR_CHECK(setter); CJC_NULLPTR_CHECK(setter->funcBody); if (setter->funcBody->paramLists.empty() || setter->funcBody->paramLists[0]->params.empty()) { return; } if (!setter->funcBody->paramLists[0]->params[0]->type) { setter->funcBody->paramLists[0]->params[0]->type = ASTCloner::Clone(pd.type.get()); } AddUnitType(*setter); } } /** * Add Getter/Setter in the abstract property(no body) of Interface/Class. */ void AddSetterGetterInProp(const InheritableDecl& cld) { for (auto decl : cld.GetMemberDeclPtrs()) { CJC_ASSERT(decl); if (decl->astKind != ASTKind::PROP_DECL || decl->TestAttr(Attribute::IMPORTED) || decl->TestAttr(Attribute::FROM_COMMON_PART)) { continue; } // abstract propDecl and commonWithoutDefault propDecl need add default getter and setter if (!decl->TestAttr(Attribute::ABSTRACT) && !IsCommonWithoutDefault(*decl)) { continue; } auto propDecl = RawStaticCast(decl); // Getter OwnedPtr getter = MakeOwnedNode(); getter->propDecl = propDecl; getter->identifier = "get"; getter->isGetter = true; getter->CloneAttrs(*propDecl); getter->DisableAttr(Attribute::MUT); getter->EnableAttr(Attribute::IMPLICIT_ADD); getter->EnableAttr(Attribute::COMPILER_ADD); if (cld.astKind == ASTKind::INTERFACE_DECL) { getter->EnableAttr(Attribute::PUBLIC); } auto getFuncParamList = MakeOwnedNode(); OwnedPtr getFuncBody = MakeOwnedNode(); getFuncBody->paramLists.push_back(std::move(getFuncParamList)); getter->funcBody = std::move(getFuncBody); propDecl->getters.emplace_back(std::move(getter)); if (!propDecl->isVar) { continue; } // Setter OwnedPtr setter = MakeOwnedNode(); setter->propDecl = propDecl; setter->isSetter = true; setter->identifier = "set"; setter->CloneAttrs(*propDecl); setter->DisableAttr(Attribute::MUT); setter->EnableAttr(Attribute::IMPLICIT_ADD); if (cld.astKind == ASTKind::INTERFACE_DECL) { setter->EnableAttr(Attribute::PUBLIC); } auto setFuncParamList = MakeOwnedNode(); auto param = CreateFuncParam("set"); setFuncParamList->params.push_back(std::move(param)); OwnedPtr setFuncBody = MakeOwnedNode(); setFuncBody->paramLists.push_back(std::move(setFuncParamList)); setter->funcBody = std::move(setFuncBody); propDecl->setters.emplace_back(std::move(setter)); } } constexpr std::string_view INOUT_TEMP_VALUE{"temporary value"}; constexpr std::string_view INOUT_IMMUTABLE{"is a immutable variable"}; } // namespace bool TypeChecker::TypeCheckerImpl::CheckReturnThisInFuncBody(const FuncBody& fb) const { // traverse funcBody to update returnThis which will be set to true only in two cases: // 1. return this // 2. return calling other member function which return This. // 3. return other expression which type is 'This' (NOTE: incorrect implementation, should be fixed). std::function)> checkExprType = [&checkExprType](Ptr expr) { while (expr->desugarExpr) { expr = expr->desugarExpr.get(); } if (auto refExpr = DynamicCast(expr); refExpr) { return refExpr->isThis; } else if (auto ce = DynamicCast(expr); ce && ce->resolvedFunction) { bool isMemberCall = Is(ce->baseFunc.get()); if (auto ma = DynamicCast(ce->baseFunc.get()); ma && ma->baseExpr) { isMemberCall = IsThisOrSuper(*ma->baseExpr); } return isMemberCall && IsFuncReturnThisType(*ce->resolvedFunction); } else if (auto paren = DynamicCast(expr)) { return checkExprType(paren->expr); } bool returnThis = false; auto visitor = [&returnThis, &checkExprType](Ptr n) { CJC_ASSERT(n); if (n->astKind != ASTKind::RETURN_EXPR) { return VisitAction::WALK_CHILDREN; } auto re = RawStaticCast(n); returnThis = checkExprType(re->expr.get()); return VisitAction::SKIP_CHILDREN; }; Walker(expr, visitor).Walk(); return returnThis; }; if (fb.body != nullptr && !fb.body->body.empty()) { auto lastNode = fb.body->body.back().get(); if (auto expr = DynamicCast(lastNode); expr) { while (expr->desugarExpr) { expr = expr->desugarExpr.get(); } // When function's last expression has 'This' type and current funcBody has parent class decl, // 'This' type is valid. return checkExprType(expr) && Is(fb.parentClassLike); } } return false; } void TypeChecker::TypeCheckerImpl::CheckCtorFuncBody(ASTContext& ctx, FuncBody& fb) { CJC_ASSERT(fb.funcDecl); // Ctor must have related funcDecl. if (fb.parentClassLike) { if (auto attr = HasJavaAttr(*fb.parentClassLike); attr) { fb.funcDecl->EnableAttr(attr.value()); } } Ptr ctorTy = TypeManager::GetInvalidTy(); if (fb.funcDecl->TestAttr(Attribute::STATIC)) { // Static init always has type of 'Unit' as return type. ctorTy = TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); } else { if (fb.parentStruct) { ctorTy = fb.parentStruct->ty; } if (fb.parentClassLike) { ctorTy = fb.parentClassLike->ty; } } if (!Ty::IsTyCorrect(ctorTy) || fb.paramLists.empty()) { return; } CheckFuncParamList(ctx, *fb.paramLists[0].get()); auto paramTys = GetFuncBodyParamTys(fb); fb.ty = typeManager.GetFunctionTy(paramTys, ctorTy); fb.funcDecl->ty = fb.ty; fb.retType->ty = ctorTy; Synthesize(ctx, fb.body.get()); } void TypeChecker::TypeCheckerImpl::CheckFuncParamList(ASTContext& ctx, FuncParamList& fpl) { // We use the tupleType to handle the type of FuncParamList. std::vector> paramTys; if (fpl.params.empty()) { fpl.ty = typeManager.GetTupleTy(paramTys); return; } for (auto& param : fpl.params) { CJC_NULLPTR_CHECK(param); if (!Ty::IsTyCorrect(Synthesize(ctx, param.get()))) { paramTys.push_back(TypeManager::GetInvalidTy()); // Set type to get correct size of function type. continue; } Ptr paramTy = param->ty; paramTys.push_back(paramTy); // Infer param assignment's type by param's type. if (param->assignment) { auto curNode = param->assignment.get(); auto expr = param->assignment.get(); // Need infer for desugarExpr otherwise node's type will be removed. while (expr != nullptr && expr->desugarExpr != nullptr) { curNode = expr->desugarExpr.get(); expr = As(curNode); } (void)Check(ctx, paramTy, curNode); // Error is reported during checking, return value can be ignored. if (Ty::IsTyCorrect(curNode->ty)) { param->assignment->ty = curNode->ty; } } } fpl.ty = typeManager.GetTupleTy(paramTys); } bool TypeChecker::TypeCheckerImpl::ChkFuncArg(ASTContext& ctx, Ty& target, FuncArg& fa) { if (!fa.expr) { fa.ty = TypeManager::GetInvalidTy(); return false; } if (fa.withInout) { return ChkFuncArgWithInout(ctx, target, fa); } if (!Check(ctx, &target, fa.expr.get())) { fa.ty = TypeManager::GetInvalidTy(); return false; } fa.ty = fa.expr->ty; return true; } bool TypeChecker::TypeCheckerImpl::ChkFuncArgWithInout(ASTContext& ctx, Ty& target, FuncArg& fa) { auto realTarget = ⌖ auto exprTy = fa.expr->ty; if (!Ty::IsTyCorrect(fa.expr->ty)) { // Trying to infer fa type without target. exprTy = Synthesize(ctx, fa.expr.get()); } auto argTy = DynamicCast(exprTy); auto ptrParamTy = DynamicCast(&target); auto varrParamTy = DynamicCast(&target); // Case 1: 'argTy' is VArray, target is CPointer. 'realTarget' trans to // VArray. // Case 2: 'argTy' is VArray, target is VArray. 'realTarget' is VArray. // Other case: 'argTy' is T, target is CPointer. 'realTarget' trans to T. if (argTy && ptrParamTy) { realTarget = typeManager.GetVArrayTy(*ptrParamTy->typeArgs[0], argTy->size); } else if (argTy && varrParamTy) { realTarget = ⌖ } else if (ptrParamTy) { realTarget = ptrParamTy->typeArgs[0]; } if (!Ty::IsTyCorrect(exprTy) || !typeManager.IsSubtype(exprTy, realTarget)) { if (Ty::IsTyCorrect(exprTy)) { DiagMismatchedTypes(diag, *fa.expr, *realTarget); } fa.ty = TypeManager::GetInvalidTy(); return false; } if (!ChkInoutFuncArg(fa)) { fa.ty = TypeManager::GetInvalidTy(); return false; } fa.ty = typeManager.GetPointerTy(Is(realTarget) ? realTarget->typeArgs[0].get() : realTarget); return true; } bool TypeChecker::TypeCheckerImpl::ChkInoutFuncArg(const FuncArg& fa) { switch (fa.expr->astKind) { case ASTKind::REF_EXPR: return ChkInoutRefExpr(*StaticCast(fa.expr.get())); case ASTKind::MEMBER_ACCESS: return ChkInoutMemberAccess(*StaticCast(fa.expr.get())); case ASTKind::LIT_CONST_EXPR: { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_must_be_var_variable, *fa.expr); builder.AddMainHintArguments("literal"); return false; } default: { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_must_be_var_variable, *fa.expr); builder.AddMainHintArguments(std::string(INOUT_TEMP_VALUE)); return false; } } } bool TypeChecker::TypeCheckerImpl::ChkInoutRefExpr(RefExpr& re, bool isBase) { auto target = GetRealTarget(&re, re.GetTarget()); bool thisOrSuper = re.isThis || re.isSuper; if (isBase && thisOrSuper) { return true; } if (thisOrSuper || !target || !Utils::In(target->astKind, {ASTKind::VAR_DECL, ASTKind::FUNC_PARAM})) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_must_be_var_variable, re); builder.AddMainHintArguments("not a variable"); return false; } auto vd = StaticAs(target); if (!vd->isVar) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_must_be_var_variable, re); builder.AddMainHintArguments(std::string(INOUT_IMMUTABLE)); return false; } if (vd->outerDecl && vd->outerDecl->IsNominalDecl()) { if (vd->outerDecl->astKind != ASTKind::STRUCT_DECL && !vd->TestAttr(Attribute::STATIC)) { (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_modify_heap_variable, re); return false; } } return true; } /** * For member access a.n: * - if a is VArray value and n is "size", it's desugar as a LitConstExpr, report error; * - if a or n defined with 'let', report error; * - if a is class object, report error. */ bool TypeChecker::TypeCheckerImpl::ChkInoutMemberAccess(const MemberAccess& ma) { // Currently, `ma.desugarExpr` can only be `VArray.size`. if (ma.desugarExpr && ma.desugarExpr->astKind == ASTKind::LIT_CONST_EXPR) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_must_be_var_variable, ma); builder.AddMainHintArguments(std::string(INOUT_IMMUTABLE)); return false; } if (!ma.target) { return false; } bool meetContraints = true; if (ma.target->astKind == ASTKind::VAR_DECL) { auto vd = StaticAs(ma.target); if (!vd->isVar) { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_must_be_var_variable, ma.field.Begin()); builder.AddMainHintArguments(std::string(INOUT_IMMUTABLE)); meetContraints = false; } if (vd->TestAttr(Attribute::STATIC)) { return meetContraints; } } else { auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_must_be_var_variable, ma); if (ma.target->astKind == ASTKind::FUNC_DECL) { builder.AddMainHintArguments("function declaration"); } else { builder.AddMainHintArguments(std::string(INOUT_TEMP_VALUE)); } meetContraints = false; } auto& be = ma.baseExpr; if (Ty::IsTyCorrect(be->ty) && be->ty->IsClassLike()) { diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_modify_heap_variable, *be); return false; } if (be->astKind == ASTKind::REF_EXPR) { return ChkInoutRefExpr(static_cast(*be), true) && meetContraints; } if (be->astKind == ASTKind::MEMBER_ACCESS) { if (auto beTarget = GetRealTarget(be.get(), be->GetTarget()); beTarget && beTarget->astKind != ASTKind::PACKAGE_DECL) { return ChkInoutMemberAccess(static_cast(*be)) && meetContraints; } return false; } auto builder = diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_must_be_var_variable, *be); builder.AddMainHintArguments(std::string(INOUT_TEMP_VALUE)); return false; } Ptr TypeChecker::TypeCheckerImpl::SynFuncArg(ASTContext& ctx, FuncArg& fa) { if (fa.expr) { Synthesize(ctx, fa.expr.get()); if (fa.withInout) { if (!Ty::IsTyCorrect(fa.expr->ty) || !ChkInoutFuncArg(fa)) { fa.ty = TypeManager::GetInvalidTy(); } else if (!Ty::IsMetCType(*fa.expr->ty)) { diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_modify_non_ctype, fa); fa.ty = TypeManager::GetInvalidTy(); } else if (fa.expr->ty->IsCString()) { diag.DiagnoseRefactor(DiagKindRefactor::sema_inout_modify_cstring_or_zerosized, fa, "type 'CString'"); fa.ty = TypeManager::GetInvalidTy(); } else if (Is(fa.expr->ty)) { fa.ty = typeManager.GetPointerTy(fa.expr->ty->typeArgs[0]); } else { fa.ty = typeManager.GetPointerTy(fa.expr->ty); } } else { fa.ty = fa.expr->ty; } } else { fa.ty = TypeManager::GetInvalidTy(); } return fa.ty; } void TypeChecker::TypeCheckerImpl::SubstituteTypeArguments( const TypeAliasDecl& tad, std::vector>& typeArguments, const TypeSubst& typeMapping) { if (!typeArguments.empty() && typeArguments[0]->TestAttr(Attribute::COMPILER_ADD)) { return; } if (auto rt = DynamicCast(tad.type.get())) { SubstituteTypeArguments(typeArguments, *rt, typeMapping); } if (auto qt = DynamicCast(tad.type.get())) { SubstituteTypeArguments(typeArguments, *qt, typeMapping); } } Ptr TypeChecker::TypeCheckerImpl::SubstituteTypeAliasInTy( AST::Ty& ty, bool needSubstituteGeneric, const TypeSubst& typeMapping) { if (!Ty::IsTyCorrect(&ty)) { return TypeManager::GetInvalidTy(); } if (ty.kind <= TypeKind::TYPE_BOOLEAN) { return &ty; } std::vector> typeArgs = RecursiveSubstituteTypeAliasInTy(&ty, needSubstituteGeneric, typeMapping); switch (ty.kind) { case TypeKind::TYPE_CLASS: { if (auto ctt = DynamicCast(&ty); ctt) { return typeManager.GetClassThisTy(*ctt->declPtr, typeArgs); } return typeManager.GetClassTy(*static_cast(ty).declPtr, typeArgs); } case TypeKind::TYPE_STRUCT: { return typeManager.GetStructTy(*static_cast(ty).declPtr, typeArgs); } case TypeKind::TYPE_INTERFACE: { return typeManager.GetInterfaceTy(*static_cast(ty).declPtr, typeArgs); } case TypeKind::TYPE_ENUM: { return typeManager.GetEnumTy(*static_cast(ty).declPtr, typeArgs); } case TypeKind::TYPE_FUNC: { auto returnTy = typeArgs.back(); typeArgs.pop_back(); auto& funcTy = static_cast(ty); return typeManager.GetFunctionTy(typeArgs, returnTy, {funcTy.isC, false, funcTy.hasVariableLenArg}); } case TypeKind::TYPE: { auto inner = GetUnaliasedTypeFromTypeAlias(static_cast(ty), typeArgs); if (auto nestedAlias = DynamicCast(inner)) { auto type = Ty::GetDeclPtrOfTy(nestedAlias); // the aliased type is in cycle, stop recursive substitution to avoid endless loop if (type && type->TestAttr(Attribute::IN_REFERENCE_CYCLE)) { return nestedAlias; } return SubstituteTypeAliasInTy(*nestedAlias, needSubstituteGeneric, typeMapping); } return inner; } case TypeKind::TYPE_TUPLE: { return typeManager.GetTupleTy(typeArgs); } case TypeKind::TYPE_ARRAY: { auto& arrayTy = static_cast(ty); return typeManager.GetArrayTy(typeArgs[0], arrayTy.dims); } case TypeKind::TYPE_VARRAY: { auto& varrayTy = static_cast(ty); CJC_ASSERT(!typeArgs.empty() && typeArgs[0] != nullptr); return typeManager.GetVArrayTy(*typeArgs[0], varrayTy.size); } case TypeKind::TYPE_POINTER: { return typeManager.GetPointerTy(typeArgs[0]); } case TypeKind::TYPE_GENERICS: { if (!needSubstituteGeneric) { return &ty; } auto found = typeMapping.find(StaticCast(&ty)); if (found != typeMapping.end()) { return found->second; } // This type will not be used, just for placeholder and marking current is substituted with typealias. return typeManager.GetIntersectionTy({&ty}); } default: return &ty; } } std::vector> TypeChecker::TypeCheckerImpl::RecursiveSubstituteTypeAliasInTy( Ptr ty, bool needSubstituteGeneric, const TypeSubst& typeMapping) { CJC_ASSERT(ty); // Caller guarantees; std::vector> typeArgs; for (auto typeArg : ty->typeArgs) { CJC_ASSERT(typeArg); if (Ty::IsTyCorrect(typeArg) || needSubstituteGeneric) { auto noTypeAliasArg = SubstituteTypeAliasInTy(*typeArg, needSubstituteGeneric, typeMapping); typeArgs.push_back(noTypeAliasArg); } else { typeArgs.push_back(typeArg); } } return typeArgs; } Ptr TypeChecker::TypeCheckerImpl::GetUnaliasedTypeFromTypeAlias( const TypeAliasTy& target, const std::vector>& typeArgs) { CJC_NULLPTR_CHECK(target.declPtr->type); auto aliasedType = target.declPtr->type.get(); // Since 'SubstituteTypeAliasInTy' was called from inner to outer, we only need to substitute current type. // Only need to substitute with given typeArgument for given alias target. TypeSubst typeMapping = GenerateTypeMapping(*target.declPtr, typeArgs); Ptr type = typeManager.GetInstantiatedTy(aliasedType->ty, typeMapping); CJC_ASSERT(target.declPtr); return type; } namespace { bool IsNodeDesugared(Ptr node) { if (auto expr = DynamicCast(node)) { return expr->desugarExpr.get(); } return false; } CacheKey GetCacheKeyForSyn(const ASTContext& ctx, Ptr node) { auto it = ctx.targetTypeMap.find(node); auto target = it != ctx.targetTypeMap.cend() ? it->second : nullptr; return CacheKey{ .target = target, .isDesugared = IsNodeDesugared(node), .diagKey = DiagnosticCache::ExtractKey(ctx.diag)}; } CacheKey GetCacheKeyForChk(const ASTContext& ctx, Ptr node, Ptr target) { return CacheKey{ .target = target, .isDesugared = IsNodeDesugared(node), .diagKey = DiagnosticCache::ExtractKey(ctx.diag)}; } void RestoreCached(ASTContext& ctx, Ptr node, CacheEntry& cache, bool recoverDiag = true) { if (Ty::IsInitialTy(node->ty)) { // if node cleared before, mark it must be rechecked in post-check before restoring its ty ctx.typeCheckCache[node].lastKey = {}; } node->ty = cache.result; RestoreTargets(*node, cache.targets); if (recoverDiag) { cache.diags.Restore(ctx.diag); } } } // namespace bool TypeChecker::TypeCheckerImpl::IsChecked(ASTContext& ctx, Node& node) const { auto decl = As(&node); auto type = As(&node); bool visitedDecl = decl && (decl->TestAttr(Attribute::IS_CHECK_VISITED) || decl->TestAttr(Attribute::IMPORTED)); bool visitedExpr = !decl && !type && ctx.typeCheckCache[&node].lastKey.has_value() && typeManager.GetUnsolvedTyVars().empty(); return Ty::IsTyCorrect(node.ty) && node.ty->kind != AST::TypeKind::TYPE_QUEST && (visitedExpr || visitedDecl || (type && type->TestAttr(Attribute::IS_CHECK_VISITED))); } std::optional> TypeChecker::TypeCheckerImpl::PerformBasicChecksForSynthesize( ASTContext& ctx, Ptr node) const { if (!node) { return {TypeManager::GetInvalidTy()}; } // IS_BROKEN indicates that the node may be illegal, and it may cause unknown errors if it continues. if (node->TestAttr(Attribute::IS_BROKEN)) { node->ty = TypeManager::GetInvalidTy(); return {node->ty}; } if (IsChecked(ctx, *node)) { return {node->ty}; } if (node->IsDecl() && !node->symbol) { return {node->ty}; } return {}; } Ptr TypeChecker::TypeCheckerImpl::Synthesize(ASTContext& ctx, Ptr node) { if (auto res = PerformBasicChecksForSynthesize(ctx, node)) { return *res; } ctx.typeCheckCache[node].lastKey = GetCacheKeyForSyn(ctx, node); ASTContext* curCtx = &ctx; // If decl belongs to another package node, then switch to another AST context according to package node. if (ci->GetSourcePackages().size() > 1 && node->curFile) { if (auto ctx1 = ci->GetASTContextByPackage(node->curFile->curPackage)) { curCtx = ctx1; } } if (auto decl = DynamicCast(node)) { auto stashDiagnoseStatus = diag.AutoStashDisableDiagnoseStatus(); CheckAnnotations(ctx, *decl); } switch (node->astKind) { case ASTKind::FUNC_DECL: { auto stashDiagnoseStatus = diag.AutoStashDisableDiagnoseStatus(); if (StaticAs(node)->ownerFunc) { // Needn't report any error when check the default param desugar function. auto ds = DiagSuppressor(diag); CheckFuncDecl(*curCtx, *StaticAs(node)); } else { CheckFuncDecl(*curCtx, *StaticAs(node)); } break; } case ASTKind::FUNC_BODY: { (void)CheckFuncBody(*curCtx, *StaticAs(node)); break; } case ASTKind::BLOCK: { node->ty = SynBlock(*curCtx, *StaticAs(node)); break; } case ASTKind::FUNC_PARAM_LIST: { CheckFuncParamList(*curCtx, *StaticAs(node)); break; } case ASTKind::FUNC_PARAM: { node->ty = SynFuncParam(*curCtx, *StaticAs(node)); break; } case ASTKind::FUNC_ARG: { node->ty = SynFuncArg(*curCtx, *StaticAs(node)); break; } case ASTKind::INC_OR_DEC_EXPR: { node->ty = SynIncOrDecExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::PAREN_EXPR: { node->ty = SynParenExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::LAMBDA_EXPR: { node->ty = SynLamExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::RETURN_EXPR: { node->ty = SynReturnExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::LIT_CONST_EXPR: { node->ty = SynLitConstExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::WHILE_EXPR: { node->ty = SynWhileExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::DO_WHILE_EXPR: { node->ty = SynDoWhileExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::FOR_IN_EXPR: { node->ty = SynForInExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::UNARY_EXPR: { node->ty = SynUnaryExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::BINARY_EXPR: { node->ty = SynBinaryExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::ASSIGN_EXPR: { node->ty = SynAssignExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::QUOTE_EXPR: { node->ty = SynQuoteExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::IF_EXPR: { node->ty = SynIfExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::TRY_EXPR: { node->ty = SynTryExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::REF_EXPR: { auto re = StaticAs(node); InferRefExpr(*curCtx, *re); break; } case ASTKind::PRIMITIVE_TYPE_EXPR: { auto te = StaticAs(node); te->ty = TypeManager::GetPrimitiveTy(te->typeKind); break; } case ASTKind::CALL_EXPR: { node->ty = SynCallExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::TRAIL_CLOSURE_EXPR: { node->ty = SynTrailingClosure(ctx, *StaticAs(node)); break; } case ASTKind::MEMBER_ACCESS: { auto ma = StaticAs(node); InferMemberAccess(*curCtx, *ma); break; } case ASTKind::TYPE_CONV_EXPR: { node->ty = SynTypeConvExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::IF_AVAILABLE_EXPR: node->ty = SynIfAvailableExpr(*curCtx, StaticCast(*node)); break; case ASTKind::ARRAY_LIT: { node->ty = SynArrayLit(*curCtx, *StaticAs(node)); break; } case ASTKind::ARRAY_EXPR: { auto ae = StaticAs(node); node->ty = ae->isValueArray ? SynVArrayExpr(*curCtx, *ae) : SynArrayExpr(*curCtx, *ae); break; } case ASTKind::POINTER_EXPR: { node->ty = SynPointerExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::MATCH_EXPR: { node->ty = SynMatchExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::IS_EXPR: { node->ty = SynIsExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::AS_EXPR: { node->ty = SynAsExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::OPTIONAL_CHAIN_EXPR: { node->ty = SynOptionalChainExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::ENUM_DECL: { CheckEnumDecl(*curCtx, *StaticAs(node)); break; } case ASTKind::STRUCT_DECL: { auto sd = StaticAs(node); CheckStructDecl(*curCtx, *sd); break; } case ASTKind::CLASS_DECL: { auto cd = StaticAs(node); CheckClassDecl(*curCtx, *cd); break; } case ASTKind::INTERFACE_DECL: { auto id = StaticAs(node); CheckInterfaceDecl(*curCtx, *id); break; } case ASTKind::TUPLE_LIT: { node->ty = SynTupleLit(*curCtx, *StaticAs(node)); break; } case ASTKind::JUMP_EXPR: { node->ty = SynLoopControlExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::THROW_EXPR: { node->ty = SynThrowExpr(*curCtx, *StaticAs(node)); break; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // Effect handlers are only enabled in the CJNative backend, for now case ASTKind::PERFORM_EXPR: { node->ty = SynPerformExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::RESUME_EXPR: { node->ty = SynResumeExpr(*curCtx, *StaticAs(node)); break; } #endif // CANGJIE_CODEGEN_CJNATIVE_BACKEND case ASTKind::TYPE_ALIAS_DECL: { CheckTypeAlias(*curCtx, *StaticAs(node)); break; } case ASTKind::SUBSCRIPT_EXPR: { node->ty = SynSubscriptExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::RANGE_EXPR: { node->ty = SynRangeExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::TYPE: case ASTKind::INVALID_TYPE: case ASTKind::OPTION_TYPE: case ASTKind::THIS_TYPE: case ASTKind::PAREN_TYPE: case ASTKind::VARRAY_TYPE: case ASTKind::FUNC_TYPE: case ASTKind::TUPLE_TYPE: case ASTKind::PRIMITIVE_TYPE: case ASTKind::REF_TYPE: // Fall through !. case ASTKind::QUALIFIED_TYPE: { CheckReferenceTypeLegality(ctx, *StaticAs(node)); break; } case ASTKind::PROP_DECL: { auto propDecl = StaticAs(node); CheckPropDecl(*curCtx, *propDecl); break; } case ASTKind::VAR_WITH_PATTERN_DECL: { auto vpd = StaticAs(node); CheckVarWithPatternDecl(*curCtx, *vpd); break; } case ASTKind::VAR_DECL: { if (IsGlobalOrMember(*node)) { // When varDecl is global or member variable, the diable status should be stashed. auto stashDiagnoseStatus = diag.AutoStashDisableDiagnoseStatus(); CheckVarDecl(*curCtx, *StaticAs(node)); } else { CheckVarDecl(*curCtx, *StaticAs(node)); } break; } case ASTKind::SPAWN_EXPR: { node->ty = SynSpawnExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::SYNCHRONIZED_EXPR: { node->ty = SynSyncExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::EXTEND_DECL: { CheckExtendDecl(*curCtx, *StaticAs(node)); break; } case ASTKind::MACRO_EXPAND_EXPR: // Just for LSP, normal process does not have macros here. case ASTKind::MACRO_EXPAND_PARAM: case ASTKind::MACRO_EXPAND_DECL: { CheckMacroCall(*curCtx, *node); break; } case ASTKind::INVALID_EXPR: { node->ty = TypeManager::GetInvalidTy(); break; } default: { break; } } CJC_ASSERT(!Ty::IsTyCorrect(node->ty) || As(node) == nullptr || StaticAs(node)->desugarExpr == nullptr || node->ty == StaticAs(node)->desugarExpr->ty); node->ty = typeManager.TryGreedySubst(node->ty); return TypeManager::GetNonNullTy(node->ty); } std::optional TypeChecker::TypeCheckerImpl::PerformBasicChecksForCheck( ASTContext& ctx, Ptr target, Ptr node) const { if (!node) { return {false}; } if (!Ty::IsTyCorrect(target)) { node->ty = TypeManager::GetInvalidTy(); return {false}; } ctx.targetTypeMap[node] = target; // IS_BROKEN indicates that the node may be illegal, and it may cause unknown errors if it continues. if (node->TestAttr(Attribute::IS_BROKEN)) { node->ty = TypeManager::GetInvalidTy(); return {false}; } if (target->HasQuestTy() && !IsQuestableNode(*node)) { return {false}; } return {}; } bool TypeChecker::TypeCheckerImpl::Check(ASTContext& ctx, Ptr target, Ptr node) { if (auto res = PerformBasicChecksForCheck(ctx, target, node)) { return *res; } ctx.typeCheckCache[node].lastKey = GetCacheKeyForChk(ctx, node, target); ASTContext* curCtx = &ctx; // If decl belongs to another package node, then switch to another AST context according to package node. if (ci->GetSourcePackages().size() > 1) { if (auto decl = AST::As(node); decl && decl->curFile) { if (auto ctx1 = ci->GetASTContextByPackage(decl->curFile->curPackage)) { curCtx = ctx1; } } } bool chkRet = false; auto realTarget = typeManager.TryGreedySubst(target); if (realTarget->IsPlaceholder() && !AcceptPlaceholderTarget(*node)) { auto& cst = typeManager.constraints[RawStaticCast(realTarget)]; Ptr lub = nullptr; if (!cst.ubs.empty()) { auto meetRes = JoinAndMeet(typeManager, cst.ubs.raw(), typeManager.GetUnsolvedTyVars()).MeetAsVisibleTy(); if (std::holds_alternative>(meetRes)) { lub = std::get>(meetRes); } } if (lub) { chkRet = Check(ctx, lub, node) && typeManager.IsSubtype(node->ty, realTarget); } else { Synthesize(ctx, node); ReplaceIdealTy(*node); chkRet = typeManager.IsSubtype(node->ty, realTarget); } } else { switch (node->astKind) { case ASTKind::IF_EXPR: { chkRet = ChkIfExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::ASSIGN_EXPR: { chkRet = ChkAssignExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::LIT_CONST_EXPR: { auto lce = StaticAs(node); chkRet = ChkLitConstExpr(*curCtx, *realTarget, *lce); InitializeLitConstValue(*lce); break; } case ASTKind::ARRAY_LIT: { chkRet = ChkArrayLit(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::ARRAY_EXPR: { auto ae = StaticAs(node); chkRet = ae->isValueArray ? ChkVArrayExpr(*curCtx, *realTarget, *ae) : ChkArrayExpr(*curCtx, *realTarget, *ae); break; } case ASTKind::POINTER_EXPR: { chkRet = ChkPointerExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::TUPLE_LIT: { chkRet = ChkTupleLit(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::WHILE_EXPR: { chkRet = ChkWhileExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::DO_WHILE_EXPR: { chkRet = ChkDoWhileExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::FOR_IN_EXPR: { chkRet = ChkForInExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::RANGE_EXPR: { chkRet = ChkRangeExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::PAREN_EXPR: { chkRet = ChkParenExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::BINARY_EXPR: { chkRet = ChkBinaryExpr(ctx, *realTarget, *StaticAs(node)); break; } case ASTKind::INC_OR_DEC_EXPR: { chkRet = ChkIncOrDecExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::UNARY_EXPR: { chkRet = ChkUnaryExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::TYPE_CONV_EXPR: { chkRet = ChkTypeConvExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::IF_AVAILABLE_EXPR: chkRet = ChkIfAvailableExpr(*curCtx, *realTarget, StaticCast(*node)); break; case ASTKind::JUMP_EXPR: chkRet = ChkLoopControlExpr(*curCtx, *StaticAs(node)); break; case ASTKind::MATCH_EXPR: { chkRet = ChkMatchExpr(*curCtx, *realTarget, *StaticAs(node)); break; } // These patterns are supported, so do type infer framework. case ASTKind::EXCEPT_TYPE_PATTERN: case ASTKind::COMMAND_TYPE_PATTERN: case ASTKind::WILDCARD_PATTERN: case ASTKind::CONST_PATTERN: case ASTKind::TYPE_PATTERN: case ASTKind::VAR_PATTERN: case ASTKind::TUPLE_PATTERN: case ASTKind::ENUM_PATTERN: case ASTKind::VAR_OR_ENUM_PATTERN: { // Use type check of local type infer framework. chkRet = ChkPattern(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::BLOCK: { chkRet = ChkBlock(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::SUBSCRIPT_EXPR: { chkRet = ChkSubscriptExpr(*curCtx, realTarget, *StaticAs(node)); break; } case ASTKind::MEMBER_ACCESS: // Fall-through. case ASTKind::REF_EXPR: { chkRet = ChkRefExpr(*curCtx, *realTarget, *RawStaticCast(node)); break; } case ASTKind::CALL_EXPR: { chkRet = ChkCallExpr(*curCtx, realTarget, *StaticAs(node)); break; } case ASTKind::TRAIL_CLOSURE_EXPR: { chkRet = ChkTrailingClosureExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::TRY_EXPR: { chkRet = ChkTryExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::RETURN_EXPR: { chkRet = ChkReturnExpr(*curCtx, *StaticAs(node)); break; } case ASTKind::LAMBDA_EXPR: { chkRet = ChkLamExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::QUOTE_EXPR: { chkRet = ChkQuoteExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::FUNC_PARAM: { chkRet = ChkFuncParam(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::FUNC_ARG: { chkRet = ChkFuncArg(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::SPAWN_EXPR: { chkRet = ChkSpawnExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::SYNCHRONIZED_EXPR: { chkRet = ChkSyncExpr(*curCtx, realTarget, *StaticAs(node)); break; } case ASTKind::IS_EXPR: { chkRet = ChkIsExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::AS_EXPR: { chkRet = ChkAsExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::OPTIONAL_CHAIN_EXPR: { chkRet = ChkOptionalChainExpr(*curCtx, *realTarget, *StaticAs(node)); break; } case ASTKind::MACRO_EXPAND_EXPR: // Just for LSP, normal process does not have macros here. case ASTKind::MACRO_EXPAND_PARAM: case ASTKind::MACRO_EXPAND_DECL: { CheckMacroCall(*curCtx, *node); break; } default: { Synthesize(ctx, node); ReplaceIdealTy(*node); chkRet = typeManager.IsSubtype(node->ty, realTarget); break; } } } CJC_ASSERT(!Ty::IsTyCorrect(node->ty) || As(node) == nullptr || StaticAs(node)->desugarExpr == nullptr || node->ty == StaticAs(node)->desugarExpr->ty); ctx.targetTypeMap[node] = nullptr; node->ty = typeManager.TryGreedySubst(node->ty); return chkRet; } void TypeChecker::TypeCheckerImpl::CheckConstructor(ASTContext& ctx, const Decl& decl, FuncDecl& fd) { if (decl.astKind != ASTKind::CLASS_DECL && decl.astKind != ASTKind::STRUCT_DECL) { return; } if (!fd.funcBody) { return; } bool hasBrokenBody = fd.funcBody->generic && !fd.TestAttr(Attribute::STATIC) && !HasJavaAttr(decl); if (hasBrokenBody) { diag.Diagnose(fd, DiagKind::sema_forbid_generic_constructor, fd.identifier.Val()); } if (fd.funcBody->paramLists.size() > 1) { diag.Diagnose(*fd.funcBody->paramLists[0], DiagKind::sema_cannot_currying, "constructor"); hasBrokenBody = true; } if (hasBrokenBody) { fd.funcBody->EnableAttr(Attribute::IS_BROKEN); fd.EnableAttr(Attribute::HAS_BROKEN); } if (hasBrokenBody || !fd.funcBody->body || fd.funcBody->body->body.empty()) { return; } Ptr firstExprOrDecl = fd.funcBody->body->body.front().get(); if (firstExprOrDecl == nullptr) { return; } bool needEraseSuper{false}; if (auto ce = AST::As(firstExprOrDecl); ce) { CheckConstructorSuper(*ce, decl, fd, needEraseSuper); } CheckCallsInConstructor(ctx, decl, fd, *firstExprOrDecl, needEraseSuper); } void TypeChecker::TypeCheckerImpl::CheckCallsInConstructor( ASTContext& ctx, const Decl& decl, FuncDecl& fd, Node& firstExprOrDecl, bool needEraseSuper) { CJC_ASSERT(!fd.funcBody->body->body.empty()); // Caller guarantees. auto firstExprOrDeclPtr = &firstExprOrDecl; std::vector> calls; Walker(fd.funcBody.get(), [&calls](auto node) { if (auto ce = DynamicCast(node); ce) { calls.emplace_back(ce); } return VisitAction::WALK_CHILDREN; }).Walk(); for (auto ce : calls) { if (ce->baseFunc == nullptr || ce->baseFunc->astKind != ASTKind::REF_EXPR) { continue; } auto re = RawStaticCast(ce->baseFunc.get()); if (!re->isThis && !re->isSuper) { continue; } if (re->isThis && fd.TestAttr(Attribute::PRIMARY_CONSTRUCTOR)) { diag.Diagnose(*ce, DiagKind::sema_illegal_place_of_calling_this_primary_constructor); } else if (ce != firstExprOrDeclPtr) { Ptr tce = nullptr; if (firstExprOrDeclPtr && firstExprOrDeclPtr->astKind == ASTKind::TRAIL_CLOSURE_EXPR) { tce = RawStaticCast(firstExprOrDeclPtr); } auto notIllegalPlace = !tce || (ce->TestAttr(Attribute::COMPILER_ADD) && ce != tce->desugarExpr.get()); if (notIllegalPlace) { diag.Diagnose(*ce, DiagKind::sema_illegal_place_of_calling_this_or_super, re->ref.identifier.Val(), decl.astKind == ASTKind::CLASS_DECL ? "class" : "struct", decl.identifier.Val()); } } // Only should remove super call when current callExpr is the first expression in the function body. auto needReset = needEraseSuper && re->isSuper && ce->TestAttr(Attribute::COMPILER_ADD) && ce->args.empty() && decl.astKind == ASTKind::CLASS_DECL && ce == fd.funcBody->body->body.front().get(); if (needReset) { needEraseSuper = false; // Avoid re-enter erasion. // remove compiler added super() when there is no non-parameter constructor in super class auto it = fd.funcBody->body->body.begin(); ctx.DeleteInvertedIndexes(it->get()); it->reset(); fd.funcBody->body->body.erase(it); firstExprOrDeclPtr = nullptr; } if (decl.astKind == ASTKind::STRUCT_DECL) { fd.constructorCall = ConstructorCall::OTHER_INIT; } } } Ptr TypeChecker::TypeCheckerImpl::SynthesizeWithCache(ASTContext& ctx, Ptr node) { CJC_NULLPTR_CHECK(node); if (!typeManager.GetUnsolvedTyVars().empty()) { return Synthesize(ctx, node); } CacheKey key = GetCacheKeyForSyn(ctx, node); if (ctx.typeCheckCache[node].synCache.count(key) != 0) { auto& cache = ctx.typeCheckCache[node].synCache[key]; RestoreCached(ctx, node, cache); return cache.result; } else { return SynthesizeAndCache(ctx, node, key); } } bool TypeChecker::TypeCheckerImpl::CheckWithCache(ASTContext& ctx, Ptr target, Ptr node) { CJC_NULLPTR_CHECK(node); if (!typeManager.GetUnsolvedTyVars().empty()) { return Check(ctx, target, node); } CacheKey key = GetCacheKeyForChk(ctx, node, target); if (ctx.typeCheckCache[node].chkCache.count(key) != 0) { auto& cache = ctx.typeCheckCache[node].chkCache[key]; RestoreCached(ctx, node, cache); return cache.successful; } else { return CheckAndCache(ctx, target, node, key); } } Ptr TypeChecker::TypeCheckerImpl::SynthesizeWithNegCache(ASTContext& ctx, Ptr node) { CJC_NULLPTR_CHECK(node); if (!typeManager.GetUnsolvedTyVars().empty()) { return Synthesize(ctx, node); } CacheKey key = GetCacheKeyForSyn(ctx, node); if (ctx.typeCheckCache[node].synCache.count(key) != 0 && !ctx.typeCheckCache[node].synCache[key].successful) { auto& cache = ctx.typeCheckCache[node].synCache[key]; RestoreCached(ctx, node, cache); return cache.result; } else { return SynthesizeAndCache(ctx, node, key); } } bool TypeChecker::TypeCheckerImpl::CheckWithNegCache(ASTContext& ctx, Ptr target, Ptr node) { CJC_NULLPTR_CHECK(node); if (!typeManager.GetUnsolvedTyVars().empty()) { return Check(ctx, target, node); } CacheKey key = GetCacheKeyForChk(ctx, node, target); if (ctx.typeCheckCache[node].chkCache.count(key) != 0 && !ctx.typeCheckCache[node].chkCache[key].successful) { auto& cache = ctx.typeCheckCache[node].chkCache[key]; RestoreCached(ctx, node, cache); return false; } else { return CheckAndCache(ctx, target, node, key); } } bool TypeChecker::TypeCheckerImpl::CheckWithEffectiveCache( ASTContext& ctx, Ptr target, Ptr node, bool recoverDiag) { if (!typeManager.GetUnsolvedTyVars().empty() || !node) { return Check(ctx, target, node); } CacheKey key = GetCacheKeyForChk(ctx, node, target); if (!Ty::IsInitialTy(node->ty)) { if (ctx.typeCheckCache[node].lastKey && ctx.typeCheckCache[node].lastKey.value() == key) { if (ctx.typeCheckCache[node].chkCache.count(key) != 0) { auto& cache = ctx.typeCheckCache[node].chkCache[key]; RestoreCached(ctx, node, cache, recoverDiag); return cache.successful; } else { return CheckAndCache(ctx, target, node, key); } } } return CheckAndCache(ctx, target, node, key); } Ptr TypeChecker::TypeCheckerImpl::SynthesizeWithEffectiveCache(ASTContext& ctx, Ptr node, bool recoverDiag) { if (!typeManager.GetUnsolvedTyVars().empty() || !node) { return Synthesize(ctx, node); } CacheKey key = GetCacheKeyForSyn(ctx, node); if (!Ty::IsInitialTy(node->ty)) { if (ctx.typeCheckCache[node].lastKey && ctx.typeCheckCache[node].lastKey.value() == key) { if (ctx.typeCheckCache[node].synCache.count(key) != 0) { auto& cache = ctx.typeCheckCache[node].synCache[key]; RestoreCached(ctx, node, cache, recoverDiag); return cache.result; } else { return SynthesizeAndCache(ctx, node, key); } } } return SynthesizeAndCache(ctx, node, key); } Ptr TypeChecker::TypeCheckerImpl::SynthesizeAndCache(ASTContext& ctx, Ptr node, const CacheKey& key) { DiagnosticCache dc; dc.ToExclude(ctx.diag); auto ret = Synthesize(ctx, node); dc.BackUp(ctx.diag); ctx.typeCheckCache[node].synCache[key] = {.successful = ret && Ty::IsTyCorrect(ret) && dc.NoError(), .result = ret, .diags = std::move(dc), .targets = CollectTargets(*node)}; return ret; } bool TypeChecker::TypeCheckerImpl::CheckAndCache(ASTContext& ctx, Ptr target, Ptr node, const CacheKey& key) { DiagnosticCache dc; dc.ToExclude(ctx.diag); bool ret = Check(ctx, target, node); dc.BackUp(ctx.diag); ctx.typeCheckCache[node].chkCache[key] = { .successful = ret, .result = node->ty, .diags = std::move(dc), .targets = CollectTargets(*node)}; return ret; } namespace { /** Check if a class has a non-parameter constructor. */ bool HasNonParamCtorForClass(const ClassDecl& classDecl) { for (auto& decl : classDecl.body->decls) { auto fd = DynamicCast(decl.get()); if (!fd || !IsInstanceConstructor(*fd)) { continue; } if (fd->funcBody->paramLists.empty()) { return false; } auto& params = fd->funcBody->paramLists[0]->params; if (params.empty()) { if (fd->TestAttr(Attribute::PRIVATE)) { return false; } return true; } bool allHasDefaultValue{true}; for (auto& param : params) { if (!(param->assignment)) { allHasDefaultValue = false; break; } } if (allHasDefaultValue) { if (fd->TestAttr(Attribute::PRIVATE)) { // There may exist another constructor with different number of parameters which all have default value. continue; } return true; } } return false; } } // namespace void TypeChecker::TypeCheckerImpl::CheckConstructorSuper( const CallExpr& ce, const Decl& decl, FuncDecl& fd, bool& needEraseSuper) { if (!ce.baseFunc || ce.baseFunc->astKind != ASTKind::REF_EXPR) { return; } auto re = RawStaticCast(ce.baseFunc.get()); if (!re->isSuper || !ce.TestAttr(Attribute::COMPILER_ADD) || !ce.args.empty()) { return; } if (decl.astKind != ASTKind::CLASS_DECL) { return; } auto cd = RawStaticCast(&decl); // remove compiler added super() when there is no non-parameter constructor in super class Ptr superCD = cd->GetSuperClassDecl(); needEraseSuper = superCD && !HasNonParamCtorForClass(*superCD); if (!needEraseSuper) { return; } auto& fdName = fd.TestAttr(Attribute::PRIMARY_CONSTRUCTOR) ? fd.identifierForLsp : fd.identifier.Val(); auto range = fd.TestAttr(Attribute::IMPLICIT_ADD) ? MakeRange(decl.identifier) : MakeRange(fd.identifier.Begin(), fdName); (void)diag.DiagnoseRefactor(DiagKindRefactor::sema_no_non_param_constructor_in_super_class, fd, range); fd.constructorCall = ConstructorCall::NONE; } void TypeChecker::TypeCheckerImpl::CheckFinalizer(const FuncDecl& fd) { if (!fd.funcBody) { return; } bool invalidGeneric = fd.funcBody->generic && !fd.TestAttr(Attribute::STATIC) && fd.outerDecl && !HasJavaAttr(*fd.outerDecl); if (invalidGeneric) { diag.Diagnose(fd, DiagKind::sema_forbid_generic_finalizer, fd.identifier.Val()); } if (!fd.funcBody->paramLists.empty()) { if (fd.funcBody->paramLists.size() > 1) { diag.Diagnose(*fd.funcBody->paramLists[0], DiagKind::sema_cannot_currying, "finalizer"); } if (!fd.funcBody->paramLists[0]->params.empty()) { diag.Diagnose(fd, DiagKind::sema_cannot_have_parameter, "finalizer"); } } } void TypeChecker::TypeCheckerImpl::CheckPrimaryCtorForClassOrStruct(InheritableDecl& id) { if (id.TestAttr(Attribute::IMPORTED)) { return; // Do not desugar primary ctor for imported type decl. } bool primaryCtor = false; bool hasDesugared = false; // For incremental case. Ptr target = nullptr; for (auto& decl : id.GetMemberDecls()) { if (auto fd = DynamicCast(decl.get()); fd) { if (primaryCtor) { auto typeName = id.astKind == ASTKind::CLASS_DECL ? "class" : "struct"; diag.Diagnose(*fd, DiagKind::sema_multiple_primary_constructors, typeName, id.identifier.Val()); } else { primaryCtor = true; target = fd; } if (fd->funcBody && fd->funcBody->generic) { diag.Diagnose(*decl, DiagKind::sema_forbid_generic_constructor, decl->identifier.Val()); } if (fd->funcBody && fd->funcBody->paramLists.size() > 1) { diag.Diagnose(*fd->funcBody->paramLists[0], DiagKind::sema_cannot_currying, "constructor"); } } if (decl->TestAttr(Attribute::PRIMARY_CONSTRUCTOR) && decl->astKind == ASTKind::FUNC_DECL) { hasDesugared = true; } } // The corresponding init constructor with primary constructor auto-generated if (target != nullptr && !hasDesugared) { DesugarPrimaryCtor(id, *target); } } void TypeChecker::TypeCheckerImpl::TypeCheckCompositeBody( ASTContext& ctx, const Decl& structDecl, const std::vector>& body) { for (auto& decl : body) { CJC_ASSERT(decl); if (auto fd = DynamicCast(decl.get()); fd) { if (fd->TestAttr(Attribute::CONSTRUCTOR)) { CheckConstructor(ctx, structDecl, *fd); } if (fd->IsFinalizer()) { CheckFinalizer(*fd); } } Synthesize(ctx, decl.get()); CheckCTypeMember(*decl); } } void TypeChecker::TypeCheckerImpl::CheckJavaInteropLibImport(Decl& decl) { constexpr auto INTEROPLIB_JAVA_PACKAGE_NAME = "interoplib.interop"; auto interopPackage = importManager.GetPackageDecl(INTEROPLIB_JAVA_PACKAGE_NAME); if (!interopPackage) { diag.DiagnoseRefactor(DiagKindRefactor::sema_java_mirror_interoplib_must_be_imported, decl); decl.EnableAttr(Attribute::IS_BROKEN); } } void TypeChecker::TypeCheckerImpl::CheckObjCInteropLibImport(Decl& decl) { constexpr auto INTEROPLIB_OBJ_C_PACKAGE_NAME = "interoplib.objc"; auto interopPackage = importManager.GetPackageDecl(INTEROPLIB_OBJ_C_PACKAGE_NAME); if (!interopPackage) { diag.DiagnoseRefactor(DiagKindRefactor::sema_objc_mirror_interoplib_must_be_imported, decl); decl.EnableAttr(Attribute::IS_BROKEN); } } bool TypeChecker::TypeCheckerImpl::IsCapturedInCFuncLambda(const ASTContext& ctx, const AST::RefExpr& re) const { if (re.ref.target == nullptr || re.ref.target->fullPackageName != ctx.fullPackageName) { return false; } auto fb = GetCurFuncBody(ctx, re.scopeName); auto targetFb = GetCurFuncBody(ctx, re.ref.target->scopeName); // Fast path 1: RefExpr and target are in the same function body. Or target is a global node. if (fb == nullptr || targetFb == nullptr || fb == targetFb || re.ref.target->TestAttr(Attribute::GLOBAL)) { return false; } // Fast path 2: RefExpr and target are not in the same function body and current function is a CFunc. if (auto fty = DynamicCast(fb->ty); fty && fty->isC) { return true; } if (!Is(re.ref.target) && !Is(re.ref.target)) { return false; } // Check whether outer function of current function body is CFunc. auto outerFb = GetCurFuncBody(ctx, ScopeManagerApi::GetParentScopeName(fb->scopeName)); while (outerFb != nullptr) { if (outerFb == targetFb) { return false; } if (auto fty = DynamicCast(outerFb->ty); fty && fty->isC) { return true; } outerFb = GetCurFuncBody(ctx, ScopeManagerApi::GetParentScopeName(outerFb->scopeName)); } return false; } void TypeChecker::TypeCheckerImpl::CheckLegalUseOfClosure(Expr& e, DiagKind kind) const { if (e.TestAttr(Attribute::IS_BROKEN)) { return; } if (auto target = DynamicCast(&e); target && target->funcBody) { if (target->funcBody->captureKind == CaptureKind::CAPTURE_VAR) { DiagUseClosureCaptureVarAlone(diag, e); } else if (target->funcBody->captureKind == CaptureKind::TRANSITIVE_CAPTURE) { diag.Diagnose(e, kind, "lambda", "transitively", "lambda"); } } if (auto ref = DynamicCast(&e); ref) { if (auto target = DynamicCast(ref->ref.target); target) { if (target->funcBody->captureKind == CaptureKind::CAPTURE_VAR) { DiagUseClosureCaptureVarAlone(diag, e); } else if (target->funcBody->captureKind == CaptureKind::TRANSITIVE_CAPTURE) { diag.Diagnose(e, kind, target->identifier.Val(), "transitively", target->identifier.Val()); } } } } void TypeChecker::TypeCheckerImpl::CheckLegalUseOfClosure(const ASTContext& ctx, Node& node) const { if (auto vd = DynamicCast(&node); vd) { if (vd->initializer) { CheckLegalUseOfClosure(*vd->initializer, DiagKind::sema_func_capture_var_cannot_assign); } } else if (auto re = DynamicCast(&node); re) { if (re->expr) { CheckLegalUseOfClosure(*re->expr, DiagKind::sema_func_capture_var_cannot_return); } } else if (auto ce = DynamicCast(&node); ce) { if (auto baseRe = DynamicCast(ce->baseFunc.get()); baseRe && IsCapturedInCFuncLambda(ctx, *baseRe)) { diag.Diagnose(*baseRe, DiagKind::sema_cfunc_cannot_capture_var, baseRe->ref.identifier.Val()); } for (auto& arg : ce->args) { if (arg && arg->expr) { CheckLegalUseOfClosure(*arg->expr, DiagKind::sema_func_capture_var_cannot_param); } } } else if (auto ref = DynamicCast(&node); ref && !ref->isBaseFunc) { CheckLegalUseOfClosure(*ref, DiagKind::sema_func_capture_var_cannot_expr); if (auto refVd = DynamicCast(ref->ref.target); refVd) { if (IsCapturedCStructOfClosure(*refVd)) { diag.Diagnose(*ref, DiagKind::sema_func_capture_var_not_ctype); } } if (IsCapturedInCFuncLambda(ctx, *ref)) { diag.Diagnose(*ref, DiagKind::sema_cfunc_cannot_capture_var, ref->ref.identifier.Val()); } } else if (auto lambda = DynamicCast(&node); lambda && !lambda->isBaseFunc) { CheckLegalUseOfClosure(*lambda, DiagKind::sema_func_capture_var_cannot_expr); } } bool TypeChecker::TypeCheckerImpl::IsCapturedCStructOfClosure(const VarDecl& decl) const { return decl.TestAttr(Attribute::IS_CAPTURE) && decl.ty && Ty::IsCTypeConstraint(*decl.ty); } void TypeChecker::TypeCheckerImpl::CheckCHIRClassDependencies() { auto objectDecl = importManager.GetImportedDecl(CORE_PACKAGE_NAME, "Object"); if (objectDecl == nullptr) { diag.DiagnoseRefactor(DiagKindRefactor::sema_core_object_not_found_when_no_prelude, DEFAULT_POSITION); } } namespace { void MarkOverflow(Node& node) { Walker checkOverflowWalker(&node, nullptr, [](Ptr node) -> VisitAction { switch (node->astKind) { case ASTKind::LIT_CONST_EXPR: { auto lce = StaticAs(node); if (lce->ty && lce->ty->IsInteger()) { auto primitiveTy = RawStaticCast(lce->ty); lce->constNumValue.asInt.SetOutOfRange(primitiveTy); } return VisitAction::WALK_CHILDREN; } default: { return VisitAction::WALK_CHILDREN; } } }); checkOverflowWalker.Walk(); } void AddBuiltInArrayDecl(Package& pkg) { auto bid = MakeOwned(BuiltInType::ARRAY); bid->identifier = RAW_ARRAY_NAME; bid->generic = MakeOwned(); auto gpd = MakeOwnedNode(); gpd->identifier = "T"; gpd->outerDecl = bid.get(); bid->generic->typeParameters.emplace_back(std::move(gpd)); bid->EnableAttr(Attribute::GLOBAL, Attribute::GENERIC); CopyFileID(bid.get(), pkg.files[0].get()); pkg.files[0]->decls.emplace_back(std::move(bid)); // Caller guarantees file not empty. } void AddBuiltInPointerDecl(Package& pkg) { auto bid = MakeOwned(BuiltInType::POINTER); bid->identifier = CPOINTER_NAME; bid->generic = MakeOwned(); auto gpd = MakeOwnedNode(); gpd->identifier = "T"; gpd->outerDecl = bid.get(); bid->generic->typeParameters.emplace_back(std::move(gpd)); bid->generic->genericConstraints.emplace_back(CreateConstraintForFFI(CTYPE_NAME)); bid->EnableAttr(Attribute::PUBLIC, Attribute::GLOBAL, Attribute::GENERIC); CopyFileID(bid.get(), pkg.files[0].get()); pkg.files[0]->decls.emplace_back(std::move(bid)); // Caller guarantees file not empty. } void AddBuiltInCStringDecl(Package& pkg) { auto bid = MakeOwned(BuiltInType::CSTRING); bid->identifier = CSTRING_NAME; bid->generic = nullptr; bid->EnableAttr(Attribute::PUBLIC, Attribute::GLOBAL); CopyFileID(bid.get(), pkg.files[0].get()); pkg.files[0]->decls.emplace_back(std::move(bid)); // Caller guarantees file not empty. } void AddBuiltInVArrayDecl(Package& pkg) { auto bid = MakeOwned(BuiltInType::VARRAY); bid->identifier = VARRAY_NAME; bid->generic = MakeOwned(); auto gpd = MakeOwnedNode(); gpd->identifier = "T"; gpd->outerDecl = bid.get(); bid->generic->typeParameters.emplace_back(std::move(gpd)); bid->EnableAttr(Attribute::GLOBAL); bid->EnableAttr(Attribute::PUBLIC); bid->EnableAttr(Attribute::GENERIC); CopyFileID(bid.get(), pkg.files[0].get()); pkg.files[0]->decls.emplace_back(std::move(bid)); // Caller guarantees file not empty. } void AddBuiltinCFuncDecl(Package& pkg) { auto bid = MakeOwned(BuiltInType::CFUNC); bid->identifier = CFUNC_NAME; bid->generic = MakeOwned(); auto gpd = MakeOwnedNode(); gpd->identifier = "T"; gpd->outerDecl = &*bid; bid->generic->typeParameters.push_back(std::move(gpd)); bid->EnableAttr(Attribute::GLOBAL); bid->EnableAttr(Attribute::PUBLIC); bid->EnableAttr(Attribute::GENERIC); CopyFileID(&*bid, &*pkg.files[0]); pkg.files[0]->decls.push_back(std::move(bid)); } void AddAttrForDefaultFuncParam(Package& pkg) { Walker(&pkg, [](auto node) { if (node->astKind != ASTKind::FUNC_DECL) { return VisitAction::WALK_CHILDREN; } auto fd = StaticAs(node); if (!fd->funcBody || fd->funcBody->paramLists.empty()) { return VisitAction::SKIP_CHILDREN; } for (auto& param : fd->funcBody->paramLists[0]->params) { if (param->desugarDecl) { if (fd->TestAttr(Attribute::GLOBAL)) { param->desugarDecl->EnableAttr(Attribute::GLOBAL); } } } return VisitAction::WALK_CHILDREN; }).Walk(); } void MarkImplicitUsedFunctions(const Package& pkg) { // NOTE: These are special toplevel functions that are not exported but will be reference in other package. static const std::unordered_map> SPECIAL_EXPORTED_FUNCS{ {CORE_PACKAGE_NAME, {"arrayInitByCollection", "arrayInitByFunction", "composition", "handleException", "createOverflowExceptionMsg", "createArithmeticExceptionMsg", "getCommandLineArgs"}}, {AST_PACKAGE_NAME, {MACRO_OBJECT_NAME, "refreshTokensPosition", "refreshPos", "unsafePointerCastFromUint8Array"}}}; auto found = SPECIAL_EXPORTED_FUNCS.find(pkg.fullPackageName); if (found == SPECIAL_EXPORTED_FUNCS.end()) { return; } IterateToplevelDecls(pkg, [&found](auto& decl) { if (found->second.count(decl->identifier) != 0) { decl->EnableAttr(Attribute::IMPLICIT_USED); } }); } } // namespace Ptr TypeChecker::TypeCheckerImpl::GetImplementedTargetIfExist( const ASTContext& ctx, const Ty& interfaceTy, Decl& target, const MultiTypeSubst& typeMapping) { auto targetInstanceTy = typeManager.GetBestInstantiatedTy(target.ty, typeMapping); auto id = Ty::GetDeclPtrOfTy(&interfaceTy); auto members = FieldLookup(ctx, id, target.identifier); for (auto& member : members) { bool isSameSignature = false; if (member->IsFunc() && target.IsFunc()) { if (!IsOverrideOrShadow(typeManager, *RawStaticCast(member), static_cast(target))) { continue; } // Use the typeMapping provided by the call chain to perform instantiation comparison and confirm that the // override version is correct. // eg: interface I { f(T) }; interface I1 <: I & I { f(A) } // I1.f(b) should diag error. // f(A) is the overwritten version, but not the correct version of the final call. auto memberFuncTy = DynamicCast(member->ty); auto targetFuncTy = DynamicCast(targetInstanceTy); if (!Ty::IsTyCorrect(memberFuncTy) || !Ty::IsTyCorrect(targetFuncTy)) { continue; } auto mts = typeMapping; auto memberFuncDecl = DynamicCast(member); auto targetFuncDecl = DynamicCast(&target); CJC_NULLPTR_CHECK(memberFuncDecl); CJC_NULLPTR_CHECK(targetFuncDecl); auto ts = GenerateTypeMappingBetweenFuncs(typeManager, *memberFuncDecl, *targetFuncDecl); mts.merge(ts); // Cannot be a reference type. Otherwise, SemaTy will be modified. auto memberParamTys = memberFuncTy->paramTys; auto targetParamTys = targetFuncTy->paramTys; for (auto& it : memberParamTys) { it = typeManager.GetBestInstantiatedTy(it, mts); } for (auto& it : targetParamTys) { it = typeManager.GetBestInstantiatedTy(it, mts); } isSameSignature = typeManager.IsFuncParameterTypesIdentical(memberParamTys, targetParamTys); } else if (member->astKind == ASTKind::PROP_DECL && target.astKind == ASTKind::PROP_DECL) { if (!IsOverrideOrShadow(typeManager, *RawStaticCast(member), static_cast(target))) { continue; } isSameSignature = member->ty == targetInstanceTy; } if (isSameSignature && !member->TestAttr(Attribute::ABSTRACT)) { return member; } } return nullptr; } std::pair> TypeChecker::TypeCheckerImpl::CheckInvokeTargetHasImpl(const ASTContext& ctx, Ty& interfaceTy, Decl& decl, MultiTypeSubst& typeMapping, std::unordered_set>& traversedDecls) { std::pair> ret{false, nullptr}; auto preVisit = [this, &ret, &ctx, &interfaceTy, &typeMapping, &traversedDecls](Ptr node) -> VisitAction { // Find interface members that are directly referenced by name in the interface member function/property // definition. if (node->astKind != ASTKind::REF_EXPR) { return AST::VisitAction::WALK_CHILDREN; } const auto re = RawStaticCast(node); auto target = re->GetTarget(); // If it is a function defined in the current function, Use the ast check directly. // Otherwise, 'GetImplementedTargetIfExist' cannot find the member of the implementation version. if (target && target->TestAttr(Attribute::STATIC) && target->outerDecl && !target->outerDecl->IsFunc()) { // Check whether traversal has been performed. if (traversedDecls.find(target) != traversedDecls.end()) { return VisitAction::SKIP_CHILDREN; } traversedDecls.emplace(target); // Update all generic derivation results to typeMapping. if (re->matchedParentTy && target->outerDecl->ty) { typeMapping.merge(promotion.GetPromoteTypeMapping(*re->matchedParentTy, *target->outerDecl->ty)); } // Check whether the current invoking is implemented in the 'interfaceTy'. auto newTarget = GetImplementedTargetIfExist(ctx, interfaceTy, *target, typeMapping); if (newTarget == nullptr) { ret.first = true; ret.second = re; return VisitAction::STOP_NOW; } ret = CheckInvokeTargetHasImpl(ctx, interfaceTy, *newTarget, typeMapping, traversedDecls); if (ret.first) { return VisitAction::STOP_NOW; } } return AST::VisitAction::WALK_CHILDREN; }; Walker walker(&decl, preVisit); walker.Walk(); return ret; } void TypeChecker::TypeCheckForPackages(const std::vector>& pkgs) const { impl->TypeCheckForPackages(pkgs); } std::vector> TypeChecker::TypeCheckerImpl::PreTypeCheck(const std::vector>& pkgs) { Utils::ProfileRecorder recorder("Semantic", "Pre TypeCheck"); std::vector> contexts; // Only check for packages that have corresponding ASTContext. std::for_each(pkgs.begin(), pkgs.end(), [this, &contexts](Ptr pkg) { CJC_NULLPTR_CHECK(pkg); // There should not be any nullptr Ptr in pkgs. if (auto ctx = ci->GetASTContextByPackage(pkg)) { contexts.emplace_back(ctx); } }); for (auto& ctx : contexts) { PrepareTypeCheck(*ctx, *ctx->curPackage); } // Pre checking for resolving types and rules without semantic type. PreCheck(contexts); for (auto pkg : pkgs) { if (auto ctx = ci->GetASTContextByPackage(pkg)) { CollectDeclsWithMember(pkg, *ctx); } } return contexts; } void TypeChecker::TypeCheckerImpl::PostTypeCheck(std::vector>& contexts) { Utils::ProfileRecorder recorder("Semantic", "Post TypeCheck"); // Post checking for legality of semantic. for (auto& ctx : contexts) { CheckOverflow(*ctx->curPackage); CheckUnusedImportSpec(*ctx->curPackage); // Check duplicated super interfaces in class, interface when type arguments applied. CheckInstDupSuperInterfacesEntry(*ctx->curPackage); // Check legality of usage after sema type completed. CheckLegalityOfUsage(*ctx, *ctx->curPackage); // Check cjmp match rules. mpImpl->MatchPlatformWithCommon(*ctx->curPackage); AddAttrForDefaultFuncParam(*ctx->curPackage); // Because of the cjlint checking policy, desugar of propDecl should be done in sema stage for now. DesugarForPropDecl(*ctx->curPackage); CheckConstEvaluation(*ctx->curPackage); MarkImplicitUsedFunctions(*ctx->curPackage); // Collect program entry separately. IterateToplevelDecls(*ctx->curPackage, [this](auto& decl) { if (auto md = DynamicCast(decl.get()); md && md->desugarDecl) { (void)mainFunctionMap[md->curFile].emplace(md->desugarDecl.get()); } }); APILevelCheck::APILevelAnnoChecker(*ci, diag, importManager).Check(*ctx->curPackage); } CheckWhetherHasProgramEntry(); } void TypeChecker::TypeCheckerImpl::PrepareTypeCheck(ASTContext& ctx, Package& pkg) { // Reset search's cache. ctx.searcher->InvalidateCache(); CheckPrimaryCtorBeforeMerge(pkg); // Merging common classes into platform if any mpImpl->PrepareTypeCheck4CJMP(pkg); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND Interop::Java::PrepareTypeCheck(pkg); Interop::ObjC::PrepareTypeCheck(pkg); #endif // Phase: add some default function. AddDefaultFunction(pkg); // Add built-in decls to core package. if (pkg.fullPackageName == CORE_PACKAGE_NAME && !pkg.files.empty() && !pkg.TestAttr(Attribute::IMPORTED)) { AddBuiltInArrayDecl(pkg); AddBuiltInVArrayDecl(pkg); AddBuiltInPointerDecl(pkg); AddBuiltinCFuncDecl(pkg); AddBuiltInCStringDecl(pkg); } // Phase: build symbol table. Collector collector(scopeManager, ci->invocation.globalOptions.enableMacroInLSP); collector.BuildSymbolTable(ctx, &pkg, ci->buildTrie); // Phase: mark outermost binary expressions. MarkOutermostBinaryExpressions(pkg); AddCurFile(pkg); MarkParamWithInitialValue(pkg); // Warmup cache to speed up search. WarmupCache(ctx); } void TypeChecker::TypeCheckerImpl::TypeCheckTopLevelDecl(ASTContext& ctx, Decl& decl) { TyVarScope ts(typeManager); // to release local placeholder ty var Synthesize(ctx, &decl); MarkOverflow(decl); } // Check generic members declared inside non-generic inheritable decls. void TypeChecker::TypeCheckerImpl::TypeCheckImportedGenericMember(ASTContext& ctx) { std::vector syms = GetAllStructDecls(ctx); for (auto sym : syms) { CJC_ASSERT(sym && sym->node); auto id = StaticCast(sym->node); // Generic toplevel decls will be checked before by 'TypeCheckTopLevelDecl'. if (id->TestAttr(Attribute::GENERIC)) { continue; } for (auto& member : id->GetMemberDecls()) { if (member->TestAttr(Attribute::GENERIC)) { Synthesize(ctx, member.get()); } } } } void TypeChecker::TypeCheckerImpl::TypeCheck(ASTContext& ctx, Package& pkg) { std::vector syms = GetToplevelDecls(ctx); // 1. Check from toplevel decls. for (auto sym : syms) { CJC_ASSERT(sym && sym->node); if (!Is(sym->node)) { return; } auto decl = StaticAs(sym->node); TypeCheckTopLevelDecl(ctx, *decl); } // 2. check source imported decls. for (auto& node : pkg.srcImportedNonGenericDecls) { Synthesize(ctx, node); } // 3. check imported generic member decls which is defined in non-generic decl. // NOTE: This kind of decls will not be checked in step 1. if (pkg.TestAttr(Attribute::IMPORTED)) { TypeCheckImportedGenericMember(ctx); } // For lsp, need to find the target of all macrocalls, include case: var a = call(@M(1)). std::function)> visitMacrocall = [this, &visitMacrocall, &ctx]( Ptr curNode) -> VisitAction { if (curNode->astKind == ASTKind::FILE) { auto file = StaticAs(curNode); for (auto& it : file->originalMacroCallNodes) { Walker(it.get(), visitMacrocall).Walk(); } } if (curNode->IsMacroCallNode()) { CheckMacroCall(ctx, *curNode); } return VisitAction::WALK_CHILDREN; }; if (ci->invocation.globalOptions.enableMacroInLSP) { for (auto& file : pkg.files) { Walker(file.get(), visitMacrocall).Walk(); } } } void TypeChecker::TypeCheckerImpl::CheckWhetherHasProgramEntry() { CJC_ASSERT(ci); if (!ci->invocation.globalOptions.CompileExecutable() || ci->invocation.globalOptions.enableCompileTest || ci->invocation.frontendOptions.dumpAction == FrontendOptions::DumpAction::TYPE_CHECK || !mainFunctionMap.empty() || ci->invocation.globalOptions.compileCjd) { return; } if (ci->srcPkgs.empty() || ci->srcPkgs[0] == nullptr || ci->srcPkgs[0]->files.empty()) { return; } auto& file = ci->srcPkgs[0]->files[0]; if (file != nullptr) { diag.Diagnose(*file, DiagKind::sema_missing_entry); } } namespace { /** Check if a type decl (class/strcut) has any constructor. */ bool HasCtorForTypeDecl(const InheritableDecl& id) { for (auto& decl : id.GetMemberDeclPtrs()) { CJC_NULLPTR_CHECK(decl); if (decl->TestAttr(Attribute::CONSTRUCTOR) && !decl->TestAttr(Attribute::STATIC)) { return true; } } return false; } } // namespace VisitAction TypeChecker::TypeCheckerImpl::CheckDefaultParamFunc(StructDecl& sd) const { CJC_ASSERT(sd.body); // Do not desugar for broken body. if (sd.body->TestAttr(Attribute::IS_BROKEN)) { return VisitAction::SKIP_CHILDREN; } if (!HasCtorForTypeDecl(sd)) { AddDefaultCtor(sd); } AddSetterGetterInProp(sd); return VisitAction::WALK_CHILDREN; } VisitAction TypeChecker::TypeCheckerImpl::CheckDefaultParamFunc(ClassDecl& cd, const File& file) const { CJC_ASSERT(cd.body); // Do not desugar for broken body. if (cd.body->TestAttr(Attribute::IS_BROKEN)) { return VisitAction::SKIP_CHILDREN; } if (!HasCtorForTypeDecl(cd)) { AddDefaultCtor(cd); } AddSetterGetterInProp(cd); // Add 'super()' to the constructor which doesn't call either init(..) or super(..) inside. for (auto& decl : cd.body->decls) { CJC_ASSERT(decl); if (decl->astKind != ASTKind::FUNC_DECL) { continue; } auto& fd = *RawStaticCast(decl.get()); if (fd.funcBody == nullptr || fd.funcBody->body == nullptr || !IsInstanceConstructor(fd)) { continue; } // The constructorCall only need to do once when there is a `init` function. SetFuncDeclConstructorCall(fd); // For now, the classes in `HLIR` do not automatically inherit from core.Object, // so in these two backends, it is not necessary to add super call to the constructors // of the classes which have no super class. if (fd.constructorCall == ConstructorCall::NONE) { std::string_view fullPackageName; if (file.curPackage) { fullPackageName = file.curPackage->fullPackageName; } auto objPkgName = CORE_PACKAGE_NAME; bool nonObjectClass = !(cd.identifier == OBJECT_NAME && fullPackageName == objPkgName); if (nonObjectClass) { AddDefaultSuperCall(*fd.funcBody); fd.constructorCall = ConstructorCall::SUPER; } } } return VisitAction::WALK_CHILDREN; } void TypeChecker::TypeCheckerImpl::SetFuncDeclConstructorCall(FuncDecl& fd) const { CJC_ASSERT(fd.funcBody && fd.funcBody->body); if (fd.funcBody->body->body.empty()) { return; } Ptr refExpr = nullptr; if (auto ce = DynamicCast(fd.funcBody->body->body.begin()->get()); ce) { if (auto re = DynamicCast(ce->baseFunc.get()); re) { refExpr = re; } } else if (auto tce = DynamicCast(fd.funcBody->body->body.begin()->get()); tce) { if (auto callExpr = DynamicCast(tce->desugarExpr.get()); callExpr) { if (auto re = DynamicCast(callExpr->baseFunc.get()); re) { refExpr = re; } } else if (auto re = DynamicCast(tce->desugarExpr.get()); re) { refExpr = re; } } if (refExpr != nullptr) { if (refExpr->isThis && !fd.TestAttr(Attribute::PRIMARY_CONSTRUCTOR)) { fd.constructorCall = ConstructorCall::OTHER_INIT; } if (refExpr->isSuper) { fd.constructorCall = ConstructorCall::SUPER; } } } VisitAction TypeChecker::TypeCheckerImpl::CheckDefaultParamFunc(const InterfaceDecl& ifd) const { CJC_ASSERT(ifd.body); // Do not desugar for broken body. if (ifd.body->TestAttr(Attribute::IS_BROKEN)) { return VisitAction::SKIP_CHILDREN; } AddSetterGetterInProp(ifd); return VisitAction::WALK_CHILDREN; } VisitAction TypeChecker::TypeCheckerImpl::CheckDefaultParamFunc(const EnumDecl& ed) const { AddSetterGetterInProp(ed); return VisitAction::WALK_CHILDREN; } void TypeChecker::TypeCheckerImpl::CheckDefaultParamFuncsEntry(File& file) { auto visitFunc = [&file, this](Ptr node) -> VisitAction { switch (node->astKind) { case ASTKind::FUNC_DECL: { auto fd = StaticAs(node); GetSingleParamFunc(*fd); if (fd->IsFinalizer()) { AddUnitType(*fd); } return VisitAction::WALK_CHILDREN; } case ASTKind::PROP_DECL: { auto pd = StaticAs(node); AddReturnTypeForPropMemDecl(*pd); return VisitAction::WALK_CHILDREN; } case ASTKind::STRUCT_DECL: { auto sd = StaticAs(node); return CheckDefaultParamFunc(*sd); } case ASTKind::CLASS_DECL: { auto cd = StaticAs(node); return CheckDefaultParamFunc(*cd, file); } case ASTKind::INTERFACE_DECL: { auto ifd = StaticAs(node); return CheckDefaultParamFunc(*ifd); } case ASTKind::ENUM_DECL: { auto ed = StaticAs(node); return CheckDefaultParamFunc(*ed); } case ASTKind::EXTEND_DECL: { auto ed = StaticAs(node); AddSetterGetterInProp(*ed); return VisitAction::WALK_CHILDREN; } case ASTKind::FUNC_PARAM: // Nested function inside default param value is dealt inside GetSingleParamFunc, should skip here. return VisitAction::SKIP_CHILDREN; default: return VisitAction::WALK_CHILDREN; } }; for (auto& decl : file.decls) { Walker walker(decl.get(), visitFunc); walker.Walk(); } // Walk exported internal decls for any generic decl defined in toplevel or defined in nominal decls. for (auto& decl : file.exportedInternalDecls) { Walker(decl.get(), visitFunc).Walk(); } // Walk macroCall nodes for lsp. if (ci->invocation.globalOptions.enableMacroInLSP) { for (auto& node : file.originalMacroCallNodes) { Walker(node.get(), visitFunc).Walk(); } } } namespace { #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void CollectGenericParam(const FuncDecl& funcDecl, Ptr desugared) { if (!funcDecl.funcBody || !funcDecl.funcBody->generic || funcDecl.funcBody->generic->typeParameters.empty()) { return; } OwnedPtr generic = MakeOwnedNode(); int typeParamIdx = 0; // Rename the type param decl: ===> . std::map orig2New; for (auto& param : std::as_const(funcDecl.funcBody->generic->typeParameters)) { OwnedPtr tp = MakeOwnedNode(); tp->identifier = param->identifier + "$" + std::to_string(typeParamIdx++); tp->outerDecl = desugared; // If the generic parameter name same as outerDecl's, the outerDecl's will be shadowed. orig2New.emplace(param->identifier, tp->identifier); generic->typeParameters.emplace_back(std::move(tp)); } for (auto& constraint : std::as_const(funcDecl.funcBody->generic->genericConstraints)) { auto gc = MakeOwnedNode(); auto& constraintType = constraint->type; auto rt = MakeOwnedNode(); rt->ref.identifier = orig2New[constraintType->ref.identifier]; gc->type = std::move(rt); for (auto& upperBound : std::as_const(constraint->upperBounds)) { auto ub = ASTCloner::Clone(upperBound.get()); gc->upperBounds.emplace_back(std::move(ub)); } generic->genericConstraints.emplace_back(std::move(gc)); } desugared->funcBody->generic = std::move(generic); auto replacesTypeParams = [&orig2New](Ptr node) { // Replaces the name of all generic declaration references, which can only be refer node. if (auto rt = DynamicCast(node)) { auto found = orig2New.find(rt->ref.identifier); if (found != orig2New.end()) { rt->ref.identifier = found->second; } } else if (auto re = DynamicCast(node)) { auto found = orig2New.find(re->ref.identifier); if (found != orig2New.end()) { re->ref.identifier = found->second; } } return VisitAction::WALK_CHILDREN; }; Walker(desugared, replacesTypeParams).Walk(); desugared->EnableAttr(Attribute::GENERIC); } #endif /** Each optional parameter generates a function. NOTICE: it will change AST Node! * *************** before desugar **************** * class A { * func foo(a: T, b!: T = a) {} * } * *************** after desugar **************** * class A { * // chir will generic apply for 'b' when call 'foo', like 'foo(a, b.0(a))' * func foo(a: T, b: T) {} * func b.1(a: T) { * return a * } * } */ OwnedPtr MakeDefaultParamFunction( FuncParam& fp, FuncDecl& funcDecl, const std::vector>& funcParams) { fp.EnableAttr(Attribute::HAS_INITIAL); OwnedPtr ret = MakeOwnedNode(); CopyBasicInfo(fp.assignment.get(), ret.get()); ret->isFrozen = funcDecl.isFrozen || funcDecl.HasAnno(AnnotationKind::FROZEN); // The desugar function declaration is in the same scope as the current function to ensure that external generic // parameters can be accessed. CopyNodeScopeInfo(&funcDecl, ret.get()); // Default param's function's name is 'paramName.paramIndex' // 'funcParams' is vector of previous params, so the size of 'funcParams' is the index of curren param. ret->identifier = fp.identifier + "." + std::to_string(funcParams.size()); // Make FuncBody. OwnedPtr funcBody = MakeOwnedNode(); funcBody->body = MakeOwnedNode(); funcBody->paramLists.push_back(MakeOwnedNode()); std::vector> params; for (auto& param : funcParams) { if (param == nullptr) { continue; // Double Check. } params.emplace_back(CreateFuncParamForOptional(*param)); } funcBody->paramLists[0]->params = std::move(params); ret->funcBody = std::move(funcBody); // Set return expr. auto returnExpr = MakeOwnedNode(); returnExpr->begin = fp.assignment->begin; returnExpr->end = fp.assignment->end; returnExpr->expr = ASTCloner::Clone(fp.assignment.get()); returnExpr->refFuncBody = ret->funcBody.get(); ret->funcBody->body->body.emplace_back(std::move(returnExpr)); ret->funcBody->retType = ASTCloner::Clone(fp.type.get()); // The resolvedFunction contains the assignment, so mark it as UNREACHABLE. fp.assignment->EnableAttr(Attribute::UNREACHABLE); ret->funcBody->funcDecl = ret.get(); ret->EnableAttr(Attribute::HAS_INITIAL, Attribute::IMPLICIT_ADD, Attribute::NO_REFLECT_INFO); ret->toBeCompiled = funcDecl.toBeCompiled; // For incremental compilation. ret->ownerFunc = &funcDecl; ret->fullPackageName = funcDecl.fullPackageName; ret->outerDecl = funcDecl.outerDecl; if (funcDecl.TestAttr(Attribute::CONSTRUCTOR)) { // Desugared default param function should be considered as a static function. // NOTE: used to avoid unexpected 'this' insertion during AST2CHIR. ret->EnableAttr(Attribute::STATIC); } // Default value should inherit 'mut' attribute. if (funcDecl.TestAttr(Attribute::MUT)) { ret->EnableAttr(Attribute::MUT); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND // It may be a static constructor, but export is not allowed. // This prevents link problems when chir creates virtual tables of upstream packets in downstream packets. ret->EnableAttr(Attribute::PRIVATE); CollectGenericParam(funcDecl, ret.get()); #endif return ret; } } // namespace void TypeChecker::TypeCheckerImpl::GetSingleParamFunc(Decl& decl) { auto fd = As(&decl); // Source imported decl does not inherit initial state and desugar decl, need to generate again. bool notInherit = !fd || !fd->funcBody || fd->funcBody->paramLists.empty() || fd->TestAttr(Attribute::IMPORTED); if (notInherit) { return; } std::vector> funcParams; bool isStatic = fd->TestAttr(Attribute::STATIC); auto walkFunc = [isStatic, this](Ptr node) -> VisitAction { CJC_ASSERT(node); if (node->astKind == ASTKind::FUNC_DECL) { if (isStatic) { node->EnableAttr(Attribute::STATIC); // Functions inside static function also has static attribute. } GetSingleParamFunc(*StaticAs(node)); } return VisitAction::WALK_CHILDREN; }; // Decls inside origin default assignment will report error if exists. // Compiler added function only used for code generation. auto walkOrigin = [isStatic](Ptr node) -> VisitAction { if (node->astKind == ASTKind::FUNC_DECL && isStatic) { node->EnableAttr(Attribute::STATIC); // Functions inside static function also has static attribute. } return VisitAction::WALK_CHILDREN; }; for (auto& fp : fd->funcBody->paramLists[0]->params) { if (fp && fp->assignment && !fp->TestAttr(Attribute::HAS_INITIAL)) { if (fd->op != TokenKind::ILLEGAL || fd->TestAttr(Attribute::OPEN) || fd->TestAttr(Attribute::ABSTRACT) || fd->TestAttr(Attribute::DEFAULT)) { DiagCannotHaveDefaultParam(diag, *fd, *fp); return; } fp->desugarDecl = MakeDefaultParamFunction(*fp, *fd, funcParams); // There may be nested functions inside default value function. Walker walker(fp->desugarDecl.get(), walkFunc); walker.Walk(); Walker originWalker(fp->assignment.get(), walkOrigin); originWalker.Walk(); MarkParamWithInitialValue(*fp->assignment); } funcParams.push_back(fp.get()); } } Ptr TypeChecker::TypeCheckerImpl::GetDupInterfaceRecursively(const Node& triggerNode, Ty& interfaceTy, const TypeSubst& instantiateMap, std::unordered_set>& res, std::unordered_set>& passedClassLikeDecls) { auto insTy = typeManager.GetInstantiatedTy(&interfaceTy, instantiateMap); // When `ity` is invalid, it indicates that an inheritance cycle appeared. // In this case, we do not need to continue with the following steps. if (!Ty::IsTyCorrect(insTy) || !insTy->IsInterface()) { return nullptr; } auto ity = RawStaticCast(insTy); auto insertRes = res.insert(ity); if (!insertRes.second) { return ity->declPtr; } CJC_NULLPTR_CHECK(ity->declPtr); TypeSubst superInstantiateMap = GenerateTypeMapping(*ity->declPtr, ity->typeArgs); if (superInstantiateMap.empty()) { return nullptr; } return GetDupSuperInterface(triggerNode, *ity->declPtr, superInstantiateMap, passedClassLikeDecls); } Ptr TypeChecker::TypeCheckerImpl::GetExtendDupSuperInterface(const Node& triggerNode, const InheritableDecl& decl, const TypeSubst& instantiateMap, std::unordered_set>& res, std::unordered_set>& passedClassLikeDecls) { if (!decl.TestAttr(Attribute::GENERIC) || !Ty::IsTyCorrect(decl.ty)) { return nullptr; } auto extends = typeManager.GetDeclExtends(decl); for (auto& extend : extends) { // Generate typeMapping from extend decl's generic type to original decl type. TypeSubst extendInstMap = GenerateTypeMapping(*extend, decl.ty->typeArgs); if (extendInstMap.size() != instantiateMap.size()) { continue; } extendInstMap.insert(instantiateMap.begin(), instantiateMap.end()); for (auto& interfaceType : extend->inheritedTypes) { if (!Ty::IsTyCorrect(interfaceType->ty)) { continue; } auto ret = GetDupInterfaceRecursively(triggerNode, *interfaceType->ty, extendInstMap, res, passedClassLikeDecls); if (ret) { return ret; } } } return nullptr; } Ptr TypeChecker::TypeCheckerImpl::GetDupSuperInterface(const Node& triggerNode, InheritableDecl& decl, const TypeSubst& instantiateMap, std::unordered_set>& passedClassLikeDecls, bool checkExtend) { if (decl.IsClassLikeDecl()) { auto cld = RawStaticCast(&decl); if (passedClassLikeDecls.find(cld) != passedClassLikeDecls.end()) { return nullptr; } passedClassLikeDecls.insert(cld); } std::unordered_set> instInterfaceTys; for (auto& interfaceType : decl.inheritedTypes) { if (!Ty::IsTyCorrect(interfaceType->ty)) { continue; } auto ret = GetDupInterfaceRecursively( triggerNode, *interfaceType->ty, instantiateMap, instInterfaceTys, passedClassLikeDecls); if (ret) { return ret; } } if (checkExtend) { return GetExtendDupSuperInterface(triggerNode, decl, instantiateMap, instInterfaceTys, passedClassLikeDecls); } else { return nullptr; } } void TypeChecker::TypeCheckerImpl::CheckInstDupSuperInterfaces( const Node& triggerNode, InheritableDecl& decl, const TypeSubst& instantiateMap, bool checkExtend) { std::unordered_set> passedClassLikeDecls; auto interfaceDecl = GetDupSuperInterface(triggerNode, decl, instantiateMap, passedClassLikeDecls, checkExtend); auto baseDecl = Ty::GetDeclPtrOfTy(decl.ty); std::string name = baseDecl ? baseDecl->identifier.Val() : Ty::ToString(decl.ty); if (interfaceDecl) { diag.Diagnose(triggerNode, DiagKind::sema_inherit_duplicate_interface, DeclKindToString(decl), name, interfaceDecl->identifier.Val()); } } VisitAction TypeChecker::TypeCheckerImpl::CheckInstDupSuperInterfaces(const Type& type) { if (!Ty::IsTyCorrect(type.ty)) { return VisitAction::SKIP_CHILDREN; } auto typeTarget = TypeCheckUtil::GetRealTarget(type.GetTarget()); if (!typeTarget || !typeTarget->IsNominalDecl()) { return VisitAction::WALK_CHILDREN; } TypeSubst typeMapping = GenerateTypeMapping(*typeTarget, type.ty->typeArgs); if (typeMapping.empty()) { return VisitAction::SKIP_CHILDREN; } // Duplicate check of extend's parent class has been completed in Extend check. CheckInstDupSuperInterfaces(type, *StaticCast(typeTarget), typeMapping, false); return VisitAction::WALK_CHILDREN; } VisitAction TypeChecker::TypeCheckerImpl::CheckInstDupSuperInterfaces(const Expr& expr) { if (!Ty::IsTyCorrect(expr.ty)) { return VisitAction::WALK_CHILDREN; } auto target = TypeCheckUtil::GetRealTarget(expr.GetTarget()); auto instTys = TypeCheckUtil::GetInstanationTys(expr); if (!target || instTys.empty()) { return VisitAction::WALK_CHILDREN; } if (IsClassOrEnumConstructor(*target) && target->outerDecl) { target = target->outerDecl; } if (!target->IsNominalDecl()) { return VisitAction::WALK_CHILDREN; } TypeSubst instantiateMap = GenerateTypeMapping(*target, instTys); if (instantiateMap.empty()) { return VisitAction::SKIP_CHILDREN; } CheckInstDupSuperInterfaces(expr, *StaticCast(target), instantiateMap); return VisitAction::WALK_CHILDREN; } void TypeChecker::TypeCheckerImpl::CheckInstDupSuperInterfacesEntry(Node& n) { std::function)> visitor = [this, &visitor](Ptr n) { switch (n->astKind) { case ASTKind::PACKAGE: { auto& pkg = *RawStaticCast(n); for (auto& it : pkg.files) { Walker(it.get(), visitor).Walk(); } return VisitAction::STOP_NOW; } case ASTKind::REF_TYPE: case ASTKind::QUALIFIED_TYPE: { return CheckInstDupSuperInterfaces(*RawStaticCast(n)); } case ASTKind::REF_EXPR: case ASTKind::MEMBER_ACCESS: { return CheckInstDupSuperInterfaces(*RawStaticCast(n)); } default: { return VisitAction::WALK_CHILDREN; } } }; Walker walker(&n, visitor); walker.Walk(); } // Remove after Chir's constant folding really works. void TypeChecker::TypeCheckerImpl::CheckOverflow(Node& node) { Walker walker(&node, nullptr, [this](Ptr node) -> VisitAction { switch (node->astKind) { case ASTKind::LIT_CONST_EXPR: { auto& lce = *StaticAs(node); if (!lce.desugarExpr && Ty::IsTyCorrect(lce.ty)) { ChkLitConstExprRange(lce); } return VisitAction::WALK_CHILDREN; } default: { return VisitAction::WALK_CHILDREN; } } }); walker.Walk(); } Ptr TypeChecker::TypeCheckerImpl::CalcFuncRetTyFromBody(const FuncBody& fb) { if (!fb.body) { return TypeManager::GetInvalidTy(); } if (fb.body->body.empty()) { return TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT); } auto& lastNode = fb.body->body.back(); Ptr bodyTy = lastNode->IsDecl() ? TypeManager::GetPrimitiveTy(TypeKind::TYPE_UNIT) : lastNode->ty; Ptr retTy = bodyTy; std::set> retTys; Walker(fb.body.get(), [&retTys](auto node) { CJC_ASSERT(node); if (node->astKind == ASTKind::FUNC_DECL || node->astKind == ASTKind::LAMBDA_EXPR) { return VisitAction::SKIP_CHILDREN; } else if (auto re = DynamicCast(node); re && re->expr) { if (Ty::IsTyCorrect(re->expr->ty)) { retTys.emplace(re->expr->ty); } } return VisitAction::WALK_CHILDREN; }).Walk(); CJC_NULLPTR_CHECK(fb.retType); // Only calculate return type with the function body ty when the given return type is not exist or is not unit. bool returnUnit = Ty::IsTyCorrect(fb.retType->ty) && fb.retType->ty->IsUnit(); if (returnUnit) { // It no return expression existed, return the user given unit type. if (retTys.empty()) { return fb.retType->ty; } } else { retTys.emplace(bodyTy); } if (Ty::IsTyCorrect(bodyTy)) { auto joinAndMeet = JoinAndMeet(typeManager, retTys, {}, &importManager, fb.curFile); auto joinRes = joinAndMeet.JoinAsVisibleTy(); if (auto optErrs = JoinAndMeet::SetJoinedType(retTy, joinRes)) { auto builder = diag.Diagnose(*lastNode, DiagKind::sema_incompatible_func_body_and_return_type); builder.AddNote(*optErrs); } return retTy; } else { return TypeManager::GetInvalidTy(); } } } // namespace Cangjie cangjie_compiler-1.0.7/src/Sema/TypeCheckerImpl.h000066400000000000000000002761171510705540100217020ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file declares the TypeChecker related classes, which provides typecheck capabilities. */ #ifndef CANGJIE_SEMA_TYPECHECKER_IMPL_H #define CANGJIE_SEMA_TYPECHECKER_IMPL_H #include #include "Promotion.h" #include "ScopeManager.h" #include "TypeCheckUtil.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Symbol.h" #include "cangjie/AST/Types.h" #include "cangjie/AST/Walker.h" #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Sema/TypeChecker.h" #include "cangjie/Sema/TypeManager.h" #include "cangjie/Utils/ProfileRecorder.h" #include "CJMP/MPTypeCheckerImpl.h" namespace Cangjie { class Synthesizer; struct MatchingStat { uint32_t matchedArgs; /**< The count of matched arguments for current candidate. */ bool argNameValid; /**< Whether the checking of arguments' name passed. */ }; struct FunctionMatchingUnit { public: int64_t id = -1; /**< The index in targets.*/ AST::FuncDecl& fd; /**< Current unit's function, one of the candidates. */ std::vector> tysInArgOrder; /**< The parameters types in arguments order.*/ SubstPack typeMapping; /**< For generic type comparison.*/ std::pair, MatchingStat> diags; /**< Stashed diag msg.*/ CstVersionID ver; /**< ID to fetch constraints generated for this candidate.*/ FunctionMatchingUnit(AST::FuncDecl& f, const std::vector>& tys, const SubstPack& map) : fd(f) { tysInArgOrder = tys; typeMapping = map; } FunctionMatchingUnit(const FunctionMatchingUnit& fmu) = delete; FunctionMatchingUnit& operator=(const FunctionMatchingUnit& fmu) = delete; FunctionMatchingUnit(FunctionMatchingUnit&&) = default; FunctionMatchingUnit& operator=(FunctionMatchingUnit&&) = delete; }; struct FunctionCandidate { AST::FuncDecl& fd; /**< The reference of candidate function declare. */ AST::CallExpr& ce; /**< The reference of call expression. */ std::vector>> argCombinations; /**< The combinations of call expression's arguments types. */ MatchingStat stat; /**< Checking status of current candidate */ }; struct ArgumentTypeUnit { public: std::vector> argTys; /**< The arguments types in order.*/ std::vector> tysInArgOrder; /**< The parameters types in arguments order.*/ AST::TyVarEnv assumptionCollection; /**< For generic type comparison.*/ ArgumentTypeUnit(const std::vector>& tys, const std::vector>& paramTys, const AST::TyVarEnv& assumption) : argTys(tys), tysInArgOrder(paramTys), assumptionCollection(assumption) { } }; struct LookupInfo { Ptr baseTy = nullptr; Ptr file = nullptr; bool lookupInherit = true; bool lookupExtend = true; bool isSetter = false; }; struct CollectDeclsInfo { std::vector> funcDecls; std::vector> initFuncDecls; std::vector> nonFuncDecls; std::vector> staticFuncDecls; std::vector> annotations; }; enum class MatchResult { NONE, UNIQUE, AMBIGUOUS }; class TypeChecker::TypeCheckerImpl { public: explicit TypeCheckerImpl(CompilerInstance* ci); ~TypeCheckerImpl(); /** * Using control statement "for" to finish packages' typecheck. It invokes two functions as followed. * @see PrepareTypeCheck * @see TypeCheck */ void TypeCheckForPackages(const std::vector>& pkgs) { auto contexts = PreTypeCheck(pkgs); DoTypeCheck(contexts); PostTypeCheck(contexts); } /** * Perform auto boxing and recursive type resolving of enum. */ void PerformDesugarAfterInstantiation(ASTContext& ctx, AST::Package& pkg); // Desugar after sema. void PerformDesugarAfterSema(const std::vector>& pkgs); /** * Synthesize the given @p expr in given @p scopeName and return the found candidate decls or types. * If the @p hasLocal is true, the target will be found from local scope firstly. * @param ctx cached sema context. * @param scopeName the scopeName of current position. * @param expr the expression waiting to found candidate decls or types. * @param hasLocalDecl whether the given expression is existed in the given scope. * @return found candidate decls or types. */ Candidate SynReferenceSeparately(ASTContext& ctx, const std::string& scopeName, AST::Expr& expr, bool hasLocalDecl); void RemoveTargetNotMeetExtendConstraint(const Ptr baseTy, std::vector>& targets); private: /** * Main entry of the synthesis mode of the type checking. */ Ptr Synthesize(ASTContext& ctx, Ptr node); bool SynthesizeAndReplaceIdealTy(ASTContext& ctx, AST::Node& node); /** * Main entry of the check mode of the type checking. */ bool Check(ASTContext& ctx, Ptr target, Ptr node); bool IsChecked(ASTContext& ctx, AST::Node& node) const; std::optional PerformBasicChecksForCheck(ASTContext& ctx, Ptr target, Ptr node) const; /** * Cahched version of Synthesize and Check. * * Note that since cache key does not include any info from * ASTContext except for diags, this version must be used only at selected * places, such that the differences in ASTContext (notably, the types of Decls) * between two invokes with the same cache key won't affect the type check result. * Currently only for type check phase of overloading resolution. * * Once a change in AST or ASTContext could affect correctness of the cache, * the affected entries must be cleared. Currently, this could happen when allocating * new nodes during DesugarInTypeCheck, and checking lambda with omitted param type. */ Ptr SynthesizeWithCache(ASTContext& ctx, Ptr node); bool CheckWithCache(ASTContext& ctx, Ptr target, Ptr node); /** * Cahched version of Synthesize and Check. But only cache failed results. * Only for resolving overloaded builtin operators, * because this procedure lacks a post-check phase and will stop on success, * thus the successful check will always need to fully execute. * */ Ptr SynthesizeWithNegCache(ASTContext& ctx, Ptr node); bool CheckWithNegCache(ASTContext& ctx, Ptr target, Ptr node); /* * Use cached version of Synthesize and Check only when the same key was used * the last time this AST was fully checked, and not cleared ever since. * Also, it will NOT recover diags. * For function call post-check and any execution path that won't reach post-check. */ Ptr SynthesizeWithEffectiveCache(ASTContext& ctx, Ptr node, bool recoverDiag); bool CheckWithEffectiveCache(ASTContext& ctx, Ptr target, Ptr node, bool recoverDiag); Ptr SynthesizeAndCache(ASTContext& ctx, Ptr node, const AST::CacheKey& key); bool CheckAndCache(ASTContext& ctx, Ptr target, Ptr node, const AST::CacheKey& key); /** ======== PreCheck related functions implemented in src/Sema/PreCheck.cpp. ======== */ /** * Declaration redefinition check. * Note that declMap, which is used to look up an target declaration of a reference with known reference name and * scope name, is also built in this stage. */ void CheckRedefinition(ASTContext& ctx); /** * Perform redefinition check for all declarations @p syms of current package @p ctx , * invoked by CheckRedefinition. */ void CheckRedefinitionInDeclHelper(ASTContext& ctx, std::vector& syms); /** * Perform collision with sub-package names for all toplevel declarations in package @p pkg , * invoked by CheckRedefinition. */ void CheckConflictDeclWithSubPackage(const AST::Package& pkg); /** * Perform redefinition check for one symbol. If the declaration corresponding to the symbol @p sym is not * collected in declMap, add it to the declMap. If there are declarations with same @p names , * check whether redefinition happens according to the specification. */ void CollectDeclMapAndCheckRedefinitionForOneSymbol(ASTContext& ctx, const AST::Symbol& sym, const Names& names); /** * A collection of Functions which get sematic Ty from given AST Type. * @param Node: the AST Type. * @param stage: the stage of the PreCheck, which controls the LookUp parameters during declaration or reference * name resolution. * @return Sematic Ty. */ Ptr GetTyFromASTType(ASTContext& ctx, Ptr type); Ptr GetTyFromASTType(ASTContext& ctx, AST::RefType& rt); Ptr GetTyFromASTType(ASTContext& ctx, AST::QualifiedType& qt); Ptr GetTyFromASTCFuncType(ASTContext& ctx, AST::RefType& rt); Ptr GetTyFromASTType(ASTContext& ctx, AST::VArrayType& varrayType); Ptr GetTyFromASTType(ASTContext& ctx, AST::TupleType& tupleType); Ptr GetTyFromASTType(ASTContext& ctx, AST::FuncType& funcType); Ptr GetTyFromASTType(ASTContext& ctx, AST::OptionType& optionType); std::vector> GetTyFromASTType(ASTContext& ctx, std::vector>& typeArguments); Ptr GetTyFromASTType(AST::Decl& decl, const std::vector>& typeArgs); std::vector> GetTyFromASTType(const std::vector>& typeParameters); Ptr GetTyFromBuiltinDecl(const AST::BuiltInDecl& bid, const std::vector>& typeArgs); Ptr GetBuiltInArrayType(const std::vector>& typeArgs); Ptr GetBuiltInVArrayType(const std::vector>& typeArgs); Ptr GetBuiltinCFuncType(const std::vector>& typeArgs); Ptr GetBuiltInPointerType(const std::vector>& typeArgs); /** Check legality of attributes for all decls. */ void CheckAllDeclAttributes(const ASTContext& ctx); /** * Set the semantic Ty for a declaration @p decl directly by its ASTKind, name and typeArguments. */ void SetDeclTy(AST::Decl& decl); /** * Set the semantic Ty for a TypeAliasDecl @p tad and its aliased Ty. */ void SetTypeAliasDeclTy(ASTContext& ctx, AST::TypeAliasDecl& tad); /** * 1.Set declaration's semantic types only by its ASTKind, name and type arguments. * 2.Check declaration's attributes. * @param ctx type Checker context. * @param root the root node of AST. */ void ResolveDecls(ASTContext& ctx); void ResolveTypeAlias(const std::vector>& contexts); /** * 1.Set declaration's semantic types only by its ASTKind, name and type arguments. * 2.Check declaration's attributes. * @param ctx type Checker context. * @param decl the declaration to be resolved. */ void ResolveOneDecl(ASTContext& ctx, AST::Decl& decl); /** * Resolve all reference types' semantic type according to its name and its type arguments, more over, * substituting its ty when it is an aliased one. * @param ctx type Checker context. */ void ResolveNames(ASTContext& ctx); void SubstituteTypeAliasForAlias(AST::TypeAliasDecl& tad); /** * Resolve all reference types' semantic type according to its name and its type arguments, more over, * substituting its ty when it is an aliased one. * @param ctx type Checker context. * @param root the reference type. */ void SetTypeTy(ASTContext& ctx, AST::Type& type); /** * Cause of circular dependence between two or more packages, * firstly, build symbol table for all packages and typecheck redefinition except functions. */ void PrepareTypeCheck(ASTContext& ctx, AST::Package& pkg); /** * Collect mem2Decls map in ASTContext for quick lookup during type check */ void CollectDeclsWithMember(Ptr pkg, ASTContext& ctx); /** * The main Entry of PreCheck. * @param contexts the context of source packages to be checked. */ void PreCheck(const std::vector>& contexts); void PreCheckUsage(ASTContext& ctx, const AST::Package& pkg); void PreCheckInvalidInherit(const ASTContext& ctx, const AST::Package& pkg); /** * Check if there is a TypeAlias circle after all Decls are resolved. * @param ctx type Checker context. */ void TypeAliasCircleCheck(const ASTContext& ctx); /** * Check if the TypeAliasDecl @p tad is in type alias circle, * if so, add it into the circle path queue @p path. */ void CheckTypeAliasCycleForOneDecl(AST::TypeAliasDecl& tad, std::deque>& path); /** * Check if the typeArguments @p typeArgs is in type alias circle, * if so, add it into the circle path queue @p path. */ void CheckTypeAliasCycleForTypeArgsRecursively( std::vector>& typeArgs, std::deque>& path); /** * Check if a reference type @p type is in type alias circle, * if so, add it into the circle path queue @p path. */ void CheckTypeAliasCycleForOneType(AST::Type& type, std::deque>& path); /** * Check if there is type inheritance cycle after all Decls are resolved. * @param ctx type Checker context. */ void StructDeclCircleOrDupCheck(ASTContext& ctx); template void CheckInheritanceCycleHelper(ASTContext& ctx, const AST::Decl& decl, const Cangjie::AST::Type& te, std::deque>& path, Ptr extendDecl = nullptr); /** * Check whether the @p root exists cyclic dependency. */ void CheckInheritanceCycleDFS( ASTContext& ctx, AST::Decl& decl, std::deque>& path, Ptr extendDecl = nullptr); void CheckInheritanceCycleWithExtend( ASTContext& ctx, const AST::InheritableDecl& decl, std::deque>& path); /** * Check if the structure declaration corresponding to the symbol @p sym is in inheritance cycle. */ void StructDeclCircleOrDupCheckForOneSymbol(ASTContext& ctx, const AST::Symbol& sym); /** * Check if the structure declaration corresponding to the symbol @p sym is in inheritance cycle. */ void CheckInheritanceCycleDFSHandleVisiting( const AST::Decl& decl, const std::deque>& path, Ptr extendDecl); /** * Collect assumption for all generic Decls and check constraints sanity. * @param ctx type Checker context. */ void CollectAndCheckAssumption(ASTContext& ctx); /** * Collect assumption for all generic Decls and check constraints sanity for one generic Decl @p decl. */ void CollectAssumption(ASTContext& ctx, const AST::Decl& decl); /** * Check all upper bound legality. */ bool CheckUpperBoundsLegality(const AST::Generic& generic); /** * Check upper bound legality recursively. */ bool CheckUpperBoundsLegalityRecursively(const AST::Ty& upper); /** * Check assumption legality for a Decl @p decl including: * 1. Generic constraints check. * 2. Generic upper bounds legality check. * 3. Generic assumption sanity check. */ void CheckAssumption(ASTContext& ctx, const AST::Decl& decl); /** * Expose generic upper bounds transitively for generic constraints in @p generic. * For example, if there are constraints: where T <: U, U <: Int32, T will also have constraint T <: Int32 by * exposing to U. */ void ExposeGenericUpperBounds(ASTContext& ctx, const AST::Generic& generic) const; /** * Perform assumption sanity check for generic assumptions in @p generic. */ void AssumptionSanityCheck(const AST::Generic& generic); /** * Perform assumption sanity check for one generic ty @p genericTy whose assumptions is saved in its upperBounds. */ void SanityCheckForOneGenericTy(AST::GenericsTy& genericTy); /** * Assumption sanity check Rule 1: There can be no recursive constraint if the upper bound of * a type argument is a class irrelevant type. */ bool ValidRecursiveConstraintCheck(const AST::Generic& generic); /** * Assumption sanity check Rule 3: * Class irrelevant and classlike relevant types cannot exist at same time in the upper bound. */ bool SanityCheckForVarietyTypesOfUpperBounds(AST::GenericsTy& genericTy, const std::set>& classIrrelevantUpperBounds, const std::set>& classlikeUpperBounds); /** * Assumption sanity check Rule 4: If there are multiple classes, they must be in one inheritance chain. */ void SanityCheckForClassUpperBounds(AST::GenericsTy& genericTy, const std::set>& classUpperBounds); /** * Check and reduce upper bounds where there are upper bounds in same inheritance chain, * using their maximum common child type. */ bool CheckAndReduceUpperBounds(AST::GenericsTy& genericTy, const std::set>& upperBounds); /** * Add assumption for all type alias declarations. */ void IgnoreAssumptionForTypeAliasDecls(const ASTContext& ctx) const; void AddAssumptionForExtendDecls(ASTContext& ctx); /** * Add assumption for type according to its type's assumption. * For type alias A = C. C and a1..an may have constraints and * related constraints must be imposed on T1..Tn to make type check on C pass. * For achieving this, we do: * 1. Construct a reversed map (revTypeMapping) from T1..Tn to their constraints declared in C * by applying GetRevTypeMapping. * 2. Construct all assumption collection map (allAssumptionMap) following subtype constraints * 3. Construct a type arguments applied map (typeArgAppliedMap) from C to C * 4. For each type in T1..Tn, find revTypeMapping[Ti], this get Ti's correspond type parameter * 5. Get its constraints from allAssumptionMap[Pi]. This may contains generic arguments that are not declared * in type alias, so we need to substitute them by typeArgAppliedMap. * 6. Add the constraints to type alias. */ void AddAssumptionForType(ASTContext& ctx, const AST::Type& type, AST::Generic& generic); /** * Add assumption to the upperBound of generic parameters' ty of generic declarations @p generic according to * its aliased type @p aliasedTypeTarget's assumption. */ void AddUpperBoundOnTypeParameters(ASTContext& ctx, const AST::Generic& generic, const AST::Decl& typeTarget, MultiTypeSubst& revTypeMapping, TyVarUB& allAssumptionMap, const TypeSubst& typeArgAppliedMap); void GetAllAssumptions(AST::TyVarEnv& source, AST::TyVarEnv& newMap); /** * Add Object to all ClassDecls' inheritedTypes if there is no one. */ void AddSuperClassObjectForClassDecl(ASTContext& ctx); void CheckAndAddSubDecls( const AST::Type& type, AST::ClassDecl& cd, bool& hasSuperClass, int& superClassLikeNum) const; bool HasSuperClass(AST::ClassDecl& cd) const; /** * If a class doesn't have super class, set Core.Object class as its super class. * NOTICE: it will change AST Node! */ void AddObjectSuperClass(ASTContext& ctx, AST::ClassDecl& cd); /** * LLVM-java interop scenario. */ bool AddJObjectSuperClassJavaInterop(ASTContext& ctx, AST::ClassDecl& cd); void AddDefaultSuperCall(const AST::FuncBody& funcBody) const; /** * If there is no default constructor, insert one. * NOTICE: it will change AST Node! */ void AddDefaultCtor(AST::InheritableDecl& decl) const; void AddDefaultFunction(AST::Node& root); /** * Check function redefinition at PreCheck stage for the scenario including: * 1. Functions in the Enum. * 2. Functions are Foreign. * 3. Static and non-static duplications in class-like context. * 4. Same signature. */ void PreCheckFuncRedefinition(const ASTContext& ctx); /** * Check function redefinition at PreCheck stage for the functions in the Enum. */ void PreCheckMacroRedefinition(const std::vector>& funcs) const; void PreCheckFuncRedefinitionForEnum(const std::vector>& funcs); /** * Check function redefinition at PreCheck stage for the functions related to FFI feature. * For C FFI, this check includes foreign func and @C func. * NOTE: C FFI FFI cannot be used at the same time currently. */ void PreCheckFuncRedefinitionForFFI(const std::vector>& funcs); /** * Check function redefinition at PreCheck stage for the static and non-static duplications as member decl. */ void PreCheckFuncStaticConflict(const std::vector>& funcs); /** * Check function redefinition at PreCheck stage for the functions with same signature. */ bool PreCheckFuncRedefinitionWithSameSignature(std::vector> funcs, bool needReportErr = true); void PreCheckExtend(ASTContext& ctx, AST::ExtendDecl& ed); void PreCheckAllExtendInterface(); void CheckSpecializationExtend(const AST::InheritableDecl& extendedDecl, const AST::ExtendDecl& extendDecl, const std::set, AST::CmpNodeByPos> otherExtendDecls); void CheckSpecializationExtendDupImstantation(const AST::Ty& extendedDeclTy, const AST::ExtendDecl& compareExtend, const AST::InheritableDecl& beComparedDecl, const TypeSubst& instantMapping, const bool checkParent = false); void BuildImportedExtendMap(); void BuildExtendMap(ASTContext& ctx); void CheckExtendRules(const ASTContext& ctx); /* Set integer overflow strategy before sema typechecking. */ void SetIntegerOverflowStrategy() const; void CheckDefaultParamFuncsEntry(AST::File& file); AST::VisitAction CheckDefaultParamFunc(AST::StructDecl& sd) const; AST::VisitAction CheckDefaultParamFunc(AST::ClassDecl& cd, const AST::File& file) const; AST::VisitAction CheckDefaultParamFunc(const AST::InterfaceDecl& ifd) const; AST::VisitAction CheckDefaultParamFunc(const AST::EnumDecl& ed) const; void GetSingleParamFunc(AST::Decl& decl); void CheckPrimaryCtorForClassOrStruct(AST::InheritableDecl& id); /** * ReturnExpr must in FuncBody and JumpExpr must in LoopExpr. */ void CheckReturnAndJump(const ASTContext& ctx); void SetEnumEleTy(AST::Decl& constructor); void SetEnumEleTyHandleFuncDecl(AST::FuncDecl& funcDecl); void PreSetDeclType(const ASTContext& ctx); void ReplaceThisTypeInFunc(const AST::FuncDecl& funcDecl); /** * Substitute typeAlias typeArguments, and return function targets for a reference node. */ Ptr GetRealTarget(Ptr const node, Ptr const target); /** * Search target by namespace(like 'class', 'interface', 'struct', 'enum') and field name. */ std::vector> FieldLookup( const ASTContext& ctx, Ptr decl, const std::string& fieldName, const LookupInfo& info = {}); std::vector> PreTypeCheck(const std::vector>& pkgs); void DoTypeCheck(std::vector>& contexts) { Utils::ProfileRecorder recorder("Semantic", "In TypeCheck"); // Type checking to add semantic type values. for (auto& ctx : contexts) { TypeCheck(*ctx, *ctx->curPackage); } } void PostTypeCheck(std::vector>& contexts); /** * Cause of circular dependence between two or more packages, * secondly, typecheck function redefinition and variable initialization. */ void TypeCheck(ASTContext& ctx, AST::Package& pkg); void TypeCheckTopLevelDecl(ASTContext& ctx, AST::Decl& decl); void TypeCheckImportedGenericMember(ASTContext& ctx); void CheckOverflow(AST::Node& node); void CheckWhetherHasProgramEntry(); /** * Desugar APIs during sema check. */ void DesugarArrayCall(ASTContext& ctx, AST::CallExpr& ce); void DesugarPointerCall(ASTContext& ctx, AST::CallExpr& ce); /** Desugar 'propDecl' after typecheck but still inside 'Sema' stage. Keep this order for cjLint usage. */ void DesugarForPropDecl(AST::Node& pkg); /** Desugar for string interpolation expr. */ std::vector> MatchToStringImpl(const ASTContext& ctx, const AST::File& file, AST::Ty& ty); OwnedPtr DesugarStrPartExpr( const ASTContext& ctx, AST::Expr& expr, const std::vector> appendDecls, AST::VarDecl& sbItem); void DesugarStrInterpolationExpr(ASTContext& ctx, AST::LitConstExpr& litConstExpr); /** * Desugar APIs after sema check. */ void PerformDesugarAfterTypeCheck(ASTContext& ctx, AST::Package& pkg); void GenerateMainInvoke(); void TryDesugarForCoalescing(AST::Node& root) const; void DesugarForCoalescing(AST::BinaryExpr& binaryExpr) const; void DesugarForInExpr(ASTContext& ctx, AST::ForInExpr& forInExpr); void DesugarForInCloseRange(ASTContext& ctx, AST::ForInExpr& forInExpr); void DesugarForInNonCloseRange(ASTContext& ctx, AST::ForInExpr& forInExpr); void DesugarForInIter(ASTContext& ctx, AST::ForInExpr& forInExpr); void DesugarForInString(ASTContext& ctx, AST::ForInExpr& forInExpr); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void ReArrangeForInExpr(ASTContext& ctx, AST::ForInExpr& forInExpr); #endif void ReArrangeForInRangeExpr(ASTContext& ctx, AST::ForInExpr& forInExpr); void ReArrangeForInIterExpr(ASTContext& ctx, AST::ForInExpr& forInExpr); void ReArrangeForInStringExpr(ASTContext& ctx, AST::ForInExpr& forInExpr); void DesugarTryWithResourcesExpr(ASTContext& ctx, AST::TryExpr& te); OwnedPtr ConstructOptionMatch(OwnedPtr selector, OwnedPtr someExpr, OwnedPtr otherExpr, AST::RefExpr& someVar, Ptr someTy) const; void DesugarTryToFrame(ASTContext& ctx, AST::TryExpr& te); void DesugarPerform(ASTContext& ctx, AST::PerformExpr& pe); void DesugarResume(ASTContext& ctx, AST::ResumeExpr& re); void DesugarImmediateResume(ASTContext& ctx, AST::ResumeExpr& re); OwnedPtr GetHelperFrameMethod( AST::Node& base, const std::string& methodName, std::vector> typeArgs); void CreateResult( ASTContext& ctx, const AST::TryExpr& te, AST::VarDecl& frame, std::vector>& block); void CreateSetHandler( ASTContext& ctx, AST::TryExpr& te, AST::VarDecl& frame, std::vector>& block); void CreateSetFinally( ASTContext& ctx, AST::TryExpr& te, AST::VarDecl& frame, std::vector>& block); AST::VarDecl& CreateFrame(ASTContext& ctx, AST::TryExpr& te, std::vector>& block); void EncloseTryLambda(ASTContext& ctx, OwnedPtr& tryLambda); /* Synthesize specialized for desugar after sema. Will not recover previous desugar results */ Ptr SynthesizeWithoutRecover(ASTContext& ctx, Ptr node); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void PerformToAnyInsertion(AST::Package& pkg); OwnedPtr CreateToAny(AST::Decl& outerDecl); #endif void DesugarJArrayCtorCall(const ASTContext& ctx, AST::CallExpr& ce); void DesugarTokenCallExpr(ASTContext& ctx, AST::CallExpr& ce); void DesugarSpawnExpr(const ASTContext& ctx, AST::SpawnExpr& se); void DesugarSpawnArgExpr(const ASTContext& ctx, const AST::SpawnExpr& se); /** Get decls with symbol by context searcher. */ std::vector GetToplevelDecls(const ASTContext& ctx) const; std::vector GetAllDecls(const ASTContext& ctx) const; std::vector GetGenericCandidates(const ASTContext& ctx) const; std::vector GetAllStructDecls(const ASTContext& ctx) const; void WarmupCache(const ASTContext& ctx) const; std::vector GetSymsByASTKind( const ASTContext& ctx, AST::ASTKind astKind, const Order& order = Sort::posDesc) const; /** * Get the target members for the extends of @param ty. */ std::vector> ExtendFieldLookup( const ASTContext& ctx, const AST::File& file, Ptr ty, const std::string& fieldName); /** * Find the symbols from name and scope name. * @param ctx: the current ASTContext. * @param name: the name of the symbol. * @param scopeName: the scopeName of the symbol. * @param node: the astNode of symbol. */ std::vector> Lookup(const ASTContext& ctx, const std::string& name, const std::string& scopeName, const AST::Node& node, bool isSetter = false); std::vector> LookupTopLevel(const ASTContext& ctx, const std::string& name, const std::string& scopeName, const AST::Node& node, bool isSetter = false); /** * Analyze the linkage of functions and mark them as internal if possible. */ void AnalyzeFunctionLinkage(AST::Package& pkg) const; void CheckSealedInheritance(const AST::Decl& child, const AST::Type& parent); void CheckThreadContextInheritance(const AST::Decl& decl, const AST::Type& parent); void CheckJavaInteropLibImport(AST::Decl& decl); void CheckObjCInteropLibImport(AST::Decl& decl); void CheckClassDecl(ASTContext& ctx, AST::ClassDecl& cd); void CheckInterfaceDecl(ASTContext& ctx, AST::InterfaceDecl& id); void CheckStructDecl(ASTContext& ctx, AST::StructDecl& sd); void BuildImportedEnumConstructorMap(ASTContext& ctx); void BuildEnumConstructorMap(ASTContext& ctx) const; void CheckEnumDecl(ASTContext& ctx, AST::EnumDecl& ed); void TypeCheckCompositeBody( ASTContext& ctx, const AST::Decl& structDecl, const std::vector>& body); void CheckVarWithPatternDecl(ASTContext& ctx, AST::VarWithPatternDecl& vpd); // T should be VarWithPatternDecl or VarDecl. A way to avoid restructuring the AST.h file. template void SynchronizeTypeAndInitializer(ASTContext& ctx, T& vd); void CheckVarDecl(ASTContext& ctx, AST::VarDecl& vd); void CheckPropDecl(ASTContext& ctx, AST::PropDecl& pd); void UpdateMemberVariableTy(const AST::Decl& decl, const AST::EnumTy& eTy); Ptr SynBlock(ASTContext& ctx, AST::Block& b); bool ChkBlock(ASTContext& ctx, AST::Ty& target, AST::Block& b); /** * Arithmetic operator { +, -, *, /, ** } */ std::optional> SynArithmeticOrRelationalExpr( ASTContext& ctx, AST::BinaryExpr& be, bool isArithmetic = true); Ptr SynLogicalExpr(ASTContext& ctx, AST::BinaryExpr& be); Ptr SynShiftExpr(ASTContext& ctx, AST::BinaryExpr& be); Ptr SynFlowExpr(ASTContext& ctx, AST::BinaryExpr& be); Ptr SynCoalescingExpr(ASTContext& ctx, AST::BinaryExpr& be); bool ChkArithmeticExpr(ASTContext& ctx, AST::Ty& target, AST::BinaryExpr& be); // Check exponentiation expressions with target types. bool ChkExpoExpr(ASTContext& ctx, AST::Ty& tgtTy, AST::BinaryExpr& be); bool ChkExpoExprBase(ASTContext& ctx, const AST::BinaryExpr& be, AST::Ty& baseTy); bool ChkExpoExprExponent(ASTContext& ctx, AST::Expr& exponent, std::vector> exTys); bool CheckExponentByBaseTy(ASTContext& ctx, AST::Ty& baseTy, const AST::Expr& base, AST::Expr& exponent); Ptr SynExpoExpr(ASTContext& ctx, AST::BinaryExpr& be); bool ChkLogicalExpr(ASTContext& ctx, AST::Ty& target, AST::BinaryExpr& be); bool ChkRelationalExpr(ASTContext& ctx, AST::Ty& target, AST::BinaryExpr& be); bool ChkShiftExpr(ASTContext& ctx, AST::Ty& target, AST::BinaryExpr& be); bool ChkCoalescingExpr(ASTContext& ctx, Ptr tgtTy, AST::BinaryExpr& be); bool IsCoalescingLeftTyValid(AST::Ty& ty) const; /** * Try to find a unique Ty that can constrain the ty var. * The new constraint will be added only if the result is UNIQUE. */ MatchResult PickConstaintFromTys(TyVar& tv, std::set> tys, bool isUB); MatchResult PickConstaintFromTys( AST::Ty& tv1, AST::Ty& tv2, std::set, Ptr>> tys, bool isUB); std::optional> PerformBasicChecksForSynthesize(ASTContext& ctx, Ptr node) const; /** * Check if the compiler can add a built-in == (or !=) for these two tuple types. * If and only if all element types of two tuple types supports == (or !=), * the compiler provides a built-in == (or !=) for these two tuple types. */ bool CheckTupleCanEqual(ASTContext& ctx, AST::BinaryExpr& be); bool ChkFlowExpr(ASTContext& ctx, Ptr target, AST::BinaryExpr& be); bool CheckFlowOperandsHaveNamedParam(const AST::CallExpr& ce); void DiagnoseForBinaryExpr(ASTContext& ctx, AST::BinaryExpr& be); void DiagnoseForUnaryExpr(ASTContext& ctx, AST::UnaryExpr& ue); void DiagnoseForUnaryExprWithTarget(ASTContext& ctx, AST::UnaryExpr& ue, AST::Ty& target); bool ChkBinaryExpr(ASTContext& ctx, AST::Ty& target, AST::BinaryExpr& be); Ptr SynBinaryExpr(ASTContext& ctx, AST::BinaryExpr& be); Ptr SynIncOrDecExpr(ASTContext& ctx, AST::IncOrDecExpr& ide); bool ChkIncOrDecExpr(ASTContext& ctx, AST::Ty& target, AST::IncOrDecExpr& ide); Ptr SynTypeConvExpr(ASTContext& ctx, AST::TypeConvExpr& tce); Ptr SynNumTypeConvExpr(AST::TypeConvExpr& tce); bool SynCFuncCall(ASTContext& ctx, AST::CallExpr& ce); bool ChkTypeConvExpr(ASTContext& ctx, AST::Ty& targetTy, AST::TypeConvExpr& tce); Ptr SynLoopControlExpr(const ASTContext& ctx, AST::JumpExpr& je) const; bool ChkLoopControlExpr(const ASTContext& ctx, AST::JumpExpr& je) const; Ptr SynLamExpr(ASTContext& ctx, AST::LambdaExpr& le); bool SolveLamExprParamTys(ASTContext& ctx, AST::LambdaExpr& le); void ResetLambdaForReinfer(ASTContext& ctx, const AST::LambdaExpr& le); void TryInferFromSyntaxInfo(ASTContext& ctx, const AST::LambdaExpr& le); bool ChkLamExpr(ASTContext& ctx, AST::Ty& target, AST::LambdaExpr& le); bool ChkLamParamTys(ASTContext& ctx, AST::LambdaExpr& le, const std::vector>& tgtParamTys, std::vector>& lamParamTys); bool ChkLamBody(ASTContext& ctx, AST::FuncBody& lamFb); Ptr SynIfExpr(ASTContext& ctx, AST::IfExpr& ie); bool ChkIfExpr(ASTContext& ctx, AST::Ty& tgtTy, AST::IfExpr& ie); Ptr ReplaceThisTy(Ptr now); bool ChkIfExprNoElse(ASTContext& ctx, AST::Ty& target, AST::IfExpr& ie); bool ChkIfExprTwoBranches(ASTContext& ctx, AST::Ty& target, AST::IfExpr& ie); /// Check and diagnose conditions in if and while /// The target type is necessarily Bool and thus omitted. bool CheckCondition(ASTContext& ctx, AST::Expr& e, bool suppressIntroducingVariableError); bool CheckBinaryCondition(ASTContext& ctx, AST::BinaryExpr& e, bool suppressIntroducingVariableError); bool SynLetPatternDestructor( ASTContext& ctx, AST::LetPatternDestructor& lpd, bool suppressIntroducingVariableError); std::optional> PromoteToCommandTy(const AST::Node& cause, AST::Ty& cmdTy); Ptr SynThrowExpr(ASTContext& ctx, AST::ThrowExpr& te); Ptr SynPerformExpr(ASTContext& ctx, AST::PerformExpr& pe); Ptr SynResumeExpr(ASTContext& ctx, AST::ResumeExpr& re); Ptr SynTryExpr(ASTContext& ctx, AST::TryExpr& te); Ptr SynTryWithResourcesExpr(ASTContext& ctx, AST::TryExpr& te); std::optional> SynTryExprCatchesAndHandles(ASTContext& ctx, AST::TryExpr& te); bool SynHandler(ASTContext& ctx, AST::Handler& handler, Ptr tgtTy, AST::TryExpr& te); bool ChkTryExpr(ASTContext& ctx, AST::Ty& tgtTy, AST::TryExpr& te); bool ChkTryExprCatchesAndHandles(ASTContext& ctx, AST::Ty& tgtTy, AST::TryExpr& te); bool ChkTryExprCatchPatterns(ASTContext& ctx, AST::TryExpr& te); bool ChkTryExprHandlePatterns(ASTContext& ctx, AST::TryExpr& te); bool ChkHandler(ASTContext& ctx, AST::Handler& handler, AST::Ty& tgtTy); bool ValidateBlockInTryHandle(AST::Block& block); bool ValidateHandler(AST::Handler& h); bool ChkTryExprFinallyBlock(ASTContext& ctx, const AST::TryExpr& te); bool ChkQuoteExpr(ASTContext& ctx, AST::Ty& target, AST::QuoteExpr& qe); Ptr SynQuoteExpr(ASTContext& ctx, AST::QuoteExpr& qe); Ptr SynUnaryExpr(ASTContext& ctx, AST::UnaryExpr& ue); bool ChkUnaryExpr(ASTContext& ctx, AST::Ty& target, AST::UnaryExpr& ue); Ptr SynBuiltinUnaryExpr(ASTContext& ctx, AST::UnaryExpr& ue); Ptr SynParenExpr(ASTContext& ctx, AST::ParenExpr& pe); bool ChkParenExpr(ASTContext& ctx, AST::Ty& target, AST::ParenExpr& pe); Ptr SynAssignExpr(ASTContext& ctx, AST::AssignExpr& ae); Ptr SynMultipleAssignExpr(ASTContext& ctx, AST::AssignExpr& ae); bool ChkAssignExpr(ASTContext& ctx, AST::Ty& target, AST::AssignExpr& ae); bool IsAssignable(AST::Expr& e, bool isCompound, const std::vector& diags) const; bool IsShiftAssignValid(const AST::AssignExpr& ae); Ptr SynLitConstExpr(ASTContext& ctx, AST::LitConstExpr& lce); Ptr SynLitConstStringExpr(ASTContext& ctx, AST::LitConstExpr& lce); bool ChkLitConstExpr(ASTContext& ctx, AST::Ty& target, AST::LitConstExpr& lce); bool ChkLitConstExprOfTypeBool(AST::Ty& target, AST::LitConstExpr& lce); bool ChkLitConstExprOfTypeUnit(AST::Ty& target, AST::LitConstExpr& lce); bool ChkLitConstExprOfTypeInteger(AST::Ty& target, AST::LitConstExpr& lce); bool ChkLitConstExprOfTypeFloat(AST::Ty& targetTy, AST::LitConstExpr& lce); bool ChkLitConstExprOfTypeChar(AST::Ty& targetTy, AST::LitConstExpr& lce); bool ChkLitConstExprOfTypeString(ASTContext& ctx, AST::Ty& target, AST::LitConstExpr& lce); Ptr SynWhileExpr(ASTContext& ctx, AST::WhileExpr& we); bool ChkWhileExpr(ASTContext& ctx, AST::Ty& target, AST::WhileExpr& we); Ptr SynDoWhileExpr(ASTContext& ctx, AST::DoWhileExpr& dwe); bool ChkDoWhileExpr(ASTContext& ctx, AST::Ty& target, AST::DoWhileExpr& dwe); Ptr SynTupleLit(ASTContext& ctx, AST::TupleLit& tl); bool ChkTupleLit(ASTContext& ctx, AST::Ty& target, AST::TupleLit& tl); Ptr SynReturnExpr(ASTContext& ctx, AST::ReturnExpr& re); bool ChkReturnExpr(ASTContext& ctx, AST::ReturnExpr& re); bool CheckReturnInConstructors(ASTContext& ctx, const AST::ReturnExpr& re); Ptr SynFuncArg(ASTContext& ctx, AST::FuncArg& fa); bool ChkFuncArg(ASTContext& ctx, AST::Ty& target, AST::FuncArg& fa); bool ChkFuncArgWithInout(ASTContext& ctx, AST::Ty& target, AST::FuncArg& fa); bool ChkInoutFuncArg(const AST::FuncArg& fa); bool ChkInoutRefExpr(AST::RefExpr& re, bool isBase = false); bool ChkInoutMemberAccess(const AST::MemberAccess& ma); Ptr SynFuncParam(ASTContext& ctx, AST::FuncParam& fp); bool ChkFuncParam(ASTContext& ctx, AST::Ty& target, AST::FuncParam& fp); Ptr SynIsExpr(ASTContext& ctx, AST::IsExpr& ie); bool ChkIsExpr(ASTContext& ctx, AST::Ty& target, AST::IsExpr& ie); Ptr SynAsExpr(ASTContext& ctx, AST::AsExpr& ae); bool ChkAsExpr(ASTContext& ctx, AST::Ty& target, AST::AsExpr& ae); Ptr SynOptionalChainExpr(ASTContext& ctx, AST::OptionalChainExpr& oce); bool ChkOptionalChainExpr(ASTContext& ctx, AST::Ty& target, AST::OptionalChainExpr& oce); /** * Checks whether @param target is an auto-boxed Option of @param ty */ bool CheckOptionBox(AST::Ty& target, AST::Ty& ty); bool ChkRangeExpr(ASTContext& ctx, AST::Ty& target, AST::RangeExpr& re); Ptr SynRangeExpr(ASTContext& ctx, AST::RangeExpr& re); Ptr SynRangeExprInferElemTy(const AST::RangeExpr& re, ASTContext& ctx); bool CheckRangeElements(ASTContext& ctx, Ptr elemTy, const AST::RangeExpr& re); Ptr SynArrayLit(ASTContext& ctx, AST::ArrayLit& al); bool ChkArrayLit(ASTContext& ctx, AST::Ty& target, AST::ArrayLit& al); Ptr GetArrayTypeByInterface(AST::Ty& interfaceTy); Ptr SynArrayExpr(ASTContext& ctx, AST::ArrayExpr& ae); Ptr SynVArrayExpr(ASTContext& ctx, AST::ArrayExpr& ve); bool ChkArrayExpr(ASTContext& ctx, AST::Ty& target, AST::ArrayExpr& ae); bool ChkVArrayExpr(ASTContext& ctx, AST::Ty& target, AST::ArrayExpr& ve); bool ChkVArrayArg(ASTContext& ctx, AST::ArrayExpr& ve); bool ChkSizedArrayExpr(ASTContext& ctx, AST::Ty& target, AST::ArrayExpr& ae); bool ChkSingeArgArrayExpr(ASTContext& ctx, AST::Ty& target, AST::ArrayExpr& ae); bool ChkSingeArgArrayWithoutElemTy(ASTContext& ctx, AST::Ty& target, AST::ArrayExpr& ae); bool ChkArrayArgs(AST::ArrayExpr& ae); /** * Check whether the alias is a built-in type. * @param kind Check whether the alias is of the specified type. If it's TYPE_ANY, Check Array/CPointer/CString. */ bool IsBuiltinTypeAlias(const AST::Decl& decl, const AST::TypeKind kind = AST::TypeKind::TYPE_ANY) const; bool ChkSizedArrayElement(ASTContext& ctx, AST::Ty& elemTargetTy, AST::ArrayExpr& ae); bool ChkSizedArrayWithoutElemTy(ASTContext& ctx, AST::Ty& target, AST::ArrayExpr& ae); bool IsCallOfBuiltInType(const AST::CallExpr& ce, const AST::TypeKind kind) const; bool ChkArrayCall(ASTContext& ctx, AST::Ty& target, AST::CallExpr& ce); bool ChkCFuncCall(ASTContext& ctx, AST::Ty& target, AST::CallExpr& ce); bool ChkBuiltinCall(ASTContext& ctx, AST::Ty& target, AST::CallExpr& ce); bool ChkPointerCall(ASTContext& ctx, AST::Ty& target, AST::CallExpr& ce); bool ChkVArrayCall(ASTContext& ctx, AST::Ty& target, AST::CallExpr& ce); bool ChkPointerExpr(ASTContext& ctx, AST::Ty& target, AST::PointerExpr& cpe); /** * Check whether the CString is invoked. */ bool ChkCStringCall(ASTContext& ctx, AST::Ty& target, AST::CallExpr& ce); Ptr SynPointerExpr(ASTContext& ctx, AST::PointerExpr& cptrExpr); Ptr SynMatchExpr(ASTContext& ctx, AST::MatchExpr& me); Ptr SynMatchExprHasSelector(ASTContext& ctx, AST::MatchExpr& me); Ptr SynMatchExprNoSelector(ASTContext& ctx, AST::MatchExpr& me); Ptr SynMatchCaseNoSelector(ASTContext& ctx, AST::MatchCaseOther& mco); Ptr SynNormalMatchCaseBody(ASTContext& ctx, AST::MatchExpr& me); Ptr SynQuestSugarMatchCaseBody(ASTContext& ctx, AST::MatchExpr& me); bool ChkMatchExpr(ASTContext& ctx, AST::Ty& target, AST::MatchExpr& me); bool ChkMatchExprHasSelector(ASTContext& ctx, AST::Ty& target, AST::MatchExpr& me); bool ChkMatchExprNoSelector(ASTContext& ctx, AST::Ty& target, AST::MatchExpr& me); bool ChkPatternsSameASTKind(const ASTContext& ctx, const std::vector>& patterns); bool ChkNoVarPatternInOrPattern(const ASTContext& ctx, const std::vector>& ps); bool ChkMatchCasePatterns(ASTContext& ctx, Ptr target, AST::MatchCase& mc); bool ChkMatchCasePatGuard(ASTContext& ctx, const AST::MatchCase& mc); bool ChkMatchCaseActions(ASTContext& ctx, Ptr target, AST::MatchCase& mc); bool ChkMatchCaseNoSelector(ASTContext& ctx, AST::Ty& target, AST::MatchCaseOther& mco); bool ChkSubscriptExpr(ASTContext& ctx, Ptr target, AST::SubscriptExpr& se); Ptr SynSubscriptExpr(ASTContext& ctx, AST::SubscriptExpr& se); bool ChkTupleAccess(ASTContext& ctx, Ptr target, AST::SubscriptExpr& se, AST::TupleTy& tupleTy); bool ChkVArrayAccess(ASTContext& ctx, Ptr target, AST::SubscriptExpr& se, AST::VArrayTy& varrTy); Ptr SynCallExpr(ASTContext& ctx, AST::CallExpr& ce); Ptr SynTrailingClosure(ASTContext& ctx, AST::TrailingClosureExpr& tc); bool ChkTrailingClosureExpr(ASTContext& ctx, AST::Ty& target, AST::TrailingClosureExpr& tc); void CheckMacroCall(ASTContext& ctx, AST::Node& macroNode); /** * Check call expressions' related APIs. */ bool ChkCallExpr(ASTContext& ctx, Ptr target, AST::CallExpr& ce); bool ChkDesugarExprOfCallExpr(ASTContext& ctx, Ptr target, AST::CallExpr& ce); bool CheckCallKind(const AST::CallExpr& ce, Ptr decl, AST::CallKind& type); /** * Check call expression's base expression which is member access's or normal reference type. * Get current target and function candidates. */ bool GetCallBaseCandidates(const ASTContext& ctx, const AST::CallExpr& ce, AST::Expr& expr, Ptr& target, std::vector>& candidates); bool CheckRefConstructor(const ASTContext& ctx, const AST::CallExpr& ce, const AST::RefExpr& re); /** * Check call expression's base expression. Get current target and function candidates. */ bool ChkCallBaseExpr(ASTContext& ctx, AST::CallExpr& ce, Ptr& targetDecl, Ptr& targetRet, std::vector>& candidates); bool ChkCallBaseRefExpr( ASTContext& ctx, AST::CallExpr& ce, Ptr& target, std::vector>& candidates); bool ChkCallBaseMemberAccess( ASTContext& ctx, AST::CallExpr& ce, Ptr& target, std::vector>& candidates); bool ChkCurryCallBase(ASTContext& ctx, AST::CallExpr& ce, Ptr& targetRet); bool CheckNonNormalCall(ASTContext& ctx, Ptr target, AST::CallExpr& ce); bool ChkFunctionCallExpr(ASTContext& ctx, Ptr target, AST::CallExpr& ce); bool ChkVariadicCallExpr(ASTContext& ctx, Ptr target, AST::CallExpr& ce, const std::vector>& candidates, std::vector& diagnostics); bool SynArgsOfNothingBaseExpr(ASTContext& ctx, AST::CallExpr& ce); /** * One call expression may have multiple function candidates. * Filter them with parameters' conditions before legality check. * @param ctx Type Checker context. * @param inCandidates The possible target functions list to be filtered. * @param ce The call expression target. * @return Filtered candidates list. */ std::vector> FilterCandidates( const ASTContext& ctx, const std::vector>& inCandidates, const AST::CallExpr& ce); std::vector> FilterCandidatesWithReExport( const ASTContext& ctx, const std::vector>& inCandidates, const AST::CallExpr& ce); /** * Override member funcs in Class/Interface inheritance will be filter in previous lookup stage. * So 2 cases considered here (generic function's comparison not considered here), * 1) Nested functions will shadow other functions. * 2) Class/Interface funcs will shadow top-level functions. * 3) implemented version in extend will shadow its abstract func in its extend type or interface. * @param candidates FuncDecl candidates. all should be synthesized. */ void FilterShadowedFunc(std::vector>& candidates); /** * Implemented version in extend will shadow its abstract func in its extend type or interface */ void FilterExtendImplAbstractFunc(std::vector>& candidates); void RemoveShadowedFunc( const AST::FuncDecl& fd, int64_t currentLevel, int64_t targetLevel, std::vector>& funcs); /** * In CallExpr, the called function may has default parameters and callExpr can omit the specification of * some parameters. Reorder all arguments. * @param fd The target function found for callExpr. * @return The matched target of callExpr, may be a nullptr. */ std::vector> ReorderCallArgument(ASTContext& ctx, FunctionMatchingUnit& fmu, AST::CallExpr& ce); AST::Ty* GetCallTy(ASTContext& ctx, const AST::CallExpr& ce, const AST::FuncDecl& target) const; /** * Check whether arguments match function declare. In this case target function is not overloaded. * @param fd Target function to be checked. * @param ce The call expression target. * @param target Given upper-bound of function return type * @return Matched function decl. */ std::vector> CheckFunctionMatch( ASTContext& ctx, FunctionCandidate& candidate, Ptr target, SubstPack& typeMapping); std::vector> GetOrderedCandidates(const ASTContext& ctx, const AST::CallExpr& ce, std::vector>& candidates, std::unordered_map, int64_t>& fdScopeLevelMap) const; std::vector> MatchFunctionForCall(ASTContext& ctx, std::vector>& candidates, AST::CallExpr& ce, Ptr target, SubstPack& typeMapping); /** * Get valid function types for given candidates of @p expr * returns: genericIgnored, std::vector. */ using FuncTyPair = std::pair, Ptr, TypeSubst>>>; FuncTyPair CollectValidFuncTys( ASTContext& ctx, std::vector>& funcs, AST::Expr& expr, Ptr targetTy = nullptr); /** * Determines whether the parameters of the funcDecl are compatible with the arguments in the callExpr. * In case of compatibility, the parameter types corresponding to the arguments are collected. * @param fd Target function to be checked. * @param argMapping Record the type of the parameter corresponding to the argument. Will be used in function * overload resolution. * @return Whether funcDecl is a legal target of the callExpr. If there is a parameter's type is incompatible with * the argument, return false. Otherwise, return true. */ OwnedPtr CheckCandidate( ASTContext& ctx, FunctionCandidate& candidate, Ptr targetRet, SubstPack& typeMapping); /** * Check whether the parameters type is compatible with arguments type. * The arguments can be parameters subtype. */ bool CheckCallCompatible(ASTContext& ctx, FunctionCandidate& candidate); /** * When the candidate is a generic function, or function is a member of generic class/interface/struct..., * we should use this function to do compatibility check. First we will build a unordered map to represent * the mapping of generic types. Then we check the compatibility. Pay attention to checking generic constraints. */ bool CheckGenericCallCompatible( ASTContext& ctx, FunctionCandidate& candidate, SubstPack& typeMapping, Ptr targetRet); void FilterTypeMappings( const AST::Expr& expr, AST::FuncDecl& fd, std::vector& typeMappings); bool CheckCandidateConstrains(const AST::CallExpr& ce, const AST::FuncDecl& fd, const SubstPack& typeMapping); bool CheckAndGetMappingForTypeDecl(const AST::Expr& baseExpr, const AST::Decl& typeDecl, const AST::Decl& targetDecl, const SubstPack& typeMapping); /** * Get combinations of all possible function types for every argument of function call. */ std::vector>> GetArgTyPossibilities(ASTContext& ctx, AST::CallExpr& ce); std::vector>> GetArgsCombination(ASTContext& ctx, AST::CallExpr& ce); // check if any level of baseExpr(case it's MemberAccess) is of placeholder ty // will Synthesize the expr if it doesn't have a ty yet bool HasBaseOfPlaceholderTy(ASTContext& ctx, Ptr n); /** * Build the generic type mapping table. The constraints should be checked in the process. True is returned only if * the mapping meets the constraints. */ bool NeedSynthesis(const AST::CallExpr& ce, const AST::FuncDecl& fd, Ptr generic, const std::vector>& typeArgs) const; std::vector GenerateTypeMappingForCall( ASTContext& ctx, FunctionCandidate& candidate, Ptr retTarget); bool GenerateExtendGenericTypeMapping( const ASTContext& ctx, FunctionCandidate& candidate, MultiTypeSubst& typeMapping); SubstPack GenerateGenericTypeMapping(const ASTContext& ctx, const AST::Expr& expr); /** * Build the generic type mapping table while the baseExpr of other expr is a memberAccess. */ void GenerateTypeMappingForBaseExpr(const AST::Expr& baseExpr, MultiTypeSubst& typeMapping); void GenerateTypeMappingForBaseExpr(const AST::Expr& baseExpr, SubstPack& typeMapping); /** * Build the generic type mapping table by current context type and candidate function's context * when call base is direct function reference call. */ bool GenerateTypeMappingByCallContext( const ASTContext& ctx, const AST::FuncDecl& fd, const AST::CallExpr& ce, MultiTypeSubst& typeMapping); TypeSubst GenerateTypeMappingByTyArgs( const std::vector>& typeArgs, const AST::Generic& generic) const; std::vector GenerateTypeMappingByInference( ASTContext& ctx, const FunctionCandidate& candidate, Ptr retTarget); ErrOrSubst PrepareTyArgsSynthesis(ASTContext& ctx, const FunctionCandidate& candidate, Ptr const retTyUB); std::optional PropagatePlaceholderAndSolve(ASTContext& ctx, AST::CallExpr& ce, const std::vector>& paramTys, const Ptr retTy, Ptr const retTyUB); // static and deterministic version of unify, never result in branched versions of constraint // don't consider error reporting, for now // returns whether the unification is possible // on success, the cst parameter will be updated // on failure, the cst parameter will not change bool Unify(Constraint& cst, AST::Ty& argTy, AST::Ty& paramTy); // static version of solving, only need to handle one possible version of constraint std::optional SolveConstraints(const Constraint& cst); /** * After the function overload resolved, instantiate args and function target type for generic call. * @return empty vector if instantiation fails. */ std::vector> UpdateFuncGenericType( ASTContext& ctx, FunctionMatchingUnit& fmu, AST::CallExpr& ce); void ReplaceIdealTypeInSubstPack(SubstPack& maps); /** * Replace all T according to typeMapping. */ void SpreadInstantiationTy(AST::Node& node, const SubstPack& typeMapping); /** * Resolve the most matching function, and return the function's index in targets. * @param candidates The vector of matched candidates. * @param ce The callExpr need to be resolved. * @return The most matching functions index. */ std::vector ResolveOverload( std::vector>& candidates, const AST::CallExpr& ce); FunctionMatchingUnit* FindFuncWithMaxChildRetTy( const AST::CallExpr& ce, std::vector>& candidates); /** * Check matched results and report associated/reasonable set of diagnoses. * @param ctx current expression's ASTContext/ * @param ce The callExpr need to be resolved. * @param legals The vector of matched candidates. * @param diagnoses The vector of diagnostics of all function candidates and their count of matched arguments. * @return The vector of matched function decls. */ std::vector> CheckMatchResult(ASTContext& ctx, AST::CallExpr& ce, std::vector>& legals, std::vector& illegals); void ReInferCallArgs(ASTContext& ctx, const AST::CallExpr& ce, const FunctionMatchingUnit& legal); void RecoverCallArgs(ASTContext& ctx, const AST::CallExpr& ce, const std::vector>& argsTys); void FillEnumTypeArgumentsTy(const AST::Decl& ctorDecl, const SubstPack& typeMapping, AST::MemberAccess& ma); void FillTypeArgumentsTy(const AST::FuncDecl& fd, const AST::CallExpr& ce, SubstPack& typeMapping); /** * Instantiates the type argument introduced from the outer layer decl. */ void InstantiatePartOfTheGenericParameters(std::vector>& candidates); /** * Compare arguments and parameters. Choose the most consistent one. * Each parameter of the result must be the best match. * @return Returns the comparison result of column i and column j in vectors of argMapping. */ bool CompareFuncCandidates(FunctionMatchingUnit& i, FunctionMatchingUnit& j, const AST::CallExpr& ce); /** * Check non-static calling in static method. * @return If callExpr is in static method and target is non-static, returns false. Else returns true. */ bool CheckStaticCallNonStatic(const ASTContext& ctx, const AST::CallExpr& ce, const AST::FuncDecl& result); Ptr GetDeclOfThisType(const AST::Expr& expr) const; std::optional> DynamicBindingThisType( AST::Expr& baseExpr, const AST::FuncDecl& fd, const SubstPack& typeMapping = {}); bool CheckFuncPtrCall(ASTContext& ctx, Ptr target, AST::CallExpr& ce, AST::FuncTy& funcTy); bool IsGenericCall(const ASTContext& ctx, const AST::CallExpr& ce, const AST::FuncDecl& fd) const; bool CheckArgsWithParamName(const AST::CallExpr& ce, const AST::FuncDecl& fd); bool PostCheckCallExpr(const ASTContext& ctx, AST::CallExpr& ce, AST::FuncDecl& func, const SubstPack& typeMapping); void PostProcessForLSP(AST::CallExpr& ce, const std::vector>& result) const; void CheckUnsafeInvoke(const AST::CallExpr& ce); void CheckToTokensImpCallExpr(const AST::CallExpr& ce); DiagKind GetErrorKindForCall(const std::vector>& candidatesBeforeCheck, const std::vector>& candidatesAfterCheck, const AST::CallExpr& ce) const; void DiagnoseForCall(const std::vector>& candidatesBeforeCheck, const std::vector>& candidatesAfterCheck, AST::CallExpr& ce, const AST::Decl& decl); /** Check Pattern's related APIs. */ bool ChkPattern(ASTContext& ctx, AST::Ty& target, AST::Pattern& p, bool isPatternInMatch = true); bool ChkWildcardPattern(AST::Ty& target, AST::WildcardPattern& p) const; bool ChkConstPattern(ASTContext& ctx, AST::Ty& target, AST::ConstPattern& p); bool ChkOpOverloadForConstPattern(ASTContext& ctx, AST::Ty& target, AST::ConstPattern& p); bool ChkTypePattern(ASTContext& ctx, AST::Ty& target, AST::TypePattern& p); bool ChkVarPattern(const ASTContext& ctx, AST::Ty& target, AST::VarPattern& p); bool ChkTuplePattern(ASTContext& ctx, AST::Ty& target, AST::TuplePattern& p, bool isPatternInMatch = true); bool ChkEnumPattern(ASTContext& ctx, AST::Ty& target, AST::EnumPattern& p); bool ChkVarOrEnumPattern(ASTContext& ctx, AST::Ty& target, AST::VarOrEnumPattern& p); bool ChkExceptTypePattern(ASTContext& ctx, AST::ExceptTypePattern& etp, std::vector>& included); bool ChkHandlePatterns(ASTContext& ctx, AST::Handler& h, std::vector>& included); std::optional> ChkCommandTypePattern( ASTContext& ctx, AST::CommandTypePattern& ctp, std::vector>& included); bool ChkTryWildcardPattern(Ptr target, AST::WildcardPattern& p, std::vector>& included); void FindEnumPatternTarget(ASTContext& ctx, Ptr ed, AST::EnumPattern& ep); std::vector> FindEnumPatternTargets(ASTContext& ctx, Ptr ed, AST::EnumPattern& ep); void UpdateAnyTy(); void UpdateCTypeTy(); bool IsIrrefutablePattern(const AST::Pattern& pattern); Ptr SynForInExpr(ASTContext& ctx, AST::ForInExpr& fie); bool ChkForInExpr(ASTContext& ctx, AST::Ty& target, AST::ForInExpr& fie); Ptr SynSpawnExpr(ASTContext& ctx, AST::SpawnExpr& se); bool ChkSpawnExpr(ASTContext& ctx, AST::Ty& tgtTy, AST::SpawnExpr& se); bool ChkSpawnExprSimple(ASTContext& ctx, AST::Ty& tgtTy, AST::SpawnExpr& se); bool CheckSpawnArgValid(const ASTContext& ctx, const AST::Expr& arg); Ptr SynSyncExpr(ASTContext& ctx, AST::SynchronizedExpr& se); bool ChkSyncExpr(ASTContext& ctx, Ptr tgtTy, AST::SynchronizedExpr& se); /** * Resolve referenced objects. When referencing declaration like var, class, * struct, interface..., the target can be uniquely determined. When * referencing a function, all targets matched by the identifier are stored * in targets. */ void InferRefExpr(ASTContext& ctx, AST::RefExpr& re); void InferCFuncExpr(ASTContext& ctx, AST::RefExpr& re); bool SynTargetOnUsed(ASTContext& ctx, const AST::NameReferenceExpr& nre, AST::Decl& target); void CheckThisOrSuper(const ASTContext& ctx, AST::RefExpr& re); Ptr InferTypeOfThis(AST::RefExpr& re, AST::InheritableDecl& objDecl); Ptr InferTypeOfSuper(AST::RefExpr& re, const AST::InheritableDecl& objDecl); Ptr ReplaceWithGenericTyInInheritableDecl( Ptr ty, const AST::Decl& outerDecl, const AST::InheritableDecl& id); void CheckUsageOfThis(const ASTContext& ctx, const AST::RefExpr& re) const; void CheckUsageOfSuper(const ASTContext& ctx, const AST::RefExpr& re) const; void CheckThisOrSuperInInitializer(const AST::Node& node, const AST::RefExpr& re) const; /** When expression's type argument not empty, check whether argument size is matched. */ bool IsRefTypeArgSizeValid(const AST::Expr& expr, std::vector>& targets); /** Filter targets of reference @p re first and check whether matched number of targets valid. */ bool FilterAndCheckTargetsOfRef(const ASTContext& ctx, AST::RefExpr& re, std::vector>& targets); bool FilterInvalidEnumTargets(const AST::NameReferenceExpr& nre, std::vector>& targets); void DiagMemberAccessNotFound(const AST::MemberAccess& ma); bool FilterAndCheckTargetsOfNameAccess( const ASTContext& ctx, const AST::MemberAccess& ma, std::vector>& targets); Ptr FilterAndGetTargetsOfObjAccess( const ASTContext& ctx, AST::MemberAccess& ma, std::vector>& targets); /** * Filter of reference @p expr when expr's type needs to be inferred. * 1. when expr does not have type argument but target is generic. * 2. found multiple function candidates. * @param expr [in] reference expression which target type is not given. * @param targets [in/out] */ bool FilterTargetsForFuncReference(const AST::Expr& expr, std::vector>& targets); bool IsLegalAccessFromStaticFunc(const ASTContext& ctx, const AST::RefExpr& re, const AST::Decl& decl); /** Filter targets of reference @p re . */ void FilterCandidatesForRef(const ASTContext& ctx, const AST::RefExpr& re, std::vector>& targets); /** Filter valid function targets of reference @p re when @p candidates are all functions. */ void FilterIncompatibleCandidatesForCall(const AST::CallExpr& ce, std::vector>& candidates); /** Instantiate @p re 's sema type with given type arguments. */ void InstantiateReferenceType( const ASTContext& ctx, AST::NameReferenceExpr& expr, const TypeSubst& instantiateMap = {}); void CanTargetOfRefBeCaptured(const ASTContext& ctx, const AST::NameReferenceExpr& nre, const AST::Decl& decl, const AST::FuncBody& curFuncBody) const; void CanTargetOfRefBeCapturedCaseNominalDecl(const ASTContext& ctx, const AST::NameReferenceExpr& nre, const AST::Decl& decl, const AST::FuncBody& curFuncBody) const; void CanTargetOfRefBeCapturedCaseMutFunc(const ASTContext& ctx, const AST::NameReferenceExpr& nre, const AST::Decl& decl, const AST::FuncBody& curFuncBody) const; void SetCaptureKind(const ASTContext& ctx, const AST::NameReferenceExpr& nre, AST::FuncBody& curFuncBody) const; void MarkAndCheckRefExprVarCaptureStatus(const ASTContext& ctx, const AST::NameReferenceExpr& nre) const; void CheckImmutableFuncAccessMutableFunc( const Position& pos, const AST::Node& srcNode, const AST::Decl& destNode, bool isLeftStructValue) const; void CheckForbiddenFuncReferenceAccess(const Position& pos, const AST::FuncDecl& fd, const AST::Decl& decl) const; /** * Check whether given node can match target type. refNode can be RefExpr or MemberAccess. */ bool ChkRefExpr(ASTContext& ctx, AST::Ty& target, AST::NameReferenceExpr& refNode); /** * Report a warning when capture a variable in outer scope, but has a same name decl in inter scope. * example:let x = 4 * func f() { * func f1() { * x // Need report a warning. * } * let x = 2 * } */ void CheckWarningOfCaptureVariable(const ASTContext& ctx, const AST::RefExpr& re) const; /** * MemberAccess's semaType depends on its target. The base can be ClassDecl, * InterfaceDecl, EnumDecl, PackageDecl or class object(including super, * this and builtin data structures object). */ void InferMemberAccess(ASTContext& ctx, AST::MemberAccess& ma); void InferArrayStaticAccess(const ASTContext& ctx, AST::MemberAccess& ma); void InferBuiltInStaticAccess(const ASTContext& ctx, AST::MemberAccess& ma, const AST::BuiltInDecl& bid); void InferInstanceAccess(const ASTContext& ctx, AST::MemberAccess& ma); void InferStaticAccess(const ASTContext& ctx, AST::MemberAccess& ma, AST::Decl& targetOfBaseExpr); void CheckExtendField(const ASTContext& ctx, AST::MemberAccess& ma); /** Filter targets that @p ma 's instantiated types does not satisfied with extend's generic constraints. */ bool FilterTargetsInExtend( const AST::NameReferenceExpr& nre, Ptr baseTy, std::vector>& targets); Ptr GetBaseDeclInMemberAccess(ASTContext& ctx, const AST::MemberAccess& ma); /** Get the target of @p MemberAccess whose baseExpr's ty is @p baseExprTy. */ Ptr GetObjMemberAccessTarget(const ASTContext& ctx, AST::MemberAccess& ma, AST::Ty& baseExprTy); /** Get target function from extend function like 1.add(). */ Ptr GetIdealTypeFuncTargetFromExtend( const ASTContext& ctx, AST::MemberAccess& ma, const AST::Ty& baseExprTy); /** * Get the target of @p MemberAccess whose baseExpr's ty is @p baseExprTy. * For example: func Foo2(a:U) where U <: Bar2 { * var b : U = a.member // Field access by using assumption upper bound. * return b * } * In this case a.member will be the member in Bar2. * */ Ptr GetMemberAccessExposedTarget( const ASTContext& ctx, AST::MemberAccess& ma, const AST::GenericsTy& genericsTy, bool isStaticAccess); /** * Check the targets set obtained from upper bounds because all upperBounds' matched members or methods will * be collected. */ std::vector> GetUpperBoundTargets( const ASTContext& ctx, const AST::MemberAccess& ma, AST::Ty& baseExprTy, const bool isStaticAccess); Ptr CheckUpperBoundTargetsCaseFuncCall(const ASTContext& ctx, AST::MemberAccess& ma, const std::unordered_map, std::vector>>& allTargets); Ptr CheckUpperBoundTargetsCaseOthers(const ASTContext& ctx, AST::MemberAccess& ma, const std::unordered_map, std::vector>>& allTargets); void TryInitializeBaseSum(ASTContext& ctx, AST::MemberAccess& ma); bool FilterSumUpperbound(AST::MemberAccess& ma, AST::GenericsTy& tv, const AST::Decl& d); void FilterSumUpperbound(const ASTContext& ctx, AST::MemberAccess& ma, AST::GenericsTy& tv, std::vector>& targets, const std::unordered_map, std::vector>>& allTargets); /** * Check type legality recursively. */ void CheckReferenceTypeLegality(ASTContext& ctx, AST::Type& t); void CheckRefType(ASTContext& ctx, AST::RefType& rt); void CheckCFuncType(ASTContext& ctx, const AST::RefType& rt); void CheckTupleType(ASTContext& ctx, AST::TupleType& tt); void CheckFuncType(ASTContext& ctx, AST::FuncType& ft); void CheckOptionType(ASTContext& ctx, const AST::OptionType& ot); void CheckVArrayType(ASTContext& ctx, const AST::VArrayType& vt); std::tuple CheckVArrayWithRefType(AST::Ty& ty, std::unordered_set>& traversedTy); void CheckQualifiedType(const ASTContext& ctx, AST::QualifiedType& qt); bool IsGenericTypeWithTypeArgs(AST::Type& type) const; // Returns true if further checks can be omitted. bool CheckRefExprCheckTyArgs(const AST::RefType& rt, const AST::Decl& target); bool CheckRefTypeCheckAccessLegality(const ASTContext& ctx, AST::RefType& rt, const AST::Decl& target); void CheckRefTypeWithRealTarget(AST::RefType& rt); void HandleAliasForRefType(AST::RefType& rt, Ptr& target); void GetRevTypeMapping( std::vector>& params, std::vector>& args, MultiTypeSubst& revTyMap); TypeSubst GetGenericTysToInstTysMapping(AST::Ty& genericTy, AST::Ty& instTy) const; /** * Check TypeAlias entry. */ void CheckTypeAlias(ASTContext& ctx, AST::TypeAliasDecl& tad); void CheckTypeAliasAccess(const AST::TypeAliasDecl& tad); std::vector> GetUnusedTysInTypeAlias(const AST::TypeAliasDecl& tad) const; /** * Get RefType or QualifiedType type arguments application map */ void GetTypeArgsOfType(Ptr type, std::vector>& params); bool ReplaceIdealTy(AST::Node& node); bool ChkLitConstExprRange(AST::LitConstExpr& lce); bool ChkFloatTypeOverflow(const AST::LitConstExpr& lce); bool CheckThisTypeOfFuncBody(const AST::FuncBody& fb) const; bool IsIndexAssignmentOperator(const AST::FuncDecl& fd) const; void CheckFuncDecl(ASTContext& ctx, AST::FuncDecl& fd); /** * Check program entry specifically, whose return type must be `Int64` */ void CheckEntryFunc(AST::FuncDecl& fd); bool CheckNormalFuncBody(ASTContext& ctx, AST::FuncBody& fb, std::vector>& paramTys); bool CheckFuncBody(ASTContext& ctx, AST::FuncBody& fb); void AddRetTypeNode(AST::FuncBody& fb) const; bool CheckBodyRetType(ASTContext& ctx, AST::FuncBody& fb); void CheckFuncParamList(ASTContext& ctx, AST::FuncParamList& fpl); Ptr CalcFuncRetTyFromBody(const AST::FuncBody& fb); void ReplaceFuncRetTyWithThis(AST::FuncBody& fb, Ptr ty); void CheckCtorFuncBody(ASTContext& ctx, AST::FuncBody& fb); bool CheckReturnThisInFuncBody(const AST::FuncBody& fb) const; /** * If a constructor contains super-calling or init-calling in its body, the * the super-calling or init-calling must be in the first line. */ void CheckConstructor(ASTContext& ctx, const AST::Decl& decl, AST::FuncDecl& fd); void CheckConstructorSuper(const AST::CallExpr& ce, const AST::Decl& decl, AST::FuncDecl& fd, bool& needEraseSuper); void CheckCallsInConstructor( ASTContext& ctx, const AST::Decl& decl, AST::FuncDecl& fd, AST::Node& firstExprOrDecl, bool needEraseSuper); void CheckFinalizer(const AST::FuncDecl& fd); void CheckOperatorOverloadFunc(const AST::FuncDecl& fd); void HandIndexOperatorOverload(const AST::FuncDecl& fd, const AST::FuncTy& funcTy); bool NeedCheckBodyReturn(const AST::FuncBody& fb) const; void CheckAnnotations(ASTContext& ctx, AST::Decl& decl); void CheckAnnotationDecl(ASTContext& ctx, AST::Annotation& ann); OwnedPtr CheckCustomAnnotation(ASTContext& ctx, const AST::Decl& decl, AST::Annotation& ann); bool HasModifier(const std::set& modifiers, TokenKind kind) const; void CheckLegalityOfUsage(ASTContext &ctx, AST::Package &pkg); void CheckClosures(const ASTContext &ctx, AST::Node &node) const; // Desugar primary constructor into `init` and fields before cjmp customDef merging void CheckPrimaryCtorBeforeMerge(AST::Node& root); /** * Check whether the members are legal according to the members of * super class and interfaces. For example some functions should be overridden; * some shadowing attempts may lead to error. * @param pkg The package node which contains structure decls. */ void CheckInheritance(AST::Package& pkg); void CheckLegalUseOfClosure(AST::Expr& e, DiagKind kind) const; void CheckLegalUseOfClosure(const ASTContext& ctx, AST::Node& node) const; bool IsCapturedInCFuncLambda(const ASTContext& ctx, const AST::RefExpr& re) const; bool IsCapturedCStructOfClosure(const AST::VarDecl& decl) const; /** * Check non-static member accessed by static variable. */ void CheckStaticVarAccessNonStatic(const AST::VarDecl& vd); /** * Filter out the targets without access rights in the searched targets. * @param curComposite Represents the position of the referrer. * NOTICE: Whether the modifier will be downgraded in spec is not yet determined. */ bool IsLegalAccess(AST::Symbol* curComposite, const AST::Decl& d, const AST::Node& node) const; /** * Check non-function access control of the objective target. The target cannot be a * function here. If is legal, return true, else return false; */ bool CheckNonFuncAccessControl(const ASTContext& ctx, const AST::Expr& e, const AST::Decl& target) const; /** * Check function access control of the objective target such as A().f. */ Ptr CheckFuncAccessControl( const ASTContext& ctx, const AST::Expr& e, const std::vector>& targets) const; /** * Get all accessible decls of expression @p e from given @p targets. */ std::vector> GetAccessibleDecls( const ASTContext& ctx, const AST::Expr& e, const std::vector>& targets) const; /** * Get first accessible decl of expression @p e from given @p targets. */ Ptr GetAccessibleDecl( const ASTContext& ctx, const AST::Expr& e, const std::vector>& targets) const; /** * Check Whether there exists ref or access of member function before * the finishing the initialization of the class */ void CheckMemberAccessInCtorParamOrCtorArg(const ASTContext& ctx, const AST::FuncDecl& fd); void CheckIllegalMemberWalker( const ASTContext& ctx, Ptr node, bool reportThis, const std::string& errorStr); void CheckIllegalMemberHelper( const ASTContext& ctx, bool reportThis, const std::string& errorStr, const AST::NameReferenceExpr& re); /** Checking Initialization APIs. */ void CheckGlobalVarInitialization(ASTContext& ctx, const AST::Package& package); #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND void CheckStaticMembersWithGeneric(const AST::Package& pkg); void CheckStaticMemberWithGeneric(AST::Decl& member, const std::vector>& outerGenericTys); #endif /** * Check if the type nodes in the generic constraint are valid. */ void CheckUpperBound(ASTContext& ctx, const AST::GenericConstraint& genericConstraint); /** * Check the generic constraint: * 1. call 'CheckUpperBound' to check types' legality. * 2. if left value of generic constraints is in the typeParameters. */ void CheckGenericConstraints(ASTContext& ctx, const AST::Generic& generic); /** * Get the type of generic declaration. */ Ptr GetGenericType(AST::Decl& d, const std::vector>& typeArgs); /** * Check generic Expr: check if the expr's typeArguments fulfil requirements of the target function's * typeParameters, then instantiate the typeParameters of target function. */ void CheckGenericExpr(AST::Expr& expr); /** * Check whether the @p typeArgs meet the @p decl's generic constraints. */ bool HasIncompleteStaticFuncOrProp( const ASTContext& ctx, AST::InheritableDecl& cd, std::vector>& staticMemberFuncsOrProps); bool CheckInstTypeCompleteness(const ASTContext& ctx, const AST::NameReferenceExpr& expr); bool CheckInstTyWithUpperbound(const ASTContext& ctx, TypeSubst& typeMapping, const AST::NameReferenceExpr& expr); bool CheckCallGenericDeclInstantiation( Ptr d, const std::vector>& typeArgs, const AST::Expr& checkNode); /** * Check if the typeArguments fulfil requirements of given decl's typeParameters * @param d the decl which defines the 'typeParameters' to be checked. * @param arguments the type arguments used to instantiated given decl. * @param checkNode the ast node which using the instantiate type of given decl. */ bool CheckGenericDeclInstantiation(Ptr d, const std::variant>, std::vector>>& arguments, const AST::Node& checkNode); /** * Check whether the @p pkg exists value type recursive. */ void CheckValueTypeRecursive(const AST::Package& pkg); void CheckValueTypeRecursiveDFS(Ptr root, std::deque> path = {}); void CheckValueTypeRecursiveDFSSwitch(Ptr root, const std::deque>& path); void CheckRecursiveConstructorCall(const std::vector>& decls); /** ======== Generic assumption related functions implemented in src/Sema/Assumption.cpp. ======== */ /** * Main entrance of assumption. Collect type constraints recursively. * @param typeConstraintCollection the type constraints set, which is modified during the assumption period. * @param decl the declaration to be assumption. * @param typeMapping the instantiation substitute map. */ void Assumption(AST::TyVarEnv& typeConstraintCollection, GCBlames& blames, const AST::Decl& decl, const TypeSubst& typeMapping = std::map, Ptr>()); /** * Collect type constraints recursively if the upper bounds of @p gc have generic constraints with * instantiation substitute map @p typeMapping . */ void PerformAssumptionForOneGenericConstraint(AST::TyVarEnv& typeConstraintCollection, GCBlames& blames, const AST::GenericConstraint& gc, const TypeSubst& typeMapping); /** Collect type constraints for an upper bound @p upperBound with instantiation substitute map @p typeMapping . */ void AssumeOneUpperBound(AST::TyVarEnv& typeConstraintCollection, GCBlames& blames, const AST::Type& upperBound, const TypeSubst& typeMapping); /** * Collect type constraints for an reference type upper bound @p referenceTypeUpperBound with instantiation * substitute map @p typeMapping . */ void PerformAssumeReferenceTypeUpperBound(AST::TyVarEnv& typeConstraintCollection, GCBlames& blames, const AST::Type& referenceTypeUpperBound, const TypeSubst& typeMapping); /** * Check that whether a structure declaration inherits a duplicate interface directly. * For example: class C <: I & I {} * @param decl the declaration to be checked. This function is invoked in PreCheck stage. */ void CheckDupInterfaceInStructDecl(AST::InheritableDecl& decl); /** * The main entry function of the check that whether a structure declaration inherits a duplicate interface * instantiated by given type arguments. There is a AST walker inside, the reference node including refExpr, refType * will trigger the check. * */ void CheckInstDupSuperInterfacesEntry(AST::Node& n); void CheckUnusedImportSpec(AST::Package& pkg); AST::VisitAction CheckInstDupSuperInterfaces(const AST::Type& type); AST::VisitAction CheckInstDupSuperInterfaces(const AST::Expr& expr); /** * The main entry function of the check that whether a structure declaration inherits a duplicate interface * instantiated by given type arguments. The @p triggerNode is the reference node which triggers the instantiation * with given type arguments. The @p decl is the declaration to be checked. The @p instantiateMap is the type * mapping built by the given type arguments. */ void CheckInstDupSuperInterfaces(const AST::Node& triggerNode, AST::InheritableDecl& decl, const TypeSubst& instantiateMap, bool checkExtend = true); /** * Check whether the declaration corresponding to the @p ty inherits or implements duplicate interfaces with * given type mapping @p instantiateMap. If yes, return declaration of the interface with 'true'. Otherwise, return * declaration of the 'ty' with `false`. * @param passedClassLikeDecls a set of checked nodes to avoid duplicate check. */ Ptr GetDupSuperInterface(const AST::Node& triggerNode, AST::InheritableDecl& decl, const TypeSubst& instantiateMap, std::unordered_set>& passedClassLikeDecls, bool checkExtend = true); /** * Check whether the @p interfaceTy instantiated by given type mapping @p instantiateMap exists in * the implemented interfaceTy set @p res. If yes, it means duplicate interface are inherited or implemented, * declaration of the interface with 'true' will be returned. * Otherwise, return declaration of the 'ty' with `false`. * @param passedClassLikeDecls a set of checked nodes to avoid duplicate check. */ Ptr GetDupInterfaceRecursively(const AST::Node& triggerNode, AST::Ty& interfaceTy, const TypeSubst& instantiateMap, std::unordered_set>& res, std::unordered_set>& passedClassLikeDecls); /** * Check whether whether all the extend declaration of @p decl inherits or implements duplicate interfaces * with given type mapping @p instantiateMap exists in the implemented interfaceTy set @p res. If yes, it means * duplicate interface are inherited or implemented, declaration of the interface with 'true' will be returned. * Otherwise, return declaration of the 'ty' with `false`. * @param passedClassLikeDecls a set of checked nodes to avoid duplicate check. */ Ptr GetExtendDupSuperInterface(const AST::Node& triggerNode, const AST::InheritableDecl& decl, const TypeSubst& instantiateMap, std::unordered_set>& res, std::unordered_set>& passedClassLikeDecls); void CheckExtendDecl(ASTContext& ctx, AST::ExtendDecl& ed); void SetExtendExternalAttr(const ASTContext& ctx, AST::ExtendDecl& ed); void CheckExtendOrphanRule(const ASTContext& ctx, AST::ExtendDecl& ed); void CheckImmutExtendInhertMutSuper(const AST::Type& inheritedType, const AST::ExtendDecl& ed); /** * Check if a type implements duplicate interfaces, * remove duplicate interfaces and invalid interfaces in extendDecl->interfaces. */ void CheckExtendInterfaces(AST::Ty& ty, const std::set, AST::CmpNodeByPos>& extendDecls); void CheckDefImplWithoutOutsideGeneric(AST::Decl& inhertDecl, const AST::ExtendDecl& extend); void CheckExtendDupDefImplByDiffTypArgs( const std::set, AST::CmpNodeByPos>& extendDecls, const AST::ExtendDecl& extend); void CheckExtendGenerics(const AST::ExtendDecl& ed); void CheckExtendedTypeValidity(const AST::Type& extendedType); bool IsImplementation( AST::Ty& baseTy, AST::InterfaceTy& iTy, const AST::Decl& interfaceMember, const AST::Decl& childMember); bool HasOverrideDefaultImplement( const AST::InheritableDecl& decl, const AST::Decl& defaultImplement, AST::InterfaceTy& superTy); OwnedPtr GetCloneDecl(AST::Decl& decl, AST::InheritableDecl& inheritableDecl, AST::InterfaceTy& superTy); void CloneAndInsert( std::unordered_map, std::unordered_set>>& originFuncToCopyFuncsMap, AST::Decl& decl, AST::InheritableDecl& inheritableDecl, AST::InterfaceTy& superTy); std::unordered_map, std::unordered_set>> CopyDefaultImplement( const AST::Package& pkg); void HandleDefaultImplement(const AST::Package& pkg); Ptr SubstituteTypeAliasInTy( AST::Ty& ty, bool needSubstituteGeneric = false, const TypeSubst& typeMapping = {}); Ptr GetUnaliasedTypeFromTypeAlias( const AST::TypeAliasTy& target, const std::vector>& typeArgs); void SubstituteTypeForTypeAliasTypeMapping( const AST::TypeAliasDecl& tad, const std::vector>& typeArgs, TypeSubst& typeMapping) const; TypeSubst GenerateTypeMappingForTypeAliasDecl(const AST::TypeAliasDecl& tad) const; TypeSubst GenerateTypeMappingForTypeAliasDeclVisit( const AST::TypeAliasDecl& tad, std::unordered_set>& visited) const; // usage could be an AST::Type or AST::Expr, both of which can have written type arguements template TypeSubst GenerateTypeMappingForTypeAliasUse(const AST::TypeAliasDecl& tad, const T& usage) const { TypeSubst typeMapping; if (!tad.type) { return typeMapping; } std::vector> typeArgs; for (auto& it : usage.GetTypeArgs()) { typeArgs.push_back(it->ty); } auto target = tad.type->GetTarget(); if (!target || target->astKind != AST::ASTKind::TYPE_ALIAS_DECL) { return TypeCheckUtil::GenerateTypeMapping(tad, typeArgs); } typeMapping = GenerateTypeMappingForTypeAliasDecl(tad); SubstituteTypeForTypeAliasTypeMapping(tad, typeArgs, typeMapping); return typeMapping; } void SubstituteTypeArguments( const AST::TypeAliasDecl& tad, std::vector>& typeArguments, const TypeSubst& typeMapping); // Declarations of higher accessibility cannot use declarations of lower accessibility. // Check all non-private toplevel declarations in package. void CheckAccessLevelValidity(AST::Package& package); void CheckNominalDeclAccessLevelValidity(const AST::InheritableDecl& id); void CheckFuncAccessLevelValidity(const AST::FuncDecl& fd); void CheckPatternVarAccessLevelValidity(AST::Pattern& pattern); void CheckNonPrivateDeclAccessLevelValidity(AST::Decl& decl); void CheckCHIRClassDependencies(); void MarkOutermostBinaryExpressions(AST::Package& pkg) const; // FFI Sema Prepare Check. void PreCheckAnnoForFFI(AST::Node& root); /** * Set the ABI information to Foreign Modifier. By default Foreign Modifier has Attribute::C. */ void SetForeignABIAttr(AST::Decl& decl); AST::Attribute GetDefaultABI(); // C FFI Sema Prepare Check. void PreCheckAnnoForCFFI(AST::Decl& decl); // C FFI Sema Check. void CheckCFuncParam(const AST::FuncParam& fp); void CheckCFuncParamType(const AST::Type& type); void CheckCFuncReturnType(const AST::Type& type); void UnsafeCheck(const AST::FuncBody& fb); void CheckCTypeMember(const AST::Decl& decl); bool IsInCFunc(const ASTContext& ctx, const AST::RefExpr& re) const; void CheckInvalidRefInCFunc(const ASTContext& ctx, const AST::RefExpr& re) const; void CheckInvalidRefInCFunc(const ASTContext& ctx, const AST::RefExpr& re, const AST::Decl& target) const; void CheckEnumFuncDeclIsCStructParam(const AST::FuncDecl& funcDecl); bool CheckMatchExprNoSelectorExhaustiveness(AST::MatchExpr& me, bool hasDefault); void SetFuncDeclConstructorCall(AST::FuncDecl& fd) const; bool IsFuncTyEnumPatternMatched( ASTContext& ctx, AST::Ty& target, const AST::FuncTy& funcTy, const AST::EnumPattern& p); /** * Post legality checks after sema typecheck finished. */ void CheckAccessLegalityOfRefExpr(const ASTContext& ctx, AST::RefExpr& re); void CheckAccessLegalityOfMemberAccess(const ASTContext& ctx, AST::MemberAccess& ma); void CheckTypeArgLegalityOfJArrayCtor(const AST::NameReferenceExpr& re); void CheckStaticMemberAccessLegality(const AST::MemberAccess& ma, const AST::Decl& target); void CheckInstanceMemberAccessLegality(const ASTContext& ctx, const AST::MemberAccess& ma, const AST::Decl& target); void CheckLegalityOfReference(ASTContext& ctx, AST::Node& node); void CheckLegalityOfUnsafeAndInout(AST::Node& root); bool ShouldSkipDeprecationDiagnostic(const Ptr target, bool strict); void CheckUsageOfDeprecatedWithTarget( const Ptr usage, const Ptr target ); void CheckUsageOfDeprecated(AST::Node& node); void CheckUsageOfDeprecatedParameters(const Ptr usage); void CheckUsageOfDeprecatedNominative( const Ptr usage, const Ptr target ); void CheckOverridingOrRedefiningOfDeprecated( const Ptr overridden, const Ptr overriding, const std::string& declType ); void CheckOverridingOrRedefinitionOfDeprecatedFunction( const Ptr cd, const Ptr member ); void CheckOverridingOrRedefinitionOfDeprecatedProperty( const Ptr cd, const Ptr member ); void CheckOverridingOrRedefinitionOfDeprecated(const Ptr cd); void CheckUsageOfDeprecatedSetter(const Ptr usage); void CheckDeprecationLevelOnInheritors(const Ptr classLike); bool IsDeprecatedStrict(const Ptr decl) const; std::string GetDiagnoseKindOfFuncDecl(const Ptr target) const; std::optional GetDiagnoseKindOfFuncDecl( const Ptr usage, const Ptr target ) const; std::string GetDiagnoseKindOfVarDecl(const Ptr target) const; /** * Return name and/or kind of deprecated declaration. * Return std::nullopt if the declaration deprecation should not be reported. */ std::optional GetKindfOfDeprecatedDeclaration( const Ptr target, const Ptr usage) const; void DiagnoseDeprecatedUsage( const Ptr usage, const Ptr target, const std::string& nameOfDiagnose = "" ); /** * Check memberAccess's targets are legal or not. * @param ma member access node. * @param targetOfBaseDecl The base namespace of memberAccess, can be package name or type name. */ void IsNamespaceMemberAccessLegal( const AST::MemberAccess& ma, AST::Decl& targetOfBaseDecl, const AST::Decl& target); void CheckForbiddenMemberAccess(const ASTContext& ctx, const AST::MemberAccess& ma, const AST::Decl& target) const; void CheckMutationInStruct(const ASTContext& ctx, const AST::Expr& expr) const; void CheckLetInstanceAccessMutableFunc(const ASTContext& ctx, const AST::MemberAccess& ma, const AST::Decl& target); bool CheckIfUseInout(const AST::FuncDecl& decl); void CheckConstEvaluation(AST::Package& pkg); std::optional> SynLiteralInBinaryExprFromRight(ASTContext& ctx, AST::BinaryExpr& be); Ptr SynLiteralInBinaryExprFromLeft(ASTContext& ctx, AST::BinaryExpr& be); void DiagnoseForSubscriptAssignExpr(ASTContext& ctx, const AST::AssignExpr& ae, std::vector& diags); std::optional> InferAssignExprCheckCaseOverloading( ASTContext& ctx, AST::AssignExpr& ae, std::vector& diags); bool PreCheckCompoundAssign( ASTContext& ctx, const AST::AssignExpr& ae, const AST::Ty& lTy, const std::vector& diags); std::optional> InferBinaryExprCaseBuiltIn( ASTContext& ctx, AST::BinaryExpr& be, Ptr& inferRet); std::optional CheckBinaryExprCaseBuiltIn(ASTContext& ctx, AST::BinaryExpr& be, Ptr target); Ptr SynLiteralInBinaryExpr(ASTContext& ctx, AST::BinaryExpr& be); void SynBinaryLeafs(ASTContext& ctx, AST::BinaryExpr& be); void HandleAlias(Ptr expr, std::vector>& targets); std::vector> RecursiveSubstituteTypeAliasInTy( Ptr ty, bool needSubstituteGeneric, const TypeSubst& typeMapping = {}); template void SubstituteTypeArguments(std::vector>& typeArguments, T& type, const TypeSubst& typeMapping) { std::vector> typeArgs; for (auto& it : typeArguments) { typeArgs.push_back(it->ty); } if (!typeArguments.empty()) { Ptr file = typeArguments[0]->curFile; std::move(typeArguments.begin(), typeArguments.end(), std::back_inserter(file->trashBin)); typeArguments.clear(); } for (auto& it : type.typeArguments) { auto newTypeArg = AST::ASTCloner::Clone(it.get()); newTypeArg->ty = newTypeArg->ty ? SubstituteTypeAliasInTy(*newTypeArg->ty, true, typeMapping) : TypeManager::GetInvalidTy(); if (auto ity = DynamicCast(newTypeArg->ty); ity && ity->tys.empty()) { continue; } newTypeArg->EnableAttr(AST::Attribute::COMPILER_ADD); typeArguments.emplace_back(std::move(newTypeArg)); } } /** * Check which functions can be inlined. * @param Packages need to be checked. */ void CheckInlineFunctions(const std::vector>& pkgs) const; void PerformRecursiveTypesElimination(); /** * Check whether static invocations of all interfaces contain unimplemented invocations. * @param node the top level node to be checked. */ void CheckAllInvocationHasImpl(const ASTContext& ctx, AST::Node& node); void CheckSubscriptLegality(AST::Node& node); std::pair> CheckInvokeTargetHasImpl(const ASTContext& ctx, AST::Ty& interfaceTy, AST::Decl& decl, MultiTypeSubst& typeMapping, std::unordered_set>& traversedDecls); Ptr GetImplementedTargetIfExist( const ASTContext& ctx, const AST::Ty& interfaceTy, AST::Decl& target, const MultiTypeSubst& typeMapping); friend class Synthesizer; friend class TypeChecker; friend class EnumSugarChecker; friend class InstCtxScope; bool ChkIfAvailableExpr(ASTContext& ctx, AST::Ty& ty, AST::IfAvailableExpr& ie); Ptr SynIfAvailableExpr(ASTContext& ctx, AST::IfAvailableExpr& iae); /** Members */ Promotion promotion; TypeManager& typeManager; CompilerInstance* const ci{nullptr}; DiagnosticEngine& diag; ImportManager& importManager; ScopeManager scopeManager; std::unordered_map, std::unordered_set>> mainFunctionMap; std::unordered_map, bool> inoutCache; Triple::BackendType backendType; // outermost @Derpecated declaration Ptr deprecatedContext = nullptr; // strict version of outermost @Deprecated declaration Ptr strictDeprecatedContext = nullptr; // cjmp typechecker implementation class class MPTypeCheckerImpl* mpImpl; }; } // namespace Cangjie #endif cangjie_compiler-1.0.7/src/Sema/TypeManager.cpp000066400000000000000000002353641510705540100214200ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements the TypeManager related classes. */ #include "cangjie/Sema/TypeManager.h" #include #include #include #include "TypeCheckUtil.h" #include "LocalTypeArgumentSynthesis.h" #include "ExtraScopes.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/ScopeManagerApi.h" #include "cangjie/AST/Utils.h" #include "cangjie/Utils/CheckUtils.h" namespace Cangjie { using namespace AST; using namespace TypeCheckUtil; TypeManager::TypeManager() { topScope = new TyVarScope(*this); } TypeManager::~TypeManager() { delete topScope; Clear(); } Ptr TypeManager::GetPrimitiveTy(TypeKind kind) { bool validParam = static_cast(kind) >= static_cast(TYPE_PRIMITIVE_MIN) && static_cast(kind) <= static_cast(TYPE_PRIMITIVE_MAX); CJC_ASSERT(validParam); size_t index = validParam ? static_cast(kind) - static_cast(TYPE_PRIMITIVE_MIN) : 0; CJC_ASSERT(index < primitiveTys.size()); return &primitiveTys[index]; } template TypeT* TypeManager::GetTypeTy(Args&&... args) { TypeT tmpTy(std::forward(args)...); auto it = allocatedTys.find(TypePointer(&tmpTy)); if (it == allocatedTys.end()) { auto ty = new TypeT(std::forward(args)...); allocatedTys.insert(TypePointer(ty)); return ty; } else { return RawStaticCast((*it).Get()); } } Ptr TypeManager::GetGenericsTy(GenericParamDecl& gpd) { auto ty = new GenericsTy(gpd.identifier, gpd); auto it = allocatedTys.find(TypePointer(ty)); if (it != allocatedTys.end()) { delete ty; return RawStaticCast((*it).Get()); } allocatedTys.insert(TypePointer(ty)); return ty; } Ptr TypeManager::GetEnumTy(EnumDecl& ed, const std::vector>& typeArgs) { return GetTypeTy(ed.identifier, ed, typeArgs); } Ptr TypeManager::GetRefEnumTy(EnumDecl& ed, const std::vector>& typeArgs) { return GetTypeTy(ed.identifier, ed, typeArgs); } Ptr TypeManager::GetClassTy(ClassDecl& cd, const std::vector>& typeArgs) { return GetTypeTy(cd.identifier, cd, typeArgs); } Ptr TypeManager::GetClassThisTy(ClassDecl& cd, const std::vector>& typeArgs) { return GetTypeTy(cd.identifier, cd, typeArgs); } Ptr TypeManager::GetInterfaceTy(InterfaceDecl& id, const std::vector>& typeArgs) { return GetTypeTy(id.identifier, id, typeArgs); } Ptr TypeManager::GetStructTy(StructDecl& sd, const std::vector>& typeArgs) { return GetTypeTy(sd.identifier, sd, typeArgs); } Ptr TypeManager::GetTypeAliasTy(TypeAliasDecl& tad, const std::vector>& typeArgs) { return GetTypeTy(tad.identifier, tad, typeArgs); } Ptr TypeManager::GetArrayTy(Ptr elemTy, unsigned int dims) { Ptr tmpElemTy = elemTy; unsigned int tmpDims = dims; if (Ty::IsTyCorrect(elemTy)) { if (elemTy->IsArray()) { auto arrayTy = RawStaticCast(elemTy); tmpElemTy = arrayTy->typeArgs[0]; tmpDims += arrayTy->dims; } } else { tmpElemTy = GetInvalidTy(); } return GetTypeTy(tmpElemTy, tmpDims); } Ptr TypeManager::GetVArrayTy(Ty& elemTy, int64_t size) { return GetTypeTy(&elemTy, size); } Ptr TypeManager::GetPointerTy(Ptr elemTy) { Ptr tmpElemTy = elemTy; if (!Ty::IsTyCorrect(elemTy)) { tmpElemTy = GetInvalidTy(); } return GetTypeTy(tmpElemTy); } Ptr TypeManager::GetArrayTy() { // Use 'Array' to present unique array type for extend lookup. return GetArrayTy(GetInvalidTy(), 1); } Ptr TypeManager::GetTupleTy(const std::vector>& typeArgs, bool isClosureTy) { return GetTypeTy(typeArgs, isClosureTy); } Ptr TypeManager::GetFunctionTy(const std::vector>& paramTys, Ptr retTy, FuncTy::Config cfg) { return GetTypeTy(paramTys, retTy, cfg); } Ptr TypeManager::GetIntersectionTy(const std::set>& tys) { return GetTypeTy(tys); } Ptr TypeManager::GetUnionTy(const std::set>& tys) { return GetTypeTy(tys); } Ptr TypeManager::GetBlockRealTy(const Block& block) const { if (block.desugarExpr) { return block.desugarExpr->ty; } if (block.body.empty()) { return GetPrimitiveTy(TypeKind::TYPE_UNIT); } Ptr lastNode = block.body[block.body.size() - 1].get(); if (lastNode->IsDecl()) { return GetPrimitiveTy(TypeKind::TYPE_UNIT); } if (Is(lastNode)) { auto expr = RawStaticCast(lastNode); return expr->desugarExpr.get() ? expr->desugarExpr->ty : expr->ty; } return block.ty; } Ptr TypeManager::TyInstantiator::GetInstantiatedGenericTy(GenericsTy& ty) { Ptr curTy = &ty; auto mapping = typeMapping; TypeSubst::const_iterator it = mapping.find(&ty); // Direct mapping from genericsTy to genericsTy. while (it != mapping.cend()) { curTy = it->second; // Erase current substitution from mapping, avoid circular substitution. // Shouldn't be needed after entirely shifting to SubstPack mapping.erase(it->first); if (auto genTy = DynamicCast(curTy)) { it = mapping.find(genTy); } else { break; } } // Instantiates from composite type which contains genericsTy. bool needInstantiate = !mapping.empty() && !curTy->IsGeneric() && curTy->HasGeneric(); if (needInstantiate) { auto instantiator = TyInstantiator(tyMgr, mapping); curTy = instantiator.Instantiate(curTy); } return curTy; } Ptr TypeManager::TyInstantiator::GetInstantiatedStructTy(StructTy& structTy) { // If is a struct without generic parameter, no need do instantiation. if (!structTy.declPtr || !structTy.declPtr->generic) { return &structTy; } std::vector> typeArgs; // Build type arguments. for (auto& it : structTy.typeArgs) { typeArgs.push_back(Instantiate(it)); } auto recTy = tyMgr.GetStructTy(*structTy.declPtr, typeArgs); return recTy; } Ptr TypeManager::TyInstantiator::GetInstantiatedClassTy(ClassTy& classTy) { if (!classTy.declPtr || !classTy.declPtr->generic) { return &classTy; } std::vector> typeArgs; for (auto& it : classTy.typeArgs) { typeArgs.push_back(Instantiate(it)); } Ptr insTy = nullptr; if (Is(classTy)) { insTy = tyMgr.GetClassThisTy(*classTy.declPtr, typeArgs); } else { insTy = tyMgr.GetClassTy(*classTy.declPtr, typeArgs); } return insTy; } Ptr TypeManager::TyInstantiator::GetInstantiatedInterfaceTy(InterfaceTy& interfaceTy) { if (!interfaceTy.declPtr || !interfaceTy.declPtr->generic) { return &interfaceTy; } std::vector> typeArgs; for (auto& it : interfaceTy.typeArgs) { typeArgs.push_back(Instantiate(it)); } auto insTy = tyMgr.GetInterfaceTy(*interfaceTy.declPtr, typeArgs); return insTy; } Ptr TypeManager::TyInstantiator::GetInstantiatedEnumTy(EnumTy& enumTy) { // If is an enum without generic parameter, no need to do instantiation. if (!enumTy.declPtr || !enumTy.declPtr->generic) { return &enumTy; } std::vector> typeArgs; // Build type arguments. for (auto& it : enumTy.typeArgs) { typeArgs.push_back(Instantiate(it)); } if (Is(enumTy)) { return tyMgr.GetRefEnumTy(*enumTy.declPtr, typeArgs); } auto tmp = tyMgr.GetEnumTy(*enumTy.declPtr, typeArgs); tmp->hasCorrespondRefEnumTy = enumTy.hasCorrespondRefEnumTy; return tmp; } Ptr TypeManager::TyInstantiator::GetInstantiatedArrayTy(ArrayTy& arrayTy) { if (arrayTy.typeArgs.empty()) { return &arrayTy; } auto elemTy = Instantiate(arrayTy.typeArgs[0]); auto dims = arrayTy.dims; return tyMgr.GetArrayTy(elemTy, dims); } Ptr TypeManager::TyInstantiator::GetInstantiatedPointerTy(PointerTy& cptrTy) { if (cptrTy.typeArgs.empty()) { return &cptrTy; } auto elemTy = Instantiate(cptrTy.typeArgs[0]); return tyMgr.GetPointerTy(elemTy); } template Ptr TypeManager::TyInstantiator::GetInstantiatedSetTy(SetTy& ty) { std::set> tys; for (auto it : ty.tys) { tys.emplace(Instantiate(it)); } return tyMgr.GetTypeTy(tys); } Ptr TypeManager::TyInstantiator::Instantiate(Ty& ty) { if (typeMapping.empty()) { return &ty; } switch (ty.kind) { // If is generic type, should replace it with type mapping. case TypeKind::TYPE_GENERICS: return GetInstantiatedGenericTy(StaticCast(ty)); case TypeKind::TYPE_FUNC: { std::vector> paramTys; auto& funcTy = static_cast(ty); for (auto& it : funcTy.paramTys) { paramTys.push_back(Instantiate(it)); } auto retType = Instantiate(funcTy.retTy); Ptr ret = tyMgr.GetFunctionTy( paramTys, retType, {funcTy.IsCFunc(), funcTy.isClosureTy, funcTy.hasVariableLenArg}); return ret; } case TypeKind::TYPE_TUPLE: { std::vector> typeArgs; std::transform(ty.typeArgs.begin(), ty.typeArgs.end(), std::back_inserter(typeArgs), [this](auto it) { return Instantiate(it); }); return tyMgr.GetTupleTy(typeArgs, static_cast(ty).isClosureTy); } case TypeKind::TYPE_ARRAY: return GetInstantiatedArrayTy(static_cast(ty)); case TypeKind::TYPE_POINTER: return GetInstantiatedPointerTy(static_cast(ty)); case TypeKind::TYPE_STRUCT: return GetInstantiatedStructTy(static_cast(ty)); case TypeKind::TYPE_CLASS: return GetInstantiatedClassTy(static_cast(ty)); case TypeKind::TYPE_INTERFACE: return GetInstantiatedInterfaceTy(static_cast(ty)); case TypeKind::TYPE_ENUM: return GetInstantiatedEnumTy(static_cast(ty)); case TypeKind::TYPE: { std::vector> typeArgs; for (auto& it : ty.typeArgs) { typeArgs.push_back(Instantiate(it)); } return tyMgr.GetTypeAliasTy(*static_cast(ty).declPtr, typeArgs); } case TypeKind::TYPE_INTERSECTION: return GetInstantiatedSetTy(static_cast(ty)); case TypeKind::TYPE_UNION: return GetInstantiatedSetTy(static_cast(ty)); default:; } return &ty; } // Temporarily for backward compatibility. Ptr TypeManager::GetBestInstantiatedTy(Ptr ty, const MultiTypeSubst& mts) { if (mts.empty()) { return ty; } if (!Ty::IsTyCorrect(ty)) { return TypeManager::GetInvalidTy(); } auto instTys = GetInstantiatedTys(ty, mts); return instTys.empty() ? ty : *instTys.begin(); } std::set> TypeManager::GetInstantiatedTys(Ptr ty, const MultiTypeSubst& mts) { if (mts.empty()) { return {ty}; } std::set ms = ExpandMultiTypeSubst(ReduceMultiTypeSubst(*this, StaticToTyVars(GetAllGenericTys(ty)), mts), {ty}); std::set> res; std::for_each(ms.cbegin(), ms.cend(), [this, &ty, &res](const TypeSubst& m) { if (auto instTy = GetInstantiatedTy(ty, m)) { res.insert(instTy); } }); if (res.size() > 1) { res.erase(ty); // Ignore self ty. } return res; } Ptr TypeManager::GetInstantiatedTy(Ptr ty, const TypeSubst& typeMapping) { auto instantiator = TyInstantiator(*this, typeMapping); return instantiator.Instantiate(ty); } std::set> TypeManager::ApplyTypeSubstForTys(const TypeSubst& subst, const std::set>& tys) { std::set> res; for (auto& i : tys) { res.insert(GetInstantiatedTy(i, subst)); } return res; } void TypeManager::GenerateExtendGenericMappingVisit( MultiTypeSubst& typeMapping, AST::Ty& baseType, std::unordered_set>& visited) { if (baseType.IsInvalid()) { return; } std::set> extends = GetAllExtendsByTy(baseType); for (auto& extend : extends) { GenerateStructDeclGenericMappingVisit(typeMapping, *extend, baseType, visited); } } namespace { bool IsInheritableType(Ptr ty) { // Non-classlike type cannot be treated as inherited type. if (!Ty::IsTyCorrect(ty) || !ty->IsClassLike()) { return false; } auto inherit = Ty::GetDeclPtrOfTy(ty); return inherit && !inherit->TestAttr(Attribute::IN_REFERENCE_CYCLE); } } // namespace void TypeManager::PackMapping(SubstPack& maps, const MultiTypeSubst m) { for (auto [tv, instTys] : m) { if (tv->isPlaceholder) { instTys.erase(tv); maps.inst[tv].merge(instTys); } else { if (maps.u2i.count(tv) == 0) { maps.u2i[tv] = AllocTyVar(tv->name); } if (!instTys.empty()) { auto instTv = StaticCast(maps.u2i[tv]); maps.inst[instTv].merge(instTys); } } } } void TypeManager::PackMapping(SubstPack& maps, const TypeSubst m) { for (auto [tv, instTy] : m) { if (tv->isPlaceholder) { if (tv != instTy) { maps.inst[tv].insert(instTy); } } else { if (maps.u2i.count(tv) == 0) { maps.u2i[tv] = AllocTyVar(tv->name); } auto instTv = StaticCast(maps.u2i[tv]); maps.inst[instTv].insert(instTy); } } } void TypeManager::PackMapping(SubstPack& maps, GenericsTy& tv, Ty& instTy) { if (tv.isPlaceholder) { if (&tv != &instTy) { maps.inst[&tv].emplace(&instTy); } } else { if (maps.u2i.count(&tv) == 0) { maps.u2i[&tv] = AllocTyVar(tv.name); } auto instTv = StaticCast(maps.u2i[&tv]); maps.inst[instTv].emplace(&instTy); } } MultiTypeSubst TypeManager::ZipSubstPack(const SubstPack& mapping) { MultiTypeSubst mts; for (auto& [tvu, tyi] : mapping.u2i) { auto tvi = StaticCast(tyi); if (mapping.inst.count(tvi) > 0) { mts[tvu] = ApplySubstPackNonUniq(tvu, mapping); } } return mts; } void TypeManager::MakeInstTyVar(SubstPack& maps, const AST::Decl& d) { if (d.generic) { for (auto& genParam : d.generic->typeParameters) { if (Ty::IsTyCorrect(genParam->ty)) { MakeInstTyVar(maps, *StaticCast(genParam->ty)); } } } } void TypeManager::MakeInstTyVar(SubstPack& maps, AST::GenericsTy& uTv) { CJC_ASSERT(!uTv.isPlaceholder); if (maps.u2i.count(&uTv) == 0) { maps.u2i[&uTv] = AllocTyVar(uTv.name); } } void TypeManager::GenerateStructDeclGenericMapping(MultiTypeSubst& m, const InheritableDecl& decl, const Ty& targetTy) { std::unordered_set> visited; GenerateStructDeclGenericMappingVisit(m, decl, targetTy, visited); } void TypeManager::GenerateStructDeclGenericMappingVisit(MultiTypeSubst& m, const AST::InheritableDecl& decl, const AST::Ty& targetTy, std::unordered_set>& visited) { if (targetTy.IsInvalid()) { return; } MergeTypeSubstToMultiTypeSubst(m, GenerateTypeMapping(decl, targetTy.typeArgs)); for (auto& inheritedType : decl.inheritedTypes) { if (IsInheritableType(inheritedType->ty)) { GenerateGenericMappingVisit(m, *inheritedType->ty, visited); } } } void TypeManager::GenerateTypeMappingForUpperBounds(SubstPack& m, const MemberAccess& ma, Decl& target) { auto found = ma.foundUpperBoundMap.find(&target); if (found == ma.foundUpperBoundMap.end()) { return; } for (auto upper : found->second) { if (upper) { GenerateGenericMapping(m, *upper); } } } void TypeManager::GenerateGenericMapping(SubstPack& m, Ty& baseType) { std::unordered_set> visited; GenerateGenericMappingVisit(m, baseType, visited, true); } void TypeManager::GenerateGenericMappingVisit( SubstPack& m, AST::Ty& baseType, std::unordered_set>& visited, bool contextual) { if (baseType.IsInvalid() || visited.count(&baseType) > 0) { return; } // avoid repeatedly visiting same super-type when there are multiple paths to it visited.emplace(&baseType); GenerateExtendGenericMappingVisit(m, baseType, visited, contextual); if (auto inheritableDecl = Ty::GetDeclPtrOfTy(&baseType)) { GenerateStructDeclGenericMappingVisit(m, *inheritableDecl, baseType, visited, contextual); } } void TypeManager::GenerateExtendGenericMappingVisit( SubstPack& typeMapping, AST::Ty& baseType, std::unordered_set>& visited, bool contextual) { if (baseType.IsInvalid()) { return; } std::set> extends = GetAllExtendsByTy(baseType); for (auto& extend : extends) { GenerateStructDeclGenericMappingVisit(typeMapping, *extend, baseType, visited, contextual); } } void TypeManager::GenerateStructDeclGenericMappingVisit(SubstPack& m, const AST::InheritableDecl& decl, const AST::Ty& targetTy, std::unordered_set>& visited, bool contextual) { if (targetTy.IsInvalid()) { return; } if (contextual) { GenerateTypeMapping(*this, m, decl, targetTy.typeArgs); } else { auto typeArgs = targetTy.typeArgs; for (auto& ty : typeArgs) { ty = GetInstantiatedTy(ty, m.u2i); // use placeholder ty var for mid-level targetTys } GenerateTypeMapping(*this, m, decl, typeArgs); } for (auto& inheritedType : decl.inheritedTypes) { if (IsInheritableType(inheritedType->ty)) { GenerateGenericMappingVisit(m, *inheritedType->ty, visited, false); } } } void TypeManager::GenerateTypeMappingForUpperBounds(MultiTypeSubst& m, const MemberAccess& ma, Decl& target) { auto found = ma.foundUpperBoundMap.find(&target); if (found == ma.foundUpperBoundMap.end()) { return; } for (auto upper : found->second) { if (upper) { GenerateGenericMapping(m, *upper); } } } void TypeManager::GenerateGenericMapping(MultiTypeSubst& m, Ty& baseType) { std::unordered_set> visited; GenerateGenericMappingVisit(m, baseType, visited); } void TypeManager::GenerateGenericMappingVisit( MultiTypeSubst& m, AST::Ty& baseType, std::unordered_set>& visited) { if (baseType.IsInvalid() || visited.count(&baseType) > 0) { return; } // avoid repeatedly visiting same super-type when there are multiple paths to it visited.emplace(&baseType); GenerateExtendGenericMappingVisit(m, baseType, visited); if (auto inheritableDecl = Ty::GetDeclPtrOfTy(&baseType)) { GenerateStructDeclGenericMappingVisit(m, *inheritableDecl, baseType, visited); } } TypeSubst TypeManager::GenerateGenericMappingFromGeneric(const Decl& parentDecl, const Decl& childDecl) const { TypeSubst typeMapping; Ptr parentGeneric = parentDecl.GetGeneric(); Ptr childGeneric = childDecl.GetGeneric(); bool validGenerics = parentGeneric != nullptr && childGeneric != nullptr && parentGeneric->typeParameters.size() == childGeneric->typeParameters.size(); if (validGenerics) { for (size_t i = 0; i < parentGeneric->typeParameters.size(); ++i) { typeMapping[StaticCast(parentGeneric->typeParameters[i]->ty)] = childGeneric->typeParameters[i]->ty; } } return typeMapping; } MultiTypeSubst TypeManager::GenerateStructDeclTypeMapping(const Decl& decl) { if (!decl.IsNominalDecl()) { return {}; } auto parentTy = decl.ty; if (decl.astKind == ASTKind::EXTEND_DECL) { auto& ed = static_cast(decl); CJC_ASSERT(ed.extendedType); parentTy = ed.extendedType->ty; } if (!parentTy) { return {}; } MultiTypeSubst typeMapping; GenerateGenericMapping(typeMapping, *parentTy); return typeMapping; } bool TypeManager::IsCoreFutureType(const Ty& ty) { if (!ty.IsClass()) { return false; } auto declPtr = Ty::GetDeclPtrOfTy(&ty); return declPtr && declPtr->identifier == FUTURE_TYPE_NAME && declPtr->curFile->curPackage->fullPackageName == CORE_PACKAGE_NAME && ty.typeArgs.size() == 1; } bool TypeManager::IsPlaceholderSubtype(Ty& leaf, Ty& root) { if (leaf.IsPlaceholder() || root.IsPlaceholder()) { return LocalTypeArgumentSynthesis::Unify(*this, constraints, leaf, root); } return false; } bool TypeManager::IsGenericSubtype(Ty& leaf, Ty& root, bool implicitBoxed, bool allowOptionBox) { if (leaf.IsGeneric()) { auto& gTy = static_cast(leaf); if (root.IsAny() && !implicitBoxed) { // If implicit box is not allowed and the target type is 'Any', // only return true when there exists any class type upper found. return std::any_of(gTy.upperBounds.begin(), gTy.upperBounds.end(), [](auto it) { return it->IsClass(); }); } // NOTE: transmitting upperBound of generic types are flattened in 'PreCheck' step. // If the constraint can be found in typeConstraintCollection, return true. if (gTy.upperBounds.find(&root) != gTy.upperBounds.end()) { return true; } if (!gTy.isUpperBoundLegal) { return false; } if (gTy.isAliasParam) { return true; } for (auto& upperbound : gTy.upperBounds) { if (IsSubtype(upperbound, &root, implicitBoxed, allowOptionBox)) { return true; } } } if (leaf.kind == TypeKind::TYPE_INTERSECTION) { auto& iSectTy = static_cast(leaf); for (auto& ty : iSectTy.tys) { if (IsSubtype(ty, &root, implicitBoxed, allowOptionBox)) { return true; } } } if (root.kind == TypeKind::TYPE_INTERSECTION) { auto& iSectTy = static_cast(root); auto success = true; for (auto& ty : iSectTy.tys) { success = success && IsSubtype(&leaf, ty, implicitBoxed, allowOptionBox); } return success; } if (leaf.kind == TypeKind::TYPE_UNION) { auto& unionTy = static_cast(leaf); auto success = true; for (auto& ty : unionTy.tys) { success = success && IsSubtype(ty, &root, implicitBoxed, allowOptionBox); } return success; } return false; } // Treating platform and common classes/struct/enums types as subtypes of each other bool IsCommonAndPlatformRelation(const Ty& leafTy, const Ty& rootTy) { if (!leafTy.IsNominal() || !rootTy.IsNominal()) { return false; } auto leafDecl = Ty::GetDeclOfTy(&leafTy); auto rootDecl = Ty::GetDeclOfTy(&rootTy); if (leafDecl && rootDecl) { leafDecl = leafDecl->platformImplementation == nullptr ? leafDecl : leafDecl->platformImplementation; rootDecl = rootDecl->platformImplementation == nullptr ? rootDecl : rootDecl->platformImplementation; if (!leafDecl->TestAttr(Attribute::PLATFORM) || !rootDecl->TestAttr(Attribute::PLATFORM)) { return false; } return leafDecl == rootDecl; } return false; } bool TypeManager::IsClassLikeSubtype(Ty& leaf, Ty& root, bool implicitBoxed, bool allowOptionBox) { if (auto thisTyOfLeaf = DynamicCast(&leaf); thisTyOfLeaf && thisTyOfLeaf->declPtr) { auto leafClassTy = GetClassTy(*thisTyOfLeaf->declPtr, thisTyOfLeaf->typeArgs); if (auto thisTyOfRoot = DynamicCast(&root); thisTyOfRoot && thisTyOfRoot->declPtr) { // 'root' is class type, must not exist boxing relation. return IsSubtype(leafClassTy, GetClassTy(*thisTyOfRoot->declPtr, thisTyOfRoot->typeArgs)); } return IsSubtype(leafClassTy, &root, implicitBoxed, allowOptionBox); } if (leaf.IsClassLike() && root.IsClassLike()) { // Types are class like, only may existing extend boxing relation. #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND auto superTys = GetAllSuperTys(leaf, {}); #endif auto cs = PData::CommitScope(constraints); for (auto ty : superTys) { if (ty && IsClassTyEqual(*ty, root)) { return true; } PData::Reset(constraints); } } return false; } bool TypeManager::IsPlaceholderEqual(Ty& leaf, Ty& root) { if (leaf.IsPlaceholder() || root.IsPlaceholder()) { return IsTyEqual(&leaf, &root); } return false; } bool TypeManager::IsClassTyEqual(Ty& leaf, Ty& root) { if (&leaf == &root || IsCommonAndPlatformRelation(leaf, root)) { return true; } if (auto ctt = DynamicCast(&leaf); ctt) { return false; } if (auto ctt = DynamicCast(&root); ctt) { return false; } size_t index = 0; auto predFunc = [&index, &root, this](Ptr ty) { if (root.typeArgs[index]->IsIntersection()) { if (IsSubtype(ty, root.typeArgs[index])) { // This is for partial generic type-alias case. root.typeArgs[index] = ty; ++index; return true; } } else { auto rootArg = root.typeArgs[index]; if (rootArg == ty || IsPlaceholderEqual(*rootArg, *ty)) { ++index; return true; } } return false; }; // Need add test case to verify this logic. return static_cast(leaf).commonDecl->ty == static_cast(root).commonDecl->ty && (leaf.typeArgs.size() == root.typeArgs.size()) && !leaf.IsClass() && std::all_of(leaf.typeArgs.begin(), leaf.typeArgs.end(), predFunc); } bool TypeManager::IsStructOrEnumSubtype(Ty& leaf, Ty& root, bool implicitBoxed, bool allowOptionBox) { auto b1 = Is(leaf) && Is(root); auto b2 = Is(root) && Is(leaf); auto isEnumCompatible = [this, &implicitBoxed](const EnumTy& p, const EnumTy& q) { if (p.typeArgs.size() != q.typeArgs.size() || p.name != q.name || p.declPtr != q.declPtr) { return false; } bool flag = true; for (size_t idx = 0; idx < p.typeArgs.size(); ++idx) { if (CheckTypeCompatibility(p.typeArgs[idx], q.typeArgs[idx], implicitBoxed, p.typeArgs[idx]->IsGeneric()) == TypeCompatibility::INCOMPATIBLE) { flag = false; break; } } return flag; }; if ((b1 || b2) && isEnumCompatible(static_cast(leaf), static_cast(root))) { return true; } if (implicitBoxed) { if (leaf.IsStruct() && root.kind == TypeKind::TYPE_INTERFACE) { if (HasSuperTy(leaf, root, {}, implicitBoxed)) { return true; } } if (root.IsCoreOptionType() && allowOptionBox && CountOptionNestedLevel(leaf) < CountOptionNestedLevel(root)) { // Core's enum Option check, support for auto package Option. return IsSubtype(&leaf, root.typeArgs[0], implicitBoxed); } if (leaf.IsEnum() && root.kind == TypeKind::TYPE_INTERFACE) { if (HasSuperTy(leaf, root, {}, implicitBoxed)) { return true; } } } return false; } bool TypeManager::IsFuncSubtype(const Ty& leaf, const Ty& root) { if (!leaf.IsFunc() || !root.IsFunc()) { return false; } auto& leafFuncType = static_cast(leaf); auto& rootFuncType = static_cast(root); if (IsFuncParametersSubtype(leafFuncType, rootFuncType)) { bool noCast = leafFuncType.noCast || rootFuncType.noCast; return IsSubtype(leafFuncType.retTy, rootFuncType.retTy, noCast); } return false; } bool TypeManager::IsFuncParametersSubtype(const FuncTy& leaf, const FuncTy& root) { bool noCast = leaf.noCast || root.noCast; if (leaf.paramTys.size() == root.paramTys.size()) { bool result = true; for (size_t i = 0; i < leaf.paramTys.size(); i++) { result = result && IsSubtype(root.paramTys[i], leaf.paramTys[i], noCast); if (!result) { return false; } } result = result && leaf.isC == root.isC; result = result && leaf.hasVariableLenArg == root.hasVariableLenArg; return result; } return false; } bool TypeManager::IsTupleSubtype(const Ty& leaf, const Ty& root) { if (leaf.IsTuple() && root.IsTuple() && leaf.typeArgs.size() == root.typeArgs.size()) { for (size_t i = 0; i < leaf.typeArgs.size(); i++) { if (!IsSubtype(leaf.typeArgs[i], root.typeArgs[i], false)) { return false; } } return true; } return false; } bool TypeManager::IsArraySubtype(const Ty& leaf, const Ty& root) { if (!leaf.IsArray() || !root.IsArray()) { return false; } auto& leafArrayType = static_cast(leaf); auto& rootArrayType = static_cast(root); if (leafArrayType.dims == rootArrayType.dims && !leafArrayType.typeArgs.empty() && !rootArrayType.typeArgs.empty()) { auto leafArg = leafArrayType.typeArgs[0]; auto rootArg = rootArrayType.typeArgs[0]; return IsTyEqual(leafArg, rootArg); } return false; } bool TypeManager::IsVArraySubtype(const Ty& leaf, const Ty& root) { if (!Is(leaf) || !Is(root)) { return false; } auto& leafVArrayType = static_cast(leaf); auto& rootVArrayType = static_cast(root); if (leafVArrayType.size == rootVArrayType.size && !leafVArrayType.typeArgs.empty() && !rootVArrayType.typeArgs.empty()) { auto leafArg = leafVArrayType.typeArgs[0]; auto rootArg = rootVArrayType.typeArgs[0]; return IsTyEqual(leafArg, rootArg); } return false; } bool TypeManager::IsPointerSubtype(const Ty& leaf, const Ty& root) { if (!leaf.IsPointer() || !root.IsPointer()) { return false; } auto& leafPtrTy = static_cast(leaf); auto& rootPtrTy = static_cast(root); if (leafPtrTy.typeArgs.empty() || rootPtrTy.typeArgs.empty()) { return false; } auto leafArg = leafPtrTy.typeArgs[0]; auto rootArg = rootPtrTy.typeArgs[0]; return IsTyEqual(leafArg, rootArg); } bool TypeManager::IsPrimitiveSubtype(const Ty& leaf, Ty& root) { if (leaf.kind == TypeKind::TYPE_IDEAL_INT && root.IsInteger()) { return true; } if (leaf.kind == TypeKind::TYPE_IDEAL_FLOAT && root.IsFloating()) { return true; } if (leaf.kind == TypeKind::TYPE_IDEAL_INT) { if (IsSubtype(GetPrimitiveTy(TypeKind::TYPE_INT64), &root) || IsSubtype(GetPrimitiveTy(TypeKind::TYPE_UINT64), &root) || IsSubtype(GetPrimitiveTy(TypeKind::TYPE_INT32), &root) || IsSubtype(GetPrimitiveTy(TypeKind::TYPE_UINT32), &root) || IsSubtype(GetPrimitiveTy(TypeKind::TYPE_INT16), &root) || IsSubtype(GetPrimitiveTy(TypeKind::TYPE_UINT16), &root) || IsSubtype(GetPrimitiveTy(TypeKind::TYPE_INT8), &root) || IsSubtype(GetPrimitiveTy(TypeKind::TYPE_UINT8), &root)) { return true; } } if (leaf.kind == TypeKind::TYPE_IDEAL_FLOAT) { if (IsSubtype(GetPrimitiveTy(TypeKind::TYPE_FLOAT64), &root) || IsSubtype(GetPrimitiveTy(TypeKind::TYPE_FLOAT32), &root) || IsSubtype(GetPrimitiveTy(TypeKind::TYPE_FLOAT16), &root)) { return true; } } // Since the 'PrimitiveTy' can be implicited copied, also check for equality of primitive types' kind. return leaf.IsPrimitive() && root.IsPrimitive() && leaf.kind == root.kind; } // Note: By default, @implicitBoxed is true. For function type and tuple type, // covariant & contravariant are both not allowed for elements' value types (implementing interfaces) // and class type (when implementing interfaces by extend). For this situation, implicitBoxed is false. bool TypeManager::IsSubtype(Ptr leaf, Ptr root, bool implicitBoxed, bool allowOptionBox) { if (!Ty::IsTyCorrect(leaf) || !Ty::IsTyCorrect(root)) { return false; } if (leaf->IsQuest() || root->IsQuest()) { return true; } // Return true if any of the following holds: // 1. types are exactly same // 2. the 'leaf' is the 'Nothing' type // 3. currently allowing implicit boxing and // a) the 'root' type is the 'Any' type, OR // b) the 'leaf' is one of cffi types and the 'root' is 'CType', OR // 4. currently disallowing implicit boxing but the 'leaf' is classLike type and the 'root' is the 'Any' type. // NOTE: all cffi types are not classLike type, so using conditions as below. bool ffiFastCheck = (Ty::IsMetCType(*leaf) && root->IsCType()); bool fastCheck = ffiFastCheck || leaf == root || (leaf->IsNothing() && !root->IsPlaceholder()) || ((implicitBoxed || leaf->IsClassLike()) && root->IsAny() && !leaf->IsPlaceholder()); if (fastCheck) { return true; } if (root->IsNothing()) { return false; } // Note: this cache is NOT for speedup, but to avoid recursive judgement on same types SubtypeCacheKey cacheKey(leaf, root, implicitBoxed, allowOptionBox); auto cacheResult = subtypeCache.find(cacheKey); if (cacheResult != subtypeCache.cend()) { return cacheResult->second; } auto cacheEntry = subtypeCache.emplace(std::make_pair(cacheKey, false)).first; if (IsCommonAndPlatformRelation(*leaf, *root)) { cacheEntry->second = true; } else if (IsPlaceholderSubtype(*leaf, *root)) { cacheEntry->second = true; } else if (IsGenericSubtype(*leaf, *root, implicitBoxed, allowOptionBox)) { cacheEntry->second = true; } else if (IsClassLikeSubtype(*leaf, *root, implicitBoxed, allowOptionBox)) { cacheEntry->second = true; } else if (IsPointerSubtype(*leaf, *root)) { cacheEntry->second = true; } else if (IsStructOrEnumSubtype(*leaf, *root, implicitBoxed, allowOptionBox)) { cacheEntry->second = true; } else if (IsFuncSubtype(*leaf, *root)) { cacheEntry->second = true; } else if (IsTupleSubtype(*leaf, *root)) { cacheEntry->second = true; } else if (IsArraySubtype(*leaf, *root)) { cacheEntry->second = true; } else if (IsVArraySubtype(*leaf, *root)) { cacheEntry->second = true; } else if (IsPrimitiveSubtype(*leaf, *root)) { cacheEntry->second = true; } else if (implicitBoxed && !leaf->IsGeneric() && root->IsInterface()) { // The 'GenericTy' will never have extends. // Process extends. auto extendTys = GetAllExtendInterfaceTy(*leaf); if (std::find(extendTys.begin(), extendTys.end(), root) != extendTys.end()) { cacheEntry->second = true; } else if ((leaf->HasPlaceholder() || root->HasPlaceholder()) && LocalTypeArgumentSynthesis::Unify(*this, constraints, *leaf, *root)) { cacheEntry->second = true; } } else { cacheEntry->second = false; } bool ret = cacheEntry->second; // result for placeholder depends on global state, therefore shouldn't be cached beyond one judgement if (leaf->HasPlaceholder() || root->HasPlaceholder()) { subtypeCache.erase(cacheKey); } return ret; } bool TypeManager::IsTyEqual(Ptr subTy, Ptr baseTy) { PData::CommitScope cs(constraints); if (IsSubtype(subTy, baseTy, false, false) && IsSubtype(baseTy, subTy, false, false)) { return true; } PData::Reset(constraints); return false; } bool TypeManager::IsLitBoxableType(Ptr leaf, Ptr root) { if (!Ty::IsTyCorrect(leaf) || !Ty::IsTyCorrect(root)) { return false; } // LitConst leaf type will be int64 for integer and float 64 for float. // Automatically Adapt to all numeric literal for Option here. if (leaf == root || (root->IsInteger() && leaf->IsInteger()) || (root->IsFloating() && leaf->IsFloating())) { return true; } // Check whether a type with a literal value (leaf) is a subtype of another type (root). // e.g. For string literals, whether String is a subtype of an interface that String implements or extends. if (IsSubtype(leaf, root)) { return true; } if (root->IsCoreOptionType()) { // Core's enum Option check for litconst types. return IsLitBoxableType(leaf, root->typeArgs[0]); } return false; } bool TypeManager::IsFuncParameterTypesIdentical(const FuncTy& t1, const FuncTy& t2) { bool result{false}; if (Ty::IsTyCorrect(&t1) && Ty::IsTyCorrect(&t2) && t1.paramTys.size() == t2.paramTys.size()) { result = true; for (size_t i = 0; i < t2.paramTys.size(); i++) { result = result && CheckTypeCompatibility(t1.paramTys[i], t2.paramTys[i], false) == TypeCompatibility::IDENTICAL; } } return result; } bool TypeManager::IsFuncParameterTypesIdentical( const std::vector>& paramTys1, const std::vector>& paramTys2, const TypeSubst& typeMapping) { bool result{false}; if (paramTys1.size() == paramTys2.size()) { result = true; for (size_t i = 0; i < paramTys2.size(); i++) { auto paramTy1 = GetInstantiatedTy(paramTys1[i], typeMapping); result = result && CheckTypeCompatibility(paramTy1, paramTys2[i], false) == TypeCompatibility::IDENTICAL; } } return result; } bool TypeManager::CheckGenericType(Ptr lvalue, Ptr rvalue, bool implicitBoxed) { if (Ty::AreTysCorrect(std::set{lvalue, rvalue}) && lvalue->kind == TypeKind::TYPE_GENERICS && rvalue->kind == TypeKind::TYPE_GENERICS) { auto lg = RawStaticCast(lvalue); auto rg = RawStaticCast(rvalue); if (lg->isPlaceholder || rg->isPlaceholder) { return false; } if (lg->upperBounds.empty() && rg->upperBounds.empty()) { return true; } if (lg->upperBounds.size() != rg->upperBounds.size()) { return false; } auto itl = lg->upperBounds.begin(); auto itr = rg->upperBounds.begin(); bool result = true; for (; itl != lg->upperBounds.end(); ++itl, ++itr) { result = result && CheckTypeCompatibility(*itl, *itr, implicitBoxed) == TypeCompatibility::IDENTICAL; } return result; } return IsSubtype(lvalue, rvalue, implicitBoxed, false); } // Check type compatibility between two types. // For type A and type B, // return TypeCompatibility::IDENTICAL if A and B are identical. // return TypeCompatibility::SUBTYPE if A is subtype of B. // return TypeCompatibility::INCOMPATIBLE for other situations. // Note: By default, @implicitBoxed is true. For function type and tuple type, // covariance is not allowed for value types (implementing interfaces) // and class type (when implementing interfaces by extend). For this situation, implicitBoxed is false. TypeCompatibility TypeManager::CheckTypeCompatibility( Ptr lvalue, Ptr rvalue, bool implicitBoxed, bool isGeneric) { if (!Ty::AreTysCorrect(std::set{lvalue, rvalue})) { return TypeCompatibility::INCOMPATIBLE; } if (lvalue == rvalue || IsCommonAndPlatformRelation(*lvalue, *rvalue)) { return TypeCompatibility::IDENTICAL; } if (IsSubtype(lvalue, rvalue, implicitBoxed)) { return TypeCompatibility::SUBTYPE; } if (isGeneric && CheckGenericType(lvalue, rvalue, implicitBoxed)) { return TypeCompatibility::IDENTICAL; } auto isEnumCompatible = [this, &implicitBoxed](EnumTy& p, EnumTy& q) { if (p.typeArgs.size() != q.typeArgs.size() || p.name != q.name || p.declPtr != q.declPtr) { return false; } bool flag = true; for (size_t idx = 0; idx < p.typeArgs.size(); ++idx) { if (CheckTypeCompatibility(p.typeArgs[idx], q.typeArgs[idx], implicitBoxed, p.typeArgs[idx]->IsGeneric()) != TypeCompatibility::IDENTICAL) { flag = false; break; } } return flag; }; return lvalue->IsEnum() && rvalue->IsEnum() && isEnumCompatible(*RawStaticCast(lvalue), *RawStaticCast(rvalue)) ? TypeCompatibility::IDENTICAL : TypeCompatibility::INCOMPATIBLE; } void TypeManager::ReplaceIdealTy(Ptr* ty) { if (!ty || !Ty::IsTyCorrect(*ty)) { return; } switch ((*ty)->kind) { case TypeKind::TYPE_IDEAL_INT: *ty = GetPrimitiveTy(TypeKind::TYPE_INT64); break; case TypeKind::TYPE_IDEAL_FLOAT: *ty = GetPrimitiveTy(TypeKind::TYPE_FLOAT64); break; case TypeKind::TYPE_CLASS: case TypeKind::TYPE_INTERFACE: case TypeKind::TYPE_ENUM: case TypeKind::TYPE_ARRAY: case TypeKind::TYPE_POINTER: case TypeKind::TYPE_TUPLE: for (auto& typeArg : (*ty)->typeArgs) { ReplaceIdealTy(&typeArg); } break; default: break; } } /* Check whether the interface is extended by a class type. * */ bool TypeManager::IsTyExtendInterface(const Ty& classTy, const Ty& interfaceTy) { auto decl = Ty::GetDeclPtrOfTy(&classTy); if (decl == nullptr) { return false; } auto extends = GetDeclExtends(*decl); auto typeArgs = GetTypeArgs(classTy); auto ret = GetAllExtendInterfaceTyHelper(extends, typeArgs); for (auto iTy : ret) { if (&interfaceTy == iTy) { return true; } } return false; } bool TypeManager::HasExtendInterfaceTyHelper(Ty& superTy, const std::set>& extends, const std::vector>& typeArgs) { PData::CommitScope cs(constraints); for (auto& extend : extends) { if (!CheckGenericDeclInstantiation(extend, typeArgs)) { PData::Reset(constraints); continue; } bool extendTyNotMatch = (!extend->extendedType || !extend->extendedType->ty || extend->extendedType->ty->typeArgs.size() != typeArgs.size()); if (extendTyNotMatch) { PData::Reset(constraints); continue; } TypeSubst typeMapping; for (size_t i = 0; i < typeArgs.size(); ++i) { // may be used in generic instantiation if (auto genSuper = DynamicCast(extend->extendedType->ty->typeArgs[i])) { typeMapping[genSuper] = typeArgs[i]; } } for (auto& superInterfaceTy : extend->inheritedTypes) { if (!IsInheritableType(superInterfaceTy->ty)) { continue; } Ptr instTy = GetInstantiatedTy(superInterfaceTy->ty, typeMapping); // search super interfaceTy recursively. if (HasSuperTy(*instTy, superTy, typeMapping)) { return true; } } PData::Reset(constraints); } return false; } std::unordered_set> TypeManager::GetAllExtendInterfaceTyHelper( const std::set>& extends, const std::vector>& typeArgs) { PData::CommitScope cs(constraints); std::unordered_set> ret; for (auto& extend : extends) { if (!CheckGenericDeclInstantiation(extend, typeArgs)) { PData::Reset(constraints); continue; } bool extendTyNotMatch = (!extend->extendedType || !extend->extendedType->ty || extend->extendedType->ty->typeArgs.size() != typeArgs.size()); if (extendTyNotMatch) { PData::Reset(constraints); continue; } TypeSubst typeMapping; for (size_t i = 0; i < typeArgs.size(); ++i) { // may be used in generic instantiation if (auto genSuper = DynamicCast(extend->extendedType->ty->typeArgs[i])) { typeMapping[genSuper] = typeArgs[i]; } } for (auto& superInterfaceTy : extend->inheritedTypes) { if (!IsInheritableType(superInterfaceTy->ty)) { continue; } Ptr instTy = GetInstantiatedTy(superInterfaceTy->ty, typeMapping); // Get super interfaceTy recursively. auto superInterfaceTys = GetAllSuperTys(*instTy); for (auto i : superInterfaceTys) { ret.insert(i); } } } return ret; } bool TypeManager::HasExtendedInterfaceTy(Ty& ty, Ty& superTy, const TypeSubst& typeMapping) { if (!Ty::IsTyCorrect(&ty) || !Ty::IsTyCorrect(&superTy)) { return false; } std::set> extends = GetAllExtendsByTy(ty); if (HasExtendInterfaceTyHelper(superTy, extends, GetTypeArgs(ty))) { return true; } if (ty.IsClass()) { auto declPtr = Ty::GetDeclPtrOfTy(&ty); CJC_ASSERT(declPtr); TypeSubst substituteMapping = GetSubstituteMapping(ty, typeMapping); // Get super classTy & interfaceTy. for (auto& inheritedType : declPtr->inheritedTypes) { CJC_ASSERT(inheritedType); // Get inherited types recursively. BUT do not collect inherited type which has cyclic inheritance. if (inheritedType->ty->IsClass() && IsInheritableType(inheritedType->ty)) { return HasExtendedInterfaceTy(*inheritedType->ty, superTy, substituteMapping); } } } return false; } std::unordered_set> TypeManager::GetAllExtendInterfaceTy(Ty& ty) { if (!Ty::IsTyCorrect(&ty)) { return {}; } if (auto found = tyExtendInterfaceTyMap.find(&ty); found != tyExtendInterfaceTyMap.end()) { return found->second; } std::set> extends = GetAllExtendsByTy(ty); auto ret = GetAllExtendInterfaceTyHelper(extends, GetTypeArgs(ty)); if (ty.IsClass()) { Ptr decl = Ty::GetDeclPtrOfTy(&ty); auto* cd = RawStaticCast(decl); CJC_ASSERT(cd); std::unordered_set> superTys; GetNominalSuperTy(ty, GenerateTypeMapping(*cd, ty.typeArgs), superTys); for (auto superTy : superTys) { auto cTy = DynamicCast(superTy); if (!cTy) { continue; } std::set> superExtends = GetDeclExtends(*cTy->declPtr); auto extendInterfaces = GetAllExtendInterfaceTyHelper(superExtends, superTy->typeArgs); for (auto i : extendInterfaces) { ret.insert(i); } } } if (!ty.HasPlaceholder()) { (void)tyExtendInterfaceTyMap.emplace(&ty, ret); } return ret; } void TypeManager::GetNominalSuperTy( const Ty& nominalTy, const TypeSubst& typeMapping, std::unordered_set>& tyList) { auto declPtr = Ty::GetDeclPtrOfTy(&nominalTy); if (!declPtr) { return; } TypeSubst substituteMapping = GetSubstituteMapping(nominalTy, typeMapping); // Get super classTy & interfaceTy. for (auto& inheritedType : declPtr->inheritedTypes) { CJC_ASSERT(inheritedType); // Get inherited types recursively. BUT do not collect inherited type which has cyclic inheritance. if (IsInheritableType(inheritedType->ty)) { auto inheritedTys = GetAllSuperTys(*inheritedType->ty, substituteMapping); tyList.merge(inheritedTys); } } } bool TypeManager::HasNominalSuperTy(Ty& nominalTy, Ty& superTy, const TypeSubst& typeMapping) { auto declPtr = Ty::GetDeclPtrOfTy(&nominalTy); if (!declPtr) { return false; } TypeSubst substituteMapping = GetSubstituteMapping(nominalTy, typeMapping); // Get super classTy & interfaceTy. for (auto& inheritedType : declPtr->inheritedTypes) { CJC_ASSERT(inheritedType); // search inherited types recursively. BUT do not search inherited type which has cyclic inheritance. if (IsInheritableType(inheritedType->ty) && HasSuperTy(*inheritedType->ty, superTy, substituteMapping)) { return true; } } return false; } namespace { // collect all transitive GenericsTy upperbound in addition to gty.upperBounds std::unordered_set> GetAllUpperBounds(const GenericsTy& gty) { std::unordered_set> ubs(gty.upperBounds.begin(), gty.upperBounds.end()); std::unordered_set> newGens; std::unordered_set> newUbs; for (auto ty : ubs) { if (ty->IsGeneric()) { newGens.insert(ty); } } while (!newGens.empty()) { for (auto ty : newGens) { for (auto ub : RawStaticCast(ty)->upperBounds) { if (ub->IsGeneric()) { newUbs.insert(ub); } } } ubs.insert(newUbs.begin(), newUbs.end()); newGens = newUbs; newUbs.clear(); } return ubs; } } // namespace bool TypeManager::HasSuperTy(Ty& ty, Ty& superTy, const TypeSubst& typeMapping, bool withExtended) { if (!Ty::IsTyCorrect(&ty) || !Ty::IsTyCorrect(&superTy)) { return false; } auto maybeInstTy = typeMapping.empty() ? Ptr(&ty) : GetInstantiatedTy(&ty, typeMapping); if (maybeInstTy == &superTy) { return true; } if (auto genTy = DynamicCast(maybeInstTy)) { if (GetAllUpperBounds(*genTy).count(&superTy) > 0) { return true; } } else if (auto itsTy = DynamicCast(maybeInstTy)) { for (auto iTy : itsTy->tys) { if (HasSuperTy(*iTy, superTy, typeMapping, withExtended)) { return true; } } } if (ty.IsNominal() && HasNominalSuperTy(ty, superTy, typeMapping)) { return true; } if (withExtended && HasExtendedInterfaceTy(ty, superTy, typeMapping)) { return true; } return false; } std::unordered_set> TypeManager::GetAllSuperTys(Ty& ty, const TypeSubst& typeMapping, bool withExtended) { std::unordered_set> tyList; if (!Ty::IsTyCorrect(&ty)) { return tyList; } TypeInfo key{&ty, typeMapping, withExtended}; if (auto found = tyToSuperTysMap.find(key); found != tyToSuperTysMap.end()) { return found->second; } auto maybeInstTy = typeMapping.empty() ? Ptr(&ty) : GetInstantiatedTy(&ty, typeMapping); if (auto classLikeTy = DynamicCast(maybeInstTy)) { tyList.emplace(classLikeTy); } else if (auto structTy = DynamicCast(maybeInstTy)) { tyList.emplace(structTy); } else if (auto genTy = DynamicCast(maybeInstTy)) { tyList.merge(GetAllUpperBounds(*genTy)); tyList.emplace(genTy); } else if (auto itsTy = DynamicCast(maybeInstTy)) { for (auto iTy : itsTy->tys) { tyList.merge(GetAllSuperTys(*iTy, typeMapping, withExtended)); } tyList.emplace(itsTy); } if (ty.IsNominal()) { GetNominalSuperTy(ty, typeMapping, tyList); } if (withExtended) { // Collect extend interface type. auto extendInterfaces = GetAllExtendInterfaceTy(ty); for (auto extendInterfaceTy : extendInterfaces) { tyList.insert(GetInstantiatedTy(extendInterfaceTy, typeMapping)); } } if (!ty.HasPlaceholder()) { tyToSuperTysMap.emplace(std::move(key), tyList); } return tyList; } std::unordered_set> TypeManager::GetAllCommonSuperTys(const std::unordered_set>& tys) { std::unordered_set> supers = GetAllSuperTys(**tys.begin()); std::unordered_set> supersNext; for (auto ty : tys) { std::unordered_set> supersCur; if (Ty::IsTyCorrect(ty)) { supersCur = GetAllSuperTys(*ty); supersCur.insert(ty); } for (auto tyn : supersCur) { if (supers.count(tyn) > 0) { supersNext.insert(tyn); } } supers = supersNext; supersNext.clear(); } return supers; } TypeSubst TypeManager::GetSubstituteMapping(const Ty& nominalTy, const TypeSubst& typeMapping) { auto declPtr = Ty::GetDeclPtrOfTy(&nominalTy); if (!declPtr) { return {}; } TypeSubst substituteMapping = GenerateTypeMapping(*declPtr, nominalTy.typeArgs); // Update 'substituteMapping' with input 'typeMapping'. for (auto& it : substituteMapping) { it.second = GetInstantiatedTy(it.second, typeMapping); } return substituteMapping; } std::vector> TypeManager::GetAllSuperInterfaceTysBFS(const InheritableDecl& decl) { std::unordered_set> visitedTys; std::vector> allSuperTys; std::deque, TypeSubst>> tempDeque{}; for (auto& type : decl.inheritedTypes) { if (!type->ty->IsInterface()) { continue; } auto interfaceTy = RawStaticCast(type->ty); auto typeMapping = GetSubstituteMapping(*interfaceTy, {}); tempDeque.emplace_back(interfaceTy, typeMapping); } while (!tempDeque.empty()) { auto temp = tempDeque.front(); tempDeque.pop_front(); if (visitedTys.count(temp.first) == 0) { allSuperTys.push_back(temp.first); visitedTys.insert(temp.first); for (auto& type : temp.first->declPtr->inheritedTypes) { if (!type->ty->IsInterface()) { // Type may be non-interface when user code is invalid. continue; } auto interfaceTy = RawStaticCast(type->ty); auto typeMapping = GetSubstituteMapping(*interfaceTy, temp.second); tempDeque.emplace_back( RawStaticCast(GetInstantiatedTy(interfaceTy, temp.second)), typeMapping); } } } return allSuperTys; } bool TypeManager::CheckExtendWithConstraint(const Ty& ty, Ptr extend) { std::vector> tys = GetTypeArgs(ty); return CheckGenericDeclInstantiation(extend, tys); } bool TypeManager::CheckGenericDeclInstantiation(Ptr d, const std::vector>& typeArgs) { if (!d) { return false; } // If 'typeArgs' is empty, return check succeed. if (typeArgs.empty()) { return true; } auto genericParams = GetDeclTypeParams(*d); Ptr genericDecl = d->GetGeneric(); bool invalid = genericDecl && std::any_of(genericDecl->genericConstraints.begin(), genericDecl->genericConstraints.end(), [](auto& gc) { return !gc || !gc->type; }); if (invalid || genericParams.size() != typeArgs.size()) { return false; } for (size_t i = 0; i < typeArgs.size(); ++i) { if (!genericParams[i]->HasGeneric() && genericParams[i] != typeArgs[i]) { return false; } } if (!genericDecl) { CJC_ASSERT(d->astKind == ASTKind::EXTEND_DECL); // Extend of instantiated type. return true; } // Previous inspections are quicker than map finding when extend and typeArgs set have huge combinations. if (auto found = declInstantiationStatus.find(d); found != declInstantiationStatus.end()) { if (auto foundTys = found->second.find(typeArgs); foundTys != found->second.end()) { return foundTys->second; } } TypeSubst instantiateMap = GenerateTypeMapping(*d, typeArgs); if (d->astKind == ASTKind::EXTEND_DECL) { for (size_t i = 0; i < typeArgs.size(); ++i) { // NOTE: extend may be 'extend A>', we need to consider nested generics. auto ty = GetInstantiatedTy(genericParams[i], instantiateMap); if (ty != typeArgs[i]) { declInstantiationStatus[d].emplace(typeArgs, false); return false; } } } bool result = true; // Check generic constraints. for (auto& gc : genericDecl->genericConstraints) { auto typeTy = GetInstantiatedTy(gc->type->ty, instantiateMap); bool isNotGeneric = !typeTy || typeTy->kind != TypeKind::TYPE_GENERICS; for (const auto& upperBound : gc->upperBounds) { auto upperBoundTy = GetInstantiatedTy(upperBound->ty, instantiateMap); bool typeNotMatch = !IsSubtype(typeTy, upperBoundTy); if (isNotGeneric && typeNotMatch) { result = false; break; } else if (isNotGeneric) { continue; } auto gt = RawStaticCast(typeTy); bool satisfyConstraint = std::any_of(gt->upperBounds.begin(), gt->upperBounds.end(), [this, &upperBoundTy](auto assumpUp) { return IsSubtype(assumpUp, upperBoundTy); }); if (!satisfyConstraint && typeNotMatch) { result = false; break; } } } (void)declInstantiationStatus[d].emplace(typeArgs, result); return result; } void TypeManager::Clear() { for (auto& i : allocatedTys) { delete i.Get(); } allocatedTys.clear(); } std::set> TypeManager::GetBuiltinTyExtends(Ty& ty) { auto foundBuiltin = builtinTyToExtendMap.find(&ty); if (foundBuiltin != builtinTyToExtendMap.end()) { return foundBuiltin->second; } auto foundInstantiated = instantiateBuiltInTyToExtendMap.find(&ty); if (foundInstantiated != instantiateBuiltInTyToExtendMap.end()) { return foundInstantiated->second; } return {}; } std::optional TypeManager::GetOverrideCache( const AST::FuncDecl* src, const AST::FuncDecl* target, Ty* baseTy, Ty* expectInstParent) { OverrideOrShadowKey key(src, target, baseTy, expectInstParent); auto it = overrideOrShadowCache.find(key); if (it != overrideOrShadowCache.end()) { return it->second; } return {}; } void TypeManager::AddOverrideCache( const AST::FuncDecl& src, const AST::FuncDecl& target, Ty* baseTy, Ty* expectInstParent, bool val) { OverrideOrShadowKey key(&src, &target, baseTy, expectInstParent); overrideOrShadowCache.emplace(key, val); if (val && src.outerDecl && src.outerDecl == Ty::GetDeclPtrOfTy(baseTy)) { UpdateTopOverriddenFuncDeclCache(&src, &target); } } std::set> TypeManager::GetDeclExtends(const InheritableDecl& decl) { auto found = declToExtendMap.find(&decl); if (found != declToExtendMap.end()) { return found->second; } return {}; } // Find the original extend decl. std::set> TypeManager::GetAllExtendsByTy(Ty& ty) { auto decl = Ty::GetDeclPtrOfTy(&ty); if (decl) { return GetDeclExtends(*decl); } auto builtInTy = GetTyForExtendMap(ty); if (builtInTy->IsIdeal()) { std::set> extends; auto kinds = GetIdealTypesByKind(builtInTy->kind); for (auto kind : kinds) { auto primitivety = GetPrimitiveTy(kind); auto found = builtinTyToExtendMap.find(primitivety); if (found != builtinTyToExtendMap.end()) { extends.insert(found->second.begin(), found->second.end()); } } return extends; } auto found = builtinTyToExtendMap.find(builtInTy); return found != builtinTyToExtendMap.end() ? found->second : std::set>{}; } std::unordered_set> TypeManager::GetAllExtendedDecls() { return Utils::GetKeys(declToExtendMap); } std::unordered_set> TypeManager::GetAllExtendedBuiltIn() { return Utils::GetKeys(builtinTyToExtendMap); } void TypeManager::RemoveExtendFromMap(ExtendDecl& ed) { for (auto& declIt : declToExtendMap) { if (declIt.second.count(&ed) > 0) { declIt.second.erase(&ed); return; } } for (auto& it : instantiateBuiltInTyToExtendMap) { if (it.second.count(&ed) > 0) { it.second.erase(&ed); return; } } } void TypeManager::UpdateBuiltInTyExtendDecl(Ty& builtinTy, ExtendDecl& ed) { instantiateBuiltInTyToExtendMap[&builtinTy].emplace(&ed); } void TypeManager::RecordUsedExtend(Ty& child, Ty& interfaceTy) { auto pair = std::make_pair(&child, &interfaceTy); if (checkedTyExtendRelation.count(pair) > 0) { return; } checkedTyExtendRelation.insert(pair); auto extendedTy = GetRealExtendedTy(child, interfaceTy); (void)boxedTys.emplace(extendedTy); RecordUsedGenericExtend(*extendedTy); std::set> extends; auto decl = Ty::GetDeclOfTy(extendedTy); if (decl) { // Collect non-generic decl. if (!decl->GetGeneric()) { boxedNonGenericDecls.emplace(decl); } extends = CollectAllRelatedExtends(*this, *decl); } else if (extendedTy->IsArray() || extendedTy->IsPointer()) { auto found = instantiateBuiltInTyToExtendMap.find(extendedTy); if (found == instantiateBuiltInTyToExtendMap.end()) { return; } extends = found->second; } else { auto found = builtinTyToExtendMap.find(extendedTy); if (found == builtinTyToExtendMap.end()) { return; } extends = found->second; } // For the generation of boxed decl, all member functions of related extends should be collected, // So we need to mark all related extends as used when boxing is happened. boxUsedExtends.insert(extends.begin(), extends.end()); } void TypeManager::RecordUsedGenericExtend(Ty& boxedTy, Ptr extend) { // If given extend decl to struct, the 'extend' must be generic decl. // If only given boxedTy, the boxedTy must have typeArguments. bool ignored = (extend && (!extend->GetGeneric() || extend->TestAttr(Attribute::GENERIC_INSTANTIATED))); if (ignored) { return; } // Record given extend decl or all related extends. auto& tyExtends = tyUsedExtends[&boxedTy]; if (extend) { tyExtends.emplace(extend); } else { auto decl = Ty::GetDeclPtrOfTy(&boxedTy); auto extends = decl ? CollectAllRelatedExtends(*this, *decl) : GetAllExtendsByTy(boxedTy); tyExtends.insert(extends.begin(), extends.end()); } } /** * When a class type does not extend the interface, but its super class does. * Get which super class extend the interface. */ Ptr TypeManager::GetExtendInterfaceSuperTy(ClassTy& classTy, const Ty& interfaceTy) { Ptr decl = Ty::GetDeclPtrOfTy(&classTy); auto* cd = RawStaticCast(decl); CJC_ASSERT(cd); auto sd = cd->GetSuperClassDecl(); if (sd == nullptr) { return nullptr; } // TypeMapping from classTy to super classTy will only have exact one pattern. MultiTypeSubst mts; GenerateGenericMapping(mts, classTy); auto typeMapping = MultiTypeSubstToTypeSubst(mts); while (sd != nullptr) { auto instTy = GetInstantiatedTy(sd->ty, typeMapping); if (instTy && IsTyExtendInterface(*instTy, interfaceTy)) { return instTy; } sd = sd->GetSuperClassDecl(); } return nullptr; } Ptr TypeManager::GetRealExtendedTy(Ty& child, const Ty& interfaceTy) { auto* extendedTy = &child; bool isNotDirectExtend = interfaceTy.IsInterface() && child.IsClass() && !IsTyExtendInterface(child, interfaceTy); if (isNotDirectExtend) { // If the class type ty does not extend the interface type iTy, // then we should find out which super class of the ty extend the interface. auto superTy = GetExtendInterfaceSuperTy(*RawStaticCast(&child), interfaceTy); if (Ty::IsTyCorrect(superTy)) { extendedTy = superTy; } } return extendedTy; } std::vector> TypeManager::GetTypeArgs(const Ty& ty) { if (!ty.IsArray()) { return ty.typeArgs; } auto& arrayTy = static_cast(ty); if (arrayTy.dims == 1) { return arrayTy.typeArgs; } // Since Array is a generic type, it's type argument should be array type dimension - 1 when dimension > 1. return {GetArrayTy(arrayTy.typeArgs[0], arrayTy.dims - 1)}; } Ptr TypeManager::GetNonNullTy(Ptr ty) { return Ty::IsInitialTy(ty) ? GetInvalidTy() : ty; } bool TypeManager::HasExtensionRelation(Ty& childTy, Ty& interfaceTy) { if (!Ty::IsTyCorrect(&childTy) || !Ty::IsTyCorrect(&interfaceTy)) { return false; } // Interface type, nothing type and invalid type should not be boxed. bool validRelation = childTy.kind != TypeKind::TYPE_INTERFACE && childTy.kind != TypeKind::TYPE_NOTHING && interfaceTy.kind == TypeKind::TYPE_INTERFACE; if (!validRelation) { return false; } if (interfaceTy.IsAny()) { return true; } if (childTy.kind != TypeKind::TYPE_CLASS) { // Enum/Struct can directly inherit interfaces, but also needs boxing when passing to interface type. auto inheritedTypes = GetAllSuperTys(childTy, {}, true); return inheritedTypes.count(&interfaceTy) != 0; } auto extendInterfaceList = GetAllExtendInterfaceTy(childTy); return extendInterfaceList.count(&interfaceTy) != 0; } Ptr TypeManager::GetTyForExtendMap(Ty& ty) { auto genericTy = Ty::GetGenericTyOfInsTy(ty); auto baseTy = &ty; if (ty.IsArray()) { baseTy = GetArrayTy(); } else if (ty.IsPointer()) { baseTy = GetPointerTy(GetInvalidTy()); } else if (genericTy != nullptr) { baseTy = genericTy; } return baseTy; } void TypeManager::RestoreJavaGenericsTy(AST::Decl& decl) const { CJC_ASSERT(decl.TestAttr(Attribute::GENERIC_INSTANTIATED) && HasJavaAttr(decl)); // When instantiated with erase mode, we make all same class's types pointing to unique // instantiated decl. for (auto& it : std::as_const(allocatedTys)) { auto ty = it.Get(); if (Ty::GetDeclPtrOfTy(ty) == decl.genericDecl) { if (auto cty = DynamicCast(ty)) { cty->decl = StaticCast(&decl); cty->commonDecl = StaticCast(&decl); } else if (auto ity = DynamicCast(ty)) { ity->decl = StaticCast(&decl); ity->commonDecl = StaticCast(&decl); } } } } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND bool TypeManager::IsFuncDeclSubType(const AST::FuncDecl& decl, const AST::FuncDecl& funcDecl) { auto declType = StaticCast(decl.ty); auto resolvedFuncType = DynamicCast(funcDecl.ty); if (resolvedFuncType && decl.identifier == funcDecl.identifier && IsFuncParameterTypesIdentical(declType->paramTys, resolvedFuncType->paramTys) && IsSubtype(declType->retTy, resolvedFuncType->retTy)) { return true; } return false; } #endif bool TypeManager::IsFuncDeclEqualType(const AST::FuncDecl& decl, const AST::FuncDecl& funcDecl) { return IsFuncDeclSubType(decl, funcDecl) && IsFuncDeclSubType(funcDecl, decl); } void TypeManager::UpdateTopOverriddenFuncDeclCache(const AST::Decl* src, const AST::Decl* target) { if (auto funcDecl = DynamicCast(src)) { auto& temp = overrideCache[funcDecl]; temp.emplace_back(StaticCast(target)); } else if (auto propDecl = DynamicCast(src)) { auto targetPropDecl = StaticCast(target); if (!propDecl->getters.empty() && !targetPropDecl->getters.empty()) { auto& temp = overrideCache[propDecl->getters.front().get()]; temp.emplace_back(targetPropDecl->getters.front().get()); } if (!propDecl->setters.empty() && !targetPropDecl->setters.empty()) { auto& temp = overrideCache[propDecl->setters.front().get()]; temp.emplace_back(targetPropDecl->setters.front().get()); } } } Ptr TypeManager::GetTopOverriddenFuncDecl(const AST::FuncDecl* funcDecl) const { const auto& decls = overrideCache.find(funcDecl); if (decls == overrideCache.end() || decls->second.empty()) { return nullptr; } Ptr ret = decls->second.front(); while ((overrideCache.find(ret) != overrideCache.end()) && !(overrideCache.find(ret)->second.empty())) { ret = overrideCache.find(ret)->second.front(); } return ret; } Ptr TypeManager::GetOverrideDeclInClassLike( AST::Decl& baseDecl, const AST::FuncDecl& funcDecl, bool withAbstractOverrides) { auto& decls = baseDecl.GetMemberDecls(); for (auto& decl : decls) { if (decl->astKind != ASTKind::FUNC_DECL && decl->astKind != ASTKind::PROP_DECL) { continue; } if ((!withAbstractOverrides && decl->TestAttr(Attribute::ABSTRACT)) || decl->TestAttr(Attribute::GENERIC)) { continue; } if (Is(decl.get())) { auto declFunc = StaticCast(decl.get()); if (IsFuncDeclSubType(*declFunc, funcDecl)) { return declFunc; } } else if (auto propDecl = DynamicCast(decl.get()); propDecl) { for (auto& tempGetFunc : propDecl->getters) { auto declFunc = StaticCast(tempGetFunc.get()); if (IsFuncDeclSubType(*declFunc, funcDecl)) { return declFunc; } } for (auto& tempSetFunc : propDecl->setters) { auto declFunc = StaticCast(tempSetFunc.get()); if (IsFuncDeclSubType(*declFunc, funcDecl)) { return declFunc; } } } } return nullptr; } std::pair TypeManager::IsExtendInheritRelation(const ExtendDecl& r, const ExtendDecl& l) { bool hasInheritRelat = false; bool isRSuper = false; auto mapping = GenerateTypeMappingByTy(r.ty, l.ty); for (auto& lSuper : std::as_const(l.inheritedTypes)) { for (auto& rSuper : std::as_const(r.inheritedTypes)) { auto lTy = GetInstantiatedTy(lSuper->ty, mapping); auto rTy = GetInstantiatedTy(rSuper->ty, mapping); if (IsSubtype(lTy, rTy)) { isRSuper = true; } if (isRSuper || IsSubtype(rTy, lTy)) { hasInheritRelat = true; break; } } } return {hasInheritRelat, isRSuper}; } Ptr TypeManager::AllocTyVar(const std::string& srcId, bool needSolving, Ptr derivedFrom) { Ptr ret; // allocate if (tyVarPool.empty()) { auto dummyDecl = MakeOwned(); auto newVar = GetGenericsTy(*dummyDecl); dummyGenDecls.emplace_back(std::move(dummyDecl)); newVar->isPlaceholder = true; ret = newVar; // name nextUniqId++; std::string name = srcId + "-" + std::to_string(nextUniqId); ret->decl->identifier = name; ret->name = name; } else { auto tv = *tyVarPool.begin(); tyVarPool.erase(tv); ret = RawStaticCast(tv.Get()); } // manage scope if (derivedFrom) { size_t lv = tyVarScopeDepth[derivedFrom]; tyVarScopes[lv]->AddTyVar(ret); tyVarScopeDepth[ret] = lv; } else { tyVarScopes.back()->AddTyVar(ret); tyVarScopeDepth[ret] = tyVarScopes.size() - 1; } if (needSolving) { unsolvedTyVars.insert(ret); constraints[ret].sum = PSet({GetAnyTy()}); } return ret; } void TypeManager::ReleaseTyVar(Ptr genTy) { CJC_ASSERT(tyVarPool.count(TypePointer(genTy)) == 0); genTy->upperBounds.clear(); tyVarPool.emplace(genTy); tyVarScopeDepth.erase(genTy); unsolvedTyVars.erase(genTy); constraints.erase(genTy); } const std::set>& TypeManager::GetUnsolvedTyVars() { return unsolvedTyVars; } void TypeManager::MarkAsUnsolvedTyVar(GenericsTy& tv) { CJC_ASSERT(tv.isPlaceholder); unsolvedTyVars.emplace(&tv); constraints[&tv].sum = PSet({GetAnyTy()}); } std::set> TypeManager::GetInnermostUnsolvedTyVars() { std::set> ret; for (auto tv : tyVarScopes.back()->tyVars) { if (unsolvedTyVars.count(tv) > 0) { ret.insert(tv); } } return ret; } size_t TypeManager::ScopeDepthOfTyVar(const GenericsTy& tyVar) { CJC_ASSERT(tyVarScopeDepth.count(&tyVar) > 0); return tyVarScopeDepth.at(&tyVar); } Ptr TypeManager::InstOf(const Ptr ty) { return ApplySubstPack(ty, instCtxScopes.back()->maps); } Ptr TypeManager::RecoverUnivTyVar(Ptr ty) { TypeSubst i2uMap; for (auto [univ, inst] : GetInstMapping().u2i) { i2uMap[StaticCast(inst)] = univ; } return GetInstantiatedTy(ty, i2uMap); } SubstPack TypeManager::GetInstMapping() { return instCtxScopes.back()->maps; } Ptr TypeManager::ApplySubstPack(const Ptr declaredTy, const SubstPack& maps, bool ignoreUnsolved) { Ptr t1; if (ignoreUnsolved) { TypeSubst u2iSolved; for (auto [tvu, tvi] : maps.u2i) { if (maps.inst.count(StaticCast(tvi)) > 0) { u2iSolved.emplace(tvu, tvi); } } t1 = GetInstantiatedTy(declaredTy, u2iSolved); } else { t1 = GetInstantiatedTy(declaredTy, maps.u2i); } return GetBestInstantiatedTy(t1, maps.inst); } std::set> TypeManager::ApplySubstPackNonUniq( const Ptr declaredTy, const SubstPack& maps, bool ignoreUnsolved) { Ptr t1; if (ignoreUnsolved) { TypeSubst u2iSolved; for (auto [tvu, tvi] : maps.u2i) { if (maps.inst.count(StaticCast(tvi)) > 0) { u2iSolved.emplace(tvu, tvi); } } t1 = GetInstantiatedTy(declaredTy, u2iSolved); } else { t1 = GetInstantiatedTy(declaredTy, maps.u2i); } return GetInstantiatedTys(t1, maps.inst); } bool TypeManager::PairIsOverrideOrImpl( const Decl& child, const Decl& parent, const Ptr baseTy, const Ptr parentTy) { if (child.astKind != parent.astKind || child.identifier.Val() != parent.identifier.Val()) { return false; } return child.astKind == ASTKind::FUNC_DECL ? IsOverrideOrShadow(*this, StaticCast(child), StaticCast(parent), baseTy, parentTy) : IsOverrideOrShadow(*this, StaticCast(child), StaticCast(parent), baseTy); } std::optional> TypeManager::GetExtendDeclByInterface(Ty& baseTy, Ty& interfaceTy) { if (!interfaceTy.IsInterface()) { return {}; } auto extendedTy = GetRealExtendedTy(baseTy, interfaceTy); auto extends = GetAllExtendsByTy(*extendedTy); for (auto extend : extends) { CJC_ASSERT(extend); auto& types = extend->inheritedTypes; bool isExtended = std::any_of( types.begin(), types.end(), [this, &interfaceTy](auto& type) { return IsSubtype(type->ty, &interfaceTy); }); if (isExtended) { return extend; } } return {}; } Ptr TypeManager::GetExtendDeclByMember(const Decl& member, Ty& baseTy) { if (!member.outerDecl) { return nullptr; } Ptr extend = nullptr; if (member.outerDecl->astKind == ASTKind::INTERFACE_DECL && member.outerDecl->ty) { auto extendDeclOp = GetExtendDeclByInterface(baseTy, *member.outerDecl->ty); extend = extendDeclOp.has_value() ? extendDeclOp.value() : nullptr; } else if (member.outerDecl->astKind == ASTKind::EXTEND_DECL) { extend = StaticCast(member.outerDecl); } return extend; } Ptr TypeManager::AddSumByCtor(GenericsTy& tv, Ty& tyCtor, std::vector>& tyArgs) { CJC_ASSERT(tv.isPlaceholder); for (size_t i = tyArgs.size(); i < tyCtor.typeArgs.size(); i++) { tyArgs.push_back(AllocTyVar("T-Fly", true, &tv)); } TypeSubst mapping; for (size_t i = 0; i < tyCtor.typeArgs.size(); i++) { mapping.emplace(StaticCast(tyCtor.typeArgs[i]), tyArgs[i]); } auto placeholderTy = GetInstantiatedTy(&tyCtor, mapping); constraints[&tv].sum.insert(placeholderTy); return placeholderTy; } Ptr TypeManager::ConstrainByCtor(GenericsTy& tv, Ty& tyCtor) { CJC_ASSERT(tv.isPlaceholder); TypeSubst mapping; for (auto ub : constraints[&tv].ubs) { if (OfSameCtor(ub, &tyCtor)) { return ub; } } for (auto tyArg : tyCtor.typeArgs) { mapping.emplace(StaticCast(tyArg), AllocTyVar("T-Fly", true, &tv)); } auto placeholderTy = GetInstantiatedTy(&tyCtor, mapping); if (IsSubtype(&tv, placeholderTy, true, false)) { return placeholderTy; } else { return nullptr; } } bool TypeManager::OfSameCtor(Ptr ty, Ptr tyCtor) { if (!Ty::IsTyCorrect(ty) || !Ty::IsTyCorrect(tyCtor)) { return false; } if (ty->typeArgs.size() != tyCtor->typeArgs.size()) { return false; } TypeSubst m; for (size_t i = 0; i < tyCtor->typeArgs.size(); i++) { m[StaticCast(tyCtor->typeArgs[i])] = ty->typeArgs[i]; } return GetInstantiatedTy(tyCtor, m) == ty; } namespace { TypeSubst GetGreedySubst(Constraint& cst) { TypeSubst m; for (auto& [tv, bound] : cst) { if (!bound.eq.empty()) { m.emplace(tv, *bound.eq.begin()); } } return m; } } Ptr TypeManager::TryGreedySubst(Ptr ty) { if (Ty::IsTyCorrect(ty) && ty->HasPlaceholder()) { return GetInstantiatedTy(ty, GetGreedySubst(constraints)); } return ty; } bool TypeManager::TyVarHasNoSum(TyVar& tv) const { return constraints.count(&tv) > 0 && constraints.at(&tv).sum.size() == 1 && (*constraints.at(&tv).sum.cbegin())->IsAny(); } Ptr TypeManager::GetDummyBuiltInDecl(Ptr ty) { if (dummyBuiltInDecls.count(ty) == 0) { dummyBuiltInDecls.emplace(ty, MakeOwnedNode()); dummyBuiltInDecls[ty]->ty = ty; } return dummyBuiltInDecls[ty].get(); } } // namespace Cangjie cangjie_compiler-1.0.7/src/Sema/Utils.cpp000066400000000000000000000600211510705540100202660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements utility functions for TypeChecker. */ #include #include "TypeCheckUtil.h" #include "Diags.h" #include "TypeCheckerImpl.h" #include "cangjie/AST/Clone.h" #include "cangjie/AST/Create.h" #include "cangjie/AST/Match.h" #include "cangjie/AST/Utils.h" namespace Cangjie { using namespace AST; using namespace Sema; using namespace TypeCheckUtil; namespace { std::vector> GetCycleOnPath(Ptr const root, std::deque>& path) { std::vector> cycle; bool foundCycleStart = false; for (auto& i : std::as_const(path)) { if (i == root) { foundCycleStart = true; } if (foundCycleStart) { cycle.emplace_back(i); } } return cycle; } bool IsInitRef(const Node& node) { const auto target = node.GetTarget(); return target && IsInstanceConstructor(*target); } std::vector> CollectInitRefsInVar(const std::vector>& decls) { std::vector> initRefsInVar; for (auto& decl : decls) { CJC_NULLPTR_CHECK(decl); // Constructors don't depend on static variables. if (auto vd = DynamicCast(decl.get()); vd && !vd->TestAttr(Attribute::STATIC)) { Walker(vd->initializer.get(), [&initRefsInVar](auto node) { if (IsInitRef(*node)) { (void)initRefsInVar.emplace_back(node); } return VisitAction::WALK_CHILDREN; }).Walk(); } } return initRefsInVar; } std::vector> CollectInitRefsInVar(const Decl& typeDecl) { if (typeDecl.astKind == ASTKind::STRUCT_DECL) { const auto& sd = StaticCast(typeDecl); CJC_NULLPTR_CHECK(sd.body); return CollectInitRefsInVar(sd.body->decls); } else if (typeDecl.astKind == ASTKind::CLASS_DECL) { const auto& cd = StaticCast(typeDecl); CJC_NULLPTR_CHECK(cd.body); return CollectInitRefsInVar(cd.body->decls); } else { return {}; } } struct RecursiveCtorCtx { std::unordered_set> finished; std::unordered_set> visited; std::unordered_set> ignored; // A map from struct/class declaration to its dependencies. std::unordered_map, std::vector>> typeDeclToInitRefsInVar; std::vector> path; // A stack to keep track of the cycle. bool foundCycle = false; const std::vector>& GetDependencies(const Decl& init) { auto typeDecl = init.outerDecl; CJC_NULLPTR_CHECK(typeDecl); auto iter = typeDeclToInitRefsInVar.find(typeDecl); if (iter == typeDeclToInitRefsInVar.cend()) { iter = typeDeclToInitRefsInVar.emplace(typeDecl, CollectInitRefsInVar(*typeDecl)).first; } return iter->second; } }; VisitAction VisitRecursiveCtorCtx(RecursiveCtorCtx& ctx, const Node& node) { ctx.path.emplace_back(&node); if (Utils::In(&node, ctx.finished)) { return VisitAction::SKIP_CHILDREN; } if (Utils::In(&node, ctx.visited)) { ctx.foundCycle = true; return VisitAction::STOP_NOW; } (void)ctx.visited.emplace(&node); return VisitAction::WALK_CHILDREN; } // Use DFS to detect cycle for constructor calls. // The dependency graph is embedded in the AST so we don't have to build the graph explicitly. // There are two kinds of vertices in the dependency graph: // 1. Constructor declarations, judged by `IsInstanceConstructor`. // 2. Constructor references, judged by `IsInitRef`. // We use these two predicates to skip irrelevant nodes in the AST. bool CheckRecursiveCtorFoundCycle(RecursiveCtorCtx& ctx, Node& decl) { auto postVisit = [&ctx](const auto node) { if (IsInstanceConstructor(*node) || IsInitRef(*node)) { ctx.path.pop_back(); (void)ctx.finished.emplace(node); } return VisitAction::SKIP_CHILDREN; }; auto preVisit = [&ctx](const auto node) { if (Utils::In>(node, ctx.ignored)) { return VisitAction::SKIP_CHILDREN; } if (auto d = DynamicCast(node); d && d->annotationsArray) { // Annotations are not dependencies. (void)ctx.ignored.emplace(d->annotationsArray.get()); for (auto& anno : d->annotations) { (void)ctx.ignored.emplace(anno.get()); } } if (IsInstanceConstructor(*node)) { const Decl& init = StaticCast(*node); // There must be syntax error if `decl.outerDecl` is null. if (init.outerDecl == nullptr) { return VisitAction::STOP_NOW; } auto action = VisitRecursiveCtorCtx(ctx, init); if (action != VisitAction::WALK_CHILDREN) { return action; } // Member variables are initialized before constructor call. So for each constructor declaration, // there is an arc from the declaration to the initializer in the dependency graph. for (Ptr ref : ctx.GetDependencies(init)) { if (CheckRecursiveCtorFoundCycle(ctx, *ref)) { return VisitAction::STOP_NOW; } } } else if (IsInitRef(*node)) { auto action = VisitRecursiveCtorCtx(ctx, *node); if (action != VisitAction::WALK_CHILDREN) { return action; } // Reference expressions depend on the constructor declarations. // So there is an arc from the reference to the target. Ptr target = node->GetTarget(); CJC_NULLPTR_CHECK(target); if (CheckRecursiveCtorFoundCycle(ctx, *target)) { return VisitAction::STOP_NOW; } } // There may be more arcs in the children. return VisitAction::WALK_CHILDREN; }; Walker(&decl, preVisit, postVisit).Walk(); return ctx.foundCycle; } Range MakeRangeForRecursiveConstructorCall(const Node& node) { if (node.astKind == ASTKind::FUNC_DECL) { return MakeRangeForDeclIdentifier(StaticCast(node)); } else { return MakeRange(node.begin, node.end); } } void DiagRecursiveConstructorCall(DiagnosticEngine& diag, const std::vector>& path) { CJC_ASSERT(!path.empty()); // The head of the cycle is the same as the last one in path. const Node& head = *path.back(); auto builder = diag.DiagnoseRefactor( DiagKindRefactor::sema_recursive_constructor_call, head, MakeRangeForRecursiveConstructorCall(head)); auto iter = std::find(path.cbegin(), path.cend(), path.back()); // Skip the head of the cycle, which is the same as the error message. ++iter; for (; iter != path.cend(); ++iter) { CJC_NULLPTR_CHECK(*iter); const Node& node = **iter; // We should skip the compiler added `super`. // But keep in mind that `super` in primary constructors also have the `COMPILER_ADD` attribute. // We use the position to distinguish these situations. if (node.astKind == ASTKind::REF_EXPR && node.TestAttr(Attribute::COMPILER_ADD) && StaticCast(node).isSuper) { // Only `init` depends on `super`. const FuncDecl& init = StaticCast(**(iter - 1)); CJC_NULLPTR_CHECK(init.funcBody); if (init.funcBody->begin == node.begin) { continue; } } builder.AddNote(node, MakeRangeForRecursiveConstructorCall(node), "depends on"); } } } // namespace // desugar primary ctor before cjmp customDef merging void TypeChecker::TypeCheckerImpl::CheckPrimaryCtorBeforeMerge(Node &root) { // Create by cjogen is not need to add default construct. if (root.TestAttr(Attribute::TOOL_ADD)) { return; } Walker walkerPackage(&root, [this](Ptr node) -> VisitAction { if (node->astKind == ASTKind::FILE) { auto file = StaticAs(node); for (auto &decl : file->decls) { if (decl->astKind == ASTKind::CLASS_DECL || decl->astKind == ASTKind::STRUCT_DECL) { CheckPrimaryCtorForClassOrStruct(StaticCast(*decl)); } } return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }); walkerPackage.Walk(); } void TypeChecker::TypeCheckerImpl::AddDefaultFunction(Node& root) { // Create by cjogen is not need to add default construct. if (root.TestAttr(Attribute::TOOL_ADD)) { return; } Walker walkerPackage(&root, [this](Ptr node) -> VisitAction { if (node && node->astKind == ASTKind::FILE) { auto file = StaticAs(node); CheckDefaultParamFuncsEntry(*file); return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }); walkerPackage.Walk(); } namespace { void SetCtorFuncPosition(Decl& decl, FuncDecl& initFunc) { if (initFunc.TestAttr(Attribute::STATIC)) { // Do not set ctor position for static constructor here. return; } std::vector>& decls = (decl.astKind == ASTKind::STRUCT_DECL) ? StaticAs(&decl)->body->decls : StaticAs(&decl)->body->decls; for (auto it = decls.rbegin(); it != decls.rend(); ++it) { if ((*it)->astKind != ASTKind::VAR_DECL || (*it)->TestAttr(Attribute::STATIC)) { continue; } // Find last instance vardecl. initFunc.begin = (*it)->begin; break; } initFunc.begin.fileID = decl.begin.fileID; } bool HasOnlyStructTypeInCycle(std::vector> const& cycle) { bool res = true; for (auto& i : cycle) { if (i->astKind != ASTKind::STRUCT_DECL) { res = false; break; } } return res; } bool HasRecompilingMemberVar(const std::vector>& members, bool isStatic) { std::vector> variables; for (auto& it : std::as_const(members)) { if (it->astKind == ASTKind::VAR_DECL && it->TestAttr(Attribute::STATIC) == isStatic) { (void)variables.emplace_back(it.get()); } } return variables.empty() || std::any_of(variables.cbegin(), variables.cend(), [](auto it) { return it->toBeCompiled; }); } } // namespace OwnedPtr TypeCheckUtil::CreateDefaultCtor(InheritableDecl& decl, bool isStatic) { OwnedPtr funcBody = MakeOwnedNode(); CopyFileID(funcBody.get(), &decl); funcBody->body = MakeOwnedNode(); CopyFileID(funcBody->body.get(), &decl); auto funcParamList = MakeOwnedNode(); CopyFileID(funcParamList.get(), &decl); funcBody->paramLists.push_back(std::move(funcParamList)); OwnedPtr initFunc = MakeOwnedNode(); CopyFileID(initFunc.get(), &decl); initFunc->funcBody = std::move(funcBody); initFunc->funcBody->funcDecl = initFunc.get(); initFunc->identifier = "init"; initFunc->identifier.SetFileID(decl.begin.fileID); initFunc->begin = decl.identifier.Begin(); // Set function postion to declare's line. initFunc->end = initFunc->begin + decl.identifier.Length(); // Set function end postion to end of declare name. initFunc->funcBody->begin = initFunc->begin; initFunc->funcBody->end = initFunc->end; initFunc->outerDecl = &decl; initFunc->EnableAttr(Attribute::IMPLICIT_ADD); initFunc->toBeCompiled = HasRecompilingMemberVar(decl.GetMemberDecls(), isStatic); if (isStatic) { // Static init is always 'private'. initFunc->EnableAttr(Attribute::STATIC, Attribute::PRIVATE); } else { initFunc->EnableAttr(Attribute::PUBLIC); } SetCtorFuncPosition(decl, *initFunc); // If member exists, set function position to member's line. initFunc->EnableAttr(Attribute::CONSTRUCTOR, Attribute::COMPILER_ADD); if (decl.astKind == ASTKind::STRUCT_DECL) { auto structDecl = StaticAs(&decl); initFunc->funcBody->parentStruct = structDecl; } else if (decl.astKind == ASTKind::CLASS_DECL) { auto classDecl = StaticAs(&decl); initFunc->funcBody->parentClassLike = classDecl; } return initFunc; } Ptr TypeCheckUtil::GetCurFuncBody(const ASTContext& ctx, const std::string& scopeName) { auto sym = ScopeManager::GetCurSymbolByKind(SymbolKind::FUNC_LIKE, ctx, scopeName); Ptr ret{nullptr}; if (sym) { if (auto fd = AST::As(sym->node); fd) { ret = fd->funcBody.get(); } else if (auto le = AST::As(sym->node); le) { ret = le->funcBody.get(); } else if (auto md = AST::As(sym->node); md) { ret = md->desugarDecl->funcBody.get(); } else if (auto pcd = AST::As(sym->node); pcd) { ret = pcd->funcBody.get(); } } return ret; } // if there is no default constructor, insert one. // NOTICE: it will change AST Node! void TypeChecker::TypeCheckerImpl::AddDefaultCtor(InheritableDecl& decl) const { if (decl.astKind != ASTKind::STRUCT_DECL && decl.astKind != ASTKind::CLASS_DECL) { return; } // Do not add default constructor to Objective-C mirrors declarations // because it requires explicitly added one if (decl.TestAnyAttr(Attribute::OBJ_C_MIRROR)) { return; } // Do not add default constructor to common class(struct) because // it requires explicitly added one // Do not add default constructor to platform class(struct) because it // always has one in common class(struct) due to above requirement if (decl.astKind == ASTKind::STRUCT_DECL) { auto structDecl = StaticAs(&decl); if (!structDecl->TestAnyAttr(Attribute::COMMON)) { structDecl->body->decls.push_back(CreateDefaultCtor(decl)); } } else if (decl.astKind == ASTKind::CLASS_DECL) { auto classDecl = StaticAs(&decl); if (!classDecl->TestAnyAttr(Attribute::COMMON, Attribute::JAVA_MIRROR)) { classDecl->body->decls.push_back(CreateDefaultCtor(decl)); } } } void TypeChecker::TypeCheckerImpl::AddDefaultSuperCall(const FuncBody& funcBody) const { bool hasSuper = false; Walker(funcBody.body.get(), [&hasSuper](auto node) { if (auto re = DynamicCast(node); re && re->isSuper && re->isBaseFunc) { hasSuper = true; return VisitAction::STOP_NOW; } else if (node->astKind == ASTKind::FUNC_BODY) { return VisitAction::SKIP_CHILDREN; } return VisitAction::WALK_CHILDREN; }).Walk(); if (hasSuper) { return; // If the funcbody already contains 'super' expression, do not add super call. } OwnedPtr superCall = MakeOwned(); CopyBasicInfo(&funcBody, superCall.get()); auto superExpr = CreateRefExpr("super"); superExpr->isSuper = true; superExpr->isAlone = false; superCall->baseFunc = std::move(superExpr); CopyBasicInfo(&funcBody, superCall->baseFunc.get()); superCall->EnableAttr(Attribute::COMPILER_ADD); if (funcBody.body) { if (funcBody.body->body.empty()) { superCall->begin = funcBody.begin; } funcBody.body->body.insert(funcBody.body->body.begin(), std::move(superCall)); } } void TypeChecker::TypeCheckerImpl::GetTypeArgsOfType(Ptr type, std::vector>& params) { if (type == nullptr) { return; } params.push_back(type); switch (type->astKind) { case ASTKind::REF_TYPE: { auto ty = StaticAs(type); std::for_each(ty->typeArguments.begin(), ty->typeArguments.end(), [this, ¶ms](auto& p) { GetTypeArgsOfType(p.get(), params); }); break; } case ASTKind::QUALIFIED_TYPE: { auto ty = StaticAs(type); std::for_each(ty->typeArguments.begin(), ty->typeArguments.end(), [this, ¶ms](auto& p) { GetTypeArgsOfType(p.get(), params); }); break; } case ASTKind::TUPLE_TYPE: { auto ty = StaticAs(type); std::for_each(ty->fieldTypes.begin(), ty->fieldTypes.end(), [this, ¶ms](auto& p) { GetTypeArgsOfType(p.get(), params); }); break; } case ASTKind::FUNC_TYPE: { auto ty = StaticAs(type); std::for_each(ty->paramTypes.begin(), ty->paramTypes.end(), [this, ¶ms](auto& p) { GetTypeArgsOfType(p.get(), params); }); GetTypeArgsOfType(ty->retType.get(), params); break; } case ASTKind::OPTION_TYPE: { auto ty = StaticAs(type); GetTypeArgsOfType(ty->desugarType.get(), params); break; } default: break; } } // Check value type recursive. void TypeChecker::TypeCheckerImpl::CheckValueTypeRecursiveDFS(Ptr root, std::deque> path) { if (!root || root->checkFlag == InheritanceVisitStatus::VISITED) { return; } if (root->checkFlag == InheritanceVisitStatus::VISITING) { // Found a cycle; mark every decl in this cycle. auto cycle = GetCycleOnPath(root, path); if (HasOnlyStructTypeInCycle(cycle)) { std::string str; for (auto node : cycle) { str += node->identifier + "->"; } str += root->identifier; diag.Diagnose(*root, DiagKind::sema_value_type_recursive, str.c_str()); } return; } root->checkFlag = InheritanceVisitStatus::VISITING; path.push_back(root); CheckValueTypeRecursiveDFSSwitch(root, path); path.pop_back(); root->checkFlag = InheritanceVisitStatus::VISITED; } void TypeChecker::TypeCheckerImpl::CheckValueTypeRecursiveDFSSwitch(Ptr root, const std::deque>& path) { if (root == nullptr) { return; } if (root->astKind != ASTKind::STRUCT_DECL && root->astKind != ASTKind::ENUM_DECL) { return; } if (root->ty != nullptr && root->ty->IsEnum() && DynamicCast(root->ty)) { return; } auto checkField = [this, &path](const Decl& decl) { if (decl.ty->IsEnum() && DynamicCast(decl.ty)) { return; } auto needCheckElemTy = Is(decl.ty) || Is(decl.ty); if (!needCheckElemTy) { return CheckValueTypeRecursiveDFS(Ty::GetDeclOfTy(decl.ty), path); } for (auto elementTy : decl.ty->typeArgs) { CheckValueTypeRecursiveDFS(Ty::GetDeclOfTy(elementTy), path); } }; if (root->astKind == ASTKind::STRUCT_DECL) { auto rd = StaticAs(root); for (auto& d : rd->body->decls) { CJC_NULLPTR_CHECK(d); if (d->astKind != ASTKind::VAR_DECL || d->TestAttr(Attribute::STATIC)) { continue; } checkField(*d); } } else { Ptr ed = StaticAs(root); for (auto& ctor : ed->constructors) { if (ctor->astKind != ASTKind::FUNC_DECL) { continue; } auto fd = StaticAs(ctor.get()); if (fd->funcBody->paramLists.empty()) { continue; } for (auto& val : fd->funcBody->paramLists[0]->params) { checkField(*val); } } } } void TypeChecker::TypeCheckerImpl::CheckRecursiveConstructorCall(const std::vector>& decls) { if (decls.empty()) { return; } CJC_NULLPTR_CHECK(decls.front()->outerDecl); RecursiveCtorCtx ctx; for (auto& decl : decls) { CJC_NULLPTR_CHECK(decl); if ((decl->astKind == ASTKind::VAR_DECL && !decl->TestAttr(Attribute::STATIC)) || IsInstanceConstructor(*decl)) { if (CheckRecursiveCtorFoundCycle(ctx, *decl)) { DiagRecursiveConstructorCall(diag, ctx.path); return; } } } } bool TypeChecker::TypeCheckerImpl::HasModifier(const std::set& modifiers, TokenKind kind) const { return std::any_of(modifiers.begin(), modifiers.end(), [kind](const auto& it) { return it.modifier == kind; }); } bool TypeChecker::TypeCheckerImpl::IsDeprecatedStrict( const Ptr decl ) const { for (auto& anno: decl->annotations) { if (anno->kind == AnnotationKind::DEPRECATED) { std::string message = ""; std::string since = ""; bool strict = false; AST::ExtractArgumentsOfDeprecatedAnno(anno, message, since, strict); return strict; } } InternalError("Declaration was not marked as deprecated."); return false; } using std::get; template<> void PData::Commit(Constraint& data) { for (auto& [tyvar, bounds] : data) { bounds.lbs.commit(); bounds.ubs.commit(); bounds.sum.commit(); bounds.eq.commit(); } } template<> void PData::Reset(Constraint& data) { for (auto& [tyvar, bounds] : data) { bounds.lbs.reset(); bounds.ubs.reset(); bounds.sum.reset(); bounds.eq.reset(); } } template<> CstVersionID PData::Stash(Constraint& data) { CstVersionID ver; for (auto& [tyvar, bounds] : data) { auto id1 = bounds.lbs.stash(); auto id2 = bounds.ubs.stash(); auto id3 = bounds.sum.stash(); auto id4 = bounds.eq.stash(); ver[tyvar] = {id1, id2, id3, id4}; } return ver; } template<> void PData::Apply(Constraint& data, CstVersionID& version) { for (auto& [tyvar, ver] : version) { data[tyvar].lbs.apply(get<0>(ver)); data[tyvar].ubs.apply(get<1>(ver)); data[tyvar].sum.apply(get<2>(ver)); data[tyvar].eq.apply(get<3>(ver)); } } template<> void PData::ResetSoft(Constraint& data) { for (auto& [tyvar, bounds] : data) { bounds.lbs.resetSoft(); bounds.ubs.resetSoft(); bounds.sum.resetSoft(); bounds.eq.resetSoft(); } } std::string TyVarBounds::ToString() const { std::string s = "Lower bounds: "; s += "{"; for (auto& ty : std::as_const(lbs)) { s += Ty::ToString(ty) + ", "; } s += "}\n"; s += "Upper bounds: "; s += "{"; for (auto& ty : std::as_const(ubs)) { s += Ty::ToString(ty) + ", "; } s += "}\n"; s += "Sum: "; s += "{"; for (auto& ty : std::as_const(sum)) { s += Ty::ToString(ty) + ", "; } s += "}\n"; s += "Equals: "; s += "{"; for (auto& ty : std::as_const(eq)) { s += Ty::ToString(ty) + ", "; } s += "}\n"; return s; } std::string ToStringC(const Constraint& c) { std::string s; for (auto& [tv, bounds] : c) { s += Ty::ToString(tv) + " :\n" + bounds.ToString() + "\n"; } return s; } std::string ToStringS(const TypeSubst& m) { std::string s = "["; for (auto& [tv, ty] : m) { s += Ty::ToString(tv) + " |-> " + Ty::ToString(ty) + ", "; } s += "]\n"; return s; } std::string ToStringMS(const MultiTypeSubst& m) { std::string s = "["; for (auto& [tv, tys] : m) { s += Ty::ToString(tv) + " |-> {"; for (auto ty : tys) { s += Ty::ToString(ty) + ", "; } s += "},\n"; } s += "]\n"; return s; } std::string ToStringP(const SubstPack& m) { return ToStringS(m.u2i) + ToStringMS(m.inst); } } // namespace Cangjie cangjie_compiler-1.0.7/src/Utils/000077500000000000000000000000001510705540100166765ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Utils/CMakeLists.txt000066400000000000000000000057411510705540100214450ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. if(EXISTS ${CANGJIE_CJNATIVE_SOURCE_DIR}) set(UNICODE_DEPENDENCIES ${CANGJIE_CJNATIVE_SOURCE_DIR}) else() execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/utils_dep/ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) execute_process(COMMAND git clone https://gitcode.com/openharmony/third_party_llvm-project.git --depth 1 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/utils_dep/ RESULT_VARIABLE GIT_CLONE_RESULT) execute_process(COMMAND git fetch --depth 1 origin 5c68a1cb123161b54b72ce90e7975d95a8eaf2a4 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/utils_dep/third_party_llvm-project RESULT_VARIABLE GIT_FETCH_RESULT) execute_process(COMMAND git checkout 5c68a1cb123161b54b72ce90e7975d95a8eaf2a4 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/utils_dep/third_party_llvm-project RESULT_VARIABLE GIT_FETCH_RESULT) set(UNICODE_DEPENDENCIES ${CMAKE_BINARY_DIR}/utils_dep/third_party_llvm-project) endif() set(UNICODE_UTIL_SRC Unicode.cpp UnicodeNFC.cpp UnicodeWidth.cpp ${UNICODE_DEPENDENCIES}/llvm/lib/Support/ConvertUTF.cpp) set(COMMON_UTIL_SRC FileUtil.cpp SafePointer.cpp SignalUtil.cpp FloatFormat.cpp Utils.cpp ICEUtil.cpp Semaphore.cpp StdUtils/StdUtils.cpp) set(PROFILE_SRC ProfileRecorder.cpp UserBase.cpp UserTimer.cpp UserMemoryUsage.cpp UserCodeInfo.cpp) if(CANGJIE_BUILD_CJC OR CANGJIE_BUILD_TESTS) if(WIN32) list(APPEND COMMON_UTIL_SRC SignalWin.cpp) else() list(APPEND COMMON_UTIL_SRC SignalUnix.cpp) endif() endif() add_library(CangjieCommonUtil OBJECT ${COMMON_UTIL_SRC}) add_library(CangjieCommonUtilFFI OBJECT FileUtil.cpp SafePointer.cpp FloatFormat.cpp Utils.cpp ICEUtil.cpp Semaphore.cpp StdUtils/StdUtils.cpp) add_library(CangjieUnicodeUtils OBJECT ${UNICODE_UTIL_SRC}) target_include_directories(CangjieUnicodeUtils PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(CangjieUnicodeUtils PRIVATE ${UNICODE_DEPENDENCIES}/llvm/include) if(CANGJIE_GENERATE_UNICODE_TABLE) add_dependencies(CangjieUnicodeUtils GenerateNFCData) add_dependencies(CangjieUnicodeUtils GenerateWidthData) endif() if(CANGJIE_BUILD_CJC) add_library(CangjieProfileUtils OBJECT ${PROFILE_SRC}) target_compile_options(CangjieProfileUtils PRIVATE ${CJC_EXTRA_WARNINGS}) endif() target_include_directories(CangjieCommonUtil PRIVATE ${BOUNDSCHECK}/include) target_compile_options(CangjieCommonUtil PRIVATE ${CJC_WITH_LLVM_EXTRA_WARNINGS}) target_compile_options(CangjieCommonUtilFFI PRIVATE ${CJC_WITH_LLVM_EXTRA_WARNINGS}) target_compile_options(CangjieUnicodeUtils PRIVATE ${CJC_WITH_LLVM_EXTRA_WARNINGS}) cangjie_compiler-1.0.7/src/Utils/FileUtil.cpp000066400000000000000000001106631510705540100211260ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements file util related apis. */ #include "cangjie/Utils/FileUtil.h" #include #include #include #include #include #include #include #include #include "cangjie/Basic/Print.h" #include "cangjie/Basic/Utils.h" #include "cangjie/Driver/StdlibMap.h" #include "cangjie/Modules/ImportManager.h" #include "cangjie/Utils/Unicode.h" #include "cangjie/Lex/Lexer.h" #ifdef _WIN32 #include "cangjie/Basic/StringConvertor.h" #include #include #include #include #include #ifndef PATH_MAX #define PATH_MAX MAX_PATH #endif #elif defined(__linux__) || defined(__APPLE__) #include #endif namespace Cangjie::FileUtil { namespace { constexpr int SHORT_HASH_NUM = 8; constexpr int BOM_ENCODING_LEN = 3; // EF BB BF constexpr char SLASH = '/'; constexpr char BACKSLASH = '\\'; constexpr char BACKTICK = '`'; constexpr std::string_view TEST_FILE_NAME{"_test.cj"}; #ifdef _WIN32 inline int Mkdir(const std::string& path) { return _mkdir(path.c_str()); } #else inline int Mkdir(const std::string& path) { return mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); } #endif /** * Find last of dir split, if found return true and the pos. */ inline std::optional FindLastOfDirSplit(const std::string& str) { auto lastDirSepPos = str.find_last_of(DIR_SEPARATOR); if (lastDirSepPos != std::string::npos) { return {lastDirSepPos}; } return std::nullopt; } /** * Whether the last char is '/\\'. */ inline bool IsLastCharDirSplit(const std::string& str) { if (str.empty()) { return false; } return str.find_last_of(DIR_SEPARATOR) == str.size() - 1; } inline std::string AppendPath(const std::string& p1, const std::string& p2) { if (IsLastCharDirSplit(p1)) { return p1 + p2; } return p1 + DIR_SEPARATOR[0] + p2; } /** * Find last of '.', if found returns the pos, otherwise returns nullopt. */ inline std::optional FindLastOfDot(const std::string& str) { auto pos = str.find_last_of('.'); if (pos != std::string::npos) { return {pos}; } return std::nullopt; } /** * Whether the @p str contain '/\\'. */ inline bool HasDirSplit(const std::string& str) { return str.find_first_of(DIR_SEPARATOR) != std::string::npos; } inline bool CanExecute(const std::string& path) { if (FileExist(path)) { return Access(path, FM_EXE); } return false; } } // namespace bool IsSlash(char c) { #ifdef _WIN32 return c == BACKSLASH || c == SLASH; #else return c == SLASH; #endif } bool CheckCommandLineInjection(const std::string& cmd) { if (cmd.find_first_of(INJECTION_STRING) != std::string::npos) { return true; } // Allow ` appears in cmd in pairs. // Backticks are allowed becasue module names of Cangjie accept backticks. A user may wrap a // module name with a pair of backticks to ensure Compiler doesn't treat the name as reserved // keywords. However, it seems unnecessary to ask for wrapping such module names with backticks // in the command. We know the argument of --module-name IS a module name not a reserved // keyword. bool isEven = true; size_t pos = 0; while ((pos = cmd.find(BACKTICK, pos)) != std::string::npos) { isEven = !isEven; pos++; } return !isEven; } std::string TransferEscapeBacktick(const std::string& s) { #ifdef _WIN32 return s; #else std::string ret = s; auto cnt = std::count(s.begin(), s.end(), BACKTICK); ret.reserve(s.size() + static_cast(cnt)); size_t pos = s.size() - 1; while ((pos = s.rfind(BACKTICK, pos)) != std::string::npos) { ret.insert(pos, 1, BACKSLASH); if (pos == 0) { break; } pos--; } return ret; #endif } std::string GetFileName(const std::string& filePath) { std::string path = filePath; path = Normalize(path); auto posOpt = FindLastOfDirSplit(path); if (posOpt) { return path.substr(posOpt.value() + 1); } return path; } std::string GetQuoted(const std::string& str) { std::stringstream ss; ss << std::quoted(str); return ss.str(); } std::string GetDirName(const std::string& filePath) { std::string path = filePath; if (IsLastCharDirSplit(path)) { path.pop_back(); } if (path == "." || path == "..") { auto absolutePath = GetAbsPath(path); path = absolutePath.has_value() ? absolutePath.value() : path; } return GetFileName(path); } std::string GetDirPath(const std::string& filePath) { auto posOpt = FindLastOfDirSplit(filePath); if (posOpt) { return Normalize(filePath.substr(0, posOpt.value())); } return "."; } std::string GetFileExtension(const std::string& filePath) { auto dotPos = GetFileExtensionSeparatorPos(filePath).value_or(filePath.size()); if (dotPos < filePath.size()) { return filePath.substr(dotPos + 1); } else { return ""; } } bool HasExtension(const std::string& filePath, const std::string& extension) { return GetFileExtension(filePath) == extension; } bool HasCJDExtension(std::string_view path) { auto fileIt = path.rbegin(); auto fileEnd = path.rend(); auto extensionIt = CJ_D_FILE_EXTENSION.rbegin(); auto extensionEnd = CJ_D_FILE_EXTENSION.rend(); while (fileIt != fileEnd && extensionIt != extensionEnd) { if (*fileIt != *extensionIt) { return false; } ++fileIt; ++extensionIt; } // exclude .cj.d file with no filename return extensionIt == extensionEnd && fileIt != fileEnd; } std::optional GetFileExtensionSeparatorPos(const std::string& filePath) { auto dotOpt = FindLastOfDot(filePath); // e.g. 'out' or '.out' if (!dotOpt.has_value() || dotOpt.value() == 0) { return std::nullopt; } size_t dotPos = dotOpt.value(); auto dirSplitOpt = FindLastOfDirSplit(filePath); if (dirSplitOpt.has_value()) { auto dirSplitPos = dirSplitOpt.value(); // e.g. '/path/.out' or '.vscode/out' if ((dotPos == dirSplitPos + 1) || (dirSplitPos > dotPos)) { return std::nullopt; } } return {dotPos}; } std::string GetFileNameWithoutExtension(const std::string& filePath) { auto posOpt = FindLastOfDot(filePath); size_t dotPos = posOpt.value_or(filePath.size()); posOpt = FindLastOfDirSplit(filePath); size_t dirSplitPos = posOpt ? posOpt.value() + 1 : 0; // consider the filename starting with a dot and without extension, e.g. /path/.out or .out if (dotPos == dirSplitPos) { dotPos = filePath.size(); } return filePath.substr(dirSplitPos, dotPos - dirSplitPos); } std::string GetFileBase(const std::string& filePath) { auto dotPos = GetFileExtensionSeparatorPos(filePath).value_or(filePath.size()); return filePath.substr(0, dotPos); } std::string Normalize(const std::string& input, const bool toUnix) { std::string output = ""; output.reserve(input.size()); #ifdef _WIN32 const char slash = toUnix ? SLASH : BACKSLASH; #else (void)toUnix; const char slash = SLASH; #endif bool previousIsSlash = false; for (auto c : input) { if (previousIsSlash && IsSlash(c)) { continue; } if (c == '\0') { break; } if (IsSlash(c)) { output.push_back(slash); previousIsSlash = true; } else { output.push_back(c); previousIsSlash = false; } } return output; } bool IsDir(const std::string& filePath) { #ifdef _WIN32 DWORD dwAttr = GetFileAttributesA(filePath.c_str()); if (dwAttr == INVALID_FILE_ATTRIBUTES) { return false; } if ((dwAttr & FILE_ATTRIBUTE_DIRECTORY) != 0) { return true; } #else // If we have no read permission to the file path, we will not be able to open it for // checking whether it is a directory. Thus in such case we use stat instead of opendir. // Otherwise we will get `false` falsely on directories which have no read permissions. if (AccessWithResult(filePath, FM_READ) == AccessResultType::NO_PERMISSION) { struct stat statbuf; if (stat(filePath.c_str(), &statbuf) != 0) { return false; } return S_ISDIR(statbuf.st_mode); } DIR* dp = opendir(filePath.c_str()); if (dp != nullptr) { closedir(dp); return true; } #endif return false; } bool IsAbsolutePath(const std::string& filePath) { if (filePath.empty()) { return false; } #ifdef _WIN32 // 3 is the length of an absolute path, such as: `c:\\`. if (filePath.size() < 3) { return false; } // In an absolute path on windows: // position 0 means volume or drive letter // position 1 means volume separator `:` // Position 2 means a directory separator character `\\` or `/`. return std::isalpha(filePath[0]) && filePath[1] == ':' && filePath.find_first_of(DIR_SEPARATOR) == 2; #else return filePath.find_first_of(DIR_SEPARATOR) == 0; #endif } bool Remove(const std::string& itemPath) { #ifdef _WIN32 DWORD dwAttr = GetFileAttributesA(itemPath.c_str()); if (dwAttr == INVALID_FILE_ATTRIBUTES) { return false; } if ((dwAttr & FILE_ATTRIBUTE_READONLY) == 1 || (dwAttr & FILE_ATTRIBUTE_SYSTEM) == 1 || (dwAttr & FILE_ATTRIBUTE_DEVICE) == 1 || (dwAttr & FILE_ATTRIBUTE_VIRTUAL) == 1) { return false; } return (IsDir(itemPath) ? _rmdir(itemPath.c_str()) : remove(itemPath.c_str())) == 0; #else struct stat statBuf; if (lstat(itemPath.c_str(), &statBuf) != 0) { return false; } if (!S_ISREG(statBuf.st_mode) && !S_ISDIR(statBuf.st_mode) && !S_ISLNK(statBuf.st_mode)) { return false; } return remove(itemPath.c_str()) == 0; #endif } bool ReadBinaryFileToBuffer( const std::string& filePath, std::vector& buffer, std::string& failedReason) { failedReason.clear(); std::ifstream is(filePath, std::ifstream::in | std::ifstream::binary); if (!is.is_open()) { failedReason = "open file failed"; return false; } size_t fileLength = GetFileSize(filePath); if (fileLength == 0) { failedReason = "empty binary file"; } if (fileLength >= FILE_LEN_LIMIT) { failedReason = "exceed the max file length: 4 GB"; } if (!failedReason.empty()) { is.close(); return false; } buffer.resize(fileLength); is.read(reinterpret_cast(buffer.data()), static_cast(fileLength)); size_t realLen = static_cast(is.gcount()); if (is.bad()) { failedReason = "only " + std::to_string(realLen) + " could be read"; } else if (is.eof() || realLen < fileLength) { buffer.resize(realLen); } is.close(); return failedReason.empty(); } bool WriteToFile(const std::string& filePath, const std::string& data) { if (!FileExist(filePath)) { if (CreateDirs(filePath) != 0) { Errorln("Failed to create file " + filePath); return false; } } std::string normalizedPath = Normalize(filePath); std::ofstream file(normalizedPath); if (!file.is_open()) { Errorln("Failed to open file " + normalizedPath); return false; } file << data; bool didSucceed = !file.fail(); if (!didSucceed) { Errorln("Failed to write to file " + normalizedPath); } file.close(); return didSucceed; } bool WriteBufferToASTFile(const std::string& filePath, const std::vector& buffer) { std::string baseDir = GetDirPath(filePath); if (!FileExist(baseDir)) { if (CreateDirs(baseDir + "/") != 0) { return false; } } std::ofstream outStream(filePath, std::ofstream::out | std::ofstream::binary); if (!outStream.is_open()) { return false; } auto length = buffer.size(); outStream.write(reinterpret_cast(buffer.data()), static_cast(length)); bool success = !outStream.fail(); outStream.close(); return success; } std::optional ReadFileContent(const std::string& filePath, std::string& failedReason) { failedReason.clear(); if (!FileExist(filePath)) { failedReason = "file not exist"; return std::nullopt; } std::ifstream is(filePath, std::ios::in | std::ios::binary); if (!is) { failedReason = "open file failed"; return std::nullopt; } size_t len = GetFileSize(filePath); if (len >= FILE_LEN_LIMIT) { failedReason = "exceed the max file length: 4 GB"; is.close(); return std::nullopt; } std::string content; // Check BOM UTF-8 EF BB BF. if (len >= BOM_ENCODING_LEN) { content.resize(BOM_ENCODING_LEN); is.read(content.data(), BOM_ENCODING_LEN); // 0, 1, and 2 respectively indicate the three bytes of the byte order mark (BOM) in UTF-8. if (content[0] == '\xEF' && content[1] == '\xBB' && content[2] == '\xBF') { is.seekg(BOM_ENCODING_LEN, std::ifstream::beg); len -= BOM_ENCODING_LEN; } else { is.seekg(0, std::ifstream::beg); } } content.resize(len); is.read(content.data(), static_cast(len)); size_t realLen = static_cast(is.gcount()); if (is.bad()) { failedReason = "only " + std::to_string(realLen) + " could be read"; } else if (is.eof() || realLen < len) { content.resize(realLen); } is.close(); if (!failedReason.empty()) { return std::nullopt; } return content; } std::string AppendExtension(const std::string& file, const std::string& backup, const std::string& extension) { std::string result = GetFileBase(file); return (result.empty() ? (backup + extension) : (result + extension)); } bool IsAbsolutePathAboveLengthLimit(const std::string& path) { if (path.length() > PATH_MAX) { return true; } #ifdef _WIN32 char resolvedPath[PATH_MAX] = {0}; int retval = GetFullPathNameA(path.c_str(), PATH_MAX, resolvedPath, NULL); // Error code 206 (ERROR_FILENAME_EXCED_RANGE) indicates that the filename or extension is too long. if (retval == 0 && GetLastError() == ERROR_FILENAME_EXCED_RANGE) { return true; } #endif return false; } std::optional GetAbsPath(const std::string& path) { if (path.length() > PATH_MAX) { return {}; } char resolvedPath[PATH_MAX] = {0}; #ifdef _WIN32 int retval = GetFullPathNameA(path.c_str(), PATH_MAX, resolvedPath, NULL); if (retval == 0) { return {}; } if (!FileExist(resolvedPath)) { return {}; } #else if (!realpath(path.c_str(), resolvedPath)) { return {}; } #endif return Normalize(std::string(resolvedPath)); } std::optional GetRelativePath(const std::string& basePath, const std::string& path) { if (basePath.empty() || path.empty()) { return {}; } std::string::size_type lastDelimPos = 0; std::string::size_type pos = 0; for (; pos != basePath.length() && pos != path.length() && basePath[pos] == path[pos]; ++pos) { if (IsSlash(basePath[pos])) { lastDelimPos = pos; } } #ifdef _WIN32 // The different volume case. The behavior is consistent with Windows' `PathRelativePathTo` API. if (lastDelimPos == 0 && !IsSlash(path[0])) { return {}; } #endif size_t parentCount = static_cast( std::count_if(basePath.begin() + static_cast(lastDelimPos + 1), basePath.end(), IsSlash)); size_t suffixLength = (path.length() - lastDelimPos) - 1; // Special handling for pure parent directory if (pos == path.length() && !IsSlash(path.back())) { // To avoid integer overflow when parentCount == 0. parentCount = (parentCount == 0) ? 0 : (parentCount - 1); suffixLength = 0; } std::string result; if (parentCount == 0) { // 2 is the length of `"./"` result.reserve(suffixLength + 2); result += "./"; } else { // 3 is the length of `"../"` result.reserve(parentCount * 3 + suffixLength); for (size_t i = 0; i < parentCount; ++i) { result += "../"; } } result.append(path, lastDelimPos + 1, suffixLength); return Normalize(result); } std::vector GetAllDirsUnderCurrentPath(const std::string& path) { std::string root = Normalize(path); std::vector allDirs; #ifdef _WIN32 GetAllDirsUnderCurrentPath(root, allDirs); #else std::queue pathQueue; pathQueue.push(root); while (!pathQueue.empty()) { auto tmpPath = pathQueue.front(); pathQueue.pop(); DIR* dir = opendir(tmpPath.c_str()); if (!dir) { return allDirs; } for (auto entry = readdir(dir); entry != nullptr; entry = readdir(dir)) { if (entry->d_type != DT_DIR) { continue; } auto dirName = std::string(entry->d_name); // Not include "." and "..". if (dirName != "." && dirName != "..") { allDirs.push_back(JoinPath(tmpPath, dirName)); pathQueue.push(JoinPath(tmpPath, dirName)); } } closedir(dir); } #endif return allDirs; } size_t GetFileSize(const std::string& filePath) { // `tellg` does not report the size of the file. // Though it usually works, it may fail. #ifdef _WIN32 struct __stat64 statBuf; int rc = _stat64(filePath.c_str(), &statBuf); return rc == 0 ? static_cast(statBuf.st_size) : 0; #else struct stat statBuf; int rc = stat(filePath.c_str(), &statBuf); return rc == 0 ? static_cast(statBuf.st_size) : 0; #endif } inline bool AddFileIfNeeded( const std::string& fileName, std::vector& allFiles, bool shouldSkipTestFiles, bool shouldSkipRegularFiles) { auto findTestSuffix = fileName.rfind(TEST_FILE_NAME); // Not compile test, and file is ending with '_test.cj'. if (shouldSkipTestFiles && findTestSuffix != std::string::npos && fileName.size() - findTestSuffix == TEST_FILE_NAME.size()) { return false; } else if (shouldSkipRegularFiles && findTestSuffix == std::string::npos) { return false; } allFiles.emplace_back(fileName); return true; } #ifdef _WIN32 void GetAllDirsUnderCurrentPath(const std::string& dir, std::vector& allFiles) { HANDLE hFind; WIN32_FIND_DATA findData; std::string dirNew = JoinPath(dir, "*.*"); hFind = FindFirstFileA(dirNew.c_str(), &findData); do { if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && strcmp(findData.cFileName, "") != 0 && strcmp(findData.cFileName, ".") != 0 && strcmp(findData.cFileName, "..") != 0) { dirNew = JoinPath(dir, std::string(findData.cFileName)); allFiles.push_back(dirNew); GetAllDirsUnderCurrentPath(dirNew, allFiles); } } while (FindNextFile(hFind, &findData)); FindClose(hFind); } std::vector GetAllFilesUnderCurrentPath( const std::string& path, const std::string& extension, bool shouldSkipTestFiles, bool shouldSkipRegularFiles) { std::vector allFiles; HANDLE hFind; WIN32_FIND_DATAA findData; std::string dirNew = JoinPath(path, "*.*"); hFind = FindFirstFileA(dirNew.c_str(), &findData); do { auto fileName = std::string(findData.cFileName); bool isFile = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 && strcmp(findData.cFileName, ".") != 0 && HasExtension(fileName, extension); if (!isFile) { continue; } if (!AddFileIfNeeded(fileName, allFiles, shouldSkipTestFiles, shouldSkipRegularFiles)) { continue; } } while (FindNextFileA(hFind, &findData)); FindClose(hFind); return allFiles; } std::vector GetDirectories(const std::string& path) { std::vector directories; HANDLE hFind; WIN32_FIND_DATAA findData; std::string dirNew = JoinPath(path, "*"); hFind = FindFirstFileA(dirNew.c_str(), &findData); do { if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && strcmp(findData.cFileName, "") != 0 && strcmp(findData.cFileName, ".") != 0 && strcmp(findData.cFileName, "..") != 0) { Directory d; d.name = findData.cFileName; d.path = JoinPath(path, std::string(findData.cFileName)); directories.emplace_back(d); } } while (FindNextFileA(hFind, &findData)); FindClose(hFind); return directories; } #else std::vector GetAllFilesUnderCurrentPath( const std::string& path, const std::string& extension, bool shouldSkipTestFiles, bool shouldSkipRegularFiles) { std::vector allFiles; DIR* dir = opendir(path.c_str()); if (!dir) { return allFiles; } for (auto entry = readdir(dir); entry != nullptr; entry = readdir(dir)) { if (entry->d_type != DT_REG) { continue; } // File name cannot start with '.'. std::string fileName(entry->d_name); if (fileName[0] == '.' || !HasExtension(fileName, extension)) { continue; } if (!AddFileIfNeeded(fileName, allFiles, shouldSkipTestFiles, shouldSkipRegularFiles)) { continue; } } closedir(dir); return allFiles; } std::vector GetDirectories(const std::string& path) { std::vector directories; struct dirent* entry; DIR* dir = opendir(path.c_str()); if (dir) { while ((entry = readdir(dir)) != nullptr) { // get directories and symbolic links (they may refer to directories) only // In the future: we may need to check if symbolic links are referring to dir or not if (entry->d_type != DT_DIR && entry->d_type != DT_LNK) { continue; } // "." and ".." are also directories, but we get directories in 'path' only if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } Directory d; d.name = entry->d_name; d.path = JoinPath(path, entry->d_name); (void)directories.emplace_back(d); } closedir(dir); } return directories; } #endif std::string JoinPath(const std::string& base, const std::string& append) { std::string joinedPath = base; if (base.find_last_of(DIR_SEPARATOR) != base.size() - 1) { joinedPath += DIR_SEPARATOR[0]; } joinedPath += append; return Normalize(joinedPath); } std::string GetShortHash(uint64_t fullHash) { std::string hash = std::to_string(fullHash); return hash.size() <= SHORT_HASH_NUM ? hash : hash.substr(0, SHORT_HASH_NUM); } std::vector SplitStr(const std::string& str, const char del) { std::vector res; size_t slow = 0; size_t fast = 0; size_t len = str.size(); while (slow < len && fast < len) { if (str[fast] == del && str[slow] == del) { slow = ++fast; } else if (str[fast] != del && str[slow] != del) { fast++; } else if (str[fast] == del && str[slow] != del) { res.emplace_back(str.substr(slow, fast - slow)); slow = fast; } } if (slow != fast) { res.emplace_back(str.substr(slow, fast)); } return res; } std::string IdenticalFunc(const std::string& str) { return str; } std::string GetPkgNameFromRelativePath(const std::string& path) { std::string packageName = Normalize(path); size_t pos = 0; // The path containing '../' is not supported, and return "default". if (packageName.find(UPPERLEVEL_PATH) != std::string::npos) { return DEFAULT_PACKAGE_NAME; } while ((pos = packageName.find(CURRENT_PATH, pos)) != std::string::npos) { packageName.replace(pos, CURRENT_PATH.size(), ""); } pos = 0; while ((pos = packageName.find_first_of(DIR_SEPARATOR, pos)) != std::string::npos) { // 1 is size of "\\" or "/". packageName.replace(pos, 1, "."); pos += 1; } while (!packageName.empty() && packageName.back() == '.') { packageName.pop_back(); } return packageName.empty() ? DEFAULT_PACKAGE_NAME : packageName; } namespace { inline bool IsKeyword(const std::string& str) { static std::unordered_set keywordsSet{}; if (keywordsSet.empty()) { for (unsigned char i = 0; i < static_cast(TokenKind::IDENTIFIER); i++) { keywordsSet.emplace(std::string(TOKENS[i])); } keywordsSet.emplace("true"); keywordsSet.emplace("false"); } return keywordsSet.count(str) != 0; } } // namespace using namespace Unicode; bool IsIdentifier(const std::string& str, bool useContextualKeyWords) { if (str.empty()) { return false; } auto start{reinterpret_cast(str.data())}; auto end{reinterpret_cast(str.data() + str.size())}; if (str[0] == BACKTICK) { ++start; --end; // '3' means original id can't be empty. if ((str.size() < 3 || *(str.end() - 1) != BACKTICK)) { return false; } } bool isFirst{true}; UTF32 codepoint; UTF32* targetEnd{&codepoint + 1}; while (start < end) { UTF32* targetStart{&codepoint}; auto cr = ConvertUTF8toUTF32(&start, end, &targetStart, targetEnd); if (cr != ConversionResult::OK) { return false; } if (isFirst) { if (!IsASCIIIdStart(codepoint) && !IsCJXIDStart(codepoint)) { return false; } } else if (!IsASCIIIdContinue(codepoint) && !IsXIDContinue(codepoint)) { return false; } isFirst = false; } if (IsKeyword(str)) { if (useContextualKeyWords && IsContextualKeyword(str)) { return true; } return false; } return true; } std::string NormalizePath(const std::string& path) { if (path.empty()) { return path; } if (GetDirPath(path) == ".") { return JoinPath(".", GetFileName(path)); } return Normalize(path); } std::string ConvertPackageNameToLibCangjieBaseFormat(const std::string& fullPackageName) { if (auto found = STANDARD_LIBS.find(fullPackageName); found != STANDARD_LIBS.end()) { auto names = Utils::SplitQualifiedName(fullPackageName); if (names.size() == 1) { return "cangjie-" + found->second; } auto moduleName = names.front(); return "cangjie-" + moduleName + "-" + found->second; } return {}; } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND std::string ConvertFilenameToLibCangjieBaseFormat(const std::string& objectFile) { auto fullPackageName = GetFileNameWithoutExtension(objectFile); return ConvertPackageNameToLibCangjieBaseFormat(fullPackageName); } std::string ConvertFilenameToLibCangjieFormat( const std::string& objectFile, const std::string& extension) { auto fullPackageName = GetFileNameWithoutExtension(objectFile); auto base = ConvertPackageNameToLibCangjieBaseFormat(fullPackageName); return base.empty() ? std::string() : "lib" + base + extension; } #endif std::string ConvertFilenameToLtoLibCangjieFormat(const std::string& objectFile) { auto dotOptional = FindLastOfDot(objectFile); auto dirSplitOptional = FindLastOfDirSplit(objectFile); size_t dotPos = dotOptional.value_or(objectFile.size()); size_t dirSplitPos = dirSplitOptional.value_or(0); std::string path = objectFile.substr(0, dirSplitPos); std::string libName = objectFile.substr(dirSplitPos + 1, dotPos - (dirSplitPos + 1)); std::stringstream ss; ss << path << DIR_SEPARATOR << "lib" << libName; ss << ".bc"; return ss.str(); } AccessResultType AccessWithResult(const std::string& path, FileMode mode) { #ifdef _WIN32 return Access(path, mode) ? AccessResultType::OK : AccessResultType::FAILED_WITH_UNKNOWN_REASON; #else if (access(path.c_str(), static_cast(mode)) == 0) { return AccessResultType::OK; } if (errno == ENOENT) { return AccessResultType::NOT_EXIST; } else if (errno == EACCES) { return AccessResultType::NO_PERMISSION; } return AccessResultType::FAILED_WITH_UNKNOWN_REASON; #endif } bool Access(const std::string& path, FileMode mode) { #ifdef _WIN32 return _access(path.c_str(), static_cast(mode)) == 0; #else return access(path.c_str(), static_cast(mode)) == 0; #endif } bool FileExist(const std::string& path, [[maybe_unused]] bool caseSensitive) { #ifdef _WIN32 if (!Access(path, FM_EXIST)) { return false; } if (!caseSensitive) { return true; } std::string pathWithoutSep = path; if (auto pos = path.find_last_not_of(DIR_SEPARATOR); pos != std::string::npos) { pathWithoutSep = path.substr(0, pos + 1); } const std::string filename = GetFileName(pathWithoutSep); HANDLE hFind; WIN32_FIND_DATAA findData; std::string dirNew = JoinPath(GetDirPath(pathWithoutSep), "*.*"); hFind = FindFirstFileA(dirNew.c_str(), &findData); do { if (filename == findData.cFileName) { FindClose(hFind); return true; } } while (FindNextFileA(hFind, &findData)); FindClose(hFind); return false; #else return Access(path, FM_EXIST); #endif } std::optional FindFileByName( const std::string& fileName, const std::vector& searchPaths, bool caseSensitive) { if (fileName.empty()) { return std::nullopt; } for (const auto& path : searchPaths) { if (path.empty()) { continue; } auto filepath = JoinPath(path, fileName); if (FileExist(filepath, caseSensitive)) { return {filepath}; } } return std::nullopt; } std::string FindProgramByName(const std::string& name, const std::vector& paths) { if (name.empty()) { return ""; } if (HasDirSplit(name)) { return name; } std::string exePath; for (const auto& path : paths) { if (path.empty()) { continue; } exePath = AppendPath(path, name); if (CanExecute(exePath)) { return exePath; } } return ""; } int32_t CreateDirs(const std::string& directoryPath) { size_t dirPathLen = directoryPath.length(); if (dirPathLen > DIR_PATH_MAX_LENGTH) { std::string msg = "Failed to create directory: " + directoryPath + "\nthe path cannot be longer than " + std::to_string(DIR_PATH_MAX_LENGTH) + " characters."; Errorln(msg); return -1; } char tmpDirPath[DIR_PATH_MAX_LENGTH + 1] = {0}; size_t fileNameStartIdx = 0; for (size_t i = 0; i < dirPathLen; ++i) { tmpDirPath[i] = directoryPath[i]; #ifdef _WIN32 if (!Utils::In(tmpDirPath[i], {BACKSLASH, SLASH})) { #elif defined(__linux__) || defined(__APPLE__) if (tmpDirPath[i] != SLASH) { #endif continue; } if (i - fileNameStartIdx > FILE_NAME_MAX_LENGTH) { std::string msg = "Failed to create directory: " + std::string(tmpDirPath) + "\nthe directory name cannot be longer than " + std::to_string(FILE_NAME_MAX_LENGTH) + " characters."; Errorln(msg); return -1; } fileNameStartIdx = i + 1; // current 'i' is slash, so file name start index is next char if (Access(tmpDirPath, FM_EXIST)) { continue; } // The path may be created by another thread or process after the first access before reaching this line. // In that case, the path already exists and cannot be successfully created. As the required folder is created // anyway in this case, do not return a failure exit code by checking the existence of the folder again. int32_t ret = Mkdir(tmpDirPath); if (ret != 0 && !Access(tmpDirPath, FM_EXIST)) { return ret; } } return 0; } std::string FindSerializationFile( const std::string& fullPackageName, const std::string& suffix, const std::vector& searchPaths) { auto names = Utils::SplitQualifiedName(fullPackageName); auto fileName = ToCjoFileName(names.front()) + DIR_SEPARATOR + fullPackageName; auto filePath = FileUtil::FindFileByName(fileName + suffix, searchPaths, true).value_or(""); if (filePath.empty()) { // TEMPORARILY, find file in direct path. return FileUtil::FindFileByName(fullPackageName + suffix, searchPaths, true).value_or(""); } return filePath; } std::vector SplitEnvironmentPaths(const std::string& pathsString) { #ifdef _WIN32 const std::string separator = ";"; #else const std::string separator = ":"; #endif auto strings = Utils::SplitString(pathsString, separator); std::vector paths; for (const std::string& s : strings) { // remove empty strings from paths. if (s.empty()) { continue; } auto maybePath = GetAbsPath(s); if (maybePath.has_value()) { paths.emplace_back(maybePath.value()); } } return paths; } bool CanWrite(const std::string& path) { #ifdef _WIN32 std::optional tempPath = StringConvertor::StringToWString(path); if (!tempPath.has_value()) { return false; } std::wstring wPath = tempPath.value(); WIN32_FILE_ATTRIBUTE_DATA wfad; BOOL res = GetFileAttributesExW(wPath.c_str(), GetFileExInfoStandard, &wfad); if (!res) { return false; } return (wfad.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != FILE_ATTRIBUTE_READONLY; #else return access(path.c_str(), W_OK) == 0; #endif } std::string RemovePathPrefix(const std::string& absolutePath, const std::vector& removedPathPrefix) { // for the path: aa/bb/cc.cj, the absolutePath is "aa/bb", and the last "/" is need to be added. #ifdef _WIN32 char split = '\\'; std::string res = absolutePath + split; std::string tempRes{}; tempRes.reserve(res.size()); std::transform(res.begin(), res.end(), std::back_inserter(tempRes), &tolower); for (auto prefix : removedPathPrefix) { std::transform(prefix.begin(), prefix.end(), prefix.begin(), &tolower); auto pos = tempRes.find(prefix); #else char split = '/'; std::string res = absolutePath + split; for (auto& prefix : removedPathPrefix) { auto pos = res.find(prefix); #endif // when pos is 0, it means the path starts with the prefix. if (pos == 0) { res = res.substr(prefix.length()); if (res.empty()) { return res; } res = res[0] == split ? res.substr(1) : res; break; } } // remove the last symbol. return res.empty() ? res : res.substr(0, res.size() - 1); } #ifdef _WIN32 void HideFile(const std::string& path) { (void)SetFileAttributesA(path.c_str(), FILE_ATTRIBUTE_HIDDEN | GetFileAttributesA(path.c_str())); } #endif std::string ToCjoFileName(std::string_view fullPackageName) { std::string ret{}; size_t i{0}; for (; i < fullPackageName.size(); ++i) { // replace first :: with # as organization name separator if (fullPackageName[i] == ':' && i + 1 < fullPackageName.size() && fullPackageName[i + 1] == ':') { ret += ORG_NAME_SEPARATOR; i += strlen(TOKENS[static_cast(TokenKind::DOUBLE_COLON)]); break; } if (fullPackageName[i] == '.') { break; } ret.push_back(fullPackageName[i]); } if (i < fullPackageName.size()) { ret.insert(ret.end(), fullPackageName.begin() + i, fullPackageName.end()); } return ret; } std::string ToPackageName(std::string_view cjoName) { auto it = cjoName.find(ORG_NAME_SEPARATOR); if (it != std::string::npos) { return std::string{cjoName.substr(0, it)} + TOKENS[static_cast(TokenKind::DOUBLE_COLON)] + std::string{cjoName.substr(it + 1)}; } return std::string{cjoName}; } } // namespace Cangjie::FileUtil cangjie_compiler-1.0.7/src/Utils/FloatFormat.cpp000066400000000000000000000045611510705540100216260ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements float format related apis. */ #include "cangjie/Utils/FloatFormat.h" #include #include namespace { constexpr uint32_t FLOAT32_SIGN_MASK = 0x80000000; constexpr uint32_t FLOAT32_EXP_MASK = 0x7F800000; constexpr uint32_t FLOAT32_TAIL_MASK = 0x007FFFFF; constexpr uint32_t FLOAT32_EXP_BASE = 0b01111111; constexpr uint32_t FLOAT32_TAIL_WIDTH = 23; constexpr uint16_t FLOAT16_SIGN_MASK = 0x8000; constexpr uint16_t FLOAT16_EXP_BASE = 0b01111; constexpr uint16_t FLOAT16_EXP_MAX = 0b11110; constexpr uint16_t FLOAT16_TAIL_WIDTH = 10; constexpr uint16_t FLOAT16_INF = 0b11111 << FLOAT16_TAIL_WIDTH; constexpr uint16_t FLOAT32_FLOAT16_TAIL_OFFSET = FLOAT32_TAIL_WIDTH - FLOAT16_TAIL_WIDTH; } namespace Cangjie::FloatFormat { uint16_t Float32ToFloat16(float value) { uint16_t result = 0; // We need the original bit field of float, thus we can not use `static_cast` here. uint32_t bit32 = *reinterpret_cast(&value); int32_t exp = static_cast(((bit32 & FLOAT32_EXP_MASK) >> FLOAT32_TAIL_WIDTH) - FLOAT32_EXP_BASE + FLOAT16_EXP_BASE); // exponent if (exp > FLOAT16_EXP_MAX) { // inf result = FLOAT16_INF; } else if (exp <= 0) { // subnormal result = static_cast(((bit32 & FLOAT32_TAIL_MASK) | (1 << FLOAT32_TAIL_WIDTH)) >> (static_cast(FLOAT32_FLOAT16_TAIL_OFFSET + (1 - exp)))); } else { // normal result = static_cast(((bit32 & FLOAT32_TAIL_MASK) >> FLOAT32_FLOAT16_TAIL_OFFSET) | static_cast(static_cast(exp) << FLOAT16_TAIL_WIDTH)); } if ((bit32 & FLOAT32_SIGN_MASK) != 0) { result |= FLOAT16_SIGN_MASK; } return result; } bool IsUnderFlowFloat(const std::string& literal) { std::stringstream ss(literal); double value; // If the string is too large or too small to be represented by C++, the value obtained through ss is either a value // greater than 1 or less than 1. ss >> value; if (std::fabs(value) < 1.0) { return true; } return false; } } // namespace Cangjie::FloatFormatcangjie_compiler-1.0.7/src/Utils/ICEUtil.cpp000066400000000000000000000041141510705540100206400ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements ICE related variables and functions. */ #include "cangjie/Utils/ICEUtil.h" #include "cangjie/Basic/Version.h" #include "cangjie/Driver/TempFileManager.h" #include "cangjie/Frontend/CompilerInstance.h" namespace { using namespace Cangjie; std::atomic g_writeOnceICEMessag(false); } // namespace namespace Cangjie { namespace ICE { int64_t TriggerPointSetter::triggerPoint = static_cast(Cangjie::CompileStage::COMPILE_STAGE_NUMBER); int64_t TriggerPointSetter::interpreterTP = static_cast(Cangjie::CompileStage::COMPILE_STAGE_NUMBER) + 1; int64_t TriggerPointSetter::writeCahedTP = static_cast(Cangjie::CompileStage::COMPILE_STAGE_NUMBER) + 2; void TriggerPointSetter::SetICETriggerPoint(CompileStage cs) { if (TriggerPointSetter::triggerPoint == Cangjie::ICE::LSP_TP) { return; } if (cs >= CompileStage::COMPILE_STAGE_NUMBER) { TriggerPointSetter::triggerPoint = static_cast(CompileStage::COMPILE_STAGE_NUMBER); } else { TriggerPointSetter::triggerPoint = static_cast(cs); } } void TriggerPointSetter::SetICETriggerPoint(int64_t tp) { if (tp == Cangjie::ICE::LSP_TP) { TriggerPointSetter::triggerPoint = tp; return; } if (tp == FRONTEND_TP) { TriggerPointSetter::triggerPoint = static_cast(CompileStage::COMPILE_STAGE_NUMBER); } else { TriggerPointSetter::triggerPoint = tp; } } int64_t GetTriggerPoint() { return TriggerPointSetter::triggerPoint; } bool CanWriteOnceICEMessage() { return !g_writeOnceICEMessag.exchange(true); } void PrintVersionFromError() { std::cerr << CANGJIE_COMPILER_VERSION << std::endl; } void RemoveTempFile() { Cangjie::TempFileManager::Instance().DeleteTempFiles(); } } // namespace ICE } // namespace Cangjie cangjie_compiler-1.0.7/src/Utils/ProfileRecorder.cpp000066400000000000000000000071621510705540100224760ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/Utils/ProfileRecorder.h" #include "UserBase.h" #include "UserCodeInfo.h" #include "UserMemoryUsage.h" #include "UserTimer.h" using namespace Cangjie; using namespace Cangjie::Utils; namespace { inline bool operator&(ProfileRecorder::Type a, ProfileRecorder::Type b) { return static_cast(static_cast(a) & static_cast(b)); } } // namespace ProfileRecorder::ProfileRecorder( const std::string& title, const std::string& subtitle, const std::string& desc) : title_(title), subtitle_(subtitle), desc_(desc) { Start(title, subtitle, desc); } ProfileRecorder::~ProfileRecorder() { #ifndef CANGJIE_ENABLE_GCOV try { #endif Stop(title_, subtitle_, desc_); #ifndef CANGJIE_ENABLE_GCOV } catch (...) { // The try-catch block is placed here as the used functions above do not declare with noexcept. // We shall avoid any exception occurring in the destruction function. } #endif } void ProfileRecorder::SetPackageName(const std::string& name) { UserTimer::Instance().SetPackageName(name); UserMemoryUsage::Instance().SetPackageName(name); UserCodeInfo::Instance().SetPackageName(name); } void ProfileRecorder::SetOutputDir(const std::string& path) { UserTimer::Instance().SetOutputDir(path); UserMemoryUsage::Instance().SetOutputDir(path); UserCodeInfo::Instance().SetOutputDir(path); } void ProfileRecorder::Start( const std::string& title, const std::string& subtitle, const std::string& desc) { if (UserTimer::Instance().IsEnable()) { UserTimer::Instance().Start(title, subtitle, desc); } if (UserMemoryUsage::Instance().IsEnable()) { UserMemoryUsage::Instance().Start(title, subtitle, desc); } } void ProfileRecorder::Stop( const std::string& title, const std::string& subtitle, const std::string& desc) { if (UserTimer::Instance().IsEnable()) { UserTimer::Instance().Stop(title, subtitle, desc); } if (UserMemoryUsage::Instance().IsEnable()) { UserMemoryUsage::Instance().Stop(title, subtitle, desc); } } void ProfileRecorder::RecordCodeInfo(const std::string& item, const std::function& getData) { if (UserTimer::Instance().IsEnable() || UserMemoryUsage::Instance().IsEnable()) { UserCodeInfo::Instance().RecordInfo(item, getData()); } } void ProfileRecorder::RecordCodeInfo(const std::string& item, int64_t value) { UserCodeInfo::Instance().RecordInfo(item, value); } void ProfileRecorder::Enable(bool en, const Type& type) { if (type & ProfileRecorder::Type::TIMER) { UserTimer::Instance().Enable(en); } if (type & ProfileRecorder::Type::MEMORY) { UserMemoryUsage::Instance().Enable(en); } if (UserTimer::Instance().IsEnable() || UserMemoryUsage::Instance().IsEnable()) { UserCodeInfo::Instance().Enable(true); } else { UserCodeInfo::Instance().Enable(false); } } std::string ProfileRecorder::GetResult(const Type& type) { std::string result; if ((type & ProfileRecorder::Type::TIMER) || (type & ProfileRecorder::Type::MEMORY)) { result += UserCodeInfo::Instance().GetResult(); } if (type & ProfileRecorder::Type::TIMER) { result += UserTimer::Instance().GetResult(); } if (type & ProfileRecorder::Type::MEMORY) { result += UserMemoryUsage::Instance().GetResult(); } return result; } cangjie_compiler-1.0.7/src/Utils/SafePointer.cpp000066400000000000000000000007251510705540100216250ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/Utils/SafePointer.h" #include "cangjie/Utils/ICEUtil.h" #ifndef CANGJIE_ENABLE_GCOV NullPointerException::NullPointerException() : triggerPoint(Cangjie::ICE::GetTriggerPoint()) { } #endif cangjie_compiler-1.0.7/src/Utils/Semaphore.cpp000066400000000000000000000022041510705540100213230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements Semaphore class and its methods. */ #include "cangjie/Utils/Semaphore.h" using namespace Cangjie::Utils; Semaphore::Semaphore() { auto numCores = std::thread::hardware_concurrency(); // Leave 2 cores to avoid competing with user's other tasks. count = numCores > 2 ? numCores - 2 : 1; } Semaphore& Semaphore::Get() { static Semaphore sem = Semaphore(); return sem; } void Semaphore::Release() { std::unique_lock lock(mtx); count++; cv.notify_one(); } void Semaphore::Acquire() { std::unique_lock lock(mtx); cv.wait(lock, [this] { return count > 0; }); count--; } void Semaphore::SetCount(std::size_t newCount) { std::unique_lock lock(mtx); count = newCount; } std::size_t Semaphore::GetCount() { std::unique_lock lock(mtx); return count; }cangjie_compiler-1.0.7/src/Utils/SignalUnix.cpp000066400000000000000000000054161510705540100214710ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements functions related to the Unix crash signal handler. */ #include "cangjie/Driver/TempFileManager.h" #include "cangjie/Macro/InvokeUtil.h" #if (defined RELEASE) #include "SignalUtil.h" namespace { // standby stack size after stack overflow constexpr size_t SIGNAL_STACK_SIZE = 8192; constexpr size_t SIGNAL_NUM = 6; // We treat following signals as crashes. constexpr int SIGNALS[SIGNAL_NUM] = {SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP}; const std::vector SIGNALS_STR = {"SIGABRT", "SIGBUS", "SIGFPE", "SIGILL", "SIGSEGV", "SIGTRAP"}; void SignalHandler(int signum, [[maybe_unused]] siginfo_t* si, [[maybe_unused]] void* arg) { Cangjie::Signal::ConcurrentSynchronousSignalHandler(signum); } } // namespace // Save old alternate stack. static stack_t g_oldAltStack; // Alternative signal stack in case stack overflow happened. __attribute__((__used__)) static char g_altStackPointer[SIGNAL_STACK_SIZE] = {0}; namespace Cangjie { void CreateAltSignalStack() { // If there is an existing alternate signal stack, we do not introduce new one. if (sigaltstack(nullptr, &g_oldAltStack) != 0 || (g_oldAltStack.ss_flags & SS_ONSTACK) != 0 || (g_oldAltStack.ss_sp && g_oldAltStack.ss_size >= SIGNAL_STACK_SIZE)) { return; } stack_t sigstack; sigstack.ss_sp = static_cast(g_altStackPointer); sigstack.ss_size = SIGNAL_STACK_SIZE; sigstack.ss_flags = 0; if (sigaltstack(&sigstack, &g_oldAltStack) != 0) { InternalError("Failed to create a backup stack for the thread."); } } void RegisterCrashSignalHandler() { struct sigaction sa; sa.sa_sigaction = SignalHandler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_ONSTACK; for (auto& sig : SIGNALS) { if (sigaction(sig, &sa, nullptr) == -1) { // Even if sigaction failed, we are still able to continue our compiling. continue; } } } } // namespace Cangjie #else #include #endif // (defined NDEBUG) namespace { void SigintHandler(int signum, [[maybe_unused]] siginfo_t* si, [[maybe_unused]] void* arg) { Cangjie::TempFileManager::Instance().DeleteTempFiles(); _exit(signum + 128); // Add 128 to return the same error code as if the program crashed. } } // namespace namespace Cangjie { void RegisterCrtlCSignalHandler() { struct sigaction sa; sa.sa_sigaction = SigintHandler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_ONSTACK; (void)sigaction(SIGINT, &sa, nullptr); } } // namespace Cangjie cangjie_compiler-1.0.7/src/Utils/SignalUtil.cpp000066400000000000000000000107211510705540100214560ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements crash signal handler related functions. */ #if (defined RELEASE) #include "SignalUtil.h" #include #include "cangjie/Basic/Version.h" #include "cangjie/Driver/TempFileManager.h" namespace { #ifdef CANGJIE_BUILD_TESTS // The file handle of output to stderror. // In the case of a signal test, it could be a real file handle. int g_errorFd = STDERR_FILENO; // void (*)(int) test callback function pointer Cangjie::SignalTest::SignalTestCallbackFuncType g_signalTestCallbackFunction = nullptr; // The test callback function is not executed by default. Cangjie::SignalTest::TriggerPointer g_signalTestCallbackFunctionTriggerPoint = Cangjie::SignalTest::TriggerPointer::NON_POINTER; #else // The file handle of output to stderror constexpr int g_errorFd = STDERR_FILENO; #endif std::atomic g_processingSignalOrException(false); constexpr size_t LOOP_SIZE = 100000; void AsyncSigSafeReverse(char str[]) { for (size_t i = 0, j = strlen(str) - 1; i < j; i++, j--) { char c = str[i]; str[i] = str[j]; str[j] = c; } } /* Convert int to base 10 string (from K&R) */ void AsyncSigSafeItoa(int64_t num, char str[]) { bool neg = num < 0; if (neg) { num = -num; } int64_t c = 0; size_t i = 0; const int64_t base = 10; // decimal do { c = num % base; str[i++] = static_cast((c < base) ? (c + static_cast('0')) : ((c - base) + static_cast('a'))); } while ((num /= base) > 0); if (neg) { str[i++] = '-'; } str[i] = '\0'; AsyncSigSafeReverse(str); } ssize_t AsyncSigSafeWriteToError(const char str[]) { return write(g_errorFd, str, strlen(str)); } ssize_t AsyncSigSafePut(int64_t num) /* Put int */ { char str[128] = {0}; AsyncSigSafeItoa(num, str); /* Based on K&R itoa() */ return AsyncSigSafeWriteToError(str); } #ifdef CANGJIE_BUILD_TESTS void CloseTempFileHandle() { if (g_errorFd != STDERR_FILENO) { close(g_errorFd); g_errorFd = STDERR_FILENO; } } #endif } // namespace using namespace Cangjie; void Signal::WriteICEMessage(int64_t errorCode) { (void)AsyncSigSafeWriteToError(Cangjie::CANGJIE_COMPILER_VERSION.c_str()); (void)AsyncSigSafeWriteToError("\n"); (void)AsyncSigSafeWriteToError(Cangjie::ICE::MSG_PART_ONE.c_str()); (void)AsyncSigSafeWriteToError(Cangjie::SIGNAL_MSG_PART_ONE.c_str()); (void)AsyncSigSafePut(errorCode); (void)AsyncSigSafeWriteToError(Cangjie::SIGNAL_MSG_PART_TWO.c_str()); (void)AsyncSigSafeWriteToError(Cangjie::ICE::MSG_PART_TWO.c_str()); (void)AsyncSigSafePut(Cangjie::ICE::GetTriggerPoint()); (void)AsyncSigSafeWriteToError("\n"); #ifdef CANGJIE_BUILD_TESTS CloseTempFileHandle(); #endif } void Signal::ThreadDelaySynchronizer() { // When multiple threads call this function at the same time, only the first thread can exit immediately, // and other threads can exit at a later time. if (g_processingSignalOrException.exchange(true)) { for (size_t i = LOOP_SIZE; i > 0; --i) { asm volatile(""); // This assembly prevents the loop from being optimized. } } } void Signal::ConcurrentSynchronousSignalHandler(int signum) { ThreadDelaySynchronizer(); WriteICEMessage(signum); Cangjie::TempFileManager::Instance().DeleteTempFiles(true); int exitCode = 128 + signum; // Add 128 to return the same error code as if the program crashed. _exit(exitCode); } #ifdef CANGJIE_BUILD_TESTS void SignalTest::SetSignalTestCallbackFunc(SignalTestCallbackFuncType fp, TriggerPointer triggerPoint, int fd) { g_signalTestCallbackFunction = fp; g_signalTestCallbackFunctionTriggerPoint = triggerPoint; g_errorFd = fd; } void SignalTest::ExecuteSignalTestCallbackFunc(TriggerPointer executionPoint) { if (g_signalTestCallbackFunction == nullptr) { return; } if (executionPoint == TriggerPointer::NON_POINTER || g_signalTestCallbackFunctionTriggerPoint == TriggerPointer::NON_POINTER) { return; } if (executionPoint != g_signalTestCallbackFunctionTriggerPoint) { return; } g_signalTestCallbackFunction(); } #endif // CANGJIE_BUILD_TESTS #endif // (defined NDEBUG)cangjie_compiler-1.0.7/src/Utils/SignalUtil.h000066400000000000000000000014221510705540100211210ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements crash signal handler related functions. */ #ifndef CANGJIE_UTILS_SIGNALUTIL_H #define CANGJIE_UTILS_SIGNALUTIL_H #if (defined RELEASE) #include #include #include #include "cangjie/Utils/Signal.h" namespace Cangjie { namespace Signal { void WriteICEMessage(int64_t errorCode); void ThreadDelaySynchronizer(); void ConcurrentSynchronousSignalHandler(int signum); } // namespace Signal } // namespace Cangjie #endif #endif // CANGJIE_UTILS_SIGNALUTIL_Hcangjie_compiler-1.0.7/src/Utils/SignalWin.cpp000066400000000000000000000034721510705540100213030ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements functions related to the Windows crash signal handler. */ #include "cangjie/Driver/TempFileManager.h" #if (defined RELEASE) #include "SignalUtil.h" namespace { LONG WINAPI WindowsExceptionHandler(LPEXCEPTION_POINTERS ep) { Cangjie::Signal::ThreadDelaySynchronizer(); // Write out the exception code. if (!ep || !ep->ExceptionRecord) { return EXCEPTION_EXECUTE_HANDLER; } int64_t exitCode = ep->ExceptionRecord->ExceptionCode; Cangjie::Signal::WriteICEMessage(exitCode); Cangjie::TempFileManager::Instance().DeleteTempFiles(); return exitCode; } void SignalHandler(int signum) { Cangjie::Signal::ConcurrentSynchronousSignalHandler(signum); } } // namespace namespace Cangjie { void RegisterCrashExceptionHandler() { SetUnhandledExceptionFilter(WindowsExceptionHandler); // Windows API } void RegisterCrashSignalHandler() { int signals[] = {SIGABRT, SIGFPE, SIGILL, SIGSEGV}; for (auto& sig : signals) { if (signal(sig, SignalHandler) == SIG_ERR) { // Even if sigaction failed, we are still able to continue our compiling. continue; } } } } // namespace Cangjie #else #include #include #endif // (defined NDEBUG) namespace { BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType) { Cangjie::TempFileManager::Instance().DeleteTempFiles(); return FALSE; } } // namespace namespace Cangjie { void RegisterCrtlCSignalHandler() { (void)SetConsoleCtrlHandler(LLVMConsoleCtrlHandler, TRUE); } } // namespace Cangjie cangjie_compiler-1.0.7/src/Utils/StdUtils/000077500000000000000000000000001510705540100204515ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Utils/StdUtils/StdUtils.cpp000066400000000000000000000026571510705540100227420ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/Utils/StdUtils.h" namespace Cangjie { std::optional Stoi(const std::string& s, int base) { try { return std::stoi(s, nullptr, base); } catch (...) { return {}; } } std::optional Stol(const std::string& s, int base) { try { return std::stol(s, nullptr, base); } catch (...) { return {}; } } std::optional Stoul(const std::string& s, int base) { try { return std::stoul(s, nullptr, base); } catch (...) { return {}; } } std::optional Stoll(const std::string& s, int base) { try { return std::stoll(s, nullptr, base); } catch (...) { return {}; } } std::optional Stoull(const std::string& s, int base) { try { return std::stoull(s, nullptr, base); } catch (...) { return {}; } } std::optional Stod(const std::string& s) { try { return std::stod(s, nullptr); } catch (...) { return {}; } } std::optional Stold(const std::string& s) { try { return std::stold(s, nullptr); } catch (...) { return {}; } } } cangjie_compiler-1.0.7/src/Utils/Unicode.cpp000066400000000000000000001210361510705540100207730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/Utils/Unicode.h" #include #include #include #include #include #include "cangjie/Utils/CheckUtils.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/SwapByteOrder.h" namespace Cangjie::Unicode { namespace { /// Index into the table below with the first byte of a UTF-8 sequence to get the number of trailing bytes that are /// supposed to follow it. Note that LEGAL UTF-8 values cannot have 4-5 bytes. const char TRAILING_BYTES_FOR_UTF8[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}; } bool IsASCII(UTF32 v) { constexpr unsigned a = 127; return v <= a && TRAILING_BYTES_FOR_UTF8[static_cast(v)] == 0; } bool IsASCIIIdStart(UTF32 c) { auto c1 = static_cast(static_cast(c)); return (c1 >= 'a' && c1 <= 'z') || (c1 >= 'A' && c1 <= 'Z') || c1 == '_'; } bool IsASCIIIdContinue(UTF32 c) { return isdigit(static_cast(c)) || IsASCIIIdStart(c); } ConversionResult ConvertUTF8toUTF32( const UTF8** sourceStart, const UTF8* sourceEnd, UTF32** targetStart, UTF32* targetEnd) { auto cr = llvm::ConvertUTF8toUTF32(sourceStart, sourceEnd, targetStart, targetEnd, llvm::strictConversion); switch (cr) { case llvm::conversionOK: case llvm::targetExhausted: return ConversionResult::OK; case llvm::sourceIllegal: return ConversionResult::SOURCE_ILLEGAL; case llvm::sourceExhausted: return ConversionResult::SOURCE_EXHAUSTED; } #if defined(__hm__) && defined(NDEBUG) return ConversionResult::OK; #endif } UTF32 ReadOneUnicodeChar(const UTF8** sourceStart, const UTF8* sourceEnd) { CJC_ASSERT(*sourceStart != sourceEnd); UTF32 c; UTF32* p{&c}; auto status = ConvertUTF8toUTF32(sourceStart, sourceEnd, &p, p + 1); CJC_ASSERT(status == ConversionResult::OK); return c; } ConversionResult ConvertUTF32toUTF8( const UTF32** sourceStart, const UTF32* sourceEnd, UTF8** targetStart, UTF8* targetEnd) { auto cr = llvm::ConvertUTF32toUTF8(sourceStart, sourceEnd, targetStart, targetEnd, llvm::strictConversion); switch (cr) { case llvm::conversionOK: return ConversionResult::OK; case llvm::targetExhausted: return ConversionResult::TARGET_EXHAUSTED; case llvm::sourceIllegal: return ConversionResult::SOURCE_ILLEGAL; case llvm::sourceExhausted: return ConversionResult::SOURCE_EXHAUSTED; } #if defined(__hm__) && defined(NDEBUG) return ConversionResult::OK; #endif } bool ConvertUTF32ToUTF8String(ArrayRef srcBytes, std::string& res) { if (srcBytes.Size() % 4 != 0) { return false; } if (srcBytes.Empty()) { return true; } auto src{reinterpret_cast(srcBytes.begin())}; auto srcEnd{reinterpret_cast(srcBytes.end())}; CJC_ASSERT(reinterpret_cast(src) % sizeof(UTF32) == 0); // byteswap if necessary std::vector byteSwapped; if (src[0] == UNI_UTF32_BYTE_ORDER_MARK_SWAPPED) { byteSwapped.insert(byteSwapped.end(), src, srcEnd); for (auto& i : byteSwapped) { i = llvm::ByteSwap_32(i); } src = &byteSwapped[0]; srcEnd = &byteSwapped[byteSwapped.size() - 1] + 1; } // Skip the BOM for conversion if (src[0] == UNI_UTF32_BYTE_ORDER_MARK_NATIVE) { ++src; } // Allocate enough space up front and shrink it later. res.resize(static_cast(srcBytes.Size() * UNI_MAX_UTF8_BYTES_PER_CODE_POINT + 1)); auto dst = reinterpret_cast(&res[0]); auto dstEnd = dst + res.size(); auto cr = ConvertUTF32toUTF8(&src, srcEnd, &dst, dstEnd); CJC_ASSERT(cr != ConversionResult::TARGET_EXHAUSTED); if (cr != ConversionResult::OK) { res.clear(); return false; } res.resize(static_cast(reinterpret_cast(dst) - &res[0])); res.push_back('\0'); res.pop_back(); return true; } bool ConvertUTF32ToUTF8String(ArrayRef src, std::string& res) { return ConvertUTF32ToUTF8String( ArrayRef{reinterpret_cast(src.begin()), src.Size() * static_cast(sizeof(UTF32))}, res); } bool ConvertCodepointToUTF8(UTF32 codepoint, char*& resultPtr) { const UTF32* sourceStart = &codepoint; auto sourceEnd = sourceStart + 1; UTF8* targetStart = reinterpret_cast(resultPtr); UTF8* targetEnd = targetStart + 4; auto cr = ConvertUTF32toUTF8(&sourceStart, sourceEnd, &targetStart, targetEnd); if (cr != ConversionResult::OK) { return false; } resultPtr = reinterpret_cast(targetStart); return true; } bool UnicodeCharSet::Contains(UTF32 c) const { return std::binary_search(ranges, ranges + size, c); } // clang-format off // Unicode 15.0 XID_Start static constexpr UnicodeCharRange XID_START_RANGES[]{{0x0041, 0x005A}, {0x0061, 0x007A}, {0x00AA, 0x00AA}, {0x00B5, 0x00B5}, {0x00BA, 0x00BA}, {0x00C0, 0x00D6}, {0x00D8, 0x00F6}, {0x00F8, 0x02C1}, {0x02C6, 0x02D1}, {0x02E0, 0x02E4}, {0x02EC, 0x02EC}, {0x02EE, 0x02EE}, {0x0370, 0x0374}, {0x0376, 0x0377}, {0x037B, 0x037D}, {0x037F, 0x037F}, {0x0386, 0x0386}, {0x0388, 0x038A}, {0x038C, 0x038C}, {0x038E, 0x03A1}, {0x03A3, 0x03F5}, {0x03F7, 0x0481}, {0x048A, 0x052F}, {0x0531, 0x0556}, {0x0559, 0x0559}, {0x0560, 0x0588}, {0x05D0, 0x05EA}, {0x05EF, 0x05F2}, {0x0620, 0x064A}, {0x066E, 0x066F}, {0x0671, 0x06D3}, {0x06D5, 0x06D5}, {0x06E5, 0x06E6}, {0x06EE, 0x06EF}, {0x06FA, 0x06FC}, {0x06FF, 0x06FF}, {0x0710, 0x0710}, {0x0712, 0x072F}, {0x074D, 0x07A5}, {0x07B1, 0x07B1}, {0x07CA, 0x07EA}, {0x07F4, 0x07F5}, {0x07FA, 0x07FA}, {0x0800, 0x0815}, {0x081A, 0x081A}, {0x0824, 0x0824}, {0x0828, 0x0828}, {0x0840, 0x0858}, {0x0860, 0x086A}, {0x0870, 0x0887}, {0x0889, 0x088E}, {0x08A0, 0x08C9}, {0x0904, 0x0939}, {0x093D, 0x093D}, {0x0950, 0x0950}, {0x0958, 0x0961}, {0x0971, 0x0980}, {0x0985, 0x098C}, {0x098F, 0x0990}, {0x0993, 0x09A8}, {0x09AA, 0x09B0}, {0x09B2, 0x09B2}, {0x09B6, 0x09B9}, {0x09BD, 0x09BD}, {0x09CE, 0x09CE}, {0x09DC, 0x09DD}, {0x09DF, 0x09E1}, {0x09F0, 0x09F1}, {0x09FC, 0x09FC}, {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, {0x0A32, 0x0A33}, {0x0A35, 0x0A36}, {0x0A38, 0x0A39}, {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, {0x0A72, 0x0A74}, {0x0A85, 0x0A8D}, {0x0A8F, 0x0A91}, {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, {0x0ABD, 0x0ABD}, {0x0AD0, 0x0AD0}, {0x0AE0, 0x0AE1}, {0x0AF9, 0x0AF9}, {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, {0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, {0x0B35, 0x0B39}, {0x0B3D, 0x0B3D}, {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B61}, {0x0B71, 0x0B71}, {0x0B83, 0x0B83}, {0x0B85, 0x0B8A}, {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, {0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9}, {0x0BD0, 0x0BD0}, {0x0C05, 0x0C0C}, {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, {0x0C2A, 0x0C39}, {0x0C3D, 0x0C3D}, {0x0C58, 0x0C5A}, {0x0C5D, 0x0C5D}, {0x0C60, 0x0C61}, {0x0C80, 0x0C80}, {0x0C85, 0x0C8C}, {0x0C8E, 0x0C90}, {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, {0x0CBD, 0x0CBD}, {0x0CDD, 0x0CDE}, {0x0CE0, 0x0CE1}, {0x0CF1, 0x0CF2}, {0x0D04, 0x0D0C}, {0x0D0E, 0x0D10}, {0x0D12, 0x0D3A}, {0x0D3D, 0x0D3D}, {0x0D4E, 0x0D4E}, {0x0D54, 0x0D56}, {0x0D5F, 0x0D61}, {0x0D7A, 0x0D7F}, {0x0D85, 0x0D96}, {0x0D9A, 0x0DB1}, {0x0DB3, 0x0DBB}, {0x0DBD, 0x0DBD}, {0x0DC0, 0x0DC6}, {0x0E01, 0x0E30}, {0x0E32, 0x0E32}, {0x0E40, 0x0E46}, {0x0E81, 0x0E82}, {0x0E84, 0x0E84}, {0x0E86, 0x0E8A}, {0x0E8C, 0x0EA3}, {0x0EA5, 0x0EA5}, {0x0EA7, 0x0EB0}, {0x0EB2, 0x0EB2}, {0x0EBD, 0x0EBD}, {0x0EC0, 0x0EC4}, {0x0EC6, 0x0EC6}, {0x0EDC, 0x0EDF}, {0x0F00, 0x0F00}, {0x0F40, 0x0F47}, {0x0F49, 0x0F6C}, {0x0F88, 0x0F8C}, {0x1000, 0x102A}, {0x103F, 0x103F}, {0x1050, 0x1055}, {0x105A, 0x105D}, {0x1061, 0x1061}, {0x1065, 0x1066}, {0x106E, 0x1070}, {0x1075, 0x1081}, {0x108E, 0x108E}, {0x10A0, 0x10C5}, {0x10C7, 0x10C7}, {0x10CD, 0x10CD}, {0x10D0, 0x10FA}, {0x10FC, 0x1248}, {0x124A, 0x124D}, {0x1250, 0x1256}, {0x1258, 0x1258}, {0x125A, 0x125D}, {0x1260, 0x1288}, {0x128A, 0x128D}, {0x1290, 0x12B0}, {0x12B2, 0x12B5}, {0x12B8, 0x12BE}, {0x12C0, 0x12C0}, {0x12C2, 0x12C5}, {0x12C8, 0x12D6}, {0x12D8, 0x1310}, {0x1312, 0x1315}, {0x1318, 0x135A}, {0x1380, 0x138F}, {0x13A0, 0x13F5}, {0x13F8, 0x13FD}, {0x1401, 0x166C}, {0x166F, 0x167F}, {0x1681, 0x169A}, {0x16A0, 0x16EA}, {0x16EE, 0x16F8}, {0x1700, 0x1711}, {0x171F, 0x1731}, {0x1740, 0x1751}, {0x1760, 0x176C}, {0x176E, 0x1770}, {0x1780, 0x17B3}, {0x17D7, 0x17D7}, {0x17DC, 0x17DC}, {0x1820, 0x1878}, {0x1880, 0x18A8}, {0x18AA, 0x18AA}, {0x18B0, 0x18F5}, {0x1900, 0x191E}, {0x1950, 0x196D}, {0x1970, 0x1974}, {0x1980, 0x19AB}, {0x19B0, 0x19C9}, {0x1A00, 0x1A16}, {0x1A20, 0x1A54}, {0x1AA7, 0x1AA7}, {0x1B05, 0x1B33}, {0x1B45, 0x1B4C}, {0x1B83, 0x1BA0}, {0x1BAE, 0x1BAF}, {0x1BBA, 0x1BE5}, {0x1C00, 0x1C23}, {0x1C4D, 0x1C4F}, {0x1C5A, 0x1C7D}, {0x1C80, 0x1C88}, {0x1C90, 0x1CBA}, {0x1CBD, 0x1CBF}, {0x1CE9, 0x1CEC}, {0x1CEE, 0x1CF3}, {0x1CF5, 0x1CF6}, {0x1CFA, 0x1CFA}, {0x1D00, 0x1DBF}, {0x1E00, 0x1F15}, {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, {0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, {0x1FB6, 0x1FBC}, {0x1FBE, 0x1FBE}, {0x1FC2, 0x1FC4}, {0x1FC6, 0x1FCC}, {0x1FD0, 0x1FD3}, {0x1FD6, 0x1FDB}, {0x1FE0, 0x1FEC}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFC}, {0x2071, 0x2071}, {0x207F, 0x207F}, {0x2090, 0x209C}, {0x2102, 0x2102}, {0x2107, 0x2107}, {0x210A, 0x2113}, {0x2115, 0x2115}, {0x2118, 0x211D}, {0x2124, 0x2124}, {0x2126, 0x2126}, {0x2128, 0x2128}, {0x212A, 0x2139}, {0x213C, 0x213F}, {0x2145, 0x2149}, {0x214E, 0x214E}, {0x2160, 0x2188}, {0x2C00, 0x2CE4}, {0x2CEB, 0x2CEE}, {0x2CF2, 0x2CF3}, {0x2D00, 0x2D25}, {0x2D27, 0x2D27}, {0x2D2D, 0x2D2D}, {0x2D30, 0x2D67}, {0x2D6F, 0x2D6F}, {0x2D80, 0x2D96}, {0x2DA0, 0x2DA6}, {0x2DA8, 0x2DAE}, {0x2DB0, 0x2DB6}, {0x2DB8, 0x2DBE}, {0x2DC0, 0x2DC6}, {0x2DC8, 0x2DCE}, {0x2DD0, 0x2DD6}, {0x2DD8, 0x2DDE}, {0x3005, 0x3007}, {0x3021, 0x3029}, {0x3031, 0x3035}, {0x3038, 0x303C}, {0x3041, 0x3096}, {0x309D, 0x309F}, {0x30A1, 0x30FA}, {0x30FC, 0x30FF}, {0x3105, 0x312F}, {0x3131, 0x318E}, {0x31A0, 0x31BF}, {0x31F0, 0x31FF}, {0x3400, 0x4DBF}, {0x4E00, 0xA48C}, {0xA4D0, 0xA4FD}, {0xA500, 0xA60C}, {0xA610, 0xA61F}, {0xA62A, 0xA62B}, {0xA640, 0xA66E}, {0xA67F, 0xA69D}, {0xA6A0, 0xA6EF}, {0xA717, 0xA71F}, {0xA722, 0xA788}, {0xA78B, 0xA7CA}, {0xA7D0, 0xA7D1}, {0xA7D3, 0xA7D3}, {0xA7D5, 0xA7D9}, {0xA7F2, 0xA801}, {0xA803, 0xA805}, {0xA807, 0xA80A}, {0xA80C, 0xA822}, {0xA840, 0xA873}, {0xA882, 0xA8B3}, {0xA8F2, 0xA8F7}, {0xA8FB, 0xA8FB}, {0xA8FD, 0xA8FE}, {0xA90A, 0xA925}, {0xA930, 0xA946}, {0xA960, 0xA97C}, {0xA984, 0xA9B2}, {0xA9CF, 0xA9CF}, {0xA9E0, 0xA9E4}, {0xA9E6, 0xA9EF}, {0xA9FA, 0xA9FE}, {0xAA00, 0xAA28}, {0xAA40, 0xAA42}, {0xAA44, 0xAA4B}, {0xAA60, 0xAA76}, {0xAA7A, 0xAA7A}, {0xAA7E, 0xAAAF}, {0xAAB1, 0xAAB1}, {0xAAB5, 0xAAB6}, {0xAAB9, 0xAABD}, {0xAAC0, 0xAAC0}, {0xAAC2, 0xAAC2}, {0xAADB, 0xAADD}, {0xAAE0, 0xAAEA}, {0xAAF2, 0xAAF4}, {0xAB01, 0xAB06}, {0xAB09, 0xAB0E}, {0xAB11, 0xAB16}, {0xAB20, 0xAB26}, {0xAB28, 0xAB2E}, {0xAB30, 0xAB5A}, {0xAB5C, 0xAB69}, {0xAB70, 0xABE2}, {0xAC00, 0xD7A3}, {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, {0xF900, 0xFA6D}, {0xFA70, 0xFAD9}, {0xFB00, 0xFB06}, {0xFB13, 0xFB17}, {0xFB1D, 0xFB1D}, {0xFB1F, 0xFB28}, {0xFB2A, 0xFB36}, {0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41}, {0xFB43, 0xFB44}, {0xFB46, 0xFBB1}, {0xFBD3, 0xFC5D}, {0xFC64, 0xFD3D}, {0xFD50, 0xFD8F}, {0xFD92, 0xFDC7}, {0xFDF0, 0xFDF9}, {0xFE71, 0xFE71}, {0xFE73, 0xFE73}, {0xFE77, 0xFE77}, {0xFE79, 0xFE79}, {0xFE7B, 0xFE7B}, {0xFE7D, 0xFE7D}, {0xFE7F, 0xFEFC}, {0xFF21, 0xFF3A}, {0xFF41, 0xFF5A}, {0xFF66, 0xFF9D}, {0xFFA0, 0xFFBE}, {0xFFC2, 0xFFC7}, {0xFFCA, 0xFFCF}, {0xFFD2, 0xFFD7}, {0xFFDA, 0xFFDC}, {0x10000, 0x1000B}, {0x1000D, 0x10026}, {0x10028, 0x1003A}, {0x1003C, 0x1003D}, {0x1003F, 0x1004D}, {0x10050, 0x1005D}, {0x10080, 0x100FA}, {0x10140, 0x10174}, {0x10280, 0x1029C}, {0x102A0, 0x102D0}, {0x10300, 0x1031F}, {0x1032D, 0x1034A}, {0x10350, 0x10375}, {0x10380, 0x1039D}, {0x103A0, 0x103C3}, {0x103C8, 0x103CF}, {0x103D1, 0x103D5}, {0x10400, 0x1049D}, {0x104B0, 0x104D3}, {0x104D8, 0x104FB}, {0x10500, 0x10527}, {0x10530, 0x10563}, {0x10570, 0x1057A}, {0x1057C, 0x1058A}, {0x1058C, 0x10592}, {0x10594, 0x10595}, {0x10597, 0x105A1}, {0x105A3, 0x105B1}, {0x105B3, 0x105B9}, {0x105BB, 0x105BC}, {0x10600, 0x10736}, {0x10740, 0x10755}, {0x10760, 0x10767}, {0x10780, 0x10785}, {0x10787, 0x107B0}, {0x107B2, 0x107BA}, {0x10800, 0x10805}, {0x10808, 0x10808}, {0x1080A, 0x10835}, {0x10837, 0x10838}, {0x1083C, 0x1083C}, {0x1083F, 0x10855}, {0x10860, 0x10876}, {0x10880, 0x1089E}, {0x108E0, 0x108F2}, {0x108F4, 0x108F5}, {0x10900, 0x10915}, {0x10920, 0x10939}, {0x10980, 0x109B7}, {0x109BE, 0x109BF}, {0x10A00, 0x10A00}, {0x10A10, 0x10A13}, {0x10A15, 0x10A17}, {0x10A19, 0x10A35}, {0x10A60, 0x10A7C}, {0x10A80, 0x10A9C}, {0x10AC0, 0x10AC7}, {0x10AC9, 0x10AE4}, {0x10B00, 0x10B35}, {0x10B40, 0x10B55}, {0x10B60, 0x10B72}, {0x10B80, 0x10B91}, {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, {0x10CC0, 0x10CF2}, {0x10D00, 0x10D23}, {0x10E80, 0x10EA9}, {0x10EB0, 0x10EB1}, {0x10F00, 0x10F1C}, {0x10F27, 0x10F27}, {0x10F30, 0x10F45}, {0x10F70, 0x10F81}, {0x10FB0, 0x10FC4}, {0x10FE0, 0x10FF6}, {0x11003, 0x11037}, {0x11071, 0x11072}, {0x11075, 0x11075}, {0x11083, 0x110AF}, {0x110D0, 0x110E8}, {0x11103, 0x11126}, {0x11144, 0x11144}, {0x11147, 0x11147}, {0x11150, 0x11172}, {0x11176, 0x11176}, {0x11183, 0x111B2}, {0x111C1, 0x111C4}, {0x111DA, 0x111DA}, {0x111DC, 0x111DC}, {0x11200, 0x11211}, {0x11213, 0x1122B}, {0x1123F, 0x11240}, {0x11280, 0x11286}, {0x11288, 0x11288}, {0x1128A, 0x1128D}, {0x1128F, 0x1129D}, {0x1129F, 0x112A8}, {0x112B0, 0x112DE}, {0x11305, 0x1130C}, {0x1130F, 0x11310}, {0x11313, 0x11328}, {0x1132A, 0x11330}, {0x11332, 0x11333}, {0x11335, 0x11339}, {0x1133D, 0x1133D}, {0x11350, 0x11350}, {0x1135D, 0x11361}, {0x11400, 0x11434}, {0x11447, 0x1144A}, {0x1145F, 0x11461}, {0x11480, 0x114AF}, {0x114C4, 0x114C5}, {0x114C7, 0x114C7}, {0x11580, 0x115AE}, {0x115D8, 0x115DB}, {0x11600, 0x1162F}, {0x11644, 0x11644}, {0x11680, 0x116AA}, {0x116B8, 0x116B8}, {0x11700, 0x1171A}, {0x11740, 0x11746}, {0x11800, 0x1182B}, {0x118A0, 0x118DF}, {0x118FF, 0x11906}, {0x11909, 0x11909}, {0x1190C, 0x11913}, {0x11915, 0x11916}, {0x11918, 0x1192F}, {0x1193F, 0x1193F}, {0x11941, 0x11941}, {0x119A0, 0x119A7}, {0x119AA, 0x119D0}, {0x119E1, 0x119E1}, {0x119E3, 0x119E3}, {0x11A00, 0x11A00}, {0x11A0B, 0x11A32}, {0x11A3A, 0x11A3A}, {0x11A50, 0x11A50}, {0x11A5C, 0x11A89}, {0x11A9D, 0x11A9D}, {0x11AB0, 0x11AF8}, {0x11C00, 0x11C08}, {0x11C0A, 0x11C2E}, {0x11C40, 0x11C40}, {0x11C72, 0x11C8F}, {0x11D00, 0x11D06}, {0x11D08, 0x11D09}, {0x11D0B, 0x11D30}, {0x11D46, 0x11D46}, {0x11D60, 0x11D65}, {0x11D67, 0x11D68}, {0x11D6A, 0x11D89}, {0x11D98, 0x11D98}, {0x11EE0, 0x11EF2}, {0x11F02, 0x11F02}, {0x11F04, 0x11F10}, {0x11F12, 0x11F33}, {0x11FB0, 0x11FB0}, {0x12000, 0x12399}, {0x12400, 0x1246E}, {0x12480, 0x12543}, {0x12F90, 0x12FF0}, {0x13000, 0x1342F}, {0x13441, 0x13446}, {0x14400, 0x14646}, {0x16800, 0x16A38}, {0x16A40, 0x16A5E}, {0x16A70, 0x16ABE}, {0x16AD0, 0x16AED}, {0x16B00, 0x16B2F}, {0x16B40, 0x16B43}, {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16E40, 0x16E7F}, {0x16F00, 0x16F4A}, {0x16F50, 0x16F50}, {0x16F93, 0x16F9F}, {0x16FE0, 0x16FE1}, {0x16FE3, 0x16FE3}, {0x17000, 0x187F7}, {0x18800, 0x18CD5}, {0x18D00, 0x18D08}, {0x1AFF0, 0x1AFF3}, {0x1AFF5, 0x1AFFB}, {0x1AFFD, 0x1AFFE}, {0x1B000, 0x1B122}, {0x1B132, 0x1B132}, {0x1B150, 0x1B152}, {0x1B155, 0x1B155}, {0x1B164, 0x1B167}, {0x1B170, 0x1B2FB}, {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, {0x1BC80, 0x1BC88}, {0x1BC90, 0x1BC99}, {0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, {0x1D49E, 0x1D49F}, {0x1D4A2, 0x1D4A2}, {0x1D4A5, 0x1D4A6}, {0x1D4A9, 0x1D4AC}, {0x1D4AE, 0x1D4B9}, {0x1D4BB, 0x1D4BB}, {0x1D4BD, 0x1D4C3}, {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, {0x1D50D, 0x1D514}, {0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, {0x1D53B, 0x1D53E}, {0x1D540, 0x1D544}, {0x1D546, 0x1D546}, {0x1D54A, 0x1D550}, {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D6C0}, {0x1D6C2, 0x1D6DA}, {0x1D6DC, 0x1D6FA}, {0x1D6FC, 0x1D714}, {0x1D716, 0x1D734}, {0x1D736, 0x1D74E}, {0x1D750, 0x1D76E}, {0x1D770, 0x1D788}, {0x1D78A, 0x1D7A8}, {0x1D7AA, 0x1D7C2}, {0x1D7C4, 0x1D7CB}, {0x1DF00, 0x1DF1E}, {0x1DF25, 0x1DF2A}, {0x1E030, 0x1E06D}, {0x1E100, 0x1E12C}, {0x1E137, 0x1E13D}, {0x1E14E, 0x1E14E}, {0x1E290, 0x1E2AD}, {0x1E2C0, 0x1E2EB}, {0x1E4D0, 0x1E4EB}, {0x1E7E0, 0x1E7E6}, {0x1E7E8, 0x1E7EB}, {0x1E7ED, 0x1E7EE}, {0x1E7F0, 0x1E7FE}, {0x1E800, 0x1E8C4}, {0x1E900, 0x1E943}, {0x1E94B, 0x1E94B}, {0x1EE00, 0x1EE03}, {0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, {0x1EE24, 0x1EE24}, {0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32}, {0x1EE34, 0x1EE37}, {0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, {0x1EE42, 0x1EE42}, {0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, {0x1EE4B, 0x1EE4B}, {0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52}, {0x1EE54, 0x1EE54}, {0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, {0x1EE5B, 0x1EE5B}, {0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, {0x1EE61, 0x1EE62}, {0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72}, {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, {0x1EE7E, 0x1EE7E}, {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, {0x1EEA1, 0x1EEA3}, {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, {0x20000, 0x2A6DF}, {0x2A700, 0x2B739}, {0x2B740, 0x2B81D}, {0x2B820, 0x2CEA1}, {0x2CEB0, 0x2EBE0}, {0x2F800, 0x2FA1D}, {0x30000, 0x3134A}, {0x31350, 0x323AF}}; // clang-format on bool IsXIDStart(UTF32 c) { static const UnicodeCharSet XID_START_SET{XID_START_RANGES}; return XID_START_SET.Contains(c); } bool IsCJXIDStart(UTF32 c) { return c == static_cast(static_cast('_')) || IsXIDStart(c); } // clang-format off // Unicode 15.0 XID_Continue, excluding XID_Start // The Unicode Property XID_Continue is a super set of XID_Start. // To save Space, the table below only contains the codepoints // that are not also in XID_Start. static constexpr UnicodeCharRange XID_CONTINUE_RANGES[]{{0x0030, 0x0039}, {0x5F, 0x5F}, {0x00B7, 0x00B7}, {0x0300, 0x036F}, {0x0387, 0x0387}, {0x0483, 0x0487}, {0x0591, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, {0x05C4, 0x05C5}, {0x05C7, 0x05C7}, {0x0610, 0x061A}, {0x064B, 0x0669}, {0x0670, 0x0670}, {0x06D6, 0x06DC}, {0x06DF, 0x06E4}, {0x06E7, 0x06E8}, {0x06EA, 0x06ED}, {0x06F0, 0x06F9}, {0x0711, 0x0711}, {0x0730, 0x074A}, {0x07A6, 0x07B0}, {0x07C0, 0x07C9}, {0x07EB, 0x07F3}, {0x07FD, 0x07FD}, {0x0816, 0x0819}, {0x081B, 0x0823}, {0x0825, 0x0827}, {0x0829, 0x082D}, {0x0859, 0x085B}, {0x0898, 0x089F}, {0x08CA, 0x08E1}, {0x08E3, 0x0903}, {0x093A, 0x093C}, {0x093E, 0x094F}, {0x0951, 0x0957}, {0x0962, 0x0963}, {0x0966, 0x096F}, {0x0981, 0x0983}, {0x09BC, 0x09BC}, {0x09BE, 0x09C4}, {0x09C7, 0x09C8}, {0x09CB, 0x09CD}, {0x09D7, 0x09D7}, {0x09E2, 0x09E3}, {0x09E6, 0x09EF}, {0x09FE, 0x09FE}, {0x0A01, 0x0A03}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, {0x0A66, 0x0A71}, {0x0A75, 0x0A75}, {0x0A81, 0x0A83}, {0x0ABC, 0x0ABC}, {0x0ABE, 0x0AC5}, {0x0AC7, 0x0AC9}, {0x0ACB, 0x0ACD}, {0x0AE2, 0x0AE3}, {0x0AE6, 0x0AEF}, {0x0AFA, 0x0AFF}, {0x0B01, 0x0B03}, {0x0B3C, 0x0B3C}, {0x0B3E, 0x0B44}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4D}, {0x0B55, 0x0B57}, {0x0B62, 0x0B63}, {0x0B66, 0x0B6F}, {0x0B82, 0x0B82}, {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD}, {0x0BD7, 0x0BD7}, {0x0BE6, 0x0BEF}, {0x0C00, 0x0C04}, {0x0C3C, 0x0C3C}, {0x0C3E, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, {0x0C62, 0x0C63}, {0x0C66, 0x0C6F}, {0x0C81, 0x0C83}, {0x0CBC, 0x0CBC}, {0x0CBE, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6}, {0x0CE2, 0x0CE3}, {0x0CE6, 0x0CEF}, {0x0CF3, 0x0CF3}, {0x0D00, 0x0D03}, {0x0D3B, 0x0D3C}, {0x0D3E, 0x0D44}, {0x0D46, 0x0D48}, {0x0D4A, 0x0D4D}, {0x0D57, 0x0D57}, {0x0D62, 0x0D63}, {0x0D66, 0x0D6F}, {0x0D81, 0x0D83}, {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF}, {0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF3}, {0x0E31, 0x0E31}, {0x0E33, 0x0E3A}, {0x0E47, 0x0E4E}, {0x0E50, 0x0E59}, {0x0EB1, 0x0EB1}, {0x0EB3, 0x0EBC}, {0x0EC8, 0x0ECE}, {0x0ED0, 0x0ED9}, {0x0F18, 0x0F19}, {0x0F20, 0x0F29}, {0x0F35, 0x0F35}, {0x0F37, 0x0F37}, {0x0F39, 0x0F39}, {0x0F3E, 0x0F3F}, {0x0F71, 0x0F84}, {0x0F86, 0x0F87}, {0x0F8D, 0x0F97}, {0x0F99, 0x0FBC}, {0x0FC6, 0x0FC6}, {0x102B, 0x103E}, {0x1040, 0x1049}, {0x1056, 0x1059}, {0x105E, 0x1060}, {0x1062, 0x1064}, {0x1067, 0x106D}, {0x1071, 0x1074}, {0x1082, 0x108D}, {0x108F, 0x109D}, {0x135D, 0x135F}, {0x1369, 0x1371}, {0x1712, 0x1715}, {0x1732, 0x1734}, {0x1752, 0x1753}, {0x1772, 0x1773}, {0x17B4, 0x17D3}, {0x17DD, 0x17DD}, {0x17E0, 0x17E9}, {0x180B, 0x180D}, {0x180F, 0x1819}, {0x18A9, 0x18A9}, {0x1920, 0x192B}, {0x1930, 0x193B}, {0x1946, 0x194F}, {0x19D0, 0x19DA}, {0x1A17, 0x1A1B}, {0x1A55, 0x1A5E}, {0x1A60, 0x1A7C}, {0x1A7F, 0x1A89}, {0x1A90, 0x1A99}, {0x1AB0, 0x1ABD}, {0x1ABF, 0x1ACE}, {0x1B00, 0x1B04}, {0x1B34, 0x1B44}, {0x1B50, 0x1B59}, {0x1B6B, 0x1B73}, {0x1B80, 0x1B82}, {0x1BA1, 0x1BAD}, {0x1BB0, 0x1BB9}, {0x1BE6, 0x1BF3}, {0x1C24, 0x1C37}, {0x1C40, 0x1C49}, {0x1C50, 0x1C59}, {0x1CD0, 0x1CD2}, {0x1CD4, 0x1CE8}, {0x1CED, 0x1CED}, {0x1CF4, 0x1CF4}, {0x1CF7, 0x1CF9}, {0x1DC0, 0x1DFF}, {0x203F, 0x2040}, {0x2054, 0x2054}, {0x20D0, 0x20DC}, {0x20E1, 0x20E1}, {0x20E5, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2D7F, 0x2D7F}, {0x2DE0, 0x2DFF}, {0x302A, 0x302F}, {0x3099, 0x309A}, {0xA620, 0xA629}, {0xA66F, 0xA66F}, {0xA674, 0xA67D}, {0xA69E, 0xA69F}, {0xA6F0, 0xA6F1}, {0xA802, 0xA802}, {0xA806, 0xA806}, {0xA80B, 0xA80B}, {0xA823, 0xA827}, {0xA82C, 0xA82C}, {0xA880, 0xA881}, {0xA8B4, 0xA8C5}, {0xA8D0, 0xA8D9}, {0xA8E0, 0xA8F1}, {0xA8FF, 0xA909}, {0xA926, 0xA92D}, {0xA947, 0xA953}, {0xA980, 0xA983}, {0xA9B3, 0xA9C0}, {0xA9D0, 0xA9D9}, {0xA9E5, 0xA9E5}, {0xA9F0, 0xA9F9}, {0xAA29, 0xAA36}, {0xAA43, 0xAA43}, {0xAA4C, 0xAA4D}, {0xAA50, 0xAA59}, {0xAA7B, 0xAA7D}, {0xAAB0, 0xAAB0}, {0xAAB2, 0xAAB4}, {0xAAB7, 0xAAB8}, {0xAABE, 0xAABF}, {0xAAC1, 0xAAC1}, {0xAAEB, 0xAAEF}, {0xAAF5, 0xAAF6}, {0xABE3, 0xABEA}, {0xABEC, 0xABED}, {0xABF0, 0xABF9}, {0xFB1E, 0xFB1E}, {0xFE00, 0xFE0F}, {0xFE20, 0xFE2F}, {0xFE33, 0xFE34}, {0xFE4D, 0xFE4F}, {0xFF10, 0xFF19}, {0xFF3F, 0xFF3F}, {0xFF9E, 0xFF9F}, {0x101FD, 0x101FD}, {0x102E0, 0x102E0}, {0x10376, 0x1037A}, {0x104A0, 0x104A9}, {0x10A01, 0x10A03}, {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F}, {0x10A38, 0x10A3A}, {0x10A3F, 0x10A3F}, {0x10AE5, 0x10AE6}, {0x10D24, 0x10D27}, {0x10D30, 0x10D39}, {0x10EAB, 0x10EAC}, {0x10EFD, 0x10EFF}, {0x10F46, 0x10F50}, {0x10F82, 0x10F85}, {0x11000, 0x11002}, {0x11038, 0x11046}, {0x11066, 0x11070}, {0x11073, 0x11074}, {0x1107F, 0x11082}, {0x110B0, 0x110BA}, {0x110C2, 0x110C2}, {0x110F0, 0x110F9}, {0x11100, 0x11102}, {0x11127, 0x11134}, {0x11136, 0x1113F}, {0x11145, 0x11146}, {0x11173, 0x11173}, {0x11180, 0x11182}, {0x111B3, 0x111C0}, {0x111C9, 0x111CC}, {0x111CE, 0x111D9}, {0x1122C, 0x11237}, {0x1123E, 0x1123E}, {0x11241, 0x11241}, {0x112DF, 0x112EA}, {0x112F0, 0x112F9}, {0x11300, 0x11303}, {0x1133B, 0x1133C}, {0x1133E, 0x11344}, {0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11357, 0x11357}, {0x11362, 0x11363}, {0x11366, 0x1136C}, {0x11370, 0x11374}, {0x11435, 0x11446}, {0x11450, 0x11459}, {0x1145E, 0x1145E}, {0x114B0, 0x114C3}, {0x114D0, 0x114D9}, {0x115AF, 0x115B5}, {0x115B8, 0x115C0}, {0x115DC, 0x115DD}, {0x11630, 0x11640}, {0x11650, 0x11659}, {0x116AB, 0x116B7}, {0x116C0, 0x116C9}, {0x1171D, 0x1172B}, {0x11730, 0x11739}, {0x1182C, 0x1183A}, {0x118E0, 0x118E9}, {0x11930, 0x11935}, {0x11937, 0x11938}, {0x1193B, 0x1193E}, {0x11940, 0x11940}, {0x11942, 0x11943}, {0x11950, 0x11959}, {0x119D1, 0x119D7}, {0x119DA, 0x119E0}, {0x119E4, 0x119E4}, {0x11A01, 0x11A0A}, {0x11A33, 0x11A39}, {0x11A3B, 0x11A3E}, {0x11A47, 0x11A47}, {0x11A51, 0x11A5B}, {0x11A8A, 0x11A99}, {0x11C2F, 0x11C36}, {0x11C38, 0x11C3F}, {0x11C50, 0x11C59}, {0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6}, {0x11D31, 0x11D36}, {0x11D3A, 0x11D3A}, {0x11D3C, 0x11D3D}, {0x11D3F, 0x11D45}, {0x11D47, 0x11D47}, {0x11D50, 0x11D59}, {0x11D8A, 0x11D8E}, {0x11D90, 0x11D91}, {0x11D93, 0x11D97}, {0x11DA0, 0x11DA9}, {0x11EF3, 0x11EF6}, {0x11F00, 0x11F01}, {0x11F03, 0x11F03}, {0x11F34, 0x11F3A}, {0x11F3E, 0x11F42}, {0x11F50, 0x11F59}, {0x13440, 0x13440}, {0x13447, 0x13455}, {0x16A60, 0x16A69}, {0x16AC0, 0x16AC9}, {0x16AF0, 0x16AF4}, {0x16B30, 0x16B36}, {0x16B50, 0x16B59}, {0x16F4F, 0x16F4F}, {0x16F51, 0x16F87}, {0x16F8F, 0x16F92}, {0x16FE4, 0x16FE4}, {0x16FF0, 0x16FF1}, {0x1BC9D, 0x1BC9E}, {0x1CF00, 0x1CF2D}, {0x1CF30, 0x1CF46}, {0x1D165, 0x1D169}, {0x1D16D, 0x1D172}, {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD}, {0x1D242, 0x1D244}, {0x1D7CE, 0x1D7FF}, {0x1DA00, 0x1DA36}, {0x1DA3B, 0x1DA6C}, {0x1DA75, 0x1DA75}, {0x1DA84, 0x1DA84}, {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, {0x1E026, 0x1E02A}, {0x1E08F, 0x1E08F}, {0x1E130, 0x1E136}, {0x1E140, 0x1E149}, {0x1E2AE, 0x1E2AE}, {0x1E2EC, 0x1E2F9}, {0x1E4EC, 0x1E4F9}, {0x1E8D0, 0x1E8D6}, {0x1E944, 0x1E94A}, {0x1E950, 0x1E959}, {0x1FBF0, 0x1FBF9}, {0xE0100, 0xE01EF}}; // clang-format on bool IsXIDContinue(UTF32 c) { static const UnicodeCharSet XID_CONTINUE_SET{XID_CONTINUE_RANGES}; // XIDContinue set excludes XIDStart set, but every character that is XIDStart is also XIDContinue return XID_CONTINUE_SET.Contains(c) || IsXIDStart(c); } // clang-format off // These tables should have constant initialisation, so it is guaranteed by the C++ standard their initialisation is // before any of their usages. static const UnicodeCharRange NFC_QUICK_CHECK_NO[]{{0x0340, 0x0341}, {0x0343, 0x0344}, {0x0374, 0x0374}, {0x037E, 0x037E}, {0x0387, 0x0387}, {0x0958, 0x095F}, {0x09DC, 0x09DD}, {0x09DF, 0x09DF}, {0x0A33, 0x0A33}, {0x0A36, 0x0A36}, {0x0A59, 0x0A5B}, {0x0A5E, 0x0A5E}, {0x0B5C, 0x0B5D}, {0x0F43, 0x0F43}, {0x0F4D, 0x0F4D}, {0x0F52, 0x0F52}, {0x0F57, 0x0F57}, {0x0F5C, 0x0F5C}, {0x0F69, 0x0F69}, {0x0F73, 0x0F73}, {0x0F75, 0x0F76}, {0x0F78, 0x0F78}, {0x0F81, 0x0F81}, {0x0F93, 0x0F93}, {0x0F9D, 0x0F9D}, {0x0FA2, 0x0FA2}, {0x0FA7, 0x0FA7}, {0x0FAC, 0x0FAC}, {0x0FB9, 0x0FB9}, {0x1F71, 0x1F71}, {0x1F73, 0x1F73}, {0x1F75, 0x1F75}, {0x1F77, 0x1F77}, {0x1F79, 0x1F79}, {0x1F7B, 0x1F7B}, {0x1F7D, 0x1F7D}, {0x1FBB, 0x1FBB}, {0x1FBE, 0x1FBE}, {0x1FC9, 0x1FC9}, {0x1FCB, 0x1FCB}, {0x1FD3, 0x1FD3}, {0x1FDB, 0x1FDB}, {0x1FE3, 0x1FE3}, {0x1FEB, 0x1FEB}, {0x1FEE, 0x1FEF}, {0x1FF9, 0x1FF9}, {0x1FFB, 0x1FFB}, {0x1FFD, 0x1FFD}, {0x2000, 0x2001}, {0x2126, 0x2126}, {0x212A, 0x212B}, {0x2329, 0x2329}, {0x232A, 0x232A}, {0x2ADC, 0x2ADC}, {0xF900, 0xFA0D}, {0xFA10, 0xFA10}, {0xFA12, 0xFA12}, {0xFA15, 0xFA1E}, {0xFA20, 0xFA20}, {0xFA22, 0xFA22}, {0xFA25, 0xFA26}, {0xFA2A, 0xFA6D}, {0xFA70, 0xFAD9}, {0xFB1D, 0xFB1D}, {0xFB1F, 0xFB1F}, {0xFB2A, 0xFB36}, {0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41}, {0xFB43, 0xFB44}, {0xFB46, 0xFB4E}, {0x1D15E, 0x1D164}, {0x1D1BB, 0x1D1C0}, {0x2F800, 0x2FA1D}}; static const UnicodeCharRange NFC_QUICK_CHECK_MAYBE[]{ {0x0300, 0x0304}, {0x0306, 0x030C}, {0x030F, 0x030F}, {0x0311, 0x0311}, {0x0313, 0x0314}, {0x031B, 0x031B}, {0x0323, 0x0328}, {0x032D, 0x032E}, {0x0330, 0x0331}, {0x0338, 0x0338}, {0x0342, 0x0342}, {0x0345, 0x0345}, {0x0653, 0x0655}, {0x093C, 0x093C}, {0x09BE, 0x09BE}, {0x09D7, 0x09D7}, {0x0B3E, 0x0B3E}, {0x0B56, 0x0B56}, {0x0B57, 0x0B57}, {0x0BBE, 0x0BBE}, {0x0BD7, 0x0BD7}, {0x0C56, 0x0C56}, {0x0CC2, 0x0CC2}, {0x0CD5, 0x0CD6}, {0x0D3E, 0x0D3E}, {0x0D57, 0x0D57}, {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DCF}, {0x0DDF, 0x0DDF}, {0x102E, 0x102E}, {0x1161, 0x1175}, {0x11A8, 0x11C2}, {0x1B35, 0x1B35}, {0x3099, 0x309A}, {0x110BA, 0x110BA}, {0x11127, 0x11127}, {0x1133E, 0x1133E}, {0x11357, 0x11357}, {0x114B0, 0x114B0}, {0x114BA, 0x114BA}, {0x114BD, 0x114BD}, {0x115AF, 0x115AF}, {0x11930, 0x11930}, }; // clang-format on /** Get quick check kind according to normalisation form C quick check tables. If the input code point \p c is an invalid unicode character, the behaviour is undefined. */ static NfcQcResult IsAllowed(UTF32 c) { static const UnicodeCharSet NFC_QC_NO_SET{NFC_QUICK_CHECK_NO}; static const UnicodeCharSet NFC_QC_MAYBE_SET{NFC_QUICK_CHECK_MAYBE}; if (NFC_QC_NO_SET.Contains(c)) { return NfcQcResult::NO; } if (NFC_QC_MAYBE_SET.Contains(c)) { return NfcQcResult::MAYBE; } return NfcQcResult::YES; } // clang-format off /** * This map is not exactly correct in the way that the validity of unicode codepoint is not checked. When an invalid * codepoint is checked against this map, the result is undefined. */ static const std::map> CCC_TABLE{ {0x0000, 0}, {0x0300, 230}, {0x0315, 232}, {0x0316, 220}, {0x031A, 232}, {0x031B, 216}, {0x031C, 220}, {0x0321, 202}, {0x0323, 220}, {0x0327, 202}, {0x0329, 220}, {0x0334, 1}, {0x0339, 220}, {0x033D, 230}, {0x0345, 240}, {0x0346, 230}, {0x0347, 220}, {0x034A, 230}, {0x034D, 220}, {0x034F, 0}, {0x0350, 230}, {0x0353, 220}, {0x0357, 230}, {0x0358, 232}, {0x0359, 220}, {0x035B, 230}, {0x035C, 233}, {0x035D, 234}, {0x035F, 233}, {0x0360, 234}, {0x0362, 233}, {0x0363, 230}, {0x0370, 0}, {0x0483, 230}, {0x0488, 0}, {0x0591, 220}, {0x0592, 230}, {0x0596, 220}, {0x0597, 230}, {0x059A, 222}, {0x059B, 220}, {0x059C, 230}, {0x05A2, 220}, {0x05A8, 230}, {0x05AA, 220}, {0x05AB, 230}, {0x05AD, 222}, {0x05AE, 228}, {0x05AF, 230}, {0x05B0, 10}, {0x05B1, 11}, {0x05B2, 12}, {0x05B3, 13}, {0x05B4, 14}, {0x05B5, 15}, {0x05B6, 16}, {0x05B7, 17}, {0x05B8, 18}, {0x05B9, 19}, {0x05BB, 20}, {0x05BC, 21}, {0x05BD, 22}, {0x05BE, 0}, {0x05BF, 23}, {0x05C0, 0}, {0x05C1, 24}, {0x05C2, 25}, {0x05C3, 0}, {0x05C4, 230}, {0x05C5, 220}, {0x05C6, 0}, {0x05C7, 18}, {0x05D0, 0}, {0x0610, 230}, {0x0618, 30}, {0x0619, 31}, {0x061A, 32}, {0x061B, 0}, {0x064B, 27}, {0x064C, 28}, {0x064D, 29}, {0x064E, 30}, {0x064F, 31}, {0x0650, 32}, {0x0651, 33}, {0x0652, 34}, {0x0653, 230}, {0x0655, 220}, {0x0657, 230}, {0x065C, 220}, {0x065D, 230}, {0x065F, 220}, {0x0660, 0}, {0x0670, 35}, {0x0671, 0}, {0x06D6, 230}, {0x06DD, 0}, {0x06DF, 230}, {0x06E3, 220}, {0x06E4, 230}, {0x06E5, 0}, {0x06E7, 230}, {0x06E9, 0}, {0x06EA, 220}, {0x06EB, 230}, {0x06ED, 220}, {0x06EE, 0}, {0x0711, 36}, {0x0712, 0}, {0x0730, 230}, {0x0731, 220}, {0x0732, 230}, {0x0734, 220}, {0x0735, 230}, {0x0737, 220}, {0x073A, 230}, {0x073B, 220}, {0x073D, 230}, {0x073E, 220}, {0x073F, 230}, {0x0742, 220}, {0x0743, 230}, {0x0744, 220}, {0x0745, 230}, {0x0746, 220}, {0x0747, 230}, {0x0748, 220}, {0x0749, 230}, {0x074D, 0}, {0x07EB, 230}, {0x07F2, 220}, {0x07F3, 230}, {0x07F4, 0}, {0x07FD, 220}, {0x07FE, 0}, {0x0816, 230}, {0x081A, 0}, {0x081B, 230}, {0x0824, 0}, {0x0825, 230}, {0x0828, 0}, {0x0829, 230}, {0x0830, 0}, {0x0859, 220}, {0x085E, 0}, {0x0898, 230}, {0x0899, 220}, {0x089C, 230}, {0x08A0, 0}, {0x08CA, 230}, {0x08CF, 220}, {0x08D4, 230}, {0x08E2, 0}, {0x08E3, 220}, {0x08E4, 230}, {0x08E6, 220}, {0x08E7, 230}, {0x08E9, 220}, {0x08EA, 230}, {0x08ED, 220}, {0x08F0, 27}, {0x08F1, 28}, {0x08F2, 29}, {0x08F3, 230}, {0x08F6, 220}, {0x08F7, 230}, {0x08F9, 220}, {0x08FB, 230}, {0x0900, 0}, {0x093C, 7}, {0x093D, 0}, {0x094D, 9}, {0x094E, 0}, {0x0951, 230}, {0x0952, 220}, {0x0953, 230}, {0x0955, 0}, {0x09BC, 7}, {0x09BD, 0}, {0x09CD, 9}, {0x09CE, 0}, {0x09FE, 230}, {0x0A01, 0}, {0x0A3C, 7}, {0x0A3E, 0}, {0x0A4D, 9}, {0x0A51, 0}, {0x0ABC, 7}, {0x0ABD, 0}, {0x0ACD, 9}, {0x0AD0, 0}, {0x0B3C, 7}, {0x0B3D, 0}, {0x0B4D, 9}, {0x0B55, 0}, {0x0BCD, 9}, {0x0BD0, 0}, {0x0C3C, 7}, {0x0C3D, 0}, {0x0C4D, 9}, {0x0C55, 84}, {0x0C56, 91}, {0x0C58, 0}, {0x0CBC, 7}, {0x0CBD, 0}, {0x0CCD, 9}, {0x0CD5, 0}, {0x0D3B, 9}, {0x0D3D, 0}, {0x0D4D, 9}, {0x0D4E, 0}, {0x0DCA, 9}, {0x0DCF, 0}, {0x0E38, 103}, {0x0E3A, 9}, {0x0E3F, 0}, {0x0E48, 107}, {0x0E4C, 0}, {0x0EB8, 118}, {0x0EBA, 9}, {0x0EBB, 0}, {0x0EC8, 122}, {0x0ECC, 0}, {0x0F18, 220}, {0x0F1A, 0}, {0x0F35, 220}, {0x0F36, 0}, {0x0F37, 220}, {0x0F38, 0}, {0x0F39, 216}, {0x0F3A, 0}, {0x0F71, 129}, {0x0F72, 130}, {0x0F73, 0}, {0x0F74, 132}, {0x0F75, 0}, {0x0F7A, 130}, {0x0F7E, 0}, {0x0F80, 130}, {0x0F81, 0}, {0x0F82, 230}, {0x0F84, 9}, {0x0F85, 0}, {0x0F86, 230}, {0x0F88, 0}, {0x0FC6, 220}, {0x0FC7, 0}, {0x1037, 7}, {0x1038, 0}, {0x1039, 9}, {0x103B, 0}, {0x108D, 220}, {0x108E, 0}, {0x135D, 230}, {0x1360, 0}, {0x1714, 9}, {0x171F, 0}, {0x1734, 9}, {0x1735, 0}, {0x17D2, 9}, {0x17D3, 0}, {0x17DD, 230}, {0x17E0, 0}, {0x18A9, 228}, {0x18AA, 0}, {0x1939, 222}, {0x193A, 230}, {0x193B, 220}, {0x1940, 0}, {0x1A17, 230}, {0x1A18, 220}, {0x1A19, 0}, {0x1A60, 9}, {0x1A61, 0}, {0x1A75, 230}, {0x1A7F, 220}, {0x1A80, 0}, {0x1AB0, 230}, {0x1AB5, 220}, {0x1ABB, 230}, {0x1ABD, 220}, {0x1ABE, 0}, {0x1ABF, 220}, {0x1AC1, 230}, {0x1AC3, 220}, {0x1AC5, 230}, {0x1ACA, 220}, {0x1ACB, 230}, {0x1B00, 0}, {0x1B34, 7}, {0x1B35, 0}, {0x1B44, 9}, {0x1B45, 0}, {0x1B6B, 230}, {0x1B6C, 220}, {0x1B6D, 230}, {0x1B74, 0}, {0x1BAA, 9}, {0x1BAC, 0}, {0x1BE6, 7}, {0x1BE7, 0}, {0x1BF2, 9}, {0x1BFC, 0}, {0x1C37, 7}, {0x1C3B, 0}, {0x1CD0, 230}, {0x1CD3, 0}, {0x1CD4, 1}, {0x1CD5, 220}, {0x1CDA, 230}, {0x1CDC, 220}, {0x1CE0, 230}, {0x1CE1, 0}, {0x1CE2, 1}, {0x1CE9, 0}, {0x1CED, 220}, {0x1CEE, 0}, {0x1CF4, 230}, {0x1CF5, 0}, {0x1CF8, 230}, {0x1CFA, 0}, {0x1DC0, 230}, {0x1DC2, 220}, {0x1DC3, 230}, {0x1DCA, 220}, {0x1DCB, 230}, {0x1DCD, 234}, {0x1DCE, 214}, {0x1DCF, 220}, {0x1DD0, 202}, {0x1DD1, 230}, {0x1DF6, 232}, {0x1DF7, 228}, {0x1DF9, 220}, {0x1DFA, 218}, {0x1DFB, 230}, {0x1DFC, 233}, {0x1DFD, 220}, {0x1DFE, 230}, {0x1DFF, 220}, {0x1E00, 0}, {0x20D0, 230}, {0x20D2, 1}, {0x20D4, 230}, {0x20D8, 1}, {0x20DB, 230}, {0x20DD, 0}, {0x20E1, 230}, {0x20E2, 0}, {0x20E5, 1}, {0x20E7, 230}, {0x20E8, 220}, {0x20E9, 230}, {0x20EA, 1}, {0x20EC, 220}, {0x20F0, 230}, {0x2100, 0}, {0x2CEF, 230}, {0x2CF2, 0}, {0x2D7F, 9}, {0x2D80, 0}, {0x2DE0, 230}, {0x2E00, 0}, {0x302A, 218}, {0x302B, 228}, {0x302C, 232}, {0x302D, 222}, {0x302E, 224}, {0x3030, 0}, {0x3099, 8}, {0x309B, 0}, {0xA66F, 230}, {0xA670, 0}, {0xA674, 230}, {0xA67E, 0}, {0xA69E, 230}, {0xA6A0, 0}, {0xA6F0, 230}, {0xA6F2, 0}, {0xA806, 9}, {0xA807, 0}, {0xA82C, 9}, {0xA830, 0}, {0xA8C4, 9}, {0xA8C5, 0}, {0xA8E0, 230}, {0xA8F2, 0}, {0xA92B, 220}, {0xA92E, 0}, {0xA953, 9}, {0xA95F, 0}, {0xA9B3, 7}, {0xA9B4, 0}, {0xA9C0, 9}, {0xA9C1, 0}, {0xAAB0, 230}, {0xAAB1, 0}, {0xAAB2, 230}, {0xAAB4, 220}, {0xAAB5, 0}, {0xAAB7, 230}, {0xAAB9, 0}, {0xAABE, 230}, {0xAAC0, 0}, {0xAAC1, 230}, {0xAAC2, 0}, {0xAAF6, 9}, {0xAB01, 0}, {0xABED, 9}, {0xABF0, 0}, {0xFB1E, 26}, {0xFB1F, 0}, {0xFE20, 230}, {0xFE27, 220}, {0xFE2E, 230}, {0xFE30, 0}, {0x101FD, 220}, {0x10280, 0}, {0x102E0, 220}, {0x102E1, 0}, {0x10376, 230}, {0x10380, 0}, {0x10A0D, 220}, {0x10A0E, 0}, {0x10A0F, 230}, {0x10A10, 0}, {0x10A38, 230}, {0x10A39, 1}, {0x10A3A, 220}, {0x10A3F, 9}, {0x10A40, 0}, {0x10AE5, 230}, {0x10AE6, 220}, {0x10AEB, 0}, {0x10D24, 230}, {0x10D30, 0}, {0x10EAB, 230}, {0x10EAD, 0}, {0x10EFD, 220}, {0x10F00, 0}, {0x10F46, 220}, {0x10F48, 230}, {0x10F4B, 220}, {0x10F4C, 230}, {0x10F4D, 220}, {0x10F51, 0}, {0x10F82, 230}, {0x10F83, 220}, {0x10F84, 230}, {0x10F85, 220}, {0x10F86, 0}, {0x11046, 9}, {0x11047, 0}, {0x11070, 9}, {0x11071, 0}, {0x1107F, 9}, {0x11080, 0}, {0x110B9, 9}, {0x110BA, 7}, {0x110BB, 0}, {0x11100, 230}, {0x11103, 0}, {0x11133, 9}, {0x11136, 0}, {0x11173, 7}, {0x11174, 0}, {0x111C0, 9}, {0x111C1, 0}, {0x111CA, 7}, {0x111CB, 0}, {0x11235, 9}, {0x11236, 7}, {0x11237, 0}, {0x112E9, 7}, {0x112EA, 9}, {0x112F0, 0}, {0x1133B, 7}, {0x1133D, 0}, {0x1134D, 9}, {0x11350, 0}, {0x11366, 230}, {0x11400, 0}, {0x11442, 9}, {0x11443, 0}, {0x11446, 7}, {0x11447, 0}, {0x1145E, 230}, {0x1145F, 0}, {0x114C2, 9}, {0x114C3, 7}, {0x114C4, 0}, {0x115BF, 9}, {0x115C0, 7}, {0x115C1, 0}, {0x1163F, 9}, {0x11640, 0}, {0x116B6, 9}, {0x116B7, 7}, {0x116B8, 0}, {0x1172B, 9}, {0x11730, 0}, {0x11839, 9}, {0x1183A, 7}, {0x1183B, 0}, {0x1193D, 9}, {0x1193F, 0}, {0x11943, 7}, {0x11944, 0}, {0x119E0, 9}, {0x119E1, 0}, {0x11A34, 9}, {0x11A35, 0}, {0x11A47, 9}, {0x11A50, 0}, {0x11A99, 9}, {0x11A9A, 0}, {0x11C3F, 9}, {0x11C40, 0}, {0x11D42, 7}, {0x11D43, 0}, {0x11D44, 9}, {0x11D46, 0}, {0x11D97, 9}, {0x11D98, 0}, {0x11F41, 9}, {0x11F43, 0}, {0x16AF0, 1}, {0x16AF5, 0}, {0x16B30, 230}, {0x16B37, 0}, {0x16FF0, 6}, {0x17000, 0}, {0x1BC9E, 1}, {0x1BC9F, 0}, {0x1D165, 216}, {0x1D167, 1}, {0x1D16A, 0}, {0x1D16D, 226}, {0x1D16E, 216}, {0x1D173, 0}, {0x1D17B, 220}, {0x1D183, 0}, {0x1D185, 230}, {0x1D18A, 220}, {0x1D18C, 0}, {0x1D1AA, 230}, {0x1D1AE, 0}, {0x1D242, 230}, {0x1D245, 0}, {0x1E000, 230}, {0x1E030, 0}, {0x1E08F, 230}, {0x1E100, 0}, {0x1E130, 230}, {0x1E137, 0}, {0x1E2AE, 230}, {0x1E2C0, 0}, {0x1E2EC, 230}, {0x1E2F0, 0}, {0x1E4EC, 232}, {0x1E4EE, 220}, {0x1E4EF, 230}, {0x1E4F0, 0}, {0x1E8D0, 220}, {0x1E900, 0}, {0x1E944, 230}, {0x1E94A, 7}, {0x1E94B, 0} }; // clang-format off uint_fast8_t GetCanonicalCombiningClass(UTF32 c) { auto it = CCC_TABLE.lower_bound(c); CJC_ASSERT(it != CCC_TABLE.cend()); return it->second; } NfcQcResult NfcQuickCheck(const std::string& s) { uint_fast8_t lastcc{0}; NfcQcResult res{NfcQcResult::YES}; const UTF8* cur{reinterpret_cast(s.c_str())}; const UTF8* end{reinterpret_cast(s.c_str() + s.size())}; while (cur != end) { UTF32 ch{ReadOneUnicodeChar(&cur, end)}; // cur is advanced as reading unicode char if (ch <= '\x7f') { lastcc = 0; continue; } uint_fast8_t cc = GetCanonicalCombiningClass(ch); if (lastcc > cc && cc != 0) { return NfcQcResult::NO; } auto check = IsAllowed(ch); if (check == NfcQcResult::NO) { return NfcQcResult::NO; } if (check == NfcQcResult::MAYBE) { res = NfcQcResult::MAYBE; } lastcc = cc; } return res; }; bool IsNFC(const std::string& s) { auto res = NfcQuickCheck(s); if (res == NfcQcResult::YES) { return true; } if (res == NfcQcResult::NO) { return false; } return CanonicalRecompose(s) == s; } void NFC(std::string& s) { // it is very much faster to check whether a string has already conformed to a normal form than to convert it. // quick exit if already conforms if (auto res = NfcQuickCheck(s); res == NfcQcResult::YES) { return; } s = CanonicalRecompose(s); } std::vector StringRef::ToUTF32() const { auto sourceStart = begin(); auto sourceEnd = begin() + Size(); std::vector res(static_cast(static_cast(Size()))); auto targetStart = res.data(); auto targetEnd = res.data() + res.size(); auto cr = ConvertUTF8toUTF32(&sourceStart, sourceEnd, &targetStart, targetEnd); if (cr != ConversionResult::OK) { // if UTF8 conversion fails, copy the remaining source UTF8 sequence to the result for (auto sourceLeft{sourceStart}; sourceLeft != sourceEnd; ++sourceLeft) { *targetStart++ = *sourceLeft; } // do not return here, the result still need resize } res.resize(static_cast(targetStart - res.data())); return res; } bool IsLegalUnicode(UTF32 c) { const UTF32 unicodeTop{0xd7ff}; const UTF32 surrogateBegin{0xe000}; const UTF32 surrogateEnd{0x10ffff}; return c <= unicodeTop || (c >= surrogateBegin && c <= surrogateEnd); } } // namespace Cangjie::Unicode cangjie_compiler-1.0.7/src/Utils/UnicodeNFC.cpp000066400000000000000000000322031510705540100213170ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file provides unicode normalization functions. */ #include "cangjie/Utils/Unicode.h" #include #include #include #include "cangjie/Utils/CheckUtils.h" #include "UnicodeTables/NormalisationData.generated.inc" namespace Cangjie::Unicode { namespace { uint64_t MyHash(UTF32 key, UTF32 salt, uint64_t n) { UTF32 t1; (void)__builtin_uadd_overflow(key, salt, &t1); constexpr auto other = 2654435769; UTF32 y; (void)__builtin_umul_overflow(t1, other, &y); constexpr auto other2 = 0x31415926; UTF32 t2; (void)__builtin_umul_overflow(key, other2, &t2); y = y ^ t2; return (static_cast(y) * static_cast(n)) >> (sizeof(uint32_t) * CHAR_BIT); } template std::optional MetaLookup(UTF32 x, const short (&salt)[saltLen], const std::pair* kv, Key (*keyProjection)(const std::pair&), std::optional (*valueProjection)(const std::pair&), std::optional defaultVal = std::nullopt) { UTF32 s = static_cast(salt[MyHash(x, 0, saltLen)]); auto& keyValuePair = kv[MyHash(x, s, saltLen)]; return x == keyProjection(keyValuePair) ? valueProjection(keyValuePair) : defaultVal; } template inline UTF32 PairLookupFk(const std::pair& kv) { return kv.first; } template inline std::optional PairLookupFvOpt(const std::pair& kv) { return {kv.second}; } } std::optional CompositionTable(UTF32 c1, UTF32 c2) { constexpr UTF32 m = 0x10000; constexpr UTF32 m2 = 16; if (c1 < m && c2 < m) { return MetaLookup((c1 << m2) | c2, COMPOSITION_TABLE_SALT, COMPOSITION_TABLE_KV, PairLookupFk, PairLookupFvOpt, std::optional{std::nullopt}); } return CompositionTableAstral(c1, c2); } namespace { ///@{ /** See Unicode 15.0.0 3.12 Hangul Syllable Decomposition Common Constants */ constexpr UTF32 SBASE = 0xAC00; constexpr UTF32 LBASE = 0x1100; constexpr UTF32 VBASE = 0x1161; constexpr UTF32 TBASE = 0x11A7; constexpr UTF32 LCOUNT = 19; constexpr UTF32 VCOUNT = 21; constexpr UTF32 TCOUNT = 28; constexpr UTF32 NCOUNT = VCOUNT * TCOUNT; constexpr UTF32 SCOUNT = LCOUNT * NCOUNT; constexpr UTF32 SLAST = SBASE + SCOUNT - 1; constexpr UTF32 LLAST = LBASE + LCOUNT - 1; constexpr UTF32 VLAST = VBASE + VCOUNT - 1; constexpr UTF32 TLAST = TBASE + TCOUNT - 1; constexpr UTF32 TFIRST = TBASE + 1; ///@} bool IsHangulSyllable(UTF32 c) { return static_cast(c) >= SBASE && static_cast(c) < SBASE + SCOUNT; } void DecomposeHangul(UTF32 c, std::function&& emitChar) { // at most 0x2baf, the size of Hangul Syllables block auto sindex = c - SBASE; // at most 0x2baf / (21 * 28) = 19 auto lindex = sindex / NCOUNT; // at most 0x1113 emitChar(LBASE + lindex); // at most 21 auto vindex = (sindex % NCOUNT) / TCOUNT; // at most 0x1176 emitChar(VBASE + vindex); // at most 27 auto tindex = sindex % TCOUNT; if (tindex > 0) { emitChar(TBASE + tindex); } } } std::optional ComposeHangul(UTF32 a, UTF32 b) { if (a >= LBASE && a <= LLAST && b >= VBASE && b <= VLAST) { auto lindex = a - LBASE; auto vindex = b - VBASE; auto lvindex = lindex * NCOUNT + vindex * TCOUNT; auto s = SBASE + lvindex; return {s}; } if (a >= SBASE && a <= SLAST && b >= TFIRST && b <= TLAST && (a - SBASE) % TCOUNT == 0) { return {a + b - TBASE}; } return {}; } namespace { template void Decompose(UTF32 c, D&& decomposeChar, std::function&& emitChar) { // ASCII never decomposes if (c <= '\x7f') { emitChar(c); return; } if (IsHangulSyllable(c)) { DecomposeHangul(c, std::move(emitChar)); return; } if (auto d = decomposeChar(c)) { for (auto it = d->first; it != d->second; ++it) { emitChar(*it); } return; } emitChar(c); } std::optional> CanonicalFullyDecomposed(UTF32 c) { auto r = MetaLookup(c, CANONICAL_DECOMPOSED_SALT, CANONICAL_DECOMPOSED_KV, PairLookupFk, PairLookupFvOpt, std::optional>{std::nullopt}); if (!r) { return std::nullopt; } return std::pair{CANONICAL_DECOMPOSED_CHARS + r->first, CANONICAL_DECOMPOSED_CHARS + r->first + r->second}; } template void DecomposeCanonical(UTF32 c, F&& emitChar) { Decompose(c, CanonicalFullyDecomposed, emitChar); } /** Range class, upper exclusive. */ template struct Range { static_assert(std::is_integral_v); T begin; T end; }; template std::ostream& operator<<(std::ostream& out, const std::optional& value) { if (value) { out << std::string_view{"Some("}; out << *value; out << std::string_view{")"}; } else { out << std::string_view{"None"}; } return out; } template std::ostream& operator<<(std::ostream& out, const std::vector& value) { for (auto v:value) { out<> buffer; Range ready; explicit Decompositions(const std::string& s): begin{reinterpret_cast(s.c_str())}, end{reinterpret_cast(s.c_str() + s.size())}, buffer{}, ready{0, 0} {} Decompositions(const UTF8* b, const UTF8* e) : begin{b}, end{e}, buffer{}, ready{0, 0} {} std::vector Get() && { cur = begin; std::vector res; std::optional k; while ((k = Next())) { res.push_back(*k); } return res; } std::optional Next() { while (ready.end == 0) { if (cur < end) { UTF32 codepoint{ReadOneUnicodeChar(&cur, end)}; DecomposeCanonical(codepoint, [this](UTF32 p) { PushBack(p); }); } else { if (buffer.empty()) { // std::cout<<"Decompose "<<0<<" return "<<"None\n"; return {}; } else { SortPending(); ready.end = buffer.size(); break; } } } auto ch = buffer[ready.begin].second; IncrementNextReady(); // std::cout<<"Decompose "<<1<<" return "<& a, const std::pair& b) { return a.first < b.first; } void SortPending() { if (ready.end < buffer.size()) { std::stable_sort(buffer.begin() + static_cast(ready.end), buffer.end(), F); } } void ResetBuffer() { auto pending = buffer.size() - ready.end; for (size_t i{0}; i < pending; ++i) { buffer[i] = buffer[i + ready.end]; } buffer.erase(buffer.begin() + static_cast(pending), buffer.end()); ready = {0, 0}; } void IncrementNextReady() { auto next = ready.begin + 1; if (next == ready.end) { ResetBuffer(); } else { ready.begin = next; } } }; std::optional Compose(UTF32 a, UTF32 b) { if (auto h = ComposeHangul(a, b)) { return h; } return CompositionTable(a, b); } struct RecompositionState { size_t sz; enum class V { COMPOSING, PURGING, FINISHED } v; }; struct Recompositions { ArrayRef source; RecompositionState state; std::vector buffer; std::optional composee; std::optional lastccc; explicit Recompositions(ArrayRef s): source{s}, state{0, RecompositionState::V::COMPOSING} {} std::string Get() && { std::string res{}; std::optional c{}; cur = source.begin(); while ((c = Next())) { char r[UNI_MAX_UTF8_BYTES_PER_CODE_POINT]; char* endPtr{r}; bool convRes = ConvertCodepointToUTF8(*c, endPtr); CJC_ASSERT(convRes); for (char* k{r}; k < endPtr; ++k) { res.push_back(*k); } } return res; } private: // current position of inner iterator const UTF32* cur{nullptr}; std::optional Next() { auto end{source.end()}; while (true) { #if defined __GNUC__ && not defined __clang__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-default" #endif switch (state.v) { case RecompositionState::V::COMPOSING: { while (cur != end) { UTF32 ch = *cur++; auto cc = GetCanonicalCombiningClass(ch); UTF32 k; if (composee) { k = *composee; } else { if (cc != 0) { return {ch}; } composee = {ch}; continue; } if (!lastccc) { auto comp = Compose(k, ch); if (comp) { composee = comp; continue; } else { if (cc == 0) { composee = {ch}; return {k}; } buffer.push_back(ch); lastccc = {cc}; } } else { auto lclass = *lastccc; if (lclass >= cc) { // ch is blocked from composee if (cc == 0) { composee = {ch}; lastccc = {}; state = {0, RecompositionState::V::PURGING}; return {k}; } buffer.push_back(ch); lastccc = {cc}; continue; } auto comp = Compose(k, ch); if (comp) { composee = comp; continue; } else { buffer.push_back(ch); lastccc = cc; } } } state = {0, RecompositionState::V::FINISHED}; if (composee) { auto b = composee; composee = {}; return b; } break; } case RecompositionState::V::PURGING: if (buffer.size() <= state.sz) { buffer.clear(); state.v = RecompositionState::V::COMPOSING; } else { return buffer[state.sz++]; } break; case RecompositionState::V::FINISHED: if (buffer.size() <= state.sz) { buffer.clear(); auto comp = composee; composee = {}; return comp; } else { return buffer[state.sz++]; } } } #if defined __GNUC__ && not defined __clang__ #pragma GCC diagnostic pop #endif } }; } std::string CanonicalDecompose(const std::string& s) { Decompositions d{s}; auto decomposed = std::move(d).Get(); std::string res; ConvertUTF32ToUTF8String(decomposed, res); return res; } std::string CanonicalRecompose(const std::string& s) { Decompositions d{s}; auto decomposed = std::move(d).Get(); Recompositions r{decomposed}; return std::move(r).Get(); } std::string CanonicalRecompose(const UTF8* begin, const UTF8* end) { Decompositions d{begin, end}; auto decomposed = std::move(d).Get(); Recompositions r{decomposed}; return std::move(r).Get(); } } cangjie_compiler-1.0.7/src/Utils/UnicodeTables/000077500000000000000000000000001510705540100214175ustar00rootroot00000000000000cangjie_compiler-1.0.7/src/Utils/UnicodeTables/NormalisationData.generated.inc000066400000000000000000003704611510705540100274730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file provides unicode normalisation data. */ // NOTE: The following code was generated by "NormalisationTests.py", do not edit directly #ifndef CANGJIE_UTILS_UNICODETABLES_NORMALISATIONDATA_H #define CANGJIE_UTILS_UNICODETABLES_NORMALISATIONDATA_H namespace Cangjie::Unicode { namespace { constexpr short COMPOSITION_TABLE_SALT[] { 0x000, 0x000, 0x000, 0x000, 0x25D, 0x003, 0x05D, 0x000, 0x002, 0x009, 0x004, 0x087, 0x006, 0x135, 0x001, 0x000, 0x000, 0x004, 0x003, 0x0E1, 0x078, 0x000, 0x002, 0x029, 0x008, 0x08F, 0x024, 0x000, 0x000, 0x000, 0x031, 0x000, 0x000, 0x06D, 0x000, 0x033, 0x001, 0x04C, 0x000, 0x001, 0x000, 0x000, 0x008, 0x07C, 0x006, 0x04C, 0x045, 0x02A, 0x002, 0x000, 0x000, 0x000, 0x00A, 0x000, 0x000, 0x059, 0x000, 0x010, 0x003, 0x009, 0x00E, 0x006, 0x001, 0x00C, 0x010, 0x000, 0x000, 0x000, 0x03D, 0x000, 0x000, 0x02A, 0x000, 0x000, 0x001, 0x020, 0x00A, 0x000, 0x001, 0x00E, 0x002, 0x010, 0x022, 0x000, 0x061, 0x000, 0x004, 0x015, 0x000, 0x004, 0x003, 0x000, 0x000, 0x003, 0x000, 0x00D, 0x001, 0x001, 0x024, 0x003, 0x004, 0x000, 0x000, 0x018, 0x058, 0x000, 0x000, 0x00A, 0x019, 0x010, 0x000, 0x002, 0x000, 0x000, 0x012, 0x015, 0x000, 0x00C, 0x007, 0x003, 0x000, 0x01A, 0x007, 0x00C, 0x001, 0x005, 0x004, 0x021, 0x001, 0x002, 0x000, 0x000, 0x001, 0x00A, 0x000, 0x00A, 0x000, 0x004, 0x009, 0x005, 0x00D, 0x005, 0x000, 0x003, 0x006, 0x026, 0x00B, 0x000, 0x004, 0x006, 0x000, 0x013, 0x000, 0x000, 0x004, 0x001, 0x000, 0x000, 0x000, 0x024, 0x001, 0x01D, 0x001, 0x000, 0x000, 0x010, 0x000, 0x001, 0x001, 0x001, 0x000, 0x000, 0x00A, 0x000, 0x00D, 0x000, 0x010, 0x000, 0x006, 0x005, 0x000, 0x000, 0x009, 0x003, 0x001, 0x000, 0x000, 0x000, 0x000, 0x002, 0x001, 0x002, 0x000, 0x000, 0x016, 0x001, 0x002, 0x000, 0x001, 0x000, 0x006, 0x001, 0x025, 0x005, 0x000, 0x00A, 0x00D, 0x002, 0x000, 0x001, 0x000, 0x000, 0x012, 0x000, 0x000, 0x001, 0x000, 0x001, 0x000, 0x001, 0x000, 0x004, 0x009, 0x000, 0x001, 0x004, 0x000, 0x004, 0x001, 0x006, 0x00B, 0x003, 0x006, 0x007, 0x000, 0x000, 0x000, 0x00D, 0x00F, 0x00E, 0x000, 0x001, 0x001, 0x004, 0x00F, 0x010, 0x000, 0x005, 0x000, 0x019, 0x001, 0x00E, 0x000, 0x008, 0x001, 0x003, 0x000, 0x000, 0x005, 0x001, 0x000, 0x000, 0x011, 0x000, 0x004, 0x005, 0x000, 0x001, 0x000, 0x000, 0x000, 0x000, 0x002, 0x001, 0x000, 0x000, 0x002, 0x001, 0x001, 0x005, 0x008, 0x000, 0x000, 0x00C, 0x00A, 0x000, 0x006, 0x009, 0x003, 0x001, 0x006, 0x001, 0x000, 0x01B, 0x001, 0x00E, 0x000, 0x008, 0x005, 0x003, 0x015, 0x000, 0x002, 0x007, 0x00B, 0x003, 0x000, 0x00A, 0x003, 0x000, 0x006, 0x000, 0x00A, 0x000, 0x001, 0x003, 0x008, 0x000, 0x009, 0x000, 0x001, 0x002, 0x000, 0x005, 0x000, 0x001, 0x000, 0x000, 0x000, 0x017, 0x000, 0x000, 0x002, 0x000, 0x005, 0x016, 0x000, 0x010, 0x003, 0x004, 0x011, 0x001, 0x003, 0x000, 0x000, 0x000, 0x004, 0x001, 0x000, 0x005, 0x000, 0x002, 0x001, 0x000, 0x002, 0x000, 0x002, 0x002, 0x000, 0x000, 0x000, 0x000, 0x002, 0x000, 0x012, 0x001, 0x004, 0x00A, 0x00B, 0x004, 0x000, 0x002, 0x000, 0x003, 0x000, 0x004, 0x005, 0x000, 0x000, 0x011, 0x001, 0x006, 0x006, 0x000, 0x002, 0x000, 0x001, 0x000, 0x004, 0x000, 0x000, 0x001, 0x001, 0x007, 0x000, 0x003, 0x00A, 0x000, 0x001, 0x00A, 0x002, 0x000, 0x001, 0x000, 0x000, 0x00D, 0x005, 0x001, 0x001, 0x000, 0x000, 0x005, 0x006, 0x008, 0x004, 0x005, 0x000, 0x00B, 0x000, 0x004, 0x00B, 0x000, 0x000, 0x001, 0x005, 0x000, 0x00B, 0x000, 0x006, 0x000, 0x000, 0x000, 0x000, 0x001, 0x000, 0x004, 0x000, 0x006, 0x000, 0x000, 0x007, 0x013, 0x004, 0x019, 0x003, 0x001, 0x000, 0x001, 0x006, 0x006, 0x000, 0x000, 0x000, 0x000, 0x00C, 0x002, 0x003, 0x000, 0x004, 0x000, 0x001, 0x000, 0x001, 0x005, 0x001, 0x001, 0x002, 0x001, 0x000, 0x000, 0x000, 0x000, 0x009, 0x000, 0x000, 0x007, 0x001, 0x009, 0x006, 0x004, 0x001, 0x005, 0x000, 0x001, 0x002, 0x008, 0x000, 0x009, 0x000, 0x000, 0x001, 0x003, 0x00B, 0x000, 0x001, 0x000, 0x000, 0x005, 0x000, 0x000, 0x000, 0x000, 0x000, 0x003, 0x000, 0x002, 0x001, 0x002, 0x000, 0x000, 0x000, 0x004, 0x001, 0x000, 0x006, 0x002, 0x005, 0x008, 0x012, 0x000, 0x000, 0x000, 0x003, 0x001, 0x000, 0x000, 0x000, 0x002, 0x003, 0x003, 0x000, 0x000, 0x001, 0x000, 0x001, 0x000, 0x003, 0x000, 0x000, 0x002, 0x001, 0x004, 0x00D, 0x000, 0x000, 0x00B, 0x000, 0x001, 0x000, 0x000, 0x00C, 0x000, 0x000, 0x000, 0x005, 0x008, 0x002, 0x000, 0x002, 0x001, 0x006, 0x001, 0x001, 0x001, 0x000, 0x002, 0x000, 0x005, 0x001, 0x001, 0x000, 0x002, 0x000, 0x000, 0x000, 0x000, 0x00E, 0x010, 0x000, 0x005, 0x004, 0x002, 0x001, 0x000, 0x000, 0x003, 0x001, 0x000, 0x002, 0x001, 0x004, 0x000, 0x000, 0x003, 0x000, 0x001, 0x001, 0x003, 0x001, 0x005, 0x002, 0x003, 0x004, 0x000, 0x003, 0x003, 0x006, 0x000, 0x000, 0x001, 0x000, 0x002, 0x00E, 0x001, 0x002, 0x001, 0x004, 0x002, 0x005, 0x002, 0x001, 0x001, 0x000, 0x001, 0x000, 0x005, 0x005, 0x006, 0x006, 0x005, 0x000, 0x002, 0x001, 0x000, 0x002, 0x004, 0x001, 0x000, 0x005, 0x000, 0x006, 0x002, 0x008, 0x000, 0x000, 0x007, 0x001, 0x002, 0x002, 0x005, 0x004, 0x011, 0x000, 0x003, 0x000, 0x001, 0x000, 0x001, 0x004, 0x000, 0x003, 0x002, 0x000, 0x001, 0x003, 0x000, 0x00B, 0x006, 0x000, 0x006, 0x000, 0x004, 0x001, 0x002, 0x000, 0x001, 0x000, 0x002, 0x001, 0x002, 0x000, 0x005, 0x001, 0x000, 0x000, 0x000, 0x001, 0x003, 0x002, 0x006, 0x000, 0x000, 0x002, 0x004, 0x006, 0x000, 0x000, 0x000, 0x000, 0x00C, 0x00C, 0x000, 0x000, 0x000, 0x004, 0x004, 0x000, 0x008, 0x003, 0x001, 0x000, 0x001, 0x000, 0x003, 0x000, 0x000, 0x005, 0x000, 0x005, 0x001, 0x000, 0x003, 0x004, 0x001, 0x002, 0x003, 0x000, 0x001, 0x007, 0x000, 0x000, 0x007, 0x000, 0x002, 0x002, 0x000, 0x000, 0x001, 0x001, 0x001, 0x001, 0x00B, 0x000, 0x004, 0x000, 0x001, 0x001, 0x000, 0x002, 0x001, 0x001, 0x005, 0x000, 0x000, 0x003, 0x006, 0x000, 0x000, 0x004, 0x002, 0x001, 0x003, 0x003, 0x00A, 0x007, 0x000, 0x004, 0x001, 0x006, 0x000, 0x001, 0x000, 0x000, 0x002, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x002, 0x001, 0x001, 0x001, 0x004, 0x000, 0x003, 0x000, 0x001, 0x000, 0x000, 0x001, 0x000, 0x001, 0x000, 0x004, 0x000, 0x001, 0x000, 0x006, 0x000, 0x001, 0x000, 0x004, 0x000, 0x001, 0x002, 0x006, 0x000, 0x002, 0x000, 0x003, 0x007, 0x000, 0x003, 0x000, 0x001, 0x001, 0x000, 0x000, 0x003, 0x005, 0x002, 0x000, 0x001, 0x001, 0x002, 0x002, 0x004, 0x001, 0x000, 0x000, 0x001, 0x000, 0x003, 0x002, 0x000, 0x001, 0x000, 0x002, 0x004, 0x002, 0x000, 0x001, 0x005, 0x000, 0x000, 0x002, 0x001, 0x001, 0x000, 0x000, 0x001, 0x000, 0x005, 0x002, 0x000, 0x008, 0x003, 0x001, 0x001, 0x001, 0x003, 0x004, 0x000, 0x002, 0x000, 0x001, 0x002, 0x000, 0x000, 0x000, 0x000, 0x000, 0x001, 0x000, 0x006, 0x000, 0x000, 0x000, 0x003, 0x000, 0x000, 0x002, 0x002, 0x000, 0x001, 0x003, 0x001, 0x003, 0x001, 0x002, 0x000, 0x001, 0x002, 0x000, 0x000, 0x001, 0x000, 0x003, 0x001, 0x002, 0x000, 0x002, 0x001, 0x000, 0x002, 0x004, 0x000, 0x005, 0x005, 0x000, 0x005, 0x002, 0x000, 0x001, 0x003, 0x001, 0x000, 0x002, 0x006, 0x002, 0x001, 0x000, 0x000, 0x000, 0x003 }; /** Key: codepoint Value: first: index into COMPOSITION_TABLE_CHARS, second: length */ constexpr std::pair COMPOSITION_TABLE_KV[] { {0x064A0654, 0x000626}, {0x00690309, 0x001EC9}, {0x00650306, 0x000115}, {0x0423030B, 0x0004F2}, {0x0047030C, 0x0001E6}, {0x00570301, 0x001E82}, {0x00C50301, 0x0001FA}, {0x21D20338, 0x0021CF}, {0x00430307, 0x00010A}, {0x007A0307, 0x00017C}, {0x0049030C, 0x0001CF}, {0x00720307, 0x001E59}, {0x304B3099, 0x00304C}, {0x1F610345, 0x001FA1}, {0x00410307, 0x000226}, {0x006B0323, 0x001E33}, {0x03990304, 0x001FD9}, {0x1F490301, 0x001F4D}, {0x03CA0342, 0x001FD7}, {0x00550304, 0x00016A}, {0x30DB309A, 0x0030DD}, {0x1F6E0345, 0x001FAE}, {0x00420331, 0x001E06}, {0x041E0308, 0x0004E6}, {0x00410325, 0x001E00}, {0x00520331, 0x001E5E}, {0x1FF60345, 0x001FF7}, {0x22720338, 0x002274}, {0x22480338, 0x002249}, {0x1FBF0301, 0x001FCE}, {0x30C63099, 0x0030C7}, {0x00760323, 0x001E7F}, {0x00700307, 0x001E57}, {0x1F510301, 0x001F55}, {0x00750328, 0x000173}, {0x305B3099, 0x00305C}, {0x0041030A, 0x0000C5}, {0x01A10323, 0x001EE3}, {0x00550311, 0x000216}, {0x00530307, 0x001E60}, {0x00DC030C, 0x0001D9}, {0x004F0300, 0x0000D2}, {0x006F0307, 0x00022F}, {0x00470301, 0x0001F4}, {0x00610309, 0x001EA3}, {0x1F080301, 0x001F0C}, {0x03B90301, 0x0003AF}, {0x04330301, 0x000453}, {0x1F200345, 0x001F90}, {0x00620307, 0x001E03}, {0x1F030345, 0x001F83}, {0x00570308, 0x001E84}, {0x005A0307, 0x00017B}, {0x30C13099, 0x0030C2}, {0x01A10309, 0x001EDF}, {0x06270655, 0x000625}, {0x03BF0314, 0x001F41}, {0x00670301, 0x0001F5}, {0x039F0301, 0x00038C}, {0x00EA0301, 0x001EBF}, {0x0063030C, 0x00010D}, {0x1F000345, 0x001F80}, {0x04130301, 0x000403}, {0x004B0323, 0x001E32}, {0x00790301, 0x0000FD}, {0x00770301, 0x001E83}, {0x1F0E0345, 0x001F8E}, {0x00480308, 0x001E26}, {0x30A63099, 0x0030F4}, {0x01020300, 0x001EB0}, {0x006C0301, 0x00013A}, {0x00450309, 0x001EBA}, {0x1F390301, 0x001F3D}, {0x00690311, 0x00020B}, {0x04230304, 0x0004EE}, {0x0061030C, 0x0001CE}, {0x1F100300, 0x001F12}, {0x04380300, 0x00045D}, {0x00480323, 0x001E24}, {0x1B111B35, 0x001B12}, {0x00430301, 0x000106}, {0x00540323, 0x001E6C}, {0x00490303, 0x000128}, {0x1EA10302, 0x001EAD}, {0x04350308, 0x000451}, {0x00610307, 0x000227}, {0x1F000300, 0x001F02}, {0x006D0307, 0x001E41}, {0x00760303, 0x001E7D}, {0x00730327, 0x00015F}, {0x00470304, 0x001E20}, {0x00470306, 0x00011E}, {0x1F6D0345, 0x001FAD}, {0x006F0303, 0x0000F5}, {0x006B0331, 0x001E35}, {0x00720331, 0x001E5F}, {0x01B00300, 0x001EEB}, {0x1B0B1B35, 0x001B0C}, {0x0055032D, 0x001E76}, {0x0061030A, 0x0000E5}, {0x30463099, 0x003094}, {0x1B3A1B35, 0x001B3B}, {0x0055030A, 0x00016E}, {0x0928093C, 0x000929}, {0x03C90313, 0x001F60}, {0x00650330, 0x001E1B}, {0x00590303, 0x001EF8}, {0x00450330, 0x001E1A}, {0x00640331, 0x001E0F}, {0x03B90308, 0x0003CA}, {0x309D3099, 0x00309E}, {0x0443030B, 0x0004F3}, {0x0B470B3E, 0x000B4B}, {0x006F030F, 0x00020D}, {0x006E0323, 0x001E47}, {0x00690308, 0x0000EF}, {0x0065032D, 0x001E19}, {0x015A0307, 0x001E64}, {0x1F180301, 0x001F1C}, {0x1F090301, 0x001F0D}, {0x1F270345, 0x001F97}, {0x007A0302, 0x001E91}, {0x00590301, 0x0000DD}, {0x004C0331, 0x001E3A}, {0x1F640345, 0x001FA4}, {0x1EB90302, 0x001EC7}, {0x1F240345, 0x001F94}, {0x01130300, 0x001E15}, {0x0069030F, 0x000209}, {0x00740331, 0x001E6F}, {0x1F480301, 0x001F4C}, {0x007A0331, 0x001E95}, {0x1F290345, 0x001F99}, {0x00D40301, 0x001ED0}, {0x00C40304, 0x0001DE}, {0x03970301, 0x000389}, {0x0072030C, 0x000159}, {0x1F110301, 0x001F15}, {0x00F40309, 0x001ED5}, {0x00450327, 0x000228}, {0x006F031B, 0x0001A1}, {0x00C60304, 0x0001E2}, {0x03990308, 0x0003AA}, {0x03CE0345, 0x001FF4}, {0x006D0301, 0x001E3F}, {0x01030309, 0x001EB3}, {0x006F0302, 0x0000F4}, {0x0073030C, 0x000161}, {0x00750301, 0x0000FA}, {0x1F690301, 0x001F6D}, {0x03A90301, 0x00038F}, {0x003D0338, 0x002260}, {0x1FC60345, 0x001FC7}, {0x00770300, 0x001E81}, {0x02280306, 0x001E1C}, {0x006C0331, 0x001E3B}, {0x30AD3099, 0x0030AE}, {0x1EB80302, 0x001EC6}, {0x00550302, 0x0000DB}, {0x0DD90DCF, 0x000DDC}, {0x03A50304, 0x001FE9}, {0x03A90314, 0x001F69}, {0x00740327, 0x000163}, {0x00530326, 0x000218}, {0x0055030B, 0x000170}, {0x306F309A, 0x003071}, {0x006B0327, 0x000137}, {0x00750330, 0x001E75}, {0x00730301, 0x00015B}, {0x1F300342, 0x001F36}, {0x00470307, 0x000120}, {0x0CC60CD5, 0x000CC7}, {0x1F690300, 0x001F6B}, {0x01030301, 0x001EAF}, {0x00750311, 0x000217}, {0x0930093C, 0x000931}, {0x30613099, 0x003062}, {0x00770323, 0x001E89}, {0x00F50301, 0x001E4D}, {0x00FC0300, 0x0001DC}, {0x0049030F, 0x000208}, {0x00690302, 0x0000EE}, {0x1F230345, 0x001F93}, {0x00F40300, 0x001ED3}, {0x044B0308, 0x0004F9}, {0x1F2E0345, 0x001F9E}, {0x00650300, 0x0000E8}, {0x00490308, 0x0000CF}, {0x1F280301, 0x001F2C}, {0x1F180300, 0x001F1A}, {0x06270653, 0x000622}, {0x1F200301, 0x001F24}, {0x22030338, 0x002204}, {0x00790304, 0x000233}, {0x1F390300, 0x001F3B}, {0x00610325, 0x001E01}, {0x00790309, 0x001EF7}, {0x01AF0303, 0x001EEE}, {0x006F0301, 0x0000F3}, {0x00640323, 0x001E0D}, {0x304F3099, 0x003050}, {0x1F010345, 0x001F81}, {0x00CA0309, 0x001EC2}, {0x03D20308, 0x0003D4}, {0x1F500301, 0x001F54}, {0x1F010300, 0x001F03}, {0x03A90345, 0x001FFC}, {0x04270308, 0x0004F4}, {0x00530302, 0x00015C}, {0x00520301, 0x000154}, {0x00450306, 0x000114}, {0x00530323, 0x001E62}, {0x00670306, 0x00011F}, {0x22760338, 0x002278}, {0x04350300, 0x000450}, {0x30B53099, 0x0030B6}, {0x006F030B, 0x000151}, {0x04180304, 0x0004E2}, {0x00630307, 0x00010B}, {0x03D20301, 0x0003D3}, {0x227C0338, 0x0022E0}, {0x00610304, 0x000101}, {0x00A80300, 0x001FED}, {0x00440331, 0x001E0E}, {0x00650311, 0x000207}, {0x00F80301, 0x0001FF}, {0x0075030C, 0x0001D4}, {0x00530327, 0x00015E}, {0x30573099, 0x003058}, {0x03B70345, 0x001FC3}, {0x0069030C, 0x0001D0}, {0x006E0327, 0x000146}, {0x00780307, 0x001E8B}, {0x1F000342, 0x001F06}, {0x01B00303, 0x001EEF}, {0x00470327, 0x000122}, {0x00C20309, 0x001EA8}, {0x006C032D, 0x001E3D}, {0x304D3099, 0x00304E}, {0x00690303, 0x000129}, {0x01AF0323, 0x001EF0}, {0x004F0302, 0x0000D4}, {0x01020301, 0x001EAE}, {0x04470308, 0x0004F5}, {0x22A80338, 0x0022AD}, {0x03C50300, 0x001F7A}, {0x03910313, 0x001F08}, {0x0064032D, 0x001E13}, {0x00CF0301, 0x001E2E}, {0x0061030F, 0x000201}, {0x1EA10306, 0x001EB7}, {0x00790323, 0x001EF5}, {0x21900338, 0x00219A}, {0x1F250345, 0x001F95}, {0x004F0304, 0x00014C}, {0x004F0306, 0x00014E}, {0x30B33099, 0x0030B4}, {0x00440327, 0x001E10}, {0x0B920BD7, 0x000B94}, {0x00550308, 0x0000DC}, {0x00650323, 0x001EB9}, {0x307B3099, 0x00307C}, {0x004F030C, 0x0001D1}, {0x00670304, 0x001E21}, {0x006A030C, 0x0001F0}, {0x00550323, 0x001EE4}, {0x01A10301, 0x001EDB}, {0x00590323, 0x001EF4}, {0x00410300, 0x0000C0}, {0x30B93099, 0x0030BA}, {0x04230308, 0x0004F0}, {0x00530301, 0x00015A}, {0x30D8309A, 0x0030DA}, {0x00750309, 0x001EE7}, {0x00550330, 0x001E74}, {0x00520327, 0x000156}, {0x04370308, 0x0004DF}, {0x00CA0300, 0x001EC0}, {0x21940338, 0x0021AE}, {0x03B10306, 0x001FB0}, {0x1F610342, 0x001F67}, {0x223C0338, 0x002241}, {0x30D23099, 0x0030D3}, {0x1F6B0345, 0x001FAB}, {0x00490307, 0x000130}, {0x00410323, 0x001EA0}, {0x00550309, 0x001EE6}, {0x0075030B, 0x000171}, {0x30533099, 0x003054}, {0x305D3099, 0x00305E}, {0x004C0323, 0x001E36}, {0x02260304, 0x0001E0}, {0x0475030F, 0x000477}, {0x00490328, 0x00012E}, {0x1F210300, 0x001F23}, {0x1F610301, 0x001F65}, {0x1F210342, 0x001F27}, {0x00670302, 0x00011D}, {0x004E0301, 0x000143}, {0x004E0303, 0x0000D1}, {0x03990300, 0x001FDA}, {0x03BF0301, 0x0003CC}, {0x04150300, 0x000400}, {0x03C90314, 0x001F61}, {0x22A20338, 0x0022AC}, {0x30DB3099, 0x0030DC}, {0x03A50314, 0x001F59}, {0x0052030F, 0x000210}, {0x004E0323, 0x001E46}, {0x1F0D0345, 0x001F8D}, {0x1ECD0302, 0x001ED9}, {0x00FC030C, 0x0001DA}, {0x00D60304, 0x00022A}, {0x1F000301, 0x001F04}, {0x30CF3099, 0x0030D0}, {0x04350306, 0x0004D7}, {0x220B0338, 0x00220C}, {0x00590300, 0x001EF2}, {0x04170308, 0x0004DE}, {0x006E030C, 0x000148}, {0x004C030C, 0x00013D}, {0x01690301, 0x001E79}, {0x04100308, 0x0004D2}, {0x01A00301, 0x001EDA}, {0x0041030F, 0x000200}, {0x00720301, 0x000155}, {0x00450303, 0x001EBC}, {0x1B0D1B35, 0x001B0E}, {0x01120301, 0x001E16}, {0x004A0302, 0x000134}, {0x03BF0300, 0x001F78}, {0x30593099, 0x00305A}, {0x004F030B, 0x000150}, {0x00610328, 0x000105}, {0x30D83099, 0x0030D9}, {0x1F2B0345, 0x001F9B}, {0x1F400301, 0x001F44}, {0x0065030F, 0x000205}, {0x03AE0345, 0x001FC4}, {0x01680301, 0x001E78}, {0x00740326, 0x00021B}, {0x03B90342, 0x001FD6}, {0x1F6F0345, 0x001FAF}, {0x03A50301, 0x00038E}, {0x1F090345, 0x001F89}, {0x00550300, 0x0000D9}, {0x0041030C, 0x0001CD}, {0x00430327, 0x0000C7}, {0x00460307, 0x001E1E}, {0x03B50301, 0x0003AD}, {0x0D470D3E, 0x000D4B}, {0x00610311, 0x000203}, {0x00D50308, 0x001E4E}, {0x03C90300, 0x001F7C}, {0x04430306, 0x00045E}, {0x1FFE0301, 0x001FDE}, {0x03990301, 0x00038A}, {0x00790307, 0x001E8F}, {0x1E5A0304, 0x001E5C}, {0x0075030F, 0x000215}, {0x09C709BE, 0x0009CB}, {0x1B071B35, 0x001B08}, {0x00690330, 0x001E2D}, {0x307B309A, 0x00307D}, {0x30C83099, 0x0030C9}, {0x22B30338, 0x0022EB}, {0x01B00301, 0x001EE9}, {0x00790300, 0x001EF3}, {0x00570307, 0x001E86}, {0x00690328, 0x00012F}, {0x005A0323, 0x001E92}, {0x03C50342, 0x001FE6}, {0x004F0309, 0x001ECE}, {0x1F290342, 0x001F2F}, {0x01A00300, 0x001EDC}, {0x1F2A0345, 0x001F9A}, {0x014D0301, 0x001E53}, {0x21D00338, 0x0021CD}, {0x00550328, 0x000172}, {0x00680331, 0x001E96}, {0x0068030C, 0x00021F}, {0x004C0301, 0x000139}, {0x22860338, 0x002288}, {0x039F0314, 0x001F49}, {0x03A50306, 0x001FE8}, {0x0B470B56, 0x000B48}, {0x30753099, 0x003076}, {0x1E5B0304, 0x001E5D}, {0x1F6A0345, 0x001FAA}, {0x00540327, 0x000162}, {0x01020303, 0x001EB4}, {0x039F0313, 0x001F48}, {0x1F090342, 0x001F0F}, {0x04100306, 0x0004D0}, {0x014D0300, 0x001E51}, {0x04430304, 0x0004EF}, {0x21920338, 0x00219B}, {0x22610338, 0x002262}, {0x00F50304, 0x00022D}, {0x03B90313, 0x001F30}, {0x006F0308, 0x0000F6}, {0x00730307, 0x001E61}, {0x0075031B, 0x0001B0}, {0x30B73099, 0x0030B8}, {0x1E620307, 0x001E68}, {0x00410306, 0x000102}, {0x03970314, 0x001F29}, {0x00630302, 0x000109}, {0x006A0302, 0x000135}, {0x00E50301, 0x0001FB}, {0x00730326, 0x000219}, {0x1F740345, 0x001FC2}, {0x00650309, 0x001EBB}, {0x005A030C, 0x00017D}, {0x22250338, 0x002226}, {0x03A90313, 0x001F68}, {0x00720327, 0x000157}, {0x1F090300, 0x001F0B}, {0x04360308, 0x0004DD}, {0x1F080345, 0x001F88}, {0x00620331, 0x001E07}, {0x22730338, 0x002275}, {0x00560323, 0x001E7E}, {0x004F0323, 0x001ECC}, {0x022E0304, 0x000230}, {0x06D50654, 0x0006C0}, {0x03B10313, 0x001F00}, {0x004F0303, 0x0000D5}, {0x1F010301, 0x001F05}, {0x00650304, 0x000113}, {0x00450323, 0x001EB8}, {0x0BC60BBE, 0x000BCA}, {0x22650338, 0x002271}, {0x03910314, 0x001F09}, {0x01120300, 0x001E14}, {0x004E0307, 0x001E44}, {0x00680308, 0x001E27}, {0x004F0311, 0x00020E}, {0x00550303, 0x000168}, {0x03A10314, 0x001FEC}, {0x03B10301, 0x0003AC}, {0x0055030F, 0x000214}, {0x003E0338, 0x00226F}, {0x01610307, 0x001E67}, {0x1F040345, 0x001F84}, {0x00450300, 0x0000C8}, {0x1B091B35, 0x001B0A}, {0x30FD3099, 0x0030FE}, {0x1F680301, 0x001F6C}, {0x00650303, 0x001EBD}, {0x03B90304, 0x001FD1}, {0x09C709D7, 0x0009CC}, {0x042B0308, 0x0004F8}, {0x004E0327, 0x000145}, {0x1F630345, 0x001FA3}, {0x04380306, 0x000439}, {0x03B10314, 0x001F01}, {0x00540326, 0x00021A}, {0x00740307, 0x001E6B}, {0x03C90301, 0x0003CE}, {0x03990314, 0x001F39}, {0x30B13099, 0x0030B2}, {0x00750302, 0x0000FB}, {0x0CC60CD6, 0x000CC8}, {0x30BD3099, 0x0030BE}, {0x006E0300, 0x0001F9}, {0x1F390342, 0x001F3F}, {0x02270304, 0x0001E1}, {0x22080338, 0x002209}, {0x04180306, 0x000419}, {0x0044030C, 0x00010E}, {0x0075030A, 0x00016F}, {0x06270654, 0x000623}, {0x00750306, 0x00016D}, {0x03B70300, 0x001F74}, {0x1F660345, 0x001FA6}, {0x1F690345, 0x001FA9}, {0x015B0307, 0x001E65}, {0x1F280342, 0x001F2E}, {0x1F700345, 0x001FB2}, {0x1F0F0345, 0x001F8F}, {0x00540331, 0x001E6E}, {0x227B0338, 0x002281}, {0x22870338, 0x002289}, {0x1F010342, 0x001F07}, {0x03A50308, 0x0003AB}, {0x1F650345, 0x001FA5}, {0x00D40300, 0x001ED2}, {0x00F60304, 0x00022B}, {0x0CBF0CD5, 0x000CC0}, {0x1F380342, 0x001F3E}, {0x22B20338, 0x0022EA}, {0x0072030F, 0x000211}, {0x30BF3099, 0x0030C0}, {0x04E90308, 0x0004EB}, {0x22830338, 0x002285}, {0x03CB0300, 0x001FE2}, {0x00CA0301, 0x001EBE}, {0x00590304, 0x000232}, {0x00690301, 0x0000ED}, {0x305F3099, 0x003060}, {0x00440307, 0x001E0A}, {0x1ECC0302, 0x001ED8}, {0x1F210345, 0x001F91}, {0x00410311, 0x000202}, {0x00720311, 0x000213}, {0x03A50300, 0x001FEA}, {0x1F590300, 0x001F5B}, {0x00E40304, 0x0001DF}, {0x1F500300, 0x001F52}, {0x224D0338, 0x00226D}, {0x006E0301, 0x000144}, {0x0075032D, 0x001E77}, {0x00F40301, 0x001ED1}, {0x03C90342, 0x001FF6}, {0x03B70301, 0x0003AE}, {0x006E0307, 0x001E45}, {0x00410304, 0x000100}, {0x1F190301, 0x001F1D}, {0x03B10304, 0x001FB1}, {0x3078309A, 0x00307A}, {0x00570323, 0x001E88}, {0x04430308, 0x0004F1}, {0x0045032D, 0x001E18}, {0x04150308, 0x000401}, {0x004F0328, 0x0001EA}, {0x04D90308, 0x0004DB}, {0x04060308, 0x000407}, {0x00F40303, 0x001ED7}, {0x03B90306, 0x001FD0}, {0x00650307, 0x000117}, {0x1F680342, 0x001F6E}, {0x04380304, 0x0004E3}, {0x1F410300, 0x001F43}, {0x1FFE0300, 0x001FDD}, {0x004F0301, 0x0000D3}, {0x00640327, 0x001E11}, {0x02290306, 0x001E1D}, {0x007A0301, 0x00017A}, {0x00620323, 0x001E05}, {0x1F7C0345, 0x001FF2}, {0x01B00323, 0x001EF1}, {0x1F600301, 0x001F64}, {0x30663099, 0x003067}, {0x22640338, 0x002270}, {0x1F310300, 0x001F33}, {0x00610323, 0x001EA1}, {0x1F480300, 0x001F4A}, {0x00550301, 0x0000DA}, {0x0C460C56, 0x000C48}, {0x03CA0300, 0x001FD2}, {0x01B7030C, 0x0001EE}, {0x00720323, 0x001E5B}, {0x1F200342, 0x001F26}, {0x04300308, 0x0004D3}, {0x22820338, 0x002284}, {0x1F590301, 0x001F5D}, {0x1F080342, 0x001F0E}, {0x00440323, 0x001E0C}, {0x1025102E, 0x001026}, {0x03910304, 0x001FB9}, {0x004B0331, 0x001E34}, {0x00690300, 0x0000EC}, {0x0048032E, 0x001E2A}, {0x00630301, 0x000107}, {0x1F690342, 0x001F6F}, {0x03B50313, 0x001F10}, {0x30CF309A, 0x0030D1}, {0x004E030C, 0x000147}, {0x0B470B57, 0x000B4C}, {0x03910301, 0x000386}, {0x00650328, 0x000119}, {0x1FBF0300, 0x001FCD}, {0x03B70342, 0x001FC6}, {0x00450307, 0x000116}, {0x1F380301, 0x001F3C}, {0x3075309A, 0x003077}, {0x004B0301, 0x001E30}, {0x01030300, 0x001EB1}, {0x00E20303, 0x001EAB}, {0x00410302, 0x0000C2}, {0x00650308, 0x0000EB}, {0x00580307, 0x001E8A}, {0x0054030C, 0x000164}, {0x03C50313, 0x001F50}, {0x03B50314, 0x001F11}, {0x1B3E1B35, 0x001B40}, {0x1F100301, 0x001F14}, {0x3072309A, 0x003074}, {0x1F280345, 0x001F98}, {0x22770338, 0x002279}, {0x227A0338, 0x002280}, {0x00470302, 0x00011C}, {0x00450308, 0x0000CB}, {0x00550324, 0x001E72}, {0x1F2C0345, 0x001F9C}, {0x004B0327, 0x000136}, {0x00490302, 0x0000CE}, {0x00680302, 0x000125}, {0x00520307, 0x001E58}, {0x00610301, 0x0000E1}, {0x004B030C, 0x0001E8}, {0x00490309, 0x001EC8}, {0x006C030C, 0x00013E}, {0x004D0307, 0x001E40}, {0x1E360304, 0x001E38}, {0x03C50304, 0x001FE1}, {0x0079030A, 0x001E99}, {0x03970313, 0x001F28}, {0x0054032D, 0x001E70}, {0x1F490300, 0x001F4B}, {0x1B421B35, 0x001B43}, {0x03B70313, 0x001F20}, {0x00EF0301, 0x001E2F}, {0x00E70301, 0x001E09}, {0x016A0308, 0x001E7A}, {0x00CA0303, 0x001EC4}, {0x03B10300, 0x001F70}, {0x06D20654, 0x0006D3}, {0x30D53099, 0x0030D6}, {0x1F2F0345, 0x001F9F}, {0x03B50300, 0x001F72}, {0x00750324, 0x001E73}, {0x04150306, 0x0004D6}, {0x0065030C, 0x00011B}, {0x00570302, 0x000174}, {0x1EA00306, 0x001EB6}, {0x1F070345, 0x001F87}, {0x00A80301, 0x000385}, {0x00790302, 0x000177}, {0x006E032D, 0x001E4B}, {0x006F030C, 0x0001D2}, {0x03C90345, 0x001FF3}, {0x00430302, 0x000108}, {0x00790303, 0x001EF9}, {0x00740323, 0x001E6D}, {0x00E20309, 0x001EA9}, {0x1F300300, 0x001F32}, {0x006B0301, 0x001E31}, {0x22920338, 0x0022E3}, {0x03950314, 0x001F19}, {0x1F290301, 0x001F2D}, {0x00450328, 0x000118}, {0x1FB60345, 0x001FB7}, {0x00D50304, 0x00022C}, {0x04300306, 0x0004D1}, {0x00F50308, 0x001E4F}, {0x1EA00302, 0x001EAC}, {0x1F680300, 0x001F6A}, {0x03970300, 0x001FCA}, {0x00450301, 0x0000C9}, {0x00690304, 0x00012B}, {0x0BC60BD7, 0x000BCC}, {0x006F0304, 0x00014D}, {0x004F0308, 0x0000D6}, {0x0BC70BBE, 0x000BCB}, {0x0055030C, 0x0001D3}, {0x03AC0345, 0x001FB4}, {0x1FBF0342, 0x001FCF}, {0x30D2309A, 0x0030D4}, {0x00660307, 0x001E1F}, {0x004E0300, 0x0001F8}, {0x00790308, 0x0000FF}, {0x0068032E, 0x001E2B}, {0x0064030C, 0x00010F}, {0x004F0307, 0x00022E}, {0x1F590342, 0x001F5F}, {0x30AF3099, 0x0030B0}, {0x00E60304, 0x0001E3}, {0x03C50301, 0x0003CD}, {0x00770308, 0x001E85}, {0x03B90314, 0x001F31}, {0x00750323, 0x001EE5}, {0x00590309, 0x001EF6}, {0x0292030C, 0x0001EF}, {0x00550306, 0x00016C}, {0x00E60301, 0x0001FD}, {0x016B0308, 0x001E7B}, {0x004F030F, 0x00020C}, {0x03BF0313, 0x001F40}, {0x00D80301, 0x0001FE}, {0x00C20303, 0x001EAA}, {0x1F510300, 0x001F53}, {0x01B00309, 0x001EED}, {0x004E032D, 0x001E4A}, {0x006F0309, 0x001ECF}, {0x04180300, 0x00040D}, {0x22A90338, 0x0022AE}, {0x1F680345, 0x001FA8}, {0x1B3C1B35, 0x001B3D}, {0x1F400300, 0x001F42}, {0x1F0B0345, 0x001F8B}, {0x03C50306, 0x001FE0}, {0x0044032D, 0x001E12}, {0x0CCA0CD5, 0x000CCB}, {0x00EA0303, 0x001EC5}, {0x04230306, 0x00040E}, {0x00C70301, 0x001E08}, {0x007A030C, 0x00017E}, {0x00670307, 0x000121}, {0x00450311, 0x000206}, {0x006F0300, 0x0000F2}, {0x30BB3099, 0x0030BC}, {0x04180308, 0x0004E4}, {0x30513099, 0x003052}, {0x006C0323, 0x001E37}, {0x03B70314, 0x001F21}, {0x0077030A, 0x001E98}, {0x00690323, 0x001ECB}, {0x00610303, 0x0000E3}, {0x30F13099, 0x0030F9}, {0x03B10345, 0x001FB3}, {0x0D460D3E, 0x000D4A}, {0x03B90300, 0x001F76}, {0x21D40338, 0x0021CE}, {0x1F620345, 0x001FA2}, {0x00770302, 0x000175}, {0x00FC0304, 0x0001D6}, {0x01130301, 0x001E17}, {0x30683099, 0x003069}, {0x1F600342, 0x001F66}, {0x00680323, 0x001E25}, {0x03950313, 0x001F18}, {0x00EA0300, 0x001EC1}, {0x03CB0342, 0x001FE7}, {0x22430338, 0x002244}, {0x30783099, 0x003079}, {0x03990313, 0x001F38}, {0x00490330, 0x001E2C}, {0x00770307, 0x001E87}, {0x00650301, 0x0000E9}, {0x00680327, 0x001E29}, {0x22B50338, 0x0022ED}, {0x03B10342, 0x001FB6}, {0x03910306, 0x001FB8}, {0x004D0301, 0x001E3E}, {0x00410309, 0x001EA2}, {0x1B051B35, 0x001B06}, {0x003C0338, 0x00226E}, {0x00590308, 0x000178}, {0x01030303, 0x001EB5}, {0x00480327, 0x001E28}, {0x006D0323, 0x001E43}, {0x30643099, 0x003065}, {0x04560308, 0x000457}, {0x00590302, 0x000176}, {0x06C10654, 0x0006C2}, {0x043A0301, 0x00045C}, {0x00E20300, 0x001EA7}, {0x03950301, 0x000388}, {0x00630327, 0x0000E7}, {0x017F0307, 0x001E9B}, {0x06480654, 0x000624}, {0x30AB3099, 0x0030AC}, {0x01A00323, 0x001EE2}, {0x04D80308, 0x0004DA}, {0x005A0331, 0x001E94}, {0x0067030C, 0x0001E7}, {0x039F0300, 0x001FF8}, {0x00650327, 0x000229}, {0x00490323, 0x001ECA}, {0x1F110300, 0x001F13}, {0x1F510342, 0x001F57}, {0x004D0323, 0x001E42}, {0x1F300301, 0x001F34}, {0x006C0327, 0x00013C}, {0x1F050345, 0x001F85}, {0x01A10300, 0x001EDD}, {0x04160308, 0x0004DC}, {0x00640307, 0x001E0B}, {0x004C032D, 0x001E3C}, {0x03C10314, 0x001FE5}, {0x01EA0304, 0x0001EC}, {0x1F080300, 0x001F0A}, {0x00650302, 0x0000EA}, {0x03910300, 0x001FBA}, {0x00700301, 0x001E55}, {0x1F020345, 0x001F82}, {0x01020309, 0x001EB2}, {0x00750303, 0x000169}, {0x1F0C0345, 0x001F8C}, {0x03CB0301, 0x0003B0}, {0x00740308, 0x001E97}, {0x03950300, 0x001FC8}, {0x227D0338, 0x0022E1}, {0x007A0323, 0x001E93}, {0x006F0311, 0x00020F}, {0x006F0328, 0x0001EB}, {0x1F200300, 0x001F22}, {0x1F190300, 0x001F1B}, {0x00560303, 0x001E7C}, {0x006B030C, 0x0001E9}, {0x04E80308, 0x0004EA}, {0x03A90300, 0x001FFA}, {0x00A80342, 0x001FC1}, {0x04360306, 0x0004C2}, {0x00DC0301, 0x0001D7}, {0x00750304, 0x00016B}, {0x22450338, 0x002247}, {0x00490304, 0x00012A}, {0x04380308, 0x0004E5}, {0x0D460D57, 0x000D4C}, {0x00750300, 0x0000F9}, {0x22B40338, 0x0022EC}, {0x00750308, 0x0000FC}, {0x03C50314, 0x001F51}, {0x01600307, 0x001E66}, {0x04160306, 0x0004C1}, {0x0055031B, 0x0001AF}, {0x22AB0338, 0x0022AF}, {0x014C0300, 0x001E50}, {0x1F310342, 0x001F37}, {0x1F260345, 0x001F96}, {0x22910338, 0x0022E2}, {0x00500301, 0x001E54}, {0x004C0327, 0x00013B}, {0x005A0301, 0x000179}, {0x00420307, 0x001E02}, {0x03C10313, 0x001FE4}, {0x1F410301, 0x001F45}, {0x00500307, 0x001E56}, {0x0043030C, 0x00010C}, {0x00730323, 0x001E63}, {0x30553099, 0x003056}, {0x306F3099, 0x003070}, {0x044D0308, 0x0004ED}, {0x1F670345, 0x001FA7}, {0x01AF0301, 0x001EE8}, {0x1F290300, 0x001F2B}, {0x1B3F1B35, 0x001B41}, {0x00450302, 0x0000CA}, {0x006F0306, 0x00014F}, {0x00FC0301, 0x0001D8}, {0x30F23099, 0x0030FA}, {0x1F2D0345, 0x001F9D}, {0x014C0301, 0x001E52}, {0x00610308, 0x0000E4}, {0x1F600345, 0x001FA0}, {0x1F0A0345, 0x001F8A}, {0x00610306, 0x000103}, {0x1F600300, 0x001F62}, {0x01AF0300, 0x001EEA}, {0x043E0308, 0x0004E7}, {0x22230338, 0x002224}, {0x03CA0301, 0x000390}, {0x30D5309A, 0x0030D7}, {0x1F220345, 0x001F92}, {0x00D50301, 0x001E4C}, {0x1F6C0345, 0x001FAC}, {0x00540307, 0x001E6A}, {0x1E370304, 0x001E39}, {0x0DD90DCA, 0x000DDA}, {0x01A10303, 0x001EE1}, {0x0048030C, 0x00021E}, {0x00EA0309, 0x001EC3}, {0x0052030C, 0x000158}, {0x00D40303, 0x001ED6}, {0x0045030C, 0x00011A}, {0x30EF3099, 0x0030F7}, {0x00480302, 0x000124}, {0x00520323, 0x001E5A}, {0x00C20300, 0x001EA6}, {0x0074032D, 0x001E71}, {0x042D0308, 0x0004EC}, {0x0DD90DDF, 0x000DDE}, {0x00410308, 0x0000C4}, {0x0474030F, 0x000476}, {0x00C60301, 0x0001FC}, {0x1F310301, 0x001F35}, {0x1F610300, 0x001F63}, {0x0CC60CC2, 0x000CCA}, {0x00C20301, 0x001EA4}, {0x03C50308, 0x0003CB}, {0x00E20301, 0x001EA5}, {0x1F500342, 0x001F56}, {0x00610300, 0x0000E0}, {0x00490311, 0x00020A}, {0x00670327, 0x000123}, {0x00590307, 0x001E8E}, {0x03970345, 0x001FCC}, {0x00D40309, 0x001ED4}, {0x01A00303, 0x001EE0}, {0x1FFE0342, 0x001FDF}, {0x041A0301, 0x00040C}, {0x0074030C, 0x000165}, {0x00DC0300, 0x0001DB}, {0x00780308, 0x001E8D}, {0x00DC0304, 0x0001D5}, {0x00610302, 0x0000E2}, {0x005A0302, 0x001E90}, {0x0933093C, 0x000934}, {0x004F031B, 0x0001A0}, {0x00520311, 0x000212}, {0x006E0303, 0x0000F1}, {0x0045030F, 0x000204}, {0x01AF0309, 0x001EEC}, {0x00450304, 0x000112}, {0x1F210301, 0x001F25}, {0x022F0304, 0x000231}, {0x03990306, 0x001FD8}, {0x004E0331, 0x001E48}, {0x1F380300, 0x001F3A}, {0x00490306, 0x00012C}, {0x00690306, 0x00012D}, {0x1F060345, 0x001F86}, {0x00490301, 0x0000CD}, {0x00570300, 0x001E80}, {0x30F03099, 0x0030F8}, {0x0053030C, 0x000160}, {0x00490300, 0x0000CC}, {0x30723099, 0x003073}, {0x1F280300, 0x001F2A}, {0x00480307, 0x001E22}, {0x01A00309, 0x001EDE}, {0x01EB0304, 0x0001ED}, {0x1E630307, 0x001E69}, {0x006F0323, 0x001ECD}, {0x00410303, 0x0000C3}, {0x03910345, 0x001FBC}, {0x00410328, 0x000104}, {0x00680307, 0x001E23}, {0x006E0331, 0x001E49}, {0x0DDC0DCA, 0x000DDD}, {0x00580308, 0x001E8C}, {0x00410301, 0x0000C1}, {0x30C43099, 0x0030C5}, {0x00420323, 0x001E04}, {0x00730302, 0x00015D} }; const std::tuple COMPOSITION_TABLE_ASTRAL_VALUES[] { {0x11099, 0x110BA, 0x1109A}, {0x1109B, 0x110BA, 0x1109C}, {0x110A5, 0x110BA, 0x110AB}, {0x11131, 0x11127, 0x1112E}, {0x11132, 0x11127, 0x1112F}, {0x11347, 0x1133E, 0x1134B}, {0x11347, 0x11357, 0x1134C}, {0x114B9, 0x114B0, 0x114BC}, {0x114B9, 0x114BA, 0x114BB}, {0x114B9, 0x114BD, 0x114BE}, {0x115B8, 0x115AF, 0x115BA}, {0x115B9, 0x115AF, 0x115BB}, {0x11935, 0x11930, 0x11938} }; struct Cmp1 { bool operator()(const std::tuple& a, UTF32 b) const { return std::get<0>(a) < b; } bool operator()(UTF32 a, const std::tuple& b) const { return a < std::get<0>(b); } } const CMP1{}; std::optional CompositionTableAstral(UTF32 c1, UTF32 c2) { auto [begin, end] = std::equal_range(std::begin(COMPOSITION_TABLE_ASTRAL_VALUES), std::end(COMPOSITION_TABLE_ASTRAL_VALUES), c1, CMP1); if (begin == std::end(COMPOSITION_TABLE_ASTRAL_VALUES)) { // not found return std::nullopt; } for (auto it{begin}; it != end; ++it) { if (std::get<1>(*it) == c2) { return {std::get<2>(*it)}; } } return std::nullopt; } constexpr UTF32 CANONICAL_DECOMPOSED_CHARS[]{ 0x0041, 0x0300, 0x0041, 0x0301, 0x0041, 0x0302, 0x0041, 0x0303, 0x0041, 0x0308, 0x0041, 0x030A, 0x0043, 0x0327, 0x0045, 0x0300, 0x0045, 0x0301, 0x0045, 0x0302, 0x0045, 0x0308, 0x0049, 0x0300, 0x0049, 0x0301, 0x0049, 0x0302, 0x0049, 0x0308, 0x004E, 0x0303, 0x004F, 0x0300, 0x004F, 0x0301, 0x004F, 0x0302, 0x004F, 0x0303, 0x004F, 0x0308, 0x0055, 0x0300, 0x0055, 0x0301, 0x0055, 0x0302, 0x0055, 0x0308, 0x0059, 0x0301, 0x0061, 0x0300, 0x0061, 0x0301, 0x0061, 0x0302, 0x0061, 0x0303, 0x0061, 0x0308, 0x0061, 0x030A, 0x0063, 0x0327, 0x0065, 0x0300, 0x0065, 0x0301, 0x0065, 0x0302, 0x0065, 0x0308, 0x0069, 0x0300, 0x0069, 0x0301, 0x0069, 0x0302, 0x0069, 0x0308, 0x006E, 0x0303, 0x006F, 0x0300, 0x006F, 0x0301, 0x006F, 0x0302, 0x006F, 0x0303, 0x006F, 0x0308, 0x0075, 0x0300, 0x0075, 0x0301, 0x0075, 0x0302, 0x0075, 0x0308, 0x0079, 0x0301, 0x0079, 0x0308, 0x0041, 0x0304, 0x0061, 0x0304, 0x0041, 0x0306, 0x0061, 0x0306, 0x0041, 0x0328, 0x0061, 0x0328, 0x0043, 0x0301, 0x0063, 0x0301, 0x0043, 0x0302, 0x0063, 0x0302, 0x0043, 0x0307, 0x0063, 0x0307, 0x0043, 0x030C, 0x0063, 0x030C, 0x0044, 0x030C, 0x0064, 0x030C, 0x0045, 0x0304, 0x0065, 0x0304, 0x0045, 0x0306, 0x0065, 0x0306, 0x0045, 0x0307, 0x0065, 0x0307, 0x0045, 0x0328, 0x0065, 0x0328, 0x0045, 0x030C, 0x0065, 0x030C, 0x0047, 0x0302, 0x0067, 0x0302, 0x0047, 0x0306, 0x0067, 0x0306, 0x0047, 0x0307, 0x0067, 0x0307, 0x0047, 0x0327, 0x0067, 0x0327, 0x0048, 0x0302, 0x0068, 0x0302, 0x0049, 0x0303, 0x0069, 0x0303, 0x0049, 0x0304, 0x0069, 0x0304, 0x0049, 0x0306, 0x0069, 0x0306, 0x0049, 0x0328, 0x0069, 0x0328, 0x0049, 0x0307, 0x004A, 0x0302, 0x006A, 0x0302, 0x004B, 0x0327, 0x006B, 0x0327, 0x004C, 0x0301, 0x006C, 0x0301, 0x004C, 0x0327, 0x006C, 0x0327, 0x004C, 0x030C, 0x006C, 0x030C, 0x004E, 0x0301, 0x006E, 0x0301, 0x004E, 0x0327, 0x006E, 0x0327, 0x004E, 0x030C, 0x006E, 0x030C, 0x004F, 0x0304, 0x006F, 0x0304, 0x004F, 0x0306, 0x006F, 0x0306, 0x004F, 0x030B, 0x006F, 0x030B, 0x0052, 0x0301, 0x0072, 0x0301, 0x0052, 0x0327, 0x0072, 0x0327, 0x0052, 0x030C, 0x0072, 0x030C, 0x0053, 0x0301, 0x0073, 0x0301, 0x0053, 0x0302, 0x0073, 0x0302, 0x0053, 0x0327, 0x0073, 0x0327, 0x0053, 0x030C, 0x0073, 0x030C, 0x0054, 0x0327, 0x0074, 0x0327, 0x0054, 0x030C, 0x0074, 0x030C, 0x0055, 0x0303, 0x0075, 0x0303, 0x0055, 0x0304, 0x0075, 0x0304, 0x0055, 0x0306, 0x0075, 0x0306, 0x0055, 0x030A, 0x0075, 0x030A, 0x0055, 0x030B, 0x0075, 0x030B, 0x0055, 0x0328, 0x0075, 0x0328, 0x0057, 0x0302, 0x0077, 0x0302, 0x0059, 0x0302, 0x0079, 0x0302, 0x0059, 0x0308, 0x005A, 0x0301, 0x007A, 0x0301, 0x005A, 0x0307, 0x007A, 0x0307, 0x005A, 0x030C, 0x007A, 0x030C, 0x004F, 0x031B, 0x006F, 0x031B, 0x0055, 0x031B, 0x0075, 0x031B, 0x0041, 0x030C, 0x0061, 0x030C, 0x0049, 0x030C, 0x0069, 0x030C, 0x004F, 0x030C, 0x006F, 0x030C, 0x0055, 0x030C, 0x0075, 0x030C, 0x0055, 0x0308, 0x0304, 0x0075, 0x0308, 0x0304, 0x0055, 0x0308, 0x0301, 0x0075, 0x0308, 0x0301, 0x0055, 0x0308, 0x030C, 0x0075, 0x0308, 0x030C, 0x0055, 0x0308, 0x0300, 0x0075, 0x0308, 0x0300, 0x0041, 0x0308, 0x0304, 0x0061, 0x0308, 0x0304, 0x0041, 0x0307, 0x0304, 0x0061, 0x0307, 0x0304, 0x00C6, 0x0304, 0x00E6, 0x0304, 0x0047, 0x030C, 0x0067, 0x030C, 0x004B, 0x030C, 0x006B, 0x030C, 0x004F, 0x0328, 0x006F, 0x0328, 0x004F, 0x0328, 0x0304, 0x006F, 0x0328, 0x0304, 0x01B7, 0x030C, 0x0292, 0x030C, 0x006A, 0x030C, 0x0047, 0x0301, 0x0067, 0x0301, 0x004E, 0x0300, 0x006E, 0x0300, 0x0041, 0x030A, 0x0301, 0x0061, 0x030A, 0x0301, 0x00C6, 0x0301, 0x00E6, 0x0301, 0x00D8, 0x0301, 0x00F8, 0x0301, 0x0041, 0x030F, 0x0061, 0x030F, 0x0041, 0x0311, 0x0061, 0x0311, 0x0045, 0x030F, 0x0065, 0x030F, 0x0045, 0x0311, 0x0065, 0x0311, 0x0049, 0x030F, 0x0069, 0x030F, 0x0049, 0x0311, 0x0069, 0x0311, 0x004F, 0x030F, 0x006F, 0x030F, 0x004F, 0x0311, 0x006F, 0x0311, 0x0052, 0x030F, 0x0072, 0x030F, 0x0052, 0x0311, 0x0072, 0x0311, 0x0055, 0x030F, 0x0075, 0x030F, 0x0055, 0x0311, 0x0075, 0x0311, 0x0053, 0x0326, 0x0073, 0x0326, 0x0054, 0x0326, 0x0074, 0x0326, 0x0048, 0x030C, 0x0068, 0x030C, 0x0041, 0x0307, 0x0061, 0x0307, 0x0045, 0x0327, 0x0065, 0x0327, 0x004F, 0x0308, 0x0304, 0x006F, 0x0308, 0x0304, 0x004F, 0x0303, 0x0304, 0x006F, 0x0303, 0x0304, 0x004F, 0x0307, 0x006F, 0x0307, 0x004F, 0x0307, 0x0304, 0x006F, 0x0307, 0x0304, 0x0059, 0x0304, 0x0079, 0x0304, 0x0300, 0x0301, 0x0313, 0x0308, 0x0301, 0x02B9, 0x003B, 0x00A8, 0x0301, 0x0391, 0x0301, 0x00B7, 0x0395, 0x0301, 0x0397, 0x0301, 0x0399, 0x0301, 0x039F, 0x0301, 0x03A5, 0x0301, 0x03A9, 0x0301, 0x03B9, 0x0308, 0x0301, 0x0399, 0x0308, 0x03A5, 0x0308, 0x03B1, 0x0301, 0x03B5, 0x0301, 0x03B7, 0x0301, 0x03B9, 0x0301, 0x03C5, 0x0308, 0x0301, 0x03B9, 0x0308, 0x03C5, 0x0308, 0x03BF, 0x0301, 0x03C5, 0x0301, 0x03C9, 0x0301, 0x03D2, 0x0301, 0x03D2, 0x0308, 0x0415, 0x0300, 0x0415, 0x0308, 0x0413, 0x0301, 0x0406, 0x0308, 0x041A, 0x0301, 0x0418, 0x0300, 0x0423, 0x0306, 0x0418, 0x0306, 0x0438, 0x0306, 0x0435, 0x0300, 0x0435, 0x0308, 0x0433, 0x0301, 0x0456, 0x0308, 0x043A, 0x0301, 0x0438, 0x0300, 0x0443, 0x0306, 0x0474, 0x030F, 0x0475, 0x030F, 0x0416, 0x0306, 0x0436, 0x0306, 0x0410, 0x0306, 0x0430, 0x0306, 0x0410, 0x0308, 0x0430, 0x0308, 0x0415, 0x0306, 0x0435, 0x0306, 0x04D8, 0x0308, 0x04D9, 0x0308, 0x0416, 0x0308, 0x0436, 0x0308, 0x0417, 0x0308, 0x0437, 0x0308, 0x0418, 0x0304, 0x0438, 0x0304, 0x0418, 0x0308, 0x0438, 0x0308, 0x041E, 0x0308, 0x043E, 0x0308, 0x04E8, 0x0308, 0x04E9, 0x0308, 0x042D, 0x0308, 0x044D, 0x0308, 0x0423, 0x0304, 0x0443, 0x0304, 0x0423, 0x0308, 0x0443, 0x0308, 0x0423, 0x030B, 0x0443, 0x030B, 0x0427, 0x0308, 0x0447, 0x0308, 0x042B, 0x0308, 0x044B, 0x0308, 0x0627, 0x0653, 0x0627, 0x0654, 0x0648, 0x0654, 0x0627, 0x0655, 0x064A, 0x0654, 0x06D5, 0x0654, 0x06C1, 0x0654, 0x06D2, 0x0654, 0x0928, 0x093C, 0x0930, 0x093C, 0x0933, 0x093C, 0x0915, 0x093C, 0x0916, 0x093C, 0x0917, 0x093C, 0x091C, 0x093C, 0x0921, 0x093C, 0x0922, 0x093C, 0x092B, 0x093C, 0x092F, 0x093C, 0x09C7, 0x09BE, 0x09C7, 0x09D7, 0x09A1, 0x09BC, 0x09A2, 0x09BC, 0x09AF, 0x09BC, 0x0A32, 0x0A3C, 0x0A38, 0x0A3C, 0x0A16, 0x0A3C, 0x0A17, 0x0A3C, 0x0A1C, 0x0A3C, 0x0A2B, 0x0A3C, 0x0B47, 0x0B56, 0x0B47, 0x0B3E, 0x0B47, 0x0B57, 0x0B21, 0x0B3C, 0x0B22, 0x0B3C, 0x0B92, 0x0BD7, 0x0BC6, 0x0BBE, 0x0BC7, 0x0BBE, 0x0BC6, 0x0BD7, 0x0C46, 0x0C56, 0x0CBF, 0x0CD5, 0x0CC6, 0x0CD5, 0x0CC6, 0x0CD6, 0x0CC6, 0x0CC2, 0x0CC6, 0x0CC2, 0x0CD5, 0x0D46, 0x0D3E, 0x0D47, 0x0D3E, 0x0D46, 0x0D57, 0x0DD9, 0x0DCA, 0x0DD9, 0x0DCF, 0x0DD9, 0x0DCF, 0x0DCA, 0x0DD9, 0x0DDF, 0x0F42, 0x0FB7, 0x0F4C, 0x0FB7, 0x0F51, 0x0FB7, 0x0F56, 0x0FB7, 0x0F5B, 0x0FB7, 0x0F40, 0x0FB5, 0x0F71, 0x0F72, 0x0F71, 0x0F74, 0x0FB2, 0x0F80, 0x0FB3, 0x0F80, 0x0F71, 0x0F80, 0x0F92, 0x0FB7, 0x0F9C, 0x0FB7, 0x0FA1, 0x0FB7, 0x0FA6, 0x0FB7, 0x0FAB, 0x0FB7, 0x0F90, 0x0FB5, 0x1025, 0x102E, 0x1B05, 0x1B35, 0x1B07, 0x1B35, 0x1B09, 0x1B35, 0x1B0B, 0x1B35, 0x1B0D, 0x1B35, 0x1B11, 0x1B35, 0x1B3A, 0x1B35, 0x1B3C, 0x1B35, 0x1B3E, 0x1B35, 0x1B3F, 0x1B35, 0x1B42, 0x1B35, 0x0041, 0x0325, 0x0061, 0x0325, 0x0042, 0x0307, 0x0062, 0x0307, 0x0042, 0x0323, 0x0062, 0x0323, 0x0042, 0x0331, 0x0062, 0x0331, 0x0043, 0x0327, 0x0301, 0x0063, 0x0327, 0x0301, 0x0044, 0x0307, 0x0064, 0x0307, 0x0044, 0x0323, 0x0064, 0x0323, 0x0044, 0x0331, 0x0064, 0x0331, 0x0044, 0x0327, 0x0064, 0x0327, 0x0044, 0x032D, 0x0064, 0x032D, 0x0045, 0x0304, 0x0300, 0x0065, 0x0304, 0x0300, 0x0045, 0x0304, 0x0301, 0x0065, 0x0304, 0x0301, 0x0045, 0x032D, 0x0065, 0x032D, 0x0045, 0x0330, 0x0065, 0x0330, 0x0045, 0x0327, 0x0306, 0x0065, 0x0327, 0x0306, 0x0046, 0x0307, 0x0066, 0x0307, 0x0047, 0x0304, 0x0067, 0x0304, 0x0048, 0x0307, 0x0068, 0x0307, 0x0048, 0x0323, 0x0068, 0x0323, 0x0048, 0x0308, 0x0068, 0x0308, 0x0048, 0x0327, 0x0068, 0x0327, 0x0048, 0x032E, 0x0068, 0x032E, 0x0049, 0x0330, 0x0069, 0x0330, 0x0049, 0x0308, 0x0301, 0x0069, 0x0308, 0x0301, 0x004B, 0x0301, 0x006B, 0x0301, 0x004B, 0x0323, 0x006B, 0x0323, 0x004B, 0x0331, 0x006B, 0x0331, 0x004C, 0x0323, 0x006C, 0x0323, 0x004C, 0x0323, 0x0304, 0x006C, 0x0323, 0x0304, 0x004C, 0x0331, 0x006C, 0x0331, 0x004C, 0x032D, 0x006C, 0x032D, 0x004D, 0x0301, 0x006D, 0x0301, 0x004D, 0x0307, 0x006D, 0x0307, 0x004D, 0x0323, 0x006D, 0x0323, 0x004E, 0x0307, 0x006E, 0x0307, 0x004E, 0x0323, 0x006E, 0x0323, 0x004E, 0x0331, 0x006E, 0x0331, 0x004E, 0x032D, 0x006E, 0x032D, 0x004F, 0x0303, 0x0301, 0x006F, 0x0303, 0x0301, 0x004F, 0x0303, 0x0308, 0x006F, 0x0303, 0x0308, 0x004F, 0x0304, 0x0300, 0x006F, 0x0304, 0x0300, 0x004F, 0x0304, 0x0301, 0x006F, 0x0304, 0x0301, 0x0050, 0x0301, 0x0070, 0x0301, 0x0050, 0x0307, 0x0070, 0x0307, 0x0052, 0x0307, 0x0072, 0x0307, 0x0052, 0x0323, 0x0072, 0x0323, 0x0052, 0x0323, 0x0304, 0x0072, 0x0323, 0x0304, 0x0052, 0x0331, 0x0072, 0x0331, 0x0053, 0x0307, 0x0073, 0x0307, 0x0053, 0x0323, 0x0073, 0x0323, 0x0053, 0x0301, 0x0307, 0x0073, 0x0301, 0x0307, 0x0053, 0x030C, 0x0307, 0x0073, 0x030C, 0x0307, 0x0053, 0x0323, 0x0307, 0x0073, 0x0323, 0x0307, 0x0054, 0x0307, 0x0074, 0x0307, 0x0054, 0x0323, 0x0074, 0x0323, 0x0054, 0x0331, 0x0074, 0x0331, 0x0054, 0x032D, 0x0074, 0x032D, 0x0055, 0x0324, 0x0075, 0x0324, 0x0055, 0x0330, 0x0075, 0x0330, 0x0055, 0x032D, 0x0075, 0x032D, 0x0055, 0x0303, 0x0301, 0x0075, 0x0303, 0x0301, 0x0055, 0x0304, 0x0308, 0x0075, 0x0304, 0x0308, 0x0056, 0x0303, 0x0076, 0x0303, 0x0056, 0x0323, 0x0076, 0x0323, 0x0057, 0x0300, 0x0077, 0x0300, 0x0057, 0x0301, 0x0077, 0x0301, 0x0057, 0x0308, 0x0077, 0x0308, 0x0057, 0x0307, 0x0077, 0x0307, 0x0057, 0x0323, 0x0077, 0x0323, 0x0058, 0x0307, 0x0078, 0x0307, 0x0058, 0x0308, 0x0078, 0x0308, 0x0059, 0x0307, 0x0079, 0x0307, 0x005A, 0x0302, 0x007A, 0x0302, 0x005A, 0x0323, 0x007A, 0x0323, 0x005A, 0x0331, 0x007A, 0x0331, 0x0068, 0x0331, 0x0074, 0x0308, 0x0077, 0x030A, 0x0079, 0x030A, 0x017F, 0x0307, 0x0041, 0x0323, 0x0061, 0x0323, 0x0041, 0x0309, 0x0061, 0x0309, 0x0041, 0x0302, 0x0301, 0x0061, 0x0302, 0x0301, 0x0041, 0x0302, 0x0300, 0x0061, 0x0302, 0x0300, 0x0041, 0x0302, 0x0309, 0x0061, 0x0302, 0x0309, 0x0041, 0x0302, 0x0303, 0x0061, 0x0302, 0x0303, 0x0041, 0x0323, 0x0302, 0x0061, 0x0323, 0x0302, 0x0041, 0x0306, 0x0301, 0x0061, 0x0306, 0x0301, 0x0041, 0x0306, 0x0300, 0x0061, 0x0306, 0x0300, 0x0041, 0x0306, 0x0309, 0x0061, 0x0306, 0x0309, 0x0041, 0x0306, 0x0303, 0x0061, 0x0306, 0x0303, 0x0041, 0x0323, 0x0306, 0x0061, 0x0323, 0x0306, 0x0045, 0x0323, 0x0065, 0x0323, 0x0045, 0x0309, 0x0065, 0x0309, 0x0045, 0x0303, 0x0065, 0x0303, 0x0045, 0x0302, 0x0301, 0x0065, 0x0302, 0x0301, 0x0045, 0x0302, 0x0300, 0x0065, 0x0302, 0x0300, 0x0045, 0x0302, 0x0309, 0x0065, 0x0302, 0x0309, 0x0045, 0x0302, 0x0303, 0x0065, 0x0302, 0x0303, 0x0045, 0x0323, 0x0302, 0x0065, 0x0323, 0x0302, 0x0049, 0x0309, 0x0069, 0x0309, 0x0049, 0x0323, 0x0069, 0x0323, 0x004F, 0x0323, 0x006F, 0x0323, 0x004F, 0x0309, 0x006F, 0x0309, 0x004F, 0x0302, 0x0301, 0x006F, 0x0302, 0x0301, 0x004F, 0x0302, 0x0300, 0x006F, 0x0302, 0x0300, 0x004F, 0x0302, 0x0309, 0x006F, 0x0302, 0x0309, 0x004F, 0x0302, 0x0303, 0x006F, 0x0302, 0x0303, 0x004F, 0x0323, 0x0302, 0x006F, 0x0323, 0x0302, 0x004F, 0x031B, 0x0301, 0x006F, 0x031B, 0x0301, 0x004F, 0x031B, 0x0300, 0x006F, 0x031B, 0x0300, 0x004F, 0x031B, 0x0309, 0x006F, 0x031B, 0x0309, 0x004F, 0x031B, 0x0303, 0x006F, 0x031B, 0x0303, 0x004F, 0x031B, 0x0323, 0x006F, 0x031B, 0x0323, 0x0055, 0x0323, 0x0075, 0x0323, 0x0055, 0x0309, 0x0075, 0x0309, 0x0055, 0x031B, 0x0301, 0x0075, 0x031B, 0x0301, 0x0055, 0x031B, 0x0300, 0x0075, 0x031B, 0x0300, 0x0055, 0x031B, 0x0309, 0x0075, 0x031B, 0x0309, 0x0055, 0x031B, 0x0303, 0x0075, 0x031B, 0x0303, 0x0055, 0x031B, 0x0323, 0x0075, 0x031B, 0x0323, 0x0059, 0x0300, 0x0079, 0x0300, 0x0059, 0x0323, 0x0079, 0x0323, 0x0059, 0x0309, 0x0079, 0x0309, 0x0059, 0x0303, 0x0079, 0x0303, 0x03B1, 0x0313, 0x03B1, 0x0314, 0x03B1, 0x0313, 0x0300, 0x03B1, 0x0314, 0x0300, 0x03B1, 0x0313, 0x0301, 0x03B1, 0x0314, 0x0301, 0x03B1, 0x0313, 0x0342, 0x03B1, 0x0314, 0x0342, 0x0391, 0x0313, 0x0391, 0x0314, 0x0391, 0x0313, 0x0300, 0x0391, 0x0314, 0x0300, 0x0391, 0x0313, 0x0301, 0x0391, 0x0314, 0x0301, 0x0391, 0x0313, 0x0342, 0x0391, 0x0314, 0x0342, 0x03B5, 0x0313, 0x03B5, 0x0314, 0x03B5, 0x0313, 0x0300, 0x03B5, 0x0314, 0x0300, 0x03B5, 0x0313, 0x0301, 0x03B5, 0x0314, 0x0301, 0x0395, 0x0313, 0x0395, 0x0314, 0x0395, 0x0313, 0x0300, 0x0395, 0x0314, 0x0300, 0x0395, 0x0313, 0x0301, 0x0395, 0x0314, 0x0301, 0x03B7, 0x0313, 0x03B7, 0x0314, 0x03B7, 0x0313, 0x0300, 0x03B7, 0x0314, 0x0300, 0x03B7, 0x0313, 0x0301, 0x03B7, 0x0314, 0x0301, 0x03B7, 0x0313, 0x0342, 0x03B7, 0x0314, 0x0342, 0x0397, 0x0313, 0x0397, 0x0314, 0x0397, 0x0313, 0x0300, 0x0397, 0x0314, 0x0300, 0x0397, 0x0313, 0x0301, 0x0397, 0x0314, 0x0301, 0x0397, 0x0313, 0x0342, 0x0397, 0x0314, 0x0342, 0x03B9, 0x0313, 0x03B9, 0x0314, 0x03B9, 0x0313, 0x0300, 0x03B9, 0x0314, 0x0300, 0x03B9, 0x0313, 0x0301, 0x03B9, 0x0314, 0x0301, 0x03B9, 0x0313, 0x0342, 0x03B9, 0x0314, 0x0342, 0x0399, 0x0313, 0x0399, 0x0314, 0x0399, 0x0313, 0x0300, 0x0399, 0x0314, 0x0300, 0x0399, 0x0313, 0x0301, 0x0399, 0x0314, 0x0301, 0x0399, 0x0313, 0x0342, 0x0399, 0x0314, 0x0342, 0x03BF, 0x0313, 0x03BF, 0x0314, 0x03BF, 0x0313, 0x0300, 0x03BF, 0x0314, 0x0300, 0x03BF, 0x0313, 0x0301, 0x03BF, 0x0314, 0x0301, 0x039F, 0x0313, 0x039F, 0x0314, 0x039F, 0x0313, 0x0300, 0x039F, 0x0314, 0x0300, 0x039F, 0x0313, 0x0301, 0x039F, 0x0314, 0x0301, 0x03C5, 0x0313, 0x03C5, 0x0314, 0x03C5, 0x0313, 0x0300, 0x03C5, 0x0314, 0x0300, 0x03C5, 0x0313, 0x0301, 0x03C5, 0x0314, 0x0301, 0x03C5, 0x0313, 0x0342, 0x03C5, 0x0314, 0x0342, 0x03A5, 0x0314, 0x03A5, 0x0314, 0x0300, 0x03A5, 0x0314, 0x0301, 0x03A5, 0x0314, 0x0342, 0x03C9, 0x0313, 0x03C9, 0x0314, 0x03C9, 0x0313, 0x0300, 0x03C9, 0x0314, 0x0300, 0x03C9, 0x0313, 0x0301, 0x03C9, 0x0314, 0x0301, 0x03C9, 0x0313, 0x0342, 0x03C9, 0x0314, 0x0342, 0x03A9, 0x0313, 0x03A9, 0x0314, 0x03A9, 0x0313, 0x0300, 0x03A9, 0x0314, 0x0300, 0x03A9, 0x0313, 0x0301, 0x03A9, 0x0314, 0x0301, 0x03A9, 0x0313, 0x0342, 0x03A9, 0x0314, 0x0342, 0x03B1, 0x0300, 0x03B1, 0x0301, 0x03B5, 0x0300, 0x03B5, 0x0301, 0x03B7, 0x0300, 0x03B7, 0x0301, 0x03B9, 0x0300, 0x03B9, 0x0301, 0x03BF, 0x0300, 0x03BF, 0x0301, 0x03C5, 0x0300, 0x03C5, 0x0301, 0x03C9, 0x0300, 0x03C9, 0x0301, 0x03B1, 0x0313, 0x0345, 0x03B1, 0x0314, 0x0345, 0x03B1, 0x0313, 0x0300, 0x0345, 0x03B1, 0x0314, 0x0300, 0x0345, 0x03B1, 0x0313, 0x0301, 0x0345, 0x03B1, 0x0314, 0x0301, 0x0345, 0x03B1, 0x0313, 0x0342, 0x0345, 0x03B1, 0x0314, 0x0342, 0x0345, 0x0391, 0x0313, 0x0345, 0x0391, 0x0314, 0x0345, 0x0391, 0x0313, 0x0300, 0x0345, 0x0391, 0x0314, 0x0300, 0x0345, 0x0391, 0x0313, 0x0301, 0x0345, 0x0391, 0x0314, 0x0301, 0x0345, 0x0391, 0x0313, 0x0342, 0x0345, 0x0391, 0x0314, 0x0342, 0x0345, 0x03B7, 0x0313, 0x0345, 0x03B7, 0x0314, 0x0345, 0x03B7, 0x0313, 0x0300, 0x0345, 0x03B7, 0x0314, 0x0300, 0x0345, 0x03B7, 0x0313, 0x0301, 0x0345, 0x03B7, 0x0314, 0x0301, 0x0345, 0x03B7, 0x0313, 0x0342, 0x0345, 0x03B7, 0x0314, 0x0342, 0x0345, 0x0397, 0x0313, 0x0345, 0x0397, 0x0314, 0x0345, 0x0397, 0x0313, 0x0300, 0x0345, 0x0397, 0x0314, 0x0300, 0x0345, 0x0397, 0x0313, 0x0301, 0x0345, 0x0397, 0x0314, 0x0301, 0x0345, 0x0397, 0x0313, 0x0342, 0x0345, 0x0397, 0x0314, 0x0342, 0x0345, 0x03C9, 0x0313, 0x0345, 0x03C9, 0x0314, 0x0345, 0x03C9, 0x0313, 0x0300, 0x0345, 0x03C9, 0x0314, 0x0300, 0x0345, 0x03C9, 0x0313, 0x0301, 0x0345, 0x03C9, 0x0314, 0x0301, 0x0345, 0x03C9, 0x0313, 0x0342, 0x0345, 0x03C9, 0x0314, 0x0342, 0x0345, 0x03A9, 0x0313, 0x0345, 0x03A9, 0x0314, 0x0345, 0x03A9, 0x0313, 0x0300, 0x0345, 0x03A9, 0x0314, 0x0300, 0x0345, 0x03A9, 0x0313, 0x0301, 0x0345, 0x03A9, 0x0314, 0x0301, 0x0345, 0x03A9, 0x0313, 0x0342, 0x0345, 0x03A9, 0x0314, 0x0342, 0x0345, 0x03B1, 0x0306, 0x03B1, 0x0304, 0x03B1, 0x0300, 0x0345, 0x03B1, 0x0345, 0x03B1, 0x0301, 0x0345, 0x03B1, 0x0342, 0x03B1, 0x0342, 0x0345, 0x0391, 0x0306, 0x0391, 0x0304, 0x0391, 0x0300, 0x0391, 0x0301, 0x0391, 0x0345, 0x03B9, 0x00A8, 0x0342, 0x03B7, 0x0300, 0x0345, 0x03B7, 0x0345, 0x03B7, 0x0301, 0x0345, 0x03B7, 0x0342, 0x03B7, 0x0342, 0x0345, 0x0395, 0x0300, 0x0395, 0x0301, 0x0397, 0x0300, 0x0397, 0x0301, 0x0397, 0x0345, 0x1FBF, 0x0300, 0x1FBF, 0x0301, 0x1FBF, 0x0342, 0x03B9, 0x0306, 0x03B9, 0x0304, 0x03B9, 0x0308, 0x0300, 0x03B9, 0x0308, 0x0301, 0x03B9, 0x0342, 0x03B9, 0x0308, 0x0342, 0x0399, 0x0306, 0x0399, 0x0304, 0x0399, 0x0300, 0x0399, 0x0301, 0x1FFE, 0x0300, 0x1FFE, 0x0301, 0x1FFE, 0x0342, 0x03C5, 0x0306, 0x03C5, 0x0304, 0x03C5, 0x0308, 0x0300, 0x03C5, 0x0308, 0x0301, 0x03C1, 0x0313, 0x03C1, 0x0314, 0x03C5, 0x0342, 0x03C5, 0x0308, 0x0342, 0x03A5, 0x0306, 0x03A5, 0x0304, 0x03A5, 0x0300, 0x03A5, 0x0301, 0x03A1, 0x0314, 0x00A8, 0x0300, 0x00A8, 0x0301, 0x0060, 0x03C9, 0x0300, 0x0345, 0x03C9, 0x0345, 0x03C9, 0x0301, 0x0345, 0x03C9, 0x0342, 0x03C9, 0x0342, 0x0345, 0x039F, 0x0300, 0x039F, 0x0301, 0x03A9, 0x0300, 0x03A9, 0x0301, 0x03A9, 0x0345, 0x00B4, 0x2002, 0x2003, 0x03A9, 0x004B, 0x0041, 0x030A, 0x2190, 0x0338, 0x2192, 0x0338, 0x2194, 0x0338, 0x21D0, 0x0338, 0x21D4, 0x0338, 0x21D2, 0x0338, 0x2203, 0x0338, 0x2208, 0x0338, 0x220B, 0x0338, 0x2223, 0x0338, 0x2225, 0x0338, 0x223C, 0x0338, 0x2243, 0x0338, 0x2245, 0x0338, 0x2248, 0x0338, 0x003D, 0x0338, 0x2261, 0x0338, 0x224D, 0x0338, 0x003C, 0x0338, 0x003E, 0x0338, 0x2264, 0x0338, 0x2265, 0x0338, 0x2272, 0x0338, 0x2273, 0x0338, 0x2276, 0x0338, 0x2277, 0x0338, 0x227A, 0x0338, 0x227B, 0x0338, 0x2282, 0x0338, 0x2283, 0x0338, 0x2286, 0x0338, 0x2287, 0x0338, 0x22A2, 0x0338, 0x22A8, 0x0338, 0x22A9, 0x0338, 0x22AB, 0x0338, 0x227C, 0x0338, 0x227D, 0x0338, 0x2291, 0x0338, 0x2292, 0x0338, 0x22B2, 0x0338, 0x22B3, 0x0338, 0x22B4, 0x0338, 0x22B5, 0x0338, 0x3008, 0x3009, 0x2ADD, 0x0338, 0x304B, 0x3099, 0x304D, 0x3099, 0x304F, 0x3099, 0x3051, 0x3099, 0x3053, 0x3099, 0x3055, 0x3099, 0x3057, 0x3099, 0x3059, 0x3099, 0x305B, 0x3099, 0x305D, 0x3099, 0x305F, 0x3099, 0x3061, 0x3099, 0x3064, 0x3099, 0x3066, 0x3099, 0x3068, 0x3099, 0x306F, 0x3099, 0x306F, 0x309A, 0x3072, 0x3099, 0x3072, 0x309A, 0x3075, 0x3099, 0x3075, 0x309A, 0x3078, 0x3099, 0x3078, 0x309A, 0x307B, 0x3099, 0x307B, 0x309A, 0x3046, 0x3099, 0x309D, 0x3099, 0x30AB, 0x3099, 0x30AD, 0x3099, 0x30AF, 0x3099, 0x30B1, 0x3099, 0x30B3, 0x3099, 0x30B5, 0x3099, 0x30B7, 0x3099, 0x30B9, 0x3099, 0x30BB, 0x3099, 0x30BD, 0x3099, 0x30BF, 0x3099, 0x30C1, 0x3099, 0x30C4, 0x3099, 0x30C6, 0x3099, 0x30C8, 0x3099, 0x30CF, 0x3099, 0x30CF, 0x309A, 0x30D2, 0x3099, 0x30D2, 0x309A, 0x30D5, 0x3099, 0x30D5, 0x309A, 0x30D8, 0x3099, 0x30D8, 0x309A, 0x30DB, 0x3099, 0x30DB, 0x309A, 0x30A6, 0x3099, 0x30EF, 0x3099, 0x30F0, 0x3099, 0x30F1, 0x3099, 0x30F2, 0x3099, 0x30FD, 0x3099, 0x8C48, 0x66F4, 0x8ECA, 0x8CC8, 0x6ED1, 0x4E32, 0x53E5, 0x9F9C, 0x9F9C, 0x5951, 0x91D1, 0x5587, 0x5948, 0x61F6, 0x7669, 0x7F85, 0x863F, 0x87BA, 0x88F8, 0x908F, 0x6A02, 0x6D1B, 0x70D9, 0x73DE, 0x843D, 0x916A, 0x99F1, 0x4E82, 0x5375, 0x6B04, 0x721B, 0x862D, 0x9E1E, 0x5D50, 0x6FEB, 0x85CD, 0x8964, 0x62C9, 0x81D8, 0x881F, 0x5ECA, 0x6717, 0x6D6A, 0x72FC, 0x90CE, 0x4F86, 0x51B7, 0x52DE, 0x64C4, 0x6AD3, 0x7210, 0x76E7, 0x8001, 0x8606, 0x865C, 0x8DEF, 0x9732, 0x9B6F, 0x9DFA, 0x788C, 0x797F, 0x7DA0, 0x83C9, 0x9304, 0x9E7F, 0x8AD6, 0x58DF, 0x5F04, 0x7C60, 0x807E, 0x7262, 0x78CA, 0x8CC2, 0x96F7, 0x58D8, 0x5C62, 0x6A13, 0x6DDA, 0x6F0F, 0x7D2F, 0x7E37, 0x964B, 0x52D2, 0x808B, 0x51DC, 0x51CC, 0x7A1C, 0x7DBE, 0x83F1, 0x9675, 0x8B80, 0x62CF, 0x6A02, 0x8AFE, 0x4E39, 0x5BE7, 0x6012, 0x7387, 0x7570, 0x5317, 0x78FB, 0x4FBF, 0x5FA9, 0x4E0D, 0x6CCC, 0x6578, 0x7D22, 0x53C3, 0x585E, 0x7701, 0x8449, 0x8AAA, 0x6BBA, 0x8FB0, 0x6C88, 0x62FE, 0x82E5, 0x63A0, 0x7565, 0x4EAE, 0x5169, 0x51C9, 0x6881, 0x7CE7, 0x826F, 0x8AD2, 0x91CF, 0x52F5, 0x5442, 0x5973, 0x5EEC, 0x65C5, 0x6FFE, 0x792A, 0x95AD, 0x9A6A, 0x9E97, 0x9ECE, 0x529B, 0x66C6, 0x6B77, 0x8F62, 0x5E74, 0x6190, 0x6200, 0x649A, 0x6F23, 0x7149, 0x7489, 0x79CA, 0x7DF4, 0x806F, 0x8F26, 0x84EE, 0x9023, 0x934A, 0x5217, 0x52A3, 0x54BD, 0x70C8, 0x88C2, 0x8AAA, 0x5EC9, 0x5FF5, 0x637B, 0x6BAE, 0x7C3E, 0x7375, 0x4EE4, 0x56F9, 0x5BE7, 0x5DBA, 0x601C, 0x73B2, 0x7469, 0x7F9A, 0x8046, 0x9234, 0x96F6, 0x9748, 0x9818, 0x4F8B, 0x79AE, 0x91B4, 0x96B8, 0x60E1, 0x4E86, 0x50DA, 0x5BEE, 0x5C3F, 0x6599, 0x6A02, 0x71CE, 0x7642, 0x84FC, 0x907C, 0x9F8D, 0x6688, 0x962E, 0x5289, 0x677B, 0x67F3, 0x6D41, 0x6E9C, 0x7409, 0x7559, 0x786B, 0x7D10, 0x985E, 0x516D, 0x622E, 0x9678, 0x502B, 0x5D19, 0x6DEA, 0x8F2A, 0x5F8B, 0x6144, 0x6817, 0x7387, 0x9686, 0x5229, 0x540F, 0x5C65, 0x6613, 0x674E, 0x68A8, 0x6CE5, 0x7406, 0x75E2, 0x7F79, 0x88CF, 0x88E1, 0x91CC, 0x96E2, 0x533F, 0x6EBA, 0x541D, 0x71D0, 0x7498, 0x85FA, 0x96A3, 0x9C57, 0x9E9F, 0x6797, 0x6DCB, 0x81E8, 0x7ACB, 0x7B20, 0x7C92, 0x72C0, 0x7099, 0x8B58, 0x4EC0, 0x8336, 0x523A, 0x5207, 0x5EA6, 0x62D3, 0x7CD6, 0x5B85, 0x6D1E, 0x66B4, 0x8F3B, 0x884C, 0x964D, 0x898B, 0x5ED3, 0x5140, 0x55C0, 0x585A, 0x6674, 0x51DE, 0x732A, 0x76CA, 0x793C, 0x795E, 0x7965, 0x798F, 0x9756, 0x7CBE, 0x7FBD, 0x8612, 0x8AF8, 0x9038, 0x90FD, 0x98EF, 0x98FC, 0x9928, 0x9DB4, 0x90DE, 0x96B7, 0x4FAE, 0x50E7, 0x514D, 0x52C9, 0x52E4, 0x5351, 0x559D, 0x5606, 0x5668, 0x5840, 0x58A8, 0x5C64, 0x5C6E, 0x6094, 0x6168, 0x618E, 0x61F2, 0x654F, 0x65E2, 0x6691, 0x6885, 0x6D77, 0x6E1A, 0x6F22, 0x716E, 0x722B, 0x7422, 0x7891, 0x793E, 0x7949, 0x7948, 0x7950, 0x7956, 0x795D, 0x798D, 0x798E, 0x7A40, 0x7A81, 0x7BC0, 0x7DF4, 0x7E09, 0x7E41, 0x7F72, 0x8005, 0x81ED, 0x8279, 0x8279, 0x8457, 0x8910, 0x8996, 0x8B01, 0x8B39, 0x8CD3, 0x8D08, 0x8FB6, 0x9038, 0x96E3, 0x97FF, 0x983B, 0x6075, 0x242EE, 0x8218, 0x4E26, 0x51B5, 0x5168, 0x4F80, 0x5145, 0x5180, 0x52C7, 0x52FA, 0x559D, 0x5555, 0x5599, 0x55E2, 0x585A, 0x58B3, 0x5944, 0x5954, 0x5A62, 0x5B28, 0x5ED2, 0x5ED9, 0x5F69, 0x5FAD, 0x60D8, 0x614E, 0x6108, 0x618E, 0x6160, 0x61F2, 0x6234, 0x63C4, 0x641C, 0x6452, 0x6556, 0x6674, 0x6717, 0x671B, 0x6756, 0x6B79, 0x6BBA, 0x6D41, 0x6EDB, 0x6ECB, 0x6F22, 0x701E, 0x716E, 0x77A7, 0x7235, 0x72AF, 0x732A, 0x7471, 0x7506, 0x753B, 0x761D, 0x761F, 0x76CA, 0x76DB, 0x76F4, 0x774A, 0x7740, 0x78CC, 0x7AB1, 0x7BC0, 0x7C7B, 0x7D5B, 0x7DF4, 0x7F3E, 0x8005, 0x8352, 0x83EF, 0x8779, 0x8941, 0x8986, 0x8996, 0x8ABF, 0x8AF8, 0x8ACB, 0x8B01, 0x8AFE, 0x8AED, 0x8B39, 0x8B8A, 0x8D08, 0x8F38, 0x9072, 0x9199, 0x9276, 0x967C, 0x96E3, 0x9756, 0x97DB, 0x97FF, 0x980B, 0x983B, 0x9B12, 0x9F9C, 0x2284A, 0x22844, 0x233D5, 0x3B9D, 0x4018, 0x4039, 0x25249, 0x25CD0, 0x27ED3, 0x9F43, 0x9F8E, 0x05D9, 0x05B4, 0x05F2, 0x05B7, 0x05E9, 0x05C1, 0x05E9, 0x05C2, 0x05E9, 0x05BC, 0x05C1, 0x05E9, 0x05BC, 0x05C2, 0x05D0, 0x05B7, 0x05D0, 0x05B8, 0x05D0, 0x05BC, 0x05D1, 0x05BC, 0x05D2, 0x05BC, 0x05D3, 0x05BC, 0x05D4, 0x05BC, 0x05D5, 0x05BC, 0x05D6, 0x05BC, 0x05D8, 0x05BC, 0x05D9, 0x05BC, 0x05DA, 0x05BC, 0x05DB, 0x05BC, 0x05DC, 0x05BC, 0x05DE, 0x05BC, 0x05E0, 0x05BC, 0x05E1, 0x05BC, 0x05E3, 0x05BC, 0x05E4, 0x05BC, 0x05E6, 0x05BC, 0x05E7, 0x05BC, 0x05E8, 0x05BC, 0x05E9, 0x05BC, 0x05EA, 0x05BC, 0x05D5, 0x05B9, 0x05D1, 0x05BF, 0x05DB, 0x05BF, 0x05E4, 0x05BF, 0x11099, 0x110BA, 0x1109B, 0x110BA, 0x110A5, 0x110BA, 0x11131, 0x11127, 0x11132, 0x11127, 0x11347, 0x1133E, 0x11347, 0x11357, 0x114B9, 0x114BA, 0x114B9, 0x114B0, 0x114B9, 0x114BD, 0x115B8, 0x115AF, 0x115B9, 0x115AF, 0x11935, 0x11930, 0x1D157, 0x1D165, 0x1D158, 0x1D165, 0x1D158, 0x1D165, 0x1D16E, 0x1D158, 0x1D165, 0x1D16F, 0x1D158, 0x1D165, 0x1D170, 0x1D158, 0x1D165, 0x1D171, 0x1D158, 0x1D165, 0x1D172, 0x1D1B9, 0x1D165, 0x1D1BA, 0x1D165, 0x1D1B9, 0x1D165, 0x1D16E, 0x1D1BA, 0x1D165, 0x1D16E, 0x1D1B9, 0x1D165, 0x1D16F, 0x1D1BA, 0x1D165, 0x1D16F, 0x4E3D, 0x4E38, 0x4E41, 0x20122, 0x4F60, 0x4FAE, 0x4FBB, 0x5002, 0x507A, 0x5099, 0x50E7, 0x50CF, 0x349E, 0x2063A, 0x514D, 0x5154, 0x5164, 0x5177, 0x2051C, 0x34B9, 0x5167, 0x518D, 0x2054B, 0x5197, 0x51A4, 0x4ECC, 0x51AC, 0x51B5, 0x291DF, 0x51F5, 0x5203, 0x34DF, 0x523B, 0x5246, 0x5272, 0x5277, 0x3515, 0x52C7, 0x52C9, 0x52E4, 0x52FA, 0x5305, 0x5306, 0x5317, 0x5349, 0x5351, 0x535A, 0x5373, 0x537D, 0x537F, 0x537F, 0x537F, 0x20A2C, 0x7070, 0x53CA, 0x53DF, 0x20B63, 0x53EB, 0x53F1, 0x5406, 0x549E, 0x5438, 0x5448, 0x5468, 0x54A2, 0x54F6, 0x5510, 0x5553, 0x5563, 0x5584, 0x5584, 0x5599, 0x55AB, 0x55B3, 0x55C2, 0x5716, 0x5606, 0x5717, 0x5651, 0x5674, 0x5207, 0x58EE, 0x57CE, 0x57F4, 0x580D, 0x578B, 0x5832, 0x5831, 0x58AC, 0x214E4, 0x58F2, 0x58F7, 0x5906, 0x591A, 0x5922, 0x5962, 0x216A8, 0x216EA, 0x59EC, 0x5A1B, 0x5A27, 0x59D8, 0x5A66, 0x36EE, 0x36FC, 0x5B08, 0x5B3E, 0x5B3E, 0x219C8, 0x5BC3, 0x5BD8, 0x5BE7, 0x5BF3, 0x21B18, 0x5BFF, 0x5C06, 0x5F53, 0x5C22, 0x3781, 0x5C60, 0x5C6E, 0x5CC0, 0x5C8D, 0x21DE4, 0x5D43, 0x21DE6, 0x5D6E, 0x5D6B, 0x5D7C, 0x5DE1, 0x5DE2, 0x382F, 0x5DFD, 0x5E28, 0x5E3D, 0x5E69, 0x3862, 0x22183, 0x387C, 0x5EB0, 0x5EB3, 0x5EB6, 0x5ECA, 0x2A392, 0x5EFE, 0x22331, 0x22331, 0x8201, 0x5F22, 0x5F22, 0x38C7, 0x232B8, 0x261DA, 0x5F62, 0x5F6B, 0x38E3, 0x5F9A, 0x5FCD, 0x5FD7, 0x5FF9, 0x6081, 0x393A, 0x391C, 0x6094, 0x226D4, 0x60C7, 0x6148, 0x614C, 0x614E, 0x614C, 0x617A, 0x618E, 0x61B2, 0x61A4, 0x61AF, 0x61DE, 0x61F2, 0x61F6, 0x6210, 0x621B, 0x625D, 0x62B1, 0x62D4, 0x6350, 0x22B0C, 0x633D, 0x62FC, 0x6368, 0x6383, 0x63E4, 0x22BF1, 0x6422, 0x63C5, 0x63A9, 0x3A2E, 0x6469, 0x647E, 0x649D, 0x6477, 0x3A6C, 0x654F, 0x656C, 0x2300A, 0x65E3, 0x66F8, 0x6649, 0x3B19, 0x6691, 0x3B08, 0x3AE4, 0x5192, 0x5195, 0x6700, 0x669C, 0x80AD, 0x43D9, 0x6717, 0x671B, 0x6721, 0x675E, 0x6753, 0x233C3, 0x3B49, 0x67FA, 0x6785, 0x6852, 0x6885, 0x2346D, 0x688E, 0x681F, 0x6914, 0x3B9D, 0x6942, 0x69A3, 0x69EA, 0x6AA8, 0x236A3, 0x6ADB, 0x3C18, 0x6B21, 0x238A7, 0x6B54, 0x3C4E, 0x6B72, 0x6B9F, 0x6BBA, 0x6BBB, 0x23A8D, 0x21D0B, 0x23AFA, 0x6C4E, 0x23CBC, 0x6CBF, 0x6CCD, 0x6C67, 0x6D16, 0x6D3E, 0x6D77, 0x6D41, 0x6D69, 0x6D78, 0x6D85, 0x23D1E, 0x6D34, 0x6E2F, 0x6E6E, 0x3D33, 0x6ECB, 0x6EC7, 0x23ED1, 0x6DF9, 0x6F6E, 0x23F5E, 0x23F8E, 0x6FC6, 0x7039, 0x701E, 0x701B, 0x3D96, 0x704A, 0x707D, 0x7077, 0x70AD, 0x20525, 0x7145, 0x24263, 0x719C, 0x243AB, 0x7228, 0x7235, 0x7250, 0x24608, 0x7280, 0x7295, 0x24735, 0x24814, 0x737A, 0x738B, 0x3EAC, 0x73A5, 0x3EB8, 0x3EB8, 0x7447, 0x745C, 0x7471, 0x7485, 0x74CA, 0x3F1B, 0x7524, 0x24C36, 0x753E, 0x24C92, 0x7570, 0x2219F, 0x7610, 0x24FA1, 0x24FB8, 0x25044, 0x3FFC, 0x4008, 0x76F4, 0x250F3, 0x250F2, 0x25119, 0x25133, 0x771E, 0x771F, 0x771F, 0x774A, 0x4039, 0x778B, 0x4046, 0x4096, 0x2541D, 0x784E, 0x788C, 0x78CC, 0x40E3, 0x25626, 0x7956, 0x2569A, 0x256C5, 0x798F, 0x79EB, 0x412F, 0x7A40, 0x7A4A, 0x7A4F, 0x2597C, 0x25AA7, 0x25AA7, 0x7AEE, 0x4202, 0x25BAB, 0x7BC6, 0x7BC9, 0x4227, 0x25C80, 0x7CD2, 0x42A0, 0x7CE8, 0x7CE3, 0x7D00, 0x25F86, 0x7D63, 0x4301, 0x7DC7, 0x7E02, 0x7E45, 0x4334, 0x26228, 0x26247, 0x4359, 0x262D9, 0x7F7A, 0x2633E, 0x7F95, 0x7FFA, 0x8005, 0x264DA, 0x26523, 0x8060, 0x265A8, 0x8070, 0x2335F, 0x43D5, 0x80B2, 0x8103, 0x440B, 0x813E, 0x5AB5, 0x267A7, 0x267B5, 0x23393, 0x2339C, 0x8201, 0x8204, 0x8F9E, 0x446B, 0x8291, 0x828B, 0x829D, 0x52B3, 0x82B1, 0x82B3, 0x82BD, 0x82E6, 0x26B3C, 0x82E5, 0x831D, 0x8363, 0x83AD, 0x8323, 0x83BD, 0x83E7, 0x8457, 0x8353, 0x83CA, 0x83CC, 0x83DC, 0x26C36, 0x26D6B, 0x26CD5, 0x452B, 0x84F1, 0x84F3, 0x8516, 0x273CA, 0x8564, 0x26F2C, 0x455D, 0x4561, 0x26FB1, 0x270D2, 0x456B, 0x8650, 0x865C, 0x8667, 0x8669, 0x86A9, 0x8688, 0x870E, 0x86E2, 0x8779, 0x8728, 0x876B, 0x8786, 0x45D7, 0x87E1, 0x8801, 0x45F9, 0x8860, 0x8863, 0x27667, 0x88D7, 0x88DE, 0x4635, 0x88FA, 0x34BB, 0x278AE, 0x27966, 0x46BE, 0x46C7, 0x8AA0, 0x8AED, 0x8B8A, 0x8C55, 0x27CA8, 0x8CAB, 0x8CC1, 0x8D1B, 0x8D77, 0x27F2F, 0x20804, 0x8DCB, 0x8DBC, 0x8DF0, 0x208DE, 0x8ED4, 0x8F38, 0x285D2, 0x285ED, 0x9094, 0x90F1, 0x9111, 0x2872E, 0x911B, 0x9238, 0x92D7, 0x92D8, 0x927C, 0x93F9, 0x9415, 0x28BFA, 0x958B, 0x4995, 0x95B7, 0x28D77, 0x49E6, 0x96C3, 0x5DB2, 0x9723, 0x29145, 0x2921A, 0x4A6E, 0x4A76, 0x97E0, 0x2940A, 0x4AB2, 0x29496, 0x980B, 0x980B, 0x9829, 0x295B6, 0x98E2, 0x4B33, 0x9929, 0x99A7, 0x99C2, 0x99FE, 0x4BCE, 0x29B30, 0x9B12, 0x9C40, 0x9CFD, 0x4CCE, 0x4CED, 0x9D67, 0x2A0CE, 0x4CF8, 0x2A105, 0x2A20E, 0x2A291, 0x9EBB, 0x4D56, 0x9EF9, 0x9EFE, 0x9F05, 0x9F0F, 0x9F16, 0x9F3B, 0x2A600 }; constexpr short CANONICAL_DECOMPOSED_SALT[] { 0xCAE, 0x00E, 0x000, 0x000, 0x45A, 0x23F, 0x000, 0x1E8, 0x028, 0x280, 0x000, 0x006, 0x14C, 0x201, 0x006, 0x020, 0x003, 0x000, 0x071, 0x18B, 0x00D, 0x14D, 0x0D3, 0x000, 0x002, 0x085, 0x045, 0x00A, 0x004, 0x0E9, 0x021, 0x000, 0x000, 0x0AA, 0x002, 0x105, 0x000, 0x1B5, 0x00D, 0x000, 0x000, 0x000, 0x001, 0x000, 0x006, 0x067, 0x008, 0x000, 0x002, 0x000, 0x000, 0x047, 0x032, 0x000, 0x000, 0x03C, 0x045, 0x000, 0x000, 0x001, 0x000, 0x002, 0x010, 0x001, 0x000, 0x000, 0x001, 0x000, 0x0CE, 0x02C, 0x000, 0x01D, 0x000, 0x012, 0x000, 0x000, 0x011, 0x003, 0x01C, 0x007, 0x000, 0x001, 0x06A, 0x001, 0x016, 0x000, 0x06F, 0x000, 0x001, 0x025, 0x000, 0x027, 0x010, 0x000, 0x022, 0x000, 0x0BC, 0x000, 0x009, 0x000, 0x000, 0x015, 0x000, 0x000, 0x013, 0x000, 0x087, 0x000, 0x03A, 0x027, 0x000, 0x005, 0x016, 0x005, 0x014, 0x012, 0x021, 0x005, 0x002, 0x001, 0x000, 0x015, 0x026, 0x000, 0x004, 0x000, 0x000, 0x001, 0x017, 0x019, 0x000, 0x004, 0x000, 0x003, 0x000, 0x010, 0x001, 0x001, 0x000, 0x005, 0x001, 0x000, 0x000, 0x022, 0x000, 0x002, 0x004, 0x013, 0x000, 0x011, 0x05A, 0x005, 0x013, 0x00D, 0x04E, 0x008, 0x004, 0x018, 0x009, 0x064, 0x026, 0x000, 0x011, 0x000, 0x005, 0x030, 0x000, 0x001, 0x001, 0x000, 0x002, 0x001, 0x03E, 0x046, 0x005, 0x000, 0x001, 0x000, 0x001, 0x037, 0x000, 0x000, 0x005, 0x003, 0x000, 0x013, 0x002, 0x000, 0x001, 0x000, 0x006, 0x000, 0x00B, 0x01C, 0x001, 0x000, 0x005, 0x02F, 0x001, 0x000, 0x008, 0x01D, 0x030, 0x000, 0x025, 0x002, 0x012, 0x000, 0x000, 0x023, 0x002, 0x000, 0x000, 0x00B, 0x018, 0x00E, 0x000, 0x011, 0x00C, 0x015, 0x002, 0x000, 0x000, 0x003, 0x000, 0x008, 0x008, 0x008, 0x000, 0x000, 0x009, 0x00E, 0x000, 0x001, 0x009, 0x02C, 0x003, 0x001, 0x016, 0x011, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x004, 0x018, 0x001, 0x030, 0x004, 0x00C, 0x00E, 0x009, 0x000, 0x003, 0x00B, 0x000, 0x002, 0x005, 0x000, 0x010, 0x003, 0x002, 0x006, 0x00B, 0x008, 0x00A, 0x000, 0x008, 0x003, 0x001, 0x025, 0x003, 0x01B, 0x002, 0x012, 0x005, 0x007, 0x012, 0x000, 0x010, 0x000, 0x011, 0x000, 0x009, 0x000, 0x009, 0x000, 0x00C, 0x000, 0x000, 0x001, 0x000, 0x000, 0x000, 0x008, 0x000, 0x000, 0x000, 0x003, 0x000, 0x000, 0x000, 0x017, 0x000, 0x000, 0x003, 0x00C, 0x000, 0x000, 0x002, 0x001, 0x043, 0x000, 0x000, 0x00B, 0x005, 0x009, 0x000, 0x000, 0x022, 0x002, 0x004, 0x002, 0x002, 0x001, 0x001, 0x014, 0x001, 0x031, 0x000, 0x004, 0x020, 0x009, 0x007, 0x001, 0x021, 0x016, 0x001, 0x002, 0x000, 0x006, 0x000, 0x00E, 0x000, 0x005, 0x012, 0x00E, 0x004, 0x000, 0x000, 0x01D, 0x000, 0x000, 0x006, 0x000, 0x00A, 0x000, 0x000, 0x000, 0x016, 0x000, 0x000, 0x000, 0x000, 0x008, 0x000, 0x001, 0x000, 0x00B, 0x000, 0x001, 0x000, 0x000, 0x000, 0x004, 0x000, 0x000, 0x016, 0x000, 0x001, 0x000, 0x001, 0x01D, 0x007, 0x000, 0x001, 0x006, 0x004, 0x001, 0x003, 0x00E, 0x009, 0x010, 0x008, 0x002, 0x001, 0x000, 0x000, 0x003, 0x002, 0x002, 0x022, 0x000, 0x000, 0x004, 0x013, 0x011, 0x000, 0x002, 0x00D, 0x000, 0x001, 0x004, 0x00C, 0x001, 0x016, 0x00E, 0x002, 0x000, 0x000, 0x004, 0x000, 0x000, 0x000, 0x000, 0x000, 0x015, 0x000, 0x000, 0x003, 0x000, 0x000, 0x000, 0x00C, 0x000, 0x005, 0x004, 0x000, 0x000, 0x011, 0x00D, 0x011, 0x000, 0x004, 0x000, 0x003, 0x002, 0x001, 0x003, 0x001, 0x002, 0x002, 0x002, 0x001, 0x001, 0x003, 0x001, 0x001, 0x000, 0x002, 0x002, 0x002, 0x001, 0x00D, 0x000, 0x000, 0x000, 0x001, 0x002, 0x006, 0x00D, 0x001, 0x001, 0x004, 0x00C, 0x002, 0x006, 0x000, 0x004, 0x027, 0x01C, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x00A, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x00F, 0x007, 0x008, 0x000, 0x003, 0x000, 0x004, 0x000, 0x000, 0x001, 0x000, 0x00F, 0x001, 0x000, 0x000, 0x000, 0x002, 0x003, 0x000, 0x016, 0x00B, 0x005, 0x000, 0x000, 0x002, 0x008, 0x004, 0x000, 0x008, 0x000, 0x000, 0x000, 0x003, 0x004, 0x000, 0x001, 0x00B, 0x001, 0x000, 0x000, 0x018, 0x009, 0x004, 0x007, 0x000, 0x001, 0x002, 0x001, 0x000, 0x000, 0x001, 0x01E, 0x000, 0x002, 0x001, 0x000, 0x000, 0x009, 0x000, 0x000, 0x003, 0x00A, 0x002, 0x003, 0x001, 0x000, 0x004, 0x000, 0x004, 0x01F, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x024, 0x007, 0x005, 0x002, 0x002, 0x00A, 0x001, 0x000, 0x000, 0x003, 0x000, 0x001, 0x00C, 0x000, 0x001, 0x000, 0x000, 0x001, 0x002, 0x001, 0x00B, 0x000, 0x000, 0x001, 0x003, 0x006, 0x002, 0x00C, 0x00E, 0x01E, 0x000, 0x000, 0x010, 0x000, 0x001, 0x002, 0x00D, 0x003, 0x000, 0x006, 0x000, 0x002, 0x00A, 0x004, 0x000, 0x00F, 0x010, 0x002, 0x001, 0x002, 0x006, 0x000, 0x000, 0x00B, 0x000, 0x005, 0x00D, 0x002, 0x000, 0x003, 0x007, 0x000, 0x005, 0x004, 0x000, 0x000, 0x006, 0x001, 0x000, 0x000, 0x00F, 0x003, 0x000, 0x000, 0x00C, 0x000, 0x017, 0x002, 0x009, 0x000, 0x000, 0x000, 0x000, 0x000, 0x002, 0x000, 0x007, 0x001, 0x003, 0x00D, 0x000, 0x005, 0x007, 0x004, 0x008, 0x000, 0x00B, 0x008, 0x009, 0x001, 0x000, 0x000, 0x009, 0x005, 0x000, 0x008, 0x001, 0x000, 0x006, 0x000, 0x008, 0x000, 0x007, 0x000, 0x002, 0x002, 0x005, 0x004, 0x005, 0x000, 0x000, 0x001, 0x013, 0x008, 0x000, 0x000, 0x001, 0x000, 0x00A, 0x000, 0x006, 0x000, 0x000, 0x000, 0x000, 0x00C, 0x002, 0x004, 0x002, 0x000, 0x000, 0x003, 0x000, 0x004, 0x000, 0x003, 0x002, 0x007, 0x001, 0x000, 0x000, 0x000, 0x003, 0x000, 0x000, 0x000, 0x003, 0x000, 0x000, 0x000, 0x004, 0x000, 0x001, 0x000, 0x002, 0x000, 0x000, 0x002, 0x007, 0x000, 0x004, 0x000, 0x001, 0x001, 0x001, 0x009, 0x002, 0x002, 0x006, 0x003, 0x000, 0x000, 0x000, 0x001, 0x006, 0x005, 0x002, 0x000, 0x000, 0x004, 0x001, 0x002, 0x011, 0x001, 0x000, 0x000, 0x001, 0x001, 0x005, 0x000, 0x001, 0x000, 0x007, 0x000, 0x000, 0x000, 0x000, 0x003, 0x00E, 0x000, 0x000, 0x003, 0x000, 0x000, 0x000, 0x002, 0x001, 0x000, 0x010, 0x000, 0x000, 0x000, 0x002, 0x000, 0x002, 0x002, 0x006, 0x000, 0x009, 0x000, 0x006, 0x000, 0x001, 0x006, 0x005, 0x001, 0x001, 0x002, 0x008, 0x002, 0x003, 0x001, 0x004, 0x001, 0x000, 0x000, 0x00D, 0x002, 0x000, 0x000, 0x001, 0x02F, 0x001, 0x002, 0x001, 0x006, 0x015, 0x000, 0x000, 0x000, 0x00C, 0x004, 0x000, 0x000, 0x000, 0x000, 0x01A, 0x000, 0x000, 0x000, 0x000, 0x003, 0x007, 0x000, 0x009, 0x002, 0x001, 0x000, 0x001, 0x001, 0x000, 0x000, 0x000, 0x003, 0x000, 0x002, 0x000, 0x00A, 0x000, 0x000, 0x001, 0x000, 0x00E, 0x002, 0x002, 0x009, 0x002, 0x00B, 0x002, 0x001, 0x003, 0x001, 0x000, 0x000, 0x00A, 0x003, 0x001, 0x001, 0x002, 0x002, 0x002, 0x000, 0x002, 0x009, 0x000, 0x000, 0x001, 0x006, 0x001, 0x000, 0x007, 0x018, 0x001, 0x000, 0x007, 0x001, 0x001, 0x000, 0x019, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x003, 0x000, 0x000, 0x000, 0x000, 0x000, 0x004, 0x002, 0x000, 0x000, 0x001, 0x000, 0x00B, 0x001, 0x005, 0x000, 0x000, 0x000, 0x003, 0x002, 0x006, 0x002, 0x001, 0x005, 0x001, 0x006, 0x003, 0x001, 0x002, 0x001, 0x001, 0x01C, 0x006, 0x002, 0x002, 0x003, 0x001, 0x008, 0x000, 0x002, 0x000, 0x001, 0x003, 0x001, 0x016, 0x006, 0x00B, 0x000, 0x004, 0x004, 0x001, 0x002, 0x000, 0x000, 0x000, 0x000, 0x004, 0x000, 0x004, 0x000, 0x000, 0x000, 0x016, 0x000, 0x000, 0x000, 0x000, 0x003, 0x000, 0x000, 0x000, 0x001, 0x000, 0x004, 0x009, 0x00F, 0x005, 0x000, 0x001, 0x001, 0x008, 0x000, 0x001, 0x000, 0x000, 0x001, 0x002, 0x001, 0x001, 0x001, 0x000, 0x001, 0x000, 0x008, 0x001, 0x002, 0x001, 0x003, 0x002, 0x000, 0x002, 0x004, 0x001, 0x001, 0x004, 0x000, 0x00F, 0x000, 0x000, 0x000, 0x002, 0x009, 0x001, 0x000, 0x002, 0x000, 0x002, 0x004, 0x001, 0x000, 0x007, 0x005, 0x003, 0x002, 0x00A, 0x002, 0x003, 0x000, 0x005, 0x000, 0x000, 0x000, 0x006, 0x002, 0x00A, 0x000, 0x001, 0x006, 0x005, 0x000, 0x002, 0x003, 0x001, 0x008, 0x002, 0x000, 0x001, 0x000, 0x014, 0x000, 0x001, 0x002, 0x005, 0x000, 0x001, 0x001, 0x000, 0x001, 0x000, 0x002, 0x009, 0x000, 0x003, 0x001, 0x000, 0x00D, 0x001, 0x000, 0x000, 0x001, 0x004, 0x000, 0x000, 0x000, 0x000, 0x000, 0x002, 0x00E, 0x000, 0x002, 0x017, 0x005, 0x000, 0x000, 0x001, 0x002, 0x008, 0x000, 0x001, 0x009, 0x003, 0x000, 0x001, 0x007, 0x000, 0x004, 0x001, 0x008, 0x000, 0x000, 0x000, 0x001, 0x000, 0x000, 0x000, 0x002, 0x002, 0x005, 0x000, 0x005, 0x004, 0x001, 0x000, 0x003, 0x000, 0x002, 0x002, 0x003, 0x001, 0x00F, 0x001, 0x003, 0x002, 0x000, 0x000, 0x000, 0x001, 0x005, 0x000, 0x002, 0x007, 0x000, 0x000, 0x000, 0x000, 0x001, 0x000, 0x000, 0x008, 0x006, 0x000, 0x000, 0x001, 0x000, 0x000, 0x00A, 0x004, 0x005, 0x000, 0x000, 0x000, 0x001, 0x000, 0x002, 0x000, 0x000, 0x000, 0x004, 0x000, 0x009, 0x001, 0x001, 0x000, 0x006, 0x000, 0x000, 0x010, 0x001, 0x002, 0x005, 0x001, 0x001, 0x000, 0x000, 0x004, 0x000, 0x002, 0x002, 0x002, 0x000, 0x000, 0x001, 0x001, 0x003, 0x001, 0x003, 0x000, 0x003, 0x000, 0x000, 0x000, 0x000, 0x001, 0x006, 0x004, 0x001, 0x001, 0x002, 0x008, 0x003, 0x003, 0x000, 0x000, 0x004, 0x002, 0x001, 0x00E, 0x020, 0x000, 0x001, 0x001, 0x00C, 0x000, 0x000, 0x000, 0x004, 0x000, 0x002, 0x000, 0x000, 0x000, 0x004, 0x00A, 0x002, 0x001, 0x004, 0x006, 0x000, 0x000, 0x000, 0x006, 0x006, 0x000, 0x003, 0x000, 0x004, 0x001, 0x007, 0x001, 0x006, 0x008, 0x001, 0x007, 0x001, 0x001, 0x000, 0x001, 0x002, 0x003, 0x000, 0x006, 0x000, 0x000, 0x001, 0x008, 0x000, 0x003, 0x001, 0x001, 0x006, 0x002, 0x000, 0x002, 0x004, 0x006, 0x001, 0x004, 0x003, 0x006, 0x001, 0x003, 0x002, 0x004, 0x002, 0x000, 0x006, 0x002, 0x000, 0x005, 0x001, 0x001, 0x002, 0x000, 0x001, 0x002, 0x001, 0x001, 0x002, 0x002, 0x007, 0x003, 0x003, 0x000, 0x001, 0x007, 0x001, 0x00E, 0x000, 0x001, 0x000, 0x00B, 0x000, 0x001, 0x002, 0x001, 0x001, 0x003, 0x000, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x005, 0x000, 0x000, 0x000, 0x000, 0x000, 0x001, 0x002, 0x000, 0x000, 0x003, 0x006, 0x003, 0x002, 0x006, 0x000, 0x001, 0x003, 0x001, 0x002, 0x001, 0x002, 0x003, 0x000, 0x003, 0x001, 0x002, 0x004, 0x003, 0x000, 0x000, 0x010, 0x001, 0x002, 0x000, 0x001, 0x000, 0x003, 0x001, 0x002, 0x001, 0x001, 0x001, 0x000, 0x001, 0x000, 0x001, 0x000, 0x000, 0x000, 0x002, 0x000, 0x000, 0x005, 0x005, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x001, 0x005, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x002, 0x004, 0x002, 0x001, 0x001, 0x000, 0x002, 0x000, 0x000, 0x001, 0x000, 0x007, 0x00B, 0x003, 0x001, 0x002, 0x001, 0x002, 0x001, 0x007, 0x001, 0x004, 0x001, 0x001, 0x000, 0x001, 0x000, 0x001, 0x003, 0x001, 0x002, 0x002, 0x005, 0x001, 0x001, 0x009, 0x001, 0x001, 0x001, 0x000, 0x002, 0x003, 0x001, 0x000, 0x000, 0x008, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x003, 0x000, 0x000, 0x000, 0x005, 0x000, 0x004, 0x000, 0x000, 0x000, 0x001, 0x003, 0x001, 0x000, 0x001, 0x001, 0x003, 0x001, 0x000, 0x000, 0x000, 0x003, 0x004, 0x009, 0x006, 0x000, 0x009, 0x006, 0x002, 0x001, 0x003, 0x001, 0x001, 0x002, 0x004, 0x002, 0x003, 0x002, 0x000, 0x006, 0x001, 0x000, 0x002, 0x000, 0x000, 0x005, 0x00A, 0x001, 0x000, 0x000, 0x004, 0x000, 0x000, 0x004, 0x000, 0x001, 0x002, 0x001, 0x000, 0x000, 0x000, 0x000, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x00D, 0x000, 0x000, 0x003, 0x003, 0x000, 0x001, 0x002, 0x001, 0x000, 0x003, 0x000, 0x001, 0x000, 0x000, 0x000, 0x003, 0x000, 0x001, 0x002, 0x001, 0x000, 0x004, 0x002, 0x000, 0x001, 0x003, 0x000, 0x002, 0x003, 0x005, 0x005, 0x000, 0x000, 0x005, 0x001, 0x001, 0x010, 0x001, 0x003, 0x002, 0x000, 0x001, 0x001, 0x003, 0x001, 0x006, 0x005, 0x001, 0x002, 0x001, 0x002, 0x000, 0x003, 0x000, 0x001, 0x000, 0x000, 0x000, 0x001, 0x000, 0x000, 0x003, 0x007, 0x001, 0x002, 0x003, 0x000, 0x006, 0x002, 0x001, 0x000, 0x000, 0x001, 0x000, 0x002, 0x001, 0x000, 0x003, 0x001, 0x001, 0x003, 0x000, 0x002, 0x001, 0x001, 0x002, 0x001, 0x000, 0x000, 0x001, 0x000, 0x003, 0x000, 0x000, 0x002, 0x003, 0x000, 0x002, 0x000, 0x002, 0x001, 0x000, 0x003, 0x000, 0x001, 0x000, 0x000, 0x005, 0x002, 0x002, 0x002, 0x000, 0x002, 0x007, 0x005, 0x003, 0x001, 0x000, 0x00A, 0x001, 0x001, 0x004, 0x000, 0x006, 0x000, 0x000, 0x000, 0x000, 0x000, 0x001, 0x001, 0x001, 0x002, 0x000, 0x003, 0x001, 0x001, 0x001, 0x003, 0x003, 0x001, 0x004, 0x001, 0x000, 0x001, 0x001, 0x001, 0x003, 0x001, 0x005, 0x000, 0x002, 0x000, 0x001, 0x001, 0x000, 0x004, 0x000, 0x002, 0x001, 0x000, 0x000, 0x000, 0x004, 0x002, 0x000, 0x000, 0x000, 0x000, 0x004, 0x002, 0x003, 0x000, 0x000, 0x000, 0x000, 0x001, 0x000, 0x00A, 0x001, 0x002, 0x002, 0x001, 0x003, 0x000, 0x006, 0x001, 0x003, 0x006, 0x000, 0x002, 0x000, 0x000, 0x001, 0x000, 0x004, 0x005, 0x004, 0x001, 0x002, 0x000, 0x003, 0x000, 0x001, 0x002, 0x004, 0x001, 0x000, 0x003, 0x000, 0x000, 0x000, 0x000, 0x001, 0x001, 0x001, 0x001, 0x001, 0x003, 0x000, 0x001, 0x001, 0x000, 0x003, 0x000, 0x001, 0x004, 0x00A, 0x000, 0x003, 0x004, 0x005, 0x005, 0x007, 0x003, 0x003, 0x002, 0x000, 0x001, 0x002, 0x002, 0x004, 0x000, 0x001, 0x000, 0x000, 0x000, 0x002, 0x000, 0x000, 0x009, 0x003, 0x010, 0x001, 0x000, 0x002, 0x000, 0x001, 0x004, 0x000, 0x005, 0x002, 0x000, 0x005, 0x000, 0x001, 0x001, 0x004, 0x000, 0x007, 0x006, 0x004, 0x000, 0x000, 0x001, 0x007, 0x000, 0x002, 0x003, 0x000, 0x000, 0x000, 0x000, 0x000, 0x001, 0x000, 0x000, 0x000, 0x000, 0x003, 0x000, 0x001, 0x005, 0x002, 0x006, 0x001, 0x002, 0x005, 0x006, 0x002, 0x004, 0x000, 0x003, 0x001, 0x002, 0x001, 0x001, 0x000, 0x000, 0x002, 0x001, 0x003, 0x006, 0x001, 0x001, 0x001, 0x003, 0x007, 0x000, 0x000, 0x000, 0x002, 0x004, 0x000, 0x001, 0x001, 0x003, 0x001, 0x001, 0x002, 0x000, 0x001, 0x000, 0x005, 0x002, 0x005, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x005, 0x000, 0x007, 0x000, 0x000, 0x000, 0x000, 0x004, 0x000, 0x002, 0x004, 0x000, 0x000, 0x000, 0x001, 0x000, 0x000, 0x002, 0x000, 0x000, 0x001, 0x000, 0x001, 0x001, 0x001, 0x004, 0x002, 0x00B, 0x001, 0x000, 0x000, 0x001, 0x004, 0x002, 0x000, 0x001, 0x004, 0x001, 0x002, 0x002, 0x001, 0x002, 0x002, 0x000, 0x000, 0x000, 0x005, 0x002, 0x002, 0x000, 0x000, 0x004, 0x004, 0x005, 0x002, 0x000, 0x000, 0x00D, 0x000, 0x000, 0x000, 0x000, 0x000, 0x002, 0x000, 0x006, 0x000, 0x003, 0x000, 0x000, 0x000, 0x001, 0x002, 0x001, 0x001, 0x002, 0x009, 0x003, 0x000, 0x000, 0x003, 0x000, 0x001, 0x001, 0x001, 0x002, 0x000, 0x002, 0x003, 0x001, 0x004, 0x000, 0x002, 0x007, 0x001, 0x000, 0x002, 0x001, 0x001, 0x001, 0x002, 0x000, 0x005, 0x003, 0x000, 0x000, 0x001, 0x000, 0x005, 0x000, 0x001, 0x000, 0x007, 0x000, 0x000, 0x002, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x002, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x004, 0x004, 0x000, 0x000, 0x001, 0x002, 0x006, 0x000, 0x000, 0x002, 0x001, 0x002, 0x000, 0x001, 0x000, 0x002, 0x002, 0x002, 0x006, 0x001, 0x001, 0x007, 0x000, 0x001, 0x002, 0x005, 0x001, 0x002, 0x001, 0x001, 0x001, 0x003, 0x002, 0x002, 0x001, 0x004, 0x000, 0x000, 0x000, 0x004, 0x001, 0x001, 0x000, 0x002, 0x000, 0x001, 0x003, 0x000, 0x001, 0x002 }; /** Key: codepoint Value: first: index into CANONICAL_DECOMPOSED_CHARS, second: length */ constexpr std::pair> CANONICAL_DECOMPOSED_KV[] { {0x4EE, {0x29A, 0x2}}, {0x2F869, {0xB99, 0x1}}, {0x30B2, {0x8A9, 0x2}}, {0x1F2E, {0x60A, 0x3}}, {0x120, {0x0A6, 0x2}}, {0xF905, {0x8E6, 0x1}}, {0xF9E8, {0x9C9, 0x1}}, {0x1E3A, {0x3D2, 0x2}}, {0xF965, {0x946, 0x1}}, {0x1EBD, {0x4F6, 0x2}}, {0x2F83A, {0xB6A, 0x1}}, {0x101, {0x06C, 0x2}}, {0xF986, {0x967, 0x1}}, {0xFA57, {0xA2C, 0x1}}, {0x1F91, {0x704, 0x3}}, {0x4DF, {0x284, 0x2}}, {0x1E2, {0x17E, 0x2}}, {0x229, {0x1F2, 0x2}}, {0x1AF, {0x146, 0x2}}, {0xFA66, {0xA3B, 0x1}}, {0x1F39, {0x628, 0x2}}, {0x1E7A, {0x464, 0x3}}, {0x1F6C, {0x69D, 0x3}}, {0xFAA8, {0xA7B, 0x1}}, {0x2F8CE, {0xBFE, 0x1}}, {0x128, {0x0B2, 0x2}}, {0xFA4B, {0xA20, 0x1}}, {0x2F818, {0xB48, 0x1}}, {0x2F988, {0xCB8, 0x1}}, {0xF974, {0x955, 0x1}}, {0x1EA9, {0x4BF, 0x3}}, {0x1F51, {0x65E, 0x2}}, {0x2F833, {0xB63, 0x1}}, {0x2F8FC, {0xC2C, 0x1}}, {0x38F, {0x224, 0x2}}, {0xFA3E, {0xA13, 0x1}}, {0xFA5E, {0xA33, 0x1}}, {0xFA1E, {0x9FA, 0x1}}, {0x2F9F7, {0xD27, 0x1}}, {0x15F, {0x106, 0x2}}, {0x1E35, {0x3C6, 0x2}}, {0xFB47, {0xAE3, 0x2}}, {0x2F962, {0xC92, 0x1}}, {0xFA86, {0xA59, 0x1}}, {0xF92F, {0x910, 0x1}}, {0x2F8DD, {0xC0D, 0x1}}, {0xF73, {0x324, 0x2}}, {0x1E15, {0x37F, 0x3}}, {0x1ED0, {0x526, 0x3}}, {0xF9CA, {0x9AB, 0x1}}, {0x4E6, {0x28E, 0x2}}, {0x1FA2, {0x743, 0x4}}, {0x2F999, {0xCC9, 0x1}}, {0x1FC2, {0x797, 0x3}}, {0x2F8C7, {0xBF7, 0x1}}, {0xFABC, {0xA8F, 0x1}}, {0x30B4, {0x8AB, 0x2}}, {0x2F9B9, {0xCE9, 0x1}}, {0x1E73, {0x454, 0x2}}, {0xF95B, {0x93C, 0x1}}, {0x2F952, {0xC82, 0x1}}, {0xFA5F, {0xA34, 0x1}}, {0x1B08, {0x33E, 0x2}}, {0x2204, {0x81D, 0x2}}, {0x1EA2, {0x4AC, 0x2}}, {0x1EA, {0x18A, 0x2}}, {0x129, {0x0B4, 0x2}}, {0xF920, {0x901, 0x1}}, {0x1F85, {0x6D7, 0x4}}, {0x1E93, {0x498, 0x2}}, {0x2F9B2, {0xCE2, 0x1}}, {0x2F821, {0xB51, 0x1}}, {0x2F9D6, {0xD06, 0x1}}, {0xFA65, {0xA3A, 0x1}}, {0xF971, {0x952, 0x1}}, {0x230, {0x204, 0x3}}, {0x16D, {0x11E, 0x2}}, {0x21B, {0x1E6, 0x2}}, {0x2F9D4, {0xD04, 0x1}}, {0x1D161, {0xB14, 0x3}}, {0xFA94, {0xA67, 0x1}}, {0x403, {0x24A, 0x2}}, {0x16A, {0x118, 0x2}}, {0x958, {0x2C4, 0x2}}, {0x2F922, {0xC52, 0x1}}, {0xF93D, {0x91E, 0x1}}, {0x1E39, {0x3CF, 0x3}}, {0x2F870, {0xBA0, 0x1}}, {0x1EAE, {0x4CE, 0x3}}, {0x1F35, {0x61D, 0x3}}, {0xFA2F, {0xA04, 0x1}}, {0x15E, {0x104, 0x2}}, {0x1E43, {0x3E4, 0x2}}, {0x2F842, {0xB72, 0x1}}, {0xF9B1, {0x992, 0x1}}, {0xFA5D, {0xA32, 0x1}}, {0xF9D2, {0x9B3, 0x1}}, {0x1ED8, {0x53E, 0x3}}, {0xFACA, {0xA9D, 0x1}}, {0x1EDC, {0x54A, 0x3}}, {0x2F9FD, {0xD2D, 0x1}}, {0x2F90B, {0xC3B, 0x1}}, {0x1EC9, {0x518, 0x2}}, {0x2329, {0x869, 0x1}}, {0x1E96, {0x49E, 0x2}}, {0x2F9A0, {0xCD0, 0x1}}, {0xF9D0, {0x9B1, 0x1}}, {0x4ED, {0x298, 0x2}}, {0xC2, {0x004, 0x2}}, {0x1E79, {0x461, 0x3}}, {0x4F1, {0x2A0, 0x2}}, {0x1FD0, {0x7B4, 0x2}}, {0x1EF5, {0x58E, 0x2}}, {0x125, {0x0B0, 0x2}}, {0xFB2C, {0xAB5, 0x3}}, {0x387, {0x219, 0x1}}, {0xF9EF, {0x9D0, 0x1}}, {0x2F9F5, {0xD25, 0x1}}, {0x170, {0x124, 0x2}}, {0x2270, {0x839, 0x2}}, {0x2F9CC, {0xCFC, 0x1}}, {0xFA6D, {0xA42, 0x1}}, {0xF980, {0x961, 0x1}}, {0xF907, {0x8E8, 0x1}}, {0x2F857, {0xB87, 0x1}}, {0x1EB9, {0x4EE, 0x2}}, {0x2F8D5, {0xC05, 0x1}}, {0xF9F9, {0x9DA, 0x1}}, {0x2F8A2, {0xBD2, 0x1}}, {0xF94B, {0x92C, 0x1}}, {0x2F87E, {0xBAE, 0x1}}, {0xF91F, {0x900, 0x1}}, {0xF9EC, {0x9CD, 0x1}}, {0x4F4, {0x2A6, 0x2}}, {0x451, {0x25A, 0x2}}, {0x22AD, {0x853, 0x2}}, {0x2F917, {0xC47, 0x1}}, {0x2F861, {0xB91, 0x1}}, {0xF91D, {0x8FE, 0x1}}, {0xFA96, {0xA69, 0x1}}, {0xF6, {0x05C, 0x2}}, {0x1E27, {0x3A8, 0x2}}, {0x1F48, {0x64C, 0x2}}, {0x1FEB, {0x7EA, 0x2}}, {0x1EE5, {0x564, 0x2}}, {0x2F99F, {0xCCF, 0x1}}, {0x2F8E8, {0xC18, 0x1}}, {0xF9DF, {0x9C0, 0x1}}, {0x1EAF, {0x4D1, 0x3}}, {0xF92A, {0x90B, 0x1}}, {0xFA78, {0xA4B, 0x1}}, {0xFA6C, {0xA41, 0x1}}, {0x1E22, {0x39E, 0x2}}, {0x1EEA, {0x570, 0x3}}, {0x2F850, {0xB80, 0x1}}, {0x2F9C9, {0xCF9, 0x1}}, {0xF91C, {0x8FD, 0x1}}, {0xA33, {0x2DE, 0x2}}, {0xE7, {0x040, 0x2}}, {0x2F80B, {0xB3B, 0x1}}, {0xFA91, {0xA64, 0x1}}, {0x9DD, {0x2DA, 0x2}}, {0x2F848, {0xB78, 0x1}}, {0xF966, {0x947, 0x1}}, {0xFB36, {0xACB, 0x2}}, {0xF957, {0x938, 0x1}}, {0x2F88A, {0xBBA, 0x1}}, {0x305C, {0x87D, 0x2}}, {0x2F982, {0xCB2, 0x1}}, {0x122, {0x0AA, 0x2}}, {0xF95D, {0x93E, 0x1}}, {0x2F8CC, {0xBFC, 0x1}}, {0x2F86A, {0xB9A, 0x1}}, {0x1F81, {0x6C8, 0x3}}, {0x212A, {0x80E, 0x1}}, {0x17E, {0x140, 0x2}}, {0xF9A9, {0x98A, 0x1}}, {0xFABD, {0xA90, 0x1}}, {0x2F91E, {0xC4E, 0x1}}, {0xF9EB, {0x9CC, 0x1}}, {0x2F937, {0xC67, 0x1}}, {0x2F8A6, {0xBD6, 0x1}}, {0x1E42, {0x3E2, 0x2}}, {0x1112F, {0xAFB, 0x2}}, {0x1FA8, {0x75B, 0x3}}, {0xC0, {0x000, 0x2}}, {0x2F8DA, {0xC0A, 0x1}}, {0x2F9B5, {0xCE5, 0x1}}, {0x2F8C1, {0xBF1, 0x1}}, {0xF9B4, {0x995, 0x1}}, {0x171, {0x126, 0x2}}, {0xF952, {0x933, 0x1}}, {0xFA08, {0x9E9, 0x1}}, {0x1FAC, {0x769, 0x4}}, {0x1FB, {0x1A5, 0x3}}, {0xF912, {0x8F3, 0x1}}, {0x1F11, {0x5C6, 0x2}}, {0x201, {0x1B2, 0x2}}, {0x2F8B9, {0xBE9, 0x1}}, {0x2FA1C, {0xD4C, 0x1}}, {0x2F8B5, {0xBE5, 0x1}}, {0x1E88, {0x482, 0x2}}, {0xFAB8, {0xA8B, 0x1}}, {0xFA43, {0xA18, 0x1}}, {0x144, {0x0DA, 0x2}}, {0x1E8B, {0x488, 0x2}}, {0x2F9E1, {0xD11, 0x1}}, {0xF97C, {0x95D, 0x1}}, {0xF985, {0x966, 0x1}}, {0x1F06, {0x5A8, 0x3}}, {0x1FB2, {0x77D, 0x3}}, {0x2F9DB, {0xD0B, 0x1}}, {0x1FE4, {0x7DB, 0x2}}, {0xFAC3, {0xA96, 0x1}}, {0x177, {0x132, 0x2}}, {0x2F948, {0xC78, 0x1}}, {0xCB, {0x014, 0x2}}, {0x1F9F, {0x739, 0x4}}, {0xFA99, {0xA6C, 0x1}}, {0x2F942, {0xC72, 0x1}}, {0x2F976, {0xCA6, 0x1}}, {0xF988, {0x969, 0x1}}, {0x1FE6, {0x7DF, 0x2}}, {0x156, {0x0F4, 0x2}}, {0x1E8E, {0x48E, 0x2}}, {0x2F8FE, {0xC2E, 0x1}}, {0x1E46, {0x3EA, 0x2}}, {0x2F81E, {0xB4E, 0x1}}, {0x45D, {0x262, 0x2}}, {0x1FCF, {0x7B2, 0x2}}, {0x1ECF, {0x524, 0x2}}, {0x1E48, {0x3EE, 0x2}}, {0xF9CB, {0x9AC, 0x1}}, {0x2F946, {0xC76, 0x1}}, {0xFACF, {0xAA2, 0x1}}, {0x109, {0x07C, 0x2}}, {0x22E3, {0x85F, 0x2}}, {0xF52, {0x31C, 0x2}}, {0x304C, {0x86D, 0x2}}, {0xF9B2, {0x993, 0x1}}, {0x12C, {0x0BA, 0x2}}, {0x2F969, {0xC99, 0x1}}, {0x2F9A5, {0xCD5, 0x1}}, {0x307A, {0x899, 0x2}}, {0x2F9BE, {0xCEE, 0x1}}, {0x1F1B, {0x5DB, 0x3}}, {0x1F5D, {0x677, 0x3}}, {0x2F9AB, {0xCDB, 0x1}}, {0x2F8F1, {0xC21, 0x1}}, {0xF9BE, {0x99F, 0x1}}, {0xF9B8, {0x999, 0x1}}, {0x1FB4, {0x782, 0x3}}, {0x1EDF, {0x553, 0x3}}, {0x22F, {0x202, 0x2}}, {0x934, {0x2C2, 0x2}}, {0x2F98A, {0xCBA, 0x1}}, {0x1FE8, {0x7E4, 0x2}}, {0xFAAB, {0xA7E, 0x1}}, {0xFA5B, {0xA30, 0x1}}, {0x4D3, {0x274, 0x2}}, {0x2226, {0x825, 0x2}}, {0x2FA03, {0xD33, 0x1}}, {0xFA12, {0x9F0, 0x1}}, {0x1EE4, {0x562, 0x2}}, {0x1FD6, {0x7BE, 0x2}}, {0xF931, {0x912, 0x1}}, {0xFAA1, {0xA74, 0x1}}, {0x2F936, {0xC66, 0x1}}, {0x1FE9, {0x7E6, 0x2}}, {0x1FF4, {0x7F8, 0x3}}, {0x2F86C, {0xB9C, 0x1}}, {0x2F911, {0xC41, 0x1}}, {0x95F, {0x2D2, 0x2}}, {0xCD, {0x018, 0x2}}, {0x2F95E, {0xC8E, 0x1}}, {0xF98F, {0x970, 0x1}}, {0x1E08, {0x362, 0x3}}, {0x30FA, {0x8DD, 0x2}}, {0x4DA, {0x27A, 0x2}}, {0xF9C5, {0x9A6, 0x1}}, {0x1D8, {0x163, 0x3}}, {0x1EC, {0x18E, 0x3}}, {0xF95E, {0x93F, 0x1}}, {0xFB4E, {0xAF1, 0x2}}, {0xC5, {0x00A, 0x2}}, {0x2F87D, {0xBAD, 0x1}}, {0x1B0C, {0x342, 0x2}}, {0x2F9C6, {0xCF6, 0x1}}, {0x2F879, {0xBA9, 0x1}}, {0x2F9F2, {0xD22, 0x1}}, {0x1ECE, {0x522, 0x2}}, {0xDDA, {0x30F, 0x2}}, {0x1F80, {0x6C5, 0x3}}, {0x3074, {0x891, 0x2}}, {0x2289, {0x84F, 0x2}}, {0x1D0, {0x150, 0x2}}, {0x1EAC, {0x4C8, 0x3}}, {0x2F851, {0xB81, 0x1}}, {0x22C, {0x1FA, 0x3}}, {0x1F9E, {0x735, 0x4}}, {0x2F9D2, {0xD02, 0x1}}, {0xFA05, {0x9E6, 0x1}}, {0x2F88C, {0xBBC, 0x1}}, {0x1FFD, {0x80A, 0x1}}, {0x1F0B, {0x5B5, 0x3}}, {0xFABA, {0xA8D, 0x1}}, {0x1E82, {0x476, 0x2}}, {0x2F8C2, {0xBF2, 0x1}}, {0x1F30, {0x610, 0x2}}, {0x1F87, {0x6DF, 0x4}}, {0xFA61, {0xA36, 0x1}}, {0x1B06, {0x33C, 0x2}}, {0x2F9E8, {0xD18, 0x1}}, {0x2F80E, {0xB3E, 0x1}}, {0x1F00, {0x598, 0x2}}, {0xFA03, {0x9E4, 0x1}}, {0x202, {0x1B4, 0x2}}, {0x1CE, {0x14C, 0x2}}, {0x2FA12, {0xD42, 0x1}}, {0x2F84F, {0xB7F, 0x1}}, {0x4C1, {0x26A, 0x2}}, {0xFA81, {0xA54, 0x1}}, {0xFAC7, {0xA9A, 0x1}}, {0xF9BB, {0x99C, 0x1}}, {0x16F, {0x122, 0x2}}, {0x114, {0x08E, 0x2}}, {0xCC7, {0x300, 0x2}}, {0x1B0E, {0x344, 0x2}}, {0x2F9D1, {0xD01, 0x1}}, {0x139, {0x0CC, 0x2}}, {0x2F836, {0xB66, 0x1}}, {0xB48, {0x2EA, 0x2}}, {0x30F9, {0x8DB, 0x2}}, {0xFA79, {0xA4C, 0x1}}, {0x3058, {0x879, 0x2}}, {0x2F971, {0xCA1, 0x1}}, {0x2F928, {0xC58, 0x1}}, {0x1FC, {0x1A8, 0x2}}, {0x2224, {0x823, 0x2}}, {0xFAB3, {0xA86, 0x1}}, {0x2F8E7, {0xC17, 0x1}}, {0x1FDE, {0x7CD, 0x2}}, {0x1E7, {0x184, 0x2}}, {0x1E13, {0x37A, 0x2}}, {0xFA5A, {0xA2F, 0x1}}, {0x1FCE, {0x7B0, 0x2}}, {0x1E58, {0x416, 0x2}}, {0x2F964, {0xC94, 0x1}}, {0xFABE, {0xA91, 0x1}}, {0x1E0F, {0x372, 0x2}}, {0x2F938, {0xC68, 0x1}}, {0x11D, {0x0A0, 0x2}}, {0xF994, {0x975, 0x1}}, {0xF947, {0x928, 0x1}}, {0x3AA, {0x229, 0x2}}, {0xF981, {0x962, 0x1}}, {0x1E30, {0x3BC, 0x2}}, {0x1F14, {0x5CE, 0x3}}, {0xF9F1, {0x9D2, 0x1}}, {0x1FA9, {0x75E, 0x3}}, {0x1E62, {0x42C, 0x2}}, {0x2F85D, {0xB8D, 0x1}}, {0xFA4E, {0xA23, 0x1}}, {0x1F22, {0x5E8, 0x3}}, {0x2F838, {0xB68, 0x1}}, {0x2F9A6, {0xCD6, 0x1}}, {0xF93F, {0x920, 0x1}}, {0x2F9F3, {0xD23, 0x1}}, {0xFAAD, {0xA80, 0x1}}, {0x2F87A, {0xBAA, 0x1}}, {0x2F830, {0xB60, 0x1}}, {0x2F8C8, {0xBF8, 0x1}}, {0x10A, {0x07E, 0x2}}, {0x20B, {0x1C6, 0x2}}, {0x1F77, {0x6B7, 0x2}}, {0xFAB4, {0xA87, 0x1}}, {0x2F985, {0xCB5, 0x1}}, {0xF9F7, {0x9D8, 0x1}}, {0x2F94F, {0xC7F, 0x1}}, {0xFA40, {0xA15, 0x1}}, {0x2F968, {0xC98, 0x1}}, {0xFA8C, {0xA5F, 0x1}}, {0xCE, {0x01A, 0x2}}, {0xFAC0, {0xA93, 0x1}}, {0x3062, {0x883, 0x2}}, {0xF9, {0x05E, 0x2}}, {0x1FB0, {0x779, 0x2}}, {0xB94, {0x2F4, 0x2}}, {0x2F926, {0xC56, 0x1}}, {0x1F84, {0x6D3, 0x4}}, {0x4EF, {0x29C, 0x2}}, {0xF9D, {0x330, 0x2}}, {0x226D, {0x833, 0x2}}, {0x1F28, {0x5FA, 0x2}}, {0x2F955, {0xC85, 0x1}}, {0x2279, {0x843, 0x2}}, {0xFAB6, {0xA89, 0x1}}, {0xF935, {0x916, 0x1}}, {0x439, {0x256, 0x2}}, {0x11938, {0xB0B, 0x2}}, {0xEE, {0x04E, 0x2}}, {0x17B, {0x13A, 0x2}}, {0x1E0C, {0x36C, 0x2}}, {0x1E6A, {0x442, 0x2}}, {0x214, {0x1D8, 0x2}}, {0xF57, {0x31E, 0x2}}, {0x2F825, {0xB55, 0x1}}, {0x114BB, {0xB01, 0x2}}, {0x100, {0x06A, 0x2}}, {0x1E6, {0x182, 0x2}}, {0x2F951, {0xC81, 0x1}}, {0x2F9CE, {0xCFE, 0x1}}, {0x2F984, {0xCB4, 0x1}}, {0x22EB, {0x863, 0x2}}, {0x1F6B, {0x69A, 0x3}}, {0x13B, {0x0D0, 0x2}}, {0x2F9E9, {0xD19, 0x1}}, {0x2F8BE, {0xBEE, 0x1}}, {0x2F823, {0xB53, 0x1}}, {0x2F9D3, {0xD03, 0x1}}, {0x2F97C, {0xCAC, 0x1}}, {0xFA49, {0xA1E, 0x1}}, {0x1EB1, {0x4D7, 0x3}}, {0x2F998, {0xCC8, 0x1}}, {0x304E, {0x86F, 0x2}}, {0x1F0D, {0x5BB, 0x3}}, {0xD4A, {0x309, 0x2}}, {0xFB39, {0xACF, 0x2}}, {0x305A, {0x87B, 0x2}}, {0xF9B0, {0x991, 0x1}}, {0x2F8FF, {0xC2F, 0x1}}, {0x30AE, {0x8A5, 0x2}}, {0x2F977, {0xCA7, 0x1}}, {0x1FA, {0x1A2, 0x3}}, {0x1E20, {0x39A, 0x2}}, {0xF917, {0x8F8, 0x1}}, {0xF989, {0x96A, 0x1}}, {0xF90E, {0x8EF, 0x1}}, {0xFB34, {0xAC7, 0x2}}, {0x2F889, {0xBB9, 0x1}}, {0x2F866, {0xB96, 0x1}}, {0x2F865, {0xB95, 0x1}}, {0x12F, {0x0C0, 0x2}}, {0x2FA0E, {0xD3E, 0x1}}, {0x1E61, {0x42A, 0x2}}, {0x1FD, {0x1AA, 0x2}}, {0x2F803, {0xB33, 0x1}}, {0x1FE3, {0x7D8, 0x3}}, {0xFA44, {0xA19, 0x1}}, {0x1FA6, {0x753, 0x4}}, {0x20F, {0x1CE, 0x2}}, {0x1FA4, {0x74B, 0x4}}, {0xCCB, {0x306, 0x3}}, {0xFA4F, {0xA24, 0x1}}, {0x2F997, {0xCC7, 0x1}}, {0x1E4B, {0x3F4, 0x2}}, {0xFAA2, {0xA75, 0x1}}, {0x2F839, {0xB69, 0x1}}, {0x1F55, {0x669, 0x3}}, {0x1E32, {0x3C0, 0x2}}, {0xF9F0, {0x9D1, 0x1}}, {0xEA, {0x046, 0x2}}, {0x2F8D4, {0xC04, 0x1}}, {0xF969, {0x94A, 0x1}}, {0xFA, {0x060, 0x2}}, {0x1E33, {0x3C2, 0x2}}, {0x1E6F, {0x44C, 0x2}}, {0xF9AA, {0x98B, 0x1}}, {0xF938, {0x919, 0x1}}, {0x2F826, {0xB56, 0x1}}, {0xF93C, {0x91D, 0x1}}, {0x4E2, {0x286, 0x2}}, {0x1E5F, {0x426, 0x2}}, {0x4C2, {0x26C, 0x2}}, {0x4DD, {0x280, 0x2}}, {0x390, {0x226, 0x3}}, {0x2F923, {0xC53, 0x1}}, {0x419, {0x254, 0x2}}, {0x2F8E9, {0xC19, 0x1}}, {0x1B3B, {0x348, 0x2}}, {0x2F860, {0xB90, 0x1}}, {0x1EC0, {0x4FE, 0x3}}, {0x124, {0x0AE, 0x2}}, {0x2F935, {0xC65, 0x1}}, {0x21CD, {0x817, 0x2}}, {0x2F81F, {0xB4F, 0x1}}, {0x1F95, {0x713, 0x4}}, {0xFA59, {0xA2E, 0x1}}, {0xFB4A, {0xAE9, 0x2}}, {0x137, {0x0CA, 0x2}}, {0x2FA19, {0xD49, 0x1}}, {0x1E8C, {0x48A, 0x2}}, {0x2FA11, {0xD41, 0x1}}, {0xF992, {0x973, 0x1}}, {0xF99B, {0x97C, 0x1}}, {0x226F, {0x837, 0x2}}, {0x2F908, {0xC38, 0x1}}, {0x1EA1, {0x4AA, 0x2}}, {0x30C7, {0x8BD, 0x2}}, {0xF5C, {0x320, 0x2}}, {0x114BC, {0xB03, 0x2}}, {0xF91A, {0x8FB, 0x1}}, {0xFB49, {0xAE7, 0x2}}, {0x2F892, {0xBC2, 0x1}}, {0x1D1BB, {0xB20, 0x2}}, {0x40E, {0x252, 0x2}}, {0x1E7E, {0x46E, 0x2}}, {0x1FD3, {0x7BB, 0x3}}, {0x1FB6, {0x785, 0x2}}, {0x164, {0x110, 0x2}}, {0x2F92B, {0xC5B, 0x1}}, {0x1F3D, {0x633, 0x3}}, {0x386, {0x217, 0x2}}, {0x1F24, {0x5EE, 0x3}}, {0xFACD, {0xAA0, 0x1}}, {0xFA17, {0x9F3, 0x1}}, {0x1E17, {0x385, 0x3}}, {0xFA0B, {0x9EC, 0x1}}, {0x2F980, {0xCB0, 0x1}}, {0xFA2D, {0xA02, 0x1}}, {0x2F88E, {0xBBE, 0x1}}, {0xFB2E, {0xABB, 0x2}}, {0xFA31, {0xA06, 0x1}}, {0x1F09, {0x5B0, 0x2}}, {0x2F815, {0xB45, 0x1}}, {0x2F9CA, {0xCFA, 0x1}}, {0xFA55, {0xA2A, 0x1}}, {0x2F9F9, {0xD29, 0x1}}, {0x1F62, {0x681, 0x3}}, {0x106, {0x076, 0x2}}, {0x1EBF, {0x4FB, 0x3}}, {0x1F38, {0x626, 0x2}}, {0x1F20, {0x5E4, 0x2}}, {0x2F965, {0xC95, 0x1}}, {0x2F829, {0xB59, 0x1}}, {0x1FFB, {0x806, 0x2}}, {0xC9, {0x010, 0x2}}, {0x147, {0x0E0, 0x2}}, {0x2F9C3, {0xCF3, 0x1}}, {0x213, {0x1D6, 0x2}}, {0x1F9D, {0x731, 0x4}}, {0x1F25, {0x5F1, 0x3}}, {0x2F9AF, {0xCDF, 0x1}}, {0xFA77, {0xA4A, 0x1}}, {0x2209, {0x81F, 0x2}}, {0xF76, {0x328, 0x2}}, {0xF956, {0x937, 0x1}}, {0x1FCC, {0x7AC, 0x2}}, {0x1FA0, {0x73D, 0x3}}, {0x13C, {0x0D2, 0x2}}, {0x1EB3, {0x4DD, 0x3}}, {0x1E5A, {0x41A, 0x2}}, {0xFB35, {0xAC9, 0x2}}, {0xF9B5, {0x996, 0x1}}, {0xFA0A, {0x9EB, 0x1}}, {0x1FA5, {0x74F, 0x4}}, {0x22E, {0x200, 0x2}}, {0x1EEE, {0x57C, 0x3}}, {0x1D5, {0x15A, 0x3}}, {0x15C, {0x100, 0x2}}, {0xE3, {0x03A, 0x2}}, {0xFA8B, {0xA5E, 0x1}}, {0x3079, {0x897, 0x2}}, {0xFB38, {0xACD, 0x2}}, {0x2F855, {0xB85, 0x1}}, {0xFA9E, {0xA71, 0x1}}, {0x14E, {0x0E8, 0x2}}, {0x1E75, {0x458, 0x2}}, {0xFA46, {0xA1B, 0x1}}, {0x2FA0A, {0xD3A, 0x1}}, {0x211, {0x1D2, 0x2}}, {0x2F8A9, {0xBD9, 0x1}}, {0x115, {0x090, 0x2}}, {0x30D1, {0x8C3, 0x2}}, {0x1E47, {0x3EC, 0x2}}, {0x2F8E0, {0xC10, 0x1}}, {0xFA7E, {0xA51, 0x1}}, {0x1FD8, {0x7C3, 0x2}}, {0x1E92, {0x496, 0x2}}, {0x2F867, {0xB97, 0x1}}, {0xFB1D, {0xAAD, 0x2}}, {0xF97D, {0x95E, 0x1}}, {0xFAB9, {0xA8C, 0x1}}, {0x2F846, {0xB76, 0x1}}, {0x1E7D, {0x46C, 0x2}}, {0xFAC8, {0xA9B, 0x1}}, {0x104, {0x072, 0x2}}, {0x4F5, {0x2A8, 0x2}}, {0x2FA10, {0xD40, 0x1}}, {0xF975, {0x956, 0x1}}, {0xF96A, {0x94B, 0x1}}, {0xFA2B, {0xA00, 0x1}}, {0x1F73, {0x6AF, 0x2}}, {0x2F957, {0xC87, 0x1}}, {0x1EDD, {0x54D, 0x3}}, {0x3AD, {0x22F, 0x2}}, {0x2F924, {0xC54, 0x1}}, {0xFAD4, {0xAA7, 0x1}}, {0x2F97B, {0xCAB, 0x1}}, {0x2F853, {0xB83, 0x1}}, {0xFA3F, {0xA14, 0x1}}, {0xFA8A, {0xA5D, 0x1}}, {0xFB4C, {0xAED, 0x2}}, {0x1F3B, {0x62D, 0x3}}, {0xC4, {0x008, 0x2}}, {0x1109A, {0xAF3, 0x2}}, {0x341, {0x20F, 0x1}}, {0x1E0D, {0x36E, 0x2}}, {0x4DC, {0x27E, 0x2}}, {0xFA10, {0x9EF, 0x1}}, {0x1EF1, {0x585, 0x3}}, {0x2F903, {0xC33, 0x1}}, {0x1FED, {0x7EE, 0x2}}, {0x1FEF, {0x7F2, 0x1}}, {0x1EA4, {0x4B0, 0x3}}, {0x21F, {0x1EA, 0x2}}, {0xFA92, {0xA65, 0x1}}, {0x2F8A4, {0xBD4, 0x1}}, {0xF9C8, {0x9A9, 0x1}}, {0xF9A0, {0x981, 0x1}}, {0x1E31, {0x3BE, 0x2}}, {0xF903, {0x8E4, 0x1}}, {0x3D4, {0x244, 0x2}}, {0x2F809, {0xB39, 0x1}}, {0x1E07, {0x360, 0x2}}, {0x1FB1, {0x77B, 0x2}}, {0x2F8D1, {0xC01, 0x1}}, {0x1E11, {0x376, 0x2}}, {0x1FD2, {0x7B8, 0x3}}, {0x1D15E, {0xB0D, 0x2}}, {0x2F941, {0xC71, 0x1}}, {0xF962, {0x943, 0x1}}, {0x1EF0, {0x582, 0x3}}, {0x1EE0, {0x556, 0x3}}, {0x3052, {0x873, 0x2}}, {0x148, {0x0E2, 0x2}}, {0x1F44, {0x646, 0x3}}, {0x2F930, {0xC60, 0x1}}, {0x1E0B, {0x36A, 0x2}}, {0x2F83E, {0xB6E, 0x1}}, {0x3094, {0x89F, 0x2}}, {0x1E5B, {0x41C, 0x2}}, {0xFA54, {0xA29, 0x1}}, {0xF9FC, {0x9DD, 0x1}}, {0x1DE, {0x172, 0x3}}, {0x1E02, {0x356, 0x2}}, {0x2F9E4, {0xD14, 0x1}}, {0xF97F, {0x960, 0x1}}, {0x1FE1, {0x7D3, 0x2}}, {0x1ECB, {0x51C, 0x2}}, {0x2F947, {0xC77, 0x1}}, {0x2F82D, {0xB5D, 0x1}}, {0x1F6D, {0x6A0, 0x3}}, {0x1E4C, {0x3F6, 0x3}}, {0x1E00, {0x352, 0x2}}, {0x2F93C, {0xC6C, 0x1}}, {0x2F9C4, {0xCF4, 0x1}}, {0x1F2D, {0x607, 0x3}}, {0x38C, {0x220, 0x2}}, {0x115BB, {0xB09, 0x2}}, {0xFA0C, {0x9ED, 0x1}}, {0x2F986, {0xCB6, 0x1}}, {0x1F0A, {0x5B2, 0x3}}, {0x1F83, {0x6CF, 0x4}}, {0x1EE3, {0x55F, 0x3}}, {0x2F93E, {0xC6E, 0x1}}, {0x2F8ED, {0xC1D, 0x1}}, {0x2F981, {0xCB1, 0x1}}, {0x1F54, {0x666, 0x3}}, {0x1E16, {0x382, 0x3}}, {0xFA6A, {0xA3F, 0x1}}, {0x179, {0x136, 0x2}}, {0xFB2D, {0xAB8, 0x3}}, {0xF90B, {0x8EC, 0x1}}, {0xFB3E, {0xAD7, 0x2}}, {0x2F9B0, {0xCE0, 0x1}}, {0xF932, {0x913, 0x1}}, {0x1EE6, {0x566, 0x2}}, {0x2F85E, {0xB8E, 0x1}}, {0x2F8D7, {0xC07, 0x1}}, {0x1E40, {0x3DE, 0x2}}, {0x22ED, {0x867, 0x2}}, {0x1F43, {0x643, 0x3}}, {0x4E5, {0x28C, 0x2}}, {0xFAA5, {0xA78, 0x1}}, {0x1FA3, {0x747, 0x4}}, {0x2F967, {0xC97, 0x1}}, {0xF993, {0x974, 0x1}}, {0x2F88D, {0xBBD, 0x1}}, {0xED, {0x04C, 0x2}}, {0x2F906, {0xC36, 0x1}}, {0xFA98, {0xA6B, 0x1}}, {0x2F901, {0xC31, 0x1}}, {0x1FF7, {0x7FD, 0x3}}, {0x95C, {0x2CC, 0x2}}, {0xFA60, {0xA35, 0x1}}, {0x3CA, {0x238, 0x2}}, {0xFA62, {0xA37, 0x1}}, {0x2F8C3, {0xBF3, 0x1}}, {0x1ECA, {0x51A, 0x2}}, {0xF950, {0x931, 0x1}}, {0x173, {0x12A, 0x2}}, {0x2F99A, {0xCCA, 0x1}}, {0x2F970, {0xCA0, 0x1}}, {0x2F85A, {0xB8A, 0x1}}, {0x2FA13, {0xD43, 0x1}}, {0xF98B, {0x96C, 0x1}}, {0x2F913, {0xC43, 0x1}}, {0xFB9, {0x338, 0x2}}, {0xF919, {0x8FA, 0x1}}, {0x1EBA, {0x4F0, 0x2}}, {0x2F890, {0xBC0, 0x1}}, {0xF953, {0x934, 0x1}}, {0x1EB2, {0x4DA, 0x3}}, {0xF904, {0x8E5, 0x1}}, {0x1DC, {0x16F, 0x3}}, {0x3AB, {0x22B, 0x2}}, {0xF94E, {0x92F, 0x1}}, {0x2F8DF, {0xC0F, 0x1}}, {0x1F19, {0x5D6, 0x2}}, {0x30BC, {0x8B3, 0x2}}, {0x305E, {0x87F, 0x2}}, {0x1E87, {0x480, 0x2}}, {0xF9FA, {0x9DB, 0x1}}, {0x2F82B, {0xB5B, 0x1}}, {0x2F81D, {0xB4D, 0x1}}, {0x2F8A3, {0xBD3, 0x1}}, {0x2FA04, {0xD34, 0x1}}, {0xFA3A, {0xA0F, 0x1}}, {0x4E4, {0x28A, 0x2}}, {0x2F9D5, {0xD05, 0x1}}, {0x2F90D, {0xC3D, 0x1}}, {0xF967, {0x948, 0x1}}, {0xFAD3, {0xAA6, 0x1}}, {0x1FF2, {0x7F3, 0x3}}, {0x1E86, {0x47E, 0x2}}, {0xF92D, {0x90E, 0x1}}, {0x1EF7, {0x592, 0x2}}, {0xFA89, {0xA5C, 0x1}}, {0x2F874, {0xBA4, 0x1}}, {0x2F852, {0xB82, 0x1}}, {0xF9CD, {0x9AE, 0x1}}, {0x2F854, {0xB84, 0x1}}, {0x1E25, {0x3A4, 0x2}}, {0x1EC1, {0x501, 0x3}}, {0x1EDB, {0x547, 0x3}}, {0x205, {0x1BA, 0x2}}, {0x2F843, {0xB73, 0x1}}, {0x2F8B1, {0xBE1, 0x1}}, {0x1FB8, {0x78A, 0x2}}, {0xBCB, {0x2F8, 0x2}}, {0x2F84C, {0xB7C, 0x1}}, {0xFA30, {0xA05, 0x1}}, {0x9CB, {0x2D4, 0x2}}, {0x2F858, {0xB88, 0x1}}, {0x1F5B, {0x674, 0x3}}, {0x6C2, {0x2BA, 0x2}}, {0x2F950, {0xC80, 0x1}}, {0xB5C, {0x2F0, 0x2}}, {0xFA4D, {0xA22, 0x1}}, {0x1F93, {0x70B, 0x4}}, {0xFA74, {0xA47, 0x1}}, {0x1F59, {0x672, 0x2}}, {0x2FA1D, {0xD4D, 0x1}}, {0xFA70, {0xA43, 0x1}}, {0xFA18, {0x9F4, 0x1}}, {0x2F888, {0xBB8, 0x1}}, {0x2F8B7, {0xBE7, 0x1}}, {0xF99D, {0x97E, 0x1}}, {0xF9DB, {0x9BC, 0x1}}, {0xF940, {0x921, 0x1}}, {0x1F8D, {0x6F5, 0x4}}, {0x1F97, {0x71B, 0x4}}, {0x1E10, {0x374, 0x2}}, {0xF997, {0x978, 0x1}}, {0x2F956, {0xC86, 0x1}}, {0x2F9AA, {0xCDA, 0x1}}, {0x1E41, {0x3E0, 0x2}}, {0x2F90C, {0xC3C, 0x1}}, {0x2F862, {0xB92, 0x1}}, {0x1E66, {0x436, 0x3}}, {0x1EF8, {0x594, 0x2}}, {0x2F849, {0xB79, 0x1}}, {0x2F894, {0xBC4, 0x1}}, {0x1E7F, {0x470, 0x2}}, {0x1F29, {0x5FC, 0x2}}, {0xFA1C, {0x9F8, 0x1}}, {0x1EB0, {0x4D4, 0x3}}, {0x1F74, {0x6B1, 0x2}}, {0x1EE9, {0x56D, 0x3}}, {0x1FBB, {0x790, 0x2}}, {0x162, {0x10C, 0x2}}, {0xD2, {0x020, 0x2}}, {0xFA97, {0xA6A, 0x1}}, {0x2F824, {0xB54, 0x1}}, {0x233, {0x20C, 0x2}}, {0x22AF, {0x857, 0x2}}, {0x1FEC, {0x7EC, 0x2}}, {0xFA7F, {0xA52, 0x1}}, {0x2F94A, {0xC7A, 0x1}}, {0x2F887, {0xBB7, 0x1}}, {0x2FA17, {0xD47, 0x1}}, {0x931, {0x2C0, 0x2}}, {0x130, {0x0C2, 0x2}}, {0x135, {0x0C6, 0x2}}, {0x3076, {0x893, 0x2}}, {0xFA2, {0x332, 0x2}}, {0x4F8, {0x2AA, 0x2}}, {0x1F27, {0x5F7, 0x3}}, {0xF963, {0x944, 0x1}}, {0xF94D, {0x92E, 0x1}}, {0x340, {0x20E, 0x1}}, {0x2F9CD, {0xCFD, 0x1}}, {0x1E01, {0x354, 0x2}}, {0x1ED3, {0x52F, 0x3}}, {0x103, {0x070, 0x2}}, {0x200, {0x1B0, 0x2}}, {0x17C, {0x13C, 0x2}}, {0xF92C, {0x90D, 0x1}}, {0x1E12, {0x378, 0x2}}, {0xFB33, {0xAC5, 0x2}}, {0xFA93, {0xA66, 0x1}}, {0xF5, {0x05A, 0x2}}, {0x30F8, {0x8D9, 0x2}}, {0x210, {0x1D0, 0x2}}, {0xF3, {0x056, 0x2}}, {0x2F804, {0xB34, 0x1}}, {0x2F8C0, {0xBF0, 0x1}}, {0x21A, {0x1E4, 0x2}}, {0x1E1C, {0x390, 0x3}}, {0x11E, {0x0A2, 0x2}}, {0x1F8F, {0x6FD, 0x4}}, {0xFA4C, {0xA21, 0x1}}, {0x95B, {0x2CA, 0x2}}, {0xFA45, {0xA1A, 0x1}}, {0xFAD5, {0xAA8, 0x1}}, {0x2F82E, {0xB5E, 0x1}}, {0xFA5C, {0xA31, 0x1}}, {0xF9B7, {0x998, 0x1}}, {0x206, {0x1BC, 0x2}}, {0x1FC7, {0x7A1, 0x3}}, {0x3CB, {0x23A, 0x2}}, {0xF9DC, {0x9BD, 0x1}}, {0x22B, {0x1F7, 0x3}}, {0xF92E, {0x90F, 0x1}}, {0x1EE2, {0x55C, 0x3}}, {0x2F88F, {0xBBF, 0x1}}, {0x2F94C, {0xC7C, 0x1}}, {0xFA84, {0xA57, 0x1}}, {0x2F9E3, {0xD13, 0x1}}, {0xF9D6, {0x9B7, 0x1}}, {0x15D, {0x102, 0x2}}, {0x1E24, {0x3A2, 0x2}}, {0x2F95C, {0xC8C, 0x1}}, {0x1EA6, {0x4B6, 0x3}}, {0x1EA8, {0x4BC, 0x3}}, {0x30F7, {0x8D7, 0x2}}, {0xF977, {0x958, 0x1}}, {0xFAC4, {0xA97, 0x1}}, {0xFA3B, {0xA10, 0x1}}, {0x2F904, {0xC34, 0x1}}, {0xF9AB, {0x98C, 0x1}}, {0x4EB, {0x294, 0x2}}, {0xFA20, {0x9FB, 0x1}}, {0x2F907, {0xC37, 0x1}}, {0x1EC6, {0x510, 0x3}}, {0x2FA05, {0xD35, 0x1}}, {0x110AB, {0xAF7, 0x2}}, {0x1E3F, {0x3DC, 0x2}}, {0x2285, {0x84B, 0x2}}, {0x343, {0x210, 0x1}}, {0xF9DA, {0x9BB, 0x1}}, {0x2FA07, {0xD37, 0x1}}, {0x2FA15, {0xD45, 0x1}}, {0x4F3, {0x2A4, 0x2}}, {0x113, {0x08C, 0x2}}, {0x1F7B, {0x6BF, 0x2}}, {0x21CF, {0x81B, 0x2}}, {0x1F7A, {0x6BD, 0x2}}, {0xE5, {0x03E, 0x2}}, {0x307D, {0x89D, 0x2}}, {0x2FA14, {0xD44, 0x1}}, {0x2F891, {0xBC1, 0x1}}, {0x2F9E5, {0xD15, 0x1}}, {0x2F983, {0xCB3, 0x1}}, {0x1F90, {0x701, 0x3}}, {0x1E85, {0x47C, 0x2}}, {0xD4, {0x024, 0x2}}, {0x22E0, {0x859, 0x2}}, {0x3AE, {0x231, 0x2}}, {0x2F8AE, {0xBDE, 0x1}}, {0xF9FB, {0x9DC, 0x1}}, {0xF99C, {0x97D, 0x1}}, {0x217, {0x1DE, 0x2}}, {0x1EB7, {0x4E9, 0x3}}, {0x2F8AA, {0xBDA, 0x1}}, {0x385, {0x215, 0x2}}, {0xFA16, {0x9F2, 0x1}}, {0x2F9E2, {0xD12, 0x1}}, {0x1E1, {0x17B, 0x3}}, {0x2FA00, {0xD30, 0x1}}, {0x154, {0x0F0, 0x2}}, {0x1FB7, {0x787, 0x3}}, {0x1EEF, {0x57F, 0x3}}, {0xFA02, {0x9E3, 0x1}}, {0x2F990, {0xCC0, 0x1}}, {0x1E52, {0x408, 0x3}}, {0x624, {0x2B2, 0x2}}, {0x2F99D, {0xCCD, 0x1}}, {0x2F883, {0xBB3, 0x1}}, {0x1FD7, {0x7C0, 0x3}}, {0xF9CC, {0x9AD, 0x1}}, {0xF926, {0x907, 0x1}}, {0x1E56, {0x412, 0x2}}, {0xFA06, {0x9E7, 0x1}}, {0xFA51, {0xA26, 0x1}}, {0x1EF4, {0x58C, 0x2}}, {0x2F9DA, {0xD0A, 0x1}}, {0x1E36, {0x3C8, 0x2}}, {0x6D3, {0x2BC, 0x2}}, {0x2F9FF, {0xD2F, 0x1}}, {0x2F8E2, {0xC12, 0x1}}, {0x1EAA, {0x4C2, 0x3}}, {0x2F8AD, {0xBDD, 0x1}}, {0xFA07, {0x9E8, 0x1}}, {0x2F97A, {0xCAA, 0x1}}, {0x2F966, {0xC96, 0x1}}, {0x1E80, {0x472, 0x2}}, {0x1D2, {0x154, 0x2}}, {0x1F63, {0x684, 0x3}}, {0x1FAD, {0x76D, 0x4}}, {0xF91E, {0x8FF, 0x1}}, {0xFAA0, {0xA73, 0x1}}, {0x2F863, {0xB93, 0x1}}, {0x30DD, {0x8D3, 0x2}}, {0x45E, {0x264, 0x2}}, {0x2F91A, {0xC4A, 0x1}}, {0x1E05, {0x35C, 0x2}}, {0x11A, {0x09A, 0x2}}, {0x1F61, {0x67F, 0x2}}, {0x2FA1B, {0xD4B, 0x1}}, {0x2F9E0, {0xD10, 0x1}}, {0x13D, {0x0D4, 0x2}}, {0xFB32, {0xAC3, 0x2}}, {0x1EA5, {0x4B3, 0x3}}, {0xDDD, {0x313, 0x3}}, {0x118, {0x096, 0x2}}, {0x207, {0x1BE, 0x2}}, {0x1FFA, {0x804, 0x2}}, {0x2F93F, {0xC6F, 0x1}}, {0x2F886, {0xBB6, 0x1}}, {0xF9E0, {0x9C1, 0x1}}, {0xF970, {0x951, 0x1}}, {0x2F97E, {0xCAE, 0x1}}, {0xF972, {0x953, 0x1}}, {0x136, {0x0C8, 0x2}}, {0x1B0A, {0x340, 0x2}}, {0x3073, {0x88F, 0x2}}, {0xF916, {0x8F7, 0x1}}, {0x22E2, {0x85D, 0x2}}, {0x2F921, {0xC51, 0x1}}, {0x2260, {0x82F, 0x2}}, {0x1F66, {0x68D, 0x3}}, {0x2F80F, {0xB3F, 0x1}}, {0x2001, {0x80C, 0x1}}, {0x1F88, {0x6E3, 0x3}}, {0xFAA6, {0xA79, 0x1}}, {0x1FAE, {0x771, 0x4}}, {0xFAA4, {0xA77, 0x1}}, {0x1F3A, {0x62A, 0x3}}, {0xFAC1, {0xA94, 0x1}}, {0xFA8F, {0xA62, 0x1}}, {0x95D, {0x2CE, 0x2}}, {0xF9D1, {0x9B2, 0x1}}, {0x1B41, {0x34E, 0x2}}, {0xF948, {0x929, 0x1}}, {0x1E3C, {0x3D6, 0x2}}, {0xFAD9, {0xAAC, 0x1}}, {0x400, {0x246, 0x2}}, {0xF96D, {0x94E, 0x1}}, {0x1E7C, {0x46A, 0x2}}, {0x3D3, {0x242, 0x2}}, {0x172, {0x128, 0x2}}, {0x1E1F, {0x398, 0x2}}, {0xFA9F, {0xA72, 0x1}}, {0x226E, {0x835, 0x2}}, {0x2F822, {0xB52, 0x1}}, {0xF75, {0x326, 0x2}}, {0x2F871, {0xBA1, 0x1}}, {0x2F812, {0xB42, 0x1}}, {0x2275, {0x83F, 0x2}}, {0x1F3F, {0x639, 0x3}}, {0x30D7, {0x8CB, 0x2}}, {0xD9, {0x02A, 0x2}}, {0x2F8AB, {0xBDB, 0x1}}, {0xFA7A, {0xA4D, 0x1}}, {0xF915, {0x8F6, 0x1}}, {0x1E1D, {0x393, 0x3}}, {0x1EB, {0x18C, 0x2}}, {0x2F808, {0xB38, 0x1}}, {0x2F98E, {0xCBE, 0x1}}, {0xFA53, {0xA28, 0x1}}, {0xF9A6, {0x987, 0x1}}, {0x2F80C, {0xB3C, 0x1}}, {0x2F8F5, {0xC25, 0x1}}, {0x2FA02, {0xD32, 0x1}}, {0x1E81, {0x474, 0x2}}, {0xF930, {0x911, 0x1}}, {0x3060, {0x881, 0x2}}, {0x1FEA, {0x7E8, 0x2}}, {0x38A, {0x21E, 0x2}}, {0x2F85C, {0xB8C, 0x1}}, {0x1FC9, {0x7A6, 0x2}}, {0x2F86F, {0xB9F, 0x1}}, {0x2F90A, {0xC3A, 0x1}}, {0x2F902, {0xC32, 0x1}}, {0x2F835, {0xB65, 0x1}}, {0x14D, {0x0E6, 0x2}}, {0x1D162, {0xB17, 0x3}}, {0xF923, {0x904, 0x1}}, {0x2F896, {0xBC6, 0x1}}, {0x22D, {0x1FD, 0x3}}, {0xF9F2, {0x9D3, 0x1}}, {0x30C2, {0x8B9, 0x2}}, {0x30D4, {0x8C7, 0x2}}, {0xF97E, {0x95F, 0x1}}, {0x1FE2, {0x7D5, 0x3}}, {0xEC, {0x04A, 0x2}}, {0x1F49, {0x64E, 0x2}}, {0xFA8D, {0xA60, 0x1}}, {0x2F898, {0xBC8, 0x1}}, {0x1EB5, {0x4E3, 0x3}}, {0x2F994, {0xCC4, 0x1}}, {0x12B, {0x0B8, 0x2}}, {0x20E, {0x1CC, 0x2}}, {0x168, {0x114, 0x2}}, {0x1F56, {0x66C, 0x3}}, {0x2F8A0, {0xBD0, 0x1}}, {0xFA52, {0xA27, 0x1}}, {0x2F93D, {0xC6D, 0x1}}, {0xDA, {0x02C, 0x2}}, {0x1EC4, {0x50A, 0x3}}, {0x1E95, {0x49C, 0x2}}, {0x1F67, {0x690, 0x3}}, {0x2F9C1, {0xCF1, 0x1}}, {0xF9DE, {0x9BF, 0x1}}, {0x2F83D, {0xB6D, 0x1}}, {0xF9ED, {0x9CE, 0x1}}, {0xFABF, {0xA92, 0x1}}, {0x457, {0x25E, 0x2}}, {0xFA0D, {0x9EE, 0x1}}, {0xFAB1, {0xA84, 0x1}}, {0x2F8FA, {0xC2A, 0x1}}, {0x1E49, {0x3F0, 0x2}}, {0xF91B, {0x8FC, 0x1}}, {0x1E50, {0x402, 0x3}}, {0xF94A, {0x92B, 0x1}}, {0xFA90, {0xA63, 0x1}}, {0x2284, {0x849, 0x2}}, {0x1EA0, {0x4A8, 0x2}}, {0x169, {0x116, 0x2}}, {0xF900, {0x8E1, 0x1}}, {0x1EEB, {0x573, 0x3}}, {0xF9E5, {0x9C6, 0x1}}, {0x2F9EA, {0xD1A, 0x1}}, {0x2F8AC, {0xBDC, 0x1}}, {0x2F8F8, {0xC28, 0x1}}, {0xCC8, {0x302, 0x2}}, {0x2F8F6, {0xC26, 0x1}}, {0xF968, {0x949, 0x1}}, {0x1E6C, {0x446, 0x2}}, {0x2F8D8, {0xC08, 0x1}}, {0x1EB6, {0x4E6, 0x3}}, {0x2F899, {0xBC9, 0x1}}, {0xC3, {0x006, 0x2}}, {0x1B0, {0x148, 0x2}}, {0x1F13, {0x5CB, 0x3}}, {0x1E63, {0x42E, 0x2}}, {0x112, {0x08A, 0x2}}, {0x2F920, {0xC50, 0x1}}, {0xFA04, {0x9E5, 0x1}}, {0x2F8D9, {0xC09, 0x1}}, {0x1E38, {0x3CC, 0x3}}, {0x1F94, {0x70F, 0x4}}, {0x2F940, {0xC70, 0x1}}, {0x2F97D, {0xCAD, 0x1}}, {0x2F84E, {0xB7E, 0x1}}, {0xF9A5, {0x986, 0x1}}, {0x9DC, {0x2D8, 0x2}}, {0xFC, {0x064, 0x2}}, {0x1EEC, {0x576, 0x3}}, {0x1A0, {0x142, 0x2}}, {0x2F9B4, {0xCE4, 0x1}}, {0xBCA, {0x2F6, 0x2}}, {0x2F834, {0xB64, 0x1}}, {0x2FA0B, {0xD3B, 0x1}}, {0xFAD8, {0xAAB, 0x1}}, {0x30C5, {0x8BB, 0x2}}, {0xF9F5, {0x9D6, 0x1}}, {0x2F84B, {0xB7B, 0x1}}, {0x1E5E, {0x424, 0x2}}, {0x2F8C6, {0xBF6, 0x1}}, {0x2F93B, {0xC6B, 0x1}}, {0x151, {0x0EE, 0x2}}, {0x1FFC, {0x808, 0x2}}, {0xFA39, {0xA0E, 0x1}}, {0x1E09, {0x365, 0x3}}, {0xF999, {0x97A, 0x1}}, {0x2F8E6, {0xC16, 0x1}}, {0x2F8BA, {0xBEA, 0x1}}, {0x2F876, {0xBA6, 0x1}}, {0x123, {0x0AC, 0x2}}, {0xFB3C, {0xAD5, 0x2}}, {0xF996, {0x977, 0x1}}, {0x1E8D, {0x48C, 0x2}}, {0x10C, {0x082, 0x2}}, {0x2F8EE, {0xC1E, 0x1}}, {0xF909, {0x8EA, 0x1}}, {0xFA4A, {0xA1F, 0x1}}, {0x2F9A9, {0xCD9, 0x1}}, {0x2F806, {0xB36, 0x1}}, {0x30D0, {0x8C1, 0x2}}, {0x2F9BB, {0xCEB, 0x1}}, {0x2F975, {0xCA5, 0x1}}, {0x2FA18, {0xD48, 0x1}}, {0x2F989, {0xCB9, 0x1}}, {0xF987, {0x968, 0x1}}, {0x2F80A, {0xB3A, 0x1}}, {0xF998, {0x979, 0x1}}, {0xF979, {0x95A, 0x1}}, {0xF90D, {0x8EE, 0x1}}, {0xF922, {0x903, 0x1}}, {0x1F8C, {0x6F1, 0x4}}, {0x1F37, {0x623, 0x3}}, {0xFB48, {0xAE5, 0x2}}, {0x3067, {0x887, 0x2}}, {0x2F932, {0xC62, 0x1}}, {0x1ED2, {0x52C, 0x3}}, {0xD1, {0x01E, 0x2}}, {0x1E28, {0x3AA, 0x2}}, {0x178, {0x134, 0x2}}, {0x2F9AC, {0xCDC, 0x1}}, {0x161, {0x10A, 0x2}}, {0xFAD0, {0xAA3, 0x1}}, {0x401, {0x248, 0x2}}, {0x2241, {0x827, 0x2}}, {0xF9BD, {0x99E, 0x1}}, {0x2F8F0, {0xC20, 0x1}}, {0x1E45, {0x3E8, 0x2}}, {0xF9D9, {0x9BA, 0x1}}, {0xFAA9, {0xA7C, 0x1}}, {0xFA35, {0xA0A, 0x1}}, {0x4D2, {0x272, 0x2}}, {0x1E34, {0x3C4, 0x2}}, {0xF927, {0x908, 0x1}}, {0x1EC3, {0x507, 0x3}}, {0x2F8DE, {0xC0E, 0x1}}, {0x2F9A2, {0xCD2, 0x1}}, {0xF943, {0x924, 0x1}}, {0x1E8F, {0x490, 0x2}}, {0xF9D5, {0x9B6, 0x1}}, {0xF982, {0x963, 0x1}}, {0x1F50, {0x65C, 0x2}}, {0x1F92, {0x707, 0x4}}, {0xF81, {0x32C, 0x2}}, {0xFA87, {0xA5A, 0x1}}, {0x2F94E, {0xC7E, 0x1}}, {0x1FAB, {0x765, 0x4}}, {0xFA3D, {0xA12, 0x1}}, {0x1D9, {0x166, 0x3}}, {0x2F996, {0xCC6, 0x1}}, {0x1E8A, {0x486, 0x2}}, {0xFA58, {0xA2D, 0x1}}, {0x1FC1, {0x795, 0x2}}, {0x2F961, {0xC91, 0x1}}, {0x344, {0x211, 0x2}}, {0x1F34, {0x61A, 0x3}}, {0xF9A8, {0x989, 0x1}}, {0x163, {0x10E, 0x2}}, {0xFB4B, {0xAEB, 0x2}}, {0x9CC, {0x2D6, 0x2}}, {0x1E71, {0x450, 0x2}}, {0xE2, {0x038, 0x2}}, {0xF9F3, {0x9D4, 0x1}}, {0x2F89E, {0xBCE, 0x1}}, {0x2F8B3, {0xBE3, 0x1}}, {0x1F4D, {0x659, 0x3}}, {0x1EF9, {0x596, 0x2}}, {0x2F82F, {0xB5F, 0x1}}, {0xFA2E, {0xA03, 0x1}}, {0x2F814, {0xB44, 0x1}}, {0xFAD1, {0xAA4, 0x1}}, {0x1EC5, {0x50D, 0x3}}, {0x9DF, {0x2DC, 0x2}}, {0x2F8A1, {0xBD1, 0x1}}, {0xF939, {0x91A, 0x1}}, {0x228, {0x1F0, 0x2}}, {0x2F8D2, {0xC02, 0x1}}, {0x1D1BD, {0xB24, 0x3}}, {0x1F86, {0x6DB, 0x4}}, {0x2F868, {0xB98, 0x1}}, {0x22A, {0x1F4, 0x3}}, {0x2F8E3, {0xC13, 0x1}}, {0xA5A, {0x2E4, 0x2}}, {0x1FC8, {0x7A4, 0x2}}, {0xFA22, {0x9FC, 0x1}}, {0x1ED, {0x191, 0x3}}, {0xF99F, {0x980, 0x1}}, {0x2F9B8, {0xCE8, 0x1}}, {0x2F9E7, {0xD17, 0x1}}, {0xFABB, {0xA8E, 0x1}}, {0x1D6, {0x15D, 0x3}}, {0xFB31, {0xAC1, 0x2}}, {0x146, {0x0DE, 0x2}}, {0x1EF2, {0x588, 0x2}}, {0x1FA1, {0x740, 0x3}}, {0x1E18, {0x388, 0x2}}, {0xFA9B, {0xA6E, 0x1}}, {0xFA80, {0xA53, 0x1}}, {0xFACC, {0xA9F, 0x1}}, {0x17A, {0x138, 0x2}}, {0x1E3B, {0x3D4, 0x2}}, {0x1E74, {0x456, 0x2}}, {0x2F86B, {0xB9B, 0x1}}, {0x1E23, {0x3A0, 0x2}}, {0x175, {0x12E, 0x2}}, {0x1E3E, {0x3DA, 0x2}}, {0x4DB, {0x27C, 0x2}}, {0x219B, {0x813, 0x2}}, {0x1E84, {0x47A, 0x2}}, {0xF9CF, {0x9B0, 0x1}}, {0x1E44, {0x3E6, 0x2}}, {0x1E1A, {0x38C, 0x2}}, {0x1F65, {0x68A, 0x3}}, {0xF929, {0x90A, 0x1}}, {0x2247, {0x82B, 0x2}}, {0x4F2, {0x2A2, 0x2}}, {0x2F87C, {0xBAC, 0x1}}, {0x1E2F, {0x3B9, 0x3}}, {0x2F84A, {0xB7A, 0x1}}, {0x2F98C, {0xCBC, 0x1}}, {0x2F9A1, {0xCD1, 0x1}}, {0x1EC8, {0x516, 0x2}}, {0x157, {0x0F6, 0x2}}, {0x2F8BF, {0xBEF, 0x1}}, {0xFB41, {0xADB, 0x2}}, {0x4F9, {0x2AC, 0x2}}, {0x2F995, {0xCC5, 0x1}}, {0x12E, {0x0BE, 0x2}}, {0xF9B6, {0x997, 0x1}}, {0x1EBC, {0x4F4, 0x2}}, {0x1E21, {0x39C, 0x2}}, {0x2F841, {0xB71, 0x1}}, {0x116, {0x092, 0x2}}, {0x453, {0x25C, 0x2}}, {0x2F992, {0xCC2, 0x1}}, {0x1D164, {0xB1D, 0x3}}, {0x2F8E1, {0xC11, 0x1}}, {0xFA75, {0xA48, 0x1}}, {0xF902, {0x8E3, 0x1}}, {0xF908, {0x8E9, 0x1}}, {0x2F817, {0xB47, 0x1}}, {0xF95F, {0x940, 0x1}}, {0x2F83B, {0xB6B, 0x1}}, {0x10F, {0x088, 0x2}}, {0xD3, {0x022, 0x2}}, {0x3069, {0x889, 0x2}}, {0x1E65, {0x433, 0x3}}, {0xFB43, {0xADD, 0x2}}, {0x30BA, {0x8B1, 0x2}}, {0x1F9B, {0x729, 0x4}}, {0x16C, {0x11C, 0x2}}, {0xC7, {0x00C, 0x2}}, {0x2F96B, {0xC9B, 0x1}}, {0xF9F8, {0x9D9, 0x1}}, {0x158, {0x0F8, 0x2}}, {0x1D163, {0xB1A, 0x3}}, {0x1EA7, {0x4B9, 0x3}}, {0x2288, {0x84D, 0x2}}, {0xFACB, {0xA9E, 0x1}}, {0xF901, {0x8E2, 0x1}}, {0x1F8E, {0x6F9, 0x4}}, {0x2FA09, {0xD39, 0x1}}, {0x2F9C8, {0xCF8, 0x1}}, {0x1E0E, {0x370, 0x2}}, {0xF949, {0x92A, 0x1}}, {0xE8, {0x042, 0x2}}, {0xFA3C, {0xA11, 0x1}}, {0x2F8EC, {0xC1C, 0x1}}, {0x2F8DB, {0xC0B, 0x1}}, {0x218, {0x1E0, 0x2}}, {0x3065, {0x885, 0x2}}, {0xFA7C, {0xA4F, 0x1}}, {0x1F82, {0x6CB, 0x4}}, {0x1E9B, {0x4A6, 0x2}}, {0x209, {0x1C2, 0x2}}, {0x1FD1, {0x7B6, 0x2}}, {0x2F9FA, {0xD2A, 0x1}}, {0x30F4, {0x8D5, 0x2}}, {0x1F1A, {0x5D8, 0x3}}, {0x1E5C, {0x41E, 0x3}}, {0xFA6B, {0xA40, 0x1}}, {0x2F9DE, {0xD0E, 0x1}}, {0xF9C6, {0x9A7, 0x1}}, {0x1DA, {0x169, 0x3}}, {0x1F0E, {0x5BE, 0x3}}, {0xF97B, {0x95C, 0x1}}, {0xF9C3, {0x9A4, 0x1}}, {0x117, {0x094, 0x2}}, {0xF945, {0x926, 0x1}}, {0x2F9FE, {0xD2E, 0x1}}, {0x1ED6, {0x538, 0x3}}, {0x625, {0x2B4, 0x2}}, {0x2F9BC, {0xCEC, 0x1}}, {0x2249, {0x82D, 0x2}}, {0x1D4, {0x158, 0x2}}, {0x2F9F0, {0xD20, 0x1}}, {0x2F931, {0xC61, 0x1}}, {0x2F92A, {0xC5A, 0x1}}, {0x1FDB, {0x7C9, 0x2}}, {0xFA7B, {0xA4E, 0x1}}, {0x2F882, {0xBB2, 0x1}}, {0x2F8EF, {0xC1F, 0x1}}, {0xF94F, {0x930, 0x1}}, {0xF93, {0x32E, 0x2}}, {0x2F945, {0xC75, 0x1}}, {0x3056, {0x877, 0x2}}, {0x959, {0x2C6, 0x2}}, {0x22AC, {0x851, 0x2}}, {0x1FF6, {0x7FB, 0x2}}, {0x2F801, {0xB31, 0x1}}, {0xF921, {0x902, 0x1}}, {0xFA41, {0xA16, 0x1}}, {0xF9FE, {0x9DF, 0x1}}, {0xFAA3, {0xA76, 0x1}}, {0x2F8B4, {0xBE4, 0x1}}, {0x20C, {0x1C8, 0x2}}, {0xF9C1, {0x9A2, 0x1}}, {0x2000, {0x80B, 0x1}}, {0x2F905, {0xC35, 0x1}}, {0x40D, {0x250, 0x2}}, {0x1F79, {0x6BB, 0x2}}, {0x2F95B, {0xC8B, 0x1}}, {0x1E72, {0x452, 0x2}}, {0x2F91B, {0xC4B, 0x1}}, {0xF9E1, {0x9C2, 0x1}}, {0x2F9CB, {0xCFB, 0x1}}, {0x2F84D, {0xB7D, 0x1}}, {0x2F8A7, {0xBD7, 0x1}}, {0x1F05, {0x5A5, 0x3}}, {0xFB30, {0xABF, 0x2}}, {0xFA01, {0x9E2, 0x1}}, {0xEF, {0x050, 0x2}}, {0x1D1C0, {0xB2D, 0x3}}, {0x2F819, {0xB49, 0x1}}, {0xCA, {0x012, 0x2}}, {0x21AE, {0x815, 0x2}}, {0x1E2E, {0x3B6, 0x3}}, {0x30DA, {0x8CF, 0x2}}, {0x2F880, {0xBB0, 0x1}}, {0x2F832, {0xB62, 0x1}}, {0x1DB, {0x16C, 0x3}}, {0x4E7, {0x290, 0x2}}, {0x2F85B, {0xB8B, 0x1}}, {0x1F0, {0x198, 0x2}}, {0x2F9C7, {0xCF7, 0x1}}, {0x1E68, {0x43C, 0x3}}, {0xF90C, {0x8ED, 0x1}}, {0xFB4D, {0xAEF, 0x2}}, {0x2F8F4, {0xC24, 0x1}}, {0x3B0, {0x235, 0x3}}, {0xF90F, {0x8F0, 0x1}}, {0x2F89F, {0xBCF, 0x1}}, {0x1F9A, {0x725, 0x4}}, {0xFA88, {0xA5B, 0x1}}, {0x1D1BF, {0xB2A, 0x3}}, {0x3071, {0x88D, 0x2}}, {0xFA69, {0xA3E, 0x1}}, {0x1026, {0x33A, 0x2}}, {0xFA1A, {0x9F6, 0x1}}, {0x1E03, {0x358, 0x2}}, {0xF928, {0x909, 0x1}}, {0x2F974, {0xCA4, 0x1}}, {0xB4B, {0x2EC, 0x2}}, {0x1F1D, {0x5E1, 0x3}}, {0x165, {0x112, 0x2}}, {0xFAD2, {0xAA5, 0x1}}, {0xFAC9, {0xA9C, 0x1}}, {0x2F897, {0xBC7, 0x1}}, {0x2FA0F, {0xD3F, 0x1}}, {0x2F9AE, {0xCDE, 0x1}}, {0xC8, {0x00E, 0x2}}, {0x2F8BC, {0xBEC, 0x1}}, {0x2F96A, {0xC9A, 0x1}}, {0x2F9FB, {0xD2B, 0x1}}, {0x2F8CD, {0xBFD, 0x1}}, {0xFB1F, {0xAAF, 0x2}}, {0x2F94B, {0xC7B, 0x1}}, {0x2F811, {0xB41, 0x1}}, {0x95A, {0x2C8, 0x2}}, {0x30DC, {0x8D1, 0x2}}, {0xF78, {0x32A, 0x2}}, {0x2F909, {0xC39, 0x1}}, {0x22AE, {0x855, 0x2}}, {0xF9D8, {0x9B9, 0x1}}, {0xFA8E, {0xA61, 0x1}}, {0x1E2A, {0x3AE, 0x2}}, {0x108, {0x07A, 0x2}}, {0x2F9CF, {0xCFF, 0x1}}, {0x1E7B, {0x467, 0x3}}, {0x2F8CB, {0xBFB, 0x1}}, {0xFAC2, {0xA95, 0x1}}, {0x1D15F, {0xB0F, 0x2}}, {0xF924, {0x905, 0x1}}, {0xF96F, {0x950, 0x1}}, {0x1EDE, {0x550, 0x3}}, {0x2F987, {0xCB7, 0x1}}, {0x1F72, {0x6AD, 0x2}}, {0x1ED4, {0x532, 0x3}}, {0x2F8B2, {0xBE2, 0x1}}, {0xFA42, {0xA17, 0x1}}, {0x626, {0x2B6, 0x2}}, {0xFA9D, {0xA70, 0x1}}, {0x2F9B3, {0xCE3, 0x1}}, {0x2F919, {0xC49, 0x1}}, {0xF9D3, {0x9B4, 0x1}}, {0xFA71, {0xA44, 0x1}}, {0x1F2F, {0x60D, 0x3}}, {0x1ED9, {0x541, 0x3}}, {0xFB2F, {0xABD, 0x2}}, {0x2F8DC, {0xC0C, 0x1}}, {0x1EB4, {0x4E0, 0x3}}, {0xFA25, {0x9FD, 0x1}}, {0x2F86D, {0xB9D, 0x1}}, {0x4EA, {0x292, 0x2}}, {0x1ECD, {0x520, 0x2}}, {0x1F01, {0x59A, 0x2}}, {0x2F8D0, {0xC00, 0x1}}, {0x2F93A, {0xC6A, 0x1}}, {0x40C, {0x24E, 0x2}}, {0xF93A, {0x91B, 0x1}}, {0xF9FF, {0x9E0, 0x1}}, {0x1E26, {0x3A6, 0x2}}, {0x1F08, {0x5AE, 0x2}}, {0x1F03, {0x59F, 0x3}}, {0xFAD7, {0xAAA, 0x1}}, {0x2F884, {0xBB4, 0x1}}, {0x2F845, {0xB75, 0x1}}, {0x38E, {0x222, 0x2}}, {0x4D0, {0x26E, 0x2}}, {0x30BE, {0x8B5, 0x2}}, {0x1F32, {0x614, 0x3}}, {0x2FA08, {0xD38, 0x1}}, {0x17D, {0x13E, 0x2}}, {0x1E3D, {0x3D8, 0x2}}, {0xFA2C, {0xA01, 0x1}}, {0x2F95F, {0xC8F, 0x1}}, {0x2F99C, {0xCCC, 0x1}}, {0x1F6F, {0x6A6, 0x3}}, {0xF937, {0x918, 0x1}}, {0xFA34, {0xA09, 0x1}}, {0x1F2A, {0x5FE, 0x3}}, {0xF69, {0x322, 0x2}}, {0xE0, {0x034, 0x2}}, {0x2F978, {0xCA8, 0x1}}, {0x2F97F, {0xCAF, 0x1}}, {0x1EF, {0x196, 0x2}}, {0x1B43, {0x350, 0x2}}, {0x155, {0x0F2, 0x2}}, {0x1FC6, {0x79F, 0x2}}, {0x20A, {0x1C4, 0x2}}, {0x3077, {0x895, 0x2}}, {0x1E1B, {0x38E, 0x2}}, {0x2F885, {0xBB5, 0x1}}, {0x20D, {0x1CA, 0x2}}, {0xF9C4, {0x9A5, 0x1}}, {0xF9A7, {0x988, 0x1}}, {0xF98E, {0x96F, 0x1}}, {0xF4D, {0x31A, 0x2}}, {0x2F805, {0xB35, 0x1}}, {0x159, {0x0FA, 0x2}}, {0xF4, {0x058, 0x2}}, {0x1F40, {0x63C, 0x2}}, {0xFA00, {0x9E1, 0x1}}, {0x2F9F6, {0xD26, 0x1}}, {0xFAAE, {0xA81, 0x1}}, {0xFA15, {0x9F1, 0x1}}, {0x212B, {0x80F, 0x2}}, {0x2FA06, {0xD36, 0x1}}, {0xD5, {0x026, 0x2}}, {0x1E2D, {0x3B4, 0x2}}, {0x1F0F, {0x5C1, 0x3}}, {0x2271, {0x83B, 0x2}}, {0x1F71, {0x6AB, 0x2}}, {0xFA47, {0xA1C, 0x1}}, {0xD4B, {0x30B, 0x2}}, {0x2F81C, {0xB4C, 0x1}}, {0x1FB9, {0x78C, 0x2}}, {0xFA26, {0x9FE, 0x1}}, {0x1FF9, {0x802, 0x2}}, {0x134, {0x0C4, 0x2}}, {0xF991, {0x972, 0x1}}, {0x1D160, {0xB11, 0x3}}, {0xF9AC, {0x98D, 0x1}}, {0x2F828, {0xB58, 0x1}}, {0x1FD9, {0x7C5, 0x2}}, {0x2F8EB, {0xC1B, 0x1}}, {0x2F88B, {0xBBB, 0x1}}, {0x2F9B1, {0xCE1, 0x1}}, {0xF9BC, {0x99D, 0x1}}, {0xFA2A, {0x9FF, 0x1}}, {0x307C, {0x89B, 0x2}}, {0x1F5, {0x19C, 0x2}}, {0x1F78, {0x6B9, 0x2}}, {0xFA72, {0xA45, 0x1}}, {0x2280, {0x845, 0x2}}, {0x2F918, {0xC48, 0x1}}, {0xE4, {0x03C, 0x2}}, {0x2F802, {0xB32, 0x1}}, {0x2F8D6, {0xC06, 0x1}}, {0x388, {0x21A, 0x2}}, {0xFAC5, {0xA98, 0x1}}, {0x10D, {0x084, 0x2}}, {0xF958, {0x939, 0x1}}, {0x1EBE, {0x4F8, 0x3}}, {0xF90A, {0x8EB, 0x1}}, {0x1F9, {0x1A0, 0x2}}, {0xB4C, {0x2EE, 0x2}}, {0x1F57, {0x66F, 0x3}}, {0x1E06, {0x35E, 0x2}}, {0x1ED1, {0x529, 0x3}}, {0x1FF3, {0x7F6, 0x2}}, {0x1E2B, {0x3B0, 0x2}}, {0x1EB8, {0x4EC, 0x2}}, {0x2F875, {0xBA5, 0x1}}, {0xF942, {0x923, 0x1}}, {0x1F6A, {0x697, 0x3}}, {0x1E0, {0x178, 0x3}}, {0x2F94D, {0xC7D, 0x1}}, {0xC48, {0x2FC, 0x2}}, {0x2F810, {0xB40, 0x1}}, {0x1F36, {0x620, 0x3}}, {0x2F8F3, {0xC23, 0x1}}, {0xFD, {0x066, 0x2}}, {0xF961, {0x942, 0x1}}, {0x2F873, {0xBA3, 0x1}}, {0x37E, {0x214, 0x1}}, {0xF911, {0x8F2, 0x1}}, {0xFAC6, {0xA99, 0x1}}, {0x2F89D, {0xBCD, 0x1}}, {0x2F960, {0xC90, 0x1}}, {0xFA82, {0xA55, 0x1}}, {0x2F96F, {0xC9F, 0x1}}, {0x1F33, {0x617, 0x3}}, {0x1F5F, {0x67A, 0x3}}, {0x1E67, {0x439, 0x3}}, {0x30D9, {0x8CD, 0x2}}, {0xF99A, {0x97B, 0x1}}, {0x22EC, {0x865, 0x2}}, {0x204, {0x1B8, 0x2}}, {0xDC, {0x030, 0x2}}, {0x2F925, {0xC55, 0x1}}, {0xFA68, {0xA3D, 0x1}}, {0x2F840, {0xB70, 0x1}}, {0x3CD, {0x23E, 0x2}}, {0x1E90, {0x492, 0x2}}, {0x1F75, {0x6B3, 0x2}}, {0xDDC, {0x311, 0x2}}, {0x2F83F, {0xB6F, 0x1}}, {0xF976, {0x957, 0x1}}, {0x2F86E, {0xB9E, 0x1}}, {0x150, {0x0EC, 0x2}}, {0x1E99, {0x4A4, 0x2}}, {0x1D1, {0x152, 0x2}}, {0x2F916, {0xC46, 0x1}}, {0x2F929, {0xC59, 0x1}}, {0x1F04, {0x5A2, 0x3}}, {0x1FA7, {0x757, 0x4}}, {0x1DF, {0x175, 0x3}}, {0xFB44, {0xADF, 0x2}}, {0x11C, {0x09E, 0x2}}, {0x13A, {0x0CE, 0x2}}, {0x1F10, {0x5C4, 0x2}}, {0x2F8C4, {0xBF4, 0x1}}, {0x1E97, {0x4A0, 0x2}}, {0xF954, {0x935, 0x1}}, {0x2F900, {0xC30, 0x1}}, {0x30FE, {0x8DF, 0x2}}, {0x30B6, {0x8AD, 0x2}}, {0x1F69, {0x695, 0x2}}, {0xF9AD, {0x98E, 0x1}}, {0x1E77, {0x45C, 0x2}}, {0xFA37, {0xA0C, 0x1}}, {0xF934, {0x915, 0x1}}, {0xFAB0, {0xA83, 0x1}}, {0x22EA, {0x861, 0x2}}, {0x2F89C, {0xBCC, 0x1}}, {0x227, {0x1EE, 0x2}}, {0x1F2B, {0x601, 0x3}}, {0x2F979, {0xCA9, 0x1}}, {0x2F954, {0xC84, 0x1}}, {0x1E1E, {0x396, 0x2}}, {0xF9AE, {0x98F, 0x1}}, {0x2F8E5, {0xC15, 0x1}}, {0x3AF, {0x233, 0x2}}, {0x1F4C, {0x656, 0x3}}, {0xF941, {0x922, 0x1}}, {0x2F934, {0xC64, 0x1}}, {0xF9A3, {0x984, 0x1}}, {0x929, {0x2BE, 0x2}}, {0x2F89B, {0xBCB, 0x1}}, {0x2F8B0, {0xBE0, 0x1}}, {0x1E83, {0x478, 0x2}}, {0x2F9EC, {0xD1C, 0x1}}, {0x1F60, {0x67D, 0x2}}, {0x623, {0x2B0, 0x2}}, {0x1E6E, {0x44A, 0x2}}, {0x1112E, {0xAF9, 0x2}}, {0xB5D, {0x2F2, 0x2}}, {0x215, {0x1DA, 0x2}}, {0x2F9BD, {0xCED, 0x1}}, {0x176, {0x130, 0x2}}, {0x1EAB, {0x4C5, 0x3}}, {0x2F9E6, {0xD16, 0x1}}, {0x2F864, {0xB94, 0x1}}, {0x1F31, {0x612, 0x2}}, {0x2F92F, {0xC5F, 0x1}}, {0x2F820, {0xB50, 0x1}}, {0x1FDA, {0x7C7, 0x2}}, {0x2F82C, {0xB5C, 0x1}}, {0x1F45, {0x649, 0x3}}, {0x1F7C, {0x6C1, 0x2}}, {0x1E53, {0x40B, 0x3}}, {0x2F972, {0xCA2, 0x1}}, {0x2FA1A, {0xD4A, 0x1}}, {0xF960, {0x941, 0x1}}, {0x1D7, {0x160, 0x3}}, {0x1EE8, {0x56A, 0x3}}, {0xCCA, {0x304, 0x2}}, {0x1F42, {0x640, 0x3}}, {0x2F95D, {0xC8D, 0x1}}, {0x1EE, {0x194, 0x2}}, {0x2F8D3, {0xC03, 0x1}}, {0x1E4D, {0x3F9, 0x3}}, {0xD6, {0x028, 0x2}}, {0x1F96, {0x717, 0x4}}, {0xF918, {0x8F9, 0x1}}, {0xF9AF, {0x990, 0x1}}, {0x2F8FB, {0xC2B, 0x1}}, {0x1E60, {0x428, 0x2}}, {0x1B12, {0x346, 0x2}}, {0xF9FD, {0x9DE, 0x1}}, {0x2F9EB, {0xD1B, 0x1}}, {0x1E64, {0x430, 0x3}}, {0xFB2B, {0xAB3, 0x2}}, {0x1FE0, {0x7D1, 0x2}}, {0x2F915, {0xC45, 0x1}}, {0x2F81B, {0xB4B, 0x1}}, {0x1FDD, {0x7CB, 0x2}}, {0x2F8B6, {0xBE6, 0x1}}, {0x1E89, {0x484, 0x2}}, {0xF9B9, {0x99A, 0x1}}, {0x1FB3, {0x780, 0x2}}, {0x1E3, {0x180, 0x2}}, {0x4E3, {0x288, 0x2}}, {0xCF, {0x01C, 0x2}}, {0xF1, {0x052, 0x2}}, {0x2F9BF, {0xCEF, 0x1}}, {0x2F8C9, {0xBF9, 0x1}}, {0x2F8A8, {0xBD8, 0x1}}, {0x2F95A, {0xC8A, 0x1}}, {0x2F9A8, {0xCD8, 0x1}}, {0x1A1, {0x144, 0x2}}, {0xF99E, {0x97F, 0x1}}, {0xFB46, {0xAE1, 0x2}}, {0xF9C7, {0x9A8, 0x1}}, {0x476, {0x266, 0x2}}, {0x1CD, {0x14A, 0x2}}, {0xF9BF, {0x9A0, 0x1}}, {0x1EAD, {0x4CB, 0x3}}, {0x105, {0x074, 0x2}}, {0x2F9A4, {0xCD4, 0x1}}, {0x15B, {0x0FE, 0x2}}, {0xF9C9, {0x9AA, 0x1}}, {0x119, {0x098, 0x2}}, {0xF946, {0x927, 0x1}}, {0x3054, {0x875, 0x2}}, {0x2FA0C, {0xD3C, 0x1}}, {0x1CF, {0x14E, 0x2}}, {0xFA7D, {0xA50, 0x1}}, {0xF944, {0x925, 0x1}}, {0xFA1D, {0x9F9, 0x1}}, {0x2F9C2, {0xCF2, 0x1}}, {0xF964, {0x945, 0x1}}, {0x219, {0x1E2, 0x2}}, {0xF9DD, {0x9BE, 0x1}}, {0x1F8B, {0x6ED, 0x4}}, {0x2262, {0x831, 0x2}}, {0xF9B3, {0x994, 0x1}}, {0xF9A2, {0x983, 0x1}}, {0x3050, {0x871, 0x2}}, {0x1E8, {0x186, 0x2}}, {0x30D3, {0x8C5, 0x2}}, {0x30C9, {0x8BF, 0x2}}, {0xF95A, {0x93B, 0x1}}, {0xF98A, {0x96B, 0x1}}, {0xFA76, {0xA49, 0x1}}, {0x2F816, {0xB46, 0x1}}, {0x1B40, {0x34C, 0x2}}, {0x212, {0x1D4, 0x2}}, {0x1E91, {0x494, 0x2}}, {0xF9E4, {0x9C5, 0x1}}, {0x1F98, {0x71F, 0x3}}, {0xF910, {0x8F1, 0x1}}, {0x2F9D8, {0xD08, 0x1}}, {0x2F91D, {0xC4D, 0x1}}, {0x1F9C, {0x72D, 0x4}}, {0xDB, {0x02E, 0x2}}, {0x232, {0x20A, 0x2}}, {0xF995, {0x976, 0x1}}, {0xF9E2, {0x9C3, 0x1}}, {0x1F23, {0x5EB, 0x3}}, {0xDDE, {0x316, 0x2}}, {0xF9CE, {0x9AF, 0x1}}, {0x1EED, {0x579, 0x3}}, {0x1F0C, {0x5B8, 0x3}}, {0x2F91C, {0xC4C, 0x1}}, {0x30B0, {0x8A7, 0x2}}, {0xF990, {0x971, 0x1}}, {0x1F68, {0x693, 0x2}}, {0x1FBC, {0x792, 0x2}}, {0x2F9B7, {0xCE7, 0x1}}, {0xF96B, {0x94C, 0x1}}, {0xFA09, {0x9EA, 0x1}}, {0x1FF, {0x1AE, 0x2}}, {0x2F910, {0xC40, 0x1}}, {0x2F9F4, {0xD24, 0x1}}, {0x2F87F, {0xBAF, 0x1}}, {0x1E59, {0x418, 0x2}}, {0x114BE, {0xB05, 0x2}}, {0xF96C, {0x94D, 0x1}}, {0xFAC, {0x336, 0x2}}, {0xF913, {0x8F4, 0x1}}, {0x2F80D, {0xB3D, 0x1}}, {0x2F927, {0xC57, 0x1}}, {0x2F837, {0xB67, 0x1}}, {0x30AC, {0x8A3, 0x2}}, {0x1FEE, {0x7F0, 0x2}}, {0xF9A1, {0x982, 0x1}}, {0x1E54, {0x40E, 0x2}}, {0x1F6E, {0x6A3, 0x3}}, {0xFAB7, {0xA8A, 0x1}}, {0xFB3B, {0xAD3, 0x2}}, {0x2F9F1, {0xD21, 0x1}}, {0xA36, {0x2E0, 0x2}}, {0x1F89, {0x6E6, 0x3}}, {0x1ECC, {0x51E, 0x2}}, {0x2F958, {0xC88, 0x1}}, {0x16B, {0x11A, 0x2}}, {0x2F944, {0xC74, 0x1}}, {0x2F8F9, {0xC29, 0x1}}, {0x407, {0x24C, 0x2}}, {0x1EF6, {0x590, 0x2}}, {0x2F9A3, {0xCD3, 0x1}}, {0x2F881, {0xBB1, 0x1}}, {0x1134C, {0xAFF, 0x2}}, {0x2F81A, {0xB4A, 0x1}}, {0x2F9DD, {0xD0D, 0x1}}, {0xFA64, {0xA39, 0x1}}, {0x2F8AF, {0xBDF, 0x1}}, {0x2F9ED, {0xD1D, 0x1}}, {0xF9EA, {0x9CB, 0x1}}, {0x1109C, {0xAF5, 0x2}}, {0x14F, {0x0EA, 0x2}}, {0xF97A, {0x95B, 0x1}}, {0x2F949, {0xC79, 0x1}}, {0xA5B, {0x2E6, 0x2}}, {0xF936, {0x917, 0x1}}, {0xA5E, {0x2E8, 0x2}}, {0x2F89A, {0xBCA, 0x1}}, {0x1F4B, {0x653, 0x3}}, {0xF984, {0x965, 0x1}}, {0x1E6D, {0x448, 0x2}}, {0x2F90E, {0xC3E, 0x1}}, {0x232A, {0x86A, 0x1}}, {0x2FA0D, {0xD3D, 0x1}}, {0xFAB2, {0xA85, 0x1}}, {0x450, {0x258, 0x2}}, {0x1F02, {0x59C, 0x3}}, {0xFA32, {0xA07, 0x1}}, {0x1F76, {0x6B5, 0x2}}, {0x2F96D, {0xC9D, 0x1}}, {0x2F9A7, {0xCD7, 0x1}}, {0x1EA3, {0x4AE, 0x2}}, {0xF9BA, {0x99B, 0x1}}, {0x1F15, {0x5D1, 0x3}}, {0x1FAF, {0x775, 0x4}}, {0x160, {0x108, 0x2}}, {0x2F963, {0xC93, 0x1}}, {0xF925, {0x906, 0x1}}, {0xF9F6, {0x9D7, 0x1}}, {0x30C0, {0x8B7, 0x2}}, {0x21E, {0x1E8, 0x2}}, {0xFA9C, {0xA6F, 0x1}}, {0xFB3A, {0xAD1, 0x2}}, {0x1FBA, {0x78E, 0x2}}, {0x374, {0x213, 0x1}}, {0x2ADC, {0x86B, 0x2}}, {0x2F91F, {0xC4F, 0x1}}, {0xCC0, {0x2FE, 0x2}}, {0x1EF3, {0x58A, 0x2}}, {0x1FC3, {0x79A, 0x2}}, {0x2F8BD, {0xBED, 0x1}}, {0x11F, {0x0A4, 0x2}}, {0xFA7, {0x334, 0x2}}, {0xF9F4, {0x9D5, 0x1}}, {0xBCC, {0x2FA, 0x2}}, {0x2F847, {0xB77, 0x1}}, {0x1F4A, {0x650, 0x3}}, {0x2F98F, {0xCBF, 0x1}}, {0xF96E, {0x94F, 0x1}}, {0xF9E6, {0x9C7, 0x1}}, {0xF9E3, {0x9C4, 0x1}}, {0x2F844, {0xB74, 0x1}}, {0x2F813, {0xB43, 0x1}}, {0xFAB5, {0xA88, 0x1}}, {0x1FAA, {0x761, 0x4}}, {0xC1, {0x002, 0x2}}, {0x1E6B, {0x444, 0x2}}, {0x1FE5, {0x7DD, 0x2}}, {0x2F807, {0xB37, 0x1}}, {0x2F85F, {0xB8F, 0x1}}, {0xF93B, {0x91C, 0x1}}, {0x1F8, {0x19E, 0x2}}, {0x1E69, {0x43F, 0x3}}, {0x1FE7, {0x7E1, 0x3}}, {0x1E57, {0x414, 0x2}}, {0xCC, {0x016, 0x2}}, {0x2F99B, {0xCCB, 0x1}}, {0x4F0, {0x29E, 0x2}}, {0x2F8FD, {0xC2D, 0x1}}, {0x121, {0x0A8, 0x2}}, {0xF2, {0x054, 0x2}}, {0xFA9A, {0xA6D, 0x1}}, {0x2F9EF, {0xD1F, 0x1}}, {0x2F878, {0xBA8, 0x1}}, {0xF959, {0x93A, 0x1}}, {0x1134B, {0xAFD, 0x2}}, {0x1FBE, {0x794, 0x1}}, {0xFA56, {0xA2B, 0x1}}, {0x1D1BE, {0xB27, 0x3}}, {0x2F8BB, {0xBEB, 0x1}}, {0x2F8E4, {0xC14, 0x1}}, {0x1E19, {0x38A, 0x2}}, {0x2F92D, {0xC5D, 0x1}}, {0x1F2C, {0x604, 0x3}}, {0xE1, {0x036, 0x2}}, {0x2F9B6, {0xCE6, 0x1}}, {0xF98D, {0x96E, 0x1}}, {0xF914, {0x8F5, 0x1}}, {0xFA19, {0x9F5, 0x1}}, {0xFACE, {0xAA1, 0x1}}, {0x15A, {0x0FC, 0x2}}, {0x2274, {0x83D, 0x2}}, {0xF94C, {0x92D, 0x1}}, {0x1FC4, {0x79C, 0x3}}, {0x115BA, {0xB07, 0x2}}, {0x2F8B8, {0xBE8, 0x1}}, {0x2FA01, {0xD31, 0x1}}, {0x2F8EA, {0xC1A, 0x1}}, {0x1E98, {0x4A2, 0x2}}, {0x2F98B, {0xCBB, 0x1}}, {0x2F9DC, {0xD0C, 0x1}}, {0x1F99, {0x722, 0x3}}, {0x1F8A, {0x6E9, 0x4}}, {0x1F1C, {0x5DE, 0x3}}, {0xFA38, {0xA0D, 0x1}}, {0x2F953, {0xC83, 0x1}}, {0x2F8F7, {0xC27, 0x1}}, {0x1F41, {0x63E, 0x2}}, {0x1EC7, {0x513, 0x3}}, {0x2FA16, {0xD46, 0x1}}, {0x2F859, {0xB89, 0x1}}, {0x2F959, {0xC89, 0x1}}, {0x2F8CF, {0xBFF, 0x1}}, {0x14C, {0x0E4, 0x2}}, {0x2F993, {0xCC3, 0x1}}, {0x13E, {0x0D6, 0x2}}, {0x12A, {0x0B6, 0x2}}, {0x2F991, {0xCC1, 0x1}}, {0x1FCA, {0x7A8, 0x2}}, {0x389, {0x21C, 0x2}}, {0xFA73, {0xA46, 0x1}}, {0x2F9D9, {0xD09, 0x1}}, {0x143, {0x0D8, 0x2}}, {0x2F893, {0xBC3, 0x1}}, {0x2F827, {0xB57, 0x1}}, {0xFA63, {0xA38, 0x1}}, {0x3AC, {0x22D, 0x2}}, {0x1F07, {0x5AB, 0x3}}, {0x1EBB, {0x4F2, 0x2}}, {0x2F943, {0xC73, 0x1}}, {0xFA67, {0xA3C, 0x1}}, {0x4D6, {0x276, 0x2}}, {0x1F12, {0x5C8, 0x3}}, {0x1F4, {0x19A, 0x2}}, {0x1E37, {0x3CA, 0x2}}, {0x2F90F, {0xC3F, 0x1}}, {0x1F18, {0x5D4, 0x2}}, {0x477, {0x268, 0x2}}, {0x102, {0x06E, 0x2}}, {0x2F800, {0xB30, 0x1}}, {0xFAAF, {0xA82, 0x1}}, {0x6C0, {0x2B8, 0x2}}, {0xEB, {0x048, 0x2}}, {0x1EDA, {0x544, 0x3}}, {0x1F7D, {0x6C3, 0x2}}, {0xA59, {0x2E2, 0x2}}, {0x2F8F2, {0xC22, 0x1}}, {0x309E, {0x8A1, 0x2}}, {0x30D6, {0x8C9, 0x2}}, {0x2F92E, {0xC5E, 0x1}}, {0xF933, {0x914, 0x1}}, {0x2F96E, {0xC9E, 0x1}}, {0x1E55, {0x410, 0x2}}, {0x2281, {0x847, 0x2}}, {0x1E4E, {0x3FC, 0x3}}, {0x2F872, {0xBA2, 0x1}}, {0x2F9FC, {0xD2C, 0x1}}, {0x1D1BC, {0xB22, 0x2}}, {0xFA50, {0xA25, 0x1}}, {0x2F92C, {0xC5C, 0x1}}, {0x3070, {0x88B, 0x2}}, {0xF9E9, {0x9CA, 0x1}}, {0x2F83C, {0xB6C, 0x1}}, {0x2F9F8, {0xD28, 0x1}}, {0x1F52, {0x660, 0x3}}, {0xFAAC, {0xA7F, 0x1}}, {0x1E94, {0x49A, 0x2}}, {0xFAD6, {0xAA9, 0x1}}, {0x1FCB, {0x7AA, 0x2}}, {0xFA33, {0xA08, 0x1}}, {0xFA1B, {0x9F7, 0x1}}, {0xF9EE, {0x9CF, 0x1}}, {0x1E2C, {0x3B2, 0x2}}, {0x203, {0x1B6, 0x2}}, {0x2F877, {0xBA7, 0x1}}, {0x1FF8, {0x800, 0x2}}, {0x1E9, {0x188, 0x2}}, {0x1FE, {0x1AC, 0x2}}, {0x2F96C, {0xC9C, 0x1}}, {0x1E51, {0x405, 0x3}}, {0x4DE, {0x282, 0x2}}, {0xF9A4, {0x985, 0x1}}, {0xFB40, {0xAD9, 0x2}}, {0x622, {0x2AE, 0x2}}, {0x3CC, {0x23C, 0x2}}, {0x2F99E, {0xCCE, 0x1}}, {0xF92B, {0x90C, 0x1}}, {0x22E1, {0x85B, 0x2}}, {0x174, {0x12C, 0x2}}, {0x1E4A, {0x3F2, 0x2}}, {0x2F939, {0xC69, 0x1}}, {0x3CE, {0x240, 0x2}}, {0xFAA7, {0xA7A, 0x1}}, {0xFA85, {0xA58, 0x1}}, {0xF9E7, {0x9C8, 0x1}}, {0x107, {0x078, 0x2}}, {0xFA95, {0xA68, 0x1}}, {0xF973, {0x954, 0x1}}, {0x2F933, {0xC63, 0x1}}, {0xF9C2, {0x9A3, 0x1}}, {0x1EE1, {0x559, 0x3}}, {0x1D3, {0x156, 0x2}}, {0x2F9D0, {0xD00, 0x1}}, {0xFB2A, {0xAB1, 0x2}}, {0xFB, {0x062, 0x2}}, {0x1E4F, {0x3FF, 0x3}}, {0x1F26, {0x5F4, 0x3}}, {0xDD, {0x032, 0x2}}, {0x226, {0x1EC, 0x2}}, {0x2F831, {0xB61, 0x1}}, {0x2F9EE, {0xD1E, 0x1}}, {0xF955, {0x936, 0x1}}, {0x16E, {0x120, 0x2}}, {0x2F8A5, {0xBD5, 0x1}}, {0x11B, {0x09C, 0x2}}, {0x1E76, {0x45A, 0x2}}, {0xD4C, {0x30D, 0x2}}, {0x1F3C, {0x630, 0x3}}, {0x1E0A, {0x368, 0x2}}, {0x1E78, {0x45E, 0x3}}, {0x2F82A, {0xB5A, 0x1}}, {0x12D, {0x0BC, 0x2}}, {0x2F8C5, {0xBF5, 0x1}}, {0x1F3E, {0x636, 0x3}}, {0xF95C, {0x93D, 0x1}}, {0x1F21, {0x5E6, 0x2}}, {0x2F9D7, {0xD07, 0x1}}, {0x1F70, {0x6A9, 0x2}}, {0x2F914, {0xC44, 0x1}}, {0xFA83, {0xA56, 0x1}}, {0x1ED5, {0x535, 0x3}}, {0x1EE7, {0x568, 0x2}}, {0x2278, {0x841, 0x2}}, {0xF9C0, {0x9A1, 0x1}}, {0x1ED7, {0x53B, 0x3}}, {0x2F8CA, {0xBFA, 0x1}}, {0x1B3D, {0x34A, 0x2}}, {0x2F9AD, {0xCDD, 0x1}}, {0x95E, {0x2D0, 0x2}}, {0x220C, {0x821, 0x2}}, {0x1FDF, {0x7CF, 0x2}}, {0xFA48, {0xA1D, 0x1}}, {0x1E04, {0x35A, 0x2}}, {0x4D7, {0x278, 0x2}}, {0x216, {0x1DC, 0x2}}, {0x1E70, {0x44E, 0x2}}, {0x2F856, {0xB86, 0x1}}, {0xE9, {0x044, 0x2}}, {0xF951, {0x932, 0x1}}, {0x231, {0x207, 0x3}}, {0x1F53, {0x663, 0x3}}, {0x1E14, {0x37C, 0x3}}, {0xF983, {0x964, 0x1}}, {0xF9D4, {0x9B5, 0x1}}, {0x2F973, {0xCA3, 0x1}}, {0x2F9DF, {0xD0F, 0x1}}, {0xF98C, {0x96D, 0x1}}, {0x208, {0x1C0, 0x2}}, {0x2F895, {0xBC5, 0x1}}, {0xFAAA, {0xA7D, 0x1}}, {0x45C, {0x260, 0x2}}, {0x30B8, {0x8AF, 0x2}}, {0x2244, {0x829, 0x2}}, {0xFF, {0x068, 0x2}}, {0x4D1, {0x270, 0x2}}, {0x2126, {0x80D, 0x1}}, {0x10B, {0x080, 0x2}}, {0x145, {0x0DC, 0x2}}, {0xF978, {0x959, 0x1}}, {0x1FCD, {0x7AE, 0x2}}, {0x2F912, {0xC42, 0x1}}, {0xFA36, {0xA0B, 0x1}}, {0x2F9C0, {0xCF0, 0x1}}, {0x4EC, {0x296, 0x2}}, {0x10E, {0x086, 0x2}}, {0x2F98D, {0xCBD, 0x1}}, {0x1E5D, {0x421, 0x3}}, {0xF43, {0x318, 0x2}}, {0x1E29, {0x3AC, 0x2}}, {0xF93E, {0x91F, 0x1}}, {0x2F9C5, {0xCF5, 0x1}}, {0x21CE, {0x819, 0x2}}, {0x2F87B, {0xBAB, 0x1}}, {0x2F9BA, {0xCEA, 0x1}}, {0x1EC2, {0x504, 0x3}}, {0x219A, {0x811, 0x2}}, {0x1F64, {0x687, 0x3}}, {0xF906, {0x8E7, 0x1}}, {0xF9D7, {0x9B8, 0x1}} }; } } // namespace Cangjie::Unicode #endif // CANGJIE_UTILS_UNICODETABLES_NORMALISATIONDATA_H cangjie_compiler-1.0.7/src/Utils/UnicodeTables/NormalisationTestData.generated.inc000066400000000000000000021373401510705540100303320ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file provides unicode normalisation test data. */ // NOTE: The following code was generated by "NormalisationTests.py", do not edit directly #include struct NormalisationTest { std::string_view source; std::string_view nfc; }; const NormalisationTest NORMALISATION_TEST_DATA[] { {"\u1E0A", "\u1E0A"}, {"\u1E0C", "\u1E0C"}, {"\u1E0A\u0323", "\u1E0C\u0307"}, {"\u1E0C\u0307", "\u1E0C\u0307"}, {"\u0044\u0307\u0323", "\u1E0C\u0307"}, {"\u0044\u0323\u0307", "\u1E0C\u0307"}, {"\u1E0A\u031B", "\u1E0A\u031B"}, {"\u1E0C\u031B", "\u1E0C\u031B"}, {"\u1E0A\u031B\u0323", "\u1E0C\u031B\u0307"}, {"\u1E0C\u031B\u0307", "\u1E0C\u031B\u0307"}, {"\u0044\u031B\u0307\u0323", "\u1E0C\u031B\u0307"}, {"\u0044\u031B\u0323\u0307", "\u1E0C\u031B\u0307"}, {"\u00C8", "\u00C8"}, {"\u0112", "\u0112"}, {"\u0045\u0300", "\u00C8"}, {"\u0045\u0304", "\u0112"}, {"\u1E14", "\u1E14"}, {"\u0112\u0300", "\u1E14"}, {"\u1E14\u0304", "\u1E14\u0304"}, {"\u0045\u0304\u0300", "\u1E14"}, {"\u0045\u0300\u0304", "\u00C8\u0304"}, {"\u05B8\u05B9\u05B1\u0591\u05C3\u05B0\u05AC\u059F", "\u05B1\u05B8\u05B9\u0591\u05C3\u05B0\u05AC\u059F"}, {"\u0592\u05B7\u05BC\u05A5\u05B0\u05C0\u05C4\u05AD", "\u05B0\u05B7\u05BC\u05A5\u0592\u05C0\u05AD\u05C4"}, {"\u1100\uAC00\u11A8", "\u1100\uAC01"}, {"\u1100\uAC00\u11A8\u11A8", "\u1100\uAC01\u11A8"}, {"\u00A0", "\u00A0"}, {"\u00A8", "\u00A8"}, {"\u00AA", "\u00AA"}, {"\u00AF", "\u00AF"}, {"\u00B2", "\u00B2"}, {"\u00B3", "\u00B3"}, {"\u00B4", "\u00B4"}, {"\u00B5", "\u00B5"}, {"\u00B8", "\u00B8"}, {"\u00B9", "\u00B9"}, {"\u00BA", "\u00BA"}, {"\u00BC", "\u00BC"}, {"\u00BD", "\u00BD"}, {"\u00BE", "\u00BE"}, {"\u00C0", "\u00C0"}, {"\u00C1", "\u00C1"}, {"\u00C2", "\u00C2"}, {"\u00C3", "\u00C3"}, {"\u00C4", "\u00C4"}, {"\u00C5", "\u00C5"}, {"\u00C7", "\u00C7"}, {"\u00C8", "\u00C8"}, {"\u00C9", "\u00C9"}, {"\u00CA", "\u00CA"}, {"\u00CB", "\u00CB"}, {"\u00CC", "\u00CC"}, {"\u00CD", "\u00CD"}, {"\u00CE", "\u00CE"}, {"\u00CF", "\u00CF"}, {"\u00D1", "\u00D1"}, {"\u00D2", "\u00D2"}, {"\u00D3", "\u00D3"}, {"\u00D4", "\u00D4"}, {"\u00D5", "\u00D5"}, {"\u00D6", "\u00D6"}, {"\u00D9", "\u00D9"}, {"\u00DA", "\u00DA"}, {"\u00DB", "\u00DB"}, {"\u00DC", "\u00DC"}, {"\u00DD", "\u00DD"}, {"\u00E0", "\u00E0"}, {"\u00E1", "\u00E1"}, {"\u00E2", "\u00E2"}, {"\u00E3", "\u00E3"}, {"\u00E4", "\u00E4"}, {"\u00E5", "\u00E5"}, {"\u00E7", "\u00E7"}, {"\u00E8", "\u00E8"}, {"\u00E9", "\u00E9"}, {"\u00EA", "\u00EA"}, {"\u00EB", "\u00EB"}, {"\u00EC", "\u00EC"}, {"\u00ED", "\u00ED"}, {"\u00EE", "\u00EE"}, {"\u00EF", "\u00EF"}, {"\u00F1", "\u00F1"}, {"\u00F2", "\u00F2"}, {"\u00F3", "\u00F3"}, {"\u00F4", "\u00F4"}, {"\u00F5", "\u00F5"}, {"\u00F6", "\u00F6"}, {"\u00F9", "\u00F9"}, {"\u00FA", "\u00FA"}, {"\u00FB", "\u00FB"}, {"\u00FC", "\u00FC"}, {"\u00FD", "\u00FD"}, {"\u00FF", "\u00FF"}, {"\u0100", "\u0100"}, {"\u0101", "\u0101"}, {"\u0102", "\u0102"}, {"\u0103", "\u0103"}, {"\u0104", "\u0104"}, {"\u0105", "\u0105"}, {"\u0106", "\u0106"}, {"\u0107", "\u0107"}, {"\u0108", "\u0108"}, {"\u0109", "\u0109"}, {"\u010A", "\u010A"}, {"\u010B", "\u010B"}, {"\u010C", "\u010C"}, {"\u010D", "\u010D"}, {"\u010E", "\u010E"}, {"\u010F", "\u010F"}, {"\u0112", "\u0112"}, {"\u0113", "\u0113"}, {"\u0114", "\u0114"}, {"\u0115", "\u0115"}, {"\u0116", "\u0116"}, {"\u0117", "\u0117"}, {"\u0118", "\u0118"}, {"\u0119", "\u0119"}, {"\u011A", "\u011A"}, {"\u011B", "\u011B"}, {"\u011C", "\u011C"}, {"\u011D", "\u011D"}, {"\u011E", "\u011E"}, {"\u011F", "\u011F"}, {"\u0120", "\u0120"}, {"\u0121", "\u0121"}, {"\u0122", "\u0122"}, {"\u0123", "\u0123"}, {"\u0124", "\u0124"}, {"\u0125", "\u0125"}, {"\u0128", "\u0128"}, {"\u0129", "\u0129"}, {"\u012A", "\u012A"}, {"\u012B", "\u012B"}, {"\u012C", "\u012C"}, {"\u012D", "\u012D"}, {"\u012E", "\u012E"}, {"\u012F", "\u012F"}, {"\u0130", "\u0130"}, {"\u0132", "\u0132"}, {"\u0133", "\u0133"}, {"\u0134", "\u0134"}, {"\u0135", "\u0135"}, {"\u0136", "\u0136"}, {"\u0137", "\u0137"}, {"\u0139", "\u0139"}, {"\u013A", "\u013A"}, {"\u013B", "\u013B"}, {"\u013C", "\u013C"}, {"\u013D", "\u013D"}, {"\u013E", "\u013E"}, {"\u013F", "\u013F"}, {"\u0140", "\u0140"}, {"\u0143", "\u0143"}, {"\u0144", "\u0144"}, {"\u0145", "\u0145"}, {"\u0146", "\u0146"}, {"\u0147", "\u0147"}, {"\u0148", "\u0148"}, {"\u0149", "\u0149"}, {"\u014C", "\u014C"}, {"\u014D", "\u014D"}, {"\u014E", "\u014E"}, {"\u014F", "\u014F"}, {"\u0150", "\u0150"}, {"\u0151", "\u0151"}, {"\u0154", "\u0154"}, {"\u0155", "\u0155"}, {"\u0156", "\u0156"}, {"\u0157", "\u0157"}, {"\u0158", "\u0158"}, {"\u0159", "\u0159"}, {"\u015A", "\u015A"}, {"\u015B", "\u015B"}, {"\u015C", "\u015C"}, {"\u015D", "\u015D"}, {"\u015E", "\u015E"}, {"\u015F", "\u015F"}, {"\u0160", "\u0160"}, {"\u0161", "\u0161"}, {"\u0162", "\u0162"}, {"\u0163", "\u0163"}, {"\u0164", "\u0164"}, {"\u0165", "\u0165"}, {"\u0168", "\u0168"}, {"\u0169", "\u0169"}, {"\u016A", "\u016A"}, {"\u016B", "\u016B"}, {"\u016C", "\u016C"}, {"\u016D", "\u016D"}, {"\u016E", "\u016E"}, {"\u016F", "\u016F"}, {"\u0170", "\u0170"}, {"\u0171", "\u0171"}, {"\u0172", "\u0172"}, {"\u0173", "\u0173"}, {"\u0174", "\u0174"}, {"\u0175", "\u0175"}, {"\u0176", "\u0176"}, {"\u0177", "\u0177"}, {"\u0178", "\u0178"}, {"\u0179", "\u0179"}, {"\u017A", "\u017A"}, {"\u017B", "\u017B"}, {"\u017C", "\u017C"}, {"\u017D", "\u017D"}, {"\u017E", "\u017E"}, {"\u017F", "\u017F"}, {"\u01A0", "\u01A0"}, {"\u01A1", "\u01A1"}, {"\u01AF", "\u01AF"}, {"\u01B0", "\u01B0"}, {"\u01C4", "\u01C4"}, {"\u01C5", "\u01C5"}, {"\u01C6", "\u01C6"}, {"\u01C7", "\u01C7"}, {"\u01C8", "\u01C8"}, {"\u01C9", "\u01C9"}, {"\u01CA", "\u01CA"}, {"\u01CB", "\u01CB"}, {"\u01CC", "\u01CC"}, {"\u01CD", "\u01CD"}, {"\u01CE", "\u01CE"}, {"\u01CF", "\u01CF"}, {"\u01D0", "\u01D0"}, {"\u01D1", "\u01D1"}, {"\u01D2", "\u01D2"}, {"\u01D3", "\u01D3"}, {"\u01D4", "\u01D4"}, {"\u01D5", "\u01D5"}, {"\u01D6", "\u01D6"}, {"\u01D7", "\u01D7"}, {"\u01D8", "\u01D8"}, {"\u01D9", "\u01D9"}, {"\u01DA", "\u01DA"}, {"\u01DB", "\u01DB"}, {"\u01DC", "\u01DC"}, {"\u01DE", "\u01DE"}, {"\u01DF", "\u01DF"}, {"\u01E0", "\u01E0"}, {"\u01E1", "\u01E1"}, {"\u01E2", "\u01E2"}, {"\u01E3", "\u01E3"}, {"\u01E6", "\u01E6"}, {"\u01E7", "\u01E7"}, {"\u01E8", "\u01E8"}, {"\u01E9", "\u01E9"}, {"\u01EA", "\u01EA"}, {"\u01EB", "\u01EB"}, {"\u01EC", "\u01EC"}, {"\u01ED", "\u01ED"}, {"\u01EE", "\u01EE"}, {"\u01EF", "\u01EF"}, {"\u01F0", "\u01F0"}, {"\u01F1", "\u01F1"}, {"\u01F2", "\u01F2"}, {"\u01F3", "\u01F3"}, {"\u01F4", "\u01F4"}, {"\u01F5", "\u01F5"}, {"\u01F8", "\u01F8"}, {"\u01F9", "\u01F9"}, {"\u01FA", "\u01FA"}, {"\u01FB", "\u01FB"}, {"\u01FC", "\u01FC"}, {"\u01FD", "\u01FD"}, {"\u01FE", "\u01FE"}, {"\u01FF", "\u01FF"}, {"\u0200", "\u0200"}, {"\u0201", "\u0201"}, {"\u0202", "\u0202"}, {"\u0203", "\u0203"}, {"\u0204", "\u0204"}, {"\u0205", "\u0205"}, {"\u0206", "\u0206"}, {"\u0207", "\u0207"}, {"\u0208", "\u0208"}, {"\u0209", "\u0209"}, {"\u020A", "\u020A"}, {"\u020B", "\u020B"}, {"\u020C", "\u020C"}, {"\u020D", "\u020D"}, {"\u020E", "\u020E"}, {"\u020F", "\u020F"}, {"\u0210", "\u0210"}, {"\u0211", "\u0211"}, {"\u0212", "\u0212"}, {"\u0213", "\u0213"}, {"\u0214", "\u0214"}, {"\u0215", "\u0215"}, {"\u0216", "\u0216"}, {"\u0217", "\u0217"}, {"\u0218", "\u0218"}, {"\u0219", "\u0219"}, {"\u021A", "\u021A"}, {"\u021B", "\u021B"}, {"\u021E", "\u021E"}, {"\u021F", "\u021F"}, {"\u0226", "\u0226"}, {"\u0227", "\u0227"}, {"\u0228", "\u0228"}, {"\u0229", "\u0229"}, {"\u022A", "\u022A"}, {"\u022B", "\u022B"}, {"\u022C", "\u022C"}, {"\u022D", "\u022D"}, {"\u022E", "\u022E"}, {"\u022F", "\u022F"}, {"\u0230", "\u0230"}, {"\u0231", "\u0231"}, {"\u0232", "\u0232"}, {"\u0233", "\u0233"}, {"\u02B0", "\u02B0"}, {"\u02B1", "\u02B1"}, {"\u02B2", "\u02B2"}, {"\u02B3", "\u02B3"}, {"\u02B4", "\u02B4"}, {"\u02B5", "\u02B5"}, {"\u02B6", "\u02B6"}, {"\u02B7", "\u02B7"}, {"\u02B8", "\u02B8"}, {"\u02D8", "\u02D8"}, {"\u02D9", "\u02D9"}, {"\u02DA", "\u02DA"}, {"\u02DB", "\u02DB"}, {"\u02DC", "\u02DC"}, {"\u02DD", "\u02DD"}, {"\u02E0", "\u02E0"}, {"\u02E1", "\u02E1"}, {"\u02E2", "\u02E2"}, {"\u02E3", "\u02E3"}, {"\u02E4", "\u02E4"}, {"\u0340", "\u0300"}, {"\u0341", "\u0301"}, {"\u0343", "\u0313"}, {"\u0344", "\u0308\u0301"}, {"\u0374", "\u02B9"}, {"\u037A", "\u037A"}, {"\u037E", "\u003B"}, {"\u0384", "\u0384"}, {"\u0385", "\u0385"}, {"\u0386", "\u0386"}, {"\u0387", "\u00B7"}, {"\u0388", "\u0388"}, {"\u0389", "\u0389"}, {"\u038A", "\u038A"}, {"\u038C", "\u038C"}, {"\u038E", "\u038E"}, {"\u038F", "\u038F"}, {"\u0390", "\u0390"}, {"\u03AA", "\u03AA"}, {"\u03AB", "\u03AB"}, {"\u03AC", "\u03AC"}, {"\u03AD", "\u03AD"}, {"\u03AE", "\u03AE"}, {"\u03AF", "\u03AF"}, {"\u03B0", "\u03B0"}, {"\u03CA", "\u03CA"}, {"\u03CB", "\u03CB"}, {"\u03CC", "\u03CC"}, {"\u03CD", "\u03CD"}, {"\u03CE", "\u03CE"}, {"\u03D0", "\u03D0"}, {"\u03D1", "\u03D1"}, {"\u03D2", "\u03D2"}, {"\u03D3", "\u03D3"}, {"\u03D4", "\u03D4"}, {"\u03D5", "\u03D5"}, {"\u03D6", "\u03D6"}, {"\u03F0", "\u03F0"}, {"\u03F1", "\u03F1"}, {"\u03F2", "\u03F2"}, {"\u03F4", "\u03F4"}, {"\u03F5", "\u03F5"}, {"\u03F9", "\u03F9"}, {"\u0400", "\u0400"}, {"\u0401", "\u0401"}, {"\u0403", "\u0403"}, {"\u0407", "\u0407"}, {"\u040C", "\u040C"}, {"\u040D", "\u040D"}, {"\u040E", "\u040E"}, {"\u0419", "\u0419"}, {"\u0439", "\u0439"}, {"\u0450", "\u0450"}, {"\u0451", "\u0451"}, {"\u0453", "\u0453"}, {"\u0457", "\u0457"}, {"\u045C", "\u045C"}, {"\u045D", "\u045D"}, {"\u045E", "\u045E"}, {"\u0476", "\u0476"}, {"\u0477", "\u0477"}, {"\u04C1", "\u04C1"}, {"\u04C2", "\u04C2"}, {"\u04D0", "\u04D0"}, {"\u04D1", "\u04D1"}, {"\u04D2", "\u04D2"}, {"\u04D3", "\u04D3"}, {"\u04D6", "\u04D6"}, {"\u04D7", "\u04D7"}, {"\u04DA", "\u04DA"}, {"\u04DB", "\u04DB"}, {"\u04DC", "\u04DC"}, {"\u04DD", "\u04DD"}, {"\u04DE", "\u04DE"}, {"\u04DF", "\u04DF"}, {"\u04E2", "\u04E2"}, {"\u04E3", "\u04E3"}, {"\u04E4", "\u04E4"}, {"\u04E5", "\u04E5"}, {"\u04E6", "\u04E6"}, {"\u04E7", "\u04E7"}, {"\u04EA", "\u04EA"}, {"\u04EB", "\u04EB"}, {"\u04EC", "\u04EC"}, {"\u04ED", "\u04ED"}, {"\u04EE", "\u04EE"}, {"\u04EF", "\u04EF"}, {"\u04F0", "\u04F0"}, {"\u04F1", "\u04F1"}, {"\u04F2", "\u04F2"}, {"\u04F3", "\u04F3"}, {"\u04F4", "\u04F4"}, {"\u04F5", "\u04F5"}, {"\u04F8", "\u04F8"}, {"\u04F9", "\u04F9"}, {"\u0587", "\u0587"}, {"\u0622", "\u0622"}, {"\u0623", "\u0623"}, {"\u0624", "\u0624"}, {"\u0625", "\u0625"}, {"\u0626", "\u0626"}, {"\u0675", "\u0675"}, {"\u0676", "\u0676"}, {"\u0677", "\u0677"}, {"\u0678", "\u0678"}, {"\u06C0", "\u06C0"}, {"\u06C2", "\u06C2"}, {"\u06D3", "\u06D3"}, {"\u0929", "\u0929"}, {"\u0931", "\u0931"}, {"\u0934", "\u0934"}, {"\u0958", "\u0915\u093C"}, {"\u0959", "\u0916\u093C"}, {"\u095A", "\u0917\u093C"}, {"\u095B", "\u091C\u093C"}, {"\u095C", "\u0921\u093C"}, {"\u095D", "\u0922\u093C"}, {"\u095E", "\u092B\u093C"}, {"\u095F", "\u092F\u093C"}, {"\u09CB", "\u09CB"}, {"\u09CC", "\u09CC"}, {"\u09DC", "\u09A1\u09BC"}, {"\u09DD", "\u09A2\u09BC"}, {"\u09DF", "\u09AF\u09BC"}, {"\u0A33", "\u0A32\u0A3C"}, {"\u0A36", "\u0A38\u0A3C"}, {"\u0A59", "\u0A16\u0A3C"}, {"\u0A5A", "\u0A17\u0A3C"}, {"\u0A5B", "\u0A1C\u0A3C"}, {"\u0A5E", "\u0A2B\u0A3C"}, {"\u0B48", "\u0B48"}, {"\u0B4B", "\u0B4B"}, {"\u0B4C", "\u0B4C"}, {"\u0B5C", "\u0B21\u0B3C"}, {"\u0B5D", "\u0B22\u0B3C"}, {"\u0B94", "\u0B94"}, {"\u0BCA", "\u0BCA"}, {"\u0BCB", "\u0BCB"}, {"\u0BCC", "\u0BCC"}, {"\u0C48", "\u0C48"}, {"\u0CC0", "\u0CC0"}, {"\u0CC7", "\u0CC7"}, {"\u0CC8", "\u0CC8"}, {"\u0CCA", "\u0CCA"}, {"\u0CCB", "\u0CCB"}, {"\u0D4A", "\u0D4A"}, {"\u0D4B", "\u0D4B"}, {"\u0D4C", "\u0D4C"}, {"\u0DDA", "\u0DDA"}, {"\u0DDC", "\u0DDC"}, {"\u0DDD", "\u0DDD"}, {"\u0DDE", "\u0DDE"}, {"\u0E33", "\u0E33"}, {"\u0EB3", "\u0EB3"}, {"\u0EDC", "\u0EDC"}, {"\u0EDD", "\u0EDD"}, {"\u0F0C", "\u0F0C"}, {"\u0F43", "\u0F42\u0FB7"}, {"\u0F4D", "\u0F4C\u0FB7"}, {"\u0F52", "\u0F51\u0FB7"}, {"\u0F57", "\u0F56\u0FB7"}, {"\u0F5C", "\u0F5B\u0FB7"}, {"\u0F69", "\u0F40\u0FB5"}, {"\u0F73", "\u0F71\u0F72"}, {"\u0F75", "\u0F71\u0F74"}, {"\u0F76", "\u0FB2\u0F80"}, {"\u0F77", "\u0F77"}, {"\u0F78", "\u0FB3\u0F80"}, {"\u0F79", "\u0F79"}, {"\u0F81", "\u0F71\u0F80"}, {"\u0F93", "\u0F92\u0FB7"}, {"\u0F9D", "\u0F9C\u0FB7"}, {"\u0FA2", "\u0FA1\u0FB7"}, {"\u0FA7", "\u0FA6\u0FB7"}, {"\u0FAC", "\u0FAB\u0FB7"}, {"\u0FB9", "\u0F90\u0FB5"}, {"\u1026", "\u1026"}, {"\u10FC", "\u10FC"}, {"\u1B06", "\u1B06"}, {"\u1B08", "\u1B08"}, {"\u1B0A", "\u1B0A"}, {"\u1B0C", "\u1B0C"}, {"\u1B0E", "\u1B0E"}, {"\u1B12", "\u1B12"}, {"\u1B3B", "\u1B3B"}, {"\u1B3D", "\u1B3D"}, {"\u1B40", "\u1B40"}, {"\u1B41", "\u1B41"}, {"\u1B43", "\u1B43"}, {"\u1D2C", "\u1D2C"}, {"\u1D2D", "\u1D2D"}, {"\u1D2E", "\u1D2E"}, {"\u1D30", "\u1D30"}, {"\u1D31", "\u1D31"}, {"\u1D32", "\u1D32"}, {"\u1D33", "\u1D33"}, {"\u1D34", "\u1D34"}, {"\u1D35", "\u1D35"}, {"\u1D36", "\u1D36"}, {"\u1D37", "\u1D37"}, {"\u1D38", "\u1D38"}, {"\u1D39", "\u1D39"}, {"\u1D3A", "\u1D3A"}, {"\u1D3C", "\u1D3C"}, {"\u1D3D", "\u1D3D"}, {"\u1D3E", "\u1D3E"}, {"\u1D3F", "\u1D3F"}, {"\u1D40", "\u1D40"}, {"\u1D41", "\u1D41"}, {"\u1D42", "\u1D42"}, {"\u1D43", "\u1D43"}, {"\u1D44", "\u1D44"}, {"\u1D45", "\u1D45"}, {"\u1D46", "\u1D46"}, {"\u1D47", "\u1D47"}, {"\u1D48", "\u1D48"}, {"\u1D49", "\u1D49"}, {"\u1D4A", "\u1D4A"}, {"\u1D4B", "\u1D4B"}, {"\u1D4C", "\u1D4C"}, {"\u1D4D", "\u1D4D"}, {"\u1D4F", "\u1D4F"}, {"\u1D50", "\u1D50"}, {"\u1D51", "\u1D51"}, {"\u1D52", "\u1D52"}, {"\u1D53", "\u1D53"}, {"\u1D54", "\u1D54"}, {"\u1D55", "\u1D55"}, {"\u1D56", "\u1D56"}, {"\u1D57", "\u1D57"}, {"\u1D58", "\u1D58"}, {"\u1D59", "\u1D59"}, {"\u1D5A", "\u1D5A"}, {"\u1D5B", "\u1D5B"}, {"\u1D5C", "\u1D5C"}, {"\u1D5D", "\u1D5D"}, {"\u1D5E", "\u1D5E"}, {"\u1D5F", "\u1D5F"}, {"\u1D60", "\u1D60"}, {"\u1D61", "\u1D61"}, {"\u1D62", "\u1D62"}, {"\u1D63", "\u1D63"}, {"\u1D64", "\u1D64"}, {"\u1D65", "\u1D65"}, {"\u1D66", "\u1D66"}, {"\u1D67", "\u1D67"}, {"\u1D68", "\u1D68"}, {"\u1D69", "\u1D69"}, {"\u1D6A", "\u1D6A"}, {"\u1D78", "\u1D78"}, {"\u1D9B", "\u1D9B"}, {"\u1D9C", "\u1D9C"}, {"\u1D9D", "\u1D9D"}, {"\u1D9E", "\u1D9E"}, {"\u1D9F", "\u1D9F"}, {"\u1DA0", "\u1DA0"}, {"\u1DA1", "\u1DA1"}, {"\u1DA2", "\u1DA2"}, {"\u1DA3", "\u1DA3"}, {"\u1DA4", "\u1DA4"}, {"\u1DA5", "\u1DA5"}, {"\u1DA6", "\u1DA6"}, {"\u1DA7", "\u1DA7"}, {"\u1DA8", "\u1DA8"}, {"\u1DA9", "\u1DA9"}, {"\u1DAA", "\u1DAA"}, {"\u1DAB", "\u1DAB"}, {"\u1DAC", "\u1DAC"}, {"\u1DAD", "\u1DAD"}, {"\u1DAE", "\u1DAE"}, {"\u1DAF", "\u1DAF"}, {"\u1DB0", "\u1DB0"}, {"\u1DB1", "\u1DB1"}, {"\u1DB2", "\u1DB2"}, {"\u1DB3", "\u1DB3"}, {"\u1DB4", "\u1DB4"}, {"\u1DB5", "\u1DB5"}, {"\u1DB6", "\u1DB6"}, {"\u1DB7", "\u1DB7"}, {"\u1DB8", "\u1DB8"}, {"\u1DB9", "\u1DB9"}, {"\u1DBA", "\u1DBA"}, {"\u1DBB", "\u1DBB"}, {"\u1DBC", "\u1DBC"}, {"\u1DBD", "\u1DBD"}, {"\u1DBE", "\u1DBE"}, {"\u1DBF", "\u1DBF"}, {"\u1E00", "\u1E00"}, {"\u1E01", "\u1E01"}, {"\u1E02", "\u1E02"}, {"\u1E03", "\u1E03"}, {"\u1E04", "\u1E04"}, {"\u1E05", "\u1E05"}, {"\u1E06", "\u1E06"}, {"\u1E07", "\u1E07"}, {"\u1E08", "\u1E08"}, {"\u1E09", "\u1E09"}, {"\u1E0A", "\u1E0A"}, {"\u1E0B", "\u1E0B"}, {"\u1E0C", "\u1E0C"}, {"\u1E0D", "\u1E0D"}, {"\u1E0E", "\u1E0E"}, {"\u1E0F", "\u1E0F"}, {"\u1E10", "\u1E10"}, {"\u1E11", "\u1E11"}, {"\u1E12", "\u1E12"}, {"\u1E13", "\u1E13"}, {"\u1E14", "\u1E14"}, {"\u1E15", "\u1E15"}, {"\u1E16", "\u1E16"}, {"\u1E17", "\u1E17"}, {"\u1E18", "\u1E18"}, {"\u1E19", "\u1E19"}, {"\u1E1A", "\u1E1A"}, {"\u1E1B", "\u1E1B"}, {"\u1E1C", "\u1E1C"}, {"\u1E1D", "\u1E1D"}, {"\u1E1E", "\u1E1E"}, {"\u1E1F", "\u1E1F"}, {"\u1E20", "\u1E20"}, {"\u1E21", "\u1E21"}, {"\u1E22", "\u1E22"}, {"\u1E23", "\u1E23"}, {"\u1E24", "\u1E24"}, {"\u1E25", "\u1E25"}, {"\u1E26", "\u1E26"}, {"\u1E27", "\u1E27"}, {"\u1E28", "\u1E28"}, {"\u1E29", "\u1E29"}, {"\u1E2A", "\u1E2A"}, {"\u1E2B", "\u1E2B"}, {"\u1E2C", "\u1E2C"}, {"\u1E2D", "\u1E2D"}, {"\u1E2E", "\u1E2E"}, {"\u1E2F", "\u1E2F"}, {"\u1E30", "\u1E30"}, {"\u1E31", "\u1E31"}, {"\u1E32", "\u1E32"}, {"\u1E33", "\u1E33"}, {"\u1E34", "\u1E34"}, {"\u1E35", "\u1E35"}, {"\u1E36", "\u1E36"}, {"\u1E37", "\u1E37"}, {"\u1E38", "\u1E38"}, {"\u1E39", "\u1E39"}, {"\u1E3A", "\u1E3A"}, {"\u1E3B", "\u1E3B"}, {"\u1E3C", "\u1E3C"}, {"\u1E3D", "\u1E3D"}, {"\u1E3E", "\u1E3E"}, {"\u1E3F", "\u1E3F"}, {"\u1E40", "\u1E40"}, {"\u1E41", "\u1E41"}, {"\u1E42", "\u1E42"}, {"\u1E43", "\u1E43"}, {"\u1E44", "\u1E44"}, {"\u1E45", "\u1E45"}, {"\u1E46", "\u1E46"}, {"\u1E47", "\u1E47"}, {"\u1E48", "\u1E48"}, {"\u1E49", "\u1E49"}, {"\u1E4A", "\u1E4A"}, {"\u1E4B", "\u1E4B"}, {"\u1E4C", "\u1E4C"}, {"\u1E4D", "\u1E4D"}, {"\u1E4E", "\u1E4E"}, {"\u1E4F", "\u1E4F"}, {"\u1E50", "\u1E50"}, {"\u1E51", "\u1E51"}, {"\u1E52", "\u1E52"}, {"\u1E53", "\u1E53"}, {"\u1E54", "\u1E54"}, {"\u1E55", "\u1E55"}, {"\u1E56", "\u1E56"}, {"\u1E57", "\u1E57"}, {"\u1E58", "\u1E58"}, {"\u1E59", "\u1E59"}, {"\u1E5A", "\u1E5A"}, {"\u1E5B", "\u1E5B"}, {"\u1E5C", "\u1E5C"}, {"\u1E5D", "\u1E5D"}, {"\u1E5E", "\u1E5E"}, {"\u1E5F", "\u1E5F"}, {"\u1E60", "\u1E60"}, {"\u1E61", "\u1E61"}, {"\u1E62", "\u1E62"}, {"\u1E63", "\u1E63"}, {"\u1E64", "\u1E64"}, {"\u1E65", "\u1E65"}, {"\u1E66", "\u1E66"}, {"\u1E67", "\u1E67"}, {"\u1E68", "\u1E68"}, {"\u1E69", "\u1E69"}, {"\u1E6A", "\u1E6A"}, {"\u1E6B", "\u1E6B"}, {"\u1E6C", "\u1E6C"}, {"\u1E6D", "\u1E6D"}, {"\u1E6E", "\u1E6E"}, {"\u1E6F", "\u1E6F"}, {"\u1E70", "\u1E70"}, {"\u1E71", "\u1E71"}, {"\u1E72", "\u1E72"}, {"\u1E73", "\u1E73"}, {"\u1E74", "\u1E74"}, {"\u1E75", "\u1E75"}, {"\u1E76", "\u1E76"}, {"\u1E77", "\u1E77"}, {"\u1E78", "\u1E78"}, {"\u1E79", "\u1E79"}, {"\u1E7A", "\u1E7A"}, {"\u1E7B", "\u1E7B"}, {"\u1E7C", "\u1E7C"}, {"\u1E7D", "\u1E7D"}, {"\u1E7E", "\u1E7E"}, {"\u1E7F", "\u1E7F"}, {"\u1E80", "\u1E80"}, {"\u1E81", "\u1E81"}, {"\u1E82", "\u1E82"}, {"\u1E83", "\u1E83"}, {"\u1E84", "\u1E84"}, {"\u1E85", "\u1E85"}, {"\u1E86", "\u1E86"}, {"\u1E87", "\u1E87"}, {"\u1E88", "\u1E88"}, {"\u1E89", "\u1E89"}, {"\u1E8A", "\u1E8A"}, {"\u1E8B", "\u1E8B"}, {"\u1E8C", "\u1E8C"}, {"\u1E8D", "\u1E8D"}, {"\u1E8E", "\u1E8E"}, {"\u1E8F", "\u1E8F"}, {"\u1E90", "\u1E90"}, {"\u1E91", "\u1E91"}, {"\u1E92", "\u1E92"}, {"\u1E93", "\u1E93"}, {"\u1E94", "\u1E94"}, {"\u1E95", "\u1E95"}, {"\u1E96", "\u1E96"}, {"\u1E97", "\u1E97"}, {"\u1E98", "\u1E98"}, {"\u1E99", "\u1E99"}, {"\u1E9A", "\u1E9A"}, {"\u1E9B", "\u1E9B"}, {"\u1EA0", "\u1EA0"}, {"\u1EA1", "\u1EA1"}, {"\u1EA2", "\u1EA2"}, {"\u1EA3", "\u1EA3"}, {"\u1EA4", "\u1EA4"}, {"\u1EA5", "\u1EA5"}, {"\u1EA6", "\u1EA6"}, {"\u1EA7", "\u1EA7"}, {"\u1EA8", "\u1EA8"}, {"\u1EA9", "\u1EA9"}, {"\u1EAA", "\u1EAA"}, {"\u1EAB", "\u1EAB"}, {"\u1EAC", "\u1EAC"}, {"\u1EAD", "\u1EAD"}, {"\u1EAE", "\u1EAE"}, {"\u1EAF", "\u1EAF"}, {"\u1EB0", "\u1EB0"}, {"\u1EB1", "\u1EB1"}, {"\u1EB2", "\u1EB2"}, {"\u1EB3", "\u1EB3"}, {"\u1EB4", "\u1EB4"}, {"\u1EB5", "\u1EB5"}, {"\u1EB6", "\u1EB6"}, {"\u1EB7", "\u1EB7"}, {"\u1EB8", "\u1EB8"}, {"\u1EB9", "\u1EB9"}, {"\u1EBA", "\u1EBA"}, {"\u1EBB", "\u1EBB"}, {"\u1EBC", "\u1EBC"}, {"\u1EBD", "\u1EBD"}, {"\u1EBE", "\u1EBE"}, {"\u1EBF", "\u1EBF"}, {"\u1EC0", "\u1EC0"}, {"\u1EC1", "\u1EC1"}, {"\u1EC2", "\u1EC2"}, {"\u1EC3", "\u1EC3"}, {"\u1EC4", "\u1EC4"}, {"\u1EC5", "\u1EC5"}, {"\u1EC6", "\u1EC6"}, {"\u1EC7", "\u1EC7"}, {"\u1EC8", "\u1EC8"}, {"\u1EC9", "\u1EC9"}, {"\u1ECA", "\u1ECA"}, {"\u1ECB", "\u1ECB"}, {"\u1ECC", "\u1ECC"}, {"\u1ECD", "\u1ECD"}, {"\u1ECE", "\u1ECE"}, {"\u1ECF", "\u1ECF"}, {"\u1ED0", "\u1ED0"}, {"\u1ED1", "\u1ED1"}, {"\u1ED2", "\u1ED2"}, {"\u1ED3", "\u1ED3"}, {"\u1ED4", "\u1ED4"}, {"\u1ED5", "\u1ED5"}, {"\u1ED6", "\u1ED6"}, {"\u1ED7", "\u1ED7"}, {"\u1ED8", "\u1ED8"}, {"\u1ED9", "\u1ED9"}, {"\u1EDA", "\u1EDA"}, {"\u1EDB", "\u1EDB"}, {"\u1EDC", "\u1EDC"}, {"\u1EDD", "\u1EDD"}, {"\u1EDE", "\u1EDE"}, {"\u1EDF", "\u1EDF"}, {"\u1EE0", "\u1EE0"}, {"\u1EE1", "\u1EE1"}, {"\u1EE2", "\u1EE2"}, {"\u1EE3", "\u1EE3"}, {"\u1EE4", "\u1EE4"}, {"\u1EE5", "\u1EE5"}, {"\u1EE6", "\u1EE6"}, {"\u1EE7", "\u1EE7"}, {"\u1EE8", "\u1EE8"}, {"\u1EE9", "\u1EE9"}, {"\u1EEA", "\u1EEA"}, {"\u1EEB", "\u1EEB"}, {"\u1EEC", "\u1EEC"}, {"\u1EED", "\u1EED"}, {"\u1EEE", "\u1EEE"}, {"\u1EEF", "\u1EEF"}, {"\u1EF0", "\u1EF0"}, {"\u1EF1", "\u1EF1"}, {"\u1EF2", "\u1EF2"}, {"\u1EF3", "\u1EF3"}, {"\u1EF4", "\u1EF4"}, {"\u1EF5", "\u1EF5"}, {"\u1EF6", "\u1EF6"}, {"\u1EF7", "\u1EF7"}, {"\u1EF8", "\u1EF8"}, {"\u1EF9", "\u1EF9"}, {"\u1F00", "\u1F00"}, {"\u1F01", "\u1F01"}, {"\u1F02", "\u1F02"}, {"\u1F03", "\u1F03"}, {"\u1F04", "\u1F04"}, {"\u1F05", "\u1F05"}, {"\u1F06", "\u1F06"}, {"\u1F07", "\u1F07"}, {"\u1F08", "\u1F08"}, {"\u1F09", "\u1F09"}, {"\u1F0A", "\u1F0A"}, {"\u1F0B", "\u1F0B"}, {"\u1F0C", "\u1F0C"}, {"\u1F0D", "\u1F0D"}, {"\u1F0E", "\u1F0E"}, {"\u1F0F", "\u1F0F"}, {"\u1F10", "\u1F10"}, {"\u1F11", "\u1F11"}, {"\u1F12", "\u1F12"}, {"\u1F13", "\u1F13"}, {"\u1F14", "\u1F14"}, {"\u1F15", "\u1F15"}, {"\u1F18", "\u1F18"}, {"\u1F19", "\u1F19"}, {"\u1F1A", "\u1F1A"}, {"\u1F1B", "\u1F1B"}, {"\u1F1C", "\u1F1C"}, {"\u1F1D", "\u1F1D"}, {"\u1F20", "\u1F20"}, {"\u1F21", "\u1F21"}, {"\u1F22", "\u1F22"}, {"\u1F23", "\u1F23"}, {"\u1F24", "\u1F24"}, {"\u1F25", "\u1F25"}, {"\u1F26", "\u1F26"}, {"\u1F27", "\u1F27"}, {"\u1F28", "\u1F28"}, {"\u1F29", "\u1F29"}, {"\u1F2A", "\u1F2A"}, {"\u1F2B", "\u1F2B"}, {"\u1F2C", "\u1F2C"}, {"\u1F2D", "\u1F2D"}, {"\u1F2E", "\u1F2E"}, {"\u1F2F", "\u1F2F"}, {"\u1F30", "\u1F30"}, {"\u1F31", "\u1F31"}, {"\u1F32", "\u1F32"}, {"\u1F33", "\u1F33"}, {"\u1F34", "\u1F34"}, {"\u1F35", "\u1F35"}, {"\u1F36", "\u1F36"}, {"\u1F37", "\u1F37"}, {"\u1F38", "\u1F38"}, {"\u1F39", "\u1F39"}, {"\u1F3A", "\u1F3A"}, {"\u1F3B", "\u1F3B"}, {"\u1F3C", "\u1F3C"}, {"\u1F3D", "\u1F3D"}, {"\u1F3E", "\u1F3E"}, {"\u1F3F", "\u1F3F"}, {"\u1F40", "\u1F40"}, {"\u1F41", "\u1F41"}, {"\u1F42", "\u1F42"}, {"\u1F43", "\u1F43"}, {"\u1F44", "\u1F44"}, {"\u1F45", "\u1F45"}, {"\u1F48", "\u1F48"}, {"\u1F49", "\u1F49"}, {"\u1F4A", "\u1F4A"}, {"\u1F4B", "\u1F4B"}, {"\u1F4C", "\u1F4C"}, {"\u1F4D", "\u1F4D"}, {"\u1F50", "\u1F50"}, {"\u1F51", "\u1F51"}, {"\u1F52", "\u1F52"}, {"\u1F53", "\u1F53"}, {"\u1F54", "\u1F54"}, {"\u1F55", "\u1F55"}, {"\u1F56", "\u1F56"}, {"\u1F57", "\u1F57"}, {"\u1F59", "\u1F59"}, {"\u1F5B", "\u1F5B"}, {"\u1F5D", "\u1F5D"}, {"\u1F5F", "\u1F5F"}, {"\u1F60", "\u1F60"}, {"\u1F61", "\u1F61"}, {"\u1F62", "\u1F62"}, {"\u1F63", "\u1F63"}, {"\u1F64", "\u1F64"}, {"\u1F65", "\u1F65"}, {"\u1F66", "\u1F66"}, {"\u1F67", "\u1F67"}, {"\u1F68", "\u1F68"}, {"\u1F69", "\u1F69"}, {"\u1F6A", "\u1F6A"}, {"\u1F6B", "\u1F6B"}, {"\u1F6C", "\u1F6C"}, {"\u1F6D", "\u1F6D"}, {"\u1F6E", "\u1F6E"}, {"\u1F6F", "\u1F6F"}, {"\u1F70", "\u1F70"}, {"\u1F71", "\u03AC"}, {"\u1F72", "\u1F72"}, {"\u1F73", "\u03AD"}, {"\u1F74", "\u1F74"}, {"\u1F75", "\u03AE"}, {"\u1F76", "\u1F76"}, {"\u1F77", "\u03AF"}, {"\u1F78", "\u1F78"}, {"\u1F79", "\u03CC"}, {"\u1F7A", "\u1F7A"}, {"\u1F7B", "\u03CD"}, {"\u1F7C", "\u1F7C"}, {"\u1F7D", "\u03CE"}, {"\u1F80", "\u1F80"}, {"\u1F81", "\u1F81"}, {"\u1F82", "\u1F82"}, {"\u1F83", "\u1F83"}, {"\u1F84", "\u1F84"}, {"\u1F85", "\u1F85"}, {"\u1F86", "\u1F86"}, {"\u1F87", "\u1F87"}, {"\u1F88", "\u1F88"}, {"\u1F89", "\u1F89"}, {"\u1F8A", "\u1F8A"}, {"\u1F8B", "\u1F8B"}, {"\u1F8C", "\u1F8C"}, {"\u1F8D", "\u1F8D"}, {"\u1F8E", "\u1F8E"}, {"\u1F8F", "\u1F8F"}, {"\u1F90", "\u1F90"}, {"\u1F91", "\u1F91"}, {"\u1F92", "\u1F92"}, {"\u1F93", "\u1F93"}, {"\u1F94", "\u1F94"}, {"\u1F95", "\u1F95"}, {"\u1F96", "\u1F96"}, {"\u1F97", "\u1F97"}, {"\u1F98", "\u1F98"}, {"\u1F99", "\u1F99"}, {"\u1F9A", "\u1F9A"}, {"\u1F9B", "\u1F9B"}, {"\u1F9C", "\u1F9C"}, {"\u1F9D", "\u1F9D"}, {"\u1F9E", "\u1F9E"}, {"\u1F9F", "\u1F9F"}, {"\u1FA0", "\u1FA0"}, {"\u1FA1", "\u1FA1"}, {"\u1FA2", "\u1FA2"}, {"\u1FA3", "\u1FA3"}, {"\u1FA4", "\u1FA4"}, {"\u1FA5", "\u1FA5"}, {"\u1FA6", "\u1FA6"}, {"\u1FA7", "\u1FA7"}, {"\u1FA8", "\u1FA8"}, {"\u1FA9", "\u1FA9"}, {"\u1FAA", "\u1FAA"}, {"\u1FAB", "\u1FAB"}, {"\u1FAC", "\u1FAC"}, {"\u1FAD", "\u1FAD"}, {"\u1FAE", "\u1FAE"}, {"\u1FAF", "\u1FAF"}, {"\u1FB0", "\u1FB0"}, {"\u1FB1", "\u1FB1"}, {"\u1FB2", "\u1FB2"}, {"\u1FB3", "\u1FB3"}, {"\u1FB4", "\u1FB4"}, {"\u1FB6", "\u1FB6"}, {"\u1FB7", "\u1FB7"}, {"\u1FB8", "\u1FB8"}, {"\u1FB9", "\u1FB9"}, {"\u1FBA", "\u1FBA"}, {"\u1FBB", "\u0386"}, {"\u1FBC", "\u1FBC"}, {"\u1FBD", "\u1FBD"}, {"\u1FBE", "\u03B9"}, {"\u1FBF", "\u1FBF"}, {"\u1FC0", "\u1FC0"}, {"\u1FC1", "\u1FC1"}, {"\u1FC2", "\u1FC2"}, {"\u1FC3", "\u1FC3"}, {"\u1FC4", "\u1FC4"}, {"\u1FC6", "\u1FC6"}, {"\u1FC7", "\u1FC7"}, {"\u1FC8", "\u1FC8"}, {"\u1FC9", "\u0388"}, {"\u1FCA", "\u1FCA"}, {"\u1FCB", "\u0389"}, {"\u1FCC", "\u1FCC"}, {"\u1FCD", "\u1FCD"}, {"\u1FCE", "\u1FCE"}, {"\u1FCF", "\u1FCF"}, {"\u1FD0", "\u1FD0"}, {"\u1FD1", "\u1FD1"}, {"\u1FD2", "\u1FD2"}, {"\u1FD3", "\u0390"}, {"\u1FD6", "\u1FD6"}, {"\u1FD7", "\u1FD7"}, {"\u1FD8", "\u1FD8"}, {"\u1FD9", "\u1FD9"}, {"\u1FDA", "\u1FDA"}, {"\u1FDB", "\u038A"}, {"\u1FDD", "\u1FDD"}, {"\u1FDE", "\u1FDE"}, {"\u1FDF", "\u1FDF"}, {"\u1FE0", "\u1FE0"}, {"\u1FE1", "\u1FE1"}, {"\u1FE2", "\u1FE2"}, {"\u1FE3", "\u03B0"}, {"\u1FE4", "\u1FE4"}, {"\u1FE5", "\u1FE5"}, {"\u1FE6", "\u1FE6"}, {"\u1FE7", "\u1FE7"}, {"\u1FE8", "\u1FE8"}, {"\u1FE9", "\u1FE9"}, {"\u1FEA", "\u1FEA"}, {"\u1FEB", "\u038E"}, {"\u1FEC", "\u1FEC"}, {"\u1FED", "\u1FED"}, {"\u1FEE", "\u0385"}, {"\u1FEF", "\u0060"}, {"\u1FF2", "\u1FF2"}, {"\u1FF3", "\u1FF3"}, {"\u1FF4", "\u1FF4"}, {"\u1FF6", "\u1FF6"}, {"\u1FF7", "\u1FF7"}, {"\u1FF8", "\u1FF8"}, {"\u1FF9", "\u038C"}, {"\u1FFA", "\u1FFA"}, {"\u1FFB", "\u038F"}, {"\u1FFC", "\u1FFC"}, {"\u1FFD", "\u00B4"}, {"\u1FFE", "\u1FFE"}, {"\u2000", "\u2002"}, {"\u2001", "\u2003"}, {"\u2002", "\u2002"}, {"\u2003", "\u2003"}, {"\u2004", "\u2004"}, {"\u2005", "\u2005"}, {"\u2006", "\u2006"}, {"\u2007", "\u2007"}, {"\u2008", "\u2008"}, {"\u2009", "\u2009"}, {"\u200A", "\u200A"}, {"\u2011", "\u2011"}, {"\u2017", "\u2017"}, {"\u2024", "\u2024"}, {"\u2025", "\u2025"}, {"\u2026", "\u2026"}, {"\u202F", "\u202F"}, {"\u2033", "\u2033"}, {"\u2034", "\u2034"}, {"\u2036", "\u2036"}, {"\u2037", "\u2037"}, {"\u203C", "\u203C"}, {"\u203E", "\u203E"}, {"\u2047", "\u2047"}, {"\u2048", "\u2048"}, {"\u2049", "\u2049"}, {"\u2057", "\u2057"}, {"\u205F", "\u205F"}, {"\u2070", "\u2070"}, {"\u2071", "\u2071"}, {"\u2074", "\u2074"}, {"\u2075", "\u2075"}, {"\u2076", "\u2076"}, {"\u2077", "\u2077"}, {"\u2078", "\u2078"}, {"\u2079", "\u2079"}, {"\u207A", "\u207A"}, {"\u207B", "\u207B"}, {"\u207C", "\u207C"}, {"\u207D", "\u207D"}, {"\u207E", "\u207E"}, {"\u207F", "\u207F"}, {"\u2080", "\u2080"}, {"\u2081", "\u2081"}, {"\u2082", "\u2082"}, {"\u2083", "\u2083"}, {"\u2084", "\u2084"}, {"\u2085", "\u2085"}, {"\u2086", "\u2086"}, {"\u2087", "\u2087"}, {"\u2088", "\u2088"}, {"\u2089", "\u2089"}, {"\u208A", "\u208A"}, {"\u208B", "\u208B"}, {"\u208C", "\u208C"}, {"\u208D", "\u208D"}, {"\u208E", "\u208E"}, {"\u2090", "\u2090"}, {"\u2091", "\u2091"}, {"\u2092", "\u2092"}, {"\u2093", "\u2093"}, {"\u2094", "\u2094"}, {"\u2095", "\u2095"}, {"\u2096", "\u2096"}, {"\u2097", "\u2097"}, {"\u2098", "\u2098"}, {"\u2099", "\u2099"}, {"\u209A", "\u209A"}, {"\u209B", "\u209B"}, {"\u209C", "\u209C"}, {"\u20A8", "\u20A8"}, {"\u2100", "\u2100"}, {"\u2101", "\u2101"}, {"\u2102", "\u2102"}, {"\u2103", "\u2103"}, {"\u2105", "\u2105"}, {"\u2106", "\u2106"}, {"\u2107", "\u2107"}, {"\u2109", "\u2109"}, {"\u210A", "\u210A"}, {"\u210B", "\u210B"}, {"\u210C", "\u210C"}, {"\u210D", "\u210D"}, {"\u210E", "\u210E"}, {"\u210F", "\u210F"}, {"\u2110", "\u2110"}, {"\u2111", "\u2111"}, {"\u2112", "\u2112"}, {"\u2113", "\u2113"}, {"\u2115", "\u2115"}, {"\u2116", "\u2116"}, {"\u2119", "\u2119"}, {"\u211A", "\u211A"}, {"\u211B", "\u211B"}, {"\u211C", "\u211C"}, {"\u211D", "\u211D"}, {"\u2120", "\u2120"}, {"\u2121", "\u2121"}, {"\u2122", "\u2122"}, {"\u2124", "\u2124"}, {"\u2126", "\u03A9"}, {"\u2128", "\u2128"}, {"\u212A", "\u004B"}, {"\u212B", "\u00C5"}, {"\u212C", "\u212C"}, {"\u212D", "\u212D"}, {"\u212F", "\u212F"}, {"\u2130", "\u2130"}, {"\u2131", "\u2131"}, {"\u2133", "\u2133"}, {"\u2134", "\u2134"}, {"\u2135", "\u2135"}, {"\u2136", "\u2136"}, {"\u2137", "\u2137"}, {"\u2138", "\u2138"}, {"\u2139", "\u2139"}, {"\u213B", "\u213B"}, {"\u213C", "\u213C"}, {"\u213D", "\u213D"}, {"\u213E", "\u213E"}, {"\u213F", "\u213F"}, {"\u2140", "\u2140"}, {"\u2145", "\u2145"}, {"\u2146", "\u2146"}, {"\u2147", "\u2147"}, {"\u2148", "\u2148"}, {"\u2149", "\u2149"}, {"\u2150", "\u2150"}, {"\u2151", "\u2151"}, {"\u2152", "\u2152"}, {"\u2153", "\u2153"}, {"\u2154", "\u2154"}, {"\u2155", "\u2155"}, {"\u2156", "\u2156"}, {"\u2157", "\u2157"}, {"\u2158", "\u2158"}, {"\u2159", "\u2159"}, {"\u215A", "\u215A"}, {"\u215B", "\u215B"}, {"\u215C", "\u215C"}, {"\u215D", "\u215D"}, {"\u215E", "\u215E"}, {"\u215F", "\u215F"}, {"\u2160", "\u2160"}, {"\u2161", "\u2161"}, {"\u2162", "\u2162"}, {"\u2163", "\u2163"}, {"\u2164", "\u2164"}, {"\u2165", "\u2165"}, {"\u2166", "\u2166"}, {"\u2167", "\u2167"}, {"\u2168", "\u2168"}, {"\u2169", "\u2169"}, {"\u216A", "\u216A"}, {"\u216B", "\u216B"}, {"\u216C", "\u216C"}, {"\u216D", "\u216D"}, {"\u216E", "\u216E"}, {"\u216F", "\u216F"}, {"\u2170", "\u2170"}, {"\u2171", "\u2171"}, {"\u2172", "\u2172"}, {"\u2173", "\u2173"}, {"\u2174", "\u2174"}, {"\u2175", "\u2175"}, {"\u2176", "\u2176"}, {"\u2177", "\u2177"}, {"\u2178", "\u2178"}, {"\u2179", "\u2179"}, {"\u217A", "\u217A"}, {"\u217B", "\u217B"}, {"\u217C", "\u217C"}, {"\u217D", "\u217D"}, {"\u217E", "\u217E"}, {"\u217F", "\u217F"}, {"\u2189", "\u2189"}, {"\u219A", "\u219A"}, {"\u219B", "\u219B"}, {"\u21AE", "\u21AE"}, {"\u21CD", "\u21CD"}, {"\u21CE", "\u21CE"}, {"\u21CF", "\u21CF"}, {"\u2204", "\u2204"}, {"\u2209", "\u2209"}, {"\u220C", "\u220C"}, {"\u2224", "\u2224"}, {"\u2226", "\u2226"}, {"\u222C", "\u222C"}, {"\u222D", "\u222D"}, {"\u222F", "\u222F"}, {"\u2230", "\u2230"}, {"\u2241", "\u2241"}, {"\u2244", "\u2244"}, {"\u2247", "\u2247"}, {"\u2249", "\u2249"}, {"\u2260", "\u2260"}, {"\u2262", "\u2262"}, {"\u226D", "\u226D"}, {"\u226E", "\u226E"}, {"\u226F", "\u226F"}, {"\u2270", "\u2270"}, {"\u2271", "\u2271"}, {"\u2274", "\u2274"}, {"\u2275", "\u2275"}, {"\u2278", "\u2278"}, {"\u2279", "\u2279"}, {"\u2280", "\u2280"}, {"\u2281", "\u2281"}, {"\u2284", "\u2284"}, {"\u2285", "\u2285"}, {"\u2288", "\u2288"}, {"\u2289", "\u2289"}, {"\u22AC", "\u22AC"}, {"\u22AD", "\u22AD"}, {"\u22AE", "\u22AE"}, {"\u22AF", "\u22AF"}, {"\u22E0", "\u22E0"}, {"\u22E1", "\u22E1"}, {"\u22E2", "\u22E2"}, {"\u22E3", "\u22E3"}, {"\u22EA", "\u22EA"}, {"\u22EB", "\u22EB"}, {"\u22EC", "\u22EC"}, {"\u22ED", "\u22ED"}, {"\u2329", "\u3008"}, {"\u232A", "\u3009"}, {"\u2460", "\u2460"}, {"\u2461", "\u2461"}, {"\u2462", "\u2462"}, {"\u2463", "\u2463"}, {"\u2464", "\u2464"}, {"\u2465", "\u2465"}, {"\u2466", "\u2466"}, {"\u2467", "\u2467"}, {"\u2468", "\u2468"}, {"\u2469", "\u2469"}, {"\u246A", "\u246A"}, {"\u246B", "\u246B"}, {"\u246C", "\u246C"}, {"\u246D", "\u246D"}, {"\u246E", "\u246E"}, {"\u246F", "\u246F"}, {"\u2470", "\u2470"}, {"\u2471", "\u2471"}, {"\u2472", "\u2472"}, {"\u2473", "\u2473"}, {"\u2474", "\u2474"}, {"\u2475", "\u2475"}, {"\u2476", "\u2476"}, {"\u2477", "\u2477"}, {"\u2478", "\u2478"}, {"\u2479", "\u2479"}, {"\u247A", "\u247A"}, {"\u247B", "\u247B"}, {"\u247C", "\u247C"}, {"\u247D", "\u247D"}, {"\u247E", "\u247E"}, {"\u247F", "\u247F"}, {"\u2480", "\u2480"}, {"\u2481", "\u2481"}, {"\u2482", "\u2482"}, {"\u2483", "\u2483"}, {"\u2484", "\u2484"}, {"\u2485", "\u2485"}, {"\u2486", "\u2486"}, {"\u2487", "\u2487"}, {"\u2488", "\u2488"}, {"\u2489", "\u2489"}, {"\u248A", "\u248A"}, {"\u248B", "\u248B"}, {"\u248C", "\u248C"}, {"\u248D", "\u248D"}, {"\u248E", "\u248E"}, {"\u248F", "\u248F"}, {"\u2490", "\u2490"}, {"\u2491", "\u2491"}, {"\u2492", "\u2492"}, {"\u2493", "\u2493"}, {"\u2494", "\u2494"}, {"\u2495", "\u2495"}, {"\u2496", "\u2496"}, {"\u2497", "\u2497"}, {"\u2498", "\u2498"}, {"\u2499", "\u2499"}, {"\u249A", "\u249A"}, {"\u249B", "\u249B"}, {"\u249C", "\u249C"}, {"\u249D", "\u249D"}, {"\u249E", "\u249E"}, {"\u249F", "\u249F"}, {"\u24A0", "\u24A0"}, {"\u24A1", "\u24A1"}, {"\u24A2", "\u24A2"}, {"\u24A3", "\u24A3"}, {"\u24A4", "\u24A4"}, {"\u24A5", "\u24A5"}, {"\u24A6", "\u24A6"}, {"\u24A7", "\u24A7"}, {"\u24A8", "\u24A8"}, {"\u24A9", "\u24A9"}, {"\u24AA", "\u24AA"}, {"\u24AB", "\u24AB"}, {"\u24AC", "\u24AC"}, {"\u24AD", "\u24AD"}, {"\u24AE", "\u24AE"}, {"\u24AF", "\u24AF"}, {"\u24B0", "\u24B0"}, {"\u24B1", "\u24B1"}, {"\u24B2", "\u24B2"}, {"\u24B3", "\u24B3"}, {"\u24B4", "\u24B4"}, {"\u24B5", "\u24B5"}, {"\u24B6", "\u24B6"}, {"\u24B7", "\u24B7"}, {"\u24B8", "\u24B8"}, {"\u24B9", "\u24B9"}, {"\u24BA", "\u24BA"}, {"\u24BB", "\u24BB"}, {"\u24BC", "\u24BC"}, {"\u24BD", "\u24BD"}, {"\u24BE", "\u24BE"}, {"\u24BF", "\u24BF"}, {"\u24C0", "\u24C0"}, {"\u24C1", "\u24C1"}, {"\u24C2", "\u24C2"}, {"\u24C3", "\u24C3"}, {"\u24C4", "\u24C4"}, {"\u24C5", "\u24C5"}, {"\u24C6", "\u24C6"}, {"\u24C7", "\u24C7"}, {"\u24C8", "\u24C8"}, {"\u24C9", "\u24C9"}, {"\u24CA", "\u24CA"}, {"\u24CB", "\u24CB"}, {"\u24CC", "\u24CC"}, {"\u24CD", "\u24CD"}, {"\u24CE", "\u24CE"}, {"\u24CF", "\u24CF"}, {"\u24D0", "\u24D0"}, {"\u24D1", "\u24D1"}, {"\u24D2", "\u24D2"}, {"\u24D3", "\u24D3"}, {"\u24D4", "\u24D4"}, {"\u24D5", "\u24D5"}, {"\u24D6", "\u24D6"}, {"\u24D7", "\u24D7"}, {"\u24D8", "\u24D8"}, {"\u24D9", "\u24D9"}, {"\u24DA", "\u24DA"}, {"\u24DB", "\u24DB"}, {"\u24DC", "\u24DC"}, {"\u24DD", "\u24DD"}, {"\u24DE", "\u24DE"}, {"\u24DF", "\u24DF"}, {"\u24E0", "\u24E0"}, {"\u24E1", "\u24E1"}, {"\u24E2", "\u24E2"}, {"\u24E3", "\u24E3"}, {"\u24E4", "\u24E4"}, {"\u24E5", "\u24E5"}, {"\u24E6", "\u24E6"}, {"\u24E7", "\u24E7"}, {"\u24E8", "\u24E8"}, {"\u24E9", "\u24E9"}, {"\u24EA", "\u24EA"}, {"\u2A0C", "\u2A0C"}, {"\u2A74", "\u2A74"}, {"\u2A75", "\u2A75"}, {"\u2A76", "\u2A76"}, {"\u2ADC", "\u2ADD\u0338"}, {"\u2C7C", "\u2C7C"}, {"\u2C7D", "\u2C7D"}, {"\u2D6F", "\u2D6F"}, {"\u2E9F", "\u2E9F"}, {"\u2EF3", "\u2EF3"}, {"\u2F00", "\u2F00"}, {"\u2F01", "\u2F01"}, {"\u2F02", "\u2F02"}, {"\u2F03", "\u2F03"}, {"\u2F04", "\u2F04"}, {"\u2F05", "\u2F05"}, {"\u2F06", "\u2F06"}, {"\u2F07", "\u2F07"}, {"\u2F08", "\u2F08"}, {"\u2F09", "\u2F09"}, {"\u2F0A", "\u2F0A"}, {"\u2F0B", "\u2F0B"}, {"\u2F0C", "\u2F0C"}, {"\u2F0D", "\u2F0D"}, {"\u2F0E", "\u2F0E"}, {"\u2F0F", "\u2F0F"}, {"\u2F10", "\u2F10"}, {"\u2F11", "\u2F11"}, {"\u2F12", "\u2F12"}, {"\u2F13", "\u2F13"}, {"\u2F14", "\u2F14"}, {"\u2F15", "\u2F15"}, {"\u2F16", "\u2F16"}, {"\u2F17", "\u2F17"}, {"\u2F18", "\u2F18"}, {"\u2F19", "\u2F19"}, {"\u2F1A", "\u2F1A"}, {"\u2F1B", "\u2F1B"}, {"\u2F1C", "\u2F1C"}, {"\u2F1D", "\u2F1D"}, {"\u2F1E", "\u2F1E"}, {"\u2F1F", "\u2F1F"}, {"\u2F20", "\u2F20"}, {"\u2F21", "\u2F21"}, {"\u2F22", "\u2F22"}, {"\u2F23", "\u2F23"}, {"\u2F24", "\u2F24"}, {"\u2F25", "\u2F25"}, {"\u2F26", "\u2F26"}, {"\u2F27", "\u2F27"}, {"\u2F28", "\u2F28"}, {"\u2F29", "\u2F29"}, {"\u2F2A", "\u2F2A"}, {"\u2F2B", "\u2F2B"}, {"\u2F2C", "\u2F2C"}, {"\u2F2D", "\u2F2D"}, {"\u2F2E", "\u2F2E"}, {"\u2F2F", "\u2F2F"}, {"\u2F30", "\u2F30"}, {"\u2F31", "\u2F31"}, {"\u2F32", "\u2F32"}, {"\u2F33", "\u2F33"}, {"\u2F34", "\u2F34"}, {"\u2F35", "\u2F35"}, {"\u2F36", "\u2F36"}, {"\u2F37", "\u2F37"}, {"\u2F38", "\u2F38"}, {"\u2F39", "\u2F39"}, {"\u2F3A", "\u2F3A"}, {"\u2F3B", "\u2F3B"}, {"\u2F3C", "\u2F3C"}, {"\u2F3D", "\u2F3D"}, {"\u2F3E", "\u2F3E"}, {"\u2F3F", "\u2F3F"}, {"\u2F40", "\u2F40"}, {"\u2F41", "\u2F41"}, {"\u2F42", "\u2F42"}, {"\u2F43", "\u2F43"}, {"\u2F44", "\u2F44"}, {"\u2F45", "\u2F45"}, {"\u2F46", "\u2F46"}, {"\u2F47", "\u2F47"}, {"\u2F48", "\u2F48"}, {"\u2F49", "\u2F49"}, {"\u2F4A", "\u2F4A"}, {"\u2F4B", "\u2F4B"}, {"\u2F4C", "\u2F4C"}, {"\u2F4D", "\u2F4D"}, {"\u2F4E", "\u2F4E"}, {"\u2F4F", "\u2F4F"}, {"\u2F50", "\u2F50"}, {"\u2F51", "\u2F51"}, {"\u2F52", "\u2F52"}, {"\u2F53", "\u2F53"}, {"\u2F54", "\u2F54"}, {"\u2F55", "\u2F55"}, {"\u2F56", "\u2F56"}, {"\u2F57", "\u2F57"}, {"\u2F58", "\u2F58"}, {"\u2F59", "\u2F59"}, {"\u2F5A", "\u2F5A"}, {"\u2F5B", "\u2F5B"}, {"\u2F5C", "\u2F5C"}, {"\u2F5D", "\u2F5D"}, {"\u2F5E", "\u2F5E"}, {"\u2F5F", "\u2F5F"}, {"\u2F60", "\u2F60"}, {"\u2F61", "\u2F61"}, {"\u2F62", "\u2F62"}, {"\u2F63", "\u2F63"}, {"\u2F64", "\u2F64"}, {"\u2F65", "\u2F65"}, {"\u2F66", "\u2F66"}, {"\u2F67", "\u2F67"}, {"\u2F68", "\u2F68"}, {"\u2F69", "\u2F69"}, {"\u2F6A", "\u2F6A"}, {"\u2F6B", "\u2F6B"}, {"\u2F6C", "\u2F6C"}, {"\u2F6D", "\u2F6D"}, {"\u2F6E", "\u2F6E"}, {"\u2F6F", "\u2F6F"}, {"\u2F70", "\u2F70"}, {"\u2F71", "\u2F71"}, {"\u2F72", "\u2F72"}, {"\u2F73", "\u2F73"}, {"\u2F74", "\u2F74"}, {"\u2F75", "\u2F75"}, {"\u2F76", "\u2F76"}, {"\u2F77", "\u2F77"}, {"\u2F78", "\u2F78"}, {"\u2F79", "\u2F79"}, {"\u2F7A", "\u2F7A"}, {"\u2F7B", "\u2F7B"}, {"\u2F7C", "\u2F7C"}, {"\u2F7D", "\u2F7D"}, {"\u2F7E", "\u2F7E"}, {"\u2F7F", "\u2F7F"}, {"\u2F80", "\u2F80"}, {"\u2F81", "\u2F81"}, {"\u2F82", "\u2F82"}, {"\u2F83", "\u2F83"}, {"\u2F84", "\u2F84"}, {"\u2F85", "\u2F85"}, {"\u2F86", "\u2F86"}, {"\u2F87", "\u2F87"}, {"\u2F88", "\u2F88"}, {"\u2F89", "\u2F89"}, {"\u2F8A", "\u2F8A"}, {"\u2F8B", "\u2F8B"}, {"\u2F8C", "\u2F8C"}, {"\u2F8D", "\u2F8D"}, {"\u2F8E", "\u2F8E"}, {"\u2F8F", "\u2F8F"}, {"\u2F90", "\u2F90"}, {"\u2F91", "\u2F91"}, {"\u2F92", "\u2F92"}, {"\u2F93", "\u2F93"}, {"\u2F94", "\u2F94"}, {"\u2F95", "\u2F95"}, {"\u2F96", "\u2F96"}, {"\u2F97", "\u2F97"}, {"\u2F98", "\u2F98"}, {"\u2F99", "\u2F99"}, {"\u2F9A", "\u2F9A"}, {"\u2F9B", "\u2F9B"}, {"\u2F9C", "\u2F9C"}, {"\u2F9D", "\u2F9D"}, {"\u2F9E", "\u2F9E"}, {"\u2F9F", "\u2F9F"}, {"\u2FA0", "\u2FA0"}, {"\u2FA1", "\u2FA1"}, {"\u2FA2", "\u2FA2"}, {"\u2FA3", "\u2FA3"}, {"\u2FA4", "\u2FA4"}, {"\u2FA5", "\u2FA5"}, {"\u2FA6", "\u2FA6"}, {"\u2FA7", "\u2FA7"}, {"\u2FA8", "\u2FA8"}, {"\u2FA9", "\u2FA9"}, {"\u2FAA", "\u2FAA"}, {"\u2FAB", "\u2FAB"}, {"\u2FAC", "\u2FAC"}, {"\u2FAD", "\u2FAD"}, {"\u2FAE", "\u2FAE"}, {"\u2FAF", "\u2FAF"}, {"\u2FB0", "\u2FB0"}, {"\u2FB1", "\u2FB1"}, {"\u2FB2", "\u2FB2"}, {"\u2FB3", "\u2FB3"}, {"\u2FB4", "\u2FB4"}, {"\u2FB5", "\u2FB5"}, {"\u2FB6", "\u2FB6"}, {"\u2FB7", "\u2FB7"}, {"\u2FB8", "\u2FB8"}, {"\u2FB9", "\u2FB9"}, {"\u2FBA", "\u2FBA"}, {"\u2FBB", "\u2FBB"}, {"\u2FBC", "\u2FBC"}, {"\u2FBD", "\u2FBD"}, {"\u2FBE", "\u2FBE"}, {"\u2FBF", "\u2FBF"}, {"\u2FC0", "\u2FC0"}, {"\u2FC1", "\u2FC1"}, {"\u2FC2", "\u2FC2"}, {"\u2FC3", "\u2FC3"}, {"\u2FC4", "\u2FC4"}, {"\u2FC5", "\u2FC5"}, {"\u2FC6", "\u2FC6"}, {"\u2FC7", "\u2FC7"}, {"\u2FC8", "\u2FC8"}, {"\u2FC9", "\u2FC9"}, {"\u2FCA", "\u2FCA"}, {"\u2FCB", "\u2FCB"}, {"\u2FCC", "\u2FCC"}, {"\u2FCD", "\u2FCD"}, {"\u2FCE", "\u2FCE"}, {"\u2FCF", "\u2FCF"}, {"\u2FD0", "\u2FD0"}, {"\u2FD1", "\u2FD1"}, {"\u2FD2", "\u2FD2"}, {"\u2FD3", "\u2FD3"}, {"\u2FD4", "\u2FD4"}, {"\u2FD5", "\u2FD5"}, {"\u3000", "\u3000"}, {"\u3036", "\u3036"}, {"\u3038", "\u3038"}, {"\u3039", "\u3039"}, {"\u303A", "\u303A"}, {"\u304C", "\u304C"}, {"\u304E", "\u304E"}, {"\u3050", "\u3050"}, {"\u3052", "\u3052"}, {"\u3054", "\u3054"}, {"\u3056", "\u3056"}, {"\u3058", "\u3058"}, {"\u305A", "\u305A"}, {"\u305C", "\u305C"}, {"\u305E", "\u305E"}, {"\u3060", "\u3060"}, {"\u3062", "\u3062"}, {"\u3065", "\u3065"}, {"\u3067", "\u3067"}, {"\u3069", "\u3069"}, {"\u3070", "\u3070"}, {"\u3071", "\u3071"}, {"\u3073", "\u3073"}, {"\u3074", "\u3074"}, {"\u3076", "\u3076"}, {"\u3077", "\u3077"}, {"\u3079", "\u3079"}, {"\u307A", "\u307A"}, {"\u307C", "\u307C"}, {"\u307D", "\u307D"}, {"\u3094", "\u3094"}, {"\u309B", "\u309B"}, {"\u309C", "\u309C"}, {"\u309E", "\u309E"}, {"\u309F", "\u309F"}, {"\u30AC", "\u30AC"}, {"\u30AE", "\u30AE"}, {"\u30B0", "\u30B0"}, {"\u30B2", "\u30B2"}, {"\u30B4", "\u30B4"}, {"\u30B6", "\u30B6"}, {"\u30B8", "\u30B8"}, {"\u30BA", "\u30BA"}, {"\u30BC", "\u30BC"}, {"\u30BE", "\u30BE"}, {"\u30C0", "\u30C0"}, {"\u30C2", "\u30C2"}, {"\u30C5", "\u30C5"}, {"\u30C7", "\u30C7"}, {"\u30C9", "\u30C9"}, {"\u30D0", "\u30D0"}, {"\u30D1", "\u30D1"}, {"\u30D3", "\u30D3"}, {"\u30D4", "\u30D4"}, {"\u30D6", "\u30D6"}, {"\u30D7", "\u30D7"}, {"\u30D9", "\u30D9"}, {"\u30DA", "\u30DA"}, {"\u30DC", "\u30DC"}, {"\u30DD", "\u30DD"}, {"\u30F4", "\u30F4"}, {"\u30F7", "\u30F7"}, {"\u30F8", "\u30F8"}, {"\u30F9", "\u30F9"}, {"\u30FA", "\u30FA"}, {"\u30FE", "\u30FE"}, {"\u30FF", "\u30FF"}, {"\u3131", "\u3131"}, {"\u3132", "\u3132"}, {"\u3133", "\u3133"}, {"\u3134", "\u3134"}, {"\u3135", "\u3135"}, {"\u3136", "\u3136"}, {"\u3137", "\u3137"}, {"\u3138", "\u3138"}, {"\u3139", "\u3139"}, {"\u313A", "\u313A"}, {"\u313B", "\u313B"}, {"\u313C", "\u313C"}, {"\u313D", "\u313D"}, {"\u313E", "\u313E"}, {"\u313F", "\u313F"}, {"\u3140", "\u3140"}, {"\u3141", "\u3141"}, {"\u3142", "\u3142"}, {"\u3143", "\u3143"}, {"\u3144", "\u3144"}, {"\u3145", "\u3145"}, {"\u3146", "\u3146"}, {"\u3147", "\u3147"}, {"\u3148", "\u3148"}, {"\u3149", "\u3149"}, {"\u314A", "\u314A"}, {"\u314B", "\u314B"}, {"\u314C", "\u314C"}, {"\u314D", "\u314D"}, {"\u314E", "\u314E"}, {"\u314F", "\u314F"}, {"\u3150", "\u3150"}, {"\u3151", "\u3151"}, {"\u3152", "\u3152"}, {"\u3153", "\u3153"}, {"\u3154", "\u3154"}, {"\u3155", "\u3155"}, {"\u3156", "\u3156"}, {"\u3157", "\u3157"}, {"\u3158", "\u3158"}, {"\u3159", "\u3159"}, {"\u315A", "\u315A"}, {"\u315B", "\u315B"}, {"\u315C", "\u315C"}, {"\u315D", "\u315D"}, {"\u315E", "\u315E"}, {"\u315F", "\u315F"}, {"\u3160", "\u3160"}, {"\u3161", "\u3161"}, {"\u3162", "\u3162"}, {"\u3163", "\u3163"}, {"\u3164", "\u3164"}, {"\u3165", "\u3165"}, {"\u3166", "\u3166"}, {"\u3167", "\u3167"}, {"\u3168", "\u3168"}, {"\u3169", "\u3169"}, {"\u316A", "\u316A"}, {"\u316B", "\u316B"}, {"\u316C", "\u316C"}, {"\u316D", "\u316D"}, {"\u316E", "\u316E"}, {"\u316F", "\u316F"}, {"\u3170", "\u3170"}, {"\u3171", "\u3171"}, {"\u3172", "\u3172"}, {"\u3173", "\u3173"}, {"\u3174", "\u3174"}, {"\u3175", "\u3175"}, {"\u3176", "\u3176"}, {"\u3177", "\u3177"}, {"\u3178", "\u3178"}, {"\u3179", "\u3179"}, {"\u317A", "\u317A"}, {"\u317B", "\u317B"}, {"\u317C", "\u317C"}, {"\u317D", "\u317D"}, {"\u317E", "\u317E"}, {"\u317F", "\u317F"}, {"\u3180", "\u3180"}, {"\u3181", "\u3181"}, {"\u3182", "\u3182"}, {"\u3183", "\u3183"}, {"\u3184", "\u3184"}, {"\u3185", "\u3185"}, {"\u3186", "\u3186"}, {"\u3187", "\u3187"}, {"\u3188", "\u3188"}, {"\u3189", "\u3189"}, {"\u318A", "\u318A"}, {"\u318B", "\u318B"}, {"\u318C", "\u318C"}, {"\u318D", "\u318D"}, {"\u318E", "\u318E"}, {"\u3192", "\u3192"}, {"\u3193", "\u3193"}, {"\u3194", "\u3194"}, {"\u3195", "\u3195"}, {"\u3196", "\u3196"}, {"\u3197", "\u3197"}, {"\u3198", "\u3198"}, {"\u3199", "\u3199"}, {"\u319A", "\u319A"}, {"\u319B", "\u319B"}, {"\u319C", "\u319C"}, {"\u319D", "\u319D"}, {"\u319E", "\u319E"}, {"\u319F", "\u319F"}, {"\u3200", "\u3200"}, {"\u3201", "\u3201"}, {"\u3202", "\u3202"}, {"\u3203", "\u3203"}, {"\u3204", "\u3204"}, {"\u3205", "\u3205"}, {"\u3206", "\u3206"}, {"\u3207", "\u3207"}, {"\u3208", "\u3208"}, {"\u3209", "\u3209"}, {"\u320A", "\u320A"}, {"\u320B", "\u320B"}, {"\u320C", "\u320C"}, {"\u320D", "\u320D"}, {"\u320E", "\u320E"}, {"\u320F", "\u320F"}, {"\u3210", "\u3210"}, {"\u3211", "\u3211"}, {"\u3212", "\u3212"}, {"\u3213", "\u3213"}, {"\u3214", "\u3214"}, {"\u3215", "\u3215"}, {"\u3216", "\u3216"}, {"\u3217", "\u3217"}, {"\u3218", "\u3218"}, {"\u3219", "\u3219"}, {"\u321A", "\u321A"}, {"\u321B", "\u321B"}, {"\u321C", "\u321C"}, {"\u321D", "\u321D"}, {"\u321E", "\u321E"}, {"\u3220", "\u3220"}, {"\u3221", "\u3221"}, {"\u3222", "\u3222"}, {"\u3223", "\u3223"}, {"\u3224", "\u3224"}, {"\u3225", "\u3225"}, {"\u3226", "\u3226"}, {"\u3227", "\u3227"}, {"\u3228", "\u3228"}, {"\u3229", "\u3229"}, {"\u322A", "\u322A"}, {"\u322B", "\u322B"}, {"\u322C", "\u322C"}, {"\u322D", "\u322D"}, {"\u322E", "\u322E"}, {"\u322F", "\u322F"}, {"\u3230", "\u3230"}, {"\u3231", "\u3231"}, {"\u3232", "\u3232"}, {"\u3233", "\u3233"}, {"\u3234", "\u3234"}, {"\u3235", "\u3235"}, {"\u3236", "\u3236"}, {"\u3237", "\u3237"}, {"\u3238", "\u3238"}, {"\u3239", "\u3239"}, {"\u323A", "\u323A"}, {"\u323B", "\u323B"}, {"\u323C", "\u323C"}, {"\u323D", "\u323D"}, {"\u323E", "\u323E"}, {"\u323F", "\u323F"}, {"\u3240", "\u3240"}, {"\u3241", "\u3241"}, {"\u3242", "\u3242"}, {"\u3243", "\u3243"}, {"\u3244", "\u3244"}, {"\u3245", "\u3245"}, {"\u3246", "\u3246"}, {"\u3247", "\u3247"}, {"\u3250", "\u3250"}, {"\u3251", "\u3251"}, {"\u3252", "\u3252"}, {"\u3253", "\u3253"}, {"\u3254", "\u3254"}, {"\u3255", "\u3255"}, {"\u3256", "\u3256"}, {"\u3257", "\u3257"}, {"\u3258", "\u3258"}, {"\u3259", "\u3259"}, {"\u325A", "\u325A"}, {"\u325B", "\u325B"}, {"\u325C", "\u325C"}, {"\u325D", "\u325D"}, {"\u325E", "\u325E"}, {"\u325F", "\u325F"}, {"\u3260", "\u3260"}, {"\u3261", "\u3261"}, {"\u3262", "\u3262"}, {"\u3263", "\u3263"}, {"\u3264", "\u3264"}, {"\u3265", "\u3265"}, {"\u3266", "\u3266"}, {"\u3267", "\u3267"}, {"\u3268", "\u3268"}, {"\u3269", "\u3269"}, {"\u326A", "\u326A"}, {"\u326B", "\u326B"}, {"\u326C", "\u326C"}, {"\u326D", "\u326D"}, {"\u326E", "\u326E"}, {"\u326F", "\u326F"}, {"\u3270", "\u3270"}, {"\u3271", "\u3271"}, {"\u3272", "\u3272"}, {"\u3273", "\u3273"}, {"\u3274", "\u3274"}, {"\u3275", "\u3275"}, {"\u3276", "\u3276"}, {"\u3277", "\u3277"}, {"\u3278", "\u3278"}, {"\u3279", "\u3279"}, {"\u327A", "\u327A"}, {"\u327B", "\u327B"}, {"\u327C", "\u327C"}, {"\u327D", "\u327D"}, {"\u327E", "\u327E"}, {"\u3280", "\u3280"}, {"\u3281", "\u3281"}, {"\u3282", "\u3282"}, {"\u3283", "\u3283"}, {"\u3284", "\u3284"}, {"\u3285", "\u3285"}, {"\u3286", "\u3286"}, {"\u3287", "\u3287"}, {"\u3288", "\u3288"}, {"\u3289", "\u3289"}, {"\u328A", "\u328A"}, {"\u328B", "\u328B"}, {"\u328C", "\u328C"}, {"\u328D", "\u328D"}, {"\u328E", "\u328E"}, {"\u328F", "\u328F"}, {"\u3290", "\u3290"}, {"\u3291", "\u3291"}, {"\u3292", "\u3292"}, {"\u3293", "\u3293"}, {"\u3294", "\u3294"}, {"\u3295", "\u3295"}, {"\u3296", "\u3296"}, {"\u3297", "\u3297"}, {"\u3298", "\u3298"}, {"\u3299", "\u3299"}, {"\u329A", "\u329A"}, {"\u329B", "\u329B"}, {"\u329C", "\u329C"}, {"\u329D", "\u329D"}, {"\u329E", "\u329E"}, {"\u329F", "\u329F"}, {"\u32A0", "\u32A0"}, {"\u32A1", "\u32A1"}, {"\u32A2", "\u32A2"}, {"\u32A3", "\u32A3"}, {"\u32A4", "\u32A4"}, {"\u32A5", "\u32A5"}, {"\u32A6", "\u32A6"}, {"\u32A7", "\u32A7"}, {"\u32A8", "\u32A8"}, {"\u32A9", "\u32A9"}, {"\u32AA", "\u32AA"}, {"\u32AB", "\u32AB"}, {"\u32AC", "\u32AC"}, {"\u32AD", "\u32AD"}, {"\u32AE", "\u32AE"}, {"\u32AF", "\u32AF"}, {"\u32B0", "\u32B0"}, {"\u32B1", "\u32B1"}, {"\u32B2", "\u32B2"}, {"\u32B3", "\u32B3"}, {"\u32B4", "\u32B4"}, {"\u32B5", "\u32B5"}, {"\u32B6", "\u32B6"}, {"\u32B7", "\u32B7"}, {"\u32B8", "\u32B8"}, {"\u32B9", "\u32B9"}, {"\u32BA", "\u32BA"}, {"\u32BB", "\u32BB"}, {"\u32BC", "\u32BC"}, {"\u32BD", "\u32BD"}, {"\u32BE", "\u32BE"}, {"\u32BF", "\u32BF"}, {"\u32C0", "\u32C0"}, {"\u32C1", "\u32C1"}, {"\u32C2", "\u32C2"}, {"\u32C3", "\u32C3"}, {"\u32C4", "\u32C4"}, {"\u32C5", "\u32C5"}, {"\u32C6", "\u32C6"}, {"\u32C7", "\u32C7"}, {"\u32C8", "\u32C8"}, {"\u32C9", "\u32C9"}, {"\u32CA", "\u32CA"}, {"\u32CB", "\u32CB"}, {"\u32CC", "\u32CC"}, {"\u32CD", "\u32CD"}, {"\u32CE", "\u32CE"}, {"\u32CF", "\u32CF"}, {"\u32D0", "\u32D0"}, {"\u32D1", "\u32D1"}, {"\u32D2", "\u32D2"}, {"\u32D3", "\u32D3"}, {"\u32D4", "\u32D4"}, {"\u32D5", "\u32D5"}, {"\u32D6", "\u32D6"}, {"\u32D7", "\u32D7"}, {"\u32D8", "\u32D8"}, {"\u32D9", "\u32D9"}, {"\u32DA", "\u32DA"}, {"\u32DB", "\u32DB"}, {"\u32DC", "\u32DC"}, {"\u32DD", "\u32DD"}, {"\u32DE", "\u32DE"}, {"\u32DF", "\u32DF"}, {"\u32E0", "\u32E0"}, {"\u32E1", "\u32E1"}, {"\u32E2", "\u32E2"}, {"\u32E3", "\u32E3"}, {"\u32E4", "\u32E4"}, {"\u32E5", "\u32E5"}, {"\u32E6", "\u32E6"}, {"\u32E7", "\u32E7"}, {"\u32E8", "\u32E8"}, {"\u32E9", "\u32E9"}, {"\u32EA", "\u32EA"}, {"\u32EB", "\u32EB"}, {"\u32EC", "\u32EC"}, {"\u32ED", "\u32ED"}, {"\u32EE", "\u32EE"}, {"\u32EF", "\u32EF"}, {"\u32F0", "\u32F0"}, {"\u32F1", "\u32F1"}, {"\u32F2", "\u32F2"}, {"\u32F3", "\u32F3"}, {"\u32F4", "\u32F4"}, {"\u32F5", "\u32F5"}, {"\u32F6", "\u32F6"}, {"\u32F7", "\u32F7"}, {"\u32F8", "\u32F8"}, {"\u32F9", "\u32F9"}, {"\u32FA", "\u32FA"}, {"\u32FB", "\u32FB"}, {"\u32FC", "\u32FC"}, {"\u32FD", "\u32FD"}, {"\u32FE", "\u32FE"}, {"\u32FF", "\u32FF"}, {"\u3300", "\u3300"}, {"\u3301", "\u3301"}, {"\u3302", "\u3302"}, {"\u3303", "\u3303"}, {"\u3304", "\u3304"}, {"\u3305", "\u3305"}, {"\u3306", "\u3306"}, {"\u3307", "\u3307"}, {"\u3308", "\u3308"}, {"\u3309", "\u3309"}, {"\u330A", "\u330A"}, {"\u330B", "\u330B"}, {"\u330C", "\u330C"}, {"\u330D", "\u330D"}, {"\u330E", "\u330E"}, {"\u330F", "\u330F"}, {"\u3310", "\u3310"}, {"\u3311", "\u3311"}, {"\u3312", "\u3312"}, {"\u3313", "\u3313"}, {"\u3314", "\u3314"}, {"\u3315", "\u3315"}, {"\u3316", "\u3316"}, {"\u3317", "\u3317"}, {"\u3318", "\u3318"}, {"\u3319", "\u3319"}, {"\u331A", "\u331A"}, {"\u331B", "\u331B"}, {"\u331C", "\u331C"}, {"\u331D", "\u331D"}, {"\u331E", "\u331E"}, {"\u331F", "\u331F"}, {"\u3320", "\u3320"}, {"\u3321", "\u3321"}, {"\u3322", "\u3322"}, {"\u3323", "\u3323"}, {"\u3324", "\u3324"}, {"\u3325", "\u3325"}, {"\u3326", "\u3326"}, {"\u3327", "\u3327"}, {"\u3328", "\u3328"}, {"\u3329", "\u3329"}, {"\u332A", "\u332A"}, {"\u332B", "\u332B"}, {"\u332C", "\u332C"}, {"\u332D", "\u332D"}, {"\u332E", "\u332E"}, {"\u332F", "\u332F"}, {"\u3330", "\u3330"}, {"\u3331", "\u3331"}, {"\u3332", "\u3332"}, {"\u3333", "\u3333"}, {"\u3334", "\u3334"}, {"\u3335", "\u3335"}, {"\u3336", "\u3336"}, {"\u3337", "\u3337"}, {"\u3338", "\u3338"}, {"\u3339", "\u3339"}, {"\u333A", "\u333A"}, {"\u333B", "\u333B"}, {"\u333C", "\u333C"}, {"\u333D", "\u333D"}, {"\u333E", "\u333E"}, {"\u333F", "\u333F"}, {"\u3340", "\u3340"}, {"\u3341", "\u3341"}, {"\u3342", "\u3342"}, {"\u3343", "\u3343"}, {"\u3344", "\u3344"}, {"\u3345", "\u3345"}, {"\u3346", "\u3346"}, {"\u3347", "\u3347"}, {"\u3348", "\u3348"}, {"\u3349", "\u3349"}, {"\u334A", "\u334A"}, {"\u334B", "\u334B"}, {"\u334C", "\u334C"}, {"\u334D", "\u334D"}, {"\u334E", "\u334E"}, {"\u334F", "\u334F"}, {"\u3350", "\u3350"}, {"\u3351", "\u3351"}, {"\u3352", "\u3352"}, {"\u3353", "\u3353"}, {"\u3354", "\u3354"}, {"\u3355", "\u3355"}, {"\u3356", "\u3356"}, {"\u3357", "\u3357"}, {"\u3358", "\u3358"}, {"\u3359", "\u3359"}, {"\u335A", "\u335A"}, {"\u335B", "\u335B"}, {"\u335C", "\u335C"}, {"\u335D", "\u335D"}, {"\u335E", "\u335E"}, {"\u335F", "\u335F"}, {"\u3360", "\u3360"}, {"\u3361", "\u3361"}, {"\u3362", "\u3362"}, {"\u3363", "\u3363"}, {"\u3364", "\u3364"}, {"\u3365", "\u3365"}, {"\u3366", "\u3366"}, {"\u3367", "\u3367"}, {"\u3368", "\u3368"}, {"\u3369", "\u3369"}, {"\u336A", "\u336A"}, {"\u336B", "\u336B"}, {"\u336C", "\u336C"}, {"\u336D", "\u336D"}, {"\u336E", "\u336E"}, {"\u336F", "\u336F"}, {"\u3370", "\u3370"}, {"\u3371", "\u3371"}, {"\u3372", "\u3372"}, {"\u3373", "\u3373"}, {"\u3374", "\u3374"}, {"\u3375", "\u3375"}, {"\u3376", "\u3376"}, {"\u3377", "\u3377"}, {"\u3378", "\u3378"}, {"\u3379", "\u3379"}, {"\u337A", "\u337A"}, {"\u337B", "\u337B"}, {"\u337C", "\u337C"}, {"\u337D", "\u337D"}, {"\u337E", "\u337E"}, {"\u337F", "\u337F"}, {"\u3380", "\u3380"}, {"\u3381", "\u3381"}, {"\u3382", "\u3382"}, {"\u3383", "\u3383"}, {"\u3384", "\u3384"}, {"\u3385", "\u3385"}, {"\u3386", "\u3386"}, {"\u3387", "\u3387"}, {"\u3388", "\u3388"}, {"\u3389", "\u3389"}, {"\u338A", "\u338A"}, {"\u338B", "\u338B"}, {"\u338C", "\u338C"}, {"\u338D", "\u338D"}, {"\u338E", "\u338E"}, {"\u338F", "\u338F"}, {"\u3390", "\u3390"}, {"\u3391", "\u3391"}, {"\u3392", "\u3392"}, {"\u3393", "\u3393"}, {"\u3394", "\u3394"}, {"\u3395", "\u3395"}, {"\u3396", "\u3396"}, {"\u3397", "\u3397"}, {"\u3398", "\u3398"}, {"\u3399", "\u3399"}, {"\u339A", "\u339A"}, {"\u339B", "\u339B"}, {"\u339C", "\u339C"}, {"\u339D", "\u339D"}, {"\u339E", "\u339E"}, {"\u339F", "\u339F"}, {"\u33A0", "\u33A0"}, {"\u33A1", "\u33A1"}, {"\u33A2", "\u33A2"}, {"\u33A3", "\u33A3"}, {"\u33A4", "\u33A4"}, {"\u33A5", "\u33A5"}, {"\u33A6", "\u33A6"}, {"\u33A7", "\u33A7"}, {"\u33A8", "\u33A8"}, {"\u33A9", "\u33A9"}, {"\u33AA", "\u33AA"}, {"\u33AB", "\u33AB"}, {"\u33AC", "\u33AC"}, {"\u33AD", "\u33AD"}, {"\u33AE", "\u33AE"}, {"\u33AF", "\u33AF"}, {"\u33B0", "\u33B0"}, {"\u33B1", "\u33B1"}, {"\u33B2", "\u33B2"}, {"\u33B3", "\u33B3"}, {"\u33B4", "\u33B4"}, {"\u33B5", "\u33B5"}, {"\u33B6", "\u33B6"}, {"\u33B7", "\u33B7"}, {"\u33B8", "\u33B8"}, {"\u33B9", "\u33B9"}, {"\u33BA", "\u33BA"}, {"\u33BB", "\u33BB"}, {"\u33BC", "\u33BC"}, {"\u33BD", "\u33BD"}, {"\u33BE", "\u33BE"}, {"\u33BF", "\u33BF"}, {"\u33C0", "\u33C0"}, {"\u33C1", "\u33C1"}, {"\u33C2", "\u33C2"}, {"\u33C3", "\u33C3"}, {"\u33C4", "\u33C4"}, {"\u33C5", "\u33C5"}, {"\u33C6", "\u33C6"}, {"\u33C7", "\u33C7"}, {"\u33C8", "\u33C8"}, {"\u33C9", "\u33C9"}, {"\u33CA", "\u33CA"}, {"\u33CB", "\u33CB"}, {"\u33CC", "\u33CC"}, {"\u33CD", "\u33CD"}, {"\u33CE", "\u33CE"}, {"\u33CF", "\u33CF"}, {"\u33D0", "\u33D0"}, {"\u33D1", "\u33D1"}, {"\u33D2", "\u33D2"}, {"\u33D3", "\u33D3"}, {"\u33D4", "\u33D4"}, {"\u33D5", "\u33D5"}, {"\u33D6", "\u33D6"}, {"\u33D7", "\u33D7"}, {"\u33D8", "\u33D8"}, {"\u33D9", "\u33D9"}, {"\u33DA", "\u33DA"}, {"\u33DB", "\u33DB"}, {"\u33DC", "\u33DC"}, {"\u33DD", "\u33DD"}, {"\u33DE", "\u33DE"}, {"\u33DF", "\u33DF"}, {"\u33E0", "\u33E0"}, {"\u33E1", "\u33E1"}, {"\u33E2", "\u33E2"}, {"\u33E3", "\u33E3"}, {"\u33E4", "\u33E4"}, {"\u33E5", "\u33E5"}, {"\u33E6", "\u33E6"}, {"\u33E7", "\u33E7"}, {"\u33E8", "\u33E8"}, {"\u33E9", "\u33E9"}, {"\u33EA", "\u33EA"}, {"\u33EB", "\u33EB"}, {"\u33EC", "\u33EC"}, {"\u33ED", "\u33ED"}, {"\u33EE", "\u33EE"}, {"\u33EF", "\u33EF"}, {"\u33F0", "\u33F0"}, {"\u33F1", "\u33F1"}, {"\u33F2", "\u33F2"}, {"\u33F3", "\u33F3"}, {"\u33F4", "\u33F4"}, {"\u33F5", "\u33F5"}, {"\u33F6", "\u33F6"}, {"\u33F7", "\u33F7"}, {"\u33F8", "\u33F8"}, {"\u33F9", "\u33F9"}, {"\u33FA", "\u33FA"}, {"\u33FB", "\u33FB"}, {"\u33FC", "\u33FC"}, {"\u33FD", "\u33FD"}, {"\u33FE", "\u33FE"}, {"\u33FF", "\u33FF"}, {"\uA69C", "\uA69C"}, {"\uA69D", "\uA69D"}, {"\uA770", "\uA770"}, {"\uA7F2", "\uA7F2"}, {"\uA7F3", "\uA7F3"}, {"\uA7F4", "\uA7F4"}, {"\uA7F8", "\uA7F8"}, {"\uA7F9", "\uA7F9"}, {"\uAB5C", "\uAB5C"}, {"\uAB5D", "\uAB5D"}, {"\uAB5E", "\uAB5E"}, {"\uAB5F", "\uAB5F"}, {"\uAB69", "\uAB69"}, {"\uAC00", "\uAC00"}, {"\uAC01", "\uAC01"}, {"\uAC02", "\uAC02"}, {"\uAC03", "\uAC03"}, {"\uAC04", "\uAC04"}, {"\uAC05", "\uAC05"}, {"\uAC06", "\uAC06"}, {"\uAC07", "\uAC07"}, {"\uAC08", "\uAC08"}, {"\uAC09", "\uAC09"}, {"\uAC0A", "\uAC0A"}, {"\uAC0B", "\uAC0B"}, {"\uAC0C", "\uAC0C"}, {"\uAC0D", "\uAC0D"}, {"\uAC0E", "\uAC0E"}, {"\uAC0F", "\uAC0F"}, {"\uAC10", "\uAC10"}, {"\uAC11", "\uAC11"}, {"\uAC12", "\uAC12"}, {"\uAC13", "\uAC13"}, {"\uAC14", "\uAC14"}, {"\uAC15", "\uAC15"}, {"\uAC16", "\uAC16"}, {"\uAC17", "\uAC17"}, {"\uAC18", "\uAC18"}, {"\uAC19", "\uAC19"}, {"\uAC1A", "\uAC1A"}, {"\uAC1B", "\uAC1B"}, {"\uAC1C", "\uAC1C"}, {"\uAC1D", "\uAC1D"}, {"\uAC1E", "\uAC1E"}, {"\uAC1F", "\uAC1F"}, {"\uAC20", "\uAC20"}, {"\uAC21", "\uAC21"}, {"\uAC22", "\uAC22"}, {"\uAC23", "\uAC23"}, {"\uAC24", "\uAC24"}, {"\uAC25", "\uAC25"}, {"\uAC26", "\uAC26"}, {"\uAC27", "\uAC27"}, {"\uAC28", "\uAC28"}, {"\uAC29", "\uAC29"}, {"\uAC2A", "\uAC2A"}, {"\uAC2B", "\uAC2B"}, {"\uAC2C", "\uAC2C"}, {"\uAC2D", "\uAC2D"}, {"\uAC2E", "\uAC2E"}, {"\uAC2F", "\uAC2F"}, {"\uAC30", "\uAC30"}, {"\uAC31", "\uAC31"}, {"\uAC32", "\uAC32"}, {"\uAC33", "\uAC33"}, {"\uAC34", "\uAC34"}, {"\uAC35", "\uAC35"}, {"\uAC36", "\uAC36"}, {"\uAC37", "\uAC37"}, {"\uAC38", "\uAC38"}, {"\uAC39", "\uAC39"}, {"\uAC3A", "\uAC3A"}, {"\uAC3B", "\uAC3B"}, {"\uAC3C", "\uAC3C"}, {"\uAC3D", "\uAC3D"}, {"\uAC3E", "\uAC3E"}, {"\uAC3F", "\uAC3F"}, {"\uAC40", "\uAC40"}, {"\uAC41", "\uAC41"}, {"\uAC42", "\uAC42"}, {"\uAC43", "\uAC43"}, {"\uAC44", "\uAC44"}, {"\uAC45", "\uAC45"}, {"\uAC46", "\uAC46"}, {"\uAC47", "\uAC47"}, {"\uAC48", "\uAC48"}, {"\uAC49", "\uAC49"}, {"\uAC4A", "\uAC4A"}, {"\uAC4B", "\uAC4B"}, {"\uAC4C", "\uAC4C"}, {"\uAC4D", "\uAC4D"}, {"\uAC4E", "\uAC4E"}, {"\uAC4F", "\uAC4F"}, {"\uAC50", "\uAC50"}, {"\uAC51", "\uAC51"}, {"\uAC52", "\uAC52"}, {"\uAC53", "\uAC53"}, {"\uAC54", "\uAC54"}, {"\uAC55", "\uAC55"}, {"\uAC56", "\uAC56"}, {"\uAC57", "\uAC57"}, {"\uAC58", "\uAC58"}, {"\uAC59", "\uAC59"}, {"\uAC5A", "\uAC5A"}, {"\uAC5B", "\uAC5B"}, {"\uAC5C", "\uAC5C"}, {"\uAC5D", "\uAC5D"}, {"\uAC5E", "\uAC5E"}, {"\uAC5F", "\uAC5F"}, {"\uAC60", "\uAC60"}, {"\uAC61", "\uAC61"}, {"\uAC62", "\uAC62"}, {"\uAC63", "\uAC63"}, {"\uAC64", "\uAC64"}, {"\uAC65", "\uAC65"}, {"\uAC66", "\uAC66"}, {"\uAC67", "\uAC67"}, {"\uAC68", "\uAC68"}, {"\uAC69", "\uAC69"}, {"\uAC6A", "\uAC6A"}, {"\uAC6B", "\uAC6B"}, {"\uAC6C", "\uAC6C"}, {"\uAC6D", "\uAC6D"}, {"\uAC6E", "\uAC6E"}, {"\uAC6F", "\uAC6F"}, {"\uAC70", "\uAC70"}, {"\uAC71", "\uAC71"}, {"\uAC72", "\uAC72"}, {"\uAC73", "\uAC73"}, {"\uAC74", "\uAC74"}, {"\uAC75", "\uAC75"}, {"\uAC76", "\uAC76"}, {"\uAC77", "\uAC77"}, {"\uAC78", "\uAC78"}, {"\uAC79", "\uAC79"}, {"\uAC7A", "\uAC7A"}, {"\uAC7B", "\uAC7B"}, {"\uAC7C", "\uAC7C"}, {"\uAC7D", "\uAC7D"}, {"\uAC7E", "\uAC7E"}, {"\uAC7F", "\uAC7F"}, {"\uAC80", "\uAC80"}, {"\uAC81", "\uAC81"}, {"\uAC82", "\uAC82"}, {"\uAC83", "\uAC83"}, {"\uAC84", "\uAC84"}, {"\uAC85", "\uAC85"}, {"\uAC86", "\uAC86"}, {"\uAC87", "\uAC87"}, {"\uAC88", "\uAC88"}, {"\uAC89", "\uAC89"}, {"\uAC8A", "\uAC8A"}, {"\uAC8B", "\uAC8B"}, {"\uAC8C", "\uAC8C"}, {"\uAC8D", "\uAC8D"}, {"\uAC8E", "\uAC8E"}, {"\uAC8F", "\uAC8F"}, {"\uAC90", "\uAC90"}, {"\uAC91", "\uAC91"}, {"\uAC92", "\uAC92"}, {"\uAC93", "\uAC93"}, {"\uAC94", "\uAC94"}, {"\uAC95", "\uAC95"}, {"\uAC96", "\uAC96"}, {"\uAC97", "\uAC97"}, {"\uAC98", "\uAC98"}, {"\uAC99", "\uAC99"}, {"\uAC9A", "\uAC9A"}, {"\uAC9B", "\uAC9B"}, {"\uAC9C", "\uAC9C"}, {"\uAC9D", "\uAC9D"}, {"\uAC9E", "\uAC9E"}, {"\uAC9F", "\uAC9F"}, {"\uACA0", "\uACA0"}, {"\uACA1", "\uACA1"}, {"\uACA2", "\uACA2"}, {"\uACA3", "\uACA3"}, {"\uACA4", "\uACA4"}, {"\uACA5", "\uACA5"}, {"\uACA6", "\uACA6"}, {"\uACA7", "\uACA7"}, {"\uACA8", "\uACA8"}, {"\uACA9", "\uACA9"}, {"\uACAA", "\uACAA"}, {"\uACAB", "\uACAB"}, {"\uACAC", "\uACAC"}, {"\uACAD", "\uACAD"}, {"\uACAE", "\uACAE"}, {"\uACAF", "\uACAF"}, {"\uACB0", "\uACB0"}, {"\uACB1", "\uACB1"}, {"\uACB2", "\uACB2"}, {"\uACB3", "\uACB3"}, {"\uACB4", "\uACB4"}, {"\uACB5", "\uACB5"}, {"\uACB6", "\uACB6"}, {"\uACB7", "\uACB7"}, {"\uACB8", "\uACB8"}, {"\uACB9", "\uACB9"}, {"\uACBA", "\uACBA"}, {"\uACBB", "\uACBB"}, {"\uACBC", "\uACBC"}, {"\uACBD", "\uACBD"}, {"\uACBE", "\uACBE"}, {"\uACBF", "\uACBF"}, {"\uACC0", "\uACC0"}, {"\uACC1", "\uACC1"}, {"\uACC2", "\uACC2"}, {"\uACC3", "\uACC3"}, {"\uACC4", "\uACC4"}, {"\uACC5", "\uACC5"}, {"\uACC6", "\uACC6"}, {"\uACC7", "\uACC7"}, {"\uACC8", "\uACC8"}, {"\uACC9", "\uACC9"}, {"\uACCA", "\uACCA"}, {"\uACCB", "\uACCB"}, {"\uACCC", "\uACCC"}, {"\uACCD", "\uACCD"}, {"\uACCE", "\uACCE"}, {"\uACCF", "\uACCF"}, {"\uACD0", "\uACD0"}, {"\uACD1", "\uACD1"}, {"\uACD2", "\uACD2"}, {"\uACD3", "\uACD3"}, {"\uACD4", "\uACD4"}, {"\uACD5", "\uACD5"}, {"\uACD6", "\uACD6"}, {"\uACD7", "\uACD7"}, {"\uACD8", "\uACD8"}, {"\uACD9", "\uACD9"}, {"\uACDA", "\uACDA"}, {"\uACDB", "\uACDB"}, {"\uACDC", "\uACDC"}, {"\uACDD", "\uACDD"}, {"\uACDE", "\uACDE"}, {"\uACDF", "\uACDF"}, {"\uACE0", "\uACE0"}, {"\uACE1", "\uACE1"}, {"\uACE2", "\uACE2"}, {"\uACE3", "\uACE3"}, {"\uACE4", "\uACE4"}, {"\uACE5", "\uACE5"}, {"\uACE6", "\uACE6"}, {"\uACE7", "\uACE7"}, {"\uACE8", "\uACE8"}, {"\uACE9", "\uACE9"}, {"\uACEA", "\uACEA"}, {"\uACEB", "\uACEB"}, {"\uACEC", "\uACEC"}, {"\uACED", "\uACED"}, {"\uACEE", "\uACEE"}, {"\uACEF", "\uACEF"}, {"\uACF0", "\uACF0"}, {"\uACF1", "\uACF1"}, {"\uACF2", "\uACF2"}, {"\uACF3", "\uACF3"}, {"\uACF4", "\uACF4"}, {"\uACF5", "\uACF5"}, {"\uACF6", "\uACF6"}, {"\uACF7", "\uACF7"}, {"\uACF8", "\uACF8"}, {"\uACF9", "\uACF9"}, {"\uACFA", "\uACFA"}, {"\uACFB", "\uACFB"}, {"\uACFC", "\uACFC"}, {"\uACFD", "\uACFD"}, {"\uACFE", "\uACFE"}, {"\uACFF", "\uACFF"}, {"\uAD00", "\uAD00"}, {"\uAD01", "\uAD01"}, {"\uAD02", "\uAD02"}, {"\uAD03", "\uAD03"}, {"\uAD04", "\uAD04"}, {"\uAD05", "\uAD05"}, {"\uAD06", "\uAD06"}, {"\uAD07", "\uAD07"}, {"\uAD08", "\uAD08"}, {"\uAD09", "\uAD09"}, {"\uAD0A", "\uAD0A"}, {"\uAD0B", "\uAD0B"}, {"\uAD0C", "\uAD0C"}, {"\uAD0D", "\uAD0D"}, {"\uAD0E", "\uAD0E"}, {"\uAD0F", "\uAD0F"}, {"\uAD10", "\uAD10"}, {"\uAD11", "\uAD11"}, {"\uAD12", "\uAD12"}, {"\uAD13", "\uAD13"}, {"\uAD14", "\uAD14"}, {"\uAD15", "\uAD15"}, {"\uAD16", "\uAD16"}, {"\uAD17", "\uAD17"}, {"\uAD18", "\uAD18"}, {"\uAD19", "\uAD19"}, {"\uAD1A", "\uAD1A"}, {"\uAD1B", "\uAD1B"}, {"\uAD1C", "\uAD1C"}, {"\uAD1D", "\uAD1D"}, {"\uAD1E", "\uAD1E"}, {"\uAD1F", "\uAD1F"}, {"\uAD20", "\uAD20"}, {"\uAD21", "\uAD21"}, {"\uAD22", "\uAD22"}, {"\uAD23", "\uAD23"}, {"\uAD24", "\uAD24"}, {"\uAD25", "\uAD25"}, {"\uAD26", "\uAD26"}, {"\uAD27", "\uAD27"}, {"\uAD28", "\uAD28"}, {"\uAD29", "\uAD29"}, {"\uAD2A", "\uAD2A"}, {"\uAD2B", "\uAD2B"}, {"\uAD2C", "\uAD2C"}, {"\uAD2D", "\uAD2D"}, {"\uAD2E", "\uAD2E"}, {"\uAD2F", "\uAD2F"}, {"\uAD30", "\uAD30"}, {"\uAD31", "\uAD31"}, {"\uAD32", "\uAD32"}, {"\uAD33", "\uAD33"}, {"\uAD34", "\uAD34"}, {"\uAD35", "\uAD35"}, {"\uAD36", "\uAD36"}, {"\uAD37", "\uAD37"}, {"\uAD38", "\uAD38"}, {"\uAD39", "\uAD39"}, {"\uAD3A", "\uAD3A"}, {"\uAD3B", "\uAD3B"}, {"\uAD3C", "\uAD3C"}, {"\uAD3D", "\uAD3D"}, {"\uAD3E", "\uAD3E"}, {"\uAD3F", "\uAD3F"}, {"\uAD40", "\uAD40"}, {"\uAD41", "\uAD41"}, {"\uAD42", "\uAD42"}, {"\uAD43", "\uAD43"}, {"\uAD44", "\uAD44"}, {"\uAD45", "\uAD45"}, {"\uAD46", "\uAD46"}, {"\uAD47", "\uAD47"}, {"\uAD48", "\uAD48"}, {"\uAD49", "\uAD49"}, {"\uAD4A", "\uAD4A"}, {"\uAD4B", "\uAD4B"}, {"\uAD4C", "\uAD4C"}, {"\uAD4D", "\uAD4D"}, {"\uAD4E", "\uAD4E"}, {"\uAD4F", "\uAD4F"}, {"\uAD50", "\uAD50"}, {"\uAD51", "\uAD51"}, {"\uAD52", "\uAD52"}, {"\uAD53", "\uAD53"}, {"\uAD54", "\uAD54"}, {"\uAD55", "\uAD55"}, {"\uAD56", "\uAD56"}, {"\uAD57", "\uAD57"}, {"\uAD58", "\uAD58"}, {"\uAD59", "\uAD59"}, {"\uAD5A", "\uAD5A"}, {"\uAD5B", "\uAD5B"}, {"\uAD5C", "\uAD5C"}, {"\uAD5D", "\uAD5D"}, {"\uAD5E", "\uAD5E"}, {"\uAD5F", "\uAD5F"}, {"\uAD60", "\uAD60"}, {"\uAD61", "\uAD61"}, {"\uAD62", "\uAD62"}, {"\uAD63", "\uAD63"}, {"\uAD64", "\uAD64"}, {"\uAD65", "\uAD65"}, {"\uAD66", "\uAD66"}, {"\uAD67", "\uAD67"}, {"\uAD68", "\uAD68"}, {"\uAD69", "\uAD69"}, {"\uAD6A", "\uAD6A"}, {"\uAD6B", "\uAD6B"}, {"\uAD6C", "\uAD6C"}, {"\uAD6D", "\uAD6D"}, {"\uAD6E", "\uAD6E"}, {"\uAD6F", "\uAD6F"}, {"\uAD70", "\uAD70"}, {"\uAD71", "\uAD71"}, {"\uAD72", "\uAD72"}, {"\uAD73", "\uAD73"}, {"\uAD74", "\uAD74"}, {"\uAD75", "\uAD75"}, {"\uAD76", "\uAD76"}, {"\uAD77", "\uAD77"}, {"\uAD78", "\uAD78"}, {"\uAD79", "\uAD79"}, {"\uAD7A", "\uAD7A"}, {"\uAD7B", "\uAD7B"}, {"\uAD7C", "\uAD7C"}, {"\uAD7D", "\uAD7D"}, {"\uAD7E", "\uAD7E"}, {"\uAD7F", "\uAD7F"}, {"\uAD80", "\uAD80"}, {"\uAD81", "\uAD81"}, {"\uAD82", "\uAD82"}, {"\uAD83", "\uAD83"}, {"\uAD84", "\uAD84"}, {"\uAD85", "\uAD85"}, {"\uAD86", "\uAD86"}, {"\uAD87", "\uAD87"}, {"\uAD88", "\uAD88"}, {"\uAD89", "\uAD89"}, {"\uAD8A", "\uAD8A"}, {"\uAD8B", "\uAD8B"}, {"\uAD8C", "\uAD8C"}, {"\uAD8D", "\uAD8D"}, {"\uAD8E", "\uAD8E"}, {"\uAD8F", "\uAD8F"}, {"\uAD90", "\uAD90"}, {"\uAD91", "\uAD91"}, {"\uAD92", "\uAD92"}, {"\uAD93", "\uAD93"}, {"\uAD94", "\uAD94"}, {"\uAD95", "\uAD95"}, {"\uAD96", "\uAD96"}, {"\uAD97", "\uAD97"}, {"\uAD98", "\uAD98"}, {"\uAD99", "\uAD99"}, {"\uAD9A", "\uAD9A"}, {"\uAD9B", "\uAD9B"}, {"\uAD9C", "\uAD9C"}, {"\uAD9D", "\uAD9D"}, {"\uAD9E", "\uAD9E"}, {"\uAD9F", "\uAD9F"}, {"\uADA0", "\uADA0"}, {"\uADA1", "\uADA1"}, {"\uADA2", "\uADA2"}, {"\uADA3", "\uADA3"}, {"\uADA4", "\uADA4"}, {"\uADA5", "\uADA5"}, {"\uADA6", "\uADA6"}, {"\uADA7", "\uADA7"}, {"\uADA8", "\uADA8"}, {"\uADA9", "\uADA9"}, {"\uADAA", "\uADAA"}, {"\uADAB", "\uADAB"}, {"\uADAC", "\uADAC"}, {"\uADAD", "\uADAD"}, {"\uADAE", "\uADAE"}, {"\uADAF", "\uADAF"}, {"\uADB0", "\uADB0"}, {"\uADB1", "\uADB1"}, {"\uADB2", "\uADB2"}, {"\uADB3", "\uADB3"}, {"\uADB4", "\uADB4"}, {"\uADB5", "\uADB5"}, {"\uADB6", "\uADB6"}, {"\uADB7", "\uADB7"}, {"\uADB8", "\uADB8"}, {"\uADB9", "\uADB9"}, {"\uADBA", "\uADBA"}, {"\uADBB", "\uADBB"}, {"\uADBC", "\uADBC"}, {"\uADBD", "\uADBD"}, {"\uADBE", "\uADBE"}, {"\uADBF", "\uADBF"}, {"\uADC0", "\uADC0"}, {"\uADC1", "\uADC1"}, {"\uADC2", "\uADC2"}, {"\uADC3", "\uADC3"}, {"\uADC4", "\uADC4"}, {"\uADC5", "\uADC5"}, {"\uADC6", "\uADC6"}, {"\uADC7", "\uADC7"}, {"\uADC8", "\uADC8"}, {"\uADC9", "\uADC9"}, {"\uADCA", "\uADCA"}, {"\uADCB", "\uADCB"}, {"\uADCC", "\uADCC"}, {"\uADCD", "\uADCD"}, {"\uADCE", "\uADCE"}, {"\uADCF", "\uADCF"}, {"\uADD0", "\uADD0"}, {"\uADD1", "\uADD1"}, {"\uADD2", "\uADD2"}, {"\uADD3", "\uADD3"}, {"\uADD4", "\uADD4"}, {"\uADD5", "\uADD5"}, {"\uADD6", "\uADD6"}, {"\uADD7", "\uADD7"}, {"\uADD8", "\uADD8"}, {"\uADD9", "\uADD9"}, {"\uADDA", "\uADDA"}, {"\uADDB", "\uADDB"}, {"\uADDC", "\uADDC"}, {"\uADDD", "\uADDD"}, {"\uADDE", "\uADDE"}, {"\uADDF", "\uADDF"}, {"\uADE0", "\uADE0"}, {"\uADE1", "\uADE1"}, {"\uADE2", "\uADE2"}, {"\uADE3", "\uADE3"}, {"\uADE4", "\uADE4"}, {"\uADE5", "\uADE5"}, {"\uADE6", "\uADE6"}, {"\uADE7", "\uADE7"}, {"\uADE8", "\uADE8"}, {"\uADE9", "\uADE9"}, {"\uADEA", "\uADEA"}, {"\uADEB", "\uADEB"}, {"\uADEC", "\uADEC"}, {"\uADED", "\uADED"}, {"\uADEE", "\uADEE"}, {"\uADEF", "\uADEF"}, {"\uADF0", "\uADF0"}, {"\uADF1", "\uADF1"}, {"\uADF2", "\uADF2"}, {"\uADF3", "\uADF3"}, {"\uADF4", "\uADF4"}, {"\uADF5", "\uADF5"}, {"\uADF6", "\uADF6"}, {"\uADF7", "\uADF7"}, {"\uADF8", "\uADF8"}, {"\uADF9", "\uADF9"}, {"\uADFA", "\uADFA"}, {"\uADFB", "\uADFB"}, {"\uADFC", "\uADFC"}, {"\uADFD", "\uADFD"}, {"\uADFE", "\uADFE"}, {"\uADFF", "\uADFF"}, {"\uAE00", "\uAE00"}, {"\uAE01", "\uAE01"}, {"\uAE02", "\uAE02"}, {"\uAE03", "\uAE03"}, {"\uAE04", "\uAE04"}, {"\uAE05", "\uAE05"}, {"\uAE06", "\uAE06"}, {"\uAE07", "\uAE07"}, {"\uAE08", "\uAE08"}, {"\uAE09", "\uAE09"}, {"\uAE0A", "\uAE0A"}, {"\uAE0B", "\uAE0B"}, {"\uAE0C", "\uAE0C"}, {"\uAE0D", "\uAE0D"}, {"\uAE0E", "\uAE0E"}, {"\uAE0F", "\uAE0F"}, {"\uAE10", "\uAE10"}, {"\uAE11", "\uAE11"}, {"\uAE12", "\uAE12"}, {"\uAE13", "\uAE13"}, {"\uAE14", "\uAE14"}, {"\uAE15", "\uAE15"}, {"\uAE16", "\uAE16"}, {"\uAE17", "\uAE17"}, {"\uAE18", "\uAE18"}, {"\uAE19", "\uAE19"}, {"\uAE1A", "\uAE1A"}, {"\uAE1B", "\uAE1B"}, {"\uAE1C", "\uAE1C"}, {"\uAE1D", "\uAE1D"}, {"\uAE1E", "\uAE1E"}, {"\uAE1F", "\uAE1F"}, {"\uAE20", "\uAE20"}, {"\uAE21", "\uAE21"}, {"\uAE22", "\uAE22"}, {"\uAE23", "\uAE23"}, {"\uAE24", "\uAE24"}, {"\uAE25", "\uAE25"}, {"\uAE26", "\uAE26"}, {"\uAE27", "\uAE27"}, {"\uAE28", "\uAE28"}, {"\uAE29", "\uAE29"}, {"\uAE2A", "\uAE2A"}, {"\uAE2B", "\uAE2B"}, {"\uAE2C", "\uAE2C"}, {"\uAE2D", "\uAE2D"}, {"\uAE2E", "\uAE2E"}, {"\uAE2F", "\uAE2F"}, {"\uAE30", "\uAE30"}, {"\uAE31", "\uAE31"}, {"\uAE32", "\uAE32"}, {"\uAE33", "\uAE33"}, {"\uAE34", "\uAE34"}, {"\uAE35", "\uAE35"}, {"\uAE36", "\uAE36"}, {"\uAE37", "\uAE37"}, {"\uAE38", "\uAE38"}, {"\uAE39", "\uAE39"}, {"\uAE3A", "\uAE3A"}, {"\uAE3B", "\uAE3B"}, {"\uAE3C", "\uAE3C"}, {"\uAE3D", "\uAE3D"}, {"\uAE3E", "\uAE3E"}, {"\uAE3F", "\uAE3F"}, {"\uAE40", "\uAE40"}, {"\uAE41", "\uAE41"}, {"\uAE42", "\uAE42"}, {"\uAE43", "\uAE43"}, {"\uAE44", "\uAE44"}, {"\uAE45", "\uAE45"}, {"\uAE46", "\uAE46"}, {"\uAE47", "\uAE47"}, {"\uAE48", "\uAE48"}, {"\uAE49", "\uAE49"}, {"\uAE4A", "\uAE4A"}, {"\uAE4B", "\uAE4B"}, {"\uAE4C", "\uAE4C"}, {"\uAE4D", "\uAE4D"}, {"\uAE4E", "\uAE4E"}, {"\uAE4F", "\uAE4F"}, {"\uAE50", "\uAE50"}, {"\uAE51", "\uAE51"}, {"\uAE52", "\uAE52"}, {"\uAE53", "\uAE53"}, {"\uAE54", "\uAE54"}, {"\uAE55", "\uAE55"}, {"\uAE56", "\uAE56"}, {"\uAE57", "\uAE57"}, {"\uAE58", "\uAE58"}, {"\uAE59", "\uAE59"}, {"\uAE5A", "\uAE5A"}, {"\uAE5B", "\uAE5B"}, {"\uAE5C", "\uAE5C"}, {"\uAE5D", "\uAE5D"}, {"\uAE5E", "\uAE5E"}, {"\uAE5F", "\uAE5F"}, {"\uAE60", "\uAE60"}, {"\uAE61", "\uAE61"}, {"\uAE62", "\uAE62"}, {"\uAE63", "\uAE63"}, {"\uAE64", "\uAE64"}, {"\uAE65", "\uAE65"}, {"\uAE66", "\uAE66"}, {"\uAE67", "\uAE67"}, {"\uAE68", "\uAE68"}, {"\uAE69", "\uAE69"}, {"\uAE6A", "\uAE6A"}, {"\uAE6B", "\uAE6B"}, {"\uAE6C", "\uAE6C"}, {"\uAE6D", "\uAE6D"}, {"\uAE6E", "\uAE6E"}, {"\uAE6F", "\uAE6F"}, {"\uAE70", "\uAE70"}, {"\uAE71", "\uAE71"}, {"\uAE72", "\uAE72"}, {"\uAE73", "\uAE73"}, {"\uAE74", "\uAE74"}, {"\uAE75", "\uAE75"}, {"\uAE76", "\uAE76"}, {"\uAE77", "\uAE77"}, {"\uAE78", "\uAE78"}, {"\uAE79", "\uAE79"}, {"\uAE7A", "\uAE7A"}, {"\uAE7B", "\uAE7B"}, {"\uAE7C", "\uAE7C"}, {"\uAE7D", "\uAE7D"}, {"\uAE7E", "\uAE7E"}, {"\uAE7F", "\uAE7F"}, {"\uAE80", "\uAE80"}, {"\uAE81", "\uAE81"}, {"\uAE82", "\uAE82"}, {"\uAE83", "\uAE83"}, {"\uAE84", "\uAE84"}, {"\uAE85", "\uAE85"}, {"\uAE86", "\uAE86"}, {"\uAE87", "\uAE87"}, {"\uAE88", "\uAE88"}, {"\uAE89", "\uAE89"}, {"\uAE8A", "\uAE8A"}, {"\uAE8B", "\uAE8B"}, {"\uAE8C", "\uAE8C"}, {"\uAE8D", "\uAE8D"}, {"\uAE8E", "\uAE8E"}, {"\uAE8F", "\uAE8F"}, {"\uAE90", "\uAE90"}, {"\uAE91", "\uAE91"}, {"\uAE92", "\uAE92"}, {"\uAE93", "\uAE93"}, {"\uAE94", "\uAE94"}, {"\uAE95", "\uAE95"}, {"\uAE96", "\uAE96"}, {"\uAE97", "\uAE97"}, {"\uAE98", "\uAE98"}, {"\uAE99", "\uAE99"}, {"\uAE9A", "\uAE9A"}, {"\uAE9B", "\uAE9B"}, {"\uAE9C", "\uAE9C"}, {"\uAE9D", "\uAE9D"}, {"\uAE9E", "\uAE9E"}, {"\uAE9F", "\uAE9F"}, {"\uAEA0", "\uAEA0"}, {"\uAEA1", "\uAEA1"}, {"\uAEA2", "\uAEA2"}, {"\uAEA3", "\uAEA3"}, {"\uAEA4", "\uAEA4"}, {"\uAEA5", "\uAEA5"}, {"\uAEA6", "\uAEA6"}, {"\uAEA7", "\uAEA7"}, {"\uAEA8", "\uAEA8"}, {"\uAEA9", "\uAEA9"}, {"\uAEAA", "\uAEAA"}, {"\uAEAB", "\uAEAB"}, {"\uAEAC", "\uAEAC"}, {"\uAEAD", "\uAEAD"}, {"\uAEAE", "\uAEAE"}, {"\uAEAF", "\uAEAF"}, {"\uAEB0", "\uAEB0"}, {"\uAEB1", "\uAEB1"}, {"\uAEB2", "\uAEB2"}, {"\uAEB3", "\uAEB3"}, {"\uAEB4", "\uAEB4"}, {"\uAEB5", "\uAEB5"}, {"\uAEB6", "\uAEB6"}, {"\uAEB7", "\uAEB7"}, {"\uAEB8", "\uAEB8"}, {"\uAEB9", "\uAEB9"}, {"\uAEBA", "\uAEBA"}, {"\uAEBB", "\uAEBB"}, {"\uAEBC", "\uAEBC"}, {"\uAEBD", "\uAEBD"}, {"\uAEBE", "\uAEBE"}, {"\uAEBF", "\uAEBF"}, {"\uAEC0", "\uAEC0"}, {"\uAEC1", "\uAEC1"}, {"\uAEC2", "\uAEC2"}, {"\uAEC3", "\uAEC3"}, {"\uAEC4", "\uAEC4"}, {"\uAEC5", "\uAEC5"}, {"\uAEC6", "\uAEC6"}, {"\uAEC7", "\uAEC7"}, {"\uAEC8", "\uAEC8"}, {"\uAEC9", "\uAEC9"}, {"\uAECA", "\uAECA"}, {"\uAECB", "\uAECB"}, {"\uAECC", "\uAECC"}, {"\uAECD", "\uAECD"}, {"\uAECE", "\uAECE"}, {"\uAECF", "\uAECF"}, {"\uAED0", "\uAED0"}, {"\uAED1", "\uAED1"}, {"\uAED2", "\uAED2"}, {"\uAED3", "\uAED3"}, {"\uAED4", "\uAED4"}, {"\uAED5", "\uAED5"}, {"\uAED6", "\uAED6"}, {"\uAED7", "\uAED7"}, {"\uAED8", "\uAED8"}, {"\uAED9", "\uAED9"}, {"\uAEDA", "\uAEDA"}, {"\uAEDB", "\uAEDB"}, {"\uAEDC", "\uAEDC"}, {"\uAEDD", "\uAEDD"}, {"\uAEDE", "\uAEDE"}, {"\uAEDF", "\uAEDF"}, {"\uAEE0", "\uAEE0"}, {"\uAEE1", "\uAEE1"}, {"\uAEE2", "\uAEE2"}, {"\uAEE3", "\uAEE3"}, {"\uAEE4", "\uAEE4"}, {"\uAEE5", "\uAEE5"}, {"\uAEE6", "\uAEE6"}, {"\uAEE7", "\uAEE7"}, {"\uAEE8", "\uAEE8"}, {"\uAEE9", "\uAEE9"}, {"\uAEEA", "\uAEEA"}, {"\uAEEB", "\uAEEB"}, {"\uAEEC", "\uAEEC"}, {"\uAEED", "\uAEED"}, {"\uAEEE", "\uAEEE"}, {"\uAEEF", "\uAEEF"}, {"\uAEF0", "\uAEF0"}, {"\uAEF1", "\uAEF1"}, {"\uAEF2", "\uAEF2"}, {"\uAEF3", "\uAEF3"}, {"\uAEF4", "\uAEF4"}, {"\uAEF5", "\uAEF5"}, {"\uAEF6", "\uAEF6"}, {"\uAEF7", "\uAEF7"}, {"\uAEF8", "\uAEF8"}, {"\uAEF9", "\uAEF9"}, {"\uAEFA", "\uAEFA"}, {"\uAEFB", "\uAEFB"}, {"\uAEFC", "\uAEFC"}, {"\uAEFD", "\uAEFD"}, {"\uAEFE", "\uAEFE"}, {"\uAEFF", "\uAEFF"}, {"\uAF00", "\uAF00"}, {"\uAF01", "\uAF01"}, {"\uAF02", "\uAF02"}, {"\uAF03", "\uAF03"}, {"\uAF04", "\uAF04"}, {"\uAF05", "\uAF05"}, {"\uAF06", "\uAF06"}, {"\uAF07", "\uAF07"}, {"\uAF08", "\uAF08"}, {"\uAF09", "\uAF09"}, {"\uAF0A", "\uAF0A"}, {"\uAF0B", "\uAF0B"}, {"\uAF0C", "\uAF0C"}, {"\uAF0D", "\uAF0D"}, {"\uAF0E", "\uAF0E"}, {"\uAF0F", "\uAF0F"}, {"\uAF10", "\uAF10"}, {"\uAF11", "\uAF11"}, {"\uAF12", "\uAF12"}, {"\uAF13", "\uAF13"}, {"\uAF14", "\uAF14"}, {"\uAF15", "\uAF15"}, {"\uAF16", "\uAF16"}, {"\uAF17", "\uAF17"}, {"\uAF18", "\uAF18"}, {"\uAF19", "\uAF19"}, {"\uAF1A", "\uAF1A"}, {"\uAF1B", "\uAF1B"}, {"\uAF1C", "\uAF1C"}, {"\uAF1D", "\uAF1D"}, {"\uAF1E", "\uAF1E"}, {"\uAF1F", "\uAF1F"}, {"\uAF20", "\uAF20"}, {"\uAF21", "\uAF21"}, {"\uAF22", "\uAF22"}, {"\uAF23", "\uAF23"}, {"\uAF24", "\uAF24"}, {"\uAF25", "\uAF25"}, {"\uAF26", "\uAF26"}, {"\uAF27", "\uAF27"}, {"\uAF28", "\uAF28"}, {"\uAF29", "\uAF29"}, {"\uAF2A", "\uAF2A"}, {"\uAF2B", "\uAF2B"}, {"\uAF2C", "\uAF2C"}, {"\uAF2D", "\uAF2D"}, {"\uAF2E", "\uAF2E"}, {"\uAF2F", "\uAF2F"}, {"\uAF30", "\uAF30"}, {"\uAF31", "\uAF31"}, {"\uAF32", "\uAF32"}, {"\uAF33", "\uAF33"}, {"\uAF34", "\uAF34"}, {"\uAF35", "\uAF35"}, {"\uAF36", "\uAF36"}, {"\uAF37", "\uAF37"}, {"\uAF38", "\uAF38"}, {"\uAF39", "\uAF39"}, {"\uAF3A", "\uAF3A"}, {"\uAF3B", "\uAF3B"}, {"\uAF3C", "\uAF3C"}, {"\uAF3D", "\uAF3D"}, {"\uAF3E", "\uAF3E"}, {"\uAF3F", "\uAF3F"}, {"\uAF40", "\uAF40"}, {"\uAF41", "\uAF41"}, {"\uAF42", "\uAF42"}, {"\uAF43", "\uAF43"}, {"\uAF44", "\uAF44"}, {"\uAF45", "\uAF45"}, {"\uAF46", "\uAF46"}, {"\uAF47", "\uAF47"}, {"\uAF48", "\uAF48"}, {"\uAF49", "\uAF49"}, {"\uAF4A", "\uAF4A"}, {"\uAF4B", "\uAF4B"}, {"\uAF4C", "\uAF4C"}, {"\uAF4D", "\uAF4D"}, {"\uAF4E", "\uAF4E"}, {"\uAF4F", "\uAF4F"}, {"\uAF50", "\uAF50"}, {"\uAF51", "\uAF51"}, {"\uAF52", "\uAF52"}, {"\uAF53", "\uAF53"}, {"\uAF54", "\uAF54"}, {"\uAF55", "\uAF55"}, {"\uAF56", "\uAF56"}, {"\uAF57", "\uAF57"}, {"\uAF58", "\uAF58"}, {"\uAF59", "\uAF59"}, {"\uAF5A", "\uAF5A"}, {"\uAF5B", "\uAF5B"}, {"\uAF5C", "\uAF5C"}, {"\uAF5D", "\uAF5D"}, {"\uAF5E", "\uAF5E"}, {"\uAF5F", "\uAF5F"}, {"\uAF60", "\uAF60"}, {"\uAF61", "\uAF61"}, {"\uAF62", "\uAF62"}, {"\uAF63", "\uAF63"}, {"\uAF64", "\uAF64"}, {"\uAF65", "\uAF65"}, {"\uAF66", "\uAF66"}, {"\uAF67", "\uAF67"}, {"\uAF68", "\uAF68"}, {"\uAF69", "\uAF69"}, {"\uAF6A", "\uAF6A"}, {"\uAF6B", "\uAF6B"}, {"\uAF6C", "\uAF6C"}, {"\uAF6D", "\uAF6D"}, {"\uAF6E", "\uAF6E"}, {"\uAF6F", "\uAF6F"}, {"\uAF70", "\uAF70"}, {"\uAF71", "\uAF71"}, {"\uAF72", "\uAF72"}, {"\uAF73", "\uAF73"}, {"\uAF74", "\uAF74"}, {"\uAF75", "\uAF75"}, {"\uAF76", "\uAF76"}, {"\uAF77", "\uAF77"}, {"\uAF78", "\uAF78"}, {"\uAF79", "\uAF79"}, {"\uAF7A", "\uAF7A"}, {"\uAF7B", "\uAF7B"}, {"\uAF7C", "\uAF7C"}, {"\uAF7D", "\uAF7D"}, {"\uAF7E", "\uAF7E"}, {"\uAF7F", "\uAF7F"}, {"\uAF80", "\uAF80"}, {"\uAF81", "\uAF81"}, {"\uAF82", "\uAF82"}, {"\uAF83", "\uAF83"}, {"\uAF84", "\uAF84"}, {"\uAF85", "\uAF85"}, {"\uAF86", "\uAF86"}, {"\uAF87", "\uAF87"}, {"\uAF88", "\uAF88"}, {"\uAF89", "\uAF89"}, {"\uAF8A", "\uAF8A"}, {"\uAF8B", "\uAF8B"}, {"\uAF8C", "\uAF8C"}, {"\uAF8D", "\uAF8D"}, {"\uAF8E", "\uAF8E"}, {"\uAF8F", "\uAF8F"}, {"\uAF90", "\uAF90"}, {"\uAF91", "\uAF91"}, {"\uAF92", "\uAF92"}, {"\uAF93", "\uAF93"}, {"\uAF94", "\uAF94"}, {"\uAF95", "\uAF95"}, {"\uAF96", "\uAF96"}, {"\uAF97", "\uAF97"}, {"\uAF98", "\uAF98"}, {"\uAF99", "\uAF99"}, {"\uAF9A", "\uAF9A"}, {"\uAF9B", "\uAF9B"}, {"\uAF9C", "\uAF9C"}, {"\uAF9D", "\uAF9D"}, {"\uAF9E", "\uAF9E"}, {"\uAF9F", "\uAF9F"}, {"\uAFA0", "\uAFA0"}, {"\uAFA1", "\uAFA1"}, {"\uAFA2", "\uAFA2"}, {"\uAFA3", "\uAFA3"}, {"\uAFA4", "\uAFA4"}, {"\uAFA5", "\uAFA5"}, {"\uAFA6", "\uAFA6"}, {"\uAFA7", "\uAFA7"}, {"\uAFA8", "\uAFA8"}, {"\uAFA9", "\uAFA9"}, {"\uAFAA", "\uAFAA"}, {"\uAFAB", "\uAFAB"}, {"\uAFAC", "\uAFAC"}, {"\uAFAD", "\uAFAD"}, {"\uAFAE", "\uAFAE"}, {"\uAFAF", "\uAFAF"}, {"\uAFB0", "\uAFB0"}, {"\uAFB1", "\uAFB1"}, {"\uAFB2", "\uAFB2"}, {"\uAFB3", "\uAFB3"}, {"\uAFB4", "\uAFB4"}, {"\uAFB5", "\uAFB5"}, {"\uAFB6", "\uAFB6"}, {"\uAFB7", "\uAFB7"}, {"\uAFB8", "\uAFB8"}, {"\uAFB9", "\uAFB9"}, {"\uAFBA", "\uAFBA"}, {"\uAFBB", "\uAFBB"}, {"\uAFBC", "\uAFBC"}, {"\uAFBD", "\uAFBD"}, {"\uAFBE", "\uAFBE"}, {"\uAFBF", "\uAFBF"}, {"\uAFC0", "\uAFC0"}, {"\uAFC1", "\uAFC1"}, {"\uAFC2", "\uAFC2"}, {"\uAFC3", "\uAFC3"}, {"\uAFC4", "\uAFC4"}, {"\uAFC5", "\uAFC5"}, {"\uAFC6", "\uAFC6"}, {"\uAFC7", "\uAFC7"}, {"\uAFC8", "\uAFC8"}, {"\uAFC9", "\uAFC9"}, {"\uAFCA", "\uAFCA"}, {"\uAFCB", "\uAFCB"}, {"\uAFCC", "\uAFCC"}, {"\uAFCD", "\uAFCD"}, {"\uAFCE", "\uAFCE"}, {"\uAFCF", "\uAFCF"}, {"\uAFD0", "\uAFD0"}, {"\uAFD1", "\uAFD1"}, {"\uAFD2", "\uAFD2"}, {"\uAFD3", "\uAFD3"}, {"\uAFD4", "\uAFD4"}, {"\uAFD5", "\uAFD5"}, {"\uAFD6", "\uAFD6"}, {"\uAFD7", "\uAFD7"}, {"\uAFD8", "\uAFD8"}, {"\uAFD9", "\uAFD9"}, {"\uAFDA", "\uAFDA"}, {"\uAFDB", "\uAFDB"}, {"\uAFDC", "\uAFDC"}, {"\uAFDD", "\uAFDD"}, {"\uAFDE", "\uAFDE"}, {"\uAFDF", "\uAFDF"}, {"\uAFE0", "\uAFE0"}, {"\uAFE1", "\uAFE1"}, {"\uAFE2", "\uAFE2"}, {"\uAFE3", "\uAFE3"}, {"\uAFE4", "\uAFE4"}, {"\uAFE5", "\uAFE5"}, {"\uAFE6", "\uAFE6"}, {"\uAFE7", "\uAFE7"}, {"\uAFE8", "\uAFE8"}, {"\uAFE9", "\uAFE9"}, {"\uAFEA", "\uAFEA"}, {"\uAFEB", "\uAFEB"}, {"\uAFEC", "\uAFEC"}, {"\uAFED", "\uAFED"}, {"\uAFEE", "\uAFEE"}, {"\uAFEF", "\uAFEF"}, {"\uAFF0", "\uAFF0"}, {"\uAFF1", "\uAFF1"}, {"\uAFF2", "\uAFF2"}, {"\uAFF3", "\uAFF3"}, {"\uAFF4", "\uAFF4"}, {"\uAFF5", "\uAFF5"}, {"\uAFF6", "\uAFF6"}, {"\uAFF7", "\uAFF7"}, {"\uAFF8", "\uAFF8"}, {"\uAFF9", "\uAFF9"}, {"\uAFFA", "\uAFFA"}, {"\uAFFB", "\uAFFB"}, {"\uAFFC", "\uAFFC"}, {"\uAFFD", "\uAFFD"}, {"\uAFFE", "\uAFFE"}, {"\uAFFF", "\uAFFF"}, {"\uB000", "\uB000"}, {"\uB001", "\uB001"}, {"\uB002", "\uB002"}, {"\uB003", "\uB003"}, {"\uB004", "\uB004"}, {"\uB005", "\uB005"}, {"\uB006", "\uB006"}, {"\uB007", "\uB007"}, {"\uB008", "\uB008"}, {"\uB009", "\uB009"}, {"\uB00A", "\uB00A"}, {"\uB00B", "\uB00B"}, {"\uB00C", "\uB00C"}, {"\uB00D", "\uB00D"}, {"\uB00E", "\uB00E"}, {"\uB00F", "\uB00F"}, {"\uB010", "\uB010"}, {"\uB011", "\uB011"}, {"\uB012", "\uB012"}, {"\uB013", "\uB013"}, {"\uB014", "\uB014"}, {"\uB015", "\uB015"}, {"\uB016", "\uB016"}, {"\uB017", "\uB017"}, {"\uB018", "\uB018"}, {"\uB019", "\uB019"}, {"\uB01A", "\uB01A"}, {"\uB01B", "\uB01B"}, {"\uB01C", "\uB01C"}, {"\uB01D", "\uB01D"}, {"\uB01E", "\uB01E"}, {"\uB01F", "\uB01F"}, {"\uB020", "\uB020"}, {"\uB021", "\uB021"}, {"\uB022", "\uB022"}, {"\uB023", "\uB023"}, {"\uB024", "\uB024"}, {"\uB025", "\uB025"}, {"\uB026", "\uB026"}, {"\uB027", "\uB027"}, {"\uB028", "\uB028"}, {"\uB029", "\uB029"}, {"\uB02A", "\uB02A"}, {"\uB02B", "\uB02B"}, {"\uB02C", "\uB02C"}, {"\uB02D", "\uB02D"}, {"\uB02E", "\uB02E"}, {"\uB02F", "\uB02F"}, {"\uB030", "\uB030"}, {"\uB031", "\uB031"}, {"\uB032", "\uB032"}, {"\uB033", "\uB033"}, {"\uB034", "\uB034"}, {"\uB035", "\uB035"}, {"\uB036", "\uB036"}, {"\uB037", "\uB037"}, {"\uB038", "\uB038"}, {"\uB039", "\uB039"}, {"\uB03A", "\uB03A"}, {"\uB03B", "\uB03B"}, {"\uB03C", "\uB03C"}, {"\uB03D", "\uB03D"}, {"\uB03E", "\uB03E"}, {"\uB03F", "\uB03F"}, {"\uB040", "\uB040"}, {"\uB041", "\uB041"}, {"\uB042", "\uB042"}, {"\uB043", "\uB043"}, {"\uB044", "\uB044"}, {"\uB045", "\uB045"}, {"\uB046", "\uB046"}, {"\uB047", "\uB047"}, {"\uB048", "\uB048"}, {"\uB049", "\uB049"}, {"\uB04A", "\uB04A"}, {"\uB04B", "\uB04B"}, {"\uB04C", "\uB04C"}, {"\uB04D", "\uB04D"}, {"\uB04E", "\uB04E"}, {"\uB04F", "\uB04F"}, {"\uB050", "\uB050"}, {"\uB051", "\uB051"}, {"\uB052", "\uB052"}, {"\uB053", "\uB053"}, {"\uB054", "\uB054"}, {"\uB055", "\uB055"}, {"\uB056", "\uB056"}, {"\uB057", "\uB057"}, {"\uB058", "\uB058"}, {"\uB059", "\uB059"}, {"\uB05A", "\uB05A"}, {"\uB05B", "\uB05B"}, {"\uB05C", "\uB05C"}, {"\uB05D", "\uB05D"}, {"\uB05E", "\uB05E"}, {"\uB05F", "\uB05F"}, {"\uB060", "\uB060"}, {"\uB061", "\uB061"}, {"\uB062", "\uB062"}, {"\uB063", "\uB063"}, {"\uB064", "\uB064"}, {"\uB065", "\uB065"}, {"\uB066", "\uB066"}, {"\uB067", "\uB067"}, {"\uB068", "\uB068"}, {"\uB069", "\uB069"}, {"\uB06A", "\uB06A"}, {"\uB06B", "\uB06B"}, {"\uB06C", "\uB06C"}, {"\uB06D", "\uB06D"}, {"\uB06E", "\uB06E"}, {"\uB06F", "\uB06F"}, {"\uB070", "\uB070"}, {"\uB071", "\uB071"}, {"\uB072", "\uB072"}, {"\uB073", "\uB073"}, {"\uB074", "\uB074"}, {"\uB075", "\uB075"}, {"\uB076", "\uB076"}, {"\uB077", "\uB077"}, {"\uB078", "\uB078"}, {"\uB079", "\uB079"}, {"\uB07A", "\uB07A"}, {"\uB07B", "\uB07B"}, {"\uB07C", "\uB07C"}, {"\uB07D", "\uB07D"}, {"\uB07E", "\uB07E"}, {"\uB07F", "\uB07F"}, {"\uB080", "\uB080"}, {"\uB081", "\uB081"}, {"\uB082", "\uB082"}, {"\uB083", "\uB083"}, {"\uB084", "\uB084"}, {"\uB085", "\uB085"}, {"\uB086", "\uB086"}, {"\uB087", "\uB087"}, {"\uB088", "\uB088"}, {"\uB089", "\uB089"}, {"\uB08A", "\uB08A"}, {"\uB08B", "\uB08B"}, {"\uB08C", "\uB08C"}, {"\uB08D", "\uB08D"}, {"\uB08E", "\uB08E"}, {"\uB08F", "\uB08F"}, {"\uB090", "\uB090"}, {"\uB091", "\uB091"}, {"\uB092", "\uB092"}, {"\uB093", "\uB093"}, {"\uB094", "\uB094"}, {"\uB095", "\uB095"}, {"\uB096", "\uB096"}, {"\uB097", "\uB097"}, {"\uB098", "\uB098"}, {"\uB099", "\uB099"}, {"\uB09A", "\uB09A"}, {"\uB09B", "\uB09B"}, {"\uB09C", "\uB09C"}, {"\uB09D", "\uB09D"}, {"\uB09E", "\uB09E"}, {"\uB09F", "\uB09F"}, {"\uB0A0", "\uB0A0"}, {"\uB0A1", "\uB0A1"}, {"\uB0A2", "\uB0A2"}, {"\uB0A3", "\uB0A3"}, {"\uB0A4", "\uB0A4"}, {"\uB0A5", "\uB0A5"}, {"\uB0A6", "\uB0A6"}, {"\uB0A7", "\uB0A7"}, {"\uB0A8", "\uB0A8"}, {"\uB0A9", "\uB0A9"}, {"\uB0AA", "\uB0AA"}, {"\uB0AB", "\uB0AB"}, {"\uB0AC", "\uB0AC"}, {"\uB0AD", "\uB0AD"}, {"\uB0AE", "\uB0AE"}, {"\uB0AF", "\uB0AF"}, {"\uB0B0", "\uB0B0"}, {"\uB0B1", "\uB0B1"}, {"\uB0B2", "\uB0B2"}, {"\uB0B3", "\uB0B3"}, {"\uB0B4", "\uB0B4"}, {"\uB0B5", "\uB0B5"}, {"\uB0B6", "\uB0B6"}, {"\uB0B7", "\uB0B7"}, {"\uB0B8", "\uB0B8"}, {"\uB0B9", "\uB0B9"}, {"\uB0BA", "\uB0BA"}, {"\uB0BB", "\uB0BB"}, {"\uB0BC", "\uB0BC"}, {"\uB0BD", "\uB0BD"}, {"\uB0BE", "\uB0BE"}, {"\uB0BF", "\uB0BF"}, {"\uB0C0", "\uB0C0"}, {"\uB0C1", "\uB0C1"}, {"\uB0C2", "\uB0C2"}, {"\uB0C3", "\uB0C3"}, {"\uB0C4", "\uB0C4"}, {"\uB0C5", "\uB0C5"}, {"\uB0C6", "\uB0C6"}, {"\uB0C7", "\uB0C7"}, {"\uB0C8", "\uB0C8"}, {"\uB0C9", "\uB0C9"}, {"\uB0CA", "\uB0CA"}, {"\uB0CB", "\uB0CB"}, {"\uB0CC", "\uB0CC"}, {"\uB0CD", "\uB0CD"}, {"\uB0CE", "\uB0CE"}, {"\uB0CF", "\uB0CF"}, {"\uB0D0", "\uB0D0"}, {"\uB0D1", "\uB0D1"}, {"\uB0D2", "\uB0D2"}, {"\uB0D3", "\uB0D3"}, {"\uB0D4", "\uB0D4"}, {"\uB0D5", "\uB0D5"}, {"\uB0D6", "\uB0D6"}, {"\uB0D7", "\uB0D7"}, {"\uB0D8", "\uB0D8"}, {"\uB0D9", "\uB0D9"}, {"\uB0DA", "\uB0DA"}, {"\uB0DB", "\uB0DB"}, {"\uB0DC", "\uB0DC"}, {"\uB0DD", "\uB0DD"}, {"\uB0DE", "\uB0DE"}, {"\uB0DF", "\uB0DF"}, {"\uB0E0", "\uB0E0"}, {"\uB0E1", "\uB0E1"}, {"\uB0E2", "\uB0E2"}, {"\uB0E3", "\uB0E3"}, {"\uB0E4", "\uB0E4"}, {"\uB0E5", "\uB0E5"}, {"\uB0E6", "\uB0E6"}, {"\uB0E7", "\uB0E7"}, {"\uB0E8", "\uB0E8"}, {"\uB0E9", "\uB0E9"}, {"\uB0EA", "\uB0EA"}, {"\uB0EB", "\uB0EB"}, {"\uB0EC", "\uB0EC"}, {"\uB0ED", "\uB0ED"}, {"\uB0EE", "\uB0EE"}, {"\uB0EF", "\uB0EF"}, {"\uB0F0", "\uB0F0"}, {"\uB0F1", "\uB0F1"}, {"\uB0F2", "\uB0F2"}, {"\uB0F3", "\uB0F3"}, {"\uB0F4", "\uB0F4"}, {"\uB0F5", "\uB0F5"}, {"\uB0F6", "\uB0F6"}, {"\uB0F7", "\uB0F7"}, {"\uB0F8", "\uB0F8"}, {"\uB0F9", "\uB0F9"}, {"\uB0FA", "\uB0FA"}, {"\uB0FB", "\uB0FB"}, {"\uB0FC", "\uB0FC"}, {"\uB0FD", "\uB0FD"}, {"\uB0FE", "\uB0FE"}, {"\uB0FF", "\uB0FF"}, {"\uB100", "\uB100"}, {"\uB101", "\uB101"}, {"\uB102", "\uB102"}, {"\uB103", "\uB103"}, {"\uB104", "\uB104"}, {"\uB105", "\uB105"}, {"\uB106", "\uB106"}, {"\uB107", "\uB107"}, {"\uB108", "\uB108"}, {"\uB109", "\uB109"}, {"\uB10A", "\uB10A"}, {"\uB10B", "\uB10B"}, {"\uB10C", "\uB10C"}, {"\uB10D", "\uB10D"}, {"\uB10E", "\uB10E"}, {"\uB10F", "\uB10F"}, {"\uB110", "\uB110"}, {"\uB111", "\uB111"}, {"\uB112", "\uB112"}, {"\uB113", "\uB113"}, {"\uB114", "\uB114"}, {"\uB115", "\uB115"}, {"\uB116", "\uB116"}, {"\uB117", "\uB117"}, {"\uB118", "\uB118"}, {"\uB119", "\uB119"}, {"\uB11A", "\uB11A"}, {"\uB11B", "\uB11B"}, {"\uB11C", "\uB11C"}, {"\uB11D", "\uB11D"}, {"\uB11E", "\uB11E"}, {"\uB11F", "\uB11F"}, {"\uB120", "\uB120"}, {"\uB121", "\uB121"}, {"\uB122", "\uB122"}, {"\uB123", "\uB123"}, {"\uB124", "\uB124"}, {"\uB125", "\uB125"}, {"\uB126", "\uB126"}, {"\uB127", "\uB127"}, {"\uB128", "\uB128"}, {"\uB129", "\uB129"}, {"\uB12A", "\uB12A"}, {"\uB12B", "\uB12B"}, {"\uB12C", "\uB12C"}, {"\uB12D", "\uB12D"}, {"\uB12E", "\uB12E"}, {"\uB12F", "\uB12F"}, {"\uB130", "\uB130"}, {"\uB131", "\uB131"}, {"\uB132", "\uB132"}, {"\uB133", "\uB133"}, {"\uB134", "\uB134"}, {"\uB135", "\uB135"}, {"\uB136", "\uB136"}, {"\uB137", "\uB137"}, {"\uB138", "\uB138"}, {"\uB139", "\uB139"}, {"\uB13A", "\uB13A"}, {"\uB13B", "\uB13B"}, {"\uB13C", "\uB13C"}, {"\uB13D", "\uB13D"}, {"\uB13E", "\uB13E"}, {"\uB13F", "\uB13F"}, {"\uB140", "\uB140"}, {"\uB141", "\uB141"}, {"\uB142", "\uB142"}, {"\uB143", "\uB143"}, {"\uB144", "\uB144"}, {"\uB145", "\uB145"}, {"\uB146", "\uB146"}, {"\uB147", "\uB147"}, {"\uB148", "\uB148"}, {"\uB149", "\uB149"}, {"\uB14A", "\uB14A"}, {"\uB14B", "\uB14B"}, {"\uB14C", "\uB14C"}, {"\uB14D", "\uB14D"}, {"\uB14E", "\uB14E"}, {"\uB14F", "\uB14F"}, {"\uB150", "\uB150"}, {"\uB151", "\uB151"}, {"\uB152", "\uB152"}, {"\uB153", "\uB153"}, {"\uB154", "\uB154"}, {"\uB155", "\uB155"}, {"\uB156", "\uB156"}, {"\uB157", "\uB157"}, {"\uB158", "\uB158"}, {"\uB159", "\uB159"}, {"\uB15A", "\uB15A"}, {"\uB15B", "\uB15B"}, {"\uB15C", "\uB15C"}, {"\uB15D", "\uB15D"}, {"\uB15E", "\uB15E"}, {"\uB15F", "\uB15F"}, {"\uB160", "\uB160"}, {"\uB161", "\uB161"}, {"\uB162", "\uB162"}, {"\uB163", "\uB163"}, {"\uB164", "\uB164"}, {"\uB165", "\uB165"}, {"\uB166", "\uB166"}, {"\uB167", "\uB167"}, {"\uB168", "\uB168"}, {"\uB169", "\uB169"}, {"\uB16A", "\uB16A"}, {"\uB16B", "\uB16B"}, {"\uB16C", "\uB16C"}, {"\uB16D", "\uB16D"}, {"\uB16E", "\uB16E"}, {"\uB16F", "\uB16F"}, {"\uB170", "\uB170"}, {"\uB171", "\uB171"}, {"\uB172", "\uB172"}, {"\uB173", "\uB173"}, {"\uB174", "\uB174"}, {"\uB175", "\uB175"}, {"\uB176", "\uB176"}, {"\uB177", "\uB177"}, {"\uB178", "\uB178"}, {"\uB179", "\uB179"}, {"\uB17A", "\uB17A"}, {"\uB17B", "\uB17B"}, {"\uB17C", "\uB17C"}, {"\uB17D", "\uB17D"}, {"\uB17E", "\uB17E"}, {"\uB17F", "\uB17F"}, {"\uB180", "\uB180"}, {"\uB181", "\uB181"}, {"\uB182", "\uB182"}, {"\uB183", "\uB183"}, {"\uB184", "\uB184"}, {"\uB185", "\uB185"}, {"\uB186", "\uB186"}, {"\uB187", "\uB187"}, {"\uB188", "\uB188"}, {"\uB189", "\uB189"}, {"\uB18A", "\uB18A"}, {"\uB18B", "\uB18B"}, {"\uB18C", "\uB18C"}, {"\uB18D", "\uB18D"}, {"\uB18E", "\uB18E"}, {"\uB18F", "\uB18F"}, {"\uB190", "\uB190"}, {"\uB191", "\uB191"}, {"\uB192", "\uB192"}, {"\uB193", "\uB193"}, {"\uB194", "\uB194"}, {"\uB195", "\uB195"}, {"\uB196", "\uB196"}, {"\uB197", "\uB197"}, {"\uB198", "\uB198"}, {"\uB199", "\uB199"}, {"\uB19A", "\uB19A"}, {"\uB19B", "\uB19B"}, {"\uB19C", "\uB19C"}, {"\uB19D", "\uB19D"}, {"\uB19E", "\uB19E"}, {"\uB19F", "\uB19F"}, {"\uB1A0", "\uB1A0"}, {"\uB1A1", "\uB1A1"}, {"\uB1A2", "\uB1A2"}, {"\uB1A3", "\uB1A3"}, {"\uB1A4", "\uB1A4"}, {"\uB1A5", "\uB1A5"}, {"\uB1A6", "\uB1A6"}, {"\uB1A7", "\uB1A7"}, {"\uB1A8", "\uB1A8"}, {"\uB1A9", "\uB1A9"}, {"\uB1AA", "\uB1AA"}, {"\uB1AB", "\uB1AB"}, {"\uB1AC", "\uB1AC"}, {"\uB1AD", "\uB1AD"}, {"\uB1AE", "\uB1AE"}, {"\uB1AF", "\uB1AF"}, {"\uB1B0", "\uB1B0"}, {"\uB1B1", "\uB1B1"}, {"\uB1B2", "\uB1B2"}, {"\uB1B3", "\uB1B3"}, {"\uB1B4", "\uB1B4"}, {"\uB1B5", "\uB1B5"}, {"\uB1B6", "\uB1B6"}, {"\uB1B7", "\uB1B7"}, {"\uB1B8", "\uB1B8"}, {"\uB1B9", "\uB1B9"}, {"\uB1BA", "\uB1BA"}, {"\uB1BB", "\uB1BB"}, {"\uB1BC", "\uB1BC"}, {"\uB1BD", "\uB1BD"}, {"\uB1BE", "\uB1BE"}, {"\uB1BF", "\uB1BF"}, {"\uB1C0", "\uB1C0"}, {"\uB1C1", "\uB1C1"}, {"\uB1C2", "\uB1C2"}, {"\uB1C3", "\uB1C3"}, {"\uB1C4", "\uB1C4"}, {"\uB1C5", "\uB1C5"}, {"\uB1C6", "\uB1C6"}, {"\uB1C7", "\uB1C7"}, {"\uB1C8", "\uB1C8"}, {"\uB1C9", "\uB1C9"}, {"\uB1CA", "\uB1CA"}, {"\uB1CB", "\uB1CB"}, {"\uB1CC", "\uB1CC"}, {"\uB1CD", "\uB1CD"}, {"\uB1CE", "\uB1CE"}, {"\uB1CF", "\uB1CF"}, {"\uB1D0", "\uB1D0"}, {"\uB1D1", "\uB1D1"}, {"\uB1D2", "\uB1D2"}, {"\uB1D3", "\uB1D3"}, {"\uB1D4", "\uB1D4"}, {"\uB1D5", "\uB1D5"}, {"\uB1D6", "\uB1D6"}, {"\uB1D7", "\uB1D7"}, {"\uB1D8", "\uB1D8"}, {"\uB1D9", "\uB1D9"}, {"\uB1DA", "\uB1DA"}, {"\uB1DB", "\uB1DB"}, {"\uB1DC", "\uB1DC"}, {"\uB1DD", "\uB1DD"}, {"\uB1DE", "\uB1DE"}, {"\uB1DF", "\uB1DF"}, {"\uB1E0", "\uB1E0"}, {"\uB1E1", "\uB1E1"}, {"\uB1E2", "\uB1E2"}, {"\uB1E3", "\uB1E3"}, {"\uB1E4", "\uB1E4"}, {"\uB1E5", "\uB1E5"}, {"\uB1E6", "\uB1E6"}, {"\uB1E7", "\uB1E7"}, {"\uB1E8", "\uB1E8"}, {"\uB1E9", "\uB1E9"}, {"\uB1EA", "\uB1EA"}, {"\uB1EB", "\uB1EB"}, {"\uB1EC", "\uB1EC"}, {"\uB1ED", "\uB1ED"}, {"\uB1EE", "\uB1EE"}, {"\uB1EF", "\uB1EF"}, {"\uB1F0", "\uB1F0"}, {"\uB1F1", "\uB1F1"}, {"\uB1F2", "\uB1F2"}, {"\uB1F3", "\uB1F3"}, {"\uB1F4", "\uB1F4"}, {"\uB1F5", "\uB1F5"}, {"\uB1F6", "\uB1F6"}, {"\uB1F7", "\uB1F7"}, {"\uB1F8", "\uB1F8"}, {"\uB1F9", "\uB1F9"}, {"\uB1FA", "\uB1FA"}, {"\uB1FB", "\uB1FB"}, {"\uB1FC", "\uB1FC"}, {"\uB1FD", "\uB1FD"}, {"\uB1FE", "\uB1FE"}, {"\uB1FF", "\uB1FF"}, {"\uB200", "\uB200"}, {"\uB201", "\uB201"}, {"\uB202", "\uB202"}, {"\uB203", "\uB203"}, {"\uB204", "\uB204"}, {"\uB205", "\uB205"}, {"\uB206", "\uB206"}, {"\uB207", "\uB207"}, {"\uB208", "\uB208"}, {"\uB209", "\uB209"}, {"\uB20A", "\uB20A"}, {"\uB20B", "\uB20B"}, {"\uB20C", "\uB20C"}, {"\uB20D", "\uB20D"}, {"\uB20E", "\uB20E"}, {"\uB20F", "\uB20F"}, {"\uB210", "\uB210"}, {"\uB211", "\uB211"}, {"\uB212", "\uB212"}, {"\uB213", "\uB213"}, {"\uB214", "\uB214"}, {"\uB215", "\uB215"}, {"\uB216", "\uB216"}, {"\uB217", "\uB217"}, {"\uB218", "\uB218"}, {"\uB219", "\uB219"}, {"\uB21A", "\uB21A"}, {"\uB21B", "\uB21B"}, {"\uB21C", "\uB21C"}, {"\uB21D", "\uB21D"}, {"\uB21E", "\uB21E"}, {"\uB21F", "\uB21F"}, {"\uB220", "\uB220"}, {"\uB221", "\uB221"}, {"\uB222", "\uB222"}, {"\uB223", "\uB223"}, {"\uB224", "\uB224"}, {"\uB225", "\uB225"}, {"\uB226", "\uB226"}, {"\uB227", "\uB227"}, {"\uB228", "\uB228"}, {"\uB229", "\uB229"}, {"\uB22A", "\uB22A"}, {"\uB22B", "\uB22B"}, {"\uB22C", "\uB22C"}, {"\uB22D", "\uB22D"}, {"\uB22E", "\uB22E"}, {"\uB22F", "\uB22F"}, {"\uB230", "\uB230"}, {"\uB231", "\uB231"}, {"\uB232", "\uB232"}, {"\uB233", "\uB233"}, {"\uB234", "\uB234"}, {"\uB235", "\uB235"}, {"\uB236", "\uB236"}, {"\uB237", "\uB237"}, {"\uB238", "\uB238"}, {"\uB239", "\uB239"}, {"\uB23A", "\uB23A"}, {"\uB23B", "\uB23B"}, {"\uB23C", "\uB23C"}, {"\uB23D", "\uB23D"}, {"\uB23E", "\uB23E"}, {"\uB23F", "\uB23F"}, {"\uB240", "\uB240"}, {"\uB241", "\uB241"}, {"\uB242", "\uB242"}, {"\uB243", "\uB243"}, {"\uB244", "\uB244"}, {"\uB245", "\uB245"}, {"\uB246", "\uB246"}, {"\uB247", "\uB247"}, {"\uB248", "\uB248"}, {"\uB249", "\uB249"}, {"\uB24A", "\uB24A"}, {"\uB24B", "\uB24B"}, {"\uB24C", "\uB24C"}, {"\uB24D", "\uB24D"}, {"\uB24E", "\uB24E"}, {"\uB24F", "\uB24F"}, {"\uB250", "\uB250"}, {"\uB251", "\uB251"}, {"\uB252", "\uB252"}, {"\uB253", "\uB253"}, {"\uB254", "\uB254"}, {"\uB255", "\uB255"}, {"\uB256", "\uB256"}, {"\uB257", "\uB257"}, {"\uB258", "\uB258"}, {"\uB259", "\uB259"}, {"\uB25A", "\uB25A"}, {"\uB25B", "\uB25B"}, {"\uB25C", "\uB25C"}, {"\uB25D", "\uB25D"}, {"\uB25E", "\uB25E"}, {"\uB25F", "\uB25F"}, {"\uB260", "\uB260"}, {"\uB261", "\uB261"}, {"\uB262", "\uB262"}, {"\uB263", "\uB263"}, {"\uB264", "\uB264"}, {"\uB265", "\uB265"}, {"\uB266", "\uB266"}, {"\uB267", "\uB267"}, {"\uB268", "\uB268"}, {"\uB269", "\uB269"}, {"\uB26A", "\uB26A"}, {"\uB26B", "\uB26B"}, {"\uB26C", "\uB26C"}, {"\uB26D", "\uB26D"}, {"\uB26E", "\uB26E"}, {"\uB26F", "\uB26F"}, {"\uB270", "\uB270"}, {"\uB271", "\uB271"}, {"\uB272", "\uB272"}, {"\uB273", "\uB273"}, {"\uB274", "\uB274"}, {"\uB275", "\uB275"}, {"\uB276", "\uB276"}, {"\uB277", "\uB277"}, {"\uB278", "\uB278"}, {"\uB279", "\uB279"}, {"\uB27A", "\uB27A"}, {"\uB27B", "\uB27B"}, {"\uB27C", "\uB27C"}, {"\uB27D", "\uB27D"}, {"\uB27E", "\uB27E"}, {"\uB27F", "\uB27F"}, {"\uB280", "\uB280"}, {"\uB281", "\uB281"}, {"\uB282", "\uB282"}, {"\uB283", "\uB283"}, {"\uB284", "\uB284"}, {"\uB285", "\uB285"}, {"\uB286", "\uB286"}, {"\uB287", "\uB287"}, {"\uB288", "\uB288"}, {"\uB289", "\uB289"}, {"\uB28A", "\uB28A"}, {"\uB28B", "\uB28B"}, {"\uB28C", "\uB28C"}, {"\uB28D", "\uB28D"}, {"\uB28E", "\uB28E"}, {"\uB28F", "\uB28F"}, {"\uB290", "\uB290"}, {"\uB291", "\uB291"}, {"\uB292", "\uB292"}, {"\uB293", "\uB293"}, {"\uB294", "\uB294"}, {"\uB295", "\uB295"}, {"\uB296", "\uB296"}, {"\uB297", "\uB297"}, {"\uB298", "\uB298"}, {"\uB299", "\uB299"}, {"\uB29A", "\uB29A"}, {"\uB29B", "\uB29B"}, {"\uB29C", "\uB29C"}, {"\uB29D", "\uB29D"}, {"\uB29E", "\uB29E"}, {"\uB29F", "\uB29F"}, {"\uB2A0", "\uB2A0"}, {"\uB2A1", "\uB2A1"}, {"\uB2A2", "\uB2A2"}, {"\uB2A3", "\uB2A3"}, {"\uB2A4", "\uB2A4"}, {"\uB2A5", "\uB2A5"}, {"\uB2A6", "\uB2A6"}, {"\uB2A7", "\uB2A7"}, {"\uB2A8", "\uB2A8"}, {"\uB2A9", "\uB2A9"}, {"\uB2AA", "\uB2AA"}, {"\uB2AB", "\uB2AB"}, {"\uB2AC", "\uB2AC"}, {"\uB2AD", "\uB2AD"}, {"\uB2AE", "\uB2AE"}, {"\uB2AF", "\uB2AF"}, {"\uB2B0", "\uB2B0"}, {"\uB2B1", "\uB2B1"}, {"\uB2B2", "\uB2B2"}, {"\uB2B3", "\uB2B3"}, {"\uB2B4", "\uB2B4"}, {"\uB2B5", "\uB2B5"}, {"\uB2B6", "\uB2B6"}, {"\uB2B7", "\uB2B7"}, {"\uB2B8", "\uB2B8"}, {"\uB2B9", "\uB2B9"}, {"\uB2BA", "\uB2BA"}, {"\uB2BB", "\uB2BB"}, {"\uB2BC", "\uB2BC"}, {"\uB2BD", "\uB2BD"}, {"\uB2BE", "\uB2BE"}, {"\uB2BF", "\uB2BF"}, {"\uB2C0", "\uB2C0"}, {"\uB2C1", "\uB2C1"}, {"\uB2C2", "\uB2C2"}, {"\uB2C3", "\uB2C3"}, {"\uB2C4", "\uB2C4"}, {"\uB2C5", "\uB2C5"}, {"\uB2C6", "\uB2C6"}, {"\uB2C7", "\uB2C7"}, {"\uB2C8", "\uB2C8"}, {"\uB2C9", "\uB2C9"}, {"\uB2CA", "\uB2CA"}, {"\uB2CB", "\uB2CB"}, {"\uB2CC", "\uB2CC"}, {"\uB2CD", "\uB2CD"}, {"\uB2CE", "\uB2CE"}, {"\uB2CF", "\uB2CF"}, {"\uB2D0", "\uB2D0"}, {"\uB2D1", "\uB2D1"}, {"\uB2D2", "\uB2D2"}, {"\uB2D3", "\uB2D3"}, {"\uB2D4", "\uB2D4"}, {"\uB2D5", "\uB2D5"}, {"\uB2D6", "\uB2D6"}, {"\uB2D7", "\uB2D7"}, {"\uB2D8", "\uB2D8"}, {"\uB2D9", "\uB2D9"}, {"\uB2DA", "\uB2DA"}, {"\uB2DB", "\uB2DB"}, {"\uB2DC", "\uB2DC"}, {"\uB2DD", "\uB2DD"}, {"\uB2DE", "\uB2DE"}, {"\uB2DF", "\uB2DF"}, {"\uB2E0", "\uB2E0"}, {"\uB2E1", "\uB2E1"}, {"\uB2E2", "\uB2E2"}, {"\uB2E3", "\uB2E3"}, {"\uB2E4", "\uB2E4"}, {"\uB2E5", "\uB2E5"}, {"\uB2E6", "\uB2E6"}, {"\uB2E7", "\uB2E7"}, {"\uB2E8", "\uB2E8"}, {"\uB2E9", "\uB2E9"}, {"\uB2EA", "\uB2EA"}, {"\uB2EB", "\uB2EB"}, {"\uB2EC", "\uB2EC"}, {"\uB2ED", "\uB2ED"}, {"\uB2EE", "\uB2EE"}, {"\uB2EF", "\uB2EF"}, {"\uB2F0", "\uB2F0"}, {"\uB2F1", "\uB2F1"}, {"\uB2F2", "\uB2F2"}, {"\uB2F3", "\uB2F3"}, {"\uB2F4", "\uB2F4"}, {"\uB2F5", "\uB2F5"}, {"\uB2F6", "\uB2F6"}, {"\uB2F7", "\uB2F7"}, {"\uB2F8", "\uB2F8"}, {"\uB2F9", "\uB2F9"}, {"\uB2FA", "\uB2FA"}, {"\uB2FB", "\uB2FB"}, {"\uB2FC", "\uB2FC"}, {"\uB2FD", "\uB2FD"}, {"\uB2FE", "\uB2FE"}, {"\uB2FF", "\uB2FF"}, {"\uB300", "\uB300"}, {"\uB301", "\uB301"}, {"\uB302", "\uB302"}, {"\uB303", "\uB303"}, {"\uB304", "\uB304"}, {"\uB305", "\uB305"}, {"\uB306", "\uB306"}, {"\uB307", "\uB307"}, {"\uB308", "\uB308"}, {"\uB309", "\uB309"}, {"\uB30A", "\uB30A"}, {"\uB30B", "\uB30B"}, {"\uB30C", "\uB30C"}, {"\uB30D", "\uB30D"}, {"\uB30E", "\uB30E"}, {"\uB30F", "\uB30F"}, {"\uB310", "\uB310"}, {"\uB311", "\uB311"}, {"\uB312", "\uB312"}, {"\uB313", "\uB313"}, {"\uB314", "\uB314"}, {"\uB315", "\uB315"}, {"\uB316", "\uB316"}, {"\uB317", "\uB317"}, {"\uB318", "\uB318"}, {"\uB319", "\uB319"}, {"\uB31A", "\uB31A"}, {"\uB31B", "\uB31B"}, {"\uB31C", "\uB31C"}, {"\uB31D", "\uB31D"}, {"\uB31E", "\uB31E"}, {"\uB31F", "\uB31F"}, {"\uB320", "\uB320"}, {"\uB321", "\uB321"}, {"\uB322", "\uB322"}, {"\uB323", "\uB323"}, {"\uB324", "\uB324"}, {"\uB325", "\uB325"}, {"\uB326", "\uB326"}, {"\uB327", "\uB327"}, {"\uB328", "\uB328"}, {"\uB329", "\uB329"}, {"\uB32A", "\uB32A"}, {"\uB32B", "\uB32B"}, {"\uB32C", "\uB32C"}, {"\uB32D", "\uB32D"}, {"\uB32E", "\uB32E"}, {"\uB32F", "\uB32F"}, {"\uB330", "\uB330"}, {"\uB331", "\uB331"}, {"\uB332", "\uB332"}, {"\uB333", "\uB333"}, {"\uB334", "\uB334"}, {"\uB335", "\uB335"}, {"\uB336", "\uB336"}, {"\uB337", "\uB337"}, {"\uB338", "\uB338"}, {"\uB339", "\uB339"}, {"\uB33A", "\uB33A"}, {"\uB33B", "\uB33B"}, {"\uB33C", "\uB33C"}, {"\uB33D", "\uB33D"}, {"\uB33E", "\uB33E"}, {"\uB33F", "\uB33F"}, {"\uB340", "\uB340"}, {"\uB341", "\uB341"}, {"\uB342", "\uB342"}, {"\uB343", "\uB343"}, {"\uB344", "\uB344"}, {"\uB345", "\uB345"}, {"\uB346", "\uB346"}, {"\uB347", "\uB347"}, {"\uB348", "\uB348"}, {"\uB349", "\uB349"}, {"\uB34A", "\uB34A"}, {"\uB34B", "\uB34B"}, {"\uB34C", "\uB34C"}, {"\uB34D", "\uB34D"}, {"\uB34E", "\uB34E"}, {"\uB34F", "\uB34F"}, {"\uB350", "\uB350"}, {"\uB351", "\uB351"}, {"\uB352", "\uB352"}, {"\uB353", "\uB353"}, {"\uB354", "\uB354"}, {"\uB355", "\uB355"}, {"\uB356", "\uB356"}, {"\uB357", "\uB357"}, {"\uB358", "\uB358"}, {"\uB359", "\uB359"}, {"\uB35A", "\uB35A"}, {"\uB35B", "\uB35B"}, {"\uB35C", "\uB35C"}, {"\uB35D", "\uB35D"}, {"\uB35E", "\uB35E"}, {"\uB35F", "\uB35F"}, {"\uB360", "\uB360"}, {"\uB361", "\uB361"}, {"\uB362", "\uB362"}, {"\uB363", "\uB363"}, {"\uB364", "\uB364"}, {"\uB365", "\uB365"}, {"\uB366", "\uB366"}, {"\uB367", "\uB367"}, {"\uB368", "\uB368"}, {"\uB369", "\uB369"}, {"\uB36A", "\uB36A"}, {"\uB36B", "\uB36B"}, {"\uB36C", "\uB36C"}, {"\uB36D", "\uB36D"}, {"\uB36E", "\uB36E"}, {"\uB36F", "\uB36F"}, {"\uB370", "\uB370"}, {"\uB371", "\uB371"}, {"\uB372", "\uB372"}, {"\uB373", "\uB373"}, {"\uB374", "\uB374"}, {"\uB375", "\uB375"}, {"\uB376", "\uB376"}, {"\uB377", "\uB377"}, {"\uB378", "\uB378"}, {"\uB379", "\uB379"}, {"\uB37A", "\uB37A"}, {"\uB37B", "\uB37B"}, {"\uB37C", "\uB37C"}, {"\uB37D", "\uB37D"}, {"\uB37E", "\uB37E"}, {"\uB37F", "\uB37F"}, {"\uB380", "\uB380"}, {"\uB381", "\uB381"}, {"\uB382", "\uB382"}, {"\uB383", "\uB383"}, {"\uB384", "\uB384"}, {"\uB385", "\uB385"}, {"\uB386", "\uB386"}, {"\uB387", "\uB387"}, {"\uB388", "\uB388"}, {"\uB389", "\uB389"}, {"\uB38A", "\uB38A"}, {"\uB38B", "\uB38B"}, {"\uB38C", "\uB38C"}, {"\uB38D", "\uB38D"}, {"\uB38E", "\uB38E"}, {"\uB38F", "\uB38F"}, {"\uB390", "\uB390"}, {"\uB391", "\uB391"}, {"\uB392", "\uB392"}, {"\uB393", "\uB393"}, {"\uB394", "\uB394"}, {"\uB395", "\uB395"}, {"\uB396", "\uB396"}, {"\uB397", "\uB397"}, {"\uB398", "\uB398"}, {"\uB399", "\uB399"}, {"\uB39A", "\uB39A"}, {"\uB39B", "\uB39B"}, {"\uB39C", "\uB39C"}, {"\uB39D", "\uB39D"}, {"\uB39E", "\uB39E"}, {"\uB39F", "\uB39F"}, {"\uB3A0", "\uB3A0"}, {"\uB3A1", "\uB3A1"}, {"\uB3A2", "\uB3A2"}, {"\uB3A3", "\uB3A3"}, {"\uB3A4", "\uB3A4"}, {"\uB3A5", "\uB3A5"}, {"\uB3A6", "\uB3A6"}, {"\uB3A7", "\uB3A7"}, {"\uB3A8", "\uB3A8"}, {"\uB3A9", "\uB3A9"}, {"\uB3AA", "\uB3AA"}, {"\uB3AB", "\uB3AB"}, {"\uB3AC", "\uB3AC"}, {"\uB3AD", "\uB3AD"}, {"\uB3AE", "\uB3AE"}, {"\uB3AF", "\uB3AF"}, {"\uB3B0", "\uB3B0"}, {"\uB3B1", "\uB3B1"}, {"\uB3B2", "\uB3B2"}, {"\uB3B3", "\uB3B3"}, {"\uB3B4", "\uB3B4"}, {"\uB3B5", "\uB3B5"}, {"\uB3B6", "\uB3B6"}, {"\uB3B7", "\uB3B7"}, {"\uB3B8", "\uB3B8"}, {"\uB3B9", "\uB3B9"}, {"\uB3BA", "\uB3BA"}, {"\uB3BB", "\uB3BB"}, {"\uB3BC", "\uB3BC"}, {"\uB3BD", "\uB3BD"}, {"\uB3BE", "\uB3BE"}, {"\uB3BF", "\uB3BF"}, {"\uB3C0", "\uB3C0"}, {"\uB3C1", "\uB3C1"}, {"\uB3C2", "\uB3C2"}, {"\uB3C3", "\uB3C3"}, {"\uB3C4", "\uB3C4"}, {"\uB3C5", "\uB3C5"}, {"\uB3C6", "\uB3C6"}, {"\uB3C7", "\uB3C7"}, {"\uB3C8", "\uB3C8"}, {"\uB3C9", "\uB3C9"}, {"\uB3CA", "\uB3CA"}, {"\uB3CB", "\uB3CB"}, {"\uB3CC", "\uB3CC"}, {"\uB3CD", "\uB3CD"}, {"\uB3CE", "\uB3CE"}, {"\uB3CF", "\uB3CF"}, {"\uB3D0", "\uB3D0"}, {"\uB3D1", "\uB3D1"}, {"\uB3D2", "\uB3D2"}, {"\uB3D3", "\uB3D3"}, {"\uB3D4", "\uB3D4"}, {"\uB3D5", "\uB3D5"}, {"\uB3D6", "\uB3D6"}, {"\uB3D7", "\uB3D7"}, {"\uB3D8", "\uB3D8"}, {"\uB3D9", "\uB3D9"}, {"\uB3DA", "\uB3DA"}, {"\uB3DB", "\uB3DB"}, {"\uB3DC", "\uB3DC"}, {"\uB3DD", "\uB3DD"}, {"\uB3DE", "\uB3DE"}, {"\uB3DF", "\uB3DF"}, {"\uB3E0", "\uB3E0"}, {"\uB3E1", "\uB3E1"}, {"\uB3E2", "\uB3E2"}, {"\uB3E3", "\uB3E3"}, {"\uB3E4", "\uB3E4"}, {"\uB3E5", "\uB3E5"}, {"\uB3E6", "\uB3E6"}, {"\uB3E7", "\uB3E7"}, {"\uB3E8", "\uB3E8"}, {"\uB3E9", "\uB3E9"}, {"\uB3EA", "\uB3EA"}, {"\uB3EB", "\uB3EB"}, {"\uB3EC", "\uB3EC"}, {"\uB3ED", "\uB3ED"}, {"\uB3EE", "\uB3EE"}, {"\uB3EF", "\uB3EF"}, {"\uB3F0", "\uB3F0"}, {"\uB3F1", "\uB3F1"}, {"\uB3F2", "\uB3F2"}, {"\uB3F3", "\uB3F3"}, {"\uB3F4", "\uB3F4"}, {"\uB3F5", "\uB3F5"}, {"\uB3F6", "\uB3F6"}, {"\uB3F7", "\uB3F7"}, {"\uB3F8", "\uB3F8"}, {"\uB3F9", "\uB3F9"}, {"\uB3FA", "\uB3FA"}, {"\uB3FB", "\uB3FB"}, {"\uB3FC", "\uB3FC"}, {"\uB3FD", "\uB3FD"}, {"\uB3FE", "\uB3FE"}, {"\uB3FF", "\uB3FF"}, {"\uB400", "\uB400"}, {"\uB401", "\uB401"}, {"\uB402", "\uB402"}, {"\uB403", "\uB403"}, {"\uB404", "\uB404"}, {"\uB405", "\uB405"}, {"\uB406", "\uB406"}, {"\uB407", "\uB407"}, {"\uB408", "\uB408"}, {"\uB409", "\uB409"}, {"\uB40A", "\uB40A"}, {"\uB40B", "\uB40B"}, {"\uB40C", "\uB40C"}, {"\uB40D", "\uB40D"}, {"\uB40E", "\uB40E"}, {"\uB40F", "\uB40F"}, {"\uB410", "\uB410"}, {"\uB411", "\uB411"}, {"\uB412", "\uB412"}, {"\uB413", "\uB413"}, {"\uB414", "\uB414"}, {"\uB415", "\uB415"}, {"\uB416", "\uB416"}, {"\uB417", "\uB417"}, {"\uB418", "\uB418"}, {"\uB419", "\uB419"}, {"\uB41A", "\uB41A"}, {"\uB41B", "\uB41B"}, {"\uB41C", "\uB41C"}, {"\uB41D", "\uB41D"}, {"\uB41E", "\uB41E"}, {"\uB41F", "\uB41F"}, {"\uB420", "\uB420"}, {"\uB421", "\uB421"}, {"\uB422", "\uB422"}, {"\uB423", "\uB423"}, {"\uB424", "\uB424"}, {"\uB425", "\uB425"}, {"\uB426", "\uB426"}, {"\uB427", "\uB427"}, {"\uB428", "\uB428"}, {"\uB429", "\uB429"}, {"\uB42A", "\uB42A"}, {"\uB42B", "\uB42B"}, {"\uB42C", "\uB42C"}, {"\uB42D", "\uB42D"}, {"\uB42E", "\uB42E"}, {"\uB42F", "\uB42F"}, {"\uB430", "\uB430"}, {"\uB431", "\uB431"}, {"\uB432", "\uB432"}, {"\uB433", "\uB433"}, {"\uB434", "\uB434"}, {"\uB435", "\uB435"}, {"\uB436", "\uB436"}, {"\uB437", "\uB437"}, {"\uB438", "\uB438"}, {"\uB439", "\uB439"}, {"\uB43A", "\uB43A"}, {"\uB43B", "\uB43B"}, {"\uB43C", "\uB43C"}, {"\uB43D", "\uB43D"}, {"\uB43E", "\uB43E"}, {"\uB43F", "\uB43F"}, {"\uB440", "\uB440"}, {"\uB441", "\uB441"}, {"\uB442", "\uB442"}, {"\uB443", "\uB443"}, {"\uB444", "\uB444"}, {"\uB445", "\uB445"}, {"\uB446", "\uB446"}, {"\uB447", "\uB447"}, {"\uB448", "\uB448"}, {"\uB449", "\uB449"}, {"\uB44A", "\uB44A"}, {"\uB44B", "\uB44B"}, {"\uB44C", "\uB44C"}, {"\uB44D", "\uB44D"}, {"\uB44E", "\uB44E"}, {"\uB44F", "\uB44F"}, {"\uB450", "\uB450"}, {"\uB451", "\uB451"}, {"\uB452", "\uB452"}, {"\uB453", "\uB453"}, {"\uB454", "\uB454"}, {"\uB455", "\uB455"}, {"\uB456", "\uB456"}, {"\uB457", "\uB457"}, {"\uB458", "\uB458"}, {"\uB459", "\uB459"}, {"\uB45A", "\uB45A"}, {"\uB45B", "\uB45B"}, {"\uB45C", "\uB45C"}, {"\uB45D", "\uB45D"}, {"\uB45E", "\uB45E"}, {"\uB45F", "\uB45F"}, {"\uB460", "\uB460"}, {"\uB461", "\uB461"}, {"\uB462", "\uB462"}, {"\uB463", "\uB463"}, {"\uB464", "\uB464"}, {"\uB465", "\uB465"}, {"\uB466", "\uB466"}, {"\uB467", "\uB467"}, {"\uB468", "\uB468"}, {"\uB469", "\uB469"}, {"\uB46A", "\uB46A"}, {"\uB46B", "\uB46B"}, {"\uB46C", "\uB46C"}, {"\uB46D", "\uB46D"}, {"\uB46E", "\uB46E"}, {"\uB46F", "\uB46F"}, {"\uB470", "\uB470"}, {"\uB471", "\uB471"}, {"\uB472", "\uB472"}, {"\uB473", "\uB473"}, {"\uB474", "\uB474"}, {"\uB475", "\uB475"}, {"\uB476", "\uB476"}, {"\uB477", "\uB477"}, {"\uB478", "\uB478"}, {"\uB479", "\uB479"}, {"\uB47A", "\uB47A"}, {"\uB47B", "\uB47B"}, {"\uB47C", "\uB47C"}, {"\uB47D", "\uB47D"}, {"\uB47E", "\uB47E"}, {"\uB47F", "\uB47F"}, {"\uB480", "\uB480"}, {"\uB481", "\uB481"}, {"\uB482", "\uB482"}, {"\uB483", "\uB483"}, {"\uB484", "\uB484"}, {"\uB485", "\uB485"}, {"\uB486", "\uB486"}, {"\uB487", "\uB487"}, {"\uB488", "\uB488"}, {"\uB489", "\uB489"}, {"\uB48A", "\uB48A"}, {"\uB48B", "\uB48B"}, {"\uB48C", "\uB48C"}, {"\uB48D", "\uB48D"}, {"\uB48E", "\uB48E"}, {"\uB48F", "\uB48F"}, {"\uB490", "\uB490"}, {"\uB491", "\uB491"}, {"\uB492", "\uB492"}, {"\uB493", "\uB493"}, {"\uB494", "\uB494"}, {"\uB495", "\uB495"}, {"\uB496", "\uB496"}, {"\uB497", "\uB497"}, {"\uB498", "\uB498"}, {"\uB499", "\uB499"}, {"\uB49A", "\uB49A"}, {"\uB49B", "\uB49B"}, {"\uB49C", "\uB49C"}, {"\uB49D", "\uB49D"}, {"\uB49E", "\uB49E"}, {"\uB49F", "\uB49F"}, {"\uB4A0", "\uB4A0"}, {"\uB4A1", "\uB4A1"}, {"\uB4A2", "\uB4A2"}, {"\uB4A3", "\uB4A3"}, {"\uB4A4", "\uB4A4"}, {"\uB4A5", "\uB4A5"}, {"\uB4A6", "\uB4A6"}, {"\uB4A7", "\uB4A7"}, {"\uB4A8", "\uB4A8"}, {"\uB4A9", "\uB4A9"}, {"\uB4AA", "\uB4AA"}, {"\uB4AB", "\uB4AB"}, {"\uB4AC", "\uB4AC"}, {"\uB4AD", "\uB4AD"}, {"\uB4AE", "\uB4AE"}, {"\uB4AF", "\uB4AF"}, {"\uB4B0", "\uB4B0"}, {"\uB4B1", "\uB4B1"}, {"\uB4B2", "\uB4B2"}, {"\uB4B3", "\uB4B3"}, {"\uB4B4", "\uB4B4"}, {"\uB4B5", "\uB4B5"}, {"\uB4B6", "\uB4B6"}, {"\uB4B7", "\uB4B7"}, {"\uB4B8", "\uB4B8"}, {"\uB4B9", "\uB4B9"}, {"\uB4BA", "\uB4BA"}, {"\uB4BB", "\uB4BB"}, {"\uB4BC", "\uB4BC"}, {"\uB4BD", "\uB4BD"}, {"\uB4BE", "\uB4BE"}, {"\uB4BF", "\uB4BF"}, {"\uB4C0", "\uB4C0"}, {"\uB4C1", "\uB4C1"}, {"\uB4C2", "\uB4C2"}, {"\uB4C3", "\uB4C3"}, {"\uB4C4", "\uB4C4"}, {"\uB4C5", "\uB4C5"}, {"\uB4C6", "\uB4C6"}, {"\uB4C7", "\uB4C7"}, {"\uB4C8", "\uB4C8"}, {"\uB4C9", "\uB4C9"}, {"\uB4CA", "\uB4CA"}, {"\uB4CB", "\uB4CB"}, {"\uB4CC", "\uB4CC"}, {"\uB4CD", "\uB4CD"}, {"\uB4CE", "\uB4CE"}, {"\uB4CF", "\uB4CF"}, {"\uB4D0", "\uB4D0"}, {"\uB4D1", "\uB4D1"}, {"\uB4D2", "\uB4D2"}, {"\uB4D3", "\uB4D3"}, {"\uB4D4", "\uB4D4"}, {"\uB4D5", "\uB4D5"}, {"\uB4D6", "\uB4D6"}, {"\uB4D7", "\uB4D7"}, {"\uB4D8", "\uB4D8"}, {"\uB4D9", "\uB4D9"}, {"\uB4DA", "\uB4DA"}, {"\uB4DB", "\uB4DB"}, {"\uB4DC", "\uB4DC"}, {"\uB4DD", "\uB4DD"}, {"\uB4DE", "\uB4DE"}, {"\uB4DF", "\uB4DF"}, {"\uB4E0", "\uB4E0"}, {"\uB4E1", "\uB4E1"}, {"\uB4E2", "\uB4E2"}, {"\uB4E3", "\uB4E3"}, {"\uB4E4", "\uB4E4"}, {"\uB4E5", "\uB4E5"}, {"\uB4E6", "\uB4E6"}, {"\uB4E7", "\uB4E7"}, {"\uB4E8", "\uB4E8"}, {"\uB4E9", "\uB4E9"}, {"\uB4EA", "\uB4EA"}, {"\uB4EB", "\uB4EB"}, {"\uB4EC", "\uB4EC"}, {"\uB4ED", "\uB4ED"}, {"\uB4EE", "\uB4EE"}, {"\uB4EF", "\uB4EF"}, {"\uB4F0", "\uB4F0"}, {"\uB4F1", "\uB4F1"}, {"\uB4F2", "\uB4F2"}, {"\uB4F3", "\uB4F3"}, {"\uB4F4", "\uB4F4"}, {"\uB4F5", "\uB4F5"}, {"\uB4F6", "\uB4F6"}, {"\uB4F7", "\uB4F7"}, {"\uB4F8", "\uB4F8"}, {"\uB4F9", "\uB4F9"}, {"\uB4FA", "\uB4FA"}, {"\uB4FB", "\uB4FB"}, {"\uB4FC", "\uB4FC"}, {"\uB4FD", "\uB4FD"}, {"\uB4FE", "\uB4FE"}, {"\uB4FF", "\uB4FF"}, {"\uB500", "\uB500"}, {"\uB501", "\uB501"}, {"\uB502", "\uB502"}, {"\uB503", "\uB503"}, {"\uB504", "\uB504"}, {"\uB505", "\uB505"}, {"\uB506", "\uB506"}, {"\uB507", "\uB507"}, {"\uB508", "\uB508"}, {"\uB509", "\uB509"}, {"\uB50A", "\uB50A"}, {"\uB50B", "\uB50B"}, {"\uB50C", "\uB50C"}, {"\uB50D", "\uB50D"}, {"\uB50E", "\uB50E"}, {"\uB50F", "\uB50F"}, {"\uB510", "\uB510"}, {"\uB511", "\uB511"}, {"\uB512", "\uB512"}, {"\uB513", "\uB513"}, {"\uB514", "\uB514"}, {"\uB515", "\uB515"}, {"\uB516", "\uB516"}, {"\uB517", "\uB517"}, {"\uB518", "\uB518"}, {"\uB519", "\uB519"}, {"\uB51A", "\uB51A"}, {"\uB51B", "\uB51B"}, {"\uB51C", "\uB51C"}, {"\uB51D", "\uB51D"}, {"\uB51E", "\uB51E"}, {"\uB51F", "\uB51F"}, {"\uB520", "\uB520"}, {"\uB521", "\uB521"}, {"\uB522", "\uB522"}, {"\uB523", "\uB523"}, {"\uB524", "\uB524"}, {"\uB525", "\uB525"}, {"\uB526", "\uB526"}, {"\uB527", "\uB527"}, {"\uB528", "\uB528"}, {"\uB529", "\uB529"}, {"\uB52A", "\uB52A"}, {"\uB52B", "\uB52B"}, {"\uB52C", "\uB52C"}, {"\uB52D", "\uB52D"}, {"\uB52E", "\uB52E"}, {"\uB52F", "\uB52F"}, {"\uB530", "\uB530"}, {"\uB531", "\uB531"}, {"\uB532", "\uB532"}, {"\uB533", "\uB533"}, {"\uB534", "\uB534"}, {"\uB535", "\uB535"}, {"\uB536", "\uB536"}, {"\uB537", "\uB537"}, {"\uB538", "\uB538"}, {"\uB539", "\uB539"}, {"\uB53A", "\uB53A"}, {"\uB53B", "\uB53B"}, {"\uB53C", "\uB53C"}, {"\uB53D", "\uB53D"}, {"\uB53E", "\uB53E"}, {"\uB53F", "\uB53F"}, {"\uB540", "\uB540"}, {"\uB541", "\uB541"}, {"\uB542", "\uB542"}, {"\uB543", "\uB543"}, {"\uB544", "\uB544"}, {"\uB545", "\uB545"}, {"\uB546", "\uB546"}, {"\uB547", "\uB547"}, {"\uB548", "\uB548"}, {"\uB549", "\uB549"}, {"\uB54A", "\uB54A"}, {"\uB54B", "\uB54B"}, {"\uB54C", "\uB54C"}, {"\uB54D", "\uB54D"}, {"\uB54E", "\uB54E"}, {"\uB54F", "\uB54F"}, {"\uB550", "\uB550"}, {"\uB551", "\uB551"}, {"\uB552", "\uB552"}, {"\uB553", "\uB553"}, {"\uB554", "\uB554"}, {"\uB555", "\uB555"}, {"\uB556", "\uB556"}, {"\uB557", "\uB557"}, {"\uB558", "\uB558"}, {"\uB559", "\uB559"}, {"\uB55A", "\uB55A"}, {"\uB55B", "\uB55B"}, {"\uB55C", "\uB55C"}, {"\uB55D", "\uB55D"}, {"\uB55E", "\uB55E"}, {"\uB55F", "\uB55F"}, {"\uB560", "\uB560"}, {"\uB561", "\uB561"}, {"\uB562", "\uB562"}, {"\uB563", "\uB563"}, {"\uB564", "\uB564"}, {"\uB565", "\uB565"}, {"\uB566", "\uB566"}, {"\uB567", "\uB567"}, {"\uB568", "\uB568"}, {"\uB569", "\uB569"}, {"\uB56A", "\uB56A"}, {"\uB56B", "\uB56B"}, {"\uB56C", "\uB56C"}, {"\uB56D", "\uB56D"}, {"\uB56E", "\uB56E"}, {"\uB56F", "\uB56F"}, {"\uB570", "\uB570"}, {"\uB571", "\uB571"}, {"\uB572", "\uB572"}, {"\uB573", "\uB573"}, {"\uB574", "\uB574"}, {"\uB575", "\uB575"}, {"\uB576", "\uB576"}, {"\uB577", "\uB577"}, {"\uB578", "\uB578"}, {"\uB579", "\uB579"}, {"\uB57A", "\uB57A"}, {"\uB57B", "\uB57B"}, {"\uB57C", "\uB57C"}, {"\uB57D", "\uB57D"}, {"\uB57E", "\uB57E"}, {"\uB57F", "\uB57F"}, {"\uB580", "\uB580"}, {"\uB581", "\uB581"}, {"\uB582", "\uB582"}, {"\uB583", "\uB583"}, {"\uB584", "\uB584"}, {"\uB585", "\uB585"}, {"\uB586", "\uB586"}, {"\uB587", "\uB587"}, {"\uB588", "\uB588"}, {"\uB589", "\uB589"}, {"\uB58A", "\uB58A"}, {"\uB58B", "\uB58B"}, {"\uB58C", "\uB58C"}, {"\uB58D", "\uB58D"}, {"\uB58E", "\uB58E"}, {"\uB58F", "\uB58F"}, {"\uB590", "\uB590"}, {"\uB591", "\uB591"}, {"\uB592", "\uB592"}, {"\uB593", "\uB593"}, {"\uB594", "\uB594"}, {"\uB595", "\uB595"}, {"\uB596", "\uB596"}, {"\uB597", "\uB597"}, {"\uB598", "\uB598"}, {"\uB599", "\uB599"}, {"\uB59A", "\uB59A"}, {"\uB59B", "\uB59B"}, {"\uB59C", "\uB59C"}, {"\uB59D", "\uB59D"}, {"\uB59E", "\uB59E"}, {"\uB59F", "\uB59F"}, {"\uB5A0", "\uB5A0"}, {"\uB5A1", "\uB5A1"}, {"\uB5A2", "\uB5A2"}, {"\uB5A3", "\uB5A3"}, {"\uB5A4", "\uB5A4"}, {"\uB5A5", "\uB5A5"}, {"\uB5A6", "\uB5A6"}, {"\uB5A7", "\uB5A7"}, {"\uB5A8", "\uB5A8"}, {"\uB5A9", "\uB5A9"}, {"\uB5AA", "\uB5AA"}, {"\uB5AB", "\uB5AB"}, {"\uB5AC", "\uB5AC"}, {"\uB5AD", "\uB5AD"}, {"\uB5AE", "\uB5AE"}, {"\uB5AF", "\uB5AF"}, {"\uB5B0", "\uB5B0"}, {"\uB5B1", "\uB5B1"}, {"\uB5B2", "\uB5B2"}, {"\uB5B3", "\uB5B3"}, {"\uB5B4", "\uB5B4"}, {"\uB5B5", "\uB5B5"}, {"\uB5B6", "\uB5B6"}, {"\uB5B7", "\uB5B7"}, {"\uB5B8", "\uB5B8"}, {"\uB5B9", "\uB5B9"}, {"\uB5BA", "\uB5BA"}, {"\uB5BB", "\uB5BB"}, {"\uB5BC", "\uB5BC"}, {"\uB5BD", "\uB5BD"}, {"\uB5BE", "\uB5BE"}, {"\uB5BF", "\uB5BF"}, {"\uB5C0", "\uB5C0"}, {"\uB5C1", "\uB5C1"}, {"\uB5C2", "\uB5C2"}, {"\uB5C3", "\uB5C3"}, {"\uB5C4", "\uB5C4"}, {"\uB5C5", "\uB5C5"}, {"\uB5C6", "\uB5C6"}, {"\uB5C7", "\uB5C7"}, {"\uB5C8", "\uB5C8"}, {"\uB5C9", "\uB5C9"}, {"\uB5CA", "\uB5CA"}, {"\uB5CB", "\uB5CB"}, {"\uB5CC", "\uB5CC"}, {"\uB5CD", "\uB5CD"}, {"\uB5CE", "\uB5CE"}, {"\uB5CF", "\uB5CF"}, {"\uB5D0", "\uB5D0"}, {"\uB5D1", "\uB5D1"}, {"\uB5D2", "\uB5D2"}, {"\uB5D3", "\uB5D3"}, {"\uB5D4", "\uB5D4"}, {"\uB5D5", "\uB5D5"}, {"\uB5D6", "\uB5D6"}, {"\uB5D7", "\uB5D7"}, {"\uB5D8", "\uB5D8"}, {"\uB5D9", "\uB5D9"}, {"\uB5DA", "\uB5DA"}, {"\uB5DB", "\uB5DB"}, {"\uB5DC", "\uB5DC"}, {"\uB5DD", "\uB5DD"}, {"\uB5DE", "\uB5DE"}, {"\uB5DF", "\uB5DF"}, {"\uB5E0", "\uB5E0"}, {"\uB5E1", "\uB5E1"}, {"\uB5E2", "\uB5E2"}, {"\uB5E3", "\uB5E3"}, {"\uB5E4", "\uB5E4"}, {"\uB5E5", "\uB5E5"}, {"\uB5E6", "\uB5E6"}, {"\uB5E7", "\uB5E7"}, {"\uB5E8", "\uB5E8"}, {"\uB5E9", "\uB5E9"}, {"\uB5EA", "\uB5EA"}, {"\uB5EB", "\uB5EB"}, {"\uB5EC", "\uB5EC"}, {"\uB5ED", "\uB5ED"}, {"\uB5EE", "\uB5EE"}, {"\uB5EF", "\uB5EF"}, {"\uB5F0", "\uB5F0"}, {"\uB5F1", "\uB5F1"}, {"\uB5F2", "\uB5F2"}, {"\uB5F3", "\uB5F3"}, {"\uB5F4", "\uB5F4"}, {"\uB5F5", "\uB5F5"}, {"\uB5F6", "\uB5F6"}, {"\uB5F7", "\uB5F7"}, {"\uB5F8", "\uB5F8"}, {"\uB5F9", "\uB5F9"}, {"\uB5FA", "\uB5FA"}, {"\uB5FB", "\uB5FB"}, {"\uB5FC", "\uB5FC"}, {"\uB5FD", "\uB5FD"}, {"\uB5FE", "\uB5FE"}, {"\uB5FF", "\uB5FF"}, {"\uB600", "\uB600"}, {"\uB601", "\uB601"}, {"\uB602", "\uB602"}, {"\uB603", "\uB603"}, {"\uB604", "\uB604"}, {"\uB605", "\uB605"}, {"\uB606", "\uB606"}, {"\uB607", "\uB607"}, {"\uB608", "\uB608"}, {"\uB609", "\uB609"}, {"\uB60A", "\uB60A"}, {"\uB60B", "\uB60B"}, {"\uB60C", "\uB60C"}, {"\uB60D", "\uB60D"}, {"\uB60E", "\uB60E"}, {"\uB60F", "\uB60F"}, {"\uB610", "\uB610"}, {"\uB611", "\uB611"}, {"\uB612", "\uB612"}, {"\uB613", "\uB613"}, {"\uB614", "\uB614"}, {"\uB615", "\uB615"}, {"\uB616", "\uB616"}, {"\uB617", "\uB617"}, {"\uB618", "\uB618"}, {"\uB619", "\uB619"}, {"\uB61A", "\uB61A"}, {"\uB61B", "\uB61B"}, {"\uB61C", "\uB61C"}, {"\uB61D", "\uB61D"}, {"\uB61E", "\uB61E"}, {"\uB61F", "\uB61F"}, {"\uB620", "\uB620"}, {"\uB621", "\uB621"}, {"\uB622", "\uB622"}, {"\uB623", "\uB623"}, {"\uB624", "\uB624"}, {"\uB625", "\uB625"}, {"\uB626", "\uB626"}, {"\uB627", "\uB627"}, {"\uB628", "\uB628"}, {"\uB629", "\uB629"}, {"\uB62A", "\uB62A"}, {"\uB62B", "\uB62B"}, {"\uB62C", "\uB62C"}, {"\uB62D", "\uB62D"}, {"\uB62E", "\uB62E"}, {"\uB62F", "\uB62F"}, {"\uB630", "\uB630"}, {"\uB631", "\uB631"}, {"\uB632", "\uB632"}, {"\uB633", "\uB633"}, {"\uB634", "\uB634"}, {"\uB635", "\uB635"}, {"\uB636", "\uB636"}, {"\uB637", "\uB637"}, {"\uB638", "\uB638"}, {"\uB639", "\uB639"}, {"\uB63A", "\uB63A"}, {"\uB63B", "\uB63B"}, {"\uB63C", "\uB63C"}, {"\uB63D", "\uB63D"}, {"\uB63E", "\uB63E"}, {"\uB63F", "\uB63F"}, {"\uB640", "\uB640"}, {"\uB641", "\uB641"}, {"\uB642", "\uB642"}, {"\uB643", "\uB643"}, {"\uB644", "\uB644"}, {"\uB645", "\uB645"}, {"\uB646", "\uB646"}, {"\uB647", "\uB647"}, {"\uB648", "\uB648"}, {"\uB649", "\uB649"}, {"\uB64A", "\uB64A"}, {"\uB64B", "\uB64B"}, {"\uB64C", "\uB64C"}, {"\uB64D", "\uB64D"}, {"\uB64E", "\uB64E"}, {"\uB64F", "\uB64F"}, {"\uB650", "\uB650"}, {"\uB651", "\uB651"}, {"\uB652", "\uB652"}, {"\uB653", "\uB653"}, {"\uB654", "\uB654"}, {"\uB655", "\uB655"}, {"\uB656", "\uB656"}, {"\uB657", "\uB657"}, {"\uB658", "\uB658"}, {"\uB659", "\uB659"}, {"\uB65A", "\uB65A"}, {"\uB65B", "\uB65B"}, {"\uB65C", "\uB65C"}, {"\uB65D", "\uB65D"}, {"\uB65E", "\uB65E"}, {"\uB65F", "\uB65F"}, {"\uB660", "\uB660"}, {"\uB661", "\uB661"}, {"\uB662", "\uB662"}, {"\uB663", "\uB663"}, {"\uB664", "\uB664"}, {"\uB665", "\uB665"}, {"\uB666", "\uB666"}, {"\uB667", "\uB667"}, {"\uB668", "\uB668"}, {"\uB669", "\uB669"}, {"\uB66A", "\uB66A"}, {"\uB66B", "\uB66B"}, {"\uB66C", "\uB66C"}, {"\uB66D", "\uB66D"}, {"\uB66E", "\uB66E"}, {"\uB66F", "\uB66F"}, {"\uB670", "\uB670"}, {"\uB671", "\uB671"}, {"\uB672", "\uB672"}, {"\uB673", "\uB673"}, {"\uB674", "\uB674"}, {"\uB675", "\uB675"}, {"\uB676", "\uB676"}, {"\uB677", "\uB677"}, {"\uB678", "\uB678"}, {"\uB679", "\uB679"}, {"\uB67A", "\uB67A"}, {"\uB67B", "\uB67B"}, {"\uB67C", "\uB67C"}, {"\uB67D", "\uB67D"}, {"\uB67E", "\uB67E"}, {"\uB67F", "\uB67F"}, {"\uB680", "\uB680"}, {"\uB681", "\uB681"}, {"\uB682", "\uB682"}, {"\uB683", "\uB683"}, {"\uB684", "\uB684"}, {"\uB685", "\uB685"}, {"\uB686", "\uB686"}, {"\uB687", "\uB687"}, {"\uB688", "\uB688"}, {"\uB689", "\uB689"}, {"\uB68A", "\uB68A"}, {"\uB68B", "\uB68B"}, {"\uB68C", "\uB68C"}, {"\uB68D", "\uB68D"}, {"\uB68E", "\uB68E"}, {"\uB68F", "\uB68F"}, {"\uB690", "\uB690"}, {"\uB691", "\uB691"}, {"\uB692", "\uB692"}, {"\uB693", "\uB693"}, {"\uB694", "\uB694"}, {"\uB695", "\uB695"}, {"\uB696", "\uB696"}, {"\uB697", "\uB697"}, {"\uB698", "\uB698"}, {"\uB699", "\uB699"}, {"\uB69A", "\uB69A"}, {"\uB69B", "\uB69B"}, {"\uB69C", "\uB69C"}, {"\uB69D", "\uB69D"}, {"\uB69E", "\uB69E"}, {"\uB69F", "\uB69F"}, {"\uB6A0", "\uB6A0"}, {"\uB6A1", "\uB6A1"}, {"\uB6A2", "\uB6A2"}, {"\uB6A3", "\uB6A3"}, {"\uB6A4", "\uB6A4"}, {"\uB6A5", "\uB6A5"}, {"\uB6A6", "\uB6A6"}, {"\uB6A7", "\uB6A7"}, {"\uB6A8", "\uB6A8"}, {"\uB6A9", "\uB6A9"}, {"\uB6AA", "\uB6AA"}, {"\uB6AB", "\uB6AB"}, {"\uB6AC", "\uB6AC"}, {"\uB6AD", "\uB6AD"}, {"\uB6AE", "\uB6AE"}, {"\uB6AF", "\uB6AF"}, {"\uB6B0", "\uB6B0"}, {"\uB6B1", "\uB6B1"}, {"\uB6B2", "\uB6B2"}, {"\uB6B3", "\uB6B3"}, {"\uB6B4", "\uB6B4"}, {"\uB6B5", "\uB6B5"}, {"\uB6B6", "\uB6B6"}, {"\uB6B7", "\uB6B7"}, {"\uB6B8", "\uB6B8"}, {"\uB6B9", "\uB6B9"}, {"\uB6BA", "\uB6BA"}, {"\uB6BB", "\uB6BB"}, {"\uB6BC", "\uB6BC"}, {"\uB6BD", "\uB6BD"}, {"\uB6BE", "\uB6BE"}, {"\uB6BF", "\uB6BF"}, {"\uB6C0", "\uB6C0"}, {"\uB6C1", "\uB6C1"}, {"\uB6C2", "\uB6C2"}, {"\uB6C3", "\uB6C3"}, {"\uB6C4", "\uB6C4"}, {"\uB6C5", "\uB6C5"}, {"\uB6C6", "\uB6C6"}, {"\uB6C7", "\uB6C7"}, {"\uB6C8", "\uB6C8"}, {"\uB6C9", "\uB6C9"}, {"\uB6CA", "\uB6CA"}, {"\uB6CB", "\uB6CB"}, {"\uB6CC", "\uB6CC"}, {"\uB6CD", "\uB6CD"}, {"\uB6CE", "\uB6CE"}, {"\uB6CF", "\uB6CF"}, {"\uB6D0", "\uB6D0"}, {"\uB6D1", "\uB6D1"}, {"\uB6D2", "\uB6D2"}, {"\uB6D3", "\uB6D3"}, {"\uB6D4", "\uB6D4"}, {"\uB6D5", "\uB6D5"}, {"\uB6D6", "\uB6D6"}, {"\uB6D7", "\uB6D7"}, {"\uB6D8", "\uB6D8"}, {"\uB6D9", "\uB6D9"}, {"\uB6DA", "\uB6DA"}, {"\uB6DB", "\uB6DB"}, {"\uB6DC", "\uB6DC"}, {"\uB6DD", "\uB6DD"}, {"\uB6DE", "\uB6DE"}, {"\uB6DF", "\uB6DF"}, {"\uB6E0", "\uB6E0"}, {"\uB6E1", "\uB6E1"}, {"\uB6E2", "\uB6E2"}, {"\uB6E3", "\uB6E3"}, {"\uB6E4", "\uB6E4"}, {"\uB6E5", "\uB6E5"}, {"\uB6E6", "\uB6E6"}, {"\uB6E7", "\uB6E7"}, {"\uB6E8", "\uB6E8"}, {"\uB6E9", "\uB6E9"}, {"\uB6EA", "\uB6EA"}, {"\uB6EB", "\uB6EB"}, {"\uB6EC", "\uB6EC"}, {"\uB6ED", "\uB6ED"}, {"\uB6EE", "\uB6EE"}, {"\uB6EF", "\uB6EF"}, {"\uB6F0", "\uB6F0"}, {"\uB6F1", "\uB6F1"}, {"\uB6F2", "\uB6F2"}, {"\uB6F3", "\uB6F3"}, {"\uB6F4", "\uB6F4"}, {"\uB6F5", "\uB6F5"}, {"\uB6F6", "\uB6F6"}, {"\uB6F7", "\uB6F7"}, {"\uB6F8", "\uB6F8"}, {"\uB6F9", "\uB6F9"}, {"\uB6FA", "\uB6FA"}, {"\uB6FB", "\uB6FB"}, {"\uB6FC", "\uB6FC"}, {"\uB6FD", "\uB6FD"}, {"\uB6FE", "\uB6FE"}, {"\uB6FF", "\uB6FF"}, {"\uB700", "\uB700"}, {"\uB701", "\uB701"}, {"\uB702", "\uB702"}, {"\uB703", "\uB703"}, {"\uB704", "\uB704"}, {"\uB705", "\uB705"}, {"\uB706", "\uB706"}, {"\uB707", "\uB707"}, {"\uB708", "\uB708"}, {"\uB709", "\uB709"}, {"\uB70A", "\uB70A"}, {"\uB70B", "\uB70B"}, {"\uB70C", "\uB70C"}, {"\uB70D", "\uB70D"}, {"\uB70E", "\uB70E"}, {"\uB70F", "\uB70F"}, {"\uB710", "\uB710"}, {"\uB711", "\uB711"}, {"\uB712", "\uB712"}, {"\uB713", "\uB713"}, {"\uB714", "\uB714"}, {"\uB715", "\uB715"}, {"\uB716", "\uB716"}, {"\uB717", "\uB717"}, {"\uB718", "\uB718"}, {"\uB719", "\uB719"}, {"\uB71A", "\uB71A"}, {"\uB71B", "\uB71B"}, {"\uB71C", "\uB71C"}, {"\uB71D", "\uB71D"}, {"\uB71E", "\uB71E"}, {"\uB71F", "\uB71F"}, {"\uB720", "\uB720"}, {"\uB721", "\uB721"}, {"\uB722", "\uB722"}, {"\uB723", "\uB723"}, {"\uB724", "\uB724"}, {"\uB725", "\uB725"}, {"\uB726", "\uB726"}, {"\uB727", "\uB727"}, {"\uB728", "\uB728"}, {"\uB729", "\uB729"}, {"\uB72A", "\uB72A"}, {"\uB72B", "\uB72B"}, {"\uB72C", "\uB72C"}, {"\uB72D", "\uB72D"}, {"\uB72E", "\uB72E"}, {"\uB72F", "\uB72F"}, {"\uB730", "\uB730"}, {"\uB731", "\uB731"}, {"\uB732", "\uB732"}, {"\uB733", "\uB733"}, {"\uB734", "\uB734"}, {"\uB735", "\uB735"}, {"\uB736", "\uB736"}, {"\uB737", "\uB737"}, {"\uB738", "\uB738"}, {"\uB739", "\uB739"}, {"\uB73A", "\uB73A"}, {"\uB73B", "\uB73B"}, {"\uB73C", "\uB73C"}, {"\uB73D", "\uB73D"}, {"\uB73E", "\uB73E"}, {"\uB73F", "\uB73F"}, {"\uB740", "\uB740"}, {"\uB741", "\uB741"}, {"\uB742", "\uB742"}, {"\uB743", "\uB743"}, {"\uB744", "\uB744"}, {"\uB745", "\uB745"}, {"\uB746", "\uB746"}, {"\uB747", "\uB747"}, {"\uB748", "\uB748"}, {"\uB749", "\uB749"}, {"\uB74A", "\uB74A"}, {"\uB74B", "\uB74B"}, {"\uB74C", "\uB74C"}, {"\uB74D", "\uB74D"}, {"\uB74E", "\uB74E"}, {"\uB74F", "\uB74F"}, {"\uB750", "\uB750"}, {"\uB751", "\uB751"}, {"\uB752", "\uB752"}, {"\uB753", "\uB753"}, {"\uB754", "\uB754"}, {"\uB755", "\uB755"}, {"\uB756", "\uB756"}, {"\uB757", "\uB757"}, {"\uB758", "\uB758"}, {"\uB759", "\uB759"}, {"\uB75A", "\uB75A"}, {"\uB75B", "\uB75B"}, {"\uB75C", "\uB75C"}, {"\uB75D", "\uB75D"}, {"\uB75E", "\uB75E"}, {"\uB75F", "\uB75F"}, {"\uB760", "\uB760"}, {"\uB761", "\uB761"}, {"\uB762", "\uB762"}, {"\uB763", "\uB763"}, {"\uB764", "\uB764"}, {"\uB765", "\uB765"}, {"\uB766", "\uB766"}, {"\uB767", "\uB767"}, {"\uB768", "\uB768"}, {"\uB769", "\uB769"}, {"\uB76A", "\uB76A"}, {"\uB76B", "\uB76B"}, {"\uB76C", "\uB76C"}, {"\uB76D", "\uB76D"}, {"\uB76E", "\uB76E"}, {"\uB76F", "\uB76F"}, {"\uB770", "\uB770"}, {"\uB771", "\uB771"}, {"\uB772", "\uB772"}, {"\uB773", "\uB773"}, {"\uB774", "\uB774"}, {"\uB775", "\uB775"}, {"\uB776", "\uB776"}, {"\uB777", "\uB777"}, {"\uB778", "\uB778"}, {"\uB779", "\uB779"}, {"\uB77A", "\uB77A"}, {"\uB77B", "\uB77B"}, {"\uB77C", "\uB77C"}, {"\uB77D", "\uB77D"}, {"\uB77E", "\uB77E"}, {"\uB77F", "\uB77F"}, {"\uB780", "\uB780"}, {"\uB781", "\uB781"}, {"\uB782", "\uB782"}, {"\uB783", "\uB783"}, {"\uB784", "\uB784"}, {"\uB785", "\uB785"}, {"\uB786", "\uB786"}, {"\uB787", "\uB787"}, {"\uB788", "\uB788"}, {"\uB789", "\uB789"}, {"\uB78A", "\uB78A"}, {"\uB78B", "\uB78B"}, {"\uB78C", "\uB78C"}, {"\uB78D", "\uB78D"}, {"\uB78E", "\uB78E"}, {"\uB78F", "\uB78F"}, {"\uB790", "\uB790"}, {"\uB791", "\uB791"}, {"\uB792", "\uB792"}, {"\uB793", "\uB793"}, {"\uB794", "\uB794"}, {"\uB795", "\uB795"}, {"\uB796", "\uB796"}, {"\uB797", "\uB797"}, {"\uB798", "\uB798"}, {"\uB799", "\uB799"}, {"\uB79A", "\uB79A"}, {"\uB79B", "\uB79B"}, {"\uB79C", "\uB79C"}, {"\uB79D", "\uB79D"}, {"\uB79E", "\uB79E"}, {"\uB79F", "\uB79F"}, {"\uB7A0", "\uB7A0"}, {"\uB7A1", "\uB7A1"}, {"\uB7A2", "\uB7A2"}, {"\uB7A3", "\uB7A3"}, {"\uB7A4", "\uB7A4"}, {"\uB7A5", "\uB7A5"}, {"\uB7A6", "\uB7A6"}, {"\uB7A7", "\uB7A7"}, {"\uB7A8", "\uB7A8"}, {"\uB7A9", "\uB7A9"}, {"\uB7AA", "\uB7AA"}, {"\uB7AB", "\uB7AB"}, {"\uB7AC", "\uB7AC"}, {"\uB7AD", "\uB7AD"}, {"\uB7AE", "\uB7AE"}, {"\uB7AF", "\uB7AF"}, {"\uB7B0", "\uB7B0"}, {"\uB7B1", "\uB7B1"}, {"\uB7B2", "\uB7B2"}, {"\uB7B3", "\uB7B3"}, {"\uB7B4", "\uB7B4"}, {"\uB7B5", "\uB7B5"}, {"\uB7B6", "\uB7B6"}, {"\uB7B7", "\uB7B7"}, {"\uB7B8", "\uB7B8"}, {"\uB7B9", "\uB7B9"}, {"\uB7BA", "\uB7BA"}, {"\uB7BB", "\uB7BB"}, {"\uB7BC", "\uB7BC"}, {"\uB7BD", "\uB7BD"}, {"\uB7BE", "\uB7BE"}, {"\uB7BF", "\uB7BF"}, {"\uB7C0", "\uB7C0"}, {"\uB7C1", "\uB7C1"}, {"\uB7C2", "\uB7C2"}, {"\uB7C3", "\uB7C3"}, {"\uB7C4", "\uB7C4"}, {"\uB7C5", "\uB7C5"}, {"\uB7C6", "\uB7C6"}, {"\uB7C7", "\uB7C7"}, {"\uB7C8", "\uB7C8"}, {"\uB7C9", "\uB7C9"}, {"\uB7CA", "\uB7CA"}, {"\uB7CB", "\uB7CB"}, {"\uB7CC", "\uB7CC"}, {"\uB7CD", "\uB7CD"}, {"\uB7CE", "\uB7CE"}, {"\uB7CF", "\uB7CF"}, {"\uB7D0", "\uB7D0"}, {"\uB7D1", "\uB7D1"}, {"\uB7D2", "\uB7D2"}, {"\uB7D3", "\uB7D3"}, {"\uB7D4", "\uB7D4"}, {"\uB7D5", "\uB7D5"}, {"\uB7D6", "\uB7D6"}, {"\uB7D7", "\uB7D7"}, {"\uB7D8", "\uB7D8"}, {"\uB7D9", "\uB7D9"}, {"\uB7DA", "\uB7DA"}, {"\uB7DB", "\uB7DB"}, {"\uB7DC", "\uB7DC"}, {"\uB7DD", "\uB7DD"}, {"\uB7DE", "\uB7DE"}, {"\uB7DF", "\uB7DF"}, {"\uB7E0", "\uB7E0"}, {"\uB7E1", "\uB7E1"}, {"\uB7E2", "\uB7E2"}, {"\uB7E3", "\uB7E3"}, {"\uB7E4", "\uB7E4"}, {"\uB7E5", "\uB7E5"}, {"\uB7E6", "\uB7E6"}, {"\uB7E7", "\uB7E7"}, {"\uB7E8", "\uB7E8"}, {"\uB7E9", "\uB7E9"}, {"\uB7EA", "\uB7EA"}, {"\uB7EB", "\uB7EB"}, {"\uB7EC", "\uB7EC"}, {"\uB7ED", "\uB7ED"}, {"\uB7EE", "\uB7EE"}, {"\uB7EF", "\uB7EF"}, {"\uB7F0", "\uB7F0"}, {"\uB7F1", "\uB7F1"}, {"\uB7F2", "\uB7F2"}, {"\uB7F3", "\uB7F3"}, {"\uB7F4", "\uB7F4"}, {"\uB7F5", "\uB7F5"}, {"\uB7F6", "\uB7F6"}, {"\uB7F7", "\uB7F7"}, {"\uB7F8", "\uB7F8"}, {"\uB7F9", "\uB7F9"}, {"\uB7FA", "\uB7FA"}, {"\uB7FB", "\uB7FB"}, {"\uB7FC", "\uB7FC"}, {"\uB7FD", "\uB7FD"}, {"\uB7FE", "\uB7FE"}, {"\uB7FF", "\uB7FF"}, {"\uB800", "\uB800"}, {"\uB801", "\uB801"}, {"\uB802", "\uB802"}, {"\uB803", "\uB803"}, {"\uB804", "\uB804"}, {"\uB805", "\uB805"}, {"\uB806", "\uB806"}, {"\uB807", "\uB807"}, {"\uB808", "\uB808"}, {"\uB809", "\uB809"}, {"\uB80A", "\uB80A"}, {"\uB80B", "\uB80B"}, {"\uB80C", "\uB80C"}, {"\uB80D", "\uB80D"}, {"\uB80E", "\uB80E"}, {"\uB80F", "\uB80F"}, {"\uB810", "\uB810"}, {"\uB811", "\uB811"}, {"\uB812", "\uB812"}, {"\uB813", "\uB813"}, {"\uB814", "\uB814"}, {"\uB815", "\uB815"}, {"\uB816", "\uB816"}, {"\uB817", "\uB817"}, {"\uB818", "\uB818"}, {"\uB819", "\uB819"}, {"\uB81A", "\uB81A"}, {"\uB81B", "\uB81B"}, {"\uB81C", "\uB81C"}, {"\uB81D", "\uB81D"}, {"\uB81E", "\uB81E"}, {"\uB81F", "\uB81F"}, {"\uB820", "\uB820"}, {"\uB821", "\uB821"}, {"\uB822", "\uB822"}, {"\uB823", "\uB823"}, {"\uB824", "\uB824"}, {"\uB825", "\uB825"}, {"\uB826", "\uB826"}, {"\uB827", "\uB827"}, {"\uB828", "\uB828"}, {"\uB829", "\uB829"}, {"\uB82A", "\uB82A"}, {"\uB82B", "\uB82B"}, {"\uB82C", "\uB82C"}, {"\uB82D", "\uB82D"}, {"\uB82E", "\uB82E"}, {"\uB82F", "\uB82F"}, {"\uB830", "\uB830"}, {"\uB831", "\uB831"}, {"\uB832", "\uB832"}, {"\uB833", "\uB833"}, {"\uB834", "\uB834"}, {"\uB835", "\uB835"}, {"\uB836", "\uB836"}, {"\uB837", "\uB837"}, {"\uB838", "\uB838"}, {"\uB839", "\uB839"}, {"\uB83A", "\uB83A"}, {"\uB83B", "\uB83B"}, {"\uB83C", "\uB83C"}, {"\uB83D", "\uB83D"}, {"\uB83E", "\uB83E"}, {"\uB83F", "\uB83F"}, {"\uB840", "\uB840"}, {"\uB841", "\uB841"}, {"\uB842", "\uB842"}, {"\uB843", "\uB843"}, {"\uB844", "\uB844"}, {"\uB845", "\uB845"}, {"\uB846", "\uB846"}, {"\uB847", "\uB847"}, {"\uB848", "\uB848"}, {"\uB849", "\uB849"}, {"\uB84A", "\uB84A"}, {"\uB84B", "\uB84B"}, {"\uB84C", "\uB84C"}, {"\uB84D", "\uB84D"}, {"\uB84E", "\uB84E"}, {"\uB84F", "\uB84F"}, {"\uB850", "\uB850"}, {"\uB851", "\uB851"}, {"\uB852", "\uB852"}, {"\uB853", "\uB853"}, {"\uB854", "\uB854"}, {"\uB855", "\uB855"}, {"\uB856", "\uB856"}, {"\uB857", "\uB857"}, {"\uB858", "\uB858"}, {"\uB859", "\uB859"}, {"\uB85A", "\uB85A"}, {"\uB85B", "\uB85B"}, {"\uB85C", "\uB85C"}, {"\uB85D", "\uB85D"}, {"\uB85E", "\uB85E"}, {"\uB85F", "\uB85F"}, {"\uB860", "\uB860"}, {"\uB861", "\uB861"}, {"\uB862", "\uB862"}, {"\uB863", "\uB863"}, {"\uB864", "\uB864"}, {"\uB865", "\uB865"}, {"\uB866", "\uB866"}, {"\uB867", "\uB867"}, {"\uB868", "\uB868"}, {"\uB869", "\uB869"}, {"\uB86A", "\uB86A"}, {"\uB86B", "\uB86B"}, {"\uB86C", "\uB86C"}, {"\uB86D", "\uB86D"}, {"\uB86E", "\uB86E"}, {"\uB86F", "\uB86F"}, {"\uB870", "\uB870"}, {"\uB871", "\uB871"}, {"\uB872", "\uB872"}, {"\uB873", "\uB873"}, {"\uB874", "\uB874"}, {"\uB875", "\uB875"}, {"\uB876", "\uB876"}, {"\uB877", "\uB877"}, {"\uB878", "\uB878"}, {"\uB879", "\uB879"}, {"\uB87A", "\uB87A"}, {"\uB87B", "\uB87B"}, {"\uB87C", "\uB87C"}, {"\uB87D", "\uB87D"}, {"\uB87E", "\uB87E"}, {"\uB87F", "\uB87F"}, {"\uB880", "\uB880"}, {"\uB881", "\uB881"}, {"\uB882", "\uB882"}, {"\uB883", "\uB883"}, {"\uB884", "\uB884"}, {"\uB885", "\uB885"}, {"\uB886", "\uB886"}, {"\uB887", "\uB887"}, {"\uB888", "\uB888"}, {"\uB889", "\uB889"}, {"\uB88A", "\uB88A"}, {"\uB88B", "\uB88B"}, {"\uB88C", "\uB88C"}, {"\uB88D", "\uB88D"}, {"\uB88E", "\uB88E"}, {"\uB88F", "\uB88F"}, {"\uB890", "\uB890"}, {"\uB891", "\uB891"}, {"\uB892", "\uB892"}, {"\uB893", "\uB893"}, {"\uB894", "\uB894"}, {"\uB895", "\uB895"}, {"\uB896", "\uB896"}, {"\uB897", "\uB897"}, {"\uB898", "\uB898"}, {"\uB899", "\uB899"}, {"\uB89A", "\uB89A"}, {"\uB89B", "\uB89B"}, {"\uB89C", "\uB89C"}, {"\uB89D", "\uB89D"}, {"\uB89E", "\uB89E"}, {"\uB89F", "\uB89F"}, {"\uB8A0", "\uB8A0"}, {"\uB8A1", "\uB8A1"}, {"\uB8A2", "\uB8A2"}, {"\uB8A3", "\uB8A3"}, {"\uB8A4", "\uB8A4"}, {"\uB8A5", "\uB8A5"}, {"\uB8A6", "\uB8A6"}, {"\uB8A7", "\uB8A7"}, {"\uB8A8", "\uB8A8"}, {"\uB8A9", "\uB8A9"}, {"\uB8AA", "\uB8AA"}, {"\uB8AB", "\uB8AB"}, {"\uB8AC", "\uB8AC"}, {"\uB8AD", "\uB8AD"}, {"\uB8AE", "\uB8AE"}, {"\uB8AF", "\uB8AF"}, {"\uB8B0", "\uB8B0"}, {"\uB8B1", "\uB8B1"}, {"\uB8B2", "\uB8B2"}, {"\uB8B3", "\uB8B3"}, {"\uB8B4", "\uB8B4"}, {"\uB8B5", "\uB8B5"}, {"\uB8B6", "\uB8B6"}, {"\uB8B7", "\uB8B7"}, {"\uB8B8", "\uB8B8"}, {"\uB8B9", "\uB8B9"}, {"\uB8BA", "\uB8BA"}, {"\uB8BB", "\uB8BB"}, {"\uB8BC", "\uB8BC"}, {"\uB8BD", "\uB8BD"}, {"\uB8BE", "\uB8BE"}, {"\uB8BF", "\uB8BF"}, {"\uB8C0", "\uB8C0"}, {"\uB8C1", "\uB8C1"}, {"\uB8C2", "\uB8C2"}, {"\uB8C3", "\uB8C3"}, {"\uB8C4", "\uB8C4"}, {"\uB8C5", "\uB8C5"}, {"\uB8C6", "\uB8C6"}, {"\uB8C7", "\uB8C7"}, {"\uB8C8", "\uB8C8"}, {"\uB8C9", "\uB8C9"}, {"\uB8CA", "\uB8CA"}, {"\uB8CB", "\uB8CB"}, {"\uB8CC", "\uB8CC"}, {"\uB8CD", "\uB8CD"}, {"\uB8CE", "\uB8CE"}, {"\uB8CF", "\uB8CF"}, {"\uB8D0", "\uB8D0"}, {"\uB8D1", "\uB8D1"}, {"\uB8D2", "\uB8D2"}, {"\uB8D3", "\uB8D3"}, {"\uB8D4", "\uB8D4"}, {"\uB8D5", "\uB8D5"}, {"\uB8D6", "\uB8D6"}, {"\uB8D7", "\uB8D7"}, {"\uB8D8", "\uB8D8"}, {"\uB8D9", "\uB8D9"}, {"\uB8DA", "\uB8DA"}, {"\uB8DB", "\uB8DB"}, {"\uB8DC", "\uB8DC"}, {"\uB8DD", "\uB8DD"}, {"\uB8DE", "\uB8DE"}, {"\uB8DF", "\uB8DF"}, {"\uB8E0", "\uB8E0"}, {"\uB8E1", "\uB8E1"}, {"\uB8E2", "\uB8E2"}, {"\uB8E3", "\uB8E3"}, {"\uB8E4", "\uB8E4"}, {"\uB8E5", "\uB8E5"}, {"\uB8E6", "\uB8E6"}, {"\uB8E7", "\uB8E7"}, {"\uB8E8", "\uB8E8"}, {"\uB8E9", "\uB8E9"}, {"\uB8EA", "\uB8EA"}, {"\uB8EB", "\uB8EB"}, {"\uB8EC", "\uB8EC"}, {"\uB8ED", "\uB8ED"}, {"\uB8EE", "\uB8EE"}, {"\uB8EF", "\uB8EF"}, {"\uB8F0", "\uB8F0"}, {"\uB8F1", "\uB8F1"}, {"\uB8F2", "\uB8F2"}, {"\uB8F3", "\uB8F3"}, {"\uB8F4", "\uB8F4"}, {"\uB8F5", "\uB8F5"}, {"\uB8F6", "\uB8F6"}, {"\uB8F7", "\uB8F7"}, {"\uB8F8", "\uB8F8"}, {"\uB8F9", "\uB8F9"}, {"\uB8FA", "\uB8FA"}, {"\uB8FB", "\uB8FB"}, {"\uB8FC", "\uB8FC"}, {"\uB8FD", "\uB8FD"}, {"\uB8FE", "\uB8FE"}, {"\uB8FF", "\uB8FF"}, {"\uB900", "\uB900"}, {"\uB901", "\uB901"}, {"\uB902", "\uB902"}, {"\uB903", "\uB903"}, {"\uB904", "\uB904"}, {"\uB905", "\uB905"}, {"\uB906", "\uB906"}, {"\uB907", "\uB907"}, {"\uB908", "\uB908"}, {"\uB909", "\uB909"}, {"\uB90A", "\uB90A"}, {"\uB90B", "\uB90B"}, {"\uB90C", "\uB90C"}, {"\uB90D", "\uB90D"}, {"\uB90E", "\uB90E"}, {"\uB90F", "\uB90F"}, {"\uB910", "\uB910"}, {"\uB911", "\uB911"}, {"\uB912", "\uB912"}, {"\uB913", "\uB913"}, {"\uB914", "\uB914"}, {"\uB915", "\uB915"}, {"\uB916", "\uB916"}, {"\uB917", "\uB917"}, {"\uB918", "\uB918"}, {"\uB919", "\uB919"}, {"\uB91A", "\uB91A"}, {"\uB91B", "\uB91B"}, {"\uB91C", "\uB91C"}, {"\uB91D", "\uB91D"}, {"\uB91E", "\uB91E"}, {"\uB91F", "\uB91F"}, {"\uB920", "\uB920"}, {"\uB921", "\uB921"}, {"\uB922", "\uB922"}, {"\uB923", "\uB923"}, {"\uB924", "\uB924"}, {"\uB925", "\uB925"}, {"\uB926", "\uB926"}, {"\uB927", "\uB927"}, {"\uB928", "\uB928"}, {"\uB929", "\uB929"}, {"\uB92A", "\uB92A"}, {"\uB92B", "\uB92B"}, {"\uB92C", "\uB92C"}, {"\uB92D", "\uB92D"}, {"\uB92E", "\uB92E"}, {"\uB92F", "\uB92F"}, {"\uB930", "\uB930"}, {"\uB931", "\uB931"}, {"\uB932", "\uB932"}, {"\uB933", "\uB933"}, {"\uB934", "\uB934"}, {"\uB935", "\uB935"}, {"\uB936", "\uB936"}, {"\uB937", "\uB937"}, {"\uB938", "\uB938"}, {"\uB939", "\uB939"}, {"\uB93A", "\uB93A"}, {"\uB93B", "\uB93B"}, {"\uB93C", "\uB93C"}, {"\uB93D", "\uB93D"}, {"\uB93E", "\uB93E"}, {"\uB93F", "\uB93F"}, {"\uB940", "\uB940"}, {"\uB941", "\uB941"}, {"\uB942", "\uB942"}, {"\uB943", "\uB943"}, {"\uB944", "\uB944"}, {"\uB945", "\uB945"}, {"\uB946", "\uB946"}, {"\uB947", "\uB947"}, {"\uB948", "\uB948"}, {"\uB949", "\uB949"}, {"\uB94A", "\uB94A"}, {"\uB94B", "\uB94B"}, {"\uB94C", "\uB94C"}, {"\uB94D", "\uB94D"}, {"\uB94E", "\uB94E"}, {"\uB94F", "\uB94F"}, {"\uB950", "\uB950"}, {"\uB951", "\uB951"}, {"\uB952", "\uB952"}, {"\uB953", "\uB953"}, {"\uB954", "\uB954"}, {"\uB955", "\uB955"}, {"\uB956", "\uB956"}, {"\uB957", "\uB957"}, {"\uB958", "\uB958"}, {"\uB959", "\uB959"}, {"\uB95A", "\uB95A"}, {"\uB95B", "\uB95B"}, {"\uB95C", "\uB95C"}, {"\uB95D", "\uB95D"}, {"\uB95E", "\uB95E"}, {"\uB95F", "\uB95F"}, {"\uB960", "\uB960"}, {"\uB961", "\uB961"}, {"\uB962", "\uB962"}, {"\uB963", "\uB963"}, {"\uB964", "\uB964"}, {"\uB965", "\uB965"}, {"\uB966", "\uB966"}, {"\uB967", "\uB967"}, {"\uB968", "\uB968"}, {"\uB969", "\uB969"}, {"\uB96A", "\uB96A"}, {"\uB96B", "\uB96B"}, {"\uB96C", "\uB96C"}, {"\uB96D", "\uB96D"}, {"\uB96E", "\uB96E"}, {"\uB96F", "\uB96F"}, {"\uB970", "\uB970"}, {"\uB971", "\uB971"}, {"\uB972", "\uB972"}, {"\uB973", "\uB973"}, {"\uB974", "\uB974"}, {"\uB975", "\uB975"}, {"\uB976", "\uB976"}, {"\uB977", "\uB977"}, {"\uB978", "\uB978"}, {"\uB979", "\uB979"}, {"\uB97A", "\uB97A"}, {"\uB97B", "\uB97B"}, {"\uB97C", "\uB97C"}, {"\uB97D", "\uB97D"}, {"\uB97E", "\uB97E"}, {"\uB97F", "\uB97F"}, {"\uB980", "\uB980"}, {"\uB981", "\uB981"}, {"\uB982", "\uB982"}, {"\uB983", "\uB983"}, {"\uB984", "\uB984"}, {"\uB985", "\uB985"}, {"\uB986", "\uB986"}, {"\uB987", "\uB987"}, {"\uB988", "\uB988"}, {"\uB989", "\uB989"}, {"\uB98A", "\uB98A"}, {"\uB98B", "\uB98B"}, {"\uB98C", "\uB98C"}, {"\uB98D", "\uB98D"}, {"\uB98E", "\uB98E"}, {"\uB98F", "\uB98F"}, {"\uB990", "\uB990"}, {"\uB991", "\uB991"}, {"\uB992", "\uB992"}, {"\uB993", "\uB993"}, {"\uB994", "\uB994"}, {"\uB995", "\uB995"}, {"\uB996", "\uB996"}, {"\uB997", "\uB997"}, {"\uB998", "\uB998"}, {"\uB999", "\uB999"}, {"\uB99A", "\uB99A"}, {"\uB99B", "\uB99B"}, {"\uB99C", "\uB99C"}, {"\uB99D", "\uB99D"}, {"\uB99E", "\uB99E"}, {"\uB99F", "\uB99F"}, {"\uB9A0", "\uB9A0"}, {"\uB9A1", "\uB9A1"}, {"\uB9A2", "\uB9A2"}, {"\uB9A3", "\uB9A3"}, {"\uB9A4", "\uB9A4"}, {"\uB9A5", "\uB9A5"}, {"\uB9A6", "\uB9A6"}, {"\uB9A7", "\uB9A7"}, {"\uB9A8", "\uB9A8"}, {"\uB9A9", "\uB9A9"}, {"\uB9AA", "\uB9AA"}, {"\uB9AB", "\uB9AB"}, {"\uB9AC", "\uB9AC"}, {"\uB9AD", "\uB9AD"}, {"\uB9AE", "\uB9AE"}, {"\uB9AF", "\uB9AF"}, {"\uB9B0", "\uB9B0"}, {"\uB9B1", "\uB9B1"}, {"\uB9B2", "\uB9B2"}, {"\uB9B3", "\uB9B3"}, {"\uB9B4", "\uB9B4"}, {"\uB9B5", "\uB9B5"}, {"\uB9B6", "\uB9B6"}, {"\uB9B7", "\uB9B7"}, {"\uB9B8", "\uB9B8"}, {"\uB9B9", "\uB9B9"}, {"\uB9BA", "\uB9BA"}, {"\uB9BB", "\uB9BB"}, {"\uB9BC", "\uB9BC"}, {"\uB9BD", "\uB9BD"}, {"\uB9BE", "\uB9BE"}, {"\uB9BF", "\uB9BF"}, {"\uB9C0", "\uB9C0"}, {"\uB9C1", "\uB9C1"}, {"\uB9C2", "\uB9C2"}, {"\uB9C3", "\uB9C3"}, {"\uB9C4", "\uB9C4"}, {"\uB9C5", "\uB9C5"}, {"\uB9C6", "\uB9C6"}, {"\uB9C7", "\uB9C7"}, {"\uB9C8", "\uB9C8"}, {"\uB9C9", "\uB9C9"}, {"\uB9CA", "\uB9CA"}, {"\uB9CB", "\uB9CB"}, {"\uB9CC", "\uB9CC"}, {"\uB9CD", "\uB9CD"}, {"\uB9CE", "\uB9CE"}, {"\uB9CF", "\uB9CF"}, {"\uB9D0", "\uB9D0"}, {"\uB9D1", "\uB9D1"}, {"\uB9D2", "\uB9D2"}, {"\uB9D3", "\uB9D3"}, {"\uB9D4", "\uB9D4"}, {"\uB9D5", "\uB9D5"}, {"\uB9D6", "\uB9D6"}, {"\uB9D7", "\uB9D7"}, {"\uB9D8", "\uB9D8"}, {"\uB9D9", "\uB9D9"}, {"\uB9DA", "\uB9DA"}, {"\uB9DB", "\uB9DB"}, {"\uB9DC", "\uB9DC"}, {"\uB9DD", "\uB9DD"}, {"\uB9DE", "\uB9DE"}, {"\uB9DF", "\uB9DF"}, {"\uB9E0", "\uB9E0"}, {"\uB9E1", "\uB9E1"}, {"\uB9E2", "\uB9E2"}, {"\uB9E3", "\uB9E3"}, {"\uB9E4", "\uB9E4"}, {"\uB9E5", "\uB9E5"}, {"\uB9E6", "\uB9E6"}, {"\uB9E7", "\uB9E7"}, {"\uB9E8", "\uB9E8"}, {"\uB9E9", "\uB9E9"}, {"\uB9EA", "\uB9EA"}, {"\uB9EB", "\uB9EB"}, {"\uB9EC", "\uB9EC"}, {"\uB9ED", "\uB9ED"}, {"\uB9EE", "\uB9EE"}, {"\uB9EF", "\uB9EF"}, {"\uB9F0", "\uB9F0"}, {"\uB9F1", "\uB9F1"}, {"\uB9F2", "\uB9F2"}, {"\uB9F3", "\uB9F3"}, {"\uB9F4", "\uB9F4"}, {"\uB9F5", "\uB9F5"}, {"\uB9F6", "\uB9F6"}, {"\uB9F7", "\uB9F7"}, {"\uB9F8", "\uB9F8"}, {"\uB9F9", "\uB9F9"}, {"\uB9FA", "\uB9FA"}, {"\uB9FB", "\uB9FB"}, {"\uB9FC", "\uB9FC"}, {"\uB9FD", "\uB9FD"}, {"\uB9FE", "\uB9FE"}, {"\uB9FF", "\uB9FF"}, {"\uBA00", "\uBA00"}, {"\uBA01", "\uBA01"}, {"\uBA02", "\uBA02"}, {"\uBA03", "\uBA03"}, {"\uBA04", "\uBA04"}, {"\uBA05", "\uBA05"}, {"\uBA06", "\uBA06"}, {"\uBA07", "\uBA07"}, {"\uBA08", "\uBA08"}, {"\uBA09", "\uBA09"}, {"\uBA0A", "\uBA0A"}, {"\uBA0B", "\uBA0B"}, {"\uBA0C", "\uBA0C"}, {"\uBA0D", "\uBA0D"}, {"\uBA0E", "\uBA0E"}, {"\uBA0F", "\uBA0F"}, {"\uBA10", "\uBA10"}, {"\uBA11", "\uBA11"}, {"\uBA12", "\uBA12"}, {"\uBA13", "\uBA13"}, {"\uBA14", "\uBA14"}, {"\uBA15", "\uBA15"}, {"\uBA16", "\uBA16"}, {"\uBA17", "\uBA17"}, {"\uBA18", "\uBA18"}, {"\uBA19", "\uBA19"}, {"\uBA1A", "\uBA1A"}, {"\uBA1B", "\uBA1B"}, {"\uBA1C", "\uBA1C"}, {"\uBA1D", "\uBA1D"}, {"\uBA1E", "\uBA1E"}, {"\uBA1F", "\uBA1F"}, {"\uBA20", "\uBA20"}, {"\uBA21", "\uBA21"}, {"\uBA22", "\uBA22"}, {"\uBA23", "\uBA23"}, {"\uBA24", "\uBA24"}, {"\uBA25", "\uBA25"}, {"\uBA26", "\uBA26"}, {"\uBA27", "\uBA27"}, {"\uBA28", "\uBA28"}, {"\uBA29", "\uBA29"}, {"\uBA2A", "\uBA2A"}, {"\uBA2B", "\uBA2B"}, {"\uBA2C", "\uBA2C"}, {"\uBA2D", "\uBA2D"}, {"\uBA2E", "\uBA2E"}, {"\uBA2F", "\uBA2F"}, {"\uBA30", "\uBA30"}, {"\uBA31", "\uBA31"}, {"\uBA32", "\uBA32"}, {"\uBA33", "\uBA33"}, {"\uBA34", "\uBA34"}, {"\uBA35", "\uBA35"}, {"\uBA36", "\uBA36"}, {"\uBA37", "\uBA37"}, {"\uBA38", "\uBA38"}, {"\uBA39", "\uBA39"}, {"\uBA3A", "\uBA3A"}, {"\uBA3B", "\uBA3B"}, {"\uBA3C", "\uBA3C"}, {"\uBA3D", "\uBA3D"}, {"\uBA3E", "\uBA3E"}, {"\uBA3F", "\uBA3F"}, {"\uBA40", "\uBA40"}, {"\uBA41", "\uBA41"}, {"\uBA42", "\uBA42"}, {"\uBA43", "\uBA43"}, {"\uBA44", "\uBA44"}, {"\uBA45", "\uBA45"}, {"\uBA46", "\uBA46"}, {"\uBA47", "\uBA47"}, {"\uBA48", "\uBA48"}, {"\uBA49", "\uBA49"}, {"\uBA4A", "\uBA4A"}, {"\uBA4B", "\uBA4B"}, {"\uBA4C", "\uBA4C"}, {"\uBA4D", "\uBA4D"}, {"\uBA4E", "\uBA4E"}, {"\uBA4F", "\uBA4F"}, {"\uBA50", "\uBA50"}, {"\uBA51", "\uBA51"}, {"\uBA52", "\uBA52"}, {"\uBA53", "\uBA53"}, {"\uBA54", "\uBA54"}, {"\uBA55", "\uBA55"}, {"\uBA56", "\uBA56"}, {"\uBA57", "\uBA57"}, {"\uBA58", "\uBA58"}, {"\uBA59", "\uBA59"}, {"\uBA5A", "\uBA5A"}, {"\uBA5B", "\uBA5B"}, {"\uBA5C", "\uBA5C"}, {"\uBA5D", "\uBA5D"}, {"\uBA5E", "\uBA5E"}, {"\uBA5F", "\uBA5F"}, {"\uBA60", "\uBA60"}, {"\uBA61", "\uBA61"}, {"\uBA62", "\uBA62"}, {"\uBA63", "\uBA63"}, {"\uBA64", "\uBA64"}, {"\uBA65", "\uBA65"}, {"\uBA66", "\uBA66"}, {"\uBA67", "\uBA67"}, {"\uBA68", "\uBA68"}, {"\uBA69", "\uBA69"}, {"\uBA6A", "\uBA6A"}, {"\uBA6B", "\uBA6B"}, {"\uBA6C", "\uBA6C"}, {"\uBA6D", "\uBA6D"}, {"\uBA6E", "\uBA6E"}, {"\uBA6F", "\uBA6F"}, {"\uBA70", "\uBA70"}, {"\uBA71", "\uBA71"}, {"\uBA72", "\uBA72"}, {"\uBA73", "\uBA73"}, {"\uBA74", "\uBA74"}, {"\uBA75", "\uBA75"}, {"\uBA76", "\uBA76"}, {"\uBA77", "\uBA77"}, {"\uBA78", "\uBA78"}, {"\uBA79", "\uBA79"}, {"\uBA7A", "\uBA7A"}, {"\uBA7B", "\uBA7B"}, {"\uBA7C", "\uBA7C"}, {"\uBA7D", "\uBA7D"}, {"\uBA7E", "\uBA7E"}, {"\uBA7F", "\uBA7F"}, {"\uBA80", "\uBA80"}, {"\uBA81", "\uBA81"}, {"\uBA82", "\uBA82"}, {"\uBA83", "\uBA83"}, {"\uBA84", "\uBA84"}, {"\uBA85", "\uBA85"}, {"\uBA86", "\uBA86"}, {"\uBA87", "\uBA87"}, {"\uBA88", "\uBA88"}, {"\uBA89", "\uBA89"}, {"\uBA8A", "\uBA8A"}, {"\uBA8B", "\uBA8B"}, {"\uBA8C", "\uBA8C"}, {"\uBA8D", "\uBA8D"}, {"\uBA8E", "\uBA8E"}, {"\uBA8F", "\uBA8F"}, {"\uBA90", "\uBA90"}, {"\uBA91", "\uBA91"}, {"\uBA92", "\uBA92"}, {"\uBA93", "\uBA93"}, {"\uBA94", "\uBA94"}, {"\uBA95", "\uBA95"}, {"\uBA96", "\uBA96"}, {"\uBA97", "\uBA97"}, {"\uBA98", "\uBA98"}, {"\uBA99", "\uBA99"}, {"\uBA9A", "\uBA9A"}, {"\uBA9B", "\uBA9B"}, {"\uBA9C", "\uBA9C"}, {"\uBA9D", "\uBA9D"}, {"\uBA9E", "\uBA9E"}, {"\uBA9F", "\uBA9F"}, {"\uBAA0", "\uBAA0"}, {"\uBAA1", "\uBAA1"}, {"\uBAA2", "\uBAA2"}, {"\uBAA3", "\uBAA3"}, {"\uBAA4", "\uBAA4"}, {"\uBAA5", "\uBAA5"}, {"\uBAA6", "\uBAA6"}, {"\uBAA7", "\uBAA7"}, {"\uBAA8", "\uBAA8"}, {"\uBAA9", "\uBAA9"}, {"\uBAAA", "\uBAAA"}, {"\uBAAB", "\uBAAB"}, {"\uBAAC", "\uBAAC"}, {"\uBAAD", "\uBAAD"}, {"\uBAAE", "\uBAAE"}, {"\uBAAF", "\uBAAF"}, {"\uBAB0", "\uBAB0"}, {"\uBAB1", "\uBAB1"}, {"\uBAB2", "\uBAB2"}, {"\uBAB3", "\uBAB3"}, {"\uBAB4", "\uBAB4"}, {"\uBAB5", "\uBAB5"}, {"\uBAB6", "\uBAB6"}, {"\uBAB7", "\uBAB7"}, {"\uBAB8", "\uBAB8"}, {"\uBAB9", "\uBAB9"}, {"\uBABA", "\uBABA"}, {"\uBABB", "\uBABB"}, {"\uBABC", "\uBABC"}, {"\uBABD", "\uBABD"}, {"\uBABE", "\uBABE"}, {"\uBABF", "\uBABF"}, {"\uBAC0", "\uBAC0"}, {"\uBAC1", "\uBAC1"}, {"\uBAC2", "\uBAC2"}, {"\uBAC3", "\uBAC3"}, {"\uBAC4", "\uBAC4"}, {"\uBAC5", "\uBAC5"}, {"\uBAC6", "\uBAC6"}, {"\uBAC7", "\uBAC7"}, {"\uBAC8", "\uBAC8"}, {"\uBAC9", "\uBAC9"}, {"\uBACA", "\uBACA"}, {"\uBACB", "\uBACB"}, {"\uBACC", "\uBACC"}, {"\uBACD", "\uBACD"}, {"\uBACE", "\uBACE"}, {"\uBACF", "\uBACF"}, {"\uBAD0", "\uBAD0"}, {"\uBAD1", "\uBAD1"}, {"\uBAD2", "\uBAD2"}, {"\uBAD3", "\uBAD3"}, {"\uBAD4", "\uBAD4"}, {"\uBAD5", "\uBAD5"}, {"\uBAD6", "\uBAD6"}, {"\uBAD7", "\uBAD7"}, {"\uBAD8", "\uBAD8"}, {"\uBAD9", "\uBAD9"}, {"\uBADA", "\uBADA"}, {"\uBADB", "\uBADB"}, {"\uBADC", "\uBADC"}, {"\uBADD", "\uBADD"}, {"\uBADE", "\uBADE"}, {"\uBADF", "\uBADF"}, {"\uBAE0", "\uBAE0"}, {"\uBAE1", "\uBAE1"}, {"\uBAE2", "\uBAE2"}, {"\uBAE3", "\uBAE3"}, {"\uBAE4", "\uBAE4"}, {"\uBAE5", "\uBAE5"}, {"\uBAE6", "\uBAE6"}, {"\uBAE7", "\uBAE7"}, {"\uBAE8", "\uBAE8"}, {"\uBAE9", "\uBAE9"}, {"\uBAEA", "\uBAEA"}, {"\uBAEB", "\uBAEB"}, {"\uBAEC", "\uBAEC"}, {"\uBAED", "\uBAED"}, {"\uBAEE", "\uBAEE"}, {"\uBAEF", "\uBAEF"}, {"\uBAF0", "\uBAF0"}, {"\uBAF1", "\uBAF1"}, {"\uBAF2", "\uBAF2"}, {"\uBAF3", "\uBAF3"}, {"\uBAF4", "\uBAF4"}, {"\uBAF5", "\uBAF5"}, {"\uBAF6", "\uBAF6"}, {"\uBAF7", "\uBAF7"}, {"\uBAF8", "\uBAF8"}, {"\uBAF9", "\uBAF9"}, {"\uBAFA", "\uBAFA"}, {"\uBAFB", "\uBAFB"}, {"\uBAFC", "\uBAFC"}, {"\uBAFD", "\uBAFD"}, {"\uBAFE", "\uBAFE"}, {"\uBAFF", "\uBAFF"}, {"\uBB00", "\uBB00"}, {"\uBB01", "\uBB01"}, {"\uBB02", "\uBB02"}, {"\uBB03", "\uBB03"}, {"\uBB04", "\uBB04"}, {"\uBB05", "\uBB05"}, {"\uBB06", "\uBB06"}, {"\uBB07", "\uBB07"}, {"\uBB08", "\uBB08"}, {"\uBB09", "\uBB09"}, {"\uBB0A", "\uBB0A"}, {"\uBB0B", "\uBB0B"}, {"\uBB0C", "\uBB0C"}, {"\uBB0D", "\uBB0D"}, {"\uBB0E", "\uBB0E"}, {"\uBB0F", "\uBB0F"}, {"\uBB10", "\uBB10"}, {"\uBB11", "\uBB11"}, {"\uBB12", "\uBB12"}, {"\uBB13", "\uBB13"}, {"\uBB14", "\uBB14"}, {"\uBB15", "\uBB15"}, {"\uBB16", "\uBB16"}, {"\uBB17", "\uBB17"}, {"\uBB18", "\uBB18"}, {"\uBB19", "\uBB19"}, {"\uBB1A", "\uBB1A"}, {"\uBB1B", "\uBB1B"}, {"\uBB1C", "\uBB1C"}, {"\uBB1D", "\uBB1D"}, {"\uBB1E", "\uBB1E"}, {"\uBB1F", "\uBB1F"}, {"\uBB20", "\uBB20"}, {"\uBB21", "\uBB21"}, {"\uBB22", "\uBB22"}, {"\uBB23", "\uBB23"}, {"\uBB24", "\uBB24"}, {"\uBB25", "\uBB25"}, {"\uBB26", "\uBB26"}, {"\uBB27", "\uBB27"}, {"\uBB28", "\uBB28"}, {"\uBB29", "\uBB29"}, {"\uBB2A", "\uBB2A"}, {"\uBB2B", "\uBB2B"}, {"\uBB2C", "\uBB2C"}, {"\uBB2D", "\uBB2D"}, {"\uBB2E", "\uBB2E"}, {"\uBB2F", "\uBB2F"}, {"\uBB30", "\uBB30"}, {"\uBB31", "\uBB31"}, {"\uBB32", "\uBB32"}, {"\uBB33", "\uBB33"}, {"\uBB34", "\uBB34"}, {"\uBB35", "\uBB35"}, {"\uBB36", "\uBB36"}, {"\uBB37", "\uBB37"}, {"\uBB38", "\uBB38"}, {"\uBB39", "\uBB39"}, {"\uBB3A", "\uBB3A"}, {"\uBB3B", "\uBB3B"}, {"\uBB3C", "\uBB3C"}, {"\uBB3D", "\uBB3D"}, {"\uBB3E", "\uBB3E"}, {"\uBB3F", "\uBB3F"}, {"\uBB40", "\uBB40"}, {"\uBB41", "\uBB41"}, {"\uBB42", "\uBB42"}, {"\uBB43", "\uBB43"}, {"\uBB44", "\uBB44"}, {"\uBB45", "\uBB45"}, {"\uBB46", "\uBB46"}, {"\uBB47", "\uBB47"}, {"\uBB48", "\uBB48"}, {"\uBB49", "\uBB49"}, {"\uBB4A", "\uBB4A"}, {"\uBB4B", "\uBB4B"}, {"\uBB4C", "\uBB4C"}, {"\uBB4D", "\uBB4D"}, {"\uBB4E", "\uBB4E"}, {"\uBB4F", "\uBB4F"}, {"\uBB50", "\uBB50"}, {"\uBB51", "\uBB51"}, {"\uBB52", "\uBB52"}, {"\uBB53", "\uBB53"}, {"\uBB54", "\uBB54"}, {"\uBB55", "\uBB55"}, {"\uBB56", "\uBB56"}, {"\uBB57", "\uBB57"}, {"\uBB58", "\uBB58"}, {"\uBB59", "\uBB59"}, {"\uBB5A", "\uBB5A"}, {"\uBB5B", "\uBB5B"}, {"\uBB5C", "\uBB5C"}, {"\uBB5D", "\uBB5D"}, {"\uBB5E", "\uBB5E"}, {"\uBB5F", "\uBB5F"}, {"\uBB60", "\uBB60"}, {"\uBB61", "\uBB61"}, {"\uBB62", "\uBB62"}, {"\uBB63", "\uBB63"}, {"\uBB64", "\uBB64"}, {"\uBB65", "\uBB65"}, {"\uBB66", "\uBB66"}, {"\uBB67", "\uBB67"}, {"\uBB68", "\uBB68"}, {"\uBB69", "\uBB69"}, {"\uBB6A", "\uBB6A"}, {"\uBB6B", "\uBB6B"}, {"\uBB6C", "\uBB6C"}, {"\uBB6D", "\uBB6D"}, {"\uBB6E", "\uBB6E"}, {"\uBB6F", "\uBB6F"}, {"\uBB70", "\uBB70"}, {"\uBB71", "\uBB71"}, {"\uBB72", "\uBB72"}, {"\uBB73", "\uBB73"}, {"\uBB74", "\uBB74"}, {"\uBB75", "\uBB75"}, {"\uBB76", "\uBB76"}, {"\uBB77", "\uBB77"}, {"\uBB78", "\uBB78"}, {"\uBB79", "\uBB79"}, {"\uBB7A", "\uBB7A"}, {"\uBB7B", "\uBB7B"}, {"\uBB7C", "\uBB7C"}, {"\uBB7D", "\uBB7D"}, {"\uBB7E", "\uBB7E"}, {"\uBB7F", "\uBB7F"}, {"\uBB80", "\uBB80"}, {"\uBB81", "\uBB81"}, {"\uBB82", "\uBB82"}, {"\uBB83", "\uBB83"}, {"\uBB84", "\uBB84"}, {"\uBB85", "\uBB85"}, {"\uBB86", "\uBB86"}, {"\uBB87", "\uBB87"}, {"\uBB88", "\uBB88"}, {"\uBB89", "\uBB89"}, {"\uBB8A", "\uBB8A"}, {"\uBB8B", "\uBB8B"}, {"\uBB8C", "\uBB8C"}, {"\uBB8D", "\uBB8D"}, {"\uBB8E", "\uBB8E"}, {"\uBB8F", "\uBB8F"}, {"\uBB90", "\uBB90"}, {"\uBB91", "\uBB91"}, {"\uBB92", "\uBB92"}, {"\uBB93", "\uBB93"}, {"\uBB94", "\uBB94"}, {"\uBB95", "\uBB95"}, {"\uBB96", "\uBB96"}, {"\uBB97", "\uBB97"}, {"\uBB98", "\uBB98"}, {"\uBB99", "\uBB99"}, {"\uBB9A", "\uBB9A"}, {"\uBB9B", "\uBB9B"}, {"\uBB9C", "\uBB9C"}, {"\uBB9D", "\uBB9D"}, {"\uBB9E", "\uBB9E"}, {"\uBB9F", "\uBB9F"}, {"\uBBA0", "\uBBA0"}, {"\uBBA1", "\uBBA1"}, {"\uBBA2", "\uBBA2"}, {"\uBBA3", "\uBBA3"}, {"\uBBA4", "\uBBA4"}, {"\uBBA5", "\uBBA5"}, {"\uBBA6", "\uBBA6"}, {"\uBBA7", "\uBBA7"}, {"\uBBA8", "\uBBA8"}, {"\uBBA9", "\uBBA9"}, {"\uBBAA", "\uBBAA"}, {"\uBBAB", "\uBBAB"}, {"\uBBAC", "\uBBAC"}, {"\uBBAD", "\uBBAD"}, {"\uBBAE", "\uBBAE"}, {"\uBBAF", "\uBBAF"}, {"\uBBB0", "\uBBB0"}, {"\uBBB1", "\uBBB1"}, {"\uBBB2", "\uBBB2"}, {"\uBBB3", "\uBBB3"}, {"\uBBB4", "\uBBB4"}, {"\uBBB5", "\uBBB5"}, {"\uBBB6", "\uBBB6"}, {"\uBBB7", "\uBBB7"}, {"\uBBB8", "\uBBB8"}, {"\uBBB9", "\uBBB9"}, {"\uBBBA", "\uBBBA"}, {"\uBBBB", "\uBBBB"}, {"\uBBBC", "\uBBBC"}, {"\uBBBD", "\uBBBD"}, {"\uBBBE", "\uBBBE"}, {"\uBBBF", "\uBBBF"}, {"\uBBC0", "\uBBC0"}, {"\uBBC1", "\uBBC1"}, {"\uBBC2", "\uBBC2"}, {"\uBBC3", "\uBBC3"}, {"\uBBC4", "\uBBC4"}, {"\uBBC5", "\uBBC5"}, {"\uBBC6", "\uBBC6"}, {"\uBBC7", "\uBBC7"}, {"\uBBC8", "\uBBC8"}, {"\uBBC9", "\uBBC9"}, {"\uBBCA", "\uBBCA"}, {"\uBBCB", "\uBBCB"}, {"\uBBCC", "\uBBCC"}, {"\uBBCD", "\uBBCD"}, {"\uBBCE", "\uBBCE"}, {"\uBBCF", "\uBBCF"}, {"\uBBD0", "\uBBD0"}, {"\uBBD1", "\uBBD1"}, {"\uBBD2", "\uBBD2"}, {"\uBBD3", "\uBBD3"}, {"\uBBD4", "\uBBD4"}, {"\uBBD5", "\uBBD5"}, {"\uBBD6", "\uBBD6"}, {"\uBBD7", "\uBBD7"}, {"\uBBD8", "\uBBD8"}, {"\uBBD9", "\uBBD9"}, {"\uBBDA", "\uBBDA"}, {"\uBBDB", "\uBBDB"}, {"\uBBDC", "\uBBDC"}, {"\uBBDD", "\uBBDD"}, {"\uBBDE", "\uBBDE"}, {"\uBBDF", "\uBBDF"}, {"\uBBE0", "\uBBE0"}, {"\uBBE1", "\uBBE1"}, {"\uBBE2", "\uBBE2"}, {"\uBBE3", "\uBBE3"}, {"\uBBE4", "\uBBE4"}, {"\uBBE5", "\uBBE5"}, {"\uBBE6", "\uBBE6"}, {"\uBBE7", "\uBBE7"}, {"\uBBE8", "\uBBE8"}, {"\uBBE9", "\uBBE9"}, {"\uBBEA", "\uBBEA"}, {"\uBBEB", "\uBBEB"}, {"\uBBEC", "\uBBEC"}, {"\uBBED", "\uBBED"}, {"\uBBEE", "\uBBEE"}, {"\uBBEF", "\uBBEF"}, {"\uBBF0", "\uBBF0"}, {"\uBBF1", "\uBBF1"}, {"\uBBF2", "\uBBF2"}, {"\uBBF3", "\uBBF3"}, {"\uBBF4", "\uBBF4"}, {"\uBBF5", "\uBBF5"}, {"\uBBF6", "\uBBF6"}, {"\uBBF7", "\uBBF7"}, {"\uBBF8", "\uBBF8"}, {"\uBBF9", "\uBBF9"}, {"\uBBFA", "\uBBFA"}, {"\uBBFB", "\uBBFB"}, {"\uBBFC", "\uBBFC"}, {"\uBBFD", "\uBBFD"}, {"\uBBFE", "\uBBFE"}, {"\uBBFF", "\uBBFF"}, {"\uBC00", "\uBC00"}, {"\uBC01", "\uBC01"}, {"\uBC02", "\uBC02"}, {"\uBC03", "\uBC03"}, {"\uBC04", "\uBC04"}, {"\uBC05", "\uBC05"}, {"\uBC06", "\uBC06"}, {"\uBC07", "\uBC07"}, {"\uBC08", "\uBC08"}, {"\uBC09", "\uBC09"}, {"\uBC0A", "\uBC0A"}, {"\uBC0B", "\uBC0B"}, {"\uBC0C", "\uBC0C"}, {"\uBC0D", "\uBC0D"}, {"\uBC0E", "\uBC0E"}, {"\uBC0F", "\uBC0F"}, {"\uBC10", "\uBC10"}, {"\uBC11", "\uBC11"}, {"\uBC12", "\uBC12"}, {"\uBC13", "\uBC13"}, {"\uBC14", "\uBC14"}, {"\uBC15", "\uBC15"}, {"\uBC16", "\uBC16"}, {"\uBC17", "\uBC17"}, {"\uBC18", "\uBC18"}, {"\uBC19", "\uBC19"}, {"\uBC1A", "\uBC1A"}, {"\uBC1B", "\uBC1B"}, {"\uBC1C", "\uBC1C"}, {"\uBC1D", "\uBC1D"}, {"\uBC1E", "\uBC1E"}, {"\uBC1F", "\uBC1F"}, {"\uBC20", "\uBC20"}, {"\uBC21", "\uBC21"}, {"\uBC22", "\uBC22"}, {"\uBC23", "\uBC23"}, {"\uBC24", "\uBC24"}, {"\uBC25", "\uBC25"}, {"\uBC26", "\uBC26"}, {"\uBC27", "\uBC27"}, {"\uBC28", "\uBC28"}, {"\uBC29", "\uBC29"}, {"\uBC2A", "\uBC2A"}, {"\uBC2B", "\uBC2B"}, {"\uBC2C", "\uBC2C"}, {"\uBC2D", "\uBC2D"}, {"\uBC2E", "\uBC2E"}, {"\uBC2F", "\uBC2F"}, {"\uBC30", "\uBC30"}, {"\uBC31", "\uBC31"}, {"\uBC32", "\uBC32"}, {"\uBC33", "\uBC33"}, {"\uBC34", "\uBC34"}, {"\uBC35", "\uBC35"}, {"\uBC36", "\uBC36"}, {"\uBC37", "\uBC37"}, {"\uBC38", "\uBC38"}, {"\uBC39", "\uBC39"}, {"\uBC3A", "\uBC3A"}, {"\uBC3B", "\uBC3B"}, {"\uBC3C", "\uBC3C"}, {"\uBC3D", "\uBC3D"}, {"\uBC3E", "\uBC3E"}, {"\uBC3F", "\uBC3F"}, {"\uBC40", "\uBC40"}, {"\uBC41", "\uBC41"}, {"\uBC42", "\uBC42"}, {"\uBC43", "\uBC43"}, {"\uBC44", "\uBC44"}, {"\uBC45", "\uBC45"}, {"\uBC46", "\uBC46"}, {"\uBC47", "\uBC47"}, {"\uBC48", "\uBC48"}, {"\uBC49", "\uBC49"}, {"\uBC4A", "\uBC4A"}, {"\uBC4B", "\uBC4B"}, {"\uBC4C", "\uBC4C"}, {"\uBC4D", "\uBC4D"}, {"\uBC4E", "\uBC4E"}, {"\uBC4F", "\uBC4F"}, {"\uBC50", "\uBC50"}, {"\uBC51", "\uBC51"}, {"\uBC52", "\uBC52"}, {"\uBC53", "\uBC53"}, {"\uBC54", "\uBC54"}, {"\uBC55", "\uBC55"}, {"\uBC56", "\uBC56"}, {"\uBC57", "\uBC57"}, {"\uBC58", "\uBC58"}, {"\uBC59", "\uBC59"}, {"\uBC5A", "\uBC5A"}, {"\uBC5B", "\uBC5B"}, {"\uBC5C", "\uBC5C"}, {"\uBC5D", "\uBC5D"}, {"\uBC5E", "\uBC5E"}, {"\uBC5F", "\uBC5F"}, {"\uBC60", "\uBC60"}, {"\uBC61", "\uBC61"}, {"\uBC62", "\uBC62"}, {"\uBC63", "\uBC63"}, {"\uBC64", "\uBC64"}, {"\uBC65", "\uBC65"}, {"\uBC66", "\uBC66"}, {"\uBC67", "\uBC67"}, {"\uBC68", "\uBC68"}, {"\uBC69", "\uBC69"}, {"\uBC6A", "\uBC6A"}, {"\uBC6B", "\uBC6B"}, {"\uBC6C", "\uBC6C"}, {"\uBC6D", "\uBC6D"}, {"\uBC6E", "\uBC6E"}, {"\uBC6F", "\uBC6F"}, {"\uBC70", "\uBC70"}, {"\uBC71", "\uBC71"}, {"\uBC72", "\uBC72"}, {"\uBC73", "\uBC73"}, {"\uBC74", "\uBC74"}, {"\uBC75", "\uBC75"}, {"\uBC76", "\uBC76"}, {"\uBC77", "\uBC77"}, {"\uBC78", "\uBC78"}, {"\uBC79", "\uBC79"}, {"\uBC7A", "\uBC7A"}, {"\uBC7B", "\uBC7B"}, {"\uBC7C", "\uBC7C"}, {"\uBC7D", "\uBC7D"}, {"\uBC7E", "\uBC7E"}, {"\uBC7F", "\uBC7F"}, {"\uBC80", "\uBC80"}, {"\uBC81", "\uBC81"}, {"\uBC82", "\uBC82"}, {"\uBC83", "\uBC83"}, {"\uBC84", "\uBC84"}, {"\uBC85", "\uBC85"}, {"\uBC86", "\uBC86"}, {"\uBC87", "\uBC87"}, {"\uBC88", "\uBC88"}, {"\uBC89", "\uBC89"}, {"\uBC8A", "\uBC8A"}, {"\uBC8B", "\uBC8B"}, {"\uBC8C", "\uBC8C"}, {"\uBC8D", "\uBC8D"}, {"\uBC8E", "\uBC8E"}, {"\uBC8F", "\uBC8F"}, {"\uBC90", "\uBC90"}, {"\uBC91", "\uBC91"}, {"\uBC92", "\uBC92"}, {"\uBC93", "\uBC93"}, {"\uBC94", "\uBC94"}, {"\uBC95", "\uBC95"}, {"\uBC96", "\uBC96"}, {"\uBC97", "\uBC97"}, {"\uBC98", "\uBC98"}, {"\uBC99", "\uBC99"}, {"\uBC9A", "\uBC9A"}, {"\uBC9B", "\uBC9B"}, {"\uBC9C", "\uBC9C"}, {"\uBC9D", "\uBC9D"}, {"\uBC9E", "\uBC9E"}, {"\uBC9F", "\uBC9F"}, {"\uBCA0", "\uBCA0"}, {"\uBCA1", "\uBCA1"}, {"\uBCA2", "\uBCA2"}, {"\uBCA3", "\uBCA3"}, {"\uBCA4", "\uBCA4"}, {"\uBCA5", "\uBCA5"}, {"\uBCA6", "\uBCA6"}, {"\uBCA7", "\uBCA7"}, {"\uBCA8", "\uBCA8"}, {"\uBCA9", "\uBCA9"}, {"\uBCAA", "\uBCAA"}, {"\uBCAB", "\uBCAB"}, {"\uBCAC", "\uBCAC"}, {"\uBCAD", "\uBCAD"}, {"\uBCAE", "\uBCAE"}, {"\uBCAF", "\uBCAF"}, {"\uBCB0", "\uBCB0"}, {"\uBCB1", "\uBCB1"}, {"\uBCB2", "\uBCB2"}, {"\uBCB3", "\uBCB3"}, {"\uBCB4", "\uBCB4"}, {"\uBCB5", "\uBCB5"}, {"\uBCB6", "\uBCB6"}, {"\uBCB7", "\uBCB7"}, {"\uBCB8", "\uBCB8"}, {"\uBCB9", "\uBCB9"}, {"\uBCBA", "\uBCBA"}, {"\uBCBB", "\uBCBB"}, {"\uBCBC", "\uBCBC"}, {"\uBCBD", "\uBCBD"}, {"\uBCBE", "\uBCBE"}, {"\uBCBF", "\uBCBF"}, {"\uBCC0", "\uBCC0"}, {"\uBCC1", "\uBCC1"}, {"\uBCC2", "\uBCC2"}, {"\uBCC3", "\uBCC3"}, {"\uBCC4", "\uBCC4"}, {"\uBCC5", "\uBCC5"}, {"\uBCC6", "\uBCC6"}, {"\uBCC7", "\uBCC7"}, {"\uBCC8", "\uBCC8"}, {"\uBCC9", "\uBCC9"}, {"\uBCCA", "\uBCCA"}, {"\uBCCB", "\uBCCB"}, {"\uBCCC", "\uBCCC"}, {"\uBCCD", "\uBCCD"}, {"\uBCCE", "\uBCCE"}, {"\uBCCF", "\uBCCF"}, {"\uBCD0", "\uBCD0"}, {"\uBCD1", "\uBCD1"}, {"\uBCD2", "\uBCD2"}, {"\uBCD3", "\uBCD3"}, {"\uBCD4", "\uBCD4"}, {"\uBCD5", "\uBCD5"}, {"\uBCD6", "\uBCD6"}, {"\uBCD7", "\uBCD7"}, {"\uBCD8", "\uBCD8"}, {"\uBCD9", "\uBCD9"}, {"\uBCDA", "\uBCDA"}, {"\uBCDB", "\uBCDB"}, {"\uBCDC", "\uBCDC"}, {"\uBCDD", "\uBCDD"}, {"\uBCDE", "\uBCDE"}, {"\uBCDF", "\uBCDF"}, {"\uBCE0", "\uBCE0"}, {"\uBCE1", "\uBCE1"}, {"\uBCE2", "\uBCE2"}, {"\uBCE3", "\uBCE3"}, {"\uBCE4", "\uBCE4"}, {"\uBCE5", "\uBCE5"}, {"\uBCE6", "\uBCE6"}, {"\uBCE7", "\uBCE7"}, {"\uBCE8", "\uBCE8"}, {"\uBCE9", "\uBCE9"}, {"\uBCEA", "\uBCEA"}, {"\uBCEB", "\uBCEB"}, {"\uBCEC", "\uBCEC"}, {"\uBCED", "\uBCED"}, {"\uBCEE", "\uBCEE"}, {"\uBCEF", "\uBCEF"}, {"\uBCF0", "\uBCF0"}, {"\uBCF1", "\uBCF1"}, {"\uBCF2", "\uBCF2"}, {"\uBCF3", "\uBCF3"}, {"\uBCF4", "\uBCF4"}, {"\uBCF5", "\uBCF5"}, {"\uBCF6", "\uBCF6"}, {"\uBCF7", "\uBCF7"}, {"\uBCF8", "\uBCF8"}, {"\uBCF9", "\uBCF9"}, {"\uBCFA", "\uBCFA"}, {"\uBCFB", "\uBCFB"}, {"\uBCFC", "\uBCFC"}, {"\uBCFD", "\uBCFD"}, {"\uBCFE", "\uBCFE"}, {"\uBCFF", "\uBCFF"}, {"\uBD00", "\uBD00"}, {"\uBD01", "\uBD01"}, {"\uBD02", "\uBD02"}, {"\uBD03", "\uBD03"}, {"\uBD04", "\uBD04"}, {"\uBD05", "\uBD05"}, {"\uBD06", "\uBD06"}, {"\uBD07", "\uBD07"}, {"\uBD08", "\uBD08"}, {"\uBD09", "\uBD09"}, {"\uBD0A", "\uBD0A"}, {"\uBD0B", "\uBD0B"}, {"\uBD0C", "\uBD0C"}, {"\uBD0D", "\uBD0D"}, {"\uBD0E", "\uBD0E"}, {"\uBD0F", "\uBD0F"}, {"\uBD10", "\uBD10"}, {"\uBD11", "\uBD11"}, {"\uBD12", "\uBD12"}, {"\uBD13", "\uBD13"}, {"\uBD14", "\uBD14"}, {"\uBD15", "\uBD15"}, {"\uBD16", "\uBD16"}, {"\uBD17", "\uBD17"}, {"\uBD18", "\uBD18"}, {"\uBD19", "\uBD19"}, {"\uBD1A", "\uBD1A"}, {"\uBD1B", "\uBD1B"}, {"\uBD1C", "\uBD1C"}, {"\uBD1D", "\uBD1D"}, {"\uBD1E", "\uBD1E"}, {"\uBD1F", "\uBD1F"}, {"\uBD20", "\uBD20"}, {"\uBD21", "\uBD21"}, {"\uBD22", "\uBD22"}, {"\uBD23", "\uBD23"}, {"\uBD24", "\uBD24"}, {"\uBD25", "\uBD25"}, {"\uBD26", "\uBD26"}, {"\uBD27", "\uBD27"}, {"\uBD28", "\uBD28"}, {"\uBD29", "\uBD29"}, {"\uBD2A", "\uBD2A"}, {"\uBD2B", "\uBD2B"}, {"\uBD2C", "\uBD2C"}, {"\uBD2D", "\uBD2D"}, {"\uBD2E", "\uBD2E"}, {"\uBD2F", "\uBD2F"}, {"\uBD30", "\uBD30"}, {"\uBD31", "\uBD31"}, {"\uBD32", "\uBD32"}, {"\uBD33", "\uBD33"}, {"\uBD34", "\uBD34"}, {"\uBD35", "\uBD35"}, {"\uBD36", "\uBD36"}, {"\uBD37", "\uBD37"}, {"\uBD38", "\uBD38"}, {"\uBD39", "\uBD39"}, {"\uBD3A", "\uBD3A"}, {"\uBD3B", "\uBD3B"}, {"\uBD3C", "\uBD3C"}, {"\uBD3D", "\uBD3D"}, {"\uBD3E", "\uBD3E"}, {"\uBD3F", "\uBD3F"}, {"\uBD40", "\uBD40"}, {"\uBD41", "\uBD41"}, {"\uBD42", "\uBD42"}, {"\uBD43", "\uBD43"}, {"\uBD44", "\uBD44"}, {"\uBD45", "\uBD45"}, {"\uBD46", "\uBD46"}, {"\uBD47", "\uBD47"}, {"\uBD48", "\uBD48"}, {"\uBD49", "\uBD49"}, {"\uBD4A", "\uBD4A"}, {"\uBD4B", "\uBD4B"}, {"\uBD4C", "\uBD4C"}, {"\uBD4D", "\uBD4D"}, {"\uBD4E", "\uBD4E"}, {"\uBD4F", "\uBD4F"}, {"\uBD50", "\uBD50"}, {"\uBD51", "\uBD51"}, {"\uBD52", "\uBD52"}, {"\uBD53", "\uBD53"}, {"\uBD54", "\uBD54"}, {"\uBD55", "\uBD55"}, {"\uBD56", "\uBD56"}, {"\uBD57", "\uBD57"}, {"\uBD58", "\uBD58"}, {"\uBD59", "\uBD59"}, {"\uBD5A", "\uBD5A"}, {"\uBD5B", "\uBD5B"}, {"\uBD5C", "\uBD5C"}, {"\uBD5D", "\uBD5D"}, {"\uBD5E", "\uBD5E"}, {"\uBD5F", "\uBD5F"}, {"\uBD60", "\uBD60"}, {"\uBD61", "\uBD61"}, {"\uBD62", "\uBD62"}, {"\uBD63", "\uBD63"}, {"\uBD64", "\uBD64"}, {"\uBD65", "\uBD65"}, {"\uBD66", "\uBD66"}, {"\uBD67", "\uBD67"}, {"\uBD68", "\uBD68"}, {"\uBD69", "\uBD69"}, {"\uBD6A", "\uBD6A"}, {"\uBD6B", "\uBD6B"}, {"\uBD6C", "\uBD6C"}, {"\uBD6D", "\uBD6D"}, {"\uBD6E", "\uBD6E"}, {"\uBD6F", "\uBD6F"}, {"\uBD70", "\uBD70"}, {"\uBD71", "\uBD71"}, {"\uBD72", "\uBD72"}, {"\uBD73", "\uBD73"}, {"\uBD74", "\uBD74"}, {"\uBD75", "\uBD75"}, {"\uBD76", "\uBD76"}, {"\uBD77", "\uBD77"}, {"\uBD78", "\uBD78"}, {"\uBD79", "\uBD79"}, {"\uBD7A", "\uBD7A"}, {"\uBD7B", "\uBD7B"}, {"\uBD7C", "\uBD7C"}, {"\uBD7D", "\uBD7D"}, {"\uBD7E", "\uBD7E"}, {"\uBD7F", "\uBD7F"}, {"\uBD80", "\uBD80"}, {"\uBD81", "\uBD81"}, {"\uBD82", "\uBD82"}, {"\uBD83", "\uBD83"}, {"\uBD84", "\uBD84"}, {"\uBD85", "\uBD85"}, {"\uBD86", "\uBD86"}, {"\uBD87", "\uBD87"}, {"\uBD88", "\uBD88"}, {"\uBD89", "\uBD89"}, {"\uBD8A", "\uBD8A"}, {"\uBD8B", "\uBD8B"}, {"\uBD8C", "\uBD8C"}, {"\uBD8D", "\uBD8D"}, {"\uBD8E", "\uBD8E"}, {"\uBD8F", "\uBD8F"}, {"\uBD90", "\uBD90"}, {"\uBD91", "\uBD91"}, {"\uBD92", "\uBD92"}, {"\uBD93", "\uBD93"}, {"\uBD94", "\uBD94"}, {"\uBD95", "\uBD95"}, {"\uBD96", "\uBD96"}, {"\uBD97", "\uBD97"}, {"\uBD98", "\uBD98"}, {"\uBD99", "\uBD99"}, {"\uBD9A", "\uBD9A"}, {"\uBD9B", "\uBD9B"}, {"\uBD9C", "\uBD9C"}, {"\uBD9D", "\uBD9D"}, {"\uBD9E", "\uBD9E"}, {"\uBD9F", "\uBD9F"}, {"\uBDA0", "\uBDA0"}, {"\uBDA1", "\uBDA1"}, {"\uBDA2", "\uBDA2"}, {"\uBDA3", "\uBDA3"}, {"\uBDA4", "\uBDA4"}, {"\uBDA5", "\uBDA5"}, {"\uBDA6", "\uBDA6"}, {"\uBDA7", "\uBDA7"}, {"\uBDA8", "\uBDA8"}, {"\uBDA9", "\uBDA9"}, {"\uBDAA", "\uBDAA"}, {"\uBDAB", "\uBDAB"}, {"\uBDAC", "\uBDAC"}, {"\uBDAD", "\uBDAD"}, {"\uBDAE", "\uBDAE"}, {"\uBDAF", "\uBDAF"}, {"\uBDB0", "\uBDB0"}, {"\uBDB1", "\uBDB1"}, {"\uBDB2", "\uBDB2"}, {"\uBDB3", "\uBDB3"}, {"\uBDB4", "\uBDB4"}, {"\uBDB5", "\uBDB5"}, {"\uBDB6", "\uBDB6"}, {"\uBDB7", "\uBDB7"}, {"\uBDB8", "\uBDB8"}, {"\uBDB9", "\uBDB9"}, {"\uBDBA", "\uBDBA"}, {"\uBDBB", "\uBDBB"}, {"\uBDBC", "\uBDBC"}, {"\uBDBD", "\uBDBD"}, {"\uBDBE", "\uBDBE"}, {"\uBDBF", "\uBDBF"}, {"\uBDC0", "\uBDC0"}, {"\uBDC1", "\uBDC1"}, {"\uBDC2", "\uBDC2"}, {"\uBDC3", "\uBDC3"}, {"\uBDC4", "\uBDC4"}, {"\uBDC5", "\uBDC5"}, {"\uBDC6", "\uBDC6"}, {"\uBDC7", "\uBDC7"}, {"\uBDC8", "\uBDC8"}, {"\uBDC9", "\uBDC9"}, {"\uBDCA", "\uBDCA"}, {"\uBDCB", "\uBDCB"}, {"\uBDCC", "\uBDCC"}, {"\uBDCD", "\uBDCD"}, {"\uBDCE", "\uBDCE"}, {"\uBDCF", "\uBDCF"}, {"\uBDD0", "\uBDD0"}, {"\uBDD1", "\uBDD1"}, {"\uBDD2", "\uBDD2"}, {"\uBDD3", "\uBDD3"}, {"\uBDD4", "\uBDD4"}, {"\uBDD5", "\uBDD5"}, {"\uBDD6", "\uBDD6"}, {"\uBDD7", "\uBDD7"}, {"\uBDD8", "\uBDD8"}, {"\uBDD9", "\uBDD9"}, {"\uBDDA", "\uBDDA"}, {"\uBDDB", "\uBDDB"}, {"\uBDDC", "\uBDDC"}, {"\uBDDD", "\uBDDD"}, {"\uBDDE", "\uBDDE"}, {"\uBDDF", "\uBDDF"}, {"\uBDE0", "\uBDE0"}, {"\uBDE1", "\uBDE1"}, {"\uBDE2", "\uBDE2"}, {"\uBDE3", "\uBDE3"}, {"\uBDE4", "\uBDE4"}, {"\uBDE5", "\uBDE5"}, {"\uBDE6", "\uBDE6"}, {"\uBDE7", "\uBDE7"}, {"\uBDE8", "\uBDE8"}, {"\uBDE9", "\uBDE9"}, {"\uBDEA", "\uBDEA"}, {"\uBDEB", "\uBDEB"}, {"\uBDEC", "\uBDEC"}, {"\uBDED", "\uBDED"}, {"\uBDEE", "\uBDEE"}, {"\uBDEF", "\uBDEF"}, {"\uBDF0", "\uBDF0"}, {"\uBDF1", "\uBDF1"}, {"\uBDF2", "\uBDF2"}, {"\uBDF3", "\uBDF3"}, {"\uBDF4", "\uBDF4"}, {"\uBDF5", "\uBDF5"}, {"\uBDF6", "\uBDF6"}, {"\uBDF7", "\uBDF7"}, {"\uBDF8", "\uBDF8"}, {"\uBDF9", "\uBDF9"}, {"\uBDFA", "\uBDFA"}, {"\uBDFB", "\uBDFB"}, {"\uBDFC", "\uBDFC"}, {"\uBDFD", "\uBDFD"}, {"\uBDFE", "\uBDFE"}, {"\uBDFF", "\uBDFF"}, {"\uBE00", "\uBE00"}, {"\uBE01", "\uBE01"}, {"\uBE02", "\uBE02"}, {"\uBE03", "\uBE03"}, {"\uBE04", "\uBE04"}, {"\uBE05", "\uBE05"}, {"\uBE06", "\uBE06"}, {"\uBE07", "\uBE07"}, {"\uBE08", "\uBE08"}, {"\uBE09", "\uBE09"}, {"\uBE0A", "\uBE0A"}, {"\uBE0B", "\uBE0B"}, {"\uBE0C", "\uBE0C"}, {"\uBE0D", "\uBE0D"}, {"\uBE0E", "\uBE0E"}, {"\uBE0F", "\uBE0F"}, {"\uBE10", "\uBE10"}, {"\uBE11", "\uBE11"}, {"\uBE12", "\uBE12"}, {"\uBE13", "\uBE13"}, {"\uBE14", "\uBE14"}, {"\uBE15", "\uBE15"}, {"\uBE16", "\uBE16"}, {"\uBE17", "\uBE17"}, {"\uBE18", "\uBE18"}, {"\uBE19", "\uBE19"}, {"\uBE1A", "\uBE1A"}, {"\uBE1B", "\uBE1B"}, {"\uBE1C", "\uBE1C"}, {"\uBE1D", "\uBE1D"}, {"\uBE1E", "\uBE1E"}, {"\uBE1F", "\uBE1F"}, {"\uBE20", "\uBE20"}, {"\uBE21", "\uBE21"}, {"\uBE22", "\uBE22"}, {"\uBE23", "\uBE23"}, {"\uBE24", "\uBE24"}, {"\uBE25", "\uBE25"}, {"\uBE26", "\uBE26"}, {"\uBE27", "\uBE27"}, {"\uBE28", "\uBE28"}, {"\uBE29", "\uBE29"}, {"\uBE2A", "\uBE2A"}, {"\uBE2B", "\uBE2B"}, {"\uBE2C", "\uBE2C"}, {"\uBE2D", "\uBE2D"}, {"\uBE2E", "\uBE2E"}, {"\uBE2F", "\uBE2F"}, {"\uBE30", "\uBE30"}, {"\uBE31", "\uBE31"}, {"\uBE32", "\uBE32"}, {"\uBE33", "\uBE33"}, {"\uBE34", "\uBE34"}, {"\uBE35", "\uBE35"}, {"\uBE36", "\uBE36"}, {"\uBE37", "\uBE37"}, {"\uBE38", "\uBE38"}, {"\uBE39", "\uBE39"}, {"\uBE3A", "\uBE3A"}, {"\uBE3B", "\uBE3B"}, {"\uBE3C", "\uBE3C"}, {"\uBE3D", "\uBE3D"}, {"\uBE3E", "\uBE3E"}, {"\uBE3F", "\uBE3F"}, {"\uBE40", "\uBE40"}, {"\uBE41", "\uBE41"}, {"\uBE42", "\uBE42"}, {"\uBE43", "\uBE43"}, {"\uBE44", "\uBE44"}, {"\uBE45", "\uBE45"}, {"\uBE46", "\uBE46"}, {"\uBE47", "\uBE47"}, {"\uBE48", "\uBE48"}, {"\uBE49", "\uBE49"}, {"\uBE4A", "\uBE4A"}, {"\uBE4B", "\uBE4B"}, {"\uBE4C", "\uBE4C"}, {"\uBE4D", "\uBE4D"}, {"\uBE4E", "\uBE4E"}, {"\uBE4F", "\uBE4F"}, {"\uBE50", "\uBE50"}, {"\uBE51", "\uBE51"}, {"\uBE52", "\uBE52"}, {"\uBE53", "\uBE53"}, {"\uBE54", "\uBE54"}, {"\uBE55", "\uBE55"}, {"\uBE56", "\uBE56"}, {"\uBE57", "\uBE57"}, {"\uBE58", "\uBE58"}, {"\uBE59", "\uBE59"}, {"\uBE5A", "\uBE5A"}, {"\uBE5B", "\uBE5B"}, {"\uBE5C", "\uBE5C"}, {"\uBE5D", "\uBE5D"}, {"\uBE5E", "\uBE5E"}, {"\uBE5F", "\uBE5F"}, {"\uBE60", "\uBE60"}, {"\uBE61", "\uBE61"}, {"\uBE62", "\uBE62"}, {"\uBE63", "\uBE63"}, {"\uBE64", "\uBE64"}, {"\uBE65", "\uBE65"}, {"\uBE66", "\uBE66"}, {"\uBE67", "\uBE67"}, {"\uBE68", "\uBE68"}, {"\uBE69", "\uBE69"}, {"\uBE6A", "\uBE6A"}, {"\uBE6B", "\uBE6B"}, {"\uBE6C", "\uBE6C"}, {"\uBE6D", "\uBE6D"}, {"\uBE6E", "\uBE6E"}, {"\uBE6F", "\uBE6F"}, {"\uBE70", "\uBE70"}, {"\uBE71", "\uBE71"}, {"\uBE72", "\uBE72"}, {"\uBE73", "\uBE73"}, {"\uBE74", "\uBE74"}, {"\uBE75", "\uBE75"}, {"\uBE76", "\uBE76"}, {"\uBE77", "\uBE77"}, {"\uBE78", "\uBE78"}, {"\uBE79", "\uBE79"}, {"\uBE7A", "\uBE7A"}, {"\uBE7B", "\uBE7B"}, {"\uBE7C", "\uBE7C"}, {"\uBE7D", "\uBE7D"}, {"\uBE7E", "\uBE7E"}, {"\uBE7F", "\uBE7F"}, {"\uBE80", "\uBE80"}, {"\uBE81", "\uBE81"}, {"\uBE82", "\uBE82"}, {"\uBE83", "\uBE83"}, {"\uBE84", "\uBE84"}, {"\uBE85", "\uBE85"}, {"\uBE86", "\uBE86"}, {"\uBE87", "\uBE87"}, {"\uBE88", "\uBE88"}, {"\uBE89", "\uBE89"}, {"\uBE8A", "\uBE8A"}, {"\uBE8B", "\uBE8B"}, {"\uBE8C", "\uBE8C"}, {"\uBE8D", "\uBE8D"}, {"\uBE8E", "\uBE8E"}, {"\uBE8F", "\uBE8F"}, {"\uBE90", "\uBE90"}, {"\uBE91", "\uBE91"}, {"\uBE92", "\uBE92"}, {"\uBE93", "\uBE93"}, {"\uBE94", "\uBE94"}, {"\uBE95", "\uBE95"}, {"\uBE96", "\uBE96"}, {"\uBE97", "\uBE97"}, {"\uBE98", "\uBE98"}, {"\uBE99", "\uBE99"}, {"\uBE9A", "\uBE9A"}, {"\uBE9B", "\uBE9B"}, {"\uBE9C", "\uBE9C"}, {"\uBE9D", "\uBE9D"}, {"\uBE9E", "\uBE9E"}, {"\uBE9F", "\uBE9F"}, {"\uBEA0", "\uBEA0"}, {"\uBEA1", "\uBEA1"}, {"\uBEA2", "\uBEA2"}, {"\uBEA3", "\uBEA3"}, {"\uBEA4", "\uBEA4"}, {"\uBEA5", "\uBEA5"}, {"\uBEA6", "\uBEA6"}, {"\uBEA7", "\uBEA7"}, {"\uBEA8", "\uBEA8"}, {"\uBEA9", "\uBEA9"}, {"\uBEAA", "\uBEAA"}, {"\uBEAB", "\uBEAB"}, {"\uBEAC", "\uBEAC"}, {"\uBEAD", "\uBEAD"}, {"\uBEAE", "\uBEAE"}, {"\uBEAF", "\uBEAF"}, {"\uBEB0", "\uBEB0"}, {"\uBEB1", "\uBEB1"}, {"\uBEB2", "\uBEB2"}, {"\uBEB3", "\uBEB3"}, {"\uBEB4", "\uBEB4"}, {"\uBEB5", "\uBEB5"}, {"\uBEB6", "\uBEB6"}, {"\uBEB7", "\uBEB7"}, {"\uBEB8", "\uBEB8"}, {"\uBEB9", "\uBEB9"}, {"\uBEBA", "\uBEBA"}, {"\uBEBB", "\uBEBB"}, {"\uBEBC", "\uBEBC"}, {"\uBEBD", "\uBEBD"}, {"\uBEBE", "\uBEBE"}, {"\uBEBF", "\uBEBF"}, {"\uBEC0", "\uBEC0"}, {"\uBEC1", "\uBEC1"}, {"\uBEC2", "\uBEC2"}, {"\uBEC3", "\uBEC3"}, {"\uBEC4", "\uBEC4"}, {"\uBEC5", "\uBEC5"}, {"\uBEC6", "\uBEC6"}, {"\uBEC7", "\uBEC7"}, {"\uBEC8", "\uBEC8"}, {"\uBEC9", "\uBEC9"}, {"\uBECA", "\uBECA"}, {"\uBECB", "\uBECB"}, {"\uBECC", "\uBECC"}, {"\uBECD", "\uBECD"}, {"\uBECE", "\uBECE"}, {"\uBECF", "\uBECF"}, {"\uBED0", "\uBED0"}, {"\uBED1", "\uBED1"}, {"\uBED2", "\uBED2"}, {"\uBED3", "\uBED3"}, {"\uBED4", "\uBED4"}, {"\uBED5", "\uBED5"}, {"\uBED6", "\uBED6"}, {"\uBED7", "\uBED7"}, {"\uBED8", "\uBED8"}, {"\uBED9", "\uBED9"}, {"\uBEDA", "\uBEDA"}, {"\uBEDB", "\uBEDB"}, {"\uBEDC", "\uBEDC"}, {"\uBEDD", "\uBEDD"}, {"\uBEDE", "\uBEDE"}, {"\uBEDF", "\uBEDF"}, {"\uBEE0", "\uBEE0"}, {"\uBEE1", "\uBEE1"}, {"\uBEE2", "\uBEE2"}, {"\uBEE3", "\uBEE3"}, {"\uBEE4", "\uBEE4"}, {"\uBEE5", "\uBEE5"}, {"\uBEE6", "\uBEE6"}, {"\uBEE7", "\uBEE7"}, {"\uBEE8", "\uBEE8"}, {"\uBEE9", "\uBEE9"}, {"\uBEEA", "\uBEEA"}, {"\uBEEB", "\uBEEB"}, {"\uBEEC", "\uBEEC"}, {"\uBEED", "\uBEED"}, {"\uBEEE", "\uBEEE"}, {"\uBEEF", "\uBEEF"}, {"\uBEF0", "\uBEF0"}, {"\uBEF1", "\uBEF1"}, {"\uBEF2", "\uBEF2"}, {"\uBEF3", "\uBEF3"}, {"\uBEF4", "\uBEF4"}, {"\uBEF5", "\uBEF5"}, {"\uBEF6", "\uBEF6"}, {"\uBEF7", "\uBEF7"}, {"\uBEF8", "\uBEF8"}, {"\uBEF9", "\uBEF9"}, {"\uBEFA", "\uBEFA"}, {"\uBEFB", "\uBEFB"}, {"\uBEFC", "\uBEFC"}, {"\uBEFD", "\uBEFD"}, {"\uBEFE", "\uBEFE"}, {"\uBEFF", "\uBEFF"}, {"\uBF00", "\uBF00"}, {"\uBF01", "\uBF01"}, {"\uBF02", "\uBF02"}, {"\uBF03", "\uBF03"}, {"\uBF04", "\uBF04"}, {"\uBF05", "\uBF05"}, {"\uBF06", "\uBF06"}, {"\uBF07", "\uBF07"}, {"\uBF08", "\uBF08"}, {"\uBF09", "\uBF09"}, {"\uBF0A", "\uBF0A"}, {"\uBF0B", "\uBF0B"}, {"\uBF0C", "\uBF0C"}, {"\uBF0D", "\uBF0D"}, {"\uBF0E", "\uBF0E"}, {"\uBF0F", "\uBF0F"}, {"\uBF10", "\uBF10"}, {"\uBF11", "\uBF11"}, {"\uBF12", "\uBF12"}, {"\uBF13", "\uBF13"}, {"\uBF14", "\uBF14"}, {"\uBF15", "\uBF15"}, {"\uBF16", "\uBF16"}, {"\uBF17", "\uBF17"}, {"\uBF18", "\uBF18"}, {"\uBF19", "\uBF19"}, {"\uBF1A", "\uBF1A"}, {"\uBF1B", "\uBF1B"}, {"\uBF1C", "\uBF1C"}, {"\uBF1D", "\uBF1D"}, {"\uBF1E", "\uBF1E"}, {"\uBF1F", "\uBF1F"}, {"\uBF20", "\uBF20"}, {"\uBF21", "\uBF21"}, {"\uBF22", "\uBF22"}, {"\uBF23", "\uBF23"}, {"\uBF24", "\uBF24"}, {"\uBF25", "\uBF25"}, {"\uBF26", "\uBF26"}, {"\uBF27", "\uBF27"}, {"\uBF28", "\uBF28"}, {"\uBF29", "\uBF29"}, {"\uBF2A", "\uBF2A"}, {"\uBF2B", "\uBF2B"}, {"\uBF2C", "\uBF2C"}, {"\uBF2D", "\uBF2D"}, {"\uBF2E", "\uBF2E"}, {"\uBF2F", "\uBF2F"}, {"\uBF30", "\uBF30"}, {"\uBF31", "\uBF31"}, {"\uBF32", "\uBF32"}, {"\uBF33", "\uBF33"}, {"\uBF34", "\uBF34"}, {"\uBF35", "\uBF35"}, {"\uBF36", "\uBF36"}, {"\uBF37", "\uBF37"}, {"\uBF38", "\uBF38"}, {"\uBF39", "\uBF39"}, {"\uBF3A", "\uBF3A"}, {"\uBF3B", "\uBF3B"}, {"\uBF3C", "\uBF3C"}, {"\uBF3D", "\uBF3D"}, {"\uBF3E", "\uBF3E"}, {"\uBF3F", "\uBF3F"}, {"\uBF40", "\uBF40"}, {"\uBF41", "\uBF41"}, {"\uBF42", "\uBF42"}, {"\uBF43", "\uBF43"}, {"\uBF44", "\uBF44"}, {"\uBF45", "\uBF45"}, {"\uBF46", "\uBF46"}, {"\uBF47", "\uBF47"}, {"\uBF48", "\uBF48"}, {"\uBF49", "\uBF49"}, {"\uBF4A", "\uBF4A"}, {"\uBF4B", "\uBF4B"}, {"\uBF4C", "\uBF4C"}, {"\uBF4D", "\uBF4D"}, {"\uBF4E", "\uBF4E"}, {"\uBF4F", "\uBF4F"}, {"\uBF50", "\uBF50"}, {"\uBF51", "\uBF51"}, {"\uBF52", "\uBF52"}, {"\uBF53", "\uBF53"}, {"\uBF54", "\uBF54"}, {"\uBF55", "\uBF55"}, {"\uBF56", "\uBF56"}, {"\uBF57", "\uBF57"}, {"\uBF58", "\uBF58"}, {"\uBF59", "\uBF59"}, {"\uBF5A", "\uBF5A"}, {"\uBF5B", "\uBF5B"}, {"\uBF5C", "\uBF5C"}, {"\uBF5D", "\uBF5D"}, {"\uBF5E", "\uBF5E"}, {"\uBF5F", "\uBF5F"}, {"\uBF60", "\uBF60"}, {"\uBF61", "\uBF61"}, {"\uBF62", "\uBF62"}, {"\uBF63", "\uBF63"}, {"\uBF64", "\uBF64"}, {"\uBF65", "\uBF65"}, {"\uBF66", "\uBF66"}, {"\uBF67", "\uBF67"}, {"\uBF68", "\uBF68"}, {"\uBF69", "\uBF69"}, {"\uBF6A", "\uBF6A"}, {"\uBF6B", "\uBF6B"}, {"\uBF6C", "\uBF6C"}, {"\uBF6D", "\uBF6D"}, {"\uBF6E", "\uBF6E"}, {"\uBF6F", "\uBF6F"}, {"\uBF70", "\uBF70"}, {"\uBF71", "\uBF71"}, {"\uBF72", "\uBF72"}, {"\uBF73", "\uBF73"}, {"\uBF74", "\uBF74"}, {"\uBF75", "\uBF75"}, {"\uBF76", "\uBF76"}, {"\uBF77", "\uBF77"}, {"\uBF78", "\uBF78"}, {"\uBF79", "\uBF79"}, {"\uBF7A", "\uBF7A"}, {"\uBF7B", "\uBF7B"}, {"\uBF7C", "\uBF7C"}, {"\uBF7D", "\uBF7D"}, {"\uBF7E", "\uBF7E"}, {"\uBF7F", "\uBF7F"}, {"\uBF80", "\uBF80"}, {"\uBF81", "\uBF81"}, {"\uBF82", "\uBF82"}, {"\uBF83", "\uBF83"}, {"\uBF84", "\uBF84"}, {"\uBF85", "\uBF85"}, {"\uBF86", "\uBF86"}, {"\uBF87", "\uBF87"}, {"\uBF88", "\uBF88"}, {"\uBF89", "\uBF89"}, {"\uBF8A", "\uBF8A"}, {"\uBF8B", "\uBF8B"}, {"\uBF8C", "\uBF8C"}, {"\uBF8D", "\uBF8D"}, {"\uBF8E", "\uBF8E"}, {"\uBF8F", "\uBF8F"}, {"\uBF90", "\uBF90"}, {"\uBF91", "\uBF91"}, {"\uBF92", "\uBF92"}, {"\uBF93", "\uBF93"}, {"\uBF94", "\uBF94"}, {"\uBF95", "\uBF95"}, {"\uBF96", "\uBF96"}, {"\uBF97", "\uBF97"}, {"\uBF98", "\uBF98"}, {"\uBF99", "\uBF99"}, {"\uBF9A", "\uBF9A"}, {"\uBF9B", "\uBF9B"}, {"\uBF9C", "\uBF9C"}, {"\uBF9D", "\uBF9D"}, {"\uBF9E", "\uBF9E"}, {"\uBF9F", "\uBF9F"}, {"\uBFA0", "\uBFA0"}, {"\uBFA1", "\uBFA1"}, {"\uBFA2", "\uBFA2"}, {"\uBFA3", "\uBFA3"}, {"\uBFA4", "\uBFA4"}, {"\uBFA5", "\uBFA5"}, {"\uBFA6", "\uBFA6"}, {"\uBFA7", "\uBFA7"}, {"\uBFA8", "\uBFA8"}, {"\uBFA9", "\uBFA9"}, {"\uBFAA", "\uBFAA"}, {"\uBFAB", "\uBFAB"}, {"\uBFAC", "\uBFAC"}, {"\uBFAD", "\uBFAD"}, {"\uBFAE", "\uBFAE"}, {"\uBFAF", "\uBFAF"}, {"\uBFB0", "\uBFB0"}, {"\uBFB1", "\uBFB1"}, {"\uBFB2", "\uBFB2"}, {"\uBFB3", "\uBFB3"}, {"\uBFB4", "\uBFB4"}, {"\uBFB5", "\uBFB5"}, {"\uBFB6", "\uBFB6"}, {"\uBFB7", "\uBFB7"}, {"\uBFB8", "\uBFB8"}, {"\uBFB9", "\uBFB9"}, {"\uBFBA", "\uBFBA"}, {"\uBFBB", "\uBFBB"}, {"\uBFBC", "\uBFBC"}, {"\uBFBD", "\uBFBD"}, {"\uBFBE", "\uBFBE"}, {"\uBFBF", "\uBFBF"}, {"\uBFC0", "\uBFC0"}, {"\uBFC1", "\uBFC1"}, {"\uBFC2", "\uBFC2"}, {"\uBFC3", "\uBFC3"}, {"\uBFC4", "\uBFC4"}, {"\uBFC5", "\uBFC5"}, {"\uBFC6", "\uBFC6"}, {"\uBFC7", "\uBFC7"}, {"\uBFC8", "\uBFC8"}, {"\uBFC9", "\uBFC9"}, {"\uBFCA", "\uBFCA"}, {"\uBFCB", "\uBFCB"}, {"\uBFCC", "\uBFCC"}, {"\uBFCD", "\uBFCD"}, {"\uBFCE", "\uBFCE"}, {"\uBFCF", "\uBFCF"}, {"\uBFD0", "\uBFD0"}, {"\uBFD1", "\uBFD1"}, {"\uBFD2", "\uBFD2"}, {"\uBFD3", "\uBFD3"}, {"\uBFD4", "\uBFD4"}, {"\uBFD5", "\uBFD5"}, {"\uBFD6", "\uBFD6"}, {"\uBFD7", "\uBFD7"}, {"\uBFD8", "\uBFD8"}, {"\uBFD9", "\uBFD9"}, {"\uBFDA", "\uBFDA"}, {"\uBFDB", "\uBFDB"}, {"\uBFDC", "\uBFDC"}, {"\uBFDD", "\uBFDD"}, {"\uBFDE", "\uBFDE"}, {"\uBFDF", "\uBFDF"}, {"\uBFE0", "\uBFE0"}, {"\uBFE1", "\uBFE1"}, {"\uBFE2", "\uBFE2"}, {"\uBFE3", "\uBFE3"}, {"\uBFE4", "\uBFE4"}, {"\uBFE5", "\uBFE5"}, {"\uBFE6", "\uBFE6"}, {"\uBFE7", "\uBFE7"}, {"\uBFE8", "\uBFE8"}, {"\uBFE9", "\uBFE9"}, {"\uBFEA", "\uBFEA"}, {"\uBFEB", "\uBFEB"}, {"\uBFEC", "\uBFEC"}, {"\uBFED", "\uBFED"}, {"\uBFEE", "\uBFEE"}, {"\uBFEF", "\uBFEF"}, {"\uBFF0", "\uBFF0"}, {"\uBFF1", "\uBFF1"}, {"\uBFF2", "\uBFF2"}, {"\uBFF3", "\uBFF3"}, {"\uBFF4", "\uBFF4"}, {"\uBFF5", "\uBFF5"}, {"\uBFF6", "\uBFF6"}, {"\uBFF7", "\uBFF7"}, {"\uBFF8", "\uBFF8"}, {"\uBFF9", "\uBFF9"}, {"\uBFFA", "\uBFFA"}, {"\uBFFB", "\uBFFB"}, {"\uBFFC", "\uBFFC"}, {"\uBFFD", "\uBFFD"}, {"\uBFFE", "\uBFFE"}, {"\uBFFF", "\uBFFF"}, {"\uC000", "\uC000"}, {"\uC001", "\uC001"}, {"\uC002", "\uC002"}, {"\uC003", "\uC003"}, {"\uC004", "\uC004"}, {"\uC005", "\uC005"}, {"\uC006", "\uC006"}, {"\uC007", "\uC007"}, {"\uC008", "\uC008"}, {"\uC009", "\uC009"}, {"\uC00A", "\uC00A"}, {"\uC00B", "\uC00B"}, {"\uC00C", "\uC00C"}, {"\uC00D", "\uC00D"}, {"\uC00E", "\uC00E"}, {"\uC00F", "\uC00F"}, {"\uC010", "\uC010"}, {"\uC011", "\uC011"}, {"\uC012", "\uC012"}, {"\uC013", "\uC013"}, {"\uC014", "\uC014"}, {"\uC015", "\uC015"}, {"\uC016", "\uC016"}, {"\uC017", "\uC017"}, {"\uC018", "\uC018"}, {"\uC019", "\uC019"}, {"\uC01A", "\uC01A"}, {"\uC01B", "\uC01B"}, {"\uC01C", "\uC01C"}, {"\uC01D", "\uC01D"}, {"\uC01E", "\uC01E"}, {"\uC01F", "\uC01F"}, {"\uC020", "\uC020"}, {"\uC021", "\uC021"}, {"\uC022", "\uC022"}, {"\uC023", "\uC023"}, {"\uC024", "\uC024"}, {"\uC025", "\uC025"}, {"\uC026", "\uC026"}, {"\uC027", "\uC027"}, {"\uC028", "\uC028"}, {"\uC029", "\uC029"}, {"\uC02A", "\uC02A"}, {"\uC02B", "\uC02B"}, {"\uC02C", "\uC02C"}, {"\uC02D", "\uC02D"}, {"\uC02E", "\uC02E"}, {"\uC02F", "\uC02F"}, {"\uC030", "\uC030"}, {"\uC031", "\uC031"}, {"\uC032", "\uC032"}, {"\uC033", "\uC033"}, {"\uC034", "\uC034"}, {"\uC035", "\uC035"}, {"\uC036", "\uC036"}, {"\uC037", "\uC037"}, {"\uC038", "\uC038"}, {"\uC039", "\uC039"}, {"\uC03A", "\uC03A"}, {"\uC03B", "\uC03B"}, {"\uC03C", "\uC03C"}, {"\uC03D", "\uC03D"}, {"\uC03E", "\uC03E"}, {"\uC03F", "\uC03F"}, {"\uC040", "\uC040"}, {"\uC041", "\uC041"}, {"\uC042", "\uC042"}, {"\uC043", "\uC043"}, {"\uC044", "\uC044"}, {"\uC045", "\uC045"}, {"\uC046", "\uC046"}, {"\uC047", "\uC047"}, {"\uC048", "\uC048"}, {"\uC049", "\uC049"}, {"\uC04A", "\uC04A"}, {"\uC04B", "\uC04B"}, {"\uC04C", "\uC04C"}, {"\uC04D", "\uC04D"}, {"\uC04E", "\uC04E"}, {"\uC04F", "\uC04F"}, {"\uC050", "\uC050"}, {"\uC051", "\uC051"}, {"\uC052", "\uC052"}, {"\uC053", "\uC053"}, {"\uC054", "\uC054"}, {"\uC055", "\uC055"}, {"\uC056", "\uC056"}, {"\uC057", "\uC057"}, {"\uC058", "\uC058"}, {"\uC059", "\uC059"}, {"\uC05A", "\uC05A"}, {"\uC05B", "\uC05B"}, {"\uC05C", "\uC05C"}, {"\uC05D", "\uC05D"}, {"\uC05E", "\uC05E"}, {"\uC05F", "\uC05F"}, {"\uC060", "\uC060"}, {"\uC061", "\uC061"}, {"\uC062", "\uC062"}, {"\uC063", "\uC063"}, {"\uC064", "\uC064"}, {"\uC065", "\uC065"}, {"\uC066", "\uC066"}, {"\uC067", "\uC067"}, {"\uC068", "\uC068"}, {"\uC069", "\uC069"}, {"\uC06A", "\uC06A"}, {"\uC06B", "\uC06B"}, {"\uC06C", "\uC06C"}, {"\uC06D", "\uC06D"}, {"\uC06E", "\uC06E"}, {"\uC06F", "\uC06F"}, {"\uC070", "\uC070"}, {"\uC071", "\uC071"}, {"\uC072", "\uC072"}, {"\uC073", "\uC073"}, {"\uC074", "\uC074"}, {"\uC075", "\uC075"}, {"\uC076", "\uC076"}, {"\uC077", "\uC077"}, {"\uC078", "\uC078"}, {"\uC079", "\uC079"}, {"\uC07A", "\uC07A"}, {"\uC07B", "\uC07B"}, {"\uC07C", "\uC07C"}, {"\uC07D", "\uC07D"}, {"\uC07E", "\uC07E"}, {"\uC07F", "\uC07F"}, {"\uC080", "\uC080"}, {"\uC081", "\uC081"}, {"\uC082", "\uC082"}, {"\uC083", "\uC083"}, {"\uC084", "\uC084"}, {"\uC085", "\uC085"}, {"\uC086", "\uC086"}, {"\uC087", "\uC087"}, {"\uC088", "\uC088"}, {"\uC089", "\uC089"}, {"\uC08A", "\uC08A"}, {"\uC08B", "\uC08B"}, {"\uC08C", "\uC08C"}, {"\uC08D", "\uC08D"}, {"\uC08E", "\uC08E"}, {"\uC08F", "\uC08F"}, {"\uC090", "\uC090"}, {"\uC091", "\uC091"}, {"\uC092", "\uC092"}, {"\uC093", "\uC093"}, {"\uC094", "\uC094"}, {"\uC095", "\uC095"}, {"\uC096", "\uC096"}, {"\uC097", "\uC097"}, {"\uC098", "\uC098"}, {"\uC099", "\uC099"}, {"\uC09A", "\uC09A"}, {"\uC09B", "\uC09B"}, {"\uC09C", "\uC09C"}, {"\uC09D", "\uC09D"}, {"\uC09E", "\uC09E"}, {"\uC09F", "\uC09F"}, {"\uC0A0", "\uC0A0"}, {"\uC0A1", "\uC0A1"}, {"\uC0A2", "\uC0A2"}, {"\uC0A3", "\uC0A3"}, {"\uC0A4", "\uC0A4"}, {"\uC0A5", "\uC0A5"}, {"\uC0A6", "\uC0A6"}, {"\uC0A7", "\uC0A7"}, {"\uC0A8", "\uC0A8"}, {"\uC0A9", "\uC0A9"}, {"\uC0AA", "\uC0AA"}, {"\uC0AB", "\uC0AB"}, {"\uC0AC", "\uC0AC"}, {"\uC0AD", "\uC0AD"}, {"\uC0AE", "\uC0AE"}, {"\uC0AF", "\uC0AF"}, {"\uC0B0", "\uC0B0"}, {"\uC0B1", "\uC0B1"}, {"\uC0B2", "\uC0B2"}, {"\uC0B3", "\uC0B3"}, {"\uC0B4", "\uC0B4"}, {"\uC0B5", "\uC0B5"}, {"\uC0B6", "\uC0B6"}, {"\uC0B7", "\uC0B7"}, {"\uC0B8", "\uC0B8"}, {"\uC0B9", "\uC0B9"}, {"\uC0BA", "\uC0BA"}, {"\uC0BB", "\uC0BB"}, {"\uC0BC", "\uC0BC"}, {"\uC0BD", "\uC0BD"}, {"\uC0BE", "\uC0BE"}, {"\uC0BF", "\uC0BF"}, {"\uC0C0", "\uC0C0"}, {"\uC0C1", "\uC0C1"}, {"\uC0C2", "\uC0C2"}, {"\uC0C3", "\uC0C3"}, {"\uC0C4", "\uC0C4"}, {"\uC0C5", "\uC0C5"}, {"\uC0C6", "\uC0C6"}, {"\uC0C7", "\uC0C7"}, {"\uC0C8", "\uC0C8"}, {"\uC0C9", "\uC0C9"}, {"\uC0CA", "\uC0CA"}, {"\uC0CB", "\uC0CB"}, {"\uC0CC", "\uC0CC"}, {"\uC0CD", "\uC0CD"}, {"\uC0CE", "\uC0CE"}, {"\uC0CF", "\uC0CF"}, {"\uC0D0", "\uC0D0"}, {"\uC0D1", "\uC0D1"}, {"\uC0D2", "\uC0D2"}, {"\uC0D3", "\uC0D3"}, {"\uC0D4", "\uC0D4"}, {"\uC0D5", "\uC0D5"}, {"\uC0D6", "\uC0D6"}, {"\uC0D7", "\uC0D7"}, {"\uC0D8", "\uC0D8"}, {"\uC0D9", "\uC0D9"}, {"\uC0DA", "\uC0DA"}, {"\uC0DB", "\uC0DB"}, {"\uC0DC", "\uC0DC"}, {"\uC0DD", "\uC0DD"}, {"\uC0DE", "\uC0DE"}, {"\uC0DF", "\uC0DF"}, {"\uC0E0", "\uC0E0"}, {"\uC0E1", "\uC0E1"}, {"\uC0E2", "\uC0E2"}, {"\uC0E3", "\uC0E3"}, {"\uC0E4", "\uC0E4"}, {"\uC0E5", "\uC0E5"}, {"\uC0E6", "\uC0E6"}, {"\uC0E7", "\uC0E7"}, {"\uC0E8", "\uC0E8"}, {"\uC0E9", "\uC0E9"}, {"\uC0EA", "\uC0EA"}, {"\uC0EB", "\uC0EB"}, {"\uC0EC", "\uC0EC"}, {"\uC0ED", "\uC0ED"}, {"\uC0EE", "\uC0EE"}, {"\uC0EF", "\uC0EF"}, {"\uC0F0", "\uC0F0"}, {"\uC0F1", "\uC0F1"}, {"\uC0F2", "\uC0F2"}, {"\uC0F3", "\uC0F3"}, {"\uC0F4", "\uC0F4"}, {"\uC0F5", "\uC0F5"}, {"\uC0F6", "\uC0F6"}, {"\uC0F7", "\uC0F7"}, {"\uC0F8", "\uC0F8"}, {"\uC0F9", "\uC0F9"}, {"\uC0FA", "\uC0FA"}, {"\uC0FB", "\uC0FB"}, {"\uC0FC", "\uC0FC"}, {"\uC0FD", "\uC0FD"}, {"\uC0FE", "\uC0FE"}, {"\uC0FF", "\uC0FF"}, {"\uC100", "\uC100"}, {"\uC101", "\uC101"}, {"\uC102", "\uC102"}, {"\uC103", "\uC103"}, {"\uC104", "\uC104"}, {"\uC105", "\uC105"}, {"\uC106", "\uC106"}, {"\uC107", "\uC107"}, {"\uC108", "\uC108"}, {"\uC109", "\uC109"}, {"\uC10A", "\uC10A"}, {"\uC10B", "\uC10B"}, {"\uC10C", "\uC10C"}, {"\uC10D", "\uC10D"}, {"\uC10E", "\uC10E"}, {"\uC10F", "\uC10F"}, {"\uC110", "\uC110"}, {"\uC111", "\uC111"}, {"\uC112", "\uC112"}, {"\uC113", "\uC113"}, {"\uC114", "\uC114"}, {"\uC115", "\uC115"}, {"\uC116", "\uC116"}, {"\uC117", "\uC117"}, {"\uC118", "\uC118"}, {"\uC119", "\uC119"}, {"\uC11A", "\uC11A"}, {"\uC11B", "\uC11B"}, {"\uC11C", "\uC11C"}, {"\uC11D", "\uC11D"}, {"\uC11E", "\uC11E"}, {"\uC11F", "\uC11F"}, {"\uC120", "\uC120"}, {"\uC121", "\uC121"}, {"\uC122", "\uC122"}, {"\uC123", "\uC123"}, {"\uC124", "\uC124"}, {"\uC125", "\uC125"}, {"\uC126", "\uC126"}, {"\uC127", "\uC127"}, {"\uC128", "\uC128"}, {"\uC129", "\uC129"}, {"\uC12A", "\uC12A"}, {"\uC12B", "\uC12B"}, {"\uC12C", "\uC12C"}, {"\uC12D", "\uC12D"}, {"\uC12E", "\uC12E"}, {"\uC12F", "\uC12F"}, {"\uC130", "\uC130"}, {"\uC131", "\uC131"}, {"\uC132", "\uC132"}, {"\uC133", "\uC133"}, {"\uC134", "\uC134"}, {"\uC135", "\uC135"}, {"\uC136", "\uC136"}, {"\uC137", "\uC137"}, {"\uC138", "\uC138"}, {"\uC139", "\uC139"}, {"\uC13A", "\uC13A"}, {"\uC13B", "\uC13B"}, {"\uC13C", "\uC13C"}, {"\uC13D", "\uC13D"}, {"\uC13E", "\uC13E"}, {"\uC13F", "\uC13F"}, {"\uC140", "\uC140"}, {"\uC141", "\uC141"}, {"\uC142", "\uC142"}, {"\uC143", "\uC143"}, {"\uC144", "\uC144"}, {"\uC145", "\uC145"}, {"\uC146", "\uC146"}, {"\uC147", "\uC147"}, {"\uC148", "\uC148"}, {"\uC149", "\uC149"}, {"\uC14A", "\uC14A"}, {"\uC14B", "\uC14B"}, {"\uC14C", "\uC14C"}, {"\uC14D", "\uC14D"}, {"\uC14E", "\uC14E"}, {"\uC14F", "\uC14F"}, {"\uC150", "\uC150"}, {"\uC151", "\uC151"}, {"\uC152", "\uC152"}, {"\uC153", "\uC153"}, {"\uC154", "\uC154"}, {"\uC155", "\uC155"}, {"\uC156", "\uC156"}, {"\uC157", "\uC157"}, {"\uC158", "\uC158"}, {"\uC159", "\uC159"}, {"\uC15A", "\uC15A"}, {"\uC15B", "\uC15B"}, {"\uC15C", "\uC15C"}, {"\uC15D", "\uC15D"}, {"\uC15E", "\uC15E"}, {"\uC15F", "\uC15F"}, {"\uC160", "\uC160"}, {"\uC161", "\uC161"}, {"\uC162", "\uC162"}, {"\uC163", "\uC163"}, {"\uC164", "\uC164"}, {"\uC165", "\uC165"}, {"\uC166", "\uC166"}, {"\uC167", "\uC167"}, {"\uC168", "\uC168"}, {"\uC169", "\uC169"}, {"\uC16A", "\uC16A"}, {"\uC16B", "\uC16B"}, {"\uC16C", "\uC16C"}, {"\uC16D", "\uC16D"}, {"\uC16E", "\uC16E"}, {"\uC16F", "\uC16F"}, {"\uC170", "\uC170"}, {"\uC171", "\uC171"}, {"\uC172", "\uC172"}, {"\uC173", "\uC173"}, {"\uC174", "\uC174"}, {"\uC175", "\uC175"}, {"\uC176", "\uC176"}, {"\uC177", "\uC177"}, {"\uC178", "\uC178"}, {"\uC179", "\uC179"}, {"\uC17A", "\uC17A"}, {"\uC17B", "\uC17B"}, {"\uC17C", "\uC17C"}, {"\uC17D", "\uC17D"}, {"\uC17E", "\uC17E"}, {"\uC17F", "\uC17F"}, {"\uC180", "\uC180"}, {"\uC181", "\uC181"}, {"\uC182", "\uC182"}, {"\uC183", "\uC183"}, {"\uC184", "\uC184"}, {"\uC185", "\uC185"}, {"\uC186", "\uC186"}, {"\uC187", "\uC187"}, {"\uC188", "\uC188"}, {"\uC189", "\uC189"}, {"\uC18A", "\uC18A"}, {"\uC18B", "\uC18B"}, {"\uC18C", "\uC18C"}, {"\uC18D", "\uC18D"}, {"\uC18E", "\uC18E"}, {"\uC18F", "\uC18F"}, {"\uC190", "\uC190"}, {"\uC191", "\uC191"}, {"\uC192", "\uC192"}, {"\uC193", "\uC193"}, {"\uC194", "\uC194"}, {"\uC195", "\uC195"}, {"\uC196", "\uC196"}, {"\uC197", "\uC197"}, {"\uC198", "\uC198"}, {"\uC199", "\uC199"}, {"\uC19A", "\uC19A"}, {"\uC19B", "\uC19B"}, {"\uC19C", "\uC19C"}, {"\uC19D", "\uC19D"}, {"\uC19E", "\uC19E"}, {"\uC19F", "\uC19F"}, {"\uC1A0", "\uC1A0"}, {"\uC1A1", "\uC1A1"}, {"\uC1A2", "\uC1A2"}, {"\uC1A3", "\uC1A3"}, {"\uC1A4", "\uC1A4"}, {"\uC1A5", "\uC1A5"}, {"\uC1A6", "\uC1A6"}, {"\uC1A7", "\uC1A7"}, {"\uC1A8", "\uC1A8"}, {"\uC1A9", "\uC1A9"}, {"\uC1AA", "\uC1AA"}, {"\uC1AB", "\uC1AB"}, {"\uC1AC", "\uC1AC"}, {"\uC1AD", "\uC1AD"}, {"\uC1AE", "\uC1AE"}, {"\uC1AF", "\uC1AF"}, {"\uC1B0", "\uC1B0"}, {"\uC1B1", "\uC1B1"}, {"\uC1B2", "\uC1B2"}, {"\uC1B3", "\uC1B3"}, {"\uC1B4", "\uC1B4"}, {"\uC1B5", "\uC1B5"}, {"\uC1B6", "\uC1B6"}, {"\uC1B7", "\uC1B7"}, {"\uC1B8", "\uC1B8"}, {"\uC1B9", "\uC1B9"}, {"\uC1BA", "\uC1BA"}, {"\uC1BB", "\uC1BB"}, {"\uC1BC", "\uC1BC"}, {"\uC1BD", "\uC1BD"}, {"\uC1BE", "\uC1BE"}, {"\uC1BF", "\uC1BF"}, {"\uC1C0", "\uC1C0"}, {"\uC1C1", "\uC1C1"}, {"\uC1C2", "\uC1C2"}, {"\uC1C3", "\uC1C3"}, {"\uC1C4", "\uC1C4"}, {"\uC1C5", "\uC1C5"}, {"\uC1C6", "\uC1C6"}, {"\uC1C7", "\uC1C7"}, {"\uC1C8", "\uC1C8"}, {"\uC1C9", "\uC1C9"}, {"\uC1CA", "\uC1CA"}, {"\uC1CB", "\uC1CB"}, {"\uC1CC", "\uC1CC"}, {"\uC1CD", "\uC1CD"}, {"\uC1CE", "\uC1CE"}, {"\uC1CF", "\uC1CF"}, {"\uC1D0", "\uC1D0"}, {"\uC1D1", "\uC1D1"}, {"\uC1D2", "\uC1D2"}, {"\uC1D3", "\uC1D3"}, {"\uC1D4", "\uC1D4"}, {"\uC1D5", "\uC1D5"}, {"\uC1D6", "\uC1D6"}, {"\uC1D7", "\uC1D7"}, {"\uC1D8", "\uC1D8"}, {"\uC1D9", "\uC1D9"}, {"\uC1DA", "\uC1DA"}, {"\uC1DB", "\uC1DB"}, {"\uC1DC", "\uC1DC"}, {"\uC1DD", "\uC1DD"}, {"\uC1DE", "\uC1DE"}, {"\uC1DF", "\uC1DF"}, {"\uC1E0", "\uC1E0"}, {"\uC1E1", "\uC1E1"}, {"\uC1E2", "\uC1E2"}, {"\uC1E3", "\uC1E3"}, {"\uC1E4", "\uC1E4"}, {"\uC1E5", "\uC1E5"}, {"\uC1E6", "\uC1E6"}, {"\uC1E7", "\uC1E7"}, {"\uC1E8", "\uC1E8"}, {"\uC1E9", "\uC1E9"}, {"\uC1EA", "\uC1EA"}, {"\uC1EB", "\uC1EB"}, {"\uC1EC", "\uC1EC"}, {"\uC1ED", "\uC1ED"}, {"\uC1EE", "\uC1EE"}, {"\uC1EF", "\uC1EF"}, {"\uC1F0", "\uC1F0"}, {"\uC1F1", "\uC1F1"}, {"\uC1F2", "\uC1F2"}, {"\uC1F3", "\uC1F3"}, {"\uC1F4", "\uC1F4"}, {"\uC1F5", "\uC1F5"}, {"\uC1F6", "\uC1F6"}, {"\uC1F7", "\uC1F7"}, {"\uC1F8", "\uC1F8"}, {"\uC1F9", "\uC1F9"}, {"\uC1FA", "\uC1FA"}, {"\uC1FB", "\uC1FB"}, {"\uC1FC", "\uC1FC"}, {"\uC1FD", "\uC1FD"}, {"\uC1FE", "\uC1FE"}, {"\uC1FF", "\uC1FF"}, {"\uC200", "\uC200"}, {"\uC201", "\uC201"}, {"\uC202", "\uC202"}, {"\uC203", "\uC203"}, {"\uC204", "\uC204"}, {"\uC205", "\uC205"}, {"\uC206", "\uC206"}, {"\uC207", "\uC207"}, {"\uC208", "\uC208"}, {"\uC209", "\uC209"}, {"\uC20A", "\uC20A"}, {"\uC20B", "\uC20B"}, {"\uC20C", "\uC20C"}, {"\uC20D", "\uC20D"}, {"\uC20E", "\uC20E"}, {"\uC20F", "\uC20F"}, {"\uC210", "\uC210"}, {"\uC211", "\uC211"}, {"\uC212", "\uC212"}, {"\uC213", "\uC213"}, {"\uC214", "\uC214"}, {"\uC215", "\uC215"}, {"\uC216", "\uC216"}, {"\uC217", "\uC217"}, {"\uC218", "\uC218"}, {"\uC219", "\uC219"}, {"\uC21A", "\uC21A"}, {"\uC21B", "\uC21B"}, {"\uC21C", "\uC21C"}, {"\uC21D", "\uC21D"}, {"\uC21E", "\uC21E"}, {"\uC21F", "\uC21F"}, {"\uC220", "\uC220"}, {"\uC221", "\uC221"}, {"\uC222", "\uC222"}, {"\uC223", "\uC223"}, {"\uC224", "\uC224"}, {"\uC225", "\uC225"}, {"\uC226", "\uC226"}, {"\uC227", "\uC227"}, {"\uC228", "\uC228"}, {"\uC229", "\uC229"}, {"\uC22A", "\uC22A"}, {"\uC22B", "\uC22B"}, {"\uC22C", "\uC22C"}, {"\uC22D", "\uC22D"}, {"\uC22E", "\uC22E"}, {"\uC22F", "\uC22F"}, {"\uC230", "\uC230"}, {"\uC231", "\uC231"}, {"\uC232", "\uC232"}, {"\uC233", "\uC233"}, {"\uC234", "\uC234"}, {"\uC235", "\uC235"}, {"\uC236", "\uC236"}, {"\uC237", "\uC237"}, {"\uC238", "\uC238"}, {"\uC239", "\uC239"}, {"\uC23A", "\uC23A"}, {"\uC23B", "\uC23B"}, {"\uC23C", "\uC23C"}, {"\uC23D", "\uC23D"}, {"\uC23E", "\uC23E"}, {"\uC23F", "\uC23F"}, {"\uC240", "\uC240"}, {"\uC241", "\uC241"}, {"\uC242", "\uC242"}, {"\uC243", "\uC243"}, {"\uC244", "\uC244"}, {"\uC245", "\uC245"}, {"\uC246", "\uC246"}, {"\uC247", "\uC247"}, {"\uC248", "\uC248"}, {"\uC249", "\uC249"}, {"\uC24A", "\uC24A"}, {"\uC24B", "\uC24B"}, {"\uC24C", "\uC24C"}, {"\uC24D", "\uC24D"}, {"\uC24E", "\uC24E"}, {"\uC24F", "\uC24F"}, {"\uC250", "\uC250"}, {"\uC251", "\uC251"}, {"\uC252", "\uC252"}, {"\uC253", "\uC253"}, {"\uC254", "\uC254"}, {"\uC255", "\uC255"}, {"\uC256", "\uC256"}, {"\uC257", "\uC257"}, {"\uC258", "\uC258"}, {"\uC259", "\uC259"}, {"\uC25A", "\uC25A"}, {"\uC25B", "\uC25B"}, {"\uC25C", "\uC25C"}, {"\uC25D", "\uC25D"}, {"\uC25E", "\uC25E"}, {"\uC25F", "\uC25F"}, {"\uC260", "\uC260"}, {"\uC261", "\uC261"}, {"\uC262", "\uC262"}, {"\uC263", "\uC263"}, {"\uC264", "\uC264"}, {"\uC265", "\uC265"}, {"\uC266", "\uC266"}, {"\uC267", "\uC267"}, {"\uC268", "\uC268"}, {"\uC269", "\uC269"}, {"\uC26A", "\uC26A"}, {"\uC26B", "\uC26B"}, {"\uC26C", "\uC26C"}, {"\uC26D", "\uC26D"}, {"\uC26E", "\uC26E"}, {"\uC26F", "\uC26F"}, {"\uC270", "\uC270"}, {"\uC271", "\uC271"}, {"\uC272", "\uC272"}, {"\uC273", "\uC273"}, {"\uC274", "\uC274"}, {"\uC275", "\uC275"}, {"\uC276", "\uC276"}, {"\uC277", "\uC277"}, {"\uC278", "\uC278"}, {"\uC279", "\uC279"}, {"\uC27A", "\uC27A"}, {"\uC27B", "\uC27B"}, {"\uC27C", "\uC27C"}, {"\uC27D", "\uC27D"}, {"\uC27E", "\uC27E"}, {"\uC27F", "\uC27F"}, {"\uC280", "\uC280"}, {"\uC281", "\uC281"}, {"\uC282", "\uC282"}, {"\uC283", "\uC283"}, {"\uC284", "\uC284"}, {"\uC285", "\uC285"}, {"\uC286", "\uC286"}, {"\uC287", "\uC287"}, {"\uC288", "\uC288"}, {"\uC289", "\uC289"}, {"\uC28A", "\uC28A"}, {"\uC28B", "\uC28B"}, {"\uC28C", "\uC28C"}, {"\uC28D", "\uC28D"}, {"\uC28E", "\uC28E"}, {"\uC28F", "\uC28F"}, {"\uC290", "\uC290"}, {"\uC291", "\uC291"}, {"\uC292", "\uC292"}, {"\uC293", "\uC293"}, {"\uC294", "\uC294"}, {"\uC295", "\uC295"}, {"\uC296", "\uC296"}, {"\uC297", "\uC297"}, {"\uC298", "\uC298"}, {"\uC299", "\uC299"}, {"\uC29A", "\uC29A"}, {"\uC29B", "\uC29B"}, {"\uC29C", "\uC29C"}, {"\uC29D", "\uC29D"}, {"\uC29E", "\uC29E"}, {"\uC29F", "\uC29F"}, {"\uC2A0", "\uC2A0"}, {"\uC2A1", "\uC2A1"}, {"\uC2A2", "\uC2A2"}, {"\uC2A3", "\uC2A3"}, {"\uC2A4", "\uC2A4"}, {"\uC2A5", "\uC2A5"}, {"\uC2A6", "\uC2A6"}, {"\uC2A7", "\uC2A7"}, {"\uC2A8", "\uC2A8"}, {"\uC2A9", "\uC2A9"}, {"\uC2AA", "\uC2AA"}, {"\uC2AB", "\uC2AB"}, {"\uC2AC", "\uC2AC"}, {"\uC2AD", "\uC2AD"}, {"\uC2AE", "\uC2AE"}, {"\uC2AF", "\uC2AF"}, {"\uC2B0", "\uC2B0"}, {"\uC2B1", "\uC2B1"}, {"\uC2B2", "\uC2B2"}, {"\uC2B3", "\uC2B3"}, {"\uC2B4", "\uC2B4"}, {"\uC2B5", "\uC2B5"}, {"\uC2B6", "\uC2B6"}, {"\uC2B7", "\uC2B7"}, {"\uC2B8", "\uC2B8"}, {"\uC2B9", "\uC2B9"}, {"\uC2BA", "\uC2BA"}, {"\uC2BB", "\uC2BB"}, {"\uC2BC", "\uC2BC"}, {"\uC2BD", "\uC2BD"}, {"\uC2BE", "\uC2BE"}, {"\uC2BF", "\uC2BF"}, {"\uC2C0", "\uC2C0"}, {"\uC2C1", "\uC2C1"}, {"\uC2C2", "\uC2C2"}, {"\uC2C3", "\uC2C3"}, {"\uC2C4", "\uC2C4"}, {"\uC2C5", "\uC2C5"}, {"\uC2C6", "\uC2C6"}, {"\uC2C7", "\uC2C7"}, {"\uC2C8", "\uC2C8"}, {"\uC2C9", "\uC2C9"}, {"\uC2CA", "\uC2CA"}, {"\uC2CB", "\uC2CB"}, {"\uC2CC", "\uC2CC"}, {"\uC2CD", "\uC2CD"}, {"\uC2CE", "\uC2CE"}, {"\uC2CF", "\uC2CF"}, {"\uC2D0", "\uC2D0"}, {"\uC2D1", "\uC2D1"}, {"\uC2D2", "\uC2D2"}, {"\uC2D3", "\uC2D3"}, {"\uC2D4", "\uC2D4"}, {"\uC2D5", "\uC2D5"}, {"\uC2D6", "\uC2D6"}, {"\uC2D7", "\uC2D7"}, {"\uC2D8", "\uC2D8"}, {"\uC2D9", "\uC2D9"}, {"\uC2DA", "\uC2DA"}, {"\uC2DB", "\uC2DB"}, {"\uC2DC", "\uC2DC"}, {"\uC2DD", "\uC2DD"}, {"\uC2DE", "\uC2DE"}, {"\uC2DF", "\uC2DF"}, {"\uC2E0", "\uC2E0"}, {"\uC2E1", "\uC2E1"}, {"\uC2E2", "\uC2E2"}, {"\uC2E3", "\uC2E3"}, {"\uC2E4", "\uC2E4"}, {"\uC2E5", "\uC2E5"}, {"\uC2E6", "\uC2E6"}, {"\uC2E7", "\uC2E7"}, {"\uC2E8", "\uC2E8"}, {"\uC2E9", "\uC2E9"}, {"\uC2EA", "\uC2EA"}, {"\uC2EB", "\uC2EB"}, {"\uC2EC", "\uC2EC"}, {"\uC2ED", "\uC2ED"}, {"\uC2EE", "\uC2EE"}, {"\uC2EF", "\uC2EF"}, {"\uC2F0", "\uC2F0"}, {"\uC2F1", "\uC2F1"}, {"\uC2F2", "\uC2F2"}, {"\uC2F3", "\uC2F3"}, {"\uC2F4", "\uC2F4"}, {"\uC2F5", "\uC2F5"}, {"\uC2F6", "\uC2F6"}, {"\uC2F7", "\uC2F7"}, {"\uC2F8", "\uC2F8"}, {"\uC2F9", "\uC2F9"}, {"\uC2FA", "\uC2FA"}, {"\uC2FB", "\uC2FB"}, {"\uC2FC", "\uC2FC"}, {"\uC2FD", "\uC2FD"}, {"\uC2FE", "\uC2FE"}, {"\uC2FF", "\uC2FF"}, {"\uC300", "\uC300"}, {"\uC301", "\uC301"}, {"\uC302", "\uC302"}, {"\uC303", "\uC303"}, {"\uC304", "\uC304"}, {"\uC305", "\uC305"}, {"\uC306", "\uC306"}, {"\uC307", "\uC307"}, {"\uC308", "\uC308"}, {"\uC309", "\uC309"}, {"\uC30A", "\uC30A"}, {"\uC30B", "\uC30B"}, {"\uC30C", "\uC30C"}, {"\uC30D", "\uC30D"}, {"\uC30E", "\uC30E"}, {"\uC30F", "\uC30F"}, {"\uC310", "\uC310"}, {"\uC311", "\uC311"}, {"\uC312", "\uC312"}, {"\uC313", "\uC313"}, {"\uC314", "\uC314"}, {"\uC315", "\uC315"}, {"\uC316", "\uC316"}, {"\uC317", "\uC317"}, {"\uC318", "\uC318"}, {"\uC319", "\uC319"}, {"\uC31A", "\uC31A"}, {"\uC31B", "\uC31B"}, {"\uC31C", "\uC31C"}, {"\uC31D", "\uC31D"}, {"\uC31E", "\uC31E"}, {"\uC31F", "\uC31F"}, {"\uC320", "\uC320"}, {"\uC321", "\uC321"}, {"\uC322", "\uC322"}, {"\uC323", "\uC323"}, {"\uC324", "\uC324"}, {"\uC325", "\uC325"}, {"\uC326", "\uC326"}, {"\uC327", "\uC327"}, {"\uC328", "\uC328"}, {"\uC329", "\uC329"}, {"\uC32A", "\uC32A"}, {"\uC32B", "\uC32B"}, {"\uC32C", "\uC32C"}, {"\uC32D", "\uC32D"}, {"\uC32E", "\uC32E"}, {"\uC32F", "\uC32F"}, {"\uC330", "\uC330"}, {"\uC331", "\uC331"}, {"\uC332", "\uC332"}, {"\uC333", "\uC333"}, {"\uC334", "\uC334"}, {"\uC335", "\uC335"}, {"\uC336", "\uC336"}, {"\uC337", "\uC337"}, {"\uC338", "\uC338"}, {"\uC339", "\uC339"}, {"\uC33A", "\uC33A"}, {"\uC33B", "\uC33B"}, {"\uC33C", "\uC33C"}, {"\uC33D", "\uC33D"}, {"\uC33E", "\uC33E"}, {"\uC33F", "\uC33F"}, {"\uC340", "\uC340"}, {"\uC341", "\uC341"}, {"\uC342", "\uC342"}, {"\uC343", "\uC343"}, {"\uC344", "\uC344"}, {"\uC345", "\uC345"}, {"\uC346", "\uC346"}, {"\uC347", "\uC347"}, {"\uC348", "\uC348"}, {"\uC349", "\uC349"}, {"\uC34A", "\uC34A"}, {"\uC34B", "\uC34B"}, {"\uC34C", "\uC34C"}, {"\uC34D", "\uC34D"}, {"\uC34E", "\uC34E"}, {"\uC34F", "\uC34F"}, {"\uC350", "\uC350"}, {"\uC351", "\uC351"}, {"\uC352", "\uC352"}, {"\uC353", "\uC353"}, {"\uC354", "\uC354"}, {"\uC355", "\uC355"}, {"\uC356", "\uC356"}, {"\uC357", "\uC357"}, {"\uC358", "\uC358"}, {"\uC359", "\uC359"}, {"\uC35A", "\uC35A"}, {"\uC35B", "\uC35B"}, {"\uC35C", "\uC35C"}, {"\uC35D", "\uC35D"}, {"\uC35E", "\uC35E"}, {"\uC35F", "\uC35F"}, {"\uC360", "\uC360"}, {"\uC361", "\uC361"}, {"\uC362", "\uC362"}, {"\uC363", "\uC363"}, {"\uC364", "\uC364"}, {"\uC365", "\uC365"}, {"\uC366", "\uC366"}, {"\uC367", "\uC367"}, {"\uC368", "\uC368"}, {"\uC369", "\uC369"}, {"\uC36A", "\uC36A"}, {"\uC36B", "\uC36B"}, {"\uC36C", "\uC36C"}, {"\uC36D", "\uC36D"}, {"\uC36E", "\uC36E"}, {"\uC36F", "\uC36F"}, {"\uC370", "\uC370"}, {"\uC371", "\uC371"}, {"\uC372", "\uC372"}, {"\uC373", "\uC373"}, {"\uC374", "\uC374"}, {"\uC375", "\uC375"}, {"\uC376", "\uC376"}, {"\uC377", "\uC377"}, {"\uC378", "\uC378"}, {"\uC379", "\uC379"}, {"\uC37A", "\uC37A"}, {"\uC37B", "\uC37B"}, {"\uC37C", "\uC37C"}, {"\uC37D", "\uC37D"}, {"\uC37E", "\uC37E"}, {"\uC37F", "\uC37F"}, {"\uC380", "\uC380"}, {"\uC381", "\uC381"}, {"\uC382", "\uC382"}, {"\uC383", "\uC383"}, {"\uC384", "\uC384"}, {"\uC385", "\uC385"}, {"\uC386", "\uC386"}, {"\uC387", "\uC387"}, {"\uC388", "\uC388"}, {"\uC389", "\uC389"}, {"\uC38A", "\uC38A"}, {"\uC38B", "\uC38B"}, {"\uC38C", "\uC38C"}, {"\uC38D", "\uC38D"}, {"\uC38E", "\uC38E"}, {"\uC38F", "\uC38F"}, {"\uC390", "\uC390"}, {"\uC391", "\uC391"}, {"\uC392", "\uC392"}, {"\uC393", "\uC393"}, {"\uC394", "\uC394"}, {"\uC395", "\uC395"}, {"\uC396", "\uC396"}, {"\uC397", "\uC397"}, {"\uC398", "\uC398"}, {"\uC399", "\uC399"}, {"\uC39A", "\uC39A"}, {"\uC39B", "\uC39B"}, {"\uC39C", "\uC39C"}, {"\uC39D", "\uC39D"}, {"\uC39E", "\uC39E"}, {"\uC39F", "\uC39F"}, {"\uC3A0", "\uC3A0"}, {"\uC3A1", "\uC3A1"}, {"\uC3A2", "\uC3A2"}, {"\uC3A3", "\uC3A3"}, {"\uC3A4", "\uC3A4"}, {"\uC3A5", "\uC3A5"}, {"\uC3A6", "\uC3A6"}, {"\uC3A7", "\uC3A7"}, {"\uC3A8", "\uC3A8"}, {"\uC3A9", "\uC3A9"}, {"\uC3AA", "\uC3AA"}, {"\uC3AB", "\uC3AB"}, {"\uC3AC", "\uC3AC"}, {"\uC3AD", "\uC3AD"}, {"\uC3AE", "\uC3AE"}, {"\uC3AF", "\uC3AF"}, {"\uC3B0", "\uC3B0"}, {"\uC3B1", "\uC3B1"}, {"\uC3B2", "\uC3B2"}, {"\uC3B3", "\uC3B3"}, {"\uC3B4", "\uC3B4"}, {"\uC3B5", "\uC3B5"}, {"\uC3B6", "\uC3B6"}, {"\uC3B7", "\uC3B7"}, {"\uC3B8", "\uC3B8"}, {"\uC3B9", "\uC3B9"}, {"\uC3BA", "\uC3BA"}, {"\uC3BB", "\uC3BB"}, {"\uC3BC", "\uC3BC"}, {"\uC3BD", "\uC3BD"}, {"\uC3BE", "\uC3BE"}, {"\uC3BF", "\uC3BF"}, {"\uC3C0", "\uC3C0"}, {"\uC3C1", "\uC3C1"}, {"\uC3C2", "\uC3C2"}, {"\uC3C3", "\uC3C3"}, {"\uC3C4", "\uC3C4"}, {"\uC3C5", "\uC3C5"}, {"\uC3C6", "\uC3C6"}, {"\uC3C7", "\uC3C7"}, {"\uC3C8", "\uC3C8"}, {"\uC3C9", "\uC3C9"}, {"\uC3CA", "\uC3CA"}, {"\uC3CB", "\uC3CB"}, {"\uC3CC", "\uC3CC"}, {"\uC3CD", "\uC3CD"}, {"\uC3CE", "\uC3CE"}, {"\uC3CF", "\uC3CF"}, {"\uC3D0", "\uC3D0"}, {"\uC3D1", "\uC3D1"}, {"\uC3D2", "\uC3D2"}, {"\uC3D3", "\uC3D3"}, {"\uC3D4", "\uC3D4"}, {"\uC3D5", "\uC3D5"}, {"\uC3D6", "\uC3D6"}, {"\uC3D7", "\uC3D7"}, {"\uC3D8", "\uC3D8"}, {"\uC3D9", "\uC3D9"}, {"\uC3DA", "\uC3DA"}, {"\uC3DB", "\uC3DB"}, {"\uC3DC", "\uC3DC"}, {"\uC3DD", "\uC3DD"}, {"\uC3DE", "\uC3DE"}, {"\uC3DF", "\uC3DF"}, {"\uC3E0", "\uC3E0"}, {"\uC3E1", "\uC3E1"}, {"\uC3E2", "\uC3E2"}, {"\uC3E3", "\uC3E3"}, {"\uC3E4", "\uC3E4"}, {"\uC3E5", "\uC3E5"}, {"\uC3E6", "\uC3E6"}, {"\uC3E7", "\uC3E7"}, {"\uC3E8", "\uC3E8"}, {"\uC3E9", "\uC3E9"}, {"\uC3EA", "\uC3EA"}, {"\uC3EB", "\uC3EB"}, {"\uC3EC", "\uC3EC"}, {"\uC3ED", "\uC3ED"}, {"\uC3EE", "\uC3EE"}, {"\uC3EF", "\uC3EF"}, {"\uC3F0", "\uC3F0"}, {"\uC3F1", "\uC3F1"}, {"\uC3F2", "\uC3F2"}, {"\uC3F3", "\uC3F3"}, {"\uC3F4", "\uC3F4"}, {"\uC3F5", "\uC3F5"}, {"\uC3F6", "\uC3F6"}, {"\uC3F7", "\uC3F7"}, {"\uC3F8", "\uC3F8"}, {"\uC3F9", "\uC3F9"}, {"\uC3FA", "\uC3FA"}, {"\uC3FB", "\uC3FB"}, {"\uC3FC", "\uC3FC"}, {"\uC3FD", "\uC3FD"}, {"\uC3FE", "\uC3FE"}, {"\uC3FF", "\uC3FF"}, {"\uC400", "\uC400"}, {"\uC401", "\uC401"}, {"\uC402", "\uC402"}, {"\uC403", "\uC403"}, {"\uC404", "\uC404"}, {"\uC405", "\uC405"}, {"\uC406", "\uC406"}, {"\uC407", "\uC407"}, {"\uC408", "\uC408"}, {"\uC409", "\uC409"}, {"\uC40A", "\uC40A"}, {"\uC40B", "\uC40B"}, {"\uC40C", "\uC40C"}, {"\uC40D", "\uC40D"}, {"\uC40E", "\uC40E"}, {"\uC40F", "\uC40F"}, {"\uC410", "\uC410"}, {"\uC411", "\uC411"}, {"\uC412", "\uC412"}, {"\uC413", "\uC413"}, {"\uC414", "\uC414"}, {"\uC415", "\uC415"}, {"\uC416", "\uC416"}, {"\uC417", "\uC417"}, {"\uC418", "\uC418"}, {"\uC419", "\uC419"}, {"\uC41A", "\uC41A"}, {"\uC41B", "\uC41B"}, {"\uC41C", "\uC41C"}, {"\uC41D", "\uC41D"}, {"\uC41E", "\uC41E"}, {"\uC41F", "\uC41F"}, {"\uC420", "\uC420"}, {"\uC421", "\uC421"}, {"\uC422", "\uC422"}, {"\uC423", "\uC423"}, {"\uC424", "\uC424"}, {"\uC425", "\uC425"}, {"\uC426", "\uC426"}, {"\uC427", "\uC427"}, {"\uC428", "\uC428"}, {"\uC429", "\uC429"}, {"\uC42A", "\uC42A"}, {"\uC42B", "\uC42B"}, {"\uC42C", "\uC42C"}, {"\uC42D", "\uC42D"}, {"\uC42E", "\uC42E"}, {"\uC42F", "\uC42F"}, {"\uC430", "\uC430"}, {"\uC431", "\uC431"}, {"\uC432", "\uC432"}, {"\uC433", "\uC433"}, {"\uC434", "\uC434"}, {"\uC435", "\uC435"}, {"\uC436", "\uC436"}, {"\uC437", "\uC437"}, {"\uC438", "\uC438"}, {"\uC439", "\uC439"}, {"\uC43A", "\uC43A"}, {"\uC43B", "\uC43B"}, {"\uC43C", "\uC43C"}, {"\uC43D", "\uC43D"}, {"\uC43E", "\uC43E"}, {"\uC43F", "\uC43F"}, {"\uC440", "\uC440"}, {"\uC441", "\uC441"}, {"\uC442", "\uC442"}, {"\uC443", "\uC443"}, {"\uC444", "\uC444"}, {"\uC445", "\uC445"}, {"\uC446", "\uC446"}, {"\uC447", "\uC447"}, {"\uC448", "\uC448"}, {"\uC449", "\uC449"}, {"\uC44A", "\uC44A"}, {"\uC44B", "\uC44B"}, {"\uC44C", "\uC44C"}, {"\uC44D", "\uC44D"}, {"\uC44E", "\uC44E"}, {"\uC44F", "\uC44F"}, {"\uC450", "\uC450"}, {"\uC451", "\uC451"}, {"\uC452", "\uC452"}, {"\uC453", "\uC453"}, {"\uC454", "\uC454"}, {"\uC455", "\uC455"}, {"\uC456", "\uC456"}, {"\uC457", "\uC457"}, {"\uC458", "\uC458"}, {"\uC459", "\uC459"}, {"\uC45A", "\uC45A"}, {"\uC45B", "\uC45B"}, {"\uC45C", "\uC45C"}, {"\uC45D", "\uC45D"}, {"\uC45E", "\uC45E"}, {"\uC45F", "\uC45F"}, {"\uC460", "\uC460"}, {"\uC461", "\uC461"}, {"\uC462", "\uC462"}, {"\uC463", "\uC463"}, {"\uC464", "\uC464"}, {"\uC465", "\uC465"}, {"\uC466", "\uC466"}, {"\uC467", "\uC467"}, {"\uC468", "\uC468"}, {"\uC469", "\uC469"}, {"\uC46A", "\uC46A"}, {"\uC46B", "\uC46B"}, {"\uC46C", "\uC46C"}, {"\uC46D", "\uC46D"}, {"\uC46E", "\uC46E"}, {"\uC46F", "\uC46F"}, {"\uC470", "\uC470"}, {"\uC471", "\uC471"}, {"\uC472", "\uC472"}, {"\uC473", "\uC473"}, {"\uC474", "\uC474"}, {"\uC475", "\uC475"}, {"\uC476", "\uC476"}, {"\uC477", "\uC477"}, {"\uC478", "\uC478"}, {"\uC479", "\uC479"}, {"\uC47A", "\uC47A"}, {"\uC47B", "\uC47B"}, {"\uC47C", "\uC47C"}, {"\uC47D", "\uC47D"}, {"\uC47E", "\uC47E"}, {"\uC47F", "\uC47F"}, {"\uC480", "\uC480"}, {"\uC481", "\uC481"}, {"\uC482", "\uC482"}, {"\uC483", "\uC483"}, {"\uC484", "\uC484"}, {"\uC485", "\uC485"}, {"\uC486", "\uC486"}, {"\uC487", "\uC487"}, {"\uC488", "\uC488"}, {"\uC489", "\uC489"}, {"\uC48A", "\uC48A"}, {"\uC48B", "\uC48B"}, {"\uC48C", "\uC48C"}, {"\uC48D", "\uC48D"}, {"\uC48E", "\uC48E"}, {"\uC48F", "\uC48F"}, {"\uC490", "\uC490"}, {"\uC491", "\uC491"}, {"\uC492", "\uC492"}, {"\uC493", "\uC493"}, {"\uC494", "\uC494"}, {"\uC495", "\uC495"}, {"\uC496", "\uC496"}, {"\uC497", "\uC497"}, {"\uC498", "\uC498"}, {"\uC499", "\uC499"}, {"\uC49A", "\uC49A"}, {"\uC49B", "\uC49B"}, {"\uC49C", "\uC49C"}, {"\uC49D", "\uC49D"}, {"\uC49E", "\uC49E"}, {"\uC49F", "\uC49F"}, {"\uC4A0", "\uC4A0"}, {"\uC4A1", "\uC4A1"}, {"\uC4A2", "\uC4A2"}, {"\uC4A3", "\uC4A3"}, {"\uC4A4", "\uC4A4"}, {"\uC4A5", "\uC4A5"}, {"\uC4A6", "\uC4A6"}, {"\uC4A7", "\uC4A7"}, {"\uC4A8", "\uC4A8"}, {"\uC4A9", "\uC4A9"}, {"\uC4AA", "\uC4AA"}, {"\uC4AB", "\uC4AB"}, {"\uC4AC", "\uC4AC"}, {"\uC4AD", "\uC4AD"}, {"\uC4AE", "\uC4AE"}, {"\uC4AF", "\uC4AF"}, {"\uC4B0", "\uC4B0"}, {"\uC4B1", "\uC4B1"}, {"\uC4B2", "\uC4B2"}, {"\uC4B3", "\uC4B3"}, {"\uC4B4", "\uC4B4"}, {"\uC4B5", "\uC4B5"}, {"\uC4B6", "\uC4B6"}, {"\uC4B7", "\uC4B7"}, {"\uC4B8", "\uC4B8"}, {"\uC4B9", "\uC4B9"}, {"\uC4BA", "\uC4BA"}, {"\uC4BB", "\uC4BB"}, {"\uC4BC", "\uC4BC"}, {"\uC4BD", "\uC4BD"}, {"\uC4BE", "\uC4BE"}, {"\uC4BF", "\uC4BF"}, {"\uC4C0", "\uC4C0"}, {"\uC4C1", "\uC4C1"}, {"\uC4C2", "\uC4C2"}, {"\uC4C3", "\uC4C3"}, {"\uC4C4", "\uC4C4"}, {"\uC4C5", "\uC4C5"}, {"\uC4C6", "\uC4C6"}, {"\uC4C7", "\uC4C7"}, {"\uC4C8", "\uC4C8"}, {"\uC4C9", "\uC4C9"}, {"\uC4CA", "\uC4CA"}, {"\uC4CB", "\uC4CB"}, {"\uC4CC", "\uC4CC"}, {"\uC4CD", "\uC4CD"}, {"\uC4CE", "\uC4CE"}, {"\uC4CF", "\uC4CF"}, {"\uC4D0", "\uC4D0"}, {"\uC4D1", "\uC4D1"}, {"\uC4D2", "\uC4D2"}, {"\uC4D3", "\uC4D3"}, {"\uC4D4", "\uC4D4"}, {"\uC4D5", "\uC4D5"}, {"\uC4D6", "\uC4D6"}, {"\uC4D7", "\uC4D7"}, {"\uC4D8", "\uC4D8"}, {"\uC4D9", "\uC4D9"}, {"\uC4DA", "\uC4DA"}, {"\uC4DB", "\uC4DB"}, {"\uC4DC", "\uC4DC"}, {"\uC4DD", "\uC4DD"}, {"\uC4DE", "\uC4DE"}, {"\uC4DF", "\uC4DF"}, {"\uC4E0", "\uC4E0"}, {"\uC4E1", "\uC4E1"}, {"\uC4E2", "\uC4E2"}, {"\uC4E3", "\uC4E3"}, {"\uC4E4", "\uC4E4"}, {"\uC4E5", "\uC4E5"}, {"\uC4E6", "\uC4E6"}, {"\uC4E7", "\uC4E7"}, {"\uC4E8", "\uC4E8"}, {"\uC4E9", "\uC4E9"}, {"\uC4EA", "\uC4EA"}, {"\uC4EB", "\uC4EB"}, {"\uC4EC", "\uC4EC"}, {"\uC4ED", "\uC4ED"}, {"\uC4EE", "\uC4EE"}, {"\uC4EF", "\uC4EF"}, {"\uC4F0", "\uC4F0"}, {"\uC4F1", "\uC4F1"}, {"\uC4F2", "\uC4F2"}, {"\uC4F3", "\uC4F3"}, {"\uC4F4", "\uC4F4"}, {"\uC4F5", "\uC4F5"}, {"\uC4F6", "\uC4F6"}, {"\uC4F7", "\uC4F7"}, {"\uC4F8", "\uC4F8"}, {"\uC4F9", "\uC4F9"}, {"\uC4FA", "\uC4FA"}, {"\uC4FB", "\uC4FB"}, {"\uC4FC", "\uC4FC"}, {"\uC4FD", "\uC4FD"}, {"\uC4FE", "\uC4FE"}, {"\uC4FF", "\uC4FF"}, {"\uC500", "\uC500"}, {"\uC501", "\uC501"}, {"\uC502", "\uC502"}, {"\uC503", "\uC503"}, {"\uC504", "\uC504"}, {"\uC505", "\uC505"}, {"\uC506", "\uC506"}, {"\uC507", "\uC507"}, {"\uC508", "\uC508"}, {"\uC509", "\uC509"}, {"\uC50A", "\uC50A"}, {"\uC50B", "\uC50B"}, {"\uC50C", "\uC50C"}, {"\uC50D", "\uC50D"}, {"\uC50E", "\uC50E"}, {"\uC50F", "\uC50F"}, {"\uC510", "\uC510"}, {"\uC511", "\uC511"}, {"\uC512", "\uC512"}, {"\uC513", "\uC513"}, {"\uC514", "\uC514"}, {"\uC515", "\uC515"}, {"\uC516", "\uC516"}, {"\uC517", "\uC517"}, {"\uC518", "\uC518"}, {"\uC519", "\uC519"}, {"\uC51A", "\uC51A"}, {"\uC51B", "\uC51B"}, {"\uC51C", "\uC51C"}, {"\uC51D", "\uC51D"}, {"\uC51E", "\uC51E"}, {"\uC51F", "\uC51F"}, {"\uC520", "\uC520"}, {"\uC521", "\uC521"}, {"\uC522", "\uC522"}, {"\uC523", "\uC523"}, {"\uC524", "\uC524"}, {"\uC525", "\uC525"}, {"\uC526", "\uC526"}, {"\uC527", "\uC527"}, {"\uC528", "\uC528"}, {"\uC529", "\uC529"}, {"\uC52A", "\uC52A"}, {"\uC52B", "\uC52B"}, {"\uC52C", "\uC52C"}, {"\uC52D", "\uC52D"}, {"\uC52E", "\uC52E"}, {"\uC52F", "\uC52F"}, {"\uC530", "\uC530"}, {"\uC531", "\uC531"}, {"\uC532", "\uC532"}, {"\uC533", "\uC533"}, {"\uC534", "\uC534"}, {"\uC535", "\uC535"}, {"\uC536", "\uC536"}, {"\uC537", "\uC537"}, {"\uC538", "\uC538"}, {"\uC539", "\uC539"}, {"\uC53A", "\uC53A"}, {"\uC53B", "\uC53B"}, {"\uC53C", "\uC53C"}, {"\uC53D", "\uC53D"}, {"\uC53E", "\uC53E"}, {"\uC53F", "\uC53F"}, {"\uC540", "\uC540"}, {"\uC541", "\uC541"}, {"\uC542", "\uC542"}, {"\uC543", "\uC543"}, {"\uC544", "\uC544"}, {"\uC545", "\uC545"}, {"\uC546", "\uC546"}, {"\uC547", "\uC547"}, {"\uC548", "\uC548"}, {"\uC549", "\uC549"}, {"\uC54A", "\uC54A"}, {"\uC54B", "\uC54B"}, {"\uC54C", "\uC54C"}, {"\uC54D", "\uC54D"}, {"\uC54E", "\uC54E"}, {"\uC54F", "\uC54F"}, {"\uC550", "\uC550"}, {"\uC551", "\uC551"}, {"\uC552", "\uC552"}, {"\uC553", "\uC553"}, {"\uC554", "\uC554"}, {"\uC555", "\uC555"}, {"\uC556", "\uC556"}, {"\uC557", "\uC557"}, {"\uC558", "\uC558"}, {"\uC559", "\uC559"}, {"\uC55A", "\uC55A"}, {"\uC55B", "\uC55B"}, {"\uC55C", "\uC55C"}, {"\uC55D", "\uC55D"}, {"\uC55E", "\uC55E"}, {"\uC55F", "\uC55F"}, {"\uC560", "\uC560"}, {"\uC561", "\uC561"}, {"\uC562", "\uC562"}, {"\uC563", "\uC563"}, {"\uC564", "\uC564"}, {"\uC565", "\uC565"}, {"\uC566", "\uC566"}, {"\uC567", "\uC567"}, {"\uC568", "\uC568"}, {"\uC569", "\uC569"}, {"\uC56A", "\uC56A"}, {"\uC56B", "\uC56B"}, {"\uC56C", "\uC56C"}, {"\uC56D", "\uC56D"}, {"\uC56E", "\uC56E"}, {"\uC56F", "\uC56F"}, {"\uC570", "\uC570"}, {"\uC571", "\uC571"}, {"\uC572", "\uC572"}, {"\uC573", "\uC573"}, {"\uC574", "\uC574"}, {"\uC575", "\uC575"}, {"\uC576", "\uC576"}, {"\uC577", "\uC577"}, {"\uC578", "\uC578"}, {"\uC579", "\uC579"}, {"\uC57A", "\uC57A"}, {"\uC57B", "\uC57B"}, {"\uC57C", "\uC57C"}, {"\uC57D", "\uC57D"}, {"\uC57E", "\uC57E"}, {"\uC57F", "\uC57F"}, {"\uC580", "\uC580"}, {"\uC581", "\uC581"}, {"\uC582", "\uC582"}, {"\uC583", "\uC583"}, {"\uC584", "\uC584"}, {"\uC585", "\uC585"}, {"\uC586", "\uC586"}, {"\uC587", "\uC587"}, {"\uC588", "\uC588"}, {"\uC589", "\uC589"}, {"\uC58A", "\uC58A"}, {"\uC58B", "\uC58B"}, {"\uC58C", "\uC58C"}, {"\uC58D", "\uC58D"}, {"\uC58E", "\uC58E"}, {"\uC58F", "\uC58F"}, {"\uC590", "\uC590"}, {"\uC591", "\uC591"}, {"\uC592", "\uC592"}, {"\uC593", "\uC593"}, {"\uC594", "\uC594"}, {"\uC595", "\uC595"}, {"\uC596", "\uC596"}, {"\uC597", "\uC597"}, {"\uC598", "\uC598"}, {"\uC599", "\uC599"}, {"\uC59A", "\uC59A"}, {"\uC59B", "\uC59B"}, {"\uC59C", "\uC59C"}, {"\uC59D", "\uC59D"}, {"\uC59E", "\uC59E"}, {"\uC59F", "\uC59F"}, {"\uC5A0", "\uC5A0"}, {"\uC5A1", "\uC5A1"}, {"\uC5A2", "\uC5A2"}, {"\uC5A3", "\uC5A3"}, {"\uC5A4", "\uC5A4"}, {"\uC5A5", "\uC5A5"}, {"\uC5A6", "\uC5A6"}, {"\uC5A7", "\uC5A7"}, {"\uC5A8", "\uC5A8"}, {"\uC5A9", "\uC5A9"}, {"\uC5AA", "\uC5AA"}, {"\uC5AB", "\uC5AB"}, {"\uC5AC", "\uC5AC"}, {"\uC5AD", "\uC5AD"}, {"\uC5AE", "\uC5AE"}, {"\uC5AF", "\uC5AF"}, {"\uC5B0", "\uC5B0"}, {"\uC5B1", "\uC5B1"}, {"\uC5B2", "\uC5B2"}, {"\uC5B3", "\uC5B3"}, {"\uC5B4", "\uC5B4"}, {"\uC5B5", "\uC5B5"}, {"\uC5B6", "\uC5B6"}, {"\uC5B7", "\uC5B7"}, {"\uC5B8", "\uC5B8"}, {"\uC5B9", "\uC5B9"}, {"\uC5BA", "\uC5BA"}, {"\uC5BB", "\uC5BB"}, {"\uC5BC", "\uC5BC"}, {"\uC5BD", "\uC5BD"}, {"\uC5BE", "\uC5BE"}, {"\uC5BF", "\uC5BF"}, {"\uC5C0", "\uC5C0"}, {"\uC5C1", "\uC5C1"}, {"\uC5C2", "\uC5C2"}, {"\uC5C3", "\uC5C3"}, {"\uC5C4", "\uC5C4"}, {"\uC5C5", "\uC5C5"}, {"\uC5C6", "\uC5C6"}, {"\uC5C7", "\uC5C7"}, {"\uC5C8", "\uC5C8"}, {"\uC5C9", "\uC5C9"}, {"\uC5CA", "\uC5CA"}, {"\uC5CB", "\uC5CB"}, {"\uC5CC", "\uC5CC"}, {"\uC5CD", "\uC5CD"}, {"\uC5CE", "\uC5CE"}, {"\uC5CF", "\uC5CF"}, {"\uC5D0", "\uC5D0"}, {"\uC5D1", "\uC5D1"}, {"\uC5D2", "\uC5D2"}, {"\uC5D3", "\uC5D3"}, {"\uC5D4", "\uC5D4"}, {"\uC5D5", "\uC5D5"}, {"\uC5D6", "\uC5D6"}, {"\uC5D7", "\uC5D7"}, {"\uC5D8", "\uC5D8"}, {"\uC5D9", "\uC5D9"}, {"\uC5DA", "\uC5DA"}, {"\uC5DB", "\uC5DB"}, {"\uC5DC", "\uC5DC"}, {"\uC5DD", "\uC5DD"}, {"\uC5DE", "\uC5DE"}, {"\uC5DF", "\uC5DF"}, {"\uC5E0", "\uC5E0"}, {"\uC5E1", "\uC5E1"}, {"\uC5E2", "\uC5E2"}, {"\uC5E3", "\uC5E3"}, {"\uC5E4", "\uC5E4"}, {"\uC5E5", "\uC5E5"}, {"\uC5E6", "\uC5E6"}, {"\uC5E7", "\uC5E7"}, {"\uC5E8", "\uC5E8"}, {"\uC5E9", "\uC5E9"}, {"\uC5EA", "\uC5EA"}, {"\uC5EB", "\uC5EB"}, {"\uC5EC", "\uC5EC"}, {"\uC5ED", "\uC5ED"}, {"\uC5EE", "\uC5EE"}, {"\uC5EF", "\uC5EF"}, {"\uC5F0", "\uC5F0"}, {"\uC5F1", "\uC5F1"}, {"\uC5F2", "\uC5F2"}, {"\uC5F3", "\uC5F3"}, {"\uC5F4", "\uC5F4"}, {"\uC5F5", "\uC5F5"}, {"\uC5F6", "\uC5F6"}, {"\uC5F7", "\uC5F7"}, {"\uC5F8", "\uC5F8"}, {"\uC5F9", "\uC5F9"}, {"\uC5FA", "\uC5FA"}, {"\uC5FB", "\uC5FB"}, {"\uC5FC", "\uC5FC"}, {"\uC5FD", "\uC5FD"}, {"\uC5FE", "\uC5FE"}, {"\uC5FF", "\uC5FF"}, {"\uC600", "\uC600"}, {"\uC601", "\uC601"}, {"\uC602", "\uC602"}, {"\uC603", "\uC603"}, {"\uC604", "\uC604"}, {"\uC605", "\uC605"}, {"\uC606", "\uC606"}, {"\uC607", "\uC607"}, {"\uC608", "\uC608"}, {"\uC609", "\uC609"}, {"\uC60A", "\uC60A"}, {"\uC60B", "\uC60B"}, {"\uC60C", "\uC60C"}, {"\uC60D", "\uC60D"}, {"\uC60E", "\uC60E"}, {"\uC60F", "\uC60F"}, {"\uC610", "\uC610"}, {"\uC611", "\uC611"}, {"\uC612", "\uC612"}, {"\uC613", "\uC613"}, {"\uC614", "\uC614"}, {"\uC615", "\uC615"}, {"\uC616", "\uC616"}, {"\uC617", "\uC617"}, {"\uC618", "\uC618"}, {"\uC619", "\uC619"}, {"\uC61A", "\uC61A"}, {"\uC61B", "\uC61B"}, {"\uC61C", "\uC61C"}, {"\uC61D", "\uC61D"}, {"\uC61E", "\uC61E"}, {"\uC61F", "\uC61F"}, {"\uC620", "\uC620"}, {"\uC621", "\uC621"}, {"\uC622", "\uC622"}, {"\uC623", "\uC623"}, {"\uC624", "\uC624"}, {"\uC625", "\uC625"}, {"\uC626", "\uC626"}, {"\uC627", "\uC627"}, {"\uC628", "\uC628"}, {"\uC629", "\uC629"}, {"\uC62A", "\uC62A"}, {"\uC62B", "\uC62B"}, {"\uC62C", "\uC62C"}, {"\uC62D", "\uC62D"}, {"\uC62E", "\uC62E"}, {"\uC62F", "\uC62F"}, {"\uC630", "\uC630"}, {"\uC631", "\uC631"}, {"\uC632", "\uC632"}, {"\uC633", "\uC633"}, {"\uC634", "\uC634"}, {"\uC635", "\uC635"}, {"\uC636", "\uC636"}, {"\uC637", "\uC637"}, {"\uC638", "\uC638"}, {"\uC639", "\uC639"}, {"\uC63A", "\uC63A"}, {"\uC63B", "\uC63B"}, {"\uC63C", "\uC63C"}, {"\uC63D", "\uC63D"}, {"\uC63E", "\uC63E"}, {"\uC63F", "\uC63F"}, {"\uC640", "\uC640"}, {"\uC641", "\uC641"}, {"\uC642", "\uC642"}, {"\uC643", "\uC643"}, {"\uC644", "\uC644"}, {"\uC645", "\uC645"}, {"\uC646", "\uC646"}, {"\uC647", "\uC647"}, {"\uC648", "\uC648"}, {"\uC649", "\uC649"}, {"\uC64A", "\uC64A"}, {"\uC64B", "\uC64B"}, {"\uC64C", "\uC64C"}, {"\uC64D", "\uC64D"}, {"\uC64E", "\uC64E"}, {"\uC64F", "\uC64F"}, {"\uC650", "\uC650"}, {"\uC651", "\uC651"}, {"\uC652", "\uC652"}, {"\uC653", "\uC653"}, {"\uC654", "\uC654"}, {"\uC655", "\uC655"}, {"\uC656", "\uC656"}, {"\uC657", "\uC657"}, {"\uC658", "\uC658"}, {"\uC659", "\uC659"}, {"\uC65A", "\uC65A"}, {"\uC65B", "\uC65B"}, {"\uC65C", "\uC65C"}, {"\uC65D", "\uC65D"}, {"\uC65E", "\uC65E"}, {"\uC65F", "\uC65F"}, {"\uC660", "\uC660"}, {"\uC661", "\uC661"}, {"\uC662", "\uC662"}, {"\uC663", "\uC663"}, {"\uC664", "\uC664"}, {"\uC665", "\uC665"}, {"\uC666", "\uC666"}, {"\uC667", "\uC667"}, {"\uC668", "\uC668"}, {"\uC669", "\uC669"}, {"\uC66A", "\uC66A"}, {"\uC66B", "\uC66B"}, {"\uC66C", "\uC66C"}, {"\uC66D", "\uC66D"}, {"\uC66E", "\uC66E"}, {"\uC66F", "\uC66F"}, {"\uC670", "\uC670"}, {"\uC671", "\uC671"}, {"\uC672", "\uC672"}, {"\uC673", "\uC673"}, {"\uC674", "\uC674"}, {"\uC675", "\uC675"}, {"\uC676", "\uC676"}, {"\uC677", "\uC677"}, {"\uC678", "\uC678"}, {"\uC679", "\uC679"}, {"\uC67A", "\uC67A"}, {"\uC67B", "\uC67B"}, {"\uC67C", "\uC67C"}, {"\uC67D", "\uC67D"}, {"\uC67E", "\uC67E"}, {"\uC67F", "\uC67F"}, {"\uC680", "\uC680"}, {"\uC681", "\uC681"}, {"\uC682", "\uC682"}, {"\uC683", "\uC683"}, {"\uC684", "\uC684"}, {"\uC685", "\uC685"}, {"\uC686", "\uC686"}, {"\uC687", "\uC687"}, {"\uC688", "\uC688"}, {"\uC689", "\uC689"}, {"\uC68A", "\uC68A"}, {"\uC68B", "\uC68B"}, {"\uC68C", "\uC68C"}, {"\uC68D", "\uC68D"}, {"\uC68E", "\uC68E"}, {"\uC68F", "\uC68F"}, {"\uC690", "\uC690"}, {"\uC691", "\uC691"}, {"\uC692", "\uC692"}, {"\uC693", "\uC693"}, {"\uC694", "\uC694"}, {"\uC695", "\uC695"}, {"\uC696", "\uC696"}, {"\uC697", "\uC697"}, {"\uC698", "\uC698"}, {"\uC699", "\uC699"}, {"\uC69A", "\uC69A"}, {"\uC69B", "\uC69B"}, {"\uC69C", "\uC69C"}, {"\uC69D", "\uC69D"}, {"\uC69E", "\uC69E"}, {"\uC69F", "\uC69F"}, {"\uC6A0", "\uC6A0"}, {"\uC6A1", "\uC6A1"}, {"\uC6A2", "\uC6A2"}, {"\uC6A3", "\uC6A3"}, {"\uC6A4", "\uC6A4"}, {"\uC6A5", "\uC6A5"}, {"\uC6A6", "\uC6A6"}, {"\uC6A7", "\uC6A7"}, {"\uC6A8", "\uC6A8"}, {"\uC6A9", "\uC6A9"}, {"\uC6AA", "\uC6AA"}, {"\uC6AB", "\uC6AB"}, {"\uC6AC", "\uC6AC"}, {"\uC6AD", "\uC6AD"}, {"\uC6AE", "\uC6AE"}, {"\uC6AF", "\uC6AF"}, {"\uC6B0", "\uC6B0"}, {"\uC6B1", "\uC6B1"}, {"\uC6B2", "\uC6B2"}, {"\uC6B3", "\uC6B3"}, {"\uC6B4", "\uC6B4"}, {"\uC6B5", "\uC6B5"}, {"\uC6B6", "\uC6B6"}, {"\uC6B7", "\uC6B7"}, {"\uC6B8", "\uC6B8"}, {"\uC6B9", "\uC6B9"}, {"\uC6BA", "\uC6BA"}, {"\uC6BB", "\uC6BB"}, {"\uC6BC", "\uC6BC"}, {"\uC6BD", "\uC6BD"}, {"\uC6BE", "\uC6BE"}, {"\uC6BF", "\uC6BF"}, {"\uC6C0", "\uC6C0"}, {"\uC6C1", "\uC6C1"}, {"\uC6C2", "\uC6C2"}, {"\uC6C3", "\uC6C3"}, {"\uC6C4", "\uC6C4"}, {"\uC6C5", "\uC6C5"}, {"\uC6C6", "\uC6C6"}, {"\uC6C7", "\uC6C7"}, {"\uC6C8", "\uC6C8"}, {"\uC6C9", "\uC6C9"}, {"\uC6CA", "\uC6CA"}, {"\uC6CB", "\uC6CB"}, {"\uC6CC", "\uC6CC"}, {"\uC6CD", "\uC6CD"}, {"\uC6CE", "\uC6CE"}, {"\uC6CF", "\uC6CF"}, {"\uC6D0", "\uC6D0"}, {"\uC6D1", "\uC6D1"}, {"\uC6D2", "\uC6D2"}, {"\uC6D3", "\uC6D3"}, {"\uC6D4", "\uC6D4"}, {"\uC6D5", "\uC6D5"}, {"\uC6D6", "\uC6D6"}, {"\uC6D7", "\uC6D7"}, {"\uC6D8", "\uC6D8"}, {"\uC6D9", "\uC6D9"}, {"\uC6DA", "\uC6DA"}, {"\uC6DB", "\uC6DB"}, {"\uC6DC", "\uC6DC"}, {"\uC6DD", "\uC6DD"}, {"\uC6DE", "\uC6DE"}, {"\uC6DF", "\uC6DF"}, {"\uC6E0", "\uC6E0"}, {"\uC6E1", "\uC6E1"}, {"\uC6E2", "\uC6E2"}, {"\uC6E3", "\uC6E3"}, {"\uC6E4", "\uC6E4"}, {"\uC6E5", "\uC6E5"}, {"\uC6E6", "\uC6E6"}, {"\uC6E7", "\uC6E7"}, {"\uC6E8", "\uC6E8"}, {"\uC6E9", "\uC6E9"}, {"\uC6EA", "\uC6EA"}, {"\uC6EB", "\uC6EB"}, {"\uC6EC", "\uC6EC"}, {"\uC6ED", "\uC6ED"}, {"\uC6EE", "\uC6EE"}, {"\uC6EF", "\uC6EF"}, {"\uC6F0", "\uC6F0"}, {"\uC6F1", "\uC6F1"}, {"\uC6F2", "\uC6F2"}, {"\uC6F3", "\uC6F3"}, {"\uC6F4", "\uC6F4"}, {"\uC6F5", "\uC6F5"}, {"\uC6F6", "\uC6F6"}, {"\uC6F7", "\uC6F7"}, {"\uC6F8", "\uC6F8"}, {"\uC6F9", "\uC6F9"}, {"\uC6FA", "\uC6FA"}, {"\uC6FB", "\uC6FB"}, {"\uC6FC", "\uC6FC"}, {"\uC6FD", "\uC6FD"}, {"\uC6FE", "\uC6FE"}, {"\uC6FF", "\uC6FF"}, {"\uC700", "\uC700"}, {"\uC701", "\uC701"}, {"\uC702", "\uC702"}, {"\uC703", "\uC703"}, {"\uC704", "\uC704"}, {"\uC705", "\uC705"}, {"\uC706", "\uC706"}, {"\uC707", "\uC707"}, {"\uC708", "\uC708"}, {"\uC709", "\uC709"}, {"\uC70A", "\uC70A"}, {"\uC70B", "\uC70B"}, {"\uC70C", "\uC70C"}, {"\uC70D", "\uC70D"}, {"\uC70E", "\uC70E"}, {"\uC70F", "\uC70F"}, {"\uC710", "\uC710"}, {"\uC711", "\uC711"}, {"\uC712", "\uC712"}, {"\uC713", "\uC713"}, {"\uC714", "\uC714"}, {"\uC715", "\uC715"}, {"\uC716", "\uC716"}, {"\uC717", "\uC717"}, {"\uC718", "\uC718"}, {"\uC719", "\uC719"}, {"\uC71A", "\uC71A"}, {"\uC71B", "\uC71B"}, {"\uC71C", "\uC71C"}, {"\uC71D", "\uC71D"}, {"\uC71E", "\uC71E"}, {"\uC71F", "\uC71F"}, {"\uC720", "\uC720"}, {"\uC721", "\uC721"}, {"\uC722", "\uC722"}, {"\uC723", "\uC723"}, {"\uC724", "\uC724"}, {"\uC725", "\uC725"}, {"\uC726", "\uC726"}, {"\uC727", "\uC727"}, {"\uC728", "\uC728"}, {"\uC729", "\uC729"}, {"\uC72A", "\uC72A"}, {"\uC72B", "\uC72B"}, {"\uC72C", "\uC72C"}, {"\uC72D", "\uC72D"}, {"\uC72E", "\uC72E"}, {"\uC72F", "\uC72F"}, {"\uC730", "\uC730"}, {"\uC731", "\uC731"}, {"\uC732", "\uC732"}, {"\uC733", "\uC733"}, {"\uC734", "\uC734"}, {"\uC735", "\uC735"}, {"\uC736", "\uC736"}, {"\uC737", "\uC737"}, {"\uC738", "\uC738"}, {"\uC739", "\uC739"}, {"\uC73A", "\uC73A"}, {"\uC73B", "\uC73B"}, {"\uC73C", "\uC73C"}, {"\uC73D", "\uC73D"}, {"\uC73E", "\uC73E"}, {"\uC73F", "\uC73F"}, {"\uC740", "\uC740"}, {"\uC741", "\uC741"}, {"\uC742", "\uC742"}, {"\uC743", "\uC743"}, {"\uC744", "\uC744"}, {"\uC745", "\uC745"}, {"\uC746", "\uC746"}, {"\uC747", "\uC747"}, {"\uC748", "\uC748"}, {"\uC749", "\uC749"}, {"\uC74A", "\uC74A"}, {"\uC74B", "\uC74B"}, {"\uC74C", "\uC74C"}, {"\uC74D", "\uC74D"}, {"\uC74E", "\uC74E"}, {"\uC74F", "\uC74F"}, {"\uC750", "\uC750"}, {"\uC751", "\uC751"}, {"\uC752", "\uC752"}, {"\uC753", "\uC753"}, {"\uC754", "\uC754"}, {"\uC755", "\uC755"}, {"\uC756", "\uC756"}, {"\uC757", "\uC757"}, {"\uC758", "\uC758"}, {"\uC759", "\uC759"}, {"\uC75A", "\uC75A"}, {"\uC75B", "\uC75B"}, {"\uC75C", "\uC75C"}, {"\uC75D", "\uC75D"}, {"\uC75E", "\uC75E"}, {"\uC75F", "\uC75F"}, {"\uC760", "\uC760"}, {"\uC761", "\uC761"}, {"\uC762", "\uC762"}, {"\uC763", "\uC763"}, {"\uC764", "\uC764"}, {"\uC765", "\uC765"}, {"\uC766", "\uC766"}, {"\uC767", "\uC767"}, {"\uC768", "\uC768"}, {"\uC769", "\uC769"}, {"\uC76A", "\uC76A"}, {"\uC76B", "\uC76B"}, {"\uC76C", "\uC76C"}, {"\uC76D", "\uC76D"}, {"\uC76E", "\uC76E"}, {"\uC76F", "\uC76F"}, {"\uC770", "\uC770"}, {"\uC771", "\uC771"}, {"\uC772", "\uC772"}, {"\uC773", "\uC773"}, {"\uC774", "\uC774"}, {"\uC775", "\uC775"}, {"\uC776", "\uC776"}, {"\uC777", "\uC777"}, {"\uC778", "\uC778"}, {"\uC779", "\uC779"}, {"\uC77A", "\uC77A"}, {"\uC77B", "\uC77B"}, {"\uC77C", "\uC77C"}, {"\uC77D", "\uC77D"}, {"\uC77E", "\uC77E"}, {"\uC77F", "\uC77F"}, {"\uC780", "\uC780"}, {"\uC781", "\uC781"}, {"\uC782", "\uC782"}, {"\uC783", "\uC783"}, {"\uC784", "\uC784"}, {"\uC785", "\uC785"}, {"\uC786", "\uC786"}, {"\uC787", "\uC787"}, {"\uC788", "\uC788"}, {"\uC789", "\uC789"}, {"\uC78A", "\uC78A"}, {"\uC78B", "\uC78B"}, {"\uC78C", "\uC78C"}, {"\uC78D", "\uC78D"}, {"\uC78E", "\uC78E"}, {"\uC78F", "\uC78F"}, {"\uC790", "\uC790"}, {"\uC791", "\uC791"}, {"\uC792", "\uC792"}, {"\uC793", "\uC793"}, {"\uC794", "\uC794"}, {"\uC795", "\uC795"}, {"\uC796", "\uC796"}, {"\uC797", "\uC797"}, {"\uC798", "\uC798"}, {"\uC799", "\uC799"}, {"\uC79A", "\uC79A"}, {"\uC79B", "\uC79B"}, {"\uC79C", "\uC79C"}, {"\uC79D", "\uC79D"}, {"\uC79E", "\uC79E"}, {"\uC79F", "\uC79F"}, {"\uC7A0", "\uC7A0"}, {"\uC7A1", "\uC7A1"}, {"\uC7A2", "\uC7A2"}, {"\uC7A3", "\uC7A3"}, {"\uC7A4", "\uC7A4"}, {"\uC7A5", "\uC7A5"}, {"\uC7A6", "\uC7A6"}, {"\uC7A7", "\uC7A7"}, {"\uC7A8", "\uC7A8"}, {"\uC7A9", "\uC7A9"}, {"\uC7AA", "\uC7AA"}, {"\uC7AB", "\uC7AB"}, {"\uC7AC", "\uC7AC"}, {"\uC7AD", "\uC7AD"}, {"\uC7AE", "\uC7AE"}, {"\uC7AF", "\uC7AF"}, {"\uC7B0", "\uC7B0"}, {"\uC7B1", "\uC7B1"}, {"\uC7B2", "\uC7B2"}, {"\uC7B3", "\uC7B3"}, {"\uC7B4", "\uC7B4"}, {"\uC7B5", "\uC7B5"}, {"\uC7B6", "\uC7B6"}, {"\uC7B7", "\uC7B7"}, {"\uC7B8", "\uC7B8"}, {"\uC7B9", "\uC7B9"}, {"\uC7BA", "\uC7BA"}, {"\uC7BB", "\uC7BB"}, {"\uC7BC", "\uC7BC"}, {"\uC7BD", "\uC7BD"}, {"\uC7BE", "\uC7BE"}, {"\uC7BF", "\uC7BF"}, {"\uC7C0", "\uC7C0"}, {"\uC7C1", "\uC7C1"}, {"\uC7C2", "\uC7C2"}, {"\uC7C3", "\uC7C3"}, {"\uC7C4", "\uC7C4"}, {"\uC7C5", "\uC7C5"}, {"\uC7C6", "\uC7C6"}, {"\uC7C7", "\uC7C7"}, {"\uC7C8", "\uC7C8"}, {"\uC7C9", "\uC7C9"}, {"\uC7CA", "\uC7CA"}, {"\uC7CB", "\uC7CB"}, {"\uC7CC", "\uC7CC"}, {"\uC7CD", "\uC7CD"}, {"\uC7CE", "\uC7CE"}, {"\uC7CF", "\uC7CF"}, {"\uC7D0", "\uC7D0"}, {"\uC7D1", "\uC7D1"}, {"\uC7D2", "\uC7D2"}, {"\uC7D3", "\uC7D3"}, {"\uC7D4", "\uC7D4"}, {"\uC7D5", "\uC7D5"}, {"\uC7D6", "\uC7D6"}, {"\uC7D7", "\uC7D7"}, {"\uC7D8", "\uC7D8"}, {"\uC7D9", "\uC7D9"}, {"\uC7DA", "\uC7DA"}, {"\uC7DB", "\uC7DB"}, {"\uC7DC", "\uC7DC"}, {"\uC7DD", "\uC7DD"}, {"\uC7DE", "\uC7DE"}, {"\uC7DF", "\uC7DF"}, {"\uC7E0", "\uC7E0"}, {"\uC7E1", "\uC7E1"}, {"\uC7E2", "\uC7E2"}, {"\uC7E3", "\uC7E3"}, {"\uC7E4", "\uC7E4"}, {"\uC7E5", "\uC7E5"}, {"\uC7E6", "\uC7E6"}, {"\uC7E7", "\uC7E7"}, {"\uC7E8", "\uC7E8"}, {"\uC7E9", "\uC7E9"}, {"\uC7EA", "\uC7EA"}, {"\uC7EB", "\uC7EB"}, {"\uC7EC", "\uC7EC"}, {"\uC7ED", "\uC7ED"}, {"\uC7EE", "\uC7EE"}, {"\uC7EF", "\uC7EF"}, {"\uC7F0", "\uC7F0"}, {"\uC7F1", "\uC7F1"}, {"\uC7F2", "\uC7F2"}, {"\uC7F3", "\uC7F3"}, {"\uC7F4", "\uC7F4"}, {"\uC7F5", "\uC7F5"}, {"\uC7F6", "\uC7F6"}, {"\uC7F7", "\uC7F7"}, {"\uC7F8", "\uC7F8"}, {"\uC7F9", "\uC7F9"}, {"\uC7FA", "\uC7FA"}, {"\uC7FB", "\uC7FB"}, {"\uC7FC", "\uC7FC"}, {"\uC7FD", "\uC7FD"}, {"\uC7FE", "\uC7FE"}, {"\uC7FF", "\uC7FF"}, {"\uC800", "\uC800"}, {"\uC801", "\uC801"}, {"\uC802", "\uC802"}, {"\uC803", "\uC803"}, {"\uC804", "\uC804"}, {"\uC805", "\uC805"}, {"\uC806", "\uC806"}, {"\uC807", "\uC807"}, {"\uC808", "\uC808"}, {"\uC809", "\uC809"}, {"\uC80A", "\uC80A"}, {"\uC80B", "\uC80B"}, {"\uC80C", "\uC80C"}, {"\uC80D", "\uC80D"}, {"\uC80E", "\uC80E"}, {"\uC80F", "\uC80F"}, {"\uC810", "\uC810"}, {"\uC811", "\uC811"}, {"\uC812", "\uC812"}, {"\uC813", "\uC813"}, {"\uC814", "\uC814"}, {"\uC815", "\uC815"}, {"\uC816", "\uC816"}, {"\uC817", "\uC817"}, {"\uC818", "\uC818"}, {"\uC819", "\uC819"}, {"\uC81A", "\uC81A"}, {"\uC81B", "\uC81B"}, {"\uC81C", "\uC81C"}, {"\uC81D", "\uC81D"}, {"\uC81E", "\uC81E"}, {"\uC81F", "\uC81F"}, {"\uC820", "\uC820"}, {"\uC821", "\uC821"}, {"\uC822", "\uC822"}, {"\uC823", "\uC823"}, {"\uC824", "\uC824"}, {"\uC825", "\uC825"}, {"\uC826", "\uC826"}, {"\uC827", "\uC827"}, {"\uC828", "\uC828"}, {"\uC829", "\uC829"}, {"\uC82A", "\uC82A"}, {"\uC82B", "\uC82B"}, {"\uC82C", "\uC82C"}, {"\uC82D", "\uC82D"}, {"\uC82E", "\uC82E"}, {"\uC82F", "\uC82F"}, {"\uC830", "\uC830"}, {"\uC831", "\uC831"}, {"\uC832", "\uC832"}, {"\uC833", "\uC833"}, {"\uC834", "\uC834"}, {"\uC835", "\uC835"}, {"\uC836", "\uC836"}, {"\uC837", "\uC837"}, {"\uC838", "\uC838"}, {"\uC839", "\uC839"}, {"\uC83A", "\uC83A"}, {"\uC83B", "\uC83B"}, {"\uC83C", "\uC83C"}, {"\uC83D", "\uC83D"}, {"\uC83E", "\uC83E"}, {"\uC83F", "\uC83F"}, {"\uC840", "\uC840"}, {"\uC841", "\uC841"}, {"\uC842", "\uC842"}, {"\uC843", "\uC843"}, {"\uC844", "\uC844"}, {"\uC845", "\uC845"}, {"\uC846", "\uC846"}, {"\uC847", "\uC847"}, {"\uC848", "\uC848"}, {"\uC849", "\uC849"}, {"\uC84A", "\uC84A"}, {"\uC84B", "\uC84B"}, {"\uC84C", "\uC84C"}, {"\uC84D", "\uC84D"}, {"\uC84E", "\uC84E"}, {"\uC84F", "\uC84F"}, {"\uC850", "\uC850"}, {"\uC851", "\uC851"}, {"\uC852", "\uC852"}, {"\uC853", "\uC853"}, {"\uC854", "\uC854"}, {"\uC855", "\uC855"}, {"\uC856", "\uC856"}, {"\uC857", "\uC857"}, {"\uC858", "\uC858"}, {"\uC859", "\uC859"}, {"\uC85A", "\uC85A"}, {"\uC85B", "\uC85B"}, {"\uC85C", "\uC85C"}, {"\uC85D", "\uC85D"}, {"\uC85E", "\uC85E"}, {"\uC85F", "\uC85F"}, {"\uC860", "\uC860"}, {"\uC861", "\uC861"}, {"\uC862", "\uC862"}, {"\uC863", "\uC863"}, {"\uC864", "\uC864"}, {"\uC865", "\uC865"}, {"\uC866", "\uC866"}, {"\uC867", "\uC867"}, {"\uC868", "\uC868"}, {"\uC869", "\uC869"}, {"\uC86A", "\uC86A"}, {"\uC86B", "\uC86B"}, {"\uC86C", "\uC86C"}, {"\uC86D", "\uC86D"}, {"\uC86E", "\uC86E"}, {"\uC86F", "\uC86F"}, {"\uC870", "\uC870"}, {"\uC871", "\uC871"}, {"\uC872", "\uC872"}, {"\uC873", "\uC873"}, {"\uC874", "\uC874"}, {"\uC875", "\uC875"}, {"\uC876", "\uC876"}, {"\uC877", "\uC877"}, {"\uC878", "\uC878"}, {"\uC879", "\uC879"}, {"\uC87A", "\uC87A"}, {"\uC87B", "\uC87B"}, {"\uC87C", "\uC87C"}, {"\uC87D", "\uC87D"}, {"\uC87E", "\uC87E"}, {"\uC87F", "\uC87F"}, {"\uC880", "\uC880"}, {"\uC881", "\uC881"}, {"\uC882", "\uC882"}, {"\uC883", "\uC883"}, {"\uC884", "\uC884"}, {"\uC885", "\uC885"}, {"\uC886", "\uC886"}, {"\uC887", "\uC887"}, {"\uC888", "\uC888"}, {"\uC889", "\uC889"}, {"\uC88A", "\uC88A"}, {"\uC88B", "\uC88B"}, {"\uC88C", "\uC88C"}, {"\uC88D", "\uC88D"}, {"\uC88E", "\uC88E"}, {"\uC88F", "\uC88F"}, {"\uC890", "\uC890"}, {"\uC891", "\uC891"}, {"\uC892", "\uC892"}, {"\uC893", "\uC893"}, {"\uC894", "\uC894"}, {"\uC895", "\uC895"}, {"\uC896", "\uC896"}, {"\uC897", "\uC897"}, {"\uC898", "\uC898"}, {"\uC899", "\uC899"}, {"\uC89A", "\uC89A"}, {"\uC89B", "\uC89B"}, {"\uC89C", "\uC89C"}, {"\uC89D", "\uC89D"}, {"\uC89E", "\uC89E"}, {"\uC89F", "\uC89F"}, {"\uC8A0", "\uC8A0"}, {"\uC8A1", "\uC8A1"}, {"\uC8A2", "\uC8A2"}, {"\uC8A3", "\uC8A3"}, {"\uC8A4", "\uC8A4"}, {"\uC8A5", "\uC8A5"}, {"\uC8A6", "\uC8A6"}, {"\uC8A7", "\uC8A7"}, {"\uC8A8", "\uC8A8"}, {"\uC8A9", "\uC8A9"}, {"\uC8AA", "\uC8AA"}, {"\uC8AB", "\uC8AB"}, {"\uC8AC", "\uC8AC"}, {"\uC8AD", "\uC8AD"}, {"\uC8AE", "\uC8AE"}, {"\uC8AF", "\uC8AF"}, {"\uC8B0", "\uC8B0"}, {"\uC8B1", "\uC8B1"}, {"\uC8B2", "\uC8B2"}, {"\uC8B3", "\uC8B3"}, {"\uC8B4", "\uC8B4"}, {"\uC8B5", "\uC8B5"}, {"\uC8B6", "\uC8B6"}, {"\uC8B7", "\uC8B7"}, {"\uC8B8", "\uC8B8"}, {"\uC8B9", "\uC8B9"}, {"\uC8BA", "\uC8BA"}, {"\uC8BB", "\uC8BB"}, {"\uC8BC", "\uC8BC"}, {"\uC8BD", "\uC8BD"}, {"\uC8BE", "\uC8BE"}, {"\uC8BF", "\uC8BF"}, {"\uC8C0", "\uC8C0"}, {"\uC8C1", "\uC8C1"}, {"\uC8C2", "\uC8C2"}, {"\uC8C3", "\uC8C3"}, {"\uC8C4", "\uC8C4"}, {"\uC8C5", "\uC8C5"}, {"\uC8C6", "\uC8C6"}, {"\uC8C7", "\uC8C7"}, {"\uC8C8", "\uC8C8"}, {"\uC8C9", "\uC8C9"}, {"\uC8CA", "\uC8CA"}, {"\uC8CB", "\uC8CB"}, {"\uC8CC", "\uC8CC"}, {"\uC8CD", "\uC8CD"}, {"\uC8CE", "\uC8CE"}, {"\uC8CF", "\uC8CF"}, {"\uC8D0", "\uC8D0"}, {"\uC8D1", "\uC8D1"}, {"\uC8D2", "\uC8D2"}, {"\uC8D3", "\uC8D3"}, {"\uC8D4", "\uC8D4"}, {"\uC8D5", "\uC8D5"}, {"\uC8D6", "\uC8D6"}, {"\uC8D7", "\uC8D7"}, {"\uC8D8", "\uC8D8"}, {"\uC8D9", "\uC8D9"}, {"\uC8DA", "\uC8DA"}, {"\uC8DB", "\uC8DB"}, {"\uC8DC", "\uC8DC"}, {"\uC8DD", "\uC8DD"}, {"\uC8DE", "\uC8DE"}, {"\uC8DF", "\uC8DF"}, {"\uC8E0", "\uC8E0"}, {"\uC8E1", "\uC8E1"}, {"\uC8E2", "\uC8E2"}, {"\uC8E3", "\uC8E3"}, {"\uC8E4", "\uC8E4"}, {"\uC8E5", "\uC8E5"}, {"\uC8E6", "\uC8E6"}, {"\uC8E7", "\uC8E7"}, {"\uC8E8", "\uC8E8"}, {"\uC8E9", "\uC8E9"}, {"\uC8EA", "\uC8EA"}, {"\uC8EB", "\uC8EB"}, {"\uC8EC", "\uC8EC"}, {"\uC8ED", "\uC8ED"}, {"\uC8EE", "\uC8EE"}, {"\uC8EF", "\uC8EF"}, {"\uC8F0", "\uC8F0"}, {"\uC8F1", "\uC8F1"}, {"\uC8F2", "\uC8F2"}, {"\uC8F3", "\uC8F3"}, {"\uC8F4", "\uC8F4"}, {"\uC8F5", "\uC8F5"}, {"\uC8F6", "\uC8F6"}, {"\uC8F7", "\uC8F7"}, {"\uC8F8", "\uC8F8"}, {"\uC8F9", "\uC8F9"}, {"\uC8FA", "\uC8FA"}, {"\uC8FB", "\uC8FB"}, {"\uC8FC", "\uC8FC"}, {"\uC8FD", "\uC8FD"}, {"\uC8FE", "\uC8FE"}, {"\uC8FF", "\uC8FF"}, {"\uC900", "\uC900"}, {"\uC901", "\uC901"}, {"\uC902", "\uC902"}, {"\uC903", "\uC903"}, {"\uC904", "\uC904"}, {"\uC905", "\uC905"}, {"\uC906", "\uC906"}, {"\uC907", "\uC907"}, {"\uC908", "\uC908"}, {"\uC909", "\uC909"}, {"\uC90A", "\uC90A"}, {"\uC90B", "\uC90B"}, {"\uC90C", "\uC90C"}, {"\uC90D", "\uC90D"}, {"\uC90E", "\uC90E"}, {"\uC90F", "\uC90F"}, {"\uC910", "\uC910"}, {"\uC911", "\uC911"}, {"\uC912", "\uC912"}, {"\uC913", "\uC913"}, {"\uC914", "\uC914"}, {"\uC915", "\uC915"}, {"\uC916", "\uC916"}, {"\uC917", "\uC917"}, {"\uC918", "\uC918"}, {"\uC919", "\uC919"}, {"\uC91A", "\uC91A"}, {"\uC91B", "\uC91B"}, {"\uC91C", "\uC91C"}, {"\uC91D", "\uC91D"}, {"\uC91E", "\uC91E"}, {"\uC91F", "\uC91F"}, {"\uC920", "\uC920"}, {"\uC921", "\uC921"}, {"\uC922", "\uC922"}, {"\uC923", "\uC923"}, {"\uC924", "\uC924"}, {"\uC925", "\uC925"}, {"\uC926", "\uC926"}, {"\uC927", "\uC927"}, {"\uC928", "\uC928"}, {"\uC929", "\uC929"}, {"\uC92A", "\uC92A"}, {"\uC92B", "\uC92B"}, {"\uC92C", "\uC92C"}, {"\uC92D", "\uC92D"}, {"\uC92E", "\uC92E"}, {"\uC92F", "\uC92F"}, {"\uC930", "\uC930"}, {"\uC931", "\uC931"}, {"\uC932", "\uC932"}, {"\uC933", "\uC933"}, {"\uC934", "\uC934"}, {"\uC935", "\uC935"}, {"\uC936", "\uC936"}, {"\uC937", "\uC937"}, {"\uC938", "\uC938"}, {"\uC939", "\uC939"}, {"\uC93A", "\uC93A"}, {"\uC93B", "\uC93B"}, {"\uC93C", "\uC93C"}, {"\uC93D", "\uC93D"}, {"\uC93E", "\uC93E"}, {"\uC93F", "\uC93F"}, {"\uC940", "\uC940"}, {"\uC941", "\uC941"}, {"\uC942", "\uC942"}, {"\uC943", "\uC943"}, {"\uC944", "\uC944"}, {"\uC945", "\uC945"}, {"\uC946", "\uC946"}, {"\uC947", "\uC947"}, {"\uC948", "\uC948"}, {"\uC949", "\uC949"}, {"\uC94A", "\uC94A"}, {"\uC94B", "\uC94B"}, {"\uC94C", "\uC94C"}, {"\uC94D", "\uC94D"}, {"\uC94E", "\uC94E"}, {"\uC94F", "\uC94F"}, {"\uC950", "\uC950"}, {"\uC951", "\uC951"}, {"\uC952", "\uC952"}, {"\uC953", "\uC953"}, {"\uC954", "\uC954"}, {"\uC955", "\uC955"}, {"\uC956", "\uC956"}, {"\uC957", "\uC957"}, {"\uC958", "\uC958"}, {"\uC959", "\uC959"}, {"\uC95A", "\uC95A"}, {"\uC95B", "\uC95B"}, {"\uC95C", "\uC95C"}, {"\uC95D", "\uC95D"}, {"\uC95E", "\uC95E"}, {"\uC95F", "\uC95F"}, {"\uC960", "\uC960"}, {"\uC961", "\uC961"}, {"\uC962", "\uC962"}, {"\uC963", "\uC963"}, {"\uC964", "\uC964"}, {"\uC965", "\uC965"}, {"\uC966", "\uC966"}, {"\uC967", "\uC967"}, {"\uC968", "\uC968"}, {"\uC969", "\uC969"}, {"\uC96A", "\uC96A"}, {"\uC96B", "\uC96B"}, {"\uC96C", "\uC96C"}, {"\uC96D", "\uC96D"}, {"\uC96E", "\uC96E"}, {"\uC96F", "\uC96F"}, {"\uC970", "\uC970"}, {"\uC971", "\uC971"}, {"\uC972", "\uC972"}, {"\uC973", "\uC973"}, {"\uC974", "\uC974"}, {"\uC975", "\uC975"}, {"\uC976", "\uC976"}, {"\uC977", "\uC977"}, {"\uC978", "\uC978"}, {"\uC979", "\uC979"}, {"\uC97A", "\uC97A"}, {"\uC97B", "\uC97B"}, {"\uC97C", "\uC97C"}, {"\uC97D", "\uC97D"}, {"\uC97E", "\uC97E"}, {"\uC97F", "\uC97F"}, {"\uC980", "\uC980"}, {"\uC981", "\uC981"}, {"\uC982", "\uC982"}, {"\uC983", "\uC983"}, {"\uC984", "\uC984"}, {"\uC985", "\uC985"}, {"\uC986", "\uC986"}, {"\uC987", "\uC987"}, {"\uC988", "\uC988"}, {"\uC989", "\uC989"}, {"\uC98A", "\uC98A"}, {"\uC98B", "\uC98B"}, {"\uC98C", "\uC98C"}, {"\uC98D", "\uC98D"}, {"\uC98E", "\uC98E"}, {"\uC98F", "\uC98F"}, {"\uC990", "\uC990"}, {"\uC991", "\uC991"}, {"\uC992", "\uC992"}, {"\uC993", "\uC993"}, {"\uC994", "\uC994"}, {"\uC995", "\uC995"}, {"\uC996", "\uC996"}, {"\uC997", "\uC997"}, {"\uC998", "\uC998"}, {"\uC999", "\uC999"}, {"\uC99A", "\uC99A"}, {"\uC99B", "\uC99B"}, {"\uC99C", "\uC99C"}, {"\uC99D", "\uC99D"}, {"\uC99E", "\uC99E"}, {"\uC99F", "\uC99F"}, {"\uC9A0", "\uC9A0"}, {"\uC9A1", "\uC9A1"}, {"\uC9A2", "\uC9A2"}, {"\uC9A3", "\uC9A3"}, {"\uC9A4", "\uC9A4"}, {"\uC9A5", "\uC9A5"}, {"\uC9A6", "\uC9A6"}, {"\uC9A7", "\uC9A7"}, {"\uC9A8", "\uC9A8"}, {"\uC9A9", "\uC9A9"}, {"\uC9AA", "\uC9AA"}, {"\uC9AB", "\uC9AB"}, {"\uC9AC", "\uC9AC"}, {"\uC9AD", "\uC9AD"}, {"\uC9AE", "\uC9AE"}, {"\uC9AF", "\uC9AF"}, {"\uC9B0", "\uC9B0"}, {"\uC9B1", "\uC9B1"}, {"\uC9B2", "\uC9B2"}, {"\uC9B3", "\uC9B3"}, {"\uC9B4", "\uC9B4"}, {"\uC9B5", "\uC9B5"}, {"\uC9B6", "\uC9B6"}, {"\uC9B7", "\uC9B7"}, {"\uC9B8", "\uC9B8"}, {"\uC9B9", "\uC9B9"}, {"\uC9BA", "\uC9BA"}, {"\uC9BB", "\uC9BB"}, {"\uC9BC", "\uC9BC"}, {"\uC9BD", "\uC9BD"}, {"\uC9BE", "\uC9BE"}, {"\uC9BF", "\uC9BF"}, {"\uC9C0", "\uC9C0"}, {"\uC9C1", "\uC9C1"}, {"\uC9C2", "\uC9C2"}, {"\uC9C3", "\uC9C3"}, {"\uC9C4", "\uC9C4"}, {"\uC9C5", "\uC9C5"}, {"\uC9C6", "\uC9C6"}, {"\uC9C7", "\uC9C7"}, {"\uC9C8", "\uC9C8"}, {"\uC9C9", "\uC9C9"}, {"\uC9CA", "\uC9CA"}, {"\uC9CB", "\uC9CB"}, {"\uC9CC", "\uC9CC"}, {"\uC9CD", "\uC9CD"}, {"\uC9CE", "\uC9CE"}, {"\uC9CF", "\uC9CF"}, {"\uC9D0", "\uC9D0"}, {"\uC9D1", "\uC9D1"}, {"\uC9D2", "\uC9D2"}, {"\uC9D3", "\uC9D3"}, {"\uC9D4", "\uC9D4"}, {"\uC9D5", "\uC9D5"}, {"\uC9D6", "\uC9D6"}, {"\uC9D7", "\uC9D7"}, {"\uC9D8", "\uC9D8"}, {"\uC9D9", "\uC9D9"}, {"\uC9DA", "\uC9DA"}, {"\uC9DB", "\uC9DB"}, {"\uC9DC", "\uC9DC"}, {"\uC9DD", "\uC9DD"}, {"\uC9DE", "\uC9DE"}, {"\uC9DF", "\uC9DF"}, {"\uC9E0", "\uC9E0"}, {"\uC9E1", "\uC9E1"}, {"\uC9E2", "\uC9E2"}, {"\uC9E3", "\uC9E3"}, {"\uC9E4", "\uC9E4"}, {"\uC9E5", "\uC9E5"}, {"\uC9E6", "\uC9E6"}, {"\uC9E7", "\uC9E7"}, {"\uC9E8", "\uC9E8"}, {"\uC9E9", "\uC9E9"}, {"\uC9EA", "\uC9EA"}, {"\uC9EB", "\uC9EB"}, {"\uC9EC", "\uC9EC"}, {"\uC9ED", "\uC9ED"}, {"\uC9EE", "\uC9EE"}, {"\uC9EF", "\uC9EF"}, {"\uC9F0", "\uC9F0"}, {"\uC9F1", "\uC9F1"}, {"\uC9F2", "\uC9F2"}, {"\uC9F3", "\uC9F3"}, {"\uC9F4", "\uC9F4"}, {"\uC9F5", "\uC9F5"}, {"\uC9F6", "\uC9F6"}, {"\uC9F7", "\uC9F7"}, {"\uC9F8", "\uC9F8"}, {"\uC9F9", "\uC9F9"}, {"\uC9FA", "\uC9FA"}, {"\uC9FB", "\uC9FB"}, {"\uC9FC", "\uC9FC"}, {"\uC9FD", "\uC9FD"}, {"\uC9FE", "\uC9FE"}, {"\uC9FF", "\uC9FF"}, {"\uCA00", "\uCA00"}, {"\uCA01", "\uCA01"}, {"\uCA02", "\uCA02"}, {"\uCA03", "\uCA03"}, {"\uCA04", "\uCA04"}, {"\uCA05", "\uCA05"}, {"\uCA06", "\uCA06"}, {"\uCA07", "\uCA07"}, {"\uCA08", "\uCA08"}, {"\uCA09", "\uCA09"}, {"\uCA0A", "\uCA0A"}, {"\uCA0B", "\uCA0B"}, {"\uCA0C", "\uCA0C"}, {"\uCA0D", "\uCA0D"}, {"\uCA0E", "\uCA0E"}, {"\uCA0F", "\uCA0F"}, {"\uCA10", "\uCA10"}, {"\uCA11", "\uCA11"}, {"\uCA12", "\uCA12"}, {"\uCA13", "\uCA13"}, {"\uCA14", "\uCA14"}, {"\uCA15", "\uCA15"}, {"\uCA16", "\uCA16"}, {"\uCA17", "\uCA17"}, {"\uCA18", "\uCA18"}, {"\uCA19", "\uCA19"}, {"\uCA1A", "\uCA1A"}, {"\uCA1B", "\uCA1B"}, {"\uCA1C", "\uCA1C"}, {"\uCA1D", "\uCA1D"}, {"\uCA1E", "\uCA1E"}, {"\uCA1F", "\uCA1F"}, {"\uCA20", "\uCA20"}, {"\uCA21", "\uCA21"}, {"\uCA22", "\uCA22"}, {"\uCA23", "\uCA23"}, {"\uCA24", "\uCA24"}, {"\uCA25", "\uCA25"}, {"\uCA26", "\uCA26"}, {"\uCA27", "\uCA27"}, {"\uCA28", "\uCA28"}, {"\uCA29", "\uCA29"}, {"\uCA2A", "\uCA2A"}, {"\uCA2B", "\uCA2B"}, {"\uCA2C", "\uCA2C"}, {"\uCA2D", "\uCA2D"}, {"\uCA2E", "\uCA2E"}, {"\uCA2F", "\uCA2F"}, {"\uCA30", "\uCA30"}, {"\uCA31", "\uCA31"}, {"\uCA32", "\uCA32"}, {"\uCA33", "\uCA33"}, {"\uCA34", "\uCA34"}, {"\uCA35", "\uCA35"}, {"\uCA36", "\uCA36"}, {"\uCA37", "\uCA37"}, {"\uCA38", "\uCA38"}, {"\uCA39", "\uCA39"}, {"\uCA3A", "\uCA3A"}, {"\uCA3B", "\uCA3B"}, {"\uCA3C", "\uCA3C"}, {"\uCA3D", "\uCA3D"}, {"\uCA3E", "\uCA3E"}, {"\uCA3F", "\uCA3F"}, {"\uCA40", "\uCA40"}, {"\uCA41", "\uCA41"}, {"\uCA42", "\uCA42"}, {"\uCA43", "\uCA43"}, {"\uCA44", "\uCA44"}, {"\uCA45", "\uCA45"}, {"\uCA46", "\uCA46"}, {"\uCA47", "\uCA47"}, {"\uCA48", "\uCA48"}, {"\uCA49", "\uCA49"}, {"\uCA4A", "\uCA4A"}, {"\uCA4B", "\uCA4B"}, {"\uCA4C", "\uCA4C"}, {"\uCA4D", "\uCA4D"}, {"\uCA4E", "\uCA4E"}, {"\uCA4F", "\uCA4F"}, {"\uCA50", "\uCA50"}, {"\uCA51", "\uCA51"}, {"\uCA52", "\uCA52"}, {"\uCA53", "\uCA53"}, {"\uCA54", "\uCA54"}, {"\uCA55", "\uCA55"}, {"\uCA56", "\uCA56"}, {"\uCA57", "\uCA57"}, {"\uCA58", "\uCA58"}, {"\uCA59", "\uCA59"}, {"\uCA5A", "\uCA5A"}, {"\uCA5B", "\uCA5B"}, {"\uCA5C", "\uCA5C"}, {"\uCA5D", "\uCA5D"}, {"\uCA5E", "\uCA5E"}, {"\uCA5F", "\uCA5F"}, {"\uCA60", "\uCA60"}, {"\uCA61", "\uCA61"}, {"\uCA62", "\uCA62"}, {"\uCA63", "\uCA63"}, {"\uCA64", "\uCA64"}, {"\uCA65", "\uCA65"}, {"\uCA66", "\uCA66"}, {"\uCA67", "\uCA67"}, {"\uCA68", "\uCA68"}, {"\uCA69", "\uCA69"}, {"\uCA6A", "\uCA6A"}, {"\uCA6B", "\uCA6B"}, {"\uCA6C", "\uCA6C"}, {"\uCA6D", "\uCA6D"}, {"\uCA6E", "\uCA6E"}, {"\uCA6F", "\uCA6F"}, {"\uCA70", "\uCA70"}, {"\uCA71", "\uCA71"}, {"\uCA72", "\uCA72"}, {"\uCA73", "\uCA73"}, {"\uCA74", "\uCA74"}, {"\uCA75", "\uCA75"}, {"\uCA76", "\uCA76"}, {"\uCA77", "\uCA77"}, {"\uCA78", "\uCA78"}, {"\uCA79", "\uCA79"}, {"\uCA7A", "\uCA7A"}, {"\uCA7B", "\uCA7B"}, {"\uCA7C", "\uCA7C"}, {"\uCA7D", "\uCA7D"}, {"\uCA7E", "\uCA7E"}, {"\uCA7F", "\uCA7F"}, {"\uCA80", "\uCA80"}, {"\uCA81", "\uCA81"}, {"\uCA82", "\uCA82"}, {"\uCA83", "\uCA83"}, {"\uCA84", "\uCA84"}, {"\uCA85", "\uCA85"}, {"\uCA86", "\uCA86"}, {"\uCA87", "\uCA87"}, {"\uCA88", "\uCA88"}, {"\uCA89", "\uCA89"}, {"\uCA8A", "\uCA8A"}, {"\uCA8B", "\uCA8B"}, {"\uCA8C", "\uCA8C"}, {"\uCA8D", "\uCA8D"}, {"\uCA8E", "\uCA8E"}, {"\uCA8F", "\uCA8F"}, {"\uCA90", "\uCA90"}, {"\uCA91", "\uCA91"}, {"\uCA92", "\uCA92"}, {"\uCA93", "\uCA93"}, {"\uCA94", "\uCA94"}, {"\uCA95", "\uCA95"}, {"\uCA96", "\uCA96"}, {"\uCA97", "\uCA97"}, {"\uCA98", "\uCA98"}, {"\uCA99", "\uCA99"}, {"\uCA9A", "\uCA9A"}, {"\uCA9B", "\uCA9B"}, {"\uCA9C", "\uCA9C"}, {"\uCA9D", "\uCA9D"}, {"\uCA9E", "\uCA9E"}, {"\uCA9F", "\uCA9F"}, {"\uCAA0", "\uCAA0"}, {"\uCAA1", "\uCAA1"}, {"\uCAA2", "\uCAA2"}, {"\uCAA3", "\uCAA3"}, {"\uCAA4", "\uCAA4"}, {"\uCAA5", "\uCAA5"}, {"\uCAA6", "\uCAA6"}, {"\uCAA7", "\uCAA7"}, {"\uCAA8", "\uCAA8"}, {"\uCAA9", "\uCAA9"}, {"\uCAAA", "\uCAAA"}, {"\uCAAB", "\uCAAB"}, {"\uCAAC", "\uCAAC"}, {"\uCAAD", "\uCAAD"}, {"\uCAAE", "\uCAAE"}, {"\uCAAF", "\uCAAF"}, {"\uCAB0", "\uCAB0"}, {"\uCAB1", "\uCAB1"}, {"\uCAB2", "\uCAB2"}, {"\uCAB3", "\uCAB3"}, {"\uCAB4", "\uCAB4"}, {"\uCAB5", "\uCAB5"}, {"\uCAB6", "\uCAB6"}, {"\uCAB7", "\uCAB7"}, {"\uCAB8", "\uCAB8"}, {"\uCAB9", "\uCAB9"}, {"\uCABA", "\uCABA"}, {"\uCABB", "\uCABB"}, {"\uCABC", "\uCABC"}, {"\uCABD", "\uCABD"}, {"\uCABE", "\uCABE"}, {"\uCABF", "\uCABF"}, {"\uCAC0", "\uCAC0"}, {"\uCAC1", "\uCAC1"}, {"\uCAC2", "\uCAC2"}, {"\uCAC3", "\uCAC3"}, {"\uCAC4", "\uCAC4"}, {"\uCAC5", "\uCAC5"}, {"\uCAC6", "\uCAC6"}, {"\uCAC7", "\uCAC7"}, {"\uCAC8", "\uCAC8"}, {"\uCAC9", "\uCAC9"}, {"\uCACA", "\uCACA"}, {"\uCACB", "\uCACB"}, {"\uCACC", "\uCACC"}, {"\uCACD", "\uCACD"}, {"\uCACE", "\uCACE"}, {"\uCACF", "\uCACF"}, {"\uCAD0", "\uCAD0"}, {"\uCAD1", "\uCAD1"}, {"\uCAD2", "\uCAD2"}, {"\uCAD3", "\uCAD3"}, {"\uCAD4", "\uCAD4"}, {"\uCAD5", "\uCAD5"}, {"\uCAD6", "\uCAD6"}, {"\uCAD7", "\uCAD7"}, {"\uCAD8", "\uCAD8"}, {"\uCAD9", "\uCAD9"}, {"\uCADA", "\uCADA"}, {"\uCADB", "\uCADB"}, {"\uCADC", "\uCADC"}, {"\uCADD", "\uCADD"}, {"\uCADE", "\uCADE"}, {"\uCADF", "\uCADF"}, {"\uCAE0", "\uCAE0"}, {"\uCAE1", "\uCAE1"}, {"\uCAE2", "\uCAE2"}, {"\uCAE3", "\uCAE3"}, {"\uCAE4", "\uCAE4"}, {"\uCAE5", "\uCAE5"}, {"\uCAE6", "\uCAE6"}, {"\uCAE7", "\uCAE7"}, {"\uCAE8", "\uCAE8"}, {"\uCAE9", "\uCAE9"}, {"\uCAEA", "\uCAEA"}, {"\uCAEB", "\uCAEB"}, {"\uCAEC", "\uCAEC"}, {"\uCAED", "\uCAED"}, {"\uCAEE", "\uCAEE"}, {"\uCAEF", "\uCAEF"}, {"\uCAF0", "\uCAF0"}, {"\uCAF1", "\uCAF1"}, {"\uCAF2", "\uCAF2"}, {"\uCAF3", "\uCAF3"}, {"\uCAF4", "\uCAF4"}, {"\uCAF5", "\uCAF5"}, {"\uCAF6", "\uCAF6"}, {"\uCAF7", "\uCAF7"}, {"\uCAF8", "\uCAF8"}, {"\uCAF9", "\uCAF9"}, {"\uCAFA", "\uCAFA"}, {"\uCAFB", "\uCAFB"}, {"\uCAFC", "\uCAFC"}, {"\uCAFD", "\uCAFD"}, {"\uCAFE", "\uCAFE"}, {"\uCAFF", "\uCAFF"}, {"\uCB00", "\uCB00"}, {"\uCB01", "\uCB01"}, {"\uCB02", "\uCB02"}, {"\uCB03", "\uCB03"}, {"\uCB04", "\uCB04"}, {"\uCB05", "\uCB05"}, {"\uCB06", "\uCB06"}, {"\uCB07", "\uCB07"}, {"\uCB08", "\uCB08"}, {"\uCB09", "\uCB09"}, {"\uCB0A", "\uCB0A"}, {"\uCB0B", "\uCB0B"}, {"\uCB0C", "\uCB0C"}, {"\uCB0D", "\uCB0D"}, {"\uCB0E", "\uCB0E"}, {"\uCB0F", "\uCB0F"}, {"\uCB10", "\uCB10"}, {"\uCB11", "\uCB11"}, {"\uCB12", "\uCB12"}, {"\uCB13", "\uCB13"}, {"\uCB14", "\uCB14"}, {"\uCB15", "\uCB15"}, {"\uCB16", "\uCB16"}, {"\uCB17", "\uCB17"}, {"\uCB18", "\uCB18"}, {"\uCB19", "\uCB19"}, {"\uCB1A", "\uCB1A"}, {"\uCB1B", "\uCB1B"}, {"\uCB1C", "\uCB1C"}, {"\uCB1D", "\uCB1D"}, {"\uCB1E", "\uCB1E"}, {"\uCB1F", "\uCB1F"}, {"\uCB20", "\uCB20"}, {"\uCB21", "\uCB21"}, {"\uCB22", "\uCB22"}, {"\uCB23", "\uCB23"}, {"\uCB24", "\uCB24"}, {"\uCB25", "\uCB25"}, {"\uCB26", "\uCB26"}, {"\uCB27", "\uCB27"}, {"\uCB28", "\uCB28"}, {"\uCB29", "\uCB29"}, {"\uCB2A", "\uCB2A"}, {"\uCB2B", "\uCB2B"}, {"\uCB2C", "\uCB2C"}, {"\uCB2D", "\uCB2D"}, {"\uCB2E", "\uCB2E"}, {"\uCB2F", "\uCB2F"}, {"\uCB30", "\uCB30"}, {"\uCB31", "\uCB31"}, {"\uCB32", "\uCB32"}, {"\uCB33", "\uCB33"}, {"\uCB34", "\uCB34"}, {"\uCB35", "\uCB35"}, {"\uCB36", "\uCB36"}, {"\uCB37", "\uCB37"}, {"\uCB38", "\uCB38"}, {"\uCB39", "\uCB39"}, {"\uCB3A", "\uCB3A"}, {"\uCB3B", "\uCB3B"}, {"\uCB3C", "\uCB3C"}, {"\uCB3D", "\uCB3D"}, {"\uCB3E", "\uCB3E"}, {"\uCB3F", "\uCB3F"}, {"\uCB40", "\uCB40"}, {"\uCB41", "\uCB41"}, {"\uCB42", "\uCB42"}, {"\uCB43", "\uCB43"}, {"\uCB44", "\uCB44"}, {"\uCB45", "\uCB45"}, {"\uCB46", "\uCB46"}, {"\uCB47", "\uCB47"}, {"\uCB48", "\uCB48"}, {"\uCB49", "\uCB49"}, {"\uCB4A", "\uCB4A"}, {"\uCB4B", "\uCB4B"}, {"\uCB4C", "\uCB4C"}, {"\uCB4D", "\uCB4D"}, {"\uCB4E", "\uCB4E"}, {"\uCB4F", "\uCB4F"}, {"\uCB50", "\uCB50"}, {"\uCB51", "\uCB51"}, {"\uCB52", "\uCB52"}, {"\uCB53", "\uCB53"}, {"\uCB54", "\uCB54"}, {"\uCB55", "\uCB55"}, {"\uCB56", "\uCB56"}, {"\uCB57", "\uCB57"}, {"\uCB58", "\uCB58"}, {"\uCB59", "\uCB59"}, {"\uCB5A", "\uCB5A"}, {"\uCB5B", "\uCB5B"}, {"\uCB5C", "\uCB5C"}, {"\uCB5D", "\uCB5D"}, {"\uCB5E", "\uCB5E"}, {"\uCB5F", "\uCB5F"}, {"\uCB60", "\uCB60"}, {"\uCB61", "\uCB61"}, {"\uCB62", "\uCB62"}, {"\uCB63", "\uCB63"}, {"\uCB64", "\uCB64"}, {"\uCB65", "\uCB65"}, {"\uCB66", "\uCB66"}, {"\uCB67", "\uCB67"}, {"\uCB68", "\uCB68"}, {"\uCB69", "\uCB69"}, {"\uCB6A", "\uCB6A"}, {"\uCB6B", "\uCB6B"}, {"\uCB6C", "\uCB6C"}, {"\uCB6D", "\uCB6D"}, {"\uCB6E", "\uCB6E"}, {"\uCB6F", "\uCB6F"}, {"\uCB70", "\uCB70"}, {"\uCB71", "\uCB71"}, {"\uCB72", "\uCB72"}, {"\uCB73", "\uCB73"}, {"\uCB74", "\uCB74"}, {"\uCB75", "\uCB75"}, {"\uCB76", "\uCB76"}, {"\uCB77", "\uCB77"}, {"\uCB78", "\uCB78"}, {"\uCB79", "\uCB79"}, {"\uCB7A", "\uCB7A"}, {"\uCB7B", "\uCB7B"}, {"\uCB7C", "\uCB7C"}, {"\uCB7D", "\uCB7D"}, {"\uCB7E", "\uCB7E"}, {"\uCB7F", "\uCB7F"}, {"\uCB80", "\uCB80"}, {"\uCB81", "\uCB81"}, {"\uCB82", "\uCB82"}, {"\uCB83", "\uCB83"}, {"\uCB84", "\uCB84"}, {"\uCB85", "\uCB85"}, {"\uCB86", "\uCB86"}, {"\uCB87", "\uCB87"}, {"\uCB88", "\uCB88"}, {"\uCB89", "\uCB89"}, {"\uCB8A", "\uCB8A"}, {"\uCB8B", "\uCB8B"}, {"\uCB8C", "\uCB8C"}, {"\uCB8D", "\uCB8D"}, {"\uCB8E", "\uCB8E"}, {"\uCB8F", "\uCB8F"}, {"\uCB90", "\uCB90"}, {"\uCB91", "\uCB91"}, {"\uCB92", "\uCB92"}, {"\uCB93", "\uCB93"}, {"\uCB94", "\uCB94"}, {"\uCB95", "\uCB95"}, {"\uCB96", "\uCB96"}, {"\uCB97", "\uCB97"}, {"\uCB98", "\uCB98"}, {"\uCB99", "\uCB99"}, {"\uCB9A", "\uCB9A"}, {"\uCB9B", "\uCB9B"}, {"\uCB9C", "\uCB9C"}, {"\uCB9D", "\uCB9D"}, {"\uCB9E", "\uCB9E"}, {"\uCB9F", "\uCB9F"}, {"\uCBA0", "\uCBA0"}, {"\uCBA1", "\uCBA1"}, {"\uCBA2", "\uCBA2"}, {"\uCBA3", "\uCBA3"}, {"\uCBA4", "\uCBA4"}, {"\uCBA5", "\uCBA5"}, {"\uCBA6", "\uCBA6"}, {"\uCBA7", "\uCBA7"}, {"\uCBA8", "\uCBA8"}, {"\uCBA9", "\uCBA9"}, {"\uCBAA", "\uCBAA"}, {"\uCBAB", "\uCBAB"}, {"\uCBAC", "\uCBAC"}, {"\uCBAD", "\uCBAD"}, {"\uCBAE", "\uCBAE"}, {"\uCBAF", "\uCBAF"}, {"\uCBB0", "\uCBB0"}, {"\uCBB1", "\uCBB1"}, {"\uCBB2", "\uCBB2"}, {"\uCBB3", "\uCBB3"}, {"\uCBB4", "\uCBB4"}, {"\uCBB5", "\uCBB5"}, {"\uCBB6", "\uCBB6"}, {"\uCBB7", "\uCBB7"}, {"\uCBB8", "\uCBB8"}, {"\uCBB9", "\uCBB9"}, {"\uCBBA", "\uCBBA"}, {"\uCBBB", "\uCBBB"}, {"\uCBBC", "\uCBBC"}, {"\uCBBD", "\uCBBD"}, {"\uCBBE", "\uCBBE"}, {"\uCBBF", "\uCBBF"}, {"\uCBC0", "\uCBC0"}, {"\uCBC1", "\uCBC1"}, {"\uCBC2", "\uCBC2"}, {"\uCBC3", "\uCBC3"}, {"\uCBC4", "\uCBC4"}, {"\uCBC5", "\uCBC5"}, {"\uCBC6", "\uCBC6"}, {"\uCBC7", "\uCBC7"}, {"\uCBC8", "\uCBC8"}, {"\uCBC9", "\uCBC9"}, {"\uCBCA", "\uCBCA"}, {"\uCBCB", "\uCBCB"}, {"\uCBCC", "\uCBCC"}, {"\uCBCD", "\uCBCD"}, {"\uCBCE", "\uCBCE"}, {"\uCBCF", "\uCBCF"}, {"\uCBD0", "\uCBD0"}, {"\uCBD1", "\uCBD1"}, {"\uCBD2", "\uCBD2"}, {"\uCBD3", "\uCBD3"}, {"\uCBD4", "\uCBD4"}, {"\uCBD5", "\uCBD5"}, {"\uCBD6", "\uCBD6"}, {"\uCBD7", "\uCBD7"}, {"\uCBD8", "\uCBD8"}, {"\uCBD9", "\uCBD9"}, {"\uCBDA", "\uCBDA"}, {"\uCBDB", "\uCBDB"}, {"\uCBDC", "\uCBDC"}, {"\uCBDD", "\uCBDD"}, {"\uCBDE", "\uCBDE"}, {"\uCBDF", "\uCBDF"}, {"\uCBE0", "\uCBE0"}, {"\uCBE1", "\uCBE1"}, {"\uCBE2", "\uCBE2"}, {"\uCBE3", "\uCBE3"}, {"\uCBE4", "\uCBE4"}, {"\uCBE5", "\uCBE5"}, {"\uCBE6", "\uCBE6"}, {"\uCBE7", "\uCBE7"}, {"\uCBE8", "\uCBE8"}, {"\uCBE9", "\uCBE9"}, {"\uCBEA", "\uCBEA"}, {"\uCBEB", "\uCBEB"}, {"\uCBEC", "\uCBEC"}, {"\uCBED", "\uCBED"}, {"\uCBEE", "\uCBEE"}, {"\uCBEF", "\uCBEF"}, {"\uCBF0", "\uCBF0"}, {"\uCBF1", "\uCBF1"}, {"\uCBF2", "\uCBF2"}, {"\uCBF3", "\uCBF3"}, {"\uCBF4", "\uCBF4"}, {"\uCBF5", "\uCBF5"}, {"\uCBF6", "\uCBF6"}, {"\uCBF7", "\uCBF7"}, {"\uCBF8", "\uCBF8"}, {"\uCBF9", "\uCBF9"}, {"\uCBFA", "\uCBFA"}, {"\uCBFB", "\uCBFB"}, {"\uCBFC", "\uCBFC"}, {"\uCBFD", "\uCBFD"}, {"\uCBFE", "\uCBFE"}, {"\uCBFF", "\uCBFF"}, {"\uCC00", "\uCC00"}, {"\uCC01", "\uCC01"}, {"\uCC02", "\uCC02"}, {"\uCC03", "\uCC03"}, {"\uCC04", "\uCC04"}, {"\uCC05", "\uCC05"}, {"\uCC06", "\uCC06"}, {"\uCC07", "\uCC07"}, {"\uCC08", "\uCC08"}, {"\uCC09", "\uCC09"}, {"\uCC0A", "\uCC0A"}, {"\uCC0B", "\uCC0B"}, {"\uCC0C", "\uCC0C"}, {"\uCC0D", "\uCC0D"}, {"\uCC0E", "\uCC0E"}, {"\uCC0F", "\uCC0F"}, {"\uCC10", "\uCC10"}, {"\uCC11", "\uCC11"}, {"\uCC12", "\uCC12"}, {"\uCC13", "\uCC13"}, {"\uCC14", "\uCC14"}, {"\uCC15", "\uCC15"}, {"\uCC16", "\uCC16"}, {"\uCC17", "\uCC17"}, {"\uCC18", "\uCC18"}, {"\uCC19", "\uCC19"}, {"\uCC1A", "\uCC1A"}, {"\uCC1B", "\uCC1B"}, {"\uCC1C", "\uCC1C"}, {"\uCC1D", "\uCC1D"}, {"\uCC1E", "\uCC1E"}, {"\uCC1F", "\uCC1F"}, {"\uCC20", "\uCC20"}, {"\uCC21", "\uCC21"}, {"\uCC22", "\uCC22"}, {"\uCC23", "\uCC23"}, {"\uCC24", "\uCC24"}, {"\uCC25", "\uCC25"}, {"\uCC26", "\uCC26"}, {"\uCC27", "\uCC27"}, {"\uCC28", "\uCC28"}, {"\uCC29", "\uCC29"}, {"\uCC2A", "\uCC2A"}, {"\uCC2B", "\uCC2B"}, {"\uCC2C", "\uCC2C"}, {"\uCC2D", "\uCC2D"}, {"\uCC2E", "\uCC2E"}, {"\uCC2F", "\uCC2F"}, {"\uCC30", "\uCC30"}, {"\uCC31", "\uCC31"}, {"\uCC32", "\uCC32"}, {"\uCC33", "\uCC33"}, {"\uCC34", "\uCC34"}, {"\uCC35", "\uCC35"}, {"\uCC36", "\uCC36"}, {"\uCC37", "\uCC37"}, {"\uCC38", "\uCC38"}, {"\uCC39", "\uCC39"}, {"\uCC3A", "\uCC3A"}, {"\uCC3B", "\uCC3B"}, {"\uCC3C", "\uCC3C"}, {"\uCC3D", "\uCC3D"}, {"\uCC3E", "\uCC3E"}, {"\uCC3F", "\uCC3F"}, {"\uCC40", "\uCC40"}, {"\uCC41", "\uCC41"}, {"\uCC42", "\uCC42"}, {"\uCC43", "\uCC43"}, {"\uCC44", "\uCC44"}, {"\uCC45", "\uCC45"}, {"\uCC46", "\uCC46"}, {"\uCC47", "\uCC47"}, {"\uCC48", "\uCC48"}, {"\uCC49", "\uCC49"}, {"\uCC4A", "\uCC4A"}, {"\uCC4B", "\uCC4B"}, {"\uCC4C", "\uCC4C"}, {"\uCC4D", "\uCC4D"}, {"\uCC4E", "\uCC4E"}, {"\uCC4F", "\uCC4F"}, {"\uCC50", "\uCC50"}, {"\uCC51", "\uCC51"}, {"\uCC52", "\uCC52"}, {"\uCC53", "\uCC53"}, {"\uCC54", "\uCC54"}, {"\uCC55", "\uCC55"}, {"\uCC56", "\uCC56"}, {"\uCC57", "\uCC57"}, {"\uCC58", "\uCC58"}, {"\uCC59", "\uCC59"}, {"\uCC5A", "\uCC5A"}, {"\uCC5B", "\uCC5B"}, {"\uCC5C", "\uCC5C"}, {"\uCC5D", "\uCC5D"}, {"\uCC5E", "\uCC5E"}, {"\uCC5F", "\uCC5F"}, {"\uCC60", "\uCC60"}, {"\uCC61", "\uCC61"}, {"\uCC62", "\uCC62"}, {"\uCC63", "\uCC63"}, {"\uCC64", "\uCC64"}, {"\uCC65", "\uCC65"}, {"\uCC66", "\uCC66"}, {"\uCC67", "\uCC67"}, {"\uCC68", "\uCC68"}, {"\uCC69", "\uCC69"}, {"\uCC6A", "\uCC6A"}, {"\uCC6B", "\uCC6B"}, {"\uCC6C", "\uCC6C"}, {"\uCC6D", "\uCC6D"}, {"\uCC6E", "\uCC6E"}, {"\uCC6F", "\uCC6F"}, {"\uCC70", "\uCC70"}, {"\uCC71", "\uCC71"}, {"\uCC72", "\uCC72"}, {"\uCC73", "\uCC73"}, {"\uCC74", "\uCC74"}, {"\uCC75", "\uCC75"}, {"\uCC76", "\uCC76"}, {"\uCC77", "\uCC77"}, {"\uCC78", "\uCC78"}, {"\uCC79", "\uCC79"}, {"\uCC7A", "\uCC7A"}, {"\uCC7B", "\uCC7B"}, {"\uCC7C", "\uCC7C"}, {"\uCC7D", "\uCC7D"}, {"\uCC7E", "\uCC7E"}, {"\uCC7F", "\uCC7F"}, {"\uCC80", "\uCC80"}, {"\uCC81", "\uCC81"}, {"\uCC82", "\uCC82"}, {"\uCC83", "\uCC83"}, {"\uCC84", "\uCC84"}, {"\uCC85", "\uCC85"}, {"\uCC86", "\uCC86"}, {"\uCC87", "\uCC87"}, {"\uCC88", "\uCC88"}, {"\uCC89", "\uCC89"}, {"\uCC8A", "\uCC8A"}, {"\uCC8B", "\uCC8B"}, {"\uCC8C", "\uCC8C"}, {"\uCC8D", "\uCC8D"}, {"\uCC8E", "\uCC8E"}, {"\uCC8F", "\uCC8F"}, {"\uCC90", "\uCC90"}, {"\uCC91", "\uCC91"}, {"\uCC92", "\uCC92"}, {"\uCC93", "\uCC93"}, {"\uCC94", "\uCC94"}, {"\uCC95", "\uCC95"}, {"\uCC96", "\uCC96"}, {"\uCC97", "\uCC97"}, {"\uCC98", "\uCC98"}, {"\uCC99", "\uCC99"}, {"\uCC9A", "\uCC9A"}, {"\uCC9B", "\uCC9B"}, {"\uCC9C", "\uCC9C"}, {"\uCC9D", "\uCC9D"}, {"\uCC9E", "\uCC9E"}, {"\uCC9F", "\uCC9F"}, {"\uCCA0", "\uCCA0"}, {"\uCCA1", "\uCCA1"}, {"\uCCA2", "\uCCA2"}, {"\uCCA3", "\uCCA3"}, {"\uCCA4", "\uCCA4"}, {"\uCCA5", "\uCCA5"}, {"\uCCA6", "\uCCA6"}, {"\uCCA7", "\uCCA7"}, {"\uCCA8", "\uCCA8"}, {"\uCCA9", "\uCCA9"}, {"\uCCAA", "\uCCAA"}, {"\uCCAB", "\uCCAB"}, {"\uCCAC", "\uCCAC"}, {"\uCCAD", "\uCCAD"}, {"\uCCAE", "\uCCAE"}, {"\uCCAF", "\uCCAF"}, {"\uCCB0", "\uCCB0"}, {"\uCCB1", "\uCCB1"}, {"\uCCB2", "\uCCB2"}, {"\uCCB3", "\uCCB3"}, {"\uCCB4", "\uCCB4"}, {"\uCCB5", "\uCCB5"}, {"\uCCB6", "\uCCB6"}, {"\uCCB7", "\uCCB7"}, {"\uCCB8", "\uCCB8"}, {"\uCCB9", "\uCCB9"}, {"\uCCBA", "\uCCBA"}, {"\uCCBB", "\uCCBB"}, {"\uCCBC", "\uCCBC"}, {"\uCCBD", "\uCCBD"}, {"\uCCBE", "\uCCBE"}, {"\uCCBF", "\uCCBF"}, {"\uCCC0", "\uCCC0"}, {"\uCCC1", "\uCCC1"}, {"\uCCC2", "\uCCC2"}, {"\uCCC3", "\uCCC3"}, {"\uCCC4", "\uCCC4"}, {"\uCCC5", "\uCCC5"}, {"\uCCC6", "\uCCC6"}, {"\uCCC7", "\uCCC7"}, {"\uCCC8", "\uCCC8"}, {"\uCCC9", "\uCCC9"}, {"\uCCCA", "\uCCCA"}, {"\uCCCB", "\uCCCB"}, {"\uCCCC", "\uCCCC"}, {"\uCCCD", "\uCCCD"}, {"\uCCCE", "\uCCCE"}, {"\uCCCF", "\uCCCF"}, {"\uCCD0", "\uCCD0"}, {"\uCCD1", "\uCCD1"}, {"\uCCD2", "\uCCD2"}, {"\uCCD3", "\uCCD3"}, {"\uCCD4", "\uCCD4"}, {"\uCCD5", "\uCCD5"}, {"\uCCD6", "\uCCD6"}, {"\uCCD7", "\uCCD7"}, {"\uCCD8", "\uCCD8"}, {"\uCCD9", "\uCCD9"}, {"\uCCDA", "\uCCDA"}, {"\uCCDB", "\uCCDB"}, {"\uCCDC", "\uCCDC"}, {"\uCCDD", "\uCCDD"}, {"\uCCDE", "\uCCDE"}, {"\uCCDF", "\uCCDF"}, {"\uCCE0", "\uCCE0"}, {"\uCCE1", "\uCCE1"}, {"\uCCE2", "\uCCE2"}, {"\uCCE3", "\uCCE3"}, {"\uCCE4", "\uCCE4"}, {"\uCCE5", "\uCCE5"}, {"\uCCE6", "\uCCE6"}, {"\uCCE7", "\uCCE7"}, {"\uCCE8", "\uCCE8"}, {"\uCCE9", "\uCCE9"}, {"\uCCEA", "\uCCEA"}, {"\uCCEB", "\uCCEB"}, {"\uCCEC", "\uCCEC"}, {"\uCCED", "\uCCED"}, {"\uCCEE", "\uCCEE"}, {"\uCCEF", "\uCCEF"}, {"\uCCF0", "\uCCF0"}, {"\uCCF1", "\uCCF1"}, {"\uCCF2", "\uCCF2"}, {"\uCCF3", "\uCCF3"}, {"\uCCF4", "\uCCF4"}, {"\uCCF5", "\uCCF5"}, {"\uCCF6", "\uCCF6"}, {"\uCCF7", "\uCCF7"}, {"\uCCF8", "\uCCF8"}, {"\uCCF9", "\uCCF9"}, {"\uCCFA", "\uCCFA"}, {"\uCCFB", "\uCCFB"}, {"\uCCFC", "\uCCFC"}, {"\uCCFD", "\uCCFD"}, {"\uCCFE", "\uCCFE"}, {"\uCCFF", "\uCCFF"}, {"\uCD00", "\uCD00"}, {"\uCD01", "\uCD01"}, {"\uCD02", "\uCD02"}, {"\uCD03", "\uCD03"}, {"\uCD04", "\uCD04"}, {"\uCD05", "\uCD05"}, {"\uCD06", "\uCD06"}, {"\uCD07", "\uCD07"}, {"\uCD08", "\uCD08"}, {"\uCD09", "\uCD09"}, {"\uCD0A", "\uCD0A"}, {"\uCD0B", "\uCD0B"}, {"\uCD0C", "\uCD0C"}, {"\uCD0D", "\uCD0D"}, {"\uCD0E", "\uCD0E"}, {"\uCD0F", "\uCD0F"}, {"\uCD10", "\uCD10"}, {"\uCD11", "\uCD11"}, {"\uCD12", "\uCD12"}, {"\uCD13", "\uCD13"}, {"\uCD14", "\uCD14"}, {"\uCD15", "\uCD15"}, {"\uCD16", "\uCD16"}, {"\uCD17", "\uCD17"}, {"\uCD18", "\uCD18"}, {"\uCD19", "\uCD19"}, {"\uCD1A", "\uCD1A"}, {"\uCD1B", "\uCD1B"}, {"\uCD1C", "\uCD1C"}, {"\uCD1D", "\uCD1D"}, {"\uCD1E", "\uCD1E"}, {"\uCD1F", "\uCD1F"}, {"\uCD20", "\uCD20"}, {"\uCD21", "\uCD21"}, {"\uCD22", "\uCD22"}, {"\uCD23", "\uCD23"}, {"\uCD24", "\uCD24"}, {"\uCD25", "\uCD25"}, {"\uCD26", "\uCD26"}, {"\uCD27", "\uCD27"}, {"\uCD28", "\uCD28"}, {"\uCD29", "\uCD29"}, {"\uCD2A", "\uCD2A"}, {"\uCD2B", "\uCD2B"}, {"\uCD2C", "\uCD2C"}, {"\uCD2D", "\uCD2D"}, {"\uCD2E", "\uCD2E"}, {"\uCD2F", "\uCD2F"}, {"\uCD30", "\uCD30"}, {"\uCD31", "\uCD31"}, {"\uCD32", "\uCD32"}, {"\uCD33", "\uCD33"}, {"\uCD34", "\uCD34"}, {"\uCD35", "\uCD35"}, {"\uCD36", "\uCD36"}, {"\uCD37", "\uCD37"}, {"\uCD38", "\uCD38"}, {"\uCD39", "\uCD39"}, {"\uCD3A", "\uCD3A"}, {"\uCD3B", "\uCD3B"}, {"\uCD3C", "\uCD3C"}, {"\uCD3D", "\uCD3D"}, {"\uCD3E", "\uCD3E"}, {"\uCD3F", "\uCD3F"}, {"\uCD40", "\uCD40"}, {"\uCD41", "\uCD41"}, {"\uCD42", "\uCD42"}, {"\uCD43", "\uCD43"}, {"\uCD44", "\uCD44"}, {"\uCD45", "\uCD45"}, {"\uCD46", "\uCD46"}, {"\uCD47", "\uCD47"}, {"\uCD48", "\uCD48"}, {"\uCD49", "\uCD49"}, {"\uCD4A", "\uCD4A"}, {"\uCD4B", "\uCD4B"}, {"\uCD4C", "\uCD4C"}, {"\uCD4D", "\uCD4D"}, {"\uCD4E", "\uCD4E"}, {"\uCD4F", "\uCD4F"}, {"\uCD50", "\uCD50"}, {"\uCD51", "\uCD51"}, {"\uCD52", "\uCD52"}, {"\uCD53", "\uCD53"}, {"\uCD54", "\uCD54"}, {"\uCD55", "\uCD55"}, {"\uCD56", "\uCD56"}, {"\uCD57", "\uCD57"}, {"\uCD58", "\uCD58"}, {"\uCD59", "\uCD59"}, {"\uCD5A", "\uCD5A"}, {"\uCD5B", "\uCD5B"}, {"\uCD5C", "\uCD5C"}, {"\uCD5D", "\uCD5D"}, {"\uCD5E", "\uCD5E"}, {"\uCD5F", "\uCD5F"}, {"\uCD60", "\uCD60"}, {"\uCD61", "\uCD61"}, {"\uCD62", "\uCD62"}, {"\uCD63", "\uCD63"}, {"\uCD64", "\uCD64"}, {"\uCD65", "\uCD65"}, {"\uCD66", "\uCD66"}, {"\uCD67", "\uCD67"}, {"\uCD68", "\uCD68"}, {"\uCD69", "\uCD69"}, {"\uCD6A", "\uCD6A"}, {"\uCD6B", "\uCD6B"}, {"\uCD6C", "\uCD6C"}, {"\uCD6D", "\uCD6D"}, {"\uCD6E", "\uCD6E"}, {"\uCD6F", "\uCD6F"}, {"\uCD70", "\uCD70"}, {"\uCD71", "\uCD71"}, {"\uCD72", "\uCD72"}, {"\uCD73", "\uCD73"}, {"\uCD74", "\uCD74"}, {"\uCD75", "\uCD75"}, {"\uCD76", "\uCD76"}, {"\uCD77", "\uCD77"}, {"\uCD78", "\uCD78"}, {"\uCD79", "\uCD79"}, {"\uCD7A", "\uCD7A"}, {"\uCD7B", "\uCD7B"}, {"\uCD7C", "\uCD7C"}, {"\uCD7D", "\uCD7D"}, {"\uCD7E", "\uCD7E"}, {"\uCD7F", "\uCD7F"}, {"\uCD80", "\uCD80"}, {"\uCD81", "\uCD81"}, {"\uCD82", "\uCD82"}, {"\uCD83", "\uCD83"}, {"\uCD84", "\uCD84"}, {"\uCD85", "\uCD85"}, {"\uCD86", "\uCD86"}, {"\uCD87", "\uCD87"}, {"\uCD88", "\uCD88"}, {"\uCD89", "\uCD89"}, {"\uCD8A", "\uCD8A"}, {"\uCD8B", "\uCD8B"}, {"\uCD8C", "\uCD8C"}, {"\uCD8D", "\uCD8D"}, {"\uCD8E", "\uCD8E"}, {"\uCD8F", "\uCD8F"}, {"\uCD90", "\uCD90"}, {"\uCD91", "\uCD91"}, {"\uCD92", "\uCD92"}, {"\uCD93", "\uCD93"}, {"\uCD94", "\uCD94"}, {"\uCD95", "\uCD95"}, {"\uCD96", "\uCD96"}, {"\uCD97", "\uCD97"}, {"\uCD98", "\uCD98"}, {"\uCD99", "\uCD99"}, {"\uCD9A", "\uCD9A"}, {"\uCD9B", "\uCD9B"}, {"\uCD9C", "\uCD9C"}, {"\uCD9D", "\uCD9D"}, {"\uCD9E", "\uCD9E"}, {"\uCD9F", "\uCD9F"}, {"\uCDA0", "\uCDA0"}, {"\uCDA1", "\uCDA1"}, {"\uCDA2", "\uCDA2"}, {"\uCDA3", "\uCDA3"}, {"\uCDA4", "\uCDA4"}, {"\uCDA5", "\uCDA5"}, {"\uCDA6", "\uCDA6"}, {"\uCDA7", "\uCDA7"}, {"\uCDA8", "\uCDA8"}, {"\uCDA9", "\uCDA9"}, {"\uCDAA", "\uCDAA"}, {"\uCDAB", "\uCDAB"}, {"\uCDAC", "\uCDAC"}, {"\uCDAD", "\uCDAD"}, {"\uCDAE", "\uCDAE"}, {"\uCDAF", "\uCDAF"}, {"\uCDB0", "\uCDB0"}, {"\uCDB1", "\uCDB1"}, {"\uCDB2", "\uCDB2"}, {"\uCDB3", "\uCDB3"}, {"\uCDB4", "\uCDB4"}, {"\uCDB5", "\uCDB5"}, {"\uCDB6", "\uCDB6"}, {"\uCDB7", "\uCDB7"}, {"\uCDB8", "\uCDB8"}, {"\uCDB9", "\uCDB9"}, {"\uCDBA", "\uCDBA"}, {"\uCDBB", "\uCDBB"}, {"\uCDBC", "\uCDBC"}, {"\uCDBD", "\uCDBD"}, {"\uCDBE", "\uCDBE"}, {"\uCDBF", "\uCDBF"}, {"\uCDC0", "\uCDC0"}, {"\uCDC1", "\uCDC1"}, {"\uCDC2", "\uCDC2"}, {"\uCDC3", "\uCDC3"}, {"\uCDC4", "\uCDC4"}, {"\uCDC5", "\uCDC5"}, {"\uCDC6", "\uCDC6"}, {"\uCDC7", "\uCDC7"}, {"\uCDC8", "\uCDC8"}, {"\uCDC9", "\uCDC9"}, {"\uCDCA", "\uCDCA"}, {"\uCDCB", "\uCDCB"}, {"\uCDCC", "\uCDCC"}, {"\uCDCD", "\uCDCD"}, {"\uCDCE", "\uCDCE"}, {"\uCDCF", "\uCDCF"}, {"\uCDD0", "\uCDD0"}, {"\uCDD1", "\uCDD1"}, {"\uCDD2", "\uCDD2"}, {"\uCDD3", "\uCDD3"}, {"\uCDD4", "\uCDD4"}, {"\uCDD5", "\uCDD5"}, {"\uCDD6", "\uCDD6"}, {"\uCDD7", "\uCDD7"}, {"\uCDD8", "\uCDD8"}, {"\uCDD9", "\uCDD9"}, {"\uCDDA", "\uCDDA"}, {"\uCDDB", "\uCDDB"}, {"\uCDDC", "\uCDDC"}, {"\uCDDD", "\uCDDD"}, {"\uCDDE", "\uCDDE"}, {"\uCDDF", "\uCDDF"}, {"\uCDE0", "\uCDE0"}, {"\uCDE1", "\uCDE1"}, {"\uCDE2", "\uCDE2"}, {"\uCDE3", "\uCDE3"}, {"\uCDE4", "\uCDE4"}, {"\uCDE5", "\uCDE5"}, {"\uCDE6", "\uCDE6"}, {"\uCDE7", "\uCDE7"}, {"\uCDE8", "\uCDE8"}, {"\uCDE9", "\uCDE9"}, {"\uCDEA", "\uCDEA"}, {"\uCDEB", "\uCDEB"}, {"\uCDEC", "\uCDEC"}, {"\uCDED", "\uCDED"}, {"\uCDEE", "\uCDEE"}, {"\uCDEF", "\uCDEF"}, {"\uCDF0", "\uCDF0"}, {"\uCDF1", "\uCDF1"}, {"\uCDF2", "\uCDF2"}, {"\uCDF3", "\uCDF3"}, {"\uCDF4", "\uCDF4"}, {"\uCDF5", "\uCDF5"}, {"\uCDF6", "\uCDF6"}, {"\uCDF7", "\uCDF7"}, {"\uCDF8", "\uCDF8"}, {"\uCDF9", "\uCDF9"}, {"\uCDFA", "\uCDFA"}, {"\uCDFB", "\uCDFB"}, {"\uCDFC", "\uCDFC"}, {"\uCDFD", "\uCDFD"}, {"\uCDFE", "\uCDFE"}, {"\uCDFF", "\uCDFF"}, {"\uCE00", "\uCE00"}, {"\uCE01", "\uCE01"}, {"\uCE02", "\uCE02"}, {"\uCE03", "\uCE03"}, {"\uCE04", "\uCE04"}, {"\uCE05", "\uCE05"}, {"\uCE06", "\uCE06"}, {"\uCE07", "\uCE07"}, {"\uCE08", "\uCE08"}, {"\uCE09", "\uCE09"}, {"\uCE0A", "\uCE0A"}, {"\uCE0B", "\uCE0B"}, {"\uCE0C", "\uCE0C"}, {"\uCE0D", "\uCE0D"}, {"\uCE0E", "\uCE0E"}, {"\uCE0F", "\uCE0F"}, {"\uCE10", "\uCE10"}, {"\uCE11", "\uCE11"}, {"\uCE12", "\uCE12"}, {"\uCE13", "\uCE13"}, {"\uCE14", "\uCE14"}, {"\uCE15", "\uCE15"}, {"\uCE16", "\uCE16"}, {"\uCE17", "\uCE17"}, {"\uCE18", "\uCE18"}, {"\uCE19", "\uCE19"}, {"\uCE1A", "\uCE1A"}, {"\uCE1B", "\uCE1B"}, {"\uCE1C", "\uCE1C"}, {"\uCE1D", "\uCE1D"}, {"\uCE1E", "\uCE1E"}, {"\uCE1F", "\uCE1F"}, {"\uCE20", "\uCE20"}, {"\uCE21", "\uCE21"}, {"\uCE22", "\uCE22"}, {"\uCE23", "\uCE23"}, {"\uCE24", "\uCE24"}, {"\uCE25", "\uCE25"}, {"\uCE26", "\uCE26"}, {"\uCE27", "\uCE27"}, {"\uCE28", "\uCE28"}, {"\uCE29", "\uCE29"}, {"\uCE2A", "\uCE2A"}, {"\uCE2B", "\uCE2B"}, {"\uCE2C", "\uCE2C"}, {"\uCE2D", "\uCE2D"}, {"\uCE2E", "\uCE2E"}, {"\uCE2F", "\uCE2F"}, {"\uCE30", "\uCE30"}, {"\uCE31", "\uCE31"}, {"\uCE32", "\uCE32"}, {"\uCE33", "\uCE33"}, {"\uCE34", "\uCE34"}, {"\uCE35", "\uCE35"}, {"\uCE36", "\uCE36"}, {"\uCE37", "\uCE37"}, {"\uCE38", "\uCE38"}, {"\uCE39", "\uCE39"}, {"\uCE3A", "\uCE3A"}, {"\uCE3B", "\uCE3B"}, {"\uCE3C", "\uCE3C"}, {"\uCE3D", "\uCE3D"}, {"\uCE3E", "\uCE3E"}, {"\uCE3F", "\uCE3F"}, {"\uCE40", "\uCE40"}, {"\uCE41", "\uCE41"}, {"\uCE42", "\uCE42"}, {"\uCE43", "\uCE43"}, {"\uCE44", "\uCE44"}, {"\uCE45", "\uCE45"}, {"\uCE46", "\uCE46"}, {"\uCE47", "\uCE47"}, {"\uCE48", "\uCE48"}, {"\uCE49", "\uCE49"}, {"\uCE4A", "\uCE4A"}, {"\uCE4B", "\uCE4B"}, {"\uCE4C", "\uCE4C"}, {"\uCE4D", "\uCE4D"}, {"\uCE4E", "\uCE4E"}, {"\uCE4F", "\uCE4F"}, {"\uCE50", "\uCE50"}, {"\uCE51", "\uCE51"}, {"\uCE52", "\uCE52"}, {"\uCE53", "\uCE53"}, {"\uCE54", "\uCE54"}, {"\uCE55", "\uCE55"}, {"\uCE56", "\uCE56"}, {"\uCE57", "\uCE57"}, {"\uCE58", "\uCE58"}, {"\uCE59", "\uCE59"}, {"\uCE5A", "\uCE5A"}, {"\uCE5B", "\uCE5B"}, {"\uCE5C", "\uCE5C"}, {"\uCE5D", "\uCE5D"}, {"\uCE5E", "\uCE5E"}, {"\uCE5F", "\uCE5F"}, {"\uCE60", "\uCE60"}, {"\uCE61", "\uCE61"}, {"\uCE62", "\uCE62"}, {"\uCE63", "\uCE63"}, {"\uCE64", "\uCE64"}, {"\uCE65", "\uCE65"}, {"\uCE66", "\uCE66"}, {"\uCE67", "\uCE67"}, {"\uCE68", "\uCE68"}, {"\uCE69", "\uCE69"}, {"\uCE6A", "\uCE6A"}, {"\uCE6B", "\uCE6B"}, {"\uCE6C", "\uCE6C"}, {"\uCE6D", "\uCE6D"}, {"\uCE6E", "\uCE6E"}, {"\uCE6F", "\uCE6F"}, {"\uCE70", "\uCE70"}, {"\uCE71", "\uCE71"}, {"\uCE72", "\uCE72"}, {"\uCE73", "\uCE73"}, {"\uCE74", "\uCE74"}, {"\uCE75", "\uCE75"}, {"\uCE76", "\uCE76"}, {"\uCE77", "\uCE77"}, {"\uCE78", "\uCE78"}, {"\uCE79", "\uCE79"}, {"\uCE7A", "\uCE7A"}, {"\uCE7B", "\uCE7B"}, {"\uCE7C", "\uCE7C"}, {"\uCE7D", "\uCE7D"}, {"\uCE7E", "\uCE7E"}, {"\uCE7F", "\uCE7F"}, {"\uCE80", "\uCE80"}, {"\uCE81", "\uCE81"}, {"\uCE82", "\uCE82"}, {"\uCE83", "\uCE83"}, {"\uCE84", "\uCE84"}, {"\uCE85", "\uCE85"}, {"\uCE86", "\uCE86"}, {"\uCE87", "\uCE87"}, {"\uCE88", "\uCE88"}, {"\uCE89", "\uCE89"}, {"\uCE8A", "\uCE8A"}, {"\uCE8B", "\uCE8B"}, {"\uCE8C", "\uCE8C"}, {"\uCE8D", "\uCE8D"}, {"\uCE8E", "\uCE8E"}, {"\uCE8F", "\uCE8F"}, {"\uCE90", "\uCE90"}, {"\uCE91", "\uCE91"}, {"\uCE92", "\uCE92"}, {"\uCE93", "\uCE93"}, {"\uCE94", "\uCE94"}, {"\uCE95", "\uCE95"}, {"\uCE96", "\uCE96"}, {"\uCE97", "\uCE97"}, {"\uCE98", "\uCE98"}, {"\uCE99", "\uCE99"}, {"\uCE9A", "\uCE9A"}, {"\uCE9B", "\uCE9B"}, {"\uCE9C", "\uCE9C"}, {"\uCE9D", "\uCE9D"}, {"\uCE9E", "\uCE9E"}, {"\uCE9F", "\uCE9F"}, {"\uCEA0", "\uCEA0"}, {"\uCEA1", "\uCEA1"}, {"\uCEA2", "\uCEA2"}, {"\uCEA3", "\uCEA3"}, {"\uCEA4", "\uCEA4"}, {"\uCEA5", "\uCEA5"}, {"\uCEA6", "\uCEA6"}, {"\uCEA7", "\uCEA7"}, {"\uCEA8", "\uCEA8"}, {"\uCEA9", "\uCEA9"}, {"\uCEAA", "\uCEAA"}, {"\uCEAB", "\uCEAB"}, {"\uCEAC", "\uCEAC"}, {"\uCEAD", "\uCEAD"}, {"\uCEAE", "\uCEAE"}, {"\uCEAF", "\uCEAF"}, {"\uCEB0", "\uCEB0"}, {"\uCEB1", "\uCEB1"}, {"\uCEB2", "\uCEB2"}, {"\uCEB3", "\uCEB3"}, {"\uCEB4", "\uCEB4"}, {"\uCEB5", "\uCEB5"}, {"\uCEB6", "\uCEB6"}, {"\uCEB7", "\uCEB7"}, {"\uCEB8", "\uCEB8"}, {"\uCEB9", "\uCEB9"}, {"\uCEBA", "\uCEBA"}, {"\uCEBB", "\uCEBB"}, {"\uCEBC", "\uCEBC"}, {"\uCEBD", "\uCEBD"}, {"\uCEBE", "\uCEBE"}, {"\uCEBF", "\uCEBF"}, {"\uCEC0", "\uCEC0"}, {"\uCEC1", "\uCEC1"}, {"\uCEC2", "\uCEC2"}, {"\uCEC3", "\uCEC3"}, {"\uCEC4", "\uCEC4"}, {"\uCEC5", "\uCEC5"}, {"\uCEC6", "\uCEC6"}, {"\uCEC7", "\uCEC7"}, {"\uCEC8", "\uCEC8"}, {"\uCEC9", "\uCEC9"}, {"\uCECA", "\uCECA"}, {"\uCECB", "\uCECB"}, {"\uCECC", "\uCECC"}, {"\uCECD", "\uCECD"}, {"\uCECE", "\uCECE"}, {"\uCECF", "\uCECF"}, {"\uCED0", "\uCED0"}, {"\uCED1", "\uCED1"}, {"\uCED2", "\uCED2"}, {"\uCED3", "\uCED3"}, {"\uCED4", "\uCED4"}, {"\uCED5", "\uCED5"}, {"\uCED6", "\uCED6"}, {"\uCED7", "\uCED7"}, {"\uCED8", "\uCED8"}, {"\uCED9", "\uCED9"}, {"\uCEDA", "\uCEDA"}, {"\uCEDB", "\uCEDB"}, {"\uCEDC", "\uCEDC"}, {"\uCEDD", "\uCEDD"}, {"\uCEDE", "\uCEDE"}, {"\uCEDF", "\uCEDF"}, {"\uCEE0", "\uCEE0"}, {"\uCEE1", "\uCEE1"}, {"\uCEE2", "\uCEE2"}, {"\uCEE3", "\uCEE3"}, {"\uCEE4", "\uCEE4"}, {"\uCEE5", "\uCEE5"}, {"\uCEE6", "\uCEE6"}, {"\uCEE7", "\uCEE7"}, {"\uCEE8", "\uCEE8"}, {"\uCEE9", "\uCEE9"}, {"\uCEEA", "\uCEEA"}, {"\uCEEB", "\uCEEB"}, {"\uCEEC", "\uCEEC"}, {"\uCEED", "\uCEED"}, {"\uCEEE", "\uCEEE"}, {"\uCEEF", "\uCEEF"}, {"\uCEF0", "\uCEF0"}, {"\uCEF1", "\uCEF1"}, {"\uCEF2", "\uCEF2"}, {"\uCEF3", "\uCEF3"}, {"\uCEF4", "\uCEF4"}, {"\uCEF5", "\uCEF5"}, {"\uCEF6", "\uCEF6"}, {"\uCEF7", "\uCEF7"}, {"\uCEF8", "\uCEF8"}, {"\uCEF9", "\uCEF9"}, {"\uCEFA", "\uCEFA"}, {"\uCEFB", "\uCEFB"}, {"\uCEFC", "\uCEFC"}, {"\uCEFD", "\uCEFD"}, {"\uCEFE", "\uCEFE"}, {"\uCEFF", "\uCEFF"}, {"\uCF00", "\uCF00"}, {"\uCF01", "\uCF01"}, {"\uCF02", "\uCF02"}, {"\uCF03", "\uCF03"}, {"\uCF04", "\uCF04"}, {"\uCF05", "\uCF05"}, {"\uCF06", "\uCF06"}, {"\uCF07", "\uCF07"}, {"\uCF08", "\uCF08"}, {"\uCF09", "\uCF09"}, {"\uCF0A", "\uCF0A"}, {"\uCF0B", "\uCF0B"}, {"\uCF0C", "\uCF0C"}, {"\uCF0D", "\uCF0D"}, {"\uCF0E", "\uCF0E"}, {"\uCF0F", "\uCF0F"}, {"\uCF10", "\uCF10"}, {"\uCF11", "\uCF11"}, {"\uCF12", "\uCF12"}, {"\uCF13", "\uCF13"}, {"\uCF14", "\uCF14"}, {"\uCF15", "\uCF15"}, {"\uCF16", "\uCF16"}, {"\uCF17", "\uCF17"}, {"\uCF18", "\uCF18"}, {"\uCF19", "\uCF19"}, {"\uCF1A", "\uCF1A"}, {"\uCF1B", "\uCF1B"}, {"\uCF1C", "\uCF1C"}, {"\uCF1D", "\uCF1D"}, {"\uCF1E", "\uCF1E"}, {"\uCF1F", "\uCF1F"}, {"\uCF20", "\uCF20"}, {"\uCF21", "\uCF21"}, {"\uCF22", "\uCF22"}, {"\uCF23", "\uCF23"}, {"\uCF24", "\uCF24"}, {"\uCF25", "\uCF25"}, {"\uCF26", "\uCF26"}, {"\uCF27", "\uCF27"}, {"\uCF28", "\uCF28"}, {"\uCF29", "\uCF29"}, {"\uCF2A", "\uCF2A"}, {"\uCF2B", "\uCF2B"}, {"\uCF2C", "\uCF2C"}, {"\uCF2D", "\uCF2D"}, {"\uCF2E", "\uCF2E"}, {"\uCF2F", "\uCF2F"}, {"\uCF30", "\uCF30"}, {"\uCF31", "\uCF31"}, {"\uCF32", "\uCF32"}, {"\uCF33", "\uCF33"}, {"\uCF34", "\uCF34"}, {"\uCF35", "\uCF35"}, {"\uCF36", "\uCF36"}, {"\uCF37", "\uCF37"}, {"\uCF38", "\uCF38"}, {"\uCF39", "\uCF39"}, {"\uCF3A", "\uCF3A"}, {"\uCF3B", "\uCF3B"}, {"\uCF3C", "\uCF3C"}, {"\uCF3D", "\uCF3D"}, {"\uCF3E", "\uCF3E"}, {"\uCF3F", "\uCF3F"}, {"\uCF40", "\uCF40"}, {"\uCF41", "\uCF41"}, {"\uCF42", "\uCF42"}, {"\uCF43", "\uCF43"}, {"\uCF44", "\uCF44"}, {"\uCF45", "\uCF45"}, {"\uCF46", "\uCF46"}, {"\uCF47", "\uCF47"}, {"\uCF48", "\uCF48"}, {"\uCF49", "\uCF49"}, {"\uCF4A", "\uCF4A"}, {"\uCF4B", "\uCF4B"}, {"\uCF4C", "\uCF4C"}, {"\uCF4D", "\uCF4D"}, {"\uCF4E", "\uCF4E"}, {"\uCF4F", "\uCF4F"}, {"\uCF50", "\uCF50"}, {"\uCF51", "\uCF51"}, {"\uCF52", "\uCF52"}, {"\uCF53", "\uCF53"}, {"\uCF54", "\uCF54"}, {"\uCF55", "\uCF55"}, {"\uCF56", "\uCF56"}, {"\uCF57", "\uCF57"}, {"\uCF58", "\uCF58"}, {"\uCF59", "\uCF59"}, {"\uCF5A", "\uCF5A"}, {"\uCF5B", "\uCF5B"}, {"\uCF5C", "\uCF5C"}, {"\uCF5D", "\uCF5D"}, {"\uCF5E", "\uCF5E"}, {"\uCF5F", "\uCF5F"}, {"\uCF60", "\uCF60"}, {"\uCF61", "\uCF61"}, {"\uCF62", "\uCF62"}, {"\uCF63", "\uCF63"}, {"\uCF64", "\uCF64"}, {"\uCF65", "\uCF65"}, {"\uCF66", "\uCF66"}, {"\uCF67", "\uCF67"}, {"\uCF68", "\uCF68"}, {"\uCF69", "\uCF69"}, {"\uCF6A", "\uCF6A"}, {"\uCF6B", "\uCF6B"}, {"\uCF6C", "\uCF6C"}, {"\uCF6D", "\uCF6D"}, {"\uCF6E", "\uCF6E"}, {"\uCF6F", "\uCF6F"}, {"\uCF70", "\uCF70"}, {"\uCF71", "\uCF71"}, {"\uCF72", "\uCF72"}, {"\uCF73", "\uCF73"}, {"\uCF74", "\uCF74"}, {"\uCF75", "\uCF75"}, {"\uCF76", "\uCF76"}, {"\uCF77", "\uCF77"}, {"\uCF78", "\uCF78"}, {"\uCF79", "\uCF79"}, {"\uCF7A", "\uCF7A"}, {"\uCF7B", "\uCF7B"}, {"\uCF7C", "\uCF7C"}, {"\uCF7D", "\uCF7D"}, {"\uCF7E", "\uCF7E"}, {"\uCF7F", "\uCF7F"}, {"\uCF80", "\uCF80"}, {"\uCF81", "\uCF81"}, {"\uCF82", "\uCF82"}, {"\uCF83", "\uCF83"}, {"\uCF84", "\uCF84"}, {"\uCF85", "\uCF85"}, {"\uCF86", "\uCF86"}, {"\uCF87", "\uCF87"}, {"\uCF88", "\uCF88"}, {"\uCF89", "\uCF89"}, {"\uCF8A", "\uCF8A"}, {"\uCF8B", "\uCF8B"}, {"\uCF8C", "\uCF8C"}, {"\uCF8D", "\uCF8D"}, {"\uCF8E", "\uCF8E"}, {"\uCF8F", "\uCF8F"}, {"\uCF90", "\uCF90"}, {"\uCF91", "\uCF91"}, {"\uCF92", "\uCF92"}, {"\uCF93", "\uCF93"}, {"\uCF94", "\uCF94"}, {"\uCF95", "\uCF95"}, {"\uCF96", "\uCF96"}, {"\uCF97", "\uCF97"}, {"\uCF98", "\uCF98"}, {"\uCF99", "\uCF99"}, {"\uCF9A", "\uCF9A"}, {"\uCF9B", "\uCF9B"}, {"\uCF9C", "\uCF9C"}, {"\uCF9D", "\uCF9D"}, {"\uCF9E", "\uCF9E"}, {"\uCF9F", "\uCF9F"}, {"\uCFA0", "\uCFA0"}, {"\uCFA1", "\uCFA1"}, {"\uCFA2", "\uCFA2"}, {"\uCFA3", "\uCFA3"}, {"\uCFA4", "\uCFA4"}, {"\uCFA5", "\uCFA5"}, {"\uCFA6", "\uCFA6"}, {"\uCFA7", "\uCFA7"}, {"\uCFA8", "\uCFA8"}, {"\uCFA9", "\uCFA9"}, {"\uCFAA", "\uCFAA"}, {"\uCFAB", "\uCFAB"}, {"\uCFAC", "\uCFAC"}, {"\uCFAD", "\uCFAD"}, {"\uCFAE", "\uCFAE"}, {"\uCFAF", "\uCFAF"}, {"\uCFB0", "\uCFB0"}, {"\uCFB1", "\uCFB1"}, {"\uCFB2", "\uCFB2"}, {"\uCFB3", "\uCFB3"}, {"\uCFB4", "\uCFB4"}, {"\uCFB5", "\uCFB5"}, {"\uCFB6", "\uCFB6"}, {"\uCFB7", "\uCFB7"}, {"\uCFB8", "\uCFB8"}, {"\uCFB9", "\uCFB9"}, {"\uCFBA", "\uCFBA"}, {"\uCFBB", "\uCFBB"}, {"\uCFBC", "\uCFBC"}, {"\uCFBD", "\uCFBD"}, {"\uCFBE", "\uCFBE"}, {"\uCFBF", "\uCFBF"}, {"\uCFC0", "\uCFC0"}, {"\uCFC1", "\uCFC1"}, {"\uCFC2", "\uCFC2"}, {"\uCFC3", "\uCFC3"}, {"\uCFC4", "\uCFC4"}, {"\uCFC5", "\uCFC5"}, {"\uCFC6", "\uCFC6"}, {"\uCFC7", "\uCFC7"}, {"\uCFC8", "\uCFC8"}, {"\uCFC9", "\uCFC9"}, {"\uCFCA", "\uCFCA"}, {"\uCFCB", "\uCFCB"}, {"\uCFCC", "\uCFCC"}, {"\uCFCD", "\uCFCD"}, {"\uCFCE", "\uCFCE"}, {"\uCFCF", "\uCFCF"}, {"\uCFD0", "\uCFD0"}, {"\uCFD1", "\uCFD1"}, {"\uCFD2", "\uCFD2"}, {"\uCFD3", "\uCFD3"}, {"\uCFD4", "\uCFD4"}, {"\uCFD5", "\uCFD5"}, {"\uCFD6", "\uCFD6"}, {"\uCFD7", "\uCFD7"}, {"\uCFD8", "\uCFD8"}, {"\uCFD9", "\uCFD9"}, {"\uCFDA", "\uCFDA"}, {"\uCFDB", "\uCFDB"}, {"\uCFDC", "\uCFDC"}, {"\uCFDD", "\uCFDD"}, {"\uCFDE", "\uCFDE"}, {"\uCFDF", "\uCFDF"}, {"\uCFE0", "\uCFE0"}, {"\uCFE1", "\uCFE1"}, {"\uCFE2", "\uCFE2"}, {"\uCFE3", "\uCFE3"}, {"\uCFE4", "\uCFE4"}, {"\uCFE5", "\uCFE5"}, {"\uCFE6", "\uCFE6"}, {"\uCFE7", "\uCFE7"}, {"\uCFE8", "\uCFE8"}, {"\uCFE9", "\uCFE9"}, {"\uCFEA", "\uCFEA"}, {"\uCFEB", "\uCFEB"}, {"\uCFEC", "\uCFEC"}, {"\uCFED", "\uCFED"}, {"\uCFEE", "\uCFEE"}, {"\uCFEF", "\uCFEF"}, {"\uCFF0", "\uCFF0"}, {"\uCFF1", "\uCFF1"}, {"\uCFF2", "\uCFF2"}, {"\uCFF3", "\uCFF3"}, {"\uCFF4", "\uCFF4"}, {"\uCFF5", "\uCFF5"}, {"\uCFF6", "\uCFF6"}, {"\uCFF7", "\uCFF7"}, {"\uCFF8", "\uCFF8"}, {"\uCFF9", "\uCFF9"}, {"\uCFFA", "\uCFFA"}, {"\uCFFB", "\uCFFB"}, {"\uCFFC", "\uCFFC"}, {"\uCFFD", "\uCFFD"}, {"\uCFFE", "\uCFFE"}, {"\uCFFF", "\uCFFF"}, {"\uD000", "\uD000"}, {"\uD001", "\uD001"}, {"\uD002", "\uD002"}, {"\uD003", "\uD003"}, {"\uD004", "\uD004"}, {"\uD005", "\uD005"}, {"\uD006", "\uD006"}, {"\uD007", "\uD007"}, {"\uD008", "\uD008"}, {"\uD009", "\uD009"}, {"\uD00A", "\uD00A"}, {"\uD00B", "\uD00B"}, {"\uD00C", "\uD00C"}, {"\uD00D", "\uD00D"}, {"\uD00E", "\uD00E"}, {"\uD00F", "\uD00F"}, {"\uD010", "\uD010"}, {"\uD011", "\uD011"}, {"\uD012", "\uD012"}, {"\uD013", "\uD013"}, {"\uD014", "\uD014"}, {"\uD015", "\uD015"}, {"\uD016", "\uD016"}, {"\uD017", "\uD017"}, {"\uD018", "\uD018"}, {"\uD019", "\uD019"}, {"\uD01A", "\uD01A"}, {"\uD01B", "\uD01B"}, {"\uD01C", "\uD01C"}, {"\uD01D", "\uD01D"}, {"\uD01E", "\uD01E"}, {"\uD01F", "\uD01F"}, {"\uD020", "\uD020"}, {"\uD021", "\uD021"}, {"\uD022", "\uD022"}, {"\uD023", "\uD023"}, {"\uD024", "\uD024"}, {"\uD025", "\uD025"}, {"\uD026", "\uD026"}, {"\uD027", "\uD027"}, {"\uD028", "\uD028"}, {"\uD029", "\uD029"}, {"\uD02A", "\uD02A"}, {"\uD02B", "\uD02B"}, {"\uD02C", "\uD02C"}, {"\uD02D", "\uD02D"}, {"\uD02E", "\uD02E"}, {"\uD02F", "\uD02F"}, {"\uD030", "\uD030"}, {"\uD031", "\uD031"}, {"\uD032", "\uD032"}, {"\uD033", "\uD033"}, {"\uD034", "\uD034"}, {"\uD035", "\uD035"}, {"\uD036", "\uD036"}, {"\uD037", "\uD037"}, {"\uD038", "\uD038"}, {"\uD039", "\uD039"}, {"\uD03A", "\uD03A"}, {"\uD03B", "\uD03B"}, {"\uD03C", "\uD03C"}, {"\uD03D", "\uD03D"}, {"\uD03E", "\uD03E"}, {"\uD03F", "\uD03F"}, {"\uD040", "\uD040"}, {"\uD041", "\uD041"}, {"\uD042", "\uD042"}, {"\uD043", "\uD043"}, {"\uD044", "\uD044"}, {"\uD045", "\uD045"}, {"\uD046", "\uD046"}, {"\uD047", "\uD047"}, {"\uD048", "\uD048"}, {"\uD049", "\uD049"}, {"\uD04A", "\uD04A"}, {"\uD04B", "\uD04B"}, {"\uD04C", "\uD04C"}, {"\uD04D", "\uD04D"}, {"\uD04E", "\uD04E"}, {"\uD04F", "\uD04F"}, {"\uD050", "\uD050"}, {"\uD051", "\uD051"}, {"\uD052", "\uD052"}, {"\uD053", "\uD053"}, {"\uD054", "\uD054"}, {"\uD055", "\uD055"}, {"\uD056", "\uD056"}, {"\uD057", "\uD057"}, {"\uD058", "\uD058"}, {"\uD059", "\uD059"}, {"\uD05A", "\uD05A"}, {"\uD05B", "\uD05B"}, {"\uD05C", "\uD05C"}, {"\uD05D", "\uD05D"}, {"\uD05E", "\uD05E"}, {"\uD05F", "\uD05F"}, {"\uD060", "\uD060"}, {"\uD061", "\uD061"}, {"\uD062", "\uD062"}, {"\uD063", "\uD063"}, {"\uD064", "\uD064"}, {"\uD065", "\uD065"}, {"\uD066", "\uD066"}, {"\uD067", "\uD067"}, {"\uD068", "\uD068"}, {"\uD069", "\uD069"}, {"\uD06A", "\uD06A"}, {"\uD06B", "\uD06B"}, {"\uD06C", "\uD06C"}, {"\uD06D", "\uD06D"}, {"\uD06E", "\uD06E"}, {"\uD06F", "\uD06F"}, {"\uD070", "\uD070"}, {"\uD071", "\uD071"}, {"\uD072", "\uD072"}, {"\uD073", "\uD073"}, {"\uD074", "\uD074"}, {"\uD075", "\uD075"}, {"\uD076", "\uD076"}, {"\uD077", "\uD077"}, {"\uD078", "\uD078"}, {"\uD079", "\uD079"}, {"\uD07A", "\uD07A"}, {"\uD07B", "\uD07B"}, {"\uD07C", "\uD07C"}, {"\uD07D", "\uD07D"}, {"\uD07E", "\uD07E"}, {"\uD07F", "\uD07F"}, {"\uD080", "\uD080"}, {"\uD081", "\uD081"}, {"\uD082", "\uD082"}, {"\uD083", "\uD083"}, {"\uD084", "\uD084"}, {"\uD085", "\uD085"}, {"\uD086", "\uD086"}, {"\uD087", "\uD087"}, {"\uD088", "\uD088"}, {"\uD089", "\uD089"}, {"\uD08A", "\uD08A"}, {"\uD08B", "\uD08B"}, {"\uD08C", "\uD08C"}, {"\uD08D", "\uD08D"}, {"\uD08E", "\uD08E"}, {"\uD08F", "\uD08F"}, {"\uD090", "\uD090"}, {"\uD091", "\uD091"}, {"\uD092", "\uD092"}, {"\uD093", "\uD093"}, {"\uD094", "\uD094"}, {"\uD095", "\uD095"}, {"\uD096", "\uD096"}, {"\uD097", "\uD097"}, {"\uD098", "\uD098"}, {"\uD099", "\uD099"}, {"\uD09A", "\uD09A"}, {"\uD09B", "\uD09B"}, {"\uD09C", "\uD09C"}, {"\uD09D", "\uD09D"}, {"\uD09E", "\uD09E"}, {"\uD09F", "\uD09F"}, {"\uD0A0", "\uD0A0"}, {"\uD0A1", "\uD0A1"}, {"\uD0A2", "\uD0A2"}, {"\uD0A3", "\uD0A3"}, {"\uD0A4", "\uD0A4"}, {"\uD0A5", "\uD0A5"}, {"\uD0A6", "\uD0A6"}, {"\uD0A7", "\uD0A7"}, {"\uD0A8", "\uD0A8"}, {"\uD0A9", "\uD0A9"}, {"\uD0AA", "\uD0AA"}, {"\uD0AB", "\uD0AB"}, {"\uD0AC", "\uD0AC"}, {"\uD0AD", "\uD0AD"}, {"\uD0AE", "\uD0AE"}, {"\uD0AF", "\uD0AF"}, {"\uD0B0", "\uD0B0"}, {"\uD0B1", "\uD0B1"}, {"\uD0B2", "\uD0B2"}, {"\uD0B3", "\uD0B3"}, {"\uD0B4", "\uD0B4"}, {"\uD0B5", "\uD0B5"}, {"\uD0B6", "\uD0B6"}, {"\uD0B7", "\uD0B7"}, {"\uD0B8", "\uD0B8"}, {"\uD0B9", "\uD0B9"}, {"\uD0BA", "\uD0BA"}, {"\uD0BB", "\uD0BB"}, {"\uD0BC", "\uD0BC"}, {"\uD0BD", "\uD0BD"}, {"\uD0BE", "\uD0BE"}, {"\uD0BF", "\uD0BF"}, {"\uD0C0", "\uD0C0"}, {"\uD0C1", "\uD0C1"}, {"\uD0C2", "\uD0C2"}, {"\uD0C3", "\uD0C3"}, {"\uD0C4", "\uD0C4"}, {"\uD0C5", "\uD0C5"}, {"\uD0C6", "\uD0C6"}, {"\uD0C7", "\uD0C7"}, {"\uD0C8", "\uD0C8"}, {"\uD0C9", "\uD0C9"}, {"\uD0CA", "\uD0CA"}, {"\uD0CB", "\uD0CB"}, {"\uD0CC", "\uD0CC"}, {"\uD0CD", "\uD0CD"}, {"\uD0CE", "\uD0CE"}, {"\uD0CF", "\uD0CF"}, {"\uD0D0", "\uD0D0"}, {"\uD0D1", "\uD0D1"}, {"\uD0D2", "\uD0D2"}, {"\uD0D3", "\uD0D3"}, {"\uD0D4", "\uD0D4"}, {"\uD0D5", "\uD0D5"}, {"\uD0D6", "\uD0D6"}, {"\uD0D7", "\uD0D7"}, {"\uD0D8", "\uD0D8"}, {"\uD0D9", "\uD0D9"}, {"\uD0DA", "\uD0DA"}, {"\uD0DB", "\uD0DB"}, {"\uD0DC", "\uD0DC"}, {"\uD0DD", "\uD0DD"}, {"\uD0DE", "\uD0DE"}, {"\uD0DF", "\uD0DF"}, {"\uD0E0", "\uD0E0"}, {"\uD0E1", "\uD0E1"}, {"\uD0E2", "\uD0E2"}, {"\uD0E3", "\uD0E3"}, {"\uD0E4", "\uD0E4"}, {"\uD0E5", "\uD0E5"}, {"\uD0E6", "\uD0E6"}, {"\uD0E7", "\uD0E7"}, {"\uD0E8", "\uD0E8"}, {"\uD0E9", "\uD0E9"}, {"\uD0EA", "\uD0EA"}, {"\uD0EB", "\uD0EB"}, {"\uD0EC", "\uD0EC"}, {"\uD0ED", "\uD0ED"}, {"\uD0EE", "\uD0EE"}, {"\uD0EF", "\uD0EF"}, {"\uD0F0", "\uD0F0"}, {"\uD0F1", "\uD0F1"}, {"\uD0F2", "\uD0F2"}, {"\uD0F3", "\uD0F3"}, {"\uD0F4", "\uD0F4"}, {"\uD0F5", "\uD0F5"}, {"\uD0F6", "\uD0F6"}, {"\uD0F7", "\uD0F7"}, {"\uD0F8", "\uD0F8"}, {"\uD0F9", "\uD0F9"}, {"\uD0FA", "\uD0FA"}, {"\uD0FB", "\uD0FB"}, {"\uD0FC", "\uD0FC"}, {"\uD0FD", "\uD0FD"}, {"\uD0FE", "\uD0FE"}, {"\uD0FF", "\uD0FF"}, {"\uD100", "\uD100"}, {"\uD101", "\uD101"}, {"\uD102", "\uD102"}, {"\uD103", "\uD103"}, {"\uD104", "\uD104"}, {"\uD105", "\uD105"}, {"\uD106", "\uD106"}, {"\uD107", "\uD107"}, {"\uD108", "\uD108"}, {"\uD109", "\uD109"}, {"\uD10A", "\uD10A"}, {"\uD10B", "\uD10B"}, {"\uD10C", "\uD10C"}, {"\uD10D", "\uD10D"}, {"\uD10E", "\uD10E"}, {"\uD10F", "\uD10F"}, {"\uD110", "\uD110"}, {"\uD111", "\uD111"}, {"\uD112", "\uD112"}, {"\uD113", "\uD113"}, {"\uD114", "\uD114"}, {"\uD115", "\uD115"}, {"\uD116", "\uD116"}, {"\uD117", "\uD117"}, {"\uD118", "\uD118"}, {"\uD119", "\uD119"}, {"\uD11A", "\uD11A"}, {"\uD11B", "\uD11B"}, {"\uD11C", "\uD11C"}, {"\uD11D", "\uD11D"}, {"\uD11E", "\uD11E"}, {"\uD11F", "\uD11F"}, {"\uD120", "\uD120"}, {"\uD121", "\uD121"}, {"\uD122", "\uD122"}, {"\uD123", "\uD123"}, {"\uD124", "\uD124"}, {"\uD125", "\uD125"}, {"\uD126", "\uD126"}, {"\uD127", "\uD127"}, {"\uD128", "\uD128"}, {"\uD129", "\uD129"}, {"\uD12A", "\uD12A"}, {"\uD12B", "\uD12B"}, {"\uD12C", "\uD12C"}, {"\uD12D", "\uD12D"}, {"\uD12E", "\uD12E"}, {"\uD12F", "\uD12F"}, {"\uD130", "\uD130"}, {"\uD131", "\uD131"}, {"\uD132", "\uD132"}, {"\uD133", "\uD133"}, {"\uD134", "\uD134"}, {"\uD135", "\uD135"}, {"\uD136", "\uD136"}, {"\uD137", "\uD137"}, {"\uD138", "\uD138"}, {"\uD139", "\uD139"}, {"\uD13A", "\uD13A"}, {"\uD13B", "\uD13B"}, {"\uD13C", "\uD13C"}, {"\uD13D", "\uD13D"}, {"\uD13E", "\uD13E"}, {"\uD13F", "\uD13F"}, {"\uD140", "\uD140"}, {"\uD141", "\uD141"}, {"\uD142", "\uD142"}, {"\uD143", "\uD143"}, {"\uD144", "\uD144"}, {"\uD145", "\uD145"}, {"\uD146", "\uD146"}, {"\uD147", "\uD147"}, {"\uD148", "\uD148"}, {"\uD149", "\uD149"}, {"\uD14A", "\uD14A"}, {"\uD14B", "\uD14B"}, {"\uD14C", "\uD14C"}, {"\uD14D", "\uD14D"}, {"\uD14E", "\uD14E"}, {"\uD14F", "\uD14F"}, {"\uD150", "\uD150"}, {"\uD151", "\uD151"}, {"\uD152", "\uD152"}, {"\uD153", "\uD153"}, {"\uD154", "\uD154"}, {"\uD155", "\uD155"}, {"\uD156", "\uD156"}, {"\uD157", "\uD157"}, {"\uD158", "\uD158"}, {"\uD159", "\uD159"}, {"\uD15A", "\uD15A"}, {"\uD15B", "\uD15B"}, {"\uD15C", "\uD15C"}, {"\uD15D", "\uD15D"}, {"\uD15E", "\uD15E"}, {"\uD15F", "\uD15F"}, {"\uD160", "\uD160"}, {"\uD161", "\uD161"}, {"\uD162", "\uD162"}, {"\uD163", "\uD163"}, {"\uD164", "\uD164"}, {"\uD165", "\uD165"}, {"\uD166", "\uD166"}, {"\uD167", "\uD167"}, {"\uD168", "\uD168"}, {"\uD169", "\uD169"}, {"\uD16A", "\uD16A"}, {"\uD16B", "\uD16B"}, {"\uD16C", "\uD16C"}, {"\uD16D", "\uD16D"}, {"\uD16E", "\uD16E"}, {"\uD16F", "\uD16F"}, {"\uD170", "\uD170"}, {"\uD171", "\uD171"}, {"\uD172", "\uD172"}, {"\uD173", "\uD173"}, {"\uD174", "\uD174"}, {"\uD175", "\uD175"}, {"\uD176", "\uD176"}, {"\uD177", "\uD177"}, {"\uD178", "\uD178"}, {"\uD179", "\uD179"}, {"\uD17A", "\uD17A"}, {"\uD17B", "\uD17B"}, {"\uD17C", "\uD17C"}, {"\uD17D", "\uD17D"}, {"\uD17E", "\uD17E"}, {"\uD17F", "\uD17F"}, {"\uD180", "\uD180"}, {"\uD181", "\uD181"}, {"\uD182", "\uD182"}, {"\uD183", "\uD183"}, {"\uD184", "\uD184"}, {"\uD185", "\uD185"}, {"\uD186", "\uD186"}, {"\uD187", "\uD187"}, {"\uD188", "\uD188"}, {"\uD189", "\uD189"}, {"\uD18A", "\uD18A"}, {"\uD18B", "\uD18B"}, {"\uD18C", "\uD18C"}, {"\uD18D", "\uD18D"}, {"\uD18E", "\uD18E"}, {"\uD18F", "\uD18F"}, {"\uD190", "\uD190"}, {"\uD191", "\uD191"}, {"\uD192", "\uD192"}, {"\uD193", "\uD193"}, {"\uD194", "\uD194"}, {"\uD195", "\uD195"}, {"\uD196", "\uD196"}, {"\uD197", "\uD197"}, {"\uD198", "\uD198"}, {"\uD199", "\uD199"}, {"\uD19A", "\uD19A"}, {"\uD19B", "\uD19B"}, {"\uD19C", "\uD19C"}, {"\uD19D", "\uD19D"}, {"\uD19E", "\uD19E"}, {"\uD19F", "\uD19F"}, {"\uD1A0", "\uD1A0"}, {"\uD1A1", "\uD1A1"}, {"\uD1A2", "\uD1A2"}, {"\uD1A3", "\uD1A3"}, {"\uD1A4", "\uD1A4"}, {"\uD1A5", "\uD1A5"}, {"\uD1A6", "\uD1A6"}, {"\uD1A7", "\uD1A7"}, {"\uD1A8", "\uD1A8"}, {"\uD1A9", "\uD1A9"}, {"\uD1AA", "\uD1AA"}, {"\uD1AB", "\uD1AB"}, {"\uD1AC", "\uD1AC"}, {"\uD1AD", "\uD1AD"}, {"\uD1AE", "\uD1AE"}, {"\uD1AF", "\uD1AF"}, {"\uD1B0", "\uD1B0"}, {"\uD1B1", "\uD1B1"}, {"\uD1B2", "\uD1B2"}, {"\uD1B3", "\uD1B3"}, {"\uD1B4", "\uD1B4"}, {"\uD1B5", "\uD1B5"}, {"\uD1B6", "\uD1B6"}, {"\uD1B7", "\uD1B7"}, {"\uD1B8", "\uD1B8"}, {"\uD1B9", "\uD1B9"}, {"\uD1BA", "\uD1BA"}, {"\uD1BB", "\uD1BB"}, {"\uD1BC", "\uD1BC"}, {"\uD1BD", "\uD1BD"}, {"\uD1BE", "\uD1BE"}, {"\uD1BF", "\uD1BF"}, {"\uD1C0", "\uD1C0"}, {"\uD1C1", "\uD1C1"}, {"\uD1C2", "\uD1C2"}, {"\uD1C3", "\uD1C3"}, {"\uD1C4", "\uD1C4"}, {"\uD1C5", "\uD1C5"}, {"\uD1C6", "\uD1C6"}, {"\uD1C7", "\uD1C7"}, {"\uD1C8", "\uD1C8"}, {"\uD1C9", "\uD1C9"}, {"\uD1CA", "\uD1CA"}, {"\uD1CB", "\uD1CB"}, {"\uD1CC", "\uD1CC"}, {"\uD1CD", "\uD1CD"}, {"\uD1CE", "\uD1CE"}, {"\uD1CF", "\uD1CF"}, {"\uD1D0", "\uD1D0"}, {"\uD1D1", "\uD1D1"}, {"\uD1D2", "\uD1D2"}, {"\uD1D3", "\uD1D3"}, {"\uD1D4", "\uD1D4"}, {"\uD1D5", "\uD1D5"}, {"\uD1D6", "\uD1D6"}, {"\uD1D7", "\uD1D7"}, {"\uD1D8", "\uD1D8"}, {"\uD1D9", "\uD1D9"}, {"\uD1DA", "\uD1DA"}, {"\uD1DB", "\uD1DB"}, {"\uD1DC", "\uD1DC"}, {"\uD1DD", "\uD1DD"}, {"\uD1DE", "\uD1DE"}, {"\uD1DF", "\uD1DF"}, {"\uD1E0", "\uD1E0"}, {"\uD1E1", "\uD1E1"}, {"\uD1E2", "\uD1E2"}, {"\uD1E3", "\uD1E3"}, {"\uD1E4", "\uD1E4"}, {"\uD1E5", "\uD1E5"}, {"\uD1E6", "\uD1E6"}, {"\uD1E7", "\uD1E7"}, {"\uD1E8", "\uD1E8"}, {"\uD1E9", "\uD1E9"}, {"\uD1EA", "\uD1EA"}, {"\uD1EB", "\uD1EB"}, {"\uD1EC", "\uD1EC"}, {"\uD1ED", "\uD1ED"}, {"\uD1EE", "\uD1EE"}, {"\uD1EF", "\uD1EF"}, {"\uD1F0", "\uD1F0"}, {"\uD1F1", "\uD1F1"}, {"\uD1F2", "\uD1F2"}, {"\uD1F3", "\uD1F3"}, {"\uD1F4", "\uD1F4"}, {"\uD1F5", "\uD1F5"}, {"\uD1F6", "\uD1F6"}, {"\uD1F7", "\uD1F7"}, {"\uD1F8", "\uD1F8"}, {"\uD1F9", "\uD1F9"}, {"\uD1FA", "\uD1FA"}, {"\uD1FB", "\uD1FB"}, {"\uD1FC", "\uD1FC"}, {"\uD1FD", "\uD1FD"}, {"\uD1FE", "\uD1FE"}, {"\uD1FF", "\uD1FF"}, {"\uD200", "\uD200"}, {"\uD201", "\uD201"}, {"\uD202", "\uD202"}, {"\uD203", "\uD203"}, {"\uD204", "\uD204"}, {"\uD205", "\uD205"}, {"\uD206", "\uD206"}, {"\uD207", "\uD207"}, {"\uD208", "\uD208"}, {"\uD209", "\uD209"}, {"\uD20A", "\uD20A"}, {"\uD20B", "\uD20B"}, {"\uD20C", "\uD20C"}, {"\uD20D", "\uD20D"}, {"\uD20E", "\uD20E"}, {"\uD20F", "\uD20F"}, {"\uD210", "\uD210"}, {"\uD211", "\uD211"}, {"\uD212", "\uD212"}, {"\uD213", "\uD213"}, {"\uD214", "\uD214"}, {"\uD215", "\uD215"}, {"\uD216", "\uD216"}, {"\uD217", "\uD217"}, {"\uD218", "\uD218"}, {"\uD219", "\uD219"}, {"\uD21A", "\uD21A"}, {"\uD21B", "\uD21B"}, {"\uD21C", "\uD21C"}, {"\uD21D", "\uD21D"}, {"\uD21E", "\uD21E"}, {"\uD21F", "\uD21F"}, {"\uD220", "\uD220"}, {"\uD221", "\uD221"}, {"\uD222", "\uD222"}, {"\uD223", "\uD223"}, {"\uD224", "\uD224"}, {"\uD225", "\uD225"}, {"\uD226", "\uD226"}, {"\uD227", "\uD227"}, {"\uD228", "\uD228"}, {"\uD229", "\uD229"}, {"\uD22A", "\uD22A"}, {"\uD22B", "\uD22B"}, {"\uD22C", "\uD22C"}, {"\uD22D", "\uD22D"}, {"\uD22E", "\uD22E"}, {"\uD22F", "\uD22F"}, {"\uD230", "\uD230"}, {"\uD231", "\uD231"}, {"\uD232", "\uD232"}, {"\uD233", "\uD233"}, {"\uD234", "\uD234"}, {"\uD235", "\uD235"}, {"\uD236", "\uD236"}, {"\uD237", "\uD237"}, {"\uD238", "\uD238"}, {"\uD239", "\uD239"}, {"\uD23A", "\uD23A"}, {"\uD23B", "\uD23B"}, {"\uD23C", "\uD23C"}, {"\uD23D", "\uD23D"}, {"\uD23E", "\uD23E"}, {"\uD23F", "\uD23F"}, {"\uD240", "\uD240"}, {"\uD241", "\uD241"}, {"\uD242", "\uD242"}, {"\uD243", "\uD243"}, {"\uD244", "\uD244"}, {"\uD245", "\uD245"}, {"\uD246", "\uD246"}, {"\uD247", "\uD247"}, {"\uD248", "\uD248"}, {"\uD249", "\uD249"}, {"\uD24A", "\uD24A"}, {"\uD24B", "\uD24B"}, {"\uD24C", "\uD24C"}, {"\uD24D", "\uD24D"}, {"\uD24E", "\uD24E"}, {"\uD24F", "\uD24F"}, {"\uD250", "\uD250"}, {"\uD251", "\uD251"}, {"\uD252", "\uD252"}, {"\uD253", "\uD253"}, {"\uD254", "\uD254"}, {"\uD255", "\uD255"}, {"\uD256", "\uD256"}, {"\uD257", "\uD257"}, {"\uD258", "\uD258"}, {"\uD259", "\uD259"}, {"\uD25A", "\uD25A"}, {"\uD25B", "\uD25B"}, {"\uD25C", "\uD25C"}, {"\uD25D", "\uD25D"}, {"\uD25E", "\uD25E"}, {"\uD25F", "\uD25F"}, {"\uD260", "\uD260"}, {"\uD261", "\uD261"}, {"\uD262", "\uD262"}, {"\uD263", "\uD263"}, {"\uD264", "\uD264"}, {"\uD265", "\uD265"}, {"\uD266", "\uD266"}, {"\uD267", "\uD267"}, {"\uD268", "\uD268"}, {"\uD269", "\uD269"}, {"\uD26A", "\uD26A"}, {"\uD26B", "\uD26B"}, {"\uD26C", "\uD26C"}, {"\uD26D", "\uD26D"}, {"\uD26E", "\uD26E"}, {"\uD26F", "\uD26F"}, {"\uD270", "\uD270"}, {"\uD271", "\uD271"}, {"\uD272", "\uD272"}, {"\uD273", "\uD273"}, {"\uD274", "\uD274"}, {"\uD275", "\uD275"}, {"\uD276", "\uD276"}, {"\uD277", "\uD277"}, {"\uD278", "\uD278"}, {"\uD279", "\uD279"}, {"\uD27A", "\uD27A"}, {"\uD27B", "\uD27B"}, {"\uD27C", "\uD27C"}, {"\uD27D", "\uD27D"}, {"\uD27E", "\uD27E"}, {"\uD27F", "\uD27F"}, {"\uD280", "\uD280"}, {"\uD281", "\uD281"}, {"\uD282", "\uD282"}, {"\uD283", "\uD283"}, {"\uD284", "\uD284"}, {"\uD285", "\uD285"}, {"\uD286", "\uD286"}, {"\uD287", "\uD287"}, {"\uD288", "\uD288"}, {"\uD289", "\uD289"}, {"\uD28A", "\uD28A"}, {"\uD28B", "\uD28B"}, {"\uD28C", "\uD28C"}, {"\uD28D", "\uD28D"}, {"\uD28E", "\uD28E"}, {"\uD28F", "\uD28F"}, {"\uD290", "\uD290"}, {"\uD291", "\uD291"}, {"\uD292", "\uD292"}, {"\uD293", "\uD293"}, {"\uD294", "\uD294"}, {"\uD295", "\uD295"}, {"\uD296", "\uD296"}, {"\uD297", "\uD297"}, {"\uD298", "\uD298"}, {"\uD299", "\uD299"}, {"\uD29A", "\uD29A"}, {"\uD29B", "\uD29B"}, {"\uD29C", "\uD29C"}, {"\uD29D", "\uD29D"}, {"\uD29E", "\uD29E"}, {"\uD29F", "\uD29F"}, {"\uD2A0", "\uD2A0"}, {"\uD2A1", "\uD2A1"}, {"\uD2A2", "\uD2A2"}, {"\uD2A3", "\uD2A3"}, {"\uD2A4", "\uD2A4"}, {"\uD2A5", "\uD2A5"}, {"\uD2A6", "\uD2A6"}, {"\uD2A7", "\uD2A7"}, {"\uD2A8", "\uD2A8"}, {"\uD2A9", "\uD2A9"}, {"\uD2AA", "\uD2AA"}, {"\uD2AB", "\uD2AB"}, {"\uD2AC", "\uD2AC"}, {"\uD2AD", "\uD2AD"}, {"\uD2AE", "\uD2AE"}, {"\uD2AF", "\uD2AF"}, {"\uD2B0", "\uD2B0"}, {"\uD2B1", "\uD2B1"}, {"\uD2B2", "\uD2B2"}, {"\uD2B3", "\uD2B3"}, {"\uD2B4", "\uD2B4"}, {"\uD2B5", "\uD2B5"}, {"\uD2B6", "\uD2B6"}, {"\uD2B7", "\uD2B7"}, {"\uD2B8", "\uD2B8"}, {"\uD2B9", "\uD2B9"}, {"\uD2BA", "\uD2BA"}, {"\uD2BB", "\uD2BB"}, {"\uD2BC", "\uD2BC"}, {"\uD2BD", "\uD2BD"}, {"\uD2BE", "\uD2BE"}, {"\uD2BF", "\uD2BF"}, {"\uD2C0", "\uD2C0"}, {"\uD2C1", "\uD2C1"}, {"\uD2C2", "\uD2C2"}, {"\uD2C3", "\uD2C3"}, {"\uD2C4", "\uD2C4"}, {"\uD2C5", "\uD2C5"}, {"\uD2C6", "\uD2C6"}, {"\uD2C7", "\uD2C7"}, {"\uD2C8", "\uD2C8"}, {"\uD2C9", "\uD2C9"}, {"\uD2CA", "\uD2CA"}, {"\uD2CB", "\uD2CB"}, {"\uD2CC", "\uD2CC"}, {"\uD2CD", "\uD2CD"}, {"\uD2CE", "\uD2CE"}, {"\uD2CF", "\uD2CF"}, {"\uD2D0", "\uD2D0"}, {"\uD2D1", "\uD2D1"}, {"\uD2D2", "\uD2D2"}, {"\uD2D3", "\uD2D3"}, {"\uD2D4", "\uD2D4"}, {"\uD2D5", "\uD2D5"}, {"\uD2D6", "\uD2D6"}, {"\uD2D7", "\uD2D7"}, {"\uD2D8", "\uD2D8"}, {"\uD2D9", "\uD2D9"}, {"\uD2DA", "\uD2DA"}, {"\uD2DB", "\uD2DB"}, {"\uD2DC", "\uD2DC"}, {"\uD2DD", "\uD2DD"}, {"\uD2DE", "\uD2DE"}, {"\uD2DF", "\uD2DF"}, {"\uD2E0", "\uD2E0"}, {"\uD2E1", "\uD2E1"}, {"\uD2E2", "\uD2E2"}, {"\uD2E3", "\uD2E3"}, {"\uD2E4", "\uD2E4"}, {"\uD2E5", "\uD2E5"}, {"\uD2E6", "\uD2E6"}, {"\uD2E7", "\uD2E7"}, {"\uD2E8", "\uD2E8"}, {"\uD2E9", "\uD2E9"}, {"\uD2EA", "\uD2EA"}, {"\uD2EB", "\uD2EB"}, {"\uD2EC", "\uD2EC"}, {"\uD2ED", "\uD2ED"}, {"\uD2EE", "\uD2EE"}, {"\uD2EF", "\uD2EF"}, {"\uD2F0", "\uD2F0"}, {"\uD2F1", "\uD2F1"}, {"\uD2F2", "\uD2F2"}, {"\uD2F3", "\uD2F3"}, {"\uD2F4", "\uD2F4"}, {"\uD2F5", "\uD2F5"}, {"\uD2F6", "\uD2F6"}, {"\uD2F7", "\uD2F7"}, {"\uD2F8", "\uD2F8"}, {"\uD2F9", "\uD2F9"}, {"\uD2FA", "\uD2FA"}, {"\uD2FB", "\uD2FB"}, {"\uD2FC", "\uD2FC"}, {"\uD2FD", "\uD2FD"}, {"\uD2FE", "\uD2FE"}, {"\uD2FF", "\uD2FF"}, {"\uD300", "\uD300"}, {"\uD301", "\uD301"}, {"\uD302", "\uD302"}, {"\uD303", "\uD303"}, {"\uD304", "\uD304"}, {"\uD305", "\uD305"}, {"\uD306", "\uD306"}, {"\uD307", "\uD307"}, {"\uD308", "\uD308"}, {"\uD309", "\uD309"}, {"\uD30A", "\uD30A"}, {"\uD30B", "\uD30B"}, {"\uD30C", "\uD30C"}, {"\uD30D", "\uD30D"}, {"\uD30E", "\uD30E"}, {"\uD30F", "\uD30F"}, {"\uD310", "\uD310"}, {"\uD311", "\uD311"}, {"\uD312", "\uD312"}, {"\uD313", "\uD313"}, {"\uD314", "\uD314"}, {"\uD315", "\uD315"}, {"\uD316", "\uD316"}, {"\uD317", "\uD317"}, {"\uD318", "\uD318"}, {"\uD319", "\uD319"}, {"\uD31A", "\uD31A"}, {"\uD31B", "\uD31B"}, {"\uD31C", "\uD31C"}, {"\uD31D", "\uD31D"}, {"\uD31E", "\uD31E"}, {"\uD31F", "\uD31F"}, {"\uD320", "\uD320"}, {"\uD321", "\uD321"}, {"\uD322", "\uD322"}, {"\uD323", "\uD323"}, {"\uD324", "\uD324"}, {"\uD325", "\uD325"}, {"\uD326", "\uD326"}, {"\uD327", "\uD327"}, {"\uD328", "\uD328"}, {"\uD329", "\uD329"}, {"\uD32A", "\uD32A"}, {"\uD32B", "\uD32B"}, {"\uD32C", "\uD32C"}, {"\uD32D", "\uD32D"}, {"\uD32E", "\uD32E"}, {"\uD32F", "\uD32F"}, {"\uD330", "\uD330"}, {"\uD331", "\uD331"}, {"\uD332", "\uD332"}, {"\uD333", "\uD333"}, {"\uD334", "\uD334"}, {"\uD335", "\uD335"}, {"\uD336", "\uD336"}, {"\uD337", "\uD337"}, {"\uD338", "\uD338"}, {"\uD339", "\uD339"}, {"\uD33A", "\uD33A"}, {"\uD33B", "\uD33B"}, {"\uD33C", "\uD33C"}, {"\uD33D", "\uD33D"}, {"\uD33E", "\uD33E"}, {"\uD33F", "\uD33F"}, {"\uD340", "\uD340"}, {"\uD341", "\uD341"}, {"\uD342", "\uD342"}, {"\uD343", "\uD343"}, {"\uD344", "\uD344"}, {"\uD345", "\uD345"}, {"\uD346", "\uD346"}, {"\uD347", "\uD347"}, {"\uD348", "\uD348"}, {"\uD349", "\uD349"}, {"\uD34A", "\uD34A"}, {"\uD34B", "\uD34B"}, {"\uD34C", "\uD34C"}, {"\uD34D", "\uD34D"}, {"\uD34E", "\uD34E"}, {"\uD34F", "\uD34F"}, {"\uD350", "\uD350"}, {"\uD351", "\uD351"}, {"\uD352", "\uD352"}, {"\uD353", "\uD353"}, {"\uD354", "\uD354"}, {"\uD355", "\uD355"}, {"\uD356", "\uD356"}, {"\uD357", "\uD357"}, {"\uD358", "\uD358"}, {"\uD359", "\uD359"}, {"\uD35A", "\uD35A"}, {"\uD35B", "\uD35B"}, {"\uD35C", "\uD35C"}, {"\uD35D", "\uD35D"}, {"\uD35E", "\uD35E"}, {"\uD35F", "\uD35F"}, {"\uD360", "\uD360"}, {"\uD361", "\uD361"}, {"\uD362", "\uD362"}, {"\uD363", "\uD363"}, {"\uD364", "\uD364"}, {"\uD365", "\uD365"}, {"\uD366", "\uD366"}, {"\uD367", "\uD367"}, {"\uD368", "\uD368"}, {"\uD369", "\uD369"}, {"\uD36A", "\uD36A"}, {"\uD36B", "\uD36B"}, {"\uD36C", "\uD36C"}, {"\uD36D", "\uD36D"}, {"\uD36E", "\uD36E"}, {"\uD36F", "\uD36F"}, {"\uD370", "\uD370"}, {"\uD371", "\uD371"}, {"\uD372", "\uD372"}, {"\uD373", "\uD373"}, {"\uD374", "\uD374"}, {"\uD375", "\uD375"}, {"\uD376", "\uD376"}, {"\uD377", "\uD377"}, {"\uD378", "\uD378"}, {"\uD379", "\uD379"}, {"\uD37A", "\uD37A"}, {"\uD37B", "\uD37B"}, {"\uD37C", "\uD37C"}, {"\uD37D", "\uD37D"}, {"\uD37E", "\uD37E"}, {"\uD37F", "\uD37F"}, {"\uD380", "\uD380"}, {"\uD381", "\uD381"}, {"\uD382", "\uD382"}, {"\uD383", "\uD383"}, {"\uD384", "\uD384"}, {"\uD385", "\uD385"}, {"\uD386", "\uD386"}, {"\uD387", "\uD387"}, {"\uD388", "\uD388"}, {"\uD389", "\uD389"}, {"\uD38A", "\uD38A"}, {"\uD38B", "\uD38B"}, {"\uD38C", "\uD38C"}, {"\uD38D", "\uD38D"}, {"\uD38E", "\uD38E"}, {"\uD38F", "\uD38F"}, {"\uD390", "\uD390"}, {"\uD391", "\uD391"}, {"\uD392", "\uD392"}, {"\uD393", "\uD393"}, {"\uD394", "\uD394"}, {"\uD395", "\uD395"}, {"\uD396", "\uD396"}, {"\uD397", "\uD397"}, {"\uD398", "\uD398"}, {"\uD399", "\uD399"}, {"\uD39A", "\uD39A"}, {"\uD39B", "\uD39B"}, {"\uD39C", "\uD39C"}, {"\uD39D", "\uD39D"}, {"\uD39E", "\uD39E"}, {"\uD39F", "\uD39F"}, {"\uD3A0", "\uD3A0"}, {"\uD3A1", "\uD3A1"}, {"\uD3A2", "\uD3A2"}, {"\uD3A3", "\uD3A3"}, {"\uD3A4", "\uD3A4"}, {"\uD3A5", "\uD3A5"}, {"\uD3A6", "\uD3A6"}, {"\uD3A7", "\uD3A7"}, {"\uD3A8", "\uD3A8"}, {"\uD3A9", "\uD3A9"}, {"\uD3AA", "\uD3AA"}, {"\uD3AB", "\uD3AB"}, {"\uD3AC", "\uD3AC"}, {"\uD3AD", "\uD3AD"}, {"\uD3AE", "\uD3AE"}, {"\uD3AF", "\uD3AF"}, {"\uD3B0", "\uD3B0"}, {"\uD3B1", "\uD3B1"}, {"\uD3B2", "\uD3B2"}, {"\uD3B3", "\uD3B3"}, {"\uD3B4", "\uD3B4"}, {"\uD3B5", "\uD3B5"}, {"\uD3B6", "\uD3B6"}, {"\uD3B7", "\uD3B7"}, {"\uD3B8", "\uD3B8"}, {"\uD3B9", "\uD3B9"}, {"\uD3BA", "\uD3BA"}, {"\uD3BB", "\uD3BB"}, {"\uD3BC", "\uD3BC"}, {"\uD3BD", "\uD3BD"}, {"\uD3BE", "\uD3BE"}, {"\uD3BF", "\uD3BF"}, {"\uD3C0", "\uD3C0"}, {"\uD3C1", "\uD3C1"}, {"\uD3C2", "\uD3C2"}, {"\uD3C3", "\uD3C3"}, {"\uD3C4", "\uD3C4"}, {"\uD3C5", "\uD3C5"}, {"\uD3C6", "\uD3C6"}, {"\uD3C7", "\uD3C7"}, {"\uD3C8", "\uD3C8"}, {"\uD3C9", "\uD3C9"}, {"\uD3CA", "\uD3CA"}, {"\uD3CB", "\uD3CB"}, {"\uD3CC", "\uD3CC"}, {"\uD3CD", "\uD3CD"}, {"\uD3CE", "\uD3CE"}, {"\uD3CF", "\uD3CF"}, {"\uD3D0", "\uD3D0"}, {"\uD3D1", "\uD3D1"}, {"\uD3D2", "\uD3D2"}, {"\uD3D3", "\uD3D3"}, {"\uD3D4", "\uD3D4"}, {"\uD3D5", "\uD3D5"}, {"\uD3D6", "\uD3D6"}, {"\uD3D7", "\uD3D7"}, {"\uD3D8", "\uD3D8"}, {"\uD3D9", "\uD3D9"}, {"\uD3DA", "\uD3DA"}, {"\uD3DB", "\uD3DB"}, {"\uD3DC", "\uD3DC"}, {"\uD3DD", "\uD3DD"}, {"\uD3DE", "\uD3DE"}, {"\uD3DF", "\uD3DF"}, {"\uD3E0", "\uD3E0"}, {"\uD3E1", "\uD3E1"}, {"\uD3E2", "\uD3E2"}, {"\uD3E3", "\uD3E3"}, {"\uD3E4", "\uD3E4"}, {"\uD3E5", "\uD3E5"}, {"\uD3E6", "\uD3E6"}, {"\uD3E7", "\uD3E7"}, {"\uD3E8", "\uD3E8"}, {"\uD3E9", "\uD3E9"}, {"\uD3EA", "\uD3EA"}, {"\uD3EB", "\uD3EB"}, {"\uD3EC", "\uD3EC"}, {"\uD3ED", "\uD3ED"}, {"\uD3EE", "\uD3EE"}, {"\uD3EF", "\uD3EF"}, {"\uD3F0", "\uD3F0"}, {"\uD3F1", "\uD3F1"}, {"\uD3F2", "\uD3F2"}, {"\uD3F3", "\uD3F3"}, {"\uD3F4", "\uD3F4"}, {"\uD3F5", "\uD3F5"}, {"\uD3F6", "\uD3F6"}, {"\uD3F7", "\uD3F7"}, {"\uD3F8", "\uD3F8"}, {"\uD3F9", "\uD3F9"}, {"\uD3FA", "\uD3FA"}, {"\uD3FB", "\uD3FB"}, {"\uD3FC", "\uD3FC"}, {"\uD3FD", "\uD3FD"}, {"\uD3FE", "\uD3FE"}, {"\uD3FF", "\uD3FF"}, {"\uD400", "\uD400"}, {"\uD401", "\uD401"}, {"\uD402", "\uD402"}, {"\uD403", "\uD403"}, {"\uD404", "\uD404"}, {"\uD405", "\uD405"}, {"\uD406", "\uD406"}, {"\uD407", "\uD407"}, {"\uD408", "\uD408"}, {"\uD409", "\uD409"}, {"\uD40A", "\uD40A"}, {"\uD40B", "\uD40B"}, {"\uD40C", "\uD40C"}, {"\uD40D", "\uD40D"}, {"\uD40E", "\uD40E"}, {"\uD40F", "\uD40F"}, {"\uD410", "\uD410"}, {"\uD411", "\uD411"}, {"\uD412", "\uD412"}, {"\uD413", "\uD413"}, {"\uD414", "\uD414"}, {"\uD415", "\uD415"}, {"\uD416", "\uD416"}, {"\uD417", "\uD417"}, {"\uD418", "\uD418"}, {"\uD419", "\uD419"}, {"\uD41A", "\uD41A"}, {"\uD41B", "\uD41B"}, {"\uD41C", "\uD41C"}, {"\uD41D", "\uD41D"}, {"\uD41E", "\uD41E"}, {"\uD41F", "\uD41F"}, {"\uD420", "\uD420"}, {"\uD421", "\uD421"}, {"\uD422", "\uD422"}, {"\uD423", "\uD423"}, {"\uD424", "\uD424"}, {"\uD425", "\uD425"}, {"\uD426", "\uD426"}, {"\uD427", "\uD427"}, {"\uD428", "\uD428"}, {"\uD429", "\uD429"}, {"\uD42A", "\uD42A"}, {"\uD42B", "\uD42B"}, {"\uD42C", "\uD42C"}, {"\uD42D", "\uD42D"}, {"\uD42E", "\uD42E"}, {"\uD42F", "\uD42F"}, {"\uD430", "\uD430"}, {"\uD431", "\uD431"}, {"\uD432", "\uD432"}, {"\uD433", "\uD433"}, {"\uD434", "\uD434"}, {"\uD435", "\uD435"}, {"\uD436", "\uD436"}, {"\uD437", "\uD437"}, {"\uD438", "\uD438"}, {"\uD439", "\uD439"}, {"\uD43A", "\uD43A"}, {"\uD43B", "\uD43B"}, {"\uD43C", "\uD43C"}, {"\uD43D", "\uD43D"}, {"\uD43E", "\uD43E"}, {"\uD43F", "\uD43F"}, {"\uD440", "\uD440"}, {"\uD441", "\uD441"}, {"\uD442", "\uD442"}, {"\uD443", "\uD443"}, {"\uD444", "\uD444"}, {"\uD445", "\uD445"}, {"\uD446", "\uD446"}, {"\uD447", "\uD447"}, {"\uD448", "\uD448"}, {"\uD449", "\uD449"}, {"\uD44A", "\uD44A"}, {"\uD44B", "\uD44B"}, {"\uD44C", "\uD44C"}, {"\uD44D", "\uD44D"}, {"\uD44E", "\uD44E"}, {"\uD44F", "\uD44F"}, {"\uD450", "\uD450"}, {"\uD451", "\uD451"}, {"\uD452", "\uD452"}, {"\uD453", "\uD453"}, {"\uD454", "\uD454"}, {"\uD455", "\uD455"}, {"\uD456", "\uD456"}, {"\uD457", "\uD457"}, {"\uD458", "\uD458"}, {"\uD459", "\uD459"}, {"\uD45A", "\uD45A"}, {"\uD45B", "\uD45B"}, {"\uD45C", "\uD45C"}, {"\uD45D", "\uD45D"}, {"\uD45E", "\uD45E"}, {"\uD45F", "\uD45F"}, {"\uD460", "\uD460"}, {"\uD461", "\uD461"}, {"\uD462", "\uD462"}, {"\uD463", "\uD463"}, {"\uD464", "\uD464"}, {"\uD465", "\uD465"}, {"\uD466", "\uD466"}, {"\uD467", "\uD467"}, {"\uD468", "\uD468"}, {"\uD469", "\uD469"}, {"\uD46A", "\uD46A"}, {"\uD46B", "\uD46B"}, {"\uD46C", "\uD46C"}, {"\uD46D", "\uD46D"}, {"\uD46E", "\uD46E"}, {"\uD46F", "\uD46F"}, {"\uD470", "\uD470"}, {"\uD471", "\uD471"}, {"\uD472", "\uD472"}, {"\uD473", "\uD473"}, {"\uD474", "\uD474"}, {"\uD475", "\uD475"}, {"\uD476", "\uD476"}, {"\uD477", "\uD477"}, {"\uD478", "\uD478"}, {"\uD479", "\uD479"}, {"\uD47A", "\uD47A"}, {"\uD47B", "\uD47B"}, {"\uD47C", "\uD47C"}, {"\uD47D", "\uD47D"}, {"\uD47E", "\uD47E"}, {"\uD47F", "\uD47F"}, {"\uD480", "\uD480"}, {"\uD481", "\uD481"}, {"\uD482", "\uD482"}, {"\uD483", "\uD483"}, {"\uD484", "\uD484"}, {"\uD485", "\uD485"}, {"\uD486", "\uD486"}, {"\uD487", "\uD487"}, {"\uD488", "\uD488"}, {"\uD489", "\uD489"}, {"\uD48A", "\uD48A"}, {"\uD48B", "\uD48B"}, {"\uD48C", "\uD48C"}, {"\uD48D", "\uD48D"}, {"\uD48E", "\uD48E"}, {"\uD48F", "\uD48F"}, {"\uD490", "\uD490"}, {"\uD491", "\uD491"}, {"\uD492", "\uD492"}, {"\uD493", "\uD493"}, {"\uD494", "\uD494"}, {"\uD495", "\uD495"}, {"\uD496", "\uD496"}, {"\uD497", "\uD497"}, {"\uD498", "\uD498"}, {"\uD499", "\uD499"}, {"\uD49A", "\uD49A"}, {"\uD49B", "\uD49B"}, {"\uD49C", "\uD49C"}, {"\uD49D", "\uD49D"}, {"\uD49E", "\uD49E"}, {"\uD49F", "\uD49F"}, {"\uD4A0", "\uD4A0"}, {"\uD4A1", "\uD4A1"}, {"\uD4A2", "\uD4A2"}, {"\uD4A3", "\uD4A3"}, {"\uD4A4", "\uD4A4"}, {"\uD4A5", "\uD4A5"}, {"\uD4A6", "\uD4A6"}, {"\uD4A7", "\uD4A7"}, {"\uD4A8", "\uD4A8"}, {"\uD4A9", "\uD4A9"}, {"\uD4AA", "\uD4AA"}, {"\uD4AB", "\uD4AB"}, {"\uD4AC", "\uD4AC"}, {"\uD4AD", "\uD4AD"}, {"\uD4AE", "\uD4AE"}, {"\uD4AF", "\uD4AF"}, {"\uD4B0", "\uD4B0"}, {"\uD4B1", "\uD4B1"}, {"\uD4B2", "\uD4B2"}, {"\uD4B3", "\uD4B3"}, {"\uD4B4", "\uD4B4"}, {"\uD4B5", "\uD4B5"}, {"\uD4B6", "\uD4B6"}, {"\uD4B7", "\uD4B7"}, {"\uD4B8", "\uD4B8"}, {"\uD4B9", "\uD4B9"}, {"\uD4BA", "\uD4BA"}, {"\uD4BB", "\uD4BB"}, {"\uD4BC", "\uD4BC"}, {"\uD4BD", "\uD4BD"}, {"\uD4BE", "\uD4BE"}, {"\uD4BF", "\uD4BF"}, {"\uD4C0", "\uD4C0"}, {"\uD4C1", "\uD4C1"}, {"\uD4C2", "\uD4C2"}, {"\uD4C3", "\uD4C3"}, {"\uD4C4", "\uD4C4"}, {"\uD4C5", "\uD4C5"}, {"\uD4C6", "\uD4C6"}, {"\uD4C7", "\uD4C7"}, {"\uD4C8", "\uD4C8"}, {"\uD4C9", "\uD4C9"}, {"\uD4CA", "\uD4CA"}, {"\uD4CB", "\uD4CB"}, {"\uD4CC", "\uD4CC"}, {"\uD4CD", "\uD4CD"}, {"\uD4CE", "\uD4CE"}, {"\uD4CF", "\uD4CF"}, {"\uD4D0", "\uD4D0"}, {"\uD4D1", "\uD4D1"}, {"\uD4D2", "\uD4D2"}, {"\uD4D3", "\uD4D3"}, {"\uD4D4", "\uD4D4"}, {"\uD4D5", "\uD4D5"}, {"\uD4D6", "\uD4D6"}, {"\uD4D7", "\uD4D7"}, {"\uD4D8", "\uD4D8"}, {"\uD4D9", "\uD4D9"}, {"\uD4DA", "\uD4DA"}, {"\uD4DB", "\uD4DB"}, {"\uD4DC", "\uD4DC"}, {"\uD4DD", "\uD4DD"}, {"\uD4DE", "\uD4DE"}, {"\uD4DF", "\uD4DF"}, {"\uD4E0", "\uD4E0"}, {"\uD4E1", "\uD4E1"}, {"\uD4E2", "\uD4E2"}, {"\uD4E3", "\uD4E3"}, {"\uD4E4", "\uD4E4"}, {"\uD4E5", "\uD4E5"}, {"\uD4E6", "\uD4E6"}, {"\uD4E7", "\uD4E7"}, {"\uD4E8", "\uD4E8"}, {"\uD4E9", "\uD4E9"}, {"\uD4EA", "\uD4EA"}, {"\uD4EB", "\uD4EB"}, {"\uD4EC", "\uD4EC"}, {"\uD4ED", "\uD4ED"}, {"\uD4EE", "\uD4EE"}, {"\uD4EF", "\uD4EF"}, {"\uD4F0", "\uD4F0"}, {"\uD4F1", "\uD4F1"}, {"\uD4F2", "\uD4F2"}, {"\uD4F3", "\uD4F3"}, {"\uD4F4", "\uD4F4"}, {"\uD4F5", "\uD4F5"}, {"\uD4F6", "\uD4F6"}, {"\uD4F7", "\uD4F7"}, {"\uD4F8", "\uD4F8"}, {"\uD4F9", "\uD4F9"}, {"\uD4FA", "\uD4FA"}, {"\uD4FB", "\uD4FB"}, {"\uD4FC", "\uD4FC"}, {"\uD4FD", "\uD4FD"}, {"\uD4FE", "\uD4FE"}, {"\uD4FF", "\uD4FF"}, {"\uD500", "\uD500"}, {"\uD501", "\uD501"}, {"\uD502", "\uD502"}, {"\uD503", "\uD503"}, {"\uD504", "\uD504"}, {"\uD505", "\uD505"}, {"\uD506", "\uD506"}, {"\uD507", "\uD507"}, {"\uD508", "\uD508"}, {"\uD509", "\uD509"}, {"\uD50A", "\uD50A"}, {"\uD50B", "\uD50B"}, {"\uD50C", "\uD50C"}, {"\uD50D", "\uD50D"}, {"\uD50E", "\uD50E"}, {"\uD50F", "\uD50F"}, {"\uD510", "\uD510"}, {"\uD511", "\uD511"}, {"\uD512", "\uD512"}, {"\uD513", "\uD513"}, {"\uD514", "\uD514"}, {"\uD515", "\uD515"}, {"\uD516", "\uD516"}, {"\uD517", "\uD517"}, {"\uD518", "\uD518"}, {"\uD519", "\uD519"}, {"\uD51A", "\uD51A"}, {"\uD51B", "\uD51B"}, {"\uD51C", "\uD51C"}, {"\uD51D", "\uD51D"}, {"\uD51E", "\uD51E"}, {"\uD51F", "\uD51F"}, {"\uD520", "\uD520"}, {"\uD521", "\uD521"}, {"\uD522", "\uD522"}, {"\uD523", "\uD523"}, {"\uD524", "\uD524"}, {"\uD525", "\uD525"}, {"\uD526", "\uD526"}, {"\uD527", "\uD527"}, {"\uD528", "\uD528"}, {"\uD529", "\uD529"}, {"\uD52A", "\uD52A"}, {"\uD52B", "\uD52B"}, {"\uD52C", "\uD52C"}, {"\uD52D", "\uD52D"}, {"\uD52E", "\uD52E"}, {"\uD52F", "\uD52F"}, {"\uD530", "\uD530"}, {"\uD531", "\uD531"}, {"\uD532", "\uD532"}, {"\uD533", "\uD533"}, {"\uD534", "\uD534"}, {"\uD535", "\uD535"}, {"\uD536", "\uD536"}, {"\uD537", "\uD537"}, {"\uD538", "\uD538"}, {"\uD539", "\uD539"}, {"\uD53A", "\uD53A"}, {"\uD53B", "\uD53B"}, {"\uD53C", "\uD53C"}, {"\uD53D", "\uD53D"}, {"\uD53E", "\uD53E"}, {"\uD53F", "\uD53F"}, {"\uD540", "\uD540"}, {"\uD541", "\uD541"}, {"\uD542", "\uD542"}, {"\uD543", "\uD543"}, {"\uD544", "\uD544"}, {"\uD545", "\uD545"}, {"\uD546", "\uD546"}, {"\uD547", "\uD547"}, {"\uD548", "\uD548"}, {"\uD549", "\uD549"}, {"\uD54A", "\uD54A"}, {"\uD54B", "\uD54B"}, {"\uD54C", "\uD54C"}, {"\uD54D", "\uD54D"}, {"\uD54E", "\uD54E"}, {"\uD54F", "\uD54F"}, {"\uD550", "\uD550"}, {"\uD551", "\uD551"}, {"\uD552", "\uD552"}, {"\uD553", "\uD553"}, {"\uD554", "\uD554"}, {"\uD555", "\uD555"}, {"\uD556", "\uD556"}, {"\uD557", "\uD557"}, {"\uD558", "\uD558"}, {"\uD559", "\uD559"}, {"\uD55A", "\uD55A"}, {"\uD55B", "\uD55B"}, {"\uD55C", "\uD55C"}, {"\uD55D", "\uD55D"}, {"\uD55E", "\uD55E"}, {"\uD55F", "\uD55F"}, {"\uD560", "\uD560"}, {"\uD561", "\uD561"}, {"\uD562", "\uD562"}, {"\uD563", "\uD563"}, {"\uD564", "\uD564"}, {"\uD565", "\uD565"}, {"\uD566", "\uD566"}, {"\uD567", "\uD567"}, {"\uD568", "\uD568"}, {"\uD569", "\uD569"}, {"\uD56A", "\uD56A"}, {"\uD56B", "\uD56B"}, {"\uD56C", "\uD56C"}, {"\uD56D", "\uD56D"}, {"\uD56E", "\uD56E"}, {"\uD56F", "\uD56F"}, {"\uD570", "\uD570"}, {"\uD571", "\uD571"}, {"\uD572", "\uD572"}, {"\uD573", "\uD573"}, {"\uD574", "\uD574"}, {"\uD575", "\uD575"}, {"\uD576", "\uD576"}, {"\uD577", "\uD577"}, {"\uD578", "\uD578"}, {"\uD579", "\uD579"}, {"\uD57A", "\uD57A"}, {"\uD57B", "\uD57B"}, {"\uD57C", "\uD57C"}, {"\uD57D", "\uD57D"}, {"\uD57E", "\uD57E"}, {"\uD57F", "\uD57F"}, {"\uD580", "\uD580"}, {"\uD581", "\uD581"}, {"\uD582", "\uD582"}, {"\uD583", "\uD583"}, {"\uD584", "\uD584"}, {"\uD585", "\uD585"}, {"\uD586", "\uD586"}, {"\uD587", "\uD587"}, {"\uD588", "\uD588"}, {"\uD589", "\uD589"}, {"\uD58A", "\uD58A"}, {"\uD58B", "\uD58B"}, {"\uD58C", "\uD58C"}, {"\uD58D", "\uD58D"}, {"\uD58E", "\uD58E"}, {"\uD58F", "\uD58F"}, {"\uD590", "\uD590"}, {"\uD591", "\uD591"}, {"\uD592", "\uD592"}, {"\uD593", "\uD593"}, {"\uD594", "\uD594"}, {"\uD595", "\uD595"}, {"\uD596", "\uD596"}, {"\uD597", "\uD597"}, {"\uD598", "\uD598"}, {"\uD599", "\uD599"}, {"\uD59A", "\uD59A"}, {"\uD59B", "\uD59B"}, {"\uD59C", "\uD59C"}, {"\uD59D", "\uD59D"}, {"\uD59E", "\uD59E"}, {"\uD59F", "\uD59F"}, {"\uD5A0", "\uD5A0"}, {"\uD5A1", "\uD5A1"}, {"\uD5A2", "\uD5A2"}, {"\uD5A3", "\uD5A3"}, {"\uD5A4", "\uD5A4"}, {"\uD5A5", "\uD5A5"}, {"\uD5A6", "\uD5A6"}, {"\uD5A7", "\uD5A7"}, {"\uD5A8", "\uD5A8"}, {"\uD5A9", "\uD5A9"}, {"\uD5AA", "\uD5AA"}, {"\uD5AB", "\uD5AB"}, {"\uD5AC", "\uD5AC"}, {"\uD5AD", "\uD5AD"}, {"\uD5AE", "\uD5AE"}, {"\uD5AF", "\uD5AF"}, {"\uD5B0", "\uD5B0"}, {"\uD5B1", "\uD5B1"}, {"\uD5B2", "\uD5B2"}, {"\uD5B3", "\uD5B3"}, {"\uD5B4", "\uD5B4"}, {"\uD5B5", "\uD5B5"}, {"\uD5B6", "\uD5B6"}, {"\uD5B7", "\uD5B7"}, {"\uD5B8", "\uD5B8"}, {"\uD5B9", "\uD5B9"}, {"\uD5BA", "\uD5BA"}, {"\uD5BB", "\uD5BB"}, {"\uD5BC", "\uD5BC"}, {"\uD5BD", "\uD5BD"}, {"\uD5BE", "\uD5BE"}, {"\uD5BF", "\uD5BF"}, {"\uD5C0", "\uD5C0"}, {"\uD5C1", "\uD5C1"}, {"\uD5C2", "\uD5C2"}, {"\uD5C3", "\uD5C3"}, {"\uD5C4", "\uD5C4"}, {"\uD5C5", "\uD5C5"}, {"\uD5C6", "\uD5C6"}, {"\uD5C7", "\uD5C7"}, {"\uD5C8", "\uD5C8"}, {"\uD5C9", "\uD5C9"}, {"\uD5CA", "\uD5CA"}, {"\uD5CB", "\uD5CB"}, {"\uD5CC", "\uD5CC"}, {"\uD5CD", "\uD5CD"}, {"\uD5CE", "\uD5CE"}, {"\uD5CF", "\uD5CF"}, {"\uD5D0", "\uD5D0"}, {"\uD5D1", "\uD5D1"}, {"\uD5D2", "\uD5D2"}, {"\uD5D3", "\uD5D3"}, {"\uD5D4", "\uD5D4"}, {"\uD5D5", "\uD5D5"}, {"\uD5D6", "\uD5D6"}, {"\uD5D7", "\uD5D7"}, {"\uD5D8", "\uD5D8"}, {"\uD5D9", "\uD5D9"}, {"\uD5DA", "\uD5DA"}, {"\uD5DB", "\uD5DB"}, {"\uD5DC", "\uD5DC"}, {"\uD5DD", "\uD5DD"}, {"\uD5DE", "\uD5DE"}, {"\uD5DF", "\uD5DF"}, {"\uD5E0", "\uD5E0"}, {"\uD5E1", "\uD5E1"}, {"\uD5E2", "\uD5E2"}, {"\uD5E3", "\uD5E3"}, {"\uD5E4", "\uD5E4"}, {"\uD5E5", "\uD5E5"}, {"\uD5E6", "\uD5E6"}, {"\uD5E7", "\uD5E7"}, {"\uD5E8", "\uD5E8"}, {"\uD5E9", "\uD5E9"}, {"\uD5EA", "\uD5EA"}, {"\uD5EB", "\uD5EB"}, {"\uD5EC", "\uD5EC"}, {"\uD5ED", "\uD5ED"}, {"\uD5EE", "\uD5EE"}, {"\uD5EF", "\uD5EF"}, {"\uD5F0", "\uD5F0"}, {"\uD5F1", "\uD5F1"}, {"\uD5F2", "\uD5F2"}, {"\uD5F3", "\uD5F3"}, {"\uD5F4", "\uD5F4"}, {"\uD5F5", "\uD5F5"}, {"\uD5F6", "\uD5F6"}, {"\uD5F7", "\uD5F7"}, {"\uD5F8", "\uD5F8"}, {"\uD5F9", "\uD5F9"}, {"\uD5FA", "\uD5FA"}, {"\uD5FB", "\uD5FB"}, {"\uD5FC", "\uD5FC"}, {"\uD5FD", "\uD5FD"}, {"\uD5FE", "\uD5FE"}, {"\uD5FF", "\uD5FF"}, {"\uD600", "\uD600"}, {"\uD601", "\uD601"}, {"\uD602", "\uD602"}, {"\uD603", "\uD603"}, {"\uD604", "\uD604"}, {"\uD605", "\uD605"}, {"\uD606", "\uD606"}, {"\uD607", "\uD607"}, {"\uD608", "\uD608"}, {"\uD609", "\uD609"}, {"\uD60A", "\uD60A"}, {"\uD60B", "\uD60B"}, {"\uD60C", "\uD60C"}, {"\uD60D", "\uD60D"}, {"\uD60E", "\uD60E"}, {"\uD60F", "\uD60F"}, {"\uD610", "\uD610"}, {"\uD611", "\uD611"}, {"\uD612", "\uD612"}, {"\uD613", "\uD613"}, {"\uD614", "\uD614"}, {"\uD615", "\uD615"}, {"\uD616", "\uD616"}, {"\uD617", "\uD617"}, {"\uD618", "\uD618"}, {"\uD619", "\uD619"}, {"\uD61A", "\uD61A"}, {"\uD61B", "\uD61B"}, {"\uD61C", "\uD61C"}, {"\uD61D", "\uD61D"}, {"\uD61E", "\uD61E"}, {"\uD61F", "\uD61F"}, {"\uD620", "\uD620"}, {"\uD621", "\uD621"}, {"\uD622", "\uD622"}, {"\uD623", "\uD623"}, {"\uD624", "\uD624"}, {"\uD625", "\uD625"}, {"\uD626", "\uD626"}, {"\uD627", "\uD627"}, {"\uD628", "\uD628"}, {"\uD629", "\uD629"}, {"\uD62A", "\uD62A"}, {"\uD62B", "\uD62B"}, {"\uD62C", "\uD62C"}, {"\uD62D", "\uD62D"}, {"\uD62E", "\uD62E"}, {"\uD62F", "\uD62F"}, {"\uD630", "\uD630"}, {"\uD631", "\uD631"}, {"\uD632", "\uD632"}, {"\uD633", "\uD633"}, {"\uD634", "\uD634"}, {"\uD635", "\uD635"}, {"\uD636", "\uD636"}, {"\uD637", "\uD637"}, {"\uD638", "\uD638"}, {"\uD639", "\uD639"}, {"\uD63A", "\uD63A"}, {"\uD63B", "\uD63B"}, {"\uD63C", "\uD63C"}, {"\uD63D", "\uD63D"}, {"\uD63E", "\uD63E"}, {"\uD63F", "\uD63F"}, {"\uD640", "\uD640"}, {"\uD641", "\uD641"}, {"\uD642", "\uD642"}, {"\uD643", "\uD643"}, {"\uD644", "\uD644"}, {"\uD645", "\uD645"}, {"\uD646", "\uD646"}, {"\uD647", "\uD647"}, {"\uD648", "\uD648"}, {"\uD649", "\uD649"}, {"\uD64A", "\uD64A"}, {"\uD64B", "\uD64B"}, {"\uD64C", "\uD64C"}, {"\uD64D", "\uD64D"}, {"\uD64E", "\uD64E"}, {"\uD64F", "\uD64F"}, {"\uD650", "\uD650"}, {"\uD651", "\uD651"}, {"\uD652", "\uD652"}, {"\uD653", "\uD653"}, {"\uD654", "\uD654"}, {"\uD655", "\uD655"}, {"\uD656", "\uD656"}, {"\uD657", "\uD657"}, {"\uD658", "\uD658"}, {"\uD659", "\uD659"}, {"\uD65A", "\uD65A"}, {"\uD65B", "\uD65B"}, {"\uD65C", "\uD65C"}, {"\uD65D", "\uD65D"}, {"\uD65E", "\uD65E"}, {"\uD65F", "\uD65F"}, {"\uD660", "\uD660"}, {"\uD661", "\uD661"}, {"\uD662", "\uD662"}, {"\uD663", "\uD663"}, {"\uD664", "\uD664"}, {"\uD665", "\uD665"}, {"\uD666", "\uD666"}, {"\uD667", "\uD667"}, {"\uD668", "\uD668"}, {"\uD669", "\uD669"}, {"\uD66A", "\uD66A"}, {"\uD66B", "\uD66B"}, {"\uD66C", "\uD66C"}, {"\uD66D", "\uD66D"}, {"\uD66E", "\uD66E"}, {"\uD66F", "\uD66F"}, {"\uD670", "\uD670"}, {"\uD671", "\uD671"}, {"\uD672", "\uD672"}, {"\uD673", "\uD673"}, {"\uD674", "\uD674"}, {"\uD675", "\uD675"}, {"\uD676", "\uD676"}, {"\uD677", "\uD677"}, {"\uD678", "\uD678"}, {"\uD679", "\uD679"}, {"\uD67A", "\uD67A"}, {"\uD67B", "\uD67B"}, {"\uD67C", "\uD67C"}, {"\uD67D", "\uD67D"}, {"\uD67E", "\uD67E"}, {"\uD67F", "\uD67F"}, {"\uD680", "\uD680"}, {"\uD681", "\uD681"}, {"\uD682", "\uD682"}, {"\uD683", "\uD683"}, {"\uD684", "\uD684"}, {"\uD685", "\uD685"}, {"\uD686", "\uD686"}, {"\uD687", "\uD687"}, {"\uD688", "\uD688"}, {"\uD689", "\uD689"}, {"\uD68A", "\uD68A"}, {"\uD68B", "\uD68B"}, {"\uD68C", "\uD68C"}, {"\uD68D", "\uD68D"}, {"\uD68E", "\uD68E"}, {"\uD68F", "\uD68F"}, {"\uD690", "\uD690"}, {"\uD691", "\uD691"}, {"\uD692", "\uD692"}, {"\uD693", "\uD693"}, {"\uD694", "\uD694"}, {"\uD695", "\uD695"}, {"\uD696", "\uD696"}, {"\uD697", "\uD697"}, {"\uD698", "\uD698"}, {"\uD699", "\uD699"}, {"\uD69A", "\uD69A"}, {"\uD69B", "\uD69B"}, {"\uD69C", "\uD69C"}, {"\uD69D", "\uD69D"}, {"\uD69E", "\uD69E"}, {"\uD69F", "\uD69F"}, {"\uD6A0", "\uD6A0"}, {"\uD6A1", "\uD6A1"}, {"\uD6A2", "\uD6A2"}, {"\uD6A3", "\uD6A3"}, {"\uD6A4", "\uD6A4"}, {"\uD6A5", "\uD6A5"}, {"\uD6A6", "\uD6A6"}, {"\uD6A7", "\uD6A7"}, {"\uD6A8", "\uD6A8"}, {"\uD6A9", "\uD6A9"}, {"\uD6AA", "\uD6AA"}, {"\uD6AB", "\uD6AB"}, {"\uD6AC", "\uD6AC"}, {"\uD6AD", "\uD6AD"}, {"\uD6AE", "\uD6AE"}, {"\uD6AF", "\uD6AF"}, {"\uD6B0", "\uD6B0"}, {"\uD6B1", "\uD6B1"}, {"\uD6B2", "\uD6B2"}, {"\uD6B3", "\uD6B3"}, {"\uD6B4", "\uD6B4"}, {"\uD6B5", "\uD6B5"}, {"\uD6B6", "\uD6B6"}, {"\uD6B7", "\uD6B7"}, {"\uD6B8", "\uD6B8"}, {"\uD6B9", "\uD6B9"}, {"\uD6BA", "\uD6BA"}, {"\uD6BB", "\uD6BB"}, {"\uD6BC", "\uD6BC"}, {"\uD6BD", "\uD6BD"}, {"\uD6BE", "\uD6BE"}, {"\uD6BF", "\uD6BF"}, {"\uD6C0", "\uD6C0"}, {"\uD6C1", "\uD6C1"}, {"\uD6C2", "\uD6C2"}, {"\uD6C3", "\uD6C3"}, {"\uD6C4", "\uD6C4"}, {"\uD6C5", "\uD6C5"}, {"\uD6C6", "\uD6C6"}, {"\uD6C7", "\uD6C7"}, {"\uD6C8", "\uD6C8"}, {"\uD6C9", "\uD6C9"}, {"\uD6CA", "\uD6CA"}, {"\uD6CB", "\uD6CB"}, {"\uD6CC", "\uD6CC"}, {"\uD6CD", "\uD6CD"}, {"\uD6CE", "\uD6CE"}, {"\uD6CF", "\uD6CF"}, {"\uD6D0", "\uD6D0"}, {"\uD6D1", "\uD6D1"}, {"\uD6D2", "\uD6D2"}, {"\uD6D3", "\uD6D3"}, {"\uD6D4", "\uD6D4"}, {"\uD6D5", "\uD6D5"}, {"\uD6D6", "\uD6D6"}, {"\uD6D7", "\uD6D7"}, {"\uD6D8", "\uD6D8"}, {"\uD6D9", "\uD6D9"}, {"\uD6DA", "\uD6DA"}, {"\uD6DB", "\uD6DB"}, {"\uD6DC", "\uD6DC"}, {"\uD6DD", "\uD6DD"}, {"\uD6DE", "\uD6DE"}, {"\uD6DF", "\uD6DF"}, {"\uD6E0", "\uD6E0"}, {"\uD6E1", "\uD6E1"}, {"\uD6E2", "\uD6E2"}, {"\uD6E3", "\uD6E3"}, {"\uD6E4", "\uD6E4"}, {"\uD6E5", "\uD6E5"}, {"\uD6E6", "\uD6E6"}, {"\uD6E7", "\uD6E7"}, {"\uD6E8", "\uD6E8"}, {"\uD6E9", "\uD6E9"}, {"\uD6EA", "\uD6EA"}, {"\uD6EB", "\uD6EB"}, {"\uD6EC", "\uD6EC"}, {"\uD6ED", "\uD6ED"}, {"\uD6EE", "\uD6EE"}, {"\uD6EF", "\uD6EF"}, {"\uD6F0", "\uD6F0"}, {"\uD6F1", "\uD6F1"}, {"\uD6F2", "\uD6F2"}, {"\uD6F3", "\uD6F3"}, {"\uD6F4", "\uD6F4"}, {"\uD6F5", "\uD6F5"}, {"\uD6F6", "\uD6F6"}, {"\uD6F7", "\uD6F7"}, {"\uD6F8", "\uD6F8"}, {"\uD6F9", "\uD6F9"}, {"\uD6FA", "\uD6FA"}, {"\uD6FB", "\uD6FB"}, {"\uD6FC", "\uD6FC"}, {"\uD6FD", "\uD6FD"}, {"\uD6FE", "\uD6FE"}, {"\uD6FF", "\uD6FF"}, {"\uD700", "\uD700"}, {"\uD701", "\uD701"}, {"\uD702", "\uD702"}, {"\uD703", "\uD703"}, {"\uD704", "\uD704"}, {"\uD705", "\uD705"}, {"\uD706", "\uD706"}, {"\uD707", "\uD707"}, {"\uD708", "\uD708"}, {"\uD709", "\uD709"}, {"\uD70A", "\uD70A"}, {"\uD70B", "\uD70B"}, {"\uD70C", "\uD70C"}, {"\uD70D", "\uD70D"}, {"\uD70E", "\uD70E"}, {"\uD70F", "\uD70F"}, {"\uD710", "\uD710"}, {"\uD711", "\uD711"}, {"\uD712", "\uD712"}, {"\uD713", "\uD713"}, {"\uD714", "\uD714"}, {"\uD715", "\uD715"}, {"\uD716", "\uD716"}, {"\uD717", "\uD717"}, {"\uD718", "\uD718"}, {"\uD719", "\uD719"}, {"\uD71A", "\uD71A"}, {"\uD71B", "\uD71B"}, {"\uD71C", "\uD71C"}, {"\uD71D", "\uD71D"}, {"\uD71E", "\uD71E"}, {"\uD71F", "\uD71F"}, {"\uD720", "\uD720"}, {"\uD721", "\uD721"}, {"\uD722", "\uD722"}, {"\uD723", "\uD723"}, {"\uD724", "\uD724"}, {"\uD725", "\uD725"}, {"\uD726", "\uD726"}, {"\uD727", "\uD727"}, {"\uD728", "\uD728"}, {"\uD729", "\uD729"}, {"\uD72A", "\uD72A"}, {"\uD72B", "\uD72B"}, {"\uD72C", "\uD72C"}, {"\uD72D", "\uD72D"}, {"\uD72E", "\uD72E"}, {"\uD72F", "\uD72F"}, {"\uD730", "\uD730"}, {"\uD731", "\uD731"}, {"\uD732", "\uD732"}, {"\uD733", "\uD733"}, {"\uD734", "\uD734"}, {"\uD735", "\uD735"}, {"\uD736", "\uD736"}, {"\uD737", "\uD737"}, {"\uD738", "\uD738"}, {"\uD739", "\uD739"}, {"\uD73A", "\uD73A"}, {"\uD73B", "\uD73B"}, {"\uD73C", "\uD73C"}, {"\uD73D", "\uD73D"}, {"\uD73E", "\uD73E"}, {"\uD73F", "\uD73F"}, {"\uD740", "\uD740"}, {"\uD741", "\uD741"}, {"\uD742", "\uD742"}, {"\uD743", "\uD743"}, {"\uD744", "\uD744"}, {"\uD745", "\uD745"}, {"\uD746", "\uD746"}, {"\uD747", "\uD747"}, {"\uD748", "\uD748"}, {"\uD749", "\uD749"}, {"\uD74A", "\uD74A"}, {"\uD74B", "\uD74B"}, {"\uD74C", "\uD74C"}, {"\uD74D", "\uD74D"}, {"\uD74E", "\uD74E"}, {"\uD74F", "\uD74F"}, {"\uD750", "\uD750"}, {"\uD751", "\uD751"}, {"\uD752", "\uD752"}, {"\uD753", "\uD753"}, {"\uD754", "\uD754"}, {"\uD755", "\uD755"}, {"\uD756", "\uD756"}, {"\uD757", "\uD757"}, {"\uD758", "\uD758"}, {"\uD759", "\uD759"}, {"\uD75A", "\uD75A"}, {"\uD75B", "\uD75B"}, {"\uD75C", "\uD75C"}, {"\uD75D", "\uD75D"}, {"\uD75E", "\uD75E"}, {"\uD75F", "\uD75F"}, {"\uD760", "\uD760"}, {"\uD761", "\uD761"}, {"\uD762", "\uD762"}, {"\uD763", "\uD763"}, {"\uD764", "\uD764"}, {"\uD765", "\uD765"}, {"\uD766", "\uD766"}, {"\uD767", "\uD767"}, {"\uD768", "\uD768"}, {"\uD769", "\uD769"}, {"\uD76A", "\uD76A"}, {"\uD76B", "\uD76B"}, {"\uD76C", "\uD76C"}, {"\uD76D", "\uD76D"}, {"\uD76E", "\uD76E"}, {"\uD76F", "\uD76F"}, {"\uD770", "\uD770"}, {"\uD771", "\uD771"}, {"\uD772", "\uD772"}, {"\uD773", "\uD773"}, {"\uD774", "\uD774"}, {"\uD775", "\uD775"}, {"\uD776", "\uD776"}, {"\uD777", "\uD777"}, {"\uD778", "\uD778"}, {"\uD779", "\uD779"}, {"\uD77A", "\uD77A"}, {"\uD77B", "\uD77B"}, {"\uD77C", "\uD77C"}, {"\uD77D", "\uD77D"}, {"\uD77E", "\uD77E"}, {"\uD77F", "\uD77F"}, {"\uD780", "\uD780"}, {"\uD781", "\uD781"}, {"\uD782", "\uD782"}, {"\uD783", "\uD783"}, {"\uD784", "\uD784"}, {"\uD785", "\uD785"}, {"\uD786", "\uD786"}, {"\uD787", "\uD787"}, {"\uD788", "\uD788"}, {"\uD789", "\uD789"}, {"\uD78A", "\uD78A"}, {"\uD78B", "\uD78B"}, {"\uD78C", "\uD78C"}, {"\uD78D", "\uD78D"}, {"\uD78E", "\uD78E"}, {"\uD78F", "\uD78F"}, {"\uD790", "\uD790"}, {"\uD791", "\uD791"}, {"\uD792", "\uD792"}, {"\uD793", "\uD793"}, {"\uD794", "\uD794"}, {"\uD795", "\uD795"}, {"\uD796", "\uD796"}, {"\uD797", "\uD797"}, {"\uD798", "\uD798"}, {"\uD799", "\uD799"}, {"\uD79A", "\uD79A"}, {"\uD79B", "\uD79B"}, {"\uD79C", "\uD79C"}, {"\uD79D", "\uD79D"}, {"\uD79E", "\uD79E"}, {"\uD79F", "\uD79F"}, {"\uD7A0", "\uD7A0"}, {"\uD7A1", "\uD7A1"}, {"\uD7A2", "\uD7A2"}, {"\uD7A3", "\uD7A3"}, {"\uF900", "\u8C48"}, {"\uF901", "\u66F4"}, {"\uF902", "\u8ECA"}, {"\uF903", "\u8CC8"}, {"\uF904", "\u6ED1"}, {"\uF905", "\u4E32"}, {"\uF906", "\u53E5"}, {"\uF907", "\u9F9C"}, {"\uF908", "\u9F9C"}, {"\uF909", "\u5951"}, {"\uF90A", "\u91D1"}, {"\uF90B", "\u5587"}, {"\uF90C", "\u5948"}, {"\uF90D", "\u61F6"}, {"\uF90E", "\u7669"}, {"\uF90F", "\u7F85"}, {"\uF910", "\u863F"}, {"\uF911", "\u87BA"}, {"\uF912", "\u88F8"}, {"\uF913", "\u908F"}, {"\uF914", "\u6A02"}, {"\uF915", "\u6D1B"}, {"\uF916", "\u70D9"}, {"\uF917", "\u73DE"}, {"\uF918", "\u843D"}, {"\uF919", "\u916A"}, {"\uF91A", "\u99F1"}, {"\uF91B", "\u4E82"}, {"\uF91C", "\u5375"}, {"\uF91D", "\u6B04"}, {"\uF91E", "\u721B"}, {"\uF91F", "\u862D"}, {"\uF920", "\u9E1E"}, {"\uF921", "\u5D50"}, {"\uF922", "\u6FEB"}, {"\uF923", "\u85CD"}, {"\uF924", "\u8964"}, {"\uF925", "\u62C9"}, {"\uF926", "\u81D8"}, {"\uF927", "\u881F"}, {"\uF928", "\u5ECA"}, {"\uF929", "\u6717"}, {"\uF92A", "\u6D6A"}, {"\uF92B", "\u72FC"}, {"\uF92C", "\u90CE"}, {"\uF92D", "\u4F86"}, {"\uF92E", "\u51B7"}, {"\uF92F", "\u52DE"}, {"\uF930", "\u64C4"}, {"\uF931", "\u6AD3"}, {"\uF932", "\u7210"}, {"\uF933", "\u76E7"}, {"\uF934", "\u8001"}, {"\uF935", "\u8606"}, {"\uF936", "\u865C"}, {"\uF937", "\u8DEF"}, {"\uF938", "\u9732"}, {"\uF939", "\u9B6F"}, {"\uF93A", "\u9DFA"}, {"\uF93B", "\u788C"}, {"\uF93C", "\u797F"}, {"\uF93D", "\u7DA0"}, {"\uF93E", "\u83C9"}, {"\uF93F", "\u9304"}, {"\uF940", "\u9E7F"}, {"\uF941", "\u8AD6"}, {"\uF942", "\u58DF"}, {"\uF943", "\u5F04"}, {"\uF944", "\u7C60"}, {"\uF945", "\u807E"}, {"\uF946", "\u7262"}, {"\uF947", "\u78CA"}, {"\uF948", "\u8CC2"}, {"\uF949", "\u96F7"}, {"\uF94A", "\u58D8"}, {"\uF94B", "\u5C62"}, {"\uF94C", "\u6A13"}, {"\uF94D", "\u6DDA"}, {"\uF94E", "\u6F0F"}, {"\uF94F", "\u7D2F"}, {"\uF950", "\u7E37"}, {"\uF951", "\u964B"}, {"\uF952", "\u52D2"}, {"\uF953", "\u808B"}, {"\uF954", "\u51DC"}, {"\uF955", "\u51CC"}, {"\uF956", "\u7A1C"}, {"\uF957", "\u7DBE"}, {"\uF958", "\u83F1"}, {"\uF959", "\u9675"}, {"\uF95A", "\u8B80"}, {"\uF95B", "\u62CF"}, {"\uF95C", "\u6A02"}, {"\uF95D", "\u8AFE"}, {"\uF95E", "\u4E39"}, {"\uF95F", "\u5BE7"}, {"\uF960", "\u6012"}, {"\uF961", "\u7387"}, {"\uF962", "\u7570"}, {"\uF963", "\u5317"}, {"\uF964", "\u78FB"}, {"\uF965", "\u4FBF"}, {"\uF966", "\u5FA9"}, {"\uF967", "\u4E0D"}, {"\uF968", "\u6CCC"}, {"\uF969", "\u6578"}, {"\uF96A", "\u7D22"}, {"\uF96B", "\u53C3"}, {"\uF96C", "\u585E"}, {"\uF96D", "\u7701"}, {"\uF96E", "\u8449"}, {"\uF96F", "\u8AAA"}, {"\uF970", "\u6BBA"}, {"\uF971", "\u8FB0"}, {"\uF972", "\u6C88"}, {"\uF973", "\u62FE"}, {"\uF974", "\u82E5"}, {"\uF975", "\u63A0"}, {"\uF976", "\u7565"}, {"\uF977", "\u4EAE"}, {"\uF978", "\u5169"}, {"\uF979", "\u51C9"}, {"\uF97A", "\u6881"}, {"\uF97B", "\u7CE7"}, {"\uF97C", "\u826F"}, {"\uF97D", "\u8AD2"}, {"\uF97E", "\u91CF"}, {"\uF97F", "\u52F5"}, {"\uF980", "\u5442"}, {"\uF981", "\u5973"}, {"\uF982", "\u5EEC"}, {"\uF983", "\u65C5"}, {"\uF984", "\u6FFE"}, {"\uF985", "\u792A"}, {"\uF986", "\u95AD"}, {"\uF987", "\u9A6A"}, {"\uF988", "\u9E97"}, {"\uF989", "\u9ECE"}, {"\uF98A", "\u529B"}, {"\uF98B", "\u66C6"}, {"\uF98C", "\u6B77"}, {"\uF98D", "\u8F62"}, {"\uF98E", "\u5E74"}, {"\uF98F", "\u6190"}, {"\uF990", "\u6200"}, {"\uF991", "\u649A"}, {"\uF992", "\u6F23"}, {"\uF993", "\u7149"}, {"\uF994", "\u7489"}, {"\uF995", "\u79CA"}, {"\uF996", "\u7DF4"}, {"\uF997", "\u806F"}, {"\uF998", "\u8F26"}, {"\uF999", "\u84EE"}, {"\uF99A", "\u9023"}, {"\uF99B", "\u934A"}, {"\uF99C", "\u5217"}, {"\uF99D", "\u52A3"}, {"\uF99E", "\u54BD"}, {"\uF99F", "\u70C8"}, {"\uF9A0", "\u88C2"}, {"\uF9A1", "\u8AAA"}, {"\uF9A2", "\u5EC9"}, {"\uF9A3", "\u5FF5"}, {"\uF9A4", "\u637B"}, {"\uF9A5", "\u6BAE"}, {"\uF9A6", "\u7C3E"}, {"\uF9A7", "\u7375"}, {"\uF9A8", "\u4EE4"}, {"\uF9A9", "\u56F9"}, {"\uF9AA", "\u5BE7"}, {"\uF9AB", "\u5DBA"}, {"\uF9AC", "\u601C"}, {"\uF9AD", "\u73B2"}, {"\uF9AE", "\u7469"}, {"\uF9AF", "\u7F9A"}, {"\uF9B0", "\u8046"}, {"\uF9B1", "\u9234"}, {"\uF9B2", "\u96F6"}, {"\uF9B3", "\u9748"}, {"\uF9B4", "\u9818"}, {"\uF9B5", "\u4F8B"}, {"\uF9B6", "\u79AE"}, {"\uF9B7", "\u91B4"}, {"\uF9B8", "\u96B8"}, {"\uF9B9", "\u60E1"}, {"\uF9BA", "\u4E86"}, {"\uF9BB", "\u50DA"}, {"\uF9BC", "\u5BEE"}, {"\uF9BD", "\u5C3F"}, {"\uF9BE", "\u6599"}, {"\uF9BF", "\u6A02"}, {"\uF9C0", "\u71CE"}, {"\uF9C1", "\u7642"}, {"\uF9C2", "\u84FC"}, {"\uF9C3", "\u907C"}, {"\uF9C4", "\u9F8D"}, {"\uF9C5", "\u6688"}, {"\uF9C6", "\u962E"}, {"\uF9C7", "\u5289"}, {"\uF9C8", "\u677B"}, {"\uF9C9", "\u67F3"}, {"\uF9CA", "\u6D41"}, {"\uF9CB", "\u6E9C"}, {"\uF9CC", "\u7409"}, {"\uF9CD", "\u7559"}, {"\uF9CE", "\u786B"}, {"\uF9CF", "\u7D10"}, {"\uF9D0", "\u985E"}, {"\uF9D1", "\u516D"}, {"\uF9D2", "\u622E"}, {"\uF9D3", "\u9678"}, {"\uF9D4", "\u502B"}, {"\uF9D5", "\u5D19"}, {"\uF9D6", "\u6DEA"}, {"\uF9D7", "\u8F2A"}, {"\uF9D8", "\u5F8B"}, {"\uF9D9", "\u6144"}, {"\uF9DA", "\u6817"}, {"\uF9DB", "\u7387"}, {"\uF9DC", "\u9686"}, {"\uF9DD", "\u5229"}, {"\uF9DE", "\u540F"}, {"\uF9DF", "\u5C65"}, {"\uF9E0", "\u6613"}, {"\uF9E1", "\u674E"}, {"\uF9E2", "\u68A8"}, {"\uF9E3", "\u6CE5"}, {"\uF9E4", "\u7406"}, {"\uF9E5", "\u75E2"}, {"\uF9E6", "\u7F79"}, {"\uF9E7", "\u88CF"}, {"\uF9E8", "\u88E1"}, {"\uF9E9", "\u91CC"}, {"\uF9EA", "\u96E2"}, {"\uF9EB", "\u533F"}, {"\uF9EC", "\u6EBA"}, {"\uF9ED", "\u541D"}, {"\uF9EE", "\u71D0"}, {"\uF9EF", "\u7498"}, {"\uF9F0", "\u85FA"}, {"\uF9F1", "\u96A3"}, {"\uF9F2", "\u9C57"}, {"\uF9F3", "\u9E9F"}, {"\uF9F4", "\u6797"}, {"\uF9F5", "\u6DCB"}, {"\uF9F6", "\u81E8"}, {"\uF9F7", "\u7ACB"}, {"\uF9F8", "\u7B20"}, {"\uF9F9", "\u7C92"}, {"\uF9FA", "\u72C0"}, {"\uF9FB", "\u7099"}, {"\uF9FC", "\u8B58"}, {"\uF9FD", "\u4EC0"}, {"\uF9FE", "\u8336"}, {"\uF9FF", "\u523A"}, {"\uFA00", "\u5207"}, {"\uFA01", "\u5EA6"}, {"\uFA02", "\u62D3"}, {"\uFA03", "\u7CD6"}, {"\uFA04", "\u5B85"}, {"\uFA05", "\u6D1E"}, {"\uFA06", "\u66B4"}, {"\uFA07", "\u8F3B"}, {"\uFA08", "\u884C"}, {"\uFA09", "\u964D"}, {"\uFA0A", "\u898B"}, {"\uFA0B", "\u5ED3"}, {"\uFA0C", "\u5140"}, {"\uFA0D", "\u55C0"}, {"\uFA10", "\u585A"}, {"\uFA12", "\u6674"}, {"\uFA15", "\u51DE"}, {"\uFA16", "\u732A"}, {"\uFA17", "\u76CA"}, {"\uFA18", "\u793C"}, {"\uFA19", "\u795E"}, {"\uFA1A", "\u7965"}, {"\uFA1B", "\u798F"}, {"\uFA1C", "\u9756"}, {"\uFA1D", "\u7CBE"}, {"\uFA1E", "\u7FBD"}, {"\uFA20", "\u8612"}, {"\uFA22", "\u8AF8"}, {"\uFA25", "\u9038"}, {"\uFA26", "\u90FD"}, {"\uFA2A", "\u98EF"}, {"\uFA2B", "\u98FC"}, {"\uFA2C", "\u9928"}, {"\uFA2D", "\u9DB4"}, {"\uFA2E", "\u90DE"}, {"\uFA2F", "\u96B7"}, {"\uFA30", "\u4FAE"}, {"\uFA31", "\u50E7"}, {"\uFA32", "\u514D"}, {"\uFA33", "\u52C9"}, {"\uFA34", "\u52E4"}, {"\uFA35", "\u5351"}, {"\uFA36", "\u559D"}, {"\uFA37", "\u5606"}, {"\uFA38", "\u5668"}, {"\uFA39", "\u5840"}, {"\uFA3A", "\u58A8"}, {"\uFA3B", "\u5C64"}, {"\uFA3C", "\u5C6E"}, {"\uFA3D", "\u6094"}, {"\uFA3E", "\u6168"}, {"\uFA3F", "\u618E"}, {"\uFA40", "\u61F2"}, {"\uFA41", "\u654F"}, {"\uFA42", "\u65E2"}, {"\uFA43", "\u6691"}, {"\uFA44", "\u6885"}, {"\uFA45", "\u6D77"}, {"\uFA46", "\u6E1A"}, {"\uFA47", "\u6F22"}, {"\uFA48", "\u716E"}, {"\uFA49", "\u722B"}, {"\uFA4A", "\u7422"}, {"\uFA4B", "\u7891"}, {"\uFA4C", "\u793E"}, {"\uFA4D", "\u7949"}, {"\uFA4E", "\u7948"}, {"\uFA4F", "\u7950"}, {"\uFA50", "\u7956"}, {"\uFA51", "\u795D"}, {"\uFA52", "\u798D"}, {"\uFA53", "\u798E"}, {"\uFA54", "\u7A40"}, {"\uFA55", "\u7A81"}, {"\uFA56", "\u7BC0"}, {"\uFA57", "\u7DF4"}, {"\uFA58", "\u7E09"}, {"\uFA59", "\u7E41"}, {"\uFA5A", "\u7F72"}, {"\uFA5B", "\u8005"}, {"\uFA5C", "\u81ED"}, {"\uFA5D", "\u8279"}, {"\uFA5E", "\u8279"}, {"\uFA5F", "\u8457"}, {"\uFA60", "\u8910"}, {"\uFA61", "\u8996"}, {"\uFA62", "\u8B01"}, {"\uFA63", "\u8B39"}, {"\uFA64", "\u8CD3"}, {"\uFA65", "\u8D08"}, {"\uFA66", "\u8FB6"}, {"\uFA67", "\u9038"}, {"\uFA68", "\u96E3"}, {"\uFA69", "\u97FF"}, {"\uFA6A", "\u983B"}, {"\uFA6B", "\u6075"}, {"\uFA6C", "\U000242EE"}, {"\uFA6D", "\u8218"}, {"\uFA70", "\u4E26"}, {"\uFA71", "\u51B5"}, {"\uFA72", "\u5168"}, {"\uFA73", "\u4F80"}, {"\uFA74", "\u5145"}, {"\uFA75", "\u5180"}, {"\uFA76", "\u52C7"}, {"\uFA77", "\u52FA"}, {"\uFA78", "\u559D"}, {"\uFA79", "\u5555"}, {"\uFA7A", "\u5599"}, {"\uFA7B", "\u55E2"}, {"\uFA7C", "\u585A"}, {"\uFA7D", "\u58B3"}, {"\uFA7E", "\u5944"}, {"\uFA7F", "\u5954"}, {"\uFA80", "\u5A62"}, {"\uFA81", "\u5B28"}, {"\uFA82", "\u5ED2"}, {"\uFA83", "\u5ED9"}, {"\uFA84", "\u5F69"}, {"\uFA85", "\u5FAD"}, {"\uFA86", "\u60D8"}, {"\uFA87", "\u614E"}, {"\uFA88", "\u6108"}, {"\uFA89", "\u618E"}, {"\uFA8A", "\u6160"}, {"\uFA8B", "\u61F2"}, {"\uFA8C", "\u6234"}, {"\uFA8D", "\u63C4"}, {"\uFA8E", "\u641C"}, {"\uFA8F", "\u6452"}, {"\uFA90", "\u6556"}, {"\uFA91", "\u6674"}, {"\uFA92", "\u6717"}, {"\uFA93", "\u671B"}, {"\uFA94", "\u6756"}, {"\uFA95", "\u6B79"}, {"\uFA96", "\u6BBA"}, {"\uFA97", "\u6D41"}, {"\uFA98", "\u6EDB"}, {"\uFA99", "\u6ECB"}, {"\uFA9A", "\u6F22"}, {"\uFA9B", "\u701E"}, {"\uFA9C", "\u716E"}, {"\uFA9D", "\u77A7"}, {"\uFA9E", "\u7235"}, {"\uFA9F", "\u72AF"}, {"\uFAA0", "\u732A"}, {"\uFAA1", "\u7471"}, {"\uFAA2", "\u7506"}, {"\uFAA3", "\u753B"}, {"\uFAA4", "\u761D"}, {"\uFAA5", "\u761F"}, {"\uFAA6", "\u76CA"}, {"\uFAA7", "\u76DB"}, {"\uFAA8", "\u76F4"}, {"\uFAA9", "\u774A"}, {"\uFAAA", "\u7740"}, {"\uFAAB", "\u78CC"}, {"\uFAAC", "\u7AB1"}, {"\uFAAD", "\u7BC0"}, {"\uFAAE", "\u7C7B"}, {"\uFAAF", "\u7D5B"}, {"\uFAB0", "\u7DF4"}, {"\uFAB1", "\u7F3E"}, {"\uFAB2", "\u8005"}, {"\uFAB3", "\u8352"}, {"\uFAB4", "\u83EF"}, {"\uFAB5", "\u8779"}, {"\uFAB6", "\u8941"}, {"\uFAB7", "\u8986"}, {"\uFAB8", "\u8996"}, {"\uFAB9", "\u8ABF"}, {"\uFABA", "\u8AF8"}, {"\uFABB", "\u8ACB"}, {"\uFABC", "\u8B01"}, {"\uFABD", "\u8AFE"}, {"\uFABE", "\u8AED"}, {"\uFABF", "\u8B39"}, {"\uFAC0", "\u8B8A"}, {"\uFAC1", "\u8D08"}, {"\uFAC2", "\u8F38"}, {"\uFAC3", "\u9072"}, {"\uFAC4", "\u9199"}, {"\uFAC5", "\u9276"}, {"\uFAC6", "\u967C"}, {"\uFAC7", "\u96E3"}, {"\uFAC8", "\u9756"}, {"\uFAC9", "\u97DB"}, {"\uFACA", "\u97FF"}, {"\uFACB", "\u980B"}, {"\uFACC", "\u983B"}, {"\uFACD", "\u9B12"}, {"\uFACE", "\u9F9C"}, {"\uFACF", "\U0002284A"}, {"\uFAD0", "\U00022844"}, {"\uFAD1", "\U000233D5"}, {"\uFAD2", "\u3B9D"}, {"\uFAD3", "\u4018"}, {"\uFAD4", "\u4039"}, {"\uFAD5", "\U00025249"}, {"\uFAD6", "\U00025CD0"}, {"\uFAD7", "\U00027ED3"}, {"\uFAD8", "\u9F43"}, {"\uFAD9", "\u9F8E"}, {"\uFB00", "\uFB00"}, {"\uFB01", "\uFB01"}, {"\uFB02", "\uFB02"}, {"\uFB03", "\uFB03"}, {"\uFB04", "\uFB04"}, {"\uFB05", "\uFB05"}, {"\uFB06", "\uFB06"}, {"\uFB13", "\uFB13"}, {"\uFB14", "\uFB14"}, {"\uFB15", "\uFB15"}, {"\uFB16", "\uFB16"}, {"\uFB17", "\uFB17"}, {"\uFB1D", "\u05D9\u05B4"}, {"\uFB1F", "\u05F2\u05B7"}, {"\uFB20", "\uFB20"}, {"\uFB21", "\uFB21"}, {"\uFB22", "\uFB22"}, {"\uFB23", "\uFB23"}, {"\uFB24", "\uFB24"}, {"\uFB25", "\uFB25"}, {"\uFB26", "\uFB26"}, {"\uFB27", "\uFB27"}, {"\uFB28", "\uFB28"}, {"\uFB29", "\uFB29"}, {"\uFB2A", "\u05E9\u05C1"}, {"\uFB2B", "\u05E9\u05C2"}, {"\uFB2C", "\u05E9\u05BC\u05C1"}, {"\uFB2D", "\u05E9\u05BC\u05C2"}, {"\uFB2E", "\u05D0\u05B7"}, {"\uFB2F", "\u05D0\u05B8"}, {"\uFB30", "\u05D0\u05BC"}, {"\uFB31", "\u05D1\u05BC"}, {"\uFB32", "\u05D2\u05BC"}, {"\uFB33", "\u05D3\u05BC"}, {"\uFB34", "\u05D4\u05BC"}, {"\uFB35", "\u05D5\u05BC"}, {"\uFB36", "\u05D6\u05BC"}, {"\uFB38", "\u05D8\u05BC"}, {"\uFB39", "\u05D9\u05BC"}, {"\uFB3A", "\u05DA\u05BC"}, {"\uFB3B", "\u05DB\u05BC"}, {"\uFB3C", "\u05DC\u05BC"}, {"\uFB3E", "\u05DE\u05BC"}, {"\uFB40", "\u05E0\u05BC"}, {"\uFB41", "\u05E1\u05BC"}, {"\uFB43", "\u05E3\u05BC"}, {"\uFB44", "\u05E4\u05BC"}, {"\uFB46", "\u05E6\u05BC"}, {"\uFB47", "\u05E7\u05BC"}, {"\uFB48", "\u05E8\u05BC"}, {"\uFB49", "\u05E9\u05BC"}, {"\uFB4A", "\u05EA\u05BC"}, {"\uFB4B", "\u05D5\u05B9"}, {"\uFB4C", "\u05D1\u05BF"}, {"\uFB4D", "\u05DB\u05BF"}, {"\uFB4E", "\u05E4\u05BF"}, {"\uFB4F", "\uFB4F"}, {"\uFB50", "\uFB50"}, {"\uFB51", "\uFB51"}, {"\uFB52", "\uFB52"}, {"\uFB53", "\uFB53"}, {"\uFB54", "\uFB54"}, {"\uFB55", "\uFB55"}, {"\uFB56", "\uFB56"}, {"\uFB57", "\uFB57"}, {"\uFB58", "\uFB58"}, {"\uFB59", "\uFB59"}, {"\uFB5A", "\uFB5A"}, {"\uFB5B", "\uFB5B"}, {"\uFB5C", "\uFB5C"}, {"\uFB5D", "\uFB5D"}, {"\uFB5E", "\uFB5E"}, {"\uFB5F", "\uFB5F"}, {"\uFB60", "\uFB60"}, {"\uFB61", "\uFB61"}, {"\uFB62", "\uFB62"}, {"\uFB63", "\uFB63"}, {"\uFB64", "\uFB64"}, {"\uFB65", "\uFB65"}, {"\uFB66", "\uFB66"}, {"\uFB67", "\uFB67"}, {"\uFB68", "\uFB68"}, {"\uFB69", "\uFB69"}, {"\uFB6A", "\uFB6A"}, {"\uFB6B", "\uFB6B"}, {"\uFB6C", "\uFB6C"}, {"\uFB6D", "\uFB6D"}, {"\uFB6E", "\uFB6E"}, {"\uFB6F", "\uFB6F"}, {"\uFB70", "\uFB70"}, {"\uFB71", "\uFB71"}, {"\uFB72", "\uFB72"}, {"\uFB73", "\uFB73"}, {"\uFB74", "\uFB74"}, {"\uFB75", "\uFB75"}, {"\uFB76", "\uFB76"}, {"\uFB77", "\uFB77"}, {"\uFB78", "\uFB78"}, {"\uFB79", "\uFB79"}, {"\uFB7A", "\uFB7A"}, {"\uFB7B", "\uFB7B"}, {"\uFB7C", "\uFB7C"}, {"\uFB7D", "\uFB7D"}, {"\uFB7E", "\uFB7E"}, {"\uFB7F", "\uFB7F"}, {"\uFB80", "\uFB80"}, {"\uFB81", "\uFB81"}, {"\uFB82", "\uFB82"}, {"\uFB83", "\uFB83"}, {"\uFB84", "\uFB84"}, {"\uFB85", "\uFB85"}, {"\uFB86", "\uFB86"}, {"\uFB87", "\uFB87"}, {"\uFB88", "\uFB88"}, {"\uFB89", "\uFB89"}, {"\uFB8A", "\uFB8A"}, {"\uFB8B", "\uFB8B"}, {"\uFB8C", "\uFB8C"}, {"\uFB8D", "\uFB8D"}, {"\uFB8E", "\uFB8E"}, {"\uFB8F", "\uFB8F"}, {"\uFB90", "\uFB90"}, {"\uFB91", "\uFB91"}, {"\uFB92", "\uFB92"}, {"\uFB93", "\uFB93"}, {"\uFB94", "\uFB94"}, {"\uFB95", "\uFB95"}, {"\uFB96", "\uFB96"}, {"\uFB97", "\uFB97"}, {"\uFB98", "\uFB98"}, {"\uFB99", "\uFB99"}, {"\uFB9A", "\uFB9A"}, {"\uFB9B", "\uFB9B"}, {"\uFB9C", "\uFB9C"}, {"\uFB9D", "\uFB9D"}, {"\uFB9E", "\uFB9E"}, {"\uFB9F", "\uFB9F"}, {"\uFBA0", "\uFBA0"}, {"\uFBA1", "\uFBA1"}, {"\uFBA2", "\uFBA2"}, {"\uFBA3", "\uFBA3"}, {"\uFBA4", "\uFBA4"}, {"\uFBA5", "\uFBA5"}, {"\uFBA6", "\uFBA6"}, {"\uFBA7", "\uFBA7"}, {"\uFBA8", "\uFBA8"}, {"\uFBA9", "\uFBA9"}, {"\uFBAA", "\uFBAA"}, {"\uFBAB", "\uFBAB"}, {"\uFBAC", "\uFBAC"}, {"\uFBAD", "\uFBAD"}, {"\uFBAE", "\uFBAE"}, {"\uFBAF", "\uFBAF"}, {"\uFBB0", "\uFBB0"}, {"\uFBB1", "\uFBB1"}, {"\uFBD3", "\uFBD3"}, {"\uFBD4", "\uFBD4"}, {"\uFBD5", "\uFBD5"}, {"\uFBD6", "\uFBD6"}, {"\uFBD7", "\uFBD7"}, {"\uFBD8", "\uFBD8"}, {"\uFBD9", "\uFBD9"}, {"\uFBDA", "\uFBDA"}, {"\uFBDB", "\uFBDB"}, {"\uFBDC", "\uFBDC"}, {"\uFBDD", "\uFBDD"}, {"\uFBDE", "\uFBDE"}, {"\uFBDF", "\uFBDF"}, {"\uFBE0", "\uFBE0"}, {"\uFBE1", "\uFBE1"}, {"\uFBE2", "\uFBE2"}, {"\uFBE3", "\uFBE3"}, {"\uFBE4", "\uFBE4"}, {"\uFBE5", "\uFBE5"}, {"\uFBE6", "\uFBE6"}, {"\uFBE7", "\uFBE7"}, {"\uFBE8", "\uFBE8"}, {"\uFBE9", "\uFBE9"}, {"\uFBEA", "\uFBEA"}, {"\uFBEB", "\uFBEB"}, {"\uFBEC", "\uFBEC"}, {"\uFBED", "\uFBED"}, {"\uFBEE", "\uFBEE"}, {"\uFBEF", "\uFBEF"}, {"\uFBF0", "\uFBF0"}, {"\uFBF1", "\uFBF1"}, {"\uFBF2", "\uFBF2"}, {"\uFBF3", "\uFBF3"}, {"\uFBF4", "\uFBF4"}, {"\uFBF5", "\uFBF5"}, {"\uFBF6", "\uFBF6"}, {"\uFBF7", "\uFBF7"}, {"\uFBF8", "\uFBF8"}, {"\uFBF9", "\uFBF9"}, {"\uFBFA", "\uFBFA"}, {"\uFBFB", "\uFBFB"}, {"\uFBFC", "\uFBFC"}, {"\uFBFD", "\uFBFD"}, {"\uFBFE", "\uFBFE"}, {"\uFBFF", "\uFBFF"}, {"\uFC00", "\uFC00"}, {"\uFC01", "\uFC01"}, {"\uFC02", "\uFC02"}, {"\uFC03", "\uFC03"}, {"\uFC04", "\uFC04"}, {"\uFC05", "\uFC05"}, {"\uFC06", "\uFC06"}, {"\uFC07", "\uFC07"}, {"\uFC08", "\uFC08"}, {"\uFC09", "\uFC09"}, {"\uFC0A", "\uFC0A"}, {"\uFC0B", "\uFC0B"}, {"\uFC0C", "\uFC0C"}, {"\uFC0D", "\uFC0D"}, {"\uFC0E", "\uFC0E"}, {"\uFC0F", "\uFC0F"}, {"\uFC10", "\uFC10"}, {"\uFC11", "\uFC11"}, {"\uFC12", "\uFC12"}, {"\uFC13", "\uFC13"}, {"\uFC14", "\uFC14"}, {"\uFC15", "\uFC15"}, {"\uFC16", "\uFC16"}, {"\uFC17", "\uFC17"}, {"\uFC18", "\uFC18"}, {"\uFC19", "\uFC19"}, {"\uFC1A", "\uFC1A"}, {"\uFC1B", "\uFC1B"}, {"\uFC1C", "\uFC1C"}, {"\uFC1D", "\uFC1D"}, {"\uFC1E", "\uFC1E"}, {"\uFC1F", "\uFC1F"}, {"\uFC20", "\uFC20"}, {"\uFC21", "\uFC21"}, {"\uFC22", "\uFC22"}, {"\uFC23", "\uFC23"}, {"\uFC24", "\uFC24"}, {"\uFC25", "\uFC25"}, {"\uFC26", "\uFC26"}, {"\uFC27", "\uFC27"}, {"\uFC28", "\uFC28"}, {"\uFC29", "\uFC29"}, {"\uFC2A", "\uFC2A"}, {"\uFC2B", "\uFC2B"}, {"\uFC2C", "\uFC2C"}, {"\uFC2D", "\uFC2D"}, {"\uFC2E", "\uFC2E"}, {"\uFC2F", "\uFC2F"}, {"\uFC30", "\uFC30"}, {"\uFC31", "\uFC31"}, {"\uFC32", "\uFC32"}, {"\uFC33", "\uFC33"}, {"\uFC34", "\uFC34"}, {"\uFC35", "\uFC35"}, {"\uFC36", "\uFC36"}, {"\uFC37", "\uFC37"}, {"\uFC38", "\uFC38"}, {"\uFC39", "\uFC39"}, {"\uFC3A", "\uFC3A"}, {"\uFC3B", "\uFC3B"}, {"\uFC3C", "\uFC3C"}, {"\uFC3D", "\uFC3D"}, {"\uFC3E", "\uFC3E"}, {"\uFC3F", "\uFC3F"}, {"\uFC40", "\uFC40"}, {"\uFC41", "\uFC41"}, {"\uFC42", "\uFC42"}, {"\uFC43", "\uFC43"}, {"\uFC44", "\uFC44"}, {"\uFC45", "\uFC45"}, {"\uFC46", "\uFC46"}, {"\uFC47", "\uFC47"}, {"\uFC48", "\uFC48"}, {"\uFC49", "\uFC49"}, {"\uFC4A", "\uFC4A"}, {"\uFC4B", "\uFC4B"}, {"\uFC4C", "\uFC4C"}, {"\uFC4D", "\uFC4D"}, {"\uFC4E", "\uFC4E"}, {"\uFC4F", "\uFC4F"}, {"\uFC50", "\uFC50"}, {"\uFC51", "\uFC51"}, {"\uFC52", "\uFC52"}, {"\uFC53", "\uFC53"}, {"\uFC54", "\uFC54"}, {"\uFC55", "\uFC55"}, {"\uFC56", "\uFC56"}, {"\uFC57", "\uFC57"}, {"\uFC58", "\uFC58"}, {"\uFC59", "\uFC59"}, {"\uFC5A", "\uFC5A"}, {"\uFC5B", "\uFC5B"}, {"\uFC5C", "\uFC5C"}, {"\uFC5D", "\uFC5D"}, {"\uFC5E", "\uFC5E"}, {"\uFC5F", "\uFC5F"}, {"\uFC60", "\uFC60"}, {"\uFC61", "\uFC61"}, {"\uFC62", "\uFC62"}, {"\uFC63", "\uFC63"}, {"\uFC64", "\uFC64"}, {"\uFC65", "\uFC65"}, {"\uFC66", "\uFC66"}, {"\uFC67", "\uFC67"}, {"\uFC68", "\uFC68"}, {"\uFC69", "\uFC69"}, {"\uFC6A", "\uFC6A"}, {"\uFC6B", "\uFC6B"}, {"\uFC6C", "\uFC6C"}, {"\uFC6D", "\uFC6D"}, {"\uFC6E", "\uFC6E"}, {"\uFC6F", "\uFC6F"}, {"\uFC70", "\uFC70"}, {"\uFC71", "\uFC71"}, {"\uFC72", "\uFC72"}, {"\uFC73", "\uFC73"}, {"\uFC74", "\uFC74"}, {"\uFC75", "\uFC75"}, {"\uFC76", "\uFC76"}, {"\uFC77", "\uFC77"}, {"\uFC78", "\uFC78"}, {"\uFC79", "\uFC79"}, {"\uFC7A", "\uFC7A"}, {"\uFC7B", "\uFC7B"}, {"\uFC7C", "\uFC7C"}, {"\uFC7D", "\uFC7D"}, {"\uFC7E", "\uFC7E"}, {"\uFC7F", "\uFC7F"}, {"\uFC80", "\uFC80"}, {"\uFC81", "\uFC81"}, {"\uFC82", "\uFC82"}, {"\uFC83", "\uFC83"}, {"\uFC84", "\uFC84"}, {"\uFC85", "\uFC85"}, {"\uFC86", "\uFC86"}, {"\uFC87", "\uFC87"}, {"\uFC88", "\uFC88"}, {"\uFC89", "\uFC89"}, {"\uFC8A", "\uFC8A"}, {"\uFC8B", "\uFC8B"}, {"\uFC8C", "\uFC8C"}, {"\uFC8D", "\uFC8D"}, {"\uFC8E", "\uFC8E"}, {"\uFC8F", "\uFC8F"}, {"\uFC90", "\uFC90"}, {"\uFC91", "\uFC91"}, {"\uFC92", "\uFC92"}, {"\uFC93", "\uFC93"}, {"\uFC94", "\uFC94"}, {"\uFC95", "\uFC95"}, {"\uFC96", "\uFC96"}, {"\uFC97", "\uFC97"}, {"\uFC98", "\uFC98"}, {"\uFC99", "\uFC99"}, {"\uFC9A", "\uFC9A"}, {"\uFC9B", "\uFC9B"}, {"\uFC9C", "\uFC9C"}, {"\uFC9D", "\uFC9D"}, {"\uFC9E", "\uFC9E"}, {"\uFC9F", "\uFC9F"}, {"\uFCA0", "\uFCA0"}, {"\uFCA1", "\uFCA1"}, {"\uFCA2", "\uFCA2"}, {"\uFCA3", "\uFCA3"}, {"\uFCA4", "\uFCA4"}, {"\uFCA5", "\uFCA5"}, {"\uFCA6", "\uFCA6"}, {"\uFCA7", "\uFCA7"}, {"\uFCA8", "\uFCA8"}, {"\uFCA9", "\uFCA9"}, {"\uFCAA", "\uFCAA"}, {"\uFCAB", "\uFCAB"}, {"\uFCAC", "\uFCAC"}, {"\uFCAD", "\uFCAD"}, {"\uFCAE", "\uFCAE"}, {"\uFCAF", "\uFCAF"}, {"\uFCB0", "\uFCB0"}, {"\uFCB1", "\uFCB1"}, {"\uFCB2", "\uFCB2"}, {"\uFCB3", "\uFCB3"}, {"\uFCB4", "\uFCB4"}, {"\uFCB5", "\uFCB5"}, {"\uFCB6", "\uFCB6"}, {"\uFCB7", "\uFCB7"}, {"\uFCB8", "\uFCB8"}, {"\uFCB9", "\uFCB9"}, {"\uFCBA", "\uFCBA"}, {"\uFCBB", "\uFCBB"}, {"\uFCBC", "\uFCBC"}, {"\uFCBD", "\uFCBD"}, {"\uFCBE", "\uFCBE"}, {"\uFCBF", "\uFCBF"}, {"\uFCC0", "\uFCC0"}, {"\uFCC1", "\uFCC1"}, {"\uFCC2", "\uFCC2"}, {"\uFCC3", "\uFCC3"}, {"\uFCC4", "\uFCC4"}, {"\uFCC5", "\uFCC5"}, {"\uFCC6", "\uFCC6"}, {"\uFCC7", "\uFCC7"}, {"\uFCC8", "\uFCC8"}, {"\uFCC9", "\uFCC9"}, {"\uFCCA", "\uFCCA"}, {"\uFCCB", "\uFCCB"}, {"\uFCCC", "\uFCCC"}, {"\uFCCD", "\uFCCD"}, {"\uFCCE", "\uFCCE"}, {"\uFCCF", "\uFCCF"}, {"\uFCD0", "\uFCD0"}, {"\uFCD1", "\uFCD1"}, {"\uFCD2", "\uFCD2"}, {"\uFCD3", "\uFCD3"}, {"\uFCD4", "\uFCD4"}, {"\uFCD5", "\uFCD5"}, {"\uFCD6", "\uFCD6"}, {"\uFCD7", "\uFCD7"}, {"\uFCD8", "\uFCD8"}, {"\uFCD9", "\uFCD9"}, {"\uFCDA", "\uFCDA"}, {"\uFCDB", "\uFCDB"}, {"\uFCDC", "\uFCDC"}, {"\uFCDD", "\uFCDD"}, {"\uFCDE", "\uFCDE"}, {"\uFCDF", "\uFCDF"}, {"\uFCE0", "\uFCE0"}, {"\uFCE1", "\uFCE1"}, {"\uFCE2", "\uFCE2"}, {"\uFCE3", "\uFCE3"}, {"\uFCE4", "\uFCE4"}, {"\uFCE5", "\uFCE5"}, {"\uFCE6", "\uFCE6"}, {"\uFCE7", "\uFCE7"}, {"\uFCE8", "\uFCE8"}, {"\uFCE9", "\uFCE9"}, {"\uFCEA", "\uFCEA"}, {"\uFCEB", "\uFCEB"}, {"\uFCEC", "\uFCEC"}, {"\uFCED", "\uFCED"}, {"\uFCEE", "\uFCEE"}, {"\uFCEF", "\uFCEF"}, {"\uFCF0", "\uFCF0"}, {"\uFCF1", "\uFCF1"}, {"\uFCF2", "\uFCF2"}, {"\uFCF3", "\uFCF3"}, {"\uFCF4", "\uFCF4"}, {"\uFCF5", "\uFCF5"}, {"\uFCF6", "\uFCF6"}, {"\uFCF7", "\uFCF7"}, {"\uFCF8", "\uFCF8"}, {"\uFCF9", "\uFCF9"}, {"\uFCFA", "\uFCFA"}, {"\uFCFB", "\uFCFB"}, {"\uFCFC", "\uFCFC"}, {"\uFCFD", "\uFCFD"}, {"\uFCFE", "\uFCFE"}, {"\uFCFF", "\uFCFF"}, {"\uFD00", "\uFD00"}, {"\uFD01", "\uFD01"}, {"\uFD02", "\uFD02"}, {"\uFD03", "\uFD03"}, {"\uFD04", "\uFD04"}, {"\uFD05", "\uFD05"}, {"\uFD06", "\uFD06"}, {"\uFD07", "\uFD07"}, {"\uFD08", "\uFD08"}, {"\uFD09", "\uFD09"}, {"\uFD0A", "\uFD0A"}, {"\uFD0B", "\uFD0B"}, {"\uFD0C", "\uFD0C"}, {"\uFD0D", "\uFD0D"}, {"\uFD0E", "\uFD0E"}, {"\uFD0F", "\uFD0F"}, {"\uFD10", "\uFD10"}, {"\uFD11", "\uFD11"}, {"\uFD12", "\uFD12"}, {"\uFD13", "\uFD13"}, {"\uFD14", "\uFD14"}, {"\uFD15", "\uFD15"}, {"\uFD16", "\uFD16"}, {"\uFD17", "\uFD17"}, {"\uFD18", "\uFD18"}, {"\uFD19", "\uFD19"}, {"\uFD1A", "\uFD1A"}, {"\uFD1B", "\uFD1B"}, {"\uFD1C", "\uFD1C"}, {"\uFD1D", "\uFD1D"}, {"\uFD1E", "\uFD1E"}, {"\uFD1F", "\uFD1F"}, {"\uFD20", "\uFD20"}, {"\uFD21", "\uFD21"}, {"\uFD22", "\uFD22"}, {"\uFD23", "\uFD23"}, {"\uFD24", "\uFD24"}, {"\uFD25", "\uFD25"}, {"\uFD26", "\uFD26"}, {"\uFD27", "\uFD27"}, {"\uFD28", "\uFD28"}, {"\uFD29", "\uFD29"}, {"\uFD2A", "\uFD2A"}, {"\uFD2B", "\uFD2B"}, {"\uFD2C", "\uFD2C"}, {"\uFD2D", "\uFD2D"}, {"\uFD2E", "\uFD2E"}, {"\uFD2F", "\uFD2F"}, {"\uFD30", "\uFD30"}, {"\uFD31", "\uFD31"}, {"\uFD32", "\uFD32"}, {"\uFD33", "\uFD33"}, {"\uFD34", "\uFD34"}, {"\uFD35", "\uFD35"}, {"\uFD36", "\uFD36"}, {"\uFD37", "\uFD37"}, {"\uFD38", "\uFD38"}, {"\uFD39", "\uFD39"}, {"\uFD3A", "\uFD3A"}, {"\uFD3B", "\uFD3B"}, {"\uFD3C", "\uFD3C"}, {"\uFD3D", "\uFD3D"}, {"\uFD50", "\uFD50"}, {"\uFD51", "\uFD51"}, {"\uFD52", "\uFD52"}, {"\uFD53", "\uFD53"}, {"\uFD54", "\uFD54"}, {"\uFD55", "\uFD55"}, {"\uFD56", "\uFD56"}, {"\uFD57", "\uFD57"}, {"\uFD58", "\uFD58"}, {"\uFD59", "\uFD59"}, {"\uFD5A", "\uFD5A"}, {"\uFD5B", "\uFD5B"}, {"\uFD5C", "\uFD5C"}, {"\uFD5D", "\uFD5D"}, {"\uFD5E", "\uFD5E"}, {"\uFD5F", "\uFD5F"}, {"\uFD60", "\uFD60"}, {"\uFD61", "\uFD61"}, {"\uFD62", "\uFD62"}, {"\uFD63", "\uFD63"}, {"\uFD64", "\uFD64"}, {"\uFD65", "\uFD65"}, {"\uFD66", "\uFD66"}, {"\uFD67", "\uFD67"}, {"\uFD68", "\uFD68"}, {"\uFD69", "\uFD69"}, {"\uFD6A", "\uFD6A"}, {"\uFD6B", "\uFD6B"}, {"\uFD6C", "\uFD6C"}, {"\uFD6D", "\uFD6D"}, {"\uFD6E", "\uFD6E"}, {"\uFD6F", "\uFD6F"}, {"\uFD70", "\uFD70"}, {"\uFD71", "\uFD71"}, {"\uFD72", "\uFD72"}, {"\uFD73", "\uFD73"}, {"\uFD74", "\uFD74"}, {"\uFD75", "\uFD75"}, {"\uFD76", "\uFD76"}, {"\uFD77", "\uFD77"}, {"\uFD78", "\uFD78"}, {"\uFD79", "\uFD79"}, {"\uFD7A", "\uFD7A"}, {"\uFD7B", "\uFD7B"}, {"\uFD7C", "\uFD7C"}, {"\uFD7D", "\uFD7D"}, {"\uFD7E", "\uFD7E"}, {"\uFD7F", "\uFD7F"}, {"\uFD80", "\uFD80"}, {"\uFD81", "\uFD81"}, {"\uFD82", "\uFD82"}, {"\uFD83", "\uFD83"}, {"\uFD84", "\uFD84"}, {"\uFD85", "\uFD85"}, {"\uFD86", "\uFD86"}, {"\uFD87", "\uFD87"}, {"\uFD88", "\uFD88"}, {"\uFD89", "\uFD89"}, {"\uFD8A", "\uFD8A"}, {"\uFD8B", "\uFD8B"}, {"\uFD8C", "\uFD8C"}, {"\uFD8D", "\uFD8D"}, {"\uFD8E", "\uFD8E"}, {"\uFD8F", "\uFD8F"}, {"\uFD92", "\uFD92"}, {"\uFD93", "\uFD93"}, {"\uFD94", "\uFD94"}, {"\uFD95", "\uFD95"}, {"\uFD96", "\uFD96"}, {"\uFD97", "\uFD97"}, {"\uFD98", "\uFD98"}, {"\uFD99", "\uFD99"}, {"\uFD9A", "\uFD9A"}, {"\uFD9B", "\uFD9B"}, {"\uFD9C", "\uFD9C"}, {"\uFD9D", "\uFD9D"}, {"\uFD9E", "\uFD9E"}, {"\uFD9F", "\uFD9F"}, {"\uFDA0", "\uFDA0"}, {"\uFDA1", "\uFDA1"}, {"\uFDA2", "\uFDA2"}, {"\uFDA3", "\uFDA3"}, {"\uFDA4", "\uFDA4"}, {"\uFDA5", "\uFDA5"}, {"\uFDA6", "\uFDA6"}, {"\uFDA7", "\uFDA7"}, {"\uFDA8", "\uFDA8"}, {"\uFDA9", "\uFDA9"}, {"\uFDAA", "\uFDAA"}, {"\uFDAB", "\uFDAB"}, {"\uFDAC", "\uFDAC"}, {"\uFDAD", "\uFDAD"}, {"\uFDAE", "\uFDAE"}, {"\uFDAF", "\uFDAF"}, {"\uFDB0", "\uFDB0"}, {"\uFDB1", "\uFDB1"}, {"\uFDB2", "\uFDB2"}, {"\uFDB3", "\uFDB3"}, {"\uFDB4", "\uFDB4"}, {"\uFDB5", "\uFDB5"}, {"\uFDB6", "\uFDB6"}, {"\uFDB7", "\uFDB7"}, {"\uFDB8", "\uFDB8"}, {"\uFDB9", "\uFDB9"}, {"\uFDBA", "\uFDBA"}, {"\uFDBB", "\uFDBB"}, {"\uFDBC", "\uFDBC"}, {"\uFDBD", "\uFDBD"}, {"\uFDBE", "\uFDBE"}, {"\uFDBF", "\uFDBF"}, {"\uFDC0", "\uFDC0"}, {"\uFDC1", "\uFDC1"}, {"\uFDC2", "\uFDC2"}, {"\uFDC3", "\uFDC3"}, {"\uFDC4", "\uFDC4"}, {"\uFDC5", "\uFDC5"}, {"\uFDC6", "\uFDC6"}, {"\uFDC7", "\uFDC7"}, {"\uFDF0", "\uFDF0"}, {"\uFDF1", "\uFDF1"}, {"\uFDF2", "\uFDF2"}, {"\uFDF3", "\uFDF3"}, {"\uFDF4", "\uFDF4"}, {"\uFDF5", "\uFDF5"}, {"\uFDF6", "\uFDF6"}, {"\uFDF7", "\uFDF7"}, {"\uFDF8", "\uFDF8"}, {"\uFDF9", "\uFDF9"}, {"\uFDFA", "\uFDFA"}, {"\uFDFB", "\uFDFB"}, {"\uFDFC", "\uFDFC"}, {"\uFE10", "\uFE10"}, {"\uFE11", "\uFE11"}, {"\uFE12", "\uFE12"}, {"\uFE13", "\uFE13"}, {"\uFE14", "\uFE14"}, {"\uFE15", "\uFE15"}, {"\uFE16", "\uFE16"}, {"\uFE17", "\uFE17"}, {"\uFE18", "\uFE18"}, {"\uFE19", "\uFE19"}, {"\uFE30", "\uFE30"}, {"\uFE31", "\uFE31"}, {"\uFE32", "\uFE32"}, {"\uFE33", "\uFE33"}, {"\uFE34", "\uFE34"}, {"\uFE35", "\uFE35"}, {"\uFE36", "\uFE36"}, {"\uFE37", "\uFE37"}, {"\uFE38", "\uFE38"}, {"\uFE39", "\uFE39"}, {"\uFE3A", "\uFE3A"}, {"\uFE3B", "\uFE3B"}, {"\uFE3C", "\uFE3C"}, {"\uFE3D", "\uFE3D"}, {"\uFE3E", "\uFE3E"}, {"\uFE3F", "\uFE3F"}, {"\uFE40", "\uFE40"}, {"\uFE41", "\uFE41"}, {"\uFE42", "\uFE42"}, {"\uFE43", "\uFE43"}, {"\uFE44", "\uFE44"}, {"\uFE47", "\uFE47"}, {"\uFE48", "\uFE48"}, {"\uFE49", "\uFE49"}, {"\uFE4A", "\uFE4A"}, {"\uFE4B", "\uFE4B"}, {"\uFE4C", "\uFE4C"}, {"\uFE4D", "\uFE4D"}, {"\uFE4E", "\uFE4E"}, {"\uFE4F", "\uFE4F"}, {"\uFE50", "\uFE50"}, {"\uFE51", "\uFE51"}, {"\uFE52", "\uFE52"}, {"\uFE54", "\uFE54"}, {"\uFE55", "\uFE55"}, {"\uFE56", "\uFE56"}, {"\uFE57", "\uFE57"}, {"\uFE58", "\uFE58"}, {"\uFE59", "\uFE59"}, {"\uFE5A", "\uFE5A"}, {"\uFE5B", "\uFE5B"}, {"\uFE5C", "\uFE5C"}, {"\uFE5D", "\uFE5D"}, {"\uFE5E", "\uFE5E"}, {"\uFE5F", "\uFE5F"}, {"\uFE60", "\uFE60"}, {"\uFE61", "\uFE61"}, {"\uFE62", "\uFE62"}, {"\uFE63", "\uFE63"}, {"\uFE64", "\uFE64"}, {"\uFE65", "\uFE65"}, {"\uFE66", "\uFE66"}, {"\uFE68", "\uFE68"}, {"\uFE69", "\uFE69"}, {"\uFE6A", "\uFE6A"}, {"\uFE6B", "\uFE6B"}, {"\uFE70", "\uFE70"}, {"\uFE71", "\uFE71"}, {"\uFE72", "\uFE72"}, {"\uFE74", "\uFE74"}, {"\uFE76", "\uFE76"}, {"\uFE77", "\uFE77"}, {"\uFE78", "\uFE78"}, {"\uFE79", "\uFE79"}, {"\uFE7A", "\uFE7A"}, {"\uFE7B", "\uFE7B"}, {"\uFE7C", "\uFE7C"}, {"\uFE7D", "\uFE7D"}, {"\uFE7E", "\uFE7E"}, {"\uFE7F", "\uFE7F"}, {"\uFE80", "\uFE80"}, {"\uFE81", "\uFE81"}, {"\uFE82", "\uFE82"}, {"\uFE83", "\uFE83"}, {"\uFE84", "\uFE84"}, {"\uFE85", "\uFE85"}, {"\uFE86", "\uFE86"}, {"\uFE87", "\uFE87"}, {"\uFE88", "\uFE88"}, {"\uFE89", "\uFE89"}, {"\uFE8A", "\uFE8A"}, {"\uFE8B", "\uFE8B"}, {"\uFE8C", "\uFE8C"}, {"\uFE8D", "\uFE8D"}, {"\uFE8E", "\uFE8E"}, {"\uFE8F", "\uFE8F"}, {"\uFE90", "\uFE90"}, {"\uFE91", "\uFE91"}, {"\uFE92", "\uFE92"}, {"\uFE93", "\uFE93"}, {"\uFE94", "\uFE94"}, {"\uFE95", "\uFE95"}, {"\uFE96", "\uFE96"}, {"\uFE97", "\uFE97"}, {"\uFE98", "\uFE98"}, {"\uFE99", "\uFE99"}, {"\uFE9A", "\uFE9A"}, {"\uFE9B", "\uFE9B"}, {"\uFE9C", "\uFE9C"}, {"\uFE9D", "\uFE9D"}, {"\uFE9E", "\uFE9E"}, {"\uFE9F", "\uFE9F"}, {"\uFEA0", "\uFEA0"}, {"\uFEA1", "\uFEA1"}, {"\uFEA2", "\uFEA2"}, {"\uFEA3", "\uFEA3"}, {"\uFEA4", "\uFEA4"}, {"\uFEA5", "\uFEA5"}, {"\uFEA6", "\uFEA6"}, {"\uFEA7", "\uFEA7"}, {"\uFEA8", "\uFEA8"}, {"\uFEA9", "\uFEA9"}, {"\uFEAA", "\uFEAA"}, {"\uFEAB", "\uFEAB"}, {"\uFEAC", "\uFEAC"}, {"\uFEAD", "\uFEAD"}, {"\uFEAE", "\uFEAE"}, {"\uFEAF", "\uFEAF"}, {"\uFEB0", "\uFEB0"}, {"\uFEB1", "\uFEB1"}, {"\uFEB2", "\uFEB2"}, {"\uFEB3", "\uFEB3"}, {"\uFEB4", "\uFEB4"}, {"\uFEB5", "\uFEB5"}, {"\uFEB6", "\uFEB6"}, {"\uFEB7", "\uFEB7"}, {"\uFEB8", "\uFEB8"}, {"\uFEB9", "\uFEB9"}, {"\uFEBA", "\uFEBA"}, {"\uFEBB", "\uFEBB"}, {"\uFEBC", "\uFEBC"}, {"\uFEBD", "\uFEBD"}, {"\uFEBE", "\uFEBE"}, {"\uFEBF", "\uFEBF"}, {"\uFEC0", "\uFEC0"}, {"\uFEC1", "\uFEC1"}, {"\uFEC2", "\uFEC2"}, {"\uFEC3", "\uFEC3"}, {"\uFEC4", "\uFEC4"}, {"\uFEC5", "\uFEC5"}, {"\uFEC6", "\uFEC6"}, {"\uFEC7", "\uFEC7"}, {"\uFEC8", "\uFEC8"}, {"\uFEC9", "\uFEC9"}, {"\uFECA", "\uFECA"}, {"\uFECB", "\uFECB"}, {"\uFECC", "\uFECC"}, {"\uFECD", "\uFECD"}, {"\uFECE", "\uFECE"}, {"\uFECF", "\uFECF"}, {"\uFED0", "\uFED0"}, {"\uFED1", "\uFED1"}, {"\uFED2", "\uFED2"}, {"\uFED3", "\uFED3"}, {"\uFED4", "\uFED4"}, {"\uFED5", "\uFED5"}, {"\uFED6", "\uFED6"}, {"\uFED7", "\uFED7"}, {"\uFED8", "\uFED8"}, {"\uFED9", "\uFED9"}, {"\uFEDA", "\uFEDA"}, {"\uFEDB", "\uFEDB"}, {"\uFEDC", "\uFEDC"}, {"\uFEDD", "\uFEDD"}, {"\uFEDE", "\uFEDE"}, {"\uFEDF", "\uFEDF"}, {"\uFEE0", "\uFEE0"}, {"\uFEE1", "\uFEE1"}, {"\uFEE2", "\uFEE2"}, {"\uFEE3", "\uFEE3"}, {"\uFEE4", "\uFEE4"}, {"\uFEE5", "\uFEE5"}, {"\uFEE6", "\uFEE6"}, {"\uFEE7", "\uFEE7"}, {"\uFEE8", "\uFEE8"}, {"\uFEE9", "\uFEE9"}, {"\uFEEA", "\uFEEA"}, {"\uFEEB", "\uFEEB"}, {"\uFEEC", "\uFEEC"}, {"\uFEED", "\uFEED"}, {"\uFEEE", "\uFEEE"}, {"\uFEEF", "\uFEEF"}, {"\uFEF0", "\uFEF0"}, {"\uFEF1", "\uFEF1"}, {"\uFEF2", "\uFEF2"}, {"\uFEF3", "\uFEF3"}, {"\uFEF4", "\uFEF4"}, {"\uFEF5", "\uFEF5"}, {"\uFEF6", "\uFEF6"}, {"\uFEF7", "\uFEF7"}, {"\uFEF8", "\uFEF8"}, {"\uFEF9", "\uFEF9"}, {"\uFEFA", "\uFEFA"}, {"\uFEFB", "\uFEFB"}, {"\uFEFC", "\uFEFC"}, {"\uFF01", "\uFF01"}, {"\uFF02", "\uFF02"}, {"\uFF03", "\uFF03"}, {"\uFF04", "\uFF04"}, {"\uFF05", "\uFF05"}, {"\uFF06", "\uFF06"}, {"\uFF07", "\uFF07"}, {"\uFF08", "\uFF08"}, {"\uFF09", "\uFF09"}, {"\uFF0A", "\uFF0A"}, {"\uFF0B", "\uFF0B"}, {"\uFF0C", "\uFF0C"}, {"\uFF0D", "\uFF0D"}, {"\uFF0E", "\uFF0E"}, {"\uFF0F", "\uFF0F"}, {"\uFF10", "\uFF10"}, {"\uFF11", "\uFF11"}, {"\uFF12", "\uFF12"}, {"\uFF13", "\uFF13"}, {"\uFF14", "\uFF14"}, {"\uFF15", "\uFF15"}, {"\uFF16", "\uFF16"}, {"\uFF17", "\uFF17"}, {"\uFF18", "\uFF18"}, {"\uFF19", "\uFF19"}, {"\uFF1A", "\uFF1A"}, {"\uFF1B", "\uFF1B"}, {"\uFF1C", "\uFF1C"}, {"\uFF1D", "\uFF1D"}, {"\uFF1E", "\uFF1E"}, {"\uFF1F", "\uFF1F"}, {"\uFF20", "\uFF20"}, {"\uFF21", "\uFF21"}, {"\uFF22", "\uFF22"}, {"\uFF23", "\uFF23"}, {"\uFF24", "\uFF24"}, {"\uFF25", "\uFF25"}, {"\uFF26", "\uFF26"}, {"\uFF27", "\uFF27"}, {"\uFF28", "\uFF28"}, {"\uFF29", "\uFF29"}, {"\uFF2A", "\uFF2A"}, {"\uFF2B", "\uFF2B"}, {"\uFF2C", "\uFF2C"}, {"\uFF2D", "\uFF2D"}, {"\uFF2E", "\uFF2E"}, {"\uFF2F", "\uFF2F"}, {"\uFF30", "\uFF30"}, {"\uFF31", "\uFF31"}, {"\uFF32", "\uFF32"}, {"\uFF33", "\uFF33"}, {"\uFF34", "\uFF34"}, {"\uFF35", "\uFF35"}, {"\uFF36", "\uFF36"}, {"\uFF37", "\uFF37"}, {"\uFF38", "\uFF38"}, {"\uFF39", "\uFF39"}, {"\uFF3A", "\uFF3A"}, {"\uFF3B", "\uFF3B"}, {"\uFF3C", "\uFF3C"}, {"\uFF3D", "\uFF3D"}, {"\uFF3E", "\uFF3E"}, {"\uFF3F", "\uFF3F"}, {"\uFF40", "\uFF40"}, {"\uFF41", "\uFF41"}, {"\uFF42", "\uFF42"}, {"\uFF43", "\uFF43"}, {"\uFF44", "\uFF44"}, {"\uFF45", "\uFF45"}, {"\uFF46", "\uFF46"}, {"\uFF47", "\uFF47"}, {"\uFF48", "\uFF48"}, {"\uFF49", "\uFF49"}, {"\uFF4A", "\uFF4A"}, {"\uFF4B", "\uFF4B"}, {"\uFF4C", "\uFF4C"}, {"\uFF4D", "\uFF4D"}, {"\uFF4E", "\uFF4E"}, {"\uFF4F", "\uFF4F"}, {"\uFF50", "\uFF50"}, {"\uFF51", "\uFF51"}, {"\uFF52", "\uFF52"}, {"\uFF53", "\uFF53"}, {"\uFF54", "\uFF54"}, {"\uFF55", "\uFF55"}, {"\uFF56", "\uFF56"}, {"\uFF57", "\uFF57"}, {"\uFF58", "\uFF58"}, {"\uFF59", "\uFF59"}, {"\uFF5A", "\uFF5A"}, {"\uFF5B", "\uFF5B"}, {"\uFF5C", "\uFF5C"}, {"\uFF5D", "\uFF5D"}, {"\uFF5E", "\uFF5E"}, {"\uFF5F", "\uFF5F"}, {"\uFF60", "\uFF60"}, {"\uFF61", "\uFF61"}, {"\uFF62", "\uFF62"}, {"\uFF63", "\uFF63"}, {"\uFF64", "\uFF64"}, {"\uFF65", "\uFF65"}, {"\uFF66", "\uFF66"}, {"\uFF67", "\uFF67"}, {"\uFF68", "\uFF68"}, {"\uFF69", "\uFF69"}, {"\uFF6A", "\uFF6A"}, {"\uFF6B", "\uFF6B"}, {"\uFF6C", "\uFF6C"}, {"\uFF6D", "\uFF6D"}, {"\uFF6E", "\uFF6E"}, {"\uFF6F", "\uFF6F"}, {"\uFF70", "\uFF70"}, {"\uFF71", "\uFF71"}, {"\uFF72", "\uFF72"}, {"\uFF73", "\uFF73"}, {"\uFF74", "\uFF74"}, {"\uFF75", "\uFF75"}, {"\uFF76", "\uFF76"}, {"\uFF77", "\uFF77"}, {"\uFF78", "\uFF78"}, {"\uFF79", "\uFF79"}, {"\uFF7A", "\uFF7A"}, {"\uFF7B", "\uFF7B"}, {"\uFF7C", "\uFF7C"}, {"\uFF7D", "\uFF7D"}, {"\uFF7E", "\uFF7E"}, {"\uFF7F", "\uFF7F"}, {"\uFF80", "\uFF80"}, {"\uFF81", "\uFF81"}, {"\uFF82", "\uFF82"}, {"\uFF83", "\uFF83"}, {"\uFF84", "\uFF84"}, {"\uFF85", "\uFF85"}, {"\uFF86", "\uFF86"}, {"\uFF87", "\uFF87"}, {"\uFF88", "\uFF88"}, {"\uFF89", "\uFF89"}, {"\uFF8A", "\uFF8A"}, {"\uFF8B", "\uFF8B"}, {"\uFF8C", "\uFF8C"}, {"\uFF8D", "\uFF8D"}, {"\uFF8E", "\uFF8E"}, {"\uFF8F", "\uFF8F"}, {"\uFF90", "\uFF90"}, {"\uFF91", "\uFF91"}, {"\uFF92", "\uFF92"}, {"\uFF93", "\uFF93"}, {"\uFF94", "\uFF94"}, {"\uFF95", "\uFF95"}, {"\uFF96", "\uFF96"}, {"\uFF97", "\uFF97"}, {"\uFF98", "\uFF98"}, {"\uFF99", "\uFF99"}, {"\uFF9A", "\uFF9A"}, {"\uFF9B", "\uFF9B"}, {"\uFF9C", "\uFF9C"}, {"\uFF9D", "\uFF9D"}, {"\uFF9E", "\uFF9E"}, {"\uFF9F", "\uFF9F"}, {"\uFFA0", "\uFFA0"}, {"\uFFA1", "\uFFA1"}, {"\uFFA2", "\uFFA2"}, {"\uFFA3", "\uFFA3"}, {"\uFFA4", "\uFFA4"}, {"\uFFA5", "\uFFA5"}, {"\uFFA6", "\uFFA6"}, {"\uFFA7", "\uFFA7"}, {"\uFFA8", "\uFFA8"}, {"\uFFA9", "\uFFA9"}, {"\uFFAA", "\uFFAA"}, {"\uFFAB", "\uFFAB"}, {"\uFFAC", "\uFFAC"}, {"\uFFAD", "\uFFAD"}, {"\uFFAE", "\uFFAE"}, {"\uFFAF", "\uFFAF"}, {"\uFFB0", "\uFFB0"}, {"\uFFB1", "\uFFB1"}, {"\uFFB2", "\uFFB2"}, {"\uFFB3", "\uFFB3"}, {"\uFFB4", "\uFFB4"}, {"\uFFB5", "\uFFB5"}, {"\uFFB6", "\uFFB6"}, {"\uFFB7", "\uFFB7"}, {"\uFFB8", "\uFFB8"}, {"\uFFB9", "\uFFB9"}, {"\uFFBA", "\uFFBA"}, {"\uFFBB", "\uFFBB"}, {"\uFFBC", "\uFFBC"}, {"\uFFBD", "\uFFBD"}, {"\uFFBE", "\uFFBE"}, {"\uFFC2", "\uFFC2"}, {"\uFFC3", "\uFFC3"}, {"\uFFC4", "\uFFC4"}, {"\uFFC5", "\uFFC5"}, {"\uFFC6", "\uFFC6"}, {"\uFFC7", "\uFFC7"}, {"\uFFCA", "\uFFCA"}, {"\uFFCB", "\uFFCB"}, {"\uFFCC", "\uFFCC"}, {"\uFFCD", "\uFFCD"}, {"\uFFCE", "\uFFCE"}, {"\uFFCF", "\uFFCF"}, {"\uFFD2", "\uFFD2"}, {"\uFFD3", "\uFFD3"}, {"\uFFD4", "\uFFD4"}, {"\uFFD5", "\uFFD5"}, {"\uFFD6", "\uFFD6"}, {"\uFFD7", "\uFFD7"}, {"\uFFDA", "\uFFDA"}, {"\uFFDB", "\uFFDB"}, {"\uFFDC", "\uFFDC"}, {"\uFFE0", "\uFFE0"}, {"\uFFE1", "\uFFE1"}, {"\uFFE2", "\uFFE2"}, {"\uFFE3", "\uFFE3"}, {"\uFFE4", "\uFFE4"}, {"\uFFE5", "\uFFE5"}, {"\uFFE6", "\uFFE6"}, {"\uFFE8", "\uFFE8"}, {"\uFFE9", "\uFFE9"}, {"\uFFEA", "\uFFEA"}, {"\uFFEB", "\uFFEB"}, {"\uFFEC", "\uFFEC"}, {"\uFFED", "\uFFED"}, {"\uFFEE", "\uFFEE"}, {"\U00010781", "\U00010781"}, {"\U00010782", "\U00010782"}, {"\U00010783", "\U00010783"}, {"\U00010784", "\U00010784"}, {"\U00010785", "\U00010785"}, {"\U00010787", "\U00010787"}, {"\U00010788", "\U00010788"}, {"\U00010789", "\U00010789"}, {"\U0001078A", "\U0001078A"}, {"\U0001078B", "\U0001078B"}, {"\U0001078C", "\U0001078C"}, {"\U0001078D", "\U0001078D"}, {"\U0001078E", "\U0001078E"}, {"\U0001078F", "\U0001078F"}, {"\U00010790", "\U00010790"}, {"\U00010791", "\U00010791"}, {"\U00010792", "\U00010792"}, {"\U00010793", "\U00010793"}, {"\U00010794", "\U00010794"}, {"\U00010795", "\U00010795"}, {"\U00010796", "\U00010796"}, {"\U00010797", "\U00010797"}, {"\U00010798", "\U00010798"}, {"\U00010799", "\U00010799"}, {"\U0001079A", "\U0001079A"}, {"\U0001079B", "\U0001079B"}, {"\U0001079C", "\U0001079C"}, {"\U0001079D", "\U0001079D"}, {"\U0001079E", "\U0001079E"}, {"\U0001079F", "\U0001079F"}, {"\U000107A0", "\U000107A0"}, {"\U000107A1", "\U000107A1"}, {"\U000107A2", "\U000107A2"}, {"\U000107A3", "\U000107A3"}, {"\U000107A4", "\U000107A4"}, {"\U000107A5", "\U000107A5"}, {"\U000107A6", "\U000107A6"}, {"\U000107A7", "\U000107A7"}, {"\U000107A8", "\U000107A8"}, {"\U000107A9", "\U000107A9"}, {"\U000107AA", "\U000107AA"}, {"\U000107AB", "\U000107AB"}, {"\U000107AC", "\U000107AC"}, {"\U000107AD", "\U000107AD"}, {"\U000107AE", "\U000107AE"}, {"\U000107AF", "\U000107AF"}, {"\U000107B0", "\U000107B0"}, {"\U000107B2", "\U000107B2"}, {"\U000107B3", "\U000107B3"}, {"\U000107B4", "\U000107B4"}, {"\U000107B5", "\U000107B5"}, {"\U000107B6", "\U000107B6"}, {"\U000107B7", "\U000107B7"}, {"\U000107B8", "\U000107B8"}, {"\U000107B9", "\U000107B9"}, {"\U000107BA", "\U000107BA"}, {"\U0001109A", "\U0001109A"}, {"\U0001109C", "\U0001109C"}, {"\U000110AB", "\U000110AB"}, {"\U0001112E", "\U0001112E"}, {"\U0001112F", "\U0001112F"}, {"\U0001134B", "\U0001134B"}, {"\U0001134C", "\U0001134C"}, {"\U000114BB", "\U000114BB"}, {"\U000114BC", "\U000114BC"}, {"\U000114BE", "\U000114BE"}, {"\U000115BA", "\U000115BA"}, {"\U000115BB", "\U000115BB"}, {"\U00011938", "\U00011938"}, {"\U0001D15E", "\U0001D157\U0001D165"}, {"\U0001D15F", "\U0001D158\U0001D165"}, {"\U0001D160", "\U0001D158\U0001D165\U0001D16E"}, {"\U0001D161", "\U0001D158\U0001D165\U0001D16F"}, {"\U0001D162", "\U0001D158\U0001D165\U0001D170"}, {"\U0001D163", "\U0001D158\U0001D165\U0001D171"}, {"\U0001D164", "\U0001D158\U0001D165\U0001D172"}, {"\U0001D1BB", "\U0001D1B9\U0001D165"}, {"\U0001D1BC", "\U0001D1BA\U0001D165"}, {"\U0001D1BD", "\U0001D1B9\U0001D165\U0001D16E"}, {"\U0001D1BE", "\U0001D1BA\U0001D165\U0001D16E"}, {"\U0001D1BF", "\U0001D1B9\U0001D165\U0001D16F"}, {"\U0001D1C0", "\U0001D1BA\U0001D165\U0001D16F"}, {"\U0001D400", "\U0001D400"}, {"\U0001D401", "\U0001D401"}, {"\U0001D402", "\U0001D402"}, {"\U0001D403", "\U0001D403"}, {"\U0001D404", "\U0001D404"}, {"\U0001D405", "\U0001D405"}, {"\U0001D406", "\U0001D406"}, {"\U0001D407", "\U0001D407"}, {"\U0001D408", "\U0001D408"}, {"\U0001D409", "\U0001D409"}, {"\U0001D40A", "\U0001D40A"}, {"\U0001D40B", "\U0001D40B"}, {"\U0001D40C", "\U0001D40C"}, {"\U0001D40D", "\U0001D40D"}, {"\U0001D40E", "\U0001D40E"}, {"\U0001D40F", "\U0001D40F"}, {"\U0001D410", "\U0001D410"}, {"\U0001D411", "\U0001D411"}, {"\U0001D412", "\U0001D412"}, {"\U0001D413", "\U0001D413"}, {"\U0001D414", "\U0001D414"}, {"\U0001D415", "\U0001D415"}, {"\U0001D416", "\U0001D416"}, {"\U0001D417", "\U0001D417"}, {"\U0001D418", "\U0001D418"}, {"\U0001D419", "\U0001D419"}, {"\U0001D41A", "\U0001D41A"}, {"\U0001D41B", "\U0001D41B"}, {"\U0001D41C", "\U0001D41C"}, {"\U0001D41D", "\U0001D41D"}, {"\U0001D41E", "\U0001D41E"}, {"\U0001D41F", "\U0001D41F"}, {"\U0001D420", "\U0001D420"}, {"\U0001D421", "\U0001D421"}, {"\U0001D422", "\U0001D422"}, {"\U0001D423", "\U0001D423"}, {"\U0001D424", "\U0001D424"}, {"\U0001D425", "\U0001D425"}, {"\U0001D426", "\U0001D426"}, {"\U0001D427", "\U0001D427"}, {"\U0001D428", "\U0001D428"}, {"\U0001D429", "\U0001D429"}, {"\U0001D42A", "\U0001D42A"}, {"\U0001D42B", "\U0001D42B"}, {"\U0001D42C", "\U0001D42C"}, {"\U0001D42D", "\U0001D42D"}, {"\U0001D42E", "\U0001D42E"}, {"\U0001D42F", "\U0001D42F"}, {"\U0001D430", "\U0001D430"}, {"\U0001D431", "\U0001D431"}, {"\U0001D432", "\U0001D432"}, {"\U0001D433", "\U0001D433"}, {"\U0001D434", "\U0001D434"}, {"\U0001D435", "\U0001D435"}, {"\U0001D436", "\U0001D436"}, {"\U0001D437", "\U0001D437"}, {"\U0001D438", "\U0001D438"}, {"\U0001D439", "\U0001D439"}, {"\U0001D43A", "\U0001D43A"}, {"\U0001D43B", "\U0001D43B"}, {"\U0001D43C", "\U0001D43C"}, {"\U0001D43D", "\U0001D43D"}, {"\U0001D43E", "\U0001D43E"}, {"\U0001D43F", "\U0001D43F"}, {"\U0001D440", "\U0001D440"}, {"\U0001D441", "\U0001D441"}, {"\U0001D442", "\U0001D442"}, {"\U0001D443", "\U0001D443"}, {"\U0001D444", "\U0001D444"}, {"\U0001D445", "\U0001D445"}, {"\U0001D446", "\U0001D446"}, {"\U0001D447", "\U0001D447"}, {"\U0001D448", "\U0001D448"}, {"\U0001D449", "\U0001D449"}, {"\U0001D44A", "\U0001D44A"}, {"\U0001D44B", "\U0001D44B"}, {"\U0001D44C", "\U0001D44C"}, {"\U0001D44D", "\U0001D44D"}, {"\U0001D44E", "\U0001D44E"}, {"\U0001D44F", "\U0001D44F"}, {"\U0001D450", "\U0001D450"}, {"\U0001D451", "\U0001D451"}, {"\U0001D452", "\U0001D452"}, {"\U0001D453", "\U0001D453"}, {"\U0001D454", "\U0001D454"}, {"\U0001D456", "\U0001D456"}, {"\U0001D457", "\U0001D457"}, {"\U0001D458", "\U0001D458"}, {"\U0001D459", "\U0001D459"}, {"\U0001D45A", "\U0001D45A"}, {"\U0001D45B", "\U0001D45B"}, {"\U0001D45C", "\U0001D45C"}, {"\U0001D45D", "\U0001D45D"}, {"\U0001D45E", "\U0001D45E"}, {"\U0001D45F", "\U0001D45F"}, {"\U0001D460", "\U0001D460"}, {"\U0001D461", "\U0001D461"}, {"\U0001D462", "\U0001D462"}, {"\U0001D463", "\U0001D463"}, {"\U0001D464", "\U0001D464"}, {"\U0001D465", "\U0001D465"}, {"\U0001D466", "\U0001D466"}, {"\U0001D467", "\U0001D467"}, {"\U0001D468", "\U0001D468"}, {"\U0001D469", "\U0001D469"}, {"\U0001D46A", "\U0001D46A"}, {"\U0001D46B", "\U0001D46B"}, {"\U0001D46C", "\U0001D46C"}, {"\U0001D46D", "\U0001D46D"}, {"\U0001D46E", "\U0001D46E"}, {"\U0001D46F", "\U0001D46F"}, {"\U0001D470", "\U0001D470"}, {"\U0001D471", "\U0001D471"}, {"\U0001D472", "\U0001D472"}, {"\U0001D473", "\U0001D473"}, {"\U0001D474", "\U0001D474"}, {"\U0001D475", "\U0001D475"}, {"\U0001D476", "\U0001D476"}, {"\U0001D477", "\U0001D477"}, {"\U0001D478", "\U0001D478"}, {"\U0001D479", "\U0001D479"}, {"\U0001D47A", "\U0001D47A"}, {"\U0001D47B", "\U0001D47B"}, {"\U0001D47C", "\U0001D47C"}, {"\U0001D47D", "\U0001D47D"}, {"\U0001D47E", "\U0001D47E"}, {"\U0001D47F", "\U0001D47F"}, {"\U0001D480", "\U0001D480"}, {"\U0001D481", "\U0001D481"}, {"\U0001D482", "\U0001D482"}, {"\U0001D483", "\U0001D483"}, {"\U0001D484", "\U0001D484"}, {"\U0001D485", "\U0001D485"}, {"\U0001D486", "\U0001D486"}, {"\U0001D487", "\U0001D487"}, {"\U0001D488", "\U0001D488"}, {"\U0001D489", "\U0001D489"}, {"\U0001D48A", "\U0001D48A"}, {"\U0001D48B", "\U0001D48B"}, {"\U0001D48C", "\U0001D48C"}, {"\U0001D48D", "\U0001D48D"}, {"\U0001D48E", "\U0001D48E"}, {"\U0001D48F", "\U0001D48F"}, {"\U0001D490", "\U0001D490"}, {"\U0001D491", "\U0001D491"}, {"\U0001D492", "\U0001D492"}, {"\U0001D493", "\U0001D493"}, {"\U0001D494", "\U0001D494"}, {"\U0001D495", "\U0001D495"}, {"\U0001D496", "\U0001D496"}, {"\U0001D497", "\U0001D497"}, {"\U0001D498", "\U0001D498"}, {"\U0001D499", "\U0001D499"}, {"\U0001D49A", "\U0001D49A"}, {"\U0001D49B", "\U0001D49B"}, {"\U0001D49C", "\U0001D49C"}, {"\U0001D49E", "\U0001D49E"}, {"\U0001D49F", "\U0001D49F"}, {"\U0001D4A2", "\U0001D4A2"}, {"\U0001D4A5", "\U0001D4A5"}, {"\U0001D4A6", "\U0001D4A6"}, {"\U0001D4A9", "\U0001D4A9"}, {"\U0001D4AA", "\U0001D4AA"}, {"\U0001D4AB", "\U0001D4AB"}, {"\U0001D4AC", "\U0001D4AC"}, {"\U0001D4AE", "\U0001D4AE"}, {"\U0001D4AF", "\U0001D4AF"}, {"\U0001D4B0", "\U0001D4B0"}, {"\U0001D4B1", "\U0001D4B1"}, {"\U0001D4B2", "\U0001D4B2"}, {"\U0001D4B3", "\U0001D4B3"}, {"\U0001D4B4", "\U0001D4B4"}, {"\U0001D4B5", "\U0001D4B5"}, {"\U0001D4B6", "\U0001D4B6"}, {"\U0001D4B7", "\U0001D4B7"}, {"\U0001D4B8", "\U0001D4B8"}, {"\U0001D4B9", "\U0001D4B9"}, {"\U0001D4BB", "\U0001D4BB"}, {"\U0001D4BD", "\U0001D4BD"}, {"\U0001D4BE", "\U0001D4BE"}, {"\U0001D4BF", "\U0001D4BF"}, {"\U0001D4C0", "\U0001D4C0"}, {"\U0001D4C1", "\U0001D4C1"}, {"\U0001D4C2", "\U0001D4C2"}, {"\U0001D4C3", "\U0001D4C3"}, {"\U0001D4C5", "\U0001D4C5"}, {"\U0001D4C6", "\U0001D4C6"}, {"\U0001D4C7", "\U0001D4C7"}, {"\U0001D4C8", "\U0001D4C8"}, {"\U0001D4C9", "\U0001D4C9"}, {"\U0001D4CA", "\U0001D4CA"}, {"\U0001D4CB", "\U0001D4CB"}, {"\U0001D4CC", "\U0001D4CC"}, {"\U0001D4CD", "\U0001D4CD"}, {"\U0001D4CE", "\U0001D4CE"}, {"\U0001D4CF", "\U0001D4CF"}, {"\U0001D4D0", "\U0001D4D0"}, {"\U0001D4D1", "\U0001D4D1"}, {"\U0001D4D2", "\U0001D4D2"}, {"\U0001D4D3", "\U0001D4D3"}, {"\U0001D4D4", "\U0001D4D4"}, {"\U0001D4D5", "\U0001D4D5"}, {"\U0001D4D6", "\U0001D4D6"}, {"\U0001D4D7", "\U0001D4D7"}, {"\U0001D4D8", "\U0001D4D8"}, {"\U0001D4D9", "\U0001D4D9"}, {"\U0001D4DA", "\U0001D4DA"}, {"\U0001D4DB", "\U0001D4DB"}, {"\U0001D4DC", "\U0001D4DC"}, {"\U0001D4DD", "\U0001D4DD"}, {"\U0001D4DE", "\U0001D4DE"}, {"\U0001D4DF", "\U0001D4DF"}, {"\U0001D4E0", "\U0001D4E0"}, {"\U0001D4E1", "\U0001D4E1"}, {"\U0001D4E2", "\U0001D4E2"}, {"\U0001D4E3", "\U0001D4E3"}, {"\U0001D4E4", "\U0001D4E4"}, {"\U0001D4E5", "\U0001D4E5"}, {"\U0001D4E6", "\U0001D4E6"}, {"\U0001D4E7", "\U0001D4E7"}, {"\U0001D4E8", "\U0001D4E8"}, {"\U0001D4E9", "\U0001D4E9"}, {"\U0001D4EA", "\U0001D4EA"}, {"\U0001D4EB", "\U0001D4EB"}, {"\U0001D4EC", "\U0001D4EC"}, {"\U0001D4ED", "\U0001D4ED"}, {"\U0001D4EE", "\U0001D4EE"}, {"\U0001D4EF", "\U0001D4EF"}, {"\U0001D4F0", "\U0001D4F0"}, {"\U0001D4F1", "\U0001D4F1"}, {"\U0001D4F2", "\U0001D4F2"}, {"\U0001D4F3", "\U0001D4F3"}, {"\U0001D4F4", "\U0001D4F4"}, {"\U0001D4F5", "\U0001D4F5"}, {"\U0001D4F6", "\U0001D4F6"}, {"\U0001D4F7", "\U0001D4F7"}, {"\U0001D4F8", "\U0001D4F8"}, {"\U0001D4F9", "\U0001D4F9"}, {"\U0001D4FA", "\U0001D4FA"}, {"\U0001D4FB", "\U0001D4FB"}, {"\U0001D4FC", "\U0001D4FC"}, {"\U0001D4FD", "\U0001D4FD"}, {"\U0001D4FE", "\U0001D4FE"}, {"\U0001D4FF", "\U0001D4FF"}, {"\U0001D500", "\U0001D500"}, {"\U0001D501", "\U0001D501"}, {"\U0001D502", "\U0001D502"}, {"\U0001D503", "\U0001D503"}, {"\U0001D504", "\U0001D504"}, {"\U0001D505", "\U0001D505"}, {"\U0001D507", "\U0001D507"}, {"\U0001D508", "\U0001D508"}, {"\U0001D509", "\U0001D509"}, {"\U0001D50A", "\U0001D50A"}, {"\U0001D50D", "\U0001D50D"}, {"\U0001D50E", "\U0001D50E"}, {"\U0001D50F", "\U0001D50F"}, {"\U0001D510", "\U0001D510"}, {"\U0001D511", "\U0001D511"}, {"\U0001D512", "\U0001D512"}, {"\U0001D513", "\U0001D513"}, {"\U0001D514", "\U0001D514"}, {"\U0001D516", "\U0001D516"}, {"\U0001D517", "\U0001D517"}, {"\U0001D518", "\U0001D518"}, {"\U0001D519", "\U0001D519"}, {"\U0001D51A", "\U0001D51A"}, {"\U0001D51B", "\U0001D51B"}, {"\U0001D51C", "\U0001D51C"}, {"\U0001D51E", "\U0001D51E"}, {"\U0001D51F", "\U0001D51F"}, {"\U0001D520", "\U0001D520"}, {"\U0001D521", "\U0001D521"}, {"\U0001D522", "\U0001D522"}, {"\U0001D523", "\U0001D523"}, {"\U0001D524", "\U0001D524"}, {"\U0001D525", "\U0001D525"}, {"\U0001D526", "\U0001D526"}, {"\U0001D527", "\U0001D527"}, {"\U0001D528", "\U0001D528"}, {"\U0001D529", "\U0001D529"}, {"\U0001D52A", "\U0001D52A"}, {"\U0001D52B", "\U0001D52B"}, {"\U0001D52C", "\U0001D52C"}, {"\U0001D52D", "\U0001D52D"}, {"\U0001D52E", "\U0001D52E"}, {"\U0001D52F", "\U0001D52F"}, {"\U0001D530", "\U0001D530"}, {"\U0001D531", "\U0001D531"}, {"\U0001D532", "\U0001D532"}, {"\U0001D533", "\U0001D533"}, {"\U0001D534", "\U0001D534"}, {"\U0001D535", "\U0001D535"}, {"\U0001D536", "\U0001D536"}, {"\U0001D537", "\U0001D537"}, {"\U0001D538", "\U0001D538"}, {"\U0001D539", "\U0001D539"}, {"\U0001D53B", "\U0001D53B"}, {"\U0001D53C", "\U0001D53C"}, {"\U0001D53D", "\U0001D53D"}, {"\U0001D53E", "\U0001D53E"}, {"\U0001D540", "\U0001D540"}, {"\U0001D541", "\U0001D541"}, {"\U0001D542", "\U0001D542"}, {"\U0001D543", "\U0001D543"}, {"\U0001D544", "\U0001D544"}, {"\U0001D546", "\U0001D546"}, {"\U0001D54A", "\U0001D54A"}, {"\U0001D54B", "\U0001D54B"}, {"\U0001D54C", "\U0001D54C"}, {"\U0001D54D", "\U0001D54D"}, {"\U0001D54E", "\U0001D54E"}, {"\U0001D54F", "\U0001D54F"}, {"\U0001D550", "\U0001D550"}, {"\U0001D552", "\U0001D552"}, {"\U0001D553", "\U0001D553"}, {"\U0001D554", "\U0001D554"}, {"\U0001D555", "\U0001D555"}, {"\U0001D556", "\U0001D556"}, {"\U0001D557", "\U0001D557"}, {"\U0001D558", "\U0001D558"}, {"\U0001D559", "\U0001D559"}, {"\U0001D55A", "\U0001D55A"}, {"\U0001D55B", "\U0001D55B"}, {"\U0001D55C", "\U0001D55C"}, {"\U0001D55D", "\U0001D55D"}, {"\U0001D55E", "\U0001D55E"}, {"\U0001D55F", "\U0001D55F"}, {"\U0001D560", "\U0001D560"}, {"\U0001D561", "\U0001D561"}, {"\U0001D562", "\U0001D562"}, {"\U0001D563", "\U0001D563"}, {"\U0001D564", "\U0001D564"}, {"\U0001D565", "\U0001D565"}, {"\U0001D566", "\U0001D566"}, {"\U0001D567", "\U0001D567"}, {"\U0001D568", "\U0001D568"}, {"\U0001D569", "\U0001D569"}, {"\U0001D56A", "\U0001D56A"}, {"\U0001D56B", "\U0001D56B"}, {"\U0001D56C", "\U0001D56C"}, {"\U0001D56D", "\U0001D56D"}, {"\U0001D56E", "\U0001D56E"}, {"\U0001D56F", "\U0001D56F"}, {"\U0001D570", "\U0001D570"}, {"\U0001D571", "\U0001D571"}, {"\U0001D572", "\U0001D572"}, {"\U0001D573", "\U0001D573"}, {"\U0001D574", "\U0001D574"}, {"\U0001D575", "\U0001D575"}, {"\U0001D576", "\U0001D576"}, {"\U0001D577", "\U0001D577"}, {"\U0001D578", "\U0001D578"}, {"\U0001D579", "\U0001D579"}, {"\U0001D57A", "\U0001D57A"}, {"\U0001D57B", "\U0001D57B"}, {"\U0001D57C", "\U0001D57C"}, {"\U0001D57D", "\U0001D57D"}, {"\U0001D57E", "\U0001D57E"}, {"\U0001D57F", "\U0001D57F"}, {"\U0001D580", "\U0001D580"}, {"\U0001D581", "\U0001D581"}, {"\U0001D582", "\U0001D582"}, {"\U0001D583", "\U0001D583"}, {"\U0001D584", "\U0001D584"}, {"\U0001D585", "\U0001D585"}, {"\U0001D586", "\U0001D586"}, {"\U0001D587", "\U0001D587"}, {"\U0001D588", "\U0001D588"}, {"\U0001D589", "\U0001D589"}, {"\U0001D58A", "\U0001D58A"}, {"\U0001D58B", "\U0001D58B"}, {"\U0001D58C", "\U0001D58C"}, {"\U0001D58D", "\U0001D58D"}, {"\U0001D58E", "\U0001D58E"}, {"\U0001D58F", "\U0001D58F"}, {"\U0001D590", "\U0001D590"}, {"\U0001D591", "\U0001D591"}, {"\U0001D592", "\U0001D592"}, {"\U0001D593", "\U0001D593"}, {"\U0001D594", "\U0001D594"}, {"\U0001D595", "\U0001D595"}, {"\U0001D596", "\U0001D596"}, {"\U0001D597", "\U0001D597"}, {"\U0001D598", "\U0001D598"}, {"\U0001D599", "\U0001D599"}, {"\U0001D59A", "\U0001D59A"}, {"\U0001D59B", "\U0001D59B"}, {"\U0001D59C", "\U0001D59C"}, {"\U0001D59D", "\U0001D59D"}, {"\U0001D59E", "\U0001D59E"}, {"\U0001D59F", "\U0001D59F"}, {"\U0001D5A0", "\U0001D5A0"}, {"\U0001D5A1", "\U0001D5A1"}, {"\U0001D5A2", "\U0001D5A2"}, {"\U0001D5A3", "\U0001D5A3"}, {"\U0001D5A4", "\U0001D5A4"}, {"\U0001D5A5", "\U0001D5A5"}, {"\U0001D5A6", "\U0001D5A6"}, {"\U0001D5A7", "\U0001D5A7"}, {"\U0001D5A8", "\U0001D5A8"}, {"\U0001D5A9", "\U0001D5A9"}, {"\U0001D5AA", "\U0001D5AA"}, {"\U0001D5AB", "\U0001D5AB"}, {"\U0001D5AC", "\U0001D5AC"}, {"\U0001D5AD", "\U0001D5AD"}, {"\U0001D5AE", "\U0001D5AE"}, {"\U0001D5AF", "\U0001D5AF"}, {"\U0001D5B0", "\U0001D5B0"}, {"\U0001D5B1", "\U0001D5B1"}, {"\U0001D5B2", "\U0001D5B2"}, {"\U0001D5B3", "\U0001D5B3"}, {"\U0001D5B4", "\U0001D5B4"}, {"\U0001D5B5", "\U0001D5B5"}, {"\U0001D5B6", "\U0001D5B6"}, {"\U0001D5B7", "\U0001D5B7"}, {"\U0001D5B8", "\U0001D5B8"}, {"\U0001D5B9", "\U0001D5B9"}, {"\U0001D5BA", "\U0001D5BA"}, {"\U0001D5BB", "\U0001D5BB"}, {"\U0001D5BC", "\U0001D5BC"}, {"\U0001D5BD", "\U0001D5BD"}, {"\U0001D5BE", "\U0001D5BE"}, {"\U0001D5BF", "\U0001D5BF"}, {"\U0001D5C0", "\U0001D5C0"}, {"\U0001D5C1", "\U0001D5C1"}, {"\U0001D5C2", "\U0001D5C2"}, {"\U0001D5C3", "\U0001D5C3"}, {"\U0001D5C4", "\U0001D5C4"}, {"\U0001D5C5", "\U0001D5C5"}, {"\U0001D5C6", "\U0001D5C6"}, {"\U0001D5C7", "\U0001D5C7"}, {"\U0001D5C8", "\U0001D5C8"}, {"\U0001D5C9", "\U0001D5C9"}, {"\U0001D5CA", "\U0001D5CA"}, {"\U0001D5CB", "\U0001D5CB"}, {"\U0001D5CC", "\U0001D5CC"}, {"\U0001D5CD", "\U0001D5CD"}, {"\U0001D5CE", "\U0001D5CE"}, {"\U0001D5CF", "\U0001D5CF"}, {"\U0001D5D0", "\U0001D5D0"}, {"\U0001D5D1", "\U0001D5D1"}, {"\U0001D5D2", "\U0001D5D2"}, {"\U0001D5D3", "\U0001D5D3"}, {"\U0001D5D4", "\U0001D5D4"}, {"\U0001D5D5", "\U0001D5D5"}, {"\U0001D5D6", "\U0001D5D6"}, {"\U0001D5D7", "\U0001D5D7"}, {"\U0001D5D8", "\U0001D5D8"}, {"\U0001D5D9", "\U0001D5D9"}, {"\U0001D5DA", "\U0001D5DA"}, {"\U0001D5DB", "\U0001D5DB"}, {"\U0001D5DC", "\U0001D5DC"}, {"\U0001D5DD", "\U0001D5DD"}, {"\U0001D5DE", "\U0001D5DE"}, {"\U0001D5DF", "\U0001D5DF"}, {"\U0001D5E0", "\U0001D5E0"}, {"\U0001D5E1", "\U0001D5E1"}, {"\U0001D5E2", "\U0001D5E2"}, {"\U0001D5E3", "\U0001D5E3"}, {"\U0001D5E4", "\U0001D5E4"}, {"\U0001D5E5", "\U0001D5E5"}, {"\U0001D5E6", "\U0001D5E6"}, {"\U0001D5E7", "\U0001D5E7"}, {"\U0001D5E8", "\U0001D5E8"}, {"\U0001D5E9", "\U0001D5E9"}, {"\U0001D5EA", "\U0001D5EA"}, {"\U0001D5EB", "\U0001D5EB"}, {"\U0001D5EC", "\U0001D5EC"}, {"\U0001D5ED", "\U0001D5ED"}, {"\U0001D5EE", "\U0001D5EE"}, {"\U0001D5EF", "\U0001D5EF"}, {"\U0001D5F0", "\U0001D5F0"}, {"\U0001D5F1", "\U0001D5F1"}, {"\U0001D5F2", "\U0001D5F2"}, {"\U0001D5F3", "\U0001D5F3"}, {"\U0001D5F4", "\U0001D5F4"}, {"\U0001D5F5", "\U0001D5F5"}, {"\U0001D5F6", "\U0001D5F6"}, {"\U0001D5F7", "\U0001D5F7"}, {"\U0001D5F8", "\U0001D5F8"}, {"\U0001D5F9", "\U0001D5F9"}, {"\U0001D5FA", "\U0001D5FA"}, {"\U0001D5FB", "\U0001D5FB"}, {"\U0001D5FC", "\U0001D5FC"}, {"\U0001D5FD", "\U0001D5FD"}, {"\U0001D5FE", "\U0001D5FE"}, {"\U0001D5FF", "\U0001D5FF"}, {"\U0001D600", "\U0001D600"}, {"\U0001D601", "\U0001D601"}, {"\U0001D602", "\U0001D602"}, {"\U0001D603", "\U0001D603"}, {"\U0001D604", "\U0001D604"}, {"\U0001D605", "\U0001D605"}, {"\U0001D606", "\U0001D606"}, {"\U0001D607", "\U0001D607"}, {"\U0001D608", "\U0001D608"}, {"\U0001D609", "\U0001D609"}, {"\U0001D60A", "\U0001D60A"}, {"\U0001D60B", "\U0001D60B"}, {"\U0001D60C", "\U0001D60C"}, {"\U0001D60D", "\U0001D60D"}, {"\U0001D60E", "\U0001D60E"}, {"\U0001D60F", "\U0001D60F"}, {"\U0001D610", "\U0001D610"}, {"\U0001D611", "\U0001D611"}, {"\U0001D612", "\U0001D612"}, {"\U0001D613", "\U0001D613"}, {"\U0001D614", "\U0001D614"}, {"\U0001D615", "\U0001D615"}, {"\U0001D616", "\U0001D616"}, {"\U0001D617", "\U0001D617"}, {"\U0001D618", "\U0001D618"}, {"\U0001D619", "\U0001D619"}, {"\U0001D61A", "\U0001D61A"}, {"\U0001D61B", "\U0001D61B"}, {"\U0001D61C", "\U0001D61C"}, {"\U0001D61D", "\U0001D61D"}, {"\U0001D61E", "\U0001D61E"}, {"\U0001D61F", "\U0001D61F"}, {"\U0001D620", "\U0001D620"}, {"\U0001D621", "\U0001D621"}, {"\U0001D622", "\U0001D622"}, {"\U0001D623", "\U0001D623"}, {"\U0001D624", "\U0001D624"}, {"\U0001D625", "\U0001D625"}, {"\U0001D626", "\U0001D626"}, {"\U0001D627", "\U0001D627"}, {"\U0001D628", "\U0001D628"}, {"\U0001D629", "\U0001D629"}, {"\U0001D62A", "\U0001D62A"}, {"\U0001D62B", "\U0001D62B"}, {"\U0001D62C", "\U0001D62C"}, {"\U0001D62D", "\U0001D62D"}, {"\U0001D62E", "\U0001D62E"}, {"\U0001D62F", "\U0001D62F"}, {"\U0001D630", "\U0001D630"}, {"\U0001D631", "\U0001D631"}, {"\U0001D632", "\U0001D632"}, {"\U0001D633", "\U0001D633"}, {"\U0001D634", "\U0001D634"}, {"\U0001D635", "\U0001D635"}, {"\U0001D636", "\U0001D636"}, {"\U0001D637", "\U0001D637"}, {"\U0001D638", "\U0001D638"}, {"\U0001D639", "\U0001D639"}, {"\U0001D63A", "\U0001D63A"}, {"\U0001D63B", "\U0001D63B"}, {"\U0001D63C", "\U0001D63C"}, {"\U0001D63D", "\U0001D63D"}, {"\U0001D63E", "\U0001D63E"}, {"\U0001D63F", "\U0001D63F"}, {"\U0001D640", "\U0001D640"}, {"\U0001D641", "\U0001D641"}, {"\U0001D642", "\U0001D642"}, {"\U0001D643", "\U0001D643"}, {"\U0001D644", "\U0001D644"}, {"\U0001D645", "\U0001D645"}, {"\U0001D646", "\U0001D646"}, {"\U0001D647", "\U0001D647"}, {"\U0001D648", "\U0001D648"}, {"\U0001D649", "\U0001D649"}, {"\U0001D64A", "\U0001D64A"}, {"\U0001D64B", "\U0001D64B"}, {"\U0001D64C", "\U0001D64C"}, {"\U0001D64D", "\U0001D64D"}, {"\U0001D64E", "\U0001D64E"}, {"\U0001D64F", "\U0001D64F"}, {"\U0001D650", "\U0001D650"}, {"\U0001D651", "\U0001D651"}, {"\U0001D652", "\U0001D652"}, {"\U0001D653", "\U0001D653"}, {"\U0001D654", "\U0001D654"}, {"\U0001D655", "\U0001D655"}, {"\U0001D656", "\U0001D656"}, {"\U0001D657", "\U0001D657"}, {"\U0001D658", "\U0001D658"}, {"\U0001D659", "\U0001D659"}, {"\U0001D65A", "\U0001D65A"}, {"\U0001D65B", "\U0001D65B"}, {"\U0001D65C", "\U0001D65C"}, {"\U0001D65D", "\U0001D65D"}, {"\U0001D65E", "\U0001D65E"}, {"\U0001D65F", "\U0001D65F"}, {"\U0001D660", "\U0001D660"}, {"\U0001D661", "\U0001D661"}, {"\U0001D662", "\U0001D662"}, {"\U0001D663", "\U0001D663"}, {"\U0001D664", "\U0001D664"}, {"\U0001D665", "\U0001D665"}, {"\U0001D666", "\U0001D666"}, {"\U0001D667", "\U0001D667"}, {"\U0001D668", "\U0001D668"}, {"\U0001D669", "\U0001D669"}, {"\U0001D66A", "\U0001D66A"}, {"\U0001D66B", "\U0001D66B"}, {"\U0001D66C", "\U0001D66C"}, {"\U0001D66D", "\U0001D66D"}, {"\U0001D66E", "\U0001D66E"}, {"\U0001D66F", "\U0001D66F"}, {"\U0001D670", "\U0001D670"}, {"\U0001D671", "\U0001D671"}, {"\U0001D672", "\U0001D672"}, {"\U0001D673", "\U0001D673"}, {"\U0001D674", "\U0001D674"}, {"\U0001D675", "\U0001D675"}, {"\U0001D676", "\U0001D676"}, {"\U0001D677", "\U0001D677"}, {"\U0001D678", "\U0001D678"}, {"\U0001D679", "\U0001D679"}, {"\U0001D67A", "\U0001D67A"}, {"\U0001D67B", "\U0001D67B"}, {"\U0001D67C", "\U0001D67C"}, {"\U0001D67D", "\U0001D67D"}, {"\U0001D67E", "\U0001D67E"}, {"\U0001D67F", "\U0001D67F"}, {"\U0001D680", "\U0001D680"}, {"\U0001D681", "\U0001D681"}, {"\U0001D682", "\U0001D682"}, {"\U0001D683", "\U0001D683"}, {"\U0001D684", "\U0001D684"}, {"\U0001D685", "\U0001D685"}, {"\U0001D686", "\U0001D686"}, {"\U0001D687", "\U0001D687"}, {"\U0001D688", "\U0001D688"}, {"\U0001D689", "\U0001D689"}, {"\U0001D68A", "\U0001D68A"}, {"\U0001D68B", "\U0001D68B"}, {"\U0001D68C", "\U0001D68C"}, {"\U0001D68D", "\U0001D68D"}, {"\U0001D68E", "\U0001D68E"}, {"\U0001D68F", "\U0001D68F"}, {"\U0001D690", "\U0001D690"}, {"\U0001D691", "\U0001D691"}, {"\U0001D692", "\U0001D692"}, {"\U0001D693", "\U0001D693"}, {"\U0001D694", "\U0001D694"}, {"\U0001D695", "\U0001D695"}, {"\U0001D696", "\U0001D696"}, {"\U0001D697", "\U0001D697"}, {"\U0001D698", "\U0001D698"}, {"\U0001D699", "\U0001D699"}, {"\U0001D69A", "\U0001D69A"}, {"\U0001D69B", "\U0001D69B"}, {"\U0001D69C", "\U0001D69C"}, {"\U0001D69D", "\U0001D69D"}, {"\U0001D69E", "\U0001D69E"}, {"\U0001D69F", "\U0001D69F"}, {"\U0001D6A0", "\U0001D6A0"}, {"\U0001D6A1", "\U0001D6A1"}, {"\U0001D6A2", "\U0001D6A2"}, {"\U0001D6A3", "\U0001D6A3"}, {"\U0001D6A4", "\U0001D6A4"}, {"\U0001D6A5", "\U0001D6A5"}, {"\U0001D6A8", "\U0001D6A8"}, {"\U0001D6A9", "\U0001D6A9"}, {"\U0001D6AA", "\U0001D6AA"}, {"\U0001D6AB", "\U0001D6AB"}, {"\U0001D6AC", "\U0001D6AC"}, {"\U0001D6AD", "\U0001D6AD"}, {"\U0001D6AE", "\U0001D6AE"}, {"\U0001D6AF", "\U0001D6AF"}, {"\U0001D6B0", "\U0001D6B0"}, {"\U0001D6B1", "\U0001D6B1"}, {"\U0001D6B2", "\U0001D6B2"}, {"\U0001D6B3", "\U0001D6B3"}, {"\U0001D6B4", "\U0001D6B4"}, {"\U0001D6B5", "\U0001D6B5"}, {"\U0001D6B6", "\U0001D6B6"}, {"\U0001D6B7", "\U0001D6B7"}, {"\U0001D6B8", "\U0001D6B8"}, {"\U0001D6B9", "\U0001D6B9"}, {"\U0001D6BA", "\U0001D6BA"}, {"\U0001D6BB", "\U0001D6BB"}, {"\U0001D6BC", "\U0001D6BC"}, {"\U0001D6BD", "\U0001D6BD"}, {"\U0001D6BE", "\U0001D6BE"}, {"\U0001D6BF", "\U0001D6BF"}, {"\U0001D6C0", "\U0001D6C0"}, {"\U0001D6C1", "\U0001D6C1"}, {"\U0001D6C2", "\U0001D6C2"}, {"\U0001D6C3", "\U0001D6C3"}, {"\U0001D6C4", "\U0001D6C4"}, {"\U0001D6C5", "\U0001D6C5"}, {"\U0001D6C6", "\U0001D6C6"}, {"\U0001D6C7", "\U0001D6C7"}, {"\U0001D6C8", "\U0001D6C8"}, {"\U0001D6C9", "\U0001D6C9"}, {"\U0001D6CA", "\U0001D6CA"}, {"\U0001D6CB", "\U0001D6CB"}, {"\U0001D6CC", "\U0001D6CC"}, {"\U0001D6CD", "\U0001D6CD"}, {"\U0001D6CE", "\U0001D6CE"}, {"\U0001D6CF", "\U0001D6CF"}, {"\U0001D6D0", "\U0001D6D0"}, {"\U0001D6D1", "\U0001D6D1"}, {"\U0001D6D2", "\U0001D6D2"}, {"\U0001D6D3", "\U0001D6D3"}, {"\U0001D6D4", "\U0001D6D4"}, {"\U0001D6D5", "\U0001D6D5"}, {"\U0001D6D6", "\U0001D6D6"}, {"\U0001D6D7", "\U0001D6D7"}, {"\U0001D6D8", "\U0001D6D8"}, {"\U0001D6D9", "\U0001D6D9"}, {"\U0001D6DA", "\U0001D6DA"}, {"\U0001D6DB", "\U0001D6DB"}, {"\U0001D6DC", "\U0001D6DC"}, {"\U0001D6DD", "\U0001D6DD"}, {"\U0001D6DE", "\U0001D6DE"}, {"\U0001D6DF", "\U0001D6DF"}, {"\U0001D6E0", "\U0001D6E0"}, {"\U0001D6E1", "\U0001D6E1"}, {"\U0001D6E2", "\U0001D6E2"}, {"\U0001D6E3", "\U0001D6E3"}, {"\U0001D6E4", "\U0001D6E4"}, {"\U0001D6E5", "\U0001D6E5"}, {"\U0001D6E6", "\U0001D6E6"}, {"\U0001D6E7", "\U0001D6E7"}, {"\U0001D6E8", "\U0001D6E8"}, {"\U0001D6E9", "\U0001D6E9"}, {"\U0001D6EA", "\U0001D6EA"}, {"\U0001D6EB", "\U0001D6EB"}, {"\U0001D6EC", "\U0001D6EC"}, {"\U0001D6ED", "\U0001D6ED"}, {"\U0001D6EE", "\U0001D6EE"}, {"\U0001D6EF", "\U0001D6EF"}, {"\U0001D6F0", "\U0001D6F0"}, {"\U0001D6F1", "\U0001D6F1"}, {"\U0001D6F2", "\U0001D6F2"}, {"\U0001D6F3", "\U0001D6F3"}, {"\U0001D6F4", "\U0001D6F4"}, {"\U0001D6F5", "\U0001D6F5"}, {"\U0001D6F6", "\U0001D6F6"}, {"\U0001D6F7", "\U0001D6F7"}, {"\U0001D6F8", "\U0001D6F8"}, {"\U0001D6F9", "\U0001D6F9"}, {"\U0001D6FA", "\U0001D6FA"}, {"\U0001D6FB", "\U0001D6FB"}, {"\U0001D6FC", "\U0001D6FC"}, {"\U0001D6FD", "\U0001D6FD"}, {"\U0001D6FE", "\U0001D6FE"}, {"\U0001D6FF", "\U0001D6FF"}, {"\U0001D700", "\U0001D700"}, {"\U0001D701", "\U0001D701"}, {"\U0001D702", "\U0001D702"}, {"\U0001D703", "\U0001D703"}, {"\U0001D704", "\U0001D704"}, {"\U0001D705", "\U0001D705"}, {"\U0001D706", "\U0001D706"}, {"\U0001D707", "\U0001D707"}, {"\U0001D708", "\U0001D708"}, {"\U0001D709", "\U0001D709"}, {"\U0001D70A", "\U0001D70A"}, {"\U0001D70B", "\U0001D70B"}, {"\U0001D70C", "\U0001D70C"}, {"\U0001D70D", "\U0001D70D"}, {"\U0001D70E", "\U0001D70E"}, {"\U0001D70F", "\U0001D70F"}, {"\U0001D710", "\U0001D710"}, {"\U0001D711", "\U0001D711"}, {"\U0001D712", "\U0001D712"}, {"\U0001D713", "\U0001D713"}, {"\U0001D714", "\U0001D714"}, {"\U0001D715", "\U0001D715"}, {"\U0001D716", "\U0001D716"}, {"\U0001D717", "\U0001D717"}, {"\U0001D718", "\U0001D718"}, {"\U0001D719", "\U0001D719"}, {"\U0001D71A", "\U0001D71A"}, {"\U0001D71B", "\U0001D71B"}, {"\U0001D71C", "\U0001D71C"}, {"\U0001D71D", "\U0001D71D"}, {"\U0001D71E", "\U0001D71E"}, {"\U0001D71F", "\U0001D71F"}, {"\U0001D720", "\U0001D720"}, {"\U0001D721", "\U0001D721"}, {"\U0001D722", "\U0001D722"}, {"\U0001D723", "\U0001D723"}, {"\U0001D724", "\U0001D724"}, {"\U0001D725", "\U0001D725"}, {"\U0001D726", "\U0001D726"}, {"\U0001D727", "\U0001D727"}, {"\U0001D728", "\U0001D728"}, {"\U0001D729", "\U0001D729"}, {"\U0001D72A", "\U0001D72A"}, {"\U0001D72B", "\U0001D72B"}, {"\U0001D72C", "\U0001D72C"}, {"\U0001D72D", "\U0001D72D"}, {"\U0001D72E", "\U0001D72E"}, {"\U0001D72F", "\U0001D72F"}, {"\U0001D730", "\U0001D730"}, {"\U0001D731", "\U0001D731"}, {"\U0001D732", "\U0001D732"}, {"\U0001D733", "\U0001D733"}, {"\U0001D734", "\U0001D734"}, {"\U0001D735", "\U0001D735"}, {"\U0001D736", "\U0001D736"}, {"\U0001D737", "\U0001D737"}, {"\U0001D738", "\U0001D738"}, {"\U0001D739", "\U0001D739"}, {"\U0001D73A", "\U0001D73A"}, {"\U0001D73B", "\U0001D73B"}, {"\U0001D73C", "\U0001D73C"}, {"\U0001D73D", "\U0001D73D"}, {"\U0001D73E", "\U0001D73E"}, {"\U0001D73F", "\U0001D73F"}, {"\U0001D740", "\U0001D740"}, {"\U0001D741", "\U0001D741"}, {"\U0001D742", "\U0001D742"}, {"\U0001D743", "\U0001D743"}, {"\U0001D744", "\U0001D744"}, {"\U0001D745", "\U0001D745"}, {"\U0001D746", "\U0001D746"}, {"\U0001D747", "\U0001D747"}, {"\U0001D748", "\U0001D748"}, {"\U0001D749", "\U0001D749"}, {"\U0001D74A", "\U0001D74A"}, {"\U0001D74B", "\U0001D74B"}, {"\U0001D74C", "\U0001D74C"}, {"\U0001D74D", "\U0001D74D"}, {"\U0001D74E", "\U0001D74E"}, {"\U0001D74F", "\U0001D74F"}, {"\U0001D750", "\U0001D750"}, {"\U0001D751", "\U0001D751"}, {"\U0001D752", "\U0001D752"}, {"\U0001D753", "\U0001D753"}, {"\U0001D754", "\U0001D754"}, {"\U0001D755", "\U0001D755"}, {"\U0001D756", "\U0001D756"}, {"\U0001D757", "\U0001D757"}, {"\U0001D758", "\U0001D758"}, {"\U0001D759", "\U0001D759"}, {"\U0001D75A", "\U0001D75A"}, {"\U0001D75B", "\U0001D75B"}, {"\U0001D75C", "\U0001D75C"}, {"\U0001D75D", "\U0001D75D"}, {"\U0001D75E", "\U0001D75E"}, {"\U0001D75F", "\U0001D75F"}, {"\U0001D760", "\U0001D760"}, {"\U0001D761", "\U0001D761"}, {"\U0001D762", "\U0001D762"}, {"\U0001D763", "\U0001D763"}, {"\U0001D764", "\U0001D764"}, {"\U0001D765", "\U0001D765"}, {"\U0001D766", "\U0001D766"}, {"\U0001D767", "\U0001D767"}, {"\U0001D768", "\U0001D768"}, {"\U0001D769", "\U0001D769"}, {"\U0001D76A", "\U0001D76A"}, {"\U0001D76B", "\U0001D76B"}, {"\U0001D76C", "\U0001D76C"}, {"\U0001D76D", "\U0001D76D"}, {"\U0001D76E", "\U0001D76E"}, {"\U0001D76F", "\U0001D76F"}, {"\U0001D770", "\U0001D770"}, {"\U0001D771", "\U0001D771"}, {"\U0001D772", "\U0001D772"}, {"\U0001D773", "\U0001D773"}, {"\U0001D774", "\U0001D774"}, {"\U0001D775", "\U0001D775"}, {"\U0001D776", "\U0001D776"}, {"\U0001D777", "\U0001D777"}, {"\U0001D778", "\U0001D778"}, {"\U0001D779", "\U0001D779"}, {"\U0001D77A", "\U0001D77A"}, {"\U0001D77B", "\U0001D77B"}, {"\U0001D77C", "\U0001D77C"}, {"\U0001D77D", "\U0001D77D"}, {"\U0001D77E", "\U0001D77E"}, {"\U0001D77F", "\U0001D77F"}, {"\U0001D780", "\U0001D780"}, {"\U0001D781", "\U0001D781"}, {"\U0001D782", "\U0001D782"}, {"\U0001D783", "\U0001D783"}, {"\U0001D784", "\U0001D784"}, {"\U0001D785", "\U0001D785"}, {"\U0001D786", "\U0001D786"}, {"\U0001D787", "\U0001D787"}, {"\U0001D788", "\U0001D788"}, {"\U0001D789", "\U0001D789"}, {"\U0001D78A", "\U0001D78A"}, {"\U0001D78B", "\U0001D78B"}, {"\U0001D78C", "\U0001D78C"}, {"\U0001D78D", "\U0001D78D"}, {"\U0001D78E", "\U0001D78E"}, {"\U0001D78F", "\U0001D78F"}, {"\U0001D790", "\U0001D790"}, {"\U0001D791", "\U0001D791"}, {"\U0001D792", "\U0001D792"}, {"\U0001D793", "\U0001D793"}, {"\U0001D794", "\U0001D794"}, {"\U0001D795", "\U0001D795"}, {"\U0001D796", "\U0001D796"}, {"\U0001D797", "\U0001D797"}, {"\U0001D798", "\U0001D798"}, {"\U0001D799", "\U0001D799"}, {"\U0001D79A", "\U0001D79A"}, {"\U0001D79B", "\U0001D79B"}, {"\U0001D79C", "\U0001D79C"}, {"\U0001D79D", "\U0001D79D"}, {"\U0001D79E", "\U0001D79E"}, {"\U0001D79F", "\U0001D79F"}, {"\U0001D7A0", "\U0001D7A0"}, {"\U0001D7A1", "\U0001D7A1"}, {"\U0001D7A2", "\U0001D7A2"}, {"\U0001D7A3", "\U0001D7A3"}, {"\U0001D7A4", "\U0001D7A4"}, {"\U0001D7A5", "\U0001D7A5"}, {"\U0001D7A6", "\U0001D7A6"}, {"\U0001D7A7", "\U0001D7A7"}, {"\U0001D7A8", "\U0001D7A8"}, {"\U0001D7A9", "\U0001D7A9"}, {"\U0001D7AA", "\U0001D7AA"}, {"\U0001D7AB", "\U0001D7AB"}, {"\U0001D7AC", "\U0001D7AC"}, {"\U0001D7AD", "\U0001D7AD"}, {"\U0001D7AE", "\U0001D7AE"}, {"\U0001D7AF", "\U0001D7AF"}, {"\U0001D7B0", "\U0001D7B0"}, {"\U0001D7B1", "\U0001D7B1"}, {"\U0001D7B2", "\U0001D7B2"}, {"\U0001D7B3", "\U0001D7B3"}, {"\U0001D7B4", "\U0001D7B4"}, {"\U0001D7B5", "\U0001D7B5"}, {"\U0001D7B6", "\U0001D7B6"}, {"\U0001D7B7", "\U0001D7B7"}, {"\U0001D7B8", "\U0001D7B8"}, {"\U0001D7B9", "\U0001D7B9"}, {"\U0001D7BA", "\U0001D7BA"}, {"\U0001D7BB", "\U0001D7BB"}, {"\U0001D7BC", "\U0001D7BC"}, {"\U0001D7BD", "\U0001D7BD"}, {"\U0001D7BE", "\U0001D7BE"}, {"\U0001D7BF", "\U0001D7BF"}, {"\U0001D7C0", "\U0001D7C0"}, {"\U0001D7C1", "\U0001D7C1"}, {"\U0001D7C2", "\U0001D7C2"}, {"\U0001D7C3", "\U0001D7C3"}, {"\U0001D7C4", "\U0001D7C4"}, {"\U0001D7C5", "\U0001D7C5"}, {"\U0001D7C6", "\U0001D7C6"}, {"\U0001D7C7", "\U0001D7C7"}, {"\U0001D7C8", "\U0001D7C8"}, {"\U0001D7C9", "\U0001D7C9"}, {"\U0001D7CA", "\U0001D7CA"}, {"\U0001D7CB", "\U0001D7CB"}, {"\U0001D7CE", "\U0001D7CE"}, {"\U0001D7CF", "\U0001D7CF"}, {"\U0001D7D0", "\U0001D7D0"}, {"\U0001D7D1", "\U0001D7D1"}, {"\U0001D7D2", "\U0001D7D2"}, {"\U0001D7D3", "\U0001D7D3"}, {"\U0001D7D4", "\U0001D7D4"}, {"\U0001D7D5", "\U0001D7D5"}, {"\U0001D7D6", "\U0001D7D6"}, {"\U0001D7D7", "\U0001D7D7"}, {"\U0001D7D8", "\U0001D7D8"}, {"\U0001D7D9", "\U0001D7D9"}, {"\U0001D7DA", "\U0001D7DA"}, {"\U0001D7DB", "\U0001D7DB"}, {"\U0001D7DC", "\U0001D7DC"}, {"\U0001D7DD", "\U0001D7DD"}, {"\U0001D7DE", "\U0001D7DE"}, {"\U0001D7DF", "\U0001D7DF"}, {"\U0001D7E0", "\U0001D7E0"}, {"\U0001D7E1", "\U0001D7E1"}, {"\U0001D7E2", "\U0001D7E2"}, {"\U0001D7E3", "\U0001D7E3"}, {"\U0001D7E4", "\U0001D7E4"}, {"\U0001D7E5", "\U0001D7E5"}, {"\U0001D7E6", "\U0001D7E6"}, {"\U0001D7E7", "\U0001D7E7"}, {"\U0001D7E8", "\U0001D7E8"}, {"\U0001D7E9", "\U0001D7E9"}, {"\U0001D7EA", "\U0001D7EA"}, {"\U0001D7EB", "\U0001D7EB"}, {"\U0001D7EC", "\U0001D7EC"}, {"\U0001D7ED", "\U0001D7ED"}, {"\U0001D7EE", "\U0001D7EE"}, {"\U0001D7EF", "\U0001D7EF"}, {"\U0001D7F0", "\U0001D7F0"}, {"\U0001D7F1", "\U0001D7F1"}, {"\U0001D7F2", "\U0001D7F2"}, {"\U0001D7F3", "\U0001D7F3"}, {"\U0001D7F4", "\U0001D7F4"}, {"\U0001D7F5", "\U0001D7F5"}, {"\U0001D7F6", "\U0001D7F6"}, {"\U0001D7F7", "\U0001D7F7"}, {"\U0001D7F8", "\U0001D7F8"}, {"\U0001D7F9", "\U0001D7F9"}, {"\U0001D7FA", "\U0001D7FA"}, {"\U0001D7FB", "\U0001D7FB"}, {"\U0001D7FC", "\U0001D7FC"}, {"\U0001D7FD", "\U0001D7FD"}, {"\U0001D7FE", "\U0001D7FE"}, {"\U0001D7FF", "\U0001D7FF"}, {"\U0001E030", "\U0001E030"}, {"\U0001E031", "\U0001E031"}, {"\U0001E032", "\U0001E032"}, {"\U0001E033", "\U0001E033"}, {"\U0001E034", "\U0001E034"}, {"\U0001E035", "\U0001E035"}, {"\U0001E036", "\U0001E036"}, {"\U0001E037", "\U0001E037"}, {"\U0001E038", "\U0001E038"}, {"\U0001E039", "\U0001E039"}, {"\U0001E03A", "\U0001E03A"}, {"\U0001E03B", "\U0001E03B"}, {"\U0001E03C", "\U0001E03C"}, {"\U0001E03D", "\U0001E03D"}, {"\U0001E03E", "\U0001E03E"}, {"\U0001E03F", "\U0001E03F"}, {"\U0001E040", "\U0001E040"}, {"\U0001E041", "\U0001E041"}, {"\U0001E042", "\U0001E042"}, {"\U0001E043", "\U0001E043"}, {"\U0001E044", "\U0001E044"}, {"\U0001E045", "\U0001E045"}, {"\U0001E046", "\U0001E046"}, {"\U0001E047", "\U0001E047"}, {"\U0001E048", "\U0001E048"}, {"\U0001E049", "\U0001E049"}, {"\U0001E04A", "\U0001E04A"}, {"\U0001E04B", "\U0001E04B"}, {"\U0001E04C", "\U0001E04C"}, {"\U0001E04D", "\U0001E04D"}, {"\U0001E04E", "\U0001E04E"}, {"\U0001E04F", "\U0001E04F"}, {"\U0001E050", "\U0001E050"}, {"\U0001E051", "\U0001E051"}, {"\U0001E052", "\U0001E052"}, {"\U0001E053", "\U0001E053"}, {"\U0001E054", "\U0001E054"}, {"\U0001E055", "\U0001E055"}, {"\U0001E056", "\U0001E056"}, {"\U0001E057", "\U0001E057"}, {"\U0001E058", "\U0001E058"}, {"\U0001E059", "\U0001E059"}, {"\U0001E05A", "\U0001E05A"}, {"\U0001E05B", "\U0001E05B"}, {"\U0001E05C", "\U0001E05C"}, {"\U0001E05D", "\U0001E05D"}, {"\U0001E05E", "\U0001E05E"}, {"\U0001E05F", "\U0001E05F"}, {"\U0001E060", "\U0001E060"}, {"\U0001E061", "\U0001E061"}, {"\U0001E062", "\U0001E062"}, {"\U0001E063", "\U0001E063"}, {"\U0001E064", "\U0001E064"}, {"\U0001E065", "\U0001E065"}, {"\U0001E066", "\U0001E066"}, {"\U0001E067", "\U0001E067"}, {"\U0001E068", "\U0001E068"}, {"\U0001E069", "\U0001E069"}, {"\U0001E06A", "\U0001E06A"}, {"\U0001E06B", "\U0001E06B"}, {"\U0001E06C", "\U0001E06C"}, {"\U0001E06D", "\U0001E06D"}, {"\U0001EE00", "\U0001EE00"}, {"\U0001EE01", "\U0001EE01"}, {"\U0001EE02", "\U0001EE02"}, {"\U0001EE03", "\U0001EE03"}, {"\U0001EE05", "\U0001EE05"}, {"\U0001EE06", "\U0001EE06"}, {"\U0001EE07", "\U0001EE07"}, {"\U0001EE08", "\U0001EE08"}, {"\U0001EE09", "\U0001EE09"}, {"\U0001EE0A", "\U0001EE0A"}, {"\U0001EE0B", "\U0001EE0B"}, {"\U0001EE0C", "\U0001EE0C"}, {"\U0001EE0D", "\U0001EE0D"}, {"\U0001EE0E", "\U0001EE0E"}, {"\U0001EE0F", "\U0001EE0F"}, {"\U0001EE10", "\U0001EE10"}, {"\U0001EE11", "\U0001EE11"}, {"\U0001EE12", "\U0001EE12"}, {"\U0001EE13", "\U0001EE13"}, {"\U0001EE14", "\U0001EE14"}, {"\U0001EE15", "\U0001EE15"}, {"\U0001EE16", "\U0001EE16"}, {"\U0001EE17", "\U0001EE17"}, {"\U0001EE18", "\U0001EE18"}, {"\U0001EE19", "\U0001EE19"}, {"\U0001EE1A", "\U0001EE1A"}, {"\U0001EE1B", "\U0001EE1B"}, {"\U0001EE1C", "\U0001EE1C"}, {"\U0001EE1D", "\U0001EE1D"}, {"\U0001EE1E", "\U0001EE1E"}, {"\U0001EE1F", "\U0001EE1F"}, {"\U0001EE21", "\U0001EE21"}, {"\U0001EE22", "\U0001EE22"}, {"\U0001EE24", "\U0001EE24"}, {"\U0001EE27", "\U0001EE27"}, {"\U0001EE29", "\U0001EE29"}, {"\U0001EE2A", "\U0001EE2A"}, {"\U0001EE2B", "\U0001EE2B"}, {"\U0001EE2C", "\U0001EE2C"}, {"\U0001EE2D", "\U0001EE2D"}, {"\U0001EE2E", "\U0001EE2E"}, {"\U0001EE2F", "\U0001EE2F"}, {"\U0001EE30", "\U0001EE30"}, {"\U0001EE31", "\U0001EE31"}, {"\U0001EE32", "\U0001EE32"}, {"\U0001EE34", "\U0001EE34"}, {"\U0001EE35", "\U0001EE35"}, {"\U0001EE36", "\U0001EE36"}, {"\U0001EE37", "\U0001EE37"}, {"\U0001EE39", "\U0001EE39"}, {"\U0001EE3B", "\U0001EE3B"}, {"\U0001EE42", "\U0001EE42"}, {"\U0001EE47", "\U0001EE47"}, {"\U0001EE49", "\U0001EE49"}, {"\U0001EE4B", "\U0001EE4B"}, {"\U0001EE4D", "\U0001EE4D"}, {"\U0001EE4E", "\U0001EE4E"}, {"\U0001EE4F", "\U0001EE4F"}, {"\U0001EE51", "\U0001EE51"}, {"\U0001EE52", "\U0001EE52"}, {"\U0001EE54", "\U0001EE54"}, {"\U0001EE57", "\U0001EE57"}, {"\U0001EE59", "\U0001EE59"}, {"\U0001EE5B", "\U0001EE5B"}, {"\U0001EE5D", "\U0001EE5D"}, {"\U0001EE5F", "\U0001EE5F"}, {"\U0001EE61", "\U0001EE61"}, {"\U0001EE62", "\U0001EE62"}, {"\U0001EE64", "\U0001EE64"}, {"\U0001EE67", "\U0001EE67"}, {"\U0001EE68", "\U0001EE68"}, {"\U0001EE69", "\U0001EE69"}, {"\U0001EE6A", "\U0001EE6A"}, {"\U0001EE6C", "\U0001EE6C"}, {"\U0001EE6D", "\U0001EE6D"}, {"\U0001EE6E", "\U0001EE6E"}, {"\U0001EE6F", "\U0001EE6F"}, {"\U0001EE70", "\U0001EE70"}, {"\U0001EE71", "\U0001EE71"}, {"\U0001EE72", "\U0001EE72"}, {"\U0001EE74", "\U0001EE74"}, {"\U0001EE75", "\U0001EE75"}, {"\U0001EE76", "\U0001EE76"}, {"\U0001EE77", "\U0001EE77"}, {"\U0001EE79", "\U0001EE79"}, {"\U0001EE7A", "\U0001EE7A"}, {"\U0001EE7B", "\U0001EE7B"}, {"\U0001EE7C", "\U0001EE7C"}, {"\U0001EE7E", "\U0001EE7E"}, {"\U0001EE80", "\U0001EE80"}, {"\U0001EE81", "\U0001EE81"}, {"\U0001EE82", "\U0001EE82"}, {"\U0001EE83", "\U0001EE83"}, {"\U0001EE84", "\U0001EE84"}, {"\U0001EE85", "\U0001EE85"}, {"\U0001EE86", "\U0001EE86"}, {"\U0001EE87", "\U0001EE87"}, {"\U0001EE88", "\U0001EE88"}, {"\U0001EE89", "\U0001EE89"}, {"\U0001EE8B", "\U0001EE8B"}, {"\U0001EE8C", "\U0001EE8C"}, {"\U0001EE8D", "\U0001EE8D"}, {"\U0001EE8E", "\U0001EE8E"}, {"\U0001EE8F", "\U0001EE8F"}, {"\U0001EE90", "\U0001EE90"}, {"\U0001EE91", "\U0001EE91"}, {"\U0001EE92", "\U0001EE92"}, {"\U0001EE93", "\U0001EE93"}, {"\U0001EE94", "\U0001EE94"}, {"\U0001EE95", "\U0001EE95"}, {"\U0001EE96", "\U0001EE96"}, {"\U0001EE97", "\U0001EE97"}, {"\U0001EE98", "\U0001EE98"}, {"\U0001EE99", "\U0001EE99"}, {"\U0001EE9A", "\U0001EE9A"}, {"\U0001EE9B", "\U0001EE9B"}, {"\U0001EEA1", "\U0001EEA1"}, {"\U0001EEA2", "\U0001EEA2"}, {"\U0001EEA3", "\U0001EEA3"}, {"\U0001EEA5", "\U0001EEA5"}, {"\U0001EEA6", "\U0001EEA6"}, {"\U0001EEA7", "\U0001EEA7"}, {"\U0001EEA8", "\U0001EEA8"}, {"\U0001EEA9", "\U0001EEA9"}, {"\U0001EEAB", "\U0001EEAB"}, {"\U0001EEAC", "\U0001EEAC"}, {"\U0001EEAD", "\U0001EEAD"}, {"\U0001EEAE", "\U0001EEAE"}, {"\U0001EEAF", "\U0001EEAF"}, {"\U0001EEB0", "\U0001EEB0"}, {"\U0001EEB1", "\U0001EEB1"}, {"\U0001EEB2", "\U0001EEB2"}, {"\U0001EEB3", "\U0001EEB3"}, {"\U0001EEB4", "\U0001EEB4"}, {"\U0001EEB5", "\U0001EEB5"}, {"\U0001EEB6", "\U0001EEB6"}, {"\U0001EEB7", "\U0001EEB7"}, {"\U0001EEB8", "\U0001EEB8"}, {"\U0001EEB9", "\U0001EEB9"}, {"\U0001EEBA", "\U0001EEBA"}, {"\U0001EEBB", "\U0001EEBB"}, {"\U0001F100", "\U0001F100"}, {"\U0001F101", "\U0001F101"}, {"\U0001F102", "\U0001F102"}, {"\U0001F103", "\U0001F103"}, {"\U0001F104", "\U0001F104"}, {"\U0001F105", "\U0001F105"}, {"\U0001F106", "\U0001F106"}, {"\U0001F107", "\U0001F107"}, {"\U0001F108", "\U0001F108"}, {"\U0001F109", "\U0001F109"}, {"\U0001F10A", "\U0001F10A"}, {"\U0001F110", "\U0001F110"}, {"\U0001F111", "\U0001F111"}, {"\U0001F112", "\U0001F112"}, {"\U0001F113", "\U0001F113"}, {"\U0001F114", "\U0001F114"}, {"\U0001F115", "\U0001F115"}, {"\U0001F116", "\U0001F116"}, {"\U0001F117", "\U0001F117"}, {"\U0001F118", "\U0001F118"}, {"\U0001F119", "\U0001F119"}, {"\U0001F11A", "\U0001F11A"}, {"\U0001F11B", "\U0001F11B"}, {"\U0001F11C", "\U0001F11C"}, {"\U0001F11D", "\U0001F11D"}, {"\U0001F11E", "\U0001F11E"}, {"\U0001F11F", "\U0001F11F"}, {"\U0001F120", "\U0001F120"}, {"\U0001F121", "\U0001F121"}, {"\U0001F122", "\U0001F122"}, {"\U0001F123", "\U0001F123"}, {"\U0001F124", "\U0001F124"}, {"\U0001F125", "\U0001F125"}, {"\U0001F126", "\U0001F126"}, {"\U0001F127", "\U0001F127"}, {"\U0001F128", "\U0001F128"}, {"\U0001F129", "\U0001F129"}, {"\U0001F12A", "\U0001F12A"}, {"\U0001F12B", "\U0001F12B"}, {"\U0001F12C", "\U0001F12C"}, {"\U0001F12D", "\U0001F12D"}, {"\U0001F12E", "\U0001F12E"}, {"\U0001F130", "\U0001F130"}, {"\U0001F131", "\U0001F131"}, {"\U0001F132", "\U0001F132"}, {"\U0001F133", "\U0001F133"}, {"\U0001F134", "\U0001F134"}, {"\U0001F135", "\U0001F135"}, {"\U0001F136", "\U0001F136"}, {"\U0001F137", "\U0001F137"}, {"\U0001F138", "\U0001F138"}, {"\U0001F139", "\U0001F139"}, {"\U0001F13A", "\U0001F13A"}, {"\U0001F13B", "\U0001F13B"}, {"\U0001F13C", "\U0001F13C"}, {"\U0001F13D", "\U0001F13D"}, {"\U0001F13E", "\U0001F13E"}, {"\U0001F13F", "\U0001F13F"}, {"\U0001F140", "\U0001F140"}, {"\U0001F141", "\U0001F141"}, {"\U0001F142", "\U0001F142"}, {"\U0001F143", "\U0001F143"}, {"\U0001F144", "\U0001F144"}, {"\U0001F145", "\U0001F145"}, {"\U0001F146", "\U0001F146"}, {"\U0001F147", "\U0001F147"}, {"\U0001F148", "\U0001F148"}, {"\U0001F149", "\U0001F149"}, {"\U0001F14A", "\U0001F14A"}, {"\U0001F14B", "\U0001F14B"}, {"\U0001F14C", "\U0001F14C"}, {"\U0001F14D", "\U0001F14D"}, {"\U0001F14E", "\U0001F14E"}, {"\U0001F14F", "\U0001F14F"}, {"\U0001F16A", "\U0001F16A"}, {"\U0001F16B", "\U0001F16B"}, {"\U0001F16C", "\U0001F16C"}, {"\U0001F190", "\U0001F190"}, {"\U0001F200", "\U0001F200"}, {"\U0001F201", "\U0001F201"}, {"\U0001F202", "\U0001F202"}, {"\U0001F210", "\U0001F210"}, {"\U0001F211", "\U0001F211"}, {"\U0001F212", "\U0001F212"}, {"\U0001F213", "\U0001F213"}, {"\U0001F214", "\U0001F214"}, {"\U0001F215", "\U0001F215"}, {"\U0001F216", "\U0001F216"}, {"\U0001F217", "\U0001F217"}, {"\U0001F218", "\U0001F218"}, {"\U0001F219", "\U0001F219"}, {"\U0001F21A", "\U0001F21A"}, {"\U0001F21B", "\U0001F21B"}, {"\U0001F21C", "\U0001F21C"}, {"\U0001F21D", "\U0001F21D"}, {"\U0001F21E", "\U0001F21E"}, {"\U0001F21F", "\U0001F21F"}, {"\U0001F220", "\U0001F220"}, {"\U0001F221", "\U0001F221"}, {"\U0001F222", "\U0001F222"}, {"\U0001F223", "\U0001F223"}, {"\U0001F224", "\U0001F224"}, {"\U0001F225", "\U0001F225"}, {"\U0001F226", "\U0001F226"}, {"\U0001F227", "\U0001F227"}, {"\U0001F228", "\U0001F228"}, {"\U0001F229", "\U0001F229"}, {"\U0001F22A", "\U0001F22A"}, {"\U0001F22B", "\U0001F22B"}, {"\U0001F22C", "\U0001F22C"}, {"\U0001F22D", "\U0001F22D"}, {"\U0001F22E", "\U0001F22E"}, {"\U0001F22F", "\U0001F22F"}, {"\U0001F230", "\U0001F230"}, {"\U0001F231", "\U0001F231"}, {"\U0001F232", "\U0001F232"}, {"\U0001F233", "\U0001F233"}, {"\U0001F234", "\U0001F234"}, {"\U0001F235", "\U0001F235"}, {"\U0001F236", "\U0001F236"}, {"\U0001F237", "\U0001F237"}, {"\U0001F238", "\U0001F238"}, {"\U0001F239", "\U0001F239"}, {"\U0001F23A", "\U0001F23A"}, {"\U0001F23B", "\U0001F23B"}, {"\U0001F240", "\U0001F240"}, {"\U0001F241", "\U0001F241"}, {"\U0001F242", "\U0001F242"}, {"\U0001F243", "\U0001F243"}, {"\U0001F244", "\U0001F244"}, {"\U0001F245", "\U0001F245"}, {"\U0001F246", "\U0001F246"}, {"\U0001F247", "\U0001F247"}, {"\U0001F248", "\U0001F248"}, {"\U0001F250", "\U0001F250"}, {"\U0001F251", "\U0001F251"}, {"\U0001FBF0", "\U0001FBF0"}, {"\U0001FBF1", "\U0001FBF1"}, {"\U0001FBF2", "\U0001FBF2"}, {"\U0001FBF3", "\U0001FBF3"}, {"\U0001FBF4", "\U0001FBF4"}, {"\U0001FBF5", "\U0001FBF5"}, {"\U0001FBF6", "\U0001FBF6"}, {"\U0001FBF7", "\U0001FBF7"}, {"\U0001FBF8", "\U0001FBF8"}, {"\U0001FBF9", "\U0001FBF9"}, {"\U0002F800", "\u4E3D"}, {"\U0002F801", "\u4E38"}, {"\U0002F802", "\u4E41"}, {"\U0002F803", "\U00020122"}, {"\U0002F804", "\u4F60"}, {"\U0002F805", "\u4FAE"}, {"\U0002F806", "\u4FBB"}, {"\U0002F807", "\u5002"}, {"\U0002F808", "\u507A"}, {"\U0002F809", "\u5099"}, {"\U0002F80A", "\u50E7"}, {"\U0002F80B", "\u50CF"}, {"\U0002F80C", "\u349E"}, {"\U0002F80D", "\U0002063A"}, {"\U0002F80E", "\u514D"}, {"\U0002F80F", "\u5154"}, {"\U0002F810", "\u5164"}, {"\U0002F811", "\u5177"}, {"\U0002F812", "\U0002051C"}, {"\U0002F813", "\u34B9"}, {"\U0002F814", "\u5167"}, {"\U0002F815", "\u518D"}, {"\U0002F816", "\U0002054B"}, {"\U0002F817", "\u5197"}, {"\U0002F818", "\u51A4"}, {"\U0002F819", "\u4ECC"}, {"\U0002F81A", "\u51AC"}, {"\U0002F81B", "\u51B5"}, {"\U0002F81C", "\U000291DF"}, {"\U0002F81D", "\u51F5"}, {"\U0002F81E", "\u5203"}, {"\U0002F81F", "\u34DF"}, {"\U0002F820", "\u523B"}, {"\U0002F821", "\u5246"}, {"\U0002F822", "\u5272"}, {"\U0002F823", "\u5277"}, {"\U0002F824", "\u3515"}, {"\U0002F825", "\u52C7"}, {"\U0002F826", "\u52C9"}, {"\U0002F827", "\u52E4"}, {"\U0002F828", "\u52FA"}, {"\U0002F829", "\u5305"}, {"\U0002F82A", "\u5306"}, {"\U0002F82B", "\u5317"}, {"\U0002F82C", "\u5349"}, {"\U0002F82D", "\u5351"}, {"\U0002F82E", "\u535A"}, {"\U0002F82F", "\u5373"}, {"\U0002F830", "\u537D"}, {"\U0002F831", "\u537F"}, {"\U0002F832", "\u537F"}, {"\U0002F833", "\u537F"}, {"\U0002F834", "\U00020A2C"}, {"\U0002F835", "\u7070"}, {"\U0002F836", "\u53CA"}, {"\U0002F837", "\u53DF"}, {"\U0002F838", "\U00020B63"}, {"\U0002F839", "\u53EB"}, {"\U0002F83A", "\u53F1"}, {"\U0002F83B", "\u5406"}, {"\U0002F83C", "\u549E"}, {"\U0002F83D", "\u5438"}, {"\U0002F83E", "\u5448"}, {"\U0002F83F", "\u5468"}, {"\U0002F840", "\u54A2"}, {"\U0002F841", "\u54F6"}, {"\U0002F842", "\u5510"}, {"\U0002F843", "\u5553"}, {"\U0002F844", "\u5563"}, {"\U0002F845", "\u5584"}, {"\U0002F846", "\u5584"}, {"\U0002F847", "\u5599"}, {"\U0002F848", "\u55AB"}, {"\U0002F849", "\u55B3"}, {"\U0002F84A", "\u55C2"}, {"\U0002F84B", "\u5716"}, {"\U0002F84C", "\u5606"}, {"\U0002F84D", "\u5717"}, {"\U0002F84E", "\u5651"}, {"\U0002F84F", "\u5674"}, {"\U0002F850", "\u5207"}, {"\U0002F851", "\u58EE"}, {"\U0002F852", "\u57CE"}, {"\U0002F853", "\u57F4"}, {"\U0002F854", "\u580D"}, {"\U0002F855", "\u578B"}, {"\U0002F856", "\u5832"}, {"\U0002F857", "\u5831"}, {"\U0002F858", "\u58AC"}, {"\U0002F859", "\U000214E4"}, {"\U0002F85A", "\u58F2"}, {"\U0002F85B", "\u58F7"}, {"\U0002F85C", "\u5906"}, {"\U0002F85D", "\u591A"}, {"\U0002F85E", "\u5922"}, {"\U0002F85F", "\u5962"}, {"\U0002F860", "\U000216A8"}, {"\U0002F861", "\U000216EA"}, {"\U0002F862", "\u59EC"}, {"\U0002F863", "\u5A1B"}, {"\U0002F864", "\u5A27"}, {"\U0002F865", "\u59D8"}, {"\U0002F866", "\u5A66"}, {"\U0002F867", "\u36EE"}, {"\U0002F868", "\u36FC"}, {"\U0002F869", "\u5B08"}, {"\U0002F86A", "\u5B3E"}, {"\U0002F86B", "\u5B3E"}, {"\U0002F86C", "\U000219C8"}, {"\U0002F86D", "\u5BC3"}, {"\U0002F86E", "\u5BD8"}, {"\U0002F86F", "\u5BE7"}, {"\U0002F870", "\u5BF3"}, {"\U0002F871", "\U00021B18"}, {"\U0002F872", "\u5BFF"}, {"\U0002F873", "\u5C06"}, {"\U0002F874", "\u5F53"}, {"\U0002F875", "\u5C22"}, {"\U0002F876", "\u3781"}, {"\U0002F877", "\u5C60"}, {"\U0002F878", "\u5C6E"}, {"\U0002F879", "\u5CC0"}, {"\U0002F87A", "\u5C8D"}, {"\U0002F87B", "\U00021DE4"}, {"\U0002F87C", "\u5D43"}, {"\U0002F87D", "\U00021DE6"}, {"\U0002F87E", "\u5D6E"}, {"\U0002F87F", "\u5D6B"}, {"\U0002F880", "\u5D7C"}, {"\U0002F881", "\u5DE1"}, {"\U0002F882", "\u5DE2"}, {"\U0002F883", "\u382F"}, {"\U0002F884", "\u5DFD"}, {"\U0002F885", "\u5E28"}, {"\U0002F886", "\u5E3D"}, {"\U0002F887", "\u5E69"}, {"\U0002F888", "\u3862"}, {"\U0002F889", "\U00022183"}, {"\U0002F88A", "\u387C"}, {"\U0002F88B", "\u5EB0"}, {"\U0002F88C", "\u5EB3"}, {"\U0002F88D", "\u5EB6"}, {"\U0002F88E", "\u5ECA"}, {"\U0002F88F", "\U0002A392"}, {"\U0002F890", "\u5EFE"}, {"\U0002F891", "\U00022331"}, {"\U0002F892", "\U00022331"}, {"\U0002F893", "\u8201"}, {"\U0002F894", "\u5F22"}, {"\U0002F895", "\u5F22"}, {"\U0002F896", "\u38C7"}, {"\U0002F897", "\U000232B8"}, {"\U0002F898", "\U000261DA"}, {"\U0002F899", "\u5F62"}, {"\U0002F89A", "\u5F6B"}, {"\U0002F89B", "\u38E3"}, {"\U0002F89C", "\u5F9A"}, {"\U0002F89D", "\u5FCD"}, {"\U0002F89E", "\u5FD7"}, {"\U0002F89F", "\u5FF9"}, {"\U0002F8A0", "\u6081"}, {"\U0002F8A1", "\u393A"}, {"\U0002F8A2", "\u391C"}, {"\U0002F8A3", "\u6094"}, {"\U0002F8A4", "\U000226D4"}, {"\U0002F8A5", "\u60C7"}, {"\U0002F8A6", "\u6148"}, {"\U0002F8A7", "\u614C"}, {"\U0002F8A8", "\u614E"}, {"\U0002F8A9", "\u614C"}, {"\U0002F8AA", "\u617A"}, {"\U0002F8AB", "\u618E"}, {"\U0002F8AC", "\u61B2"}, {"\U0002F8AD", "\u61A4"}, {"\U0002F8AE", "\u61AF"}, {"\U0002F8AF", "\u61DE"}, {"\U0002F8B0", "\u61F2"}, {"\U0002F8B1", "\u61F6"}, {"\U0002F8B2", "\u6210"}, {"\U0002F8B3", "\u621B"}, {"\U0002F8B4", "\u625D"}, {"\U0002F8B5", "\u62B1"}, {"\U0002F8B6", "\u62D4"}, {"\U0002F8B7", "\u6350"}, {"\U0002F8B8", "\U00022B0C"}, {"\U0002F8B9", "\u633D"}, {"\U0002F8BA", "\u62FC"}, {"\U0002F8BB", "\u6368"}, {"\U0002F8BC", "\u6383"}, {"\U0002F8BD", "\u63E4"}, {"\U0002F8BE", "\U00022BF1"}, {"\U0002F8BF", "\u6422"}, {"\U0002F8C0", "\u63C5"}, {"\U0002F8C1", "\u63A9"}, {"\U0002F8C2", "\u3A2E"}, {"\U0002F8C3", "\u6469"}, {"\U0002F8C4", "\u647E"}, {"\U0002F8C5", "\u649D"}, {"\U0002F8C6", "\u6477"}, {"\U0002F8C7", "\u3A6C"}, {"\U0002F8C8", "\u654F"}, {"\U0002F8C9", "\u656C"}, {"\U0002F8CA", "\U0002300A"}, {"\U0002F8CB", "\u65E3"}, {"\U0002F8CC", "\u66F8"}, {"\U0002F8CD", "\u6649"}, {"\U0002F8CE", "\u3B19"}, {"\U0002F8CF", "\u6691"}, {"\U0002F8D0", "\u3B08"}, {"\U0002F8D1", "\u3AE4"}, {"\U0002F8D2", "\u5192"}, {"\U0002F8D3", "\u5195"}, {"\U0002F8D4", "\u6700"}, {"\U0002F8D5", "\u669C"}, {"\U0002F8D6", "\u80AD"}, {"\U0002F8D7", "\u43D9"}, {"\U0002F8D8", "\u6717"}, {"\U0002F8D9", "\u671B"}, {"\U0002F8DA", "\u6721"}, {"\U0002F8DB", "\u675E"}, {"\U0002F8DC", "\u6753"}, {"\U0002F8DD", "\U000233C3"}, {"\U0002F8DE", "\u3B49"}, {"\U0002F8DF", "\u67FA"}, {"\U0002F8E0", "\u6785"}, {"\U0002F8E1", "\u6852"}, {"\U0002F8E2", "\u6885"}, {"\U0002F8E3", "\U0002346D"}, {"\U0002F8E4", "\u688E"}, {"\U0002F8E5", "\u681F"}, {"\U0002F8E6", "\u6914"}, {"\U0002F8E7", "\u3B9D"}, {"\U0002F8E8", "\u6942"}, {"\U0002F8E9", "\u69A3"}, {"\U0002F8EA", "\u69EA"}, {"\U0002F8EB", "\u6AA8"}, {"\U0002F8EC", "\U000236A3"}, {"\U0002F8ED", "\u6ADB"}, {"\U0002F8EE", "\u3C18"}, {"\U0002F8EF", "\u6B21"}, {"\U0002F8F0", "\U000238A7"}, {"\U0002F8F1", "\u6B54"}, {"\U0002F8F2", "\u3C4E"}, {"\U0002F8F3", "\u6B72"}, {"\U0002F8F4", "\u6B9F"}, {"\U0002F8F5", "\u6BBA"}, {"\U0002F8F6", "\u6BBB"}, {"\U0002F8F7", "\U00023A8D"}, {"\U0002F8F8", "\U00021D0B"}, {"\U0002F8F9", "\U00023AFA"}, {"\U0002F8FA", "\u6C4E"}, {"\U0002F8FB", "\U00023CBC"}, {"\U0002F8FC", "\u6CBF"}, {"\U0002F8FD", "\u6CCD"}, {"\U0002F8FE", "\u6C67"}, {"\U0002F8FF", "\u6D16"}, {"\U0002F900", "\u6D3E"}, {"\U0002F901", "\u6D77"}, {"\U0002F902", "\u6D41"}, {"\U0002F903", "\u6D69"}, {"\U0002F904", "\u6D78"}, {"\U0002F905", "\u6D85"}, {"\U0002F906", "\U00023D1E"}, {"\U0002F907", "\u6D34"}, {"\U0002F908", "\u6E2F"}, {"\U0002F909", "\u6E6E"}, {"\U0002F90A", "\u3D33"}, {"\U0002F90B", "\u6ECB"}, {"\U0002F90C", "\u6EC7"}, {"\U0002F90D", "\U00023ED1"}, {"\U0002F90E", "\u6DF9"}, {"\U0002F90F", "\u6F6E"}, {"\U0002F910", "\U00023F5E"}, {"\U0002F911", "\U00023F8E"}, {"\U0002F912", "\u6FC6"}, {"\U0002F913", "\u7039"}, {"\U0002F914", "\u701E"}, {"\U0002F915", "\u701B"}, {"\U0002F916", "\u3D96"}, {"\U0002F917", "\u704A"}, {"\U0002F918", "\u707D"}, {"\U0002F919", "\u7077"}, {"\U0002F91A", "\u70AD"}, {"\U0002F91B", "\U00020525"}, {"\U0002F91C", "\u7145"}, {"\U0002F91D", "\U00024263"}, {"\U0002F91E", "\u719C"}, {"\U0002F91F", "\U000243AB"}, {"\U0002F920", "\u7228"}, {"\U0002F921", "\u7235"}, {"\U0002F922", "\u7250"}, {"\U0002F923", "\U00024608"}, {"\U0002F924", "\u7280"}, {"\U0002F925", "\u7295"}, {"\U0002F926", "\U00024735"}, {"\U0002F927", "\U00024814"}, {"\U0002F928", "\u737A"}, {"\U0002F929", "\u738B"}, {"\U0002F92A", "\u3EAC"}, {"\U0002F92B", "\u73A5"}, {"\U0002F92C", "\u3EB8"}, {"\U0002F92D", "\u3EB8"}, {"\U0002F92E", "\u7447"}, {"\U0002F92F", "\u745C"}, {"\U0002F930", "\u7471"}, {"\U0002F931", "\u7485"}, {"\U0002F932", "\u74CA"}, {"\U0002F933", "\u3F1B"}, {"\U0002F934", "\u7524"}, {"\U0002F935", "\U00024C36"}, {"\U0002F936", "\u753E"}, {"\U0002F937", "\U00024C92"}, {"\U0002F938", "\u7570"}, {"\U0002F939", "\U0002219F"}, {"\U0002F93A", "\u7610"}, {"\U0002F93B", "\U00024FA1"}, {"\U0002F93C", "\U00024FB8"}, {"\U0002F93D", "\U00025044"}, {"\U0002F93E", "\u3FFC"}, {"\U0002F93F", "\u4008"}, {"\U0002F940", "\u76F4"}, {"\U0002F941", "\U000250F3"}, {"\U0002F942", "\U000250F2"}, {"\U0002F943", "\U00025119"}, {"\U0002F944", "\U00025133"}, {"\U0002F945", "\u771E"}, {"\U0002F946", "\u771F"}, {"\U0002F947", "\u771F"}, {"\U0002F948", "\u774A"}, {"\U0002F949", "\u4039"}, {"\U0002F94A", "\u778B"}, {"\U0002F94B", "\u4046"}, {"\U0002F94C", "\u4096"}, {"\U0002F94D", "\U0002541D"}, {"\U0002F94E", "\u784E"}, {"\U0002F94F", "\u788C"}, {"\U0002F950", "\u78CC"}, {"\U0002F951", "\u40E3"}, {"\U0002F952", "\U00025626"}, {"\U0002F953", "\u7956"}, {"\U0002F954", "\U0002569A"}, {"\U0002F955", "\U000256C5"}, {"\U0002F956", "\u798F"}, {"\U0002F957", "\u79EB"}, {"\U0002F958", "\u412F"}, {"\U0002F959", "\u7A40"}, {"\U0002F95A", "\u7A4A"}, {"\U0002F95B", "\u7A4F"}, {"\U0002F95C", "\U0002597C"}, {"\U0002F95D", "\U00025AA7"}, {"\U0002F95E", "\U00025AA7"}, {"\U0002F95F", "\u7AEE"}, {"\U0002F960", "\u4202"}, {"\U0002F961", "\U00025BAB"}, {"\U0002F962", "\u7BC6"}, {"\U0002F963", "\u7BC9"}, {"\U0002F964", "\u4227"}, {"\U0002F965", "\U00025C80"}, {"\U0002F966", "\u7CD2"}, {"\U0002F967", "\u42A0"}, {"\U0002F968", "\u7CE8"}, {"\U0002F969", "\u7CE3"}, {"\U0002F96A", "\u7D00"}, {"\U0002F96B", "\U00025F86"}, {"\U0002F96C", "\u7D63"}, {"\U0002F96D", "\u4301"}, {"\U0002F96E", "\u7DC7"}, {"\U0002F96F", "\u7E02"}, {"\U0002F970", "\u7E45"}, {"\U0002F971", "\u4334"}, {"\U0002F972", "\U00026228"}, {"\U0002F973", "\U00026247"}, {"\U0002F974", "\u4359"}, {"\U0002F975", "\U000262D9"}, {"\U0002F976", "\u7F7A"}, {"\U0002F977", "\U0002633E"}, {"\U0002F978", "\u7F95"}, {"\U0002F979", "\u7FFA"}, {"\U0002F97A", "\u8005"}, {"\U0002F97B", "\U000264DA"}, {"\U0002F97C", "\U00026523"}, {"\U0002F97D", "\u8060"}, {"\U0002F97E", "\U000265A8"}, {"\U0002F97F", "\u8070"}, {"\U0002F980", "\U0002335F"}, {"\U0002F981", "\u43D5"}, {"\U0002F982", "\u80B2"}, {"\U0002F983", "\u8103"}, {"\U0002F984", "\u440B"}, {"\U0002F985", "\u813E"}, {"\U0002F986", "\u5AB5"}, {"\U0002F987", "\U000267A7"}, {"\U0002F988", "\U000267B5"}, {"\U0002F989", "\U00023393"}, {"\U0002F98A", "\U0002339C"}, {"\U0002F98B", "\u8201"}, {"\U0002F98C", "\u8204"}, {"\U0002F98D", "\u8F9E"}, {"\U0002F98E", "\u446B"}, {"\U0002F98F", "\u8291"}, {"\U0002F990", "\u828B"}, {"\U0002F991", "\u829D"}, {"\U0002F992", "\u52B3"}, {"\U0002F993", "\u82B1"}, {"\U0002F994", "\u82B3"}, {"\U0002F995", "\u82BD"}, {"\U0002F996", "\u82E6"}, {"\U0002F997", "\U00026B3C"}, {"\U0002F998", "\u82E5"}, {"\U0002F999", "\u831D"}, {"\U0002F99A", "\u8363"}, {"\U0002F99B", "\u83AD"}, {"\U0002F99C", "\u8323"}, {"\U0002F99D", "\u83BD"}, {"\U0002F99E", "\u83E7"}, {"\U0002F99F", "\u8457"}, {"\U0002F9A0", "\u8353"}, {"\U0002F9A1", "\u83CA"}, {"\U0002F9A2", "\u83CC"}, {"\U0002F9A3", "\u83DC"}, {"\U0002F9A4", "\U00026C36"}, {"\U0002F9A5", "\U00026D6B"}, {"\U0002F9A6", "\U00026CD5"}, {"\U0002F9A7", "\u452B"}, {"\U0002F9A8", "\u84F1"}, {"\U0002F9A9", "\u84F3"}, {"\U0002F9AA", "\u8516"}, {"\U0002F9AB", "\U000273CA"}, {"\U0002F9AC", "\u8564"}, {"\U0002F9AD", "\U00026F2C"}, {"\U0002F9AE", "\u455D"}, {"\U0002F9AF", "\u4561"}, {"\U0002F9B0", "\U00026FB1"}, {"\U0002F9B1", "\U000270D2"}, {"\U0002F9B2", "\u456B"}, {"\U0002F9B3", "\u8650"}, {"\U0002F9B4", "\u865C"}, {"\U0002F9B5", "\u8667"}, {"\U0002F9B6", "\u8669"}, {"\U0002F9B7", "\u86A9"}, {"\U0002F9B8", "\u8688"}, {"\U0002F9B9", "\u870E"}, {"\U0002F9BA", "\u86E2"}, {"\U0002F9BB", "\u8779"}, {"\U0002F9BC", "\u8728"}, {"\U0002F9BD", "\u876B"}, {"\U0002F9BE", "\u8786"}, {"\U0002F9BF", "\u45D7"}, {"\U0002F9C0", "\u87E1"}, {"\U0002F9C1", "\u8801"}, {"\U0002F9C2", "\u45F9"}, {"\U0002F9C3", "\u8860"}, {"\U0002F9C4", "\u8863"}, {"\U0002F9C5", "\U00027667"}, {"\U0002F9C6", "\u88D7"}, {"\U0002F9C7", "\u88DE"}, {"\U0002F9C8", "\u4635"}, {"\U0002F9C9", "\u88FA"}, {"\U0002F9CA", "\u34BB"}, {"\U0002F9CB", "\U000278AE"}, {"\U0002F9CC", "\U00027966"}, {"\U0002F9CD", "\u46BE"}, {"\U0002F9CE", "\u46C7"}, {"\U0002F9CF", "\u8AA0"}, {"\U0002F9D0", "\u8AED"}, {"\U0002F9D1", "\u8B8A"}, {"\U0002F9D2", "\u8C55"}, {"\U0002F9D3", "\U00027CA8"}, {"\U0002F9D4", "\u8CAB"}, {"\U0002F9D5", "\u8CC1"}, {"\U0002F9D6", "\u8D1B"}, {"\U0002F9D7", "\u8D77"}, {"\U0002F9D8", "\U00027F2F"}, {"\U0002F9D9", "\U00020804"}, {"\U0002F9DA", "\u8DCB"}, {"\U0002F9DB", "\u8DBC"}, {"\U0002F9DC", "\u8DF0"}, {"\U0002F9DD", "\U000208DE"}, {"\U0002F9DE", "\u8ED4"}, {"\U0002F9DF", "\u8F38"}, {"\U0002F9E0", "\U000285D2"}, {"\U0002F9E1", "\U000285ED"}, {"\U0002F9E2", "\u9094"}, {"\U0002F9E3", "\u90F1"}, {"\U0002F9E4", "\u9111"}, {"\U0002F9E5", "\U0002872E"}, {"\U0002F9E6", "\u911B"}, {"\U0002F9E7", "\u9238"}, {"\U0002F9E8", "\u92D7"}, {"\U0002F9E9", "\u92D8"}, {"\U0002F9EA", "\u927C"}, {"\U0002F9EB", "\u93F9"}, {"\U0002F9EC", "\u9415"}, {"\U0002F9ED", "\U00028BFA"}, {"\U0002F9EE", "\u958B"}, {"\U0002F9EF", "\u4995"}, {"\U0002F9F0", "\u95B7"}, {"\U0002F9F1", "\U00028D77"}, {"\U0002F9F2", "\u49E6"}, {"\U0002F9F3", "\u96C3"}, {"\U0002F9F4", "\u5DB2"}, {"\U0002F9F5", "\u9723"}, {"\U0002F9F6", "\U00029145"}, {"\U0002F9F7", "\U0002921A"}, {"\U0002F9F8", "\u4A6E"}, {"\U0002F9F9", "\u4A76"}, {"\U0002F9FA", "\u97E0"}, {"\U0002F9FB", "\U0002940A"}, {"\U0002F9FC", "\u4AB2"}, {"\U0002F9FD", "\U00029496"}, {"\U0002F9FE", "\u980B"}, {"\U0002F9FF", "\u980B"}, {"\U0002FA00", "\u9829"}, {"\U0002FA01", "\U000295B6"}, {"\U0002FA02", "\u98E2"}, {"\U0002FA03", "\u4B33"}, {"\U0002FA04", "\u9929"}, {"\U0002FA05", "\u99A7"}, {"\U0002FA06", "\u99C2"}, {"\U0002FA07", "\u99FE"}, {"\U0002FA08", "\u4BCE"}, {"\U0002FA09", "\U00029B30"}, {"\U0002FA0A", "\u9B12"}, {"\U0002FA0B", "\u9C40"}, {"\U0002FA0C", "\u9CFD"}, {"\U0002FA0D", "\u4CCE"}, {"\U0002FA0E", "\u4CED"}, {"\U0002FA0F", "\u9D67"}, {"\U0002FA10", "\U0002A0CE"}, {"\U0002FA11", "\u4CF8"}, {"\U0002FA12", "\U0002A105"}, {"\U0002FA13", "\U0002A20E"}, {"\U0002FA14", "\U0002A291"}, {"\U0002FA15", "\u9EBB"}, {"\U0002FA16", "\u4D56"}, {"\U0002FA17", "\u9EF9"}, {"\U0002FA18", "\u9EFE"}, {"\U0002FA19", "\u9F05"}, {"\U0002FA1A", "\u9F0F"}, {"\U0002FA1B", "\u9F16"}, {"\U0002FA1C", "\u9F3B"}, {"\U0002FA1D", "\U0002A600"}, {"\u0061\u0315\u0300\u05AE\u0300\u0062", "\u00E0\u05AE\u0300\u0315\u0062"}, {"\u0061\u0300\u0315\u0300\u05AE\u0062", "\u00E0\u05AE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0301\u0062", "\u00E0\u05AE\u0301\u0315\u0062"}, {"\u0061\u0301\u0315\u0300\u05AE\u0062", "\u00E1\u05AE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0302\u0062", "\u00E0\u05AE\u0302\u0315\u0062"}, {"\u0061\u0302\u0315\u0300\u05AE\u0062", "\u1EA7\u05AE\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0303\u0062", "\u00E0\u05AE\u0303\u0315\u0062"}, {"\u0061\u0303\u0315\u0300\u05AE\u0062", "\u00E3\u05AE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0304\u0062", "\u00E0\u05AE\u0304\u0315\u0062"}, {"\u0061\u0304\u0315\u0300\u05AE\u0062", "\u0101\u05AE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0305\u0062", "\u00E0\u05AE\u0305\u0315\u0062"}, {"\u0061\u0305\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0305\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0306\u0062", "\u00E0\u05AE\u0306\u0315\u0062"}, {"\u0061\u0306\u0315\u0300\u05AE\u0062", "\u1EB1\u05AE\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0307\u0062", "\u00E0\u05AE\u0307\u0315\u0062"}, {"\u0061\u0307\u0315\u0300\u05AE\u0062", "\u0227\u05AE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0308\u0062", "\u00E0\u05AE\u0308\u0315\u0062"}, {"\u0061\u0308\u0315\u0300\u05AE\u0062", "\u00E4\u05AE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0309\u0062", "\u00E0\u05AE\u0309\u0315\u0062"}, {"\u0061\u0309\u0315\u0300\u05AE\u0062", "\u1EA3\u05AE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u030A\u0062", "\u00E0\u05AE\u030A\u0315\u0062"}, {"\u0061\u030A\u0315\u0300\u05AE\u0062", "\u00E5\u05AE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u030B\u0062", "\u00E0\u05AE\u030B\u0315\u0062"}, {"\u0061\u030B\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u030B\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u030C\u0062", "\u00E0\u05AE\u030C\u0315\u0062"}, {"\u0061\u030C\u0315\u0300\u05AE\u0062", "\u01CE\u05AE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u030D\u0062", "\u00E0\u05AE\u030D\u0315\u0062"}, {"\u0061\u030D\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u030D\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u030E\u0062", "\u00E0\u05AE\u030E\u0315\u0062"}, {"\u0061\u030E\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u030E\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u030F\u0062", "\u00E0\u05AE\u030F\u0315\u0062"}, {"\u0061\u030F\u0315\u0300\u05AE\u0062", "\u0201\u05AE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0310\u0062", "\u00E0\u05AE\u0310\u0315\u0062"}, {"\u0061\u0310\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0310\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0311\u0062", "\u00E0\u05AE\u0311\u0315\u0062"}, {"\u0061\u0311\u0315\u0300\u05AE\u0062", "\u0203\u05AE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0312\u0062", "\u00E0\u05AE\u0312\u0315\u0062"}, {"\u0061\u0312\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0312\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0313\u0062", "\u00E0\u05AE\u0313\u0315\u0062"}, {"\u0061\u0313\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0313\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0314\u0062", "\u00E0\u05AE\u0314\u0315\u0062"}, {"\u0061\u0314\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0314\u0300\u0315\u0062"}, {"\u0061\u035C\u0315\u0300\u0315\u0062", "\u00E0\u0315\u0315\u035C\u0062"}, {"\u0061\u0315\u035C\u0315\u0300\u0062", "\u00E0\u0315\u0315\u035C\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0316\u0062", "\u0061\u1DFA\u0316\u0316\u059A\u0062"}, {"\u0061\u0316\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0316\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0317\u0062", "\u0061\u1DFA\u0316\u0317\u059A\u0062"}, {"\u0061\u0317\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0317\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0318\u0062", "\u0061\u1DFA\u0316\u0318\u059A\u0062"}, {"\u0061\u0318\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0318\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0319\u0062", "\u0061\u1DFA\u0316\u0319\u059A\u0062"}, {"\u0061\u0319\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0319\u0316\u059A\u0062"}, {"\u0061\u035C\u0315\u0300\u031A\u0062", "\u00E0\u0315\u031A\u035C\u0062"}, {"\u0061\u031A\u035C\u0315\u0300\u0062", "\u00E0\u031A\u0315\u035C\u0062"}, {"\u0061\u1DFA\u031B\u1DCE\u031B\u0062", "\u0061\u1DCE\u031B\u031B\u1DFA\u0062"}, {"\u0061\u031B\u1DFA\u031B\u1DCE\u0062", "\u0061\u1DCE\u031B\u031B\u1DFA\u0062"}, {"\u0061\u059A\u0316\u1DFA\u031C\u0062", "\u0061\u1DFA\u0316\u031C\u059A\u0062"}, {"\u0061\u031C\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u031C\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u031D\u0062", "\u0061\u1DFA\u0316\u031D\u059A\u0062"}, {"\u0061\u031D\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u031D\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u031E\u0062", "\u0061\u1DFA\u0316\u031E\u059A\u0062"}, {"\u0061\u031E\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u031E\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u031F\u0062", "\u0061\u1DFA\u0316\u031F\u059A\u0062"}, {"\u0061\u031F\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u031F\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0320\u0062", "\u0061\u1DFA\u0316\u0320\u059A\u0062"}, {"\u0061\u0320\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0320\u0316\u059A\u0062"}, {"\u0061\u1DCE\u0321\u0F74\u0321\u0062", "\u0061\u0F74\u0321\u0321\u1DCE\u0062"}, {"\u0061\u0321\u1DCE\u0321\u0F74\u0062", "\u0061\u0F74\u0321\u0321\u1DCE\u0062"}, {"\u0061\u1DCE\u0321\u0F74\u0322\u0062", "\u0061\u0F74\u0321\u0322\u1DCE\u0062"}, {"\u0061\u0322\u1DCE\u0321\u0F74\u0062", "\u0061\u0F74\u0322\u0321\u1DCE\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0323\u0062", "\u0061\u1DFA\u0316\u0323\u059A\u0062"}, {"\u0061\u0323\u059A\u0316\u1DFA\u0062", "\u1EA1\u1DFA\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0324\u0062", "\u0061\u1DFA\u0316\u0324\u059A\u0062"}, {"\u0061\u0324\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0324\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0325\u0062", "\u0061\u1DFA\u0316\u0325\u059A\u0062"}, {"\u0061\u0325\u059A\u0316\u1DFA\u0062", "\u1E01\u1DFA\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0326\u0062", "\u0061\u1DFA\u0316\u0326\u059A\u0062"}, {"\u0061\u0326\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0326\u0316\u059A\u0062"}, {"\u0061\u1DCE\u0321\u0F74\u0327\u0062", "\u0061\u0F74\u0321\u0327\u1DCE\u0062"}, {"\u0061\u0327\u1DCE\u0321\u0F74\u0062", "\u0061\u0F74\u0327\u0321\u1DCE\u0062"}, {"\u0061\u1DCE\u0321\u0F74\u0328\u0062", "\u0061\u0F74\u0321\u0328\u1DCE\u0062"}, {"\u0061\u0328\u1DCE\u0321\u0F74\u0062", "\u0105\u0F74\u0321\u1DCE\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0329\u0062", "\u0061\u1DFA\u0316\u0329\u059A\u0062"}, {"\u0061\u0329\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0329\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u032A\u0062", "\u0061\u1DFA\u0316\u032A\u059A\u0062"}, {"\u0061\u032A\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u032A\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u032B\u0062", "\u0061\u1DFA\u0316\u032B\u059A\u0062"}, {"\u0061\u032B\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u032B\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u032C\u0062", "\u0061\u1DFA\u0316\u032C\u059A\u0062"}, {"\u0061\u032C\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u032C\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u032D\u0062", "\u0061\u1DFA\u0316\u032D\u059A\u0062"}, {"\u0061\u032D\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u032D\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u032E\u0062", "\u0061\u1DFA\u0316\u032E\u059A\u0062"}, {"\u0061\u032E\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u032E\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u032F\u0062", "\u0061\u1DFA\u0316\u032F\u059A\u0062"}, {"\u0061\u032F\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u032F\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0330\u0062", "\u0061\u1DFA\u0316\u0330\u059A\u0062"}, {"\u0061\u0330\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0330\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0331\u0062", "\u0061\u1DFA\u0316\u0331\u059A\u0062"}, {"\u0061\u0331\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0331\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0332\u0062", "\u0061\u1DFA\u0316\u0332\u059A\u0062"}, {"\u0061\u0332\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0332\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0333\u0062", "\u0061\u1DFA\u0316\u0333\u059A\u0062"}, {"\u0061\u0333\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0333\u0316\u059A\u0062"}, {"\u0061\U00016FF0\u0334\u0334\u0062", "\u0061\u0334\u0334\U00016FF0\u0062"}, {"\u0061\u0334\U00016FF0\u0334\u0062", "\u0061\u0334\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\u0335\u0062", "\u0061\u0334\u0335\U00016FF0\u0062"}, {"\u0061\u0335\U00016FF0\u0334\u0062", "\u0061\u0335\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\u0336\u0062", "\u0061\u0334\u0336\U00016FF0\u0062"}, {"\u0061\u0336\U00016FF0\u0334\u0062", "\u0061\u0336\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\u0337\u0062", "\u0061\u0334\u0337\U00016FF0\u0062"}, {"\u0061\u0337\U00016FF0\u0334\u0062", "\u0061\u0337\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\u0338\u0062", "\u0061\u0334\u0338\U00016FF0\u0062"}, {"\u0061\u0338\U00016FF0\u0334\u0062", "\u0061\u0338\u0334\U00016FF0\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0339\u0062", "\u0061\u1DFA\u0316\u0339\u059A\u0062"}, {"\u0061\u0339\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0339\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u033A\u0062", "\u0061\u1DFA\u0316\u033A\u059A\u0062"}, {"\u0061\u033A\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u033A\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u033B\u0062", "\u0061\u1DFA\u0316\u033B\u059A\u0062"}, {"\u0061\u033B\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u033B\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u033C\u0062", "\u0061\u1DFA\u0316\u033C\u059A\u0062"}, {"\u0061\u033C\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u033C\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u033D\u0062", "\u00E0\u05AE\u033D\u0315\u0062"}, {"\u0061\u033D\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u033D\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u033E\u0062", "\u00E0\u05AE\u033E\u0315\u0062"}, {"\u0061\u033E\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u033E\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u033F\u0062", "\u00E0\u05AE\u033F\u0315\u0062"}, {"\u0061\u033F\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u033F\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0340\u0062", "\u00E0\u05AE\u0300\u0315\u0062"}, {"\u0061\u0340\u0315\u0300\u05AE\u0062", "\u00E0\u05AE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0341\u0062", "\u00E0\u05AE\u0301\u0315\u0062"}, {"\u0061\u0341\u0315\u0300\u05AE\u0062", "\u00E1\u05AE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0342\u0062", "\u00E0\u05AE\u0342\u0315\u0062"}, {"\u0061\u0342\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0342\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0343\u0062", "\u00E0\u05AE\u0313\u0315\u0062"}, {"\u0061\u0343\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0313\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0344\u0062", "\u00E0\u05AE\u0308\u0301\u0315\u0062"}, {"\u0061\u0344\u0315\u0300\u05AE\u0062", "\u00E4\u05AE\u0301\u0300\u0315\u0062"}, {"\u0061\u0345\u035D\u0345\u0062", "\u0061\u035D\u0345\u0345\u0062"}, {"\u0061\u0345\u0345\u035D\u0062", "\u0061\u035D\u0345\u0345\u0062"}, {"\u0061\u0315\u0300\u05AE\u0346\u0062", "\u00E0\u05AE\u0346\u0315\u0062"}, {"\u0061\u0346\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0346\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0347\u0062", "\u0061\u1DFA\u0316\u0347\u059A\u0062"}, {"\u0061\u0347\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0347\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0348\u0062", "\u0061\u1DFA\u0316\u0348\u059A\u0062"}, {"\u0061\u0348\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0348\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0349\u0062", "\u0061\u1DFA\u0316\u0349\u059A\u0062"}, {"\u0061\u0349\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0349\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u034A\u0062", "\u00E0\u05AE\u034A\u0315\u0062"}, {"\u0061\u034A\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u034A\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u034B\u0062", "\u00E0\u05AE\u034B\u0315\u0062"}, {"\u0061\u034B\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u034B\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u034C\u0062", "\u00E0\u05AE\u034C\u0315\u0062"}, {"\u0061\u034C\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u034C\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u034D\u0062", "\u0061\u1DFA\u0316\u034D\u059A\u0062"}, {"\u0061\u034D\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u034D\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u034E\u0062", "\u0061\u1DFA\u0316\u034E\u059A\u0062"}, {"\u0061\u034E\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u034E\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u0350\u0062", "\u00E0\u05AE\u0350\u0315\u0062"}, {"\u0061\u0350\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0350\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0351\u0062", "\u00E0\u05AE\u0351\u0315\u0062"}, {"\u0061\u0351\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0351\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0352\u0062", "\u00E0\u05AE\u0352\u0315\u0062"}, {"\u0061\u0352\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0352\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0353\u0062", "\u0061\u1DFA\u0316\u0353\u059A\u0062"}, {"\u0061\u0353\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0353\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0354\u0062", "\u0061\u1DFA\u0316\u0354\u059A\u0062"}, {"\u0061\u0354\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0354\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0355\u0062", "\u0061\u1DFA\u0316\u0355\u059A\u0062"}, {"\u0061\u0355\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0355\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0356\u0062", "\u0061\u1DFA\u0316\u0356\u059A\u0062"}, {"\u0061\u0356\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0356\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u0357\u0062", "\u00E0\u05AE\u0357\u0315\u0062"}, {"\u0061\u0357\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0357\u0300\u0315\u0062"}, {"\u0061\u035C\u0315\u0300\u0358\u0062", "\u00E0\u0315\u0358\u035C\u0062"}, {"\u0061\u0358\u035C\u0315\u0300\u0062", "\u00E0\u0358\u0315\u035C\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0359\u0062", "\u0061\u1DFA\u0316\u0359\u059A\u0062"}, {"\u0061\u0359\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0359\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u035A\u0062", "\u0061\u1DFA\u0316\u035A\u059A\u0062"}, {"\u0061\u035A\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u035A\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u035B\u0062", "\u00E0\u05AE\u035B\u0315\u0062"}, {"\u0061\u035B\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u035B\u0300\u0315\u0062"}, {"\u0061\u035D\u035C\u0315\u035C\u0062", "\u0061\u0315\u035C\u035C\u035D\u0062"}, {"\u0061\u035C\u035D\u035C\u0315\u0062", "\u0061\u0315\u035C\u035C\u035D\u0062"}, {"\u0061\u0345\u035D\u035C\u035D\u0062", "\u0061\u035C\u035D\u035D\u0345\u0062"}, {"\u0061\u035D\u0345\u035D\u035C\u0062", "\u0061\u035C\u035D\u035D\u0345\u0062"}, {"\u0061\u0345\u035D\u035C\u035E\u0062", "\u0061\u035C\u035D\u035E\u0345\u0062"}, {"\u0061\u035E\u0345\u035D\u035C\u0062", "\u0061\u035C\u035E\u035D\u0345\u0062"}, {"\u0061\u035D\u035C\u0315\u035F\u0062", "\u0061\u0315\u035C\u035F\u035D\u0062"}, {"\u0061\u035F\u035D\u035C\u0315\u0062", "\u0061\u0315\u035F\u035C\u035D\u0062"}, {"\u0061\u0345\u035D\u035C\u0360\u0062", "\u0061\u035C\u035D\u0360\u0345\u0062"}, {"\u0061\u0360\u0345\u035D\u035C\u0062", "\u0061\u035C\u0360\u035D\u0345\u0062"}, {"\u0061\u0345\u035D\u035C\u0361\u0062", "\u0061\u035C\u035D\u0361\u0345\u0062"}, {"\u0061\u0361\u0345\u035D\u035C\u0062", "\u0061\u035C\u0361\u035D\u0345\u0062"}, {"\u0061\u035D\u035C\u0315\u0362\u0062", "\u0061\u0315\u035C\u0362\u035D\u0062"}, {"\u0061\u0362\u035D\u035C\u0315\u0062", "\u0061\u0315\u0362\u035C\u035D\u0062"}, {"\u0061\u0315\u0300\u05AE\u0363\u0062", "\u00E0\u05AE\u0363\u0315\u0062"}, {"\u0061\u0363\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0363\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0364\u0062", "\u00E0\u05AE\u0364\u0315\u0062"}, {"\u0061\u0364\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0364\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0365\u0062", "\u00E0\u05AE\u0365\u0315\u0062"}, {"\u0061\u0365\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0365\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0366\u0062", "\u00E0\u05AE\u0366\u0315\u0062"}, {"\u0061\u0366\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0366\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0367\u0062", "\u00E0\u05AE\u0367\u0315\u0062"}, {"\u0061\u0367\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0367\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0368\u0062", "\u00E0\u05AE\u0368\u0315\u0062"}, {"\u0061\u0368\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0368\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0369\u0062", "\u00E0\u05AE\u0369\u0315\u0062"}, {"\u0061\u0369\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0369\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u036A\u0062", "\u00E0\u05AE\u036A\u0315\u0062"}, {"\u0061\u036A\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u036A\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u036B\u0062", "\u00E0\u05AE\u036B\u0315\u0062"}, {"\u0061\u036B\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u036B\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u036C\u0062", "\u00E0\u05AE\u036C\u0315\u0062"}, {"\u0061\u036C\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u036C\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u036D\u0062", "\u00E0\u05AE\u036D\u0315\u0062"}, {"\u0061\u036D\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u036D\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u036E\u0062", "\u00E0\u05AE\u036E\u0315\u0062"}, {"\u0061\u036E\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u036E\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u036F\u0062", "\u00E0\u05AE\u036F\u0315\u0062"}, {"\u0061\u036F\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u036F\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0483\u0062", "\u00E0\u05AE\u0483\u0315\u0062"}, {"\u0061\u0483\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0483\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0484\u0062", "\u00E0\u05AE\u0484\u0315\u0062"}, {"\u0061\u0484\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0484\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0485\u0062", "\u00E0\u05AE\u0485\u0315\u0062"}, {"\u0061\u0485\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0485\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0486\u0062", "\u00E0\u05AE\u0486\u0315\u0062"}, {"\u0061\u0486\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0486\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0487\u0062", "\u00E0\u05AE\u0487\u0315\u0062"}, {"\u0061\u0487\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0487\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0591\u0062", "\u0061\u1DFA\u0316\u0591\u059A\u0062"}, {"\u0061\u0591\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0591\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u0592\u0062", "\u00E0\u05AE\u0592\u0315\u0062"}, {"\u0061\u0592\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0592\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0593\u0062", "\u00E0\u05AE\u0593\u0315\u0062"}, {"\u0061\u0593\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0593\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0594\u0062", "\u00E0\u05AE\u0594\u0315\u0062"}, {"\u0061\u0594\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0594\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0595\u0062", "\u00E0\u05AE\u0595\u0315\u0062"}, {"\u0061\u0595\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0595\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0596\u0062", "\u0061\u1DFA\u0316\u0596\u059A\u0062"}, {"\u0061\u0596\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0596\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u0597\u0062", "\u00E0\u05AE\u0597\u0315\u0062"}, {"\u0061\u0597\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0597\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0598\u0062", "\u00E0\u05AE\u0598\u0315\u0062"}, {"\u0061\u0598\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0598\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0599\u0062", "\u00E0\u05AE\u0599\u0315\u0062"}, {"\u0061\u0599\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0599\u0300\u0315\u0062"}, {"\u0061\u302E\u059A\u0316\u059A\u0062", "\u0061\u0316\u059A\u059A\u302E\u0062"}, {"\u0061\u059A\u302E\u059A\u0316\u0062", "\u0061\u0316\u059A\u059A\u302E\u0062"}, {"\u0061\u059A\u0316\u1DFA\u059B\u0062", "\u0061\u1DFA\u0316\u059B\u059A\u0062"}, {"\u0061\u059B\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u059B\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u059C\u0062", "\u00E0\u05AE\u059C\u0315\u0062"}, {"\u0061\u059C\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u059C\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u059D\u0062", "\u00E0\u05AE\u059D\u0315\u0062"}, {"\u0061\u059D\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u059D\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u059E\u0062", "\u00E0\u05AE\u059E\u0315\u0062"}, {"\u0061\u059E\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u059E\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u059F\u0062", "\u00E0\u05AE\u059F\u0315\u0062"}, {"\u0061\u059F\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u059F\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u05A0\u0062", "\u00E0\u05AE\u05A0\u0315\u0062"}, {"\u0061\u05A0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u05A0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u05A1\u0062", "\u00E0\u05AE\u05A1\u0315\u0062"}, {"\u0061\u05A1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u05A1\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u05A2\u0062", "\u0061\u1DFA\u0316\u05A2\u059A\u0062"}, {"\u0061\u05A2\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u05A2\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u05A3\u0062", "\u0061\u1DFA\u0316\u05A3\u059A\u0062"}, {"\u0061\u05A3\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u05A3\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u05A4\u0062", "\u0061\u1DFA\u0316\u05A4\u059A\u0062"}, {"\u0061\u05A4\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u05A4\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u05A5\u0062", "\u0061\u1DFA\u0316\u05A5\u059A\u0062"}, {"\u0061\u05A5\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u05A5\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u05A6\u0062", "\u0061\u1DFA\u0316\u05A6\u059A\u0062"}, {"\u0061\u05A6\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u05A6\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u05A7\u0062", "\u0061\u1DFA\u0316\u05A7\u059A\u0062"}, {"\u0061\u05A7\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u05A7\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u05A8\u0062", "\u00E0\u05AE\u05A8\u0315\u0062"}, {"\u0061\u05A8\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u05A8\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u05A9\u0062", "\u00E0\u05AE\u05A9\u0315\u0062"}, {"\u0061\u05A9\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u05A9\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u05AA\u0062", "\u0061\u1DFA\u0316\u05AA\u059A\u0062"}, {"\u0061\u05AA\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u05AA\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u05AB\u0062", "\u00E0\u05AE\u05AB\u0315\u0062"}, {"\u0061\u05AB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u05AB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u05AC\u0062", "\u00E0\u05AE\u05AC\u0315\u0062"}, {"\u0061\u05AC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u05AC\u0300\u0315\u0062"}, {"\u0061\u302E\u059A\u0316\u05AD\u0062", "\u0061\u0316\u059A\u05AD\u302E\u0062"}, {"\u0061\u05AD\u302E\u059A\u0316\u0062", "\u0061\u0316\u05AD\u059A\u302E\u0062"}, {"\u0061\u0300\u05AE\U0001D16D\u05AE\u0062", "\u00E0\U0001D16D\u05AE\u05AE\u0062"}, {"\u0061\u05AE\u0300\u05AE\U0001D16D\u0062", "\u00E0\U0001D16D\u05AE\u05AE\u0062"}, {"\u0061\u0315\u0300\u05AE\u05AF\u0062", "\u00E0\u05AE\u05AF\u0315\u0062"}, {"\u0061\u05AF\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u05AF\u0300\u0315\u0062"}, {"\u0061\u05B1\u05B0\u094D\u05B0\u0062", "\u0061\u094D\u05B0\u05B0\u05B1\u0062"}, {"\u0061\u05B0\u05B1\u05B0\u094D\u0062", "\u0061\u094D\u05B0\u05B0\u05B1\u0062"}, {"\u0061\u05B2\u05B1\u05B0\u05B1\u0062", "\u0061\u05B0\u05B1\u05B1\u05B2\u0062"}, {"\u0061\u05B1\u05B2\u05B1\u05B0\u0062", "\u0061\u05B0\u05B1\u05B1\u05B2\u0062"}, {"\u0061\u05B3\u05B2\u05B1\u05B2\u0062", "\u0061\u05B1\u05B2\u05B2\u05B3\u0062"}, {"\u0061\u05B2\u05B3\u05B2\u05B1\u0062", "\u0061\u05B1\u05B2\u05B2\u05B3\u0062"}, {"\u0061\u05B4\u05B3\u05B2\u05B3\u0062", "\u0061\u05B2\u05B3\u05B3\u05B4\u0062"}, {"\u0061\u05B3\u05B4\u05B3\u05B2\u0062", "\u0061\u05B2\u05B3\u05B3\u05B4\u0062"}, {"\u0061\u05B5\u05B4\u05B3\u05B4\u0062", "\u0061\u05B3\u05B4\u05B4\u05B5\u0062"}, {"\u0061\u05B4\u05B5\u05B4\u05B3\u0062", "\u0061\u05B3\u05B4\u05B4\u05B5\u0062"}, {"\u0061\u05B6\u05B5\u05B4\u05B5\u0062", "\u0061\u05B4\u05B5\u05B5\u05B6\u0062"}, {"\u0061\u05B5\u05B6\u05B5\u05B4\u0062", "\u0061\u05B4\u05B5\u05B5\u05B6\u0062"}, {"\u0061\u05B7\u05B6\u05B5\u05B6\u0062", "\u0061\u05B5\u05B6\u05B6\u05B7\u0062"}, {"\u0061\u05B6\u05B7\u05B6\u05B5\u0062", "\u0061\u05B5\u05B6\u05B6\u05B7\u0062"}, {"\u0061\u05B8\u05B7\u05B6\u05B7\u0062", "\u0061\u05B6\u05B7\u05B7\u05B8\u0062"}, {"\u0061\u05B7\u05B8\u05B7\u05B6\u0062", "\u0061\u05B6\u05B7\u05B7\u05B8\u0062"}, {"\u0061\u05B9\u05B8\u05B7\u05B8\u0062", "\u0061\u05B7\u05B8\u05B8\u05B9\u0062"}, {"\u0061\u05B8\u05B9\u05B8\u05B7\u0062", "\u0061\u05B7\u05B8\u05B8\u05B9\u0062"}, {"\u0061\u05BB\u05B9\u05B8\u05B9\u0062", "\u0061\u05B8\u05B9\u05B9\u05BB\u0062"}, {"\u0061\u05B9\u05BB\u05B9\u05B8\u0062", "\u0061\u05B8\u05B9\u05B9\u05BB\u0062"}, {"\u0061\u05BB\u05B9\u05B8\u05BA\u0062", "\u0061\u05B8\u05B9\u05BA\u05BB\u0062"}, {"\u0061\u05BA\u05BB\u05B9\u05B8\u0062", "\u0061\u05B8\u05BA\u05B9\u05BB\u0062"}, {"\u0061\u05BC\u05BB\u05B9\u05BB\u0062", "\u0061\u05B9\u05BB\u05BB\u05BC\u0062"}, {"\u0061\u05BB\u05BC\u05BB\u05B9\u0062", "\u0061\u05B9\u05BB\u05BB\u05BC\u0062"}, {"\u0061\u05BD\u05BC\u05BB\u05BC\u0062", "\u0061\u05BB\u05BC\u05BC\u05BD\u0062"}, {"\u0061\u05BC\u05BD\u05BC\u05BB\u0062", "\u0061\u05BB\u05BC\u05BC\u05BD\u0062"}, {"\u0061\u05BF\u05BD\u05BC\u05BD\u0062", "\u0061\u05BC\u05BD\u05BD\u05BF\u0062"}, {"\u0061\u05BD\u05BF\u05BD\u05BC\u0062", "\u0061\u05BC\u05BD\u05BD\u05BF\u0062"}, {"\u0061\u05C1\u05BF\u05BD\u05BF\u0062", "\u0061\u05BD\u05BF\u05BF\u05C1\u0062"}, {"\u0061\u05BF\u05C1\u05BF\u05BD\u0062", "\u0061\u05BD\u05BF\u05BF\u05C1\u0062"}, {"\u0061\u05C2\u05C1\u05BF\u05C1\u0062", "\u0061\u05BF\u05C1\u05C1\u05C2\u0062"}, {"\u0061\u05C1\u05C2\u05C1\u05BF\u0062", "\u0061\u05BF\u05C1\u05C1\u05C2\u0062"}, {"\u0061\uFB1E\u05C2\u05C1\u05C2\u0062", "\u0061\u05C1\u05C2\u05C2\uFB1E\u0062"}, {"\u0061\u05C2\uFB1E\u05C2\u05C1\u0062", "\u0061\u05C1\u05C2\u05C2\uFB1E\u0062"}, {"\u0061\u0315\u0300\u05AE\u05C4\u0062", "\u00E0\u05AE\u05C4\u0315\u0062"}, {"\u0061\u05C4\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u05C4\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u05C5\u0062", "\u0061\u1DFA\u0316\u05C5\u059A\u0062"}, {"\u0061\u05C5\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u05C5\u0316\u059A\u0062"}, {"\u0061\u05B9\u05B8\u05B7\u05C7\u0062", "\u0061\u05B7\u05B8\u05C7\u05B9\u0062"}, {"\u0061\u05C7\u05B9\u05B8\u05B7\u0062", "\u0061\u05B7\u05C7\u05B8\u05B9\u0062"}, {"\u0061\u0315\u0300\u05AE\u0610\u0062", "\u00E0\u05AE\u0610\u0315\u0062"}, {"\u0061\u0610\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0610\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0611\u0062", "\u00E0\u05AE\u0611\u0315\u0062"}, {"\u0061\u0611\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0611\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0612\u0062", "\u00E0\u05AE\u0612\u0315\u0062"}, {"\u0061\u0612\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0612\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0613\u0062", "\u00E0\u05AE\u0613\u0315\u0062"}, {"\u0061\u0613\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0613\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0614\u0062", "\u00E0\u05AE\u0614\u0315\u0062"}, {"\u0061\u0614\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0614\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0615\u0062", "\u00E0\u05AE\u0615\u0315\u0062"}, {"\u0061\u0615\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0615\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0616\u0062", "\u00E0\u05AE\u0616\u0315\u0062"}, {"\u0061\u0616\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0616\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0617\u0062", "\u00E0\u05AE\u0617\u0315\u0062"}, {"\u0061\u0617\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0617\u0300\u0315\u0062"}, {"\u0061\u0619\u0618\u064D\u0618\u0062", "\u0061\u064D\u0618\u0618\u0619\u0062"}, {"\u0061\u0618\u0619\u0618\u064D\u0062", "\u0061\u064D\u0618\u0618\u0619\u0062"}, {"\u0061\u061A\u0619\u0618\u0619\u0062", "\u0061\u0618\u0619\u0619\u061A\u0062"}, {"\u0061\u0619\u061A\u0619\u0618\u0062", "\u0061\u0618\u0619\u0619\u061A\u0062"}, {"\u0061\u0651\u061A\u0619\u061A\u0062", "\u0061\u0619\u061A\u061A\u0651\u0062"}, {"\u0061\u061A\u0651\u061A\u0619\u0062", "\u0061\u0619\u061A\u061A\u0651\u0062"}, {"\u0061\u064C\u064B\uFB1E\u064B\u0062", "\u0061\uFB1E\u064B\u064B\u064C\u0062"}, {"\u0061\u064B\u064C\u064B\uFB1E\u0062", "\u0061\uFB1E\u064B\u064B\u064C\u0062"}, {"\u0061\u064D\u064C\u064B\u064C\u0062", "\u0061\u064B\u064C\u064C\u064D\u0062"}, {"\u0061\u064C\u064D\u064C\u064B\u0062", "\u0061\u064B\u064C\u064C\u064D\u0062"}, {"\u0061\u0618\u064D\u064C\u064D\u0062", "\u0061\u064C\u064D\u064D\u0618\u0062"}, {"\u0061\u064D\u0618\u064D\u064C\u0062", "\u0061\u064C\u064D\u064D\u0618\u0062"}, {"\u0061\u0619\u0618\u064D\u064E\u0062", "\u0061\u064D\u0618\u064E\u0619\u0062"}, {"\u0061\u064E\u0619\u0618\u064D\u0062", "\u0061\u064D\u064E\u0618\u0619\u0062"}, {"\u0061\u061A\u0619\u0618\u064F\u0062", "\u0061\u0618\u0619\u064F\u061A\u0062"}, {"\u0061\u064F\u061A\u0619\u0618\u0062", "\u0061\u0618\u064F\u0619\u061A\u0062"}, {"\u0061\u0651\u061A\u0619\u0650\u0062", "\u0061\u0619\u061A\u0650\u0651\u0062"}, {"\u0061\u0650\u0651\u061A\u0619\u0062", "\u0061\u0619\u0650\u061A\u0651\u0062"}, {"\u0061\u0652\u0651\u061A\u0651\u0062", "\u0061\u061A\u0651\u0651\u0652\u0062"}, {"\u0061\u0651\u0652\u0651\u061A\u0062", "\u0061\u061A\u0651\u0651\u0652\u0062"}, {"\u0061\u0670\u0652\u0651\u0652\u0062", "\u0061\u0651\u0652\u0652\u0670\u0062"}, {"\u0061\u0652\u0670\u0652\u0651\u0062", "\u0061\u0651\u0652\u0652\u0670\u0062"}, {"\u0061\u0315\u0300\u05AE\u0653\u0062", "\u00E0\u05AE\u0653\u0315\u0062"}, {"\u0061\u0653\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0653\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0654\u0062", "\u00E0\u05AE\u0654\u0315\u0062"}, {"\u0061\u0654\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0654\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0655\u0062", "\u0061\u1DFA\u0316\u0655\u059A\u0062"}, {"\u0061\u0655\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0655\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0656\u0062", "\u0061\u1DFA\u0316\u0656\u059A\u0062"}, {"\u0061\u0656\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0656\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u0657\u0062", "\u00E0\u05AE\u0657\u0315\u0062"}, {"\u0061\u0657\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0657\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0658\u0062", "\u00E0\u05AE\u0658\u0315\u0062"}, {"\u0061\u0658\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0658\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0659\u0062", "\u00E0\u05AE\u0659\u0315\u0062"}, {"\u0061\u0659\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0659\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u065A\u0062", "\u00E0\u05AE\u065A\u0315\u0062"}, {"\u0061\u065A\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u065A\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u065B\u0062", "\u00E0\u05AE\u065B\u0315\u0062"}, {"\u0061\u065B\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u065B\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u065C\u0062", "\u0061\u1DFA\u0316\u065C\u059A\u0062"}, {"\u0061\u065C\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u065C\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u065D\u0062", "\u00E0\u05AE\u065D\u0315\u0062"}, {"\u0061\u065D\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u065D\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u065E\u0062", "\u00E0\u05AE\u065E\u0315\u0062"}, {"\u0061\u065E\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u065E\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u065F\u0062", "\u0061\u1DFA\u0316\u065F\u059A\u0062"}, {"\u0061\u065F\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u065F\u0316\u059A\u0062"}, {"\u0061\u0711\u0670\u0652\u0670\u0062", "\u0061\u0652\u0670\u0670\u0711\u0062"}, {"\u0061\u0670\u0711\u0670\u0652\u0062", "\u0061\u0652\u0670\u0670\u0711\u0062"}, {"\u0061\u0315\u0300\u05AE\u06D6\u0062", "\u00E0\u05AE\u06D6\u0315\u0062"}, {"\u0061\u06D6\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u06D6\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u06D7\u0062", "\u00E0\u05AE\u06D7\u0315\u0062"}, {"\u0061\u06D7\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u06D7\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u06D8\u0062", "\u00E0\u05AE\u06D8\u0315\u0062"}, {"\u0061\u06D8\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u06D8\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u06D9\u0062", "\u00E0\u05AE\u06D9\u0315\u0062"}, {"\u0061\u06D9\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u06D9\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u06DA\u0062", "\u00E0\u05AE\u06DA\u0315\u0062"}, {"\u0061\u06DA\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u06DA\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u06DB\u0062", "\u00E0\u05AE\u06DB\u0315\u0062"}, {"\u0061\u06DB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u06DB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u06DC\u0062", "\u00E0\u05AE\u06DC\u0315\u0062"}, {"\u0061\u06DC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u06DC\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u06DF\u0062", "\u00E0\u05AE\u06DF\u0315\u0062"}, {"\u0061\u06DF\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u06DF\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u06E0\u0062", "\u00E0\u05AE\u06E0\u0315\u0062"}, {"\u0061\u06E0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u06E0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u06E1\u0062", "\u00E0\u05AE\u06E1\u0315\u0062"}, {"\u0061\u06E1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u06E1\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u06E2\u0062", "\u00E0\u05AE\u06E2\u0315\u0062"}, {"\u0061\u06E2\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u06E2\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u06E3\u0062", "\u0061\u1DFA\u0316\u06E3\u059A\u0062"}, {"\u0061\u06E3\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u06E3\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u06E4\u0062", "\u00E0\u05AE\u06E4\u0315\u0062"}, {"\u0061\u06E4\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u06E4\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u06E7\u0062", "\u00E0\u05AE\u06E7\u0315\u0062"}, {"\u0061\u06E7\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u06E7\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u06E8\u0062", "\u00E0\u05AE\u06E8\u0315\u0062"}, {"\u0061\u06E8\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u06E8\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u06EA\u0062", "\u0061\u1DFA\u0316\u06EA\u059A\u0062"}, {"\u0061\u06EA\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u06EA\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u06EB\u0062", "\u00E0\u05AE\u06EB\u0315\u0062"}, {"\u0061\u06EB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u06EB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u06EC\u0062", "\u00E0\u05AE\u06EC\u0315\u0062"}, {"\u0061\u06EC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u06EC\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u06ED\u0062", "\u0061\u1DFA\u0316\u06ED\u059A\u0062"}, {"\u0061\u06ED\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u06ED\u0316\u059A\u0062"}, {"\u0061\u0C55\u0711\u0670\u0711\u0062", "\u0061\u0670\u0711\u0711\u0C55\u0062"}, {"\u0061\u0711\u0C55\u0711\u0670\u0062", "\u0061\u0670\u0711\u0711\u0C55\u0062"}, {"\u0061\u0315\u0300\u05AE\u0730\u0062", "\u00E0\u05AE\u0730\u0315\u0062"}, {"\u0061\u0730\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0730\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0731\u0062", "\u0061\u1DFA\u0316\u0731\u059A\u0062"}, {"\u0061\u0731\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0731\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u0732\u0062", "\u00E0\u05AE\u0732\u0315\u0062"}, {"\u0061\u0732\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0732\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0733\u0062", "\u00E0\u05AE\u0733\u0315\u0062"}, {"\u0061\u0733\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0733\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0734\u0062", "\u0061\u1DFA\u0316\u0734\u059A\u0062"}, {"\u0061\u0734\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0734\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u0735\u0062", "\u00E0\u05AE\u0735\u0315\u0062"}, {"\u0061\u0735\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0735\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0736\u0062", "\u00E0\u05AE\u0736\u0315\u0062"}, {"\u0061\u0736\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0736\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0737\u0062", "\u0061\u1DFA\u0316\u0737\u059A\u0062"}, {"\u0061\u0737\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0737\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0738\u0062", "\u0061\u1DFA\u0316\u0738\u059A\u0062"}, {"\u0061\u0738\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0738\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0739\u0062", "\u0061\u1DFA\u0316\u0739\u059A\u0062"}, {"\u0061\u0739\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0739\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u073A\u0062", "\u00E0\u05AE\u073A\u0315\u0062"}, {"\u0061\u073A\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u073A\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u073B\u0062", "\u0061\u1DFA\u0316\u073B\u059A\u0062"}, {"\u0061\u073B\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u073B\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u073C\u0062", "\u0061\u1DFA\u0316\u073C\u059A\u0062"}, {"\u0061\u073C\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u073C\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u073D\u0062", "\u00E0\u05AE\u073D\u0315\u0062"}, {"\u0061\u073D\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u073D\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u073E\u0062", "\u0061\u1DFA\u0316\u073E\u059A\u0062"}, {"\u0061\u073E\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u073E\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u073F\u0062", "\u00E0\u05AE\u073F\u0315\u0062"}, {"\u0061\u073F\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u073F\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0740\u0062", "\u00E0\u05AE\u0740\u0315\u0062"}, {"\u0061\u0740\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0740\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0741\u0062", "\u00E0\u05AE\u0741\u0315\u0062"}, {"\u0061\u0741\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0741\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0742\u0062", "\u0061\u1DFA\u0316\u0742\u059A\u0062"}, {"\u0061\u0742\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0742\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u0743\u0062", "\u00E0\u05AE\u0743\u0315\u0062"}, {"\u0061\u0743\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0743\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0744\u0062", "\u0061\u1DFA\u0316\u0744\u059A\u0062"}, {"\u0061\u0744\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0744\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u0745\u0062", "\u00E0\u05AE\u0745\u0315\u0062"}, {"\u0061\u0745\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0745\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0746\u0062", "\u0061\u1DFA\u0316\u0746\u059A\u0062"}, {"\u0061\u0746\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0746\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u0747\u0062", "\u00E0\u05AE\u0747\u0315\u0062"}, {"\u0061\u0747\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0747\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0748\u0062", "\u0061\u1DFA\u0316\u0748\u059A\u0062"}, {"\u0061\u0748\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0748\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u0749\u0062", "\u00E0\u05AE\u0749\u0315\u0062"}, {"\u0061\u0749\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0749\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u074A\u0062", "\u00E0\u05AE\u074A\u0315\u0062"}, {"\u0061\u074A\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u074A\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u07EB\u0062", "\u00E0\u05AE\u07EB\u0315\u0062"}, {"\u0061\u07EB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u07EB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u07EC\u0062", "\u00E0\u05AE\u07EC\u0315\u0062"}, {"\u0061\u07EC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u07EC\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u07ED\u0062", "\u00E0\u05AE\u07ED\u0315\u0062"}, {"\u0061\u07ED\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u07ED\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u07EE\u0062", "\u00E0\u05AE\u07EE\u0315\u0062"}, {"\u0061\u07EE\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u07EE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u07EF\u0062", "\u00E0\u05AE\u07EF\u0315\u0062"}, {"\u0061\u07EF\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u07EF\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u07F0\u0062", "\u00E0\u05AE\u07F0\u0315\u0062"}, {"\u0061\u07F0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u07F0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u07F1\u0062", "\u00E0\u05AE\u07F1\u0315\u0062"}, {"\u0061\u07F1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u07F1\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u07F2\u0062", "\u0061\u1DFA\u0316\u07F2\u059A\u0062"}, {"\u0061\u07F2\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u07F2\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u07F3\u0062", "\u00E0\u05AE\u07F3\u0315\u0062"}, {"\u0061\u07F3\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u07F3\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u07FD\u0062", "\u0061\u1DFA\u0316\u07FD\u059A\u0062"}, {"\u0061\u07FD\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u07FD\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u0816\u0062", "\u00E0\u05AE\u0816\u0315\u0062"}, {"\u0061\u0816\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0816\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0817\u0062", "\u00E0\u05AE\u0817\u0315\u0062"}, {"\u0061\u0817\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0817\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0818\u0062", "\u00E0\u05AE\u0818\u0315\u0062"}, {"\u0061\u0818\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0818\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0819\u0062", "\u00E0\u05AE\u0819\u0315\u0062"}, {"\u0061\u0819\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0819\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u081B\u0062", "\u00E0\u05AE\u081B\u0315\u0062"}, {"\u0061\u081B\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u081B\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u081C\u0062", "\u00E0\u05AE\u081C\u0315\u0062"}, {"\u0061\u081C\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u081C\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u081D\u0062", "\u00E0\u05AE\u081D\u0315\u0062"}, {"\u0061\u081D\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u081D\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u081E\u0062", "\u00E0\u05AE\u081E\u0315\u0062"}, {"\u0061\u081E\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u081E\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u081F\u0062", "\u00E0\u05AE\u081F\u0315\u0062"}, {"\u0061\u081F\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u081F\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0820\u0062", "\u00E0\u05AE\u0820\u0315\u0062"}, {"\u0061\u0820\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0820\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0821\u0062", "\u00E0\u05AE\u0821\u0315\u0062"}, {"\u0061\u0821\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0821\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0822\u0062", "\u00E0\u05AE\u0822\u0315\u0062"}, {"\u0061\u0822\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0822\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0823\u0062", "\u00E0\u05AE\u0823\u0315\u0062"}, {"\u0061\u0823\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0823\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0825\u0062", "\u00E0\u05AE\u0825\u0315\u0062"}, {"\u0061\u0825\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0825\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0826\u0062", "\u00E0\u05AE\u0826\u0315\u0062"}, {"\u0061\u0826\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0826\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0827\u0062", "\u00E0\u05AE\u0827\u0315\u0062"}, {"\u0061\u0827\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0827\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0829\u0062", "\u00E0\u05AE\u0829\u0315\u0062"}, {"\u0061\u0829\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0829\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u082A\u0062", "\u00E0\u05AE\u082A\u0315\u0062"}, {"\u0061\u082A\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u082A\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u082B\u0062", "\u00E0\u05AE\u082B\u0315\u0062"}, {"\u0061\u082B\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u082B\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u082C\u0062", "\u00E0\u05AE\u082C\u0315\u0062"}, {"\u0061\u082C\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u082C\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u082D\u0062", "\u00E0\u05AE\u082D\u0315\u0062"}, {"\u0061\u082D\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u082D\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0859\u0062", "\u0061\u1DFA\u0316\u0859\u059A\u0062"}, {"\u0061\u0859\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0859\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u085A\u0062", "\u0061\u1DFA\u0316\u085A\u059A\u0062"}, {"\u0061\u085A\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u085A\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u085B\u0062", "\u0061\u1DFA\u0316\u085B\u059A\u0062"}, {"\u0061\u085B\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u085B\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u0898\u0062", "\u00E0\u05AE\u0898\u0315\u0062"}, {"\u0061\u0898\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0898\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0899\u0062", "\u0061\u1DFA\u0316\u0899\u059A\u0062"}, {"\u0061\u0899\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0899\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u089A\u0062", "\u0061\u1DFA\u0316\u089A\u059A\u0062"}, {"\u0061\u089A\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u089A\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u089B\u0062", "\u0061\u1DFA\u0316\u089B\u059A\u0062"}, {"\u0061\u089B\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u089B\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u089C\u0062", "\u00E0\u05AE\u089C\u0315\u0062"}, {"\u0061\u089C\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u089C\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u089D\u0062", "\u00E0\u05AE\u089D\u0315\u0062"}, {"\u0061\u089D\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u089D\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u089E\u0062", "\u00E0\u05AE\u089E\u0315\u0062"}, {"\u0061\u089E\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u089E\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u089F\u0062", "\u00E0\u05AE\u089F\u0315\u0062"}, {"\u0061\u089F\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u089F\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08CA\u0062", "\u00E0\u05AE\u08CA\u0315\u0062"}, {"\u0061\u08CA\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08CA\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08CB\u0062", "\u00E0\u05AE\u08CB\u0315\u0062"}, {"\u0061\u08CB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08CB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08CC\u0062", "\u00E0\u05AE\u08CC\u0315\u0062"}, {"\u0061\u08CC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08CC\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08CD\u0062", "\u00E0\u05AE\u08CD\u0315\u0062"}, {"\u0061\u08CD\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08CD\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08CE\u0062", "\u00E0\u05AE\u08CE\u0315\u0062"}, {"\u0061\u08CE\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08CE\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u08CF\u0062", "\u0061\u1DFA\u0316\u08CF\u059A\u0062"}, {"\u0061\u08CF\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u08CF\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u08D0\u0062", "\u0061\u1DFA\u0316\u08D0\u059A\u0062"}, {"\u0061\u08D0\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u08D0\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u08D1\u0062", "\u0061\u1DFA\u0316\u08D1\u059A\u0062"}, {"\u0061\u08D1\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u08D1\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u08D2\u0062", "\u0061\u1DFA\u0316\u08D2\u059A\u0062"}, {"\u0061\u08D2\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u08D2\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u08D3\u0062", "\u0061\u1DFA\u0316\u08D3\u059A\u0062"}, {"\u0061\u08D3\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u08D3\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u08D4\u0062", "\u00E0\u05AE\u08D4\u0315\u0062"}, {"\u0061\u08D4\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08D4\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08D5\u0062", "\u00E0\u05AE\u08D5\u0315\u0062"}, {"\u0061\u08D5\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08D5\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08D6\u0062", "\u00E0\u05AE\u08D6\u0315\u0062"}, {"\u0061\u08D6\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08D6\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08D7\u0062", "\u00E0\u05AE\u08D7\u0315\u0062"}, {"\u0061\u08D7\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08D7\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08D8\u0062", "\u00E0\u05AE\u08D8\u0315\u0062"}, {"\u0061\u08D8\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08D8\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08D9\u0062", "\u00E0\u05AE\u08D9\u0315\u0062"}, {"\u0061\u08D9\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08D9\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08DA\u0062", "\u00E0\u05AE\u08DA\u0315\u0062"}, {"\u0061\u08DA\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08DA\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08DB\u0062", "\u00E0\u05AE\u08DB\u0315\u0062"}, {"\u0061\u08DB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08DB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08DC\u0062", "\u00E0\u05AE\u08DC\u0315\u0062"}, {"\u0061\u08DC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08DC\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08DD\u0062", "\u00E0\u05AE\u08DD\u0315\u0062"}, {"\u0061\u08DD\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08DD\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08DE\u0062", "\u00E0\u05AE\u08DE\u0315\u0062"}, {"\u0061\u08DE\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08DE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08DF\u0062", "\u00E0\u05AE\u08DF\u0315\u0062"}, {"\u0061\u08DF\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08DF\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08E0\u0062", "\u00E0\u05AE\u08E0\u0315\u0062"}, {"\u0061\u08E0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08E0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08E1\u0062", "\u00E0\u05AE\u08E1\u0315\u0062"}, {"\u0061\u08E1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08E1\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u08E3\u0062", "\u0061\u1DFA\u0316\u08E3\u059A\u0062"}, {"\u0061\u08E3\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u08E3\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u08E4\u0062", "\u00E0\u05AE\u08E4\u0315\u0062"}, {"\u0061\u08E4\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08E4\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08E5\u0062", "\u00E0\u05AE\u08E5\u0315\u0062"}, {"\u0061\u08E5\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08E5\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u08E6\u0062", "\u0061\u1DFA\u0316\u08E6\u059A\u0062"}, {"\u0061\u08E6\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u08E6\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u08E7\u0062", "\u00E0\u05AE\u08E7\u0315\u0062"}, {"\u0061\u08E7\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08E7\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08E8\u0062", "\u00E0\u05AE\u08E8\u0315\u0062"}, {"\u0061\u08E8\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08E8\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u08E9\u0062", "\u0061\u1DFA\u0316\u08E9\u059A\u0062"}, {"\u0061\u08E9\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u08E9\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u08EA\u0062", "\u00E0\u05AE\u08EA\u0315\u0062"}, {"\u0061\u08EA\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08EA\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08EB\u0062", "\u00E0\u05AE\u08EB\u0315\u0062"}, {"\u0061\u08EB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08EB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08EC\u0062", "\u00E0\u05AE\u08EC\u0315\u0062"}, {"\u0061\u08EC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08EC\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u08ED\u0062", "\u0061\u1DFA\u0316\u08ED\u059A\u0062"}, {"\u0061\u08ED\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u08ED\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u08EE\u0062", "\u0061\u1DFA\u0316\u08EE\u059A\u0062"}, {"\u0061\u08EE\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u08EE\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u08EF\u0062", "\u0061\u1DFA\u0316\u08EF\u059A\u0062"}, {"\u0061\u08EF\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u08EF\u0316\u059A\u0062"}, {"\u0061\u064C\u064B\uFB1E\u08F0\u0062", "\u0061\uFB1E\u064B\u08F0\u064C\u0062"}, {"\u0061\u08F0\u064C\u064B\uFB1E\u0062", "\u0061\uFB1E\u08F0\u064B\u064C\u0062"}, {"\u0061\u064D\u064C\u064B\u08F1\u0062", "\u0061\u064B\u064C\u08F1\u064D\u0062"}, {"\u0061\u08F1\u064D\u064C\u064B\u0062", "\u0061\u064B\u08F1\u064C\u064D\u0062"}, {"\u0061\u0618\u064D\u064C\u08F2\u0062", "\u0061\u064C\u064D\u08F2\u0618\u0062"}, {"\u0061\u08F2\u0618\u064D\u064C\u0062", "\u0061\u064C\u08F2\u064D\u0618\u0062"}, {"\u0061\u0315\u0300\u05AE\u08F3\u0062", "\u00E0\u05AE\u08F3\u0315\u0062"}, {"\u0061\u08F3\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08F3\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08F4\u0062", "\u00E0\u05AE\u08F4\u0315\u0062"}, {"\u0061\u08F4\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08F4\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08F5\u0062", "\u00E0\u05AE\u08F5\u0315\u0062"}, {"\u0061\u08F5\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08F5\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u08F6\u0062", "\u0061\u1DFA\u0316\u08F6\u059A\u0062"}, {"\u0061\u08F6\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u08F6\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u08F7\u0062", "\u00E0\u05AE\u08F7\u0315\u0062"}, {"\u0061\u08F7\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08F7\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08F8\u0062", "\u00E0\u05AE\u08F8\u0315\u0062"}, {"\u0061\u08F8\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08F8\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u08F9\u0062", "\u0061\u1DFA\u0316\u08F9\u059A\u0062"}, {"\u0061\u08F9\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u08F9\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u08FA\u0062", "\u0061\u1DFA\u0316\u08FA\u059A\u0062"}, {"\u0061\u08FA\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u08FA\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u08FB\u0062", "\u00E0\u05AE\u08FB\u0315\u0062"}, {"\u0061\u08FB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08FB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08FC\u0062", "\u00E0\u05AE\u08FC\u0315\u0062"}, {"\u0061\u08FC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08FC\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08FD\u0062", "\u00E0\u05AE\u08FD\u0315\u0062"}, {"\u0061\u08FD\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08FD\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08FE\u0062", "\u00E0\u05AE\u08FE\u0315\u0062"}, {"\u0061\u08FE\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08FE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u08FF\u0062", "\u00E0\u05AE\u08FF\u0315\u0062"}, {"\u0061\u08FF\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u08FF\u0300\u0315\u0062"}, {"\u0061\u3099\u093C\U00016FF0\u093C\u0062", "\u0061\U00016FF0\u093C\u093C\u3099\u0062"}, {"\u0061\u093C\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\u093C\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\u094D\u0062", "\u0061\u3099\u094D\u094D\u05B0\u0062"}, {"\u0061\u094D\u05B0\u094D\u3099\u0062", "\u0061\u3099\u094D\u094D\u05B0\u0062"}, {"\u0061\u0315\u0300\u05AE\u0951\u0062", "\u00E0\u05AE\u0951\u0315\u0062"}, {"\u0061\u0951\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0951\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0952\u0062", "\u0061\u1DFA\u0316\u0952\u059A\u0062"}, {"\u0061\u0952\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0952\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u0953\u0062", "\u00E0\u05AE\u0953\u0315\u0062"}, {"\u0061\u0953\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0953\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0954\u0062", "\u00E0\u05AE\u0954\u0315\u0062"}, {"\u0061\u0954\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0954\u0300\u0315\u0062"}, {"\u0061\u3099\u093C\U00016FF0\u09BC\u0062", "\u0061\U00016FF0\u093C\u09BC\u3099\u0062"}, {"\u0061\u09BC\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\u09BC\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\u09CD\u0062", "\u0061\u3099\u094D\u09CD\u05B0\u0062"}, {"\u0061\u09CD\u05B0\u094D\u3099\u0062", "\u0061\u3099\u09CD\u094D\u05B0\u0062"}, {"\u0061\u0315\u0300\u05AE\u09FE\u0062", "\u00E0\u05AE\u09FE\u0315\u0062"}, {"\u0061\u09FE\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u09FE\u0300\u0315\u0062"}, {"\u0061\u3099\u093C\U00016FF0\u0A3C\u0062", "\u0061\U00016FF0\u093C\u0A3C\u3099\u0062"}, {"\u0061\u0A3C\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\u0A3C\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\u0A4D\u0062", "\u0061\u3099\u094D\u0A4D\u05B0\u0062"}, {"\u0061\u0A4D\u05B0\u094D\u3099\u0062", "\u0061\u3099\u0A4D\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\u0ABC\u0062", "\u0061\U00016FF0\u093C\u0ABC\u3099\u0062"}, {"\u0061\u0ABC\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\u0ABC\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\u0ACD\u0062", "\u0061\u3099\u094D\u0ACD\u05B0\u0062"}, {"\u0061\u0ACD\u05B0\u094D\u3099\u0062", "\u0061\u3099\u0ACD\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\u0B3C\u0062", "\u0061\U00016FF0\u093C\u0B3C\u3099\u0062"}, {"\u0061\u0B3C\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\u0B3C\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\u0B4D\u0062", "\u0061\u3099\u094D\u0B4D\u05B0\u0062"}, {"\u0061\u0B4D\u05B0\u094D\u3099\u0062", "\u0061\u3099\u0B4D\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\u0BCD\u0062", "\u0061\u3099\u094D\u0BCD\u05B0\u0062"}, {"\u0061\u0BCD\u05B0\u094D\u3099\u0062", "\u0061\u3099\u0BCD\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\u0C3C\u0062", "\u0061\U00016FF0\u093C\u0C3C\u3099\u0062"}, {"\u0061\u0C3C\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\u0C3C\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\u0C4D\u0062", "\u0061\u3099\u094D\u0C4D\u05B0\u0062"}, {"\u0061\u0C4D\u05B0\u094D\u3099\u0062", "\u0061\u3099\u0C4D\u094D\u05B0\u0062"}, {"\u0061\u0C56\u0C55\u0711\u0C55\u0062", "\u0061\u0711\u0C55\u0C55\u0C56\u0062"}, {"\u0061\u0C55\u0C56\u0C55\u0711\u0062", "\u0061\u0711\u0C55\u0C55\u0C56\u0062"}, {"\u0061\u0E38\u0C56\u0C55\u0C56\u0062", "\u0061\u0C55\u0C56\u0C56\u0E38\u0062"}, {"\u0061\u0C56\u0E38\u0C56\u0C55\u0062", "\u0061\u0C55\u0C56\u0C56\u0E38\u0062"}, {"\u0061\u3099\u093C\U00016FF0\u0CBC\u0062", "\u0061\U00016FF0\u093C\u0CBC\u3099\u0062"}, {"\u0061\u0CBC\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\u0CBC\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\u0CCD\u0062", "\u0061\u3099\u094D\u0CCD\u05B0\u0062"}, {"\u0061\u0CCD\u05B0\u094D\u3099\u0062", "\u0061\u3099\u0CCD\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\u0D3B\u0062", "\u0061\u3099\u094D\u0D3B\u05B0\u0062"}, {"\u0061\u0D3B\u05B0\u094D\u3099\u0062", "\u0061\u3099\u0D3B\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\u0D3C\u0062", "\u0061\u3099\u094D\u0D3C\u05B0\u0062"}, {"\u0061\u0D3C\u05B0\u094D\u3099\u0062", "\u0061\u3099\u0D3C\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\u0D4D\u0062", "\u0061\u3099\u094D\u0D4D\u05B0\u0062"}, {"\u0061\u0D4D\u05B0\u094D\u3099\u0062", "\u0061\u3099\u0D4D\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\u0DCA\u0062", "\u0061\u3099\u094D\u0DCA\u05B0\u0062"}, {"\u0061\u0DCA\u05B0\u094D\u3099\u0062", "\u0061\u3099\u0DCA\u094D\u05B0\u0062"}, {"\u0061\u0E48\u0E38\u0C56\u0E38\u0062", "\u0061\u0C56\u0E38\u0E38\u0E48\u0062"}, {"\u0061\u0E38\u0E48\u0E38\u0C56\u0062", "\u0061\u0C56\u0E38\u0E38\u0E48\u0062"}, {"\u0061\u0E48\u0E38\u0C56\u0E39\u0062", "\u0061\u0C56\u0E38\u0E39\u0E48\u0062"}, {"\u0061\u0E39\u0E48\u0E38\u0C56\u0062", "\u0061\u0C56\u0E39\u0E38\u0E48\u0062"}, {"\u0061\u05B0\u094D\u3099\u0E3A\u0062", "\u0061\u3099\u094D\u0E3A\u05B0\u0062"}, {"\u0061\u0E3A\u05B0\u094D\u3099\u0062", "\u0061\u3099\u0E3A\u094D\u05B0\u0062"}, {"\u0061\u0EB8\u0E48\u0E38\u0E48\u0062", "\u0061\u0E38\u0E48\u0E48\u0EB8\u0062"}, {"\u0061\u0E48\u0EB8\u0E48\u0E38\u0062", "\u0061\u0E38\u0E48\u0E48\u0EB8\u0062"}, {"\u0061\u0EB8\u0E48\u0E38\u0E49\u0062", "\u0061\u0E38\u0E48\u0E49\u0EB8\u0062"}, {"\u0061\u0E49\u0EB8\u0E48\u0E38\u0062", "\u0061\u0E38\u0E49\u0E48\u0EB8\u0062"}, {"\u0061\u0EB8\u0E48\u0E38\u0E4A\u0062", "\u0061\u0E38\u0E48\u0E4A\u0EB8\u0062"}, {"\u0061\u0E4A\u0EB8\u0E48\u0E38\u0062", "\u0061\u0E38\u0E4A\u0E48\u0EB8\u0062"}, {"\u0061\u0EB8\u0E48\u0E38\u0E4B\u0062", "\u0061\u0E38\u0E48\u0E4B\u0EB8\u0062"}, {"\u0061\u0E4B\u0EB8\u0E48\u0E38\u0062", "\u0061\u0E38\u0E4B\u0E48\u0EB8\u0062"}, {"\u0061\u0EC8\u0EB8\u0E48\u0EB8\u0062", "\u0061\u0E48\u0EB8\u0EB8\u0EC8\u0062"}, {"\u0061\u0EB8\u0EC8\u0EB8\u0E48\u0062", "\u0061\u0E48\u0EB8\u0EB8\u0EC8\u0062"}, {"\u0061\u0EC8\u0EB8\u0E48\u0EB9\u0062", "\u0061\u0E48\u0EB8\u0EB9\u0EC8\u0062"}, {"\u0061\u0EB9\u0EC8\u0EB8\u0E48\u0062", "\u0061\u0E48\u0EB9\u0EB8\u0EC8\u0062"}, {"\u0061\u05B0\u094D\u3099\u0EBA\u0062", "\u0061\u3099\u094D\u0EBA\u05B0\u0062"}, {"\u0061\u0EBA\u05B0\u094D\u3099\u0062", "\u0061\u3099\u0EBA\u094D\u05B0\u0062"}, {"\u0061\u0F71\u0EC8\u0EB8\u0EC8\u0062", "\u0061\u0EB8\u0EC8\u0EC8\u0F71\u0062"}, {"\u0061\u0EC8\u0F71\u0EC8\u0EB8\u0062", "\u0061\u0EB8\u0EC8\u0EC8\u0F71\u0062"}, {"\u0061\u0F71\u0EC8\u0EB8\u0EC9\u0062", "\u0061\u0EB8\u0EC8\u0EC9\u0F71\u0062"}, {"\u0061\u0EC9\u0F71\u0EC8\u0EB8\u0062", "\u0061\u0EB8\u0EC9\u0EC8\u0F71\u0062"}, {"\u0061\u0F71\u0EC8\u0EB8\u0ECA\u0062", "\u0061\u0EB8\u0EC8\u0ECA\u0F71\u0062"}, {"\u0061\u0ECA\u0F71\u0EC8\u0EB8\u0062", "\u0061\u0EB8\u0ECA\u0EC8\u0F71\u0062"}, {"\u0061\u0F71\u0EC8\u0EB8\u0ECB\u0062", "\u0061\u0EB8\u0EC8\u0ECB\u0F71\u0062"}, {"\u0061\u0ECB\u0F71\u0EC8\u0EB8\u0062", "\u0061\u0EB8\u0ECB\u0EC8\u0F71\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0F18\u0062", "\u0061\u1DFA\u0316\u0F18\u059A\u0062"}, {"\u0061\u0F18\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0F18\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0F19\u0062", "\u0061\u1DFA\u0316\u0F19\u059A\u0062"}, {"\u0061\u0F19\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0F19\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0F35\u0062", "\u0061\u1DFA\u0316\u0F35\u059A\u0062"}, {"\u0061\u0F35\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0F35\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0F37\u0062", "\u0061\u1DFA\u0316\u0F37\u059A\u0062"}, {"\u0061\u0F37\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0F37\u0316\u059A\u0062"}, {"\u0061\u1DFA\u031B\u1DCE\u0F39\u0062", "\u0061\u1DCE\u031B\u0F39\u1DFA\u0062"}, {"\u0061\u0F39\u1DFA\u031B\u1DCE\u0062", "\u0061\u1DCE\u0F39\u031B\u1DFA\u0062"}, {"\u0061\u0F72\u0F71\u0EC8\u0F71\u0062", "\u0061\u0EC8\u0F71\u0F71\u0F72\u0062"}, {"\u0061\u0F71\u0F72\u0F71\u0EC8\u0062", "\u0061\u0EC8\u0F71\u0F71\u0F72\u0062"}, {"\u0061\u0F74\u0F72\u0F71\u0F72\u0062", "\u0061\u0F71\u0F72\u0F72\u0F74\u0062"}, {"\u0061\u0F72\u0F74\u0F72\u0F71\u0062", "\u0061\u0F71\u0F72\u0F72\u0F74\u0062"}, {"\u0061\u0321\u0F74\u0F72\u0F74\u0062", "\u0061\u0F72\u0F74\u0F74\u0321\u0062"}, {"\u0061\u0F74\u0321\u0F74\u0F72\u0062", "\u0061\u0F72\u0F74\u0F74\u0321\u0062"}, {"\u0061\u0F74\u0F72\u0F71\u0F7A\u0062", "\u0061\u0F71\u0F72\u0F7A\u0F74\u0062"}, {"\u0061\u0F7A\u0F74\u0F72\u0F71\u0062", "\u0061\u0F71\u0F7A\u0F72\u0F74\u0062"}, {"\u0061\u0F74\u0F72\u0F71\u0F7B\u0062", "\u0061\u0F71\u0F72\u0F7B\u0F74\u0062"}, {"\u0061\u0F7B\u0F74\u0F72\u0F71\u0062", "\u0061\u0F71\u0F7B\u0F72\u0F74\u0062"}, {"\u0061\u0F74\u0F72\u0F71\u0F7C\u0062", "\u0061\u0F71\u0F72\u0F7C\u0F74\u0062"}, {"\u0061\u0F7C\u0F74\u0F72\u0F71\u0062", "\u0061\u0F71\u0F7C\u0F72\u0F74\u0062"}, {"\u0061\u0F74\u0F72\u0F71\u0F7D\u0062", "\u0061\u0F71\u0F72\u0F7D\u0F74\u0062"}, {"\u0061\u0F7D\u0F74\u0F72\u0F71\u0062", "\u0061\u0F71\u0F7D\u0F72\u0F74\u0062"}, {"\u0061\u0F74\u0F72\u0F71\u0F80\u0062", "\u0061\u0F71\u0F72\u0F80\u0F74\u0062"}, {"\u0061\u0F80\u0F74\u0F72\u0F71\u0062", "\u0061\u0F71\u0F80\u0F72\u0F74\u0062"}, {"\u0061\u0315\u0300\u05AE\u0F82\u0062", "\u00E0\u05AE\u0F82\u0315\u0062"}, {"\u0061\u0F82\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0F82\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0F83\u0062", "\u00E0\u05AE\u0F83\u0315\u0062"}, {"\u0061\u0F83\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0F83\u0300\u0315\u0062"}, {"\u0061\u05B0\u094D\u3099\u0F84\u0062", "\u0061\u3099\u094D\u0F84\u05B0\u0062"}, {"\u0061\u0F84\u05B0\u094D\u3099\u0062", "\u0061\u3099\u0F84\u094D\u05B0\u0062"}, {"\u0061\u0315\u0300\u05AE\u0F86\u0062", "\u00E0\u05AE\u0F86\u0315\u0062"}, {"\u0061\u0F86\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0F86\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u0F87\u0062", "\u00E0\u05AE\u0F87\u0315\u0062"}, {"\u0061\u0F87\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u0F87\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u0FC6\u0062", "\u0061\u1DFA\u0316\u0FC6\u059A\u0062"}, {"\u0061\u0FC6\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u0FC6\u0316\u059A\u0062"}, {"\u0061\u3099\u093C\U00016FF0\u1037\u0062", "\u0061\U00016FF0\u093C\u1037\u3099\u0062"}, {"\u0061\u1037\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\u1037\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\u1039\u0062", "\u0061\u3099\u094D\u1039\u05B0\u0062"}, {"\u0061\u1039\u05B0\u094D\u3099\u0062", "\u0061\u3099\u1039\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\u103A\u0062", "\u0061\u3099\u094D\u103A\u05B0\u0062"}, {"\u0061\u103A\u05B0\u094D\u3099\u0062", "\u0061\u3099\u103A\u094D\u05B0\u0062"}, {"\u0061\u059A\u0316\u1DFA\u108D\u0062", "\u0061\u1DFA\u0316\u108D\u059A\u0062"}, {"\u0061\u108D\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u108D\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u135D\u0062", "\u00E0\u05AE\u135D\u0315\u0062"}, {"\u0061\u135D\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u135D\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u135E\u0062", "\u00E0\u05AE\u135E\u0315\u0062"}, {"\u0061\u135E\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u135E\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u135F\u0062", "\u00E0\u05AE\u135F\u0315\u0062"}, {"\u0061\u135F\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u135F\u0300\u0315\u0062"}, {"\u0061\u05B0\u094D\u3099\u1714\u0062", "\u0061\u3099\u094D\u1714\u05B0\u0062"}, {"\u0061\u1714\u05B0\u094D\u3099\u0062", "\u0061\u3099\u1714\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\u1715\u0062", "\u0061\u3099\u094D\u1715\u05B0\u0062"}, {"\u0061\u1715\u05B0\u094D\u3099\u0062", "\u0061\u3099\u1715\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\u1734\u0062", "\u0061\u3099\u094D\u1734\u05B0\u0062"}, {"\u0061\u1734\u05B0\u094D\u3099\u0062", "\u0061\u3099\u1734\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\u17D2\u0062", "\u0061\u3099\u094D\u17D2\u05B0\u0062"}, {"\u0061\u17D2\u05B0\u094D\u3099\u0062", "\u0061\u3099\u17D2\u094D\u05B0\u0062"}, {"\u0061\u0315\u0300\u05AE\u17DD\u0062", "\u00E0\u05AE\u17DD\u0315\u0062"}, {"\u0061\u17DD\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u17DD\u0300\u0315\u0062"}, {"\u0061\u0300\u05AE\U0001D16D\u18A9\u0062", "\u00E0\U0001D16D\u05AE\u18A9\u0062"}, {"\u0061\u18A9\u0300\u05AE\U0001D16D\u0062", "\u00E0\U0001D16D\u18A9\u05AE\u0062"}, {"\u0061\u302E\u059A\u0316\u1939\u0062", "\u0061\u0316\u059A\u1939\u302E\u0062"}, {"\u0061\u1939\u302E\u059A\u0316\u0062", "\u0061\u0316\u1939\u059A\u302E\u0062"}, {"\u0061\u0315\u0300\u05AE\u193A\u0062", "\u00E0\u05AE\u193A\u0315\u0062"}, {"\u0061\u193A\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u193A\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u193B\u0062", "\u0061\u1DFA\u0316\u193B\u059A\u0062"}, {"\u0061\u193B\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u193B\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u1A17\u0062", "\u00E0\u05AE\u1A17\u0315\u0062"}, {"\u0061\u1A17\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1A17\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1A18\u0062", "\u0061\u1DFA\u0316\u1A18\u059A\u0062"}, {"\u0061\u1A18\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1A18\u0316\u059A\u0062"}, {"\u0061\u05B0\u094D\u3099\u1A60\u0062", "\u0061\u3099\u094D\u1A60\u05B0\u0062"}, {"\u0061\u1A60\u05B0\u094D\u3099\u0062", "\u0061\u3099\u1A60\u094D\u05B0\u0062"}, {"\u0061\u0315\u0300\u05AE\u1A75\u0062", "\u00E0\u05AE\u1A75\u0315\u0062"}, {"\u0061\u1A75\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1A75\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1A76\u0062", "\u00E0\u05AE\u1A76\u0315\u0062"}, {"\u0061\u1A76\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1A76\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1A77\u0062", "\u00E0\u05AE\u1A77\u0315\u0062"}, {"\u0061\u1A77\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1A77\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1A78\u0062", "\u00E0\u05AE\u1A78\u0315\u0062"}, {"\u0061\u1A78\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1A78\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1A79\u0062", "\u00E0\u05AE\u1A79\u0315\u0062"}, {"\u0061\u1A79\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1A79\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1A7A\u0062", "\u00E0\u05AE\u1A7A\u0315\u0062"}, {"\u0061\u1A7A\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1A7A\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1A7B\u0062", "\u00E0\u05AE\u1A7B\u0315\u0062"}, {"\u0061\u1A7B\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1A7B\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1A7C\u0062", "\u00E0\u05AE\u1A7C\u0315\u0062"}, {"\u0061\u1A7C\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1A7C\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1A7F\u0062", "\u0061\u1DFA\u0316\u1A7F\u059A\u0062"}, {"\u0061\u1A7F\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1A7F\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u1AB0\u0062", "\u00E0\u05AE\u1AB0\u0315\u0062"}, {"\u0061\u1AB0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1AB0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1AB1\u0062", "\u00E0\u05AE\u1AB1\u0315\u0062"}, {"\u0061\u1AB1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1AB1\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1AB2\u0062", "\u00E0\u05AE\u1AB2\u0315\u0062"}, {"\u0061\u1AB2\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1AB2\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1AB3\u0062", "\u00E0\u05AE\u1AB3\u0315\u0062"}, {"\u0061\u1AB3\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1AB3\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1AB4\u0062", "\u00E0\u05AE\u1AB4\u0315\u0062"}, {"\u0061\u1AB4\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1AB4\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1AB5\u0062", "\u0061\u1DFA\u0316\u1AB5\u059A\u0062"}, {"\u0061\u1AB5\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1AB5\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1AB6\u0062", "\u0061\u1DFA\u0316\u1AB6\u059A\u0062"}, {"\u0061\u1AB6\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1AB6\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1AB7\u0062", "\u0061\u1DFA\u0316\u1AB7\u059A\u0062"}, {"\u0061\u1AB7\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1AB7\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1AB8\u0062", "\u0061\u1DFA\u0316\u1AB8\u059A\u0062"}, {"\u0061\u1AB8\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1AB8\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1AB9\u0062", "\u0061\u1DFA\u0316\u1AB9\u059A\u0062"}, {"\u0061\u1AB9\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1AB9\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1ABA\u0062", "\u0061\u1DFA\u0316\u1ABA\u059A\u0062"}, {"\u0061\u1ABA\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1ABA\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u1ABB\u0062", "\u00E0\u05AE\u1ABB\u0315\u0062"}, {"\u0061\u1ABB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1ABB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1ABC\u0062", "\u00E0\u05AE\u1ABC\u0315\u0062"}, {"\u0061\u1ABC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1ABC\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1ABD\u0062", "\u0061\u1DFA\u0316\u1ABD\u059A\u0062"}, {"\u0061\u1ABD\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1ABD\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1ABF\u0062", "\u0061\u1DFA\u0316\u1ABF\u059A\u0062"}, {"\u0061\u1ABF\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1ABF\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1AC0\u0062", "\u0061\u1DFA\u0316\u1AC0\u059A\u0062"}, {"\u0061\u1AC0\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1AC0\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u1AC1\u0062", "\u00E0\u05AE\u1AC1\u0315\u0062"}, {"\u0061\u1AC1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1AC1\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1AC2\u0062", "\u00E0\u05AE\u1AC2\u0315\u0062"}, {"\u0061\u1AC2\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1AC2\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1AC3\u0062", "\u0061\u1DFA\u0316\u1AC3\u059A\u0062"}, {"\u0061\u1AC3\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1AC3\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1AC4\u0062", "\u0061\u1DFA\u0316\u1AC4\u059A\u0062"}, {"\u0061\u1AC4\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1AC4\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u1AC5\u0062", "\u00E0\u05AE\u1AC5\u0315\u0062"}, {"\u0061\u1AC5\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1AC5\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1AC6\u0062", "\u00E0\u05AE\u1AC6\u0315\u0062"}, {"\u0061\u1AC6\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1AC6\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1AC7\u0062", "\u00E0\u05AE\u1AC7\u0315\u0062"}, {"\u0061\u1AC7\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1AC7\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1AC8\u0062", "\u00E0\u05AE\u1AC8\u0315\u0062"}, {"\u0061\u1AC8\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1AC8\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1AC9\u0062", "\u00E0\u05AE\u1AC9\u0315\u0062"}, {"\u0061\u1AC9\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1AC9\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1ACA\u0062", "\u0061\u1DFA\u0316\u1ACA\u059A\u0062"}, {"\u0061\u1ACA\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1ACA\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u1ACB\u0062", "\u00E0\u05AE\u1ACB\u0315\u0062"}, {"\u0061\u1ACB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1ACB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1ACC\u0062", "\u00E0\u05AE\u1ACC\u0315\u0062"}, {"\u0061\u1ACC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1ACC\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1ACD\u0062", "\u00E0\u05AE\u1ACD\u0315\u0062"}, {"\u0061\u1ACD\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1ACD\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1ACE\u0062", "\u00E0\u05AE\u1ACE\u0315\u0062"}, {"\u0061\u1ACE\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1ACE\u0300\u0315\u0062"}, {"\u0061\u3099\u093C\U00016FF0\u1B34\u0062", "\u0061\U00016FF0\u093C\u1B34\u3099\u0062"}, {"\u0061\u1B34\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\u1B34\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\u1B44\u0062", "\u0061\u3099\u094D\u1B44\u05B0\u0062"}, {"\u0061\u1B44\u05B0\u094D\u3099\u0062", "\u0061\u3099\u1B44\u094D\u05B0\u0062"}, {"\u0061\u0315\u0300\u05AE\u1B6B\u0062", "\u00E0\u05AE\u1B6B\u0315\u0062"}, {"\u0061\u1B6B\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1B6B\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1B6C\u0062", "\u0061\u1DFA\u0316\u1B6C\u059A\u0062"}, {"\u0061\u1B6C\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1B6C\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u1B6D\u0062", "\u00E0\u05AE\u1B6D\u0315\u0062"}, {"\u0061\u1B6D\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1B6D\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1B6E\u0062", "\u00E0\u05AE\u1B6E\u0315\u0062"}, {"\u0061\u1B6E\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1B6E\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1B6F\u0062", "\u00E0\u05AE\u1B6F\u0315\u0062"}, {"\u0061\u1B6F\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1B6F\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1B70\u0062", "\u00E0\u05AE\u1B70\u0315\u0062"}, {"\u0061\u1B70\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1B70\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1B71\u0062", "\u00E0\u05AE\u1B71\u0315\u0062"}, {"\u0061\u1B71\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1B71\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1B72\u0062", "\u00E0\u05AE\u1B72\u0315\u0062"}, {"\u0061\u1B72\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1B72\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1B73\u0062", "\u00E0\u05AE\u1B73\u0315\u0062"}, {"\u0061\u1B73\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1B73\u0300\u0315\u0062"}, {"\u0061\u05B0\u094D\u3099\u1BAA\u0062", "\u0061\u3099\u094D\u1BAA\u05B0\u0062"}, {"\u0061\u1BAA\u05B0\u094D\u3099\u0062", "\u0061\u3099\u1BAA\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\u1BAB\u0062", "\u0061\u3099\u094D\u1BAB\u05B0\u0062"}, {"\u0061\u1BAB\u05B0\u094D\u3099\u0062", "\u0061\u3099\u1BAB\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\u1BE6\u0062", "\u0061\U00016FF0\u093C\u1BE6\u3099\u0062"}, {"\u0061\u1BE6\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\u1BE6\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\u1BF2\u0062", "\u0061\u3099\u094D\u1BF2\u05B0\u0062"}, {"\u0061\u1BF2\u05B0\u094D\u3099\u0062", "\u0061\u3099\u1BF2\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\u1BF3\u0062", "\u0061\u3099\u094D\u1BF3\u05B0\u0062"}, {"\u0061\u1BF3\u05B0\u094D\u3099\u0062", "\u0061\u3099\u1BF3\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\u1C37\u0062", "\u0061\U00016FF0\u093C\u1C37\u3099\u0062"}, {"\u0061\u1C37\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\u1C37\u093C\u3099\u0062"}, {"\u0061\u0315\u0300\u05AE\u1CD0\u0062", "\u00E0\u05AE\u1CD0\u0315\u0062"}, {"\u0061\u1CD0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1CD0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1CD1\u0062", "\u00E0\u05AE\u1CD1\u0315\u0062"}, {"\u0061\u1CD1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1CD1\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1CD2\u0062", "\u00E0\u05AE\u1CD2\u0315\u0062"}, {"\u0061\u1CD2\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1CD2\u0300\u0315\u0062"}, {"\u0061\U00016FF0\u0334\u1CD4\u0062", "\u0061\u0334\u1CD4\U00016FF0\u0062"}, {"\u0061\u1CD4\U00016FF0\u0334\u0062", "\u0061\u1CD4\u0334\U00016FF0\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1CD5\u0062", "\u0061\u1DFA\u0316\u1CD5\u059A\u0062"}, {"\u0061\u1CD5\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1CD5\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1CD6\u0062", "\u0061\u1DFA\u0316\u1CD6\u059A\u0062"}, {"\u0061\u1CD6\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1CD6\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1CD7\u0062", "\u0061\u1DFA\u0316\u1CD7\u059A\u0062"}, {"\u0061\u1CD7\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1CD7\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1CD8\u0062", "\u0061\u1DFA\u0316\u1CD8\u059A\u0062"}, {"\u0061\u1CD8\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1CD8\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1CD9\u0062", "\u0061\u1DFA\u0316\u1CD9\u059A\u0062"}, {"\u0061\u1CD9\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1CD9\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u1CDA\u0062", "\u00E0\u05AE\u1CDA\u0315\u0062"}, {"\u0061\u1CDA\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1CDA\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1CDB\u0062", "\u00E0\u05AE\u1CDB\u0315\u0062"}, {"\u0061\u1CDB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1CDB\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1CDC\u0062", "\u0061\u1DFA\u0316\u1CDC\u059A\u0062"}, {"\u0061\u1CDC\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1CDC\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1CDD\u0062", "\u0061\u1DFA\u0316\u1CDD\u059A\u0062"}, {"\u0061\u1CDD\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1CDD\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1CDE\u0062", "\u0061\u1DFA\u0316\u1CDE\u059A\u0062"}, {"\u0061\u1CDE\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1CDE\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1CDF\u0062", "\u0061\u1DFA\u0316\u1CDF\u059A\u0062"}, {"\u0061\u1CDF\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1CDF\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u1CE0\u0062", "\u00E0\u05AE\u1CE0\u0315\u0062"}, {"\u0061\u1CE0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1CE0\u0300\u0315\u0062"}, {"\u0061\U00016FF0\u0334\u1CE2\u0062", "\u0061\u0334\u1CE2\U00016FF0\u0062"}, {"\u0061\u1CE2\U00016FF0\u0334\u0062", "\u0061\u1CE2\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\u1CE3\u0062", "\u0061\u0334\u1CE3\U00016FF0\u0062"}, {"\u0061\u1CE3\U00016FF0\u0334\u0062", "\u0061\u1CE3\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\u1CE4\u0062", "\u0061\u0334\u1CE4\U00016FF0\u0062"}, {"\u0061\u1CE4\U00016FF0\u0334\u0062", "\u0061\u1CE4\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\u1CE5\u0062", "\u0061\u0334\u1CE5\U00016FF0\u0062"}, {"\u0061\u1CE5\U00016FF0\u0334\u0062", "\u0061\u1CE5\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\u1CE6\u0062", "\u0061\u0334\u1CE6\U00016FF0\u0062"}, {"\u0061\u1CE6\U00016FF0\u0334\u0062", "\u0061\u1CE6\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\u1CE7\u0062", "\u0061\u0334\u1CE7\U00016FF0\u0062"}, {"\u0061\u1CE7\U00016FF0\u0334\u0062", "\u0061\u1CE7\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\u1CE8\u0062", "\u0061\u0334\u1CE8\U00016FF0\u0062"}, {"\u0061\u1CE8\U00016FF0\u0334\u0062", "\u0061\u1CE8\u0334\U00016FF0\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1CED\u0062", "\u0061\u1DFA\u0316\u1CED\u059A\u0062"}, {"\u0061\u1CED\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1CED\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u1CF4\u0062", "\u00E0\u05AE\u1CF4\u0315\u0062"}, {"\u0061\u1CF4\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1CF4\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1CF8\u0062", "\u00E0\u05AE\u1CF8\u0315\u0062"}, {"\u0061\u1CF8\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1CF8\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1CF9\u0062", "\u00E0\u05AE\u1CF9\u0315\u0062"}, {"\u0061\u1CF9\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1CF9\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DC0\u0062", "\u00E0\u05AE\u1DC0\u0315\u0062"}, {"\u0061\u1DC0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DC0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DC1\u0062", "\u00E0\u05AE\u1DC1\u0315\u0062"}, {"\u0061\u1DC1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DC1\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1DC2\u0062", "\u0061\u1DFA\u0316\u1DC2\u059A\u0062"}, {"\u0061\u1DC2\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1DC2\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DC3\u0062", "\u00E0\u05AE\u1DC3\u0315\u0062"}, {"\u0061\u1DC3\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DC3\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DC4\u0062", "\u00E0\u05AE\u1DC4\u0315\u0062"}, {"\u0061\u1DC4\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DC4\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DC5\u0062", "\u00E0\u05AE\u1DC5\u0315\u0062"}, {"\u0061\u1DC5\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DC5\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DC6\u0062", "\u00E0\u05AE\u1DC6\u0315\u0062"}, {"\u0061\u1DC6\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DC6\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DC7\u0062", "\u00E0\u05AE\u1DC7\u0315\u0062"}, {"\u0061\u1DC7\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DC7\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DC8\u0062", "\u00E0\u05AE\u1DC8\u0315\u0062"}, {"\u0061\u1DC8\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DC8\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DC9\u0062", "\u00E0\u05AE\u1DC9\u0315\u0062"}, {"\u0061\u1DC9\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DC9\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1DCA\u0062", "\u0061\u1DFA\u0316\u1DCA\u059A\u0062"}, {"\u0061\u1DCA\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1DCA\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DCB\u0062", "\u00E0\u05AE\u1DCB\u0315\u0062"}, {"\u0061\u1DCB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DCB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DCC\u0062", "\u00E0\u05AE\u1DCC\u0315\u0062"}, {"\u0061\u1DCC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DCC\u0300\u0315\u0062"}, {"\u0061\u0345\u035D\u035C\u1DCD\u0062", "\u0061\u035C\u035D\u1DCD\u0345\u0062"}, {"\u0061\u1DCD\u0345\u035D\u035C\u0062", "\u0061\u035C\u1DCD\u035D\u0345\u0062"}, {"\u0061\u031B\u1DCE\u0321\u1DCE\u0062", "\u0061\u0321\u1DCE\u1DCE\u031B\u0062"}, {"\u0061\u1DCE\u031B\u1DCE\u0321\u0062", "\u0061\u0321\u1DCE\u1DCE\u031B\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1DCF\u0062", "\u0061\u1DFA\u0316\u1DCF\u059A\u0062"}, {"\u0061\u1DCF\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1DCF\u0316\u059A\u0062"}, {"\u0061\u1DCE\u0321\u0F74\u1DD0\u0062", "\u0061\u0F74\u0321\u1DD0\u1DCE\u0062"}, {"\u0061\u1DD0\u1DCE\u0321\u0F74\u0062", "\u0061\u0F74\u1DD0\u0321\u1DCE\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DD1\u0062", "\u00E0\u05AE\u1DD1\u0315\u0062"}, {"\u0061\u1DD1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DD1\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DD2\u0062", "\u00E0\u05AE\u1DD2\u0315\u0062"}, {"\u0061\u1DD2\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DD2\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DD3\u0062", "\u00E0\u05AE\u1DD3\u0315\u0062"}, {"\u0061\u1DD3\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DD3\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DD4\u0062", "\u00E0\u05AE\u1DD4\u0315\u0062"}, {"\u0061\u1DD4\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DD4\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DD5\u0062", "\u00E0\u05AE\u1DD5\u0315\u0062"}, {"\u0061\u1DD5\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DD5\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DD6\u0062", "\u00E0\u05AE\u1DD6\u0315\u0062"}, {"\u0061\u1DD6\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DD6\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DD7\u0062", "\u00E0\u05AE\u1DD7\u0315\u0062"}, {"\u0061\u1DD7\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DD7\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DD8\u0062", "\u00E0\u05AE\u1DD8\u0315\u0062"}, {"\u0061\u1DD8\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DD8\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DD9\u0062", "\u00E0\u05AE\u1DD9\u0315\u0062"}, {"\u0061\u1DD9\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DD9\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DDA\u0062", "\u00E0\u05AE\u1DDA\u0315\u0062"}, {"\u0061\u1DDA\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DDA\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DDB\u0062", "\u00E0\u05AE\u1DDB\u0315\u0062"}, {"\u0061\u1DDB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DDB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DDC\u0062", "\u00E0\u05AE\u1DDC\u0315\u0062"}, {"\u0061\u1DDC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DDC\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DDD\u0062", "\u00E0\u05AE\u1DDD\u0315\u0062"}, {"\u0061\u1DDD\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DDD\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DDE\u0062", "\u00E0\u05AE\u1DDE\u0315\u0062"}, {"\u0061\u1DDE\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DDE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DDF\u0062", "\u00E0\u05AE\u1DDF\u0315\u0062"}, {"\u0061\u1DDF\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DDF\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DE0\u0062", "\u00E0\u05AE\u1DE0\u0315\u0062"}, {"\u0061\u1DE0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DE0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DE1\u0062", "\u00E0\u05AE\u1DE1\u0315\u0062"}, {"\u0061\u1DE1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DE1\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DE2\u0062", "\u00E0\u05AE\u1DE2\u0315\u0062"}, {"\u0061\u1DE2\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DE2\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DE3\u0062", "\u00E0\u05AE\u1DE3\u0315\u0062"}, {"\u0061\u1DE3\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DE3\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DE4\u0062", "\u00E0\u05AE\u1DE4\u0315\u0062"}, {"\u0061\u1DE4\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DE4\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DE5\u0062", "\u00E0\u05AE\u1DE5\u0315\u0062"}, {"\u0061\u1DE5\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DE5\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DE6\u0062", "\u00E0\u05AE\u1DE6\u0315\u0062"}, {"\u0061\u1DE6\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DE6\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DE7\u0062", "\u00E0\u05AE\u1DE7\u0315\u0062"}, {"\u0061\u1DE7\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DE7\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DE8\u0062", "\u00E0\u05AE\u1DE8\u0315\u0062"}, {"\u0061\u1DE8\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DE8\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DE9\u0062", "\u00E0\u05AE\u1DE9\u0315\u0062"}, {"\u0061\u1DE9\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DE9\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DEA\u0062", "\u00E0\u05AE\u1DEA\u0315\u0062"}, {"\u0061\u1DEA\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DEA\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DEB\u0062", "\u00E0\u05AE\u1DEB\u0315\u0062"}, {"\u0061\u1DEB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DEB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DEC\u0062", "\u00E0\u05AE\u1DEC\u0315\u0062"}, {"\u0061\u1DEC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DEC\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DED\u0062", "\u00E0\u05AE\u1DED\u0315\u0062"}, {"\u0061\u1DED\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DED\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DEE\u0062", "\u00E0\u05AE\u1DEE\u0315\u0062"}, {"\u0061\u1DEE\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DEE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DEF\u0062", "\u00E0\u05AE\u1DEF\u0315\u0062"}, {"\u0061\u1DEF\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DEF\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DF0\u0062", "\u00E0\u05AE\u1DF0\u0315\u0062"}, {"\u0061\u1DF0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DF0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DF1\u0062", "\u00E0\u05AE\u1DF1\u0315\u0062"}, {"\u0061\u1DF1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DF1\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DF2\u0062", "\u00E0\u05AE\u1DF2\u0315\u0062"}, {"\u0061\u1DF2\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DF2\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DF3\u0062", "\u00E0\u05AE\u1DF3\u0315\u0062"}, {"\u0061\u1DF3\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DF3\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DF4\u0062", "\u00E0\u05AE\u1DF4\u0315\u0062"}, {"\u0061\u1DF4\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DF4\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DF5\u0062", "\u00E0\u05AE\u1DF5\u0315\u0062"}, {"\u0061\u1DF5\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DF5\u0300\u0315\u0062"}, {"\u0061\u035C\u0315\u0300\u1DF6\u0062", "\u00E0\u0315\u1DF6\u035C\u0062"}, {"\u0061\u1DF6\u035C\u0315\u0300\u0062", "\u00E0\u1DF6\u0315\u035C\u0062"}, {"\u0061\u0300\u05AE\U0001D16D\u1DF7\u0062", "\u00E0\U0001D16D\u05AE\u1DF7\u0062"}, {"\u0061\u1DF7\u0300\u05AE\U0001D16D\u0062", "\u00E0\U0001D16D\u1DF7\u05AE\u0062"}, {"\u0061\u0300\u05AE\U0001D16D\u1DF8\u0062", "\u00E0\U0001D16D\u05AE\u1DF8\u0062"}, {"\u0061\u1DF8\u0300\u05AE\U0001D16D\u0062", "\u00E0\U0001D16D\u1DF8\u05AE\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1DF9\u0062", "\u0061\u1DFA\u0316\u1DF9\u059A\u0062"}, {"\u0061\u1DF9\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1DF9\u0316\u059A\u0062"}, {"\u0061\u0316\u1DFA\u031B\u1DFA\u0062", "\u0061\u031B\u1DFA\u1DFA\u0316\u0062"}, {"\u0061\u1DFA\u0316\u1DFA\u031B\u0062", "\u0061\u031B\u1DFA\u1DFA\u0316\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DFB\u0062", "\u00E0\u05AE\u1DFB\u0315\u0062"}, {"\u0061\u1DFB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DFB\u0300\u0315\u0062"}, {"\u0061\u035D\u035C\u0315\u1DFC\u0062", "\u0061\u0315\u035C\u1DFC\u035D\u0062"}, {"\u0061\u1DFC\u035D\u035C\u0315\u0062", "\u0061\u0315\u1DFC\u035C\u035D\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1DFD\u0062", "\u0061\u1DFA\u0316\u1DFD\u059A\u0062"}, {"\u0061\u1DFD\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1DFD\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u1DFE\u0062", "\u00E0\u05AE\u1DFE\u0315\u0062"}, {"\u0061\u1DFE\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u1DFE\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u1DFF\u0062", "\u0061\u1DFA\u0316\u1DFF\u059A\u0062"}, {"\u0061\u1DFF\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u1DFF\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u20D0\u0062", "\u00E0\u05AE\u20D0\u0315\u0062"}, {"\u0061\u20D0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u20D0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u20D1\u0062", "\u00E0\u05AE\u20D1\u0315\u0062"}, {"\u0061\u20D1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u20D1\u0300\u0315\u0062"}, {"\u0061\U00016FF0\u0334\u20D2\u0062", "\u0061\u0334\u20D2\U00016FF0\u0062"}, {"\u0061\u20D2\U00016FF0\u0334\u0062", "\u0061\u20D2\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\u20D3\u0062", "\u0061\u0334\u20D3\U00016FF0\u0062"}, {"\u0061\u20D3\U00016FF0\u0334\u0062", "\u0061\u20D3\u0334\U00016FF0\u0062"}, {"\u0061\u0315\u0300\u05AE\u20D4\u0062", "\u00E0\u05AE\u20D4\u0315\u0062"}, {"\u0061\u20D4\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u20D4\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u20D5\u0062", "\u00E0\u05AE\u20D5\u0315\u0062"}, {"\u0061\u20D5\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u20D5\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u20D6\u0062", "\u00E0\u05AE\u20D6\u0315\u0062"}, {"\u0061\u20D6\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u20D6\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u20D7\u0062", "\u00E0\u05AE\u20D7\u0315\u0062"}, {"\u0061\u20D7\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u20D7\u0300\u0315\u0062"}, {"\u0061\U00016FF0\u0334\u20D8\u0062", "\u0061\u0334\u20D8\U00016FF0\u0062"}, {"\u0061\u20D8\U00016FF0\u0334\u0062", "\u0061\u20D8\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\u20D9\u0062", "\u0061\u0334\u20D9\U00016FF0\u0062"}, {"\u0061\u20D9\U00016FF0\u0334\u0062", "\u0061\u20D9\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\u20DA\u0062", "\u0061\u0334\u20DA\U00016FF0\u0062"}, {"\u0061\u20DA\U00016FF0\u0334\u0062", "\u0061\u20DA\u0334\U00016FF0\u0062"}, {"\u0061\u0315\u0300\u05AE\u20DB\u0062", "\u00E0\u05AE\u20DB\u0315\u0062"}, {"\u0061\u20DB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u20DB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u20DC\u0062", "\u00E0\u05AE\u20DC\u0315\u0062"}, {"\u0061\u20DC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u20DC\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u20E1\u0062", "\u00E0\u05AE\u20E1\u0315\u0062"}, {"\u0061\u20E1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u20E1\u0300\u0315\u0062"}, {"\u0061\U00016FF0\u0334\u20E5\u0062", "\u0061\u0334\u20E5\U00016FF0\u0062"}, {"\u0061\u20E5\U00016FF0\u0334\u0062", "\u0061\u20E5\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\u20E6\u0062", "\u0061\u0334\u20E6\U00016FF0\u0062"}, {"\u0061\u20E6\U00016FF0\u0334\u0062", "\u0061\u20E6\u0334\U00016FF0\u0062"}, {"\u0061\u0315\u0300\u05AE\u20E7\u0062", "\u00E0\u05AE\u20E7\u0315\u0062"}, {"\u0061\u20E7\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u20E7\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\u20E8\u0062", "\u0061\u1DFA\u0316\u20E8\u059A\u0062"}, {"\u0061\u20E8\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u20E8\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u20E9\u0062", "\u00E0\u05AE\u20E9\u0315\u0062"}, {"\u0061\u20E9\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u20E9\u0300\u0315\u0062"}, {"\u0061\U00016FF0\u0334\u20EA\u0062", "\u0061\u0334\u20EA\U00016FF0\u0062"}, {"\u0061\u20EA\U00016FF0\u0334\u0062", "\u0061\u20EA\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\u20EB\u0062", "\u0061\u0334\u20EB\U00016FF0\u0062"}, {"\u0061\u20EB\U00016FF0\u0334\u0062", "\u0061\u20EB\u0334\U00016FF0\u0062"}, {"\u0061\u059A\u0316\u1DFA\u20EC\u0062", "\u0061\u1DFA\u0316\u20EC\u059A\u0062"}, {"\u0061\u20EC\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u20EC\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u20ED\u0062", "\u0061\u1DFA\u0316\u20ED\u059A\u0062"}, {"\u0061\u20ED\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u20ED\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u20EE\u0062", "\u0061\u1DFA\u0316\u20EE\u059A\u0062"}, {"\u0061\u20EE\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u20EE\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\u20EF\u0062", "\u0061\u1DFA\u0316\u20EF\u059A\u0062"}, {"\u0061\u20EF\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\u20EF\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\u20F0\u0062", "\u00E0\u05AE\u20F0\u0315\u0062"}, {"\u0061\u20F0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u20F0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2CEF\u0062", "\u00E0\u05AE\u2CEF\u0315\u0062"}, {"\u0061\u2CEF\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2CEF\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2CF0\u0062", "\u00E0\u05AE\u2CF0\u0315\u0062"}, {"\u0061\u2CF0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2CF0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2CF1\u0062", "\u00E0\u05AE\u2CF1\u0315\u0062"}, {"\u0061\u2CF1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2CF1\u0300\u0315\u0062"}, {"\u0061\u05B0\u094D\u3099\u2D7F\u0062", "\u0061\u3099\u094D\u2D7F\u05B0\u0062"}, {"\u0061\u2D7F\u05B0\u094D\u3099\u0062", "\u0061\u3099\u2D7F\u094D\u05B0\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DE0\u0062", "\u00E0\u05AE\u2DE0\u0315\u0062"}, {"\u0061\u2DE0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DE0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DE1\u0062", "\u00E0\u05AE\u2DE1\u0315\u0062"}, {"\u0061\u2DE1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DE1\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DE2\u0062", "\u00E0\u05AE\u2DE2\u0315\u0062"}, {"\u0061\u2DE2\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DE2\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DE3\u0062", "\u00E0\u05AE\u2DE3\u0315\u0062"}, {"\u0061\u2DE3\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DE3\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DE4\u0062", "\u00E0\u05AE\u2DE4\u0315\u0062"}, {"\u0061\u2DE4\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DE4\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DE5\u0062", "\u00E0\u05AE\u2DE5\u0315\u0062"}, {"\u0061\u2DE5\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DE5\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DE6\u0062", "\u00E0\u05AE\u2DE6\u0315\u0062"}, {"\u0061\u2DE6\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DE6\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DE7\u0062", "\u00E0\u05AE\u2DE7\u0315\u0062"}, {"\u0061\u2DE7\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DE7\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DE8\u0062", "\u00E0\u05AE\u2DE8\u0315\u0062"}, {"\u0061\u2DE8\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DE8\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DE9\u0062", "\u00E0\u05AE\u2DE9\u0315\u0062"}, {"\u0061\u2DE9\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DE9\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DEA\u0062", "\u00E0\u05AE\u2DEA\u0315\u0062"}, {"\u0061\u2DEA\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DEA\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DEB\u0062", "\u00E0\u05AE\u2DEB\u0315\u0062"}, {"\u0061\u2DEB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DEB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DEC\u0062", "\u00E0\u05AE\u2DEC\u0315\u0062"}, {"\u0061\u2DEC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DEC\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DED\u0062", "\u00E0\u05AE\u2DED\u0315\u0062"}, {"\u0061\u2DED\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DED\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DEE\u0062", "\u00E0\u05AE\u2DEE\u0315\u0062"}, {"\u0061\u2DEE\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DEE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DEF\u0062", "\u00E0\u05AE\u2DEF\u0315\u0062"}, {"\u0061\u2DEF\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DEF\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DF0\u0062", "\u00E0\u05AE\u2DF0\u0315\u0062"}, {"\u0061\u2DF0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DF0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DF1\u0062", "\u00E0\u05AE\u2DF1\u0315\u0062"}, {"\u0061\u2DF1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DF1\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DF2\u0062", "\u00E0\u05AE\u2DF2\u0315\u0062"}, {"\u0061\u2DF2\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DF2\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DF3\u0062", "\u00E0\u05AE\u2DF3\u0315\u0062"}, {"\u0061\u2DF3\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DF3\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DF4\u0062", "\u00E0\u05AE\u2DF4\u0315\u0062"}, {"\u0061\u2DF4\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DF4\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DF5\u0062", "\u00E0\u05AE\u2DF5\u0315\u0062"}, {"\u0061\u2DF5\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DF5\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DF6\u0062", "\u00E0\u05AE\u2DF6\u0315\u0062"}, {"\u0061\u2DF6\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DF6\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DF7\u0062", "\u00E0\u05AE\u2DF7\u0315\u0062"}, {"\u0061\u2DF7\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DF7\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DF8\u0062", "\u00E0\u05AE\u2DF8\u0315\u0062"}, {"\u0061\u2DF8\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DF8\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DF9\u0062", "\u00E0\u05AE\u2DF9\u0315\u0062"}, {"\u0061\u2DF9\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DF9\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DFA\u0062", "\u00E0\u05AE\u2DFA\u0315\u0062"}, {"\u0061\u2DFA\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DFA\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DFB\u0062", "\u00E0\u05AE\u2DFB\u0315\u0062"}, {"\u0061\u2DFB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DFB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DFC\u0062", "\u00E0\u05AE\u2DFC\u0315\u0062"}, {"\u0061\u2DFC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DFC\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DFD\u0062", "\u00E0\u05AE\u2DFD\u0315\u0062"}, {"\u0061\u2DFD\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DFD\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DFE\u0062", "\u00E0\u05AE\u2DFE\u0315\u0062"}, {"\u0061\u2DFE\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DFE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\u2DFF\u0062", "\u00E0\u05AE\u2DFF\u0315\u0062"}, {"\u0061\u2DFF\u0315\u0300\u05AE\u0062", "\u0061\u05AE\u2DFF\u0300\u0315\u0062"}, {"\u0061\u0316\u1DFA\u031B\u302A\u0062", "\u0061\u031B\u1DFA\u302A\u0316\u0062"}, {"\u0061\u302A\u0316\u1DFA\u031B\u0062", "\u0061\u031B\u302A\u1DFA\u0316\u0062"}, {"\u0061\u0300\u05AE\U0001D16D\u302B\u0062", "\u00E0\U0001D16D\u05AE\u302B\u0062"}, {"\u0061\u302B\u0300\u05AE\U0001D16D\u0062", "\u00E0\U0001D16D\u302B\u05AE\u0062"}, {"\u0061\u035C\u0315\u0300\u302C\u0062", "\u00E0\u0315\u302C\u035C\u0062"}, {"\u0061\u302C\u035C\u0315\u0300\u0062", "\u00E0\u302C\u0315\u035C\u0062"}, {"\u0061\u302E\u059A\u0316\u302D\u0062", "\u0061\u0316\u059A\u302D\u302E\u0062"}, {"\u0061\u302D\u302E\u059A\u0316\u0062", "\u0061\u0316\u302D\u059A\u302E\u0062"}, {"\u0061\U0001D16D\u302E\u059A\u302E\u0062", "\u0061\u059A\u302E\u302E\U0001D16D\u0062"}, {"\u0061\u302E\U0001D16D\u302E\u059A\u0062", "\u0061\u059A\u302E\u302E\U0001D16D\u0062"}, {"\u0061\U0001D16D\u302E\u059A\u302F\u0062", "\u0061\u059A\u302E\u302F\U0001D16D\u0062"}, {"\u0061\u302F\U0001D16D\u302E\u059A\u0062", "\u0061\u059A\u302F\u302E\U0001D16D\u0062"}, {"\u0061\u094D\u3099\u093C\u3099\u0062", "\u0061\u093C\u3099\u3099\u094D\u0062"}, {"\u0061\u3099\u094D\u3099\u093C\u0062", "\u0061\u093C\u3099\u3099\u094D\u0062"}, {"\u0061\u094D\u3099\u093C\u309A\u0062", "\u0061\u093C\u3099\u309A\u094D\u0062"}, {"\u0061\u309A\u094D\u3099\u093C\u0062", "\u0061\u093C\u309A\u3099\u094D\u0062"}, {"\u0061\u0315\u0300\u05AE\uA66F\u0062", "\u00E0\u05AE\uA66F\u0315\u0062"}, {"\u0061\uA66F\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA66F\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA674\u0062", "\u00E0\u05AE\uA674\u0315\u0062"}, {"\u0061\uA674\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA674\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA675\u0062", "\u00E0\u05AE\uA675\u0315\u0062"}, {"\u0061\uA675\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA675\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA676\u0062", "\u00E0\u05AE\uA676\u0315\u0062"}, {"\u0061\uA676\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA676\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA677\u0062", "\u00E0\u05AE\uA677\u0315\u0062"}, {"\u0061\uA677\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA677\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA678\u0062", "\u00E0\u05AE\uA678\u0315\u0062"}, {"\u0061\uA678\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA678\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA679\u0062", "\u00E0\u05AE\uA679\u0315\u0062"}, {"\u0061\uA679\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA679\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA67A\u0062", "\u00E0\u05AE\uA67A\u0315\u0062"}, {"\u0061\uA67A\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA67A\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA67B\u0062", "\u00E0\u05AE\uA67B\u0315\u0062"}, {"\u0061\uA67B\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA67B\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA67C\u0062", "\u00E0\u05AE\uA67C\u0315\u0062"}, {"\u0061\uA67C\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA67C\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA67D\u0062", "\u00E0\u05AE\uA67D\u0315\u0062"}, {"\u0061\uA67D\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA67D\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA69E\u0062", "\u00E0\u05AE\uA69E\u0315\u0062"}, {"\u0061\uA69E\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA69E\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA69F\u0062", "\u00E0\u05AE\uA69F\u0315\u0062"}, {"\u0061\uA69F\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA69F\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA6F0\u0062", "\u00E0\u05AE\uA6F0\u0315\u0062"}, {"\u0061\uA6F0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA6F0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA6F1\u0062", "\u00E0\u05AE\uA6F1\u0315\u0062"}, {"\u0061\uA6F1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA6F1\u0300\u0315\u0062"}, {"\u0061\u05B0\u094D\u3099\uA806\u0062", "\u0061\u3099\u094D\uA806\u05B0\u0062"}, {"\u0061\uA806\u05B0\u094D\u3099\u0062", "\u0061\u3099\uA806\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\uA82C\u0062", "\u0061\u3099\u094D\uA82C\u05B0\u0062"}, {"\u0061\uA82C\u05B0\u094D\u3099\u0062", "\u0061\u3099\uA82C\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\uA8C4\u0062", "\u0061\u3099\u094D\uA8C4\u05B0\u0062"}, {"\u0061\uA8C4\u05B0\u094D\u3099\u0062", "\u0061\u3099\uA8C4\u094D\u05B0\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8E0\u0062", "\u00E0\u05AE\uA8E0\u0315\u0062"}, {"\u0061\uA8E0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8E0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8E1\u0062", "\u00E0\u05AE\uA8E1\u0315\u0062"}, {"\u0061\uA8E1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8E1\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8E2\u0062", "\u00E0\u05AE\uA8E2\u0315\u0062"}, {"\u0061\uA8E2\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8E2\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8E3\u0062", "\u00E0\u05AE\uA8E3\u0315\u0062"}, {"\u0061\uA8E3\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8E3\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8E4\u0062", "\u00E0\u05AE\uA8E4\u0315\u0062"}, {"\u0061\uA8E4\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8E4\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8E5\u0062", "\u00E0\u05AE\uA8E5\u0315\u0062"}, {"\u0061\uA8E5\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8E5\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8E6\u0062", "\u00E0\u05AE\uA8E6\u0315\u0062"}, {"\u0061\uA8E6\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8E6\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8E7\u0062", "\u00E0\u05AE\uA8E7\u0315\u0062"}, {"\u0061\uA8E7\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8E7\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8E8\u0062", "\u00E0\u05AE\uA8E8\u0315\u0062"}, {"\u0061\uA8E8\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8E8\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8E9\u0062", "\u00E0\u05AE\uA8E9\u0315\u0062"}, {"\u0061\uA8E9\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8E9\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8EA\u0062", "\u00E0\u05AE\uA8EA\u0315\u0062"}, {"\u0061\uA8EA\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8EA\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8EB\u0062", "\u00E0\u05AE\uA8EB\u0315\u0062"}, {"\u0061\uA8EB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8EB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8EC\u0062", "\u00E0\u05AE\uA8EC\u0315\u0062"}, {"\u0061\uA8EC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8EC\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8ED\u0062", "\u00E0\u05AE\uA8ED\u0315\u0062"}, {"\u0061\uA8ED\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8ED\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8EE\u0062", "\u00E0\u05AE\uA8EE\u0315\u0062"}, {"\u0061\uA8EE\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8EE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8EF\u0062", "\u00E0\u05AE\uA8EF\u0315\u0062"}, {"\u0061\uA8EF\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8EF\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8F0\u0062", "\u00E0\u05AE\uA8F0\u0315\u0062"}, {"\u0061\uA8F0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8F0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uA8F1\u0062", "\u00E0\u05AE\uA8F1\u0315\u0062"}, {"\u0061\uA8F1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uA8F1\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\uA92B\u0062", "\u0061\u1DFA\u0316\uA92B\u059A\u0062"}, {"\u0061\uA92B\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\uA92B\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\uA92C\u0062", "\u0061\u1DFA\u0316\uA92C\u059A\u0062"}, {"\u0061\uA92C\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\uA92C\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\uA92D\u0062", "\u0061\u1DFA\u0316\uA92D\u059A\u0062"}, {"\u0061\uA92D\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\uA92D\u0316\u059A\u0062"}, {"\u0061\u05B0\u094D\u3099\uA953\u0062", "\u0061\u3099\u094D\uA953\u05B0\u0062"}, {"\u0061\uA953\u05B0\u094D\u3099\u0062", "\u0061\u3099\uA953\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\uA9B3\u0062", "\u0061\U00016FF0\u093C\uA9B3\u3099\u0062"}, {"\u0061\uA9B3\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\uA9B3\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\uA9C0\u0062", "\u0061\u3099\u094D\uA9C0\u05B0\u0062"}, {"\u0061\uA9C0\u05B0\u094D\u3099\u0062", "\u0061\u3099\uA9C0\u094D\u05B0\u0062"}, {"\u0061\u0315\u0300\u05AE\uAAB0\u0062", "\u00E0\u05AE\uAAB0\u0315\u0062"}, {"\u0061\uAAB0\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uAAB0\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uAAB2\u0062", "\u00E0\u05AE\uAAB2\u0315\u0062"}, {"\u0061\uAAB2\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uAAB2\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uAAB3\u0062", "\u00E0\u05AE\uAAB3\u0315\u0062"}, {"\u0061\uAAB3\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uAAB3\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\uAAB4\u0062", "\u0061\u1DFA\u0316\uAAB4\u059A\u0062"}, {"\u0061\uAAB4\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\uAAB4\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\uAAB7\u0062", "\u00E0\u05AE\uAAB7\u0315\u0062"}, {"\u0061\uAAB7\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uAAB7\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uAAB8\u0062", "\u00E0\u05AE\uAAB8\u0315\u0062"}, {"\u0061\uAAB8\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uAAB8\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uAABE\u0062", "\u00E0\u05AE\uAABE\u0315\u0062"}, {"\u0061\uAABE\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uAABE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uAABF\u0062", "\u00E0\u05AE\uAABF\u0315\u0062"}, {"\u0061\uAABF\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uAABF\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uAAC1\u0062", "\u00E0\u05AE\uAAC1\u0315\u0062"}, {"\u0061\uAAC1\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uAAC1\u0300\u0315\u0062"}, {"\u0061\u05B0\u094D\u3099\uAAF6\u0062", "\u0061\u3099\u094D\uAAF6\u05B0\u0062"}, {"\u0061\uAAF6\u05B0\u094D\u3099\u0062", "\u0061\u3099\uAAF6\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\uABED\u0062", "\u0061\u3099\u094D\uABED\u05B0\u0062"}, {"\u0061\uABED\u05B0\u094D\u3099\u0062", "\u0061\u3099\uABED\u094D\u05B0\u0062"}, {"\u0061\u064B\uFB1E\u05C2\uFB1E\u0062", "\u0061\u05C2\uFB1E\uFB1E\u064B\u0062"}, {"\u0061\uFB1E\u064B\uFB1E\u05C2\u0062", "\u0061\u05C2\uFB1E\uFB1E\u064B\u0062"}, {"\u0061\u0315\u0300\u05AE\uFE20\u0062", "\u00E0\u05AE\uFE20\u0315\u0062"}, {"\u0061\uFE20\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uFE20\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uFE21\u0062", "\u00E0\u05AE\uFE21\u0315\u0062"}, {"\u0061\uFE21\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uFE21\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uFE22\u0062", "\u00E0\u05AE\uFE22\u0315\u0062"}, {"\u0061\uFE22\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uFE22\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uFE23\u0062", "\u00E0\u05AE\uFE23\u0315\u0062"}, {"\u0061\uFE23\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uFE23\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uFE24\u0062", "\u00E0\u05AE\uFE24\u0315\u0062"}, {"\u0061\uFE24\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uFE24\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uFE25\u0062", "\u00E0\u05AE\uFE25\u0315\u0062"}, {"\u0061\uFE25\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uFE25\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uFE26\u0062", "\u00E0\u05AE\uFE26\u0315\u0062"}, {"\u0061\uFE26\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uFE26\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\uFE27\u0062", "\u0061\u1DFA\u0316\uFE27\u059A\u0062"}, {"\u0061\uFE27\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\uFE27\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\uFE28\u0062", "\u0061\u1DFA\u0316\uFE28\u059A\u0062"}, {"\u0061\uFE28\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\uFE28\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\uFE29\u0062", "\u0061\u1DFA\u0316\uFE29\u059A\u0062"}, {"\u0061\uFE29\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\uFE29\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\uFE2A\u0062", "\u0061\u1DFA\u0316\uFE2A\u059A\u0062"}, {"\u0061\uFE2A\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\uFE2A\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\uFE2B\u0062", "\u0061\u1DFA\u0316\uFE2B\u059A\u0062"}, {"\u0061\uFE2B\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\uFE2B\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\uFE2C\u0062", "\u0061\u1DFA\u0316\uFE2C\u059A\u0062"}, {"\u0061\uFE2C\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\uFE2C\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\uFE2D\u0062", "\u0061\u1DFA\u0316\uFE2D\u059A\u0062"}, {"\u0061\uFE2D\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\uFE2D\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\uFE2E\u0062", "\u00E0\u05AE\uFE2E\u0315\u0062"}, {"\u0061\uFE2E\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uFE2E\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\uFE2F\u0062", "\u00E0\u05AE\uFE2F\u0315\u0062"}, {"\u0061\uFE2F\u0315\u0300\u05AE\u0062", "\u0061\u05AE\uFE2F\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\U000101FD\u0062", "\u0061\u1DFA\u0316\U000101FD\u059A\u0062"}, {"\u0061\U000101FD\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U000101FD\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U000102E0\u0062", "\u0061\u1DFA\u0316\U000102E0\u059A\u0062"}, {"\u0061\U000102E0\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U000102E0\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010376\u0062", "\u00E0\u05AE\U00010376\u0315\u0062"}, {"\u0061\U00010376\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010376\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010377\u0062", "\u00E0\u05AE\U00010377\u0315\u0062"}, {"\u0061\U00010377\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010377\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010378\u0062", "\u00E0\u05AE\U00010378\u0315\u0062"}, {"\u0061\U00010378\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010378\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010379\u0062", "\u00E0\u05AE\U00010379\u0315\u0062"}, {"\u0061\U00010379\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010379\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001037A\u0062", "\u00E0\u05AE\U0001037A\u0315\u0062"}, {"\u0061\U0001037A\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001037A\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\U00010A0D\u0062", "\u0061\u1DFA\u0316\U00010A0D\u059A\u0062"}, {"\u0061\U00010A0D\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U00010A0D\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010A0F\u0062", "\u00E0\u05AE\U00010A0F\u0315\u0062"}, {"\u0061\U00010A0F\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010A0F\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010A38\u0062", "\u00E0\u05AE\U00010A38\u0315\u0062"}, {"\u0061\U00010A38\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010A38\u0300\u0315\u0062"}, {"\u0061\U00016FF0\u0334\U00010A39\u0062", "\u0061\u0334\U00010A39\U00016FF0\u0062"}, {"\u0061\U00010A39\U00016FF0\u0334\u0062", "\u0061\U00010A39\u0334\U00016FF0\u0062"}, {"\u0061\u059A\u0316\u1DFA\U00010A3A\u0062", "\u0061\u1DFA\u0316\U00010A3A\u059A\u0062"}, {"\u0061\U00010A3A\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U00010A3A\u0316\u059A\u0062"}, {"\u0061\u05B0\u094D\u3099\U00010A3F\u0062", "\u0061\u3099\u094D\U00010A3F\u05B0\u0062"}, {"\u0061\U00010A3F\u05B0\u094D\u3099\u0062", "\u0061\u3099\U00010A3F\u094D\u05B0\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010AE5\u0062", "\u00E0\u05AE\U00010AE5\u0315\u0062"}, {"\u0061\U00010AE5\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010AE5\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\U00010AE6\u0062", "\u0061\u1DFA\u0316\U00010AE6\u059A\u0062"}, {"\u0061\U00010AE6\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U00010AE6\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010D24\u0062", "\u00E0\u05AE\U00010D24\u0315\u0062"}, {"\u0061\U00010D24\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010D24\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010D25\u0062", "\u00E0\u05AE\U00010D25\u0315\u0062"}, {"\u0061\U00010D25\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010D25\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010D26\u0062", "\u00E0\u05AE\U00010D26\u0315\u0062"}, {"\u0061\U00010D26\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010D26\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010D27\u0062", "\u00E0\u05AE\U00010D27\u0315\u0062"}, {"\u0061\U00010D27\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010D27\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010EAB\u0062", "\u00E0\u05AE\U00010EAB\u0315\u0062"}, {"\u0061\U00010EAB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010EAB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010EAC\u0062", "\u00E0\u05AE\U00010EAC\u0315\u0062"}, {"\u0061\U00010EAC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010EAC\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\U00010EFD\u0062", "\u0061\u1DFA\u0316\U00010EFD\u059A\u0062"}, {"\u0061\U00010EFD\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U00010EFD\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U00010EFE\u0062", "\u0061\u1DFA\u0316\U00010EFE\u059A\u0062"}, {"\u0061\U00010EFE\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U00010EFE\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U00010EFF\u0062", "\u0061\u1DFA\u0316\U00010EFF\u059A\u0062"}, {"\u0061\U00010EFF\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U00010EFF\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U00010F46\u0062", "\u0061\u1DFA\u0316\U00010F46\u059A\u0062"}, {"\u0061\U00010F46\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U00010F46\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U00010F47\u0062", "\u0061\u1DFA\u0316\U00010F47\u059A\u0062"}, {"\u0061\U00010F47\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U00010F47\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010F48\u0062", "\u00E0\u05AE\U00010F48\u0315\u0062"}, {"\u0061\U00010F48\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010F48\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010F49\u0062", "\u00E0\u05AE\U00010F49\u0315\u0062"}, {"\u0061\U00010F49\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010F49\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010F4A\u0062", "\u00E0\u05AE\U00010F4A\u0315\u0062"}, {"\u0061\U00010F4A\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010F4A\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\U00010F4B\u0062", "\u0061\u1DFA\u0316\U00010F4B\u059A\u0062"}, {"\u0061\U00010F4B\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U00010F4B\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010F4C\u0062", "\u00E0\u05AE\U00010F4C\u0315\u0062"}, {"\u0061\U00010F4C\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010F4C\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\U00010F4D\u0062", "\u0061\u1DFA\u0316\U00010F4D\u059A\u0062"}, {"\u0061\U00010F4D\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U00010F4D\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U00010F4E\u0062", "\u0061\u1DFA\u0316\U00010F4E\u059A\u0062"}, {"\u0061\U00010F4E\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U00010F4E\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U00010F4F\u0062", "\u0061\u1DFA\u0316\U00010F4F\u059A\u0062"}, {"\u0061\U00010F4F\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U00010F4F\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U00010F50\u0062", "\u0061\u1DFA\u0316\U00010F50\u059A\u0062"}, {"\u0061\U00010F50\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U00010F50\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010F82\u0062", "\u00E0\u05AE\U00010F82\u0315\u0062"}, {"\u0061\U00010F82\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010F82\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\U00010F83\u0062", "\u0061\u1DFA\u0316\U00010F83\u059A\u0062"}, {"\u0061\U00010F83\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U00010F83\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\U00010F84\u0062", "\u00E0\u05AE\U00010F84\u0315\u0062"}, {"\u0061\U00010F84\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00010F84\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\U00010F85\u0062", "\u0061\u1DFA\u0316\U00010F85\u059A\u0062"}, {"\u0061\U00010F85\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U00010F85\u0316\u059A\u0062"}, {"\u0061\u05B0\u094D\u3099\U00011046\u0062", "\u0061\u3099\u094D\U00011046\u05B0\u0062"}, {"\u0061\U00011046\u05B0\u094D\u3099\u0062", "\u0061\u3099\U00011046\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\U00011070\u0062", "\u0061\u3099\u094D\U00011070\u05B0\u0062"}, {"\u0061\U00011070\u05B0\u094D\u3099\u0062", "\u0061\u3099\U00011070\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\U0001107F\u0062", "\u0061\u3099\u094D\U0001107F\u05B0\u0062"}, {"\u0061\U0001107F\u05B0\u094D\u3099\u0062", "\u0061\u3099\U0001107F\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\U000110B9\u0062", "\u0061\u3099\u094D\U000110B9\u05B0\u0062"}, {"\u0061\U000110B9\u05B0\u094D\u3099\u0062", "\u0061\u3099\U000110B9\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\U000110BA\u0062", "\u0061\U00016FF0\u093C\U000110BA\u3099\u0062"}, {"\u0061\U000110BA\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\U000110BA\u093C\u3099\u0062"}, {"\u0061\u0315\u0300\u05AE\U00011100\u0062", "\u00E0\u05AE\U00011100\u0315\u0062"}, {"\u0061\U00011100\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00011100\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00011101\u0062", "\u00E0\u05AE\U00011101\u0315\u0062"}, {"\u0061\U00011101\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00011101\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00011102\u0062", "\u00E0\u05AE\U00011102\u0315\u0062"}, {"\u0061\U00011102\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00011102\u0300\u0315\u0062"}, {"\u0061\u05B0\u094D\u3099\U00011133\u0062", "\u0061\u3099\u094D\U00011133\u05B0\u0062"}, {"\u0061\U00011133\u05B0\u094D\u3099\u0062", "\u0061\u3099\U00011133\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\U00011134\u0062", "\u0061\u3099\u094D\U00011134\u05B0\u0062"}, {"\u0061\U00011134\u05B0\u094D\u3099\u0062", "\u0061\u3099\U00011134\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\U00011173\u0062", "\u0061\U00016FF0\u093C\U00011173\u3099\u0062"}, {"\u0061\U00011173\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\U00011173\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\U000111C0\u0062", "\u0061\u3099\u094D\U000111C0\u05B0\u0062"}, {"\u0061\U000111C0\u05B0\u094D\u3099\u0062", "\u0061\u3099\U000111C0\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\U000111CA\u0062", "\u0061\U00016FF0\u093C\U000111CA\u3099\u0062"}, {"\u0061\U000111CA\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\U000111CA\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\U00011235\u0062", "\u0061\u3099\u094D\U00011235\u05B0\u0062"}, {"\u0061\U00011235\u05B0\u094D\u3099\u0062", "\u0061\u3099\U00011235\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\U00011236\u0062", "\u0061\U00016FF0\u093C\U00011236\u3099\u0062"}, {"\u0061\U00011236\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\U00011236\u093C\u3099\u0062"}, {"\u0061\u3099\u093C\U00016FF0\U000112E9\u0062", "\u0061\U00016FF0\u093C\U000112E9\u3099\u0062"}, {"\u0061\U000112E9\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\U000112E9\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\U000112EA\u0062", "\u0061\u3099\u094D\U000112EA\u05B0\u0062"}, {"\u0061\U000112EA\u05B0\u094D\u3099\u0062", "\u0061\u3099\U000112EA\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\U0001133B\u0062", "\u0061\U00016FF0\u093C\U0001133B\u3099\u0062"}, {"\u0061\U0001133B\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\U0001133B\u093C\u3099\u0062"}, {"\u0061\u3099\u093C\U00016FF0\U0001133C\u0062", "\u0061\U00016FF0\u093C\U0001133C\u3099\u0062"}, {"\u0061\U0001133C\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\U0001133C\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\U0001134D\u0062", "\u0061\u3099\u094D\U0001134D\u05B0\u0062"}, {"\u0061\U0001134D\u05B0\u094D\u3099\u0062", "\u0061\u3099\U0001134D\u094D\u05B0\u0062"}, {"\u0061\u0315\u0300\u05AE\U00011366\u0062", "\u00E0\u05AE\U00011366\u0315\u0062"}, {"\u0061\U00011366\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00011366\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00011367\u0062", "\u00E0\u05AE\U00011367\u0315\u0062"}, {"\u0061\U00011367\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00011367\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00011368\u0062", "\u00E0\u05AE\U00011368\u0315\u0062"}, {"\u0061\U00011368\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00011368\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00011369\u0062", "\u00E0\u05AE\U00011369\u0315\u0062"}, {"\u0061\U00011369\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00011369\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001136A\u0062", "\u00E0\u05AE\U0001136A\u0315\u0062"}, {"\u0061\U0001136A\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001136A\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001136B\u0062", "\u00E0\u05AE\U0001136B\u0315\u0062"}, {"\u0061\U0001136B\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001136B\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001136C\u0062", "\u00E0\u05AE\U0001136C\u0315\u0062"}, {"\u0061\U0001136C\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001136C\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00011370\u0062", "\u00E0\u05AE\U00011370\u0315\u0062"}, {"\u0061\U00011370\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00011370\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00011371\u0062", "\u00E0\u05AE\U00011371\u0315\u0062"}, {"\u0061\U00011371\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00011371\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00011372\u0062", "\u00E0\u05AE\U00011372\u0315\u0062"}, {"\u0061\U00011372\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00011372\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00011373\u0062", "\u00E0\u05AE\U00011373\u0315\u0062"}, {"\u0061\U00011373\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00011373\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00011374\u0062", "\u00E0\u05AE\U00011374\u0315\u0062"}, {"\u0061\U00011374\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00011374\u0300\u0315\u0062"}, {"\u0061\u05B0\u094D\u3099\U00011442\u0062", "\u0061\u3099\u094D\U00011442\u05B0\u0062"}, {"\u0061\U00011442\u05B0\u094D\u3099\u0062", "\u0061\u3099\U00011442\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\U00011446\u0062", "\u0061\U00016FF0\u093C\U00011446\u3099\u0062"}, {"\u0061\U00011446\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\U00011446\u093C\u3099\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001145E\u0062", "\u00E0\u05AE\U0001145E\u0315\u0062"}, {"\u0061\U0001145E\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001145E\u0300\u0315\u0062"}, {"\u0061\u05B0\u094D\u3099\U000114C2\u0062", "\u0061\u3099\u094D\U000114C2\u05B0\u0062"}, {"\u0061\U000114C2\u05B0\u094D\u3099\u0062", "\u0061\u3099\U000114C2\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\U000114C3\u0062", "\u0061\U00016FF0\u093C\U000114C3\u3099\u0062"}, {"\u0061\U000114C3\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\U000114C3\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\U000115BF\u0062", "\u0061\u3099\u094D\U000115BF\u05B0\u0062"}, {"\u0061\U000115BF\u05B0\u094D\u3099\u0062", "\u0061\u3099\U000115BF\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\U000115C0\u0062", "\u0061\U00016FF0\u093C\U000115C0\u3099\u0062"}, {"\u0061\U000115C0\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\U000115C0\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\U0001163F\u0062", "\u0061\u3099\u094D\U0001163F\u05B0\u0062"}, {"\u0061\U0001163F\u05B0\u094D\u3099\u0062", "\u0061\u3099\U0001163F\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\U000116B6\u0062", "\u0061\u3099\u094D\U000116B6\u05B0\u0062"}, {"\u0061\U000116B6\u05B0\u094D\u3099\u0062", "\u0061\u3099\U000116B6\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\U000116B7\u0062", "\u0061\U00016FF0\u093C\U000116B7\u3099\u0062"}, {"\u0061\U000116B7\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\U000116B7\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\U0001172B\u0062", "\u0061\u3099\u094D\U0001172B\u05B0\u0062"}, {"\u0061\U0001172B\u05B0\u094D\u3099\u0062", "\u0061\u3099\U0001172B\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\U00011839\u0062", "\u0061\u3099\u094D\U00011839\u05B0\u0062"}, {"\u0061\U00011839\u05B0\u094D\u3099\u0062", "\u0061\u3099\U00011839\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\U0001183A\u0062", "\u0061\U00016FF0\u093C\U0001183A\u3099\u0062"}, {"\u0061\U0001183A\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\U0001183A\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\U0001193D\u0062", "\u0061\u3099\u094D\U0001193D\u05B0\u0062"}, {"\u0061\U0001193D\u05B0\u094D\u3099\u0062", "\u0061\u3099\U0001193D\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\U0001193E\u0062", "\u0061\u3099\u094D\U0001193E\u05B0\u0062"}, {"\u0061\U0001193E\u05B0\u094D\u3099\u0062", "\u0061\u3099\U0001193E\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\U00011943\u0062", "\u0061\U00016FF0\u093C\U00011943\u3099\u0062"}, {"\u0061\U00011943\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\U00011943\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\U000119E0\u0062", "\u0061\u3099\u094D\U000119E0\u05B0\u0062"}, {"\u0061\U000119E0\u05B0\u094D\u3099\u0062", "\u0061\u3099\U000119E0\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\U00011A34\u0062", "\u0061\u3099\u094D\U00011A34\u05B0\u0062"}, {"\u0061\U00011A34\u05B0\u094D\u3099\u0062", "\u0061\u3099\U00011A34\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\U00011A47\u0062", "\u0061\u3099\u094D\U00011A47\u05B0\u0062"}, {"\u0061\U00011A47\u05B0\u094D\u3099\u0062", "\u0061\u3099\U00011A47\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\U00011A99\u0062", "\u0061\u3099\u094D\U00011A99\u05B0\u0062"}, {"\u0061\U00011A99\u05B0\u094D\u3099\u0062", "\u0061\u3099\U00011A99\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\U00011C3F\u0062", "\u0061\u3099\u094D\U00011C3F\u05B0\u0062"}, {"\u0061\U00011C3F\u05B0\u094D\u3099\u0062", "\u0061\u3099\U00011C3F\u094D\u05B0\u0062"}, {"\u0061\u3099\u093C\U00016FF0\U00011D42\u0062", "\u0061\U00016FF0\u093C\U00011D42\u3099\u0062"}, {"\u0061\U00011D42\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\U00011D42\u093C\u3099\u0062"}, {"\u0061\u05B0\u094D\u3099\U00011D44\u0062", "\u0061\u3099\u094D\U00011D44\u05B0\u0062"}, {"\u0061\U00011D44\u05B0\u094D\u3099\u0062", "\u0061\u3099\U00011D44\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\U00011D45\u0062", "\u0061\u3099\u094D\U00011D45\u05B0\u0062"}, {"\u0061\U00011D45\u05B0\u094D\u3099\u0062", "\u0061\u3099\U00011D45\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\U00011D97\u0062", "\u0061\u3099\u094D\U00011D97\u05B0\u0062"}, {"\u0061\U00011D97\u05B0\u094D\u3099\u0062", "\u0061\u3099\U00011D97\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\U00011F41\u0062", "\u0061\u3099\u094D\U00011F41\u05B0\u0062"}, {"\u0061\U00011F41\u05B0\u094D\u3099\u0062", "\u0061\u3099\U00011F41\u094D\u05B0\u0062"}, {"\u0061\u05B0\u094D\u3099\U00011F42\u0062", "\u0061\u3099\u094D\U00011F42\u05B0\u0062"}, {"\u0061\U00011F42\u05B0\u094D\u3099\u0062", "\u0061\u3099\U00011F42\u094D\u05B0\u0062"}, {"\u0061\U00016FF0\u0334\U00016AF0\u0062", "\u0061\u0334\U00016AF0\U00016FF0\u0062"}, {"\u0061\U00016AF0\U00016FF0\u0334\u0062", "\u0061\U00016AF0\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\U00016AF1\u0062", "\u0061\u0334\U00016AF1\U00016FF0\u0062"}, {"\u0061\U00016AF1\U00016FF0\u0334\u0062", "\u0061\U00016AF1\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\U00016AF2\u0062", "\u0061\u0334\U00016AF2\U00016FF0\u0062"}, {"\u0061\U00016AF2\U00016FF0\u0334\u0062", "\u0061\U00016AF2\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\U00016AF3\u0062", "\u0061\u0334\U00016AF3\U00016FF0\u0062"}, {"\u0061\U00016AF3\U00016FF0\u0334\u0062", "\u0061\U00016AF3\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\U00016AF4\u0062", "\u0061\u0334\U00016AF4\U00016FF0\u0062"}, {"\u0061\U00016AF4\U00016FF0\u0334\u0062", "\u0061\U00016AF4\u0334\U00016FF0\u0062"}, {"\u0061\u0315\u0300\u05AE\U00016B30\u0062", "\u00E0\u05AE\U00016B30\u0315\u0062"}, {"\u0061\U00016B30\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00016B30\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00016B31\u0062", "\u00E0\u05AE\U00016B31\u0315\u0062"}, {"\u0061\U00016B31\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00016B31\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00016B32\u0062", "\u00E0\u05AE\U00016B32\u0315\u0062"}, {"\u0061\U00016B32\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00016B32\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00016B33\u0062", "\u00E0\u05AE\U00016B33\u0315\u0062"}, {"\u0061\U00016B33\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00016B33\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00016B34\u0062", "\u00E0\u05AE\U00016B34\u0315\u0062"}, {"\u0061\U00016B34\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00016B34\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00016B35\u0062", "\u00E0\u05AE\U00016B35\u0315\u0062"}, {"\u0061\U00016B35\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00016B35\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U00016B36\u0062", "\u00E0\u05AE\U00016B36\u0315\u0062"}, {"\u0061\U00016B36\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U00016B36\u0300\u0315\u0062"}, {"\u0061\u093C\U00016FF0\u0334\U00016FF0\u0062", "\u0061\u0334\U00016FF0\U00016FF0\u093C\u0062"}, {"\u0061\U00016FF0\u093C\U00016FF0\u0334\u0062", "\u0061\u0334\U00016FF0\U00016FF0\u093C\u0062"}, {"\u0061\u093C\U00016FF0\u0334\U00016FF1\u0062", "\u0061\u0334\U00016FF0\U00016FF1\u093C\u0062"}, {"\u0061\U00016FF1\u093C\U00016FF0\u0334\u0062", "\u0061\u0334\U00016FF1\U00016FF0\u093C\u0062"}, {"\u0061\U00016FF0\u0334\U0001BC9E\u0062", "\u0061\u0334\U0001BC9E\U00016FF0\u0062"}, {"\u0061\U0001BC9E\U00016FF0\u0334\u0062", "\u0061\U0001BC9E\u0334\U00016FF0\u0062"}, {"\u0061\u1DFA\u031B\u1DCE\U0001D165\u0062", "\u0061\u1DCE\u031B\U0001D165\u1DFA\u0062"}, {"\u0061\U0001D165\u1DFA\u031B\u1DCE\u0062", "\u0061\u1DCE\U0001D165\u031B\u1DFA\u0062"}, {"\u0061\u1DFA\u031B\u1DCE\U0001D166\u0062", "\u0061\u1DCE\u031B\U0001D166\u1DFA\u0062"}, {"\u0061\U0001D166\u1DFA\u031B\u1DCE\u0062", "\u0061\u1DCE\U0001D166\u031B\u1DFA\u0062"}, {"\u0061\U00016FF0\u0334\U0001D167\u0062", "\u0061\u0334\U0001D167\U00016FF0\u0062"}, {"\u0061\U0001D167\U00016FF0\u0334\u0062", "\u0061\U0001D167\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\U0001D168\u0062", "\u0061\u0334\U0001D168\U00016FF0\u0062"}, {"\u0061\U0001D168\U00016FF0\u0334\u0062", "\u0061\U0001D168\u0334\U00016FF0\u0062"}, {"\u0061\U00016FF0\u0334\U0001D169\u0062", "\u0061\u0334\U0001D169\U00016FF0\u0062"}, {"\u0061\U0001D169\U00016FF0\u0334\u0062", "\u0061\U0001D169\u0334\U00016FF0\u0062"}, {"\u0061\u05AE\U0001D16D\u302E\U0001D16D\u0062", "\u0061\u302E\U0001D16D\U0001D16D\u05AE\u0062"}, {"\u0061\U0001D16D\u05AE\U0001D16D\u302E\u0062", "\u0061\u302E\U0001D16D\U0001D16D\u05AE\u0062"}, {"\u0061\u1DFA\u031B\u1DCE\U0001D16E\u0062", "\u0061\u1DCE\u031B\U0001D16E\u1DFA\u0062"}, {"\u0061\U0001D16E\u1DFA\u031B\u1DCE\u0062", "\u0061\u1DCE\U0001D16E\u031B\u1DFA\u0062"}, {"\u0061\u1DFA\u031B\u1DCE\U0001D16F\u0062", "\u0061\u1DCE\u031B\U0001D16F\u1DFA\u0062"}, {"\u0061\U0001D16F\u1DFA\u031B\u1DCE\u0062", "\u0061\u1DCE\U0001D16F\u031B\u1DFA\u0062"}, {"\u0061\u1DFA\u031B\u1DCE\U0001D170\u0062", "\u0061\u1DCE\u031B\U0001D170\u1DFA\u0062"}, {"\u0061\U0001D170\u1DFA\u031B\u1DCE\u0062", "\u0061\u1DCE\U0001D170\u031B\u1DFA\u0062"}, {"\u0061\u1DFA\u031B\u1DCE\U0001D171\u0062", "\u0061\u1DCE\u031B\U0001D171\u1DFA\u0062"}, {"\u0061\U0001D171\u1DFA\u031B\u1DCE\u0062", "\u0061\u1DCE\U0001D171\u031B\u1DFA\u0062"}, {"\u0061\u1DFA\u031B\u1DCE\U0001D172\u0062", "\u0061\u1DCE\u031B\U0001D172\u1DFA\u0062"}, {"\u0061\U0001D172\u1DFA\u031B\u1DCE\u0062", "\u0061\u1DCE\U0001D172\u031B\u1DFA\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001D17B\u0062", "\u0061\u1DFA\u0316\U0001D17B\u059A\u0062"}, {"\u0061\U0001D17B\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001D17B\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001D17C\u0062", "\u0061\u1DFA\u0316\U0001D17C\u059A\u0062"}, {"\u0061\U0001D17C\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001D17C\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001D17D\u0062", "\u0061\u1DFA\u0316\U0001D17D\u059A\u0062"}, {"\u0061\U0001D17D\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001D17D\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001D17E\u0062", "\u0061\u1DFA\u0316\U0001D17E\u059A\u0062"}, {"\u0061\U0001D17E\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001D17E\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001D17F\u0062", "\u0061\u1DFA\u0316\U0001D17F\u059A\u0062"}, {"\u0061\U0001D17F\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001D17F\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001D180\u0062", "\u0061\u1DFA\u0316\U0001D180\u059A\u0062"}, {"\u0061\U0001D180\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001D180\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001D181\u0062", "\u0061\u1DFA\u0316\U0001D181\u059A\u0062"}, {"\u0061\U0001D181\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001D181\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001D182\u0062", "\u0061\u1DFA\u0316\U0001D182\u059A\u0062"}, {"\u0061\U0001D182\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001D182\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001D185\u0062", "\u00E0\u05AE\U0001D185\u0315\u0062"}, {"\u0061\U0001D185\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001D185\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001D186\u0062", "\u00E0\u05AE\U0001D186\u0315\u0062"}, {"\u0061\U0001D186\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001D186\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001D187\u0062", "\u00E0\u05AE\U0001D187\u0315\u0062"}, {"\u0061\U0001D187\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001D187\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001D188\u0062", "\u00E0\u05AE\U0001D188\u0315\u0062"}, {"\u0061\U0001D188\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001D188\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001D189\u0062", "\u00E0\u05AE\U0001D189\u0315\u0062"}, {"\u0061\U0001D189\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001D189\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001D18A\u0062", "\u0061\u1DFA\u0316\U0001D18A\u059A\u0062"}, {"\u0061\U0001D18A\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001D18A\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001D18B\u0062", "\u0061\u1DFA\u0316\U0001D18B\u059A\u0062"}, {"\u0061\U0001D18B\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001D18B\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001D1AA\u0062", "\u00E0\u05AE\U0001D1AA\u0315\u0062"}, {"\u0061\U0001D1AA\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001D1AA\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001D1AB\u0062", "\u00E0\u05AE\U0001D1AB\u0315\u0062"}, {"\u0061\U0001D1AB\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001D1AB\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001D1AC\u0062", "\u00E0\u05AE\U0001D1AC\u0315\u0062"}, {"\u0061\U0001D1AC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001D1AC\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001D1AD\u0062", "\u00E0\u05AE\U0001D1AD\u0315\u0062"}, {"\u0061\U0001D1AD\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001D1AD\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001D242\u0062", "\u00E0\u05AE\U0001D242\u0315\u0062"}, {"\u0061\U0001D242\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001D242\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001D243\u0062", "\u00E0\u05AE\U0001D243\u0315\u0062"}, {"\u0061\U0001D243\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001D243\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001D244\u0062", "\u00E0\u05AE\U0001D244\u0315\u0062"}, {"\u0061\U0001D244\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001D244\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E000\u0062", "\u00E0\u05AE\U0001E000\u0315\u0062"}, {"\u0061\U0001E000\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E000\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E001\u0062", "\u00E0\u05AE\U0001E001\u0315\u0062"}, {"\u0061\U0001E001\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E001\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E002\u0062", "\u00E0\u05AE\U0001E002\u0315\u0062"}, {"\u0061\U0001E002\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E002\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E003\u0062", "\u00E0\u05AE\U0001E003\u0315\u0062"}, {"\u0061\U0001E003\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E003\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E004\u0062", "\u00E0\u05AE\U0001E004\u0315\u0062"}, {"\u0061\U0001E004\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E004\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E005\u0062", "\u00E0\u05AE\U0001E005\u0315\u0062"}, {"\u0061\U0001E005\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E005\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E006\u0062", "\u00E0\u05AE\U0001E006\u0315\u0062"}, {"\u0061\U0001E006\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E006\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E008\u0062", "\u00E0\u05AE\U0001E008\u0315\u0062"}, {"\u0061\U0001E008\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E008\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E009\u0062", "\u00E0\u05AE\U0001E009\u0315\u0062"}, {"\u0061\U0001E009\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E009\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E00A\u0062", "\u00E0\u05AE\U0001E00A\u0315\u0062"}, {"\u0061\U0001E00A\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E00A\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E00B\u0062", "\u00E0\u05AE\U0001E00B\u0315\u0062"}, {"\u0061\U0001E00B\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E00B\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E00C\u0062", "\u00E0\u05AE\U0001E00C\u0315\u0062"}, {"\u0061\U0001E00C\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E00C\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E00D\u0062", "\u00E0\u05AE\U0001E00D\u0315\u0062"}, {"\u0061\U0001E00D\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E00D\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E00E\u0062", "\u00E0\u05AE\U0001E00E\u0315\u0062"}, {"\u0061\U0001E00E\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E00E\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E00F\u0062", "\u00E0\u05AE\U0001E00F\u0315\u0062"}, {"\u0061\U0001E00F\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E00F\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E010\u0062", "\u00E0\u05AE\U0001E010\u0315\u0062"}, {"\u0061\U0001E010\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E010\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E011\u0062", "\u00E0\u05AE\U0001E011\u0315\u0062"}, {"\u0061\U0001E011\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E011\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E012\u0062", "\u00E0\u05AE\U0001E012\u0315\u0062"}, {"\u0061\U0001E012\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E012\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E013\u0062", "\u00E0\u05AE\U0001E013\u0315\u0062"}, {"\u0061\U0001E013\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E013\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E014\u0062", "\u00E0\u05AE\U0001E014\u0315\u0062"}, {"\u0061\U0001E014\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E014\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E015\u0062", "\u00E0\u05AE\U0001E015\u0315\u0062"}, {"\u0061\U0001E015\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E015\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E016\u0062", "\u00E0\u05AE\U0001E016\u0315\u0062"}, {"\u0061\U0001E016\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E016\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E017\u0062", "\u00E0\u05AE\U0001E017\u0315\u0062"}, {"\u0061\U0001E017\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E017\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E018\u0062", "\u00E0\u05AE\U0001E018\u0315\u0062"}, {"\u0061\U0001E018\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E018\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E01B\u0062", "\u00E0\u05AE\U0001E01B\u0315\u0062"}, {"\u0061\U0001E01B\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E01B\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E01C\u0062", "\u00E0\u05AE\U0001E01C\u0315\u0062"}, {"\u0061\U0001E01C\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E01C\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E01D\u0062", "\u00E0\u05AE\U0001E01D\u0315\u0062"}, {"\u0061\U0001E01D\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E01D\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E01E\u0062", "\u00E0\u05AE\U0001E01E\u0315\u0062"}, {"\u0061\U0001E01E\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E01E\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E01F\u0062", "\u00E0\u05AE\U0001E01F\u0315\u0062"}, {"\u0061\U0001E01F\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E01F\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E020\u0062", "\u00E0\u05AE\U0001E020\u0315\u0062"}, {"\u0061\U0001E020\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E020\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E021\u0062", "\u00E0\u05AE\U0001E021\u0315\u0062"}, {"\u0061\U0001E021\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E021\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E023\u0062", "\u00E0\u05AE\U0001E023\u0315\u0062"}, {"\u0061\U0001E023\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E023\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E024\u0062", "\u00E0\u05AE\U0001E024\u0315\u0062"}, {"\u0061\U0001E024\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E024\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E026\u0062", "\u00E0\u05AE\U0001E026\u0315\u0062"}, {"\u0061\U0001E026\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E026\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E027\u0062", "\u00E0\u05AE\U0001E027\u0315\u0062"}, {"\u0061\U0001E027\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E027\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E028\u0062", "\u00E0\u05AE\U0001E028\u0315\u0062"}, {"\u0061\U0001E028\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E028\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E029\u0062", "\u00E0\u05AE\U0001E029\u0315\u0062"}, {"\u0061\U0001E029\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E029\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E02A\u0062", "\u00E0\u05AE\U0001E02A\u0315\u0062"}, {"\u0061\U0001E02A\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E02A\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E08F\u0062", "\u00E0\u05AE\U0001E08F\u0315\u0062"}, {"\u0061\U0001E08F\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E08F\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E130\u0062", "\u00E0\u05AE\U0001E130\u0315\u0062"}, {"\u0061\U0001E130\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E130\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E131\u0062", "\u00E0\u05AE\U0001E131\u0315\u0062"}, {"\u0061\U0001E131\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E131\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E132\u0062", "\u00E0\u05AE\U0001E132\u0315\u0062"}, {"\u0061\U0001E132\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E132\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E133\u0062", "\u00E0\u05AE\U0001E133\u0315\u0062"}, {"\u0061\U0001E133\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E133\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E134\u0062", "\u00E0\u05AE\U0001E134\u0315\u0062"}, {"\u0061\U0001E134\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E134\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E135\u0062", "\u00E0\u05AE\U0001E135\u0315\u0062"}, {"\u0061\U0001E135\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E135\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E136\u0062", "\u00E0\u05AE\U0001E136\u0315\u0062"}, {"\u0061\U0001E136\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E136\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E2AE\u0062", "\u00E0\u05AE\U0001E2AE\u0315\u0062"}, {"\u0061\U0001E2AE\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E2AE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E2EC\u0062", "\u00E0\u05AE\U0001E2EC\u0315\u0062"}, {"\u0061\U0001E2EC\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E2EC\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E2ED\u0062", "\u00E0\u05AE\U0001E2ED\u0315\u0062"}, {"\u0061\U0001E2ED\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E2ED\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E2EE\u0062", "\u00E0\u05AE\U0001E2EE\u0315\u0062"}, {"\u0061\U0001E2EE\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E2EE\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E2EF\u0062", "\u00E0\u05AE\U0001E2EF\u0315\u0062"}, {"\u0061\U0001E2EF\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E2EF\u0300\u0315\u0062"}, {"\u0061\u035C\u0315\u0300\U0001E4EC\u0062", "\u00E0\u0315\U0001E4EC\u035C\u0062"}, {"\u0061\U0001E4EC\u035C\u0315\u0300\u0062", "\u00E0\U0001E4EC\u0315\u035C\u0062"}, {"\u0061\u035C\u0315\u0300\U0001E4ED\u0062", "\u00E0\u0315\U0001E4ED\u035C\u0062"}, {"\u0061\U0001E4ED\u035C\u0315\u0300\u0062", "\u00E0\U0001E4ED\u0315\u035C\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001E4EE\u0062", "\u0061\u1DFA\u0316\U0001E4EE\u059A\u0062"}, {"\u0061\U0001E4EE\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001E4EE\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E4EF\u0062", "\u00E0\u05AE\U0001E4EF\u0315\u0062"}, {"\u0061\U0001E4EF\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E4EF\u0300\u0315\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001E8D0\u0062", "\u0061\u1DFA\u0316\U0001E8D0\u059A\u0062"}, {"\u0061\U0001E8D0\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001E8D0\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001E8D1\u0062", "\u0061\u1DFA\u0316\U0001E8D1\u059A\u0062"}, {"\u0061\U0001E8D1\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001E8D1\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001E8D2\u0062", "\u0061\u1DFA\u0316\U0001E8D2\u059A\u0062"}, {"\u0061\U0001E8D2\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001E8D2\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001E8D3\u0062", "\u0061\u1DFA\u0316\U0001E8D3\u059A\u0062"}, {"\u0061\U0001E8D3\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001E8D3\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001E8D4\u0062", "\u0061\u1DFA\u0316\U0001E8D4\u059A\u0062"}, {"\u0061\U0001E8D4\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001E8D4\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001E8D5\u0062", "\u0061\u1DFA\u0316\U0001E8D5\u059A\u0062"}, {"\u0061\U0001E8D5\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001E8D5\u0316\u059A\u0062"}, {"\u0061\u059A\u0316\u1DFA\U0001E8D6\u0062", "\u0061\u1DFA\u0316\U0001E8D6\u059A\u0062"}, {"\u0061\U0001E8D6\u059A\u0316\u1DFA\u0062", "\u0061\u1DFA\U0001E8D6\u0316\u059A\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E944\u0062", "\u00E0\u05AE\U0001E944\u0315\u0062"}, {"\u0061\U0001E944\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E944\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E945\u0062", "\u00E0\u05AE\U0001E945\u0315\u0062"}, {"\u0061\U0001E945\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E945\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E946\u0062", "\u00E0\u05AE\U0001E946\u0315\u0062"}, {"\u0061\U0001E946\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E946\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E947\u0062", "\u00E0\u05AE\U0001E947\u0315\u0062"}, {"\u0061\U0001E947\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E947\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E948\u0062", "\u00E0\u05AE\U0001E948\u0315\u0062"}, {"\u0061\U0001E948\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E948\u0300\u0315\u0062"}, {"\u0061\u0315\u0300\u05AE\U0001E949\u0062", "\u00E0\u05AE\U0001E949\u0315\u0062"}, {"\u0061\U0001E949\u0315\u0300\u05AE\u0062", "\u0061\u05AE\U0001E949\u0300\u0315\u0062"}, {"\u0061\u3099\u093C\U00016FF0\U0001E94A\u0062", "\u0061\U00016FF0\u093C\U0001E94A\u3099\u0062"}, {"\u0061\U0001E94A\u3099\u093C\U00016FF0\u0062", "\u0061\U00016FF0\U0001E94A\u093C\u3099\u0062"}, {"\u09C7\u0334\u09BE", "\u09C7\u0334\u09BE"}, {"\u09C7\u0334\u09D7", "\u09C7\u0334\u09D7"}, {"\u0B47\u0334\u0B3E", "\u0B47\u0334\u0B3E"}, {"\u0B47\u0334\u0B56", "\u0B47\u0334\u0B56"}, {"\u0B47\u0334\u0B57", "\u0B47\u0334\u0B57"}, {"\u0B92\u0334\u0BD7", "\u0B92\u0334\u0BD7"}, {"\u0BC6\u0334\u0BBE", "\u0BC6\u0334\u0BBE"}, {"\u0BC6\u0334\u0BD7", "\u0BC6\u0334\u0BD7"}, {"\u0BC7\u0334\u0BBE", "\u0BC7\u0334\u0BBE"}, {"\u0CBF\u0334\u0CD5", "\u0CBF\u0334\u0CD5"}, {"\u0CC6\u0334\u0CC2", "\u0CC6\u0334\u0CC2"}, {"\u0CC6\u0334\u0CD5", "\u0CC6\u0334\u0CD5"}, {"\u0CC6\u0334\u0CD6", "\u0CC6\u0334\u0CD6"}, {"\u0CCA\u0334\u0CD5", "\u0CCA\u0334\u0CD5"}, {"\u0D46\u0334\u0D3E", "\u0D46\u0334\u0D3E"}, {"\u0D46\u0334\u0D57", "\u0D46\u0334\u0D57"}, {"\u0D47\u0334\u0D3E", "\u0D47\u0334\u0D3E"}, {"\u0DD9\u0334\u0DCF", "\u0DD9\u0334\u0DCF"}, {"\u0DD9\u0334\u0DDF", "\u0DD9\u0334\u0DDF"}, {"\u0F40\u0334\u0FB5", "\u0F40\u0334\u0FB5"}, {"\u0F42\u0334\u0FB7", "\u0F42\u0334\u0FB7"}, {"\u0F4C\u0334\u0FB7", "\u0F4C\u0334\u0FB7"}, {"\u0F51\u0334\u0FB7", "\u0F51\u0334\u0FB7"}, {"\u0F56\u0334\u0FB7", "\u0F56\u0334\u0FB7"}, {"\u0F5B\u0334\u0FB7", "\u0F5B\u0334\u0FB7"}, {"\u0F90\u0334\u0FB5", "\u0F90\u0334\u0FB5"}, {"\u0F92\u0334\u0FB7", "\u0F92\u0334\u0FB7"}, {"\u0F9C\u0334\u0FB7", "\u0F9C\u0334\u0FB7"}, {"\u0FA1\u0334\u0FB7", "\u0FA1\u0334\u0FB7"}, {"\u0FA6\u0334\u0FB7", "\u0FA6\u0334\u0FB7"}, {"\u0FAB\u0334\u0FB7", "\u0FAB\u0334\u0FB7"}, {"\u1025\u0334\u102E", "\u1025\u0334\u102E"}, {"\u1100\u0334\u1161", "\u1100\u0334\u1161"}, {"\u1100\u0334\u116E", "\u1100\u0334\u116E"}, {"\u1101\u0334\u1166", "\u1101\u0334\u1166"}, {"\u1101\u0334\u1173", "\u1101\u0334\u1173"}, {"\u1102\u0334\u116B", "\u1102\u0334\u116B"}, {"\u1103\u0334\u1163", "\u1103\u0334\u1163"}, {"\u1103\u0334\u1170", "\u1103\u0334\u1170"}, {"\u1104\u0334\u1168", "\u1104\u0334\u1168"}, {"\u1104\u0334\u1175", "\u1104\u0334\u1175"}, {"\u1105\u0334\u116D", "\u1105\u0334\u116D"}, {"\u1106\u0334\u1165", "\u1106\u0334\u1165"}, {"\u1106\u0334\u1172", "\u1106\u0334\u1172"}, {"\u1107\u0334\u116A", "\u1107\u0334\u116A"}, {"\u1108\u0334\u1162", "\u1108\u0334\u1162"}, {"\u1108\u0334\u116F", "\u1108\u0334\u116F"}, {"\u1109\u0334\u1167", "\u1109\u0334\u1167"}, {"\u1109\u0334\u1174", "\u1109\u0334\u1174"}, {"\u110A\u0334\u116C", "\u110A\u0334\u116C"}, {"\u110B\u0334\u1164", "\u110B\u0334\u1164"}, {"\u110B\u0334\u1171", "\u110B\u0334\u1171"}, {"\u110C\u0334\u1169", "\u110C\u0334\u1169"}, {"\u110D\u0334\u1161", "\u110D\u0334\u1161"}, {"\u110D\u0334\u116E", "\u110D\u0334\u116E"}, {"\u110E\u0334\u1166", "\u110E\u0334\u1166"}, {"\u110E\u0334\u1173", "\u110E\u0334\u1173"}, {"\u110F\u0334\u116B", "\u110F\u0334\u116B"}, {"\u1110\u0334\u1163", "\u1110\u0334\u1163"}, {"\u1110\u0334\u1170", "\u1110\u0334\u1170"}, {"\u1111\u0334\u1168", "\u1111\u0334\u1168"}, {"\u1111\u0334\u1175", "\u1111\u0334\u1175"}, {"\u1112\u0334\u116D", "\u1112\u0334\u116D"}, {"\u1B05\u0334\u1B35", "\u1B05\u0334\u1B35"}, {"\u1B07\u0334\u1B35", "\u1B07\u0334\u1B35"}, {"\u1B09\u0334\u1B35", "\u1B09\u0334\u1B35"}, {"\u1B0B\u0334\u1B35", "\u1B0B\u0334\u1B35"}, {"\u1B0D\u0334\u1B35", "\u1B0D\u0334\u1B35"}, {"\u1B11\u0334\u1B35", "\u1B11\u0334\u1B35"}, {"\u1B3A\u0334\u1B35", "\u1B3A\u0334\u1B35"}, {"\u1B3C\u0334\u1B35", "\u1B3C\u0334\u1B35"}, {"\u1B3E\u0334\u1B35", "\u1B3E\u0334\u1B35"}, {"\u1B3F\u0334\u1B35", "\u1B3F\u0334\u1B35"}, {"\u1B42\u0334\u1B35", "\u1B42\u0334\u1B35"}, {"\uAC54\u0334\u11AE", "\uAC54\u0334\u11AE"}, {"\uACA8\u0334\u11B5", "\uACA8\u0334\u11B5"}, {"\uACFC\u0334\u11BC", "\uACFC\u0334\u11BC"}, {"\uADC0\u0334\u11AE", "\uADC0\u0334\u11AE"}, {"\uAE14\u0334\u11B5", "\uAE14\u0334\u11B5"}, {"\uAE68\u0334\u11BC", "\uAE68\u0334\u11BC"}, {"\uAF2C\u0334\u11AE", "\uAF2C\u0334\u11AE"}, {"\uAF80\u0334\u11B5", "\uAF80\u0334\u11B5"}, {"\uAFD4\u0334\u11BC", "\uAFD4\u0334\u11BC"}, {"\uB098\u0334\u11AE", "\uB098\u0334\u11AE"}, {"\uB0EC\u0334\u11B5", "\uB0EC\u0334\u11B5"}, {"\uB140\u0334\u11BC", "\uB140\u0334\u11BC"}, {"\uB204\u0334\u11AE", "\uB204\u0334\u11AE"}, {"\uB258\u0334\u11B5", "\uB258\u0334\u11B5"}, {"\uB2AC\u0334\u11BC", "\uB2AC\u0334\u11BC"}, {"\uB370\u0334\u11AE", "\uB370\u0334\u11AE"}, {"\uB3C4\u0334\u11B5", "\uB3C4\u0334\u11B5"}, {"\uB418\u0334\u11BC", "\uB418\u0334\u11BC"}, {"\uB4DC\u0334\u11AE", "\uB4DC\u0334\u11AE"}, {"\uB530\u0334\u11B5", "\uB530\u0334\u11B5"}, {"\uB584\u0334\u11BC", "\uB584\u0334\u11BC"}, {"\uB648\u0334\u11AE", "\uB648\u0334\u11AE"}, {"\uB69C\u0334\u11B5", "\uB69C\u0334\u11B5"}, {"\uB6F0\u0334\u11BC", "\uB6F0\u0334\u11BC"}, {"\uB7B4\u0334\u11AE", "\uB7B4\u0334\u11AE"}, {"\uB808\u0334\u11B5", "\uB808\u0334\u11B5"}, {"\uB85C\u0334\u11BC", "\uB85C\u0334\u11BC"}, {"\uB920\u0334\u11AE", "\uB920\u0334\u11AE"}, {"\uB974\u0334\u11B5", "\uB974\u0334\u11B5"}, {"\uB9C8\u0334\u11BC", "\uB9C8\u0334\u11BC"}, {"\uBA8C\u0334\u11AE", "\uBA8C\u0334\u11AE"}, {"\uBAE0\u0334\u11B5", "\uBAE0\u0334\u11B5"}, {"\uBB34\u0334\u11BC", "\uBB34\u0334\u11BC"}, {"\uBBF8\u0334\u11AE", "\uBBF8\u0334\u11AE"}, {"\uBC4C\u0334\u11B5", "\uBC4C\u0334\u11B5"}, {"\uBCA0\u0334\u11BC", "\uBCA0\u0334\u11BC"}, {"\uBD64\u0334\u11AE", "\uBD64\u0334\u11AE"}, {"\uBDB8\u0334\u11B5", "\uBDB8\u0334\u11B5"}, {"\uBE0C\u0334\u11BC", "\uBE0C\u0334\u11BC"}, {"\uBED0\u0334\u11AE", "\uBED0\u0334\u11AE"}, {"\uBF24\u0334\u11B5", "\uBF24\u0334\u11B5"}, {"\uBF78\u0334\u11BC", "\uBF78\u0334\u11BC"}, {"\uC03C\u0334\u11AE", "\uC03C\u0334\u11AE"}, {"\uC090\u0334\u11B5", "\uC090\u0334\u11B5"}, {"\uC0E4\u0334\u11BC", "\uC0E4\u0334\u11BC"}, {"\uC1A8\u0334\u11AE", "\uC1A8\u0334\u11AE"}, {"\uC1FC\u0334\u11B5", "\uC1FC\u0334\u11B5"}, {"\uC250\u0334\u11BC", "\uC250\u0334\u11BC"}, {"\uC314\u0334\u11AE", "\uC314\u0334\u11AE"}, {"\uC368\u0334\u11B5", "\uC368\u0334\u11B5"}, {"\uC3BC\u0334\u11BC", "\uC3BC\u0334\u11BC"}, {"\uC480\u0334\u11AE", "\uC480\u0334\u11AE"}, {"\uC4D4\u0334\u11B5", "\uC4D4\u0334\u11B5"}, {"\uC528\u0334\u11BC", "\uC528\u0334\u11BC"}, {"\uC5EC\u0334\u11AE", "\uC5EC\u0334\u11AE"}, {"\uC640\u0334\u11B5", "\uC640\u0334\u11B5"}, {"\uC694\u0334\u11BC", "\uC694\u0334\u11BC"}, {"\uC758\u0334\u11AE", "\uC758\u0334\u11AE"}, {"\uC7AC\u0334\u11B5", "\uC7AC\u0334\u11B5"}, {"\uC800\u0334\u11BC", "\uC800\u0334\u11BC"}, {"\uC8C4\u0334\u11AE", "\uC8C4\u0334\u11AE"}, {"\uC918\u0334\u11B5", "\uC918\u0334\u11B5"}, {"\uC96C\u0334\u11BC", "\uC96C\u0334\u11BC"}, {"\uCA30\u0334\u11AE", "\uCA30\u0334\u11AE"}, {"\uCA84\u0334\u11B5", "\uCA84\u0334\u11B5"}, {"\uCAD8\u0334\u11BC", "\uCAD8\u0334\u11BC"}, {"\uCB9C\u0334\u11AE", "\uCB9C\u0334\u11AE"}, {"\uCBF0\u0334\u11B5", "\uCBF0\u0334\u11B5"}, {"\uCC44\u0334\u11BC", "\uCC44\u0334\u11BC"}, {"\uCD08\u0334\u11AE", "\uCD08\u0334\u11AE"}, {"\uCD5C\u0334\u11B5", "\uCD5C\u0334\u11B5"}, {"\uCDB0\u0334\u11BC", "\uCDB0\u0334\u11BC"}, {"\uCE74\u0334\u11AE", "\uCE74\u0334\u11AE"}, {"\uCEC8\u0334\u11B5", "\uCEC8\u0334\u11B5"}, {"\uCF1C\u0334\u11BC", "\uCF1C\u0334\u11BC"}, {"\uCFE0\u0334\u11AE", "\uCFE0\u0334\u11AE"}, {"\uD034\u0334\u11B5", "\uD034\u0334\u11B5"}, {"\uD088\u0334\u11BC", "\uD088\u0334\u11BC"}, {"\uD14C\u0334\u11AE", "\uD14C\u0334\u11AE"}, {"\uD1A0\u0334\u11B5", "\uD1A0\u0334\u11B5"}, {"\uD1F4\u0334\u11BC", "\uD1F4\u0334\u11BC"}, {"\uD2B8\u0334\u11AE", "\uD2B8\u0334\u11AE"}, {"\uD30C\u0334\u11B5", "\uD30C\u0334\u11B5"}, {"\uD360\u0334\u11BC", "\uD360\u0334\u11BC"}, {"\uD424\u0334\u11AE", "\uD424\u0334\u11AE"}, {"\uD478\u0334\u11B5", "\uD478\u0334\u11B5"}, {"\uD4CC\u0334\u11BC", "\uD4CC\u0334\u11BC"}, {"\uD590\u0334\u11AE", "\uD590\u0334\u11AE"}, {"\uD5E4\u0334\u11B5", "\uD5E4\u0334\u11B5"}, {"\uD638\u0334\u11BC", "\uD638\u0334\u11BC"}, {"\uD6FC\u0334\u11AE", "\uD6FC\u0334\u11AE"}, {"\uD750\u0334\u11B5", "\uD750\u0334\u11B5"}, {"\U00011131\u0334\U00011127", "\U00011131\u0334\U00011127"}, {"\U00011132\u0334\U00011127", "\U00011132\u0334\U00011127"}, {"\U00011347\u0334\U0001133E", "\U00011347\u0334\U0001133E"}, {"\U00011347\u0334\U00011357", "\U00011347\u0334\U00011357"}, {"\U000114B9\u0334\U000114B0", "\U000114B9\u0334\U000114B0"}, {"\U000114B9\u0334\U000114BA", "\U000114B9\u0334\U000114BA"}, {"\U000114B9\u0334\U000114BD", "\U000114B9\u0334\U000114BD"}, {"\U000115B8\u0334\U000115AF", "\U000115B8\u0334\U000115AF"}, {"\U000115B9\u0334\U000115AF", "\U000115B9\u0334\U000115AF"}, {"\U00011935\u0334\U00011930", "\U00011935\u0334\U00011930"} }; cangjie_compiler-1.0.7/src/Utils/UnicodeTables/WidthData.generated.inc000066400000000000000000001337261510705540100257340ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file provides unicode display width functions. */ // NOTE: The following code was generated by "WidthData.py", do not edit directly #ifndef CANGJIE_UTILS_UNICODETABLES_WIDTHDATA_H #define CANGJIE_UTILS_UNICODETABLES_WIDTHDATA_H /// Autogenerated. 1 sub-table(s). Consult `LookupWidth` for layout info. constexpr unsigned char TABLES_0 alignas(128)[] { 0, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x0F, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x0F, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x10, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /// Autogenerated. 19 sub-table(s). Consult `LookupWidth` for layout info. constexpr unsigned char TABLES_1 alignas(128)[] { 0, 0, 0x01, 0x02, 0, 0, 0, 0, 0, 0, 0, 0x03, 0x04, 0x05, 0x06, 0, 0, 0, 0x07, 0, 0, 0, 0x08, 0x09, 0x0A, 0x0B, 0, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x19, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0, 0x33, 0x34, 0x04, 0x04, 0, 0, 0, 0, 0, 0x35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x36, 0x37, 0x38, 0x39, 0x3A, 0, 0x3B, 0, 0x3C, 0, 0, 0, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0, 0, 0x46, 0, 0, 0, 0x04, 0, 0, 0, 0, 0, 0, 0, 0, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0, 0x52, 0, 0, 0x53, 0, 0x54, 0x55, 0x56, 0x55, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x61, 0x62, 0, 0, 0, 0, 0, 0x63, 0, 0x64, 0, 0x65, 0, 0, 0x66, 0x67, 0x33, 0x33, 0x33, 0x68, 0x69, 0x6A, 0x6B, 0x33, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x72, 0x73, 0, 0, 0, 0, 0, 0x74, 0x75, 0x76, 0, 0, 0, 0, 0x77, 0, 0, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0, 0, 0, 0x81, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x82, 0x83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x85, 0x86, 0, 0x64, 0x6A, 0x87, 0x88, 0x89, 0, 0, 0, 0, 0, 0, 0, 0x8A, 0, 0, 0, 0x8B, 0, 0x8C, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x8D, 0, 0, 0x8E, 0, 0, 0, 0, 0, 0, 0, 0, 0x8F, 0, 0, 0, 0, 0, 0x90, 0x91, 0, 0x92, 0x93, 0, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x26, 0, 0x9D, 0x24, 0x9E, 0, 0, 0x9F, 0xA0, 0xA1, 0xA2, 0, 0, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0, 0xA8, 0, 0, 0, 0xA9, 0, 0, 0, 0xAA, 0xAB, 0, 0xAC, 0xAD, 0xAE, 0xAF, 0, 0, 0, 0, 0, 0xB0, 0, 0xB1, 0, 0xB2, 0xB3, 0xB4, 0, 0, 0, 0, 0xB5, 0xB6, 0xB7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB9, 0xBA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xBB, 0xBC, 0xBD, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xBE, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xBF, 0xC0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC1, 0x33, 0x33, 0x33, 0x33, 0xC2, 0xC3, 0x33, 0x33, 0x33, 0x33, 0x33, 0xC4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC6, 0xC7, 0, 0, 0, 0, 0, 0, 0, 0xC8, 0xC9, 0, 0, 0xCA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xCB, 0xCC, 0xCD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xCE, 0, 0xBB, 0, 0xBA, 0, 0, 0, 0, 0, 0xCF, 0xD0, 0, 0, 0, 0, 0, 0, 0, 0xD0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xD1, 0, 0xD2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xD3, 0, 0, 0xD4, 0xD5, 0xD6, 0xD7, 0, 0xD8, 0xD9, 0, 0, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0x33, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0x33, 0xE5, 0x33, 0xE6, 0, 0, 0, 0xE7, 0, 0, 0, 0, 0xE8, 0xE9, 0x33, 0x33, 0, 0xEA, 0xEB, 0xEC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xE1, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xED }; /// Autogenerated. 238 sub-table(s). Consult `LookupWidth` for layout info. constexpr unsigned char TABLES_2 alignas(16)[] { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x5D, 0xD7, 0x55, 0x71, 0xFF, 0xF5, 0x5D, 0xFF, 0x55, 0x55, 0x55, 0x55, 0x55, 0xD5, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xD5, 0x55, 0x55, 0x55, 0xD5, 0xFD, 0x5D, 0x57, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x55, 0x55, 0x55, 0x55, 0x55, 0xD5, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0x41, 0x10, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x51, 0x55, 0x55, 0, 0, 0x40, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0, 0, 0, 0, 0, 0x55, 0x55, 0x55, 0x55, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0, 0x14, 0, 0x14, 0x04, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0, 0, 0, 0, 0, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0, 0, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0, 0, 0x55, 0x55, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0x10, 0, 0, 0x01, 0x01, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 0x55, 0, 0, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45, 0x54, 0x01, 0, 0x54, 0x51, 0x01, 0, 0x55, 0x55, 0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x44, 0x01, 0x54, 0x55, 0x51, 0x55, 0x15, 0x55, 0x55, 0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45, 0x41, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x54, 0x41, 0x15, 0x14, 0x50, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 0x51, 0x55, 0x55, 0x01, 0x10, 0x54, 0x51, 0x55, 0x55, 0x55, 0x55, 0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x04, 0x01, 0x54, 0x55, 0x51, 0x55, 0x01, 0x55, 0x55, 0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45, 0x54, 0x55, 0x55, 0x51, 0x55, 0x15, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x04, 0x54, 0x05, 0x04, 0x50, 0x55, 0x41, 0x55, 0x55, 0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x14, 0x44, 0x05, 0x04, 0x50, 0x55, 0x41, 0x55, 0x55, 0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x44, 0x01, 0x54, 0x55, 0x51, 0x55, 0x15, 0x55, 0x55, 0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45, 0x15, 0x05, 0x44, 0x55, 0x15, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x51, 0, 0x40, 0x55, 0x55, 0x15, 0, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x51, 0, 0, 0x54, 0x55, 0x55, 0, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x11, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01, 0, 0, 0x40, 0, 0x04, 0x55, 0x01, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0x54, 0x55, 0x45, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01, 0x04, 0, 0x41, 0x41, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 0x05, 0x54, 0x55, 0x55, 0x55, 0x01, 0x54, 0x55, 0x55, 0x45, 0x41, 0x55, 0x51, 0x55, 0x55, 0x55, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0, 0, 0, 0, 0, 0, 0, 0, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x10, 0, 0x50, 0x55, 0x45, 0x01, 0, 0, 0x55, 0x55, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x41, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x40, 0x15, 0x54, 0x55, 0x45, 0x55, 0x01, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x14, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45, 0, 0x40, 0x44, 0x01, 0, 0x54, 0x15, 0, 0, 0x14, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0, 0, 0, 0, 0, 0, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0, 0x50, 0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0, 0, 0x55, 0x55, 0x55, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0x50, 0x10, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45, 0x50, 0x11, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0, 0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x40, 0, 0, 0, 0x04, 0, 0x54, 0x51, 0x55, 0x54, 0x50, 0x55, 0x55, 0x55, 0x15, 0, 0xD7, 0x7F, 0x5F, 0x5F, 0x7F, 0xFF, 0x05, 0x40, 0xF7, 0x5D, 0xD5, 0x75, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0, 0, 0, 0x55, 0x57, 0x55, 0x55, 0xFD, 0x57, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x57, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0, 0, 0, 0, 0, 0, 0, 0x54, 0x55, 0x55, 0x55, 0xD5, 0x5D, 0x5D, 0x55, 0xD5, 0x75, 0x55, 0x55, 0x7D, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xD5, 0x57, 0xD5, 0x7F, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0x5F, 0x55, 0x55, 0x55, 0x5D, 0x55, 0xFF, 0xFF, 0xFF, 0x55, 0x55, 0x55, 0x55, 0x75, 0x55, 0x55, 0x5F, 0x55, 0x55, 0x55, 0x55, 0xF5, 0x75, 0x57, 0x55, 0x55, 0x55, 0xD5, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xF7, 0xD7, 0xDF, 0xD7, 0x5D, 0x5D, 0x75, 0xFD, 0xD7, 0xFF, 0xFF, 0x77, 0x55, 0xFF, 0x55, 0x5F, 0x5D, 0x55, 0x5F, 0x57, 0x75, 0x55, 0x55, 0x55, 0x7F, 0xFF, 0xF5, 0xF5, 0x5F, 0x55, 0x55, 0x55, 0xF5, 0xFF, 0x5F, 0x55, 0x55, 0x5D, 0x5D, 0x55, 0x55, 0x5D, 0x55, 0x55, 0x55, 0x55, 0x55, 0xD5, 0x55, 0x55, 0x55, 0x55, 0x75, 0x55, 0xA5, 0x55, 0x55, 0x55, 0x69, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xA9, 0x56, 0x96, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x55, 0x55, 0x55, 0xFF, 0xFF, 0xFF, 0xFF, 0xF5, 0x5F, 0x55, 0x55, 0xDF, 0xFF, 0x5F, 0x55, 0xF5, 0xF5, 0x55, 0x5F, 0x5F, 0xF5, 0xD7, 0xF5, 0x5F, 0x55, 0x55, 0x55, 0xF5, 0x5F, 0x55, 0xD5, 0x55, 0x55, 0x55, 0x69, 0x55, 0x7D, 0x5D, 0xF5, 0x55, 0x5A, 0x55, 0x77, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x77, 0x55, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0xDF, 0xDF, 0x7F, 0xDF, 0x55, 0x55, 0x55, 0x95, 0x55, 0x55, 0x55, 0x55, 0x95, 0x55, 0x55, 0xF5, 0x59, 0x55, 0xA5, 0x55, 0x55, 0x55, 0x55, 0xE9, 0x55, 0xFA, 0xFF, 0xEF, 0xFF, 0xFE, 0xFF, 0xFF, 0xDF, 0x55, 0xEF, 0xFF, 0xAF, 0xFB, 0xEF, 0xFB, 0x55, 0x59, 0xA5, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x56, 0x55, 0x55, 0x55, 0x55, 0x5D, 0x55, 0x55, 0x55, 0x66, 0x95, 0x9A, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xF5, 0xFF, 0xFF, 0x55, 0x55, 0x55, 0x55, 0x55, 0xA9, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x56, 0x55, 0x55, 0x95, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x95, 0x56, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x56, 0xF9, 0x5F, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0, 0, 0, 0, 0, 0, 0, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x9A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x5A, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x0A, 0, 0xAA, 0xAA, 0xAA, 0x6A, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x6A, 0x81, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x6A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x6A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xFF, 0xFF, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x56, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x6A, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x40, 0, 0, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 0x55, 0x55, 0x55, 0x45, 0x45, 0x15, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x41, 0x55, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0, 0, 0, 0x50, 0x55, 0x45, 0x15, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0, 0, 0x50, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x56, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x05, 0x50, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01, 0x40, 0x41, 0x41, 0x55, 0x55, 0x15, 0x55, 0x55, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x04, 0x14, 0x54, 0x05, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 0x55, 0x45, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x51, 0x54, 0x51, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0, 0, 0, 0, 0, 0x40, 0x15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0, 0, 0, 0xAA, 0xAA, 0x5A, 0x55, 0, 0, 0, 0, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x6A, 0xAA, 0xAA, 0xAA, 0xAA, 0x6A, 0xAA, 0x55, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x56, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xAA, 0x6A, 0x55, 0x55, 0, 0, 0x54, 0x5D, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0x40, 0x55, 0x01, 0x41, 0x55, 0, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x40, 0x15, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x41, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01, 0x55, 0x05, 0, 0, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0, 0, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x14, 0x54, 0x55, 0x15, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x40, 0x41, 0x55, 0x45, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0, 0x01, 0, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x55, 0x55, 0x55, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0, 0x40, 0x55, 0x55, 0x01, 0x14, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x50, 0x04, 0x55, 0x45, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x15, 0, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x54, 0x55, 0x55, 0x55, 0x55, 0x15, 0x55, 0x55, 0x55, 0x05, 0, 0x54, 0, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0, 0x05, 0x44, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x14, 0, 0x44, 0x11, 0x04, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x05, 0x50, 0x55, 0x10, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0, 0x40, 0x11, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x51, 0, 0x10, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01, 0x05, 0x10, 0, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0, 0, 0x41, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x54, 0x55, 0x15, 0x44, 0x15, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0x05, 0x55, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01, 0, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0, 0x14, 0x40, 0x55, 0x15, 0x55, 0x55, 0x01, 0x40, 0x01, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0, 0, 0x40, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0x40, 0, 0x10, 0x55, 0x55, 0x55, 0x55, 0x05, 0, 0, 0, 0, 0, 0x05, 0, 0x04, 0x41, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x01, 0x40, 0x45, 0x10, 0, 0x10, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x50, 0x11, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x54, 0x55, 0x55, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0x40, 0x55, 0x44, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x54, 0x15, 0, 0, 0, 0x50, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xAA, 0x54, 0x55, 0x55, 0x5A, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x5A, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0x56, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xA9, 0xAA, 0x69, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x6A, 0x55, 0x55, 0x55, 0x65, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x6A, 0x59, 0x55, 0x55, 0x55, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x41, 0, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x50, 0, 0, 0, 0, 0, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x11, 0x50, 0x05, 0, 0, 0, 0, 0x40, 0x01, 0, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x05, 0x50, 0x55, 0x55, 0x55, 0x55, 0x05, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0x15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x54, 0x55, 0x51, 0x55, 0x55, 0x55, 0x54, 0x55, 0x55, 0x55, 0x55, 0x15, 0, 0x01, 0, 0, 0, 0x55, 0x55, 0x55, 0x55, 0, 0x40, 0, 0, 0, 0, 0x14, 0, 0x10, 0x04, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x56, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x95, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0xFF, 0x7F, 0x55, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5F, 0x55, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xAB, 0xAA, 0xEA, 0xFF, 0xFF, 0xFF, 0xFF, 0x57, 0x55, 0x55, 0x55, 0x55, 0x6A, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0xAA, 0xAA, 0x56, 0x55, 0x5A, 0x55, 0x55, 0x55, 0xAA, 0x5A, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x56, 0x55, 0x55, 0xA9, 0xAA, 0x9A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x6A, 0x95, 0xAA, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0x56, 0x56, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x6A, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x96, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x5A, 0x55, 0x55, 0x95, 0x6A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0x65, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x69, 0x55, 0x55, 0x55, 0x56, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x95, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xAA, 0x5A, 0x55, 0x56, 0x6A, 0xA9, 0x55, 0xAA, 0x55, 0x55, 0x95, 0x56, 0x55, 0xAA, 0xAA, 0x56, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0x55, 0x56, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x6A, 0xAA, 0xAA, 0x9A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xAA, 0xAA, 0xAA, 0x56, 0xAA, 0xAA, 0x56, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x9A, 0xAA, 0x5A, 0x55, 0xA5, 0xAA, 0xAA, 0xAA, 0x55, 0xAA, 0xAA, 0x56, 0x55, 0xAA, 0xAA, 0x56, 0x55, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5F }; /// Array of 1024-bit bitmaps. Index into the correct bitmap with the 10 LSB of your codepoint /// to get whether it can start an emoji presentation sequence. constexpr unsigned char EMOJI_PRESENTATION_LEAVES alignas(128)[][128] { { 0, 0, 0, 0, 0x08, 0x04, 0xFF, 0x03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0x10, 0, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x04, 0, 0, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xF0, 0x03, 0, 0x06, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0C, 0, 0x07, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0, 0, 0xFE, 0x0F, 0x07 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x04, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0C, 0x40, 0, 0x01, 0, 0, 0, 0, 0, 0, 0x78, 0x1F, 0x40, 0x32, 0x21, 0x4D, 0xC4, 0, 0x07, 0x05, 0xFF, 0x0F, 0x80, 0x69, 0x01, 0, 0xC8, 0, 0, 0xFC, 0x1A, 0x83, 0x0C, 0x03, 0x60, 0x30, 0xC1, 0x1A, 0, 0, 0x06, 0xBF, 0x27, 0x24, 0xBF, 0x54, 0x20, 0x02, 0x01, 0x18, 0, 0x90, 0x50, 0xB8, 0, 0x18, 0, 0, 0, 0, 0, 0xE0, 0, 0x02, 0, 0x01, 0x80, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xE0, 0, 0, 0x18, 0, 0, 0, 0, 0, 0, 0x21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x03, 0xC0, 0, 0x40, 0xFE, 0x07, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x07, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0x01, 0x03, 0, 0x3F, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xCE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB9, 0xFF }, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0, 0x7E, 0xFF, 0xFF, 0xFF, 0x80, 0xF9, 0x07, 0x80, 0x3C, 0x61, 0, 0x30, 0x01, 0x06, 0x10, 0x1C, 0, 0x0E, 0x70, 0x0A, 0x81, 0x08, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0xF8, 0xE7, 0xF0, 0x3F, 0x1A, 0xF9, 0x1F, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0x0F, 0x01, 0 }, }; /// Array of 1024-bit bitmaps. Index into the correct bitmap with the 10 LSB of your codepoint /// to get whether it can start a text presentation sequence. constexpr unsigned char TEXT_PRESENTATION_LEAVES alignas(128)[][128] { { 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFE, 0xFF }, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xAF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0x01, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0, 0, 0, 0, 0, 0xF0, 0, 0xFE, 0xFC, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0xE0, 0x20, 0x10, 0xFE, 0x1F, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0x21, 0, 0, 0xF8, 0xFF, 0x80, 0x70, 0, 0, 0x54, 0x7C, 0xF0, 0xFF, 0x01, 0x20, 0xEE, 0 }, { 0, 0x01, 0x20, 0x80, 0x40, 0, 0, 0x80, 0xC6, 0x63, 0x08, 0, 0, 0x04, 0, 0x20, 0, 0, 0, 0, 0x08, 0, 0x09, 0x88, 0, 0x08, 0, 0x84, 0x70, 0x3C, 0x80, 0x6E, 0, 0x21, 0x0C, 0, 0, 0, 0, 0xC0, 0xFF, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0x9F, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x20, 0x12, 0x01, 0, 0x20, 0x04, 0x16, 0xC0, 0xEF, 0x18, 0x0F, 0xFF, 0xE7, 0x0F, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0xF0, 0xFE, 0xFF }, }; namespace Cangjie::Unicode { /// Whether this character forms an emoji presentation sequence /// when followed by `'\uFEOF'`. /// Emoji presentation sequences are considered to have width 2. /// This may spuriously return `true` or `false` for characters that are always wide. bool StartsEmojiPresentationSeq(UTF32 cp) { // First level of lookup uses all but 10 LSB auto topBits = cp >> 10; int idxOfLeaf; switch (topBits) { case 0: idxOfLeaf = 0; break; case 8: idxOfLeaf = 1; break; case 9: idxOfLeaf = 2; break; case 10: idxOfLeaf = 3; break; case 124: idxOfLeaf = 4; break; case 125: idxOfLeaf = 5; break; default: return false; }; // Extract the 3-9th (0-indexed) least significant bits of `cp`, // and use them to index into `leaf_row`. auto indexWithinLeaf = (cp >> 3) & 0x7F; auto leafByte = EMOJI_PRESENTATION_LEAVES[idxOfLeaf][indexWithinLeaf]; // Use the 3 LSB of `cp` to index into `leafByte`. return ((leafByte >> (cp & 7)) & 1) == 1; } /// Returns `true` iff `c` has default emoji presentation, but forms a text presentation sequence /// when followed by `'\u{FEOE}'`, and is not ideographic. /// Such sequences are considered to have width 1. /// /// This may spuriously return `true` for characters of narrow or ambiguous width. bool StartsNonIdeographicTextPresentationSeq(UTF32 cp) { // First level of lookup uses all but 10 LSB auto topBits = cp >> 10; int idxOfLeaf; switch (topBits) { case 8: idxOfLeaf = 0; break; case 9: idxOfLeaf = 1; break; case 10: idxOfLeaf = 2; break; case 124: idxOfLeaf = 3; break; case 125: idxOfLeaf = 4; break; default: return false; }; // Extract the 3-9th (0-indexed) least significant bits of `cp`, // and use them to index into `leaf_row`. auto indexWithinLeaf = (cp >> 3) & 0x7F; auto leafByte = TEXT_PRESENTATION_LEAVES[idxOfLeaf][indexWithinLeaf]; // Use the 3 LSB of `cp` to index into `leafByte`. return ((leafByte >> (cp & 7)) & 1) == 1; } } // namespace Cangjie::Unicode #endif // CANGJIE_UTILS_UNICODETABLES_WIDTHDATA_H cangjie_compiler-1.0.7/src/Utils/UnicodeWidth.cpp000066400000000000000000000111641510705540100217730ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include "cangjie/Utils/Unicode.h" #include "UnicodeTables/WidthData.generated.inc" namespace Cangjie::Unicode { /// For maintenance: /// The tables themselves are autogenerated but this function is hardcoded. You should have /// nothing to worry about if you re-run `WidthData.py` (for example, when updating Unicode.) /// However, if you change the *actual structure* of the lookup tables (perhaps by editing the /// `TABLE_CFGS` global in `WidthData.py`) you must ensure that this code reflects those changes. int LookupWidth(UTF32 cp, bool isCJK) { auto t1Offset = TABLES_0[(cp >> 13) & 0xFF]; auto t2Offset = TABLES_1[128 * t1Offset + ((cp >> 6) & 0x7F)]; auto packedWidths = TABLES_2[16 * t2Offset + ((cp >> 2) & 0xF)]; auto width = (packedWidths >> (2 * (cp & 0b11))) & 0b11; return width == 3 ? isCJK ? 2 : 1 : width; } namespace { enum class NextCharInfo { DEFAULT, LF, /// \u0338 /// For preserving canonical equivalence with CJK COMBINING_LONG_SOLIDUS_OVERLAY, /// \ua4fc..\ua4fd /// Unicode ch18 TRAILING_LISU_TONE_LETTER, /// \ufe0e VS15, /// \ufe0f VS16 }; std::pair WidthInStr(UTF32 c, bool isCJK, NextCharInfo nextInfo) { if ((isCJK && nextInfo == NextCharInfo::COMBINING_LONG_SOLIDUS_OVERLAY && (c == '<' || c == '=' || c == '>')) || (nextInfo == NextCharInfo::VS16 && StartsEmojiPresentationSeq(c))) { return {2, NextCharInfo::DEFAULT}; } if (c <= 0xa0) { if (c == '\n') { return {1, NextCharInfo::LF}; } if (c == '\r' && nextInfo == NextCharInfo::LF) { return {0, NextCharInfo::DEFAULT}; } return {1, NextCharInfo::DEFAULT}; } if (c >= 0xa4f8 && c <= 0xa4fb && nextInfo == NextCharInfo::TRAILING_LISU_TONE_LETTER) { return {0, NextCharInfo::DEFAULT}; } if (c == 0x0338) { return {0, NextCharInfo::COMBINING_LONG_SOLIDUS_OVERLAY}; } if (c >= 0xa4fc && c <= 0xa4fd) { return {1, NextCharInfo::TRAILING_LISU_TONE_LETTER}; } if (c == 0xfe0e) { return {0, NextCharInfo::VS15}; } if (c == 0xfe0f) { return {0, NextCharInfo::VS16}; } if (nextInfo == NextCharInfo::VS15 && !isCJK && StartsNonIdeographicTextPresentationSeq(c)) { return {1, NextCharInfo::DEFAULT}; } return {LookupWidth(c, isCJK), NextCharInfo::DEFAULT}; } } int StrWidth(StringRef s, bool isCJK) { auto str = s.ToUTF32(); auto res{0}; NextCharInfo st{NextCharInfo::DEFAULT}; for (auto it = str.crbegin(); it != str.crend(); ++it) { UTF32 c = *it; auto r = WidthInStr(c, isCJK, st); res += static_cast(r.first); st = r.second; } return res; } int SingleCharWidth(UTF32 codepoint, bool isCJK) { if (codepoint < 0x7f) { if (codepoint >= 0x20) { // U+0020..U+007f are single-width ASCII chars return 1; } // U+0001..U+0020 are control codes return 1; } if (codepoint >= 0xa0) { return LookupWidth(codepoint, isCJK); } // U+007f..U+00a0 are control codes return 1; } constexpr static UTF32 TAB = 0x9; constexpr static int TAB_LENGTH{4}; constexpr static int UNICODE_ESCAPE_WIDTH{8}; int DisplayWidth(StringRef s, bool isCJK) { auto str = s.ToUTF32(); auto res{0}; NextCharInfo st{NextCharInfo::DEFAULT}; for (auto it = str.crbegin(); it != str.crend(); ++it) { UTF32 c = *it; if (c == TAB) { // this may be incorrect if there is a remaining sequence awaiting in the cache res += 4; // Table is 4 spaces st = NextCharInfo::DEFAULT; continue; } // these chars are output in form '\u{00xx}', which has a length of 8 if (c <= 0x8 || (0xb <= c && c <= 0x1f) || c == 0x7f) { res += UNICODE_ESCAPE_WIDTH; st = NextCharInfo::DEFAULT; continue; } auto r = WidthInStr(c, isCJK, st); res += static_cast(r.first); st = r.second; } return res; } int DisplayWidth(UTF32 cp, bool isCJK) { // these chars are output in form '\u{00xx}', which has a length of 8 if (cp <= 0x8 || (0xb <= cp && cp <= 0x1f) || cp == 0x7f) { return UNICODE_ESCAPE_WIDTH; } if (cp == TAB) { return TAB_LENGTH; } return SingleCharWidth(cp, isCJK); } } cangjie_compiler-1.0.7/src/Utils/UserBase.cpp000066400000000000000000000033661510705540100211230ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "UserBase.h" #include #include #include "cangjie/Utils/CheckUtils.h" #include "cangjie/Utils/FileUtil.h" using namespace Cangjie; std::string UserBase::GetResult() const { if (!enable) { return ""; } return GetJson(); } void UserBase::OutputResult() const noexcept { if (!enable) { return; } #ifndef CANGJIE_ENABLE_GCOV try { #endif std::string result = GetResult(); WriteJson(result, GetSuffix()); #ifndef CANGJIE_ENABLE_GCOV } catch (...) { std::cerr << "Get an exception while running function 'OutputResult' !!!\n" << std::endl; } #endif } void UserBase::WriteJson(const std::string& context, const std::string& suffix) const { std::string name = packageName; size_t startPos = name.find('/'); if (startPos != std::string::npos) { (void)name.replace(startPos, 1, "-"); } std::string filename = name + suffix; std::ofstream out(FileUtil::JoinPath(outputDir, filename).c_str()); if (!out) { return; } out.write(context.c_str(), static_cast(context.size())); out.close(); } void UserBase::Enable(bool en) { enable = en; } bool UserBase::IsEnable() const { return enable; } void UserBase::SetPackageName(const std::string& name) { packageName = name; } void UserBase::SetOutputDir(const std::string& path) { if (FileUtil::IsDir(path)) { outputDir = path; } else { outputDir = FileUtil::GetDirPath(path); } }cangjie_compiler-1.0.7/src/Utils/UserBase.h000066400000000000000000000017271510705540100205670ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_USERBASE_H #define CANGJIE_USERBASE_H #include namespace Cangjie { class UserBase { public: virtual std::string GetResult() const; void OutputResult() const noexcept; void WriteJson(const std::string& context, const std::string& suffix) const; virtual ~UserBase() = default; void Enable(bool en); bool IsEnable() const; void SetPackageName(const std::string& name); void SetOutputDir(const std::string& path); protected: bool enable{false}; std::string packageName; std::string outputDir; private: virtual std::string GetSuffix() const = 0; virtual std::string GetJson() const = 0; }; } // namespace Cangjie #endif // CANGJIE_USERBASE_H cangjie_compiler-1.0.7/src/Utils/UserCodeInfo.cpp000066400000000000000000000013731510705540100217330ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "UserCodeInfo.h" namespace Cangjie { void UserCodeInfo::RecordInfo(const std::string& item, int64_t value) { codeInfo.emplace_back(item, value); } std::string UserCodeInfo::GetJson() const { std::string output; output += "{"; for (auto& it : codeInfo) { output += ("\n \"" + it.first + "\": " + std::to_string(it.second) + ","); } if (!codeInfo.empty()) { output.pop_back(); } output += "\n}\n"; return output; } } // namespace Cangjie cangjie_compiler-1.0.7/src/Utils/UserCodeInfo.h000066400000000000000000000017051510705540100213770ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_USERCODEINFO_H #define CANGJIE_USERCODEINFO_H #include #include #include "UserBase.h" namespace Cangjie { class UserCodeInfo : public UserBase { public: UserCodeInfo() = default; ~UserCodeInfo() override { OutputResult(); } static UserCodeInfo& Instance() { static UserCodeInfo single{}; return single; } void RecordInfo(const std::string& item, int64_t value); private: std::string GetJson() const override; std::string GetSuffix() const final { return ".info.prof"; } std::list> codeInfo; }; } // namespace Cangjie #endif // CANGJIE_USERCODEINFO_H cangjie_compiler-1.0.7/src/Utils/UserMemoryUsage.cpp000066400000000000000000000102111510705540100224710ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "UserMemoryUsage.h" #include #include #include #include #include #if defined(_WIN32) && defined(__MINGW64__) #include #include #endif #ifdef __APPLE__ #include #include #endif #include "cangjie/Basic/Color.h" #include "cangjie/Utils/CheckUtils.h" #if defined(__linux__) || (defined(_WIN32) && defined(__MINGW64__)) static const int PAGE_SIZE = 4; #endif static const int DISPLAY_PRECISION = 2; static const int KILOBYTE = 1024; namespace Cangjie { std::string UserMemoryUsage::GetJson() const { std::ostringstream out; out << "{"; out << std::fixed; out.precision(DISPLAY_PRECISION); for (auto& phrase : titleOrder) { out << "\n \"" + phrase + "\": {"; auto it = titleInfoMap.find(phrase); CJC_ASSERT(it != titleInfoMap.end()); for (auto& sec : it->second) { out << "\n \"" + sec.subtitle + "\": " << sec.end << ","; } if (!it->second.empty()) { // remove last ',' out.seekp(-1, std::ios_base::cur); } out << " \n },"; } if (!titleOrder.empty()) { out.seekp(-1, std::ios_base::cur); } out << "\n}\n"; return out.str(); } void UserMemoryUsage::Start(const std::string& title, const std::string& subtitle, const std::string& desc) { auto infos = titleInfoMap.find(title); if (std::count(titleOrder.begin(), titleOrder.end(), title) == 0) { titleOrder.push_back(title); } if (infos != titleInfoMap.end()) { auto it = std::find_if(infos->second.begin(), infos->second.end(), [&subtitle](const Info& info) { return info.subtitle == subtitle; }); if (it != infos->second.end()) { // overwrite existing info it->start = Sampling(); return; } } titleInfoMap[title].emplace_back(Info(title, subtitle, desc, Sampling())); } void UserMemoryUsage::Stop(const std::string& title, const std::string& subtitle, const std::string& /* desc */) { auto infos = titleInfoMap.find(title); if (infos == titleInfoMap.end()) { return; } auto it = std::find_if(infos->second.begin(), infos->second.end(), [&subtitle](const Info& info) { return info.subtitle == subtitle; }); if (it != infos->second.end()) { it->end = Sampling(); } } // Memory Size Unit: MB float UserMemoryUsage::Sampling() { #ifdef __linux__ std::ifstream mem("/proc/self/statm"); long totalProgramSize = 0; long residentSetSize = 0; mem >> totalProgramSize >> residentSetSize; // `residentSetSize` means page size (usually 4 KB one page on x86 and x86_64 systems) return static_cast(residentSetSize) * PAGE_SIZE / KILOBYTE; #elif defined(_WIN32) && defined(__MINGW64__) HANDLE hProcess; PROCESS_MEMORY_COUNTERS pmc; auto curPid = GetCurrentProcessId(); // obtain the process handle. hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, curPid); CJC_ASSERT(hProcess && "Get process handle failed"); if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) { return float(pmc.WorkingSetSize) / KILOBYTE / KILOBYTE; } else { CJC_ASSERT(false && "Get process memory info failed."); } CloseHandle(hProcess); return 0.0f; #elif defined(__APPLE__) task_basic_info_data_t info; mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT; if (task_info(mach_task_self(), TASK_BASIC_INFO, reinterpret_cast(&info), &count) == KERN_SUCCESS) { return float(info.resident_size) / KILOBYTE / KILOBYTE; } else { CJC_ASSERT(false && "Get process memory info failed."); } return 0.0f; #else // Other platforms need to be adapted. CJC_ASSERT(false && "Not support for current platform."); return 0.0f; #endif } } // namespace Cangjie cangjie_compiler-1.0.7/src/Utils/UserMemoryUsage.h000066400000000000000000000032631510705540100221470ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_USERMEMORYUSAGE_H #define CANGJIE_USERMEMORYUSAGE_H #include #include #include #include "UserBase.h" namespace Cangjie { class UserMemoryUsage : public UserBase { public: UserMemoryUsage() = default; ~UserMemoryUsage() override { OutputResult(); } static UserMemoryUsage& Instance() { static UserMemoryUsage single{}; return single; } void Start(const std::string& title, const std::string& subtitle, const std::string& desc); void Stop(const std::string& title, const std::string& subtitle, const std::string& desc); private: std::string GetJson() const override; std::string GetSuffix() const final { return ".mem.prof"; } /** * @brief get current process(cjc)'s memory usage at callsite * * @return float */ static float Sampling(); struct Info { std::string title; std::string subtitle; std::string desc; float start{0.}; float end{0.}; explicit Info(std::string title, std::string subtitle, std::string desc, float start) : title(std::move(title)), subtitle(std::move(subtitle)), desc(std::move(desc)), start(start) { } }; std::vector titleOrder; std::unordered_map> titleInfoMap; }; } // namespace Cangjie #endif // CANGJIE_USERMEMORYUSAGE_H cangjie_compiler-1.0.7/src/Utils/UserTimer.cpp000066400000000000000000000066731510705540100213350ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "UserTimer.h" #include #include #include "cangjie/Utils/CheckUtils.h" namespace Cangjie { std::string UserTimer::GetJson() const { auto [result, _] = GetDataAndOrder(); std::string output; output += "{"; for (auto& phrase : result) { output += ("\n \"" + phrase.first + "\": {"); for (auto& sec : phrase.second) { output += ("\n \"" + sec.first + "\": " + std::to_string(sec.second) + ","); } if (!phrase.second.empty()) { output.pop_back(); } output += " \n },"; } if (!result.empty()) { output.pop_back(); } output += "\n}\n"; return output; } void UserTimer::Start(const std::string& title, const std::string& subtitle, const std::string& desc) { auto infoIt = std::find_if(infoList.begin(), infoList.end(), [&title, &subtitle, &desc](const Info& it) { return it.title == title && it.subtitle == subtitle && it.desc == desc; }); // The first time, create a new Info. if (infoIt == infoList.end()) { infoList.emplace_back(Info(title, subtitle, desc)); return; } // Not the first time, must be done, reset the status. if ((*infoIt).isDone) { #if defined(__APPLE__) || defined(__MINGW64__) (*infoIt).start = std::chrono::system_clock::now(); #else (*infoIt).start = std::chrono::high_resolution_clock::now(); #endif (*infoIt).isDone = false; // It's still not over. } } void UserTimer::Stop(const std::string& title, const std::string& subtitle, const std::string& desc) { auto infoIt = std::find_if(infoList.begin(), infoList.end(), [&title, &subtitle, &desc](const Info& it) { return it.title == title && it.subtitle == subtitle && it.desc == desc; }); CJC_ASSERT(infoIt != infoList.end() && !(*infoIt).isDone); #if defined(__APPLE__) || defined(__MINGW64__) (*infoIt).end = std::chrono::system_clock::now(); #else (*infoIt).end = std::chrono::high_resolution_clock::now(); #endif // The costMs has an initial value. Therefore, the costMs can be correctly processed regardless of whether merge // is required. (*infoIt).costMs = (*infoIt).costMs + ((*infoIt).end - (*infoIt).start); (*infoIt).isDone = true; } std::pair> UserTimer::GetDataAndOrder() const { ResultDataType result; std::vector order; // Record the order of inserting keys in the map. for (const auto& info : std::as_const(infoList)) { if (!info.isDone) { #ifdef _WIN32 printf("[ %s ] only has beginning time, does not have ending time\n", info.title.c_str()); #else printf("[ \033[31;1m%s\033[0m ] only has beginning time, does not have ending time\n", info.title.c_str()); #endif continue; } std::vector> ele; auto eleRet = result.emplace(info.title, ele); if (eleRet.second) { order.emplace_back(info.title); } eleRet.first->second.emplace_back(info.subtitle, static_cast(info.costMs.count())); } return std::make_pair(result, order); } } // namespace Cangjie cangjie_compiler-1.0.7/src/Utils/UserTimer.h000066400000000000000000000037331510705540100207740ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #ifndef CANGJIE_USERTIMER_H #define CANGJIE_USERTIMER_H #include #include #include #include #include #include "UserBase.h" namespace Cangjie { class UserTimer : public UserBase { public: UserTimer() = default; ~UserTimer() override { OutputResult(); } static UserTimer& Instance() { static UserTimer single{}; return single; } void Start(const std::string& title, const std::string& subtitle, const std::string& desc); void Stop(const std::string& title, const std::string& subtitle, const std::string& desc); private: using ResultDataType = std::unordered_map>>; struct Info { std::string title; std::string subtitle; std::string desc; std::chrono::system_clock::time_point start; std::chrono::system_clock::time_point end; std::chrono::duration costMs{}; bool isDone = false; explicit Info() = default; explicit Info(std::string title, std::string subtitle, std::string desc) : title(std::move(title)), subtitle(std::move(subtitle)), desc(std::move(desc)) { #if defined(__APPLE__) || defined(__MINGW64__) this->start = std::chrono::system_clock::now(); #else this->start = std::chrono::high_resolution_clock::now(); #endif } }; std::pair> GetDataAndOrder() const; std::string GetJson() const override; std::string GetSuffix() const final { return ".time.prof"; } std::list infoList; }; } // namespace Cangjie #endif // CANGJIE_USERTIMER_H cangjie_compiler-1.0.7/src/Utils/Utils.cpp000066400000000000000000000161711510705540100205100ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file implements some utility functions. */ #ifdef __MINGW64__ #define _CRT_RAND_S // for secure random number generation on Windows. #endif #include "cangjie/Utils/Utils.h" #include #include #include #include #include "cangjie/AST/Node.h" #include "cangjie/Parse/Parser.h" #include "cangjie/Utils/StdUtils.h" #ifdef __WIN32 #include // should be at the end to avoid same macro define #endif namespace Cangjie::Utils { using namespace AST; namespace { const std::map NAMES_TO_OVERFLOW_STRATEGY = { {"no", OverflowStrategy::NA}, {"checked", OverflowStrategy::CHECKED}, {"wrapping", OverflowStrategy::WRAPPING}, {"throwing", OverflowStrategy::THROWING}, {"saturating", OverflowStrategy::SATURATING}, }; const std::map OVERFLOW_STRATEGY_TO_NAMES = { {OverflowStrategy::NA, "no"}, {OverflowStrategy::CHECKED, "checked"}, {OverflowStrategy::WRAPPING, "wrapping"}, {OverflowStrategy::THROWING, "throwing"}, {OverflowStrategy::SATURATING, "saturating"}, }; } // namespace OverflowStrategy StringToOverflowStrategy(const std::string& name) { auto found = NAMES_TO_OVERFLOW_STRATEGY.find(name); if (found == NAMES_TO_OVERFLOW_STRATEGY.end()) { return OverflowStrategy::NA; } return found->second; } bool ValidOverflowStrategy(const std::string& name) { return NAMES_TO_OVERFLOW_STRATEGY.count(name) != 0; } std::string OverflowStrategyName(const OverflowStrategy& overflowStrategy) { auto found = OVERFLOW_STRATEGY_TO_NAMES.find(overflowStrategy); CJC_ASSERT(found != OVERFLOW_STRATEGY_TO_NAMES.end()); return found->second; } std::string GenerateRandomHexString() { #ifdef _WIN32 unsigned int randomInt = 0; rand_s(&randomInt); std::stringstream stream; stream << std::hex << static_cast(randomInt); return stream.str(); #else int randomInt = 0; int fd = open("/dev/urandom", O_RDONLY); if (fd > 0) { (void)read(fd, &randomInt, sizeof(int)); } (void)close(fd); std::stringstream stream; stream << std::hex << static_cast(randomInt); return stream.str(); #endif } std::optional TryParseInt(const std::string& str) { if (str.empty()) { return std::nullopt; } int res = 0; bool isValid = std::all_of(str.begin(), str.end(), [](auto c) { return std::isdigit(c); }); if (isValid) { if (auto r = Stoi(str)) { res = *r; } else { isValid = false; } } if (!isValid) { return std::nullopt; } return {res}; } /* Get Mangled name for wrapper function of macro. */ std::string GetMacroFuncName(const std::string& fullPackageName, bool isAttr, const std::string& ident) { const std::string prefixForAttrMacro = "macroCall_a_"; const std::string prefixForPlainMacro = "macroCall_c_"; auto macroFuncName = (isAttr ? prefixForAttrMacro : prefixForPlainMacro) + ident + "_" + fullPackageName; /* '.' is not allowed in cangjie function name, so replace '.' to '_' */ std::replace(macroFuncName.begin(), macroFuncName.end(), '.', '_'); return macroFuncName; } std::vector StringifyArgumentVector(int argc, const char** argv) { std::vector args; // Convert all arguments to strings for (int i = 0; i < argc; ++i) { if (!argv[i]) { continue; } args.emplace_back(argv[i]); } return args; } std::unordered_map StringifyEnvironmentPointer(const char** envp) { std::unordered_map environmentVars; if (!envp) { return environmentVars; } // Read all environment variables for (int i = 0;; i++) { if (!envp[i]) { break; } std::string item(envp[i]); if (auto pos = item.find('='); pos != std::string::npos) { auto key = item.substr(0, pos); #ifdef _WIN32 // Environment variable names in Windows is case-insensitive, so they need to be unified to all UPPERCASE. std::transform(key.begin(), key.end(), key.begin(), [](unsigned char c) { return std::toupper(c); }); #endif environmentVars.emplace(key, item.substr(pos + 1)); }; } return environmentVars; } static std::vector GetPathsFromEnvironmentVars( const std::unordered_map& environmentVars) { const std::string pathLit = "PATH"; std::vector searchPaths; if (environmentVars.find(pathLit) != environmentVars.end()) { searchPaths = Cangjie::FileUtil::SplitEnvironmentPaths(environmentVars.at(pathLit)); } return searchPaths; } std::string GetRootPackageName(const std::string& fullPackageName) { if (fullPackageName.empty()) { return "default"; } if (std::count(fullPackageName.begin(), fullPackageName.end(), '`') % 2 != 0) { // 2 means '`' should be pairs. return ""; } if (fullPackageName[0] == '`') { size_t secondPos = fullPackageName.find('`', 1); return fullPackageName.substr(0, secondPos + 1); } if (auto dotPos = fullPackageName.find("."); dotPos != std::string::npos) { return fullPackageName.substr(0, dotPos + 1); } return fullPackageName; } #ifdef _WIN32 std::optional GetApplicationPath() { char buffer[MAX_PATH]; DWORD ret = GetModuleFileNameA(NULL, buffer, MAX_PATH); if (ret == 0) { Cangjie::Errorf("Get path of cjc.exe failed.\n"); return std::nullopt; } return {std::string(buffer)}; } #else std::optional GetApplicationPath( const std::string& argv0, const std::unordered_map& environmentVars) { auto maybeExePath = Cangjie::FileUtil::GetAbsPath( Cangjie::FileUtil::FindProgramByName(argv0, GetPathsFromEnvironmentVars(environmentVars))); if (!maybeExePath.has_value()) { Cangjie::Errorf("Get path of %s failed.\n", argv0.c_str()); return std::nullopt; } std::string exePath = maybeExePath.value(); const std::string environmentPathsSpecialCharacters = ":;"; // environmentPathsSpecialCharacters (for example, `:` or `;`) has special meaning in LD_LIBRARY_PATH. // To be able to call tools (to be specific, opt and llc) that require setting of LD_LIBRARY_PATH, appearance // of these special characters in cjc installation path will cause problems. if (exePath.find_first_of(environmentPathsSpecialCharacters) != std::string::npos) { Cangjie::Errorf("Invalid cjc installation path: %s\n", exePath.c_str()); Cangjie::Infof("Do not install `cjc` under a path that contains the following characters: %s\n", environmentPathsSpecialCharacters.c_str()); return std::nullopt; } return {exePath}; } #endif } // namespacecangjie_compiler-1.0.7/src/main-chir-dis.cpp000066400000000000000000000120301510705540100207220ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file is the main entry of chir-dis. */ #include #include #include #include "cangjie/Basic/Print.h" #include "cangjie/Basic/Version.h" #include "cangjie/CHIR/CHIRBuilder.h" #include "cangjie/CHIR/CHIRPrinter.h" #include "cangjie/CHIR/Serializer/CHIRDeserializer.h" #include "cangjie/Utils/FileUtil.h" #include "cangjie/Utils/Signal.h" #include "cangjie/Utils/Utils.h" using namespace Cangjie; namespace { const int EXIT_CODE_SUCCESS = 0; const int EXIT_CODE_ERROR = 1; // Normal compiler error const std::string_view CHIR_DIS_USAGE = R"(A tool used to deserialize and dump CHIR. Overview: chir-dis xxx.chir -> xxx.chirtxt Usage: chir-dis [option] file Options: -v print compiler version information. -h print this help.)"; struct ActionInfo { bool printHelp{false}; bool printVersion{false}; std::string inputFilePath{}; }; void RegisterSignalHandler() { #if (defined RELEASE) && (!defined CANGJIE_ENABLE_CJCFUZZ) #if (defined __unix__) CreateAltSignalStack(); #elif (defined _WIN32) RegisterCrashExceptionHandler(); #endif RegisterCrashSignalHandler(); #endif RegisterCrtlCSignalHandler(); } std::string GetOptionName(const std::string& arg) { size_t pos = arg.find_first_of('='); if (pos != std::string::npos) { return arg.substr(0, pos); } return arg; } bool ParseArgs(const std::vector& args, ActionInfo& info) { const std::string_view helpOption = "-h"; const std::string_view versionOption = "-v"; bool multiInput{false}; if (args.size() == 1) { Errorln("expected one serialization file of CHIR."); Println(CHIR_DIS_USAGE); return false; } for (size_t i = 1; i < args.size(); i++) { if (args[i] == helpOption) { info.printHelp = true; continue; } if (args[i] == versionOption) { info.printVersion = true; continue; } if (args[i].front() == '-') { Errorln("invalid option: '", GetOptionName(args[i]), "'"); Println(CHIR_DIS_USAGE); return false; } if (!info.inputFilePath.empty()) { multiInput = true; continue; } info.inputFilePath = args[i]; } if (info.printHelp) { return true; } if (multiInput) { Errorln("Only one file can be entered at a time"); Println(CHIR_DIS_USAGE); return false; } return true; } bool DeserializeInputCHIR(const std::string& path) { auto access = FileUtil::AccessWithResult(".", FileUtil::FileMode::FM_WRITE); switch (access) { case FileUtil::AccessResultType::NOT_EXIST: CJC_ABORT(); return false; case FileUtil::AccessResultType::NO_PERMISSION: Errorln("can't access current directory to write .chirtxt due no permisson"); return false; case FileUtil::AccessResultType::FAILED_WITH_UNKNOWN_REASON: Errorln("can't access current directory to write .chirtxt for unknow reason"); return false; case FileUtil::AccessResultType::OK: break; } std::unordered_map fileNameMap; CHIR::CHIRContext cctx(&fileNameMap); CHIR::CHIRBuilder chirBuilder(cctx); CHIR::ToCHIR::Phase phase; if (!CHIR::CHIRDeserializer::Deserialize(path, chirBuilder, phase)) { return false; } std::string outputFilePath = FileUtil::GetFileNameWithoutExtension(path) + CHIR_READABLE_FILE_EXTENSION; // print serialize extension info which just for serialization not necessary for chir nodes CHIR::CHIRPrinter::PrintCHIRSerializeInfo(phase, outputFilePath); // print package CHIR::CHIRPrinter::PrintPackage(*cctx.GetCurPackage(), outputFilePath); return true; } } // namespace int main(int argc, const char** argv) { try { RegisterSignalHandler(); auto args = Utils::StringifyArgumentVector(argc, argv); ActionInfo info; if (!ParseArgs(args, info)) { return EXIT_CODE_ERROR; } if (info.printHelp) { Println(CHIR_DIS_USAGE); return EXIT_CODE_SUCCESS; } if (info.printVersion) { Cangjie::PrintVersion(); return EXIT_CODE_SUCCESS; } if (!DeserializeInputCHIR(info.inputFilePath)) { return EXIT_CODE_ERROR; } #ifndef CANGJIE_ENABLE_GCOV } catch (const NullPointerException& nullPointerException) { Cangjie::ICE::TriggerPointSetter iceSetter(nullPointerException.GetTriggerPoint()); #else } catch (const std::exception& nullPointerException) { #endif InternalError("null pointer"); } return EXIT_CODE_SUCCESS; }cangjie_compiler-1.0.7/src/main-frontend.cpp000066400000000000000000000054351510705540100210520ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include #include #include #include namespace { std::string SetDoubleQuoted(const std::string& str) { std::stringstream ss; ss << "\""; for (char c : str) { // backslash cannot be used as escape character in Shell Command Language. To be able to // use double quote in a command, we generate backslash string and join them with // `"`. For example, ab"cd is transformed to "ab\"cd"; ab\cd is transformed to "ab"\\"cd". if (c == '"') { ss << "\\\""; } else if (c == '\\') { ss << "\"\\\\\""; } else { ss << c; } } ss << "\""; return ss.str(); } } // namespace /** * This simple program executes (exact*) the executing command with a different executable. * To be specific, the program starts `cjc.exe` to process the command, but without changes * the executable name, i.e. argv[0], to `cjc.exe`. If `cjc.exe` is started by this program, * when `cjc.exe` checks `argv[0]`, it would get the current program name, not `cjc.exe`. */ int main(int argc, const char** argv) { // Since we are using c++, there is no reason to handle unsafe char * instead of std::string. std::vector args; for (int i = 0; i < argc; ++i) { args.emplace_back(argv[i]); } // Retrive user command by concatenating all arguments. std::ostringstream oss; for (size_t i = 0; i < args.size(); ++i) { if (i != 0) { oss << " "; } oss << SetDoubleQuoted(args[i]); } std::string commandLine = oss.str(); // To keep the exact same behavior with symbolic link, we access the `cjc.exe` which located // in the same directory current program in. char buffer[MAX_PATH]; GetModuleFileName(NULL, buffer, MAX_PATH); std::string exePath = std::string(buffer); auto pos = exePath.find_last_of('\\'); if (pos == std::string::npos) { return 1; } std::string cjcPath = exePath.substr(0, pos) + "\\cjc.exe"; STARTUPINFOA si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); if (!CreateProcessA(cjcPath.c_str(), commandLine.data(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { return 1; } WaitForSingleObject(pi.hProcess, INFINITE); DWORD exit_code; if (FALSE == GetExitCodeProcess(pi.hProcess, &exit_code)) { return 1; } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return (int)exit_code; } cangjie_compiler-1.0.7/src/main-macrosrv.cpp000066400000000000000000000117251510705540100210660ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. #include "cangjie/Macro/InvokeUtil.h" #include "cangjie/Macro/MacroEvaluation.h" #include #include #include #include #include #ifdef __WIN32 #include #else #include #include #include #endif using namespace Cangjie; namespace { #if defined(__linux__) || defined(__APPLE__) #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND const size_t ARGS_NUM = 6; #endif #else const size_t ARGS_NUM = 5; #endif const size_t IDX_OF_READ_HANDLE = 1; const size_t IDX_OF_WRITE_HANDLE = 2; const size_t IDX_OF_ENABLE_PARA = 3; const size_t IDX_OF_CJC_FOLDER = 4; const size_t IDX_OF_PPID = 5; #if defined(__linux__) || defined(__APPLE__) const unsigned int CHECK_INTERVAL = 2; static void MonitoringParentProcess(pid_t pid) { while (true) { sleep(CHECK_INTERVAL); if (kill(pid, 0) != 0) { perror("parent process not exits"); [[maybe_unused]] std::lock_guard lg(MacroProcMsger::GetInstance().mutex); MacroProcMsger::GetInstance().CloseClientResource(); RuntimeInit::GetInstance().CloseRuntime(); exit(1); } } } #endif bool IsNumber(const std::string& str) { for (char c : str) { if (!std::isdigit(c)) { return false; } } return true; } bool IsArgsValid(const std::vector& args) { if (args.size() != ARGS_NUM) { Errorln( "Macro srv: Incorrect number of args, " + std::to_string(args.size()) + " : " + std::to_string(ARGS_NUM)); return false; } if (!IsNumber(args[IDX_OF_READ_HANDLE])) { Errorln("Macro srv: Arg of read handle is not number"); return false; } if (!IsNumber(args[IDX_OF_WRITE_HANDLE])) { Errorln("Macro srv: Arg of write handle is not number"); return false; } if (args[IDX_OF_CJC_FOLDER].empty()) { Errorln("Macro srv: Arg of cjc folder is empty"); return false; } return true; } #ifdef __WIN32 bool CheckPipe(HANDLE read, HANDLE write) { if (GetNamedPipeInfo(read, nullptr, nullptr, nullptr, nullptr) == FALSE) { Errorln("Macro srv: Read handle is not available"); return false; } if (GetNamedPipeInfo(write, nullptr, nullptr, nullptr, nullptr) == FALSE) { CloseHandle(read); Errorln("Macro srv: Write handle is not available"); return false; } return true; } #else bool IsPipe(int fd) { struct stat status; fstat(fd, &status); return S_ISFIFO(status.st_mode); } bool CheckPipe(int read, int write) { if (!IsPipe(read)) { Errorln("Macro srv: Read pipe is not available"); return false; } if (!IsPipe(write)) { close(read); Errorln("Macro srv: Write pipe is not available"); return false; } return true; } #endif } // namespace int main(int argc, const char* argv[], [[maybe_unused]] const char** envp) { auto args = Utils::StringifyArgumentVector(argc, argv); if (!IsArgsValid(args)) { return -1; } #ifdef _WIN32 HANDLE hRead = reinterpret_cast(atoi(args[IDX_OF_READ_HANDLE].c_str())); HANDLE hWrite = reinterpret_cast(atoi(args[IDX_OF_WRITE_HANDLE].c_str())); #else int hRead = stoi(args[IDX_OF_READ_HANDLE]); int hWrite = stoi(args[IDX_OF_WRITE_HANDLE]); int ppid = stoi(args[IDX_OF_PPID]); std::thread w(MonitoringParentProcess, ppid); w.detach(); #endif if (!CheckPipe(hRead, hWrite)) { return -1; } GlobalOptions gpt; // cjc folder to find runtime for lsp not in sdk std::string cjcFolder = args[IDX_OF_CJC_FOLDER]; if (cjcFolder.back() == '"') { cjcFolder.back() = '\\'; } gpt.executablePath = cjcFolder; gpt.enableParallelMacro = args[IDX_OF_ENABLE_PARA] == "1" ? true : false; DiagnosticEngine diag; CompilerInvocation compilerInvocation; compilerInvocation.globalOptions = gpt; CompilerInstance ci(compilerInvocation, diag); MacroCollector macroCollector; MacroEvaluation evaluator(&ci, ¯oCollector, false); { [[maybe_unused]] std::lock_guard lg(MacroProcMsger::GetInstance().mutex); MacroProcMsger::GetInstance().SetSrvPipeHandle(hRead, hWrite); } #ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND RuntimeInit::GetInstance().InitRuntime( ci.invocation.GetRuntimeLibPath(), ci.invocation.globalOptions.environment.allVariables); #endif evaluator.ExecuteEvalSrvTask(); RuntimeInit::GetInstance().CloseRuntime(); #ifdef _WIN32 CloseHandle(hRead); CloseHandle(hWrite); #else [[maybe_unused]] std::lock_guard lg(MacroProcMsger::GetInstance().mutex); MacroProcMsger::GetInstance().CloseClientResource(); #endif return 0; } cangjie_compiler-1.0.7/src/main.cpp000066400000000000000000000073471510705540100172410ustar00rootroot00000000000000// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. // This source file is part of the Cangjie project, licensed under Apache-2.0 // with Runtime Library Exception. // // See https://cangjie-lang.cn/pages/LICENSE for license information. /** * @file * * This file is the main entry of compiler. */ #include "cangjie/Basic/DiagnosticEngine.h" #include "cangjie/Driver/Driver.h" #include "cangjie/Driver/TempFileManager.h" #include "cangjie/FrontendTool/FrontendTool.h" #include "cangjie/Utils/FileUtil.h" #include #include #include #ifdef _WIN32 #include #include #endif #include "cangjie/Basic/Print.h" #include "cangjie/Utils/Signal.h" namespace { #if (defined RELEASE) void RegisterSignalHandler() { #if (defined __unix__) Cangjie::CreateAltSignalStack(); #elif (defined _WIN32) Cangjie::RegisterCrashExceptionHandler(); #endif Cangjie::RegisterCrashSignalHandler(); } #endif const int EXIT_CODE_SUCCESS = 0; const int EXIT_CODE_ERROR = 1; // Normal compiler error } // namespace using namespace Cangjie; int main(int argc, const char** argv, const char** envp) { try { #if (defined RELEASE) RegisterSignalHandler(); #endif RegisterCrtlCSignalHandler(); // Convert all arguments to string list. std::vector args = Utils::StringifyArgumentVector(argc, argv); std::unordered_map environmentVars = Utils::StringifyEnvironmentPointer(envp); SourceManager sm; DiagnosticEngine diag; diag.SetSourceManager(&sm); #ifdef _WIN32 auto maybeExePath = Utils::GetApplicationPath(); #else auto maybeExePath = Utils::GetApplicationPath(args[0], environmentVars); #endif if (!maybeExePath.has_value()) { return EXIT_CODE_ERROR; } std::string exePath = maybeExePath.value(); std::string exeName = FileUtil::GetFileName(args[0]); // The program is executed by the symbolic link `cjc-frontend`. Run in Frontend mode instead of Driver mode. if (exeName == "cjc-frontend" || exeName == "cjc-frontend.exe") { auto ret = ExecuteFrontend(exePath, args, environmentVars); RuntimeInit::GetInstance().CloseRuntime(); TempFileManager::Instance().DeleteTempFiles(); return ret; } #ifdef SIGNAL_TEST // The interrupt signal triggers the function. In normal cases, this function does not take effect. Cangjie::SignalTest::ExecuteSignalTestCallbackFunc(Cangjie::SignalTest::TriggerPointer::MAIN_POINTER); #endif std::unique_ptr driver = std::make_unique(args, diag, exePath); driver->EnvironmentSetup(environmentVars); if (!driver->ParseArgs()) { // Driver should have printed error messages, // but if driver didn't, users may be confused since cjc did neither compilation // nor error reporting. Therefore, we add an error message (and also a help message) here. WriteError("Invalid options. Try: 'cjc --help' for more information.\n"); return EXIT_CODE_ERROR; } auto res = driver->ExecuteCompilation(); TempFileManager::Instance().DeleteTempFiles(); if (!res) { RuntimeInit::GetInstance().CloseRuntime(); return EXIT_CODE_ERROR; } RuntimeInit::GetInstance().CloseRuntime(); #ifndef CANGJIE_ENABLE_GCOV } catch (const NullPointerException& nullPointerException) { Cangjie::ICE::TriggerPointSetter iceSetter(nullPointerException.GetTriggerPoint()); #else } catch (const std::exception& nullPointerException) { #endif InternalError("null pointer"); } return EXIT_CODE_SUCCESS; } cangjie_compiler-1.0.7/third_party/000077500000000000000000000000001510705540100173405ustar00rootroot00000000000000cangjie_compiler-1.0.7/third_party/CMakeLists.txt000066400000000000000000000610661510705540100221110ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. include(ExternalProject) find_program(SSH_EXECUTABLE ssh) # Don't check known_host to get rid of interactive operations in cmake if(CMAKE_HOST_WIN32) set(GIT_ARGS "ssh.variant=ssh") else() set(GIT_ARGS "core.sshCommand=${SSH_EXECUTABLE}\\ -o\\ StrictHostKeyChecking=no" "ssh.variant=ssh") endif() set(THIRD_PARTY_LLVM "third_party/llvm") set(LLVM_TAG master) set(RUNTIME_TAG main) set(GCC_TOOLCHAIN_FLAG "") if(MINGW) # There will be lots of warnings when compiling backend with MinGW toolchain, temporarily suppress them. set(GCC_TOOLCHAIN_FLAG "${GCC_TOOLCHAIN_FLAG} -w") endif() if(BUILD_GCC_TOOLCHAIN) message(STATUS "Set GCC toolchain: ${BUILD_GCC_TOOLCHAIN}") set(GCC_TOOLCHAIN_FLAG "${GCC_TOOLCHAIN_FLAG} --gcc-toolchain=\"${BUILD_GCC_TOOLCHAIN}\"") endif() # Handle third-party dependencies list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") include(Flatbuffer) if(CANGJIE_BUILD_CJDB) include(Xml2) endif() set(LLVM_LIB_SUFFIX .a) set(LLVM_LIB_NAMES LLVMLinker LLVMIRReader LLVMAsmParser LLVMTransformUtils LLVMBitWriter LLVMAnalysis LLVMProfileData LLVMDebugInfoDWARF LLVMDebugInfoMSF LLVMObject LLVMTextAPI LLVMMCParser LLVMMC LLVMDebugInfoCodeView LLVMBitReader LLVMCore LLVMRemarks LLVMBitstreamReader LLVMBinaryFormat LLVMSupport LLVMDemangle) list(TRANSFORM LLVM_LIB_NAMES PREPEND "lib") list(TRANSFORM LLVM_LIB_NAMES APPEND "${LLVM_LIB_SUFFIX}") set(LLVM_DEP_LIB_NAMES LLVMBinaryFormat LLVMRemarks LLVMBitstreamReader LLVMSupport LLVMDemangle) list(TRANSFORM LLVM_DEP_LIB_NAMES PREPEND "lib") list(TRANSFORM LLVM_DEP_LIB_NAMES APPEND "${LLVM_LIB_SUFFIX}") set(LLVM_LINK_FLAGS -lpthread -lm) if(CANGJIE_BUILD_CJDB AND NOT WIN32) list(APPEND LLVM_LINK_FLAGS -lncurses) if(NOT DARWIN) list(APPEND LLVM_LINK_FLAGS -ltinfo) endif() endif() set(LLVM_INSTALL_PATTERN PATTERN "include" EXCLUDE PATTERN "libexec" EXCLUDE PATTERN "share" EXCLUDE PATTERN "cmake" EXCLUDE PATTERN "VERSION.txt" EXCLUDE PATTERN "llvm-config" EXCLUDE PATTERN "lib/lib*.a" EXCLUDE PATTERN "lib/liblldbIntelFeatures*" EXCLUDE PATTERN "lib/linux" EXCLUDE PATTERN "bin" EXCLUDE ) # Only the following binaries are needed. The other unused llvm binaries will not be installed. set(LLVM_BIN_INSTALL_PATTERN # For cjc general compilation REGEX "bin/(opt|llc)" PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ # For cjc general compilation (e.g. OHOS) and LTO and lldb related binaries REGEX "bin/(lld|ld64.lld|ld.lld|lld-link)" PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ # For cjc general compilation (e.g. OHOS) REGEX "bin/(llvm-ar)" PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ # For LTO REGEX "bin/(llvm-link|llvm-lto|llvm-lto2)" PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ # For cjc --coverage feature REGEX "bin/(llvm-cov)" PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ # For PGO REGEX "bin/(llvm-profdata|llvm-profgen)" PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ # For compiler developer debugging REGEX "bin/(llvm-as|llvm-dis)" PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ REGEX "bin/(lli)" PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ REGEX "bin/(llvm-addr2line|llvm-symbolizer)" PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ REGEX "bin/(llvm-otool|llvm-objdump)" PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ REGEX "bin/(llvm-objcopy)" PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ # For Windows REGEX "bin/(libclang\\.dll|libclang-cpp\\.dll)" REGEX "bin/(libgcc_s_seh-1\\.dll|libssp-0\\.dll|libstdc\\+\\+-6\\.dll|libwinpthread-1\\.dll)" REGEX "bin/(libLTO\\.dll|libLLVM-15\\.dll|libLLVM-Foundation-15\\.dll)" ) set(LIB_PROFILE profile) set(LIB_FUZZER_NOMAIN fuzzer_no_main) set(LIB_PROFILE_NAME libclang_rt-${LIB_PROFILE}.a) if(MACOS) set(LIB_BUILTINS osx) set(LIB_BUILTINS_NAME libclang_rt.osx.a) elseif(IOS) if(IOS_PLATFORM MATCHES "SIMULATOR") set(LIB_BUILTINS iossim) set(LIB_BUILTINS_NAME libclang_rt.iossim.a) set(LIB_PROFILE profile_iossim) set(LIB_PROFILE_NAME libclang_rt.profile_iossim.a) else() set(LIB_BUILTINS ios) set(LIB_BUILTINS_NAME libclang_rt.ios.a) set(LIB_PROFILE profile_ios) set(LIB_PROFILE_NAME libclang_rt.profile_ios.a) endif() else() set(LIB_BUILTINS builtins) set(LIB_BUILTINS_NAME libclang_rt-${LIB_BUILTINS}.a) endif() # Keep the same name with standard libfuzzer: libclang_rt.fuzz_no_main.a set(LIB_FUZZER_NOMAIN_NAME libclang_rt.${LIB_FUZZER_NOMAIN}.a) set(CJNATIVE_BACKEND "cjnative") string(TOLOWER ${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND} output_lib_dir) install(DIRECTORY DESTINATION modules/${output_lib_dir}${SANITIZER_SUBPATH}) # binary if(CANGJIE_CODEGEN_CJNATIVE_BACKEND) # i.e. "--compile-backend" option include(CompileRTLib) if(CANGJIE_USE_OH_LLVM_REPO) set(REPOSITORY_PATH "https://gitcode.com/openharmony/third_party_llvm-project.git") set(LLVM_TAG master) else() set(REPOSITORY_PATH "https://gitcode.com/Cangjie/llvm-project.git") set(LLVM_TAG main) endif() # llvm message(STATUS "Set cjnative REPOSITORY_PATH: ${REPOSITORY_PATH}") if(CANGJIE_BUILD_CJC) set(LLVM_LIB_PATH ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/lib) set(LLVM_LIBS) set(LLVM_SANITIZER) if(CANGJIE_ENABLE_ASAN OR CANGJIE_ENABLE_ASAN_COV) set(LLVM_SANITIZER Address) endif() foreach(LIB_NAME ${LLVM_LIB_NAMES}) list(APPEND LLVM_LIBS ${LLVM_LIB_PATH}/${LIB_NAME}) endforeach() set(LLVM_PROJECTS lld|clang|compiler-rt) if(CANGJIE_ENABLE_ASAN OR CANGJIE_ENABLE_ASAN_COV) set(LLVM_PROJECTS lld) endif() set(LLVM_CMAKE_C_FLAGS ${GCC_TOOLCHAIN_FLAG}) set(LLVM_CMAKE_CXX_FLAGS ${GCC_TOOLCHAIN_FLAG}) if(MINGW) set(LLVM_CMAKE_C_FLAGS "${LLVM_CMAKE_C_FLAGS} --unwindlib=libunwind") set(LLVM_CMAKE_CXX_FLAGS "${LLVM_CMAKE_CXX_FLAGS} --unwindlib=libunwind") endif() set(LLVM_CMAKE_EXE_LINKER_FLAGS) set(LLVM_CMAKE_SHARED_LINKER_FLAGS) string(REPLACE ";" "|" CMAKE_PREFIX_PATH_ALT_SEP "${CMAKE_PREFIX_PATH}") set(LLVM_CMAKE_ARGS # Hide warnings during configure which are intended for LLVM developers. -Wno-dev -DLLVM_ENABLE_BACKTRACES:BOOL=OFF -DLLVM_ENABLE_PROJECTS=${LLVM_PROJECTS} # Disable `libear` generation -DLLVM_ENABLE_Z3_SOLVER=OFF -DCLANG_ENABLE_STATIC_ANALYZER=OFF -DCLANG_ENABLE_ARCMT=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_ZSTD=OFF # Build both targets in case of cross-compilation. -DLLVM_TARGETS_TO_BUILD=ARM|AArch64|X86 -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH_ALT_SEP} -DCOMPILER_RT_ENABLE_IOS=OFF # Propagate build type and compiler. # For now, we can only use release version -DCMAKE_BUILD_TYPE=${CANGJIE_LLVM_BUILD_TYPE} -DCMAKE_C_COMPILER=${LLVM_BUILD_C_COMPILER} -DCMAKE_CXX_COMPILER=${LLVM_BUILD_CXX_COMPILER} -DCMAKE_AR=${CMAKE_AR} -DCMAKE_RANLIB=${CMAKE_RANLIB} -DCMAKE_CXX_STANDARD=17) if(CANGJIE_BUILD_CJDB) if(NOT WIN32) list(APPEND LLVM_CMAKE_ARGS ${LLVM_BUILD_ARG} -DLLVM_ENABLE_TERMINFO=1 -DCURSES_NEED_NCURSES=1) endif() list(APPEND LLVM_CMAKE_ARGS -DLLVM_ENABLE_RTTI=ON) # When making debug build, LLDB is debug build but LLVM is still release build. LLDB calls some # dump() functions which exist in LLVM debug build only. To prevent undefined references errors, # LLVM_ENABLE_DUMP needs to be enabled in LLVM release build for LLDB debug build. if(CANGJIE_CJDB_BUILD_TYPE MATCHES "^Debug$") list(APPEND LLVM_CMAKE_ARGS -DLLVM_ENABLE_DUMP=ON) endif() else() list(APPEND LLVM_CMAKE_ARGS # Hide warnings during configure which are intended for LLVM developers. -Wno-dev # Bindings is disabled for two reasons. First, we simply do not need it. # Second, installation of bindings may require extra privilege on some environments, # which may cause building errors. -DLLVM_ENABLE_BINDINGS=OFF -DLLVM_ENABLE_TERMINFO=OFF -DLLVM_ENABLE_LIBEDIT=OFF -DLLVM_USE_SANITIZER=${LLVM_SANITIZER} -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH}) endif() list(APPEND LLVM_CMAKE_ARGS -DCMAKE_C_FLAGS=${LLVM_CMAKE_C_FLAGS} -DCMAKE_CXX_FLAGS=${LLVM_CMAKE_CXX_FLAGS}) foreach(LLVM_BUILD_ARG ${CANGJIE_LLVM_BUILD_ARGS}) list(APPEND LLVM_CMAKE_ARGS ${LLVM_BUILD_ARG}) endforeach() # Build `libLLVM.so` to link against. list(APPEND LLVM_CMAKE_ARGS -DLLVM_BUILD_LLVM_DYLIB=ON) list(APPEND LLVM_CMAKE_ARGS -DLLVM_LINK_LLVM_DYLIB=ON) if(MINGW) list(APPEND LLVM_CMAKE_ARGS -DLLVM_SPLIT_LLVM_DYLIB=ON) list(APPEND LLVM_CMAKE_ARGS -DCOMPILER_RT_USE_BUILTINS_LIBRARY=TRUE) list(APPEND LLVM_CMAKE_ARGS -DCOMPILER_RT_USE_LLVM_UNWINDER=TRUE) list(APPEND LLVM_CMAKE_ARGS -DSANITIZER_CXX_ABI=libc++) list(APPEND LLVM_CMAKE_ARGS -DCMAKE_SYSTEM_NAME=Windows) list(APPEND LLVM_CMAKE_ARGS -DLLVM_HOST_TRIPLE=x86_64-w64-mingw32) set(NATIVE_LLVM_CMAKE_ARGS -Wno-dev|-Wno-deprecated) list(APPEND LLVM_CMAKE_ARGS -DCROSS_TOOLCHAIN_FLAGS_LLVM_NATIVE=${NATIVE_LLVM_CMAKE_ARGS}) set(LLVM_CMAKE_EXE_LINKER_FLAGS "${LLVM_CMAKE_EXE_LINKER_FLAGS} -Wl,--no-insert-timestamp -lunwind") set(LLVM_CMAKE_SHARED_LINKER_FLAGS "${LLVM_CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-insert-timestamp -lunwind") endif() list(APPEND LLVM_CMAKE_ARGS -DCMAKE_EXE_LINKER_FLAGS=${LLVM_CMAKE_EXE_LINKER_FLAGS}) list(APPEND LLVM_CMAKE_ARGS -DCMAKE_SHARED_LINKER_FLAGS=${LLVM_CMAKE_SHARED_LINKER_FLAGS}) # ranlib / ar on Windows may fail to create temp files when multiple processes are invoked simultaneously, # so the maximum number of parallel linking jobs needs to be limit to 1. if(CMAKE_HOST_WIN32) list(APPEND LLVM_CMAKE_ARGS -DLLVM_PARALLEL_LINK_JOBS=1) endif() set(boundscheck_suffix ${CMAKE_SHARED_LIBRARY_SUFFIX}) set(boundscheck_target_suffix ${CMAKE_SHARED_LIBRARY_SUFFIX}) if(WIN32) set(boundscheck_suffix "-static.a") set(boundscheck_target_suffix ".a") endif() set(APPLY_LLVM_PATCH_COMMAND ${CMAKE_COMMAND} -E echo "") if(EXISTS ${CANGJIE_CJNATIVE_SOURCE_DIR}) if(CANGJIE_USE_OH_LLVM_REPO) set(APPLY_LLVM_PATCH_COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ApplyLLVMPatch.cmake" ${CANGJIE_CJNATIVE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/third_party/llvmPatch.diff) endif() ExternalProject_Add( cjnative SOURCE_DIR ${CANGJIE_CJNATIVE_SOURCE_DIR} BUILD_BYPRODUCTS ${LLVM_LIBS} SOURCE_SUBDIR llvm PATCH_COMMAND ${APPLY_LLVM_PATCH_COMMAND} COMMAND ${CMAKE_COMMAND} -E make_directory /utils/demangle COMMAND ${CMAKE_COMMAND} -E make_directory /utils/boundscheck/include COMMAND ${CMAKE_COMMAND} -E make_directory /utils/boundscheck/lib/${CMAKE_SYSTEM_PROCESSOR} COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/cangjie-demangler/include/CangjieDemangle.h /utils/demangle COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/cangjie-demangler/lib/libcangjie-demangle.a /utils/demangle COMMAND ${CMAKE_COMMAND} -E copy ${BOUNDSCHECK}/include/securec.h ${BOUNDSCHECK}/include/securectype.h /utils/boundscheck/include COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/lib/libboundscheck${boundscheck_suffix} /utils/boundscheck/lib/${CMAKE_SYSTEM_PROCESSOR}/libboundscheck${boundscheck_target_suffix} LIST_SEPARATOR | CMAKE_ARGS ${LLVM_CMAKE_ARGS} USES_TERMINAL_BUILD ON DEPENDS cangjie-demangler boundscheck) else() set(LLVM_REPO_DOWNLOAD_ARGS DOWNLOAD_COMMAND git clone -b ${LLVM_TAG} --depth=1 ${REPOSITORY_PATH} ) if(CANGJIE_USE_OH_LLVM_REPO) set(APPLY_LLVM_PATCH_COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ApplyLLVMPatch.cmake" ${CMAKE_SOURCE_DIR}/third_party/llvmPatch.diff) set(LLVM_REPO_DOWNLOAD_ARGS GIT_REPOSITORY ${REPOSITORY_PATH} GIT_TAG ${LLVM_TAG} GIT_PROGRESS ON GIT_CONFIG ${GIT_ARGS} GIT_SHALLOW OFF) endif() ExternalProject_Add( cjnative ${LLVM_REPO_DOWNLOAD_ARGS} BUILD_BYPRODUCTS ${LLVM_LIBS} SOURCE_SUBDIR llvm PATCH_COMMAND ${APPLY_LLVM_PATCH_COMMAND} COMMAND ${CMAKE_COMMAND} -E make_directory /utils/demangle COMMAND ${CMAKE_COMMAND} -E make_directory /utils/boundscheck/include COMMAND ${CMAKE_COMMAND} -E make_directory /utils/boundscheck/lib/${CMAKE_SYSTEM_PROCESSOR} COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/cangjie-demangler/include/CangjieDemangle.h /utils/demangle COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/cangjie-demangler/lib/libcangjie-demangle.a /utils/demangle COMMAND ${CMAKE_COMMAND} -E copy ${BOUNDSCHECK}/include/securec.h ${BOUNDSCHECK}/include/securectype.h /utils/boundscheck/include COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/lib/libboundscheck${boundscheck_suffix} /utils/boundscheck/lib/${CMAKE_SYSTEM_PROCESSOR}/libboundscheck${boundscheck_target_suffix} LIST_SEPARATOR | CMAKE_ARGS ${LLVM_CMAKE_ARGS} USES_TERMINAL_BUILD ON DEPENDS cangjie-demangler boundscheck) endif() ExternalProject_Get_Property(cjnative SOURCE_DIR) add_custom_command( TARGET cjnative POST_BUILD COMMAND ${CMAKE_COMMAND} -E $,rm,remove> -f ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/$,bin,lib>/libRemarks.* COMMENT "Removing unnecessary llvm libraries..." ) if(CANGJIE_BUILD_CJDB) include(BuildCJDB) endif() elseif (NOT CANGJIE_SKIP_BUILD_CLANG_RT) compile_rtlib(cjnative INSTALL_PREFIX ${CMAKE_BINARY_DIR}/third_party/llvm) ExternalProject_Get_Property(cjnative SOURCE_DIR) endif() if(NOT CANGJIE_SKIP_BUILD_CLANG_RT) # Set name and path of the profile library set(LIB_RTLIB_ROOT_PATH ${CMAKE_BINARY_DIR}/lib/) set(LIB_PROFILE_PATH ${LIB_RTLIB_ROOT_PATH}/${LIB_PROFILE_NAME}) set(LIB_BUILTINS_PATH ${LIB_RTLIB_ROOT_PATH}/${LIB_BUILTINS_NAME}) set(LIB_FUZZER_NOMAIN_PATH ${LIB_RTLIB_ROOT_PATH}/${LIB_FUZZER_NOMAIN_NAME}) if(NOT (CANGJIE_ENABLE_ASAN OR CANGJIE_ENABLE_ASAN_COV)) set(KEEP_CLANG_CPP_LIBS ${CANGJIE_BUILD_CJDB}) set(COMMAND_REMOVE_AFTER_EXTRACTION COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/RemoveByproductLib.cmake" ${CMAKE_BINARY_DIR} ${THIRD_PARTY_LLVM} ${KEEP_CLANG_CPP_LIBS}) endif() set(CLANG_RT_LIBS) # copy the builtin and profile library if(NOT OHOS) if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64" OR DARWIN) list(APPEND CLANG_RT_LIBS ${LIB_BUILTINS_PATH}) endif() list(APPEND CLANG_RT_LIBS ${LIB_PROFILE_PATH}) if(NOT WIN32 AND NOT IOS) list(APPEND CLANG_RT_LIBS ${LIB_FUZZER_NOMAIN_PATH}) endif() endif() set(CLANG_RT_TARGET_SUFFIX ${CMAKE_SYSTEM_PROCESSOR}$<$:-android>) if(CANGJIE_ASAN_SUPPORT) list(APPEND CLANG_RT_LIBS ${LIB_RTLIB_ROOT_PATH}/libclang_rt-asan.a) set(SANITIZER_COPY_CMD COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ExtractRtlib.cmake" ${CMAKE_BINARY_DIR} asan ${LIB_RTLIB_ROOT_PATH}/libclang_rt-asan.a ${CLANG_RT_TARGET_SUFFIX} ${CANGJIE_BUILD_CJC} ${THIRD_PARTY_LLVM} ${IOS}) elseif(CANGJIE_TSAN_SUPPORT) list(APPEND CLANG_RT_LIBS ${LIB_RTLIB_ROOT_PATH}/libclang_rt-tsan.a) set(SANITIZER_COPY_CMD COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ExtractRtlib.cmake" ${CMAKE_BINARY_DIR} tsan_cangjie ${LIB_RTLIB_ROOT_PATH}/libclang_rt-tsan.a ${CLANG_RT_TARGET_SUFFIX} ${CANGJIE_BUILD_CJC} ${THIRD_PARTY_LLVM} ${IOS}) elseif(CANGJIE_HWASAN_SUPPORT) list(APPEND CLANG_RT_LIBS ${LIB_RTLIB_ROOT_PATH}/libclang_rt-hwasan.a) set(SANITIZER_COPY_CMD COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ExtractRtlib.cmake" ${CMAKE_BINARY_DIR} hwasan ${LIB_RTLIB_ROOT_PATH}/libclang_rt-hwasan.a ${CMAKE_SYSTEM_PROCESSOR} ${CANGJIE_BUILD_CJC} ${THIRD_PARTY_LLVM} ${IOS}) endif() if(NOT WIN32 AND NOT IOS) set(FUZZER_NOMAIN_COPY_CMD COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ExtractRtlib.cmake" ${CMAKE_BINARY_DIR} ${LIB_FUZZER_NOMAIN} ${LIB_FUZZER_NOMAIN_PATH} ${CLANG_RT_TARGET_SUFFIX} ${CANGJIE_BUILD_CJC} ${THIRD_PARTY_LLVM} ${IOS}) endif() if(NOT CANGJIE_ENABLE_ASAN AND NOT CANGJIE_ENABLE_ASAN_COV) # Generate version info and extract the profile library add_custom_command( TARGET cjnative POST_BUILD COMMAND git log -1 --oneline --decorate > ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/VERSION.txt # The command `cmake -P ExtractRtlib.cmake` will run the `file` commands like `file(GLOB ...)` # at the building stage instead of the configuration stage. COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ExtractRtlib.cmake" ${CMAKE_BINARY_DIR} ${LIB_PROFILE} ${LIB_PROFILE_PATH} ${CLANG_RT_TARGET_SUFFIX} ${CANGJIE_BUILD_CJC} ${THIRD_PARTY_LLVM} ${IOS} COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ExtractRtlib.cmake" ${CMAKE_BINARY_DIR} ${LIB_BUILTINS} ${LIB_BUILTINS_PATH} ${CLANG_RT_TARGET_SUFFIX} ${CANGJIE_BUILD_CJC} ${THIRD_PARTY_LLVM} ${IOS} ${FUZZER_NOMAIN_COPY_CMD} ${SANITIZER_COPY_CMD} ${COMMAND_REMOVE_AFTER_EXTRACTION} # copy to runtime/lib for build-binary-tar COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/third_party/runtime/lib COMMAND ${CMAKE_COMMAND} -E copy ${CLANG_RT_LIBS} ${CMAKE_BINARY_DIR}/third_party/runtime/lib WORKING_DIRECTORY ${SOURCE_DIR}) else() # this condition is equals to disable asan rt-lib compile (i.e. NOT CANGJIE_ASAN_SUPPORT/CANGJIE_TSAN_SUPPORT), # so just don't add asan compile here # When compiling asan version, clang_rt-profile will not be generated even compile-rt # project is added. To be able to supply clang_rt-profile for --coverage support, we # have to compile clang-rt again without asan settings. compile_rtlib(cjnative-rtlib INSTALL_PREFIX ${CMAKE_BINARY_DIR}/third_party/llvm-rtlib) set(LIB_BUILTINS_COPY_COMMAND COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/third_party/llvm-rtlib/lib/*/libclang_rt.${LIB_BUILTINS}-${CMAKE_SYSTEM_PROCESSOR}.a ${LIB_BUILTINS_PATH} ) if(DARWIN) set(LIB_BUILTINS_COPY_COMMAND) endif() add_custom_command( TARGET cjnative-rtlib POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/third_party/llvm-rtlib/lib/*/libclang_rt.${LIB_PROFILE}-${CMAKE_SYSTEM_PROCESSOR}.a ${LIB_PROFILE_PATH} # The command `cmake -P ExtractRtlib.cmake` will run the `file` commands like `file(GLOB ...)` # at the building stage instead of the configuration stage. ${LIB_BUILTINS_COPY_COMMAND} COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/third_party/llvm-rtlib/lib/*/libclang_rt.${LIB_FUZZER_NOMAIN}-${CMAKE_SYSTEM_PROCESSOR}.a ${LIB_FUZZER_NOMAIN_PATH} ${COMMAND_REMOVE_AFTER_EXTRACTION} # copy to runtime/lib for build-binary-tar COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/third_party/runtime/lib COMMAND ${CMAKE_COMMAND} -E copy ${CLANG_RT_LIBS} ${CMAKE_BINARY_DIR}/third_party/runtime/lib WORKING_DIRECTORY ${SOURCE_DIR}) endif() if(CANGJIE_BUILD_CJC) install( DIRECTORY ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/ DESTINATION ${THIRD_PARTY_LLVM} ${LLVM_INSTALL_PATTERN}) install( DIRECTORY ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/bin DESTINATION ${THIRD_PARTY_LLVM} FILES_MATCHING ${LLVM_BIN_INSTALL_PATTERN} ) endif() install(FILES ${CLANG_RT_LIBS} DESTINATION lib/${output_lib_dir}${SANITIZER_SUBPATH}) set(LLVM_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/include PARENT_SCOPE) # for other targets to use foreach(LIB_NAME ${LLVM_DEP_LIB_NAMES}) list(APPEND LLVM_LIBS ${LLVM_LIB_PATH}/${LIB_NAME}) endforeach() list(APPEND LLVM_LIBS ${LLVM_LINK_FLAGS}) set(LLVM_LIBS ${LLVM_LIBS} PARENT_SCOPE) endif() if(MINGW) include(AddMinGWLibs) endif() if(OHOS) include(AddOHOSLibs) endif() set(BINARY_TARGET_DEPENDS runtime) if(NOT CANGJIE_SKIP_BUILD_CLANG_RT AND NOT OHOS) list(APPEND BINARY_TARGET_DEPENDS cjnative) endif() endif() cangjie_compiler-1.0.7/third_party/README.md000066400000000000000000000115171510705540100206240ustar00rootroot00000000000000# 使用的开源软件说明 ## libboundscheck ### 代码来源说明 该仓被编译器及周边组件源码依赖,仓库源码地址为 [third_party_bounds_checking_function](https://gitcode.com/openharmony/third_party_bounds_checking_function),版本为 [OpenHarmony-v6.0-Release](https://gitcode.com/openharmony/third_party_bounds_checking_function/tags/OpenHarmony-v6.0-Release)。 该开源软件被编译器及周边组件以包含头文件的方式使用,并通过链接库(动态库或静态库)的方式依赖。 ### 构建说明 本仓库通过 CMake 作为子目标项目引入,编译过程中会自动完成构建并建立依赖关系,具体构建参数请参见 [CMakeLists.txt](./cmake/CMakeLists.txt) 文件。 ## flatbuffers ### 代码来源说明 FlatBuffers 是一个高效的跨平台、跨语言序列化库,仓颉语言使用 FlatBuffers 库完成编译器数据到指定格式的序列化反序列化操作。 该开源软件被编译器及周边组件以包含头文件的方式使用,并通过链接库(动态库或静态库)的方式依赖。 ### 构建说明 本仓库通过 CMake 作为子目标项目引入,编译过程中会自动完成构建并建立依赖关系,具体构建参数请参见 [Flatbuffer.cmake](./cmake/Flatbuffer.cmake) 文件。 开发者也可以手动下载 [flatbuffers](https://gitcode.com/openharmony/third_party_flatbuffers.git) 源码,命令如下: ```shell mkdir -p third_party/flatbuffers cd third_party/flatbuffers git clone https://gitcode.com/openharmony/third_party_flatbuffers.git -b master ./ ``` 构建项目时,则直接使用 third_party/flatbuffers 目录源码进行构建。 ## LLVM ### 代码来源说明 LLVM 作为仓颉编译器后端,当前基于官方代码仓 [llvmorg-15.0.4](https://gitcode.com/openharmony/third_party_llvm-project)(对应commit 5c68a1cb123161b54b72ce90e7975d95a8eaf2a4)开源版本修改实现。为了便于代码管理以及支持鸿蒙版本构建,LLVM 在构建时来源有两种: - 来源于仓库 https://gitcode.com/Cangjie/llvm-project/ ,使用此代码仓为默认方式,便于日常代码开发、检视和管理。 - 来源于仓库 https://gitcode.com/openharmony/third_party_llvm-project (llvmorg-15.0.4 对应 commit hash),并外加 llvmPatch.diff 进行构建,此方式主要为 OpenHarmony 构建版本时采用。 llvmPatch.diff 基于 https://gitcode.com/Cangjie/llvm-project/ 仓库改动经过验证后生成,每个版本都会保证 patch 可用。需要注意的是,LLVM 版本升级需结合 OpenHarmony 社区开源软件升级要求共同评估可行性,确保 OpenHarmony 版本可用。 ### 构建说明 本仓库通过 CMake 作为子目标项目引入,编译过程中会自动完成代码构建并建立依赖关系。 当使用选项 "--use-oh-llvm-repo" 时,默认通过 [third_party_llvm-project](https://gitcode.com/openharmony/third_party_llvm-project) 仓代码外加 llvmPatch.diff 进行构建。 开发者也可以手动下载 [third_party_llvm-project](https://gitcode.com/openharmony/third_party_llvm-project) 源码,并应用 patch 文件,命令如下: ```shell mkdir -p third_party/llvm-project cd third_party/llvm-project git clone -b master --depth 1 https://gitcode.com/openharmony/third_party_llvm-project ./ git fetch --depth 1 origin 5c68a1cb123161b54b72ce90e7975d95a8eaf2a4 git checkout 5c68a1cb123161b54b72ce90e7975d95a8eaf2a4 git apply --reject --whitespace=fix ../llvmPatch.diff ``` 或直接拉取 [Cangjie/llvm-project](https://gitcode.com/Cangjie/llvm-project/): ```shell mkdir -p third_party/llvm-project cd third_party/llvm-project git clone -b main --depth 1 https://gitcode.com/Cangjie/llvm-project.git ./ ``` 构建项目时,则直接使用 third_party/llvm-project 目录源码进行构建。 ## MinGW-w64 ### 代码来源说明 仓颉 Windows 版本 SDK 携带 MinGW 中的部分静态库文件,与仓颉代码生成的目标文件链接在一起,为用户生成最终的可以调用 Windows API 的可执行二进制文件。 该仓部分产物将被打包至仓颉发布包中,基于开源仓库 [third_party_mingw-w64 12.0.0](https://gitcode.com/openharmony/third_party_mingw-w64/commit/feea9a87fa42591b298b18fe0e07198f0b8c2f63?ref=master) 进行编译。 该开源软件被编译器及周边组件以包含头文件的方式使用,并通过链接库(动态库或静态库)的方式依赖。 ### 构建说明 当编译目标平台为 Windows 时,需要依赖该开源软件。 该开源软件需在构建编译器之前完成,并且构建编译器时,需指定其编译产物路径。MinGW-w64 的详细构建流程请参阅 [构建指导书](https://gitcode.com/Cangjie/cangjie_build/blob/main/docs/linux_cross_windows_zh.md#23-%E7%BC%96%E8%AF%91mingw-w64%E5%8F%8A%E9%85%8D%E5%A5%97%E5%B7%A5%E5%85%B7%E9%93%BE-%E5%85%B3%E9%94%AE%E6%AD%A5%E9%AA%A4)。 cangjie_compiler-1.0.7/third_party/cmake/000077500000000000000000000000001510705540100204205ustar00rootroot00000000000000cangjie_compiler-1.0.7/third_party/cmake/AddMinGWLibs.cmake000066400000000000000000000072261510705540100236350ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set(MINGW_PACKAGE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/binary/windows-x86_64-mingw.tar.gz) if(NOT EXISTS ${MINGW_PACKAGE_PATH}) # Explicitly Download the MinGW binary package from the lfs repo if it is not found in the build directory or third_party/binary. # There are 2 situations where there is no need to download it from here: # 1. When building release, the mingw package will be built from source and copied to third_party/binary. # 2. It has been already downloaded along with the whole binary-lfs repo (i.e. "binary-deps" target). message(STATUS "Set binary-deps REPOSITORY_PATH: ${REPOSITORY_PATH}") # In order to download contents from lfs as few as possible, shallowly clone the repo without any object, then pull the MinGW package only. ExternalProject_Add( binary-deps-mingw DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E env GIT_LFS_SKIP_SMUDGE=1 git clone ${REPOSITORY_PATH} binary-deps-mingw -b dev --depth 1 COMMAND ${CMAKE_COMMAND} -E chdir git lfs pull --include=windows-x86_64-mingw.tar.gz CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "") externalproject_get_property(binary-deps-mingw SOURCE_DIR) set(BINARY_DEPS_MINGW_DIR ${SOURCE_DIR}) else() # The binary repo has been already downloaded, just create a dummy target. add_custom_target( binary-deps-mingw ALL DEPENDS $ COMMENT "Add a target for binary-deps-mingw") set(BINARY_DEPS_MINGW_DIR $,${BINARY_DEPS_DIR},${CMAKE_CURRENT_SOURCE_DIR}/binary>) endif() set(MINGW_PATH ${BINARY_DEPS_MINGW_DIR}/mingw) # When building cjc, the running platform of binutils should follow the host of that cjc. # When building libs, the running platform of binutils should follow the host platform. if(CANGJIE_BUILD_CJC) set(BINUTILS_DIR ${MINGW_PATH}/bin/${CMAKE_SYSTEM_NAME}/${CMAKE_SYSTEM_PROCESSOR}/) elseif(CANGJIE_BUILD_STD_SUPPORT) set(BINUTILS_DIR ${MINGW_PATH}/bin/${CMAKE_HOST_SYSTEM_NAME}/${CMAKE_HOST_SYSTEM_PROCESSOR}/) endif() set(NATIVE_CANGJIE_BUILD_PATH ${CMAKE_BINARY_DIR}) # If cross-compiling libs, MinGW toolchain should be placed in the native build directory, # in order to be easily found by the native cjc. if(CANGJIE_BUILD_STD_SUPPORT) set(NATIVE_CANGJIE_BUILD_PATH ${CMAKE_BINARY_DIR}/../build) endif() if(CANGJIE_BUILD_CJC) set(COPY_DLL_COMMAND COMMAND ${CMAKE_COMMAND} -E copy_directory ${MINGW_PATH}/dll/ ${CMAKE_BINARY_DIR}/third_party/llvm/bin/) endif() add_custom_command( TARGET binary-deps-mingw POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${MINGW_PATH} COMMAND tar -C ${MINGW_PATH} -xf windows-x86_64-mingw.tar.gz COMMAND ${CMAKE_COMMAND} -E make_directory ${NATIVE_CANGJIE_BUILD_PATH}/third_party/mingw/ COMMAND ${CMAKE_COMMAND} -E copy_directory ${MINGW_PATH}/lib/ ${NATIVE_CANGJIE_BUILD_PATH}/third_party/mingw/lib/ ${COPY_DLL_COMMAND} WORKING_DIRECTORY ${BINARY_DEPS_MINGW_DIR} COMMENT "Uncompressing MinGW...") install(DIRECTORY ${MINGW_PATH}/lib/ DESTINATION third_party/mingw/lib/) # LLVM binaries and dependent dlls are only needed when we are building a cjc for Windows platform. if(CANGJIE_BUILD_CJC) install(DIRECTORY ${MINGW_PATH}/dll/ DESTINATION third_party/llvm/bin/) endif() if(CANGJIE_BUILD_CJDB) install(DIRECTORY ${MINGW_PATH}/dll/ DESTINATION tools/bin/) endif() cangjie_compiler-1.0.7/third_party/cmake/AddOHOSLibs.cmake000066400000000000000000000050171510705540100234200ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set(OHOS_PATH $ENV{OHOS_ROOT}) set(CJNATIVE_BACKEND "cjnative") string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} HOST_OS) string(TOLOWER ${CMAKE_HOST_SYSTEM_PROCESSOR} HOST_ARCH) if("${HOST_OS}" STREQUAL "darwin" AND "${HOST_ARCH}" STREQUAL "arm64") set(HOST_ARCH "aarch64") endif() add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/lib/${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND}/libclang_rt.builtins.a ${CMAKE_BINARY_DIR}/lib/${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND}/libclang_rt.profile.a ${CMAKE_BINARY_DIR}/lib/${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND}/libunwind.a COMMAND ${CMAKE_COMMAND} -E copy ${OHOS_PATH}/prebuilts/clang/ohos/${HOST_OS}-${HOST_ARCH}/llvm/lib/clang/15.0.4/lib/${CMAKE_SYSTEM_PROCESSOR}-linux-ohos/libclang_rt.builtins.a ${CMAKE_BINARY_DIR}/lib/${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND}/libclang_rt.builtins.a COMMAND ${CMAKE_COMMAND} -E copy ${OHOS_PATH}/prebuilts/clang/ohos/${HOST_OS}-${HOST_ARCH}/llvm/lib/clang/15.0.4/lib/${CMAKE_SYSTEM_PROCESSOR}-linux-ohos/libclang_rt.profile.a ${CMAKE_BINARY_DIR}/lib/${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND}/libclang_rt.profile.a COMMAND ${CMAKE_COMMAND} -E copy ${OHOS_PATH}/prebuilts/clang/ohos/${HOST_OS}-${HOST_ARCH}/llvm/lib/${CMAKE_SYSTEM_PROCESSOR}-linux-ohos/libunwind.a ${CMAKE_BINARY_DIR}/lib/${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND}/libunwind.a COMMENT "Copying OHOS libraries...") add_custom_target(external-deps-ohos ALL DEPENDS ${CMAKE_BINARY_DIR}/lib/${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND}/libclang_rt.builtins.a ${CMAKE_BINARY_DIR}/lib/${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND}/libclang_rt.profile.a ${CMAKE_BINARY_DIR}/lib/${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND}/libunwind.a) install( FILES ${CMAKE_BINARY_DIR}/lib/${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND}/libclang_rt.builtins.a ${CMAKE_BINARY_DIR}/lib/${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND}/libclang_rt.profile.a ${CMAKE_BINARY_DIR}/lib/${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND}/libunwind.a DESTINATION lib/${TARGET_TRIPLE_DIRECTORY_PREFIX}_${CJNATIVE_BACKEND}${SANITIZER_SUBPATH}) cangjie_compiler-1.0.7/third_party/cmake/ApplyLLVMPatch.cmake000066400000000000000000000015721510705540100241670ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # cmake -P # ApplyLLVMPatch.cmake (arg 2) # ${LLVM_SOURCE_DIR} (arg 3) # ${LLVM_PATCH} (arg 4) set(LLVM_SOURCE_DIR ${CMAKE_ARGV3}) set(LLVM_PATCH ${CMAKE_ARGV4}) execute_process(COMMAND git diff --quiet WORKING_DIRECTORY ${LLVM_SOURCE_DIR} RESULT_VARIABLE CJNATIVE_SOURCE_DIR_IS_MODIFIED) if(CJNATIVE_SOURCE_DIR_IS_MODIFIED EQUAL 0) execute_process( COMMAND git reset --hard 5c68a1cb123161b54b72ce90e7975d95a8eaf2a4 WORKING_DIRECTORY ${LLVM_SOURCE_DIR} ) execute_process( COMMAND git apply --reject --whitespace=fix ${LLVM_PATCH} WORKING_DIRECTORY ${LLVM_SOURCE_DIR} ) endif()cangjie_compiler-1.0.7/third_party/cmake/BuildCJDB.cmake000066400000000000000000000324221510705540100231070ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. externalproject_get_property(cjnative BINARY_DIR) set(LLVM_GC_BINARY_DIR "${BINARY_DIR}") find_package(Python3 COMPONENTS Interpreter REQUIRED) set(LLVM_GC_LLDB_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/third_party/lldb") string(REPLACE ";" "|" CMAKE_PREFIX_PATH_ALT_SEP "${CMAKE_PREFIX_PATH}") set(CMAKE_PREFIX_PATH_ALT_SEP "${CMAKE_PREFIX_PATH_ALT_SEP}|${CMAKE_BINARY_DIR}/third_party/xml2") set(LLDB_CMAKE_ARGS # Hide warnings during configure which are intended for LLVM developers. -Wno-dev -DCMAKE_BUILD_TYPE=${CANGJIE_CJDB_BUILD_TYPE} -DCMAKE_C_COMPILER=${LLVM_BUILD_C_COMPILER} -DCMAKE_CXX_COMPILER=${LLVM_BUILD_CXX_COMPILER} -DCMAKE_CXX_STANDARD=17 -DCMAKE_INSTALL_PREFIX=${LLVM_GC_LLDB_INSTALL_PREFIX} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH_ALT_SEP} -DLLVM_ENABLE_PROJECTS=lldb -DLLVM_TARGETS_TO_BUILD=AArch64|X86 -DPython3_EXECUTABLE=${Python3_EXECUTABLE} -DLLVM_DIR=${LLVM_GC_BINARY_DIR}/lib/cmake/llvm -DLLVM_EXTERNAL_LIT=${LLVM_GC_BINARY_DIR}/bin/llvm-lit${CMAKE_EXECUTABLE_SUFFIX} -DLLVM_ENABLE_RTTI=ON) set(LLDB_CMAKE_EXE_LINKER_FLAGS "-fstack-protector-strong") set(LLDB_CMAKE_SHARED_LINKER_FLAGS "-fstack-protector-strong") if(WIN32) list(APPEND LLDB_CMAKE_ARGS -DLLDB_ENABLE_LIBEDIT=OFF) set(LLDB_CMAKE_C_FLAGS "${LLDB_CMAKE_C_FLAGS} -fstack-protector-strong") set(LLDB_CMAKE_CXX_FLAGS "${LLDB_CMAKE_CXX_FLAGS} -fstack-protector-strong") set(LLDB_CMAKE_EXE_LINKER_FLAGS "${LLDB_CMAKE_EXE_LINKER_FLAGS} -Wl,--no-insert-timestamp") set(LLDB_CMAKE_SHARED_LINKER_FLAGS "${LLDB_CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-insert-timestamp") else() if(DARWIN) list(APPEND LLDB_CMAKE_ARGS -DLLDB_INCLUDE_TESTS=OFF) set(LLDB_CMAKE_INSTALL_RPATH "@loader_path/../lib") else() set(LLDB_CMAKE_C_FLAGS "${LLDB_CMAKE_C_FLAGS} ${GCC_TOOLCHAIN_FLAG} -fstack-protector-strong -D_FORTIFY_SOURCE=2") set(LLDB_CMAKE_CXX_FLAGS "${LLDB_CMAKE_CXX_FLAGS} ${GCC_TOOLCHAIN_FLAG} -fstack-protector-strong -ftrapv -fpie -D_FORTIFY_SOURCE=2") set(LLDB_CMAKE_EXE_LINKER_FLAGS "${LLDB_CMAKE_EXE_LINKER_FLAGS} -fpie -Wl,-z,relro,-z,now,-z,noexecstack") set(LLDB_CMAKE_SHARED_LINKER_FLAGS "${LLDB_CMAKE_SHARED_LINKER_FLAGS} -fpie -Wl,-z,relro,-z,now,-z,noexecstack") set(LLDB_CMAKE_INSTALL_RPATH "\\$ORIGIN/../lib") if(CANGJIE_CJDB_BUILD_TYPE MATCHES "^(Release|MinSizeRel)$") set(LLDB_CMAKE_CXX_FLAGS "${LLDB_CMAKE_CXX_FLAGS} -O2") set(LLDB_CMAKE_EXE_LINKER_FLAGS "${LLDB_CMAKE_EXE_LINKER_FLAGS} -s") set(LLDB_CMAKE_SHARED_LINKER_FLAGS "${LLDB_CMAKE_SHARED_LINKER_FLAGS} -s") endif() endif() list(APPEND LLDB_CMAKE_ARGS ${LLVM_BUILD_ARG} -DLLDB_ENABLE_LIBEDIT=1 -DLLVM_ENABLE_TERMINFO=1 -DCURSES_NEED_NCURSES=1) endif() if(NOT "${LLDB_CMAKE_C_FLAGS}" STREQUAL "") list(APPEND LLDB_CMAKE_ARGS -DCMAKE_C_FLAGS=${LLDB_CMAKE_C_FLAGS}) endif() if(NOT "${LLDB_CMAKE_CXX_FLAGS}" STREQUAL "") list(APPEND LLDB_CMAKE_ARGS -DCMAKE_CXX_FLAGS=${LLDB_CMAKE_CXX_FLAGS}) endif() list(APPEND LLDB_CMAKE_ARGS -DCMAKE_EXE_LINKER_FLAGS=${LLDB_CMAKE_EXE_LINKER_FLAGS}) list(APPEND LLDB_CMAKE_ARGS -DCMAKE_SHARED_LINKER_FLAGS=${LLDB_CMAKE_SHARED_LINKER_FLAGS}) if(NOT "${LLDB_CMAKE_INSTALL_RPATH}" STREQUAL "") list(APPEND LLDB_CMAKE_ARGS -DCMAKE_INSTALL_RPATH=${LLDB_CMAKE_INSTALL_RPATH}) endif() set(CANGJIE_FRONTEND_LIB ${CMAKE_BINARY_DIR}/lib/libcangjie-frontend${CMAKE_SHARED_LIBRARY_SUFFIX}) set(CANGJIE_LSP_LIB ${CMAKE_BINARY_DIR}/lib/libcangjie-lsp${CMAKE_SHARED_LIBRARY_SUFFIX}) if(WIN32) set(CANGJIE_FRONTEND_LIB ${CMAKE_BINARY_DIR}/bin/libcangjie-frontend.dll) set(CANGJIE_LSP_LIB ${CMAKE_BINARY_DIR}/bin/libcangjie-lsp.dll) endif() set(CANGJIE_FRONTEND_IMPLIB ${CMAKE_BINARY_DIR}/lib/libcangjie-frontend.dll.a) set(CANGJIE_LSP_IMPLIB ${CMAKE_BINARY_DIR}/lib/libcangjie-lsp.dll.a) list(APPEND LLDB_CMAKE_ARGS ${LLDB_CMAKE_ARGS} -DLLDB_ENABLE_PYTHON=false -DLLDB_RELOCATABLE_PYTHON=false -DLLDB_ENABLE_LZMA=false -DLLDB_ENABLE_LIBXML2=true -DCANGJIE_ROOT=${CMAKE_SOURCE_DIR} -DCANGJIE_FRONTEND_LIB=${CANGJIE_FRONTEND_LIB} -DCANGJIE_FRONTEND_IMPLIB=${CANGJIE_FRONTEND_IMPLIB} -DCANGJIE_LSP_LIB=${CANGJIE_LSP_LIB} -DCANGJIE_LSP_IMPLIB=${CANGJIE_LSP_IMPLIB}) if(CMAKE_CROSSCOMPILING AND WIN32) set(MINGW_STRIP "x86_64-w64-mingw32-strip -D") list(APPEND LLDB_CMAKE_ARGS -DCMAKE_STRIP=${MINGW_STRIP}) list(APPEND LLDB_CMAKE_ARGS -DCMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}) list(APPEND LLDB_CMAKE_ARGS -DCMAKE_SYSTEM_NAME=Windows) list(APPEND LLDB_CMAKE_ARGS -DLLVM_HOST_TRIPLE=x86_64-w64-mingw32) list(APPEND LLDB_CMAKE_ARGS -DNATIVE_Clang_DIR=${LLVM_GC_BINARY_DIR}/NATIVE/tools/bin) list(APPEND LLDB_CMAKE_ARGS -DNATIVE_LLVM_DIR=${LLVM_GC_BINARY_DIR}/NATIVE/tools/bin) list(APPEND LLDB_CMAKE_ARGS -DLLDB_INCLUDE_TESTS=OFF) set(NATIVE_BUILD_CMAKE_ARGS -Wno-dev|-Wno-deprecated|-DLLDB_INCLUDE_TESTS=OFF) list(APPEND LLDB_CMAKE_ARGS -DCROSS_TOOLCHAIN_FLAGS_lldb_NATIVE=${NATIVE_BUILD_CMAKE_ARGS}) endif() if(CMAKE_HOST_WIN32) list(APPEND LLDB_CMAKE_ARGS -DLLVM_PARALLEL_LINK_JOBS=1) endif() externalproject_get_property(cjnative SOURCE_DIR) ExternalProject_Add( lldb SOURCE_DIR ${SOURCE_DIR} DOWNLOAD_COMMAND "" BUILD_BYPRODUCTS ${LLVM_GC_LLDB_INSTALL_PREFIX}/bin/lldb SOURCE_SUBDIR lldb BINARY_DIR ${CMAKE_BINARY_DIR}/third_party/lldb-build LIST_SEPARATOR | CMAKE_ARGS ${LLDB_CMAKE_ARGS} USES_TERMINAL_BUILD ON DEPENDS cjnative) # Install lldb binaries, lldb, lldb-server, etc. install( DIRECTORY ${LLVM_GC_LLDB_INSTALL_PREFIX}/bin USE_SOURCE_PERMISSIONS DESTINATION third_party/llvm) # For Windows, dlls are installed in bin directory. Import libraries are not required. if(DARWIN) install(FILES ${CMAKE_BINARY_DIR}/third_party/xml2/lib/libxml2.dylib ${CMAKE_BINARY_DIR}/third_party/xml2/lib/libxml2.16.dylib ${CMAKE_BINARY_DIR}/third_party/xml2/lib/libxml2.2.14.0.dylib DESTINATION third_party/llvm/lib) # Install lldb libraries, liblldb.so, liblldb.so.15, etc. liblldbIntelFeatures.so is not installed. install( DIRECTORY ${LLVM_GC_LLDB_INSTALL_PREFIX}/lib DESTINATION third_party/llvm USE_SOURCE_PERMISSIONS FILES_MATCHING REGEX "lib/liblldb") elseif(NOT WIN32) install(FILES ${CMAKE_BINARY_DIR}/third_party/xml2/lib/libxml2.so ${CMAKE_BINARY_DIR}/third_party/xml2/lib/libxml2.so.16 ${CMAKE_BINARY_DIR}/third_party/xml2/lib/libxml2.so.2.14.0 DESTINATION third_party/llvm/lib) # Install lldb libraries, liblldb.so, liblldb.so.15, etc. liblldbIntelFeatures.so is not installed. install( DIRECTORY ${LLVM_GC_LLDB_INSTALL_PREFIX}/lib DESTINATION third_party/llvm USE_SOURCE_PERMISSIONS FILES_MATCHING REGEX "lib/liblldb") endif() # Generate binary cjdb and its dependencies. A symbolic link of lldb will be created if it's possible, # otherwise a copy of lldb will be created. if(WIN32) install(FILES ${CMAKE_BINARY_DIR}/third_party/xml2/bin/libxml2.dll DESTINATION tools/bin/) install( PROGRAMS ${CMAKE_BINARY_DIR}/third_party/lldb/bin/lldb${CMAKE_EXECUTABLE_SUFFIX} DESTINATION tools/bin/ RENAME cjdb${CMAKE_EXECUTABLE_SUFFIX}) install(FILES ${CMAKE_BINARY_DIR}/third_party/llvm/bin/libclang.dll ${CMAKE_BINARY_DIR}/third_party/llvm/bin/libclang-cpp.dll ${CMAKE_BINARY_DIR}/third_party/llvm/bin/libLTO.dll ${CMAKE_BINARY_DIR}/third_party/llvm/bin/libLLVM-15.dll ${CMAKE_BINARY_DIR}/third_party/llvm/bin/libLLVM-Foundation-15.dll ${CMAKE_BINARY_DIR}/third_party/lldb/bin/liblldb.dll DESTINATION tools/bin/) else() # Create symbolic link for cjdb add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/tools/bin/cjdb${CMAKE_EXECUTABLE_SUFFIX} COMMAND ${CMAKE_COMMAND} -D CMAKE_CROSSCOMPILING=${CMAKE_CROSSCOMPILING} -D WIN32=OFF -D LINK_TARGET=../../third_party/llvm/bin/lldb${CMAKE_EXECUTABLE_SUFFIX} -D LINK_NAME=cjdb${CMAKE_EXECUTABLE_SUFFIX} -D WORKING_DIR=${CMAKE_BINARY_DIR}/tools/bin -P "${CMAKE_SOURCE_DIR}/cmake/modules/make-symlink.cmake" DEPENDS cjnative) add_custom_target( cjdb ALL DEPENDS ${CMAKE_BINARY_DIR}/tools/bin/cjdb${CMAKE_EXECUTABLE_SUFFIX} COMMENT "Making cjdb${CMAKE_EXECUTABLE_SUFFIX}") install( CODE "execute_process(COMMAND ${CMAKE_COMMAND} -D CMAKE_CROSSCOMPILING=${CMAKE_CROSSCOMPILING} -D WIN32=OFF -D LINK_TARGET=../../third_party/llvm/bin/lldb${CMAKE_EXECUTABLE_SUFFIX} -D LINK_NAME=cjdb${CMAKE_EXECUTABLE_SUFFIX} -D WORKING_DIR=${CMAKE_INSTALL_PREFIX}/tools/bin -P ${CMAKE_SOURCE_DIR}/cmake/modules/make-symlink.cmake)") # Create symbolic link for boundscheck set(CJNATIVE_BACKEND "cjnative") string(TOLOWER ${CMAKE_SYSTEM_NAME}_${CMAKE_SYSTEM_PROCESSOR}_${CJNATIVE_BACKEND} output_lib_dir) add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/third_party/lldb/lib/libboundscheck${CMAKE_SHARED_LIBRARY_SUFFIX} COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/runtime/lib/${output_lib_dir} COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/lib/libboundscheck${CMAKE_SHARED_LIBRARY_SUFFIX} ${CMAKE_BINARY_DIR}/runtime/lib/${output_lib_dir} COMMAND ${CMAKE_COMMAND} -D CMAKE_CROSSCOMPILING=${CMAKE_CROSSCOMPILING} -D WIN32=OFF -D LINK_TARGET=../../../runtime/lib/${output_lib_dir}/libboundscheck${CMAKE_SHARED_LIBRARY_SUFFIX} -D LINK_NAME=libboundscheck${CMAKE_SHARED_LIBRARY_SUFFIX} -D WORKING_DIR=${CMAKE_BINARY_DIR}/third_party/llvm/lib -P "${CMAKE_SOURCE_DIR}/cmake/modules/make-symlink.cmake" DEPENDS cjnative) add_custom_target( lldb-boundscheck ALL DEPENDS ${CMAKE_BINARY_DIR}/third_party/lldb/lib/libboundscheck${CMAKE_SHARED_LIBRARY_SUFFIX} COMMENT "Making third_party/llvm/lib/libboundscheck${CMAKE_SHARED_LIBRARY_SUFFIX}") install( CODE "execute_process(COMMAND ${CMAKE_COMMAND} -D CMAKE_CROSSCOMPILING=${CMAKE_CROSSCOMPILING} -D WIN32=OFF -D LINK_TARGET=../../../runtime/lib/${output_lib_dir}/libboundscheck${CMAKE_SHARED_LIBRARY_SUFFIX} -D LINK_NAME=libboundscheck${CMAKE_SHARED_LIBRARY_SUFFIX} -D WORKING_DIR=${CMAKE_INSTALL_PREFIX}/third_party/llvm/lib -P ${CMAKE_SOURCE_DIR}/cmake/modules/make-symlink.cmake)") # Create symbolic link for cangjie-lsp add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/third_party/lldb/lib/libcangjie-lsp${CMAKE_SHARED_LIBRARY_SUFFIX} COMMAND ${CMAKE_COMMAND} -D CMAKE_CROSSCOMPILING=${CMAKE_CROSSCOMPILING} -D WIN32=OFF -D LINK_TARGET=../../../tools/lib/libcangjie-lsp${CMAKE_SHARED_LIBRARY_SUFFIX} -D LINK_NAME=libcangjie-lsp${CMAKE_SHARED_LIBRARY_SUFFIX} -D WORKING_DIR=${CMAKE_BINARY_DIR}/third_party/llvm/lib -P "${CMAKE_SOURCE_DIR}/cmake/modules/make-symlink.cmake" DEPENDS lldb) add_custom_target( lldb-cangjie-lsp ALL DEPENDS ${CMAKE_BINARY_DIR}/third_party/lldb/lib/libcangjie-lsp${CMAKE_SHARED_LIBRARY_SUFFIX} COMMENT "Making third_party/llvm/lib/libcangjie-lsp${CMAKE_SHARED_LIBRARY_SUFFIX}") install( CODE "execute_process(COMMAND ${CMAKE_COMMAND} -D CMAKE_CROSSCOMPILING=${CMAKE_CROSSCOMPILING} -D WIN32=OFF -D LINK_TARGET=../../../tools/lib/libcangjie-lsp${CMAKE_SHARED_LIBRARY_SUFFIX} -D LINK_NAME=libcangjie-lsp${CMAKE_SHARED_LIBRARY_SUFFIX} -D WORKING_DIR=${CMAKE_INSTALL_PREFIX}/third_party/llvm/lib -P ${CMAKE_SOURCE_DIR}/cmake/modules/make-symlink.cmake)") # Create symbolic link for cangjie-frontend add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/third_party/lldb/lib/libcangjie-frontend${CMAKE_SHARED_LIBRARY_SUFFIX} COMMAND ${CMAKE_COMMAND} -D CMAKE_CROSSCOMPILING=${CMAKE_CROSSCOMPILING} -D WIN32=OFF -D LINK_TARGET=../../../tools/lib/libcangjie-frontend${CMAKE_SHARED_LIBRARY_SUFFIX} -D LINK_NAME=libcangjie-frontend${CMAKE_SHARED_LIBRARY_SUFFIX} -D WORKING_DIR=${CMAKE_BINARY_DIR}/third_party/llvm/lib -P "${CMAKE_SOURCE_DIR}/cmake/modules/make-symlink.cmake" DEPENDS lldb) add_custom_target( lldb-cangjie-frontend ALL DEPENDS ${CMAKE_BINARY_DIR}/third_party/lldb/lib/libcangjie-frontend${CMAKE_SHARED_LIBRARY_SUFFIX} COMMENT "Making third_party/llvm/lib/libcangjie-frontend${CMAKE_SHARED_LIBRARY_SUFFIX}") install( CODE "execute_process(COMMAND ${CMAKE_COMMAND} -D CMAKE_CROSSCOMPILING=${CMAKE_CROSSCOMPILING} -D WIN32=OFF -D LINK_TARGET=../../../tools/lib/libcangjie-frontend${CMAKE_SHARED_LIBRARY_SUFFIX} -D LINK_NAME=libcangjie-frontend${CMAKE_SHARED_LIBRARY_SUFFIX} -D WORKING_DIR=${CMAKE_INSTALL_PREFIX}/third_party/llvm/lib -P ${CMAKE_SOURCE_DIR}/cmake/modules/make-symlink.cmake)") endif() cangjie_compiler-1.0.7/third_party/cmake/CMakeLists.txt000066400000000000000000000061611510705540100231640ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. cmake_minimum_required(VERSION 3.16.5) project(boundscheck) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(SECURE_CFLAG_FOR_SHARED_LIBRARY "-fstack-protector-all") # we are not expect libboundscheck to be instrumented by asan or hwasan # otherwise it will generate false positive everywhere if(CANGJIE_SANITIZER_SUPPORT_ENABLED) get_directory_property(boundscheck_COMPILE_OPTIONS COMPILE_OPTIONS) list(REMOVE_ITEM boundscheck_COMPILE_OPTIONS "-fsanitize=address" "-fsanitize=hwaddress") set_directory_properties(PROPERTIES COMPILE_OPTIONS "${boundscheck_COMPILE_OPTIONS}") endif() if (MINGW) set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) set(WARNING_FLAGS "-w") set(SECURE_CFLAG_FOR_SHARED_LIBRARY "-static ${SECURE_CFLAG_FOR_SHARED_LIBRARY}") else() if(NOT DARWIN) set(SECURE_LDFLAG_FOR_SHARED_LIBRARY "-Wl,-z,relro,-z,now,-z,noexecstack") endif() endif() set(STRIP_FLAG "-s") set(CMAKE_C_FLAGS "${SECURE_CFLAG_FOR_SHARED_LIBRARY} ${WARNING_FLAGS}") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g") set(CMAKE_C_FLAGS_RELEASE "-D_FORTIFY_SOURCE=2 -O2") set(CMAKE_C_FLAGS_DEBUG "-O0 -g") if(CLANG_TARGET_TRIPLE) # We add --target option for clang only since gcc does not support --target option. # In case of gcc, cross compilation requires a target-specific gcc (a cross compiler). add_compile_options(--target=${CLANG_TARGET_TRIPLE}) add_link_options(--target=${CLANG_TARGET_TRIPLE}) endif() if(CANGJIE_CMAKE_SYSROOT) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --sysroot=${CANGJIE_CMAKE_SYSROOT}") endif() if(CANGJIE_ENABLE_HWASAN) add_compile_options(-shared-libsan -fsanitize=hwaddress -fno-emulated-tls -mllvm -hwasan-globals=0 -fno-lto -fno-whole-program-vtables) add_link_options(-shared-libsan -fsanitize=hwaddress -fno-emulated-tls -mllvm -hwasan-globals=0 -fno-lto -fno-whole-program-vtables) endif() if (MINGW) # MSVCRT already provides most of secure functions, # so only files listed below need to be compiled from boundscheck library, # to avoid "multiple definition" error. # Refer to offiical manual of boundscheck library for more information. list(APPEND SRC_FILE src/memset_s.c src/snprintf_s.c src/vsnprintf_s.c src/secureprintoutput_a.c) else() aux_source_directory(./src SRC_FILE) endif() # Build shared library. add_library(boundscheck SHARED ${SRC_FILE}) add_library(boundscheck-static STATIC ${SRC_FILE}) target_include_directories(boundscheck PRIVATE ./include) target_include_directories(boundscheck-static PRIVATE ./include) target_link_options(boundscheck PRIVATE ${SECURE_LDFLAG_FOR_SHARED_LIBRARY} ${STRIP_FLAG}) if (EXPORT_boundscheck) install(DIRECTORY include DESTINATION .) install(TARGETS boundscheck RUNTIME DESTINATION lib LIBRARY DESTINATION lib) install(TARGETS boundscheck-static ARCHIVE DESTINATION lib) endif()cangjie_compiler-1.0.7/third_party/cmake/CompileRTLib.cmake000066400000000000000000000152511510705540100237130ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # The compile_rtlib compiles libclang_rt-profile.a and libclang_rt-builtins.a for the target platforms. function(compile_rtlib target_name) set(oneValueArgs INSTALL_PREFIX) cmake_parse_arguments( COMPILE_RTLIB "${options}" "${oneValueArgs}" "" ${ARGN}) set(LLVM_RTLIBS libclang_rt.profile-${CMAKE_SYSTEM_PROCESSOR}.a) if(NOT WIN32 AND NOT IOS) list(APPEND LLVM_RTLIBS libclang_rt.fuzzer_no_main-${CMAKE_SYSTEM_PROCESSOR}.a) set(CANGJIE_LIBFUZZER_BUILD_ENABLED ON) else() set(CANGJIE_LIBFUZZER_BUILD_ENABLED OFF) endif() if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64") list(APPEND LLVM_RTLIBS libclang_rt.builtins-${CMAKE_SYSTEM_PROCESSOR}.a) endif() set(RTLIBS_TARGET ${TRIPLE}) if(CANGJIE_ASAN_SUPPORT) set(SANITIZER_BUILD_TARGET -DCOMPILER_RT_SANITIZERS_TO_BUILD=asan) list(APPEND LLVM_RTLIBS libclang_rt.asan-${CMAKE_SYSTEM_PROCESSOR}.a) elseif(CANGJIE_TSAN_SUPPORT) set(SANITIZER_BUILD_TARGET -DCOMPILER_RT_SANITIZERS_TO_BUILD=tsan) list(APPEND LLVM_RTLIBS libclang_rt.tsan-${CMAKE_SYSTEM_PROCESSOR}.a) elseif(CANGJIE_HWASAN_SUPPORT) list(APPEND LLVM_RTLIBS libclang_rt.hwasan-${CMAKE_SYSTEM_PROCESSOR}.a) endif() # Setting flags for cross-compiling set(RTLIBS_C_FLAGS "${C_OTHER_FLAGS} -pipe -fno-common -fno-strict-aliasing -O3 -U_FORTIFY_SOURCE --prefix=${CANGJIE_TARGET_TOOLCHAIN}" ) set(RTLIBS_CXX_FLAGS "${C_OTHER_FLAGS} -pipe -fno-common -fno-strict-aliasing -O3 -U_FORTIFY_SOURCE --prefix=${CANGJIE_TARGET_TOOLCHAIN}" ) if(IOS) set(RTLIBS_C_FLAGS "${RTLIBS_C_FLAGS} -isysroot ${CMAKE_IOS_SDK_ROOT}") set(RTLIBS_CXX_FLAGS "${RTLIBS_CXX_FLAGS} -isysroot ${CMAKE_IOS_SDK_ROOT}") else() set(RTLIBS_C_FLAGS "${RTLIBS_C_FLAGS} --sysroot=${CMAKE_SYSROOT} -fstack-protector-all") set(RTLIBS_CXX_FLAGS "${RTLIBS_CXX_FLAGS} --sysroot=${CMAKE_SYSROOT} -fstack-protector-all") endif() # The -fPIE option is not provided on Windows. # Address space layout randomization (ASLR) is enabled by MinGW ld by default. if(NOT WIN32) set(RTLIBS_C_FLAGS "${RTLIBS_C_FLAGS} -fPIE") set(RTLIBS_CXX_FLAGS "${RTLIBS_CXX_FLAGS} -fPIE") endif() if(MINGW OR (CMAKE_CROSSCOMPILING AND NOT OHOS AND NOT IOS)) set(CLANG_C_COMPILER ${TRIPLE}-gcc) set(CLANG_CXX_COMPILER ${TRIPLE}-g++) else() set(CLANG_C_COMPILER clang) set(CLANG_CXX_COMPILER clang++) endif() # Use clang which the current cmake uses instead of system installed clang. if(CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_COMPILER_ID STREQUAL "AppleClang") set(CLANG_C_COMPILER ${CMAKE_C_COMPILER}) message(STATUS ${CLANG_C_COMPILER}) endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") set(CLANG_CXX_COMPILER ${CMAKE_CXX_COMPILER}) message(STATUS ${CLANG_CXX_COMPILER}) endif() # Finding full paths of these pre-installed llvm tools. find_program(RTLIBS_AR_PATH "llvm-ar") find_program(RTLIBS_NM_PATH "llvm-nm") find_program(RTLIBS_RANLIB_PATH "llvm-ranlib") # sanitizer version REQUIRES llvm-config, otherwise will fail. if (CANGJIE_SANITIZER_SUPPORT_ENABLED) find_program(RTLIBS_CONFIG_PATH "llvm-config" REQUIRED) else() find_program(RTLIBS_CONFIG_PATH "llvm-config") endif() set(LLVM_CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${COMPILE_RTLIB_INSTALL_PREFIX} -DCMAKE_BUILD_TYPE=Release -DCOMPILER_RT_BUILD_BUILTINS=ON -DCOMPILER_RT_BUILD_PROFILE=ON -DCOMPILER_RT_BUILD_SANITIZERS=${CANGJIE_SANITIZER_SUPPORT_ENABLED} ${SANITIZER_BUILD_TARGET} -DCOMPILER_RT_BUILD_XRAY=OFF -DCOMPILER_RT_BUILD_LIBFUZZER=${CANGJIE_LIBFUZZER_BUILD_ENABLED} -DCOMPILER_RT_BUILD_MEMPROF=OFF -DCOMPILER_RT_BUILD_CRT=OFF -DCOMPILER_RT_USE_LIBCXX=OFF -DCMAKE_C_COMPILER=${CLANG_C_COMPILER} -DCMAKE_CXX_COMPILER=${CLANG_CXX_COMPILER} -DCMAKE_AR=${RTLIBS_AR_PATH} -DCMAKE_NM=${RTLIBS_NM_PATH} -DCMAKE_RANLIB=${RTLIBS_RANLIB_PATH} -DLLVM_CONFIG_PATH=${RTLIBS_CONFIG_PATH} -DCMAKE_C_COMPILER_TARGET=${RTLIBS_TARGET} -DCMAKE_CXX_COMPILER_TARGET=${RTLIBS_TARGET} -DCMAKE_ASM_COMPILER_TARGET=${RTLIBS_TARGET} -DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON -DCMAKE_C_FLAGS=${RTLIBS_C_FLAGS} -DCMAKE_CXX_FLAGS=${RTLIBS_CXX_FLAGS} -DCMAKE_IOS_SDK_ROOT=${CMAKE_IOS_SDK_ROOT} -DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT}) if(MINGW) list(APPEND LLVM_CMAKE_ARGS -DCMAKE_SYSTEM_NAME=Windows) list(APPEND LLVM_CMAKE_ARGS -DLLVM_HOST_TRIPLE=x86_64-w64-mingw32) endif() if(ANDROID) list(APPEND LLVM_CMAKE_ARGS -DCMAKE_SYSTEM_NAME=Android) list(APPEND LLVM_CMAKE_ARGS -DCMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}) endif() # For Jenkins, which download source files first. if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/llvm-project) ExternalProject_Add( ${target_name} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/llvm-project BUILD_BYPRODUCTS ${LLVM_RTLIBS} SOURCE_SUBDIR compiler-rt CMAKE_ARGS ${LLVM_CMAKE_ARGS}) else() if(CANGJIE_USE_OH_LLVM_REPO) set(REPOSITORY_PATH "https://gitcode.com/openharmony/third_party_llvm-project.git") set(LLVM_TAG master) else() set(REPOSITORY_PATH "https://gitcode.com/Cangjie/llvm-project.git") set(LLVM_TAG dev) endif() set(LLVM_REPO_DOWNLOAD_ARGS DOWNLOAD_COMMAND git clone -b ${LLVM_TAG} --depth=1 ${REPOSITORY_PATH} ) if(CANGJIE_USE_OH_LLVM_REPO) set(APPLY_LLVM_PATCH_COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ApplyLLVMPatch.cmake" ${CMAKE_SOURCE_DIR}/third_party/llvmPatch.diff) set(LLVM_REPO_DOWNLOAD_ARGS GIT_REPOSITORY ${REPOSITORY_PATH} GIT_TAG ${LLVM_TAG} GIT_PROGRESS ON GIT_CONFIG ${GIT_ARGS} GIT_SHALLOW OFF) endif() ExternalProject_Add( ${target_name} ${LLVM_REPO_DOWNLOAD_ARGS} BUILD_BYPRODUCTS ${LLVM_RTLIBS} SOURCE_SUBDIR compiler-rt CMAKE_ARGS ${LLVM_CMAKE_ARGS}) endif() endfunction(compile_rtlib) cangjie_compiler-1.0.7/third_party/cmake/CopyIfDifferent.cmake000066400000000000000000000050521510705540100244440ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # cmake -P # CopyIfDifferent.cmake (arg 2) # ${FROM_DIRECTORY} (arg 3) - copy source # ${TO_DIRECTORY} (arg 4) - copy destination # ${CHECK_FILES} (arg 5) - a list of files for difference checking, if all CHECK_FILES are the same, CopyIfDifferent is not performed. set(FROM_DIRECTORY ${CMAKE_ARGV3}) set(TO_DIRECTORY ${CMAKE_ARGV4}) set(CHECK_FILES ${CMAKE_ARGV5}) # Enable cmake if IN_LIST feature cmake_policy(SET CMP0057 NEW) set(NORMALIZED_CHECK_FILES_SOURCE_PATH) set(NEED_COPY TRUE) if(CHECK_FILES) # Only perform copying if any of user specified check files are changed set(NEED_COPY FALSE) foreach(file ${CHECK_FILES}) file(TO_CMAKE_PATH ${FROM_DIRECTORY}/${file} NORMALIZED_CHECK_FILE_PATH) list(APPEND NORMALIZED_CHECK_FILES_SOURCE_PATH ${NORMALIZED_CHECK_FILE_PATH}) # Target file doesn't exist, perform copying if(NOT EXISTS ${TO_DIRECTORY}/${file}) set(NEED_COPY TRUE) break() endif() execute_process( COMMAND ${CMAKE_COMMAND} -E compare_files ${FROM_DIRECTORY}/${file} ${TO_DIRECTORY}/${file} RESULT_VARIABLE ret ERROR_QUIET) # suppress `Files are different` output of the command # User specified file is changed, perform copying if(NOT ret EQUAL "0") set(NEED_COPY TRUE) break() endif() endforeach(file) endif() if(NEED_COPY) file( GLOB_RECURSE allfiles RELATIVE "${FROM_DIRECTORY}" "${FROM_DIRECTORY}/*") foreach(file ${allfiles}) set(source_path ${FROM_DIRECTORY}/${file}) file(TO_CMAKE_PATH ${source_path} NORMALIZED_SOURCE_FILE_PATH) if(NORMALIZED_SOURCE_FILE_PATH IN_LIST NORMALIZED_CHECK_FILES_SOURCE_PATH) # CHECK_FILES are copied at the last. CHECK_FILES may prevent other files being copied. Update or # copy CHECK_FILES too early may result an incomplete copy in incremental compilation. continue() endif() execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${source_path} ${TO_DIRECTORY}/${file}) endforeach(file) # copy CHECK_FILES foreach(file ${CHECK_FILES}) execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${FROM_DIRECTORY}/${file} ${TO_DIRECTORY}/${file}) endforeach(file) endif() cangjie_compiler-1.0.7/third_party/cmake/ExtractRtlib.cmake000066400000000000000000000071021510705540100240310ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. # cmake -P # ExtractRtlib.cmake (arg 2) # ${CMAKE_BINARY_DIR} (arg 3) # ${SPECIFIC_LIB} (arg 4) # ${NEW_SPECIFIC_LIB_PATH} (arg 5) # ${CANGJIE_TARGET_ARCH} (arg 6) # ${CROSS_COMPILING} (arg 7) # ${THIRD_PARTY_LLVM} (arg 8) # ${IOS} (arg 9) set(CMAKE_BINARY_DIR ${CMAKE_ARGV3}) set(SPECIFIC_LIB ${CMAKE_ARGV4}) set(NEW_SPECIFIC_LIB_PATH ${CMAKE_ARGV5}) set(CANGJIE_TARGET_ARCH ${CMAKE_ARGV6}) set(CANGJIE_BUILD_CJC ${CMAKE_ARGV7}) set(THIRD_PARTY_LLVM ${CMAKE_ARGV8}) set(IOS ${CMAKE_ARGV9}) set(LIB_SUFFIX ".a") # Create the specific library # 1. Find the path of the specific library compiled from llvm compiler-rt if(CANGJIE_BUILD_CJC) # It is native-compiling # asan/tsan has multiple product, select precisely if(("asan" STREQUAL "${SPECIFIC_LIB}") OR ("tsan" STREQUAL "${SPECIFIC_LIB}")) file(GLOB OLD_SPECIFIC_LIB_PATHS ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/lib/clang/15.0.4/lib/*/libclang_rt.${SPECIFIC_LIB}-${CANGJIE_TARGET_ARCH}${LIB_SUFFIX}) if(NOT "${OLD_SPECIFIC_LIB_PATHS}") file(GLOB OLD_SPECIFIC_LIB_PATHS ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/lib/clang/15.0.4/lib/*/libclang_rt.${SPECIFIC_LIB}${LIB_SUFFIX}) endif() else() file(GLOB OLD_SPECIFIC_LIB_PATHS ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/lib/clang/15.0.4/lib/*/libclang_rt.${SPECIFIC_LIB}*${LIB_SUFFIX}) endif() foreach(f ${OLD_SPECIFIC_LIB_PATHS}) if(f MATCHES ".*${CANGJIE_TARGET_ARCH}.*\\.a" OR f MATCHES ".*darwin.*\\.a") set(OLD_SPECIFIC_LIB_PATH ${f}) endif() endforeach() else() # It is cross-compiling if(IOS) file(GLOB OLD_SPECIFIC_LIB_PATH ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/lib/*/libclang_rt.${SPECIFIC_LIB}${LIB_SUFFIX}) else() file(GLOB OLD_SPECIFIC_LIB_PATH ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/lib/*/libclang_rt.${SPECIFIC_LIB}-${CANGJIE_TARGET_ARCH}${LIB_SUFFIX}) endif() endif() # 2. Move the specific lib to a temporary location; currently we use the ${CMAKE_BINARY_DIR}/lib if(NOT "${OLD_SPECIFIC_LIB_PATH}" STREQUAL "" AND EXISTS ${OLD_SPECIFIC_LIB_PATH}) # The libraries to be extracted have the following relationship with the supported target arch: # | lib \ Arch | x86_64_ | aarch64 | # | -------------- | ------- | ------- | # | clang-builtins | need | no need | # | clang-profile | need | need | # | clang-asan | need | need | # | clang-tsan | need | need | if(NOT (CANGJIE_TARGET_ARCH STREQUAL "aarch64" AND ${SPECIFIC_LIB} STREQUAL "builtins")) file(RENAME ${OLD_SPECIFIC_LIB_PATH} ${NEW_SPECIFIC_LIB_PATH}) endif() else() message(STATUS "The libclang_rt.${SPECIFIC_LIB}-${CANGJIE_TARGET_ARCH}.a has been extracted and does not exist.") endif() # 3. Avoid installing redundant empty folder when cross-compiling. # All its contents have been already moved to Cangjie's lib folder, so it is empty and should be removed. if(NOT CANGJIE_BUILD_CJC) file(GLOB REMAINING_FILES ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/lib/linux/*) list(LENGTH REMAINING_FILES LEN) if(${LEN} STREQUAL "0") file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/lib/linux) endif() endif() # 4. Copy the specific libraries to the runtime directory for build-binary-tar (see TARGET cjnative POST_BUILD) cangjie_compiler-1.0.7/third_party/cmake/Flatbuffer.cmake000066400000000000000000000030221510705540100234770ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. set(FLATBUFFERS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/flatbuffers) set(FLATBUFFERS_COMPILE_OPTIONS -DCMAKE_C_FLAGS=${GCC_TOOLCHAIN_FLAG} -DCMAKE_CXX_FLAGS=${GCC_TOOLCHAIN_FLAG}) if(CMAKE_HOST_WIN32) list(APPEND FLATBUFFERS_COMPILE_OPTIONS -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++) else() list(APPEND FLATBUFFERS_COMPILE_OPTIONS -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++) endif() # for CloudDragon, download in Prebuild if(NOT EXISTS ${FLATBUFFERS_SRC}/CMakeLists.txt) set(REPOSITORY_PATH https://gitcode.com/openharmony/third_party_flatbuffers.git) message(STATUS "Set flatbuffers REPOSITORY_PATH: ${REPOSITORY_PATH}") set(FLATBUFFERS_DOWNLOAD_ARGS GIT_REPOSITORY ${REPOSITORY_PATH} GIT_TAG master GIT_PROGRESS ON GIT_CONFIG ${GIT_ARGS} GIT_SHALLOW ON) endif() ExternalProject_Add( flatbuffers ${FLATBUFFERS_DOWNLOAD_ARGS} CMAKE_ARGS # no need to Build tests and install. -DFLATBUFFERS_BUILD_TESTS=OFF -DFLATBUFFERS_INSTALL=ON # Build only necessary targets. -DFLATBUFFERS_BUILD_FLATHASH=OFF -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} ${FLATBUFFERS_COMPILE_OPTIONS}) externalproject_get_property(flatbuffers SOURCE_DIR) set(FLATBUFFERS_SRC ${SOURCE_DIR}) cangjie_compiler-1.0.7/third_party/cmake/RemoveByproductLib.cmake000066400000000000000000000035261510705540100252100ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. #[[ cmake -P CompileRTLib.cmake (arg 2) ${CMAKE_BINARY_DIR} (arg 3) ${THIRD_PARTY_LLVM} (arg 4) ]] set(CMAKE_BINARY_DIR ${CMAKE_ARGV3}) set(THIRD_PARTY_LLVM ${CMAKE_ARGV4}) set(KEEP_CLANG_CPP_LIBS ${CMAKE_ARGV5}) # 1. Since other libraries will be built together with the specific library, we # should remove them. file(GLOB CLANG_LIBS_TO_REMOVE ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/lib/libclang*.*) if(KEEP_CLANG_CPP_LIBS) list(REMOVE_ITEM CLANG_LIBS_TO_REMOVE "${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/lib/libclang-cpp.dylib" "${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/lib/libclang-cpp.so.15" "${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/lib/libclang-cpp.so.15.0.4") endif() list(LENGTH CLANG_LIBS_TO_REMOVE CLANG_LIBS_TO_REMOVE_LENGTH) if(CLANG_LIBS_TO_REMOVE_LENGTH GREATER 0) file(REMOVE ${CLANG_LIBS_TO_REMOVE}) endif() if(EXISTS ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/lib/clang) file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/lib/clang) endif() # 2. Remove clang related binaries file(GLOB CLANG_BINS_TO_REMOVE ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/bin/clang*) list(LENGTH CLANG_BINS_TO_REMOVE CLANG_BINS_TO_REMOVE_LENGTH) if(CLANG_BINS_TO_REMOVE_LENGTH GREATER 0) set(OTHER_CLANG_BINS_TO_REMOVE c-index-test diagtool flang git-clang-format hmaptool scan-build scan-view) foreach(BIN_NAME ${OTHER_CLANG_BINS_TO_REMOVE}) list(APPEND CLANG_BINS_TO_REMOVE ${CMAKE_BINARY_DIR}/${THIRD_PARTY_LLVM}/bin/${BIN_NAME}) endforeach() file(REMOVE ${CLANG_BINS_TO_REMOVE}) endif() cangjie_compiler-1.0.7/third_party/cmake/Xml2.cmake000066400000000000000000000061401510705540100222450ustar00rootroot00000000000000# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. # This source file is part of the Cangjie project, licensed under Apache-2.0 # with Runtime Library Exception. # # See https://cangjie-lang.cn/pages/LICENSE for license information. message(STATUS "Configuring xml2 library...") set(XML2_BUILD_DIR ${CMAKE_BINARY_DIR}/third_party/xml2-build) set(XML2_INSTALL_DIR ${CMAKE_BINARY_DIR}/third_party/xml2) file(MAKE_DIRECTORY ${XML2_BUILD_DIR}) set(CMAKE_SHARED_LINKER_FLAGS "${LINK_FLAGS} ${LINK_FLAGS_BUILD_ID} ${STRIP_FLAG}") set(XML2_SOURCE_DIR ${CMAKE_BINARY_DIR}/third_party/libxml2) if(NOT EXISTS ${CANGJIE_XML2_SOURCE_DIR}) if(NOT EXISTS ${XML2_SOURCE_DIR}) execute_process(COMMAND git clone --branch OpenHarmony-v6.0-Release --depth=1 https://gitcode.com/openharmony/third_party_libxml2.git ${XML2_SOURCE_DIR}) endif() message(STATUS "Uncompressing libxml2...") execute_process(COMMAND tar -C ${CMAKE_SOURCE_DIR}/third_party -xf ${XML2_SOURCE_DIR}/libxml2-2.14.0.tar.xz) endif() execute_process( COMMAND ${CMAKE_COMMAND} -G Ninja -DLIBXML2_WITH_PYTHON=OFF -DLIBXML2_WITH_ICONV=OFF -DLIBXML2_WITH_LZMA=OFF -DLIBXML2_WITH_ZLIB=OFF -DCUSTOM_WARNING_SETTINGS=-Wno-error -DCANGJIE_TARGET_TOOLCHAIN=${CANGJIE_TARGET_TOOLCHAIN} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} -DCMAKE_SYSROOT=${CANGJIE_TARGET_SYSROOT} -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON -DCMAKE_INSTALL_LIBDIR=lib -DCMAKE_BUILD_TYPE=Release -DCMAKE_SHARED_LINKER_FLAGS=${CMAKE_SHARED_LINKER_FLAGS} -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=${XML2_INSTALL_DIR} ${CANGJIE_XML2_SOURCE_DIR} WORKING_DIRECTORY ${XML2_BUILD_DIR} RESULT_VARIABLE config_result OUTPUT_VARIABLE config_stdout ERROR_VARIABLE config_stderr) if(NOT ${config_result} STREQUAL "0") message(STATUS "${config_stdout}") message(STATUS "${config_stderr}") message(FATAL_ERROR "Configuring xml2 Failed!") endif() message(STATUS "Building xml2 libraries...") execute_process( COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${XML2_BUILD_DIR} RESULT_VARIABLE build_result OUTPUT_VARIABLE build_stdout ERROR_VARIABLE build_stderr) if(NOT ${build_result} STREQUAL "0") message(STATUS "${config_stdout}") message(STATUS "${config_stderr}") message(STATUS "${build_stdout}") message(STATUS "${build_stderr}") message(FATAL_ERROR "Building xml2 Failed!") endif() message(STATUS "Installing xml2 libraries to Cangjie library source...") execute_process( COMMAND ${CMAKE_COMMAND} --install . WORKING_DIRECTORY ${XML2_BUILD_DIR} RESULT_VARIABLE install_result OUTPUT_VARIABLE install_stdout ERROR_VARIABLE install_stderr) if(NOT ${install_result} STREQUAL "0") message(STATUS "${config_stdout}") message(STATUS "${config_stderr}") message(STATUS "${build_stdout}") message(STATUS "${build_stderr}") message(STATUS "${install_stdout}") message(STATUS "${install_stderr}") message(FATAL_ERROR "Installing xml2 Failed!") endif() cangjie_compiler-1.0.7/third_party/llvmPatch.diff000066400000000000000000216240311510705540100221350ustar00rootroot00000000000000diff --git a/LICENSE.TXT b/LICENSE.TXT new file mode 100644 index 000000000..571517657 --- /dev/null +++ b/LICENSE.TXT @@ -0,0 +1,278 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign. +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. diff --git a/compiler-rt/CMakeLists.txt b/compiler-rt/CMakeLists.txt index 627377356..ed017f23d 100644 --- a/compiler-rt/CMakeLists.txt +++ b/compiler-rt/CMakeLists.txt @@ -51,7 +51,7 @@ option(COMPILER_RT_BUILD_MEMPROF "Build memory profiling runtime" ON) mark_as_advanced(COMPILER_RT_BUILD_MEMPROF) option(COMPILER_RT_BUILD_XRAY_NO_PREINIT "Build xray with no preinit patching" OFF) mark_as_advanced(COMPILER_RT_BUILD_XRAY_NO_PREINIT) -option(COMPILER_RT_BUILD_ORC "Build ORC runtime" ON) +option(COMPILER_RT_BUILD_ORC "Build ORC runtime" OFF) mark_as_advanced(COMPILER_RT_BUILD_ORC) option(COMPILER_RT_BUILD_GWP_ASAN "Build GWP-ASan, and link it into SCUDO" ON) mark_as_advanced(COMPILER_RT_BUILD_GWP_ASAN) diff --git a/compiler-rt/lib/builtins/CMakeLists.txt b/compiler-rt/lib/builtins/CMakeLists.txt index ec668e294..c2cc9b956 100644 --- a/compiler-rt/lib/builtins/CMakeLists.txt +++ b/compiler-rt/lib/builtins/CMakeLists.txt @@ -96,7 +96,6 @@ set(GENERIC_SOURCES divsi3.c divti3.c extendsfdf2.c - extendhfsf2.c ffsdi2.c ffssi2.c ffsti2.c @@ -165,9 +164,7 @@ set(GENERIC_SOURCES subvsi3.c subvti3.c trampoline_setup.c - truncdfhf2.c truncdfsf2.c - truncsfhf2.c ucmpdi2.c ucmpti2.c udivdi3.c @@ -307,6 +304,22 @@ if (NOT MSVC) x86_64/floatundisf.S ) + if (UNIX AND NOT APPLE) + set(x86_64_SOURCES + ${x86_64_SOURCES} + x86_64/extendhfsf2.S + x86_64/truncsfhf2.S + x86_64/truncdfhf2.S + ) + else() + set(x86_64_SOURCES + ${x86_64_SOURCES} + extendhfsf2.c + truncsfhf2.c + truncdfhf2.c + ) + endif() + if (NOT ANDROID) set(x86_64_SOURCES ${x86_64_SOURCES} diff --git a/compiler-rt/lib/builtins/x86_64/extendhfsf2.S b/compiler-rt/lib/builtins/x86_64/extendhfsf2.S new file mode 100644 index 000000000..7e1fd4b40 --- /dev/null +++ b/compiler-rt/lib/builtins/x86_64/extendhfsf2.S @@ -0,0 +1,107 @@ + .text + .file "extendhfsf2.c" + .section .text.__extendhfsf2,"ax",@progbits + .globl __extendhfsf2 # -- Begin function __extendhfsf2 + .p2align 4, 0x90 + .type __extendhfsf2,@function +__extendhfsf2: # @__extendhfsf2 +.L__extendhfsf2$local: + .cfi_startproc +# %bb.0: + pushq %rax + .cfi_def_cfa_offset 16 + movq %fs:40, %rax + movq %rax, (%rsp) + pextrw $0, %xmm0, %eax + movl %eax, %edx + andl $32767, %edx # imm = 0x7FFF + leal -1024(%rdx), %ecx + movzwl %cx, %ecx + cmpl $30719, %ecx # imm = 0x77FF + ja .LBB0_2 +# %bb.1: + shll $13, %edx + addl $939524096, %edx # imm = 0x38000000 + movq %fs:40, %rcx + cmpq (%rsp), %rcx + je .LBB0_8 + jmp .LBB0_9 +.LBB0_2: + cmpl $31744, %edx # imm = 0x7C00 + jb .LBB0_4 +# %bb.3: + shll $13, %edx + orl $2139095040, %edx # imm = 0x7F800000 + movq %fs:40, %rcx + cmpq (%rsp), %rcx + je .LBB0_8 + jmp .LBB0_9 +.LBB0_4: + testl %edx, %edx + je .LBB0_5 +# %bb.6: + bsrl %edx, %esi + xorl $31, %esi + leal -8(%rsi), %ecx + # kill: def $cl killed $cl killed $ecx + shll %cl, %edx + xorl $8388608, %edx # imm = 0x800000 + shll $23, %esi + movl $1124073472, %ecx # imm = 0x43000000 + subl %esi, %ecx + orl %edx, %ecx + movl %ecx, %edx + movq %fs:40, %rcx + cmpq (%rsp), %rcx + jne .LBB0_9 +.LBB0_8: + andl $32768, %eax # imm = 0x8000 + shll $16, %eax + orl %eax, %edx + movd %edx, %xmm0 + popq %rax + .cfi_def_cfa_offset 8 + retq +.LBB0_5: + .cfi_def_cfa_offset 16 + xorl %edx, %edx + movq %fs:40, %rcx + cmpq (%rsp), %rcx + je .LBB0_8 +.LBB0_9: + callq __stack_chk_fail@PLT +.Lfunc_end0: + .size __extendhfsf2, .Lfunc_end0-__extendhfsf2 + .cfi_endproc + # -- End function + .section .text.__gnu_h2f_ieee,"ax",@progbits + .globl __gnu_h2f_ieee # -- Begin function __gnu_h2f_ieee + .p2align 4, 0x90 + .type __gnu_h2f_ieee,@function +__gnu_h2f_ieee: # @__gnu_h2f_ieee +.L__gnu_h2f_ieee$local: + .cfi_startproc +# %bb.0: + pushq %rax + .cfi_def_cfa_offset 16 + movq %fs:40, %rax + movq %rax, (%rsp) + callq .L__extendhfsf2$local + movq %fs:40, %rax + cmpq (%rsp), %rax + jne .LBB1_2 +# %bb.1: + popq %rax + .cfi_def_cfa_offset 8 + retq +.LBB1_2: + .cfi_def_cfa_offset 16 + callq __stack_chk_fail@PLT +.Lfunc_end1: + .size __gnu_h2f_ieee, .Lfunc_end1-__gnu_h2f_ieee + .cfi_endproc + # -- End function + .ident "clang version 15.0.4" + .section ".note.GNU-stack","",@progbits + .addrsig + .addrsig_sym __stack_chk_fail diff --git a/compiler-rt/lib/builtins/x86_64/truncdfhf2.S b/compiler-rt/lib/builtins/x86_64/truncdfhf2.S new file mode 100644 index 000000000..a91b5ad50 --- /dev/null +++ b/compiler-rt/lib/builtins/x86_64/truncdfhf2.S @@ -0,0 +1,121 @@ + .text + .file "truncdfhf2.c" + .section .text.__truncdfhf2,"ax",@progbits + .globl __truncdfhf2 # -- Begin function __truncdfhf2 + .p2align 4, 0x90 + .type __truncdfhf2,@function +__truncdfhf2: # @__truncdfhf2 +.L__truncdfhf2$local: + .cfi_startproc +# %bb.0: + pushq %rax + .cfi_def_cfa_offset 16 + movq %fs:40, %rax + movq %rax, (%rsp) + movq %xmm0, %rax + movabsq $9223372036854775807, %rdx # imm = 0x7FFFFFFFFFFFFFFF + andq %rax, %rdx + movabsq $-4544132024016830464, %rcx # imm = 0xC0F0000000000000 + addq %rdx, %rcx + movabsq $-4679240012837945344, %rsi # imm = 0xBF10000000000000 + addq %rdx, %rsi + cmpq %rsi, %rcx + jae .LBB0_5 +# %bb.1: + movabsq $2199023255552, %rsi # imm = 0x20000000000 + movq %rax, %rdx + shrq $42, %rdx + movl %edx, %ecx + addl $16384, %ecx # imm = 0x4000 + movabsq $4398046511103, %rdi # imm = 0x3FFFFFFFFFF + andq %rax, %rdi + cmpq %rsi, %rdi + jbe .LBB0_3 +# %bb.2: + leal 16385(%rdx), %ecx + jmp .LBB0_14 +.LBB0_5: + movabsq $9218868437227405313, %rcx # imm = 0x7FF0000000000001 + cmpq %rcx, %rdx + jb .LBB0_7 +# %bb.6: + movq %rax, %rcx + shrq $42, %rcx + andl $511, %ecx # imm = 0x1FF + orl $32256, %ecx # imm = 0x7E00 + jmp .LBB0_14 +.LBB0_3: + jne .LBB0_14 +# %bb.4: + andl $1, %edx + addl %edx, %ecx + jmp .LBB0_14 +.LBB0_7: + shrq $52, %rdx + movw $31744, %cx # imm = 0x7C00 + cmpl $1038, %edx # imm = 0x40E + ja .LBB0_14 +# %bb.8: + cmpl $957, %edx # imm = 0x3BD + jae .LBB0_10 +# %bb.9: + xorl %ecx, %ecx + jmp .LBB0_14 +.LBB0_10: + movabsq $4503599627370495, %rcx # imm = 0xFFFFFFFFFFFFF + movq %rax, %rsi + andq %rcx, %rsi + addq %rcx, %rsi + incq %rsi + leal 79(%rdx), %ecx + movq %rsi, %rdi + # kill: def $cl killed $cl killed $ecx + shlq %cl, %rdi + xorl %r8d, %r8d + testq %rdi, %rdi + setne %r8b + movb $-15, %cl + subb %dl, %cl + shrq %cl, %rsi + movabsq $2199023255552, %rdx # imm = 0x20000000000 + movabsq $4398046511103, %rdi # imm = 0x3FFFFFFFFFF + andq %rsi, %rdi + movq %rsi, %rcx + shrq $42, %rcx + orq %r8, %rdi + cmpq %rdx, %rdi + jbe .LBB0_12 +# %bb.11: + movzwl %cx, %ecx + incl %ecx + jmp .LBB0_14 +.LBB0_12: + jne .LBB0_14 +# %bb.13: + movzwl %cx, %edx + movl %edx, %ecx + andl $1, %ecx + addl %edx, %ecx +.LBB0_14: + movq %fs:40, %rdx + cmpq (%rsp), %rdx + jne .LBB0_16 +# %bb.15: + shrq $48, %rax + andl $32768, %eax # imm = 0x8000 + orl %eax, %ecx + pinsrw $0, %ecx, %xmm0 + popq %rax + .cfi_def_cfa_offset 8 + retq +.LBB0_16: + .cfi_def_cfa_offset 16 + callq __stack_chk_fail@PLT +.Lfunc_end0: + .size __truncdfhf2, .Lfunc_end0-__truncdfhf2 + .cfi_endproc + # -- End function + .ident "clang version 15.0.4" + .section ".note.GNU-stack","",@progbits + .addrsig + .addrsig_sym __stack_chk_fail diff --git a/compiler-rt/lib/builtins/x86_64/truncsfhf2.S b/compiler-rt/lib/builtins/x86_64/truncsfhf2.S new file mode 100644 index 000000000..c98cda7ad --- /dev/null +++ b/compiler-rt/lib/builtins/x86_64/truncsfhf2.S @@ -0,0 +1,140 @@ + .text + .file "truncsfhf2.c" + .section .text.__truncsfhf2,"ax",@progbits + .globl __truncsfhf2 # -- Begin function __truncsfhf2 + .p2align 4, 0x90 + .type __truncsfhf2,@function +__truncsfhf2: # @__truncsfhf2 +.L__truncsfhf2$local: + .cfi_startproc +# %bb.0: + pushq %rax + .cfi_def_cfa_offset 16 + movq %fs:40, %rax + movq %rax, (%rsp) + movd %xmm0, %eax + movl %eax, %edx + andl $2147483647, %edx # imm = 0x7FFFFFFF + leal -947912704(%rdx), %ecx + leal -1199570944(%rdx), %esi + cmpl %esi, %ecx + jae .LBB0_5 +# %bb.1: + movl %eax, %edx + shrl $13, %edx + movzwl %dx, %ecx + movl %eax, %esi + andl $8191, %esi # imm = 0x1FFF + cmpl $4097, %esi # imm = 0x1001 + jb .LBB0_3 +# %bb.2: + addl $-114687, %ecx # imm = 0xFFFE4001 + jmp .LBB0_13 +.LBB0_5: + cmpl $2139095041, %edx # imm = 0x7F800001 + jb .LBB0_7 +# %bb.6: + movl %eax, %ecx + shrl $13, %ecx + andl $511, %ecx # imm = 0x1FF + orl $32256, %ecx # imm = 0x7E00 + jmp .LBB0_13 +.LBB0_3: + addl $-114688, %ecx # imm = 0xFFFE4000 + cmpl $4096, %esi # imm = 0x1000 + jne .LBB0_13 +# %bb.4: + movzwl %cx, %ecx + andl $1, %edx + addl %ecx, %edx + movl %edx, %ecx + jmp .LBB0_13 +.LBB0_7: + movl $31744, %ecx # imm = 0x7C00 + cmpl $1199570943, %edx # imm = 0x477FFFFF + ja .LBB0_13 +# %bb.8: + xorl %ecx, %ecx + cmpl $754974720, %edx # imm = 0x2D000000 + jb .LBB0_13 +# %bb.9: + shrl $23, %edx + movl %eax, %esi + andl $8388607, %esi # imm = 0x7FFFFF + orl $8388608, %esi # imm = 0x800000 + leal -81(%rdx), %ecx + movl %esi, %edi + # kill: def $cl killed $cl killed $ecx + shll %cl, %edi + xorl %r8d, %r8d + testl %edi, %edi + setne %r8b + movb $113, %cl + subb %dl, %cl + shrl %cl, %esi + movl %esi, %ecx + shrl $13, %ecx + andl $8191, %esi # imm = 0x1FFF + orl %r8d, %esi + cmpl $4097, %esi # imm = 0x1001 + jb .LBB0_11 +# %bb.10: + incl %ecx + jmp .LBB0_13 +.LBB0_11: + cmpl $4096, %esi # imm = 0x1000 + jne .LBB0_13 +# %bb.12: + movl %ecx, %edx + andl $1, %edx + addl %edx, %ecx +.LBB0_13: + movq %fs:40, %rdx + cmpq (%rsp), %rdx + jne .LBB0_15 +# %bb.14: + shrl $16, %eax + andl $32768, %eax # imm = 0x8000 + orl %eax, %ecx + pinsrw $0, %ecx, %xmm0 + popq %rax + .cfi_def_cfa_offset 8 + retq +.LBB0_15: + .cfi_def_cfa_offset 16 + callq __stack_chk_fail@PLT +.Lfunc_end0: + .size __truncsfhf2, .Lfunc_end0-__truncsfhf2 + .cfi_endproc + # -- End function + .section .text.__gnu_f2h_ieee,"ax",@progbits + .globl __gnu_f2h_ieee # -- Begin function __gnu_f2h_ieee + .p2align 4, 0x90 + .type __gnu_f2h_ieee,@function +__gnu_f2h_ieee: # @__gnu_f2h_ieee +.L__gnu_f2h_ieee$local: + .cfi_startproc +# %bb.0: + pushq %rax + .cfi_def_cfa_offset 16 + movq %fs:40, %rax + movq %rax, (%rsp) + callq .L__truncsfhf2$local + movq %fs:40, %rax + cmpq (%rsp), %rax + jne .LBB1_2 +# %bb.1: + popq %rax + .cfi_def_cfa_offset 8 + retq +.LBB1_2: + .cfi_def_cfa_offset 16 + callq __stack_chk_fail@PLT +.Lfunc_end1: + .size __gnu_f2h_ieee, .Lfunc_end1-__gnu_f2h_ieee + .cfi_endproc + # -- End function + .ident "clang version 15.0.4" + .section ".note.GNU-stack","",@progbits + .addrsig + .addrsig_sym __stack_chk_fail diff --git a/compiler-rt/lib/fuzzer/CMakeLists.txt b/compiler-rt/lib/fuzzer/CMakeLists.txt index a9a10f724..0854c7c92 100644 --- a/compiler-rt/lib/fuzzer/CMakeLists.txt +++ b/compiler-rt/lib/fuzzer/CMakeLists.txt @@ -58,6 +58,7 @@ CHECK_CXX_SOURCE_COMPILES(" " HAS_THREAD_LOCAL) set(LIBFUZZER_CFLAGS ${COMPILER_RT_COMMON_CFLAGS}) +list(APPEND LIBFUZZER_CFLAGS -fstack-protector-all) if(OS_NAME MATCHES "Linux|Fuchsia" AND COMPILER_RT_LIBCXX_PATH AND diff --git a/compiler-rt/lib/fuzzer/FuzzerExtFunctions.def b/compiler-rt/lib/fuzzer/FuzzerExtFunctions.def index 51edf8444..161fdaedb 100644 --- a/compiler-rt/lib/fuzzer/FuzzerExtFunctions.def +++ b/compiler-rt/lib/fuzzer/FuzzerExtFunctions.def @@ -48,3 +48,7 @@ EXT_FUNC(__msan_scoped_disable_interceptor_checks, void, (), false); EXT_FUNC(__msan_scoped_enable_interceptor_checks, void, (), false); EXT_FUNC(__msan_unpoison, void, (const volatile void *, size_t size), false); EXT_FUNC(__msan_unpoison_param, void, (size_t n), false); +EXT_FUNC(__sanitizer_cangjie_symbolize_pc, int, (void *, const char *fmt, char *out_buf, size_t out_buf_size), true); +EXT_FUNC(__sanitizer_cangjie_get_module_and_offset_for_pc, int, + (void *pc, char *module_path, + size_t module_path_len,void **pc_offset), true); diff --git a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp index f09575722..b8cf759e1 100644 --- a/compiler-rt/lib/fuzzer/FuzzerLoop.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerLoop.cpp @@ -852,7 +852,7 @@ void Fuzzer::ReadAndExecuteSeedCorpora(std::vector &CorporaFiles) { // so we add one fake input to the in-memory corpus. Corpus.AddToCorpus({'\n'}, /*NumFeatures=*/1, /*MayDeleteFile=*/true, /*HasFocusFunction=*/false, /*NeverReduce=*/false, - /*TimeOfUnit=*/duration_cast(0s), {0}, DFT, + /*TimeOfUnit=*/std::chrono::microseconds(0), {0}, DFT, /*BaseII*/ nullptr); } } diff --git a/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp b/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp index f12f7aa61..d271759a7 100644 --- a/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerTracePC.cpp @@ -224,7 +224,13 @@ const TracePC::PCTableEntry *TracePC::PCTableEntryByIdx(uintptr_t Idx) { static std::string GetModuleName(uintptr_t PC) { char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++? void *OffsetRaw = nullptr; - if (!EF->__sanitizer_get_module_and_offset_for_pc( + if (EF->__sanitizer_cangjie_get_module_and_offset_for_pc && + EF->__sanitizer_cangjie_get_module_and_offset_for_pc( + reinterpret_cast(PC), ModulePathRaw, + sizeof(ModulePathRaw), &OffsetRaw)) + return ModulePathRaw; + if (EF->__sanitizer_get_module_and_offset_for_pc && + !EF->__sanitizer_get_module_and_offset_for_pc( reinterpret_cast(PC), ModulePathRaw, sizeof(ModulePathRaw), &OffsetRaw)) return ""; @@ -280,11 +286,20 @@ bool TracePC::ObservedFocusFunction() { } void TracePC::PrintCoverage(bool PrintAllCounters) { - if (!EF->__sanitizer_symbolize_pc || - !EF->__sanitizer_get_module_and_offset_for_pc) { - Printf("INFO: __sanitizer_symbolize_pc or " - "__sanitizer_get_module_and_offset_for_pc is not available," - " not printing coverage\n"); + bool missing_symbolize = !EF->__sanitizer_symbolize_pc || !EF->__sanitizer_get_module_and_offset_for_pc; + bool missing_cangjie_symbolize = !EF->__sanitizer_cangjie_symbolize_pc || + !EF->__sanitizer_cangjie_get_module_and_offset_for_pc; + if (missing_symbolize) + Printf("INFO: __sanitizer_symbolize_pc or " + "__sanitizer_get_module_and_offset_for_pc is not available," + " missing C-code coverage\n"); + if (missing_cangjie_symbolize) + Printf("INFO: __sanitizer_cangjie_symbolize_pc or " + "__sanitizer_cangjie_get_module_and_offset_for_pc is not available," + " missing cangjie coverage\n"); + if (missing_symbolize && missing_cangjie_symbolize) { + Printf("INFO: All symbolize functions are missing,\n" + " not printing coverage\n"); return; } Printf(PrintAllCounters ? "FULL COVERAGE:\n" : "COVERAGE:\n"); diff --git a/compiler-rt/lib/fuzzer/FuzzerUtil.cpp b/compiler-rt/lib/fuzzer/FuzzerUtil.cpp index aeab70f20..0cdf6930e 100644 --- a/compiler-rt/lib/fuzzer/FuzzerUtil.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerUtil.cpp @@ -188,17 +188,25 @@ static std::mutex SymbolizeMutex; std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC) { std::unique_lock l(SymbolizeMutex, std::try_to_lock); - if (!EF->__sanitizer_symbolize_pc || !l.owns_lock()) + if (!l.owns_lock()) return ""; char PcDescr[1024] = {}; - EF->__sanitizer_symbolize_pc(reinterpret_cast(PC), - SymbolizedFMT, PcDescr, sizeof(PcDescr)); + // Use cangjie symbolize first. If fail, fallback to default symbolize + if (EF->__sanitizer_cangjie_symbolize_pc) { + if (EF->__sanitizer_cangjie_symbolize_pc(reinterpret_cast(PC), SymbolizedFMT, PcDescr, sizeof(PcDescr))) { + PcDescr[sizeof(PcDescr) - 1] = 0; // Just in case. + return PcDescr; + } + } + if (EF->__sanitizer_symbolize_pc) { + EF->__sanitizer_symbolize_pc(reinterpret_cast(PC), SymbolizedFMT, PcDescr, sizeof(PcDescr)); + } PcDescr[sizeof(PcDescr) - 1] = 0; // Just in case. return PcDescr; } void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC) { - if (EF->__sanitizer_symbolize_pc) + if (EF->__sanitizer_symbolize_pc || EF->__sanitizer_cangjie_symbolize_pc) Printf("%s", DescribePC(SymbolizedFMT, PC).c_str()); else Printf(FallbackFMT, PC); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp index d74851c43..c15456e39 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp @@ -93,7 +93,7 @@ SANITIZER_WEAK_ATTRIBUTE int real_sigaction(int signum, const void *act, void *oldact); int internal_sigaction(int signum, const void *act, void *oldact) { -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ if (&real_sigaction) return real_sigaction(signum, act, oldact); #endif diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_mutex.h b/compiler-rt/lib/sanitizer_common/sanitizer_mutex.h index b1a58e421..ed016b88c 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_mutex.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_mutex.h @@ -101,7 +101,7 @@ enum { // THREADLOCAL variables they are not usable early on during process init when // `__sanitizer::Mutex` is used. #define SANITIZER_CHECK_DEADLOCKS \ - (SANITIZER_DEBUG && !SANITIZER_GO && SANITIZER_SUPPORTS_THREADLOCAL && !SANITIZER_APPLE) + (SANITIZER_DEBUG && !SANITIZER_GO && !SANITIZER_CJ && SANITIZER_SUPPORTS_THREADLOCAL && !SANITIZER_APPLE) #if SANITIZER_CHECK_DEADLOCKS struct MutexMeta { diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp index 46e41c669..71a753372 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp @@ -392,7 +392,7 @@ int my_pthread_attr_getstack(void *attr, void **addr, uptr *size) { return pthread_attr_getstack((pthread_attr_t *)attr, addr, (size_t *)size); } -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ void AdjustStackSize(void *attr_) { pthread_attr_t *attr = (pthread_attr_t *)attr_; uptr stackaddr = 0; diff --git a/compiler-rt/lib/tsan/CMakeLists.txt b/compiler-rt/lib/tsan/CMakeLists.txt index a327fb14d..5167a833e 100644 --- a/compiler-rt/lib/tsan/CMakeLists.txt +++ b/compiler-rt/lib/tsan/CMakeLists.txt @@ -22,6 +22,7 @@ if (TSAN_USE_OLD_RUNTIME) else() add_subdirectory(rtl) endif() +add_subdirectory(cangjie) # Build libcxx instrumented with TSan. if(COMPILER_RT_LIBCXX_PATH AND diff --git a/compiler-rt/lib/tsan/cangjie/CMakeLists.txt b/compiler-rt/lib/tsan/cangjie/CMakeLists.txt new file mode 100644 index 000000000..c071dba9d --- /dev/null +++ b/compiler-rt/lib/tsan/cangjie/CMakeLists.txt @@ -0,0 +1,106 @@ +set(COMMON_TSAN_SOURCE + tsan_cangjie.cpp + ../rtl/tsan_external.cpp + ../rtl/tsan_flags.cpp + ../rtl/tsan_interface_atomic.cpp + ../rtl/tsan_md5.cpp + ../rtl/tsan_mutexset.cpp + ../rtl/tsan_report.cpp + ../rtl/tsan_rtl.cpp + ../rtl/tsan_rtl_access.cpp + ../rtl/tsan_rtl_mutex.cpp + ../rtl/tsan_rtl_report.cpp + ../rtl/tsan_rtl_thread.cpp + ../rtl/tsan_rtl_proc.cpp + ../rtl/tsan_stack_trace.cpp + ../rtl/tsan_suppressions.cpp + ../rtl/tsan_symbolize.cpp + ../rtl/tsan_sync.cpp + ../rtl/tsan_vector_clock.cpp + ../../sanitizer_common/sanitizer_allocator.cpp + ../../sanitizer_common/sanitizer_common.cpp + ../../sanitizer_common/sanitizer_common_libcdep.cpp + ../../sanitizer_common/sanitizer_deadlock_detector2.cpp + ../../sanitizer_common/sanitizer_file.cpp + ../../sanitizer_common/sanitizer_flag_parser.cpp + ../../sanitizer_common/sanitizer_flags.cpp + ../../sanitizer_common/sanitizer_libc.cpp + ../../sanitizer_common/sanitizer_mutex.cpp + ../../sanitizer_common/sanitizer_printf.cpp + ../../sanitizer_common/sanitizer_suppressions.cpp + ../../sanitizer_common/sanitizer_stack_store.cpp + ../../sanitizer_common/sanitizer_stackdepot.cpp + ../../sanitizer_common/sanitizer_stacktrace.cpp + ../../sanitizer_common/sanitizer_stacktrace_libcdep.cpp + ../../sanitizer_common/sanitizer_symbolizer.cpp + ../../sanitizer_common/sanitizer_symbolizer_libcdep.cpp + ../../sanitizer_common/sanitizer_symbolizer_report.cpp + ../../sanitizer_common/sanitizer_symbolizer_libbacktrace.cpp + ../../sanitizer_common/sanitizer_termination.cpp + ../../sanitizer_common/sanitizer_thread_registry.cpp + ../../sanitizer_common/sanitizer_stacktrace_printer.cpp +) + +set(CJ_TSAN_CFLAG -Wall -fno-exceptions -fno-rtti -fno-omit-frame-pointer -DSANITIZER_CJ=1 -DSANITIZER_DEADLOCK_DETECTOR_VERSION=2) + +string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) +if (uppercase_CMAKE_BUILD_TYPE MATCHES "DEBUG") + set(CJ_TSAN_CFLAG ${CJ_TSAN_CFLAG} -DSANITIZER_DEBUG=1 -g) +else () + set(CJ_TSAN_CFLAG ${CJ_TSAN_CFLAG} -DSANITIZER_DEBUG=0 -O3) +endif () + +if (OS_NAME MATCHES "Linux") + set(CJ_TSAN_CFLAG ${CJ_TSAN_CFLAG} -fPIC -Wno-uninitialized -Wno-suggest-override -Wno-format-pedantic -Wno-c99-extensions -Wno-gnu-zero-variadic-macro-arguments -Wno-unused-function) + set(CJ_TSAN_LDFLAG -lpthread -fPIC -fpie) + set(TSAN_SOURCE + ${COMMON_TSAN_SOURCE} + ../rtl/tsan_platform_linux.cpp + ../rtl/tsan_platform_posix.cpp + ../../sanitizer_common/sanitizer_linux.cpp + ../../sanitizer_common/sanitizer_linux_libcdep.cpp + ../../sanitizer_common/sanitizer_platform_limits_posix.cpp + ../../sanitizer_common/sanitizer_posix.cpp + ../../sanitizer_common/sanitizer_posix_libcdep.cpp + ../../sanitizer_common/sanitizer_procmaps_common.cpp + ../../sanitizer_common/sanitizer_procmaps_linux.cpp + ../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp + ../../sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp + ../../sanitizer_common/sanitizer_unwind_linux_libcdep.cpp + ) + + if ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") + set(CJ_TSAN_CFLAG ${CJ_TSAN_CFLAG} -m64 -msse3) + set(CJ_TSAN_LDFLAG ${CJ_TSAN_LDFLAG} -ffreestanding -Wno-unused-const-variable -Wno-unknown-warning-option) + elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64") + + else() + message(WARNING "TSan for cangjie is not support on ${CMAKE_SYSTEM_PROCESSOR} for now") + return() + endif () + set(libname clang_rt.tsan_cangjie-${CMAKE_SYSTEM_PROCESSOR}) +else () + message(WARNING "TSan for cangjie is not support on ${OS_NAME} for now") + return() +endif () + +add_library(${libname} STATIC ${TSAN_SOURCE}) +target_include_directories(${libname} PRIVATE ../rtl) +target_include_directories(${libname} PRIVATE ../../sanitizer_common) +target_include_directories(${libname} PRIVATE ../../) +target_include_directories(${libname} PRIVATE ../../../include) +target_compile_options(${libname} PRIVATE ${CJ_TSAN_CFLAG}) +target_link_options(${libname} PRIVATE ${CJ_TSAN_LDFLAG}) +get_compiler_rt_install_dir(${CMAKE_SYSTEM_PROCESSOR} install_dir_tsan_cangjie) + +add_sanitizer_rt_symbols(clang_rt.tsan_cangjie + ARCHS ${CMAKE_SYSTEM_PROCESSOR} + EXTRA ../rtl/tsan.syms.extra) + +install(TARGETS ${libname} + ARCHIVE DESTINATION ${install_dir_tsan_cangjie} + COMPONENT ${libname} + LIBRARY DESTINATION ${install_dir_tsan_cangjie} + COMPONENT ${libname} + RUNTIME DESTINATION ${install_dir_tsan_cangjie} + COMPONENT ${libname}) diff --git a/compiler-rt/lib/tsan/cangjie/tsan_cangjie.cpp b/compiler-rt/lib/tsan/cangjie/tsan_cangjie.cpp new file mode 100644 index 000000000..6d5baa57a --- /dev/null +++ b/compiler-rt/lib/tsan/cangjie/tsan_cangjie.cpp @@ -0,0 +1,248 @@ +//===-- tsan_cangjie.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 +// +//===----------------------------------------------------------------------===// +// +// ThreadSanitizer runtime for Cangjie. +// +//===----------------------------------------------------------------------===// +#include "tsan_rtl.h" +#include "tsan_symbolize.h" +#include "sanitizer_common/sanitizer_common.h" +#include "tsan_interface.h" + +extern "C" { +__tsan::Processor *CJ_MCC_TsanGetRaceProc(void) __attribute__((weak)); +} + +namespace __tsan { + +void InitializeInterceptors() {} + +void InitializeDynamicAnnotations() {} + +bool IsExpectedReport(uptr addr, uptr size) { + return false; +} + +void *Alloc(uptr sz) { + return InternalAlloc(sz); +} + +void FreeImpl(void *p) { + InternalFree(p); +} + +static Processor* get_cur_proc() { + Processor *proc = CJ_MCC_TsanGetRaceProc(); + return proc; +} + +Processor *ThreadState::proc() { + return get_cur_proc(); +} + +extern void MemoryAccess16(ThreadState* thr, uptr pc, uptr addr, AccessType typ); + +extern "C" { +static ThreadState *AllocThreadState() { + auto *thr = static_cast(Alloc(sizeof(ThreadState))); + internal_memset(thr, 0, sizeof(ThreadState)); + return thr; +} + +ThreadState *__tsan_init() { + ThreadState *thr = AllocThreadState(); + Initialize(thr); + return thr; +} + +void __tsan_fini() { + ThreadState *thr = CJ_MCC_TsanGetThreadState(); + if (thr == nullptr) { + return; + } + Finalize(thr); +} + +void __tsan_init_shadow(uptr addr, uptr size) { + // `MapShadow` needs to map memory from low to high address + MapCangjieShadow(addr, size); +} + +void __tsan_read(uptr pc, uptr addr, uptr size) { + ThreadState *thr = CJ_MCC_TsanGetThreadState(); + if (thr == nullptr) { + return; + } + AccessType type = kAccessRead; + if (size == 1 || size == 2 || size == 4 || size == 8) { + MemoryAccess(thr, pc, addr, size, type); + } else if (size == 16) { + MemoryAccess16(thr, pc, addr, type); + } else { + MemoryAccessRange(thr, pc, addr, size, false); + } +} + +void __tsan_write(uptr pc, uptr addr, uptr size) { + ThreadState *thr = CJ_MCC_TsanGetThreadState(); + if (thr == nullptr) { + return; + } + AccessType type = kAccessWrite; + if (size == 1 || size == 2 || size == 4 || size == 8) { + MemoryAccess(thr, pc, addr, size, type); + } else if (size == 16) { + MemoryAccess16(thr, pc, addr, type); + } else { + MemoryAccessRange(thr, pc, addr, size, true); + } +} + +void __tsan_read_range(uptr pc, uptr addr, uptr size) { + ThreadState *thr = CJ_MCC_TsanGetThreadState(); + if (thr == nullptr) { + return; + } + MemoryAccessRange(thr, pc, addr, size, false); +} + +void __tsan_write_range(uptr pc, uptr addr, uptr size) { + ThreadState *thr = CJ_MCC_TsanGetThreadState(); + if (thr == nullptr) { + return; + } + MemoryAccessRange(thr, pc, addr, size, true); +} + +void __tsan_func_entry(uptr pc) { + ThreadState *thr = CJ_MCC_TsanGetThreadState(); + if (thr == nullptr) { + return; + } + FuncEntry(thr, pc); +} + +void __tsan_func_exit() { + ThreadState *thr = CJ_MCC_TsanGetThreadState(); + if (thr == nullptr) { + return; + } + FuncExit(thr); +} + +void __tsan_func_restore_context(uptr pc) { + ThreadState *thr = CJ_MCC_TsanGetThreadState(); + if (thr == nullptr) { + return; + } + + // check if current exit function is instrumented + if (pc != thr->shadow_stack_pos[-1]) { + return; + } + FuncExit(thr); +} + +void __tsan_alloc(uptr pc, uptr p, uptr sz) { + ThreadState *thr = CJ_MCC_TsanGetThreadState(); + if (thr == nullptr) { + return; + } + MemoryResetRange(thr, pc, p, sz); +} + +void __tsan_clean_shadow(uptr p, uptr sz) { + RawShadow* s = MemToShadow(RoundDownTo(p, kShadowCell)); + RawShadow* e = MemToShadow(RoundUpTo(p + sz, kShadowCell)); + ShadowSet(s, e, Shadow::kEmpty); +} + +void __tsan_free(uptr pc, uptr p, uptr sz) { + ThreadState *thr = CJ_MCC_TsanGetThreadState(); + if (thr == nullptr) { + return; + } + ctx->metamap.FreeRange(get_cur_proc(), p, sz, false); + __tsan_clean_shadow(p, sz); +} + +ThreadState *__tsan_state_create(ThreadState *parent, uptr pc) { + ThreadState *thr = AllocThreadState(); + Tid tid = ThreadCreate(parent, pc, 0, true); + ThreadStart(thr, tid, 0, ThreadType::Regular); + return thr; +} + +void __tsan_state_delete(ThreadState *thr) { + if (!thr) { + return; + } + ThreadFinish(thr); + Free(thr); +} + +Processor *__tsan_proc_create(void) { + return ProcCreate(); +} + +void __tsan_proc_destroy(Processor *proc) { + ProcDestroy(proc); +} + +void __tsan_acquire(ThreadState *thr, uptr addr) { + if (thr == nullptr) { + return; + } + Acquire(thr, 0, addr); +} + +void __tsan_release_acquire(ThreadState *thr, uptr addr) { + if (thr == nullptr) { + return; + } + ReleaseStoreAcquire(thr, 0, addr); +} + +void __tsan_release(ThreadState *thr, uptr addr) { + if (thr == nullptr) { + return; + } + ReleaseStore(thr, 0, addr); +} + +void __tsan_release_merge(ThreadState *thr, uptr addr) { + if (thr == nullptr) { + return; + } + Release(thr, 0, addr); +} + +void __tsan_fix_shadow(uptr from, uptr to, uptr size) { + size = RoundDownTo(size, kShadowCell); + + RawShadow* fs = MemToShadow(from); + RawShadow* ts = MemToShadow(to); + RawShadow* fend = MemToShadow(from + size); + uptr inc = 1; + + if (to > from) { + // avoid corruption when `from` overlapped with `to` + fs = MemToShadow(from + size) - 1; + ts = MemToShadow(to + size) - 1; + fend = MemToShadow(from) - 1; + inc = -1; + } + + for (; fs != fend; fs += inc, ts += inc) { + *ts = *fs; + } + + TraceFixAll(from, to, size); +} +} // extern "C" +} // namespace __tsan diff --git a/compiler-rt/lib/tsan/rtl/tsan_defs.h b/compiler-rt/lib/tsan/rtl/tsan_defs.h index 1ffa3d6ae..2401a4575 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_defs.h +++ b/compiler-rt/lib/tsan/rtl/tsan_defs.h @@ -42,7 +42,7 @@ typedef __m128i m128; #endif #ifndef TSAN_CONTAINS_UBSAN -# if CAN_SANITIZE_UB && !SANITIZER_GO +# if CAN_SANITIZE_UB && !SANITIZER_GO && !SANITIZER_CJ # define TSAN_CONTAINS_UBSAN 1 # else # define TSAN_CONTAINS_UBSAN 0 diff --git a/compiler-rt/lib/tsan/rtl/tsan_external.cpp b/compiler-rt/lib/tsan/rtl/tsan_external.cpp index 19ae174f2..1dc510526 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_external.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_external.cpp @@ -12,7 +12,7 @@ #include "tsan_rtl.h" #include "sanitizer_common/sanitizer_ptrauth.h" -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ # include "tsan_interceptors.h" #endif @@ -58,7 +58,7 @@ uptr TagFromShadowStackFrame(uptr pc) { return (TagData *)pc_ptr - GetTagData(0); } -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ void ExternalAccess(void *addr, uptr caller_pc, void *tag, AccessType typ) { CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed)); diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface.h b/compiler-rt/lib/tsan/rtl/tsan_interface.h index 711f06417..1a19bde9f 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_interface.h +++ b/compiler-rt/lib/tsan/rtl/tsan_interface.h @@ -26,7 +26,7 @@ using __sanitizer::tid_t; extern "C" { #endif -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ // This function should be called at the very beginning of the process, // before any instrumented code is executed and before any call to malloc. diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp b/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp index f794a2fcd..2607284b3 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_interface_atomic.cpp @@ -485,6 +485,16 @@ static morder convert_morder(morder mo) { return (morder)(mo & 0x7fff); } +#if SANITIZER_CJ +# define ATOMIC_IMPL(func, ...) \ + do { \ + ThreadState *const thr = cur_thread(); \ + if (UNLIKELY(!thr || thr->ignore_sync || thr->ignore_interceptors)) \ + return NoTsanAtomic##func(__VA_ARGS__); \ + mo = convert_morder(mo); \ + return Atomic##func(thr, GET_CALLER_PC(), __VA_ARGS__); \ + } while (0) +#else # define ATOMIC_IMPL(func, ...) \ ThreadState *const thr = cur_thread(); \ ProcessPendingSignals(thr); \ @@ -492,6 +502,7 @@ static morder convert_morder(morder mo) { return NoTsanAtomic##func(__VA_ARGS__); \ mo = convert_morder(mo); \ return Atomic##func(thr, GET_CALLER_PC(), __VA_ARGS__); +#endif extern "C" { SANITIZER_INTERFACE_ATTRIBUTE diff --git a/compiler-rt/lib/tsan/rtl/tsan_platform.h b/compiler-rt/lib/tsan/rtl/tsan_platform.h index 7c13c7335..a014bcabd 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_platform.h +++ b/compiler-rt/lib/tsan/rtl/tsan_platform.h @@ -864,6 +864,9 @@ inline uptr RestoreAddr(uptr addr) { void InitializePlatform(); void InitializePlatformEarly(); void CheckAndProtect(); +#if SANITIZER_CJ +void MapCangjieShadow(uptr heap, u64 size); +#endif void InitializeShadowMemoryPlatform(); void WriteMemoryProfile(char *buf, uptr buf_size, u64 uptime_ns); int ExtractResolvFDs(void *state, int *fds, int nfd); diff --git a/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp b/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp index 807f6be2e..b37545cf9 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp @@ -148,7 +148,33 @@ void WriteMemoryProfile(char *buf, uptr buf_size, u64 uptime_ns) { stacks.allocated >> 20, nlive, nthread); } -#if !SANITIZER_GO +#if SANITIZER_CJ +void MapCangjieShadow(uptr heap, u64 size) { + char name[256]; + uptr last = 0; + bool heap_mapped = false; + MemoryMappingLayout proc_maps(true); + MemoryMappedSegment segment(name, ARRAY_SIZE(name)); + while (proc_maps.Next(&segment)) { + if (segment.filename[0] != 0 && segment.filename[0] != '[' && segment.IsWritable()) { + if (!heap_mapped && last < heap && segment.start > heap) { + MapShadow(heap, size); + heap_mapped = true; + } + MapShadow(segment.start, segment.end - segment.start); + last = segment.start; + } + } + if (!heap_mapped) { + MapShadow(heap, size); + } +} + +void InitializeShadowMemoryPlatform() { +} +#endif + +#if !SANITIZER_GO && !SANITIZER_CJ // Mark shadow for .rodata sections with the special Shadow::kRodata marker. // Accesses to .rodata can't race, so this saves time, memory and trace space. static void MapRodata() { @@ -211,7 +237,7 @@ void InitializeShadowMemoryPlatform() { MapRodata(); } -#endif // #if !SANITIZER_GO +#endif // #if !SANITIZER_GO && !SANITIZER_CJ void InitializePlatformEarly() { vmaSize = @@ -267,7 +293,7 @@ void InitializePlatform() { // Go maps shadow memory lazily and works fine with limited address space. // Unlimited stack is not a problem as well, because the executable // is not compiled with -pie. -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ { bool reexec = false; // TSan doesn't play well with unlimited stack size (as stack @@ -475,7 +501,7 @@ int call_pthread_cancel_with_cleanup(int (*fn)(void *arg), } #endif // !SANITIZER_GO -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ void ReplaceSystemMalloc() { } #endif diff --git a/compiler-rt/lib/tsan/rtl/tsan_platform_posix.cpp b/compiler-rt/lib/tsan/rtl/tsan_platform_posix.cpp index 71874aad8..d13db7a53 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_platform_posix.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_platform_posix.cpp @@ -79,6 +79,7 @@ void InitializeShadowMemory() { reinterpret_cast(dlsym(RTLD_DEFAULT, "__tsan_on_finalize")); } +#if !SANITIZER_CJ static bool TryProtectRange(uptr beg, uptr end) { CHECK_LE(beg, end); if (beg == end) @@ -137,6 +138,7 @@ void CheckAndProtect() { #endif } #endif +#endif } // namespace __tsan diff --git a/compiler-rt/lib/tsan/rtl/tsan_report.cpp b/compiler-rt/lib/tsan/rtl/tsan_report.cpp index 3b0bddc33..f7f2b89f3 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_report.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_report.cpp @@ -54,11 +54,17 @@ ReportDesc::~ReportDesc() { const int kThreadBufSize = 32; const char *thread_name(char *buf, Tid tid) { if (tid == kMainTid) +#if SANITIZER_CJ + return "main cjthread"; + internal_snprintf(buf, kThreadBufSize, "cjthread T%d", tid); +#else return "main thread"; internal_snprintf(buf, kThreadBufSize, "thread T%d", tid); +#endif return buf; } + static const char *ReportTypeString(ReportType typ, uptr tag) { switch (typ) { case ReportTypeRace: @@ -82,7 +88,11 @@ static const char *ReportTypeString(ReportType typ, uptr tag) { case ReportTypeMutexInvalidAccess: return "use of an invalid mutex (e.g. uninitialized or destroyed)"; case ReportTypeMutexBadUnlock: +#if SANITIZER_CJ + return "unlock of an unlocked mutex (or by a wrong cjthread)"; +#else return "unlock of an unlocked mutex (or by a wrong thread)"; +#endif case ReportTypeMutexBadReadLock: return "read lock of a write locked mutex"; case ReportTypeMutexBadReadUnlock: @@ -235,7 +245,11 @@ static void PrintThread(const ReportThread *rt) { if (rt->id == kMainTid) // Little sense in describing the main thread. return; Printf("%s", d.ThreadDescription()); +#if SANITIZER_CJ + Printf(" CJThread T%d", rt->id); +#else Printf(" Thread T%d", rt->id); +#endif if (rt->name && rt->name[0] != '\0') Printf(" '%s'", rt->name); char thrbuf[kThreadBufSize]; diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp index ff3bb33eb..ffbc82bba 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp @@ -45,7 +45,7 @@ void (*on_initialize)(void); int (*on_finalize)(int); #endif -#if !SANITIZER_GO && !SANITIZER_APPLE +#if !SANITIZER_GO && !SANITIZER_CJ && !SANITIZER_APPLE __attribute__((tls_model("initial-exec"))) THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED( SANITIZER_CACHE_LINE_SIZE); @@ -76,6 +76,114 @@ void OnInitialize() { } #endif +#if SANITIZER_CJ +template +uptr TraceGetEventMemSize(T *ev) { + return 1 << ev->size_log; +} + +template< > +uptr TraceGetEventMemSize(EventAccessRange *ev) { + return (ev->size_hi << EventAccessRange::kSizeLoBits) + ev->size_lo; +} + +template< > +uptr TraceGetEventMemSize(EventLock *ev) { + return 0; +} + +template< > +uptr TraceGetEventMemSize(EventUnlock *ev) { + return 0; +} + +template +void TraceDoFix(T *ev, uptr from, uptr to, uptr size) { + uptr ev_addr = RestoreAddr(ev->addr); + uptr ev_size = TraceGetEventMemSize(ev); + if (!(from <= ev_addr && (from + size) >= (ev_addr + ev_size))) + return; + + uptr newAddr = ev_addr - from + to; + ev->addr = CompressAddr(newAddr); +} + +void TraceFixEvent(TracePart *part, Event *end, uptr from, uptr to, uptr size) { + for (Event *evp = &part->events[0]; evp < end; evp++) { + if (evp->is_access) { + if (evp->is_func == 0 && evp->type == EventType::kAccessExt && + evp->_ == 0) // NopEvent + continue; + auto *ev = reinterpret_cast(evp); + TraceDoFix(ev, from, to, size); + continue; + } else if (evp->is_func) { + continue; + } + switch (evp->type) { + case EventType::kAccessExt: { + auto *ev = reinterpret_cast(evp); + evp++; + TraceDoFix(ev, from, to, size); + break; + } + case EventType::kAccessRange: { + auto *ev = reinterpret_cast(evp); + evp++; + TraceDoFix(ev, from, to, size); + break; + } + case EventType::kLock: + FALLTHROUGH; + case EventType::kRLock: { + auto *ev = reinterpret_cast(evp); + evp++; + TraceDoFix(ev, from, to, size); + break; + } + case EventType::kUnlock: { + auto *ev = reinterpret_cast(evp); + TraceDoFix(ev, from, to, size); + break; + } + default: { + break; + } + } + } +} + +void TraceFixAll(uptr from, uptr to, uptr size) { + ThreadRegistryLock lock0(&ctx->thread_registry); + + for (u32 i = 0; i < ctx->thread_registry.NumThreadsLocked(); i++) { + ThreadContext* tctx = (ThreadContext*)ctx->thread_registry.GetThreadLocked( + static_cast(i)); + auto trace = &tctx->trace; + Lock lock(&trace->mtx); + + TracePart *part = trace->parts.Front(); + if (!part) + continue; + + TracePart *last_part = trace->parts.Back(); + Event *last_pos = trace->final_pos; + if (tctx->thr) + last_pos = (Event *)atomic_load_relaxed(&tctx->thr->trace_pos); + for (;;) { + Event *end = &part->events[TracePart::kSize - 1]; + if (part == last_part) + end = last_pos; + TraceFixEvent(part, end, from, to, size); + + if (part == last_part) + break; + part = trace->parts.Next(part); + } + } +} +#endif + static TracePart* TracePartAlloc(ThreadState* thr) { TracePart* part = nullptr; { @@ -407,7 +515,7 @@ ThreadState::ThreadState(Tid tid) // ignore_interceptors() : tid(tid) { CHECK_EQ(reinterpret_cast(this) % SANITIZER_CACHE_LINE_SIZE, 0); -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ // C/C++ uses fixed size shadow stack. const int kInitStackSize = kShadowStackSize; shadow_stack = static_cast( @@ -423,7 +531,7 @@ ThreadState::ThreadState(Tid tid) shadow_stack_end = shadow_stack + kInitStackSize; } -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ void MemoryProfiler(u64 uptime) { if (ctx->memprof_fd == kInvalidFd) return; @@ -531,7 +639,7 @@ void DontNeedShadowFor(uptr addr, uptr size) { reinterpret_cast(MemToShadow(addr + size))); } -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ // We call UnmapShadow before the actual munmap, at that point we don't yet // know if the provided address/size are sane. We can't call UnmapShadow // after the actual munmap becuase at that point the memory range can @@ -620,8 +728,14 @@ void MapShadow(uptr addr, uptr size) { static uptr mapped_meta_end = 0; uptr meta_begin = (uptr)MemToMeta(addr); uptr meta_end = (uptr)MemToMeta(addr + size); +#if SANITIZER_CJ && defined(__linux__) + // 64K is too large and may cause collision on linux + meta_begin = RoundDownTo(meta_begin, GetPageSizeCached()); + meta_end = RoundUpTo(meta_end, GetPageSizeCached()); +#else meta_begin = RoundDownTo(meta_begin, 64 << 10); meta_end = RoundUpTo(meta_end, 64 << 10); +#endif if (!data_mapped) { // First call maps data+bss. data_mapped = true; @@ -629,10 +743,15 @@ void MapShadow(uptr addr, uptr size) { "meta shadow")) Die(); } else { +#if SANITIZER_CJ && defined(__linux__) + meta_begin = RoundDownTo(meta_begin, GetPageSizeCached()); + meta_end = RoundUpTo(meta_end, GetPageSizeCached()); +#else // Mapping continuous heap. // Windows wants 64K alignment. meta_begin = RoundDownTo(meta_begin, 64 << 10); meta_end = RoundUpTo(meta_end, 64 << 10); +#endif CHECK_GT(meta_end, mapped_meta_end); if (meta_begin < mapped_meta_end) meta_begin = mapped_meta_end; @@ -645,7 +764,7 @@ void MapShadow(uptr addr, uptr size) { addr + size, meta_begin, meta_end); } -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ static void OnStackUnwind(const SignalContext &sig, const void *, BufferedStackTrace *stack) { stack->Unwind(StackTrace::GetNextInstructionPc(sig.pc), sig.bp, sig.context, @@ -662,7 +781,7 @@ void CheckUnwind() { // on the other hand there is no sense in processing interceptors // since we are going to die soon. ScopedIgnoreInterceptors ignore; -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ ThreadState* thr = cur_thread(); thr->nomalloc = false; thr->ignore_sync++; @@ -695,7 +814,7 @@ void Initialize(ThreadState *thr) { __sanitizer::InitializePlatformEarly(); __tsan::InitializePlatformEarly(); -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ InitializeAllocator(); ReplaceSystemMalloc(); #endif @@ -708,13 +827,15 @@ void Initialize(ThreadState *thr) { InitializeDynamicAnnotations(); #if !SANITIZER_GO InitializeShadowMemory(); +#if !SANITIZER_CJ InitializeAllocatorLate(); InstallDeadlySignalHandlers(TsanOnDeadlySignal); +#endif #endif // Setup correct file descriptor for error reports. __sanitizer_set_report_path(common_flags()->log_path); InitializeSuppressions(); -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ InitializeLibIgnore(); Symbolizer::GetOrInit()->AddHooks(EnterSymbolizer, ExitSymbolizer); #endif @@ -730,7 +851,7 @@ void Initialize(ThreadState *thr) { __ubsan::InitAsPlugin(); #endif -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ Symbolizer::LateInitialize(); if (InitializeMemoryProfiler() || flags()->force_background_thread) MaybeSpawnBackgroundThread(); @@ -751,7 +872,7 @@ void MaybeSpawnBackgroundThread() { // On MIPS, TSan initialization is run before // __pthread_initialize_minimal_internal() is finished, so we can not spawn // new threads. -#if !SANITIZER_GO && !defined(__mips__) +#if !SANITIZER_GO && !SANITIZER_CJ && !defined(__mips__) static atomic_uint32_t bg_thread = {}; if (atomic_load(&bg_thread, memory_order_relaxed) == 0 && atomic_exchange(&bg_thread, 1, memory_order_relaxed) == 0) { @@ -777,7 +898,7 @@ int Finalize(ThreadState *thr) { ScopedErrorReportLock lock; } -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ if (Verbosity()) AllocatorPrintStats(); #endif @@ -800,7 +921,7 @@ int Finalize(ThreadState *thr) { return failed ? common_flags()->exitcode : 0; } -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ void ForkBefore(ThreadState* thr, uptr pc) SANITIZER_NO_THREAD_SAFETY_ANALYSIS { GlobalProcessorLock(); // Detaching from the slot makes OnUserFree skip writing to the shadow. @@ -867,7 +988,7 @@ void ForkChildAfter(ThreadState* thr, uptr pc, bool start_thread) { } #endif -#if SANITIZER_GO +#if SANITIZER_GO || SANITIZER_CJ NOINLINE void GrowShadowStack(ThreadState *thr) { const int sz = thr->shadow_stack_end - thr->shadow_stack; @@ -882,12 +1003,12 @@ void GrowShadowStack(ThreadState *thr) { #endif StackID CurrentStackId(ThreadState *thr, uptr pc) { -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ if (!thr->is_inited) // May happen during bootstrap. return kInvalidStackID; #endif if (pc != 0) { -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end); #else if (thr->shadow_stack_pos == thr->shadow_stack_end) @@ -937,7 +1058,7 @@ NOINLINE void TraceSwitchPart(ThreadState* thr) { if (TraceSkipGap(thr)) return; -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ if (ctx->after_multithreaded_fork) { // We just need to survive till exec. TracePart* part = thr->tctx->trace.parts.Back(); @@ -1032,7 +1153,7 @@ void ThreadIgnoreBegin(ThreadState* thr, uptr pc) { thr->ignore_reads_and_writes++; CHECK_GT(thr->ignore_reads_and_writes, 0); thr->fast_state.SetIgnoreBit(); -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ if (pc && !ctx->after_multithreaded_fork) thr->mop_ignore_set.Add(CurrentStackId(thr, pc)); #endif @@ -1044,7 +1165,7 @@ void ThreadIgnoreEnd(ThreadState *thr) { thr->ignore_reads_and_writes--; if (thr->ignore_reads_and_writes == 0) { thr->fast_state.ClearIgnoreBit(); -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ thr->mop_ignore_set.Reset(); #endif } @@ -1062,7 +1183,7 @@ void ThreadIgnoreSyncBegin(ThreadState *thr, uptr pc) { DPrintf("#%d: ThreadIgnoreSyncBegin\n", thr->tid); thr->ignore_sync++; CHECK_GT(thr->ignore_sync, 0); -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ if (pc && !ctx->after_multithreaded_fork) thr->sync_ignore_set.Add(CurrentStackId(thr, pc)); #endif @@ -1072,7 +1193,7 @@ void ThreadIgnoreSyncEnd(ThreadState *thr) { DPrintf("#%d: ThreadIgnoreSyncEnd\n", thr->tid); CHECK_GT(thr->ignore_sync, 0); thr->ignore_sync--; -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ if (thr->ignore_sync == 0) thr->sync_ignore_set.Reset(); #endif diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.h b/compiler-rt/lib/tsan/rtl/tsan_rtl.h index e1e121e2e..6db1a056e 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl.h +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.h @@ -30,6 +30,7 @@ #include "sanitizer_common/sanitizer_asm.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_deadlock_detector_interface.h" +#include "sanitizer_common/sanitizer_dense_map.h" #include "sanitizer_common/sanitizer_libignore.h" #include "sanitizer_common/sanitizer_suppressions.h" #include "sanitizer_common/sanitizer_thread_registry.h" @@ -182,7 +183,7 @@ struct ThreadState { int ignore_reads_and_writes; int suppress_reports; // Go does not support ignores. -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ IgnoreSet mop_ignore_set; IgnoreSet sync_ignore_set; #endif @@ -211,7 +212,7 @@ struct ThreadState { // Current wired Processor, or nullptr. Required to handle any events. Processor *proc1; -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ Processor *proc() { return proc1; } #else Processor *proc(); @@ -235,6 +236,18 @@ struct ThreadState { } ALIGNED(SANITIZER_CACHE_LINE_SIZE); #if !SANITIZER_GO +#if SANITIZER_CJ +extern "C" { +ThreadState *CJ_MCC_TsanGetThreadState(void) __attribute__((weak)); +} +inline ThreadState *cur_thread() { + return CJ_MCC_TsanGetThreadState(); +} +inline ThreadState *cur_thread_init() { + return cur_thread(); +} +void TraceFixAll(uptr from, uptr to, uptr size); +#else #if SANITIZER_APPLE || SANITIZER_ANDROID ThreadState *cur_thread(); void set_cur_thread(ThreadState *thr); @@ -257,6 +270,7 @@ inline void set_cur_thread(ThreadState *thr) { } inline void cur_thread_finalize() { } # endif // SANITIZER_APPLE || SANITIZER_ANDROID +#endif // !SANITIZER_CJ #endif // SANITIZER_GO class ThreadContext final : public ThreadContextBase { @@ -299,7 +313,7 @@ struct Context { Context(); bool initialized; -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ bool after_multithreaded_fork; #endif @@ -372,7 +386,7 @@ struct Context { uptr trace_part_total_allocated SANITIZER_GUARDED_BY(slot_mtx); uptr trace_part_recycle_finished SANITIZER_GUARDED_BY(slot_mtx); uptr trace_part_finished_excess SANITIZER_GUARDED_BY(slot_mtx); -#if SANITIZER_GO +#if SANITIZER_GO || SANITIZER_CJ uptr mapped_shadow_begin; uptr mapped_shadow_end; #endif @@ -386,13 +400,13 @@ ALWAYS_INLINE Flags *flags() { struct ScopedIgnoreInterceptors { ScopedIgnoreInterceptors() { -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ cur_thread()->ignore_interceptors++; #endif } ~ScopedIgnoreInterceptors() { -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ cur_thread()->ignore_interceptors--; #endif } @@ -771,7 +785,7 @@ void FuncEntry(ThreadState *thr, uptr pc) { if (UNLIKELY(!TryTraceFunc(thr, pc))) return TraceRestartFuncEntry(thr, pc); DCHECK_GE(thr->shadow_stack_pos, thr->shadow_stack); -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end); #else if (thr->shadow_stack_pos == thr->shadow_stack_end) @@ -787,7 +801,7 @@ void FuncExit(ThreadState *thr) { if (UNLIKELY(!TryTraceFunc(thr, 0))) return TraceRestartFuncExit(thr); DCHECK_GT(thr->shadow_stack_pos, thr->shadow_stack); -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end); #endif thr->shadow_stack_pos--; diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_proc.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_proc.cpp index 5acc39672..ce6335940 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl_proc.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_proc.cpp @@ -22,7 +22,7 @@ Processor *ProcCreate() { internal_memset(mem, 0, sizeof(Processor)); Processor *proc = new(mem) Processor; proc->thr = nullptr; -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ AllocatorProcStart(proc); #endif if (common_flags()->detect_deadlocks) @@ -32,7 +32,7 @@ Processor *ProcCreate() { void ProcDestroy(Processor *proc) { CHECK_EQ(proc->thr, nullptr); -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ AllocatorProcFinish(proc); #endif ctx->metamap.OnProcIdle(proc); diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp index 444f21039..a2903bb3e 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp @@ -57,7 +57,7 @@ static void StackStripMain(SymbolizedStack *frames) { if (last_frame2 == 0) return; -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ const char *last = last_frame->info.function; const char *last2 = last_frame2->info.function; // Strip frame above 'main' @@ -141,7 +141,7 @@ bool ShouldReport(ThreadState *thr, ReportType typ) { case ReportTypeSignalUnsafe: return flags()->report_signal_unsafe; case ReportTypeThreadLeak: -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ // It's impossible to join phantom threads // in the child after fork. if (ctx->after_multithreaded_fork) @@ -277,7 +277,7 @@ int ScopedReportBase::AddMutex(uptr addr, StackID creation_stack_id) { void ScopedReportBase::AddLocation(uptr addr, uptr size) { if (addr == 0) return; -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ int fd = -1; Tid creat_tid = kInvalidTid; StackID creat_stack = 0; diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp index 77488f843..cc3e802ef 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp @@ -30,7 +30,7 @@ ThreadContext::~ThreadContext() { void ThreadContext::OnReset() { CHECK(!sync); } -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ struct ThreadLeak { ThreadContext *tctx; int count; @@ -53,7 +53,7 @@ static void CollectThreadLeaks(ThreadContextBase *tctx_base, void *arg) { // Disabled on Mac because lldb test TestTsanBasic fails: // https://reviews.llvm.org/D112603#3163158 -#if !SANITIZER_GO && !SANITIZER_APPLE +#if !SANITIZER_GO && !SANITIZER_CJ && !SANITIZER_APPLE static void ReportIgnoresEnabled(ThreadContext *tctx, IgnoreSet *set) { if (tctx->tid == kMainTid) { Printf("ThreadSanitizer: main thread finished with ignores enabled\n"); @@ -85,7 +85,7 @@ static void ThreadCheckIgnore(ThreadState *thr) {} void ThreadFinalize(ThreadState *thr) { ThreadCheckIgnore(thr); -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ if (!ShouldReport(thr, ReportTypeThreadLeak)) return; ThreadRegistryLock l(&ctx->thread_registry); @@ -164,7 +164,7 @@ void ThreadStart(ThreadState *thr, Tid tid, tid_t os_id, uptr stk_size = 0; uptr tls_addr = 0; uptr tls_size = 0; -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ if (thread_type != ThreadType::Fiber) GetThreadStackAndTls(tid == kMainTid, &stk_addr, &stk_size, &tls_addr, &tls_size); @@ -174,7 +174,7 @@ void ThreadStart(ThreadState *thr, Tid tid, tid_t os_id, thr->tls_addr = tls_addr; thr->tls_size = tls_size; -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ if (ctx->after_multithreaded_fork) { thr->ignore_interceptors++; ThreadIgnoreBegin(thr, 0); @@ -182,7 +182,7 @@ void ThreadStart(ThreadState *thr, Tid tid, tid_t os_id, } #endif -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ // Don't imitate stack/TLS writes for the main thread, // because its initialization is synchronized with all // subsequent threads anyway. @@ -219,7 +219,7 @@ void ThreadFinish(ThreadState *thr) { if (thr->tls_addr && thr->tls_size) DontNeedShadowFor(thr->tls_addr, thr->tls_size); thr->is_dead = true; -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ thr->is_inited = false; thr->ignore_interceptors++; PlatformCleanUpThreadState(thr); @@ -235,7 +235,7 @@ void ThreadFinish(ThreadState *thr) { IncrementEpoch(thr); } } -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ UnmapOrDie(thr->shadow_stack, kShadowStackSize * sizeof(uptr)); #else Free(thr->shadow_stack); @@ -329,7 +329,7 @@ void ThreadSetName(ThreadState *thr, const char *name) { ctx->thread_registry.SetThreadName(thr->tid, name); } -#if !SANITIZER_GO +#if !SANITIZER_GO && !SANITIZER_CJ void FiberSwitchImpl(ThreadState *from, ThreadState *to) { Processor *proc = from->proc(); ProcUnwire(proc, from); diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 296fb4220..9d365c792 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -202,7 +202,7 @@ static bool isBitcode(MemoryBufferRef mb) { } // Opens a file and create a file object. Path has to be resolved already. -void LinkerDriver::addFile(StringRef path, bool withLOption) { +void LinkerDriver::addFile(StringRef path, bool withLOption, bool exportSymbol) { using namespace sys::fs; Optional buffer = readFile(path); @@ -223,7 +223,7 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) { if (inWholeArchive) { for (const auto &p : getArchiveMembers(mbref)) { if (isBitcode(p.first)) - files.push_back(make(p.first, path, p.second, false)); + files.push_back(make(p.first, path, p.second, false, exportSymbol)); else files.push_back(createObjFile(p.first, path)); } @@ -252,7 +252,7 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) { if (magic == file_magic::elf_relocatable) files.push_back(createObjFile(p.first, path, true)); else if (magic == file_magic::bitcode) - files.push_back(make(p.first, path, p.second, true)); + files.push_back(make(p.first, path, p.second, true, exportSymbol)); else warn(path + ": archive member '" + p.first.getBufferIdentifier() + "' is neither ET_REL nor LLVM bitcode"); @@ -277,7 +277,7 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) { make(mbref, withLOption ? path::filename(path) : path)); return; case file_magic::bitcode: - files.push_back(make(mbref, "", 0, inLib)); + files.push_back(make(mbref, "", 0, inLib, exportSymbol)); break; case file_magic::elf_relocatable: files.push_back(createObjFile(mbref, "", inLib)); @@ -1172,10 +1172,15 @@ static void readConfigs(opt::InputArgList &args) { } } + if (args.hasArg(OPT_no_rosegment)) { + error("The --no-rosegment option does not supported for Cangjie!"); + } + config->searchPaths = args::getStrings(args, OPT_library_path); config->sectionStartMap = getSectionStartMap(args); config->shared = args.hasArg(OPT_shared); - config->singleRoRx = !args.hasFlag(OPT_rosegment, OPT_no_rosegment, true); + // The --no-rosegment option does not supported for Cangjie. + config->singleRoRx = false; config->soName = args.getLastArgValue(OPT_soname); config->sortSection = getSortSection(args); config->splitStackAdjustSize = args::getInteger(args, OPT_split_stack_adjust_size, 16384); @@ -1561,12 +1566,21 @@ void LinkerDriver::createFiles(opt::InputArgList &args) { // Iterate over argv to process input files and positional arguments. InputFile::isInGroup = false; bool hasInput = false; + bool addExportBCOnly = false; + bool compileAsEXE = false; for (auto *arg : args) { switch (arg->getOption().getID()) { case OPT_library: addLibrary(arg->getValue()); hasInput = true; break; + case OPT_compile_as_exe: + compileAsEXE = true; + break; + case OPT_export_bc_only: + addFile(arg->getValue(), false, true); + addExportBCOnly = true; + break; case OPT_INPUT: addFile(arg->getValue(), /*withLOption=*/false); hasInput = true; @@ -1660,6 +1674,18 @@ void LinkerDriver::createFiles(opt::InputArgList &args) { if (files.empty() && !hasInput && errorCount() == 0) error("no input files"); + + if (compileAsEXE) { + if (addExportBCOnly) + error("use compile-as-exe && export-bc-only. Hidden all sym is needed."); + for (InputFile *file : files) + if (auto bitcodeFile = dyn_cast(file)) + bitcodeFile->ExportSymbols = false; + } else if (!addExportBCOnly) { + for (InputFile *file : files) + if (auto bitcodeFile = dyn_cast(file)) + bitcodeFile->ExportSymbols = true; + } } // If -m was not given, infer it from object files. diff --git a/lld/ELF/Driver.h b/lld/ELF/Driver.h index c5c46fbf1..abef62a44 100644 --- a/lld/ELF/Driver.h +++ b/lld/ELF/Driver.h @@ -25,7 +25,7 @@ extern std::unique_ptr driver; class LinkerDriver { public: void linkerMain(ArrayRef args); - void addFile(StringRef path, bool withLOption); + void addFile(StringRef path, bool withLOption, bool exportSymbol = false); void addLibrary(StringRef name); private: diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 473809b05..1a653d852 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -1559,7 +1559,8 @@ static uint8_t getOsAbi(const Triple &t) { } BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, - uint64_t offsetInArchive, bool lazy) + uint64_t offsetInArchive, bool lazy, + bool ExportSym = true) : InputFile(BitcodeKind, mb) { this->archiveName = archiveName; this->lazy = lazy; @@ -1586,6 +1587,7 @@ BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, ekind = getBitcodeELFKind(t); emachine = getBitcodeMachineKind(mb.getBufferIdentifier(), t); osabi = getOsAbi(t); + ExportSymbols = ExportSym; } static uint8_t mapVisibility(GlobalValue::VisibilityTypes gvVisibility) { @@ -1625,7 +1627,7 @@ createBitcodeSymbol(Symbol *&sym, const std::vector &keptComdats, objSym.getCommonSize()}); } else { Defined newSym(&f, StringRef(), binding, visibility, type, 0, 0, nullptr); - if (objSym.canBeOmittedFromSymbolTable()) + if (!f.ExportSymbols || objSym.canBeOmittedFromSymbolTable()) newSym.exportDynamic = false; sym->resolve(newSym); } diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h index a24e664a7..d5c7271fd 100644 --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -318,13 +318,14 @@ private: class BitcodeFile : public InputFile { public: BitcodeFile(MemoryBufferRef m, StringRef archiveName, - uint64_t offsetInArchive, bool lazy); + uint64_t offsetInArchive, bool lazy, bool ExportSym); static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } template void parse(); void parseLazy(); void postParse(); std::unique_ptr obj; std::vector keptComdats; + bool ExportSymbols; }; // .so file. diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index 9f30117c0..67f80dd55 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -982,17 +982,12 @@ void LinkerScript::assignOffsets(OutputSection *sec) { } ctx->outSec = sec; - if (sec->addrExpr && script->hasSectionsCommand) { - // The alignment is ignored. - sec->addr = dot; - } else { - // sec->alignment is the max of ALIGN and the maximum of input - // section alignments. - const uint64_t pos = dot; - dot = alignToPowerOf2(dot, sec->alignment); - sec->addr = dot; - expandMemoryRegions(dot - pos); - } + // sec->alignment is the max of ALIGN and the maximum of input + // section alignments. + const uint64_t pos = dot; + dot = alignToPowerOf2(dot, sec->alignment); + sec->addr = dot; + expandMemoryRegions(dot - pos); // ctx->lmaOffset is LMA minus VMA. If LMA is explicitly specified via AT() or // AT>, recompute ctx->lmaOffset; otherwise, if both previous/current LMA diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index d9266e595..b2eb2a104 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -190,6 +190,10 @@ defm execute_only: BB<"execute-only", "Mark executable sections unreadable", "Mark executable sections readable (default)">; +defm export_bc_only: Eq<"export-bc", "export bc symbols only">, MetaVarName<"file">; + +def compile_as_exe: Flag<["--"], "compile-as-exe">, HelpText<"hidden all symbol">; + defm export_dynamic: B<"export-dynamic", "Put symbols in the dynamic symbol table", "Do not put symbols in the dynamic symbol table (default)">; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index ad80d4344..84f875406 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -2129,7 +2129,7 @@ template void Writer::finalizeSections() { } } - if (!script->hasSectionsCommand && !config->relocatable) + if (!config->relocatable) fixSectionAlignments(); // This is used to: diff --git a/lld/MachO/OutputSegment.cpp b/lld/MachO/OutputSegment.cpp index da1394c08..f2949e7f3 100644 --- a/lld/MachO/OutputSegment.cpp +++ b/lld/MachO/OutputSegment.cpp @@ -44,6 +44,12 @@ static uint32_t maxProt(StringRef name) { return initProt(name); } +static uint32_t flags(StringRef name) { + // If we ever implement shared cache output support, SG_READ_ONLY should not + // be used for dylibs that can be placed in it. + return name == segment_names::dataConst ? SG_READ_ONLY : 0; +} + size_t OutputSegment::numNonHiddenSections() const { size_t count = 0; for (const OutputSection *osec : sections) @@ -184,6 +190,7 @@ OutputSegment *macho::getOrCreateOutputSegment(StringRef name) { segRef->name = name; segRef->maxProt = maxProt(name); segRef->initProt = initProt(name); + segRef->flags = flags(name); outputSegments.push_back(segRef); return segRef; diff --git a/lld/MachO/OutputSegment.h b/lld/MachO/OutputSegment.h index bff99e28a..a5d54b181 100644 --- a/lld/MachO/OutputSegment.h +++ b/lld/MachO/OutputSegment.h @@ -56,6 +56,7 @@ public: StringRef name; uint32_t maxProt = 0; uint32_t initProt = 0; + uint32_t flags = 0; uint8_t index; llvm::TinyPtrVector segmentStartSymbols; diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp index ce9672dd0..7effeaf9f 100644 --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -246,6 +246,7 @@ public: c->vmsize = seg->vmSize; c->filesize = seg->fileSize; c->nsects = seg->numNonHiddenSections(); + c->flags = seg->flags; for (const OutputSection *osec : seg->getSections()) { if (osec->isHidden()) diff --git a/lld/test/MachO/builtin-rename.s b/lld/test/MachO/builtin-rename.s index dc9b7f8f7..22b2479c0 100644 --- a/lld/test/MachO/builtin-rename.s +++ b/lld/test/MachO/builtin-rename.s @@ -53,6 +53,23 @@ # YDATA-DAG: __DATA_CONST,__objc_protolist __DATA__objc_protolist # YDATA-DAG: __DATA_CONST,__nl_symbol_ptr __IMPORT__pointers +## Check that the SG_READ_ONLY flag is set on __DATA_CONST. +# RUN: llvm-otool -v -l %t/ydata | \ +# RUN: FileCheck %s --check-prefix=FLAGS + +# FLAGS-LABEL: Load command 2 +# FLAGS-NEXT: cmd LC_SEGMENT_64 +# FLAGS-NEXT: cmdsize +# FLAGS-NEXT: segname __DATA_CONST +# FLAGS-NEXT: vmaddr +# FLAGS-NEXT: vmsize +# FLAGS-NEXT: fileoff +# FLAGS-NEXT: filesize +# FLAGS-NEXT: maxprot rw- +# FLAGS-NEXT: initprot rw- +# FLAGS-NEXT: nsects 13 +# FLAGS-NEXT: flags SG_READ_ONLY + ## LLD doesn't support defining symbols in synthetic sections, so we test them ## via this slightly more awkward route. # RUN: llvm-readobj --section-headers %t/ydata | \ diff --git a/lld/tools/lld/lld.cpp b/lld/tools/lld/lld.cpp index 3ac59877a..7852f16b4 100644 --- a/lld/tools/lld/lld.cpp +++ b/lld/tools/lld/lld.cpp @@ -33,6 +33,7 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" +#include "llvm/CodeGen/LinkAllCodegenComponents.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Host.h" diff --git a/lldb/README.md b/lldb/README.md new file mode 100644 index 000000000..e610f6d60 --- /dev/null +++ b/lldb/README.md @@ -0,0 +1,80 @@ +# Introduction to cjdb + +## Introduction + +CJDB is a command-line debugging tool for Cangjie programming language developed based on `lldb`. The current `cjdb` tool is an adapted and evolved tool based on [llvm15.0.4](https://github.com/llvm/llvm-project/releases/tag/llvmorg-15.0.4). It provides debugging capabilities for Cangjie developers. For more information, please refer to the [Cangjie Debugger](https://cangjie-lang.cn/docs?url=%2F1.0.0%2Ftools%2Fsource_zh_cn%2Ftools%2Fcjdb_manual_cjnative.html). + +This repository provides the source code of the cjdb debugger tool. The architecture is shown in the following diagram: + +![Architecture Diagram](docs/CJDB_Architecture_Diagram.png) + +## Directory Structure + +```text +lldb/ +├─bindings # 各种语言与LLDB交互的绑定代码 +├─cmake # Cmake构建系统相关的配置文件 +├─docs # 文档 +├─examples # 示例代码 +├─include # 公共头文件 +├─packages # 打包脚本 +├─resources # 运行所需各类资源文件 +├─scripts # 辅助脚本 +├─source # 核心源代码目录,Plugins包含cangjie扩展 +├─test # 测试脚本及测试用例 +├─third_party # 三方库 +├─tools # 与LLDB集成的工具 +├─unittests # 单元测试 +└─utils # 通用工具类和辅助函数 +``` + +## Constraints + +Building the Cangjie compiler is supported on Ubuntu/MacOS (x86_64, aarch64) environments. For more details on environment and tool dependencies, please refer to the [Build Environment Guide](https://gitcode.com/Cangjie/cangjie_build). + +## Building from Source + +Download the source code: + +```shell +$ git clone https://gitcode.com/Cangjie/cangjie-compiler.git -b release-cangjie-merged; +``` + +### Build Steps + +```shell +$ cd cangjie-compiler +$ python3 build.py clean +$ python3 build.py build -t release --build-cjdb +$ python3 build.py install +``` + +1. The `clean` command clears temporary files in the workspace. +2. The `build` command starts compilation. The `-t` or `--build-type` option specifies the build type, which can be `release` or `debug`. +3. The `install` command installs the build artifacts to the `output` directory. + +The `output` directory structure is as follows: + +```text +./output +├── bin # Cangjie executable +├── envsetup.sh # One-click environment variable setup script +├── include # Public header files for the frontend +├── lib # Libraries required by the Cangjie build, subfolders by target platform +├── modules # Reserved folder for Cangjie standard library cjo files, subfolders by target platform +├── runtime # Runtime libraries required by the Cangjie build +├── third_party # Third-party binaries and libraries such as LLVM +└── tools # cangjie tools +│ ├── bin # contain CJDB executable +│ ├── config +│ └── lib +``` + +## Related Repositories + +This repository contains the Cangjie compiler source code. The complete compiler ecosystem also includes: + +- [Cangjie Tools](https://gitcode.com/Cangjie/cangjie-tools/tree/release-cangjie-merged): Tool suite for Cangjie, including code formatting, package management, and more. +- [Cangjie Language Developer Guide](https://gitcode.com/Cangjie/cangjie-docs/tree/release-cangjie-merged/docs/dev-guide): Usage guide for Cangjie language development. +- [Cangjie Standard Library](https://gitcode.com/Cangjie/cangjie-runtime/tree/release-cangjie-merged/runtime): Source code for the Cangjie standard library. +- [Cangjie Runtime](https://gitcode.com/Cangjie/cangjie-runtime/tree/release-cangjie-merged/std): Essential standard library code for the Cangjie language. diff --git a/lldb/README_zh.md b/lldb/README_zh.md new file mode 100644 index 000000000..ad3735d48 --- /dev/null +++ b/lldb/README_zh.md @@ -0,0 +1,90 @@ +# `cjdb` 工具 + +## 简介 + +CJDB 是一款基于 `lldb` 开发的仓颉程序命令行调试工具。当前 `cjdb` 工具是在[llvm15.0.4](https://github.com/llvm/llvm-project/releases/tag/llvmorg-15.0.4)基础上适配演进出来的工具。为仓颉开发者提供程序调试的能力。了解更多仓颉调试器的介绍,请参阅 [调试工具](https://cangjie-lang.cn/docs?url=%2F1.0.0%2Ftools%2Fsource_zh_cn%2Ftools%2Fcjdb_manual_cjnative.html)。 + +本仓提供了 cjdb 调试工具源码,整体框架如下图展示: + +![架构图](docs/CJDB_Architecture_Diagram.png) + +## 目录结构 + +```text +lldb/ +├─bindings # 各种语言与LLDB交互的绑定代码 +├─cmake # Cmake构建系统相关的配置文件 +├─docs # 文档 +├─examples # 示例代码 +├─include # 公共头文件 +├─packages # 打包脚本 +├─resources # 运行所需各类资源文件 +├─scripts # 辅助脚本 +├─source # 核心源代码目录,Plugins包含cangjie扩展 +├─test # 测试脚本及测试用例 +├─third_party # 三方库 +├─tools # 与LLDB集成的工具 +├─unittests # 单元测试 +└─utils # 通用工具类和辅助函数 +``` + +## 约束 + +支持在 Ubuntu/MacOS(x86_64, aarch64) 环境中对仓颉调试器进行构建。更详细的环境及工具依赖请参阅 [构建环境指导书](https://gitcode.com/Cangjie/cangjie_build)。 + +## 编译构建 + +下载源码: + +```shell +$ git clone https://gitcode.com/Cangjie/cangjie-compiler.git -b release-cangjie-merged; +``` + +### 构建步骤 + +```shell +$ cd cangjie-compiler +$ python3 build.py clean +$ python3 build.py build -t release --build-cjdb +$ python3 build.py install +``` + +1. `clean` 命令用于清空工作区临时文件; +2. `build` 命令开始执行编译,选项 `-t` 即 `--build-type`,指定编译产物类型,可以是 `release` 或 `debug`; +3. `install` 命令将编译产物安装到 `output` 目录下。 + +`output` 目录结构如下: + +```text +./output +├── bin # 仓颉可执行文件 +├── envsetup.sh # 一键环境变量配置脚本 +├── include # 编译前端对外头文件 +├── lib # 仓颉编译产物依赖库,子文件夹按照目标平台拆分 +├── modules # 仓颉标准库 cjo 文件预留文件夹,子文件夹按照目标平台拆分 +├── runtime # 仓颉编译产物依赖运行时库 +├── third_party # llvm 等第三方依赖二进制及库 +└── tools # 仓颉工具 +│ ├── bin # 包含CJDB可执行文件 +│ ├── config +│ └── lib +``` + +### 更多构建选项 + +如需了解更多构建选项,请参阅 [build.py 构建脚本](./build.py) 或通过 `--help` 选项查看。 + +```shell +$ python3 build.py --help +``` + +### 集成构建指导 + +集成构建请参阅 [仓颉 SDK 集成构建指导书](https://gitcode.com/Cangjie/cangjie_build/blob/main/README_zh.md)。 + +## 相关仓 + +- [仓颉工具](https://gitcode.com/Cangjie/cangjie-tools/tree/release-cangjie-merged):提供仓颉工具套,包含代码格式化、包管理等工具。 +- [仓颉语言开发指南](https://gitcode.com/Cangjie/cangjie-docs/tree/release-cangjie-merged/docs/dev-guide):提供仓颉语言开发使用指南; +- [仓颉语言标准库](https://gitcode.com/Cangjie/cangjie-runtime/tree/release-cangjie-merged/runtime):提供仓颉标准库源码; +- [仓颉运行时](https://gitcode.com/Cangjie/cangjie-runtime/tree/release-cangjie-merged/std):提供仓颉语言所必需的标准库代码; diff --git a/lldb/bindings/headers.swig b/lldb/bindings/headers.swig index 3c2cd85f5..c2f314c4f 100644 --- a/lldb/bindings/headers.swig +++ b/lldb/bindings/headers.swig @@ -75,4 +75,5 @@ #include "lldb/API/SBValueList.h" #include "lldb/API/SBVariablesOptions.h" #include "lldb/API/SBWatchpoint.h" +#include "lldb/API/SBMixedArkTSDebugger.h" %} diff --git a/lldb/bindings/interface/SBMixedArkTSDebugger.i b/lldb/bindings/interface/SBMixedArkTSDebugger.i new file mode 100644 index 000000000..7b394390f --- /dev/null +++ b/lldb/bindings/interface/SBMixedArkTSDebugger.i @@ -0,0 +1,25 @@ +//===-- SBMixedArkTSDebugger.h --------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +namespace lldb { + +%feature("docstring", +"Represents a list of :py:class:`SBMixedArkTSDebugger`." +) SBMixedArkTSDebugger; +class SBMixedArkTSDebugger +{ +public: + + SBMixedArkTSDebugger (); + + SBMixedArkTSDebugger (const lldb::SBMixedArkTSDebugger &rhs); + + ~SBMixedArkTSDebugger (); +}; + +} // namespace lldb diff --git a/lldb/bindings/interfaces.swig b/lldb/bindings/interfaces.swig index c9a6d0f06..46aa3be34 100644 --- a/lldb/bindings/interfaces.swig +++ b/lldb/bindings/interfaces.swig @@ -2,7 +2,9 @@ #define __extension__ /* Undefine GCC keyword to make Swig happy when processing glibc's stdint.h. */ /* The ISO C99 standard specifies that in C++ implementations limit macros such as INT32_MAX should only be defined if __STDC_LIMIT_MACROS is. */ +#ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS +#endif %include "stdint.i" %include "lldb/lldb-defines.h" @@ -48,6 +50,7 @@ %include "./interface/SBListener.i" %include "./interface/SBMemoryRegionInfo.i" %include "./interface/SBMemoryRegionInfoList.i" +%include "./interface/SBMixedArkTSDebugger.i" %include "./interface/SBModule.i" %include "./interface/SBModuleSpec.i" %include "./interface/SBPlatform.i" diff --git a/lldb/bindings/python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig index bf3de66b9..d45431c77 100644 --- a/lldb/bindings/python/python-typemaps.swig +++ b/lldb/bindings/python/python-typemaps.swig @@ -435,7 +435,7 @@ template <> bool SetNumberFromPyObject(double &number, PyObject *obj) { %typemap(out) lldb::FileSP { $result = nullptr; - lldb::FileSP &sp = $1; + const lldb::FileSP &sp = $1; if (sp) { PythonFile pyfile = unwrapOrSetPythonException(PythonFile::FromFile(*sp)); if (!pyfile.IsValid()) diff --git a/lldb/docs/CJDB_Architecture_Diagram.png b/lldb/docs/CJDB_Architecture_Diagram.png new file mode 100644 index 000000000..99f332305 Binary files /dev/null and b/lldb/docs/CJDB_Architecture_Diagram.png differ diff --git a/lldb/include/lldb/API/LLDB.h b/lldb/include/lldb/API/LLDB.h index eacbbeafc..f5b68f14c 100644 --- a/lldb/include/lldb/API/LLDB.h +++ b/lldb/include/lldb/API/LLDB.h @@ -44,6 +44,7 @@ #include "lldb/API/SBListener.h" #include "lldb/API/SBMemoryRegionInfo.h" #include "lldb/API/SBMemoryRegionInfoList.h" +#include "lldb/API/SBMixedArkTSDebugger.h" #include "lldb/API/SBModule.h" #include "lldb/API/SBModuleSpec.h" #include "lldb/API/SBPlatform.h" diff --git a/lldb/include/lldb/API/SBDefines.h b/lldb/include/lldb/API/SBDefines.h index ecf1dc34d..373271de5 100644 --- a/lldb/include/lldb/API/SBDefines.h +++ b/lldb/include/lldb/API/SBDefines.h @@ -15,6 +15,8 @@ #include "lldb/lldb-types.h" #include "lldb/lldb-versioning.h" +#include // For FILE * + #ifndef LLDB_API #if defined(_WIN32) #if defined(LLDB_IN_LIBLLDB) diff --git a/lldb/include/lldb/API/SBMixedArkTSDebugger.h b/lldb/include/lldb/API/SBMixedArkTSDebugger.h new file mode 100644 index 000000000..aba32338c --- /dev/null +++ b/lldb/include/lldb/API/SBMixedArkTSDebugger.h @@ -0,0 +1,36 @@ +//===-- SBMixedArkTSDebugger.h --------------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_API_SBMIXEDARKTSDEBUGGER_H +#define LLDB_API_SBMIXEDARKTSDEBUGGER_H + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class LLDB_API SBMixedArkTSDebugger { +public: + SBMixedArkTSDebugger(); + + SBMixedArkTSDebugger(const lldb::SBTarget &rhs); + + SBMixedArkTSDebugger(const lldb::TargetSP &target_sp); + + ~SBMixedArkTSDebugger(); + + lldb::SBData GetBackTrace(SBError &er); + + lldb::SBData OperateDebugMessage(const char *message, SBError &er); + +}; + +} // namespace lldb + +#endif // LLDB_API_SBMIXEDARKTSDEBUGGER_H \ No newline at end of file diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h index 018acfd59..8ba358634 100644 --- a/lldb/include/lldb/API/SBTarget.h +++ b/lldb/include/lldb/API/SBTarget.h @@ -21,6 +21,8 @@ #include "lldb/API/SBType.h" #include "lldb/API/SBValue.h" #include "lldb/API/SBWatchpoint.h" +#include +#include namespace lldb { diff --git a/lldb/include/lldb/Breakpoint/Watchpoint.h b/lldb/include/lldb/Breakpoint/Watchpoint.h index 41b723a66..f48032e72 100644 --- a/lldb/include/lldb/Breakpoint/Watchpoint.h +++ b/lldb/include/lldb/Breakpoint/Watchpoint.h @@ -131,6 +131,8 @@ public: /// \b true if the target should stop at this watchpoint and \b false not. bool InvokeCallback(StoppointCallbackContext *context); + bool InvokeInnerCallback(StoppointCallbackContext *context); + // Condition /// Set the watchpoint's condition. /// diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h index aaefb69f0..bf416243c 100644 --- a/lldb/include/lldb/Core/Mangled.h +++ b/lldb/include/lldb/Core/Mangled.h @@ -41,6 +41,7 @@ public: enum ManglingScheme { eManglingSchemeNone = 0, + eManglingSchemeCangjie, eManglingSchemeMSVC, eManglingSchemeItanium, eManglingSchemeRustV0, @@ -126,13 +127,15 @@ public: /// /// \return /// A const reference to the demangled name string object. - ConstString GetDemangledName() const; + ConstString GetDemangledName(const SymbolContext *sc = nullptr) const; + + ConstString GetDemangledBaseName(const SymbolContext *sc = nullptr) const; /// Display demangled name get accessor. /// /// \return /// A const reference to the display demangled name string object. - ConstString GetDisplayDemangledName() const; + ConstString GetDisplayDemangledName(const SymbolContext *sc = nullptr) const; void SetDemangledName(ConstString name) { m_demangled = name; } @@ -159,7 +162,9 @@ public: /// A const reference to the preferred name string object if this /// object has a valid name of that kind, else a const reference to the /// other name is returned. - ConstString GetName(NamePreference preference = ePreferDemangled) const; + ConstString GetName(NamePreference preference = ePreferDemangled, const SymbolContext *sc = nullptr) const; + + static std::string GetDemangledTypeName(std::string &type_name); /// Check if "name" matches either the mangled or demangled name. /// diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h index 58a053525..7f6cfdc5a 100644 --- a/lldb/include/lldb/Core/ValueObject.h +++ b/lldb/include/lldb/Core/ValueObject.h @@ -347,6 +347,8 @@ public: return m_update_point.GetExecutionContextRef().GetFrameSP(); } + CompilerType GetDynamicType(); + void SetNeedsUpdate(); CompilerType GetCompilerType() { return MaybeCalculateCompleteType(); } @@ -790,6 +792,25 @@ public: virtual void SetLanguageFlags(uint64_t flags) { m_language_flags = flags; } + bool IsInternalType() { return is_internal; } + + void SetIsInternalType(bool internal) { is_internal = internal; } + + bool IsOptionlikeReference() { return is_optionlike_reference; } + + void SetIsOptionlikeReference(bool reference) { is_optionlike_reference = reference; } + + bool GetCangjieDynamicType(TypeAndOrName &class_type_or_name, bool should_update = true, bool should_check_parent = true); + + bool IsCangjieDynamicType(); + + bool IsCangjieGenericType(); + + void SetOverrideType(CompilerType type) { m_override_type = type; } + + void SetGenericValueType(bool is_value_type) { is_generic_value_type = is_value_type; } + bool IsGenericValueType(); + protected: typedef ClusterManager ValueObjectManager; @@ -904,6 +925,14 @@ protected: /// Unique identifier for every value object. UserID m_id; + bool is_internal = false; + + bool is_optionlike_reference = false; + + bool has_typeinfo = false; + + bool is_generic_value_type = false; + // Utility class for initializing all bitfields in ValueObject's constructors. // FIXME: This could be done via default initializers once we have C++20. struct Bitflags { diff --git a/lldb/include/lldb/Core/ValueObjectDynamicValue.h b/lldb/include/lldb/Core/ValueObjectDynamicValue.h index a36db44f4..3a4962173 100644 --- a/lldb/include/lldb/Core/ValueObjectDynamicValue.h +++ b/lldb/include/lldb/Core/ValueObjectDynamicValue.h @@ -92,6 +92,8 @@ public: void SetLanguageFlags(uint64_t flags) override; + void UpdateDynamicType(); + protected: bool UpdateValue() override; diff --git a/lldb/include/lldb/Expression/IRMemoryMap.h b/lldb/include/lldb/Expression/IRMemoryMap.h index abec54427..163945d64 100644 --- a/lldb/include/lldb/Expression/IRMemoryMap.h +++ b/lldb/include/lldb/Expression/IRMemoryMap.h @@ -51,7 +51,7 @@ public: }; lldb::addr_t Malloc(size_t size, uint8_t alignment, uint32_t permissions, - AllocationPolicy policy, bool zero_memory, Status &error); + AllocationPolicy policy, bool zero_memory, Status &error, bool keep = false); void Leak(lldb::addr_t process_address, Status &error); void Free(lldb::addr_t process_address, Status &error); @@ -101,11 +101,12 @@ private: /// process. In the host, the memory is always /// read/write. uint8_t m_alignment; ///< The alignment of the requested allocation. + bool m_keep; public: Allocation(lldb::addr_t process_alloc, lldb::addr_t process_start, size_t size, uint32_t permissions, uint8_t alignment, - AllocationPolicy m_policy); + AllocationPolicy m_policy, bool keep = false); Allocation(const Allocation &) = delete; const Allocation &operator=(const Allocation &) = delete; diff --git a/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h b/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h index c8ec3b941..e4992d9f6 100644 --- a/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h +++ b/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h @@ -295,6 +295,8 @@ static constexpr CommandObject::ArgumentTableEntry g_argument_table[] = { { lldb::eArgTypeSymbol, "symbol", CommandCompletions::eSymbolCompletion, {}, { nullptr, false }, "Any symbol name (function name, variable, argument, etc.)" }, { lldb::eArgTypeThreadID, "thread-id", CommandCompletions::eNoCompletion, {}, { nullptr, false }, "Thread ID number." }, { lldb::eArgTypeThreadIndex, "thread-index", CommandCompletions::eNoCompletion, {}, { nullptr, false }, "Index into the process' list of threads." }, + { lldb::eArgTypeCJThreadIndex, "cjthread-index", CommandCompletions::eNoCompletion, {}, + { nullptr, false }, "Index into the process' list of cjthreads." }, { lldb::eArgTypeThreadName, "thread-name", CommandCompletions::eNoCompletion, {}, { nullptr, false }, "The thread's name." }, { lldb::eArgTypeTypeName, "type-name", CommandCompletions::eNoCompletion, {}, { nullptr, false }, "A type name." }, { lldb::eArgTypeUnsignedInteger, "unsigned-integer", CommandCompletions::eNoCompletion, {}, { nullptr, false }, "An unsigned integer." }, diff --git a/lldb/include/lldb/Interpreter/OptionGroupVariable.h b/lldb/include/lldb/Interpreter/OptionGroupVariable.h index c9f1283d4..53c80752b 100644 --- a/lldb/include/lldb/Interpreter/OptionGroupVariable.h +++ b/lldb/include/lldb/Interpreter/OptionGroupVariable.h @@ -35,7 +35,7 @@ public: // true) show_locals : 1, // Frame option only (include_frame_options == true) show_globals : 1, // Frame option only (include_frame_options == true) - use_regex : 1, show_scope : 1, show_decl : 1; + use_regex : 1, show_scope : 1, show_decl : 1, show_values : 1; OptionValueString summary; // the name of a named summary OptionValueString summary_string; // a summary string diff --git a/lldb/include/lldb/Symbol/Function.h b/lldb/include/lldb/Symbol/Function.h index 3eb4f5d7d..589028ba6 100644 --- a/lldb/include/lldb/Symbol/Function.h +++ b/lldb/include/lldb/Symbol/Function.h @@ -529,11 +529,11 @@ public: /// A const compile unit object pointer. const DWARFExpressionList &GetFrameBaseExpression() const { return m_frame_base; } - ConstString GetName() const; + ConstString GetName(const SymbolContext *sc = nullptr) const; - ConstString GetNameNoArguments() const; + ConstString GetNameNoArguments(const SymbolContext *sc = nullptr) const; - ConstString GetDisplayName() const; + ConstString GetDisplayName(const SymbolContext *sc = nullptr) const; const Mangled &GetMangled() const { return m_mangled; } diff --git a/lldb/include/lldb/Symbol/Variable.h b/lldb/include/lldb/Symbol/Variable.h index c437624d1..b1ceae050 100644 --- a/lldb/include/lldb/Symbol/Variable.h +++ b/lldb/include/lldb/Symbol/Variable.h @@ -46,6 +46,8 @@ public: ConstString GetName() const; + ConstString GetMangledName() const; + ConstString GetUnqualifiedName() const; SymbolContextScope *GetSymbolContextScope() const { return m_owner_scope; } diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 505e211e0..38507aae3 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -382,6 +382,21 @@ public: return GetStaticBroadcasterClass(); } + void SetCJThreadRegContext(std::map reg) + { + m_cjthreadRegCtx = reg; + }; + + std::map GetCJThreadRegContext() + { + return m_cjthreadRegCtx; + }; + + void CJThreadRegContextClear() + { + m_cjthreadRegCtx.clear(); + } + /// A notification structure that can be used by clients to listen /// for changes in a process's lifetime. /// @@ -3034,6 +3049,7 @@ private: Process(const Process &) = delete; const Process &operator=(const Process &) = delete; + std::map m_cjthreadRegCtx; }; /// RAII guard that should be acquired when an utility function is called within diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index 294fd96bc..ca5a19f12 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -429,10 +429,15 @@ public: void SetIsForUtilityExpr(bool b) { m_running_utility_expression = b; } + bool IsCangjieUserExpr() const { return m_cangjie_user_expr; } + + void SetCangjieUserExpr(bool cangjie_user_expr) { m_cangjie_user_expr = cangjie_user_expr; } + private: ExecutionPolicy m_execution_policy = default_execution_policy; lldb::LanguageType m_language = lldb::eLanguageTypeUnknown; std::string m_prefix; + bool m_cangjie_user_expr = true; bool m_coerce_to_id = false; bool m_unwind_on_error = true; bool m_ignore_breakpoints = false; @@ -1560,6 +1565,21 @@ public: TargetStats &GetStatistics() { return m_stats; } + struct CJThread { + std::vector Frames; + uint64_t cjthreadID; + std::string name; + std::string status; + CJThread(std::vector Frames, uint64_t id, + std::string name, std::string state) { + this->Frames = Frames; + this->cjthreadID = id; + this->name = name; + this->status = state; + } + ~CJThread() = default; + }; + private: /// Construct with optional file and arch. /// diff --git a/lldb/include/lldb/Target/ThreadPlan.h b/lldb/include/lldb/Target/ThreadPlan.h index 616939f89..6c365d706 100644 --- a/lldb/include/lldb/Target/ThreadPlan.h +++ b/lldb/include/lldb/Target/ThreadPlan.h @@ -524,6 +524,14 @@ protected: bool IsUsuallyUnexplainedStopReason(lldb::StopReason); + bool CFFIGetValueFromReg(Target &target, StackFrame *frame, uint64_t *addr, + const char *reg); + + lldb::ThreadPlanSP CFFIMakeThreadPlanRunToAddress(Thread &thread, uint64_t cfunc_addr, + bool stop_others); + + lldb::ThreadPlanSP CFFILookForPlanToStepThroughFromCurrentPC(); + Status m_status; Process &m_process; lldb::tid_t m_tid; diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 2566a43da..e42dcce4a 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -585,6 +585,7 @@ enum CommandArgumentType { eArgTypeSymbol, eArgTypeThreadID, eArgTypeThreadIndex, + eArgTypeCJThreadIndex, eArgTypeThreadName, eArgTypeTypeName, eArgTypeUnsignedInteger, diff --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt index 46307b487..e903142eb 100644 --- a/lldb/source/API/CMakeLists.txt +++ b/lldb/source/API/CMakeLists.txt @@ -86,6 +86,7 @@ add_lldb_library(liblldb SHARED ${option_framework} SBWatchpoint.cpp SBUnixSignals.cpp SystemInitializerFull.cpp + SBMixedArkTSDebugger.cpp ${lldb_python_wrapper} ${lldb_lua_wrapper} @@ -108,6 +109,14 @@ add_lldb_library(liblldb SHARED ${option_framework} ${option_install_prefix} ) +# set rpath to link boundscheck indirectly +if(CMAKE_SYSTEM_NAME MATCHES "Darwin") + target_link_options(liblldb PUBLIC "-Wl,-rpath,${CMAKE_SOURCE_DIR}/../utils/boundscheck/lib/${CMAKE_SYSTEM_PROCESSOR}") +else() + string(REPLACE "AMD64" "x86_64" ARCH_NAME "${CMAKE_SYSTEM_PROCESSOR}") + target_link_directories(lldbPluginExpressionParserCangjie PUBLIC ${CMAKE_SOURCE_DIR}/../utils/boundscheck/lib/${ARCH_NAME}) +endif() + # lib/pythonX.Y/dist-packages/lldb/_lldb.so is a symlink to lib/liblldb.so, # which depends on lib/libLLVM*.so (BUILD_SHARED_LIBS) or lib/libLLVM-10git.so # (LLVM_LINK_LLVM_DYLIB). Add an additional rpath $ORIGIN/../../../../lib so @@ -198,12 +207,22 @@ else() # the framework (including the Clang resourece directory). if(NOT LLDB_BUILD_FRAMEWORK) set(LLDB_CLANG_RESOURCE_DIR_PARENT "$/clang") - file(MAKE_DIRECTORY "${LLDB_CLANG_RESOURCE_DIR_PARENT}") + execute_process( + COMMAND ${CMAKE_COMMAND} -E create_symlink "${LLDB_EXTERNAL_CLANG_RESOURCE_DIR}" + "${LLDB_CLANG_RESOURCE_DIR_PARENT}/${LLDB_CLANG_RESOURCE_DIR_NAME}" + ERROR_VARIABLE err_var) + set(LINK_OR_COPY_CLANG_RESOURCE_DIR COMMAND ${CMAKE_COMMAND} -E create_symlink + "${LLDB_EXTERNAL_CLANG_RESOURCE_DIR}" + "${LLDB_CLANG_RESOURCE_DIR_PARENT}/${LLDB_CLANG_RESOURCE_DIR_NAME}") + if(WIN32 AND err_var) + set(LINK_OR_COPY_CLANG_RESOURCE_DIR COMMAND ${CMAKE_COMMAND} -E copy_directory + "${LLDB_EXTERNAL_CLANG_RESOURCE_DIR}" + "${LLDB_CLANG_RESOURCE_DIR_PARENT}/${LLDB_CLANG_RESOURCE_DIR_NAME}") + endif() add_custom_command(TARGET liblldb POST_BUILD COMMENT "Linking Clang resource dir into LLDB build directory: ${LLDB_CLANG_RESOURCE_DIR_PARENT}" COMMAND ${CMAKE_COMMAND} -E make_directory "${LLDB_CLANG_RESOURCE_DIR_PARENT}" - COMMAND ${CMAKE_COMMAND} -E create_symlink "${LLDB_EXTERNAL_CLANG_RESOURCE_DIR}" - "${LLDB_CLANG_RESOURCE_DIR_PARENT}/${LLDB_CLANG_RESOURCE_DIR_NAME}" + ${LINK_OR_COPY_CLANG_RESOURCE_DIR} ) endif() endif() diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp index ea9c2bb74..f146b05ca 100644 --- a/lldb/source/API/SBFrame.cpp +++ b/lldb/source/API/SBFrame.cpp @@ -1187,7 +1187,7 @@ const char *SBFrame::GetFunctionName() const { if (name == nullptr) { if (sc.function) - name = sc.function->GetName().GetCString(); + name = sc.function->GetName(&sc).GetCString(); } if (name == nullptr) { @@ -1230,7 +1230,7 @@ const char *SBFrame::GetDisplayFunctionName() { if (name == nullptr) { if (sc.function) - name = sc.function->GetDisplayName().GetCString(); + name = sc.function->GetDisplayName(&sc).GetCString(); } if (name == nullptr) { diff --git a/lldb/source/API/SBMixedArkTSDebugger.cpp b/lldb/source/API/SBMixedArkTSDebugger.cpp new file mode 100644 index 000000000..b99f941e7 --- /dev/null +++ b/lldb/source/API/SBMixedArkTSDebugger.cpp @@ -0,0 +1,45 @@ +//===-- SBMixedArkTSDebugger.cpp ------------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBMixedArkTSDebugger.h" +#include "lldb/API/SBData.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBError.h" +#include "lldb/Utility/Instrumentation.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +SBMixedArkTSDebugger::SBMixedArkTSDebugger() { + LLDB_INSTRUMENT_VA(this); +} + +SBMixedArkTSDebugger::SBMixedArkTSDebugger(const lldb::SBTarget &rhs) { + LLDB_INSTRUMENT_VA(this, rhs); +} + +SBMixedArkTSDebugger::SBMixedArkTSDebugger(const lldb::TargetSP &target_sp) { + LLDB_INSTRUMENT_VA(this, target_sp); +} + +SBMixedArkTSDebugger::~SBMixedArkTSDebugger() {} + +lldb::SBData SBMixedArkTSDebugger::GetBackTrace(SBError &er) { + LLDB_INSTRUMENT_VA(this, er); + + return SBData(); +} + +lldb::SBData SBMixedArkTSDebugger::OperateDebugMessage(const char *message, SBError &er) { + LLDB_INSTRUMENT_VA(this, er); + + return SBData(); +} diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp index bf64bb342..1189f846f 100644 --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -2321,4 +2321,4 @@ lldb::SBTrace SBTarget::CreateTrace(lldb::SBError &error) { error.SetErrorString("missing target"); } return SBTrace(); -} +} \ No newline at end of file diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp index f9e03172a..f45873545 100644 --- a/lldb/source/API/SBValue.cpp +++ b/lldb/source/API/SBValue.cpp @@ -957,8 +957,15 @@ uint32_t SBValue::GetNumChildren(uint32_t max) { ValueLocker locker; lldb::ValueObjectSP value_sp(GetSP(locker)); - if (value_sp) - num_children = value_sp->GetNumChildren(max); + if (value_sp) { + ConstString match("^std[.]core::Range<.+>$"); + RegularExpression regex(match.GetStringRef()); + if (regex.Execute(value_sp->GetQualifiedTypeName().AsCString())) { + num_children = 0; + } else { + num_children = value_sp->GetNumChildren(max); + } + } return num_children; } diff --git a/lldb/source/Breakpoint/Breakpoint.cpp b/lldb/source/Breakpoint/Breakpoint.cpp index 578cf1428..857446e2a 100644 --- a/lldb/source/Breakpoint/Breakpoint.cpp +++ b/lldb/source/Breakpoint/Breakpoint.cpp @@ -537,7 +537,7 @@ void Breakpoint::ModulesChanged(ModuleList &module_list, bool load, // address that we haven't resolved to a section yet. So we'll have to // look in all the new modules to resolve this location. Otherwise, if // it was set in this module, re-resolve it here. - if (section_sp && section_sp->GetModule() == module_sp) { + if (!section_sp || section_sp->GetModule() == module_sp) { if (!seen) seen = true; diff --git a/lldb/source/Breakpoint/BreakpointResolverName.cpp b/lldb/source/Breakpoint/BreakpointResolverName.cpp index 24e36f35b..c42802e55 100644 --- a/lldb/source/Breakpoint/BreakpointResolverName.cpp +++ b/lldb/source/Breakpoint/BreakpointResolverName.cpp @@ -353,6 +353,9 @@ BreakpointResolverName::SearchCallback(SearchFilter &filter, break_addr.SetOffset(break_addr.GetOffset() + prologue_byte_size); } } else if (sc.symbol) { + if (sc.symbol->GetName() == "main") { + continue; + } if (sc.symbol->GetType() == eSymbolTypeReExported) { const Symbol *actual_symbol = sc.symbol->ResolveReExportedSymbol(breakpoint.GetTarget()); diff --git a/lldb/source/Breakpoint/Watchpoint.cpp b/lldb/source/Breakpoint/Watchpoint.cpp index 933661ad7..9000a5066 100644 --- a/lldb/source/Breakpoint/Watchpoint.cpp +++ b/lldb/source/Breakpoint/Watchpoint.cpp @@ -4,6 +4,12 @@ // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// //===----------------------------------------------------------------------===// #include "lldb/Breakpoint/Watchpoint.h" @@ -20,6 +26,9 @@ #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Stream.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Core/Debugger.h" using namespace lldb; using namespace lldb_private; @@ -275,7 +284,58 @@ void Watchpoint::SetIgnoreCount(uint32_t n) { SendWatchpointChangedEvent(eWatchpointEventTypeIgnoreChanged); } +// Check the variable to be watched is still in the scope or not, if not, skip the hit point. +bool Watchpoint::InvokeInnerCallback(StoppointCallbackContext *context) { + if (!IsWatchVariable()) { + return true; + } + + ExecutionContext exe_ctx(context->exe_ctx_ref); + StackFrame *frame = exe_ctx.GetFramePtr(); + if (!frame) { + return true; + } + + uint32_t options = StackFrame::eExpressionPathOptionCheckPtrVsMember | + StackFrame::eExpressionPathOptionsAllowDirectIVarAccess; + VariableSP var_sp; + Status error; + ValueObjectSP valobj_sp = frame->GetValueForVariableExpressionPath( + GetWatchSpec(), eNoDynamicValues, options, var_sp, error); + if (valobj_sp) { + return true; + } + + // Not in the frame; let's check the globals. + Target *target = exe_ctx.GetTargetPtr(); + if (!target) { + return true; + } + + VariableList variable_list; + ValueObjectList valobj_list; + auto getVariableCallback = [](void *baton, const char *name, VariableList &variable_list) { + size_t old_size = variable_list.GetSize(); + Target *target = static_cast(baton); + if (target) { + target->GetImages().FindGlobalVariables(ConstString(name), UINT32_MAX, variable_list); + } + return variable_list.GetSize() - old_size; + }; + + Variable::GetValuesForVariableExpressionPath(GetWatchSpec(), exe_ctx.GetBestExecutionContextScope(), + getVariableCallback, target->GetDebugger().GetSelectedTarget().get(), variable_list, valobj_list); + if (valobj_list.GetSize()) { + return true; + } + + return false; +} + bool Watchpoint::InvokeCallback(StoppointCallbackContext *context) { + if (!InvokeInnerCallback(context)) { + return false; + } return m_options.InvokeCallback(context, GetID()); } diff --git a/lldb/source/Commands/CMakeLists.txt b/lldb/source/Commands/CMakeLists.txt index 494249b44..0a5d3b716 100644 --- a/lldb/source/Commands/CMakeLists.txt +++ b/lldb/source/Commands/CMakeLists.txt @@ -40,6 +40,8 @@ add_lldb_library(lldbCommands CommandObjectWatchpointCommand.cpp CommandOptionArgumentTable.cpp CommandOptionsProcessLaunch.cpp + CommandObjectCJThread.cpp + CommandObjCJThreadCommon.cpp LINK_LIBS lldbBreakpoint diff --git a/lldb/source/Commands/CommandObjCJThreadCommon.cpp b/lldb/source/Commands/CommandObjCJThreadCommon.cpp new file mode 100644 index 000000000..222c5837e --- /dev/null +++ b/lldb/source/Commands/CommandObjCJThreadCommon.cpp @@ -0,0 +1,355 @@ +//===-- CommandObjCJThreadCommon.cpp --------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjCJThreadCommon.h" +#include "lldb/Target/RegisterContextUnwind.h" +#include "lldb/Expression/UserExpression.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/DataFormatters/DumpValueObjectOptions.h" + +using namespace lldb; +using namespace lldb_private; + +CommandObjCJThreadCommon::CommandObjCJThreadCommon() { +} + +CommandObjCJThreadCommon::~CommandObjCJThreadCommon() { +} + +const char *CommandObjCJThreadCommon::GetCJThreadInfoExp(Target *target) { + auto arch_type = target->GetArchitecture().GetTriple().getArch(); + switch (arch_type) { + case llvm::Triple::aarch64: + return m_info_aarch64; + case llvm::Triple::x86_64: +#if defined(__linux__) || defined(__APPLE__) + return m_info_x86_64; +#endif +#if defined(_WIN32) + return m_info_x86_64_win; +#endif + return nullptr; + default: + break; + } + return nullptr; +} + +static void SetUserExpressionOptions(lldb_private::EvaluateExpressionOptions &options, Target *target) { + options.SetUseDynamic(target->GetPreferDynamicValue()); + options.SetUnwindOnError(true); + options.SetIgnoreBreakpoints(true); + options.SetLanguage(target->GetLanguage()); + options.SetCangjieUserExpr(false); +} + +uint64_t CommandObjCJThreadCommon::GetCJThreadCount(ExecutionContext exe_ctx, Status &error) { + ValueObjectSP count_sp; + lldb_private::EvaluateExpressionOptions options; + SetUserExpressionOptions(options, exe_ctx.GetTargetPtr()); + UserExpression::Evaluate(exe_ctx, options, m_count_exp, "", count_sp, error); + + if (error.Fail()) { + return 0; + } + + if (count_sp) { + uint64_t count = count_sp->GetValueAsUnsigned(UINT64_MAX); + if (count == UINT64_MAX) { + error.SetErrorString("Get cjthread count failed\n"); + return 0; + } + m_cjthread_count = count; + return m_cjthread_count; + } + + return 0; +} + +lldb::ValueObjectSP CommandObjCJThreadCommon::GetCJThreadInfo(ExecutionContext exe_ctx, Status &error) { + auto info_base_exp = GetCJThreadInfoExp(exe_ctx.GetTargetPtr()); + if (info_base_exp == nullptr) { + error.SetErrorString("command cjthread does not support for this arch type\n"); + return ValueObjectSP(); + } + + GetCJThreadCount(exe_ctx, error); + if (error.Fail()) { + return ValueObjectSP(); + } + std::string cjthread_count = std::to_string(m_cjthread_count); + std::string info_tail = std::string("CJThreadInfo cjdb_cjthread_info[") + cjthread_count + + std::string("] = {0}; void *cjdb_thread_info_ptr = reinterpret_cast(cjdb_cjthread_info); " + "(int)CJ_MRT_GetAllCJThreadInfo(cjdb_thread_info_ptr, ") + cjthread_count + std::string("); cjdb_cjthread_info;"); + std::string info_exp = std::string(info_base_exp) + std::string(m_info_common) + info_tail; + + ValueObjectSP info_sp; + lldb_private::EvaluateExpressionOptions options; + SetUserExpressionOptions(options, exe_ctx.GetTargetPtr()); + UserExpression::Evaluate(exe_ctx, options, info_exp.c_str(), "", info_sp, error); + + if (error.Fail()) { + return ValueObjectSP(); + } + + m_cjthread_info = info_sp; + return m_cjthread_info; +} + +void CommandObjCJThreadCommon::SetCJThreadRegisterContext(ValueObjectSP cjthread_context, + ProcessSP process) { + ValueObjectSP state_sp = cjthread_context->GetChildMemberWithName(ConstString("state"), true); + auto state = state_sp->GetValueAsUnsigned(UINT64_MAX); + if (state == UINT64_MAX) { + process->CJThreadRegContextClear(); + return; + } + if (state != static_cast(State::eRunning) && + state != static_cast(State::eSyscall)) { + process->SetCJThreadRegContext(m_regcontext); + } else { + process->CJThreadRegContextClear(); + } +} + +void CommandObjCJThreadCommon::HandOneCJThread(ThreadSP thread, ValueObjectSP cjthread_context, + Status &error, bool show_all_frames) { + auto process = thread->GetProcess(); + m_regcontext.clear(); + + if (cjthread_context) { + auto reg_context = cjthread_context->GetChildMemberWithName(ConstString("context"), true); + if (!reg_context) { + error.SetErrorString("Failed to get reg context"); + return; + } + auto count = reg_context->GetNumChildren(); + for (size_t i = 0; i < count; i++) { + auto reg = reg_context->GetChildAtIndex(i, true); + m_regcontext.insert(std::make_pair(reg->GetName(), reg->GetValueAsUnsigned(UINT64_MAX))); + } + } + + SetCJThreadRegisterContext(cjthread_context, process); + UnwindLLDB unwinder = UnwindLLDB(*thread); + uint32_t idx = 0; + m_frames.clear(); + do { + lldb::addr_t pc = LLDB_INVALID_ADDRESS; + lldb::addr_t cfa = LLDB_INVALID_ADDRESS; + bool behaves_like_zeroth_frame = (idx == 0); + const bool success = unwinder.GetFrameInfoAtIndex(idx, cfa, pc, behaves_like_zeroth_frame); + if (!success) { + // We've gotten to the end of the stack. + break; + } + + StackFrameSP unwind_frame_sp; + if (idx == 0) { + unwind_frame_sp = std::make_shared( + thread, m_frames.size(), idx, nullptr, cfa, pc, + behaves_like_zeroth_frame, nullptr); + } else { + unwind_frame_sp = std::make_shared( + thread, m_frames.size(), idx, cfa, true, + pc, StackFrame::Kind::Regular, behaves_like_zeroth_frame, nullptr); + } + m_frames.push_back(unwind_frame_sp); + idx++; + } while (show_all_frames); + + process->CJThreadRegContextClear(); + return; +} + +const char *CommandObjCJThreadCommon::CJThreadStateToString(State state) { + static std::map state_map = { + {State::eIdle, "idle"}, {State::eReady, "ready"}, {State::eRunning, "running"}, + {State::ePending, "pending"}, {State::eSyscall, "syscall"} }; + if (state_map.find(state) != state_map.end()) { + return state_map[state].c_str(); + } + + return "unknown"; +} + +std::string CommandObjCJThreadCommon::GetCJThreadName(lldb::ValueObjectSP cjthread_value) { + ValueObjectSP name_sp = cjthread_value->GetChildMemberWithName(ConstString("name"), true); + if (name_sp == nullptr) { + return ""; + } + StreamString result; + DumpValueObjectOptions option; + option.SetHideRootType(true); + option.SetVariableFormatDisplayLanguage(name_sp->GetPreferredDisplayLanguage()); + name_sp->Dump(result, option); + std::string value = result.GetData(); + std::string prefix = " = \""; + size_t start = value.find_first_of(prefix); + size_t end = value.find_last_of("\""); + std::string name; + if (start != std::string::npos && end != std::string::npos && start < end) { + name = value.substr(start + prefix.size(), end - start - prefix.size()).c_str(); + } + + return name; +} + +uint64_t CommandObjCJThreadCommon::GetCJThreadID(lldb::ValueObjectSP cjthread_value) { + ValueObjectSP id_sp = cjthread_value->GetChildMemberWithName(ConstString("id"), true); + if (id_sp == nullptr) { + return UINT64_MAX; + } + uint64_t id = id_sp->GetValueAsUnsigned(UINT64_MAX); + if (id == UINT64_MAX) { + return UINT64_MAX; + } + + return id; +} + +std::string CommandObjCJThreadCommon::GetCJThreadState(lldb::ValueObjectSP cjthread_value) { + ValueObjectSP state_sp = cjthread_value->GetChildMemberWithName(ConstString("state"), true); + if (state_sp == nullptr) { + return ""; + } + uint64_t state = state_sp->GetValueAsUnsigned(UINT64_MAX); + if (state == UINT64_MAX) { + return ""; + } + + return CJThreadStateToString(static_cast(static_cast(state))); +} + +bool CommandObjCJThreadCommon::GetDescription(Stream &strm, Status &error, ValueObjectSP cjthread_value) { + std::string name = GetCJThreadName(cjthread_value); + std::string state = GetCJThreadState(cjthread_value); + uint64_t id = GetCJThreadID(cjthread_value); + if (id == UINT64_MAX) { + return false; + } + strm.Printf("cjthread #%lu state: %s name: %s\n", id, state.c_str(), name.c_str()); + + return true; +} + +bool CommandObjCJThreadCommon::GetFramesStatus(Stream &strm, Status &error, uint32_t first_frame, + uint32_t num_frames) { + if (m_frames.size() == 0) { + error.SetErrorStringWithFormat("unknow error"); + return false; + } + + if (first_frame > m_frames.size()) { + error.SetErrorStringWithFormat("invalid frame index \"%u\"", first_frame); + return false; + } + + if (num_frames <= 0) { + error.SetErrorStringWithFormat("invalid frame count \"%u\"", num_frames); + return false; + } + + uint32_t last_frame = first_frame + num_frames; + if (last_frame > m_frames.size()) { + last_frame = m_frames.size(); + } + + strm.IndentMore(); + for (uint32_t index = first_frame; index < last_frame; index++) { + auto frame_sp = m_frames[index]; + if (!frame_sp) { + break; + } + frame_sp->GetStatus(strm, true, false, false); + } + strm.IndentLess(); + return true; +} + +bool CommandObjCJThreadCommon::GetCurrentCJthreadStatus(ThreadSP thread, CommandReturnObject &result, + ValueObjectSP &info, uint32_t first_frame, + uint32_t num_frames) { + uint32_t thread_tid = thread->GetProtocolID(); + auto cjthread_context_sp = GetCJthreadInfoByThreadID(info, thread_tid); + return GetCJThreadStatus(thread, result, cjthread_context_sp, first_frame, num_frames); +} + +bool CommandObjCJThreadCommon::GetCJThreadStatus(ThreadSP thread, CommandReturnObject &result, + ValueObjectSP cjthread_context, + uint32_t first_frame, uint32_t num_frames) { + Status error; + Stream &stream = result.GetOutputStream(); + if (!cjthread_context) { + result.AppendErrorWithFormat("invalid cjthread context"); + } + GetDescription(stream, error, cjthread_context); + if (error.Fail()) { + result.AppendErrorWithFormat("%s", error.AsCString()); + return false; + } + HandOneCJThread(thread, cjthread_context, error); + if (error.Fail()) { + result.AppendErrorWithFormat("%s", error.AsCString()); + return false; + } + GetFramesStatus(stream, error, first_frame, num_frames); + if (error.Fail()) { + result.AppendErrorWithFormat("%s", error.AsCString()); + return false; + } + + return true; +} + +ValueObjectSP CommandObjCJThreadCommon::GetCJthreadInfoByCJThreadID(ValueObjectSP cjthread_info, uint32_t id) { + if (!cjthread_info) { + return nullptr; + } + + for (uint32_t i = 0; i < cjthread_info->GetNumChildren(); i++) { + ValueObjectSP child_sp = cjthread_info->GetChildAtIndex(i, true); + if (!child_sp) { + continue; + } + ValueObjectSP cjthread_id = child_sp->GetChildMemberWithName(ConstString("id"), true); + if (!cjthread_id) { + continue; + } + if (cjthread_id->GetValueAsUnsigned(UINT64_MAX) == static_cast(id)) { + return child_sp; + } + } + + return nullptr; +} + +ValueObjectSP CommandObjCJThreadCommon::GetCJthreadInfoByThreadID(ValueObjectSP cjthread_info, uint32_t ID) { + if (!cjthread_info) { + return ValueObjectSP(); + } + + for (uint32_t i = 0; i < cjthread_info->GetNumChildren(); i++) { + ValueObjectSP child_sp = cjthread_info->GetChildAtIndex(i, true); + if (!child_sp) { + continue; + } + ValueObjectSP tid_sp = child_sp->GetChildMemberWithName(ConstString("tid"), true); + if (!tid_sp) { + continue; + } + auto id = tid_sp->GetValueAsUnsigned(UINT64_MAX); + if (id == static_cast(ID)) { + return child_sp; + } + } + + return ValueObjectSP(); +} diff --git a/lldb/source/Commands/CommandObjCJThreadCommon.h b/lldb/source/Commands/CommandObjCJThreadCommon.h new file mode 100644 index 000000000..60cc3cfc0 --- /dev/null +++ b/lldb/source/Commands/CommandObjCJThreadCommon.h @@ -0,0 +1,150 @@ +//===-- CommandObjCJThreadCommon.h ----------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_COMMANDS_OBJCJTHREADCOMMON_H +#define LLDB_SOURCE_COMMANDS_OBJCJTHREADCOMMON_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Core/ValueObject.h" + +namespace lldb_private { +class CommandObjCJThreadCommon { +public: + CommandObjCJThreadCommon(); + ~CommandObjCJThreadCommon(); + + enum class State { + eIdle, + eReady, + eRunning, + ePending, + eSyscall, + eUnknown + }; + uint64_t GetCJThreadCount(ExecutionContext exe_ctx, Status &error); + lldb::ValueObjectSP GetCJThreadInfo(ExecutionContext exe_ctx, Status &error); + void HandOneCJThread(lldb::ThreadSP thread, lldb::ValueObjectSP cjthread_context, + Status &error, bool show_all_frames = true); + std::string GetCJThreadState(lldb::ValueObjectSP cjthread_value); + uint64_t GetCJThreadID(lldb::ValueObjectSP cjthread_value); + std::string GetCJThreadName(lldb::ValueObjectSP cjthread_value); + std::vector m_frames; +protected: + + const char *GetCJThreadInfoExp(Target *target); + bool GetCurrentCJthreadStatus(lldb::ThreadSP thread, CommandReturnObject &result, + lldb::ValueObjectSP &info, uint32_t first_frame, uint32_t num_frames); + bool GetDescription(Stream &strm, Status &error, lldb::ValueObjectSP cjthread_value); + bool GetFramesStatus(Stream &strm, Status &error, uint32_t first_frame, uint32_t num_frames); + lldb::ValueObjectSP GetCJthreadInfoByCJThreadID(lldb::ValueObjectSP cjthread_info, uint32_t ID); + lldb::ValueObjectSP GetCJthreadInfoByThreadID(lldb::ValueObjectSP cjthread_info, uint32_t ID); + void SetCJThreadRegisterContext(lldb::ValueObjectSP cjthread_context, lldb::ProcessSP process); + bool GetCJThreadStatus(lldb::ThreadSP thread, CommandReturnObject &result, + lldb::ValueObjectSP cjthread_context, uint32_t first_frame, uint32_t num_frames); + const char *CJThreadStateToString(State state); + uint64_t m_cjthread_count = 0; + lldb::ValueObjectSP m_cjthread_info; + +private: + std::map m_regcontext; + const char *m_count_exp = + "unsigned long long cjdb_cjthread_count = (unsigned long long)CJ_MRT_GetCJThreadNumberUnsafe();" + "cjdb_cjthread_count"; +#if defined(__linux__) || defined(__APPLE__) + const char *m_info_x86_64 = + "struct CJThreadContext {" + " unsigned long long rsp;" + " unsigned long long rbp;" + " unsigned long long rbx;" + " unsigned long long rip;" + " unsigned long long r12;" + " unsigned long long r13;" + " unsigned long long r14;" + " unsigned long long r15;" + " unsigned int mxcsr;" + " unsigned short fpuCw;" + "};"; +#endif +#if defined(_WIN32) +const char *m_info_x86_64_win = + "struct CJThreadContext {" + " unsigned long long rsp;" + " unsigned long long rbp;" + " unsigned long long rbx;" + " unsigned long long rip;" + " unsigned long long r12;" + " unsigned long long r13;" + " unsigned long long r14;" + " unsigned long long r15;" + " unsigned int mxcsr;" + " unsigned short fpuCw;" + " unsigned long long rdi;" + " unsigned long long rsi;" + " unsigned long long xmm6[2];" + " unsigned long long xmm7[2];" + " unsigned long long xmm8[2];" + " unsigned long long xmm9[2];" + " unsigned long long xmm10[2];" + " unsigned long long xmm11[2];" + " unsigned long long xmm12[2];" + " unsigned long long xmm13[2];" + " unsigned long long xmm14[2];" + " unsigned long long xmm15[2];" + " unsigned long long gsStackLow;" + " unsigned long long gsStackHigh;" + " unsigned long long gsStackDeallocation;" + "};"; +#endif + const char *m_info_aarch64 = + "struct CJThreadContext {" + " unsigned long long x18;" + " unsigned long long x19;" + " unsigned long long x20;" + " unsigned long long x21;" + " unsigned long long x22;" + " unsigned long long x23;" + " unsigned long long x24;" + " unsigned long long x25;" + " unsigned long long x26;" + " unsigned long long x27;" + " unsigned long long x28;" + " unsigned long long fp;" + " unsigned long long lr;" + " unsigned long long pc;" + " unsigned long long sp;" + " unsigned long long d8;" + " unsigned long long d9;" + " unsigned long long d10;" + " unsigned long long d11;" + " unsigned long long d12;" + " unsigned long long d13;" + " unsigned long long d14;" + " unsigned long long d15;" + " unsigned int fpcr;" + "};"; + const char *m_info_common = + "struct CJThreadInfo {" + " char name[32];" + " int state;" + " unsigned int arg_size;" + " unsigned int process_id;" + " unsigned int tid;" + " void *arg_start;" + " unsigned long sp;" + " unsigned long pc;" + " unsigned long long id;" + " unsigned long long pthread_id;" + " struct CJThreadContext context;" + "};"; +}; + +} +#endif /* LLDB_SOURCE_COMMANDS_OBJCJTHREADCOMMON_H */ diff --git a/lldb/source/Commands/CommandObjectCJThread.cpp b/lldb/source/Commands/CommandObjectCJThread.cpp new file mode 100644 index 000000000..cb88faba4 --- /dev/null +++ b/lldb/source/Commands/CommandObjectCJThread.cpp @@ -0,0 +1,272 @@ +//===-- CommandObjectCJThread.cpp -----------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectCJThread.h" +#include "Commands/CommandObjCJThreadCommon.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Host/OptionParser.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandOptionArgumentTable.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/State.h" +#include "lldb/Expression/UserExpression.h" +#include "lldb/DataFormatters/StringPrinter.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/DumpValueObjectOptions.h" +#include "lldb/DataFormatters/ValueObjectPrinter.h" + +using namespace lldb; +using namespace lldb_private; + +// CommandObjectThreadBacktrace +#define LLDB_OPTIONS_cjthread_backtrace +#include "CommandOptions.inc" + +class CommandObjectCJThreadBacktrace : public CommandObjectParsed, + public CommandObjCJThreadCommon { +public: + class CommandOptions : public Options { + public: + CommandOptions() { + // Keep default values of all options in one place: OptionParsingStarting + OptionParsingStarting(nullptr); + } + + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) { + case 'c': { + int32_t input_count = 0; + if (option_arg.getAsInteger(0, m_count)) { + m_count = UINT32_MAX; + error.SetErrorStringWithFormat( + "invalid integer value for option '%c'", short_option); + } else if (input_count < 0) { + m_count = UINT32_MAX; + } + } + break; + case 's': + if (option_arg.getAsInteger(0, m_start)) { + error.SetErrorStringWithFormat( + "invalid integer value for option '%c'", short_option); + } + break; + default: + llvm_unreachable("Unimplemented option"); + } + + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_count = UINT32_MAX; + m_start = 0; + } + + llvm::ArrayRef GetDefinitions() override { + return llvm::makeArrayRef(g_cjthread_backtrace_options); + } + + // Instance variables to hold the values for command options. + uint32_t m_count; + uint32_t m_start; + }; + + explicit CommandObjectCJThreadBacktrace(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "cjthread backtrace", + "Show cjthread call stacks. Defaults to the current thread, thread " + "indexes can be specified as arguments.\n" + "Use the thread-index \"unique\" to see cjthreads grouped by unique " + "call stacks.\n", + nullptr, + eCommandRequiresProcess | eCommandRequiresThread | + eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | + eCommandProcessMustBePaused), + CommandObjCJThreadCommon() { + CommandArgumentData cjthread_arg{eArgTypeCJThreadIndex, eArgRepeatStar}; + m_arguments.push_back({cjthread_arg}); + } + + ~CommandObjectCJThreadBacktrace() override = default; + + Options *GetOptions() override { return &m_options; } + + llvm::Optional GetRepeatCommand(Args ¤t_args, + uint32_t idx) override { + llvm::StringRef count_opt("--count"); + llvm::StringRef start_opt("--start"); + + // If no "count" was provided, we are dumping the entire backtrace, so + // there isn't a repeat command. So we search for the count option in + // the args, and if we find it, we make a copy and insert or modify the + // start option's value to start count indices greater. + + Args copy_args(current_args); + size_t num_entries = copy_args.GetArgumentCount(); + // These two point at the index of the option value if found. + size_t count_idx = 0; + size_t start_idx = 0; + size_t count_val = 0; + size_t start_val = 0; + + for (size_t idx = 0; idx < num_entries; idx++) { + llvm::StringRef arg_string = copy_args[idx].ref(); + if (arg_string.equals("-c") || count_opt.startswith(arg_string)) { + idx++; + if (idx == num_entries) { + return llvm::None; + } + count_idx = idx; + if (copy_args[idx].ref().getAsInteger(0, count_val)) { + return llvm::None; + } + } else if (arg_string.equals("-s") || start_opt.startswith(arg_string)) { + idx++; + if (idx == num_entries) { + return llvm::None; + } + start_idx = idx; + if (copy_args[idx].ref().getAsInteger(0, start_val)) { + return llvm::None; + } + } + } + if (count_idx == 0) { + return llvm::None; + } + + std::string new_start_val = llvm::formatv("{0}", start_val + count_val); + if (start_idx == 0) { + copy_args.AppendArgument(start_opt); + copy_args.AppendArgument(new_start_val); + } else { + copy_args.ReplaceArgumentAtIndex(start_idx, new_start_val); + } + std::string repeat_command; + if (!copy_args.GetQuotedCommandString(repeat_command)) { + return llvm::None; + } + + return repeat_command; + } + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + Status error; + GetCJThreadInfo(m_exe_ctx, error); + if (error.Fail()) { + result.AppendErrorWithFormat("%s", error.AsCString()); + return false; + } + + const size_t num_args = command.GetArgumentCount(); + Process *process = m_exe_ctx.GetProcessPtr(); + std::lock_guard guard( + process->GetThreadList().GetMutex()); + + if (num_args == 0) { + return GetCurrentCJthreadStatus(m_exe_ctx.GetThreadSP(), result, m_cjthread_info, + m_options.m_start, m_options.m_count); + } + + for (size_t i = 0; i < num_args; i++) { + uint32_t cjthread_idx; + if (!llvm::to_integer(command.GetArgumentAtIndex(i), cjthread_idx)) { + result.AppendErrorWithFormat("invalid cjthread specification: \"%s\"\n", + command.GetArgumentAtIndex(i)); + continue; + } + + auto cjthread_context = GetCJthreadInfoByCJThreadID(m_cjthread_info, cjthread_idx); + if (!cjthread_context) { + result.AppendErrorWithFormat("invalid cjthread index \"%u\"", cjthread_idx); + continue; + } + + if (!GetCJThreadStatus(m_exe_ctx.GetThreadSP(), result, cjthread_context, + m_options.m_start, m_options.m_count)) { + return false; + } + } + + return result.Succeeded(); + } + +private: + CommandOptions m_options; +}; + +// CommandObjectCJThreadList +class CommandObjectCJThreadList : public CommandObjectParsed, + public CommandObjCJThreadCommon { +public: + explicit CommandObjectCJThreadList(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "cjthread list", + "Show a summary of each cjthread in the current target process.", + "cjthread list", + eCommandRequiresProcess | eCommandTryTargetAPILock | + eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), + CommandObjCJThreadCommon() {} + + ~CommandObjectCJThreadList() override = default; + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + Status error; + GetCJThreadInfo(m_exe_ctx, error); + if (error.Fail()) { + result.AppendErrorWithFormat("%s", error.AsCString()); + return false; + } + + Process *process = m_exe_ctx.GetProcessPtr(); + std::lock_guard guard( + process->GetThreadList().GetMutex()); + for (uint32_t i = 0; i < m_cjthread_count; i++) { + auto cjthread_context = m_cjthread_info->GetChildAtIndex(i, true); + if (!GetCJThreadStatus(m_exe_ctx.GetThreadSP(), result, cjthread_context, 0, 1)) { + return false; + } + } + + return result.Succeeded(); + } +}; + +CommandObjectMultiwordCJThread::CommandObjectMultiwordCJThread( + CommandInterpreter &interpreter) + : CommandObjectMultiword(interpreter, "cjthread", + "Commands for operating on " + "one or more cjthread in " + "the current process.", + "cjthread []") { + LoadSubCommand("backtrace", + CommandObjectSP(new CommandObjectCJThreadBacktrace(interpreter))); + LoadSubCommand("list", + CommandObjectSP(new CommandObjectCJThreadList(interpreter))); +} + +CommandObjectMultiwordCJThread::~CommandObjectMultiwordCJThread() = default; diff --git a/lldb/source/Commands/CommandObjectCJThread.h b/lldb/source/Commands/CommandObjectCJThread.h new file mode 100644 index 000000000..c68dd0bcd --- /dev/null +++ b/lldb/source/Commands/CommandObjectCJThread.h @@ -0,0 +1,26 @@ +//===-- CommandObjectCJThread.h ---------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTCJTHREAD_H +#define LLDB_SOURCE_COMMANDS_COMMANDOBJECTCJTHREAD_H + +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +class CommandObjectMultiwordCJThread : public CommandObjectMultiword { +public: + CommandObjectMultiwordCJThread(CommandInterpreter &interpreter); + + ~CommandObjectMultiwordCJThread() override; +}; + +} // namespace lldb_private +#endif // LLDB_SOURCE_COMMANDS_COMMANDOBJECTCJTHREAD_H \ No newline at end of file diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp index 083309121..22e3e4782 100644 --- a/lldb/source/Commands/CommandObjectExpression.cpp +++ b/lldb/source/Commands/CommandObjectExpression.cpp @@ -409,6 +409,10 @@ bool CommandObjectExpression::EvaluateExpression(llvm::StringRef expr, m_fixed_expression.c_str()); } + if (success != eExpressionCompleted) { + error_stream.PutCString("error: unsupported expression\n"); + } + if (result_valobj_sp) { Format format = m_format_options.GetFormat(); @@ -437,6 +441,15 @@ bool CommandObjectExpression::EvaluateExpression(llvm::StringRef expr, result.SetStatus(eReturnStatusSuccessFinishResult); } } else { + bool isParentNull = false; + const char *error_cstr = result_valobj_sp->GetError().AsCString(); + if (error_cstr && error_cstr[0]) { + if (strstr(error_cstr, "parent is NULL") == error_cstr) + isParentNull = true; + } + ConstString match("^std[.]core::Option<.+>( \\*)?$"); + RegularExpression regex(match.GetStringRef()); + bool isRefOption = regex.Execute(result_valobj_sp->GetTypeName().AsCString()); if (result_valobj_sp->GetError().GetError() == UserExpression::kNoResult) { if (format != eFormatVoid && GetDebugger().GetNotifyVoid()) { @@ -444,6 +457,15 @@ bool CommandObjectExpression::EvaluateExpression(llvm::StringRef expr, } result.SetStatus(eReturnStatusSuccessFinishResult); + } else if (isRefOption && isParentNull) { + DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions( + m_command_options.m_verbosity, format)); + options.SetVariableFormatDisplayLanguage( + result_valobj_sp->GetPreferredDisplayLanguage()); + + output_stream.Printf("(%s) %s = None", result_valobj_sp->GetDisplayTypeName().AsCString(), + result_valobj_sp->GetName().AsCString()); + result.SetStatus(eReturnStatusSuccessFinishResult); } else { const char *error_cstr = result_valobj_sp->GetError().AsCString(); if (error_cstr && error_cstr[0]) { diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp index 23954dd3c..9b71e9466 100644 --- a/lldb/source/Commands/CommandObjectFrame.cpp +++ b/lldb/source/Commands/CommandObjectFrame.cpp @@ -386,6 +386,7 @@ protected: #pragma mark CommandObjectFrameVariable // List images with associated information + class CommandObjectFrameVariable : public CommandObjectParsed { public: CommandObjectFrameVariable(CommandInterpreter &interpreter) @@ -669,7 +670,15 @@ protected: valobj_sp->GetPreferredDisplayLanguage()); options.SetRootValueObjectName( var_sp ? var_sp->GetName().AsCString() : nullptr); - valobj_sp->Dump(result.GetOutputStream(), options); + if (m_option_variable.show_values) { + if (valobj_sp->GetName() == "$CapturedVars") { + continue; + } + valobj_sp->Dump(result.GetOutputStream(), options); + } else { + result.GetOutputStream().Printf("(%s) %s =\n", + valobj_sp->GetTypeName().AsCString(), valobj_sp->GetName().AsCString()); + } } } } diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp index 28a99ea3d..1fe8a2d21 100644 --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -107,6 +107,22 @@ protected: std::string m_new_process_action; }; +#if defined(__APPLE__) +static void SetSigBusvForCangjie(ProcessSP process_sp) { + // Set SIGBUS as default action PASS:true STOP:false NOTIFY:false for macos + bool stop_action = false; + bool pass_action = true; + bool notify_action = false; + UnixSignalsSP signals_sp = process_sp->GetUnixSignals(); + int32_t signo = signals_sp->GetSignalNumberFromName("SIGBUS"); + if (signo != LLDB_INVALID_SIGNAL_NUMBER) { + signals_sp->SetShouldStop(signo, stop_action); + signals_sp->SetShouldSuppress(signo, !pass_action); + signals_sp->SetShouldNotify(signo, notify_action); + } +} +#endif + // CommandObjectProcessLaunch #pragma mark CommandObjectProcessLaunch class CommandObjectProcessLaunch : public CommandObjectProcessLaunchOrAttach { @@ -256,6 +272,9 @@ protected: if (error.Success()) { ProcessSP process_sp(target->GetProcessSP()); +#if defined(__APPLE__) + SetSigBusvForCangjie(process_sp); +#endif if (process_sp) { // There is a race condition where this thread will return up the call // stack to the main command handler and show an (lldb) prompt before @@ -421,6 +440,9 @@ protected: if (error.Success()) { process_sp = target->GetProcessSP(); if (process_sp) { +#if defined(__APPLE__) + SetSigBusvForCangjie(process_sp); +#endif result.AppendMessage(stream.GetString()); result.SetStatus(eReturnStatusSuccessFinishNoResult); result.SetDidChangeProcessState(true); diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index cc47db575..6488b31e4 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -1035,6 +1035,13 @@ let Command = "thread backtrace" in { Arg<"Boolean">, Desc<"Show the extended backtrace, if available">; } +let Command = "cjthread backtrace" in { + def cjthread_backtrace_count : Option<"count", "c">, Group<1>, Arg<"Count">, + Desc<"How many frames to display">; + def cjthread_backtrace_start : Option<"start", "s">, Group<1>, + Arg<"FrameIndex">, Desc<"Frame in which to start the backtrace">; +} + let Command = "thread step scope" in { def thread_step_scope_step_in_avoids_no_debug : Option<"step-in-avoids-no-debug", "a">, Group<1>, Arg<"Boolean">, diff --git a/lldb/source/Core/CMakeLists.txt b/lldb/source/Core/CMakeLists.txt index d90717758..7ac029fdb 100644 --- a/lldb/source/Core/CMakeLists.txt +++ b/lldb/source/Core/CMakeLists.txt @@ -10,7 +10,10 @@ set(LLDB_CURSES_LIBS) set(LLDB_LIBEDIT_LIBS) if (LLDB_ENABLE_CURSES) - list(APPEND LLDB_CURSES_LIBS ${CURSES_LIBRARIES} ${PANEL_LIBRARIES}) + # Since PANEL_LIBRARIES uses CURSES_LIBRARIES, to link them statically we have to + # link CURSES_LIBRARIES twice so all PANEL_LIBRARIES symbols could be resolved. + # Linking twice here should not affect dynamic linking. + list(APPEND LLDB_CURSES_LIBS ${CURSES_LIBRARIES} ${PANEL_LIBRARIES} ${CURSES_LIBRARIES}) if(LLVM_ENABLE_TERMINFO) list(APPEND LLDB_CURSES_LIBS ${TERMINFO_LIB}) endif() @@ -19,6 +22,14 @@ if (LLDB_ENABLE_CURSES) endif() endif() +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../utils/demangle) +link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../utils/demangle) + +set(LLDB_DEMANGLE_LIBS) +if (CMAKE_SYSTEM_NAME MATCHES "Windows") + list(APPEND LLDB_DEMANGLE_LIBS ssp) +endif () + add_lldb_library(lldbCore Address.cpp AddressRange.cpp @@ -86,6 +97,8 @@ add_lldb_library(lldbCore lldbPluginCPlusPlusLanguage lldbPluginObjCLanguage ${LLDB_CURSES_LIBS} + ${CMAKE_CURRENT_SOURCE_DIR}/../../../utils/demangle/libcangjie-demangle.a + ${LLDB_DEMANGLE_LIBS} CLANG_LIBS clangDriver diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index 6e7f44e46..03a4e913b 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -1534,7 +1534,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, } else { const char *name = nullptr; if (sc->function) - name = sc->function->GetName().AsCString(nullptr); + name = sc->function->GetName(sc).AsCString(nullptr); else if (sc->symbol) name = sc->symbol->GetName().AsCString(nullptr); @@ -1581,7 +1581,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, } else { ConstString name; if (sc->function) - name = sc->function->GetNameNoArguments(); + name = sc->function->GetNameNoArguments(sc); else if (sc->symbol) name = sc->symbol->GetNameNoArguments(); if (name) { @@ -1616,7 +1616,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, if (sc->function) { ExecutionContextScope *exe_scope = exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; - const char *cstr = sc->function->GetName().AsCString(nullptr); + const char *cstr = sc->function->GetName(sc).AsCString(nullptr); if (cstr) { const InlineFunctionInfo *inline_info = nullptr; VariableListSP variable_list_sp; diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index 0c4d9f78c..81b0f2afc 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -4,6 +4,12 @@ // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// //===----------------------------------------------------------------------===// #include "lldb/Core/Mangled.h" @@ -18,10 +24,13 @@ #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/Stream.h" #include "lldb/lldb-enumerations.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/VariableList.h" #include "llvm/ADT/StringRef.h" #include "llvm/Demangle/Demangle.h" #include "llvm/Support/Compiler.h" +#include "CangjieDemangle.h" #include #include @@ -41,6 +50,9 @@ Mangled::ManglingScheme Mangled::GetManglingScheme(llvm::StringRef const name) { if (name.empty()) return Mangled::eManglingSchemeNone; + if (name.startswith("_C")) + return Mangled::eManglingSchemeCangjie; + if (name.startswith("?")) return Mangled::eManglingSchemeMSVC; @@ -62,7 +74,7 @@ Mangled::ManglingScheme Mangled::GetManglingScheme(llvm::StringRef const name) { Mangled::Mangled(ConstString s) : m_mangled(), m_demangled() { if (s) - SetValue(s); + SetValue(s, true); } Mangled::Mangled(llvm::StringRef name) { @@ -211,6 +223,7 @@ bool Mangled::GetRichManglingInfo(RichManglingContext &context, // The current mangled_name_filter would allow llvm_unreachable here. return false; + case eManglingSchemeCangjie: case eManglingSchemeItanium: // We want the rich mangling info here, so we don't care whether or not // there is a demangled string in the pool already. @@ -250,14 +263,121 @@ bool Mangled::GetRichManglingInfo(RichManglingContext &context, llvm_unreachable("Fully covered switch above!"); } +static void CollectionGenericParamesFromFunction(const SymbolContext &const_sc, std::vector &vector) { + SymbolContext &sc = const_cast(const_sc); + if (!sc.function) { + return; + } + + auto block = sc.GetFunctionBlock(); + if (!block) { + return; + } + auto var_list = block->GetBlockVariableList(true); + if (!var_list) { + return; + } + + for (unsigned i = 0; i < var_list->GetSize(); ++i) { + auto var_sp = var_list->GetVariableAtIndex(i); + auto name = var_sp->GetName().GetStringRef(); + if (!name.consume_front("$G_")) { + continue; + } + + uint64_t index; + // Radix is 10 + if (name.consumeInteger(10, index)) { + continue; + } + + Type *type = var_sp->GetType(); + if (!type) + continue; + + vector.push_back(type->GetName().AsCString()); + } +} + +static std::string GetDemangleInfo(const char *mangled, const SymbolContext *sc) { + Log *log = GetLog(LLDBLog::Demangle); + std::string name(mangled); + std::string suffix(".objKlass"); + auto pos = name.find(suffix); + if (pos != std::string::npos && pos == name.size() - suffix.size()) { + name.erase(pos); + } + std::vector parames; + if (sc && sc->function) { + CollectionGenericParamesFromFunction(*sc, parames); + } + + auto demangle_info = Cangjie::Demangle(name, parames); + std::string pkgname = demangle_info.GetPkgName(); + std::string fullname = demangle_info.GetFullName(); + if (!pkgname.empty()) { + fullname = pkgname + std::string("::") + fullname; + } + LLDB_LOG(log, "Cangjie Demangle: %s->%s", mangled, fullname.c_str()); + return fullname; +} + +std::string Mangled::GetDemangledTypeName(std::string &type_name) { + auto demangle_info = Cangjie::DemangleType(type_name); + std::string fullname = demangle_info.GetFullName(); + return fullname; +} + +ConstString Mangled::GetDemangledBaseName(const SymbolContext *sc) const { + std::string demangled; + if (m_mangled && m_demangled.IsNull()) { + demangled = GetDemangleInfo(m_mangled.AsCString(), sc); + } + if (!m_demangled.IsNull()) { + demangled = std::string(m_demangled.AsCString()); + } + + if (demangled.find("(") != std::string::npos) { + demangled = demangled.substr(0, demangled.find("(")); + } else { + return ConstString(); + } + + if (demangled.find_last_of("::") != std::string::npos) { + demangled = demangled.substr(demangled.find_last_of("::") + 1); + } + + while (true) { + auto left = demangled.find_first_of("<"); + auto right = demangled.find_first_of(">"); + if (left != std::string::npos && right != std::string::npos && left < right) { + demangled.erase(left, right - left + 1); + continue; + } + break; + } + if (!demangled.empty()) { + return ConstString(demangled.c_str()); + } + + return ConstString(); +} + // Generate the demangled name on demand using this accessor. Code in this // class will need to use this accessor if it wishes to decode the demangled // name. The result is cached and will be kept until a new string value is // supplied to this object, or until the end of the object's lifetime. -ConstString Mangled::GetDemangledName() const { +ConstString Mangled::GetDemangledName(const SymbolContext *sc) const { // Check to make sure we have a valid mangled name and that we haven't // already decoded our mangled name. - if (m_mangled && m_demangled.IsNull()) { + if (m_mangled && (m_demangled.IsNull() || (sc && sc->function))) { + std::string cjdemangled = GetDemangleInfo(m_mangled.AsCString(), sc); + if (!cjdemangled.empty()) { + m_demangled.SetStringWithMangledCounterpart(llvm::StringRef(cjdemangled.c_str()), + m_mangled); + return m_demangled; + } + // Don't bother running anything that isn't mangled const char *mangled_name = m_mangled.GetCString(); ManglingScheme mangling_scheme = @@ -271,6 +391,7 @@ ConstString Mangled::GetDemangledName() const { case eManglingSchemeMSVC: demangled_name = GetMSVCDemangledStr(mangled_name); break; + case eManglingSchemeCangjie: case eManglingSchemeItanium: { demangled_name = GetItaniumDemangledStr(mangled_name); break; @@ -300,8 +421,8 @@ ConstString Mangled::GetDemangledName() const { return m_demangled; } -ConstString Mangled::GetDisplayDemangledName() const { - return GetDemangledName(); +ConstString Mangled::GetDisplayDemangledName(const SymbolContext *sc) const { + return GetDemangledName(sc); } bool Mangled::NameMatches(const RegularExpression ®ex) const { @@ -313,13 +434,20 @@ bool Mangled::NameMatches(const RegularExpression ®ex) const { } // Get the demangled name if there is one, else return the mangled name. -ConstString Mangled::GetName(Mangled::NamePreference preference) const { +ConstString Mangled::GetName(Mangled::NamePreference preference, const SymbolContext *sc) const { if (preference == ePreferMangled && m_mangled) return m_mangled; // Call the accessor to make sure we get a demangled name in case it hasn't // been demangled yet... - ConstString demangled = GetDemangledName(); + ConstString demangled = GetDemangledName(sc); + if (preference == ePreferDemangledWithoutArguments && demangled) { + std::string demangled_without_arg(demangled.AsCString()); + if (demangled_without_arg.find("(") != std::string::npos) { + demangled_without_arg = demangled_without_arg.substr(0, demangled_without_arg.find("(")); + } + return ConstString(demangled_without_arg.c_str()); + } if (preference == ePreferDemangledWithoutArguments) { if (Language *lang = Language::FindPlugin(GuessLanguage())) { diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index d80139ab1..d7cc614ac 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -48,6 +48,8 @@ #include "lldb/Utility/Stream.h" #include "lldb/Utility/StreamString.h" #include "lldb/lldb-private-types.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Symbol/TypeList.h" #include "llvm/Support/Compiler.h" @@ -217,15 +219,19 @@ bool ValueObject::UpdateFormatsIfNeeded() { SetValueFormat(DataVisualization::GetFormat(*this, eNoDynamicValues)); SetSummaryFormat( DataVisualization::GetSummaryFormat(*this, GetDynamicValueType())); -#if LLDB_ENABLE_PYTHON SetSyntheticChildren( DataVisualization::GetSyntheticChildren(*this, GetDynamicValueType())); -#endif } return any_change; } +CompilerType ValueObject::GetDynamicType() { + auto dynamic_value = new ValueObjectDynamicValue(*this, eNoDynamicValues); + dynamic_value->UpdateDynamicType(); + return dynamic_value->GetCompilerType(); +} + void ValueObject::SetNeedsUpdate() { m_update_point.SetNeedsUpdate(); // We have to clear the value string here so ConstResult children will notice @@ -245,7 +251,6 @@ void ValueObject::ClearDynamicTypeInformation() { CompilerType ValueObject::MaybeCalculateCompleteType() { CompilerType compiler_type(GetCompilerTypeImpl()); - if (m_flags.m_did_calculate_complete_objc_class_type) { if (m_override_type.IsValid()) return m_override_type; @@ -386,6 +391,10 @@ ValueObjectSP ValueObject::GetChildAtIndex(size_t idx, bool can_create) { } ValueObject *child = m_children.GetChildAtIndex(idx); + if (child && (child->GetName() == "$ti*" || child->GetName() == "$ti")) { + // Filter typeinfo of generic variable. + return child_sp; + } if (child != nullptr) return child->GetSP(); } @@ -494,7 +503,8 @@ ValueObjectSP ValueObject::GetChildMemberWithName(ConstString name, } size_t ValueObject::GetNumChildren(uint32_t max) { - UpdateValueIfNeeded(); + if (IsPossibleDynamicType()) + UpdateValueIfNeeded(false); if (max < UINT32_MAX) { if (m_flags.m_children_count_valid) { @@ -1354,7 +1364,7 @@ bool ValueObject::DumpPrintableRepresentation( str = GetSummaryAsCString(); else if (val_obj_display == eValueObjectRepresentationStyleSummary) { if (!CanProvideValue()) { - strm.Printf("%s @ %s", GetTypeName().AsCString(), + strm.Printf("%s @ %s", GetDisplayTypeName().AsCString(), GetLocationAsCString()); str = strm.GetString(); } else @@ -1463,6 +1473,11 @@ addr_t ValueObject::GetPointerValue(AddressType *address_type) { } bool ValueObject::SetValueFromCString(const char *value_str, Status &error) { + if (GetCompilerType().GetTypeName().GetStringRef().contains("$G")) { + auto dynamic_value = new ValueObjectDynamicValue(*this, eNoDynamicValues); + dynamic_value->UpdateDynamicType(); + } + error.Clear(); // Make sure our value is up to date first so that our location and location // type is valid. @@ -1593,6 +1608,11 @@ bool ValueObject::IsRuntimeSupportValue() { } bool ValueObject::IsNilReference() { + ConstString match("^std[.]core::Enum\\$Option<.+>( \\*)?$"); + RegularExpression regex(match.GetStringRef()); + if (regex.Execute(GetTypeName().AsCString())) { + return false; + } if (Language *language = Language::FindPlugin(GetObjectRuntimeLanguage())) { return language->IsNilReference(*this); } @@ -2066,6 +2086,7 @@ ValueObjectSP ValueObject::GetValueForExpressionPath_Impl( llvm::StringRef remainder = expression; while (true) { + remainder = remainder.trim(); llvm::StringRef temp_expression = remainder; CompilerType root_compiler_type = root->GetCompilerType(); @@ -2139,7 +2160,7 @@ ValueObjectSP ValueObject::GetValueForExpressionPath_Impl( ValueObjectSP child_valobj_sp = root->GetChildMemberWithName(child_name, true); - if (child_valobj_sp.get()) // we know we are done, so just return + if (!root->HasSyntheticValue() && child_valobj_sp.get()) // we know we are done, so just return { *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; @@ -2208,7 +2229,7 @@ ValueObjectSP ValueObject::GetValueForExpressionPath_Impl( ValueObjectSP child_valobj_sp = root->GetChildMemberWithName(child_name, true); - if (child_valobj_sp.get()) // store the new root and move on + if (!root->HasSyntheticValue() && child_valobj_sp.get()) // store the new root and move on { root = child_valobj_sp; remainder = next_separator; @@ -2336,6 +2357,56 @@ ValueObjectSP ValueObject::GetValueForExpressionPath_Impl( // above. assert(!bracket_expr.empty()); + bracket_expr = bracket_expr.trim(); + + if (bracket_expr.contains("..")) { + // handling Cangjie Array slice and except VArray. + ConstString match("(.+[.]|^)(VArray)<.+>$"); + RegularExpression regex(match.GetStringRef()); + if (regex.Execute(root->GetTypeName().AsCString())) { + return nullptr; + } + llvm::StringRef sleft, sright; + uint32_t low_index, high_index; + std::tie(sleft, sright) = bracket_expr.split(".."); + if (sleft.getAsInteger(0, low_index)) { + if (sleft.empty()) { + low_index = 0; + } else { + low_index = UINT32_MAX; + } + } + + if (sright.getAsInteger(0, high_index)) { + if (sright.empty()) { + high_index = UINT32_MAX; + } else { + high_index = 0; + } + } + + if (low_index > high_index) { + low_index = UINT32_MAX; + high_index = UINT32_MAX; + } + + if (low_index == 0 && high_index == 0) { + low_index = UINT32_MAX - 1; + } + + if (root->HasSyntheticValue()) { + root = root->GetSyntheticValue(); + } + + root = root->GetChildAtIndex(high_index * (UINT32_MAX + 1ULL) + low_index, true); + if (!root) { + return nullptr; + } else { + remainder = temp_expression.substr(close_bracket_position + 1); // skip ] + continue; + } + } + if (!bracket_expr.contains('-')) { // if no separator, this is of the form [N]. Note that this cannot be // an unbounded range of the form [], because that case was handled @@ -3137,3 +3208,112 @@ ValueObjectSP ValueObject::Persist() { return persistent_var_sp->GetValueObject(); } + +bool ValueObject::GetCangjieDynamicType( + TypeAndOrName &class_type_or_name, bool should_update, bool should_check_parent) { + if (IsCangjieGenericType()) { + return true; + } + auto size = GetCompilerType().GetByteSize(nullptr); + // do not find dynamicType if compilerType's size less than 8 byte. + // for interfaces, dynamic type lookup is mandatory, regardless of size. + if (!size || *size < 8) { + return false; + } + Log *log = GetLog(LLDBLog::Expressions); + ConstString match("^std[.]core::(Enum\\$)?Option<.+>( \\*)?$|(.+)?E2\\$"); + RegularExpression regex(match.GetStringRef()); + if (regex.Execute(GetTypeName().AsCString())) { + return false; + } + auto type_class = GetCompilerType().GetTypeClass() == lldb::eTypeClassTypedef ? + GetCompilerType().GetTypedefedType().GetTypeClass() : + GetCompilerType().GetTypeClass(); + if (type_class == lldb::eTypeClassBuiltin) { + return false; + } + + if (should_check_parent) { + if (GetParent() && GetParent()->GetAddressOf() == GetAddressOf()) { + return false; + } + } + Status error; + uint64_t type_addr = 0; + class_type_or_name.Clear(); + auto target = GetTargetSP(); + target->ReadMemory(Address(this->GetAddressOf()), &type_addr, sizeof(uint64_t), error); + if (error.Fail()) { + return false; + } + target->ReadMemory(Address(type_addr), &type_addr, sizeof(uint64_t), error); + if (error.Fail()) { + return false; + } + // max type_name length is 128. + char type_name[128]; + target->ReadMemory(Address(type_addr), &type_name, 128, error); + if (error.Fail()) { + return false; + } + + std::string dynamic_name(type_name); + auto pos = dynamic_name.find(":"); + while (pos != std::string::npos) { + dynamic_name.replace(pos, 1, "::"); + // "::" size is 2. + pos = dynamic_name.find(":", pos + 2); + } + lldb_private::TypeList types; + llvm::DenseSet searched_symbol_files; + pos = dynamic_name.rfind(".ti"); + bool is_ti_type = false; + if (!GetTypeName().GetStringRef().contains("E1$") && pos == dynamic_name.length() - std::string(".ti").length()) { + dynamic_name = dynamic_name.substr(0, pos); + if (dynamic_name != GetTypeName().AsCString()) { + is_ti_type = true; + } + } + target->GetImages().FindTypes(nullptr, ConstString(dynamic_name), true, UINT32_MAX, + searched_symbol_files, types); + TypeSP type_sp = types.GetSize() == 0 ? TypeSP() : types.GetTypeAtIndex(0); + if (type_sp != nullptr) { + class_type_or_name.SetTypeSP(type_sp); + class_type_or_name.SetName(type_sp->GetFullCompilerType().GetTypeName().AsCString()); + if (should_update) { + m_override_type = type_sp->GetFullCompilerType(); + LLDB_LOGF(log, "Found dynamic type with name %s", type_sp->GetFullCompilerType().GetTypeName().AsCString()); + } + return true; + } + if (is_ti_type) { + has_typeinfo = true; + return true; + } + LLDB_LOGF(log, "Can not found dynamic type with variable name %s", this->GetName().AsCString()); + return false; +} + +bool ValueObject::IsCangjieGenericType() { + if (GetTypeName().GetStringRef().contains("$G") || + GetTypeName().GetStringRef().contains("Interface$")) { + return true; + } + if (GetCompilerType().GetTypeClass() ==lldb::eTypeClassTypedef) { + return GetCompilerType().GetTypedefedType().GetTypeName().GetStringRef().contains("Interface$"); + } + return has_typeinfo; +} + +bool ValueObject::IsCangjieDynamicType() { + TypeAndOrName class_type_or_name; + return GetCangjieDynamicType(class_type_or_name, false); +} + +bool ValueObject::IsGenericValueType() { + if (IsCangjieGenericType()) { + GetDynamicType(); + return is_generic_value_type; + } + return false; +} diff --git a/lldb/source/Core/ValueObjectDynamicValue.cpp b/lldb/source/Core/ValueObjectDynamicValue.cpp index cc5c481cd..2e58fd571 100644 --- a/lldb/source/Core/ValueObjectDynamicValue.cpp +++ b/lldb/source/Core/ValueObjectDynamicValue.cpp @@ -4,6 +4,12 @@ // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// //===----------------------------------------------------------------------===// #include "lldb/Core/ValueObjectDynamicValue.h" @@ -382,3 +388,49 @@ void ValueObjectDynamicValue::SetLanguageFlags(uint64_t flags) { else this->ValueObject::SetLanguageFlags(flags); } + +void ValueObjectDynamicValue::UpdateDynamicType() { + ExecutionContext exe_ctx(GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + if (!m_parent || !process) { + return; + } + + bool found_dynamic_type = false; + TypeAndOrName class_type_or_name; + Address dynamic_address; + Value::ValueType value_type; + LanguageRuntime *runtime = nullptr; + lldb::LanguageType known_type = m_parent->GetObjectRuntimeLanguage(); + if (known_type != lldb::eLanguageTypeUnknown && known_type != lldb::eLanguageTypeC) { + runtime = process->GetLanguageRuntime(known_type); + if (runtime) { + found_dynamic_type = runtime->GetDynamicTypeAndAddress( + *m_parent, m_use_dynamic, class_type_or_name, dynamic_address, value_type); + } + } else { + runtime = process->GetLanguageRuntime(lldb::eLanguageTypeC_plus_plus); + if (runtime) { + found_dynamic_type = runtime->GetDynamicTypeAndAddress( + *m_parent, m_use_dynamic, class_type_or_name, dynamic_address, value_type); + } + if (!found_dynamic_type) { + runtime = process->GetLanguageRuntime(lldb::eLanguageTypeObjC); + if (runtime) { + found_dynamic_type = runtime->GetDynamicTypeAndAddress( + *m_parent, m_use_dynamic, class_type_or_name, dynamic_address, value_type); + } + } + } + if (!found_dynamic_type) { + return; + } + m_value.SetCompilerType(class_type_or_name.GetCompilerType()); + m_value.SetValueType(value_type); + Log *log = GetLog(LLDBLog::Types); + if (log) { + LLDB_LOGF(log, "[%s %p] has a new dynamic type %s", GetName().GetCString(), + static_cast(this), GetTypeName().GetCString()); + } + return; +} diff --git a/lldb/source/DataFormatters/FormatManager.cpp b/lldb/source/DataFormatters/FormatManager.cpp index a8390c5d7..a815c514b 100644 --- a/lldb/source/DataFormatters/FormatManager.cpp +++ b/lldb/source/DataFormatters/FormatManager.cpp @@ -179,6 +179,14 @@ void FormatManager::GetPossibleMatches( bool root_level) { compiler_type = compiler_type.GetTypeForFormatters(); ConstString type_name(compiler_type.GetTypeName()); + + // Skip the possible types of VArray. + ConstString match(R"((^(unsigned|signed) char ?\[[0-9]+\]$)|(^char32_t ?\[[0-9]+\]$))"); + RegularExpression regex(match.GetStringRef()); + if (regex.Execute(type_name.AsCString())) { + return; + } + if (valobj.GetBitfieldBitSize() > 0) { StreamString sstring; sstring.Printf("%s:%d", type_name.AsCString(), valobj.GetBitfieldBitSize()); @@ -574,7 +582,7 @@ FormatManager::GetCandidateLanguages(lldb::LanguageType lang_type) { case lldb::eLanguageTypeC_plus_plus_03: case lldb::eLanguageTypeC_plus_plus_11: case lldb::eLanguageTypeC_plus_plus_14: - return {lldb::eLanguageTypeC_plus_plus, lldb::eLanguageTypeObjC}; + return {lldb::eLanguageTypeC_plus_plus}; default: return {lang_type}; } diff --git a/lldb/source/DataFormatters/ValueObjectPrinter.cpp b/lldb/source/DataFormatters/ValueObjectPrinter.cpp index 6fd6308de..75e2a2bdb 100644 --- a/lldb/source/DataFormatters/ValueObjectPrinter.cpp +++ b/lldb/source/DataFormatters/ValueObjectPrinter.cpp @@ -365,6 +365,13 @@ void ValueObjectPrinter::GetValueSummaryError(std::string &value, : m_options.m_varformat_language; if (Language *lang_plugin = Language::FindPlugin(lang_type)) { summary.assign(lang_plugin->GetNilReferenceSummaryString().str()); + if (m_options.m_omit_summary_depth == 0) { + TypeSummaryImpl *entry = GetSummaryFormatter(); + if (entry) { + m_valobj->GetSummaryAsCString(entry, summary, + m_options.m_varformat_language); + } + } } else { // We treat C as the fallback language rather than as a separate Language // plugin. diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp index 6b710084f..376492c30 100644 --- a/lldb/source/Expression/IRExecutionUnit.cpp +++ b/lldb/source/Expression/IRExecutionUnit.cpp @@ -317,11 +317,11 @@ void IRExecutionUnit::GetRunnableInfo(Status &error, lldb::addr_t &func_addr, llvm::SmallVector result_path; std::string object_name_model = "jit-object-" + module->getModuleIdentifier() + "-%%%.o"; - FileSpec model_spec + FileSpec model_spec = m_out_dir.CopyByAppendingPathComponent(object_name_model); std::string model_path = model_spec.GetPath(); - std::error_code result + std::error_code result = llvm::sys::fs::createUniqueFile(model_path, fd, result_path); if (!result) { llvm::raw_fd_ostream fds(fd, true); @@ -943,13 +943,13 @@ void IRExecutionUnit::GetStaticInitializers( } } -llvm::JITSymbol +llvm::JITSymbol IRExecutionUnit::MemoryManager::findSymbol(const std::string &Name) { bool missing_weak = false; uint64_t addr = GetSymbolAddressAndPresence(Name, missing_weak); // This is a weak symbol: - if (missing_weak) - return llvm::JITSymbol(addr, + if (missing_weak) + return llvm::JITSymbol(addr, llvm::JITSymbolFlags::Exported | llvm::JITSymbolFlags::Weak); else return llvm::JITSymbol(addr, llvm::JITSymbolFlags::Exported); @@ -961,7 +961,7 @@ IRExecutionUnit::MemoryManager::getSymbolAddress(const std::string &Name) { return GetSymbolAddressAndPresence(Name, missing_weak); } -uint64_t +uint64_t IRExecutionUnit::MemoryManager::GetSymbolAddressAndPresence( const std::string &Name, bool &missing_weak) { Log *log = GetLog(LLDBLog::Expressions); @@ -1066,9 +1066,16 @@ bool IRExecutionUnit::CommitOneAllocation(lldb::ProcessSP &process_sp, break; default: const bool zero_memory = false; + bool keep = false; + auto ostype = process_sp->GetTarget().GetArchitecture().GetTriple().getOS(); + if (ostype == llvm::Triple::Win32) { + keep = (record.m_name == ".rdata"); + } else { + keep = (record.m_name == ".rodata"); + } record.m_process_address = Malloc(record.m_size, record.m_alignment, record.m_permissions, - eAllocationPolicyProcessOnly, zero_memory, error); + eAllocationPolicyProcessOnly, zero_memory, error, keep); break; } diff --git a/lldb/source/Expression/IRMemoryMap.cpp b/lldb/source/Expression/IRMemoryMap.cpp index 6b8f7babc..cb46acd4f 100644 --- a/lldb/source/Expression/IRMemoryMap.cpp +++ b/lldb/source/Expression/IRMemoryMap.cpp @@ -270,10 +270,10 @@ ExecutionContextScope *IRMemoryMap::GetBestExecutionContextScope() const { IRMemoryMap::Allocation::Allocation(lldb::addr_t process_alloc, lldb::addr_t process_start, size_t size, uint32_t permissions, uint8_t alignment, - AllocationPolicy policy) + AllocationPolicy policy, bool keep) : m_process_alloc(process_alloc), m_process_start(process_start), m_size(size), m_policy(policy), m_leak(false), m_permissions(permissions), - m_alignment(alignment) { + m_alignment(alignment), m_keep(keep) { switch (policy) { default: llvm_unreachable("Invalid AllocationPolicy"); @@ -288,7 +288,7 @@ IRMemoryMap::Allocation::Allocation(lldb::addr_t process_alloc, lldb::addr_t IRMemoryMap::Malloc(size_t size, uint8_t alignment, uint32_t permissions, AllocationPolicy policy, - bool zero_memory, Status &error) { + bool zero_memory, Status &error, bool keep) { lldb_private::Log *log(GetLog(LLDBLog::Expressions)); error.Clear(); @@ -391,7 +391,7 @@ lldb::addr_t IRMemoryMap::Malloc(size_t size, uint8_t alignment, m_allocations.emplace( std::piecewise_construct, std::forward_as_tuple(aligned_address), std::forward_as_tuple(allocation_address, aligned_address, - allocation_size, permissions, alignment, policy)); + allocation_size, permissions, alignment, policy, keep)); if (zero_memory) { Status write_error; @@ -455,6 +455,23 @@ void IRMemoryMap::Free(lldb::addr_t process_address, Status &error) { } Allocation &allocation = iter->second; + if (allocation.m_keep) { + // When a new value is assigned to an object of the string type during expression calculation, + // the new value is stored in the .rodata section. To ensure that the object can be accessed normally, + // the .rodata section (marked as keep) needs to be retained and not freed. + // Note that this is only a workaround until a better solution is found, + // which will increase the memory footprint of the debugger as the expr command is used. + if (lldb_private::Log *log = GetLog(LLDBLog::Expressions)) { + LLDB_LOGF(log, + "IRMemoryMap::Free (0x%" PRIx64 ") keeped [0x%" PRIx64 + "..0x%" PRIx64 ")", + (uint64_t)process_address, iter->second.m_process_start, + iter->second.m_process_start + iter->second.m_size); + } + m_allocations.erase(iter); + + return; + } switch (allocation.m_policy) { default: diff --git a/lldb/source/Expression/Materializer.cpp b/lldb/source/Expression/Materializer.cpp index 946ae10d6..0456d2380 100644 --- a/lldb/source/Expression/Materializer.cpp +++ b/lldb/source/Expression/Materializer.cpp @@ -500,6 +500,10 @@ public: const bool scalar_is_load_address = false; lldb::addr_t addr_of_valobj = valobj_sp->GetAddressOf(scalar_is_load_address, &address_type); + if (valobj_sp->IsGenericValueType()) { + // Offset 8 is required if valobj is a generic value type. + addr_of_valobj += 8; + } if (addr_of_valobj != LLDB_INVALID_ADDRESS) { Status write_error; map.WritePointerToMemory(load_addr, addr_of_valobj, write_error); diff --git a/lldb/source/Expression/UserExpression.cpp b/lldb/source/Expression/UserExpression.cpp index 186e414e6..2e49bd5d9 100644 --- a/lldb/source/Expression/UserExpression.cpp +++ b/lldb/source/Expression/UserExpression.cpp @@ -406,6 +406,17 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx, exe_ctx.GetBestExecutionContextScope(), error); } + if (result_valobj_sp->IsPointerType()) { + Status err; + auto name = result_valobj_sp->GetName(); + bool is_option_enum = result_valobj_sp->GetTypeName().GetStringRef().contains("E2$"); + result_valobj_sp = result_valobj_sp->Dereference(err); + if (err.Fail()) { + return lldb::eExpressionSetupError; + } + result_valobj_sp->SetIsOptionlikeReference(is_option_enum); + result_valobj_sp->SetName(name); + } return execution_results; } diff --git a/lldb/source/Host/common/TCPSocket.cpp b/lldb/source/Host/common/TCPSocket.cpp index eabc9fef8..c1d730802 100644 --- a/lldb/source/Host/common/TCPSocket.cpp +++ b/lldb/source/Host/common/TCPSocket.cpp @@ -160,6 +160,8 @@ Status TCPSocket::Connect(llvm::StringRef name) { std::vector addresses = SocketAddress::GetAddressInfo(host_port->hostname.c_str(), nullptr, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP); + llvm::partition(addresses, + [](auto &sa) { return sa.GetFamily() == AF_INET; }); for (SocketAddress &address : addresses) { error = CreateSocket(address.GetFamily()); if (error.Fail()) diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index 6ef209b20..90a39ef20 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -41,6 +41,7 @@ #include "Commands/CommandObjectType.h" #include "Commands/CommandObjectVersion.h" #include "Commands/CommandObjectWatchpoint.h" +#include "Commands/CommandObjectCJThread.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/PluginManager.h" @@ -394,12 +395,31 @@ void CommandInterpreter::Initialize() { if (cmd_obj_sp) AddAlias("image", cmd_obj_sp); + cmd_obj_sp = GetCommandSPExact("frame variable"); + if (cmd_obj_sp) { + if (auto *locals = AddAlias("locals", cmd_obj_sp, "--")) { + locals->SetHelp("Show local variables of Cangjie for the current stack frame." + "Defaults to all arguments and local variables in scope."); + locals->SetHelpLong(""); + } + } + + cmd_obj_sp = GetCommandSPExact("target variable"); + if (cmd_obj_sp) { + if (auto *globals = AddAlias("globals", cmd_obj_sp, "--")) { + globals->SetHelp("Show global variables of Cangjie for the current target," + "before or while running a process."); + globals->SetHelpLong(""); + } + } + alias_arguments_vector_sp = std::make_shared(); cmd_obj_sp = GetCommandSPExact("expression"); if (cmd_obj_sp) { - AddAlias("p", cmd_obj_sp, "--")->SetHelpLong(""); - AddAlias("print", cmd_obj_sp, "--")->SetHelpLong(""); + AddAlias("set", cmd_obj_sp, "-- [cj]")->SetHelpLong(""); + AddAlias("p", cmd_obj_sp, "-- [cj]")->SetHelpLong(""); + AddAlias("print", cmd_obj_sp, "-- [cj]")->SetHelpLong(""); AddAlias("call", cmd_obj_sp, "--")->SetHelpLong(""); if (auto *po = AddAlias("po", cmd_obj_sp, "-O --")) { po->SetHelp("Evaluate an expression on the current thread. Displays any " @@ -540,6 +560,7 @@ void CommandInterpreter::LoadCommandDictionary() { REGISTER_COMMAND_OBJECT("version", CommandObjectVersion); REGISTER_COMMAND_OBJECT("watchpoint", CommandObjectMultiwordWatchpoint); REGISTER_COMMAND_OBJECT("language", CommandObjectLanguage); + REGISTER_COMMAND_OBJECT("cjthread", CommandObjectMultiwordCJThread); // clang-format off const char *break_regexes[][2] = { diff --git a/lldb/source/Interpreter/OptionGroupVariable.cpp b/lldb/source/Interpreter/OptionGroupVariable.cpp index 20a521b8e..cd0a4533a 100644 --- a/lldb/source/Interpreter/OptionGroupVariable.cpp +++ b/lldb/source/Interpreter/OptionGroupVariable.cpp @@ -48,6 +48,9 @@ static constexpr OptionDefinition g_variable_options[] = { {LLDB_OPT_SET_2, false, "summary-string", 'z', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeName, "Specify a summary string to use to format the variable output."}, + {LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "no-values", 'v', + OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, + "Omit variable values."}, }; static Status ValidateNamedSummary(const char *str, void *) { @@ -106,6 +109,9 @@ OptionGroupVariable::SetOptionValue(uint32_t option_idx, case 'z': error = summary_string.SetCurrentValue(option_arg); break; + case 'v': + show_values = false; + break; default: llvm_unreachable("Unimplemented option"); } @@ -122,6 +128,7 @@ void OptionGroupVariable::OptionParsingStarting( show_decl = false; use_regex = false; show_scope = false; + show_values = true; summary.Clear(); summary_string.Clear(); } diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp index 8a708c1f9..70910fd70 100644 --- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -24,6 +24,7 @@ #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/ProcessInfo.h" +#include "llvm/Support/ThreadPool.h" #include @@ -190,6 +191,10 @@ void DynamicLoaderPOSIXDYLD::DidLaunch() { } LoadVDSO(); + ModuleSP interpreter = m_interpreter_module.lock(); + if (interpreter) { + module_list.Append(interpreter); + } m_process->GetTarget().ModulesDidLoad(module_list); } } @@ -638,19 +643,40 @@ void DynamicLoaderPOSIXDYLD::LoadAllCurrentModules() { m_process->PrefetchModuleSpecs( module_names, m_process->GetTarget().GetArchitecture().GetTriple()); - for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) { - ModuleSP module_sp = - LoadModuleAtAddress(I->file_spec, I->link_addr, I->base_addr, true); - if (module_sp.get()) { - LLDB_LOG(log, "LoadAllCurrentModules loading module: {0}", - I->file_spec.GetFilename()); - module_list.Append(module_sp); - } else { - Log *log = GetLog(LLDBLog::DynamicLoader); - LLDB_LOGF( - log, - "DynamicLoaderPOSIXDYLD::%s failed loading module %s at 0x%" PRIx64, - __FUNCTION__, I->file_spec.GetCString(), I->base_addr); + if (m_process->GetTarget().GetArchitecture().GetTriple().str().find("ohos") != std::string::npos) { + llvm::ThreadPool pool(llvm::hardware_concurrency(DynamicLoaderPOSIXDYLD::DYLD_CONCURRENCY_THREADING)); + for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) { + constexpr const auto &func_name = __FUNCTION__; + pool.async([&](const FileSpec &file_spec, addr_t link_addr, addr_t base_addr) { + ModuleSP module_sp = + LoadModuleAtAddress(file_spec, link_addr, base_addr, true); + if (module_sp.get()) { + LLDB_LOG(log, "LoadAllCurrentModules loading module: {0}", file_spec.GetFilename()); + module_list.Append(module_sp); + } else { + LLDB_LOGF( + log, + "DynamicLoaderPOSIXDYLD::%s failed loading module %s at 0x%" PRIx64, + func_name, file_spec.GetCString(), base_addr); + } + }, I->file_spec, I->link_addr, I->base_addr); + } + pool.wait(); + } else { + for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) { + ModuleSP module_sp = + LoadModuleAtAddress(I->file_spec, I->link_addr, I->base_addr, true); + if (module_sp.get()) { + LLDB_LOG(log, "LoadAllCurrentModules loading module: {0}", + I->file_spec.GetFilename()); + module_list.Append(module_sp); + } else { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF( + log, + "DynamicLoaderPOSIXDYLD::%s failed loading module %s at 0x%" PRIx64, + __FUNCTION__, I->file_spec.GetCString(), I->base_addr); + } } } diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h index 8d3e4cde5..0f65c217c 100644 --- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h @@ -91,6 +91,9 @@ protected: std::map> m_loaded_modules; + /// number of woker in ThreadPool, default 3 + static constexpr int DYLD_CONCURRENCY_THREADING {3}; + /// If possible sets a breakpoint on a function called by the runtime /// linker each time a module is loaded or unloaded. bool SetRendezvousBreakpoint(); diff --git a/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp b/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp index fa6356963..023aff5b5 100644 --- a/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp @@ -174,7 +174,7 @@ ThreadPlanSP DynamicLoaderWindowsDYLD::GetStepThroughTrampolinePlan(Thread &thread, bool stop) { auto arch = m_process->GetTarget().GetArchitecture(); - if (arch.GetMachine() != llvm::Triple::x86) { + if (arch.GetMachine() != llvm::Triple::x86 && arch.GetMachine() != llvm::Triple::x86_64) { return ThreadPlanSP(); } @@ -204,7 +204,9 @@ DynamicLoaderWindowsDYLD::GetStepThroughTrampolinePlan(Thread &thread, ExecutionContext exe_ctx(m_process->GetTarget()); if (first_insn == nullptr || second_insn == nullptr || - strcmp(first_insn->GetMnemonic(&exe_ctx), "jmpl") != 0 || + (strcmp(first_insn->GetMnemonic(&exe_ctx), "jmpl") != 0 && + strcmp(first_insn->GetMnemonic(&exe_ctx), "jmpq") != 0 && + strcmp(first_insn->GetMnemonic(&exe_ctx), "jmp") != 0) || strcmp(second_insn->GetMnemonic(&exe_ctx), "nop") != 0) { return ThreadPlanSP(); } diff --git a/lldb/source/Plugins/ExpressionParser/CMakeLists.txt b/lldb/source/Plugins/ExpressionParser/CMakeLists.txt index 17c40aee4..ed3c21b97 100644 --- a/lldb/source/Plugins/ExpressionParser/CMakeLists.txt +++ b/lldb/source/Plugins/ExpressionParser/CMakeLists.txt @@ -1 +1,2 @@ +add_subdirectory(Cangjie) add_subdirectory(Clang) diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CMakeLists.txt b/lldb/source/Plugins/ExpressionParser/Cangjie/CMakeLists.txt new file mode 100644 index 000000000..b848ed20d --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CMakeLists.txt @@ -0,0 +1,83 @@ +if(NOT DEFINED CANGJIE_ROOT) + set(CANGJIE_ROOT ${CMAKE_SOURCE_DIR}/../../cangjie) +endif() +if(NOT DEFINED CANGJIE_FRONTEND_LIB) + set(CANGJIE_FRONTEND_LIB ${CMAKE_SOURCE_DIR}/../../cangjie/build/build/lib/libcangjie-frontend${CMAKE_SHARED_LIBRARY_SUFFIX}) +endif() +if(NOT DEFINED CANGJIE_FRONTEND_IMPLIB) + set(CANGJIE_FRONTEND_IMPLIB ${CMAKE_SOURCE_DIR}/../../cangjie/build/build/lib/libcangjie-frontend.dll.a) +endif() +if(NOT DEFINED CANGJIE_LSP_LIB) + set(CANGJIE_LSP_LIB ${CMAKE_SOURCE_DIR}/../../cangjie/build/build/lib/libcangjie-lsp${CMAKE_SHARED_LIBRARY_SUFFIX}) +endif() +if(NOT DEFINED CANGJIE_LSP_IMPLIB) + set(CANGJIE_LSP_IMPLIB ${CMAKE_SOURCE_DIR}/../../cangjie/build/build/lib/libcangjie-lsp.dll.a) +endif() +include_directories(${CANGJIE_ROOT}/include) +include_directories(${CANGJIE_ROOT}/third_party/flatbuffers/include) +include_directories(${CANGJIE_ROOT}/build/build/schema) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../../../utils/demangle) +set_property(SOURCE + CangjieUserExpression.cpp + CangjieCompilerInstance.cpp + CangjieDeclMap.cpp + CangjieASTUtils.cpp + CangjieExpressionSourceCode.cpp + CangjieExpressionParser.cpp + CangjieIRForTarget.cpp + APPEND PROPERTY COMPILE_FLAGS "-fexceptions -DCANGJIE_CODEGEN_CJNATIVE_BACKEND") + +if(WIN32 AND uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG") +add_lldb_library(lldbPluginExpressionParserCangjie + CangjieUserExpression.cpp + CangjieCompilerInstance.cpp + CangjieDeclMap.cpp + CangjieASTUtils.cpp + CangjieExpressionSourceCode.cpp + CangjieExpressionParser.cpp + CangjieIRForTarget.cpp + LINK_LIBS + lldbExpression + cangjie-frontend + boundscheck +) +else() +add_lldb_library(lldbPluginExpressionParserCangjie + CangjieUserExpression.cpp + CangjieCompilerInstance.cpp + CangjieDeclMap.cpp + CangjieASTUtils.cpp + CangjieExpressionSourceCode.cpp + CangjieExpressionParser.cpp + CangjieIRForTarget.cpp + LINK_LIBS + lldbExpression + cangjie-frontend + cangjie-lsp + boundscheck +) +endif() + +string(REPLACE "AMD64" "x86_64" ARCH_NAME "${CMAKE_SYSTEM_PROCESSOR}") +target_link_directories(lldbPluginExpressionParserCangjie PUBLIC ${CMAKE_SOURCE_DIR}/../utils/boundscheck/lib/${ARCH_NAME}) +add_compile_definitions(CANGJIE_CODEGEN_CJNATIVE_BACKEND) +add_compile_definitions(CANGJIE_AST2CHIR) + +if(WIN32 AND uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG") + add_library(cangjie-frontend STATIC IMPORTED) + set_target_properties(cangjie-frontend PROPERTIES + IMPORTED_LOCATION ${CANGJIE_FRONTEND_LIB} + ) +else() + add_library(cangjie-frontend SHARED IMPORTED) + add_library(cangjie-lsp SHARED IMPORTED) + set_target_properties(cangjie-frontend PROPERTIES + IMPORTED_LOCATION ${CANGJIE_FRONTEND_LIB} + IMPORTED_IMPLIB ${CANGJIE_FRONTEND_IMPLIB} + ) + set_target_properties(cangjie-lsp PROPERTIES + IMPORTED_LOCATION ${CANGJIE_LSP_LIB} + IMPORTED_IMPLIB ${CANGJIE_LSP_IMPLIB} + ) +endif() diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieASTUtils.cpp b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieASTUtils.cpp new file mode 100644 index 000000000..e7e90efba --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieASTUtils.cpp @@ -0,0 +1,665 @@ +//===-- CangjieASTUtils.cpp -----------------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#include "CangjieASTUtils.h" +#include "cangjie/AST/Create.h" +#include "cangjie/Utils/CastingTemplate.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Core/ValueObject.h" + +using namespace lldb_private; +using namespace Cangjie; +using namespace Cangjie::AST; + +bool lldb_private::StartsWith(const std::string& str, const std::string& pre) +{ + return str.find(pre) == 0; +} +bool EndsWith(const std::string& str, const std::string& suf) { + if (str.length() < suf.length()) { + return false; + } + return str.rfind(suf) == str.length() - suf.length(); +} + +std::string lldb_private::DeletePrefixOfType(const std::string& typeName) { + auto name = typeName; + if (IsEnumName(name)) { + auto enumPrefix = GetEnumPrefix(name); + auto ltPos = name.find(enumPrefix); + return name.substr(0, ltPos) + name.substr(ltPos+ enumPrefix.size(), name.size()); + } + // Delete interface$ + auto ltPosI = name.find(INTERFACE_PREFIX_NAME); + if (ltPosI != std::string::npos) { + return name.substr(0, ltPosI) + name.substr(ltPosI+ INTERFACE_PREFIX_NAME.size(), name.size()); + } + return name; +} + +std::string lldb_private::GetEnumPrefix(const std::string& name) { + if (name.find(ENUM_PREFIX_NAME) != std::string::npos) { + return ENUM_PREFIX_NAME; + } else if (name.find(E0_PREFIX_NAME) != std::string::npos) { + return E0_PREFIX_NAME; + } else if (name.find(E2_PREFIX_NAME_OPTION_LIKE) != std::string::npos) { + return E2_PREFIX_NAME_OPTION_LIKE; + } else if (name.find(E3_PREFIX_NAME) != std::string::npos) { + return E3_PREFIX_NAME; + } +} + +std::string lldb_private::GetEnumNameWithoutPrefix(std::string& name, std::string& pkgname) { + auto enumPrefix = GetEnumPrefix(name); + // For example, enum RGBColor in package pkg's name is pkg::E0$RGBColor. + auto e0Pos = name.find(E0_PREFIX_NAME); + if (e0Pos != std::string::npos) { + if (e0Pos == 0) { + return pkgname + PACKAGE_SUFFIX + name.substr(e0Pos+ E0_PREFIX_NAME.size(), name.size()); + } + return name.substr(0, e0Pos) + name.substr(e0Pos + E0_PREFIX_NAME.size(), name.size()); + } + // For example, if the enum RGBColor's name is pkg::Enum$RGBColor, then subname is Enum$RGBColor. + auto ltPos = name.find(enumPrefix); + auto subname = GetSubNameWithoutPkgname(name, pkgname); + if (subname.empty()) { + subname = name.substr(0, ltPos) + name.substr(ltPos+ enumPrefix.size(), name.size()); + return subname; + } else { + subname = pkgname + PACKAGE_SUFFIX + name.substr(ltPos+ enumPrefix.size(), name.size()); + return subname; + } +} + +std::string lldb_private::GetTypeNameWithoutPrefix(ConstString type_name, std::string& pkgname) +{ + std::string name = type_name.GetCString(); + name = DeletePrefixOfType(name); + auto subname = GetSubNameWithoutPkgname(name, pkgname); + // For example, default::A 's type name is A. + auto pos = subname.rfind("<"); + if (pos != std::string::npos) { + subname = subname.substr(0, pos); + } + return subname.empty() ? name : subname; +} + +std::string lldb_private::DeleteAllPkgname(const std::string& name, std::string& pkg) { + // Multiple package names may exist. for example, default::Test. + std::string prefix = pkg + PACKAGE_SUFFIX; + std::string typeName = name ; + typeName = GetSubNameWithoutPkgname(typeName, pkg); + if (typeName.empty()) { + typeName = name; + } + auto pos = typeName.find(prefix); + while (pos != std::string::npos) { + typeName = typeName.substr(0, pos) + typeName.substr(pos + prefix.size(), typeName.size()); + pos = typeName.find(prefix); + } + return typeName; +} + +std::string lldb_private::GetSubNameWithoutPkgname(std::string& name, std::string& pkg) +{ + if (pkg.empty()) { + return ""; + } + name.erase(std::remove(name.begin(), name.end(), ' '), name.end()); + // For example, a pkg name can be "pkg::HashMap". + auto prefix = pkg + PACKAGE_SUFFIX; + if (StartsWith(name, prefix)) { + return name.substr(prefix.size(), name.size() - prefix.size()); + } + return ""; +} + +std::string lldb_private::GetPkgNameFromCompilerUnit(const CompileUnit& compUnit) +{ + std::string diFileName = compUnit.GetPrimaryFile().GetFilename().GetCString(); + return diFileName.substr(diFileName.find('-') + 1); +} + +bool CangjieASTBuiler::IsFunctionType(ConstString type_name) +{ + ConstString compare("(.+::|^)\\(.*\\)( ?)->.+$"); + RegularExpression regex(compare.GetStringRef()); + return regex.Execute(type_name.AsCString()); +} + +static std::map base_type_map = { + { "Unit", Cangjie::AST::TypeKind::TYPE_UNIT }, + { "Bool", Cangjie::AST::TypeKind::TYPE_BOOLEAN }, + { "Rune", Cangjie::AST::TypeKind::TYPE_RUNE }, + { "Int8", Cangjie::AST::TypeKind::TYPE_INT8}, + { "UInt8", Cangjie::AST::TypeKind::TYPE_UINT8 }, + { "Int16", Cangjie::AST::TypeKind::TYPE_INT16 }, + { "UInt16", Cangjie::AST::TypeKind::TYPE_UINT16 }, + { "Int32", Cangjie::AST::TypeKind::TYPE_INT32 }, + { "UInt32", Cangjie::AST::TypeKind::TYPE_UINT32 }, + { "Int64", Cangjie::AST::TypeKind::TYPE_INT64 }, + { "UInt64", Cangjie::AST::TypeKind::TYPE_UINT64 }, + { "IntNative", Cangjie::AST::TypeKind::TYPE_INT_NATIVE}, + { "UIntNative", Cangjie::AST::TypeKind::TYPE_UINT_NATIVE}, + { "Float16", Cangjie::AST::TypeKind::TYPE_FLOAT16 }, + { "Float32", Cangjie::AST::TypeKind::TYPE_FLOAT32 }, + { "Float64", Cangjie::AST::TypeKind::TYPE_FLOAT64 }, +}; + +OwnedPtr CangjieASTBuiler::CreateFuncType(std::string name, Ptr target, std::string pkg) +{ + // For example, a func name can be "(Object, Int8, Int8) -> Int8". + OwnedPtr ret = MakeOwned(); + name.erase(std::remove(name.begin(), name.end(), ' '), name.end()); + auto arrowPos = name.find("->"); + if (arrowPos == std::string::npos) { + return ret; + } + auto paramsStr = name.substr(1, arrowPos - 2); + std::stringstream ss(paramsStr); + std::string param; + while (std::getline(ss, param, ',')) { + if (param.empty()) { + break; + } + ret->paramTypes.emplace_back(CreateAstType(param, target, pkg)); + } + auto retTypeStr = name.substr(arrowPos + 2); + ret->retType = CreateAstType(retTypeStr, target, pkg); + return ret; +} + +bool CheckTypeCorrect(std::string& typeStr) +{ + // Checks if < and > match in a string. + auto ltNum = std::count(typeStr.begin(), typeStr.end(), '<'); + auto gtNum = std::count(typeStr.begin(), typeStr.end(), '>'); + if (ltNum == gtNum) { + return true; + } + return false; +} + +OwnedPtr CangjieASTBuiler::CreateRefType(std::string name, Ptr target, std::string pkg, + bool isGenericDeclFromTarget) +{ + // For example, a reference type name can be "Range" or "Array" or "HashMap". + OwnedPtr ret = MakeOwned(); + ret->ref.target = target; + name.erase(std::remove(name.begin(), name.end(), ' '), name.end()); + if (EndsWith(name, "*")) { + name = name.substr(0, name.size() - 1); + } + // For example, enum RGBColor{Red} 's type name is pkg.Enum$RGBColor and pkg.RGBColor is need. + if (IsEnumName(name)) { + name = GetEnumNameWithoutPrefix(name, pkg); + } + // When the reference type is a generic interface, for example, if the type name is `default::Interface$B<$G_T>`, + // then the reference type name is `B`. + if (name.find(INTERFACE_PREFIX_NAME) != std::string::npos) { + name = DeletePrefixOfType(name); + } + if (IsGenericType(name)) { + if (target && target->identifier.Val() != GetGenericDeclName(name)) { + Ptr genericTarget = GetTargetOfGenericDecl(target, name); + if (genericTarget) { + ret->ref.target = genericTarget; + } + } + } + auto ltPos = name.find("<"); + // When a reference type has generic parameters, the parameters may have package names. For example, C. + if (auto lcolon = name.find(PACKAGE_SUFFIX); + lcolon == std::string::npos || (ltPos != std::string::npos && ltPos < lcolon)) { + name = pkg + PACKAGE_SUFFIX + name; + ltPos = name.find("<"); + } + if (ltPos == std::string::npos || name.back() != '>') { + if (IsGenericType(name)) { + name = GetGenericParamDeclName(name); + } + ret->ref.identifier = name; + m_type_names.insert(ret->ref.identifier); + return ret; + } + ret->ref.identifier = name.substr(0, ltPos); + m_type_names.insert(ret->ref.identifier); + if (!isGenericDeclFromTarget) { + return ret; + } + auto idLen = ret->ref.identifier.Val().size(); + // typeArg: String,Array>>,String + auto args = name.substr(idLen + 1, name.size() - idLen - 2); + auto elements = SplitTypeName(args); + for (auto& ele:elements) { + ret->typeArguments.emplace_back(CreateAstType(ele, target, pkg)); + } + return ret; +} + +bool CangjieASTBuiler::IsRefType(const Ptr& ty) { + CJC_NULLPTR_CHECK(ty); + if (ty->IsClassLike()) { + return true; + } + return false; +} + +std::vector CangjieASTBuiler::SplitCollectionName(std::string name) { + // For example, a name can be "String" or "Range" or "Array" or "HashMap". + std::vector ret; + name.erase(std::remove(name.begin(), name.end(), ' '), name.end()); + if (EndsWith(name, "*")) { + name = name.substr(0, name.size() - 1); + } + auto ltPos = name.find("<"); + if (ltPos == std::string::npos || name.back() != '>') { + return ret; + } + auto idLen = name.substr(0, ltPos).size(); + // typeArg: String,Array>>,String + auto args = name.substr(idLen + 1, name.size() - idLen - 2); + return SplitTypeName(args); +} + +std::vector CangjieASTBuiler::SplitTypeName(std::string name) { + std::vector ret; + if (name.find(",") == std::string::npos) { + ret.emplace_back(name); + return ret; + } + std::stringstream ss(name); + std::string typeStr; + std::string arg; + while (std::getline(ss, arg, ',')) { + if (arg.empty()) { + break; + } + typeStr += arg; + if (!CheckTypeCorrect(typeStr)) { + typeStr += ","; + continue; + } + ret.emplace_back(typeStr); + typeStr = ""; + } + return ret; +} + +std::vector CangjieASTBuiler::SplitTupleName(std::string name) { + // For example, a tuple name can be "Tuple>". + std::vector ret; + auto tlen = TUPLE_NAME.size(); + name.erase(std::remove(name.begin(), name.end(), ' '), name.end()); + auto args = name.substr(tlen + 1, name.size() - tlen - 2); + return SplitTypeName(args); +} + +OwnedPtr CangjieASTBuiler::CreateTupleType(std::string name, Ptr target) +{ + // For example, a tuple name can be "Tuple>". + OwnedPtr ret = MakeOwned(); + std::vector elements = SplitTupleName(name); + for (auto& ele:elements) { + ret->fieldTypes.emplace_back(CreateAstType(ele, target)); + } + return ret; +} + +OwnedPtr CangjieASTBuiler::CreateVArrayType(std::string name, Ptr target) +{ + // For example, a VArray name can be "VArray". + OwnedPtr ret = MakeOwned(); + auto valen = VARRAY_NAME.size(); + name.erase(std::remove(name.begin(), name.end(), ' '), name.end()); + auto vargs = name.substr(valen + 1, name.size() - valen - 2); + std::stringstream ss(vargs); + std::string typeStr; + std::string varg; + while (std::getline(ss, varg, ',')) { + if (varg.empty()) { + break; + } + typeStr += varg; + if (!CheckTypeCorrect(typeStr)) { + typeStr += ","; + continue; + } + if (StartsWith(typeStr, "$")) { + OwnedPtr lit = MakeOwned(); + lit->kind = LitConstKind::INTEGER; + lit->stringValue = typeStr.substr(1, typeStr.size() - 1); + OwnedPtr constType = MakeOwned(); + constType->constantExpr = std::move(lit); + ret->constantType = std::move(constType); + } else { + ret->typeArgument = CreateAstType(typeStr, target); + } + typeStr = ""; + } + return ret; +} + +OwnedPtr CangjieASTBuiler::CreateAstType(std::string name, Ptr target, std::string pkg, + bool isGenericDeclFromTarget) +{ + auto subname = GetSubNameWithoutPkgname(name, pkg); + if (!subname.empty()) { + return CreateAstType(subname, target, pkg, isGenericDeclFromTarget); + } + if (IsFunctionType(ConstString(name))) { + return CreateFuncType(name, target, pkg); + } + if (StartsWith(name, TUPLE_NAME)) { + return CreateTupleType(name, target); + } + if (StartsWith(name, VARRAY_NAME)) { + return CreateVArrayType(name, target); + } + if (base_type_map.find(name) != base_type_map.end()) { + OwnedPtr primType = MakeOwned(); + primType->kind = base_type_map[name]; + primType->str = name; + return primType; + } + return CreateRefType(name, target, pkg, isGenericDeclFromTarget); +} + +void CangjieASTBuiler::CreateFuncDeclGeneric(Ptr decl, CompilerType& type, std::string& name, + Ptr pdecl) { + if (!decl || !decl->funcBody || name.empty() || decl->identifier == name) { + return; + } + auto typeName = GetSubNameWithoutPkgname(name, decl->fullPackageName); + if (typeName.empty()) { + typeName = name; + } + auto pkgPos = typeName.rfind(PACKAGE_SUFFIX); + if (pkgPos!= std::string::npos) { + typeName = typeName.substr(pkgPos + 2, typeName.size() - 1); + } + if (!IsGenericTypeParameters(typeName)) { + return; + } + // If funcDecl's generic from outerDecl, set target. otherwise, create it. + Log *log = GetLog(LLDBLog::Expressions); + LLDB_LOGF(log, "create func [%s] 's generic type %s\n", ConstString(decl->identifier.Val()).AsCString(), + ConstString(name).AsCString()); + // for member func, like default::A::init => A::f => f + // for global func, like default::fun1 + decl->EnableAttr(Attribute::GENERIC); + decl->funcBody->generic = MakeOwned(); + // default::Pair<$G_T,$G_U> => Pair<$G_T,$G_U> => T, U + std::vector params; + SetGenericByName(decl->funcBody->generic.get(), typeName, decl); +} + +void CangjieASTBuiler::SetGenericByName(Ptr generic, std::string& typeName, const Ptr& decl) { + auto subname = typeName; + auto lpos = subname.find("<"); + auto rpos = subname.find(">"); + auto paramsName = subname.substr(lpos + 1, rpos - lpos - 1); + auto elements = SplitTypeName(paramsName); + for (auto& ele:elements) { + OwnedPtr gpd = MakeOwned(); + gpd->identifier = {GetGenericParamDeclName(ele), decl->begin, decl->end}; + gpd->fullPackageName = decl->fullPackageName; + gpd->begin = decl->begin; + gpd->end = decl->begin; + generic->typeParameters.emplace_back(std::move(gpd)); + } +} + +void CangjieASTBuiler::CreateDeclGeneric(Ptr decl, std::string& typeName) { + if (!IsGenericTypeParameters(typeName)) { + return; + } + decl->EnableAttr(Attribute::GENERIC); + decl->generic = MakeOwned(); + // default::Pair<$G_T,$G_U> => Pair<$G_T,$G_U> => T, U + std::vector params; + auto subname = GetSubNameWithoutPkgname(typeName, decl->fullPackageName); + SetGenericByName(decl->generic.get(), subname, decl); +} + +OwnedPtr CangjieASTBuiler::CreateEnumVarDecl( + CompilerType type, std::string name, std::string prefix, Ptr target) { + auto decl = MakeOwned(); + decl->identifier = name; + std::string typeStr = type.GetTypeName().GetCString(); + decl->type = nullptr; + decl->EnableAttr(Attribute::PUBLIC, Attribute::EXTERNAL); + decl->isVar = true; + if (decl->type) { + decl->ty = decl->type->ty; + } + return decl; +} + +OwnedPtr CangjieASTBuiler::CreateVarDecl( + const CompilerType type, std::string name, std::string prefix, Ptr target) { + auto decl = MakeOwned(); + decl->identifier = name; + std::string typeStr = type.GetTypeName().GetCString(); + decl->type = CreateAstType(typeStr, target, prefix); + decl->EnableAttr(Attribute::PUBLIC, Attribute::GLOBAL); + decl->isVar = true; + if (decl->type) { + decl->ty = decl->type->ty; + } + return decl; +} + +OwnedPtr CangjieASTBuiler::CreateEnumFuncDecl( + const CompilerType& enumType, const CompilerType& ctorFuncType, std::string funcName, Ptr pdecl) { + auto decl = MakeOwned(); + decl->identifier = funcName; + decl->funcBody = MakeOwned(); + // For example, a generic type's name can be default::Enum$RGBColor<$G_T>. + std::string typeStr = enumType.GetTypeName().GetCString(); + decl->funcBody->retType = CreateAstType(typeStr, pdecl, m_current_pkgname); + auto paramList = MakeOwned(); + + if (typeStr.find(E2_PREFIX_NAME_OPTION_LIKE) != std::string::npos) { + bool isMember = (pdecl != nullptr); + std::string paramTypeStr = ctorFuncType.GetTypeName().GetCString(); + if (IsGenericTypeParameters(typeStr)) { + paramTypeStr = GENERIC_TYPE_PREFIX_NAME + GetGenericParamDeclName(typeStr); + } + OwnedPtr param = MakeOwned(); + param->identifier = "a" + std::to_string(1); + auto tempDecl = isMember ? pdecl: decl.get(); + param->type = CreateAstType(paramTypeStr, tempDecl, m_current_pkgname); + paramList->params.emplace_back(std::move(param)); + } else { + for (uint32_t i = 1; i < ctorFuncType.GetNumFields(); i++) { + bool isMember = (pdecl != nullptr); + std::string member_name2; + auto field = ctorFuncType.GetFieldAtIndex(i, member_name2, nullptr, nullptr, nullptr); + std::string paramTypeStr = field.GetTypeName().GetCString(); + OwnedPtr param = MakeOwned(); + param->identifier = "a" + std::to_string(i); + auto tempDecl = isMember ? pdecl: decl.get(); + param->type = CreateAstType(paramTypeStr, tempDecl, m_current_pkgname); + paramList->params.emplace_back(std::move(param)); + } + } + decl->funcBody->paramLists.emplace_back(std::move(paramList)); + decl->EnableAttr(Attribute::PUBLIC, Attribute::EXTERNAL); + return decl; +} + +Ptr CangjieASTBuiler::GetTargetOfGenericDecl(Ptr pdecl, std::string& retTypeStr) { + if (!IsGenericType(retTypeStr) || !pdecl) { + return nullptr; + } + Ptr generic = pdecl->GetGeneric(); + if (!generic) { + return nullptr; + } + Ptr reftarget = nullptr; + // Set ref target of refType. + for (auto& tmpGenericParamDecl:generic->typeParameters) { + if (tmpGenericParamDecl->identifier.Val() == GetGenericParamDeclName(retTypeStr)) { + reftarget = tmpGenericParamDecl.get(); + break; + } + } + return reftarget; +} + +bool lldb_private::IsGenericFromFuncDecl(std::vector& instantiatedNames, std::string name) { + for (auto& cur:instantiatedNames) { + if (cur == GetGenericParamDeclName(name)) { + return true; + } + } + return false; +} + +OwnedPtr CangjieASTBuiler::CreateFuncDecl( + CompilerType type, std::string identifier, int line, Ptr pdecl, std::string funcName) { + auto decl = MakeOwned(); + auto declBegin = Position(m_file_id, line, 1); + decl->identifier = {identifier, declBegin, declBegin + Position(0, 1, 1)}; + decl->begin = Position(m_file_id, line, 1); + decl->end = decl->begin + Position(0, 1, 1); + decl->funcBody = MakeOwned(); + decl->fullPackageName = m_current_pkgname; + auto subName = GetSubNameWithoutPkgname(funcName, m_current_pkgname); + subName = subName.empty() ? funcName:subName; + auto instantiatedNames = GetInstantiatedParamDeclName(subName); + // For func with generic type, create generic type by funcName. + CreateFuncDeclGeneric(decl.get(), type, funcName, pdecl); + if (pdecl && (identifier == "init" || identifier == pdecl->identifier)) { + decl->EnableAttr(Attribute::CONSTRUCTOR); + decl->EnableAttr(Attribute::TOOL_ADD); + decl->funcBody->retType = CreateAstType(pdecl->identifier, pdecl, m_current_pkgname); + } else { + std::string retTypeStr = type.GetFunctionReturnType().GetTypeName().GetCString(); + auto tempDecl = lldb_private::IsGenericFromFuncDecl(instantiatedNames, retTypeStr) ? decl.get():pdecl; + decl->funcBody->retType = CreateAstType(retTypeStr, tempDecl, m_current_pkgname); + } + auto paramList = MakeOwned(); + for (int i = 0; i < type.GetFunctionArgumentCount(); i++) { + auto paramType = type.GetFunctionArgumentAtIndex(i); + std::string paramTypeStr = paramType.GetTypeName().GetCString(); + if (i == 0 && paramTypeStr.find(LOCAL_FUNC_CAPTUREDVAR) != std::string::npos) { + // If the first argument is about captured variables, you need to omit the first argument. + m_capture_vars.insert({identifier, paramType}); + continue; + } + OwnedPtr param = MakeOwned(); + param->identifier = "a" + std::to_string(i); + auto tempDecl = lldb_private::IsGenericFromFuncDecl(instantiatedNames, paramTypeStr) ? decl.get():pdecl; + param->type = CreateAstType(paramTypeStr, tempDecl, m_current_pkgname); + paramList->params.emplace_back(std::move(param)); + } + decl->funcBody->paramLists.emplace_back(std::move(paramList)); + if (!pdecl) { + decl->EnableAttr(Attribute::GLOBAL); + } + decl->EnableAttr(Attribute::PUBLIC); + return decl; +} + +OwnedPtr CangjieASTBuiler::CreateEnumDecl(const CompilerType& type) { + OwnedPtr decl = MakeOwned(); + // If the enum type `RGBColor` has parameters, the name is Enum$RGBColor.Otherwise, the name is default.RGBColor. + decl->identifier = GetTypeNameWithoutPrefix(type.GetTypeName(), m_current_pkgname); + decl->fullPackageName = m_current_pkgname; + decl->mangledName = BaseMangler().Mangle(*decl); + decl->EnableAttr(Attribute::PUBLIC, Attribute::EXTERNAL, Attribute::GLOBAL); + return decl; +} + +bool lldb_private::IsEnumName(const std::string& name) { + return name.find("Enum$") != std::string::npos || + name.find(ENUM_PREFIX_NAME) != std::string::npos || + name.find(E0_PREFIX_NAME) != std::string::npos || + name.find(E2_PREFIX_NAME_OPTION_LIKE) != std::string::npos || + name.find(E3_PREFIX_NAME) != std::string::npos; +} + +bool lldb_private::IsGenericType(const std::string& type_name) { + auto pos = type_name.find(GENERIC_TYPE_PREFIX_NAME); + return pos != std::string::npos; +} + +bool lldb_private::IsGenericTypeParameters(const std::string& type_name) { + auto pos = type_name.rfind("<"); + auto rpos = type_name.rfind(">"); + if (pos != std::string::npos && rpos!= std::string::npos) { + return true; + } + return false; +} + +std::vector CangjieASTBuiler::GetInstantiatedParamDeclName(const std::string& declName) { + std::vector result; + std::string name = declName; + name.erase(std::remove(name.begin(), name.end(), ' '), name.end()); + auto pkgPos = name.find(PACKAGE_SUFFIX); + if (pkgPos!= std::string::npos) { + name = name.substr(pkgPos + 2, name.size() - 1); + } + auto lpos = name.find("<"); + auto rpos = name.rfind(">"); + if (lpos == std::string::npos || rpos== std::string::npos) { + return result; + } + name = name.substr(lpos + 1, rpos - lpos - 1); + return SplitTypeName(name); +} + +std::string lldb_private::GetGenericParamDeclName(const std::string& gname) { + std::string name = gname; + if (IsGenericTypeParameters(name)) { + auto lpos = name.find("<"); + auto rpos = name.find(">"); + name = name.substr(lpos + 1, rpos - lpos - 1); + } + auto pos = name.rfind(GENERIC_TYPE_PREFIX_NAME); + if (pos == std::string::npos) { + return name; + } + auto subNameStr = name.substr(pos + GENERIC_TYPE_PREFIX_NAME.size(), name.size()-1); + return subNameStr; +} + +bool lldb_private::IsGenericInstanced(const std::string& type_name) { + auto pos = type_name.rfind("<"); + auto rpos = type_name.rfind(">"); + if (pos == std::string::npos || rpos== std::string::npos) { + return false; + } + auto subName = type_name.substr(pos+1, rpos-1); + if (subName.rfind(GENERIC_TYPE_PREFIX_NAME) == std::string::npos ) { + return true; + } + return false; +} + +std::string lldb_private::GetGenericDeclName(const std::string& declName) { + // Obtain the declaration of a generic type, for example, get A from default::A<$G_T>. + std::string name = declName; + // Delete pkg name. + auto pkgPos = name.find(PACKAGE_SUFFIX); + if (pkgPos != std::string::npos) { + name = name.substr(pkgPos + 2, name.size() - 1); + } + auto pos = name.find("<"); + auto rpos = name.rfind(">"); + if (pos == std::string::npos || rpos== std::string::npos) { + return name; + } + return name.substr(0, pos); +} diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieASTUtils.h b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieASTUtils.h new file mode 100644 index 000000000..bce5bcb4e --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieASTUtils.h @@ -0,0 +1,90 @@ +//===-- CangjieASTUtils.h -------------------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_AST_UTILS_H +#define LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_AST_UTILS_H +#include "lldb/Symbol/CompileUnit.h" +#include "cangjie/Mangle/BaseMangler.h" +#include "cangjie/Sema/TypeManager.h" + +using namespace Cangjie; +using namespace Cangjie::AST; + +namespace lldb_private { +inline const std::string LOCAL_PACKAGE_NAME = "__lldb_locals"; +inline const std::string EXPR_RESULT_NAME = "__lldb_expr_result"; +inline const std::string RANGE_NAME = "Range"; +inline const std::string TUPLE_NAME = "Tuple"; +inline const std::string INJECTED_SUPER_NAME = "$__lldb_injected_super"; +inline const std::string INJECTED_THIS_NAME = "__lldb_injected_self"; +inline const std::string COLLECTION_PACKAGE_NAME = "std.collection"; +inline const std::string ENUM_PREFIX_NAME = "E1$"; // Common enum +inline const std::string E0_PREFIX_NAME = "E0$"; // all enum constructors are without parameter. +inline const std::string E2_PREFIX_NAME_OPTION_LIKE = "E2$"; // option like enum +inline const std::string E2_CTOR_PREFIX_NAME = "N$_"; +inline const std::string E3_PREFIX_NAME = "E3$"; // all enum constructors are without ref parameter. +inline const std::string INTERFACE_PREFIX_NAME = "Interface$"; +inline const std::string GENERIC_TYPE_PREFIX_NAME = "$G_"; +inline const std::string CJDB_ADD_GENERIC_MEMBER_FUNC_NAME = "$GetGenericDef"; +inline const std::string LOCAL_FUNC_CAPTUREDVAR = "$CapturedVars"; +inline const std::string PACKAGE_SUFFIX = "::"; +class CangjieASTBuiler { +public: + OwnedPtr CreateRefType(std::string name, Ptr target, std::string pkg, + bool isGenericDeclFromTarget = true); + OwnedPtr CreateFuncType(std::string name, Ptr target, std::string pkg); + OwnedPtr CreateTupleType(std::string name, Ptr target); + std::vector GetInstantiatedParamDeclName(const std::string& name); + std::vector SplitTupleName(std::string name); + std::vector SplitTypeName(std::string name); + std::vector SplitCollectionName(std::string name); + OwnedPtr CreateVArrayType(std::string name, Ptr target); + OwnedPtr CreateAstType(std::string name, Ptr target, std::string pkg = "", + bool isGenericDeclFromTarget = true); + static bool IsFunctionType(ConstString type_name); + OwnedPtr CreateVarDecl(const CompilerType type, std::string name, + std::string prefix, Ptr target = nullptr); + OwnedPtr CreateEnumVarDecl(CompilerType type, std::string name, + std::string prefix, Ptr target = nullptr); + + OwnedPtr CreateEnumFuncDecl(const CompilerType &enumType, const CompilerType &ctorType, + std::string name, Ptr pdecl = nullptr); + OwnedPtr CreateFuncDecl( + CompilerType type, std::string identifier, int line = 1, Ptr pdecl = nullptr, std::string funcName = ""); + OwnedPtr CreateEnumDecl(const CompilerType& type); + void SetGenericByName(Ptr generic, std::string& typeName, const Ptr& decl); + void CreateDeclGeneric(Ptr ret, std::string& typeName); + void CreateFuncDeclGeneric(Ptr ret, CompilerType& type, std::string& name, Ptr pdecl); + Ptr GetTargetOfGenericDecl(Ptr pdecl, std::string& retTypeStr); + bool IsRefType(const Ptr& ty); + std::string m_current_pkgname; + std::set m_type_names; + std::map m_capture_vars; + unsigned int m_file_id; +}; + +bool IsEnumName(const std::string& str); +std::string GetEnumPrefix(const std::string& name); +std::string GetEnumNameWithoutPrefix(std::string& name, std::string& pkgname); +bool StartsWith(const std::string& str, const std::string& pre); +std::string GetPkgNameFromCompilerUnit(const CompileUnit& compUnit); +std::string GetSubNameWithoutPkgname(std::string& name, std::string& pkg); +std::string DeleteAllPkgname(const std::string& name, std::string& pkg); +std::string DeletePrefixOfType(const std::string& name); +bool IsGenericTypeParameters(const std::string& type_name); +bool IsGenericType(const std::string& type_name); +bool IsGenericInstanced(const std::string& type_name); +bool IsGenericFromFuncDecl(std::vector& instantiatedNames, std::string name); +std::string GetGenericDeclName(const std::string& type_name); +std::string GetGenericParamDeclName(const std::string& name); +std::string GetTypeNameWithoutPrefix(ConstString type_name, std::string& pkgname); +} + +#endif diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieCompilerInstance.cpp b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieCompilerInstance.cpp new file mode 100644 index 000000000..5d5be5eef --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieCompilerInstance.cpp @@ -0,0 +1,1219 @@ +//===-- CangjieCompilerInstance.cpp ---------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#include "CangjieCompilerInstance.h" +#include "cangjie/Mangle/BaseMangler.h" + +#include "cangjie/AST/PrintNode.h" +#include "cangjie/AST/Walker.h" +#include "cangjie/AST/Create.h" +#include "cangjie/Utils/SafePointer.h" +#include "cangjie/Modules/ImportManager.h" +#include "cangjie/Sema/TypeManager.h" + +#include "cangjie/CodeGen/EmitPackageIR.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/LLDBLog.h" + +using namespace Cangjie; +using namespace AST; +using namespace lldb_private; +using namespace lldb_private::CangjieExpr; + +bool CangjieCompilerInstance::PerformCodeGen() { + if (chirData.GetCurrentCHIRPackage() == nullptr) { + return false; + } + Cangjie::CHIR::CHIRBuilder builder(chirData.GetCHIRContext()); + auto llvmModules = Cangjie::CodeGen::GenPackageModules(builder, chirData, invocation.globalOptions, *this, false); + CJC_ASSERT(llvmModules.size() == 1); + m_module_up = std::move(llvmModules[0]); + return true; +} + +std::unique_ptr CangjieCompilerInstance::ReleaseModule() { + return std::move(m_module_up); +} + +void CangjieCompilerInstance::AddTyOfRefType(AST::RefType& rt) +{ + Ptr decl = nullptr; + for (auto& pkg : m_package_names) { + // If the identifier begins with the package name. + auto curpkgPre = pkg + PACKAGE_SUFFIX; + if (rt.ref.identifier.Val().find(curpkgPre) == 0) { + // Delete prefix "std.core::"/"std.collection::" + rt.ref.identifier = rt.ref.identifier.Val().substr(curpkgPre.size()); + decl = importManager.GetImportedDecl(pkg, rt.ref.identifier); + break; + } + } + if (!decl) { + // Check if it is in core package. + decl = importManager.GetCoreDecl(rt.ref.identifier); + if (!decl) { + // Check if it is in collection package. + decl = importManager.GetImportedDecl(COLLECTION_PACKAGE_NAME, rt.ref.identifier); + } + } + std::vector> typeArgs; + for (auto& arg : rt.typeArguments) { + CheckTypeAndAddTy(arg); + typeArgs.push_back(arg->ty); + } + if (!decl) { + decl = rt.ref.target; + } + if (decl) { + rt.ty = GetTyFromASTType(*decl, typeArgs); + return; + } + return; +} + +void CangjieCompilerInstance::CheckTypeAndAddTy(OwnedPtr& type) +{ + if (!type) { + return; + } + bool isGenericType = false; + if (type->astKind == AST::ASTKind::REF_TYPE) { + auto rt = RawStaticCast(type.get()); + if (rt->ref.target && rt->ref.target->astKind == AST::ASTKind::GENERIC_PARAM_DECL) { + isGenericType = true; + } + } + if (AST::Ty::IsTyCorrect(type->ty) && !isGenericType) { + return; + } + if (type->astKind == AST::ASTKind::PRIMITIVE_TYPE) { + auto pt = RawStaticCast(type.get()); + pt->ty = typeManager->GetPrimitiveTy(pt->kind); + } else if (type->astKind == AST::ASTKind::REF_TYPE) { + auto rt = RawStaticCast(type.get()); + AddTyOfRefType(*rt); + } else if (type->astKind == AST::ASTKind::TUPLE_TYPE) { + auto tt = RawStaticCast(type.get()); + std::vector> subTys; + for (auto& subType : tt->fieldTypes) { + CheckTypeAndAddTy(subType); + subTys.push_back(subType->ty); + } + tt->ty = typeManager->GetTupleTy(subTys); + } else if (type->astKind == AST::ASTKind::VARRAY_TYPE) { + auto vaType = RawStaticCast(type.get()); + auto constType = RawStaticCast(vaType->constantType.get()); + auto le = RawStaticCast(constType->constantExpr.get()); + auto vaSize = stoll(le->stringValue); + CheckTypeAndAddTy(vaType->typeArgument); + if (vaType->typeArgument) { + vaType->ty = typeManager->GetVArrayTy(*vaType->typeArgument->ty, vaSize); + } + } else if (type->astKind == AST::ASTKind::FUNC_TYPE) { + auto func_type = RawStaticCast(type.get()); + std::vector> params; + for (auto ¶m : func_type->paramTypes) { + CheckTypeAndAddTy(param); + params.emplace_back(param->ty); + } + CheckTypeAndAddTy(func_type->retType); + if (func_type->retType) { + type->ty = typeManager->GetFunctionTy(params, func_type->retType->ty); + } + } +} + +Ptr CangjieCompilerInstance::GetInstantiatedTy(AST::ClassTy& cTy, Ptr& paramTy) { + if (!paramTy->IsGeneric()) { + return paramTy; + } + auto& typeParams = cTy.decl->generic->typeParameters; + CJC_ASSERT(typeParams.size() == cTy.typeArgs.size()); + for (size_t i = 0; i < typeParams.size(); i++) { + if (typeParams[i]->identifier.Val() == paramTy->name) { + return cTy.typeArgs[i]; + } + } + return paramTy; +} + +OwnedPtr CangjieCompilerInstance::CreateSuperCall(AST::ClassDecl& cd) +{ + auto superType = cd.inheritedTypes.front().get(); + if (!superType) { + return nullptr; + } + auto cTy = StaticCast(superType->ty); + auto superExpr = CreateRefExpr("super"); + superExpr->isSuper = true; + std::vector> args; + for (auto& decl : cTy->decl->body->decls) { + if (auto fd = DynamicCast(decl.get()); fd && fd->TestAttr(Attribute::CONSTRUCTOR)) { + superExpr->ref.target = fd; + superExpr->ty = fd->ty; + for (auto& param : fd->funcBody->paramLists[0]->params) { + if (!param || !param->ty || param->ty->IsInvalid()) { + continue; + } + // Parent's init func may have generic param. + auto argTy = GetInstantiatedTy(*cTy, param->ty); + auto initializer = CreateInitializer(argTy); + if (!initializer) { + return nullptr; + } + args.emplace_back(CreateFuncArg(std::move(initializer))); + } + break; + } + } + auto superCall = CreateCallExpr(std::move(superExpr), std::move(args)); + superCall->callKind = CallKind::CALL_SUPER_FUNCTION; + superCall->ty = superType->ty; + for (auto& decl : cTy->decl->body->decls) { + if (auto fd = DynamicCast(decl.get()); fd && fd->TestAttr(Attribute::CONSTRUCTOR)) { + superCall->resolvedFunction = fd; + } + } + return superCall; +} + +void CangjieCompilerInstance::CreateInitFunc(AST::Decl& decl) +{ + auto funcTy = typeManager->GetFunctionTy({}, decl.ty); + auto funcBody = MakeOwned(); + auto funcParamList = MakeOwned(); + funcBody->paramLists.push_back(std::move(funcParamList)); + funcBody->body = MakeOwned(); + funcBody->ty = funcTy; + + auto initFunc = MakeOwned(); + initFunc->ty = funcTy; + initFunc->funcBody = std::move(funcBody); + initFunc->funcBody->funcDecl = initFunc.get(); + initFunc->identifier = "init"; + initFunc->outerDecl = &decl; + initFunc->fullPackageName = decl.fullPackageName; + initFunc->EnableAttr(Attribute::CONSTRUCTOR, Attribute::PUBLIC, Attribute::COMPILER_ADD); + if (decl.astKind == ASTKind::STRUCT_DECL) { + initFunc->EnableAttr(Attribute::IN_STRUCT); + auto structDecl = static_cast(&decl); + if (!structDecl->body) { + structDecl->body = MakeOwned(); + } + initFunc->funcBody->parentStruct = structDecl; + structDecl->body->decls.emplace_back(std::move(initFunc)); + return; + } + if (decl.astKind == ASTKind::CLASS_DECL) { + initFunc->constructorCall = ConstructorCall::SUPER; + initFunc->EnableAttr(Attribute::IN_CLASSLIKE); + auto classDecl = static_cast(&decl); + // Add Object Super Class + auto refType = MakeOwned(); + refType->curFile = classDecl->curFile; + refType->ref.identifier = OBJECT_NAME; + refType->EnableAttr(Attribute::COMPILER_ADD); + refType->ref.target = importManager.GetCoreDecl(OBJECT_NAME); + if (refType->ref.target) { + refType->ty = refType->ref.target->ty; + } + classDecl->inheritedTypes.emplace_back(std::move(refType)); + if (!classDecl->body) { + classDecl->body = MakeOwned(); + } + initFunc->funcBody->parentClassLike = classDecl; + // Add super call `super()` + auto superCall = CreateSuperCall(*classDecl); + initFunc->funcBody->body->ty = superCall->ty; + initFunc->funcBody->body->body.push_back(std::move(superCall)); + classDecl->body->decls.emplace_back(std::move(initFunc)); + } + return; +} + +void CangjieCompilerInstance::UpdateDeclTyByGeneric(Ptr decl) { + std::vector> typeArgs; + auto generic = decl->GetGeneric(); + if (!generic) { + return; + } + for (auto& it: generic->typeParameters) { + auto gpd = RawStaticCast(it.get()); + CreateTyAndDefaultCtor(*gpd, {}); + typeArgs.push_back(gpd->ty); + } + for (auto& it: generic->genericConstraints) { + auto gc = RawStaticCast(it.get()); + if (gc->type->ref.target) { + CreateTyAndDefaultCtor(*gc->type->ref.target, {}); + gc->type->ty = GetTyFromASTType(*gc->type->ref.target, {}); + } + for (auto& bound:gc->upperBounds) { + auto rt = RawStaticCast(bound.get()); + if (rt->ref.target) { + CreateTyAndDefaultCtor(*rt->ref.target, {}); + rt->ty = GetTyFromASTType(*rt->ref.target, {}); + } + } + } + decl->ty = GetTyFromASTType(*decl, typeArgs); +} + +Ptr CangjieCompilerInstance::GetTyFromASTType(Decl& decl, const std::vector>& typeArgs) +{ + switch (decl.astKind) { + case ASTKind::CLASS_DECL: { + auto cd = StaticAs(&decl); + for (auto& it : cd->inheritedTypes) { + CheckTypeAndAddTy(it); + } + return typeManager->GetClassTy(*cd, typeArgs); + } + case ASTKind::INTERFACE_DECL: { + auto id = StaticAs(&decl); + for (auto& it : id->inheritedTypes) { + CheckTypeAndAddTy(it); + } + return typeManager->GetInterfaceTy(*id, typeArgs); + } + case ASTKind::STRUCT_DECL: { + auto sd = StaticAs(&decl); + return typeManager->GetStructTy(*sd, typeArgs); + } + case ASTKind::ENUM_DECL: { + auto ed = StaticAs(&decl); + return typeManager->GetEnumTy(*ed, typeArgs); + } + case ASTKind::TYPE_ALIAS_DECL: { + auto tad = StaticAs(&decl); + return typeManager->GetTypeAliasTy(*tad, typeArgs); + } + case ASTKind::GENERIC_PARAM_DECL: { + auto gpd = StaticAs(&decl); + return typeManager->GetGenericsTy(*gpd); + } + default: + return decl.ty; + } +} + +void CangjieCompilerInstance::CreateDefaultCtor(AST::Decl& decl) +{ + if (decl.astKind == ASTKind::CLASS_DECL) { + auto cd = StaticCast(&decl); + for (auto& it : cd->inheritedTypes) { + CheckTypeAndAddTy(it); + } + bool hasConstructor = false; + for (auto& d : cd->body->decls) { + if (auto fd = DynamicCast(d.get()); fd && fd->TestAttr(Attribute::CONSTRUCTOR)) { + hasConstructor = true; + fd->EnableAttr(Attribute::IN_CLASSLIKE, Attribute::COMPILER_ADD); + fd->funcBody->parentClassLike = cd; + fd->funcBody->funcDecl = fd; + if (fd->funcBody->retType) { + fd->funcBody->retType->ty = decl.ty; + } + // Add super call `super()` + auto superCall = CreateSuperCall(*cd); + if (superCall) { + fd->funcBody->body = MakeOwned(); + fd->funcBody->body->ty = superCall->ty; + fd->funcBody->body->body.push_back(std::move(superCall)); + fd->constructorCall = ConstructorCall::SUPER; + } + } + } + if (!hasConstructor) { + CreateInitFunc(*cd); + } + } else if (decl.astKind == ASTKind::STRUCT_DECL) { + auto sd = StaticCast(&decl); + bool hasConstructor = false; + for (auto& d : sd->body->decls) { + if (auto fd = DynamicCast(d.get()); fd && fd->TestAttr(Attribute::CONSTRUCTOR)) { + hasConstructor = true; + fd->EnableAttr(Attribute::IN_STRUCT, Attribute::COMPILER_ADD); + fd->funcBody->parentStruct = sd; + fd->funcBody->funcDecl = fd; + if (fd->funcBody->retType) { + fd->funcBody->retType->ty = decl.ty; + } + } + } + if (!hasConstructor) { + CreateInitFunc(*sd); + } + } + return; +} + +void CangjieCompilerInstance::CreateFuncdeclTy(AST::FuncDecl& fd) +{ + CheckTypeAndAddTy(fd.funcBody->retType); + if (!fd.funcBody->retType) { + return; + } + auto retTy = fd.funcBody->retType->ty; + std::vector> params; + for (auto& param : fd.funcBody->paramLists[0]->params) { + if (!param->type) { + continue; + } + CheckTypeAndAddTy(param->type); + param->ty = param->type->ty; + params.emplace_back(param->ty); + } + UpdateDeclTyByGeneric(&fd); + fd.ty = typeManager->GetFunctionTy(params, retTy); + fd.funcBody->ty = fd.ty; +} + +void CangjieCompilerInstance::CreateTyAndDefaultCtor( + AST::Decl& decl, const std::vector>& typeArgs) +{ + switch (decl.astKind) { + case ASTKind::CLASS_DECL: { + auto cd = StaticAs(&decl); + for (auto& it : cd->inheritedTypes) { + if (it->astKind == AST::ASTKind::REF_TYPE && it->GetTypeArgs().empty()) { + continue; + } + auto rt = RawStaticCast(it.get()); + if (rt->ref.target) { + CreateTyAndDefaultCtor(*rt->ref.target, {}); + } + } + decl.ty = typeManager->GetClassTy(*cd, typeArgs); + UpdateDeclTyByGeneric(&decl); + CreateDefaultCtor(decl); + break; + } + case ASTKind::INTERFACE_DECL: { + auto id = StaticAs(&decl); + decl.ty = typeManager->GetInterfaceTy(*id, typeArgs); + UpdateDeclTyByGeneric(&decl); + break; + } + case ASTKind::STRUCT_DECL: { + auto sd = StaticAs(&decl); + decl.ty = typeManager->GetStructTy(*sd, typeArgs); + UpdateDeclTyByGeneric(&decl); + CreateDefaultCtor(decl); + break; + } + case ASTKind::ENUM_DECL: { + auto ed = StaticAs(&decl); + decl.ty = typeManager->GetEnumTy(*ed, typeArgs); + UpdateDeclTyByGeneric(&decl); + for (size_t i = 0; i < ed->constructors.size(); i++) { + auto tempDecl = RawStaticCast(ed->constructors[i].get()); + tempDecl->ty = decl.ty; + } + break; + } + case ASTKind::TYPE_ALIAS_DECL: { + auto tad = StaticAs(&decl); + decl.ty = typeManager->GetTypeAliasTy(*tad, typeArgs); + break; + } + case ASTKind::GENERIC_PARAM_DECL: { + auto gpd = StaticAs(&decl); + decl.ty = typeManager->GetGenericsTy(*gpd); + break; + } + case ASTKind::FUNC_DECL: { + auto fd = StaticAs(&decl); + CreateFuncdeclTy(*fd); + break; + } + case ASTKind::VAR_DECL: { + auto vd = StaticAs(&decl); + UpdateDeclTyByGeneric(&decl); + if (vd->type) { + CheckTypeAndAddTy(vd->type); + decl.ty = vd->type->ty; + } + break; + } + default: + break; + } +} + +std::string CangjieCompilerInstance::GetStringOfASTNode(const Ptr& node) { + std::stringstream buffer; + std::streambuf* oldbuf = std::cout.rdbuf(buffer.rdbuf()); + PrintNode(node); + std::cout.rdbuf(oldbuf); + std::string astNodeString = buffer.str(); + return astNodeString; +} + +std::vector> CangjieCompilerInstance::CreateFuncArgsFromCapturedVars(std::string ident) +{ + std::vector> funcargs; + for (auto& decl: m_tryExpr_result->curFile->decls) { + if (decl->astKind == AST::ASTKind::CLASS_DECL && decl->identifier == ident) { + auto cd = StaticAs(decl.get()); + for (auto& d : cd->body->decls) { + if (d->astKind == AST::ASTKind::VAR_DECL && + d->identifier.Val().find("$Captured") == std::string::npos) { + funcargs.emplace_back(CreateFuncArg(CreateRefExpr(d->identifier))); + } + } + } + } + return funcargs; +} + +bool HasSameCapturedVar(std::vector>& capturedVars, Ptr vd) { + for (auto v : capturedVars) { + if (v->identifier == vd->identifier) { + return true; + } + } + return false; +} + +void CangjieCompilerInstance::AddCapturedVarsToTryExpr( + std::vector>& localDecls, AST::TryExpr& tryExpr) { + auto hasCapturedVarsClass = [this](std::string fn) -> bool { + auto classname = fn + "$class" + LOCAL_FUNC_CAPTUREDVAR; + for (auto& decl: m_tryExpr_result->curFile->decls) { + if (decl->astKind == AST::ASTKind::CLASS_DECL && + decl->identifier == classname) { + return true; + } + } + return false; + }; + for (auto& decl: localDecls) { + if (decl->astKind != AST::ASTKind::FUNC_DECL) { + continue; + } + if (!hasCapturedVarsClass(decl->identifier)) { + // If the local function does not have $CapturedVars, add `var foo$Object = Object()`. + auto vd = MakeOwned(); + vd->identifier = decl->identifier + "$Object"; + auto re = MakeOwned(); + re->ref.identifier = "Object"; + re->ref.identifier.SetPos(re->begin, re->begin + re->ref.identifier.Val().size()); + vd->initializer = CreateCallExpr(std::move(re), {}); + AddCurFile(*vd, m_tryExpr_result->curFile); + if (!HasSameCapturedVar(m_captured_vars, vd.get())) { + m_captured_vars.emplace_back(vd.get()); + tryExpr.tryBlock->body.emplace(tryExpr.tryBlock->body.begin(), std::move(vd)); + } + continue; + } + /* + If the local function has $CapturedVars, add the following statements. + var foo$CaptureVars = foo$class$CapturedVars(aa) + var foo$Object = (foo$CaptureVars as Object).getOrThrow() + */ + // Add `var foo$CapturedVars = foo$class$CapturedVars()`. + auto vd1 = MakeOwned(); + vd1->identifier = decl->identifier + LOCAL_FUNC_CAPTUREDVAR; + auto re1 = MakeOwned(); + re1->ref.identifier = decl->identifier + "$class" + LOCAL_FUNC_CAPTUREDVAR; + re1->ref.identifier.SetPos(re1->begin, re1->begin + re1->ref.identifier.Val().size()); + auto funcArgs = CreateFuncArgsFromCapturedVars(re1->ref.identifier); + vd1->initializer = CreateCallExpr(std::move(re1), std::move(funcArgs)); + AddCurFile(*vd1, m_tryExpr_result->curFile); + + // Add `var foo$Object = (foo$CaptureVars as Object).getOrThrow()`. + auto vd2 = MakeOwned(); + vd2->identifier = decl->identifier + "$Object"; + auto re2 = MakeOwned(); + re2->ref.identifier = vd1->identifier; + re2->ref.identifier.SetPos(re2->begin, re2->begin + re2->ref.identifier.Val().size()); + auto asExpr = MakeOwned(); + asExpr->leftExpr = std::move(re2); + asExpr->asType = CreateRefTypeInCore("Object"); + auto ma = CreateMemberAccess(std::move(asExpr), "getOrThrow"); + vd2->initializer = CreateCallExpr(std::move(ma), {}); + AddCurFile(*vd2, m_tryExpr_result->curFile); + if (!HasSameCapturedVar(m_captured_vars, vd2.get())) { + m_captured_vars.emplace_back(vd2.get()); + tryExpr.tryBlock->body.emplace(tryExpr.tryBlock->body.begin(), std::move(vd2)); + } + + tryExpr.tryBlock->body.emplace(tryExpr.tryBlock->body.begin(), std::move(vd1)); + } +} + +void CangjieCompilerInstance::AddLocalsToExprResult(std::vector>& localDecls) +{ + auto addLocals = [this, &localDecls](Ptr curNode) -> AST::VisitAction { + if (curNode->astKind != AST::ASTKind::TRY_EXPR) { + return AST::VisitAction::WALK_CHILDREN; + } + auto tryExpr = RawStaticCast(curNode.get()); + // Add capturedVars of local function first. + AddCapturedVarsToTryExpr(localDecls, *tryExpr); + // Add local variable then. + for (auto& decl: localDecls) { + if (decl->astKind != AST::ASTKind::VAR_DECL) { + continue; + } + auto var = RawStaticCast(decl.get()); + // Add `var a = __lldb_locals_a` at the begin of the tryblock. + auto varDecl = MakeOwned(); + varDecl->isVar = var->isVar; + varDecl->identifier = var->identifier; + varDecl->initializer = CreateRefExpr(LOCAL_PACKAGE_NAME + "_" + var->identifier); + AddCurFile(*varDecl, m_tryExpr_result->curFile); + tryExpr->tryBlock->body.emplace(tryExpr->tryBlock->body.begin(), std::move(varDecl)); + if (var->isVar && !var->ty->IsClass() && !var->ty->IsFunc()) { + // Add `__lldb_locals_a = a` to the end of the tryBlock. + auto leftExpr = CreateRefExpr(LOCAL_PACKAGE_NAME + "_" + var->identifier); + auto assignExpr = CreateAssignExpr(std::move(leftExpr), CreateRefExpr(var->identifier)); + AddCurFile(*assignExpr, m_tryExpr_result->curFile); + tryExpr->tryBlock->body.emplace_back(std::move(assignExpr)); + } + } + return AST::VisitAction::SKIP_CHILDREN; + }; + + AST::Walker walker(m_tryExpr_result, addLocals); + walker.Walk(); +} + +void CangjieCompilerInstance::PerformSuperExpression(bool inClassContext, bool isConstructor, bool isStaticMethod) +{ + if (!inClassContext && !isStaticMethod) { + return; + } + // If in the context of a class, modify super to __lldb_injected_super. + // If in a static method of a class, the diagnostic reports an error. + auto modifySuper = [this, &isConstructor, &isStaticMethod](Ptr curNode) -> AST::VisitAction { + if (curNode->astKind == AST::ASTKind::CALL_EXPR) { + auto ce = RawStaticCast(curNode.get()); + if (ce->baseFunc->astKind == AST::ASTKind::REF_EXPR) { + auto re = RawStaticCast(ce->baseFunc.get()); + if (isStaticMethod && (re->isSuper || re->isThis)) { + diag.Diagnose(*re, DiagKind::sema_static_members_cannot_call_members, re->ref.identifier.Val()); + return AST::VisitAction::SKIP_CHILDREN; + } + if (!isConstructor && re->isSuper) { + diag.Diagnose(*ce, DiagKind::sema_invalid_this_call_outside_ctor, re->ref.identifier.Val()); + return AST::VisitAction::SKIP_CHILDREN; + } + if (re->isThis) { + re->ref.identifier = INJECTED_THIS_NAME; + re->isThis = false; + auto ma = CreateMemberAccess(std::move(ce->baseFunc), "init"); + ce->baseFunc = std::move(ma); + return AST::VisitAction::SKIP_CHILDREN; + } + if (re->isSuper) { + re->ref.identifier = INJECTED_SUPER_NAME; + re->isSuper = false; + auto ma = CreateMemberAccess(std::move(ce->baseFunc), "init"); + ce->baseFunc = std::move(ma); + return AST::VisitAction::SKIP_CHILDREN; + } + } + } + if (curNode->astKind != AST::ASTKind::REF_EXPR) { + return AST::VisitAction::WALK_CHILDREN; + } + auto re = RawStaticCast(curNode.get()); + if (isStaticMethod && (re->isSuper || re->isThis)) { + diag.Diagnose(*re, DiagKind::sema_static_members_cannot_call_members, re->ref.identifier.Val()); + return AST::VisitAction::SKIP_CHILDREN; + } + if (re->isSuper) { + re->ref.identifier = INJECTED_SUPER_NAME; + re->isSuper = false; + } + return AST::VisitAction::SKIP_CHILDREN; + }; + + AST::Walker walker(m_expr_result, modifySuper); + walker.Walk(); + return; +} + +bool checkUnsupportedNode(AST::Node& node) { + Log *log = GetLog(LLDBLog::Expressions); + if (node.astKind == ASTKind::REF_TYPE) { + auto decl = Ty::GetDeclOfTy(node.ty); + if (decl && decl->fullPackageName == "std.core" && decl->identifier == "Future") { + if (log) { + LLDB_LOGF(log, "%s is not supported in expression currently.\n", decl->identifier.Val().c_str()); + } + return true; + } + } + return false; +} + +void DeletePackagePrefix(Ptr decl) +{ + auto deletepkg = [](Ptr curNode) -> AST::VisitAction { + if (curNode->astKind == AST::ASTKind::REF_TYPE) { + auto rt = RawStaticCast(curNode.get()); + auto pos = rt->ref.identifier.Val().find(PACKAGE_SUFFIX); + if (pos != std::string::npos) { + rt->ref.identifier = rt->ref.identifier.Val().substr(pos + PACKAGE_SUFFIX.size()); + } + return AST::VisitAction::SKIP_CHILDREN; + } + return AST::VisitAction::WALK_CHILDREN; + }; + + AST::Walker walker(decl, deletepkg); + walker.Walk(); +} + +bool CangjieCompilerInstance::PerformImportPackageForCjdb() { + std::vector> packages; + // Collect rawptr of fake package. + for (auto& pkg : this->m_fake_packages) { + if (pkg->fullPackageName == LOCAL_FUNC_CAPTUREDVAR) { + // Move local capturedvar class to Expr package. + for (auto& decl : pkg->files[0]->decls) { + DeletePackagePrefix(decl.get()); + m_tryExpr_result->curFile->decls.emplace_back(std::move(decl)); + } + pkg->files[0]->decls.clear(); + continue; + } + packages.emplace_back(pkg.get()); + } + + // Add import to __lldb_expr_pkg. + AddImportSpecToLLDBExprPackage(); + // Set fake package to importManager. + importManager.SetImportedPackageFromASTNode(m_fake_packages); + + PerformImportPackage(); + bool inBlacklist = false; + // Walk decls in packages, and set decl's ty especially for those from std.core. + auto addDeclTy = [&](Ptr curNode) -> VisitAction { + if (curNode->IsDecl()) { + auto decl = RawStaticCast(curNode.get()); + CreateTyAndDefaultCtor(*decl, {}); + decl->mangledName = BaseMangler().Mangle(*decl); + } + if (checkUnsupportedNode(*curNode)) { + inBlacklist = true; + } + return VisitAction::WALK_CHILDREN; + }; + + for (auto& fpkg : packages) { + AST::Walker astWalker(fpkg.get(), addDeclTy); + astWalker.Walk(); + if (fpkg->fullPackageName == LOCAL_PACKAGE_NAME) { + // Add local variable: var a = __lldb_locals_a + AddLocalsToExprResult(fpkg->files[0]->decls); + } + // Expression in the blacklist, which is not supported currently. + if (inBlacklist) { + return false; + } + } + Log *log = GetLog(LLDBLog::Expressions); + if (log) { + std::string fakePkg; + for (auto& fpkg : packages) { + fakePkg = fakePkg + GetStringOfASTNode(fpkg); + } + LLDB_LOGF(log, "-------- Package Node after Importpackage -------- \n%s", fakePkg.c_str()); + std::string str = GetStringOfASTNode(this->GetSourcePackages()[0]); + LLDB_LOGF(log, "-------- Package Node before sema -------- \n%s", str.c_str()); + } + return true; +} + +// Collect identifiers in user string after parsing and walking the AST. +std::vector CangjieCompilerInstance::WalkAndCollectIdentifiers() +{ + auto exprPkg = this->GetSourcePackages()[0]; + auto findExprResult = [this](Ptr curNode) -> VisitAction { + if (curNode->astKind != AST::ASTKind::TRY_EXPR) { + return AST::VisitAction::WALK_CHILDREN; + } + auto tryExpr = RawStaticCast(curNode.get()); + for (auto& node : tryExpr->tryBlock->body) { + if (node->astKind != AST::ASTKind::VAR_DECL) { + continue; + } + auto varDecl = RawStaticCast(node.get()); + if (varDecl->identifier == EXPR_RESULT_NAME) { + m_tryExpr_result = tryExpr; + m_expr_result = varDecl; + return VisitAction::SKIP_CHILDREN; + } + } + return VisitAction::WALK_CHILDREN; + }; + AST::Walker lldbFindWalker(exprPkg, findExprResult); + lldbFindWalker.Walk(); + std::vector names; + auto collectIdentifier = [&names](Ptr curNode) -> VisitAction { + if (curNode->astKind == AST::ASTKind::REF_TYPE) { + auto re = RawStaticCast(curNode.get()); + auto find = std::find_if(names.begin(), names.end(), + [&re](auto& name) { return name == re->ref.identifier; }); + if (find == names.end()) { + names.emplace_back(re->ref.identifier); + } + return VisitAction::SKIP_CHILDREN; + } + if (curNode->astKind == AST::ASTKind::REF_EXPR) { + auto re = RawStaticCast(curNode.get()); + if (re->ref.identifier == "this") { + return VisitAction::SKIP_CHILDREN; + } + auto find = std::find_if(names.begin(), names.end(), + [&re](auto& name) { return name == re->ref.identifier; }); + if (find == names.end()) { + names.emplace_back(re->ref.identifier); + } + return VisitAction::WALK_CHILDREN; + } + return VisitAction::WALK_CHILDREN; + }; + AST::Walker lldbWalker(m_expr_result, collectIdentifier); + lldbWalker.Walk(); + Log *log = GetLog(LLDBLog::Expressions); + if (log) { + std::string ids = ""; + for (auto tmpId:names) { + ids = ids + " ; " + tmpId; + } + LLDB_LOGF(log, "All collected identifiers are as follows: %s! \n", ids.c_str()); + } + return names; +} + +bool HasImportSameIdentifier(OwnedPtr& file, + const std::vector& pkgPath, const std::string& identifier) { + for (auto& im : file->imports) { + if (im->content.prefixPaths == pkgPath && im->content.identifier == identifier) { + return true; + } + } + return false; +} + +bool HasImportSamePkg(OwnedPtr& file, const std::vector& pkgPath) { + for (auto& im : file->imports) { + if (im->content.prefixPaths == pkgPath && im->content.kind == ImportKind::IMPORT_ALL) { + return true; + } + } + return false; +} + +void CangjieCompilerInstance::AddImportNodeFromFakePkg( + OwnedPtr& file, OwnedPtr& fPkg) +{ + auto& fakeFile = fPkg->files[0]; + m_package_names.emplace_back(fPkg->fullPackageName); + for (auto& im : fakeFile->imports) { + if (HasImportSamePkg(file, im->content.prefixPaths)) { + continue; + } + auto impkg = Utils::JoinStrings(im->content.prefixPaths, "."); + auto find = std::find_if(m_package_names.begin(), m_package_names.end(), + [&impkg](auto& pkg) { return pkg == impkg; }); + if (find == m_package_names.end()) { + m_package_names.emplace_back(impkg); + } + auto import = MakeOwned(); + import->content.prefixPaths = im->content.prefixPaths; + const size_t prefixLen = import->content.prefixPaths.size(); + import->content.prefixPoses.resize(prefixLen); + import->content.prefixDotPoses.resize(prefixLen); + import->content.identifier = "*"; + import->content.kind = ImportKind::IMPORT_ALL; + import->EnableAttr(Attribute::IMPLICIT_ADD, Attribute::COMPILER_ADD); + import->curFile = file.get(); + file->imports.emplace_back(std::move(import)); + } +} + +void CangjieCompilerInstance::AddImportSpecToLLDBExprPackage() { + auto tempPkg = this->GetSourcePackages()[0]; + for (auto& fPkg : this->m_fake_packages) { + auto pkgPath = Utils::SplitQualifiedName(fPkg->fullPackageName); + auto pkgName = fPkg->fullPackageName; + for (auto &file : tempPkg->files) { + auto collectImport = [&file, &pkgPath](Ptr curNode) -> VisitAction { + // locals: vardecl or funcdecl + if (pkgPath.back() == LOCAL_PACKAGE_NAME && + (curNode->astKind == ASTKind::VAR_DECL || curNode->astKind == ASTKind::FUNC_DECL)) { + auto localdecl = RawStaticCast(curNode.get()); + if (HasImportSameIdentifier(file, pkgPath, localdecl->identifier)) { + return VisitAction::SKIP_CHILDREN; + } + // import __lldb_locals.a as __lldb_locals_a + auto importPackage = MakeOwned(); + importPackage->content.prefixPaths = pkgPath; + importPackage->content.prefixPoses.resize(pkgPath.size()); + importPackage->content.prefixDotPoses.resize(pkgPath.size()); + importPackage->content.identifier = localdecl->identifier.Val(); + importPackage->content.kind = ImportKind::IMPORT_ALIAS; + if (curNode->astKind == ASTKind::VAR_DECL) { + importPackage->content.aliasName = LOCAL_PACKAGE_NAME + "_" + localdecl->identifier; + } else { + importPackage->content.aliasName = localdecl->identifier.Val(); + } + importPackage->curFile = file.get(); + importPackage->EnableAttr(Attribute::IMPLICIT_ADD, Attribute::COMPILER_ADD); + file->imports.emplace_back(std::move(importPackage)); + return VisitAction::SKIP_CHILDREN; + } + // globals: vardecl or funcdecl or classdecl + if (curNode->IsDecl()) { + if (HasImportSamePkg(file, pkgPath)) { + return VisitAction::SKIP_CHILDREN; + } + // import pkg.* + auto importPackage = MakeOwned(); + importPackage->content.prefixPaths = pkgPath; + importPackage->content.prefixPoses.resize(pkgPath.size()); + importPackage->content.prefixDotPoses.resize(pkgPath.size()); + importPackage->content.identifier = "*"; + importPackage->content.kind = ImportKind::IMPORT_ALL; + importPackage->curFile = file.get(); + importPackage->EnableAttr(Attribute::IMPLICIT_ADD, Attribute::COMPILER_ADD); + file->imports.emplace_back(std::move(importPackage)); + return VisitAction::SKIP_CHILDREN; + } + return VisitAction::WALK_CHILDREN; + }; + AST::Walker walkFakePkg(fPkg.get(), collectImport); + walkFakePkg.Walk(); + // Add import nodes such as std/collection. + AddImportNodeFromFakePkg(file, fPkg); + } + } +} + +OwnedPtr CangjieCompilerInstance::CreatePrimitiveInitializer(Ptr& ty) +{ + switch (ty->kind) { + case Cangjie::AST::TypeKind::TYPE_INT8: + case Cangjie::AST::TypeKind::TYPE_INT16: + case Cangjie::AST::TypeKind::TYPE_INT32: + case Cangjie::AST::TypeKind::TYPE_INT64: + case Cangjie::AST::TypeKind::TYPE_UINT8: + case Cangjie::AST::TypeKind::TYPE_UINT16: + case Cangjie::AST::TypeKind::TYPE_UINT32: + case Cangjie::AST::TypeKind::TYPE_UINT64: + case Cangjie::AST::TypeKind::TYPE_INT_NATIVE: + case Cangjie::AST::TypeKind::TYPE_UINT_NATIVE: + return Cangjie::AST::CreateLitConstExpr(Cangjie::AST::LitConstKind::INTEGER, "0", ty); + case Cangjie::AST::TypeKind::TYPE_FLOAT16: + case Cangjie::AST::TypeKind::TYPE_FLOAT32: + case Cangjie::AST::TypeKind::TYPE_FLOAT64: + return Cangjie::AST::CreateLitConstExpr(Cangjie::AST::LitConstKind::FLOAT, "0", ty); + case Cangjie::AST::TypeKind::TYPE_RUNE: + return Cangjie::AST::CreateLitConstExpr(Cangjie::AST::LitConstKind::RUNE, "a", ty); + case Cangjie::AST::TypeKind::TYPE_BOOLEAN: + return Cangjie::AST::CreateLitConstExpr(Cangjie::AST::LitConstKind::BOOL, "true", ty); + case Cangjie::AST::TypeKind::TYPE_UNIT: + return Cangjie::AST::CreateLitConstExpr(Cangjie::AST::LitConstKind::UNIT, "()", ty); + default: + return nullptr; + } +} + +OwnedPtr CangjieCompilerInstance::CreateEnumInitializer(Ptr& ty) +{ + auto enumTy = RawStaticCast(ty); + if (!enumTy) { + return nullptr; + } + // Choose the first one to init. + auto edecl = enumTy->decl; + if (edecl->constructors.empty()) { + return nullptr; + } + if (enumTy->IsCoreOptionType()) { + // For Option type, initial is memberAccess, Option.None + auto noneCtor = edecl->constructors[1].get(); + if (noneCtor->astKind == AST::ASTKind::VAR_DECL) { + auto init = RawStaticCast(noneCtor); + auto memberAccess = AST::CreateMemberAccess(AST::CreateRefExpr(*edecl), init->identifier); + return memberAccess; + } + } + auto firstCtor = edecl->constructors[0].get(); + if (firstCtor->astKind == AST::ASTKind::VAR_DECL) { + auto init = RawStaticCast(firstCtor); + auto refExpr = AST::CreateRefExpr(*init); + return refExpr; + } + return nullptr; +} + +Ptr CangjieCompilerInstance::GetInitFuncFromClassOrStruct( + std::vector>& decls, std::vector>& params) +{ + for (auto& decl : decls) { + if (decl->TestAttr(Attribute::CONSTRUCTOR)) { + auto initFunc = RawStaticCast(decl.get()); + for (auto& param : initFunc->funcBody->paramLists[0]->params) { + if (!param->type) { + continue; + } + CheckTypeAndAddTy(param->type); + param->ty = param->type->ty; + params.emplace_back(param->ty); + } + return initFunc; + } + } + return nullptr; +} + +OwnedPtr CangjieCompilerInstance::CreateClassInitializer(Ptr& ty) +{ + auto classTy = RawStaticCast(ty); + if (!classTy || !classTy->decl) { + return nullptr; + } + auto cd = classTy->decl; + std::vector> params; + auto initFunc = GetInitFuncFromClassOrStruct(cd->body->decls, params); + if (!initFunc) { + return nullptr; + } + std::vector> args; + for (auto& pty : params) { + args.emplace_back(CreateFuncArg(CreateInitializer(pty))); + } + auto refExpr = CreateRefExpr(*initFunc); + refExpr->ref.identifier = cd->identifier; + refExpr->ty = typeManager->GetFunctionTy(params, classTy); + auto call = AST::CreateCallExpr(std::move(refExpr), std::move(args), initFunc, classTy); + call->callKind = CallKind::CALL_OBJECT_CREATION; + return call; +} + +OwnedPtr CangjieCompilerInstance::CreateStructInitializer(Ptr& ty) +{ + auto structTy = RawStaticCast(ty); + if (!structTy || !structTy->decl) { + return nullptr; + } + auto sd = structTy->decl; + std::vector> sparams; + auto initFunc = GetInitFuncFromClassOrStruct(sd->body->decls, sparams); + if (!initFunc) { + return nullptr; + } + std::vector> args; + for (auto& pty : sparams) { + args.emplace_back(CreateFuncArg(CreateInitializer(pty))); + } + auto refExpr = CreateRefExpr(*initFunc); + refExpr->ref.identifier = sd->identifier; + refExpr->ty = typeManager->GetFunctionTy(sparams, structTy); + auto call = AST::CreateCallExpr(std::move(refExpr), std::move(args), initFunc, structTy); + call->callKind = CallKind::CALL_STRUCT_CREATION; + return call; +} + +OwnedPtr CangjieCompilerInstance::CreateFunctionInitializer(Ptr& ty) +{ + // var result: (Int8) -> Int8 = {a: Int8 => 0 } + auto functy = RawStaticCast(ty); + if (!functy) { + return nullptr; + } + auto retTy = functy->retTy; + auto funcBody = MakeOwned(); + auto paramList = MakeOwned(); + size_t i = 0; + for (auto& pty : functy->paramTys) { + auto paramName = "a" + std::to_string(i); + paramList->params.emplace_back(AST::CreateFuncParam(paramName, nullptr, nullptr, pty)); + i++; + } + funcBody->paramLists.emplace_back(std::move(paramList)); + OwnedPtr block = MakeOwned(); + block->body.emplace_back(CreateInitializer(retTy)); + funcBody->body = std::move(block); + funcBody->ty = ty; + return AST::CreateLambdaExpr(std::move(funcBody)); +} + +OwnedPtr CangjieCompilerInstance::CreateRangeInitializer(Ptr& ty) +{ + if (ty->typeArgs.empty()) { + return nullptr; + } + auto rangeExpr = MakeOwned(); + rangeExpr->startExpr = CreateInitializer(ty->typeArgs[0]); + rangeExpr->stopExpr = CreateInitializer(ty->typeArgs[0]); + auto int64ty = typeManager->GetPrimitiveTy(AST::TypeKind::TYPE_INT64); + rangeExpr->stepExpr = AST::CreateLitConstExpr(Cangjie::AST::LitConstKind::INTEGER, "1", int64ty); + rangeExpr->ty = ty; + rangeExpr->decl = importManager.GetCoreDecl(RANGE_NAME); + return rangeExpr; +} + +OwnedPtr CangjieCompilerInstance::CreateInitializer(Ptr& ty) +{ + if (!ty) { + return nullptr; + } + if (ty->IsPrimitive()) { + return CreatePrimitiveInitializer(ty); + } + if (ty->IsFunc()) { + return CreateFunctionInitializer(ty); + } + if (ty->IsClass()) { + return CreateClassInitializer(ty); + } + if (ty->IsEnum()) { + return CreateEnumInitializer(ty); + } + if (ty->IsString()) { + return AST::CreateLitConstExpr(Cangjie::AST::LitConstKind::STRING, "", ty); + } + if (ty->IsStructArray()) { + std::vector> elements; + elements.emplace_back(CreateInitializer(ty->typeArgs[0])); + return AST::CreateArrayLit(std::move(elements), ty); + } + if (ty->IsTuple()) { + std::vector> elements; + for (auto& subTy : ty->typeArgs) { + elements.emplace_back(CreateInitializer(subTy)); + } + return AST::CreateTupleLit(std::move(elements), ty); + } + if (ty->kind == Cangjie::AST::TypeKind::TYPE_VARRAY) { + auto ret = MakeOwned(); + auto varrayTy = RawStaticCast(ty); + for (int64_t i = 0; i < varrayTy->size; i++) { + ret->children.emplace_back(CreateInitializer(ty->typeArgs[0])); + } + ret->ty = ty; + return ret; + } + if (ty->IsRange()) { + return CreateRangeInitializer(ty); + } + if (ty->IsStruct()) { + return CreateStructInitializer(ty); + } + return nullptr; +} + +Ptr CangjieCompilerInstance::AddObjectToFunctionTy(Ptr& fTy, Ptr& objectTy) +{ + auto funcTy = RawStaticCast(fTy); + std::vector> ptys{objectTy}; + for (auto &pty : funcTy->paramTys) { + ptys.emplace_back(pty); + } + return typeManager->GetFunctionTy(ptys, funcTy->retTy); +} + +void CangjieCompilerInstance::AddCapturedvarsToCallExprOfLocalFunc() +{ + auto modifyCall = [this](Ptr curNode) -> AST::VisitAction { + if (curNode->astKind != AST::ASTKind::CALL_EXPR) { + return AST::VisitAction::WALK_CHILDREN; + } + auto ce = RawStaticCast(curNode.get()); + if (ce->baseFunc && ce->baseFunc->astKind == AST::ASTKind::REF_EXPR) { + auto re = RawStaticCast(ce->baseFunc.get()); + if (re->ref.target && re->ref.target->fullPackageName == LOCAL_PACKAGE_NAME && + re->ref.target->astKind == AST::ASTKind::FUNC_DECL) { + auto fd = RawStaticCast(re->ref.target); + // Add param "$CapturedVars: Object" to local funcdecl. + auto paramList = fd->funcBody->paramLists[0].get(); + OwnedPtr param = MakeOwned(); + param->identifier = LOCAL_FUNC_CAPTUREDVAR; + param->EnableAttr(Attribute::COMPILER_ADD); + + // modify: foo() -> foo(foo$Object) + auto refExpr = MakeOwned(); + AddCurFile(*refExpr, ce->curFile); + refExpr->ref.identifier = re->ref.identifier + "$Object"; + for (auto& vd : m_captured_vars) { + if (vd->identifier == refExpr->ref.identifier) { + refExpr->ref.target = vd; + refExpr->ty = vd->ty; + param->ty = vd->ty; + fd->ty = AddObjectToFunctionTy(fd->ty, vd->ty); + re->ty = AddObjectToFunctionTy(re->ty, vd->ty); + break; + } + } + paramList->params.insert(paramList->params.begin(), std::move(param)); + refExpr->ref.identifier.SetPos(refExpr->begin, + refExpr->begin + refExpr->ref.identifier.Val().size()); + ce->args.insert(ce->args.begin(), CreateFuncArg(std::move(refExpr))); + ce->desugarArgs.reset(); + return AST::VisitAction::SKIP_CHILDREN; + } + } + return AST::VisitAction::WALK_CHILDREN; + }; + + AST::Walker walker(m_expr_result, modifyCall); + walker.Walk(); +} + +void CangjieCompilerInstance::PerformAfterSemaForCjdb() +{ + AddCapturedvarsToCallExprOfLocalFunc(); + Log *log = GetLog(LLDBLog::Expressions); + if (!m_expr_result || !m_expr_result->ty) { + if (log) { + LLDB_LOGF(log, "%s's type not found\n", EXPR_RESULT_NAME.c_str()); + } + return; + } + // Get the mangle value of the result's type of an expression evaluation after the type is semantically inferred. + m_result_type_name = BaseMangler().MangleType(*m_expr_result->ty); + if (log) { + LLDB_LOGF(log, "result type name is:%s -> %s\n", m_expr_result->ty->String().c_str(), m_result_type_name.c_str()); + std::string str = GetStringOfASTNode(this->GetSourcePackages()[0]); + LLDB_LOGF(log, "-------- Package Node after sema -------- \n%s", str.c_str()); + } +} + +bool CangjieCompilerInstance::IsUserLookupFunction() { + Log *log = GetLog(LLDBLog::Expressions); + CJC_ASSERT(m_expr_result->initializer.get()->astKind == AST::ASTKind::BLOCK); + if (!m_expr_result->ty->IsFunc()) { + return false; + } + auto block = RawStaticCast(m_expr_result->initializer.get()); + if (block->body.back()->astKind == AST::ASTKind::REF_EXPR) { + auto refExpr = RawStaticCast(block->body.back().get()); + if (refExpr->ref.target && refExpr->ref.target->astKind == AST::ASTKind::FUNC_DECL) { + // CC is closed only for global function. For member function, CC is needed. + LLDB_LOGF(log, "user is searching global func name now! \n"); + return true; + } + } else if (block->body.back()->astKind == AST::ASTKind::MEMBER_ACCESS) { + auto member = RawStaticCast(block->body.back().get()); + if (member->target && member->target->astKind == AST::ASTKind::FUNC_DECL) { + LLDB_LOGF(log, "user is searching member func name now!\n"); + return true; + } + } + return false; +} diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieCompilerInstance.h b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieCompilerInstance.h new file mode 100644 index 000000000..6595dcf91 --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieCompilerInstance.h @@ -0,0 +1,75 @@ +//===-- CangjieCompilerInstance.h -----------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_CANGJIECOMPILERINSTANCE_H +#define LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_CANGJIECOMPILERINSTANCE_H + +#include "CangjieExpressionParser.h" +#include "cangjie/FrontendTool/DefaultCompilerInstance.h" + +namespace lldb_private { +namespace CangjieExpr { +using namespace Cangjie; + +class CangjieCompilerInstance : public Cangjie::DefaultCompilerInstance { +public: + CangjieCompilerInstance(Cangjie::CompilerInvocation &invocation, + Cangjie::DiagnosticEngine &diag) + : DefaultCompilerInstance(invocation, diag) {} + bool PerformCodeGen() override; + std::unique_ptr ReleaseModule(); + + void PerformSuperExpression(bool inClassContext, bool isConstructor, bool isStaticMethod); + bool PerformImportPackageForCjdb(); + void PerformAfterSemaForCjdb(); + bool IsUserLookupFunction(); + std::vector WalkAndCollectIdentifiers(); + std::string GetStringOfASTNode(const Ptr& node); + std::vector> m_fake_packages; + std::string m_result_type_name; + Ptr m_expr_result = nullptr; +private: + std::unique_ptr m_module_up; + Ptr m_tryExpr_result = nullptr; + std::vector m_package_names; + std::vector> m_captured_vars; + + void AddImportNodeFromFakePkg(OwnedPtr& file, OwnedPtr& fPkg); + void AddImportSpecToLLDBExprPackage(); + Ptr AddObjectToFunctionTy(Ptr& fTy, Ptr& objectTy); + void AddCapturedvarsToCallExprOfLocalFunc(); + std::vector> CreateFuncArgsFromCapturedVars(std::string ident); + void AddCapturedVarsToTryExpr(std::vector>& localDecls, AST::TryExpr& tryExpr); + void AddLocalsToExprResult(std::vector>& localDecls); + void AddTyOfRefType(AST::RefType& rt); + void CheckTypeAndAddTy(OwnedPtr& type); + Ptr GetInstantiatedTy(AST::ClassTy& cTy, Ptr& paramTy); + OwnedPtr CreateSuperCall(AST::ClassDecl& cd); + void CreateInitFunc(AST::Decl& decl); + void CreateDefaultCtor(AST::Decl& decl); + void UpdateDeclTyByGeneric(Ptr decl); + Ptr GetTyFromASTType(Decl& decl, const std::vector>& typeArgs); + void CreateFuncdeclTy(AST::FuncDecl& fd); + void CreateTyAndDefaultCtor(AST::Decl& decl, const std::vector>& typeArgs); + + Ptr GetInitFuncFromClassOrStruct( + std::vector>& decls, std::vector>& params); + OwnedPtr CreatePrimitiveInitializer(Ptr& ty); + OwnedPtr CreateFunctionInitializer(Ptr& ty); + OwnedPtr CreateStructInitializer(Ptr& ty); + OwnedPtr CreateClassInitializer(Ptr& ty); + OwnedPtr CreateEnumInitializer(Ptr& ty); + OwnedPtr CreateRangeInitializer(Ptr& ty); + OwnedPtr CreateInitializer(Ptr& ty); +}; + +} // namespace CangjieExpr +} // namespace lldb_private +#endif // LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_CANGJIECOMPILERINSTANCE_H diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieDeclMap.cpp b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieDeclMap.cpp new file mode 100644 index 000000000..fb79a00d5 --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieDeclMap.cpp @@ -0,0 +1,2149 @@ +//===-- CangjieDeclMap.cpp ------------------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#include "CangjieDeclMap.h" +#include "cangjie/AST/Create.h" +#include "cangjie/AST/Utils.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "clang/Sema/Sema.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "CangjieDemangle.h" +using namespace lldb_private; +using namespace Cangjie; +using namespace Cangjie::AST; + +bool CangjieDeclMap::IsInterfaceType(CompilerType& type) { + if (type.GetTypeClass() == lldb::eTypeClassClass) { + std::string typeName = type.GetTypeName().GetCString(); + if (typeName.find(INTERFACE_PREFIX_NAME) != std::string::npos) { + return true; + } + } + return false; +} + +void CangjieDeclMap::CollectLocalVariableAddress(const std::string& sname, lldb::VariableSP& var) { + auto frame = m_exe_ctx.GetFramePtr(); + auto target = m_exe_ctx.GetTargetSP(); + if (!var || !frame || !target) { + return; + } + auto valobj = frame->GetValueObjectForFrameVariable(var, lldb::DynamicValueType::eNoDynamicValues); + AddressType address_type = eAddressTypeInvalid; + const bool scalar_is_load_address = false; + lldb::addr_t addr = valobj->GetAddressOf(scalar_is_load_address, &address_type); + m_captured_var_addrs[sname] = (uint64_t)addr; +} + +void CangjieDeclMap::CacheParsedVars(ConstString name, lldb::VariableSP& var, VariableKind kind) { + CompilerType type = var->GetType()->GetFullCompilerType(); + if (m_parsed_vars.find(name) != m_parsed_vars.end()) { + if (kind == VariableKind::LocalVariable) { + m_parsed_vars[name] = var; + } + } else { + if (kind != VariableKind::LocalVariable && IsInterfaceType(type)) { + m_parsed_vars.insert({var->GetName(), var}); + if (kind == VariableKind::StaticVariable) { + m_parsed_vars.insert({name, var}); + } + } else if (kind != VariableKind::LocalVariable) { + m_parsed_vars.insert({var->GetName(), var}); + } + m_parsed_vars.insert({name, var}); + } +} + +lldb::VariableSP CangjieDeclMap::GetVariableByName(const std::string& sname, VariableKind kind) { + ConstString name(sname.c_str()); + lldb::VariableSP var; + if (kind == VariableKind::LocalVariable) { + var = m_local_variables->FindVariable(name, true); + CollectLocalVariableAddress(sname, var); + } else if (kind == VariableKind::StaticVariable) { + var = m_static_variables->FindVariable(name, true); + } else { + var = m_global_variables->FindVariable(name, true); + } + return var; +} + +CompilerType CangjieDeclMap::GetVariableType(lldb::VariableSP var) { + CompilerType type = var->GetType()->GetFullCompilerType(); + bool isLet = type.IsConst(); + ReplaceTypeDefWithInterfaceType(type); + std::string type_name = type.GetTypeName().GetCString(); + if (CangjieASTBuiler::IsFunctionType(type.GetTypeName())) { + type_name = GetSubNameWithoutPkgname(type_name, m_current_pkgname); + } + // Variables of type interface will have a dynamic type. For example, var a:interface1 = classA() + if (IsInterfaceType(type)) { + auto frame = m_exe_ctx.GetFramePtr(); + if (frame != nullptr) { + auto valobj = frame->GetValueObjectForFrameVariable(var, lldb::DynamicValueType::eNoDynamicValues); + dynamic_ype = valobj->GetDynamicType(); + } + } + if (ConstString(type_name).GetStringRef().contains(GENERIC_TYPE_PREFIX_NAME)) { + auto frame = m_exe_ctx.GetFramePtr(); + if (frame != nullptr) { + auto valobj = frame->GetValueObjectForFrameVariable(var, lldb::DynamicValueType::eNoDynamicValues); + auto dynamic_Type = valobj->GetDynamicType(); + type = dynamic_Type; + type_name = type.GetTypeName().GetCString(); + if (CangjieASTBuiler::IsFunctionType(type.GetTypeName())) { + type_name = GetSubNameWithoutPkgname(type_name, m_current_pkgname); + } + } + } + type_name = DeletePrefixOfType(type_name); + if (type.GetTypeClass() == lldb::eTypeClassPointer && + (type_name.find("std.core::Option") != std::string::npos)) { + // Option is now implemented as a reference type. + type_name = type.GetPointeeType().GetTypeName().AsCString(); + } + if (type.GetTypeClass() == lldb::eTypeClassPointer && + type.GetTypeName().GetStringRef().contains(E2_PREFIX_NAME_OPTION_LIKE)) { + type_name = DeletePrefixOfType(type.GetPointeeType().GetTypeName().AsCString()); + } + m_parsed_types.insert({type_name, type}); + return type; +} + +void CangjieDeclMap::CollectVarDecl(const std::string& sname, CompilerType& type, VariableKind kind, + std::vector& m_translate_decls) { + LookUpMemberVariable(type, m_translate_decls); + lldb::VariableSP var = GetVariableByName(sname, kind); + ConstString name(sname.c_str()); + if (!var) { + return; + } + std::string mangledName = kind == VariableKind::StaticVariable ? var->GetMangledName().AsCString() : ""; + bool isLet = var->GetType()->GetFullCompilerType().IsConst(); + if (type.IsValid()) { + CompilerTypeInfo tempInfo(type, 0, mangledName); + if (var->GetScope() == lldb::eValueTypeVariableArgument) { + isLet = true; + } + bool find_global = kind != VariableKind::LocalVariable; + tempInfo.SetDeclInfo(DeclKind::VarDecl, name.AsCString(), find_global, "", isLet); + m_translate_decls.emplace_back(tempInfo); + } +} + +CompilerType CangjieDeclMap::LookUpVariable(const std::string& sname, VariableKind kind) { + lldb::VariableSP var = GetVariableByName(sname, kind); + if (!var) { + return CompilerType(); + } + CompilerType type = GetVariableType(var); + ConstString name(sname.c_str()); + CacheParsedVars(name, var, kind); + return type; +} + +void CangjieDeclMap::LookUpMemberVariable(CompilerType& type, std::vector& m_translate_decls) { + std::string refTypeName = type.GetTypeName().AsCString(); + // Only collect generic instantiation types of user-defined types. + if (!IsGenericInstanced(refTypeName)) { + return; + } + if (type.GetTypeClass() != lldb::eTypeClassStruct && type.GetTypeClass() != lldb::eTypeClassClass + && type.GetTypeClass() != lldb::eTypeClassPointer) { + return; + } + Log *log = GetLog(LLDBLog::Expressions); + // If the generic type is a custom type, collect it. + auto instantiatedNames = builder.GetInstantiatedParamDeclName(refTypeName); + for (auto& ele:instantiatedNames) { + LookUpTypeByName(ele, m_translate_decls); + auto instantiateName = GetSubNameWithoutPkgname(ele, m_current_pkgname); + if (instantiateName.empty()) { + instantiateName = ele; + } + LookUpTypeByName(instantiateName, m_translate_decls); + } + // for generic type, for example: + // class C1 { var c1:T } + // struct S3 { var s3:T} + // var a1 = C1>(S3(8)) + // expr a1.c1 , S3 needs to be collected here. + for (uint32_t i = 0; i < type.GetNumFields(); i++) { + std::string member_name; + auto child_type = type.GetFieldAtIndex(i, member_name, nullptr, nullptr, nullptr); + if (member_name == "$ti" || member_name == "$ti*" || !child_type.IsValid()) { + continue; + } + if (child_type.GetTypeClass() != lldb::eTypeClassStruct && type.GetTypeClass() != lldb::eTypeClassClass) { + continue; + } + auto tempName = child_type.GetTypeName().AsCString(); + if (m_parsed_types.find(tempName) != m_parsed_types.end()) { + continue; + } + m_parsed_types.insert({tempName, child_type}); + CompilerTypeInfo tempInfo(child_type, 0, ""); + tempInfo.SetDeclInfo(DeclKind::UserDefinedDecl, tempName, true); + m_translate_decls.emplace_back(tempInfo); + if (log) { + LLDB_LOGF(log, "[Generic]: add instantiated type [%s] for [%s:%s]. \n", tempName, + ConstString(refTypeName).AsCString(), ConstString(member_name).AsCString()); + } + } +} + +CompilerType CangjieDeclMap::LookUpGlobalVariable(lldb::ModuleSP module, ConstString name, + const CompilerDeclContext &namespace_decl) { + auto target = m_exe_ctx.GetTargetSP(); + if (!target) { + return CompilerType(); + } + + VariableList vars; + if (module && namespace_decl) { + module->FindGlobalVariables(name, namespace_decl, -1, vars); + } else { + target->GetImages().FindGlobalVariables(name, -1, vars); + } + + if (vars.GetSize() == 0) { + return CompilerType(); + } + + return vars.GetVariableAtIndex(0)->GetType()->GetFullCompilerType(); +} + +CompilerType CangjieDeclMap::LookUptype(lldb::ModuleSP module, + ConstString name, const CompilerDeclContext &namespace_decl) { + TypeList types; + const bool exact_match = true; + llvm::DenseSet searched_symbol_files; + if (module && namespace_decl) { + module->FindTypesInNamespace(name, namespace_decl, 1, types); + } else { + auto target = m_exe_ctx.GetTargetSP(); + target->GetImages().FindTypes( + module.get(), name, exact_match, 1, searched_symbol_files, types); + } + + size_t num_types = types.GetSize(); + for (size_t ti = 0; ti < num_types; ++ti) { + lldb::TypeSP type_sp = types.GetTypeAtIndex(ti); + CompilerType full_type = type_sp->GetFullCompilerType(); + if (full_type.IsValid()) { + return full_type; + } + continue; + } + + return CompilerType(); +} + +bool CangjieDeclMap::LookUpPackage(ConstString name) { + auto target = m_exe_ctx.GetTargetSP(); + NamespaceMapSP parsed_package = std::make_shared(); + for (lldb::ModuleSP image : target->GetImages().Modules()) { + if (!image) { + continue; + } + + SymbolFile *symbol_file = image->GetSymbolFile(); + if (!symbol_file) { + continue; + } + CompilerDeclContext namespace_decl; + CompilerDeclContext found_namespace_decl = symbol_file->FindNamespace(name, namespace_decl); + if (found_namespace_decl) { + parsed_package->push_back( + std::pair(image, found_namespace_decl)); + } + } + + if (!parsed_package->empty()) { + m_parsed_packages.insert(std::pair(name, parsed_package)); + return true; + } + + return false; +} + +std::vector CangjieDeclMap::LookUpFunction(lldb::ModuleSP module, + ConstString name, const CompilerDeclContext &namespace_decl) { + std::vector result; + SymbolContextList sc_list; + auto target = m_exe_ctx.GetTargetPtr(); + if (namespace_decl && module) { + ModuleFunctionSearchOptions function_options; + function_options.include_inlines = false; + function_options.include_symbols = false; + + module->FindFunctions(name, namespace_decl, lldb::eFunctionNameTypeBase, + function_options, sc_list); + } else if (target && !namespace_decl) { + ModuleFunctionSearchOptions function_options; + function_options.include_inlines = false; + function_options.include_symbols = true; + target->GetImages().FindFunctions( + name, lldb::eFunctionNameTypeFull | lldb::eFunctionNameTypeBase, function_options, + sc_list); + } + + if (!sc_list.GetSize()) { + return result; + } + + Log *log = GetLog(LLDBLog::Expressions); + auto num_indices = sc_list.GetSize(); + for (uint32_t index = 0; index < num_indices; ++index) { + SymbolContext sym_ctx; + sc_list.GetContextAtIndex(index, sym_ctx); + if (sym_ctx.function) { + CompilerDeclContext decl_ctx = sym_ctx.function->GetDeclContext(); + if (!decl_ctx) { + continue; + } + // Filter out class/instance methods. + if (decl_ctx.IsClassMethod(nullptr, nullptr, nullptr)) { + continue; + } + std::string funcName = sym_ctx.function->GetNameNoArguments().GetCString(); + auto type = sym_ctx.function->GetCompilerType(); + CompilerTypeInfo tempInfo(type, sym_ctx.GetFunctionStartLineEntry().line, + sym_ctx.GetFunctionName(Mangled::NamePreference::ePreferMangled).AsCString()); + tempInfo.SetDeclInfo(DeclKind::FuncDecl, funcName, true); + if (log) { + LLDB_LOGF(log, "add func [%s] 's type %s to m_parsed_functions. \n", + ConstString(name).AsCString(), type.GetTypeName().AsCString()); + } + result.emplace_back(tempInfo); + } + } + + return result; +} + +static bool IsLocalFunction(std::string& localScope, std::string& funcScope) { + if (localScope == funcScope) { + return true; + } + auto pos = localScope.rfind("::"); + if (pos != std::string::npos) { + auto subLocal = localScope.substr(0, pos); + if (subLocal.rfind("::") == std::string::npos) { + // If there is no "::" in subLocal, then subLocal is actually globalScope. + return false; + } + return IsLocalFunction(subLocal, funcScope); + } + return false; +} + +std::string CangjieDeclMap::GetFuncNameFromComplierType(const CompilerType& funcType) { + // For example, a func name can be (default.(Int64, Int64) -> Int64) + std::string funcname = "("; + bool firstParam = true; + auto retTypeName = GetTypeNameWithoutPrefix(funcType.GetFunctionReturnType().GetTypeName(), m_current_pkgname); + for (int i = 0; i < funcType.GetFunctionArgumentCount(); i++) { + std::string tempargs = funcType.GetFunctionArgumentAtIndex(i).GetTypeName().AsCString(); + if (firstParam) { + funcname = funcname + tempargs; + firstParam = false; + } else { + funcname = funcname + "," + tempargs; + } + } + funcname = funcname + ")->" + retTypeName; + return funcname; +} + +bool CheckNestedGenericFunction(lldb_private::ConstString funcname) { + int count = 0; + size_t pos = 0; + auto str = funcname.GetStringRef(); + llvm::StringRef substr = " 1; +} + +std::vector CangjieDeclMap::LookUpFunction(std::string name, bool find_global) { + std::vector result; + auto target = m_exe_ctx.GetTargetPtr(); + auto frame = m_exe_ctx.GetFramePtr(); + if (!target || !frame) { + return result; + } + SymbolContextList sc_list; + SymbolContextList sc_generic_list; + ModuleFunctionSearchOptions function_options; + function_options.include_inlines = false; + function_options.include_symbols = true; + + target->GetImages().FindFunctions(ConstString(name), + lldb::eFunctionNameTypeFull | lldb::eFunctionNameTypeBase, function_options, sc_list); + + std::string re_cstr = std::string("^") + name + std::string("<.+>$"); + target->GetImages().FindFunctions(RegularExpression(llvm::StringRef(re_cstr)), function_options, sc_generic_list); + sc_list.Append(sc_generic_list); + if (sc_list.IsEmpty()) { + return result; + } + std::string globalScope = m_current_pkgname; + std::string localScope; + // Collect some info about our frame's context. + auto frame_sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | lldb::eSymbolContextBlock); + if (frame_sym_ctx.function) { + localScope = frame_sym_ctx.function->GetNameNoArguments().GetCString(); + } + Log *log = GetLog(LLDBLog::Expressions); + auto num_indices = sc_list.GetSize(); + for (uint32_t index = 0; index < num_indices; ++index) { + SymbolContext sym_ctx; + sc_list.GetContextAtIndex(index, sym_ctx); + if (!sym_ctx.function) { + continue; + } + if (CheckNestedGenericFunction(sym_ctx.function->GetNameNoArguments())) { + return result; + } + std::string funcNameWithPkg = sym_ctx.function->GetNameNoArguments().GetCString(); + if (funcNameWithPkg.find(m_current_pkgname) == std::string::npos) { + continue; + } + CompilerDeclContext decl_ctx = sym_ctx.function->GetDeclContext(); + if (!decl_ctx || decl_ctx.IsClassMethod(nullptr, nullptr, nullptr)) { + // Filter out class/instance methods. + continue; + } + std::string funcName = m_current_pkgname + PACKAGE_SUFFIX + decl_ctx.GetName().AsCString(); + std::string mangleName = sym_ctx.function->GetNameNoArguments().GetCString(); + auto funcScope = mangleName.substr(0, mangleName.rfind(PACKAGE_SUFFIX)); + if (IsLocalFunction(localScope, funcScope)) { + funcName = decl_ctx.GetName().AsCString(); + } + std::string identifier = GetGenericDeclName(funcName); + auto funcType = sym_ctx.function->GetCompilerType(); + std::string type_name = GetFuncNameFromComplierType(funcType); + if ((find_global && globalScope == funcScope) || + (!find_global && IsLocalFunction(localScope, funcScope))) { + if (log) { + LLDB_LOGF(log, "add func [%s] 's type %s to m_parsed_functions. genericName[%s]\n", + ConstString(identifier).AsCString(), ConstString(type_name).AsCString(), ConstString(identifier).AsCString()); + } + CompilerTypeInfo tempInfo(sym_ctx.function->GetCompilerType(), sym_ctx.GetFunctionStartLineEntry().line, + sym_ctx.GetFunctionName(Mangled::NamePreference::ePreferMangled).AsCString()); + tempInfo.SetDeclInfo(DeclKind::FuncDecl, identifier, find_global, funcName); + result.emplace_back(tempInfo); + } else { + builder.m_type_names.insert(identifier); + } + m_parsed_functions.emplace_back(ParsedFunction(funcScope, funcName, type_name, funcType)); + } + return result; +} + +CompilerType CangjieDeclMap::LookUpType(std::string name) +{ + TypeList types; + llvm::DenseSet searched_symbol_files; + auto target = m_exe_ctx.GetTargetSP(); + name.erase(std::remove(name.begin(), name.end(), ' '), name.end()); + target->GetImages().FindTypes(nullptr, ConstString(name), false, 1, searched_symbol_files, types); + + size_t num_types = types.GetSize(); + for (size_t ti = 0; ti < num_types; ++ti) { + lldb::TypeSP type_sp = types.GetTypeAtIndex(ti); + auto type = type_sp->GetFullCompilerType(); + std::string enumName = type.GetTypeName().AsCString(); + if (type.IsValid() && type.GetTypeClass() == lldb::eTypeClassEnumeration) { + if (enumName.find(m_current_pkgname) != std::string::npos) { + enumName = enumName.substr(m_current_pkgname.size() + std::string(PACKAGE_SUFFIX).size()); + } + auto enumType = LookUpType(ENUM_PREFIX_NAME + enumName); + if (!enumType.IsValid()) { + enumType = LookUpType(E0_PREFIX_NAME + enumName); + } + if (!enumType.IsValid()) { + enumType = LookUpType(E2_PREFIX_NAME_OPTION_LIKE + enumName); + } + if (!enumType.IsValid()) { + enumType = LookUpType(E3_PREFIX_NAME + enumName); + } + if (!enumType.IsValid()) { + return CompilerType(); + } + if (m_parsed_types.find(name) != m_parsed_types.end()) { + auto var_type = m_parsed_types[name]; + if (var_type.GetTypeClass() != lldb::eTypeClassPointer || + !var_type.GetTypeName().GetStringRef().contains(E2_PREFIX_NAME_OPTION_LIKE)) { + m_parsed_types[name] = enumType; + } + } else { + m_parsed_types.insert({name, enumType}); + } + return enumType; + } + if (type.IsValid()) { + ReplaceTypeDefWithInterfaceType(type); + return type; + } + } + + return CompilerType(); +} + +OwnedPtr CangjieDeclMap::CreateVariable(const CompilerType& type, + std::string& name, Ptr pdecl) +{ + if (!pdecl) { + pdecl = CreateTypeDecl(type); + } + auto vardecl = builder.CreateVarDecl(type, name, m_current_pkgname, pdecl); + vardecl->isVar = !type.IsConst(); + auto var = FindParsedVarByName(ConstString(name)); + if (var) { + // Add position info of vardecl for diag. + auto line = var->GetDeclaration().GetLine(); + vardecl->begin = Position(m_file_id, line, 1); + vardecl->identifier.SetPos(vardecl->begin, + vardecl->begin + vardecl->identifier.Val().size()); + vardecl->end = vardecl->begin + Position(0, 1, 1); + } + return vardecl; +} + +lldb_private::CompilerType CangjieDeclMap::GetDynamicTypeFromGenericTypeInfo(Ptr& ty, std::string& demangled_name) { + std::string genericDeclName = lldb_private::GetGenericDeclName(demangled_name); + auto genericType = this->FindParsedTypesByName(genericDeclName); + if (!genericType.IsValid()) { + return CompilerType(); + } + Log *log = GetLog(LLDBLog::Expressions); + if (log) { + LLDB_LOGF(log, " ----------- [Generic] start instantiated Generic CompilerType by AST::Ty ----------- \n"); + } + return GetDynamicTypeFromTy(ty, demangled_name, genericType); +} + +void CangjieDeclMap::SetMemberDeclParent(Ptr funcdecl, Ptr decl) { + funcdecl->outerDecl = decl; + if (decl->astKind == ASTKind::CLASS_DECL) { + funcdecl->funcBody->parentClassLike = RawStaticCast(decl); + } else if (decl->astKind == ASTKind::INTERFACE_DECL) { + funcdecl->funcBody->parentClassLike = RawStaticCast(decl); + } else if (decl->astKind == ASTKind::STRUCT_DECL) { + funcdecl->funcBody->parentStruct = RawStaticCast(decl); + } else if (decl->astKind == ASTKind::ENUM_DECL) { + funcdecl->funcBody->parentEnum = RawStaticCast(decl); + } +} + +void CangjieDeclMap::CreateMemberFuncDecls(CompilerType& type, Ptr &decl, std::vector>& members) { + if (type.GetTypeName().GetStringRef().contains(LOCAL_FUNC_CAPTUREDVAR)) { + return; + } + Log *log = GetLog(LLDBLog::Expressions); + for (size_t i = 0; i < type.GetNumMemberFunctions(); i++) { + TypeMemberFunctionImpl func = type.GetMemberFunctionAtIndex(i); + if (func.IsValid()) { + std::string memberFunc = func.GetName().AsCString(); + if (IsGenericTypeParameters(memberFunc)) { + memberFunc = GetGenericDeclName(memberFunc); + } + if (memberFunc == CJDB_ADD_GENERIC_MEMBER_FUNC_NAME) { + continue; + } + std::string func_type_str = GetFuncNameFromComplierType(func.GetType()); + if (log) { + LLDB_LOGF(log, "add func [%s:%s] 's type %s to m_parsed_functions. \n", ConstString(decl->identifier.Val()).AsCString(), + ConstString(memberFunc).AsCString(), ConstString(func_type_str).AsCString()); + } + m_parsed_functions.emplace_back(ParsedFunction(decl->identifier, memberFunc, func_type_str, func.GetType())); + auto funcdecl = builder.CreateFuncDecl(func.GetType(), memberFunc, 1, decl, func.GetName().AsCString()); + if (func.GetKind() == lldb::eMemberFunctionKindStaticMethod) { + funcdecl->modifiers.emplace(Modifier(TokenKind::STATIC, BEGIN_POSITION)); + funcdecl->EnableAttr(Attribute::STATIC); + } + funcdecl->fullPackageName = m_current_pkgname; + SetMemberDeclParent(funcdecl, decl); + members.emplace_back(std::move(funcdecl)); + } + } +} + +void CangjieDeclMap::CreateStaticVarDecls( + CompilerType& type, Ptr &decl, std::vector>& members) { + if (!m_static_variables || m_static_variables->Empty()) { + return; + } + // Check and add static variable + std::string prefixname = type.GetTypeName().GetCString() + std::string("::"); + auto pos = prefixname.find("<"); + if (pos != std::string::npos) { + // "default::Rect2<$G_T>"" ==> "default::Rect2<" + prefixname = prefixname.substr(0, pos + 1); + } + auto varSize = m_static_variables->GetSize(); + for (size_t i = 0; i < varSize; i++) { + auto var = m_static_variables->GetVariableAtIndex(i); + std::string varname = var->GetName().GetCString(); + if (varname.find(prefixname) != 0) { + continue; + } + auto varPos = varname.rfind("::"); + if (varPos != std::string::npos) { + varname = varname.substr(varPos + 2); // 2 is size of string "::" + } + auto vartype = var->GetType()->GetFullCompilerType(); + if (!vartype.IsValid()) { + continue; + } + std::string vtypename = vartype.GetTypeName().AsCString(); + auto type_static = LookUpVariable(varname, VariableKind::StaticVariable); + Ptr target = CreateTypeDecl(type_static); + if (!target) { + target = decl; + } + // static variable of decl. + auto vardecl = builder.CreateVarDecl(vartype, varname, m_current_pkgname, target); + vardecl->modifiers.emplace(Modifier(TokenKind::STATIC, BEGIN_POSITION)); + vardecl->EnableAttr(Attribute::STATIC); + vardecl->fullPackageName = m_current_pkgname; + vardecl->outerDecl = decl; + vardecl->mangledName = BaseMangler().Mangle(*vardecl); + members.emplace_back(std::move(vardecl)); + } +} + +void CangjieDeclMap::CreateMemberVarDecls( + CompilerType& type, Ptr &decl, std::vector>& members) { + Log *log = GetLog(LLDBLog::Expressions); + // Add all member variable. + for (uint32_t i = 0; i < type.GetNumFields(); i++) { + std::string member_name; + auto child_type = type.GetFieldAtIndex(i, member_name, nullptr, nullptr, nullptr); + if (member_name == "$ti" || member_name == "$ti*") { + continue; + } + if (child_type.IsPointerType()) { + child_type = child_type.GetPointeeType(); + } + auto child_type_name = GetTypeNameWithoutPrefix(child_type.GetTypeName(), m_current_pkgname); + Ptr target = nullptr; + if (m_parsed_types.find(child_type_name) == m_parsed_types.end()) { + m_parsed_types.insert({child_type_name, child_type}); + target = CreateTypeDecl(child_type); + } + if (!target) { + target = decl; + } + auto vardecl = CreateVariable(child_type, member_name, target); + vardecl->DisableAttr(Attribute::GLOBAL); + // If outDecl is genericDecl and field is generic type, set ref type here. + if (log) { + LLDB_LOGF(log, "[Generic] set member varDecl's param[%s.%s]. \n", + decl->identifier.Val().c_str(), member_name.c_str()); + } + vardecl->fullPackageName = m_current_pkgname; + vardecl->outerDecl = decl; + vardecl->mangledName = BaseMangler().Mangle(*vardecl); + members.emplace_back(std::move(vardecl)); + } +} + +void CangjieDeclMap::CreateMemberDecls( + CompilerType type, Ptr &decl, std::vector>& members) { + CreateMemberVarDecls(type, decl, members); + CreateStaticVarDecls(type, decl, members); + CreateMemberFuncDecls(type, decl, members); +} + +CompilerType CangjieDeclMap::GetSuperClassDefinedType(CompilerType& type) { + for (size_t i = 0; i < type.GetNumMemberFunctions(); i++) { + TypeMemberFunctionImpl func = type.GetMemberFunctionAtIndex(i); + if (!func.IsValid()) { + continue; + } + std::string memberFunc = func.GetName().AsCString(); + if (memberFunc == CJDB_ADD_GENERIC_MEMBER_FUNC_NAME) { + auto superType = func.GetType().GetFunctionReturnType(); + return superType; + } + } + return CompilerType(); +} + +void CangjieDeclMap::SetGenericDeclBySubclass(Ptr refType, std::string name, + Ptr& subclass) { + if (!subclass->generic) { + return; + } + name.erase(std::remove(name.begin(), name.end(), ' '), name.end()); + if (name.find(INTERFACE_PREFIX_NAME) != std::string::npos) { + name = DeletePrefixOfType(name); + } + auto idLen = refType->ref.identifier.Val().size(); + auto args = name.substr(idLen + 1, name.size() - idLen - 2); + auto elements = builder.SplitTypeName(args); + // The generic type parameter of the superclass comes from the subclass or instancetiated. + // for example, class B <: A. + for (auto& ele:elements) { + bool isFromSubclass = false; + for (auto& param:subclass->generic->typeParameters) { + if (param->identifier.Val() == GetGenericParamDeclName(ele)) { + refType->typeArguments.emplace_back(builder.CreateAstType(ele, param.get(), m_current_pkgname)); + isFromSubclass = true; + break; + } + } + if (!isFromSubclass) { + refType->typeArguments.emplace_back(builder.CreateAstType(ele, nullptr, m_current_pkgname)); + } + } +} + +void CangjieDeclMap::ReplaceTypeDefWithInterfaceType(CompilerType& type) { + bool isTypeDef = type.IsValid() && type.GetTypeClass() == lldb::eTypeClassTypedef; + if (!isTypeDef) { + return; + } + auto underlyingType = type.GetTypedefedType(); + if (IsInterfaceType(underlyingType)) { + Log *log = GetLog(LLDBLog::Expressions); + LLDB_LOGF(log, "[Generic] replace typedef [%s] with inteface[%s]. \n", + type.GetTypeName().AsCString(), underlyingType.GetTypeName().AsCString()); + type = underlyingType; + } +} + +void CangjieDeclMap::CreateClassLikeParentDecls(CompilerType type, Ptr id) { + Log *log = GetLog(LLDBLog::Expressions); + for (size_t i = 0; i < type.GetNumDirectBaseClasses(); i++) { + auto superType = type.GetDirectBaseClassAtIndex(i, nullptr); + ReplaceTypeDefWithInterfaceType(superType); + std::string superName = superType.GetTypeName().GetCString(); + if (!superType.IsValid()) { + continue; + } + // The generic parameters of a certain class may differ from those of its superclass. + // For example, interface I2 {} class A <: I2 {} + // A's superType is type I2. And the return type of the A's member function $GetGenericDef is type I2. + auto definedType = GetSuperClassDefinedType(superType); + ReplaceTypeDefWithInterfaceType(definedType); + if (definedType.IsValid()) { + if (log) { + LLDB_LOGF(log, "[Generic] replace parent type [%s] -> %s . \n", + superType.GetTypeName().AsCString(), definedType.GetTypeName().AsCString()); + } + superType = definedType; + } + // If superType have memberFunc called $GetGenericDef, real type should be replaced. + // Create the parent class. + Ptr target = nullptr; + target = CreateTypeDecl(superType); + if (superName == "Object" || superName == "std.core::Object") { + id->inheritedTypes.emplace_back(builder.CreateAstType("std.core::Object", target, m_current_pkgname)); + } else if (superName.find(GENERIC_TYPE_PREFIX_NAME) == std::string::npos) { + // The generic type parameter of the superclass not comes from the subclass. + // For example, class B <: A, class B <: A + id->inheritedTypes.emplace_back(builder.CreateAstType(superName, target, m_current_pkgname)); + } else { + // The generic type parameter of the superclass comes from the subclass. + // For example, class B <: A, class B <: A. + OwnedPtr refType = builder.CreateAstType(superName, target, m_current_pkgname, false); + auto rt = RawStaticCast(refType.get()); + SetGenericDeclBySubclass(rt, superName, id); + id->inheritedTypes.emplace_back(std::move(refType)); + } + } +} + +bool CangjieDeclMap::IsGenericTypeWithConstraints(CompilerType& type) { + if (type.GetTypeClass() != lldb::eTypeClassStruct) { + return false; + } + std::string name = type.GetTypeName().AsCString(); + if (name.find(GENERIC_TYPE_PREFIX_NAME) != 0) { + return false; + } + if (type.GetNumDirectBaseClasses() > 0) { + auto str = type.GetDirectBaseClassAtIndex(0, nullptr).GetTypeName(); + if (str == "Equatable<$G_T>" || str == "Hashable") { + return false; + } + return true; + } + return false; +} + +std::set CangjieDeclMap::GetGenericTypeWithConstraints(CompilerType& type) { + std::set types; + for (uint32_t i = 0; i < type.GetNumFields(); i++) { + std::string member_name; + auto child_type = type.GetFieldAtIndex(i, member_name, nullptr, nullptr, nullptr); + if (IsGenericTypeWithConstraints(child_type)) { + types.insert(child_type); + } + } + for (size_t i = 0; i < type.GetNumMemberFunctions(); i++) { + TypeMemberFunctionImpl func = type.GetMemberFunctionAtIndex(i); + if (func.GetName() == "$GenericVirtualFunc") { + continue; + } + auto funcType = func.GetType(); + for (auto j = 0; j < funcType.GetFunctionArgumentCount(); j++) { + auto argType = funcType.GetFunctionArgumentAtIndex(j); + if (IsGenericTypeWithConstraints(argType)) { + types.insert(argType); + } + } + } + return types; +} + +OwnedPtr CangjieDeclMap::CreateGenericConstraintsDecl(Ptr &decl, CompilerType& type) { + auto gc = MakeOwned(); + OwnedPtr t = MakeOwned(); + std::string gn = type.GetTypeName().AsCString(); + t->ref.identifier = gn.substr(GENERIC_TYPE_PREFIX_NAME.size(), gn.size()); + for (auto& gen:decl->generic->typeParameters) { + if (gen->identifier.Val() == t->ref.identifier.Val()) { + t->ref.target = gen; + } + break; + } + gc->type = std::move(t); + for (size_t i = 0; i < type.GetNumDirectBaseClasses(); i++) { + auto superType = type.GetDirectBaseClassAtIndex(i, nullptr); + ReplaceTypeDefWithInterfaceType(superType); + if (!superType.IsValid()) { + continue; + } + Ptr decltmp = CreateTypeDecl(superType); + std::string up = DeletePrefixOfType(superType.GetTypeName().AsCString()); + OwnedPtr ctype = builder.CreateAstType(up, decltmp, m_current_pkgname); + + gc->upperBounds.emplace_back(std::move(ctype)); + std::string superName = superType.GetTypeName().GetCString(); + } + return gc; +} + +void CangjieDeclMap::CreateGenericConstraints(Ptr &decl, CompilerType& type) { + std::set types; + if (type.GetTypeClass() == lldb::eTypeClassClass || type.GetTypeClass() == lldb::eTypeClassStruct) { + types = GetGenericTypeWithConstraints(type); + } + for (auto type:types) { + auto constraint = CreateGenericConstraintsDecl(decl, type); + decl->generic->genericConstraints.emplace_back(std::move(constraint)); + } +} + +void CangjieDeclMap::CreateClassMemberDecls(CompilerType type, Ptr &decl) { + if (decl == nullptr || !type.IsValid()) { + return; + } + auto cd = RawStaticCast(decl); + if (!cd->body->decls.empty()) { + return; + } + CreateClassLikeParentDecls(type, cd); + CreateMemberDecls(type, decl, cd->body->decls); +} + +Ptr CangjieDeclMap::CreateClassDecl(CompilerType type, std::string prefix) { + auto name = GetTypeNameWithoutPrefix(type.GetTypeName(), prefix); + if (lldb_private::IsGenericTypeParameters(name)) { + name = GetGenericDeclName(name); + } + auto decl = MakeOwned(); + decl->body = MakeOwned(); + decl->identifier = name; + decl->fullPackageName = m_current_pkgname; + decl->mangledName = BaseMangler().Mangle(*decl); + std::string typeName = type.GetTypeName().GetCString(); + builder.CreateDeclGeneric(decl.get(), typeName); + Ptr cla = RawStaticCast(decl.get()); + CreateGenericConstraints(cla, type); + decl->EnableAttr(Attribute::PUBLIC, Attribute::GLOBAL); + auto target = GetSameGlobalDecl(std::move(decl), true); + CreateClassMemberDecls(type, target); + return target; +} + +void CangjieDeclMap::CreateInterfaceMemberDecls(Ptr &decl, CompilerType type) { + if (decl == nullptr || !type.IsValid()) { + return; + } + auto id = RawStaticCast(decl); + if (!id->body->decls.empty()) { + return; + } + CreateClassLikeParentDecls(type, id); + CreateMemberDecls(type, decl, id->body->decls); +} + +void CangjieDeclMap::CreateStructMemberDecls(CompilerType type, Ptr &decl) { + auto sd = RawStaticCast(decl); + if (!sd->body->decls.empty()) { + return; + } + CreateMemberDecls(type, decl, sd->body->decls); +} + +Ptr CangjieDeclMap::CreateEnumDecl(CompilerType type, std::string prefix) { + auto decl = builder.CreateEnumDecl(type); + std::string typeName = type.GetTypeName().GetCString(); + builder.CreateDeclGeneric(decl.get(), typeName); + CreateEnumConstructors(decl, type); + auto eDecl = Ptr(RawStaticCast(decl.get())); + CreateMemberFuncDecls(type, eDecl, decl->members); + return GetSameGlobalDecl(std::move(decl)); +} + +Ptr CangjieDeclMap::CreateStructDecl(CompilerType type, std::string prefix) { + auto name = GetTypeNameWithoutPrefix(type.GetTypeName(), prefix); + auto decl = MakeOwned(); + decl->body = MakeOwned(); + decl->identifier = name; + decl->fullPackageName = m_current_pkgname; + decl->mangledName = BaseMangler().Mangle(*decl); + decl->EnableAttr(Attribute::PUBLIC, Attribute::GLOBAL); + std::string typeName = type.GetTypeName().GetCString(); + builder.CreateDeclGeneric(decl.get(), typeName); + Ptr cla = RawStaticCast(decl.get()); + CreateGenericConstraints(cla, type); + auto target = GetSameGlobalDecl(std::move(decl), true); + CreateStructMemberDecls(type, target); + return target; +} + +Ptr CangjieDeclMap::CreateInterfaceDecl(CompilerType type, std::string prefix) { + auto name = GetTypeNameWithoutPrefix(type.GetTypeName(), prefix); + auto decl = MakeOwned(); + decl->body = MakeOwned(); + decl->identifier = name; + decl->fullPackageName = m_current_pkgname; + decl->mangledName = BaseMangler().Mangle(*decl); + decl->EnableAttr(Attribute::PUBLIC, Attribute::GLOBAL); + std::string typeName = type.GetTypeName().GetCString(); + typeName = DeletePrefixOfType(typeName); + builder.CreateDeclGeneric(decl.get(), typeName); + auto target = GetSameGlobalDecl(std::move(decl), true); + CreateInterfaceMemberDecls(target, type); + return target; +} + +bool CangjieDeclMap::IsInstantiatedOfGenericDecl(std::string typeName) { + auto declName = ConstString(GetGenericDeclName(typeName)); + if (m_generic_types.find(declName) != m_generic_types.end()) { + return true; + } + return false; +} + +CompilerType CangjieDeclMap::CreateOrGetGenericType(const CompilerType& type) +{ + Log *log = GetLog(LLDBLog::Expressions); + std::string refTypeName = type.GetTypeName().AsCString(); + auto declName = ConstString(GetGenericDeclName(refTypeName)); + if (m_generic_types.find(declName) != m_generic_types.end()) { + return m_generic_types[declName]; + } + std::string name = declName.AsCString(); + auto userDefinedType = GetGenericTypeByPartName(name); + if (userDefinedType.IsValid()) { + LLDB_LOGF(log, "[Generic]: get generic type [%s] from variable[%s]. \n", + userDefinedType.GetTypeName().GetCString(), ConstString(declName).AsCString()); + m_generic_types.insert({ConstString(declName), userDefinedType}); + } + return userDefinedType; +} + +Ptr CangjieDeclMap::CreateTypeDecl(CompilerType type) { + std::string typeName = type.GetTypeName().GetCString(); + if (CangjieASTBuiler::IsFunctionType(ConstString(typeName.c_str()))) { + return nullptr; + } + auto subName = GetSubNameWithoutPkgname(typeName, m_current_pkgname); + if (subName.empty()) { + // If the type is not from the current package, no need to create decl. + if (typeName.find("::") < typeName.find("<")) { + builder.m_type_names.insert(typeName); + } + return nullptr; + } + // If it is the instantiated type of a generic, generic type is needed here. + if (lldb_private::IsGenericInstanced(typeName)) { + type = CreateOrGetGenericType(type); + } + if (type.GetTypeClass() == lldb::eTypeClassEnumeration || IsEnumName(typeName)) { + return CreateEnumDecl(type, m_current_pkgname); + } + if (type.GetTypeClass() == lldb::eTypeClassStruct) { + return CreateStructDecl(type, m_current_pkgname); + } + if (IsInterfaceType(type)) { + return CreateInterfaceDecl(type, m_current_pkgname); + } + return CreateClassDecl(type, m_current_pkgname); +} + +void CangjieDeclMap::CreateInjectedSuper(Ptr& thisdecl) +{ + // Add $__lldb_injected_super: Object + auto supervar = MakeOwned(); + supervar->identifier = INJECTED_SUPER_NAME; + supervar->EnableAttr(Attribute::PUBLIC, Attribute::GLOBAL); + supervar->isVar = true; + auto thiscd = RawStaticCast(thisdecl); + for (auto& type : thiscd->inheritedTypes) { + supervar->type = ASTCloner::Clone(type.get()); + } + supervar->fullPackageName = "default"; + supervar->mangledName = BaseMangler().Mangle(*supervar); + m_global_decls.emplace_back(std::move(supervar)); +} + +void CangjieDeclMap::CreateInjectedThis() +{ + m_parsed_vars.insert({ConstString("this"), m_this_variable->GetVariable()}); + std::string name = m_this_variable->GetTypeName().GetCString(); + CompilerType realType = m_this_variable->GetCompilerType(); + if (IsGenericTypeParameters(name)) { + realType = m_this_variable->GetDynamicType(); + Log *log = GetLog(LLDBLog::Expressions); + if (log) { + LLDB_LOGF(log, "[Generic] this's dynamic type [%s] -> %s . \n", + ConstString(name).AsCString(), realType.GetTypeName().AsCString()); + } + CollectGenericTypeOfVariable(realType); + } + auto decl = CreateTypeDecl(realType); + if (!decl) { + return; + } + // Add __lldb_injected_self: A + auto thisvar = builder.CreateVarDecl(realType, "__lldb_injected_self", "default", decl); + thisvar->fullPackageName = "default"; + thisvar->mangledName = BaseMangler().Mangle(*thisvar); + if (decl->astKind == ASTKind::CLASS_DECL) { + CreateInjectedSuper(decl); + } + m_global_decls.emplace_back(std::move(thisvar)); +} + +void CangjieDeclMap::InitVariable() { + StackFrame *frame = m_exe_ctx.GetFramePtr(); + if (!frame) { + return; + } + SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextCompUnit); + if (sym_ctx.comp_unit) { + // Get current package name + m_current_pkgname = GetPkgNameFromCompilerUnit(*sym_ctx.comp_unit); + builder.m_current_pkgname = m_current_pkgname; + } + sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | lldb::eSymbolContextBlock); + if (sym_ctx.block) { + CompilerDeclContext frame_decl_context = sym_ctx.block->GetDeclContext(); + if (frame_decl_context) { + m_type_system = llvm::dyn_cast_or_null(frame_decl_context.GetTypeSystem()); + } + } + if (m_type_system == nullptr) { + m_type_system = ScratchTypeSystemClang::GetForTarget(*m_exe_ctx.GetTargetPtr(), + ScratchTypeSystemClang::DefaultAST, false); + } + + builder.m_file_id = m_file_id; + // Find this variable if in the context of a class. + m_this_variable = frame->FindVariable(ConstString("this")); + if (m_this_variable) { + CreateInjectedThis(); + } + m_local_variables = frame->GetInScopeVariableList(false); + m_static_variables = frame->GetInScopeVariableList(true); + auto svarSize = m_static_variables->GetSize(); + for (size_t i = svarSize - 1; i >= 0 && i < svarSize; i--) { + auto svar = m_static_variables->GetVariableAtIndex(i); + if (svar->GetScope() != lldb::eValueTypeVariableStatic) { + m_static_variables->RemoveVariableAtIndex(i); + } + } + m_global_variables = frame->GetInScopeVariableList(true); + auto varSize = m_global_variables->GetSize(); + if (varSize == 0) { + return; + } + // Check and delete local variable + for (size_t i = varSize - 1; i >= 0 && i < varSize; i--) { + auto var = m_global_variables->GetVariableAtIndex(i); + if (var->GetScope() != lldb::eValueTypeVariableGlobal) { + m_global_variables->RemoveVariableAtIndex(i); + } + } +} + +Ptr CangjieDeclMap::GetSameGlobalDecl(OwnedPtr decl, bool insert_begin) +{ + for (auto& d : m_global_decls) { + if (d->mangledName == decl->mangledName) { + return d.get(); + } + } + auto ret = decl.get(); + if (insert_begin) { + m_global_decls.emplace(m_global_decls.begin(), std::move(decl)); + } else { + m_global_decls.emplace_back(std::move(decl)); + } + return ret; +} + +bool CangjieDeclMap::AddPackageNameSpace(ConstString name) { + if (m_parsed_packages.find(name) != m_parsed_packages.end()) { + return true; + } + return LookUpPackage(name); +} + +std::vector CangjieDeclMap::GetEnumeratorName(CompilerType& enumType) { + lldb_private::TypeEnumMemberListImpl enum_member_list; + enumType.ForEachEnumerator([&enum_member_list]( + const CompilerType &integer_type, + ConstString name, + const llvm::APSInt &value) -> bool { + auto enum_member = lldb::TypeEnumMemberImplSP( + new TypeEnumMemberImpl(lldb::TypeImplSP(new TypeImpl(integer_type)), name, value)); + enum_member_list.Append(enum_member); + return true; + }); + std::vector enumCtorNameList; + for (size_t i = 0; i < enum_member_list.GetSize(); i++) { + lldb::TypeEnumMemberImplSP temp = enum_member_list.GetTypeEnumMemberAtIndex(i); + enumCtorNameList.emplace_back(temp->GetName()); + } + return enumCtorNameList; +} + +CompilerType CangjieDeclMap::GetEnumerationType(const CompilerType& type, bool hasArgs) { + CompilerType enumType; + auto typeName = type.GetTypeName().GetStringRef(); + if (!hasArgs || typeName.contains(E2_PREFIX_NAME_OPTION_LIKE)) { + for (uint32_t i = 0; i < type.GetNumFields(); i++) { + std::string member_name; + auto tmpType = type.GetFieldAtIndex(i, member_name, nullptr, nullptr, nullptr); + if (member_name == "constructor" || member_name == "constructor_R") { + enumType = tmpType; + break; + } + } + } else { + CompilerType ctorFuncType = type.GetDirectBaseClassAtIndex(0, nullptr); + std::string member_name; + ctorFuncType = ctorFuncType.GetFieldAtIndex(0, member_name, nullptr, nullptr, nullptr); + if (type.GetTypeName().GetStringRef().contains(E3_PREFIX_NAME) || + type.GetTypeName().GetStringRef().contains(ENUM_PREFIX_NAME)) { + CJC_ASSERT(member_name == "constructor"); + return ctorFuncType; + } + CJC_ASSERT(member_name == "EnumClass$"); + if (ctorFuncType.IsPointerType()) { + ctorFuncType = ctorFuncType.GetPointeeType(); + } + enumType = ctorFuncType.GetFieldAtIndex(0, member_name, nullptr, nullptr, nullptr); + CJC_ASSERT(member_name == "constructor"); + } + return enumType; +} + +void CangjieDeclMap::CreateEnumOptionLikeCtors(OwnedPtr &decl, const CompilerType& type, + std::vector& enumCtorNameList) { + for (size_t i = 0; i < enumCtorNameList.size(); i++) { + std::string ctorName = enumCtorNameList[i].AsCString(); + if (ctorName.find(E2_CTOR_PREFIX_NAME) == std::string::npos) { + std::string member_name; + // Val is arg1 + auto argType = type.GetFieldAtIndex(1, member_name, nullptr, nullptr, nullptr); + auto funcDecl = builder.CreateEnumFuncDecl(type, argType, ctorName, decl); + funcDecl->outerDecl = decl; + funcDecl->EnableAttr(AST::Attribute::ENUM_CONSTRUCTOR); + auto curFunc = Ptr(funcDecl.get()); + SetMemberDeclParent(curFunc, decl); + decl->constructors.emplace_back(std::move(funcDecl)); + } else { + ctorName = ctorName.substr(E2_CTOR_PREFIX_NAME.size(), ctorName.size()); + auto vardecl = builder.CreateEnumVarDecl(type, ctorName, m_current_pkgname, decl); + vardecl->fullPackageName = m_current_pkgname; + vardecl->outerDecl = decl.get(); + vardecl->mangledName = BaseMangler().Mangle(*vardecl); + vardecl->EnableAttr(AST::Attribute::ENUM_CONSTRUCTOR); + decl->constructors.emplace_back(std::move(vardecl)); + } + } +} + +void CangjieDeclMap::CreateEnumConstructors(OwnedPtr &decl, const CompilerType& type) { + // For one cangjie enumDecl with param, debugInfo save two types. + // for example, enum RGBColor { Red | Green | Blue(UInt8)} have enum type and struct type. + // DW_TAG_enumeration_type Ctor-RGBColor with member Red,Green,Blue. + // DW_TAG_structure_type Enum$RGBColor with RGBColor_ctor_0, RGBColor_ctor_1, RGBColor_ctor_2. + // For one cangjie enumDecl without param, debugInfo save two types. + // for example, enum RGBColor { Red | Green} have enum type RGBColor. + // step1: find enum type Ctor-RGBColor. + // for example, enum RGBColor's type name is Enum$RGBColor, subName is RGBColor. + std::string typeName = type.GetTypeName().GetCString(); + EnumLayout kind = GetEnumLayout(typeName); + // for enum type + decl->hasArguments = (kind != EnumLayout::E0Int); + // for an enum type with args, like RGBColor {Red(Int64)}, enumName is default::Enum$RGBColor + // for an enum type without args, like RGBColor {Red}, enumName is default::RGBColor, RGBColor is needed. + AddPackageNameSpace(ConstString(m_current_pkgname.c_str())); + + CompilerType enumType = GetEnumerationType(type, decl->hasArguments); + std::vector enumCtorNameList = GetEnumeratorName(enumType); + if (kind == EnumLayout::E2OptionLike) { + CreateEnumOptionLikeCtors(decl, type, enumCtorNameList); + return; + } else if (kind == EnumLayout::E0Int) { + for (size_t i = 0; i < enumCtorNameList.size(); i++) { + auto vardecl = builder.CreateEnumVarDecl(type, enumCtorNameList[i].AsCString(), m_current_pkgname, decl); + vardecl->fullPackageName = m_current_pkgname; + vardecl->outerDecl = decl.get(); + vardecl->mangledName = BaseMangler().Mangle(*vardecl); + vardecl->EnableAttr(AST::Attribute::ENUM_CONSTRUCTOR); + decl->constructors.emplace_back(std::move(vardecl)); + } + return; + } + // for structure type + // VarDecl Red {*Ty: Enum-RGBColor have no type and no initializer}. + for (size_t i = 0; i < type.GetNumDirectBaseClasses(); i++) { + CompilerType ctorFuncType = type.GetDirectBaseClassAtIndex(i, nullptr); + ReplaceTypeDefWithInterfaceType(ctorFuncType); + if (IsInterfaceType(ctorFuncType)) { + CreateInterfaceDecl(ctorFuncType, m_current_pkgname); + continue; + } + std::string member_name; + if (!type.GetTypeName().GetStringRef().contains(E3_PREFIX_NAME) && + !type.GetTypeName().GetStringRef().contains(ENUM_PREFIX_NAME)) { + ctorFuncType = ctorFuncType.GetFieldAtIndex(0, member_name, nullptr, nullptr, nullptr); + } + if (ctorFuncType.IsPointerType()) { + ctorFuncType = ctorFuncType.GetPointeeType(); + } + if (ctorFuncType.GetNumFields() == 1) { + // for struct type RGBColor_ctor_0, VarDecl have only one filed. + auto vardecl = builder.CreateEnumVarDecl(type, enumCtorNameList[i].AsCString(), m_current_pkgname, decl); + vardecl->fullPackageName = m_current_pkgname; + vardecl->outerDecl = decl.get(); + vardecl->mangledName = BaseMangler().Mangle(*vardecl); + vardecl->EnableAttr(AST::Attribute::ENUM_CONSTRUCTOR); + vardecl->outerDecl = decl; + decl->constructors.emplace_back(std::move(vardecl)); + } else { + std::string funcName = enumCtorNameList[i].AsCString(); + auto funcDecl = builder.CreateEnumFuncDecl(type, ctorFuncType, funcName, decl); + funcDecl->outerDecl = decl; + funcDecl->EnableAttr(AST::Attribute::ENUM_CONSTRUCTOR); + auto curFunc = Ptr(funcDecl.get()); + SetMemberDeclParent(curFunc, decl); + decl->constructors.emplace_back(std::move(funcDecl)); + } + } +} + +bool CangjieDeclMap::IsLocalConstVariable(const std::string& mangledName) +{ + auto name = Cangjie::Demangle(mangledName).GetFullName(); + if (name.find("::") != std::string::npos) { + return true; + } + return false; +} + +void CangjieDeclMap::CreateVarDeclByType(const CompilerTypeInfo& typeInfo, std::string name) +{ + auto type = typeInfo.Type; + bool find_global = typeInfo.Global; + auto vardecl = CreateVariable(type, name); + vardecl->isVar = !typeInfo.IsLet; + if (IsLocalConstVariable(typeInfo.mangle_name)) { + // Because of local const variables are promoted to global variables in the CHIR stage, + // special handling is required here. + find_global = false; + } + if (Cangjie::Demangle(typeInfo.mangle_name).IsPrivateDeclaration()) { + vardecl->EnableAttr(Attribute::PRIVATE); + vardecl->begin = Position(0, vardecl->begin.line, 1); + vardecl->end = vardecl->begin + Position(0, 1, 1); + } + if (find_global) { + vardecl->fullPackageName = m_current_pkgname; + vardecl->mangledName = BaseMangler().Mangle(*vardecl); + GetSameGlobalDecl(std::move(vardecl)); + } else { + vardecl->fullPackageName = LOCAL_PACKAGE_NAME; + vardecl->mangledName = BaseMangler().Mangle(*vardecl); + for (auto& d : m_local_decls) { + if (d->mangledName == vardecl->mangledName) { + return; + } + } + m_local_decls.emplace_back(std::move(vardecl)); + } +} + +void CangjieDeclMap::CreateTypeDeclByType(const CompilerType& type) +{ + CreateTypeDecl(type); +} + +void CangjieDeclMap::CreateFuncDeclByType(const CompilerTypeInfo& ft, std::string name, bool find_global) +{ + auto funcdecl = builder.CreateFuncDecl(ft.Type, name, ft.Line, nullptr, ft.genericName); + if (find_global) { + if (m_local_func.find(ft.Type.GetTypeName()) != m_local_func.end()) { + return; + } + // Create global funcdecl. + funcdecl->fullPackageName = m_current_pkgname; + m_global_decls.emplace_back(std::move(funcdecl)); + } else { + // Create local funcdecl. + funcdecl->fullPackageName = LOCAL_PACKAGE_NAME; + funcdecl->mangledName = ft.mangle_name; + m_local_decls.emplace_back(std::move(funcdecl)); + m_local_func.insert(ft.Type.GetTypeName()); + } +} + +void CangjieDeclMap::CreatePackage(const std::string &name) { + if (m_parsed_packages.find(ConstString(name.c_str())) == m_parsed_packages.end()) { + return; + } + auto namespace_map = m_parsed_packages[ConstString(name.c_str())]; + for (const auto &element : m_identifys) { + for (auto it = namespace_map->begin(); it != namespace_map->end(); ++it) { + // Lookup global var first. + auto type = LookUpGlobalVariable(it->first, ConstString(element.c_str()), it->second); + if (type.IsValid()) { + auto vardecl = builder.CreateVarDecl(type, element, name); + // Check and add variable type of decl. + if (vardecl != nullptr) { + vardecl->fullPackageName = name; + vardecl->mangledName = BaseMangler().Mangle(*vardecl); + m_global_decls.emplace_back(std::move(vardecl)); + continue; + } + } + // lookup function + auto funcs = LookUpFunction(it->first, ConstString(element.c_str()), it->second); + // look up type + type = LookUptype(it->first, ConstString(element.c_str()), it->second); + if (type.IsValid()) { + auto typedecl = CreateTypeDecl(type); + if (typedecl != nullptr) { + typedecl->identifier = element; + continue; + } + } + } + } + return; +} + +void CangjieDeclMap::AddImportSpec(OwnedPtr& file, std::string curPkgName) +{ + for (auto& fullpkg : m_fullpkgs) { + if (curPkgName == fullpkg) { + continue; + } + auto import = MakeOwned(); + import->content.prefixPaths = Utils::SplitQualifiedName(fullpkg); + const size_t prefixLen = import->content.prefixPaths.size(); + import->content.prefixPoses.resize(prefixLen); + import->content.prefixDotPoses.resize(prefixLen); + import->content.identifier = "*"; + import->content.kind = ImportKind::IMPORT_ALL; + import->EnableAttr(Attribute::IMPLICIT_ADD, Attribute::COMPILER_ADD); + import->curFile = file.get(); + file->imports.emplace_back(std::move(import)); + } + return; +} + +void CangjieDeclMap::CollectImportPkgName() +{ + std::for_each(builder.m_type_names.begin(), builder.m_type_names.end(), [&](auto& name) { + auto pos = name.find(PACKAGE_SUFFIX); + if (pos == std::string::npos || pos > name.find("(") || pos > name.find("<")) { + return; + } + auto pkgname = name.substr(0, pos); + if (pkgname.empty() || pkgname == "std") { + return; + } + m_fullpkgs.insert(pkgname); + Log *log = GetLog(LLDBLog::Expressions); + if (log) { + LLDB_LOGF(log, "collect import package [%s]. \n", ConstString(pkgname).AsCString()); + } + }); +} + +std::vector> CangjieDeclMap::CreateFakePackage() { + std::vector> packages; + // Collect other package names from typenames for import. + CollectImportPkgName(); + if (!m_captured_vars.empty()) { + auto file = MakeOwned(); + file->fileName = std::string("$CapturedVars.cj"); + file->decls = std::move(m_captured_vars); + auto package = MakeOwned(LOCAL_FUNC_CAPTUREDVAR); + package->EnableAttr(Attribute::IMPORTED); + package->files.emplace_back(std::move(file)); + packages.push_back(std::move(package)); + } + if (!m_local_decls.empty()) { + auto file = MakeOwned(); + file->fileName = std::string("locals.cj"); + AddImportSpec(file, LOCAL_PACKAGE_NAME); + file->decls = std::move(m_local_decls); + auto package = MakeOwned(LOCAL_PACKAGE_NAME); + package->EnableAttr(Attribute::IMPORTED); + package->files.emplace_back(std::move(file)); + packages.push_back(std::move(package)); + } + auto file = MakeOwned(); + file->fileName = m_current_filename; + AddImportSpec(file, m_current_pkgname); + for (auto& tmpDecl:m_global_decls) { + if (tmpDecl->TestAttr(Attribute::PRIVATE)) { + file->begin = tmpDecl->begin; + tmpDecl->curFile = file.get(); + } + } + file->decls = std::move(m_global_decls); + auto package = MakeOwned(!m_current_pkgname.empty() ? m_current_pkgname : "default"); + package->EnableAttr(Attribute::IMPORTED); + package->files.emplace_back(std::move(file)); + packages.push_back(std::move(package)); + return packages; +} + +void CangjieDeclMap::CollectGenericTypeOfVariable(const CompilerType& type) { + Log *log = GetLog(LLDBLog::Expressions); + std::string varTypeName = std::string(type.GetTypeName().AsCString()); + + if (!type.IsValid() || type.GetTypeClass() == lldb::eTypeClassEnumeration) { + return; + } + std::string fullName = type.GetTypeName().AsCString(); + if (!lldb_private::IsGenericInstanced(fullName)) { + return; + } + auto name = lldb_private::GetGenericDeclName(varTypeName); + auto userDefinedType = GetGenericTypeByPartName(name); + if (userDefinedType.IsValid()) { + LLDB_LOGF(log, "[Generic]: get generic type [%s] from variable[%s]. \n", + userDefinedType.GetTypeName().GetCString(), ConstString(name).AsCString()); + m_generic_types.insert({ConstString(name), userDefinedType}); + } +} + +CompilerType CangjieDeclMap::GetGenericTypeByPartName(const std::string& name) { + SymbolFile *symfile = m_type_system->GetSymbolFile(); + if (!symfile) { + return CompilerType(); + } + lldb_private::TypeList type_list; + symfile->GetTypes(nullptr, lldb::eTypeClassClass|lldb::eTypeClassStruct, type_list); + size_t ntypes = type_list.GetSize(); + std::string combine_name = name + "<" + GENERIC_TYPE_PREFIX_NAME; + auto subName = name; + if (subName = GetSubNameWithoutPkgname(subName, m_current_pkgname); !subName.empty()) { + combine_name = subName + "<" + GENERIC_TYPE_PREFIX_NAME; + } + for (size_t i = 0; i < ntypes; ++i) { + lldb::TypeSP type = type_list.GetTypeAtIndex(i); + std::string tmpName = type->GetName().AsCString(); + auto tlpos = tmpName.find(combine_name); + auto tpos = tmpName.find("<"); + if (tlpos != std::string::npos && (tpos == std::string::npos || tpos > tlpos)) { + CompilerType full_type = type->GetFullCompilerType(); + return full_type; + } + } + return CompilerType(); +} + +void CangjieDeclMap::LookUpTypeByName(const std::string& name, std::vector& m_translate_decls) { + Log *log = GetLog(LLDBLog::Expressions); + // Find and create local, global variables. + CompilerType type_local = LookUpVariable(name, VariableKind::LocalVariable); + CollectVarDecl(name, type_local, VariableKind::LocalVariable, m_translate_decls); + CompilerType type_global = LookUpVariable(name, VariableKind::GlobalVariable); + CollectVarDecl(name, type_global, VariableKind::GlobalVariable, m_translate_decls); + CompilerType type_static = LookUpVariable(name, VariableKind::StaticVariable); + CollectVarDecl(name, type_static, VariableKind::StaticVariable, m_translate_decls); + // Find and create local, global funcdecls. + auto localFuncTypes = LookUpFunction(name, false); + for (auto& ft : localFuncTypes) { + if (!ft.Type.IsValid()) { + continue; + } + m_translate_decls.emplace_back(ft); + } + auto globalFuncTypes = LookUpFunction(name, true); + for (auto& ft : globalFuncTypes) { + if (!ft.Type.IsValid()) { + continue; + } + auto retTypeName = GetTypeNameWithoutPrefix(ft.Type.GetFunctionReturnType().GetTypeName(), m_current_pkgname); + LookUpTypeByName(retTypeName, m_translate_decls); + m_translate_decls.emplace_back(ft); + } + // Find structDecl, enumDecl, classDecl, interfaceDecl. + auto userDefinedType = LookUpType(name); + if (!userDefinedType.IsValid()) { + userDefinedType = GetGenericTypeByPartName(name); + if (userDefinedType.IsValid()) { + LLDB_LOGF(log, "[Generic]: find type decl with generic param by part name[%s] -> [%s]. \n", + ConstString(name).AsCString(), userDefinedType.GetTypeName().GetCString()); + m_generic_types.insert({ConstString(name), userDefinedType}); + } + } + if (userDefinedType.IsValid()) { + CompilerTypeInfo tempInfo(userDefinedType, 0, ""); + tempInfo.SetDeclInfo(DeclKind::UserDefinedDecl, name, true); + m_translate_decls.emplace_back(tempInfo); + } +} + +void CangjieDeclMap::CreateDeclByType(const CompilerTypeInfo& typeInfo) { + std::string name = typeInfo.name; + switch (typeInfo.Kind) { + case DeclKind::VarDecl:{ + // Find and create local ,global variables. + CreateVarDeclByType(typeInfo, name); + break; + } + case DeclKind::FuncDecl:{ + // Find and create local funcdecls. + CreateFuncDeclByType(typeInfo, name, typeInfo.Global); + break; + } + case DeclKind::UserDefinedDecl:{ + // Find and create types. + CreateTypeDeclByType(typeInfo.Type); + } + default :{} + } +} + +bool CangjieDeclMap::CheckAndModifyCapturedVardecl(VarDecl& vd, OwnedPtr& paramList) { + if (vd.type && vd.type->astKind == AST::ASTKind::REF_TYPE) { + auto rt = RawStaticCast(vd.type.get()); + static std::string CAPTURED_PREFIX = "$Captured_"; + if (auto pos = rt->ref.identifier.Val().find(CAPTURED_PREFIX); pos != std::string::npos) { + OwnedPtr param = MakeOwned(); + param->identifier = vd.identifier; + rt->ref.identifier = rt->ref.identifier.Val().substr(pos + CAPTURED_PREFIX.size()); + auto capturedType = builder.CreateAstType(rt->ref.identifier.Val(), nullptr); + param->type = capturedType->astKind == AST::ASTKind::REF_TYPE ? + std::move(vd.type) : std::move(capturedType); + paramList->params.emplace_back(std::move(param)); + // Modify captured variable: "var aa: $Captured_Int64 --> var aa: Int64 = 0x7fff...". + auto primType = MakeOwned(); + primType->kind = AST::TypeKind::TYPE_INT64; + primType->str = "Int64"; + vd.type = std::move(primType); + auto lit = MakeOwned(); + lit->kind = AST::LitConstKind::INTEGER; + // For the captured variable, the pointer transferred by the front end is offset by 8 bytes. + // Therefore, the pointer needs to be offset back 8 bytes to the original $Captured_xxx. + lit->stringValue = std::to_string(m_captured_var_addrs[vd.identifier.Val()] - 8); + vd.initializer = std::move(lit); + return true; + } + } + return false; +} + +void CreateCapturedVirtualFunc(OwnedPtr& cd) { + // For example, a func name can be _CN7default4mainHvE4foo2Hv$i + auto vdi = MakeOwned(); + vdi->identifier = "$Captured$i"; + vdi->initializer = CreateCallExpr(CreateRefExpr("Object"), {}); + cd->body->decls.emplace(cd->body->decls.begin(), std::move(vdi)); + + // For example, a func name can be _CN7default4mainHvE4foo2Hv$g + auto vdg = MakeOwned(); + vdg->identifier = "$Captured$g"; + vdg->initializer = CreateCallExpr(CreateRefExpr("Object"), {}); + cd->body->decls.emplace(cd->body->decls.begin(), std::move(vdg)); +} + +void CangjieDeclMap::CreateCapturedVars() { + // $CapturedVars: {i8*, i8*, ...} + for (auto& [fn, type] : builder.m_capture_vars) { + // $CapturedVars is a pointer now, get the base type. + type = type.GetPointeeType(); + // Create $CapturedVars class + auto cd = MakeOwned(); + cd->body = MakeOwned(); + cd->identifier = fn + "$class" + LOCAL_FUNC_CAPTUREDVAR; + cd->fullPackageName = m_current_pkgname; + cd->mangledName = BaseMangler().Mangle(*cd); + std::string typeName = type.GetTypeName().GetCString(); + builder.CreateDeclGeneric(cd.get(), typeName); + Ptr cla = RawStaticCast(cd.get()); + CreateGenericConstraints(cla, type); + CreateClassMemberDecls(type, cla); + // Create class init + auto initFunc = MakeOwned(); + initFunc->funcBody = MakeOwned(); + initFunc->funcBody->body = MakeOwned(); + initFunc->identifier = "init"; + initFunc->EnableAttr(Attribute::CONSTRUCTOR, Attribute::COMPILER_ADD); + SetMemberDeclParent(initFunc.get(), cla); + auto paramList = MakeOwned(); + for (auto& d : cd->body->decls) { + if (d->astKind == AST::ASTKind::VAR_DECL) { + // Create local variable. + std::vector typeinfos; + auto type_local = LookUpVariable(d->identifier.Val(), VariableKind::LocalVariable); + CollectVarDecl(d->identifier.Val(), type_local, VariableKind::LocalVariable, typeinfos); + for (auto& type: typeinfos) { + CreateDeclByType(type); + } + auto vd = RawStaticCast(d.get()); + // If the capturedVar is not let variable, 'var aa: $Captured_Int64'. + if (CheckAndModifyCapturedVardecl(*vd, paramList)) { + continue; + } + // Create assignExpr 'this.aa = aa' in initFunc. + auto ma = CreateMemberAccess(CreateRefExpr("this"), d->identifier); + auto ae = CreateAssignExpr(std::move(ma), CreateRefExpr(d->identifier)); + initFunc->funcBody->body->body.emplace_back(std::move(ae)); + OwnedPtr param = MakeOwned(); + param->identifier = d->identifier; + param->type = ASTCloner::Clone(vd->type.get()); + paramList->params.emplace_back(std::move(param)); + } + } + initFunc->funcBody->paramLists.emplace_back(std::move(paramList)); + cd->body->decls.emplace_back(std::move(initFunc)); + CreateCapturedVirtualFunc(cd); + m_captured_vars.emplace_back(std::move(cd)); + } +} + +std::vector> CangjieDeclMap::CreateExternalAST() { + for (const auto &element : m_identifys) { + if (LookUpPackage(ConstString(element.c_str()))) { + CreatePackage(element); + } + } + Log *log = GetLog(LLDBLog::Expressions); + // First, collect all type + for (const auto& element : m_identifys) { + std::vector translate_decls; + LookUpTypeByName(element, translate_decls); + // Translate types to decls. + if (log) { + LLDB_LOGF(log, "%s 's decls size is %ld. \n", ConstString(element).AsCString(), translate_decls.size()); + } + for (const auto& decl: translate_decls) { + CreateDeclByType(decl); + } + } + for (auto& name:builder.m_type_names) { + if (std::find(m_identifys.begin(), m_identifys.end(), name) != m_identifys.end()) { + continue; + } + std::vector ctypeinfos; + LookUpTypeByName(name, ctypeinfos); + for (auto& ti: ctypeinfos) { + CreateDeclByType(ti); + } + } + CreateCapturedVars(); + return CreateFakePackage(); +} + +lldb::VariableSP CangjieDeclMap::FindParsedVarByName(lldb_private::ConstString name) { + if (m_parsed_vars.find(name) != m_parsed_vars.end()) { + return m_parsed_vars[name]; + } + return nullptr; +} + +lldb_private::CompilerType CangjieDeclMap::FindParsedTypesByName(std::string name) { + name.erase(std::remove(name.begin(), name.end(), ' '), name.end()); + if (m_parsed_types.find(name) != m_parsed_types.end()) { + return m_parsed_types[name]; + } + + if (m_generic_types.find(ConstString(name)) != m_generic_types.end()) { + return m_generic_types[ConstString(name)]; + } + return GetPrimitiveTypeByName(name); +} + +static std::map basic_clang_type_map = { + { "Bool", lldb::eBasicTypeBool }, + { "Rune", lldb::eBasicTypeChar32 }, + { "Int8", lldb::eBasicTypeSignedChar }, + { "Int16", lldb::eBasicTypeShort }, + { "Int32", lldb::eBasicTypeInt }, + { "Int64", lldb::eBasicTypeLongLong }, + { "IntNative", lldb::eBasicTypeLongLong }, + { "UInt8", lldb::eBasicTypeUnsignedChar }, + { "UInt16", lldb::eBasicTypeUnsignedShort }, + { "UInt32", lldb::eBasicTypeUnsignedInt }, + { "UInt64", lldb::eBasicTypeUnsignedLongLong }, + { "UIntNative", lldb::eBasicTypeUnsignedLongLong }, + { "Float16", lldb::eBasicTypeHalf }, + { "Float32", lldb::eBasicTypeFloat }, + { "Float64", lldb::eBasicTypeDouble }, +}; + +CompilerType CangjieDeclMap::GetPrimitiveTypeByName(std::string &name) { + if (!m_type_system) { + return CompilerType(); + } + if (basic_clang_type_map.find(name) != basic_clang_type_map.end()) { + return m_type_system->GetBasicType(basic_clang_type_map[name]).CreateTypedef( + name.c_str(), m_type_system->CreateDeclContext(m_type_system->GetTranslationUnitDecl()), 0); + } + return CompilerType(); +} + +CompilerType CangjieDeclMap::GetDynamicTypeFromTy(Ptr& ty, std::string typeName, CompilerType& genericType) { + Log *log = GetLog(LLDBLog::Expressions); + if (log && !ty->IsPrimitive()) { + LLDB_LOGF(log, "[Generic]: instantiated [%s] by [%s]. \n", genericType.GetTypeName().AsCString(), ConstString(typeName).AsCString()); + } + if (ty->IsPrimitive()) { + return GetPrimitiveTypeByName(typeName); + } else if (ty->IsString()) { + auto type = this->FindParsedTypesByName(typeName); + if (!type.IsValid()) { + type = LookUptype(lldb::ModuleSP(), lldb_private::ConstString(typeName.c_str()), + lldb_private::CompilerDeclContext()); + m_parsed_types.insert({"std.core::String", type}); + } + return type; + } else if (ty->IsStructArray()) { + return GetDynamicArrayType(ty, typeName, genericType); + } + + switch (ty->kind) { + case AST::TypeKind::TYPE_CLASS: { + return GetDynamicClassType(ty, typeName, genericType); + } + case AST::TypeKind::TYPE_STRUCT: { + return GetDynamicStructType(ty, typeName, genericType); + } + case AST::TypeKind::TYPE_TUPLE:{ + return GetDynamicTupleType(ty, typeName, genericType); + } + case AST::TypeKind::TYPE_ENUM: { + return GetDynamicEnumType(ty, typeName, genericType); + } + case AST::TypeKind::TYPE_FUNC: { + return GetDynamicFuncType(ty, typeName, genericType); + } + default: + return CompilerType(); + } + return CompilerType(); +} + +void CangjieDeclMap::AddFieldToRecordType(CompilerType& instancedType, Ptr& ty, std::string& typeName, CompilerType& genericType) +{ + Log *log = GetLog(LLDBLog::Expressions); + CJC_ASSERT(ty->IsStruct() || ty->IsEnum() || ty->IsClass() || ty->IsInterface()|| ty->IsArray() ); + Ptr decl = AST::Ty::GetDeclOfTy(ty); + if (log) { + LLDB_LOGF(log, "|-- [%s] [%s]:[%s] start add member! \n", ConstString(AST::Ty::KindName(ty->kind)).AsCString(), + ConstString(decl->identifier.Val()).AsCString(), ConstString(typeName).AsCString()); + } + std::vector instantiatedNames; + if (decl->generic) { + for (auto arg : ty->typeArgs) { + instantiatedNames.emplace_back(arg->String()); + } + } + for (uint32_t i = 0; i < genericType.GetNumFields(); i++) { + std::string member_name; + CompilerType field_type; + auto child_type = genericType.GetFieldAtIndex(i, member_name, nullptr, nullptr, nullptr); + if (member_name == "$ti" || member_name == "$ti*") { + continue; + } + if (log) { + LLDB_LOGF(log, " start add [%s.%s : %s]. \n", ConstString(decl->identifier.Val()).AsCString(), + ConstString(member_name).AsCString(), child_type.GetTypeName().AsCString()); + } + std::string compilerTypeName = child_type.GetTypeName().AsCString(); + auto subTy = GetMemberDeclTyByName(ty, member_name, typeName); + // There are three kind cases, for example + // class A { + // public var value3: Int64 case: non-generic type + // public var value1: T case: generic type + // public var value2: Array case: compound type with generic + // } + if (subTy->IsGeneric()) { + // Case generic type. + auto index = GetSubGenericTyIndex(decl, subTy->name); + std::string subTypeName = instantiatedNames[index]; + subTy = ty->typeArgs[index]; + auto tempGenericType = this->GetGenericTypeByPartName(subTy->name); + field_type = GetDynamicTypeFromTy(subTy, subTypeName, tempGenericType); + } else if (compilerTypeName.find(GENERIC_TYPE_PREFIX_NAME) != std::string::npos) { + // Case compound type with Generic. + field_type = GetDynamicTypeFromTy(subTy, subTy->String(), child_type); + } else { + // Case non-generic type + field_type = child_type; + } + CJC_ASSERT(field_type.IsValid()); + if (builder.IsRefType(subTy)) { + field_type = field_type.GetPointerType(); + } + if (log) { + LLDB_LOGF(log, " [%s] add field [%s : %s]. \n", ConstString(decl->identifier.Val()).AsCString(), + ConstString(member_name).AsCString(), field_type.GetTypeName().AsCString()); + } + this->GetTypeSystem()->AddFieldToRecordType(instancedType, member_name.c_str(), field_type, lldb::eAccessPublic, 0); + } +} + +Ptr CangjieDeclMap::GetMemberDeclTyByName(Ptr& ty, std::string& memberName, std::string& typeName) +{ + // class A { + // public var value: T + // init(a:T) { + // value = a + // } + // } + // memberName is value + Ptr decl = AST::Ty::GetDeclOfTy(ty); + auto& memberDecls = decl->GetMemberDecls(); + for (size_t i = 0; i < memberDecls.size(); i++) { + if (memberDecls[i]->astKind != AST::ASTKind::VAR_DECL) { + continue; + } + if (memberDecls[i]->identifier.Val() == memberName) { + return memberDecls[i]->ty; + } + } + return decl->ty; +} + +CompilerType CangjieDeclMap::GetEnumType(Ptr& ty, std::string enum_name, CompilerType& genericType) { + TypeSystemClang* ast = this->GetTypeSystem(); + CompilerType basic_type = ast->GetBasicType(lldb::eBasicTypeInt).CreateTypedef( + "Int32", ast->CreateDeclContext(ast->GetTranslationUnitDecl()), 0); + CompilerType enumType = ast->CreateEnumerationType(enum_name.c_str(), + ast->GetTranslationUnitDecl(), OptionalClangModuleID(), Declaration(), basic_type, false); + ast->StartTagDeclarationDefinition(enumType); + + std::string member_name; + bool hasArgs = RawStaticCast(ty.get())->decl->hasArguments; + auto genericEnumType = GetEnumerationType(genericType, hasArgs); + auto enum_member_list = GetEnumeratorName(genericEnumType); + for (size_t i = 0; i < enum_member_list.size(); i++) { + const char* ctorName = enum_member_list[i].AsCString(); + ast->AddEnumerationValueToEnumerationType(enumType, Declaration(), ctorName, i, 4); + } + ast->CompleteTagDeclarationDefinition(enumType); + return enumType; +} + +void CangjieDeclMap::CreateAndAddInheritTypeToRecordType(Ptr& ty, CompilerType& enum_type, + CompilerType& instancetiatedType, CompilerType& genericType) { + TypeSystemClang* ast = this->GetTypeSystem(); + auto ti_type = ast->GetBasicType(lldb::eBasicTypeVoid).GetPointerType(); + std::string enum_name(enum_type.GetTypeName().AsCString()); + std::vector> bases; // DW_TAG_inheritance + for (size_t i = 0; i < genericType.GetNumDirectBaseClasses(); i++) { + CompilerType ctorFuncType = genericType.GetDirectBaseClassAtIndex(i, nullptr); + auto ctor_name = enum_name + "_ctor_" + std::to_string(i); + auto ctor_type = ast->CreateRecordType(nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + ctor_name.c_str(), clang::TTK_Struct, lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(ctor_type); + ast->AddFieldToRecordType(ctor_type, "$ti*", ti_type, lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(ctor_type, "constructor", enum_type, lldb::eAccessPublic, 0); + + Ptr decl = AST::Ty::GetDeclOfTy(ty); + auto& enumDecl = RawStaticCast(decl)->constructors; + auto ctorDecl = Ptr(RawStaticCast(enumDecl[i].get())); + size_t paramSize = 0; + if (ctorDecl->astKind == ASTKind::FUNC_DECL) { + auto& params = ctorDecl->funcBody->paramLists[0]->params; + paramSize = params.size(); + } + std::string member_name; + std::vector instantiatedNames = builder.SplitCollectionName(enum_name); + for (size_t j = 0; j < paramSize; j++) { + auto subTy = ctorDecl->funcBody->paramLists[0]->params[j].get()->ty; + CompilerType ctor_para_type = ctorFuncType.GetFieldAtIndex(j + 1, member_name, nullptr, nullptr, nullptr); + auto tempGenericType = ctor_para_type; + if (subTy->IsGeneric()) { + // case generic type. + auto index = GetSubGenericTyIndex(decl, subTy->name); + std::string subTypeName = instantiatedNames[index]; + subTy = ty->typeArgs[index]; + // Find generic type by name. + ctor_para_type = GetDynamicTypeFromTy(subTy, subTypeName, tempGenericType); + } + std::string ctor_para_name = "arg_" + std::to_string(j + 1); + auto argType = builder.IsRefType(subTy) ? ctor_para_type.GetPointerType() : ctor_para_type; + ast->AddFieldToRecordType(ctor_type, ctor_para_name.c_str(), argType, lldb::eAccessPublic, 0); + } + + ast->CompleteTagDeclarationDefinition(ctor_type); + auto inherit_type = ast->CreateBaseClassSpecifier(ctor_type.GetOpaqueQualType(), lldb::eAccessPublic, false, true); + auto type_source_info = inherit_type->getTypeSourceInfo(); + if (type_source_info) { + auto type = ast->GetType(type_source_info->getType()); + ast->StartTagDeclarationDefinition(type); + ast->CompleteTagDeclarationDefinition(type); + } + bases.push_back(std::move(inherit_type)); + } + ast->TransferBaseClasses(instancetiatedType.GetOpaqueQualType(), std::move(bases)); +} + +CompilerType CangjieDeclMap::GetDynamicEnumType(Ptr& ty, std::string typeName, CompilerType& genericType) { + std::string genericName = genericType.GetTypeName().AsCString(); + EnumLayout enumKind = GetEnumLayout(genericName); + // case: enum without args , compilerType is enumType. + auto enum_type = GetEnumType(ty, typeName, genericType); + if (enumKind == EnumLayout::E0Int) { + return enum_type; + } + // case: enum with args , compilerType is struct type with ctor. + auto pos = typeName.find(PACKAGE_SUFFIX) + PACKAGE_SUFFIX.size(); + CJC_ASSERT(pos != std::string::npos); + auto enumPrefix = GetEnumPrefix(genericName); + typeName = typeName.substr(0, pos) + enumPrefix + typeName.substr(pos); + + TypeSystemClang* ast = this->GetTypeSystem(); + CompilerType dynamic_type = ast->CreateRecordType(nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + ConstString(typeName).GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(dynamic_type); + if (enumKind == EnumLayout::E2OptionLike) { + ast->AddFieldToRecordType(dynamic_type, "constructor", enum_type, lldb::eAccessPublic, 0); + auto args = builder.GetInstantiatedParamDeclName(typeName); + auto valType = GetDynamicTypeFromTy(ty->typeArgs[0], args[0], genericType); + ast->AddFieldToRecordType(dynamic_type, "val", valType, lldb::eAccessPublic, 0); + } else { + // add typeinfo*. + auto ti_type = ast->GetBasicType(lldb::eBasicTypeVoid).GetPointerType(); + ast->AddFieldToRecordType(dynamic_type, "$ti*", ti_type, lldb::eAccessPublic, 0); + // E1Arg and E3ArgNoRef, set inheritance. + CreateAndAddInheritTypeToRecordType(ty, enum_type, dynamic_type, genericType); + } + ast->CompleteTagDeclarationDefinition(dynamic_type); + return dynamic_type; +} + +lldb_private::CangjieDeclMap::EnumLayout CangjieDeclMap::GetEnumLayout(const std::string& eName) { + std::string name = eName; + name = GetSubNameWithoutPkgname(name, m_current_pkgname); + if (name.empty()) { + name = eName; + } + if (name.find(E0_PREFIX_NAME) == 0) { + return EnumLayout::E0Int; + } + if (name.find(E2_PREFIX_NAME_OPTION_LIKE) == 0) { + return EnumLayout::E2OptionLike; + } + if (name.find(E3_PREFIX_NAME) == 0) { + return EnumLayout::E3ArgNoRef; + } + return EnumLayout::E1Arg; +} + +CompilerType CangjieDeclMap::GetDynamicFuncType(Ptr& ty, std::string typeName, CompilerType& genericType) { + CJC_ASSERT(ty->IsFunc()); + TypeSystemClang* ast = this->GetTypeSystem(); + auto funcTy = RawStaticCast(ty.get()); + typeName.erase(std::remove(typeName.begin(), typeName.end(), ' '), typeName.end()); + CompilerType dynamic_type = ast->CreateRecordType(nullptr, lldb_private::OptionalClangModuleID(), + lldb::eAccessPublic, ConstString(typeName).GetStringRef(), + clang::TTK_Class, lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(dynamic_type); + // Add typeinfo* to class member. + CompilerType ti_type = ast->GetBasicType(lldb::eBasicTypeVoid).GetPointerType(); + ast->AddFieldToRecordType(dynamic_type, "$ti*", ti_type, lldb::eAccessPublic, 0); + // The first element of the type_args, contains information about the return type. + Ptr retTy = Ptr(funcTy->retTy.get()); + std::string retName = retTy->String(); + lldb_private::CompilerType return_type = GetDynamicTypeFromGenericTypeInfo(retTy, retName); + std::vector param_types; + // para_typeinfo: type_args[1..type_arg_num-1] + for (auto& param: funcTy->paramTys) { + Ptr tmpParam = Ptr(param.get()); + std::string tmpName = param->String(); + param_types.push_back(GetDynamicTypeFromGenericTypeInfo(tmpParam, tmpName)); + } + // type -> subroutine -> pointer + unsigned type_quals = 0; + auto subroutine_type = ast->CreateFunctionType(return_type, param_types.data(), param_types.size(), + false, type_quals, clang::CC_C); + ast->AddFieldToRecordType(dynamic_type, "ptr", subroutine_type.GetPointerType(), lldb::eAccessPublic, 0); + ast->CompleteTagDeclarationDefinition(dynamic_type); + return dynamic_type; +} + +CompilerType CangjieDeclMap::GetDynamicClassType(Ptr& ty, std::string typeName, CompilerType& genericType) { + Ptr decl = AST::Ty::GetDeclOfTy(ty); + CJC_ASSERT(decl->generic && !decl->generic->typeParameters.empty()); + + lldb_private::CompilerType type = this->GetTypeSystem()->CreateRecordType(nullptr, lldb_private::OptionalClangModuleID(), lldb::eAccessPublic, + lldb_private::ConstString(typeName).GetStringRef(), clang::TTK_Class, lldb::eLanguageTypeC); + this->GetTypeSystem()->StartTagDeclarationDefinition(type); + // Add typeinfo* to class member. + CompilerType ti_type = this->GetTypeSystem()->GetBasicType(lldb::eBasicTypeVoid).GetPointerType(); + this->GetTypeSystem()->AddFieldToRecordType(type, "$ti*", ti_type, lldb::eAccessPublic, 0); + + AddFieldToRecordType(type, ty, typeName, genericType); + this->GetTypeSystem()->CompleteTagDeclarationDefinition(type); + return type; +} + +CompilerType CangjieDeclMap::GetDynamicStructType(Ptr& ty, std::string typeName, CompilerType& genericType) { + lldb_private::CompilerType type = this->GetTypeSystem()->CreateRecordType(nullptr, lldb_private::OptionalClangModuleID(), lldb::eAccessPublic, + lldb_private::ConstString(typeName).GetStringRef(), clang::TTK_Struct, lldb::eLanguageTypeC); + this->GetTypeSystem()->StartTagDeclarationDefinition(type); + AddFieldToRecordType(type, ty, typeName, genericType); + this->GetTypeSystem()->CompleteTagDeclarationDefinition(type); + return type; +} + +CompilerType CangjieDeclMap::GetDynamicTupleType(Ptr& ty, std::string typeName, CompilerType& genericType) +{ + CompilerType instancedType = this->GetTypeSystem()->CreateRecordType(nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + lldb_private::ConstString(typeName).GetStringRef(), clang::TTK_Struct, lldb::eLanguageTypeC); + this->GetTypeSystem()->StartTagDeclarationDefinition(instancedType); + auto tupleTy = RawStaticCast(ty.get()); + std::vector instantiatedNames = builder.SplitTupleName(typeName); + CJC_ASSERT(instantiatedNames.size() == tupleTy->typeArgs.size()); + for (size_t i = 0; i < tupleTy->typeArgs.size(); i++) { + auto field_type = GetDynamicTypeFromTy(tupleTy->typeArgs[i], instantiatedNames[i], genericType); + std::string field_name = "_" + std::to_string(i); + this->GetTypeSystem()->AddFieldToRecordType(instancedType, field_name.c_str(), field_type, lldb::eAccessPublic, 0); + } + this->GetTypeSystem()->CompleteTagDeclarationDefinition(instancedType); + return instancedType; +} + +CompilerType CangjieDeclMap::GetDynamicRawArrayType(Ptr& ty, std::string typeName, CompilerType& genericType) { + auto tyName = ty->String(); + Ptr decl = AST::Ty::GetDeclOfTy(ty); + if (decl) { + auto demangle_info = Cangjie::Demangle(decl->mangledName); + std::string pkgname = demangle_info.GetPkgName(); + std::string fullname = demangle_info.GetFullName(); + tyName = pkgname.empty() ? fullname : pkgname + std::string(PACKAGE_SUFFIX) + fullname; + typeName = std::string("RawArray<") + tyName + std::string(">"); + } else { + typeName = std::string("RawArray<") + typeName + std::string(">"); + } + CompilerType type = this->GetTypeSystem()->CreateRecordType(nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + ConstString(typeName).GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); + this->GetTypeSystem()->StartTagDeclarationDefinition(type); + CompilerType basic_type = this->GetTypeSystem()->GetBasicType(lldb::eBasicTypeLongLong).CreateTypedef( + "Int64", this->GetTypeSystem()->CreateDeclContext(this->GetTypeSystem()->GetTranslationUnitDecl()), 0); + // Add _typeinfo to raw-array member. + this->GetTypeSystem()->AddFieldToRecordType(type, "_typeinfo", basic_type.GetPointerType(), lldb::eAccessPublic, 0); + // Add size to raw-array member. + this->GetTypeSystem()->AddFieldToRecordType(type, "size", basic_type, lldb::eAccessPublic, 0); + + // Add elements to raw-array member. + CompilerType field_type; + // Reftype may contain enum. + if (builder.IsRefType(ty)) { + field_type = GetDynamicTypeFromTy(ty, tyName, genericType).GetPointerType(); + } else { + field_type = GetDynamicTypeFromTy(ty, tyName, genericType); + } + this->GetTypeSystem()->AddFieldToRecordType(type, "elements", field_type, lldb::eAccessPublic, 0); + this->GetTypeSystem()->CompleteTagDeclarationDefinition(type); + return type; +} + +CompilerType CangjieDeclMap::GetDynamicArrayType(Ptr& ty, std::string typeName, CompilerType& genericType) { + Ptr decl = AST::Ty::GetDeclOfTy(ty); + if (decl) { + auto demangle_info = Cangjie::Demangle(decl->mangledName); + std::string pkgname = demangle_info.GetPkgName(); + std::string fullname = demangle_info.GetFullName(); + typeName = pkgname.empty() ? fullname : pkgname + std::string(PACKAGE_SUFFIX) + fullname; + } + lldb_private::CompilerType type = this->GetTypeSystem()->CreateRecordType(nullptr, lldb_private::OptionalClangModuleID(), lldb::eAccessPublic, + ConstString(typeName).GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); + this->GetTypeSystem()->StartTagDeclarationDefinition(type); + + auto arrayTy = RawStaticCast(ty.get()); + CJC_ASSERT(arrayTy->typeArgs.size() == 1); + // public struct Array { + // let rawptr: RawArray + // let start: Int64 + // let len: Int64 + // ... + // } + // Add elements to array member. + std::vector instantiatedNames = builder.SplitCollectionName(typeName); + auto field_type = GetDynamicRawArrayType(arrayTy->typeArgs[0], instantiatedNames[0], genericType).GetPointerType(); + this->GetTypeSystem()->AddFieldToRecordType(type, "rawptr", field_type, lldb::eAccessPublic, 0); + + CompilerType basic_type = this->GetTypeSystem()->GetBasicType(lldb::eBasicTypeLongLong).CreateTypedef( + "Int64", this->GetTypeSystem()->CreateDeclContext(this->GetTypeSystem()->GetTranslationUnitDecl()), 0); + this->GetTypeSystem()->AddFieldToRecordType(type, "start", basic_type, lldb::eAccessPublic, 0); + this->GetTypeSystem()->AddFieldToRecordType(type, "len", basic_type, lldb::eAccessPublic, 0); + this->GetTypeSystem()->CompleteTagDeclarationDefinition(type); + return type; +} + +size_t CangjieDeclMap::GetSubGenericTyIndex(const Ptr& decl, std::string typeName) +{ + CJC_ASSERT(decl->generic && !decl->generic->typeParameters.empty()); + if (!decl->generic || decl->generic->typeParameters.empty()){ + return 0; + } + // Get generic decl index. + size_t index = 0; + for (size_t i = 0; i < decl->generic->typeParameters.size(); i++) { + if (decl->generic->typeParameters[i]->identifier.Val() == typeName) { + index = i; + break; + } + } + return index; +} diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieDeclMap.h b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieDeclMap.h new file mode 100644 index 000000000..58b19c2ec --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieDeclMap.h @@ -0,0 +1,205 @@ +//===-- CangjieDeclMap.h --------------------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_DECL_MAP_H +#define LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_DECL_MAP_H + + +#include "lldb/lldb-private.h" +#include "lldb/Symbol/CompilerType.h" +#include "lldb/Target/ExecutionContext.h" +#include "CangjieASTUtils.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" + +using namespace Cangjie; +using namespace Cangjie::AST; + +namespace lldb_private { + +class CangjieDeclMap { + typedef std::pair NamespaceMapItem; + typedef std::vector NamespaceMap; + typedef std::shared_ptr NamespaceMapSP; + +public: + CangjieDeclMap(lldb_private::ExecutionContext exe_ctx, + std::vector identifiers, unsigned int fileID, std::string fileName) + : m_exe_ctx(exe_ctx), m_identifys(identifiers), m_file_id(fileID), m_current_filename(fileName) { + InitVariable(); + }; + enum class DeclKind{ + VarDecl, + FuncDecl, + UserDefinedDecl, + BuiltInDecl, + OtherDecl + }; + enum class VariableKind{ + LocalVariable, + StaticVariable, + GlobalVariable + }; + enum class EnumLayout { + E0Int, + E1Arg, + E2OptionLike, + E3ArgNoRef, + }; + EnumLayout GetEnumLayout(const std::string& name); + struct CompilerTypeInfo { + CompilerTypeInfo(CompilerType type, uint32_t line, std::string mangledName) + : Type(type), Line(line), mangle_name(mangledName){}; + void SetDeclInfo(DeclKind kind, std::string id, bool global, std::string gn = "", bool isLet = false) { + this->Kind = kind; + this->name = id; + this->Global = global; + this->IsLet = isLet; + this->genericName = gn; + } + CompilerType Type; + uint32_t Line; + std::string mangle_name; + std::string name = ""; + std::string genericName = ""; // for generic memberFunc + bool Global = true; + DeclKind Kind; + bool IsLet = false; + }; + std::vector> CreateExternalAST(); + TypeSystemClang * GetTypeSystem() { return m_type_system; } + bool LookUpPackage(ConstString name); + CompilerType LookUptype(lldb::ModuleSP module, + ConstString name, const CompilerDeclContext &namespace_decl); + CompilerType LookUpType(std::string name); + std::vector LookUpFunction(lldb::ModuleSP module, + ConstString name, const CompilerDeclContext &namespace_decl); + CompilerType LookUpGlobalVariable(lldb::ModuleSP module, + ConstString name, const CompilerDeclContext &namespace_decl); + std::string GetFuncNameFromComplierType(const CompilerType& funcType); + lldb::VariableSP FindParsedVarByName(lldb_private::ConstString name); + lldb_private::CompilerType FindParsedTypesByName(std::string name); + CompilerType GetPrimitiveTypeByName(std::string &name); + bool IsInterfaceType(CompilerType& type); + bool IsInstantiatedOfGenericDecl(std::string name); + CompilerType GetDynamicTypeFromTy(Ptr& ty, std::string typeName, CompilerType& genericType); + lldb_private::CompilerType GetDynamicTypeFromGenericTypeInfo(Ptr& ty, std::string& demangled_name); + CompilerType GetDynamicClassType(Ptr& ty, std::string typeName, CompilerType& genericType); + CompilerType GetDynamicFuncType(Ptr& ty, std::string typeName, CompilerType& genericType); + CompilerType GetDynamicStructType(Ptr& ty, std::string typeName, CompilerType& genericType); + CompilerType GetDynamicInterfaceType(Ptr& ty, std::string typeName, CompilerType& genericType); + CompilerType GetDynamicTupleType(Ptr& ty, std::string typeName, CompilerType& genericType); + CompilerType GetDynamicEnumType(Ptr& ty, std::string typeName, CompilerType& genericType); + CompilerType GetEnumType(Ptr& ty, std::string typeName, CompilerType& genericType); + void CreateAndAddInheritTypeToRecordType(Ptr& ty, CompilerType& enum_type, CompilerType& instancetiateType, CompilerType& generic_type); + + CompilerType GetDynamicArrayType(Ptr& ty, std::string typeName, CompilerType& genericType); + CompilerType GetDynamicRawArrayType(Ptr& ty, std::string typeName, CompilerType& genericType); + void AddFieldToRecordType(CompilerType& instancedType, Ptr& ty, std::string& resultTypeName, CompilerType& genericType); + Ptr GetMemberDeclTyByName(Ptr& ty, std::string& memberName, std::string& parentTypeName); + size_t GetSubGenericTyIndex(const Ptr& decl, std::string memberName); + + lldb_private::CompilerType dynamic_ype; + struct ParsedFunction { + ParsedFunction(std::string fscope, std::string fName, std::string func_type, lldb_private::CompilerType type) + : ScopeName(fscope), FuncName(fName), FuncTypeStr(func_type), funcType(type){}; + std::string ScopeName; + std::string FuncName; + std::string FuncTypeStr; + lldb_private::CompilerType funcType; + }; + std::vector m_parsed_functions; + std::string m_current_pkgname; + +private: + lldb_private::CangjieASTBuiler builder; + lldb_private::ExecutionContext m_exe_ctx; + std::vector m_identifys; + unsigned int m_file_id; + std::string m_current_filename; + lldb::ValueObjectSP m_this_variable; + lldb::VariableListSP m_local_variables; + lldb::VariableListSP m_global_variables; + lldb::VariableListSP m_static_variables; + + std::map m_generic_types; + std::vector> m_local_decls; + std::vector> m_global_decls; + std::vector> m_captured_vars; + std::set m_fullpkgs; + std::map m_parsed_packages; + std::map m_parsed_vars; + std::map m_parsed_types; + std::map m_captured_var_addrs; + + TypeSystemClang * m_type_system = nullptr; + + void InitVariable(); + void CreateInjectedThis(); + void CreateInjectedSuper(Ptr& thisdecl); + void LookUpTypeByName(const std::string& id, std::vector& decls); + lldb::VariableSP GetVariableByName(const std::string& sname, VariableKind kind); + CompilerType GetVariableType(lldb::VariableSP var); + void CollectVarDecl(const std::string& sname, CompilerType& type, VariableKind kind, + std::vector& m_translate_decls); + CompilerType LookUpVariable(const std::string& name, VariableKind kind); + void LookUpMemberVariable(CompilerType& type, std::vector& m_translate_decls); + std::vector LookUpFunction(std::string name, bool find_global); + OwnedPtr CreateVariable(const CompilerType& type, std::string& name, Ptr decl = nullptr); + void CreateMemberDecls(CompilerType type, Ptr &decl, std::vector>& members); + void CreateMemberVarDecls(CompilerType& type, Ptr &decl, std::vector>& members); + void CreateMemberFuncDecls(CompilerType& type, Ptr &decl, std::vector>& members); + void CreateStaticVarDecls(CompilerType& type, Ptr &decl, std::vector>& members); + void CreateClassMemberDecls(CompilerType type, Ptr &decl); + bool IsGenericTypeWithConstraints(CompilerType& type); + OwnedPtr CreateGenericConstraintsDecl(Ptr &decl, CompilerType& type); + void CreateGenericConstraints(Ptr& decl, CompilerType& type); + std::set GetGenericTypeWithConstraints(CompilerType& type); + void CreateStructMemberDecls(CompilerType type, Ptr &decl); + void CreateClassLikeParentDecls(CompilerType type, Ptr decl); + void SetGenericDeclBySubclass(Ptr refType, std::string name, Ptr& base); + CompilerType GetSuperClassDefinedType(CompilerType& type); + void SetMemberDeclParent(Ptr funcdecl, Ptr decl); + void CreateInterfaceMemberDecls(Ptr &decl, CompilerType type); + Ptr CreateClassDecl(CompilerType type, std::string prefix); + Ptr CreateStructDecl(CompilerType type, std::string prefix); + Ptr CreateEnumDecl(CompilerType type, std::string prefix); + Ptr CreateInterfaceDecl(CompilerType type, std::string prefix); + Ptr CreateTypeDecl(CompilerType type); + std::vector GetEnumeratorName(CompilerType& type); + CompilerType GetEnumerationType(const CompilerType& type, bool hasArgs); + void ReplaceTypeDefWithInterfaceType(CompilerType& type); + void CacheParsedVars(ConstString name, lldb::VariableSP& var, VariableKind kind); + void CreateEnumConstructors(OwnedPtr &decl, const CompilerType& type); + void CreateEnumOptionLikeCtors(OwnedPtr &decl, const CompilerType& type, + std::vector& list); + bool AddPackageNameSpace(ConstString nam); + bool IsLocalConstVariable(const std::string& name); + void CreateVarDeclByType(const CompilerTypeInfo& type, std::string name); + void CreateDeclByType(const CompilerTypeInfo& type); + void CreateFuncDeclByType(const CompilerTypeInfo& type, std::string name, bool find_global); + void CreateTypeDeclByType(const CompilerType& type); + Ptr GetSameGlobalDecl(OwnedPtr decl, bool insert_begin = false); + void CollectImportPkgName(); + void AddImportSpec(OwnedPtr& file, std::string curPkgName); + void CreatePackage(const std::string &name); + void CollectLocalVariableAddress(const std::string& sname, lldb::VariableSP& var); + bool CheckAndModifyCapturedVardecl(VarDecl& vd, OwnedPtr& paramList); + void CreateCapturedVars(); + std::vector> CreateFakePackage(); + CompilerType GetGenericTypeByPartName(const std::string& part_name); + CompilerType CreateOrGetGenericType(const CompilerType& type); + void CollectGenericTypeOfVariable(const CompilerType& type); + void AddFieldToRecordType(const CompilerType& genericType, CompilerType& instancedType, std::string& name); + std::set m_local_func; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_DECL_MAP_H diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieExpressionParser.cpp new file mode 100644 index 000000000..1ef64be5e --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieExpressionParser.cpp @@ -0,0 +1,290 @@ +//===-- CangjieExpressionParser.cpp ---------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#include "CangjieExpressionParser.h" +#include "CangjieIRForTarget.h" +#include "CangjieCompilerInstance.h" +#include "cangjie/Frontend/CompilerInstance.h" +#include "cangjie/Frontend/CompilerInvocation.h" + +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +using namespace clang; +using namespace llvm; +using namespace lldb_private; +using namespace Cangjie; +using namespace Cangjie::AST; + +CangjieExpressionParser::CangjieExpressionParser( + ExecutionContextScope *exe_scope, Expression &expr, + bool generate_debug_info) + : ExpressionParser(exe_scope, expr, generate_debug_info) {} + +CangjieExpressionParser::~CangjieExpressionParser() = default; + +bool CangjieExpressionParser::Complete(CompletionRequest &request, + unsigned line, unsigned pos, + unsigned typed_pos) { + return true; +} + +void CangjieExpressionParser::ResetDeclMap( + ExecutionContext &exe_ctx, std::vector identifiers, unsigned int fileID, std::string fileName) { + m_expr_decl_map_up = std::make_unique(exe_ctx, identifiers, fileID, fileName); +} + +void CangjieExpressionParser::PrepareContextInfo(ExecutionContext &exeCtx, Cangjie::SourceManager& sm) +{ + auto frame = exeCtx.GetFramePtr(); + if (!frame) { + return; + } + auto sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | lldb::eSymbolContextBlock); + + // Get fileID for diag. + auto file = sym_ctx.GetFunctionStartLineEntry().file; + auto dir = file.GetDirectory(); + auto base = file.GetFilename(); + std::string filePath = std::string(!dir.IsEmpty() ? dir.AsCString() : ""); + std::string fileName = std::string(!base.IsEmpty() ? base.AsCString() : ""); + m_current_filename = fileName; + auto path = FileUtil::JoinPath(filePath, fileName); + std::string failedReason; + auto content = FileUtil::ReadFileContent(path, failedReason); + if (content.has_value()) { + m_file_id = sm.AddSource(path, content.value()); + } + + // Find this variable if in the context of a class. + auto thisVar = frame->FindVariable(ConstString("this")); + if (!thisVar) { + return; + } + m_in_class_context = true; + if (sym_ctx.function) { + std::string classname = thisVar->GetTypeName().GetCString(); + std::string funcscope = sym_ctx.function->GetNameNoArguments().GetCString(); + auto pos = funcscope.rfind("::"); + auto funcname = (pos == std::string::npos) ? funcscope: funcscope.substr(pos + 2); + m_in_constructor = (funcname == "init" || funcname == classname); + } + return; +} + +static void SetTripleInfoIfNeed(ArchSpec target_arch, Cangjie::Triple::Info &target_triple) { + std::string triple = target_arch.GetTriple().str(); + if (target_arch.GetTriple().getArch() == llvm::Triple::x86_64) { + target_triple.arch = Cangjie::Triple::ArchType::X86_64; + } else { + target_triple.arch = Cangjie::Triple::ArchType::AARCH64; + } + + auto os = target_arch.GetTriple().getOS(); + if (os == llvm::Triple::Linux) { + target_triple.os = Cangjie::Triple::OSType::LINUX; + } else if (os == llvm::Triple::Win32) { + target_triple.os = Cangjie::Triple::OSType::WINDOWS; + } else if (os == llvm::Triple::Darwin || os == llvm::Triple::MacOSX) { + target_triple.os = Cangjie::Triple::OSType::DARWIN; + target_triple.vendor = Cangjie::Triple::Vendor::APPLE; + target_triple.env = Cangjie::Triple::Environment::NOT_AVAILABLE; + return; + } else if (os == llvm::Triple::IOS) { + target_triple.os = Cangjie::Triple::OSType::IOS; + target_triple.env = Cangjie::Triple::Environment::SIMULATOR; + target_triple.vendor = Cangjie::Triple::Vendor::APPLE; + return; + } else { + target_triple.os = Cangjie::Triple::OSType::UNKNOWN; + } + auto env = target_arch.GetTriple().getEnvironment(); + if (env == llvm::Triple::GNU) { + target_triple.env = Cangjie::Triple::Environment::GNU; + } else if (env == llvm::Triple::Android) { + target_triple.env = Cangjie::Triple::Environment::ANDROID; + } else { + target_triple.env = Cangjie::Triple::Environment::OHOS; + } + target_triple.vendor = Cangjie::Triple::Vendor::UNKNOWN; + return; +} + +bool CangjieExpressionParser::Parse(ExecutionContext &exeCtx, const std::string text) { + Log *log = GetLog(LLDBLog::Expressions); + std::string cangjieExprTempFile = "./expr.temp"; + std::unique_ptr invocation = std::make_unique(); + auto cangjie_home = getenv("CANGJIE_HOME"); + if (cangjie_home == nullptr) { + LLDB_LOGF(log, "CANGJIE_HOME is not set!"); + return false; + } + auto cangjie_path = getenv("CANGJIE_PATH"); + if (cangjie_path != nullptr) { + std::stringstream sstream(cangjie_path); + std::string tmp_path; + while (std::getline(sstream, tmp_path, ':')) { + LLDB_LOGF(log, "add [%s] to env CANGJIE_PATH\n", tmp_path.c_str()); + invocation->globalOptions.environment.cangjiePaths.emplace_back(tmp_path); + } + } + Cangjie::Triple::Info target_triple; + ArchSpec target_arch = exeCtx.GetTargetSP()->GetArchitecture(); + auto triple = target_arch.GetTriple(); + SetTripleInfoIfNeed(target_arch, target_triple); + LLDB_LOGF(log, "using target triple %s %s %s %s %s\n", triple.str().c_str(), + triple.getArchName().data(), triple.getVendorName().data(), + triple.getOSName().data(), triple.getEnvironmentName().data()); + if (target_triple.env == Cangjie::Triple::Environment::ANDROID) { + target_triple.apiLevel = "31"; + } + invocation->globalOptions.target = target_triple; + invocation->globalOptions.environment.cangjieHome = cangjie_home; + invocation->globalOptions.srcFiles.push_back(cangjieExprTempFile); + invocation->globalOptions.outputMode = GlobalOptions::OutputMode::STATIC_LIB; + invocation->globalOptions.overflowStrategy = OverflowStrategy::WRAPPING; + invocation->globalOptions.implicitPrelude = true; + invocation->globalOptions.chirLLVM = true; + invocation->globalOptions.aggressiveParallelCompile = 1; + invocation->globalOptions.disableInstantiation = true; + + DiagnosticEngine diag; + diag.SetDisableWarning(true); + std::unique_ptr instance = + std::make_unique(*invocation, diag); + instance->bufferCache.emplace(cangjieExprTempFile, text); + instance->loadSrcFilesFromCache = true; + instance->PerformParse(); + if (diag.GetErrorCount() > 0) { + // There was a syntax parsing error in the user string. + return false; + } + + PrepareContextInfo(exeCtx, instance->GetSourceManager()); + + // If user string is `expr a + b`, the refStrs is {a, b}. + auto refStrs = instance->WalkAndCollectIdentifiers(); + + // Create fake packages. + ResetDeclMap(exeCtx, refStrs, m_file_id, m_current_filename); + instance->m_fake_packages = m_expr_decl_map_up->CreateExternalAST(); + + instance->PerformSuperExpression(m_in_class_context, m_in_constructor, m_in_static_method); + if (diag.GetErrorCount() > 0) { + return false; + } + if (!instance->PerformImportPackageForCjdb()) { + return false; + } + instance->PerformSema(); + if (diag.GetErrorCount() > 0) { + return false; + } + instance->PerformAfterSemaForCjdb(); + m_search_func_name = instance->IsUserLookupFunction(); + if (m_search_func_name) { + return false; + } + instance->PerformDesugarAfterSema(); + instance->PerformGenericInstantiation(); + instance->PerformOverflowStrategy(); + instance->PerformMangling(); + instance->PerformCHIRCompilation(); + instance->PerformCodeGen(); + m_module = instance->ReleaseModule(); + if (m_module == nullptr) { + LLDB_LOGF(log, "m_module is null"); + return false; + } + m_result_type_name = instance->m_result_type_name; + std::string demangled_name = lldb_private::Mangled::GetDemangledTypeName(m_result_type_name); + if (!instance->m_expr_result->ty->typeArgs.empty()) { + std::string typeName = GetSubNameWithoutPkgname(demangled_name, m_expr_decl_map_up->m_current_pkgname); + if (typeName.empty()) { + typeName = demangled_name; + } + auto retType = m_expr_decl_map_up->LookUpType(typeName); + if (retType.IsValid() && retType.GetTypeClass() != lldb::eTypeClassEnumeration) { + m_expr_result_type = retType; + } else { + m_expr_result_type = m_expr_decl_map_up->GetDynamicTypeFromGenericTypeInfo( + instance->m_expr_result->ty, demangled_name); + } + } + return true; +} + +bool CangjieExpressionParser::RewriteExpression( + DiagnosticManager &diagnostic_manager) { + return true; +} + +lldb_private::Status CangjieExpressionParser::PrepareForExecution( + lldb::addr_t &func_addr, lldb::addr_t &func_end, + lldb::IRExecutionUnitSP &execution_unit_sp, ExecutionContext &exe_ctx, + bool &can_interpret, ExecutionPolicy execution_policy) { + Log *log = GetLog(LLDBLog::Expressions); + lldb_private::Status err; + std::unique_ptr context_up(&m_module->getContext()); + lldb::TargetSP target_sp = exe_ctx.GetTargetSP(); + + SymbolContext sym_ctx; + if (lldb::StackFrameSP frame_sp = exe_ctx.GetFrameSP()) { + sym_ctx = frame_sp->GetSymbolContext(lldb::eSymbolContextEverything); + } else if (lldb::TargetSP target_sp = exe_ctx.GetTargetSP()) { + sym_ctx.target_sp = target_sp; + } + + lldb_private::ConstString function_name("_CN4expr11__lldb_exprHPu"); + std::vector cpu_features; + execution_unit_sp = std::make_shared( + context_up, m_module, function_name, target_sp, sym_ctx, cpu_features); + + if (log) { + std::string s; + llvm::raw_string_ostream oss(s); + execution_unit_sp->GetModule()->print(oss, nullptr); + oss.flush(); + LLDB_LOGF(log, "module in IRExecutionUnit: \n%s", s.c_str()); + } + CangjieIRForTarget ir_for_execute(m_materializer, execution_unit_sp->GetModule(), + function_name, m_expr_decl_map_up.get(), exe_ctx); + std::string error_type_name = "Int64"; + ir_for_execute.m_expr_result_type = m_expr_result_type; + ir_for_execute.CreateResultVariable(m_result_delegate, m_result_type_name, + m_in_member_method, ConstString("__lldb_expr_result")); + ir_for_execute.CreateResultVariable(m_error_result_delegate, error_type_name, + m_in_member_method, ConstString("__lldb_error_result")); + ir_for_execute.HandleIRForExecute(); + execution_unit_sp->GetRunnableInfo(err, func_addr, func_end); + if (err.Fail()) { + return err; + } + + m_materialized_address = execution_unit_sp->Malloc( + m_materializer->GetStructByteSize(), + m_materializer->GetStructAlignment(), + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + IRMemoryMap::eAllocationPolicyMirror, false, err); + return err; +} + +void CangjieExpressionParser::PrepareParse( + lldb_private::Materializer *materializer, + lldb_private::Materializer::PersistentVariableDelegate *result_delegate, + lldb_private::Materializer::PersistentVariableDelegate *error_result_delegate, + bool is_static_method) { + m_materializer = materializer; + m_result_delegate = result_delegate; + m_error_result_delegate = error_result_delegate; + m_in_static_method = is_static_method; +} diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieExpressionParser.h b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieExpressionParser.h new file mode 100644 index 000000000..18bfba3ea --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieExpressionParser.h @@ -0,0 +1,96 @@ +//===-- CangjieExpressionParser.h -----------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_CANGJIEEXPRESSIONPARSER_H +#define LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_CANGJIEEXPRESSIONPARSER_H + +#include "CangjieDeclMap.h" + +#include "cangjie/Basic/SourceManager.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/ExpressionParser.h" +#include "llvm/IR/Module.h" +#include +#include + +namespace llvm { +class LLVMContext; +} + +namespace lldb_private { + +class IRExecutionUnit; +class TypeSystemClang; + +class CangjieExpressionParser : public ExpressionParser { +public: + CangjieExpressionParser(ExecutionContextScope *exe_scope, Expression &expr, + bool generate_debug_info); + ~CangjieExpressionParser() override; + + bool Complete(CompletionRequest &request, unsigned line, unsigned pos, + unsigned typed_pos) override; + + bool Parse(ExecutionContext &exe_ctx, const std::string text); + + bool RewriteExpression(DiagnosticManager &diagnostic_manager) override; + + Status + PrepareForExecution(lldb::addr_t &func_addr, lldb::addr_t &func_end, + lldb::IRExecutionUnitSP &execution_unit_sp, + ExecutionContext &exe_ctx, bool &can_interpret, + lldb_private::ExecutionPolicy execution_policy) override; + + void ResetDeclMap(ExecutionContext &exe_ctx, + std::vector identifiers, unsigned int fileID, + std::string fileName); + void PrepareContextInfo(ExecutionContext &exeCtx, Cangjie::SourceManager& sm); + void PrepareParse(lldb_private::Materializer *materializer, + lldb_private::Materializer::PersistentVariableDelegate *result_delegate, + lldb_private::Materializer::PersistentVariableDelegate *error_result_delegate, + bool is_static_method); + std::unique_ptr m_expr_decl_map_up; + lldb::addr_t m_materialized_address; + /// True if it was parsed when exe_ctx was in a non-static method. + bool m_in_member_method = false; +private: + /// The context to use for IR generation. + std::unique_ptr m_llvm_context; + /// The module to build IR into. + std::unique_ptr m_module; + /// The container for the IR, to be JIT-compiled or interpreted. + lldb::IRExecutionUnitSP m_execution_unit_sp; + /// The AST context to build the expression into. + std::unique_ptr m_ast_context; + + lldb_private::Materializer *m_materializer = nullptr; ///< If non-NULL, the materializer + ///to use when reporting used + ///variables. + lldb_private::Materializer::PersistentVariableDelegate + *m_result_delegate; ///< If non-NULL, used to report expression results to + ///ClangUserExpression. + + lldb_private::Materializer::PersistentVariableDelegate + *m_error_result_delegate; ///< If non-NULL, used to report expression results to + ///ClangUserExpression. + std::vector m_include_directories; + std::string m_result_type_name; + CompilerType m_expr_result_type; + unsigned int m_file_id = 0; + bool m_in_class_context = false; + bool m_in_constructor = false; + bool m_in_static_method = false; + bool m_search_func_name = false; + std::string m_current_filename; +}; + +} // namespace lldb_private +#endif // LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_CANGJIEEXPRESSIONPARSER_H diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieExpressionSourceCode.cpp b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieExpressionSourceCode.cpp new file mode 100644 index 000000000..8d40ec32c --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieExpressionSourceCode.cpp @@ -0,0 +1,82 @@ +//===-- CangjieExpressionSourceCode.cpp -----------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#include "CangjieExpressionSourceCode.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb_private; + +bool CangjieExpressionSourceCode::GetText( + std::string &text, ExecutionContext &exe_ctx, bool add_locals, + bool force_add_all_locals, llvm::ArrayRef modules) const { + StreamString lldb_local_var_decls; + if (m_wrap) { + // Generate a list of @import statements that will import the specified + // module into our expression. + std::string module_imports = {}; + for (const std::string &module : modules) { + module_imports.append(module); + module_imports.append("\n"); + } + StreamString wrap_stream; + // First construct a tagged form of the user expression so we can find it + // later: + switch (m_wrap_kind) { + case WrapKind::Function: + wrap_stream.Printf( + "package expr\n" + "%s\n" + "@OverflowThrowing\n" + "public func __lldb_expr(__lldb_arg: CPointer): Int64 {\n" + " try {\n" + " var __lldb_expr_result = unsafe {\n" + " %s \n" + " }\n" + " } catch (e: Exception) {\n" + " println(e.toString()) \n" + " var __lldb_error_result = -1\n" + " -1\n" + " }\n" + " 0\n" + "}\n", + module_imports.c_str(), m_body.c_str()); + break; + case WrapKind::MemberFunction: + wrap_stream.Printf( + "package expr\n" + "%s\n" + "extend %s {\n" + " @OverflowThrowing\n" + " public func __lldb_wrapped_expr(__lldb_arg: CPointer): Int64 {\n" + " try {\n" + " var __lldb_expr_result = unsafe {\n" + " %s \n" + " }\n" + " } catch (e: Exception) {\n" + " println(e.toString()) \n" + " var __lldb_error_result = -1\n" + " -1\n" + " }\n" + " 0\n" + " }\n" + "}\n" + "public func __lldb_expr(__lldb_arg: CPointer): Int64 {\n" + " __lldb_injected_self.__lldb_wrapped_expr(__lldb_arg)\n" + "}\n", + module_imports.c_str(), + m_class_name.c_str(), m_body.c_str()); + break; + } + text = std::string(wrap_stream.GetString()); + } else { + text.append(m_body); + } + return true; +} diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieExpressionSourceCode.h b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieExpressionSourceCode.h new file mode 100644 index 000000000..4d9a88c12 --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieExpressionSourceCode.h @@ -0,0 +1,49 @@ +//===-- CangjieExpressionSourceCode.h -------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_CANGJIEEXPRESSIONSOUECECODE_H +#define LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_CANGJIEEXPRESSIONSOUECECODE_H +#include "lldb/Expression/Expression.h" +#include "lldb/Expression/ExpressionSourceCode.h" + +namespace lldb_private { +class CangjieExpressionSourceCode : public ExpressionSourceCode { +public: + /// The possible ways an expression can be wrapped. + enum class WrapKind { + /// Wrapped in a function. + Function, + /// Wrapped in a non-static member function of a cj class. + MemberFunction, + }; + + static CangjieExpressionSourceCode *CreateWrapped(llvm::StringRef prefix, + llvm::StringRef body, + WrapKind wrap_kind, + llvm::StringRef classname) { + return new CangjieExpressionSourceCode("$__lldb_expr", prefix, body, Wrap, wrap_kind, classname); + } + + bool GetText(std::string &text, ExecutionContext &exe_ctx, bool add_locals, + bool force_add_all_locals, + llvm::ArrayRef modules) const; + +private: + CangjieExpressionSourceCode( + llvm::StringRef name, llvm::StringRef prefix, llvm::StringRef body, + Wrapping wrap, WrapKind wrap_kind, llvm::StringRef classname): + ExpressionSourceCode(name, prefix, body, wrap), m_wrap_kind(wrap_kind), m_class_name(classname) {}; + + /// How the expression has been wrapped. + const WrapKind m_wrap_kind; + std::string m_class_name; +}; +} // namespace lldb_private +#endif // LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_CANGJIEEXPRESSIONSOUECECODE_H diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieIRForTarget.cpp b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieIRForTarget.cpp new file mode 100644 index 000000000..313a9af43 --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieIRForTarget.cpp @@ -0,0 +1,553 @@ +//===-- CangjieIRForTarget.cpp --------------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#include "CangjieIRForTarget.h" +#include "CangjieDeclMap.h" + +#include "llvm/IR/Constants.h" +#include "llvm/IR/Operator.h" +#include "llvm/IR/Instructions.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +using namespace llvm; +using lldb_private::LLDBLog; + +CangjieIRForTarget::FunctionValueCache::FunctionValueCache(Maker const &maker) + : m_maker(maker), m_values() {} + +CangjieIRForTarget::FunctionValueCache::~FunctionValueCache() = default; + +static llvm::Value *FindEntryInstruction(llvm::Function *function) { + if (function->empty()) { + return nullptr; + } + + return function->getEntryBlock().getFirstNonPHIOrDbg(); +} + +llvm::Value * CangjieIRForTarget::FunctionValueCache::GetValue( + llvm::Function *function) { + if (m_values.find(function) == m_values.end()) { + llvm::Value *ret = m_maker(function); + m_values[function] = ret; + return ret; + } else { + return m_values[function]; + } +} + +CangjieIRForTarget::CangjieIRForTarget(lldb_private::Materializer *materializer, + llvm::Module *module, + lldb_private::ConstString func_name, + lldb_private::CangjieDeclMap *decl_map, + lldb_private::ExecutionContext &exe_ctx) + : m_materializer(materializer), m_module(module), m_func_name(func_name), + m_exe_ctx(exe_ctx), m_decl_map(decl_map), m_entry_instruction_finder(FindEntryInstruction) { + m_this_obj_name.SetCString("_CN7default20__lldb_injected_selfE"); + m_super_obj_name.SetCString("_CN7default22$__lldb_injected_superE"); +} + +bool CangjieIRForTarget::ReplaceIRGlobals() { + Function *const llvm_function = + m_func_name.IsEmpty() ? nullptr + : m_module->getFunction(m_func_name.GetStringRef()); + if (!llvm_function) { + return false; + } + + llvm::Function::arg_iterator iter(llvm_function->arg_begin()); + if (iter == llvm_function->arg_end()) { + return false; + } + Argument *argument = &*iter; + while (argument->getName().equals("this") || argument->getName().equals("") || argument->getName().equals("$BP")) { + ++iter; + if (iter == llvm_function->arg_end()) { + return false; + } + argument = &*iter; + } + + llvm::BasicBlock &entry_block(llvm_function->getEntryBlock()); + LLVMContext &context(m_module->getContext()); + llvm::IntegerType *offset_type(llvm::Type::getInt32Ty(context)); + + for (auto it = m_args.begin(); it != m_args.end(); it++) { + llvm::Value *value = it->m_llvm_value; + lldb::offset_t offset = it->m_offset; + bool is_reference = it->m_is_reference; + auto name = it->m_name; + if (name == m_super_obj_name) { + offset = m_args.begin()->m_offset; + } + if (name == m_this_obj_name && m_this_is_replaced) { + continue; + } + if (value) { + FunctionValueCache body_result_maker([this, is_reference, offset_type, offset, argument, + value](llvm::Function *function) -> llvm::Value * { + llvm::Instruction *entry_instruction = llvm::cast( + m_entry_instruction_finder.GetValue(function)); + + llvm::Type *int8Ty = llvm::Type::getInt8Ty(function->getContext()); + ConstantInt *offset_int( + ConstantInt::get(offset_type, offset, true)); + GetElementPtrInst *get_element_ptr = GetElementPtrInst::Create( + int8Ty, argument, offset_int, "", entry_instruction); + if (is_reference) { + BitCastInst *bit_cast = new BitCastInst( + get_element_ptr, value->getType(), "", entry_instruction); + return bit_cast; + } + BitCastInst *bit_cast = new BitCastInst( + get_element_ptr, value->getType()->getPointerTo(), "", + entry_instruction); + + LoadInst *load = new LoadInst(value->getType(), bit_cast, "", + entry_instruction); + return load; + }); + + if (Constant *constant = dyn_cast(value)) { + if (!UnfoldConstant(constant, llvm_function, body_result_maker, + m_entry_instruction_finder)) { + return false; + } + } else if (llvm::Instruction *instruction = dyn_cast(value)) { + if (instruction->getParent()->getParent() != llvm_function) { + return false; + } + value->replaceAllUsesWith( + body_result_maker.GetValue(instruction->getParent()->getParent())); + } else { + return false; + } + if (GlobalVariable *var = dyn_cast(value)) + var->eraseFromParent(); + } + + if (m_replace_this_object && name == m_this_obj_name) { + m_this_is_replaced = true; + m_replace_this_object = false; + break; + } + } + + return true; +} + +bool CangjieIRForTarget::UnfoldConstant(Constant *old_constant, + llvm::Function *llvm_function, + FunctionValueCache &value_maker, + FunctionValueCache &entry_instruction_finder) { + SmallVector users; + + for (llvm::User *u : old_constant->users()) + users.push_back(u); + + for (size_t i = 0; i < users.size(); ++i) { + User *user = users[i]; + + if (llvm::Constant *constant = dyn_cast(user)) { + if (llvm::ConstantExpr *constant_expr = dyn_cast(constant)) { + switch (constant_expr->getOpcode()) { + default: + + return false; + case llvm::Instruction::GetElementPtr: { + // GetElementPtrConstantExpr, OperandList[0] is base, OperandList[1]... are indices + + FunctionValueCache get_element_pointer_maker( + [&value_maker, &entry_instruction_finder, old_constant, + constant_expr](llvm::Function *function) -> llvm::Value * { + auto *gep = cast(constant_expr); + llvm::Value *ptr = gep->getPointerOperand(); + + if (ptr == old_constant) + ptr = value_maker.GetValue(function); + + std::vector index_vector; + for (llvm::Value *operand : gep->indices()) { + if (operand == old_constant) + operand = value_maker.GetValue(function); + + index_vector.push_back(operand); + } + + ArrayRef indices(index_vector); + + return GetElementPtrInst::Create( + gep->getSourceElementType(), ptr, indices, "", + llvm::cast( + entry_instruction_finder.GetValue(function))); + }); + + if (!UnfoldConstant(constant_expr, llvm_function, + get_element_pointer_maker, + entry_instruction_finder)) + return false; + } break; + case llvm::Instruction::BitCast: { + FunctionValueCache bit_cast_maker( + [&value_maker, &entry_instruction_finder, old_constant, + constant_expr](llvm::Function *function) -> llvm::Value * { + // UnaryExpr, OperandList[0] is value + if (old_constant != constant_expr->getOperand(0)) { + return constant_expr; + } + return new BitCastInst( + value_maker.GetValue(function), constant_expr->getType(), + "", llvm::cast( + entry_instruction_finder.GetValue(function))); + }); + + if (!UnfoldConstant(constant_expr, llvm_function, bit_cast_maker, + entry_instruction_finder)) + return false; + } break; + } + } else { + return false; + } + } else { + if (llvm::Instruction *inst = llvm::dyn_cast(user)) { + if (llvm_function && inst->getParent()->getParent() != llvm_function) { + return false; + } + inst->replaceUsesOfWith( + old_constant, value_maker.GetValue(inst->getParent()->getParent())); + } else { + return false; + } + } + } + + if (!isa(old_constant)) { + old_constant->destroyConstant(); + } + + return true; +} + +bool CangjieIRForTarget::IsReferenceType(const lldb_private::CompilerType &ty) { + std::string type_name = ty.GetTypeName().AsCString(); + if (ty.GetTypeClass() == lldb::eTypeClassFunction) { + return false; + } + if (ty.GetTypeClass() == lldb::eTypeClassEnumeration) { + return false; + } + // For UG, enum with args is reference type. + if (ty.GetTypeClass() == lldb::eTypeClassStruct && + type_name.find(lldb_private::ENUM_PREFIX_NAME) != std::string::npos) { + return true; + } + if (ty.GetTypeClass() == lldb::eTypeClassStruct && + type_name.find(lldb_private::E2_PREFIX_NAME_OPTION_LIKE) != std::string::npos) { + // Option like enum's ref is based on args type. + for (uint32_t i = 0; i < ty.GetNumFields(); i++) { + std::string member_name; + auto child_type = ty.GetFieldAtIndex(i, member_name, nullptr, nullptr, nullptr); + if (member_name == "val") { + return IsReferenceType(child_type); + } + } + } + std::vector value_type = {"Float64", "Float32", "Float16", "Int64", "Int32", "Int16", "Int8", + "UInt64", "UInt32", "UInt16", "UInt8", "Bool", "Unit", "IntNative", + "UIntNative", "std.core.String", "Rune",}; + lldb_private::ConstString match( + "(^Tuple<.+>$)|(^std.core::Range<.+>$)|(^VArray<.+>$)|(^Enum\\$)|(^std[.]core::Array<.+>$)"); + lldb_private::RegularExpression regex(match.GetStringRef()); + if (regex.Execute(type_name.c_str())) { + return false; + } + lldb_private::ConstString func_match("(.+::|^)\\(.*\\)( ?)->.+$"); + lldb_private::RegularExpression func_regex(func_match.GetStringRef()); + if (func_regex.Execute(type_name.c_str())) { + return true; + } + if (std::find(value_type.begin(), value_type.end(), type_name) != value_type.end()) { + return false; + } + if (ty.GetTypeClass() == lldb::eTypeClassStruct) { + return false; + } + return true; +} + +bool CangjieIRForTarget::IsReferenceType(lldb::VariableSP value) { + lldb_private::Log *log = GetLog(LLDBLog::Expressions); + auto frame = m_exe_ctx.GetFramePtr(); + if (frame == nullptr) { + return false; + } + + auto valobj = frame->GetValueObjectForFrameVariable(value, lldb::DynamicValueType::eNoDynamicValues); + if (valobj == nullptr) { + return false; + } + bool isRef = IsReferenceType(valobj->GetCompilerType()); + if (log) { + LLDB_LOGF(log, "variable [%s]'s type is ref[%d].", valobj->GetName().AsCString(), isRef); + } + return isRef; +} + +void CangjieIRForTarget::AddVariableToArgs() { + lldb_private::Log *log = GetLog(LLDBLog::Expressions); + for (llvm::GlobalVariable &global_var : m_module->globals()) { + lldb_private::ConstString global_name(global_var.getName().data()); + lldb_private::Mangled mangled_name(global_name); + lldb_private::ConstString demanglede_name = mangled_name.GetName(); + if (demanglede_name.GetStringRef().startswith("__lldb_locals::")) { + demanglede_name.SetCString(demanglede_name.GetStringRef().substr(strlen("__lldb_locals::")).data()); + } + + lldb::VariableSP value; + if (global_name == m_this_obj_name) { + m_replace_this_object = true; + value = m_decl_map->FindParsedVarByName(lldb_private::ConstString("this")); + } else if (global_name == m_super_obj_name) { + ArgsForIR arg(global_name, &global_var, 0, nullptr); + arg.m_is_reference = true; + m_args.push_back(arg); + if (log) { + LLDB_LOGF(log, "llvm ir: add variable [%s] to Args.", global_name.AsCString()); + } + } else { + value = m_decl_map->FindParsedVarByName(demanglede_name); + } + + if (value != nullptr) { + lldb_private::Status err; + auto offset = m_materializer->AddVariable(value, err); + if (err.Fail()) { + continue; + } + llvm::Value *llvm_value = &global_var; + ArgsForIR arg(global_name, llvm_value, offset, value); + + if (global_name == m_this_obj_name) { + arg.m_is_reference = IsReferenceType(value); + m_args.insert(m_args.begin(), arg); + continue; + } else { + arg.m_is_reference = IsReferenceType(value); + } + if (log) { + LLDB_LOGF(log, "llvm ir: add variable [%s] to Args.", global_name.AsCString()); + } + m_args.push_back(arg); + } + } +} + +void CangjieIRForTarget::RemoveGlobalInitFunctionCall() { + for (llvm::Function &function : *m_module) { + for (llvm::BasicBlock &bb : function) { + RemoveGlobalInitFunctionCall(bb); + } + } +} + +void CangjieIRForTarget::RemoveGlobalInitFunctionCall(llvm::BasicBlock &basic_block) { + std::vector calls_to_remove; + + for (llvm::Instruction &inst : basic_block) { + llvm::CallInst *call = dyn_cast(&inst); + if (!call) { + continue; + } + + llvm::Function *func = call->getCalledFunction(); + if (!func) { + continue; + } + + auto func_name = func->getName(); + if (func_name.startswith("_CGP4expr") || func_name.startswith("_CGP13__lldb_locals")) { + calls_to_remove.push_back(call); + } + } + + for (llvm::CallInst *ci : calls_to_remove) { + ci->eraseFromParent(); + } +} + +CangjieIRForTarget::ArgsForIR::ArgsForIR(lldb_private::ConstString name, llvm::Value *value, + int32_t offset, lldb::VariableSP variable) + : m_name(name), m_llvm_value(value), m_offset(offset), m_value(variable) {} + +CangjieIRForTarget::ArgsForIR::~ArgsForIR() = default; + +void CangjieIRForTarget::SetPlatformInfo() { + auto arch = m_exe_ctx.GetTargetSP()->GetArchitecture(); + if (!arch.IsValid()) { + return; + } + llvm::Triple::OSType ostype = arch.GetTriple().getOS(); + llvm::Triple::ArchType archtype = arch.GetTriple().getArch(); + auto env = arch.GetTriple().getEnvironmentName(); + if (ostype == llvm::Triple::Linux && archtype == llvm::Triple::ArchType::x86_64) { + m_module->setDataLayout("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"); + } + if (ostype == llvm::Triple::Win32 && archtype == llvm::Triple::ArchType::x86_64) { + m_module->setTargetTriple("x86_64-w64-windows-gnu"); + m_module->setDataLayout("e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"); + } + if (ostype == llvm::Triple::Linux && archtype == llvm::Triple::ArchType::aarch64) { + if (env.equals("ohos")) { + m_module->setTargetTriple("aarch64-unknown-linux-ohos"); + } + if (env.equals("android")) { + m_module->setTargetTriple("aarch64-unknown-linux-android"); + } + m_module->setDataLayout("e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"); + } + if (ostype == llvm::Triple::Linux && archtype == llvm::Triple::ArchType::x86_64) { + if (env.equals("ohos")) { + m_module->setTargetTriple("x86_64-unknown-linux-ohos"); + m_module->setDataLayout("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"); + } + } + if (ostype == llvm::Triple::Darwin || ostype == llvm::Triple::MacOSX) { + if (archtype == llvm::Triple::ArchType::aarch64) { + m_module->setTargetTriple(arch.GetTriple().str().c_str()); + m_module->setDataLayout("e-m:o-i64:64-i128:128-n32:64-S128"); + } else if (archtype == llvm::Triple::ArchType::x86_64) { + m_module->setDataLayout("e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"); + } + } + if (ostype == llvm::Triple::IOS && archtype == llvm::Triple::ArchType::aarch64) { + m_module->setTargetTriple(arch.GetTriple().str().c_str()); + m_module->setDataLayout("e-m:o-i64:64-i128:128-n32:64-S128-Fn32"); + } +} + +llvm::StringRef CangjieIRForTarget::FindExtendFunction() +{ + for (llvm::Function &func : m_module->functions()) { + if (func.getName().startswith("_CN4exprUexpr.temp$X") && func.getName().endswith("__lldb_wrapped_exprHPu")) { + return func.getName(); + } + } + + return ""; +} + +bool CangjieIRForTarget::HandleIRForExecute() { + // First: Collect all variables that need to be replaced. + AddVariableToArgs(); + + auto extend_func = FindExtendFunction(); + if (!extend_func.empty()) { + // If we need to replace this variable, this variable will always be the first. + auto this_var = m_args.begin()->m_name; + // If the class context does not contain the this variable, the struct is zero size. + // __lldb_injected_self will be deleted by codegen. + if (this_var != m_this_obj_name) { + m_func_name.SetCString(extend_func.data()); + m_replace_this_object = false; + } + } + + // Check whether the "this" variable needs to be replaced. + if (m_replace_this_object) { + ReplaceIRGlobals(); + m_func_name.SetCString(extend_func.data()); + } + + // Replace all result variables and variables in the program with parameters of the expr function. + ReplaceIRGlobals(); + + RemoveGlobalInitFunctionCall(); + + SetPlatformInfo(); + return true; +} + +lldb_private::CompilerType CangjieIRForTarget::GetCompilerTypeByName(std::string &name) { + std::string demangled_name = lldb_private::Mangled::GetDemangledTypeName(name); + lldb_private::Log *log = GetLog(LLDBLog::Expressions); + auto type = m_decl_map->FindParsedTypesByName(demangled_name); + if (type.GetTypeClass() == lldb::eTypeClassStruct && + type.GetTypeName().GetStringRef().contains(lldb_private::E0_PREFIX_NAME)) { + std::string member_name; + auto child_type = type.GetFieldAtIndex(0, member_name, nullptr, nullptr, nullptr); + if (child_type.GetTypeClass() == lldb::eTypeClassEnumeration) { + return child_type; + } + } + if (m_decl_map->IsInterfaceType(type) && m_decl_map->dynamic_ype.IsValid()) { + type = m_decl_map->dynamic_ype; + } + if (type.IsValid()) { + if (log) { + LLDB_LOGF(log, "get type by name[%s] from m_parsed_types. \n", demangled_name.c_str()); + } + return type; + } + // Create compilerType for generic decl. For example, define CT and use CT() + if (!type.IsValid() && m_decl_map->IsInstantiatedOfGenericDecl(demangled_name)) { + return m_expr_result_type; + } + type = m_decl_map->LookUpType(demangled_name); + return type; +} + +bool CangjieIRForTarget::CreateResultVariable(lldb_private::Materializer::PersistentVariableDelegate* result_delegate, + std::string type_name, bool in_member, + lldb_private::ConstString result_name) { + lldb_private::Status err; + lldb_private::Log *log = GetLog(LLDBLog::Expressions); + auto result_type = GetCompilerTypeByName(type_name); + + if (log) { + lldb_private::StreamString s; + result_type.DumpTypeDescription(&s); + LLDB_LOGF(log, "Result variable Decl:\n %s", s.GetData()); + } + + auto offset = m_materializer->AddResultVariable(result_type, false, true, result_delegate, err); + + // There are two wrapper kind: Function and MemberFunction. + lldb_private::ConstString replaceFunc = m_func_name; + if (in_member) { + replaceFunc.SetCString(FindExtendFunction().data()); + } + Function *llvm_function = + replaceFunc.IsEmpty() ? nullptr + : m_module->getFunction(replaceFunc.GetStringRef()); + if (!llvm_function) { + return false; + } + // Get llvm::Type from local variable. + for (auto &I:llvm_function->getEntryBlock()) { + if (AllocaInst* allocaInst = dyn_cast(&I)) { + if (allocaInst->getName() == result_name.AsCString()) { + llvm::Type* localResultType = allocaInst->getAllocatedType(); + auto res = llvm::cast( + m_module->getOrInsertGlobal(result_name.GetStringRef(), localResultType)); + allocaInst->replaceAllUsesWith(res); + allocaInst->removeFromParent(); + break; + } + } + } + llvm::Value *result_value = m_module->getNamedValue(result_name.GetStringRef()); + ArgsForIR arg(result_name, result_value, offset, nullptr); + arg.m_is_reference = IsReferenceType(result_type); + m_args.push_back(arg); + return true; +} diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieIRForTarget.h b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieIRForTarget.h new file mode 100644 index 000000000..0c46e8581 --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieIRForTarget.h @@ -0,0 +1,121 @@ +//===-- CangjieIRForTarget.h ----------------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_IRFORTARGET_H +#define LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_IRFORTARGET_H + +#include "lldb/Expression/Materializer.h" +#include "lldb/Target/ExecutionContext.h" + +namespace llvm { +class BasicBlock; +class Function; +class GlobalValue; +class StoreInst; +class DataLayout; +class GlobalVariable; +class CallInst; +class Instruction; +class Constant; +class ConstantInt; +class Module; +class Value; +} + +namespace lldb_private { +class CangjieDeclMap; +class IRExecutionUnit; +class IRMemoryMap; +} + +class CangjieIRForTarget { +public: + CangjieIRForTarget(lldb_private::Materializer *materializer, llvm::Module *module, + lldb_private::ConstString func_name, lldb_private::CangjieDeclMap *decl_map, + lldb_private::ExecutionContext &exe_ctx); + bool HandleIRForExecute(); + bool CreateResultVariable(lldb_private::Materializer::PersistentVariableDelegate* result_delegate, + std::string type_name, bool in_member, lldb_private::ConstString result_name); + void AddVariableToArgs(); + bool ReplaceIRGlobals(); + void RemoveGlobalInitFunctionCall(); + void RemoveGlobalInitFunctionCall(llvm::BasicBlock &basic_block); + lldb_private::CompilerType GetCompilerTypeByName(std::string &name); + llvm::StringRef FindExtendFunction(); + void SetPlatformInfo(); + static bool IsReferenceType(const lldb_private::CompilerType &ty); + bool IsReferenceType(lldb::VariableSP value); + lldb_private::CompilerType m_expr_result_type; +private: + class FunctionValueCache { + public: + typedef std::function Maker; + typedef std::map FunctionValueMap; + + FunctionValueCache(Maker const &maker); + ~FunctionValueCache(); + llvm::Value *GetValue(llvm::Function *function); + + private: + Maker const m_maker; + FunctionValueMap m_values; + }; + + class ArgsForIR { + public: + ArgsForIR(lldb_private::ConstString name, llvm::Value *value, + int32_t offset, lldb::VariableSP variable); + + ~ArgsForIR(); + + lldb_private::ConstString m_name; + + // Value to be replaced in LLVM IR, include result + llvm::Value *m_llvm_value; + + // The offset to args needs to be replaced. + int32_t m_offset; + + lldb::VariableSP m_value; + + bool m_is_reference = false; + }; + + lldb_private::Materializer *m_materializer = nullptr; + + // The module being processed, or NULL if that has not been determined yet. + llvm::Module *m_module = nullptr; + + // Variables that need to be added to arg + std::vector m_args; + + lldb_private::ConstString m_func_name; + + lldb_private::ExecutionContext m_exe_ctx; + + lldb_private::CangjieDeclMap *m_decl_map; + + FunctionValueCache m_entry_instruction_finder; + + bool m_replace_this_object = false; + + bool m_this_is_replaced = false; + + lldb_private::ConstString m_this_obj_name; + + lldb_private::ConstString m_super_obj_name; + + bool UnfoldConstant(llvm::Constant *old_constant, + llvm::Function *llvm_function, + FunctionValueCache &value_maker, + FunctionValueCache &entry_instruction_finder); +}; + +#endif /* LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_IRFORTARGET_H */ diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieUserExpression.cpp new file mode 100644 index 000000000..7149a2801 --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieUserExpression.cpp @@ -0,0 +1,277 @@ +//===-- CangjieUserExpression.cpp -----------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#include "CangjieUserExpression.h" +#include "CangjieCompilerInstance.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "clang/AST/DeclCXX.h" +#include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h" + + +using namespace lldb_private; +using namespace Cangjie; + +CangjieUserExpression::CangjieUserExpression( + ExecutionContextScope &exe_scope, llvm::StringRef expr, + llvm::StringRef prefix, lldb::LanguageType language, + ResultType desired_type, const EvaluateExpressionOptions &options, + ValueObject *ctx_obj) + : LLVMUserExpression(exe_scope, expr, prefix, language, desired_type, + options), m_ctx_obj(ctx_obj) {} + +CangjieUserExpression::~CangjieUserExpression() = default; + +bool CangjieUserExpression::Parse( + DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, bool keep_result_in_memory, + bool generate_debug_info) { + if (!SetupPersistentState(diagnostic_manager, exe_ctx)) { + return false; + } + m_materializer_up = std::make_unique(); + // Create source code. + std::vector m_imported_cpp_modules; + CreateSourceCode(diagnostic_manager, exe_ctx, m_imported_cpp_modules, false); + // Do compile. + if (!TryParse(exe_ctx, generate_debug_info)) { + return false; + } + + m_parser->m_in_member_method = m_in_member_method; + Status jit_error = m_parser->PrepareForExecution( + m_jit_start_addr, m_jit_end_addr, m_execution_unit_sp, exe_ctx, + m_can_interpret, execution_policy); + if (jit_error.Fail()) { + Log *log = GetLog(LLDBLog::Expressions); + LLDB_LOGF(log, "%s", jit_error.AsCString()); + return false; + } + m_materialized_address = m_parser->m_materialized_address; + Process *process = exe_ctx.GetProcessPtr(); + if (process && m_jit_start_addr != LLDB_INVALID_ADDRESS) + m_jit_process_wp = lldb::ProcessWP(process->shared_from_this()); + + return true; +} + +bool CangjieUserExpression::TryParse(ExecutionContext &exe_ctx, + bool generate_debug_info) { + StackFrame *frame = exe_ctx.GetFramePtr(); + if (!frame) { + return false; + } + + auto exe_scope = exe_ctx.GetBestExecutionContextScope(); + m_parser = std::make_unique( + exe_scope, *this, generate_debug_info); + m_parser->PrepareParse(m_materializer_up.get(), &m_result_delegate, &m_error_result_delegate, m_in_static_method); + return m_parser->Parse(exe_ctx, m_transformed_text); +} + +bool CangjieUserExpression::Complete(ExecutionContext &exe_ctx, + CompletionRequest &request, + unsigned complete_pos) { + return true; +} + +void CangjieUserExpression::ScanContext(ExecutionContext &exe_ctx, Status &err) { + Log *log = GetLog(LLDBLog::Expressions); + LLDB_LOGF(log, "CangjieUserExpression::ScanContext()"); + + StackFrame *frame = exe_ctx.GetFramePtr(); + if (!frame) { + LLDB_LOGF(log, " [ScanContext] Null stack frame"); + return; + } + + SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | + lldb::eSymbolContextBlock); + if (!sym_ctx.function) { + LLDB_LOGF(log, " [ScanContext] Null function"); + return; + } + + Block *function_block = sym_ctx.GetFunctionBlock(); + if (!function_block) { + LLDB_LOGF(log, " [ScanContext] Null function block"); + return; + } + auto this_variable = frame->FindVariable(ConstString("this")); + if (this_variable) { + auto pkg_name = GetPkgNameFromCompilerUnit(*sym_ctx.comp_unit); + std::string type_name = this_variable->GetTypeName().GetCString(); + if (ConstString(type_name).GetStringRef().contains(GENERIC_TYPE_PREFIX_NAME)) { + auto dynamic_Type = this_variable->GetDynamicType(); + std::string tmp_name = dynamic_Type.GetTypeName().GetCString(); + auto pkg_name = GetPkgNameFromCompilerUnit(*sym_ctx.comp_unit); + m_class_name = DeleteAllPkgname(tmp_name, pkg_name); + } + type_name = DeletePrefixOfType(type_name); + m_class_name = GetSubNameWithoutPkgname(type_name, pkg_name); + } + CompilerDeclContext decl_context = function_block->GetDeclContext(); + if (!decl_context) { + if (this_variable) { + LLDB_LOGF(log, " in member method context"); + m_in_member_method = true; + return; + } + LLDB_LOGF(log, " [ScanContext] Null decl context"); + return; + } + + if (clang::CXXMethodDecl *method_decl = + TypeSystemClang::DeclContextGetAsCXXMethodDecl(decl_context)) { + ClangASTMetadata *metadata = + TypeSystemClang::DeclContextGetMetaData(decl_context, method_decl); + if (!this_variable) { + // If the function is static. + LLDB_LOGF(log, " in static method context"); + m_in_static_method = true; + return; + } + // Get the class name if the ctx in instance Member Function. + auto classdecl = method_decl->getParent(); + if (classdecl) { + m_class_name = classdecl->getIdentifier()->getName(); + m_class_name = DeletePrefixOfType(m_class_name); + if (ConstString(m_class_name).GetStringRef().contains(GENERIC_TYPE_PREFIX_NAME)) { + // use `extend A {...}` instand of `extend A<$G_T> {...}` + auto m_this_variable = frame->FindVariable(ConstString("this")); + auto dynamic_Type = m_this_variable->GetDynamicType(); + auto pkg_name = GetPkgNameFromCompilerUnit(*sym_ctx.comp_unit); + std::string type_name = dynamic_Type.GetTypeName().GetCString(); + m_class_name = DeleteAllPkgname(type_name, pkg_name); + m_class_name = DeletePrefixOfType(m_class_name); + LLDB_LOGF(log, "class name %s -> %s", type_name.c_str(), m_class_name.c_str()); + } + } + LLDB_LOGF(log, " in member method context"); + m_in_member_method = true; + return; + } else if (clang::ObjCMethodDecl *method_decl = + TypeSystemClang::DeclContextGetAsObjCMethodDecl(decl_context)) { + if (!this_variable) { + LLDB_LOGF(log, " in static method context"); + m_in_static_method = true; + return; + } + LLDB_LOGF(log, " in member method context"); + m_in_member_method = true; + return; + } else if (clang::FunctionDecl *function_decl = + TypeSystemClang::DeclContextGetAsFunctionDecl(decl_context)) { + ClangASTMetadata *metadata = + TypeSystemClang::DeclContextGetMetaData(decl_context, function_decl); + if (metadata && metadata->HasObjectPtr()) { + LLDB_LOGF(log, " in member method context"); + m_in_member_method = true; + } + return; + } + LLDB_LOGF(log, " in normal function context"); + return; +} + +CangjieExpressionSourceCode::WrapKind CangjieUserExpression::GetWrapKind() const { + using Kind = CangjieExpressionSourceCode::WrapKind; + if (m_in_member_method) { + return Kind::MemberFunction; + } + + // Not in any kind of 'special' function, + // so just wrap it in a normal cj function. + return Kind::Function; +} + +void CangjieUserExpression::CreateSourceCode( + DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx, + std::vector modules_to_import, bool for_completion) { + // Scan wrapkind for SourceCode. + Status err; + ScanContext(exe_ctx, err); + + std::string prefix = m_expr_prefix; + if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) { + m_transformed_text = m_expr_text; + } else { + m_source_code.reset(CangjieExpressionSourceCode::CreateWrapped( + prefix, m_expr_text, GetWrapKind(), m_class_name)); + + if (!m_source_code->GetText(m_transformed_text, exe_ctx, for_completion, + for_completion, modules_to_import)) { + return; + } + } + Log *log = GetLog(LLDBLog::Expressions); + if (log) { + LLDB_LOGF(log, " ------ [ Cangjie source code start] ------ \n%s", m_transformed_text.c_str()); + LLDB_LOGF(log, " ------ [ Cangjie source code end] ------ \n"); + } +} + +bool CangjieUserExpression::AddArguments(ExecutionContext &exe_ctx, std::vector &args, + lldb::addr_t struct_address, + DiagnosticManager &diagnostic_manager) { + args.push_back(struct_address); + return true; +} + +ConstString CangjieUserExpression::CangjiePersistentVariableDelegate::GetName() { + return m_persistent_state->GetNextPersistentVariableName(false); +} + +void CangjieUserExpression::CangjiePersistentVariableDelegate::DidDematerialize( + lldb::ExpressionVariableSP &variable) { + m_variable = variable; +} + +void CangjieUserExpression::CangjiePersistentVariableDelegate::RegisterPersistentState( + PersistentExpressionState *persistent_state) { + m_persistent_state = persistent_state; +} + +bool CangjieUserExpression::SetupPersistentState(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx) { + Target *target = exe_ctx.GetTargetPtr(); + if (!target) { + diagnostic_manager.PutString(eDiagnosticSeverityError, + "error: couldn't start parsing (no target)"); + return false; + } + if (PersistentExpressionState *persistent_state = + target->GetPersistentExpressionStateForLanguage( + lldb::eLanguageTypeC)) { + m_result_delegate.RegisterPersistentState(persistent_state); + m_error_result_delegate.RegisterPersistentState(persistent_state); + return true; + } else { + diagnostic_manager.PutString( + eDiagnosticSeverityError, + "couldn't start parsing (no persistent data)"); + return false; + } +} + +lldb::ExpressionVariableSP CangjieUserExpression::GetResultAfterDematerialization( + ExecutionContextScope *exe_scope) { + lldb::TargetSP target_sp = exe_scope->CalculateTarget(); + auto persistent_state = target_sp->GetPersistentExpressionStateForLanguage(lldb::eLanguageTypeC); + auto error = m_error_result_delegate.GetVariable()->GetValueObject(); + if (error->GetValueAsSigned(-1) == -1) { + persistent_state->RemovePersistentVariable(m_error_result_delegate.GetVariable()); + persistent_state->RemovePersistentVariable(m_result_delegate.GetVariable()); + return nullptr; + } + + persistent_state->RemovePersistentVariable(m_error_result_delegate.GetVariable()); + return m_result_delegate.GetVariable(); +} diff --git a/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieUserExpression.h b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieUserExpression.h new file mode 100644 index 000000000..1b0fef56d --- /dev/null +++ b/lldb/source/Plugins/ExpressionParser/Cangjie/CangjieUserExpression.h @@ -0,0 +1,99 @@ +//===-- CangjieUserExpression.h -------------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_CANGJIEUSEREXPRESSION_H +#define LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_CANGJIEUSEREXPRESSION_H + +#include "CangjieExpressionSourceCode.h" +#include "lldb/Expression/LLVMUserExpression.h" + +namespace lldb_private { + +class CangjieExpressionParser; + +class CangjieUserExpression : public LLVMUserExpression { +public: + CangjieUserExpression(ExecutionContextScope &exe_scope, llvm::StringRef expr, + llvm::StringRef prefix, lldb::LanguageType language, + ResultType desired_type, + const EvaluateExpressionOptions &options, + ValueObject *ctx_obj); + + ~CangjieUserExpression() override; + + bool Parse(DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + bool keep_result_in_memory, bool generate_debug_info) override; + bool TryParse(ExecutionContext &exe_ctx, bool generate_debug_info); + bool Complete(ExecutionContext &exe_ctx, CompletionRequest &request, + unsigned complete_pos) override; + CangjieExpressionSourceCode::WrapKind GetWrapKind() const; + + lldb::ExpressionVariableSP + GetResultAfterDematerialization(ExecutionContextScope *exe_scope) override; + + bool SetupPersistentState(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx); + + std::unique_ptr m_source_code; + + bool AddVarToArgs(ExecutionContext &exe_ctx); + +private: + void ScanContext(ExecutionContext &exe_ctx, + lldb_private::Status &err) override; + + bool AddArguments(ExecutionContext &exe_ctx, std::vector &args, + lldb::addr_t struct_address, + DiagnosticManager &diagnostic_manager) override; + + void CreateSourceCode(DiagnosticManager &diagnostic_manager, + ExecutionContext &exe_ctx, + std::vector modules_to_import, + bool for_completion); + + class CangjiePersistentVariableDelegate + : public Materializer::PersistentVariableDelegate { + public: + CangjiePersistentVariableDelegate() {} + ConstString GetName(); + void DidDematerialize(lldb::ExpressionVariableSP &variable); + lldb::ExpressionVariableSP GetVariable() { return m_variable; } + void RegisterPersistentState(PersistentExpressionState *persistent_state); + private: + PersistentExpressionState *m_persistent_state; + lldb::ExpressionVariableSP m_variable; + }; + + ValueObject *m_ctx_obj; + /// Class name used for the expression. + std::string m_class_name; + + /// True if it was parsed when exe_ctx was in a non-static method of class. + bool m_in_member_method = false; + + /// True if it was parsed when exe_ctx was in a static method of class. + bool m_in_static_method = false; + + ConstString m_func_name; + + CangjiePersistentVariableDelegate m_result_delegate; + CangjiePersistentVariableDelegate m_error_result_delegate; + + /// True if "this" or "self" must be looked up and passed in. False if the + /// expression doesn't really use them and they can be NULL. + bool m_needs_object_ptr = false; + + std::unique_ptr m_parser; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_EXPRESSIONPARSER_CANGJIE_CANGJIEUSEREXPRESSION_H diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt index 80135daf4..30d06ae8f 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt +++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -3,6 +3,8 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN CPlusPlusLanguage.cpp CPlusPlusNameParser.cpp CxxStringTypes.cpp + CjTypes.cpp + LibCj.cpp GenericBitset.cpp GenericOptional.cpp LibCxx.cpp diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 23ce1654f..3dff69ad7 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -35,7 +35,9 @@ #include "BlockPointer.h" #include "CPlusPlusNameParser.h" +#include "CjTypes.h" #include "CxxStringTypes.h" +#include "LibCj.h" #include "Generic.h" #include "LibCxx.h" #include "LibCxxAtomic.h" @@ -226,6 +228,8 @@ void CPlusPlusLanguage::MethodName::Parse() { } else { m_parse_error = true; } + // C++ parsing rules do not apply to Cangjie. + m_parse_error = true; } m_parsed = true; } @@ -274,11 +278,11 @@ bool CPlusPlusLanguage::MethodName::ContainsPath(llvm::StringRef path) { // If we can't parse the incoming name, then just check that it contains path. if (m_parse_error) return m_full.GetStringRef().contains(path); - + llvm::StringRef identifier; llvm::StringRef context; std::string path_str = path.str(); - bool success + bool success = CPlusPlusLanguage::ExtractContextAndIdentifier(path_str.c_str(), context, identifier); @@ -299,7 +303,7 @@ bool CPlusPlusLanguage::MethodName::ContainsPath(llvm::StringRef path) { return false; if (haystack.empty() || !isalnum(haystack.back())) return true; - + return false; } @@ -315,7 +319,7 @@ bool CPlusPlusLanguage::IsCPPMangledName(llvm::StringRef name) { return true; } -bool CPlusPlusLanguage::DemangledNameContainsPath(llvm::StringRef path, +bool CPlusPlusLanguage::DemangledNameContainsPath(llvm::StringRef path, ConstString demangled) const { MethodName demangled_name(demangled); return demangled_name.ContainsPath(path); @@ -1240,6 +1244,116 @@ static void LoadSystemFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "unichar summary provider", ConstString("unichar"), widechar_flags); } +static void LoadCjFormatterSummary(lldb::TypeCategoryImplSP cpp_category_sp) { + if (!cpp_category_sp) { + return; + } + + TypeSummaryImpl::Flags cj_flags; + cj_flags.SetDontShowValue(true).SetSkipPointers(true).SetDontShowChildren(true); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::BasicSummaryProvider, + "Rune summary provider", ConstString("Rune"), cj_flags); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::BasicSummaryProvider, + "Int8 summary provider", ConstString("Int8"), cj_flags); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::BasicSummaryProvider, + "UInt8 summary provider", ConstString("UInt8"), cj_flags); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::StringSummaryProvider, + "String summary provider", ConstString("^std[.]core::String$"), cj_flags, true); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::UnitSummaryProvider, + "Unit summary provider", ConstString("Unit"), cj_flags); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::CStringSummaryProvider, + "CString summary provider", ConstString("CString"), cj_flags); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::CPointerSummaryProvider, + "CPointer summary provider", ConstString("^CPointer<.+>$"), cj_flags, true); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::RangeSummaryProvider, + "Range summary provider", ConstString("^std[.]core::Range<.+>$"), cj_flags, true); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::DateTimeSummaryProvider, + "DateTime summary provider", ConstString("^std[.]time::DateTime$"), cj_flags, true); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::DecimalSummaryProvider, + "Decimal summary provider", ConstString("^std[.]math.numeric::Decimal$"), cj_flags, true); + + // Applies only to the enum type without parameters. + AddCXXSummary(cpp_category_sp, lldb_private::formatters::EnumSummaryProvider, + "Enum0 summary provider", ConstString("^(.+)?E0\\$(.+)$"), cj_flags, true); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::Enum2SummaryProvider, + "Enum2 summary provider", ConstString("(.+)?E2\\$"), cj_flags, true); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::OptionPtrSummaryProvider, + "Option ptr summary provider", ConstString("^std[.]core::Option<.+> \\*$"), cj_flags, true); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::OptionSummaryProvider, + "Option summary provider", ConstString("^std[.]core::Option<.+>$"), cj_flags, true); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::EnumOptionSummaryProvider, + "Option summary provider", ConstString("^std[.]core::E1\\$Option<.+>$"), cj_flags, true); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::EnumOptionPtrSummaryProvider, + "Option ptr summary provider", ConstString("^std[.]core::E1\\$Option<.+> \\*$"), cj_flags, true); + + AddCXXSummary(cpp_category_sp, lldb_private::formatters::FunctionSummaryProvider, + "Function summary provider", + ConstString("(.+::|^)\\(.*\\)( ?)->.+$"), cj_flags, true); +} + +static void LoadCjFormatterSynthetic(lldb::TypeCategoryImplSP cpp_category_sp) { + if (!cpp_category_sp) { + return; + } + + SyntheticChildren::Flags cj_synth_flags; + cj_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(false); + + AddCXXSynthetic(cpp_category_sp, lldb_private::formatters::CjClassSyntheticFrontEndCreator, + "Class synthetic children", ConstString("^.+$"), cj_synth_flags, true); + + AddCXXSynthetic(cpp_category_sp, lldb_private::formatters::CjOptionSyntheticFrontEndCreator, + "Option synthetic children", ConstString("^std[.]core::Option<.+>( \\*)?$"), cj_synth_flags, true); + + AddCXXSynthetic(cpp_category_sp, lldb_private::formatters::CjArraySyntheticFrontEndCreator, + "Array synthetic children", ConstString("^std[.]core::Array<.+>$"), cj_synth_flags, true); + + AddCXXSynthetic(cpp_category_sp, lldb_private::formatters::CjTupleSyntheticFrontEndCreator, + "Tuple synthetic children", ConstString("^Tuple<.+>$"), cj_synth_flags, true); + + AddCXXSynthetic(cpp_category_sp, lldb_private::formatters::CjEnumSyntheticFrontEndCreator, + "Enum synthetic children", ConstString("(.+)?(Enum|E1)\\$(.+)"), cj_synth_flags, true); + + AddCXXSynthetic(cpp_category_sp, lldb_private::formatters::CjE2SyntheticFrontEndCreator, + "Enum2 synthetic children", ConstString("(.+)?E2\\$"), cj_synth_flags, true); + + AddCXXSynthetic(cpp_category_sp, lldb_private::formatters::CjE3SyntheticFrontEndCreator, + "Enum3 synthetic children", ConstString("(.+)?E3\\$"), cj_synth_flags, true); + + AddCXXSynthetic(cpp_category_sp, lldb_private::formatters::CjComplexAsSimpleSyntheticFrontEndCreator, + "String synthetic children", ConstString("^(std[.]core::|C)String$"), cj_synth_flags, true); + + AddCXXSynthetic(cpp_category_sp, lldb_private::formatters::CjComplexAsSimpleSyntheticFrontEndCreator, + "CPointer synthetic children", ConstString("^CPointer<.+>$"), cj_synth_flags, true); + + AddCXXSynthetic(cpp_category_sp, lldb_private::formatters::CjArrayListSyntheticFrontEndCreator, + "ArrayList synthetic children", ConstString("^std[.]collection::ArrayList<.+>$"), cj_synth_flags, true); + + AddCXXSynthetic(cpp_category_sp, lldb_private::formatters::CjHashMapSyntheticFrontEndCreator, + "HashMap synthetic children", ConstString("^std[.]collection::HashMap<.+>$"), cj_synth_flags, true); + + AddCXXSynthetic(cpp_category_sp, lldb_private::formatters::CjHashSetSyntheticFrontEndCreator, + "HashSet synthetic children", ConstString("^std[.]collection::HashSet<.+>$"), cj_synth_flags, true); + + AddCXXSynthetic(cpp_category_sp, lldb_private::formatters::CjVArraySyntheticFrontEndCreator, + "VArray synthetic children", ConstString("^VArray<.+>$"), cj_synth_flags, true); +} + std::unique_ptr CPlusPlusLanguage::GetTypeScavenger() { class CPlusPlusTypeScavenger : public Language::ImageListTypeScavenger { public: @@ -1268,6 +1382,8 @@ lldb::TypeCategoryImplSP CPlusPlusLanguage::GetFormatters() { LoadLibStdcppFormatters(g_category); LoadLibCxxFormatters(g_category); LoadSystemFormatters(g_category); + LoadCjFormatterSummary(g_category); + LoadCjFormatterSynthetic(g_category); } }); return g_category; diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h index 53a01cfc4..646750708 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -91,7 +91,7 @@ public: bool IsNilReference(ValueObject &valobj) override; - llvm::StringRef GetNilReferenceSummaryString() override { return "nullptr"; } + llvm::StringRef GetNilReferenceSummaryString() override { return "None"; } bool IsSourceFile(llvm::StringRef file_path) const override; diff --git a/lldb/source/Plugins/Language/CPlusPlus/CjTypes.cpp b/lldb/source/Plugins/Language/CPlusPlus/CjTypes.cpp new file mode 100644 index 000000000..7d2296864 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/CjTypes.cpp @@ -0,0 +1,966 @@ +//===-- CjTypes.cpp ---------------------------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#include "CjTypes.h" +#include +#include "lldb/DataFormatters/StringPrinter.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Target/Process.h" +#include "lldb/DataFormatters/DumpValueObjectOptions.h" +#include "lldb/DataFormatters/ValueObjectPrinter.h" +#include "lldb/Core/ValueObject.h" + +using namespace lldb; +using namespace lldb_private; + +bool lldb_private::formatters::BasicSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + ConstString type = valobj.GetTypeName(); + if (type == "Rune") { + DataExtractor data; + Status error; + valobj.GetData(data, error); + + if (error.Fail()) { + return false; + } + + StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); + options.SetData(std::move(data)); + options.SetStream(&stream); + options.SetQuote('\''); + options.SetSourceSize(1); + options.SetBinaryZeroIsTerminator(false); + + return StringPrinter::ReadBufferAndDumpToStream(options); + } + + std::string value; + if (type == "Int8") { + valobj.GetValueAsCString(lldb::eFormatDecimal, value); + } else if (type == "UInt8") { + valobj.GetValueAsCString(lldb::eFormatUnsigned, value); + } + + if (!value.empty()) { + stream.Printf("%s", value.c_str()); + } + + return true; +} + +bool lldb_private::formatters::StringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + lldb::ValueObjectSP raw = valobj.GetNonSyntheticValue(); + if (!raw) { + return false; + } + + lldb::ValueObjectSP data = raw->GetChildMemberWithName(ConstString("myData"), true); + if (!data) { + return false; + } + + Status error; + if (data->IsPointerType()) { + data = data->Dereference(error); + if (error.Fail()) { + return false; + } + } + + lldb::ValueObjectSP start = raw->GetChildMemberWithName(ConstString("start"), true); + lldb::ValueObjectSP len = raw->GetChildMemberWithName(ConstString("length"), true); + lldb::ValueObjectSP elements = data->GetChildMemberWithName(ConstString("elements"), true); + if (!start || !len || !elements) { + return false; + } + + StringPrinter::ReadStringAndDumpToStreamOptions options(*elements); + uint64_t startValue = start->GetValueAsUnsigned(UINT64_MAX); + if (startValue == UINT64_MAX) { + return false; + } + options.SetLocation(elements->GetAddressOf() + startValue); + options.SetTargetSP(valobj.GetTargetSP()); + options.SetStream(&stream); + uint64_t lenValue = len->GetValueAsUnsigned(UINT64_MAX); + if (lenValue == UINT64_MAX) { + return false; + } + if (lenValue == 0) { + stream.Printf("\"\""); + return true; + } + options.SetSourceSize(lenValue); + options.SetHasSourceSize(true); + options.SetNeedsZeroTermination(false); + options.SetIgnoreMaxLength(false); + options.SetEscapeNonPrintables(true); + options.SetBinaryZeroIsTerminator(false); + + return StringPrinter::ReadStringAndDumpToStream(options); +} + +bool lldb_private::formatters::UnitSummaryProvider( + ValueObject &, Stream &stream, const TypeSummaryOptions &) { + stream.Printf("()"); + return true; +} + +bool lldb_private::formatters::CStringSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + lldb::ValueObjectSP raw = valobj.GetNonSyntheticValue(); + if (!raw) { + return false; + } + + lldb::ValueObjectSP chars = raw->GetChildMemberWithName(ConstString("chars"), true); + if (!chars) { + return false; + } + + lldb::ValueObjectSP cpoint = chars->GetChildMemberWithName(ConstString("ptr"), true); + if (!cpoint) { + // For llvmgc backend. If CString in Jet, char * is CString.chars.ptr. + // Need delete cpoint after CString refactor to built-in on Jet backend. + cpoint = chars; + } + time_t currentTime; + struct tm* localTime = localtime(¤tTime); + stream.Printf("%s", cpoint->GetSummaryAsCString()); + + return true; +} + +bool lldb_private::formatters::CPointerSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + lldb::ValueObjectSP raw = valobj.GetNonSyntheticValue(); + if (!raw) { + return false; + } + + lldb::ValueObjectSP ptr = raw->GetChildMemberWithName(ConstString("ptr"), true); + if (!ptr) { + return false; + } + + stream.Printf("%s", ptr->GetValueAsCString()); + + return true; +} + +static void DumpWithoutPrefix(lldb::ValueObjectSP valueSP, Stream &stream) +{ + if (valueSP == nullptr) { + return; + } + auto target = valueSP->GetTargetSP(); + if (target == nullptr) { + return; + } + + StreamString result; + DumpValueObjectOptions options; + auto max_depth = target->GetMaximumDepthOfChildrenToDisplay(); + options.SetMaximumPointerDepth({DumpValueObjectOptions::PointerDepth::Mode::Always, max_depth.first}); + options.SetMaximumDepth(max_depth.first, max_depth.second); + options.SetHideRootType(true); + options.SetVariableFormatDisplayLanguage(valueSP->GetPreferredDisplayLanguage()); + valueSP->Dump(result, options); + std::string value = result.GetString().data(); + std::string prefix = " = "; + size_t start = value.find_first_of(prefix); + size_t end = value.find_last_of("\n"); + if (start != std::string::npos && end != std::string::npos) { + stream.Printf("%s", value.substr(start + prefix.size(), end - start - prefix.size()).c_str()); + } + return; +} + +bool lldb_private::formatters::RangeSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + lldb::ValueObjectSP raw = valobj.GetNonSyntheticValue(); + + lldb::ValueObjectSP startSP = raw->GetChildMemberWithName(ConstString("start"), true); + lldb::ValueObjectSP endSP = raw->GetChildMemberWithName(ConstString("end"), true); + lldb::ValueObjectSP stepSP = raw->GetChildMemberWithName(ConstString("step"), true); + lldb::ValueObjectSP closedSP = raw->GetChildMemberWithName(ConstString("isClosed"), true); + lldb::ValueObjectSP hasStartSP = raw->GetChildMemberWithName(ConstString("hasStart"), true); + lldb::ValueObjectSP hasEndSP = raw->GetChildMemberWithName(ConstString("hasEnd"), true); + if (!startSP || !endSP || !stepSP || !closedSP || !hasStartSP || !hasEndSP) { + return false; + } + + std::string hasStart = hasStartSP->GetValueAsCString(); + std::string hasEnd = hasEndSP->GetValueAsCString(); + std::string closed = closedSP->GetValueAsCString(); + if (hasStart == "true") { + DumpWithoutPrefix(startSP, stream); + } + stream.Printf(".."); + if (closed == "true") { + stream.Printf("="); + } + if (hasEnd == "true") { + DumpWithoutPrefix(endSP, stream); + } + stream.Printf(" : "); + DumpWithoutPrefix(stepSP, stream); + return true; +} + +bool lldb_private::formatters::OptionSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + Status error; + lldb::ValueObjectSP raw = valobj.GetNonSyntheticValue(); + lldb::ValueObjectSP constructorSP = raw->GetChildMemberWithName(ConstString("constructor"), true); + if (constructorSP) { + std::string constructor = constructorSP->GetValueAsCString(); + if (constructor == "None") { + stream.Printf("None"); + return true; + } + } + + lldb::ValueObjectSP valueSP = raw->GetChildMemberWithName(ConstString("val"), true); + if (valueSP == nullptr) { + valueSP = raw->GetChildMemberWithName(ConstString("Recursive-val"), true); + if (valueSP) { + if (valueSP->IsPointerType()) { + stream.Printf("%s", valueSP->GetValueAsCString()); + } else { + stream.Printf("0x%" PRIx64, valueSP->GetAddressOf()); + } + return true; + } + return false; + } + + if (valueSP->IsPointerType()) { + valueSP = valueSP->Dereference(error); + if (error.Fail()) { + return false; + } + auto addr = valueSP->GetAddressOf(); + if (addr == LLDB_INVALID_ADDRESS || addr == 0) { + stream.Printf("None"); + return true; + } + } + + TypeSummaryImpl *entry = valueSP->GetSummaryFormat().get(); + if (entry) { + std::string summary; + valueSP->GetSummaryAsCString(entry, summary, lldb::LanguageType::eLanguageTypeC_plus_plus_14); + if (!summary.empty()) { + stream.Printf("%s", summary.c_str()); + return true; + } + } + + const char *value_cstr = valueSP->GetValueAsCString(); + if (value_cstr) { + stream.Printf("%s", value_cstr); + return true; + } + + return true; +} + +bool lldb_private::formatters::OptionPtrSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + if (valobj.IsPointerType()) { + Status error; + lldb::ValueObjectSP valueSP = valobj.Dereference(error); + if (error.Fail()) { + stream.Printf("None"); + return true; + } + + auto addr = valueSP->GetAddressOf(); + if (addr == LLDB_INVALID_ADDRESS || addr == 0) { + stream.Printf("None"); + return true; + } + + DumpWithoutPrefix(valueSP, stream); + return true; + } + + return false; +} + +bool lldb_private::formatters::EnumOptionSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + lldb::ValueObjectSP valueSP; + if (valobj.IsPointerType()) { + Status error; + valueSP = valobj.Dereference(error); + if (error.Fail()) { + return false; + } + valueSP = valueSP->GetNonSyntheticValue(); + } else { + valueSP = valobj.GetNonSyntheticValue(); + } + + for (size_t i = 0; i < valueSP->GetCompilerType().GetNumDirectBaseClasses(); i++) { + ValueObjectSP ctor = valueSP->GetChildAtIndex(i, true); + if (!ctor) { + continue; + } + auto enum_class = ctor->GetChildMemberWithName(ConstString("EnumClass$"), true); + if (!enum_class) { + continue; + } + + auto type = enum_class->GetChildMemberWithName(ConstString("constructor"), true); + if (!type) { + continue; + } + + auto id = type->GetValueAsUnsigned(0); + auto real_ctor = valueSP->GetChildAtIndex(id, true); + if (!real_ctor) { + continue; + } + auto real_enumclass = real_ctor->GetChildMemberWithName(ConstString("EnumClass$"), true); + if (!real_enumclass) { + continue; + } + DataExtractor data; + Status err; + enum_class->GetData(data, err); + valueSP = lldb_private::ValueObject::CreateValueObjectFromData("EnumClass", data, + real_enumclass->GetExecutionContextRef(), real_enumclass->GetCompilerType()); + valueSP = valueSP->GetChildMemberWithName(ConstString("constructor"), true); + break; + } + + TypeSummaryImpl *entry = valueSP->GetSummaryFormat().get(); + if (entry) { + std::string summary; + valueSP->GetSummaryAsCString(entry, summary, lldb::LanguageType::eLanguageTypeC_plus_plus_14); + if (!summary.empty()) { + stream.Printf("%s", summary.c_str()); + return true; + } + } + + std::string value_cstr(valueSP->GetValueAsCString()); + if (!value_cstr.empty() && value_cstr != std::string("Some")) { + stream.Printf("%s", value_cstr.c_str()); + return true; + } + return true; +} + +bool lldb_private::formatters::EnumOptionPtrSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + auto valueSP = valobj.GetNonSyntheticValue(); + if (valueSP->IsPointerType()) { + Status error; + valueSP = valueSP->Dereference(error); + if (error.Fail()) { + stream.Printf("None"); + return true; + } + + auto addr = valueSP->GetAddressOf(); + if (addr == LLDB_INVALID_ADDRESS || addr == 0) { + stream.Printf("None"); + return true; + } + + DumpWithoutPrefix(valueSP, stream); + return true; + } + + return false; +} + +bool lldb_private::formatters::FunctionSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + lldb::ValueObjectSP raw = valobj.GetNonSyntheticValue(); + if (!raw) { + return false; + } + + lldb::ValueObjectSP ptr = raw->GetChildMemberWithName(ConstString("ptr"), true); + if (!ptr) { + return false; + } + if (ptr->GetSummaryAsCString() == nullptr) { + stream.Printf("%s", ptr->GetValueAsCString()); + } else { + stream.Printf("%s %s", ptr->GetValueAsCString(), ptr->GetSummaryAsCString()); + } + return true; +} + +bool lldb_private::formatters::EnumSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + lldb::ValueObjectSP raw = valobj.GetNonSyntheticValue(); + if (!raw) { + return false; + } + + lldb::ValueObjectSP ptr = raw->GetChildMemberWithName(ConstString("constructor"), true); + if (ptr == nullptr) { + return false; + } + + std::vector enumerators; + auto type = ptr->GetCompilerType(); + type.ForEachEnumerator( + [&enumerators](const CompilerType &integer_type, ConstString name, const llvm::APSInt &value) -> bool { + enumerators.push_back(name); + return true; + } + ); + + if (enumerators.size() == 1) { + stream.Printf("%s", enumerators[0].AsCString()); + } else { + stream.Printf("%s", ptr->GetValueAsCString()); + } + + return true; +} + + +// The layout of the type is: +// public struct BigInt { +// let int: UInt64, +// let intArr: Array, +// let negSign: Bool +// } +// public struct Array { +// let rawptr: RawArray +// let start: Int64 +// let len: Int64 + +class BigInt { +public: + BigInt(uint64_t intv, std::vector big_intArr, int64_t len, bool negSign) + : m_intv(intv), + m_big_intArr(big_intArr), + m_len(len), + m_negsign(negSign) { } + std::string ToString() { + int64_t digits = 9; + uint64_t anyBase = 1000000000; + // Get str Arr size + int64_t strArrSize = (32 * (m_len + 2) / int64_t(29) + 1); + std::vector stringArr; + for (size_t i = 0; i < strArrSize; i++) { + stringArr.emplace_back(0); + } + int64_t stringArrBound = CalculateStrArr(stringArr, m_big_intArr, m_intv, 1); + + std::vector stringUtf8Arr(stringArrBound * digits + 1, '0'); + int64_t begin = ToBaseString(stringUtf8Arr, 9, stringArr[stringArrBound - 1]); + if (m_negsign) { + stringUtf8Arr[begin] = '-'; + begin--; + } + begin++; + for (int64_t i = stringArrBound - 2; i >= 0; i--) { + ToBaseString(stringUtf8Arr, digits * (stringArrBound - i), stringArr[i]); + } + std::string result; + for (size_t i = begin; i < stringUtf8Arr.size(); i++) { + result += static_cast(stringUtf8Arr[i]); + } + return result; + } + + int64_t ToBaseString(std::vector& stringUtf8Arr, int64_t index, uint64_t int1) { + const uint8_t DIGIT_DIFF = 0x30; + const uint8_t LETTER_UPPER_DIFF = 0x41 - 0x0A; + const uint8_t LETTER_LOWER_DIFF = 0x61 - 0x0A; + uint64_t base = 10; + int64_t i = index; + uint64_t quo = int1; + while (quo != 0) { + uint8_t rem = uint8_t(quo % base); + if (rem < 10) { // Decimal 10 + stringUtf8Arr[i] = rem + DIGIT_DIFF; + } else { + stringUtf8Arr[i] = rem + LETTER_UPPER_DIFF; + } + quo = quo / uint64_t(base); + i--; + } + return i; + } + + int64_t CalculateStrArr(std::vector& stringArr, uint64_t bigInt, int64_t bound) { + int64_t stringArrBound = bound; + int64_t anyBase = 1000000000; + for (int64_t j = stringArrBound - 1; j >= 0; --j) { + stringArr[j] <<= 32; // 32-bit + } + stringArr[0] += bigInt; + for (size_t j = 0; j < stringArrBound - 1; j++) { + stringArr[j + 1] += stringArr[j] / anyBase; + stringArr[j] %= anyBase; + } + int64_t rem = stringArr[stringArrBound - 1] / anyBase; + stringArr[stringArrBound - 1] %= anyBase; + while (rem > 0) { + stringArr[stringArrBound] = rem; + stringArrBound++; + rem = stringArr[stringArrBound - 1] / anyBase; + stringArr[stringArrBound - 1] %= anyBase; + } + return stringArrBound; + } + + int64_t CalculateStrArr(std::vector& stringArr, std::vector& bigArray, + uint64_t bigInt, int64_t bound) { + int64_t stringArrBound = bound; + int64_t i = bigArray.size() - 1; + while (i >= 0) { + stringArrBound = CalculateStrArr(stringArr, uint64_t(bigArray[i]), stringArrBound); + i--; + } + stringArrBound = CalculateStrArr(stringArr, bigInt >> 32, stringArrBound); // 32-bit + return CalculateStrArr(stringArr, (bigInt & 0xFFFFFFFF), stringArrBound); + } +private: + uint64_t m_intv; + std::vector m_big_intArr; + int64_t m_len; + bool m_negsign; +}; + +std::string lldb_private::formatters::GetBigIntvalue(ValueObject &value) { + // The layout of the type is: + // public struct BigInt { + // let int: UInt64, + // let intArr: Array, + // let negSign: Bool + // } + lldb::ValueObjectSP bint = value.GetChildMemberWithName(ConstString("int"), true); + lldb::ValueObjectSP negSign = value.GetChildMemberWithName(ConstString("negSign"), true); + lldb::ValueObjectSP valueIntArr = value.GetChildMemberWithName(ConstString("intArr"), true); + if (!bint || !negSign || !valueIntArr) { + return "0"; + } + auto value_int = bint->GetValueAsUnsigned(UINT64_MAX); + auto value_negSign = negSign->GetValueAsSigned(INT64_MAX); + auto array_len = valueIntArr->GetChildMemberWithName(ConstString("len"), true); + if (!array_len) { + return "0"; + } + auto len = array_len->GetValueAsSigned(INT64_MAX); + if (len == 0) { + std::string result = std::to_string(value_int); + if (value_negSign == 1) { + result = "-" + result; + } + return result; + } + // use intArr + // public struct Array { + // let rawptr: RawArray + // let start: Int64 + // let len: Int64 + auto rawptr = valueIntArr->GetChildMemberWithName(ConstString("rawptr"), true); + auto m_start = valueIntArr->GetChildMemberWithName(ConstString("start"), true); + if (!rawptr || !m_start) { + return "0"; + } + auto start = m_start->GetValueAsSigned(INT64_MAX); + + auto m_elements = rawptr->GetChildMemberWithName(ConstString("elements"), true); + CompilerType element_type = m_elements->GetCompilerType(); + llvm::Optional size = element_type.GetByteSize(nullptr); + uint64_t element_size = 4; // UInt32's size is 4. + + std::vector big_intArr; + for (size_t idx = 0; idx < len; idx++) { + addr_t addr = m_elements->GetAddressOf() + (m_start->GetValueAsUnsigned(0) + idx) * element_size; + ValueObjectSP valobj_sp( + ValueObject::CreateValueObjectFromAddress("tmp", addr, m_elements->GetExecutionContextRef(), element_type)); + + if (valobj_sp) + valobj_sp->SetSyntheticChildrenGenerated(true); + + if (valobj_sp->IsPointerType()) { + Status error; + valobj_sp = valobj_sp->Dereference(error); + valobj_sp->SetName(ConstString(valobj_sp->GetName().GetStringRef().ltrim('*').str())); + } + auto tmpU32 = valobj_sp->GetValueAsUnsigned(UINT32_MAX); + big_intArr.emplace_back(tmpU32); + } + auto bigint = BigInt(value_int, big_intArr, len, value_negSign); + return bigint.ToString(); +} + +// The layout of the type is: +// public struct Decimal { +// var _scale: Int32 +// var _precision: Int64 +// var _value: BigInt +// var _sign: Int64 +// } +class DecimalToString { +public: + DecimalToString(int32_t scale, int64_t precision, std::string bigInt, int64_t sign) + : _scale(scale), _precision(precision), _bigInt(bigInt), _sign(sign) {} + std::string ToString() { + if (_scale < 0) { + if (_sign == 0) { + return "0"; + } + std::string decimalStrArr(_bigInt.length() - _scale, '0'); + decimalStrArr = _bigInt + decimalStrArr; + return decimalStrArr; + } + std::string unscaleValStrArr = _bigInt; + // scale value bigger than 0, need insert decimal point to corresponding position. + // precision is bigger than 0 and scale range [0, Int32.Max] + auto decimalPointIndex = _precision - _scale; + std::string decimalStrArr(unscaleValStrArr); + if (decimalPointIndex == 0) { + if (unscaleValStrArr.at(0) == '-') { + decimalStrArr.insert(0, 1, '-'); + decimalStrArr.insert(2, 1, '.'); // position is 2 + } else { + decimalStrArr.insert(0, 1, '0'); + decimalStrArr.insert(1, 1, '.'); + } + } else if (decimalPointIndex > 0) { + if (unscaleValStrArr[0] == '-') { + decimalStrArr.insert(decimalPointIndex + 1, 1, '.'); + } else { + decimalStrArr.insert(decimalPointIndex, 1, '.'); + } + } else { + // decimalPointIndex less than 0, decimal point in the left of bigint value. + // when decimalPointIndex less than 0, it's range [-Int32.Max, 0], abs will nerver overflow. + std::string temp(unscaleValStrArr.size() + abs(decimalPointIndex) + 2, '0'); + decimalStrArr = temp; + if (unscaleValStrArr[0] == '-') { + decimalStrArr[0] = '-'; + decimalStrArr[2] = '.'; // Index 2. + for (int64_t i = 1; i < unscaleValStrArr.size(); i++) { + decimalStrArr[abs(decimalPointIndex) + 3 + i] = unscaleValStrArr[i]; // Offset 3. + } + } else { + decimalStrArr[1] = '.'; + for (int64_t i = 0; i < unscaleValStrArr.size(); i++) { + decimalStrArr[abs(decimalPointIndex) + 2 + i] = unscaleValStrArr[i]; // Offset 2. + } + } + } + return decimalStrArr; + } + +private: + int32_t _scale; + int64_t _precision; + std::string _bigInt; + int64_t _sign; +}; + +bool lldb_private::formatters::DecimalSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options) +{ + // The layout of the type is: + // public struct Decimal { + // var _scale: Int32 + // var _precision: Int64 + // var _value: BigInt + // var _sign: Int64 + // } + lldb::ValueObjectSP raw = valobj.GetNonSyntheticValue(); + if (!raw) { + return false; + } + lldb::ValueObjectSP scale = raw->GetChildMemberWithName(ConstString("_scale"), true); + lldb::ValueObjectSP precision = raw->GetChildMemberWithName(ConstString("_precision"), true); + lldb::ValueObjectSP value = raw->GetChildMemberWithName(ConstString("_value"), true); + lldb::ValueObjectSP sign = raw->GetChildMemberWithName(ConstString("_sign"), true); + if (!scale || !precision || !value || !sign) { + return false; + } + auto dscale = scale->GetValueAsSigned(INT64_MAX); + std::string unscaleValStrArr = GetBigIntvalue(*value.get()); + if (dscale == 0) { + stream.Printf("%s", unscaleValStrArr.c_str()); + return true; + } + auto dsign = sign->GetValueAsSigned(INT64_MAX); + auto dprecision = precision->GetValueAsSigned(INT64_MAX); + std::string tmp = DecimalToString(dscale, dprecision, unscaleValStrArr, dsign).ToString(); + stream.Printf("%s", tmp.c_str()); + return true; +} + +class CangjieDateTime { +public: + CangjieDateTime(int64_t epoch, int64_t offset): epoch(epoch), offset(offset) {} + enum class MONTH { + Invaild = 0, + January = 1, + February, + March, + April, + May, + June, + July, + August, + September, + October, + November, + December + }; + int64_t epoch; + int64_t offset; + int64_t START_AD_YEAR = 1; + int64_t MIN_YEAR = -999999999; + int64_t MAX_YEAR = 999999999; + int64_t SECS_PER_DAY = 86400; + int64_t DAYS_PER_400YEARS = 146097; + int64_t DAYS_PER_100YEARS = 36524; + int64_t DAYS_PER_4YEARS = 1461; + int64_t DAYS_OF_NORMAL_YEAR = 365; + int64_t SECS_PER_HOUR = 3600; + int64_t SECS_PER_MINUTE = 60; + int64_t MAX_DAY_AD1 = (MAX_YEAR - 1) * 365 + (MAX_YEAR - 1) / 4 - (MAX_YEAR - 1) / 100 + (MAX_YEAR - 1) / 400 + 365; + int64_t MIN_DAY_AD1 = -(MAX_DAY_AD1 + 366); + int64_t SECS_OF_MIN_TO_AD1 = MIN_DAY_AD1 * SECS_PER_DAY; + std::vector DAYS_BEFORE = { + 0, + 31, // January, 31 days + 59, // until February, 31 + 28, 59 days + 90, // 90 days + 120, // 120 days + 151, // 151 days + 181, // 181 days + 212, // 212 days + 243, // 243 days + 273, // 273 days + 304, // 304 days + 334, // 334 days + 365 // 365 days + }; + int64_t Year() { + return std::get<0>(GetYearAndSecond()); + } + MONTH Month() { + return std::get<0>(GetMonthAndDay()); + } + int64_t DayOfMonth() { + return std::get<1>(GetMonthAndDay()); + } + int64_t Hour() { + int64_t second = std::get<1>(this->GetYearAndSecond()); + return (second % this->SECS_PER_DAY) / this->SECS_PER_HOUR; + } + int64_t Minute() { + int64_t second = std::get<1>(this->GetYearAndSecond()); + return (second % this->SECS_PER_HOUR) / this->SECS_PER_MINUTE; + } + int64_t Second() { + int64_t second = std::get<1>(this->GetYearAndSecond()); + return second % this->SECS_PER_MINUTE; + } +private: + static MONTH MonthOf(int64_t mon) { + std::vector months = { + MONTH::Invaild, MONTH::January, MONTH::February, MONTH::March, MONTH::April, MONTH::May, MONTH::June, + MONTH::July, MONTH::August, MONTH::September, MONTH::October, MONTH::November, MONTH::December + }; + if (mon >= 1 && mon <= 12) { // month 12 + return months[mon]; + } + return MONTH::Invaild; + } + std::tuple GetMonthAndDay() { + auto year_second = this->GetYearAndSecond(); + return this->GetDate(std::get<0>(year_second), std::get<1>(year_second)); + } + bool IsLeapYear(int64_t year) { + return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0); // 4 year, 100 year, 400 year + } + std::tuple GetDate(int64_t year, int64_t sec) { + int64_t days = int64_t(sec) / SECS_PER_DAY; + if (IsLeapYear(year)) { + if (days == DAYS_BEFORE[2]) { // Month 2 + return {MONTH::February, 29}; // Month 2 has 29 days. + } + if (days > DAYS_BEFORE[2]) { // Month 2 + days -= 1; + } + } + int64_t idx = 0; + int64_t day = 0; + while (idx < DAYS_BEFORE.size()) { + /* 1. In this case, the corresponding date is the 1st of the month idx + 1. */ + if (days == DAYS_BEFORE[idx]) { + idx++; + day = 1; + break; + } + /* 2. In this case, the corresponding date is month idx. */ + if (days < DAYS_BEFORE[idx]) { + day = days - DAYS_BEFORE[idx - 1] + 1; + break; + } + idx++; + } + MONTH month = MonthOf(idx); + return {month, day}; + } + std::tuple GetYearAndSecond() { + auto [year, sec] = this->ToYearAndSecond(this->epoch + this->GetOffset()); + return {year, int64_t(sec)}; + } + + /** + * Calculate year and second in year through seconds since year 1 A.D. + */ + std::tuple ToYearAndSecond(int64_t sec) { + int64_t year = START_AD_YEAR; + int64_t seconds = sec; + if (seconds < 0) { + seconds -= SECS_OF_MIN_TO_AD1; + year = MIN_YEAR; + } + int64_t days = seconds / SECS_PER_DAY; + int64_t restSec = seconds % SECS_PER_DAY; + + int64_t times = days / DAYS_PER_400YEARS; + year += 400 * times; // 400 years + days %= DAYS_PER_400YEARS; + + times = days / DAYS_PER_100YEARS; + times -= times >> 2; // 2 + year += 100 * times; // 100 year + days -= DAYS_PER_100YEARS * times; + + times = days / DAYS_PER_4YEARS; + year += 4 * times; // 4 year + days %= DAYS_PER_4YEARS; + + times = days / DAYS_OF_NORMAL_YEAR; + times -= times >> 2; // 2 + year += times; + days -= DAYS_OF_NORMAL_YEAR * times; + + int64_t secInYear = restSec + days * SECS_PER_DAY; + return {year, secInYear}; + } + int64_t GetOffset() { + return this->offset; + } +}; + +bool lldb_private::formatters::DateTimeSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) +{ + // The layout of the type is: + // public struct DateTime { + // let d: Duration + // let tz: TimeZone + // } + lldb::ValueObjectSP raw = valobj.GetNonSyntheticValue(); + if (!raw) { + return false; + } + lldb::ValueObjectSP duration = raw->GetChildMemberWithName(ConstString("d"), true); + if (!duration) { + return false; + } + lldb::ValueObjectSP sec = duration->GetChildMemberWithName(ConstString("sec"), true); + if (!sec) { + return false; + } + int64_t secVal = sec->GetValueAsSigned(INT64_MAX); + lldb::ValueObjectSP timeZone = raw->GetChildMemberWithName(ConstString("tz"), true); + if (!timeZone) { + return false; + } + lldb::ValueObjectSP zoneId = timeZone->GetChildMemberWithName(ConstString("zoneId"), true); + if (!zoneId) { + return false; + } + auto str_zoneId = zoneId->GetSummaryAsCString(); + CangjieDateTime date = CangjieDateTime(secVal, 0); + if (date.Year() > 999 || date.Year() < -999) { // 999, -999 year + stream.Printf("%d-%02d-%02dT%02d:%02d:%02dZ", date.Year(), + date.Month(), date.DayOfMonth(), date.Hour(), date.Minute(), date.Second()); + } else if (date.Year() < 0) { + stream.Printf("%05d-%02d-%02dT%02d:%02d:%02dZ", date.Year(), + date.Month(), date.DayOfMonth(), date.Hour(), date.Minute(), date.Second()); + } else { + stream.Printf("%04d-%02d-%02dT%02d:%02d:%02dZ", date.Year(), + date.Month(), date.DayOfMonth(), date.Hour(), date.Minute(), date.Second()); + } + return true; +} + +bool lldb_private::formatters::Enum2SummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &) { + if (valobj.IsPointerType()) { + auto ptr = valobj.GetValueAsUnsigned(UINT64_MAX); + if (ptr == 0) { + std::string field_name; + auto enum_type = valobj.GetCompilerType().GetPointeeType(); + auto constructor = enum_type.GetFieldAtIndex(0, field_name, nullptr, nullptr, nullptr); + ConstString none; + constructor.ForEachEnumerator( + [&none](const CompilerType &integer_type, ConstString name, const llvm::APSInt &value) -> bool { + if (name.GetStringRef().contains("N$_")) { + none = name; + } + return true; + } + ); + stream.Printf("%s", none.GetStringRef().ltrim("N$_").data()); + return true; + } else { + Status err; + auto val = valobj.Dereference(err); + if (!err.Fail()) { + val->SetIsOptionlikeReference(true); + DumpWithoutPrefix(val, stream); + return true; + } + } + } + + lldb::ValueObjectSP raw = valobj.GetNonSyntheticValue(); + if (!raw) { + return false; + } + if (raw->IsOptionlikeReference()) { + return false; + } + auto constructor = raw->GetChildMemberWithName(ConstString("constructor"), true); + if (constructor == nullptr) { + return false; + } + + auto value = constructor->GetValueAsCString(); + if (value != nullptr) { + if (std::string(value).find("N$_") == 0) { + stream.Printf("%s", std::string(value).substr(std::string("N$_").size()).c_str()); + return true; + } + } + + return false; +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/CjTypes.h b/lldb/source/Plugins/Language/CPlusPlus/CjTypes.h new file mode 100644 index 000000000..86fb31227 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/CjTypes.h @@ -0,0 +1,39 @@ +//===-- CjTypes.h ---------------------------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CjTypes_h_ +#define liblldb_CjTypes_h_ + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Utility/Stream.h" + +namespace lldb_private { +namespace formatters { +bool BasicSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +bool StringSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +bool UnitSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +bool CStringSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +bool CPointerSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +bool RangeSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +bool OptionSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +bool OptionPtrSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +bool EnumOptionSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +bool EnumOptionPtrSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +bool FunctionSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +bool EnumSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +bool Enum2SummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +bool DateTimeSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +std::string GetBigIntvalue(ValueObject &valobj); +bool DecimalSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); +} // namespace formatters +} // namespace lldb_private + +#endif // liblldb_CjTypes_h_ diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCj.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCj.cpp new file mode 100644 index 000000000..3e1b632fb --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCj.cpp @@ -0,0 +1,833 @@ +//===-- LibCj.cpp ---------------------------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#include "LibCj.h" +#include "lldb/DataFormatters/FormattersHelpers.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { +namespace formatters { +class CjArraySyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit CjArraySyntheticFrontEnd(ValueObjectSP valobj_sp); + ~CjArraySyntheticFrontEnd() = default; + size_t CalculateNumChildren() override { return m_len ? m_len->GetValueAsUnsigned(0) : 0; } + ValueObjectSP GetChildAtIndex(size_t idx) override; + bool Update() override; + bool MightHaveChildren() override { return true; } + size_t GetIndexOfChildWithName(ConstString name) override { return ExtractIndexFromString(name.GetCString()); } + +private: + ValueObjectSP GetSliceValue(size_t idx); + +private: + ValueObjectSP m_elements; + ValueObjectSP m_start; + ValueObjectSP m_len; +}; + +class CjArrayListSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit CjArrayListSyntheticFrontEnd(ValueObjectSP valobj_sp); + ~CjArrayListSyntheticFrontEnd() = default; + size_t CalculateNumChildren() override { return m_size; } + ValueObjectSP GetChildAtIndex(size_t idx) override; + bool Update() override; + bool MightHaveChildren() override { return true; } + size_t GetIndexOfChildWithName(ConstString name) override { return m_data->GetIndexOfChildWithName(name); } + +private: + uint64_t m_size; + CjArraySyntheticFrontEnd *m_data; +}; + +class CjHashMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit CjHashMapSyntheticFrontEnd(ValueObjectSP valobj_sp); + ~CjHashMapSyntheticFrontEnd() = default; + size_t CalculateNumChildren() override; + ValueObjectSP GetChildAtIndex(size_t idx) override; + bool Update() override; + bool MightHaveChildren() override { return true; } + size_t GetIndexOfChildWithName(ConstString name) override { return ExtractIndexFromString(name.GetCString()); } + void SetIsInternalType(bool internal) { m_internal = internal; } + std::string GetChildName(ValueObjectSP value); + llvm::StringRef GetBasicTypeName(ValueObjectSP value); + bool IsBasicType(ValueObjectSP value); +private: + int64_t m_freeOffset = 0; + ValueObjectSP m_appendIndex; + ValueObjectSP m_freeSize; + CjArraySyntheticFrontEnd *m_data; + bool m_internal = false; +}; + +class CjHashSetSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit CjHashSetSyntheticFrontEnd(ValueObjectSP valobj_sp); + ~CjHashSetSyntheticFrontEnd() = default; + size_t CalculateNumChildren() override; + ValueObjectSP GetChildAtIndex(size_t idx) override; + bool Update() override; + bool MightHaveChildren() override { return true; } + size_t GetIndexOfChildWithName(ConstString name) override { return ExtractIndexFromString(name.GetCString()); } + +private: + CjHashMapSyntheticFrontEnd *m_map; +}; + +class CjTupleSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit CjTupleSyntheticFrontEnd(ValueObjectSP valobj_sp) : SyntheticChildrenFrontEnd(*valobj_sp) {} + ~CjTupleSyntheticFrontEnd() = default; + size_t CalculateNumChildren() override; + ValueObjectSP GetChildAtIndex(size_t idx) override; + bool Update() override { return false; } + bool MightHaveChildren() override { return true; } + size_t GetIndexOfChildWithName(ConstString name) override { return ExtractIndexFromString(name.GetCString()); } +}; + +class CjEnumSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit CjEnumSyntheticFrontEnd(ValueObjectSP valobj_sp); + ~CjEnumSyntheticFrontEnd() = default; + size_t CalculateNumChildren() override { return m_enumClass ? m_enumClass->GetNumChildren() : 0; } + ValueObjectSP GetChildAtIndex(size_t idx) override; + bool Update() override; + bool MightHaveChildren() override { return m_enumClass != nullptr; } + size_t GetIndexOfChildWithName(ConstString name) override { return m_backend.GetIndexOfChildWithName(name); } + ConstString GetSyntheticTypeName() override; + ValueObjectSP GetSyntheticValue() override { return m_enumClass; } + +private: + lldb::ValueObjectSP m_enumClass; +}; + +class CjEnum2SyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit CjEnum2SyntheticFrontEnd(ValueObjectSP valobj_sp); + ~CjEnum2SyntheticFrontEnd() = default; + size_t CalculateNumChildren() override { return m_data == nullptr ? 0 : 2; } + ValueObjectSP GetChildAtIndex(size_t idx) override; + bool Update() override; + bool MightHaveChildren() override { return m_backend.MightHaveChildren(); } + size_t GetIndexOfChildWithName(ConstString name) override { return m_backend.GetIndexOfChildWithName(name); } + ConstString GetSyntheticTypeName() override; +private: + ValueObjectSP m_some_constructor; + ValueObjectSP m_data; +}; + +class CjEnum3SyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit CjEnum3SyntheticFrontEnd(ValueObjectSP valobj_sp); + ~CjEnum3SyntheticFrontEnd() = default; + size_t CalculateNumChildren() override { return m_data != nullptr ? m_data->GetNumChildren() : 0; } + ValueObjectSP GetChildAtIndex(size_t idx) override; + bool Update() override; + bool MightHaveChildren() override { return m_data != nullptr ? m_data->MightHaveChildren() : false; } + size_t GetIndexOfChildWithName(ConstString name) override { return m_backend.GetIndexOfChildWithName(name); } + ConstString GetSyntheticTypeName() override; +private: + ValueObjectSP m_data; +}; + +class CjComplexAsSimpleSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit CjComplexAsSimpleSyntheticFrontEnd(ValueObjectSP valobj_sp) : SyntheticChildrenFrontEnd(*valobj_sp) {} + ~CjComplexAsSimpleSyntheticFrontEnd() = default; + size_t CalculateNumChildren() override { return 0; } + ValueObjectSP GetChildAtIndex(size_t idx) override { return nullptr; } + bool Update() override { return false; } + bool MightHaveChildren() override { return m_backend.MightHaveChildren(); } + size_t GetIndexOfChildWithName(ConstString name) override { return 0; } + ValueObjectSP GetSyntheticValue() override { return m_backend.GetSP(); } +}; + +class CjClassSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit CjClassSyntheticFrontEnd(ValueObjectSP valobj_sp) : SyntheticChildrenFrontEnd(*valobj_sp) {} + ~CjClassSyntheticFrontEnd() = default; + size_t CalculateNumChildren() override; + ValueObjectSP GetChildAtIndex(size_t idx) override; + bool Update() override { return false; } + bool MightHaveChildren() override { return m_backend.MightHaveChildren(); } + size_t GetIndexOfChildWithName(ConstString name) override { return m_backend.GetIndexOfChildWithName(name); } + ConstString GetSyntheticTypeName() override; +}; + +class CjOptionSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit CjOptionSyntheticFrontEnd(ValueObjectSP valobj_sp); + ~CjOptionSyntheticFrontEnd() = default; + size_t CalculateNumChildren() override; + ValueObjectSP GetChildAtIndex(size_t idx) override; + bool Update() override; + bool MightHaveChildren() override; + size_t GetIndexOfChildWithName(ConstString name) override { return 0; } + ConstString GetSyntheticTypeName() override; +private: + bool m_hasvalue = false; + ValueObjectSP m_some = nullptr; + bool m_children = false; +}; + +static std::string DeletePrefixOfTypeName(std::string type_name, std::string prefix) { + auto pos = type_name.find(prefix); + if (pos != std::string::npos) { + return type_name.erase(type_name.find(prefix), prefix.size()); + } + return type_name; +} + +bool CjOptionSyntheticFrontEnd::MightHaveChildren() { + if (m_children == false || m_some == nullptr || m_hasvalue == false) { + return false; + } + + return true; +} + +size_t CjOptionSyntheticFrontEnd::CalculateNumChildren() { + if (!m_hasvalue || m_some == nullptr || !m_hasvalue) { + return 0; + } + + return m_some->GetNumChildren(); +} + +ValueObjectSP CjOptionSyntheticFrontEnd::GetChildAtIndex(size_t idx) { + if (!m_hasvalue) { + return nullptr; + } + return m_some->GetChildAtIndex(idx, true); +} + +bool CjOptionSyntheticFrontEnd::Update() { + ValueObjectSP non_synth_valobj = m_backend.GetNonSyntheticValue(); + if (!non_synth_valobj) { + m_some = nullptr; + } + + if (non_synth_valobj->IsPointerType()) { + Status err; + non_synth_valobj = non_synth_valobj->Dereference(err); + if (err.Fail()) { + return false; + } + } + + ValueObjectSP value = non_synth_valobj->GetChildMemberWithName(ConstString("val"), true); + if (value == nullptr) { + value = non_synth_valobj->GetChildMemberWithName(ConstString("Recursive-val"), true); + } + + if (value == nullptr) { + return false; + } + + if (value->IsPointerType()) { + Status err; + value = value->Dereference(err); + } + + if (value->HasSyntheticValue()) { + m_some = value->GetSyntheticValue(); + } else { + m_some = value; + } + + if (m_some != nullptr) { + m_hasvalue = true; + m_children = (m_some->GetNumChildren() > 0); + } + + return false; +} + +class CjVArraySyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit CjVArraySyntheticFrontEnd(ValueObjectSP valobj_sp); + ~CjVArraySyntheticFrontEnd() = default; + size_t CalculateNumChildren() override { return m_backend.GetNumChildren(); } + ValueObjectSP GetChildAtIndex(size_t idx) override { return m_backend.GetChildAtIndex(idx, true); }; + bool Update() override { return false; }; + bool MightHaveChildren() override { return true; } + size_t GetIndexOfChildWithName(ConstString name) override { return m_backend.GetIndexOfChildWithName(name); } +}; + +} // namespace formatters +} // namespace lldb_private + +CjVArraySyntheticFrontEnd::CjVArraySyntheticFrontEnd(ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + Update(); +} + +CjOptionSyntheticFrontEnd::CjOptionSyntheticFrontEnd(ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + Update(); +} + +ConstString CjOptionSyntheticFrontEnd::GetSyntheticTypeName() { + auto type_name = m_backend.GetDisplayTypeName().GetStringRef(); + return ConstString(type_name.rtrim(" *").str()); +} + +CjArraySyntheticFrontEnd::CjArraySyntheticFrontEnd(ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_elements(nullptr), m_start(nullptr), m_len(nullptr) { + Update(); +} + +ValueObjectSP CjArraySyntheticFrontEnd::GetChildAtIndex(size_t idx) { + if (idx >= UINT32_MAX - 1) { + return GetSliceValue(idx); + } + + if (!m_elements || !m_start || (idx >= CalculateNumChildren())) { + return ValueObjectSP(); + } + + CompilerType element_type = m_elements->GetCompilerType(); + llvm::Optional size = element_type.GetByteSize(nullptr); + uint64_t element_size = size ? *size : 0; + addr_t addr = m_elements->GetAddressOf() + (m_start->GetValueAsUnsigned(0) + idx) * element_size; + ValueObjectSP valobj_sp = CreateValueObjectFromAddress(llvm::StringRef(llvm::formatv("[{0}]", idx)), + addr, m_elements->GetExecutionContextRef(), + element_type); + if (valobj_sp->IsPointerType()) { + Status error; + valobj_sp = valobj_sp->Dereference(error); + valobj_sp->SetName(ConstString(valobj_sp->GetName().GetStringRef().ltrim('*').str())); + } + + return valobj_sp; +} + +bool CjArraySyntheticFrontEnd::Update() { + ValueObjectSP rawptr = m_backend.GetChildMemberWithName(ConstString("rawptr"), true); + if (rawptr) { + m_elements = rawptr->GetChildMemberWithName(ConstString("elements"), true); + } + + m_start = m_backend.GetChildMemberWithName(ConstString("start"), true); + m_len = m_backend.GetChildMemberWithName(ConstString("len"), true); + + return false; +} + +ValueObjectSP CjArraySyntheticFrontEnd::GetSliceValue(size_t idx) { + if (!m_start || !m_len) { + return ValueObjectSP(); + } + + size_t left = idx % (UINT32_MAX + 1ULL); + size_t right = idx / (UINT32_MAX + 1ULL); + + auto start = m_start->GetValueAsUnsigned(0); + auto len = m_len->GetValueAsUnsigned(0); + + if (left == UINT32_MAX - 1) { + left = 0; + right = 0; + } else if (right == UINT32_MAX) { + right = start + len; + } + + if ((left > right) || (left < start) || (right > start + len)) { + return ValueObjectSP(); + } + + Status error; + m_start->SetValueFromCString(std::to_string(left).c_str(), error); + m_len->SetValueFromCString(std::to_string(right - left).c_str(), error); + + DataExtractor data; + m_backend.GetData(data, error); + + ValueObjectSP valobj_sp = CreateValueObjectFromData(llvm::StringRef(llvm::formatv("[{0}..{1}]", left, right)), data, + m_backend.GetExecutionContextRef(), m_backend.GetCompilerType()); + + m_start->SetValueFromCString(std::to_string(start).c_str(), error); + m_len->SetValueFromCString(std::to_string(len).c_str(), error); + + return valobj_sp; +} + +CjArrayListSyntheticFrontEnd::CjArrayListSyntheticFrontEnd(ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_size(0), m_data(nullptr) { + Update(); +} + +bool CjArrayListSyntheticFrontEnd::Update() { + ValueObjectSP data = m_backend.GetChildMemberWithName(ConstString("myData"), true); + ValueObjectSP size = m_backend.GetChildMemberWithName(ConstString("mySize"), true); + if (data) { + m_data = new CjArraySyntheticFrontEnd(data); + } + if (size) { + m_size = size->GetValueAsUnsigned(0); + } + + return false; +} + +ValueObjectSP CjArrayListSyntheticFrontEnd::GetChildAtIndex(size_t idx) { + if (idx >= m_size || !m_data) { + return ValueObjectSP(); + } + return m_data->GetChildAtIndex(idx); +} + +ValueObjectSP CjTupleSyntheticFrontEnd::GetChildAtIndex(size_t idx) { + ValueObjectSP valobj_sp = m_backend.GetChildAtIndex(idx, true); + std::string name = llvm::formatv("[{0}]", idx).str(); + valobj_sp->SetName(ConstString(name.c_str())); + if (valobj_sp->IsPointerType()) { + Status error; + valobj_sp = valobj_sp->Dereference(error); + valobj_sp->SetName(ConstString(valobj_sp->GetName().GetStringRef().ltrim('*').str())); + } + + return valobj_sp; +} + +size_t CjTupleSyntheticFrontEnd::CalculateNumChildren() { + return m_backend.GetNumChildren(); +} + +ConstString CjClassSyntheticFrontEnd::GetSyntheticTypeName() { + std::string raw_display = m_backend.GetDisplayTypeName().GetStringRef().rtrim(" *").str(); + auto display = DeletePrefixOfTypeName(raw_display, "E0$"); + + return ConstString(display.c_str()); +} + +size_t CjClassSyntheticFrontEnd::CalculateNumChildren() { + // Class is internal type means a child of cangjie HashMap. + // HashMap only need 2 children + return m_backend.IsInternalType()? 2 : m_backend.GetNumChildren(); +} + +CjEnumSyntheticFrontEnd::CjEnumSyntheticFrontEnd(ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + Update(); +} + +bool CjEnumSyntheticFrontEnd::Update() { + for (size_t i = 0; i < m_backend.GetCompilerType().GetNumDirectBaseClasses(); i++) { + ValueObjectSP ctor = m_backend.GetChildAtIndex(i, true); + if (!ctor) { + continue; + } + auto type = ctor->GetChildMemberWithName(ConstString("constructor"), true); + if (!type) { + continue; + } + auto id = type->GetValueAsUnsigned(0); + auto real_ctor = m_backend.GetChildAtIndex(id, true); + if (!real_ctor) { + continue; + } + DataExtractor data; + Status err; + real_ctor->GetData(data, err); + m_enumClass = CreateValueObjectFromData("EnumClass", data, real_ctor->GetExecutionContextRef(), + real_ctor->GetCompilerType()); + break; + } + + if (m_enumClass != nullptr) { + auto constructor = m_enumClass->GetChildMemberWithName(ConstString("constructor"), true); + if (constructor != nullptr) { + auto value = constructor->GetValueAsCString(); + if (value && std::string(value) == std::string("Some")) { + m_enumClass = m_enumClass->GetChildMemberWithName(ConstString("arg_1"), true); + } + } + } + + return false; +} + +ConstString CjEnumSyntheticFrontEnd::GetSyntheticTypeName() { + std::string raw_display = m_backend.GetDisplayTypeName().GetStringRef().rtrim(" *").str(); + auto display = DeletePrefixOfTypeName(raw_display, "E1$"); + display = DeletePrefixOfTypeName(display, "Enum$"); + return ConstString(display.c_str()); +} + +ValueObjectSP CjEnumSyntheticFrontEnd::GetChildAtIndex(size_t idx) { + if (!m_enumClass) { + return ValueObjectSP(); + } + + ValueObjectSP child = m_enumClass->GetChildAtIndex(idx, true); + if (child && child->IsPointerType() && child->GetValueAsUnsigned(0) != 0) { + Status error; + child = child->Dereference(error); + child->SetName(ConstString(child->GetName().GetStringRef().ltrim('*').str())); + } + + return child; +} + +ValueObjectSP CjEnum2SyntheticFrontEnd::GetChildAtIndex(size_t idx) { + return idx == 0 ? m_some_constructor : m_data; +} + +CjEnum2SyntheticFrontEnd::CjEnum2SyntheticFrontEnd(ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + Update(); +} + +bool CjEnum2SyntheticFrontEnd::Update() { + std::string field_name; + Status err; + auto enum_type = m_backend.GetCompilerType(); + auto constructor = enum_type.GetFieldAtIndex(0, field_name, nullptr, nullptr, nullptr); + llvm::APSInt enum_value; + constructor.ForEachEnumerator( + [&enum_value, this](const CompilerType &integer_type, ConstString name, const llvm::APSInt &value) -> bool { + if (!name.GetStringRef().contains("N$_")) { + enum_value = value; + } + return true; + } + ); + if (m_backend.IsPointerType()) { + return false; + } + uint32_t value = (uint32_t)enum_value.getSExtValue(); + lldb::WritableDataBufferSP buffer_sp = std::make_shared(sizeof(uint32_t), 0); + auto data_ptr = reinterpret_cast(buffer_sp->GetBytes()); + *data_ptr = value; + DataExtractor extractor(buffer_sp, m_backend.GetProcessSP()->GetByteOrder(), + m_backend.GetProcessSP()->GetAddressByteSize()); + m_some_constructor = CreateValueObjectFromData(llvm::StringRef("constructor"), extractor, + m_backend.GetExecutionContextRef(), constructor); + m_data = m_backend.GetChildMemberWithName(ConstString("val"), true); + if (m_data != nullptr) { + m_data->SetName(ConstString("arg_1")); + } + return false; +} + +ConstString CjEnum2SyntheticFrontEnd::GetSyntheticTypeName() { + std::string raw_display = m_backend.GetDisplayTypeName().GetStringRef().rtrim(" *").str(); + auto display = DeletePrefixOfTypeName(raw_display, "E2$"); + return ConstString(display.c_str()); +} + +CjEnum3SyntheticFrontEnd::CjEnum3SyntheticFrontEnd(ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + Update(); +} + +bool CjEnum3SyntheticFrontEnd::Update() { + auto enum_id = m_backend.GetChildAtIndex(0, true); + if (enum_id == nullptr) { + return false; + } + auto constructor = enum_id->GetChildMemberWithName(ConstString("constructor"), true); + if (constructor == nullptr) { + return false; + } + auto id = constructor->GetValueAsUnsigned(UINT64_MAX); + constructor = m_backend.GetChildAtIndex(id, true); + if (constructor != nullptr) { + DataExtractor data; + Status error; + m_backend.GetData(data, error); + m_data = CreateValueObjectFromData(llvm::StringRef("enum_class"), data, + m_backend.GetExecutionContextRef(), constructor->GetCompilerType()); + } + + return false; +} + +ConstString CjEnum3SyntheticFrontEnd::GetSyntheticTypeName() { + std::string raw_display = m_backend.GetDisplayTypeName().GetStringRef().rtrim(" *").str(); + auto display = DeletePrefixOfTypeName(raw_display, "E3$"); + return ConstString(display.c_str()); +} + +ValueObjectSP CjEnum3SyntheticFrontEnd::GetChildAtIndex(size_t idx) { + return m_data->GetChildAtIndex(idx, true); +} + +ValueObjectSP CjClassSyntheticFrontEnd::GetChildAtIndex(size_t idx) { + if (m_backend.IsInternalType()) { + // class is internal type means a child of cangjie HashMap. + // struct HashMapEntry { + // let hashCode: Int64 + // let next: Int64 + // let key: K + // let value: V + // } + // And idx 0 require idx 2(key), idx 1 require idx 3(value) + auto child_sp = m_backend.GetChildAtIndex(idx + 2, true); + if (child_sp != nullptr && child_sp->IsPointerType()) { + Status error; + child_sp = child_sp->Dereference(error); + child_sp->SetName(ConstString(child_sp->GetName().GetStringRef().ltrim("*").str())); + } + return child_sp; + } + + ValueObjectSP child_sp = m_backend.GetChildAtIndex(idx, true); + if (child_sp && (idx == 0)) { + std::string name = child_sp->GetName().GetCString(); + size_t pos = name.find("::"); + while (pos != std::string::npos) { + name.replace(pos, strlen("::"), "."); + pos = name.find("::", pos); + } + child_sp->SetName(ConstString(name.c_str())); + } + + if (child_sp && child_sp->IsPointerType() && child_sp->GetTypeName() != "char *") { + Status error; + auto deref = child_sp->Dereference(error); + if (error.Fail() || !deref) { + return child_sp; + } + auto addr = deref->GetAddressOf(); + if (addr == 0 || addr == LLDB_INVALID_ADDRESS) { + return child_sp; + } + deref->SetName(ConstString(child_sp->GetName().GetStringRef().ltrim('*').str())); + return deref; + } + + return child_sp; +} + +CjHashMapSyntheticFrontEnd::CjHashMapSyntheticFrontEnd(ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_appendIndex(nullptr), m_freeSize(nullptr), m_data(nullptr) { + Update(); +} + +bool CjHashMapSyntheticFrontEnd::IsBasicType(ValueObjectSP value) { + auto type = value->GetCompilerType(); + if (type.GetTypeClass() == lldb::eTypeClassTypedef) { + type = type.GetTypedefedType(); + } + if (type.GetTypeClass() == lldb::eTypeClassBuiltin) { + return true; + } + if (type.GetTypeClass() == lldb::eTypeClassStruct) { + auto typeName = type.GetTypeName().GetStringRef(); + if (typeName.find("std.core::String") == 0) { + return true; + } + if (typeName.find("std.math.numeric::Decimal") == 0) { + return true; + } + if (typeName.find("std.time::DateTime") == 0) { + return true; + } + } + return false; +} + +llvm::StringRef CjHashMapSyntheticFrontEnd::GetBasicTypeName(ValueObjectSP value) { + auto type = value->GetCompilerType(); + auto typeName = type.GetTypeName().GetStringRef(); + if (typeName == "Rune" || typeName == "Int8" || typeName == "UInt8") { + return value->GetSummaryAsCString(); + } + if (type.GetTypeClass() == lldb::eTypeClassStruct) { + // String + return value->GetSummaryAsCString(); + } + // integer, float, bool + return value->GetValueAsCString(); +} + +std::string CjHashMapSyntheticFrontEnd::GetChildName(ValueObjectSP value) { + // HashMap is internal type means a child of cangjie HashSet + // then we only need one child of value(the type of value is class) + // and this child idx is 2(key), child idx is 3(value). + ValueObjectSP child_key_sp = value->GetChildAtIndex(2, true); + ValueObjectSP child_val_sp = value->GetChildAtIndex(3, true); + if (!child_key_sp || !child_val_sp) { + return ""; + } + if (!IsBasicType(child_key_sp)) { + return ""; + } + llvm::StringRef key_str = GetBasicTypeName(child_key_sp); + if (!IsBasicType(child_val_sp)) { + std::string name = "[" + key_str.str() +"]"; + return name; + } + llvm::StringRef val_str = GetBasicTypeName(child_val_sp); + std::string name = "[" + key_str.str() + " -> "+ val_str.str() + "]"; + return name; +} + +size_t CjHashMapSyntheticFrontEnd::CalculateNumChildren() { + if (!m_appendIndex || !m_freeSize) { + return 0; + } + + int64_t appendIndex = m_appendIndex->GetValueAsSigned(INT64_MAX); + int64_t freeSize = m_freeSize->GetValueAsSigned(INT64_MAX); + if (appendIndex == INT64_MAX || freeSize == INT64_MAX || appendIndex < freeSize) { + return 0; + } + + return appendIndex - freeSize; +} + +ValueObjectSP CjHashMapSyntheticFrontEnd::GetChildAtIndex(size_t idx) { + if (!m_data) { + return ValueObjectSP(); + } + ValueObjectSP value = m_data->GetChildAtIndex(idx + m_freeOffset); + if (value) { + while (true) { + int64_t isfree = value->GetChildAtIndex(0, true)->GetValueAsSigned(INT64_MAX); + if (isfree < 0) { + m_freeOffset += 1; + value = m_data->GetChildAtIndex(idx + m_freeOffset); + continue; + } + break; + } + + if (m_internal) { + std::string name = llvm::formatv("[{0}]", idx).str(); + // HashMap is internal type means a child of cangjie HashSet + // then we only need one child of value(the type of value is class) + // and this child idx is 2(key) + value = value->GetChildAtIndex(2, true); + value->SetName(ConstString(name.c_str())); + return value; + } + + // Mark the value(class type) is a child of HashMap. + value->SetIsInternalType(true); + // idx => Bob -> 123 for metaDsl. + if (value->IsInternalType()) { + auto name = GetChildName(value); + if (!name.empty()) { + value->SetName(ConstString(name)); + } + } + return value; + } + + return ValueObjectSP(); +} + +bool CjHashMapSyntheticFrontEnd::Update() { + m_appendIndex = m_backend.GetChildMemberWithName(ConstString("appendIndex"), true); + m_freeSize = m_backend.GetChildMemberWithName(ConstString("freeSize"), true); + auto entries = m_backend.GetChildMemberWithName(ConstString("entries"), true); + if (entries) { + m_data = new CjArraySyntheticFrontEnd(entries); + } + return false; +} + +CjHashSetSyntheticFrontEnd::CjHashSetSyntheticFrontEnd(ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_map(nullptr) { + Update(); +} + +bool CjHashSetSyntheticFrontEnd::Update() { + m_map = new CjHashMapSyntheticFrontEnd(m_backend.GetChildMemberWithName(ConstString("map"), true)); + if (m_map) { + // Mark the map(HashMap) is a child of HashSet. + m_map->SetIsInternalType(true); + return true; + } + return false; +} + +ValueObjectSP CjHashSetSyntheticFrontEnd::GetChildAtIndex(size_t idx) { + ValueObjectSP value = m_map->GetChildAtIndex(idx); + if (value) { + return value; + } + + return ValueObjectSP(); +} + +size_t CjHashSetSyntheticFrontEnd::CalculateNumChildren() { + size_t size = m_map->CalculateNumChildren(); + return size; +} + +SyntheticChildrenFrontEnd *lldb_private::formatters::CjArraySyntheticFrontEndCreator( + CXXSyntheticChildren *, ValueObjectSP valobj_sp) { + return valobj_sp ? new CjArraySyntheticFrontEnd(valobj_sp) : nullptr; +} + +SyntheticChildrenFrontEnd *lldb_private::formatters::CjTupleSyntheticFrontEndCreator( + CXXSyntheticChildren *, ValueObjectSP valobj_sp) { + return valobj_sp ? new CjTupleSyntheticFrontEnd(valobj_sp) : nullptr; +} + +SyntheticChildrenFrontEnd *lldb_private::formatters::CjEnumSyntheticFrontEndCreator( + CXXSyntheticChildren *, ValueObjectSP valobj_sp) { + return valobj_sp ? new CjEnumSyntheticFrontEnd(valobj_sp) : nullptr; +} + +SyntheticChildrenFrontEnd *lldb_private::formatters::CjE2SyntheticFrontEndCreator( + CXXSyntheticChildren *, ValueObjectSP valobj_sp) { + return valobj_sp ? new CjEnum2SyntheticFrontEnd(valobj_sp) : nullptr; +} + +SyntheticChildrenFrontEnd *lldb_private::formatters::CjE3SyntheticFrontEndCreator( + CXXSyntheticChildren *, ValueObjectSP valobj_sp) { + return valobj_sp ? new CjEnum3SyntheticFrontEnd(valobj_sp) : nullptr; +} + +SyntheticChildrenFrontEnd *lldb_private::formatters::CjComplexAsSimpleSyntheticFrontEndCreator( + CXXSyntheticChildren *, ValueObjectSP valobj_sp) { + return valobj_sp ? new CjComplexAsSimpleSyntheticFrontEnd(valobj_sp) : nullptr; +} + +SyntheticChildrenFrontEnd *lldb_private::formatters::CjClassSyntheticFrontEndCreator( + CXXSyntheticChildren *, ValueObjectSP valobj_sp) { + return valobj_sp ? new CjClassSyntheticFrontEnd(valobj_sp) : nullptr; +} + +SyntheticChildrenFrontEnd *lldb_private::formatters::CjArrayListSyntheticFrontEndCreator( + CXXSyntheticChildren *, ValueObjectSP valobj_sp) { + return valobj_sp ? + new CjArrayListSyntheticFrontEnd(valobj_sp) : nullptr; +} + +SyntheticChildrenFrontEnd *lldb_private::formatters::CjHashMapSyntheticFrontEndCreator( + CXXSyntheticChildren *, ValueObjectSP valobj_sp) { + return valobj_sp ? new CjHashMapSyntheticFrontEnd(valobj_sp) : nullptr; +} + +SyntheticChildrenFrontEnd *lldb_private::formatters::CjHashSetSyntheticFrontEndCreator( + CXXSyntheticChildren *, ValueObjectSP valobj_sp) { + return valobj_sp ? new CjHashSetSyntheticFrontEnd(valobj_sp) : nullptr; +} + +SyntheticChildrenFrontEnd *lldb_private::formatters::CjOptionSyntheticFrontEndCreator( + CXXSyntheticChildren *, ValueObjectSP valobj_sp) { + return valobj_sp ? new CjOptionSyntheticFrontEnd(valobj_sp) : nullptr; +} + +SyntheticChildrenFrontEnd *lldb_private::formatters::CjVArraySyntheticFrontEndCreator( + CXXSyntheticChildren *, ValueObjectSP valobj_sp) { + return valobj_sp ? new CjVArraySyntheticFrontEnd(valobj_sp) : nullptr; +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCj.h b/lldb/source/Plugins/Language/CPlusPlus/LibCj.h new file mode 100644 index 000000000..4485cd60e --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCj.h @@ -0,0 +1,34 @@ +//===-- LibCj.h -----------------------------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCJ_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCJ_H + +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/TypeSynthetic.h" + +namespace lldb_private { +namespace formatters { +SyntheticChildrenFrontEnd *CjArraySyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +SyntheticChildrenFrontEnd *CjTupleSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +SyntheticChildrenFrontEnd *CjEnumSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +SyntheticChildrenFrontEnd *CjE2SyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +SyntheticChildrenFrontEnd *CjE3SyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +SyntheticChildrenFrontEnd *CjComplexAsSimpleSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +SyntheticChildrenFrontEnd *CjClassSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +SyntheticChildrenFrontEnd *CjArrayListSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +SyntheticChildrenFrontEnd *CjHashMapSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +SyntheticChildrenFrontEnd *CjHashSetSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +SyntheticChildrenFrontEnd *CjOptionSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +SyntheticChildrenFrontEnd *CjVArraySyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +} // namespace formatters +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCJ_H diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/CMakeLists.txt b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/CMakeLists.txt index 5ab494414..6521e8519 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/CMakeLists.txt +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/CMakeLists.txt @@ -1,3 +1,8 @@ +if(NOT DEFINED CANGJIE_ROOT) + set(CANGJIE_ROOT ${CMAKE_SOURCE_DIR}/../../cangjie) +endif() +include_directories(${CANGJIE_ROOT}/include) + add_lldb_library(lldbPluginCXXItaniumABI PLUGIN ItaniumABILanguageRuntime.cpp diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp index 1595335cf..ed29af773 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp @@ -4,37 +4,30 @@ // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// //===----------------------------------------------------------------------===// #include "ItaniumABILanguageRuntime.h" -#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" -#include "lldb/Breakpoint/BreakpointLocation.h" -#include "lldb/Core/Mangled.h" -#include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" -#include "lldb/Core/ValueObject.h" -#include "lldb/Core/ValueObjectMemory.h" #include "lldb/DataFormatters/FormattersHelpers.h" #include "lldb/Expression/DiagnosticManager.h" #include "lldb/Expression/FunctionCaller.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" #include "lldb/Interpreter/CommandReturnObject.h" -#include "lldb/Symbol/Symbol.h" -#include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/TypeList.h" -#include "lldb/Target/Process.h" -#include "lldb/Target/RegisterContext.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StopInfo.h" -#include "lldb/Target/Target.h" -#include "lldb/Target/Thread.h" -#include "lldb/Utility/ConstString.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" -#include "lldb/Utility/Scalar.h" -#include "lldb/Utility/Status.h" + +#include "cangjie/Basic/UGTypeKind.h" #include @@ -44,6 +37,7 @@ using namespace lldb_private; LLDB_PLUGIN_DEFINE_ADV(ItaniumABILanguageRuntime, CXXItaniumABI) static const char *vtable_demangled_prefix = "vtable for "; +static const size_t name_size = 128; char ItaniumABILanguageRuntime::ID = 0; @@ -182,6 +176,983 @@ TypeAndOrName ItaniumABILanguageRuntime::GetTypeInfoFromVTableAddress( return TypeAndOrName(); } +static bool ReadMemoryFromAddress(Process& process, addr_t addr, void *buf, size_t size) { + Status err; + process.ReadMemory(addr, buf, size, err); + if (err.Success()) { + return true; + } + + return false; +} + +bool TypeInfo::IsFunc() const +{ + return type == UGTypeKind::UG_FUNC; +} + +bool TypeInfo::IsCFunc() const +{ + return type == UGTypeKind::UG_CFUNC; +} + +bool TypeInfo::IsVArray() const +{ + return type == UGTypeKind::UG_VARRAY; +} + +std::string TypeInfo::GetName(Process& process) const +{ + Status err; + if (this->typeArgNum == 0) { + if (this->name != nullptr) { + char res[name_size + 1]; + res[name_size] = '\0'; + if (ReadMemoryFromAddress(process, (uint64_t)this->name, res, name_size)) { + return std::string(res); + } + } else { + return ""; + } + } + + uint64_t args = (uint64_t)this->typeArgs; + uint64_t argTiAddr; + TypeInfo argTi; + if (this->IsFunc()) { + if (!ReadMemoryFromAddress(process, (uint64_t)args, &argTiAddr, sizeof(uint64_t)) || + !ReadMemoryFromAddress(process, (uint64_t)argTiAddr, &argTi, sizeof(TypeInfo))) { + return ""; + } + return argTi.GetName(process); + } + + std::string typeName; + + uint32_t argSize = this->typeArgNum; + uint32_t startIter = 0; + std::string suffix; + // For CFunc, it should be formatted as (typeArg1,typeArg2,...,typeArgN)->typeArg0 + if (this->IsCFunc()) { + startIter = 1U; + typeName.append("("); + process.ReadMemory(args, &argTiAddr, sizeof(uint64_t), err); + process.ReadMemory(argTiAddr, &argTi, sizeof(TypeInfo), err); + std::string returnTypeName = argTi.GetName(process); + suffix.append(")->").append(returnTypeName); + } else { + TypeTemplate sourceGeneric; + process.ReadMemory((uint64_t)this->sourceGeneric, &sourceGeneric, sizeof(TypeTemplate), err); + char sourceGenericName[name_size + 1]; + sourceGenericName[name_size] = '\0'; + process.ReadMemory((uint64_t)sourceGeneric.name, sourceGenericName, name_size, err); + typeName.append(sourceGenericName).append("<"); + suffix.append(">"); + } + + if (this->IsVArray()) { + process.ReadMemory(args, &argTiAddr, sizeof(uint64_t), err); + process.ReadMemory(argTiAddr, &argTi, sizeof(TypeInfo), err); + std::string tiName = argTi.GetName(process); + typeName.append(tiName).append(",").append(std::to_string(argSize)); + } + for (uint32_t idx = startIter; idx < argSize; ++idx) { + if (ReadMemoryFromAddress(process, args + idx * 8U, &argTiAddr, sizeof(uint64_t)) && + ReadMemoryFromAddress(process, argTiAddr, &argTi, sizeof(TypeInfo))) { + std::string tiName = argTi.GetName(process); + typeName.append(tiName); + if (idx < (argSize - 1U)) { + typeName.append(","); + } + } else { + break; + } + } + typeName.append(suffix); + return typeName; +} + +ConstString GetTypeName(Process& process, TypeInfo& typeInfo) { + std::string temp_name = typeInfo.GetName(process); + UGTypeKind typekind = static_cast(typeInfo.type); + auto pos = temp_name.find(":"); + if (typekind == UGTypeKind::UG_ENUM || typekind == UGTypeKind::UG_COMMON_ENUM) { + // type_name: "default:RGBColor" ==> "default::E1$RGBColor" + if (pos != std::string::npos && temp_name.find("std.core:Option") != 0) { + std::string enum_prefix = "::E1$"; + temp_name = temp_name.substr(0, pos) + enum_prefix + temp_name.substr(pos + 1); + pos = temp_name.find(":", pos + enum_prefix.size()); + } + } + // type_name: "default:A" ==> "default::A" + while (pos != std::string::npos) { + // type_name: "default:$Captured_Int64" ==> "default::$Captured_Int64" + temp_name.replace(pos, 1, "::"); + pos = temp_name.find(":", pos + 2); // size of "::" is 2 + } + return ConstString(temp_name.c_str()); +} + +CompilerType LookupDynamicType(Process& process, UGTypeKind typekind, ConstString& type_name) { + if (typekind == UGTypeKind::UG_CLASS || typekind == UGTypeKind::UG_INTERFACE || + typekind == UGTypeKind::UG_COMMON_ENUM) { + return CompilerType(); + } + TypeList types; + llvm::DenseSet searched_symbol_files; + auto& target = process.GetTarget(); + target.GetImages().FindTypes(nullptr, type_name, true, 1, searched_symbol_files, types); + size_t num_types = types.GetSize(); + for (size_t ti = 0; ti < num_types; ++ti) { + auto type_sp = types.GetTypeAtIndex(ti); + auto type = type_sp->GetFullCompilerType(); + if (type.IsValid()) { + return type; + } + } + return CompilerType(); +} + +bool IsRefType(int8_t type) { + if (type < 0) { + return true; + } + return false; +} + +std::string ItaniumABILanguageRuntime::GetReflectionFieldName(int16_t id, TypeInfo& typeInfo) { + uint64_t field_name_addr; + Status error; + char field_name[name_size + 1]; + field_name[name_size] = '\0'; + // The `relection` field of `TypeInfo` has two structures: one for enums and another for non-enums. + // for non-enums named Reflection. + // EnumReflection + if (typeInfo.flag == UGTypeKind::UG_ENUM || typeInfo.flag == UGTypeKind::UG_COMMON_ENUM) { + return ""; + } + // Reflection + uint64_t name_addr_offset; + uint64_t names_addr; + m_process->ReadMemory((uint64_t)typeInfo.reflection, &name_addr_offset, sizeof(uint64_t), error); + field_name_addr = (uint64_t)typeInfo.reflection + name_addr_offset; + m_process->ReadMemory(field_name_addr + id * BitsPerByte, &names_addr, sizeof(uint64_t), error); + field_name_addr = field_name_addr + names_addr + id * BitsPerByte; + + m_process->ReadMemory(field_name_addr, field_name, name_size, error); + std::string fname(field_name); + if (fname.empty()) { + fname = "x" + std::to_string(id); + } + return fname; +} + +void ItaniumABILanguageRuntime::AddFieldToRecordType( + TypeSystemClang& ast, TypeInfo& typeInfo, CompilerType& dynamic_type, bool is_class_member) { + Status error; + int16_t super_fields_num = 0; + if (typeInfo.super) { + TypeInfo superti; + m_process->ReadMemory((uint64_t)(typeInfo.super), &superti, sizeof(TypeInfo), error); + if (error.Success()) { + auto super_type = GetDynamicTypeFromGenericTypeInfo(ast, superti); + if (!super_type.IsValid()) { + return; + } + if (is_class_member && super_type.GetTypeName() == "std.core::Object") { + // add typeinfo* to class member. + CompilerType ti_type = ast.GetBasicType(eBasicTypeVoid).GetPointerType(); + ast.AddFieldToRecordType(dynamic_type, "$ti*", ti_type, lldb::eAccessPublic, 0); + } else { + // DW_TAG_inheritance + std::vector> bases; + bases.push_back( + ast.CreateBaseClassSpecifier(super_type.GetOpaqueQualType(), lldb::eAccessPublic, false, true)); + ast.TransferBaseClasses(dynamic_type.GetOpaqueQualType(), std::move(bases)); + super_fields_num = superti.fieldsNum; + } + } + } + uint64_t field_type_addr = 0; + uint64_t field_name_addr = 0; + char field_name[name_size + 1]; + field_name[name_size] = '\0'; + for (int16_t i = 0; i < typeInfo.fieldsNum - super_fields_num; i++) { + m_process->ReadMemory((uint64_t)(typeInfo.fields) + i * BitsPerByte, &field_type_addr, sizeof(uint64_t), error); + TypeInfo fieldti; + m_process->ReadMemory(field_type_addr, &fieldti, sizeof(TypeInfo), error); + CompilerType field_type; + if (IsRefType(fieldti.type)) { + field_type = GetDynamicTypeFromGenericTypeInfo(ast, fieldti).GetPointerType(); + } else { + field_type = GetDynamicTypeFromGenericTypeInfo(ast, fieldti); + } + std::string fname = GetReflectionFieldName(i, typeInfo); + auto field = ast.AddFieldToRecordType(dynamic_type, fname.c_str(), field_type, lldb::eAccessPublic, 0); + if (field_type.GetTypeName() == "Unit") { + clang::ASTContext &clang_ast = ast.getASTContext(); + llvm::APInt bitfield_bit_size_apint(clang_ast.getTypeSize(clang_ast.IntTy), + 0); + auto unit_bit_width = new (clang_ast) + clang::IntegerLiteral(clang_ast, bitfield_bit_size_apint, + clang_ast.IntTy, clang::SourceLocation()); + field->setBitWidth(unit_bit_width); + } + } +} + +CompilerType ItaniumABILanguageRuntime::GetDynamicClassType( + TypeSystemClang& ast, TypeInfo& typeInfo, ConstString& type_name) { + if (type_name.GetStringRef().contains("$C")) { + return GetDynamicFuncType(ast, typeInfo, type_name); + } + CompilerType dynamic_type = ast.GetTypeForIdentifier(type_name); + if (dynamic_type.IsValid()) { + return dynamic_type; + } + dynamic_type = ast.CreateRecordType(nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + type_name.GetCString(), clang::TTK_Class, lldb::eLanguageTypeC); + ast.StartTagDeclarationDefinition(dynamic_type); + AddFieldToRecordType(ast, typeInfo, dynamic_type, true); + ast.CompleteTagDeclarationDefinition(dynamic_type); + return dynamic_type; +} + +CompilerType ItaniumABILanguageRuntime::GetDynamicRawArrayType( + TypeSystemClang& ast, TypeInfo& typeInfo, ConstString& type_name) { + CompilerType dynamic_type = ast.GetTypeForIdentifier(type_name); + if (dynamic_type.IsValid()) { + return dynamic_type; + } + dynamic_type = ast.CreateRecordType(nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + type_name.GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); + ast.StartTagDeclarationDefinition(dynamic_type); + // add _typeinfo to raw-array member. + auto ti_type = ast.GetBasicType(eBasicTypeVoid).GetPointerType(); + ast.AddFieldToRecordType(dynamic_type, "$ti*", ti_type, lldb::eAccessPublic, 0); + + // add size to raw-array member. + CompilerType basic_type = ast.GetBasicType(eBasicTypeLongLong).CreateTypedef( + "Int64", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + ast.AddFieldToRecordType(dynamic_type, "size", basic_type, lldb::eAccessPublic, 0); + + // add elements to raw-array member. + CompilerType field_type; + Status error; + TypeInfo fieldti; + m_process->ReadMemory((uint64_t)(typeInfo.super), &fieldti, sizeof(TypeInfo), error); + if (error.Success()) { + if (IsRefType(fieldti.type)) { + field_type = GetDynamicTypeFromGenericTypeInfo(ast, fieldti).GetPointerType(); + } else { + field_type = GetDynamicTypeFromGenericTypeInfo(ast, fieldti); + } + } else { + // If raw-array does not have a field, use UInt8 as the default value. + field_type = ast.GetBasicType(eBasicTypeUnsignedChar).CreateTypedef( + "UInt8", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + } + auto field = ast.AddFieldToRecordType(dynamic_type, "elements", field_type, lldb::eAccessPublic, 0); + if (field_type.GetTypeName() == "Unit") { + clang::ASTContext &clang_ast = ast.getASTContext(); + llvm::APInt bitfield_bit_size_apint(clang_ast.getTypeSize(clang_ast.IntTy), + 0); + auto unit_bit_width = new (clang_ast) + clang::IntegerLiteral(clang_ast, bitfield_bit_size_apint, + clang_ast.IntTy, clang::SourceLocation()); + field->setBitWidth(unit_bit_width); + } + ast.CompleteTagDeclarationDefinition(dynamic_type); + return dynamic_type; +} + +bool IsFunctionType(ConstString& type_name) { + ConstString compare("(.+::|^)\\(.*\\)( ?)->.+$"); + RegularExpression regex(compare.GetStringRef()); + return regex.Execute(type_name.AsCString()); +} + +ConstString GetFunctionTypeName(std::string type_name, + uint64_t para_num, std::vector& param_types, CompilerType& return_type) { + std::string name = type_name.substr(0, type_name.find(":")) + "::("; + uint64_t para_id = 0; + for (auto& para : param_types) { + para_id++; + name += std::string(para.GetTypeName().GetCString()); + if (para_id != para_num) { + name += ","; + } + } + name += ")->" + std::string(return_type.GetTypeName().GetCString()); + std::string enum_prefix = "E1$"; + auto pos = name.find(enum_prefix); + while (pos != std::string::npos) { + name.replace(pos, enum_prefix.size(), ""); + pos = name.find(enum_prefix, pos + 1); + } + return ConstString(name.c_str()); +} + +CompilerType ItaniumABILanguageRuntime::GetDynamicFuncType( + TypeSystemClang& ast, TypeInfo& typeInfo, ConstString& type_name) { + // type_name: (Int32) -> Int32 + Status error; + TypeInfo closure_ti; + TypeInfo func_ti; + bool need_refresh_name = false; + + if (typeInfo.type == UGTypeKind::UG_CLASS) { + m_process->ReadMemory((uint64_t)(typeInfo.super), &closure_ti, sizeof(TypeInfo), error); + uint64_t func_ti_addr = 0; + m_process->ReadMemory((uint64_t)(closure_ti.typeArgs), &func_ti_addr, sizeof(uint64_t), error); + m_process->ReadMemory(func_ti_addr, &func_ti, sizeof(TypeInfo), error); + need_refresh_name = true; + } else if (typeInfo.type == UGTypeKind::UG_FUNC) { + uint64_t func_ti_addr = 0; + m_process->ReadMemory((uint64_t)(typeInfo.typeArgs), &func_ti_addr, sizeof(uint64_t), error); + m_process->ReadMemory(func_ti_addr, &func_ti, sizeof(TypeInfo), error); + need_refresh_name = true; + } else if (typeInfo.type == UGTypeKind::UG_CFUNC) { + func_ti = typeInfo; + } else { + return CompilerType(); + } + + uint8_t type_arg_num = func_ti.typeArgNum; + uint64_t type_args = (uint64_t)func_ti.typeArgs; + + if (!error.Success()) { + return CompilerType(); + } + if (type_arg_num < 1) { + return CompilerType(); + } + // para_typeinfo: type_args[1..type_arg_num-1] + uint64_t para_ti_addr = 0; + TypeInfo fieldti; + std::vector param_types; + for (int8_t i = 1; i < type_arg_num; i++) { + m_process->ReadMemory(type_args + i * BitsPerByte, ¶_ti_addr, sizeof(uint64_t), error); + m_process->ReadMemory(para_ti_addr, &fieldti, sizeof(TypeInfo), error); + param_types.push_back(GetDynamicTypeFromGenericTypeInfo(ast, fieldti)); + } + + // return_typeinfo: type_args[0] + uint64_t return_ti_addr = 0; + m_process->ReadMemory(type_args, &return_ti_addr, sizeof(uint64_t), error); + m_process->ReadMemory(return_ti_addr, &fieldti, sizeof(TypeInfo), error); + if (!error.Success()) { + return CompilerType(); + } + auto return_type = GetDynamicTypeFromGenericTypeInfo(ast, fieldti); + if (need_refresh_name) { + // type_name: default$$Auto_Env__ZN7default3gooEi + type_name = GetFunctionTypeName(type_name.GetCString(), type_arg_num - 1, param_types, return_type); + } + + CompilerType dynamic_type = ast.CreateRecordType(nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + type_name.GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); + ast.StartTagDeclarationDefinition(dynamic_type); + // add typeinfo*. + auto ti_type = ast.GetBasicType(eBasicTypeVoid).GetPointerType(); + ast.AddFieldToRecordType(dynamic_type, "$ti*", ti_type, lldb::eAccessPublic, 0); + // type -> subroutine -> pointer + unsigned type_quals = 0; + auto subroutine_type = ast.CreateFunctionType(return_type, + param_types.data(), param_types.size(), false, type_quals, clang::CC_C); + ast.AddFieldToRecordType(dynamic_type, "ptr", subroutine_type.GetPointerType(), lldb::eAccessPublic, 0); + ast.CompleteTagDeclarationDefinition(dynamic_type); + return dynamic_type; +} + +CompilerType ItaniumABILanguageRuntime::GetDynamicCStringType( + TypeSystemClang& ast, TypeInfo& typeInfo, ConstString& type_name) { + CompilerType dynamic_type = ast.CreateRecordType(nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + type_name.GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); + ast.StartTagDeclarationDefinition(dynamic_type); + ast.AddFieldToRecordType(dynamic_type, "chars", ast.GetCStringType(false), lldb::eAccessPublic, 0); + ast.CompleteTagDeclarationDefinition(dynamic_type); + return dynamic_type; +} + +CompilerType ItaniumABILanguageRuntime::GetDynamicCPointerType( + TypeSystemClang& ast, TypeInfo& typeInfo, ConstString& type_name) { + // CPointer + Status error; + TypeInfo fieldti; + m_process->ReadMemory((uint64_t)(typeInfo.super), &fieldti, sizeof(TypeInfo), error); + if (!error.Success()) { + return CompilerType(); + } + CompilerType dynamic_type = ast.CreateRecordType(nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + type_name.GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); + ast.StartTagDeclarationDefinition(dynamic_type); + auto child_type = GetDynamicTypeFromGenericTypeInfo(ast, fieldti); + ast.AddFieldToRecordType(dynamic_type, "ptr", child_type.GetPointerType(), lldb::eAccessPublic, 0); + ast.CompleteTagDeclarationDefinition(dynamic_type); + return dynamic_type; +} + +CompilerType ItaniumABILanguageRuntime::GetDynamicCFuncType( + TypeSystemClang& ast, TypeInfo& typeInfo, ConstString& type_name) { + // type_name: (Int32) -> Int32 + Status error; + int8_t type_arg_num = typeInfo.typeArgNum; + uint64_t type_args = (uint64_t)typeInfo.typeArgs; + if (type_arg_num < 1) { + return CompilerType(); + } + // para_num: type_arg_num - 1 + int8_t para_num = type_arg_num - 1; + + // para_typeinfo: type_args + uint64_t para_ti_addr = 0; + TypeInfo fieldti; + std::vector param_types; + for (int8_t i = 0; i < para_num; i++) { + m_process->ReadMemory(type_args + i * BitsPerByte, ¶_ti_addr, sizeof(uint64_t), error); + m_process->ReadMemory(para_ti_addr, &fieldti, sizeof(TypeInfo), error); + param_types.push_back(GetDynamicTypeFromGenericTypeInfo(ast, fieldti)); + } + + // return_typeinfo: super.typeArgs + para_num * BitsPerByte + uint64_t return_ti_addr = 0; + m_process->ReadMemory(type_args + para_num * BitsPerByte, &return_ti_addr, sizeof(uint64_t), error); + m_process->ReadMemory(return_ti_addr, &fieldti, sizeof(TypeInfo), error); + if (!error.Success()) { + return CompilerType(); + } + auto return_type = GetDynamicTypeFromGenericTypeInfo(ast, fieldti); + CompilerType dynamic_type = ast.CreateRecordType(nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + type_name.GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); + ast.StartTagDeclarationDefinition(dynamic_type); + unsigned type_quals = 0; + auto subroutine_type = ast.CreateFunctionType(return_type, + param_types.data(), param_types.size(), false, type_quals, clang::CC_C); + ast.AddFieldToRecordType(dynamic_type, "ptr", subroutine_type.GetPointerType(), lldb::eAccessPublic, 0); + ast.CompleteTagDeclarationDefinition(dynamic_type); + return dynamic_type; +} + +CompilerType ItaniumABILanguageRuntime::GetDynamicVArrayType( + TypeSystemClang& ast, TypeInfo& typeInfo, ConstString& type_name) { + // VArray + Status error; + TypeInfo fieldti; + m_process->ReadMemory((uint64_t)(typeInfo.super), &fieldti, sizeof(TypeInfo), error); + if (!error.Success()) { + return CompilerType(); + } + auto field_type = GetDynamicTypeFromGenericTypeInfo(ast, fieldti); + if (!type_name.GetStringRef().contains("$")) { + // VArray + std::string field_type_name = field_type.GetTypeName().GetCString(); + std::string full_type_name = "VArray<" + field_type_name + ",$" + std::to_string(typeInfo.fieldsNum) + ">"; + type_name = ConstString(full_type_name.c_str()); + } + return ast.CreateArrayType(field_type, typeInfo.fieldsNum, false).CreateTypedef( + type_name.GetCString(), ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); +} + +CompilerType ItaniumABILanguageRuntime::GetDynamicTupleType( + TypeSystemClang& ast, TypeInfo& typeInfo, ConstString& type_name) { + Status error; + CompilerType dynamic_type = ast.GetTypeForIdentifier(type_name); + if (dynamic_type.IsValid()) { + return dynamic_type; + } + dynamic_type = ast.CreateRecordType(nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + type_name.GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); + ast.StartTagDeclarationDefinition(dynamic_type); + uint64_t field_type_addr = 0; + TypeInfo fieldti; + for (int16_t i = 0; i < typeInfo.fieldsNum; i++) { + m_process->ReadMemory((uint64_t)(typeInfo.fields) + i * BitsPerByte, &field_type_addr, sizeof(uint64_t), error); + m_process->ReadMemory(field_type_addr, &fieldti, sizeof(TypeInfo), error); + auto field_type = GetDynamicTypeFromGenericTypeInfo(ast, fieldti); + std::string field_name = "_" + std::to_string(i); + ast.AddFieldToRecordType(dynamic_type, field_name.c_str(), field_type, lldb::eAccessPublic, 0); + } + ast.CompleteTagDeclarationDefinition(dynamic_type); + return dynamic_type; +} + +CompilerType ItaniumABILanguageRuntime::GetDynamicStructType( + TypeSystemClang& ast, TypeInfo& typeInfo, ConstString& type_name) { + CompilerType dynamic_type = ast.GetTypeForIdentifier(type_name); + if (dynamic_type.IsValid()) { + return dynamic_type; + } + dynamic_type = ast.CreateRecordType(nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + type_name.GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); + ast.StartTagDeclarationDefinition(dynamic_type); + AddFieldToRecordType(ast, typeInfo, dynamic_type); + ast.CompleteTagDeclarationDefinition(dynamic_type); + return dynamic_type; +} + +static CompilerType CreateEnumType(TypeSystemClang& ast, Process& process, + std::string& enum_name, uint64_t ctor_num, uint64_t enumCtorInfo_addr) { + // enum_name: "RGBColor" + CompilerType basic_type = ast.GetBasicType(eBasicTypeInt).CreateTypedef( + "Int32", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + CompilerType enumType = ast.CreateEnumerationType(enum_name.c_str(), + ast.GetTranslationUnitDecl(), OptionalClangModuleID(), Declaration(), basic_type, false); + ast.StartTagDeclarationDefinition(enumType); + Status error; + char ctor_name[name_size + 1]; + ctor_name[name_size] = '\0'; + // The `relection` field of `TypeInfo` has two structures: one for enums and another for non-enums. + // for enums named EnumReflection + for (uint64_t i = 0; i < ctor_num; i++) { + uint64_t name_addr = 0; + uint64_t name_addr_offset = 0; + process.ReadMemory(enumCtorInfo_addr + i * BitsPerWidth, &name_addr_offset, sizeof(uint64_t), error); + name_addr = enumCtorInfo_addr + i * BitsPerWidth + name_addr_offset; + process.ReadMemory(name_addr, ctor_name, name_size, error); + ast.AddEnumerationValueToEnumerationType(enumType, Declaration(), ctor_name, i, 4); + } + ast.CompleteTagDeclarationDefinition(enumType); + return enumType; +} + +void ItaniumABILanguageRuntime::CreateAndAddInheritTypeToRecordType(CompilerType& dynamic_type, CompilerType& enum_type, + TypeSystemClang& ast, uint64_t ctor_num, uint64_t ctors_addr) { + CompilerType basic_type = ast.GetBasicType(eBasicTypeLongLong).CreateTypedef( + "Int64", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + auto ti_type = ast.GetBasicType(eBasicTypeVoid).GetPointerType(); + TypeInfo ctor_ti; + Status error; + std::string enum_name(enum_type.GetTypeName().AsCString()); + std::vector> bases; // DW_TAG_inheritance + for (uint64_t i = 0; i < ctor_num; i++) { + auto ctor_name = enum_name + "_ctor_" + std::to_string(i); + auto ctor_type = ast.CreateRecordType(nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + ctor_name.c_str(), clang::TTK_Struct, lldb::eLanguageTypeC); + ast.StartTagDeclarationDefinition(ctor_type); + ast.AddFieldToRecordType(ctor_type, "$ti*", ti_type, lldb::eAccessPublic, 0); + ast.AddFieldToRecordType(ctor_type, "constructor", enum_type, lldb::eAccessPublic, 0); + uint64_t ctor_addr = 0; + m_process->ReadMemory(ctors_addr + i * BitsPerByte, &ctor_addr, sizeof(uint64_t), error); + m_process->ReadMemory(ctor_addr, &ctor_ti, sizeof(TypeInfo), error); + if (!error.Success()) { + continue; + } + if (ctor_ti.fieldsNum > 1) { + uint64_t ctor_para_addr = 0; + TypeInfo ctor_para_ti; + for (int16_t j = 0; j < ctor_ti.fieldsNum - 1; j++) { + m_process->ReadMemory((uint64_t)(ctor_ti.fields) + (j + 1) * BitsPerByte, &ctor_para_addr, + sizeof(uint64_t), error); + m_process->ReadMemory(ctor_para_addr, &ctor_para_ti, sizeof(TypeInfo), error); + if (!error.Success()) { + break; + } + auto ctor_para_type = GetDynamicTypeFromGenericTypeInfo(ast, ctor_para_ti); + std::string ctor_para_name = "arg_" + std::to_string(j + 1); + ast.AddFieldToRecordType(ctor_type, ctor_para_name.c_str(), + IsRefType(ctor_para_ti.type) ? ctor_para_type.GetPointerType() : ctor_para_type, lldb::eAccessPublic, 0); + } + } + ast.CompleteTagDeclarationDefinition(ctor_type); + auto inherit_type = ast.CreateBaseClassSpecifier(ctor_type.GetOpaqueQualType(), + lldb::eAccessPublic, false, true); + auto type_source_info = inherit_type->getTypeSourceInfo(); + if (type_source_info) { + auto type = ast.GetType(type_source_info->getType()); + ast.StartTagDeclarationDefinition(type); + ast.CompleteTagDeclarationDefinition(type); + } + bases.push_back(std::move(inherit_type)); + } + ast.TransferBaseClasses(dynamic_type.GetOpaqueQualType(), std::move(bases)); +} + +void ItaniumABILanguageRuntime::CreateAndAddInheritTypeToEnum3Type(CompilerType& dynamic_type, CompilerType& enum_type, + TypeSystemClang& ast, uint64_t ctor_num, uint64_t ctors_addr) { + TypeInfo ctor_ti; + Status error; + std::string enum_name(enum_type.GetTypeName().AsCString()); + std::vector> bases; // DW_TAG_inheritance + for (uint64_t i = 0; i < ctor_num; i++) { + auto ctor_name = enum_name + "_ctor_" + std::to_string(i); + auto ctor_type = ast.CreateRecordType(nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + ctor_name.c_str(), clang::TTK_Struct, lldb::eLanguageTypeC); + ast.StartTagDeclarationDefinition(ctor_type); + uint64_t ctor_addr = 0; + // the offset of the enumCtors array member is 16, type info offset is 8. + m_process->ReadMemory(ctors_addr + i * BitsPerWidth + BitsPerByte, &ctor_addr, sizeof(uint64_t), error); + m_process->ReadMemory(ctor_addr, &ctor_ti, sizeof(TypeInfo), error); + if (!error.Success()) { + continue; + } + ast.AddFieldToRecordType(ctor_type, "constructor", enum_type, lldb::eAccessPublic, 0); + if (ctor_ti.fieldsNum > 1) { + uint64_t ctor_para_addr = 0; + TypeInfo ctor_para_ti; + for (int16_t j = 1; j < ctor_ti.fieldsNum; j++) { + m_process->ReadMemory((uint64_t)(ctor_ti.fields) + j * BitsPerByte, &ctor_para_addr, + sizeof(uint64_t), error); + m_process->ReadMemory(ctor_para_addr, &ctor_para_ti, sizeof(TypeInfo), error); + if (!error.Success()) { + break; + } + auto ctor_para_type = GetDynamicTypeFromGenericTypeInfo(ast, ctor_para_ti); + std::string ctor_para_name = "arg_" + std::to_string(j); + ast.AddFieldToRecordType(ctor_type, ctor_para_name.c_str(), ctor_para_type, lldb::eAccessPublic, 0); + } + } + auto inherit_type = ast.CreateBaseClassSpecifier(ctor_type.GetOpaqueQualType(), + lldb::eAccessPublic, false, true); + auto type_source_info = inherit_type->getTypeSourceInfo(); + if (type_source_info) { + auto type = ast.GetType(type_source_info->getType()); + ast.StartTagDeclarationDefinition(type); + ast.CompleteTagDeclarationDefinition(type); + } + bases.push_back(std::move(inherit_type)); + } + ast.TransferBaseClasses(dynamic_type.GetOpaqueQualType(), std::move(bases)); +} + +CompilerType ItaniumABILanguageRuntime::CreateEnum2Type(TypeSystemClang& ast, + CompilerType& enum_type, ConstString type_name, uint64_t ctor_num, uint64_t enumCtorInfo_addr) { + bool is_reference = false; + auto dynamic_type = ast.CreateRecordType(nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + type_name.GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); + ast.StartTagDeclarationDefinition(dynamic_type); + TypeInfo ctor_ti; + Status error; + std::string enum_name(enum_type.GetTypeName().AsCString()); + for (uint64_t i = 0; i < ctor_num; i++) { + uint64_t ctor_addr = 0; + // the offset of the enumCtors array member is 16, type info offset is 8. + m_process->ReadMemory(enumCtorInfo_addr + i * BitsPerWidth + BitsPerByte, &ctor_addr, sizeof(uint64_t), error); + m_process->ReadMemory(ctor_addr, &ctor_ti, sizeof(TypeInfo), error); + if (!error.Success()) { + continue; + } + if (ctor_ti.fieldsNum < 1) { + continue; + } + uint64_t ctor_para_addr = 0; + TypeInfo ctor_para_ti; + for (int16_t j = 1; j < ctor_ti.fieldsNum; j++) { + m_process->ReadMemory((uint64_t)(ctor_ti.fields) + (j) * BitsPerByte, &ctor_para_addr, sizeof(uint64_t), error); + m_process->ReadMemory(ctor_para_addr, &ctor_para_ti, sizeof(TypeInfo), error); + if (!error.Success()) { + break; + } + auto ctor_para_type = GetDynamicTypeFromGenericTypeInfo(ast, ctor_para_ti); + ast.AddFieldToRecordType(dynamic_type, "constructor", enum_type, lldb::eAccessPublic, 0); + if (IsRefType(ctor_para_ti.type)) { + is_reference = true; + ast.AddFieldToRecordType(dynamic_type, "val", ctor_para_type.GetPointerType(), lldb::eAccessPublic, 0); + } else { + ast.AddFieldToRecordType(dynamic_type, "val", ctor_para_type, lldb::eAccessPublic, 0); + } + } + } + ast.CompleteTagDeclarationDefinition(dynamic_type); + return is_reference ? dynamic_type.GetPointerType() : dynamic_type; +} + +CompilerType ItaniumABILanguageRuntime::GetDynamicOptionType( + TypeSystemClang& ast, TypeInfo& typeInfo, ConstString& type_name, CompilerType& enum_type) { + Status error; + uint64_t val_ti_addr = 0; + TypeInfo valti; + m_process->ReadMemory((uint64_t)typeInfo.typeArgs, &val_ti_addr, sizeof(uint64_t), error); + m_process->ReadMemory(val_ti_addr, &valti, sizeof(TypeInfo), error); + if (!error.Success()) { + CompilerType(); + } + auto option_type = ast.CreateRecordType(nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + type_name.GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); + ast.StartTagDeclarationDefinition(option_type); + auto val_type = GetDynamicTypeFromGenericTypeInfo(ast, valti); + if (IsRefType(valti.type)) { + ast.AddFieldToRecordType(option_type, "val", val_type.GetPointerType(), lldb::eAccessPublic, 0); + } else { + ast.AddFieldToRecordType(option_type, "constructor", enum_type, lldb::eAccessPublic, 0); + ast.AddFieldToRecordType(option_type, "val", val_type, lldb::eAccessPublic, 0); + } + ast.CompleteTagDeclarationDefinition(option_type); + return option_type; +} + +ReflectModifyType ItaniumABILanguageRuntime::GetEnumKind(TypeInfo& typeInfo) { + uint32_t modifier = 0; + Status error; + uint64_t modifier_addr = (uint64_t)typeInfo.reflection + BitsPerByte; + m_process->ReadMemory(modifier_addr, &modifier, sizeof(uint32_t), error); + bool isEnumKind0 = static_cast(modifier & RMT_ENUM_KIND0); + bool isEnumKind1 = static_cast(modifier & RMT_ENUM_KIND1); + bool isEnumKind2 = static_cast(modifier & RMT_ENUM_KIND2); + bool isEnumKind3 = static_cast(modifier & RMT_ENUM_KIND3); + if (isEnumKind0) { + return RMT_ENUM_KIND0; + } else if (isEnumKind1) { + return RMT_ENUM_KIND1; + } else if (isEnumKind2) { + return RMT_ENUM_KIND2; + } else if (isEnumKind3) { + return RMT_ENUM_KIND3; + } else { + return RMT_MAX; + } +} + +CompilerType ItaniumABILanguageRuntime::GetDynamicEnumType( + TypeSystemClang& ast, TypeInfo& typeInfo, ConstString& type_name) { + Status error; + auto typekind = static_cast(typeInfo.type); + if (typekind == UGTypeKind::UG_COMMON_ENUM) { + m_process->ReadMemory(reinterpret_cast(typeInfo.super), &typeInfo, sizeof(typeInfo), error); + type_name = GetTypeName(*m_process, typeInfo); + } + const std::string ENUM_PREFIX_NAME = "E1$"; + const std::string E0_PREFIX_NAME = "E0$"; + // type_name: "default::E1$RGBColor" + std::string enum_name(type_name.GetCString()); + auto pos = enum_name.find("$"); + if (pos != std::string::npos) { + auto ltPos = enum_name.find(ENUM_PREFIX_NAME); + if (ltPos != std::string::npos) { + enum_name = enum_name.substr(0, ltPos) + enum_name.substr(ltPos+ ENUM_PREFIX_NAME.size(), enum_name.size()); + } + ltPos = enum_name.find(E0_PREFIX_NAME); + if (ltPos != std::string::npos) { + enum_name = enum_name.substr(0, ltPos) + enum_name.substr(ltPos+ E0_PREFIX_NAME.size(), enum_name.size()); + } + } else { + // std.core::Option + pos = enum_name.find("::"); + if (pos != std::string::npos) { + enum_name = enum_name.substr(pos + 2); // size of "::" is 2 + } + } + // Memory Layout of Reflection + // enumCtors -> [name + typeinfo]... + // u32 modifier + // u32 enumCtorInfoCnt + uint32_t ctor_num = 0; + ReflectModifyType enum_kind = GetEnumKind(typeInfo); + // enumCtor count's offset is 12. + m_process->ReadMemory((uint64_t)typeInfo.reflection + 12, &ctor_num, sizeof(uint32_t), error); + if (ctor_num == 0) { + return CompilerType(); + } + uint64_t ctors_addr = (uint64_t)typeInfo.reflection; + uint64_t ctor_para_addr = 0; + m_process->ReadMemory(ctors_addr, &ctor_para_addr, sizeof(uint64_t), error); + ctor_para_addr = ctors_addr + ctor_para_addr; + + auto enum_type = CreateEnumType(ast, *m_process, enum_name, ctor_num, ctor_para_addr); + if (enum_kind == ReflectModifyType::RMT_ENUM_KIND0) { + return enum_type; + } + // std.core::Option + ConstString match("^std[.]core::(Enum\\$)?Option<.+>( \\*)?$"); + RegularExpression regex(match.GetStringRef()); + if (regex.Execute(type_name.AsCString())) { + return GetDynamicOptionType(ast, typeInfo, type_name, enum_type); + } + + if (enum_kind == ReflectModifyType::RMT_ENUM_KIND2) { + std::string enum_type_name(type_name.AsCString()); + if (auto pos = enum_type_name.find("E1$"); pos != std::string::npos) { + enum_type_name.replace(pos, std::string("E1$").size(), "E2$"); + } + type_name.SetCString(enum_type_name.c_str()); + return CreateEnum2Type(ast, enum_type, type_name, ctor_num, ctor_para_addr); + } + if (enum_kind == ReflectModifyType::RMT_ENUM_KIND3) { + std::string enum_type_name(type_name.AsCString()); + if (auto pos = enum_type_name.find("E1$"); pos != std::string::npos) { + enum_type_name.replace(pos, std::string("E1$").size(), "E3$"); + } + type_name.SetCString(enum_type_name.c_str()); + } + + auto dynamic_type = ast.GetTypeForIdentifier(type_name); + if (dynamic_type.IsValid()) { + return dynamic_type; + } + dynamic_type = ast.CreateRecordType(nullptr, OptionalClangModuleID(), lldb::eAccessPublic, + type_name.GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); + ast.StartTagDeclarationDefinition(dynamic_type); + auto ti_type = ast.GetBasicType(eBasicTypeVoid).GetPointerType(); + ast.AddFieldToRecordType(dynamic_type, "$ti*", ti_type, lldb::eAccessPublic, 0); + if (enum_kind == ReflectModifyType::RMT_ENUM_KIND3) { + CreateAndAddInheritTypeToEnum3Type(dynamic_type, enum_type, ast, ctor_num, ctor_para_addr); + } else { + CreateAndAddInheritTypeToRecordType(dynamic_type, enum_type, ast, ctor_num, ctor_para_addr); + } + ast.CompleteTagDeclarationDefinition(dynamic_type); + + return dynamic_type; +} + +CompilerType ItaniumABILanguageRuntime::GetDynamicTypeFromPrimitiveType(TypeSystemClang& ast, TypeInfo& typeInfo) { + CompilerType dynamic_type; + auto typekind = static_cast(typeInfo.type); + switch (typekind) { + case UGTypeKind::UG_NOTHING: + break; + case UGTypeKind::UG_UNIT: { + dynamic_type = ast.CreateRecordType(nullptr, OptionalClangModuleID(), + lldb::eAccessPublic, "Unit", clang::TTK_Struct, lldb::eLanguageTypeC); + ast.StartTagDeclarationDefinition(dynamic_type); + ast.CompleteTagDeclarationDefinition(dynamic_type); + break; + } + case UGTypeKind::UG_BOOLEAN: + dynamic_type = ast.GetBasicType(eBasicTypeBool).CreateTypedef( + "Bool", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + break; + case UGTypeKind::UG_RUNE: + dynamic_type = ast.GetBasicType(eBasicTypeChar32).CreateTypedef( + "Rune", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + break; + case UGTypeKind::UG_UINT8: + dynamic_type = ast.GetBasicType(eBasicTypeUnsignedChar).CreateTypedef( + "UInt8", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + break; + case UGTypeKind::UG_UINT16: + dynamic_type = ast.GetBasicType(eBasicTypeUnsignedShort).CreateTypedef( + "UInt16", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + break; + case UGTypeKind::UG_UINT32: + dynamic_type = ast.GetBasicType(eBasicTypeUnsignedInt).CreateTypedef( + "UInt32", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + break; + case UGTypeKind::UG_UINT64: + dynamic_type = ast.GetBasicType(eBasicTypeUnsignedLongLong).CreateTypedef( + "UInt64", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + break; + case UGTypeKind::UG_UINT_NATIVE: + dynamic_type = ast.GetBasicType(eBasicTypeUnsignedLongLong).CreateTypedef( + "UIntNative", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + break; + case UGTypeKind::UG_INT8: + dynamic_type = ast.GetBasicType(eBasicTypeSignedChar).CreateTypedef( + "Int8", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + break; + case UGTypeKind::UG_INT16: + dynamic_type = ast.GetBasicType(eBasicTypeShort).CreateTypedef( + "Int16", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + break; + case UGTypeKind::UG_INT32: + dynamic_type = ast.GetBasicType(eBasicTypeInt).CreateTypedef( + "Int32", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + break; + case UGTypeKind::UG_INT64: + dynamic_type = ast.GetBasicType(eBasicTypeLongLong).CreateTypedef( + "Int64", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + break; + case UGTypeKind::UG_INT_NATIVE: + dynamic_type = ast.GetBasicType(eBasicTypeLongLong).CreateTypedef( + "IntNative", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + break; + case UGTypeKind::UG_FLOAT16: + dynamic_type = ast.GetBasicType(eBasicTypeHalf).CreateTypedef( + "Float16", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + break; + case UGTypeKind::UG_FLOAT32: + dynamic_type = ast.GetBasicType(eBasicTypeFloat).CreateTypedef( + "Float32", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + break; + case UGTypeKind::UG_FLOAT64: + dynamic_type = ast.GetBasicType(eBasicTypeDouble).CreateTypedef( + "Float64", ast.CreateDeclContext(ast.GetTranslationUnitDecl()), 0); + break; + default: + break; + } + return dynamic_type; +} + +CompilerType ItaniumABILanguageRuntime::GetDynamicTypeFromGenericTypeInfo( + TypeSystemClang& ast, TypeInfo& typeInfo) { + auto type_name = GetTypeName(*m_process, typeInfo); + if (type_name.IsEmpty()) { + return CompilerType(); + } + auto typekind = static_cast(typeInfo.type); + // For Primitive type + if (typekind >= UGTypeKind::UG_NOTHING && typekind <= UGTypeKind::UG_FLOAT64) { + return GetDynamicTypeFromPrimitiveType(ast, typeInfo); + } + // For other types. + CompilerType dynamic_type; + switch (typekind) { + case UGTypeKind::UG_CLASS: + case UGTypeKind::UG_INTERFACE: + dynamic_type = GetDynamicClassType(ast, typeInfo, type_name); + break; + case UGTypeKind::UG_RAWARRAY: + dynamic_type = GetDynamicRawArrayType(ast, typeInfo, type_name); + break; + case UGTypeKind::UG_FUNC: + dynamic_type = GetDynamicFuncType(ast, typeInfo, type_name); + break; + case UGTypeKind::UG_CSTRING: + dynamic_type = GetDynamicCStringType(ast, typeInfo, type_name); + break; + case UGTypeKind::UG_CPOINTER: + dynamic_type = GetDynamicCPointerType(ast, typeInfo, type_name); + break; + case UGTypeKind::UG_CFUNC: + dynamic_type = GetDynamicCFuncType(ast, typeInfo, type_name); + break; + case UGTypeKind::UG_VARRAY: + dynamic_type = GetDynamicVArrayType(ast, typeInfo, type_name); + break; + case UGTypeKind::UG_TUPLE: + dynamic_type = GetDynamicTupleType(ast, typeInfo, type_name); + break; + case UGTypeKind::UG_STRUCT: + dynamic_type = GetDynamicStructType(ast, typeInfo, type_name); + break; + case UGTypeKind::UG_ENUM: + case UGTypeKind::UG_COMMON_ENUM: + dynamic_type = GetDynamicEnumType(ast, typeInfo, type_name); + break; + default: + break; + } + return dynamic_type; +} + +bool ItaniumABILanguageRuntime::GetGenericDynamicType(ValueObject &in_value, + TypeAndOrName &class_type_or_name, Address &dynamic_address, Value::ValueType &value_type) { + auto generic_type = in_value.GetCompilerType(); + auto *ast = llvm::dyn_cast_or_null(generic_type.GetTypeSystem()); + if (!ast) { + return false; + } + Status error; + uint64_t type_info_addr = 0; + TypeInfo typeInfo; + m_process->ReadMemory(in_value.GetAddressOf(), &type_info_addr, sizeof(uint64_t), error); + m_process->ReadMemory(type_info_addr, &typeInfo, sizeof(typeInfo), error); + if (!error.Success()) { + return false; + } + auto dynamic_type = GetDynamicTypeFromGenericTypeInfo(*ast, typeInfo); + if (!dynamic_type.IsValid()) { + return false; + } + in_value.SetGenericValueType(!IsRefType(typeInfo.type)); + class_type_or_name.SetCompilerType(dynamic_type); + in_value.SetOverrideType(dynamic_type); + dynamic_address = IsRefType(typeInfo.type) ? + Address(in_value.GetAddressOf()) : Address(in_value.GetAddressOf() + BitsPerByte); + value_type = Value::ValueType::LoadAddress; + return true; +} + bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress( ValueObject &in_value, lldb::DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name, Address &dynamic_address, @@ -193,7 +1164,16 @@ bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress( // contain the full class name. The second pointer above the "address point" // is the "offset_to_top". We'll use that to get the start of the value // object which holds the dynamic type. - // + if (in_value.GetCangjieDynamicType(class_type_or_name)) { + if (in_value.IsCangjieGenericType()) { + return GetGenericDynamicType(in_value, class_type_or_name, dynamic_address, value_type); + } + // Other types except struct already have the typeinfo type in debuginfo, and no offset 8 is required. + dynamic_address = in_value.GetCompilerType().GetTypeClass() == lldb::eTypeClassClass ? + Address(in_value.GetAddressOf()) : Address(in_value.GetAddressOf() + BitsPerByte); + value_type = Value::ValueType::LoadAddress; + return true; + } class_type_or_name.Clear(); value_type = Value::ValueType::Scalar; diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h index ca8d5ab1a..899538b80 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h @@ -4,25 +4,104 @@ // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// //===----------------------------------------------------------------------===// #ifndef LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_CPLUSPLUS_ITANIUMABI_ITANIUMABILANGUAGERUNTIME_H #define LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_CPLUSPLUS_ITANIUMABI_ITANIUMABILANGUAGERUNTIME_H -#include -#include -#include - -#include "lldb/Breakpoint/BreakpointResolver.h" -#include "lldb/Core/Value.h" -#include "lldb/Symbol/Type.h" #include "lldb/Target/LanguageRuntime.h" -#include "lldb/lldb-private.h" - #include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "clang/Sema/Sema.h" namespace lldb_private { +// The following TypeInfo structure is the same as that in cangjie/libs/std/core/native/typetemplate.c +typedef void* (*FnPtrType)(int32_t, void**); + +#pragma pack(push, 8) +typedef struct TypeTemplate { + const char* name; + int8_t typeKind; + uint8_t flag; + uint16_t fieldsNum; // Number of member variables + uint16_t typeArgsNum; // Number of generic parameters + FnPtrType* fieldsFns; // member variables + FnPtrType superFn; + void* finalizer; + void* reflection; // Reflection Metadata Information + void** extensionDefPtr; + uint16_t inheritedClassNum; +} TypeTemplate; +#pragma pack(pop) + +#pragma pack(push, 8) +typedef struct TypeInfo { +private: + const char* name; + +public: + int8_t type; + uint8_t flag; + uint16_t fieldsNum; // Number of member variables + uint32_t size; // Memory Size in Bytes + const void* gcTib; + uint32_t uuid; + uint8_t align; + uint8_t typeArgNum; // Number of generic parameters + uint16_t inheritedClassNum; + uint32_t* offsets; + union { + // If `typeArgNum` is greater than 0, it indicates `sourceGeneric`. Otherwise, it indicates `finalizer`. + const TypeTemplate* sourceGeneric; // Raw Generic Template + void* finalizer; + }; + const struct TypeInfo** typeArgs; // Generic parameters list + const struct TypeInfo** fields; // Member variable list + const struct TypeInfo* super; + void** extensionDefPtr; + void* mtable; + void* reflection; // Reflection Metadata Information + + bool IsFunc() const; + bool IsCFunc() const; + bool IsVArray() const; + std::string GetName(Process& process) const; +} TypeInfo; +#pragma pack(pop) + +enum ReflectModifyType : uint32_t { + RMT_DEFAULT = (uint32_t)0x1 << 0, + RMT_PRIVATE = (uint32_t)0x1 << 1, + RMT_PROTECTED = (uint32_t)0x1 << 2, + RMT_PUBLIC = (uint32_t)0x1 << 3, + RMT_IMMUTABLE = (uint32_t)0x1 << 4, + RMT_BOXCLASS = (uint32_t)0x1 << 5, + RMT_OPEN = (uint32_t)0x1 << 6, + RMT_OVERRIDE = (uint32_t)0x1 << 7, + RMT_REDEF = (uint32_t)0x1 << 8, + RMT_ABSTRACT = (uint32_t)0x1 << 9, + RMT_SEALED = (uint32_t)0x1 << 10, + RMT_MUT = (uint32_t)0x1 << 11, + RMT_STATIC = (uint32_t)0x1 << 12, + RMT_ENUM_KIND0 = (uint32_t)0x1 << 13, + RMT_ENUM_KIND1 = (uint32_t)0x1 << 14, + RMT_ENUM_KIND2 = (uint32_t)0x1 << 15, + RMT_ENUM_KIND3 = (uint32_t)0x1 << 16, + RMT_HAS_SRET0 = (uint32_t)0x1 << 17, // Has sret but it is'not generic + RMT_HAS_SRET1 = (uint32_t)0x1 << 18, // Has sret and it is 'T' + RMT_HAS_SRET2 = (uint32_t)0x1 << 19, // Has sret and it is 'known struct T' + RMT_HAS_SRET3 = (uint32_t)0x1 << 20, // Has sret and it is 'unknow struct T' + RMT_MAX = (uint32_t)0x1 << 21, +}; +const int32_t BitsPerByte = 8; +const int32_t BitsPerWidth = 16; class ItaniumABILanguageRuntime : public lldb_private::CPPLanguageRuntime { public: ~ItaniumABILanguageRuntime() override = default; @@ -46,6 +125,66 @@ public: static bool classof(const LanguageRuntime *runtime) { return runtime->isA(&ID); } + std::string GetReflectionFieldName(int16_t id, TypeInfo& typeInfo); + void AddFieldToRecordType(TypeSystemClang& ast, + TypeInfo& typeInfo, + CompilerType& dynamic_type, + bool is_class_member = false); + CompilerType GetDynamicClassType(TypeSystemClang& ast, + TypeInfo& typeInfo, + ConstString& type_name); + CompilerType GetDynamicRawArrayType(TypeSystemClang& ast, + TypeInfo& typeInfo, + ConstString& type_name); + CompilerType GetDynamicFuncType(TypeSystemClang& ast, + TypeInfo& typeInfo, + ConstString& type_name); + CompilerType GetDynamicCStringType(TypeSystemClang& ast, + TypeInfo& typeInfo, + ConstString& type_name); + CompilerType GetDynamicCPointerType(TypeSystemClang& ast, + TypeInfo& typeInfo, + ConstString& type_name); + CompilerType GetDynamicCFuncType(TypeSystemClang& ast, + TypeInfo& typeInfo, + ConstString& type_name); + CompilerType GetDynamicVArrayType(TypeSystemClang& ast, + TypeInfo& typeInfo, + ConstString& type_name); + CompilerType GetDynamicTupleType(TypeSystemClang& ast, + TypeInfo& typeInfo, + ConstString& type_name); + CompilerType GetDynamicStructType(TypeSystemClang& ast, + TypeInfo& typeInfo, + ConstString& type_name); + void CreateAndAddInheritTypeToRecordType(CompilerType& enum_type, + CompilerType& dynamic_type, + TypeSystemClang& ast, + uint64_t ctor_num, + uint64_t ctors_addr); + CompilerType CreateEnum2Type(TypeSystemClang& ast, CompilerType& enum_type, + ConstString type_name, + uint64_t ctor_num, uint64_t ctors_addr); + void CreateAndAddInheritTypeToEnum3Type(CompilerType& enum_type, + CompilerType& dynamic_type, + TypeSystemClang& ast, + uint64_t ctor_num, + uint64_t ctors_addr); + CompilerType GetDynamicOptionType(TypeSystemClang& ast, + TypeInfo& typeInfo, + ConstString& type_name, + CompilerType& enum_type); + CompilerType GetDynamicEnumType(TypeSystemClang& ast, + TypeInfo& typeInfo, + ConstString& type_name); + CompilerType GetDynamicTypeFromPrimitiveType(TypeSystemClang& ast, + TypeInfo& typeInfo); + CompilerType GetDynamicTypeFromGenericTypeInfo(TypeSystemClang& ast, + TypeInfo& typeInfo); + bool GetGenericDynamicType(ValueObject &in_value, + TypeAndOrName &class_type_or_name, + Address &dynamic_address, + Value::ValueType &value_type); bool GetDynamicTypeAndAddress(ValueObject &in_value, lldb::DynamicValueType use_dynamic, @@ -66,6 +205,7 @@ public: bool ExceptionBreakpointsExplainStop(lldb::StopInfoSP stop_reason) override; + ReflectModifyType GetEnumKind(TypeInfo& typeInfo); lldb::BreakpointResolverSP CreateExceptionResolver(const lldb::BreakpointSP &bkpt, bool catch_bp, bool throw_bp) override; diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp index 800bd9d6e..b5acada60 100644 --- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp @@ -809,6 +809,11 @@ void ObjectFilePECOFF::ParseSymtab(Symtab &symtab) { symbol.value); symbols[i].GetAddressRef() = symbol_addr; symbols[i].SetType(MapSymbolType(symbol.type)); + // When the clang compiler compiles dynamic links, the symbol type of adaptation layer is 0. + // Set this symbol type to eSymbolTypeCode. and we need exclude .text symbol + if (symbol_name != ".text" && symbol.type == 0) { + symbols[i].SetType(lldb::eSymbolTypeCode); + } } if (symbol.naux > 0) { @@ -850,12 +855,12 @@ void ObjectFilePECOFF::ParseSymtab(Symtab &symtab) { lldb::offset_t name_ordinal_offset = export_table.address_of_name_ordinals - data_start; - Symbol *symbols = symtab.Resize(export_table.number_of_names); + Symbol *symbols = symtab.Resize(num_syms + export_table.number_of_names); std::string symbol_name; // Read each export table entry - for (size_t i = 0; i < export_table.number_of_names; ++i) { + for (size_t i = num_syms; i < num_syms + export_table.number_of_names; ++i) { uint32_t name_ordinal = has_ordinal ? symtab_data.GetU16(&name_ordinal_offset) : i; uint32_t name_address = symtab_data.GetU32(&name_offset); diff --git a/lldb/source/Plugins/Platform/CMakeLists.txt b/lldb/source/Plugins/Platform/CMakeLists.txt index 6869587f9..8ff59c124 100644 --- a/lldb/source/Plugins/Platform/CMakeLists.txt +++ b/lldb/source/Plugins/Platform/CMakeLists.txt @@ -8,3 +8,4 @@ add_subdirectory(OpenBSD) add_subdirectory(POSIX) add_subdirectory(QemuUser) add_subdirectory(Windows) +add_subdirectory(OHOS) diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSXProperties.td b/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSXProperties.td index f0d305a63..0153bfa62 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSXProperties.td +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSXProperties.td @@ -8,7 +8,7 @@ let Definition = "platformdarwinkernel" in { let Definition = "platformdarwin" in { def IgnoredExceptions: Property<"ignored-exceptions", "String">, - DefaultStringValue<"">, + DefaultStringValue<"EXC_BAD_ACCESS">, Desc<"List the mach exceptions to ignore, separated by '|' " "(e.g. 'EXC_BAD_ACCESS|EXC_BAD_INSTRUCTION'). " "lldb will instead stop on the BSD signal the exception was converted " diff --git a/lldb/source/Plugins/Platform/MacOSX/cangjie_cjdb.py b/lldb/source/Plugins/Platform/MacOSX/cangjie_cjdb.py new file mode 100644 index 000000000..1c76152d6 --- /dev/null +++ b/lldb/source/Plugins/Platform/MacOSX/cangjie_cjdb.py @@ -0,0 +1,1782 @@ +# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +# This source file is part of the Cangjie project, licensed under Apache-2.0 +# with Runtime Library Exception. +# +# See https://cangjie-lang.cn/pages/LICENSE for license information. +# + +import lldb +import re +import inspect +import struct +import datetime +import functools +from enum import Enum +from typing import Callable, Union, List, Tuple, Pattern, Any + +UINT64_MAX = 0xFFFFFFFFFFFFFFFF + +def WhenException(action: str = "returnDefaultValue", defaultRetValue = ""): + valid_modes = { + "returnDefaultValue", + "returnNone", + "returnExceptionName", + "raiseAgain" + } + if action not in valid_modes: + action = "returnNone" + + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except Exception as e: + if action == "returnDefaultValue": + return defaultRetValue + elif action == "returnNone": + return None + elif action == "returnExceptionName": + return type(e).__name__ + elif action == "raiseAgain": + raise + else: + return None + return wrapper + return decorator + +class SimpleNamespace: + def __init__(self, **kwargs): + self.kwargs = kwargs + def __getattr__(self, key): + return self.kwargs.get(key, None) + def __repr__(self): + return self.kwargs.__str__() + +def get_func_addr(name): + target = lldb.debugger.GetSelectedTarget() + + for mod in target.module_iter(): + syms = mod.FindSymbols(name, lldb.eSymbolTypeCode) + for i in range(syms.GetSize()): + sym = syms.GetContextAtIndex(i).GetSymbol() + if sym and sym.GetName() == name and sym.GetType() == lldb.eSymbolTypeCode: + return sym.GetStartAddress().GetLoadAddress(target) + + return None + +def get_symbol_load_address(symbol_name): + target = lldb.debugger.GetSelectedTarget() + if not target: + print("Error: No target selected.") + return None + + # Ensure a process is running and not exited + process = target.GetProcess() + if not process or process.GetState() == lldb.eStateInvalid or \ + process.GetState() == lldb.eStateExited or process.GetState() == lldb.eStateDetached: + print("Error: No live process to get load address. Please run or attach to your target.") + return None + + symbol_context_list = target.FindSymbols(symbol_name) + if symbol_context_list.GetSize() > 0: + first_symbol_context = symbol_context_list.GetContextAtIndex(0) + first_symbol = first_symbol_context.GetSymbol() + + if first_symbol and first_symbol.IsValid(): + start_address = first_symbol.GetStartAddress() + if start_address.IsValid(): + # Attempt to get the load address + load_address_value = start_address.GetLoadAddress(target) # Pass 'target' explicitly + return load_address_value + return None + +def get_evaluated_value(expression: str): + """ + Evaluates an expression in the current frame's context and returns the SBValue object. + Returns None if the evaluation fails or preconditions are not met. + + Args: + expression: The string expression to evaluate. + + Returns: + An lldb.SBValue object if successful, otherwise None. + """ + debugger = lldb.debugger + target = debugger.GetSelectedTarget() + if not target: + print("Error: No target selected.") # Removed for simplification, you might add logging + return None + + process = target.GetProcess() + if not process or process.GetState() != lldb.eStateStopped: + print("Error: Process is not stopped. EvaluateExpression requires a stopped process.") + return None + + thread = process.GetSelectedThread() + if not thread: + print("Error: No thread selected.") + return None + + frame = thread.GetSelectedFrame() + if not frame: + print("Error: No frame selected.") + return None + + # Evaluate the expression + # lldb.eDynamicCanRunTarget is generally a good default for most cases. + expression_value = frame.EvaluateExpression(expression, lldb.eDynamicCanRunTarget) + + if expression_value.IsValid() and not expression_value.GetError().Fail(): + return expression_value + else: + print(f"Error evaluating expression '{expression}': {expression_value.GetError().GetCString()}") + return None + +def OutStringSummary(valobj, _): + data = valobj.GetChildMemberWithName("data") + start = valobj.GetChildMemberWithName("start") + length = valobj.GetChildMemberWithName("length") + + error = lldb.SBError() + offsetOfStringContent = 16 # cangjie stores the result string in data[16+start: 16+start+length] + addr = data.Dereference().GetLoadAddress() + start.GetValueAsUnsigned(0) + offsetOfStringContent + size = length.GetValueAsUnsigned(0) + resStr = valobj.GetProcess().ReadCStringFromMemory(addr, size + 1, error) + return resStr + +def infer_is_class_type(sbtype): + num_fields = sbtype.GetNumberOfFields() + offsets = [] + for i in range(num_fields): + member = sbtype.GetFieldAtIndex(i) # SBTypeMember + name = member.GetName() + offset = member.GetOffsetInBytes() + offsets.append(offset) + # for class type, the first 8 bytes are typeinfo pointer + if len(offsets) > 0: + return min(offsets) > 0 + else: + # basic value type, eg. Int/UInt + return False + +def ExtractLastPart(path: str) -> str: + path = path.rstrip("/") + if "/" not in path: + return path + return path.rsplit("/", 1)[-1] + +def RemoveOuterQuotes(s: str) -> str: + if len(s) < 2 or s[0] != '"' or s[-1] != '"': + return s + start, end = 0, len(s) + while start < end and s[start] == '"': + start += 1 + while end > start and s[end - 1] == '"': + end -= 1 + return s[start:end] + +def FunctionSummaryProvider(valobj, _): + raw = valobj.GetNonSyntheticValue() + ptr = raw.GetChildMemberWithName("ptr") + addr = ptr.GetValueAsUnsigned() + target = valobj.GetTarget() + sb_address = target.ResolveLoadAddress(addr) + sb_context = target.ResolveSymbolContextForAddress(sb_address, lldb.eSymbolContextEverything) + mangle_name = "" + if (sb_context.GetFunction().IsValid()): + mangle_name = sb_context.GetFunction().GetName() + if (sb_context.GetSymbol().IsValid()): + mangle_name = sb_context.GetSymbol().GetName() + if mangle_name.startswith("_"): + expr = f'(char *)CJ_MRT_DemangleHandle("{"_" + mangle_name}")' + else: + expr = f'(char *)CJ_MRT_DemangleHandle("{mangle_name}")' + frame = valobj.thread.GetFrameAtIndex(0) + func = frame.EvaluateExpression(expr) + sbs_stream_le = lldb.SBStream() + sbs_stream_m = lldb.SBStream() + sb_context.GetLineEntry().GetDescription(sbs_stream_le) + sb_context.GetModule().GetDescription(sbs_stream_m) + line_entry = ExtractLastPart(sbs_stream_le.GetData()) + module_path = ExtractLastPart(sbs_stream_m.GetData()) + result = func.GetValue() + " (" + module_path + "`" + RemoveOuterQuotes(func.GetSummary()) + " at " + line_entry + ")" + return result + +def StringSummaryProvider(valobj, _): + raw = valobj.GetNonSyntheticValue() + data = raw.GetChildMemberWithName("myData") + start = raw.GetChildMemberWithName("start") + length = raw.GetChildMemberWithName("length") + elements = data.GetChildMemberWithName("elements") + addr = elements.GetLoadAddress() + start.GetValueAsUnsigned(0) + maxlen = length.GetValueAsUnsigned(0) + if not addr or not maxlen: + return "" + error = lldb.SBError() + str = valobj.GetProcess().ReadCStringFromMemory(addr, maxlen + 1, error) + return "\"" + str + "\"" + +def RuneSummaryProvider(valobj, _): + error = lldb.SBError() + data_bytes = valobj.GetProcess().ReadMemory(valobj.GetLoadAddress(), 4, error) + if not error.Success(): + return f"" + rune_value = int.from_bytes(data_bytes, byteorder='little') + control_chars = { + 0: '\\0', + 7: '\\a', + 8: '\\b', + 9: '\\t', + 10: '\\n', + 11: '\\v', + 12: '\\f', + 13: '\\r', + 27: '\\e', + 34: '\\"', + ord('\\'): '\\\\', + ord('\''): '\\\'', + } + if rune_value in control_chars: + return f"'{control_chars[rune_value]}'" + else: + try: + summary = data_bytes.decode('utf-32-le') + except UnicodeDecodeError: + return "" + return "\'" + summary + "\'" + + +def Int8SummaryProvider(valobj,_): + if valobj.GetTypeName() == "Int8": + return valobj.GetValueAsSigned() + return valobj.GetValueAsUnsigned() + +def format_hex(hex_str): + clean_hex = hex_str.lower().replace("0x", "") + try: + num = int(clean_hex, 16) + except ValueError: + raise ValueError("bad hex input") + num_64bit = num & 0xFFFFFFFFFFFFFFFF + return f"0x{num_64bit:016x}" + +def CPointerSummaryProvider(valobj, _): + obj = valobj.GetNonSyntheticValue() + ptr = obj.GetChildMemberWithName("ptr") + addr = ptr.GetValueAsUnsigned() + return format_hex(hex(addr)) + +def CStringSummaryProvider(valobj, _): + obj = valobj.GetNonSyntheticValue() + chars = obj.GetChildMemberWithName("chars") + cstr = chars.GetSummary() + return cstr + +def UnitSummaryProvider(valobj, _): + return "()" + +def RangeSummaryProvider(valobj, _): + raw = valobj.GetNonSyntheticValue() + start = raw.GetChildMemberWithName("start") + end = raw.GetChildMemberWithName("end") + step = raw.GetChildMemberWithName("step") + closed = raw.GetChildMemberWithName("isClosed") + hasStart = raw.GetChildMemberWithName("hasStart") + hasEnd = raw.GetChildMemberWithName("hasEnd") + result = "" + if hasStart.IsValid() and hasStart.GetValue() == "true": + result += str(start).split('= ', 1)[1] + result += ".." + if closed.IsValid() and closed.GetValue() == "true": + result += "=" + if hasEnd.IsValid() and hasEnd.GetValue() == "true": + result += str(end).split('= ', 1)[1] + result += " : " + if step.IsValid(): + result += str(step).split('= ', 1)[1] + return result + + +def OptionSummaryProveder(valobj, _): + raw = valobj.GetNonSyntheticValue() + constructor = raw.GetChildMemberWithName("constructor") + if not constructor.IsValid(): + return "" + if constructor.GetValue() == "None": + return "nullptr" + val = raw.GetChildMemberWithName("val") + if val.IsValid(): + if val.GetType().IsPointerType(): + val = val.Dereference() + if not val.IsValid(): + return "nullptr" + addr = val.GetLoadAddress() + if addr == 0 or addr == UINT64_MAX: + return "nullptr" + if val.GetSummary(): + return val.GetSummary() + if val.GetValue(): + return val.GetValue() + + recursive = raw.GetChildMemberWithName("Recursive-val") + if recursive.IsValid(): + if recursive.GetType().IsPointerType(): + return recursive.GetValue() + return hex(recursive.GetLoadAddress()) + return "" + +def OptionPtrSummaryProveder(valobj, _): + raw = valobj.GetNonSyntheticValue() + if (raw.GetType().IsPointerType()): + val = raw.Dereference() + if not val.IsValid(): + return "nullptr" + addr = val.GetLoadAddress() + if addr == 0 or addr == UINT64_MAX: + return "nullptr" + return str(val).split('= ', 1)[1] + return "" + +def IsValobjDynamicType(valobj): + valtype = valobj.GetType() + if valtype.IsTypedefType(): + valtype = valtype.GetTypedefedType() + if valtype.GetTypeClass() != lldb.eTypeClassClass: + return False + if valtype.GetDisplayTypeName().find("Interface$") != -1: + return True + type_name = GetTypeNameFromMemory(valobj) + if type_name == "": + return False + if type_name != valobj.GetTypeName(): + return True + return False + +def SilentDelete(debugger, result): + temp_result = lldb.SBCommandReturnObject() + interpreter = debugger.GetCommandInterpreter() + + cmd1 = 'type summary delete --category cplusplus "char32_t ?\[[0-9]+\]"' + interpreter.HandleCommand(cmd1, temp_result) + error_output = temp_result.GetError() + + cmd2 = 'type summary delete --category system "^((un)?signed )?char ?\[[0-9]+\]$"' + interpreter.HandleCommand(cmd2, temp_result) + error_output += temp_result.GetError() + + patterns = [ + r"error: no custom formatter for char32_t ?\[[0-9]+\]\.?\n", + r"error: no custom formatter for ((un)?signed )?char ?\[[0-9]+\]\.?\n" + ] + if all(re.search(p, error_output) for p in patterns): + result.SetError("") + else: + result.SetError(error_output) + +class CangjieVArraySyntheticProvider: + def __init__(self, valobj, _): + target = valobj.GetTarget() + debugger = target.GetDebugger() + SilentDelete(debugger, lldb.SBCommandReturnObject()) + self.valobj = valobj + + def num_children(self): + return self.valobj.GetNumChildren() + + def get_child_at_index(self, index): + return self.valobj.GetChildAtIndex(index) + + def get_child_index(self, name): + pass + + def has_children(self): + return True + +class CangjieArraySyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + self.update() + + def update(self): + self.rawptr = self.valobj.GetChildMemberWithName("rawptr") + if (self.rawptr.IsValid()): + self.elements = self.rawptr.GetChildMemberWithName("elements") + self.start = self.valobj.GetChildMemberWithName("start") + self.len = self.valobj.GetChildMemberWithName("len") + + def num_children(self): + if (self.len.IsValid()): + return self.len.GetValueAsUnsigned() + return 0 + + def get_child_at_index(self, index): + if (self.elements.IsValid() and self.start.IsValid()): + element_size = self.elements.GetType().GetByteSize() + if not self.elements.GetAddress().IsValid(): + return None + start_address = self.elements.GetLoadAddress() + addr = start_address + (self.start.GetValueAsUnsigned(0) + index) * element_size + value = self.valobj.CreateValueFromAddress(f"[{index}]", addr, self.elements.GetType()) + if value.GetType().IsPointerType(): + if value.GetValueAsUnsigned() == 0: + value = value.CreateValueFromAddress(f"[{index}]", addr, value.GetType().GetPointeeType()) + value.SetSyntheticChildrenGenerated(True) + else: + value = value.Dereference() + if value and value.IsValid() and value.GetName().startswith("*"): + name = value.GetName()[1:] + value = self.valobj.CreateValueFromAddress(name, value.GetLoadAddress(), value.GetType()) + if (value.IsValid()): + if IsValobjDynamicType(value): + anytype = self.valobj.GetTarget().FindFirstType("Interface$Any") + value = self.valobj.CreateValueFromAddress(value.GetName(), value.GetLoadAddress(), anytype) + return value + return None + + def get_child_index(self, name): + pass + + def has_children(self): + return True + + def get_type_name(self): + displayname = DeletePrefixOfTypename(self.valobj.GetDisplayTypeName(), "const ") + return displayname.replace("::", ".") + +class CangjieArrayListSyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + self.update() + + def update(self): + self.data = CangjieArraySyntheticProvider(self.valobj.GetChildMemberWithName("myData"), None) + self.size = self.valobj.GetChildMemberWithName("mySize").GetValueAsUnsigned(0) + + def get_child_at_index(self, index): + value = self.data.get_child_at_index(index) + return value + + def num_children(self): + return 0 if self.size > 0xFFFFFFFF else self.size + + def get_child_index(self, name): + pass + + def has_children(self): + return True + + def get_type_name(self): + displayname = DeletePrefixOfTypename(self.valobj.GetDisplayTypeName(), "const ") + return displayname.replace("::", ".") + +class CangjieTupleSyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + self.update() + + def update(self): + self.size = self.valobj.GetNumChildren() + + def get_child_at_index(self, index): + value = self.valobj.GetChildAtIndex(index) + addr = value.GetLoadAddress() + value = self.valobj.CreateValueFromAddress(f"[{index}]", addr, value.GetType()) + if value.GetType().IsPointerType(): + if value.GetValueAsUnsigned() == 0: + value = value.CreateValueFromAddress(f"[{index}]", addr, value.GetType().GetPointeeType()) + value.SetSyntheticChildrenGenerated(True) + else: + value = value.Dereference() + if hasattr(value, 'IsValid') and value.IsValid() and value.GetName().startswith("*"): + name = value.GetName()[1:] + return self.valobj.CreateValueFromAddress(name, value.GetLoadAddress(), value.GetType()) + return value + + def num_children(self): + return self.size + + def get_child_index(self, name): + pass + + def has_children(self): + return True + + def get_type_name(self): + return None + +class CangjieSummaryDisplayTypeSyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + pass + + def update(self): + pass + + def get_child_at_index(self, index): + pass + + def num_children(self): + return 0 + + def get_child_index(self, name): + pass + + def has_children(self): + return True + + def get_type_name(self): + return self.valobj.GetTypeName().replace("const ", "").replace("::", ".") + +class CanjieHashMapEntrySyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + + @WhenException() + def get_child_at_index(self, index): + value = self.valobj.GetChildAtIndex(index + 2) + if value.IsValid() and value.GetType().IsPointerType(): + value = value.Dereference() + return value + + @WhenException() + def num_children(self): + return 2 + + def get_child_index(self, name): + pass + + @WhenException() + def has_children(self): + return True + +class CangjieHashMapSyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + self.IsHashSet = False + self.numEntries = 0 + self.entryIndexes = [] + self.update() + + @WhenException() + def update(self): + appendIndex = self.valobj.GetChildMemberWithName("appendIndex") + freeSize = self.valobj.GetChildMemberWithName("freeSize") + self.numEntries = appendIndex.GetValueAsSigned() - freeSize.GetValueAsSigned() + if self.numEntries < 0: self.numEntries = 0 + + entries = self.valobj.GetChildMemberWithName("entries") + if entries.IsValid(): + self.data = CangjieArraySyntheticProvider(entries, None) + else: + self.data = None + + @WhenException() + def get_child_at_index(self, index): + if not self.data: + return None + + if index >= self.numEntries: + return None + + startIndex = 0 if len(self.entryIndexes) == 0 else self.entryIndexes[-1] + 1 + entriesCapacity = self.data.num_children() + while startIndex <= entriesCapacity: + siEntry = self.data.get_child_at_index(startIndex) + siEntry = siEntry.GetNonSyntheticValue() + hashCode = siEntry.GetChildMemberWithName("hash").GetValueAsSigned() + if not (hashCode < 0): + self.entryIndexes.append(startIndex) + if len(self.entryIndexes) > index: break + startIndex += 1 + + if index >= len(self.entryIndexes): + return None + + entryIndex = self.entryIndexes[index] + value = self.data.get_child_at_index(entryIndex) + if not value.IsValid(): + return None + if self.IsHashSet: + value = value.GetChildAtIndex(0) + return value.CreateValueFromAddress(f"[{index}]", value.GetLoadAddress(), value.GetType()) + key = value.GetNonSyntheticValue().GetChildMemberWithName("key") + val = value.GetNonSyntheticValue().GetChildMemberWithName("value") + if val.GetType().IsPointerType(): val = val.Dereference() + + keysummary = self.get_oneword_summary(key) + valsummary = self.get_oneword_summary(val) + if not keysummary: + name = f"[{index}]" + elif not valsummary: + name = f"[{keysummary}]" + else: + name = f"[{keysummary} -> {valsummary}]" + value = value.CreateValueFromAddress(name, value.GetLoadAddress(), value.GetType()) + return value + + @WhenException() + def num_children(self): + return self.numEntries + + @WhenException() + def get_child_index(self, name): + pass + + def has_children(self): + return True + + def get_oneword_summary(self, obj): + if obj.TypeIsPointerType(): + obj = obj.Dereference() + if obj_summary := obj.GetSummary(): + return obj_summary + if obj_value := obj.GetValue(): + return obj_value + return None + + def SetIsHashSet(self, IsHashSet = True): + self.IsHashSet = IsHashSet + + def IsHashSet(self): + return self.IsHashSet + +class CangjieHashSetSyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + self.update() + + @WhenException() + def update(self): + map = self.valobj.GetChildMemberWithName("map") + self.data = CangjieHashMapSyntheticProvider(map, None) + self.data.SetIsHashSet(True) + + @WhenException() + def get_child_at_index(self, index): + return self.data.get_child_at_index(index) + + @WhenException() + def num_children(self): + return self.data.num_children() + + def get_child_index(self, name): + pass + + def has_children(self): + return True + + def get_type_name(self): + return None + +def DeletePrefixOfTypename(type_name, prefix): + if type_name is None: + return "" + while type_name.find(prefix) != -1: + index = type_name.find(prefix) + type_name = type_name[:index] + type_name[index + len(prefix):] + return type_name + +class CangjieSyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + self.update() + + def update(self): + valobj = self.valobj + if not valobj.IsValid() or not IsValobjDynamicType(valobj): + return + # the valobj is dynamic type + typename = GetTypeNameFromMemory(valobj) + dynamictype = valobj.GetTarget().FindFirstType(typename) + if not dynamictype.IsValid(): + return + # the dynamic type is not class + if dynamictype.GetTypeClass() != lldb.eTypeClassClass: + self.valobj = valobj.CreateValueFromAddress( + valobj.GetName(), valobj.GetLoadAddress() + 8, dynamictype) + return + + valtype = valobj.GetType() + if valtype.IsTypedefType(): + valtype = valtype.GetTypedefedType() + valTypeName = valtype.GetDisplayTypeName().replace("::", ".") + + # If the name of valobj is typename, then the valobj is an inherited class, no need to use dynamictype. + if valobj.GetName() != valTypeName: + self.valobj = valobj.CreateValueFromAddress( + valobj.GetName(), valobj.GetLoadAddress(), dynamictype) + + def num_children(self): + return self.valobj.GetNumChildren() + + def get_child_index(self, name): + pass + + def get_child_at_index(self, index): + value = self.valobj.GetChildAtIndex(index) + if value.GetType().IsPointerType() and value.GetValueAsUnsigned() != 0: + value = value.Dereference() + if hasattr(value, 'IsValid') and value.IsValid(): + if value.GetName().startswith("*"): + name = value.GetName()[1:] + return self.valobj.CreateValueFromAddress(name, value.GetLoadAddress(), value.GetType()) + else: + return value + name = value.GetName() + if name and name.find("::") != -1: + name = name.replace("::", ".") + value = self.valobj.CreateValueFromAddress(name, value.GetLoadAddress(), value.GetType()) + return value + + def has_children(self): + return True + + def get_type_name(self): + # change typename from "default::E0$E0" to "default.E0" + displayname = DeletePrefixOfTypename(self.valobj.GetDisplayTypeName(), "E0$") + displayname = DeletePrefixOfTypename(displayname, "const ") + return displayname.replace("::", ".") + +# enum E0 { +# A | B | C +# } +def CangjieEnum0SummaryProvider(valobj, _): + if valobj.GetType().IsPointerType(): + return "" + raw = valobj.GetNonSyntheticValue() + ctor = raw.GetChildMemberWithName("constructor") + enum_members = ctor.GetType().GetEnumMembers() + enum_size = enum_members.GetSize() + if enum_size == 0: + return None + ctor_id = ctor.GetValueAsUnsigned() + if ctor_id >= enum_size: + ctor_id = 0 + val = enum_members[ctor_id] + return val.GetName() + +# enum E1 { +# A(Int64) | B(Int64, Class) | C +# } +class CangjieEnum1SyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + self.update() + + def update(self): + for i in range(self.valobj.GetNumChildren()): + ctor = self.valobj.GetChildAtIndex(i) + if (ctor.IsValid()): + ctor_type = ctor.GetChildMemberWithName("constructor") + ctor_id = ctor_type.GetValueAsUnsigned() + self.data = self.valobj.GetChildAtIndex(ctor_id) + break + if hasattr(self, 'data') and self.data.IsValid(): + ctor = self.data.GetChildMemberWithName("constructor") + if ctor != None: + value = ctor.GetValue() + if hasattr(value, 'IsValid') and value.IsValid(): + self.data = self.valobj.CreateValueFromAddress("arg_1", value.GetLoadAddress(), value.GetType()) + + def num_children(self): + if hasattr(self, 'data') and self.data.IsValid(): + return self.data.GetNumChildren() + else: + return 0 + + def get_child_at_index(self, index): + if hasattr(self, 'data') and self.data.IsValid(): + value = self.data.GetChildAtIndex(index) + if value.GetType().IsPointerType() and value.GetValueAsUnsigned() != 0: + value = value.Dereference() + if hasattr(value, 'IsValid') and value.IsValid(): + if value.GetName().startswith("*"): + name = value.GetName()[1:] + return self.valobj.CreateValueFromAddress(name, value.GetLoadAddress(), value.GetType()) + else: + return value + return None + + def get_child_index(self, name): + pass + + def has_children(self): + return True + + def get_type_name(self): + type_name = self.valobj.GetTypeName() + displayname = DeletePrefixOfTypename(type_name, "E1$") + displayname = displayname.replace("::", ".") + return displayname + +# enum E2 { +# A | B(T) +# } +# var a = E2.A +def CangjieEnum2SummaryProvider(valobj, _): + if valobj.GetType().IsPointerType(): + deref_val_type = valobj.GetType().GetPointeeType() + if deref_val_type.IsPointerType(): + return "" + raw = valobj.GetNonSyntheticValue() + if not raw.GetFrame().FindVariable(raw.GetName()): + return "" + # Non-reference type enum + enum_ctor = raw.GetChildMemberWithName("constructor") + if enum_ctor.IsValid(): + return "" + # Reference type enum + enum_ctor = raw.GetChildMemberWithName("constructor_R") + enum_ctor_type = enum_ctor.GetType() + enum_members = enum_ctor_type.GetEnumMembers() + if enum_members.GetSize() != 2: + return "" + ptr = raw.GetValueAsUnsigned() + if ptr == 0 or ptr == 1: + for member in enum_members: + enum_name = member.GetName() + if hasattr(enum_name, 'startswith') and enum_name.startswith("N$_"): + enum_name = enum_name[3:] + return enum_name + else: + deref = raw.Dereference() + return str(deref).split('= ', 1)[1] + + raw = valobj.GetNonSyntheticValue() + ctor = raw.GetChildMemberWithName("constructor") + if not ctor.IsValid(): + if raw.IsSyntheticChildrenGenerated(): + enum_ctor = valobj.GetType().GetFieldAtIndex(0) + enum_ctor_type = enum_ctor.GetType() + enum_members = enum_ctor_type.GetEnumMembers() + for member in enum_members: + enum_name = member.GetName() + if hasattr(enum_name, 'startswith') and enum_name.startswith("N$_"): + enum_name = enum_name[3:] + return enum_name + return "" + value = ctor.GetValue() + if hasattr(value, 'startswith') and value.startswith("N$_"): + value = value[3:] + return value + else: + return "" + +# case1: +# enum E2 { +# B(T) | A +# } +# var a = E2.B(5) +# case2: +# class A {} +# enum E2 { +# B | C(T) +# } +# var b = E2.C(A()) +class CangjieEnum2SyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj + self.update() + + def update(self): + if self.valobj.GetType().IsPointerType(): + deref_val_type = self.valobj.GetType().GetPointeeType() + if deref_val_type.IsPointerType(): + return + for i in range(deref_val_type.GetNumberOfFields()): + field = deref_val_type.GetFieldAtIndex(i) + field_name = field.GetName() + if field_name and "constructor" in field_name: + self.ctor_type = field.GetType() + + if not (hasattr(self, 'ctor_type') and self.ctor_type.IsValid()): + return + # top-level object and nullptr + obj = self.valobj.GetFrame().FindVariable(self.valobj.GetName()) + if obj and self.valobj.GetValueAsUnsigned() == 0: + ret = self.valobj.SetValueFromCString("1") + return + + # Non-reference type: "constructor", Reference type: "constructor_R". + enum_type = self.valobj.GetType() + enum_ctor = enum_type.GetFieldAtIndex(0) + if not enum_ctor.IsValid(): + return + enum_ctor_type = enum_ctor.GetType() + enum_id = 0 + for member in enum_ctor_type.GetEnumMembers(): + enum_name = member.GetName() + if hasattr(enum_name, 'startswith') and not enum_name.startswith("N$_"): + break + enum_id = enum_id + 1 + + enum_data = lldb.SBData() + err = lldb.SBError() + target = self.valobj.GetTarget() + # Use `struct.pack` to pack integer values into a byte stream, '" + + typeInfoAddr = get_symbol_load_address(typeInfoName) + if typeInfoAddr is None: + return f"" + + invokeToString = f""" +struct string_t {{ char *data; int start, length; }} result = {{ 0 }}; +((void (*)(void *, void *, void *))0x{toStringAddr:x})( +&result, +(void *)0x{valueAddress:x}, +(void *)0x{typeInfoAddr:x}); +result +""".replace('\n', ' ').strip() + outValue = get_evaluated_value(invokeToString) + if not outValue or not outValue.IsValid(): + return None + return OutStringSummary(outValue, idict) + +@WhenException() +def TimeZoneSummaryProvider(valobj, internal_dict): + localTimesObj = valobj.GetChildMemberWithName("localTimes") + if not localTimesObj.IsValid(): + return None + + zoneIdObj = valobj.GetChildMemberWithName("zoneId") + if not zoneIdObj.IsValid(): + return None + zoneId = StringSummaryProvider(zoneIdObj, internal_dict) + + return f"TimeZone({zoneId})" + +@WhenException() +class CangjieDateTimeFormatter: + class Month(Enum): + January = 1 + February = 2 + March = 3 + April = 4 + May = 5 + June = 6 + July = 7 + August = 8 + September = 9 + October = 10 + November = 11 + December = 12 + + @classmethod + def of(cls, idx: int): + return cls(idx) + + def __init__(self, epoch, offset, nanosecs = 0): + self.epoch = epoch + self.offset = offset + self.nanosecs = nanosecs + + START_AD_YEAR = 1 + MIN_YEAR = -999999999 + MAX_YEAR = 999999999 + SECS_PER_DAY = 86400 + DAYS_PER_400YEARS = 146097 + DAYS_PER_100YEARS = 36524 + DAYS_PER_4YEARS = 1461 + DAYS_OF_NORMAL_YEAR = 365 + SECS_PER_HOUR = 3600 + SECS_PER_MINUTE = 60 + DAYS_OF_MAX_TO_AD1 = (MAX_YEAR - 1) * 365 + (MAX_YEAR - 1) // 4 - (MAX_YEAR - 1) // 100 + (MAX_YEAR - 1) // 400 + 365 + DAYS_OF_MIN_TO_AD1 = -(DAYS_OF_MAX_TO_AD1 + 366) + SECS_OF_MIN_TO_AD1 = DAYS_OF_MIN_TO_AD1 * SECS_PER_DAY + DAYS_BEFORE = [ + 0, # January + 31, # February + 31 + 28, # March + 31 + 28 + 31, + 31 + 28 + 31 + 30, + 31 + 28 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, + 365 + ] + + @property + def year(self) -> int: + return self.getYearAndSecond()[0] + + @property + def month(self): + return self.getMonthAndDay()[0] + + @property + def dayOfMonth(self) -> int: + return self.getMonthAndDay()[1] + + @property + def hour(self) -> int: + second = self.getYearAndSecond()[1] + return (second % self.SECS_PER_DAY) // self.SECS_PER_HOUR + + @property + def minute(self) -> int: + second = self.getYearAndSecond()[1] + return (second % self.SECS_PER_HOUR) // self.SECS_PER_MINUTE + + @property + def second(self) -> int: + second = self.getYearAndSecond()[1] + return second % self.SECS_PER_MINUTE + + @property + def nanosecond(self) -> int: + return self.nanosecs + + def getMonthAndDay(self) -> Tuple[Month, int]: + year_after, second_after = self.getYearAndSecond() + return self.getDate(year_after, second_after) + + def getDate(self, year: int, sec: int) -> Tuple[Month, int]: + days = sec // self.SECS_PER_DAY + if self.isLeapYear(year): + if days == self.DAYS_BEFORE[2]: + return (Month.February, 29) + if days > self.DAYS_BEFORE[2]: + days -= 1 + + idx = 0 + day = 0 + while idx < len(self.DAYS_BEFORE): + if days == self.DAYS_BEFORE[idx]: + idx += 1 + day = 1 + break + + if days < self.DAYS_BEFORE[idx]: + day = days - self.DAYS_BEFORE[idx-1] + 1 + break + + idx += 1 + + month = CangjieDateTimeFormatter.Month.of(idx) + return (month, day) + + def isLeapYear(self, year: int) -> bool: + return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) + + def getYearAndSecond(self): + offset = self.getOffset() + year, sec = self.toYearAndSecond(self.epoch + offset) + return (year, int(sec)) + + def toYearAndSecond(self, sec) -> Tuple[int, int]: + year = self.START_AD_YEAR + seconds = sec + + if seconds < 0: + seconds -= self.SECS_OF_MIN_TO_AD1 + year = self.MIN_YEAR + + days = seconds // self.SECS_PER_DAY + rest_sec = seconds % self.SECS_PER_DAY + + times = days // self.DAYS_PER_400YEARS + year += 400 * times + days %= self.DAYS_PER_400YEARS + + times = days // self.DAYS_PER_100YEARS + times -= times >> 2 + year += 100 * times + days -= self.DAYS_PER_100YEARS * times + + times = days // self.DAYS_PER_4YEARS + year += 4 * times + days %= self.DAYS_PER_4YEARS + + times = days // self.DAYS_OF_NORMAL_YEAR + times -= times >> 2 + year += times + days -= self.DAYS_OF_NORMAL_YEAR * times + + sec_in_year = rest_sec + days * self.SECS_PER_DAY + return (year, sec_in_year) + + def getOffset(self): + return self.offset + + def addZeroPrefix(self, value: int, length: int): + sign = '-' if value < 0 else '' + num_str = str(abs(value)) + num_str = num_str.rjust(length, '0') + return sign + num_str + + def toString(self): + res = self.addZeroPrefix(self.year, 4) + res += '-' + res += self.addZeroPrefix(self.month.value, 2) + res += '-' + res += self.addZeroPrefix(self.dayOfMonth, 2) + res += 'T' + res += self.addZeroPrefix(self.hour, 2) + res += ':' + res += self.addZeroPrefix(self.minute, 2) + res += ':' + res += self.addZeroPrefix(self.second, 2) + if (self.nanosecond != 0): + nano = self.nanosecond + times = 0 + while nano % 10 == 0: + times += 1 + nano //= 10 + res += '.' + res += self.addZeroPrefix(nano, 9 - times) + res += self.toOffsetString(4) + return res + + def toOffsetString(self, length): + offset = self.getOffset() + if offset < 0: + sign = "-" + off = -offset + else: + sign = "+" + off = offset + + hour = off // 3600 + minute = (off % 3600) // 60 + second = off % 60 + + if length > 3 and offset == 0: + return "Z" + elif (length == 3) or (length > 3 and second != 0): + return f"{sign}{hour:02d}:{minute:02d}:{second:02d}" + elif (length == 2) or (length > 3): + return f"{sign}{hour:02d}:{minute:02d}" + else: + return f"{sign}{hour:02d}" + +@WhenException() +def DateTimeSummaryProvider(valobj, internal_dict): + # Refer the cangjie's library implementation and RFC8536 + # Retrieved layout of std.time.DateTime from lldb + # (std.time::DateTime) a = { + # # from cangjie's source code: + # # The d records the duration since A.D.1. + # d = (sec = 63422352000, ns = 0) + # tz = 0x00007fffd75b9fb8 + # } + # + # (std.time::TimeZone) b = { + # # note, due to the unknown python version, zoneId cannot be + # # utilized below python3.8 as zoneinfo is not included in the std libs + # zoneId = "" # a Cangjie String object, eg "UTC" + # localTimes = Array # transition offset + # transTimes = Array # transition time + # } + dObj = valobj.GetNonSyntheticValue().GetChildMemberWithName("d") + if not dObj.IsValid(): + return None + + secObj = dObj.GetChildMemberWithName("sec") + if not secObj.IsValid(): + return None + + nsecObj = dObj.GetChildMemberWithName("ns") + if not nsecObj.IsValid(): + return None + nanosecs = nsecObj.GetValueAsUnsigned() + + tzObj = valobj.GetNonSyntheticValue().GetChildMemberWithName("tz") + if not tzObj.IsValid(): + ad1Epoch = secObj.GetValueAsUnsigned() + ad1Date = datetime.datetime(1, 1, 1) + datetime.timedelta(seconds = ad1Epoch) + return ad1Date.strftime("%Y-%m-%dT%H:%M:%SZ") + + zoneIdObj = tzObj.GetChildMemberWithName("zoneId") + if not zoneIdObj.IsValid(): + return None + zoneId = StringSummaryProvider(zoneIdObj, internal_dict) + + localTimesObj = tzObj.GetChildMemberWithName("localTimes") + + localTimesSynthetic = CangjieArraySyntheticProvider(localTimesObj, internal_dict) + localTimes = [] + for i in range(localTimesSynthetic.num_children()): + ithObj = localTimesSynthetic.get_child_at_index(i) + ithObj = ithObj.GetNonSyntheticValue() + localTimes.append(SimpleNamespace( + des = StringSummaryProvider(ithObj.GetChildMemberWithName("des"), internal_dict), + offset = ithObj.GetChildMemberWithName("offset").GetValueAsSigned(), + isDST = bool(ithObj.GetChildMemberWithName("isDST").GetValueAsUnsigned()), + )) + + transTimes_obj = tzObj.GetChildMemberWithName("transTimes") + transTimes_synthetic = CangjieArraySyntheticProvider(transTimes_obj, internal_dict) + transTimes = [] + for i in range(transTimes_synthetic.num_children()): + ithObj = transTimes_synthetic.get_child_at_index(i) + ithObj = ithObj.GetNonSyntheticValue() + transTimes.append(SimpleNamespace( + trans = ithObj.GetChildMemberWithName("trans").GetValueAsSigned(), + index = ithObj.GetChildMemberWithName("index").GetValueAsUnsigned(), + )) + + ad1Epoch = secObj.GetValueAsSigned() + utcEpoch = ad1Epoch - (datetime.datetime(1970, 1, 1) - datetime.datetime(1, 1, 1)).total_seconds() + + # no transition in the history or the epoch is early than the first transition + if len(transTimes) == 0 or utcEpoch < transTimes[0].trans: + # needs to infer, rules from the cj's library + + def InferRuleEarlierThanFirstTransition(): + # rule 1, if 0-th localtime is unused, it's the rule used for stage earlier than first transition + unused = not any([t.index == 0 for t in transTimes]) + if unused: + return 0 + + # rule 2, + # + # Absent the above, if there are transition times and the first transition is to a daylight time, + # find the standard type less than and closest to the zone of the first transition. + if len(transTimes) > 0 and localTimes[transTimes[0].index].isDST: + index = transTimes[0].index - 1 + for zoneIndex in range(index, -1, -1): + if not localTimes[zoneIndex].isDST: + return zoneIndex + + # If no result yet, find the first standard type. + for zoneIndex in range(0, localTimes.size): + if not localTimes[zoneIndex].isDST: + return zoneIndex + + return 0 + + if len(localTimes) > 0: + ruleIndex = InferRuleEarlierThanFirstTransition() + zoneOffset = localTimes[ruleIndex].offset + else: + zoneOffset = 0 + else: + # binary search in the transTimes, find first stage later than utcEpoch + low = 0 + high = len(transTimes) + while (high - low > 1): + mid = int((low + high) / 2) + curTime = transTimes[mid].trans + if (utcEpoch < curTime): + high = mid + else: + low = mid + ruleIndex = transTimes[low].index + zoneOffset = localTimes[ruleIndex].offset + + # note: datetime does not support datetime > 99999, + # we have to manually convert here + return CangjieDateTimeFormatter(ad1Epoch, zoneOffset, nanosecs).toString() + +class CangjieOptionSyntheticProvider: + def __init__(self, valobj, _): + self.valobj = valobj.GetNonSyntheticValue() + self.hasvalue = False + self.children = False + self.some: lldb.SBValue = None + self.update() + + def update(self): + if self.valobj.GetType().IsPointerType(): + self.valobj = self.valobj.Dereference().GetNonSyntheticValue() + value = self.valobj.GetChildMemberWithName("val") + if not value.IsValid(): + value = self.valobj.GetChildMemberWithName("Recursive-val") + if not value.IsValid(): + return False + value.SetPreferSyntheticValue(True) + self.some = value + self.hasvalue = True + self.children = self.some.GetNumChildren() > 0 + + def get_child_at_index(self, index): + return self.some.GetChildAtIndex(index) + + def num_children(self): + if self.some.IsValid() and self.some.GetError().Success(): + return self.some.GetNumChildren() + return 0 + + def get_child_index(self, name): + pass + + def has_children(self): + if self.children == False or self.some == None or self.hasvalue == False: + return False + return True + + def get_type_name(self): + if self.valobj.IsValid(): + return self.valobj.GetTypeName().rstrip(" *") + return "" + +class CommandSet: + def __init__(self, debugger, unused): + self.dbg = debugger + + def __call__(self, debugger, command, exe_ctx, result): + cmd = command.strip() + if '=' not in cmd: + result.SetError("Invalid command options. Use 'help set' to view the command format.") + return + parts = cmd.split('=', 1) + if len(parts) != 2: + result.SetError("Invalid command options. Use 'help set' to view the command format.") + return + expr = parts[0].strip() + new_value = parts[1].strip() + if not expr or not new_value: + result.SetError("Invalid command. Variable name and value cannot be empty") + return + target = debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetSelectedThread() + frame = thread.GetSelectedFrame() + parts = re.sub(r'\[(\d+)\]', r'.\1', expr) + variables = parts.split('.') + root = variables[0] + members = variables[1:] + var = frame.FindVariable(root) + if not var.IsValid(): + var = target.FindFirstGlobalVariable(root) + if not var.IsValid(): + result.SetError(f"Invalid command. Can not find variable '{root}'") + return + for member in members: + if member.isdigit(): + index = int(member) + var = var.GetChildAtIndex(index, 0, True) + else: + var = var.GetNonSyntheticValue() + var = var.GetChildMemberWithName(member) + if not var.IsValid(): + result.SetError(f"Invalid command. Can not find member '{member}'") + return + error = lldb.SBError() + var.SetValueFromCString(new_value, error) + if error.Fail(): + result.SetError(error.GetCString()) + return + result.AppendMessage(f"{expr} = {new_value}") + return + + def get_short_help(self): + return "Modify the value of a variable of a basic data type.\n" \ + "The command format is 'set variable = new_value'\n" + +class CommandCJBackTrace : + def __init__(self, debugger, unused): + self.dbg = debugger + + def __call__(self, debugger, command, exe_ctx, result): + try: + frame_limit = int(command.strip()) if command.strip() else None + except ValueError: + result.SetError("Invalid command options.") + return + + target = debugger.GetSelectedTarget() + process = target.GetProcess() + if not process or not process.IsValid(): + result.SetError("Command requires a current process.") + return + thread = process.GetSelectedThread() + result.AppendMessage( + f'* thread #{thread.GetThreadID()}, name = {thread.GetName()}, ' + f'stop reason = {thread.GetStopDescription(100)}') + if frame_limit == None: + frame_limit = thread.GetNumFrames() + status = "" + str = lldb.SBStream() + for num in range(0, frame_limit): + frame = thread.GetFrameAtIndex(num) + if num == thread.GetSelectedFrame().GetFrameID(): + status += " * " + else : + status += " " + str.Clear() + frame.GetDescription(str) + mangled = frame.GetFunctionName().split("(", 1)[0] + if mangled.startswith("_"): + expr = f'(char *)CJ_MRT_DemangleHandle("{"_" + mangled}")' + else: + expr = f'(char *)CJ_MRT_DemangleHandle("{mangled}")' + value = thread.GetFrameAtIndex(0).EvaluateExpression(expr) + if not value.GetSummary(): + status += str.GetData() + continue + demangled = value.GetSummary().strip("\"").split("(", 1)[0] + des = str.GetData().replace(mangled, demangled) + status += des + result.AppendMessage(status) + return + + def get_short_help(self): + return "Show the current thread's call stack. Any numeric argument displays at most\n" \ + "that many frames. Display all frames by default.\n" \ + "bt [ | all]" + +class CommandCJThreadBackTrace: + def __init__(self, debugger, unused): + pass + + def __call__(self, debugger, command, exe_ctx, result): + frame = exe_ctx.frame + process = exe_ctx.process + if not frame.IsValid(): + result.SetError("Command requires a current frame.") + return + options = lldb.SBExpressionOptions() + options.SetSuppressPersistentResult(False) + options.SetIgnoreBreakpoints(True) + count = "unsigned long long cjdb_cjthread_count = (unsigned long long)CJ_MRT_GetCJThreadNumberUnsafe();cjdb_cjthread_count" + result_obj = frame.EvaluateExpression(count, options) + cjthread_count = result_obj.GetValueAsSigned() - 1 + # Due to the expression limitation, the stack of up to 100 Cangjie threads can be displayed. + if cjthread_count > 100 : + cjthread_count = 100 + info = f'char* cjdb_expr_buf = (char *)malloc({cjthread_count * 2048});\ + int a=(int)CJ_MRT_GetAllCJThreadStackTrace(cjdb_expr_buf,{cjthread_count});(char *)cjdb_expr_buf' + buf = frame.EvaluateExpression(info, options) + error = lldb.SBError() + for i in range(cjthread_count): + cjthreads = process.ReadCStringFromMemory(buf.GetValueAsUnsigned() + i *2048, 2048, error).split("\n") + for cjthread in cjthreads: + result.PutCString(cjthread) + frame.EvaluateExpression("free(cjdb_expr_buf)", options) + return + + def get_short_help(self): + return "Show the all cangjie thread's call stack. excluding those in the running state. \n" + +class CangjieStdLib: + def __init__(self, target:lldb.SBTarget, extra_args, _): + pass + def handle_stop(self, exe_ctx, stream)-> bool: + thread = exe_ctx.thread + frame = exe_ctx.frame + function_name = frame.GetFunctionName() + if not function_name: + return True + if re.match("^_CN[a-c][a-z]", function_name): + plan = thread.StepOut() + exe_ctx.thread.StepUsingScriptedThreadPlan('{}.{}'.format(__name__, plan), False) + return True + return True + +def __lldb_init_module(debugger, internal_dict): + debugger.HandleCommand(f'command script add -c {__name__}.CommandSet set') + + debugger.HandleCommand(f'command alias locals frame variable') + debugger.HandleCommand(f'command alias globals target variable') + debugger.HandleCommand('command unalias bt') + debugger.HandleCommand(f'command script add -c {__name__}.CommandCJBackTrace bt') + debugger.HandleCommand('target stop-hook add -P {}.{}'.format(__name__, "CangjieStdLib")) + debugger.HandleCommand(f'command script add -c {__name__}.CommandCJThreadBackTrace cjthread') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieSyntheticProvider \ + -x "^.+$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieSummaryDisplayTypeSyntheticProvider \ + -x "^(const )?(std[.](core|time)::(String|Range|DateTime))|(CString|CPointer<.+>)$"\ + --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieVArraySyntheticProvider \ + -x "^(const )?VArray<.+>$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieArrayListSyntheticProvider \ + -x "^(const )?std[.]collection::ArrayList<.+>$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieArraySyntheticProvider \ + -x "^(const )?std[.]core::Array<.+>$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieTupleSyntheticProvider \ + -x "^(const )?Tuple<.+>$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieOptionSyntheticProvider \ + -x "^(const )?std[.]core::Option<.+>( \\*)?$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CanjieHashMapEntrySyntheticProvider \ + -x "^std[.]collection::HashMapEntry<.+>$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieHashMapSyntheticProvider \ + -x "^(const )?std[.]collection::HashMap<.+>$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieHashSetSyntheticProvider \ + -x "^(const )?std[.]collection::HashSet<.+>$" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieGTSyntheticProvider \ + -x "(const )?(\$G_[A-Z]+|Any$)" --category CustomView') + debugger.HandleCommand(f'type summary add -F {__name__}.CangjieGTSummaryProvider \ + -x "(const )?(\$G_[A-Z]+|Any$)" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.UnitSummaryProvider \ + -x "^(const )?Unit$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.RangeSummaryProvider \ + -x "^(const )?std[.]core::Range<.+>$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.FunctionSummaryProvider \ + -x "^(const )?(.+::|^)\\(.*\\)( ?)->.+$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.StringSummaryProvider \ + -x "^(const )?std[.]core::String$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.RuneSummaryProvider \ + -x "^(const )?Rune$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.Int8SummaryProvider \ + -x "^(const )?U?Int8$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.CPointerSummaryProvider \ + -x "^(const )?CPointer<.+>$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.CStringSummaryProvider \ + -x "^(const )?CString$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.OptionSummaryProveder \ + -x "^(const )?std[.]core::Option<.+>$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.OptionPtrSummaryProveder \ + -x "^(const )?std[.]core::Option<.+> \\*$" --category CustomView -v') + # use toStringProvider + debugger.HandleCommand(f'type summary add -F {__name__}.TimeZoneSummaryProvider \ + -x "^(const )?std[.]time::TimeZone$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.DateTimeSummaryProvider \ + -x "^(const )?std[.]time::DateTime$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.CangjieEnum0SummaryProvider \ + -x "^(.+)?E0\\$(.+)$" --category CustomView -v') + debugger.HandleCommand(f'type summary add -F {__name__}.CangjieEnum2SummaryProvider \ + -x "(.+)?E2\\$(.+)" --category CustomView -v') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieEnum1SyntheticProvider \ + -x "(.+)?E1\\$(.+)" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieEnum2SyntheticProvider \ + -x "(.+)?E2\\$(.+)" --category CustomView') + debugger.HandleCommand(f'type synthetic add -l {__name__}.CangjieEnum3SyntheticProvider \ + -x "(.+)?E3\\$(.+)" --category CustomView') + debugger.HandleCommand('type category enable CustomView') + debugger.HandleCommand('env cjProcessorNum=1') + debugger.HandleCommand("settings set target.max-children-depth 5") + debugger.HandleCommand("settings set target.max-children-count 100") + debugger.HandleCommand('settings set target.process.thread.step-avoid-regexp ^_CN[a-c][a-z]') diff --git a/lldb/source/Plugins/Platform/OHOS/CMakeLists.txt b/lldb/source/Plugins/Platform/OHOS/CMakeLists.txt new file mode 100644 index 000000000..38530e2f6 --- /dev/null +++ b/lldb/source/Plugins/Platform/OHOS/CMakeLists.txt @@ -0,0 +1,18 @@ +add_lldb_library(lldbPluginPlatformOHOS PLUGIN + PlatformOHOS.cpp + HdcClient.cpp + PlatformOHOSRemoteGDBServer.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbPluginPlatformLinux + lldbPluginPlatformGDB + boundscheck + LINK_COMPONENTS + Support + ) + +target_include_directories(lldbPluginPlatformOHOS PRIVATE ${CMAKE_SOURCE_DIR}/../utils/boundscheck/include) +string(REPLACE "AMD64" "x86_64" ARCH_NAME "${CMAKE_SYSTEM_PROCESSOR}") +target_link_directories(lldbPluginPlatformOHOS PUBLIC ${CMAKE_SOURCE_DIR}/../utils/boundscheck/lib/${ARCH_NAME}) diff --git a/lldb/source/Plugins/Platform/OHOS/HdcClient.cpp b/lldb/source/Plugins/Platform/OHOS/HdcClient.cpp new file mode 100644 index 000000000..997c52259 --- /dev/null +++ b/lldb/source/Plugins/Platform/OHOS/HdcClient.cpp @@ -0,0 +1,879 @@ +//===-- HdcClient.cpp -----------------------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#include "HdcClient.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/FileSystem.h" + +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/PosixApi.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/Timeout.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataEncoder.h" +#include "lldb/Utility/DataExtractor.h" + +#if defined(_WIN32) +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// On Windows, transitive dependencies pull in , which defines a +// macro that clashes with a method name. +#ifdef SendMessage +#undef SendMessage +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::platform_ohos; +using namespace std::chrono; + +namespace { + +const seconds kReadTimeout(20); +const char *kSocketNamespaceAbstract = "localabstract"; +const char *kSocketNamespaceFileSystem = "localfilesystem"; + +Status ReadAllBytes(Connection &conn, void *buffer, size_t size) { + Status error; + ConnectionStatus status; + char *read_buffer = static_cast(buffer); + + auto now = steady_clock::now(); + const auto deadline = now + kReadTimeout; + size_t total_read_bytes = 0; + while (total_read_bytes < size && now < deadline) { + auto read_bytes = conn.Read(read_buffer + total_read_bytes, + size - total_read_bytes, + duration_cast(deadline - now), + status, &error); + if (error.Fail()) { + return error; + } + total_read_bytes += read_bytes; + if (status != eConnectionStatusSuccess) { + break; + } + now = steady_clock::now(); + } + if (total_read_bytes < size) { + error = Status("Unable to read requested number of bytes. " + "Connection status: %d.", status); + } + return error; +} +} + +Status HdcClient::CreateByDeviceID(const std::string &device_id, + HdcClient &hdc) { + DeviceIDList connect_devices; + auto error = hdc.GetDevices(connect_devices); + if (error.Fail()) { + return error; + } + + std::string hdc_utid; + if (!device_id.empty()) { + hdc_utid = device_id; + } + + if (hdc_utid.empty()) { + if (connect_devices.size() != 1) { + return Status("Expected a single connected device, got instead %zu - try " + "setting 'HDC_UTID'", connect_devices.size()); + } + hdc.SetDeviceID(connect_devices.front()); + } else { + auto find_it = std::find(connect_devices.begin(), connect_devices.end(), + hdc_utid); + if (find_it == connect_devices.end()) { + return Status("Device \"%s\" not found, check HDC_UTID environment variable", + hdc_utid.c_str()); + } + hdc.SetDeviceID(*find_it); + } + return error; +} + +HdcClient::HdcClient(const std::string &connect_addr, + const std::string &device_id) + : m_device_id(device_id), m_connect_addr(connect_addr) {} + +HdcClient::HdcClient(HdcClient &&) = default; + +HdcClient::~HdcClient() = default; + +void HdcClient::SetDeviceID(const std::string &device_id) { + m_device_id = device_id; +} + +const std::string &HdcClient::GetDeviceID() const { return m_device_id; } + +bool HdcClient::IsServerLocal() { + return m_connect_addr == "localhost" || m_connect_addr == "127.0.0.1"; +} + +Status HdcClient::Connect() { + struct ChannelHandShake { + unsigned size; + char banner[12]; + char connectKey[32]; + } __attribute__((packed)); + + Status error; + ChannelHandShake handshake = {}; + if (m_device_id.size() > sizeof(handshake.connectKey)) { + return Status("Device id is too long: %s", m_device_id.c_str()); + } + m_conn.reset(new ConnectionFileDescriptor); + std::string port = "8710"; + std::string uri = "connect://" + m_connect_addr + ":" + port; + m_conn->Connect(uri.c_str(), &error); + ConnectionStatus status = eConnectionStatusError; + if (error.Success()) { + error = ReadAllBytes(&handshake, sizeof(handshake)); + if (error.Success()) { + memset_s(handshake.connectKey, sizeof(handshake.connectKey), 0, sizeof(handshake.connectKey)); + memcpy_s(handshake.connectKey, sizeof(handshake.connectKey), m_device_id.c_str(), m_device_id.size()); + m_conn->Write(&handshake, sizeof(handshake), status, &error); + } + } + return error; +} + +Status HdcClient::GetDevices(DeviceIDList &device_list) { + device_list.clear(); + + auto error = SendMessage("list targets"); + if (error.Fail()) { + return error; + } + + std::vector in_buffer; + error = ReadMessage(in_buffer); + + llvm::StringRef response(&in_buffer[0], in_buffer.size()); + const unsigned vector_size = 4; + llvm::SmallVector devices; + response.split(devices, "\n", -1, false); + + for (const auto device : devices) { + device_list.push_back(static_cast(device.split('\t').first)); + } + // Force disconnect since HDC closes connection after host:devices response is sent. + m_conn.reset(); + return error; +} + +Status HdcClient::SetPortForwarding(const uint16_t local_port, + const uint16_t remote_port) { + std::stringstream cmd; + cmd << "fport tcp:" << local_port << " tcp:" << remote_port; + + const auto error = SendMessage(cmd.str()); + if (error.Fail()) { + return error; + } + return ReadResponseStatus("Forwardport result:OK"); +} + +Status HdcClient::SetPortForwarding(const uint16_t local_port, + llvm::StringRef remote_socket_name, + const UnixSocketNamespace socket_namespace) { + const char *sock_namespace_str = (socket_namespace == UnixSocketNamespace::UnixSocketNamespaceAbstract) + ? kSocketNamespaceAbstract + : kSocketNamespaceFileSystem; + std::stringstream cmd; + cmd << "fport tcp:" << local_port << " " << sock_namespace_str + << ":" << remote_socket_name.str().c_str(); + const auto error = SendMessage(cmd.str()); + if (error.Fail()) { + return error; + } + return ReadResponseStatus("Forwardport result:OK"); +} + +Status HdcClient::DeletePortForwarding(std::pair fwd) { + std::stringstream cmd; + cmd << "fport rm tcp:" << fwd.first << " tcp:" << fwd.second; + + const auto error = SendMessage(cmd.str()); + if (error.Fail()) { + return error; + } + return ReadResponseStatus("Remove forward ruler success"); +} + +Status HdcClient::DeletePortForwarding(const uint16_t local_port, + const std::string remote_socket_name, + const UnixSocketNamespace socket_namespace) { + const char *sock_namespace_str = (socket_namespace == UnixSocketNamespace::UnixSocketNamespaceAbstract) + ? kSocketNamespaceAbstract + : kSocketNamespaceFileSystem; + std::stringstream cmd; + cmd << "fport rm tcp:" << local_port << " " << sock_namespace_str + << ":" << remote_socket_name.c_str(); + const auto error = SendMessage(cmd.str()); + if (error.Fail()) { + return error; + } + return ReadResponseStatus("Remove forward ruler success"); +} + +Status HdcClient::LocalTransferFile(const char *direction, const FileSpec &src, + const FileSpec &dst) { + const unsigned vector_size = 128; + llvm::SmallVector cwd; + std::error_code ec = llvm::sys::fs::current_path(cwd); + if (ec) + return Status(ec); + + std::stringstream cmd; + cmd << "file " << direction << " -cwd "; + cmd.write(cwd.data(), cwd.size()); + cmd << " \"" << src.GetPath() << "\" \"" << dst.GetPath() << "\""; + Status error = SendMessage(cmd.str()); + if (error.Fail()) + return error; + + return ReadResponseStatus("FileTransfer finish"); +} + +Status HdcClient::ExpectCommandMessagePrefix(uint16_t expected_command, + std::vector &message, + size_t prefix_size) { + uint16_t command; + Status error = ReadCommandMessagePrefix(command, message, prefix_size); + if (error.Fail()) + return error; + if (command != expected_command) + return Status("Unexpected HDC server command: %d, expected %d", command, + expected_command); + + return error; +} + +Status HdcClient::ExpectCommandMessage(uint16_t expected_command, + std::vector &message) { + return ExpectCommandMessagePrefix(expected_command, message, + std::numeric_limits::max()); +} + +template struct HdcIO; + +struct HdcTagIO { + enum class WireType : uint32_t { VARINT = 0, LENGTH_DELIMETED = 2 }; + + template + static llvm::Optional> ParseTag(InItT &InBegin, + InItT InEnd) { + llvm::Optional Tag = HdcIO::Parse(InBegin, InEnd); + return Tag.map([](U Val) { + // Move by 3 bits to obtain the position of the write type. + return std::make_pair(static_cast(Val >> 3), + static_cast(Val & 0x7)); + }); + } + + template + static void SerializeTag(size_t Idx, WireType Type, OutItT &OutIt) { + // Move by 3 bits to obtain the position of the write type. + HdcIO::Serialize(static_cast(Type) | (static_cast(Idx) << 3), OutIt); + } +}; + +template +struct HdcIOBase { + using ValueT = T; + + static HdcTagIO::WireType GetWireType() { return TheType; } + + template + static llvm::Optional ParseTagged(size_t Idx, InItT &InBegin, + InItT InEnd) { + if (!HdcTagIO::ParseTag(InBegin, InEnd) + .map([Idx](auto P) { + return P.first == Idx && P.second == TheType; + }) + .value_or(false)) + return {}; + return DerivedT::Parse(InBegin, InEnd); + } + + template + static void SerializeTagged(size_t Idx, T Value, OutItT &OutIt) { + HdcTagIO::SerializeTag(Idx, TheType, OutIt); + DerivedT::Serialize(Value, OutIt); + } +}; + +template +struct HdcIO : HdcIOBase> { + static_assert(std::is_integral::value, "Don't know how to parse T"); + + template + static llvm::Optional Parse(InItT &InBegin, InItT InEnd) { + constexpr size_t NBytes = (sizeof(T) * 8 + 6) / 7; + T value = 0; + for (size_t c = 0; c < NBytes; ++c) { + uint8_t x; + if (InBegin == InEnd) + return {}; + x = *InBegin++; + // Move by 7 bits to obtain the position of tag + value |= static_cast(x & 0x7Fu) << 7 * c; + if (!(x & 0x80)) + return value; + } + return {}; + } + + template static void Serialize(T value, OutItT &OutIt) { + constexpr size_t NBytes = (sizeof(T) * 8 + 6) / 7; + uint8_t b[NBytes]; + for (size_t i = 0; i < NBytes; ++i) { + b[i] = value & 0b0111'1111; + // Move by 7 bits to obtain the position of tag + value >>= 7; + if (value) { + b[i] |= 0b1000'0000; + } else { + OutIt = std::copy_n(std::begin(b), i + 1, OutIt); + return; + } + } + } +}; + +template <> +struct HdcIO : HdcIOBase> { + template + static llvm::Optional Parse(InItT &InBegin, InItT InEnd) { + auto MaybeLen = HdcIO::Parse(InBegin, InEnd); + if (!MaybeLen) + return llvm::None; + size_t Len = *MaybeLen; + std::string Res; + Res.reserve(Len); + while (Res.size() < Len && InBegin != InEnd) + Res.push_back(*InBegin++); + if (Res.size() < Len) + return {}; + return Res; + } + + template + static void Serialize(const std::string &value, OutItT &OutIt) { + HdcIO::Serialize(value.size(), OutIt); + OutIt = std::copy_n(value.begin(), value.size(), OutIt); + } +}; + +template struct HdcTaggedIOHelper { + template + static llvm::Optional> Parse(InItT &InBegin, InItT InEnd) { + return std::tuple<>{}; + } + + template static void Serialize(OutItT &OutIt) {} +}; + +template +struct HdcTaggedIOHelper { + using ResT = llvm::Optional>; + + template static ResT Parse(InItT &InBegin, InItT InEnd) { + return HdcIO::ParseTagged(StartIdx, InBegin, InEnd) + .map([&](auto &&LHS) { + return HdcTaggedIOHelper::Parse(InBegin, InEnd) + .map([LHS = std::move(LHS)](auto &&RHS) { + return std::tuple_cat(std::make_tuple(std::move(LHS)), + std::move(RHS)); + }); + }) + .value_or(ResT(llvm::None)); + } + + template static ResT ParseOnce(InItT InBegin, InItT InEnd) { + return Parse(InBegin, InEnd); + } + + template + static void Serialize(const T &Arg, const Ts &...Args, OutItT &OutIt) { + HdcIO::SerializeTagged(StartIdx, std::move(Arg), OutIt); + HdcTaggedIOHelper::Serialize(std::move(Args)..., + OutIt); + } + + template + static void SerializeOnce(const T &Arg, const Ts &...Args, OutItT OutIt) { + return Serialize(Arg, Args..., OutIt); + } +}; + +template struct HdcTaggedIO : HdcTaggedIOHelper<1, Ts...> {}; + +template struct HdcTaggedIOTuple; + +template +struct HdcTaggedIOTuple> : HdcTaggedIO {}; + +using HdcTransferConfig = std::tuple; + +using HdcTransferPayload = std::tuple; + +using HdcFileMode = std::tuple; // fullName + +enum HdcCommand : uint16_t { + CMD_KERNEL_WAKEUP_SLAVETASK = 12, + CMD_FILE_INIT = 3000, + CMD_FILE_CHECK, + CMD_FILE_BEGIN, + CMD_FILE_DATA, + CMD_FILE_FINISH, + CMD_FILE_MODE = 3006 +}; + +static constexpr size_t HdcTransferPayloadPrefixReserve = 64; +static constexpr size_t HdcTransferPayloadMaxChunkSize = 49152; + +Status HdcClient::FileCheck(int FD, size_t &file_size) { + std::vector msg; + Status error = ExpectCommandMessage(HdcCommand::CMD_FILE_MODE, msg); + if (error.Fail()) + return error; + + auto maybe_file_mode = HdcTaggedIOTuple::ParseOnce(msg.cbegin(), msg.cend()); + if (!maybe_file_mode) + return Status("Could not parse HDC server FileMode"); + + auto &file_mode = *maybe_file_mode; + + uint32_t perms = static_cast(std::get<0>(file_mode)); + auto EC = llvm::sys::fs::setPermissions( + FD, static_cast(perms) & llvm::sys::fs::all_perms); + if (EC) + return Status(EC); + + error = SendCommandMessage(HdcCommand::CMD_FILE_MODE, {}); + if (error.Fail()) + return error; + + error = ExpectCommandMessage(HdcCommand::CMD_FILE_CHECK, msg); + if (error.Fail()) + return error; + + auto transfer_config = + HdcTaggedIOTuple::ParseOnce(msg.cbegin(), msg.cend()); + if (!transfer_config.has_value()) + return Status("Could not parse HDC server TransferConfig"); + + if (auto compress_type = std::get<7>(*transfer_config)) + return Status("Compression is not supported"); + + file_size = std::get<0>(*transfer_config); + + return SendCommandMessage(HdcCommand::CMD_FILE_BEGIN, {}); +} + +Status HdcClient::PullFileChunk(std::vector &buffer) { + buffer.clear(); + + std::vector msg; + Status error = ExpectCommandMessagePrefix(HdcCommand::CMD_FILE_DATA, msg, + HdcTransferPayloadPrefixReserve); + if (error.Fail()) + return error; + + auto transfer_payload = + HdcTaggedIOTuple::ParseOnce(msg.cbegin(), msg.cend()); + if (!transfer_payload.has_value()) + return Status("Could not parse HDC server TransferPayload"); + + if (auto compress_type = std::get<1>(*transfer_payload)) + return Status("Compression is not supported"); + + uint32_t read_bytes = std::get<3>(*transfer_payload); + buffer.resize(read_bytes, 0); + error = ReadAllBytes(buffer.data(), buffer.size()); + if (error.Fail()) + buffer.clear(); + + return error; +} + +Status HdcClient::RecvFile(const FileSpec &src, const FileSpec &dst) { + if (IsServerLocal()) + return LocalTransferFile("recv", src, dst); + + const auto local_file_path = dst.GetPath(); + llvm::FileRemover local_file_remover(local_file_path); + + int dst_file_fd; + auto EC = llvm::sys::fs::openFileForWrite(local_file_path, dst_file_fd); + if (EC) + return Status("Unable to open local file %s", local_file_path.c_str()); + + std::stringstream cmd; + cmd << "file recv remote -m"; + cmd << " \"" << src.GetPath() << "\" \"" << dst.GetPath() << "\""; + Status error = SendMessage(cmd.str()); + if (error.Fail()) + return error; + + size_t cur_size = 0; + size_t all_size = 0; + error = FileCheck(dst_file_fd, all_size); + if (error.Fail()) + return error; + + llvm::raw_fd_ostream dst_file(dst_file_fd, true); + + std::vector buf; + while (cur_size < all_size) { + error = PullFileChunk(buf); + if (error.Fail()) + return error; + dst_file.write(buf.data(), buf.size()); + cur_size += buf.size(); + } + + error = SendCommandMessage(HdcCommand::CMD_FILE_FINISH, {}); + if (error.Fail()) + return error; + error = ReadResponseStatus("FileTransfer finish"); + if (error.Fail()) + return error; + + dst_file.close(); + if (dst_file.has_error()) + return Status("Failed to write file %s", local_file_path.c_str()); + + local_file_remover.releaseFile(); + return error; +} + +Status HdcClient::FileInit(size_t file_size, uint32_t perm, uint32_t u_id, + uint32_t g_id, const std::string &remote_path) { + std::vector msg; + Status error = ExpectCommandMessage(HdcCommand::CMD_FILE_INIT, msg); + if (error.Fail()) + return error; + + error = SendCommandMessage(HdcCommand::CMD_KERNEL_WAKEUP_SLAVETASK, {}); + if (error.Fail()) + return error; + + constexpr uint64_t IFREG_MASK = 0100000; + msg.clear(); + HdcTaggedIOTuple::SerializeOnce(perm | IFREG_MASK, // perm + u_id, // u_id + g_id, // g_id + "", // context + "", // fullName + std::back_inserter(msg)); + error = SendCommandMessage(HdcCommand::CMD_FILE_MODE, msg); + if (error.Fail()) + return error; + + error = ExpectCommandMessage(HdcCommand::CMD_FILE_MODE, msg); + if (error.Fail()) + return error; + + msg.clear(); + HdcTaggedIOTuple::SerializeOnce(file_size, // fileSize + 0, // atime + 0, // mtime + "", // options + remote_path, // path + "", // optionalName + false, // updateIfNew + 0, // compressType + false, // holdTimestamp + "", // funcName + "", // clientCwd + "", // reserve1 + "", // reserve2 + std::back_inserter(msg)); + error = SendCommandMessage(HdcCommand::CMD_FILE_CHECK, msg); + if (error.Fail()) + return error; + + return ExpectCommandMessage(HdcCommand::CMD_FILE_BEGIN, msg); +} + +Status HdcClient::PushFileChunk(std::vector &buffer, size_t chunk_size, + size_t index) { + std::fill_n(buffer.begin(), HdcTransferPayloadPrefixReserve, 0); + HdcTaggedIOTuple::SerializeOnce( + index, // index + 0, // compressType + chunk_size, // compressSize + chunk_size, // uncompressSize + buffer.begin()); + return SendCommandMessage(CMD_FILE_DATA, buffer); +} + +Status HdcClient::SendFile(const FileSpec &src, const FileSpec &dst) { + if (IsServerLocal()) + return LocalTransferFile("send", src, dst); + + const auto local_file_path = src.GetPath(); + std::ifstream src_file(local_file_path.c_str(), std::ios::in | std::ios::binary); + if (!src_file.is_open()) + return Status("Unable to open local file %s", local_file_path.c_str()); + + std::stringstream cmd; + cmd << "file send remote -m \"" << src.GetPath() << "\" \"" << dst.GetPath() << "\""; + Status error = SendMessage(cmd.str()); + if (error.Fail()) + return error; + + llvm::sys::fs::file_status status; + auto EC = llvm::sys::fs::status(local_file_path, status); + if (EC) + return Status(EC); + + error = FileInit(status.getSize(), status.permissions(), status.getUser(), + status.getGroup(), dst.GetPath()); + if (error.Fail()) + return error; + + std::vector buffer; + size_t sent_bytes = 0; + while (!src_file.eof()) { + buffer.resize(HdcTransferPayloadPrefixReserve + + HdcTransferPayloadMaxChunkSize); + if (src_file + .read(buffer.data() + HdcTransferPayloadPrefixReserve, + HdcTransferPayloadMaxChunkSize) + .bad()) + break; + size_t chunk_size = src_file.gcount(); + buffer.resize(HdcTransferPayloadPrefixReserve + chunk_size); + error = PushFileChunk(buffer, chunk_size, sent_bytes); + if (error.Fail()) + return error; + sent_bytes += chunk_size; + } + + if (src_file.bad()) + return Status("Failed read on %s", local_file_path.c_str()); + if (sent_bytes < status.getSize()) { + m_conn.reset(); + return Status("Failed to read all of the bytes from %s: read %zu/%zu", + local_file_path.c_str(), sent_bytes, status.getSize()); + } + + error = ExpectCommandMessage(HdcCommand::CMD_FILE_FINISH, buffer); + if (error.Fail()) + return error; + + error = SendCommandMessage(HdcCommand::CMD_FILE_FINISH, {}); + if (error.Fail()) + return error; + + return ReadResponseStatus("FileTransfer finish"); +} + +Status HdcClient::SendMessage(llvm::StringRef packet, const bool reconnect) { + Status error; + if (!m_conn || reconnect) { + error = Connect(); + if (error.Fail()) { + return error; + } + } + + unsigned msg_len = packet.size() + 1; + const unsigned vector_size = 128; + llvm::SmallVector message(msg_len + sizeof(unsigned), 0); + unsigned len = htonl(msg_len); + memcpy_s(message.data(), message.size(), &len, sizeof(len)); + memcpy_s(message.data() + sizeof(len), message.size() - sizeof(len), packet.data(), packet.size()); + + ConnectionStatus status; + m_conn->Write(message.data(), message.size(), status, &error); + if (error.Fail()) { + return error; + } + + return error; +} + +Status HdcClient::SendCommandMessage(uint16_t command, llvm::ArrayRef packet) { + llvm::SmallVector buf(sizeof(command) + packet.size()); + std::copy_n(reinterpret_cast(&command), sizeof(command), buf.begin()); + std::copy_n(packet.begin(), packet.size(), buf.begin() + sizeof(command)); + return SendMessage(llvm::StringRef(buf.data(), buf.size()), false); +} + +Status HdcClient::ReadMessage(std::vector &message) { + message.clear(); + + unsigned packet_len; + auto error = ReadAllBytes(&packet_len, sizeof(packet_len)); + if (error.Fail()) { + return error; + } + + packet_len = htonl(packet_len); + message.resize(packet_len, 0); + error = ReadAllBytes(&message[0], packet_len); + if (error.Fail()) { + message.clear(); + } + + return error; +} + +Status HdcClient::ReadCommandMessagePrefix(uint16_t &command, std::vector &message, size_t prefix_size) { + message.clear(); + + unsigned packet_len; + auto error = ReadAllBytes(&packet_len, sizeof(packet_len)); + if (error.Fail()) + return error; + + packet_len = htonl(packet_len); + if (packet_len < sizeof(command)) + return Status("Message too small to contain a command"); + + error = ReadAllBytes(&command, sizeof(command)); + if (error.Fail()) { + command = 0; + return error; + } + + message.resize(std::min(packet_len - sizeof(command), prefix_size), 0); + error = ReadAllBytes(&message[0], message.size()); + if (error.Fail()) { + command = 0; + message.clear(); + } + + return error; +} + +Status HdcClient::ReadCommandMessage(uint16_t &command, + std::vector &message) { + return ReadCommandMessagePrefix(command, message, + std::numeric_limits::max()); +} + +Status HdcClient::ReadMessageStream(std::vector &message, + milliseconds timeout) { + auto start = steady_clock::now(); + message.clear(); + + Status error; + lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess; + char buffer[1024]; + while (error.Success() && status == lldb::eConnectionStatusSuccess) { + auto end = steady_clock::now(); + auto elapsed = end - start; + if (elapsed >= timeout) { + return Status("Timed out"); + } + + size_t n = m_conn->Read(buffer, sizeof(buffer), + duration_cast(timeout - elapsed), + status, &error); + if (n > 0) { + message.insert(message.end(), &buffer[0], &buffer[n]); + } + } + return error; +} + +Status HdcClient::ReadResponseStatus(const char *expected) { + unsigned len; + auto error = ReadAllBytes(&len, sizeof(len)); + if (error.Fail()) { + return error; + } + len = htonl(len); + const unsigned vector_size = 128; + llvm::SmallVector message(len + 1); + error = ReadAllBytes(message.data(), len); + if (error.Fail()) { + return error; + } + + message[len] = 0; + if (expected == nullptr || + strncmp(message.data(), expected, strlen(expected))) { + return Status("%s", message.data()); + } + + return error; +} + +Status HdcClient::ReadAllBytes(void *buffer, size_t size) { + return ::ReadAllBytes(*m_conn, buffer, size); +} + +Status HdcClient::Shell(const char *command, milliseconds timeout, + std::string *output) { + assert(command && command[0]); + std::string cmd = "shell "; + cmd += command; + Status error = SendMessage(cmd); + if (error.Fail()) { + return error; + } + std::vector message; + error = ReadMessageStream(message, timeout); + if (error.Fail()) { + return error; + } + (*output) = std::string(message.data(), message.size()); + return error; +} diff --git a/lldb/source/Plugins/Platform/OHOS/HdcClient.h b/lldb/source/Plugins/Platform/OHOS/HdcClient.h new file mode 100644 index 000000000..084f2912a --- /dev/null +++ b/lldb/source/Plugins/Platform/OHOS/HdcClient.h @@ -0,0 +1,127 @@ +//===-- HdcClient.h -------------------------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PLATFORM_OHOS_HDCCLIENT_H +#define LLDB_SOURCE_PLUGINS_PLATFORM_OHOS_HDCCLIENT_H + +#include +#include +#include +#include +#include +#include + +#include "lldb/Utility/Status.h" + +namespace lldb_private { + +class FileSpec; + +namespace platform_ohos { + +class HdcClient { +public: + enum class UnixSocketNamespace { + UnixSocketNamespaceAbstract, + UnixSocketNamespaceFileSystem, + }; + + using DeviceIDList = std::list; + + static Status CreateByDeviceID(const std::string &device_id, HdcClient &hdc); + + explicit HdcClient(const std::string &connect_addr, + const std::string &device_id = ""); + + HdcClient(HdcClient &&); + + ~HdcClient(); + + const std::string &GetDeviceID() const; + + Status GetDevices(DeviceIDList &device_list); + + Status SetPortForwarding(const uint16_t local_port, + const uint16_t remote_port); + + Status SetPortForwarding(const uint16_t local_port, + llvm::StringRef remote_socket_name, + const UnixSocketNamespace socket_namespace); + + Status DeletePortForwarding(std::pair fwd); + + Status DeletePortForwarding(const uint16_t local_port, const std::string remote_socket_name, + const UnixSocketNamespace socket_namespace); + + Status RecvFile(const FileSpec &src, const FileSpec &dst); + + Status SendFile(const FileSpec &src, const FileSpec &dst); + + Status Shell(const char *command, std::chrono::milliseconds timeout, + std::string *output); + + HdcClient& operator=(HdcClient&&) = delete; + +private: + bool IsServerLocal(); + + Status Connect(); + + Status LocalTransferFile(const char *direction, const FileSpec &src, + const FileSpec &dst); + + Status FileCheck(int FD, size_t &file_size); + + Status PullFileChunk(std::vector &buffer); + + Status FileInit(size_t file_size, uint32_t perm, uint32_t u_id, uint32_t g_id, + const std::string &remote_path); + + Status PushFileChunk(std::vector &buffer, size_t chunk_size, + size_t index); + + void SetDeviceID(const std::string &device_id); + + Status SendMessage(llvm::StringRef packet, const bool reconnect = true); + + Status SendDeviceMessage(const std::string &packet); + + Status ReadMessage(std::vector &message); + + Status SendCommandMessage(uint16_t command, llvm::ArrayRef packet); + + Status ReadCommandMessagePrefix(uint16_t &command, std::vector &message, + size_t prefix_size); + + Status ReadCommandMessage(uint16_t &command, std::vector &message); + + Status ExpectCommandMessage(uint16_t expected_command, + std::vector &message); + + Status ExpectCommandMessagePrefix(uint16_t expected_command, + std::vector &message, + size_t prefix_size); + + Status ReadMessageStream(std::vector &message, + std::chrono::milliseconds timeout); + + Status ReadResponseStatus(const char *expected); + + Status ReadAllBytes(void *buffer, size_t size); + + std::string m_device_id; + std::string m_connect_addr; + std::unique_ptr m_conn; +}; + +} // namespace platform_ohos +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PLATFORM_OHOS_HDCCLIENT_H diff --git a/lldb/source/Plugins/Platform/OHOS/PlatformOHOS.cpp b/lldb/source/Plugins/Platform/OHOS/PlatformOHOS.cpp new file mode 100644 index 000000000..2ad6fdb41 --- /dev/null +++ b/lldb/source/Plugins/Platform/OHOS/PlatformOHOS.cpp @@ -0,0 +1,343 @@ +//===-- PlatformOHOS.cpp --------------------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#include "PlatformOHOS.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Scalar.h" +#include "lldb/Utility/UriParser.h" +#include "HdcClient.h" +#include "PlatformOHOSRemoteGDBServer.h" + +#if defined(__OHOS__) +#include +#else +// Define these constants from OHOS mman.h for use when targeting remote OHOS +// systems even when host has different values. +#define PROT_NONE 0 +#define PROT_READ 1 +#define PROT_WRITE 2 +#define PROT_EXEC 4 +#define MAP_PRIVATE 2 +#define MAP_ANON 0x20 +#endif +#define MAP_ANON_MIPS 0x800 +// MAP_JIT allows to allocate anonymous memory with executable permissions. +#define MAP_JIT 0x1000 + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::platform_ohos; +using namespace std::chrono; + +LLDB_PLUGIN_DEFINE(PlatformOHOS) + +static uint32_t g_initialize_count = 0; + + +void PlatformOHOS::Initialize() { + PlatformLinux::Initialize(); + + if (g_initialize_count++ == 0) { +#if defined(__OHOS__) + PlatformSP default_platform_sp(new PlatformOHOS(true)); + default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); + Platform::SetHostPlatform(default_platform_sp); +#endif + PluginManager::RegisterPlugin( + PlatformOHOS::GetPluginNameStatic(false), + PlatformOHOS::GetPluginDescriptionStatic(false), + PlatformOHOS::CreateInstance); + } +} + +void PlatformOHOS::Terminate() { + if (g_initialize_count > 0) { + if (--g_initialize_count == 0) { + PluginManager::UnregisterPlugin(PlatformOHOS::CreateInstance); + } + } + + PlatformLinux::Terminate(); +} + +PlatformSP PlatformOHOS::CreateInstance(bool force, const ArchSpec *arch) { + Log *log = GetLog(LLDBLog::Platform); + if (log) { + const char *arch_name; + if (arch && arch->GetArchitectureName()) { + arch_name = arch->GetArchitectureName(); + } else { + arch_name = ""; + } + + const char *triple_cstr = + arch ? arch->GetTriple().getTriple().c_str() : ""; + + LLDB_LOGF(log, "PlatformOHOS::%s(force=%s, arch={%s,%s})", __FUNCTION__, + force ? "true" : "false", arch_name, triple_cstr); + } + + bool create = force; + if (!create && arch && arch->IsValid()) { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) { + case llvm::Triple::PC: + create = true; + break; + default: + break; + } + + if (create) { + switch (triple.getEnvironment()) { + case llvm::Triple::OpenHOS: + break; + default: + create = false; + break; + } + } + } + + if (create) { + LLDB_LOGF(log, "PlatformOHOS::%s() creating remote-ohos platform", + __FUNCTION__); + + return PlatformSP(new PlatformOHOS(false)); + } + + LLDB_LOGF(log, + "PlatformOHOS::%s() aborting creation of remote-ohos platform", + __FUNCTION__); + + return PlatformSP(); +} + +PlatformOHOS::PlatformOHOS(bool is_host) + : PlatformLinux(is_host), m_sdk_version(0) {} + +PlatformOHOS::~PlatformOHOS() {} + +const char *PlatformOHOS::GetPluginDescriptionStatic(bool is_host) { + if (is_host) { + return "Local HarmonyOS user platform plug-in."; + } + return "Remote HarmonyOS user platform plug-in."; +} + +llvm::StringRef PlatformOHOS::GetPluginName() { + return GetPluginNameStatic(IsHost()); +} + +Status PlatformOHOS::ConnectRemote(Args &args) { + m_device_id.clear(); + m_connect_addr = "localhost"; + Status error; + if (IsHost()) { + error.SetErrorString("can't connect to the host platform, always connected"); + return error; + } + + if (!m_remote_platform_sp) { + m_remote_platform_sp = PlatformSP(new PlatformOHOSRemoteGDBServer()); + } + + const char *url = args.GetArgumentAtIndex(0); + if (!url) { + error.SetErrorString("URL is null."); + return error; + } + llvm::Optional parsed_url = URI::Parse(url); + if (!parsed_url) { + error.SetErrorStringWithFormat("Invalid URL: %s", url); + return error; + } + Log *log = GetLog(LLDBLog::Platform); + if (PlatformOHOSRemoteGDBServer::IsHostnameDeviceID(parsed_url->hostname)) { + // accepts no (empty) hostname too + m_device_id = parsed_url->hostname.str(); + LLDB_LOG(log, "Treating hostname as device id: \"{0}\"", m_device_id); + } else { + m_connect_addr = parsed_url->hostname.str(); + LLDB_LOG(log, "Treating hostname as remote HDC server address: \"{0}\"", + m_connect_addr); + } + + error = PlatformLinux::ConnectRemote(args); + if (error.Success()) { + HdcClient hdc(m_connect_addr); + error = HdcClient::CreateByDeviceID(m_device_id, hdc); + if (error.Fail()) { + return error; + } + m_device_id = hdc.GetDeviceID(); + } + return error; +} + +Status PlatformOHOS::DisconnectRemote() { + Status error = PlatformLinux::DisconnectRemote(); + if (error.Success()) { + m_device_id.clear(); + m_sdk_version = 0; + } + return error; +} + +uint32_t PlatformOHOS::GetDefaultMemoryCacheLineSize() { + // Fits inside 4k hdc packet. + const unsigned int g_ohos_default_cache_size = 2048; + return g_ohos_default_cache_size; +} + +uint32_t PlatformOHOS::GetSdkVersion() { + Log *log = GetLog(LLDBLog::Platform); + + if (!IsConnected()) { + LLDB_LOGF(log, "GetSdkVersion require connect first"); + return 0; + } + if (m_sdk_version != 0) { + return m_sdk_version; + } + + std::string sdk_version; + HdcClient hdc = CreateHdcClient(); + Status error = hdc.Shell("param get const.ohos.apiversion", seconds(5), &sdk_version); + sdk_version = llvm::StringRef(sdk_version).trim().str(); + if (error.Fail() || sdk_version.empty()) { + Log *log = GetLog(LLDBLog::Platform); + LLDB_LOGF(log, "Get SDK version failed. (error: %s, output: %s)", + error.AsCString(), sdk_version.c_str()); + m_sdk_version = 0xFFFFFFFF; + return 0; + } + + m_sdk_version = 0xFFFFFFFF; + llvm::to_integer(sdk_version, m_sdk_version); + if (m_sdk_version == 0xFFFFFFFF) { + return 0; + } + + return m_sdk_version; +} + +llvm::StringRef PlatformOHOS::GetLibdlFunctionDeclarations(lldb_private::Process *process) { + SymbolContextList matching_symbols; + const char *dl_open_name = nullptr; + std::vector dl_open_names = { "__dl_dlopen", "dlopen" }; + + auto moduleList = process->GetTarget().GetImages(); + for (uint64_t i = 0; i < dl_open_names.size(); i++) { + moduleList.FindFunctionSymbols(ConstString(dl_open_names[i]), + eFunctionNameTypeFull, matching_symbols); + if (matching_symbols.GetSize()) { + dl_open_name = dl_open_names[i]; + break; + } + } + // Older platform versions have the dl function symbols mangled + if (dl_open_name == dl_open_names[0]) { + return R"( + extern "C" void* dlopen(const char*, int) asm("__dl_dlopen"); + extern "C" void* dlsym(void*, const char*) asm("__dl_dlsym"); + extern "C" int dlclose(void*) asm("__dl_dlclose"); + extern "C" char* dlerror(void) asm("__dl_dlerror"); + )"; + } + return PlatformPOSIX::GetLibdlFunctionDeclarations(process); +} + +Status PlatformOHOS::GetFile(const FileSpec &source, + const FileSpec &destination) { + if (IsHost() || !m_remote_platform_sp) { + return PlatformLinux::GetFile(source, destination); + } + + FileSpec source_spec(source.GetPath(false), FileSpec::Style::posix); + if (source_spec.IsRelative()) { + source_spec = GetRemoteWorkingDirectory().CopyByAppendingPathComponent( + source_spec.GetCString(false)); + } + + HdcClient hdc = CreateHdcClient(); + Status error = hdc.RecvFile(source_spec, destination); + return error; +} + +Status PlatformOHOS::PutFile(const FileSpec &source, + const FileSpec &destination, uint32_t uid, + uint32_t gid) { + if (IsHost() || !m_remote_platform_sp) { + return PlatformLinux::PutFile(source, destination, uid, gid); + } + + FileSpec destination_spec(destination.GetPath(false), FileSpec::Style::posix); + if (destination_spec.IsRelative()) { + destination_spec = GetRemoteWorkingDirectory().CopyByAppendingPathComponent( + destination_spec.GetCString(false)); + } + + HdcClient hdc(m_device_id); + Status error = hdc.SendFile(source, destination_spec); + return error; +} + +bool PlatformOHOS::GetRemoteOSVersion() { + m_os_version = llvm::VersionTuple(GetSdkVersion()); + return !m_os_version.empty(); +} + +Status PlatformOHOS::DownloadModuleSlice(const FileSpec &src_file_spec, + const uint64_t src_offset, + const uint64_t src_size, + const FileSpec &dst_file_spec) { + if (src_offset != 0) { + return Status("Invalid offset - %" PRIu64, src_offset); + } + + return GetFile(src_file_spec, dst_file_spec); +} + +const char *PlatformOHOS::GetCacheHostname() { return m_device_id.c_str(); } + +HdcClient PlatformOHOS::CreateHdcClient() { + return HdcClient(m_connect_addr, m_device_id); +} + +MmapArgList PlatformOHOS::GetMmapArgumentList(const ArchSpec &arch, + addr_t addr, addr_t length, + unsigned prot, unsigned flags, + addr_t fd, addr_t offset) { + // Customize mmap adaptation for OHOS platform. + // OHOS platform allows anonymous memory allocation with exec permission + // only when MAP_JIT parameter is specified. + uint64_t flags_platform = 0; + const uint64_t map_anon = arch.IsMIPS() ? MAP_ANON_MIPS : MAP_ANON; + + if (flags & eMmapFlagsPrivate) { + flags_platform |= MAP_PRIVATE; + } + if (flags & eMmapFlagsAnon) { + flags_platform |= map_anon; + } + if ((flags & eMmapFlagsAnon) && (prot & PROT_EXEC)) { + flags_platform |= MAP_JIT; + } + + MmapArgList args({addr, length, prot, flags_platform, fd, offset}); + return args; +} diff --git a/lldb/source/Plugins/Platform/OHOS/PlatformOHOS.h b/lldb/source/Plugins/Platform/OHOS/PlatformOHOS.h new file mode 100644 index 000000000..bd33919de --- /dev/null +++ b/lldb/source/Plugins/Platform/OHOS/PlatformOHOS.h @@ -0,0 +1,89 @@ +//===-- PlatformOHOS.h ----------------------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PLATFORM_OHOS_PLATFORMOHOS_H +#define LLDB_SOURCE_PLUGINS_PLATFORM_OHOS_PLATFORMOHOS_H + +#include +#include + +#include "Plugins/Platform/Linux/PlatformLinux.h" + +#include "HdcClient.h" + +namespace lldb_private { +namespace platform_ohos { + +class PlatformOHOS : public platform_linux::PlatformLinux { +public: + explicit PlatformOHOS(bool is_host); + + ~PlatformOHOS() override; + + static void Initialize(); + + static void Terminate(); + + static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch); + + static llvm::StringRef GetPluginNameStatic(bool is_host) { + return is_host ? Platform::GetHostPlatformName() : "remote-ohos"; + } + + static const char *GetPluginDescriptionStatic(bool is_host); + + llvm::StringRef GetPluginName() override; + + Status ConnectRemote(Args &args) override; + + uint32_t GetSdkVersion(); + + bool GetRemoteOSVersion() override; + + Status DisconnectRemote() override; + + uint32_t GetDefaultMemoryCacheLineSize() override; + + Status GetFile(const FileSpec &source, const FileSpec &destination) override; + + Status PutFile(const FileSpec &source, const FileSpec &destination, + uint32_t uid = UINT32_MAX, uint32_t gid = UINT32_MAX) override; + + MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr, + lldb::addr_t length, unsigned prot, + unsigned flags, lldb::addr_t fd, + lldb::addr_t offset) override; + +protected: + + llvm::StringRef + GetLibdlFunctionDeclarations(lldb_private::Process *process) override; + + const char *GetCacheHostname() override; + + Status DownloadModuleSlice(const FileSpec &src_file_spec, + const uint64_t src_offset, const uint64_t src_size, + const FileSpec &dst_file_spec) override; + + HdcClient CreateHdcClient(); + +private: + std::string m_device_id; + std::string m_connect_addr; + uint32_t m_sdk_version; + + PlatformOHOS(const PlatformOHOS &other) = delete; + PlatformOHOS& operator=(const PlatformOHOS &other) = delete; +}; + +} // namespace platform_ohos +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PLATFORM_OHOS_PLATFORMOHOS_H diff --git a/lldb/source/Plugins/Platform/OHOS/PlatformOHOSRemoteGDBServer.cpp b/lldb/source/Plugins/Platform/OHOS/PlatformOHOSRemoteGDBServer.cpp new file mode 100644 index 000000000..f29b05553 --- /dev/null +++ b/lldb/source/Plugins/Platform/OHOS/PlatformOHOSRemoteGDBServer.cpp @@ -0,0 +1,296 @@ +//===-- PlatformOHOSRemoteGDBServer.cpp ----------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/UriParser.h" +#include "llvm/Support/Regex.h" + +#include "PlatformOHOSRemoteGDBServer.h" + +#include + +using namespace lldb; +using namespace lldb_private; +using namespace platform_ohos; + +// Alias for the process id of lldb-platform +static const lldb::pid_t g_remote_platform_pid = 0; + +static uint16_t g_hdc_forward_port_offset = 0; + +Status PlatformOHOSRemoteGDBServer::ForwardPortWithHdc(const uint16_t local_port, + const uint16_t remote_port, llvm::StringRef remote_socket_name, + const llvm::Optional &socket_namespace, + std::string &device_id) { + Log *log = GetLog(LLDBLog::Platform); + + HdcClient hdc(m_connect_addr); + auto error = HdcClient::CreateByDeviceID(device_id, hdc); + if (error.Fail()) { + return error; + } + + device_id = hdc.GetDeviceID(); + LLDB_LOGF(log, "Connected to OHOS device \"%s\"", device_id.c_str()); + + if (socket_namespace) { + LLDB_LOGF(log, "Forwarding remote socket \"%s\" to local TCP port %d", + remote_socket_name.str().c_str(), local_port); + return hdc.SetPortForwarding(local_port, remote_socket_name, + *socket_namespace); + } + + LLDB_LOGF(log, "Forwarding remote TCP port %d to local TCP port %d", + remote_port, local_port); + if (remote_port == 0) { + return Status("Invalid remote_port"); + } + + return hdc.SetPortForwarding(local_port, remote_port); +} + +static Status DeleteForwardPortWithHdc(const std::string &connect_addr, + std::pair ports, + const std::string &device_id) { + Log *log = GetLog(LLDBLog::Platform); + if (log) + log->Printf("Delete port forwarding %d -> %d, device=%s", ports.first, + ports.second, device_id.c_str()); + + HdcClient hdc(connect_addr, device_id); + return hdc.DeletePortForwarding(ports); +} + +static Status DeleteForwardPortWithHdc( + const std::string &connect_addr, std::pair remote_socket, + const llvm::Optional &socket_namespace, + const std::string &device_id) { + Log *log = GetLog(LLDBLog::Platform); + uint16_t local_port = remote_socket.first; + std::string remote_socket_name = remote_socket.second; + if (log) + log->Printf("Delete port forwarding %d -> %s, device=%s", local_port, + remote_socket_name.c_str(), device_id.c_str()); + if (!socket_namespace) { + return Status("Invalid socket namespace"); + } + + HdcClient hdc(connect_addr, device_id); + return hdc.DeletePortForwarding(local_port, remote_socket_name, *socket_namespace); +} + +static Status FindUnusedPort(uint16_t &port) { + Status error; + + std::unique_ptr tcp_socket = std::make_unique(true, false); + if (error.Fail()) { + return error; + } + + error = tcp_socket->Listen("127.0.0.1:0", 1); + if (error.Success()) { + port = tcp_socket->GetLocalPortNumber(); + } + + return error; +} + +PlatformOHOSRemoteGDBServer::PlatformOHOSRemoteGDBServer() {} + +PlatformOHOSRemoteGDBServer::~PlatformOHOSRemoteGDBServer() { + for (const auto &it : m_port_forwards) { + DeleteForwardPortWithHdc(m_connect_addr, it.second, m_device_id); + } + + for (const auto &it_socket : m_remote_socket_name) { + DeleteForwardPortWithHdc(m_connect_addr, it_socket.second, m_socket_namespace, m_device_id); + } +} + +static bool IsValidIPv4(llvm::StringRef ip) { + std::string pattern = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"; + llvm::Regex regex(pattern); + return regex.match(ip); +} + +static bool IsValidIPv6(llvm::StringRef ip) { + std::string pattern = "^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$"; + llvm::Regex regex(pattern); + return regex.match(ip); +} + +bool PlatformOHOSRemoteGDBServer::IsHostnameDeviceID(llvm::StringRef hostname) { + return hostname != "localhost" && !IsValidIPv4(hostname) && + !IsValidIPv6(hostname); +} + +bool PlatformOHOSRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid) { + DeleteForwardPort(pid); + return m_gdb_client_up->KillSpawnedProcess(pid); +} + +bool PlatformOHOSRemoteGDBServer::LaunchGDBServer(lldb::pid_t &pid, std::string &connect_url) { + uint16_t remote_port = 0; + std::string socket_name; + Log *log = GetLog(LLDBLog::Platform); + if (!m_gdb_client_up->LaunchGDBServer("127.0.0.1", pid, remote_port, socket_name)) { + return false; + } + + Status error = MakeConnectURL(pid, remote_port, socket_name.c_str(), connect_url); + if (error.Fail() && log) { + LLDB_LOGF(log, "gdbserver connect URL: %s fail", connect_url.c_str()); + } + return error.Success(); +} + +Status PlatformOHOSRemoteGDBServer::ConnectRemote(Args &args) { + m_device_id.clear(); + m_connect_addr = "localhost"; + if (args.GetArgumentCount() != 1) { + return Status( + "\"platform connect\" takes a single argument: "); + } + + const char *url = args.GetArgumentAtIndex(0); + if (!url) { + return Status("URL is null."); + } + llvm::Optional parsed_url = URI::Parse(url); + if (!parsed_url) { + return Status("Invalid URL: %s", url); + } + + Log *log = GetLog(LLDBLog::Platform); + // accepts no (empty) hostname too + if (IsHostnameDeviceID(parsed_url->hostname)) { + m_device_id = parsed_url->hostname.str(); + LLDB_LOG(log, "Treating hostname as device id: \"{0}\"", m_device_id); + } else { + m_connect_addr = parsed_url->hostname.str(); + LLDB_LOG(log, "Treating hostname as remote HDC server address: \"{0}\"", + m_connect_addr); + } + + m_socket_namespace.reset(); + if (parsed_url->scheme == "unix-connect") { + m_socket_namespace = HdcClient::UnixSocketNamespace::UnixSocketNamespaceFileSystem; + } else if (parsed_url->scheme == "unix-abstract-connect") { + m_socket_namespace = HdcClient::UnixSocketNamespace::UnixSocketNamespaceAbstract; + } + + std::string connect_url; + LLDB_LOGF(log, "Rewritten platform connect URL: %s", connect_url.c_str()); + auto error = MakeConnectURL(g_remote_platform_pid, parsed_url->port.value_or(0), + parsed_url->path, connect_url); + if (error.Fail()) { + return error; + } + args.ReplaceArgumentAtIndex(0, connect_url); + error = PlatformRemoteGDBServer::ConnectRemote(args); + if (error.Fail()) { + DeleteForwardPort(g_remote_platform_pid); + } + return error; +} + +Status PlatformOHOSRemoteGDBServer::DisconnectRemote() { + DeleteForwardPort(g_remote_platform_pid); + g_hdc_forward_port_offset = 0; + return PlatformRemoteGDBServer::DisconnectRemote(); +} + +void PlatformOHOSRemoteGDBServer::DeleteForwardPort(lldb::pid_t pid) { + Log *log = GetLog(LLDBLog::Platform); + + auto it = m_port_forwards.find(pid); + auto it_socket = m_remote_socket_name.find(pid); + if (it != m_port_forwards.end() && it->second.second != 0) { + DeleteForwardPortWithHdc(m_connect_addr, it->second, m_device_id); + m_port_forwards.erase(it); + } + + if (it_socket != m_remote_socket_name.end()) { + const auto error_Socket = DeleteForwardPortWithHdc( + m_connect_addr, it_socket->second, m_socket_namespace, m_device_id); + if (error_Socket.Fail()) { + if (log) + log->Printf("Failed to delete port forwarding (pid=%" PRIu64 + ", fwd=(%d->%s)device=%s): %s", pid, it_socket->second.first, + it_socket->second.second.c_str(), m_device_id.c_str(), error_Socket.AsCString()); + } + m_remote_socket_name.erase(it_socket); + } + + return; +} + +Status PlatformOHOSRemoteGDBServer::MakeConnectURL( + const lldb::pid_t pid, const uint16_t remote_port, + llvm::StringRef remote_socket_name, std::string &connect_url) { + static const int kAttempsNum = 5; + Status error; + // There is a race possibility that somebody will occupy a port while we're + // in between FindUnusedPort and ForwardPortWithHdc - adding the loop to + // mitigate such problem. + for (auto i = 0; i < kAttempsNum; ++i) { + uint16_t local_port = 0; + error = FindUnusedPort(local_port); + if (error.Fail()) { + return error; + } + + error = ForwardPortWithHdc(local_port, remote_port, + remote_socket_name, m_socket_namespace, m_device_id); + if (error.Success()) { + if (!m_socket_namespace) { + m_port_forwards[pid] = {local_port, remote_port}; + } + else { + m_remote_socket_name[pid] ={local_port, remote_socket_name.str()}; + } + std::ostringstream url_str; + url_str << "connect://" << m_connect_addr << ":" << local_port; + connect_url = url_str.str(); + break; + } + } + + return error; +} + +lldb::ProcessSP PlatformOHOSRemoteGDBServer::ConnectProcess( + llvm::StringRef connect_url, llvm::StringRef plugin_name, + lldb_private::Debugger &debugger, lldb_private::Target *target, + lldb_private::Status &error) { + // We don't have the pid of the remote gdbserver when it isn't started by us + // but we still want to store the list of port forwards we set up in our port + // forward map. Generate a fake pid for these cases what won't collide with + // any other valid pid on ohos. + static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL; + llvm::Optional parsed_url = URI::Parse(connect_url); + if (!parsed_url) { + error.SetErrorStringWithFormat("Invalid URL: %s", connect_url.str().c_str()); + return nullptr; + } + + std::string new_connect_url; + error = MakeConnectURL(s_remote_gdbserver_fake_pid--, parsed_url->port.value_or(0), + parsed_url->path, new_connect_url); + if (error.Success()) { + return PlatformRemoteGDBServer::ConnectProcess(new_connect_url, plugin_name, + debugger, target, error); + } + return nullptr; +} diff --git a/lldb/source/Plugins/Platform/OHOS/PlatformOHOSRemoteGDBServer.h b/lldb/source/Plugins/Platform/OHOS/PlatformOHOSRemoteGDBServer.h new file mode 100644 index 000000000..22319ffc0 --- /dev/null +++ b/lldb/source/Plugins/Platform/OHOS/PlatformOHOSRemoteGDBServer.h @@ -0,0 +1,74 @@ +//===-- PlatformOHOSRemoteGDBServer.h ------------------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformOHOSRemoteGDBServer_h_ +#define liblldb_PlatformOHOSRemoteGDBServer_h_ + +#include +#include + +#include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h" + +#include "llvm/ADT/Optional.h" + +#include "HdcClient.h" + +namespace lldb_private { +namespace platform_ohos { + +class PlatformOHOSRemoteGDBServer + : public platform_gdb_server::PlatformRemoteGDBServer { +public: + PlatformOHOSRemoteGDBServer(); + + ~PlatformOHOSRemoteGDBServer() override; + + Status ConnectRemote(Args &args) override; + + Status DisconnectRemote() override; + + lldb::ProcessSP ConnectProcess(llvm::StringRef connect_url, + llvm::StringRef plugin_name, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Status &error) override; + + static bool IsHostnameDeviceID(llvm::StringRef hostname); + +protected: + std::string m_connect_addr; + std::string m_device_id; + std::map> m_port_forwards; + std::map> m_remote_socket_name; + llvm::Optional m_socket_namespace; + + bool LaunchGDBServer(lldb::pid_t &pid, std::string &connect_url) override; + + bool KillSpawnedProcess(lldb::pid_t pid) override; + + void DeleteForwardPort(lldb::pid_t pid); + + Status MakeConnectURL(const lldb::pid_t pid, const uint16_t remote_port, + llvm::StringRef remote_socket_name, + std::string &connect_url); + +private: + Status ForwardPortWithHdc(const uint16_t local_port, + const uint16_t remote_port, llvm::StringRef remote_socket_name, + const llvm::Optional &socket_namespace, + std::string &device_id); + PlatformOHOSRemoteGDBServer(const PlatformOHOSRemoteGDBServer &other) = delete; + PlatformOHOSRemoteGDBServer& operator=(const PlatformOHOSRemoteGDBServer &other) = delete; +}; + +} // namespace platform_ohos +} // namespace lldb_private + +#endif // liblldb_PlatformOHOSRemoteGDBServer_h_ diff --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp index eddda6efa..243651e7b 100644 --- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp @@ -242,6 +242,12 @@ void DebuggerThread::DebugLoop() { default: llvm_unreachable("Unhandle debug event code!"); case EXCEPTION_DEBUG_EVENT: { + const DWORD exception_code = dbe.u.Exception.ExceptionRecord.ExceptionCode; + // After upgrading the mingw version of the tool used to build cjdb to 12, + // an exception is throw during the runtime. Temporarily, ignore this exception. + if (exception_code == 0x406D1388) { + break; + } ExceptionResult status = HandleExceptionEvent(dbe.u.Exception, dbe.dwThreadId); diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp index 20f5969bd..5fb1bec8c 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -392,7 +392,11 @@ void ProcessWindows::RefreshStateAfterStop() { site->GetID()); stop_thread->SetStopInfo(stop_info); - return; + // When both breakpoint and watchpoint events are reported. + // If the breakpoint IsInternal, we also need to find watchpoint at this pc. + if (!site->IsInternal()) { + return; + } } auto *reg_ctx = static_cast( @@ -416,6 +420,12 @@ void ProcessWindows::RefreshStateAfterStop() { return; } + // If an internal breakpoint has been set stopinfo. + // And we can not find any watchpoint here. We should return. + if (site && site->ValidForThisThread(*stop_thread)) { + return; + } + LLDB_LOG(log, "single stepping thread {0}", stop_thread->GetID()); stop_info = StopInfo::CreateStopReasonToTrace(*stop_thread); stop_thread->SetStopInfo(stop_info); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index 5804c13fe..ed28e2969 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -3522,7 +3522,7 @@ GDBRemoteCommunicationServerLLGS::Handle_vRun( } LLDB_LOG(log, "failed to launch exe: {0}", m_process_launch_error); } - return SendErrorResponse(8); + return SendErrorResponse(m_process_launch_error); } GDBRemoteCommunication::PacketResult diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 10dc8d1fb..3c4e0f1bf 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -2015,9 +2015,16 @@ bool DWARFASTParserClang::ParseTemplateDIE( return false; } +static bool ShouldParseCjTemplateParameterInfos(void) { + /* Cangjie has no template parameter infos. */ + return false; +} + bool DWARFASTParserClang::ParseTemplateParameterInfos( const DWARFDIE &parent_die, TypeSystemClang::TemplateParameterInfos &template_param_infos) { + if (!ShouldParseCjTemplateParameterInfos()) + return false; if (!parent_die) return false; @@ -2880,7 +2887,7 @@ void DWARFASTParserClang::ParseSingleMember( uint64_t parent_byte_size = parent_die.GetAttributeValueAsUnsigned(DW_AT_byte_size, UINT64_MAX); - if (attrs.member_byte_offset >= parent_byte_size) { + if (attrs.member_byte_offset >= parent_byte_size && member_array_element_type.GetTypeName() != "Unit") { if (member_array_size != 1 && (member_array_size != 0 || attrs.member_byte_offset > parent_byte_size)) { diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 8ee709db9..02ca1bf1c 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -3112,7 +3112,7 @@ size_t SymbolFileDWARF::ParseVariablesForContext(const SymbolContext &sc) { m_index->GetGlobalVariables(*dwarf_cu, [&](DWARFDIE die) { VariableSP var_sp(ParseVariableDIECached(sc, die)); - if (var_sp) { + if (var_sp && !var_sp->GetType()->GetFullCompilerType().GetTypeName().GetStringRef().contains("$G")) { variables->AddVariableIfUnique(var_sp); ++vars_added; } @@ -3542,7 +3542,7 @@ void SymbolFileDWARF::ParseAndAppendGlobalVariable( // Check to see if we have already parsed this variable or constant? VariableSP var_sp = GetDIEToVariable()[die.GetDIE()]; - if (var_sp) { + if (var_sp && !var_sp->GetType()->GetFullCompilerType().GetTypeName().GetStringRef().contains("$G")) { cc_variable_list.AddVariableIfUnique(var_sp); return; } @@ -3577,7 +3577,7 @@ void SymbolFileDWARF::ParseAndAppendGlobalVariable( } var_sp = ParseVariableDIECached(sc, die); - if (!var_sp) + if (!var_sp || var_sp->GetType()->GetFullCompilerType().GetTypeName().GetStringRef().contains("$G")) return; cc_variable_list.AddVariableIfUnique(var_sp); diff --git a/lldb/source/Plugins/TypeSystem/Clang/CMakeLists.txt b/lldb/source/Plugins/TypeSystem/Clang/CMakeLists.txt index 37a3142da..7d0f49d62 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/CMakeLists.txt +++ b/lldb/source/Plugins/TypeSystem/Clang/CMakeLists.txt @@ -7,6 +7,7 @@ add_lldb_library(lldbPluginTypeSystemClang PLUGIN lldbTarget lldbUtility lldbPluginExpressionParserClang + lldbPluginExpressionParserCangjie lldbPluginSymbolFileDWARF lldbPluginSymbolFilePDB lldbPluginObjCRuntime diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index a1ebe5830..c78c0f373 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -50,6 +50,7 @@ #include "Plugins/ExpressionParser/Clang/ClangUserExpression.h" #include "Plugins/ExpressionParser/Clang/ClangUtil.h" #include "Plugins/ExpressionParser/Clang/ClangUtilityFunction.h" +#include "Plugins/ExpressionParser/Cangjie/CangjieUserExpression.h" #include "lldb/Core/DumpDataExtractor.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" @@ -906,6 +907,8 @@ TypeSystemClang::GetBasicTypeEnumeration(ConstString name) { g_type_map.Append(ConstString("bool"), eBasicTypeBool); g_type_map.Append(ConstString("float"), eBasicTypeFloat); g_type_map.Append(ConstString("double"), eBasicTypeDouble); + g_type_map.Append(ConstString("__fp16"), eBasicTypeHalf); + g_type_map.Append(ConstString("char32_t"), eBasicTypeChar32); g_type_map.Append(ConstString("long double"), eBasicTypeLongDouble); g_type_map.Append(ConstString("id"), eBasicTypeObjCID); g_type_map.Append(ConstString("SEL"), eBasicTypeObjCSel); @@ -1045,6 +1048,9 @@ CompilerType TypeSystemClang::GetBuiltinTypeForDWARFEncodingAndBitSize( if (QualTypeMatchesBitSize(bit_size, ast, ast.Int128Ty)) return GetType(ast.Int128Ty); } + if (type_name.contains("Int8")) { + return GetType(ast.SignedCharTy); + } } // We weren't able to match up a type name, just search by size if (QualTypeMatchesBitSize(bit_size, ast, ast.CharTy)) @@ -3787,7 +3793,11 @@ ConstString TypeSystemClang::GetTypeName(lldb::opaque_compiler_type_t type) { return ConstString(GetTypeNameForDecl(typedef_decl)); } - return ConstString(qual_type.getAsString(GetTypePrintingPolicy())); + std::string type_name = qual_type.getAsString(GetTypePrintingPolicy()); + if (type_name.find("const ") == 0) { + type_name.erase(0, strlen("const ")); + } + return ConstString(type_name); } ConstString @@ -3801,7 +3811,17 @@ TypeSystemClang::GetDisplayTypeName(lldb::opaque_compiler_type_t type) { printing_policy.SuppressScope = false; printing_policy.SuppressUnwrittenScope = true; printing_policy.SuppressInlineNamespace = true; - return ConstString(qual_type.getAsString(printing_policy)); + std::string type_name(qual_type.getAsString(printing_policy)); + if (type_name.find("const ") == 0) { + type_name.erase(0, strlen("const ")); + } + size_t pos = type_name.find("::"); + while (pos != std::string::npos) { + type_name.replace(pos, strlen("::"), "."); + pos = type_name.find("::", pos); + } + + return ConstString(type_name); } uint32_t @@ -9895,6 +9915,10 @@ UserExpression *ScratchTypeSystemClang::GetUserExpression( TargetSP target_sp = m_target_wp.lock(); if (!target_sp) return nullptr; + if (options.IsCangjieUserExpr()) { + return new CangjieUserExpression(*target_sp.get(), expr, prefix, language, + desired_type, options, ctx_obj); + } return new ClangUserExpression(*target_sp.get(), expr, prefix, language, desired_type, options, ctx_obj); diff --git a/lldb/source/Symbol/Function.cpp b/lldb/source/Symbol/Function.cpp index 8ec4bc90c..4734a7e79 100644 --- a/lldb/source/Symbol/Function.cpp +++ b/lldb/source/Symbol/Function.cpp @@ -483,8 +483,8 @@ bool Function::IsTopLevelFunction() { return result; } -ConstString Function::GetDisplayName() const { - return m_mangled.GetDisplayDemangledName(); +ConstString Function::GetDisplayName(const SymbolContext *sc) const { + return m_mangled.GetDisplayDemangledName(sc); } CompilerDeclContext Function::GetDeclContext() { @@ -651,10 +651,10 @@ lldb::LanguageType Function::GetLanguage() const { return lldb::eLanguageTypeUnknown; } -ConstString Function::GetName() const { - return m_mangled.GetName(); +ConstString Function::GetName(const SymbolContext *sc) const { + return m_mangled.GetName(Mangled::ePreferDemangled, sc); } -ConstString Function::GetNameNoArguments() const { - return m_mangled.GetName(Mangled::ePreferDemangledWithoutArguments); +ConstString Function::GetNameNoArguments(const SymbolContext *sc) const { + return m_mangled.GetName(Mangled::ePreferDemangledWithoutArguments, sc); } diff --git a/lldb/source/Symbol/SymbolContext.cpp b/lldb/source/Symbol/SymbolContext.cpp index a10db0755..fe6237659 100644 --- a/lldb/source/Symbol/SymbolContext.cpp +++ b/lldb/source/Symbol/SymbolContext.cpp @@ -89,9 +89,9 @@ bool SymbolContext::DumpStopContext(Stream *s, ExecutionContextScope *exe_scope, } else { ConstString name; if (!show_function_arguments) - name = function->GetNameNoArguments(); + name = function->GetNameNoArguments(this); if (!name) - name = function->GetName(); + name = function->GetName(this); if (name) name.Dump(s); } diff --git a/lldb/source/Symbol/Symtab.cpp b/lldb/source/Symbol/Symtab.cpp index 936ee04ed..c14675555 100644 --- a/lldb/source/Symbol/Symtab.cpp +++ b/lldb/source/Symbol/Symtab.cpp @@ -233,6 +233,7 @@ const Symbol *Symtab::SymbolAtIndex(size_t idx) const { static bool lldb_skip_name(llvm::StringRef mangled, Mangled::ManglingScheme scheme) { switch (scheme) { + case Mangled::eManglingSchemeCangjie: case Mangled::eManglingSchemeItanium: { if (mangled.size() < 3 || !mangled.startswith("_Z")) return true; @@ -337,9 +338,17 @@ void Symtab::InitNameIndexes() { // Symbol name strings that didn't match a Mangled::ManglingScheme, are // stored in the demangled field. - if (ConstString name = mangled.GetDemangledName()) { + SymbolContext sc; + symbol->CalculateSymbolContext(&sc); + sc.module_sp = m_objfile->GetModule(); + if (ConstString name = mangled.GetDemangledName(&sc)) { name_to_index.Append(name, value); + if (ConstString base_name = mangled.GetDemangledBaseName(&sc)) { + method_to_index.Append(base_name, value); + basename_to_index.Append(base_name, value); + } + if (symbol->ContainsLinkerAnnotations()) { // If the symbol has linker annotations, also add the version without // the annotations. diff --git a/lldb/source/Symbol/Type.cpp b/lldb/source/Symbol/Type.cpp index f83bdcdc1..9b51dd2ad 100644 --- a/lldb/source/Symbol/Type.cpp +++ b/lldb/source/Symbol/Type.cpp @@ -667,7 +667,7 @@ bool Type::GetTypeScopeAndBasename(llvm::StringRef name, TypeClass &type_class) { type_class = eTypeClassAny; - if (name.empty()) + if (name.empty() || name.contains("->")) // For cangjie function type return false; basename = name; diff --git a/lldb/source/Symbol/Variable.cpp b/lldb/source/Symbol/Variable.cpp index f65e73e5d..e2e763218 100644 --- a/lldb/source/Symbol/Variable.cpp +++ b/lldb/source/Symbol/Variable.cpp @@ -75,6 +75,13 @@ ConstString Variable::GetName() const { return m_name; } +ConstString Variable::GetMangledName() const { + ConstString name = m_mangled.GetMangledName(); + if (name) + return name; + return m_name; +} + ConstString Variable::GetUnqualifiedName() const { return m_name; } bool Variable::NameMatches(ConstString name) const { diff --git a/lldb/source/Target/ModuleCache.cpp b/lldb/source/Target/ModuleCache.cpp index 29bd2b239..794f783f8 100644 --- a/lldb/source/Target/ModuleCache.cpp +++ b/lldb/source/Target/ModuleCache.cpp @@ -133,12 +133,29 @@ Status CreateHostSysRootModuleLink(const FileSpec &root_dir_spec, JoinPath(JoinPath(root_dir_spec, hostname), platform_module_spec.GetPath().c_str()); if (FileSystem::Instance().Exists(sysroot_module_path_spec)) { + if (FileSystem::Instance().Exists(local_module_spec)) { + UUID sysroot_module_sp_uuid = (std::make_shared(ModuleSpec(sysroot_module_path_spec)))->GetUUID(); + UUID module_spec_uuid = (std::make_shared(ModuleSpec(local_module_spec)))->GetUUID(); + if (sysroot_module_sp_uuid.IsValid() && (sysroot_module_sp_uuid == module_spec_uuid)) { + delete_existing = false; + } + } + if (!delete_existing) return Status(); DecrementRefExistingModule(root_dir_spec, sysroot_module_path_spec); } + if (FileSystem::Instance().Exists(sysroot_module_path_spec)) { + auto module_sp = + std::make_shared(ModuleSpec(sysroot_module_path_spec)); + UUID module_uuid = module_sp->GetUUID(); + if (!module_uuid.IsValid()) { + return Status(); + } + } + const auto error = MakeDirectory( FileSpec(sysroot_module_path_spec.GetDirectory().AsCString())); if (error.Fail()) diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index b2f1318ca..99ad0427a 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -1542,6 +1542,10 @@ bool Process::IsPossibleDynamicValue(ValueObject &in_value) { return false; LanguageType known_type = in_value.GetObjectRuntimeLanguage(); + if (in_value.IsCangjieDynamicType()) { + return true; + } + if (known_type != eLanguageTypeUnknown && known_type != eLanguageTypeC) { LanguageRuntime *runtime = GetLanguageRuntime(known_type); return runtime ? runtime->CouldHaveDynamicValue(in_value) : false; diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 2da40ba2b..8ca01a5c4 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -106,6 +106,33 @@ bool RegisterContextUnwind::IsUnwindPlanValidForCurrentPC( return false; } +static bool GetCJThreadPC(addr_t &pc, Process *process) { + if (!process) { + return false; + } + if (process->GetCJThreadRegContext().size()) { + auto reg_map = process->GetCJThreadRegContext(); + auto arch = process->GetTarget().GetArchitecture().GetTriple().getArch(); + // x86_64 cjthread pc reg + if (arch == llvm::Triple::x86_64 && + reg_map.find(ConstString("rip")) != reg_map.end()) { + pc = reg_map[ConstString("rip")]; + return true; + } + + // aarch64 cjthread pc reg + if (arch == llvm::Triple::aarch64 && + reg_map.find(ConstString("pc")) != reg_map.end()) { + pc = reg_map[ConstString("pc")]; + return true; + } + + return false; + } + + return false; +} + // Initialize a RegisterContextUnwind which is the first frame of a stack -- the // zeroth frame or currently executing frame. @@ -120,7 +147,10 @@ void RegisterContextUnwind::InitializeZerothFrame() { return; } - addr_t current_pc = reg_ctx_sp->GetPC(); + addr_t current_pc = LLDB_INVALID_ADDRESS ; + if (!GetCJThreadPC(current_pc, exe_ctx.GetProcessPtr())) { + current_pc = reg_ctx_sp->GetPC(); + } if (current_pc == LLDB_INVALID_ADDRESS) { m_frame_type = eNotAValidFrame; @@ -485,7 +515,6 @@ void RegisterContextUnwind::InitializeNonZerothFrame() { AddressRange addr_range; m_sym_ctx_valid = m_current_pc.ResolveFunctionScope(m_sym_ctx, &addr_range); - if (m_sym_ctx.symbol) { UnwindLogMsg("with pc value of 0x%" PRIx64 ", symbol name is '%s'", pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); @@ -953,6 +982,13 @@ UnwindPlanSP RegisterContextUnwind::GetFullUnwindPlanForFrame() { if (m_behaves_like_zeroth_frame && process) { unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite( process->GetTarget(), m_thread); +#if defined(_WIN32) + auto symbol_name = GetSymbolOrFunctionName(m_sym_ctx); + if (symbol_name == "CJ_MCC_C2NStub" || symbol_name == "CJ_MCC_N2CStub" || + symbol_name == "CJ_MCC_NewObjectFast" || symbol_name == "CJ_MCC_NewFinalizerFast") { + unwind_plan_sp = func_unwinders_sp->GetAssemblyUnwindPlan(process->GetTarget(), m_thread); + } +#endif if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc)) { if (unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo) { // We probably have an UnwindPlan created by inspecting assembly @@ -1222,7 +1258,7 @@ bool RegisterContextUnwind::IsTrapHandlerSymbol( const std::vector trap_handler_names( platform_sp->GetTrapHandlerSymbolNames()); for (ConstString name : trap_handler_names) { - if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == name) || + if ((m_sym_ctx.function && m_sym_ctx.function->GetName(&m_sym_ctx) == name) || (m_sym_ctx.symbol && m_sym_ctx.symbol->GetName() == name)) { return true; } @@ -1231,7 +1267,7 @@ bool RegisterContextUnwind::IsTrapHandlerSymbol( const std::vector user_specified_trap_handler_names( m_parent_unwind.GetUserSpecifiedTrapHandlerFunctionNames()); for (ConstString name : user_specified_trap_handler_names) { - if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == name) || + if ((m_sym_ctx.function && m_sym_ctx.function->GetName(&m_sym_ctx) == name) || (m_sym_ctx.symbol && m_sym_ctx.symbol->GetName() == name)) { return true; } @@ -2120,6 +2156,10 @@ bool RegisterContextUnwind::ReadGPRValue(lldb::RegisterKind register_kind, // if this is frame 0 (currently executing frame), get the requested reg // contents from the actual thread registers if (IsFrameZero()) { + if (m_thread.GetProcess()->GetCJThreadRegContext().size()) { + value = m_thread.GetProcess()->GetCJThreadRegContext()[ConstString(reg_info->name)]; + return true; + } if (m_thread.GetRegisterContext()->ReadRegister(reg_info, reg_value)) { value = reg_value.GetAsUInt64(); return true; diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp index 14663e4b7..6675796b8 100644 --- a/lldb/source/Target/StackFrameList.cpp +++ b/lldb/source/Target/StackFrameList.cpp @@ -755,6 +755,11 @@ StackFrameSP StackFrameList::GetFrameWithStackID(const StackID &stack_id) { frame_sp = GetFrameAtIndex(frame_idx); if (frame_sp && frame_sp->GetStackID() == stack_id) break; + if (frame_sp && frame_sp->GetSymbolContext(eSymbolContextEverything).\ + GetFunctionName(Mangled::ePreferDemangledWithoutArguments).\ + GetStringRef().endswith("::main")) { + break; + } frame_idx++; } while (frame_sp); } diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index f16fc6b5d..52abd534e 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -60,12 +60,15 @@ #include "lldb/Utility/State.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/Timer.h" +#include "Commands/CommandObjCJThreadCommon.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SetVector.h" #include #include +#include +#include using namespace lldb; using namespace lldb_private; @@ -2463,6 +2466,153 @@ Target *Target::GetTargetFromContexts(const ExecutionContext *exe_ctx_ptr, return target; } +static bool TypeMatch(lldb::ValueObjectSP valobj_sp) +{ + ConstString match("(^std[.]core.|^std[.]collection.)(HashSet|HashMap|Option|Range)<.+>$"); + RegularExpression regex(match.GetStringRef()); + if (regex.Execute(valobj_sp->GetDisplayTypeName().AsCString())) { + return true; + } + + return false; +} + +void SplitExpressionMembers(std::string &str, std::queue &res) +{ + std::string members = str; + auto pos = members.find_first_of("]"); + while (pos != std::string::npos) { + res.push(members.substr(0, std::min(pos + 1, members.size()))); + members = members.substr(std::min(pos + 1, members.size())); + pos = members.find_first_of("]"); + } + + if (!members.empty()) { + res.push(members); + } +} + +static lldb::ValueObjectSP FindMemberFromParentClass(lldb::ValueObjectSP &root, std::string member) +{ + if (root->IsCangjieDynamicType()) { + auto parent = root->GetChildAtIndex(0, true); + auto result = parent->GetValueForExpressionPath(member.c_str()); + if (result != nullptr) { + return result; + } + } + + return lldb::ValueObjectSP(); +} + +static lldb::ValueObjectSP GetCjValueForExpressionPath(lldb::ValueObjectSP &root, std::string &expression, + lldb::TargetSP target) +{ + if (root == nullptr) { + return lldb::ValueObjectSP(); + } + + auto max_depth = target->GetMaximumDepthOfChildrenToDisplay(); + std::queue members; + SplitExpressionMembers(expression, members); + lldb::ValueObjectSP result; + uint64_t current_depth = 0; + while (root != nullptr) { + current_depth++; + if (current_depth >= max_depth.first) { + break; + } + + std::string varname = members.front(); + result = root->GetValueForExpressionPath(varname.c_str()); + if (result == nullptr) { + result = FindMemberFromParentClass(root, varname); + } + if (result == nullptr) { + break; + } + members.pop(); + if (members.empty()) { + return result; + } + root = result; + continue; + } + + return lldb::ValueObjectSP(); +} + +static std::string StringTrim(std::string src) { + auto pos = std::min(src.size(), src.find_first_not_of(" \t\n\v\f\r")); + std::string temp = src.substr(pos); + pos = temp.size() - std::min(temp.size(), temp.find_last_not_of(" \t\n\v\f\r") + 1); + std::string result = temp.substr(0, temp.size() - pos); + return result; +} + +static ExpressionResults EvaluateCjExpression(llvm::StringRef &expr, ExecutionContext &exe_ctx, + lldb::ValueObjectSP &result_valobj_sp) { + StackFrame *frame = exe_ctx.GetFramePtr(); + if (!frame) { + return eExpressionParseError; + } + + VariableListSP variable_list = frame->GetInScopeVariableList(true); + if (!variable_list) { + return eExpressionParseError; + } + + std::string expression = expr.data(); + std::string left = expression; + std::string right; + size_t pos = expression.find('='); + if (pos != std::string::npos) { + left = expression.substr(0, pos); + right = StringTrim(expression.substr(pos + 1)); + if (right.empty()) { + return eExpressionParseError; + } + } + + std::string base = StringTrim(left); + std::string member; + pos = left.find_first_of(".["); + if (pos != std::string::npos) { + base = StringTrim(left.substr(0, pos)); + member = StringTrim(left.substr(pos)); + } + + VariableSP var_sp = variable_list->FindVariable(ConstString(base)); + if (!var_sp) { + return eExpressionParseError; + } + + lldb::ValueObjectSP valobj_sp = frame->FindVariable(ConstString(base)); + if (!valobj_sp) { + valobj_sp = frame->GetValueObjectForFrameVariable(var_sp, eNoDynamicValues); + } + + if (valobj_sp && !member.empty()) { + valobj_sp = TypeMatch(valobj_sp) ? + nullptr : GetCjValueForExpressionPath(valobj_sp, member, exe_ctx.GetTargetSP()); + } + + if (!valobj_sp) { + return eExpressionParseError; + } + + if (!right.empty()) { + Status error; + valobj_sp->SetValueFromCString(right.c_str(), error); + if (error.Fail()) { + return eExpressionParseError; + } + } + + result_valobj_sp = valobj_sp->Persist(); + return eExpressionCompleted; +} + ExpressionResults Target::EvaluateExpression( llvm::StringRef expr, ExecutionContextScope *exe_scope, lldb::ValueObjectSP &result_valobj_sp, @@ -2477,6 +2627,13 @@ ExpressionResults Target::EvaluateExpression( return execution_results; } + bool use_cj_expression = false; + llvm::StringRef cangjie_expr_prefix = "[cj] "; + if (expr.startswith(cangjie_expr_prefix)) { + expr = expr.drop_front(cangjie_expr_prefix.size()); + use_cj_expression = true; + } + // We shouldn't run stop hooks in expressions. bool old_suppress_value = m_suppress_stop_hooks; m_suppress_stop_hooks = true; @@ -2513,11 +2670,15 @@ ExpressionResults Target::EvaluateExpression( result_valobj_sp = persistent_var_sp->GetValueObject(); execution_results = eExpressionCompleted; } else { - llvm::StringRef prefix = GetExpressionPrefixContents(); - Status error; - execution_results = UserExpression::Evaluate(exe_ctx, options, expr, prefix, - result_valobj_sp, error, - fixed_expression, ctx_obj); + if (use_cj_expression) { + execution_results = EvaluateCjExpression(expr, exe_ctx, result_valobj_sp); + } else { + llvm::StringRef prefix = GetExpressionPrefixContents(); + Status error; + execution_results = UserExpression::Evaluate(exe_ctx, options, expr, prefix, + result_valobj_sp, error, + fixed_expression, ctx_obj); + } } if (execution_results == eExpressionCompleted) diff --git a/lldb/source/Target/TargetList.cpp b/lldb/source/Target/TargetList.cpp index 829036976..a4d9b372b 100644 --- a/lldb/source/Target/TargetList.cpp +++ b/lldb/source/Target/TargetList.cpp @@ -250,6 +250,13 @@ Status TargetList::CreateTargetInternal(Debugger &debugger, LLDB_SCOPED_TIMERF("TargetList::CreateTarget (file = '%s', arch = '%s')", user_exe_path.str().c_str(), specified_arch.GetArchitectureName()); + + // We set this env to support CJDB coroutine + debugger.SetPropertyValue(NULL, eVarSetOperationAssign, "target.env-vars", "cjProcessorNum=1"); +#if defined(__APPLE__) + debugger.SetPropertyValue(NULL, eVarSetOperationAssign, "target.env-vars", + "LLDB_DEBUGSERVER_EXTRA_ARG_1=--unmask-signals"); +#endif Status error; const bool is_dummy_target = false; diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td index acee09ca0..327dd7487 100644 --- a/lldb/source/Target/TargetProperties.td +++ b/lldb/source/Target/TargetProperties.td @@ -77,7 +77,7 @@ let Definition = "target" in { DefaultUnsignedValue<256>, Desc<"Maximum number of children to expand in any level of depth.">; def MaxChildrenDepth: Property<"max-children-depth", "UInt64">, - DefaultUnsignedValue<0xFFFFFFFF>, + DefaultUnsignedValue<0x05>, Desc<"Maximum depth to expand children.">; def MaxSummaryLength: Property<"max-string-summary-length", "SInt64">, DefaultUnsignedValue<1024>, @@ -271,7 +271,7 @@ let Definition = "thread" in { Desc<"If true, when step-in/step-out/step-over leave the current frame, they will continue to step out till they come to a function with debug information. Passing a frame argument to step-out will override this option.">; def StepAvoidRegex: Property<"step-avoid-regexp", "Regex">, Global, - DefaultStringValue<"^std::">, + DefaultStringValue<"^(compress|crypto|encoding|net|serialization|std)[.][a-z]+::">, Desc<"A regular expression defining functions step-in won't stop in.">; def StepAvoidLibraries: Property<"step-avoid-libraries", "FileSpecList">, Global, diff --git a/lldb/source/Target/ThreadPlan.cpp b/lldb/source/Target/ThreadPlan.cpp index 9913ecb59..378c0d11f 100644 --- a/lldb/source/Target/ThreadPlan.cpp +++ b/lldb/source/Target/ThreadPlan.cpp @@ -4,6 +4,12 @@ // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// //===----------------------------------------------------------------------===// #include "lldb/Target/ThreadPlan.h" @@ -15,6 +21,8 @@ #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/State.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Target/SectionLoadList.h" using namespace lldb; using namespace lldb_private; @@ -287,3 +295,123 @@ lldb::StateType ThreadPlanNull::GetPlanRunState() { #endif return eStateRunning; } + +bool ThreadPlan::CFFIGetValueFromReg(Target &target, StackFrame *frame, + uint64_t *addr, const char *reg) { + if (!frame) { + return false; + } + auto reg_ctx = frame->GetRegisterContext(); + if (!reg_ctx) { + return false; + } + auto reg_info = reg_ctx->GetRegisterInfoByName(reg); + if (!reg_info) { + return false; + } + RegisterValue mem_addr; + if (!reg_ctx->ReadRegister(reg_info, mem_addr)) { + return false; + } + uint64_t value = mem_addr.GetAsUInt64(UINT64_MAX); + if (value == UINT64_MAX) { + return false; + } + *addr = value; + return true; +} + +ThreadPlanSP ThreadPlan::CFFIMakeThreadPlanRunToAddress(Thread &thread, uint64_t cfunc_addr, + bool stop_others) { + typedef std::vector AddressVector; + AddressVector addrs; + addrs.push_back(cfunc_addr); + return std::make_shared(thread, addrs, stop_others); +} + +static void GetCFFIRegNameFromArchitecture(Target &target, std::string &cffi_next, + std::string &cffi_final) { + auto arch_type = target.GetArchitecture().GetTriple().getArch(); + switch (arch_type) { + case llvm::Triple::x86_64: + cffi_next = "rsp"; +#if defined(_WIN32) + cffi_final = "rcx"; +#endif +#if defined(__linux__) || defined(__APPLE__) + cffi_final = "rdi"; +#endif + break; + case llvm::Triple::aarch64: + cffi_next = "x9"; + cffi_final = "x20"; + break; + default: + break; + } +} + +static bool IsCFFIStubFunc(ConstString name) { + return name == "CJ_MCC_C2NStub" || name == "CJ_MCC_N2CStub"; +} + +static bool IsCFFIWarperFunc(ConstString name) { + return name == "wrapper.FCivE"; +} + +static bool GetCFFIFuncAddrFromMemoryAddress(Target &target, uint64_t mem_addr, uint64_t *cfunc_addr) { + Status error; + Address symbol_containing_address; + auto section_load_list = target.GetSectionLoadList(); + target.ReadMemory(Address(mem_addr), cfunc_addr, sizeof(uint64_t), error); + if (error.Fail() || !section_load_list.ResolveLoadAddress(*cfunc_addr, symbol_containing_address)) { + return false; + } + return true; +} + +ThreadPlanSP ThreadPlan::CFFILookForPlanToStepThroughFromCurrentPC() { + Thread &thread = GetThread(); + Target &target = GetTarget(); + auto current_pc = thread.GetRegisterContext()->GetPC(); + auto section_load_list = target.GetSectionLoadList(); + Address symbol_containing_address; + if (!section_load_list.ResolveLoadAddress(current_pc, symbol_containing_address)) { + return nullptr; + } + lldb_private::SymbolContext sym; + symbol_containing_address.ResolveFunctionScope(sym); + ConstString name = sym.GetFunctionName(); + StackFrame *frame = thread.GetStackFrameAtIndex(0).get(); + if (!frame) { + return nullptr; + } + std::string cffi_next; + std::string cffi_final; + uint64_t cfunc_addr = 0; + GetCFFIRegNameFromArchitecture(target, cffi_next, cffi_final); + if (IsCFFIStubFunc(name)) { + uint64_t reg_value = 0; + if (cffi_next.empty() || cffi_final.empty()) { + return nullptr; + } + CFFIGetValueFromReg(target, frame, ®_value, cffi_next.c_str()); + auto arch_type = target.GetArchitecture().GetTriple().getArch(); + if (arch_type == llvm::Triple::x86_64) { + // We are located in cangjie runtime function CJ_MCC_C2NStub or CJ_MCC_N2CStub, + // we read the cfunc from the memory, the memory addr from cffi_next register require offset 0x10. + reg_value += 0x10; + if (!GetCFFIFuncAddrFromMemoryAddress(target, reg_value, &cfunc_addr)) { + return nullptr; + } + return CFFIMakeThreadPlanRunToAddress(thread, cfunc_addr, true); + } else if (arch_type == llvm::Triple::aarch64) { + return CFFIMakeThreadPlanRunToAddress(thread, reg_value, true); + } + } else if (IsCFFIWarperFunc(name)) { + // We are located in func warpper, we direct read the cfunc address from cffi_final register. + CFFIGetValueFromReg(target, frame, &cfunc_addr, cffi_final.c_str()); + return CFFIMakeThreadPlanRunToAddress(thread, cfunc_addr, true); + } + return nullptr; +} diff --git a/lldb/source/Target/ThreadPlanShouldStopHere.cpp b/lldb/source/Target/ThreadPlanShouldStopHere.cpp index e72f8d8f5..6c8f452b9 100644 --- a/lldb/source/Target/ThreadPlanShouldStopHere.cpp +++ b/lldb/source/Target/ThreadPlanShouldStopHere.cpp @@ -138,6 +138,13 @@ ThreadPlanSP ThreadPlanShouldStopHere::DefaultStepFromHereCallback( current_plan->GetThread().QueueThreadPlanForStepOutNoShouldStop( false, nullptr, true, stop_others, eVoteNo, eVoteNoOpinion, frame_index, status, true); + + if (!return_plan_sp && status.Fail()) { + // Since "step out" may fail because a return address breakpoint cannot be inserted, + // we give it a chance to push a "step instruction" plan to get us out of here. + return_plan_sp = current_plan->GetThread().QueueThreadPlanForStepSingleInstruction(true, false, true, status); + } + return return_plan_sp; } diff --git a/lldb/source/Target/ThreadPlanStepThrough.cpp b/lldb/source/Target/ThreadPlanStepThrough.cpp index e15635e40..e83dee947 100644 --- a/lldb/source/Target/ThreadPlanStepThrough.cpp +++ b/lldb/source/Target/ThreadPlanStepThrough.cpp @@ -199,6 +199,9 @@ bool ThreadPlanStepThrough::ShouldStop(Event *event_ptr) { // might chain, for instance stepping through a dylib trampoline to the objc // dispatch function...) LookForPlanToStepThroughFromCurrentPC(); + if (!m_sub_plan_sp) { + m_sub_plan_sp = CFFILookForPlanToStepThroughFromCurrentPC(); + } if (m_sub_plan_sp) { PushPlan(m_sub_plan_sp); return false; diff --git a/lldb/source/Utility/ArchSpec.cpp b/lldb/source/Utility/ArchSpec.cpp index a99aed82b..985166a29 100644 --- a/lldb/source/Utility/ArchSpec.cpp +++ b/lldb/source/Utility/ArchSpec.cpp @@ -958,6 +958,8 @@ static bool IsCompatibleEnvironment(llvm::Triple::EnvironmentType lhs, // that they are using the Android ABI. if ((lhs == llvm::Triple::Android && rhs == llvm::Triple::EABI) || (rhs == llvm::Triple::Android && lhs == llvm::Triple::EABI) || + (lhs == llvm::Triple::OpenHOS && rhs == llvm::Triple::EABI) || + (rhs == llvm::Triple::OpenHOS && lhs == llvm::Triple::EABI) || (lhs == llvm::Triple::GNUEABI && rhs == llvm::Triple::EABI) || (rhs == llvm::Triple::GNUEABI && lhs == llvm::Triple::EABI) || (lhs == llvm::Triple::GNUEABIHF && rhs == llvm::Triple::EABIHF) || diff --git a/lldb/source/Utility/Scalar.cpp b/lldb/source/Utility/Scalar.cpp index 19e00e111..324854824 100644 --- a/lldb/source/Utility/Scalar.cpp +++ b/lldb/source/Utility/Scalar.cpp @@ -675,9 +675,10 @@ Status Scalar::SetValueFromCString(const char *value_str, Encoding encoding, // point type. This function should be refactored to take an explicit // semantics argument. const llvm::fltSemantics &sem = - byte_size <= 4 ? APFloat::IEEEsingle() - : byte_size <= 8 ? APFloat::IEEEdouble() - : APFloat::x87DoubleExtended(); + byte_size <= 2 ? APFloat::IEEEhalf() + : byte_size <= 4 ? APFloat::IEEEsingle() + : byte_size <= 8 ? APFloat::IEEEdouble() + : APFloat::x87DoubleExtended(); APFloat f(sem); if (llvm::Expected op = f.convertFromString(value_str, APFloat::rmNearestTiesToEven)) { diff --git a/lldb/source/Version/Version.cpp b/lldb/source/Version/Version.cpp index b391fe1ea..e042dcdc7 100644 --- a/lldb/source/Version/Version.cpp +++ b/lldb/source/Version/Version.cpp @@ -4,6 +4,12 @@ // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// //===----------------------------------------------------------------------===// #include "lldb/Version/Version.h" @@ -27,32 +33,17 @@ static const char *GetLLDBRevision() { #endif } -static const char *GetLLDBRepository() { -#ifdef LLDB_REPOSITORY - return LLDB_REPOSITORY; -#else - return nullptr; -#endif -} - const char *lldb_private::GetVersion() { static std::string g_version_str; if (g_version_str.empty()) { const char *lldb_version = GetLLDBVersion(); - const char *lldb_repo = GetLLDBRepository(); const char *lldb_rev = GetLLDBRevision(); g_version_str += lldb_version; - if (lldb_repo || lldb_rev) { + if (lldb_rev) { g_version_str += " ("; - if (lldb_repo) - g_version_str += lldb_repo; - if (lldb_repo && lldb_rev) - g_version_str += " "; - if (lldb_rev) { - g_version_str += "revision "; - g_version_str += lldb_rev; - } + g_version_str += "revision "; + g_version_str += lldb_rev; g_version_str += ")"; } @@ -67,6 +58,7 @@ const char *lldb_private::GetVersion() { g_version_str += "\n llvm revision "; g_version_str += llvm_rev; } + g_version_str += "\n cangjie 1.0.7 "; } return g_version_str.c_str(); diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp index 16fa2f139..8d8110c88 100644 --- a/lldb/tools/driver/Driver.cpp +++ b/lldb/tools/driver/Driver.cpp @@ -443,6 +443,7 @@ int Driver::MainLoop() { m_debugger.SetOutputFileHandle(stdout, false); // Don't take ownership of STDIN yet... m_debugger.SetInputFileHandle(stdin, false); + m_debugger.HandleCommand("setting set prompt \"(cjdb) \""); m_debugger.SetUseExternalEditor(m_option_data.m_use_external_editor); diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt index 3d6f5e6f9..66bf02a23 100644 --- a/llvm/CMakeLists.txt +++ b/llvm/CMakeLists.txt @@ -659,7 +659,7 @@ option (LLVM_BUILD_DOCS "Build the llvm documentation." OFF) option (LLVM_INCLUDE_DOCS "Generate build targets for llvm documentation." ON) option (LLVM_ENABLE_DOXYGEN "Use doxygen to generate llvm API documentation." OFF) option (LLVM_ENABLE_SPHINX "Use Sphinx to generate llvm documentation." OFF) -option (LLVM_ENABLE_OCAMLDOC "Build OCaml bindings documentation." ON) +option (LLVM_ENABLE_OCAMLDOC "Build OCaml bindings documentation." OFF) option (LLVM_ENABLE_BINDINGS "Build bindings." ON) set(LLVM_INSTALL_DOXYGEN_HTML_DIR "${CMAKE_INSTALL_DOCDIR}/llvm/doxygen-html" @@ -1060,6 +1060,58 @@ if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "SunOS") add_definitions("-D_FILE_OFFSET_BITS=64") endif() +if (NOT COV_FLAG) + set(COV_FLAG 0 CACHE STRING "cover default is false" FORCE) +endif () +message(STATUS "COV_FLAG : ${COV_FLAG}") + +if (COV_FLAG MATCHES 1) + set(CMAKE_COV_FLAGS "-fprofile-arcs -ftest-coverage -Xclang -coverage-cfg-checksum -Xclang -coverage-no-function-names-in-data -Xclang -coverage-version=A75*") +endif() + +# Used for security compilation options +if (UNIX AND NOT APPLE + AND uppercase_CMAKE_BUILD_TYPE STREQUAL "RELEASE") + set(CMAKE_EXE_LINKER_FLAGS + "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack -Wl,-s") + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack -Wl,-s") + set(CMAKE_MODULE_LINKER_FLAGS + "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack -Wl,-s") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pie -fstack-protector-all -D_FORTIFY_SOURCE=2 -O3") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pie -fstack-protector-all -D_FORTIFY_SOURCE=2 -O3") + if (NOT MINGW) + # MinGW gcc does not support -Qunused-arguments option. + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Qunused-arguments") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments") + endif() +elseif(MINGW AND uppercase_CMAKE_BUILD_TYPE STREQUAL "RELEASE") + set(CMAKE_EXE_LINKER_FLAGS + "${CMAKE_EXE_LINKER_FLAGS} -Wl,-s") + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-s") + set(CMAKE_MODULE_LINKER_FLAGS + "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-s") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-all -D_FORTIFY_SOURCE=2 -O3") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-all -D_FORTIFY_SOURCE=2 -O3") +endif() + +# Add gc-sections +if (UNIX AND NOT APPLE) + set(CMAKE_EXE_LINKER_FLAGS + "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections ${CMAKE_COV_FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections ${CMAKE_COV_FLAGS}") + set(CMAKE_MODULE_LINKER_FLAGS + "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--gc-sections ${CMAKE_COV_FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections ${CMAKE_COV_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections ${CMAKE_COV_FLAGS}") +endif() + +if (COV_FLAG MATCHES 1) + list(APPEND LINK_LIBS "gcov") +endif() + set(CMAKE_INCLUDE_CURRENT_DIR ON) include_directories( ${LLVM_INCLUDE_DIR} ${LLVM_MAIN_INCLUDE_DIR}) diff --git a/llvm/cmake/modules/HandleLLVMOptions.cmake b/llvm/cmake/modules/HandleLLVMOptions.cmake index 56d05f5b5..2603ed1fb 100644 --- a/llvm/cmake/modules/HandleLLVMOptions.cmake +++ b/llvm/cmake/modules/HandleLLVMOptions.cmake @@ -694,7 +694,7 @@ if (LLVM_ENABLE_WARNINGS AND (LLVM_COMPILER_IS_GCC_COMPATIBLE OR CLANG_CL)) append("-Wall" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) endif() - append("-Wextra -Wno-unused-parameter -Wwrite-strings" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + append("-Wextra -Wno-unused-parameter -Wwrite-strings -Wfloat-equal" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) append("-Wcast-qual" CMAKE_CXX_FLAGS) # Turn off missing field initializer warnings for gcc to avoid noise from diff --git a/llvm/cmake/modules/LLVMProcessSources.cmake b/llvm/cmake/modules/LLVMProcessSources.cmake index ba8dca313..c99e6baaa 100644 --- a/llvm/cmake/modules/LLVMProcessSources.cmake +++ b/llvm/cmake/modules/LLVMProcessSources.cmake @@ -111,8 +111,8 @@ function(llvm_check_source_file_list) else() set(fn_relative "${fn}") endif() - message(SEND_ERROR "Found unknown source file ${fn_relative} -Please update ${CMAKE_CURRENT_LIST_FILE}\n") +# message(SEND_ERROR "Found unknown source file ${fn_relative} +#Please update ${CMAKE_CURRENT_LIST_FILE}\n") endif() endif() endif() diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index bc1f63f27..3ab4272e0 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -2614,6 +2614,19 @@ When lowered, any relocated value will be recorded in the corresponding :ref:`stackmap entry `. See the intrinsic description for further details. +.. _ob_struct_live: + +Struct Live Operand Bundles +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A "struct-live" operand bundle is only valid on a :ref:`gc.statepoint ` +intrinsic. The operand bundle must contain struct pointers of gcptr that needs copy +object which potentially needs to be upadted by the runtime. + +When lowered, any relocated value will be recorded in the corresponding +:ref:`stackmap entry `. See the intrinsic description +for further details. + ObjC ARC Attached Call Operand Bundles ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/llvm/docs/StackMaps.rst b/llvm/docs/StackMaps.rst index 783336e2d..a4c205d4e 100644 --- a/llvm/docs/StackMaps.rst +++ b/llvm/docs/StackMaps.rst @@ -514,4 +514,4 @@ Supported Architectures Support for StackMap generation and the related intrinsics requires some code for each backend. Today, only a subset of LLVM's backends are supported. The currently supported architectures are X86_64, -PowerPC, Aarch64 and SystemZ. +PowerPC, AArch64 and SystemZ. diff --git a/llvm/docs/Statepoints.rst b/llvm/docs/Statepoints.rst index 25f0a093c..984935623 100644 --- a/llvm/docs/Statepoints.rst +++ b/llvm/docs/Statepoints.rst @@ -739,7 +739,7 @@ Supported Architectures ======================= Support for statepoint generation requires some code for each backend. -Today, only Aarch64 and X86_64 are supported. +Today, only AArch64 and X86_64 are supported. .. _OpenWork: diff --git a/llvm/include/llvm/ADT/StringRef.h b/llvm/include/llvm/ADT/StringRef.h index 80ba47dd6..8c2c3bc27 100644 --- a/llvm/include/llvm/ADT/StringRef.h +++ b/llvm/include/llvm/ADT/StringRef.h @@ -901,6 +901,38 @@ namespace llvm { return "\n\r"; // You monster! return "\r"; // Classic Mac } + + bool isCJMutexLock() const { return equals("CJ_MCC_MutexLock"); } + + bool isCangjieNewObjFunction() const { + return equals("CJ_MCC_NewObject") || equals("CJ_MCC_NewFinalizer"); + } + + bool isCangjieTypeInfo() const { return startswith("TypeInfo"); } + + bool isCangjieArrayLayout() const { return startswith("ArrayLayout"); } + + bool isCangjieArrayRecord() const { + return startswith("record.std.core:Array"); + } + + bool isCJStackCheck() const { return equals("CJ_MCC_StackCheck"); } + + bool isGetGCPhase() const { return equals("GetGCPhase"); } + + bool isSetDebugLocation() const { return equals("SetDebugLocation"); } + + bool isExtendFromFP16Func() const { + return equals("__extendhfsf2") || equals("__extendhfdf2") || + equals("__fixhfsi") || equals("__fixhfdi") || + equals("__fixunshfsi") || equals("__fixunshfdi"); + } + + bool isTruncToFP16Func() const { + return equals("__truncsfhf2") || equals("__truncdfhf2") || + equals("__floatsihf") || equals("__floatdihf") || + equals("__floatunsihf") || equals("__floatundihf"); + } /// @} }; diff --git a/llvm/include/llvm/ADT/Triple.h b/llvm/include/llvm/ADT/Triple.h index ba4584dc6..2f778e27a 100644 --- a/llvm/include/llvm/ADT/Triple.h +++ b/llvm/include/llvm/ADT/Triple.h @@ -176,6 +176,7 @@ public: Mesa, SUSE, OpenEmbedded, + Harmony, LastVendorType = OpenEmbedded }; enum OSType { @@ -238,6 +239,7 @@ public: Musl, MuslEABI, MuslEABIHF, + OpenHOS, MuslX32, MSVC, @@ -680,6 +682,12 @@ public: return getObjectFormat() == Triple::XCOFF; } + /// Tests whether the target is the webm platform. + bool isWebm() const { + return getArch() == Triple::aarch64 && + getVendor() == Triple::Harmony; + } + /// Tests whether the target is the PS4 platform. bool isPS4() const { return getArch() == Triple::x86_64 && @@ -700,6 +708,8 @@ public: /// Tests whether the target is Android bool isAndroid() const { return getEnvironment() == Triple::Android; } + bool isOpenHOS() const { return getEnvironment() == Triple::OpenHOS; } + bool isAndroidVersionLT(unsigned Major) const { assert(isAndroid() && "Not an Android triple!"); @@ -919,7 +929,7 @@ public: /// Tests whether the target uses emulated TLS as default. bool hasDefaultEmulatedTLS() const { - return isAndroid() || isOSOpenBSD() || isWindowsCygwinEnvironment(); + return isOpenHOS() || isAndroid() || isOSOpenBSD() || isWindowsCygwinEnvironment(); } /// Tests whether the target uses -data-sections as default. diff --git a/llvm/include/llvm/Analysis/AliasSetTracker.h b/llvm/include/llvm/Analysis/AliasSetTracker.h index 78f5545ab..3a1943ea4 100644 --- a/llvm/include/llvm/Analysis/AliasSetTracker.h +++ b/llvm/include/llvm/Analysis/AliasSetTracker.h @@ -180,6 +180,8 @@ class AliasSet : public ilist_node { unsigned SetSize = 0; + Value *BasePtr = nullptr; + void addRef() { ++RefCount; } void dropRef(AliasSetTracker &AST) { @@ -210,6 +212,10 @@ public: /// Merge the specified alias set into this alias set. void mergeSetIn(AliasSet &AS, AliasSetTracker &AST); + Value *getBasePtr() const { return BasePtr; } + void setBasePtr(Value *Ptr) { BasePtr = Ptr; } + void clearBasePtr() { BasePtr = nullptr; } + // Alias Set iteration - Allow access to all of the pointers which are part of // this alias set. class iterator; diff --git a/llvm/include/llvm/Analysis/CFGPrinter.h b/llvm/include/llvm/Analysis/CFGPrinter.h index 768cda59c..bad2c438d 100644 --- a/llvm/include/llvm/Analysis/CFGPrinter.h +++ b/llvm/include/llvm/Analysis/CFGPrinter.h @@ -18,17 +18,138 @@ #ifndef LLVM_ANALYSIS_CFGPRINTER_H #define LLVM_ANALYSIS_CFGPRINTER_H +#include "llvm/ADT/SetVector.h" #include "llvm/Analysis/BlockFrequencyInfo.h" #include "llvm/Analysis/BranchProbabilityInfo.h" #include "llvm/Analysis/HeatUtils.h" #include "llvm/IR/CFG.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" +#include "llvm/IR/InstIterator.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/PassManager.h" +#include "llvm/IR/Statepoint.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/DOTGraphTraits.h" #include "llvm/Support/FormatVariadic.h" +using namespace llvm; + +static bool IsCJCFG = false; +extern cl::opt CJCFGARG; +extern cl::opt IfCJCFGOnly; +extern cl::opt IfCJCFGComplex; +extern cl::opt CJCFGCol; + +namespace { +class CJDFXOperate { +public: + void run(const Function &F) { + setCJCFG(); + fillMap(F); + if (!CJCFGARG.empty()) { + fillArgSet(F); + } + } + + DenseMap> FlowMap; + DenseMap DerivedBase; + SetVector ArgSet; + +private: + void setCJCFG() { IsCJCFG = true; } + + void fillArgSet(const Function &F) { + for (auto &I : instructions(F)) { + if (I.getName().equals(CJCFGARG)) { + ArgSet.insert(&I); + break; + } + } + + if (ArgSet.empty()) { + return; + } + + for (auto Flow : FlowMap) { + for (auto Data : Flow.second) { + if (Data == ArgSet[0] || Data == ArgSet[0]->stripPointerCasts()) { + ArgSet.set_union(Flow.second); + break; + } + } + } + } + + void getPhiOrigin(PHINode *Phi, SetVector &Cache) { + Cache.insert(Phi); + for (auto &Val : Phi->incoming_values()) { + if (Cache.count(Val->stripPointerCasts()) == 0) { + if (auto Reloc = dyn_cast(Val->stripPointerCasts())) { + getRelocateOrigin(Reloc, Cache); + } else if (auto PhiNew = dyn_cast(Val->stripPointerCasts())) { + getPhiOrigin(PhiNew, Cache); + } else { + Cache.insert(Val->stripPointerCasts()); + } + } + } + } + + void getRelocateOrigin(const GCRelocateInst *Reloc, + SetVector &Cache) { + Cache.insert(Reloc); + auto Statepoint = cast(Reloc->getStatepoint()); + auto Derived = dyn_cast( + *(Statepoint->gc_args_begin() + Reloc->getDerivedPtrIndex())); + auto Base = dyn_cast( + *(Statepoint->gc_args_begin() + Reloc->getBasePtrIndex())); + DerivedBase[Derived] = Base; + if (auto RelocOld = + dyn_cast(Derived->stripPointerCasts())) { + getRelocateOrigin(RelocOld, Cache); + } else if (auto Phi = dyn_cast(Derived->stripPointerCasts())) { + getPhiOrigin(Phi, Cache); + } else { + Cache.insert(Derived->stripPointerCasts()); + } + } + + void fillMap(const Function &F) { + auto isFind = [&](const Value *Val) { + for (auto Flow : FlowMap) { + if (Flow.second.count(Val) > 0) { + return true; + } + } + return false; + }; + + for (auto &I : reverse(instructions(F))) { + if (auto Reloc = dyn_cast(&I)) { + if (isFind(Reloc)) { + continue; + } + SetVector Cache; + getRelocateOrigin(Reloc, Cache); + bool Changed = false; + for (auto Flow : FlowMap) { + auto Temp = Cache; + Temp.set_subtract(Flow.second); + if (Temp.size() < Cache.size()) { + FlowMap[Flow.first].set_union(Temp); + Changed = true; + } + } + if (!Changed) { + FlowMap[&I] = Cache; + } + } + } + } +}; +} // end anonymous namespace + namespace llvm { template struct GraphTraits; class CFGViewerPass : public PassInfoMixin { @@ -51,6 +172,16 @@ public: PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); }; +class CJCFGViewerPass : public PassInfoMixin { +public: + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + +class CJCFGPrinterPass : public PassInfoMixin { +public: + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + class DOTFuncInfo { private: const Function *F; @@ -131,6 +262,162 @@ struct DOTGraphTraits : public DefaultDOTGraphTraits { return "CFG for '" + CFGInfo->getFunction()->getName().str() + "' function"; } + static const Value *findOrigin(const Value *Live, CJDFXOperate &CJDFXOp) { + for (auto Flow : CJDFXOp.FlowMap) { + if (Flow.second.count(Live->stripPointerCasts()) > 0) { + return Flow.first; + } + } + return Live; + } + + static void printBase(raw_string_ostream &OS, const Value *Derived, + CJDFXOperate &CJDFXOp) { + auto Itr = CJDFXOp.DerivedBase.find(Derived); + if (Itr != CJDFXOp.DerivedBase.end() && Itr->second != Derived) { + OS << "[ %" << CJDFXOp.DerivedBase[Derived]->getName() << " ]"; + } else { + OS << "[ nullptr ]"; + } + } + + // print: gc-live[ base ]( origin1 origin2 ... ), phi1, phi2, ... + static void printLive(raw_string_ostream &OS, + DenseMap &LiveOriginMap, + bool IsRef, CJDFXOperate &CJDFXOp) { + if (LiveOriginMap.size() == 0) { + return; + } + if (IsRef) { + OS << "ref:\n"; + } else { + OS << "struct:\n"; + } + for (auto LiveOrigin : LiveOriginMap) { + const Value *Live = LiveOrigin.first; + const Value *Origin = LiveOrigin.second; + OS << "%" << Live->getName(); + printBase(OS, Live, CJDFXOp); + bool Changed = false; + OS << "("; + for (auto Flow : CJDFXOp.FlowMap[Origin]) { + if (isa(Flow) || isa(Flow) || + isa(Flow) || isa(Flow) || + isa(Flow)) { + OS << " %" << Flow->getName(); + Changed = true; + } + } + if (!Changed) { + OS << " %" << Live->stripPointerCasts()->getName(); + } + OS << " )"; + for (auto Flow : CJDFXOp.FlowMap[Origin]) { + if (isa(Flow)) { + OS << ", %" << Flow->getName(); + } + } + OS << "\n"; + } + } + + static void addCJDFXLabel(const BasicBlock *Node, raw_string_ostream &OS, + CJDFXOperate &CJDFXOp) { + auto isStructTy = [&](Type *Ty) { + if (auto PT = dyn_cast(Ty)) { + return Ty->getNonOpaquePointerElementType()->isStructTy(); + } + return Ty->isStructTy(); + }; + + auto handleGCArg = [&](raw_string_ostream &OS, const GCStatepointInst *Call, + bool IsRef) { + DenseMap LiveOriginMap; + for (auto &GCArg : Call->gc_args()) { + const Value *Live = dyn_cast(&GCArg); + const Value *Origin = findOrigin(Live, CJDFXOp); + if ((IsRef ^ isStructTy(Live->getType()))) { + LiveOriginMap.insert({Live, Origin}); + } + } + printLive(OS, LiveOriginMap, IsRef, CJDFXOp); + }; + + for (auto &I : *Node) { + if (auto Call = dyn_cast(&I)) { + // print: statepoint_tokenxxx( calleefunc ): + OS << Call->getName(); + if (auto CalledFunc = Call->getActualCalledFunction()) { + OS << "( " << CalledFunc->getName() << " ):\n"; + } else { + // 3: ActualCallee + OS << "( " << Call->getOperand(3)->getName() << " ):\n"; + } + handleGCArg(OS, Call, true); + handleGCArg(OS, Call, false); + } + } + } + + // Print base and derived for relocated + static void printReloc(raw_string_ostream &OS, + const GCRelocateInst *GCReloc) { + OS << "(%"; + auto Base = dyn_cast( + *(cast(GCReloc->getStatepoint())->gc_args_begin() + + GCReloc->getBasePtrIndex())); + auto Derived = dyn_cast( + *(cast(GCReloc->getStatepoint())->gc_args_begin() + + GCReloc->getDerivedPtrIndex())); + OS << Base->getName() << ", %"; + OS << Derived->getName() << ")\n"; + } + + static void addCJDFXArgLabel(const BasicBlock *Node, raw_string_ostream &OS, + CJDFXOperate &CJDFXOp) { + auto setFind = [&](const Value *Val) { + return CJDFXOp.ArgSet.count(Val) > 0 || + CJDFXOp.ArgSet.count(Val->stripPointerCasts()) > 0; + }; + + auto argUse = [&](const Instruction *I) { + for (auto &Arg : I->operands()) { + if (setFind(dyn_cast(&Arg))) { + return true; + } + } + return false; + }; + + if (CJDFXOp.ArgSet.empty()) { + return; + } + SetVector DataSet; + for (auto &I : *Node) { + const Value *Val = &I; + if (!IfCJCFGComplex) { + if (setFind(Val)) { + DataSet.insert(Val); + } + } else { + if (setFind(Val) || argUse(&I)) { + DataSet.insert(Val); + CJDFXOp.ArgSet.insert(Val); + } + } + } + if (!DataSet.empty()) { + OS << "\nData flow of "; + OS << CJCFGARG << ":\n"; + } + for (auto Data : DataSet) { + OS << *Data << "\n"; + if (auto GCReloc = dyn_cast(Data)) { + printReloc(OS, GCReloc); + } + } + } + static std::string getSimpleNodeLabel(const BasicBlock *Node, DOTFuncInfo *) { if (!Node->getName().empty()) return Node->getName().str(); @@ -163,7 +450,20 @@ struct DOTGraphTraits : public DefaultDOTGraphTraits { OS << ":"; } - HandleBasicBlock(OS, *Node); + CJDFXOperate CJDFXOp; + CJDFXOp.run(*(Node->getParent())); + if (IsCJCFG && IfCJCFGOnly) { + OS << Node->getName() << ":\n"; + } else { + HandleBasicBlock(OS, *Node); + } + if (IsCJCFG) { + if (CJCFGARG.empty()) { + addCJDFXLabel(Node, OS, CJDFXOp); + } else { + addCJDFXArgLabel(Node, OS, CJDFXOp); + } + } std::string OutStr = OS.str(); if (OutStr[0] == '\n') OutStr.erase(OutStr.begin()); @@ -171,6 +471,7 @@ struct DOTGraphTraits : public DefaultDOTGraphTraits { // Process string output to make it nicer... unsigned ColNum = 0; unsigned LastSpace = 0; + unsigned MaxCol = IsCJCFG ? CJCFGCol : MaxColumns; for (unsigned i = 0; i != OutStr.length(); ++i) { if (OutStr[i] == '\n') { // Left justify OutStr[i] = '\\'; @@ -180,7 +481,7 @@ struct DOTGraphTraits : public DefaultDOTGraphTraits { } else if (OutStr[i] == ';') { // Delete comments! unsigned Idx = OutStr.find('\n', i + 1); // Find end of line HandleComment(OutStr, i, Idx); - } else if (ColNum == MaxColumns) { // Wrap lines. + } else if (ColNum == MaxCol) { // Wrap lines. // Wrap very long names even though we can't find a space. if (!LastSpace) LastSpace = i; @@ -197,7 +498,6 @@ struct DOTGraphTraits : public DefaultDOTGraphTraits { } std::string getNodeLabel(const BasicBlock *Node, DOTFuncInfo *CFGInfo) { - if (isSimple()) return getSimpleNodeLabel(Node, CFGInfo); else @@ -304,6 +604,8 @@ namespace llvm { class FunctionPass; FunctionPass *createCFGPrinterLegacyPassPass(); FunctionPass *createCFGOnlyPrinterLegacyPassPass(); +FunctionPass *createCJCFGViewerLegacyPassPass(); +FunctionPass *createCJCFGPrinterLegacyPassPass(); } // End llvm namespace #endif diff --git a/llvm/include/llvm/Analysis/CJAliasAnalysis.h b/llvm/include/llvm/Analysis/CJAliasAnalysis.h new file mode 100644 index 000000000..54f7715af --- /dev/null +++ b/llvm/include/llvm/Analysis/CJAliasAnalysis.h @@ -0,0 +1,78 @@ +//===- CJAliasAnalysis.h - --------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file declares a simple AliasAnalysis using special knowledge +// of Cangjie to enhance other optimization passes which rely on the Alias +// Analysis infrastructure. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ANALYSIS_CJALIASANALYSIS_H +#define LLVM_ANALYSIS_CJALIASANALYSIS_H + +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/MemoryLocation.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Pass.h" +#include + +namespace llvm { + +class CJAAResult : public AAResultBase { + friend AAResultBase; + const DataLayout &DL; + const TargetLibraryInfo &TLI; + const DominatorTree *DT; + +public: + explicit CJAAResult(const DataLayout &DL, const TargetLibraryInfo &TLI, + const DominatorTree *DT) + : DL(DL), TLI(TLI), DT(DT) {} + CJAAResult(CJAAResult &&Arg) + : AAResultBase(std::move(Arg)), DL(Arg.DL), TLI(Arg.TLI), DT(Arg.DT) {} + + AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB, + AAQueryInfo &AAQI); + + using AAResultBase::getModRefInfo; + ModRefInfo getModRefInfo(const CallBase *Call, const MemoryLocation &Loc, + AAQueryInfo &AAQI); + bool invalidate(Function &, const PreservedAnalyses &, + FunctionAnalysisManager::Invalidator &); +}; + +class CangjieAA : public AnalysisInfoMixin { + friend AnalysisInfoMixin; + static AnalysisKey Key; + +public: + using Result = CJAAResult; + CJAAResult run(Function &F, FunctionAnalysisManager &AM); +}; + +class CangjieAAWrapperPass : public FunctionPass { + std::unique_ptr Result; + +public: + static char ID; + CangjieAAWrapperPass(); + CJAAResult &getResult() { return *Result; } + const CJAAResult &getResult() const { return *Result; } + bool runOnFunction(Function &F) override; + void getAnalysisUsage(AnalysisUsage &AU) const override; +}; + +FunctionPass *createCangjieAAWrapperPass(); + +} // namespace llvm + +#endif \ No newline at end of file diff --git a/llvm/include/llvm/Analysis/InlineCost.h b/llvm/include/llvm/Analysis/InlineCost.h index 756f1fb61..82bdb975c 100644 --- a/llvm/include/llvm/Analysis/InlineCost.h +++ b/llvm/include/llvm/Analysis/InlineCost.h @@ -246,6 +246,8 @@ InlineParams getInlineParams(unsigned OptLevel, unsigned SizeOptLevel); /// and the call/return instruction. int getCallsiteCost(CallBase &Call, const DataLayout &DL); +bool checkCJStackMapThreshold(CallBase &Call, Function &F); + /// Get an InlineCost object representing the cost of inlining this /// callsite. /// diff --git a/llvm/include/llvm/Analysis/LoopAccessAnalysis.h b/llvm/include/llvm/Analysis/LoopAccessAnalysis.h index af8e8d222..71a90f476 100644 --- a/llvm/include/llvm/Analysis/LoopAccessAnalysis.h +++ b/llvm/include/llvm/Analysis/LoopAccessAnalysis.h @@ -172,12 +172,8 @@ public: : PSE(PSE), InnermostLoop(L) {} /// Register the location (instructions are given increasing numbers) - /// of a write access. - void addAccess(StoreInst *SI); - - /// Register the location (instructions are given increasing numbers) - /// of a write access. - void addAccess(LoadInst *LI); + /// of a read or write access. + void addAccess(Instruction *I); /// Check whether the dependencies between the accesses are safe. /// @@ -626,7 +622,7 @@ public: } /// Return the list of stores to invariant addresses. - const ArrayRef getStoresToInvariantAddresses() const { + const ArrayRef getStoresToInvariantAddresses() const { return StoresToInvariantAddresses; } @@ -690,7 +686,7 @@ private: bool HasDependenceInvolvingLoopInvariantAddress = false; /// List of stores to invariant addresses. - SmallVector StoresToInvariantAddresses; + SmallVector StoresToInvariantAddresses; /// The diagnostics report generated for the analysis. E.g. why we /// couldn't analyze the loop. @@ -763,6 +759,8 @@ bool sortPtrAccesses(ArrayRef VL, Type *ElemTy, const DataLayout &DL, bool isConsecutiveAccess(Value *A, Value *B, const DataLayout &DL, ScalarEvolution &SE, bool CheckType = true); +bool isVectorizableCJGCWrite(Instruction *I); + /// This analysis provides dependence information for the memory accesses /// of a loop. /// diff --git a/llvm/include/llvm/Analysis/LoopInfo.h b/llvm/include/llvm/Analysis/LoopInfo.h index 5a4f8f143..66368d90f 100644 --- a/llvm/include/llvm/Analysis/LoopInfo.h +++ b/llvm/include/llvm/Analysis/LoopInfo.h @@ -545,6 +545,8 @@ extern template class LoopBase; /// in the CFG are necessarily loops. class LLVM_EXTERNAL_VISIBILITY Loop : public LoopBase { public: + bool IsInVectorizedProcess = false; + bool HasGCWriteInLoop = false; /// A range representing the start and end location of a loop. class LocRange { DebugLoc Start; diff --git a/llvm/include/llvm/Analysis/MemoryLocation.h b/llvm/include/llvm/Analysis/MemoryLocation.h index dfac49445..ea1d34e97 100644 --- a/llvm/include/llvm/Analysis/MemoryLocation.h +++ b/llvm/include/llvm/Analysis/MemoryLocation.h @@ -17,6 +17,7 @@ #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/Optional.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Metadata.h" #include "llvm/Support/TypeSize.h" @@ -236,6 +237,7 @@ public: /// instruction. static MemoryLocation get(const LoadInst *LI); static MemoryLocation get(const StoreInst *SI); + static MemoryLocation get(const CallBase *CI); static MemoryLocation get(const VAArgInst *VI); static MemoryLocation get(const AtomicCmpXchgInst *CXI); static MemoryLocation get(const AtomicRMWInst *RMWI); @@ -248,6 +250,7 @@ public: static MemoryLocation getForSource(const MemTransferInst *MTI); static MemoryLocation getForSource(const AtomicMemTransferInst *MTI); static MemoryLocation getForSource(const AnyMemTransferInst *MTI); + static Optional getCJMemTransferSource(const IntrinsicInst *II); /// Return a location representing the destination of a memory set or /// transfer. diff --git a/llvm/include/llvm/Analysis/ObfConfigAnalysis.h b/llvm/include/llvm/Analysis/ObfConfigAnalysis.h new file mode 100644 index 000000000..180cd6620 --- /dev/null +++ b/llvm/include/llvm/Analysis/ObfConfigAnalysis.h @@ -0,0 +1,174 @@ +//===------------------------- ObfConfigAnalysis.h ------------------------===// +// +// 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_TRANSFORMS_OBFUSCATOR_CONFIGURATION_H +#define LLVM_TRANSFORMS_OBFUSCATOR_CONFIGURATION_H +#include "llvm/IR/GlobalVariable.h" +#include "llvm/Pass.h" +#include "llvm/Support/raw_ostream.h" +#include + +namespace llvm { + +class CJSymbol { +public: + CJSymbol(); + ~CJSymbol(); + + enum CJSymbolType { + TYPE, + SYMBOL, + CLOSURE, + }; + + class CJField { + public: + std::string Name; + bool HasGeneric = false; + bool HasParameter = false; + std::vector GenericTypes; + std::vector Parameters; + }; + CJSymbolType SymbolType; + std::string Package; + std::vector Fields; + std::shared_ptr ReturnType; + + static CJSymbol get(const std::string &Symbol, const std::string &Package, CJSymbolType SymbolType); + void print(std::string Indent); + bool isWildCardMatchMultiParam(void) const; + bool isWildCardMatchAnyParam(void) const; + static bool hasSeparators(const std::string &String); + static const char FieldSeparator = '.'; + static const char ParamSeparator = ','; + static const inline std::string ParamBrackets = "()"; + static const inline std::string IdentSeparator = "::"; + static const inline std::string GenericBrackets = "<>"; + static const inline std::vector OperatorNames = { + "<=", + "<<", + "<", + ">=", + ">>", + ">", + "()", + }; +private: + bool parseField(const std::string &String, size_t Start, size_t End, CJField &Name); + bool parseFields(const std::string &String, size_t Start, size_t End); + bool parseParams(const std::string &String, size_t Start, size_t End, std::vector &Params); + bool parseClosure(const std::string &String); + bool parseSymbol(const std::string &String); + std::string parseOperatorName(const std::string &String, size_t Start, size_t End); + + static bool isLeftParamBracket(const std::string &String, size_t Index, size_t Start, size_t End) { + return Index >= Start && Index < End && String[Index] == ParamBrackets[0]; + }; + static bool isRightParamBracket(const std::string &String, size_t Index, size_t Start, size_t End) { + return Index >= Start && Index < End && String[Index] == ParamBrackets[1]; + }; + static bool isLeftGenericBracket(const std::string &String, size_t Index, + size_t Start, size_t End, bool CheckOperator = true) { + static const std::vector AvoidNextChars = { + '<', // operator "<<" + '=', // operator "<=" + '.', // operator "." + }; + if (!(Index >= Start && Index < End && String[Index] == GenericBrackets[0])) { + return false; + } + if (Index == (End - 1)) { + return true; + } + if (CheckOperator && Index == Start && String[Index + 1] == '(') { + // It is operator '<' + return false; + } + return find(AvoidNextChars.begin(), AvoidNextChars.end(), String[Index + 1]) == AvoidNextChars.end(); + }; + static bool isRightGenericBracket(const std::string &String, size_t Index, size_t Start, size_t End) { + return (Index >= Start && Index < End && String[Index] == GenericBrackets[1]) && + !isReturnBracket(String, Index, Start, End); + }; + static bool isReturnBracket(const std::string &String, size_t Index, size_t Start, size_t End) { + return Index > Start && Index < End && String[Index - 1] == '-' && String[Index] == GenericBrackets[1]; + } +}; + +class Rule { +public: + std::string Owner; + CJSymbol Symbol; + Rule(); + ~Rule(); + static Rule get(const std::string &Line); + Rule(std::string Owner, CJSymbol CJSym); + + std::string getAction(); + bool isMatch(const CJSymbol &CJSym); + std::string RawRule; + +private: + bool parseOwner(const std::string &Str); +}; + +class ObfConfig { +public: + ObfConfig(); + ~ObfConfig(); + void addRule(Rule &R); + static std::string hash(const StringRef &Input, bool ToAscii); + unsigned int getRandNum(); + static bool isValidMangleName(const std::string &Name); + static std::string getDemangleName(const std::string &MN); + static std::string getPackageName(const std::string &MN); + bool needObfuse(const std::string &N, const std::string &Owner); + unsigned int getObfLevel(); + unsigned int getMaxObfLevel(); + +private: + std::map> RuleMap; + void loadConfig(const std::string &ConfigPath); + unsigned int RandSeed; +}; + +class Matcher { +public: + static bool isSymbolMatch(const CJSymbol &Temp, const CJSymbol &Sym, bool MatchGeneric); + static bool isObfFuncMatch(const std::string &Temp, const std::string &Sym); +private: + struct MultiMatchPos { + size_t LastTIndex; + size_t LastSIndex; + size_t LastTOffset; + size_t LastSOffset; + unsigned int WType; + }; + static bool isParamMatch(const CJSymbol::CJField &TSym, const CJSymbol::CJField &SSym, bool MatchGeneric); + static bool isCJFieldsMatch( + const std::vector &Temp, const std::vector &Sym, bool MatchGeneric); + static unsigned int getWildcardType(const std::string &Name, size_t &Index); + static unsigned int getCJFieldWildcardType(const CJSymbol::CJField &Field, size_t &Index); +}; + +class ObfConfigAnalysis : public AnalysisInfoMixin { +public: + using Result = std::shared_ptr; + ObfConfigAnalysis(){}; + ~ObfConfigAnalysis(){}; + + std::shared_ptr run(Module &M, ModuleAnalysisManager &AM); + +private: + static AnalysisKey Key; + friend AnalysisInfoMixin; + std::shared_ptr ObfConfigInstance; +}; +} // namespace llvm + +#endif // LLVM_TRANSFORMS_OBFUSCATOR_CONFIGURATION_H diff --git a/llvm/include/llvm/Analysis/TargetTransformInfo.h b/llvm/include/llvm/Analysis/TargetTransformInfo.h index 102b069ac..264092e3e 100644 --- a/llvm/include/llvm/Analysis/TargetTransformInfo.h +++ b/llvm/include/llvm/Analysis/TargetTransformInfo.h @@ -877,7 +877,7 @@ public: /// Return the expected cost for the given integer when optimising /// for size. This is different than the other integer immediate cost /// functions in that it is subtarget agnostic. This is useful when you e.g. - /// target one ISA such as Aarch32 but smaller encodings could be possible + /// target one ISA such as AArch32 but smaller encodings could be possible /// with another such as Thumb. This return value is used as a penalty when /// the total costs for a constant is calculated (the bigger the cost, the /// more beneficial constant hoisting is). diff --git a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h index da1f53aa3..26a6f0413 100644 --- a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h +++ b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h @@ -640,6 +640,8 @@ public: case Intrinsic::var_annotation: case Intrinsic::experimental_gc_result: case Intrinsic::experimental_gc_relocate: + case Intrinsic::cj_gc_result: + case Intrinsic::cj_gc_relocate: case Intrinsic::coro_alloc: case Intrinsic::coro_begin: case Intrinsic::coro_free: diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h index 04235f0fd..0bec7fbdb 100644 --- a/llvm/include/llvm/AsmParser/LLToken.h +++ b/llvm/include/llvm/AsmParser/LLToken.h @@ -171,6 +171,7 @@ enum Kind { kw_amdgpu_kernel, kw_amdgpu_gfx, kw_tailcc, + kw_cangjiegccc, // Attributes: kw_attributes, diff --git a/llvm/include/llvm/BinaryFormat/MachO.h b/llvm/include/llvm/BinaryFormat/MachO.h index c05e79333..86021af70 100644 --- a/llvm/include/llvm/BinaryFormat/MachO.h +++ b/llvm/include/llvm/BinaryFormat/MachO.h @@ -106,6 +106,7 @@ enum : uint32_t { SG_FVMLIB = 0x2u, SG_NORELOC = 0x4u, SG_PROTECTED_VERSION_1 = 0x8u, + SG_READ_ONLY = 0x10u, // Constant masks for the "flags" field in llvm::MachO::section and // llvm::MachO::section_64 diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h index eee4c50cc..d3dc49a5a 100644 --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -689,6 +689,7 @@ enum AttributeKindCodes { ATTR_KIND_ALLOC_KIND = 82, ATTR_KIND_PRESPLIT_COROUTINE = 83, ATTR_KIND_FNRETTHUNK_EXTERN = 84, + ATTR_KIND_CJ_STACK_POINTER = 85, }; enum ComdatSelectionKindCodes { diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h index 5e900e916..17d033867 100644 --- a/llvm/include/llvm/CodeGen/AsmPrinter.h +++ b/llvm/include/llvm/CodeGen/AsmPrinter.h @@ -33,6 +33,7 @@ namespace llvm { class AddrLabelMap; class BasicBlock; class BlockAddress; +class CJMetadataInfo; class Constant; class ConstantArray; class DataLayout; @@ -194,6 +195,9 @@ private: protected: MCSymbol *CurrentFnBegin = nullptr; + std::map> StackCheckMap; + SmallVector> SafepointStackMap; + SmallVector> CangjieStubMap; /// A vector of all debug/EH info emitters we should use. This vector /// maintains ownership of the emitters. std::vector Handlers; @@ -229,6 +233,33 @@ private: protected: explicit AsmPrinter(TargetMachine &TM, std::unique_ptr Streamer); + void emitCangjieStackCheck(const MachineInstr &MI); + virtual bool tryEmitCangjieSpecificCall(const MachineInstr *MI); + virtual bool tryEmitCangjieSpecificCallByMOSym(const MachineInstr *MI, + const MachineOperand &MOSym, + unsigned Opcode); + + virtual void emitMccNewObjectFastPath(const MachineInstr *, + const MachineOperand &, unsigned) {}; + virtual void emitCJNewArrayFastPath(const MachineInstr &, + const MachineOperand &) {}; + virtual void emitCJThrowException(const MachineInstr *MI, + const MachineOperand &, unsigned) {}; + const Function *tryGetCangjieStubCallNativeFunc(const MachineInstr *MI, + const Function *Callee) const; + virtual void emitCangjieCallStubInstImpl(const MachineInstr *MI, + const Function *F, + const MachineOperand &MOSym, + unsigned Opcode){}; + virtual void emitCangjieCustomInst() {}; + virtual void emitGetCJThreadId() {}; + + int64_t getAllocBufferOffsetInCJTLS() const; + int64_t getMutatorOffsetInCJTLS() const; + int64_t getProtectAddrOffsetInCJTLS() const; + int64_t getSafepointCheckAddrOffsetInCJTLS() const; + int64_t getCJThreadOffsetInCJTLS() const; + public: ~AsmPrinter() override; @@ -479,7 +510,7 @@ public: /// label of that alias needs to be emitted before the corresponding element. using AliasMapTy = DenseMap>; void emitGlobalConstant(const DataLayout &DL, const Constant *CV, - AliasMapTy *AliasList = nullptr); + AliasMapTy *AliasList = nullptr, bool IsReflectGV = false); /// Unnamed constant global variables solely contaning a pointer to /// another globals variable act like a global variable "proxy", or GOT @@ -499,6 +530,9 @@ public: /// Emit the stack maps. void emitStackMaps(StackMaps &SM); + /// Emit the CJMetadata infos. + void emitCJMetadataInfo(CJMetadataInfo &CMI, Module &M); + //===------------------------------------------------------------------===// // Overridable Hooks //===------------------------------------------------------------------===// @@ -536,6 +570,26 @@ public: /// Targets can override this to emit stuff at the end of a basic block. virtual void emitBasicBlockEnd(const MachineBasicBlock &MBB); + virtual void emitCJStackCheck(const MachineInstr &) {} + + virtual int emitSOFECall(const MachineInstr &) { return 0; } + + virtual int emitStackGrow(const MachineInstr &) { return 0; } + + virtual void emitCangjieSafepoint(const MachineInstr &) {} + + virtual void emitGcStateCheck() {} + + virtual int emitSafePointDirectCall(unsigned Index) { return 0; } + + virtual void emitMetadataAddress() {} + + /// Reload the SP to the correct position when entering epilogue. + /// This is because SP might change in function execution under certain + /// circumstances and our exception handling reqires that SP should be + /// at the same place as it leaves the prologue. + virtual void reloadSPForEpilogue(MachineFunction &) {} + /// Targets should implement this to emit instructions. virtual void emitInstruction(const MachineInstr *) { llvm_unreachable("EmitInstruction not implemented"); @@ -655,6 +709,9 @@ public: /// Desc is a string saying what the encoding is specifying (e.g. "LSDA"). void emitEncodingByte(unsigned Val, const char *Desc = nullptr) const; + /// Emit a magic number in EH table header when no callsites in function. + void EmitNoCallsiteEHHeader() const; + /// Return the size of the encoding in bytes. unsigned GetSizeOfEncodedValue(unsigned Encoding) const; diff --git a/llvm/include/llvm/CodeGen/CJMetadata.h b/llvm/include/llvm/CodeGen/CJMetadata.h new file mode 100644 index 000000000..8b477ab78 --- /dev/null +++ b/llvm/include/llvm/CodeGen/CJMetadata.h @@ -0,0 +1,153 @@ +//===- CodeGen/CJMetadata.h - Self-Defined CJMetadata Section ---*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file generates cangjie self-defined CJMetadata section. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_CJMETADATA_H +#define LLVM_CODEGEN_CJMETADATA_H +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Triple.h" +#include "llvm/CodeGen/StackMapEncode.h" +#include + +namespace llvm { +class AsmPrinter; +class Function; +class GCStrategy; +class MCStreamer; +class MCSymbol; +class MCSection; +class MCExpr; +class StackMaps; +class GlobalVariable; +class Module; + +enum CJMetadataTable { + // RW Section + SDKVersionIdx, + MethodInfoTableIdx, + StringPoolDictTableIdx, + StringPoolTableIdx, + StackMapTableIdx, // StackMap Table + GCTibTableIdx, + GCRootsTableIdx, + TypeInfoTableIdx, + TypeTemplateIdx, + TypeFieldsIdx, + StaticGIIdx, + GlobalInitFuncIdx, + MTableIdx, + GCFlagsIdx, + ReflectPkgInfoIdx, + ReflectGVInfoIdx, + ReflectGIIdx, + InnerTypeExtensionsIdx, + OuterTypeExtensionsIdx, + CJMetadataMax +}; + +struct TableDesc { + MCSection *TableSection; +}; + +class CJMetadataInfo { +public: + CJMetadataInfo(AsmPrinter &AP, StackMaps &SM); + ~CJMetadataInfo() = default; + void recordCurrentFunc(); + void emitCJMetadata(Module &M); + +private: + void init(); + void emitDatas(const MCSymbol *FuncName, const MCSymbol *FuncBegin, + const MCSymbol *FuncEnd, unsigned FuncNumber); + void emitSDKVersion(); + void emitMethodInfoTable(); + void emitGlobalInitFuncTable(); + void emitStackTraceInfo(const MCSymbol *FuncSym, const MCSymbol *DescSym); + void emitEHTableOffset(unsigned FuncNumber); + void emitStringPoolDictTable(); + void emitStringPoolTable(); + void emitStackMaps(); + void emitGCTibTable(); + void emitGCRoots(); + + void emitGVToSection(SmallVectorImpl &); + void emitGVWithSubExpr(SmallVectorImpl &, MCSection *, + StringRef); + + void emitTypeInfo(); + void emitTypeTemplate(); + void emitMTable(); + void emitStaticGenericTI(); + void emitInnerTypeExtensions(); + void emitOuterTypeExtensions(); + void emitGCFlags(); + void emitReflectInfo(); + void emitReflectGenericTI(); + void emitSubExpr(StringRef Label, const GlobalVariable *GV); + const MCExpr *getGVRefSymbol(const GlobalVariable *GV); + const MCExpr *getOrInsertStrPoolOffset(std::string &Str, + const MCSymbol *DescSym, + bool MachONeedNoOffset = false); + + void recordExternalMethod(); + void recordMetadata(); + void recordGlobalVariable(const GlobalVariable *GV); + void recordKlass(const GlobalVariable *GV); + + uint64_t addMangledStr(std::string &Str); + void splitStr(std::string &Str, std::vector &CompressedCode); + Module *M; + AsmPrinter &AP; + StackMaps &SM; + MCStreamer &OS; + MCContext &Context; + const Triple &TT; + bool IsMachO = TT.isOSBinFormatMachO(); + TableDesc TD[CJMetadataMax]; + // + SmallVector, + 100> + MethodTable; + SmallVector, + 100> + InitMethodTable; + SmallVector, + 100> + ComdatMethodTable; + SmallVector GCRootTable; + SmallVector InternalTITable; + SmallVector ExternalTITable; + SmallVector TIFieldsTable; + SmallVector MTableTable; + SmallVector GCTibTable; + SmallVector StaticGenericTI; + SmallVector ReflectGeneticTI; + SmallVector ExternalMethod; + SmallVector ReflectStrGV; + SmallVector ReflectPkgInfoGV; + SmallVector ReflectOtherGV; + SmallVector OuterTypeExtensions; + SmallVector InnerTypeExtensions; + SmallVector TypeTemplateGVTable; + std::unordered_map String2Symbol; + CompressedInfo::ItemIdxHelper SplitedMangledStr; + MCSymbol *StrPoolDictOffsetsSym; + uint64_t StrPoolIdx; + uint32_t FuncPtrSize; +}; +} // namespace llvm +#endif // LLVM_CODEGEN_CJMETADATA_H diff --git a/llvm/include/llvm/CodeGen/CJStackPointerInserter.h b/llvm/include/llvm/CodeGen/CJStackPointerInserter.h new file mode 100644 index 000000000..6c5a402bd --- /dev/null +++ b/llvm/include/llvm/CodeGen/CJStackPointerInserter.h @@ -0,0 +1,86 @@ +//===- CJStackPointerInserter.h -- Cangjie Stack Pointer Inserter ---------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// This file implements the CJStackPointerInserter pass. For each statepoint +// machine instruction in the function, this pass implements live variable +// analysis of stack pointers, computes stack pointers that are alive at the +// call point, include in the register or spilled on the stack, and rewrites +// statepoints machine instructions to record them. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_CJSTACKPOINTERINSERTER_H +#define LLVM_CODEGEN_CJSTACKPOINTERINSERTER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/StackMaps.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" +#include "llvm/InitializePasses.h" +#include "llvm/PassRegistry.h" + +namespace llvm { + +// a stack slot: +using SlotInfo = std::pair; +// Structure of the stack pointer record of the statepoint +struct CJStackLives { + SetVector Slots; + SetVector Regs; + + bool empty() { return Regs.empty() && Slots.empty(); } + + void insertSlot(SlotInfo Slot) { Slots.insert(Slot); } + + void insertRegs(SetVector &Data) { + Regs.insert(Data.begin(), Data.end()); + } +}; + +// Record the function args of pointer type. +struct FuncArgPointers { + std::vector Regs; + std::vector FIs; +}; + +// map a statepoint MI to its liveset. +using StatepointLives = MapVector; + +class CJStackPointerInserter : public MachineFunctionPass { +public: + static char ID; // Pass identification, replacement for typeid + CJStackPointerInserter() : MachineFunctionPass(ID) { + initializeCJStackPointerInserterPass(*PassRegistry::getPassRegistry()); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + void getAnalysisUsage(AnalysisUsage &AU) const override; + +private: // Intermediate data structures + bool needRewriteCall(MachineInstr &MI); + // Filter out the part of the gc pointer from the recorded regs and stacks. + void filterGCPointer(MachineInstr &MI, StatepointOpers &SO, + SmallSetVector &Regs, + SmallSetVector &Slots); + void recordArgStackPtrs(SmallSetVector &Regs, + SmallSetVector &Slots, + FuncArgPointers &ArgPtrs); + + // Rewrite the statepoint MI based on the live stack pointers. + bool rewriteStatepoint(MachineFunction &MF, MachineInstr &MI, + CJStackLives &RecordLives, FuncArgPointers &ArgPtrs); +}; + +} // namespace llvm + +#endif diff --git a/llvm/include/llvm/CodeGen/CallingConvLower.h b/llvm/include/llvm/CodeGen/CallingConvLower.h index 5c3776e97..c4f7847c8 100644 --- a/llvm/include/llvm/CodeGen/CallingConvLower.h +++ b/llvm/include/llvm/CodeGen/CallingConvLower.h @@ -26,6 +26,7 @@ class CCState; class MachineFunction; class MVT; class TargetRegisterInfo; +class Function; /// CCValAssign - Represent assignment of one arg/retval to a location. class CCValAssign { @@ -270,6 +271,8 @@ public: return alignTo(StackOffset, MaxStackArgAlign); } + void AddAlignedCallFrameSizeMetaDataForCJFFI(Function *Func, + unsigned CallFrameSize) const; /// isAllocated - Return true if the specified register (or an alias) is /// allocated. bool isAllocated(MCRegister Reg) const { diff --git a/llvm/include/llvm/CodeGen/CodeGenPassBuilder.h b/llvm/include/llvm/CodeGen/CodeGenPassBuilder.h index f4b1980b9..c39890ff1 100644 --- a/llvm/include/llvm/CodeGen/CodeGenPassBuilder.h +++ b/llvm/include/llvm/CodeGen/CodeGenPassBuilder.h @@ -21,6 +21,7 @@ #include "llvm/Analysis/BasicAliasAnalysis.h" #include "llvm/Analysis/CFLAndersAliasAnalysis.h" #include "llvm/Analysis/CFLSteensAliasAnalysis.h" +#include "llvm/Analysis/CJAliasAnalysis.h" #include "llvm/Analysis/ScopedNoAliasAA.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Analysis/TypeBasedAliasAnalysis.h" @@ -53,6 +54,10 @@ #include #include +namespace llvm { +extern cl::opt CJPipeline; +} // namespace llvm + namespace llvm { // FIXME: Dummy target independent passes definitions that have not yet been @@ -512,6 +517,9 @@ static inline AAManager registerAAAnalyses(CFLAAType UseCFLAA) { AA.registerFunctionAnalysis(); AA.registerFunctionAnalysis(); + if (CJPipeline) + AA.registerFunctionAnalysis(); + return AA; } diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h index b7f6de402..68b46a1c8 100644 --- a/llvm/include/llvm/CodeGen/ISDOpcodes.h +++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h @@ -838,6 +838,13 @@ enum NodeType { FP_TO_SINT_SAT, FP_TO_UINT_SAT, + /// Return data of float state register + /// if last bit of result is 1, it means float conversion has an exception + GET_FP_STATE, + + /// Set float state to zero + RESET_FP_STATE, + /// X = FP_ROUND(Y, TRUNC) - Rounding 'Y' from a larger floating point type /// down to the precision of the destination VT. TRUNC is a flag, which is /// always an integer that is zero or one. If TRUNC is 0, this is a diff --git a/llvm/include/llvm/CodeGen/MachineFrameInfo.h b/llvm/include/llvm/CodeGen/MachineFrameInfo.h index 7ea731b46..269119295 100644 --- a/llvm/include/llvm/CodeGen/MachineFrameInfo.h +++ b/llvm/include/llvm/CodeGen/MachineFrameInfo.h @@ -241,6 +241,9 @@ private: /// to be allocated on entry to the function. uint64_t StackSize = 0; + /// Record the offset of frame pointer from stack base pointer on Win64. + uint64_t FPOffset = 0; + /// The amount that a frame offset needs to be adjusted to /// have the actual offset from the stack/frame pointer. The exact usage of /// this is target-dependent, but it is typically used to adjust between @@ -579,6 +582,12 @@ public: /// Set the size of the stack. void setStackSize(uint64_t Size) { StackSize = Size; } + /// Return the offset of the frame pointer on Win64. + uint64_t getWin64FramePointerOffset() const { return FPOffset; } + + /// Set the offset of the frame pointer on Win64. + void setWin64FramePointerOffset(uint64_t offset) { FPOffset = offset; } + /// Estimate and return the size of the stack frame. uint64_t estimateStackSize(const MachineFunction &MF) const; diff --git a/llvm/include/llvm/CodeGen/MachineFunction.h b/llvm/include/llvm/CodeGen/MachineFunction.h index fc1188186..36986fde7 100644 --- a/llvm/include/llvm/CodeGen/MachineFunction.h +++ b/llvm/include/llvm/CodeGen/MachineFunction.h @@ -400,6 +400,16 @@ class LLVM_EXTERNAL_VISIBILITY MachineFunction { /// \pre Fn, Target, MMI, and FunctionNumber are properly set. void init(); + /// The symbol for the epilogue of current function. + MCSymbol *EpilogueLabel = nullptr; + + /// Whether we've found the Epilogue for the current funciton. + bool EpilogueLabelEmitted = false; + + /// Whether the current cangjie function needs Reload RSP, used by + /// append epilogue. + bool NeedCangjieReloadSP = false; + public: struct VariableDbgInfo { const DILocalVariable *Var; @@ -1260,12 +1270,28 @@ public: /// Move the call site info from \p Old to \New call site info. This function /// is used when we are replacing one call instruction with another one to /// the same callee. - void moveCallSiteInfo(const MachineInstr *Old, - const MachineInstr *New); + void moveCallSiteInfo(const MachineInstr *Old, const MachineInstr *New); - unsigned getNewDebugInstrNum() { - return ++DebugInstrNumberingCount; - } + unsigned getNewDebugInstrNum() { return ++DebugInstrNumberingCount; } + + /// Return whether the epilogue position has been set. + bool isEpilogueLabelEmitted() const { return EpilogueLabelEmitted; } + + /// Set the epilogue label emitted to indicate we've found and emitted the + /// epilogue label. + void setEpilogueLabelEmitted() { EpilogueLabelEmitted = true; } + + /// Get epilogue label for the current function. + MCSymbol *getEpilogueLabel() const { return EpilogueLabel; } + + /// Set epilogue label for the current function. + void setEpilogueLabel(MCSymbol *label) { EpilogueLabel = label; } + + /// Get flag for reload rsp before epilogue. + bool getNeedCangjieReloadSP() const { return NeedCangjieReloadSP; } + + /// Set the flag whether we need to reload rsp in epilogue. + void setNeedCangjieReloadSP(bool value) { NeedCangjieReloadSP = value; } }; //===--------------------------------------------------------------------===// diff --git a/llvm/include/llvm/CodeGen/MachineInstr.h b/llvm/include/llvm/CodeGen/MachineInstr.h index 5f483a8d0..5c8f20a3b 100644 --- a/llvm/include/llvm/CodeGen/MachineInstr.h +++ b/llvm/include/llvm/CodeGen/MachineInstr.h @@ -114,6 +114,14 @@ public: // this instruction. }; + enum MIExtFlag { + NoExtFlags = 0, + EpilogueIns = 1, // Set to mark certain RSP recovery + // instruction as part of epilogue + // instructions for Cangjie function. + // Not all epilogue instructions need + // to be marked. + }; private: const MCInstrDesc *MCID; // Instruction descriptor. MachineBasicBlock *Parent = nullptr; // Pointer to the owning basic block. @@ -126,6 +134,9 @@ private: // information about machine // instruction. + uint8_t ExtFlags = 0; // Extended bits of flag used by + // Cangjie about machine instruction. + uint8_t AsmPrinterFlags = 0; // Various bits of information used by // the AsmPrinter to emit helpful // comments. This is *not* semantic @@ -257,6 +268,11 @@ private: /// defined by this instruction. unsigned DebugInstrNum; + /// Used for cangjie stack check. + /// For X86: the size is frame size. + /// For aarch64: the size is addsize calculated for sp. + unsigned CJStackSize; + // Intrusive list support friend struct ilist_traits; friend struct ilist_callback_traits; @@ -349,6 +365,12 @@ public: Flags &= ~((uint16_t)Flag); } + /// Return whether an extended MI flag is set. + bool getExtFlag(MIExtFlag Flag) const { return ExtFlags & Flag; } + + /// Set a extended MI flag. + void setExtFlag(MIExtFlag Flag) { ExtFlags |= (uint16_t)Flag; } + /// Return true if MI is in a bundle (but not the first MI in a bundle). /// /// A bundle looks like this before it's finalized: @@ -462,6 +484,10 @@ public: /// don't immediately insert them. unsigned getDebugInstrNum(MachineFunction &MF); + unsigned peekCJStackSize() const { return CJStackSize; } + + void setCJStackSize(uint64_t Num) { CJStackSize = Num; } + /// Examine the instruction number of this MachineInstr. May be zero if /// it hasn't been assigned a number yet. unsigned peekDebugInstrNum() const { return DebugInstrNum; } @@ -970,9 +996,6 @@ public: return hasProperty(MCID::RegSequence, Type); } - /// Return true if this instruction behaves - /// the same way as the generic EXTRACT_SUBREG instructions. - /// E.g., on ARM, /// rX, rY VMOVRRD dZ /// is equivalent to two EXTRACT_SUBREG: /// rX = EXTRACT_SUBREG dZ, ssub_0 @@ -1156,6 +1179,8 @@ public: bool isIdenticalTo(const MachineInstr &Other, MICheckType Check = CheckDefs) const; + bool isCalDerivedPtrInCangjieCopyGC(MachineRegisterInfo *MRI) const; + /// Unlink 'this' from the containing basic block, and return it without /// deleting it. /// diff --git a/llvm/include/llvm/CodeGen/MachineInstrBuilder.h b/llvm/include/llvm/CodeGen/MachineInstrBuilder.h index 80f30231a..6dfb85ed9 100644 --- a/llvm/include/llvm/CodeGen/MachineInstrBuilder.h +++ b/llvm/include/llvm/CodeGen/MachineInstrBuilder.h @@ -280,6 +280,11 @@ public: return *this; } + const MachineInstrBuilder &setMIExtFlag(MachineInstr::MIExtFlag Flag) const { + MI->setExtFlag(Flag); + return *this; + } + // Add a displacement from an existing MachineOperand with an added offset. const MachineInstrBuilder &addDisp(const MachineOperand &Disp, int64_t off, unsigned char TargetFlags = 0) const { diff --git a/llvm/include/llvm/CodeGen/Passes.h b/llvm/include/llvm/CodeGen/Passes.h index 9822f8013..85fb0e1b1 100644 --- a/llvm/include/llvm/CodeGen/Passes.h +++ b/llvm/include/llvm/CodeGen/Passes.h @@ -102,6 +102,9 @@ namespace llvm { /// variable is life and sets machine operand kill flags. extern char &LiveVariablesID; + /// CJStackPointerInserter pass -. + extern char &CJStackPointerInserterID; + /// PHIElimination - This pass eliminates machine instruction PHI nodes /// by inserting copy instructions. This destroys SSA information, but is the /// desired input for some register allocators. This pass is "required" by @@ -275,6 +278,14 @@ namespace llvm { /// information. extern char &MachineBlockPlacementStatsID; + /// CJBarrierLowering Pass - Used by cangjie gc barrier to perform its + /// default lowering operations. + FunctionPass *createCJBarrierLoweringPass(CodeGenOpt::Level OptLevel); + + /// CJBarrierLowering Pass - Used by cangjie gc barrier to perform its + /// default lowering operations. + extern char &CJBarrierLoweringID; + /// GCLowering Pass - Used by gc.root to perform its default lowering /// operations. FunctionPass *createGCLoweringPass(); @@ -516,6 +527,9 @@ namespace llvm { /// Create IR Type Promotion pass. \see TypePromotion.cpp FunctionPass *createTypePromotionPass(); + /// Create Thread Sanitizer pass. \see ThreadSanitizer.cpp + FunctionPass *createThreadSanitizerPass(); + /// Add Flow Sensitive Discriminators. PassNum specifies the /// sequence number of this pass (starting from 1). FunctionPass * diff --git a/llvm/include/llvm/CodeGen/StackMapEncode.h b/llvm/include/llvm/CodeGen/StackMapEncode.h new file mode 100644 index 000000000..e89c72887 --- /dev/null +++ b/llvm/include/llvm/CodeGen/StackMapEncode.h @@ -0,0 +1,397 @@ +//===- StackMapsEncode.h - StackMaps ----------------------------*- 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 +// +// Utils for compressing stack maps +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_STACKMAPENCODE_H +#define LLVM_CODEGEN_STACKMAPENCODE_H + +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +namespace llvm { +namespace { +const int32_t SingleVarIntBits = 4; +const int32_t SingleVarIntMaxValue = 11; +const int32_t BitsPerByte = 8; +const int32_t BufferWidth = 64; +} // namespace + +inline uint32_t getMinBytesForInt(int32_t Value) { + if ((Value >= INT8_MIN) && (Value <= INT8_MAX)) { + return 1; + } + if ((Value >= INT16_MIN) && (Value <= INT16_MAX)) { + return 2; + } + return 4; +} +inline uint32_t getMinBytesForUInt(uint32_t Value) { + if (Value <= UINT8_MAX) { + return 1; + } + if (Value <= UINT16_MAX) { + return 2; + } + if (Value <= 0xFFFFFF) { + return 3; + } + return 4; +} + +inline uint32_t getVarIntBitNumsForInt(int32_t Value) { + if ((Value >= 0) && (Value <= SingleVarIntMaxValue)) { + return SingleVarIntBits; + } + return SingleVarIntBits + BitsPerByte * getMinBytesForInt(Value); +} + +inline uint32_t getVarIntBitNumsForUInt(uint32_t Value) { + if (Value <= SingleVarIntMaxValue) { + return SingleVarIntBits; + } + return SingleVarIntBits + BitsPerByte * getMinBytesForUInt(Value); +} + +inline uint32_t getValidBitNums(uint32_t Value) { + for (int I = 31; I >= 1; --I) { // 31: uint32_t occupy 32 bits at most + if (Value & (1 << I)) { + return I + 1; + } + } + return 1; // value 0 and 1 occupy 1 bits. +} + +struct CompressedInfo { + CompressedInfo(const std::unordered_map &CallSavedRegMap) + : CSRegMap(CallSavedRegMap) { + // Items[0] means useless records and will not be emitted. + // so that StackMapItem's Index should minus 1 before use. + RegItem RegInfo{0}; + RegItems.getOrInsertIndex(RegInfo); + + SlotItem SlotInfo{0, {0}, {}}; + SlotItems.getOrInsertIndex(SlotInfo); + + LineNumberItem LineNumberInfo{0}; + LNItems.getOrInsertIndex(LineNumberInfo); + + DerivedInfo.emplace_back(std::make_pair(0, 0)); + } + ~CompressedInfo() = default; + + struct RegItem { + unsigned RegBit; + bool operator<(const RegItem &X) const { return RegBit < X.RegBit; } + }; + + struct SlotItem { + int64_t BaseOffset; + std::vector SlotBit; + std::vector CompressedSlotBit; + bool operator<(const SlotItem &X) const { + return (BaseOffset < X.BaseOffset) || + ((BaseOffset == X.BaseOffset) && (SlotBit < X.SlotBit)) || + ((BaseOffset == X.BaseOffset) && (SlotBit == X.SlotBit) && + (CompressedSlotBit < X.CompressedSlotBit)); + } + }; + + struct LineNumberItem { + unsigned LN; + bool operator<(const LineNumberItem &X) const { return LN < X.LN; } + }; + + struct IdxItem { + unsigned RegIdxPlusOne; + unsigned SlotIdxPlusOne; + unsigned LNIdxPlusOne; + unsigned DerivedInfoStartIdx; + unsigned SPRegIdxPlusOne; + unsigned SPSlotIdxPlusOne; + }; + + template struct ItemIdxHelper { + std::vector Items; + std::map Item2Idx; + unsigned getOrInsertIndex(T &Item) { + auto Itr = Item2Idx.find(Item); + if (Itr != Item2Idx.end()) { + return Itr->second; + } + Item2Idx[Item] = Items.size(); + Items.emplace_back(Item); + return Items.size() - 1; + } + }; + struct MaxBitsInfo { + // StackMapItem + uint32_t RegIdx = 0; + uint32_t SlotIdx = 0; + uint32_t LNIdx = 0; + uint32_t DerivedIdx = 0; + uint32_t SPRegIdx = 0; // stack ptr RegIdx + uint32_t SPSlotIdx = 0; // stack ptr SlotIdx + // RegItem + uint32_t RegBit = 0; + // SlotItem + uint32_t BaseOffset = 0; + uint32_t SlotBit = 0; + // LineNumberItem + uint32_t LN = 0; + }; + +public: + // prologue + uint64_t StackSize; + uint64_t FormatType; + unsigned CalleeSavedReg = 0; // aarch64 and x86 use 32 bit at most + SmallVector CalleeSavedOffsets; // 32: preset vector size + + MapVector StackMapItem; + ItemIdxHelper RegItems; + ItemIdxHelper SlotItems; + ItemIdxHelper LNItems; + // + std::vector> DerivedInfo; + + MaxBitsInfo MaxBits; + // unordered_map, regNo can be used to find callee saved reg + // while bitIdx is used in prologue. + const std::unordered_map &CSRegMap; +}; + +class DataEncoder { +public: + DataEncoder(MCStreamer &Streamer, const CompressedInfo &InitData, + const std::vector &InitPrologueBit2Reg, + const std::vector &Bit2Reg) + : OS(Streamer), Data(InitData), PrologueBit2Reg(InitPrologueBit2Reg), + Bit2RegStr(Bit2Reg) { + reset(); + } + ~DataEncoder() = default; + void emitPrologueAndStackMapItemHeader(); + void emitStackMapItem(); + void writeRegRefAndSlotRef(); + void writeLineNumber(); + void writeDerivedInfo(); + void emitBufferContent(); + void emitCommentForDataAfterStackMapItem(); + +private: + void writeBits(uint32_t BitNums, int64_t Value) { + assert(BitNums <= BufferWidth && "can't write bits lager than 64!"); + if (BitNums == 0) { + return; + } + auto Temp = *reinterpret_cast(&Value); + if (BitNums != BufferWidth) { + // clear high bits: Temp &= 0b0001..BitNums..1 + Temp &= (((uint64_t)1 << BitNums) - 1); + } + if (RemainedBits == 0) { + Buffer.emplace_back(Temp); + RemainedBits = BufferWidth - BitNums; + return; + } + if (BitNums > RemainedBits) { + // put some bits to remained bits and others to new uint64_t + Buffer.back() |= Temp << (BufferWidth - RemainedBits); + Buffer.emplace_back(Temp >> RemainedBits); + RemainedBits = BufferWidth - (BitNums - RemainedBits); + return; + } + // put values to remained bits + Buffer.back() |= Temp << (BufferWidth - RemainedBits); + RemainedBits -= BitNums; + return; + } + + void writeBitVec(uint32_t BitNums, const std::vector &Value) { + unsigned Idx = 0; + assert(!Value.empty() && "Value should not be empty!"); + assert(BitNums >= ((Value.size() - 1) * BufferWidth) && + "BitNums should not less than (Value.size() - 1) * 64"); + while (Idx < Value.size() - 1) { + writeBits(64, Value[Idx++]); // 64: bitnum + BitNums -= BufferWidth; + } + if (BitNums > BufferWidth) { + // write extra 0s when BitNums larger then vector size + writeBits(BufferWidth, Value[Idx]); + BitNums -= BufferWidth; + while (BitNums >= BufferWidth) { + writeBits(BufferWidth, 0); + BitNums -= BufferWidth; + } + writeBits(BitNums, 0); + } else { + writeBits(BitNums, Value[Idx]); + } + return; + } + + void writeCompressedBitVec(uint32_t BitNums, + const std::vector &Value) { + uint32_t BitCnt = 0; + assert(!Value.empty() && "Value should not be empty!"); + // bit31 == 1 means the following 31bits/remain bits are origion val + // bit31 == 0: + // bit30's value stand for 0-compressed/1-compressed: + // remain bits are nums of 0/1 compressed unit + for (unsigned Idx = 0, End = Value.size(); Idx < End; ++Idx) { + if (Value[Idx] & 0x80000000) { + writeBits(1, 1); + uint32_t WriteBitNums = 31; + // we need to get valid nums if the origin value is the last element. + if (Idx == End - 1) { + WriteBitNums = getValidBitNums(Value[Idx] & 0x7fffffff); + } + writeBits(WriteBitNums, Value[Idx] & 0x7fffffff); + BitCnt += 1 + WriteBitNums; + } else { + writeBits(1, 0); + if (Value[Idx] & 0x40000000) { + writeBits(1, 1); + } else { + writeBits(1, 0); + } + uint32_t VarIntVal = Value[Idx] & 0x3fffffff; + writeVarUint(VarIntVal); + BitCnt += 2; + BitCnt += getVarIntBitNumsForUInt(VarIntVal); + } + } + + if (BitNums < BitCnt) { + report_fatal_error("bitlen error while write compressed bit map!\n"); + } + // write extra 0s when BitNums larger then BitCnt + uint32_t LeftBits = BitNums - BitCnt; + while (LeftBits > 0) { + if (LeftBits >= BufferWidth) { + writeBits(BufferWidth, 0); + LeftBits -= BufferWidth; + } else { + writeBits(LeftBits, 0); + break; + } + } + } + + void writeVarInt(int64_t Value, bool IsUnsigned = false) { + if ((Value >= 0) && (Value <= SingleVarIntMaxValue)) { + writeBits(SingleVarIntBits, Value); + } else { + uint32_t ByteNums = + IsUnsigned ? getMinBytesForUInt(Value) : getMinBytesForInt(Value); + writeBits(SingleVarIntBits, ByteNums + SingleVarIntMaxValue); + writeBits(ByteNums * BitsPerByte, Value); + } + return; + } + + void writeVarUint(int64_t Value) { writeVarInt(Value, true); } + + void writeBitsToPadding() { + // 4: PaddingBits range from 0 to 7 which occupy 4 bits. + unsigned PaddingBits = + (RemainedBits + BufferWidth - SingleVarIntBits) % BitsPerByte; + writeVarInt(PaddingBits); + if (PaddingBits != 0) { + writeBits(PaddingBits, 0); + } + } + + uint64_t readBits(uint32_t BitNums) { + assert(BitNums <= BufferWidth && "Can't read bits larger than 64"); + if (BitNums == 0) { + return 0; + } + unsigned ReadIdx = ReadBitsAll / BufferWidth; + uint64_t ReadBitsInCurIdx = ReadBitsAll % BufferWidth; + ReadBitsAll += BitNums; + uint64_t Value = Buffer.at(ReadIdx) >> (ReadBitsInCurIdx); + if (BitNums + ReadBitsInCurIdx <= BufferWidth) { + if (BitNums != BufferWidth) { + Value &= ((uint64_t)1 << BitNums) - 1; + } + } else { + // ReadBitsInCurIdx = [1, 63] and BitNums > (64 -ReadBitsInCurIdx) + BitNums -= (BufferWidth - ReadBitsInCurIdx); + Value |= ((Buffer.at(ReadIdx + 1) & (((uint64_t)1 << BitNums) - 1)) + << (BufferWidth - ReadBitsInCurIdx)); + } + return Value; + } + + int32_t readBitsAsInt(uint32_t BitNums) { + uint64_t TempValue = readBits(BitNums); + switch (BitNums) { + case 8: { + uint8_t Result = TempValue & 0xFF; + return *(reinterpret_cast(&Result)); + } + case 16: { + uint16_t Result = TempValue & 0xFFFF; + return *(reinterpret_cast(&Result)); + } + case 32: { + uint32_t Result = TempValue & 0xFFFFFFFF; + return *(reinterpret_cast(&Result)); + } + default: { + report_fatal_error("BitNums should be 8/16/32 for Int Value!"); + return 0; + } + } + } + int32_t readVarInt() { + uint32_t Value = readBits(SingleVarIntBits); + if (Value <= SingleVarIntMaxValue) { + return Value; + } + return readBitsAsInt((Value - SingleVarIntMaxValue) * BitsPerByte); + } + uint32_t readVarUint() { + uint32_t Value = readBits(SingleVarIntBits); + if (Value <= SingleVarIntMaxValue) { + return Value; + } + return readBits((Value - SingleVarIntMaxValue) * BitsPerByte); + } + + void emitCommentForPrologueAndStackMapItemHeader(); + void emitCommentForStackMapItem(); + void emitCommentForRegsAndSlots(); + void emitCommentForLineNumber(); + void emitCommentForDerivedInfo(); + void reset() { + Buffer.clear(); + RemainedBits = 0; + ReadBitsAll = 0; + } + +private: + MCStreamer &OS; + std::vector Buffer; + uint32_t RemainedBits = 0; + uint32_t ReadBitsAll = 0; + uint64_t FormatType = 0; + CompressedInfo::MaxBitsInfo MaxBits; + const CompressedInfo &Data; + const std::vector &PrologueBit2Reg; + const std::vector &Bit2RegStr; + void emitCommentForSlots(raw_svector_ostream &Comment, unsigned int I); +}; +} // end namespace llvm +#endif // LLVM_CODEGEN_STACKMAPENCODE_H diff --git a/llvm/include/llvm/CodeGen/StackMaps.h b/llvm/include/llvm/CodeGen/StackMaps.h index 01cc9bc37..d8c04a760 100644 --- a/llvm/include/llvm/CodeGen/StackMaps.h +++ b/llvm/include/llvm/CodeGen/StackMaps.h @@ -22,12 +22,13 @@ namespace llvm { class AsmPrinter; +class GCStrategy; class MCSymbol; class MCExpr; class MCStreamer; class raw_ostream; class TargetRegisterInfo; - +struct CompressedInfo; /// MI-level stackmap operands. /// /// MI stackmap operations take the form: @@ -150,9 +151,13 @@ public: /// , , [deopt args...], /// , , [gc pointer args...], /// , , [gc allocas args...], -/// , , [base/derived pairs] +/// , , [base/derived pairs] /// base/derived pairs in gc map are logical indices into /// section. +/// , , [base/offset pairs] +/// base/offset pairs in gc fields are logical indices into +/// section. +/// , , [stack ptr...]. /// All gc pointers assigned to VRegs produce new value (in form of MI Def /// operand) and are tied to it. class StatepointOpers { @@ -183,6 +188,13 @@ public: /// Get index of Num Call Arguments operand. unsigned getNCallArgsPos() const { return NumDefs + NCallArgsPos; } + /// Get starting index of Call Arguments operand. + unsigned getCallArgsPos() const { + assert(MI->getOperand(NumDefs + NCallArgsPos).getImm() > 0 && + "The Num of CallArgs is 0!"); + return NumDefs + MetaEnd; + } + /// Get starting index of non call related arguments /// (calling convention, statepoint flags, vm state and gc state). unsigned getVarIdx() const { @@ -221,10 +233,22 @@ public: /// Return the statepoint flags. uint64_t getFlags() const { return MI->getOperand(getFlagsIdx()).getImm(); } + unsigned getNumCallArgs() const { + return MI->getOperand(getNCallArgsPos()).getImm(); + } + uint64_t getNumDeoptArgs() const { return MI->getOperand(getNumDeoptArgsIdx()).getImm(); } + const Function *getCalledFunction(); + + /// Get index of number of stack pointers. + unsigned getNumStackPtrsIdx(); + + /// Get index of number of struct arg map entries. + unsigned getNumStructArgEntriesIdx(); + /// Get index of number of gc map entries. unsigned getNumGcMapEntriesIdx(); @@ -237,12 +261,29 @@ public: /// Get index of first GC pointer operand of -1 if there are none. int getFirstGCPtrIdx(); + /// Get index of first Struct pointer operand of -1 if there are none. + int getFirstAllocaIdx(); + + /// Get index of first Stack pointer operand of -1 if there are none. + int getFirstStackPtrIdx(); + + /// Get index of first Stack Arg operand of -1 if there are none. + int getFirstStackArgIdx(); + /// Get vector of base/derived pairs from statepoint. /// Elements are indices into GC Pointer operand list (logical). /// Returns number of elements in GCMap. unsigned getGCPointerMap(SmallVectorImpl> &GCMap); + /// Get vector of struct ptr/offset pairs from statepoint. + /// Elements are indices Struct Pointer operand list (logical). + /// Returns number of elements in StructArgMap. + unsigned + getStructArgMap(SmallVectorImpl> &StructArgMap); + + bool isCJStackCheck(); + private: const MachineInstr *MI; unsigned NumDefs; @@ -263,6 +304,18 @@ public: unsigned Size = 0; unsigned Reg = 0; int64_t Offset = 0; + // base/derive ptr will be traversing by the following order: + // small register -> large register -> large SlotOffset -> small SlotOffset + bool operator<(const Location &X) const { + return (Type < X.Type) || (Type == X.Type && Size < X.Size) || + (Type == X.Type && Size == X.Size && Reg < X.Reg) || + (Type == X.Type && Size == X.Size && Reg == X.Reg && + Offset > X.Offset); + } + bool operator==(const Location &X) const { + return Type == X.Type && Size == X.Size && Reg == X.Reg && + Offset == X.Offset; + } Location() = default; Location(LocationType Type, unsigned Size, unsigned Reg, int64_t Offset) @@ -303,10 +356,12 @@ public: struct FunctionInfo { uint64_t StackSize = 0; - uint64_t RecordCount = 1; + uint64_t RecordCount = 0; + std::map CSReg2Stack; // FunctionInfo() = default; - explicit FunctionInfo(uint64_t StackSize) : StackSize(StackSize) {} + FunctionInfo(uint64_t StackSize, std::map &Info) + : StackSize(StackSize), CSReg2Stack(Info) {} }; struct CallsiteInfo { @@ -314,17 +369,30 @@ public: uint64_t ID = 0; LocationVec Locations; LiveOutVec LiveOuts; + LocationVec FOLocations; + LocationVec StackLocations; + uint32_t LineNumber = 0; + bool RecordAllRefInReg = false; + // vector which stores {base, derived, base, derived...} + LocationVec RefPairs; CallsiteInfo() = default; CallsiteInfo(const MCExpr *CSOffsetExpr, uint64_t ID, - LocationVec &&Locations, LiveOutVec &&LiveOuts) + LocationVec &&Locations, LiveOutVec &&LiveOuts, + LocationVec &&FOLocs, uint32_t LineNum = 0) : CSOffsetExpr(CSOffsetExpr), ID(ID), Locations(std::move(Locations)), - LiveOuts(std::move(LiveOuts)) {} + LiveOuts(std::move(LiveOuts)), FOLocations(std::move(FOLocs)), + LineNumber(LineNum) {} }; using FnInfoMap = MapVector; using CallsiteInfoList = std::vector; - + inline void setX86_64() { IsX86_64 = true; } + inline bool isX86_64() const { return IsX86_64; } + inline void setAArch64() { IsAArch64 = true; } + inline bool isAArch64() const { return IsAArch64; } + inline void setARM() { IsARM = true; } + inline bool isARM() const { return IsARM; } /// Generate a stackmap record for a stackmap instruction. /// /// MI must be a raw STACKMAP, not a PATCHPOINT. @@ -336,20 +404,33 @@ public: const MachineInstr &MI); /// Generate a stackmap record for a statepoint instruction. - void recordStatepoint(const MCSymbol &L, - const MachineInstr &MI); + void recordStatepoint(const MCSymbol &L, const MachineInstr &MI, + bool RecordAllRefInReg = false); + + /// Generate a stackmap record for a cangjie statepoint instruction. + void recordCJStackMap(const MachineInstr &MI, bool RecordAllRefInReg = false); /// If there is any stack map data, create a stack map section and serialize /// the map info into it. This clears the stack map data structures /// afterwards. void serializeToStackMapSection(); + void updateOrInsertFnInfo(const MCSymbol *FnSym, CallsiteInfo &CallInfo); + /// Get call site info. CallsiteInfoList &getCSInfos() { return CSInfos; } /// Get function info. FnInfoMap &getFnInfos() { return FnInfos; } + void emitCangjieStackMaps(MCStreamer &OS); + + struct StackMapOpersInfo { + uint64_t ID; + MachineInstr::const_mop_iterator MOI; + MachineInstr::const_mop_iterator MOE; + }; + private: static const char *WSMP; @@ -357,18 +438,57 @@ private: CallsiteInfoList CSInfos; ConstantPool ConstPool; FnInfoMap FnInfos; + bool IsX86_64 = false; + bool IsAArch64 = false; + bool IsARM = false; + + void processArrayType(ArrayType *ST, int64_t RefOffset, + LocationVec &Locations, unsigned Reg, + const GCStrategy *GS) const; + + void processAllocaStructType(StructType *ST, int64_t RefOffset, + LocationVec &Locations, unsigned Reg, + const GCStrategy *GS) const; + + MachineInstr::const_mop_iterator + parseOperand(MachineInstr::const_mop_iterator MOI, CallsiteInfo &CSInfo, + bool IsResult) const; + + MachineInstr::const_mop_iterator + parseImmOperand(MachineInstr::const_mop_iterator MOI, CallsiteInfo &CSInfo, + bool IsResult) const; + + MachineInstr::const_mop_iterator + parseRegOperand(MachineInstr::const_mop_iterator MOI, CallsiteInfo &CSInfo, + LocationVec &Locations, bool IsResult) const; MachineInstr::const_mop_iterator - parseOperand(MachineInstr::const_mop_iterator MOI, - MachineInstr::const_mop_iterator MOE, LocationVec &Locs, - LiveOutVec &LiveOuts) const; + parseStructArgsOperand(MachineInstr::const_mop_iterator MOI, + LocationVec &FOLocations, unsigned Offset) const; + + MachineInstr::const_mop_iterator + parseAllocaOperand(MachineInstr::const_mop_iterator MOI, + LocationVec &Locations) const; + + MachineInstr::const_mop_iterator + parseStackPtrOperand(MachineInstr::const_mop_iterator MOI, + LocationVec &Locations) const; + + MachineInstr::const_mop_iterator + parseCangjieStackOpers(MachineInstr::const_mop_iterator MOI, + CallsiteInfo &CSInfo) const; + + void + parseCangjieStatepointOpers(const MachineInstr &MI, StatepointOpers &SO, + CallsiteInfo &CSInfo, unsigned NumAllocas, + SmallVectorImpl &GCPtrIndices) const; /// Specialized parser of statepoint operands. /// They do not directly correspond to StackMap record entries. void parseStatepointOpers(const MachineInstr &MI, MachineInstr::const_mop_iterator MOI, MachineInstr::const_mop_iterator MOE, - LocationVec &Locations, LiveOutVec &LiveOuts); + CallsiteInfo &CSInfo); /// Create a live-out register record for the given register @p Reg. LiveOutReg createLiveOutReg(unsigned Reg, @@ -384,12 +504,12 @@ private: /// STACKMAP, and PATCHPOINT the label is expected to immediately *preceed* /// lowering of the MI to MCInsts. For STATEPOINT, it expected to /// immediately *follow*. It's not clear this difference was intentional, - /// but it exists today. - void recordStackMapOpers(const MCSymbol &L, - const MachineInstr &MI, uint64_t ID, - MachineInstr::const_mop_iterator MOI, - MachineInstr::const_mop_iterator MOE, - bool recordResult = false); + /// but it exists today. + void recordStackMapOpers(const MCSymbol &L, const MachineInstr &MI, + StackMapOpersInfo OpersInfo, + bool RecordResult = false, bool RecordAllRef = true); + + void emitCangjieCompressedStackMaps(MCStreamer &OS); /// Emit the stackmap header. void emitStackmapHeader(MCStreamer &OS); @@ -405,6 +525,12 @@ private: void print(raw_ostream &OS); void debug() { print(dbgs()); } + + void prepareCompressedData(CompressedInfo &Data, const FunctionInfo &FnInfo, + unsigned CSIdx, unsigned CSIdxEnd) const; + + void emitCangjieCompressedData(MCStreamer &OS, + const CompressedInfo &Data) const; }; } // end namespace llvm diff --git a/llvm/include/llvm/CodeGen/TargetFrameLowering.h b/llvm/include/llvm/CodeGen/TargetFrameLowering.h index fbce5d7a9..01138ec90 100644 --- a/llvm/include/llvm/CodeGen/TargetFrameLowering.h +++ b/llvm/include/llvm/CodeGen/TargetFrameLowering.h @@ -14,6 +14,9 @@ #define LLVM_CODEGEN_TARGETFRAMELOWERING_H #include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/StackMaps.h" +#include "llvm/IR/Function.h" #include "llvm/Support/TypeSize.h" #include @@ -33,6 +36,9 @@ enum Value { }; } +// see MCRegister.h +using MCPhysReg = uint16_t; + /// Information about stack frame layout on the target. It holds the direction /// of stack growth, the known stack alignment on entry to each function, and /// the offset to the locals area. @@ -111,6 +117,21 @@ public: return SPAdj; } + virtual const std::vector + getArgRegs(const MachineFunction &MF) const { + return {}; + }; + + virtual MCPhysReg getX8Register() const { return 0; } + + // Prepare for Cangjie StackCheck: find CJ_MCC_StackCheck, and + // store the size. + // For X86: the size is frame size + // For aarch64: the size is addsize calculated for sp + MachineBasicBlock::iterator + preCJStackCheck(MachineFunction &MF, MachineBasicBlock::iterator InsertPos, + int32_t Size, bool NeedRecoverStack = true) const; + /// getTransientStackAlignment - This method returns the number of bytes to /// which the stack pointer must be aligned at all times, even between /// calls. @@ -315,6 +336,10 @@ public: virtual StackOffset getFrameIndexReference(const MachineFunction &MF, int FI, Register &FrameReg) const; + /// getFrameIndexRefForCJ - This method should be used for cangjie function + virtual StackOffset getFrameIndexRefForCJ(const MachineFunction &MF, int FI, + Register &FrameReg) const; + /// Same as \c getFrameIndexReference, except that the stack pointer (as /// opposed to the frame pointer) will be the preferred value for \p /// FrameReg. This is generally used for emitting statepoint or EH tables that diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h index 72f69f4c6..cfd90a3d6 100644 --- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h +++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h @@ -334,6 +334,18 @@ public: return false; } + virtual bool isLEA64r(const MachineInstr &MI) const { + return false; + } + + virtual bool isADDXri(const MachineInstr &MI) const { + return false; + } + + virtual bool isORRXri(const MachineInstr &MI) const { + return false; + } + /// Compute the size in bytes and offset within a stack slot of a spilled /// register or subregister. /// diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h index 1bb2a8e50..dee78cc0d 100644 --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -4326,6 +4326,11 @@ public: return DL.isLittleEndian(); } + virtual bool + isCangjieGetFPStateInstr(unsigned Opcode) const { + return false; + } + /// Returns a 0 terminated array of registers that can be safely used as /// scratch registers. virtual const MCPhysReg *getScratchRegisters(CallingConv::ID CC) const { diff --git a/llvm/include/llvm/CodeGen/TargetRegisterInfo.h b/llvm/include/llvm/CodeGen/TargetRegisterInfo.h index 04369a5bf..1fa21a11f 100644 --- a/llvm/include/llvm/CodeGen/TargetRegisterInfo.h +++ b/llvm/include/llvm/CodeGen/TargetRegisterInfo.h @@ -1120,6 +1120,13 @@ public: virtual bool isNonallocatableRegisterCalleeSave(MCRegister Reg) const { return false; } + + virtual MCPhysReg getX8Register() const { return 0; } + + virtual const std::vector + getArgRegs(const MachineFunction &MF) const { + return {}; + } }; //===----------------------------------------------------------------------===// diff --git a/llvm/include/llvm/IR/Argument.h b/llvm/include/llvm/IR/Argument.h index 3b74853cd..52b14820e 100644 --- a/llvm/include/llvm/IR/Argument.h +++ b/llvm/include/llvm/IR/Argument.h @@ -28,6 +28,7 @@ namespace llvm { class Argument final : public Value { Function *Parent; unsigned ArgNo; + uint64_t EscapeArgInfo; friend class Function; void setParent(Function *parent); @@ -48,6 +49,14 @@ public: return ArgNo; } + /// Return arg escapeInfo to other param or escape global. + /// highest bit represent escape global and others escape to ArgNo of bit set. + uint64_t getEscapeInfo() const { + assert(Parent && "can't get number of unparented arg."); + return EscapeArgInfo; + } + + void setEscapeInfo(uint64_t EscapeInfo) { EscapeArgInfo = EscapeInfo; } /// Return true if this argument has the nonnull attribute. Also returns true /// if at least one byte is known to be dereferenceable and the pointer is in /// addrspace(0). diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td index ea4bf8020..f468e0c42 100644 --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -57,6 +57,10 @@ def AllocKind: IntAttr<"allockind", [FnAttr]>; /// Parameter is the pointer to be manipulated by the allocator function. def AllocatedPointer : EnumAttr<"allocptr", [ParamAttr]>; +/// Parameter is the stack pointer for cangjie. +/// Used for PartialEscapeAnalysis and memcpy opt of CJLateOpt. +def CJStackPointer : EnumAttr<"cjstackptr", [ParamAttr]>; + /// The result of the function is guaranteed to point to a number of bytes that /// we can determine if we know the value of the function's arguments. def AllocSize : IntAttr<"allocsize", [FnAttr]>; diff --git a/llvm/include/llvm/IR/CJIntrinsics.h b/llvm/include/llvm/IR/CJIntrinsics.h new file mode 100644 index 000000000..afc6ac1ac --- /dev/null +++ b/llvm/include/llvm/IR/CJIntrinsics.h @@ -0,0 +1,98 @@ +//===- llvm/IR/CJIntrinsics.h - Cangjie Intrinsic Wrappers ------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file contains utility functions and wrapper class to cangjie intrinsics. +// And define a set of arg-index of the cangjie intrinsic instruction. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_IR_CJINTRINSICS_H +#define LLVM_IR_CJINTRINSICS_H + +#include "llvm/IR/IntrinsicInst.h" + +using namespace llvm; + +namespace cangjie { + +struct GCReadRef { + enum { BaseObj, FieldPtr }; +}; + +struct GCReadStaticRef { + enum { Ptr }; +}; + +struct GCWriteRef { + enum { Val, BaseObj, FieldPtr }; +}; + +struct GCWriteStaticRef { + enum { Val, Ptr }; +}; + +struct GCReadStruct { + enum { Dst, BaseObj, Src, Size }; +}; + +struct GCWriteStruct { + enum { BaseObj, Dst, Src, Size }; +}; + +struct GCReadStaticStruct { + enum { Dst, Src, Size }; +}; + +struct GCWriteStaticStruct { + enum { Dst, Src, Size }; +}; + +struct GCReadGeneric { + enum { DstPtr, BaseObj, SrcPtr, Size }; +}; + +struct GCWriteGeneric { + enum { BaseObj, DstPtr, SrcPtr, Size }; +}; + +struct AssignGeneric { + enum { DstPtr, SrcPtr, TypeInfo }; +}; + +struct ArrayCopy { + enum { DstObj, DstPtr, SrcObj, SrcPtr, Size }; +}; + +struct AtomicLoad { + enum { Obj, Field, Order }; +}; + +struct AtomicStore { + enum { Ref, Obj, Field, Order }; +}; + +struct AtomicSwap { + enum { Ref, Obj, Field, Order }; +}; + +struct AtomicCompareSwap { + enum { OldRef, NewRef, Obj, Field, SuccOrder, FailOrder }; +}; + +Value *getBaseObj(const CallBase *CI); +Value *getValueArg(const CallBase *CI); +Value *getPointerArg(const CallBase *CI); +Value *getDest(const CallBase *CI); +Value *getSource(const CallBase *CI); +Value *getSize(const CallBase *CI); +Value *getAtomicOrder(const CallBase *CI); +} // namespace cangjie + +#endif diff --git a/llvm/include/llvm/IR/CJStructTypeGCInfo.h b/llvm/include/llvm/IR/CJStructTypeGCInfo.h new file mode 100644 index 000000000..6e4840a2f --- /dev/null +++ b/llvm/include/llvm/IR/CJStructTypeGCInfo.h @@ -0,0 +1,79 @@ +//===- llvm/IR/CJStructTypeGCInfo.h - gc info utilities ----------*-C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file contains utility functions to generate gc info for llvm types +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_IR_CJStructTypeGCInfo_H +#define LLVM_IR_CJStructTypeGCInfo_H + +#include "llvm/IR/Constants.h" +#include "llvm/IR/Module.h" +#include +#include + +namespace llvm { +struct BitMapInfo { + bool HasRef = false; + std::string BMStr; +}; + +struct TypeGCInfo { + uint32_t ObjSize = 0; + BitMapInfo BMInfo; + Type *ElementType = nullptr; +}; + +struct CommonBitmap { + explicit CommonBitmap(Module &M) : M(M), DL(M.getDataLayout()) {} + Constant *insertBitMapU64(const std::string &BMStr, Type *CommonBMType); + Constant *insertBitMapGV(const std::string &BMStr, Type *CommonBMType, + const std::string &BitMapName); + Constant *getOrInsertBitMap(const std::string &BMStr, Type *CommonBMType, + const std::string &BitMapName); + + Module &M; + const DataLayout &DL; + std::unordered_map BitMapStr2Constant; +}; + +class CJStructTypeGCInfo : public CommonBitmap { +public: + explicit CJStructTypeGCInfo(Module &Module) : CommonBitmap(Module) { + PtrSize = DL.getPointerSize(); + assert(PtrSize == + DL.getABITypeAlignment(Type::getInt8PtrTy(M.getContext())) && + "PtrSize should be equal to PtrAlignment"); + } + ~CJStructTypeGCInfo() = default; + + TypeGCInfo &getOrInsertTypeGCInfo(StructType *ST, bool IsArrayType = false); + +private: + bool isReferenceType(Type *T) { + return (T->isPointerTy() && T->getPointerAddressSpace() == 1); + } + + TypeGCInfo &insertArrayTypeGCInfo(StructType *ST); + + TypeGCInfo &insertNonArrayTypeGCInfo(StructType *ST); + + void getOrInsertArrayTypeGCInfo(ArrayType *AT, BitMapInfo &BMInfo, + uint64_t &CurSize); + + void fillBMInfo(Type *Ty, BitMapInfo &BMInfo, uint64_t &CurSize); + + unsigned PtrSize; + std::unordered_map Type2GCInfo; +}; +} // end namespace llvm + +#endif // LLVM_IR_CJStructTypeGCInfo_H diff --git a/llvm/include/llvm/IR/CallingConv.h b/llvm/include/llvm/IR/CallingConv.h index fd2854246..40bddd170 100644 --- a/llvm/include/llvm/IR/CallingConv.h +++ b/llvm/include/llvm/IR/CallingConv.h @@ -252,6 +252,9 @@ namespace CallingConv { /// M68k_INTR - Calling convention used for M68k interrupt routines. M68k_INTR = 101, + /// Calling convention for Cangjie gc + CangjieGC = 102, + /// The highest possible calling convention ID. Must be some 2^k - 1. MaxID = 1023 }; diff --git a/llvm/include/llvm/IR/DerivedTypes.h b/llvm/include/llvm/IR/DerivedTypes.h index f505fd3f3..d548c3849 100644 --- a/llvm/include/llvm/IR/DerivedTypes.h +++ b/llvm/include/llvm/IR/DerivedTypes.h @@ -300,6 +300,8 @@ public: /// Specify a body for an opaque identified type. void setBody(ArrayRef Elements, bool isPacked = false); + /// For cangjie frontend, allowing re-entry + void setBodyForCJ(ArrayRef Elements); template std::enable_if_t::value, void> @@ -658,6 +660,8 @@ public: return PointerType::get(C, 0); } + Type *getElementType() const { return PointeeTy; } + /// This constructs a pointer type with the same pointee type as input /// PointerType (or opaque pointer if the input PointerType is opaque) and the /// given address space. This is only useful during the opaque pointer diff --git a/llvm/include/llvm/IR/DiagnosticInfo.h b/llvm/include/llvm/IR/DiagnosticInfo.h index da37801b6..7d73a2f9a 100644 --- a/llvm/include/llvm/IR/DiagnosticInfo.h +++ b/llvm/include/llvm/IR/DiagnosticInfo.h @@ -21,6 +21,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/IR/DebugLoc.h" +#include "llvm/IR/Function.h" #include "llvm/Support/CBindingWrapping.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/SourceMgr.h" @@ -211,7 +212,7 @@ public: const char *getResourceName() const { return ResourceName; } uint64_t getResourceSize() const { return ResourceSize; } uint64_t getResourceLimit() const { return ResourceLimit; } - + std::string GetDemangleName() const; /// \see DiagnosticInfo::print. void print(DiagnosticPrinter &DP) const override; diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def index c7cb59b13..7413c08f5 100644 --- a/llvm/include/llvm/IR/FixedMetadataKinds.def +++ b/llvm/include/llvm/IR/FixedMetadataKinds.def @@ -47,3 +47,9 @@ LLVM_FIXED_MD_KIND(MD_func_sanitize, "func_sanitize", 32) LLVM_FIXED_MD_KIND(MD_exclude, "exclude", 33) LLVM_FIXED_MD_KIND(MD_memprof, "memprof", 34) LLVM_FIXED_MD_KIND(MD_callsite, "callsite", 35) +LLVM_FIXED_MD_KIND(MD_cj_agg, "AggType", 36) +LLVM_FIXED_MD_KIND(MD_virtual_call, "cj_vtable_func", 37) +LLVM_FIXED_MD_KIND(MD_intro_type, "IntroType", 38) +LLVM_FIXED_MD_KIND(MD_func_table, "FuncTable", 39) +LLVM_FIXED_MD_KIND(MD_untrusted_ref, "untrusted_ref", 40) +LLVM_FIXED_MD_KIND(MD_obj_type, "objType", 41) diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h index 7945c64c8..f6b3a339e 100644 --- a/llvm/include/llvm/IR/Function.h +++ b/llvm/include/llvm/IR/Function.h @@ -74,6 +74,7 @@ private: BasicBlockListType BasicBlocks; ///< The basic blocks mutable Argument *Arguments = nullptr; ///< The formal arguments size_t NumArgs; + uint64_t EscapedInfo = 0; std::unique_ptr SymTab; ///< Symbol table of args/instructions AttributeList AttributeSets; ///< Parameter attributes @@ -209,6 +210,10 @@ public: /// returns Intrinsic::not_intrinsic! bool isIntrinsic() const { return HasLLVMReservedName; } + /// isCangjieIntrinsic - Returns true if IID is an Cangjie intrinsic specific. + /// If it is a generic intrinsic or other function, false is returned. + bool isCJIntrinsic() const; + /// isTargetIntrinsic - Returns true if IID is an intrinsic specific to a /// certain target. If it is a generic intrinsic false is returned. static bool isTargetIntrinsic(Intrinsic::ID IID); @@ -223,6 +228,8 @@ public: /// getIntrinsicID() returns Intrinsic::not_intrinsic. bool isConstrainedFPIntrinsic() const; + bool hasCangjieGC() const { return hasGC() && getGC() == "cangjie"; } + static Intrinsic::ID lookupIntrinsicID(StringRef Name); /// Recalculate the ID for this function if it is an Intrinsic defined @@ -891,6 +898,52 @@ public: /// Return value: true => null pointer dereference is not undefined. bool nullPointerIsDefined() const; + bool isCangjieSafePoint() const { + return getName().equals("CJ_MCC_HandleSafepoint"); + } + + bool isGetCJThreadId() const { + return getName().equals("GetCJThreadIdForMutexOpt"); + } + + bool isCangjieNewObject() const { + return getName().isCangjieNewObjFunction(); + } + + bool isCangjieNativeStub(const Function &Caller) const { + return hasFnAttribute("cj2c") || + // @C can be called by native and CJ func. only native call need stub + (hasFnAttribute("c2cj") && Caller.hasFnAttribute("cjstub")); + } + + bool isCangjieStackCheck() const { + return getName().equals("CJ_MCC_StackCheck"); + } + + bool isCangjieThrowException() const { + return getName().equals("CJ_MCC_ThrowException"); + } + + bool isCangjieIntrinsic() const { + return isIntrinsic() && getName().startswith("llvm.cj."); + } + + void setEscapeAnalysis() { + addFnAttr(StringRef("EscapeAnalysis")); + } + + bool hasEscapeAnalysis() const { + return hasFnAttribute("EscapeAnalysis"); + } + + void setEscapeInfo(uint64_t EI) { + EscapedInfo = EI; + } + + uint64_t getEscapeInfo() { + return EscapedInfo; + } + private: void allocHungoffUselist(); template void setHungoffOperand(Constant *C); diff --git a/llvm/include/llvm/IR/GlobalVariable.h b/llvm/include/llvm/IR/GlobalVariable.h index e772964fc..f89a08d11 100644 --- a/llvm/include/llvm/IR/GlobalVariable.h +++ b/llvm/include/llvm/IR/GlobalVariable.h @@ -159,6 +159,51 @@ public: isExternallyInitializedConstant = Val; } + bool isCJMeta() const { + return hasAttribute("CFileKlass") || hasAttribute("CFileGCTib") || + hasAttribute("CFileReflect") || hasAttribute("cj_generic_ti") || + hasAttribute("CFileMTable") || hasAttribute("CJGlobalValue") || + hasAttribute("CFileStaticGenericTI") || + hasAttribute("InnerTypeExtensions") || + hasAttribute("OuterTypeExtensions") || hasAttribute("cj_tt") || + hasAttribute("CJTypeName") || hasAttribute("CJTIOffsets") || + hasAttribute("CJTITypeArgs") || hasAttribute("CJTIFields") || + hasAttribute("CJTTFieldsFns") || hasAttribute("CJTIUpperBounds") || + hasAttribute("CJFuncTable") || getName().equals("cj.sdk.version"); + } + + bool isCJSDKVersion() const { return getName().equals("cj.sdk.version"); } + bool isCJTypeInfo() const { return hasAttribute("CFileKlass"); } + bool isCJTypeTemplate() const { return hasAttribute("cj_tt"); } + bool isCJTypeName() const { return hasAttribute("CJTypeName"); } + bool isCJTIOffsets() const { return hasAttribute("CJTIOffsets"); } + bool isCJTITypeArgs() const { return hasAttribute("CJTITypeArgs"); } + bool isCJTIFields() const { return hasAttribute("CJTIFields"); } + bool isCJTTFieldsFns() const { return hasAttribute("CJTTFieldsFns"); } + bool isCJFunctableTable() const { return hasAttribute("CJFuncTable"); } + bool isCJGlobalValue() const { return hasAttribute("CJGlobalValue"); } + bool isCJGCTib() const { return hasAttribute("CFileGCTib"); } + bool isCJMTable() const { return hasAttribute("CFileMTable"); } + bool isCJInnerTypeExtensions() const { + return hasAttribute("InnerTypeExtensions"); + } + bool isCJOuterTypeExtensions() const { + return hasAttribute("OuterTypeExtensions"); + } + bool isCJReflectPkgInfo() const { + return hasAttribute("CFileReflect") && getName().endswith(".packageInfo"); + } + bool isCJReflectGV() const { + return hasAttribute("CFileReflect") && !getName().endswith(".packageInfo"); + } + bool isCJReflectGeneticTI() const { return hasAttribute("cj_generic_ti"); } + bool isCJStaticGenericTI() const { + return hasAttribute("CFileStaticGenericTI"); + } + bool isCJReflectUpperBounds() const { + return hasAttribute("CJTIUpperBounds"); + } + /// copyAttributesFrom - copy all additional attributes (those not needed to /// create a GlobalVariable) from the GlobalVariable Src to this one. void copyAttributesFrom(const GlobalVariable *Src); diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h index 0d3ffba95..98f0f55ab 100644 --- a/llvm/include/llvm/IR/IRBuilder.h +++ b/llvm/include/llvm/IR/IRBuilder.h @@ -584,6 +584,11 @@ public: MDNode *ScopeTag = nullptr, MDNode *NoAliasTag = nullptr); + CallInst *CreateCJMemSet(Value *Ptr, Value *Val, Value *Size, MaybeAlign Align, + bool isVolatile = false, MDNode *TBAATag = nullptr, + MDNode *ScopeTag = nullptr, + MDNode *NoAliasTag = nullptr); + /// Create and insert an element unordered-atomic memset of the region of /// memory starting at the given pointer to the given value. /// @@ -810,6 +815,15 @@ public: ArrayRef GCArgs, const Twine &Name = ""); + /// Create a call to the experimental.gc.statepoint intrinsic to + /// start a new statepoint sequence. + CallInst *CreateCJGCStatepointCall(uint64_t ID, uint32_t NumPatchBytes, + FunctionCallee ActualCallee, + uint32_t Flags, ArrayRef CallArgs, + ArrayRef GCArgs, + ArrayRef StructArgs, + const Twine &Name = ""); + /// Create an invoke to the experimental.gc.statepoint intrinsic to /// start a new statepoint sequence. InvokeInst * @@ -817,7 +831,8 @@ public: FunctionCallee ActualInvokee, BasicBlock *NormalDest, BasicBlock *UnwindDest, ArrayRef InvokeArgs, Optional> DeoptArgs, - ArrayRef GCArgs, const Twine &Name = ""); + ArrayRef GCArgs, + const Twine &Name = ""); /// Create an invoke to the experimental.gc.statepoint intrinsic to /// start a new statepoint sequence. @@ -836,7 +851,16 @@ public: FunctionCallee ActualInvokee, BasicBlock *NormalDest, BasicBlock *UnwindDest, ArrayRef InvokeArgs, Optional> DeoptArgs, - ArrayRef GCArgs, const Twine &Name = ""); + ArrayRef GCArgs, + const Twine &Name = ""); + + /// Create an invoke to the cj.gc.statepoint intrinsic to + /// start a new statepoint sequence. + InvokeInst *CreateCJGCStatepointInvoke( + uint64_t ID, uint32_t NumPatchBytes, FunctionCallee ActualInvokee, + BasicBlock *NormalDest, BasicBlock *UnwindDest, uint32_t Flags, + ArrayRef InvokeArgs, ArrayRef GCArgs, + ArrayRef StructArgs, const Twine &Name = ""); /// Create a call to the experimental.gc.result intrinsic to extract /// the result from a call wrapped in a statepoint. @@ -844,6 +868,12 @@ public: Type *ResultType, const Twine &Name = ""); + /// Create a call to the cj.gc.result intrinsic to extract + /// the result from a call wrapped in a statepoint. + CallInst *CreateCJGCResult(Instruction *Statepoint, + Type *ResultType, + const Twine &Name = ""); + /// Create a call to the experimental.gc.relocate intrinsics to /// project the relocated value of one pointer from the statepoint. CallInst *CreateGCRelocate(Instruction *Statepoint, @@ -852,6 +882,14 @@ public: Type *ResultType, const Twine &Name = ""); + /// Create a call to the cj.gc.relocate intrinsics to + /// project the relocated value of one pointer from the statepoint. + CallInst *CreateCJGCRelocate(Instruction *Statepoint, + int BaseOffset, + int DerivedOffset, + Type *ResultType, + const Twine &Name = ""); + /// Create a call to the experimental.gc.pointer.base intrinsic to get the /// base pointer for the specified derived pointer. CallInst *CreateGCGetPointerBase(Value *DerivedPtr, const Twine &Name = ""); diff --git a/llvm/include/llvm/IR/InstVisitor.h b/llvm/include/llvm/IR/InstVisitor.h index 7fec081d8..87b058511 100644 --- a/llvm/include/llvm/IR/InstVisitor.h +++ b/llvm/include/llvm/IR/InstVisitor.h @@ -292,6 +292,7 @@ private: case Intrinsic::memcpy: DELEGATE(MemCpyInst); case Intrinsic::memmove: DELEGATE(MemMoveInst); case Intrinsic::memset: DELEGATE(MemSetInst); + case Intrinsic::cj_memset: DELEGATE(MemSetInst); case Intrinsic::vastart: DELEGATE(VAStartInst); case Intrinsic::vaend: DELEGATE(VAEndInst); case Intrinsic::vacopy: DELEGATE(VACopyInst); diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h index 480a559e2..70a2e2339 100644 --- a/llvm/include/llvm/IR/Instructions.h +++ b/llvm/include/llvm/IR/Instructions.h @@ -29,6 +29,7 @@ #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instruction.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/IR/OperandTraits.h" #include "llvm/IR/Use.h" #include "llvm/IR/User.h" @@ -2455,6 +2456,8 @@ public: /// Null is returned if the indices are invalid for the specified type. static Type *getIndexedType(Type *Agg, ArrayRef Idxs); + int getIndexOffset(const DataLayout &DL); + using idx_iterator = const unsigned*; inline idx_iterator idx_begin() const { return Indices.begin(); } @@ -5330,6 +5333,17 @@ inline const Value *getLoadStorePointerOperand(const Value *V) { return Load->getPointerOperand(); if (auto *Store = dyn_cast(V)) return Store->getPointerOperand(); + if (auto *CI = dyn_cast(V)) { + Intrinsic::ID ID = CI->getIntrinsicID(); + if (ID == Intrinsic::cj_gcwrite_ref) + return CI->getOperand(2); + if (ID == Intrinsic::cj_gcwrite_static_ref) + return CI->getOperand(1); + if (ID == Intrinsic::cj_gcread_ref) + return CI->getOperand(1); + if (ID == Intrinsic::cj_gcread_static_ref) + return CI->getOperand(0); + } return nullptr; } inline Value *getLoadStorePointerOperand(Value *V) { @@ -5337,6 +5351,23 @@ inline Value *getLoadStorePointerOperand(Value *V) { getLoadStorePointerOperand(static_cast(V))); } +/// A helper function that returns the value operand of a store or gcwrite +/// instruction. Returns nullptr if not store or gcwrite. +inline const Value *getStoreValueOperand(const Value *V) { + if (auto *Store = dyn_cast(V)) + return Store->getValueOperand(); + if (auto *CI = dyn_cast(V)) { + if (CI->getIntrinsicID() == Intrinsic::cj_gcwrite_ref) { + return CI->getOperand(0); + } + } + return nullptr; +} +inline Value *getStoreValueOperand(Value *V) { + return const_cast( + getStoreValueOperand(static_cast(V))); +} + /// A helper function that returns the pointer operand of a load, store /// or GEP instruction. Returns nullptr if not load, store, or GEP. inline const Value *getPointerOperand(const Value *V) { @@ -5362,6 +5393,11 @@ inline Align getLoadStoreAlignment(Value *I) { /// A helper function that returns the address space of the pointer operand of /// load or store instruction. inline unsigned getLoadStoreAddressSpace(Value *I) { + if (auto *CI = dyn_cast(I)) { + if (CI->getIntrinsicID() == Intrinsic::cj_gcwrite_ref) { + return CI->getOperand(2)->getType()->getPointerAddressSpace(); + } + } assert((isa(I) || isa(I)) && "Expected Load or Store instruction"); if (auto *LI = dyn_cast(I)) @@ -5371,6 +5407,11 @@ inline unsigned getLoadStoreAddressSpace(Value *I) { /// A helper function that returns the type of a load or store instruction. inline Type *getLoadStoreType(Value *I) { + if (auto *CI = dyn_cast(I)) { + if (CI->getIntrinsicID() == Intrinsic::cj_gcwrite_ref) { + return CI->getOperand(0)->getType(); + } + } assert((isa(I) || isa(I)) && "Expected Load or Store instruction"); if (auto *LI = dyn_cast(I)) diff --git a/llvm/include/llvm/IR/IntrinsicInst.h b/llvm/include/llvm/IR/IntrinsicInst.h index 4ff48c366..fd01aa3bd 100644 --- a/llvm/include/llvm/IR/IntrinsicInst.h +++ b/llvm/include/llvm/IR/IntrinsicInst.h @@ -107,6 +107,38 @@ public: return false; } + bool isCJStructGCRead() const { + auto ID = getIntrinsicID(); + return ID == Intrinsic::cj_gcread_static_struct || + ID == Intrinsic::cj_gcread_struct; + } + + bool isCJRefGCRead() const { + auto ID = getIntrinsicID(); + return ID == Intrinsic::cj_gcread_static_ref || + ID == Intrinsic::cj_gcread_ref; + } + + bool isCJStructGCWrite() const { + auto ID = getIntrinsicID(); + return ID == Intrinsic::cj_gcwrite_static_struct || + ID == Intrinsic::cj_gcwrite_struct; + } + + bool isCJRefGCWrite() const { + auto ID = getIntrinsicID(); + return ID == Intrinsic::cj_gcwrite_static_ref || + ID == Intrinsic::cj_gcwrite_ref; + } + + Value *getCJRefGCReadPtr() const { + if (!isCJRefGCRead()) + return nullptr; + auto ID = getIntrinsicID(); + return ID == Intrinsic::cj_gcread_static_ref ? getArgOperand(0) + : getArgOperand(1); + } + /// Check if the intrinsic might lower into a regular function call in the /// course of IR transformations static bool mayLowerToFunctionCall(Intrinsic::ID IID); @@ -1366,7 +1398,9 @@ class GCProjectionInst : public IntrinsicInst { public: static bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::experimental_gc_relocate || - I->getIntrinsicID() == Intrinsic::experimental_gc_result; + I->getIntrinsicID() == Intrinsic::experimental_gc_result || + I->getIntrinsicID() == Intrinsic::cj_gc_relocate || + I->getIntrinsicID() == Intrinsic::cj_gc_result; } static bool classof(const Value *V) { @@ -1389,7 +1423,8 @@ public: class GCRelocateInst : public GCProjectionInst { public: static bool classof(const IntrinsicInst *I) { - return I->getIntrinsicID() == Intrinsic::experimental_gc_relocate; + return I->getIntrinsicID() == Intrinsic::experimental_gc_relocate || + I->getIntrinsicID() == Intrinsic::cj_gc_relocate; } static bool classof(const Value *V) { @@ -1417,7 +1452,8 @@ public: class GCResultInst : public GCProjectionInst { public: static bool classof(const IntrinsicInst *I) { - return I->getIntrinsicID() == Intrinsic::experimental_gc_result; + return I->getIntrinsicID() == Intrinsic::experimental_gc_result || + I->getIntrinsicID() == Intrinsic::cj_gc_result; } static bool classof(const Value *V) { @@ -1425,7 +1461,6 @@ public: } }; - /// This represents the llvm.assume intrinsic. class AssumeInst : public IntrinsicInst { public: diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index d46fa4fbf..e29a51efb 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -180,6 +180,9 @@ class LLVMQualPointerType class LLVMPointerType : LLVMQualPointerType; +class LLVMGCPointerType + : LLVMQualPointerType; + class LLVMAnyPointerType : LLVMType{ LLVMType ElTy = elty; @@ -240,26 +243,38 @@ let isAny = true in { def llvm_anyfloat_ty : LLVMType; def llvm_anyvector_ty : LLVMType; } -def llvm_i1_ty : LLVMType; -def llvm_i8_ty : LLVMType; -def llvm_i16_ty : LLVMType; -def llvm_i32_ty : LLVMType; -def llvm_i64_ty : LLVMType; -def llvm_i128_ty : LLVMType; -def llvm_half_ty : LLVMType; -def llvm_bfloat_ty : LLVMType; -def llvm_float_ty : LLVMType; -def llvm_double_ty : LLVMType; -def llvm_f80_ty : LLVMType; -def llvm_f128_ty : LLVMType; -def llvm_ppcf128_ty : LLVMType; -def llvm_ptr_ty : LLVMPointerType; // i8* -def llvm_ptrptr_ty : LLVMPointerType; // i8** -def llvm_anyptr_ty : LLVMAnyPointerType; // (space)i8* -def llvm_empty_ty : LLVMType; // { } -def llvm_descriptor_ty : LLVMPointerType; // { }* -def llvm_metadata_ty : LLVMType; // !{...} -def llvm_token_ty : LLVMType; // token +def llvm_i1_ty : LLVMType; +def llvm_i8_ty : LLVMType; +def llvm_i16_ty : LLVMType; +def llvm_i32_ty : LLVMType; +def llvm_i64_ty : LLVMType; +def llvm_i128_ty : LLVMType; +def llvm_half_ty : LLVMType; +def llvm_bfloat_ty : LLVMType; +def llvm_float_ty : LLVMType; +def llvm_double_ty : LLVMType; +def llvm_f80_ty : LLVMType; +def llvm_f128_ty : LLVMType; +def llvm_ppcf128_ty : LLVMType; +def llvm_ptr_ty : LLVMPointerType; // i8* +def llvm_i1gcptr_ty : LLVMGCPointerType; // i1 addrspace1* +def llvm_i16gcptr_ty : LLVMGCPointerType; // i16 addrspace1* +def llvm_i32gcptr_ty : LLVMGCPointerType; // i32 addrspace1* +def llvm_i64gcptr_ty : LLVMGCPointerType; // i64 addrspace1* +def llvm_anygcptr_ty : LLVMGCPointerType; // (any) addrspace1* +def llvm_halfgcptr_ty : LLVMGCPointerType; // half addrspace1* +def llvm_floatgcptr_ty : LLVMGCPointerType; // float addrspace1* +def llvm_doublegcptr_ty : LLVMGCPointerType; // double addrspace1* +def llvm_gcptrptr_ty : LLVMGCPointerType; // i8* addrspace1* +def llvm_gcptr_ty : LLVMGCPointerType; // i8 addrspace1* +def llvm_ptrptr_ty : LLVMPointerType; // i8** +def llvm_ptrgcptr_ty : LLVMPointerType; // i8 addrspace1** +def llvm_gcptrgcptr_ty : LLVMGCPointerType; // i8 addrspace1* addrspace1* +def llvm_anyptr_ty : LLVMAnyPointerType; // i8 addrspace* +def llvm_empty_ty : LLVMType; // { } +def llvm_descriptor_ty : LLVMPointerType; // { }* +def llvm_metadata_ty : LLVMType; // !{...} +def llvm_token_ty : LLVMType; // token def llvm_x86mmx_ty : LLVMType; def llvm_ptrx86mmx_ty : LLVMPointerType; // <1 x i64>* @@ -417,6 +432,198 @@ def int_gcwrite : Intrinsic<[], [IntrArgMemOnly, NoCapture>, NoCapture>]>; +//===------------------- Cangjie GC Barrier Intrinsics --------------------===// +// +def int_cj_gcwrite_ref : Intrinsic<[], + [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptrgcptr_ty], + [IntrArgMemOnly, NoCapture>, NoCapture>]>; +def int_cj_gcwrite_struct : Intrinsic<[], + [llvm_gcptr_ty, llvm_gcptr_ty, llvm_anyptr_ty, llvm_anyint_ty], + [IntrArgMemOnly, NoCapture>, NoCapture>, NoAlias>, + NoAlias>, WriteOnly>, ReadOnly>]>; +def int_cj_gcwrite_static_ref : Intrinsic<[], + [llvm_gcptr_ty, llvm_ptrgcptr_ty], + [IntrArgMemOnly, NoCapture>]>; +def int_cj_gcwrite_static_struct : Intrinsic<[], + [llvm_ptr_ty, llvm_ptr_ty, llvm_anyint_ty], + [IntrArgMemOnly, NoCapture>, NoCapture>, + NoAlias>, NoAlias>, WriteOnly>, ReadOnly>]>; +def int_cj_gcread_ref : Intrinsic<[llvm_gcptr_ty], + [llvm_gcptr_ty, llvm_gcptrgcptr_ty], + [IntrReadMem, IntrArgMemOnly, NoCapture>, NoCapture>]>; +def int_cj_gcread_weakref : Intrinsic<[llvm_gcptr_ty], + [llvm_gcptr_ty, llvm_gcptrgcptr_ty], + [IntrReadMem, IntrArgMemOnly, NoCapture>, NoCapture>]>; + +def int_cj_gcread_struct : Intrinsic<[], + [llvm_ptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_anyint_ty], + [IntrArgMemOnly, NoCapture>, NoCapture>, NoAlias>, + NoAlias>, WriteOnly>, ReadOnly>]>; +def int_cj_gcread_static_ref : Intrinsic<[llvm_gcptr_ty], + [llvm_ptrgcptr_ty], + [IntrReadMem, IntrArgMemOnly, NoCapture>]>; +def int_cj_gcread_static_struct : Intrinsic<[], + [llvm_ptr_ty, llvm_ptr_ty, llvm_anyint_ty], + [IntrArgMemOnly, NoCapture>, NoCapture>, + NoAlias>, NoAlias>, WriteOnly>, ReadOnly>]>; +def int_cj_array_copy_ref : Intrinsic<[], + [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_anyint_ty]>; +def int_cj_array_copy_struct : Intrinsic<[], + [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_anyint_ty]>; +def int_cj_copy_struct_field : Intrinsic<[], + [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_anyint_ty]>; +def int_cj_cross_access_barrier : Intrinsic<[], [llvm_i64_ty]>; +def int_cj_get_exported_ref : Intrinsic<[llvm_gcptr_ty], + [llvm_i64_ty]>; +def int_cj_remove_exported_ref : Intrinsic<[], [llvm_i64_ty]>; +def int_cj_create_export_handle : Intrinsic<[llvm_i64_ty], + [llvm_gcptr_ty]>; + +//===------------------- Cangjie runtime Intrinsics ------------------------===// +// +def int_cj_get_obj_klass : Intrinsic<[llvm_ptr_ty], + [llvm_gcptr_ty]>; +def int_cj_memset : Intrinsic<[], + [llvm_anyptr_ty, llvm_i8_ty, llvm_i64_ty, llvm_i1_ty], + [IntrArgMemOnly, IntrWriteMem, + NoCapture>, WriteOnly>]>; +def int_cj_malloc_object : Intrinsic<[llvm_gcptr_ty], + [llvm_ptr_ty, llvm_i32_ty], + [Throws]>; +def int_cj_malloc_array : Intrinsic<[llvm_gcptr_ty], + [llvm_ptr_ty, llvm_i64_ty, llvm_i64_ty], + [Throws]>; +def int_cj_division_check_sdiv : Intrinsic<[llvm_anyint_ty], + [llvm_anyint_ty, llvm_anyint_ty], + [Throws]>; +def int_cj_division_check_udiv : Intrinsic<[llvm_anyint_ty], + [llvm_anyint_ty, llvm_anyint_ty], + [Throws]>; +def int_cj_division_check_srem : Intrinsic<[llvm_anyint_ty], + [llvm_anyint_ty, llvm_anyint_ty], + [Throws]>; +def int_cj_division_check_urem : Intrinsic<[llvm_anyint_ty], + [llvm_anyint_ty, llvm_anyint_ty], + [Throws]>; +def int_cj_acquire_rawdata : Intrinsic<[llvm_ptr_ty], + [llvm_gcptr_ty, llvm_ptr_ty]>; +def int_cj_release_rawdata : Intrinsic<[], + [llvm_gcptr_ty, llvm_ptr_ty, llvm_i32_ty]>; +def int_cj_post_throw_exception : Intrinsic<[llvm_gcptr_ty], + [llvm_ptr_ty]>; +def int_cj_throw_exception : Intrinsic<[], + [llvm_gcptr_ty], + [Throws]>; +def int_cj_get_exception_wrapper : Intrinsic<[llvm_ptr_ty], + []>; +def int_cj_get_exception_typeid : Intrinsic<[llvm_i32_ty], + []>; +def int_cj_pre_initialize_package : Intrinsic<[], + [llvm_ptr_ty]>; +def int_cj_fill_in_stack_trace : Intrinsic<[llvm_gcptr_ty], + [llvm_ptr_ty, llvm_gcptr_ty]>; +def int_cj_get_real_heap_size : Intrinsic<[llvm_anyint_ty], + []>; +def int_cj_get_allocated_heap_size : Intrinsic<[llvm_anyint_ty], + []>; +def int_cj_get_max_heap_size : Intrinsic<[llvm_anyint_ty], + []>; +def int_cj_dump_heap_data : Intrinsic<[llvm_i1_ty], + [llvm_i32_ty]>; +def int_cj_get_thread_number : Intrinsic<[llvm_anyint_ty], + []>; +def int_cj_get_blocking_thread_number : Intrinsic<[llvm_anyint_ty], + []>; +def int_cj_get_native_thread_number : Intrinsic<[llvm_anyint_ty], + []>; +def int_cj_get_gc_count : Intrinsic<[llvm_anyint_ty], + []>; +def int_cj_get_gc_time_us : Intrinsic<[llvm_i64_ty], + []>; +def int_cj_get_gc_freed_size : Intrinsic<[llvm_anyint_ty], + []>; +def int_cj_start_cpu_profiling : Intrinsic<[llvm_i1_ty], + []>; +def int_cj_stop_cpu_profiling : Intrinsic<[llvm_i1_ty], + [llvm_i32_ty]>; +def int_cj_set_gc_threshold : Intrinsic<[], + [llvm_i64_ty]>; +def int_cj_invoke_gc : Intrinsic<[], + [llvm_i1_ty]>; +def int_cj_register_implicit_exception_raisers : Intrinsic<[], + [llvm_ptr_ty]>; + +//===------------------- Cangjie atomic Operations Intrinsics --------------===// +// +def int_cj_atomic_store : Intrinsic<[], + [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptrgcptr_ty, llvm_i32_ty]>; +def int_cj_atomic_load : Intrinsic<[llvm_gcptr_ty], + [llvm_gcptr_ty, llvm_gcptrgcptr_ty, llvm_i32_ty]>; +def int_cj_atomic_swap : Intrinsic<[llvm_gcptr_ty], + [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptrgcptr_ty, llvm_i32_ty]>; +def int_cj_atomic_compare_swap : Intrinsic<[llvm_i1_ty], + [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptrgcptr_ty, llvm_i32_ty, llvm_i32_ty]>; + +//===------------------- Cangjie backend Intrinsics ------------------------===// +// +def int_cj_get_fp_state : Intrinsic<[llvm_i64_ty], [llvm_anyint_ty]>; +def int_cj_reset_fp_state : Intrinsic<[], []>; +def int_cj_set_location : Intrinsic<[], []>; +def int_cj_blackhole : Intrinsic<[llvm_ptr_ty], [llvm_ptr_ty]>; + +//===------------------- Cangjie generic Intrinsics ------------------------===// +// +def int_cj_get_type_info : Intrinsic<[llvm_ptr_ty], + [llvm_ptr_ty, llvm_i32_ty, llvm_ptrptr_ty], + [IntrReadMem, IntrArgMemOnly, IntrWillReturn]>; +def int_cj_get_field_offset : Intrinsic<[llvm_i64_ty], + [llvm_ptr_ty, llvm_i64_ty, llvm_i32_ty]>; +def int_cj_is_subtype : Intrinsic<[llvm_i1_ty], + [llvm_ptr_ty, llvm_ptr_ty], + [IntrNoMem, IntrSpeculatable, IntrWillReturn, + NoCapture>, NoCapture>]>; +def int_cj_is_tupletype_of : Intrinsic<[llvm_i1_ty], + [llvm_anyptr_ty, llvm_ptr_ty, llvm_ptr_ty], + [IntrSpeculatable, IntrWillReturn, + NoCapture>, NoCapture>, NoCapture>]>; +def int_cj_is_typeinfo_equal : Intrinsic<[llvm_i1_ty], + [llvm_ptr_ty, llvm_ptr_ty]>; +def int_cj_is_reference : Intrinsic<[llvm_i1_ty], + [llvm_ptr_ty]>; +def int_cj_alloca_generic : Intrinsic<[llvm_gcptr_ty], + [llvm_ptr_ty, llvm_i32_ty], + [Throws]>; +def int_cj_gcwrite_generic : Intrinsic<[], + [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_i32_ty], + [IntrArgMemOnly, IntrWillReturn, + NoCapture>, + NoCapture>, WriteOnly>, + NoCapture>, NoAlias>, ReadOnly>]>; +def int_cj_gcread_generic : Intrinsic<[], + [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_i32_ty], + [IntrArgMemOnly, IntrWillReturn, + NoCapture>, NoAlias>, WriteOnly>, + NoCapture>, ReadOnly>, + NoCapture>, ReadOnly>]>; +def int_cj_get_mtable_func : Intrinsic<[llvm_ptr_ty], + [llvm_ptr_ty, llvm_ptr_ty, llvm_i64_ty], + [IntrNoMem, IntrWillReturn, IntrSpeculatable]>; +def int_cj_get_vtable_func : Intrinsic<[llvm_ptr_ty], + [llvm_ptr_ty, llvm_i64_ty, llvm_i64_ty]>; +def int_cj_assign_generic : Intrinsic<[], + [llvm_gcptr_ty, llvm_gcptr_ty, llvm_ptr_ty], + [IntrArgMemOnly, IntrWillReturn, + NoCapture>, NoAlias>, WriteOnly>, + NoCapture>, NoAlias>, ReadOnly>, + NoCapture>, NoAlias>, ReadOnly>]>; +def int_cj_malloc_array_generic : Intrinsic<[llvm_gcptr_ty], + [llvm_ptr_ty, llvm_i64_ty], + [Throws]>; +def int_cj_array_copy_generic : Intrinsic<[], + [llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_gcptr_ty, llvm_anyint_ty]>; +def int_cj_gcwrite_generic_payload : Intrinsic<[], + [llvm_gcptr_ty, llvm_ptr_ty, llvm_i32_ty]>; + //===------------------- ObjC ARC runtime Intrinsics --------------------===// // // Note these are to support the Objective-C ARC optimizer which wants to @@ -1257,6 +1464,25 @@ def int_experimental_gc_get_pointer_offset : Intrinsic<[llvm_i64_ty], [llvm_anyptr_ty], [IntrNoMem, IntrWillReturn, ReadNone>, NoCapture>]>; + +//===---------------- Cangjie Garbage Collection Intrinsics ---------------===// +def int_cj_gc_statepoint : Intrinsic<[llvm_token_ty], + [llvm_vararg_ty], + [Throws]>; + +def int_cj_gc_result : Intrinsic<[llvm_any_ty], [llvm_token_ty], + [IntrNoMem]>; + +def int_cj_gc_relocate : Intrinsic<[llvm_any_ty], + [llvm_token_ty, llvm_i32_ty, + llvm_i32_ty], + [IntrNoMem, ImmArg>, + ImmArg>]>; + +def int_cj_stack_relocate : Intrinsic<[llvm_any_ty], + [llvm_token_ty, llvm_i32_ty], + [IntrNoMem, ImmArg>]>; + //===------------------------ Coroutine Intrinsics ---------------===// // These are documented in docs/Coroutines.rst diff --git a/llvm/include/llvm/IR/IntrinsicsAArch64.td b/llvm/include/llvm/IR/IntrinsicsAArch64.td index fc66bdfc3..a0011f532 100644 --- a/llvm/include/llvm/IR/IntrinsicsAArch64.td +++ b/llvm/include/llvm/IR/IntrinsicsAArch64.td @@ -707,6 +707,17 @@ let TargetPrefix = "aarch64" in { def int_aarch64_get_fpcr : FPCR_Get_Intrinsic; def int_aarch64_set_fpcr : FPCR_Set_Intrinsic; +let TargetPrefix = "aarch64" in { + class FPSR_Get_Intrinsic + : DefaultAttrsIntrinsic<[llvm_i64_ty], [], [IntrNoMem, IntrHasSideEffects]>; + class FPSR_Reset_Intrinsic + : DefaultAttrsIntrinsic<[], [], [IntrNoMem, IntrHasSideEffects]>; +} + +// FPSR +def int_aarch64_get_fpsr : FPSR_Get_Intrinsic; +def int_aarch64_reset_fpsr : FPSR_Reset_Intrinsic; + // Armv8.5-A Random number generation intrinsics def int_aarch64_rndr : RNDR_Intrinsic; def int_aarch64_rndrrs : RNDR_Intrinsic; diff --git a/llvm/include/llvm/IR/LLVMContext.h b/llvm/include/llvm/IR/LLVMContext.h index 91712df15..e083074f6 100644 --- a/llvm/include/llvm/IR/LLVMContext.h +++ b/llvm/include/llvm/IR/LLVMContext.h @@ -95,6 +95,7 @@ public: OB_gc_live = 5, // "gc-live" OB_clang_arc_attachedcall = 6, // "clang.arc.attachedcall" OB_ptrauth = 7, // "ptrauth" + OB_struct_live = 8, // "struct-live" }; /// getMDKindID - Return a unique non-zero ID for the specified metadata kind. diff --git a/llvm/include/llvm/IR/LegacyPassManagers.h b/llvm/include/llvm/IR/LegacyPassManagers.h index 41c11d26a..fd0d62dd7 100644 --- a/llvm/include/llvm/IR/LegacyPassManagers.h +++ b/llvm/include/llvm/IR/LegacyPassManagers.h @@ -15,8 +15,10 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/IR/IRPrintingPasses.h" #include "llvm/Pass.h" #include @@ -91,6 +93,9 @@ class Module; class StringRef; class Value; class PMDataManager; +namespace legacy { +class FunctionPassManagerImpl; +} // enums for debugging strings enum PassDebuggingString { @@ -326,6 +331,13 @@ public: /// through getAnalysis interface. virtual void addLowerLevelRequiredPass(Pass *P, Pass *RequiredPass); + /// Add RequiredPass into list of lower level passes required by pass P. + /// implment of on the fly managers, called by CGPassManger && MPPassManager + void addLowerLevelRequiredPassImpl( + MapVector + &OnTheFlyManagers, + Pass *P, Pass *RequiredPass); + virtual std::tuple getOnTheFlyPass(Pass *P, AnalysisID PI, Function &F); @@ -508,7 +520,73 @@ public: return PMT_FunctionPassManager; } }; +namespace legacy { +//===----------------------------------------------------------------------===// +// FunctionPassManagerImpl +// +/// FunctionPassManagerImpl manages FPPassManagers +class FunctionPassManagerImpl : public Pass, + public PMDataManager, + public PMTopLevelManager { + virtual void anchor(); + +private: + bool WasRun; + +public: + static char ID; + explicit FunctionPassManagerImpl() + : Pass(PT_PassManager, ID), PMDataManager(), + PMTopLevelManager(new FPPassManager()), WasRun(false) {} + + /// \copydoc FunctionPassManager::add() + void add(Pass *P) { schedulePass(P); } + + /// createPrinterPass - Get a function printer pass. + Pass *createPrinterPass(raw_ostream &O, + const std::string &Banner) const override { + return createPrintFunctionPass(O, Banner); + } + + // Prepare for running an on the fly pass, freeing memory if needed + // from a previous run. + void releaseMemoryOnTheFly(); + + /// run - Execute all of the passes scheduled for execution. Keep track of + /// whether any of the passes modifies the module, and if so, return true. + bool run(Function &F); + + /// doInitialization - Run all of the initializers for the function passes. + /// + bool doInitialization(Module &M) override; + + /// doFinalization - Run all of the finalizers for the function passes. + /// + bool doFinalization(Module &M) override; + + PMDataManager *getAsPMDataManager() override { return this; } + Pass *getAsPass() override { return this; } + PassManagerType getTopLevelPassManagerType() override { + return PMT_FunctionPassManager; + } + /// Pass Manager itself does not invalidate any analysis info. + void getAnalysisUsage(AnalysisUsage &Info) const override { + Info.setPreservesAll(); + } + + FPPassManager *getContainedManager(unsigned N) { + assert(N < PassManagers.size() && "Pass number out of range!"); + FPPassManager *FP = static_cast(PassManagers[N]); + return FP; + } + + void dumpPassStructure(unsigned Offset) override { + for (unsigned I = 0; I < getNumContainedManagers(); ++I) + getContainedManager(I)->dumpPassStructure(Offset); + } +}; +} // namespace legacy } #endif diff --git a/llvm/include/llvm/IR/Module.h b/llvm/include/llvm/IR/Module.h index 24da08d70..84dcf96d8 100644 --- a/llvm/include/llvm/IR/Module.h +++ b/llvm/include/llvm/IR/Module.h @@ -370,6 +370,9 @@ public: FunctionCallee getOrInsertFunction(StringRef Name, FunctionType *T); + Function *declareCJRuntimeFunc(StringRef Name, FunctionType *T, + bool GCLeafFunc, bool GCMalloc = false); + /// Look up the specified function in the module symbol table. If it does not /// exist, add a prototype for the function and return it. This function /// guarantees to return a constant of pointer to the specified function type diff --git a/llvm/include/llvm/IR/SafepointIRVerifier.h b/llvm/include/llvm/IR/SafepointIRVerifier.h index 246d236ad..0f4f31af1 100644 --- a/llvm/include/llvm/IR/SafepointIRVerifier.h +++ b/llvm/include/llvm/IR/SafepointIRVerifier.h @@ -18,6 +18,10 @@ #ifndef LLVM_IR_SAFEPOINTIRVERIFIER_H #define LLVM_IR_SAFEPOINTIRVERIFIER_H +#include "llvm/ADT/SmallSet.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/IRBuilder.h" #include "llvm/IR/PassManager.h" namespace llvm { @@ -41,6 +45,43 @@ public: PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); }; + +/// TODO: Once we can get to the GCStrategy, this becomes +/// Optional isGCManagedPointer(const Type *Ty) const override { +bool isGCPointerType(Type *T); + +/// Return true if this base is a gc pointer type. +bool isGCPtr(Value *V); + +/// Returns true if this type contains a gc pointer whether we know how to +/// handle that type or not. +bool containsGCPtrType(Type *Ty); + +/// Return true if this type is one which a) is a gc pointer or contains a GC +/// pointer and b) is of a type this code expects to encounter as a live value. +/// (The insertion code will assert that a type which matches (a) and not (b) +/// is not encountered.) +bool isHandledGCPointerType(Type *T); + +/// Return true if a stack var pointer to gc ref or a struct var on stack +/// contains gc ref. +bool isMemoryContainsGCPtrType(Type *T); + +Value *findMemoryBasePointer(Value *V); + +Instruction *createStoreOrMems(CallBase *CI, IRBuilder<> &Builder); + +bool isCJWriteBarrierIntrinsic(Intrinsic::ID IID); +bool isCJReadBarrierIntrinsic(Intrinsic::ID IID); +bool isCJMemcpyIntrinsic(Intrinsic::ID IID); +bool isCJAtomicIntrinsic(Intrinsic::ID IID); +bool isSafepointCall(CallBase *CB); +bool isInt8AS1Pty(Type *Ty); +bool isInt8AS0Pty(Value *Ptr); +bool maybeCJFinalizerObj(Value *V); +Type *getUniqueActualType(Value *V); +StructType *getLastStructTypeContain(Value *V); +void getAllStructTypes(Value *V, SmallSet &STs); } #endif // LLVM_IR_SAFEPOINTIRVERIFIER_H diff --git a/llvm/include/llvm/IR/Statepoint.h b/llvm/include/llvm/IR/Statepoint.h index 559840a33..70c98f02b 100644 --- a/llvm/include/llvm/IR/Statepoint.h +++ b/llvm/include/llvm/IR/Statepoint.h @@ -34,6 +34,15 @@ namespace llvm { +// Cangjie Statepoint ID +enum CJStatepointID : uint64_t { + Default = 0, + // 1 and 2 are reserved for others + Safepoint = 3, + StackCheck = 4, + NewArrayFast = 5, +}; + /// The statepoint intrinsic accepts a set of flags as its third argument. /// Valid values come out of this set. enum class StatepointFlags { @@ -66,7 +75,8 @@ public: static bool classof(const CallBase *I) { if (const Function *CF = I->getCalledFunction()) - return CF->getIntrinsicID() == Intrinsic::experimental_gc_statepoint; + return CF->getIntrinsicID() == Intrinsic::experimental_gc_statepoint || + CF->getIntrinsicID() == Intrinsic::cj_gc_statepoint; return false; } @@ -120,14 +130,23 @@ public: /// Return the type of the value returned by the call underlying the /// statepoint. Type *getActualReturnType() const { - auto *FT = cast(getParamElementType(CalledFunctionPos)); - return FT->getReturnType(); + Function *CalledFunction = getActualCalledFunction(); + if (CalledFunction) { + return CalledFunction->getReturnType(); + } else { + Type *TargetElemType = getParamElementType(CalledFunctionPos); + if (TargetElemType == nullptr) { + TargetElemType = getArgOperand(CalledFunctionPos) + ->getType() + ->getNonOpaquePointerElementType(); + } + return cast(TargetElemType)->getReturnType(); + } } - /// Return the number of arguments to the underlying call. size_t actual_arg_size() const { return getNumCallArgs(); } - /// Return an iterator to the begining of the arguments to the underlying call + /// Return an iterator to the beginning of the arguments to the underlying call const_op_iterator actual_arg_begin() const { assert(CallArgsBeginPos <= (int)arg_size()); return arg_begin() + CallArgsBeginPos; @@ -175,7 +194,7 @@ public: return make_range(deopt_begin(), deopt_end()); } - /// Returns an iterator to the begining of the argument range describing gc + /// Returns an iterator to the beginning of the argument range describing gc /// values for the statepoint. const_op_iterator gc_args_begin() const { if (auto Opt = getOperandBundle(LLVMContext::OB_gc_live)) @@ -195,6 +214,25 @@ public: return make_range(gc_args_begin(), gc_args_end()); } + /// Returns an iterator to the beginning of the struct-live range for the + /// statepoint. + const_op_iterator struct_args_begin() const { + if (auto Opt = getOperandBundle(LLVMContext::OB_struct_live)) + return Opt->Inputs.begin(); + return arg_end(); + } + + /// Return an end iterator for the struct-live range + const_op_iterator struct_args_end() const { + if (auto Opt = getOperandBundle(LLVMContext::OB_struct_live)) + return Opt->Inputs.end(); + return arg_end(); + } + + /// range adapter for struct arguments + iterator_range struct_args() const { + return make_range(struct_args_begin(), struct_args_end()); + } /// Get list of all gc reloactes linked to this statepoint /// May contain several relocations for the same base/derived pair. @@ -248,6 +286,8 @@ StatepointDirectives parseStatepointDirectivesFromAttrs(AttributeList AS); /// directive. bool isStatepointDirectiveAttr(Attribute Attr); +uint64_t getCJStatepointID(CallBase *Call); + } // end namespace llvm #endif // LLVM_IR_STATEPOINT_H diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h index 8cf31c08d..b6df3bdc7 100644 --- a/llvm/include/llvm/InitializePasses.h +++ b/llvm/include/llvm/InitializePasses.h @@ -88,7 +88,14 @@ void initializeBranchProbabilityInfoWrapperPassPass(PassRegistry&); void initializeBranchRelaxationPass(PassRegistry&); void initializeBreakCriticalEdgesPass(PassRegistry&); void initializeBreakFalseDepsPass(PassRegistry&); +void initializeCangjieSpecificOptLegacyPassPass(PassRegistry &); void initializeCanonicalizeFreezeInLoopsPass(PassRegistry &); +void initializeCJBarrierOptLegacyPassPass(PassRegistry &); +void initializeCJBarrierSplitLegacyPassPass(PassRegistry &); +void initializeCJRuntimeLoweringLegacyPassPass(PassRegistry &); +void initializeCJGenericIntrinsicOptLegacyPassPass(PassRegistry &); +void initializeCJIRVerifierLegacyPassPass(PassRegistry &); +void initializeCJLoopFloatOptLegacyPassPass(PassRegistry &); void initializeCFGOnlyPrinterLegacyPassPass(PassRegistry&); void initializeCFGOnlyViewerLegacyPassPass(PassRegistry&); void initializeCFGPrinterLegacyPassPass(PassRegistry&); @@ -96,6 +103,11 @@ void initializeCFGSimplifyPassPass(PassRegistry&); void initializeCFGuardPass(PassRegistry&); void initializeCFGuardLongjmpPass(PassRegistry&); void initializeCFGViewerLegacyPassPass(PassRegistry&); +void initializeCJBarrierLoweringPass(PassRegistry&); +void initializeCJCFGViewerLegacyPassPass(PassRegistry&); +void initializeCJCFGPrinterLegacyPassPass(PassRegistry&); +void initializeCJRewriteStatepointLegacyPassPass(PassRegistry &); +void initializeCJStackPointerInserterPass(PassRegistry &); void initializeCFIFixupPass(PassRegistry&); void initializeCFIInstrInserterPass(PassRegistry&); void initializeCFLAndersAAWrapperPassPass(PassRegistry&); @@ -145,6 +157,7 @@ void initializeEarlyTailDuplicatePass(PassRegistry&); void initializeEdgeBundlesPass(PassRegistry&); void initializeEHContGuardCatchretPass(PassRegistry &); void initializeEliminateAvailableExternallyLegacyPassPass(PassRegistry&); +void initializeEscapeAnalysisPass(PassRegistry&); void initializeExpandMemCmpPassPass(PassRegistry&); void initializeExpandPostRAPass(PassRegistry&); void initializeExpandReductionsPass(PassRegistry&); @@ -152,6 +165,7 @@ void initializeExpandVectorPredicationPass(PassRegistry &); void initializeMakeGuardsExplicitLegacyPassPass(PassRegistry&); void initializeExternalAAWrapperPassPass(PassRegistry&); void initializeFEntryInserterPass(PassRegistry&); +void initializeCJFillMetadataLegacyPassPass(PassRegistry &); void initializeFinalizeISelPass(PassRegistry&); void initializeFinalizeMachineBundlesPass(PassRegistry&); void initializeFixIrreduciblePass(PassRegistry &); @@ -164,6 +178,7 @@ void initializeFuncletLayoutPass(PassRegistry&); void initializeFunctionSpecializationLegacyPassPass(PassRegistry &); void initializeGCMachineCodeAnalysisPass(PassRegistry&); void initializeGCModuleInfoPass(PassRegistry&); +void initializeGCOVProfilerLegacyPassPass(PassRegistry&); void initializeGVNHoistLegacyPassPass(PassRegistry&); void initializeGVNLegacyPassPass(PassRegistry&); void initializeGVNSinkLegacyPassPass(PassRegistry&); @@ -192,6 +207,9 @@ void initializeInferAddressSpacesPass(PassRegistry&); void initializeInferFunctionAttrsLegacyPassPass(PassRegistry&); void initializeInjectTLIMappingsLegacyPass(PassRegistry &); void initializeInlineCostAnalysisPass(PassRegistry&); +void initializeCJSimpleRangeAnalysisPass(PassRegistry&); +void initializeCangjieAAWrapperPassPass(PassRegistry&); +void initializeInsertCJTBAALegacyPassPass(PassRegistry&); void initializeInstCountLegacyPassPass(PassRegistry &); void initializeInstNamerPass(PassRegistry&); void initializeInstSimplifyLegacyPassPass(PassRegistry &); @@ -332,7 +350,7 @@ void initializePeepholeOptimizerPass(PassRegistry&); void initializePhiValuesWrapperPassPass(PassRegistry&); void initializePhysicalRegisterUsageInfoPass(PassRegistry&); void initializePlaceBackedgeSafepointsImplPass(PassRegistry&); -void initializePlaceSafepointsPass(PassRegistry&); +void initializePlaceSafepointsLegacyPassPass(PassRegistry&); void initializePostDomOnlyPrinterWrapperPassPass(PassRegistry &); void initializePostDomOnlyViewerWrapperPassPass(PassRegistry &); void initializePostDomPrinterWrapperPassPass(PassRegistry &); @@ -421,6 +439,8 @@ void initializeTailDuplicatePass(PassRegistry&); void initializeTargetLibraryInfoWrapperPassPass(PassRegistry&); void initializeTargetPassConfigPass(PassRegistry&); void initializeTargetTransformInfoWrapperPassPass(PassRegistry&); +void initializeThreadSanitizerPassPass(PassRegistry&); +void initializePtrAuthBackwardCFIPass(PassRegistry&); void initializeTLSVariableHoistLegacyPassPass(PassRegistry &); void initializeTwoAddressInstructionPassPass(PassRegistry&); void initializeTypeBasedAAWrapperPassPass(PassRegistry&); diff --git a/llvm/include/llvm/LTO/Config.h b/llvm/include/llvm/LTO/Config.h index b2ed8e60b..8d7b124c0 100644 --- a/llvm/include/llvm/LTO/Config.h +++ b/llvm/include/llvm/LTO/Config.h @@ -171,7 +171,7 @@ struct Config { /// Time trace granularity. unsigned TimeTraceGranularity = 500; - bool ShouldDiscardValueNames = true; + bool ShouldDiscardValueNames = false; DiagnosticHandlerFunction DiagHandler; /// Add FSAFDO discriminators. diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h index ea52226dc..abedee3be 100644 --- a/llvm/include/llvm/LTO/LTO.h +++ b/llvm/include/llvm/LTO/LTO.h @@ -277,6 +277,9 @@ public: /// Cache) for each task identifier. Error run(AddStreamFn AddStream, FileCache Cache = nullptr); + // Ckeck if all modules from cangjie bc files for cangjie LTO! + void checkCJBC(); + /// Static method that returns a list of libcall symbols that can be generated /// by LTO but might not be visible from bitcode symbol table. static ArrayRef getRuntimeLibcallSymbols(); diff --git a/llvm/include/llvm/MC/MCObjectFileInfo.h b/llvm/include/llvm/MC/MCObjectFileInfo.h index ebc9b95d6..61d9801a3 100644 --- a/llvm/include/llvm/MC/MCObjectFileInfo.h +++ b/llvm/include/llvm/MC/MCObjectFileInfo.h @@ -228,6 +228,38 @@ protected: // GOFF specific sections. MCSection *PPA1Section = nullptr; + /// Cangjie File(CFile) specific sections. + /// Sections about cangjie generic. + MCSection *CJTypeTemplateSection = nullptr; // TypeTemplate + MCSection *CJStaticGenericTISection = + nullptr; // Compiler-generated generic instantiation types + /// Sections about cangjie typeinfo. + MCSection *CJTypeInfoSection = nullptr; // TypeInfo + MCSection *CJTypeFieldsSection = nullptr; // Fields of TI or TT + + /// Sections about vtable and itable + MCSection *CJMtableSection = nullptr; // Tile all extensiondefs + MCSection *CJInnerTypeExtensionsSection = nullptr; // Reference of inner eds + MCSection *CJOuterTypeExtensionsSection = nullptr; // Reference of outer eds + + /// Sections about cangjie reflect. + MCSection *CJReflectPkgInfoSection = nullptr; + MCSection *CJReflectGVSection = nullptr; + MCSection *CJReflectGenericTISection = nullptr; + + /// Sections about cangjie gc. + MCSection *CJMethodInfoSection = nullptr; // Method descriptor + MCSection *CJStackMapSection = nullptr; + MCSection *CJGCTibSection = nullptr; + MCSection *CJGCRootSection = nullptr; + MCSection *CJGCFlagsSection = nullptr; + + /// Other cangjie sections. + MCSection *CJSDKVersionSection = nullptr; + MCSection *CJStringPoolDictSection = nullptr; + MCSection *CJStringPoolSection = nullptr; + MCSection *CJGlobalInitFuncSection = nullptr; + // XCOFF specific sections MCSection *TOCBaseSection = nullptr; MCSection *ReadOnly8Section = nullptr; @@ -434,6 +466,48 @@ public: MCSection *getEHFrameSection() const { return EHFrameSection; } + /// Cangjie File(CFile) specific sections. + // TypeInfo information + MCSection *getCJTypeTemplateSection() const { return CJTypeTemplateSection; } + MCSection *getCJStaticGenericTISection() const { return CJStaticGenericTISection; } + MCSection *getTypeInfoSection() const { return CJTypeInfoSection; } + MCSection *getCJTypeFieldsSection() const { return CJTypeFieldsSection; } + + // VTable/ITable + MCSection *getCJMtableSection() const { return CJMtableSection; } + MCSection *getCJInnerTypeExtensionsSection() const { + return CJInnerTypeExtensionsSection; + } + MCSection *getCJOuterTypeExtensionsSection() const { + return CJOuterTypeExtensionsSection; + } + + // Reflect + MCSection *getCJReflectPkgInfoSection() const { + return CJReflectPkgInfoSection; + } + MCSection *getCJReflectGVSection() const { return CJReflectGVSection; } + MCSection *getCJReflectGenericTISection() const { + return CJReflectGenericTISection; + } + + // GC + MCSection *getCJMethodInfoSection() const { return CJMethodInfoSection; } + MCSection *getCJStackMapSection() const { return CJStackMapSection; } + MCSection *getCJGCTibSection() const { return CJGCTibSection; } + MCSection *getCJGCRootSection() const { return CJGCRootSection; } + MCSection *getCJGCFlagsSection() const { return CJGCFlagsSection; } + + // Others + MCSection *getCJSDKVersionSection() const { return CJSDKVersionSection; } + MCSection *getCJStringPoolDictSection() const { + return CJStringPoolDictSection; + } + MCSection *getCJStringPoolSection() const { return CJStringPoolSection; } + MCSection *getCJGlobalInitFuncSection() const { + return CJGlobalInitFuncSection; + } + bool isPositionIndependent() const { return PositionIndependent; } // Swift5 Reflection Data Sections diff --git a/llvm/include/llvm/MC/SectionKind.h b/llvm/include/llvm/MC/SectionKind.h index 61e400fe9..e3dbb6f1d 100644 --- a/llvm/include/llvm/MC/SectionKind.h +++ b/llvm/include/llvm/MC/SectionKind.h @@ -116,9 +116,42 @@ class SectionKind { /// can write to them. If it chooses to, the dynamic linker can /// mark the pages these globals end up on as read-only after it is /// done with its relocation phase. - ReadOnlyWithRel + ReadOnlyWithRel, + + CJMetadataInfo, + CJTypeTemplate, + CJStaticGenericTI, + CJTypeInfo, + CJTypeFields, + CJMTable, + CJInnerTypeExtensions, + CJOuterTypeExtensions, + CJReflectPkgInfo, + CJReflectGV, + CJReflectGeneticTI, + CJGCTib, } K : 8; + public: + bool isCJMetadataInfo() const { return K == CJMetadataInfo; } + bool isCJTypeInfo() const { return K == CJTypeInfo; } + bool isCJTypeTemplate() const { return K == CJTypeTemplate; } + bool isCJTypeFields() const { return K == CJTypeFields; } + bool isCJGCTib() const { return K == CJGCTib; } + bool isCJMTable() const { return K == CJMTable; } + bool isCJReflectPkgInfo() const { return K == CJReflectPkgInfo; } + bool isCJReflectGV() const { return K == CJReflectGV; } + bool isCJStaticGenericTI() const { return K == CJStaticGenericTI; } + bool isCJInnerTypeExtensions() const { return K == CJInnerTypeExtensions; } + bool isCJOuterTypeExtensions() const { return K == CJOuterTypeExtensions; } + bool isCJReflectGenericTI() const { return K == CJReflectGeneticTI; } + bool isCJMetaData() const { + return isCJMetadataInfo() || isCJTypeInfo() || isCJTypeTemplate() || + isCJTypeFields() || isCJGCTib() || isCJMTable() || + isCJReflectPkgInfo() || isCJReflectGV() || isCJStaticGenericTI() || + isCJInnerTypeExtensions() || isCJOuterTypeExtensions() || + isCJReflectGenericTI(); + } bool isMetadata() const { return K == Metadata; } @@ -163,7 +196,8 @@ public: bool isThreadBSSLocal() const { return K == ThreadBSSLocal; } bool isGlobalWriteableData() const { - return isBSS() || isCommon() || isData() || isReadOnlyWithRel(); + return isBSS() || isCommon() || isData() || isReadOnlyWithRel() || + isCJData(); } bool isBSS() const { return K == BSS || K == BSSLocal || K == BSSExtern; } @@ -173,6 +207,7 @@ public: bool isCommon() const { return K == Common; } bool isData() const { return K == Data; } + bool isCJData() const { return K > CJMetadataInfo && K <= CJGCTib; } bool isReadOnlyWithRel() const { return K == ReadOnlyWithRel; @@ -211,6 +246,22 @@ public: static SectionKind getBSSExtern() { return get(BSSExtern); } static SectionKind getCommon() { return get(Common); } static SectionKind getData() { return get(Data); } + static SectionKind getCJMetadataInfo() { return get(CJMetadataInfo); } + static SectionKind getCJTypeTemplate() { return get(CJTypeTemplate); } + static SectionKind getCJTypeInfo() { return get(CJTypeInfo); } + static SectionKind getCJTypeFields() { return get(CJTypeFields); } + static SectionKind getCJGCTib() { return get(CJGCTib); } + static SectionKind getCJMTable() { return get(CJMTable); } + static SectionKind getCJReflectGV() { return get(CJReflectGV); } + static SectionKind getCJReflectPkgInfo() { return get(CJReflectPkgInfo); } + static SectionKind getCJStaticGenericTI() { return get(CJStaticGenericTI); } + static SectionKind getCJReflectGenericTI() { return get(CJReflectGeneticTI); } + static SectionKind getCJInnerTypeExtensions() { + return get(CJInnerTypeExtensions); + } + static SectionKind getCJOuterTypeExtensions() { + return get(CJOuterTypeExtensions); + } static SectionKind getReadOnlyWithRel() { return get(ReadOnlyWithRel); } }; diff --git a/llvm/include/llvm/Transforms/CJCFI/PtrAuthBackwardCFI.h b/llvm/include/llvm/Transforms/CJCFI/PtrAuthBackwardCFI.h new file mode 100644 index 000000000..fd732c210 --- /dev/null +++ b/llvm/include/llvm/Transforms/CJCFI/PtrAuthBackwardCFI.h @@ -0,0 +1,29 @@ +//===---------------------- PtrAuthBackwardCFI.h --------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef PTRAUTH_BACKWARD_CFI_H +#define PTRAUTH_BACKWARD_CFI_H + +#include "llvm/IR/PassManager.h" +#include "llvm/Pass.h" + +namespace llvm { + +class PtrAuthBackwardCFI : public PassInfoMixin { +public: + PtrAuthBackwardCFI() {}; + ~PtrAuthBackwardCFI() {}; + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); + static bool isRequired() { return true; } +}; + +} + +#endif \ No newline at end of file diff --git a/llvm/include/llvm/Transforms/IPO.h b/llvm/include/llvm/Transforms/IPO.h index 52f0bc934..fe5c77c6b 100644 --- a/llvm/include/llvm/Transforms/IPO.h +++ b/llvm/include/llvm/Transforms/IPO.h @@ -243,7 +243,6 @@ ModulePass *createGlobalSplitPass(); /// Write ThinLTO-ready bitcode to Str. ModulePass *createWriteThinLTOBitcodePass(raw_ostream &Str, raw_ostream *ThinLinkOS = nullptr); - } // End llvm namespace #endif diff --git a/llvm/include/llvm/Transforms/IPO/CJPartialEscapeAnalysis.h b/llvm/include/llvm/Transforms/IPO/CJPartialEscapeAnalysis.h new file mode 100644 index 000000000..1ec3ef1f2 --- /dev/null +++ b/llvm/include/llvm/Transforms/IPO/CJPartialEscapeAnalysis.h @@ -0,0 +1,60 @@ +//===- CJPartialEscapeAnalysis.h - escape analysis for Cangjie ------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file provides interface to "PEA" pass. +// +// This file implements escape analysis, and trans non-escape object from heap +// to stack if possible. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_IPO_CJPartialEscapeAnalysis_H +#define LLVM_TRANSFORMS_IPO_CJPartialEscapeAnalysis_H + +#include "llvm/Analysis/CGSCCPassManager.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/IR/PassManager.h" + +class GCPtr; +class MemPtr; + +namespace llvm { +enum EscapeState : unsigned { NotEscape, InfoEscaped, TransEscaped, Escaped }; +struct CJPartialEscapeAnalysisPass + : public PassInfoMixin { + PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, + LazyCallGraph &CG, CGSCCUpdateResult &URx) const; +}; + +class CJEscapeAnalysis { +public: + Value *getBaseValue(Value *CV, int &Offset); + void setInfoEscaped(GCPtr *, BasicBlock *); + bool isEscapedValue(Value *V); + + Function *ProcessedFunc = nullptr; + DenseMap AllPtrLocInfo; + DenseMap, MemPtr *> AllMemLocInfo; + DenseMap> EscapeBBInfo; +}; + +Type *getAllocaType(GlobalVariable *Klass, bool &HasRef, uint32_t &AS, bool &, + Instruction *Val); +void setEscapeMeta(Instruction *I); + +GlobalVariable *getNewKlass(CallBase *I); + +bool isRewriteableCangjieMallocFunc(Function *F); + +bool escapeAnalysisFuncImpl(Function *F, + function_ref LoopLoop); +} // namespace llvm + +#endif diff --git a/llvm/include/llvm/Transforms/IPO/GlobalDCE.h b/llvm/include/llvm/Transforms/IPO/GlobalDCE.h index a24196efb..0bb3b5311 100644 --- a/llvm/include/llvm/Transforms/IPO/GlobalDCE.h +++ b/llvm/include/llvm/Transforms/IPO/GlobalDCE.h @@ -18,7 +18,9 @@ #define LLVM_TRANSFORMS_IPO_GLOBALDCE_H #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/PassManager.h" #include @@ -70,6 +72,44 @@ private: void ScanVTableLoad(Function *Caller, Metadata *TypeId, uint64_t CallOffset); void ComputeDependencies(Value *V, SmallPtrSetImpl &U); + + friend struct CangjieVFE; +}; + +struct CangjieVFE { + struct GenericFuncInfo { + bool IsValid= false; + GenericFuncInfo *Prev = nullptr; + // Save all func tables with current type. + SmallPtrSet FuncTables; + DenseMap Next; + }; + + GlobalDCEPass &DCE; + GenericFuncInfo *Root; + + CangjieVFE() = delete; + CangjieVFE(GlobalDCEPass &DCE) : DCE(DCE) { Root = new GenericFuncInfo; } + ~CangjieVFE(); + + GenericFuncInfo *insertInstance(const SmallVectorImpl &, + GlobalVariable *FT); + + GenericFuncInfo * + findTargetInstance(const SmallVectorImpl &); + + // Parse metadata and return the corresponding GFI. + GenericFuncInfo *resolveVFEMeta(Metadata *, Module &); + + void addCangjieVirtualFunctionDependencies(Module &); + + // Obtain the function table FT that achieves P from C, return {C, P, FT}. + std::tuple + scanVTable(GlobalVariable &); + + // Add dependencies between caller and callee. + void updateDependencies(Function *, + DenseMap> &); }; } diff --git a/llvm/include/llvm/Transforms/Instrumentation.h b/llvm/include/llvm/Transforms/Instrumentation.h index 0c688e3bd..ca10c511e 100644 --- a/llvm/include/llvm/Transforms/Instrumentation.h +++ b/llvm/include/llvm/Transforms/Instrumentation.h @@ -78,6 +78,9 @@ struct GCOVOptions { std::string Exclude; }; +ModulePass * +createGCOVProfilerPass(const GCOVOptions &Options = GCOVOptions::getDefault()); + // The pgo-specific indirect call promotion function declared below is used by // the pgo-driven indirect call promotion and sample profile passes. It's a // wrapper around llvm::promoteCall, et al. that additionally computes !prof diff --git a/llvm/include/llvm/Transforms/Obfuscator/ControlFlowObfuscator.h b/llvm/include/llvm/Transforms/Obfuscator/ControlFlowObfuscator.h new file mode 100644 index 000000000..4d703eb1d --- /dev/null +++ b/llvm/include/llvm/Transforms/Obfuscator/ControlFlowObfuscator.h @@ -0,0 +1,94 @@ +//===---------------------- ControlFlowObfuscator.h -----------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef CANGJIE_CONTROLFLOW_OBFUSCATOR_H +#define CANGJIE_CONTROLFLOW_OBFUSCATOR_H + +#include "llvm/IR/PassManager.h" +#include "llvm/Pass.h" +#include "llvm/Analysis/ObfConfigAnalysis.h" + +namespace llvm { + +class ControlFlowObfuscator : public PassInfoMixin { +public: + ControlFlowObfuscator() {} + ~ControlFlowObfuscator(); + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); + +private: + std::vector BlockVec; + std::vector BlockSet; + std::vector MapTagVec; + std::vector MapValueVec; + std::vector BlockFlagVec; + std::vector PredisFlagVec; + + void doBcfObfuse(Module &M); + void doIndBrObfuse(Module &M); + void doFltObfuse(Module &M); + + unsigned int getRandNum() const; + void renameOperand(ValueToValueMapTy &TV, BasicBlock &TB); + BasicBlock *createAlterBasicBlock(BasicBlock &B, const Twine &N, Function &F); + bool createBrForBasicBlock(Type >, BasicBlock &Father, BasicBlock &LChild, + BasicBlock &RChild); + void createBasicBlock(BasicBlock &B, Function &F); + void sortBasicBlock(Function &F); + void insertBranches(Function &F); + void insertBogusBlocks(Instruction &I); + void handleFloatCmp(Instruction &I); + void handleBinaryCmp(Instruction &I); + void handleFloatInstruction(Instruction &I); + void handleBinaryInstruction(Instruction &I); + bool isFloatOpcode(unsigned int OP) const; + bool isBinaryOpcode(unsigned int OP) const; + void findBrInstWithType(std::vector &InstVec, + std::vector &CondVec, Function &F, + uint32_t Pre); + + BinaryOperator *createFemaMul(Module &M, Instruction &I, IntegerType *GT); + void createBrExpressFema(Function &F, Instruction &I); + void createBrExpressFema2(Function &F, Instruction &I); + void applyFema(Function &F); + bool needObfuse(Function &F, std::string Owner); + BasicBlock *createCloneBasicBlock(BasicBlock *B); + void repaireBrInst(Function &F); + + void doFltObfuse(Function &F); + void handleRegStack(Function &F); + void searchInstructions(Function &F, std::vector &PHI, + std::vector &Reg); + void pushToVector(BasicBlock &B, Instruction &I, BasicBlock *E, + std::vector &PHI, + std::vector &Reg); + bool isEscapeInst(Instruction &I); + bool isUsedOutside(Instruction &I, BasicBlock &B); + + bool updateTags(Function &F, LoadInst &OptTag, BasicBlock &Router); + Value *getBranches(uint32_t &Fbran, uint32_t &Tbran, Function &F, + BasicBlock &B, uint32_t Index); + BinaryOperator *genTags(Function &F, LoadInst &NewInst, BasicBlock &B, + uint32_t Index, std::vector &BS); + Value *getFuncConstInt(Function &F, uint32_t Index, uint32_t T); + bool updateCaseInst(SwitchInst &CaseInst, BasicBlock &Router); + uint32_t getBlockIndex(BasicBlock &B); + bool initOrigBlock(Function &F); + void initTags(size_t Length); + bool checkPreDisIsSkip(BasicBlock &B); + void swapBlockSet(std::vector &BV); + void clearObfData(); + bool skipBlock(); + + std::shared_ptr ObfConfig; +}; + +} // namespace llvm +#endif \ No newline at end of file diff --git a/llvm/include/llvm/Transforms/Obfuscator/DataObfuscator.h b/llvm/include/llvm/Transforms/Obfuscator/DataObfuscator.h new file mode 100644 index 000000000..c63f87dca --- /dev/null +++ b/llvm/include/llvm/Transforms/Obfuscator/DataObfuscator.h @@ -0,0 +1,74 @@ +//===-------------------------- DataObfuscator.h --------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef CANGJIE_DATA_OBFUSCATOR_H +#define CANGJIE_DATA_OBFUSCATOR_H + +#include "llvm/IR/PassManager.h" +#include "llvm/Pass.h" +#include "llvm/Analysis/ObfConfigAnalysis.h" + +namespace llvm { + +struct ObfVar { + GlobalVariable *OriVar; + GlobalVariable *ObfVar; + std::string ReplaceName; + uint8_t Key; + const char *Data; + uint32_t Len; +}; + +class DataObfuscator : public PassInfoMixin { +public: + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); + +private: + bool needObfuseString(GlobalVariable &G); + void stripGlobalString(Module &M); + static StringRef getGlobalString(GlobalVariable &G); + static bool cmpGlb(GlobalVariable *&FG, GlobalVariable *&SG); + char *getGlobalStringData(GlobalVariable &G, unsigned int &Len); + void replaceGlobalVariable(Module &M, GlobalVariable &G, + std::vector> &ObfGVec); + void doObfuseString(Module &M); + void addDecodeFunction(Module &M, + std::vector> &ObfGVec); + void insertDecodeBlock(Module &M, Function &DecodeFunc, IRBuilder<> &Builder, + std::unique_ptr &ObfGv); + Function *createDecodeFunction(Module &M); + void doDecode(LLVMContext &CTX, Value &V, IRBuilder<> &Builder, + const std::unique_ptr &ObfGv); + void insertDecodeFunction(Module &M, FunctionCallee F); + bool isIntrinsic(Instruction &I); + void handleConst(Instruction &I, int Index); + Value *addConstZero(Instruction &I, ConstantInt &V); + void initInstList(Function &F, std::vector &FunctionLocalVariables); + Value *bitCalTrans(IRBuilder<> &Builder, Instruction &I, Value &X, IntegerType &TransType); + Value *logicCalTrans(IRBuilder<> &Builder, Instruction &I, Value &X, Value &Y, + IntegerType &TransType); + Value *handleZero(Instruction &I, ConstantInt &V, + std::vector &FunctionLocalVariables); + void handleInstWithZero(Instruction &I, std::vector &FunctionLocalVariables); + void doObfuseConst(Function &F, BasicBlock &B, std::vector &FunctionLocalVariables, + std::vector &CandidateValues); + bool isImmCallArg(Instruction &I, unsigned int Index); + void doObfuseConst(Function &F); + void doObfuseConst(Module &M); + void recordInst(Value *V, std::vector &CandidateValues); + bool needObfuse(Instruction &I); + bool isDominateValue(Instruction &I, Value &V); + Value *getRandomDominateValue(Instruction &I, std::vector &CandidateValues, int TryTimes); + std::shared_ptr ObfConfig; +}; + +} // namespace llvm + +#endif diff --git a/llvm/include/llvm/Transforms/Obfuscator/LayoutObfuscator.h b/llvm/include/llvm/Transforms/Obfuscator/LayoutObfuscator.h new file mode 100644 index 000000000..5a3740955 --- /dev/null +++ b/llvm/include/llvm/Transforms/Obfuscator/LayoutObfuscator.h @@ -0,0 +1,82 @@ +//===------------------------- LayoutObfuscator.h -------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// + +#ifndef CANGJIE_LAYOUT_OBFUSCATOR_H +#define CANGJIE_LAYOUT_OBFUSCATOR_H + +#include "llvm/IR/PassManager.h" +#include "llvm/Pass.h" +#include "llvm/Analysis/ObfConfigAnalysis.h" + +namespace llvm { +struct SymbolMapInfo { + std::string NewName; + std::string OldName; + std::string Path; +}; + +class SymbolMapping { +public: + std::map InputMapping; // Key is NewName + std::map OutputMapping; // Key is OldName + std::map UserMapping; + SymbolMapping(){}; + void addToOutputMapping(const std::string &OldName, const std::string &NewName, const std::string &Path = ""); + bool isObfuscatedSymbol(const std::string &Name); + std::string getMappedName(const std::string &Name); + void saveOutputMappingFile(const std::string &FileName); + void loadUserMappingFile(const std::string &FileName); + void loadMappingFile( + const std::string &FileName, + const std::function &UpdateMapping, + const bool CloseBracket); + void loadMappingFiles(const std::string &FileNames); +}; + +class LayoutObfuscator : public PassInfoMixin { +public: + LayoutObfuscator() {} + ~LayoutObfuscator(); + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); + +private: + void doObfuscation(GlobalVariable &G); + void doObfuscation(Function &F); + bool isPublicGlobal(const GlobalVariable &G); + bool isPublicFunction(const Function &F); + bool isStdFunction(const Function &F); + bool needObfuse(const Function &F); + bool needObfuse(const GlobalVariable &G); + void reorderFunctions(Module &M); + void recordDebugInfo(const Module &M); + void recordAllSymbols(const Module &M); + void getCurrentPackage(const Module &M); + std::string generateUniqueName(void); + void obfuscateDebugInfo(Module &M); + + const static int LIST_LENGTH = 'z' - 'a' + 1; + int IndexList[LIST_LENGTH]; + const static inline std::string OBF_DEBUG_DIR_NAME = ""; + const static inline std::string OBF_DEBUG_FILE_NAME = "SOURCE"; + const static inline std::vector InternalSymbols = { + "file_global_init", + "package_global_init", + }; + const static inline int OBF_DEBUG_LINE_NO = 0; + + std::set AllSymbols; + std::shared_ptr ObfConfig; + SymbolMapping SymbolMap; + std::string CurrentPackage; +}; + +} // namespace llvm + +#endif diff --git a/llvm/include/llvm/Transforms/Scalar.h b/llvm/include/llvm/Transforms/Scalar.h index 5f852963c..856f2d63f 100644 --- a/llvm/include/llvm/Transforms/Scalar.h +++ b/llvm/include/llvm/Transforms/Scalar.h @@ -470,7 +470,7 @@ FunctionPass *createStraightLineStrengthReducePass(); // RewriteStatepointsForGC which can be run at an arbitrary point in the pass // order following this pass. // -FunctionPass *createPlaceSafepointsPass(); +ModulePass *createPlaceSafepointsLegacyPass(); //===----------------------------------------------------------------------===// // @@ -549,13 +549,75 @@ Pass *createWarnMissedTransformationsPass(); // FunctionPass *createInstSimplifyLegacyPass(); - //===----------------------------------------------------------------------===// // // createScalarizeMaskedMemIntrinPass - Replace masked load, store, gather // and scatter intrinsics with scalar code when target doesn't support them. // FunctionPass *createScalarizeMaskedMemIntrinLegacyPass(); -} // End llvm namespace + +//===----------------------------------------------------------------------===// +// +// CJLoopFloatOpt - optimizes cangjie Loop Float Computation +// +FunctionPass *createCJLoopFloatOptLegacyPass(); + +//===----------------------------------------------------------------------===// +// +// CJSimpleRangeAnalysis - simple range analysis of cangjie +// +FunctionPass *createCJSimpleRangeAnalysis(); + +//===----------------------------------------------------------------------===// +// +// CJFillMetadata - Fill size/bitmap/flag fields for klass global varible +// CJFillMetadata - Fill size/bitmap/flag fields for klass global varible +// +ModulePass *createCJFillMetadataLegacyPass(); +ModulePass *createCJFillMetadataLegacyPass(); + +//===----------------------------------------------------------------------===// +// +// CJRuntimeLowering - Rewrite frontend method to runtime interfaces +// +ModulePass *createCJRuntimeLoweringLegacyPass(); + +//===----------------------------------------------------------------------===// +// +// CheckFastCall - Checks the fastcall from the optimized code and marks it +// +ModulePass *createCangjieSpecificOptLegacyPass(unsigned OptLevel); + +//===----------------------------------------------------------------------===// +// +// CJRewriteStatepoint - Rewrite any gc.statepoints which do not yet +// have explicit relocations according to Cangjie GC. +// +ModulePass *createCJRewriteStatepointLegacyPass(unsigned OptLevel); + +//===----------------------------------------------------------------------===// +// +// CJBarrierOpt - optimizes cangjie write barriers +// +ModulePass *createCJBarrierOptLegacyPass(); + +//===----------------------------------------------------------------------===// +// +// CJBarrierSplit - Splitting the cangjie barriers +// +ModulePass *createCJBarrierSplitLegacyPass(); + +//===----------------------------------------------------------------------===// +// +// CJGenericIntrinsicOpt - optimizes cangjie generic intrinsics +// +FunctionPass *createCJGenericIntrinsicOptLegacyPass(); + +//===----------------------------------------------------------------------===// +// +// InsertCJTBAA - insert Cangjie TBAA Metadata +// +FunctionPass *createInsertCJTBAALegacyPass(); +} // namespace llvm #endif diff --git a/llvm/include/llvm/Transforms/Scalar/CJBarrierOpt.h b/llvm/include/llvm/Transforms/Scalar/CJBarrierOpt.h new file mode 100644 index 000000000..844022582 --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/CJBarrierOpt.h @@ -0,0 +1,27 @@ +//===- CJBarrierOpt.cpp - optimize cangjie write barriers -----------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This pass optimizes cangjie write barriers. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_CJ_BARRIER_OPT_H +#define LLVM_TRANSFORMS_SCALAR_CJ_BARRIER_OPT_H + +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { +struct CJBarrierOpt : public PassInfoMixin { + PreservedAnalyses run(Module &M, ModuleAnalysisManager &) const; +}; +} // namespace llvm + +#endif // LLVM_TRANSFORMS_SCALAR_CJ_BARRIER_OPT_H diff --git a/llvm/include/llvm/Transforms/Scalar/CJBarrierSplit.h b/llvm/include/llvm/Transforms/Scalar/CJBarrierSplit.h new file mode 100644 index 000000000..7dce8c0d8 --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/CJBarrierSplit.h @@ -0,0 +1,30 @@ +//===- CJBarrierSplit.h - ---------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file provides interface to "Cangjie Barrier Split" pass. +// +// This pass mainly implements the cangjie barrier splitting optimization. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_CJ_BARRIER_SPLIT_H +#define LLVM_TRANSFORMS_SCALAR_CJ_BARRIER_SPLIT_H + +#include "llvm/IR/PassManager.h" + +namespace llvm { +class Function; + +struct CJBarrierSplit : public PassInfoMixin { + PreservedAnalyses run(Module &M, ModuleAnalysisManager &) const; +}; +} // namespace llvm + +#endif // LLVM_TRANSFORMS_SCALAR_CJ_BARRIER_SPLIT_H \ No newline at end of file diff --git a/llvm/include/llvm/Transforms/Scalar/CJDevirtualOpt.h b/llvm/include/llvm/Transforms/Scalar/CJDevirtualOpt.h new file mode 100644 index 000000000..18bd5afdd --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/CJDevirtualOpt.h @@ -0,0 +1,30 @@ +//===- CJDevirtualOpt.h - ---------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file provides interface to "CJ Devirtual Optimization" pass. +// +// This pass converts cj virtual call into direct call if possible. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TRANSFORMS_SCALAR_CJDEVIRTUALOPT_H +#define LLVM_TRANSFORMS_SCALAR_CJDEVIRTUALOPT_H + +#include "llvm/Analysis/CGSCCPassManager.h" +#include "llvm/IR/PassManager.h" +namespace llvm { +class Function; + +struct CJDevirtualOpt : public PassInfoMixin { + PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM, + LazyCallGraph &CG, CGSCCUpdateResult &UR) const; +}; +} // namespace llvm + +#endif // LLVM_TRANSFORMS_SCALAR_CJ_DEVIRTUAL_OPT_H diff --git a/llvm/include/llvm/Transforms/Scalar/CJFillMetadata.h b/llvm/include/llvm/Transforms/Scalar/CJFillMetadata.h new file mode 100644 index 000000000..4a8f3a25f --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/CJFillMetadata.h @@ -0,0 +1,272 @@ +//===- CJFillMetadata.h - ---------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file provides interface to "Fill Cangjie Metadata" pass. +// +// This pass Fill Cangjie Metadata for global varible such as TypeInfo and +// ReflectionInfo. +//===----------------------------------------------------------------------===// +#ifndef LLVM_TRANSFORMS_SCALAR_FILL_CJ_METADATA_H +#define LLVM_TRANSFORMS_SCALAR_FILL_CJ_METADATA_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { +enum ClassInfoFieldType : uint8_t { + CIT_NAME = 0, + CIT_TYPE, + CIT_FLAG, + CIT_FIELD_NUM, + CIT_SIZE, + CIT_GCTIB = 5, + CIT_UUID, + CIT_ALIGN, + CIT_TYPE_ARG_NUM, // generic type argumnet number + CIT_VALID_INHERIT_NUM, // valid inherit index + CIT_OFFSETS = 10, // offset of fields + CIT_GENERIC_FROM, // generic type template + CIT_TYPE_ARG, // generic type arg[] + CIT_FIELD, // fields[] + CIT_SUPER, // super klass or component klass + CIT_VTABLE = 15, // virtual extension def ref in extension_defs[] + CIT_ITABLE, // interface hashtable + CIT_REFLECTION, // reflection* + CIT_MAX, // klass type length +}; + +enum TypeKind : int8_t { + // reference type + TK_CLASS = -128, + TK_INTERFACE = -127, + TK_RAWARRAY = -126, + TK_FUNC = -125, + TK_TEMP_ENUM = -124, + TK_GENERIC = -1, // TI: { name, kind } + TK_GENERIC_CUSTOM = -2, // TI: { name, kind, typeArgCnt, typeArgs, TT} + + // value type + TK_NOTHING = 0, + TK_UNIT, + TK_BOOL, + TK_RUNE, + TK_UINT8, + TK_UINT16 = 5, + TK_UINT32, + TK_UINT64, + TK_UINT_NATIVE, + TK_INT8, + TK_INT16 = 10, + TK_INT32, + TK_INT64, + TK_INT_NATIVE, + TK_FLOAT16, + TK_FLOAT32 = 15, + TK_FLOAT64, + TK_CSTRING, + TK_CPOINTER, + TK_CFUNC, + TK_VARRAY = 20, + TK_TUPLE, + TK_STRUCT, + TK_ENUM, + TK_MAX, +}; + +enum TypeTemplateFieldType : uint8_t { + TT_NAME = 0, + TT_TYPE, + TT_FLAG, + TT_FIELD_NUM, + TT_TYPE_ARG_NUM, // generic type argumnet number + TT_FIELDS_FNS = 5, // function array for fields compute + TT_SUPER_FN, // function for super compute + TT_FINALIZER, // generic type finalizer + TT_REFLECTION, // reflection* + TT_VEDREF, // virtual extension def ref in extension_defs[] + TT_VALID_INHERIT_NUM, // valid inherit index + TT_MAX, // typeTemplate length +}; + +enum TypeFlag : uint8_t { + TF_NONE = 0, + TF_HAS_REF_FIELD = 0x1 << 0, + TF_HAS_FINALIZER = 0x1 << 1, + TF_FUTURE_CLASS = 0x1 << 2, + TF_MUTEX_CLASS = 0x1 << 3, + TF_MONITOR_CLASS = 0x1 << 4, + TF_WAIT_QUEUE_CLASS = 0x1 << 5, + TF_REFLECTION = 0x1 << 6, + // used to mark weakref in the backend, + // does not set the flag value in typeinfo. + TF_WEAK_REF_CLASS = 0x1 << 7, +}; + +enum ExtensionDefFieldType : uint8_t { + ET_TYPE_PARAM_COUNT = 0, + ET_IS_INTERFACE_TYPEINFO, + ET_TARGET_TYPE, + ET_INTERFACE_FN, + ET_WHERE_COND_FN, + ET_FUNC_TABLE, +}; + +enum ReflectModifyType : uint32_t { + RMT_DEFAULT = 0x1 << 0, + RMT_PRIVATE = 0x1 << 1, + RMT_PROTECTED = 0x1 << 2, + RMT_PUBLIC = 0x1 << 3, + RMT_IMMUTABLE = 0x1 << 4, + RMT_BOXCLASS = 0x1 << 5, + RMT_OPEN = 0x1 << 6, + RMT_OVERRIDE = 0x1 << 7, + RMT_REDEF = 0x1 << 8, + RMT_ABSTRACT = 0x1 << 9, + RMT_SEALED = 0x1 << 10, + RMT_MUT = 0x1 << 11, + RMT_STATIC = 0x1 << 12, + RMT_ENUM_KIND0 = 0x1 << 13, + RMT_ENUM_KIND1 = 0x1 << 14, + RMT_ENUM_KIND2 = 0x1 << 15, + RMT_ENUM_KIND3 = 0x1 << 16, + RMT_HAS_SRET0 = 0x1 << 17, // has sret but it is'not generic + RMT_HAS_SRET1 = 0x1 << 18, // has sret and it is 'T' + RMT_HAS_SRET2 = 0x1 << 19, // has sret and it is 'known struct T' + RMT_HAS_SRET3 = 0x1 << 20, // has sret and it is 'unknow struct T' + RMT_MAX = 0x1 << 21, +}; + +struct ExtensionDefData { + unsigned TypeParamCount; + uint8_t IsInterfaceTypeInfo; + GlobalVariable *TargetType = 0; + union { + Function *InterfaceFn = nullptr; + GlobalVariable *InterfaceTypeInfo; + }; + Function *WhereCondFn = nullptr; + GlobalVariable *FuncTable = nullptr; + + explicit ExtensionDefData(GlobalVariable *GV); + bool isTypeInfo() { return TypeParamCount == 0; } + Function *getFuncByIndex(unsigned Idx); +}; + +struct TypeTemplate { + ConstantStruct *TT; + GlobalVariable *GV; + TypeKind Kind; + + explicit TypeTemplate(GlobalVariable *GV); + + bool isEnum(); + bool isSyncClass(); + + Constant *get(unsigned Idx); + TypeKind getKind(); + uint8_t getFlag(); + unsigned getFieldNum(); + unsigned getTypeArgNum(); +}; + +struct TypeInfo { + ConstantStruct *Data; + GlobalVariable *GV; + TypeKind Kind; + + explicit TypeInfo(GlobalVariable *GV); + + bool isStructType(); + bool isPrimitiveType(); + bool isReferenceType(); + bool isArray(); + bool isEnum(); + bool isTuple(); + bool isFunction(); + bool isVarray(); + bool isClass(); + bool isInterface(); + bool isCType(); + bool isPureValueType(); + bool isSyncClass(); + bool isGenericType(); + + Constant *get(unsigned Idx); + uint8_t getTypeFlag(); + unsigned getSize(); + unsigned getFieldNum(); + unsigned getTypeArgNum(); + Constant *getGCTib(); + unsigned getAlign(); + Constant *getOffsets(); + Constant *getTypeArgs(); + Constant *getFields(); + // If it is object-type, get the super class, if array-type, + // get the component class. + GlobalVariable *getSuperOrComponent(); + unsigned getOffset(unsigned Idx); + GlobalVariable *getField(unsigned Idx); + GlobalVariable *getTypeArg(unsigned Idx); + GlobalVariable *getTypeTemplate(); + StructType *getLayoutType(); + Type *getArrayElementType(); + bool hasRefField(); + bool hasTypeTempate(); + + // update typeinfo global variable. + void resetTypeInfo(GlobalVariable *GV); + static Value *getReferenceType(GlobalVariable *GV); +}; + +class RefValue { +public: + explicit RefValue(Value *V) : Ref(V) {} + ~RefValue() = default; + + GlobalVariable *findTypeInfoGV(); + +private: + Value *Ref; + SmallSet VisitedValue; + DenseMap TypeVec; + + void getTypeInfoGV(Value *V); + void getTypeInfoGVLoad(Value *V); + void getTypeInfoGVParam(Function *F, int ArgNo); + bool isNewObjectCallSite(CallBase *CI); +}; + +GlobalVariable *findTypeInfoGV(Value *V); + +class Module; + +struct CJFillMetadata : public PassInfoMixin { + PreservedAnalyses run(Module &M, ModuleAnalysisManager &) const; +}; + +constexpr uint32_t ArrayHeadSize = 16; +constexpr uint32_t ObjectHeadSize = 8; +constexpr uint32_t SyncSize = 168; +constexpr uint32_t MaxAllocaObj = 10000; +constexpr uint32_t MaxArrayFast = 4 * 1024; // 4kb + +StructType *getTypeLayoutType(GlobalVariable *GV); +std::string getPrimitiveTypeName(Type *Ty); +bool isPrimitiveTypeInfo(GlobalVariable *GV); +unsigned getValueTypeSize(GlobalVariable *GV); +std::pair getKnownTypeKind(GlobalVariable *GV); +unsigned getTypeModifier(StringRef Name); +bool hasRunCangjieOpt(Module &M); +} // namespace llvm + +#endif // LLVM_TRANSFORMS_SCALAR_FILL_CJ_METADATA_H diff --git a/llvm/include/llvm/Transforms/Scalar/CJGCLiveAnalysis.h b/llvm/include/llvm/Transforms/Scalar/CJGCLiveAnalysis.h new file mode 100644 index 000000000..f547203ef --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/CJGCLiveAnalysis.h @@ -0,0 +1,375 @@ +//===- CJGCLiveAnalysis.h - -------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// Analyze and calculate live cangjie references based on cfg. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_GCLIVEANALYSIS_H +#define LLVM_TRANSFORMS_SCALAR_GCLIVEANALYSIS_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/Analysis/PostDominators.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Statepoint.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/Value.h" +#include "llvm/IR/ValueHandle.h" + +using namespace llvm; + +class FieldInfo { +public: + Value *V; + int Offset; + + FieldInfo(Value *V, int Offset) : V(V), Offset(Offset) {} + ~FieldInfo() = default; +}; + +using StructLiveSetTy = SetVector; + +struct PtrLiveData { + /// Values defined in this block. + MapVector> KillSet; + + /// Values used in this block (and thus live); does not included values + /// killed within this block. + MapVector> LiveSet; + + /// Values live into this basic block (i.e. used by any + /// instruction in this basic block or ones reachable from here) + MapVector> LiveIn; + + /// Values live out of this basic block (i.e. live into + /// any successor block) + MapVector> LiveOut; +}; + +struct StructLiveData { + /// Values defined in this block. + MapVector> KillSet; + + /// Values used in this block (and thus live); does not included values + /// killed within this block. + MapVector> LiveSet; + + /// Values live into this basic block (i.e. used by any + /// instruction in this basic block or ones reachable from here) + MapVector> LiveIn; + + /// Values live out of this basic block (i.e. live into + /// any successor block) + MapVector> LiveOut; +}; + +using PointerToBaseTy = MapVector; +using LiveSetTy = SetVector; +using RematerializedValueMapTy = + MapVector, AssertingVH>; + +struct SafepointRecord { + /// The set of values known to be gc-live across this safepoint + LiveSetTy LiveSet; + + /// Records the known gc fields in struct values inserted into the statepoint. + LiveSetTy FieldSet; + + bool isCJStackCheck = false; + + /// The *new* gc.statepoint instruction itself. This produces the token + /// that normal path gc.relocates and the gc.result are tied to. + GCStatepointInst *StatepointToken; + + /// Instruction to which exceptional gc relocates are attached + /// Makes it easier to iterate through them during relocationViaAlloca. + Instruction *UnwindToken; + + /// Record live values we are rematerialized instead of relocating. + /// They are not included into 'LiveSet' field. + /// Maps rematerialized copy to it's original value. + RematerializedValueMapTy RematerializedValues; +}; + +using PhiToAllocasTy = MapVector>; +using StructLayoutGCPtrTy = MapVector>; +struct AllocaAnalysisData { + // Record the layout of each alloca is a gcptr. + StructLayoutGCPtrTy StructLayoutGCPtrMap; + + // Records the map from the PhiNode to the Alloca instruction. + PhiToAllocasTy PhiToAllocasMap; + + // Record the structs that contain gcptr. + SetVector Structs; + + // Record large structs separately, when the number of refs contained in + // a struct exceeds the threshold. + // This is an optimization, see LargeStructSizeThreshold. + SetVector LargeStructs; + + // Record the number of gcptrs contained in the struct that is in above set. + MapVector StructGCFieldNum; + + void clear() { + StructLayoutGCPtrMap.clear(); + PhiToAllocasMap.clear(); + Structs.clear(); + StructGCFieldNum.clear(); + } +}; + +enum MemoryAccess { + MemoryNone = 0, + DefineMemory = 0b01, + UseMemory = 0b10, + DefineAndUse = 0b11, +}; + +void checkFailed(const Twine &Message, Instruction *I); + +namespace llvm { +// Check whether the base of the gcptr is from the alloca struct or argument +// instruction. +bool findAllocaInsts(Value *V, SetVector &BaseSet, + bool HasArg = false); +} // end namespace llvm + +/// Abstract method class for livenses variable analysis +class GCLiveAnalysis { +public: + Function &F; + __attribute__((unused)) DominatorTree &DT; + + GCLiveAnalysis(Function &F, DominatorTree &DT) : F(F), DT(DT) { + computeFakeGCPtrs(); + } + virtual ~GCLiveAnalysis() = default; + + bool isFakeGCPtr(Value *V) { return FakeGCPtrs.contains(V); } + + bool isFakeContainGCPtrs(Value *V) { return ContainGCPtrsFakes.contains(V); } + + // Computes variables that are converted to type `i8 addrspace(1)*` but + // are actually pointers on the stack, and save it. + // If it contains gcptr, save it to ContainGCPtrsFakes. + void computeFakeGCPtrs(); + + void checkBasicSSA(SetVector &Live, Instruction *TI, + bool TermOkay = false); + + void checkBasicSSA(PtrLiveData &Data); + + // Conservatively identifies any definitions which might be live at the + // given instruction. The analysis is performed immediately before the + // given instruction. Values defined by that instruction are not considered + // live. Values used by that instruction are considered live. + void analyzeParsePointLiveness(CallBase *Call, SetVector &LiveSet); + + /// Compute the live-in set for the location rbegin starting from + /// the live-out set of the basic block + virtual void computeLiveInValues(BasicBlock::reverse_iterator Begin, + BasicBlock::reverse_iterator End, + SetVector &LiveTmp); + + virtual void computeLiveOutSeed(BasicBlock *BB, SetVector &LiveTmp); + + virtual void computeKillSet(BasicBlock *BB, SetVector &LiveSet); + + /// Compute the live-in set for every basic block in the function + void computeLiveness(); + + /// Given results from the dataflow liveness computation, find the set of live + /// Values at a particular instruction. + void findLiveSetAtInst(Instruction *Inst, SetVector &Out); + + bool insertLiveSetForCJIntrinsic(Instruction *I, SetVector &LiveTmp); + +public: + PtrLiveData Data; + +private: + // Those are on-stack pointers, converted from addrspacecast to gc pointers. + SetVector FakeGCPtrs; + // Those are FakeGCPtrs which contains gcptr, is a subset of the above. + SetVector ContainGCPtrsFakes; + // Avoid duplicate liveness analysis in the BB. + MapVector>> + InstLiveSetCache; +}; + +/// Abstract method class for livenses variable analysis +class StructLiveAnalysis : public GCLiveAnalysis { +public: + StructLiveAnalysis(Function &F, DominatorTree &DT, + AllocaAnalysisData &AllocaData) + : GCLiveAnalysis(F, DT), DL(F.getParent()->getDataLayout()), + AllocaData(AllocaData) {} + ~StructLiveAnalysis() { clear(); } + + FieldInfo *getFieldInfo(Value *Ptr, int Offset); + + FieldInfo *getFieldInfoByValue(Value *Ptr); + + bool containGCPtrByOffset(Value *Ptr, uint64_t Size); + + bool isBaseContainGCPtr(Value *Ptr); + + void checkBasicSSA(SetVector &Live, Instruction *TI, + bool TermOkay = false); + + void checkBasicSSA(StructLiveData &Data); + + void initializeAlloca(AllocaInst *AI); + + void insertInitializerAfterLifetime(Value *V, CallInst *CI); + + void moveInitializerAfterLifetime(Value *V, Instruction *I, CallInst *CI); + + void insertMemsetForPhiStruct(); + + void computeFieldLiveInValues(BasicBlock::reverse_iterator Begin, + BasicBlock::reverse_iterator End, + SetVector &LiveTmp); + + void computeFieldLiveOutSeed(BasicBlock *BB, + SetVector &LiveOuts); + + void computeFieldKillSet(BasicBlock *BB, SetVector &KillSet); + + void computeLiveness(); + + void findLiveSetAtInst(Instruction *Inst, StructLiveData &Data, + StructLiveSetTy &Out); + + void legalizeMemoryValueSet(SetVector &LiveSet); + + bool isStackContainGCPtr(Value *V, uint64_t Size = 0); + + void addGCFields(Value *V, SetVector &Set, uint64_t Begin = 0, + uint64_t End = 0); + + void addGCFieldsByBase(Value *V, SetVector &Set); + + void addGCFieldsByValue(Value *V, SetVector &Set); + + void addGCFieldsByMemory(Value *V, SetVector &AllocaSet, + uint64_t Size = 0); + + void visitMemoryLoad(LoadInst *LI, SetVector &AllocaUses); + + void visitMemoryStore(StoreInst *SI, SetVector &AllocaDefs); + + void visitMemoryIntrinsic(IntrinsicInst *II, + SetVector &AllocaDefs, + SetVector &AllocaUses); + + void visitMemoryCallBase(CallBase *CB, SetVector &AllocaDefs, + SetVector &AllocaUses); + + void visitMemorySelect(SelectInst *SI, SetVector &AllocaDefs, + SetVector &AllocaUses); + + MemoryAccess hasMemoryDefineOrUseValue(Instruction *I, + SetVector &AllocaDefs, + SetVector &AllocaUses); + + // Conservatively identifies any definitions which might be live at the + // given instruction. The analysis is performed immediately before the + // given instruction. Values defined by that instruction are not considered + // live. Values used by that instruction are considered live. + void analyzeParsePointLiveness(CallBase *Call, + SetVector &LiveSet); + + void combineStructGCField(StructLiveSetTy &FieldInfos, + SetVector &Structs, CallBase *Call); + + void insertGEPForField(CallBase *Call, StructLiveSetTy &FieldInfos, + LiveSetTy &FieldSet); + + void clear() { + StackContainGCPtrMap.clear(); + FieldToGEPMap.clear(); + for (auto *Field : AllFields) { + delete Field; + } + AllFields.clear(); + } + +public: + StructLiveData Data; + SmallSet InsertedMemset; + +private: + const DataLayout &DL; + AllocaAnalysisData &AllocaData; + // Cache All fieldinfo data and clear it after active variable analysis. + SmallVector AllFields; + MapVector, FieldInfo *> FieldMap; + // Mapping whether a V is a stack pointer that contains gcptr, and clear it + // after active variable analysis. + MapVector StackContainGCPtrMap; + // Caches all newly generated GEP fields. + MapVector FieldToGEPMap; + // Avoid duplicate liveness analysis in the BB. + MapVector> + InstLiveSetCache; +}; + +struct LivenessData { + Function &F; + DominatorTree &DT; + PostDominatorTree &PostDT; + unsigned OptLevel; + AllocaAnalysisData &AllocaData; + SmallVectorImpl &ToUpdate; + GCLiveAnalysis *GCLA = nullptr; + + LivenessData(Function &F, DominatorTree &DT, PostDominatorTree &PostDT, + unsigned OptLevel, AllocaAnalysisData &AllocaData, + SmallVectorImpl &ToUpdate) + : F(F), DT(DT), PostDT(PostDT), OptLevel(OptLevel), + AllocaData(AllocaData), ToUpdate(ToUpdate) { + GCLA = new GCLiveAnalysis(F, DT); + } + + ~LivenessData() { deleteGCLA(); } + + LivenessData(LivenessData &&) = delete; + LivenessData(const LivenessData &) = delete; + LivenessData &operator=(LivenessData &&) = delete; + LivenessData &operator=(const LivenessData &) = delete; + + void deleteGCLA() { + if (GCLA) { + delete GCLA; + GCLA = nullptr; + } + } + + void findLiveReferences(MutableArrayRef Records); + + void recomputeLiveInValues(CallBase *Call, SafepointRecord &Info, + PointerToBaseTy &PointerToBase); + + /// Given an updated version of the dataflow liveness results, update the + /// liveset and base pointer maps for the call site CS. + void recomputeLiveInValues(MutableArrayRef Records, + PointerToBaseTy &PointerToBase); + + void computeGCFieldLiveness(MutableArrayRef Records); + void computeGCPtrLiveness(MutableArrayRef Records); +}; + +#endif // LLVM_TRANSFORMS_SCALAR_GCLIVEANALYSIS_H diff --git a/llvm/include/llvm/Transforms/Scalar/CJGenericIntrinsicOpt.h b/llvm/include/llvm/Transforms/Scalar/CJGenericIntrinsicOpt.h new file mode 100644 index 000000000..1c03d58fb --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/CJGenericIntrinsicOpt.h @@ -0,0 +1,30 @@ +//===- CJInstanceOfOpt.h - --------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file provides interface to "Cangjie Generic Intrinsic of Optimize" pass. +// +// This pass mainly implements the generic intrinsics optimization of cangjie. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_CJ_GENERIC_INTRINSIC_OPT_H +#define LLVM_TRANSFORMS_SCALAR_CJ_GENERIC_INTRINSIC_OPT_H + +#include "llvm/IR/PassManager.h" + +namespace llvm { +class Function; + +struct CJGenericIntrinsicOpt : public PassInfoMixin { + PreservedAnalyses run(Function &F, FunctionAnalysisManager &) const; +}; +} // namespace llvm + +#endif // LLVM_TRANSFORMS_SCALAR_CJ_GENERIC_INTRINSIC_OPT_H diff --git a/llvm/include/llvm/Transforms/Scalar/CJIRVerifier.h b/llvm/include/llvm/Transforms/Scalar/CJIRVerifier.h new file mode 100644 index 000000000..21f5e5b4c --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/CJIRVerifier.h @@ -0,0 +1,31 @@ +//===- CJIRVerifier.h - -----------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file provides interface to "Cangjie IR Verifier" pass. +// +// This pass will verify whether the IR generated by the frontend of the cangjie +// meets the requirements. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_CJ_IR_VERIFIER_H +#define LLVM_TRANSFORMS_SCALAR_CJ_IR_VERIFIER_H + +#include "llvm/IR/PassManager.h" + +namespace llvm { +class Module; + +struct CJIRVerifier : public PassInfoMixin { + PreservedAnalyses run(Module &M, ModuleAnalysisManager &) const; +}; +} // namespace llvm + +#endif // LLVM_TRANSFORMS_SCALAR_CJ_IR_VERIFIER_H \ No newline at end of file diff --git a/llvm/include/llvm/Transforms/Scalar/CJLoopFloatOpt.h b/llvm/include/llvm/Transforms/Scalar/CJLoopFloatOpt.h new file mode 100644 index 000000000..bb8887869 --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/CJLoopFloatOpt.h @@ -0,0 +1,29 @@ +//===- CJLoopFloatOpt.h - ---------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file provides interface to "CJ Loop Float Optimization" pass. +// +// This pass optimizes the scene where a top-level loop only involves +// straightforward floating-point computations. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TRANSFORMS_SCALAR_CJLOOPFLOATOPT_H +#define LLVM_TRANSFORMS_SCALAR_CJLOOPFLOATOPT_H + +#include "llvm/IR/PassManager.h" +namespace llvm { +class Function; + +struct CJLoopFloatOpt : public PassInfoMixin { + PreservedAnalyses run(Function &F, FunctionAnalysisManager &) const; +}; +} // namespace llvm + +#endif // LLVM_TRANSFORMS_SCALAR_CJLOOPFLOATOPT_H diff --git a/llvm/include/llvm/Transforms/Scalar/CJRSSCE.h b/llvm/include/llvm/Transforms/Scalar/CJRSSCE.h new file mode 100644 index 000000000..fc4424b60 --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/CJRSSCE.h @@ -0,0 +1,38 @@ +//===- CJRSSCE.cpp ----------------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file provides interface to "CJ Redundant Stack Struct Copy Elimination" +// pass. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_CJRSSCE_H +#define LLVM_TRANSFORMS_SCALAR_CJRSSCE_H + +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/MemorySSA.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { + +struct CJRSSCEPass : public PassInfoMixin { + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + +bool hasMemoryDefBetween(MemorySSA &MSSA, DominatorTree &DT, + const DataLayout &DL, Value *UnderObj, + Instruction *FirstI, MemoryAccess *LastMA, + bool LastClosure = true, bool EnableDiffBB = false); +} // namespace llvm + +#endif \ No newline at end of file diff --git a/llvm/include/llvm/Transforms/Scalar/CJRewriteStatepoint.h b/llvm/include/llvm/Transforms/Scalar/CJRewriteStatepoint.h new file mode 100644 index 000000000..5fbbd951d --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/CJRewriteStatepoint.h @@ -0,0 +1,47 @@ +//===- CJRewriteStatepoint.h - ----------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file provides interface to "Cangjie Rewrite Statepoints" pass. +// +// This passe rewrites call/invoke instructions so as to make potential +// relocations performed by the cangjie garbage collector explicit in the IR. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_CJREWRITESTATEPOINT_H +#define LLVM_TRANSFORMS_SCALAR_CJREWRITESTATEPOINT_H + +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { + +class DominatorTree; +class PostDominatorTree; +class Function; +class Module; +class TargetTransformInfo; +class TargetLibraryInfo; + +struct CJRewriteStatepoint : public PassInfoMixin { + CJRewriteStatepoint(unsigned OptLevel = 0) : OptLevel(OptLevel) {}; + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); + + bool runOnFunction(Function &F, DominatorTree &, PostDominatorTree &, + TargetTransformInfo &, const TargetLibraryInfo &); + unsigned OptLevel; +}; + +} // namespace llvm + +using namespace llvm; + +#endif // LLVM_TRANSFORMS_SCALAR_CJREWRITESTATEPOINT_H \ No newline at end of file diff --git a/llvm/include/llvm/Transforms/Scalar/CJRuntimeLowering.h b/llvm/include/llvm/Transforms/Scalar/CJRuntimeLowering.h new file mode 100644 index 000000000..a158f96d4 --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/CJRuntimeLowering.h @@ -0,0 +1,39 @@ +//===- CJRuntimeLowering.h - ------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file provides interface to "CJ Runtime Lowering" pass. +// +// This pass lowers the functions generated by the cangjie frontend to +// the related runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_CJ_RUNTIME_LOWERING_H +#define LLVM_TRANSFORMS_SCALAR_CJ_RUNTIME_LOWERING_H + +#include "llvm/IR/PassManager.h" + +namespace llvm { +class Module; + +struct CJRuntimeLowering : public PassInfoMixin { + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) const; +}; +} // namespace llvm + +enum ClassType : unsigned { + ClassPrim = 0x1 << 0, + ClassArray = 0x1 << 1, + ClassObject = 0x1 << 2, + ClassValueType = 0x1 << 3, + ClassInterface = 0x1 << 4, +}; + +#endif // LLVM_TRANSFORMS_SCALAR_CJ_RUNTIME_LOWERING_H diff --git a/llvm/include/llvm/Transforms/Scalar/CJSimpleOpt.h b/llvm/include/llvm/Transforms/Scalar/CJSimpleOpt.h new file mode 100644 index 000000000..4ae28b56b --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/CJSimpleOpt.h @@ -0,0 +1,25 @@ +//===- CJSimpleOpt.h - ------------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// Some early optimization points of cangjie +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TRANSFORMS_SCALAR_CJ_EARLY_OPT_H +#define LLVM_TRANSFORMS_SCALAR_CJ_EARLY_OPT_H + +#include "llvm/IR/PassManager.h" + +namespace llvm { +struct CJSimpleOpt : public PassInfoMixin { + PreservedAnalyses run(Function &F, FunctionAnalysisManager &) const; +}; +} // namespace llvm + +#endif // LLVM_TRANSFORMS_SCALAR_CJ_EARLY_OPT_H \ No newline at end of file diff --git a/llvm/include/llvm/Transforms/Scalar/CJSimpleRangeAnalysis.h b/llvm/include/llvm/Transforms/Scalar/CJSimpleRangeAnalysis.h new file mode 100644 index 000000000..074d0df01 --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/CJSimpleRangeAnalysis.h @@ -0,0 +1,91 @@ +//===- CJSimpleRangeAnalysis.h - --------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file provides interface to "CJ Simple Range Analysis" pass. +// +// This pass implements simple range analysis for integers and floats whose +// values are integers, and optimizes CFG based on the analysis result. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TRANSFORMS_SCALAR_CJSIMPLERANGEANALYSIS_H +#define LLVM_TRANSFORMS_SCALAR_CJSIMPLERANGEANALYSIS_H + +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/IR/ConstantRange.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { +struct CJSimpleRangeAnalysisPass + : public PassInfoMixin { + LoopInfo *LI; + + enum ConditionResult : uint64_t { FALSE = 0, TRUE = 1, UNKNOWN = 2 }; + + // Record constant range of condition in each BB terminator. + // pair: + // - 0: false branch range + // - 1: true branch range + // Currently, only BBs with a single predecessor are recorded, because of the + // combinatorial explosion that occurs when there are multiple predecessors + // and multiple successors. + struct TermCR { + ConstantRange TCR = ConstantRange::getFull(64); + ConstantRange FCR = ConstantRange::getFull(64); + }; + DenseMap> BBValueCRs; + + struct ConditionState { + Value *BV; // base value + APInt Diff; // difference from base value + CmpInst::Predicate Pred; // cmp predicate + ConditionResult PredCR; // cmp predicate result to succ, determine whether + // ConstantRage needs to be inverted. + int64_t CI; // cmp constant integer compare value + BasicBlock *BB = nullptr; // BB to which this condition state belongs + }; + + std::pair getValidConstantInteger(Value *V); + + Value *getRangeFromInitValue(Value *V, APInt &APValue, + SmallSet &Visited); + + std::pair checkAndGetConditionBaseDiff(BranchInst *BI, + APInt &Diff); + + ConditionState findMostRecentConditionBr(BasicBlock *BB, BasicBlock *Succ); + + std::pair getCRs(const ConditionState &Prev, + const ConditionState &Curr); + + void rewriteUses(BasicBlock *BB, BasicBlock *NewBB, + DenseMap &InstMapping); + + void createJumpBB(BasicBlock *Pred, BasicBlock *BB, BasicBlock *Succ); + + ConditionResult getConditionResult(const ConditionState &Prev, + const ConditionState &Curr); + + void updateBBValueCRs(BasicBlock *BB, + MapVector &PredInfo, + ConditionState &CurrCS); + + bool processBasicBlock(BasicBlock &BB); + bool simplifyBinaryInst(Function &F); + bool runImpl(Function &F, LoopInfo &LI, DominatorTree &DT); + + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; +} // namespace llvm + +#endif \ No newline at end of file diff --git a/llvm/include/llvm/Transforms/Scalar/CJSpecificOpt.h b/llvm/include/llvm/Transforms/Scalar/CJSpecificOpt.h new file mode 100644 index 000000000..3cd0e95e4 --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/CJSpecificOpt.h @@ -0,0 +1,33 @@ +//===- CJSpecificOpt.h - ----------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file provides interface to "Cangjie Specific Optimize" pass. +// +// This pass mainly implements the specified optimization of cangjie. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_CANGJIE_SPECIFIC_OPT_H +#define LLVM_TRANSFORMS_SCALAR_CANGJIE_SPECIFIC_OPT_H + +#include "llvm/IR/PassManager.h" + +namespace llvm { +class Module; + +struct CJSpecificOpt : public PassInfoMixin { + CJSpecificOpt(unsigned OptLevel = 0) : OptLevel(OptLevel) {}; + PreservedAnalyses run(Module &M, ModuleAnalysisManager &) const; + + unsigned OptLevel; +}; +} // namespace llvm + +#endif // LLVM_TRANSFORMS_SCALAR_CANGJIE_SPECIFIC_OPT_H \ No newline at end of file diff --git a/llvm/include/llvm/Transforms/Scalar/InsertCJTBAA.h b/llvm/include/llvm/Transforms/Scalar/InsertCJTBAA.h new file mode 100644 index 000000000..c1820c4d2 --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/InsertCJTBAA.h @@ -0,0 +1,38 @@ +//===- InsertCJTBAA.h - -----------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// Insert Cangjie TBAA Metadata for load, store, memcpy, and barrier +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TRANSFORMS_SCALAR_INSERT_CJ_TBAA_H +#define LLVM_TRANSFORMS_SCALAR_INSERT_CJ_TBAA_H + +#include "llvm/IR/PassManager.h" +#include +namespace llvm { + +class Function; + +struct InsertCJTBAA : public PassInfoMixin { + /// Run the pass over the function. + PreservedAnalyses run(Function &F, AnalysisManager &AM) const; +}; + +bool prepareCJTBAA(const DataLayout &DL, Instruction *I, Value *OP, Type *DstTy, + bool IsTBAAStruct = false, uint64_t OriginOff = 0); +bool updateTBAA(const DataLayout &DL, Instruction *I); +Type *getInnerTypeByOffset(const DataLayout &DL, StructType *ST, + uint64_t Offset); +bool insertTBAAWithDefiniteType(const DataLayout &DL, Instruction *I, + StructType *SrcTy, Type *DstTy, uint64_t Offset, + bool Tag = true); +} // namespace llvm + +#endif // LLVM_TRANSFORMS_SCALAR_INSERT_CJ_TBAA_H \ No newline at end of file diff --git a/llvm/include/llvm/Transforms/Scalar/PlaceSafepoints.h b/llvm/include/llvm/Transforms/Scalar/PlaceSafepoints.h new file mode 100644 index 000000000..8868a53b3 --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/PlaceSafepoints.h @@ -0,0 +1,30 @@ +//===- PlaceSafepoints.h - Place GC Safepoints --------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// Place garbage collection safepoints at appropriate locations in the IR. This +// does not make relocation semantics or variable liveness explicit. That's +// done by RewriteStatepointsForGC. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_PLACE_SAFE_POINTS_H +#define LLVM_TRANSFORMS_SCALAR_PLACE_SAFE_POINTS_H + +#include "llvm/IR/PassManager.h" + +namespace llvm { +class Module; + +struct PlaceSafepoints : public PassInfoMixin { + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) const; +}; +} // namespace llvm + +#endif // LLVM_TRANSFORMS_SCALAR_PLACE_SAFE_POINTS_H \ No newline at end of file diff --git a/llvm/include/llvm/Transforms/Scalar/ReflectionInfo.h b/llvm/include/llvm/Transforms/Scalar/ReflectionInfo.h new file mode 100644 index 000000000..7b2f79b52 --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/ReflectionInfo.h @@ -0,0 +1,272 @@ +//===- ReflectionInfo.h - ---------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file provides interface to "Fill Klass Global value" pass. +// +// This file contains reflection info interfaces. +// +//===----------------------------------------------------------------------===// +#include "llvm/ADT/SetVector.h" +#include + +namespace llvm { +enum PackageInfoType : uint8_t { + PIT_FLAG = 0, + PIT_VERSION, + PIT_MODULE_NAME, + PIT_PACKAGE_NAME, + PIT_CURRENT_NAME, + PIT_NEXT_NAME = 5, + PIT_MAX, +}; + +enum ClassReflectionType : uint8_t { + CRT_TYPE_NAME = 0, + CRT_GENERIC_CLASS, + CRT_INSTANCE_FIELD, + CRT_STATIC_FIELD, + CRT_INSTANCE_METHOD, + CRT_STATIC_METHOD = 5, + CRT_ATTRIBUTE, + CRT_MAX, +}; + +enum ClassDebugInfoType : uint8_t { + CDT_TYPE_NAME = 0, + CDT_INSTANCE_FIELD, + CDT_MAX, +}; + +enum EnumReflectionType : uint8_t { + ERT_TYPE_NAME = 0, + ERT_ENUM_CTOR, + ERT_INSTANCE_METHOD, + ERT_STATIC_METHOD, + ERT_ATTRIBUTE, + ERT_MAX, +}; + +enum EnumDebugInfoType : uint8_t { + EDT_TYPE_NAME = 0, + EDT_ENUM_CTOR, + EDT_ATTRIBUTE, + EDT_MAX, +}; + +enum MethodReflectionType : uint8_t { + MRT_NAME = 0, + MRT_RETURN_TYPE, + MRT_LINKAGE_NAME, + MRT_ACTUAL_PARAM, + MRT_GENERIC_PARAM, + MRT_ATTRIBUTE = 5, + MRT_MAX, +}; + +enum FillPackageInfoType : uint8_t { + FPT_NEXT_PACKAGE = 0, + FPT_TYPE_INFO_CNT, + FPT_TYPE_TEMPLATE_CNT, + FPT_GLOBAL_FUNC_CNT, + FPT_GLOBAL_VAR_CNT, + FPT_FLAG = 5, + FPT_MODULE_NAME, + FPT_PACKAGE_NAME, + FPT_VERSION, + FPT_PADDING1, + FPT_PADDING2 = 10, + FPT_PTRS, +}; + +enum FillReflectInfoType : uint8_t { + FRT_FIELD_NAME = 0, + FRT_MODIFIER, + FRT_INSTANCE_FIELD, + FRT_STATIC_FIELD, + FRT_INSTANCE_METHOD, + FRT_STATIC_METHOD = 5, + FRT_ANNOTATION, + FRT_GENERIC_CLASS, + FRT_PTRS, +}; + +enum FillEnumReflectInfoType : uint8_t { + FET_CTOR_INFO = 0, + FET_MODIFIER, + FET_CTOR_CNT, + FET_INSTANCE_METHOD, + FET_STATIC_METHOD, + FET_ANNOTATION = 5, + FET_PTRS, +}; + +struct BaseInfo { + Module &M; + LLVMContext &C; + Type *Int16Ty = nullptr; + Type *Int32Ty = nullptr; + Type *Int64Ty = nullptr; + PointerType *Int8PtrTy = nullptr; + PointerType *Int32PtrTy = nullptr; + Constant *Int8PtrNull = nullptr; + + explicit BaseInfo(Module &M) : M(M), C(M.getContext()) { + Int16Ty = Type::getInt16Ty(C); + Int32Ty = Type::getInt32Ty(C); + Int64Ty = Type::getInt64Ty(C); + Int8PtrTy = Type::getInt8PtrTy(C); + Int32PtrTy = Type::getInt32PtrTy(C); + Int8PtrNull = ConstantPointerNull::get(Int8PtrTy); + } + + Constant *createGlobalVal(std::string &Str, StringRef TypeName, + SmallVectorImpl &BodyVec); +}; + +struct ParamInfo { + StringRef TypeName; + StringRef ParamName; + Constant *Annotation; +}; + +struct MethodInfo : BaseInfo { + bool IsStatic = false; + unsigned Modifier = 0; + StringRef MethodLinkName; + StringRef MethodName; + StringRef ReturnType; + Constant *Annotation; + SmallVector ActualParams; + SmallVector GenericParams; + + explicit MethodInfo(Module &M) : BaseInfo(M) {} + + Constant *fillMethodInfo(unsigned Idx, StringRef ClassName); + Constant *fillActualParamInfo(StringRef Prefix); + Constant *fillGenericParamInfo(StringRef Prefix); +}; + +struct EnumCtorInfo { + StringRef CtorName; + StringRef TypeName; +}; + +struct FieldInfo : BaseInfo { + bool IsStatic = false; + unsigned Modifier = 0; + unsigned FieldIdx = 0; + StringRef StaticName; + StringRef TypeName; + StringRef FieldName; + Constant *Annotation; + + explicit FieldInfo(Module &M) : BaseInfo(M) {} + + Constant *fillStaticFieldInfo(); + Constant *fillInstanceFieldInfo(unsigned Idx, StringRef ClassName); +}; + +struct ClassReflectInfo : BaseInfo { + unsigned Modifier = 0; + Constant *Annotation; + StringRef GenericClass; + SmallVector FieldNames; + SmallVector InstanceFields; + SmallVector StaticFields; + SmallVector InstanceMethods; + SmallVector StaticMethods; + + explicit ClassReflectInfo(Module &M) : BaseInfo(M) {} + + Constant *fillInstanceFieldNames(StringRef ClassName); +}; + +struct EnumReflectInfo : BaseInfo { + unsigned Modifier = 0; + Constant *Annotation; + SmallVector EnumCtors; + SmallVector InstanceMethods; + SmallVector StaticMethods; + + explicit EnumReflectInfo(Module &M) : BaseInfo(M) {} + + Constant *fillEnumCtorInfos(StringRef TypeInfoName); +}; + +class ReflectInfo : BaseInfo { +public: + explicit ReflectInfo(Module &M); + ~ReflectInfo() { + for (auto M : AllMethods) + delete (M); + + for (auto F : AllFields) + delete (F); + } + Constant *getReflectionInfo(GlobalVariable *GV, bool IsEnum); + Constant *getClassReflectInfo(const MDTuple *ReflectMD); + Constant *getEnumReflectInfo(const MDTuple *ReflectMD); + Constant *getClassDebugInfo(const MDTuple *ReflectMD); + Constant *getEnumDebugInfo(const MDTuple *ReflectMD); + bool fillPackageInfo(); + static bool enable(Module &M); + +private: + StringRef ModuleName; + StringRef PkgName; + + SetVector TypeInfos; + SetVector TypeTemplates; + std::map StaticGlobalMethods; + std::map StaticGlobalFields; + + const DataLayout &DL; + StringRef TIName = ""; + + SmallVector AllMethods; + SmallVector AllFields; + + std::pair + buildPackageInfoGV(std::string PackName); + void parseAttributes(MDTuple *Attr, unsigned &Modifier, + Constant *&Annotation); + void parseInstanceFieldMDs(ClassReflectInfo &Info, const MDTuple *FieldMDs); + void parseStaticFieldMDs(ClassReflectInfo &Info, const MDTuple *FieldMDs); + void parseParamMd(SmallVectorImpl &ParamInfos, MDTuple *ParamMD); + void parseGenericParamMd(SmallVectorImpl &ParamInfos, + MDTuple *ParamMD); + bool parseOneMethodMd(MethodInfo &Info, MDTuple *MethodMD, bool IsStatic); + void parseInstanceMethodMDs(SmallVectorImpl &Infos, + MDTuple *MethodMDs); + void parseStaticMethodMDs(SmallVectorImpl &Infos, + MDTuple *MethodMDs); + void parseEnumCtorMDs(SmallVectorImpl &Info, MDTuple *CtorMDs); + void parseOneInstanceFieldMd(FieldInfo &Info, MDTuple *FieldMD); + void parseOneStaticFieldMd(FieldInfo &Info, MDTuple *FieldMD); + void fillClassReflectInfo(ClassReflectInfo &Info, + SmallVector &BodyVec); + void fillEnumReflectInfo(EnumReflectInfo &Info, + SmallVector &BodyVec); + void getGlobalsFromNamedMD(SetVector &GVs, + NamedMDNode *MDNode); + MethodInfo *getMethodInfo() { + MethodInfo *MI = new MethodInfo(M); + AllMethods.push_back(MI); + return MI; + } + FieldInfo *getFieldInfo() { + FieldInfo *FI = new FieldInfo(M); + AllFields.push_back(FI); + return FI; + } +}; +StringRef getStringFromMD(const MDTuple *MD, unsigned Idx); +MDTuple *getMDOperand(const MDTuple *MD, unsigned Idx); +} // namespace llvm diff --git a/llvm/include/llvm/Transforms/Utils/CodeExtractor.h b/llvm/include/llvm/Transforms/Utils/CodeExtractor.h index bb23cf4a9..1fc1b0149 100644 --- a/llvm/include/llvm/Transforms/Utils/CodeExtractor.h +++ b/llvm/include/llvm/Transforms/Utils/CodeExtractor.h @@ -16,6 +16,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/SetVector.h" #include @@ -84,6 +85,7 @@ public: /// as arguments, and inserting stores to the arguments for any scalars. class CodeExtractor { using ValueSet = SetVector; + using ValueMap = MapVector; // Various bits of state computed on construction. DominatorTree *const DT; @@ -193,6 +195,19 @@ public: void findInputsOutputs(ValueSet &Inputs, ValueSet &Outputs, const ValueSet &Allocas) const; + /// An overloaded function. + /// + /// Differently, record bitcasts that are derived pointers but not structs. + /// Meanwhile replace them with their base values in inputs. + bool findInputsOutputs(ValueSet &Inputs, ValueSet &Outputs, + const ValueSet &Allocas, + ValueSet &BitcastInstrs) const; + + /// Establish a map BasePtrCands from struct pointers in Values to their + /// base pointers. + void insertStructBaseInArguments(Function *F, ValueSet &Values, + ValueMap &BasePtrCands); + /// Check if life time marker nodes can be hoisted/sunk into the outline /// region. /// @@ -245,11 +260,14 @@ public: void severSplitPHINodesOfExits(const SmallPtrSetImpl &Exits); void splitReturnBlocks(); - Function *constructFunction(const ValueSet &inputs, - const ValueSet &outputs, - BasicBlock *header, - BasicBlock *newRootNode, BasicBlock *newHeader, - Function *oldFunction, Module *M); + Function *constructFunction(const ValueSet &Inputs, + const ValueSet &Outputs, + const ValueSet &BitcastInstrs, + const ValueMap &InputsBasePtrCands, + const ValueMap &OutputsBasePtrCands, + BasicBlock *Header, + BasicBlock *NewRootNode, BasicBlock *NewHeader, + Function *OldFunction, Module *M); void moveCodeToFunction(Function *newFunction); @@ -258,9 +276,11 @@ public: DenseMap &ExitWeights, BranchProbabilityInfo *BPI); - CallInst *emitCallAndSwitchStatement(Function *newFunction, - BasicBlock *newHeader, - ValueSet &inputs, ValueSet &outputs); + CallInst *emitCallAndSwitchStatement(Function *NewFunction, + BasicBlock *NewHeader, + ValueSet &Inputs, ValueSet &Outputs, + ValueMap &InputsBasePtrCands, + ValueMap &OutputsBasePtrCands); }; } // end namespace llvm diff --git a/llvm/include/llvm/Transforms/Utils/Local.h b/llvm/include/llvm/Transforms/Utils/Local.h index 946fc84b9..f86ff9b2b 100644 --- a/llvm/include/llvm/Transforms/Utils/Local.h +++ b/llvm/include/llvm/Transforms/Utils/Local.h @@ -176,6 +176,9 @@ bool simplifyCFG(BasicBlock *BB, const TargetTransformInfo &TTI, const SimplifyCFGOptions &Options = {}, ArrayRef LoopHeaders = {}); +bool simplifyFunctionCFG(Function &F, const TargetTransformInfo &TTI, + DominatorTree *DT, const SimplifyCFGOptions &Options); + /// This function is used to flatten a CFG. For example, it uses parallel-and /// and parallel-or mode to collapse if-conditions and merge if-regions with /// identical statements. diff --git a/llvm/include/llvm/Transforms/Utils/LoopUtils.h b/llvm/include/llvm/Transforms/Utils/LoopUtils.h index adb39a410..83c41a65e 100644 --- a/llvm/include/llvm/Transforms/Utils/LoopUtils.h +++ b/llvm/include/llvm/Transforms/Utils/LoopUtils.h @@ -47,6 +47,9 @@ typedef std::pair RuntimePointerCheck; +// A pair of a set of MustAlias Values and their base pointer if exist +using MustAliasPair = std::pair, Value *>; + template class Optional; template class SmallSetVector; template class SmallPriorityWorklist; @@ -208,11 +211,11 @@ void breakLoopBackedge(Loop *L, DominatorTree &DT, ScalarEvolution &SE, /// \p AllowSpeculation is whether values should be hoisted even if they are not /// guaranteed to execute in the loop, but are safe to speculatively execute. bool promoteLoopAccessesToScalars( - const SmallSetVector &, SmallVectorImpl &, + const MustAliasPair &, SmallVectorImpl &, SmallVectorImpl &, SmallVectorImpl &, PredIteratorCache &, LoopInfo *, DominatorTree *, const TargetLibraryInfo *, Loop *, MemorySSAUpdater &, ICFLoopSafetyInfo *, - OptimizationRemarkEmitter *, bool AllowSpeculation); + OptimizationRemarkEmitter *, bool AllowSpeculation, AAResults *AA); /// Does a BFS from a given node to all of its children inside a given loop. /// The returned vector of nodes includes the starting point. diff --git a/llvm/include/llvm/Transforms/Utils/MemoryTaggingSupport.h b/llvm/include/llvm/Transforms/Utils/MemoryTaggingSupport.h index a2b85e038..86999734c 100644 --- a/llvm/include/llvm/Transforms/Utils/MemoryTaggingSupport.h +++ b/llvm/include/llvm/Transforms/Utils/MemoryTaggingSupport.h @@ -6,7 +6,7 @@ //===----------------------------------------------------------------------===// // // This file declares common infrastructure for HWAddressSanitizer and -// Aarch64StackTagging. +// AArch64StackTagging. // //===----------------------------------------------------------------------===// #ifndef LLVM_TRANSFORMS_UTILS_MEMORYTAGGINGSUPPORT_H diff --git a/llvm/include/llvm/Transforms/Utils/SCCPSolver.h b/llvm/include/llvm/Transforms/Utils/SCCPSolver.h index 17bd07259..a2c1473ce 100644 --- a/llvm/include/llvm/Transforms/Utils/SCCPSolver.h +++ b/llvm/include/llvm/Transforms/Utils/SCCPSolver.h @@ -16,6 +16,7 @@ #include "llvm/ADT/MapVector.h" #include "llvm/Analysis/DomTreeUpdater.h" +#include "llvm/Analysis/LoopInfo.h" #include "llvm/Transforms/Utils/PredicateInfo.h" #include @@ -41,6 +42,7 @@ struct AnalysisResultsForFn { std::unique_ptr PredInfo; DominatorTree *DT; PostDominatorTree *PDT; + LoopInfo *LI = nullptr; }; /// Helper struct shared between Function Specialization and SCCP Solver. diff --git a/llvm/include/llvm/Transforms/Utils/VNCoercion.h b/llvm/include/llvm/Transforms/Utils/VNCoercion.h index 1cc751d1e..b432db860 100644 --- a/llvm/include/llvm/Transforms/Utils/VNCoercion.h +++ b/llvm/include/llvm/Transforms/Utils/VNCoercion.h @@ -21,6 +21,7 @@ #ifndef LLVM_TRANSFORMS_UTILS_VNCOERCION_H #define LLVM_TRANSFORMS_UTILS_VNCOERCION_H +#include "llvm/IR/IntrinsicInst.h" namespace llvm { class Constant; class StoreInst; @@ -54,6 +55,15 @@ Value *coerceAvailableValueToLoadType(Value *StoredVal, Type *LoadedTy, int analyzeLoadFromClobberingStore(Type *LoadTy, Value *LoadPtr, StoreInst *DepSI, const DataLayout &DL); +/// This function determines whether a value for the pointer LoadPtr can be +/// extracted from the gcwrite at DepSI. +/// +/// On success, it returns the offset into DepSI that extraction would start. +/// On failure, it returns -1. +int anaLyzeLoadFromClobberingGCWrite(Type *LoadTy, Value *LoadPtr, + IntrinsicInst *DepII, + const DataLayout &DL); + /// This function determines whether a value for the pointer LoadPtr can be /// extracted from the load at DepLI. /// diff --git a/llvm/include/llvm/Transforms/Utils/ValueMapper.h b/llvm/include/llvm/Transforms/Utils/ValueMapper.h index 95fd0b14d..e80438c8a 100644 --- a/llvm/include/llvm/Transforms/Utils/ValueMapper.h +++ b/llvm/include/llvm/Transforms/Utils/ValueMapper.h @@ -30,6 +30,8 @@ class Type; class Value; using ValueToValueMapTy = ValueMap; +// map between old type and new type after llvm-link +using TypeToTypeMapTy = DenseMap; /// This is a class that can be implemented by clients to remap types when /// cloning constants and instructions. @@ -42,6 +44,8 @@ public: /// The client should implement this method if they want to remap types while /// mapping values. virtual Type *remapType(Type *SrcTy) = 0; + + virtual void mapMetadata(LLVMContext &C, Value *V) = 0; }; /// This is a class that can be implemented by clients to materialize Values on diff --git a/llvm/include/llvm/Transforms/Vectorize/LoopVectorizationLegality.h b/llvm/include/llvm/Transforms/Vectorize/LoopVectorizationLegality.h index 2d69c2f86..a3164fd08 100644 --- a/llvm/include/llvm/Transforms/Vectorize/LoopVectorizationLegality.h +++ b/llvm/include/llvm/Transforms/Vectorize/LoopVectorizationLegality.h @@ -302,7 +302,7 @@ public: /// Returns True if given store is a final invariant store of one of the /// reductions found in the loop. - bool isInvariantStoreOfReduction(StoreInst *SI); + bool isInvariantStoreOfReduction(Instruction *SI); /// Returns True if given address is invariant and is used to store recurrent /// expression diff --git a/llvm/include/llvm/Transforms/Vectorize/SLPVectorizer.h b/llvm/include/llvm/Transforms/Vectorize/SLPVectorizer.h index b41f3efc5..bbb0ea7e3 100644 --- a/llvm/include/llvm/Transforms/Vectorize/SLPVectorizer.h +++ b/llvm/include/llvm/Transforms/Vectorize/SLPVectorizer.h @@ -58,6 +58,8 @@ struct SLPVectorizerPass : public PassInfoMixin { using StoreListMap = MapVector; using GEPList = SmallVector; using GEPListMap = MapVector; + using CJGCWriteList = SmallVector; + using CJGCWriteListMap = MapVector; ScalarEvolution *SE = nullptr; TargetTransformInfo *TTI = nullptr; @@ -135,13 +137,17 @@ private: bool vectorizeStoreChain(ArrayRef Chain, slpvectorizer::BoUpSLP &R, unsigned Idx, unsigned MinVF); - bool vectorizeStores(ArrayRef Stores, slpvectorizer::BoUpSLP &R); + template + bool vectorizeStoresOrGCWrites(ArrayRef Stores, slpvectorizer::BoUpSLP &R); /// The store instructions in a basic block organized by base pointer. StoreListMap Stores; /// The getelementptr instructions in a basic block organized by base pointer. GEPListMap GEPs; + + /// The gc write barrier in a basic block organized by base pointer. + CJGCWriteListMap CJGCWrites; }; } // end namespace llvm diff --git a/llvm/lib/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp index e249c38ec..f7c7ae6f7 100644 --- a/llvm/lib/Analysis/AliasAnalysis.cpp +++ b/llvm/lib/Analysis/AliasAnalysis.cpp @@ -42,6 +42,7 @@ #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/IR/Type.h" #include "llvm/IR/Value.h" #include "llvm/InitializePasses.h" @@ -680,7 +681,6 @@ ModRefInfo AAResults::getModRefInfo(const Instruction *I, } const MemoryLocation &Loc = OptLoc.value_or(MemoryLocation()); - switch (I->getOpcode()) { case Instruction::VAArg: return getModRefInfo((const VAArgInst *)I, Loc, AAQIP); diff --git a/llvm/lib/Analysis/AliasSetTracker.cpp b/llvm/lib/Analysis/AliasSetTracker.cpp index bb25244a8..ab11c65f4 100644 --- a/llvm/lib/Analysis/AliasSetTracker.cpp +++ b/llvm/lib/Analysis/AliasSetTracker.cpp @@ -438,7 +438,15 @@ void AliasSetTracker::add(Instruction *I) { return add(MTI); // Handle all calls with known mod/ref sets genericall - if (auto *Call = dyn_cast(I)) + if (auto *Call = dyn_cast(I)) { + if (Call->getIntrinsicID() == Intrinsic::cj_gcwrite_ref) { + auto &AS = addPointer(MemoryLocation::get(Call), AliasSet::ModAccess); + if (AS.isMustAlias() && !AS.getBasePtr()) + AS.setBasePtr(Call->getOperand(1)); + else if (!AS.isMustAlias()) + AS.clearBasePtr(); + return; + } if (Call->onlyAccessesArgMemory()) { auto getAccessFromModRef = [](ModRefInfo MRI) { if (isRefSet(MRI) && isModSet(MRI)) @@ -475,6 +483,7 @@ void AliasSetTracker::add(Instruction *I) { } return; } + } return addUnknown(I); } diff --git a/llvm/lib/Analysis/Analysis.cpp b/llvm/lib/Analysis/Analysis.cpp index 460dddcea..9755a5050 100644 --- a/llvm/lib/Analysis/Analysis.cpp +++ b/llvm/lib/Analysis/Analysis.cpp @@ -33,6 +33,8 @@ void llvm::initializeAnalysis(PassRegistry &Registry) { initializeCFGPrinterLegacyPassPass(Registry); initializeCFGOnlyViewerLegacyPassPass(Registry); initializeCFGOnlyPrinterLegacyPassPass(Registry); + initializeCJCFGViewerLegacyPassPass(Registry); + initializeCJCFGPrinterLegacyPassPass(Registry); initializeCFLAndersAAWrapperPassPass(Registry); initializeCFLSteensAAWrapperPassPass(Registry); initializeCycleInfoWrapperPassPass(Registry); diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp index c3b032abc..94ec02e6f 100644 --- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -45,6 +45,7 @@ #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Operator.h" +#include "llvm/IR/SafepointIRVerifier.h" #include "llvm/IR/Type.h" #include "llvm/IR/User.h" #include "llvm/IR/Value.h" @@ -63,6 +64,10 @@ using namespace llvm; +namespace llvm { +extern cl::opt CJPipeline; +} + /// Enable analysis of recursive PHI nodes. static cl::opt EnableRecPhiAnalysis("basic-aa-recphi", cl::Hidden, cl::init(true)); @@ -946,10 +951,14 @@ ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call, Result = setRef(Result); continue; } - // Operand aliases 'Object' but call only writes into it. - if (Call->onlyWritesMemory(OperandNo)) { - Result = setMod(Result); - continue; + // Parameters of the GCPtr type in cangjie are read by the runtime and + // therefore cannot be considered writeonly in any case. + if (!CJPipeline || !isGCPointerType((*CI)->getType())) { + // Operand aliases 'Object' but call only writes into it. + if (Call->onlyWritesMemory(OperandNo)) { + Result = setMod(Result); + continue; + } } // This operand aliases 'Object' and call reads and writes into it. // Setting ModRef will not yield an early return below, MustAlias is not diff --git a/llvm/lib/Analysis/CFGPrinter.cpp b/llvm/lib/Analysis/CFGPrinter.cpp index f8eba1a00..f6356240b 100644 --- a/llvm/lib/Analysis/CFGPrinter.cpp +++ b/llvm/lib/Analysis/CFGPrinter.cpp @@ -21,7 +21,6 @@ #include "llvm/ADT/PostOrderIterator.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" -#include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/GraphWriter.h" @@ -32,6 +31,19 @@ static cl::opt cl::desc("The name of a function (or its substring)" " whose CFG is viewed/printed.")); +cl::opt CJCFGARG("cj-cfg-arg", cl::Hidden, + cl::desc("Arg name to print")); + +cl::opt IfCJCFGOnly("cj-cfg-only", cl::init(false), cl::Hidden, + cl::desc("If cj cfg only")); + +cl::opt IfCJCFGComplex("cj-cfg-complex", cl::init(false), cl::Hidden, + cl::desc("If cj cfg complex")); + +// 80: llvm default cfg columns +cl::opt CJCFGCol("cj-cfg-columns", cl::init(80), cl::Hidden, + cl::desc("Max columns of cj cfg")); + static cl::opt CFGDotFilenamePrefix( "cfg-dot-filename-prefix", cl::Hidden, cl::desc("The prefix used for the CFG dot file names.")); @@ -92,6 +104,20 @@ static void viewCFG(Function &F, const BlockFrequencyInfo *BFI, ViewGraph(&CFGInfo, "cfg." + F.getName(), CFGOnly); } +// Set name for all instructions for CJ DFX +static void setNameCJDFX(Function &F) { + unsigned Index = 0; + for (auto &Arg : F.args()) { + if (!Arg.hasName()) + Arg.setName("d" + Twine(Index++)); + } + for (auto &I : instructions(F)) { + if (I.getType() != Type::getVoidTy(F.getContext()) && !I.hasName()) { + I.setName("d" + Twine(Index++)); + } + } +} + namespace { struct CFGViewerLegacyPass : public FunctionPass { static char ID; // Pass identifcation, replacement for typeid @@ -255,6 +281,92 @@ PreservedAnalyses CFGOnlyPrinterPass::run(Function &F, return PreservedAnalyses::all(); } +namespace { +struct CJCFGViewerLegacyPass : public FunctionPass { + static char ID; // Pass identifcation, replacement for typeid + CJCFGViewerLegacyPass() : FunctionPass(ID) { + initializeCJCFGViewerLegacyPassPass(*PassRegistry::getPassRegistry()); + } + + bool runOnFunction(Function &F) override { + if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName)) + return false; + setNameCJDFX(F); + auto *BPI = &getAnalysis().getBPI(); + auto *BFI = &getAnalysis().getBFI(); + viewCFG(F, BFI, BPI, getMaxFreq(F, BFI)); + return false; + } + + void print(raw_ostream &OS, const Module * = nullptr) const override {} + + void getAnalysisUsage(AnalysisUsage &AU) const override { + FunctionPass::getAnalysisUsage(AU); + AU.addRequired(); + AU.addRequired(); + AU.setPreservesAll(); + } +}; +} // namespace + +char CJCFGViewerLegacyPass::ID = 0; +INITIALIZE_PASS(CJCFGViewerLegacyPass, "view-cj-cfg", "View CJ CFG of function", + false, true) + +PreservedAnalyses CJCFGViewerPass::run(Function &F, + FunctionAnalysisManager &AM) { + if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName)) + return PreservedAnalyses::all(); + setNameCJDFX(F); + auto *BFI = &AM.getResult(F); + auto *BPI = &AM.getResult(F); + viewCFG(F, BFI, BPI, getMaxFreq(F, BFI)); + return PreservedAnalyses::all(); +} + +namespace { +struct CJCFGPrinterLegacyPass : public FunctionPass { + static char ID; // Pass identification, replacement for typeid + CJCFGPrinterLegacyPass() : FunctionPass(ID) { + initializeCJCFGPrinterLegacyPassPass(*PassRegistry::getPassRegistry()); + } + + bool runOnFunction(Function &F) override { + if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName)) + return false; + setNameCJDFX(F); + auto *BPI = &getAnalysis().getBPI(); + auto *BFI = &getAnalysis().getBFI(); + writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI)); + return false; + } + + void print(raw_ostream &OS, const Module * = nullptr) const override {} + + void getAnalysisUsage(AnalysisUsage &AU) const override { + FunctionPass::getAnalysisUsage(AU); + AU.addRequired(); + AU.addRequired(); + AU.setPreservesAll(); + } +}; +} // namespace + +char CJCFGPrinterLegacyPass::ID = 0; +INITIALIZE_PASS(CJCFGPrinterLegacyPass, "dot-cj-cfg", + "Print CJ CFG of function to 'dot' file", false, true) + +PreservedAnalyses CJCFGPrinterPass::run(Function &F, + FunctionAnalysisManager &AM) { + if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName)) + return PreservedAnalyses::all(); + setNameCJDFX(F); + auto *BFI = &AM.getResult(F); + auto *BPI = &AM.getResult(F); + writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI)); + return PreservedAnalyses::all(); +} + /// viewCFG - This function is meant for use from the debugger. You can just /// say 'call F->viewCFG()' and a ghostview window should pop up from the /// program, displaying the CFG of the current function. This depends on there @@ -290,6 +402,14 @@ FunctionPass *llvm::createCFGOnlyPrinterLegacyPassPass() { return new CFGOnlyPrinterLegacyPass(); } +FunctionPass *llvm::createCJCFGViewerLegacyPassPass() { + return new CJCFGViewerLegacyPass(); +} + +FunctionPass *llvm::createCJCFGPrinterLegacyPassPass() { + return new CJCFGPrinterLegacyPass(); +} + /// Find all blocks on the paths which terminate with a deoptimize or /// unreachable (i.e. all blocks which are post-dominated by a deoptimize /// or unreachable). These paths are hidden if the corresponding cl::opts diff --git a/llvm/lib/Analysis/CJAliasAnalysis.cpp b/llvm/lib/Analysis/CJAliasAnalysis.cpp new file mode 100644 index 000000000..096c78996 --- /dev/null +++ b/llvm/lib/Analysis/CJAliasAnalysis.cpp @@ -0,0 +1,302 @@ +//===- CJAliasAnalysis.cpp --------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file declares a simple AliasAnalysis using special knowledge +// of Cangjie to enhance other optimization passes which rely on the Alias +// Analysis infrastructure. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/CJAliasAnalysis.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/MemoryLocation.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/Argument.h" +#include "llvm/IR/CJIntrinsics.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/PassManager.h" +#include "llvm/IR/SafepointIRVerifier.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/PassRegistry.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm; +using namespace cangjie; + +namespace llvm { +extern cl::opt CJPipeline; +cl::opt EnableCJAA("enbale-cangjie-alias-analysis", + cl::desc("enbale/disable cangjie alias analysis"), + cl::init(CJPipeline), cl::Hidden); +} // namespace llvm + +static const unsigned MaxLookupSearchDepth = 6; + +bool CJAAResult::invalidate(Function &Fn, const PreservedAnalyses &PA, + FunctionAnalysisManager::Invalidator &Inv) { + return DT && Inv.invalidate(Fn, PA); +} + +static bool isGCPointerOrGlobalBase(const Value *V, + unsigned MaxLookup = MaxLookupSearchDepth) { + V = getUnderlyingObject(V, 0); + if (isa(V)) + return false; + if (isa(V)) + return true; + if (isa(V)) + return isGCPointerType(V->getType()); + if (auto *LI = dyn_cast(V)) + return isGCPointerOrGlobalBase(LI->getPointerOperand(), --MaxLookup); + if (auto *II = dyn_cast(V)) { + switch (II->getIntrinsicID()) { + case Intrinsic::cj_gcread_ref: + V = II->getArgOperand(1); + break; + case Intrinsic::cj_gcread_static_ref: + V = II->getArgOperand(0); + break; + default: + return false; + } + return isGCPointerOrGlobalBase(V, --MaxLookup); + } + if (auto *CB = dyn_cast(V)) { + if (CB->getCalledFunction() && + CB->getCalledFunction()->hasFnAttribute("cj-heapmalloc")) + return true; + else + return false; + } + // Otherwise, no handling is required. + return false; +} + +// Checking the alias relationship Between cj.malloc and V +static AliasResult checkHeapMallocAlias(const CallBase *CB, const Value *O, + const DominatorTree *DT) { + if (CB->getCalledFunction() && + CB->getCalledFunction()->hasFnAttribute("cj-heapmalloc")) { + if (auto *I = dyn_cast(O); I && DT->dominates(I, CB)) + return AliasResult::NoAlias; + if (isa(O)) + return AliasResult::NoAlias; + } + return AliasResult::MayAlias; +} + +AliasResult CJAAResult::alias(const MemoryLocation &LocA, + const MemoryLocation &LocB, AAQueryInfo &AAQI) { + const Value *O1 = getUnderlyingObject(LocA.Ptr, MaxLookupSearchDepth); + const Value *O2 = getUnderlyingObject(LocB.Ptr, MaxLookupSearchDepth); + // In cangjie, stack space and heap space has different addrspace, and they + // can never be aliased. + bool IsStackValue1 = isa(O1) || + (isa(O1) && !isGCPointerType(O1->getType())); + bool IsStackValue2 = isa(O2) || + (isa(O2) && !isGCPointerType(O2->getType())); + if ((IsStackValue1 && isGCPointerOrGlobalBase(O2)) || + (IsStackValue2 && isGCPointerOrGlobalBase(O1))) + return AliasResult::NoAlias; + bool IsNotGV1 = isa(O1) || isGCPointerType(O1->getType()); + bool IsNotGV2 = isa(O2) || isGCPointerType(O2->getType()); + if ((IsNotGV1 && isa(O2)) || (IsNotGV2 && isa(O1))) + return AliasResult::NoAlias; + + // %0 = load ... + // %1 = call void CJ_MCC_New(...) + // %2 = load %0 + // %3 = load %1 + // If one of the MemoryLocations is new, any MemoryLocations before it will + // not be aliased to it. + if (O1 != O2) { + if (auto *Call1 = dyn_cast(O1); + Call1 && checkHeapMallocAlias(Call1, O2, DT) == AliasResult::NoAlias) + return AliasResult::NoAlias; + if (auto *Call2 = dyn_cast(O2); + Call2 && checkHeapMallocAlias(Call2, O1, DT) == AliasResult::NoAlias) + return AliasResult::NoAlias; + } + + return AAResultBase::alias(LocA, LocB, AAQI); +} + +std::tuple, Optional, + Optional> +getLocInfoForCJIntrinsics(const IntrinsicInst *II, + const TargetLibraryInfo &TLI) { + Optional Src = None; + Optional Dst = None; + Optional Base = None; + switch (II->getIntrinsicID()) { + default: + break; + case Intrinsic::cj_gcwrite_struct: + Src = MemoryLocation::getCJMemTransferSource(II); + Dst = MemoryLocation::getForDest(II, TLI); + Base = MemoryLocation::getAfter(getBaseObj(II)); + break; + case Intrinsic::cj_gcread_struct: + Src = MemoryLocation::getCJMemTransferSource(II); + Dst = MemoryLocation::getForDest(II, TLI); + Base = MemoryLocation::getAfter(getBaseObj(II)); + break; + case Intrinsic::cj_gcwrite_generic: + Src = MemoryLocation::getAfter(getSource(II)); + Dst = MemoryLocation::getAfter(getDest(II)); + Base = MemoryLocation::getAfter(getBaseObj(II)); + break; + case Intrinsic::cj_gcread_generic: + Src = MemoryLocation::getAfter(getSource(II)); + Dst = MemoryLocation::getAfter(getDest(II)); + Base = MemoryLocation::getAfter(getBaseObj(II)); + break; + case Intrinsic::cj_assign_generic: + Src = MemoryLocation::getAfter(getSource(II)); + Dst = MemoryLocation::getAfter(getDest(II)); + break; + } + return {Src, Dst, Base}; +} + +ModRefInfo CJAAResult::getModRefInfo(const CallBase *Call, + const MemoryLocation &Loc, + AAQueryInfo &AAQI) { + if (Call->getCalledFunction() && + Call->getCalledFunction()->hasFnAttribute("cj-heapmalloc")) { + auto AR = getBestAAResults().alias(MemoryLocation::getBeforeOrAfter(Call), + Loc, AAQI); + if (AR == AliasResult::NoAlias) + return ModRefInfo::NoModRef; + } + auto ID = Call->getIntrinsicID(); + auto MRI = ModRefInfo::ModRef; + switch (ID) { + default: + break; + case Intrinsic::lifetime_end: { + auto AR = getBestAAResults().alias( + MemoryLocation::getBeforeOrAfter(Call->getArgOperand(1)), Loc, AAQI); + if (AR == AliasResult::NoAlias) + MRI = ModRefInfo::NoModRef; + } break; + case Intrinsic::cj_gcread_ref: + case Intrinsic::cj_gcread_static_ref: { + auto AR = getBestAAResults().alias(MemoryLocation::get(Call), Loc, AAQI); + if (AR == AliasResult::NoAlias) + MRI = ModRefInfo::NoModRef; + else if (AR == AliasResult::MustAlias) + MRI = ModRefInfo::MustRef; + else + MRI = ModRefInfo::Ref; + } break; + case Intrinsic::cj_gcwrite_ref: + case Intrinsic::cj_gcwrite_static_ref: { + auto AR = getBestAAResults().alias(MemoryLocation::get(Call), Loc, AAQI); + if (AR == AliasResult::NoAlias) + MRI = ModRefInfo::NoModRef; + else if (AR == AliasResult::MustAlias) + MRI = ModRefInfo::MustMod; + else + MRI = ModRefInfo::Mod; + } break; + case Intrinsic::cj_gcread_static_struct: + case Intrinsic::cj_gcwrite_static_struct: { + auto SrcAA = getBestAAResults().alias( + MemoryLocation::getCJMemTransferSource(cast(Call)) + .getValue(), + Loc, AAQI); + auto DestAA = getBestAAResults().alias( + MemoryLocation::getForDest(Call, TLI).getValue(), Loc, AAQI); + MRI = ModRefInfo::NoModRef; + if (SrcAA != AliasResult::NoAlias) + MRI = setRef(MRI); + if (DestAA != AliasResult::NoAlias) + MRI = setMod(MRI); + } break; + case Intrinsic::cj_gcread_struct: + case Intrinsic::cj_gcwrite_struct: + case Intrinsic::cj_gcwrite_generic: + case Intrinsic::cj_gcread_generic: + case Intrinsic::cj_assign_generic: { + auto [LocS, LocD, LocB] = + getLocInfoForCJIntrinsics(cast(Call), TLI); + if (!LocS || !LocD) + report_fatal_error("error"); + auto SrcAA = getBestAAResults().alias(LocS.getValue(), Loc, AAQI); + auto DstAA = getBestAAResults().alias(LocD.getValue(), Loc, AAQI); + auto BaseAA = AliasResult::NoAlias; + if (LocB) + BaseAA = getBestAAResults().alias(LocB.getValue(), Loc, AAQI); + if (SrcAA != AliasResult::NoAlias || BaseAA != AliasResult::NoAlias) + MRI = setRef(MRI); + if (DstAA != AliasResult::NoAlias) + MRI = setMod(MRI); + } break; + case Intrinsic::cj_memset: + auto AR = getBestAAResults().alias( + MemoryLocation::getAfter(Call->getArgOperand(0)), Loc, AAQI); + if (AR == AliasResult::NoAlias) + MRI = ModRefInfo::NoModRef; + else + MRI = ModRefInfo::Mod; + break; + } + return MRI; +} + +AnalysisKey CangjieAA::Key; + +CJAAResult CangjieAA::run(Function &F, FunctionAnalysisManager &AM) { + auto &TLI = AM.getResult(F); + auto &DT = AM.getResult(F); + return CJAAResult(F.getParent()->getDataLayout(), TLI, &DT); +} + +CangjieAAWrapperPass::CangjieAAWrapperPass() : FunctionPass(ID) { + initializeCangjieAAWrapperPassPass(*PassRegistry::getPassRegistry()); +} + +INITIALIZE_PASS_BEGIN(CangjieAAWrapperPass, "cangjie-aa", + "Cangjie Alias Analysis", true, true) +INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) +INITIALIZE_PASS_END(CangjieAAWrapperPass, "cangjie-aa", + "Cangjie Alias Analysis", true, true) + +char CangjieAAWrapperPass::ID = 0; + +FunctionPass *llvm::createCangjieAAWrapperPass() { + return new CangjieAAWrapperPass(); +} + +bool CangjieAAWrapperPass::runOnFunction(Function &F) { + auto &TLIWP = getAnalysis(); + auto &DTWP = getAnalysis(); + Result.reset(new CJAAResult(F.getParent()->getDataLayout(), TLIWP.getTLI(F), + &DTWP.getDomTree())); + return false; +} + +void CangjieAAWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesAll(); + AU.addRequired(); + AU.addRequired(); +} diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt index e59725c99..a2b5ebc5c 100644 --- a/llvm/lib/Analysis/CMakeLists.txt +++ b/llvm/lib/Analysis/CMakeLists.txt @@ -21,6 +21,7 @@ if (DEFINED LLVM_HAVE_TF_AOT OR DEFINED LLVM_HAVE_TF_API) list(APPEND MLLinkDeps ${tensorflow_c_api} ${tensorflow_fx}) endif() endif() +include_directories(${CANGJIE_DEMANGLE_DIR}) add_llvm_component_library(LLVMAnalysis AliasAnalysis.cpp @@ -31,6 +32,7 @@ add_llvm_component_library(LLVMAnalysis AssumeBundleQueries.cpp AssumptionCache.cpp BasicAliasAnalysis.cpp + CJAliasAnalysis.cpp BlockFrequencyInfo.cpp BlockFrequencyInfoImpl.cpp BranchProbabilityInfo.cpp @@ -106,6 +108,7 @@ add_llvm_component_library(LLVMAnalysis ModuleSummaryAnalysis.cpp MustExecute.cpp NoInferenceModelRunner.cpp + ObfConfig.cpp ObjCARCAliasAnalysis.cpp ObjCARCAnalysisUtils.cpp ObjCARCInstKind.cpp @@ -145,6 +148,7 @@ add_llvm_component_library(LLVMAnalysis ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/Analysis + ${CANGJIE_DEMANGLE_DIR} DEPENDS intrinsics_gen @@ -160,3 +164,7 @@ add_llvm_component_library(LLVMAnalysis ProfileData Support ) + +if(BUILD_SHARED_LIBS) + target_link_libraries(LLVMAnalysis PRIVATE ${CANGJIE_DEMANGLE_DIR}/libcangjie-demangle.a) +endif() \ No newline at end of file diff --git a/llvm/lib/Analysis/CallGraphSCCPass.cpp b/llvm/lib/Analysis/CallGraphSCCPass.cpp index 8438f33f4..d4d82e0c8 100644 --- a/llvm/lib/Analysis/CallGraphSCCPass.cpp +++ b/llvm/lib/Analysis/CallGraphSCCPass.cpp @@ -16,6 +16,7 @@ #include "llvm/Analysis/CallGraphSCCPass.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/SCCIterator.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/CallGraph.h" @@ -61,6 +62,12 @@ public: static char ID; explicit CGPassManager() : ModulePass(ID) {} + ~CGPassManager() override { + for (auto &OnTheFlyManager : OnTheFlyManagers) { + legacy::FunctionPassManagerImpl *FPP = OnTheFlyManager.second; + delete FPP; + } + } /// Execute all of the passes scheduled for execution. Keep track of /// whether any of the passes modifies the module, and if so, return true. @@ -84,6 +91,17 @@ public: PMDataManager *getAsPMDataManager() override { return this; } Pass *getAsPass() override { return this; } + /// Add RequiredPass into list of lower level passes required by pass P. + /// RequiredPass is run on the fly by Pass Manager when P requests it + /// through getAnalysis interface. + void addLowerLevelRequiredPass(Pass *P, Pass *RequiredPass) override; + + /// Return function pass corresponding to PassInfo PI, that is + /// required by CallGraph Pass. Instantiate analysis pass, by using + /// its runOnFunction() for function F. + std::tuple getOnTheFlyPass(Pass *MP, AnalysisID PI, + Function &F) override; + // Print passes managed by this manager void dumpPassStructure(unsigned Offset) override { errs().indent(Offset*2) << "Call Graph SCC Pass Manager\n"; @@ -112,6 +130,9 @@ private: bool &DevirtualizedCall); bool RefreshCallGraph(const CallGraphSCC &CurSCC, CallGraph &CG, bool IsCheckingMode); + /// Collection of on the fly FPPassManagers. These managers manage + // function passes thar are required by cg passes + MapVector OnTheFlyManagers; }; } // end anonymous namespace. @@ -509,6 +530,11 @@ bool CGPassManager::runOnModule(Module &M) { CallGraph &CG = getAnalysis().getCallGraph(); bool Changed = doInitialization(CG); + for (auto &OnTheFlyManager : OnTheFlyManagers) { + llvm::legacy::FunctionPassManagerImpl *FPP = OnTheFlyManager.second; + Changed |= FPP->doInitialization(M); + } + // Walk the callgraph in bottom-up SCC order. scc_iterator CGI = scc_begin(&CG); @@ -550,9 +576,43 @@ bool CGPassManager::runOnModule(Module &M) { MaxSCCIterations.updateMax(Iteration); } Changed |= doFinalization(CG); + for (auto &OnTheFlyManager : OnTheFlyManagers) { + llvm::legacy::FunctionPassManagerImpl *FPP = OnTheFlyManager.second; + FPP->releaseMemoryOnTheFly(); + Changed |= FPP->doFinalization(M); + } + return Changed; } +/// Return function pass corresponding to PassInfo PI, that is +/// required by CallGraph Pass. Instantiate analysis pass, by using +/// its runOnFunction() for function F. +std::tuple CGPassManager::getOnTheFlyPass(Pass *MP, AnalysisID PI, + Function &F) { + legacy::FunctionPassManagerImpl *FPP = OnTheFlyManagers[MP]; + assert(FPP && "Unable to find on the fly pass"); + + FPP->releaseMemoryOnTheFly(); + bool Changed = FPP->run(F); + return std::make_tuple(((PMTopLevelManager *)FPP)->findAnalysisPass(PI), + Changed); +} + +/// Add RequiredPass into list of lower level passes required by pass P. +/// RequiredPass is run on the fly by Pass Manager when P requests it +/// through getAnalysis interface. +void CGPassManager::addLowerLevelRequiredPass(Pass *P, Pass *RequiredPass) { + assert(RequiredPass && "No required pass?"); + assert(P->getPotentialPassManagerType() == PMT_CallGraphPassManager && + "Unable to handle Pass that requires lower level Analysis pass"); + assert((P->getPotentialPassManagerType() < + RequiredPass->getPotentialPassManagerType()) && + "Unable to handle Pass that requires lower level Analysis pass"); + + addLowerLevelRequiredPassImpl(OnTheFlyManagers, P, RequiredPass); +} + /// Initialize CG bool CGPassManager::doInitialization(CallGraph &CG) { bool Changed = false; diff --git a/llvm/lib/Analysis/IVDescriptors.cpp b/llvm/lib/Analysis/IVDescriptors.cpp index a51e97400..53e928443 100644 --- a/llvm/lib/Analysis/IVDescriptors.cpp +++ b/llvm/lib/Analysis/IVDescriptors.cpp @@ -12,6 +12,7 @@ #include "llvm/Analysis/IVDescriptors.h" #include "llvm/Analysis/DemandedBits.h" +#include "llvm/Analysis/LoopAccessAnalysis.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/ScalarEvolution.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" @@ -324,17 +325,18 @@ bool RecurrenceDescriptor::AddReductionVar( return false; } - const SCEV *PtrScev = SE->getSCEV(SI->getPointerOperand()); + const SCEV *PtrScev = SE->getSCEV(getLoadStorePointerOperand(Cur)); // Check it is the same address as previous stores if (IntermediateStore) { const SCEV *OtherScev = - SE->getSCEV(IntermediateStore->getPointerOperand()); + SE->getSCEV(getLoadStorePointerOperand(IntermediateStore)); if (OtherScev != PtrScev) { - LLVM_DEBUG(dbgs() << "Storing reduction value to different addresses " - << "inside the loop: " << *SI->getPointerOperand() - << " and " - << *IntermediateStore->getPointerOperand() << '\n'); + LLVM_DEBUG(dbgs() + << "Storing reduction value to different addresses " + << "inside the loop: " << *getLoadStorePointerOperand(Cur) + << " and " + << *getLoadStorePointerOperand(IntermediateStore) << '\n'); return false; } } @@ -342,8 +344,8 @@ bool RecurrenceDescriptor::AddReductionVar( // Check the pointer is loop invariant if (!SE->isLoopInvariant(PtrScev, TheLoop)) { LLVM_DEBUG(dbgs() << "Storing reduction value to non-uniform address " - << "inside the loop: " << *SI->getPointerOperand() - << '\n'); + << "inside the loop: " + << *getLoadStorePointerOperand(Cur) << '\n'); return false; } @@ -516,7 +518,8 @@ bool RecurrenceDescriptor::AddReductionVar( // Check that stored value goes to the phi node again. This way we make sure // that the value stored in IntermediateStore is indeed the final reduction // value. - if (!is_contained(Phi->operands(), IntermediateStore->getValueOperand())) { + if (!is_contained(Phi->operands(), + getStoreValueOperand(IntermediateStore))) { LLVM_DEBUG(dbgs() << "Not a final reduction value stored: " << *IntermediateStore << '\n'); return false; @@ -525,7 +528,7 @@ bool RecurrenceDescriptor::AddReductionVar( // If there is an exit instruction it's value should be stored in // IntermediateStore if (ExitInstruction && - IntermediateStore->getValueOperand() != ExitInstruction) { + getStoreValueOperand(IntermediateStore) != ExitInstruction) { LLVM_DEBUG(dbgs() << "Last store Instruction of reduction value does not " "store last calculated value of the reduction: " << *IntermediateStore << '\n'); @@ -535,7 +538,8 @@ bool RecurrenceDescriptor::AddReductionVar( // If all uses are inside the loop (intermediate stores), then the // reduction value after the loop will be the one used in the last store. if (!ExitInstruction) - ExitInstruction = cast(IntermediateStore->getValueOperand()); + ExitInstruction = + cast(getStoreValueOperand(IntermediateStore)); } if (!FoundStartPHI || !FoundReduxOp || !ExitInstruction) diff --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp index 8192ed56c..f2f7050a4 100644 --- a/llvm/lib/Analysis/InlineCost.cpp +++ b/llvm/lib/Analysis/InlineCost.cpp @@ -34,6 +34,7 @@ #include "llvm/IR/Dominators.h" #include "llvm/IR/GetElementPtrTypeIterator.h" #include "llvm/IR/GlobalAlias.h" +#include "llvm/IR/InstIterator.h" #include "llvm/IR/InstVisitor.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Operator.h" @@ -131,11 +132,11 @@ static cl::opt cl::desc("Do not inline functions with a stack size " "that exceeds the specified limit")); -static cl::opt - RecurStackSizeThreshold("recursive-inline-max-stacksize", cl::Hidden, - cl::init(InlineConstants::TotalAllocaSizeRecursiveCaller), - cl::desc("Do not inline recursive functions with a stack " - "size that exceeds the specified limit")); +static cl::opt RecurStackSizeThreshold( + "recursive-inline-max-stacksize", cl::Hidden, + cl::init(InlineConstants::TotalAllocaSizeRecursiveCaller), + cl::desc("Do not inline recursive functions with a stack " + "size that exceeds the specified limit")); static cl::opt OptComputeFullInlineCost( "inline-cost-full", cl::Hidden, @@ -151,7 +152,62 @@ static cl::opt DisableGEPConstOperand( "disable-gep-const-evaluation", cl::Hidden, cl::init(false), cl::desc("Disables evaluation of GetElementPtr with constant operands")); +// This flag puts a limit on the maximum amount of recursion inlining. +// The default is 0, which turns off recursion inlining. +// +// For direct recursion (i.e. A->A or "A calls A"), it bounds the times +// that a function can be inlined into itself. For MaxRecursionInl = 2, +// two inlining steps are performed at most. Step one yields A1 = A+A, +// and step 2 yields A2 = A1+A1 = A+A+A+A. +// +// For indirect recursion (e.g. A->B->A), the first inline step yields +// A1 = A+B and the recursion turns into a direct one: A1->A1. The second +// step yields A2 = A1+A1 = A+B+A+B, with recursion staying as A2->A2. +// Note that the first step will be performed regardless of MaxRecursionInl, +// as inlining B into A does not change recursion. +cl::opt MaxRecursionInl("max-recursion-inline", cl::init(0), cl::Hidden, + cl::ZeroOrMore, + cl::desc("Control the maximum levels of recursion" + " inlining")); + namespace llvm { +cl::opt + CJPipeline("cangjie-pipeline", cl::init(false), cl::Hidden, + cl::desc("use the customized pass pipeline for cangjie")); +cl::opt CangjieJIT("cangjie-JIT", cl::init(false), cl::Hidden, + cl::desc("use the customized JIT for cangjie")); +cl::opt + EnableStackGrow("cj-stack-grow", cl::Hidden, cl::init(true), + cl::desc("support stack capacity expansion on callsite")); +cl::opt + CJInlinePerStackMapThreshold("cj-inline-per-stackmap-threshold", cl::Hidden, + cl::init(15), + cl::desc("use for inline cost calculate")); +cl::opt + CJInlineStackMapCalleecallnumThreshold("cj-inline-stackmap-Calleecallnum-threshold", cl::Hidden, + cl::init(50), + cl::desc("use for inline cost calculate")); +cl::opt + CangjieStackCheckSize("cangjie-stack-check-size", cl::Hidden, + cl::init(1024), + cl::desc("Set Cangjie stack check threshold.")); +cl::opt DisableGCSupport("no-gc-support", cl::Hidden, cl::init(false), + cl::desc("Disable barriers and safepoints")); +cl::opt + EnableSafepointOnly("only-safepoint", cl::Hidden, cl::init(false), + cl::desc("Enable safepoints but disable barriers")); +cl::opt + EnableBarrierOnly("only-barrier", cl::Hidden, cl::init(false), + cl::desc("Enable barriers but disable safepoints")); +cl::opt CJLTOOpt("cj-lto-opt", cl::Hidden, cl::init(false), + cl::desc("This is optimization during cangjie lto!")); +cl::opt LICMDisableBarrier("disable-licm-barrier", cl::Hidden, + cl::init(false)); +cl::opt GVNDisableBarrier("disable-gvn-barrier", cl::Hidden, + cl::init(false)); +cl::opt EnableCJIRCEPass("enable-cj-irce-pass", cl::Hidden, + cl::init(true)); + Optional getStringFnAttrAsInt(CallBase &CB, StringRef AttrKind) { Attribute Attr = CB.getFnAttr(AttrKind); int AttrValue; @@ -629,6 +685,14 @@ class InlineCostCallAnalyzer final : public CallAnalyzer { // We account for the average 1 instruction per call argument setup here. addCost(Call.arg_size() * InlineConstants::InstrCost); + // Operand(2) of NewArray is poison, and it is fake argument. + if (CJPipeline && Call.getCalledFunction() && + Call.getCalledFunction()->getName().startswith("CJ_MCC_NewArray")) { + addCost(-Call.arg_size() * InlineConstants::InstrCost); + // 2: valid operand num + addCost(2 * InlineConstants::InstrCost); + } + // If we have a constant that we are calling as a function, we can peer // through it and see the function target. This happens not infrequently // during devirtualization and so we want to give it a hefty bonus for @@ -992,6 +1056,9 @@ class InlineCostCallAnalyzer final : public CallAnalyzer { if (Cost >= Threshold && !ComputeFullInlineCost) return InlineResult::failure("high cost"); + if (CJPipeline && !checkCJStackMapThreshold(CandidateCall, F)) + return InlineResult::failure("high stackmap cost"); + return InlineResult::success(); } @@ -2113,6 +2180,8 @@ bool CallAnalyzer::simplifyCallSite(Function *F, CallBase &Call) { } bool CallAnalyzer::visitCallBase(CallBase &Call) { + if (CJPipeline && isa(Call.getParent()->getTerminator())) + return false; if (!onCallBaseVisitStart(Call)) return true; @@ -2183,7 +2252,7 @@ bool CallAnalyzer::visitCallBase(CallBase &Call) { } } - if (F == Call.getFunction()) { + if (MaxRecursionInl == 0 && F == Call.getFunction()) { // This flag will fully abort the analysis, so don't bother with anything // else. IsRecursiveCall = true; @@ -2620,6 +2689,8 @@ InlineResult CallAnalyzer::analyze() { BBSetVector BBWorklist; BBWorklist.insert(&F.getEntryBlock()); + SmallSetVector ExecptionBB; + // Note that we *must not* cache the size, this loop grows the worklist. for (unsigned Idx = 0; Idx != BBWorklist.size(); ++Idx) { if (shouldStop()) @@ -2678,11 +2749,40 @@ InlineResult CallAnalyzer::analyze() { } } - // If we're unable to select a particular successor, just count all of - // them. - for (unsigned TIdx = 0, TSize = TI->getNumSuccessors(); TIdx != TSize; - ++TIdx) - BBWorklist.insert(TI->getSuccessor(TIdx)); + if (CJPipeline) { + for (unsigned TIdx = 0, TSize = TI->getNumSuccessors(); TIdx != TSize; + ++TIdx) { + BasicBlock *BB = TI->getSuccessor(TIdx); + if (!isa(BB->getTerminator())) { + BBWorklist.insert(TI->getSuccessor(TIdx)); + continue; + } + + // Each type of unreachable BB is combined into one in the llc phase. + // Therefore, InlineCost can calculate only once. + for (auto &I : *BB) { + if (auto *CI = dyn_cast(&I)) { + auto *Func = CI->getCalledFunction(); + if (!Func) + continue; + const auto &FuncName = Func->getName(); + // TODO Currently, only the following implicit exceptions are + // considered + if (FuncName.contains("IndexOutOfBoundsException") || + FuncName.contains("NegativeArraySizeException") || + FuncName.contains("OverflowException")) { + if (!ExecptionBB.insert(FuncName)) break; + BBWorklist.insert(TI->getSuccessor(TIdx)); + break; + } + } + } + } + } else { + for (unsigned TIdx = 0, TSize = TI->getNumSuccessors(); TIdx != TSize; + ++TIdx) + BBWorklist.insert(TI->getSuccessor(TIdx)); + } onBlockAnalyzed(BB); } @@ -2778,6 +2878,47 @@ int llvm::getCallsiteCost(CallBase &Call, const DataLayout &DL) { return Cost; } +// Calculate the cost of the stackmap based on the number of callsites. This +// check is not precises. +bool llvm::checkCJStackMapThreshold(CallBase &Call, Function &F) { + Function *Caller = Call.getCaller(); + Function *Callee = &F; + int CallSiteNum1 = 0; + int CallSiteNum2 = 0; + for (auto &I : instructions(Caller)) { + CallBase *CB = dyn_cast(&I); + if (CB && !isa(&I)) { + Function *F = CB->getCalledFunction(); + // Functions has cj-runtime, gc-leaf-function and gc-safepoint does not + // need relocate + if (!F || F->hasFnAttribute("cj-runtime") || + F->hasFnAttribute("gc-leaf-function") || + F->hasFnAttribute("gc-safepoint")) + continue; + ++CallSiteNum1; + } + } + + for (auto &I : instructions(Callee)) { + CallBase *CB = dyn_cast(&I); + if (CB && !isa(&I)) { + Function *F = CB->getCalledFunction(); + // Functions has cj-runtime, gc-leaf-function and gc-safepoint does not + // need relocate + if (!F || F->hasFnAttribute("cj-runtime") || + F->hasFnAttribute("gc-leaf-function") || + F->hasFnAttribute("gc-safepoint")) + continue; + ++CallSiteNum2; + } + } + if (CallSiteNum1 == 0 || CallSiteNum2 <= CJInlineStackMapCalleecallnumThreshold) + return true; + // 100: percentage + return CallSiteNum2 * 1.0 / CallSiteNum1 <= + CJInlinePerStackMapThreshold * 1.0 / 100; +} + InlineCost llvm::getInlineCost( CallBase &Call, const InlineParams &Params, TargetTransformInfo &CalleeTTI, function_ref GetAssumptionCache, diff --git a/llvm/lib/Analysis/LazyCallGraph.cpp b/llvm/lib/Analysis/LazyCallGraph.cpp index 20a905e04..6f4257364 100644 --- a/llvm/lib/Analysis/LazyCallGraph.cpp +++ b/llvm/lib/Analysis/LazyCallGraph.cpp @@ -23,6 +23,7 @@ #include "llvm/IR/Module.h" #include "llvm/IR/PassManager.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/Support/GraphWriter.h" @@ -40,6 +41,10 @@ using namespace llvm; +namespace llvm { + extern cl::opt CJPipeline; +} + #define DEBUG_TYPE "lcg" void LazyCallGraph::EdgeSequence::insertEdgeInternal(Node &TargetN, @@ -1956,6 +1961,12 @@ void LazyCallGraph::visitReferences(SmallVectorImpl &Worklist, while (!Worklist.empty()) { Constant *C = Worklist.pop_back_val(); + if (CJPipeline) { + if (auto *GV = dyn_cast(C); + GV && GV->hasAttribute("CFileKlass")) + continue; + } + if (Function *F = dyn_cast(C)) { if (!F->isDeclaration()) Callback(*F); diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp index 938d950e6..a3b0ae5e7 100644 --- a/llvm/lib/Analysis/Loads.cpp +++ b/llvm/lib/Analysis/Loads.cpp @@ -20,12 +20,20 @@ #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Dominators.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" #include "llvm/IR/Operator.h" +#include "llvm/IR/SafepointIRVerifier.h" +#include "llvm/Support/Casting.h" using namespace llvm; +namespace llvm { +extern cl::opt CJPipeline; +} // namespace llvm + static bool isAligned(const Value *Base, const APInt &Offset, Align Alignment, const DataLayout &DL) { Align BA = Base->getPointerAlignment(DL); @@ -34,6 +42,151 @@ static bool isAligned(const Value *Base, const APInt &Offset, Align Alignment, return BA >= Alignment && !(Offset & (APAlign - 1)); } +static void getUses(Value *Base, SetVector &Uses, + Value *StopInst = nullptr) { + SmallVector, 8> WorkList = {{Base, false}}; + auto IsLoad = [](Value *V) { + if (isa(V)) + return true; + auto *II = dyn_cast(V); + if (!II) + return false; + auto IID = II->getIntrinsicID(); + return IID == Intrinsic::cj_gcread_ref || + IID == Intrinsic::cj_gcread_static_ref; + }; + while (!WorkList.empty()) { + auto [V, L] = WorkList.pop_back_val(); + for (auto *U : V->users()) { + bool Tag = IsLoad(U); + // load after load, then stop. + if ((L && Tag) || Uses.contains(U) || U == StopInst) + continue; + Uses.insert(U); + WorkList.push_back({U, Tag | L}); + } + } +} + +static bool checkMaybeLoadFromNullST(Value *V, Value *&BP) { + // OptionTy = {i1, ptr}, i1 indicates whether ptr is null. + // bb0: + // %0 -> {i1, ptr} + // %1 = load (gep %0, 0, 0) + // %2 = icmp %1, true + // br %2, label unreachable_bb, label bb1 + // bb1: + // %3 = load (gep %0, 0, 1) + // load %3 + // V represents %3. + + // Obtains the type V comes from. If tbaa is carried, the type is obtained + // from tbaa. Otherwise, the type is obtained from cast instruction. + MDNode *TBAAMD = nullptr; + if (auto *I = dyn_cast(V); + I && I->hasMetadata(LLVMContext::MD_tbaa)) + TBAAMD = dyn_cast_or_null( + I->getMetadata(LLVMContext::MD_tbaa)->getOperand(0)); + if (TBAAMD) { + if (auto *MDStr = dyn_cast_or_null(TBAAMD->getOperand(0))) { + auto Str = MDStr->getString(); + if (Str.contains("Option") || Str.contains("Enum")) + return true; + } + } + Value *Ptr = nullptr; + if (auto *LI = dyn_cast(V)) + Ptr = LI->getPointerOperand(); + else if (auto *II = dyn_cast(V)) { + if (II->getIntrinsicID() == Intrinsic::cj_gcread_ref) + Ptr = II->getOperand(1); + else if (II->getIntrinsicID() == Intrinsic::cj_gcread_static_ref) + Ptr = II->getOperand(0); + } + if (Ptr) { + BP = getUnderlyingObject(Ptr); + SmallSet STs; + getAllStructTypes(Ptr, STs); + for (auto *ST : STs) + if (ST && (!ST->hasName() || (ST->getName().contains("Option") || + ST->getName().contains("Enum")))) + return true; + } + return false; +} + +static bool isNoNullArgumentOrLoad(const Value *V, const DominatorTree *DT) { + Value *BP = const_cast(V); + if (checkMaybeLoadFromNullST(const_cast(V), BP)) + return false; + SetVector Uses; + getUses(const_cast(BP), Uses); + // For i8*, this is correct, based on the premise that if we use a pointer + // that might be null, we need to check if it is, like Option<...>. + // The judgment here is conservative. If there is an icmp to null, we assume + // that the pointer cannot be dereferenced anywhere at will. + LLVMContext &Ctx = V->getContext(); + for (auto *U : Uses) { + if (auto *ICI = dyn_cast(U)) { + bool IsOP0Null = isa(ICI->getOperand(0)); + bool IsOP1Null = isa(ICI->getOperand(1)); + if (IsOP0Null || IsOP1Null) + return false; + Type *Ty = ICI->getOperand(0)->getType(); + bool IsI1OrI8 = Ty == Type::getInt1Ty(Ctx) || Ty == Type::getInt8Ty(Ctx); + if (auto *CI = dyn_cast(ICI->getOperand(0)); + CI && CI->getSExtValue() == 0 && IsI1OrI8) + return false; + if (auto *CI = dyn_cast(ICI->getOperand(1)); + CI && CI->getSExtValue() == 0 && IsI1OrI8) + return false; + } + if (auto *SI = dyn_cast(U); + SI && getUnderlyingObject(SI->getPointerOperand()) != BP && + !DT->dominates(V, SI)) + return false; + if (auto *MI = dyn_cast(U); + MI && getUnderlyingObject(MI->getDest()) != BP && !DT->dominates(V, MI)) + return false; + } + return true; +} + +static bool isNoNullPointer(const Value *V, const DominatorTree *DT) { + assert(isa(V->getType()) && "It should be pointer type"); + if (auto *I = dyn_cast(V); + I && I->hasMetadata(LLVMContext::MD_untrusted_ref)) + return false; + if (auto *II = dyn_cast(V)) { + if (II->isCJRefGCRead()) + return isNoNullArgumentOrLoad(V, DT); + } else if (auto *CI = dyn_cast(V)) { + auto *F = CI->getCalledFunction(); + return F && F->hasFnAttribute("cj-heapmalloc"); + } else if (isa(V)) { + return true; + } else if (isa(V) || isa(V)) { + return isNoNullArgumentOrLoad(V, DT); + } + return false; +} + +// Determine whether the pointer V can be dereferenced at CtxI, that is, +// determine whether the pointer V is null and is valid to derefernece. +static bool isCJDeferenceablePointer(const Value *V, const Instruction *CtxI, + const DominatorTree *DT) { + if (!DT || !CtxI) + return false; + if (isa(V) && V->getType()->isPointerTy()) { + auto *Base = getUnderlyingObject(V); + // If Base is a normal instruction, check whether the type of Base is in + // stack or Base is cangjie malloc. In the case of Base dominates CtxI, V + // can safely dereference at CtxI. + return DT->dominates(Base, CtxI) && isNoNullPointer(Base, DT); + } + return false; +} + /// Test if V is always a pointer to allocated and suitably aligned memory for /// a simple load or store. static bool isDereferenceableAndAlignedPointer( @@ -43,6 +196,9 @@ static bool isDereferenceableAndAlignedPointer( unsigned MaxDepth) { assert(V->getType()->isPointerTy() && "Base must be pointer"); + if (CJPipeline && isCJDeferenceablePointer(V, CtxI, DT)) + return true; + // Recursion limit. if (MaxDepth-- == 0) return false; diff --git a/llvm/lib/Analysis/LoopAccessAnalysis.cpp b/llvm/lib/Analysis/LoopAccessAnalysis.cpp index 8311b480a..8f43dc4fd 100644 --- a/llvm/lib/Analysis/LoopAccessAnalysis.cpp +++ b/llvm/lib/Analysis/LoopAccessAnalysis.cpp @@ -45,6 +45,7 @@ #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/IR/Operator.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/PatternMatch.h" @@ -134,6 +135,34 @@ static cl::opt MaxForkedSCEVDepth( "max-forked-scev-depth", cl::Hidden, cl::desc("Maximum recursion depth when finding forked SCEVs (default = 5)"), cl::init(5)); +cl::opt EnableCJGCWriteLoopVectorization( + "enable-cj-gc-write-loop-vectorization", cl::init(true), + cl::desc("enable cj gc write loop vectorization")); +namespace llvm { +bool isVectorizableCJGCWrite(Instruction *I) { + if (!EnableCJGCWriteLoopVectorization) { + return false; + } + auto *CI = dyn_cast(I); + if (CI == nullptr) { + return false; + } + if (CI->getIntrinsicID() != Intrinsic::cj_gcwrite_ref) { + return false; + } + Type *AT = CI->getArgOperand(0)->getType(); + if (AT->getTypeID() == Type::HalfTyID || AT->getTypeID() == Type::FloatTyID || + AT->getTypeID() == Type::DoubleTyID) { + return true; + } + // only support vectorized gcwrite.integer now + if (AT->getTypeID() != Type::IntegerTyID) { + return false; + } + // i1 vectorized is not supported. + return AT->getIntegerBitWidth() != 1; +} +} // namespace llvm bool VectorizerParams::isInterleaveForced() { return ::VectorizationInterleave.getNumOccurrences() > 0; @@ -1548,22 +1577,13 @@ bool llvm::isConsecutiveAccess(Value *A, Value *B, const DataLayout &DL, return Diff && *Diff == 1; } -void MemoryDepChecker::addAccess(StoreInst *SI) { - visitPointers(SI->getPointerOperand(), *InnermostLoop, - [this, SI](Value *Ptr) { - Accesses[MemAccessInfo(Ptr, true)].push_back(AccessIdx); - InstMap.push_back(SI); - ++AccessIdx; - }); -} - -void MemoryDepChecker::addAccess(LoadInst *LI) { - visitPointers(LI->getPointerOperand(), *InnermostLoop, - [this, LI](Value *Ptr) { - Accesses[MemAccessInfo(Ptr, false)].push_back(AccessIdx); - InstMap.push_back(LI); - ++AccessIdx; - }); +void MemoryDepChecker::addAccess(Instruction *I) { + visitPointers( + getLoadStorePointerOperand(I), *InnermostLoop, [this, I](Value *Ptr) { + Accesses[MemAccessInfo(Ptr, !isa(I))].push_back(AccessIdx); + InstMap.push_back(I); + ++AccessIdx; + }); } MemoryDepChecker::VectorizationSafetyStatus @@ -2108,7 +2128,7 @@ void LoopAccessInfo::analyzeLoop(AAResults *AA, LoopInfo *LI, DominatorTree *DT) { // Holds the Load and Store instructions. SmallVector Loads; - SmallVector Stores; + SmallVector Stores; // Holds all the different accesses in the loop. unsigned NumReads = 0; @@ -2126,7 +2146,8 @@ void LoopAccessInfo::analyzeLoop(AAResults *AA, LoopInfo *LI, const bool EnableMemAccessVersioningOfLoop = EnableMemAccessVersioning && - !TheLoop->getHeader()->getParent()->hasOptSize(); + !TheLoop->getHeader()->getParent()->hasOptSize() && + !TheLoop->HasGCWriteInLoop; // For each block. for (BasicBlock *BB : TheLoop->blocks()) { @@ -2149,10 +2170,13 @@ void LoopAccessInfo::analyzeLoop(AAResults *AA, LoopInfo *LI, if (HasComplexMemInst) continue; + bool IsCJGCWrite = + TheLoop->IsInVectorizedProcess && isVectorizableCJGCWrite(&I); + // If this is a load, save it. If this instruction can read from memory // but is not a load, then we quit. Notice that we don't handle function // calls that read or write. - if (I.mayReadFromMemory()) { + if (!IsCJGCWrite && I.mayReadFromMemory()) { // Many math library functions read the rounding mode. We will only // vectorize a loop if it contains known function calls that don't set // the flag. Therefore, it is safe to ignore this read from memory. @@ -2189,7 +2213,7 @@ void LoopAccessInfo::analyzeLoop(AAResults *AA, LoopInfo *LI, } // Save 'store' instructions. Abort if other instructions write to memory. - if (I.mayWriteToMemory()) { + if (!IsCJGCWrite && I.mayWriteToMemory()) { auto *St = dyn_cast(&I); if (!St) { recordAnalysis("CantVectorizeInstruction", St) @@ -2210,6 +2234,12 @@ void LoopAccessInfo::analyzeLoop(AAResults *AA, LoopInfo *LI, if (EnableMemAccessVersioningOfLoop) collectStridedAccess(St); } + + if (IsCJGCWrite) { + NumStores++; + Stores.push_back(&I); + DepChecker->addAccess(&I); + } } // Next instr. } // Next block. @@ -2243,27 +2273,29 @@ void LoopAccessInfo::analyzeLoop(AAResults *AA, LoopInfo *LI, // to the same address. SmallPtrSet UniformStores; - for (StoreInst *ST : Stores) { - Value *Ptr = ST->getPointerOperand(); + for (Instruction *CurInst : Stores) { + Value *Ptr = getLoadStorePointerOperand(CurInst); if (isUniform(Ptr)) { // Record store instructions to loop invariant addresses - StoresToInvariantAddresses.push_back(ST); + StoresToInvariantAddresses.push_back(CurInst); HasDependenceInvolvingLoopInvariantAddress |= !UniformStores.insert(Ptr).second; } - // If we did *not* see this pointer before, insert it to the read-write // list. At this phase it is only a 'write' list. - Type *AccessTy = getLoadStoreType(ST); + Type *AccessTy = getLoadStoreType(CurInst); if (Seen.insert({Ptr, AccessTy}).second) { ++NumReadWrites; - MemoryLocation Loc = MemoryLocation::get(ST); + MemoryLocation Loc = + isa(CurInst) + ? MemoryLocation::get(dyn_cast(CurInst)) + : MemoryLocation::get(dyn_cast(CurInst)); // The TBAA metadata could have a control dependency on the predication // condition, so we cannot rely on it when determining whether or not we // need runtime pointer checks. - if (blockNeedsPredication(ST->getParent(), TheLoop, DT)) + if (blockNeedsPredication(CurInst->getParent(), TheLoop, DT)) Loc.AATags.TBAA = nullptr; visitPointers(const_cast(Loc.Ptr), *TheLoop, @@ -2502,6 +2534,7 @@ bool LoopAccessInfo::isUniform(Value *V) const { void LoopAccessInfo::collectStridedAccess(Value *MemAccess) { Value *Ptr = getLoadStorePointerOperand(MemAccess); + if (!Ptr) return; diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp index 413ec6dd4..67652db5d 100644 --- a/llvm/lib/Analysis/MemoryBuiltins.cpp +++ b/llvm/lib/Analysis/MemoryBuiltins.cpp @@ -49,6 +49,10 @@ using namespace llvm; +namespace llvm { +extern cl::opt CJPipeline; +} // namespace llvm + #define DEBUG_TYPE "memory-builtins" enum AllocType : uint8_t { @@ -360,6 +364,9 @@ bool llvm::isRemovableAlloc(const CallBase *CB, const TargetLibraryInfo *TLI) { // Historically we've treated the C family allocation routines and operator // new as removable + if (CJPipeline && CB->getCalledFunction() && + CB->getCalledFunction()->hasFnAttribute("cj-heapmalloc")) + return true; return isAllocLikeFn(CB, TLI); } diff --git a/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp b/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp index fce9d5b24..84b5ee5f4 100644 --- a/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp +++ b/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp @@ -34,6 +34,7 @@ #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" @@ -81,6 +82,10 @@ static cl::opt cl::desc("The number of blocks to scan during memory " "dependency analysis (default = 1000)")); +namespace llvm { +extern cl::opt GVNDisableBarrier; +} // namespace llvm + // Limit on the number of memdep results to process. static const unsigned int NumResultsLimit = 100; @@ -465,6 +470,29 @@ MemDepResult MemoryDependenceResults::getSimplePointerDependencyFrom( continue; return MemDepResult::getClobber(II); } + case Intrinsic::cj_gcwrite_ref: { + if (!GVNDisableBarrier) { + MemoryLocation Loc = MemoryLocation::get(II); + + // Avoid optimizing finalizer obj + if (const CallBase *CB = + dyn_cast(getUnderlyingObject(Loc.Ptr))) { + Function *F = CB->getCalledFunction(); + if (F && (F->getName() == "CJ_MCC_NewFinalizerStub" || + F->getName() == "CJ_MCC_OnFinalizerCreated")) + continue; + } + + AliasResult R = BatchAA.alias(Loc, MemLoc); + if (R == AliasResult::NoAlias) + continue; + if (R == AliasResult::MustAlias) + return MemDepResult::getDef(II); + if (isInvariantLoad) + continue; + return MemDepResult::getClobber(II); + } + } } } diff --git a/llvm/lib/Analysis/MemoryLocation.cpp b/llvm/lib/Analysis/MemoryLocation.cpp index 2ed32227b..a56c3dabd 100644 --- a/llvm/lib/Analysis/MemoryLocation.cpp +++ b/llvm/lib/Analysis/MemoryLocation.cpp @@ -16,6 +16,10 @@ #include "llvm/IR/Type.h" using namespace llvm; +namespace llvm { +extern cl::opt CJPipeline; +} // namespace llvm + void LocationSize::print(raw_ostream &OS) const { OS << "LocationSize::"; if (*this == beforeOrAfterPointer()) @@ -43,13 +47,20 @@ MemoryLocation MemoryLocation::get(const LoadInst *LI) { MemoryLocation MemoryLocation::get(const StoreInst *SI) { const auto &DL = SI->getModule()->getDataLayout(); - return MemoryLocation(SI->getPointerOperand(), LocationSize::precise(DL.getTypeStoreSize( SI->getValueOperand()->getType())), SI->getAAMetadata()); } +MemoryLocation MemoryLocation::get(const CallBase *CI) { + const auto &DL = CI->getModule()->getDataLayout(); + return MemoryLocation(getLoadStorePointerOperand(CI), + LocationSize::precise(DL.getTypeStoreSize( + getLoadStorePointerOperand(CI)->getType())), + CI->getAAMetadata()); +} + MemoryLocation MemoryLocation::get(const VAArgInst *VI) { return MemoryLocation(VI->getPointerOperand(), LocationSize::afterPointer(), VI->getAAMetadata()); @@ -85,6 +96,16 @@ Optional MemoryLocation::getOrNone(const Instruction *Inst) { return get(cast(Inst)); case Instruction::AtomicRMW: return get(cast(Inst)); + case Instruction::Call: { + auto *CI = cast(Inst); + auto IID = CI->getIntrinsicID(); + if (IID == Intrinsic::cj_gcwrite_ref || + IID == Intrinsic::cj_gcwrite_static_ref || + IID == Intrinsic::cj_gcread_ref || + IID == Intrinsic::cj_gcread_static_ref) + return get(CI); + } + LLVM_FALLTHROUGH; default: return None; } @@ -103,6 +124,23 @@ MemoryLocation MemoryLocation::getForSource(const AnyMemTransferInst *MTI) { return getForArgument(MTI, 1, nullptr); } +Optional +MemoryLocation::getCJMemTransferSource(const IntrinsicInst *II) { + if (CJPipeline) { + switch (II->getIntrinsicID()) { + case Intrinsic::cj_gcwrite_static_struct: + case Intrinsic::cj_gcread_static_struct: + return getForArgument(II, 1, nullptr); + case Intrinsic::cj_gcwrite_struct: + case Intrinsic::cj_gcread_struct: + return getForArgument(II, 2, nullptr); + default: + break; + } + } + return None; +} + MemoryLocation MemoryLocation::getForDest(const MemIntrinsic *MI) { return getForDest(cast(MI)); } @@ -125,6 +163,20 @@ MemoryLocation::getForDest(const CallBase *CB, const TargetLibraryInfo &TLI) { // TODO: remove implementation restriction return None; + if (CJPipeline) { + switch (CB->getIntrinsicID()) { + case Intrinsic::cj_memset: + case Intrinsic::cj_gcwrite_static_struct: + case Intrinsic::cj_gcread_struct: + case Intrinsic::cj_gcread_static_struct: + return getForArgument(CB, 0, &TLI); + case Intrinsic::cj_gcwrite_struct: + return getForArgument(CB, 1, &TLI); + default: + break; + } + } + Value *UsedV = nullptr; Optional UsedIdx; for (unsigned i = 0; i < CB->arg_size(); i++) { @@ -169,6 +221,9 @@ MemoryLocation MemoryLocation::getForArgument(const CallBase *Call, default: break; case Intrinsic::memset: + case Intrinsic::cj_memset: + case Intrinsic::cj_gcwrite_static_struct: + case Intrinsic::cj_gcread_static_struct: case Intrinsic::memcpy: case Intrinsic::memcpy_inline: case Intrinsic::memmove: @@ -182,6 +237,15 @@ MemoryLocation MemoryLocation::getForArgument(const CallBase *Call, AATags); return MemoryLocation::getAfter(Arg, AATags); + case Intrinsic::cj_gcwrite_struct: + case Intrinsic::cj_gcread_struct: + assert((ArgIdx == 0 || ArgIdx == 1 || ArgIdx == 2) && + "Invalid argument index for cj.gcwrite.struct"); + if (ConstantInt *LenCI = dyn_cast(II->getArgOperand(3))) + return MemoryLocation(Arg, LocationSize::precise(LenCI->getZExtValue()), + AATags); + return MemoryLocation::getAfter(Arg, AATags); + case Intrinsic::lifetime_start: case Intrinsic::lifetime_end: case Intrinsic::invariant_start: diff --git a/llvm/lib/Analysis/MemorySSA.cpp b/llvm/lib/Analysis/MemorySSA.cpp index 76371b888..380893be2 100644 --- a/llvm/lib/Analysis/MemorySSA.cpp +++ b/llvm/lib/Analysis/MemorySSA.cpp @@ -84,6 +84,10 @@ static cl::opt MaxCheckLimit( cl::desc("The maximum number of stores/phis MemorySSA" "will consider trying to walk past (default = 100)")); +namespace llvm { +extern cl::opt CJPipeline; +} // namespace llvm + // Always verify MemorySSA if expensive checking is enabled. #ifdef EXPENSIVE_CHECKS bool llvm::VerifyMemorySSA = true; @@ -382,8 +386,17 @@ struct UpwardsMemoryQuery { UpwardsMemoryQuery(const Instruction *Inst, const MemoryAccess *Access) : IsCall(isa(Inst)), Inst(Inst), OriginalAccess(Access) { - if (!IsCall) + if (!IsCall) { StartingLoc = MemoryLocation::get(Inst); + } else { + const CallBase *CB = dyn_cast(Inst); + Intrinsic::ID ID = CB->getIntrinsicID(); + if (ID == Intrinsic::cj_gcwrite_ref || + ID == Intrinsic::cj_gcwrite_static_ref || + ID == Intrinsic::cj_gcread_static_ref || + ID == Intrinsic::cj_gcread_ref) + StartingLoc = MemoryLocation::get(CB); + } } }; @@ -392,6 +405,8 @@ struct UpwardsMemoryQuery { template static bool isUseTriviallyOptimizableToLiveOnEntry(AliasAnalysisType &AA, const Instruction *I) { + if (CJPipeline) + return false; // If the memory can't be changed, then loads of the memory can't be // clobbered. if (auto *LI = dyn_cast(I)) diff --git a/llvm/lib/Analysis/ObfConfig.cpp b/llvm/lib/Analysis/ObfConfig.cpp new file mode 100644 index 000000000..67f368133 --- /dev/null +++ b/llvm/lib/Analysis/ObfConfig.cpp @@ -0,0 +1,709 @@ +//===--------------------------- ObfConfig.cpp ----------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// The configuration pass for obfuscation. +// +//===----------------------------------------------------------------------===// + +#include "CangjieDemangle.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/TypeFinder.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/PassRegistry.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/SHA1.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Analysis/ObfConfigAnalysis.h" +#include +#include +#include +#include + +using namespace llvm; + +namespace { +const int OBF_LEN = 8; +} // end anonymous namespace + +static cl::opt + ConfigFile("obf-config-file", + cl::desc("Obfuscator configuration file path"), cl::NotHidden); + +static cl::opt ConfigSeed("obf-seed", + cl::desc("Obfuscator hash salt seed"), + cl::NotHidden); + +namespace { +const int MAX_OBF_LEVEL = 9; +const int DEFAULT_OBF_LEVEL = 4; +} // end anonymous namespace + +static cl::opt + ObfLevel("obf-level", cl::desc("the Level of obfuscation"), cl::NotHidden); + +static unsigned int WildcardNoneType = 0; +static unsigned int WildcardMatchOneType = 1; // ? +static unsigned int WildcardMatchMultiSepType = 1 << 1; // * +static unsigned int WildcardMatchMultiType = 1 << 2; // ** +static unsigned int WildcardMatchAnyParamType = 1 << 3; // *** +static unsigned int WildcardMatchMultiParamType = 1 << 4; // ... + +static inline std::string WildcardMatchOne = "?"; +static inline std::string WildcardMatchMultiNoSep = "*"; +static inline std::string WildcardMatchMulti = "**"; +static inline std::string WildcardMatchAnyParam = "***"; +static inline std::string WildcardMatchMultiParam = "..."; + +unsigned int Matcher::getWildcardType(const std::string &Name, size_t &Index) { + unsigned int Res = WildcardNoneType; + size_t Pos = Index; + if (Name[Index] == WildcardMatchOne[0]) { + return WildcardMatchOneType; + } + if (Index + WildcardMatchMultiParam.size() < Name.size() && + Name.compare(Index, WildcardMatchMultiParam.size(), WildcardMatchMultiParam) == 0) { + Index += (WildcardMatchMultiParam.size() - 1); + return WildcardMatchMultiParamType; + } + while (Pos < Name.size() && Name[Pos] == WildcardMatchMultiNoSep[0]) { + Pos++; + } + if (Pos - Index >= WildcardMatchMultiNoSep.size()) { + Res = Res | WildcardMatchMultiSepType; + } + if (Pos - Index >= WildcardMatchMulti.size()) { + Res = Res | WildcardMatchMultiType; + } + if (Pos - Index >= WildcardMatchAnyParam.size()) { + Res = Res | WildcardMatchAnyParamType; + } + Index = (Pos != Index ? (Pos - 1) : Index); + + return Res; +} + +unsigned int Matcher::getCJFieldWildcardType(const CJSymbol::CJField &Field, size_t &Index) { + unsigned int Res = getWildcardType(Field.Name, Index); + if ((Res & WildcardMatchMultiType) && + (Field.Name.size() != WildcardMatchMulti.size() || Field.HasParameter)) { + Res = Res & (~WildcardMatchMultiType); + } else if ((Res & WildcardMatchMultiType) && Field.Name.size() == WildcardMatchMulti.size()) { + Res = Res & (~WildcardMatchMultiSepType); + } + return Res; +} + +bool Matcher::isParamMatch(const CJSymbol::CJField &TSym, const CJSymbol::CJField &SSym, bool MatchGeneric) { + size_t TIndex = 0; + size_t SIndex = 0; + auto Temp = TSym.Parameters; + auto Sym = SSym.Parameters; + size_t TEnd = Temp.size(); + size_t SEnd = Sym.size(); + size_t LastTIndex = std::string::npos; + size_t LastSIndex = std::string::npos; + if (TSym.HasParameter != SSym.HasParameter) { + return false; + } + + while (TIndex != TEnd || SIndex != SEnd) { + if (TIndex != TEnd && SIndex != SEnd && !Temp[TIndex].isWildCardMatchMultiParam() && + (Temp[TIndex].isWildCardMatchAnyParam() || isSymbolMatch(Temp[TIndex], Sym[SIndex], MatchGeneric))) { + TIndex++; + SIndex++; + continue; + } else if (TIndex != TEnd && Temp[TIndex].isWildCardMatchMultiParam()) { + while (TIndex < TEnd && Temp[TIndex].isWildCardMatchMultiParam()) { + TIndex++; + } + if (TIndex == TEnd) { + return true; + } + while (SIndex < SEnd && + !isSymbolMatch(Temp[TIndex], Sym[SIndex], MatchGeneric)) { + SIndex++; + } + LastTIndex = TIndex - 1; + LastSIndex = SIndex + 1; + continue; + } + + if (SIndex == SEnd || LastSIndex == std::string::npos) { + return false; + } + + TIndex = LastTIndex; + SIndex = LastSIndex; + } + + return true; +} + +bool Matcher::isCJFieldsMatch( + const std::vector &Temp, const std::vector &Sym, bool MatchGeneric) { + size_t TIndex = 0; + size_t SIndex = 0; + size_t TOffset = 0; + size_t SOffset = 0; + std::vector MPVec; + unsigned int WType = WildcardNoneType; + bool lastParamMatch = true; + + while (TIndex != Temp.size() || SIndex != Sym.size()) { + if (TIndex < Temp.size() && SIndex < Sym.size() && + TOffset < Temp[TIndex].Name.size() && SOffset < Sym[SIndex].Name.size()) { + WType = getCJFieldWildcardType(Temp[TIndex], TOffset); + // Check if char is same or recored wildcard + if ((WType & WildcardMatchOneType) || + (WType == WildcardNoneType && Temp[TIndex].Name[TOffset] == Sym[SIndex].Name[SOffset])) { + // Match 1 char + TOffset++; + SOffset++; + continue; + } else if ((WType & WildcardMatchMultiSepType) || (WType & WildcardMatchMultiType)) { + // Record wildcard in `Temp` + MultiMatchPos MPos = {TIndex, SIndex, TOffset, SOffset, WType}; + MPVec.push_back(MPos); + if (WType & WildcardMatchMultiType) { + TIndex++; + TOffset = 0; + } else { + TOffset++; + } + if (TOffset != Temp[TIndex].Name.size()) { + continue; + } + } + } + if (TIndex < Temp.size() && SIndex < Sym.size() && + TOffset == Temp[TIndex].Name.size() && SOffset == Sym[SIndex].Name.size()) { + if (isParamMatch(Temp[TIndex], Sym[SIndex], MatchGeneric)) { + TIndex++; + SIndex++; + TOffset = 0; + SOffset = 0; + lastParamMatch = true; + continue; + } else { + lastParamMatch = false; + } + } + // Not match, rollback + if (MPVec.size() != 0) { + // Rollback to the last USABLE wildcard + while (MPVec.size() != 0 && + (((MPVec.back().WType & WildcardMatchMultiSepType) && + MPVec.back().LastSOffset == Sym[MPVec.back().LastSIndex].Name.size()) || + ((MPVec.back().WType & WildcardMatchMultiType) && MPVec.back().LastSIndex == Sym.size()))) { + MPVec.pop_back(); + } + if (MPVec.size() == 0) { + return false; + } + if (MPVec.size() != 0 && (MPVec.back().WType & WildcardMatchMultiSepType)) { + SIndex = MPVec.back().LastSIndex; + SOffset = ++MPVec.back().LastSOffset; + TIndex = MPVec.back().LastTIndex; + TOffset = MPVec.back().LastTOffset + 1; + } else if (MPVec.size() != 0 && (MPVec.back().WType & WildcardMatchMultiType)) { + SIndex = ++MPVec.back().LastSIndex; + SOffset = 0; + TIndex = MPVec.back().LastTIndex + 1; + TOffset = 0; + lastParamMatch = true; + } + } else { + return false; + } + + if (SIndex == Sym.size()) { + size_t Offset = 0; + while (TIndex < Temp.size() && + (getCJFieldWildcardType(Temp[TIndex], Offset) & WildcardMatchMultiType)) { + TIndex++; + Offset = 0; + } + } + } + + if (TIndex < Temp.size() && Temp[TIndex].Name.size() == TOffset) { + TIndex++; + TOffset = 0; + } + if (TIndex < Temp.size() || SIndex < Sym.size() || !lastParamMatch) { + return false; + } + return true; +} + +bool Matcher::isSymbolMatch(const CJSymbol &Temp, const CJSymbol &Sym, bool MatchGeneric) { + if (Temp.SymbolType != Sym.SymbolType) { + return false; + } + + if (Temp.SymbolType == CJSymbol::CJSymbolType::TYPE && Temp.isWildCardMatchAnyParam()) { + return true; + } + + if (!isCJFieldsMatch(Temp.Fields, Sym.Fields, MatchGeneric)) { + return false; + } + if (Temp.SymbolType == CJSymbol::CJSymbolType::CLOSURE && + !isSymbolMatch(*Temp.ReturnType.get(), *Sym.ReturnType.get(), MatchGeneric)) { + return false; + } + + return true; +} + +bool Matcher::isObfFuncMatch(const std::string &Temp, const std::string &Sym) { + size_t TEnd = Temp.size(); + size_t SEnd = Sym.size(); + size_t TIndex = 0; + size_t SIndex = 0; + size_t LastTIndex = std::string::npos; + size_t LastSIndex = std::string::npos; + unsigned int WType = WildcardNoneType; + + while (SIndex != SEnd || TIndex != TEnd) { + WType = getWildcardType(Temp, TIndex); + if (SIndex < SEnd && TIndex < TEnd && (WType == WildcardMatchOneType || Temp[TIndex] == Sym[SIndex])) { + TIndex++; + SIndex++; + continue; + } else if (TIndex < TEnd && (WType & WildcardMatchMultiSepType)) { + while (TIndex + 1 < TEnd && SIndex < SEnd && Temp[TIndex + 1] != Sym[SIndex]) { + SIndex++; + } + LastTIndex = TIndex; + LastSIndex = SIndex + 1; + TIndex++; + continue; + } + if (SIndex == SEnd || LastTIndex == std::string::npos) { + return false; + } + TIndex = LastTIndex; + SIndex = LastSIndex; + } + + return true; +} + +bool CJSymbol::isWildCardMatchMultiParam(void) const { + return SymbolType == CJSymbolType::TYPE && Fields.size() == 1 && Fields[0].Name == WildcardMatchMultiParam; +} + +bool CJSymbol::isWildCardMatchAnyParam(void) const { + return SymbolType == CJSymbolType::TYPE && Fields.size() == 1 && Fields[0].Name == WildcardMatchAnyParam; +} + +bool CJSymbol::hasSeparators(const std::string &String) { + return String.find(FieldSeparator, 0) != std::string::npos || + String.find(ParamSeparator, 0) != std::string::npos; +} + +std::string CJSymbol::parseOperatorName(const std::string &String, size_t Start, size_t End) { + for (auto OP : OperatorNames) { + if (End - Start >= OP.size() && String.compare(Start, OP.size(), OP) == 0) { + return OP; + } + } + return ""; +} + +bool CJSymbol::parseField(const std::string &String, size_t Start, size_t End, CJField &Field) { + int GenericNotClose = 0; + int ParamNotClose = 0; + size_t GenericStart = 0; + size_t GenericEnd = std::string::npos; + size_t ParamStart = String.find(ParamBrackets[0], Start); + size_t FieldEnd = 0; + + // parse field name. + auto OPName = parseOperatorName(String, Start, End); + if (OPName.size() != 0) { + FieldEnd = Start + OPName.size(); + Field.Name = OPName; + ParamStart = FieldEnd; + } else { + GenericStart = String.find(GenericBrackets[0], Start); + FieldEnd = std::min(std::min(GenericStart, ParamStart), End); + Field.Name = String.substr(Start, FieldEnd - Start); + } + + // parse field generic. + for (size_t Index = FieldEnd; String[FieldEnd] == GenericBrackets[0] && Index < End; Index++) { + if (isLeftGenericBracket(String, Index, FieldEnd, End, false)) { + GenericNotClose++; + } else if (isRightGenericBracket(String, Index, FieldEnd, End) && GenericNotClose > 0) { + GenericNotClose--; + } + if (GenericNotClose == 0 && isRightGenericBracket(String, Index, FieldEnd, End)) { + GenericEnd = Index; + break; + } + } + + if (GenericNotClose != 0) { + return false; + } + if (GenericEnd != std::string::npos) { + Field.HasGeneric = true; + if (!parseParams(String, FieldEnd + 1, GenericEnd, Field.GenericTypes)) { + return false; + } + ParamStart = GenericEnd + 1; + } + + // parse field params. + for (size_t Index = ParamStart; Index < End; Index++) { + if (isLeftParamBracket(String, Index, ParamStart, End)) { + ParamNotClose++; + } else if (isRightParamBracket(String, Index, ParamStart, End)) { + ParamNotClose--; + } + if (ParamNotClose == 0 && Index != (End - 1)) { + if (String.substr(Index, End - Index) == "[]") { + break; + } + return false; + } + } + if (ParamNotClose != 0) { + return false; + } + if (isLeftParamBracket(String, ParamStart, Start, End) && isRightParamBracket(String, End - 1, Start, End)) { + Field.HasParameter = true; + if (!parseParams(String, ParamStart + 1, End - 1, Field.Parameters)) { + return false; + } + } + return true; +} + +bool CJSymbol::parseFields(const std::string &String, size_t Start, size_t End) { + bool Success = true; + size_t Pos = 0; + + while (Start < End) { + int GenericNotClose = 0; + int ParamNotClose = 0; + Pos = std::string::npos; + for (size_t Index = Start; Index < End; Index++) { + if (isLeftGenericBracket(String, Index, Start, End)) { + GenericNotClose++; + } else if (isRightGenericBracket(String, Index, Start, End) && GenericNotClose > 0) { + GenericNotClose--; + } else if (isLeftParamBracket(String, Index, Start, End)) { + ParamNotClose++; + } else if (isRightParamBracket(String, Index, Start, End) && ParamNotClose > 0) { + ParamNotClose--; + } + if (GenericNotClose == 0 && ParamNotClose == 0 && String[Index] == FieldSeparator) { + Pos = Index; + break; + } + } + // Only check param bracket because the field could be `<` or `>`. + if (ParamNotClose != 0) { + return false; + } + Pos = (Pos == std::string::npos ? End : Pos); + CJField Field; + Success = parseField(String, Start, Pos, Field); + if (!Success || (Fields.size() != 0 && Field.Name.size() == 0)) { + // Symbol might starts with '.', so the first field can be empty + return false; + } + Fields.push_back(Field); + Start = Pos + 1; + } + return String[End - 1] != FieldSeparator; +} + +bool CJSymbol::parseSymbol(const std::string &String) { + return parseFields(String, 0, String.size()); +} + +bool CJSymbol::parseClosure(const std::string &String) { + int ParamNotClose = 0; + size_t Start = 0; + size_t Pos = 0; + size_t ParamEnd = 0; + size_t ReturnStart = 0; + for (Pos = 0; Pos < String.size(); Pos++) { + if (isLeftParamBracket(String, Pos, Start, String.size())) { + ParamNotClose++; + } else if (isRightParamBracket(String, Pos, Start, String.size())) { + ParamNotClose--; + } + + if (ParamNotClose == 0 && isReturnBracket(String, Pos, Start, String.size())) { + ParamEnd = Pos - 1; + ReturnStart = Pos + 1; + break; + } + } + if (ParamNotClose != 0 || ParamEnd == 0) { + return false; + } + CJField Field; + if (!parseParams(String, Start + 1, ParamEnd - 1, Field.Parameters)) { + return false; + } + Fields.push_back(Field); + std::string ClStr = String.substr(ReturnStart, String.size() - ReturnStart); + ReturnType = std::make_shared(CJSymbol::get(ClStr, "", CJSymbolType::TYPE)); + return true; +} + + +CJSymbol CJSymbol::get(const std::string &Symbol, const std::string &Package, CJSymbol::CJSymbolType SymbolType) { + CJSymbol CJS = CJSymbol(); + CJS.SymbolType = SymbolType; + CJS.Package = Package; + bool Success = false; + std::string TrimSymbol = ""; + + for (auto C : Symbol) { + if (C == ' ' || C == '\n' || C == '\r') { + continue; + } + TrimSymbol += C; + } + if (TrimSymbol.size() == 0) { + Success = false; + goto end; + } + while (true) { + size_t Pos = TrimSymbol.find(IdentSeparator, 0); + if (Pos == std::string::npos) { + break; + } + TrimSymbol.replace(Pos, IdentSeparator.size(), std::string(1, FieldSeparator)); + } + if (SymbolType == CJSymbol::CJSymbolType::TYPE && + (TrimSymbol == WildcardMatchMultiParam || TrimSymbol == WildcardMatchAnyParam)) { + Success = true; + CJField Field; + Field.Name = TrimSymbol; + CJS.Fields.push_back(Field); + goto end; + } + + if (TrimSymbol[0] == ParamBrackets[0]) { + CJS.SymbolType = CLOSURE; + Success = CJS.parseClosure(TrimSymbol); + } else { + Success = CJS.parseSymbol(TrimSymbol); + } + +end: + if (!Success) { + report_fatal_error("Invalid Symbol: " + StringRef(Symbol)); + } + return CJS; +} + +bool CJSymbol::parseParams(const std::string &String, size_t Start, size_t End, std::vector &Params) { + while (Start < End) { + int GenericNotClose = 0; + int ParamNotClose = 0; + size_t Pos = std::string::npos; + for (size_t Index = Start; Index < End; Index++) { + if (isLeftGenericBracket(String, Index, Start, End, false)) { + GenericNotClose++; + } else if (isRightGenericBracket(String, Index, Start, End) && GenericNotClose > 0) { + GenericNotClose--; + } else if (isLeftParamBracket(String, Index, Start, End)) { + ParamNotClose++; + } else if (isRightParamBracket(String, Index, Start, End) && ParamNotClose > 0) { + ParamNotClose--; + } + if (GenericNotClose == 0 && ParamNotClose == 0 && String[Index] == ParamSeparator) { + Pos = Index; + break; + } + } + if (GenericNotClose != 0 || ParamNotClose != 0) { + return false; + } + Pos = (Pos == std::string::npos ? End : Pos); + if (Pos == Start) { + return false; + } + CJSymbol CJS = CJSymbol::get(String.substr(Start, Pos - Start), "", CJSymbolType::TYPE); + Params.push_back(CJS); + Start = Pos + 1; + } + return String[End - 1] != ParamSeparator; +} + +CJSymbol::CJSymbol() { +} + +CJSymbol::~CJSymbol() { +} + +bool Rule::parseOwner(const std::string &Str) { + Owner = Str; + return true; +} + +Rule::Rule(std::string Owner, CJSymbol CJSym) { + if (!parseOwner(Owner)) { + report_fatal_error("Invalid obfuscator in config file: " + StringRef(Owner)); + } + + Symbol = CJSym; +} + +Rule Rule::get(const std::string &Line) { + size_t Pos = Line.find(' '); + if (Pos == std::string::npos) { + report_fatal_error("Invalid rule in Obfuscator's config file: " + StringRef(Line)); + } + + CJSymbol CJSym = CJSymbol::get(Line.substr(Pos + 1, Line.size() - Pos - 1), "", CJSymbol::CJSymbolType::SYMBOL); + Rule R = Rule(Line.substr(0, Pos), CJSym); + R.RawRule = Line; + return R; +} + +Rule::~Rule() {} + +bool Rule::isMatch(const CJSymbol &CJSym) { + return Matcher::isSymbolMatch(Symbol, CJSym, false); +} + +void ObfConfig::addRule(Rule &R) { RuleMap[R.Owner].push_back(R); } + +ObfConfig::~ObfConfig() { + RuleMap.clear(); +} + +void ObfConfig::loadConfig(const std::string &ConfigPath) { + std::string Line; + std::ifstream InputFile(ConfigPath); + + if (!InputFile.is_open()) { + return; + } + + while (getline(InputFile, Line)) { + std::string NewLine = ""; + for (auto C : Line) { + if (C == '#') { + break; + } + NewLine += C; + } + if (NewLine.size() == 0) { + continue; + } + + Rule R = Rule::get(Line); + addRule(R); + } + + InputFile.close(); +} + +ObfConfig::ObfConfig() { + if (ObfLevel > MAX_OBF_LEVEL) + ObfLevel = MAX_OBF_LEVEL; + else if (ObfLevel == 0) + ObfLevel = DEFAULT_OBF_LEVEL; + + RandSeed = ConfigSeed; + srand(RandSeed); + this->loadConfig(ConfigFile); +} + +unsigned int ObfConfig::getObfLevel() { return ObfLevel; } + +unsigned int ObfConfig::getMaxObfLevel() { return MAX_OBF_LEVEL; } + +bool ObfConfig::needObfuse(const std::string &N, const std::string &Owner) { + if (!isValidMangleName(N)) { + return true; + } + std::string DN = getDemangleName(N); + std::string Pack = getPackageName(N); + CJSymbol Target = CJSymbol::get(DN, Pack, CJSymbol::CJSymbolType::SYMBOL); + for (auto &Iter : RuleMap) { + if (!Matcher::isObfFuncMatch(Iter.first, Owner)) { + continue; + } + for (auto Rule : Iter.second) { + if (Rule.isMatch(Target)) { + return false; + } + } + } + return true; +} + +std::string ObfConfig::getPackageName(const std::string &MN) { + auto D = Cangjie::Demangle(MN); + return D.GetPkgName(); +} + +std::string ObfConfig::getDemangleName(const std::string &MN) { + std::string Suffix = ""; + std::string RealScopeRes = std::string(1, CJSymbol::FieldSeparator); + + auto D = Cangjie::Demangle(MN); + std::string FullName = D.GetFullName(); + + auto DN = (D.GetPkgName() + (D.GetPkgName().empty() ? "" : RealScopeRes)) + FullName; + + return DN; +} + +bool ObfConfig::isValidMangleName(const std::string &Name) { + auto D = Cangjie::Demangle(Name); + return D.IsValid(); +} + +std::string ObfConfig::hash(const StringRef &Input, bool ToAscii) { + SHA1 Hasher; + Hasher.update(Input); + std::string Output; + if (ToAscii) + Output = toHex(Hasher.final()); + else + Output = std::string((char *)Hasher.final().data()); + Output = Output.substr(0, OBF_LEN); + return Output; +} + +unsigned int ObfConfig::getRandNum() { + unsigned int RD = rand(); + return RD; +} + +AnalysisKey ObfConfigAnalysis::Key; + +std::shared_ptr ObfConfigAnalysis::run(Module &M, + ModuleAnalysisManager &AM) { + if (ObfConfigInstance == nullptr) { + this->ObfConfigInstance = std::make_shared(); + } + + return this->ObfConfigInstance; +} diff --git a/llvm/lib/Analysis/PHITransAddr.cpp b/llvm/lib/Analysis/PHITransAddr.cpp index 5b0fbca23..a6a87208a 100644 --- a/llvm/lib/Analysis/PHITransAddr.cpp +++ b/llvm/lib/Analysis/PHITransAddr.cpp @@ -21,6 +21,10 @@ #include "llvm/Support/raw_ostream.h" using namespace llvm; +namespace llvm { +extern cl::opt CJPipeline; +} // namespace llvm + static cl::opt EnableAddPhiTranslation( "gvn-add-phi-translation", cl::init(false), cl::Hidden, cl::desc("Enable phi-translation of add instructions")); @@ -197,15 +201,21 @@ Value *PHITransAddr::PHITranslateSubExpr(Value *V, BasicBlock *CurBB, if (Constant *C = dyn_cast(PHIIn)) return AddAsInput(ConstantExpr::getCast(Cast->getOpcode(), C, Cast->getType())); - - // Otherwise we have to see if a casted version of the incoming pointer - // is available. If so, we can use it, otherwise we have to fail. - for (User *U : PHIIn->users()) { - if (CastInst *CastI = dyn_cast(U)) - if (CastI->getOpcode() == Cast->getOpcode() && - CastI->getType() == Cast->getType() && - (!DT || DT->dominates(CastI->getParent(), PredBB))) - return CastI; + SmallVector CastValues = {PHIIn}; + if (CJPipeline) { + if (auto *CI = dyn_cast(PHIIn)) + CastValues.push_back(CI->getOperand(0)); + } + for (auto *V : CastValues) { + // Otherwise we have to see if a casted version of the incoming pointer + // is available. If so, we can use it, otherwise we have to fail. + for (User *U : V->users()) { + if (CastInst *CastI = dyn_cast(U)) + if (CastI->getOpcode() == Cast->getOpcode() && + CastI->getType() == Cast->getType() && + (!DT || DT->dominates(CastI->getParent(), PredBB))) + return CastI; + } } return nullptr; } diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index 569ee6b3e..1b72d107c 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -78,6 +78,10 @@ using namespace llvm; using namespace llvm::PatternMatch; +namespace llvm { +extern cl::opt CJPipeline; +} // namespace llvm + // Controls the number of uses of the value searched for possible // dominating comparisons. static cl::opt DomConditionsMaxUses("dom-conditions-max-uses", @@ -4697,6 +4701,20 @@ bool llvm::isSafeToSpeculativelyExecute(const Instruction *Inst, DT, TLI); } +static bool isCJRefReadSafeToSpeculativelyExecute( + const IntrinsicInst *II, const Instruction *CtxI, const DominatorTree *DT, + const TargetLibraryInfo *TLI) { + if (!II) + return false; + const DataLayout &DL = II->getModule()->getDataLayout(); + Value *Ptr = II->getCJRefGCReadPtr(); + if (!Ptr) + return false; + return isDereferenceableAndAlignedPointer( + Ptr, Ptr->getType()->getNonOpaquePointerElementType(), Align(8), DL, CtxI, + DT, TLI); +} + bool llvm::isSafeToSpeculativelyExecuteWithOpcode( unsigned Opcode, const Instruction *Inst, const Instruction *CtxI, const DominatorTree *DT, const TargetLibraryInfo *TLI) { @@ -4757,14 +4775,18 @@ bool llvm::isSafeToSpeculativelyExecuteWithOpcode( if (mustSuppressSpeculation(*LI)) return false; const DataLayout &DL = LI->getModule()->getDataLayout(); - return isDereferenceableAndAlignedPointer( - LI->getPointerOperand(), LI->getType(), LI->getAlign(), DL, CtxI, DT, - TLI); + return isDereferenceableAndAlignedPointer(LI->getPointerOperand(), + LI->getType(), LI->getAlign(), DL, + CtxI, DT, TLI); } case Instruction::Call: { auto *CI = dyn_cast(Inst); if (!CI) return false; + if (CJPipeline && isCJRefReadSafeToSpeculativelyExecute( + dyn_cast(CI), CtxI, DT, TLI)) + return true; + const Function *Callee = CI->getCalledFunction(); // The called function could have undefined behavior or side-effects, even diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index c9a982693..5525f5ff4 100644 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -629,6 +629,7 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(amdgpu_kernel); KEYWORD(amdgpu_gfx); KEYWORD(tailcc); + KEYWORD(cangjiegccc); KEYWORD(cc); KEYWORD(c); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index fd502eded..323765d4d 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -1904,6 +1904,7 @@ void LLParser::parseOptionalDLLStorageClass(unsigned &Res) { /// ::= 'amdgpu_cs' /// ::= 'amdgpu_kernel' /// ::= 'tailcc' +/// ::= 'cangjiegccc' /// ::= 'cc' UINT /// bool LLParser::parseOptionalCallingConv(unsigned &CC) { @@ -1956,6 +1957,7 @@ bool LLParser::parseOptionalCallingConv(unsigned &CC) { case lltok::kw_amdgpu_cs: CC = CallingConv::AMDGPU_CS; break; case lltok::kw_amdgpu_kernel: CC = CallingConv::AMDGPU_KERNEL; break; case lltok::kw_tailcc: CC = CallingConv::Tail; break; + case lltok::kw_cangjiegccc: CC = CallingConv::CangjieGC; break; case lltok::kw_cc: { Lex.Lex(); return parseUInt32(CC); diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index 1943b5db9..0c8822991 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -2003,6 +2003,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) { return Attribute::Hot; case bitc::ATTR_KIND_PRESPLIT_COROUTINE: return Attribute::PresplitCoroutine; + case bitc::ATTR_KIND_CJ_STACK_POINTER: + return Attribute::CJStackPointer; } } @@ -4041,7 +4043,6 @@ Error BitcodeReader::parseGlobalIndirectSymbolRecord( StringRef(Strtab.data() + Record[OpNum], Record[OpNum + 1])); OpNum += 2; } - ValueList.push_back(NewGA, getVirtualTypeID(NewGA->getType(), TypeID)); IndirectSymbolInits.push_back(std::make_pair(NewGA, Val)); return Error::success(); diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index d7e012fb6..0dced4209 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -780,6 +780,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) { return bitc::ATTR_KIND_MUSTPROGRESS; case Attribute::PresplitCoroutine: return bitc::ATTR_KIND_PRESPLIT_COROUTINE; + case Attribute::CJStackPointer: + return bitc::ATTR_KIND_CJ_STACK_POINTER; case Attribute::EndAttrKinds: llvm_unreachable("Can not encode end-attribute kinds marker."); case Attribute::None: diff --git a/llvm/lib/CMakeLists.txt b/llvm/lib/CMakeLists.txt index 5ecdf5af9..99f246d58 100644 --- a/llvm/lib/CMakeLists.txt +++ b/llvm/lib/CMakeLists.txt @@ -1,5 +1,8 @@ include(LLVM-Build) +# demangle from runtime +set(CANGJIE_DEMANGLE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../utils/demangle) + # `Demangle', `Support' and `TableGen' libraries are added on the top-level # CMakeLists.txt @@ -62,4 +65,4 @@ endif() # Component post-processing LLVMBuildResolveComponentsLink() -LLVMBuildGenerateCFragment(OUTPUT ${LLVMCONFIGLIBRARYDEPENDENCIESINC}) +LLVMBuildGenerateCFragment(OUTPUT ${LLVMCONFIGLIBRARYDEPENDENCIESINC}) \ No newline at end of file diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 32a10ad41..75d3d2e57 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -37,6 +37,7 @@ #include "llvm/BinaryFormat/COFF.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/CodeGen/CJMetadata.h" #include "llvm/CodeGen/GCMetadata.h" #include "llvm/CodeGen/GCMetadataPrinter.h" #include "llvm/CodeGen/MachineBasicBlock.h" @@ -80,6 +81,7 @@ #include "llvm/IR/Module.h" #include "llvm/IR/Operator.h" #include "llvm/IR/PseudoProbe.h" +#include "llvm/IR/Statepoint.h" #include "llvm/IR/Type.h" #include "llvm/IR/Value.h" #include "llvm/IR/ValueHandle.h" @@ -126,6 +128,14 @@ using namespace llvm; #define DEBUG_TYPE "asm-printer" +static cl::opt EnableCJNewObjFast("enable-cangjie-new-obj-fastpath", + cl::init(true), cl::ReallyHidden); +static cl::opt ReflectOffset("reflect-offset", cl::init(true), + cl::NotHidden); + +namespace llvm { +extern cl::opt CangjieStackCheckSize; +} const char DWARFGroupName[] = "dwarf"; const char DWARFGroupDescription[] = "DWARF Emission"; @@ -144,6 +154,21 @@ const char PPGroupDescription[] = "Pseudo Probe Emission"; STATISTIC(EmittedInsts, "Number of machine instrs printed"); +namespace llvm { +extern cl::opt CJPipeline; +extern cl::opt EnableStackGrow; + +// Warning: The following values must be synchronized with +// the data struct `ThreadLocalData` in runtime +constexpr int64_t AllocBufferOffsetInCJTLS = 0; +constexpr int64_t MutatorOffsetInCJTLS = AllocBufferOffsetInCJTLS + 8; +constexpr int64_t CJThreadOffsetInCJTLS = MutatorOffsetInCJTLS + 8; +constexpr int64_t ScheduleOffsetInCJTLS = CJThreadOffsetInCJTLS + 8; +constexpr int64_t PreemptFlagOffsetInCJTLS = ScheduleOffsetInCJTLS + 8; +constexpr int64_t ProtectAddrOffsetInCJTLS = PreemptFlagOffsetInCJTLS + 8; +constexpr int64_t SafePollingAddrOffsetInCJTLS = ProtectAddrOffsetInCJTLS + 8; +} // end anonymous namespace + char AsmPrinter::ID = 0; using gcp_map_type = DenseMap>; @@ -755,7 +780,7 @@ void AsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { // If we have a bss global going to a section that supports the // zerofill directive, do so here. if (GVKind.isBSS() && MAI->hasMachoZeroFillDirective() && - TheSection->isVirtualSection()) { + TheSection->isVirtualSection() && !GV->isCJMeta()) { if (Size == 0) Size = 1; // zerofill of 0 bytes is undefined. emitLinkage(GV, GVSym); @@ -767,7 +792,8 @@ void AsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { // If this is a BSS local symbol and we are emitting in the BSS // section use .lcomm/.comm directive. if (GVKind.isBSSLocal() && - getObjFileLowering().getBSSSection() == TheSection) { + getObjFileLowering().getBSSSection() == TheSection && + !GV->isCJMeta()) { if (Size == 0) Size = 1; // .comm Foo, 0 is undefined, avoid it. @@ -852,12 +878,25 @@ void AsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { emitLinkage(GV, EmittedInitSym); emitAlignment(Alignment, GV); + if (GV->isCJMeta()) { + MCSymbol *RefSym = + OutContext.getOrCreateSymbol(Twine(".LRef.") + GVSym->getName()); + OutStreamer->emitLabel(RefSym); + } OutStreamer->emitLabel(EmittedInitSym); MCSymbol *LocalAlias = getSymbolPreferLocal(*GV); if (LocalAlias != EmittedInitSym) OutStreamer->emitLabel(LocalAlias); - emitGlobalConstant(GV->getParent()->getDataLayout(), GV->getInitializer()); + bool IsReflectionGV = false; + if (ReflectOffset) { + if (GV->hasAttribute("CFileReflect")) { + IsReflectionGV = true; + } + } + + emitGlobalConstant(GV->getParent()->getDataLayout(), GV->getInitializer(), + nullptr, IsReflectionGV); if (MAI->hasDotTypeDotSizeDirective()) // .size foo, 42 @@ -969,6 +1008,18 @@ void AsmPrinter::emitFunctionHeader() { if (MAI->needsFunctionDescriptors()) emitFunctionDescriptor(); + const Triple TT(F.getParent()->getTargetTriple()); + if (F.hasCangjieGC() && !F.hasFnAttribute("leaf-function") && + !TT.isOSBinFormatMachO()) { + MCSymbol *DescSymbol = OutContext.getOrCreateSymbol( + ".Lmethod_desc." + CurrentFnSym->getName()); + MCSymbol *PCSym = OutContext.createTempSymbol(); + OutStreamer->emitLabel(PCSym); + const MCExpr *MethodDescOffset = MCBinaryExpr::createSub( + MCSymbolRefExpr::create(DescSymbol, OutContext), + MCSymbolRefExpr::create(PCSym, OutContext), OutContext); + OutStreamer->emitValue(MethodDescOffset, 4); + } // Emit the CurrentFnSym. This is a virtual function to allow targets to do // their wild and crazy things as required. emitFunctionEntryLabel(); @@ -1463,7 +1514,10 @@ void AsmPrinter::emitFunctionBody() { // Print out code for the function. bool HasAnyRealCode = false; int NumInstsInFunction = 0; - + bool CangjieSrc = MF->getFunction().hasCangjieGC(); + if (CangjieSrc) { + MF->setEpilogueLabel(nullptr); + } bool CanDoExtraAnalysis = ORE->allowExtraAnalysis(DEBUG_TYPE); for (auto &MBB : *MF) { // Print a label for the basic block. @@ -1486,6 +1540,29 @@ void AsmPrinter::emitFunctionBody() { HI.TimerGroupDescription, TimePassesIsEnabled); HI.Handler->beginInstruction(&MI); } + // Place the epilogue label for Cangjie function. + // Please note the x86 'esp/rsp' recovery instrucition + // is not recognized as in the epilogue sequence marked by + // FrameDestroy flag. The EpilogueIns flag is added to + // mark these instructions. The epilogue label will be + // placed before the first pop of callee-saved register + // or the recovery of 'esp/rsp' registers. + if ((MI.getFlag(MachineInstr::FrameDestroy) || + MI.getExtFlag(MachineInstr::EpilogueIns)) && + !MF->isEpilogueLabelEmitted() && CangjieSrc) { + auto TSym = createTempSymbol("_epilogue"); + MF->setEpilogueLabel(TSym); + OutStreamer->emitLabel(TSym); + MF->setEpilogueLabelEmitted(); + // SP might change on certain platforms during function execution. + // This would break our stack unwinding in exception path through + // epilogue. In order to guarantee that the SP is at the correct + // position when exception jumps to epilogue. The SP is reloaded + // to the correct position manually here. + if (MF->getNeedCangjieReloadSP()) { + reloadSPForEpilogue(*MF); + } + } if (isVerbose()) emitComments(MI, OutStreamer->getCommentOS()); @@ -1588,6 +1665,7 @@ void AsmPrinter::emitFunctionBody() { } } emitBasicBlockEnd(MBB); + emitCangjieCustomInst(); if (CanDoExtraAnalysis) { // Skip empty blocks. @@ -1620,6 +1698,37 @@ void AsmPrinter::emitFunctionBody() { } } + if (CangjieSrc) { + // emit stackgrow or stack_overflow_error + for (auto SC: StackCheckMap) { + for (const HandlerInfo &HI : Handlers) { + HI.Handler->beginInstruction(SC.first); + } + if (EnableStackGrow) { + NumInstsInFunction += emitStackGrow(*SC.first); + } else { + NumInstsInFunction += emitSOFECall(*SC.first); + } + for (const HandlerInfo &HI : Handlers) { + HI.Handler->endInstruction(); + } + } + StackCheckMap.clear(); + + // emit safepoint + for (unsigned SafeIndex = 0; SafeIndex < SafepointStackMap.size(); + SafeIndex++) { + for (const HandlerInfo &HI : Handlers) { + HI.Handler->beginInstruction(std::get<0>(SafepointStackMap[SafeIndex])); + } + NumInstsInFunction += emitSafePointDirectCall(SafeIndex); + for (const HandlerInfo &HI : Handlers) { + HI.Handler->endInstruction(); + } + } + SafepointStackMap.clear(); + } + EmittedInsts += NumInstsInFunction; MachineOptimizationRemarkAnalysis R(DEBUG_TYPE, "InstructionCount", MF->getFunction().getSubprogram(), @@ -1944,8 +2053,11 @@ bool AsmPrinter::doFinalization(Module &M) { computeGlobalGOTEquivs(M); // Emit global variables. - for (const auto &G : M.globals()) - emitGlobalVariable(&G); + for (const auto &G : M.globals()) { + if (G.isCJGlobalValue() || G.isCJSDKVersion() || !G.isCJMeta()) { + emitGlobalVariable(&G); + } + } // Emit remaining GOT equivalent globals. emitGlobalGOTEquivs(); @@ -2135,7 +2247,8 @@ bool AsmPrinter::doFinalization(Module &M) { for (const GlobalValue &GV : M.global_values()) { if (!GV.use_empty() && !GV.isTransitiveUsedByMetadataOnly() && !GV.isThreadLocal() && !GV.hasDLLImportStorageClass() && - !GV.getName().startswith("llvm.") && !GV.hasAtLeastLocalUnnamedAddr()) + !GV.getName().startswith("llvm.") && + !GV.hasAtLeastLocalUnnamedAddr()) OutStreamer->emitAddrsigSym(getSymbol(&GV)); } } @@ -2217,11 +2330,13 @@ void AsmPrinter::SetupMachineFunction(MachineFunction &MF) { MBBSectionRanges.clear(); MBBSectionExceptionSyms.clear(); bool NeedsLocalForSize = MAI->needsLocalForSize(); + bool NeedsInCangjieGC = F.hasCangjieGC(); if (F.hasFnAttribute("patchable-function-entry") || F.hasFnAttribute("function-instrument") || F.hasFnAttribute("xray-instruction-threshold") || needFuncLabelsForEHOrDebugInfo(MF) || NeedsLocalForSize || - MF.getTarget().Options.EmitStackSizeSection || MF.hasBBLabels()) { + NeedsInCangjieGC || MF.getTarget().Options.EmitStackSizeSection || + MF.hasBBLabels()) { CurrentFnBegin = createTempSymbol("func_begin"); if (NeedsLocalForSize) CurrentFnSymForSize = CurrentFnBegin; @@ -2861,7 +2976,9 @@ static void emitGlobalConstantImpl(const DataLayout &DL, const Constant *C, AsmPrinter &AP, const Constant *BaseCV = nullptr, uint64_t Offset = 0, - AsmPrinter::AliasMapTy *AliasList = nullptr); + AsmPrinter::AliasMapTy *AliasList = + nullptr, + bool IsReflectGV = false); static void emitGlobalConstantFP(const ConstantFP *CFP, AsmPrinter &AP); static void emitGlobalConstantFP(APFloat APF, Type *ET, AsmPrinter &AP); @@ -3033,7 +3150,8 @@ static void emitGlobalConstantVector(const DataLayout &DL, static void emitGlobalConstantStruct(const DataLayout &DL, const ConstantStruct *CS, AsmPrinter &AP, const Constant *BaseCV, uint64_t Offset, - AsmPrinter::AliasMapTy *AliasList) { + AsmPrinter::AliasMapTy *AliasList, + bool IsReflectGV = false) { // Print the fields in successive locations. Pad to align if needed! unsigned Size = DL.getTypeAllocSize(CS->getType()); const StructLayout *Layout = DL.getStructLayout(CS->getType()); @@ -3043,7 +3161,7 @@ static void emitGlobalConstantStruct(const DataLayout &DL, // Print the actual field value. emitGlobalConstantImpl(DL, Field, AP, BaseCV, Offset + SizeSoFar, - AliasList); + AliasList, IsReflectGV); // Check if padding is needed and insert one or more 0s. uint64_t FieldSize = DL.getTypeAllocSize(Field->getType()); @@ -3257,10 +3375,53 @@ static void handleIndirectSymViaGOTPCRel(AsmPrinter &AP, const MCExpr **ME, AP.GlobalGOTEquivs[GOTEquivSym] = std::make_pair(GV, NumUses); } + +static const MCExpr *transRef2OffsetForReflectGV(const MCExpr *ME, + AsmPrinter &AP) { + const Module *M = AP.MMI->getModule(); + + auto createOffsetExpr = [&](const MCSymbolRefExpr *SRE, bool ReportErr) { + const MCSymbol &Sym = SRE->getSymbol(); + const GlobalVariable *GV = nullptr; + if (AP.TM.getTargetTriple().isOSBinFormatMachO()) { + // 1: GVName name is _xxx in macos, so begin from 1 to get xxx. + GV = M->getNamedGlobal(Sym.getName().substr(1)); + } else { + GV = M->getNamedGlobal(Sym.getName()); + } + if (GV && GV->isCJReflectGV()) { + MCSymbol *RefSym = + AP.OutContext.getOrCreateSymbol(Twine(".LRef.") + Sym.getName()); + + MCSymbol *PCSymbol = AP.OutContext.createTempSymbol(); + AP.OutStreamer->emitLabel(PCSymbol); + ME = MCBinaryExpr::createSub( + MCSymbolRefExpr::create(RefSym, AP.OutContext), + MCSymbolRefExpr::create(PCSymbol, AP.OutContext), AP.OutContext); + } else if (ReportErr) { + report_fatal_error("generate reflect info failed! GV is not a valid reflect GV!\n"); + } + }; + + if (const MCSymbolRefExpr *SRE = dyn_cast(ME)) { + createOffsetExpr(SRE, false); + } else if (auto *SBE = dyn_cast(ME)) { + if (SBE->getOpcode() != MCBinaryExpr::Opcode::Add) { + report_fatal_error("Binary expr should be add expr in reflect GV!\n"); + } + auto *AddValue = SBE->getRHS(); + auto *SRExpr = cast(SBE->getLHS()); + createOffsetExpr(SRExpr, true); + ME = MCBinaryExpr::createAdd(ME, AddValue, AP.OutContext); + } + return ME; +} + static void emitGlobalConstantImpl(const DataLayout &DL, const Constant *CV, AsmPrinter &AP, const Constant *BaseCV, uint64_t Offset, - AsmPrinter::AliasMapTy *AliasList) { + AsmPrinter::AliasMapTy *AliasList, + bool IsReflectGV) { emitGlobalAliasInline(AP, Offset, AliasList); uint64_t Size = DL.getTypeAllocSize(CV->getType()); @@ -3307,13 +3468,14 @@ static void emitGlobalConstantImpl(const DataLayout &DL, const Constant *CV, return emitGlobalConstantArray(DL, CVA, AP, BaseCV, Offset, AliasList); if (const ConstantStruct *CVS = dyn_cast(CV)) - return emitGlobalConstantStruct(DL, CVS, AP, BaseCV, Offset, AliasList); + return emitGlobalConstantStruct(DL, CVS, AP, BaseCV, Offset, AliasList, IsReflectGV); if (const ConstantExpr *CE = dyn_cast(CV)) { // Look through bitcasts, which might not be able to be MCExpr'ized (e.g. of // vectors). if (CE->getOpcode() == Instruction::BitCast) - return emitGlobalConstantImpl(DL, CE->getOperand(0), AP); + return emitGlobalConstantImpl(DL, CE->getOperand(0), AP, nullptr, 0, + nullptr, IsReflectGV); if (Size > 8) { // If the constant expression's size is greater than 64-bits, then we have @@ -3338,15 +3500,51 @@ static void emitGlobalConstantImpl(const DataLayout &DL, const Constant *CV, if (AP.getObjFileLowering().supportIndirectSymViaGOTPCRel()) handleIndirectSymViaGOTPCRel(AP, &ME, BaseCV, Offset); + if (IsReflectGV) { + ME = transRef2OffsetForReflectGV(ME, AP); + } + AP.OutStreamer->emitValue(ME, Size); } +// When the allocated space is less than the threshold and the call +// functions are non-stack-overflow function, we do not need to check +// for stack out-of-bounds. +static bool isNonStackOverflowFunc(MachineFunction &MF, unsigned FrameSize) { + if (FrameSize > CangjieStackCheckSize) + return false; + + for (MachineBasicBlock &BB : MF) { + for (MachineInstr &MI : BB) { + if (MI.getOpcode() != TargetOpcode::STATEPOINT) + continue; + + StatepointOpers SO(&MI); + uint64_t ID = SO.getID(); + if (ID == CJStatepointID::Safepoint || ID == CJStatepointID::StackCheck) + continue; + + const MachineOperand &CallTarget = SO.getCallTarget(); + if (!CallTarget.isGlobal()) + return false; + + const auto *Callee = dyn_cast(CallTarget.getGlobal()); + if (Callee == nullptr) + return false; + + if (!Callee->hasFnAttribute("cj-runtime")) + return false; + } + } + return true; +} + /// EmitGlobalConstant - Print a general LLVM constant to the .s file. void AsmPrinter::emitGlobalConstant(const DataLayout &DL, const Constant *CV, - AliasMapTy *AliasList) { + AliasMapTy *AliasList, bool IsReflectGV) { uint64_t Size = DL.getTypeAllocSize(CV->getType()); if (Size) - emitGlobalConstantImpl(DL, CV, *this, nullptr, 0, AliasList); + emitGlobalConstantImpl(DL, CV, *this, nullptr, 0, AliasList, IsReflectGV); else if (MAI->hasSubsectionsViaSymbols()) { // If the global has zero size, emit a single byte so that two labels don't // look like they are at the same location. @@ -3728,6 +3926,11 @@ void AsmPrinter::emitStackMaps(StackMaps &SM) { SM.serializeToStackMapSection(); } +void AsmPrinter::emitCJMetadataInfo(CJMetadataInfo &CMI, Module &M) { + if (CJPipeline) { + CMI.emitCJMetadata(M); + } +} /// Pin vtable to this file. AsmPrinterHandler::~AsmPrinterHandler() = default; @@ -3901,3 +4104,112 @@ unsigned int AsmPrinter::getUnitLengthFieldByteSize() const { return dwarf::getUnitLengthFieldByteSize( OutStreamer->getContext().getDwarfFormat()); } + +const Function * +AsmPrinter::tryGetCangjieStubCallNativeFunc(const MachineInstr *MI, + const Function *Callee) const { + const Function *NativeFunc = nullptr; + const auto &CallerFunc = MI->getParent()->getParent()->getFunction(); + if (Callee->isCangjieNativeStub(CallerFunc)) { + if (Callee->hasFnAttribute("cj2c")) { + NativeFunc = Callee->getParent()->getFunction("CJ_MCC_C2NStub"); + } else { + NativeFunc = Callee->getParent()->getFunction("CJ_MCC_N2CStub"); + } + } + return NativeFunc; +} + +void AsmPrinter::emitCangjieStackCheck(const MachineInstr &MI) { + unsigned FrameSize = MI.peekCJStackSize() & 0x7fffffff; + if (isNonStackOverflowFunc(*MF, FrameSize)) + return; + + emitCJStackCheck(MI); +} + +// Note: emit specific inst should update inst size info in +// AArch64InstrInfo::getInstSizeInBytes for AArch64 at the same time +bool AsmPrinter::tryEmitCangjieSpecificCallByMOSym(const MachineInstr *MI, + const MachineOperand &MOSym, + unsigned Opcode) { + if (!MOSym.isGlobal()) { + return false; + } + const auto *Callee = dyn_cast(MOSym.getGlobal()); + if (Callee == nullptr) { + return false; + } + StringRef FuncName = Callee->getName(); + if (FuncName.isGetGCPhase()) { + emitGcStateCheck(); + return true; + } + // Using for debug information only, no emit instructions. + if (FuncName.isSetDebugLocation()) { + return true; + } + if (FuncName.equals("CJ_MRT_PreInitializePackage")) { + emitMetadataAddress(); + return true; + } + if (FuncName.equals("CJ_MCC_ThrowException")) { + emitCJThrowException(MI, MOSym, Opcode); + return true; + } + if ((FuncName.equals("CJ_MCC_NewObject") || + FuncName.equals("CJ_MCC_NewFinalizer")) && + EnableCJNewObjFast) { + emitMccNewObjectFastPath(MI, MOSym, Opcode); + return true; + } + if (Callee->isGetCJThreadId()) { + emitGetCJThreadId(); + return true; + } + const Function *NativeFunc = tryGetCangjieStubCallNativeFunc(MI, Callee); + if (NativeFunc != nullptr) { + emitCangjieCallStubInstImpl(MI, NativeFunc, MOSym, Opcode); + return true; + } + return false; +} + +bool AsmPrinter::tryEmitCangjieSpecificCall(const MachineInstr *MI) { + if (!MI->isCall()) { + return false; + } + + unsigned Opcode = MI->getOpcode(); + // statepoint call will be processed by LowerSTATEPOINT + if (Opcode == TargetOpcode::STATEPOINT) { + return false; + } + + if (MI->getNumOperands() == 0) { + return false; + } + + const MachineOperand &MOSym = MI->getOperand(0); + return tryEmitCangjieSpecificCallByMOSym(MI, MOSym, Opcode); +} + +int64_t AsmPrinter::getAllocBufferOffsetInCJTLS() const { + return AllocBufferOffsetInCJTLS; +} + +int64_t AsmPrinter::getMutatorOffsetInCJTLS() const { + return MutatorOffsetInCJTLS; +} + +int64_t AsmPrinter::getCJThreadOffsetInCJTLS() const { + return CJThreadOffsetInCJTLS; +} + +int64_t AsmPrinter::getProtectAddrOffsetInCJTLS() const { + return ProtectAddrOffsetInCJTLS; +} + +int64_t AsmPrinter::getSafepointCheckAddrOffsetInCJTLS() const { + return SafePollingAddrOffsetInCJTLS; +} diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp index bfa53f5b9..593514f84 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp @@ -121,6 +121,16 @@ void AsmPrinter::emitEncodingByte(unsigned Val, const char *Desc) const { OutStreamer->emitIntValue(Val, 1); } +/// When there are no callsites in a function, emit a magic number +/// 0x55555555 in EH table header. +void AsmPrinter::EmitNoCallsiteEHHeader() const { + if (isVerbose()) { + OutStreamer->AddComment(Twine("No callsite function EH table header")); + } + + OutStreamer->emitIntValue(0x55555555, 4); +} + /// GetSizeOfEncodedValue - Return the size of the encoding in bytes. unsigned AsmPrinter::GetSizeOfEncodedValue(unsigned Encoding) const { if (Encoding == dwarf::DW_EH_PE_omit) diff --git a/llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp b/llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp index 31644959b..773166df5 100644 --- a/llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp @@ -164,7 +164,7 @@ bool EHStreamer::callToNoUnwindFunction(const MachineInstr *MI) { for (const MachineOperand &MO : MI->operands()) { if (!MO.isGlobal()) continue; - const Function *F = dyn_cast(MO.getGlobal()); + const Function *F = dyn_cast_or_null(MO.getGlobal()); if (!F) continue; if (SawFunc) { @@ -242,6 +242,9 @@ void EHStreamer::computeCallSiteTable( bool IsSJLJ = Asm->MAI->getExceptionHandlingType() == ExceptionHandling::SjLj; + // Whether generate EH tabel for Cangjie. + bool CangjieSrc = Asm->MF && Asm->MF->getFunction().hasCangjieGC(); + // Visit all instructions in order of address. for (const auto &MBB : *Asm->MF) { if (&MBB == &Asm->MF->front() || MBB.isBeginSection()) { @@ -289,7 +292,8 @@ void EHStreamer::computeCallSiteTable( if (SawPotentiallyThrowing && (Asm->MAI->usesCFIForEH() || Asm->MAI->getExceptionHandlingType() == ExceptionHandling::AIX)) { - CallSites.push_back({LastLabel, BeginLabel, nullptr, 0}); + if (!CangjieSrc) + CallSites.push_back({LastLabel, BeginLabel, nullptr, 0}); PreviousIsInvoke = false; } @@ -319,7 +323,7 @@ void EHStreamer::computeCallSiteTable( } // Otherwise, create a new call-site. - if (!IsSJLJ) + if (!IsSJLJ && (Site.LPad != 0 || !CangjieSrc)) CallSites.push_back(Site); else { // SjLj EH must maintain the call sites in the order assigned @@ -340,9 +344,11 @@ void EHStreamer::computeCallSiteTable( // function may throw, create a call-site entry with no landing pad for // the region following the try-range. if (SawPotentiallyThrowing && !IsSJLJ) { - CallSiteEntry Site = {LastLabel, CallSiteRanges.back().FragmentEndLabel, - nullptr, 0}; - CallSites.push_back(Site); + if (!CangjieSrc) { + CallSiteEntry Site = {LastLabel, CallSiteRanges.back().FragmentEndLabel, + nullptr, 0}; + CallSites.push_back(Site); + } SawPotentiallyThrowing = false; } CallSiteRanges.back().CallSiteEndIdx = CallSites.size(); @@ -413,6 +419,8 @@ MCSymbol *EHStreamer::emitExceptionTable() { Asm->getObjFileLowering().getCallSiteEncoding(); bool HaveTTData = !TypeInfos.empty() || !FilterIds.empty(); + bool CangjieSrc = MF->getFunction().hasCangjieGC(); + // Type infos. MCSection *LSDASection = Asm->getObjFileLowering().getSectionForLSDA( MF->getFunction(), *Asm->CurrentFnSym, Asm->TM); @@ -461,10 +469,10 @@ MCSymbol *EHStreamer::emitExceptionTable() { Asm->emitAlignment(Align(4)); // Emit the LSDA. - MCSymbol *GCCETSym = - Asm->OutContext.getOrCreateSymbol(Twine("GCC_except_table")+ - Twine(Asm->getFunctionNumber())); - Asm->OutStreamer->emitLabel(GCCETSym); + MCSymbol *ETSym = Asm->OutContext.getOrCreateSymbol( + Twine(CangjieSrc ? "CJ_except_table" : "GCC_except_table") + + Twine(Asm->getFunctionNumber())); + Asm->OutStreamer->emitLabel(ETSym); MCSymbol *CstEndLabel = Asm->createTempSymbol( CallSiteRanges.size() > 1 ? "action_table_base" : "cst_end"); @@ -479,7 +487,9 @@ MCSymbol *EHStreamer::emitExceptionTable() { // * For Itanium, these references will be emitted for every callsite range. // * For SJLJ and Wasm, they will be emitted only once in the LSDA header. auto EmitTypeTableRefAndCallSiteTableEndRef = [&]() { - Asm->emitEncodingByte(TTypeEncoding, "@TType"); + if (CallSites.size() || !CangjieSrc) { + Asm->emitEncodingByte(TTypeEncoding, "@TType"); + } if (HaveTTData) { // N.B.: There is a dependency loop between the size of the TTBase uleb128 // here and the amount of padding before the aligned type table. The @@ -495,9 +505,11 @@ MCSymbol *EHStreamer::emitExceptionTable() { // Wasm, and start of a call-site range for Itanium) to the end of the // whole call-site table (end of the last call-site range for Itanium). MCSymbol *CstBeginLabel = Asm->createTempSymbol("cst_begin"); - Asm->emitEncodingByte(CallSiteEncoding, "Call site"); - Asm->emitLabelDifferenceAsULEB128(CstEndLabel, CstBeginLabel); - Asm->OutStreamer->emitLabel(CstBeginLabel); + if (CallSites.size() || !CangjieSrc) { + Asm->emitEncodingByte(CallSiteEncoding, "Call site"); + Asm->emitLabelDifferenceAsULEB128(CstEndLabel, CstBeginLabel); + Asm->OutStreamer->emitLabel(CstBeginLabel); + } }; // An alternative path to EmitTypeTableRefAndCallSiteTableEndRef. @@ -578,8 +590,10 @@ MCSymbol *EHStreamer::emitExceptionTable() { Asm->OutStreamer->emitLabel(Asm->getMBBExceptionSym(Asm->MF->front())); // emit the LSDA header. - Asm->emitEncodingByte(dwarf::DW_EH_PE_omit, "@LPStart"); - EmitTypeTableRefAndCallSiteTableEndRef(); + if (CallSites.size() || !CangjieSrc) { + Asm->emitEncodingByte(dwarf::DW_EH_PE_omit, "@LPStart"); + EmitTypeTableRefAndCallSiteTableEndRef(); + } unsigned idx = 0; for (SmallVectorImpl::const_iterator @@ -666,7 +680,11 @@ MCSymbol *EHStreamer::emitExceptionTable() { // If only one call-site range exists, LPStart is omitted as it is the // same as the function entry. if (CallSiteRanges.size() == 1) { - Asm->emitEncodingByte(dwarf::DW_EH_PE_omit, "@LPStart"); + if (CallSites.size() || !CangjieSrc) { + Asm->emitEncodingByte(dwarf::DW_EH_PE_omit, "@LPStart"); + } else { + Asm->EmitNoCallsiteEHHeader(); + } } else if (!Asm->isPositionIndependent()) { // For more than one call-site ranges, LPStart must be explicitly // specified. @@ -746,7 +764,8 @@ MCSymbol *EHStreamer::emitExceptionTable() { Asm->emitULEB128(S.Action); } } - Asm->OutStreamer->emitLabel(CstEndLabel); + if (CallSites.size() || !CangjieSrc) + Asm->OutStreamer->emitLabel(CstEndLabel); } // Emit the Action Table. @@ -790,8 +809,9 @@ MCSymbol *EHStreamer::emitExceptionTable() { emitTypeInfos(TTypeEncoding, TTBaseLabel); } - Asm->emitAlignment(Align(4)); - return GCCETSym; + if (CallSites.size() || !CangjieSrc) + Asm->emitAlignment(Align(4)); + return ETSym; } void EHStreamer::emitTypeInfos(unsigned TTypeEncoding, MCSymbol *TTBaseLabel) { diff --git a/llvm/lib/CodeGen/CJBarrierLowering.cpp b/llvm/lib/CodeGen/CJBarrierLowering.cpp new file mode 100644 index 000000000..b3ea0df0a --- /dev/null +++ b/llvm/lib/CodeGen/CJBarrierLowering.cpp @@ -0,0 +1,1144 @@ +//===-- GCBarrierLowering.cpp - Cangjie Barrier Lowering ------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file implements the lowering for the cangjie barrier mechanism. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/PriorityWorklist.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/IR/CJIntrinsics.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/SafepointIRVerifier.h" +#include "llvm/IR/Statepoint.h" +#include "llvm/IR/CJStructTypeGCInfo.h" +#include "llvm/InitializePasses.h" +#include "llvm/MC/MCContext.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/LoopSimplify.h" +#include "llvm/Transforms/Utils/LoopUtils.h" + +#include + +using namespace llvm; +using namespace cangjie; + +#define DEBUG_TYPE "cj-barrier-lowering" + +static cl::opt EnableTaggedPointer("enable-tagged-pointer", + cl::init(true), cl::Hidden); +static cl::opt EnableGCPhase("enable-gc-phase", cl::init(true), + cl::Hidden); +static cl::opt EnableGCFastPath("enable-gc-fast-path", cl::init(true), + cl::Hidden); +static cl::opt EnableGCStateLoop("cj-gcstate-dup-loop", cl::init(false), + cl::ReallyHidden); + +namespace llvm { +extern cl::opt CangjieJIT; +extern cl::opt DisableGCSupport; +extern cl::opt EnableSafepointOnly; +} // namespace llvm + +namespace { +const static StringRef NewObjFastStr = "CJ_MCC_NewObjectFast"; +const static StringRef NewObjFinalizerFastStr = "CJ_MCC_NewFinalizerFast"; +template +using StdMap = std::unordered_map; +const static StdMap IntrinsicMap{ + {Intrinsic::cj_gcwrite_ref, "CJ_MCC_WriteRefField"}, + {Intrinsic::cj_gcwrite_struct, "CJ_MCC_WriteStructField"}, + {Intrinsic::cj_gcwrite_static_ref, "CJ_MCC_WriteStaticRef"}, + {Intrinsic::cj_gcwrite_static_struct, "CJ_MCC_WriteStaticStruct"}, + {Intrinsic::cj_gcread_ref, "CJ_MCC_ReadRefField"}, + {Intrinsic::cj_gcread_weakref, "CJ_MCC_ReadWeakRef"}, + {Intrinsic::cj_gcread_struct, "CJ_MCC_ReadStructField"}, + {Intrinsic::cj_gcread_static_ref, "CJ_MCC_ReadStaticRef"}, + {Intrinsic::cj_gcread_static_struct, "CJ_MCC_ReadStaticStruct"}, + {Intrinsic::cj_copy_struct_field, "CJ_MCC_CopyStructField"}, + {Intrinsic::cj_array_copy_ref, "CJ_MCC_ArrayCopyRef"}, + {Intrinsic::cj_array_copy_struct, "CJ_MCC_ArrayCopyStruct"}, + {Intrinsic::cj_atomic_store, "CJ_MCC_AtomicWriteReference"}, + {Intrinsic::cj_atomic_load, "CJ_MCC_AtomicReadReference"}, + {Intrinsic::cj_atomic_swap, "CJ_MCC_AtomicSwapReference"}, + {Intrinsic::cj_atomic_compare_swap, "CJ_MCC_AtomicCompareAndSwapReference"}, + {Intrinsic::cj_assign_generic, "CJ_MCC_AssignGeneric"}, + {Intrinsic::cj_gcwrite_generic, "CJ_MCC_WriteGeneric"}, + {Intrinsic::cj_gcread_generic, "CJ_MCC_ReadGeneric"}, + {Intrinsic::cj_array_copy_generic, "CJ_MCC_ArrayCopyGeneric"}, + {Intrinsic::cj_gcwrite_generic_payload, "CJ_MCC_WriteGenericPayload"}}; + +struct BBInfo { + // BB last used barrier after safepoint + CallBase *LastBarrier; + + bool HasCalls; // has GC Calls + + // there are barriers before first safepoint + bool BeforeCall; + bool Visited; + Instruction *OutCheck; + Instruction *InCheck; + + // 8: default preBBs size + SmallVector PreBBs; + + BBInfo() : HasCalls(false), BeforeCall(false), Visited(false) { + LastBarrier = nullptr; + OutCheck = nullptr; + InCheck = nullptr; + } +}; + +static const unsigned MaxLoopBlock = 64; +static const char *ClonedPinLoopTag = "cj.pin.loop.clone"; + +static bool mayBeSafepoint(Instruction *Inst) { + if (auto CI = dyn_cast(Inst)) { + if (CI->getIntrinsicID() == Intrinsic::cj_gc_statepoint) { + return true; + } + } + return false; +} + +/// Declarations for Cangjie barrier functions. +class BarrierMaker { +public: + BarrierMaker(Module &M, CJStructTypeGCInfo &GCInfo) + : M(M), C(M.getContext()), GCInfo(GCInfo) { + GCPtr = Type::getInt8PtrTy(C, 1); + I8Ptr = Type::getInt8PtrTy(C); + I64 = Type::getInt64Ty(C); + I32 = Type::getInt32Ty(C); + } + + ~BarrierMaker() = default; + + void replaceInstWithBarrier(IntrinsicInst *II) { + if (II->isTailCall()) { + II->setTailCall(false); + } + + Function *Callee = getOrInsertRuntimeFunc(II); + switch (II->getIntrinsicID()) { + case Intrinsic::cj_gcwrite_struct: { + Value *Param[5] = {II->getArgOperand(GCWriteStruct::BaseObj), + II->getArgOperand(GCWriteStruct::Dst), + II->getArgOperand(GCWriteStruct::Size), + II->getArgOperand(GCWriteStruct::Src), + II->getArgOperand(GCWriteStruct::Size)}; + // gcwrite.agg src has two case: + // i8* on the stack and i8 addrspace(1)* on the heap. + // The runtime function has only one signature. Here, the src of i8* + // is converted to i8 addrspace(1)*. + if (!isGCPointerType(II->getArgOperand(GCWriteStruct::Src)->getType())) { + Param[3] = new AddrSpaceCastInst(II->getArgOperand(GCWriteStruct::Src), + Type::getInt8PtrTy(C, 1), "", II); + } + // Replace the original intrinsic with the runtime function. + replaceCallInst(Callee, Param, II); + break; + } + case Intrinsic::cj_gcread_static_struct: + case Intrinsic::cj_gcwrite_static_struct: { + Value *GCTib = getOrInsertGCTib(II); + Value *Param[5] = {II->getArgOperand(GCWriteStaticStruct::Dst), + II->getArgOperand(GCWriteStaticStruct::Size), + II->getArgOperand(GCWriteStaticStruct::Src), + II->getArgOperand(GCWriteStaticStruct::Size), GCTib}; + // Replace the original intrinsic with the runtime function. + replaceCallInst(Callee, Param, II); + break; + } + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + case Intrinsic::cj_array_copy_generic: { + Value *Param[6] = {II->getArgOperand(ArrayCopy::DstObj), + II->getArgOperand(ArrayCopy::DstPtr), + II->getArgOperand(ArrayCopy::Size), + II->getArgOperand(ArrayCopy::SrcObj), + II->getArgOperand(ArrayCopy::SrcPtr), + II->getArgOperand(ArrayCopy::Size)}; + // Replace the original intrinsic with the runtime function. + replaceCallInst(Callee, Param, II); + break; + } + default: + II->setCalledFunction(Callee); + break; + } + } + + void replaceCallInst(Function *Callee, ArrayRef Args, + IntrinsicInst *II) const { + IRBuilder<> Builder(II); + CallInst *NewCI = Builder.CreateCall(Callee, Args); + II->replaceAllUsesWith(NewCI); + II->eraseFromParent(); + } + +private: + Module &M; + LLVMContext &C; + CJStructTypeGCInfo &GCInfo; + DenseMap RTFuncMap; + Type *GCPtr; + Type *I8Ptr; + Type *I64; + Type *I32; + + StringRef getRuntimeFuncName(IntrinsicInst *II) { + Intrinsic::ID IID = II->getIntrinsicID(); + auto Itr = IntrinsicMap.find(IID); + assert(Itr != IntrinsicMap.end() && "Runtime Intrinsic don`t exist."); + return Itr->second; + } + + Function *getOrInsertRuntimeFunc(IntrinsicInst *II) { + StringRef Callee = getRuntimeFuncName(II); + assert(Callee != "" && "Callee don`t exist."); + auto Itr = RTFuncMap.find(Callee); + if (Itr != RTFuncMap.end()) + return Itr->second; + // add dstLen to runtime API + FunctionType *FuncType = nullptr; + const Triple TT(II->getModule()->getTargetTriple()); + auto isARM = TT.isARM(); + switch (II->getIntrinsicID()) { + case Intrinsic::cj_gcwrite_struct: { + Type *ParamType[5] = {GCPtr, GCPtr, I64, GCPtr, I64}; + if (isARM) + ParamType[2] = ParamType[4] = I32; + FuncType = FunctionType::get(Type::getVoidTy(C), ParamType, false); + break; + } + case Intrinsic::cj_gcread_static_struct: + case Intrinsic::cj_gcwrite_static_struct: { + Type *ParamType[5] = {I8Ptr, I64, I8Ptr, I64, I8Ptr}; + if (isARM) + ParamType[1] = ParamType[3] = I32; + FuncType = FunctionType::get(Type::getVoidTy(C), ParamType, false); + break; + } + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + case Intrinsic::cj_array_copy_generic: { + Type *ParamType[6] = {GCPtr, GCPtr, I64, GCPtr, GCPtr, I64}; + if (isARM) + ParamType[2] = ParamType[5] = I32; + FuncType = FunctionType::get(Type::getVoidTy(C), ParamType, false); + break; + } + default: + FuncType = II->getFunctionType(); + break; + } + Function *Func = + cast(M.getOrInsertFunction(Callee, FuncType).getCallee()); + RTFuncMap.insert({Callee, Func}); + return Func; + } + + Constant *getOrInsertGCTib(IntrinsicInst *II) const { + const MDNode *Metadata = II->getMetadata("AggType"); + assert(Metadata && "Missing AggType Metadata."); + assert(Metadata->getNumOperands() == 1 && + "AggType meta's size should be equal to 1!"); + + auto STName = + dyn_cast(Metadata->getOperand(0).get())->getString(); + auto *ST = StructType::getTypeByName(C, STName); + assert(ST && "AggType doesn't exsit."); + + Type *DstType = Type::getInt8PtrTy(C); + std::string BitMapName = STName.str(); + Constant *BitMapGV = M.getGlobalVariable(BitMapName + ".bitmap"); + if (BitMapGV == nullptr) { + auto &Info = GCInfo.getOrInsertTypeGCInfo(ST); + BitMapGV = + GCInfo.getOrInsertBitMap(Info.BMInfo.BMStr, DstType, BitMapName); + } else { + BitMapGV = ConstantExpr::getBitCast(BitMapGV, DstType); + } + assert(BitMapGV && "BitMapGV get or insert fail"); + return BitMapGV; + } +}; + +// Insert gc-phase check for write barriers. +class GCPhaseCheck { +public: + explicit GCPhaseCheck(Function &F) : C(F.getContext()) { + GCStateCheckFunc = F.getParent()->getFunction("GetGCPhase"); + assert(GCStateCheckFunc && "Has no GetGCPhase"); + }; + ~GCPhaseCheck() = default; + + void initBBInfo(Function &F) { + BBInfosMap.clear(); + Barriers.clear(); + BarrierCheckMap.clear(); + for (auto &BB : F) { + BBInfosMap[&BB] = BBInfo(); + } + } + + void setLastBarrier(CallBase *CI) { + BasicBlock *BB = CI->getParent(); + auto &Info = BBInfosMap[BB]; + if (Info.LastBarrier == nullptr) { + Info.LastBarrier = CI; + if (Info.HasCalls) { + createCheck(CI); + } + } + if (!Info.HasCalls) { + Info.BeforeCall = true; + } + } + + void setHasCall(BasicBlock *BB) { + auto &Info = BBInfosMap[BB]; + Info.HasCalls = true; + Info.LastBarrier = nullptr; + } + + bool isNullPointer(Value *V) { + if (isa(V)) { + return V->getValueID() == Value::ConstantPointerNullVal; + } + return false; + } + + bool fastBarrier(unsigned IID, CallBase *CI) { + switch (IID) { + // If Write barrier baseObj is null, we will replace to store. + // Others, inline to fastbarrier. + case Intrinsic::cj_gcwrite_ref: + case Intrinsic::cj_gcwrite_struct: { + Value *BaseObj = getBaseObj(CI); + if (isNullPointer(BaseObj)) { + IRBuilder<> Builder(CI); + createStoreOrMems(CI, Builder); + CI->eraseFromParent(); + return true; + } + break; + } + // non-constant ordering. + case Intrinsic::cj_atomic_store: + case Intrinsic::cj_atomic_swap: + if (!isa(getAtomicOrder(CI))) { + return false; + } + break; + case Intrinsic::cj_atomic_compare_swap: + if (!isa(CI->getArgOperand(AtomicCompareSwap::SuccOrder)) || + !isa(CI->getArgOperand(AtomicCompareSwap::FailOrder))) { + return false; + } + break; + default: + break; + } + + setLastBarrier(CI); + Barriers.push_back(CI); + return true; + } + + void updateBBOut(BasicBlock *BB) { + BBInfo &Info = BBInfosMap[BB]; + if (Info.HasCalls && Info.LastBarrier != nullptr) { + Info.OutCheck = BarrierCheckMap[Info.LastBarrier]; + assert(Info.OutCheck && "out check don't generate."); + } + } + + void finishAll() { + updateBBInfo(); + replaceBarrier(); + } + + void createGCCheck(BasicBlock *InsertBB, BasicBlock *True, + BasicBlock *False) { + IRBuilder<> IRB(InsertBB); + CallInst *GCState = IRB.CreateCall(GCStateCheckFunc); + GCState->setCallingConv(CallingConv::CangjieGC); + auto CheckResult = IRB.CreateICmpSLE( + GCState, Constant::getIntegerValue(GCState->getType(), APInt(32, 8))); + IRB.CreateCondBr(CheckResult, True, False); + } + +private: + LLVMContext &C; + Function *GCStateCheckFunc = nullptr; + SmallVector Barriers; + SmallMapVector BBInfosMap; + SmallMapVector BarrierCheckMap; + + Instruction *createCheck(CallBase *CI) { + Instruction *CheckResult = BarrierCheckMap[CI]; + if (CheckResult) + return CheckResult; + IRBuilder<> Builder(CI); + // direct load from thread local if possible + CallInst *GCState = Builder.CreateCall(GCStateCheckFunc); + GCState->setCallingConv(CallingConv::CangjieGC); + // 8: related and equal to enum GCPhase::kGCPhaseInit. + CheckResult = dyn_cast(Builder.CreateICmpSLE( + GCState, Constant::getIntegerValue(GCState->getType(), APInt(32, 8)))); + BarrierCheckMap[CI] = CheckResult; + return CheckResult; + } + + Instruction *createPhi(BasicBlock *BB, + SmallVector &PhiValues) { + auto &Info = BBInfosMap[BB]; + unsigned IncomingSize = PhiValues.size(); + Type *CheckType = PhiValues[0]->getType(); + IRBuilder<> Builder(BB->getFirstNonPHI()); + PHINode *Phi = Builder.CreatePHI(CheckType, IncomingSize); + for (unsigned i = 0; i < PhiValues.size(); i++) { + Phi->addIncoming(PhiValues[i], Info.PreBBs[i]); + } + return Phi; + } + + void updateBBInfo() { + for (auto &Info : BBInfosMap) { + BasicBlock *CurBB = Info.first; + for (BasicBlock *Succ : successors(CurBB)) { + auto &BBInfo = BBInfosMap[Succ]; + BBInfo.PreBBs.push_back(CurBB); + } + } + } + + void resetLastCheckResult(Instruction *&CR, BasicBlock *CurBB, unsigned i) { + for (BasicBlock::iterator BBInst = CurBB->begin();;) { + if (Barriers[i] == &*BBInst) + break; + if (mayBeSafepoint(&*BBInst)) { + CR = nullptr; + break; + } + BBInst++; + } + } + + Instruction *createFastInstr(BasicBlock *TrueBranch, CallBase *CI, + const DebugLoc &CurDbg) { + IRBuilder<> Builder(TrueBranch->getTerminator()); + Instruction *NewInst = createStoreOrMems(CI, Builder); + if (NewInst != nullptr) { + NewInst->setDebugLoc(CurDbg); + } + return NewInst; + } + + void handleSuccPhi(CallBase *CI, BasicBlock *FalseBranch, Instruction *New, + BasicBlock *TrueBranch, BasicBlock *Succ) { + Intrinsic::ID IID = CI->getIntrinsicID(); + if (IID != Intrinsic::cj_atomic_swap && + IID != Intrinsic::cj_atomic_compare_swap) { + return; + } + Value *Result = nullptr; + IRBuilder<> BuilderExtract(TrueBranch->getTerminator()); + if (IID == Intrinsic::cj_atomic_compare_swap) { + Result = BuilderExtract.CreateExtractValue(New, 1, "swapResult"); + } else { + Result = BuilderExtract.CreateIntToPtr( + New, Type::getInt8Ty(CI->getContext())->getPointerTo(1)); + } + IRBuilder<> Builder(Succ->getFirstNonPHI()); + PHINode *Phi = Builder.CreatePHI(CI->getType(), 2); + CI->replaceAllUsesWith(Phi); + Phi->addIncoming(CI, FalseBranch); + Phi->addIncoming(Result, TrueBranch); + } + // do the following conversion: + // call void @llvm.cj.gcwrite + // =====> + // br i1 CondVal, label %gcNoRunning, label %gcRunning + // gcRunning: + // call void @llvm.cj.gcwrite + // br label %storeFinish + // gcNoRunning: + // store xxx + // br label %storeFinish + // storeFinish: + // ... + BasicBlock *SplitFastPathAndSlowPath(CallBase *CurInst, BasicBlock *SplitBB, + Value *CondVal) { + const DebugLoc &CurDbg = CurInst->getDebugLoc(); + BasicBlock *FalseBranch = SplitBB->splitBasicBlock(CurInst, "gcRunning"); + BasicBlock *Succ = + FalseBranch->splitBasicBlock(CurInst->getNextNode(), "storeFinish"); + BasicBlock *TrueBranch = + BasicBlock::Create(C, "gcNoRunning", SplitBB->getParent(), Succ); + BranchInst::Create(Succ, TrueBranch); + Instruction *NewInst = createFastInstr(TrueBranch, CurInst, CurDbg); + + Instruction *OriginBr = SplitBB->getTerminator(); + IRBuilder<> BuilderBr(OriginBr); + BuilderBr.CreateCondBr(CondVal, TrueBranch, FalseBranch); + OriginBr->eraseFromParent(); + FalseBranch->getTerminator()->setDebugLoc(CurDbg); + TrueBranch->getTerminator()->setDebugLoc(CurDbg); + handleSuccPhi(CurInst, FalseBranch, NewInst, TrueBranch, Succ); + return Succ; + } + + void replaceBarrier() { + if (Barriers.empty()) + return; + // cal in state + for (auto &Info : BBInfosMap) { + // use pre bb's gc state value + auto &BBInfo = Info.second; + auto BB = Info.first; + calculateBBCheck(BB, BBInfo); + } + + for (unsigned i = 0; i < Barriers.size();) { + BasicBlock *CurBB = Barriers[i]->getParent(); + auto &Info = BBInfosMap[CurBB]; + Instruction *CR = Info.InCheck; + do { + if (!CR) { + CR = createCheck(Barriers[i]); + } + CallBase *CI = Barriers[i]; + CurBB = SplitFastPathAndSlowPath(CI, CurBB, CR); + i++; + if (i == Barriers.size() || CurBB != Barriers[i]->getParent()) { + break; + } + resetLastCheckResult(CR, CurBB, i); + } while (true); + } + } + + void calculateBBCheck(BasicBlock *BB, BBInfo &Info) { + if (Info.Visited || (Info.HasCalls && !Info.BeforeCall)) { + return; + } + Info.Visited = true; + SmallVector PhiValues; + for (auto CurBB : Info.PreBBs) { + auto &CurInfo = BBInfosMap[CurBB]; + if (!CurInfo.HasCalls) { + calculateBBCheck(CurBB, CurInfo); + } + // no barrier, and pre BB has no last barrier + if (CurInfo.OutCheck == nullptr) { + PhiValues.clear(); + break; + } else { + PhiValues.push_back(CurInfo.OutCheck); + } + } + // update outcheck + if (PhiValues.size() > 1) { + Info.InCheck = createPhi(BB, PhiValues); + } else if (PhiValues.size() == 1) { + Info.InCheck = PhiValues[0]; + } else if (!Info.HasCalls && Info.LastBarrier != nullptr) { + Info.InCheck = createCheck(Info.LastBarrier); + } + if (!Info.HasCalls) { + Info.OutCheck = Info.InCheck; + } + } +}; + +class ReadBarrier { +public: + explicit ReadBarrier(Function &F) : M(F.getParent()), C(F.getContext()) { + const Triple TT(M->getTargetTriple()); + IsX86_64 = TT.getArch() == Triple::x86_64; + } + + ~ReadBarrier() = default; + + void readFastPath(CallInst *ReadBarrier, Value *RefFieldPtr, + uint64_t Order = 0) { + setBarrier(ReadBarrier); + // %0 = load i8 addrspace1*, i8 addrspace1* addrspace1* %3 + // %1 = ptrtoint i8 addrspace1* %0 to i64 + IRBuilder<> Builder(ReadInst); + LoadInst *Load = loadTaggedPointer(Builder, RefFieldPtr, Order); + Instruction *PtrToInt = + cast(Builder.CreatePtrToInt(Load, Type::getInt64Ty(C))); + PtrToInt->setDebugLoc(*Loc); + Value *CmpEQ = cmpTaggedPointer(PtrToInt, Builder); + splitFastPathAndSlowPath(ReadInst->getParent(), CmpEQ, Load); + } + + // insert a load from RefFieldPtr: + // %val = load i8 addrspace1*, i8 addrspace1* addrspace1* %RefFieldPtr + LoadInst *loadTaggedPointer(IRBuilder<> &Builder, Value *RefFieldPtr, + uint64_t Order) { + LoadInst *Load = Builder.CreateLoad(DstTy, RefFieldPtr); + Load->setDebugLoc(*Loc); + if (Order) { + Load->setAtomic( + (AtomicOrdering)(Order + (uint64_t)AtomicOrdering::Monotonic)); + } + return Load; + } + + // %tag = lshr i64 %Ptr, 48 + // %ret = icmp eq i64 %tag, 0 + Value *cmpTaggedPointer(Value *TagPtr, IRBuilder<> &Builder) { + Value *Tag = Builder.CreateLShr(TagPtr, (uint64_t)48); + cast(Tag)->setDebugLoc(*Loc); + Value *CmpEQ = Builder.CreateICmpEQ( + Tag, ConstantInt::get(Type::getInt64Ty(C), (uint64_t)0)); + cast(CmpEQ)->setDebugLoc(*Loc); + return CmpEQ; + } + + // preBB: + // %Cond = icmp eq i64 %tag, 0 + // br i1 %Cond, label %gcNoMarked label %gcMarked + // gcNoMarked: + // %val1 = %val + // br label %loadFinish + // gcMarked: + // %val2 = call @llvm.cj.gcread.ref + // br label %loadFinish + // loadFinish: + // %val = phi [%val1, gcNoMarked], [%val2, gcMarked] + void splitFastPathAndSlowPath(BasicBlock *SplitBB, Value *Condition, + Instruction *LoadVal) { + BasicBlock *FalseBranch = SplitBB->splitBasicBlock(ReadInst, "gcMarked"); + BasicBlock *Succ = + FalseBranch->splitBasicBlock(ReadInst->getNextNode(), "loadFinish"); + BasicBlock *TrueBranch = + BasicBlock::Create(C, "gcNoMarked", SplitBB->getParent(), Succ); + BranchInst::Create(Succ, TrueBranch); + Instruction *OriginBr = SplitBB->getTerminator(); + IRBuilder<> BuilderBr(OriginBr); + BuilderBr.CreateCondBr(Condition, TrueBranch, FalseBranch); + OriginBr->eraseFromParent(); + TrueBranch->getTerminator()->setDebugLoc(*Loc); + FalseBranch->getTerminator()->setDebugLoc(*Loc); + handleSuccPhi(FalseBranch, LoadVal, TrueBranch, Succ); + return; + } + + PHINode *handleSuccPhi(BasicBlock *FalseBranch, Value *FastInst, + BasicBlock *TrueBranch, BasicBlock *Succ) { + IRBuilder<> Builder(Succ->getFirstNonPHI()); + PHINode *Phi = Builder.CreatePHI(DstTy, 2); + ReadInst->replaceAllUsesWith(Phi); + Phi->addIncoming(ReadInst, FalseBranch); + Phi->addIncoming(FastInst, TrueBranch); + return Phi; + } + + void setBarrier(CallInst *CI) { + ReadInst = CI; + DstTy = CI->getType(); + Loc = &CI->getDebugLoc(); + } + +private: + Module *M; + LLVMContext &C; + bool IsX86_64; + CallInst *ReadInst = nullptr; + Type *DstTy = nullptr; + const DebugLoc *Loc = nullptr; +}; + +/// CJBarrierLowering - This pass rewrites calls to the llvm.gcread or +/// llvm.gcwrite intrinsics, replacing them with simple loads and stores as +/// directed by the GCStrategy. It also performs automatic root initialization +/// and custom intrinsic lowering. +class CJBarrierLowering : public FunctionPass { + bool isCJBarrier(Instruction *I); + Instruction *createReadFastPath(BasicBlock *TrueBranch, Instruction *PtrToInt, + const DebugLoc &Loc); + PHINode *handleSuccPhi(Instruction *SlowInst, BasicBlock *FalseBranch, + Instruction *FastInst, BasicBlock *TrueBranch, + BasicBlock *Succ); + void splitFastPathAndSlowPath(CallBase *ReadInst, BasicBlock *SplitBB, + Value *Condition, Instruction *PtrToInt, + const DebugLoc &Loc); + void writeBarrierFastPath(Function &F, SetVector &Barriers); + void readBarrierFastPath(Function &F, SetVector &Barriers); + void doLowering(Function &F); + +public: + static char ID; + CodeGenOpt::Level OptLevel; + + CJBarrierLowering(CodeGenOpt::Level OptLevel = CodeGenOpt::Default); + StringRef getPassName() const override; + void getAnalysisUsage(AnalysisUsage &AU) const override; + + bool doInitialization(Module &M) override; + bool runOnFunction(Function &F) override; +}; +} // namespace + +// ----------------------------------------------------------------------------- + +INITIALIZE_PASS_BEGIN(CJBarrierLowering, "cj-barrier-lowering", + "Cangjie Barrier Lowering", false, false) +INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) +INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) +INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass) +INITIALIZE_PASS_END(CJBarrierLowering, "cj-barrier-lowering", + "Cangjie Barrier Lowering", false, false) + +char CJBarrierLowering::ID = 0; +char &llvm::CJBarrierLoweringID = CJBarrierLowering::ID; + +FunctionPass *llvm::createCJBarrierLoweringPass(CodeGenOpt::Level OptLevel) { + return new CJBarrierLowering(OptLevel); +} + +CJBarrierLowering::CJBarrierLowering(CodeGenOpt::Level OptLevel) + : FunctionPass(ID), OptLevel(OptLevel) { + initializeCJBarrierLoweringPass(*PassRegistry::getPassRegistry()); +} + +StringRef CJBarrierLowering::getPassName() const { + return "Cangjie Lower Garbage Collection Barrier Function"; +} + +void CJBarrierLowering::getAnalysisUsage(AnalysisUsage &AU) const { + FunctionPass::getAnalysisUsage(AU); + if (EnableGCStateLoop) { + AU.addRequired(); + AU.addPreserved(); + AU.addRequired(); + AU.addPreserved(); + AU.addRequired(); + AU.addPreserved(); + } +} + +/// doInitialization - If this module uses the GC intrinsics, find them now. +bool CJBarrierLowering::doInitialization(Module &M) { + Function *GCStateCheckFunc = M.getFunction("GetGCPhase"); + if (GCStateCheckFunc != nullptr) { // have GetGCPhase in module + return false; + } + LLVMContext &C = M.getContext(); + FunctionType *FuncType = FunctionType::get(Type::getInt32Ty(C), false); + GCStateCheckFunc = + cast(M.getOrInsertFunction("GetGCPhase", FuncType).getCallee()); + GCStateCheckFunc->addFnAttr(Attribute::get(C, "gc-leaf-function")); + GCStateCheckFunc->addFnAttr(Attribute::get(C, "cj-runtime")); + GCStateCheckFunc->setCallingConv(CallingConv::CangjieGC); + GCStateCheckFunc->setUnnamedAddr(GlobalValue::UnnamedAddr::Local); + return false; +} + +static bool fastBarrierInline(Function &F, GCPhaseCheck &GCPhase) { + bool Changed = false; + GCPhase.initBBInfo(F); + for (BasicBlock &BB : F) { + for (auto It = BB.begin(), E = BB.end(); It != E;) { + auto *CI = dyn_cast(&*It++); + if (!CI) { + continue; + } + if (mayBeSafepoint(CI)) { + GCPhase.setHasCall(&BB); + } + unsigned IID = CI->getIntrinsicID(); + switch (IID) { + default: + break; + case Intrinsic::cj_gcwrite_ref: + case Intrinsic::cj_gcwrite_static_ref: + case Intrinsic::cj_gcwrite_struct: + case Intrinsic::cj_gcwrite_static_struct: + if (DisableGCSupport || EnableSafepointOnly) { + IRBuilder<> Builder(CI); + createStoreOrMems(CI, Builder); + CI->eraseFromParent(); + Changed = true; + } else { + Changed |= GCPhase.fastBarrier(IID, CI); + } + continue; + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + case Intrinsic::cj_atomic_store: + Changed |= GCPhase.fastBarrier(IID, CI); + continue; + case Intrinsic::cj_atomic_swap: + case Intrinsic::cj_atomic_compare_swap: + // Do not implement fastpath currently now! + continue; + } + } + GCPhase.updateBBOut(&BB); + } + GCPhase.finishAll(); + return Changed; +} + +static SmallVector GCWriteBarriers; + +// on safepoint and contain barriers's loop +static bool containBarrier(Loop &L, bool &containSafepoint) { + if (!L.isInnermost() || L.getNumBlocks() > MaxLoopBlock || + !L.getLoopPreheader()) { + return false; + } + GCWriteBarriers.clear(); + BasicBlock *Latch = L.getLoopLatch(); + assert(Latch && "Simplified loops only have one latch!"); + if (Latch->getTerminator()->getMetadata(ClonedPinLoopTag)) { + return false; + } + Value *BP = nullptr; + for (auto *LoopBB : L.blocks()) { + for (auto It = LoopBB->begin(), E = LoopBB->end(); It != E;) { + auto *CI = dyn_cast(&*It++); + if (!CI) { + continue; + } + containSafepoint |= mayBeSafepoint(CI); + unsigned IID = CI->getIntrinsicID(); + switch (IID) { + default: + break; + case Intrinsic::cj_gcwrite_ref: + BP = getBaseObj(CI); + break; + case Intrinsic::cj_gcwrite_static_ref: + BP = getPointerArg(CI); + break; + case Intrinsic::cj_gcwrite_struct: + BP = getBaseObj(CI); + break; + case Intrinsic::cj_gcwrite_static_struct: + BP = getDest(CI); + break; + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + BP = CI->getArgOperand(ArrayCopy::DstObj); + break; + } + if (BP == nullptr) { + continue; + } + GCWriteBarriers.push_back(CI); + BP = nullptr; + } + } + return GCWriteBarriers.size() != 0; +} + +static Loop &cloneLoop(Loop *L, Function &F, LoopInfo &LI) { + ValueToValueMapTy Map; + SmallVector Blocks; + for (BasicBlock *BB : L->getBlocks()) { + BasicBlock *Clone = CloneBasicBlock(BB, Map, Twine(".pin"), &F); + Blocks.push_back(Clone); + Map[BB] = Clone; + } + auto GetClonedValue = [&Map](Value *V) { + assert(V && "null values not in domain!"); + auto It = Map.find(V); + if (It == Map.end()) { + return V; + } + return static_cast(It->second); + }; + auto *ClonedLatch = cast(GetClonedValue(L->getLoopLatch())); + LLVMContext &Ctx = L->getHeader()->getContext(); + ClonedLatch->getTerminator()->setMetadata(ClonedPinLoopTag, + MDNode::get(Ctx, {})); + for (unsigned i = 0, e = Blocks.size(); i != e; ++i) { + BasicBlock *ClonedBB = Blocks[i]; + BasicBlock *OriginalBB = L->getBlocks()[i]; + for (Instruction &I : *ClonedBB) { + RemapInstruction(&I, Map, + RF_NoModuleLevelChanges | RF_IgnoreMissingLocals); + } + for (auto *SBB : successors(OriginalBB)) { + if (L->contains(SBB)) { + continue; + } + for (PHINode &PN : SBB->phis()) { + Value *OldIncoming = PN.getIncomingValueForBlock(OriginalBB); + PN.addIncoming(GetClonedValue(OldIncoming), ClonedBB); + } + } + } + Loop &New = *LI.AllocateLoop(); + if (L->getParentLoop()) { + L->getParentLoop()->addChildLoop(&New); + } else { + LI.addTopLevelLoop(&New); + } + for (auto *BB : L->blocks()) { + if (LI.getLoopFor(BB) == L) { + New.addBasicBlockToLoop(cast(Map[BB]), LI); + } + } + return New; +} + +static void handleGCStateLoop(Loop &New, Loop &L, GCPhaseCheck &GCPhase) { + BasicBlock *PreHeader = L.getLoopPreheader(); + BranchInst *PreTerm = dyn_cast(PreHeader->getTerminator()); + assert(PreTerm != nullptr && "PreHeader's trem inst is not branch inst"); + BasicBlock *Header = L.getHeader(); + assert(!PreTerm->isConditional() && "PreHeader's term has conditional"); + GCPhase.createGCCheck(PreHeader, Header, New.getHeader()); + PreTerm->eraseFromParent(); +} + +static void replaceBarriers() { + for (unsigned Index = 0; Index < GCWriteBarriers.size(); Index++) { + CallBase *CI = GCWriteBarriers[Index]; + IRBuilder<> IRB(CI); + Instruction *Inst = createStoreOrMems(CI, IRB); + if (Inst != nullptr) { + Inst->setDebugLoc(CI->getDebugLoc()); + CI->eraseFromParent(); + } + } +} + +static void checkLoopBarrier(Function &F, LoopInfo &LI, DominatorTree &DT, + ScalarEvolution &SE, GCPhaseCheck &GCPhase) { + if (LI.empty()) { + return; + } + + for (auto L : LI) { + simplifyLoop(L, &DT, &LI, &SE, nullptr, nullptr, false); + formLCSSARecursively(*L, DT, &LI, &SE); + } + + SmallPriorityWorklist Worklist; + appendLoopsToWorklist(LI, Worklist); + + while (!Worklist.empty()) { + Loop *L = Worklist.pop_back_val(); + bool containSafepoint = false; + if (!containBarrier(*L, containSafepoint)) { + continue; + } + if (!containSafepoint && EnableGCStateLoop) { + Loop &New = cloneLoop(L, F, LI); + handleGCStateLoop(New, *L, GCPhase); + replaceBarriers(); + DT.recalculate(F); + formLCSSARecursively(New, DT, &LI, &SE); + simplifyLoop(&New, &DT, &LI, &SE, nullptr, nullptr, true); + formLCSSARecursively(*L, DT, &LI, &SE); + simplifyLoop(L, &DT, &LI, &SE, nullptr, nullptr, true); + } + } +} + +void CJBarrierLowering::writeBarrierFastPath(Function &F, + SetVector &Barriers) { + if (!EnableGCPhase || CangjieJIT) + return; + + GCPhaseCheck GCPhase(F); + if (EnableGCStateLoop) { + auto &LI = getAnalysis().getLoopInfo(); + auto &DT = getAnalysisIfAvailable()->getDomTree(); + auto &SE = getAnalysis().getSE(); + checkLoopBarrier(F, LI, DT, SE, GCPhase); + } + fastBarrierInline(F, GCPhase); +} + +// do the following conversion: +// %val = call void @llvm.cj.gcread.ref +// =====> +// %0 = load i8 addrspace1*, i8 addrspace1* addrspace1* %RefFieldPtr +// %1 = ptrtoint i8 addrspace1* %0 to i64 +// %2 = lshr i64 %1, 48 +// %3 = icmp eq i64 %2, 0 +// br i1 %3, label %gcNoMarked label %gcMarked +// gcNoMarked: +// %val1 = and %0, 0x0000ffffffffffff +// br label %loadFinish +// gcMarked: +// %val2 = call @llvm.cj.gcread.ref +// br label %loadFinish +// loadFinish: +// %val = phi [%val1, gcNoMarked], [%val2, gcMarked] +void CJBarrierLowering::readBarrierFastPath(Function &F, + SetVector &Barriers) { + if (!EnableTaggedPointer || CangjieJIT) + return; + + ReadBarrier RB(F); + for (CallInst *CI : Barriers) { + unsigned ID = CI->getIntrinsicID(); + if (ID == Intrinsic::cj_gcread_ref || + ID == Intrinsic::cj_gcread_static_ref) { + RB.readFastPath(CI, getPointerArg(CI)); + continue; + } + if (ID == Intrinsic::cj_atomic_load) { + if (auto AO = dyn_cast(getAtomicOrder(CI))) { + RB.readFastPath(CI, CI->getArgOperand(AtomicLoad::Field), + AO->getZExtValue()); + } + } + } +} + +void CJBarrierLowering::doLowering(Function &F) { + Module *M = F.getParent(); + CJStructTypeGCInfo GCInfo(*M); + BarrierMaker GCBarrier(*M, GCInfo); + + for (BasicBlock &BB : F) { + for (Instruction &I : llvm::make_early_inc_range(BB)) { + if (isCJBarrier(&I)) + GCBarrier.replaceInstWithBarrier(cast(&I)); + } + } +} + +bool CJBarrierLowering::isCJBarrier(Instruction *I) { + IntrinsicInst *CI = dyn_cast(I); + if (!CI) + return false; + + switch (CI->getIntrinsicID()) { + default: + return false; + case Intrinsic::cj_gcwrite_ref: + case Intrinsic::cj_gcwrite_struct: + case Intrinsic::cj_gcwrite_static_ref: + case Intrinsic::cj_gcwrite_static_struct: + case Intrinsic::cj_gcread_ref: + case Intrinsic::cj_gcread_weakref: + case Intrinsic::cj_gcread_struct: + case Intrinsic::cj_gcread_static_ref: + case Intrinsic::cj_gcread_static_struct: + case Intrinsic::cj_copy_struct_field: + case Intrinsic::cj_atomic_store: + case Intrinsic::cj_atomic_load: + case Intrinsic::cj_atomic_swap: + case Intrinsic::cj_atomic_compare_swap: + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + case Intrinsic::cj_array_copy_generic: + case Intrinsic::cj_assign_generic: + case Intrinsic::cj_gcwrite_generic: + case Intrinsic::cj_gcread_generic: + case Intrinsic::cj_gcwrite_generic_payload: + return true; + } +} + +static void replaceFastFunc(Function &F, GCStatepointInst *CI, + StringRef FuncName) { + Module *M = F.getParent(); + Function *Func = M->getFunction(FuncName); + if (Func == nullptr) { + Function *Callee = CI->getActualCalledFunction(); + Func = M->declareCJRuntimeFunc(FuncName, Callee->getFunctionType(), true); + Func->setLinkage(GlobalValue::InternalLinkage); + Func->addFnAttr(Attribute::NoInline); + Func->addFnAttr("cj-fast-new-obj"); + BasicBlock *BB = BasicBlock::Create(M->getContext(), "entry", Func); + IRBuilder<> IRB(BB); + auto Ret = IRB.CreateCall(Callee, {Func->getArg(0), Func->getArg(1)}); + IRB.CreateRet(Ret); + } + CI->setArgOperand(GCStatepointInst::CalledFunctionPos, Func); +} + +static bool doNewFastPath(Function &F, SetVector &NewObjs) { + if (CangjieJIT) + return false; + for (GCStatepointInst *CI : NewObjs) { + Function *Callee = CI->getActualCalledFunction(); + if (Callee->getName().equals("CJ_MCC_NewObject")) { + replaceFastFunc(F, CI, NewObjFastStr); + } else { + replaceFastFunc(F, CI, NewObjFinalizerFastStr); + } + } + return true; +} + +static bool isNewObj(Instruction *I) { + if (auto CI = dyn_cast(I)) { + Function *Callee = CI->getActualCalledFunction(); + if (Callee && Callee->getName().isCangjieNewObjFunction()) { + return true; + } + } + return false; +} + +bool CJBarrierLowering::runOnFunction(Function &F) { + // Quick exit for functions that do not use Cangjie GC. + if (!F.hasCangjieGC()) + return false; + + const Triple TT(F.getParent()->getTargetTriple()); + if (TT.isARM()){ + EnableTaggedPointer = false; + EnableGCPhase = false; + EnableGCFastPath = false; + } + + bool Changed = false; + SetVector Barriers; + SetVector News; + + for (BasicBlock &BB : F) { + for (Instruction &I : llvm::make_early_inc_range(BB)) { + if (isCJBarrier(&I)) + Barriers.insert(cast(&I)); + if (EnableGCFastPath && isNewObj(&I)) + News.insert(cast(&I)); + } + } + + if (!News.empty()) { + Changed = doNewFastPath(F, News); + } + + if (Barriers.empty()) { + return Changed; + } + + if (OptLevel != CodeGenOpt::None) { + writeBarrierFastPath(F, Barriers); + readBarrierFastPath(F, Barriers); + } + doLowering(F); + return true; +} diff --git a/llvm/lib/CodeGen/CJMetadata.cpp b/llvm/lib/CodeGen/CJMetadata.cpp new file mode 100644 index 000000000..88fe140be --- /dev/null +++ b/llvm/lib/CodeGen/CJMetadata.cpp @@ -0,0 +1,874 @@ +//===- CJMetadata.cpp - Self-Defined CJMetadata Section ----------*- C++-*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file generates cangjie self-defined CJMetadata section. +// +//===----------------------------------------------------------------------===// +#include "llvm/CodeGen/CJMetadata.h" + +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/GCMetadata.h" +#include "llvm/CodeGen/StackMaps.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCSectionCOFF.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { +static cl::opt NoStackTraceInfo("no-stacktrace-info", cl::init(false), + cl::NotHidden, + cl::desc("disable cj stack trace info")); +enum class StackTraceFormat { Simple, Default, All }; +static cl::opt StackTraceFormatFlag( + "stack-trace-format", cl::init(StackTraceFormat::Default), cl::NotHidden, + cl::desc("The format of cj stack trace"), + cl::values(clEnumValN(StackTraceFormat::Simple, "simple", + "with only filename and linenumber"), + clEnumValN(StackTraceFormat::Default, "default", + "without generic info"), + clEnumValN(StackTraceFormat::All, "all", "with all info"))); + +extern cl::opt DisableGCSupport; +extern cl::opt EnableBarrierOnly; +extern cl::opt EnableSafepointOnly; +extern cl::opt EnableStackGrow; + +// StrPoolDictSplitSize is defined through elapsed time test on dict loading +// and string decoding. The maximum size of 5000 can keep the elapsed time below +// 1 millisecond, which is a desired cost. When the string pool dict size +// is between 1000 and 5000, the split size will be correspondingly calculated +// by function getStrPoolDictSplitSize(). +static uint32_t StrPoolDictSplitMaxSize = 5000; +static uint32_t StrPoolDictSplitMinSize = 1000; + +namespace { +const std::string StrPool(".Lstr_pool."); +const std::string StrPoolDict(".Lstr_pool_dict."); +} // namespace +CJMetadataInfo::CJMetadataInfo(AsmPrinter &AP, StackMaps &SM) + : AP(AP), SM(SM), OS(*AP.OutStreamer), Context(AP.OutContext), + TT(AP.TM.getTargetTriple()) {} + +void processStructTypeOffsets(StructType *ST, SmallVectorImpl &Refs, + const DataLayout &DL, const GCStrategy *GS, + uint64_t BaseOff) { + for (uint32_t STNum = 0; STNum < ST->getNumElements(); STNum++) { + if (auto PT = dyn_cast(ST->getElementType(STNum))) { + if (*GS->isGCManagedPointer(PT)) { + Refs.push_back(DL.getStructLayout(ST)->getElementOffset(STNum) + + BaseOff); + } + } else if (auto EST = dyn_cast(ST->getElementType(STNum))) { + uint64_t DerivedOffset = + DL.getStructLayout(ST)->getElementOffset(STNum) + BaseOff; + processStructTypeOffsets(EST, Refs, DL, GS, DerivedOffset); + } + } +} + +void CJMetadataInfo::init() { + const MCObjectFileInfo *MOFI = Context.getObjectFileInfo(); + TD[SDKVersionIdx].TableSection = MOFI->getCJSDKVersionSection(); + TD[MethodInfoTableIdx].TableSection = MOFI->getCJMethodInfoSection(); + TD[GlobalInitFuncIdx].TableSection = MOFI->getCJGlobalInitFuncSection(); + TD[StringPoolDictTableIdx].TableSection = MOFI->getCJStringPoolDictSection(); + TD[StringPoolTableIdx].TableSection = MOFI->getCJStringPoolSection(); + TD[StackMapTableIdx].TableSection = MOFI->getCJStackMapSection(); + TD[GCTibTableIdx].TableSection = MOFI->getCJGCTibSection(); + TD[GCRootsTableIdx].TableSection = MOFI->getCJGCRootSection(); + TD[TypeInfoTableIdx].TableSection = MOFI->getTypeInfoSection(); + TD[TypeTemplateIdx].TableSection = MOFI->getCJTypeTemplateSection(); + TD[TypeFieldsIdx].TableSection = MOFI->getCJTypeFieldsSection(); + TD[MTableIdx].TableSection = MOFI->getCJMtableSection(); + TD[StaticGIIdx].TableSection = MOFI->getCJStaticGenericTISection(); + TD[GCFlagsIdx].TableSection = MOFI->getCJGCFlagsSection(); + TD[ReflectPkgInfoIdx].TableSection = MOFI->getCJReflectPkgInfoSection(); + TD[ReflectGVInfoIdx].TableSection = MOFI->getCJReflectGVSection(); + TD[ReflectGIIdx].TableSection = MOFI->getCJReflectGenericTISection(); + TD[InnerTypeExtensionsIdx].TableSection = + MOFI->getCJInnerTypeExtensionsSection(); + TD[OuterTypeExtensionsIdx].TableSection = + MOFI->getCJOuterTypeExtensionsSection(); + String2Symbol.clear(); + SplitedMangledStr.Items.clear(); + StrPoolDictOffsetsSym = Context.getOrCreateSymbol(".Lstr_pool_dict_offsets"); + StrPoolIdx = 0; + FuncPtrSize = SM.isARM() ? 4 : 8; // method pc size, arm: 4 byte, other: 8 byte +} + +void CJMetadataInfo::recordKlass(const GlobalVariable *GV) { + if (!GV->hasInitializer()) { + ExternalTITable.push_back(GV); // external , undef classmeta + return; + } + InternalTITable.push_back(GV); +} + +void CJMetadataInfo::recordGlobalVariable(const GlobalVariable *GV) { + if (!GV->isCJMeta()) + return; + + auto HandleGVAttribute = [this](const GlobalVariable *GV) -> void { + if (GV->hasAttribute("CFileReflect")) { + if (GV->getName().endswith(".packageInfo")) { + ReflectPkgInfoGV.push_back(GV); + } else if (GV->getName().endswith(".reflectStr")) { + ReflectStrGV.push_back(GV); + } else { + ReflectOtherGV.push_back(GV); + } + return; + } + + if (GV->isCJTypeTemplate()) { + TypeTemplateGVTable.push_back(GV); + return; + } + + if (GV->isCJReflectGeneticTI()) { + ReflectGeneticTI.push_back(GV); + return; + } + + if (GV->isCJTypeInfo()) { + recordKlass(GV); + return; + } + + if (GV->isCJTypeName() || GV->isCJTIOffsets() || GV->isCJTITypeArgs() || + GV->isCJTIFields() || GV->isCJTTFieldsFns() || + GV->isCJReflectUpperBounds() || GV->isCJFunctableTable()) { + TIFieldsTable.push_back(GV); + return; + } + + if (GV->isCJMTable()) { + MTableTable.push_back(GV); + return; + } + + if (GV->isCJGCTib()) { + GCTibTable.push_back(GV); + return; + } + + if (GV->isCJStaticGenericTI()) { + assert(GV->hasInitializer() && "StaticGenericTI has no Initializer!"); + const Constant *C = GV->getInitializer(); + for (unsigned Idx = 0; Idx < C->getNumOperands(); Idx++) + StaticGenericTI.push_back(cast(C->getOperand(Idx))); + return; + } + + if (GV->isCJInnerTypeExtensions()) { + assert(GV->hasInitializer()); + InnerTypeExtensions.push_back(GV); + return; + } + + if (GV->isCJOuterTypeExtensions()) { + assert(GV->hasInitializer()); + auto *C = GV->getInitializer(); + for (unsigned I = 0; I < C->getNumOperands(); ++I) + OuterTypeExtensions.push_back(cast(C->getOperand(I))); + return; + } + }; + + HandleGVAttribute(GV); + + if (!GV->hasExactDefinition() && AP.TM.getRelocationModel() == Reloc::PIC_ && + !GV->hasWeakODRLinkage()) + return; + + // Func can have different GC Strategy, but GV has only one GC Strategy. + GCStrategy *GS = AP.getAnalysisIfAvailable()->getGCStrategy( + StringRef("cangjie")); + + // GCRoots + if (auto PT = dyn_cast(GV->getValueType())) { + if (*GS->isGCManagedPointer(PT)) { + GCRootTable.push_back(getGVRefSymbol(GV)); + } + } + + // also stack map use. + if (StructType *ST = dyn_cast(GV->getValueType())) { + // 8: Initial size of RefOffsets. + SmallVector RefOffsets; + processStructTypeOffsets(ST, RefOffsets, GV->getParent()->getDataLayout(), + GS, 0); + if (RefOffsets.size() == 0) + return; + + const MCExpr *GVRef = getGVRefSymbol(GV); + for (const auto Off : RefOffsets) { + const MCExpr *GVOff = MCBinaryExpr::createAdd( + GVRef, MCConstantExpr::create(Off, Context), Context); + GCRootTable.push_back(GVOff); + } + } +} + +void CJMetadataInfo::recordMetadata() { + for (const auto &G : M->globals()) + recordGlobalVariable(&G); + + recordExternalMethod(); +} + +void CJMetadataInfo::recordExternalMethod() { + for (const auto &F : M->functions()) + if (F.isDeclaration() && F.hasCangjieGC()) + ExternalMethod.push_back(&F); +} + +void CJMetadataInfo::recordCurrentFunc() { + const Function &F = AP.MF->getFunction(); + if (!F.hasCangjieGC() || F.hasFnAttribute("leaf-function")) + return; + + // pc + methodinfo. stackmap symbol + if (F.hasComdat()) { + ComdatMethodTable.push_back( + std::make_tuple(AP.CurrentFnSym, AP.getFunctionBegin(), + AP.getFunctionEnd(), AP.MF->getFunctionNumber(), &F)); + } else if (F.hasFnAttribute("cjinit")) { + InitMethodTable.push_back( + std::make_tuple(AP.CurrentFnSym, AP.getFunctionBegin(), + AP.getFunctionEnd(), AP.MF->getFunctionNumber())); + } else { + MethodTable.push_back( + std::make_tuple(AP.CurrentFnSym, AP.getFunctionBegin(), + AP.getFunctionEnd(), AP.MF->getFunctionNumber())); + } +} + +const MCExpr *CJMetadataInfo::getGVRefSymbol(const GlobalVariable *GV) { + MCSymbol *GVSymbol = AP.TM.getSymbol(GV); + if (GV->hasExactDefinition()) { + MCSymbol *RefSym = + Context.getOrCreateSymbol(Twine(".LRef.") + GVSymbol->getName()); + return MCSymbolRefExpr::create(RefSym, Context); + } + return MCSymbolRefExpr::create(GVSymbol, Context); +} + +const MCExpr *CJMetadataInfo::getOrInsertStrPoolOffset(std::string &Str, + const MCSymbol *DescSym, + bool MachONeedNoOffset) { + auto Itr = String2Symbol.find(Str); + MCSymbol *StrPoolSym = nullptr; + if (Itr == String2Symbol.end()) { + StrPoolSym = + Context.getOrCreateSymbol(StrPool + std::to_string(StrPoolIdx++)); + String2Symbol[Str] = StrPoolSym; + } else { + StrPoolSym = Itr->second; + } + assert(StrPoolSym && ".Lstr_pool symbol should not be null"); + if (MachONeedNoOffset) { + return MCSymbolRefExpr::create(StrPoolSym, Context); + } + return MCBinaryExpr::createSub(MCSymbolRefExpr::create(StrPoolSym, Context), + MCSymbolRefExpr::create(DescSym, Context), + Context); +} + +uint64_t CJMetadataInfo::addMangledStr(std::string &Str) { + return SplitedMangledStr.getOrInsertIndex(Str) + 1; +} + +bool isCurrentCharDigit(char C) { return C >= '0' && C <= '9'; } + +void CJMetadataInfo::splitStr(std::string &Str, + std::vector &CompressedCode) { + if (Str.empty()) + return; + + uint32_t CurIdx = 0; + uint32_t StrLen = Str.length(); + std::string TmpStr; + while (CurIdx < StrLen) { + if (!isCurrentCharDigit(Str[CurIdx])) { + TmpStr += Str[CurIdx]; + CurIdx++; + continue; + } + if (!TmpStr.empty()) { + CompressedCode.push_back(addMangledStr(TmpStr)); + TmpStr.clear(); + } + bool NeedNestedSplit = false; + if (Str[CurIdx] == '0') { + NeedNestedSplit = true; + } + std::string NumStr; + while (CurIdx < StrLen && isCurrentCharDigit(Str[CurIdx])) { + NumStr += Str[CurIdx]; + ++CurIdx; + } + uint32_t IdentifyLen = atoi(NumStr.c_str()); + if (IdentifyLen == 0) { + CompressedCode.push_back(addMangledStr(NumStr)); + continue; + } + if (IdentifyLen > StrLen - CurIdx) { + CompressedCode.push_back(addMangledStr(NumStr)); + continue; + } + TmpStr = Str.substr(CurIdx, IdentifyLen); + CurIdx += IdentifyLen; + for (uint32_t i = 0; i < TmpStr.size(); i++) { + if (isCurrentCharDigit(TmpStr[i])) { + NeedNestedSplit = true; + break; + } + } + if (NeedNestedSplit) { + CompressedCode.push_back(addMangledStr(NumStr)); + splitStr(TmpStr, CompressedCode); + } else { + auto Comb = NumStr + TmpStr; + CompressedCode.push_back(addMangledStr(Comb)); + } + TmpStr.clear(); + continue; + } + if (!TmpStr.empty()) { + CompressedCode.push_back(addMangledStr(TmpStr)); + } +} + +// ULEB: The highest bit of each byte is used to indicate whether the encoding +// is complete, and the remaining 7 bits are used to represent the integer +// value. +void ulebEncodeSignleStr(uint64_t Val, std::string &Result) { + do { + uint8_t Byte = Val & 0x7f; + Val >>= 7; + if (Val != 0) { + Byte |= 0x80; + } + Result.push_back(Byte); + } while (Val != 0); +} + +std::string ulebEncode(std::vector &Strs) { + std::string StrCode; + for (auto StrIndex : Strs) { + ulebEncodeSignleStr(StrIndex, StrCode); + } + return StrCode; +} + +// Filter out the basic library functions when the exception stacktrace is +// dumped. +std::set FiltMangleNameSet = { + "user.main", + "cj_entry$", + "_CNat", + "rt$", +}; + +bool isNeedFilt(std::string MangleName) { + for (auto FiltMangleName : FiltMangleNameSet) { + if (MangleName.find(FiltMangleName) == 0) + return true; + } + return false; +} + +void CJMetadataInfo::emitStackTraceInfo(const MCSymbol *FuncSym, + const MCSymbol *DescSym) { + unsigned StrSize = 4; // string offset size, 4 bytes + if (NoStackTraceInfo) { + OS.emitIntValue(0, StrSize); + OS.emitIntValue(0, StrSize); + OS.emitIntValue(0, StrSize); + OS.emitIntValue(0, StrSize); + return; + } + + std::string MethodNameStr = FuncSym->getName().str(); + if (StackTraceFormatFlag == StackTraceFormat::Simple && + !isNeedFilt(MethodNameStr)) + MethodNameStr = ""; + // set default value "" for dir and fileName + std::string DirStr(""); + std::string FileNameStr(""); + + Function *Func = nullptr; + if (IsMachO) { + // 1: FuncSym name is _xxx in macos, so begin from 1 to get xxx. + Func = M->getFunction(FuncSym->getName().substr(1)); + } else { + Func = M->getFunction(FuncSym->getName()); + } + if (Func != nullptr) { + DISubprogram *SP = Func->getSubprogram(); + if (SP != nullptr) { + DIFile *File = SP->getFile(); + if (File != nullptr) { + DirStr = File->getDirectory().str(); + FileNameStr = File->getFilename().str(); + } + } + } + std::vector MethodCompressedCode; + splitStr(MethodNameStr, MethodCompressedCode); + std::string MethodNameStrIndex = ulebEncode(MethodCompressedCode); + + std::vector DirCompressedCode; + splitStr(DirStr, DirCompressedCode); + std::string DirStrIndex = ulebEncode(DirCompressedCode); + + std::vector FileCompressedCode; + splitStr(FileNameStr, FileCompressedCode); + std::string FileNameStrIndex = ulebEncode(FileCompressedCode); + + OS.emitValue(getOrInsertStrPoolOffset(MethodNameStrIndex, DescSym), StrSize); + OS.emitValue(getOrInsertStrPoolOffset(DirStrIndex, DescSym), StrSize); + OS.emitValue(getOrInsertStrPoolOffset(FileNameStrIndex, DescSym), StrSize); + + if (MethodCompressedCode.empty() && DirCompressedCode.empty() && + FileCompressedCode.empty()) { + OS.emitIntValue(0, 4); + } else { + OS.emitValue( + MCBinaryExpr::createSub( + MCSymbolRefExpr::create(StrPoolDictOffsetsSym, AP.OutContext), + MCSymbolRefExpr::create(DescSym, AP.OutContext), AP.OutContext), + 4); + } + return; +} + +void CJMetadataInfo::emitEHTableOffset(unsigned FuncNumber) { + MCSymbol *CurPCSymbol = Context.createTempSymbol(); + OS.emitLabel(CurPCSymbol); + MCSymbol *EHTableSymbol = + Context.lookupSymbol(Twine("CJ_except_table") + Twine(FuncNumber)); + if (EHTableSymbol != nullptr) { + if (IsMachO) { + const MCExpr *EHTableAddr = + MCSymbolRefExpr::create(EHTableSymbol, Context); + // 8: ehtable addr size, 8 bytes + OS.emitValue(EHTableAddr, 8); + } else { + const MCExpr *EHTableOffset = MCBinaryExpr::createSub( + MCSymbolRefExpr::create(EHTableSymbol, Context), + MCSymbolRefExpr::create(CurPCSymbol, Context), Context); + // 4: ehtable offset size, 4 bytes + OS.emitValue(EHTableOffset, 4); + } + } else { + // 4: ehtable offset size, 4 bytes + OS.emitIntValue(0, 4); + } +} + +void CJMetadataInfo::emitDatas(const MCSymbol *FuncName, + const MCSymbol *FuncBegin, + const MCSymbol *FuncEnd, unsigned FuncNumber) { + // Emit MethodInfo symbol + MCSymbol *DescSymbol = nullptr; + if (IsMachO) { + Metadata *MD = M->getModuleFlag("Cangjie_PACKAGE_ID"); + if (MD == nullptr) + report_fatal_error("There is not cangjie package id in module!"); + StringRef PACKAGEID = dyn_cast(MD)->getString(); + DescSymbol = Context.getOrCreateSymbol(".Lmethod_desc." + PACKAGEID + "." + + FuncName->getName()); + OS.emitSymbolAttribute(DescSymbol, MCSA_Global); + } else { + DescSymbol = + Context.getOrCreateSymbol(".Lmethod_desc." + FuncName->getName()); + } + OS.emitLabel(DescSymbol); + // Emit StackMap offset + MCSymbol *SMSymbol = + Context.lookupSymbol(".Lstack_map." + FuncName->getName()); + if (SMSymbol == nullptr) { + // 4: StackMap offset size, 4 bytes + OS.emitIntValue(0, 4); + } else { + const MCExpr *MethodStackMapOffset = MCBinaryExpr::createSub( + MCSymbolRefExpr::create(SMSymbol, Context), + MCSymbolRefExpr::create(DescSymbol, Context), Context); + OS.emitValue(MethodStackMapOffset, 4); + } + // Emit Function size. + if (LLVM_UNLIKELY(FuncBegin == nullptr)) { + // 4: FuncSize size, 4 bytes + OS.emitIntValue(0, 4); + } else { + const MCExpr *FuncSize = MCBinaryExpr::createSub( + MCSymbolRefExpr::create(FuncEnd, Context), + MCSymbolRefExpr::create(FuncBegin, Context), Context); + // 4: FuncSize size, 4 bytes + OS.emitValue(FuncSize, 4); + } + // Emit methodName/directory/filename symbol. + emitStackTraceInfo(FuncName, DescSymbol); + // Emit EHTable offset. + // Because the front is 64-bit aligned, it must be placed behind + // the emitstacktraceinfo. + emitEHTableOffset(FuncNumber); +} + +void CJMetadataInfo::emitMethodInfoTable() { + if (InitMethodTable.empty() && MethodTable.empty() && + ComdatMethodTable.empty()) + return; + + if (TT.isOSBinFormatMachO()) { + OS.switchSection(Context.getMachOSection("__TEXT", "__cjinit_func", 0, + SectionKind::getExecuteOnly())); + OS.emitLabel(Context.getOrCreateSymbol(".Lcjinit_end")); + } + + OS.switchSection(TD[MethodInfoTableIdx].TableSection); + if (TT.isOSBinFormatMachO()) + // 8: align size, 8 bytes + OS.emitValueToAlignment(8); + // Emit Method info section. + for (const auto &Method : MethodTable) { + // 0: FuncName symbol, 1 : FuncBegin symbol + emitDatas(std::get<0>(Method), std::get<1>(Method), + // 2: FuncEnd symbol, 3: funcNumber + std::get<2>(Method), std::get<3>(Method)); + } + // Emit cj-init Method info section. + for (const auto &Method : InitMethodTable) { + // 0: FuncName symbol, 1 : FuncBegin symbol + emitDatas(std::get<0>(Method), std::get<1>(Method), + // 2: FuncEnd symbol, 3: funcNumber + std::get<2>(Method), std::get<3>(Method)); + } + // Emit Comdat Method info section. + for (const auto &Method : ComdatMethodTable) { + const Function *F = std::get<4>(Method); + StringRef Group = F->getComdat()->getName(); + MCSection *CJComdatMethodInfoSection = nullptr; + if (TT.isOSBinFormatELF()) { + CJComdatMethodInfoSection = Context.getELFSection( + ".cjmetadata.methodinfo." + Group, ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE | ELF::SHF_GROUP, 0, Group, false); + } else if (TT.isOSBinFormatCOFF()) { + CJComdatMethodInfoSection = Context.getCOFFSection( + ".cjmthd.", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getReadOnly()); + } else if (TT.isOSBinFormatMachO()) { + CJComdatMethodInfoSection = Context.getMachOSection( + "__CJ_METADATA", "__cjmethodinfo_", 0, SectionKind::getReadOnly()); + } else { + report_fatal_error("unsupport object format!"); + } + OS.switchSection(CJComdatMethodInfoSection); + emitDatas(std::get<0>(Method), std::get<1>(Method), std::get<2>(Method), + std::get<3>(Method)); + } +} + +void CJMetadataInfo::emitGCRoots() { + if (GCRootTable.empty()) + return; + + OS.switchSection(TD[GCRootsTableIdx].TableSection); + + if (TT.isOSBinFormatMachO()) + // 8: align size, 8 bytes + OS.emitValueToAlignment(8); + + for (const auto GCRoot : GCRootTable) { + OS.emitValue(GCRoot, FuncPtrSize); + } +} + +void CJMetadataInfo::emitTypeInfo() { + for (const auto *TI : InternalTITable) + AP.emitGlobalVariable(TI); + for (const auto *TIFields : TIFieldsTable) + AP.emitGlobalVariable(TIFields); +} + +void CJMetadataInfo::emitTypeTemplate() { + for (const auto *TT : TypeTemplateGVTable) + AP.emitGlobalVariable(TT); +} + +void CJMetadataInfo::emitMTable() { + if (MTableTable.empty()) + return; + + for (const auto *MTable : MTableTable) { + AP.emitGlobalVariable(MTable); + } +} + +void CJMetadataInfo::emitGCFlags() { + OS.switchSection(TD[GCFlagsIdx].TableSection); + if (DisableGCSupport || EnableSafepointOnly) { + OS.emitIntValue(0, 1); // 1: width of uint + } else { + OS.emitIntValue(1, 1); + } + + if (DisableGCSupport || EnableBarrierOnly) { + OS.emitIntValue(0, 1); + } else { + OS.emitIntValue(1, 1); + } + + if (EnableStackGrow) { + OS.emitIntValue(1, 1); + } else { + OS.emitIntValue(0, 1); + } + OS.addBlankLine(); +} + +void CJMetadataInfo::emitReflectInfo() { + if (!ReflectPkgInfoGV.empty()) { + for (const auto *const PkgGV : ReflectPkgInfoGV) + AP.emitGlobalVariable(PkgGV); + } + if (!ReflectOtherGV.empty() || !ReflectStrGV.empty()) { + for (const auto *const OtherGV : ReflectOtherGV) + AP.emitGlobalVariable(OtherGV); + + for (const auto *const StrGV : ReflectStrGV) + AP.emitGlobalVariable(StrGV); + } +} + +void CJMetadataInfo::emitReflectGenericTI() { + for (const auto *RGI : ReflectGeneticTI) + AP.emitGlobalVariable(RGI); +} + +void CJMetadataInfo::emitGCTibTable() { + if (GCTibTable.empty()) + return; + + for (const auto *GCTib : GCTibTable) + AP.emitGlobalVariable(GCTib); +} + +void CJMetadataInfo::emitSubExpr(StringRef Label, const GlobalVariable *GV) { + MCSymbol *CurSymbol = Context.createTempSymbol(Label); + OS.emitLabel(CurSymbol); + const MCExpr *CurRefSym = MCSymbolRefExpr::create(CurSymbol, Context); + const MCExpr *GVRefSym = getGVRefSymbol(GV); + const MCExpr *Offset = nullptr; + if (IsMachO) { + // The subtraction(second operand) in the MAC-O address cannot be a private + // symbol. We use `gvSym - curSym` to perform the subtraction operation. + Offset = MCBinaryExpr::createSub(CurRefSym, GVRefSym, Context); + } else { + // Emit generic typeinfo offset: long xxx - generic_ti + Offset = MCBinaryExpr::createSub(GVRefSym, CurRefSym, Context); + } + OS.emitValue(Offset, 4); +} + +void CJMetadataInfo::emitInnerTypeExtensions() { + if (InnerTypeExtensions.empty()) + return; + + for (const auto *GV : InnerTypeExtensions) + AP.emitGlobalVariable(GV); +} + +void CJMetadataInfo::emitOuterTypeExtensions() { + if (OuterTypeExtensions.empty()) + return; + OS.switchSection(TD[OuterTypeExtensionsIdx].TableSection); + for (auto *EED : OuterTypeExtensions) + emitSubExpr("ext_eds", EED); +} + +void CJMetadataInfo::emitStaticGenericTI() { + if (StaticGenericTI.empty()) + return; + OS.switchSection(TD[StaticGIIdx].TableSection); + for (const auto *GenericTI : StaticGenericTI) + emitSubExpr("generic_ti", GenericTI); +} + +void CJMetadataInfo::emitGlobalInitFuncTable() { + if (InitMethodTable.empty() && MethodTable.empty()) + return; + + OS.switchSection(TD[GlobalInitFuncIdx].TableSection); + if (TT.isOSBinFormatMachO()) + // 8: align size, 8 bytes + OS.emitValueToAlignment(8); + NamedMDNode *PkgInitFuncMD = M->getNamedMetadata("pkg_init_func"); + std::set GlobalInitFuncNameSet; + std::string GlobalInitFuncName; + if (PkgInitFuncMD != nullptr) { + for (unsigned I = 0; I < PkgInitFuncMD->getNumOperands(); I++) { + MDTuple *PkgInitFuncTuple = + dyn_cast(PkgInitFuncMD->getOperand(I)); + GlobalInitFuncName = + dyn_cast(PkgInitFuncTuple->getOperand(0))->getString().str(); + if (IsMachO) + GlobalInitFuncName = "_" + GlobalInitFuncName; + GlobalInitFuncNameSet.insert(GlobalInitFuncName); + } + } + auto EmitData = [&](const MCSymbol *FuncSym, const MCSymbol *FuncBegin) { + std::string FuncName = FuncSym->getName().str(); + if (!GlobalInitFuncNameSet.count(FuncName)) + return; + + OS.emitSymbolValue(FuncBegin, FuncPtrSize); + if (IsMachO) { + FuncName.push_back('\0'); + OS.emitBytes(FuncName); + } + }; + for (auto &Method : InitMethodTable) + EmitData(std::get<0>(Method), std::get<1>(Method)); + + for (auto &Method : MethodTable) + EmitData(std::get<0>(Method), std::get<1>(Method)); + + OS.addBlankLine(); +} + +void CJMetadataInfo::emitSDKVersion() { + auto Version = M->getGlobalVariable("cj.sdk.version", true); + if (Version == nullptr || Version->isDeclaration()) + return; + + OS.switchSection(TD[SDKVersionIdx].TableSection); + + if (TT.isOSBinFormatMachO()) + // 8: align size, 8 bytes + OS.emitValueToAlignment(8); + + OS.emitValue(getGVRefSymbol(Version), FuncPtrSize); +} + +void CJMetadataInfo::emitStackMaps() { + OS.switchSection(TD[StackMapTableIdx].TableSection); + SM.emitCangjieStackMaps(OS); +} + +uint32_t getStrPoolDictSplitSize(uint32_t TotalSize) { + // 10: supposed that TotalSize = SplitSize * n and SplitSize = 10 * n. + uint32_t DesiredSize = std::sqrt(TotalSize / 10) * 10; + if (DesiredSize <= StrPoolDictSplitMinSize) + return StrPoolDictSplitMinSize; + + if (DesiredSize >= StrPoolDictSplitMaxSize) + return StrPoolDictSplitMaxSize; + + // 100: round the result to be a multiple of one hundred. + return DesiredSize / 100 * 100; +} + +void CJMetadataInfo::emitStringPoolDictTable() { + if (SplitedMangledStr.Items.empty()) + return; + + OS.switchSection(TD[StringPoolDictTableIdx].TableSection); + // 8: align size, 8 bytes + OS.emitValueToAlignment(8); + // Emit stack trace format flag. + OS.emitInt8(static_cast(StackTraceFormatFlag.getValue())); + + OS.emitLabel(StrPoolDictOffsetsSym); + // 4: string pool dict total size, 4 bytes + OS.emitIntValue(SplitedMangledStr.Items.size(), 4); + uint32_t StrPoolDictSplitSize = + getStrPoolDictSplitSize(SplitedMangledStr.Items.size()); + uint32_t NumDicts = + (SplitedMangledStr.Items.size() - 1) / StrPoolDictSplitSize + 1; + for (uint32_t I = 1; I <= NumDicts; ++I) { + MCSymbol *StrPoolDictIdxSym = + Context.getOrCreateSymbol(StrPoolDict + std::to_string(I)); + // 4: string pool dict offset, 4 bytes + OS.emitValue(MCBinaryExpr::createSub( + MCSymbolRefExpr::create(StrPoolDictIdxSym, Context), + MCSymbolRefExpr::create(StrPoolDictOffsetsSym, Context), + Context), + 4); + } + + uint32_t StrPoolDictIdx = 0; + for (size_t I = 0; I < SplitedMangledStr.Items.size(); ++I) { + // Split string pool dict by labels. + if (I % StrPoolDictSplitSize == 0) { + MCSymbol *StrPoolDictIdxSym = AP.OutContext.getOrCreateSymbol( + StrPoolDict + std::to_string(++StrPoolDictIdx)); + OS.emitLabel(StrPoolDictIdxSym); + } + std::string Str = SplitedMangledStr.Items[I]; + Str.append(","); + OS.emitBytes(StringRef(Str.c_str(), Str.length())); + } + OS.emitBytes(StringRef("\0", 1)); + OS.addBlankLine(); +} + +void CJMetadataInfo::emitStringPoolTable() { + if (String2Symbol.empty()) + return; + + // method name items + OS.switchSection(TD[StringPoolTableIdx].TableSection); + // 8: align size, 8 bytes + OS.emitValueToAlignment(8); + for (auto Itr = String2Symbol.begin(), ItrEnd = String2Symbol.end(); + Itr != ItrEnd; ++Itr) { + OS.emitLabel(Itr->second); + // append '\0' then emit stringRef + OS.emitBytes(StringRef(Itr->first.c_str(), Itr->first.length() + 1)); + } + OS.addBlankLine(); +} + +void CJMetadataInfo::emitCJMetadata(Module &M) { + CJMetadataInfo::M = &M; + recordMetadata(); + init(); + // RW Section + emitSDKVersion(); + emitStackMaps(); + emitMethodInfoTable(); + emitGlobalInitFuncTable(); + emitStringPoolDictTable(); + emitStringPoolTable(); + emitGCTibTable(); + emitGCRoots(); + emitTypeTemplate(); + emitTypeInfo(); + emitMTable(); + emitInnerTypeExtensions(); + emitOuterTypeExtensions(); + emitStaticGenericTI(); + emitGCFlags(); + emitReflectInfo(); + emitReflectGenericTI(); + OS.addBlankLine(); +} +} // namespace llvm diff --git a/llvm/lib/CodeGen/CJStackPointerInserter.cpp b/llvm/lib/CodeGen/CJStackPointerInserter.cpp new file mode 100644 index 000000000..846c6aea6 --- /dev/null +++ b/llvm/lib/CodeGen/CJStackPointerInserter.cpp @@ -0,0 +1,951 @@ +//===-- CJStackPointerInserter.cpp - Cangjie Insert Stack Pointers---------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file implements the High-performance stack-pointers analysis pass. For +// each statepoint machine instruction in the function, this pass calculates +// the set of stack pointers that are lived after the instruction (i.e., the +// instruction calculates the stack pointer that may be used after the +// instruction, these stack pointers may be in registers or stack slots). +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/CJStackPointerInserter.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/CodeGen/MachineDominators.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/IR/Instructions.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetMachine.h" +#include + +using namespace llvm; + +char CJStackPointerInserter::ID = 0; +char &llvm::CJStackPointerInserterID = CJStackPointerInserter::ID; +INITIALIZE_PASS_BEGIN(CJStackPointerInserter, "cangjie-stack-pointer-inserter", + "Cangjie Stack Pointers Inserter", false, false) +INITIALIZE_PASS_DEPENDENCY(UnreachableMachineBlockElim) +INITIALIZE_PASS_END(CJStackPointerInserter, "cangjie-stack-pointer-inserter", + "Cangjie Stack Pointers Inserter", false, false) + +namespace { + +// Output log information and terminate the program. +void Check(bool Condition, const Twine &Message) { + if (!Condition) { + dbgs() << "CJStackPointerInserter-Check: " << Message << "\n"; + report_fatal_error("Broken function found, compilation aborted!"); + } +} + +// Return true if is x86, and return false if is aarch64. +// Otherwise an error is reported. +bool isX86Triple(MachineFunction &MF) { + if (MF.getTarget().getTargetTriple().isX86()) { + return true; + } else if (MF.getTarget().getTargetTriple().isAArch64()) { + return false; + } else { + report_fatal_error("Unsupported target type!"); + } +} + +// Parse the pointer variable in the input parameter of the function. +void parseFuncArgPointers(MachineFunction &MF, FuncArgPointers &Data) { + Function &F = MF.getFunction(); + if (F.arg_empty()) + return; + + const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo(); + const bool IsWindows = MF.getTarget().getMCAsmInfo()->usesWindowsCFI(); + const std::vector RegList = TRI->getArgRegs(MF); + const unsigned MaxRegNum = RegList.size(); + unsigned RegIdx = 0; // the index of RegList + unsigned FIIdx = 1; // the Frame index number + unsigned FPNum = 0; // floating point number + unsigned MaxFPArg = 8; // xmm0-xmm7 in x86 and v0-v7 in aarch64 + if (IsWindows) { + MaxFPArg = 4; // xmm0-xmm3 in windows + } + + // Record the index of the parameter and exclude floating-point types. + for (unsigned i = 0; i < F.arg_size(); ++i) { + Argument *Arg = F.getArg(i); + if (Arg->hasStructRetAttr()) { + Check(i == 0, "The sret arg position is not 0."); + if (isX86Triple(MF)) { + MCRegister MCReg(RegList[RegIdx]); + Data.Regs.push_back(MCReg); + ++RegIdx; + if (IsWindows) { + // Windows registers are used based on parameter locations, that is, + // location 0:rcx/xmm0, 1:rdx/xmm1, 2:r8/xmm2, 3:r9/xmm3 + ++FPNum; + } + } else { // AArch64 + MCRegister X8Reg(TRI->getX8Register()); + Data.Regs.push_back(X8Reg); + } + continue; + } + + Type *ArgTy = Arg->getType(); + if (ArgTy->isVectorTy()) { + auto *VTy = llvm::dyn_cast(ArgTy); + uint64_t NumElements = VTy->getNumElements(); + uint64_t ElementBitSize = VTy->getElementType()->getPrimitiveSizeInBits(); + if ((NumElements * ElementBitSize) <= 128) { + ++FPNum; + if (FPNum > MaxFPArg) { + ++FIIdx; // push to stack when more than max float registers. + } else { + if (IsWindows) { + Check(RegIdx < MaxRegNum, "Register Idx caculate error in windows"); + ++RegIdx; + } + } + continue; + } + } + + if (ArgTy->isHalfTy() || ArgTy->isFloatTy() || ArgTy->isDoubleTy()) { + ++FPNum; + if (FPNum > MaxFPArg) { + ++FIIdx; // push to stack when more than max float registers. + } else { + if (IsWindows) { + Check(RegIdx < MaxRegNum, "Register Idx caculate error in windows"); + ++RegIdx; + } + } + continue; + } + + if (ArgTy->isPointerTy()) { + if (RegIdx < MaxRegNum) { + MCRegister MCReg(RegList[RegIdx]); + Data.Regs.push_back(MCReg); + } else { + Data.FIs.push_back(-FIIdx); + } + } + + // Parameters beyond the register range are stored in stack frames. + if (RegIdx < MaxRegNum) { + ++RegIdx; + if (IsWindows) { + Check(FPNum < MaxFPArg, "Float Register Idx caculate error in windows"); + ++FPNum; + } + } else { + ++FIIdx; + } + } +} + +class SPType { +public: + enum TypeID { + RegTyID = 0, + SlotTyID, + }; + + SPType(TypeID ID, int SubclassData) : ID(ID), SubclassData(SubclassData) {} + + TypeID getTypeID() const { return ID; } + int getSubclassData() const { return SubclassData; } + +private: + TypeID ID : 2; // The current base type of this type. + int SubclassData : 30; // Space for subclasses to store data. +}; + +class RegSPType : public SPType { +public: + explicit RegSPType(int Reg) : SPType(RegTyID, Reg) {} + + int getReg() { return getSubclassData(); } + + /// Implement support type inquiry through isa, cast, and dyn_cast. + static bool classof(const SPType *T) { return T->getTypeID() == RegTyID; } +}; + +class SlotSPType : public SPType { +public: + SlotSPType(int Data, int Offset) : SPType(SlotTyID, Data), Offset(Offset) {} + + SlotInfo getSlot() const { + SlotInfo SI(getSubclassData(), Offset); + return SI; + } + + /// Implement support type inquiry through isa, cast, and dyn_cast. + static bool classof(const SPType *T) { return T->getTypeID() == SlotTyID; } + +private: + int Offset = 0; +}; + +class StackOperand { +public: + const Triple &Target; + + StackOperand(const Triple &Target) : Target(Target) {} + + // Returns the number of bytes offset from the loaded stack address. + int getLoadStackOffset(MachineInstr &MI) { + if (Target.isX86()) { + // x86: $r0 = MOV64rm %stack, 1, $noreg, 8, $noreg + return MI.getOperand(4).getImm(); + } else if (Target.isAArch64()) { + // aarch64: $x0 = LDRXui %stack, 1 + return MI.getOperand(2).getImm(); + } + Check(false, "Incorrect target type"); + return -1; + } + + // Returns the number of bytes offset from the stored stack address. + int getStoreStackOffset(MachineInstr &MI) { + if (Target.isX86()) { + // x86: MOV64mr %stack, 1, $noreg, 24, $noreg, $rbx + return MI.getOperand(3).getImm(); + } else if (Target.isAArch64()) { + // aarch64: STRXui $x20, %stack, 3 + return MI.getOperand(2).getImm(); + } + Check(false, "Incorrect target type"); + return -1; + } + + // The destination register is operand 0 for both x86 and aarch64. + Register getLoadReg(MachineInstr &MI) { return MI.getOperand(0).getReg(); } + + Register getStoreReg(MachineInstr &MI) { + if (Target.isX86()) { + // x86: MOV64mr %stack, 1, $noreg, 24, $noreg, $rbx + return MI.getOperand(5).getReg(); + } else if (Target.isAArch64()) { + // aarch64: STRXui $x20, %stack, 3 + return MI.getOperand(0).getReg(); + } + Check(false, "Incorrect target type"); + return -1; + } +}; + +/// Use data flow analysis to calculate the stack-pointer set on statepoint. +class StackPointerAnalysis : public StackOperand { +public: + StackPointerAnalysis(MachineFunction &MF, + SetVector &Statepoints, + StatepointLives &Data, FuncArgPointers &ArgPtrs) + : StackOperand(MF.getTarget().getTargetTriple()), MF(MF), + TII(MF.getSubtarget().getInstrInfo()), + TRI(MF.getSubtarget().getRegisterInfo()), Statepoints(Statepoints), + Data(Data), ArgPtrs(ArgPtrs) {} + + ~StackPointerAnalysis() { clearSPData(); } + + void computeStackPointerMap() { + // Worklist containing pending BBs + std::queue Worklist; + MapVector> Ins, Outs; + + // Calculate the livein register of each BB. + computeLiveinRegs(); + + // Seed the stack pointer for each individual block + for (MachineBasicBlock &MBB : MF) { + if (MBB.isEntryBlock()) { + // Initialize seeds as input parameters. + initialSPData(Ins[&MBB]); + Outs[&MBB] = Ins[&MBB]; + } else { + Ins[&MBB].clear(); + Outs[&MBB].clear(); + } + + transferSPData(MBB, Outs[&MBB]); + if (!Outs[&MBB].empty()) { + for (auto *Succ : MBB.successors()) { + Worklist.push(Succ); + } + } + } + + // Iterate until stable + while (!Worklist.empty()) { + MachineBasicBlock *MBB = Worklist.front(); + Worklist.pop(); + SetVector Tmp = Ins[MBB]; + const auto OldSize = Tmp.size(); + for (auto *Pred : MBB->predecessors()) { + Tmp.set_union(Outs[Pred]); + } + + // assert: OldLiveIn is a subset of NewLiveIn + if (Tmp.size() == OldSize) { + // If the sets are the same size, then we didn't actually add anything + // when unioning our successors SPSet. Thus, the SPSet of this block + // hasn't changed. + continue; + } + + transferSPData(*MBB, Tmp); + // assert: OldLiveOut is a subset of NewLiveOut + if (Outs[MBB].size() != Tmp.size()) { + Outs[MBB] = Tmp; + for (auto *Succ : MBB->successors()) { + Worklist.push(Succ); + } + } + } + } + + void transferSPData(MachineBasicBlock &MBB, SetVector &Tmp) { + updateLiveInRegs(MBB, Tmp); + + for (MachineInstr &MI : MBB) { + if (MI.isDebugOrPseudoInstr()) + continue; + + if (Statepoints.contains(&MI)) { + StatepointOpers SI(&MI); + if (SI.isCJStackCheck()) + continue; + + updateSPDataInStatepoint(&MI, Tmp); + removeDeadReg(MI, Tmp); + continue; + } + + int FI = 0; + unsigned MemBytes = 0; + if (TII->isStoreToStackSlot(MI, FI, MemBytes) && MemBytes == 8) { + Register SrcReg = getStoreReg(MI); + if (Tmp.contains(getSPData(SrcReg))) { + int Imm = getStoreStackOffset(MI); + Tmp.insert(getSPData(FI, Imm, false)); + } + } else if (TII->isLoadFromStackSlot(MI, FI, MemBytes) && MemBytes == 8) { + int Imm = getLoadStackOffset(MI); + if (Tmp.contains(getSPData(FI, Imm, false))) { + Register DstReg = getLoadReg(MI); + Tmp.insert(getSPData(DstReg)); + } + } else if (TII->isLEA64r(MI) || TII->isADDXri(MI) || TII->isORRXri(MI)) { + MachineOperand &RegMO = MI.getOperand(0); + MachineOperand &SrcMO = MI.getOperand(1); + if (SrcMO.isFI()) { + Tmp.insert(getSPData(RegMO.getReg())); + } else { + assert(SrcMO.isReg() && "Incorrect MI operand!"); + if (Tmp.contains(getSPData(SrcMO.getReg()))) { + Tmp.insert(getSPData(RegMO.getReg())); + } + } + } else if (MI.isCopy()) { + // $r12 = COPY renamable $rbx + Register SrcReg = MI.getOperand(1).getReg(); + if (Tmp.contains(getSPData(SrcReg))) { + Tmp.insert(getSPData(MI.getOperand(0).getReg())); + } + } + + removeDeadReg(MI, Tmp); + } + } + +private: + MachineFunction &MF; + const TargetInstrInfo *TII; + const TargetRegisterInfo *TRI; + SetVector &Statepoints; + StatepointLives &Data; + FuncArgPointers &ArgPtrs; + DenseMap> LiveinRegs; + + // Cache All stack pointer data and clear it later. + SmallVector CacheSPData; + + // Keeps track of the offsets of stack slot. + DenseMap SPRegsMap; + DenseMap SPSlotsMap; + + SPType *getSPData(int Data, int Offset = 0, bool isReg = true) { + if (isReg) { + if (SPRegsMap.count(Data)) { + return SPRegsMap[Data]; + } + + RegSPType *RegSP = new RegSPType(Data); + CacheSPData.push_back(RegSP); + SPRegsMap[Data] = RegSP; + return RegSP; + } else { + // stack slot + SlotInfo SI(Data, Offset); + if (SPSlotsMap.count(SI)) { + return SPSlotsMap[SI]; + } + + SlotSPType *SlotSP = new SlotSPType(Data, Offset); + CacheSPData.push_back(SlotSP); + SPSlotsMap[SI] = SlotSP; + return SlotSP; + } + } + + void clearSPData() { + for (auto SP : CacheSPData) { + delete SP; + } + CacheSPData.clear(); + SPRegsMap.clear(); + SPSlotsMap.clear(); + } + + // The SP data is initialized to the pointer parameter of the function. + void initialSPData(SetVector &In) { + for (int Reg : ArgPtrs.Regs) { + SPType *SP = getSPData(Reg); + In.insert(SP); + } + for (int FI : ArgPtrs.FIs) { + SPType *SP = getSPData(FI, 0, false); + In.insert(SP); + } + } + + void computeLiveinRegs() { + for (MachineBasicBlock &MBB : MF) { + for (const auto &LI : MBB.liveins()) { + assert(Register::isPhysicalRegister(LI.PhysReg) && + "Cannot have a live-in virtual register!"); + const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(LI.PhysReg); + unsigned RegSize = TRI->getRegSizeInBits(*RC); + if (RegSize == 64) { // 64: Register bit width + SPType *SP = getSPData(LI.PhysReg); + LiveinRegs[&MBB].insert(SP); + } + } + } + } + + void removeDeadReg(MachineInstr &MI, SetVector &Tmp) { + SetVector KeepRegs; + for (unsigned i = 0; i < MI.getNumOperands(); ++i) { + MachineOperand &MO = MI.getOperand(i); + if (MO.isRegMask()) + continue; + + if (!MO.isReg() || MO.getReg() == 0) + continue; + + Register MOReg = MO.getReg(); + assert(Register::isPhysicalRegister(MOReg) && + "Cannot be a virtual register in Operand!"); + SPType *SP = getSPData(MOReg); + // If reg is used for target reg in the instruction, keep it. + // for example: renamable $rax = LEA64r killed renamable $rax + if (MO.isKill()) { + if (KeepRegs.contains(SP)) { + continue; + } else { + Tmp.remove(SP); + } + } else { + KeepRegs.insert(SP); + } + } + } + + // Filter out non-livein registers in SPSet. + void updateLiveInRegs(MachineBasicBlock &MBB, SetVector &SPSet) { + for (auto It = SPSet.begin(); It != SPSet.end();) { + if (auto *RegSP = dyn_cast(*It)) { + if (!LiveinRegs[&MBB].contains(RegSP)) { + It = SPSet.erase(It); + continue; + } + } + ++It; + } + } + + void updateSPDataInStatepoint(MachineInstr *MI, SetVector &SPSet) { + for (SPType *SP : SPSet) { + if (auto *SPReg = dyn_cast(SP)) { + Data[MI].Regs.insert(SPReg->getReg()); + } else { + auto *SPSlot = dyn_cast(SP); + assert(SPSlot && "Incorrect SP data."); + Data[MI].Slots.insert(SPSlot->getSlot()); + } + } + } +}; + +class StackLives : public StackOperand { +public: + // Represent the live analysis data for stacks. + MapVector> KillSet; + MapVector> LiveSet; + MapVector> LiveIn; + MapVector> LiveOut; + + StackLives(MachineFunction &MF, FuncArgPointers &ArgPtrs) + : StackOperand(MF.getTarget().getTargetTriple()), MF(MF), + TII(MF.getSubtarget().getInstrInfo()), MFI(MF.getFrameInfo()), + TRI(MF.getSubtarget().getRegisterInfo()), MRI(MF.getRegInfo()), + DL(MF.getDataLayout()), ArgPtrs(ArgPtrs) {} + + void computeStackKillSet(MachineBasicBlock *MBB) { + for (auto &MI : *MBB) { + int FI; + unsigned MemBytes; + if (TII->isStoreToStackSlot(MI, FI, MemBytes) && MemBytes == 8) { + if (MFI.getObjectAllocation(FI)) + continue; + + if (MFI.isSpillSlotObjectIndex(FI)) { + // The spill is a define point of the stack pointer. + int Imm = getStoreStackOffset(MI); + insertLiveSet(KillSet[MBB], FI, Imm); + continue; + } + assert(MFI.isStatepointSpillSlotObjectIndex(FI) && + "Unknown stack type"); + } + } + } + + void computeStackLiveIn(MachineBasicBlock::reverse_iterator Begin, + MachineBasicBlock::reverse_iterator End, + SetVector &LiveTmp) { + for (auto &MI : make_range(Begin, End)) { + int FI; + unsigned MemBytes; + + if (TII->isLoadFromStackSlot(MI, FI, MemBytes) && MemBytes == 8) { + int Imm = getLoadStackOffset(MI); + const AllocaInst *AI = MFI.getObjectAllocation(FI); + if (AI != nullptr) { + assert(MFI.isAliasedObjectIndex(FI)); + if (isPointerByLocalStack(AI, Imm)) { + insertLiveSet(LiveTmp, FI, Imm); + } + continue; + } + if (MFI.isFixedObjectIndex(FI)) { + if (isPointerByFixedObject(FI)) { + insertLiveSet(LiveTmp, FI, Imm); + } + continue; + } + if (MFI.isSpillSlotObjectIndex(FI)) { + // The reload is a use point of the stack pointer. + insertLiveSet(LiveTmp, FI, Imm); + continue; + } + assert(MFI.isStatepointSpillSlotObjectIndex(FI) && + "Unknown stack type"); + continue; + } + + if (TII->isStoreToStackSlot(MI, FI, MemBytes) && MemBytes == 8) { + int Imm = getStoreStackOffset(MI); + const AllocaInst *AI = MFI.getObjectAllocation(FI); + if (AI != nullptr) { + assert(MFI.isAliasedObjectIndex(FI)); + if (isPointerByLocalStack(AI, Imm)) { + insertLiveSet(LiveTmp, FI, Imm); + } + continue; + } + if (MFI.isFixedObjectIndex(FI)) { + if (isPointerByFixedObject(FI)) { + insertLiveSet(LiveTmp, FI, Imm); + } + continue; + } + if (MFI.isSpillSlotObjectIndex(FI)) { + // The spill is a define point of the stack pointer. + removeLiveSet(LiveTmp, FI, Imm); + continue; + } + assert(MFI.isStatepointSpillSlotObjectIndex(FI) && + "Unknown stack type"); + } + } + } + + void computeStackLiveness() { + SmallSetVector Worklist; + + // Seed the liveness for each individual block + for (MachineBasicBlock &MBBs : MF) { + MachineBasicBlock *MBB = &MBBs; + KillSet[MBB].clear(); + LiveSet[MBB].clear(); + LiveOut[MBB].clear(); + computeStackKillSet(MBB); + computeStackLiveIn(MBB->rbegin(), MBB->rend(), LiveSet[MBB]); + + LiveIn[MBB] = LiveOut[MBB]; + LiveIn[MBB].set_subtract(KillSet[MBB]); + LiveIn[MBB].set_union(LiveSet[MBB]); + if (!LiveIn[MBB].empty()) + Worklist.insert(MBB->pred_begin(), MBB->pred_end()); + } + + // Propagate that liveness until stable + while (!Worklist.empty()) { + MachineBasicBlock *MBB = Worklist.pop_back_val(); + + // Compute our new liveout set, then exit early if it hasn't changed + // despite the contribution of our successor. + SetVector TmpLiveOut = LiveOut[MBB]; + const auto OldLiveOutSize = TmpLiveOut.size(); + for (MachineBasicBlock *Succ : MBB->successors()) { + assert(LiveIn.count(Succ)); + TmpLiveOut.set_union(LiveIn[Succ]); + } + + // assert OutLiveOut is a subset of LiveOut + if (OldLiveOutSize == TmpLiveOut.size()) { + // If the sets are the same size, then we didn't actually add anything + // when unioning our successors LiveIn. Thus, the LiveIn of this block + // hasn't changed. + continue; + } + LiveOut[MBB] = TmpLiveOut; + + // Apply the effects of this basic block + SetVector LiveTmp = TmpLiveOut; + LiveTmp.set_subtract(KillSet[MBB]); + LiveTmp.set_union(LiveSet[MBB]); + + assert(LiveIn.count(MBB)); + SetVector &OldLiveIn = LiveIn[MBB]; + // assert: OldLiveIn is a subset of LiveTmp + if (OldLiveIn.size() != LiveTmp.size()) { + LiveIn[MBB] = LiveTmp; + Worklist.insert(MBB->pred_begin(), MBB->pred_end()); + } + } + } + + void analyzeStatepointLiveness(MachineInstr *MI, CJStackLives &Lives) { + MachineBasicBlock *MBB = MI->getParent(); + + // Note: The copy is intentional and required + assert(LiveOut.count(MBB)); + SetVector LiveTmp = LiveOut[MBB]; + + computeStackLiveIn(MBB->rbegin(), ++MI->getIterator().getReverse(), + LiveTmp); + + // Records the alive stack pointer. + Lives.Slots.remove_if([&](SlotInfo V) { return !LiveTmp.contains(V); }); + } + +private: + MachineFunction &MF; + const TargetInstrInfo *TII; + const MachineFrameInfo &MFI; + const TargetRegisterInfo *TRI; + const MachineRegisterInfo &MRI; + const DataLayout &DL; + FuncArgPointers &ArgPtrs; + + void insertLiveSet(SetVector &LiveTmp, int FI, int Offset) { + assert(FI < MFI.getObjectIndexEnd() && "The FI exceeds the object range."); + LiveTmp.insert(std::make_pair(FI, Offset)); + } + + void removeLiveSet(SetVector &LiveTmp, int FI, int Offset) { + LiveTmp.remove(std::make_pair(FI, Offset)); + } + + bool isPointerInArray(ArrayType *AT, const int64_t Imm, int64_t CurPos = 0) { + uint64_t Num = AT->getNumElements(); + Type *ElementType = AT->getElementType(); + if (ElementType->isPointerTy()) { + unsigned ArraySize = DL.getPointerSize() * Num; + Check(ArraySize + CurPos > Imm, "The Imm exceeds the array range."); + return true; + } else if (StructType *EST = dyn_cast(ElementType)) { + unsigned StructSize = DL.getStructLayout(EST)->getSizeInBytes(); + unsigned ArraySize = StructSize * Num; + Check(ArraySize + CurPos > Imm, "The Imm exceeds the array range."); + int Offset = CurPos; + while (Offset + StructSize <= Imm) { + Offset += StructSize; + } + return isPointerInStruct(EST, Imm, Offset); + } + return false; + } + + // Calculate the location of the field where the imm is located by greedy. + bool isPointerInStruct(StructType *ST, const int64_t Imm, + int64_t CurPos = 0) { + const StructLayout *Layout = DL.getStructLayout(ST); + unsigned StructSize = Layout->getSizeInBytes(); + Check(StructSize + CurPos > Imm, "The Imm exceeds the struct range."); + unsigned Cur = 0; + unsigned Next = Cur + 1; + for (; Cur < ST->getNumElements(); Cur++, Next++) { + if (Next < ST->getNumElements()) { + int NextOffset = Layout->getElementOffset(Next) + CurPos; + if (NextOffset <= Imm) { + continue; + } + } + + int Offset = Layout->getElementOffset(Cur) + CurPos; + Type *ET = ST->getElementType(Cur); + if (ET->isPointerTy()) { + Check(Offset == Imm, "Incorrect offset calculation"); + return true; + } else if (StructType *EST = dyn_cast(ET)) { + return isPointerInStruct(EST, Imm, Offset); + } else if (ArrayType *EAT = dyn_cast(ET)) { + return isPointerInArray(EAT, Imm, Offset); + } else { + return false; + } + } + Check(false, "Incorrect offset calculation"); + return false; + } + + bool isPointerByLocalStack(const AllocaInst *AI, int Imm) { + int Offset = Target.isX86() ? Imm : Imm * 8; // 8: pointer size. + if (auto *ST = dyn_cast(AI->getAllocatedType())) { + return isPointerInStruct(ST, Offset); + } else if (auto *AT = dyn_cast(AI->getAllocatedType())) { + return isPointerInArray(AT, Offset); + } else if (AI->getAllocatedType()->isPointerTy()) { + return true; + } + return false; + } + + bool isPointerByFixedObject(int Object) { + for (int FI : ArgPtrs.FIs) { + if (FI == Object) + return true; + } + return false; + } +}; +} // namespace + +void CJStackPointerInserter::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequiredID(UnreachableMachineBlockElimID); + AU.addRequired(); + MachineFunctionPass::getAnalysisUsage(AU); +} + +bool CJStackPointerInserter::needRewriteCall(MachineInstr &MI) { + if (MI.getOpcode() != TargetOpcode::STATEPOINT) + return false; + + StatepointOpers SO(&MI); + const Function *CalledFunc = SO.getCalledFunction(); + if (CalledFunc && (CalledFunc->hasFnAttribute("gc-safepoint") || + CalledFunc->hasFnAttribute("cj-runtime"))) { + return false; + } + + return true; +} + +void CJStackPointerInserter::filterGCPointer( + MachineInstr &MI, StatepointOpers &SO, SmallSetVector &Regs, + SmallSetVector &Slots) { + unsigned GCPtrNumIdx = SO.getNumGCPtrIdx(); + unsigned GCPtrNum = MI.getOperand(GCPtrNumIdx).getImm(); + if (GCPtrNum == 0) { + return; + } + + unsigned GCPtrIdx = (unsigned)SO.getFirstGCPtrIdx(); + assert((int)GCPtrIdx != -1); + while (GCPtrNum--) { + MachineOperand &MO = MI.getOperand(GCPtrIdx); + if (MO.isReg() && MO.getReg() != 0) { + if (Regs.contains(MO.getReg())) { + Regs.remove(MO.getReg()); + } + } else { + assert(MO.isImm() && "MO is not a Imm when parse gc stack slot!"); + assert(MO.getImm() == StackMaps::IndirectMemRefOp && + "MO is not a IndirectMemRefOp type when parse gc stack slot!"); + int FI = MI.getOperand(GCPtrIdx + 2).getIndex(); // 2: FI index + int Offset = MI.getOperand(GCPtrIdx + 3).getImm(); // 3: Offset + SlotInfo StackPair = std::make_pair(FI, Offset); + if (Slots.contains(StackPair)) { + Slots.remove(StackPair); + } + } + + GCPtrIdx = StackMaps::getNextMetaArgIdx(&MI, GCPtrIdx); + } +} + +void CJStackPointerInserter::recordArgStackPtrs( + SmallSetVector &Regs, SmallSetVector &Slots, + FuncArgPointers &ArgPtrs) { + if (!ArgPtrs.Regs.empty()) { + Regs.insert(ArgPtrs.Regs.begin(), ArgPtrs.Regs.end()); + } + + if (!ArgPtrs.FIs.empty()) { + for (int FI : ArgPtrs.FIs) { + Slots.insert(std::make_pair(FI, 0)); + } + } +} + +bool CJStackPointerInserter::rewriteStatepoint(MachineFunction &MF, + MachineInstr &MI, + CJStackLives &StackLives, + FuncArgPointers &ArgPtrs) { + SmallSetVector SPRegs; + SmallSetVector SPSlots; + const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo(); + + StatepointOpers SO(&MI); + if (SO.isCJStackCheck()) { + recordArgStackPtrs(SPRegs, SPSlots, ArgPtrs); + if (SPRegs.empty() && SPSlots.empty()) { + return false; // not need rewrite + } + } else { + if (StackLives.empty()) { // not need rewrite + return false; + } + + for (unsigned Reg : StackLives.Regs) { + if (TRI->isCalleeSavedPhysReg(Reg, MF)) { + SPRegs.insert(Reg); + } + } + SPSlots.insert(StackLives.Slots.begin(), StackLives.Slots.end()); + } + + // Filter the GC pointer. + filterGCPointer(MI, SO, SPRegs, SPSlots); + int Num = SPRegs.size() + SPSlots.size(); + if (Num == 0) { + return false; + } + + unsigned NumIdx = SO.getNumStackPtrsIdx(); + assert(MI.getOperand(NumIdx).getImm() == 0 && + "The num of stack pointers should be 0!"); + MachineFrameInfo &MFI = MF.getFrameInfo(); + MachineInstrBuilder MIB = BuildMI(MF, MI.getDebugLoc(), MI.getDesc()); + + // Inherit previous memory operands. + MIB.cloneMemRefs(MI); + for (unsigned i = 0; i < MI.getNumOperands(); ++i) { + MachineOperand &MO = MI.getOperand(i); + if (i == NumIdx) { + MIB.addImm(Num); + for (unsigned j = 0; j < SPRegs.size(); ++j) { + MIB.addReg(SPRegs[j]); + } + for (unsigned j = 0; j < SPSlots.size(); ++j) { + MIB.addImm(StackMaps::IndirectMemRefOp); + MIB.addImm(MFI.getObjectSize(SPSlots[j].first)); + MIB.addFrameIndex(SPSlots[j].first); + MIB.addImm(SPSlots[j].second); + } + continue; + } + + if (!MO.isFI()) { + // Index of Def operand this Use it tied to. + // Since Defs are coming before Uses, if Use is tied, then + // index of Def must be smaller that index of that Use. + // Also, Defs preserve their position in new MI. + unsigned TiedTo = i; + if (MO.isReg() && MO.isTied()) + TiedTo = MI.findTiedOperandIdx(i); + MIB.add(MO); + if (TiedTo < i) + MIB->tieOperands(TiedTo, MIB->getNumOperands() - 1); + } else { + MIB.add(MO); + } + } + MI.getParent()->insert(MI.getNextNode(), MIB); + return true; +} + +bool CJStackPointerInserter::runOnMachineFunction(MachineFunction &MF) { + bool Changed = false; + if (!MF.getFunction().hasCangjieGC()) + return Changed; + + // Gather all the MBBs that statepoints need to rewritten. + SetVector Statepoints; + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &I : MBB) { + if (needRewriteCall(I)) { + Statepoints.insert(&I); + } + } + } + + if (Statepoints.empty()) + return Changed; + + // This variable may not be empty and needs to be cleaned. + SmallVector RemoveMI; + FuncArgPointers ArgPtrs; + parseFuncArgPointers(MF, ArgPtrs); + + // Records the stack live variable set of each statepoint. + StatepointLives LiveData; + StackPointerAnalysis SPA(MF, Statepoints, LiveData, ArgPtrs); + SPA.computeStackPointerMap(); + + StackLives Live(MF, ArgPtrs); + Live.computeStackLiveness(); + for (auto *MI : Statepoints) { + Live.analyzeStatepointLiveness(MI, LiveData[MI]); + if (rewriteStatepoint(MF, *MI, LiveData[MI], ArgPtrs)) { + RemoveMI.push_back(MI); + } + } + + if (RemoveMI.size()) { + for (auto MI : RemoveMI) { + MI->eraseFromParent(); + } + Changed = true; + } + + return Changed; +} diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt index 689fa9651..1868e405b 100644 --- a/llvm/lib/CodeGen/CMakeLists.txt +++ b/llvm/lib/CodeGen/CMakeLists.txt @@ -37,6 +37,7 @@ add_llvm_component_library(LLVMCodeGen CallingConvLower.cpp CFGuardLongjmp.cpp CFIFixup.cpp + CJMetadata.cpp CFIInstrInserter.cpp CodeGen.cpp CodeGenCommonISel.cpp @@ -44,6 +45,8 @@ add_llvm_component_library(LLVMCodeGen CodeGenPrepare.cpp CommandFlags.cpp CriticalAntiDepBreaker.cpp + CJBarrierLowering.cpp + CJStackPointerInserter.cpp DeadMachineInstructionElim.cpp DetectDeadLanes.cpp DFAPacketizer.cpp @@ -220,6 +223,7 @@ add_llvm_component_library(LLVMCodeGen TargetRegisterInfo.cpp TargetSchedule.cpp TargetSubtargetInfo.cpp + ThreadSanitizerPass.cpp TypePromotion.cpp TwoAddressInstructionPass.cpp UnreachableBlockElim.cpp diff --git a/llvm/lib/CodeGen/CallingConvLower.cpp b/llvm/lib/CodeGen/CallingConvLower.cpp index f74ff30ab..c230e58fe 100644 --- a/llvm/lib/CodeGen/CallingConvLower.cpp +++ b/llvm/lib/CodeGen/CallingConvLower.cpp @@ -17,6 +17,7 @@ #include "llvm/CodeGen/TargetLowering.h" #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/Module.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" @@ -76,6 +77,43 @@ bool CCState::IsShadowAllocatedReg(MCRegister Reg) const { return true; } +void CCState::AddAlignedCallFrameSizeMetaDataForCJFFI( + Function *Func, unsigned CallFrameSize) const { + if (Func == nullptr) { // FFI call can not use funcPtr. + return; + } + if (!Func->hasFnAttribute("cj2c") && !Func->hasFnAttribute("c2cj")) { + return; + } + StringRef MetaName = "CallFrameSizeForCJFFI"; + MDNode *Metadata = Func->getMetadata(MetaName); + if (!Func->isVarArg() && Metadata != nullptr) { + return; + } + LLVMContext &Ctx = Func->getContext(); + if (Metadata != nullptr) { // Use max CallFrameSize for VarArg Func + unsigned OriValue = dyn_cast(dyn_cast( + Metadata->getOperand(0).get()) + ->getValue()) + ->getSExtValue(); + if (OriValue >= CallFrameSize) { + return; + } + Func->eraseMetadata(Ctx.getMDKindID(MetaName)); + } + // MCC_XXXStub require CallFrameSize to be 16 bytes align + const Triple TT(Func->getParent()->getTargetTriple()); + CallFrameSize = + TT.isARM() ? alignTo(CallFrameSize, 8) : alignTo(CallFrameSize, 16); + + MDNode *CallFrameSizeMD = + MDTuple::get(Ctx, ValueAsMetadata::getConstant(ConstantInt::get( + Type::getInt64Ty(Ctx), CallFrameSize))); + + Func->setMetadata(MetaName, CallFrameSizeMD); + return; +} + /// Analyze an array of argument values, /// incorporating info about the formals into this state. void diff --git a/llvm/lib/CodeGen/CodeGen.cpp b/llvm/lib/CodeGen/CodeGen.cpp index 5050395fb..5009789ce 100644 --- a/llvm/lib/CodeGen/CodeGen.cpp +++ b/llvm/lib/CodeGen/CodeGen.cpp @@ -26,6 +26,7 @@ void llvm::initializeCodeGen(PassRegistry &Registry) { initializeCFGuardLongjmpPass(Registry); initializeCFIFixupPass(Registry); initializeCFIInstrInserterPass(Registry); + initializeCJStackPointerInserterPass(Registry); initializeCheckDebugMachineModulePass(Registry); initializeCodeGenPreparePass(Registry); initializeDeadMachineInstructionElimPass(Registry); diff --git a/llvm/lib/CodeGen/DwarfEHPrepare.cpp b/llvm/lib/CodeGen/DwarfEHPrepare.cpp index aa81f618d..81e487df7 100644 --- a/llvm/lib/CodeGen/DwarfEHPrepare.cpp +++ b/llvm/lib/CodeGen/DwarfEHPrepare.cpp @@ -169,6 +169,7 @@ size_t DwarfEHPrepare::pruneUnreachableResumes( bool DwarfEHPrepare::InsertUnwindResumeCalls() { SmallVector Resumes; SmallVector CleanupLPads; + bool CangjieSrc = F.hasFnAttribute("cangjie"); if (F.doesNotThrow()) NumNoUnwind++; else @@ -211,95 +212,110 @@ bool DwarfEHPrepare::InsertUnwindResumeCalls() { if (ResumesLeft == 0) return true; // We pruned them all. - // RewindFunction - _Unwind_Resume or the target equivalent. - FunctionCallee RewindFunction; - CallingConv::ID RewindFunctionCallingConv; - FunctionType *FTy; - const char *RewindName; - bool DoesRewindFunctionNeedExceptionObject; - - if ((Pers == EHPersonality::GNU_CXX || Pers == EHPersonality::GNU_CXX_SjLj) && - TargetTriple.isTargetEHABICompatible()) { - RewindName = TLI.getLibcallName(RTLIB::CXA_END_CLEANUP); - FTy = FunctionType::get(Type::getVoidTy(Ctx), false); - RewindFunctionCallingConv = - TLI.getLibcallCallingConv(RTLIB::CXA_END_CLEANUP); - DoesRewindFunctionNeedExceptionObject = false; - } else { - RewindName = TLI.getLibcallName(RTLIB::UNWIND_RESUME); - FTy = - FunctionType::get(Type::getVoidTy(Ctx), Type::getInt8PtrTy(Ctx), false); - RewindFunctionCallingConv = TLI.getLibcallCallingConv(RTLIB::UNWIND_RESUME); - DoesRewindFunctionNeedExceptionObject = true; - } - RewindFunction = F.getParent()->getOrInsertFunction(RewindName, FTy); - - // Create the basic block where the _Unwind_Resume call will live. - if (ResumesLeft == 1) { - // Instead of creating a new BB and PHI node, just append the call to - // _Unwind_Resume to the end of the single resume block. - ResumeInst *RI = Resumes.front(); - BasicBlock *UnwindBB = RI->getParent(); - Value *ExnObj = GetExceptionObject(RI); + // We do not create PHI node and explict resume calls for Cangjie + // FP-based stack unwinding. For other languages, keep the original + // logic. + if (!CangjieSrc) { + // RewindFunction - _Unwind_Resume or the target equivalent. + FunctionCallee RewindFunction; + CallingConv::ID RewindFunctionCallingConv; + FunctionType *FTy; + const char *RewindName; + bool DoesRewindFunctionNeedExceptionObject; + + if ((Pers == EHPersonality::GNU_CXX || + Pers == EHPersonality::GNU_CXX_SjLj) && + TargetTriple.isTargetEHABICompatible()) { + RewindName = TLI.getLibcallName(RTLIB::CXA_END_CLEANUP); + FTy = FunctionType::get(Type::getVoidTy(Ctx), false); + RewindFunctionCallingConv = + TLI.getLibcallCallingConv(RTLIB::CXA_END_CLEANUP); + DoesRewindFunctionNeedExceptionObject = false; + } else { + RewindName = TLI.getLibcallName(RTLIB::UNWIND_RESUME); + FTy = FunctionType::get(Type::getVoidTy(Ctx), Type::getInt8PtrTy(Ctx), + false); + RewindFunctionCallingConv = + TLI.getLibcallCallingConv(RTLIB::UNWIND_RESUME); + DoesRewindFunctionNeedExceptionObject = true; + } + RewindFunction = F.getParent()->getOrInsertFunction(RewindName, FTy); + + // Create the basic block where the _Unwind_Resume call will live. + if (ResumesLeft == 1) { + // Instead of creating a new BB and PHI node, just append the call to + // _Unwind_Resume to the end of the single resume block. + ResumeInst *RI = Resumes.front(); + BasicBlock *UnwindBB = RI->getParent(); + Value *ExnObj = GetExceptionObject(RI); + llvm::SmallVector RewindFunctionArgs; + if (DoesRewindFunctionNeedExceptionObject) + RewindFunctionArgs.push_back(ExnObj); + + // Call the rewind function. + CallInst *CI = + CallInst::Create(RewindFunction, RewindFunctionArgs, "", UnwindBB); + // The verifier requires that all calls of debug-info-bearing functions + // from debug-info-bearing functions have a debug location (for inlining + // purposes). Assign a dummy location to satisfy the constraint. + Function *RewindFn = dyn_cast(RewindFunction.getCallee()); + if (RewindFn && RewindFn->getSubprogram()) + if (DISubprogram *SP = F.getSubprogram()) + CI->setDebugLoc(DILocation::get(SP->getContext(), 0, 0, SP)); + CI->setCallingConv(RewindFunctionCallingConv); + + // We never expect _Unwind_Resume to return. + CI->setDoesNotReturn(); + new UnreachableInst(Ctx, UnwindBB); + return true; + } + + std::vector Updates; + Updates.reserve(Resumes.size()); + llvm::SmallVector RewindFunctionArgs; + + BasicBlock *UnwindBB = BasicBlock::Create(Ctx, "unwind_resume", &F); + PHINode *PN = PHINode::Create(Type::getInt8PtrTy(Ctx), ResumesLeft, + "exn.obj", UnwindBB); + + // Extract the exception object from the ResumeInst and add it to the PHI + // node that feeds the _Unwind_Resume call. + for (ResumeInst *RI : Resumes) { + BasicBlock *Parent = RI->getParent(); + BranchInst::Create(UnwindBB, Parent); + Updates.push_back({DominatorTree::Insert, Parent, UnwindBB}); + + Value *ExnObj = GetExceptionObject(RI); + PN->addIncoming(ExnObj, Parent); + + ++NumResumesLowered; + } + if (DoesRewindFunctionNeedExceptionObject) - RewindFunctionArgs.push_back(ExnObj); + RewindFunctionArgs.push_back(PN); - // Call the rewind function. + // Call the function. CallInst *CI = CallInst::Create(RewindFunction, RewindFunctionArgs, "", UnwindBB); - // The verifier requires that all calls of debug-info-bearing functions - // from debug-info-bearing functions have a debug location (for inlining - // purposes). Assign a dummy location to satisfy the constraint. - Function *RewindFn = dyn_cast(RewindFunction.getCallee()); - if (RewindFn && RewindFn->getSubprogram()) - if (DISubprogram *SP = F.getSubprogram()) - CI->setDebugLoc(DILocation::get(SP->getContext(), 0, 0, SP)); CI->setCallingConv(RewindFunctionCallingConv); // We never expect _Unwind_Resume to return. CI->setDoesNotReturn(); new UnreachableInst(Ctx, UnwindBB); - return true; - } - - std::vector Updates; - Updates.reserve(Resumes.size()); - - llvm::SmallVector RewindFunctionArgs; - - BasicBlock *UnwindBB = BasicBlock::Create(Ctx, "unwind_resume", &F); - PHINode *PN = PHINode::Create(Type::getInt8PtrTy(Ctx), ResumesLeft, "exn.obj", - UnwindBB); - // Extract the exception object from the ResumeInst and add it to the PHI node - // that feeds the _Unwind_Resume call. - for (ResumeInst *RI : Resumes) { - BasicBlock *Parent = RI->getParent(); - BranchInst::Create(UnwindBB, Parent); - Updates.push_back({DominatorTree::Insert, Parent, UnwindBB}); - - Value *ExnObj = GetExceptionObject(RI); - PN->addIncoming(ExnObj, Parent); - - ++NumResumesLowered; + if (DTU) + DTU->applyUpdates(Updates); + } else { + for (ResumeInst *RI : Resumes) { + BasicBlock *UnwindBB = RI->getParent(); + RI->eraseFromParent(); + new UnreachableInst(Ctx, UnwindBB); + // Keep the original statistics even for cangjie resume. + ++NumResumesLowered; + } } - if (DoesRewindFunctionNeedExceptionObject) - RewindFunctionArgs.push_back(PN); - - // Call the function. - CallInst *CI = - CallInst::Create(RewindFunction, RewindFunctionArgs, "", UnwindBB); - CI->setCallingConv(RewindFunctionCallingConv); - - // We never expect _Unwind_Resume to return. - CI->setDoesNotReturn(); - new UnreachableInst(Ctx, UnwindBB); - - if (DTU) - DTU->applyUpdates(Updates); - return true; } diff --git a/llvm/lib/CodeGen/FixupStatepointCallerSaved.cpp b/llvm/lib/CodeGen/FixupStatepointCallerSaved.cpp index 252910fd9..10f53507c 100644 --- a/llvm/lib/CodeGen/FixupStatepointCallerSaved.cpp +++ b/llvm/lib/CodeGen/FixupStatepointCallerSaved.cpp @@ -56,6 +56,12 @@ static cl::opt MaxStatepointsWithRegs( "fixup-max-csr-statepoints", cl::Hidden, cl::desc("Max number of statepoints allowed to pass GC Ptrs in registers")); +cl::opt + EnableCalledSaveForStackMap("enable-callee-saved-stackmap", cl::init(true), + cl::ReallyHidden, + cl::desc("enable called saved for stackmap." + " ALWAYS disable if optlevel=-O0")); + namespace { class FixupStatepointCallerSaved : public MachineFunctionPass { @@ -597,6 +603,9 @@ public: } // namespace bool FixupStatepointCallerSaved::runOnMachineFunction(MachineFunction &MF) { + if (EnableCalledSaveForStackMap) { + PassGCPtrInCSR = true; + } if (skipFunction(MF.getFunction())) return false; @@ -605,10 +614,15 @@ bool FixupStatepointCallerSaved::runOnMachineFunction(MachineFunction &MF) { return false; SmallVector Statepoints; - for (MachineBasicBlock &BB : MF) - for (MachineInstr &I : BB) - if (I.getOpcode() == TargetOpcode::STATEPOINT) - Statepoints.push_back(&I); + for (MachineBasicBlock &BB : MF) { + for (MachineInstr &I : BB) { + if (I.getOpcode() == TargetOpcode::STATEPOINT) { + StatepointOpers SO(&I); + if (!SO.isCJStackCheck()) + Statepoints.push_back(&I); + } + } + } if (Statepoints.empty()) return false; diff --git a/llvm/lib/CodeGen/GCRootLowering.cpp b/llvm/lib/CodeGen/GCRootLowering.cpp index 80feb0045..7fd614fbf 100644 --- a/llvm/lib/CodeGen/GCRootLowering.cpp +++ b/llvm/lib/CodeGen/GCRootLowering.cpp @@ -20,13 +20,20 @@ #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/IR/Dominators.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstIterator.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" +#include "llvm/IR/SafepointIRVerifier.h" #include "llvm/InitializePasses.h" #include "llvm/MC/MCContext.h" +#include "llvm/Support/CommandLine.h" +#include using namespace llvm; +#define DEBUG_TYPE "gc-root-lowering" + namespace { /// LowerIntrinsics - This pass rewrites calls to the llvm.gcread or @@ -70,7 +77,7 @@ public: bool runOnMachineFunction(MachineFunction &MF) override; }; -} +} // namespace // ----------------------------------------------------------------------------- @@ -167,6 +174,7 @@ static bool InsertRootInitializers(Function &F, ArrayRef Roots) { /// runOnFunction - Replace gcread/gcwrite intrinsics with loads and stores. /// Leave gcroot intrinsics; the code generator needs to see those. +/// Lower cj intrinsic(e.g gcwrite_cj) to the corresponding runtime function. bool LowerIntrinsics::runOnFunction(Function &F) { // Quick exit for functions that do not use GC. if (!F.hasGC()) @@ -189,7 +197,7 @@ bool LowerIntrinsics::DoLowering(Function &F, GCStrategy &S) { SmallVector Roots; bool MadeChange = false; - for (BasicBlock &BB : F) + for (BasicBlock &BB : F) { for (Instruction &I : llvm::make_early_inc_range(BB)) { IntrinsicInst *CI = dyn_cast(&I); if (!CI) @@ -197,11 +205,12 @@ bool LowerIntrinsics::DoLowering(Function &F, GCStrategy &S) { Function *F = CI->getCalledFunction(); switch (F->getIntrinsicID()) { - default: break; + default: + break; case Intrinsic::gcwrite: { // Replace a write barrier with a simple store. - Value *St = new StoreInst(CI->getArgOperand(0), - CI->getArgOperand(2), CI); + Value *St = + new StoreInst(CI->getArgOperand(0), CI->getArgOperand(2), CI); CI->replaceAllUsesWith(St); CI->eraseFromParent(); MadeChange = true; @@ -225,6 +234,7 @@ bool LowerIntrinsics::DoLowering(Function &F, GCStrategy &S) { } } } + } if (Roots.size()) MadeChange |= InsertRootInitializers(F, Roots); diff --git a/llvm/lib/CodeGen/LocalStackSlotAllocation.cpp b/llvm/lib/CodeGen/LocalStackSlotAllocation.cpp index 5f54d7cc8..e81732d81 100644 --- a/llvm/lib/CodeGen/LocalStackSlotAllocation.cpp +++ b/llvm/lib/CodeGen/LocalStackSlotAllocation.cpp @@ -45,6 +45,11 @@ STATISTIC(NumAllocations, "Number of frame indices allocated into local block"); STATISTIC(NumBaseRegisters, "Number of virtual frame base registers allocated"); STATISTIC(NumReplacements, "Number of frame indices references replaced"); +namespace llvm { +extern cl::opt CJPipeline; +extern cl::opt EnableStackGrow; +} + namespace { class FrameRef { @@ -327,6 +332,14 @@ bool LocalStackSlotPass::insertFrameReferenceRegisters(MachineFunction &Fn) { if (!MFI.isObjectPreAllocated(MO.getIndex())) break; int Idx = MO.getIndex(); + auto AI = Fn.getFrameInfo().getObjectAllocation(Idx); + // For cj-stack-grow, the FrameBaseReg will make a new spill opreation + // in the entry, which cannot be identified in the rewrite-statepoint- + // for-cangjie-gc. Therefore, we do not perform it temporarily. + if (AI && EnableStackGrow && CJPipeline && + Fn.getFunction().hasCangjieGC()) + break; + int64_t LocalOffset = LocalOffsets[Idx]; if (!TRI->needsFrameBaseReg(&MI, LocalOffset)) break; diff --git a/llvm/lib/CodeGen/MachineBlockPlacement.cpp b/llvm/lib/CodeGen/MachineBlockPlacement.cpp index 9ff5c3762..aac539e67 100644 --- a/llvm/lib/CodeGen/MachineBlockPlacement.cpp +++ b/llvm/lib/CodeGen/MachineBlockPlacement.cpp @@ -216,6 +216,8 @@ extern cl::opt ViewBlockLayoutWithBFI; // Command line option to specify the name of the function for CFG dump // Defined in Analysis/BlockFrequencyInfo.cpp: -view-bfi-func-name= extern cl::opt ViewBlockFreqFuncName; + +extern cl::opt CJPipeline; } // namespace llvm namespace { @@ -1827,8 +1829,15 @@ void MachineBlockPlacement::buildChain( // Look for the best viable successor if there is one to place immediately // after this block. auto Result = selectBestSuccessor(BB, Chain, BlockFilter); - MachineBasicBlock* BestSucc = Result.BB; - bool ShouldTailDup = Result.ShouldTailDup; + MachineBasicBlock* BestSucc; + bool ShouldTailDup = false; + if (CJPipeline && Result.BB && Result.BB->succ_empty() && + !Result.BB->isReturnBlock()) { + BestSucc = nullptr; + } else { + BestSucc = Result.BB; + ShouldTailDup = Result.ShouldTailDup; + } if (allowTailDupPlacement()) ShouldTailDup |= (BestSucc && canTailDuplicateUnplacedPreds(BB, BestSucc, Chain, @@ -1855,7 +1864,7 @@ void MachineBlockPlacement::buildChain( // Check for that now. if (allowTailDupPlacement() && BestSucc && ShouldTailDup) { repeatedlyTailDuplicateBlock(BestSucc, BB, LoopHeaderBB, Chain, - BlockFilter, PrevUnplacedBlockIt); + BlockFilter, PrevUnplacedBlockIt); // If the chosen successor was duplicated into BB, don't bother laying // it out, just go round the loop again with BB as the chain end. if (!BB->isSuccessor(BestSucc)) diff --git a/llvm/lib/CodeGen/MachineCSE.cpp b/llvm/lib/CodeGen/MachineCSE.cpp index c6756b1d3..963cf7978 100644 --- a/llvm/lib/CodeGen/MachineCSE.cpp +++ b/llvm/lib/CodeGen/MachineCSE.cpp @@ -427,6 +427,9 @@ bool MachineCSE::isCSECandidate(MachineInstr *MI) { if (MI->getOpcode() == TargetOpcode::LOAD_STACK_GUARD) return false; + if (MI->isCalDerivedPtrInCangjieCopyGC(MRI)) { + return false; + } return true; } @@ -797,6 +800,9 @@ bool MachineCSE::isPRECandidate(MachineInstr *MI) { for (const auto &def : MI->defs()) if (!Register::isVirtualRegister(def.getReg())) return false; + if (MI->isCalDerivedPtrInCangjieCopyGC(MRI)) { + return false; + } for (const auto &use : MI->uses()) if (use.isReg() && !Register::isVirtualRegister(use.getReg())) diff --git a/llvm/lib/CodeGen/MachineInstr.cpp b/llvm/lib/CodeGen/MachineInstr.cpp index e92dec5be..3b5639db3 100644 --- a/llvm/lib/CodeGen/MachineInstr.cpp +++ b/llvm/lib/CodeGen/MachineInstr.cpp @@ -212,9 +212,13 @@ void MachineInstr::addOperand(MachineFunction &MF, const MachineOperand &Op) { // OpNo now points as the desired insertion point. Unless this is a variadic // instruction, only implicit regs are allowed beyond MCID->getNumOperands(). // RegMask operands go between the explicit and implicit operands. - assert((MCID->isVariadic() || OpNo < MCID->getNumOperands() || - Op.isValidExcessOperand()) && - "Trying to add an operand to a machine instr that is already done!"); + Triple ModuleTriple(MF.getFunction().getParent()->getTargetTriple()); + // 3750: AArch64::MRS + if (!(ModuleTriple.getArch() == Triple::aarch64 && getOpcode() == 3750)) { + assert((MCID->isVariadic() || OpNo < MCID->getNumOperands() || + Op.isValidExcessOperand()) && + "Trying to add an operand to a machine instr that is already done!"); + } MachineRegisterInfo *MRI = getRegInfo(); @@ -633,6 +637,31 @@ bool MachineInstr::isIdenticalTo(const MachineInstr &Other, return true; } +bool MachineInstr::isCalDerivedPtrInCangjieCopyGC( + MachineRegisterInfo *MRI) const { + Triple ModuleTriple(getMF()->getFunction().getParent()->getTargetTriple()); + // 325: X86::ADD64ri8 261: AArch64::ADDXri + if ((ModuleTriple.getArch() == Triple::x86_64 && getOpcode() == 325) || + (ModuleTriple.getArch() == Triple::aarch64 && getOpcode() == 261)) { + for (const MachineOperand &MO : operands()) { + if (!MO.isReg()) { + continue; + } + + Register Reg = MO.getReg(); + if (Reg == 0) { + continue; + } + // return true if it's gep from a ref ptr + for (auto &U : MRI->use_nodbg_operands(Reg)) { + if (U.getParent()->getOpcode() == TargetOpcode::STATEPOINT) { + return true; + } + } + } + } + return false; +} const MachineFunction *MachineInstr::getMF() const { return getParent()->getParent(); } diff --git a/llvm/lib/CodeGen/MachineLICM.cpp b/llvm/lib/CodeGen/MachineLICM.cpp index df7b6c782..abea68397 100644 --- a/llvm/lib/CodeGen/MachineLICM.cpp +++ b/llvm/lib/CodeGen/MachineLICM.cpp @@ -1007,6 +1007,12 @@ bool MachineLICMBase::IsLoopInvariantInst(MachineInstr &I) { LLVM_DEBUG(dbgs() << "LICM: Instruction not a LICM candidate\n"); return false; } + + // derived ptr need to be recalculated in copy gc + if (I.isCalDerivedPtrInCangjieCopyGC(MRI)) { + return false; + } + return CurLoop->isLoopInvariant(I); } diff --git a/llvm/lib/CodeGen/MachineOperand.cpp b/llvm/lib/CodeGen/MachineOperand.cpp index 46ad1de78..cb4fd29b2 100644 --- a/llvm/lib/CodeGen/MachineOperand.cpp +++ b/llvm/lib/CodeGen/MachineOperand.cpp @@ -853,10 +853,16 @@ void MachineOperand::print(raw_ostream &OS, ModuleSlotTracker &MST, case MachineOperand::MO_JumpTableIndex: OS << printJumpTableEntryReference(getIndex()); break; - case MachineOperand::MO_GlobalAddress: - getGlobal()->printAsOperand(OS, /*PrintType=*/false, MST); + case MachineOperand::MO_GlobalAddress: { + const GlobalValue *GV = getGlobal(); + if (!GV) { + OS << "\"\""; + } else { + GV->printAsOperand(OS, /*PrintType=*/false, MST); + } printOperandOffset(OS, getOffset()); break; + } case MachineOperand::MO_ExternalSymbol: { StringRef Name = getSymbolName(); OS << '&'; diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp index 85d051cfd..c40058d91 100644 --- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp +++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp @@ -71,13 +71,20 @@ using namespace llvm; #define DEBUG_TYPE "prologepilog" - +namespace llvm { +extern cl::opt CJPipeline; +} using MBBVector = SmallVector; STATISTIC(NumLeafFuncWithSpills, "Number of leaf functions with CSRs"); STATISTIC(NumFuncSeen, "Number of functions seen in PEI"); +static cl::opt +CJWarnStackSize("cj-warn-stack-size", + cl::desc("size of stack warning"), + cl::init(1 * 1024 * 1024)); // 1Mb + namespace { class PEI : public MachineFunctionPass { @@ -205,11 +212,22 @@ static void stashEntryDbgValues(MachineBasicBlock &MBB, MI->removeFromParent(); } +static void CJStackSizeWarning(const Function &F, unsigned StackSize) { + if (StackSize > CJWarnStackSize) { + DiagnosticInfoStackSize DiagStackSize(F, StackSize, CJWarnStackSize, DS_Warning); + F.getContext().diagnose(DiagStackSize); + } +} + /// runOnMachineFunction - Insert prolog/epilog code and replace abstract /// frame indexes with appropriate references. bool PEI::runOnMachineFunction(MachineFunction &MF) { NumFuncSeen++; const Function &F = MF.getFunction(); + if (F.hasFnAttribute("cj-fast-new-obj") && + (MF.getTarget().getTargetTriple().getArch() == Triple::x86_64)) { + return false; + } const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo(); const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering(); @@ -273,23 +291,29 @@ bool PEI::runOnMachineFunction(MachineFunction &MF) { MachineFrameInfo &MFI = MF.getFrameInfo(); uint64_t StackSize = MFI.getStackSize(); - unsigned Threshold = UINT_MAX; - if (MF.getFunction().hasFnAttribute("warn-stack-size")) { - bool Failed = MF.getFunction() - .getFnAttribute("warn-stack-size") - .getValueAsString() - .getAsInteger(10, Threshold); - // Verifier should have caught this. - assert(!Failed && "Invalid warn-stack-size fn attr value"); - (void)Failed; - } if (MF.getFunction().hasFnAttribute(Attribute::SafeStack)) { StackSize += MFI.getUnsafeStackSize(); } - if (StackSize > Threshold) { - DiagnosticInfoStackSize DiagStackSize(F, StackSize, Threshold, DS_Warning); - F.getContext().diagnose(DiagStackSize); + + if (F.hasCangjieGC()) { + CJStackSizeWarning(F, StackSize); + } else { + unsigned Threshold = UINT_MAX; + if (MF.getFunction().hasFnAttribute("warn-stack-size")) { + bool Failed = MF.getFunction() + .getFnAttribute("warn-stack-size") + .getValueAsString() + .getAsInteger(10, Threshold); + // Verifier should have caught this. + assert(!Failed && "Invalid warn-stack-size fn attr value"); + (void)Failed; + } + if (StackSize > Threshold) { + DiagnosticInfoStackSize DiagStackSize(F, StackSize, Threshold, DS_Warning); + F.getContext().diagnose(DiagStackSize); + } } + ORE->emit([&]() { return MachineOptimizationRemarkAnalysis(DEBUG_TYPE, "StackSize", MF.getFunction().getSubprogram(), @@ -475,6 +499,20 @@ static void assignCalleeSavedSpillSlots(MachineFunction &F, } CS.setFrameIdx(FrameIdx); + + if (F.getFunction().hasCangjieGC() && + F.getTarget().getTargetTriple().isARM() && + Reg == RegInfo->getFrameRegister(F)) { + assert(Reg == CSI[1].getReg()); + // 4: reg size + FrameIdx = MFI.CreateStackObject(4, Align(4), true); + // It ensures that the stack object is placed below the callee-saved + // area. + if ((unsigned)FrameIdx < MinCSFrameIndex) + MinCSFrameIndex = FrameIdx; + if ((unsigned)FrameIdx > MaxCSFrameIndex) + MaxCSFrameIndex = FrameIdx; + } } } @@ -1416,8 +1454,12 @@ void PEI::replaceFrameIndices(MachineBasicBlock *BB, MachineFunction &MF, "DBG_VALUE machine instruction"); Register Reg; MachineOperand &Offset = MI.getOperand(i + 1); - StackOffset refOffset = TFI->getFrameIndexReferencePreferSP( - MF, MI.getOperand(i).getIndex(), Reg, /*IgnoreSPUpdates*/ false); + StackOffset refOffset = CJPipeline + ? TFI->getFrameIndexRefForCJ( + MF, MI.getOperand(i).getIndex(), Reg) + : TFI->getFrameIndexReferencePreferSP( + MF, MI.getOperand(i).getIndex(), Reg, + /* IgnoreSPUpdates */ false); assert(!refOffset.getScalable() && "Frame offsets with a scalable component are not supported"); Offset.setImm(Offset.getImm() + refOffset.getFixed() + SPAdj); diff --git a/llvm/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp b/llvm/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp index aa9c77f9c..ef53e95d2 100644 --- a/llvm/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp @@ -32,6 +32,7 @@ #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" +#include "llvm/IR/Statepoint.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" @@ -44,13 +45,27 @@ using namespace llvm; /// PHI nodes or outside of the basic block that defines it, or used by a /// switch or atomic instruction, which may expand to multiple basic blocks. static bool isUsedOutsideOfDefiningBlock(const Instruction *I) { - if (I->use_empty()) return false; - if (isa(I)) return true; + if (I->use_empty()) + return false; + if (isa(I)) + return true; const BasicBlock *BB = I->getParent(); - for (const User *U : I->users()) - if (cast(U)->getParent() != BB || isa(U)) + for (const User *U : I->users()) { + if (cast(U)->getParent() != BB || isa(U)) { return true; - + } + // GCRelocate use value indirectly through Statepoint + if (isa(U)) { + for (const User *SU : U->users()) { + if (const auto *RelocateInst = dyn_cast(SU)) { + if (RelocateInst->getDerivedPtr() == I && + RelocateInst->getParent() != I->getParent()) { + return true; + } + } + } + } + } return false; } diff --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp index 3d3b504c6..30955668d 100644 --- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp @@ -24,11 +24,14 @@ #include "llvm/CodeGen/TargetLowering.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/Support/CommandLine.h" #include "llvm/IR/PseudoProbe.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Target/TargetMachine.h" using namespace llvm; - +namespace llvm { +extern cl::opt CJPipeline; +} #define DEBUG_TYPE "instr-emitter" /// MinRCSize - Smallest register class we allow when constraining virtual @@ -927,6 +930,18 @@ InstrEmitter::EmitDbgLabel(SDDbgLabel *SD) { return &*MIB; } +static bool isAArch64Machine(const Triple &TargetTriple) { + switch (TargetTriple.getArch()) { + default: + return false; + case Triple::aarch64: + case Triple::aarch64_be: + case Triple::aarch64_32: + return true; + } + report_fatal_error("program should not go here"); +} + /// EmitMachineNode - Generate machine code for a target-specific node and /// needed dependencies. /// @@ -991,7 +1006,8 @@ EmitMachineNode(SDNode *Node, bool IsClone, bool IsCloned, if (II.isVariadic()) assert(NumMIOperands >= II.getNumOperands() && "Too few operands for a variadic node!"); - else + // MRS instr may have two args in cangjie + else if (!(CJPipeline && TLI->isCangjieGetFPStateInstr(Opc))) assert(NumMIOperands >= II.getNumOperands() && NumMIOperands <= II.getNumOperands() + II.getNumImplicitDefs() + NumImpUses && @@ -1097,6 +1113,12 @@ EmitMachineNode(SDNode *Node, bool IsClone, bool IsCloned, } } + const TargetMachine &TM = MF->getTarget(); + if (Opc == TargetOpcode::STATEPOINT && + isAArch64Machine(TM.getTargetTriple()) && CJPipeline) { + UsedRegs.push_back(TLI->getRegisterByName("LR", LLT(), *MF)); + } + // Scan the glue chain for any used physregs. if (Node->getValueType(Node->getNumValues()-1) == MVT::Glue) { for (SDNode *F = Node->getGluedUser(); F; F = F->getGluedUser()) { diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp index e2173879c..0f52f5fb0 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp @@ -1722,7 +1722,7 @@ bool DAGTypeLegalizer::PromoteIntegerOperand(SDNode *N, unsigned OpNo) { case ISD::VP_REDUCE_UMIN: Res = PromoteIntOp_VP_REDUCE(N, OpNo); break; - + case ISD::GET_FP_STATE: Res = PromoteIntOp_GET_FP_STATE(N); break; case ISD::SET_ROUNDING: Res = PromoteIntOp_SET_ROUNDING(N); break; case ISD::STACKMAP: Res = PromoteIntOp_STACKMAP(N, OpNo); @@ -2334,6 +2334,11 @@ SDValue DAGTypeLegalizer::PromoteIntOp_VP_REDUCE(SDNode *N, unsigned OpNo) { return DAG.getNode(ISD::TRUNCATE, DL, VT, Reduce); } +SDValue DAGTypeLegalizer::PromoteIntOp_GET_FP_STATE(SDNode *N) { + SDValue Op = ZExtPromotedInteger(N->getOperand(1)); + return SDValue(DAG.UpdateNodeOperands(N, N->getOperand(0), Op), 0); +} + SDValue DAGTypeLegalizer::PromoteIntOp_SET_ROUNDING(SDNode *N) { SDValue Op = ZExtPromotedInteger(N->getOperand(1)); return SDValue(DAG.UpdateNodeOperands(N, N->getOperand(0), Op), 0); diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h index 6696b79cf..f21abc156 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h @@ -401,6 +401,7 @@ private: SDValue PromoteIntOp_FPOWI(SDNode *N); SDValue PromoteIntOp_VECREDUCE(SDNode *N); SDValue PromoteIntOp_VP_REDUCE(SDNode *N, unsigned OpNo); + SDValue PromoteIntOp_GET_FP_STATE(SDNode *N); SDValue PromoteIntOp_SET_ROUNDING(SDNode *N); SDValue PromoteIntOp_STACKMAP(SDNode *N, unsigned OpNo); SDValue PromoteIntOp_PATCHPOINT(SDNode *N, unsigned OpNo); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index ecdaef044..8611910c8 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -129,6 +129,10 @@ static cl::opt SwitchPeelThreshold( "switch statement. A value greater than 100 will void this " "optimization")); +namespace llvm { +extern cl::opt CJPipeline; +} + // Limit the width of DAG chains. This is important in general to prevent // DAG-based analysis from blowing up. For example, alias analysis and // load clustering may not complete in reasonable time. It is difficult to @@ -1482,6 +1486,27 @@ SDValue SelectionDAGBuilder::getCopyFromRegs(const Value *V, Type *Ty) { return Result; } +/// getValue - Return an SDValue for the given Value. +SDValue SelectionDAGBuilder::getValue(const Value *V, SDValue Chain) { + DenseMap::iterator It = FuncInfo.ValueMap.find(V); + SDValue Result; + + if (It != FuncInfo.ValueMap.end()) { + Register InReg = It->second; + + RegsForValue RFV(*DAG.getContext(), DAG.getTargetLoweringInfo(), + DAG.getDataLayout(), InReg, V->getType(), + None); // This is not an ABI copy. + Result = + RFV.getCopyFromRegs(DAG, FuncInfo, getCurSDLoc(), Chain, nullptr, V); + resolveDanglingDebugInfo(V, Result); + NodeMap[V] = Result; + return Result; + } else { + return getValue(V); + } +} + /// getValue - Return an SDValue for the given Value. SDValue SelectionDAGBuilder::getValue(const Value *V) { // If we already have an SDValue for this value, use it. It's important @@ -2897,7 +2922,7 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) { assert(!I.hasOperandBundlesOtherThan( {LLVMContext::OB_deopt, LLVMContext::OB_gc_transition, LLVMContext::OB_gc_live, LLVMContext::OB_funclet, - LLVMContext::OB_cfguardtarget, + LLVMContext::OB_struct_live, LLVMContext::OB_cfguardtarget, LLVMContext::OB_clang_arc_attachedcall}) && "Cannot lower invokes with arbitrary operand bundles yet!"); @@ -2920,6 +2945,7 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) { case Intrinsic::experimental_patchpoint_i64: visitPatchpoint(I, EHPadBB); break; + case Intrinsic::cj_gc_statepoint: case Intrinsic::experimental_gc_statepoint: LowerStatepoint(cast(I), EHPadBB); break; @@ -6749,7 +6775,31 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, } case Intrinsic::gcread: case Intrinsic::gcwrite: - llvm_unreachable("GC failed to lower gcread/gcwrite intrinsics!"); + case Intrinsic::cj_gcread_ref: + case Intrinsic::cj_gcread_struct: + case Intrinsic::cj_gcread_static_ref: + case Intrinsic::cj_gcread_static_struct: + case Intrinsic::cj_gcwrite_ref: + case Intrinsic::cj_gcwrite_struct: + case Intrinsic::cj_gcwrite_static_ref: + case Intrinsic::cj_gcwrite_static_struct: + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + case Intrinsic::cj_atomic_store: + case Intrinsic::cj_atomic_load: + case Intrinsic::cj_atomic_swap: + case Intrinsic::cj_atomic_compare_swap: + llvm_unreachable("GC failed to lower intrinsics"); + case Intrinsic::cj_get_fp_state: + Res = DAG.getNode(ISD::GET_FP_STATE, sdl, {MVT::i64, MVT::Other}, + {getRoot(), getValue(I.getArgOperand(0))}); + setValue(&I, Res); + return; + case Intrinsic::cj_reset_fp_state: + Res = DAG.getNode(ISD::RESET_FP_STATE, sdl, MVT::Other, getRoot()); + setValue(&I, Res); + DAG.setRoot(Res.getValue(0)); + return; case Intrinsic::flt_rounds: Res = DAG.getNode(ISD::FLT_ROUNDS_, sdl, {MVT::i32, MVT::Other}, getRoot()); setValue(&I, Res); @@ -6928,13 +6978,16 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, visitPatchpoint(I); return; case Intrinsic::experimental_gc_statepoint: + case Intrinsic::cj_gc_statepoint: LowerStatepoint(cast(I)); return; case Intrinsic::experimental_gc_result: + case Intrinsic::cj_gc_result: visitGCResult(cast(I)); return; case Intrinsic::experimental_gc_relocate: - visitGCRelocate(cast(I)); + case Intrinsic::cj_gc_relocate: + visitRelocate(cast(I)); return; case Intrinsic::instrprof_cover: llvm_unreachable("instrprof failed to lower a cover"); @@ -7737,6 +7790,44 @@ SelectionDAGBuilder::lowerInvokable(TargetLowering::CallLoweringInfo &CLI, return Result; } +bool SelectionDAGBuilder::needToDisableTailCall(const CallBase &CB, + const TargetLowering &TLI, + bool isMustTailCall) const { + // Avoid emitting tail calls in functions with the disable-tail-calls + // attribute. + const auto *Caller = CB.getParent()->getParent(); + if ((Caller->getFnAttribute("disable-tail-calls").getValueAsString() == + "true") && + !isMustTailCall) { + return true; + } + + const auto *CalleeFunc = CB.getCalledFunction(); + Triple::OSType Os = TLI.getTargetMachine().getTargetTriple().getOS(); + if (CalleeFunc != nullptr) { + if (Os == Triple::OSType::Win32) { + if (CalleeFunc->getName().isTruncToFP16Func() || + CalleeFunc->getName().isExtendFromFP16Func() || + CalleeFunc->hasFnAttribute("cj-runtime")) { + return true; + } + } else { + if (CalleeFunc->hasFnAttribute("cj-runtime")) { + return true; + } + } + } + + // We can't tail call inside a function with a swifterror argument. Lowering + // does not support this yet. It would have to move into the swifterror + // register before the call. + if (TLI.supportSwiftError() && + Caller->getAttributes().hasAttrSomewhere(Attribute::SwiftError)) { + return true; + } + return false; +} + void SelectionDAGBuilder::LowerCallTo(const CallBase &CB, SDValue Callee, bool isTailCall, bool isMustTailCall, @@ -7751,20 +7842,8 @@ void SelectionDAGBuilder::LowerCallTo(const CallBase &CB, SDValue Callee, const Value *SwiftErrorVal = nullptr; const TargetLowering &TLI = DAG.getTargetLoweringInfo(); - if (isTailCall) { - // Avoid emitting tail calls in functions with the disable-tail-calls - // attribute. - auto *Caller = CB.getParent()->getParent(); - if (Caller->getFnAttribute("disable-tail-calls").getValueAsString() == - "true" && !isMustTailCall) - isTailCall = false; - - // We can't tail call inside a function with a swifterror argument. Lowering - // does not support this yet. It would have to move into the swifterror - // register before the call. - if (TLI.supportSwiftError() && - Caller->getAttributes().hasAttrSomewhere(Attribute::SwiftError)) - isTailCall = false; + if (isTailCall && needToDisableTailCall(CB, TLI, isMustTailCall)) { + isTailCall = false; } for (auto I = CB.arg_begin(), E = CB.arg_end(); I != E; ++I) { @@ -9267,8 +9346,6 @@ void SelectionDAGBuilder::populateCallLoweringInfo( ArgI != ArgE; ++ArgI) { const Value *V = Call->getOperand(ArgI); - assert(!V->getType()->isEmptyTy() && "Empty type passed to intrinsic."); - TargetLowering::ArgListEntry Entry; Entry.Node = getValue(V); Entry.Ty = V->getType(); @@ -9276,13 +9353,30 @@ void SelectionDAGBuilder::populateCallLoweringInfo( Args.push_back(Entry); } - CLI.setDebugLoc(getCurSDLoc()) - .setChain(getRoot()) - .setCallee(Call->getCallingConv(), ReturnTy, Callee, std::move(Args)) - .setDiscardResult(Call->use_empty()) - .setIsPatchPoint(IsPatchPoint) - .setIsPreallocated( - Call->countOperandBundlesOfType(LLVMContext::OB_preallocated) != 0); + const Triple &TT = Triple(Call->getModule()->getTargetTriple()); + auto Statepoint = dyn_cast(Call); + bool IsCJMacArmCFFI = TT.isOSBinFormatMachO() && TT.isAArch64() && + Statepoint != nullptr && Statepoint->getActualCalledFunction() != nullptr && + Statepoint->getActualCalledFunction()->hasFnAttribute("cj2c"); + if (IsCJMacArmCFFI) { + CLI.setDebugLoc(getCurSDLoc()) + .setChain(getRoot()) + .setCallee( + ReturnTy, Statepoint->getActualCalledFunction()->getFunctionType(), + Callee, std::move(Args), *Call) + .setDiscardResult(Call->use_empty()) + .setIsPatchPoint(IsPatchPoint) + .setIsPreallocated( + Call->countOperandBundlesOfType(LLVMContext::OB_preallocated) != 0); + } else { + CLI.setDebugLoc(getCurSDLoc()) + .setChain(getRoot()) + .setCallee(Call->getCallingConv(), ReturnTy, Callee, std::move(Args)) + .setDiscardResult(Call->use_empty()) + .setIsPatchPoint(IsPatchPoint) + .setIsPreallocated( + Call->countOperandBundlesOfType(LLVMContext::OB_preallocated) != 0); + } } /// Add a stack map intrinsic call's live variable operands to a stackmap @@ -10237,6 +10331,11 @@ static void tryToElideArgumentCopy( << DebugStr(MFI.getObjectAlign(FixedIndex)) << ")\n"); return; } + if (CJPipeline && AI->getAllocatedType()->isStructTy()) { + LLVM_DEBUG(dbgs() << " argument copy elision failed: alloca type is a " + "struct\n"); + return; + } // Perform the elision. Delete the old stack object and replace its only use // in the variable info map. Mark the stack object as mutable. diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h index d1915fd4e..80aa07ecc 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h @@ -324,6 +324,7 @@ public: void resolveOrClearDbgInfo(); SDValue getValue(const Value *V); + SDValue getValue(const Value *V, SDValue Chain); SDValue getNonRegisterValue(const Value *V); SDValue getValueImpl(const Value *V); @@ -355,6 +356,8 @@ public: bool isExportableFromCurrentBlock(const Value *V, const BasicBlock *FromBB); void CopyToExportRegsIfNeeded(const Value *V); void ExportFromCurrentBlock(const Value *V); + bool needToDisableTailCall(const CallBase &CB, const TargetLowering &TLI, + bool IsMustTailCall) const; void LowerCallTo(const CallBase &CB, SDValue Callee, bool IsTailCall, bool IsMustTailCall, const BasicBlock *EHPadBB = nullptr); @@ -390,6 +393,9 @@ public: /// The full list of gc arguments to the gc.statepoint being lowered. ArrayRef GCArgs; + /// The list of struct arguments to the gc.statepoint being lowered. + ArrayRef StructArgs; + /// The gc.statepoint instruction. const Instruction *StatepointInstr = nullptr; @@ -417,6 +423,8 @@ public: /// invoke of gc.statepoint. const BasicBlock *EHPadBB = nullptr; + Function *ActualCalledFunction = nullptr; + explicit StatepointLoweringInfo(SelectionDAG &DAG) : CLI(DAG) {} }; @@ -586,7 +594,7 @@ private: void visitPatchpoint(const CallBase &CB, const BasicBlock *EHPadBB = nullptr); // These two are implemented in StatepointLowering.cpp - void visitGCRelocate(const GCRelocateInst &Relocate); + void visitRelocate(const GCProjectionInst &Relocate); void visitGCResult(const GCResultInst &I); void visitVectorReduce(const CallInst &I, unsigned Intrinsic); @@ -644,6 +652,10 @@ private: MCSymbol *&BeginLabel); SDValue lowerEndEH(SDValue Chain, const InvokeInst *II, const BasicBlock *EHPadBB, MCSymbol *BeginLabel); + void handleRelocationForVReg(SDValue SD, SDValue Relocated, + const Instruction *Relocate, + StatepointLoweringInfo &SI, + DenseMap &VirtRegs); }; /// This struct represents the registers (physical or virtual) diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp index 6ba01664e..bff0ecda4 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp @@ -422,6 +422,8 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const { return "call_alloc"; // Floating point environment manipulation + case ISD::GET_FP_STATE: return "get_fp_state"; + case ISD::RESET_FP_STATE: return "reset_fp_state"; case ISD::FLT_ROUNDS_: return "flt_rounds"; case ISD::SET_ROUNDING: return "set_rounding"; diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp index d46a0a23c..2d3908b11 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -1336,8 +1336,10 @@ void SelectionDAGISel::SelectAllBasicBlocks(const Function &Fn) { if (TM.Options.EnableFastISel) { LLVM_DEBUG(dbgs() << "Enabling fast-isel\n"); FastIS = TLI->createFastISel(*FuncInfo, LibInfo); - if (FastIS) + if (FastIS) { FastIS->useInstrRefDebugInfo(UseInstrRefDebugInfo); + SDB->StatepointLowering.setIsFastISel(true); + } } ReversePostOrderTraversal RPOT(&Fn); @@ -1492,7 +1494,7 @@ void SelectionDAGISel::SelectAllBasicBlocks(const Function &Fn) { // We cannot separate out GCrelocates to their own blocks since we need // to keep track of gc-relocates for a particular gc-statepoint. This is // done by SelectionDAGBuilder::LowerAsSTATEPOINT, called before - // visitGCRelocate. + // visitRelocate. if (isa(Inst) && !isa(Inst) && !isa(Inst) && !isa(Inst)) { OptimizationRemarkMissed R("sdagisel", "FastISelFailure", diff --git a/llvm/lib/CodeGen/SelectionDAG/StatepointLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/StatepointLowering.cpp index c5c093ae2..1c9a6aa23 100644 --- a/llvm/lib/CodeGen/SelectionDAG/StatepointLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/StatepointLowering.cpp @@ -55,7 +55,12 @@ #include using namespace llvm; +extern cl::opt EnableCalledSaveForStackMap; +namespace llvm { +extern cl::opt CJPipeline; +extern cl::opt EnableStackGrow; +} #define DEBUG_TYPE "statepoint-lowering" STATISTIC(NumSlotsAllocatedForStatepoints, @@ -88,8 +93,12 @@ static void pushStackMapConstant(SmallVectorImpl& Ops, void StatepointLoweringState::startNewStatepoint(SelectionDAGBuilder &Builder) { // Consistency check - assert(PendingGCRelocateCalls.empty() && - "Trying to visit statepoint before finished processing previous one"); +#ifndef NDEBUG + if (!IsFastISel) { + assert(PendingGCRelocateCalls.empty() && + "Trying to visit statepoint before finished processing previous one"); + } +#endif Locations.clear(); NextSlotToAllocate = 0; // Need to resize this on each safepoint - we need the two to stay in sync and @@ -102,8 +111,12 @@ void StatepointLoweringState::startNewStatepoint(SelectionDAGBuilder &Builder) { void StatepointLoweringState::clear() { Locations.clear(); AllocatedStackSlots.clear(); - assert(PendingGCRelocateCalls.empty() && - "cleared before statepoint sequence completed"); +#ifndef NDEBUG + if (!IsFastISel) { + assert(PendingGCRelocateCalls.empty() && + "cleared before statepoint sequence completed"); + } +#endif } SDValue @@ -167,8 +180,9 @@ static Optional findPreviousSpillSlot(const Value *Val, if (LookUpDepth <= 0) return None; - // Spill location is known for gc relocates - if (const auto *Relocate = dyn_cast(Val)) { + // Spill location is known for gc/stack relocates + if (isa(Val)) { + const auto *Relocate = dyn_cast(Val); const Value *Statepoint = Relocate->getStatepoint(); assert((isa(Statepoint) || isa(Statepoint)) && "GetStatepoint must return one of two types"); @@ -351,6 +365,9 @@ static std::pair lowerCallFromStatepointLoweringInfo( while (CallEnd->getOpcode() == ISD::CopyFromReg) CallEnd = CallEnd->getOperand(0).getNode(); } + if (CallEnd->getOpcode() == ISD::TokenFactor) { + CallEnd = CallEnd->getOperand(0).getOperand(0).getNode(); + } assert(CallEnd->getOpcode() == ISD::CALLSEQ_END && "expected!"); return std::make_pair(ReturnValue, CallEnd->getOperand(0).getNode()); @@ -418,6 +435,28 @@ spillIncomingStatepointValue(SDValue Incoming, SDValue Chain, return std::make_tuple(Loc, Chain, MMO); } +using FieldInfo = std::pair; + +static void +lowerStructFieldsValue(SmallVector &Fields, + SmallVectorImpl &Ops, + SelectionDAGBuilder &Builder, + DenseMap &AllocaPtrsIndexMap) { + SDLoc L = Builder.getCurSDLoc(); + pushStackMapConstant(Ops, Builder, Fields.size()); + for (auto &P : Fields) { + SDValue PtrSD = Builder.getValue(P.first); + assert(AllocaPtrsIndexMap.count(PtrSD) && "struct not found in index map"); + const DataLayout &DL = Builder.DAG.getDataLayout(); + APInt Offsets(DL.getIndexSizeInBits(0), P.second, true); + auto VT = + Builder.DAG.getTarget().getTargetTriple().isARM() ? MVT::i32 : MVT::i64; + Ops.push_back( + Builder.DAG.getTargetConstant(AllocaPtrsIndexMap[PtrSD], L, VT)); + Ops.push_back(Builder.DAG.getTargetConstant(Offsets, L, VT)); + } +} + /// Lower a single value incoming to a statepoint node. This value can be /// either a deopt value or a gc value, the handling is the same. We special /// case constants and allocas, then fall back to spilling if required. @@ -426,7 +465,6 @@ lowerIncomingStatepointValue(SDValue Incoming, bool RequireSpillSlot, SmallVectorImpl &Ops, SmallVectorImpl &MemRefs, SelectionDAGBuilder &Builder) { - if (willLowerDirectly(Incoming)) { if (FrameIndexSDNode *FI = dyn_cast(Incoming)) { // This handles allocas as arguments to the statepoint (this is only @@ -470,8 +508,6 @@ lowerIncomingStatepointValue(SDValue Incoming, bool RequireSpillSlot, llvm_unreachable("unhandled direct lowering case"); } - - if (!RequireSpillSlot) { // If this value is live in (not live-on-return, or live-through), we can // treat it the same way patchpoint treats it's "live in" values. We'll @@ -495,9 +531,39 @@ lowerIncomingStatepointValue(SDValue Incoming, bool RequireSpillSlot, Chain = std::get<1>(Res);; Builder.DAG.setRoot(Chain); } +} +static SDValue lowerToSpillSlot(SmallVectorImpl &MemRefs, + SDValue SDV, SelectionDAGBuilder &Builder) { + SDValue Chain = Builder.getRoot(); + auto Res = spillIncomingStatepointValue(SDV, Chain, Builder); + if (auto *MMO = std::get<2>(Res)) + MemRefs.push_back(MMO); + Chain = std::get<1>(Res); + Builder.DAG.setRoot(Chain); + return std::get<0>(Res); } +struct LowerStackInfo { + SelectionDAGBuilder::StatepointLoweringInfo &SI; + SmallVectorImpl &Ops; + SelectionDAGBuilder &Builder; + SmallVectorImpl &MemRefs; + + LowerStackInfo(SelectionDAGBuilder::StatepointLoweringInfo &SI, + SmallVectorImpl &Ops, SelectionDAGBuilder &Builder, + SmallVectorImpl &MemRefs) + : SI(SI), Ops(Ops), Builder(Builder), MemRefs(MemRefs) {} + ~LowerStackInfo() = default; + + void lowerStackValue(SmallSetVector &LoweredStackPtrs) { + pushStackMapConstant(Ops, Builder, LoweredStackPtrs.size()); + for (auto SDV : LoweredStackPtrs) { + Ops.push_back(lowerToSpillSlot(MemRefs, SDV, Builder)); + } + } +}; + /// Return true if value V represents the GC value. The behavior is conservative /// in case it is not sure that value is not GC the function returns true. static bool isGCValue(const Value *V, SelectionDAGBuilder &Builder) { @@ -527,6 +593,7 @@ lowerStatepointMetaArgs(SmallVectorImpl &Ops, // Lower the deopt and gc arguments for this statepoint. Layout will be: // deopt argument length, deopt arguments.., gc arguments... #ifndef NDEBUG + if (auto *GFI = Builder.GFI) { // Check that each of the gc pointer and bases we've gotten out of the // safepoint is something the strategy thinks might be a pointer (or vector @@ -555,6 +622,17 @@ lowerStatepointMetaArgs(SmallVectorImpl &Ops, } #endif + auto Callee = SI.ActualCalledFunction; + if (Callee && Callee->isCangjieStackCheck()) { + pushStackMapConstant(Ops, Builder, 0); // + pushStackMapConstant(Ops, Builder, 0); // + pushStackMapConstant(Ops, Builder, 0); // + pushStackMapConstant(Ops, Builder, 0); // + pushStackMapConstant(Ops, Builder, 0); // + pushStackMapConstant(Ops, Builder, 0); // + return; + } + // Figure out what lowering strategy we're going to use for each part // Note: Is is conservatively correct to lower both "live-in" and "live-out" // as "live-through". A "live-through" variable is one which is "live-in", @@ -591,6 +669,8 @@ lowerStatepointMetaArgs(SmallVectorImpl &Ops, SmallSetVector LoweredGCPtrs; // Map lowered GC Pointer value to the index in above vector DenseMap GCPtrIndexMap; + // List of unique lowered Stack Pointer values. + SmallSetVector LoweredStackPtrs; unsigned CurNumVRegs = 0; @@ -692,8 +772,32 @@ lowerStatepointMetaArgs(SmallVectorImpl &Ops, lowerIncomingStatepointValue(SDV, !LowerAsVReg.count(SDV), Ops, MemRefs, Builder); - // Copy to out vector. LoweredGCPtrs will be empty after this point. - GCPtrs = LoweredGCPtrs.takeVector(); + // List of unique alloca ptr values. + SmallSetVector AllocaPtrs; + // Map lowered Alloca value to the index in above vector + DenseMap AllocaPtrsIndexMap; + SmallVector Fields; + const DataLayout &DL = Builder.DAG.getDataLayout(); + + auto recordAllocaPtr = [&](const Value *V) { + SDValue PtrSD = Builder.getValue(V); + if (!AllocaPtrs.insert(PtrSD)) + return; // skip duplicates + AllocaPtrsIndexMap[PtrSD] = AllocaPtrs.size() - 1; + }; + + for (Value *V : SI.StructArgs) { + Value *Base = nullptr; + if (isa(V)) { + Base = V; + Fields.push_back(std::make_pair(Base, -1)); + } else { + APInt Offsets(DL.getIndexSizeInBits(0), 0); + Base = V->stripAndAccumulateConstantOffsets(DL, Offsets, false); + Fields.push_back(std::make_pair(Base, Offsets.getZExtValue())); + } + recordAllocaPtr(Base); + } // If there are any explicit spill slots passed to the statepoint, record // them, but otherwise do not do anything special. These are user provided @@ -701,11 +805,10 @@ lowerStatepointMetaArgs(SmallVectorImpl &Ops, // it is the contents of the slot which may get updated, not the pointer to // the alloca SmallVector Allocas; - for (Value *V : SI.GCArgs) { - SDValue Incoming = Builder.getValue(V); - if (FrameIndexSDNode *FI = dyn_cast(Incoming)) { + auto processAlloca = [&](SDValue &SDV) { + if (FrameIndexSDNode *FI = dyn_cast(SDV)) { // This handles allocas as arguments to the statepoint - assert(Incoming.getValueType() == Builder.getFrameIndexTy() && + assert(SDV.getValueType() == Builder.getFrameIndexTy() && "Incoming value is a frame index!"); Allocas.push_back(Builder.DAG.getTargetFrameIndex( FI->getIndex(), Builder.getFrameIndexTy())); @@ -714,7 +817,22 @@ lowerStatepointMetaArgs(SmallVectorImpl &Ops, auto *MMO = getMachineMemOperand(MF, *FI); MemRefs.push_back(MMO); } + }; + + for (Value *V : SI.GCArgs) { + SDValue PtrSD = Builder.getValue(V); + processAlloca(PtrSD); + } + + for (SDValue SDV : AllocaPtrs) { + if (isa(SDV)) { + processAlloca(SDV); + } else { + // If the alloca is not an FI, it is a relocated value, record it. + Allocas.push_back(lowerToSpillSlot(MemRefs, SDV, Builder)); + } } + pushStackMapConstant(Ops, Builder, Allocas.size()); Ops.append(Allocas.begin(), Allocas.end()); @@ -731,6 +849,47 @@ lowerStatepointMetaArgs(SmallVectorImpl &Ops, Ops.push_back( Builder.DAG.getTargetConstant(GCPtrIndexMap[Derived], L, MVT::i64)); } + + // Finally, we record the fields and stack ptrs. + if (CJPipeline) { + lowerStructFieldsValue(Fields, Ops, Builder, AllocaPtrsIndexMap); + if (EnableStackGrow) { + LowerStackInfo Info(SI, Ops, Builder, MemRefs); + Info.lowerStackValue(LoweredStackPtrs); + } + } + + // Copy to out vector. LoweredGCPtrs will be empty after this point. + GCPtrs = LoweredGCPtrs.takeVector(); +} + +void SelectionDAGBuilder::handleRelocationForVReg( + SDValue SD, SDValue Relocated, const Instruction *Relocate, + StatepointLoweringInfo &SI, DenseMap &VirtRegs) { + // Handle local relocate. Note that different relocates might + // map to the same SDValue. + if (SI.StatepointInstr->getParent() == Relocate->getParent()) { + SDValue Res = StatepointLowering.getLocation(SD); + if (Res) + assert(Res == Relocated); + else + StatepointLowering.setLocation(SD, Relocated); + return; + } + + // Handle multiple gc.relocates of the same input efficiently. + if (VirtRegs.count(SD)) + return; + + auto *RetTy = Relocate->getType(); + Register Reg = FuncInfo.CreateRegs(RetTy); + RegsForValue RFV(*DAG.getContext(), DAG.getTargetLoweringInfo(), + DAG.getDataLayout(), Reg, RetTy, None); + SDValue Chain = DAG.getRoot(); + RFV.getCopyToRegs(Relocated, DAG, getCurSDLoc(), Chain, nullptr); + PendingExports.push_back(Chain); + + VirtRegs[SD] = Reg; } SDValue SelectionDAGBuilder::LowerAsSTATEPOINT( @@ -744,6 +903,11 @@ SDValue SelectionDAGBuilder::LowerAsSTATEPOINT( StatepointLowering.startNewStatepoint(*this); assert(SI.Bases.size() == SI.Ptrs.size()); + if (EnableCalledSaveForStackMap) { + MaxRegistersForGCPointers = 64; + UseRegistersForGCPointersInLandingPad = true; + } + LLVM_DEBUG(dbgs() << "Lowering statepoint " << *SI.StatepointInstr << "\n"); #ifndef NDEBUG for (const auto *Reloc : SI.GCRelocates) @@ -839,7 +1003,7 @@ SDValue SelectionDAGBuilder::LowerAsSTATEPOINT( Ops.push_back(DAG.getTargetConstant(NumCallRegArgs, getCurSDLoc(), MVT::i32)); // Add call target - SDValue CallTarget = SDValue(CallNode->getOperand(1).getNode(), 0); + SDValue CallTarget = CallNode->getOperand(1); Ops.push_back(CallTarget); // Add call arguments @@ -902,39 +1066,14 @@ SDValue SelectionDAGBuilder::LowerAsSTATEPOINT( continue; SDValue Relocated = SDValue(StatepointMCNode, LowerAsVReg[SD]); - - // Handle local relocate. Note that different relocates might - // map to the same SDValue. - if (SI.StatepointInstr->getParent() == Relocate->getParent()) { - SDValue Res = StatepointLowering.getLocation(SD); - if (Res) - assert(Res == Relocated); - else - StatepointLowering.setLocation(SD, Relocated); - continue; - } - - // Handle multiple gc.relocates of the same input efficiently. - if (VirtRegs.count(SD)) - continue; - - auto *RetTy = Relocate->getType(); - Register Reg = FuncInfo.CreateRegs(RetTy); - RegsForValue RFV(*DAG.getContext(), DAG.getTargetLoweringInfo(), - DAG.getDataLayout(), Reg, RetTy, None); - SDValue Chain = DAG.getRoot(); - RFV.getCopyToRegs(Relocated, DAG, getCurSDLoc(), Chain, nullptr); - PendingExports.push_back(Chain); - - VirtRegs[SD] = Reg; + handleRelocationForVReg(SD, Relocated, Relocate, SI, VirtRegs); } // Record for later use how each relocation was lowered. This is needed to // allow later gc.relocates to mirror the lowering chosen. const Instruction *StatepointInstr = SI.StatepointInstr; auto &RelocationMap = FuncInfo.StatepointRelocationMaps[StatepointInstr]; - for (const GCRelocateInst *Relocate : SI.GCRelocates) { - const Value *V = Relocate->getDerivedPtr(); + auto RelocationRecord = [&](const Value *V, const Instruction *Relocate) { SDValue SDV = getValue(V); SDValue Loc = StatepointLowering.getLocation(SDV); @@ -961,9 +1100,11 @@ SDValue SelectionDAGBuilder::LowerAsSTATEPOINT( ExportFromCurrentBlock(V); } RelocationMap[Relocate] = Record; - } + }; - + for (const GCRelocateInst *Relocate : SI.GCRelocates) { + RelocationRecord(Relocate->getDerivedPtr(), Relocate); + } SDNode *SinkNode = StatepointMCNode; @@ -1050,7 +1191,7 @@ SelectionDAGBuilder::LowerStatepoint(const GCStatepointInst &I, SDValue ActualCallee; SDValue Callee = getValue(I.getActualCalledOperand()); - + bool VarArg = false; if (I.getNumPatchBytes() > 0) { // If we've been asked to emit a nop sequence instead of a call instruction // for this statepoint then don't lower the call target, but use a constant @@ -1060,13 +1201,19 @@ SelectionDAGBuilder::LowerStatepoint(const GCStatepointInst &I, ActualCallee = DAG.getUNDEF(Callee.getValueType()); } else { ActualCallee = Callee; + auto *FTy = dyn_cast_or_null( + dyn_cast_or_null(I.getActualCalledOperand()->getType()) + ->getElementType()); + if (FTy != nullptr) { + VarArg = FTy->isVarArg(); + } } StatepointLoweringInfo SI(DAG); populateCallLoweringInfo(SI.CLI, &I, GCStatepointInst::CallArgsBeginPos, I.getNumCallArgs(), ActualCallee, I.getActualReturnType(), false /* IsPatchPoint */); - + SI.CLI.setVarArg(VarArg); // There may be duplication in the gc.relocate list; such as two copies of // each relocation on normal and exceptional path for an invoke. We only // need to spill once and record one copy in the stackmap, but we need to @@ -1085,6 +1232,14 @@ SelectionDAGBuilder::LowerStatepoint(const GCStatepointInst &I, SI.GCRelocates.push_back(Relocate); SDValue DerivedSD = getValue(Relocate->getDerivedPtr()); + SDValue BaseSD = getValue(Relocate->getBasePtr()); + if (I.getParent()->getParent()->hasCangjieGC()) { + if (ConstantSDNode *Base = dyn_cast(BaseSD)) { + if (Base->getZExtValue() == 0) + continue; + } + } + if (Seen.insert(DerivedSD).second) { SI.Bases.push_back(Relocate->getBasePtr()); SI.Ptrs.push_back(Relocate->getDerivedPtr()); @@ -1107,6 +1262,8 @@ SelectionDAGBuilder::LowerStatepoint(const GCStatepointInst &I, } SI.GCArgs = ArrayRef(I.gc_args_begin(), I.gc_args_end()); + SI.StructArgs = ArrayRef(I.struct_args_begin(), + I.struct_args_end()); SI.StatepointInstr = &I; SI.ID = I.getID(); @@ -1118,12 +1275,21 @@ SelectionDAGBuilder::LowerStatepoint(const GCStatepointInst &I, SI.NumPatchBytes = I.getNumPatchBytes(); SI.EHPadBB = EHPadBB; - SDValue ReturnValue = LowerAsSTATEPOINT(SI); + SI.ActualCalledFunction = I.getActualCalledFunction(); + SDValue ReturnValue = LowerAsSTATEPOINT(SI); + if (CJPipeline) { + // Inform the Frame Information that we have a stackmap in this function. + // Otherwise, the optimizer will omit the frame poiner. + // See: AArch64FrameLowering.cpp:hasFP() + // See: X86FrameLowering.cpp:hasFP() + DAG.getMachineFunction().getFrameInfo().setHasStackMap(); + } // Export the result value if needed const auto GCResultLocality = getGCResultLocality(I); - if (!GCResultLocality.first && !GCResultLocality.second) { + if ((!GCResultLocality.first && !GCResultLocality.second) || + (I.getActualReturnType()->isEmptyTy())) { // The return value is not needed, just generate a poison value. // Note: This covers the void return case. setValue(&I, DAG.getIntPtrConstant(-1, getCurSDLoc())); @@ -1225,7 +1391,28 @@ void SelectionDAGBuilder::visitGCResult(const GCResultInst &CI) { setValue(&CI, CopyFromReg); } -void SelectionDAGBuilder::visitGCRelocate(const GCRelocateInst &Relocate) { +// set value with chain if GCRelocate is used by GEP which generates derived ptr +static bool trySetValueWithChainIfNeeded(SelectionDAGBuilder *SDB, + const GCRelocateInst &Relocate, + const Value *DerivedPtr) { + for (const User *U : Relocate.users()) { + if (isa(U)) { + SDValue Chain = SDB->getRoot(); + const auto *OriRelocate = dyn_cast(DerivedPtr); + // get origional relocate + while (OriRelocate != nullptr && + OriRelocate->getParent() == Relocate.getParent()) { + DerivedPtr = OriRelocate->getDerivedPtr(); + OriRelocate = dyn_cast(DerivedPtr); + } + SDB->setValue(&Relocate, SDB->getValue(DerivedPtr, Chain)); + return true; + } + } + return false; +} + +void SelectionDAGBuilder::visitRelocate(const GCProjectionInst &Relocate) { const Value *Statepoint = Relocate.getStatepoint(); #ifndef NDEBUG // Consistency check @@ -1237,15 +1424,23 @@ void SelectionDAGBuilder::visitGCRelocate(const GCRelocateInst &Relocate) { if (isa(Statepoint)) return; - if (cast(Statepoint)->getParent() == Relocate.getParent()) - StatepointLowering.relocCallVisited(Relocate); + if (auto GCReloc = dyn_cast(&Relocate)) { + if (cast(Statepoint)->getParent() == GCReloc->getParent()) + StatepointLowering.relocCallVisited(*GCReloc); - auto *Ty = Relocate.getType()->getScalarType(); - if (auto IsManaged = GFI->getStrategy().isGCManagedPointer(Ty)) - assert(*IsManaged && "Non gc managed pointer relocated!"); + auto *Ty = GCReloc->getType()->getScalarType(); + if (auto IsManaged = GFI->getStrategy().isGCManagedPointer(Ty)) + assert(*IsManaged && "Non gc managed pointer relocated!"); + } #endif - const Value *DerivedPtr = Relocate.getDerivedPtr(); + const Value *RelocatePtr = nullptr; + if (isa(Relocate)) { + RelocatePtr = cast(Relocate).getDerivedPtr(); + } else { + report_fatal_error("Invalid relocate type!"); + } + auto &RelocationMap = FuncInfo.StatepointRelocationMaps[cast(Statepoint)]; auto SlotIt = RelocationMap.find(&Relocate); @@ -1257,7 +1452,7 @@ void SelectionDAGBuilder::visitGCRelocate(const GCRelocateInst &Relocate) { assert(cast(Statepoint)->getParent() == Relocate.getParent() && "Nonlocal gc.relocate mapped via SDValue"); - SDValue SDV = StatepointLowering.getLocation(getValue(DerivedPtr)); + SDValue SDV = StatepointLowering.getLocation(getValue(RelocatePtr)); assert(SDV.getNode() && "empty SDValue"); setValue(&Relocate, SDV); return; @@ -1310,8 +1505,7 @@ void SelectionDAGBuilder::visitGCRelocate(const GCRelocateInst &Relocate) { } assert(Record.type == RecordType::NoRelocate); - SDValue SD = getValue(DerivedPtr); - + SDValue SD = getValue(RelocatePtr); if (SD.isUndef() && SD.getValueType().getSizeInBits() <= 64) { // Lowering relocate(undef) as arbitrary constant. Current constant value // is chosen such that it's unlikely to be a valid pointer. @@ -1321,7 +1515,14 @@ void SelectionDAGBuilder::visitGCRelocate(const GCRelocateInst &Relocate) { // We didn't need to spill these special cases (constants and allocas). // See the handling in spillIncomingValueForStatepoint for detail. - setValue(&Relocate, SD); + if (isa(Relocate)) { + if (!trySetValueWithChainIfNeeded(this, cast(Relocate), + RelocatePtr)) { + setValue(&Relocate, SD); + } + } else { + report_fatal_error("Invalid relocate type!"); + } } void SelectionDAGBuilder::LowerDeoptimizeCall(const CallInst *CI) { diff --git a/llvm/lib/CodeGen/SelectionDAG/StatepointLowering.h b/llvm/lib/CodeGen/SelectionDAG/StatepointLowering.h index addc0a7ee..6d4528ac4 100644 --- a/llvm/lib/CodeGen/SelectionDAG/StatepointLowering.h +++ b/llvm/lib/CodeGen/SelectionDAG/StatepointLowering.h @@ -60,6 +60,10 @@ public: Locations[Val] = Location; } + void setIsFastISel(bool Flag) { + IsFastISel = Flag; + } + /// Record the fact that we expect to encounter a given gc_relocate /// before the next statepoint. If we don't see it, we'll report /// an assertion. @@ -119,6 +123,10 @@ private: /// Keep track of pending gcrelocate calls for consistency check SmallVector PendingGCRelocateCalls; + + /// In FastISel, PendingGCRelocateCalls's insert and erase will not match. + /// We should remove the incorrect assertion for PendingGCRelocateCalls. + bool IsFastISel = false; }; } // end namespace llvm diff --git a/llvm/lib/CodeGen/ShrinkWrap.cpp b/llvm/lib/CodeGen/ShrinkWrap.cpp index f6ad2b50a..673e58614 100644 --- a/llvm/lib/CodeGen/ShrinkWrap.cpp +++ b/llvm/lib/CodeGen/ShrinkWrap.cpp @@ -86,7 +86,9 @@ #include using namespace llvm; - +namespace llvm { +extern cl::opt CJPipeline; +} #define DEBUG_TYPE "shrink-wrap" STATISTIC(NumFunc, "Number of functions"); @@ -584,7 +586,10 @@ bool ShrinkWrap::runOnMachineFunction(MachineFunction &MF) { << Restore->getName() << '\n'); MachineFrameInfo &MFI = MF.getFrameInfo(); - MFI.setSavePoint(Save); + // cangjie require prologue to be inserted at the entry BB for unwind reason + if (!CJPipeline) { + MFI.setSavePoint(Save); + } MFI.setRestorePoint(Restore); ++NumCandidates; return false; diff --git a/llvm/lib/CodeGen/SplitKit.cpp b/llvm/lib/CodeGen/SplitKit.cpp index 94149f56e..2e22299ea 100644 --- a/llvm/lib/CodeGen/SplitKit.cpp +++ b/llvm/lib/CodeGen/SplitKit.cpp @@ -174,8 +174,12 @@ void SplitAnalysis::analyzeUses() { // Get use slots form the use-def chain. const MachineRegisterInfo &MRI = MF.getRegInfo(); for (MachineOperand &MO : MRI.use_nodbg_operands(CurLI->reg())) - if (!MO.isUndef()) + // We don't want to split in the statepiont instruction point, + // so we don't put it in the UseSlots here. + if (!MO.isUndef() && + MO.getParent()->getOpcode() != TargetOpcode::STATEPOINT) { UseSlots.push_back(LIS.getInstructionIndex(*MO.getParent()).getRegSlot()); + } array_pod_sort(UseSlots.begin(), UseSlots.end()); @@ -223,9 +227,34 @@ void SplitAnalysis::calcLiveBlockInfo() { if (UseI == UseE || *UseI >= Stop) { ++NumThroughBlocks; ThroughBlocks.set(BI.MBB->getNumber()); - // The range shouldn't end mid-block if there are no uses. This shouldn't - // happen. - assert(LVI->end >= Stop && "range ends mid block with no uses"); + if (LVI->end < Stop) { + // The range shouldn't end mid-block if there are no uses and don`t + // end in statepoint. This shouldn't happen. + if (LIS.getInstructionFromIndex(LVI->end)->getOpcode() != + TargetOpcode::STATEPOINT) { + llvm_unreachable("range ends mid block with no uses"); + return; + } + // When the LVI ends with a statepoint, it may occur in mid-block. + // We do not want to split the LVI at the statepoint. In this case, + // the current LVI search ends, and the next LVI continues. + // 7200B bb1.start + // 7344B STATEPOINT %3 ... # LVI->end + // 7584B bb1.end # Stop + // 7952B bb2.start + // 7968B MOV64rm %3... # UseI + // 8176B bb2.end + if (++LVI == LVE) { + break; + } + // Pick the next basic block. + if (LVI->start < Stop) { + ++MFI; + } else { + MFI = LIS.getMBBFromIndex(LVI->start)->getIterator(); + } + continue; + } } else { // This block has uses. Find the first and last uses in the block. BI.FirstInstr = *UseI; diff --git a/llvm/lib/CodeGen/StackMaps.cpp b/llvm/lib/CodeGen/StackMaps.cpp index ccaff862f..6b82664a4 100644 --- a/llvm/lib/CodeGen/StackMaps.cpp +++ b/llvm/lib/CodeGen/StackMaps.cpp @@ -11,14 +11,22 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Twine.h" #include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/GCMetadata.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/MachineOperand.h" +#include "llvm/CodeGen/StackMapEncode.h" +#include "llvm/CodeGen/TargetFrameLowering.h" #include "llvm/CodeGen/TargetOpcodes.h" #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Statepoint.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCObjectFileInfo.h" @@ -33,6 +41,9 @@ #include #include #include +#include +#include +#include #include using namespace llvm; @@ -43,8 +54,114 @@ static cl::opt StackMapVersion( "stackmap-version", cl::init(3), cl::Hidden, cl::desc("Specify the stackmap encoding version (default = 3)")); -const char *StackMaps::WSMP = "Stack Maps: "; +static cl::opt EnableCompressedBitMap( + "enable-compressed-bitmap", cl::init(true), cl::Hidden, + cl::desc("Enable Compressed BitMap")); +namespace llvm { +// struct start address alignment +int32_t OffsetStepSize = 8; +extern cl::opt CJPipeline; +extern cl::opt EnableStackGrow; +} // namespace llvm +const char *StackMaps::WSMP = "Stack Maps: "; +namespace { +enum CJStackMapFormat : uint64_t { + CJ_STACKMAP_BITMAP = 0, + CJ_STACKMAP_COMPRESSED_BITMAP = 1 +}; +const int32_t RawDataWidth = 31; // for compressed stack map + +// unordered_map, regNo can be used to find callee saved reg +// while bitIdx is used in prologue +const std::unordered_map X86CalleeSavedReg = { + // rbx, r12 - r15 + {3, 1 << 0}, {12, 1 << 1}, {13, 1 << 2}, {14, 1 << 3}, {15, 1 << 4}, +}; +const std::vector X86PrologueBit2Reg = {"rbx", "r12", "r13", "r14", + "r15"}; +const std::unordered_map X86WinCalleeSavedReg = { + // rbx, rsi, rdi, r12 - r15, xmm6 - xmm15 + {3, 1 << 0}, {4, 1 << 1}, {5, 1 << 2}, {12, 1 << 3}, {13, 1 << 4}, + {14, 1 << 5}, {15, 1 << 6}, {23, 1 << 7}, {24, 1 << 8}, {25, 1 << 9}, + {26, 1 << 10}, {27, 1 << 11}, {28, 1 << 12}, {29, 1 << 13}, {30, 1 << 14}, + {31, 1 << 15}, {32, 1 << 16}, +}; +const std::vector X86WinPrologueBit2Reg = { + "rbx", "rsi", "rdi", "r12", "r13", "r14", "r15", "xmm6", "xmm7", + "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"}; +const std::unordered_map AArch64CalleeSavedReg = { + // x19 - x30 + {19, 1 << 0}, + {20, 1 << 1}, + {21, 1 << 2}, + {22, 1 << 3}, + {23, 1 << 4}, + {24, 1 << 5}, + {25, 1 << 6}, + {26, 1 << 7}, + {27, 1 << 8}, + {28, 1 << 9}, + {29, 1 << 10}, + {30, 1 << 11}, + // D8 - D15 + {72, 1 << 12}, + {73, 1 << 13}, + {74, 1 << 14}, + {75, 1 << 15}, + {76, 1 << 16}, + {77, 1 << 17}, + {78, 1 << 18}, + {79, 1 << 19}, +}; +const std::vector AArch64PrologueBit2Reg = { + "x19", "x20", "x21", "x22", "x23", "x24", "x25", + "x26", "x27", "x28", "x29(fp)", "x30(lr)", "d8", "d9", + "d10", "d11", "d12", "d13", "d14", "d15", +}; + +const std::vector X86Bit2Reg = { + "rax", "rdx", "rcx", "rbx", + "rsi", "rdi", "rbp", "rsp", // 0-7 + "r8", "r9", "r10", "r11", + "r12", "r13", "r14", "r15", // 8-15 + "rip", "xmm0", "xmm1", "xmm2", + "xmm3", "xmm4", "xmm5", "xmm6", // 16-23 + "xmm7", "xmm8", "xmm9", "xmm10", + "xmm11", "xmm12", "xmm13", "xmm14", // 24-31 + "xmm15"}; +const std::vector AArch64Bit2Reg = { + "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", + "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", + "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31"}; +const std::unordered_map ARMCalleeSavedReg = { + // r4 - r11, r14 + {4, 1 << 0}, + {5, 1 << 1}, + {6, 1 << 2}, + {7, 1 << 3}, + {8, 1 << 4}, + {9, 1 << 5}, + {10, 1 << 6}, + {11, 1 << 7}, + {14, 1 << 8}, + // d8 - d15 + {264, 1 << 9}, + {265, 1 << 10}, + {266, 1 << 11}, + {267, 1 << 12}, + {268, 1 << 13}, + {269, 1 << 14}, + {270, 1 << 15}, + {271, 1 << 16}, +}; +const std::vector ARMPrologueBit2Reg = { + "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r14(lr)", + "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", }; +const std::vector ARMBit2Reg = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", + "r11", "r12", "r13", "r14", "r15"}; +} // namespace static uint64_t getConstMetaVal(const MachineInstr &MI, unsigned Idx) { assert(MI.getOperand(Idx).isImm() && MI.getOperand(Idx).getImm() == StackMaps::ConstantOp); @@ -53,10 +170,8 @@ static uint64_t getConstMetaVal(const MachineInstr &MI, unsigned Idx) { return MO.getImm(); } -StackMapOpers::StackMapOpers(const MachineInstr *MI) - : MI(MI) { - assert(getVarIdx() <= MI->getNumOperands() && - "invalid stackmap definition"); +StackMapOpers::StackMapOpers(const MachineInstr *MI) : MI(MI) { + assert(getVarIdx() <= MI->getNumOperands() && "invalid stackmap definition"); } PatchPointOpers::PatchPointOpers(const MachineInstr *MI) @@ -80,17 +195,34 @@ unsigned PatchPointOpers::getNextScratchIdx(unsigned StartIdx) const { // Find the next scratch register (implicit def and early clobber) unsigned ScratchIdx = StartIdx, e = MI->getNumOperands(); - while (ScratchIdx < e && - !(MI->getOperand(ScratchIdx).isReg() && - MI->getOperand(ScratchIdx).isDef() && - MI->getOperand(ScratchIdx).isImplicit() && - MI->getOperand(ScratchIdx).isEarlyClobber())) + while (ScratchIdx < e && !(MI->getOperand(ScratchIdx).isReg() && + MI->getOperand(ScratchIdx).isDef() && + MI->getOperand(ScratchIdx).isImplicit() && + MI->getOperand(ScratchIdx).isEarlyClobber())) ++ScratchIdx; assert(ScratchIdx != e && "No scratch register available"); return ScratchIdx; } +unsigned StatepointOpers::getNumStackPtrsIdx() { + // Take index of num of struct arg entries and skip all struct arg entries. + unsigned CurIdx = getNumStructArgEntriesIdx(); + unsigned StructArgSize = getConstMetaVal(*MI, CurIdx - 1); + CurIdx++; + // 2 : StructArgMap comes with pairs + return (CurIdx + 1 + 2 * StructArgSize); +} + +unsigned StatepointOpers::getNumStructArgEntriesIdx() { + // Take index of num of gc map entries and skip all gc map entries. + unsigned CurIdx = getNumGcMapEntriesIdx(); + unsigned GCMapSize = getConstMetaVal(*MI, CurIdx - 1); + CurIdx++; + // 2 : GcMap comes with pairs + return (CurIdx + 1 + 2 * GCMapSize); // skip +} + unsigned StatepointOpers::getNumGcMapEntriesIdx() { // Take index of num of allocas and skip all allocas records. unsigned CurIdx = getNumAllocaIdx(); @@ -132,6 +264,26 @@ int StatepointOpers::getFirstGCPtrIdx() { return (int)NumGCPtrsIdx; } +int StatepointOpers::getFirstAllocaIdx() { + unsigned NumAllocaIdx = getNumAllocaIdx(); + unsigned NumAllocas = getConstMetaVal(*MI, NumAllocaIdx - 1); + if (NumAllocas == 0) + return -1; + ++NumAllocaIdx; // skip + assert(NumAllocaIdx < MI->getNumOperands()); + return (int)NumAllocaIdx; +} + +int StatepointOpers::getFirstStackPtrIdx() { + unsigned NumStackPtrsIdx = getNumStackPtrsIdx(); + unsigned NumStackPtrs = getConstMetaVal(*MI, NumStackPtrsIdx - 1); + if (NumStackPtrs == 0) + return -1; + ++NumStackPtrsIdx; // skip + assert(NumStackPtrsIdx < MI->getNumOperands()); + return (int)NumStackPtrsIdx; +} + unsigned StatepointOpers::getGCPointerMap( SmallVectorImpl> &GCMap) { unsigned CurIdx = getNumGcMapEntriesIdx(); @@ -146,6 +298,32 @@ unsigned StatepointOpers::getGCPointerMap( return GCMapSize; } +unsigned StatepointOpers::getStructArgMap( + SmallVectorImpl> &StructArgMap) { + unsigned CurIdx = getNumStructArgEntriesIdx(); + unsigned StructArgMapSize = getConstMetaVal(*MI, CurIdx - 1); + CurIdx++; + for (unsigned N = 0; N < StructArgMapSize; ++N) { + unsigned B = MI->getOperand(CurIdx++).getImm(); + signed Offset = MI->getOperand(CurIdx++).getImm(); + StructArgMap.push_back(std::make_pair(B, Offset)); + } + + return StructArgMapSize; +} + +const Function *StatepointOpers::getCalledFunction() { + const MachineOperand &CallTarget = getCallTarget(); + if (!CallTarget.isGlobal()) + return nullptr; + + return dyn_cast(CallTarget.getGlobal()); +} + +bool StatepointOpers::isCJStackCheck() { + return getID() == CJStatepointID::StackCheck; +} + StackMaps::StackMaps(AsmPrinter &AP) : AP(AP) { if (StackMapVersion != 3) llvm_unreachable("Unsupported stackmap version!"); @@ -159,8 +337,6 @@ unsigned StackMaps::getNextMetaArgIdx(const MachineInstr *MI, unsigned CurIdx) { default: llvm_unreachable("Unrecognized operand type."); case StackMaps::DirectMemRefOp: - CurIdx += 2; - break; case StackMaps::IndirectMemRefOp: CurIdx += 3; break; @@ -184,81 +360,254 @@ static unsigned getDwarfRegNum(unsigned Reg, const TargetRegisterInfo *TRI) { return (unsigned)RegNum; } -MachineInstr::const_mop_iterator -StackMaps::parseOperand(MachineInstr::const_mop_iterator MOI, - MachineInstr::const_mop_iterator MOE, LocationVec &Locs, - LiveOutVec &LiveOuts) const { +// cj array element should be pointer type, or primitive type or struct type +void StackMaps::processArrayType(ArrayType *AT, int64_t RefOffset, + LocationVec &Locations, unsigned Reg, + const GCStrategy *GS) const { const TargetRegisterInfo *TRI = AP.MF->getSubtarget().getRegisterInfo(); - if (MOI->isImm()) { - switch (MOI->getImm()) { - default: - llvm_unreachable("Unrecognized operand type."); - case StackMaps::DirectMemRefOp: { - auto &DL = AP.MF->getDataLayout(); - - unsigned Size = DL.getPointerSizeInBits(); - assert((Size % 8) == 0 && "Need pointer size in bytes."); - Size /= 8; - Register Reg = (++MOI)->getReg(); - int64_t Imm = (++MOI)->getImm(); - Locs.emplace_back(StackMaps::Location::Direct, Size, - getDwarfRegNum(Reg, TRI), Imm); - break; + const DataLayout &DL = AP.MF->getDataLayout(); + uint64_t Size = AT->getNumElements(); + Type *ElementType = AT->getElementType(); + if (isa(ElementType)) { + for (unsigned Index = 0; Index < Size; Index++) { + Locations.emplace_back(StackMaps::Location::Indirect, + 8, // 8: size of the register + getDwarfRegNum(Reg, TRI), RefOffset + Index * 8); } - case StackMaps::IndirectMemRefOp: { - int64_t Size = (++MOI)->getImm(); - assert(Size > 0 && "Need a valid size for indirect memory locations."); - Register Reg = (++MOI)->getReg(); - int64_t Imm = (++MOI)->getImm(); - Locs.emplace_back(StackMaps::Location::Indirect, Size, - getDwarfRegNum(Reg, TRI), Imm); - break; + } else if (StructType *ArrayStruct = dyn_cast(ElementType)) { + int64_t DerivedOffset = DL.getStructLayout(ArrayStruct)->getSizeInBytes(); + for (unsigned Index = 0; Index < Size; Index++) { + processAllocaStructType(ArrayStruct, RefOffset + Index * DerivedOffset, + Locations, Reg, GS); } - case StackMaps::ConstantOp: { - ++MOI; - assert(MOI->isImm() && "Expected constant operand."); - int64_t Imm = MOI->getImm(); - Locs.emplace_back(Location::Constant, sizeof(int64_t), 0, Imm); - break; + } +} + +void StackMaps::processAllocaStructType(StructType *ST, int64_t RefOffset, + LocationVec &Locations, unsigned Reg, + const GCStrategy *GS) const { + const TargetRegisterInfo *TRI = AP.MF->getSubtarget().getRegisterInfo(); + const DataLayout &DL = AP.MF->getDataLayout(); + for (int64_t i = 0; i < ST->getNumElements(); i++) { + int64_t Offset = DL.getStructLayout(ST)->getElementOffset(i) + RefOffset; + if (PointerType *EPT = dyn_cast(ST->getElementType(i))) { + if (!*GS->isGCManagedPointer(EPT)) { + continue; + } + Locations.emplace_back(StackMaps::Location::Indirect, + 8, // 8: size of the register + getDwarfRegNum(Reg, TRI), Offset); + } else if (StructType *EST = dyn_cast(ST->getElementType(i))) { + processAllocaStructType(EST, Offset, Locations, Reg, GS); + } else if (ArrayType *AT = dyn_cast(ST->getElementType(i))) { + processArrayType(AT, Offset, Locations, Reg, GS); } + } +} + +MachineInstr::const_mop_iterator +StackMaps::parseImmOperand(MachineInstr::const_mop_iterator MOI, + CallsiteInfo &CSInfo, bool IsResult) const { + const TargetRegisterInfo *TRI = AP.MF->getSubtarget().getRegisterInfo(); + assert(MOI->isImm() && "MOI is not a Imm when parseImmOperand!"); + switch (MOI->getImm()) { + default: + report_fatal_error("Unrecognized operand type."); + case StackMaps::DirectMemRefOp: { + const auto &DL = AP.MF->getDataLayout(); + unsigned Size = DL.getPointerSizeInBits(); + assert((Size % 8) == 0 && "Need pointer size in bytes."); + Size /= 8; + Register Reg = (++MOI)->getReg(); + int64_t Imm = (++MOI)->getImm(); + CSInfo.Locations.emplace_back(StackMaps::Location::Direct, Size, + getDwarfRegNum(Reg, TRI), Imm); + int64_t FI = (++MOI)->getImm(); + (void)FI; + assert(!CJPipeline && "illegal cangjie process"); + break; + } + case StackMaps::IndirectMemRefOp: { + int64_t Size = (++MOI)->getImm(); + assert(Size > 0 && "Need a valid size for indirect memory locations."); + Register Reg = (++MOI)->getReg(); + int64_t Imm = (++MOI)->getImm(); + CSInfo.Locations.emplace_back(StackMaps::Location::Indirect, Size, + getDwarfRegNum(Reg, TRI), Imm); + if (!IsResult) { + CSInfo.RefPairs.emplace_back(CSInfo.Locations.back()); } + break; + } + case StackMaps::ConstantOp: { + ++MOI; + assert(MOI->isImm() && "Expected constant operand."); + int64_t Imm = MOI->getImm(); + CSInfo.Locations.emplace_back(Location::Constant, sizeof(int64_t), 0, Imm); + break; + } + } + return ++MOI; +} + +MachineInstr::const_mop_iterator +StackMaps::parseStructArgsOperand(MachineInstr::const_mop_iterator MOI, + LocationVec &FOLocations, + unsigned Offset) const { + assert(MOI->isImm() && "MOI is not a Imm when parseStructArgsOperand!"); + assert((Offset % OffsetStepSize == 0) && "Struct Offset should be 8 aligned!"); + const TargetRegisterInfo *TRI = AP.MF->getSubtarget().getRegisterInfo(); + if (MOI->getImm() == StackMaps::DirectMemRefOp) { + Register Reg = (++MOI)->getReg(); + int64_t Imm = (++MOI)->getImm(); + int64_t FI = (++MOI)->getImm(); + (void)FI; + +#ifndef NDEBUG + const AllocaInst *Inst = AP.MF->getFrameInfo().getObjectAllocation(FI); + assert(Inst && "The FI is not alloca inst!"); + const DataLayout &DL = AP.MF->getDataLayout(); + StructType *ST = dyn_cast(Inst->getAllocatedType()); + assert(DL.getStructLayout(ST)->getSizeInBytes() > Offset && + "The offset cannot be greater than the size of struct."); +#endif + // 8:ptr size + FOLocations.emplace_back(StackMaps::Location::Indirect, 8, + getDwarfRegNum(Reg, TRI), Imm + Offset); + } else if (MOI->getImm() == StackMaps::IndirectMemRefOp) { + int64_t Size = (++MOI)->getImm(); + Register Reg = (++MOI)->getReg(); + int64_t Imm = (++MOI)->getImm(); + + FOLocations.emplace_back(StackMaps::Location::Indirect, Size, + getDwarfRegNum(Reg, TRI), Imm); + } else { + assert(false && "MOI is error when parseStructArgsOperand!"); + } + return ++MOI; +} + +MachineInstr::const_mop_iterator +StackMaps::parseAllocaOperand(MachineInstr::const_mop_iterator MOI, + LocationVec &Locations) const { + const TargetRegisterInfo *TRI = AP.MF->getSubtarget().getRegisterInfo(); + assert(MOI->isImm() && "MOI is not a Imm when parseAllocaOperand!"); + assert(MOI->getImm() == StackMaps::DirectMemRefOp && + "MOI is not a DirectMemRefOp when parseAllocaOperand!"); + + Register Reg = (++MOI)->getReg(); + int64_t Imm = (++MOI)->getImm(); + int64_t FI = (++MOI)->getImm(); + + GCStrategy *GS = AP.getAnalysisIfAvailable()->getGCStrategy( + StringRef("cangjie")); // cfile ref strategy. + // add FI in FOLocation + const AllocaInst *Inst = AP.MF->getFrameInfo().getObjectAllocation(FI); + if (Inst != nullptr) { + if (auto *ST = dyn_cast(Inst->getAllocatedType())) { + processAllocaStructType(ST, Imm, Locations, Reg, GS); + } else if (auto *PT = dyn_cast(Inst->getAllocatedType())) { + if (*GS->isGCManagedPointer(PT) || + *GS->isGCManagedPointer(PT->getElementType())) { + // 8:ptr size + Locations.emplace_back(StackMaps::Location::Indirect, 8, + getDwarfRegNum(Reg, TRI), Imm); + } + } + } else { + // The FI of alloca may be modify in tryToElideArgumentCopy. + // In this case, alloca type must be a pointer type. + // 8:ptr size + Locations.emplace_back(StackMaps::Location::Indirect, 8, + getDwarfRegNum(Reg, TRI), Imm); + } + return ++MOI; +} + +MachineInstr::const_mop_iterator +StackMaps::parseStackPtrOperand(MachineInstr::const_mop_iterator MOI, + LocationVec &Locations) const { + assert(MOI->isImm() && "MOI is not a Imm when parseStackPtrOperand!"); + const TargetRegisterInfo *TRI = AP.MF->getSubtarget().getRegisterInfo(); + if (MOI->getImm() == StackMaps::IndirectMemRefOp) { + int64_t Size = (++MOI)->getImm(); + Register Reg = (++MOI)->getReg(); + int64_t Imm = (++MOI)->getImm(); + assert((Imm % OffsetStepSize == 0) && "Stack Offset align error!"); + Locations.emplace_back(StackMaps::Location::Indirect, Size, + getDwarfRegNum(Reg, TRI), Imm); + } else if (MOI->getImm() == StackMaps::DirectMemRefOp) { + Register Reg = (++MOI)->getReg(); + int64_t Imm = (++MOI)->getImm(); + int64_t FI = (++MOI)->getImm(); + (void)FI; + assert((Imm % OffsetStepSize == 0) && "Stack Offset align error!"); + Locations.emplace_back(StackMaps::Location::Indirect, 8, + getDwarfRegNum(Reg, TRI), Imm); // 8: pointer size + } else { + report_fatal_error("MOI is error when parseStackPtrOperand!"); + } + return ++MOI; +} + +MachineInstr::const_mop_iterator +StackMaps::parseRegOperand(MachineInstr::const_mop_iterator MOI, + CallsiteInfo &CSInfo, LocationVec &Locations, + bool IsResult) const { + const TargetRegisterInfo *TRI = AP.MF->getSubtarget().getRegisterInfo(); + assert(MOI->isReg() && "MOI is not a Reg when parseRegOperand!"); + // Skip implicit registers (this includes our scratch registers) + if (MOI->isImplicit()) return ++MOI; + + if (MOI->isUndef()) { + // Record `undef` register as constant. Use same value as ISel uses. + Locations.emplace_back(Location::Constant, sizeof(int64_t), 0, 0xFEFEFEFE); + return ++MOI; + } + + assert(Register::isPhysicalRegister(MOI->getReg()) && + "Virtreg operands should have been rewritten before now."); + const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(MOI->getReg()); + assert(!MOI->getSubReg() && "Physical subreg still around."); + + // 15, 31: max general regNum of ref, x86_64:15 , aarch64:31 and arm:15 + unsigned MaxRegIdx = isAArch64() ? 31 : 15; + unsigned Offset = 0; + unsigned DwarfRegNum = getDwarfRegNum(MOI->getReg(), TRI); + unsigned LLVMRegNum = *TRI->getLLVMRegNum(DwarfRegNum, false); + unsigned SubRegIdx = TRI->getSubRegIndex(LLVMRegNum, MOI->getReg()); + if (SubRegIdx) + Offset = TRI->getSubRegIdxOffset(SubRegIdx); + + Locations.emplace_back(Location::Register, TRI->getSpillSize(*RC), + DwarfRegNum, Offset); + + if (!IsResult && CJPipeline) { + if (DwarfRegNum > MaxRegIdx) { + report_fatal_error("ref reg don't support float regNum!"); + } + CSInfo.RefPairs.emplace_back(CSInfo.Locations.back()); } + return ++MOI; +} +MachineInstr::const_mop_iterator +StackMaps::parseOperand(MachineInstr::const_mop_iterator MOI, + CallsiteInfo &CSInfo, bool IsResult) const { + if (MOI->isImm()) { + return parseImmOperand(MOI, CSInfo, IsResult); + } // The physical register number will ultimately be encoded as a DWARF regno. // The stack map also records the size of a spill slot that can hold the // register content. (The runtime can track the actual size of the data type // if it needs to.) if (MOI->isReg()) { - // Skip implicit registers (this includes our scratch registers) - if (MOI->isImplicit()) - return ++MOI; - - if (MOI->isUndef()) { - // Record `undef` register as constant. Use same value as ISel uses. - Locs.emplace_back(Location::Constant, sizeof(int64_t), 0, 0xFEFEFEFE); - return ++MOI; - } - - assert(Register::isPhysicalRegister(MOI->getReg()) && - "Virtreg operands should have been rewritten before now."); - const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(MOI->getReg()); - assert(!MOI->getSubReg() && "Physical subreg still around."); - - unsigned Offset = 0; - unsigned DwarfRegNum = getDwarfRegNum(MOI->getReg(), TRI); - unsigned LLVMRegNum = *TRI->getLLVMRegNum(DwarfRegNum, false); - unsigned SubRegIdx = TRI->getSubRegIndex(LLVMRegNum, MOI->getReg()); - if (SubRegIdx) - Offset = TRI->getSubRegIdxOffset(SubRegIdx); - - Locs.emplace_back(Location::Register, TRI->getSpillSize(*RC), - DwarfRegNum, Offset); - return ++MOI; + return parseRegOperand(MOI, CSInfo, CSInfo.Locations, IsResult); } if (MOI->isRegLiveOut()) - LiveOuts = parseRegisterLiveOutMask(MOI->getRegLiveOut()); + CSInfo.LiveOuts = parseRegisterLiveOutMask(MOI->getRegLiveOut()); return ++MOI; } @@ -383,26 +732,123 @@ StackMaps::parseRegisterLiveOutMask(const uint32_t *Mask) const { return LiveOuts; } +void StackMaps::updateOrInsertFnInfo(const MCSymbol *FnSym, + CallsiteInfo &CallInfo) { + auto Itr = FnInfos.find(FnSym); + if (Itr == FnInfos.end()) { + // create a new function info. + // get the stack size of the current function + const MachineFrameInfo &MFI = AP.MF->getFrameInfo(); + const TargetRegisterInfo *RegInfo = AP.MF->getSubtarget().getRegisterInfo(); + bool HasDynamicFrameSize = + MFI.hasVarSizedObjects() || RegInfo->hasStackRealignment(*(AP.MF)); + uint64_t FrameSize = HasDynamicFrameSize ? UINT64_MAX : MFI.getStackSize(); + // Use frame pointer offset instead of frame size on win64 in cangjie. + if (CJPipeline && AP.getSubtargetInfo().getTargetTriple().isOSWindows()) { + FrameSize = MFI.getWin64FramePointerOffset(); + } + // get func callee saved regInfo + const auto &CSInfo = AP.MF->getFrameInfo().getCalleeSavedInfo(); + std::map CSReg2Stack; // + const TargetFrameLowering *TFI = AP.MF->getSubtarget().getFrameLowering(); + for (const auto &CS : CSInfo) { + if (CS.isSpilledToReg()) { + continue; + } + unsigned RegNo = getDwarfRegNum(CS.getReg(), RegInfo); + Register Reg; + CSReg2Stack[RegNo] = + TFI->getFrameIndexRefForCJ(*AP.MF, CS.getFrameIdx(), Reg).getFixed(); + } + FnInfos.insert(std::make_pair(FnSym, FunctionInfo(FrameSize, CSReg2Stack))); + Itr = FnInfos.find(FnSym); + } + + // Do not need emit callsite info when it has no any Locations and LineNumber + // in cangjie pipeline. + if (CJPipeline && CallInfo.RefPairs.empty() && CallInfo.FOLocations.empty() && + CallInfo.StackLocations.empty() && CallInfo.LineNumber == 0) + return; + + // update callsite count. + Itr->second.RecordCount++; + CSInfos.emplace_back(CallInfo); +} + +MachineInstr::const_mop_iterator +StackMaps::parseCangjieStackOpers(MachineInstr::const_mop_iterator MOI, + CallsiteInfo &CSInfo) const { + if (MOI->isReg()) { + MOI = parseRegOperand(MOI, CSInfo, CSInfo.StackLocations, true); + } else if (MOI->isImm()) { + MOI = parseStackPtrOperand(MOI, CSInfo.StackLocations); + } else { + report_fatal_error("Unrecognized operand type."); + } + return MOI; +} + +void StackMaps::parseCangjieStatepointOpers( + const MachineInstr &MI, StatepointOpers &SO, CallsiteInfo &CSInfo, + unsigned NumAllocas, SmallVectorImpl &GCPtrIndices) const { + auto MOB = MI.operands_begin(); + // Map logical index of alloca ptr to MI operand index. + SmallVector AllocaIndices; + + if (NumAllocas) { + unsigned AllocaIdx = (unsigned)SO.getFirstAllocaIdx(); + assert((int)AllocaIdx != -1 && "AllocaIdx cannot be -1"); + while (NumAllocas--) { + AllocaIndices.push_back(AllocaIdx); + AllocaIdx = StackMaps::getNextMetaArgIdx(&MI, AllocaIdx); + } + + SmallVector, 8> StructPairs; + unsigned NumStructPairs = SO.getStructArgMap(StructPairs); + (void)NumStructPairs; + LLVM_DEBUG(dbgs() << "NumStructPairs = " << NumStructPairs << "\n"); + + for (auto &P : StructPairs) { + assert(P.first < AllocaIndices.size() && "struct ptr index not found"); + unsigned StructIdx = AllocaIndices[P.first]; + if (P.second == -1) { + parseAllocaOperand(MOB + StructIdx, CSInfo.FOLocations); + } else { + parseStructArgsOperand(MOB + StructIdx, CSInfo.FOLocations, P.second); + } + } + } + + if (EnableStackGrow) { + unsigned NumStackPtrsIdx = SO.getNumStackPtrsIdx(); + auto MOI = MOB + NumStackPtrsIdx; + unsigned NumStackPtrs = MOI->getImm(); + ++MOI; + while (NumStackPtrs--) { + MOI = parseCangjieStackOpers(MOI, CSInfo); + } + } +} + // See statepoint MI format description in StatepointOpers' class comment // in include/llvm/CodeGen/StackMaps.h void StackMaps::parseStatepointOpers(const MachineInstr &MI, MachineInstr::const_mop_iterator MOI, MachineInstr::const_mop_iterator MOE, - LocationVec &Locations, - LiveOutVec &LiveOuts) { + CallsiteInfo &CSInfo) { LLVM_DEBUG(dbgs() << "record statepoint : " << MI << "\n"); StatepointOpers SO(&MI); - MOI = parseOperand(MOI, MOE, Locations, LiveOuts); // CC - MOI = parseOperand(MOI, MOE, Locations, LiveOuts); // Flags - MOI = parseOperand(MOI, MOE, Locations, LiveOuts); // Num Deopts + MOI = parseOperand(MOI, CSInfo, false); // CC + MOI = parseOperand(MOI, CSInfo, false); // Flags + MOI = parseOperand(MOI, CSInfo, false); // Num Deopts // Record Deopt Args. - unsigned NumDeoptArgs = Locations.back().Offset; - assert(Locations.back().Type == Location::Constant); + unsigned NumDeoptArgs = CSInfo.Locations.back().Offset; + assert(CSInfo.Locations.back().Type == Location::Constant); assert(NumDeoptArgs == SO.getNumDeoptArgs()); while (NumDeoptArgs--) - MOI = parseOperand(MOI, MOE, Locations, LiveOuts); + MOI = parseOperand(MOI, CSInfo, false); // Record gc base/derived pairs assert(MOI->isImm() && MOI->getImm() == StackMaps::ConstantOp); @@ -410,9 +856,10 @@ void StackMaps::parseStatepointOpers(const MachineInstr &MI, assert(MOI->isImm()); unsigned NumGCPointers = MOI->getImm(); ++MOI; + + // Map logical index of GC ptr to MI operand index. + SmallVector GCPtrIndices; if (NumGCPointers) { - // Map logical index of GC ptr to MI operand index. - SmallVector GCPtrIndices; unsigned GCPtrIdx = (unsigned)SO.getFirstGCPtrIdx(); assert((int)GCPtrIdx != -1); assert(MOI - MI.operands_begin() == GCPtrIdx + 0LL); @@ -435,8 +882,8 @@ void StackMaps::parseStatepointOpers(const MachineInstr &MI, unsigned DerivedIdx = GCPtrIndices[P.second]; LLVM_DEBUG(dbgs() << "Base : " << BaseIdx << " Derived : " << DerivedIdx << "\n"); - (void)parseOperand(MOB + BaseIdx, MOE, Locations, LiveOuts); - (void)parseOperand(MOB + DerivedIdx, MOE, Locations, LiveOuts); + parseOperand(MOB + BaseIdx, CSInfo, false); + parseOperand(MOB + DerivedIdx, CSInfo, false); } MOI = MOB + GCPtrIdx; @@ -448,37 +895,37 @@ void StackMaps::parseStatepointOpers(const MachineInstr &MI, ++MOI; unsigned NumAllocas = MOI->getImm(); ++MOI; - while (NumAllocas--) { - MOI = parseOperand(MOI, MOE, Locations, LiveOuts); - assert(MOI < MOE); + if (CJPipeline) { + parseCangjieStatepointOpers(MI, SO, CSInfo, NumAllocas, GCPtrIndices); + } else { + while (NumAllocas--) { + MOI = parseOperand(MOI, CSInfo, false); + assert(MOI < MOE); + } } } void StackMaps::recordStackMapOpers(const MCSymbol &MILabel, - const MachineInstr &MI, uint64_t ID, - MachineInstr::const_mop_iterator MOI, - MachineInstr::const_mop_iterator MOE, - bool recordResult) { - MCContext &OutContext = AP.OutStreamer->getContext(); - - LocationVec Locations; - LiveOutVec LiveOuts; - - if (recordResult) { + const MachineInstr &MI, + StackMapOpersInfo OpersInfo, + bool RecordResult, bool RecordAllRef) { + CallsiteInfo CSInfo; + CSInfo.ID = OpersInfo.ID; + CSInfo.RecordAllRefInReg = RecordAllRef; + if (RecordResult) { assert(PatchPointOpers(&MI).hasDef() && "Stackmap has no return value."); - parseOperand(MI.operands_begin(), std::next(MI.operands_begin()), Locations, - LiveOuts); + parseOperand(MI.operands_begin(), CSInfo, true); } // Parse operands. if (MI.getOpcode() == TargetOpcode::STATEPOINT) - parseStatepointOpers(MI, MOI, MOE, Locations, LiveOuts); + parseStatepointOpers(MI, OpersInfo.MOI, OpersInfo.MOE, CSInfo); else - while (MOI != MOE) - MOI = parseOperand(MOI, MOE, Locations, LiveOuts); + while (OpersInfo.MOI != OpersInfo.MOE) + OpersInfo.MOI = parseOperand(OpersInfo.MOI, CSInfo, false); // Move large constants into the constant pool. - for (auto &Loc : Locations) { + for (auto &Loc : CSInfo.Locations) { // Constants are encoded as sign-extended integers. // -1 is directly encoded as .long 0xFFFFFFFF with no constant pool. if (Loc.Type == Location::Constant && !isInt<32>(Loc.Offset)) { @@ -497,68 +944,92 @@ void StackMaps::recordStackMapOpers(const MCSymbol &MILabel, Loc.Offset = Result.first - ConstPool.begin(); } } - + MCContext &OutContext = AP.OutStreamer->getContext(); // Create an expression to calculate the offset of the callsite from function // entry. - const MCExpr *CSOffsetExpr = MCBinaryExpr::createSub( + CSInfo.CSOffsetExpr = MCBinaryExpr::createSub( MCSymbolRefExpr::create(&MILabel, OutContext), MCSymbolRefExpr::create(AP.CurrentFnSymForSize, OutContext), OutContext); - CSInfos.emplace_back(CSOffsetExpr, ID, std::move(Locations), - std::move(LiveOuts)); - - // Record the stack size of the current function and update callsite count. - const MachineFrameInfo &MFI = AP.MF->getFrameInfo(); - const TargetRegisterInfo *RegInfo = AP.MF->getSubtarget().getRegisterInfo(); - bool HasDynamicFrameSize = - MFI.hasVarSizedObjects() || RegInfo->hasStackRealignment(*(AP.MF)); - uint64_t FrameSize = HasDynamicFrameSize ? UINT64_MAX : MFI.getStackSize(); - - auto CurrentIt = FnInfos.find(AP.CurrentFnSym); - if (CurrentIt != FnInfos.end()) - CurrentIt->second.RecordCount++; - else - FnInfos.insert(std::make_pair(AP.CurrentFnSym, FunctionInfo(FrameSize))); + // get Line number from debug info + uint32_t Line = 0; + if (MI.getOpcode() == TargetOpcode::STATEPOINT) { + if (OpersInfo.ID == CJStatepointID::StackCheck && + MI.getMF()->getFunction().getSubprogram() != nullptr) { + Line = MI.getMF()->getFunction().getSubprogram()->getLine(); + } + } + CSInfo.LineNumber = Line; + DILocation *DIL = MI.getDebugLoc().get(); + if (DIL != nullptr) { + DILocation *InlinedDIL = DIL->getInlinedAt(); + while (InlinedDIL != nullptr) { + DIL = InlinedDIL; + InlinedDIL = InlinedDIL->getInlinedAt(); + } + CSInfo.LineNumber = DIL->getLine(); + } + updateOrInsertFnInfo(AP.CurrentFnSym, CSInfo); } void StackMaps::recordStackMap(const MCSymbol &L, const MachineInstr &MI) { assert(MI.getOpcode() == TargetOpcode::STACKMAP && "expected stackmap"); - StackMapOpers opers(&MI); - const int64_t ID = MI.getOperand(PatchPointOpers::IDPos).getImm(); - recordStackMapOpers(L, MI, ID, std::next(MI.operands_begin(), - opers.getVarIdx()), - MI.operands_end()); + StackMapOpers Opers(&MI); + StackMapOpersInfo OpersInfo; + OpersInfo.ID = MI.getOperand(PatchPointOpers::IDPos).getImm(); + OpersInfo.MOI = std::next(MI.operands_begin(), Opers.getVarIdx()); + OpersInfo.MOE = MI.operands_end(); + recordStackMapOpers(L, MI, OpersInfo); } void StackMaps::recordPatchPoint(const MCSymbol &L, const MachineInstr &MI) { assert(MI.getOpcode() == TargetOpcode::PATCHPOINT && "expected patchpoint"); - PatchPointOpers opers(&MI); - const int64_t ID = opers.getID(); - auto MOI = std::next(MI.operands_begin(), opers.getStackMapStartIdx()); - recordStackMapOpers(L, MI, ID, MOI, MI.operands_end(), - opers.isAnyReg() && opers.hasDef()); + PatchPointOpers Opers(&MI); + StackMapOpersInfo OpersInfo; + OpersInfo.ID = Opers.getID(); + OpersInfo.MOI = std::next(MI.operands_begin(), Opers.getStackMapStartIdx()); + OpersInfo.MOE = MI.operands_end(); + recordStackMapOpers(L, MI, OpersInfo, Opers.isAnyReg() && Opers.hasDef()); #ifndef NDEBUG // verify anyregcc auto &Locations = CSInfos.back().Locations; - if (opers.isAnyReg()) { - unsigned NArgs = opers.getNumCallArgs(); - for (unsigned i = 0, e = (opers.hasDef() ? NArgs + 1 : NArgs); i != e; ++i) + if (Opers.isAnyReg()) { + unsigned NArgs = Opers.getNumCallArgs(); + for (unsigned i = 0, e = (Opers.hasDef() ? NArgs + 1 : NArgs); i != e; ++i) assert(Locations[i].Type == Location::Register && "anyreg arg must be in reg."); } #endif } -void StackMaps::recordStatepoint(const MCSymbol &L, const MachineInstr &MI) { - assert(MI.getOpcode() == TargetOpcode::STATEPOINT && "expected statepoint"); +void StackMaps::recordStatepoint(const MCSymbol &L, const MachineInstr &MI, + bool RecordAllRefInReg) { + StackMapOpersInfo OpersInfo; + if (MI.getOpcode() == TargetOpcode::STATEPOINT) { + // Record all the deopt and gc operands (they're contiguous and run from the + // initial index to the end of the operand list) + StatepointOpers opers(&MI); + OpersInfo.ID = opers.getID(); + OpersInfo.MOI = MI.operands_begin() + opers.getVarIdx(); + OpersInfo.MOE = MI.operands_end(); + } else { + // for div/idiv, don't need to traverse operands, only to record label + OpersInfo.ID = 0; + OpersInfo.MOI = MI.operands_end(); + OpersInfo.MOE = MI.operands_end(); + } + + recordStackMapOpers(L, MI, OpersInfo, false, RecordAllRefInReg); +} - StatepointOpers opers(&MI); - const unsigned StartIdx = opers.getVarIdx(); - recordStackMapOpers(L, MI, opers.getID(), MI.operands_begin() + StartIdx, - MI.operands_end(), false); +void StackMaps::recordCJStackMap(const MachineInstr &MI, + bool RecordAllRefInReg) { + MCSymbol *MILabel = AP.OutStreamer->getContext().createTempSymbol(); + AP.OutStreamer->emitLabel(MILabel); + recordStatepoint(*MILabel, MI, RecordAllRefInReg); } /// Emit the stackmap header. @@ -680,7 +1151,7 @@ void StackMaps::emitCallsiteEntries(MCStreamer &OS) { for (const auto &Loc : CSLocs) { OS.emitIntValue(Loc.Type, 1); - OS.emitIntValue(0, 1); // Reserved + OS.emitIntValue(0, 1); // Reserved OS.emitInt16(Loc.Size); OS.emitInt16(Loc.Reg); OS.emitInt16(0); // Reserved @@ -738,3 +1209,723 @@ void StackMaps::serializeToStackMapSection() { CSInfos.clear(); ConstPool.clear(); } + +void StackMaps::emitCangjieStackMaps(MCStreamer &OS) { + emitCangjieCompressedStackMaps(OS); + return; +} +// use varint and BitTable to store stackMaps +// Variable-length integer: +// The first four bits determine the length of the encoded integer: +// Values 0..11 represent the result as-is, with no further following bits. +// Values 12..15 mean the result is in the next 8/16/24/32-bits respectively. +// BitTable: +// varInt RecordNums +// varInt Record.elem0's maxBitLen +// varInt Record.elem1's maxBitLen +// ... +// varInt record.elemN's maxBitLen(N are fixed for different tables) +// Record0 {elem0, elem1, ... elemN} +// Record1 {elem0, elem1, ... elemN} +// ... +// RecordM {elem0, elem1, ... elemN} +// each elem of Record is extends to related maxBitLen +void StackMaps::emitCangjieCompressedStackMaps(MCStreamer &OS) { + MCContext &OutContext = OS.getContext(); + unsigned CSIdxStart = 0; + unsigned CSIdxEnd = 0; + const Triple TT(AP.MMI->getModule()->getTargetTriple()); + bool IsWindows = TT.isOSWindows(); + OffsetStepSize = TT.isARM() ? 4 : 8; + for (auto const &FR : FnInfos) { + MCSymbol *StackmapFunction = + OutContext.getOrCreateSymbol(".Lstack_map." + FR.first->getName()); + OS.emitLabel(StackmapFunction); + CSIdxEnd = CSIdxStart + FR.second.RecordCount; + CompressedInfo Data( + (IsWindows && isX86_64()) ? X86WinCalleeSavedReg + : isX86_64() ? X86CalleeSavedReg + : isAArch64() ? AArch64CalleeSavedReg : ARMCalleeSavedReg); + prepareCompressedData(Data, FR.second, CSIdxStart, CSIdxEnd); + emitCangjieCompressedData(OS, Data); + CSIdxStart = CSIdxEnd; + } + OS.addBlankLine(); + reset(); +} + +static void setPrologueInfo(CompressedInfo &Data, + const StackMaps::FunctionInfo &FnInfo) { + if (FnInfo.StackSize == UINT64_MAX) { + Data.StackSize = 0; // lea offset(%rbp) rsp, stack back + } else { + Data.StackSize = FnInfo.StackSize; + } + for (const auto &CS : FnInfo.CSReg2Stack) { + Data.CalleeSavedReg |= Data.CSRegMap.at(CS.first); + int Offset = CS.second / OffsetStepSize; + Offset = (Offset > 0) ? Offset : -Offset; + Data.CalleeSavedOffsets.emplace_back(Offset); + } + return; +} + +static void +parseCallsiteRefs(const StackMaps::CallsiteInfo &CSI, + std::map> + &Base2Derived) { + if (CSI.RefPairs.size() % 2 != 0) { + report_fatal_error("Base and Derived Ptrs should be paired!"); + } + for (unsigned I = 0, J = CSI.RefPairs.size(); I < J;) { + if (CSI.RefPairs[I] == CSI.RefPairs[I + 1]) { + // insert empty derived if base and derived are the same + Base2Derived[CSI.RefPairs[I]].insert({}); + } else { + Base2Derived[CSI.RefPairs[I]].insert(CSI.RefPairs[I + 1]); + } + I += 2; // ptr comes in pair. + } + // FO doesn't record deriveds + for (const auto &FOLoc : CSI.FOLocations) { + Base2Derived.insert({FOLoc, {}}); + } +} + +namespace { +// this func gets base ptrs. +StackMaps::Location +getLocation(std::map>::const_iterator Itr) { + return Itr->first; +} +// this func gets derived ptrs. +StackMaps::Location +getLocation(std::set::const_iterator Itr) { + return *Itr; +} + +struct MaxWidthOfRefInfo { + unsigned RegBit; + unsigned BaseOffsetBytes; + unsigned SlotBitIdx; + unsigned CompressedSlotBit; +}; + +void calculateStackSlots(MaxWidthOfRefInfo &WidthInfo, + CompressedInfo::SlotItem &StackSlot, + const SmallVector &BOffsets, + int64_t MaxOffset, int64_t MinOffset) { + StackSlot.BaseOffset = MaxOffset; + uint32_t MaxBitIdx = (MaxOffset - MinOffset) / OffsetStepSize; + WidthInfo.SlotBitIdx = std::max(WidthInfo.SlotBitIdx, MaxBitIdx); + // 64: uint64_t + uint32_t SlotBitVecSize = (MaxBitIdx / 64) + 1; + StackSlot.SlotBit = std::vector(SlotBitVecSize, 0); + + uint32_t OriDataVecSize = (MaxBitIdx / RawDataWidth) + 1; + auto OriDataVec = std::vector(OriDataVecSize, 0); + + for (const auto &Offset : BOffsets) { + if (Offset % OffsetStepSize != 0) { + report_fatal_error("Offset should be 8 aligned!"); + } + uint32_t BitIdx = (MaxOffset - Offset) / OffsetStepSize; + // 64: uint64_t + StackSlot.SlotBit[BitIdx / 64] |= ((uint64_t)1 << (BitIdx % 64)); + // prepare origion data. highest bit is always keep zero here. + OriDataVec[BitIdx / RawDataWidth] |= + ((uint32_t)1 << (BitIdx % RawDataWidth)); + } + // compress origion data + uint32_t AllZeroCnt = 0; + uint32_t AllOneCnt = 0; + unsigned CompressedBitCnt = 0; + auto PushCompressedValue = [&](uint32_t &Value, uint32_t Flag) { + // 2: 2 bits for compressed fowarding bit + CompressedBitCnt += getVarIntBitNumsForUInt(Value) + 2; + StackSlot.CompressedSlotBit.push_back(Value | Flag); + if (Value >= (uint32_t)1 << 30) { + report_fatal_error("unsupport AllOneCnt/ AllZeroCnt now!\n"); + } + Value = 0; + }; + + for (unsigned I = 0; I < OriDataVecSize; ++I) { + if (OriDataVec[I] == 0) { + if (AllOneCnt != 0) { + PushCompressedValue(AllOneCnt, 0x40000000); + } + AllZeroCnt++; + } else if (OriDataVec[I] == 0x7fffffff) { + if (AllZeroCnt != 0) { + PushCompressedValue(AllZeroCnt, 0); + } + AllOneCnt++; + } else { + if (AllOneCnt != 0) { + PushCompressedValue(AllOneCnt, 0x40000000); + } + if (AllZeroCnt != 0) { + PushCompressedValue(AllZeroCnt, 0); + } + // + 1: 1 bit to indicate value is not compressed + if (I != OriDataVecSize - 1) { + CompressedBitCnt += RawDataWidth + 1; + } else { + CompressedBitCnt += getValidBitNums(OriDataVec[I]) + 1; + } + + StackSlot.CompressedSlotBit.push_back(OriDataVec[I] | 0x80000000); + } + } + if (AllOneCnt != 0) { + PushCompressedValue(AllOneCnt, 0x40000000); + } + WidthInfo.CompressedSlotBit = std::max(WidthInfo.CompressedSlotBit, + CompressedBitCnt); +} + +// Process base when traversing map and process derivation when traversing set +template +std::pair +addItemInfo(CompressedInfo &Data, const StackMaps::CallsiteInfo &CSI, + MaxWidthOfRefInfo &WidthInfo, T &Input) { + auto Itr = Input.cbegin(); + auto EndItr = Input.cend(); + int64_t MaxOffset = INT64_MIN; + int64_t MinOffset = INT64_MAX; + SmallVector BOffsets; + CompressedInfo::RegItem RegInfo{0}; + CompressedInfo::SlotItem StackSlot{0, {0}, {}}; + while (Itr != EndItr) { + StackMaps::Location Loc = getLocation(Itr); + if (Loc.Type == StackMaps::Location::Register) { + // Record all references at yield point and record called saved only at + // other points + if (CSI.RecordAllRefInReg || + Data.CSRegMap.find(Loc.Reg) != Data.CSRegMap.end()) { + RegInfo.RegBit |= 1 << Loc.Reg; + } + } else { + BOffsets.push_back(Loc.Offset); + MaxOffset = std::max(Loc.Offset, MaxOffset); + MinOffset = std::min(Loc.Offset, MinOffset); + } + ++Itr; + } + + WidthInfo.RegBit = std::max(WidthInfo.RegBit, RegInfo.RegBit); + // 64: use uint64_t to store bit value. + if (!BOffsets.empty()) { + calculateStackSlots(WidthInfo, StackSlot, BOffsets, MaxOffset, MinOffset); + } + unsigned BaseOffsetBytes = getMinBytesForInt(StackSlot.BaseOffset); + WidthInfo.BaseOffsetBytes = std::max(WidthInfo.BaseOffsetBytes, + BaseOffsetBytes); + unsigned RegIdxPlusOne = Data.RegItems.getOrInsertIndex(RegInfo); + unsigned SlotIdxPlusOne = Data.SlotItems.getOrInsertIndex(StackSlot); + return std::make_pair(RegIdxPlusOne, SlotIdxPlusOne); +} +} // end anonymous namespace + +static void genStackMapInfo(CompressedInfo &Data, + const StackMaps::CallsiteInfo &CSI, + MaxWidthOfRefInfo &WidthInfo) { + // >. use map and set to keep order + std::map> Base2Derived; + parseCallsiteRefs(CSI, Base2Derived); + + CompressedInfo::IdxItem &IdxInfo = Data.StackMapItem.back().second; + // pass map> to addItemInfo to process base ptrs + std::pair RefIdx = + addItemInfo(Data, CSI, WidthInfo, Base2Derived); + IdxInfo.RegIdxPlusOne = RefIdx.first; + IdxInfo.SlotIdxPlusOne = RefIdx.second; + + bool IsAllIdxsInvalid = true; + // + std::vector> IdxsInfo; + auto Itr = Base2Derived.cbegin(); + auto EndItr = Base2Derived.cend(); + while (Itr != EndItr) { + // pass each base's set to addItemInfo to process derived ptrs + std::pair DerivedRefIdx = + addItemInfo(Data, CSI, WidthInfo, Itr->second); + if (DerivedRefIdx.first != 0 || DerivedRefIdx.second != 0) { + IsAllIdxsInvalid = false; + } + IdxsInfo.emplace_back(DerivedRefIdx); + ++Itr; + } + if (IsAllIdxsInvalid) { + IdxInfo.DerivedInfoStartIdx = 0; + } else { + IdxInfo.DerivedInfoStartIdx = Data.DerivedInfo.size(); + Data.DerivedInfo.insert(Data.DerivedInfo.end(), IdxsInfo.begin(), + IdxsInfo.end()); + } + + if (EnableStackGrow) { + // The set of the Stack Ptrs Locations. + std::set SPLocs(CSI.StackLocations.begin(), + CSI.StackLocations.end()); + std::pair StackPtrIdx = + addItemInfo(Data, CSI, WidthInfo, SPLocs); + IdxInfo.SPRegIdxPlusOne = StackPtrIdx.first; + IdxInfo.SPSlotIdxPlusOne = StackPtrIdx.second; + } +} + +void StackMaps::prepareCompressedData(CompressedInfo &Data, + const FunctionInfo &FnInfo, + unsigned CSIdx, unsigned CSIdxEnd) const { + // prologue + setPrologueInfo(Data, FnInfo); + // 1: baseoffset occupies at least 1 byte because it may be a negative number + MaxWidthOfRefInfo WidthInfo{0, 1, 0, 0}; + unsigned MaxLN = 0; + // CallSiteItem format: + // callsite label + // RegIdx, SlotIdx, LNIdx, DerivedStartIdx, SPRegIdx, SPSlotIdx + while (CSIdx < CSIdxEnd) { + const auto &CSI = CSInfos[CSIdx++]; + Data.StackMapItem.insert( + std::make_pair(CSI.CSOffsetExpr, CompressedInfo::IdxItem())); + genStackMapInfo(Data, CSI, WidthInfo); + + CompressedInfo::LineNumberItem LineNumber{CSI.LineNumber}; + MaxLN = (MaxLN > LineNumber.LN) ? MaxLN : LineNumber.LN; + Data.StackMapItem.back().second.LNIdxPlusOne = + Data.LNItems.getOrInsertIndex(LineNumber); + } + // stackMapItem, the data width of each column is recorded based on the + // maximum size. + Data.MaxBits.RegIdx = getValidBitNums(Data.RegItems.Items.size()); + Data.MaxBits.SlotIdx = getValidBitNums(Data.SlotItems.Items.size()); + Data.MaxBits.LNIdx = getValidBitNums(Data.LNItems.Items.size()); + Data.MaxBits.DerivedIdx = getValidBitNums(Data.DerivedInfo.size()); + if (EnableStackGrow) { + Data.MaxBits.SPRegIdx = Data.MaxBits.RegIdx; + Data.MaxBits.SPSlotIdx = Data.MaxBits.SlotIdx; + // add paddingBits to SPSlotIdx since PC should be byte (8 bits) aligned + uint32_t PaddingBits = + (8 - ((Data.MaxBits.RegIdx + Data.MaxBits.SlotIdx + Data.MaxBits.LNIdx + + Data.MaxBits.DerivedIdx + Data.MaxBits.SPRegIdx + + Data.MaxBits.SPSlotIdx) % + 8)) % + 8; + Data.MaxBits.SPSlotIdx += PaddingBits; + } else { + // add paddingBits to DerivedIdx since PC should be byte (8 bits) aligned + uint32_t PaddingBits = + (8 - ((Data.MaxBits.RegIdx + Data.MaxBits.SlotIdx + Data.MaxBits.LNIdx + + Data.MaxBits.DerivedIdx) % + 8)) % + 8; + Data.MaxBits.DerivedIdx += PaddingBits; + } + + // RegItem + Data.MaxBits.RegBit = getValidBitNums(WidthInfo.RegBit); + // SlotItem. 8: 8 bits per byte + Data.MaxBits.BaseOffset = WidthInfo.BaseOffsetBytes * 8; + Data.MaxBits.SlotBit = WidthInfo.SlotBitIdx + 1; + // 1024: a empirical value of stacksize. + if (EnableCompressedBitMap && Data.StackSize > 1024 && + WidthInfo.CompressedSlotBit != 0 && + Data.MaxBits.SlotBit > WidthInfo.CompressedSlotBit) { + Data.FormatType = CJ_STACKMAP_COMPRESSED_BITMAP; + Data.MaxBits.SlotBit = WidthInfo.CompressedSlotBit; + } else { + Data.FormatType = CJ_STACKMAP_BITMAP; + } + // LineNumberItem + Data.MaxBits.LN = getValidBitNums(MaxLN); + return; +} + +// ========================= +// varInt StackSize +// varInt FormatVersion +// varInt CalleeSaveReg +// varInt CalleeSaveOffsets[] +// ========================= +// varInt StackMapItemNums +// varInt RegIdx +// varInt SlotIdx +// varInt LNIdx +// varInt DerivedIdx (EnableCJCopyGC) +// varInt SPRegIdx (EnableStackGrow) +// varInt SPSlotIdx (EnableStackGrow) +// varInt PaddingBits +// bits[PaddingBits] +// StackMapItemNums * {PC, RegIdxPlusOne, SlotIdxPlusOne, LNIdxPlusOne, +// DerivedInfoStartIdx(EnableCJCopyGC), +// SPRegIdxPlusOne(EnableStackGrow), +// SPSlotIdxPlusOne(EnableStackGrow)} +// ========================= +// varInt RegInfosNums +// varInt RegBit +// RegInfosNums * {RegBit} +// ========================= +// varInt StackSlotsNums +// varInt BaseOffset +// varInt SlotBit +// StackSlotsNums * {BaseOffset, SlotBit} +// ========================= +// varInt LineNumbersNums +// varInt LNBit +// LineNumbersNums * {LN} +// ========================= (EnableCJCopyGC) +// varInt DerivedInfoNums +// DerivedInfoNums * {RegIdxPlusOne, SlotIdxPlusOne,} +void StackMaps::emitCangjieCompressedData(MCStreamer &OS, + const CompressedInfo &Data) const { + const Triple TT(AP.MMI->getModule()->getTargetTriple()); + bool IsWindows = TT.isOSWindows(); + DataEncoder Writer( + OS, Data, + ((IsWindows && isX86_64()) ? X86WinPrologueBit2Reg + : isX86_64() ? X86PrologueBit2Reg + : isAArch64() ? AArch64PrologueBit2Reg : ARMPrologueBit2Reg), + (isX86_64() ? X86Bit2Reg + : isAArch64() ? AArch64Bit2Reg : ARMBit2Reg)); + Writer.emitPrologueAndStackMapItemHeader(); + // emit PC breaks the consistency of the emit buffer + Writer.emitStackMapItem(); + if (!Data.StackMapItem.empty()) { + // Now we can write all the data before we emit the buffer. + Writer.writeRegRefAndSlotRef(); + Writer.writeLineNumber(); + Writer.writeDerivedInfo(); + Writer.emitCommentForDataAfterStackMapItem(); + } + Writer.emitBufferContent(); + return; +} + +// varInt CalleeSaveReg +// varInt CalleeSaveOffsets[] +// ========================= +// varInt StackMapItemNums +// varInt RegIdx +// varInt SlotIdx +// varInt LNIdx +// varInt DerivedIdx (EnableCJCopyGC) +// varInt SPRegIdx (EnableStackGrow) +// varInt SPSlotIdx (EnableStackGrow) +// varInt PaddingBits +void DataEncoder::emitPrologueAndStackMapItemHeader() { + writeVarUint(Data.StackSize); + writeVarUint(EnableCompressedBitMap ? Data.FormatType : CJ_STACKMAP_BITMAP); + + writeVarUint(Data.CalleeSavedReg); + for (auto Offset : Data.CalleeSavedOffsets) { + writeVarUint(Offset); + } + writeVarUint(Data.StackMapItem.size()); + if (!Data.StackMapItem.empty()) { + writeVarUint(Data.MaxBits.RegIdx); + writeVarUint(Data.MaxBits.SlotIdx); + writeVarUint(Data.MaxBits.LNIdx); + writeVarUint(Data.MaxBits.DerivedIdx); + if (EnableStackGrow) { + writeVarUint(Data.MaxBits.SPRegIdx); + writeVarUint(Data.MaxBits.SPSlotIdx); + } + } + writeBitsToPadding(); + // emitBuffer since PC should be Byte aligned and can't write into buffer + emitCommentForPrologueAndStackMapItemHeader(); + emitBufferContent(); +} + +// StackMapItemNums * {PC, RegIdxPlusOne, SlotIdxPlusOne, LNIdxPlusOne, +// DerivedInfoStartIdx(EnableCJCopyGC), +// SPRegIdxPlusOne(EnableStackGrow), +// SPSlotIdxPlusOne(EnableStackGrow)} +void DataEncoder::emitStackMapItem() { + for (const auto &StackMap : Data.StackMapItem) { + // emit PC breaks the consistency of the emit buffer + OS.emitValue(StackMap.first, 4); + writeBits(Data.MaxBits.RegIdx, StackMap.second.RegIdxPlusOne); + writeBits(Data.MaxBits.SlotIdx, StackMap.second.SlotIdxPlusOne); + writeBits(Data.MaxBits.LNIdx, StackMap.second.LNIdxPlusOne); + writeBits(Data.MaxBits.DerivedIdx, StackMap.second.DerivedInfoStartIdx); + + if (EnableStackGrow) { + writeBits(Data.MaxBits.SPRegIdx, StackMap.second.SPRegIdxPlusOne); + writeBits(Data.MaxBits.SPSlotIdx, StackMap.second.SPSlotIdxPlusOne); + } + emitCommentForStackMapItem(); + emitBufferContent(); + } +} + +// varInt RegInfosNums +// varInt RegBit +// RegInfosNums * {RegBit} +// ========================= +// varInt SlotsNums +// varInt BaseOffset +// varInt SlotBit +// SlotsNums * {BaseOffset, SlotBit} +void DataEncoder::writeRegRefAndSlotRef() { + // skip index 0 since it is invalid + writeVarUint(Data.RegItems.Items.size() - 1); + writeVarUint(Data.MaxBits.RegBit); + for (unsigned I = 1, E = Data.RegItems.Items.size(); I < E; ++I) { + writeBits(Data.MaxBits.RegBit, Data.RegItems.Items[I].RegBit); + } + // skip index 0 since it is invalid + writeVarUint(Data.SlotItems.Items.size() - 1); + writeVarUint(Data.MaxBits.BaseOffset); + writeVarUint(Data.MaxBits.SlotBit); + for (unsigned I = 1, E = Data.SlotItems.Items.size(); I < E; ++I) { + writeBits(Data.MaxBits.BaseOffset, Data.SlotItems.Items[I].BaseOffset); + if (FormatType == CJ_STACKMAP_BITMAP) { + writeBitVec(Data.MaxBits.SlotBit, Data.SlotItems.Items[I].SlotBit); + } else { + writeCompressedBitVec(Data.MaxBits.SlotBit, + Data.SlotItems.Items[I].CompressedSlotBit); + } + } +} +// varInt LineNumbersNums +// varInt LNBit +// LineNumbersNums * {LN} +void DataEncoder::writeLineNumber() { + // skip index 0 since it is invalid + writeVarUint(Data.LNItems.Items.size() - 1); + writeVarUint(Data.MaxBits.LN); + for (unsigned I = 1, E = Data.LNItems.Items.size(); I < E; ++I) { + writeBits(Data.MaxBits.LN, Data.LNItems.Items[I].LN); + } +} +// varInt DerivedInfoNums +// DerivedInfoNums * {RegIdxPlusOne, SlotIdxPlusOne,} +void DataEncoder::writeDerivedInfo() { + writeVarUint(Data.DerivedInfo.size() - 1); + for (unsigned I = 1, E = Data.DerivedInfo.size(); I < E; ++I) { + writeBits(Data.MaxBits.RegIdx, Data.DerivedInfo[I].first); + writeBits(Data.MaxBits.SlotIdx, Data.DerivedInfo[I].second); + } +} + +void DataEncoder::emitBufferContent() { + if (Buffer.empty()) { + RemainedBits = 0; + return; + } + unsigned Idx = 0; + while (Idx < Buffer.size() - 1) { + OS.emitIntValue(Buffer[Idx++], 8); // 8: 8 bytes of uint64_t + } + uint64_t Value = Buffer[Idx]; + // 64: use uint64_t as buffer + int32_t UsedBits = 64 - RemainedBits; + while (UsedBits > 0) { + OS.emitIntValue(Value & 0xFF, 1); + // 8: 8 bits per Byte + Value >>= 8; + UsedBits -= 8; + } + // clear Buffer + reset(); + return; +} + +void DataEncoder::emitCommentForPrologueAndStackMapItemHeader() { + const static std::vector StackMapTypeStr = { + "bit map", "compressed bit map"}; + SmallString<128> Str; // initial vector size + raw_svector_ostream Comment(Str); + uint64_t StackSize = readVarUint(); + Comment << "StackSize: " << StackSize; + FormatType = readVarUint(); + Comment << "\n\t#StackmapFormatType: " << StackMapTypeStr.at(FormatType); + Comment << "\n\t#CalleeSaveReg: "; + int32_t CalleeSaveReg = readVarUint(); + int CalleeSaveOffsetsSize = 0; + (Comment << "(0x").write_hex(CalleeSaveReg) << "=" << CalleeSaveReg << ")"; + for (int I = 0, E = PrologueBit2Reg.size(); I < E; ++I) { + if (CalleeSaveReg & (1 << I)) { + Comment << ", " << PrologueBit2Reg.at(I); + CalleeSaveOffsetsSize++; + } + } + Comment << ", offsets(without sign): ["; + if (CalleeSaveOffsetsSize != 0) { + for (int I = 0; I < CalleeSaveOffsetsSize - 1; ++I) { + Comment << (readVarUint() * OffsetStepSize) << ", "; + } + Comment << (readVarUint() * OffsetStepSize); + } + uint32_t StackMapItemNum = readVarUint(); + Comment << "]\n\t#StackMapItem nums:" << StackMapItemNum; + OS.emitRawComment(Comment.str()); + + if (StackMapItemNum > 0) { + MaxBits.RegIdx = readVarUint(); + MaxBits.SlotIdx = readVarUint(); + MaxBits.LNIdx = readVarUint(); + MaxBits.DerivedIdx = readVarUint(); + if (EnableStackGrow) { + MaxBits.SPRegIdx = readVarUint(); + MaxBits.SPSlotIdx = readVarUint(); + } + } + uint64_t SkipBitNums = readVarUint(); + if (SkipBitNums != 0) { + readBits(SkipBitNums); + } +} + +void DataEncoder::emitCommentForStackMapItem() { + SmallString<128> Str; // initial vector size + raw_svector_ostream Comment(Str); + int32_t RegIdx = readBits(MaxBits.RegIdx) - 1; + int32_t SlotIdx = readBits(MaxBits.SlotIdx) - 1; + int32_t LNIdx = readBits(MaxBits.LNIdx) - 1; + int32_t DerivedStartIdx = readBits(MaxBits.DerivedIdx) - 1; + if (EnableStackGrow) { + int32_t SPRegIdx = readBits(MaxBits.SPRegIdx) - 1; + int32_t SPSlotIdx = readBits(MaxBits.SPSlotIdx) - 1; + Comment << "[RegIdx: " << RegIdx << ", SlotIdx: " << SlotIdx + << ", LNIdx: " << LNIdx << ", DerivedStartIdx: " << DerivedStartIdx + << ", SPRegIdx: " << SPRegIdx << ", SPSlotIdx: " << SPSlotIdx << "]"; + } else { + Comment << "[RegIdx: " << RegIdx << ", SlotIdx: " << SlotIdx + << ", LNIdx: " << LNIdx << ", DerivedStartIdx: " << DerivedStartIdx + << "]"; + } + OS.emitRawComment(Comment.str()); +} + +void DataEncoder::emitCommentForSlots(raw_svector_ostream &Comment, + unsigned int I) { + int32_t BaseOffset = readBitsAsInt(MaxBits.BaseOffset); + auto EmitSlotBitComment = [&](uint64_t SlotBit, unsigned Width) { + (Comment << " 0x").write_hex(SlotBit) << "["; + for (unsigned Idx = 0; Idx < Width; ++Idx) { // 64: uint64_t + if (SlotBit & ((uint64_t)1 << Idx)) { + // 8: Each bit represents 8 Byte offsets + int32_t Off = BaseOffset - Idx * 8; + Comment << " " << Off; + } + } + Comment << " ]"; + }; + + Comment << "\n\t\t#Idx[" << I << "]: BaseOffset: " << BaseOffset + << ", SlotBits:"; + int32_t SlotBitBitNums = (int32_t)MaxBits.SlotBit; + if (FormatType == CJ_STACKMAP_BITMAP) { + // 64: use uint64_t to store info + while (SlotBitBitNums > 64) { + SlotBitBitNums -= 64; + EmitSlotBitComment(readBits(64), 64); + BaseOffset -= 64 * 8; // each bit represents 8 bytes + } + EmitSlotBitComment(readBits(SlotBitBitNums), SlotBitBitNums); + } else { + uint64_t IsOrginalVal; + uint64_t AllOne; + uint64_t Cnts; + while (SlotBitBitNums > 0) { + IsOrginalVal = readBits(1); + SlotBitBitNums--; + if (IsOrginalVal) { + int32_t EmitBits = std::min(SlotBitBitNums, RawDataWidth); + EmitSlotBitComment(readBits(EmitBits), EmitBits); + SlotBitBitNums -= EmitBits; + BaseOffset -= EmitBits * 8; + } else { + // bits are padding bits if remain bits are less than var int bits + 1 + if (SlotBitBitNums < SingleVarIntBits + 1) { + readBits(SlotBitBitNums); + break; + } + AllOne = readBits(1); + SlotBitBitNums--; + Cnts = readVarUint(); + SlotBitBitNums -= getVarIntBitNumsForUInt(Cnts); + assert(SlotBitBitNums >= 0 && "slot bit nums must >= 0!\n"); + // Cnts == 0 means we are in padding bits. read remain bits and break + if (Cnts == 0) { + while (SlotBitBitNums > 64) { + readBits(64); + SlotBitBitNums -= 64; + } + readBits(SlotBitBitNums); + break; + } + Comment << (AllOne ? " AllOnes(Ref)[" : " AllZeros(Val)[") << BaseOffset + << "..." + << (BaseOffset - (int32_t)(Cnts * RawDataWidth - 1) * 8) + << " ]"; + BaseOffset -= Cnts * RawDataWidth * 8; + } + } + } + return; +} + +void DataEncoder::emitCommentForRegsAndSlots() { + SmallString<128> Str; // initial vector size + raw_svector_ostream Comment(Str); + uint32_t RegNums = readVarUint(); + Comment << "RegNums: " << RegNums; + MaxBits.RegBit = readVarUint(); + for (unsigned I = 0; I < RegNums; ++I) { + uint32_t Reg = readBits(MaxBits.RegBit); + (Comment << "\n\t\t#Idx[" << I << "]: (0x").write_hex(Reg) + << "=" << Reg << ")"; + for (int J = 0; J < 32; ++J) { // 32: uint32_t + if (Reg & (1 << J)) { + Comment << ", " << Bit2RegStr.at(J); + } + } + } + uint32_t SlotsNums = readVarUint(); + Comment << "\n\t#SlotsNums: " << SlotsNums; + MaxBits.BaseOffset = readVarUint(); + MaxBits.SlotBit = readVarUint(); + for (unsigned I = 0; I < SlotsNums; ++I) { + emitCommentForSlots(Comment, I); + } + OS.emitRawComment(Comment.str()); +} + +void DataEncoder::emitCommentForLineNumber() { + SmallString<128> Str; // initial vector size + raw_svector_ostream Comment(Str); + uint32_t LineNumbersNums = readVarUint(); + Comment << "LineNumbersNums: " << LineNumbersNums; + MaxBits.LN = readVarUint(); + for (unsigned I = 0; I < LineNumbersNums; ++I) { + Comment << "\n\t\t#Idx[" << I << "]: " << readBits(MaxBits.LN); + } + OS.emitRawComment(Comment.str()); +} + +void DataEncoder::emitCommentForDerivedInfo() { + SmallString<128> Str; // initial vector size + raw_svector_ostream Comment(Str); + uint32_t DerivedInfoNums = readVarUint(); + Comment << "DerivedInfoNums: " << DerivedInfoNums; + for (unsigned I = 0; I < DerivedInfoNums; ++I) { + int32_t RegIdx = readBits(MaxBits.RegIdx) - 1; + int32_t SlotIdx = readBits(MaxBits.SlotIdx) - 1; + Comment << "\n\t\t#Idx[" << I << "]: RegIdx: " << RegIdx + << ", SlotIdx: " << SlotIdx; + } + OS.emitRawComment(Comment.str()); +} + +void DataEncoder::emitCommentForDataAfterStackMapItem() { + emitCommentForRegsAndSlots(); + emitCommentForLineNumber(); + emitCommentForDerivedInfo(); +} diff --git a/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp b/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp index 9430e86fe..dbf0b87e1 100644 --- a/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp +++ b/llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp @@ -13,6 +13,7 @@ #include "llvm/ADT/BitVector.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/TargetFrameLowering.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" @@ -62,6 +63,12 @@ TargetFrameLowering::getFrameIndexReference(const MachineFunction &MF, int FI, MFI.getOffsetAdjustment()); } +StackOffset +TargetFrameLowering::getFrameIndexRefForCJ(const MachineFunction &MF, int FI, + Register &FrameReg) const { + return getFrameIndexReference(MF, FI, FrameReg); +} + bool TargetFrameLowering::needsFrameIndexResolution( const MachineFunction &MF) const { return MF.getFrameInfo().hasStackObjects(); @@ -163,6 +170,56 @@ bool TargetFrameLowering::isSafeForNoCSROpt(const Function &F) { return true; } +// Prepare for Cangjie StackCheck: find CJ_MCC_StackCheck, and +// store the size. +// For X86: the size is frame size +// For aarch64: the size is addsize calculated for sp +MachineBasicBlock::iterator TargetFrameLowering::preCJStackCheck( + MachineFunction &MF, MachineBasicBlock::iterator InsertPos, int32_t Size, + bool NeedRecoverStack) const { + if (!MF.getFunction().hasCangjieGC()) { + return InsertPos; + } + + for (auto BBI = MF.front().begin(); BBI != MF.front().end(); BBI++) { + MachineInstr *MI = &*BBI; + if (MI->getOpcode() != TargetOpcode::STATEPOINT) + continue; + + StatepointOpers SO(MI); + if (!SO.isCJStackCheck()) + continue; + + assert((Size >= 0) && "Size should not be less than 0!"); + unsigned FrameSize = Size; + if (!NeedRecoverStack) { + // Use MSB(Most Significant Bit)to mark + // whether the stack needs to be recovered. + FrameSize = FrameSize | (1 << 31); + } + + MachineBasicBlock *CurBB = BBI->getParent(); + // This is not the last inst in BB, we move to the current inst. + // Otherwise, it is placed at the end of BB. + if (InsertPos == BBI) { + MI->setCJStackSize(FrameSize); + return ++BBI; + } else if (InsertPos != CurBB->end()) { + MI->moveBefore(&*InsertPos); + MI->setCJStackSize(FrameSize); + } else { + auto CloneMI = MF.CloneMachineInstr(MI); + CurBB->push_back(CloneMI); + // The size is stored in DebugInstrNum interface temporarily and + // it will be used for emitStackCheck. + CloneMI->setCJStackSize(FrameSize); + MI->eraseFromParent(); + } + break; + } + return InsertPos; +} + int TargetFrameLowering::getInitialCFAOffset(const MachineFunction &MF) const { llvm_unreachable("getInitialCFAOffset() not implemented!"); } diff --git a/llvm/lib/CodeGen/TargetLoweringBase.cpp b/llvm/lib/CodeGen/TargetLoweringBase.cpp index a342a4dd1..b9cb176e9 100644 --- a/llvm/lib/CodeGen/TargetLoweringBase.cpp +++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp @@ -1218,6 +1218,7 @@ TargetLoweringBase::emitPatchPoint(MachineInstr &InitialMI, MIB.addImm(StackMaps::DirectMemRefOp); MIB.add(MO); MIB.addImm(0); + MIB.addImm(FI); } assert(MIB->mayLoad() && "Folded a stackmap use to a non-load!"); diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp index 2badbe34a..9d3a43a04 100644 --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -593,6 +593,78 @@ static unsigned getEntrySizeForKind(SectionKind Kind) { } } +static StringRef getCJSectionPrefixForGlobal(SectionKind Kind) { + assert((Kind.isCJMetaData() && !Kind.isCJMetadataInfo()) && + "It is not a cangjie section Kind!"); + if (Kind.isCJTypeInfo()) + return ".cjmetadata.typeinfo"; + if (Kind.isCJTypeTemplate()) + return ".cjmetadata.typetemplate"; + if (Kind.isCJTypeFields()) + return ".cjmetadata.type.fields"; + if (Kind.isCJGCTib()) + return ".cjmetadata.gctib"; + if (Kind.isCJMTable()) + return ".cjmetadata.mtable"; + if (Kind.isCJReflectPkgInfo()) + return ".cjmetadata.reflect.pkginfo"; + if (Kind.isCJReflectGV()) + return ".cjmetadata.reflect.gv"; + if (Kind.isCJInnerTypeExtensions()) + return ".cjmetadata.innerty.eds"; + if (Kind.isCJReflectGenericTI()) + return ".cjmetadata.reflect.generic.ti"; + llvm_unreachable("Unknown section kind"); +} + +static StringRef getCJCOFFSectionPrefixForGlobal(SectionKind Kind) { + assert((Kind.isCJMetaData() && !Kind.isCJMetadataInfo()) && + "It is not a cangjie section kind!"); + if (Kind.isCJTypeInfo()) + return ".cjti"; + if (Kind.isCJTypeTemplate()) + return ".cjtt"; + if (Kind.isCJTypeFields()) + return ".cjfield"; + if (Kind.isCJGCTib()) + return ".cjgctib"; + if (Kind.isCJMTable()) + return ".cjmtbl"; + if (Kind.isCJReflectPkgInfo()) + return ".cjrflp"; + if (Kind.isCJReflectGV()) + return ".cjrflv"; + if (Kind.isCJInnerTypeExtensions()) + return ".cjinty"; + if (Kind.isCJReflectGenericTI()) + return ".cjrflg"; + llvm_unreachable("Unknown section kind"); +} + +static StringRef getCJMachOSectionPrefixForGlobal(SectionKind Kind) { + assert((Kind.isCJMetaData() && !Kind.isCJMetadataInfo()) && + "It is not a cangjie section Kind!"); + if (Kind.isCJTypeInfo()) + return "__cjtypeinfo"; + if (Kind.isCJTypeTemplate()) + return "__cjtemplate"; + if (Kind.isCJTypeFields()) + return "__cj_fields"; + if (Kind.isCJGCTib()) + return "__cjgctib"; + if (Kind.isCJMTable()) + return "__cjmtable"; + if (Kind.isCJReflectPkgInfo()) + return "__cjref_pkginfo"; + if (Kind.isCJReflectGV()) + return "__cjref_gv"; + if (Kind.isCJInnerTypeExtensions()) + return "__cjinnerty_eds"; + if (Kind.isCJReflectGenericTI()) + return "__cjref_gi"; + llvm_unreachable("Unknown section kind"); +} + /// Return the section prefix name used by options FunctionsSections and /// DataSections. static StringRef getSectionPrefixForGlobal(SectionKind Kind) { @@ -610,6 +682,8 @@ static StringRef getSectionPrefixForGlobal(SectionKind Kind) { return ".data"; if (Kind.isReadOnlyWithRel()) return ".data.rel.ro"; + if (Kind.isCJMetaData()) + return getCJSectionPrefixForGlobal(Kind); llvm_unreachable("Unknown section kind"); } @@ -618,6 +692,12 @@ getELFSectionNameForGlobal(const GlobalObject *GO, SectionKind Kind, Mangler &Mang, const TargetMachine &TM, unsigned EntrySize, bool UniqueSectionName) { SmallString<128> Name; + bool IsCJInitFunction = false; + if (auto *F = dyn_cast(GO)) { + if (F->hasFnAttribute("cjinit")) { + IsCJInitFunction = true; + } + } if (Kind.isMergeableCString()) { // We also need alignment here. // FIXME: this is getting the alignment of the character, not the @@ -630,6 +710,8 @@ getELFSectionNameForGlobal(const GlobalObject *GO, SectionKind Kind, } else if (Kind.isMergeableConst()) { Name = ".rodata.cst"; Name += utostr(EntrySize); + } else if (IsCJInitFunction) { + Name = ".cjinit_function"; } else { Name = getSectionPrefixForGlobal(Kind); } @@ -763,9 +845,14 @@ static MCSection *selectExplicitSectionGlobal( SectionName = Attrs.getAttribute("data-section").getValueAsString(); } } - const Function *F = dyn_cast(GO); - if (F && F->hasFnAttribute("implicit-section-name")) { - SectionName = F->getFnAttribute("implicit-section-name").getValueAsString(); + + if (const auto *F = dyn_cast(GO)) { + if (F->hasFnAttribute("implicit-section-name")) { + SectionName = + F->getFnAttribute("implicit-section-name").getValueAsString(); + } else if (F->hasFnAttribute("cjinit")) { + SectionName = ".cjinit_function"; + } } // Infer section flags from the section name if we can. @@ -1249,8 +1336,12 @@ MCSection *TargetLoweringObjectFileMachO::getExplicitSectionGlobal( StringRef SectionName = GO->getSection(); const Function *F = dyn_cast(GO); - if (F && F->hasFnAttribute("implicit-section-name")) { - SectionName = F->getFnAttribute("implicit-section-name").getValueAsString(); + if (F) { + if (F->hasFnAttribute("implicit-section-name")) { + SectionName = F->getFnAttribute("implicit-section-name").getValueAsString(); + } else if (F->hasFnAttribute("cjinit")) { + SectionName = "__TEXT,__cjinit_func,regular,pure_instructions"; + } } // Parse the section specifier and create it if valid. @@ -1301,6 +1392,10 @@ MCSection *TargetLoweringObjectFileMachO::SelectSectionForGlobal( if (Kind.isText()) return GO->isWeakForLinker() ? TextCoalSection : TextSection; + if (Kind.isCJMetaData()) + return getContext().getMachOSection("__CJ_METADATA", + getCJMachOSectionPrefixForGlobal(Kind), 0, SectionKind::getReadOnly()); + // If this is weak/linkonce, put this in a coalescable section, either in text // or data depending on if it is writable. if (GO->isWeakForLinker()) { @@ -1618,6 +1713,11 @@ MCSection *TargetLoweringObjectFileCOFF::getExplicitSectionGlobal( int Selection = 0; unsigned Characteristics = getCOFFSectionFlags(Kind, TM); StringRef Name = GO->getSection(); + if (const auto *F = dyn_cast(GO)) { + if (F->hasFnAttribute("cjinit")) { + Name = ".cjinit_function"; + } + } StringRef COMDATSymName = ""; if (GO->hasComdat()) { Selection = getSelectionForCOFF(GO); @@ -1649,6 +1749,8 @@ static StringRef getCOFFSectionNameForUniqueGlobal(SectionKind Kind) { return ".tls$"; if (Kind.isReadOnly() || Kind.isReadOnlyWithRel()) return ".rdata"; + if (Kind.isCJMetaData()) + return getCJCOFFSectionPrefixForGlobal(Kind); return ".data"; } @@ -1714,6 +1816,14 @@ MCSection *TargetLoweringObjectFileCOFF::SelectSectionForGlobal( if (Kind.isReadOnly() || Kind.isReadOnlyWithRel()) return ReadOnlySection; + if (Kind.isCJMetaData()) { + return getContext().getCOFFSection(getCOFFSectionNameForUniqueGlobal(Kind), + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | + COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getReadOnly()); + } + // Note: we claim that common symbols are put in BSSSection, but they are // really emitted with the magic .comm directive, which creates a symbol table // entry but not a section. diff --git a/llvm/lib/CodeGen/TargetPassConfig.cpp b/llvm/lib/CodeGen/TargetPassConfig.cpp index 0bd229f4f..ec46d90f1 100644 --- a/llvm/lib/CodeGen/TargetPassConfig.cpp +++ b/llvm/lib/CodeGen/TargetPassConfig.cpp @@ -53,6 +53,12 @@ using namespace llvm; +namespace llvm { +extern cl::opt CJPipeline; +extern cl::opt CangjieJIT; +extern cl::opt EnableStackGrow; +} + static cl::opt EnableIPRA("enable-ipra", cl::init(false), cl::Hidden, cl::desc("Enable interprocedural register allocation " @@ -149,7 +155,7 @@ static cl::opt EnableFastISelOption("fast-isel", cl::Hidden, cl::desc("Enable the \"fast\" instruction selector")); -static cl::opt EnableGlobalISelOption( +cl::opt EnableGlobalISelOption( "global-isel", cl::Hidden, cl::desc("Enable the \"global\" instruction selector")); @@ -264,6 +270,11 @@ static cl::opt DisableSelectOptimize( "disable-select-optimize", cl::init(true), cl::Hidden, cl::desc("Disable the select-optimization pass from running")); +/// Enable cangjie thread sanitizer pass. +static cl::opt CangjieThreadSanitizer( + "tsan", cl::init(false), cl::Hidden, + cl::desc("Enable cangjie thread sanitizer pass")); + /// Allow standard passes to be disabled by command line options. This supports /// simple binary flags that either suppress the pass or do nothing. /// i.e. -disable-mypass=false has no effect. @@ -1113,6 +1124,21 @@ bool TargetPassConfig::addISelPasses() { addPass(createPreISelIntrinsicLoweringPass()); PM->add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis())); + + if (CJPipeline) { + if (CangjieJIT) { + addPass(createCJFillMetadataLegacyPass()); + addPass(createCJRuntimeLoweringLegacyPass()); + addPass(createCangjieSpecificOptLegacyPass(0)); + addPass(createPlaceSafepointsLegacyPass()); + addPass(createCJRewriteStatepointLegacyPass(0)); + } + addPass(createCJBarrierLoweringPass(getOptLevel())); + if (CangjieThreadSanitizer) { + addPass(createThreadSanitizerPass()); + } + } + addIRPasses(); addCodeGenPrepare(); addPassesToHandleExceptions(); @@ -1191,6 +1217,9 @@ void TargetPassConfig::addMachinePasses() { // Run post-ra passes. addPostRegAlloc(); + if (CJPipeline && EnableStackGrow && !CangjieJIT) + addPass(&CJStackPointerInserterID); + addPass(&RemoveRedundantDebugValuesID); addPass(&FixupStatepointCallerSavedID); diff --git a/llvm/lib/CodeGen/ThreadSanitizerPass.cpp b/llvm/lib/CodeGen/ThreadSanitizerPass.cpp new file mode 100644 index 000000000..0b2c5cfec --- /dev/null +++ b/llvm/lib/CodeGen/ThreadSanitizerPass.cpp @@ -0,0 +1,428 @@ +//===---------------------- ThreadSanitizerPass.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 +// +//===----------------------------------------------------------------------===// +// +// The thread sanitizer pass. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/Passes.h" +#include "llvm/InitializePasses.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/SafepointIRVerifier.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" + +using namespace llvm; + +#define DEBUG_TYPE "tsan" + +namespace { + +struct ThreadSanitizer { + ThreadSanitizer() { + } + + bool sanitizeFunction(Function &F); + +private: + void initialize(Module &M); + bool instrumentAtomic(Instruction *I, const DataLayout &DL); + bool instrumentCJAtomic(Instruction *I, const DataLayout &DL); + int getMemoryAccessFuncIndex(Type *OrigTy, Value *Addr, const DataLayout &DL); + + Type *IntptrTy; + // Accesses sizes are powers of two: 1, 2, 4, 8, 16. + static const size_t kNumberOfAccessSizes = 5; + FunctionCallee TsanAtomicLoad[kNumberOfAccessSizes]; + FunctionCallee TsanAtomicStore[kNumberOfAccessSizes]; + FunctionCallee TsanAtomicRMW[AtomicRMWInst::LAST_BINOP + 1] + [kNumberOfAccessSizes]; + FunctionCallee TsanAtomicCAS[kNumberOfAccessSizes]; + FunctionCallee TsanAtomicThreadFence; + FunctionCallee TsanAtomicSignalFence; +}; +} // namespace + +class ThreadSanitizerPass : public FunctionPass { +public: + static char ID; + ThreadSanitizerPass(); + bool runOnFunction(Function &F) override; + StringRef getPassName() const override; +}; + +char ThreadSanitizerPass::ID = 0; + +bool ThreadSanitizerPass::runOnFunction(Function &F) { + ThreadSanitizer TSan; + auto Res = TSan.sanitizeFunction(F); + return Res; +} + +StringRef ThreadSanitizerPass::getPassName() const { + return "Cangjie Thread Sanitizer pass"; +} + +ThreadSanitizerPass::ThreadSanitizerPass() : FunctionPass(ID) { + initializeThreadSanitizerPassPass(*PassRegistry::getPassRegistry()); +} + +void ThreadSanitizer::initialize(Module &M) { + const DataLayout &DL = M.getDataLayout(); + IntptrTy = DL.getIntPtrType(M.getContext()); + + IRBuilder<> IRB(M.getContext()); + AttributeList Attr; + Attr = Attr.addFnAttribute(M.getContext(), Attribute::NoUnwind); + + IntegerType *OrdTy = IRB.getInt32Ty(); + for (size_t i = 0; i < kNumberOfAccessSizes; ++i) { + const unsigned ByteSize = 1U << i; + const unsigned BitSize = ByteSize * 8; + std::string ByteSizeStr = utostr(ByteSize); + std::string BitSizeStr = utostr(BitSize); + + Type *Ty = Type::getIntNTy(M.getContext(), BitSize); + Type *PtrTy = Ty->getPointerTo(); + SmallString<32> AtomicLoadName("__tsan_atomic" + BitSizeStr + "_load"); + { + AttributeList AL = Attr; + AL = AL.addParamAttribute(M.getContext(), 1, Attribute::ZExt); + TsanAtomicLoad[i] = + M.getOrInsertFunction(AtomicLoadName, AL, Ty, PtrTy, OrdTy); + } + + SmallString<32> AtomicStoreName("__tsan_atomic" + BitSizeStr + "_store"); + { + AttributeList AL = Attr; + AL = AL.addParamAttribute(M.getContext(), 1, Attribute::ZExt); + AL = AL.addParamAttribute(M.getContext(), 2, Attribute::ZExt); + TsanAtomicStore[i] = M.getOrInsertFunction( + AtomicStoreName, AL, IRB.getVoidTy(), PtrTy, Ty, OrdTy); + } + + for (unsigned Op = AtomicRMWInst::FIRST_BINOP; + Op <= AtomicRMWInst::LAST_BINOP; ++Op) { + TsanAtomicRMW[Op][i] = nullptr; + const char *NamePart = nullptr; + if (Op == AtomicRMWInst::Xchg) + NamePart = "_exchange"; + else if (Op == AtomicRMWInst::Add) + NamePart = "_fetch_add"; + else if (Op == AtomicRMWInst::Sub) + NamePart = "_fetch_sub"; + else if (Op == AtomicRMWInst::And) + NamePart = "_fetch_and"; + else if (Op == AtomicRMWInst::Or) + NamePart = "_fetch_or"; + else if (Op == AtomicRMWInst::Xor) + NamePart = "_fetch_xor"; + else if (Op == AtomicRMWInst::Nand) + NamePart = "_fetch_nand"; + else + continue; + SmallString<32> RMWName("__tsan_atomic" + itostr(BitSize) + NamePart); + { + AttributeList AL = Attr; + AL = AL.addParamAttribute(M.getContext(), 1, Attribute::ZExt); + AL = AL.addParamAttribute(M.getContext(), 2, Attribute::ZExt); + TsanAtomicRMW[Op][i] = + M.getOrInsertFunction(RMWName, AL, Ty, PtrTy, Ty, OrdTy); + } + } + + SmallString<32> AtomicCASName("__tsan_atomic" + BitSizeStr + + "_compare_exchange_val"); + { + AttributeList AL = Attr; + AL = AL.addParamAttribute(M.getContext(), 1, Attribute::ZExt); + AL = AL.addParamAttribute(M.getContext(), 2, Attribute::ZExt); + AL = AL.addParamAttribute(M.getContext(), 3, Attribute::ZExt); + AL = AL.addParamAttribute(M.getContext(), 4, Attribute::ZExt); + TsanAtomicCAS[i] = M.getOrInsertFunction(AtomicCASName, AL, Ty, PtrTy, Ty, + Ty, OrdTy, OrdTy); + } + } + + { + AttributeList AL = Attr; + AL = AL.addParamAttribute(M.getContext(), 0, Attribute::ZExt); + TsanAtomicThreadFence = M.getOrInsertFunction("__tsan_atomic_thread_fence", + AL, IRB.getVoidTy(), OrdTy); + } + { + AttributeList AL = Attr; + AL = AL.addParamAttribute(M.getContext(), 0, Attribute::ZExt); + TsanAtomicSignalFence = M.getOrInsertFunction("__tsan_atomic_signal_fence", + AL, IRB.getVoidTy(), OrdTy); + } +} + +static bool isCJAtomic(const Instruction *I) { + const IntrinsicInst *II = dyn_cast(I); + if (II == nullptr) { + return false; + } + auto IID = II->getIntrinsicID(); + return isCJAtomicIntrinsic(IID); +} + +static bool isTsanAtomic(const Instruction *I) { + // TODO: Ask TTI whether synchronization scope is between threads. + auto SSID = getAtomicSyncScopeID(I); + if (!SSID) + return false; + if (isa(I) || isa(I)) + return SSID.value() != SyncScope::SingleThread; + return true; +} + +bool ThreadSanitizer::sanitizeFunction(Function &F) { + initialize(*F.getParent()); + SmallVector AtomicAccesses; + SmallVector CJAtomicAccesses; + bool Res = false; + const DataLayout &DL = F.getParent()->getDataLayout(); + for (auto &BB : F) { + for (auto &Inst : BB) { + if (isTsanAtomic(&Inst)) + AtomicAccesses.push_back(&Inst); + else if (isCJAtomic(&Inst)) + CJAtomicAccesses.push_back(&Inst); + } + } + + // Instrument atomic memory accesses in any case (they can be used to + // implement synchronization). + for (auto Inst : AtomicAccesses) { + Res |= instrumentAtomic(Inst, DL); + } + for (auto Inst : CJAtomicAccesses) { + Res |= instrumentCJAtomic(Inst, DL); + } + return Res; +} + +enum class TsanAtomicOrdering: uint32_t { + Unordered = 0, + Consume = 1, + Acquire = 2, + Release = 3, + AcquireRelease = 4, + SequentiallyConsistent = 5 +}; + +static ConstantInt *createOrdering(IRBuilder<> *IRB, AtomicOrdering ord) { + uint32_t v = 0; + switch (ord) { + case AtomicOrdering::NotAtomic: + llvm_unreachable("unexpected atomic ordering!"); + case AtomicOrdering::Unordered: + LLVM_FALLTHROUGH; + case AtomicOrdering::Monotonic: + v = static_cast(TsanAtomicOrdering::Unordered); + break; + case AtomicOrdering::Acquire: + v = static_cast(TsanAtomicOrdering::Acquire); + break; + case AtomicOrdering::Release: + v = static_cast(TsanAtomicOrdering::Release); + break; + case AtomicOrdering::AcquireRelease: + v = static_cast(TsanAtomicOrdering::AcquireRelease); + break; + case AtomicOrdering::SequentiallyConsistent: + v = static_cast(TsanAtomicOrdering::SequentiallyConsistent); + break; + } + return IRB->getInt32(v); +} + +// Both llvm and ThreadSanitizer atomic operations are based on C++11/C1x +// standards. For background see C++11 standard. A slightly older, publicly +// available draft of the standard (not entirely up-to-date, but close enough +// for casual browsing) is available here: +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf +// The following page contains more background information: +// http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/ + +bool ThreadSanitizer::instrumentAtomic(Instruction *I, const DataLayout &DL) { + InstrumentationIRBuilder IRB(I); + if (LoadInst *LI = dyn_cast(I)) { + Value *Addr = LI->getPointerOperand(); + Type *OrigTy = LI->getType(); + int Idx = getMemoryAccessFuncIndex(OrigTy, Addr, DL); + if (Idx < 0) + return false; + const unsigned ByteSize = 1U << Idx; + const unsigned BitSize = ByteSize * 8; + Type *Ty = Type::getIntNTy(IRB.getContext(), BitSize); + Type *PtrTy = Ty->getPointerTo(); + Value *Args[] = {IRB.CreatePointerCast(Addr, PtrTy), + createOrdering(&IRB, LI->getOrdering())}; + Value *C = IRB.CreateCall(TsanAtomicLoad[Idx], Args); + Value *Cast = IRB.CreateBitOrPointerCast(C, OrigTy); + I->replaceAllUsesWith(Cast); + } else if (StoreInst *SI = dyn_cast(I)) { + Value *Addr = SI->getPointerOperand(); + int Idx = + getMemoryAccessFuncIndex(SI->getValueOperand()->getType(), Addr, DL); + if (Idx < 0) + return false; + const unsigned ByteSize = 1U << Idx; + const unsigned BitSize = ByteSize * 8; + Type *Ty = Type::getIntNTy(IRB.getContext(), BitSize); + Type *PtrTy = Ty->getPointerTo(); + Value *Args[] = {IRB.CreatePointerCast(Addr, PtrTy), + IRB.CreateBitOrPointerCast(SI->getValueOperand(), Ty), + createOrdering(&IRB, SI->getOrdering())}; + CallInst *C = CallInst::Create(TsanAtomicStore[Idx], Args); + ReplaceInstWithInst(I, C); + } else if (AtomicRMWInst *RMWI = dyn_cast(I)) { + Value *Addr = RMWI->getPointerOperand(); + int Idx = + getMemoryAccessFuncIndex(RMWI->getValOperand()->getType(), Addr, DL); + if (Idx < 0) + return false; + FunctionCallee F = TsanAtomicRMW[RMWI->getOperation()][Idx]; + if (!F) + return false; + const unsigned ByteSize = 1U << Idx; + const unsigned BitSize = ByteSize * 8; + Type *Ty = Type::getIntNTy(IRB.getContext(), BitSize); + Type *PtrTy = Ty->getPointerTo(); + Value *Args[] = {IRB.CreatePointerCast(Addr, PtrTy), + IRB.CreateIntCast(RMWI->getValOperand(), Ty, false), + createOrdering(&IRB, RMWI->getOrdering())}; + CallInst *C = CallInst::Create(F, Args); + ReplaceInstWithInst(I, C); + } else if (AtomicCmpXchgInst *CASI = dyn_cast(I)) { + Value *Addr = CASI->getPointerOperand(); + Type *OrigOldValTy = CASI->getNewValOperand()->getType(); + int Idx = getMemoryAccessFuncIndex(OrigOldValTy, Addr, DL); + if (Idx < 0) + return false; + const unsigned ByteSize = 1U << Idx; + const unsigned BitSize = ByteSize * 8; + Type *Ty = Type::getIntNTy(IRB.getContext(), BitSize); + Type *PtrTy = Ty->getPointerTo(); + Value *CmpOperand = + IRB.CreateBitOrPointerCast(CASI->getCompareOperand(), Ty); + Value *NewOperand = + IRB.CreateBitOrPointerCast(CASI->getNewValOperand(), Ty); + Value *Args[] = {IRB.CreatePointerCast(Addr, PtrTy), + CmpOperand, + NewOperand, + createOrdering(&IRB, CASI->getSuccessOrdering()), + createOrdering(&IRB, CASI->getFailureOrdering())}; + CallInst *C = IRB.CreateCall(TsanAtomicCAS[Idx], Args); + Value *Success = IRB.CreateICmpEQ(C, CmpOperand); + Value *OldVal = C; + if (Ty != OrigOldValTy) { + // The value is a pointer, so we need to cast the return value. + OldVal = IRB.CreateIntToPtr(C, OrigOldValTy); + } + + Value *Res = + IRB.CreateInsertValue(UndefValue::get(CASI->getType()), OldVal, 0); + Res = IRB.CreateInsertValue(Res, Success, 1); + + I->replaceAllUsesWith(Res); + I->eraseFromParent(); + } else if (FenceInst *FI = dyn_cast(I)) { + Value *Args[] = {createOrdering(&IRB, FI->getOrdering())}; + FunctionCallee F = FI->getSyncScopeID() == SyncScope::SingleThread + ? TsanAtomicSignalFence + : TsanAtomicThreadFence; + CallInst *C = CallInst::Create(F, Args); + ReplaceInstWithInst(I, C); + } + return true; +} + +bool ThreadSanitizer::instrumentCJAtomic(Instruction *I, const DataLayout &DL) { + InstrumentationIRBuilder IRB(I); + auto ID = dyn_cast(I)->getIntrinsicID(); + const CallInst* CI = dyn_cast(I); + + if (ID == Intrinsic::cj_atomic_load) { + Value *Addr = CI->getArgOperand(1); + Type *Ty = CI->getType(); + int Idx = getMemoryAccessFuncIndex(Ty, Addr, DL); + Type *PtrTy = Type::getIntNPtrTy(I->getModule()->getContext(), DL.getTypeStoreSizeInBits(Ty)); + Value *Args[] = {IRB.CreatePointerCast(Addr, PtrTy), CI->getArgOperand(2)}; + + Value *C = IRB.CreateCall(TsanAtomicLoad[Idx], Args); + Value *Cast = IRB.CreateBitOrPointerCast(C, Ty); + I->replaceAllUsesWith(Cast); + } else if (ID == Intrinsic::cj_atomic_store || ID == Intrinsic::cj_atomic_swap) { + Value *Addr = CI->getArgOperand(2); + Value *Val = CI->getArgOperand(0); + int Idx = getMemoryAccessFuncIndex(Val->getType(), Addr, DL); + uint32_t TypeSize = DL.getTypeStoreSizeInBits(Val->getType()); + Type *PtrTy = Type::getIntNPtrTy(I->getModule()->getContext(), TypeSize); + Value *Args[] = {IRB.CreatePointerCast(Addr, PtrTy), + IRB.CreateBitOrPointerCast(Val, Type::getIntNTy(IRB.getContext(), TypeSize)), + CI->getArgOperand(3)}; + + if (ID == Intrinsic::cj_atomic_store) { + CallInst *C = CallInst::Create(TsanAtomicStore[Idx], Args); + ReplaceInstWithInst(I, C); + } else { + Value *C = IRB.CreateCall(TsanAtomicRMW[AtomicRMWInst::Xchg][Idx], Args); + Value *Cast = IRB.CreateBitOrPointerCast(C, Val->getType()); + I->replaceAllUsesWith(Cast); + I->eraseFromParent(); + } + } else if (ID == Intrinsic::cj_atomic_compare_swap) { + Value *Addr = CI->getArgOperand(3); + Value *CmpVal = CI->getArgOperand(0); + Value *NewVal = CI->getArgOperand(1); + int Idx = getMemoryAccessFuncIndex(NewVal->getType(), Addr, DL); + uint32_t TypeSize = DL.getTypeStoreSizeInBits(NewVal->getType()); + Type *Ty = Type::getIntNTy(IRB.getContext(), TypeSize); + Type *PtrTy = Type::getIntNPtrTy(I->getModule()->getContext(), TypeSize); + Value *Args[] = {IRB.CreatePointerCast(Addr, PtrTy), IRB.CreateBitOrPointerCast(CmpVal, Ty), + IRB.CreateBitOrPointerCast(NewVal, Ty), CI->getArgOperand(4), CI->getArgOperand(5)}; + + CallInst *C = IRB.CreateCall(TsanAtomicCAS[Idx], Args); + Value *Success = IRB.CreateICmpEQ(C, IRB.CreateBitOrPointerCast(CmpVal, Ty)); + StructType *ST = StructType::get(IRB.getContext(), {Ty, IRB.getInt1Ty()}); + Value *Res = IRB.CreateInsertValue(UndefValue::get(ST), C, 0); + Res = IRB.CreateInsertValue(Res, Success, 1); + ExtractValueInst *EI = ExtractValueInst::Create(Res, 1); + ReplaceInstWithInst(I, EI); + } + return true; +} + +int ThreadSanitizer::getMemoryAccessFuncIndex(Type *OrigTy, Value *Addr, + const DataLayout &DL) { + assert(OrigTy->isSized()); + assert( + cast(Addr->getType())->isOpaqueOrPointeeTypeMatches(OrigTy)); + uint32_t TypeSize = DL.getTypeStoreSizeInBits(OrigTy); + if (TypeSize != 8 && TypeSize != 16 && + TypeSize != 32 && TypeSize != 64 && TypeSize != 128) { + // Ignore all unusual sizes. + return -1; + } + size_t Idx = countTrailingZeros(TypeSize / 8); + assert(Idx < kNumberOfAccessSizes); + return Idx; +} + +INITIALIZE_PASS_BEGIN(ThreadSanitizerPass, "cj-thread-sanitizer-pass", + "Cangjie Thread-Sanitizer", false, false) +INITIALIZE_PASS_END(ThreadSanitizerPass, "cj-thread-sanitizer-pass", + "Cangjie Thread-Sanitizer", false, false) + +FunctionPass *llvm::createThreadSanitizerPass() { + return new ThreadSanitizerPass(); +} diff --git a/llvm/lib/CodeGen/TwoAddressInstructionPass.cpp b/llvm/lib/CodeGen/TwoAddressInstructionPass.cpp index 17fe819fa..a259b4c42 100644 --- a/llvm/lib/CodeGen/TwoAddressInstructionPass.cpp +++ b/llvm/lib/CodeGen/TwoAddressInstructionPass.cpp @@ -1657,13 +1657,50 @@ bool TwoAddressInstructionPass::processStatepoint( if (RegA == RegB) continue; + const TargetRegisterClass *RCA = MRI->getRegClass(RegA); + const TargetRegisterClass *RCB = MRI->getRegClass(RegB); + if (RCA->getID() != RCB->getID()) { + NeedCopy = true; + continue; + } + + // CodeGenPrepare can sink pointer compare past statepoint, which + // breaks assumption that statepoint kills tied-use register when + // in SSA form (see note in IR/SafepointIRVerifier.cpp). Fall back + // to generic tied register handling to avoid assertion failures. + // TODO: Recompute LIS/LV information for new range here. + if (LIS) { + const auto &UseLI = LIS->getInterval(RegB); + const auto &DefLI = LIS->getInterval(RegA); + if (DefLI.overlaps(UseLI)) { + LLVM_DEBUG(dbgs() << "LIS: " << printReg(RegB, TRI, 0) + << " UseLI overlaps with DefLI\n"); + NeedCopy = true; + continue; + } + } else if (LV && LV->getVarInfo(RegB).findKill(MI->getParent()) != MI) { + // Note that MachineOperand::isKill does not work here, because it + // is set only on first register use in instruction and for statepoint + // tied-use register will usually be found in preceeding deopt bundle. + LLVM_DEBUG(dbgs() << "LV: " << printReg(RegB, TRI, 0) + << " not killed by statepoint\n"); + NeedCopy = true; + continue; + } + MRI->replaceRegWith(RegA, RegB); if (LIS) { VNInfo::Allocator &A = LIS->getVNInfoAllocator(); LiveInterval &LI = LIS->getInterval(RegB); - for (auto &S : LIS->getInterval(RegA)) { - VNInfo *VNI = LI.getNextValue(S.start, A); + LiveInterval &Other = LIS->getInterval(RegA); + SmallVector NewVNIs; + for (const VNInfo *VNI : Other.valnos) { + assert(VNI->id == NewVNIs.size() && "assumed"); + NewVNIs.push_back(LI.createValueCopy(VNI, A)); + } + for (auto &S : Other) { + VNInfo *VNI = NewVNIs[S.valno->id]; LiveRange::Segment NewSeg(S.start, S.end, VNI); LI.addSegment(NewSeg); } diff --git a/llvm/lib/ExecutionEngine/MCJIT/MCJIT.cpp b/llvm/lib/ExecutionEngine/MCJIT/MCJIT.cpp index 4ac901daa..ef453c84f 100644 --- a/llvm/lib/ExecutionEngine/MCJIT/MCJIT.cpp +++ b/llvm/lib/ExecutionEngine/MCJIT/MCJIT.cpp @@ -22,6 +22,7 @@ #include "llvm/MC/MCContext.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MemoryBuffer.h" @@ -30,6 +31,11 @@ using namespace llvm; +namespace llvm { +extern cl::opt CJPipeline; +extern cl::opt CangjieJIT; +} // namespace llvm + namespace { static struct RegisterJIT { @@ -156,6 +162,12 @@ std::unique_ptr MCJIT::emitObject(Module *M) { // MCJIT instance, since these conditions are tested by our caller, // generateCodeForModule. + Metadata *MD = M->getModuleFlag("Cangjie_OPT"); + if (MD != nullptr) { + CJPipeline = true; + CangjieJIT = true; + } + legacy::PassManager PM; // The RuntimeDyld will take ownership of this shortly diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp index a29040b8c..4e552221c 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -297,6 +297,7 @@ static void PrintCallingConv(unsigned cc, raw_ostream &Out) { case CallingConv::CXX_FAST_TLS: Out << "cxx_fast_tlscc"; break; case CallingConv::GHC: Out << "ghccc"; break; case CallingConv::Tail: Out << "tailcc"; break; + case CallingConv::CangjieGC: Out << "cangjiegccc"; break; case CallingConv::CFGuard_Check: Out << "cfguard_checkcc"; break; case CallingConv::X86_StdCall: Out << "x86_stdcallcc"; break; case CallingConv::X86_FastCall: Out << "x86_fastcallcc"; break; diff --git a/llvm/lib/IR/BuiltinGCs.cpp b/llvm/lib/IR/BuiltinGCs.cpp index e9ef034c4..00225982c 100644 --- a/llvm/lib/IR/BuiltinGCs.cpp +++ b/llvm/lib/IR/BuiltinGCs.cpp @@ -112,6 +112,30 @@ public: } }; +/// A GCStrategy which is specific to the default of the Cangjie backend. +/// This GCStrategy is intended to implementation customised collector which +/// can consume the customised stackmap format, uses the default addrespace to +/// distinguish between gc managed and non-gc managed pointers, and has +/// reasonable relocation semantics. +class CangjieGC : public GCStrategy { +public: + CangjieGC() { + UseStatepoints = true; + // These options are all gc.root specific, we specify them so that the + // gc.root lowering code doesn't run. + NeededSafePoints = false; + UsesMetadata = false; + } + ~CangjieGC() = default; + + Optional isGCManagedPointer(const Type *Ty) const override { + // Method is only valid on pointer typed values. + const PointerType *PT = cast(Ty); + // We pick addrspace(1) as our GC managed heap. + return (1 == PT->getAddressSpace()); + } +}; + } // end anonymous namespace // Register all the above so that they can be found at runtime. Note that @@ -125,6 +149,7 @@ static GCRegistry::Add static GCRegistry::Add D("statepoint-example", "an example strategy for statepoint"); static GCRegistry::Add E("coreclr", "CoreCLR-compatible GC"); +static GCRegistry::Add F("cangjie", "Cangjie GC strategy"); // Provide hook to ensure the containing library is fully loaded. void llvm::linkAllBuiltinGCs() {} diff --git a/llvm/lib/IR/CJIntrinsics.cpp b/llvm/lib/IR/CJIntrinsics.cpp new file mode 100644 index 000000000..be12ae6d3 --- /dev/null +++ b/llvm/lib/IR/CJIntrinsics.cpp @@ -0,0 +1,158 @@ +//===- CJIntrinsics.cpp - Cangjie Intrinsic Wrappers ------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file contains utility functions and wrapper class to cangjie intrinsics. +// And define a set of arg-index of the cangjie intrinsic instruction. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/CJIntrinsics.h" + +using namespace llvm; +using namespace cangjie; + +namespace cangjie { +Value *getBaseObj(const CallBase *CI) { + switch (CI->getIntrinsicID()) { + default: + report_fatal_error("Unsupported intrinsic type"); + return nullptr; + case Intrinsic::cj_gcwrite_ref: + return CI->getArgOperand(GCWriteRef::BaseObj); + case Intrinsic::cj_gcwrite_struct: + return CI->getArgOperand(GCWriteStruct::BaseObj); + case Intrinsic::cj_gcread_ref: + return CI->getArgOperand(GCReadRef::BaseObj); + case Intrinsic::cj_gcread_struct: + return CI->getArgOperand(GCReadStruct::BaseObj); + case Intrinsic::cj_gcwrite_generic: + return CI->getArgOperand(GCWriteGeneric::BaseObj); + case Intrinsic::cj_gcread_generic: + return CI->getArgOperand(GCReadGeneric::BaseObj); + } +} + +Value *getValueArg(const CallBase *CI) { + switch (CI->getIntrinsicID()) { + default: + report_fatal_error("Unsupported intrinsic type"); + return nullptr; + case Intrinsic::cj_gcwrite_ref: + return CI->getArgOperand(GCWriteRef::Val); + case Intrinsic::cj_gcwrite_static_ref: + return CI->getArgOperand(GCWriteStaticRef::Val); + } +} + +Value *getPointerArg(const CallBase *CI) { + switch (CI->getIntrinsicID()) { + default: + report_fatal_error("Unsupported intrinsic type"); + return nullptr; + case Intrinsic::cj_gcwrite_ref: + return CI->getArgOperand(GCWriteRef::FieldPtr); + case Intrinsic::cj_gcwrite_static_ref: + return CI->getArgOperand(GCWriteStaticRef::Ptr); + case Intrinsic::cj_gcread_ref: + return CI->getArgOperand(GCReadRef::FieldPtr); + case Intrinsic::cj_gcread_static_ref: + return CI->getArgOperand(GCReadStaticRef::Ptr); + } +} + +Value *getDest(const CallBase *CI) { + switch (CI->getIntrinsicID()) { + default: + report_fatal_error("Unsupported intrinsic type"); + return nullptr; + case Intrinsic::cj_gcwrite_struct: + return CI->getArgOperand(GCWriteStruct::Dst); + case Intrinsic::cj_gcwrite_static_struct: + return CI->getArgOperand(GCWriteStaticStruct::Dst); + case Intrinsic::cj_gcread_struct: + return CI->getArgOperand(GCReadStruct::Dst); + case Intrinsic::cj_gcread_static_struct: + return CI->getArgOperand(GCReadStaticStruct::Dst); + case Intrinsic::cj_gcwrite_generic: + return CI->getArgOperand(GCWriteGeneric::DstPtr); + case Intrinsic::cj_gcread_generic: + return CI->getArgOperand(GCReadGeneric::DstPtr); + case Intrinsic::cj_assign_generic: + return CI->getArgOperand(AssignGeneric::DstPtr); + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + case Intrinsic::cj_array_copy_generic: + return CI->getArgOperand(ArrayCopy::DstPtr); + } +} + +Value *getSource(const CallBase *CI) { + switch (CI->getIntrinsicID()) { + default: + report_fatal_error("Unsupported intrinsic type"); + return nullptr; + case Intrinsic::cj_gcwrite_struct: + return CI->getArgOperand(GCWriteStruct::Src); + case Intrinsic::cj_gcwrite_static_struct: + return CI->getArgOperand(GCWriteStaticStruct::Src); + case Intrinsic::cj_gcread_struct: + return CI->getArgOperand(GCReadStruct::Src); + case Intrinsic::cj_gcread_static_struct: + return CI->getArgOperand(GCReadStaticStruct::Src); + case Intrinsic::cj_gcwrite_generic: + return CI->getArgOperand(GCWriteGeneric::SrcPtr); + case Intrinsic::cj_gcread_generic: + return CI->getArgOperand(GCReadGeneric::SrcPtr); + case Intrinsic::cj_assign_generic: + return CI->getArgOperand(AssignGeneric::SrcPtr); + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + case Intrinsic::cj_array_copy_generic: + return CI->getArgOperand(ArrayCopy::SrcPtr); + } +} + +Value *getSize(const CallBase *CI) { + switch (CI->getIntrinsicID()) { + default: + report_fatal_error("Unsupported intrinsic type"); + return nullptr; + case Intrinsic::cj_gcwrite_struct: + return CI->getArgOperand(GCWriteStruct::Size); + case Intrinsic::cj_gcwrite_static_struct: + return CI->getArgOperand(GCWriteStaticStruct::Size); + case Intrinsic::cj_gcread_struct: + return CI->getArgOperand(GCReadStruct::Size); + case Intrinsic::cj_gcread_static_struct: + return CI->getArgOperand(GCReadStaticStruct::Size); + case Intrinsic::cj_gcwrite_generic: + return CI->getArgOperand(GCWriteGeneric::Size); + case Intrinsic::cj_gcread_generic: + return CI->getArgOperand(GCReadGeneric::Size); + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + case Intrinsic::cj_array_copy_generic: + return CI->getArgOperand(ArrayCopy::Size); + } +} +Value *getAtomicOrder(const CallBase *CI) { + switch (CI->getIntrinsicID()) { + default: + report_fatal_error("Unsupported intrinsic type"); + return nullptr; + case Intrinsic::cj_atomic_load: + return CI->getArgOperand(AtomicLoad::Order); + case Intrinsic::cj_atomic_store: + return CI->getArgOperand(AtomicStore::Order); + case Intrinsic::cj_atomic_swap: + return CI->getArgOperand(AtomicSwap::Order); + } +} +} // namespace cangjie diff --git a/llvm/lib/IR/CJStructTypeGCInfo.cpp b/llvm/lib/IR/CJStructTypeGCInfo.cpp new file mode 100644 index 000000000..c0d3834f2 --- /dev/null +++ b/llvm/lib/IR/CJStructTypeGCInfo.cpp @@ -0,0 +1,214 @@ +//===-- IR/CJStructTypeGCInfo.cpp -- gc info utilities --------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file contains utility functions to generate gc info for llvm types +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/CJStructTypeGCInfo.h" +#include "llvm/ADT/Triple.h" + +using namespace llvm; + +TypeGCInfo &CJStructTypeGCInfo::getOrInsertTypeGCInfo(StructType *ST, + bool IsArrayType) { + auto Itr = Type2GCInfo.find(ST); + if (Itr != Type2GCInfo.end()) { + return Itr->second; + } + + if (IsArrayType) { + return insertArrayTypeGCInfo(ST); + } + return insertNonArrayTypeGCInfo(ST); +} + +TypeGCInfo &CJStructTypeGCInfo::insertArrayTypeGCInfo(StructType *ST) { + // 2: ArrayLaout.xxx = type {%ArrayBase, [0 x type(i8*|prim|struct)]} + assert(ST->getNumElements() == 2 && "ArrayLayout's elemNums should be 2"); + auto *AT = dyn_cast(ST->getElementType(1)); + assert(AT != nullptr && "ArrayLayout[1] should be ArrayType"); + + auto ElemType = AT->getElementType(); + // set size to element's size + Type2GCInfo[ST].ObjSize = + static_cast(DL.getTypeAllocSize(ElemType).getFixedSize()); + auto Itr = Type2GCInfo.find(ST); + Itr->second.ElementType = ElemType; + StructType *ElemST = nullptr; + if (ElemType->isStructTy()) { + ElemST = dyn_cast(ElemType); + } else if (ElemType->isArrayTy()) { + uint64_t NumElements = ElemType->getArrayNumElements(); + std::string ATElementTyName = "record.tmp" + + ST->getName().substr(ST->getName().find('.')).str(); + ElemST = StructType::getTypeByName(M.getContext(), ATElementTyName); + if (ElemST == nullptr) { + std::vector Elements; + for (unsigned It = 0; It < NumElements; ++It) { + Elements.push_back(ElemType->getArrayElementType()); + } + ElemST = StructType::create(M.getContext(), ATElementTyName); + ElemST->setBody(Elements); + } + } + // structArray' BitMap is element struct's BitMap + if (ElemST != nullptr) { + auto &ElemTypeGCInfo = getOrInsertTypeGCInfo(ElemST); + Itr->second.BMInfo = ElemTypeGCInfo.BMInfo; + } else if (isReferenceType(ElemType)) { + Itr->second.BMInfo.HasRef = true; + } + return Itr->second; +} + +void CJStructTypeGCInfo::getOrInsertArrayTypeGCInfo( + ArrayType *AT, BitMapInfo &BMInfo, uint64_t &CurSize) { + auto NumElements = AT->getArrayNumElements(); + auto ET = AT->getArrayElementType(); + unsigned Idx = 0; + while (Idx < NumElements) { + Idx++; + if (ET->isArrayTy()) { + getOrInsertArrayTypeGCInfo(dyn_cast(ET), BMInfo, CurSize); + continue; + } + fillBMInfo(ET, BMInfo, CurSize); + } +} + +void CJStructTypeGCInfo::fillBMInfo(Type *Ty, BitMapInfo &BMInfo, + uint64_t &CurSize) { + if (Ty->isStructTy()) { // it is a value type + auto &SubInfo = getOrInsertTypeGCInfo(dyn_cast(Ty)); + BMInfo.HasRef |= SubInfo.BMInfo.HasRef; + if (SubInfo.BMInfo.HasRef) { + assert(CurSize % PtrSize == 0 && "CurSize must be PtrSize aligned"); + BMInfo.BMStr.replace(CurSize / PtrSize, SubInfo.BMInfo.BMStr.length(), + SubInfo.BMInfo.BMStr); + } + } else if (isReferenceType(Ty)) { + // set 1 for addr pointer + BMInfo.BMStr[CurSize / PtrSize] = '1'; + BMInfo.HasRef = true; + } + CurSize += DL.getTypeAllocSize(Ty).getFixedSize(); +} + +TypeGCInfo &CJStructTypeGCInfo::insertNonArrayTypeGCInfo(StructType *ST) { + // init new TypeGCInfo + Type2GCInfo[ST].ObjSize = + static_cast(DL.getTypeAllocSize(ST).getFixedSize()); + auto Itr = Type2GCInfo.find(ST); + Itr->second.BMInfo.HasRef = false; + // 1 bit represent 1 PtrSize(4 bytes or 8 bytes) + Itr->second.BMInfo.BMStr = + std::string((Itr->second.ObjSize + PtrSize - 1) / PtrSize, '0'); + unsigned Idx = 0; + uint64_t CurSize = 0; + while (Idx < ST->getNumElements()) { + auto *SubType = ST->getElementType(Idx); + auto SubAlign = DL.getABITypeAlignment(SubType); + CurSize = (CurSize + SubAlign - 1) & (~(SubAlign - 1)); + + if (auto AT = dyn_cast(SubType)) { + getOrInsertArrayTypeGCInfo(AT, Itr->second.BMInfo, CurSize); + } else { + fillBMInfo(SubType, Itr->second.BMInfo, CurSize); + } + ++Idx; + } + if (!Itr->second.BMInfo.HasRef) { + Itr->second.BMInfo.BMStr = std::string(); + } + + uint64_t AlignSize = CurSize; + auto STAlign = DL.getABITypeAlignment(ST); + AlignSize = (AlignSize + STAlign - 1) & (~(STAlign - 1)); + assert(CurSize == Itr->second.ObjSize || AlignSize == Itr->second.ObjSize); + return Itr->second; +} + +Constant *CommonBitmap::insertBitMapU64(const std::string &BMStr, + Type *CommonBMType) { + uint64_t Value = 0; + for (unsigned I = 0, J = BMStr.length(); I < J; ++I) { + if (BMStr[I] == '1') { + Value |= (uint64_t)1 << I; + } + } + Value |= (uint64_t)1 << 63; + auto bitmap = ConstantInt::get(Type::getInt64Ty(M.getContext()), Value); + return ConstantExpr::getIntToPtr(bitmap, CommonBMType); +} + +Constant *CommonBitmap::insertBitMapGV(const std::string &BMStr, + Type *CommonBMType, + const std::string &BitMapName) { + unsigned BodySize = BMStr.length() / 8; // 8: to calculate Bytes + // create ST: {int32, int8[]} + SmallVector TypeBody(BodySize + 1, + Type::getInt8Ty(M.getContext())); + TypeBody[0] = Type::getInt32Ty(M.getContext()); + auto *BMType = + llvm::StructType::create(M.getContext(), TypeBody, "BitMapInstance"); + // creat bitMap global value + SmallVector BMBody(BodySize + 1); // 8: preset size of vector + int CurIdx = 0; + BMBody[CurIdx++] = + ConstantInt::get(Type::getInt32Ty(M.getContext()), BodySize); + unsigned Value = 0; + for (unsigned I = 0, J = BMStr.length(); I < J; ++I) { + if (BMStr[I] == '1') { + Value |= 1 << (I % 8); // 8: string to char, 8 bits of every char + } + if ((I + 1) % 8 == 0) { // 8: string to char, 8 bits of every char + BMBody[CurIdx++] = + ConstantInt::get(Type::getInt8Ty(M.getContext()), Value); + Value = 0; + } + } + + auto *BMGlobalVariable = + cast(M.getOrInsertGlobal(BitMapName + ".bitmap", BMType)); + BMGlobalVariable->setInitializer(ConstantStruct::get(BMType, BMBody)); + BMGlobalVariable->setLinkage(GlobalVariable::LinkOnceODRLinkage); + BMGlobalVariable->setVisibility(GlobalVariable::HiddenVisibility); + BMGlobalVariable->addAttribute("CFileGCTib"); + return ConstantExpr::getBitCast(BMGlobalVariable, CommonBMType); +} + +Constant *CommonBitmap::getOrInsertBitMap(const std::string &BMStr, + Type *CommonBMType, + const std::string &BitMapName) { + std::string KeyStr = BMStr; + unsigned KeyStrLen = KeyStr.length(); + // 7: aligned to 8 + unsigned AlignStrLen = ((KeyStrLen + 7) & (~7)) - KeyStrLen; + if (AlignStrLen != 0) { + KeyStr += std::string(AlignStrLen, '0'); + } + + auto Itr = BitMapStr2Constant.find(KeyStr); + // use first generated bitmap const's name for all same bitmap in the module + if (Itr != BitMapStr2Constant.end()) { + return Itr->second; + } + + Constant *BMConst = nullptr; + if (BMStr.length() < 64 && !Triple(M.getTargetTriple()).isARM()) { + BMConst = insertBitMapU64(BMStr, CommonBMType); + } else { + BMConst = insertBitMapGV(KeyStr, CommonBMType, BitMapName); + } + + BitMapStr2Constant[KeyStr] = BMConst; + return BMConst; +} diff --git a/llvm/lib/IR/CMakeLists.txt b/llvm/lib/IR/CMakeLists.txt index 3e542e462..d623f96c5 100644 --- a/llvm/lib/IR/CMakeLists.txt +++ b/llvm/lib/IR/CMakeLists.txt @@ -1,3 +1,4 @@ +include_directories(${CANGJIE_DEMANGLE_DIR}) add_llvm_component_library(LLVMCore AbstractCallSite.cpp AsmWriter.cpp @@ -6,6 +7,7 @@ add_llvm_component_library(LLVMCore AutoUpgrade.cpp BasicBlock.cpp BuiltinGCs.cpp + CJIntrinsics.cpp Comdat.cpp ConstantFold.cpp ConstantRange.cpp @@ -54,6 +56,7 @@ add_llvm_component_library(LLVMCore PseudoProbe.cpp ReplaceConstant.cpp Statepoint.cpp + CJStructTypeGCInfo.cpp StructuralHash.cpp Type.cpp TypeFinder.cpp @@ -66,6 +69,7 @@ add_llvm_component_library(LLVMCore ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/IR + ${CANGJIE_DEMANGLE_DIR} LINK_LIBS ${LLVM_PTHREAD_LIB} @@ -78,3 +82,33 @@ add_llvm_component_library(LLVMCore Remarks Support ) + +if(BUILD_SHARED_LIBS) + target_link_libraries(LLVMCore PRIVATE ${CANGJIE_DEMANGLE_DIR}/libcangjie-demangle.a) +else() + target_link_libraries(LLVMCore PRIVATE -fstack-protector-all) + if(WIN32) + add_custom_command(TARGET LLVMCore + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/tmp + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/tmp + COMMAND ${CMAKE_COMMAND} -E copy ${CANGJIE_DEMANGLE_DIR}/libcangjie-demangle.a + ${CMAKE_BINARY_DIR}/lib/ + COMMAND cd ${CMAKE_BINARY_DIR}/tmp + COMMAND ${CMAKE_AR} x ${CMAKE_BINARY_DIR}/lib/libLLVMCore.a + COMMAND ${CMAKE_AR} x ${CMAKE_BINARY_DIR}/lib/libcangjie-demangle.a + COMMAND ${CMAKE_AR} cr ${CMAKE_BINARY_DIR}/lib/libLLVMCore.a ${CMAKE_BINARY_DIR}/tmp/*${CMAKE_CXX_OUTPUT_EXTENSION} + ) + else() + add_custom_command(TARGET LLVMCore + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/tmp + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/tmp + COMMAND ${CMAKE_COMMAND} -E copy ${CANGJIE_DEMANGLE_DIR}/libcangjie-demangle.a + ${CMAKE_BINARY_DIR}/lib/ + COMMAND cd ${CMAKE_BINARY_DIR}/tmp && ${CMAKE_AR} x ${CMAKE_BINARY_DIR}/lib/libLLVMCore.a + COMMAND cd ${CMAKE_BINARY_DIR}/tmp && ${CMAKE_AR} x ${CMAKE_BINARY_DIR}/lib/libcangjie-demangle.a + COMMAND cd ${CMAKE_BINARY_DIR}/tmp && ${CMAKE_AR} cr ${CMAKE_BINARY_DIR}/lib/libLLVMCore.a *.o + ) + endif() +endif() diff --git a/llvm/lib/IR/DiagnosticInfo.cpp b/llvm/lib/IR/DiagnosticInfo.cpp index 50fe6829a..f1aff2409 100644 --- a/llvm/lib/IR/DiagnosticInfo.cpp +++ b/llvm/lib/IR/DiagnosticInfo.cpp @@ -11,6 +11,7 @@ // Diagnostics reporting is still done as part of the LLVMContext. //===----------------------------------------------------------------------===// +#include "CangjieDemangle.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Twine.h" @@ -65,6 +66,17 @@ void DiagnosticInfoInlineAsm::print(DiagnosticPrinter &DP) const { DP << " at line " << getLocCookie(); } +std::string DiagnosticInfoResourceLimit::GetDemangleName() const { + auto D = Cangjie::Demangle(Fn.getName().str()); + std::string DemangledName = D.GetPkgName() + + std::string(D.GetPkgName().empty() ? "" : "::") + + D.GetFullName(); + if (!DemangledName.empty()) { + return DemangledName; + } + return Fn.getName().str(); +} + void DiagnosticInfoResourceLimit::print(DiagnosticPrinter &DP) const { DP << getResourceName() << " (" << getResourceSize() << ") exceeds limit (" << getResourceLimit() << ") in function '" << getFunction() << '\''; diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp index d41381337..a8b5601e8 100644 --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -85,7 +85,8 @@ static cl::opt NonGlobalValueMaxNameSize( //===----------------------------------------------------------------------===// Argument::Argument(Type *Ty, const Twine &Name, Function *Par, unsigned ArgNo) - : Value(Ty, Value::ArgumentVal), Parent(Par), ArgNo(ArgNo) { + : Value(Ty, Value::ArgumentVal), Parent(Par), ArgNo(ArgNo), + EscapeArgInfo(0) { setName(Name); } @@ -407,6 +408,7 @@ Function::Function(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, // name is a valid intrinsic ID. if (IntID) setAttributes(Intrinsic::getAttributes(getContext(), IntID)); + EscapedInfo = 0; } Function::~Function() { @@ -740,6 +742,14 @@ static const char * const IntrinsicNameTable[] = { #include "llvm/IR/IntrinsicImpl.inc" #undef GET_INTRINSIC_TARGET_DATA +bool Function::isCJIntrinsic() const { + if (!isIntrinsic()) + return false; + + StringRef Name = getName(); + return Name.startswith("llvm.cj."); +} + bool Function::isTargetIntrinsic(Intrinsic::ID IID) { return IID > TargetInfos[0].Count; } @@ -1406,6 +1416,7 @@ bool Intrinsic::isLeaf(ID id) { default: return true; + case Intrinsic::cj_gc_statepoint: case Intrinsic::experimental_gc_statepoint: case Intrinsic::experimental_patchpoint_void: case Intrinsic::experimental_patchpoint_i64: diff --git a/llvm/lib/IR/IRBuilder.cpp b/llvm/lib/IR/IRBuilder.cpp index d0c622fe2..e18cb740c 100644 --- a/llvm/lib/IR/IRBuilder.cpp +++ b/llvm/lib/IR/IRBuilder.cpp @@ -164,6 +164,31 @@ CallInst *IRBuilderBase::CreateMemSet(Value *Ptr, Value *Val, Value *Size, return CI; } +CallInst *IRBuilderBase::CreateCJMemSet(Value *Ptr, Value *Val, Value *Size, + MaybeAlign Align, bool isVolatile, + MDNode *TBAATag, MDNode *ScopeTag, + MDNode *NoAliasTag) { + Ptr = getCastedInt8PtrValue(Ptr); + Value *Ops[] = {Ptr, Val, Size, getInt1(isVolatile)}; + Module *M = BB->getParent()->getParent(); + Function *TheFn = Intrinsic::getDeclaration( + M, Intrinsic::cj_memset, {Ptr->getType()}); + + CallInst *CI = createCallHelper(TheFn, Ops, this); + + // Set the TBAA info if present. + if (TBAATag) + CI->setMetadata(LLVMContext::MD_tbaa, TBAATag); + + if (ScopeTag) + CI->setMetadata(LLVMContext::MD_alias_scope, ScopeTag); + + if (NoAliasTag) + CI->setMetadata(LLVMContext::MD_noalias, NoAliasTag); + + return CI; +} + CallInst *IRBuilderBase::CreateMemSetInline(Value *Dst, MaybeAlign DstAlign, Value *Val, Value *Size, bool IsVolatile, MDNode *TBAATag, @@ -690,6 +715,38 @@ getStatepointArgs(IRBuilderBase &B, uint64_t ID, uint32_t NumPatchBytes, return Args; } +template +static std::vector +getCJStatepointArgs(IRBuilderBase &B, uint64_t ID, uint32_t NumPatchBytes, + Value *ActualCallee, uint32_t Flags, + ArrayRef CallArgs) { + std::vector Args; + Args.push_back(B.getInt64(ID)); + Args.push_back(B.getInt32(NumPatchBytes)); + Args.push_back(ActualCallee); + Args.push_back(B.getInt32(CallArgs.size())); + Args.push_back(B.getInt32(Flags)); + llvm::append_range(Args, CallArgs); + // GC args are now encoded in the gc-live operand bundle + return Args; +} + +static std::vector +getStatepointBundles(ArrayRef GCArgs, ArrayRef StructArgs) { + std::vector Rval; + if (GCArgs.size()) { + SmallVector LiveValues; + llvm::append_range(LiveValues, GCArgs); + Rval.emplace_back("gc-live", LiveValues); + } + if (StructArgs.size()) { + SmallVector StructLiveValues; + llvm::append_range(StructLiveValues, StructArgs); + Rval.emplace_back("struct-live", StructLiveValues); + } + return Rval; +} + template static std::vector getStatepointBundles(Optional> TransitionArgs, @@ -729,9 +786,11 @@ static CallInst *CreateGCStatepointCallCommon( std::vector Args = getStatepointArgs( *Builder, ID, NumPatchBytes, ActualCallee.getCallee(), Flags, CallArgs); - CallInst *CI = Builder->CreateCall( - FnStatepoint, Args, - getStatepointBundles(TransitionArgs, DeoptArgs, GCArgs), Name); + CallInst *CI = + Builder->CreateCall(FnStatepoint, Args, + getStatepointBundles(TransitionArgs, DeoptArgs, + GCArgs), + Name); CI->addParamAttr(2, Attribute::get(Builder->getContext(), Attribute::ElementType, ActualCallee.getFunctionType())); @@ -766,6 +825,22 @@ CallInst *IRBuilderBase::CreateGCStatepointCall( CallArgs, None, DeoptArgs, GCArgs, Name); } +CallInst *IRBuilderBase::CreateCJGCStatepointCall( + uint64_t ID, uint32_t NumPatchBytes, FunctionCallee ActualCallee, + uint32_t Flags, ArrayRef CallArgs, ArrayRef GCArgs, + ArrayRef StructArgs, const Twine &Name) { + Module *M = this->GetInsertBlock()->getParent()->getParent(); + // Fill in the one generic type'd argument (the function is also vararg) + Function *Fn = Intrinsic::getDeclaration(M, Intrinsic::cj_gc_statepoint); + + std::vector Args = getCJStatepointArgs( + *this, ID, NumPatchBytes, ActualCallee.getCallee(), Flags, CallArgs); + + CallInst *CI = + CreateCall(Fn, Args, getStatepointBundles(GCArgs, StructArgs), Name); + return CI; +} + template static InvokeInst *CreateGCStatepointInvokeCommon( IRBuilderBase *Builder, uint64_t ID, uint32_t NumPatchBytes, @@ -825,6 +900,24 @@ InvokeInst *IRBuilderBase::CreateGCStatepointInvoke( Name); } +InvokeInst *IRBuilderBase::CreateCJGCStatepointInvoke( + uint64_t ID, uint32_t NumPatchBytes, FunctionCallee ActualInvokee, + BasicBlock *NormalDest, BasicBlock *UnwindDest, uint32_t Flags, + ArrayRef InvokeArgs, ArrayRef GCArgs, + ArrayRef StructArgs, const Twine &Name) { + Module *M = this->GetInsertBlock()->getParent()->getParent(); + // Fill in the one generic type'd argument (the function is also vararg) + Function *Fn = Intrinsic::getDeclaration(M, Intrinsic::cj_gc_statepoint); + + std::vector Args = getCJStatepointArgs( + *this, ID, NumPatchBytes, ActualInvokee.getCallee(), Flags, InvokeArgs); + + InvokeInst *II = + CreateInvoke(Fn, NormalDest, UnwindDest, Args, + getStatepointBundles(GCArgs, StructArgs), Name); + return II; +} + CallInst *IRBuilderBase::CreateGCResult(Instruction *Statepoint, Type *ResultType, const Twine &Name) { Intrinsic::ID ID = Intrinsic::experimental_gc_result; @@ -836,6 +929,17 @@ CallInst *IRBuilderBase::CreateGCResult(Instruction *Statepoint, return createCallHelper(FnGCResult, Args, this, Name); } +CallInst *IRBuilderBase::CreateCJGCResult(Instruction *Statepoint, + Type *ResultType, const Twine &Name) { + Intrinsic::ID ID = Intrinsic::cj_gc_result; + Module *M = BB->getParent()->getParent(); + Type *Types[] = {ResultType}; + Function *FnGCResult = Intrinsic::getDeclaration(M, ID, Types); + + Value *Args[] = {Statepoint}; + return createCallHelper(FnGCResult, Args, this, Name); +} + CallInst *IRBuilderBase::CreateGCRelocate(Instruction *Statepoint, int BaseOffset, int DerivedOffset, Type *ResultType, const Twine &Name) { @@ -848,6 +952,19 @@ CallInst *IRBuilderBase::CreateGCRelocate(Instruction *Statepoint, return createCallHelper(FnGCRelocate, Args, this, Name); } +CallInst *IRBuilderBase::CreateCJGCRelocate(Instruction *Statepoint, + int BaseOffset, int DerivedOffset, + Type *ResultType, + const Twine &Name) { + Module *M = BB->getParent()->getParent(); + Type *Types[] = {ResultType}; + Function *FnGCRelocate = + Intrinsic::getDeclaration(M, Intrinsic::cj_gc_relocate, Types); + + Value *Args[] = {Statepoint, getInt32(BaseOffset), getInt32(DerivedOffset)}; + return createCallHelper(FnGCRelocate, Args, this, Name); +} + CallInst *IRBuilderBase::CreateGCGetPointerBase(Value *DerivedPtr, const Twine &Name) { Module *M = BB->getParent()->getParent(); diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp index bf76c89f2..35dc5ea38 100644 --- a/llvm/lib/IR/Instruction.cpp +++ b/llvm/lib/IR/Instruction.cpp @@ -683,8 +683,12 @@ bool Instruction::isVolatile() const { } bool Instruction::mayThrow() const { - if (const CallInst *CI = dyn_cast(this)) + if (const CallInst *CI = dyn_cast(this)) { + if (CI->getCalledFunction() && + CI->getCalledFunction()->hasFnAttribute("cj-heapmalloc")) + return false; return !CI->doesNotThrow(); + } if (const auto *CRI = dyn_cast(this)) return CRI->unwindsToCaller(); if (const auto *CatchSwitch = dyn_cast(this)) @@ -711,6 +715,10 @@ bool Instruction::willReturn() const { // return. Remove this workaround once all intrinsics are appropriately // annotated. return CB->hasFnAttr(Attribute::WillReturn) || + CB->getIntrinsicID() == Intrinsic::cj_gcwrite_ref || + CB->getIntrinsicID() == Intrinsic::cj_gcwrite_struct || + CB->getIntrinsicID() == Intrinsic::cj_gcwrite_static_ref || + CB->getIntrinsicID() == Intrinsic::cj_gcwrite_static_struct || (isa(CB) && CB->onlyReadsMemory()); return true; } diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp index f5039eb51..b0bcda45f 100644 --- a/llvm/lib/IR/Instructions.cpp +++ b/llvm/lib/IR/Instructions.cpp @@ -2627,6 +2627,28 @@ Type *ExtractValueInst::getIndexedType(Type *Agg, return const_cast(Agg); } +int ExtractValueInst::getIndexOffset(const DataLayout &DL) { + Type *Agg = getAggregateOperand()->getType(); + int Offset = 0; + for (unsigned Index : Indices) { + if (ArrayType *AT = dyn_cast(Agg)) { + if (Index >= AT->getNumElements()) + return -1; + Offset += DL.getTypeSizeInBits(Agg) * Index; + Agg = AT->getElementType(); + } else if (StructType *ST = dyn_cast(Agg)) { + if (Index >= ST->getNumElements()) + return -1; + Offset += DL.getStructLayout(ST)->getElementOffset(Index); + Agg = ST->getElementType(Index); + } else { + // Not a valid type to index into. + return -1; + } + } + return Offset; +} + //===----------------------------------------------------------------------===// // UnaryOperator Class //===----------------------------------------------------------------------===// diff --git a/llvm/lib/IR/LLVMContext.cpp b/llvm/lib/IR/LLVMContext.cpp index 4a1d5d3dc..6a4860a17 100644 --- a/llvm/lib/IR/LLVMContext.cpp +++ b/llvm/lib/IR/LLVMContext.cpp @@ -87,6 +87,11 @@ LLVMContext::LLVMContext() : pImpl(new LLVMContextImpl(*this)) { "ptrauth operand bundle id drifted!"); (void)PtrauthEntry; + auto *StructLiveEntry = pImpl->getOrInsertBundleTag("struct-live"); + assert(StructLiveEntry->second == LLVMContext::OB_struct_live && + "struct-live operand bundle id drifted!"); + (void)StructLiveEntry; + SyncScope::ID SingleThreadSSID = pImpl->getOrInsertSyncScopeID("singlethread"); assert(SingleThreadSSID == SyncScope::SingleThread && diff --git a/llvm/lib/IR/LegacyPassManager.cpp b/llvm/lib/IR/LegacyPassManager.cpp index ef3465177..4a968848b 100644 --- a/llvm/lib/IR/LegacyPassManager.cpp +++ b/llvm/lib/IR/LegacyPassManager.cpp @@ -13,7 +13,6 @@ #include "llvm/IR/LegacyPassManager.h" #include "llvm/ADT/MapVector.h" #include "llvm/IR/DiagnosticInfo.h" -#include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/LegacyPassManagers.h" #include "llvm/IR/Module.h" @@ -238,74 +237,6 @@ void PassManagerPrettyStackEntry::print(raw_ostream &OS) const { namespace llvm { namespace legacy { bool debugPassSpecified() { return PassDebugging != Disabled; } - -//===----------------------------------------------------------------------===// -// FunctionPassManagerImpl -// -/// FunctionPassManagerImpl manages FPPassManagers -class FunctionPassManagerImpl : public Pass, - public PMDataManager, - public PMTopLevelManager { - virtual void anchor(); -private: - bool wasRun; -public: - static char ID; - explicit FunctionPassManagerImpl() - : Pass(PT_PassManager, ID), PMTopLevelManager(new FPPassManager()), - wasRun(false) {} - - /// \copydoc FunctionPassManager::add() - void add(Pass *P) { - schedulePass(P); - } - - /// createPrinterPass - Get a function printer pass. - Pass *createPrinterPass(raw_ostream &O, - const std::string &Banner) const override { - return createPrintFunctionPass(O, Banner); - } - - // Prepare for running an on the fly pass, freeing memory if needed - // from a previous run. - void releaseMemoryOnTheFly(); - - /// run - Execute all of the passes scheduled for execution. Keep track of - /// whether any of the passes modifies the module, and if so, return true. - bool run(Function &F); - - /// doInitialization - Run all of the initializers for the function passes. - /// - bool doInitialization(Module &M) override; - - /// doFinalization - Run all of the finalizers for the function passes. - /// - bool doFinalization(Module &M) override; - - - PMDataManager *getAsPMDataManager() override { return this; } - Pass *getAsPass() override { return this; } - PassManagerType getTopLevelPassManagerType() override { - return PMT_FunctionPassManager; - } - - /// Pass Manager itself does not invalidate any analysis info. - void getAnalysisUsage(AnalysisUsage &Info) const override { - Info.setPreservesAll(); - } - - FPPassManager *getContainedManager(unsigned N) { - assert(N < PassManagers.size() && "Pass number out of range!"); - FPPassManager *FP = static_cast(PassManagers[N]); - return FP; - } - - void dumpPassStructure(unsigned Offset) override { - for (unsigned I = 0; I < getNumContainedManagers(); ++I) - getContainedManager(I)->dumpPassStructure(Offset); - } -}; - void FunctionPassManagerImpl::anchor() {} char FunctionPassManagerImpl::ID = 0; @@ -341,7 +272,7 @@ bool FunctionPassManagerImpl::doFinalization(Module &M) { } void FunctionPassManagerImpl::releaseMemoryOnTheFly() { - if (!wasRun) + if (!WasRun) return; for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index) { FPPassManager *FPPM = getContainedManager(Index); @@ -349,7 +280,7 @@ void FunctionPassManagerImpl::releaseMemoryOnTheFly() { FPPM->getContainedPass(Index)->releaseMemory(); } } - wasRun = false; + WasRun = false; } // Execute all the passes managed by this top level manager. @@ -366,7 +297,7 @@ bool FunctionPassManagerImpl::run(Function &F) { for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index) getContainedManager(Index)->cleanup(); - wasRun = true; + WasRun = true; return Changed; } } // namespace legacy @@ -1289,6 +1220,38 @@ void PMDataManager::addLowerLevelRequiredPass(Pass *P, Pass *RequiredPass) { llvm_unreachable("Unable to schedule pass"); } +/// Add RequiredPass into list of lower level passes required by pass P. +void PMDataManager::addLowerLevelRequiredPassImpl( + MapVector &OnTheFlyManagers, + Pass *P, Pass *RequiredPass) { + legacy::FunctionPassManagerImpl *FPP = OnTheFlyManagers[P]; + if (!FPP) { + FPP = new legacy::FunctionPassManagerImpl(); + // FPP is the top level manager. + FPP->setTopLevelManager(FPP); + + OnTheFlyManagers[P] = FPP; + } + const PassInfo *RequiredPassPI = + TPM->findAnalysisPassInfo(RequiredPass->getPassID()); + + Pass *FoundPass = nullptr; + if (RequiredPassPI && RequiredPassPI->isAnalysis()) { + FoundPass = + ((PMTopLevelManager *)FPP)->findAnalysisPass(RequiredPass->getPassID()); + } + if (!FoundPass) { + FoundPass = RequiredPass; + // This should be guaranteed to add RequiredPass to the passmanager given + // that we checked for an available analysis above. + FPP->add(RequiredPass); + } + // Register P as the last user of FoundPass or RequiredPass. + SmallVector LU; + LU.push_back(FoundPass); + FPP->setLastUser(LU, P); +} + std::tuple PMDataManager::getOnTheFlyPass(Pass *P, AnalysisID PI, Function &F) { llvm_unreachable("Unable to find on the fly pass"); @@ -1603,32 +1566,7 @@ void MPPassManager::addLowerLevelRequiredPass(Pass *P, Pass *RequiredPass) { RequiredPass->getPotentialPassManagerType()) && "Unable to handle Pass that requires lower level Analysis pass"); - legacy::FunctionPassManagerImpl *FPP = OnTheFlyManagers[P]; - if (!FPP) { - FPP = new legacy::FunctionPassManagerImpl(); - // FPP is the top level manager. - FPP->setTopLevelManager(FPP); - - OnTheFlyManagers[P] = FPP; - } - const PassInfo *RequiredPassPI = - TPM->findAnalysisPassInfo(RequiredPass->getPassID()); - - Pass *FoundPass = nullptr; - if (RequiredPassPI && RequiredPassPI->isAnalysis()) { - FoundPass = - ((PMTopLevelManager*)FPP)->findAnalysisPass(RequiredPass->getPassID()); - } - if (!FoundPass) { - FoundPass = RequiredPass; - // This should be guaranteed to add RequiredPass to the passmanager given - // that we checked for an available analysis above. - FPP->add(RequiredPass); - } - // Register P as the last user of FoundPass or RequiredPass. - SmallVector LU; - LU.push_back(FoundPass); - FPP->setLastUser(LU, P); + addLowerLevelRequiredPassImpl(OnTheFlyManagers, P, RequiredPass); } /// Return function pass corresponding to PassInfo PI, that is diff --git a/llvm/lib/IR/Module.cpp b/llvm/lib/IR/Module.cpp index b51ea45f6..f74c68a4c 100644 --- a/llvm/lib/IR/Module.cpp +++ b/llvm/lib/IR/Module.cpp @@ -169,6 +169,25 @@ FunctionCallee Module::getOrInsertFunction(StringRef Name, FunctionType *Ty) { return getOrInsertFunction(Name, Ty, AttributeList()); } +Function *Module::declareCJRuntimeFunc(StringRef Name, FunctionType *FT, + bool GCLeafFunc, bool GCMalloc) { + Function *Func = getFunction(Name); + if (Func) + return Func; + + Func = cast(getOrInsertFunction(Name, FT).getCallee()); + Func->addFnAttr(Attribute::get(Context, "cj-runtime")); + if (GCMalloc) { + Func->addFnAttr(Attribute::get(Context, "cj-heapmalloc")); + Func->addFnAttr(Attribute::ArgMemOnly); + Func->addRetAttr(Attribute::NoAlias); + } + if (GCLeafFunc) + Func->addFnAttr(Attribute::get(Context, "gc-leaf-function")); + + return Func; +} + // getFunction - Look up the specified function in the module symbol table. // If it does not exist, return null. // diff --git a/llvm/lib/IR/SafepointIRVerifier.cpp b/llvm/lib/IR/SafepointIRVerifier.cpp index 5d3fa28f7..e22cadf61 100644 --- a/llvm/lib/IR/SafepointIRVerifier.cpp +++ b/llvm/lib/IR/SafepointIRVerifier.cpp @@ -35,15 +35,23 @@ #include "llvm/ADT/PostOrderIterator.h" #include "llvm/ADT/SetOperations.h" #include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/Argument.h" #include "llvm/IR/BasicBlock.h" +#include "llvm/IR/CJIntrinsics.h" +#include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" +#include "llvm/IR/GetElementPtrTypeIterator.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/Operator.h" #include "llvm/IR/Statepoint.h" #include "llvm/IR/Value.h" #include "llvm/InitializePasses.h" #include "llvm/Support/Allocator.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" @@ -51,6 +59,7 @@ #define DEBUG_TYPE "safepoint-ir-verifier" using namespace llvm; +using namespace cangjie; /// This option is used for writing test cases. Instead of crashing the program /// when verification fails, report a message to the console (for FileCheck @@ -250,27 +259,6 @@ INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) INITIALIZE_PASS_END(SafepointIRVerifier, "verify-safepoint-ir", "Safepoint IR Verifier", false, false) -static bool isGCPointerType(Type *T) { - if (auto *PT = dyn_cast(T)) - // For the sake of this example GC, we arbitrarily pick addrspace(1) as our - // GC managed heap. We know that a pointer into this heap needs to be - // updated and that no other pointer does. - return (1 == PT->getAddressSpace()); - return false; -} - -static bool containsGCPtrType(Type *Ty) { - if (isGCPointerType(Ty)) - return true; - if (VectorType *VT = dyn_cast(Ty)) - return isGCPointerType(VT->getScalarType()); - if (ArrayType *AT = dyn_cast(Ty)) - return containsGCPtrType(AT->getElementType()); - if (StructType *ST = dyn_cast(Ty)) - return llvm::any_of(ST->elements(), containsGCPtrType); - return false; -} - // Debugging aid -- prints a [Begin, End) range of values. template static void PrintValueSet(raw_ostream &OS, IteratorTy Begin, IteratorTy End) { @@ -909,3 +897,323 @@ static void Verify(const Function &F, const DominatorTree &DT, << "\n"; } } + +bool llvm::isGCPointerType(Type *T) { + if (auto *PT = dyn_cast(T)) + // For the sake of this example GC, we arbitrarily pick addrspace(1) as our + // GC managed heap. We know that a pointer into this heap needs to be + // updated and that no other pointer does. + return (1 == PT->getAddressSpace()); + return false; +} + +bool llvm::isGCPtr(Value *V) { + if (!V->getType()->isPointerTy()) + return false; + + return isGCPointerType(V->stripPointerCasts()->getType()); +} + +bool llvm::containsGCPtrType(Type *Ty) { + if (isGCPointerType(Ty)) + return true; + if (VectorType *VT = dyn_cast(Ty)) + return isGCPointerType(VT->getScalarType()); + if (ArrayType *AT = dyn_cast(Ty)) + return containsGCPtrType(AT->getElementType()); + if (StructType *ST = dyn_cast(Ty)) + return llvm::any_of(ST->elements(), containsGCPtrType); + return false; +} + +bool llvm::isHandledGCPointerType(Type *T) { + // We fully support gc pointers + if (isGCPointerType(T)) + return true; + // We partially support vectors of gc pointers. The code will assert if it + // can't handle something. + if (auto VT = dyn_cast(T)) + if (isGCPointerType(VT->getElementType())) + return true; + // We partially support struct of gc pointers. The code will assert if it + // can't handle something. + if (StructType *ST = dyn_cast(T)) + return llvm::any_of(ST->elements(), containsGCPtrType); + return false; +} + +bool llvm::isMemoryContainsGCPtrType(Type *T) { + if (auto *PT = dyn_cast(T)) { + return !isGCPointerType(PT) && containsGCPtrType(PT->getElementType()); + } + return false; +} + +Value *llvm::findMemoryBasePointer(Value *V) { + if (!isa(V->getType())) + return V; + Value *CV = V; + while (true) { + if (auto *CI = dyn_cast(CV)) { + CV = CI->stripPointerCasts(); + // If we find a cast instruction here, it means we've found a cast which + // is not simply a pointer cast (i.e. an inttoptr). We don't know how to + // handle int->ptr && ptr->int conversion. + if (isa(CV)) { +#ifndef NDEBUG + auto *IntToPtrI = dyn_cast(CV); + assert( + !isGCPointerType(IntToPtrI->getOperand(0)->getType()) && + "IntToPtr is now allowed to be used to convert int to gc-pointer"); + assert( + !isMemoryContainsGCPtrType(IntToPtrI->getOperand(0)->getType()) && + "IntToPtr is now allowed to be used to convert int to memory type " + "which contains gc-pointer"); +#endif + return CV; + } + } else if (auto *GEPI = dyn_cast(CV)) { + CV = GEPI->getPointerOperand(); + } else if (auto *CE = dyn_cast(CV)) { + CV = CE->getOperand(0); + } else { + break; + } + } + + // Assert terminator insts + assert(isa(CV) || isa(CV) || isa(CV) || + isa(CV) || isa(CV) || isa(CV) || + isa(CV) || isa(CV) || isa(CV)); + return CV; +} + +Instruction *llvm::createStoreOrMems(CallBase *CI, IRBuilder<> &Builder) { + auto getAtomicOrdering = [](Value *Order) { + return (AtomicOrdering)(cast(Order)->getZExtValue() + + (unsigned)AtomicOrdering::Monotonic); + }; + LLVMContext &C = CI->getContext(); + Intrinsic::ID IID = CI->getIntrinsicID(); + Instruction *NewInst = nullptr; + switch (IID) { + case Intrinsic::cj_gcwrite_ref: { + if (auto *VecType = dyn_cast(getValueArg(CI)->getType())) { + MaybeAlign Align = CI->getModule()->getDataLayout().getABITypeAlign( + VecType->getElementType()); + NewInst = Builder.CreateAlignedStore(getValueArg(CI), + getPointerArg(CI), Align); + } else { + NewInst = Builder.CreateStore(getValueArg(CI), getPointerArg(CI)); + } + break; + } + case Intrinsic::cj_gcread_ref: + NewInst = Builder.CreateLoad(CI->getType(), getPointerArg(CI)); + NewInst->takeName(CI); + CI->replaceAllUsesWith(NewInst); + break; + case Intrinsic::cj_gcwrite_static_ref: + NewInst = Builder.CreateStore(getValueArg(CI), getPointerArg(CI)); + break; + case Intrinsic::cj_gcread_static_ref: + NewInst = Builder.CreateLoad(CI->getType(), getPointerArg(CI)); + NewInst->takeName(CI); + CI->replaceAllUsesWith(NewInst); + break; + case Intrinsic::cj_gcwrite_struct: + case Intrinsic::cj_gcread_struct: + case Intrinsic::cj_gcwrite_static_struct: + case Intrinsic::cj_gcread_static_struct: + NewInst = Builder.CreateMemCpy(getDest(CI), Align(8), getSource(CI), + Align(8), getSize(CI)); + break; + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + NewInst = + Builder.CreateMemMove(CI->getArgOperand(ArrayCopy::DstPtr), Align(8), + CI->getArgOperand(ArrayCopy::SrcPtr), Align(8), + CI->getArgOperand(ArrayCopy::Size)); + break; + case Intrinsic::cj_atomic_store: { + NewInst = Builder.CreateStore(CI->getArgOperand(AtomicStore::Ref), + CI->getArgOperand(AtomicStore::Field)); + cast(NewInst)->setAtomic(getAtomicOrdering(getAtomicOrder(CI))); + break; + } + case Intrinsic::cj_atomic_swap: { + Value *P = Builder.CreateAddrSpaceCast(CI->getArgOperand(AtomicSwap::Field), + Type::getInt64PtrTy(C)); + Value *V = Builder.CreatePtrToInt(CI->getArgOperand(AtomicSwap::Ref), + Type::getInt64Ty(C)); + AtomicOrdering AO = getAtomicOrdering(getAtomicOrder(CI)); + NewInst = + Builder.CreateAtomicRMW(AtomicRMWInst::Xchg, P, V, MaybeAlign(), AO); + break; + } + case Intrinsic::cj_atomic_compare_swap: { + Value *P = Builder.CreateAddrSpaceCast( + CI->getArgOperand(AtomicCompareSwap::Field), Type::getInt64PtrTy(C)); + Value *Cmp = Builder.CreatePtrToInt( + CI->getArgOperand(AtomicCompareSwap::OldRef), Type::getInt64Ty(C)); + Value *New = Builder.CreatePtrToInt( + CI->getArgOperand(AtomicCompareSwap::NewRef), Type::getInt64Ty(C)); + AtomicOrdering SuccOder = + getAtomicOrdering(CI->getArgOperand(AtomicCompareSwap::SuccOrder)); + AtomicOrdering FailOder = + getAtomicOrdering(CI->getArgOperand(AtomicCompareSwap::FailOrder)); + NewInst = Builder.CreateAtomicCmpXchg(P, Cmp, New, MaybeAlign(), SuccOder, + FailOder); + break; + } + default: + break; + } + return NewInst; +} + +bool llvm::isCJWriteBarrierIntrinsic(Intrinsic::ID IID) { + return IID == Intrinsic::cj_gcwrite_ref || + IID == Intrinsic::cj_gcwrite_struct || + IID == Intrinsic::cj_gcwrite_static_ref || + IID == Intrinsic::cj_gcwrite_static_struct || + IID == Intrinsic::cj_array_copy_ref || + IID == Intrinsic::cj_array_copy_struct; +} + +bool llvm::isCJReadBarrierIntrinsic(Intrinsic::ID IID) { + return IID == Intrinsic::cj_gcread_ref || + IID == Intrinsic::cj_gcread_struct || + IID == Intrinsic::cj_gcread_static_ref || + IID == Intrinsic::cj_gcread_static_struct; +} + +bool llvm::isCJMemcpyIntrinsic(Intrinsic::ID IID) { + return IID == Intrinsic::memcpy || + IID == Intrinsic::memset || + IID == Intrinsic::memmove || + IID == Intrinsic::cj_memset; +} + +bool llvm::isCJAtomicIntrinsic(Intrinsic::ID IID) { + return IID == Intrinsic::cj_atomic_store || + IID == Intrinsic::cj_atomic_load || + IID == Intrinsic::cj_atomic_swap || + IID == Intrinsic::cj_atomic_compare_swap; +} + +bool llvm::isSafepointCall(CallBase *CB) { + if (CB->getCalledFunction() == nullptr) { + return true; + } + Intrinsic::ID IID = CB->getIntrinsicID(); + if (isCJWriteBarrierIntrinsic(IID) || + isCJMemcpyIntrinsic(IID) || + isCJAtomicIntrinsic(IID)) { + return false; + } + return !CB->getCalledFunction()->hasFnAttribute("gc-leaf-function") && + !CB->getCalledFunction()->hasFnAttribute("cj_fast_call"); +} + +// Check if i8 AS(1)* +bool llvm::isInt8AS1Pty(Type *Ty) { + auto *PT = dyn_cast(Ty); + if (!PT) { + return false; + } + // 8: for i8 addrspace(1)* + return (PT->getAddressSpace() == 1) && + (PT->getElementType()->isIntegerTy(8)); +} + +// Check if i8 AS(0)* +bool llvm::isInt8AS0Pty(Value *Ptr) { + auto *PT = dyn_cast(Ptr->getType()); + if (!PT) { + return false; + } + // 8: for i8 addrspace(0)* + return (PT->getAddressSpace() == 0) && + (PT->getElementType()->isIntegerTy(8)); +} + +bool llvm::maybeCJFinalizerObj(Value *V) { + SmallVector Bases; + getUnderlyingObjects(V, Bases, nullptr, 0); + for (auto *Base : Bases) { + if (auto *CB = dyn_cast(Base)) { + auto *F = CB->getCalledFunction(); + if (F && F->getName() == "CJ_MCC_NewFinalizerStub") + return true; + } + } + return false; +} + +Type *llvm::getUniqueActualType(Value *V) { + Type *CommonTy = nullptr; + for (Value *U : V->users()) { + if (auto *BCI = dyn_cast(U)) { + auto *Ty = BCI->getDestTy(); + if (CommonTy == nullptr) + CommonTy = Ty->getNonOpaquePointerElementType(); + else if (CommonTy != Ty) + return nullptr; + } + } + return CommonTy; +} + +// Find all possible struct types up to. +void llvm::getAllStructTypes(Value *V, SmallSet &STs) { + SmallVector WorkList = {V}; + SmallSet Visited; + while (!WorkList.empty()) { + auto *Ptr = WorkList.pop_back_val(); + if (!Visited.insert(Ptr).second) + continue; + if (isa(Ptr->getType()->getNonOpaquePointerElementType())) + STs.insert( + cast(Ptr->getType()->getNonOpaquePointerElementType())); + if (auto *BI = dyn_cast(Ptr)) + WorkList.push_back(BI->getOperand(0)); + else if (auto *ASCI = dyn_cast(Ptr)) + WorkList.push_back(ASCI->getPointerOperand()); + else if (auto *GEP = dyn_cast(Ptr)) { + for (auto GTI = ++gep_type_begin(GEP), GTE = gep_type_end(GEP); + GTI != GTE; ++GTI) + STs.insert(GTI.getStructTypeOrNull()); + WorkList.push_back(GEP->getPointerOperand()); + } else if (auto *PN = dyn_cast(Ptr)) { + for (Value *Val : PN->incoming_values()) + WorkList.push_back(Val); + } else if (auto *SI = dyn_cast(Ptr)) { + for (auto Case : SI->cases()) + WorkList.push_back(Case.getCaseValue()); + } + // Call, Argument, Alloca, ... -> continue + } +} + +// Find last struct type which contain V +StructType *llvm::getLastStructTypeContain(Value *V) { + auto *Ptr = V->stripPointerCasts(); + if (auto *AI = dyn_cast(Ptr)) + return dyn_cast(AI->getAllocatedType()); + if (auto *Arg = dyn_cast(Ptr)) + if (auto *Ty = getUniqueActualType(Arg)) + return dyn_cast(Ty); + if (auto *GEP = dyn_cast(Ptr)) { + auto *Ty = GEP->getSourceElementType(); + // Maybe getlementptr i8, i8* %ptr, i64 24 + if (!isa(Ty)) + return getLastStructTypeContain( + GEP->getPointerOperand()->stripPointerCasts()); + for (auto GTI = ++gep_type_begin(GEP), GTE = gep_type_end(GEP); GTI != GTE; + ++GTI) + Ty = GTI.getStructTypeOrNull(); + return dyn_cast_or_null(Ty); + } + return nullptr; +} diff --git a/llvm/lib/IR/Statepoint.cpp b/llvm/lib/IR/Statepoint.cpp index 508e3cb71..cddccaced 100644 --- a/llvm/lib/IR/Statepoint.cpp +++ b/llvm/lib/IR/Statepoint.cpp @@ -38,3 +38,20 @@ llvm::parseStatepointDirectivesFromAttrs(AttributeList AS) { return Result; } + +uint64_t llvm::getCJStatepointID(CallBase *Call) { + Function *Callee = Call->getCalledFunction(); + if (!Callee) + return CJStatepointID::Default; + + if (Callee->isCangjieSafePoint()) + return CJStatepointID::Safepoint; + + if (Callee->isCangjieStackCheck()) + return CJStatepointID::StackCheck; + + if (Call->getMetadata("fast_new_array")) + return CJStatepointID::NewArrayFast; + + return CJStatepointID::Default; +} diff --git a/llvm/lib/IR/Type.cpp b/llvm/lib/IR/Type.cpp index 85b658c8a..acc25f63f 100644 --- a/llvm/lib/IR/Type.cpp +++ b/llvm/lib/IR/Type.cpp @@ -459,6 +459,19 @@ void StructType::setBody(ArrayRef Elements, bool isPacked) { ContainedTys = Elements.copy(getContext().pImpl->Alloc).data(); } +void StructType::setBodyForCJ(ArrayRef Elements) { + setSubclassData(getSubclassData() | SCDB_HasBody); + + NumContainedTys = Elements.size(); + + if (Elements.empty()) { + ContainedTys = nullptr; + return; + } + + ContainedTys = Elements.copy(getContext().pImpl->Alloc).data(); +} + void StructType::setName(StringRef Name) { if (Name == getName()) return; diff --git a/llvm/lib/IR/Value.cpp b/llvm/lib/IR/Value.cpp index 3990536f3..673d2d6a1 100644 --- a/llvm/lib/IR/Value.cpp +++ b/llvm/lib/IR/Value.cpp @@ -830,7 +830,8 @@ bool Value::canBeFreed() const { // usual trick of requesting declaration of the intrinsic from the module // doesn't work. for (auto &Fn : *F->getParent()) - if (Fn.getIntrinsicID() == Intrinsic::experimental_gc_statepoint) + if (Fn.getIntrinsicID() == Intrinsic::experimental_gc_statepoint || + Fn.getIntrinsicID() == Intrinsic::cj_gc_statepoint) return true; return false; } diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index e3ea256af..0d48707cc 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -2246,9 +2246,12 @@ void Verifier::verifyInlineAsmCall(const CallBase &Call) { /// Verify that statepoint intrinsic is well formed. void Verifier::verifyStatepoint(const CallBase &Call) { - assert(Call.getCalledFunction() && - Call.getCalledFunction()->getIntrinsicID() == - Intrinsic::experimental_gc_statepoint); + Function *Callee = Call.getCalledFunction(); + assert(Callee && "statepoint call is null!"); + unsigned ID = Callee->getIntrinsicID(); + Check(ID == Intrinsic::experimental_gc_statepoint || + ID == Intrinsic::cj_gc_statepoint, + "Incorrect statepoint call!", Call); Check(!Call.doesNotAccessMemory() && !Call.onlyReadsMemory() && !Call.onlyAccessesArgMemory(), @@ -2264,10 +2267,20 @@ void Verifier::verifyStatepoint(const CallBase &Call) { "positive", Call); - Type *TargetElemType = Call.getParamElementType(2); - Check(TargetElemType, - "gc.statepoint callee argument must have elementtype attribute", Call); - FunctionType *TargetFuncType = dyn_cast(TargetElemType); + FunctionType *TargetFuncType = nullptr; + Function *CalledFunction = dyn_cast_or_null(Call.getArgOperand(2)); + if (CalledFunction) { + TargetFuncType = CalledFunction->getFunctionType(); + } else { + Type *TargetElemType = Call.getParamElementType(2); + if (TargetElemType == nullptr) { + TargetElemType = + Call.getArgOperand(2)->getType()->getNonOpaquePointerElementType(); + } + Check(TargetElemType, + "gc.statepoint callee argument have not elementtype attribute", Call); + TargetFuncType = dyn_cast(TargetElemType); + } Check(TargetFuncType, "gc.statepoint callee elementtype must be function type", Call); @@ -2280,12 +2293,6 @@ void Verifier::verifyStatepoint(const CallBase &Call) { if (TargetFuncType->isVarArg()) { Check(NumCallArgs >= NumParams, "gc.statepoint mismatch in number of vararg call args", Call); - - // TODO: Remove this limitation - Check(TargetFuncType->getReturnType()->isVoidTy(), - "gc.statepoint doesn't support wrapping non-void " - "vararg functions yet", - Call); } else Check(NumCallArgs == NumParams, "gc.statepoint mismatch in number of call args", Call); @@ -2297,7 +2304,6 @@ void Verifier::verifyStatepoint(const CallBase &Call) { // Verify that the types of the call parameter arguments match // the type of the wrapped callee. - AttributeList Attrs = Call.getAttributes(); for (int i = 0; i < NumParams; i++) { Type *ParamType = TargetFuncType->getParamType(i); Type *ArgType = Call.getArgOperand(5 + i)->getType(); @@ -2305,39 +2311,36 @@ void Verifier::verifyStatepoint(const CallBase &Call) { "gc.statepoint call argument does not match wrapped " "function type", Call); - - if (TargetFuncType->isVarArg()) { - AttributeSet ArgAttrs = Attrs.getParamAttrs(5 + i); - Check(!ArgAttrs.hasAttribute(Attribute::StructRet), - "Attribute 'sret' cannot be used for vararg call arguments!", Call); - } } - const int EndCallArgsInx = 4 + NumCallArgs; + // Cangjie statepoint does not have deopt and transitions. + if (ID != Intrinsic::cj_gc_statepoint) { + const int EndCallArgsInx = 4 + NumCallArgs; - const Value *NumTransitionArgsV = Call.getArgOperand(EndCallArgsInx + 1); - Check(isa(NumTransitionArgsV), - "gc.statepoint number of transition arguments " - "must be constant integer", - Call); - const int NumTransitionArgs = - cast(NumTransitionArgsV)->getZExtValue(); - Check(NumTransitionArgs == 0, - "gc.statepoint w/inline transition bundle is deprecated", Call); - const int EndTransitionArgsInx = EndCallArgsInx + 1 + NumTransitionArgs; - - const Value *NumDeoptArgsV = Call.getArgOperand(EndTransitionArgsInx + 1); - Check(isa(NumDeoptArgsV), - "gc.statepoint number of deoptimization arguments " - "must be constant integer", - Call); - const int NumDeoptArgs = cast(NumDeoptArgsV)->getZExtValue(); - Check(NumDeoptArgs == 0, - "gc.statepoint w/inline deopt operands is deprecated", Call); + const Value *NumTransitionArgsV = Call.getArgOperand(EndCallArgsInx + 1); + Check(isa(NumTransitionArgsV), + "gc.statepoint number of transition arguments " + "must be constant integer", + Call); + const int NumTransitionArgs = + cast(NumTransitionArgsV)->getZExtValue(); + Check(NumTransitionArgs == 0, + "gc.statepoint w/inline transition bundle is deprecated", Call); + const int EndTransitionArgsInx = EndCallArgsInx + 1 + NumTransitionArgs; + + const Value *NumDeoptArgsV = Call.getArgOperand(EndTransitionArgsInx + 1); + Check(isa(NumDeoptArgsV), + "gc.statepoint number of deoptimization arguments " + "must be constant integer", + Call); + const int NumDeoptArgs = cast(NumDeoptArgsV)->getZExtValue(); + Check(NumDeoptArgs == 0, + "gc.statepoint w/inline deopt operands is deprecated", Call); - const int ExpectedNumArgs = 7 + NumCallArgs; - Check(ExpectedNumArgs == (int)Call.arg_size(), - "gc.statepoint too many arguments", Call); + const int ExpectedNumArgs = 7 + NumCallArgs; + Check(ExpectedNumArgs == (int)Call.arg_size(), + "gc.statepoint too many arguments", Call); + } // Check that the only uses of this gc.statepoint are gc.result or // gc.relocate calls which are tied to this statepoint and thus part @@ -3309,8 +3312,10 @@ void Verifier::visitCallBase(CallBase &Call) { // Statepoint intrinsic is vararg but the wrapped function may be not. // Allow sret here and check the wrapped function in verifyStatepoint. if (!Call.getCalledFunction() || - Call.getCalledFunction()->getIntrinsicID() != - Intrinsic::experimental_gc_statepoint) + (Call.getCalledFunction()->getIntrinsicID() != + Intrinsic::experimental_gc_statepoint && + Call.getCalledFunction()->getIntrinsicID() != + Intrinsic::cj_gc_statepoint)) Check(!ArgAttrs.hasAttribute(Attribute::StructRet), "Attribute 'sret' cannot be used for vararg call arguments!", Call); @@ -3349,7 +3354,7 @@ void Verifier::visitCallBase(CallBase &Call) { bool FoundDeoptBundle = false, FoundFuncletBundle = false, FoundGCTransitionBundle = false, FoundCFGuardTargetBundle = false, FoundPreallocatedBundle = false, FoundGCLiveBundle = false, - FoundPtrauthBundle = false, + FoundPtrauthBundle = false, FoundStructLiveBundle = false, FoundAttachedCallBundle = false; for (unsigned i = 0, e = Call.getNumOperandBundles(); i < e; ++i) { OperandBundleUse BU = Call.getOperandBundleAt(i); @@ -3400,6 +3405,9 @@ void Verifier::visitCallBase(CallBase &Call) { } else if (Tag == LLVMContext::OB_gc_live) { Check(!FoundGCLiveBundle, "Multiple gc-live operand bundles", Call); FoundGCLiveBundle = true; + } else if (Tag == LLVMContext::OB_struct_live) { + Check(!FoundStructLiveBundle, "Multiple struct-live operand bundles", Call); + FoundStructLiveBundle = true; } else if (Tag == LLVMContext::OB_clang_arc_attachedcall) { Check(!FoundAttachedCallBundle, "Multiple \"clang.arc.attachedcall\" operand bundles", Call); @@ -4669,7 +4677,7 @@ void Verifier::visitInstruction(Instruction &I) { (CBI && &CBI->getCalledOperandUse() == &I.getOperandUse(i)) || IsAttachedCallOperand(F, CBI, i)), "Cannot take the address of an intrinsic!", &I); - Check(!F->isIntrinsic() || isa(I) || + Check(!F->isIntrinsic() || isa(I) || F->isCJIntrinsic() || F->getIntrinsicID() == Intrinsic::donothing || F->getIntrinsicID() == Intrinsic::seh_try_begin || F->getIntrinsicID() == Intrinsic::seh_try_end || @@ -5115,6 +5123,25 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) { Check(Call.getParent()->getParent()->hasGC(), "Enclosing function does not use GC.", Call); break; + case Intrinsic::cj_gcwrite_ref: + case Intrinsic::cj_gcwrite_struct: + case Intrinsic::cj_gcwrite_static_ref: + case Intrinsic::cj_gcwrite_static_struct: + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + case Intrinsic::cj_atomic_store: + case Intrinsic::cj_atomic_load: + case Intrinsic::cj_atomic_swap: + case Intrinsic::cj_atomic_compare_swap: + Check(Call.getParent()->getParent()->hasCangjieGC(), + "Enclosing function check GC error.", Call); + for (Value *Arg : Call.args()) { + if (Arg->getType()->isPointerTy()) { + Check(!isa(Arg), + "cangjie intrinsic contains the undef operand!", Call); + } + } + break; case Intrinsic::init_trampoline: Check(isa(Call.getArgOperand(1)->stripPointerCasts()), "llvm.init_trampoline parameter #2 must resolve to a function.", @@ -5159,7 +5186,7 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) { std::max(uint64_t(Entry.second), IdxArg->getLimitedValue(~0U) + 1)); break; } - + case Intrinsic::cj_gc_statepoint: case Intrinsic::experimental_gc_statepoint: if (auto *CI = dyn_cast(&Call)) Check(!CI->isInlineAsm(), @@ -5169,6 +5196,7 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) { verifyStatepoint(Call); break; + case Intrinsic::cj_gc_result: case Intrinsic::experimental_gc_result: { Check(Call.getParent()->getParent()->hasGC(), "Enclosing function does not use GC.", Call); @@ -5176,19 +5204,37 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) { const auto *StatepointCall = dyn_cast(Call.getArgOperand(0)); const Function *StatepointFn = StatepointCall ? StatepointCall->getCalledFunction() : nullptr; - Check(StatepointFn && StatepointFn->isDeclaration() && - StatepointFn->getIntrinsicID() == - Intrinsic::experimental_gc_statepoint, + Check(StatepointFn && StatepointFn->isDeclaration(), + "gc.result operand #1 must be from a statepoint", Call, + Call.getArgOperand(0)); + unsigned CalledID = StatepointFn->getIntrinsicID(); + Check((CalledID == Intrinsic::experimental_gc_statepoint || + CalledID == Intrinsic::cj_gc_statepoint), "gc.result operand #1 must be from a statepoint", Call, Call.getArgOperand(0)); // Check that result type matches wrapped callee. - auto *TargetFuncType = - cast(StatepointCall->getParamElementType(2)); + FunctionType *TargetFuncType = nullptr; + Function *CalledFunction = + dyn_cast_or_null(StatepointCall->getArgOperand(2)); + if (CalledFunction) { + TargetFuncType = CalledFunction->getFunctionType(); + } else { + Type *TargetElemType = StatepointCall->getParamElementType(2); + if (TargetElemType == nullptr) { + TargetElemType = StatepointCall->getArgOperand(2) + ->getType() + ->getNonOpaquePointerElementType(); + } + Check(TargetElemType, + "gc.statepoint callee argument have not elementtype attribute", Call); + TargetFuncType = dyn_cast(TargetElemType); + } Check(Call.getType() == TargetFuncType->getReturnType(), "gc.result result type does not match wrapped callee", Call); break; } + case Intrinsic::cj_gc_relocate: case Intrinsic::experimental_gc_relocate: { Check(Call.arg_size() == 3, "wrong number of arguments", Call); diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp index cc7be24c1..8b8e2b289 100644 --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -69,6 +69,14 @@ static cl::opt DumpThinCGSCCs("dump-thin-cg-sccs", cl::init(false), cl::Hidden, cl::desc("Dump the SCCs in the ThinLTO index's callgraph")); +static cl::opt + IsCJFullLTO("cangjie-full-lto", cl::init(false), cl::Hidden, + cl::desc("Cangjie run FullLTO.")); + +namespace llvm { +extern cl::opt CJPipeline; +} + /// Enable global value internalization in LTO. cl::opt EnableLTOInternalization( "enable-lto-internalization", cl::init(true), cl::Hidden, @@ -650,11 +658,16 @@ Error LTO::addModule(InputFile &Input, unsigned ModI, BitcodeModule BM = Input.Mods[ModI]; auto ModSyms = Input.module_symbols(ModI); + // For cangjie, use IsCJFullLTO to set whether to run FullLTO. + // When IsCJFullLTO is not specified, run ThinLTO by default for cangjie. + // For others, use default set of LLVM. + bool IsThinLTO = + (CJPipeline && !IsCJFullLTO) || (!CJPipeline && LTOInfo->IsThinLTO); addModuleToGlobalRes(ModSyms, {ResI, ResE}, - LTOInfo->IsThinLTO ? ThinLTO.ModuleMap.size() + 1 : 0, + IsThinLTO ? ThinLTO.ModuleMap.size() + 1 : 0, LTOInfo->HasSummary); - if (LTOInfo->IsThinLTO) + if (IsThinLTO) return addThinLTO(BM, ModSyms, ResI, ResE); RegularLTO.EmptyCombinedModule = false; @@ -997,6 +1010,43 @@ Error LTO::checkPartiallySplit() { return Error::success(); } +void LTO::checkCJBC() { + bool IsCJBC = true; + // scan modules for ThinLTO + StringRef sourceFileName; + for (auto &Mod : ThinLTO.ModuleMap) { + LTOLLVMContext BackendContext(Conf); + Expected> MOrErr = + Mod.second.parseModule(BackendContext); + if (!MOrErr) + continue; + Module &M = **MOrErr; + Metadata *MD = M.getModuleFlag("CJBC"); + if (MD == nullptr) { + sourceFileName = M.getSourceFileName(); + IsCJBC = false; + break; + } + } + + // scan module for FullLTO + for (auto &Mod : RegularLTO.ModsWithSummaries) { + Module &M = *Mod.M; + Metadata *MD = M.getModuleFlag("CJBC"); + if (MD == nullptr) { + sourceFileName = M.getSourceFileName(); + IsCJBC = false; + break; + } + } + + if (!IsCJBC) { + report_fatal_error(Twine( + "Only allow module from cangjie bc files for cangjie LTO!") + + sourceFileName); + } +} + Error LTO::run(AddStreamFn AddStream, FileCache Cache) { // Compute "dead" symbols, we don't want to import/export these! DenseSet GUIDPreservedSymbols; @@ -1035,6 +1085,10 @@ Error LTO::run(AddStreamFn AddStream, FileCache Cache) { return StatsFileOrErr.takeError(); std::unique_ptr StatsFile = std::move(StatsFileOrErr.get()); + if (CJPipeline) { + checkCJBC(); + } + Error Result = runRegularLTO(AddStream); if (!Result) Result = runThinLTO(AddStream, Cache, GUIDPreservedSymbols); diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp index 2e32469b4..95327fab0 100644 --- a/llvm/lib/LTO/LTOBackend.cpp +++ b/llvm/lib/LTO/LTOBackend.cpp @@ -20,6 +20,7 @@ #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Bitcode/BitcodeWriter.h" +#include "llvm/CodeGen/CommandFlags.h" #include "llvm/IR/LLVMRemarkStreamer.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/PassManager.h" @@ -41,7 +42,14 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/IPO/WholeProgramDevirt.h" +#include "llvm/Transforms/Scalar/CJBarrierOpt.h" +#include "llvm/Transforms/Scalar/CJBarrierSplit.h" +#include "llvm/Transforms/Scalar/CJRewriteStatepoint.h" +#include "llvm/Transforms/Scalar/CJRuntimeLowering.h" +#include "llvm/Transforms/Scalar/CJSimpleOpt.h" +#include "llvm/Transforms/Scalar/CJSpecificOpt.h" #include "llvm/Transforms/Scalar/LoopPassManager.h" +#include "llvm/Transforms/Scalar/PlaceSafepoints.h" #include "llvm/Transforms/Utils/FunctionImportUtils.h" #include "llvm/Transforms/Utils/SplitModule.h" @@ -72,8 +80,26 @@ static cl::opt ThinLTOAssumeMerged( cl::desc("Assume the input has already undergone ThinLTO function " "importing and the other pre-optimization pipeline changes.")); +extern cl::opt MaxRecursionInl; +extern cl::opt CountedLoopTripWidth; + namespace llvm { +enum class CJPGOKind { NoPGO, InstrGen, InstrUse }; +cl::opt CJPGOKindFlag( + "cj-pgo-kind", cl::init(CJPGOKind::NoPGO), cl::Hidden, + cl::desc("The kind of cangjie profile guided optimization"), + cl::values(clEnumValN(CJPGOKind::NoPGO, "nopgo", "Do not use PGO."), + clEnumValN(CJPGOKind::InstrGen, "pgo-instr-gen-pipeline", + "Instrument the IR to generate profile."), + clEnumValN(CJPGOKind::InstrUse, "pgo-instr-use-pipeline", + "Use instrumented profile to guide PGO."))); +cl::opt CJProfileFile("cj-profile-file", + cl::desc("Path to the profile."), + cl::Hidden); extern cl::opt NoPGOWarnMismatch; +extern cl::opt CJPipeline; +extern cl::opt EnableCJBarrierSplit; +extern cl::opt RunPartialInlining; } [[noreturn]] static void reportOpenError(StringRef Path, Twine Msg) { @@ -232,6 +258,18 @@ static void runNewPMPasses(const Config &Conf, Module &Mod, TargetMachine *TM, ModuleSummaryIndex *ExportSummary, const ModuleSummaryIndex *ImportSummary) { Optional PGOOpt; + switch (CJPGOKindFlag) { + case CJPGOKind::InstrGen: + PGOOpt = PGOOptions(CJProfileFile, "", "", PGOOptions::IRInstr); + break; + case CJPGOKind::InstrUse: + PGOOpt = + PGOOptions(CJProfileFile, "", Conf.ProfileRemapping, PGOOptions::IRUse); + break; + case CJPGOKind::NoPGO: + break; + } + if (!Conf.SampleProfile.empty()) PGOOpt = PGOOptions(Conf.SampleProfile, "", Conf.ProfileRemapping, PGOOptions::SampleUse, PGOOptions::NoCSAction, true); @@ -291,6 +329,41 @@ static void runNewPMPasses(const Config &Conf, Module &Mod, TargetMachine *TM, if (!Conf.DisableVerify) MPM.addPass(VerifierPass()); + if (CJPipeline) { + // Customize parameters for cangjie-pipeline. + if (OptLevel > 1 && !MaxRecursionInl.getPosition()) { + MaxRecursionInl = 1; + } + // We put 64-bit safepoint optimization to O3 level for safety. + if (OptLevel > 2) { + CountedLoopTripWidth = 64; + } else { + CountedLoopTripWidth = 16; + } + + // Only enable partial inlining for PGO. + if (PGOOpt) + RunPartialInlining = true; + + MPM.addPass(CJRuntimeLowering()); + if (EnableCJBarrierSplit) + MPM.addPass(CJBarrierSplit()); + if (OptLevel > 1) { + MPM.addPass(createModuleToFunctionPassAdaptor(CJSimpleOpt())); + } + } + + auto addCangjiePasses = [&]() { + if (CJPipeline) { + MPM.addPass(CJSpecificOpt(OptLevel)); + MPM.addPass(PlaceSafepoints()); + if (OptLevel > 1) { + MPM.addPass(CJBarrierOpt()); + } + MPM.addPass(CJRewriteStatepoint(OptLevel)); + } + }; + OptimizationLevel OL; switch (OptLevel) { @@ -324,6 +397,8 @@ static void runNewPMPasses(const Config &Conf, Module &Mod, TargetMachine *TM, MPM.addPass(PB.buildLTODefaultPipeline(OL, ExportSummary)); } + addCangjiePasses(); + if (!Conf.DisableVerify) MPM.addPass(VerifierPass()); @@ -360,6 +435,11 @@ bool lto::opt(const Config &Conf, TargetMachine *TM, unsigned Task, Module &Mod, static void codegen(const Config &Conf, TargetMachine *TM, AddStreamFn AddStream, unsigned Task, Module &Mod, const ModuleSummaryIndex &CombinedIndex) { + if (CJPipeline) { + // Before codegen, add attributes to functions in Module based on + // commandline parameters like llc. + codegen::setFunctionAttributes("", "", Mod); + } if (Conf.PreCodeGenModuleHook && !Conf.PreCodeGenModuleHook(Task, Mod)) return; @@ -450,7 +530,7 @@ static void splitCodeGen(const Config &C, TargetMachine *TM, std::unique_ptr TM = createTargetMachine(C, T, *MPartInCtx); - codegen(C, TM.get(), AddStream, ThreadId, *MPartInCtx, + ::codegen(C, TM.get(), AddStream, ThreadId, *MPartInCtx, CombinedIndex); }, // Pass BC using std::move to ensure that it get moved rather than @@ -507,7 +587,7 @@ Error lto::backend(const Config &C, AddStreamFn AddStream, } if (ParallelCodeGenParallelismLevel == 1) { - codegen(C, TM.get(), AddStream, 0, Mod, CombinedIndex); + ::codegen(C, TM.get(), AddStream, 0, Mod, CombinedIndex); } else { splitCodeGen(C, TM.get(), AddStream, ParallelCodeGenParallelismLevel, Mod, CombinedIndex); @@ -564,7 +644,7 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream, updatePublicTypeTestCalls(Mod, CombinedIndex.withWholeProgramVisibility()); if (Conf.CodeGenOnly) { - codegen(Conf, TM.get(), AddStream, Task, Mod, CombinedIndex); + ::codegen(Conf, TM.get(), AddStream, Task, Mod, CombinedIndex); return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile)); } @@ -579,7 +659,7 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream, CmdArgs)) return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile)); - codegen(Conf, TM, AddStream, Task, Mod, CombinedIndex); + ::codegen(Conf, TM, AddStream, Task, Mod, CombinedIndex); return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile)); }; diff --git a/llvm/lib/Linker/IRMover.cpp b/llvm/lib/Linker/IRMover.cpp index e31faf642..35fa8cc16 100644 --- a/llvm/lib/Linker/IRMover.cpp +++ b/llvm/lib/Linker/IRMover.cpp @@ -30,6 +30,18 @@ #include using namespace llvm; +namespace llvm { +extern cl::opt CJPipeline; +} + +static StringRef getTypeNamePrefix(StringRef Name) { + size_t DotPos = Name.rfind('.'); + return (DotPos == 0 || DotPos == StringRef::npos || Name.back() == '.' || + !isdigit(static_cast(Name[DotPos + 1]))) + ? Name + : Name.substr(0, DotPos); +} + //===----------------------------------------------------------------------===// // TypeMap implementation. //===----------------------------------------------------------------------===// @@ -39,6 +51,12 @@ class TypeMapTy : public ValueMapTypeRemapper { /// This is a mapping from a source type to a destination type to use. DenseMap MappedTypes; + /// This is a mapping from a source type to a destination type to use. + TypeToTypeMapTy MappedStructType; + + /// Buffer to hold StringRefs. + BumpPtrAllocator Alloc; + /// When checking to see if two subgraphs are isomorphic, we speculatively /// add types to MappedTypes, but keep track of them here in case we need to /// roll back. @@ -78,6 +96,45 @@ public: return cast(get((Type *)T)); } + void addMappedStructType(StringRef OldStr, StringRef NewStr) { + StringRef OldStrc = OldStr.copy(Alloc); + StringRef NewStrc = NewStr.copy(Alloc); + MappedStructType[OldStrc] = NewStrc; + } + + void mapMetadata(LLVMContext &C, Value *V) override { + SmallVector, 4> MDs; + if (auto *Global = dyn_cast(V)) { + Global->getAllMetadata(MDs); + } else if (auto *I = dyn_cast(V)) { + I->getAllMetadata(MDs); + } else { + llvm_unreachable("Unsupported types"); + } + + for (auto &MD : MDs) { + for (unsigned It = 0; It < MD.second->getNumOperands(); It++) { + auto *MDS = dyn_cast(MD.second->getOperand(It)); + if (MDS == nullptr) { + continue; + } + StringRef NewType = getMappedStructType(MDS->getString()); + if (NewType.empty()) { + continue; + } + MD.second->replaceOperandWith(It, MDString::get(C, NewType)); + } + } + } + + StringRef getMappedStructType(StringRef OldStr) { + auto StrMap = MappedStructType.find(OldStr); + if (StrMap != MappedStructType.end()) { + return StrMap->second; + } + return ""; + } + private: Type *remapType(Type *SrcTy) override { return get(SrcTy); } @@ -110,8 +167,18 @@ void TypeMapTy::addTypeMapping(Type *DstTy, Type *SrcTy) { // module, which are in fact the same. for (Type *Ty : SpeculativeTypes) if (auto *STy = dyn_cast(Ty)) - if (STy->hasName()) - STy->setName(""); + if (STy->hasName()) { + addMappedStructType(STy->getStructName(), remapType(STy)->getStructName()); + // For many modules, maybe have the same type. + // For example, %ArrayLayout.Char.91 is the same as %ArrayLayout.Char. + // For %ArrayLayout.Char.91, do not need to set name, + // only save %ArrayLayout.Char. + if (!CJPipeline || + (CJPipeline && getTypeNamePrefix(STy->getName()).size() != + STy->getName().size())) { + STy->setName(""); + } + } } SpeculativeTypes.clear(); SpeculativeDstOpaqueTypes.clear(); @@ -329,8 +396,17 @@ Type *TypeMapTy::get(Type *Ty, SmallPtrSet &Visited) { if (StructType *OldT = DstStructTypesSet.findNonOpaque(ElementTypes, IsPacked)) { - STy->setName(""); - return *Entry = OldT; + addMappedStructType(STy->getName(), OldT->getName()); + // For many modules, maybe have the same type. + // For example, %ArrayLayout.UInt8.90 is the same as %ArrayLayout.UInt8. + // For %ArrayLayout.UInt8.90, do not need to set name, + // only save %ArrayLayout.UInt8. + if (!CJPipeline || + (CJPipeline && + getTypeNamePrefix(STy->getName()).size() != STy->getName().size())) { + STy->setName(""); + return *Entry = OldT; + } } if (!AnyChange) { @@ -545,6 +621,8 @@ public: } ~IRLinker() { SharedMDs = std::move(*ValueMap.getMDMap()); } + // update global information + void mapGlobal(GlobalVariable &Global); Error run(); Value *materialize(Value *V, bool ForIndirectSymbol); }; @@ -759,14 +837,6 @@ GlobalValue *IRLinker::copyGlobalValueProto(const GlobalValue *SGV, return NewGV; } -static StringRef getTypeNamePrefix(StringRef Name) { - size_t DotPos = Name.rfind('.'); - return (DotPos == 0 || DotPos == StringRef::npos || Name.back() == '.' || - !isdigit(static_cast(Name[DotPos + 1]))) - ? Name - : Name.substr(0, DotPos); -} - /// Loop over all of the linked values to compute type mappings. For example, /// if we link "extern Foo *x" and "Foo *x = NULL", then we have two struct /// types 'Foo' but one got renamed when the module was loaded into the same @@ -1517,6 +1587,54 @@ static std::string adjustInlineAsm(const std::string &InlineAsm, return InlineAsm; } +static StringRef getOldTypeName(StringRef Name, bool &IsArray) { + size_t DotPos = Name.rfind('.'); + size_t StarPos = Name.rfind('*'); + if (StarPos == StringRef::npos) { + return Name.substr(0, DotPos); + } + if (StarPos >= DotPos) { + report_fatal_error("* is in the type of array," + "and the type must be the front of structKlass!"); + } + IsArray = true; + return Name.substr(StarPos + 1, DotPos - StarPos - 1); +} + +// For example, Global is 2*10*oldrecord.structKlass +// OldName: oldrecord +// MappedStructType: newrecord +// SuffixName: .structKlass +// NewName: 2*10*newrecord.structKlass +void IRLinker::mapGlobal(GlobalVariable &Global) { + bool IsArray = false; + StringRef GVName = Global.getName(); + StringRef OldName = getOldTypeName(GVName, IsArray); + StringRef MappedStructType; + if (!TypeMap.getMappedStructType(OldName).empty()) { + MappedStructType = TypeMap.getMappedStructType(OldName); + } + if (MappedStructType.empty()) { + return; + } + size_t DotPos = GVName.rfind('.'); + StringRef SuffixName = GVName.substr(DotPos); + std::string NewName; + if (IsArray) { + NewName = (GVName.substr(0, GVName.rfind("*") + 1) + MappedStructType + + SuffixName) + .str(); + } else { + NewName = (MappedStructType + SuffixName).str(); + } + GlobalVariable *NewGlobal = DstM.getGlobalVariable(NewName); + if (NewGlobal != nullptr) { + Global.replaceAllUsesWith(NewGlobal); + } else { + Global.setName(NewName); + } +} + Error IRLinker::run() { // Ensure metadata materialized before value mapping. if (SrcM->getMaterializer()) @@ -1640,6 +1758,18 @@ Error IRLinker::run() { } } + for (auto &Global : Globals) { + TypeMap.mapMetadata(DstM.getContext(), &Global); + if (!Global.hasInitializer()) { + continue; + } + StringRef GlobalName = Global.getName(); + if (GlobalName.endswith(".structKlass") || + GlobalName.endswith(".uniqueAddr")) { + mapGlobal(Global); + } + } + // Merge the module flags into the DstM module. return linkModuleFlagsMetadata(); } diff --git a/llvm/lib/MC/MCMachOStreamer.cpp b/llvm/lib/MC/MCMachOStreamer.cpp index f358f593f..9c85d47af 100644 --- a/llvm/lib/MC/MCMachOStreamer.cpp +++ b/llvm/lib/MC/MCMachOStreamer.cpp @@ -172,7 +172,8 @@ void MCMachOStreamer::changeSection(MCSection *Section, StringRef SegName = MSec.getSegmentName(); if (SegName == "__DWARF") CreatedADWARFSection = true; - else if (Created && DWARFMustBeAtTheEnd && !canGoAfterDWARF(MSec)) + else if (Created && DWARFMustBeAtTheEnd && !canGoAfterDWARF(MSec) && + !SegName.equals("__CJ_METADATA")) assert((!CreatedADWARFSection || Section == getContext().getObjectFileInfo()->getStackMapSection()) && "Creating regular section after DWARF"); diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp index d6fe952c0..f18cda8c3 100644 --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -23,6 +23,7 @@ #include "llvm/MC/MCSectionSPIRV.h" #include "llvm/MC/MCSectionWasm.h" #include "llvm/MC/MCSectionXCOFF.h" +#include "llvm/MC/SectionKind.h" #include "llvm/Support/Casting.h" using namespace llvm; @@ -327,6 +328,51 @@ void MCObjectFileInfo::initMachOMCObjectFileInfo(const Triple &T) { #include "llvm/BinaryFormat/Swift.def" } + // Cangjie File(CFile) specific sections + // RW section + CJTypeTemplateSection = Ctx->getMachOSection("__CJ_METADATA", "__cjtemplate", + 0, SectionKind::getReadOnly()); + CJStaticGenericTISection = Ctx->getMachOSection( + "__CJ_METADATA", "__cjstatic_gi", 0, SectionKind::getReadOnly()); + CJTypeInfoSection = Ctx->getMachOSection("__CJ_METADATA", "__cjtypeinfo", 0, + SectionKind::getReadOnly()); + CJTypeFieldsSection = Ctx->getMachOSection("__CJ_METADATA", "__cj_fields", 0, + SectionKind::getReadOnly()); + + CJMtableSection = Ctx->getMachOSection("__CJ_METADATA", "__cjmtable", 0, + SectionKind::getReadOnly()); + CJInnerTypeExtensionsSection = Ctx->getMachOSection( + "__CJ_METADATA", "__cjinnerty_eds", 0, SectionKind::getReadOnly()); + CJOuterTypeExtensionsSection = Ctx->getMachOSection( + "__CJ_METADATA", "__cjouterty_eds", 0, SectionKind::getReadOnly()); + + CJReflectPkgInfoSection = Ctx->getMachOSection( + "__CJ_METADATA", "__cjref_pkginfo", 0, SectionKind::getReadOnly()); + CJReflectGVSection = Ctx->getMachOSection("__CJ_METADATA", "__cjref_gv", 0, + SectionKind::getReadOnly()); + CJReflectGenericTISection = Ctx->getMachOSection( + "__CJ_METADATA", "__cjref_gi", 0, SectionKind::getReadOnly()); + + CJMethodInfoSection = Ctx->getMachOSection("__CJ_METADATA", "__cjmethodinfo", + 0, SectionKind::getReadOnly()); + CJStackMapSection = Ctx->getMachOSection("__CJ_METADATA", "__cjstackmap", 0, + SectionKind::getReadOnly()); + CJGCTibSection = Ctx->getMachOSection("__CJ_METADATA", "__cjgctib", 0, + SectionKind::getReadOnly()); + CJGCRootSection = Ctx->getMachOSection("__CJ_METADATA", "__cjgcroots", 0, + SectionKind::getReadOnly()); + CJGCFlagsSection = Ctx->getMachOSection("__CJ_METADATA", "__cjgcflags", 0, + SectionKind::getReadOnly()); + + CJSDKVersionSection = Ctx->getMachOSection("__CJ_METADATA", "__cjsdkversion", + 0, SectionKind::getReadOnly()); + CJStringPoolDictSection = Ctx->getMachOSection( + "__CJ_METADATA", "__cjstrpooldict", 0, SectionKind::getReadOnly()); + CJStringPoolSection = Ctx->getMachOSection("__CJ_METADATA", "__cjstrpool", 0, + SectionKind::getReadOnly()); + CJGlobalInitFuncSection = Ctx->getMachOSection( + "__CJ_METADATA", "__cjglobalFunc", 0, SectionKind::getReadOnly()); + TLSExtraDataSection = TLSTLVSection; } @@ -531,6 +577,71 @@ void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T, bool Large) { PseudoProbeSection = Ctx->getELFSection(".pseudo_probe", DebugSecType, 0); PseudoProbeDescSection = Ctx->getELFSection(".pseudo_probe_desc", DebugSecType, 0); + + // Cangjie File(CFile) specific sections + // TypeInfo information + CJTypeTemplateSection = + Ctx->getELFSection(".cjmetadata.typetemplate", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + CJStaticGenericTISection = + Ctx->getELFSection(".cjmetadata.static.generic.ti", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + CJTypeInfoSection = + Ctx->getELFSection(".cjmetadata.typeinfo", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + CJTypeFieldsSection = + Ctx->getELFSection(".cjmetadata.type.fields", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + + // VTable/ITable + CJMtableSection = Ctx->getELFSection(".cjmetadata.mtable", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + CJInnerTypeExtensionsSection = + Ctx->getELFSection(".cjmetadata.innerty.eds", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + CJOuterTypeExtensionsSection = + Ctx->getELFSection(".cjmetadata.outerty.eds", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + + // Reflect + CJReflectPkgInfoSection = + Ctx->getELFSection(".cjmetadata.reflect.pkginfo", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + CJReflectGVSection = + Ctx->getELFSection(".cjmetadata.reflect.gv", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + CJReflectGenericTISection = + Ctx->getELFSection(".cjmetadata.reflect.generic.ti", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + + // GC + CJMethodInfoSection = + Ctx->getELFSection(".cjmetadata.methodinfo", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + CJStackMapSection = + Ctx->getELFSection(".cjmetadata.stackmap", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + CJGCTibSection = Ctx->getELFSection(".cjmetadata.gctib", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + CJGCRootSection = Ctx->getELFSection(".cjmetadata.gcroots", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + CJGCFlagsSection = + Ctx->getELFSection(".cjmetadata.gcflags", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + + // Others + CJSDKVersionSection = + Ctx->getELFSection(".cjmetadata.sdk.version", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + CJStringPoolDictSection = + Ctx->getELFSection(".cjmetadata.stringpooldict", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + CJStringPoolSection = + Ctx->getELFSection(".cjmetadata.stringpool", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); + CJGlobalInitFuncSection = + Ctx->getELFSection(".cjmetadata.globalInitFunc", ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_WRITE); } void MCObjectFileInfo::initGOFFMCObjectFileInfo(const Triple &T) { @@ -788,7 +899,7 @@ void MCObjectFileInfo::initCOFFMCObjectFileInfo(const Triple &T) { SectionKind::getData()); XDataSection = Ctx->getCOFFSection( - ".xdata", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ, + ".xdata", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE, SectionKind::getData()); SXDataSection = Ctx->getCOFFSection(".sxdata", COFF::IMAGE_SCN_LNK_INFO, @@ -819,6 +930,112 @@ void MCObjectFileInfo::initCOFFMCObjectFileInfo(const Triple &T) { COFF::IMAGE_SCN_MEM_WRITE, SectionKind::getData()); + // Cangjie File(CFile) specific sections + // TypeInfo information + CJTypeTemplateSection = Ctx->getCOFFSection( + ".cjtt", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getData()); + CJStaticGenericTISection = Ctx->getCOFFSection( + ".cjsgt", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getData()); + CJTypeInfoSection = Ctx->getCOFFSection(".cjti", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | + COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getCJTypeInfo()); + CJTypeFieldsSection = Ctx->getCOFFSection( + ".cjfield", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getData()); + + // VTable/ITable + CJMtableSection = Ctx->getCOFFSection(".cjmtbl", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | + COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getCJMTable()); + CJInnerTypeExtensionsSection = Ctx->getCOFFSection( + ".cjinty", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getData()); + CJOuterTypeExtensionsSection = Ctx->getCOFFSection( + ".cjouty", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getData()); + + // Reflect + CJReflectPkgInfoSection = Ctx->getCOFFSection( + ".cjrflp", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getData()); + CJReflectGVSection = Ctx->getCOFFSection( + ".cjrflv", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getData()); + CJReflectGenericTISection = Ctx->getCOFFSection( + ".cjrflg", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getData()); + + // GC + CJMethodInfoSection = Ctx->getCOFFSection( + ".cjmthd", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getData()); + CJStackMapSection = Ctx->getCOFFSection(".cjsm", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | + COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getData()); + CJGCTibSection = Ctx->getCOFFSection(".cjgctib", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | + COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getData()); + CJGCRootSection = Ctx->getCOFFSection(".cjgcrts", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | + COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getData()); + CJGCFlagsSection = Ctx->getCOFFSection(".cjgcflg", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | + COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getData()); + + // Others + CJSDKVersionSection = Ctx->getCOFFSection( + ".cjsdkv", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getData()); + CJStringPoolDictSection = Ctx->getCOFFSection( + ".cjspdct", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getData()); + CJStringPoolSection = Ctx->getCOFFSection( + ".cjsp", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getData()); + CJGlobalInitFuncSection = Ctx->getCOFFSection( + ".cjinitF", + COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | + COFF::IMAGE_SCN_MEM_WRITE, + SectionKind::getData()); + StackMapSection = Ctx->getCOFFSection(".llvm_stackmaps", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ, diff --git a/llvm/lib/Passes/CMakeLists.txt b/llvm/lib/Passes/CMakeLists.txt index 703969f8b..c2992aa79 100644 --- a/llvm/lib/Passes/CMakeLists.txt +++ b/llvm/lib/Passes/CMakeLists.txt @@ -16,6 +16,8 @@ add_llvm_component_library(LLVMPasses LINK_COMPONENTS AggressiveInstCombine Analysis + CJCFI + CjcObf Core Coroutines IPO diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 42fde3752..a1fa77096 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -26,6 +26,7 @@ #include "llvm/Analysis/CFLAndersAliasAnalysis.h" #include "llvm/Analysis/CFLSteensAliasAnalysis.h" #include "llvm/Analysis/CGSCCPassManager.h" +#include "llvm/Analysis/CJAliasAnalysis.h" #include "llvm/Analysis/CallGraph.h" #include "llvm/Analysis/CallPrinter.h" #include "llvm/Analysis/CostModel.h" @@ -58,6 +59,7 @@ #include "llvm/Analysis/ModuleDebugInfoPrinter.h" #include "llvm/Analysis/ModuleSummaryAnalysis.h" #include "llvm/Analysis/MustExecute.h" +#include "llvm/Analysis/ObfConfigAnalysis.h" #include "llvm/Analysis/ObjCARCAliasAnalysis.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/PhiValues.h" @@ -85,6 +87,7 @@ #include "llvm/Support/Regex.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/AggressiveInstCombine/AggressiveInstCombine.h" +#include "llvm/Transforms/CJCFI/PtrAuthBackwardCFI.h" #include "llvm/Transforms/Coroutines/CoroCleanup.h" #include "llvm/Transforms/Coroutines/CoroEarly.h" #include "llvm/Transforms/Coroutines/CoroElide.h" @@ -95,6 +98,7 @@ #include "llvm/Transforms/IPO/Attributor.h" #include "llvm/Transforms/IPO/BlockExtractor.h" #include "llvm/Transforms/IPO/CalledValuePropagation.h" +#include "llvm/Transforms/IPO/CJPartialEscapeAnalysis.h" #include "llvm/Transforms/IPO/ConstantMerge.h" #include "llvm/Transforms/IPO/CrossDSOCFI.h" #include "llvm/Transforms/IPO/DeadArgumentElimination.h" @@ -140,12 +144,27 @@ #include "llvm/Transforms/Instrumentation/PoisonChecking.h" #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" +#include "llvm/Transforms/Obfuscator/ControlFlowObfuscator.h" +#include "llvm/Transforms/Obfuscator/DataObfuscator.h" +#include "llvm/Transforms/Obfuscator/LayoutObfuscator.h" #include "llvm/Transforms/ObjCARC.h" #include "llvm/Transforms/Scalar/ADCE.h" #include "llvm/Transforms/Scalar/AlignmentFromAssumptions.h" #include "llvm/Transforms/Scalar/AnnotationRemarks.h" #include "llvm/Transforms/Scalar/BDCE.h" +#include "llvm/Transforms/Scalar/CJBarrierOpt.h" +#include "llvm/Transforms/Scalar/CJBarrierSplit.h" +#include "llvm/Transforms/Scalar/CJDevirtualOpt.h" +#include "llvm/Transforms/Scalar/CJSimpleOpt.h" +#include "llvm/Transforms/Scalar/CJGenericIntrinsicOpt.h" +#include "llvm/Transforms/Scalar/CJLoopFloatOpt.h" +#include "llvm/Transforms/Scalar/CJRSSCE.h" +#include "llvm/Transforms/Scalar/CJRuntimeLowering.h" +#include "llvm/Transforms/Scalar/CJRewriteStatepoint.h" +#include "llvm/Transforms/Scalar/CJSimpleRangeAnalysis.h" #include "llvm/Transforms/Scalar/CallSiteSplitting.h" +#include "llvm/Transforms/Scalar/CJIRVerifier.h" +#include "llvm/Transforms/Scalar/CJSpecificOpt.h" #include "llvm/Transforms/Scalar/ConstantHoisting.h" #include "llvm/Transforms/Scalar/ConstraintElimination.h" #include "llvm/Transforms/Scalar/CorrelatedValuePropagation.h" @@ -154,6 +173,7 @@ #include "llvm/Transforms/Scalar/DeadStoreElimination.h" #include "llvm/Transforms/Scalar/DivRemPairs.h" #include "llvm/Transforms/Scalar/EarlyCSE.h" +#include "llvm/Transforms/Scalar/CJFillMetadata.h" #include "llvm/Transforms/Scalar/FlattenCFG.h" #include "llvm/Transforms/Scalar/Float2Int.h" #include "llvm/Transforms/Scalar/GVN.h" @@ -162,6 +182,7 @@ #include "llvm/Transforms/Scalar/IndVarSimplify.h" #include "llvm/Transforms/Scalar/InductiveRangeCheckElimination.h" #include "llvm/Transforms/Scalar/InferAddressSpaces.h" +#include "llvm/Transforms/Scalar/InsertCJTBAA.h" #include "llvm/Transforms/Scalar/InstSimplifyPass.h" #include "llvm/Transforms/Scalar/JumpThreading.h" #include "llvm/Transforms/Scalar/LICM.h" @@ -199,6 +220,7 @@ #include "llvm/Transforms/Scalar/NaryReassociate.h" #include "llvm/Transforms/Scalar/NewGVN.h" #include "llvm/Transforms/Scalar/PartiallyInlineLibCalls.h" +#include "llvm/Transforms/Scalar/PlaceSafepoints.h" #include "llvm/Transforms/Scalar/Reassociate.h" #include "llvm/Transforms/Scalar/Reg2Mem.h" #include "llvm/Transforms/Scalar/RewriteStatepointsForGC.h" @@ -251,14 +273,65 @@ using namespace llvm; +extern cl::opt MaxRecursionInl; +extern cl::opt CountedLoopTripWidth; + static const Regex DefaultAliasRegex( "^(default|thinlto-pre-link|thinlto|lto-pre-link|lto)<(O[0123sz])>$"); +cl::opt + CangjieLTOPreOpt("cangjie-lto", cl::init(false), cl::Hidden, + cl::desc("cangjie lto pre-optimization phase, and some " + "optimizations are not enabled at this time..")); +cl::opt EnableConfigFlatten("control-flow-flatten", + cl::desc("Flatten control flow"), + cl::NotHidden, cl::init(false)); +cl::opt EnableConfigBcf("control-flow-bogus", + cl::desc("insert bogus control flow"), + cl::NotHidden, cl::init(false)); +cl::opt EnableConfigObfString("obf-string", cl::desc("Obfuscate string"), + cl::NotHidden, cl::init(false)); +cl::opt EnableConfigObfConst("obf-const", + cl::desc("Obfuscate const variable"), + cl::NotHidden, cl::init(false)); +cl::opt EnableObfLayout("obf-layout", cl::desc("Obfuscate layout"), + cl::NotHidden, cl::init(false)); +cl::opt ObfInputSymMapFiles("obf-input-symbol-mapping-files", + cl::desc("Input symbol mapping files"), + cl::NotHidden, cl::init("")); +cl::opt ObfOutputSymMapFile("obf-output-symbol-mapping-file", + cl::desc("Output symbol mapping file"), + cl::NotHidden, cl::init("")); +cl::opt + ObfUserSymMapFile("obf-apply-symbol-mapping", + cl::desc("User-defined symbol mapping file"), + cl::NotHidden, cl::init("")); +cl::opt ObfSymPrefix("obf-sym-prefix", + cl::desc("Prefix of Obfuscated symbols"), + cl::NotHidden, cl::init("")); +cl::opt EnableObfExportSymol("obf-export-symbol", + cl::desc("Obfuscate exported symbols"), + cl::NotHidden, cl::init(true)); +cl::opt EnableObfLineNumber("obf-line-number", + cl::desc("Obfuscate exported symbols"), + cl::NotHidden, cl::init(true)); +cl::opt EnableObfSourcePath("obf-source-path", + cl::desc("Obfuscate exported symbols"), + cl::NotHidden, cl::init(true)); +cl::opt + EnableCJPtrAuthBackwardCFI("cj-ptrauth-backward-cfi", + cl::desc("Cangjie PtrAuth-based backward CFI"), + cl::NotHidden, cl::init(false)); + namespace llvm { cl::opt PrintPipelinePasses( "print-pipeline-passes", cl::desc("Print a '-passes' compatible string describing the pipeline " "(best-effort only).")); +extern cl::opt CJPipeline; +extern cl::opt EnableCJBarrierSplit; +extern cl::opt RunPartialInlining; +extern cl::opt IVCallInstrEnable; } // namespace llvm namespace { @@ -1136,10 +1209,61 @@ Error PassBuilder::parseModulePass(ModulePassManager &MPM, .Case("O3", OptimizationLevel::O3) .Case("Os", OptimizationLevel::Os) .Case("Oz", OptimizationLevel::Oz); + + if (CJPipeline) { + // Customize parameters for cangjie-pipeline. + if (L.getSpeedupLevel() > 1 && !MaxRecursionInl.getPosition()) { + MaxRecursionInl = 1; + } + // We put 64-bit safepoint optimization to O3 level for safety. + if (L.getSpeedupLevel() > 2) { + CountedLoopTripWidth = 64; + } else { + CountedLoopTripWidth = 16; + } + if (PGOOpt) { + // Enable partial inlining for PGO. + RunPartialInlining = true; + } + // Verify that ir generated by the front end of the cangjie is correct. + MPM.addPass(CJIRVerifier()); + if (L.getSpeedupLevel() > 1) + MPM.addPass(createModuleToFunctionPassAdaptor(InsertCJTBAA())); + // Fill Klass at the beginning since related structs may be optimized + // later. + MPM.addPass(CJFillMetadata()); + MPM.addPass(CJRuntimeLowering()); + if (EnableCJBarrierSplit) + MPM.addPass(CJBarrierSplit()); + + if (L.getSpeedupLevel() > 1) + MPM.addPass(createModuleToFunctionPassAdaptor(CJSimpleOpt())); + + if (EnableCJPtrAuthBackwardCFI) + MPM.addPass(PtrAuthBackwardCFI()); + } + auto addCangjiePasses = [&]() { + if (CJPipeline && !CangjieLTOPreOpt) { + MPM.addPass(CJSpecificOpt(L.getSpeedupLevel())); + MPM.addPass(PlaceSafepoints()); + if (L.getSpeedupLevel() > 1) { + MPM.addPass(CJBarrierOpt()); + } + MPM.addPass(CJRewriteStatepoint(L.getSpeedupLevel())); + if (EnableConfigFlatten || EnableConfigBcf) + MPM.addPass(ControlFlowObfuscator()); + if (EnableConfigObfString || EnableConfigObfConst) + MPM.addPass(DataObfuscator()); + if (EnableObfLayout) + MPM.addPass(LayoutObfuscator()); + } + }; + if (L == OptimizationLevel::O0 && Matches[1] != "thinlto" && Matches[1] != "lto") { MPM.addPass(buildO0DefaultPipeline(L, Matches[1] == "thinlto-pre-link" || Matches[1] == "lto-pre-link")); + addCangjiePasses(); return Error::success(); } @@ -1152,7 +1276,7 @@ Error PassBuilder::parseModulePass(ModulePassManager &MPM, L.getSpeedupLevel() > 1 && L != OptimizationLevel::Oz; if (Matches[1] == "default") { - MPM.addPass(buildPerModuleDefaultPipeline(L)); + MPM.addPass(buildPerModuleDefaultPipeline(L, CangjieLTOPreOpt)); } else if (Matches[1] == "thinlto-pre-link") { MPM.addPass(buildThinLTOPreLinkDefaultPipeline(L)); } else if (Matches[1] == "thinlto") { @@ -1163,6 +1287,7 @@ Error PassBuilder::parseModulePass(ModulePassManager &MPM, assert(Matches[1] == "lto" && "Not one of the matched options!"); MPM.addPass(buildLTODefaultPipeline(L, nullptr)); } + addCangjiePasses(); return Error::success(); } diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp index 945ef5123..943a2b6d0 100644 --- a/llvm/lib/Passes/PassBuilderPipelines.cpp +++ b/llvm/lib/Passes/PassBuilderPipelines.cpp @@ -17,6 +17,7 @@ #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/BasicAliasAnalysis.h" #include "llvm/Analysis/CGSCCPassManager.h" +#include "llvm/Analysis/CJAliasAnalysis.h" #include "llvm/Analysis/GlobalsModRef.h" #include "llvm/Analysis/InlineAdvisor.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" @@ -31,6 +32,7 @@ #include "llvm/Support/PGOOptions.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/AggressiveInstCombine/AggressiveInstCombine.h" +#include "llvm/Transforms/CJCFI/PtrAuthBackwardCFI.h" #include "llvm/Transforms/Coroutines/CoroCleanup.h" #include "llvm/Transforms/Coroutines/CoroConditionalWrapper.h" #include "llvm/Transforms/Coroutines/CoroEarly.h" @@ -41,6 +43,7 @@ #include "llvm/Transforms/IPO/ArgumentPromotion.h" #include "llvm/Transforms/IPO/Attributor.h" #include "llvm/Transforms/IPO/CalledValuePropagation.h" +#include "llvm/Transforms/IPO/CJPartialEscapeAnalysis.h" #include "llvm/Transforms/IPO/ConstantMerge.h" #include "llvm/Transforms/IPO/CrossDSOCFI.h" #include "llvm/Transforms/IPO/DeadArgumentElimination.h" @@ -75,6 +78,13 @@ #include "llvm/Transforms/Scalar/AlignmentFromAssumptions.h" #include "llvm/Transforms/Scalar/AnnotationRemarks.h" #include "llvm/Transforms/Scalar/BDCE.h" +#include "llvm/Transforms/Scalar/CJBarrierSplit.h" +#include "llvm/Transforms/Scalar/CJDevirtualOpt.h" +#include "llvm/Transforms/Scalar/CJSimpleOpt.h" +#include "llvm/Transforms/Scalar/CJGenericIntrinsicOpt.h" +#include "llvm/Transforms/Scalar/CJLoopFloatOpt.h" +#include "llvm/Transforms/Scalar/CJRSSCE.h" +#include "llvm/Transforms/Scalar/CJSimpleRangeAnalysis.h" #include "llvm/Transforms/Scalar/CallSiteSplitting.h" #include "llvm/Transforms/Scalar/ConstraintElimination.h" #include "llvm/Transforms/Scalar/CorrelatedValuePropagation.h" @@ -85,6 +95,7 @@ #include "llvm/Transforms/Scalar/Float2Int.h" #include "llvm/Transforms/Scalar/GVN.h" #include "llvm/Transforms/Scalar/IndVarSimplify.h" +#include "llvm/Transforms/Scalar/InductiveRangeCheckElimination.h" #include "llvm/Transforms/Scalar/InstSimplifyPass.h" #include "llvm/Transforms/Scalar/JumpThreading.h" #include "llvm/Transforms/Scalar/LICM.h" @@ -182,6 +193,15 @@ static cl::opt EnableMergeFunctions( "enable-merge-functions", cl::init(false), cl::Hidden, cl::desc("Enable function merging as part of the optimization pipeline")); +static cl::opt EnableCJDevirtual("enable-cj-devirtual", cl::init(true), + cl::Hidden, + cl::desc("Enable CJ Devirtual")); + +extern cl::opt MaxRecursionInl; +extern cl::opt CountedLoopTripWidth; +extern cl::opt CangjieLTOPreOpt; +extern cl::opt EnableCJPtrAuthBackwardCFI; + PipelineTuningOptions::PipelineTuningOptions() { LoopInterleaving = true; LoopVectorization = true; @@ -223,6 +243,13 @@ extern cl::opt EnableMatrix; extern cl::opt DisablePreInliner; extern cl::opt PreInlineThreshold; + +extern cl::opt CJPipeline; +extern cl::opt CJLTOOpt; +extern cl::opt CJDisableEscapeAnalysis; +extern cl::opt EnableCJBarrierSplit; +extern cl::opt EnableCJIRCEPass; +extern cl::opt EnableCJGenericIntrinsicOpt; } // namespace llvm void PassBuilder::invokePeepholeEPCallbacks(FunctionPassManager &FPM, @@ -242,6 +269,49 @@ static bool isLTOPreLink(ThinOrFullLTOPhase Phase) { Phase == ThinOrFullLTOPhase::FullLTOPreLink; } +static void setSLPVectorization(PipelineTuningOptions &PTO) { + if (CJPipeline) { + PTO.SLPVectorization = true; + } +} + +static void addEliminationOptPasses(FunctionPassManager &MainFPM, + PipelineTuningOptions &PTO) { + if (CJPipeline && EnableCJIRCEPass) { + // This pass should before IndVarSimplifyPass LFTR optimization as the + // latter might change predicate to eq or ne, which IRCE would not + // recognize. Also after LICM, because the SSA that needs to be compared + // should be a loop invariant. + MainFPM.addPass(IRCEPass()); + // The new loop created by IRCE has potential unswitch conditions. If we do + // not perform this pass, the subsequent LICM may not work effectively on + // the new loop. + MainFPM.addPass( + SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(true))); + MainFPM.addPass(createFunctionToLoopPassAdaptor(SimpleLoopUnswitchPass())); + } + + MainFPM.addPass(createFunctionToLoopPassAdaptor( + LICMPass(PTO.LicmMssaOptCap, PTO.LicmMssaNoAccForPromotionCap, + /*AllowSpeculation=*/true), + /*USeMemorySSA=*/true, /*UseBlockFrequencyInfo=*/true)); + + if (RunNewGVN) + MainFPM.addPass(NewGVNPass()); + else + MainFPM.addPass(GVNPass()); + + // Remove dead memcpy()'s. + MainFPM.addPass(MemCpyOptPass()); + + if (CJPipeline) + MainFPM.addPass(CJRSSCEPass()); + + // Nuke dead stores. + MainFPM.addPass(DSEPass()); + MainFPM.addPass(MergedLoadStoreMotionPass()); +} + // TODO: Investigate the cost/benefit of tail call elimination on debugging. FunctionPassManager PassBuilder::buildO1FunctionSimplificationPipeline(OptimizationLevel Level, @@ -399,6 +469,9 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level, FunctionPassManager FPM; + if (CJPipeline && Level == OptimizationLevel::O2) + FPM.addPass(CJSimpleOpt()); + // Form SSA out of local memory accesses after breaking apart aggregates into // scalars. FPM.addPass(SROAPass()); @@ -429,6 +502,12 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level, FPM.addPass(JumpThreadingPass()); FPM.addPass(CorrelatedValuePropagationPass()); + if (CJPipeline && Level == OptimizationLevel::O2) { + if (EnableCJGenericIntrinsicOpt) + FPM.addPass(CJGenericIntrinsicOpt()); + FPM.addPass(CJSimpleRangeAnalysisPass()); + } + FPM.addPass( SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(true))); FPM.addPass(InstCombinePass()); @@ -446,7 +525,9 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level, !Level.isOptimizingForSize()) FPM.addPass(PGOMemOPSizeOpt()); - FPM.addPass(TailCallElimPass()); + if (!CangjieLTOPreOpt) + FPM.addPass(TailCallElimPass()); + FPM.addPass( SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(true))); @@ -466,6 +547,16 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level, // `LoopInstSimplify`. LoopPassManager LPM1, LPM2; + if (CJPipeline) { + LPM1.addPass(LICMPass(PTO.LicmMssaOptCap, PTO.LicmMssaNoAccForPromotionCap, + /*AllowSpeculation=*/false)); + // 2: Opt size level, Oz. -1: argument for MaxHeaderSize + LPM1.addPass(LoopRotatePass(Level == OptimizationLevel::O2 ? true : false)); + FPM.addPass(InstCombinePass()); + LPM1.addPass(IndVarSimplifyPass()); + LPM1.addPass(LoopDeletionPass()); + } + // Simplify the loop body. We do this initially to clean up after other loop // passes run, either when iterating on a loop or on inner loops with // implications on the outer loop. @@ -528,6 +619,9 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level, FPM.addPass( SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(true))); FPM.addPass(InstCombinePass()); + if (CJPipeline) { + FPM.addPass(CJLoopFloatOpt()); + } // The loop passes in LPM2 (LoopIdiomRecognizePass, IndVarSimplifyPass, // LoopDeletionPass and LoopFullUnrollPass) do not preserve MemorySSA. // *All* loop passes must preserve it, in order to be able to use it. @@ -581,6 +675,9 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level, // Specially optimize memory movement as it doesn't look like dataflow in SSA. FPM.addPass(MemCpyOptPass()); + if (CJPipeline && Level == OptimizationLevel::O2) + FPM.addPass(CJRSSCEPass()); + FPM.addPass(DSEPass()); FPM.addPass(createFunctionToLoopPassAdaptor( LICMPass(PTO.LicmMssaOptCap, PTO.LicmMssaNoAccForPromotionCap, @@ -728,10 +825,9 @@ PassBuilder::buildInlinerPipeline(OptimizationLevel Level, if (PGOOpt) IP.EnableDeferral = EnablePGOInlineDeferral; - ModuleInlinerWrapperPass MIWP( - IP, PerformMandatoryInliningsFirst, - InlineContext{Phase, InlinePass::CGSCCInliner}, - UseInlineAdvisor, MaxDevirtIterations); + ModuleInlinerWrapperPass MIWP(IP, PerformMandatoryInliningsFirst, + InlineContext{Phase, InlinePass::CGSCCInliner}, + UseInlineAdvisor, MaxDevirtIterations); // Require the GlobalsAA analysis for the module so we can query it within // the CGSCC pipeline. @@ -775,6 +871,20 @@ PassBuilder::buildInlinerPipeline(OptimizationLevel Level, for (auto &C : CGSCCOptimizerLateEPCallbacks) C(MainCGPipeline, Level); + if (CJPipeline) { + if (Level == OptimizationLevel::O2) { + if (EnableCJDevirtual) + MainCGPipeline.addPass(CJDevirtualOpt()); + } + if (!CJDisableEscapeAnalysis) { + MainCGPipeline.addPass(createCGSCCToFunctionPassAdaptor(SimplifyCFGPass( + SimplifyCFGOptions().convertSwitchRangeToICmp(true)))); + MainCGPipeline.addPass(createCGSCCToFunctionPassAdaptor(InstCombinePass())); + MainCGPipeline.addPass(createCGSCCToFunctionPassAdaptor(SCCPPass())); + MainCGPipeline.addPass(CJPartialEscapeAnalysisPass()); + } + } + // Lastly, add the core function simplification pipeline nested inside the // CGSCC walk. MainCGPipeline.addPass(createCGSCCToFunctionPassAdaptor( @@ -819,6 +929,22 @@ PassBuilder::buildModuleInlinerPipeline(OptimizationLevel Level, MPM.addPass(ModuleInlinerPass(IP, UseInlineAdvisor, Phase)); + if (CJPipeline) { + if (Level == OptimizationLevel::O2) { + if (EnableCJDevirtual) + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(CJDevirtualOpt())); + } + if (!CJDisableEscapeAnalysis) { + MPM.addPass(createModuleToFunctionPassAdaptor(SimplifyCFGPass( + SimplifyCFGOptions().convertSwitchRangeToICmp(true)))); + MPM.addPass(createModuleToFunctionPassAdaptor(InstCombinePass())); + MPM.addPass(createModuleToFunctionPassAdaptor(SCCPPass())); + MPM.addPass( + createModuleToPostOrderCGSCCPassAdaptor( + CJPartialEscapeAnalysisPass())); + } + } + MPM.addPass(createModuleToFunctionPassAdaptor( buildFunctionSimplificationPipeline(Level, Phase), PTO.EagerlyInvalidateAnalyses)); @@ -874,6 +1000,9 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level, // Create an early function pass manager to cleanup the output of the // frontend. FunctionPassManager EarlyFPM; + if (CJPipeline) + EarlyFPM.addPass(createFunctionToLoopPassAdaptor(LoopSimplifyCFGPass())); + // Lower llvm.expect to metadata before attempting transforms. // Compare/branch metadata may alter the behavior of passes like SimplifyCFG. EarlyFPM.addPass(LowerExpectIntrinsicPass()); @@ -971,14 +1100,15 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level, PTO.EagerlyInvalidateAnalyses)); // Add all the requested passes for instrumentation PGO, if requested. - if (PGOOpt && Phase != ThinOrFullLTOPhase::ThinLTOPostLink && + if (!CangjieLTOPreOpt && PGOOpt && + Phase != ThinOrFullLTOPhase::ThinLTOPostLink && (PGOOpt->Action == PGOOptions::IRInstr || PGOOpt->Action == PGOOptions::IRUse)) { addPGOInstrPasses(MPM, Level, /* RunProfileGen */ PGOOpt->Action == PGOOptions::IRInstr, /* IsCS */ false, PGOOpt->ProfileFile, PGOOpt->ProfileRemappingFile, Phase); - MPM.addPass(PGOIndirectCallPromotion(false, false)); + MPM.addPass(PGOIndirectCallPromotion(CJLTOOpt, false)); } if (PGOOpt && Phase != ThinOrFullLTOPhase::ThinLTOPostLink && PGOOpt->CSAction == PGOOptions::CSIRInstr) @@ -1008,7 +1138,6 @@ void PassBuilder::addVectorPasses(OptimizationLevel Level, FunctionPassManager &FPM, bool IsFullLTO) { FPM.addPass(LoopVectorizePass( LoopVectorizeOptions(!PTO.LoopInterleaving, !PTO.LoopVectorization))); - if (IsFullLTO) { // The vectorizer may have significantly shortened a loop body; unroll // again. Unroll small loops to hide loop backedge latency and saturate any @@ -1154,8 +1283,9 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level, // may make globals referenced by available external functions dead and saves // running remaining passes on the eliminated functions. These should be // preserved during prelinking for link-time inlining decisions. - if (!LTOPreLink) + if (!LTOPreLink && !CJPipeline) { MPM.addPass(EliminateAvailableExternallyPass()); + } if (EnableOrderFileInstrumentation) MPM.addPass(InstrOrderFilePass()); @@ -1211,6 +1341,16 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level, for (auto &C : VectorizerStartEPCallbacks) C(OptimizePM, Level); + if (CJPipeline) { + MPM.addPass( + createModuleToPostOrderCGSCCPassAdaptor(PostOrderFunctionAttrsPass())); + + FunctionPassManager MainFPM; + addEliminationOptPasses(MainFPM, PTO); + MPM.addPass(createModuleToFunctionPassAdaptor( + std::move(MainFPM), PTO.EagerlyInvalidateAnalyses)); + } + LoopPassManager LPM; // First rotate loops that may have been un-rotated by prior passes. // Disable header duplication at -Oz. @@ -1235,6 +1375,11 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level, addVectorPasses(Level, OptimizePM, /* IsFullLTO */ false); + if (CJPipeline) { + OptimizePM.addPass(createFunctionToLoopPassAdaptor(IndVarSimplifyPass())); + OptimizePM.addPass(EarlyCSEPass()); + } + // LoopSink pass sinks instructions hoisted by LICM, which serves as a // canonicalization pass that enables other optimizations. As a result, // LoopSink pass needs to be a very late IR pass to avoid undoing LICM @@ -1250,7 +1395,8 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level, OptimizePM.addPass(DivRemPairsPass()); // Try to annotate calls that were created during optimization. - OptimizePM.addPass(TailCallElimPass()); + if (!CangjieLTOPreOpt) + OptimizePM.addPass(TailCallElimPass()); // LoopSink (and other loop passes since the last simplifyCFG) might have // resulted in single-entry-single-exit or empty blocks. Clean up the CFG. @@ -1278,9 +1424,13 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level, MPM.addPass(IROutlinerPass()); // Merge functions if requested. - if (PTO.MergeFunctions) + if (PTO.MergeFunctions || CJPipeline) MPM.addPass(MergeFunctionsPass()); + if (!LTOPreLink && CJPipeline) { + MPM.addPass(EliminateAvailableExternallyPass()); + } + // Now we need to do some global optimization transforms. // FIXME: It would seem like these should come first in the optimization // pipeline and maybe be the bottom of the canonicalization pipeline? Weird @@ -1308,6 +1458,8 @@ PassBuilder::buildPerModuleDefaultPipeline(OptimizationLevel Level, ModulePassManager MPM; + setSLPVectorization(PTO); + // Convert @llvm.global.annotations to !annotation metadata. MPM.addPass(Annotation2MetadataPass()); @@ -1321,9 +1473,12 @@ PassBuilder::buildPerModuleDefaultPipeline(OptimizationLevel Level, if (PGOOpt && PGOOpt->DebugInfoForProfiling) MPM.addPass(createModuleToFunctionPassAdaptor(AddDiscriminatorsPass())); - const ThinOrFullLTOPhase LTOPhase = LTOPreLink - ? ThinOrFullLTOPhase::FullLTOPreLink - : ThinOrFullLTOPhase::None; + ThinOrFullLTOPhase LTOPhase = ThinOrFullLTOPhase::None; + if (CJPipeline && LTOPreLink) + LTOPhase = ThinOrFullLTOPhase::ThinLTOPreLink; + else if (LTOPreLink) + LTOPhase = ThinOrFullLTOPhase::FullLTOPreLink; + // Add the core simplification pipeline. MPM.addPass(buildModuleSimplificationPipeline(Level, LTOPhase)); @@ -1350,6 +1505,8 @@ PassBuilder::buildThinLTOPreLinkDefaultPipeline(OptimizationLevel Level) { ModulePassManager MPM; + setSLPVectorization(PTO); + // Convert @llvm.global.annotations to !annotation metadata. MPM.addPass(Annotation2MetadataPass()); @@ -1404,6 +1561,8 @@ ModulePassManager PassBuilder::buildThinLTODefaultPipeline( OptimizationLevel Level, const ModuleSummaryIndex *ImportSummary) { ModulePassManager MPM; + setSLPVectorization(PTO); + // Convert @llvm.global.annotations to !annotation metadata. MPM.addPass(Annotation2MetadataPass()); @@ -1470,6 +1629,8 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, ModuleSummaryIndex *ExportSummary) { ModulePassManager MPM; + setSLPVectorization(PTO); + // Convert @llvm.global.annotations to !annotation metadata. MPM.addPass(Annotation2MetadataPass()); @@ -1479,8 +1640,20 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, // Create a function that performs CFI checks for cross-DSO calls with targets // in the current module. MPM.addPass(CrossDSOCFIPass()); + if (CJPipeline && EnableCJPtrAuthBackwardCFI) { + MPM.addPass(PtrAuthBackwardCFI()); + } if (Level == OptimizationLevel::O0) { + if (CJPipeline) { + if (PGOOpt && (PGOOpt->Action == PGOOptions::IRInstr || + PGOOpt->Action == PGOOptions::IRUse)) + addPGOInstrPassesForO0( + MPM, + /* RunProfileGen */ (PGOOpt->Action == PGOOptions::IRInstr), + /* IsCS */ false, PGOOpt->ProfileFile, PGOOpt->ProfileRemappingFile); + } + // The WPD and LowerTypeTest passes need to run at -O0 to lower type // metadata and intrinsics. MPM.addPass(WholeProgramDevirtPass(ExportSummary, nullptr)); @@ -1498,6 +1671,17 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, return MPM; } + // Add all the requested passes for instrumentation PGO, if requested. + if (CJPipeline && !CangjieLTOPreOpt && PGOOpt && + (PGOOpt->Action == PGOOptions::IRInstr || + PGOOpt->Action == PGOOptions::IRUse)) { + addPGOInstrPasses(MPM, Level, + /* RunProfileGen */ PGOOpt->Action == PGOOptions::IRInstr, + /* IsCS */ false, PGOOpt->ProfileFile, + PGOOpt->ProfileRemappingFile, + ThinOrFullLTOPhase::FullLTOPostLink); + } + if (PGOOpt && PGOOpt->Action == PGOOptions::SampleUse) { // Load sample profile before running the LTO optimization pipeline. MPM.addPass(SampleProfileLoaderPass(PGOOpt->ProfileFile, @@ -1617,6 +1801,23 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, InlineContext{ThinOrFullLTOPhase::FullLTOPostLink, InlinePass::CGSCCInliner})); + if (CJPipeline) { + if (Level == OptimizationLevel::O2) { + if (EnableCJDevirtual) + MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(CJDevirtualOpt())); + } + if (!CJDisableEscapeAnalysis) { + MPM.addPass( + createModuleToFunctionPassAdaptor(SimplifyCFGPass( + SimplifyCFGOptions().convertSwitchRangeToICmp(true)))); + MPM.addPass(createModuleToFunctionPassAdaptor(InstCombinePass())); + MPM.addPass(createModuleToFunctionPassAdaptor(SCCPPass())); + MPM.addPass( + createModuleToPostOrderCGSCCPassAdaptor( + CJPartialEscapeAnalysisPass())); + } + } + // Optimize globals again after we ran the inliner. MPM.addPass(GlobalOptPass()); @@ -1627,6 +1828,16 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, // transform it to pass arguments by value instead of by reference. MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(ArgumentPromotionPass())); + if (CJPipeline) { + MPM.addPass(createModuleToFunctionPassAdaptor( + buildFunctionSimplificationPipeline( + Level, ThinOrFullLTOPhase::FullLTOPostLink), + PTO.EagerlyInvalidateAnalyses)); + } + + if (RunPartialInlining) + MPM.addPass(PartialInlinerPass()); + FunctionPassManager FPM; // The IPO Passes may leave cruft around. Clean up after them. FPM.addPass(InstCombinePass()); @@ -1654,7 +1865,8 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, // LTO provides additional opportunities for tailcall elimination due to // link-time inlining, and visibility of nocapture attribute. - FPM.addPass(TailCallElimPass()); + if (!CangjieLTOPreOpt ) + FPM.addPass(TailCallElimPass()); // Run a few AA driver optimizations here and now to cleanup the code. MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM), @@ -1672,23 +1884,7 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, createModuleToFunctionPassAdaptor(InvalidateAnalysisPass())); FunctionPassManager MainFPM; - MainFPM.addPass(createFunctionToLoopPassAdaptor( - LICMPass(PTO.LicmMssaOptCap, PTO.LicmMssaNoAccForPromotionCap, - /*AllowSpeculation=*/true), - /*USeMemorySSA=*/true, /*UseBlockFrequencyInfo=*/true)); - - if (RunNewGVN) - MainFPM.addPass(NewGVNPass()); - else - MainFPM.addPass(GVNPass()); - - // Remove dead memcpy()'s. - MainFPM.addPass(MemCpyOptPass()); - - // Nuke dead stores. - MainFPM.addPass(DSEPass()); - MainFPM.addPass(MergedLoadStoreMotionPass()); - + addEliminationOptPasses(MainFPM, PTO); if (EnableConstraintElimination) MainFPM.addPass(ConstraintEliminationPass()); @@ -1776,8 +1972,9 @@ ModulePassManager PassBuilder::buildO0DefaultPipeline(OptimizationLevel Level, if (PGOOpt && PGOOpt->PseudoProbeForProfiling) MPM.addPass(SampleProfileProbePass(TM)); - if (PGOOpt && (PGOOpt->Action == PGOOptions::IRInstr || - PGOOpt->Action == PGOOptions::IRUse)) + if (!CangjieLTOPreOpt && PGOOpt && + (PGOOpt->Action == PGOOptions::IRInstr || + PGOOpt->Action == PGOOptions::IRUse)) addPGOInstrPassesForO0( MPM, /* RunProfileGen */ (PGOOpt->Action == PGOOptions::IRInstr), @@ -1886,6 +2083,9 @@ AAManager PassBuilder::buildDefaultAAPipeline() { AA.registerFunctionAnalysis(); AA.registerFunctionAnalysis(); + if (CJPipeline) + AA.registerFunctionAnalysis(); + // Add support for querying global aliasing information when available. // Because the `AAManager` is a function analysis and `GlobalsAA` is a module // analysis, all that the `AAManager` can do is query for any *cached* diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 7c29bffbc..7854c507c 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -28,6 +28,7 @@ MODULE_ANALYSIS("verify", VerifierAnalysis()) MODULE_ANALYSIS("pass-instrumentation", PassInstrumentationAnalysis(PIC)) MODULE_ANALYSIS("inline-advisor", InlineAdvisorAnalysis()) MODULE_ANALYSIS("ir-similarity", IRSimilarityAnalysis()) +MODULE_ANALYSIS("obf-config", ObfConfigAnalysis()) #ifndef MODULE_ALIAS_ANALYSIS #define MODULE_ALIAS_ANALYSIS(NAME, CREATE_PASS) \ @@ -48,6 +49,7 @@ MODULE_PASS("called-value-propagation", CalledValuePropagationPass()) MODULE_PASS("canonicalize-aliases", CanonicalizeAliasesPass()) MODULE_PASS("cg-profile", CGProfilePass()) MODULE_PASS("check-debugify", NewPMCheckDebugifyPass()) +MODULE_PASS("cj-ptrauth-backward-cfi", PtrAuthBackwardCFI()) MODULE_PASS("constmerge", ConstantMergePass()) MODULE_PASS("coro-early", CoroEarlyPass()) MODULE_PASS("coro-cleanup", CoroCleanupPass()) @@ -126,6 +128,17 @@ MODULE_PASS("sancov-module", ModuleSanitizerCoveragePass()) MODULE_PASS("memprof-module", ModuleMemProfilerPass()) MODULE_PASS("poison-checking", PoisonCheckingPass()) MODULE_PASS("pseudo-probe-update", PseudoProbeUpdatePass()) +MODULE_PASS("place-safepoints", PlaceSafepoints()) +MODULE_PASS("cj-ir-verifier", CJIRVerifier()) +MODULE_PASS("cj-fill-metadata", CJFillMetadata()) +MODULE_PASS("cj-barrier-opt", CJBarrierOpt()) +MODULE_PASS("cj-runtime-lowering", CJRuntimeLowering()) +MODULE_PASS("cj-specific-opt", CJSpecificOpt()) +MODULE_PASS("cj-barrier-split", CJBarrierSplit()) +MODULE_PASS("cj-rewrite-statepoint", CJRewriteStatepoint()) +MODULE_PASS("obf-data", DataObfuscator()) +MODULE_PASS("obf-layout", LayoutObfuscator()) +MODULE_PASS("obf-controlflow", ControlFlowObfuscator()) #undef MODULE_PASS #ifndef MODULE_PASS_WITH_PARAMS @@ -173,6 +186,8 @@ CGSCC_PASS("function-attrs", PostOrderFunctionAttrsPass()) CGSCC_PASS("attributor-cgscc", AttributorCGSCCPass()) CGSCC_PASS("openmp-opt-cgscc", OpenMPOptCGSCCPass()) CGSCC_PASS("no-op-cgscc", NoOpCGSCCPass()) +CGSCC_PASS("cj-pea", CJPartialEscapeAnalysisPass()) +CGSCC_PASS("cj-devirtual-opt", CJDevirtualOpt()) #undef CGSCC_PASS #ifndef CGSCC_PASS_WITH_PARAMS @@ -239,6 +254,7 @@ FUNCTION_ALIAS_ANALYSIS("objc-arc-aa", objcarc::ObjCARCAA()) FUNCTION_ALIAS_ANALYSIS("scev-aa", SCEVAA()) FUNCTION_ALIAS_ANALYSIS("scoped-noalias-aa", ScopedNoAliasAA()) FUNCTION_ALIAS_ANALYSIS("tbaa", TypeBasedAA()) +FUNCTION_ALIAS_ANALYSIS("cangjie-aa", CangjieAA()) #undef FUNCTION_ALIAS_ANALYSIS #undef FUNCTION_ANALYSIS @@ -268,6 +284,7 @@ FUNCTION_PASS("div-rem-pairs", DivRemPairsPass()) FUNCTION_PASS("dse", DSEPass()) FUNCTION_PASS("dot-cfg", CFGPrinterPass()) FUNCTION_PASS("dot-cfg-only", CFGOnlyPrinterPass()) +FUNCTION_PASS("dot-cj-cfg", CJCFGPrinterPass()) FUNCTION_PASS("dot-dom", DomPrinter()) FUNCTION_PASS("dot-dom-only", DomOnlyPrinter()) FUNCTION_PASS("dot-post-dom", PostDomPrinter()) @@ -378,10 +395,17 @@ FUNCTION_PASS("verify", SafepointIRVerifierPass()) FUNCTION_PASS("verify", ScalarEvolutionVerifierPass()) FUNCTION_PASS("view-cfg", CFGViewerPass()) FUNCTION_PASS("view-cfg-only", CFGOnlyViewerPass()) +FUNCTION_PASS("view-cj-cfg", CJCFGViewerPass()) FUNCTION_PASS("tlshoist", TLSVariableHoistPass()) FUNCTION_PASS("transform-warning", WarnMissedTransformationsPass()) FUNCTION_PASS("tsan", ThreadSanitizerPass()) FUNCTION_PASS("memprof", MemProfilerPass()) +FUNCTION_PASS("cj-generic-intrinsic-opt", CJGenericIntrinsicOpt()) +FUNCTION_PASS("insert-cj-tbaa", InsertCJTBAA()) +FUNCTION_PASS("cj-simple-opt", CJSimpleOpt()) +FUNCTION_PASS("cj-loop-float-opt", CJLoopFloatOpt()) +FUNCTION_PASS("cj-simple-range-analysis", CJSimpleRangeAnalysisPass()) +FUNCTION_PASS("cj-rssce", CJRSSCEPass()) #undef FUNCTION_PASS #ifndef FUNCTION_PASS_WITH_PARAMS diff --git a/llvm/lib/ProfileData/InstrProfReader.cpp b/llvm/lib/ProfileData/InstrProfReader.cpp index 23804ce60..ed1fde373 100644 --- a/llvm/lib/ProfileData/InstrProfReader.cpp +++ b/llvm/lib/ProfileData/InstrProfReader.cpp @@ -37,6 +37,11 @@ using namespace llvm; +static cl::opt + EnableTruncatedProfile("enable-truncated-profile", cl::init(false), + cl::Hidden, + cl::desc("enable merging truncated profile data")); + // Extracts the variant information from the top 8 bits in the version and // returns an enum specifying the variants present. static InstrProfKind getProfileKindFromVersion(uint64_t Version) { @@ -519,6 +524,8 @@ Error RawInstrProfReader::readValueProfilingData( InstrProfRecord &Record) { Record.clearValueData(); CurValueDataSize = 0; + if (EnableTruncatedProfile) + return success(); // Need to match the logic in value profile dumper code in compiler-rt: uint32_t NumValueKinds = 0; for (uint32_t I = 0; I < IPVK_Last + 1; I++) diff --git a/llvm/lib/Support/Triple.cpp b/llvm/lib/Support/Triple.cpp index 6696d158b..1255795b2 100644 --- a/llvm/lib/Support/Triple.cpp +++ b/llvm/lib/Support/Triple.cpp @@ -193,6 +193,7 @@ StringRef Triple::getVendorTypeName(VendorType Kind) { case PC: return "pc"; case SCEI: return "scei"; case SUSE: return "suse"; + case Harmony: return "hm"; } llvm_unreachable("Invalid VendorType!"); @@ -249,6 +250,7 @@ StringRef Triple::getEnvironmentTypeName(EnvironmentType Kind) { switch (Kind) { case UnknownEnvironment: return "unknown"; case Android: return "android"; + case OpenHOS: return "ohos"; case CODE16: return "code16"; case CoreCLR: return "coreclr"; case Cygnus: return "cygnus"; @@ -543,6 +545,7 @@ static Triple::VendorType parseVendor(StringRef VendorName) { .Case("mesa", Triple::Mesa) .Case("suse", Triple::SUSE) .Case("oe", Triple::OpenEmbedded) + .Case("hm", Triple::Harmony) .Default(Triple::UnknownVendor); } @@ -613,6 +616,7 @@ static Triple::EnvironmentType parseEnvironment(StringRef EnvironmentName) { .StartsWith("coreclr", Triple::CoreCLR) .StartsWith("simulator", Triple::Simulator) .StartsWith("macabi", Triple::MacABI) + .StartsWith("ohos", Triple::OpenHOS) .StartsWith("pixel", Triple::Pixel) .StartsWith("vertex", Triple::Vertex) .StartsWith("geometry", Triple::Geometry) diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index c568f7347..d6870018a 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -32,6 +32,7 @@ #include "llvm/BinaryFormat/COFF.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/CJMetadata.h" #include "llvm/CodeGen/FaultMaps.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineFunction.h" @@ -43,6 +44,7 @@ #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/Statepoint.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCInst.h" @@ -66,11 +68,16 @@ using namespace llvm; #define DEBUG_TYPE "asm-printer" +namespace llvm { +extern cl::opt CJPipeline; +extern cl::opt EnableStackGrow; +} namespace { class AArch64AsmPrinter : public AsmPrinter { AArch64MCInstLower MCInstLowering; StackMaps SM; + CJMetadataInfo CMI; FaultMaps FM; const AArch64Subtarget *STI; bool ShouldEmitWeakSwiftAsyncExtendedFramePointerFlags = false; @@ -78,7 +85,9 @@ class AArch64AsmPrinter : public AsmPrinter { public: AArch64AsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) : AsmPrinter(TM, std::move(Streamer)), MCInstLowering(OutContext, *this), - SM(*this), FM(*this) {} + SM(*this), CMI(*this, SM), FM(*this) { + SM.setAArch64(); + } StringRef getPassName() const override { return "AArch64 Assembly Printer"; } @@ -155,10 +164,14 @@ public: // Emit the XRay table for this function. emitXRayTable(); + CMI.recordCurrentFunc(); + // We didn't modify anything. return false; } + void emitMetadataAddress() override; + private: void printOperand(const MachineInstr *MI, unsigned OpNum, raw_ostream &O); bool printAsmMRegister(const MachineOperand &MO, char Mode, raw_ostream &O); @@ -193,6 +206,63 @@ private: bool shouldEmitWeakSwiftAsyncExtendedFramePointerFlags() const override { return ShouldEmitWeakSwiftAsyncExtendedFramePointerFlags; } + MCOperand setGAAndLower(const MachineOperand &MOSym, const GlobalValue *GV, + unsigned Flag = AArch64II::MO_NO_FLAG) const { + MachineOperand MOAddr(MOSym); + MCOperand SymAddr; + MOAddr.ChangeToGA(GV, 0); + if (Flag != AArch64II::MO_NO_FLAG) { + MOAddr.setTargetFlags(Flag); + } + MCInstLowering.lowerOperand(MOAddr, SymAddr); + return SymAddr; + } + + void extendStackAndInsertFFIInfoForJmp(const MCOperand &SymOriAddr, + const MCOperand &SymOriAddrLo12, + unsigned CallFrameSize); + + void emitCangjieCallStubInstImpl(const MachineInstr *MI, const Function *F, + const MachineOperand &MOSym, + unsigned Opcode) override; + + void emitGetCJThreadId() override; + struct ParamForEmitNewObj { + ParamForEmitNewObj(const MachineInstr *MIInit, + const MachineOperand &MOSymInit, unsigned int OpcodeInit, + MCSymbol *LFinish, const MCSymbolRefExpr *FinishExpr, + const MCInst &CallSlow, StringRef FastName) + : MI(MIInit), MOSym(MOSymInit), Opcode(OpcodeInit), LFin(LFinish), + FinExpr(FinishExpr), CallNewObjSlowPath(CallSlow), + FastFuncName(FastName) {} + ~ParamForEmitNewObj() = default; + const MachineInstr *MI; + const MachineOperand &MOSym; + unsigned int Opcode; + MCSymbol *LFin; + const MCSymbolRefExpr *FinExpr; + const MCInst &CallNewObjSlowPath; + StringRef FastFuncName; + }; + + void emitMccNewObjectForCopyGC(const ParamForEmitNewObj &Param); + void emitMccNewObjectFastPath(const MachineInstr *MI, + const MachineOperand &MOSym, + unsigned Opcode) override; + void emitCJNewArrayFastPath(const MachineInstr &MI, + const MachineOperand &MOSym) override; + void emitCJThrowException(const MachineInstr *MI, + const MachineOperand &MOSym, + unsigned Opcode) override; + void emitAddSP(unsigned AddSize); + void emitSubSP(unsigned Size); + void emitCJStackCheck(const MachineInstr &MI) override; + int emitStackOverflowCall(const MachineInstr &MI); + void emitCangjieSafepoint(const MachineInstr &MI) override; + int emitSafePointDirectCall(unsigned Index) override; + void emitSafepoint(const MachineInstr &MI); + void emitGcStateCheck() override; + void emitGetCJTLSData(int64_t Offset); }; } // end anonymous namespace @@ -579,7 +649,7 @@ void AArch64AsmPrinter::emitEndOfAsmFile(Module &M) { // generates code that does this, it is always safe to set. OutStreamer->emitAssemblerFlag(MCAF_SubsectionsViaSymbols); } - + emitCJMetadataInfo(CMI, M); // Emit stack and fault map information. emitStackMaps(SM); FM.serializeToFaultMapSection(); @@ -1050,24 +1120,362 @@ void AArch64AsmPrinter::LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM, EmitToStreamer(OutStreamer, MCInstBuilder(AArch64::HINT).addImm(0)); } +static unsigned calculateFrameSize(const MachineFunction *MF, + const MachineInstr &MI) { + const TargetFrameLowering *TFI = MF->getSubtarget().getFrameLowering(); + const MachineFrameInfo &MFI = MF->getFrameInfo(); + bool IsFunclet = MI.getParent()->isEHFuncletEntry(); + unsigned FrameSize = 0; + if (IsFunclet) { + // This is the size of the pushed CSRs. + unsigned CSSize = + MF->getInfo()->getCalleeSavedStackSize(); + // This is the amount of stack a funclet needs to allocate. + FrameSize = alignTo(CSSize + MF->getFrameInfo().getMaxCallFrameSize(), + TFI->getStackAlign()); + } else { + FrameSize = MFI.getStackSize(); + } + return FrameSize; +} + +// Note: emit specific inst should update inst size info in +// AArch64InstrInfo::getInstSizeInBytes for AArch64 at the same time +// add sp, sp, #AddSize +void AArch64AsmPrinter::emitAddSP(unsigned AddSize) { + using namespace AArch64; + MCInst AddSPLarge; + MCInst AddSPMid; + MCInst AddSPSmall; + // 4096: the max imm for aarch64, if the size exceeds this value, we need to + // use the add instruction with shift to do so. + unsigned LargeNum = AddSize / (4096 * 4096); + if (LargeNum) { + // 24: 2 ^ 24 = 4096 * 4096 + AddSPLarge = + MCInstBuilder(ADDXri).addReg(SP).addReg(SP).addImm(LargeNum).addImm( + AArch64_AM::getShifterImm(AArch64_AM::LSL, 24)); + } + unsigned MidNum = AddSize % (4096 * 4096) / 4096; + if (MidNum > 0) { + // 12: 2 ^ 12 = 4096 + AddSPMid = + MCInstBuilder(ADDXri).addReg(SP).addReg(SP).addImm(MidNum).addImm( + AArch64_AM::getShifterImm(AArch64_AM::LSL, 12)); + } + unsigned SmallNum = AddSize % 4096; + if (SmallNum > 0) { + AddSPSmall = + MCInstBuilder(ADDXri).addReg(SP).addReg(SP).addImm(SmallNum).addImm(0); + } + + if (LargeNum > 0) { + EmitToStreamer(*OutStreamer, AddSPLarge); + } + if (MidNum > 0) { + EmitToStreamer(*OutStreamer, AddSPMid); + } + if (SmallNum > 0) { + EmitToStreamer(*OutStreamer, AddSPSmall); + } +} + +// Note: emit specific inst should update inst size info in +// AArch64InstrInfo::getInstSizeInBytes for AArch64 at the same time +// sub sp, sp, #Size +void AArch64AsmPrinter::emitSubSP(unsigned Size) { + using namespace AArch64; + unsigned RevertSize = Size; + + MCInst SubSPLarge; + MCInst SubSPMid; + MCInst SubSPSmall; + // 4096: the max imm for aarch64, if the size exceeds this value, we need to + // use the sub instruction with shift to do so. + unsigned LargeNum = RevertSize / (4096 * 4096); + if (LargeNum) { + // 24: 2 ^ 24 = 4096 * 4096 + SubSPLarge = + MCInstBuilder(SUBXri).addReg(SP).addReg(SP).addImm(LargeNum).addImm( + AArch64_AM::getShifterImm(AArch64_AM::LSL, 24)); + } + unsigned MidNum = RevertSize % (4096 * 4096) / 4096; + if (MidNum > 0) { + // 12: 2 ^ 12 = 4096 + SubSPMid = + MCInstBuilder(SUBXri).addReg(SP).addReg(SP).addImm(MidNum).addImm( + AArch64_AM::getShifterImm(AArch64_AM::LSL, 12)); + } + unsigned SmallNum = RevertSize % 4096; + if (SmallNum > 0) { + SubSPSmall = + MCInstBuilder(SUBXri).addReg(SP).addReg(SP).addImm(SmallNum).addImm(0); + } + if (LargeNum > 0) { + EmitToStreamer(*OutStreamer, SubSPLarge); + } + if (MidNum > 0) { + EmitToStreamer(*OutStreamer, SubSPMid); + } + if (SmallNum > 0) { + EmitToStreamer(*OutStreamer, SubSPSmall); + } +} + +// Note: emit specific inst should update inst size info in +// AArch64InstrInfo::getInstSizeInBytes for AArch64 at the same time +// ldr x9, [x28, #ProtectAddr] +// subs xzr, sp, x9 # stack.check +// b.gt Lstack.check.end +// ... # stack.overflow handle +// .Lstack.check.end: +void AArch64AsmPrinter::emitCJStackCheck(const MachineInstr &MI) { + using namespace AArch64; + MCContext &Ctx = MF->getContext(); + MCSymbol *EndSym = Ctx.createTempSymbol("stack.check.end"); + + // ldr x9, [x28, #ProtectAddr] + emitGetCJTLSData(getProtectAddrOffsetInCJTLS()); + MCInst Cmp = MCInstBuilder(SUBSXrx).addReg(XZR).addReg(SP).addReg(X9).addImm( + AArch64_AM::getArithExtendImm(AArch64_AM::UXTX, 0)); + EmitToStreamer(*OutStreamer, Cmp); + + auto BrExpr = MCSymbolRefExpr::create(EndSym, MCSymbolRefExpr::VK_None, Ctx); + MCInst BrInst = MCInstBuilder(Bcc).addImm(AArch64CC::GT).addExpr(BrExpr); + EmitToStreamer(*OutStreamer, BrInst); + emitStackOverflowCall(MI); + OutStreamer->emitLabel(EndSym); +} + +// Note: emit specific inst should update inst size info in +// AArch64InstrInfo::getInstSizeInBytes for AArch64 at the same time +// >>>>>>>>>>>>>>>>>>>>>>> disable stackgrow +// add sp, sp, #AddSize +// mov x0, #LeftSize +// bl CJ_MCC_ThrowStackOverflowError +// ======================= +// add sp, sp, #AddSize +// mov x9, #LeftSize +// bl CJ_MCC_StackGrowStub +// Ltemp +// sub sp, sp, #AddSize +// <<<<<<<<<<<<<<<<<<<<<<< enable stackgrow +int AArch64AsmPrinter::emitStackOverflowCall(const MachineInstr &MI) { + using namespace AArch64; + unsigned FrameSize = calculateFrameSize(MF, MI); + unsigned AddSize = MI.peekCJStackSize(); + emitAddSP(AddSize); + + MCContext &Ctx = MF->getContext(); + unsigned LeftSize = FrameSize - AddSize; + if (!EnableStackGrow) { // stack overflow + MCInst Mov = MCInstBuilder(MOVZXi).addReg(X0).addImm(LeftSize).addImm(0); + MCSymbol *Sym = nullptr; + if (MF->getTarget().getTargetTriple().isOSBinFormatMachO()) { + Sym = Ctx.getOrCreateSymbol("_CJ_MCC_ThrowStackOverflowError"); + } else { + Sym = Ctx.getOrCreateSymbol("CJ_MCC_ThrowStackOverflowError"); + } + MCInst BLToSOFE = MCInstBuilder(BL).addExpr( + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, Ctx)); + EmitToStreamer(*OutStreamer, Mov); + EmitToStreamer(*OutStreamer, BLToSOFE); + return 3; // 3: instruction nums. + } else { // stack grow + MCInst MovX9 = MCInstBuilder(MOVZXi).addReg(X9).addImm(LeftSize).addImm(0); + MCSymbol *Sym = Ctx.getOrCreateSymbol("CJ_MCC_StackGrowStub"); + MCInst BLToCall = MCInstBuilder(BL).addExpr( + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, Ctx)); + EmitToStreamer(*OutStreamer, MovX9); + EmitToStreamer(*OutStreamer, BLToCall); + SM.recordCJStackMap(MI, true); + + // revert sp + emitSubSP(AddSize); + return 6; // 6: instruction nums. + } +} + +// ldr x9, [x28, #tlsOffset] +void AArch64AsmPrinter::emitGetCJTLSData(int64_t Offset) { + MCInst Ldr; + Ldr.setOpcode(AArch64::LDRXui); + Ldr.addOperand(MCOperand::createReg(AArch64::X9)); + Ldr.addOperand(MCOperand::createReg(AArch64::X28)); + // 8: each imm represents 8 In MCInst. + Ldr.addOperand(MCOperand::createImm(Offset / 8)); + Ldr.addOperand(MCOperand::createImm(0)); + EmitToStreamer(*OutStreamer, Ldr); +} + +// Note: emit specific inst should update inst size info in +// AArch64InstrInfo::getInstSizeInBytes for AArch64 at the same time +void AArch64AsmPrinter::emitGcStateCheck() { + if (TM.getTargetTriple().isWebm()) { + // ldr x9, [x28, #MutatorOffsetInCJTLS] + // ldr x9, [x9, #0] + emitGetCJTLSData(getMutatorOffsetInCJTLS()); + MCInst Ldr; + Ldr.setOpcode(AArch64::LDRXui); + Ldr.addOperand(MCOperand::createReg(AArch64::X9)); + Ldr.addOperand(MCOperand::createReg(AArch64::X9)); + Ldr.addOperand(MCOperand::createImm(0)); + Ldr.addOperand(MCOperand::createImm(0)); + EmitToStreamer(*OutStreamer, Ldr); + } else { + // lsr x9, x28, #56 + MCInst Lsr; + Lsr.setOpcode(AArch64::UBFMXri); + Lsr.addOperand(MCOperand::createReg(AArch64::W9)); + Lsr.addOperand(MCOperand::createReg(AArch64::X28)); + Lsr.addOperand(MCOperand::createImm(56)); // 56: Shift bit + Lsr.addOperand(MCOperand::createImm(63)); // 63: Shift range + EmitToStreamer(*OutStreamer, Lsr); + } +} + +// Note: emit specific inst should update inst size info in +// AArch64InstrInfo::getInstSizeInBytes for AArch64 at the same time +// ldr x9, [x28, #SafepointCheckAddrOffset] +// cmp x9, #0 +// >>>>>>>>>>>>>>>>>>> +// case 1: +// b.ne Label +// ... +// Label: +// adrp x9, CJ_MCC_HandleSafepoint.CJStubGV +// ldr x9, [x9, :lo12:CJ_MCC_HandleSafepoint.CJStubGV] +// blr x9 +// =================== Offset beyond 0x7FFFF +// case 2: +// b.eq Label +// adrp x9, CJ_MCC_HandleSafepoint.CJStubGV +// ldr x9, [x9, :lo12:CJ_MCC_HandleSafepoint.CJStubGV] +// blr x9 +// Label: +// ... +// <<<<<<<<<<<<<<<<<<< +void AArch64AsmPrinter::emitCangjieSafepoint(const MachineInstr &MI) { + emitGetCJTLSData(getSafepointCheckAddrOffsetInCJTLS()); + + auto &Ctx = OutStreamer->getContext(); + MCInst Cmp = MCInstBuilder(AArch64::SUBSXri) + .addReg(AArch64::XZR) + .addReg(AArch64::X9) + .addImm(0) + .addImm(0); + EmitToStreamer(*OutStreamer, Cmp); + + // If Jmp size beyond the 19bit, emitting safepoint on following inst + // instead of emit it at the end of the function. + // Each Inst size is 4 bytes. + if (MI.getMF()->getInstructionCount() * 4 > 0x7FFFF) { + MCSymbol *EndLabel = Ctx.createTempSymbol(); + MCInst Branch = MCInstBuilder(AArch64::Bcc) + .addImm(AArch64CC::EQ) + .addExpr(MCSymbolRefExpr::create(EndLabel, Ctx)); + EmitToStreamer(*OutStreamer, Branch); + emitSafepoint(MI); + OutStreamer->emitLabel(EndLabel); + } else { + MCSymbol *JmpLabel = Ctx.createTempSymbol(); + MCSymbol *EndLabel = Ctx.createTempSymbol(); + MCInst Branch = MCInstBuilder(AArch64::Bcc) + .addImm(AArch64CC::NE) + .addExpr(MCSymbolRefExpr::create(JmpLabel, Ctx)); + EmitToStreamer(*OutStreamer, Branch); + OutStreamer->emitLabel(EndLabel); + SafepointStackMap.push_back(std::make_tuple(&MI, JmpLabel, EndLabel)); + } +} + +// adrp x9, CJ_MCC_HandleSafepoint.CJStubGV +// ldr x9, [x9, :lo12:CJ_MCC_HandleSafepoint.CJStubGV] +// blr x9 +// jmp Label1 +int AArch64AsmPrinter::emitSafePointDirectCall(unsigned Index) { + auto &Ctx = OutStreamer->getContext(); + auto SS = SafepointStackMap[Index]; + MCSymbol *CurLabel = std::get<1>(SS); + MCSymbol *BackLabel = std::get<2>(SS); + OutStreamer->emitLabel(CurLabel); + emitSafepoint(*std::get<0>(SS)); + + auto Expr = MCSymbolRefExpr::create(BackLabel, MCSymbolRefExpr::VK_None, Ctx); + MCInst BNoCond = MCInstBuilder(AArch64::B).addExpr(Expr); + EmitToStreamer(*OutStreamer, BNoCond); + // 4: instruction nums. + return 4; +} + +// adrp x9, CJ_MCC_HandleSafepoint.CJStubGV +// ldr x9, [x9, :lo12:CJ_MCC_HandleSafepoint.CJStubGV] +// blr x9 +void AArch64AsmPrinter::emitSafepoint(const MachineInstr &MI) { + using namespace AArch64; + + auto *AddrGV = MF->getFunction().getParent()->getGlobalVariable( + "CJ_MCC_HandleSafepoint.CJStubGV", true); + MachineOperand MOSym = MachineOperand::CreateGA(AddrGV, 0); + MCOperand SymOriAddr = setGAAndLower(MOSym, AddrGV, AArch64II::MO_PAGE); + MCOperand SymOriAddrLo12 = + setGAAndLower(MOSym, AddrGV, AArch64II::MO_PAGEOFF | AArch64II::MO_NC); + + MCInst Adrp = MCInstBuilder(ADRP).addReg(X9).addOperand(SymOriAddr); + EmitToStreamer(*OutStreamer, Adrp); + + MCInst Ldr = MCInstBuilder(LDRXui) + .addReg(X9) + .addReg(X9) + .addOperand(SymOriAddrLo12) + .addImm(0); + EmitToStreamer(*OutStreamer, Ldr); + + MCInst CallReg = MCInstBuilder(BLR).addReg(X9); + EmitToStreamer(*OutStreamer, CallReg); + SM.recordCJStackMap(MI, true); +} + void AArch64AsmPrinter::LowerSTATEPOINT(MCStreamer &OutStreamer, StackMaps &SM, const MachineInstr &MI) { StatepointOpers SOpers(&MI); + // Lower call target and choose correct opcode + const MachineOperand &CallTarget = SOpers.getCallTarget(); + if (CJPipeline) { + uint64_t ID = SOpers.getID(); + if (ID == CJStatepointID::Safepoint) { + emitCangjieSafepoint(MI); + return; + } + if (ID == CJStatepointID::StackCheck) { + emitCangjieStackCheck(MI); + return; + } + if (ID == CJStatepointID::NewArrayFast) { + emitCJNewArrayFastPath(MI, CallTarget); + return; + } + } + if (unsigned PatchBytes = SOpers.getNumPatchBytes()) { - assert(PatchBytes % 4 == 0 && "Invalid number of NOP bytes requested!"); + // 4: size of the patchpoint intrinsic + assert(PatchBytes % 4 == 0 && "Invalid number of NOP bytes requested"); for (unsigned i = 0; i < PatchBytes; i += 4) EmitToStreamer(OutStreamer, MCInstBuilder(AArch64::HINT).addImm(0)); } else { - // Lower call target and choose correct opcode - const MachineOperand &CallTarget = SOpers.getCallTarget(); MCOperand CallTargetMCOp; unsigned CallOpcode; switch (CallTarget.getType()) { case MachineOperand::MO_GlobalAddress: - case MachineOperand::MO_ExternalSymbol: - MCInstLowering.lowerOperand(CallTarget, CallTargetMCOp); + case MachineOperand::MO_ExternalSymbol: { CallOpcode = AArch64::BL; + if (tryEmitCangjieSpecificCallByMOSym(&MI, CallTarget, CallOpcode)) { + SM.recordCJStackMap(MI); + return; + } + MCInstLowering.lowerOperand(CallTarget, CallTargetMCOp); break; + } case MachineOperand::MO_Immediate: CallTargetMCOp = MCOperand::createImm(CallTarget.getImm()); CallOpcode = AArch64::BL; @@ -1196,12 +1604,69 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) { OutStreamer->emitLabel(LOHLabel); } + // try to process cangjie specific call + if (tryEmitCangjieSpecificCall(MI)) { + return; + } AArch64TargetStreamer *TS = static_cast(OutStreamer->getTargetStreamer()); // Do any manual lowerings. switch (MI->getOpcode()) { default: break; + case AArch64::ADR: { + // store func begin address to stack slot, and MI in storeFunctionAddress. + if (!MI->getFlag(MachineInstr::FrameSetup)) { + break; + } + // adr x9, Lfunc_begin + MCInst TmpInst; + TmpInst.setOpcode(AArch64::ADR); + TmpInst.addOperand(MCOperand::createReg(MI->getOperand(0).getReg())); + const MCExpr *Expr = MCSymbolRefExpr::create( + getFunctionBegin(), MCSymbolRefExpr::VK_None, OutContext); + TmpInst.addOperand(MCOperand::createExpr(Expr)); + EmitToStreamer(*OutStreamer, TmpInst); + return; + } + case AArch64::ADRP: { + // store func begin address to stack slot, and MI in storeFunctionAddress. + if (!MI->getFlag(MachineInstr::FrameSetup)) { + break; + } + // Only for macos now. + MCInst AdrpInst; + Function &Func = MF->getFunction(); + const MCExpr *Expr = nullptr; + if (!Func.hasFnAttribute("leaf-function")) { + Metadata *MD = Func.getParent()->getModuleFlag("Cangjie_PACKAGE_ID"); + if (MD == nullptr) { + report_fatal_error("There is not cangjie package id in module!"); + } + StringRef PACKAGEID = dyn_cast(MD)->getString(); + MCSymbol *DescSymbol = OutContext.getOrCreateSymbol( + ".Lmethod_desc." + PACKAGEID + "._" + MF->getName()); + Expr = MCSymbolRefExpr::create( + DescSymbol, MCSymbolRefExpr::VK_PAGE, OutContext); + // adrp x10, .Lmethod_desc.PACKAGEID.xxx@PAGE + AdrpInst.setOpcode(AArch64::ADRP); + AdrpInst.addOperand(MCOperand::createReg(AArch64::X10)); + AdrpInst.addOperand(MCOperand::createExpr(Expr)); + EmitToStreamer(*OutStreamer, AdrpInst); + + MCInst AddInst; + const MCExpr *AddExpr = MCSymbolRefExpr::create( + DescSymbol, MCSymbolRefExpr::VK_PAGEOFF, OutContext); + // add x10, x10, .Lmethod_desc.PACKAGEID.xxx@PAGEOFF + AddInst.setOpcode(AArch64::ADDXri); + AddInst.addOperand(MCOperand::createReg(AArch64::X10)); + AddInst.addOperand(MCOperand::createReg(AArch64::X10)); + AddInst.addOperand(MCOperand::createExpr(AddExpr)); + AddInst.addOperand(MCOperand::createImm(AArch64_AM::getShiftValue(0))); + EmitToStreamer(*OutStreamer, AddInst); + } + return; + } case AArch64::HINT: { // CurrentPatchableFunctionEntrySym can be CurrentFnBegin only for // -fpatchable-function-entry=N,0. The entry MBB is guaranteed to be @@ -1559,6 +2024,364 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) { MCInstLowering.Lower(MI, TmpInst); EmitToStreamer(*OutStreamer, TmpInst); } +// Note: emit specific inst should update inst size info in +// AArch64InstrInfo::getInstSizeInBytes for AArch64 at the same time +// adrp x9, xxx.CJStubGV +// add x9, x9, :lo12:xxx.CJStubGV +// ldr x9, [x9] +// mov x10, #CallFrameSize +// stp x9, x10, [rsp, #-CallFrameSize]! +void AArch64AsmPrinter::extendStackAndInsertFFIInfoForJmp( + const MCOperand &SymOriAddr, const MCOperand &SymOriAddrLo12, + unsigned CallFrameSize) { + MCInst Adrp; + Adrp.setOpcode(AArch64::ADRP); + Adrp.addOperand(MCOperand::createReg(AArch64::X9)); + Adrp.addOperand(SymOriAddr); + EmitToStreamer(*OutStreamer, Adrp); + MCInst Add; + + Add.setOpcode(AArch64::ADDXri); + Add.addOperand(MCOperand::createReg(AArch64::X9)); + Add.addOperand(MCOperand::createReg(AArch64::X9)); + Add.addOperand(SymOriAddrLo12); + Add.addOperand(MCOperand::createImm(AArch64_AM::getShiftValue(0))); + EmitToStreamer(*OutStreamer, Add); + + MCInst Ldr; + Ldr.setOpcode(AArch64::LDRXui); + Ldr.addOperand(MCOperand::createReg(AArch64::X9)); + Ldr.addOperand(MCOperand::createReg(AArch64::X9)); + Ldr.addOperand(MCOperand::createImm(0)); + EmitToStreamer(*OutStreamer, Ldr); + + MCInst Mov; + Mov.setOpcode(AArch64::MOVZXi); + Mov.addOperand(MCOperand::createReg(AArch64::X10)); + Mov.addOperand(MCOperand::createImm(CallFrameSize)); + Mov.addOperand(MCOperand::createImm(0)); + EmitToStreamer(*OutStreamer, Mov); + + MCInst Stp; + Stp.setOpcode(AArch64::STPXpre); + Stp.addOperand(MCOperand::createReg(AArch64::SP)); + Stp.addOperand(MCOperand::createReg(AArch64::X9)); + Stp.addOperand(MCOperand::createReg(AArch64::X10)); + Stp.addOperand(MCOperand::createReg(AArch64::SP)); + Stp.addOperand(MCOperand::createImm(-2)); + EmitToStreamer(*OutStreamer, Stp); +} +// Note: emit specific inst should update inst size info in +// AArch64InstrInfo::getInstSizeInBytes for AArch64 at the same time +void AArch64AsmPrinter::emitCangjieCallStubInstImpl(const MachineInstr *MI, + const Function *F, + const MachineOperand &MOSym, + unsigned Opcode) { + auto *AddrGV = F->getParent()->getGlobalVariable( + MOSym.getGlobal()->getName().str() + ".CJStubGV", true); + MCOperand SymOriAddr = setGAAndLower(MOSym, AddrGV, AArch64II::MO_PAGE); + MCOperand SymOriAddrLo12 = + setGAAndLower(MOSym, AddrGV, AArch64II::MO_PAGEOFF | AArch64II::MO_NC); + MCOperand SymNewAddr = setGAAndLower(MOSym, F); + + const auto *OriCalled = dyn_cast(MOSym.getGlobal()); + // push callee-addr and CallFrameSize to stack. This operation extends + // the sp of the caller by 16 bytes and will be fixed in MCC_XXXStub + MDNode *Metadata = OriCalled->getMetadata("CallFrameSizeForCJFFI"); + assert(Metadata != nullptr && + "FFI Func must have CallFrameSizeForCJFFI meta!"); + unsigned CallFrameSize = + dyn_cast( + dyn_cast(Metadata->getOperand(0).get()) + ->getValue()) + ->getSExtValue(); + // MCC_XXXStub expect the end of caller stack is like: + // | callee-addr | + // | param-stack-size (16 bytes align) | + extendStackAndInsertFFIInfoForJmp(SymOriAddr, SymOriAddrLo12, CallFrameSize); + MCInst TemInst; + if (Opcode == AArch64::TCRETURNdi) { + Opcode = AArch64::B; // Branch directly for tailcall + } else { + assert(Opcode == AArch64::BL && + "Opcode should be BL or TCRETURNdi for Cangjie Call Stub"); + } + TemInst.setOpcode(Opcode); + TemInst.addOperand(SymNewAddr); + EmitToStreamer(*OutStreamer, TemInst); + SM.recordCJStackMap(*MI); + return; +} + +// Note: emit specific inst should update inst size info in +// AArch64InstrInfo::getInstSizeInBytes for AArch64 at the same time +// ldr x2, [x28, #AllocBufferOffset] // get Alloc Buffer +// ldr x2, [x2] // get region ptr +// ldr x3, [x2] // x3 = allocPtr +// ldr x4, [x2, #8] // x4 = limit +// add x5, x3, x1 // x5 = allocPtr + size +// cmp x5, x4 // allocPtr + size > limit ? +// b.gt .LNewObjSlowPath +// str x0, [x3] // allocPtr->KlassInfo = Klass +// str x5, [x2] // region->allocPtr = allocPtr + size +// mov x0, x3 // ret/arg = allocPtr +// <<< hasFinalizer +// bl MCC_OnFinalizerCreated +// >>> +// b .LNewObjFin +// .LNewObjSlowPath +// bl MCC_NewObjectStub +// .LNewObjFin +void AArch64AsmPrinter::emitMccNewObjectForCopyGC( + const ParamForEmitNewObj &Param) { + using namespace AArch64; + + MCSymbol *LFast = OutContext.createTempSymbol("NewObjFastPath", true); + MCSymbol *LSlow = OutContext.createTempSymbol("NewObjSlowPath", true); + const MCSymbolRefExpr *SlowExpr = MCSymbolRefExpr::create(LSlow, OutContext); + + // 8: each imm represents 8 In MCInst. + MCInst GetAllocBuffer = MCInstBuilder(LDRXui).addReg(X2).addReg(X28).addImm( + getAllocBufferOffsetInCJTLS() / 8); + // 0: offset of region Ptr in AllocBuffer is 0 bytes + MCInst GetRegionPtr = MCInstBuilder(LDRXui).addReg(X2).addReg(X2).addImm(0); + MCInst GetAllocPtr = MCInstBuilder(LDRXui).addReg(X3).addReg(X2).addImm(0); + // 1: offset of limit in region is 8 bytes + MCInst GetLimit = MCInstBuilder(LDRXui).addReg(X4).addReg(X2).addImm(1); + MCInst CalNewAllocPtr = + MCInstBuilder(ADDXrs).addReg(X5).addReg(X3).addReg(X1).addImm(0); + MCInst CmpNewAllocPtrWithLimit = + MCInstBuilder(SUBSXrs).addReg(XZR).addReg(X5).addReg(X4).addImm(0); + MCInst BranchGToLSLow = + MCInstBuilder(Bcc).addImm(AArch64CC::GT).addExpr(SlowExpr); + MCInst SetKlass = MCInstBuilder(STRXui).addReg(X0).addReg(X3).addImm(0); + MCInst StoreNewAllocPtr = + MCInstBuilder(STRXui).addReg(X5).addReg(X2).addImm(0); + MCInst CopyAllocPtrToX0 = + MCInstBuilder(ORRXrs).addReg(X0).addReg(XZR).addReg(X3).addImm(0); + MCInst BranchToFin = MCInstBuilder(B).addExpr(Param.FinExpr); + OutStreamer->emitLabel(LFast); + EmitToStreamer(*OutStreamer, GetAllocBuffer); + EmitToStreamer(*OutStreamer, GetRegionPtr); + EmitToStreamer(*OutStreamer, GetAllocPtr); + EmitToStreamer(*OutStreamer, GetLimit); + EmitToStreamer(*OutStreamer, CalNewAllocPtr); + EmitToStreamer(*OutStreamer, CmpNewAllocPtrWithLimit); + EmitToStreamer(*OutStreamer, BranchGToLSLow); + EmitToStreamer(*OutStreamer, SetKlass); + EmitToStreamer(*OutStreamer, StoreNewAllocPtr); + EmitToStreamer(*OutStreamer, CopyAllocPtrToX0); + if (Param.FastFuncName.equals("CJ_MCC_NewFinalizerFast")) { + MCOperand FinalizerMC = setGAAndLower( + Param.MOSym, Param.MI->getMF()->getFunction().getParent()->getFunction( + "CJ_MCC_OnFinalizerCreated")); + EmitToStreamer(*OutStreamer, + MCInstBuilder(Param.Opcode).addOperand(FinalizerMC)); + } + EmitToStreamer(*OutStreamer, BranchToFin); + OutStreamer->emitLabel(LSlow); + EmitToStreamer(*OutStreamer, Param.CallNewObjSlowPath); + OutStreamer->emitLabel(Param.LFin); +} + +void AArch64AsmPrinter::emitMccNewObjectFastPath(const MachineInstr *MI, + const MachineOperand &MOSym, + unsigned Opcode) { + StringRef FastFuncName = + (MOSym.getGlobal()->getName().find("Object") != StringRef::npos) + ? "CJ_MCC_NewObjectFast" + : "CJ_MCC_NewFinalizerFast"; + + MCSymbol *LFinish = OutContext.createTempSymbol("NewObjFin", true); + const MCSymbolRefExpr *FinExpr = MCSymbolRefExpr::create(LFinish, OutContext); + MCOperand CallSlowPath; + MCInstLowering.lowerOperand(MOSym, CallSlowPath); + MCInst CallNewObjSlowPath = MCInstBuilder(Opcode).addOperand(CallSlowPath); + ParamForEmitNewObj Param(MI, MOSym, Opcode, LFinish, FinExpr, + CallNewObjSlowPath, FastFuncName); + emitMccNewObjectForCopyGC(Param); + SM.recordCJStackMap(*MI); +} + +void AArch64AsmPrinter::emitCJThrowException(const MachineInstr *MI, + const MachineOperand &MOSym, + unsigned Opcode) { + MCOperand Call; + MCInstLowering.lowerOperand(MOSym, Call); + MCInst CallThrowException = MCInstBuilder(Opcode).addOperand(Call); + EmitToStreamer(*OutStreamer, CallThrowException); + StackMaps::CallsiteInfo CSInfo; + SM.updateOrInsertFnInfo(CurrentFnSym, CSInfo); +} + +// Note: emit specific inst should update inst size info in +// AArch64InstrInfo::getInstSizeInBytes for AArch64 at the same time +// ldr x4, [x28, #AllocBufferOffset] // get Alloc Buffer +// ldr x4, [x4] // get region ptr +// ldr x5, [x4] // x5 = allocPtr +// ldr x6, [x4, #8] // x6 = limit +// add x7, x5, x2 // x7 = allocPtr + size +// cmp x7, x6 // allocPtr + size > limit ? +// b.gt .LNewArraySlowPath +// str x0, [x5] // allocPtr->KlassInfo = Klass +// str x1, [x5, #8] // allocPtr->Length = ArrayLength +// str x7, [x4] // region->allocPtr = allocPtr + size +// mov x0, x5 // ret/arg = allocPtr +// b .LNewArrayFin +// .LNewArraySlowPath +// bl MCC_NewArray +// .LNewArrayFin +void AArch64AsmPrinter::emitCJNewArrayFastPath(const MachineInstr &MI, + const MachineOperand &MOSym) { + if (MI.getOpcode() != TargetOpcode::STATEPOINT) { + report_fatal_error("New Obj Must be in Statepoint"); + } + using namespace AArch64; + + MCSymbol *LFast = OutContext.createTempSymbol("NewArrayFastPath", true); + MCSymbol *LSlow = OutContext.createTempSymbol("NewArraySlowPath", true); + const MCSymbolRefExpr *SlowExpr = MCSymbolRefExpr::create(LSlow, OutContext); + + // 8: each imm represents 8 In MCInst. + MCInst GetAllocBuffer = MCInstBuilder(LDRXui).addReg(X4).addReg(X28).addImm( + getAllocBufferOffsetInCJTLS() / 8); + // 0: offset of region Ptr in AllocBuffer is 0 bytes + MCInst GetRegionPtr = MCInstBuilder(LDRXui).addReg(X4).addReg(X4).addImm(0); + MCInst GetAllocPtr = MCInstBuilder(LDRXui).addReg(X5).addReg(X4).addImm(0); + // 1: offset of limit in region is 8 bytes + MCInst GetLimit = MCInstBuilder(LDRXui).addReg(X6).addReg(X4).addImm(1); + MCInst CalNewAllocPtr = + MCInstBuilder(ADDXrs).addReg(X7).addReg(X5).addReg(X2).addImm(0); + MCInst CmpNewAllocPtrWithLimit = + MCInstBuilder(SUBSXrs).addReg(XZR).addReg(X7).addReg(X6).addImm(0); + MCInst BranchGToLSLow = + MCInstBuilder(Bcc).addImm(AArch64CC::GT).addExpr(SlowExpr); + MCInst SetKlass = MCInstBuilder(STRXui).addReg(X0).addReg(X5).addImm(0); + MCInst StoreArrayLength = + MCInstBuilder(STRXui).addReg(X1).addReg(X5).addImm(1); + MCInst StoreNewAllocPtr = + MCInstBuilder(STRXui).addReg(X7).addReg(X4).addImm(0); + MCInst CopyAllocPtrToX0 = + MCInstBuilder(ORRXrs).addReg(X0).addReg(XZR).addReg(X5).addImm(0); + MCSymbol *LFinish = OutContext.createTempSymbol("NewArrayFin", true); + const MCSymbolRefExpr *FinExpr = MCSymbolRefExpr::create(LFinish, OutContext); + MCInst BranchToFin = MCInstBuilder(B).addExpr(FinExpr); + MCOperand CallSlowPath; + MCInstLowering.lowerOperand(MOSym, CallSlowPath); + MCInst CallNewArraySlowPath = MCInstBuilder(BL).addOperand(CallSlowPath); + OutStreamer->emitLabel(LFast); + EmitToStreamer(*OutStreamer, GetAllocBuffer); + EmitToStreamer(*OutStreamer, GetRegionPtr); + EmitToStreamer(*OutStreamer, GetAllocPtr); + EmitToStreamer(*OutStreamer, GetLimit); + EmitToStreamer(*OutStreamer, CalNewAllocPtr); + EmitToStreamer(*OutStreamer, CmpNewAllocPtrWithLimit); + EmitToStreamer(*OutStreamer, BranchGToLSLow); + EmitToStreamer(*OutStreamer, SetKlass); + EmitToStreamer(*OutStreamer, StoreArrayLength); + EmitToStreamer(*OutStreamer, StoreNewAllocPtr); + EmitToStreamer(*OutStreamer, CopyAllocPtrToX0); + EmitToStreamer(*OutStreamer, BranchToFin); + OutStreamer->emitLabel(LSlow); + EmitToStreamer(*OutStreamer, CallNewArraySlowPath); + OutStreamer->emitLabel(LFinish); + SM.recordCJStackMap(MI); +} + +void AArch64AsmPrinter::emitGetCJThreadId() { + int64_t cjthreadIdOffset = 456; + // 8: each imm represents 8 In MCInst. + int64_t OffsetDivisor = 8; + auto &Ctx = OutStreamer->getContext(); + MCSymbol *MILabel = Ctx.createTempSymbol("cjthread_id_end", true); + const MCSymbolRefExpr *MILabelExpr = MCSymbolRefExpr::create(MILabel, OutContext); + // ldr x9, [x28, #16] + emitGetCJTLSData(getCJThreadOffsetInCJTLS()); + // cbz x9, .L_cjthread_id_end + MCInst CbzInst; + CbzInst.setOpcode(AArch64::CBZW); + CbzInst.addOperand(MCOperand::createReg(AArch64::X9)); + CbzInst.addOperand(MCOperand::createExpr(MILabelExpr)); + OutStreamer->emitInstruction(CbzInst, getSubtargetInfo()); + // ldr x9, [x9, #456] + MCInst LoadCJThreadInst; + LoadCJThreadInst.setOpcode(AArch64::LDRXui); + LoadCJThreadInst.addOperand(MCOperand::createReg(AArch64::X9)); + LoadCJThreadInst.addOperand(MCOperand::createReg(AArch64::X9)); + LoadCJThreadInst.addOperand(MCOperand::createImm(cjthreadIdOffset / OffsetDivisor)); + LoadCJThreadInst.addOperand(MCOperand::createImm(0)); + EmitToStreamer(*OutStreamer, LoadCJThreadInst); + // .L_cjthread_id_end + OutStreamer->emitLabel(MILabel); + return; +} + +void AArch64AsmPrinter::emitMetadataAddress() { + const Triple &TT = getSubtargetInfo().getTargetTriple(); + MCSymbol *MetadataStart = OutContext.getOrCreateSymbol("__CJMetadataStart"); + MCContext &Ctx = MF->getContext(); + if (TT.isOSLinux()) { + // adrp x0, __CJMetadataStart + // add x0, x0, :lo12:__CJMetadataStart + // bl CJ_MRT_PreInitializePackage + MachineOperand MOsym( + MachineOperand::CreateMCSymbol(MetadataStart, AArch64II::MO_PAGE)); + MCOperand SymOriAddr; + MCInstLowering.lowerOperand(MOsym, SymOriAddr); + MachineOperand MOsymLo12(MachineOperand::CreateMCSymbol( + MetadataStart, AArch64II::MO_PAGEOFF | AArch64II::MO_NC)); + MCOperand SymOriAddrLo12; + MCInstLowering.lowerOperand(MOsymLo12, SymOriAddrLo12); + MCInst Adrp = + MCInstBuilder(AArch64::ADRP).addReg(AArch64::X0).addOperand(SymOriAddr); + EmitToStreamer(*OutStreamer, Adrp); + MCInst Add = MCInstBuilder(AArch64::ADDXri) + .addReg(AArch64::X0) + .addReg(AArch64::X0) + .addOperand(SymOriAddrLo12) + .addImm(0); + EmitToStreamer(*OutStreamer, Add); + MCSymbol *FuncSym = + OutContext.getOrCreateSymbol("CJ_MRT_PreInitializePackage"); + MCInst BLToCall = MCInstBuilder(AArch64::BL) + .addExpr(MCSymbolRefExpr::create(FuncSym, Ctx)); + EmitToStreamer(*OutStreamer, BLToCall); + } else if (TT.isOSBinFormatMachO()) { + // adrp x0, __CJMetadataStart@GOTPAGE + // ldr x0, [x0, __CJMetadataStart@GOTPAGEOFF] + // ldr x0, [x0] + // bl CJ_MRT_PreInitializePackage + MachineOperand MOsym(MachineOperand::CreateMCSymbol( + MetadataStart, AArch64II::MO_PAGE | AArch64II::MO_GOT)); + MCOperand SymOriAddr; + MCInstLowering.lowerOperand(MOsym, SymOriAddr); + MachineOperand MOsymLo12(MachineOperand::CreateMCSymbol( + MetadataStart, + AArch64II::MO_PAGEOFF | AArch64II::MO_NC | AArch64II::MO_GOT)); + MCOperand SymOriAddrLo12; + MCInstLowering.lowerOperand(MOsymLo12, SymOriAddrLo12); + + MCInst Adrp = + MCInstBuilder(AArch64::ADRP).addReg(AArch64::X0).addOperand(SymOriAddr); + EmitToStreamer(*OutStreamer, Adrp); + MCInst Ldr = MCInstBuilder(AArch64::LDRXui) + .addReg(AArch64::X0) + .addReg(AArch64::X0) + .addOperand(SymOriAddrLo12) + .addImm(0); + EmitToStreamer(*OutStreamer, Ldr); + MCInst Ldr2 = MCInstBuilder(AArch64::LDRXui) + .addReg(AArch64::X0) + .addReg(AArch64::X0) + .addImm(0); + EmitToStreamer(*OutStreamer, Ldr2); + MCSymbol *FuncSym = + OutContext.getOrCreateSymbol("_CJ_MRT_PreInitializePackage"); + MCInst BLToCall = MCInstBuilder(AArch64::BL) + .addExpr(MCSymbolRefExpr::create(FuncSym, Ctx)); + EmitToStreamer(*OutStreamer, BLToCall); + } +} // Force static initialization. extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeAArch64AsmPrinter() { diff --git a/llvm/lib/Target/AArch64/AArch64CallingConvention.h b/llvm/lib/Target/AArch64/AArch64CallingConvention.h index 59939e068..b539a3ef1 100644 --- a/llvm/lib/Target/AArch64/AArch64CallingConvention.h +++ b/llvm/lib/Target/AArch64/AArch64CallingConvention.h @@ -46,6 +46,10 @@ bool RetCC_AArch64_AAPCS(unsigned ValNo, MVT ValVT, MVT LocVT, bool RetCC_AArch64_WebKit_JS(unsigned ValNo, MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, CCState &State); +bool RetCC_AArch64_Cangjie_GCCheck(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, + ISD::ArgFlagsTy ArgFlags, CCState &State); + } // namespace llvm #endif diff --git a/llvm/lib/Target/AArch64/AArch64CallingConvention.td b/llvm/lib/Target/AArch64/AArch64CallingConvention.td index c0da242a2..31a91d44d 100644 --- a/llvm/lib/Target/AArch64/AArch64CallingConvention.td +++ b/llvm/lib/Target/AArch64/AArch64CallingConvention.td @@ -304,6 +304,12 @@ def RetCC_AArch64_WebKit_JS : CallingConv<[ CCIfType<[f64], CCAssignToReg<[D0, D1, D2, D3, D4, D5, D6, D7]>> ]>; +let Entry = 1 in +def RetCC_AArch64_Cangjie_GCCheck : CallingConv<[ + CCIfType<[i32], CCAssignToReg<[W9]>>, + CCIfType<[i64], CCAssignToReg<[X9]>> +]>; + //===----------------------------------------------------------------------===// // ARM64 Calling Convention for GHC //===----------------------------------------------------------------------===// @@ -360,9 +366,10 @@ def CC_AArch64_GHC : CallingConv<[ // vreg on entry, use it in RET & tail call generation; make that vreg def if we // end up saving LR as part of a call frame). Watch this space... def CSR_AArch64_AAPCS : CalleeSavedRegs<(add X19, X20, X21, X22, X23, X24, - X25, X26, X27, X28, LR, FP, + X25, X26, X27, X28, D8, D9, D10, D11, - D12, D13, D14, D15)>; + D12, D13, D14, D15, LR, FP)>; + // A variant for treating X18 as callee saved, when interfacing with // code that needs X18 to be preserved. @@ -424,6 +431,11 @@ def CSR_AArch64_AllRegs (sequence "S%u", 0, 31), (sequence "D%u", 0, 31), (sequence "Q%u", 0, 31))>; +def CSR_AArch64_CangjieGC + : CalleeSavedRegs<(add (sequence "X%u", 0, 8), + (sequence "X%u", 10, 28), FP, LR, SP, + (sequence "Q%u", 0, 31))>; + def CSR_AArch64_NoRegs : CalleeSavedRegs<(add)>; def CSR_AArch64_RT_MostRegs : CalleeSavedRegs<(add CSR_AArch64_AAPCS, diff --git a/llvm/lib/Target/AArch64/AArch64FastISel.cpp b/llvm/lib/Target/AArch64/AArch64FastISel.cpp index 49fffa01a..b8fb1753c 100644 --- a/llvm/lib/Target/AArch64/AArch64FastISel.cpp +++ b/llvm/lib/Target/AArch64/AArch64FastISel.cpp @@ -340,6 +340,8 @@ CCAssignFn *AArch64FastISel::CCAssignFnForCall(CallingConv::ID CC) const { return CC_AArch64_GHC; if (CC == CallingConv::CFGuard_Check) return CC_AArch64_Win64_CFGuard_Check; + if (CC == CallingConv::CangjieGC) + return RetCC_AArch64_Cangjie_GCCheck; return Subtarget->isTargetDarwin() ? CC_AArch64_DarwinPCS : CC_AArch64_AAPCS; } @@ -2998,6 +3000,11 @@ bool AArch64FastISel::processCallArgs(CallLoweringInfo &CLI, // Get a count of how many bytes are to be pushed on the stack. NumBytes = CCInfo.getNextStackOffset(); + // Cangjie need to add frame info for CFFI stub. + if (CLI.Callee) { + CCInfo.AddAlignedCallFrameSizeMetaDataForCJFFI( + const_cast(dyn_cast(CLI.Callee)), NumBytes); + } // Issue CALLSEQ_START unsigned AdjStackDown = TII.getCallFrameSetupOpcode(); @@ -3787,7 +3794,9 @@ bool AArch64FastISel::selectRet(const Instruction *I) { SmallVector ValLocs; CCState CCInfo(CC, F.isVarArg(), *FuncInfo.MF, ValLocs, I->getContext()); CCAssignFn *RetCC = CC == CallingConv::WebKit_JS ? RetCC_AArch64_WebKit_JS - : RetCC_AArch64_AAPCS; + : (CC == CallingConv::CangjieGC + ? RetCC_AArch64_Cangjie_GCCheck + : RetCC_AArch64_AAPCS); CCInfo.AnalyzeReturn(Outs, RetCC); // Only handle a single return value for now. diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp index 78babdf9f..3d08bd285 100644 --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp @@ -191,6 +191,7 @@ #include "AArch64RegisterInfo.h" #include "AArch64Subtarget.h" #include "AArch64TargetMachine.h" +#include "CangjieDemangle.h" #include "MCTargetDesc/AArch64AddressingModes.h" #include "MCTargetDesc/AArch64MCTargetDesc.h" #include "llvm/ADT/ScopeExit.h" @@ -215,7 +216,6 @@ #include "llvm/IR/CallingConv.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugLoc.h" -#include "llvm/IR/Function.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCDwarf.h" #include "llvm/Support/CommandLine.h" @@ -256,9 +256,51 @@ cl::opt EnableHomogeneousPrologEpilog( "homogeneous-prolog-epilog", cl::Hidden, cl::desc("Emit homogeneous prologue and epilogue for the size " "optimization (default = off)")); - +namespace llvm { +extern cl::opt CJPipeline; +} STATISTIC(NumRedZoneFunctions, "Number of functions using red zone"); +static bool isCJMachO(MachineFunction &MF) { + return (MF.getFunction().hasCangjieGC() && + MF.getTarget().getTargetTriple().isOSBinFormatMachO()); +} + +static void storeFunctionAddress(MachineFunction &MF, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + const DebugLoc &DL, + const TargetInstrInfo *TII) { + if (isCJMachO(MF)) { + // Use X9 store func_begin + BuildMI(MBB, MBBI, DL, TII->get(AArch64::ADR), AArch64::X9) + .addGlobalAddress(&MF.getFunction()) + .setMIFlag(MachineInstr::FrameSetup); + // Use X10 store .Lmethod_desc.xxx + BuildMI(MBB, MBBI, DL, TII->get(AArch64::ADRP), AArch64::X10) + .addGlobalAddress(&MF.getFunction()) + .setMIFlag(MachineInstr::FrameSetup); + // -2: X9 is in -2 * 8 offsets of X29. + BuildMI(MBB, MBBI, DL, TII->get(AArch64::STPXi)) + .addReg(AArch64::X10) + .addReg(AArch64::X9) + .addReg(AArch64::FP) + .addImm(-2) + .setMIFlag(MachineInstr::FrameSetup); + return; + } + // Use X9 store func_begin + BuildMI(MBB, MBBI, DL, TII->get(AArch64::ADR), AArch64::X9) // reg2 fp + .addGlobalAddress(&MF.getFunction()) + .setMIFlag(MachineInstr::FrameSetup); + // frame object has saved. + BuildMI(MBB, MBBI, DL, TII->get(AArch64::STURXi)) + .addReg(AArch64::X9) + .addReg(AArch64::FP) + .addImm(-8) + .setMIFlag(MachineInstr::FrameSetup); +} + /// Returns how much of the incoming argument stack area (in bytes) we should /// clean up in an epilogue. For the C calling convention this will be 0, for /// guaranteed tail call conventions it can be positive (a normal return or a @@ -1239,6 +1281,13 @@ static void fixupCalleeSaveRestoreStackOffset(MachineInstr &MI, return; unsigned Opc = MI.getOpcode(); + // Ignore instructions that do not operate on SP, i.e. shadow call stack + // instructions and associated CFI instruction. + if (Opc == AArch64::ADR || Opc == AArch64::STURXi) { + assert(MI.getOperand(0).getReg() != AArch64::SP); + return; + } + unsigned Scale; switch (Opc) { case AArch64::STPXi: @@ -1372,6 +1421,16 @@ static void emitShadowCallStackEpilogue(const TargetInstrInfo &TII, } } +const std::vector +AArch64FrameLowering::getArgRegs(const MachineFunction &MF) const { + return { AArch64::X0, AArch64::X1, AArch64::X2, AArch64::X3, AArch64::X4, + AArch64::X5, AArch64::X6, AArch64::X7 }; +} + +MCPhysReg AArch64FrameLowering::getX8Register() const { + return AArch64::X8; +} + void AArch64FrameLowering::emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const { MachineBasicBlock::iterator MBBI = MBB.begin(); @@ -1490,6 +1549,20 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF, // function, including the funclet. int64_t NumBytes = IsFunclet ? getWinEHFuncletFrameSize(MF) : MFI.getStackSize(); + + assert(NumBytes >= 0 && "NumBytes less than 0 in aarch64!"); + // 2147483648: 2*1024*1024*1024, 2GB + if (CJPipeline && (uint64_t)NumBytes >= 2147483648) { + auto D = Cangjie::Demangle(MF.getName().str()); + std::string DemangledName = D.GetPkgName() + + std::string(D.GetPkgName().empty() ? "" : "::") + + D.GetFullName(); + report_fatal_error("The stacksize of " + Twine(DemangledName) + + " exceeds cangjie max stacksize(2GB).\nCompilation " + "stopped! Please check the implementation of " + + Twine(DemangledName) + "!", false); + } + if (!AFI->hasStackFrame() && !windowsRequiresStackProbe(MF, NumBytes)) { assert(!HasFP && "unexpected function without stack frame but with FP"); assert(!SVEStackSize && @@ -1567,14 +1640,14 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF, ++MBBI; } - // For funclets the FP belongs to the containing function. - if (!IsFunclet && HasFP) { - // Only set up FP if we actually need to. - int64_t FPOffset = AFI->getCalleeSaveBaseToFrameRecordOffset(); + // Only set up FP if we actually need to. + int64_t FPOffset = AFI->getCalleeSaveBaseToFrameRecordOffset(); - if (CombineSPBump) - FPOffset += AFI->getLocalStackSize(); + if (CombineSPBump) + FPOffset += AFI->getLocalStackSize(); + // For funclets the FP belongs to the containing function. + if (!IsFunclet && HasFP) { if (AFI->hasSwiftAsyncContext()) { // Before we update the live FP we have to ensure there's a valid (or // null) asynchronous context in its slot just before FP in the frame @@ -1603,6 +1676,9 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF, emitFrameOffset(MBB, MBBI, DL, AArch64::FP, AArch64::SP, StackOffset::getFixed(FPOffset), TII, MachineInstr::FrameSetup, false, NeedsWinCFI, &HasWinCFI); + // store func address to stack slot. + if (MF.getFunction().hasCangjieGC()) + storeFunctionAddress(MF, MBB, MBBI, DL, TII); } if (EmitCFI) { // Define the current CFA rule to use the provided FP. @@ -1618,6 +1694,11 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF, .setMIFlags(MachineInstr::FrameSetup); } } + // Prepare for StackCheck + // 16: stur x9, [x29, #-8]. Need x9, and align16(8) + if (NumBytes == 0) { + preCJStackCheck(MF, MBBI, FPOffset - 16); + } // Now emit the moves for whatever callee saved regs we have (including FP, // LR if those are saved). Frame instructions for SVE register are emitted @@ -1777,6 +1858,19 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF, SVEStackSize + StackOffset::getFixed((int64_t)MFI.getStackSize() - NumBytes)); } + // Prepare for StackCheck + // 16: stur x9, [x29, #-8]. Need x9, and align16(8) + if (isCJMachO(MF)) { + // For macos, x9 and callee saved registers have been stored to stack now, + // and callee saved registers is after x9, so NumBytes need add the size + // of callee saved registers. + preCJStackCheck(MF, MBBI, NumBytes + FPOffset - 16); + } else { + // For others, x9 has not been stored to stack now, so reserve 16 bytes + // for it. + preCJStackCheck(MF, MBBI, NumBytes - 16); + } + if (NeedsRealignment) { const unsigned NrBitsToZero = Log2(MFI.getMaxAlign()); assert(NrBitsToZero > 1); @@ -2224,7 +2318,9 @@ AArch64FrameLowering::getFrameIndexReference(const MachineFunction &MF, int FI, return resolveFrameIndexReference( MF, FI, FrameReg, /*PreferFP=*/ - MF.getFunction().hasFnAttribute(Attribute::SanitizeHWAddress), + CJPipeline + ? true + : MF.getFunction().hasFnAttribute(Attribute::SanitizeHWAddress), /*ForSimm=*/false); } @@ -2636,6 +2732,13 @@ static void computeCalleeSaveRegisterPairs( RPI.Reg2 == AArch64::FP) ByteOffset += StackFillDir * 8; + if (NeedsFrameRecord && isCJMachO(MF) && RPI.Reg2 == AArch64::FP) { + // 16: size of CJ method PC info and Lmethod_desc.xxx. + // Cangjie's CJ method PC info and Lmethod_desc.xxx is before callee saved + // registers in macos of Mx, so allocate an extra 16 bytes for it. + ByteOffset -= 16; + } + assert(!(RPI.isScalable() && RPI.isPaired()) && "Paired spill/fill instructions don't exist for SVE vectors"); @@ -2664,6 +2767,12 @@ static void computeCalleeSaveRegisterPairs( if (NeedsFrameRecord && AFI->hasSwiftAsyncContext() && RPI.Reg2 == AArch64::FP) Offset += 8; + + if (NeedsFrameRecord && isCJMachO(MF) && RPI.Reg2 == AArch64::FP) { + // 16: size of CJ method PC info and Lmethod_desc.xxx. + Offset += 16; + } + RPI.Offset = Offset / Scale; assert(((!RPI.isScalable() && RPI.Offset >= -64 && RPI.Offset <= 63) || @@ -3102,6 +3211,10 @@ void AArch64FrameLowering::determineCalleeSaves(MachineFunction &MF, if (hasFP(MF) && AFI->hasSwiftAsyncContext()) CSStackSize += 8; + if (hasFP(MF) && isCJMachO(MF)) + // 16: size of CJ method PC info and Lmethod_desc.xxx. + CSStackSize += 16; + uint64_t AlignedCSStackSize = alignTo(CSStackSize, 16); LLVM_DEBUG(dbgs() << "Estimated stack frame size: " << EstimatedStackSize + AlignedCSStackSize @@ -3134,6 +3247,14 @@ bool AArch64FrameLowering::assignCalleeSavedSpillSlots( if (CSI.empty()) return true; // Early exit if no callee saved registers are modified! + auto UpdateFrameIdxBoundary = [&](unsigned NewValue) { + if (NewValue < MinCSFrameIndex) { + MinCSFrameIndex = NewValue; + } + if (NewValue > MaxCSFrameIndex) { + MaxCSFrameIndex = NewValue; + } + }; // Now that we know which registers need to be saved and restored, allocate // stack slots for them. MachineFrameInfo &MFI = MF.getFrameInfo(); @@ -3143,8 +3264,7 @@ bool AArch64FrameLowering::assignCalleeSavedSpillSlots( if (UsesWinAAPCS && hasFP(MF) && AFI->hasSwiftAsyncContext()) { int FrameIdx = MFI.CreateStackObject(8, Align(16), true); AFI->setSwiftAsyncContextFrameIdx(FrameIdx); - if ((unsigned)FrameIdx < MinCSFrameIndex) MinCSFrameIndex = FrameIdx; - if ((unsigned)FrameIdx > MaxCSFrameIndex) MaxCSFrameIndex = FrameIdx; + UpdateFrameIdxBoundary((unsigned)FrameIdx); } for (auto &CS : CSI) { @@ -3155,17 +3275,33 @@ bool AArch64FrameLowering::assignCalleeSavedSpillSlots( Align Alignment(RegInfo->getSpillAlign(*RC)); int FrameIdx = MFI.CreateStackObject(Size, Alignment, true); CS.setFrameIdx(FrameIdx); - - if ((unsigned)FrameIdx < MinCSFrameIndex) MinCSFrameIndex = FrameIdx; - if ((unsigned)FrameIdx > MaxCSFrameIndex) MaxCSFrameIndex = FrameIdx; + UpdateFrameIdxBoundary((unsigned)FrameIdx); + if (Reg == AArch64::FP && hasFP(MF) && MF.getFunction().hasCangjieGC()) { + bool IsCJMachO = isCJMachO(MF); + // 8: size of CJ method PC info + uint64_t CJSize = 8; + if (IsCJMachO) { + // 16: size of CJ method PC info and Lmethod_desc.xxx + CJSize = 16; + } + // For mac, store CJ method PC info and Lmethod_desc.xxx + // For others, store CJ method PC info + // CreateStackObject returning a nonnegative identifier + // to represent a new statically sized stack object + auto CJIdx = static_cast( + MFI.CreateStackObject(CJSize /*size*/, Align(8) /*align*/, true)); + if (IsCJMachO) { + AFI->setCJMachOPCContextFrameIdx(CJIdx); + } + UpdateFrameIdxBoundary(CJIdx); + } // Grab 8 bytes below FP for the extended asynchronous frame info. if (hasFP(MF) && AFI->hasSwiftAsyncContext() && !UsesWinAAPCS && Reg == AArch64::FP) { FrameIdx = MFI.CreateStackObject(8, Alignment, true); AFI->setSwiftAsyncContextFrameIdx(FrameIdx); - if ((unsigned)FrameIdx < MinCSFrameIndex) MinCSFrameIndex = FrameIdx; - if ((unsigned)FrameIdx > MaxCSFrameIndex) MaxCSFrameIndex = FrameIdx; + UpdateFrameIdxBoundary((unsigned)FrameIdx); } } return true; diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.h b/llvm/lib/Target/AArch64/AArch64FrameLowering.h index f59860a24..62de8bad7 100644 --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.h +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.h @@ -35,6 +35,11 @@ public: eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const override; + const std::vector + getArgRegs(const MachineFunction &MF) const override; + + MCPhysReg getX8Register() const override; + /// emitProlog/emitEpilog - These methods insert prolog and epilog code into /// the function. void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override; diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index 06e21f90e..fafcd5c65 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -706,6 +706,8 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM, setOperationAction(ISD::PREFETCH, MVT::Other, Custom); + setOperationAction(ISD::GET_FP_STATE, MVT::i64, Custom); + setOperationAction(ISD::RESET_FP_STATE, MVT::Other, Custom); setOperationAction(ISD::FLT_ROUNDS_, MVT::i32, Custom); setOperationAction(ISD::SET_ROUNDING, MVT::Other, Custom); @@ -4269,6 +4271,26 @@ static bool isAddSubZExt(SDNode *N, SelectionDAG &DAG) { return false; } +SDValue AArch64TargetLowering::LowerGET_FP_STATE(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + SDValue Chain = Op.getOperand(1); // chain to fptosi + + return DAG.getNode( + ISD::INTRINSIC_W_CHAIN, DL, {MVT::i64, MVT::Other}, + {Chain, DAG.getConstant(Intrinsic::aarch64_get_fpsr, DL, MVT::i64)}); +} + +SDValue AArch64TargetLowering::LowerRESET_FP_STATE(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + SDValue Chain = Op->getOperand(0); + + return DAG.getNode( + ISD::INTRINSIC_VOID, DL, MVT::Other, + {Chain, DAG.getConstant(Intrinsic::aarch64_reset_fpsr, DL, MVT::i64)}); +} + SDValue AArch64TargetLowering::LowerFLT_ROUNDS_(SDValue Op, SelectionDAG &DAG) const { // The rounding mode is in bits 23:22 of the FPSCR. @@ -5507,6 +5529,10 @@ SDValue AArch64TargetLowering::LowerOperation(SDValue Op, return LowerFP_TO_INT_SAT(Op, DAG); case ISD::FSINCOS: return LowerFSINCOS(Op, DAG); + case ISD::GET_FP_STATE: + return LowerGET_FP_STATE(Op, DAG); + case ISD::RESET_FP_STATE: + return LowerRESET_FP_STATE(Op, DAG); case ISD::FLT_ROUNDS_: return LowerFLT_ROUNDS_(Op, DAG); case ISD::SET_ROUNDING: @@ -5719,6 +5745,7 @@ CCAssignFn *AArch64TargetLowering::CCAssignFnForCall(CallingConv::ID CC, return CC_AArch64_WebKit_JS; case CallingConv::GHC: return CC_AArch64_GHC; + case CallingConv::AnyReg: case CallingConv::C: case CallingConv::Fast: case CallingConv::PreserveMost: @@ -5746,8 +5773,10 @@ CCAssignFn *AArch64TargetLowering::CCAssignFnForCall(CallingConv::ID CC, CCAssignFn * AArch64TargetLowering::CCAssignFnForReturn(CallingConv::ID CC) const { - return CC == CallingConv::WebKit_JS ? RetCC_AArch64_WebKit_JS - : RetCC_AArch64_AAPCS; + return CC == CallingConv::WebKit_JS + ? RetCC_AArch64_WebKit_JS + : CC == CallingConv::CangjieGC ? RetCC_AArch64_Cangjie_GCCheck + : RetCC_AArch64_AAPCS; } SDValue AArch64TargetLowering::LowerFormalArguments( @@ -6557,6 +6586,7 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI, // Get a count of how many bytes are to be pushed on the stack. unsigned NumBytes = CCInfo.getNextStackOffset(); + unsigned CallFrameSizeForCJFFI = NumBytes; if (IsSibCall) { // Since we're not changing the ABI to make this a tail call, the memory @@ -6577,6 +6607,7 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI, // Since callee will pop argument stack as a tail call, we must keep the // popped size 16-byte aligned. NumBytes = alignTo(NumBytes, 16); + CallFrameSizeForCJFFI = NumBytes; // FPDiff will be negative if this tail call requires more space than we // would automatically have in our incoming argument space. Positive if we @@ -6841,6 +6872,9 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI, // direct call is) turn it into a TargetGlobalAddress/TargetExternalSymbol // node so that legalize doesn't hack it. if (auto *G = dyn_cast(Callee)) { + auto *CalleeFunc = dyn_cast(G->getGlobal()); + CCInfo.AddAlignedCallFrameSizeMetaDataForCJFFI( + const_cast(CalleeFunc), CallFrameSizeForCJFFI); auto GV = G->getGlobal(); unsigned OpFlags = Subtarget->classifyGlobalFunctionReference(GV, getTargetMachine()); @@ -8700,7 +8734,11 @@ SDValue AArch64TargetLowering::LowerSPONENTRY(SDValue Op, // this table could be generated automatically from RegInfo. Register AArch64TargetLowering:: getRegisterByName(const char* RegName, LLT VT, const MachineFunction &MF) const { - Register Reg = MatchRegisterName(RegName); + StringRef NameStr(RegName); + if (NameStr == "LR") { + return AArch64::LR; + } + Register Reg = MatchRegisterName(NameStr); if (AArch64::X1 <= Reg && Reg <= AArch64::X28) { const MCRegisterInfo *MRI = Subtarget->getRegisterInfo(); unsigned DwarfRegNum = MRI->getDwarfRegNum(Reg, false); @@ -8709,8 +8747,7 @@ getRegisterByName(const char* RegName, LLT VT, const MachineFunction &MF) const } if (Reg) return Reg; - report_fatal_error(Twine("Invalid register name \"" - + StringRef(RegName) + "\".")); + report_fatal_error(Twine("Invalid register name \"" + NameStr + "\".")); } SDValue AArch64TargetLowering::LowerADDROFRETURNADDR(SDValue Op, @@ -13636,6 +13673,10 @@ bool AArch64TargetLowering::generateFMAsInMachineCombiner( !useSVEForFixedLengthVectorVT(VT); } +bool AArch64TargetLowering::isCangjieGetFPStateInstr(unsigned Opcode) const { + return Opcode == AArch64::MRS; +} + const MCPhysReg * AArch64TargetLowering::getScratchRegisters(CallingConv::ID) const { // LR is a callee-save register, but we must treat it as clobbered by any call diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h index ff3bfe897..3276e7f4b 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h @@ -651,6 +651,8 @@ public: bool generateFMAsInMachineCombiner(EVT VT, CodeGenOpt::Level OptLevel) const override; + bool isCangjieGetFPStateInstr(unsigned Opcode) const override; + const MCPhysReg *getScratchRegisters(CallingConv::ID CC) const override; /// Returns false if N is a bit extraction pattern of (X >> C) & Mask. @@ -982,6 +984,8 @@ private: SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; SDValue LowerSPONENTRY(SDValue Op, SelectionDAG &DAG) const; SDValue LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerGET_FP_STATE(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerRESET_FP_STATE(SDValue Op, SelectionDAG &DAG) const; SDValue LowerFLT_ROUNDS_(SDValue Op, SelectionDAG &DAG) const; SDValue LowerSET_ROUNDING(SDValue Op, SelectionDAG &DAG) const; SDValue LowerINSERT_VECTOR_ELT(SDValue Op, SelectionDAG &DAG) const; diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp index 835a7b6cc..72ecd9223 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -33,6 +33,7 @@ #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DebugLoc.h" #include "llvm/IR/GlobalValue.h" +#include "llvm/IR/Statepoint.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstBuilder.h" @@ -73,6 +74,59 @@ AArch64InstrInfo::AArch64InstrInfo(const AArch64Subtarget &STI) AArch64::CATCHRET), RI(STI.getTargetTriple()), Subtarget(STI) {} +/// Return the maximum number of bytes of Cangjie Specific CallInst. +/// 0 means MI is not a Cangjie Specific Call. +unsigned AArch64InstrInfo::getCangjieSpecificCallInstSizeInBytes( + const MachineInstr &MI) const { + if (!MI.isCall()) { + return 0; + } + if (MI.getNumOperands() == 0) { + return 0; + } + const MachineOperand *MOSym = nullptr; + if (MI.getOpcode() == TargetOpcode::STATEPOINT) { + StatepointOpers SO(&MI); + if (SO.getID() == CJStatepointID::NewArrayFast) { + return 52; // 52: 52 bytes(13 insts) for new array call + } + MOSym = &(SO.getCallTarget()); + } else { + MOSym = &(MI.getOperand(0)); + } + if (!MOSym->isGlobal()) { + return 0; + } + const auto *Callee = dyn_cast(MOSym->getGlobal()); + if (Callee == nullptr) { + return 0; + } + if (Callee->isCangjieSafePoint()) { + // If Jmp size beyond the 19bit, emitting safepoint on following inst + // instead of emit it at the end of the function. + // Each Inst size is 4 bytes. + if (MI.getMF()->getInstructionCount() * 4 > 0x7FFFF) { + return 24; // 24: 24 bytes(6 insts) for safepoint call + } else { + return 12; // 12: 12 bytes(3 insts) for stack-check call + } + } + if (Callee->getName().isCJStackCheck()) { + return 12; // 12: 12 bytes(3 insts) for safepoint call + } + if (Callee->getName().isGetGCPhase()) { + return 4; // 4: 4 bytes(1 insts) for GetGCPhase call + } + const auto &CallerFunc = MI.getParent()->getParent()->getFunction(); + if (Callee->isCangjieNativeStub(CallerFunc)) { + return 24; // 24: 24 bytes(6 insts) for Native Stub + } + if (Callee->isGetCJThreadId()) { + return 12; // 12: 12 bytes(3 insts) for mutex opt + } + return 0; +} + /// GetInstSize - Return the number of bytes of code the specified /// instruction may be. This returns the maximum number of bytes. unsigned AArch64InstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { @@ -90,9 +144,13 @@ unsigned AArch64InstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { if (MI.isMetaInstruction()) return 0; + unsigned NumBytes = getCangjieSpecificCallInstSizeInBytes(MI); + if (NumBytes != 0) { + return NumBytes; + } + // FIXME: We currently only handle pseudoinstructions that don't get expanded // before the assembly printer. - unsigned NumBytes = 0; const MCInstrDesc &Desc = MI.getDesc(); // Size should be preferably set in @@ -2098,6 +2156,36 @@ unsigned AArch64InstrInfo::isLoadFromStackSlot(const MachineInstr &MI, return 0; } +unsigned AArch64InstrInfo::isLoadFromStackSlot(const MachineInstr &MI, + int &FrameIndex, + unsigned &MemBytes) const { + unsigned Reg = isLoadFromStackSlot(MI, FrameIndex); + if (Reg) { + switch (MI.getOpcode()) { + default: + break; + case AArch64::LDRWui: + case AArch64::LDRSui: + MemBytes = 4; + break; + case AArch64::LDRXui: + case AArch64::LDRQui: + MemBytes = 8; + break; + case AArch64::LDRBui: + MemBytes = 1; + break; + case AArch64::LDRHui: + MemBytes = 2; + break; + case AArch64::LDRDui: + MemBytes = 16; + break; + } + } + return Reg; +} + unsigned AArch64InstrInfo::isStoreToStackSlot(const MachineInstr &MI, int &FrameIndex) const { switch (MI.getOpcode()) { @@ -2122,6 +2210,46 @@ unsigned AArch64InstrInfo::isStoreToStackSlot(const MachineInstr &MI, return 0; } +unsigned AArch64InstrInfo::isStoreToStackSlot(const MachineInstr &MI, + int &FrameIndex, + unsigned &MemBytes) const { + unsigned Reg = isStoreToStackSlot(MI, FrameIndex); + if (Reg) { + switch (MI.getOpcode()) { + default: + break; + case AArch64::STRWui: + case AArch64::STRSui: + MemBytes = 4; + break; + case AArch64::STRXui: + case AArch64::STRQui: + case AArch64::LDR_PXI: + case AArch64::STR_PXI: + MemBytes = 8; + break; + case AArch64::STRBui: + MemBytes = 1; + break; + case AArch64::STRHui: + MemBytes = 2; + break; + case AArch64::STRDui: + MemBytes = 16; + break; + } + } + return Reg; +} + +bool AArch64InstrInfo::isADDXri(const MachineInstr &MI) const { + return MI.getOpcode() == AArch64::ADDXri; +} + +bool AArch64InstrInfo::isORRXri(const MachineInstr &MI) const { + return MI.getOpcode() == AArch64::ORRXri; +} + /// Check all MachineMemOperands for a hint to suppress pairing. bool AArch64InstrInfo::isLdStPairSuppressed(const MachineInstr &MI) { return llvm::any_of(MI.memoperands(), [](MachineMemOperand *MMO) { diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.h b/llvm/lib/Target/AArch64/AArch64InstrInfo.h index b7a6ac301..9d245ee5d 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.h +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.h @@ -59,8 +59,16 @@ public: unsigned isLoadFromStackSlot(const MachineInstr &MI, int &FrameIndex) const override; + unsigned isLoadFromStackSlot(const MachineInstr &MI, int &FrameIndex, + unsigned &MemBytes) const override; unsigned isStoreToStackSlot(const MachineInstr &MI, int &FrameIndex) const override; + unsigned isStoreToStackSlot(const MachineInstr &MI, int &FrameIndex, + unsigned &MemBytes) const override; + + bool isADDXri(const MachineInstr &MI) const override; + + bool isORRXri(const MachineInstr &MI) const override; /// Does this instruction set its full destination register to zero? static bool isGPRZero(const MachineInstr &MI); @@ -364,6 +372,10 @@ private: /// constructing an outlined call if one exists. Returns 0 otherwise. Register findRegisterToSaveLRTo(outliner::Candidate &C) const; + /// Return the maximum number of bytes of Cangjie Specific CallInst Size. + /// 0 means MI is not a Cangjie Specific Call. + unsigned getCangjieSpecificCallInstSizeInBytes(const MachineInstr &MI) const; + /// Remove a ptest of a predicate-generating operation that already sets, or /// can be made to set, the condition codes in an identical manner bool optimizePTestInstr(MachineInstr *PTest, unsigned MaskReg, diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index 926e7305b..31aa3d539 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -1468,6 +1468,10 @@ def : Pat<(readcyclecounter), (MRS 0xdce8)>; def : Pat<(i64 (int_aarch64_get_fpcr)), (MRS 0xda20)>; def : Pat<(int_aarch64_set_fpcr i64:$val), (MSR 0xda20, GPR64:$val)>; +// FPSR register +def : Pat<(i64 (int_aarch64_get_fpsr)), (MRS 0xda21)>; +def : Pat<(int_aarch64_reset_fpsr), (MSR 0xda21, XZR)>; + // Generic system instructions def SYSxt : SystemXtI<0, "sys">; def SYSLxt : SystemLXtI<1, "sysl">; diff --git a/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h b/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h index f070f989a..09c650d34 100644 --- a/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h +++ b/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h @@ -175,6 +175,9 @@ class AArch64FunctionInfo final : public MachineFunctionInfo { /// The stack slot where the Swift asynchronous context is stored. int SwiftAsyncContextFrameIdx = std::numeric_limits::max(); + /// The stack slot where the Cangjie method PC info is stored in MacOS of Mx. + int CJMachOPCContextFrameIdx = std::numeric_limits::max(); + bool IsMTETagged = false; /// The function has Scalable Vector or Scalable Predicate register argument @@ -287,6 +290,14 @@ public: MaxOffset = std::max(Offset + ObjSize, MaxOffset); } + if (CJMachOPCContextFrameIdx != std::numeric_limits::max()) { + // Update cangjie size for CJ method PC info and Lmethod_desc.xxx + int64_t Offset = MFI.getObjectOffset(getCJMachOPCContextFrameIdx()); + int64_t ObjSize = MFI.getObjectSize(getCJMachOPCContextFrameIdx()); + MinOffset = std::min(Offset, MinOffset); + MaxOffset = std::max(Offset + ObjSize, MaxOffset); + } + unsigned Size = alignTo(MaxOffset - MinOffset, 16); assert((!HasCalleeSavedStackSize || getCalleeSavedStackSize() == Size) && "Invalid size calculated for callee saves"); @@ -431,6 +442,13 @@ public: } int getSwiftAsyncContextFrameIdx() const { return SwiftAsyncContextFrameIdx; } + void setCJMachOPCContextFrameIdx(int FI) { + CJMachOPCContextFrameIdx = FI; + } + int getCJMachOPCContextFrameIdx() const { + return CJMachOPCContextFrameIdx; + } + bool needsDwarfUnwindInfo() const; bool needsAsyncDwarfUnwindInfo() const; diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp index f7c06b9fb..17d3674b5 100644 --- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp @@ -99,6 +99,8 @@ AArch64RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { return CSR_AArch64_AAPCS_SwiftTail_SaveList; if (MF->getFunction().getCallingConv() == CallingConv::PreserveMost) return CSR_AArch64_RT_MostRegs_SaveList; + if (MF->getFunction().getCallingConv() == CallingConv::CangjieGC) + return CSR_AArch64_CangjieGC_SaveList; if (MF->getFunction().getCallingConv() == CallingConv::Win64) // This is for OSes other than Windows; Windows is a separate case further // above. @@ -242,6 +244,8 @@ AArch64RegisterInfo::getCallPreservedMask(const MachineFunction &MF, if (CC == CallingConv::PreserveMost) return SCS ? CSR_AArch64_RT_MostRegs_SCS_RegMask : CSR_AArch64_RT_MostRegs_RegMask; + if (CC == CallingConv::CangjieGC) + return CSR_AArch64_CangjieGC_RegMask; else return SCS ? CSR_AArch64_AAPCS_SCS_RegMask : CSR_AArch64_AAPCS_RegMask; } @@ -315,6 +319,17 @@ AArch64RegisterInfo::getReservedRegs(const MachineFunction &MF) const { markSuperRegs(Reserved, AArch64::WSP); markSuperRegs(Reserved, AArch64::WZR); + if (MF.getFunction().hasCangjieGC() || + MF.getFunction().hasFnAttribute("pkg_c_wrapper")) { + markSuperRegs(Reserved, AArch64::W28); + // MachO's compact unwind format relies on all registers being stored in + // pairs. If only W28 is set, the setting does not take effect. + // W28 is used together with W27. + if (MF.getTarget().getTargetTriple().isOSBinFormatMachO()) { + markSuperRegs(Reserved, AArch64::W27); + } + } + if (TFI->hasFP(MF) || TT.isOSDarwin()) markSuperRegs(Reserved, AArch64::W29); @@ -487,6 +502,16 @@ AArch64RegisterInfo::getFrameRegister(const MachineFunction &MF) const { return TFI->hasFP(MF) ? AArch64::FP : AArch64::SP; } +MCPhysReg AArch64RegisterInfo::getX8Register() const { + return AArch64::X8; +} + +const std::vector +AArch64RegisterInfo::getArgRegs(const MachineFunction &MF) const { + return { AArch64::X0, AArch64::X1, AArch64::X2, AArch64::X3, AArch64::X4, + AArch64::X5, AArch64::X6, AArch64::X7 }; +} + bool AArch64RegisterInfo::requiresRegisterScavenging( const MachineFunction &MF) const { return true; diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.h b/llvm/lib/Target/AArch64/AArch64RegisterInfo.h index 12dd70fa4..a666786c8 100644 --- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.h +++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.h @@ -138,6 +138,11 @@ public: void getOffsetOpcodes(const StackOffset &Offset, SmallVectorImpl &Ops) const override; + + MCPhysReg getX8Register() const override; + + const std::vector + getArgRegs(const MachineFunction &MF) const override; }; } // end namespace llvm diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp index 66617393c..da4af7417 100644 --- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp @@ -282,6 +282,7 @@ AArch64TTIImpl::getIntImmCostIntrin(Intrinsic::ID IID, unsigned Idx, return TTI::TCC_Free; break; case Intrinsic::experimental_gc_statepoint: + case Intrinsic::cj_gc_statepoint: if ((Idx < 5) || (Imm.getBitWidth() <= 64 && isInt<64>(Imm.getSExtValue()))) return TTI::TCC_Free; break; diff --git a/llvm/lib/Target/AArch64/CMakeLists.txt b/llvm/lib/Target/AArch64/CMakeLists.txt index ca7d53dce..407631e03 100644 --- a/llvm/lib/Target/AArch64/CMakeLists.txt +++ b/llvm/lib/Target/AArch64/CMakeLists.txt @@ -29,6 +29,8 @@ tablegen(LLVM AArch64GenExegesis.inc -gen-exegesis) add_public_tablegen_target(AArch64CommonTableGen) +include_directories(${CANGJIE_DEMANGLE_DIR}) + add_llvm_target(AArch64CodeGen GISel/AArch64CallLowering.cpp GISel/AArch64GlobalISelUtils.cpp @@ -85,6 +87,9 @@ add_llvm_target(AArch64CodeGen SVEIntrinsicOpts.cpp AArch64SIMDInstrOpt.cpp + ADDITIONAL_HEADER_DIRS + ${CANGJIE_DEMANGLE_DIR} + DEPENDS intrinsics_gen diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp index 57cbd7a3b..08f7cc6c0 100644 --- a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp +++ b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp @@ -27,11 +27,13 @@ #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/CodeGen/MachineModuleInfoImpls.h" +#include "llvm/CodeGen/StackMaps.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Mangler.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" +#include "llvm/IR/Statepoint.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCContext.h" @@ -55,7 +57,10 @@ using namespace llvm; ARMAsmPrinter::ARMAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) : AsmPrinter(TM, std::move(Streamer)), Subtarget(nullptr), AFI(nullptr), - MCP(nullptr), InConstantPool(false), OptimizationGoals(-1) {} + MCP(nullptr), InConstantPool(false), OptimizationGoals(-1), SM(*this), + CMI(*this, SM) { + SM.setARM(); +} void ARMAsmPrinter::emitFunctionBodyEnd() { // Make sure to terminate any constant pools that were at the end @@ -173,6 +178,8 @@ bool ARMAsmPrinter::runOnMachineFunction(MachineFunction &MF) { // Emit the XRay table for this function. emitXRayTable(); + CMI.recordCurrentFunc(); + // If we need V4T thumb mode Register Indirect Jump pads, emit them. // These are created per function, rather than per TU, since it's // relatively easy to exceed the thumb branch range within a TU. @@ -577,6 +584,9 @@ void ARMAsmPrinter::emitEndOfAsmFile(Module &M) { OptimizationGoals = -1; ATS.finishAttributeSection(); + + emitCJMetadataInfo(CMI, M); + emitStackMaps(SM); } //===----------------------------------------------------------------------===// @@ -1332,6 +1342,182 @@ void ARMAsmPrinter::EmitUnwindingInstruction(const MachineInstr *MI) { } } +// ldr R12, .Ltmp1 +// .Ltmp0: +// add R12, pc, R12 +// ldr R12, [R12] +// sub sp, [sp, #8] +// str R12, [sp] // func address +// mov r12, #0 +// str R12, [sp, #4] // CallFrameSize +// bl CJ_MCC_N2CStub +// .Ltmp1: +// .long .Ltmp1-(.calleefunc+8) +void ARMAsmPrinter::emitCangjieCallStubInstImpl(const MachineInstr *MI, + const Function *F, + const MachineOperand &MOSym, + unsigned Opcode) { + auto *AddrGV = F->getParent()->getGlobalVariable( + MOSym.getGlobal()->getName().str() + ".CJStubGV", true); + + MCSymbol *GVSymbol = GetARMGVSymbol(AddrGV, MOSym.getTargetFlags()); + MCSymbol *Label0 = OutContext.createTempSymbol(); + MCSymbol *Label1 = OutContext.createTempSymbol(); + CangjieStubMap.push_back(std::make_tuple(GVSymbol, Label0, Label1)); + + // ldr R12, .LPCIxxx + EmitToStreamer(*OutStreamer, + MCInstBuilder(ARM::LDRi12) + .addReg(ARM::R12) + .addExpr(MCSymbolRefExpr::create(Label1, OutContext)) + .addImm(0) + .addImm(ARMCC::AL) + .addReg(0)); + // .Label0: + OutStreamer->emitLabel(Label0); + // add R12, pc, R12 + EmitToStreamer(*OutStreamer, MCInstBuilder(ARM::ADDrr) + .addReg(ARM::R12) + .addReg(ARM::PC) + .addReg(ARM::R12) + .addImm(ARMCC::AL) + .addReg(0) + .addReg(0)); + // ldr R12, [R12] + EmitToStreamer(*OutStreamer, MCInstBuilder(ARM::LDRi12) + .addReg(ARM::R12) + .addReg(ARM::R12) + .addImm(0) + .addImm(ARMCC::AL) + .addReg(0)); + // sub sp, [sp, #8] + EmitToStreamer(*OutStreamer, MCInstBuilder(ARM::SUBri) + .addReg(ARM::SP) + .addReg(ARM::SP) + .addImm(8) + .addImm(ARMCC::AL) + .addReg(0) + .addReg(0)); + // str R12, [sp] + EmitToStreamer(*OutStreamer, MCInstBuilder(ARM::STRi12) + .addReg(ARM::R12) + .addReg(ARM::SP) + .addImm(0) + .addImm(ARMCC::AL) + .addReg(0)); + + // push called-addr and CallFrameSize to stack. This operation extends + // the sp of the caller by 16 bytes and will be fixed in MCC_XXXStub + const auto *OriCalled = dyn_cast(MOSym.getGlobal()); + MDNode *Metadata = OriCalled->getMetadata("CallFrameSizeForCJFFI"); + assert(Metadata != nullptr && + "FFI Func must have CallFrameSizeForCJFFI meta!"); + unsigned CallFrameSize = + dyn_cast( + dyn_cast(Metadata->getOperand(0).get()) + ->getValue()) + ->getSExtValue(); + + // mov r12 , #CallFrameSize + EmitToStreamer(*OutStreamer, MCInstBuilder(ARM::MOVi) + .addReg(ARM::R12) + .addImm(CallFrameSize) + .addImm(ARMCC::AL) + .addReg(0) + .addReg(0)); + // str R12, [sp, #4] + EmitToStreamer(*OutStreamer, MCInstBuilder(ARM::STRi12) + .addReg(ARM::R12) + .addReg(ARM::SP) + .addImm(4) + .addImm(ARMCC::AL) + .addReg(0)); + + MCSymbol *StubGVSymbol = GetARMGVSymbol(F, 0); + // bl CJ_MCC_N2CStub + EmitToStreamer(*OutStreamer, + MCInstBuilder(ARM::BL).addOperand(MCOperand::createExpr( + MCSymbolRefExpr::create(StubGVSymbol, OutContext)))); + + SM.recordCJStackMap(*MI); + return; +} + +// C2NStub +// C2NStub +// b .Lable +// .Ltmp81: +// .long xxx.CJStubGV-(.Ltmp80+8) +// .Ltmp83: +// .long xxx.CJStubGV-(.Ltmp82+8) +// .Lable: +void ARMAsmPrinter::emitCangjieCustomInst() { + if (!CangjieStubMap.empty()) { + MCSymbol *DotSym = OutContext.createTempSymbol(); + const MCExpr *GVSymExpr = MCSymbolRefExpr::create(DotSym, OutContext); + EmitToStreamer( + *OutStreamer, + MCInstBuilder(ARM::Bcc).addExpr(GVSymExpr).addImm(ARMCC::AL).addReg(0)); + for (unsigned C2NStubIndex = 0; C2NStubIndex < CangjieStubMap.size(); + C2NStubIndex++) { + auto FuncSym = std::get<0>(CangjieStubMap[C2NStubIndex]); + auto StartLabel = std::get<1>(CangjieStubMap[C2NStubIndex]); + auto EndLabel = std::get<2>(CangjieStubMap[C2NStubIndex]); + OutStreamer->emitLabel(EndLabel); + // .long xxx.CJStubGV-(.Ltmp82+8) + auto Expr2 = MCBinaryExpr::createAdd( + MCSymbolRefExpr::create(StartLabel, OutContext), + MCConstantExpr::create(8, OutContext), OutContext); + auto Offset = MCBinaryExpr::createSub( + MCSymbolRefExpr::create(FuncSym, OutContext), Expr2, OutContext); + OutStreamer->emitValue(Offset, 4); + } + OutStreamer->emitLabel(DotSym); + CangjieStubMap.clear(); + } +} + +bool ARMAsmPrinter::tryEmitCangjieSpecificCall(const MachineInstr *MI) { + if (!MI->isCall()) { + return false; + } + + unsigned Opcode = MI->getOpcode(); + // statepoint call will be processed by LowerSTATEPOINT + if (Opcode == TargetOpcode::STATEPOINT) { + return false; + } + + if (MI->getNumOperands() == 0) { + return false; + } + + const MachineOperand &MOSym = MI->getOperand(0); + return tryEmitCangjieSpecificCallByMOSym(MI, MOSym, Opcode); +} + +bool ARMAsmPrinter::tryEmitCangjieSpecificCallByMOSym( + const MachineInstr *MI, const MachineOperand &MOSym, unsigned Opcode) { + if (!MOSym.isGlobal()) { + return false; + } + const auto *Callee = dyn_cast(MOSym.getGlobal()); + if (Callee == nullptr) { + return false; + } + StringRef FuncName = Callee->getName(); + if (FuncName.isSetDebugLocation()) { + return true; + } + + const Function *NativeFunc = tryGetCangjieStubCallNativeFunc(MI, Callee); + if (NativeFunc != nullptr) { + emitCangjieCallStubInstImpl(MI, NativeFunc, MOSym, Opcode); + return true; + } + return false; +} + // Simple pseudo-instructions have their lowering (with expansion to real // instructions) auto-generated. #include "ARMGenMCPseudoLowering.inc" @@ -1363,6 +1549,9 @@ void ARMAsmPrinter::emitInstruction(const MachineInstr *MI) { assert(!convertAddSubFlagsOpcode(MI->getOpcode()) && "Pseudo flag setting opcode should be expanded early"); + if (tryEmitCangjieSpecificCall(MI)) { + return; + } // Check for manual lowerings. unsigned Opc = MI->getOpcode(); switch (Opc) { @@ -2319,6 +2508,37 @@ void ARMAsmPrinter::emitInstruction(const MachineInstr *MI) { case ARM::SEH_EpilogEnd: ATS.emitARMWinCFIEpilogEnd(); return; + + case TargetOpcode::STATEPOINT: { + StatepointOpers SOpers(MI); + const MachineOperand &CallTarget = SOpers.getCallTarget(); + MCOperand CallTargetMCOp; + unsigned CallOpcode; + switch (CallTarget.getType()) { + default: + llvm_unreachable("Unsupported operand type in statepoint call target"); + break; + case MachineOperand::MO_GlobalAddress: + case MachineOperand::MO_ExternalSymbol: + CallOpcode = ARM::BL; + if (tryEmitCangjieSpecificCallByMOSym(MI, CallTarget, CallOpcode)) { + return; + } + lowerOperand(CallTarget, CallTargetMCOp); + break; + case MachineOperand::MO_Immediate: + CallTargetMCOp = MCOperand::createImm(CallTarget.getImm()); + CallOpcode = ARM::BL; + break; + case MachineOperand::MO_Register: + CallTargetMCOp = MCOperand::createReg(CallTarget.getReg()); + CallOpcode = ARM::BLX; + break; + } + SM.recordCJStackMap(*MI); + EmitToStreamer(*OutStreamer, MCInstBuilder(CallOpcode).addOperand(CallTargetMCOp)); + return; + } } MCInst TmpInst; diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.h b/llvm/lib/Target/ARM/ARMAsmPrinter.h index f8ff047a1..d55784076 100644 --- a/llvm/lib/Target/ARM/ARMAsmPrinter.h +++ b/llvm/lib/Target/ARM/ARMAsmPrinter.h @@ -11,6 +11,8 @@ #include "ARMSubtarget.h" #include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/CJMetadata.h" +#include "llvm/CodeGen/StackMaps.h" #include "llvm/Target/TargetMachine.h" namespace llvm { @@ -65,6 +67,10 @@ class LLVM_LIBRARY_VISIBILITY ARMAsmPrinter : public AsmPrinter { /// debug info can link properly. SmallPtrSet EmittedPromotedGlobalLabels; + StackMaps SM; + CJMetadataInfo CMI; + + public: explicit ARMAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer); @@ -129,6 +135,17 @@ private: bool emitPseudoExpansionLowering(MCStreamer &OutStreamer, const MachineInstr *MI); + bool tryEmitCangjieSpecificCall(const MachineInstr *MI) override; + bool tryEmitCangjieSpecificCallByMOSym(const MachineInstr *MI, + const MachineOperand &MOSym, + unsigned Opcode) override; + void emitCangjieCallStubInstImpl(const MachineInstr *MI, + const Function *F, + const MachineOperand &MOSym, + unsigned Opcode) override; + virtual void emitCangjieCustomInst() override; + MCOperand setGAAndLower(const MachineOperand &MOSym, + const GlobalValue *GV) ; public: unsigned getISAEncoding() override { // ARM/Darwin adds ISA to the DWARF info for each function. diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp index 183febe75..aae49ab8a 100644 --- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp +++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp @@ -807,6 +807,25 @@ unsigned ARMBaseInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { Size = alignTo(Size, 4); return Size; } + case TargetOpcode::STATEPOINT: + auto NumBytes = StatepointOpers(&MI).getNumPatchBytes(); + const MachineOperand &MO = MI.getOperand(3); + + if (MO.isGlobal()){ + const auto *Callee = dyn_cast(MO.getGlobal()); + if (Callee != nullptr) { + const auto &CallerFunc = MI.getParent()->getParent()->getFunction(); + if (Callee->isCangjieNativeStub(CallerFunc)) { + // 10:intrinsics num,4 bytes per intrinsic + NumBytes= 10 * 4; + } + } + } + assert(NumBytes % 4 == 0 && "Invalid number of NOP bytes requested!"); + // No patch bytes means a normal call inst is emitted + if (NumBytes == 0) + NumBytes = 4; + return NumBytes; } } diff --git a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp index 1d0e743b9..b566bb11a 100644 --- a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp +++ b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp @@ -69,6 +69,8 @@ ARMBaseRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { // GHC set of callee saved regs is empty as all those regs are // used for passing STG regs around return CSR_NoRegs_SaveList; + } else if (F.getCallingConv() == CallingConv::CangjieGC) { + return CSR_CangjieGC_SaveList; } else if (STI.splitFramePointerPush(*MF)) { return CSR_Win_SplitFP_SaveList; } else if (F.getCallingConv() == CallingConv::CFGuard_Check) { @@ -140,6 +142,8 @@ ARMBaseRegisterInfo::getCallPreservedMask(const MachineFunction &MF, return STI.isTargetDarwin() ? CSR_iOS_SwiftTail_RegMask : CSR_AAPCS_SwiftTail_RegMask; } + if (CC == CallingConv::CangjieGC) + return CSR_CangjieGC_RegMask; if (STI.getTargetLowering()->supportSwiftError() && MF.getFunction().getAttributes().hasAttrSomewhere(Attribute::SwiftError)) return STI.isTargetDarwin() ? CSR_iOS_SwiftError_RegMask diff --git a/llvm/lib/Target/ARM/ARMCallingConv.td b/llvm/lib/Target/ARM/ARMCallingConv.td index d14424c2d..7f2b49fb2 100644 --- a/llvm/lib/Target/ARM/ARMCallingConv.td +++ b/llvm/lib/Target/ARM/ARMCallingConv.td @@ -270,6 +270,10 @@ def CSR_FPRegs : CalleeSavedRegs<(add (sequence "D%u", 0, 31))>; def CSR_AAPCS : CalleeSavedRegs<(add LR, R11, R10, R9, R8, R7, R6, R5, R4, (sequence "D%u", 15, 8))>; +def CSR_CangjieGC + : CalleeSavedRegs<(add LR, SP, (sequence "R%u", 12, 1), + (sequence "D%u", 31, 0))>; + // The Windows Control Flow Guard Check function preserves the same registers as // AAPCS, and also preserves all floating point registers. def CSR_Win_AAPCS_CFGuard_Check : CalleeSavedRegs<(add LR, R11, R10, R9, R8, R7, diff --git a/llvm/lib/Target/ARM/ARMFastISel.cpp b/llvm/lib/Target/ARM/ARMFastISel.cpp index a167225e2..7a9a87501 100644 --- a/llvm/lib/Target/ARM/ARMFastISel.cpp +++ b/llvm/lib/Target/ARM/ARMFastISel.cpp @@ -1838,6 +1838,7 @@ CCAssignFn *ARMFastISel::CCAssignFnForCall(CallingConv::ID CC, return (Return ? RetCC_ARM_AAPCS_VFP : CC_ARM_AAPCS_VFP); } LLVM_FALLTHROUGH; + case CallingConv::CangjieGC: case CallingConv::C: case CallingConv::CXX_FAST_TLS: // Use target triple & subtarget features to do actual dispatch. @@ -2384,7 +2385,14 @@ bool ARMFastISel::SelectCall(const Instruction *I, if (!ProcessCallArgs(Args, ArgRegs, ArgVTs, ArgFlags, RegArgs, CC, NumBytes, isVarArg)) return false; - + if (Callee) { + SmallVector ArgLocs; + CCState CCInfo(CC, isVarArg, *FuncInfo.MF, ArgLocs, *Context); + CCInfo.AnalyzeCallOperands(ArgVTs, ArgFlags, + CCAssignFnForCall(CC, false, isVarArg)); + CCInfo.AddAlignedCallFrameSizeMetaDataForCJFFI( + const_cast(dyn_cast(Callee)), NumBytes); + } bool UseReg = false; const GlobalValue *GV = dyn_cast(Callee); if (!GV || Subtarget->genLongCalls()) UseReg = true; diff --git a/llvm/lib/Target/ARM/ARMFrameLowering.cpp b/llvm/lib/Target/ARM/ARMFrameLowering.cpp index 48b4d266b..d09793057 100644 --- a/llvm/lib/Target/ARM/ARMFrameLowering.cpp +++ b/llvm/lib/Target/ARM/ARMFrameLowering.cpp @@ -861,6 +861,13 @@ void ARMFrameLowering::emitPrologue(MachineFunction &MF, // Move past area 1. if (GPRCS1Size > 0) { + if (MF.getFunction().hasCangjieGC()) { + while (MBBI != MBB.end() && MBBI->getFlag(MachineInstr::FrameSetup)) + ++MBBI; + assert(MBB.begin() != MBB.end()); + MBBI = std::prev(MBBI); + GPRCS1Size += 4; // 4: size of pc register + } GPRCS1Push = LastPush = MBBI++; DefCFAOffsetCandidates.addInst(LastPush, GPRCS1Size, true); } @@ -1047,6 +1054,10 @@ void ARMFrameLowering::emitPrologue(MachineFunction &MF, if (HasFP) { AfterPush = std::next(GPRCS1Push); unsigned PushSize = sizeOfSPAdjustment(*GPRCS1Push); + if (MF.getFunction().hasCangjieGC()) + PushSize += 4 * (GPRCS1Push->getOperand(4).getReg() == ARM::PC + ? 2 // 2: r11, lr + : 3); // 3: r11, lr, pc, 4: RegSize int FPOffset = PushSize + FramePtrOffsetInPush; if (STI.splitFramePointerPush(MF)) { AfterPush = std::next(GPRCS2Push); @@ -1549,8 +1560,35 @@ void ARMFrameLowering::emitPushInst(MachineBasicBlock &MBB, .addReg(ARM::SP) .setMIFlags(MIFlags) .add(predOps(ARMCC::AL)); - for (unsigned i = 0, e = Regs.size(); i < e; ++i) - MIB.addReg(Regs[i].first, getKillRegState(Regs[i].second)); + // Only need to be handle in `isARMArea1Register`, and the last reg is LR. + if (MBB.getParent()->getFunction().hasCangjieGC() && LastReg == ARM::LR) { + auto *MF = MBB.getParent(); + // start pc + BuildMI(MBB, MI, DL, TII.get(StmOpc), ARM::SP) + .addReg(ARM::SP) + .setMIFlags(MIFlags) + .add(predOps(ARMCC::AL)) + .addReg(ARM::PC); + + auto MIB2 = BuildMI(MBB, MI, DL, TII.get(StmOpc), ARM::SP) + .addReg(ARM::SP) + .setMIFlags(MIFlags) + .add(predOps(ARMCC::AL)); + for (unsigned i = 0, e = Regs.size(); i < e; ++i) + if (TRI.getEncodingValue(Regs[i].first) >= + TRI.getEncodingValue(TRI.getFrameRegister(*MF))) + // r11, lr + MIB.addReg(Regs[i].first, getKillRegState(Regs[i].second)); + else + // Other callee saved reg + MIB2.addReg(Regs[i].first, getKillRegState(Regs[i].second)); + // Empty csr operands + if (MIB2->getDesc().getNumOperands() > MIB2->getNumOperands()) + MIB2->eraseFromParent(); + } else { + for (unsigned i = 0, e = Regs.size(); i < e; ++i) + MIB.addReg(Regs[i].first, getKillRegState(Regs[i].second)); + } } else if (Regs.size() == 1) { BuildMI(MBB, MI, DL, TII.get(StrOpc), ARM::SP) .addReg(Regs[0].first, getKillRegState(Regs[0].second)) @@ -1646,8 +1684,31 @@ void ARMFrameLowering::emitPopInst(MachineBasicBlock &MBB, .addReg(ARM::SP) .add(predOps(ARMCC::AL)) .setMIFlags(MachineInstr::FrameDestroy); - for (unsigned i = 0, e = Regs.size(); i < e; ++i) - MIB.addReg(Regs[i], getDefRegState(true)); + // The last reg may be PC or LR. + if (MBB.getParent()->getFunction().hasCangjieGC() && + (LastReg == ARM::PC || LastReg == ARM::LR)) { + auto *MF = MBB.getParent(); + // 4: size of pc + emitSPUpdate(!AFI->isThumbFunction(), MBB, MI, DL, *STI.getInstrInfo(), + 4, MachineInstr::FrameDestroy); + auto MIB2 = BuildMI(MBB, MI, DL, TII.get(LdmOpc), ARM::SP) + .addReg(ARM::SP) + .add(predOps(ARMCC::AL)) + .setMIFlags(MachineInstr::FrameDestroy); + for (unsigned i = 0, e = Regs.size(); i < e; ++i) + if (TRI.getEncodingValue(Regs[i]) < + TRI.getEncodingValue(TRI.getFrameRegister(*MF))) + // r11, lr + MIB.addReg(Regs[i], getDefRegState(true)); + else + MIB2.addReg(Regs[i], getDefRegState(true)); + // Empty csr operands + if (MIB->getDesc().getNumOperands() > MIB->getNumOperands()) + MIB->eraseFromParent(); + } else { + for (unsigned i = 0, e = Regs.size(); i < e; ++i) + MIB.addReg(Regs[i], getDefRegState(true)); + } if (DeleteRet) { if (MI != MBB.end()) { MIB.copyImplicitOps(*MI); @@ -2076,6 +2137,8 @@ static unsigned estimateRSStackSizeLimit(MachineFunction &MF, for (unsigned i = 0, e = MI.getNumOperands(); i != e; ++i) { if (!MI.getOperand(i).isFI()) continue; + if (MI.isPseudo()) + continue; // When using ADDri to get the address of a stack object, 255 is the // largest offset guaranteed to fit in the immediate offset. diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp index 4c24d7020..f2a8b4124 100644 --- a/llvm/lib/Target/ARM/ARMISelLowering.cpp +++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp @@ -2067,6 +2067,7 @@ ARMTargetLowering::getEffectiveCallingConv(CallingConv::ID CC, case CallingConv::ARM_APCS: case CallingConv::GHC: case CallingConv::CFGuard_Check: + case CallingConv::CangjieGC: return CC; case CallingConv::PreserveMost: return CallingConv::PreserveMost; @@ -2116,6 +2117,8 @@ CCAssignFn *ARMTargetLowering::CCAssignFnForNode(CallingConv::ID CC, switch (getEffectiveCallingConv(CC, isVarArg)) { default: report_fatal_error("Unsupported calling convention"); + case CallingConv::CangjieGC: + return Return ? RetCC_ARM_AAPCS : CC_ARM_AAPCS; case CallingConv::ARM_APCS: return (Return ? RetCC_ARM_APCS : CC_ARM_APCS); case CallingConv::ARM_AAPCS: @@ -2403,6 +2406,7 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, // Get a count of how many bytes are to be pushed on the stack. unsigned NumBytes = CCInfo.getNextStackOffset(); + unsigned CallFrameSizeForCJFFI = NumBytes; // SPDiff is the byte offset of the call's argument area from the callee's. // Stores to callee stack arguments will be placed in FixedStackSlots offset @@ -2419,6 +2423,7 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, // popped size 16-byte aligned. Align StackAlign = DAG.getDataLayout().getStackAlignment(); NumBytes = alignTo(NumBytes, StackAlign); + CallFrameSizeForCJFFI = NumBytes; // SPDiff will be negative if this tail call requires more space than we // would automatically have in our incoming argument space. Positive if we @@ -2629,8 +2634,12 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, const TargetMachine &TM = getTargetMachine(); const Module *Mod = MF.getFunction().getParent(); const GlobalValue *GV = nullptr; - if (GlobalAddressSDNode *G = dyn_cast(Callee)) + if (GlobalAddressSDNode *G = dyn_cast(Callee)){ + auto *CalleeFunc = dyn_cast(G->getGlobal()); + CCInfo.AddAlignedCallFrameSizeMetaDataForCJFFI( + const_cast(CalleeFunc), CallFrameSizeForCJFFI); GV = G->getGlobal(); + } bool isStub = !TM.shouldAssumeDSOLocal(*Mod, GV) && Subtarget->isTargetMachO(); @@ -11758,6 +11767,14 @@ ARMTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, llvm_unreachable("Unexpected instr type to insert"); } + case TargetOpcode::STATEPOINT: + MI.addOperand(*MI.getMF(), + MachineOperand::CreateReg( + ARM::LR, /*isDef*/ true, + /*isImp*/ true, /*isKill*/ false, /*isDead*/ true, + /*isUndef*/ false, /*isEarlyClobber*/ true)); + return emitPatchPoint(MI, BB); + // Thumb1 post-indexed loads are really just single-register LDMs. case ARM::tLDR_postidx: { MachineOperand Def(MI.getOperand(1)); @@ -21193,7 +21210,12 @@ Value *ARMTargetLowering::emitLoadLinked(IRBuilderBase &Builder, Type *ValueTy, IsAcquire ? Intrinsic::arm_ldaexd : Intrinsic::arm_ldrexd; Function *Ldrex = Intrinsic::getDeclaration(M, Int); - Addr = Builder.CreateBitCast(Addr, Type::getInt8PtrTy(M->getContext())); + if (Builder.GetInsertBlock()->getParent()->hasCangjieGC()) + // It is safe to cast addrspace. + Addr = Builder.CreatePointerBitCastOrAddrSpaceCast( + Addr, Type::getInt8PtrTy(M->getContext())); + else + Addr = Builder.CreateBitCast(Addr, Type::getInt8PtrTy(M->getContext())); Value *LoHi = Builder.CreateCall(Ldrex, Addr, "lohi"); Value *Lo = Builder.CreateExtractValue(LoHi, 0, "lo"); @@ -21243,7 +21265,11 @@ Value *ARMTargetLowering::emitStoreConditional(IRBuilderBase &Builder, Value *Hi = Builder.CreateTrunc(Builder.CreateLShr(Val, 32), Int32Ty, "hi"); if (!Subtarget->isLittle()) std::swap(Lo, Hi); - Addr = Builder.CreateBitCast(Addr, Type::getInt8PtrTy(M->getContext())); + if (Builder.GetInsertBlock()->getParent()->hasCangjieGC()) + Addr = Builder.CreatePointerBitCastOrAddrSpaceCast( + Addr, Type::getInt8PtrTy(M->getContext())); + else + Addr = Builder.CreateBitCast(Addr, Type::getInt8PtrTy(M->getContext())); return Builder.CreateCall(Strex, {Lo, Hi, Addr}); } diff --git a/llvm/lib/Target/TargetLoweringObjectFile.cpp b/llvm/lib/Target/TargetLoweringObjectFile.cpp index 29cc28403..35f1aa816 100644 --- a/llvm/lib/Target/TargetLoweringObjectFile.cpp +++ b/llvm/lib/Target/TargetLoweringObjectFile.cpp @@ -213,6 +213,29 @@ SectionKind TargetLoweringObjectFile::getKindForGlobal(const GlobalObject *GO, // Global variables require more detailed analysis. const auto *GVar = cast(GO); + if (GVar->isCJTypeInfo()) + return SectionKind::getCJTypeInfo(); + if (GVar->isCJTypeTemplate()) + return SectionKind::getCJTypeTemplate(); + if (GVar->isCJTypeName() || GVar->isCJTIOffsets() || GVar->isCJTITypeArgs() || + GVar->isCJTIFields() || GVar->isCJTTFieldsFns() || + GVar->isCJReflectUpperBounds() || GVar->isCJFunctableTable()) + return SectionKind::getCJTypeFields(); + if (GVar->isCJGCTib()) + return SectionKind::getCJGCTib(); + if (GVar->isCJMTable()) + return SectionKind::getCJMTable(); + if (GVar->isCJReflectPkgInfo()) + return SectionKind::getCJReflectPkgInfo(); + if (GVar->isCJReflectGV()) + return SectionKind::getCJReflectGV(); + if (GVar->isCJReflectGeneticTI()) + return SectionKind::getCJReflectGenericTI(); + if (GVar->isCJStaticGenericTI()) + return SectionKind::getCJStaticGenericTI(); + if (GVar->isCJInnerTypeExtensions()) + return SectionKind::getCJInnerTypeExtensions(); + // Handle thread-local data first. if (GVar->isThreadLocal()) { if (isSuitableForBSS(GVar) && !TM.Options.NoZerosInBSS) { @@ -335,8 +358,11 @@ MCSection *TargetLoweringObjectFile::SectionForGlobal( } if (auto *F = dyn_cast(GO)) { - if (F->hasFnAttribute("implicit-section-name")) + bool EmitUniqueSection = TM.getFunctionSections(); + if (F->hasFnAttribute("implicit-section-name") || + (F->hasFnAttribute("cjinit") && !EmitUniqueSection)) { return getExplicitSectionGlobal(GO, Kind, TM); + } } // Use default section depending on the 'type' of global diff --git a/llvm/lib/Target/X86/CMakeLists.txt b/llvm/lib/Target/X86/CMakeLists.txt index 33a6f1b08..75e067fff 100644 --- a/llvm/lib/Target/X86/CMakeLists.txt +++ b/llvm/lib/Target/X86/CMakeLists.txt @@ -25,6 +25,8 @@ endif() add_public_tablegen_target(X86CommonTableGen) +include_directories(${CANGJIE_DEMANGLE_DIR}) + set(sources X86AsmPrinter.cpp X86AvoidTrailingCall.cpp @@ -87,6 +89,9 @@ set(sources X86VZeroUpper.cpp X86WinEHState.cpp X86InsertWait.cpp + + ADDITIONAL_HEADER_DIRS + ${CANGJIE_DEMANGLE_DIR} ) add_llvm_target(X86CodeGen ${sources} diff --git a/llvm/lib/Target/X86/X86AsmPrinter.cpp b/llvm/lib/Target/X86/X86AsmPrinter.cpp index c205395aa..56346c4e1 100644 --- a/llvm/lib/Target/X86/X86AsmPrinter.cpp +++ b/llvm/lib/Target/X86/X86AsmPrinter.cpp @@ -48,7 +48,10 @@ using namespace llvm; X86AsmPrinter::X86AsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) - : AsmPrinter(TM, std::move(Streamer)), SM(*this), FM(*this) {} + : AsmPrinter(TM, std::move(Streamer)), SM(*this), FM(*this), + CMI(*this, SM) { + SM.setX86_64(); +} //===----------------------------------------------------------------------===// // Primitive Helper Functions. @@ -84,6 +87,8 @@ bool X86AsmPrinter::runOnMachineFunction(MachineFunction &MF) { // Emit the XRay table for this function. emitXRayTable(); + CMI.recordCurrentFunc(); + EmitFPOData = false; // We didn't modify anything. @@ -799,6 +804,7 @@ void X86AsmPrinter::emitEndOfAsmFile(Module &M) { // global table for symbol lookup. emitNonLazyStubs(MMI, *OutStreamer); + emitCJMetadataInfo(CMI, M); // Emit stack and fault map information. emitStackMaps(SM); FM.serializeToFaultMapSection(); @@ -830,8 +836,10 @@ void X86AsmPrinter::emitEndOfAsmFile(Module &M) { OutStreamer->emitSymbolAttribute(S, MCSA_Global); return; } + emitCJMetadataInfo(CMI, M); emitStackMaps(SM); } else if (TT.isOSBinFormatELF()) { + emitCJMetadataInfo(CMI, M); emitStackMaps(SM); FM.serializeToFaultMapSection(); } diff --git a/llvm/lib/Target/X86/X86AsmPrinter.h b/llvm/lib/Target/X86/X86AsmPrinter.h index d53c26b72..fbe106b6f 100644 --- a/llvm/lib/Target/X86/X86AsmPrinter.h +++ b/llvm/lib/Target/X86/X86AsmPrinter.h @@ -10,6 +10,7 @@ #define LLVM_LIB_TARGET_X86_X86ASMPRINTER_H #include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/CJMetadata.h" #include "llvm/CodeGen/FaultMaps.h" #include "llvm/CodeGen/StackMaps.h" @@ -28,6 +29,7 @@ class LLVM_LIBRARY_VISIBILITY X86AsmPrinter : public AsmPrinter { const X86Subtarget *Subtarget = nullptr; StackMaps SM; FaultMaps FM; + CJMetadataInfo CMI; std::unique_ptr CodeEmitter; bool EmitFPOData = false; bool ShouldEmitWeakSwiftAsyncExtendedFramePointerFlags = false; @@ -129,8 +131,29 @@ public: void emitEndOfAsmFile(Module &M) override; + void emitStackCmp(const MachineInstr &MI); + + void emitCJStackCheck(const MachineInstr &MI) override; + + int emitSOFECall(const MachineInstr &MI) override; + + int emitStackGrow(const MachineInstr &MI) override; + + void emitCangjieSafepoint(const MachineInstr &MI) override; + + int emitSafePointDirectCall(unsigned Index) override; + + void emitMetadataAddress() override; + + void emitGcStateCheck() override; + void emitInstruction(const MachineInstr *MI) override; + void tryDoAdaptionForFP16InCJ(const MachineInstr *MI, bool &IsTruncToFP16, + bool IsWindows); + + void reloadSPForEpilogue(MachineFunction &MF) override; + void emitBasicBlockEnd(const MachineBasicBlock &MBB) override; bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, @@ -152,6 +175,42 @@ public: bool shouldEmitWeakSwiftAsyncExtendedFramePointerFlags() const override { return ShouldEmitWeakSwiftAsyncExtendedFramePointerFlags; } + void emitGetCJTLSData(int64_t Offset); + +private: + void emitCangjieCallStubInstImpl(const MachineInstr *MI, const Function *F, + const MachineOperand &MOSym, + unsigned Opcode) override; + void extendStackAndInsertFFIInfoForJmp(MCInst &MovGVToR11, + unsigned CallFrameSize); + + struct ParamForEmitNewObj { + ParamForEmitNewObj(const MachineInstr *MIInit, + const MachineOperand &MOSymInit, unsigned int OpcodeInit, + X86MCInstLower &MCILoweing, MCInst &CallSlow, + StringRef FastFuncName) + : MI(MIInit), MOSym(MOSymInit), Opcode(OpcodeInit), + MCInstLowering(MCILoweing), CallNewObjSlowPath(CallSlow), + FastFuncName(FastFuncName) {} + ~ParamForEmitNewObj() = default; + const MachineInstr *MI; + const MachineOperand &MOSym; + unsigned int Opcode; + X86MCInstLower &MCInstLowering; + MCInst &CallNewObjSlowPath; + StringRef FastFuncName; + }; + + void emitMccNewObjectForCopyGC(ParamForEmitNewObj &Param); + void emitMccNewObjectFastPath(const MachineInstr *MI, + const MachineOperand &MOSym, + unsigned Opcode) override; + void emitCJNewArrayFastPath(const MachineInstr &MI, + const MachineOperand &MOSym) override; + void emitCJThrowException(const MachineInstr *MI, + const MachineOperand &MOSym, + unsigned Opcode) override; + void emitGetCJThreadId() override; }; } // end namespace llvm diff --git a/llvm/lib/Target/X86/X86CallFrameOptimization.cpp b/llvm/lib/Target/X86/X86CallFrameOptimization.cpp index 1fa559dcf..ad385764d 100644 --- a/llvm/lib/Target/X86/X86CallFrameOptimization.cpp +++ b/llvm/lib/Target/X86/X86CallFrameOptimization.cpp @@ -56,6 +56,11 @@ static cl::opt cl::desc("Avoid optimizing x86 call frames for size"), cl::init(false), cl::Hidden); +static cl::opt + EnableX86CFOpt("enable-cangjie-x86-call-frame-opt", + cl::desc("Optimize x86 call frames for size"), + cl::init(false), cl::Hidden); + namespace { class X86CallFrameOptimization : public MachineFunctionPass { @@ -133,6 +138,10 @@ INITIALIZE_PASS(X86CallFrameOptimization, DEBUG_TYPE, // Also returns false in cases where it's potentially legal, but // we don't even want to try. bool X86CallFrameOptimization::isLegal(MachineFunction &MF) { + if (MF.getFunction().hasCangjieGC() && !EnableX86CFOpt) { + NoX86CFOpt.setValue(true); + } + if (NoX86CFOpt.getValue()) return false; diff --git a/llvm/lib/Target/X86/X86FastISel.cpp b/llvm/lib/Target/X86/X86FastISel.cpp index f2c362eea..2f28c3eaa 100644 --- a/llvm/lib/Target/X86/X86FastISel.cpp +++ b/llvm/lib/Target/X86/X86FastISel.cpp @@ -3288,7 +3288,11 @@ bool X86FastISel::fastLowerCall(CallLoweringInfo &CLI) { // Get a count of how many bytes are to be pushed on the stack. unsigned NumBytes = CCInfo.getAlignedCallFrameSize(); - + // Cangjie need to add frame info for CFFI stub. + if (Callee) { + CCInfo.AddAlignedCallFrameSizeMetaDataForCJFFI( + const_cast(dyn_cast(Callee)), NumBytes); + } // Issue CALLSEQ_START unsigned AdjStackDown = TII.getCallFrameSetupOpcode(); BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(AdjStackDown)) diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp index d524090f9..305dde3e2 100644 --- a/llvm/lib/Target/X86/X86FrameLowering.cpp +++ b/llvm/lib/Target/X86/X86FrameLowering.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "X86FrameLowering.h" +#include "CangjieDemangle.h" #include "MCTargetDesc/X86MCTargetDesc.h" #include "X86InstrBuilder.h" #include "X86InstrInfo.h" @@ -42,6 +43,11 @@ STATISTIC(NumFrameLoopProbe, "Number of loop stack probes used in prologue"); STATISTIC(NumFrameExtraProbe, "Number of extra stack probes generated in prologue"); +namespace llvm { +extern cl::opt CJPipeline; +extern cl::opt CangjieJIT; +} + using namespace llvm; X86FrameLowering::X86FrameLowering(const X86Subtarget &STI, @@ -101,7 +107,8 @@ bool X86FrameLowering::hasFP(const MachineFunction &MF) const { MF.getInfo()->hasPreallocatedCall() || MF.callsUnwindInit() || MF.hasEHFunclets() || MF.callsEHReturn() || MFI.hasStackMap() || MFI.hasPatchPoint() || - (isWin64Prologue(MF) && MFI.hasCopyImplyingStackAdjustment())); + (isWin64Prologue(MF) && MFI.hasCopyImplyingStackAdjustment()) || + CangjieJIT); } static unsigned getSUBriOpcode(bool IsLP64, int64_t Imm) { @@ -221,12 +228,17 @@ void X86FrameLowering::emitSPUpdate(MachineBasicBlock &MBB, int64_t NumBytes, bool InEpilogue) const { bool isSub = NumBytes < 0; uint64_t Offset = isSub ? -NumBytes : NumBytes; - MachineInstr::MIFlag Flag = - isSub ? MachineInstr::FrameSetup : MachineInstr::FrameDestroy; + + MachineFunction &MF = *MBB.getParent(); + bool CangjieFunc = MF.getFunction().hasCangjieGC(); + // Instrucitons generated in Prologue should not be recognized as + // FrameDestroy instructions in Cangjie. + MachineInstr::MIFlag Flag = (isSub || (CangjieFunc && !InEpilogue)) + ? MachineInstr::FrameSetup + : MachineInstr::FrameDestroy; uint64_t Chunk = (1LL << 31) - 1; - MachineFunction &MF = *MBB.getParent(); const X86Subtarget &STI = MF.getSubtarget(); const X86TargetLowering &TLI = *STI.getTargetLowering(); const bool EmitInlineStackProbe = TLI.hasInlineStackProbe(MF); @@ -1470,10 +1482,20 @@ bool X86FrameLowering::needsDwarfCFI(const MachineFunction &MF) const { - for 32-bit code, substitute %e?? registers for %r?? */ +const std::vector +X86FrameLowering::getArgRegs(const MachineFunction &MF) const { + if (isWin64Prologue(MF)) { + return { X86::RCX, X86::RDX, X86::R8, X86::R9 }; + } else { + return { X86::RDI, X86::RSI, X86::RDX, X86::RCX, X86::R8, X86::R9 }; + } +} + void X86FrameLowering::emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const { assert(&STI == &MF.getSubtarget() && "MF used frame lowering for wrong subtarget"); + MachineBasicBlock::iterator MBBI = MBB.begin(); MachineFrameInfo &MFI = MF.getFrameInfo(); const Function &Fn = MF.getFunction(); @@ -1502,7 +1524,7 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF, ? Register(getX86SubSuperRegister(FramePtr, 64)) : FramePtr; Register BasePtr = TRI->getBaseRegister(); bool HasWinCFI = false; - + bool CangjieFunc = Fn.hasCangjieGC(); // Debug location must be unknown since the first debug location is used // to determine the end of the prologue. DebugLoc DL; @@ -1557,6 +1579,15 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF, emitSPUpdate(MBB, MBBI, DL, -8, /*InEpilogue=*/false); } + unsigned CJAdjustSize = 0; + if (STI.isTargetMachO()) { + // 2: Need 2 slotsize in macos: StorePC and .Lmethod_desc.xxx + CJAdjustSize = SlotSize * 2; + } else if (STI.isTargetLinux()) { + // For StorePC in Linux + CJAdjustSize = SlotSize; + } + // If this is x86-64 and the Red Zone is not disabled, if we are a leaf // function, and use up to 128 bytes of stack space, don't have a frame // pointer, calls, or dynamic alloca then we do not need to adjust the @@ -1568,14 +1599,34 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF, !EmitStackProbeCall && // No stack probes. !MFI.hasCopyImplyingStackAdjustment() && // Don't push and pop. !MF.shouldSplitStack()) { // Regular stack - uint64_t MinSize = - X86FI->getCalleeSavedFrameSize() - X86FI->getTCReturnAddrDelta(); - if (HasFP) MinSize += SlotSize; + // In the Cangjie function, a slotsize is required to save the 'rip' value. + // The MinSize needs add it. + uint64_t MinSize = X86FI->getCalleeSavedFrameSize() - + X86FI->getTCReturnAddrDelta(); + if (CangjieFunc) { + MinSize += CJAdjustSize; + } + + if (HasFP) + MinSize += SlotSize; X86FI->setUsesRedZone(MinSize > 0 || StackSize > 0); StackSize = std::max(MinSize, StackSize > 128 ? StackSize - 128 : 0); MFI.setStackSize(StackSize); } + // 2147483648: 2*1024*1024*1024, 2GB + if (CJPipeline && StackSize >= 2147483648) { + auto D = Cangjie::Demangle(MF.getName().str()); + std::string DemangledName = + D.GetPkgName() + std::string(D.GetPkgName().empty() ? "" : "::") + + D.GetFullName(); + report_fatal_error("The stacksize of " + Twine(DemangledName) + + " exceeds cangjie max stacksize(2GB).\nCompilation " + "stopped! Please check the implementation of " + + Twine(DemangledName) + "!", + false); + } + // Insert stack pointer adjustment for later moving of return addr. Only // applies to tail call optimized functions where the callee argument stack // size is bigger than the callers. @@ -1620,12 +1671,16 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF, MBB.addLiveIn(Establisher); } + // Calculate required stack adjustment. + uint64_t FrameSize = StackSize - SlotSize; if (HasFP) { assert(MF.getRegInfo().isReserved(MachineFramePtr) && "FP reserved"); - // Calculate required stack adjustment. - uint64_t FrameSize = StackSize - SlotSize; - // If required, include space for extra hidden slot for stashing base pointer. + if (!IsWin64Prologue && CangjieFunc) { + FrameSize -= CJAdjustSize; + } + // If required, include space for extra hidden slot for stashing base + // pointer. if (X86FI->getRestoreBasePointer()) FrameSize += SlotSize; @@ -1733,6 +1788,11 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF, .addImm(0) .setMIFlag(MachineInstr::FrameSetup); } + if (CangjieFunc) { + // Use PUSHPC64 to store method info to stack slot. + // see PUSHPC64 in X86InstrCompiler.td. + BuildMI(MBB, MBBI, DL, TII.get(X86::PUSHPC64)).addImm(0); + } } } } else { @@ -1808,6 +1868,8 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF, // Adjust stack pointer: ESP -= numbytes. + bool HasProcessSOFE = false; + // Windows and cygwin/mingw require a prologue helper routine when allocating // more than 4K bytes on the stack. Windows uses __chkstk and cygwin/mingw // uses __alloca. __alloca and the 32-bit version of __chkstk will probe the @@ -1826,6 +1888,9 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF, // Check whether EAX is livein for this block. bool isEAXAlive = isEAXLiveIn(MBB); + MBBI = preCJStackCheck(MF, MBBI, NumBytes, false); + HasProcessSOFE = true; + if (isEAXAlive) { if (Is64Bit) { // Save RAX @@ -1874,6 +1939,11 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF, emitSPUpdate(MBB, MBBI, DL, -(int64_t)NumBytes, /*InEpilogue=*/false); } + // Prepare for SOFE + if (!HasProcessSOFE) { + MBBI = preCJStackCheck(MF, MBBI, FrameSize); + } + if (NeedsWinCFI && NumBytes) { HasWinCFI = true; BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_StackAlloc)) @@ -1917,12 +1987,15 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF, // this calculation on the incoming establisher, which holds the value of // RSP from the parent frame at the end of the prologue. SEHFrameOffset = calculateSetFPREG(ParentFrameNumBytes); - if (SEHFrameOffset) + if (SEHFrameOffset) { addRegOffset(BuildMI(MBB, MBBI, DL, TII.get(X86::LEA64r), FramePtr), SPOrEstablisher, false, SEHFrameOffset); - else + } else { BuildMI(MBB, MBBI, DL, TII.get(X86::MOV64rr), FramePtr) .addReg(SPOrEstablisher); + } + if (CangjieFunc) + MFI.setWin64FramePointerOffset(FrameSize - SEHFrameOffset); // If this is not a funclet, emit the CFI describing our frame pointer. if (NeedsWinCFI && !IsFunclet) { @@ -2195,6 +2268,16 @@ void X86FrameLowering::emitEpilogue(MachineFunction &MF, !MF.getTarget().getTargetTriple().isOSWindows()) && MF.needsFrameMoves(); + bool CangjieFunc = MF.getFunction().hasCangjieGC(); + bool NeedCangjiePCSlot = CangjieFunc && CSSize != 0 && !IsWin64Prologue; + unsigned CJSlotSize; + if (STI.isTargetMachO()) { + // 2: Need 2 slotsize in macos: StorePC and .Lmethod_desc.xxx + CJSlotSize = SlotSize * 2; + } else { + CJSlotSize = SlotSize; + } + if (IsFunclet) { assert(HasFP && "EH funclets without FP not yet implemented"); NumBytes = getWinEHFuncletFrameSize(MF); @@ -2202,7 +2285,9 @@ void X86FrameLowering::emitEpilogue(MachineFunction &MF, // Calculate required stack adjustment. uint64_t FrameSize = StackSize - SlotSize; NumBytes = FrameSize - CSSize - TailCallArgReserveSize; - + if (NeedCangjiePCSlot) { + NumBytes -= CJSlotSize; + } // Callee-saved registers were pushed on stack before the stack was // realigned. if (TRI->hasStackRealignment(MF) && !IsWin64Prologue) @@ -2215,6 +2300,9 @@ void X86FrameLowering::emitEpilogue(MachineFunction &MF, // AfterPop is the position to insert .cfi_restore. MachineBasicBlock::iterator AfterPop = MBBI; if (HasFP) { + if (NeedCangjiePCSlot) { + emitSPUpdate(MBB, MBBI, DL, CJSlotSize, true); + } if (X86FI->hasSwiftAsyncContext()) { // Discard the context. int Offset = 16 + mergeSPUpdates(MBB, MBBI, true); @@ -2279,7 +2367,16 @@ void X86FrameLowering::emitEpilogue(MachineFunction &MF, DL = MBBI->getDebugLoc(); // If there is an ADD32ri or SUB32ri of ESP immediately before this // instruction, merge the two instructions. - if (NumBytes || MFI.hasVarSizedObjects()) + // We do not want this merge for Cangjie functions, cause it will break our + // epilogue. Consider the following snippet: + // addq $8, %rsp + // .L_epilogue + // addq $8, %rsp + // popq %rbp + // We do not want the two addq to be merge into + // .L_epilogue + // addq $16, %rsp + if ((NumBytes || MFI.hasVarSizedObjects()) && !CangjieFunc) NumBytes += mergeSPUpdates(MBB, MBBI, true); // If dynamic alloca is used, then reset esp to point to the last callee-saved @@ -2291,9 +2388,9 @@ void X86FrameLowering::emitEpilogue(MachineFunction &MF, if (TRI->hasStackRealignment(MF)) MBBI = FirstCSPop; unsigned SEHFrameOffset = calculateSetFPREG(SEHStackAllocAmt); - uint64_t LEAAmount = - IsWin64Prologue ? SEHStackAllocAmt - SEHFrameOffset : -CSSize; - + uint64_t LEAAmount = IsWin64Prologue + ? SEHStackAllocAmt - SEHFrameOffset + : NeedCangjiePCSlot ? -CSSize - CJSlotSize : -CSSize; if (X86FI->hasSwiftAsyncContext()) LEAAmount -= 16; @@ -2304,17 +2401,22 @@ void X86FrameLowering::emitEpilogue(MachineFunction &MF, // 'mov %FramePtr, %rsp' will not be recognized as an epilogue sequence. // However, we may use this sequence if we have a frame pointer because the // effects of the prologue can safely be undone. + MachineInstrBuilder MIRSP; if (LEAAmount != 0) { unsigned Opc = getLEArOpcode(Uses64BitFramePtr); - addRegOffset(BuildMI(MBB, MBBI, DL, TII.get(Opc), StackPtr), - FramePtr, false, LEAAmount); + MIRSP = BuildMI(MBB, MBBI, DL, TII.get(Opc), StackPtr); + addRegOffset(MIRSP, FramePtr, false, LEAAmount); --MBBI; } else { unsigned Opc = (Uses64BitFramePtr ? X86::MOV64rr : X86::MOV32rr); - BuildMI(MBB, MBBI, DL, TII.get(Opc), StackPtr) - .addReg(FramePtr); + MIRSP = BuildMI(MBB, MBBI, DL, TII.get(Opc), StackPtr).addReg(FramePtr); --MBBI; } + if (CangjieFunc) { + // If we generate code for cangjie, mark the 'rsp' register recovery + // instruction as an epilogue instruction. + MIRSP.setMIExtFlag(MachineInstr::EpilogueIns); + } } else if (NumBytes) { // Adjust stack pointer back: ESP += numbytes. emitSPUpdate(MBB, MBBI, DL, NumBytes, /*InEpilogue=*/true); @@ -2377,6 +2479,29 @@ void X86FrameLowering::emitEpilogue(MachineFunction &MF, BuildMI(MBB, Terminator, DL, TII.get(X86::TILERELEASE)); } +StackOffset X86FrameLowering::getFrameIndexRefForCJ(const MachineFunction &MF, + int FI, + Register &FrameReg) const { + if (MF.getTarget().getMCAsmInfo()->usesWindowsCFI()) { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + + bool IsFixed = MFI.isFixedObjectIndex(FI); + // We can't calculate offset from frame pointer if the stack is realigned, + // so enforce usage of stack/base pointer. The base pointer is used when we + // have dynamic allocas in addition to dynamic realignment. + if (TRI->hasBasePointer(MF)) + FrameReg = IsFixed ? TRI->getFramePtr() : TRI->getBaseRegister(); + else if (TRI->hasStackRealignment(MF)) + FrameReg = IsFixed ? TRI->getFramePtr() : TRI->getStackRegister(); + else + FrameReg = TRI->getFrameRegister(MF); + + int Offset = MFI.getObjectOffset(FI) - getOffsetOfLocalArea(); + return StackOffset::getFixed(Offset + 8); // 8: Skip the saved EBP. + } + return getFrameIndexReference(MF, FI, FrameReg); +} + StackOffset X86FrameLowering::getFrameIndexReference(const MachineFunction &MF, int FI, Register &FrameReg) const { @@ -2605,6 +2730,17 @@ bool X86FrameLowering::assignCalleeSavedSpillSlots( } if (hasFP(MF)) { + bool IsWin64 = MF.getTarget().getMCAsmInfo()->usesWindowsCFI(); + if (MF.getFunction().hasCangjieGC() && !IsWin64) { + if (STI.isTargetMachO()) { + // 2: Need 2 slotsize in macos: StorePC and .Lmethod_desc.xxx + SpillSlotOffset -= SlotSize * 2; + MFI.CreateFixedSpillStackObject(SlotSize * 2, SpillSlotOffset); + } else { + SpillSlotOffset -= SlotSize; + MFI.CreateFixedSpillStackObject(SlotSize, SpillSlotOffset); + } + } // emitPrologue always spills frame register the first thing. SpillSlotOffset -= SlotSize; MFI.CreateFixedSpillStackObject(SlotSize, SpillSlotOffset); diff --git a/llvm/lib/Target/X86/X86FrameLowering.h b/llvm/lib/Target/X86/X86FrameLowering.h index 9b83fe77d..0f95417c8 100644 --- a/llvm/lib/Target/X86/X86FrameLowering.h +++ b/llvm/lib/Target/X86/X86FrameLowering.h @@ -72,6 +72,9 @@ public: MachineBasicBlock::iterator MBBI, const DebugLoc &DL, bool IsPrologue) const; + const std::vector + getArgRegs(const MachineFunction &MF) const override; + /// emitProlog/emitEpilog - These methods insert prolog and epilog code into /// the function. void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override; @@ -110,6 +113,9 @@ public: StackOffset getFrameIndexReference(const MachineFunction &MF, int FI, Register &FrameReg) const override; + StackOffset getFrameIndexRefForCJ(const MachineFunction &MF, int FI, + Register &FrameReg) const override; + int getWin64EHFrameIndexRef(const MachineFunction &MF, int FI, Register &SPReg) const; StackOffset getFrameIndexReferenceSP(const MachineFunction &MF, int FI, diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index cd45c4825..d94335cc4 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -4408,12 +4408,21 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, // Get a count of how many bytes are to be pushed on the stack. unsigned NumBytes = CCInfo.getAlignedCallFrameSize(); + unsigned CallFrameSizeForCJFFI = NumBytes; if (IsSibcall) // This is a sibcall. The memory operands are available in caller's // own caller's stack. NumBytes = 0; - else if (IsGuaranteeTCO && canGuaranteeTCO(CallConv)) + else if (IsGuaranteeTCO && canGuaranteeTCO(CallConv)) { NumBytes = GetAlignedArgumentStackSize(NumBytes, DAG); + CallFrameSizeForCJFFI = NumBytes; + } + auto *GANode = dyn_cast(Callee.getNode()); + if (GANode != nullptr) { + auto *CalleeFunc = dyn_cast(GANode->getGlobal()); + CCInfo.AddAlignedCallFrameSizeMetaDataForCJFFI( + const_cast(CalleeFunc), CallFrameSizeForCJFFI); + } int FPDiff = 0; if (isTailCall && @@ -4433,6 +4442,10 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, unsigned NumBytesToPush = NumBytes; unsigned NumBytesToPop = NumBytes; + if (NumBytesToPop != 0) { + MF.setNeedCangjieReloadSP(true); + } + // If we have an inalloca argument, all stack space has already been allocated // for us and be right at the top of the stack. We don't support multiple // arguments passed in memory when using inalloca. @@ -20469,6 +20482,8 @@ static SDValue LowerToTLSExecModel(GlobalAddressSDNode *GA, SelectionDAG &DAG, // Most TLS accesses are not RIP relative, even on x86-64. One exception is // initialexec. unsigned WrapperKind = X86ISD::Wrapper; + if ((GA->getGlobal()->getName().compare("mutatorInfo") == 0)) + model = TLSModel::LocalExec; if (model == TLSModel::LocalExec) { OperandFlags = is64Bit ? X86II::MO_TPOFF : X86II::MO_NTPOFF; } else if (model == TLSModel::InitialExec) { @@ -20506,8 +20521,40 @@ static SDValue LowerToTLSExecModel(GlobalAddressSDNode *GA, SelectionDAG &DAG, return DAG.getNode(ISD::ADD, dl, PtrVT, ThreadPointer, Offset); } -SDValue -X86TargetLowering::LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const { +static SDValue +LowerToTLSGeneralDynamicModelCJMutator(const GlobalAddressSDNode *GA, + SelectionDAG &DAG, const EVT PtrVT) { + SDLoc dl(GA); + Value *Ptr = Constant::getNullValue(Type::getInt8PtrTy(*DAG.getContext(), + 257)); // 64 bit %fs + SDValue ThreadPointer = + DAG.getLoad(PtrVT, dl, DAG.getEntryNode(), DAG.getIntPtrConstant(0, dl), + MachinePointerInfo(Ptr)); + // -16 for mutatorInfo total size. + SDValue Offset = DAG.getConstant(0xfffffffffffffff0, dl, PtrVT); + + return DAG.getNode(ISD::ADD, dl, PtrVT, ThreadPointer, Offset); +} + +static SDValue LowerToTLSGeneralDynamicModel(const X86Subtarget &Subtarget, + const GlobalValue *GV, + GlobalAddressSDNode *GA, + SelectionDAG &DAG, + const EVT PtrVT) { + if (Subtarget.is64Bit()) { + if (GV->getName().compare("mutatorInfo") == 0) { + return LowerToTLSGeneralDynamicModelCJMutator(GA, DAG, PtrVT); + } + if (Subtarget.isTarget64BitLP64()) { + return LowerToTLSGeneralDynamicModel64(GA, DAG, PtrVT); + } + return LowerToTLSGeneralDynamicModelX32(GA, DAG, PtrVT); + } + return LowerToTLSGeneralDynamicModel32(GA, DAG, PtrVT); +} + +SDValue X86TargetLowering::LowerGlobalTLSAddress(SDValue Op, + SelectionDAG &DAG) const { GlobalAddressSDNode *GA = cast(Op); @@ -20521,20 +20568,15 @@ X86TargetLowering::LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const { if (Subtarget.isTargetELF()) { TLSModel::Model model = DAG.getTarget().getTLSModel(GV); switch (model) { - case TLSModel::GeneralDynamic: - if (Subtarget.is64Bit()) { - if (Subtarget.isTarget64BitLP64()) - return LowerToTLSGeneralDynamicModel64(GA, DAG, PtrVT); - return LowerToTLSGeneralDynamicModelX32(GA, DAG, PtrVT); - } - return LowerToTLSGeneralDynamicModel32(GA, DAG, PtrVT); - case TLSModel::LocalDynamic: - return LowerToTLSLocalDynamicModel(GA, DAG, PtrVT, Subtarget.is64Bit(), - Subtarget.isTarget64BitLP64()); - case TLSModel::InitialExec: - case TLSModel::LocalExec: - return LowerToTLSExecModel(GA, DAG, PtrVT, model, Subtarget.is64Bit(), - PositionIndependent); + case TLSModel::GeneralDynamic: + return LowerToTLSGeneralDynamicModel(Subtarget, GV, GA, DAG, PtrVT); + case TLSModel::LocalDynamic: + return LowerToTLSLocalDynamicModel(GA, DAG, PtrVT, Subtarget.is64Bit(), + Subtarget.isTarget64BitLP64()); + case TLSModel::InitialExec: + case TLSModel::LocalExec: + return LowerToTLSExecModel(GA, DAG, PtrVT, model, Subtarget.is64Bit(), + PositionIndependent); } llvm_unreachable("Unknown TLS model."); } @@ -24042,8 +24084,8 @@ static SDValue LowerAndToBT(SDValue And, ISD::CondCode CC, const SDLoc &dl, if ((!isUInt<32>(AndRHSVal) || (OptForSize && !isUInt<8>(AndRHSVal))) && isPowerOf2_64(AndRHSVal)) { Src = AndLHS; - BitNo = DAG.getConstant(Log2_64_Ceil(AndRHSVal), dl, - Src.getValueType()); + BitNo = + DAG.getConstant(Log2_64_Ceil(AndRHSVal), dl, Src.getValueType()); } } } diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td index 6124755ca..9ff07c45e 100644 --- a/llvm/lib/Target/X86/X86InstrCompiler.td +++ b/llvm/lib/Target/X86/X86InstrCompiler.td @@ -31,6 +31,15 @@ let hasSideEffects = 0, isNotDuplicable = 1, Uses = [ESP, SSP], def MOVPC32r : Ii32<0xE8, Pseudo, (outs GR32:$reg), (ins i32imm:$label), "", []>; +// used by cangjie stack back calling convention: +// call $next_inst +// do nothing +// +// pop rip +// ret +let hasSideEffects = 0, isNotDuplicable = 1, SchedRW = [WriteJump] in + def PUSHPC64 : Ii64<0xE9, Pseudo, (outs), (ins i64imm:$label), "", []>; + // ADJCALLSTACKDOWN/UP implicitly use/def ESP 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. diff --git a/llvm/lib/Target/X86/X86InstrInfo.cpp b/llvm/lib/Target/X86/X86InstrInfo.cpp index 74ef831e1..df2ec65fa 100644 --- a/llvm/lib/Target/X86/X86InstrInfo.cpp +++ b/llvm/lib/Target/X86/X86InstrInfo.cpp @@ -725,6 +725,10 @@ unsigned X86InstrInfo::isStoreToStackSlotPostFE(const MachineInstr &MI, return 0; } +bool X86InstrInfo::isLEA64r(const MachineInstr &MI) const { + return MI.getOpcode() == X86::LEA64r; +} + /// Return true if register is PIC base; i.e.g defined by X86::MOVPC32r. static bool regIsPICBase(Register BaseReg, const MachineRegisterInfo &MRI) { // Don't waste compile time scanning use-def chains of physregs. diff --git a/llvm/lib/Target/X86/X86InstrInfo.h b/llvm/lib/Target/X86/X86InstrInfo.h index 81729e361..ea96d3ac9 100644 --- a/llvm/lib/Target/X86/X86InstrInfo.h +++ b/llvm/lib/Target/X86/X86InstrInfo.h @@ -240,6 +240,8 @@ public: unsigned isStoreToStackSlotPostFE(const MachineInstr &MI, int &FrameIndex) const override; + bool isLEA64r(const MachineInstr &MI) const override; + bool isReallyTriviallyReMaterializable(const MachineInstr &MI) const override; void reMaterialize(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, Register DestReg, unsigned SubIdx, diff --git a/llvm/lib/Target/X86/X86MCInstLower.cpp b/llvm/lib/Target/X86/X86MCInstLower.cpp index 3fbdb18a0..710e386b3 100644 --- a/llvm/lib/Target/X86/X86MCInstLower.cpp +++ b/llvm/lib/Target/X86/X86MCInstLower.cpp @@ -17,6 +17,7 @@ #include "MCTargetDesc/X86ShuffleDecode.h" #include "MCTargetDesc/X86TargetStreamer.h" #include "X86AsmPrinter.h" +#include "X86MachineFunctionInfo.h" #include "X86RegisterInfo.h" #include "X86ShuffleDecodeConstantPool.h" #include "X86Subtarget.h" @@ -24,6 +25,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/iterator_range.h" #include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineModuleInfoImpls.h" #include "llvm/CodeGen/MachineOperand.h" @@ -31,6 +33,7 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/Mangler.h" +#include "llvm/IR/Statepoint.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCContext.h" @@ -44,6 +47,7 @@ #include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCSymbolELF.h" #include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Target/TargetLoweringObjectFile.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/Instrumentation/AddressSanitizer.h" @@ -51,7 +55,12 @@ #include using namespace llvm; - +// we use gcc as compiler for llvm building in windows, thus for fp16 conversion, still need calling-conv adaption. +static cl::opt DisableFP16TempConversion("disable-fp16-tempconversion", + cl::init(false), cl::Hidden); +namespace llvm { +extern cl::opt CJPipeline; +} namespace { /// X86MCInstLower - This class is used to lower an MachineInstr into an MCInst. @@ -1225,31 +1234,361 @@ static void emitX86Nops(MCStreamer &OS, unsigned NumBytes, } } +// movq Offset(%r15), %rax +void X86AsmPrinter::emitGetCJTLSData(int64_t Offset) { + MCInst LoadFieldInst; + LoadFieldInst.setOpcode(X86::MOV64rm); + LoadFieldInst.addOperand(MCOperand::createReg(X86::RAX)); + LoadFieldInst.addOperand(MCOperand::createReg(X86::R15)); + LoadFieldInst.addOperand(MCOperand::createImm(1)); + LoadFieldInst.addOperand(MCOperand::createReg(0)); + LoadFieldInst.addOperand(MCOperand::createImm(Offset)); + LoadFieldInst.addOperand(MCOperand::createReg(0)); + OutStreamer->emitInstruction(LoadFieldInst, getSubtargetInfo()); +} + +void X86AsmPrinter::emitStackCmp(const MachineInstr &MI) { + // The MSB(Most Significant Bit)is used to mark + // whether the stack needs to be recovered. + // Default is 0, and that means stack need to be recovered. + // 1 means stack do not need to be recovered. + if ((MI.peekCJStackSize() >> 31) & 1) { + // 0x7fffffff: Make MSB to be 0 to get the real size. + unsigned AddSize = MI.peekCJStackSize() & 0x7fffffff; + MCInst AddInst; + AddInst.setOpcode(X86::ADD64ri32); + AddInst.addOperand(MCOperand::createReg(X86::RAX)); + AddInst.addOperand(MCOperand::createReg(X86::RAX)); + AddInst.addOperand(MCOperand::createImm(AddSize)); + AddInst.addOperand(MCOperand::createImm(0)); + OutStreamer->emitInstruction(AddInst, getSubtargetInfo()); + } + MCInst CmpInst; + CmpInst.setOpcode(X86::CMP64rr); + CmpInst.addOperand(MCOperand::createReg(X86::RSP)); + CmpInst.addOperand(MCOperand::createReg(X86::RAX)); + OutStreamer->emitInstruction(CmpInst, getSubtargetInfo()); +} + +// subq numbytes, %rsp +// movq 40(%r15), %rax # stack.check +// cmpq %rax, %rsp +// jbe .Lstack.overflow: +// .Lstack.check.end: +void X86AsmPrinter::emitCJStackCheck(const MachineInstr &MI) { + MCContext &Ctx = MF->getContext(); + // stack.check start + emitGetCJTLSData(getProtectAddrOffsetInCJTLS()); + emitStackCmp(MI); + + MCSymbol *StackOverflowSym = Ctx.createTempSymbol("stack.overflow"); + MCInst JmpInst; + JmpInst.setOpcode(X86::JCC_1); + JmpInst.addOperand(MCOperand::createExpr(MCSymbolRefExpr::create( + StackOverflowSym, MCSymbolRefExpr::VK_None, Ctx))); + JmpInst.addOperand(MCOperand::createImm(X86::COND_BE)); + OutStreamer->emitInstruction(JmpInst, getSubtargetInfo()); + MCSymbol *StackCheckEndSym = Ctx.createTempSymbol("stack.check.end"); + OutStreamer->emitLabel(StackCheckEndSym); + StackCheckMap[&MI] = std::make_pair(StackOverflowSym, StackCheckEndSym); +} + +// .Lsofe.end.xxx: +// addq $AddSize, %rsp +// callq CJ_MCC_ThrowStackOverflowError +int X86AsmPrinter::emitSOFECall(const MachineInstr &MI) { + MCContext &Ctx = MF->getContext(); + auto SC = StackCheckMap[&MI]; + MCSymbol *StackOverflowSym = SC.first; + OutStreamer->emitLabel(StackOverflowSym); + + // This DebugInstrNum interface is used to store the frame size temporarily + unsigned AddSize = MI.peekCJStackSize(); + const Triple &TT = getSubtargetInfo().getTargetTriple(); + if (TT.isOSWindows()) { + // The MSB(Most Significant Bit)is used to mark + // whether the stack needs to be recovered. + // Default is 0, and that means stack need to be recovered. + // 1 means stack do not need to be recovered. + if ((AddSize >> 31) & 1) { + X86MachineFunctionInfo *X86FI = MF->getInfo(); + const int64_t CalleeSaveSize = X86FI->getCalleeSavedFrameSize(); + unsigned TailCallArgReserveSize = -X86FI->getTCReturnAddrDelta(); + AddSize = CalleeSaveSize + TailCallArgReserveSize; + } + } else if (TT.isOSLinux()) { + AddSize = AddSize - 8; // 8: address align(16) + } + + if (AddSize > 0) { + MCInst Add; + Add.setOpcode(X86::ADD64ri32); + Add.addOperand(MCOperand::createReg(X86::RSP)); + Add.addOperand(MCOperand::createReg(X86::RSP)); + Add.addOperand(MCOperand::createImm(AddSize)); + Add.addOperand(MCOperand::createImm(0)); + OutStreamer->emitInstruction(Add, getSubtargetInfo()); + + bool IsWin64Prologue = MF->getTarget().getMCAsmInfo()->usesWindowsCFI(); + bool NeedsDwarfCFI = !IsWin64Prologue && MF->needsFrameMoves(); + if (NeedsDwarfCFI) { + // Add CFI directive for debugging + const TargetRegisterInfo *TRI = MF->getSubtarget().getRegisterInfo(); + unsigned DwarfFramePtr = TRI->getDwarfRegNum(X86::RSP, true); + // 32: left frame size of current function + OutStreamer->emitCFIDefCfa(DwarfFramePtr, 32); + } + } + + MCInst CallInst; + CallInst.setOpcode(X86::CALL64pcrel32); + MCSymbol *Sym; + if (MF->getTarget().getTargetTriple().isOSBinFormatMachO()) { + Sym = Ctx.getOrCreateSymbol("_CJ_MCC_ThrowStackOverflowError"); + } else { + Sym = Ctx.getOrCreateSymbol("CJ_MCC_ThrowStackOverflowError"); + } + CallInst.addOperand(MCOperand::createExpr( + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, Ctx))); + OutStreamer->emitInstruction(CallInst, getSubtargetInfo()); + // 2: instruction nums. + return 2; +} + +// .Lstack.overflow: +// addq numbytes, %rsp +// mov allocabytes, %rax +// callq CJ_MCC_StackGrowStub +// .LTemp +// subq numbytes, %rsp +// jmp .Lstack.check.end +int X86AsmPrinter::emitStackGrow(const MachineInstr &MI) { + MCContext &Ctx = MF->getContext(); + auto SC = StackCheckMap[&MI]; + MCSymbol *StackOverflowSym = SC.first; + MCSymbol *StackCheckEndSym = SC.second; + OutStreamer->emitLabel(StackOverflowSym); + + // Before invoking the StackGrow function, you need to restore the SP + // to ensure that the SP does not exceed the threshold. + X86MachineFunctionInfo *X86FI = MF->getInfo(); + const int64_t CalleeSaveSize = X86FI->getCalleeSavedFrameSize(); + + unsigned NumBytes = MI.peekCJStackSize(); + bool IsWindowsAndNoRecoverd = + getSubtargetInfo().getTargetTriple().isOSWindows() && + ((NumBytes >> 31) & 0x1); + if (IsWindowsAndNoRecoverd) { + // The MSB(Most Significant Bit)is used to mark + // whether the stack needs to be recovered. + // Default is 0, and that means stack need to be recovered. + // 1 means stack do not need to be recovered. + NumBytes = NumBytes & 0x7fffffff; + } else { + assert(NumBytes >= CalleeSaveSize && + "frame size is less than callee saved size"); + NumBytes = NumBytes - CalleeSaveSize; + } + + // FrameSize: Total stack size of the current function. + int64_t FrameSize = MF->getFrameInfo().getStackSize() + 8; // 8: ra slot + // Current Allocated Stack Size. + int64_t AllocaSize = FrameSize - NumBytes; + bool NeedAlign = false; + if (AllocaSize % 16) { + AllocaSize += 8; // 8: sp address align(16) + NeedAlign = true; + } + + if (!IsWindowsAndNoRecoverd) { + NumBytes = FrameSize - AllocaSize; // will unwind stack size + if (NumBytes > 0) { + MCInst Add; + Add.setOpcode(X86::ADD64ri32); + Add.addOperand(MCOperand::createReg(X86::RSP)); + Add.addOperand(MCOperand::createReg(X86::RSP)); + Add.addOperand(MCOperand::createImm(NumBytes)); + Add.addOperand(MCOperand::createImm(0)); + OutStreamer->emitInstruction(Add, getSubtargetInfo()); + } + } else { + if (NeedAlign) { + MCInst PushInst; // Use push to align sp to 16 bytes. + PushInst.setOpcode(X86::PUSH64i32); + PushInst.addOperand(MCOperand::createImm(AllocaSize)); + OutStreamer->emitInstruction(PushInst, getSubtargetInfo()); + } + } + + MCInst StoreAllocaSize; + StoreAllocaSize.setOpcode(X86::MOV64ri); + StoreAllocaSize.addOperand(MCOperand::createReg(X86::RAX)); + StoreAllocaSize.addOperand(MCOperand::createImm(AllocaSize)); + OutStreamer->emitInstruction(StoreAllocaSize, getSubtargetInfo()); + + MCInst CallInst; + CallInst.setOpcode(X86::CALL64pcrel32); + MCSymbol *Sym = Ctx.getOrCreateSymbol("CJ_MCC_StackGrowStub"); + CallInst.addOperand(MCOperand::createExpr( + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, Ctx))); + OutStreamer->emitInstruction(CallInst, getSubtargetInfo()); + SM.recordCJStackMap(MI, true); + + // Restore the SP status after stack expansion. + if (!IsWindowsAndNoRecoverd) { + if (NumBytes > 0) { + MCInst Sub; + Sub.setOpcode(X86::SUB64ri32); + Sub.addOperand(MCOperand::createReg(X86::RSP)); + Sub.addOperand(MCOperand::createReg(X86::RSP)); + Sub.addOperand(MCOperand::createImm(NumBytes)); + Sub.addOperand(MCOperand::createImm(0)); + OutStreamer->emitInstruction(Sub, getSubtargetInfo()); + } + } else { + if (NeedAlign) { + MCInst PopInst; // Use pop to restore sp. + PopInst.setOpcode(X86::POP64r); + PopInst.addOperand(MCOperand::createReg(X86::RAX)); + OutStreamer->emitInstruction(PopInst, getSubtargetInfo()); + } + } + const MCSymbolRefExpr *StackCheckEndExpr = + MCSymbolRefExpr::create(StackCheckEndSym, MCSymbolRefExpr::VK_None, Ctx); + MCInst JccInst; + JccInst.setOpcode(X86::JMP_1); + JccInst.addOperand(MCOperand::createExpr(StackCheckEndExpr)); + OutStreamer->emitInstruction(JccInst, getSubtargetInfo()); + + // 5: instruction nums. + return 5; +} + +// load tls data +// movq (%rax), %eax +void X86AsmPrinter::emitGcStateCheck() { + emitGetCJTLSData(getMutatorOffsetInCJTLS()); + MCInst LoadPollingPageInst; + LoadPollingPageInst.setOpcode(X86::MOV32rm); + LoadPollingPageInst.addOperand(MCOperand::createReg(X86::EAX)); + LoadPollingPageInst.addOperand(MCOperand::createReg(X86::RAX)); + LoadPollingPageInst.addOperand(MCOperand::createImm(1)); + LoadPollingPageInst.addOperand(MCOperand::createReg(0)); + LoadPollingPageInst.addOperand(MCOperand::createImm(0)); // GCPhase offset + LoadPollingPageInst.addOperand(MCOperand::createReg(0)); + OutStreamer->emitInstruction(LoadPollingPageInst, getSubtargetInfo()); +} + +// load tls data +// cmpq $0, %rax +// jne Label +void X86AsmPrinter::emitCangjieSafepoint(const MachineInstr &MI) { + emitGetCJTLSData(getSafepointCheckAddrOffsetInCJTLS()); + // create label + auto &Ctx = OutStreamer->getContext(); + MCSymbol *MILabel = Ctx.createTempSymbol(); + MCSymbol *MILabel1 = Ctx.createTempSymbol(); + const MCSymbolRefExpr *MILabelExpr = + MCSymbolRefExpr::create(MILabel, OutContext); + MCInst CmpInst; + CmpInst.setOpcode(X86::CMP64ri32); + CmpInst.addOperand(MCOperand::createReg(X86::RAX)); + CmpInst.addOperand(MCOperand::createImm(0)); + OutStreamer->emitInstruction(CmpInst, getSubtargetInfo()); + MCInst JccInst; + JccInst.setOpcode(X86::JCC_1); + JccInst.addOperand(MCOperand::createExpr(MILabelExpr)); + JccInst.addOperand(MCOperand::createImm(X86::COND_NE)); + OutStreamer->emitInstruction(JccInst, getSubtargetInfo()); + + OutStreamer->emitLabel(MILabel1); + SafepointStackMap.push_back(std::make_tuple(&MI, MILabel, MILabel1)); +} + +// mov CJ_MCC_HandleSafepoint.CJStubGV(%rip), %r9 +// call %r9 +// jmp Label1 +int X86AsmPrinter::emitSafePointDirectCall(unsigned Index) { + auto *AddrGV = MF->getFunction().getParent()-> + getGlobalVariable("CJ_MCC_HandleSafepoint.CJStubGV", true); + MachineOperand MOAddr = MachineOperand::CreateGA(AddrGV, 0); + X86MCInstLower MCInstLowering(*MF, *this); + const MCSymbolRefExpr *Sym = + MCSymbolRefExpr::create(MCInstLowering.GetSymbolFromOperand(MOAddr), + OutStreamer->getContext()); + auto SS = SafepointStackMap[Index]; + MCSymbol *MILabel = std::get<1>(SS); + MCSymbol *MILabel1 = std::get<2>(SS); + OutStreamer->emitLabel(MILabel); + + MCInst MovGVToRAX = MCInstBuilder(X86::MOV64rm) + .addReg(X86::RAX) + .addReg(X86::RIP) + .addImm(1) + .addReg(0) + .addExpr(Sym) + .addReg(0); + OutStreamer->emitInstruction(MovGVToRAX, getSubtargetInfo()); + MCInst CallSafepointInst; + CallSafepointInst.setOpcode(X86::CALL64r); + CallSafepointInst.addOperand(MCOperand::createReg(X86::RAX)); + OutStreamer->emitInstruction(CallSafepointInst, getSubtargetInfo()); + SM.recordCJStackMap(*std::get<0>(SS), true); + + const MCSymbolRefExpr *MILabelExpr = + MCSymbolRefExpr::create(MILabel1, OutContext); + MCInst JccInst; + JccInst.setOpcode(X86::JMP_1); + JccInst.addOperand(MCOperand::createExpr(MILabelExpr)); + OutStreamer->emitInstruction(JccInst, getSubtargetInfo()); + // 3: instruction nums. + return 3; +} + void X86AsmPrinter::LowerSTATEPOINT(const MachineInstr &MI, X86MCInstLower &MCIL) { assert(Subtarget->is64Bit() && "Statepoint currently only supports X86-64"); NoAutoPaddingScope NoPadScope(*OutStreamer); - StatepointOpers SOpers(&MI); + // Lower call target and choose correct opcode + const MachineOperand &CallTarget = SOpers.getCallTarget(); + if (CJPipeline) { + uint64_t ID = SOpers.getID(); + if (ID == CJStatepointID::Safepoint) { + emitCangjieSafepoint(MI); + return; + } + if (ID == CJStatepointID::StackCheck) { + emitCangjieStackCheck(MI); + return; + } + if (ID == CJStatepointID::NewArrayFast) { + emitCJNewArrayFastPath(MI, CallTarget); + return; + } + } + if (unsigned PatchBytes = SOpers.getNumPatchBytes()) { emitX86Nops(*OutStreamer, PatchBytes, Subtarget); } else { - // Lower call target and choose correct opcode - const MachineOperand &CallTarget = SOpers.getCallTarget(); MCOperand CallTargetMCOp; unsigned CallOpcode; switch (CallTarget.getType()) { case MachineOperand::MO_GlobalAddress: - case MachineOperand::MO_ExternalSymbol: + case MachineOperand::MO_ExternalSymbol: { CallTargetMCOp = MCIL.LowerSymbolOperand( CallTarget, MCIL.GetSymbolFromOperand(CallTarget)); CallOpcode = X86::CALL64pcrel32; + if (tryEmitCangjieSpecificCallByMOSym(&MI, CallTarget, CallOpcode)) { + return; + } // Currently, we only support relative addressing with statepoints. // Otherwise, we'll need a scratch register to hold the target // address. You'll fail asserts during load & relocation if this // symbol is to far away. (TODO: support non-relative addressing) break; + } case MachineOperand::MO_Immediate: CallTargetMCOp = MCOperand::createImm(CallTarget.getImm()); CallOpcode = X86::CALL64pcrel32; @@ -2412,6 +2751,57 @@ static void addConstantComments(const MachineInstr *MI, } } +void X86AsmPrinter::tryDoAdaptionForFP16InCJ(const MachineInstr *MI, + bool &IsTruncToFP16, + bool IsWindows) { + if (DisableFP16TempConversion || !CJPipeline) { + return; + } + if (!MI->isCall()) { + return; + } + + unsigned Opcode = MI->getOpcode(); + if (Opcode == TargetOpcode::STATEPOINT) { + return; + } + + if (MI->getNumOperands() == 0) { + return; + } + + const MachineOperand &MOSym = MI->getOperand(0); + if (!MOSym.isSymbol()) { + return; + } + + StringRef CalleeName = StringRef(MOSym.getSymbolName()); + if (CalleeName.isTruncToFP16Func()) { + IsTruncToFP16 = true; + return; + } + if (CalleeName.isExtendFromFP16Func()) { + if (IsWindows) { + // move arg0 from xmm0 to ecx to do the fp16 + // calling-convetion adaption in windows. + MCInst MovXMM0ToECX = MCInstBuilder(X86::PEXTRWrr) + .addReg(X86::ECX) + .addReg(X86::XMM0) + .addImm(0); + EmitAndCountInstruction(MovXMM0ToECX); + } else { + // move arg0 from xmm0 to edi to do the fp16 + // calling-convetion adaption in macos. + MCInst MovXMM0ToEDI = MCInstBuilder(X86::PEXTRWrr) + .addReg(X86::EDI) + .addReg(X86::XMM0) + .addImm(0); + EmitAndCountInstruction(MovXMM0ToEDI); + } + return; + } +} + void X86AsmPrinter::emitInstruction(const MachineInstr *MI) { // FIXME: Enable feature predicate checks once all the test pass. // X86_MC::verifyInstructionPredicates(MI->getOpcode(), @@ -2436,11 +2826,22 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) { if (MI->getAsmPrinterFlags() & X86::AC_EVEX_2_VEX) OutStreamer->AddComment("EVEX TO VEX Compression ", false); } - + // try to process cangjie specific call + if (tryEmitCangjieSpecificCall(MI)) { + return; + } // Add comments for values loaded from constant pool. if (OutStreamer->isVerboseAsm()) addConstantComments(MI, *OutStreamer); + bool IsTruncToFP16 = false; + const Triple TT(getSubtargetInfo().getTargetTriple()); + if (TT.isOSWindows()) { + tryDoAdaptionForFP16InCJ(MI, IsTruncToFP16, true); + } else if (TT.isOSBinFormatMachO()) { + tryDoAdaptionForFP16InCJ(MI, IsTruncToFP16, false); + } + switch (MI->getOpcode()) { case TargetOpcode::DBG_VALUE: llvm_unreachable("Should be handled target independently"); @@ -2552,6 +2953,51 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) { } return; } + case X86::MOV64ri: { + const MachineOperand &MOSym = MI->getOperand(1); + if (!MOSym.isGlobal()) + break; + + const auto *Callee = dyn_cast(MOSym.getGlobal()); + if (Callee == nullptr) + break; + + if (Callee->isCangjieStackCheck() || Callee->isCangjieSafePoint()) { + return; + } + break; + } + case X86::PUSHPC64: { + // call $nextInst for cangjie + MCSymbol *NextIns = OutContext.createTempSymbol("StorePC", true); + EmitAndCountInstruction( + MCInstBuilder(X86::CALL64pcrel32) + .addExpr(MCSymbolRefExpr::create(NextIns, OutContext))); + OutStreamer->emitLabel(NextIns); + if (MF->getTarget().getTargetTriple().isOSBinFormatMachO()) { + Function &Func = MF->getFunction(); + if (!Func.hasFnAttribute("leaf-function")) { + Metadata *MD = Func.getParent()->getModuleFlag("Cangjie_PACKAGE_ID"); + if (MD == nullptr) + report_fatal_error("There is not cangjie package id in module!"); + StringRef PACKAGEID = dyn_cast(MD)->getString(); + MCSymbol *DescSymbol = OutContext.getOrCreateSymbol( + ".Lmethod_desc." + PACKAGEID + "._" + MF->getName()); + EmitAndCountInstruction( + MCInstBuilder(X86::LEA64r) + .addReg(X86::R10) + .addReg(X86::RIP) + .addImm(0) + .addReg(0) + .addExpr(MCSymbolRefExpr::create(DescSymbol, OutContext)) + .addReg(0)); + EmitAndCountInstruction(MCInstBuilder(X86::PUSH64r).addReg(X86::R10)); + } else { + EmitAndCountInstruction(MCInstBuilder(X86::PUSH64r).addReg(X86::RIP)); + } + } + return; + } case X86::ADD32ri: { // Lower the MO_GOT_ABSOLUTE_ADDRESS form of ADD32ri. @@ -2686,8 +3132,596 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) { SMShadowTracker.emitShadowPadding(*OutStreamer, getSubtargetInfo()); // Then emit the call OutStreamer->emitInstruction(TmpInst, getSubtargetInfo()); + + // move rslt from xmm0 to eax to do the fp16 calling-convetion adaption + if (IsTruncToFP16 && !TT.isOSWindows()) { + MCInst MovXMM0ToEAX = MCInstBuilder(X86::PINSRWrr) + .addReg(X86::XMM0) + .addReg(X86::XMM0) + .addReg(X86::EAX) + .addImm(0); + EmitAndCountInstruction(MovXMM0ToEAX); + } return; } EmitAndCountInstruction(TmpInst); } + +void X86AsmPrinter::reloadSPForEpilogue(MachineFunction &MF) { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + uint64_t StackSize = MFI.getStackSize(); + bool Is64Bit = MF.getSubtarget().is64Bit(); + unsigned SubOpc = Is64Bit ? X86::SUB64ri32 : X86::SUB32ri; + unsigned MovOpc = Is64Bit ? X86::MOV64rr : X86::MOV32rr; + unsigned SPReg = Is64Bit ? X86::RSP : X86::ESP; + unsigned BPReg = Is64Bit ? X86::RBP : X86::EBP; + unsigned SlotSize = Is64Bit ? 8 : 4; + + EmitAndCountInstruction(MCInstBuilder(MovOpc).addReg(SPReg).addReg(BPReg)); + + bool IsWin64Prologue = MF.getTarget().getMCAsmInfo()->usesWindowsCFI(); + // assume MF HasFP == true + if (IsWin64Prologue) { + uint64_t FrameSize = StackSize - SlotSize; + X86MachineFunctionInfo *X86FI = MF.getInfo(); + uint64_t NumBytes = FrameSize - X86FI->getCalleeSavedFrameSize(); + + // Win64 ABI has a less restrictive limitation of 240; 128 works equally + // well and might require smaller successive adjustments. + const uint64_t Win64MaxSEHOffset = 128; + uint64_t SEHFrameOffset = std::min(NumBytes, Win64MaxSEHOffset); + // Win64 ABI requires 16-byte alignment for the UWOP_SET_FPREG opcode. + SEHFrameOffset &= -16; + + EmitAndCountInstruction(MCInstBuilder(SubOpc) + .addReg(SPReg) + .addReg(SPReg) + .addImm(SEHFrameOffset) + .addReg(0)); + } else { + EmitAndCountInstruction(MCInstBuilder(SubOpc) + .addReg(SPReg) + .addReg(SPReg) + .addImm(StackSize - SlotSize) + .addReg(0)); + } +} + +// if linux +// movq (%r15) %rdx get AllocBuffer +// movq (%rdx) %rdx get region ptr +// movq (%rdx) %rax rax = allocPtr +// movq 8(%rdx) %rcx rcx = limit +// leaq (%rax, %rsi) %r8 r8 = allocPtr + size +// cmpq %rcx %r8 allocPtr + size > limit ? +// jg LNewObjSlowPath +// movq %rdi (%rax) allocPtr->KlassInfo = Klass +// movq %r8 (%rdx) region->allocPtr = allocPtr + size +// <<<< hasFinalizer +// movq %rax %rdi arg0 = allocPtr +// callq MCC_OnFinalizerCreated +// >>>>> +// jmp LNewObjFin +// LNewObjSlowPath +// callq MCC_NewObjectStub@PLT +// LNewObjFin +// ----------------------------------------------- +// else if win64 +// movq (%r15) %r9 get AllocBuffer +// movq (%r9) %r9 get region ptr +// movq (%r9) %rax rax = allocPtr +// movq 8(%r9) %r10 r10 = limit +// leaq (%rax, %rdx) %r8 r8 = allocPtr + size +// cmpq %r10 %r8 allocPtr + size > limit ? +// jg LNewObjSlowPath +// movq %rcx (%rax) allocPtr->KlassInfo = Klass +// movq %r8 (%r9) region->allocPtr = allocPtr + size +// <<<< hasFinalizer +// movq %rax %rcx arg0 = allocPtr +// callq MCC_OnFinalizerCreated +// >>>>> +// jmp LNewObjFin +// LNewObjSlowPath +// callq MCC_NewObjectStub +// LNewObjFin +// endif +void X86AsmPrinter::emitMccNewObjectForCopyGC(ParamForEmitNewObj &Param) { + unsigned AllocBufferReg; + unsigned RegionInfoReg; + unsigned AllocPtrReg; + unsigned LimitReg; + unsigned TmpReg; + unsigned ArgReg0; + unsigned ArgReg1; + + if (!getSubtargetInfo().getTargetTriple().isOSWindows()) { + AllocBufferReg = X86::RDX; + RegionInfoReg = X86::RDX; + AllocPtrReg = X86::RAX; + LimitReg = X86::RCX; + TmpReg = X86::R8; + ArgReg0 = X86::RDI; + ArgReg1 = X86::RSI; + } else { + AllocBufferReg = X86::R9; + RegionInfoReg = X86::R9; + AllocPtrReg = X86::RAX; + LimitReg = X86::R10; + TmpReg = X86::R8; + ArgReg0 = X86::RCX; + ArgReg1 = X86::RDX; + } + MCInst AllocBuffer = MCInstBuilder(X86::MOV64rm) + .addReg(AllocBufferReg) + .addReg(X86::R15) + .addImm(1) + .addReg(0) + .addImm(getAllocBufferOffsetInCJTLS()) + .addReg(0); + EmitAndCountInstruction(AllocBuffer); + + // 0: regionPtr in AllocBuffer + MCInst GetRegionPtr = MCInstBuilder(X86::MOV64rm) + .addReg(RegionInfoReg) + .addReg(AllocBufferReg) + .addImm(1) + .addReg(0) + .addImm(0) + .addReg(0); + MCInst GetAllocPtr = MCInstBuilder(X86::MOV64rm) + .addReg(AllocPtrReg) + .addReg(RegionInfoReg) + .addImm(1) + .addReg(0) + .addImm(0) + .addReg(0); + MCInst GetLimit = MCInstBuilder(X86::MOV64rm) + .addReg(LimitReg) + .addReg(RegionInfoReg) + .addImm(1) + .addReg(0) + .addImm(8) + .addReg(0); + MCInst CalNewAllocPtr = MCInstBuilder(X86::LEA64r) + .addReg(TmpReg) + .addReg(AllocPtrReg) + .addImm(1) + .addReg(ArgReg1) + .addImm(0) + .addReg(0); + + MCInst CmpNewAllocPtrWithLimit = + MCInstBuilder(X86::CMP64rr).addReg(TmpReg).addReg(LimitReg); + + MCInst SetKlass = MCInstBuilder(X86::MOV64mr) + .addReg(AllocPtrReg) + .addImm(1) + .addReg(0) + .addImm(0) + .addReg(0) + .addReg(ArgReg0); + MCInst StoreNewAllocPtr = MCInstBuilder(X86::MOV64mr) + .addReg(RegionInfoReg) + .addImm(1) + .addReg(0) + .addImm(0) + .addReg(0) + .addReg(TmpReg); + + EmitAndCountInstruction(GetRegionPtr); + EmitAndCountInstruction(GetAllocPtr); + EmitAndCountInstruction(GetLimit); + EmitAndCountInstruction(CalNewAllocPtr); + EmitAndCountInstruction(CmpNewAllocPtrWithLimit); + EmitAndCountInstruction(Param.CallNewObjSlowPath); + EmitAndCountInstruction(SetKlass); + EmitAndCountInstruction(StoreNewAllocPtr); + if (Param.FastFuncName.equals("CJ_MCC_NewFinalizerFast")) { + MCInst CopyAllocPtrToArg0 = + MCInstBuilder(X86::MOV64rr).addReg(ArgReg0).addReg(AllocPtrReg); + MachineOperand FinalizerFunc(Param.MOSym); + auto *FinalizerGV = Param.MI->getMF()->getFunction().getParent()-> + getFunction("CJ_MCC_OnFinalizerCreated"); + FinalizerFunc.ChangeToGA(FinalizerGV, 0); + MCInst CallOnFinalizer = + MCInstBuilder(X86::JMP_1) + .addOperand( + Param.MCInstLowering.LowerMachineOperand(Param.MI, FinalizerFunc) + .getValue()); + EmitAndCountInstruction(CopyAllocPtrToArg0); + EmitAndCountInstruction(CallOnFinalizer); + } +} + +void X86AsmPrinter::emitMccNewObjectFastPath(const MachineInstr *MI, + const MachineOperand &MOSym, + unsigned int Opcode) { + StringRef FastFuncName = + (MOSym.getGlobal()->getName().find("Object") != StringRef::npos) + ? "CJ_MCC_NewObjectFast" + : "CJ_MCC_NewFinalizerFast"; + + X86MCInstLower MCInstLowering(*MF, *this); + MCInst CallNewObjSlowPath = MCInstBuilder(X86::JCC_1) + .addOperand(MCInstLowering.LowerMachineOperand(MI, MOSym).getValue()) + .addImm(X86::COND_G); + ParamForEmitNewObj Param(MI, MOSym, Opcode, MCInstLowering, + CallNewObjSlowPath, FastFuncName); + + emitMccNewObjectForCopyGC(Param); + SM.recordCJStackMap(*MI); +} + +void X86AsmPrinter::emitCJThrowException(const MachineInstr *MI, + const MachineOperand &MOSym, + unsigned int Opcode) { + X86MCInstLower MCInstLowering(*MF, *this); + MCInst CallThrowException = MCInstBuilder(Opcode).addOperand( + MCInstLowering.LowerMachineOperand(MI, MOSym).getValue()); + EmitAndCountInstruction(CallThrowException); + StackMaps::CallsiteInfo CSInfo; + SM.updateOrInsertFnInfo(CurrentFnSym, CSInfo); +} + +// linux: +// movq (%r15) %r9 get AllocBuffer +// movq (%r9) %r9 get region ptr +// movq (%r9) %rax rax = allocPtr +// movq 8(%r9) %r10 r10 = limit +// leaq (%rax, %rdx) %r8 r8 = allocPtr + size +// cmpq %r10 %r8 allocPtr + size > limit ? +// jg LNewArraySlowPath +// movq %rdi (%rax) allocPtr->KlassInfo = Klass +// movq %rsi 8(%rax) allocPtr->Length = ArrayLen +// movq %r8 (%r9) region->allocPtr = allocPtr + size +// jmp LNewArrayFin +// LNewArraySlowPath +// callq CJ_MCC_NewArray@PLT +// LNewArrayFin +// ----------------------------------------------- +// win64: +// movq (%r15) %r9 get AllocBuffer +// movq (%r9) %r9 get region ptr +// movq (%r9) %rax rax = allocPtr +// movq 8(%r9) %r10 r10 = limit +// leaq (%rax, %r8) %r11 r11 = allocPtr + size +// cmpq %r10 %r11 allocPtr + size > limit ? +// jg LNewArraySlowPath +// movq %rcx (%rax) allocPtr->KlassInfo = Klass +// movq %rdx 8(%rax) allocPtr->Length = ArrayLen +// movq %r11 (%r9) region->allocPtr = allocPtr + size +// jmp LNewArrayFin +// LNewArraySlowPath +// callq CJ_MCC_NewArray@PLT +// LNewArrayFin +void X86AsmPrinter::emitCJNewArrayFastPath(const MachineInstr &MI, + const MachineOperand &MOSym) { + if (MI.getOpcode() != TargetOpcode::STATEPOINT) { + report_fatal_error("New Array Must be in Statepoint"); + } + + MCSymbol *LFast = OutContext.createTempSymbol("NewArrayFastPath", true); + MCSymbol *LSlow = OutContext.createTempSymbol("NewArraySlowPath", true); + const MCSymbolRefExpr *SlowExpr = MCSymbolRefExpr::create(LSlow, OutContext); + unsigned AllocBufferReg = X86::R9; + unsigned RegionInfoReg = X86::R9; + unsigned AllocPtrReg = X86::RAX; + unsigned LimitReg = X86::R10; + unsigned TmpReg; + unsigned ArgReg0; + unsigned ArgReg1; + unsigned ArgReg2; + + OutStreamer->emitLabel(LFast); + if (!getSubtargetInfo().getTargetTriple().isOSWindows()) { + TmpReg = X86::R8; + ArgReg0 = X86::RDI; + ArgReg1 = X86::RSI; + ArgReg2 = X86::RDX; + } else { + TmpReg = X86::R11; + ArgReg0 = X86::RCX; + ArgReg1 = X86::RDX; + ArgReg2 = X86::R8; + } + MCInst AllocBuffer = MCInstBuilder(X86::MOV64rm) + .addReg(AllocBufferReg) + .addReg(X86::R15) + .addImm(1) + .addReg(0) + .addImm(getAllocBufferOffsetInCJTLS()) + .addReg(0); + EmitAndCountInstruction(AllocBuffer); + + // 0: regionPtr in AllocBuffer + MCInst GetRegionPtr = MCInstBuilder(X86::MOV64rm) + .addReg(RegionInfoReg) + .addReg(AllocBufferReg) + .addImm(1) + .addReg(0) + .addImm(0) + .addReg(0); + MCInst GetAllocPtr = MCInstBuilder(X86::MOV64rm) + .addReg(AllocPtrReg) + .addReg(RegionInfoReg) + .addImm(1) + .addReg(0) + .addImm(0) + .addReg(0); + MCInst GetLimit = MCInstBuilder(X86::MOV64rm) + .addReg(LimitReg) + .addReg(RegionInfoReg) + .addImm(1) + .addReg(0) + .addImm(8) + .addReg(0); + MCInst CalNewAllocPtr = MCInstBuilder(X86::LEA64r) + .addReg(TmpReg) + .addReg(AllocPtrReg) + .addImm(1) + .addReg(ArgReg2) + .addImm(0) + .addReg(0); + + MCInst CmpNewAllocPtrWithLimit = + MCInstBuilder(X86::CMP64rr).addReg(TmpReg).addReg(LimitReg); + + MCInst JmpGToLSLow = + MCInstBuilder(X86::JCC_1).addExpr(SlowExpr).addImm(X86::COND_G); + + MCInst SetKlass = MCInstBuilder(X86::MOV64mr) + .addReg(AllocPtrReg) + .addImm(1) + .addReg(0) + .addImm(0) + .addReg(0) + .addReg(ArgReg0); + MCInst StoreArrayLength = MCInstBuilder(X86::MOV64mr) + .addReg(AllocPtrReg) + .addImm(0) + .addReg(0) + .addImm(8) + .addReg(0) + .addReg(ArgReg1); + MCInst StoreNewAllocPtr = MCInstBuilder(X86::MOV64mr) + .addReg(RegionInfoReg) + .addImm(1) + .addReg(0) + .addImm(0) + .addReg(0) + .addReg(TmpReg); + MCSymbol *LFinish = OutContext.createTempSymbol("NewArrayFin", true); + const MCSymbolRefExpr *FinExpr = MCSymbolRefExpr::create(LFinish, OutContext); + MCInst JmpToLFin = MCInstBuilder(X86::JMP_1).addExpr(FinExpr); + + X86MCInstLower MCInstLowering(*MF, *this); + MCInst CallNewArraySlowPath = MCInstBuilder(X86::CALL64pcrel32).addOperand( + MCInstLowering.LowerMachineOperand(&MI, MOSym).getValue()); + EmitAndCountInstruction(GetRegionPtr); + EmitAndCountInstruction(GetAllocPtr); + EmitAndCountInstruction(GetLimit); + EmitAndCountInstruction(CalNewAllocPtr); + EmitAndCountInstruction(CmpNewAllocPtrWithLimit); + EmitAndCountInstruction(JmpGToLSLow); + EmitAndCountInstruction(SetKlass); + EmitAndCountInstruction(StoreArrayLength); + EmitAndCountInstruction(StoreNewAllocPtr); + EmitAndCountInstruction(JmpToLFin); + OutStreamer->emitLabel(LSlow); + EmitAndCountInstruction(CallNewArraySlowPath); + OutStreamer->emitLabel(LFinish); + SM.recordCJStackMap(MI); +} + +// movq (rsp) r11 +// subq $16 rsp +// movq r11 (rsp) +// movq xxx.CJStubGV(%rip), %r11 +// movq r11 16(rsp) +// movq $OnStackParamSize 8(rsp) +void X86AsmPrinter::extendStackAndInsertFFIInfoForJmp(MCInst &MovGVToR11, + unsigned CallFrameSize) { + EmitAndCountInstruction(MCInstBuilder(X86::MOV64rm) + .addReg(X86::R11) + .addReg(X86::RSP) + .addImm(1) + .addReg(0) + .addImm(0) + .addReg(0)); + EmitAndCountInstruction(MCInstBuilder(X86::SUB64ri8) + .addReg(X86::RSP) + .addReg(X86::RSP) + .addImm(16) + .addReg(0)); + EmitAndCountInstruction(MCInstBuilder(X86::MOV64mr) + .addReg(X86::RSP) + .addImm(1) + .addReg(0) + .addImm(0) + .addReg(0) + .addReg(X86::R11)); + EmitAndCountInstruction(MovGVToR11); + EmitAndCountInstruction(MCInstBuilder(X86::MOV64mr) + .addReg(X86::RSP) + .addImm(1) + .addReg(0) + .addImm(16) + .addReg(0) + .addReg(X86::R11)); + EmitAndCountInstruction(MCInstBuilder(X86::MOV64mi32) + .addReg(X86::RSP) + .addImm(1) + .addReg(0) + .addImm(8) + .addReg(0) + .addImm(CallFrameSize)); + return; +} + +void X86AsmPrinter::emitCangjieCallStubInstImpl(const MachineInstr *MI, + const Function *F, + const MachineOperand &MOSym, + unsigned Opcode) { + const auto *OriCalled = dyn_cast(MOSym.getGlobal()); + X86MCInstLower MCInstLowering(*MF, *this); + MachineOperand MOOriAddr(MOSym), MONewAddr(MOSym); + auto *AddrGV = F->getParent()->getGlobalVariable( + OriCalled->getName().str() + ".CJStubGV", true); + MOOriAddr.ChangeToGA(AddrGV, 0); + const MCSymbolRefExpr *Sym = + MCSymbolRefExpr::create(MCInstLowering.GetSymbolFromOperand(MOOriAddr), + OutStreamer->getContext()); + // push called-addr and CallFrameSize to stack. This operation extends + // the sp of the caller by 16 bytes and will be fixed in MCC_XXXStub + MDNode *Metadata = OriCalled->getMetadata("CallFrameSizeForCJFFI"); + assert(Metadata != nullptr && + "FFI Func must have CallFrameSizeForCJFFI meta!"); + unsigned CallFrameSize = + dyn_cast( + dyn_cast(Metadata->getOperand(0).get()) + ->getValue()) + ->getSExtValue(); + MCInst MovGVToR11 = MCInstBuilder(X86::MOV64rm) + .addReg(X86::R11) + .addReg(X86::RIP) + .addImm(1) + .addReg(0) + .addExpr(Sym) + .addReg(0); + // MCC_XXXStub expect the end of caller stack is like: + // | callee-addr | + // | param-stack-size (16 bytes align) | + // | return addr | + if (Opcode == X86::TAILJMPd64) { + Opcode = X86::JMP_1; + // return addr has been push on stack already for jmp inst scenario + // so we need to insert callee-addr and size before return addr + + // movq (rsp) r11 + // subq $16 rsp + // movq r11 (rsp) + // movq xxx.CJStubGV(%rip), %r11 + // movq r11 16(rsp) + // movq $OnStackParamSize 8(rsp) + extendStackAndInsertFFIInfoForJmp(MovGVToR11, CallFrameSize); + } else { + assert( + Opcode == X86::CALL64pcrel32 && + "Opcode should be CALL64pcrel32 or TAILJMPd64 for Cangjie Call Stub"); + // movq xxx.CJStubGV(%rip), %r11 + // pushq r11 + // pushq $OnStackParamSize + EmitAndCountInstruction(MovGVToR11); + EmitAndCountInstruction(MCInstBuilder(X86::PUSH64r).addReg(X86::R11)); + EmitAndCountInstruction( + MCInstBuilder(X86::PUSH64i32).addImm(CallFrameSize)); + } + + MCInst TemInst; + MONewAddr.ChangeToGA(F, 0); + auto SymNewAddr = MCInstLowering.LowerMachineOperand(MI, MONewAddr); + + TemInst.setOpcode(Opcode); + TemInst.addOperand(SymNewAddr.getValue()); + // Count then size of the call towards the shadow + SMShadowTracker.count(TemInst, getSubtargetInfo(), CodeEmitter.get()); + // Then flush the shadow so that we fill with nops before the call, not + // after it. + SMShadowTracker.emitShadowPadding(*OutStreamer, getSubtargetInfo()); + // Then emit the call + OutStreamer->emitInstruction(TemInst, getSubtargetInfo()); + SM.recordCJStackMap(*MI); + return; +} + +void X86AsmPrinter::emitGetCJThreadId() { + auto &Ctx = OutStreamer->getContext(); + MCSymbol *MILabel = Ctx.createTempSymbol("cjthread_id_end", true); + const MCSymbolRefExpr *MILabelExpr = MCSymbolRefExpr::create(MILabel, OutContext); + // movq 16(%r15), %rax + emitGetCJTLSData(getCJThreadOffsetInCJTLS()); + int64_t cjthreadIdOffset = 336; + int64_t cjthreadIdOffsetWin = 536; + if (getSubtargetInfo().getTargetTriple().isOSWindows()) { + cjthreadIdOffset = cjthreadIdOffsetWin; + } + // test %rax, %rax + MCInst TestInst; + TestInst.setOpcode(X86::TEST64rr); + TestInst.addOperand(MCOperand::createReg(X86::RAX)); + TestInst.addOperand(MCOperand::createReg(X86::RAX)); + OutStreamer->emitInstruction(TestInst, getSubtargetInfo()); + // je .L_cjthread_id_end + MCInst JmpInst; + JmpInst.setOpcode(X86::JCC_1); + JmpInst.addOperand(MCOperand::createExpr(MILabelExpr)); + JmpInst.addOperand(MCOperand::createImm(X86::COND_E)); + OutStreamer->emitInstruction(JmpInst, getSubtargetInfo()); + // movq 336(%rax), %rax + MCInst LoadCJThreadInst; + LoadCJThreadInst.setOpcode(X86::MOV64rm); + LoadCJThreadInst.addOperand(MCOperand::createReg(X86::RAX)); + LoadCJThreadInst.addOperand(MCOperand::createReg(X86::RAX)); + LoadCJThreadInst.addOperand(MCOperand::createImm(1)); + LoadCJThreadInst.addOperand(MCOperand::createReg(0)); + LoadCJThreadInst.addOperand(MCOperand::createImm(cjthreadIdOffset)); + LoadCJThreadInst.addOperand(MCOperand::createReg(0)); + OutStreamer->emitInstruction(LoadCJThreadInst, getSubtargetInfo()); + // .L_cjthread_id_end + OutStreamer->emitLabel(MILabel); + return; +} + +void X86AsmPrinter::emitMetadataAddress() { + const Triple &TT = getSubtargetInfo().getTargetTriple(); + MCSymbol *MetadataStart = OutContext.getOrCreateSymbol("__CJMetadataStart"); + MCSymbol *FuncSym; + if (TT.isOSWindows()) { + // leaq __CJMetadataStart(%rip), %rcx + EmitAndCountInstruction( + MCInstBuilder(X86::LEA64r) + .addReg(X86::RCX) + .addReg(X86::RIP) + .addImm(1) + .addReg(0) + .addExpr(MCSymbolRefExpr::create(MetadataStart, OutContext)) + .addReg(0)); + FuncSym = OutContext.getOrCreateSymbol("CJ_MRT_PreInitializePackage"); + } else if (TT.isOSLinux()) { + // leaq __CJMetadataStart(%rip), %rdi + EmitAndCountInstruction( + MCInstBuilder(X86::LEA64r) + .addReg(X86::RDI) + .addReg(X86::RIP) + .addImm(1) + .addReg(0) + .addExpr(MCSymbolRefExpr::create(MetadataStart, OutContext)) + .addReg(0)); + FuncSym = OutContext.getOrCreateSymbol("CJ_MRT_PreInitializePackage"); + } else if (TT.isOSBinFormatMachO()) { + // movq __CJMetadataStart@GOTPCREL(%rip), %rax + EmitAndCountInstruction( + MCInstBuilder(X86::MOV64rm) + .addReg(X86::RAX) + .addReg(X86::RIP) + .addImm(1) + .addReg(0) + .addExpr(MCSymbolRefExpr::create( + MetadataStart, MCSymbolRefExpr::VK_GOTPCREL, OutContext)) + .addReg(0)); + // movq (%rax), %rdi + EmitAndCountInstruction(MCInstBuilder(X86::MOV64rm) + .addReg(X86::RDI) + .addReg(X86::RAX) + .addImm(1) + .addReg(0) + .addImm(0) + .addReg(0)); + FuncSym = OutContext.getOrCreateSymbol("_CJ_MRT_PreInitializePackage"); + } + // callq CJ_MRT_PreInitializePackage + EmitAndCountInstruction( + MCInstBuilder(X86::CALL64pcrel32) + .addExpr(MCSymbolRefExpr::create(FuncSym, OutContext))); +} diff --git a/llvm/lib/Target/X86/X86RegisterInfo.cpp b/llvm/lib/Target/X86/X86RegisterInfo.cpp index f2658f704..24ebe91f5 100644 --- a/llvm/lib/Target/X86/X86RegisterInfo.cpp +++ b/llvm/lib/Target/X86/X86RegisterInfo.cpp @@ -31,6 +31,7 @@ #include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" #include "llvm/IR/Type.h" +#include "llvm/MC/MCAsmInfo.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Target/TargetMachine.h" @@ -348,6 +349,7 @@ X86RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { assert(!Is64Bit && "CFGuard check mechanism only used on 32-bit X86"); return (HasSSE ? CSR_Win32_CFGuard_Check_SaveList : CSR_Win32_CFGuard_Check_NoSSE_SaveList); + case CallingConv::CangjieGC: case CallingConv::Cold: if (Is64Bit) return CSR_64_MostRegs_SaveList; @@ -470,6 +472,7 @@ X86RegisterInfo::getCallPreservedMask(const MachineFunction &MF, assert(!Is64Bit && "CFGuard check mechanism only used on 32-bit X86"); return (HasSSE ? CSR_Win32_CFGuard_Check_RegMask : CSR_Win32_CFGuard_Check_NoSSE_RegMask); + case CallingConv::CangjieGC: case CallingConv::Cold: if (Is64Bit) return CSR_64_MostRegs_RegMask; @@ -532,6 +535,16 @@ BitVector X86RegisterInfo::getReservedRegs(const MachineFunction &MF) const { BitVector Reserved(getNumRegs()); const X86FrameLowering *TFI = getFrameLowering(MF); + if (MF.getFunction().hasCangjieGC() || + MF.getFunction().hasFnAttribute("pkg_c_wrapper")) { + Reserved.set(X86::R15B); + Reserved.set(X86::R15BH); + Reserved.set(X86::R15WH); + Reserved.set(X86::R15W); + Reserved.set(X86::R15D); + Reserved.set(X86::R15); + } + // Set the floating point control register as reserved. Reserved.set(X86::FPCW); @@ -916,6 +929,15 @@ Register X86RegisterInfo::getFrameRegister(const MachineFunction &MF) const { return TFI->hasFP(MF) ? FramePtr : StackPtr; } +const std::vector +X86RegisterInfo::getArgRegs(const MachineFunction &MF) const { + if (MF.getTarget().getMCAsmInfo()->usesWindowsCFI()) { + return { X86::RCX, X86::RDX, X86::R8, X86::R9 }; + } else { + return { X86::RDI, X86::RSI, X86::RDX, X86::RCX, X86::R8, X86::R9 }; + } +} + unsigned X86RegisterInfo::getPtrSizedFrameRegister(const MachineFunction &MF) const { const X86Subtarget &Subtarget = MF.getSubtarget(); diff --git a/llvm/lib/Target/X86/X86RegisterInfo.h b/llvm/lib/Target/X86/X86RegisterInfo.h index 6f4fb405d..93f82a6fd 100644 --- a/llvm/lib/Target/X86/X86RegisterInfo.h +++ b/llvm/lib/Target/X86/X86RegisterInfo.h @@ -157,6 +157,9 @@ public: // FIXME: Move to FrameInfok unsigned getSlotSize() const { return SlotSize; } + const std::vector + getArgRegs(const MachineFunction &MF) const override; + bool getRegAllocationHints(Register VirtReg, ArrayRef Order, SmallVectorImpl &Hints, const MachineFunction &MF, const VirtRegMap *VRM, diff --git a/llvm/lib/Transforms/CFGuard/CFGuard.cpp b/llvm/lib/Transforms/CFGuard/CFGuard.cpp index 5fc529596..ab08c3595 100644 --- a/llvm/lib/Transforms/CFGuard/CFGuard.cpp +++ b/llvm/lib/Transforms/CFGuard/CFGuard.cpp @@ -61,7 +61,7 @@ public: /// symbol. This checks that the target address is a valid address-taken /// function. The address of the target function is passed to the guard check /// function in an architecture-specific register (e.g. ECX on 32-bit X86, - /// X15 on Aarch64, and R0 on ARM). The guard check function has no return + /// X15 on AArch64, and R0 on ARM). The guard check function has no return /// value (if the target is invalid, the guard check funtion will raise an /// error). /// diff --git a/llvm/lib/Transforms/CJCFI/CMakeLists.txt b/llvm/lib/Transforms/CJCFI/CMakeLists.txt new file mode 100644 index 000000000..2952d0a57 --- /dev/null +++ b/llvm/lib/Transforms/CJCFI/CMakeLists.txt @@ -0,0 +1,15 @@ +add_llvm_component_library(LLVMCJCFI + PtrAuthBackwardCFI.cpp + + ADDITIONAL_HEADER_DIRS + ${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms + + DEPENDS + intrinsics_gen + + LINK_COMPONENTS + Core + Support + Analysis + TransformUtils + ) diff --git a/llvm/lib/Transforms/CJCFI/PtrAuthBackwardCFI.cpp b/llvm/lib/Transforms/CJCFI/PtrAuthBackwardCFI.cpp new file mode 100644 index 000000000..1d67119ad --- /dev/null +++ b/llvm/lib/Transforms/CJCFI/PtrAuthBackwardCFI.cpp @@ -0,0 +1,29 @@ +//===--------------------- PtrAuthBackwardCFI.cpp -------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// The PtrAuth Backward CFI pass. +// +//===----------------------------------------------------------------------===// +#include "llvm/IR/Module.h" +#include "llvm/IR/Constants.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/CJCFI/PtrAuthBackwardCFI.h" + +using namespace llvm; + +#define DEBUG_TYPE "cj-pac-backward-cfi" + +PreservedAnalyses PtrAuthBackwardCFI::run(Module &M, + ModuleAnalysisManager &AM) { + M.setModuleFlag(Module::ModFlagBehavior::Override, "sign-return-address", + ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(M.getContext()), 1))); + return PreservedAnalyses::all(); +} \ No newline at end of file diff --git a/llvm/lib/Transforms/CMakeLists.txt b/llvm/lib/Transforms/CMakeLists.txt index dda5f6de1..0ce537407 100644 --- a/llvm/lib/Transforms/CMakeLists.txt +++ b/llvm/lib/Transforms/CMakeLists.txt @@ -9,3 +9,5 @@ add_subdirectory(Hello) add_subdirectory(ObjCARC) add_subdirectory(Coroutines) add_subdirectory(CFGuard) +add_subdirectory(Obfuscator) +add_subdirectory(CJCFI) diff --git a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp index 8c77b6937..4e65ed2e2 100644 --- a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp +++ b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp @@ -60,6 +60,7 @@ #include "llvm/IR/Metadata.h" #include "llvm/IR/NoFolder.h" #include "llvm/IR/PassManager.h" +#include "llvm/IR/SafepointIRVerifier.h" #include "llvm/IR/Type.h" #include "llvm/IR/Use.h" #include "llvm/IR/User.h" @@ -81,6 +82,10 @@ using namespace llvm; STATISTIC(NumArgumentsPromoted, "Number of pointer arguments promoted"); STATISTIC(NumArgumentsDead, "Number of dead pointer args eliminated"); +namespace llvm { +extern cl::opt CJPipeline; +} + namespace { struct ArgPart { @@ -448,6 +453,11 @@ static bool allCallersPassValidPointerForArgument(Argument *Arg, static bool findArgParts(Argument *Arg, const DataLayout &DL, AAResults &AAR, unsigned MaxElements, bool IsRecursive, SmallVectorImpl &ArgPartsVec) { + if (CJPipeline && (containsGCPtrType(Arg->getType()) || + isMemoryContainsGCPtrType(Arg->getType()))) { + return false; + } + // Quick exit for unused arguments if (Arg->use_empty()) return true; @@ -769,7 +779,7 @@ static Function *promoteArguments(Function *F, FunctionAnalysisManager &FAM, for (Argument *PtrArg : PointerArgs) { // Replace sret attribute with noalias. This reduces register pressure by // avoiding a register copy. - if (PtrArg->hasStructRetAttr()) { + if (!CJPipeline && PtrArg->hasStructRetAttr()) { unsigned ArgNo = PtrArg->getArgNo(); F->removeParamAttr(ArgNo, Attribute::StructRet); F->addParamAttr(ArgNo, Attribute::NoAlias); diff --git a/llvm/lib/Transforms/IPO/CJPartialEscapeAnalysis.cpp b/llvm/lib/Transforms/IPO/CJPartialEscapeAnalysis.cpp new file mode 100644 index 000000000..39bc4191a --- /dev/null +++ b/llvm/lib/Transforms/IPO/CJPartialEscapeAnalysis.cpp @@ -0,0 +1,3319 @@ +//===- CJPartialEscapeAnalysis.cpp - escape analysis for Cangjie ----------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file implements escape analysis, and trans non-escape object from heap +// to stack if possible. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/IPO/CJPartialEscapeAnalysis.h" + +#include "queue" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/Analysis/CGSCCPassManager.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/Analysis/CallGraphSCCPass.h" +#include "llvm/Analysis/DomTreeUpdater.h" +#include "llvm/Analysis/LazyCallGraph.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstVisitor.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/SafepointIRVerifier.h" +#include "llvm/Transforms/Scalar/CJFillMetadata.h" +#include "llvm/Transforms/Utils/CallGraphUpdater.h" +#include "llvm/Transforms/Utils/Local.h" +#include "llvm/Transforms/Scalar.h" + +#define DEBUG_TYPE "escape-analysis" +using namespace llvm; +constexpr int alignEight = 8; +enum InitializeState : unsigned { NotInitialize, Initializing, Initialized }; +static cl::opt CJMaxStackObject("cj-max-stack-obj", cl::Hidden, + cl::init(1024)); +static cl::opt CJMaxNonMoveArraySize("cj-max-nonmove-array-size", + cl::Hidden, cl::init(64 * 1024)); +static cl::opt CJDisablePEA("cj-disable-partial-ea", + cl::Hidden, cl::init(false)); +namespace llvm { + +GlobalVariable *getNewKlass(CallBase *I) { + GlobalVariable *Klass = + dyn_cast(I->getArgOperand(0)->stripPointerCasts()); + if (Klass && (Klass->getMetadata("RelatedType") != 0)) { + return Klass; + } + return nullptr; +} + +void setEscapeMeta(Instruction *I) { + assert(isa(I) && "Only add meta for alloca inst."); + I->setMetadata("ea_val", MDNode::get(I->getContext(), {})); +} + +Type *getAllocaType(GlobalVariable *Klass, bool &HasRef, uint32_t &AS, + bool &NonMovArray, Instruction *Val) { + Module *M = Klass->getParent(); + LLVMContext &C = M ->getContext(); + StructType *ST = getTypeLayoutType(Klass); + const DataLayout &DL = M->getDataLayout(); + uint32_t ObjSize = + static_cast(DL.getTypeAllocSize(ST).getFixedSize()); + // Struct Body is null + if (ObjSize == 0) { + AS = ObjectHeadSize; + HasRef = false; + return ST; + } + // none-array type + StructType *ArrayBaseType = dyn_cast(ST->getElementType(0)); + if (!ArrayBaseType || ArrayBaseType->getName() != "ArrayBase") { + ObjSize += ObjectHeadSize; + ObjSize = (ObjSize + 7) & (~(7)); // 8 bytes alignment + AS = ObjSize; + if (AS > CJMaxStackObject) { + return nullptr; // obj size too large + } + HasRef = containsGCPtrType(ST); + return ST; + } + Value *Size = Val->getOperand(1); + uint64_t ArraySize = 0; + if (auto ConstantSize = dyn_cast(Size)) { + ArraySize = ConstantSize->getZExtValue(); + } else { + return nullptr; // unknown array size + } + + Type *ElementType = + dyn_cast(ST->getElementType(1))->getElementType(); + unsigned ElementSize = + static_cast(DL.getTypeAllocSize(ElementType).getFixedSize()); + // ElemSize may be 0, when that is RawArray.ti and Unit.type is {}. + if (ElementSize && (ArraySize >= CJMaxStackObject / ElementSize)) { + if (ArraySize > CJMaxNonMoveArraySize / ElementSize) { + NonMovArray = true; + } + return nullptr; // obj size too large. + } + AS = ArraySize * ElementSize + ArrayHeadSize; + + if (ArraySize != 0) { + HasRef = containsGCPtrType(ElementType); + } + SmallVector ElementTypes; + ElementTypes.push_back(ArrayBaseType); + ElementTypes.push_back(ArrayType::get(ElementType, ArraySize)); + return StructType::get(C, ElementTypes); +} + +// NEW func which can rewrite. +bool isRewriteableCangjieMallocFunc(Function *F) { + auto FuncName = F->getName(); + if (FuncName.startswith("CJ_MCC_NewObject") || + FuncName.startswith("CJ_MCC_NewArray") || + FuncName.startswith("CJ_MCC_NewObjArray")) { + return true; + } + return false; +} + +class NonEscapeRewriter : public InstVisitor { + friend class InstVisitor; + +public: + explicit NonEscapeRewriter( + CallGraphUpdater *CGUpdater, Module *M) + : CGUpdater(CGUpdater), M(M) { + CJMemset = Intrinsic::getDeclaration( + M, Intrinsic::cj_memset, + {IntegerType::getInt8PtrTy(M->getContext(), 0)}); + } + + bool rewrite(Instruction *Old, BasicBlock *NewInstInsertBB) { + OldInst = Old; + Visited.clear(); + if (!createNewAllocaInst(NewInstInsertBB)) { + return false; + } + rewriteNewInst(); + return true; + } + + bool handleStructAS1Cast(AllocaInst *Old) { + NewInst = Old; + rewriteNewInst(); + return true; + } + + void setStructAS1Flag() { + StructAS1Replace = true; + } + + static void clearRewritePhiIncoming() { + RewritePhiIncoming.clear(); + } + +private: + void eraseInstr(Instruction *I) { + Visited.remove(I); + I->eraseFromParent(); + } + void enqueueUsers(Instruction &I) { + for (Use &U : I.uses()) { + if (Visited.insert(U.getUser())) { + Queue.push_back(dyn_cast(U.getUser())); + } + } + } + + void rewriteNewInst() { + enqueueUsers(*NewInst); + LLVM_DEBUG(dbgs() << "begin rewrite Inst:" << *NewInst << "\n"); + while (!Queue.empty()) { + Instruction *I = Queue.pop_back_val(); + visit(I); + } + } + + void visitInstruction(Instruction &I) { + LLVM_DEBUG(dbgs() << "rewrite path:" << I << "\n"); + } + + void visitPHINode(PHINode &PN) { + if (RewritePhiIncoming[&PN].insert(NewInst)) { + Visited.remove(&PN); + } + if (RewritePhiIncoming[&PN].size() == PN.getNumIncomingValues()) { + enqueueUsers(PN); + } else { + removeInvariantLoad(&PN); + } + } + + void visitSelectInst(SelectInst &SI) { + RewriteSIIncoming[&SI].insert(NewInst); + // 2: select inst incoming size. + if (RewriteSIIncoming[&SI].size() == 2) { + enqueueUsers(SI); + } else { + removeInvariantLoad(&SI); + } + } + + void visitAddrSpaceCastInst(AddrSpaceCastInst &ASC) { enqueueUsers(ASC); } + + void visitBitCastInst(BitCastInst &BC) { enqueueUsers(BC); } + + void visitGetElementPtrInst(GetElementPtrInst &GEPI) { enqueueUsers(GEPI); } + + void visitLoadInst(LoadInst &LI) { + removeInvariantLoad(&LI); + if (!isGCPointerType(LI.getOperand(0)->getType())) { + return; + } + Type *StackObjPtr = LI.getType()->getPointerTo(0); + IRBuilder<> Builder(&LI); + Value *AS1ToAS0 = + Builder.CreateAddrSpaceCast(LI.getOperand(0), StackObjPtr); + Instruction *Load = + new LoadInst(LI.getType(), AS1ToAS0, "", false, LI.getAlign(), &LI); + LI.replaceAllUsesWith(Load); + Load->setDebugLoc(LI.getDebugLoc()); + eraseInstr(&LI); + } + + // Specifically for typeinfo load, since store typeinfo and new object are no + // longer "atomic", it is possible for load to occur before store. + void removeInvariantLoad(Instruction *I) { + if (!(isa(I) || isa(I) || isa(I))) + report_fatal_error("error instruction"); + if (isa(I)) { + I->setMetadata(LLVMContext::MD_invariant_load, nullptr); + return; + } + SmallVector Worklist = {I->user_begin(), I->user_end()}; + SetVector Visited; + while (!Worklist.empty()) { + User *U = Worklist.pop_back_val(); + if (!Visited.insert(U)) + continue; + Instruction *UI = cast(U); + switch (UI->getOpcode()) { + default: + break; + case Instruction::AddrSpaceCast: + case Instruction::BitCast: + case Instruction::PHI: + case Instruction::Select: + case Instruction::IntToPtr: + case Instruction::PtrToInt: + case Instruction::GetElementPtr: + Worklist.append(U->user_begin(), U->user_end()); + break; + case Instruction::Load: + if (UI->hasMetadata(LLVMContext::MD_invariant_load)) + UI->setMetadata(LLVMContext::MD_invariant_load, nullptr); + break; + } + } + } + + bool canReplaceBarrierCall(CallInst &CI, unsigned Arg) { + if (StructAS1Replace) + return true; + Value *Val = findMemoryBasePointer(CI.getArgOperand(Arg)); + if (PHINode *PN = dyn_cast(Val)) { + return RewritePhiIncoming.count(PN) && + (RewritePhiIncoming[PN].size() == PN->getNumIncomingValues()); + } else if (SelectInst *SI = dyn_cast(Val)) { + // 2: select inst incoming size + return RewriteSIIncoming.count(SI) && (RewriteSIIncoming[SI].size() == 2); + } + return Val == NewInst; + } + + bool isArrayCopyAllAlloca(CallInst &CI) { + if (RewriteArrayCopy[&CI].insert(NewInst)) { + Visited.remove(&CI); + } + // 2: all the ptr operand of array copy is not escape + if (RewriteArrayCopy[&CI].size() == 2) { + return false; + } + return true; + } + + void visitCallInst(CallInst &CI) { + if (CI.isTailCall()) + CI.setTailCall(false); + + if (const Function *F = CI.getCalledFunction()) { + Instruction *NewInst = nullptr; + Value *AS1ToAS0 = nullptr; + Type *StackObjPtr = nullptr; + IRBuilder<> Builder(&CI); + switch (F->getIntrinsicID()) { + default: + return; + case Intrinsic::cj_gcwrite_ref: + if (!canReplaceBarrierCall(CI, 1)) { + return; + } + StackObjPtr = CI.getArgOperand(0)->getType()->getPointerTo(0); + // 2: value operands number + AS1ToAS0 = + Builder.CreateAddrSpaceCast(CI.getArgOperand(2), StackObjPtr); + NewInst = + new StoreInst(CI.getArgOperand(0), + AS1ToAS0, false, Align(alignEight), &CI); + break; + case Intrinsic::cj_gcread_ref: + if (!canReplaceBarrierCall(CI, 0)) { + return; + } + StackObjPtr = CI.getType()->getPointerTo(0); + // 2: value operands number + AS1ToAS0 = + Builder.CreateAddrSpaceCast(CI.getArgOperand(1), StackObjPtr); + NewInst = new LoadInst(CI.getType(), AS1ToAS0, "", &CI); + break; + case Intrinsic::cj_gcwrite_struct: + if (!canReplaceBarrierCall(CI, 0)) { + return; + } + // 1: pointer operands number + // 2: value operands number + // 3: size info + NewInst = Builder.CreateMemCpy(CI.getArgOperand(1), Align(alignEight), + CI.getArgOperand(2), Align(alignEight), + CI.getArgOperand(3)); + break; + case Intrinsic::cj_gcread_struct: + if (!canReplaceBarrierCall(CI, 1)) { + return; + } + // 0: pointer operands number + // 2: value operands number + // 3: size info + NewInst = Builder.CreateMemCpy(CI.getArgOperand(0), Align(alignEight), + CI.getArgOperand(2), Align(alignEight), + CI.getArgOperand(3)); + break; + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + Type *BaseType = findMemoryBasePointer(CI.getArgOperand(0))->getType(); + if (isArrayCopyAllAlloca(CI) || + (StructAS1Replace && isGCPointerType(BaseType)) || + !canReplaceBarrierCall(CI, 0)) { + return; + } + NewInst = Builder.CreateMemMove(CI.getArgOperand(1), Align(alignEight), + CI.getArgOperand(3), Align(alignEight), + CI.getArgOperand(4)); + break; + } + CI.replaceAllUsesWith(NewInst); + NewInst->setDebugLoc(CI.getDebugLoc()); + eraseInstr(&CI); + } + } + + Type *getObjAllocaType(Type *LayoutType) { + SmallVector Elems; + auto *KlassType = StructType::getTypeByName(M->getContext(), "TypeInfo"); + Elems.push_back(KlassType->getPointerTo()); + Elems.push_back(LayoutType); + return StructType::get(M->getContext(), Elems); + } + + void createAllocaInst(IRBuilder<> &IRB, bool IsArray, CallBase *I) { + BasicBlock *EntryBB = &I->getParent()->getParent()->getEntryBlock(); + Instruction *InsertPos = &*EntryBB->getFirstInsertionPt(); + const DebugLoc &CurDbg = OldInst->getDebugLoc(); + NewInst = new AllocaInst(getObjAllocaType(AllocaType), 0, "", InsertPos); + uint32_t MemSetSize = AllocaSize - ObjectHeadSize; + if (IsArray) { + SmallVector GEPIndices; + GEPIndices.push_back(IRB.getInt32(0)); + GEPIndices.push_back(IRB.getInt32(1)); + auto SizeP = IRB.CreateInBoundsGEP( + NewInst->getType()->getNonOpaquePointerElementType(), NewInst, + GEPIndices); + SmallVector GEPSizeIndices; + GEPSizeIndices.push_back(IRB.getInt32(0)); + GEPSizeIndices.push_back(IRB.getInt32(0)); + GEPSizeIndices.push_back(IRB.getInt32(0)); + auto SizeP64 = IRB.CreateInBoundsGEP( + SizeP->getType()->getNonOpaquePointerElementType(), SizeP, + GEPSizeIndices); + IRB.CreateStore(I->getOperand(1), SizeP64); // store array size + MemSetSize = AllocaSize - ArrayHeadSize; + } + NewInst->setDebugLoc(CurDbg); + setEscapeMeta(NewInst); + Type *PI8Ty = Type::getInt8PtrTy(I->getContext()); + Value *PI = IRB.CreateBitCast(NewInst, PI8Ty->getPointerTo(0)); + IRB.CreateStore(I->getOperand(0), PI); + if (MemSetSize == 0) { + return; + } + SmallVector GEPValues; + // 8: PI Type Size + GEPValues.push_back(IRB.getInt32((AllocaSize - MemSetSize) / 8)); + auto Values = IRB.CreateGEP(PI8Ty, PI, GEPValues); + CallInst *CI = IRB.CreateMemSet( + Values, IRB.getInt8(0), IRB.getInt64(MemSetSize), MaybeAlign(0), false); + if (UseCJMemset) { + CI->setCalledFunction(CJMemset); + } + } + + void replaceOldInst(IRBuilder<> &IRB, CallBase *I) { + // replace old inst + // collect origin used point, replace them with addrspace 0 + // maybe change record type. + Type *PI8AS1Ty = Type::getInt8PtrTy(I->getContext(), 1); + Value *BI = IRB.CreateAddrSpaceCast(NewInst, PI8AS1Ty); + OldInst->replaceAllUsesWith(BI); + if (auto II = dyn_cast(OldInst)) { + IRBuilder BrIRB(OldInst); + BrIRB.CreateBr(II->getNormalDest()); + BasicBlock *UnwindBB = II->getUnwindDest(); + UnwindBB->removePredecessor(OldInst->getParent()); + } + if (OldInst->use_empty()) { + eraseInstr(OldInst); + } + } + + bool createNewAllocaInst(BasicBlock *NewInstInsertBB) { + CallBase *I = dyn_cast(OldInst); + Function *Callee = I->getCalledFunction(); + bool IsArray = !Callee->getName().isCangjieNewObjFunction(); + GlobalVariable *Klass = getNewKlass(I); + bool NonMoveArray = false; + // cache, now duplicate. + AllocaType = getAllocaType(Klass, UseCJMemset, AllocaSize, NonMoveArray, I); + if (NonMoveArray) { + return false; + } + Instruction *InsertInst = OldInst; + if (NewInstInsertBB) { + InsertInst = &*NewInstInsertBB->getTerminator(); + } + IRBuilder<> IRB(InsertInst); + // do replace so update CallGraph + if (CGUpdater) + CGUpdater->removeCallSite(*I); + createAllocaInst(IRB, IsArray, I); + replaceOldInst(IRB, I); + return true; + } + + Type *AllocaType; + Instruction *OldInst; + AllocaInst *NewInst; + bool UseCJMemset = false; + uint32_t AllocaSize = 0; + CallGraphUpdater *CGUpdater; + Module *M; + Function *CJMemset; + SmallVector Queue; + SmallSetVector Visited; + bool StructAS1Replace = false; + static DenseMap> RewritePhiIncoming; + DenseMap> RewriteSIIncoming; + DenseMap> RewriteArrayCopy; +}; +} // end namespace llvm + +DenseMap> + NonEscapeRewriter::RewritePhiIncoming; + +namespace { +enum EscapeType : unsigned { + EscapeOrigin = 0x1 << 0, + InfoEscapedParam = 0x1 << 1, + InfoEscapedDirect = 0x1 << 2, + EscapeParam = 0x1 << 3, + EscapeGlobal = 0x1 << 4, + EscapeRet = 0x1 << 5, + EscapeLoop = 0x1 << 6 +}; + +enum LocationType : unsigned { NewLoc = 0x1, ParamLoc = 0x2, TempLoc = 0x3 }; + +// each as1 ptr has a location, which contains escape state +struct ObjectLocation { + LocationType LocType; + Value *Val; + Function *Parent; + unsigned LoopDepth; + int EscapeDepth = INT32_MAX; + SetVector EdgesSet; + SmallDenseMap EdgesMap; + bool Escaped = false; + bool Visited = false; + uint32_t AllocaSize = 0; + Type *AllocaType = nullptr; + Value *EscapedInst = nullptr; + unsigned EscapeState = 0; + // escape param locs + SetVector EscapeLoc; + SetVector ZeroPathEscapeLoc; + // info may escape param locs + SetVector InfoEscapeLoc; + ObjectLocation() = default; + virtual ~ObjectLocation() = default; + ObjectLocation(Value *V, Function *F, unsigned LoopDepth) + : ObjectLocation(TempLoc, V, F, LoopDepth) {} + +public: + // compute escape info between this and Src locations. + // for Path Value + // 0 : info copy -1: Src load from this 1: this load from Src + // for Return value + // 0 : nothing changed 1: Src's state changed -1: this's State Changed + virtual int EscapeEdge(ObjectLocation &Src, int Path) { + assert(this->Escaped); + if (Src.Escaped) { + return 0; + } + return Src.setInfoEscaped(Path); + } + + void insertEdge(ObjectLocation *Loc, int Path) { + if (Loc == this) { + return; + } + + if (EdgesSet.contains(Loc)) { + assert(EdgesMap.count(Loc) && "The EdgesMap and EdgesSet do not match."); + if (EdgesMap[Loc] > Path) { + EdgesMap[Loc] = Path; + } + } else { + if (Path <= 0 && LoopDepth > Loc->LoopDepth) { + Escaped = true; + } + EdgesSet.insert(Loc); + EdgesMap[Loc] = Path; + } + } + + void removeEdge(ObjectLocation *Loc) { + if (Loc == this) { + return; + } + if (EdgesSet.contains(Loc)) { + EdgesSet.remove(Loc); + EdgesMap.erase(Loc); + } + } + + void clearEdges() { + EdgesSet.clear(); + EdgesMap.clear(); + } + + bool isInfoEscaped() const { + return (EscapeState & InfoEscapedDirect) != 0 || + (EscapeState & InfoEscapedParam) != 0; + } + + // Info escaped location has an EscapeDepth, 0 means value load from this + // location's value has been escaped. EscapedDepth is 0 when value load from + // the value's EscapeDepth is 1. + int setInfoEscaped(int i) { + if (i < 0) { + if (!Escaped) { + Escaped = true; + return 1; + } + return 0; + } + if (EscapeDepth < i || (EscapeDepth == i && + (EscapeState & InfoEscapedDirect))) { + return 0; + } + EscapeState |= InfoEscapedDirect; + InfoEscapeLoc.clear(); + EscapeDepth = i; + return 1; + } + + virtual bool isParam() { return false; } + + virtual bool isNew() const { return false; } + + bool isTemp() { return !isParam() && !isNew(); } + + bool isEscaped() const { + return Escaped || (EscapeState & (EscapeParam | EscapeGlobal)) != 0; + } + + bool isEscapeParam() const { return EscapeParam & EscapeState; } + + bool setEscapeParam(const ObjectLocation &Loc) { + EscapeState |= EscapeParam; + unsigned OldSize = EscapeLoc.size(); + for (ObjectLocation *Param : Loc.EscapeLoc) { + EscapeLoc.insert(Param); + } + return OldSize != EscapeLoc.size(); + } + + bool setInfoEscapeParam(SetVector &LocVec, + int InfoEscapeDepth) { + if (InfoEscapeDepth < 0) { + if (!Escaped) { + if (isParam() && LocVec.size() == 1 && LocVec[0]->Val == Val) { + return false; + } + Escaped = true; + InfoEscapeLoc.clear(); + for (auto Param : LocVec) { + InfoEscapeLoc.insert(Param); + } + return true; + } else if (InfoEscapeLoc.size() != 0) { + for (auto Param : LocVec) { + InfoEscapeLoc.insert(Param); + } + } + return false; + } + if (EscapeDepth < InfoEscapeDepth) { + return 0; + } + EscapeState |= InfoEscapedParam; + if (EscapeDepth > InfoEscapeDepth) { + EscapeState &= ~InfoEscapedDirect; + EscapeDepth = InfoEscapeDepth; + InfoEscapeLoc.clear(); + for (auto Param : LocVec) { + InfoEscapeLoc.insert(Param); + } + return true; + } + unsigned OldSize = InfoEscapeLoc.size(); + for (auto Param : LocVec) { + InfoEscapeLoc.insert(Param); + } + return OldSize != InfoEscapeLoc.size(); + } + + void passEdges(bool DoClear) { + for (unsigned i = 0; i < EdgesSet.size(); i++) { + ObjectLocation *IL = EdgesSet[i]; + assert(EdgesMap.count(IL) && "The EdgesMap and EdgesSet do not match."); + + if (EscapeDepth != INT32_MAX) { + IL->setInfoEscaped(EscapeDepth + EdgesMap[IL]); + } + + for (unsigned j = i + 1; j < EdgesSet.size(); j++) { + ObjectLocation *JL = EdgesSet[j]; + assert(EdgesMap.count(JL) && "The EdgesMap and EdgesSet do not match."); + int I2J = std::min(EdgesMap[JL], - JL->EdgesMap[this]) - + std::max(EdgesMap[IL], - IL->EdgesMap[this]); + int J2I = std::min(EdgesMap[IL], - IL->EdgesMap[this]) - + std::max(EdgesMap[JL], - JL->EdgesMap[this]); + IL->insertEdge(JL, I2J); + JL->insertEdge(IL, J2I); + } + if (DoClear) { + IL->removeEdge(this); + } + } + if (DoClear) { + clearEdges(); + } + } + +protected: + ObjectLocation(LocationType LT, Value *V, Function *F, unsigned LoopDepth = 0) + : LocType(LT), Val(V), Parent(F), LoopDepth(LoopDepth) {} +}; + +struct NewLocation : ObjectLocation { +public: + bool HasRefs = false; + bool NonMoveArray = false; + BasicBlock *NewInstInsertBB = nullptr; + + NewLocation(Value *V, Function *F, unsigned LoopDepth, BasicBlock *PreHeader) + : ObjectLocation(NewLoc, V, F, LoopDepth) { + CallBase *I = dyn_cast(V); + GlobalVariable *Klass = getNewKlass(I); + if (Klass == nullptr) { + Escaped = true; + return; + } + AllocaType = getAllocaType(Klass, HasRefs, AllocaSize, NonMoveArray, + dyn_cast(Val)); + if (AllocaType == nullptr && !NonMoveArray) { // fail to create alloca type + Escaped = true; + } else { + NewInstInsertBB = PreHeader; + } + } + + bool isNew() const override { return true; } + + int EscapeEdge(ObjectLocation &Src, int Path) override { + if (isEscaped() && Src.isEscaped()) { + return 0; + } + if (isEscaped()) { + return Src.setInfoEscapeParam(InfoEscapeLoc, Path - 1); + } + if (Src.isEscaped()) { + return -setInfoEscapeParam(Src.InfoEscapeLoc, -1 - Path); + } + if (Src.isParam()) { + SetVector TmpLocs; + TmpLocs.insert(&Src); + int IsRet = (Src.Val == nullptr) ? 1 : 0; + // if a new obj is path 0 to args, it not escape. if path 0 to + // ret value, it escaped. + if (setInfoEscapeParam(TmpLocs, -Path - IsRet)) + return -1; + } + if (EscapeDepth != INT32_MAX) { + if (Src.EscapeDepth > EscapeDepth + Path) { + return Src.setInfoEscapeParam(InfoEscapeLoc, EscapeDepth + Path); + } + } + if (Src.EscapeDepth != INT32_MAX) { + return -setInfoEscapeParam(Src.InfoEscapeLoc, Src.EscapeDepth - Path); + } + return 0; + } + + static bool classof(const ObjectLocation *Location) { + return Location->isNew(); + } +}; + +struct ParamLocation : ObjectLocation { + ParamLocation(Value *V, Function *F) : ObjectLocation(ParamLoc, V, F, 0) { + EscapeLoc.insert(this); + } + + int EscapeEdge(ObjectLocation &Src, int Path) override { + if (Src.isNew()) { + return -Src.EscapeEdge(*this, -Path); + } + + if (Path == 0) { + if (Parent == Src.Parent) { + ZeroPathEscapeLoc.insert(&Src); + } + return 0; + } + if (Path > 0) { + return -setEscapeParam(Src); + } + return Src.setEscapeParam(*this); + } + + bool isParam() override { return true; } +}; + +Value *getObjectVal(Instruction &I) { + auto CI = dyn_cast(&I); + if (CI && CI->getCalledFunction()) { + if (isRewriteableCangjieMallocFunc(CI->getCalledFunction())) + return &I; + } + return nullptr; +} + +#ifndef NDEBUG +void doPrintFuncObjects(SmallVector &AllLocations) { + for (unsigned i = 0; i < AllLocations.size(); i++) { + if (AllLocations[i]->Val) { + LLVM_DEBUG(dbgs() << AllLocations[i]->Parent->getName() << " : " + << *AllLocations[i]->Val << "\n"); + } else { + LLVM_DEBUG(dbgs() << AllLocations[i]->Parent->getName() << " :retval.\n"); + } + } +} +#endif + +// simple version. escape global, param's object are all escaped. We don't +// consider param and object passed by function calls. +// 0. escape from return inst +// 1. escape when store into/load from escaped object. +// 2. object size bigger than threshold. +// 3. escape as function's params. +class EADepthImpl : public InstVisitor { + // Befriend the base class so it can delegate to private visit methods. + friend class InstVisitor; + + // 27: 0x7ffffff + static constexpr int MaxSupportArgSize = 27; + +public: + EADepthImpl(CallGraphUpdater *CGUpdater, Module *M, + function_ref LookLoop) + : CGUpdater(CGUpdater), M(M), LookupLoopInfo(LookLoop) {} + + void insertSCCFunctions(Function *Func) { + SCCFunctions.insert(Func); + } + + void initialize() { + ArgsObjectNum = 0; + MayReplaceObjectNum = 0; + + // init object locations + for (Function *Func : SCCFunctions) { + for (unsigned i = 0; i < Func->arg_size(); i++) { + auto arg = Func->getArg(i); + if (arg->getType()->isPointerTy()) { + enqueueUsers(arg); + ObjectLocation *Loc = new ParamLocation(arg, Func); + AllLocations.push_back(Loc); + ValueToLocMap[arg] = Loc; + } + } + if (auto PT = dyn_cast(Func->getReturnType())) { + // multi return. + if (PT->getAddressSpace() == 1) { + ObjectLocation *Loc = new ParamLocation(nullptr, Func); + AllLocations.push_back(Loc); + ValueToLocMap[Func] = Loc; + } + } + } + ArgsObjectNum = AllLocations.size(); + for (Function *Func : SCCFunctions) { + LoopInfo &LI = LookupLoopInfo(*Func); + for (BasicBlock &BB : *Func) { + for (Instruction &I : BB) { + collectStructCastToAS1Inst(I); + Value *ObjectVal = getObjectVal(I); + if (!ObjectVal) { + if (auto PT = dyn_cast(I.getType())) { + if (PT->getAddressSpace() == 1) { + Queue.push_back(&I); + } + } + continue; + } + unsigned LoopDepth = LI.getLoopDepth(&BB); + BasicBlock *PreHeader = nullptr; + if (LoopDepth > 0) { + PreHeader = + LI.getLoopFor(&BB)->getOutermostLoop()->getLoopPreheader(); + } + ObjectLocation *Loc = + new NewLocation(ObjectVal, Func, LoopDepth, PreHeader); + AllLocations.push_back(Loc); + ValueToLocMap[ObjectVal] = Loc; + enqueueUsers(ObjectVal); + } + } + } + MayReplaceObjectNum = AllLocations.size() - ArgsObjectNum; + +#ifndef NDEBUG + doPrintFuncObjects(AllLocations); +#endif + } + + void collectStructCastToAS1Inst(Instruction &I) { + if (isa(&I)) { + Value *Base = findMemoryBasePointer(&I); + if (AllocaInst *AI = dyn_cast(Base)) { + StructCastToAS1.push_back(AI); + } + } + } + + void calculateEscapeInfo(uint32_t &EscapeInfo, ObjectLocation *CurLoc, + ObjectLocation *L) const { + if (L == CurLoc || CurLoc->Parent != L->Parent) { + return; + } + if (!L->Val) { + // 30: the second-highest bit present the arg escape to return value + EscapeInfo |= 1 << 30; + return; + } + Argument *Arg = dyn_cast(L->Val); + if (Arg == nullptr) { + // 30: the second-highest bit present the arg escape to return value + EscapeInfo |= 1 << 30; + } else { + EscapeInfo |= 1 << Arg->getArgNo(); + } + } + + void recordParamEscapeInfos() const { + for (unsigned Index = 0; Index < ArgsObjectNum; Index++) { + auto CurLoc = AllLocations[Index]; + uint32_t HighEscapeInfo = 0; + uint32_t LowEscapeInfo = 0; + if (CurLoc->EscapeDepth != INT32_MAX) { + HighEscapeInfo |= 1 << 29; // 29: InfoEscaped Info + uint32_t RecordEscapeDepth = + CurLoc->EscapeDepth > 3 ? 3 : CurLoc->EscapeDepth; + HighEscapeInfo |= RecordEscapeDepth << 27; // 27, 28 : escaped depth + } + if (CurLoc->Escaped) { + HighEscapeInfo |= 1 << 31; // 31: highest bit present global escape + } else { + if (CurLoc->EscapeLoc.size() > 1) { + for (auto L : CurLoc->EscapeLoc) { + calculateEscapeInfo(HighEscapeInfo, CurLoc, L); + } + } + for (auto L : CurLoc->ZeroPathEscapeLoc) { + calculateEscapeInfo(LowEscapeInfo, CurLoc, L); + } + } + uint64_t EscapeInfo = HighEscapeInfo; + // 32: HighEscapeInfo + EscapeInfo = (EscapeInfo << 32) | LowEscapeInfo; + if (EscapeInfo != 0) { + if (CurLoc->Val == nullptr) { + // return val + CurLoc->Parent->setEscapeInfo(EscapeInfo); + LLVM_DEBUG(dbgs() << CurLoc->Parent->getName() << " : " + << Twine::utohexstr(EscapeInfo) << "\n"); + } else { + Argument *CurArg = dyn_cast(CurLoc->Val); + CurArg->setEscapeInfo(EscapeInfo); + LLVM_DEBUG(dbgs() << CurArg->getParent()->getName() << " : " + << CurArg->getName() << ", ArgEscapeInfo: " + << Twine::utohexstr(EscapeInfo) << "\n"); + } + } + } + } + + bool finishAll(CallGraphUpdater *CGUpdater) { + recordParamEscapeInfos(); + bool Changed = false; + NonEscapeRewriter Rewriter(CGUpdater, M); + for (unsigned i = ArgsObjectNum; i < MayReplaceObjectNum + ArgsObjectNum; + i++) { + // New Obj in loop can't handle now. + if (!AllLocations[i]->isEscaped()) { + NewLocation *New = dyn_cast(AllLocations[i]); + Changed |= Rewriter.rewrite(dyn_cast(New->Val), + New->NewInstInsertBB); + } + } + + Rewriter.setStructAS1Flag(); + for (unsigned i = 0; i < StructCastToAS1.size(); i++) { + Changed |= Rewriter.handleStructAS1Cast(StructCastToAS1[i]); + } + + for (unsigned i = 0; i < AllLocations.size(); i++) { + delete AllLocations[i]; + } + AllLocations.clear(); + StructCastToAS1.clear(); + if (HeapLoc != nullptr) { + delete HeapLoc; + HeapLoc = nullptr; + } + for (Function *F : SCCFunctions) { + F->setEscapeAnalysis(); + } + return Changed; + } + + bool run() { + if (AllLocations.size() == 0 && StructCastToAS1.size() == 0 && + Queue.empty()) { + return false; + } + HeapLoc = new ObjectLocation(nullptr, nullptr, 0); + HeapLoc->Escaped = true; + + // visit all relative instruction and append their relationship by edges. + // now the origin escape loc is definite. + while (!Queue.empty()) { + visit(Queue.pop_back_val()); + } + + LLVM_DEBUG(dbgs() << "locations's escape info before walk edges.\n"); + for (unsigned i = 0; i < AllLocations.size(); i++) { + if (AllLocations[i]->isEscaped()) + LLVM_DEBUG(dbgs() << *AllLocations[i]->Val << " : escape state " + << AllLocations[i]->EscapeState << "\n"); + } + + walkEdges(); + + for (unsigned i = ArgsObjectNum; i < MayReplaceObjectNum + ArgsObjectNum; + i++) { + if (!AllLocations[i]->isEscaped()) { + LLVM_DEBUG(dbgs() << " not escape: " << *AllLocations[i]->Val << "\n"); + } else { + LLVM_DEBUG(dbgs() << " escape to heap: " + << *AllLocations[i]->Val << "\n"); + if (AllLocations[i]->EscapedInst != nullptr) { + LLVM_DEBUG(dbgs() << "escape instruction:" + << *AllLocations[i]->EscapedInst << "\n"); + } + } + } + + return finishAll(CGUpdater); + } + + bool isValueInvalid(Value *V) { + if (V->getValueID() == llvm::Value::UndefValueVal) { + report_fatal_error("store/gcwrite an undef value!"); + } + return V->getValueID() == llvm::Value::ConstantPointerNullVal; + } + + bool isGCReadIntrinsic(Value *Base) { + if (IntrinsicInst *II = dyn_cast(Base)) { + switch (II->getIntrinsicID()) { + case Intrinsic::cj_gcread_ref: + case Intrinsic::cj_gcread_static_ref: + return true; + default: + return false; + } + } + return false; + } + + ObjectLocation *getOrCreateLocation(Value *V) { + if (!dyn_cast(V) && !dyn_cast(V)) { + if (isa(V) || isa(V)) { + Value *Base = findMemoryBasePointer(V); + PointerType *PT = dyn_cast(Base->getType()); + if (PT == nullptr) { + return nullptr; + } + Type *PointeeType = PT->getNonOpaquePointerElementType(); + if (containsGCPtrType(PointeeType)) { + return HeapLoc; + } + } + return nullptr; + } + auto Loc = ValueToLocMap[V]; + if (!Loc) { + Value *Base = findMemoryBasePointer(V); + Loc = ValueToLocMap[Base]; + if (Loc) { + ValueToLocMap[V] = Loc; + return Loc; + } + unsigned LoopDepth = 0; + + if (LoadInst *Load = dyn_cast(Base)) { + ObjectLocation* SrcLoc = getOrCreateLocation(Load->getPointerOperand()); + // If SrcLoc is nullptr, load value escaped. + if (SrcLoc != nullptr) { + LoopDepth = SrcLoc->LoopDepth; + } + } else if (Instruction *I = dyn_cast(Base)) { + LoopInfo &LI = LookupLoopInfo(*I->getParent()->getParent()); + LoopDepth = LI.getLoopDepth(I->getParent()); + } + ObjectLocation *NewLoc = new ObjectLocation( + Base, dyn_cast(V)->getParent()->getParent(), LoopDepth); + AllLocations.push_back(NewLoc); + ValueToLocMap[Base] = NewLoc; + ValueToLocMap[V] = NewLoc; + Loc = NewLoc; + // we will do enqueueUsers in visitLoadInst later. + if (isa(Base) || isa(Base) || + isa(Base) || isGCReadIntrinsic(Base)) { + if (Visited.insert(cast(Base)).second) { + Queue.push_back(cast(Base)); + } + } else if (isa(Base) || isa(Base)) { + enqueueUsers(Base); + } + } + return Loc; + } + + void escapeHeap(ObjectLocation &Loc, int Path = -1) const { + if (Loc.Val == nullptr) { + return; + } + HeapLoc->insertEdge(&Loc, Path); + Loc.insertEdge(HeapLoc, -Path); + } + + void handleSCCFunctionCall(CallBase &I, Function *CalledFunction) { + for (unsigned Index = 0; Index < CalledFunction->arg_size(); Index++) { + auto CallArg = I.getOperand(Index); + if (CallArg->getType()->isPointerTy()) { + auto VLoc = getOrCreateLocation(CallArg); + if (VLoc == nullptr) { + continue; + } + auto PLoc = getOrCreateLocation(CalledFunction->getArg(Index)); + VLoc->insertEdge(PLoc, 0); + PLoc->insertEdge(VLoc, 0); + } + } + if (auto PT = dyn_cast(I.getType())) { + if (PT->getAddressSpace() == 1) { + ObjectLocation *Ret = ValueToLocMap[CalledFunction]; + ObjectLocation *Loc = getOrCreateLocation(&I); + Ret->insertEdge(Loc, 0); + Loc->insertEdge(Ret, 0); + } + } + } + + void handleEscapeInfo(ObjectLocation *VLoc, CallBase &I, + uint64_t EscapeInfo) { + uint32_t HighEscapeInfo = EscapeInfo >> 32; + uint32_t LowEscapeInfo = EscapeInfo; + if (HighEscapeInfo & 0x20000000) { + // 27 28 : escape depth + VLoc->setInfoEscaped((HighEscapeInfo & 0x18000000) >> 27); + } + if (HighEscapeInfo & 0x80000000) { + VLoc->Escaped = true; + VLoc->EscapedInst = &I; + escapeHeap(*VLoc); + return; + } + // escape to Return Val + if (HighEscapeInfo & 0x40000000) { + auto PLoc = getOrCreateLocation(&I); + if (PLoc != nullptr) { + PLoc->insertEdge(VLoc, -1); + VLoc->insertEdge(PLoc, 1); + } + } + HighEscapeInfo &= 0x7ffffff; + unsigned CurArg = 0; + while (HighEscapeInfo != 0) { + if (HighEscapeInfo & 0x1) { + auto PLoc = getOrCreateLocation(I.getOperand(CurArg)); + if (PLoc != nullptr) { + PLoc->insertEdge(VLoc, -1); + VLoc->insertEdge(PLoc, 1); + } + } + HighEscapeInfo = HighEscapeInfo >> 1; + CurArg++; + } + CurArg = 0; + if (LowEscapeInfo & 0x40000000) { + auto PLoc = getOrCreateLocation(&I); + PLoc->insertEdge(VLoc, 0); + VLoc->insertEdge(PLoc, 0); + } + LowEscapeInfo &= 0x1fffffff; + while (LowEscapeInfo != 0) { + if (LowEscapeInfo & 0x1) { + auto PLoc = getOrCreateLocation(I.getOperand(CurArg)); + if (PLoc != nullptr) { // recursive call + PLoc->insertEdge(VLoc, 0); + VLoc->insertEdge(PLoc, 0); + } + } + LowEscapeInfo = LowEscapeInfo >> 1; + CurArg++; + } + } + + void handleNormalCall(CallBase &I, Function *CalledFunction) { + for (unsigned Index = 0; Index < CalledFunction->arg_size(); Index++) { + auto CallArg = I.getOperand(Index); + if (!CallArg->getType()->isPointerTy()) { + continue; + } + auto VLoc = getOrCreateLocation(CallArg); + if (VLoc == nullptr) { + continue; + } + auto Argument = CalledFunction->getArg(Index); + uint64_t EscapeInfo = Argument->getEscapeInfo(); + handleEscapeInfo(VLoc, I, EscapeInfo); + } + if (CalledFunction->getEscapeInfo() != 0) { + auto VLoc = getOrCreateLocation(&I); + handleEscapeInfo(VLoc, I, CalledFunction->getEscapeInfo()); + } + } + + void handleCallArgs(CallBase &I) { + Function *CalledFunction = I.getCalledFunction(); + if (!CalledFunction || CalledFunction->isVarArg() || + CalledFunction->isDeclaration() || + !CalledFunction->hasEscapeAnalysis() || + CalledFunction->arg_size() > MaxSupportArgSize) { + escapeAllOperand(&I); + return; + } + if (SCCFunctions.count(CalledFunction)) { + handleSCCFunctionCall(I, CalledFunction); + } else { + handleNormalCall(I, CalledFunction); + } + } + + void handleLoad(Value *P, Instruction *I) { + ObjectLocation *SrcLoc = getOrCreateLocation(P); + ObjectLocation *Loc = getOrCreateLocation(I); + if (SrcLoc) { + if (SrcLoc->isParam()) { + Loc->EscapedInst = I; + Loc->EscapeLoc.insert(SrcLoc); + Loc->EscapeState |= EscapeOrigin | EscapeParam; + } + Loc->insertEdge(SrcLoc, 1); + SrcLoc->insertEdge(Loc, -1); + } else { + Loc->Escaped = true; + Loc->EscapedInst = I; + Loc->EscapeState = EscapeOrigin | EscapeGlobal; + escapeHeap(*Loc); + } + enqueueUsers(I); + } + + void visitLoadInst(LoadInst &I) { + if (!containsGCPtrType(I.getType())) { + return; + } + + auto Src = I.getPointerOperand(); + if (isa(I.getType())) { + handleMemcpyOrWriteAgg(&I, &I, Src); + return; + } + + handleLoad(Src, &I); + } + + void visitStoreInst(StoreInst &I) { + Value *V = I.getValueOperand(); + if (!containsGCPtrType(V->getType()) || isValueInvalid(V)) { + return; + } + Value *P = I.getPointerOperand(); + if (isa(V->getType())) { + handleMemcpyOrWriteAgg(&I, P, V); + return; + } + auto Loc = getOrCreateLocation(P); + auto SrcLoc = getOrCreateLocation(V); + if (!Loc || !SrcLoc) { + return; + } + if (SrcLoc->LoopDepth > Loc->LoopDepth) { + SrcLoc->Escaped = true; + SrcLoc->EscapedInst = &I; + SrcLoc->EscapeState = EscapeLoop; + escapeHeap(*SrcLoc); + } + Loc->insertEdge(SrcLoc, -1); + SrcLoc->insertEdge(Loc, 1); + } + + void visitGetElementPtrInst(GetElementPtrInst &GEP) { enqueueUsers(&GEP); } + + void visitBitCastInst(BitCastInst &BC) { enqueueUsers(&BC); } + + void visitAddrSpaceCastInst(AddrSpaceCastInst &AI) { enqueueUsers(&AI); } + + void visitInvokeInst(InvokeInst &II) { + handleCallArgs(II); + } + + void visitCallInst(CallInst &I) { + if (I.getCalledFunction()) { + if (handleCangjieSL(&I)) { + return; + } + } + if (const Function *F = I.getCalledFunction()) { + switch (F->getIntrinsicID()) { + default: + break; + case Intrinsic::memcpy: + case Intrinsic::memmove: + visitMemCpyOrMove(I); + return; + case Intrinsic::memset: + case Intrinsic::lifetime_start: + case Intrinsic::lifetime_end: + return; + } + } + handleCallArgs(I); + } + + void visitMemCpyOrMove(Instruction &I) { + Value *P = I.getOperand(0); + Value *V = I.getOperand(1); + handleMemcpyOrWriteAgg(&I, P, V); + } + + void visitReturnInst(ReturnInst &I) { + Value *Ret = I.getOperand(0); + if (!isGCPointerType(Ret->getType())) { + return; + } + auto Loc = getOrCreateLocation(Ret); + if (Loc) { + Loc->EscapedInst = &I; + Loc->EscapeLoc.insert(Loc); + Loc->EscapeState |= EscapeOrigin | EscapeRet | EscapeParam; + auto RetLoc = ValueToLocMap[I.getParent()->getParent()]; + if (RetLoc != nullptr) { + Loc->insertEdge(RetLoc, 0); + RetLoc->insertEdge(Loc, 0); + } + } + } + + void updateLoc(ObjectLocation *Loc, Value *Op) { + if (auto OpLoc = getOrCreateLocation(Op)) { + Loc->insertEdge(OpLoc, 0); + OpLoc->insertEdge(Loc, 0); + } + } + + void visitPHINode(PHINode &PI) { + auto Loc = getOrCreateLocation(&PI); + for (unsigned Idx = 0; Idx < PI.getNumIncomingValues(); Idx++) { + updateLoc(Loc, PI.getIncomingValue(Idx)); + } + enqueueUsers(&PI); + } + + void visitSelectInst(SelectInst &SI) { + auto Loc = getOrCreateLocation(&SI); + updateLoc(Loc, SI.getTrueValue()); + updateLoc(Loc, SI.getFalseValue()); + } + + void visitExtractValueInst(ExtractValueInst &EVT) { + if (!containsGCPtrType(EVT.getType())) { + return; + } + Value *Src = EVT.getAggregateOperand(); + if (isa(EVT.getType())) { + handleMemcpyOrWriteAgg(&EVT, &EVT, Src); + return; + } + ObjectLocation *SrcLoc = getOrCreateLocation(Src); + ObjectLocation *Loc = getOrCreateLocation(&EVT); + if (SrcLoc) { + if (SrcLoc->isParam()) { + Loc->EscapedInst = &EVT; + Loc->EscapeLoc.insert(SrcLoc); + Loc->EscapeState |= EscapeOrigin | EscapeParam; + } + Loc->insertEdge(SrcLoc, 1); + SrcLoc->insertEdge(Loc, -1); + } else { + Loc->Escaped = true; + Loc->EscapedInst = &EVT; + Loc->EscapeState = EscapeOrigin | EscapeGlobal; + escapeHeap(*Loc); + } + enqueueUsers(&EVT); + } + + void visitInstruction(Instruction &I) { escapeAllOperand(&I); } + +private: + CallGraphUpdater *CGUpdater; + + Module *M; + + function_ref LookupLoopInfo; + + SmallVector AllLocations; + + unsigned ArgsObjectNum = 0; + + unsigned MayReplaceObjectNum = 0; + + SmallVector Queue; + + SmallPtrSet Visited; + + SmallPtrSet SCCFunctions; + + ObjectLocation *HeapLoc = nullptr; + + SmallVector StructCastToAS1; + + DenseMap ValueToLocMap; + void enqueueUsers(Value *V) { + Value *Base = findMemoryBasePointer(V); + if (Base->getValueID() == llvm::Value::ConstantPointerNullVal) { + return; + } + if (Base != V) + ValueToLocMap[V] = ValueToLocMap[Base]; + + for (Use &U : V->uses()) + if (Visited.insert(U.getUser()).second) { + Queue.push_back(dyn_cast(U.getUser())); + } + } + + void escapeAllOperand(Instruction *I) { + unsigned IsClosureCall = 0; + if (auto CI = dyn_cast(I)) { + if (CI->getMetadata("IsClosureCall")) { + IsClosureCall = 1; + } + } + + if (isGCPointerType(I->getType()) || + isMemoryContainsGCPtrType(I->getType())) { + if (auto Loc = getOrCreateLocation(I)) { + Loc->Escaped = true; + Loc->EscapedInst = I; + Loc->EscapeState |= EscapeOrigin | EscapeGlobal; + escapeHeap(*Loc); + } + } + if (IsClosureCall) { + auto V = I->getOperand(0); + if (auto Loc = getOrCreateLocation(V)) { + Loc->setInfoEscaped(0); + } + } + for (unsigned i = IsClosureCall; i < I->getNumOperands() - IsClosureCall; + i++) { + auto V = I->getOperand(i); + if (!(isGCPointerType(V->getType()) || + isMemoryContainsGCPtrType(V->getType()))) + continue; + if (auto Loc = getOrCreateLocation(V)) { + Loc->Escaped = true; + Loc->EscapedInst = I; + Loc->EscapeState |= EscapeOrigin | EscapeGlobal; + escapeHeap(*Loc); + } + } + } + + void handleMemcpyOrWriteAgg(Instruction *I, Value *P, Value *V) { + auto VLoc = getOrCreateLocation(V); + auto PLoc = getOrCreateLocation(P); + if (!VLoc || !PLoc) { + return; + } + + if (VLoc->LoopDepth > PLoc->LoopDepth) { + VLoc->setInfoEscaped(0); + } + PLoc->insertEdge(VLoc, 0); + VLoc->insertEdge(PLoc, 0); + } + + void handleGCWrite(IntrinsicInst *II) { + Value *V = II->getOperand(0); + Type *T = V->getType(); + if (!T->isPointerTy() || T->getPointerAddressSpace() != 1 || + isValueInvalid(V)) { + return; + } + + Value *P = II->getOperand(2); // 2: direct store pointer + auto VLoc = getOrCreateLocation(V); + auto PLoc = getOrCreateLocation(P); + if (VLoc == nullptr) + return; + assert(PLoc != nullptr && "PLoc is null when handle GCWrite!"); + if (VLoc->LoopDepth > PLoc->LoopDepth) { + VLoc->Escaped = true; + VLoc->EscapedInst = II; + VLoc->EscapeState = EscapeLoop; + escapeHeap(*VLoc); + } + PLoc->insertEdge(VLoc, -1); + VLoc->insertEdge(PLoc, 1); + } + + void handleGCWriteStatic(IntrinsicInst *II) { + Value *V = II->getOperand(0); + Type *T = V->getType(); + if (!T->isPointerTy() || T->getPointerAddressSpace() != 1 || + isValueInvalid(V)) { + return; + } + + auto Loc = getOrCreateLocation(V); + if (!Loc) + return; + Loc->Escaped = true; + Loc->EscapedInst = II; + Loc->EscapeState |= EscapeOrigin | EscapeGlobal; + escapeHeap(*Loc); + } + + void handleGCRead(IntrinsicInst *II, bool IsStatic) { + Value *P = II->getArgOperand(IsStatic ? 0 : 1); + handleLoad(P, II); + } + + void handleGCWriteAgg(Instruction *I, bool IsStatic) { + Value *P = I->getOperand(IsStatic ? 0 : 1); + Value *V = I->getOperand(IsStatic ? 1 : 2); + handleMemcpyOrWriteAgg(I, P, V); + } + + void handleGCReadAgg(Instruction *I, bool IsStatic) { + Value *P = I->getOperand(0); + Value *V = I->getOperand(IsStatic ? 1 : 2); + handleMemcpyOrWriteAgg(I, P, V); + } + + void handleArrayCopyto(Instruction *I) { + Value *P = I->getOperand(0); + Value *V = I->getOperand(2); + handleMemcpyOrWriteAgg(I, P, V); + } + + bool handleCangjieSL(Instruction *I) { + IntrinsicInst *II = dyn_cast(I); + if (II) { + switch (II->getIntrinsicID()) { + case Intrinsic::cj_gcwrite_ref: + handleGCWrite(II); + return true; + case Intrinsic::cj_gcwrite_static_ref: + handleGCWriteStatic(II); + return true; + case Intrinsic::cj_gcwrite_struct: + handleGCWriteAgg(II, false); + return true; + case Intrinsic::cj_gcwrite_static_struct: + case Intrinsic::cj_assign_generic: + case Intrinsic::cj_copy_struct_field: + handleGCWriteAgg(II, true); + return true; + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + handleArrayCopyto(I); + return true; + case Intrinsic::cj_gcread_ref: + handleGCRead(II, false); + return true; + case Intrinsic::cj_gcread_static_ref: + handleGCRead(II, true); + return true; + case Intrinsic::cj_gcread_struct: + handleGCReadAgg(II, false); + return true; + case Intrinsic::cj_gcread_static_struct: + handleGCReadAgg(II, true); + return true; + default: + break; + } + } + return false; + } + + void walkEdge(ObjectLocation &Loc) const { + if (Loc.Visited) { + return; + } + + SmallVector VisitLoc; + VisitLoc.push_back(&Loc); + Loc.Visited = true; + while (!VisitLoc.empty()) { + auto CurLoc = VisitLoc.pop_back_val(); + bool PushCur = false; + for (auto *EdgeLoc : CurLoc->EdgesSet) { + int EdgeState = CurLoc->EscapeEdge(*EdgeLoc, CurLoc->EdgesMap[EdgeLoc]); + if (EdgeState == 1) { + VisitLoc.push_back(EdgeLoc); + EdgeLoc->Visited = true; + } + if (EdgeState == -1 && PushCur == false) { + VisitLoc.push_back(CurLoc); + PushCur = true; + } + } + } + } + + void walkEdges() { + deleteTempLocs(); + + SmallVector EscapedLoc; + for (unsigned i = 0; i < ArgsObjectNum + MayReplaceObjectNum; i++) { + EscapedLoc.push_back(AllLocations[i]); + } + if (HeapLoc != nullptr) { + EscapedLoc.push_back(HeapLoc); + } + while (!EscapedLoc.empty()) { + auto CurLoc = EscapedLoc.pop_back_val(); + walkEdge(*CurLoc); + } + } + + void deleteTempLocs() { + if (SCCFunctions.size() != 1) { + for (unsigned Index = 0; Index < ArgsObjectNum; Index++) { + AllLocations[Index]->passEdges(false); + } + } + for (unsigned Index = ArgsObjectNum + MayReplaceObjectNum; + Index < AllLocations.size(); Index++) { + ObjectLocation *CurLoc = AllLocations[Index]; + CurLoc->passEdges(true); + AllLocations[Index] = nullptr; + delete CurLoc; + } + } +}; +} +class GCPtr; +static void memPtrspreadEscape(BasicBlock *BB, unsigned ES, + DenseMap &SuccBBInfo, + GCPtr *P, SmallVector &Offset, + bool Direct); + +static void countRefOffsets(StructType *ST, SmallVector &Offsets, + uint64_t BaseOff, const DataLayout &DL, + unsigned Left, unsigned Right); + +static void countArrayOffsets(ArrayType *AT, SmallVector &Offsets, + uint64_t BaseOff, const DataLayout &DL, + unsigned Left, unsigned Right) { + uint32_t Size = AT->getNumElements(); + Type *ElementType = AT->getElementType(); + if (Size == 0) { // Array type + uint32_t ElementSize = DL.getTypeAllocSize(ElementType); + Size = (Right + ElementSize - 1) / ElementSize; + } + if (isa(ElementType)) { + for (unsigned Idx = 0; Idx < Size; Idx++) { + uint64_t Off = BaseOff + Idx * 8; + if (Off >= Left && Off < Right) { + Offsets.push_back(Off); + } + } + } else if (StructType *ArrayStruct = dyn_cast(ElementType)) { + uint32_t DerivedOffset = DL.getStructLayout(ArrayStruct)->getSizeInBytes(); + for (unsigned Idx = 0; Idx < Size; Idx++) { + countRefOffsets(ArrayStruct, Offsets, BaseOff + Idx * DerivedOffset, DL, + Left, Right); + } + } else if (ArrayType *AET = dyn_cast(ElementType)) { + uint32_t DerivedOffset = DL.getTypeSizeInBits(ElementType); + for (unsigned Idx = 0; Idx < Size; Idx++) { + countArrayOffsets(AET, Offsets, BaseOff + Idx * DerivedOffset, DL, Left, + Right); + } + } +} + +static void countRefOffsets(StructType *ST, SmallVector &Offsets, + uint64_t BaseOff, const DataLayout &DL, + unsigned Left, unsigned Right) { + for (uint32_t Idx = 0; Idx < ST->getNumElements(); Idx++) { + uint64_t Off = DL.getStructLayout(ST)->getElementOffset(Idx) + BaseOff; + Type *ET = ST->getElementType(Idx); + if (Off > Right) { + return; + } + if (PointerType *EPT = dyn_cast(ET)) { + if (isGCPointerType(EPT) && Off >= Left) { + Offsets.push_back(Off); + } + } else if (StructType *EST = dyn_cast(ET)) { + countRefOffsets(EST, Offsets, Off, DL, Left, Right); + } else if (ArrayType *AT = dyn_cast(ET)) { + countArrayOffsets(AT, Offsets, Off, DL, Left, Right); + } + } +} + +class GCPtr { +public: + explicit GCPtr(Value *Val, unsigned Depth, CJEscapeAnalysis &EAImpl) + : P(Val), IsLoad(isa(Val) || isa(Val)), + IsPhiOrSelect(isa(Val) || isa(Val)), + LoopDepth(Depth), EA(EAImpl) { + if (IntrinsicInst *II = dyn_cast(Val)) + IsLoad |= II->getIntrinsicID() == Intrinsic::cj_gcread_ref; + } + + virtual ~GCPtr() = default; + + virtual bool equal(GCPtr *V) { return V->isPtr() && (P == V->P); } + + static GCPtr *get(Value *Val, CJEscapeAnalysis &EAImpl) { + if (EAImpl.AllPtrLocInfo.count(Val)) { + return EAImpl.AllPtrLocInfo[Val]; + } + return nullptr; + } + + static GCPtr *create(Value *Val, CJEscapeAnalysis &EAImpl, + LoopInfo *LI = nullptr) { + if (Val->getValueID() == llvm::Value::ConstantPointerNullVal) { + return nullptr; + } + int Off; + Val = EAImpl.getBaseValue(Val, Off); + if (EAImpl.AllPtrLocInfo.count(Val)) { + return EAImpl.AllPtrLocInfo[Val]; + } + unsigned LD = 0; + if (LoadInst *Load = dyn_cast(Val)) { + GCPtr *Src = GCPtr::create(Load->getPointerOperand(), EAImpl, LI); + // if Src is nullptr, Load inst escaped. + if (Src != nullptr) { + LD = Src->LoopDepth; + } + } else if (Instruction *I = dyn_cast(Val)) { + if (LI != nullptr) { + LD = LI->getLoopDepth(I->getParent()); + } + } + GCPtr *New = new GCPtr(Val, LD, EAImpl); + if (EAImpl.isEscapedValue(Val)) { + Function *CurFunc = EAImpl.ProcessedFunc; + EAImpl.EscapeBBInfo[&CurFunc->getEntryBlock()][New] = Escaped; + } + EAImpl.AllPtrLocInfo[Val] = New; + return New; + } + + virtual bool isPtr() { return true; } + + void setInfoEscaped() { InfoEscapeInfo = true; } + + bool isInitializing() { return InitializeFlag == Initializing; } + + void setInitializing() { InitializeFlag = Initializing; } + + bool isInitialized() { return InitializeFlag == Initialized; } + + void setInitialized() { InitializeFlag = Initialized; } + + bool isLoad() { return IsLoad; } + + bool isPhiOrSelect() { return IsPhiOrSelect; } + + virtual bool isDerivedPtr() { return IsLoad || IsPhiOrSelect; } + + unsigned getDirectSpread() { return DirectSpread; } + + bool canSpread(GCPtr *V, unsigned ES) { + return V->isDerivedPtr() && ES > V->getDirectSpread(); + } + + bool isInfoEscaped() { return InfoEscapeInfo; } + + virtual void spreadInfoEscape(BasicBlock *BB, unsigned ES, + DenseMap &SuccBBInfo, + bool Direct = true) { + if (isDerivedPtr() && Direct && DirectSpread < InfoEscaped) { + DirectSpread = InfoEscaped; + } else if (SuccBBInfo.count(this) && SuccBBInfo[this] >= InfoEscaped) { + return; + } + if (SuccBBInfo[this] < InfoEscaped) { + SuccBBInfo[this] = InfoEscaped; + } + for (unsigned It = 0; It < MPs.size(); ++It) { + if (!SuccBBInfo.count(MPs[It]) || SuccBBInfo[MPs[It]] < ES) { + SuccBBInfo[MPs[It]] = ES; + MPs[It]->spreadEscape(BB, ES, SuccBBInfo, true); + } + } + if (Direct) { + for (auto &V : BaseValue) { + V.first->spreadInfoEscape(BB, ES, SuccBBInfo, true); + } + } + if (Alias.size() != 0) { + for (auto V : Alias) { + V.first->spreadInfoEscape(BB, ES, SuccBBInfo, false); + } + } + for (auto &V : InfoEscapedVec) { + V->spreadInfoEscape(BB, ES, SuccBBInfo, Direct); + } + } + + virtual void spreadMemEscape(BasicBlock *BB, unsigned ES, + DenseMap &SuccBBInfo, + SmallVector &Offsets, + bool Direct = true) { + if (IsVisiting) { + return; + } + IsVisiting = true; + int CurOffset = Offsets.pop_back_val(); + if (CurOffset >= 0) { + Offsets.push_back(CurOffset); + memPtrspreadEscape(BB, ES, SuccBBInfo, this, Offsets, Direct); + IsVisiting = false; + return; + } + if (isPhiOrSelect()) { + if (Offsets.size() > 0) { + for (unsigned I = 0; I < MPs.size(); ++I) + MPs[I]->spreadMemEscape(BB, ES, SuccBBInfo, Offsets, Direct); + for (auto &V : InfoEscapedVec) { + for (unsigned I = 0; I < V->MPs.size(); ++I) + V->MPs[I]->spreadMemEscape(BB, ES, SuccBBInfo, Offsets, Direct); + } + } else { + for (unsigned I = 0; I < MPs.size(); ++I) + MPs[I]->spreadEscape(BB, ES, SuccBBInfo, true); + for (auto &V : InfoEscapedVec) { + for (unsigned I = 0; I < V->MPs.size(); ++I) + V->MPs[I]->spreadEscape(BB, ES, SuccBBInfo, Direct); + } + } + Offsets.push_back(CurOffset); + if (Direct) { + for (auto &V : BaseValue) { + V.first->spreadMemEscape(BB, ES, SuccBBInfo, Offsets, Direct); + } + } + for (auto V : Alias) { + V.first->spreadMemEscape(BB, ES, SuccBBInfo, Offsets, Direct); + } + IsVisiting = false; + return; + } + if (Offsets.size() > 0) { + for (unsigned I = 0; I < MPs.size(); ++I) + MPs[I]->spreadMemEscape(BB, ES, SuccBBInfo, Offsets, Direct); + if (Direct) { + for (auto &V : BaseValue) { + for (unsigned I = 0; I < V.first->MPs.size(); ++I) + V.first->MPs[I]->spreadMemEscape(BB, ES, SuccBBInfo, Offsets, + Direct); + } + } + for (auto V : Alias) { + for (unsigned I = 0; I < V.first->MPs.size(); ++I) + V.first->MPs[I]->spreadMemEscape(BB, ES, SuccBBInfo, Offsets, Direct); + } + for (auto &V : InfoEscapedVec) { + for (unsigned I = 0; I < V->MPs.size(); ++I) + V->MPs[I]->spreadMemEscape(BB, ES, SuccBBInfo, Offsets, Direct); + } + Offsets.push_back(CurOffset); + } else { + for (unsigned I = 0; I < MPs.size(); ++I) + MPs[I]->spreadEscape(BB, ES, SuccBBInfo, true); + if (Direct) { + for (auto &V : BaseValue) { + for (unsigned I = 0; I < V.first->MPs.size(); ++I) + V.first->MPs[I]->spreadEscape(BB, ES, SuccBBInfo, true); + } + } + for (auto V : Alias) { + for (unsigned I = 0; I < V.first->MPs.size(); ++I) + V.first->MPs[I]->spreadEscape(BB, ES, SuccBBInfo, Direct); + } + for (auto &V : InfoEscapedVec) { + for (unsigned I = 0; I < V->MPs.size(); ++I) + V->MPs[I]->spreadEscape(BB, ES, SuccBBInfo, Direct); + } + Offsets.push_back(CurOffset); + } + IsVisiting = false; + } + + virtual void spreadEscape(BasicBlock *BB, unsigned ES, + DenseMap &SuccBBInfo, + bool Direct) { + if (isDerivedPtr() && Direct && DirectSpread < ES) { + DirectSpread = ES; + } + + for (unsigned It = 0; It < MPs.size(); ++It) { + if (!SuccBBInfo.count(MPs[It]) || SuccBBInfo[MPs[It]] < ES) { + SuccBBInfo[MPs[It]] = ES; + MPs[It]->spreadEscape(BB, Escaped, SuccBBInfo, true); + } + } + // is direct escape + if (isDerivedPtr() && Direct) { + for (auto &V : BaseValue) { + if (!SuccBBInfo.count(V.first) || SuccBBInfo[V.first] < ES || + canSpread(V.first, ES)) { + SuccBBInfo[V.first] = ES; + V.first->spreadEscape(BB, ES, SuccBBInfo, true); + } + } + } + if (Alias.size() != 0) { + for (auto &V : Alias) { + if (!SuccBBInfo.count(V.first) || SuccBBInfo[V.first] < ES) { + SuccBBInfo[V.first] = ES; + V.first->spreadEscape(BB, ES, SuccBBInfo, false); + } + } + } + for (auto &V : InfoEscapedVec) { + V->spreadInfoEscape(BB, ES, SuccBBInfo, Direct); + } + } + + void insertMem(GCPtr *MP) { MPs.push_back(MP); } + + // Base Value Ptr + Value *P; + bool IsLoad; + bool IsPhiOrSelect; + unsigned DirectSpread = NotEscape; + bool InfoEscapeInfo = false; + bool IsVisiting = false; + int MemOff = 0; + InitializeState InitializeFlag = NotInitialize; + unsigned LoopDepth; + CJEscapeAnalysis &EA; + // for load + DenseMap> BaseValue; + // Ptr's memPtr info + SmallVector MPs; + // pass to other instruction, such as + // %3 = phi [%1, %bb1], [%2, %bb2] + // which %1, %2's obj Alias stored %3. + // Value is the offset of %1's base and %3 + DenseMap> Alias; + SmallSetVector InfoEscapedVec; +}; + +class MemPtr final : public GCPtr { +public: + explicit MemPtr(Value *Val, uint32_t Off, unsigned Depth, + CJEscapeAnalysis &EAImpl, GCPtr *P) + : GCPtr(Val, Depth, EAImpl) { + Offset = Off; + Base = P; + } + + ~MemPtr() override = default; + + static MemPtr *create(Value *Val, int Off, CJEscapeAnalysis &EAImpl, + LoopInfo *LI = nullptr) { + if (EAImpl.AllMemLocInfo.count(std::make_pair(Val, Off))) { + return EAImpl.AllMemLocInfo[std::make_pair(Val, Off)]; + } + GCPtr *P = GCPtr::create(Val, EAImpl, LI); + if (P == nullptr) + return nullptr; + if (Off < 0) { + Off = --P->MemOff; + } + MemPtr *New = new MemPtr(Val, Off, P->LoopDepth, EAImpl, P); + EAImpl.AllMemLocInfo[std::make_pair(Val, Off)] = New; + P->insertMem(New); + return New; + } + + bool equal(GCPtr *V) override { + if (V->isPtr()) { + return false; + } + MemPtr *M = reinterpret_cast(V); + return (M->P == P) && (M->Offset == Offset); + } + + bool isPtr() override { return false; } + + void insertDefine(BasicBlock *BB, GCPtr *Val) { + DefineValues[BB].push_back(Val); + } + + void spreadInfoEscape(BasicBlock *BB, unsigned ES, + DenseMap &SuccBBInfo, + bool Direct = true) override { + if (isDerivedPtr() && Direct && DirectSpread < InfoEscaped) { + DirectSpread = InfoEscaped; + } else if (SuccBBInfo.count(this) && SuccBBInfo[this] >= InfoEscaped) { + return; + } + if (SuccBBInfo[this] < InfoEscaped) { + SuccBBInfo[this] = InfoEscaped; + } + for (auto DefineMap : DefineValues) { + for (GCPtr *Define : DefineMap.second) { + if (!SuccBBInfo.count(Define) || SuccBBInfo[Define] < InfoEscaped) { + Define->spreadInfoEscape(BB, Escaped, SuccBBInfo, Direct); + } + } + } + SmallVector Offsets; + Offsets.push_back(-1); // -1 Escaped All + Offsets.push_back(Offset); + Base->spreadMemEscape(BB, ES, SuccBBInfo, Offsets, Direct); + Offsets.pop_back(); + if (Offset < 0) { + return; + } + if (Direct) { + for (auto &V : Base->BaseValue) { + for (auto BaseValueOffset : V.second) { + Offsets.push_back(BaseValueOffset + Offset); + V.first->spreadMemEscape(BB, ES, SuccBBInfo, Offsets); + Offsets.pop_back(); + } + } + } + if (Base->Alias.size() != 0) { + for (auto &V : Base->Alias) { + for (auto AliasOffset : V.second) { + if (Offset < AliasOffset) { + continue; + } + if (AliasOffset != -1) { + Offsets.push_back(Offset - AliasOffset); + } else { + Offsets.push_back(AliasOffset); + } + V.first->spreadMemEscape(BB, ES, SuccBBInfo, Offsets, false); + Offsets.pop_back(); + } + } + } + } + + void spreadMemEscape( + BasicBlock *BB, unsigned ES, DenseMap &SuccBBInfo, + SmallVector &Offsets, bool Direct) override { + if (IsVisiting) { + return; + } + IsVisiting = true; + int CurOffset = Offsets.back(); + for (auto DefineMap : DefineValues) { + for (GCPtr *Define : DefineMap.second) { + if ((Offsets.size() == 1) && Define->isPtr()) { + MemPtr *MPtr = MemPtr::create(Define->P, CurOffset, EA); + if ((MPtr != nullptr) && + (!SuccBBInfo.count(MPtr) || SuccBBInfo[MPtr] < ES)) { + SuccBBInfo[MPtr] = ES; + MPtr->spreadEscape(BB, ES, SuccBBInfo, true); + } + continue; + } + Define->spreadMemEscape(BB, ES, SuccBBInfo, Offsets); + } + } + Offsets.push_back(Offset); + Base->spreadMemEscape(BB, ES, SuccBBInfo, Offsets, Direct); + Offsets.pop_back(); + if (Offset < 0) { + return; + } + if (Direct) { + for (auto &V : Base->BaseValue) { + for (auto BaseValueOffset : V.second) { + Offsets.push_back(BaseValueOffset + Offset); + V.first->spreadMemEscape(BB, ES, SuccBBInfo, Offsets); + Offsets.pop_back(); + } + } + } + if (Base->Alias.size() != 0) { + for (auto &V : Base->Alias) { + for (auto AliasOffset : V.second) { + if (Offset < AliasOffset) { + continue; + } + if (AliasOffset != -1) { + Offsets.push_back(Offset - AliasOffset); + } else { + Offsets.push_back(AliasOffset); + } + V.first->spreadMemEscape(BB, ES, SuccBBInfo, Offsets, false); + Offsets.pop_back(); + } + } + } + IsVisiting = false; + } + + void spreadEscape(BasicBlock *BB, unsigned ES, + DenseMap &SuccBBInfo, + bool Direct) override { + if (isDerivedPtr() && Direct && DirectSpread < ES) { + DirectSpread = ES; + } + for (auto DefineMap : DefineValues) { + for (GCPtr *Define : DefineMap.second) { + if (!SuccBBInfo.count(Define) || SuccBBInfo[Define] < ES || + canSpread(Define, ES)) { + SuccBBInfo[Define] = ES; + Define->spreadEscape(BB, ES, SuccBBInfo, true); + } + } + } + if (Offset < 0 || Base->InfoEscapedVec.size() != 0) { + Base->spreadInfoEscape(BB, Escaped, SuccBBInfo, Direct); + return; + } + SmallVector Offsets; + if (Direct) { + for (auto &V : Base->BaseValue) { + for (auto BaseValueOffset : V.second) { + Offsets.push_back(BaseValueOffset + Offset); + V.first->spreadMemEscape(BB, ES, SuccBBInfo, Offsets); + Offsets.pop_back(); + } + } + } + + if (Base->Alias.size() != 0) { + for (auto &V : Base->Alias) { + for (auto AliasOffset : V.second) { + if (Offset < AliasOffset) { + continue; + } + if (AliasOffset != -1) { + Offsets.push_back(Offset - AliasOffset); + } else { + Offsets.push_back(-1); + } + V.first->spreadMemEscape(BB, ES, SuccBBInfo, Offsets, false); + Offsets.pop_back(); + } + } + } + } + + bool isDerivedPtr() override { return Base->isDerivedPtr(); } + GCPtr *Base; + int Offset; + DenseMap> DefineValues; +}; + +static void memPtrspreadEscape( + BasicBlock *BB, unsigned ES, DenseMap &SuccBBInfo, + GCPtr *P, SmallVector &Offsets, bool Direct) { + int CurOffset = Offsets.back(); + MemPtr *MPtr = MemPtr::create(P->P, CurOffset, P->EA); + if (MPtr == nullptr) { + return; + } + if (Offsets.size() == 1) { + if (!SuccBBInfo.count(MPtr) || SuccBBInfo[MPtr] < ES) { + SuccBBInfo[MPtr] = ES; + MPtr->spreadEscape(BB, ES, SuccBBInfo, Direct); + } + } else { + Offsets.pop_back(); + MPtr->spreadMemEscape(BB, ES, SuccBBInfo, Offsets, Direct); + Offsets.push_back(CurOffset); + } +} + +void CJEscapeAnalysis::setInfoEscaped(GCPtr *GP, BasicBlock *BB) { + GP->setInfoEscaped(); + if (!EscapeBBInfo[BB].count(GP)) { + EscapeBBInfo[BB][GP] = NotEscape; + } +} + +Value *CJEscapeAnalysis::getBaseValue(Value *CV, int &Offset) { + Offset = 0; + bool InfoEscapeInfo = false; + Value *V = CV; + while (true) { + if (auto *CI = dyn_cast(V)) { + if (!CI->getType()->isPointerTy()) { // ptr2int + V = CI->getOperand(0); + continue; + } + V = CI->stripPointerCasts(); + // If we find a cast instruction here, it means we've found a cast which + // is not simply a pointer cast (i.e. an inttoptr). + if (isa(V)) { + V = CI->getOperand(0); + } + } else if (auto *GEPI = dyn_cast(V)) { + APInt GEPOffset(64, 0); + if (GEPI->accumulateConstantOffset(GEPI->getModule()->getDataLayout(), + GEPOffset)) { + Offset += GEPOffset.getZExtValue(); + } else { + InfoEscapeInfo = true; + } + V = GEPI->getPointerOperand(); + } else if (auto *CE = dyn_cast(V)) { + V = CE->getOperand(0); + } else { + break; + } + } + if (InfoEscapeInfo) { + Offset = -1; + } + return V; +} + +bool CJEscapeAnalysis::isEscapedValue(Value *V) { + int Offset = 0; + Value *Base = getBaseValue(V, Offset); + if (isa(Base)) { + return true; + } + if (isa(Base) || isa(Base)) { + if (auto PT = dyn_cast(Base->getType())) { + if (!containsGCPtrType(PT->getNonOpaquePointerElementType())) { + return false; + } + } + return true; + } + return false; +} + +class EscapeAnalysisImpl : public InstVisitor, + public CJEscapeAnalysis { + friend class InstVisitor; + +public: + EscapeAnalysisImpl(CallGraphUpdater &CGUpdater, Module *M, + function_ref LookLoop) + : CGUpdater(CGUpdater), M(M), LookupLoopInfo(LookLoop) {} + + void insertSCCFunction(Function *Func) { SCCFunctions.insert(Func); } +#ifndef NDEBUG + void dumpBBInfo(BasicBlock &BB, bool BeforeRun) { + LLVM_DEBUG(dbgs() << " bb." << BB.getName() << "\n"); + if (EscapeBBInfo.count(&BB)) { + LLVM_DEBUG(dbgs() << " bb value escape state:\n"); + for (auto EscapeInfo : EscapeBBInfo[&BB]) { + LLVM_DEBUG(dbgs() << " " << *(EscapeInfo.first->P) << " : "); + switch (EscapeInfo.second) { + case NotEscape: + LLVM_DEBUG(dbgs() << "NotEscape\n"); + break; + case Escaped: + LLVM_DEBUG(dbgs() << "Escaped"); + if (!EscapeInfo.first->isPtr()) { + LLVM_DEBUG(dbgs() << " offset: " + << ((MemPtr *)EscapeInfo.first)->Offset); + } + LLVM_DEBUG(dbgs() << "\n"); + break; + case TransEscaped: + LLVM_DEBUG(dbgs() << "TransEscaped\n"); + break; + default: + LLVM_DEBUG(dbgs() << "info escape state.\n"); + break; + } + } + } + if (StoreRelations.count(&BB) && BeforeRun) { + LLVM_DEBUG(dbgs() << " store relations:\n"); + for (auto StoreRelation : StoreRelations[&BB]) { + LLVM_DEBUG(dbgs() << " " << *StoreRelation.first + << " be stored:\n"); + for (auto Stored : StoreRelation.second) { + LLVM_DEBUG(dbgs() << " " << *Stored << "\n"); + } + } + } + if (BeforeRun) { + LLVM_DEBUG(dbgs() << " defs:\n"); + for (auto DefInfo : Def[&BB]) { + for (auto ValueVec : DefInfo.second) { + LLVM_DEBUG(dbgs() << " " << *DefInfo.first + << " offset :" << ValueVec.first << "\n"); + for (auto Value : ValueVec.second) { + LLVM_DEBUG(dbgs() << " " << *(Value->P) << "\n"); + } + } + } + } else { + LLVM_DEBUG(dbgs() << " ins:\n"); + for (auto InInfo : In[&BB]) { + for (auto ValueVec : InInfo.second) { + LLVM_DEBUG(dbgs() << " " << *InInfo.first + << " offset :" << ValueVec.first << "\n"); + for (auto Value : ValueVec.second) { + LLVM_DEBUG(dbgs() << " " << *(Value->P) << "\n"); + } + } + } + } + } + + void dumpAnalysisInfo(bool BeforeRun) { + if (BeforeRun) { + LLVM_DEBUG(dbgs() << "PartialEscapeAnalysis : after initialized.\n"); + } else { + LLVM_DEBUG(dbgs() << "PartialEscapeAnalysis : after run.\n"); + } + + for (Function *Func : SCCFunctions) { + LLVM_DEBUG(dbgs() << Func->getName() << "\n"); + for (BasicBlock &BB : *Func) { + dumpBBInfo(BB, BeforeRun); + } + } + } +#endif + void initialize() { + for (Function *Func : SCCFunctions) { + ProcessedFunc = Func; + LI = &LookupLoopInfo(*Func); + for (BasicBlock &BB : *Func) { + for (Instruction &I : BB) { + visit(I); + } + } + } +#ifndef NDEBUG + dumpAnalysisInfo(true); +#endif + } + void processLoadNew(GCPtr *LoadPtr) { + Value *V = LoadPtr->P; + BasicBlock *VBB = dyn_cast(V)->getParent(); + SmallSetVector Visited; + SmallVector BaseMemValues; + SmallVector BaseLoadValues; + if (LoadPtr->isInitializing() || LoadPtr->isInitialized()) { + return; + } + LoadPtr->setInitializing(); + for (auto &BaseV : LoadPtr->BaseValue) { + GCPtr *LoadBase = BaseV.first; + Visited.insert(LoadBase); + if (!LoadBase->isPtr()) { + BaseMemValues.push_back((MemPtr *)LoadBase); + } else if (LoadBase->isLoad()) { + BaseLoadValues.push_back(LoadBase); + } else { + continue; + } + } + for (unsigned Index = 0; Index < BaseMemValues.size(); Index++) { + MemPtr *MemBasePtr = BaseMemValues[Index]; + if (In[VBB][MemBasePtr->P][MemBasePtr->Offset].size() == 0) { + LoadPtr->BaseValue[MemBasePtr].insert(0); + } else { + LoadPtr->BaseValue.erase(MemBasePtr); + } + auto findMemBasePtr = [&] (int CurOff) { + for (GCPtr *MemVal : In[VBB][MemBasePtr->P][CurOff]) { + if (LoadPtr->BaseValue.count(MemVal) || !Visited.insert(MemVal)) { + continue; + } + if (!MemVal->isPtr()) { + BaseMemValues.push_back((MemPtr *)MemVal); + } else if (MemVal->isLoad()) { + BaseLoadValues.push_back(MemVal); + LoadPtr->BaseValue[MemVal].insert(0); + } else { + LoadPtr->BaseValue[MemVal].insert(0); + } + } + }; + findMemBasePtr(MemBasePtr->Offset); + findMemBasePtr(-1); + } + for (unsigned Index = 0; Index < BaseLoadValues.size(); Index++) { + GCPtr *LoadBasePtr = BaseLoadValues[Index]; + processLoadNew(LoadBasePtr); + // LoadBasePtr State?? + // when initialized. + if (LoadBasePtr->isInitialized()) { + for (auto &PtrPtr : LoadBasePtr->BaseValue) { + if (PtrPtr.first->isPtr() && PtrPtr.first->isLoad()) { + continue; + } + for (auto BaseOffset : PtrPtr.second) { + LoadPtr->BaseValue[PtrPtr.first].insert(BaseOffset); + } + if (PtrPtr.first->isPtr()) { + for (auto BaseOffset : PtrPtr.second) { + PtrPtr.first->Alias[LoadPtr].insert(BaseOffset); + } + } else { + ((MemPtr *)PtrPtr.first)->DefineValues[ + ((Instruction *)LoadPtr->P)->getParent()].push_back(LoadPtr); + } + } + } else { + LLVM_DEBUG(dbgs() << "error cycle occurs" << *LoadBasePtr->P << "\n"); + } + } + for (auto &Ptr : LoadPtr->BaseValue) { + if (Ptr.first->isPtr()) { + for (auto BaseOffset : Ptr.second) { + Ptr.first->Alias[LoadPtr].insert(BaseOffset); + } + } else { + ((MemPtr *)Ptr.first)->DefineValues[ + ((Instruction *)LoadPtr->P)->getParent()].push_back(LoadPtr); + } + } + LoadPtr->setInitialized(); + } + + void spreadFuncEscape(Function *Func, bool &Changed) { + for (auto &BB : *Func) { + auto &SuccBBInfo = EscapeBBInfo[&BB]; + for (auto *Pred : predecessors(&BB)) { + if (!EscapeBBInfo.count(Pred)) { + continue; + } + for (auto BBInfo : EscapeBBInfo[Pred]) { + if (BBInfo.second >= TransEscaped && + (!SuccBBInfo.count(BBInfo.first) || + SuccBBInfo[BBInfo.first] < TransEscaped)) { + Changed = true; + SuccBBInfo[BBInfo.first] = TransEscaped; + // obj stored to Escaped or TransEscaped is to be Escaped. + BBInfo.first->spreadEscape(&BB, TransEscaped, SuccBBInfo, true); + } else if (BBInfo.second == InfoEscaped && + (!SuccBBInfo.count(BBInfo.first) || + SuccBBInfo[BBInfo.first] < InfoEscaped)) { + Changed = true; + BBInfo.first->spreadInfoEscape(&BB, Escaped, SuccBBInfo); + } + } + } + } + } + + void processFunc(Function *Func) { + bool Changed = true; + for (auto &BB : *Func) { + auto &BBInfo = EscapeBBInfo[&BB]; + SmallSetVector EscapedValues; + SmallSetVector InfoEscapedValues; + for (auto EscapeInfo : BBInfo) { + if (EscapeInfo.second == Escaped) { + EscapedValues.insert(EscapeInfo.first); + } else if (EscapeInfo.first->isInfoEscaped()) { + InfoEscapedValues.insert(EscapeInfo.first); + } + } + for (unsigned Index = 0; Index < EscapedValues.size(); Index++) { + EscapedValues[Index]->spreadEscape(&BB, Escaped, BBInfo, true); + } + for (unsigned Index = 0; Index < InfoEscapedValues.size(); Index++) { + InfoEscapedValues[Index]->spreadInfoEscape(&BB, Escaped, BBInfo); + } + } + while (Changed) { + Changed = false; + spreadFuncEscape(Func, Changed); + } + } + + void handleLiveness(Function *F) { + bool Changed = true; + bool NotInitialize = true; + while (Changed) { + Changed = false; + for (auto &BB : *F) { + auto &SuccBBInfo = Def[&BB]; + auto &SuccOut = Out[&BB]; + auto &InInfo = In[&BB]; + if (NotInitialize) { // first time to initialize. + for (auto SuccDefs : Def[&BB]) { + for (auto SuccDef : SuccDefs.second) { + SuccOut[SuccDefs.first][SuccDef.first].insert( + SuccDef.second.begin(), SuccDef.second.end()); + } + } + } + for (auto *Pred : predecessors(&BB)) { + for (auto LivVal : Out[Pred]) { + for (auto ValVec : LivVal.second) { + for (auto Val : ValVec.second) { + if (!InInfo[LivVal.first][ValVec.first].count(Val)) { + InInfo[LivVal.first][ValVec.first].insert(Val); + Changed = true; + } + } + } + } + if (Pred == &BB) { + continue; + } + for (auto LivVal : Out[Pred]) { + for (auto ValVec : LivVal.second) { + for (auto Val : ValVec.second) { + if (SuccBBInfo[LivVal.first].count(ValVec.first) && + SuccBBInfo[LivVal.first][ValVec.first].size() != 0) { + continue; + } + if ((!SuccOut[LivVal.first].count(ValVec.first)) || + !SuccOut[LivVal.first][ValVec.first].count(Val)) { + Changed = true; + SuccOut[LivVal.first][ValVec.first].insert(Val); + } + } + } + } + } + } + NotInitialize = false; + } + } + + bool run() { + bool Changed = false; + for (Function *Func : SCCFunctions) { + ProcessedFunc = Func; + handleLiveness(Func); + } + for (auto LoadPtr : LoadValues) { + processLoadNew(LoadPtr); + } + for (Function *Func : SCCFunctions) { + processFunc(Func); + } +#ifndef NDEBUG + dumpAnalysisInfo(false); +#endif + finishAll(); + if (OneWayNotEscaped.size() != 0) { + LLVM_DEBUG(dbgs() << "Partial analysis object:\n"); + } +#ifndef NDEBUG + for (auto Partial : OneWayNotEscaped) { + LLVM_DEBUG(dbgs() << " " << *Partial << "\n"); + } +#endif + NonEscapeRewriter Rewriter(&CGUpdater, M); + for (auto &Partial : GCNew) { + if (!OnceEscapedValue.count(Partial.first)) { + LLVM_DEBUG(dbgs() << "not escaped obj:"); + LLVM_DEBUG(dbgs() << " " << *Partial.first << "\n"); + Rewriter.rewrite(Partial.first, Partial.second); + Changed = true; + } else { + LLVM_DEBUG(dbgs() << "partial escaped obj:"); + LLVM_DEBUG(dbgs() << " " << *Partial.first << "\n"); + } + } + + for (auto PInfo : AllPtrLocInfo) { + delete PInfo.second; + } + AllPtrLocInfo.clear(); + for (auto MemInfo : AllMemLocInfo) { + delete MemInfo.second; + } + AllMemLocInfo.clear(); + return Changed; + } + + void walkBBs(BasicBlock *CurBB) { + for (auto BBInfo : EscapeBBInfo[CurBB]) { + if (BBInfo.second == Escaped && BBInfo.first->isPtr()) { + OnceEscapedValue.insert(BBInfo.first->P); + } + } + } + + void finishAll() { + for (Function *Func : SCCFunctions) { + ProcessedFunc = Func; + for (BasicBlock &BB : *Func) { + walkBBs(&BB); + } + } + } + + bool isValueInvalid(Value *V) { + if (V->getValueID() == llvm::Value::UndefValueVal) { + report_fatal_error("store/gcwrite an undef value!"); + } + return V->getValueID() == llvm::Value::ConstantPointerNullVal; + } + + void bindInfoEscape(GCPtr *P, GCPtr *V, BasicBlock *BB) { + P->InfoEscapedVec.insert(V); + V->InfoEscapedVec.insert(P); + unsigned PES = isEscapedValue(P->P) ? Escaped : NotEscape; + unsigned VES = isEscapedValue(V->P) ? Escaped : NotEscape; + if (!EscapeBBInfo[BB].count(P) || EscapeBBInfo[BB][P] < PES) { + EscapeBBInfo[BB][P] = PES; + } + if (!EscapeBBInfo[BB].count(V) || EscapeBBInfo[BB][V] < VES) { + EscapeBBInfo[BB][V] = VES; + } + } + + void handleGCWrite(IntrinsicInst *II) { + Value *V = II->getOperand(0); + Type *T = V->getType(); + if (!T->isPointerTy() || T->getPointerAddressSpace() != 1 || + isValueInvalid(V)) { + return; + } + Value *P = II->getOperand(2); + GCPtr *GP = GCPtr::create(P, *this, LI); + GCPtr *GV = GCPtr::create(V, *this, LI); + if (isEscapedValue(P) || GP->LoopDepth < GV->LoopDepth) { + if (auto VI = dyn_cast(V)) { + EscapeBBInfo[II->getParent()][GV] = Escaped; + } + } + int Offset = 0; + Value *BaseP = getBaseValue(P, Offset); // direct get from intrinsic + MemPtr *MP = MemPtr::create(BaseP, Offset, *this, LI); + MP->insertDefine(II->getParent(), GV); + + if (isEscapedValue(V)) { + EscapeBBInfo[II->getParent()][MP] = Escaped; + } + + StoreRelations[II->getParent()][P].insert(V); + if (Offset >=0 && !Def[II->getParent()][BaseP][Offset].empty()) { + Def[II->getParent()][BaseP][Offset].clear(); + } + Def[II->getParent()][BaseP][Offset].insert(GV); + } + + void handleGCRead(IntrinsicInst *II, bool IsStatic) { + Value *P = II->getArgOperand(IsStatic ? 0 : 1); + handleLoad(P, II); + } + + void handleGCWriteStatic(IntrinsicInst *II) { + Value *V = II->getOperand(0); + Type *T = V->getType(); + if (!T->isPointerTy() || T->getPointerAddressSpace() != 1 || + isValueInvalid(V)) { + return; + } + EscapeBBInfo[II->getParent()][GCPtr::create(V, *this, LI)] = Escaped; + StoreRelations[II->getParent()][II->getOperand(1)].insert(V); + } + + void handleGCWriteAgg(Instruction *I, bool IsStatic) { + Value *P = I->getOperand(IsStatic ? 0 : 1); + Value *V = I->getOperand(IsStatic ? 1 : 2); + ConstantInt *CSI = dyn_cast(I->getOperand(IsStatic ? 2 : 3)); + handleMemcpyOrWriteAgg(I, P, V, CSI); + } + + void handleGCReadAgg(Instruction *I, bool IsStatic) { + Value *P = I->getOperand(0); + Value *V = I->getOperand(IsStatic ? 1 : 2); + ConstantInt *CSI = dyn_cast(I->getOperand(IsStatic ? 2 : 3)); + handleMemcpyOrWriteAgg(I, P, V, CSI); + } + + void handleArrayCopyto(Instruction *I) { + Value *P = I->getOperand(1); + Value *V = I->getOperand(3); + ConstantInt *CSI = dyn_cast(I->getOperand(4)); + handleMemcpyOrWriteAgg(I, P, V, CSI); + } + + void handleMemcpyOrWriteAgg(Instruction *I, Value *P, Value *V, + ConstantInt *CSI) { + int POffset = 0; + int Off; + GCPtr *GP = GCPtr::create(getBaseValue(P, POffset), *this, LI); + GCPtr *GV = GCPtr::create(getBaseValue(V, Off), *this, LI); + if (GP == nullptr || GV == nullptr) + return; + unsigned Size = 0; + const DataLayout &DL = M->getDataLayout(); + StructType *ST = nullptr; + if (isa(I)) { + ST = dyn_cast(P->getType()); + Size = DL.getStructLayout(ST)->getSizeInBytes(); + } + if (isa(I)) { + ST = dyn_cast(V->getType()); + Size = DL.getStructLayout(ST)->getSizeInBytes(); + } + if ((!Size && CSI == nullptr) || GP->isInfoEscaped() || + GV->isInfoEscaped() || POffset < 0 || Off < 0) { + bindInfoEscape(GP, GV, I->getParent()); + return; + } else if (Size == 0) { + Size = CSI->getZExtValue(); + } + Type *CopyType = nullptr; + + SmallVector RefOffsets; + Value *BaseV = getBaseValue(V, Off); + int BeginOff = Off; + if (ST != nullptr) { + CopyType = ST; + BeginOff = 0; + } else { + if (!isa(BaseV->getType())) { + return; + } + CopyType = + cast(BaseV->getType())->getNonOpaquePointerElementType(); + } + if (isa(CopyType)) { + if (!containsGCPtrType(CopyType)) { + return; + } + countRefOffsets(cast(CopyType), RefOffsets, 0, DL, BeginOff, + BeginOff + Size); + } else { + // 64: large size threshold + if (Size > 64) { + setInfoEscaped(GP, I->getParent()); + setInfoEscaped(GV, I->getParent()); + return; + } + for (uint64_t PtrOff = BeginOff; PtrOff < BeginOff + Size; + PtrOff += alignEight) { + RefOffsets.push_back(PtrOff); + } + } + Value *Base = getBaseValue(P, POffset); + + if (isEscapedValue(P) || GP->LoopDepth < GV->LoopDepth) { + for (auto Offset : RefOffsets) { + // Info Escaped + EscapeBBInfo[I->getParent()][MemPtr::create( + BaseV, Offset - BeginOff + Off, *this, LI)] = Escaped; + } + return; + } else if (isEscapedValue(V)) { + for (auto Offset : RefOffsets) { + uint64_t PVOffset = POffset + Offset - BeginOff; + EscapeBBInfo[I->getParent()] + [MemPtr::create(Base, PVOffset, *this, LI)] = Escaped; + } + return; + } + for (auto Offset : RefOffsets) { + uint64_t PVOffset = POffset + Offset - BeginOff; + uint64_t VPOffset = Off + Offset - BeginOff; + GCPtr *SrcMP = findSrcMP(BaseV, VPOffset, I->getParent()); + MemPtr *MP = MemPtr::create(Base, PVOffset, *this, LI); + MP->insertDefine(I->getParent(), SrcMP); + if (!Def[I->getParent()][Base][PVOffset].empty()) { + Def[I->getParent()][Base][PVOffset].clear(); + } + Def[I->getParent()][Base][PVOffset].insert(SrcMP); + } + } + + GCPtr *findSrcMP(Value *BaseV, uint64_t VPOffset, BasicBlock *CurBB) { + if (Def[CurBB][BaseV][VPOffset].empty()) { + return MemPtr::create(BaseV, VPOffset, *this, LI); + } + GCPtr *CurP = nullptr; + while (!Def[CurBB][BaseV][VPOffset].empty()) { + CurP = *Def[CurBB][BaseV][VPOffset].begin(); + if (CurP->isPtr()) + return CurP; + MemPtr *MP = reinterpret_cast(CurP); + VPOffset = MP->Offset; + BaseV = MP->P; + } + return CurP; + } + + bool handleCangjieSL(Instruction *I) { + IntrinsicInst *II = dyn_cast(I); + if (II) { + switch (II->getIntrinsicID()) { + case Intrinsic::cj_gcwrite_ref: + handleGCWrite(II); + return true; + case Intrinsic::cj_gcwrite_static_ref: + handleGCWriteStatic(II); + return true; + case Intrinsic::cj_gcwrite_struct: + handleGCWriteAgg(II, false); + return true; + case Intrinsic::cj_gcwrite_static_struct: + handleGCWriteAgg(II, true); + return true; + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + handleArrayCopyto(I); + return true; + case Intrinsic::cj_gcread_ref: + handleGCRead(II, false); + return true; + case Intrinsic::cj_gcread_static_ref: + handleGCRead(II, true); + return true; + case Intrinsic::cj_gcread_struct: + handleGCReadAgg(II, false); + return true; + case Intrinsic::cj_gcread_static_struct: + handleGCReadAgg(II, true); + return true; + default: + break; + } + } + return false; + } + + void handleCallArgs(CallBase &I) { + escapeAllOperand(&I); + return; + } + + void insertGCNew(Instruction *I) { + BasicBlock *CurBB = I->getParent(); + LI = &LookupLoopInfo(*CurBB->getParent()); + Loop *L = LI->getLoopFor(CurBB); + BasicBlock *PreHeader = nullptr; + if (L) { + PreHeader = L->getOutermostLoop()->getLoopPreheader(); + } + GCNew.insert(std::make_pair(I, PreHeader)); + } + + void escapeAllOperand(Instruction *I) { + int Offset = 0; + if (isGCPointerType(I->getType())) { + GCPtr *PI = GCPtr::create(I, *this, LI); + EscapeBBInfo[I->getParent()][PI] = Escaped; + } + + for (unsigned Idx = 0; Idx < I->getNumOperands(); Idx++) { + auto V = I->getOperand(Idx); + if (!(isGCPointerType(V->getType()) || + isMemoryContainsGCPtrType(V->getType()))) + continue; + GCPtr *Base = GCPtr::create(getBaseValue(V, Offset), *this, LI); + if (Base != nullptr) { + EscapeBBInfo[I->getParent()][Base] = Escaped; + } + } + } + + // If both GP1 and GP2 are not in a loop, should not call this functon. + bool isInSameLoop(GCPtr *GP1, GCPtr *GP2) { + assert(GP1->LoopDepth + GP2->LoopDepth > 0); + if (GP1->LoopDepth != GP2->LoopDepth) + return false; + if (auto *I1 = dyn_cast(GP1->P)) { + if (auto *I2 = dyn_cast(GP2->P)) { + return LI->getLoopFor(I1->getParent()) == + LI->getLoopFor(I2->getParent()); + } + } + return false; + } + + void visitPHINode(PHINode &PN) { + if (!PN.getType()->isPointerTy()) { + return; + } + GCPtr *P = GCPtr::create(&PN, *this, LI); + unsigned LD = P->LoopDepth; + bool NeedEscapeInnerLoop = false; + for (unsigned Idx = 0; Idx < PN.getNumIncomingValues(); Idx++) { + Value *PhiIn = PN.getIncomingValue(Idx); + if (isEscapedValue(PhiIn)) { + EscapeBBInfo[PN.getParent()][P] = Escaped; + } + int Offset = 0; + Value *BasePhiIn = getBaseValue(PhiIn, Offset); + GCPtr *PhiInPtr = GCPtr::create(BasePhiIn, *this, LI); + if (PhiInPtr == nullptr) { + continue; + } + P->BaseValue[PhiInPtr].insert(Offset); + PhiInPtr->Alias[P].insert(Offset); + if (P->LoopDepth == 0 && PhiInPtr->LoopDepth == 0) + continue; + if (!isInSameLoop(P, PhiInPtr)) { + if (LD > PhiInPtr->LoopDepth) + LD = PhiInPtr->LoopDepth; + NeedEscapeInnerLoop = true; + } + } + if (NeedEscapeInnerLoop) { + P->LoopDepth = LD; + SmallSet VisitedInsts; + SmallVector Phis; + for (unsigned Idx = 0; Idx < PN.getNumIncomingValues(); Idx++) { + Value *PhiIn = PN.getIncomingValue(Idx); + int Offset = 0; + Phis.push_back(PhiIn); + VisitedInsts.insert(PhiIn); + while (!Phis.empty()) { + Value *CurVal = Phis.pop_back_val(); + Value *BasePhiIn = getBaseValue(CurVal, Offset); + if (auto CurPN = dyn_cast(CurVal)) { + for (unsigned I = 0; I < CurPN->getNumIncomingValues(); I++) { + Value *CurPhiIn = CurPN->getIncomingValue(I); + if (VisitedInsts.insert(CurPhiIn).second) { + Phis.push_back(CurPhiIn); + } + } + } else if (auto CurSI = dyn_cast(CurVal)) { + Value *BaseTrue = getBaseValue(CurSI->getTrueValue(), Offset); + if (VisitedInsts.insert(BaseTrue).second) { + Phis.push_back(BaseTrue); + } + Value *BaseFalse = getBaseValue(CurSI->getFalseValue(), Offset); + if (VisitedInsts.insert(BaseFalse).second) { + Phis.push_back(BaseFalse); + } + } else { + BasePhiIn = getBaseValue(CurVal, Offset); + GCPtr *PhiInPtr = GCPtr::create(BasePhiIn, *this, LI); + // It is only possible to consider setting PhiInPtr to escape if it + // is inside a loop. + if (PhiInPtr != nullptr && + (LD < PhiInPtr->LoopDepth || + (LD != 0 && isInSameLoop(P, PhiInPtr)))) { + assert(P->LoopDepth <= PhiInPtr->LoopDepth); + EscapeBBInfo[PN.getParent()][PhiInPtr] = Escaped; + } + break; + } + } + } + } + } + + void visitSelectInst(SelectInst &SI) { + if (!SI.getType()->isPointerTy()) { + return; + } + + assert(SI.getType()->isPointerTy() && "selectInst."); + GCPtr *P = GCPtr::create(&SI, *this, LI); + if (isEscapedValue(SI.getTrueValue()) || + isEscapedValue(SI.getFalseValue())) { + EscapeBBInfo[SI.getParent()][P] = Escaped; + } + int Offset = 0; + Value *BaseTrue = getBaseValue(SI.getTrueValue(), Offset); + GCPtr *TruePtr = GCPtr::create(BaseTrue, *this, LI); + if (TruePtr != nullptr) { + P->BaseValue[TruePtr].insert(Offset); + TruePtr->Alias[P].insert(Offset); + } + Value *BaseFalse = getBaseValue(SI.getFalseValue(), Offset); + GCPtr *FalsePtr = GCPtr::create(BaseFalse, *this, LI); + if (FalsePtr != nullptr) { + P->BaseValue[FalsePtr].insert(Offset); + FalsePtr->Alias[P].insert(Offset); + } + } + + void processLoadBase(GCPtr *DefValue, GCPtr *LoadPtr, BasicBlock *CurBB) { + LoadPtr->BaseValue[DefValue].insert(0); + if (DefValue->isPtr()) { + DefValue->Alias[LoadPtr].insert(0); + return; + } + MemPtr *MP = reinterpret_cast(DefValue); + MP->insertDefine(CurBB, LoadPtr); + } + + void handleLoad(Value *P, Instruction *V) { + GCPtr *LoadPtr = GCPtr::create(V, *this, LI); + LoadValues.insert(LoadPtr); + BasicBlock *CurBB = V->getParent(); + if (isEscapedValue(P)) { + EscapeBBInfo[CurBB][LoadPtr] = Escaped; + return; + } + int SrcOffset = 0; + Value *BaseSrc = getBaseValue(P, SrcOffset); + if (auto EVT = dyn_cast(V)) { + const DataLayout &DL = M->getDataLayout(); + SrcOffset = EVT->getIndexOffset(DL); + } + if (Def[CurBB][BaseSrc].count(-1)) { + auto DefValueAny = *Def[CurBB][BaseSrc][-1].begin(); + processLoadBase(DefValueAny, LoadPtr, CurBB); + } + if (SrcOffset < 0 || Def[CurBB][BaseSrc][SrcOffset].size() == 0) { + MemPtr *MP = MemPtr::create(BaseSrc, SrcOffset, *this, LI); + if (MP) { + LoadPtr->BaseValue[MP].insert(0); + MP->insertDefine(CurBB, LoadPtr); + } + return; + } + auto DefValue = + *Def[CurBB][BaseSrc][SrcOffset].begin(); + processLoadBase(DefValue, LoadPtr, CurBB); + } + + void visitLoadInst(LoadInst &I) { + if (!containsGCPtrType(I.getType())) { + return; + } + + if (isa(I.getType())) { + Value *V = I.getPointerOperand(); + handleMemcpyOrWriteAgg(&I, &I, V, nullptr); + return; + } + auto Src = I.getPointerOperand(); + handleLoad(Src, &I); + } + + void visitStoreInst(StoreInst &SI) { + Value *V = SI.getValueOperand(); + if (!containsGCPtrType(V->getType()) || isValueInvalid(V)) { + return; + } + if (isa(V->getType())) { + Value *P = SI.getPointerOperand(); + handleMemcpyOrWriteAgg(&SI, P, V, nullptr); + return; + } + + Value *P = SI.getPointerOperand(); + GCPtr *GP = GCPtr::create(P, *this, LI); + GCPtr *GV = GCPtr::create(V, *this, LI); + if (isEscapedValue(P) || GP->LoopDepth < GV->LoopDepth) { + if (auto VI = dyn_cast(V)) { + EscapeBBInfo[SI.getParent()][GV] = Escaped; + } + } + int Offset = 0; + Value *BaseP = getBaseValue(P, Offset); + MemPtr *MP = MemPtr::create(BaseP, Offset, *this, LI); + MP->insertDefine(SI.getParent(), GV); + + if (isEscapedValue(V)) { + EscapeBBInfo[SI.getParent()][MP] = Escaped; + } + + StoreRelations[SI.getParent()][P].insert(V); + if (Offset >= 0 && !Def[SI.getParent()][BaseP][Offset].empty()) { + Def[SI.getParent()][BaseP][Offset].clear(); + } + Def[SI.getParent()][BaseP][Offset].insert(GV); + } + + void visitMemCpyOrMove(Instruction &I) { + Value *P = I.getOperand(0); + Value *V = I.getOperand(1); + ConstantInt *CSI = dyn_cast(I.getOperand(2)); + handleMemcpyOrWriteAgg(&I, P, V, CSI); + } + + void visitInvokeInst(InvokeInst &II) { + Function *Callee = II.getCalledFunction(); + if (Callee && isRewriteableCangjieMallocFunc(Callee)) { + GlobalVariable *Klass = getNewKlass(&II); + if (Klass == nullptr) { + EscapeBBInfo[II.getParent()][GCPtr::create(&II, *this, LI)] = Escaped; + return; + } + bool HasRefs = false; + uint32_t AllocaSize = 0; + bool NonMoveArray = false; + Type *AllocaType = + getAllocaType(Klass, HasRefs, AllocaSize, NonMoveArray, &II); + if (AllocaType != nullptr || NonMoveArray) { + insertGCNew(&II); + } else { + EscapeBBInfo[II.getParent()][GCPtr::create(&II, *this, LI)] = Escaped; + } + return; + } + + handleCallArgs(II); + } + + void visitCallInst(CallInst &CI) { + if (CI.getCalledFunction()) { + if (handleCangjieSL(&CI)) { + return; + } + Function *Callee = CI.getCalledFunction(); + if (isRewriteableCangjieMallocFunc(Callee)) { + GlobalVariable *Klass = getNewKlass(&CI); + if (Klass == nullptr) { + EscapeBBInfo[CI.getParent()][GCPtr::create(&CI, *this, LI)] = Escaped; + return; + } + bool HasRefs = false; + uint32_t AllocaSize = 0; + bool NonMoveArray = false; + Type *AllocaType = + getAllocaType(Klass, HasRefs, AllocaSize, NonMoveArray, &CI); + if (AllocaType != nullptr || NonMoveArray) { + insertGCNew(&CI); + } else { + EscapeBBInfo[CI.getParent()][GCPtr::create(&CI, *this, LI)] = Escaped; + } + return; + } + } + if (const Function *F = CI.getCalledFunction()) { + switch (F->getIntrinsicID()) { + default: + break; + case Intrinsic::memcpy: + case Intrinsic::memmove: + visitMemCpyOrMove(CI); + return; + case Intrinsic::memset: + case Intrinsic::lifetime_start: + case Intrinsic::lifetime_end: + return; + } + } + handleCallArgs(CI); + } + + // if not has any new. skip this func + void visitReturnInst(ReturnInst &I) { + if (I.getNumOperands() == 0) { + return; + } + Value *Ret = I.getOperand(0); + if (containsGCPtrType(Ret->getType())) { + GCPtr *RetPtr = GCPtr::create(Ret, *this, LI); + if (RetPtr != nullptr) { + EscapeBBInfo[I.getParent()][RetPtr] = Escaped; + } + } + } + + void visitExtractValueInst(ExtractValueInst &EVT) { + if (!containsGCPtrType(EVT.getType())) { + return; + } + Value *V = EVT.getAggregateOperand(); + if (isa(EVT.getType())) { + handleMemcpyOrWriteAgg(&EVT, &EVT, V, nullptr); + return; + } + handleLoad(V, &EVT); + } + +private: + CallGraphUpdater &CGUpdater; + + Module *M; + + function_ref LookupLoopInfo; + + LoopInfo *LI; + + SmallSetVector SCCFunctions; + + DenseMap PtrToBaseMap; + + DenseMap>> + StoreRelations; + + DenseMap>> + CopyRelations; + + DenseMap> LoadRelations; + DenseMap>> + AliasRelations; + + SmallSetVector, 8> GCNew; + SmallVector BBs; + SmallSetVector VisitedBBs; + SmallSetVector VisitedPhis; + DenseMap VisitedEscaped; + SmallSetVector VisitedNew; + SmallSetVector OneWayNotEscaped; + DenseMap, 8>> + PartialEscapeds; + SmallSetVector OnceEscapedValue; + // load alias find cycle. not process yet + SmallSetVector LoadCycle; + DenseMap> LoadBaseValue; + DenseMap LoadState; + SmallSetVector LoadValues; + + std::map>>> + Def; + std::map>>> + Out; + std::map>>> + In; +}; + +static bool skipLargeFunction(Function *F) { + uint32_t AllocaCount = 0; + for (const Instruction &I : F->getEntryBlock()) { + if (isa(I)) { + AllocaCount++; + } else { + continue; // alloc inst may be not continuous + } + } + if (AllocaCount > MaxAllocaObj) { + LLVM_DEBUG(dbgs() << "large alloca count:" << AllocaCount << "\n"); + return true; + } + return false; +} + +static bool removeDeadBlocks(LazyCallGraph::SCC &C, + FunctionAnalysisManager &FAM) { + bool Changed = false; + for (auto &N : C) { + auto &F = N.getFunction(); + Changed |= removeUnreachableBlocks(F); + } + return Changed; +} + +static bool escapeAnalysisImpl(LazyCallGraph::SCC &C, LazyCallGraph &CG, + CGSCCAnalysisManager &AM, + CGSCCUpdateResult &UR) { + FunctionAnalysisManager &FAM = + AM.getResult(C, CG).getManager(); + auto LookLoop = [&](Function &F) -> LoopInfo & { + return FAM.getResult(F); + }; + CallGraphUpdater CGUpdater; + CGUpdater.initialize(CG, C, AM, UR); + Module &M = *C.begin()->getFunction().getParent(); + EADepthImpl EA(&CGUpdater, &M, LookLoop); + for (LazyCallGraph::Node &N : C) { + Function *F = &N.getFunction(); + if (!F->isDeclaration()) + EA.insertSCCFunctions(F); + } + EA.initialize(); + bool Changed = EA.run(); + bool Simplified = false; + if (Changed) { + SimplifyCFGOptions Options; + Options.ConvertSwitchRangeToICmp = true; + for (LazyCallGraph::Node &N : C) { + Function *F = &N.getFunction(); + if (!F->isDeclaration()) { + FAM.invalidate(*F, PreservedAnalyses::none()); + auto &TTI = FAM.getResult(*F); + Simplified |= simplifyFunctionCFG(*F, TTI, nullptr, Options); + } + } + } + if (CJDisablePEA) + return Changed; + EscapeAnalysisImpl PEA(CGUpdater, &M, LookLoop); + for (LazyCallGraph::Node &N : C) { + Function *F = &N.getFunction(); + if (skipLargeFunction(F)) { + continue; + } + if (Simplified) { + FAM.invalidate(*F, PreservedAnalyses::none()); + } + PEA.insertSCCFunction(F); + } + PEA.initialize(); + Changed |= PEA.run(); + // Dead Blocks may appear after PEA, which will cause the analysis of these + // BBs to fail, such as LoopInfo. If these BBs are not deleted in a timely + // manner, incorrect results may be generated during subsequent optimization + // based on correct analysis. + if (Changed) + removeDeadBlocks(C, FAM); + return Changed; +} + +PreservedAnalyses CJPartialEscapeAnalysisPass::run(LazyCallGraph::SCC &C, + CGSCCAnalysisManager &AM, LazyCallGraph &CG, CGSCCUpdateResult &UR) const { + NonEscapeRewriter::clearRewritePhiIncoming(); + bool Changed = escapeAnalysisImpl(C, CG, AM, UR); + if (Changed) { + return PreservedAnalyses::none(); + } + return PreservedAnalyses::all(); +} + +namespace llvm { +bool escapeAnalysisFuncImpl(Function *F, + function_ref LookLoop) { + EADepthImpl EA(nullptr, F->getParent(), LookLoop); + EA.insertSCCFunctions(F); + EA.initialize(); + return EA.run(); +} +} diff --git a/llvm/lib/Transforms/IPO/CMakeLists.txt b/llvm/lib/Transforms/IPO/CMakeLists.txt index f9833224d..b612f0d37 100644 --- a/llvm/lib/Transforms/IPO/CMakeLists.txt +++ b/llvm/lib/Transforms/IPO/CMakeLists.txt @@ -7,6 +7,7 @@ add_llvm_component_library(LLVMipo BarrierNoopPass.cpp BlockExtractor.cpp CalledValuePropagation.cpp + CJPartialEscapeAnalysis.cpp ConstantMerge.cpp CrossDSOCFI.cpp DeadArgumentElimination.cpp diff --git a/llvm/lib/Transforms/IPO/ConstantMerge.cpp b/llvm/lib/Transforms/IPO/ConstantMerge.cpp index 73af30ece..848dbd75b 100644 --- a/llvm/lib/Transforms/IPO/ConstantMerge.cpp +++ b/llvm/lib/Transforms/IPO/ConstantMerge.cpp @@ -154,6 +154,10 @@ static bool mergeConstants(Module &M) { while (true) { // Find the canonical constants others will be merged with. for (GlobalVariable &GV : llvm::make_early_inc_range(M.globals())) { + // Cangjie native GV needs to be processed at the backend. + if (GV.hasAttribute("cj-native")) { + continue; + } // If this GV is dead, remove it. GV.removeDeadConstantUsers(); if (GV.use_empty() && GV.hasLocalLinkage()) { diff --git a/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp b/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp index 99fa4baf3..c3695524b 100644 --- a/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp +++ b/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp @@ -33,6 +33,7 @@ #include "llvm/IR/Module.h" #include "llvm/IR/NoFolder.h" #include "llvm/IR/PassManager.h" +#include "llvm/IR/SafepointIRVerifier.h" #include "llvm/IR/Type.h" #include "llvm/IR/Use.h" #include "llvm/IR/User.h" @@ -58,6 +59,10 @@ STATISTIC(NumRetValsEliminated, "Number of unused return values removed"); STATISTIC(NumArgumentsReplacedWithPoison, "Number of unread args replaced with poison"); +namespace llvm { +extern cl::opt CJPipeline; +} // namespace llvm + namespace { /// The dead argument elimination pass. @@ -291,6 +296,11 @@ bool DeadArgumentEliminationPass::removeDeadArgumentsFromCallers(Function &F) { AttributeMask UBImplyingAttributes = AttributeFuncs::getUBImplyingAttributes(); for (Argument &Arg : F.args()) { + // If it is GC pointer in cangjie, it may be used as base in callee and + // cannot be replaced with posion. + if (CJPipeline && isGCPointerType(Arg.getType())) + continue; + if (!Arg.hasSwiftErrorAttr() && Arg.use_empty() && !Arg.hasPassPointeeByValueCopyAttr()) { if (Arg.isUsedByMetadata()) { @@ -611,6 +621,7 @@ void DeadArgumentEliminationPass::surveyFunction(const Function &F) { // Now, check all of our arguments. unsigned ArgI = 0; UseVector MaybeLiveArgUses; + bool HasStructGCAlive = false; for (Function::const_arg_iterator AI = F.arg_begin(), E = F.arg_end(); AI != E; ++AI, ++ArgI) { Liveness Result; @@ -638,7 +649,30 @@ void DeadArgumentEliminationPass::surveyFunction(const Function &F) { markValue(createArg(&F, ArgI), Result, MaybeLiveArgUses); // Clear the vector again for the next iteration. MaybeLiveArgUses.clear(); + // In cangjie, if a gc struct argument is alive, then its base pointer + // should also be alive. The base pointer is the after argument of the + // struct pointer. + if (CJPipeline) { + Type *AT = AI->getType(); + if (isGCPointerType(AT) && + AT->getNonOpaquePointerElementType()->isStructTy() && + isLive(createArg(&F, ArgI))) { + HasStructGCAlive = true; + unsigned BasePtrIdx = ArgI + 1; + assert(BasePtrIdx < F.arg_size() && + "The struct parameter should have a base pointer."); + assert(isInt8AS1Pty(F.getArg(BasePtrIdx)->getType()) && + "The base pointer type should be i8 addrspace(1)*."); + markLive(createArg(&F, BasePtrIdx)); + // skip the base pointer check. + ++AI; + ++ArgI; + } + } } + // If all struct gcptr argument are dead, then remove record_mut attribute. + if (CJPipeline && !HasStructGCAlive && F.hasFnAttribute("record_mut")) + const_cast(F).removeFnAttr("record_mut"); } /// Marks the liveness of RA depending on L. If L is MaybeLive, it also takes diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index 50710eaa1..b9065ec1a 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -46,6 +46,7 @@ #include "llvm/IR/Metadata.h" #include "llvm/IR/ModuleSummaryIndex.h" #include "llvm/IR/PassManager.h" +#include "llvm/IR/SafepointIRVerifier.h" #include "llvm/IR/Type.h" #include "llvm/IR/Use.h" #include "llvm/IR/User.h" @@ -67,6 +68,10 @@ using namespace llvm; +namespace llvm { +extern cl::opt CJPipeline; +} // namespace llvm + #define DEBUG_TYPE "function-attrs" STATISTIC(NumArgMemOnly, "Number of functions marked argmemonly"); @@ -791,8 +796,11 @@ determinePointerAccessAttrs(Argument *A, return Attribute::ReadOnly; else if (IsWrite) return Attribute::WriteOnly; - else + else { + if (CJPipeline && isGCPointerType(A->getType())) + return Attribute::None; return Attribute::ReadNone; + } } /// Deduce returned attributes for the SCC. diff --git a/llvm/lib/Transforms/IPO/GlobalDCE.cpp b/llvm/lib/Transforms/IPO/GlobalDCE.cpp index f35827220..2cb899381 100644 --- a/llvm/lib/Transforms/IPO/GlobalDCE.cpp +++ b/llvm/lib/Transforms/IPO/GlobalDCE.cpp @@ -18,6 +18,7 @@ #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/TypeMetadataUtils.h" +#include "llvm/IR/InstIterator.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" @@ -25,6 +26,7 @@ #include "llvm/Pass.h" #include "llvm/Support/CommandLine.h" #include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Scalar/CJFillMetadata.h" #include "llvm/Transforms/Utils/CtorUtils.h" #include "llvm/Transforms/Utils/GlobalStatus.h" @@ -32,10 +34,18 @@ using namespace llvm; #define DEBUG_TYPE "globaldce" +namespace llvm { +extern cl::opt CJPipeline; +} // namespace llvm + static cl::opt ClEnableVFE("enable-vfe", cl::Hidden, cl::init(true), cl::desc("Enable virtual function elimination")); +static cl::opt + ClEnableCJVFE("enable-cangjie-vfe", cl::Hidden, cl::init(true), + cl::desc("Enable cangjie virtual function elimination")); + STATISTIC(NumAliases , "Number of global aliases removed"); STATISTIC(NumFunctions, "Number of functions removed"); STATISTIC(NumIFuncs, "Number of indirect functions removed"); @@ -318,6 +328,11 @@ PreservedAnalyses GlobalDCEPass::run(Module &M, ModuleAnalysisManager &MAM) { // might call, if we have that information. AddVirtualFunctionDependencies(M); + if (CJPipeline && ClEnableCJVFE) { + CangjieVFE CVFE(*this); + CVFE.addCangjieVirtualFunctionDependencies(M); + } + // Loop over the module, adding globals which are obviously necessary. for (GlobalObject &GO : M.global_objects()) { GO.removeDeadConstantUsers(); @@ -367,9 +382,14 @@ PreservedAnalyses GlobalDCEPass::run(Module &M, ModuleAnalysisManager &MAM) { // The first pass is to drop initializers of global variables which are dead. std::vector DeadGlobalVars; // Keep track of dead globals - for (GlobalVariable &GV : M.globals()) + for (GlobalVariable &GV : M.globals()) { + // Cangjie native GV needs to be processed at the backend.. + if (GV.hasAttribute("cj-native")) { + continue; + } + if (!AliveGlobals.count(&GV)) { - DeadGlobalVars.push_back(&GV); // Keep track of dead globals + DeadGlobalVars.push_back(&GV); // Keep track of dead globals if (GV.hasInitializer()) { Constant *Init = GV.getInitializer(); GV.setInitializer(nullptr); @@ -377,16 +397,20 @@ PreservedAnalyses GlobalDCEPass::run(Module &M, ModuleAnalysisManager &MAM) { Init->destroyConstant(); } } - + } // The second pass drops the bodies of functions which are dead... std::vector DeadFunctions; - for (Function &F : M) + for (Function &F : M) { + // Cangjie runtime function needs to be processed at the backend.. + if (F.hasFnAttribute("cj-runtime")) + continue; + if (!AliveGlobals.count(&F)) { - DeadFunctions.push_back(&F); // Keep track of dead globals + DeadFunctions.push_back(&F); // Keep track of dead globals if (!F.isDeclaration()) F.deleteBody(); } - + } // The third pass drops targets of aliases which are dead... std::vector DeadAliases; for (GlobalAlias &GA : M.aliases()) @@ -458,3 +482,207 @@ PreservedAnalyses GlobalDCEPass::run(Module &M, ModuleAnalysisManager &MAM) { return PreservedAnalyses::none(); return PreservedAnalyses::all(); } + +CangjieVFE::GenericFuncInfo * +CangjieVFE::insertInstance(const SmallVectorImpl &Path, + GlobalVariable *FT = nullptr) { + GenericFuncInfo *CurRoot = this->Root; + for (GlobalVariable *GV : Path) { + auto It = CurRoot->Next.find(GV); + if (It != CurRoot->Next.end()) { + CurRoot = It->second; + continue; + } + auto *GFI = new GenericFuncInfo; + GFI->Prev = CurRoot; + CurRoot->Next[GV] = GFI; + CurRoot = GFI; + } + CurRoot->IsValid = true; + if (FT) + CurRoot->FuncTables.insert(FT); + return CurRoot; +} + +CangjieVFE::GenericFuncInfo * +CangjieVFE::findTargetInstance(const SmallVectorImpl &Path) { + GenericFuncInfo *Root = this->Root; + for (GlobalVariable *GV : Path) { + auto It = Root->Next.find(GV); + if (It == Root->Next.end()) + return nullptr; + Root = It->second; + } + return Root->IsValid ? Root : nullptr; +} + +CangjieVFE::GenericFuncInfo *CangjieVFE::resolveVFEMeta(Metadata *MD, + Module &M) { + SmallVector Path; + MDNode *N; + while ((N = dyn_cast_or_null(MD))) { + auto *GV = M.getNamedGlobal( + dyn_cast(N->getOperand(0).get())->getString()); + // Some vcall may encounter situations where the target types does not appear + // in the current module. In such cases, it can be ensured that the virtual + // function being called is definitely not defined in the current module. + if (!GV) + return nullptr; + Path.push_back(GV); + // Layout of MD: + // {1.tt, [offset]} + // {1.tt, 2.ti, [offset]} + // {1.tt, !2, [offset]}, !2 = new MD + MD = N->getNumOperands() >= 2 ? N->getOperand(1) + : static_cast(nullptr); + } + if (auto *MDS = dyn_cast_or_null(MD)) + Path.push_back(M.getNamedGlobal(MDS->getString())); + return insertInstance(Path); +} + +// Obtain the function table FT that achieves P from C, return {C, P, FT}. +std::tuple +CangjieVFE::scanVTable(GlobalVariable &GV) { + assert(GV.hasInitializer()); + assert(GV.getType()->getNonOpaquePointerElementType()->getStructName() == + "ExtensionDef"); + Constant *C = GV.getInitializer(); + GlobalVariable *FT = dyn_cast( + C->getOperand(ExtensionDefFieldType::ET_FUNC_TABLE)->stripPointerCasts()); + if (!FT) + return {nullptr, nullptr, nullptr}; + GlobalVariable *TI = + cast(C->getOperand(ExtensionDefFieldType::ET_TARGET_TYPE) + ->stripPointerCasts()); + assert(GV.hasMetadata("inheritedType")); + GlobalVariable *IF = GV.getParent()->getNamedGlobal( + dyn_cast(GV.getMetadata("inheritedType")->getOperand(0)) + ->getString()); + +#ifndef NDEBUG + // Verify that the interface type obtained from metadata matches the one + // parsed from global variable. + Value *O = C->getOperand(ExtensionDefFieldType::ET_INTERFACE_FN) + ->stripPointerCasts(); + if (auto *F = dyn_cast(O)) { + auto *CB = dyn_cast(F->back().getTerminator()->getPrevNode()); + assert(CB->getCalledFunction()->getName() == "CJ_MCC_GetOrCreateTypeInfo"); + O = CB->getArgOperand(0)->stripPointerCasts(); + } else { + // O is typeinfo + auto *TT = cast(O) + ->getInitializer() + ->getOperand(CalssInfoFieldType::CIT_GENERIC_FROM) + ->stripPointerCasts(); + O = isa(TT) ? O : TT; + } + assert(IF == O); +#endif + + return {TI, IF, FT}; +} + +void CangjieVFE::updateDependencies( + Function *Caller, + DenseMap> &Relation) { + auto Mark = [Caller, this](SmallPtrSetImpl &FTs, + uint64_t Offset) { + for (auto *FT : FTs) { + auto *C = FT->getInitializer(); + if (C->getNumOperands() <= Offset) + continue; + Function *Callee = dyn_cast_or_null( + C->getOperand(Offset)->stripPointerCasts()); + if (!Callee) + continue; + assert(Callee != nullptr); + this->DCE.GVDependencies[Caller].insert(Callee); + } + }; + for (auto &[GFI, Offsets] : Relation) { + for (auto I : Offsets) { + // 1. Mark all child nodes. + SmallVector Worklist = {GFI}; + while (!Worklist.empty()) { + auto *Child = Worklist.pop_back_val(); + for (auto [_, N] : Child->Next) { + Mark(N->FuncTables, I); + Worklist.push_back(N); + } + } + // 2. Recursively mark all parent nodes. + while (GFI != Root) { + auto &FTs = GFI->FuncTables; + GFI = GFI->Prev; + Mark(FTs, I); + } + } + } +} + +void CangjieVFE::addCangjieVirtualFunctionDependencies(Module &M) { + // For cangjie, it has the following rules: + // - If A implements I, then apart from extend, the visibility of I must be + // greater than or equal to that of A. + // - If A extends I, then the visibility of I may be less than or equal to + // that of A. In this case, external packages cannot call I's methods through + // A. + // Therefore, for VFE, it is only necessary to be concerned with the + // visibility of the implemented type. As long as I is internal, optimization + // can be performed. + for (auto &GV : M.globals()) { + // If typeArgs >= 2, skip it. + if (!GV.hasAttribute("CFileMTable") || !GV.hasMetadata("inheritedType")) + continue; + if (!GV.hasInitializer()) + report_fatal_error("ExtensionDef must be initialized."); + auto [TIC, TII, FT] = scanVTable(GV); + if (!TIC || !TII || !FT) + continue; + if (GlobalValue::isLocalLinkage(TII->getLinkage())) { + auto *GFI = resolveVFEMeta(GV.getMetadata("inheritedType"), M); + assert(GFI); + GFI->FuncTables.insert(FT); + DCE.VFESafeVTables.insert(FT); + } + } + + for (Function &F : M) { + // Value: offsets + DenseMap> RelatedGV; + for (auto &I : instructions(F)) { + // There is an overlap between MD_obj_type and MD_intro_type, which needs + // to be considered for merging in the future. + auto *MD = I.getMetadata(LLVMContext::MD_obj_type); + if (!MD) + continue; + assert(isa(&I)); + auto *GFI = resolveVFEMeta(MD, M); + if (!GFI) + continue; + if (!I.hasMetadata(LLVMContext::MD_func_table)) + report_fatal_error("Missing metadata"); + auto Offset = + mdconst::extract( + I.getMetadata(LLVMContext::MD_func_table)->getOperand(0)) + ->getZExtValue(); + // The last operand is the index of functable. + RelatedGV[GFI].insert(Offset); + } + updateDependencies(&F, RelatedGV); + } +} + +CangjieVFE::~CangjieVFE() { + SmallVector Worklist = {Root}; + SmallVector Stack; + while (!Worklist.empty()) { + auto *GFI = Worklist.pop_back_val(); + Stack.push_back(GFI); + for (auto [_, N] : GFI->Next) + Worklist.push_back(N); + } + while (!Stack.empty()) + delete Stack.pop_back_val(); +} \ No newline at end of file diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp index 6df040925..86462261b 100644 --- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp +++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp @@ -1967,6 +1967,9 @@ OptimizeFunctions(Module &M, if (F.hasFnAttribute(Attribute::Naked)) continue; + // Cangjie runtime function needs to be processed at the backend.. + if (F.hasFnAttribute("cj-runtime")) + continue; // Functions without names cannot be referenced outside this module. if (!F.hasName() && !F.isDeclaration() && !F.hasLocalLinkage()) F.setLinkage(GlobalValue::InternalLinkage); @@ -2067,6 +2070,10 @@ OptimizeGlobalVars(Module &M, bool Changed = false; for (GlobalVariable &GV : llvm::make_early_inc_range(M.globals())) { + // Cangjie native GV needs to be processed at the backend.. + if (GV.hasAttribute("cj-native")) { + continue; + } // Global variables without names cannot be referenced outside this module. if (!GV.hasName() && !GV.isDeclaration() && !GV.hasLocalLinkage()) GV.setLinkage(GlobalValue::InternalLinkage); diff --git a/llvm/lib/Transforms/IPO/Inliner.cpp b/llvm/lib/Transforms/IPO/Inliner.cpp index 4d32266eb..552e571d7 100644 --- a/llvm/lib/Transforms/IPO/Inliner.cpp +++ b/llvm/lib/Transforms/IPO/Inliner.cpp @@ -72,6 +72,8 @@ using namespace llvm; #define DEBUG_TYPE "inline" +extern cl::opt MaxRecursionInl; + STATISTIC(NumInlined, "Number of functions inlined"); STATISTIC(NumCallsDeleted, "Number of call sites deleted, not inlined"); STATISTIC(NumDeleted, "Number of functions deleted because all callers found"); @@ -333,9 +335,15 @@ static InlineResult inlineCallIfPossible( /// Return true if the specified inline history ID /// indicates an inline history that includes the specified function. +/// SkipFirst allows to skip the check for the first items of the list. static bool inlineHistoryIncludes( Function *F, int InlineHistoryID, - const SmallVectorImpl> &InlineHistory) { + const SmallVectorImpl> &InlineHistory, + int SkipFirst) { + for (int I = 0; InlineHistoryID != -1 && I < SkipFirst; ++I) { + InlineHistoryID = InlineHistory[InlineHistoryID].second; + } + while (InlineHistoryID != -1) { assert(unsigned(InlineHistoryID) < InlineHistory.size() && "Invalid inline history ID"); @@ -470,7 +478,8 @@ inlineCallsImpl(CallGraphSCC &SCC, CallGraph &CG, // which would provide the same callsites, which would cause us to // infinitely inline. if (InlineHistoryID != -1 && - inlineHistoryIncludes(Callee, InlineHistoryID, InlineHistory)) { + inlineHistoryIncludes(Callee, InlineHistoryID, InlineHistory, + MaxRecursionInl - 1)) { setInlineRemark(CB, "recursive"); continue; } @@ -869,11 +878,11 @@ PreservedAnalyses InlinerPass::run(LazyCallGraph::SCC &InitialC, CallBase *CB = P.first; const int InlineHistoryID = P.second; Function &Callee = *CB->getCalledFunction(); - if (InlineHistoryID != -1 && - inlineHistoryIncludes(&Callee, InlineHistoryID, InlineHistory)) { - LLVM_DEBUG(dbgs() << "Skipping inlining due to history: " - << F.getName() << " -> " << Callee.getName() << "\n"); + inlineHistoryIncludes(&Callee, InlineHistoryID, InlineHistory, + MaxRecursionInl - 1)) { + LLVM_DEBUG(dbgs() << "Skipping inlining due to history: " << F.getName() + << " -> " << Callee.getName() << "\n"); setInlineRemark(*CB, "recursive"); continue; } diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp index b850591b4..f5754d089 100644 --- a/llvm/lib/Transforms/IPO/MergeFunctions.cpp +++ b/llvm/lib/Transforms/IPO/MergeFunctions.cpp @@ -119,6 +119,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Utils/CodeExtractor.h" #include "llvm/Transforms/Utils/FunctionComparator.h" #include "llvm/Transforms/Utils/ModuleUtils.h" #include @@ -200,6 +201,7 @@ public: } bool runOnModule(Module &M); + void doExceptionOutline(Module &M); private: // The function comparison operator is provided here so that FunctionNodes do @@ -408,9 +410,118 @@ static bool isEligibleForMerging(Function &F) { return !F.isDeclaration() && !F.hasAvailableExternallyLinkage(); } +static bool isBBEligibleForOutline(llvm::BasicBlock *BB) { + if (!BB) + return false; + + // Do not optimize the function to prevent the failure of stack frame throwing + // due to runtime computation exceptions. + if (BB->getParent()->getName().equals("rt$ThrowStackOverflowError")) + return false; + if (BB->getParent()->getName().equals("rt$ThrowImplicitException")) + return false; + for (auto &Inst : *BB) { + if (const GetElementPtrInst *GEP = dyn_cast(&Inst)) { + return false; + } + if (CallInst *CI = dyn_cast(&Inst)) { + Function *Callee = CI->getCalledFunction(); + if (!Callee) + return false; + if (Callee->isIntrinsic()) { + return false; + } + if (Callee->getName().isSetDebugLocation() || + Callee->getName().isGetGCPhase() || + Callee->getName().startswith("__builtin_") || + Callee->getName().startswith("llvm.")) { + return false; + } + } + for (auto &Use : Inst.operands()) { + if (auto *User = Use.get()) { + if (auto *UserInst = llvm::dyn_cast(User)) { + // If it's not a Instruction, there's no need to use it in the current + // context. For example GV. + if (UserInst->getParent() != BB) { + return false; + } + } + } + } + } + return true; +} + +static bool isThrowExceptionInstruction(llvm::Instruction *Inst) { + if (auto *CallInst = llvm::dyn_cast(Inst)) { + if (auto *Callee = CallInst->getCalledFunction()) { + return Callee->getName() == "CJ_MCC_ThrowException"; + } + } + return false; +} + +static void getInputArgs(llvm::BasicBlock *BB, SetVector &ArgInputs) { + for (auto &Inst : *BB) { + if (CallInst *CI = dyn_cast(&Inst)) { + Function *Callee = CI->getCalledFunction(); + if (Callee && !Callee->getName().startswith("CJ_MCC") && !Callee->isIntrinsic()) { + ArgInputs.insert(Callee); + } + } + for (auto &Use : Inst.operands()) { + if (auto *User = Use.get()) { + // Values can be Input Arguement. + if (auto *GV = llvm::dyn_cast(User)) { + ArgInputs.insert(GV); + } else if (auto *CE = dyn_cast(User)) { + if (CE->getOpcode() == Instruction::BitCast) + ArgInputs.insert(CE); + } else if (ConstantInt *CI = dyn_cast(User)) { + ArgInputs.insert(CI); + } + } + } + } +} + +static Function* generateFunc(BasicBlock *BB, Function *OrigF) { + SmallVector BE; + BE.push_back(BB); + CodeExtractor CE(BE, nullptr, false, nullptr, nullptr, nullptr, false, false, + nullptr, "exception_outlined_func"); + SetVector ArgInputs; + SetVector Outputs; + getInputArgs(BB, ArgInputs); + CodeExtractorAnalysisCache CEAC(*OrigF); + Function *Func = CE.extractCodeRegion(CEAC, ArgInputs, Outputs); + return Func; +} + +void MergeFunctions::doExceptionOutline(Module &M) { + SmallVector> ExceptionBBs; + + for (Function &F : M) { + for (BasicBlock &BB : F) { + for (Instruction &Inst : BB) { + if (isThrowExceptionInstruction(&Inst)) { + if (!isBBEligibleForOutline(&BB)) + continue; + ExceptionBBs.push_back(std::make_pair(&BB, &F)); + } + } + } + } + + for (auto &pair : ExceptionBBs) { + generateFunc(pair.first, pair.second); + } +} + bool MergeFunctions::runOnModule(Module &M) { bool Changed = false; - + doExceptionOutline(M); SmallVector UsedV; collectUsedGlobalVariables(M, UsedV, /*CompilerUsed=*/false); collectUsedGlobalVariables(M, UsedV, /*CompilerUsed=*/true); @@ -661,6 +772,27 @@ static bool canCreateThunkFor(Function *F) { // Don't merge tiny functions using a thunk, since it can just end up // making the function larger. if (F->size() == 1) { + // For a single external function in the standard library, a conservative + // strategy is adopted to replace it with a thunk function. + if (F->hasCangjieGC() && F->use_empty() && F->hasExternalLinkage()) { + uint32_t ExternalFuncThrd = 30; + if (F->front().size() <= ExternalFuncThrd) { + LLVM_DEBUG(dbgs() << "canCreateThunkFor: " << F->getName() + << "External function is too small to bother " + "creating a thunk for\n"); + return false; + } + } + // Considering the overhead introduced by GC operations and the number of + // lines of code. + uint32_t CangjieFuncThrd = 10; + if (F->hasCangjieGC() && F->front().size() <= CangjieFuncThrd) { + LLVM_DEBUG( + dbgs() + << "canCreateThunkFor: " << F->getName() + << "Cangjie function is too small to bother creating a thunk for\n"); + return false; + } if (F->front().size() <= 2) { LLVM_DEBUG(dbgs() << "canCreateThunkFor: " << F->getName() << " is too small to bother creating a thunk for\n"); diff --git a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp index f1b6f2bb7..7ce4fa79f 100644 --- a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp +++ b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp @@ -17,6 +17,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/Analysis/CFLAndersAliasAnalysis.h" #include "llvm/Analysis/CFLSteensAliasAnalysis.h" +#include "llvm/Analysis/CJAliasAnalysis.h" #include "llvm/Analysis/GlobalsModRef.h" #include "llvm/Analysis/ScopedNoAliasAA.h" #include "llvm/Analysis/TargetLibraryInfo.h" @@ -44,8 +45,16 @@ using namespace llvm; namespace llvm { +extern cl::opt CJPipeline; +cl::opt CJDisableEscapeAnalysis("cj-disable-ea", cl::Hidden, + cl::init(false)); cl::opt RunPartialInlining("enable-partial-inlining", cl::Hidden, cl::desc("Run Partial inlinining pass")); +cl::opt EnableCJBarrierSplit("enable-cj-barrier-split", cl::Hidden, + cl::init(true)); +cl::opt EnableCJGenericIntrinsicOpt( + "enable-cj-generic-intrinsic-opt", cl::init(true), cl::Hidden, + cl::desc("Enable cangjie generic intrinsics optimization")); static cl::opt UseGVNAfterVectorization("use-gvn-after-vectorization", @@ -165,14 +174,15 @@ cl::opt AttributorRun( extern cl::opt EnableKnowledgeRetention; } // namespace llvm - +extern cl::opt MaxRecursionInl; +extern cl::opt CountedLoopTripWidth; PassManagerBuilder::PassManagerBuilder() { OptLevel = 2; SizeLevel = 0; LibraryInfo = nullptr; Inliner = nullptr; DisableUnrollLoops = false; - SLPVectorize = false; + SLPVectorize = true; LoopVectorize = true; LoopsInterleaved = true; RerollLoops = RunLoopRerolling; @@ -273,12 +283,13 @@ void PassManagerBuilder::addInitialAliasAnalysisPasses( // support "obvious" type-punning idioms. PM.add(createTypeBasedAAWrapperPass()); PM.add(createScopedNoAliasAAWrapperPass()); + if (CJPipeline) + PM.add(createCangjieAAWrapperPass()); } void PassManagerBuilder::populateFunctionPassManager( legacy::FunctionPassManager &FPM) { addExtensionsToPM(EP_EarlyAsPossible, FPM); - // Add LibraryInfo if we have some. if (LibraryInfo) FPM.add(new TargetLibraryInfoWrapperPass(*LibraryInfo)); @@ -307,6 +318,7 @@ void PassManagerBuilder::addFunctionSimplificationPasses( // Start of function pass. // Break up aggregate allocas, using SSAUpdater. assert(OptLevel >= 1 && "Calling function optimizer with no optimization level!"); + MPM.add(createSROAPass()); MPM.add(createEarlyCSEPass(true /* Enable mem-ssa. */)); // Catch trivial redundancies if (EnableKnowledgeRetention) @@ -332,9 +344,12 @@ void PassManagerBuilder::addFunctionSimplificationPasses( MPM.add(createJumpThreadingPass()); // Thread jumps. MPM.add(createCorrelatedValuePropagationPass()); // Propagate conditionals } + if (CJPipeline && OptLevel > 1) + MPM.add(createCJSimpleRangeAnalysis()); MPM.add( createCFGSimplificationPass(SimplifyCFGOptions().convertSwitchRangeToICmp( true))); // Merge & remove BBs + // Combine silly seq's if (OptLevel > 2) MPM.add(createAggressiveInstCombinerPass()); @@ -350,7 +365,14 @@ void PassManagerBuilder::addFunctionSimplificationPasses( createCFGSimplificationPass(SimplifyCFGOptions().convertSwitchRangeToICmp( true))); // Merge & remove BBs MPM.add(createReassociatePass()); // Reassociate expressions - + // Begin the loop pass pipeline. + if (CJPipeline) { + // 2: Opt size level, Oz. -1: argument for MaxHeaderSize + MPM.add(createLoopRotatePass(SizeLevel == 2 ? 0 : -1)); + MPM.add(createInstructionCombiningPass()); + MPM.add(createIndVarSimplifyPass()); + MPM.add(createLoopDeletionPass()); + } // The matrix extension can introduce large vector operations early, which can // benefit from running vector-combine early on. if (EnableMatrix) @@ -384,6 +406,9 @@ void PassManagerBuilder::addFunctionSimplificationPasses( MPM.add(createCFGSimplificationPass( SimplifyCFGOptions().convertSwitchRangeToICmp(true))); MPM.add(createInstructionCombiningPass()); + if (CJPipeline) { + MPM.add(createCJLoopFloatOptLegacyPass()); + } // We resume loop passes creating a second loop pipeline here. if (EnableLoopFlatten) { MPM.add(createLoopFlattenPass()); // Flatten loops @@ -575,6 +600,33 @@ void PassManagerBuilder::addVectorPasses(legacy::PassManagerBase &PM, void PassManagerBuilder::populateModulePassManager( legacy::PassManagerBase &MPM) { + if (CJPipeline) { + // Customize parameters for cangjie-pipeline. + if (OptLevel > 1 && !MaxRecursionInl.getPosition()) { + MaxRecursionInl = 1; + } + // We put 64-bit safepoint optimization to O3 level for safety. + if (OptLevel > 2) { + CountedLoopTripWidth = 64; + } else { + CountedLoopTripWidth = 16; + } + SLPVectorize = true; + + // Fill Klass at the beginning since related structs may be optimized later + MPM.add(createCJFillMetadataLegacyPass()); + MPM.add(createCJRuntimeLoweringLegacyPass()); + if (EnableCJBarrierSplit) + MPM.add(createCJBarrierSplitLegacyPass()); + } + + auto addCangjiePasses = [&]() { + if (CJPipeline) { + MPM.add(createCangjieSpecificOptLegacyPass(OptLevel)); + MPM.add(createPlaceSafepointsLegacyPass()); + MPM.add(createCJRewriteStatepointLegacyPass(OptLevel)); + } + }; MPM.add(createAnnotation2MetadataLegacyPass()); // Allow forcing function attributes as a debugging and tuning aid. @@ -601,6 +653,7 @@ void PassManagerBuilder::populateModulePassManager( addExtensionsToPM(EP_EnabledOnOptLevel0, MPM); MPM.add(createAnnotationRemarksLegacyPass()); + addCangjiePasses(); return; } @@ -655,6 +708,11 @@ void PassManagerBuilder::populateModulePassManager( RunInliner = true; } + if (CJPipeline && OptLevel > 1) { + if (EnableCJGenericIntrinsicOpt) + MPM.add(createCJGenericIntrinsicOptLegacyPass()); + } + // Infer attributes on declarations, call sites, arguments, etc. for an SCC. if (AttributorRun & AttributorRunOption::CGSCC) MPM.add(createAttributorCGSCCLegacyPass()); @@ -798,6 +856,7 @@ void PassManagerBuilder::populateModulePassManager( addExtensionsToPM(EP_OptimizerLast, MPM); MPM.add(createAnnotationRemarksLegacyPass()); + addCangjiePasses(); } LLVMPassManagerBuilderRef LLVMPassManagerBuilderCreate() { diff --git a/llvm/lib/Transforms/IPO/PruneEH.cpp b/llvm/lib/Transforms/IPO/PruneEH.cpp index e0836a9fd..071ed72a7 100644 --- a/llvm/lib/Transforms/IPO/PruneEH.cpp +++ b/llvm/lib/Transforms/IPO/PruneEH.cpp @@ -194,6 +194,15 @@ static bool SimplifyFunction(Function *F, CallGraphUpdater &CGU) { MadeChange = true; } + // WorkAround: For N2CStub functions, the reachable block cannot be + // deleted. If the callee function contains the unreachable block of + // ThrowException, here, the runtime EH needs to unwind based on the return + // instruction. + // In the future, if EH does not rely on the epilog to return to unwind, + // remove the process. + if (F->hasFnAttribute("cjstub")) { + continue; + } for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) if (CallInst *CI = dyn_cast(I++)) if (CI->doesNotReturn() && !CI->isMustTailCall() && diff --git a/llvm/lib/Transforms/IPO/SCCP.cpp b/llvm/lib/Transforms/IPO/SCCP.cpp index 0453af184..aff04087d 100644 --- a/llvm/lib/Transforms/IPO/SCCP.cpp +++ b/llvm/lib/Transforms/IPO/SCCP.cpp @@ -16,12 +16,17 @@ #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/InitializePasses.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/Scalar/SCCP.h" #include "llvm/Transforms/Utils/SCCPSolver.h" using namespace llvm; +namespace llvm { +extern cl::opt CJPipeline; +} // namespace llvm + PreservedAnalyses IPSCCPPass::run(Module &M, ModuleAnalysisManager &AM) { const DataLayout &DL = M.getDataLayout(); auto &FAM = AM.getResult(M).getManager(); @@ -30,9 +35,10 @@ PreservedAnalyses IPSCCPPass::run(Module &M, ModuleAnalysisManager &AM) { }; auto getAnalysis = [&FAM](Function &F) -> AnalysisResultsForFn { DominatorTree &DT = FAM.getResult(F); - return { - std::make_unique(F, DT, FAM.getResult(F)), - &DT, FAM.getCachedResult(F)}; + return {std::make_unique( + F, DT, FAM.getResult(F)), + &DT, FAM.getCachedResult(F), + CJPipeline ? &FAM.getResult(F) : nullptr}; }; if (!runIPSCCP(M, DL, GetTLI, getAnalysis)) diff --git a/llvm/lib/Transforms/IPO/StripDeadPrototypes.cpp b/llvm/lib/Transforms/IPO/StripDeadPrototypes.cpp index 0f2412dce..dcd343ffe 100644 --- a/llvm/lib/Transforms/IPO/StripDeadPrototypes.cpp +++ b/llvm/lib/Transforms/IPO/StripDeadPrototypes.cpp @@ -31,6 +31,9 @@ static bool stripDeadPrototypes(Module &M) { // Erase dead function prototypes. for (Function &F : llvm::make_early_inc_range(M)) { + // Cangjie runtime function needs to be processed at the backend.. + if (F.hasFnAttribute("cj-runtime")) + continue; // Function must be a prototype and unused. if (F.isDeclaration() && F.use_empty()) { F.eraseFromParent(); @@ -41,6 +44,9 @@ static bool stripDeadPrototypes(Module &M) { // Erase dead global var prototypes. for (GlobalVariable &GV : llvm::make_early_inc_range(M.globals())) { + // Cangjie native GV needs to be processed at the backend.. + if (GV.hasAttribute("cj-native")) + continue; // Global must be a prototype and unused. if (GV.isDeclaration() && GV.use_empty()) GV.eraseFromParent(); diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp index 52596b304..65ea00482 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -50,6 +50,7 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/PatternMatch.h" +#include "llvm/IR/SafepointIRVerifier.h" #include "llvm/IR/Statepoint.h" #include "llvm/IR/Type.h" #include "llvm/IR/User.h" @@ -65,6 +66,7 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/InstCombine/InstCombiner.h" +#include "llvm/Transforms/Scalar/InsertCJTBAA.h" #include "llvm/Transforms/Utils/AssumeBundleBuilder.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/SimplifyLibCalls.h" @@ -92,6 +94,7 @@ namespace llvm { /// enable preservation of attributes in assume like: /// call void @llvm.assume(i1 true) [ "nonnull"(i32* %PTR) ] extern cl::opt EnableKnowledgeRetention; +extern cl::opt CJPipeline; } // namespace llvm /// Return the specified type promoted as it would be to pass though a va_arg @@ -117,6 +120,41 @@ static bool hasUndefSource(AnyMemTransferInst *MI) { return isa(Src) && Src->hasOneUse(); } +// If the element of src/dst is pointer type, return it. +// If can find actual type, return it. +// Others, return nullptr. +static Type *getMatchedType(Value *V, const DataLayout &DL, uint64_t Size) { + APInt Offset(64, 0); + auto *Base = V->stripAndAccumulateConstantOffsets(DL, Offset, true); + Type *Ty = Base->getType()->getNonOpaquePointerElementType(); + if (Ty->isPointerTy()) { + return Ty; + } else if (Ty->isArrayTy()) { + Type *ET = Ty->getArrayElementType(); + if (ET->isPointerTy()) { + return ET; + } + } + + // process follow case: + // %1 = phi i8* [],[] + // %2 = bitcast %1 to struct* + // or + // %3 = bitcast %1 to i64* + if (Ty->isIntegerTy()) { + if (auto *CommonTy = getUniqueActualType(Base)) { + Ty = CommonTy; + } + } + + if (auto ST = dyn_cast(Ty)) { + auto *InnerTy = getInnerTypeByOffset(DL, ST, Offset.getZExtValue()); + if (InnerTy) + return DL.getTypeSizeInBits(InnerTy) / 8 == Size ? InnerTy : nullptr; + } + return nullptr; +} + Instruction *InstCombinerImpl::SimplifyAnyMemTransfer(AnyMemTransferInst *MI) { Align DstAlign = getKnownAlignment(MI->getRawDest(), DL, MI, &AC, &DT); MaybeAlign CopyDstAlign = MI->getDestAlign(); @@ -172,15 +210,28 @@ Instruction *InstCombinerImpl::SimplifyAnyMemTransfer(AnyMemTransferInst *MI) { if (*CopyDstAlign < Size || *CopySrcAlign < Size) return nullptr; - // Use an integer load+store unless we can find something better. + // Use an load+store unless we can find something better. unsigned SrcAddrSp = cast(MI->getArgOperand(1)->getType())->getAddressSpace(); unsigned DstAddrSp = cast(MI->getArgOperand(0)->getType())->getAddressSpace(); - IntegerType* IntType = IntegerType::get(MI->getContext(), Size<<3); - Type *NewSrcPtrTy = PointerType::get(IntType, SrcAddrSp); - Type *NewDstPtrTy = PointerType::get(IntType, DstAddrSp); + // default type: intX + Type *NewType = IntegerType::get(MI->getContext(), Size << 3); + if (CJPipeline) { + Type *Arg0T = getMatchedType(MI->getArgOperand(0), DL, Size); + Type *Arg1T = getMatchedType(MI->getArgOperand(1), DL, Size); + if (Arg0T && Arg0T->isPointerTy() && Size == 8) { // 8: ptr size + NewType = Arg0T; + } else if (Arg1T && Arg1T->isPointerTy() && Size == 8) { // 8: ptr size + NewType = Arg1T; + } else if (Arg0T == Arg1T && Arg0T != nullptr) { + NewType = Arg0T; + } + } + + Type *NewSrcPtrTy = PointerType::get(NewType, SrcAddrSp); + Type *NewDstPtrTy = PointerType::get(NewType, DstAddrSp); // If the memcpy has metadata describing the members, see if we can get the // TBAA tag describing our copy. @@ -201,11 +252,13 @@ Instruction *InstCombinerImpl::SimplifyAnyMemTransfer(AnyMemTransferInst *MI) { Value *Src = Builder.CreateBitCast(MI->getArgOperand(1), NewSrcPtrTy); Value *Dest = Builder.CreateBitCast(MI->getArgOperand(0), NewDstPtrTy); - LoadInst *L = Builder.CreateLoad(IntType, Src); + LoadInst *L = Builder.CreateLoad(NewType, Src); // Alignment from the mem intrinsic will be better, so use it. L->setAlignment(*CopySrcAlign); if (CopyMD) L->setMetadata(LLVMContext::MD_tbaa, CopyMD); + if (CJPipeline && !L->hasMetadata(LLVMContext::MD_tbaa)) + prepareCJTBAA(DL, L, L->getPointerOperand(), L->getType()); MDNode *LoopMemParallelMD = MI->getMetadata(LLVMContext::MD_mem_parallel_loop_access); if (LoopMemParallelMD) @@ -219,6 +272,9 @@ Instruction *InstCombinerImpl::SimplifyAnyMemTransfer(AnyMemTransferInst *MI) { S->setAlignment(*CopyDstAlign); if (CopyMD) S->setMetadata(LLVMContext::MD_tbaa, CopyMD); + if (CJPipeline && !S->hasMetadata(LLVMContext::MD_tbaa)) + prepareCJTBAA(DL, S, S->getPointerOperand(), + S->getValueOperand()->getType()); if (LoopMemParallelMD) S->setMetadata(LLVMContext::MD_mem_parallel_loop_access, LoopMemParallelMD); if (AccessGroupMD) @@ -3083,6 +3139,7 @@ Instruction *InstCombinerImpl::visitCallBase(CallBase &Call) { // Handle intrinsics which can be used in both call and invoke context. switch (Call.getIntrinsicID()) { + case Intrinsic::cj_gc_statepoint: case Intrinsic::experimental_gc_statepoint: { GCStatepointInst &GCSP = *cast(&Call); SmallPtrSet LiveGcValues; diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp index a9a930555..ca5983220 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp @@ -15,6 +15,7 @@ #include "llvm/Analysis/ConstantFolding.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/PatternMatch.h" +#include "llvm/IR/SafepointIRVerifier.h" #include "llvm/Support/KnownBits.h" #include "llvm/Transforms/InstCombine/InstCombiner.h" using namespace llvm; @@ -96,6 +97,10 @@ Instruction *InstCombinerImpl::PromoteCastOfAllocation(BitCastInst &CI, Type *CastElTy = PTy->getNonOpaquePointerElementType(); if (!AllocElTy->isSized() || !CastElTy->isSized()) return nullptr; + // If the type of alloca contains gcptr, its type cannot be changed. + // Otherwise, we cannot identify gcptr in rewrite-statepoint-for-gc. + if (containsGCPtrType(AllocElTy)) return nullptr; + // This optimisation does not work for cases where the cast type // is scalable and the allocated type is not. This because we need to // know how many times the casted type fits into the allocated type. diff --git a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp index e03b7026f..30197dc45 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp @@ -22,6 +22,7 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/IR/PatternMatch.h" #include "llvm/Transforms/InstCombine/InstCombiner.h" +#include "llvm/Transforms/Scalar/InsertCJTBAA.h" #include "llvm/Transforms/Utils/Local.h" using namespace llvm; using namespace PatternMatch; @@ -667,6 +668,10 @@ static Instruction *unpackLoadToAggregate(InstCombinerImpl &IC, LoadInst &LI) { commonAlignment(Align, SL->getElementOffset(i)), Name + ".unpack"); // Propagate AA metadata. It'll still be valid on the narrowed load. L->setAAMetadata(LI.getAAMetadata()); + if (L->getMetadata(LLVMContext::MD_tbaa_struct)) { + L->setMetadata(LLVMContext::MD_tbaa_struct, nullptr); + prepareCJTBAA(DL, L, L->getPointerOperand(), L->getType()); + } V = IC.Builder.CreateInsertValue(V, L, i); } diff --git a/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp b/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp index 90a796a09..3a74a600a 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp @@ -738,6 +738,7 @@ Instruction *InstCombinerImpl::foldPHIArgLoadIntoPHI(PHINode &PN) { LLVMContext::MD_dereferenceable, LLVMContext::MD_dereferenceable_or_null, LLVMContext::MD_access_group, + LLVMContext::MD_cj_agg }; for (unsigned ID : KnownIDs) diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp index 71c763de4..6704b2ce5 100644 --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -167,6 +167,10 @@ MaxArraySize("instcombine-maxarray-size", cl::init(1024), static cl::opt ShouldLowerDbgDeclare("instcombine-lower-dbg-declare", cl::Hidden, cl::init(true)); +namespace llvm { +extern cl::opt CJPipeline; +} + Optional InstCombiner::targetInstCombineIntrinsic(IntrinsicInst &II) { // Handle target specific intrinsics @@ -2217,7 +2221,8 @@ Instruction *InstCombinerImpl::visitGEPOfBitcast(BitCastInst *BCI, // By avoiding such GEPs, phi translation and MemoryDependencyAnalysis have // a better chance to succeed. if (!isa(SrcOp) && GEP.accumulateConstantOffset(DL, Offset) && - !isAllocationFn(SrcOp, &TLI)) { + !isAllocationFn(SrcOp, &TLI) && + !(CJPipeline && isa(SrcOp))) { // If this GEP instruction doesn't move the pointer, just replace the GEP // with a bitcast of the real input to the dest type. if (!Offset) { @@ -2786,6 +2791,7 @@ static bool isAllocSiteRemovable(Instruction *AI, case Intrinsic::lifetime_start: case Intrinsic::lifetime_end: case Intrinsic::objectsize: + case Intrinsic::cj_memset: Users.emplace_back(I); continue; case Intrinsic::launder_invariant_group: @@ -2793,6 +2799,28 @@ static bool isAllocSiteRemovable(Instruction *AI, Users.emplace_back(I); Worklist.push_back(I); continue; + case Intrinsic::cj_gcwrite_ref: + if (II->getOperand(0) == PI) + return false; + Users.emplace_back(I); + continue; + case Intrinsic::cj_gcwrite_struct: + case Intrinsic::cj_gcwrite_generic: + // PI is written to memory, it cannot be removed. + if (II->getOperand(2) == PI) + return false; + Users.emplace_back(I); + continue; + case Intrinsic::cj_gcread_generic: + if (II->getArgOperand(1) == PI || II->getArgOperand(2) == PI) + return false; + Users.emplace_back(I); + continue; + case Intrinsic::cj_assign_generic: + if (II->getArgOperand(1) == PI) + return false; + Users.emplace_back(I); + continue; } } @@ -3886,7 +3914,7 @@ bool InstCombinerImpl::freezeOtherUses(FreezeInst &FI) { while (isa(MoveBefore)) MoveBefore = MoveBefore->getNextNode(); } else if (auto *PN = dyn_cast(Op)) { - MoveBefore = PN->getParent()->getFirstNonPHI(); + MoveBefore = &*PN->getParent()->getFirstInsertionPt(); } else if (auto *II = dyn_cast(Op)) { MoveBefore = II->getNormalDest()->getFirstNonPHI(); } else if (auto *CB = dyn_cast(Op)) { @@ -4389,6 +4417,16 @@ public: } }; +static bool isCJFPToIntOverflowCheck(Instruction *I) { + if (!CJPipeline) + return false; + + if (!isa(I) && !isa(I)) + return false; + + return true; +} + /// Populate the IC worklist from a function, by walking it in depth-first /// order and adding all reachable code to the worklist. /// @@ -4421,6 +4459,15 @@ static bool prepareICWorklistFromFunction(Function &F, const DataLayout &DL, if (!Inst.use_empty() && (Inst.getNumOperands() == 0 || isa(Inst.getOperand(0)))) if (Constant *C = ConstantFoldInstruction(&Inst, DL, TLI)) { + if (isa(C) && isCJFPToIntOverflowCheck(&Inst)) { + for (User *U : Inst.users()) { + auto II = dyn_cast(U); + if (II == nullptr || + II->getIntrinsicID() != Intrinsic::cj_get_fp_state) + continue; + II->replaceAllUsesWith(ConstantInt::get(II->getType(), 1)); + } + } LLVM_DEBUG(dbgs() << "IC: ConstFold to: " << *C << " from: " << Inst << '\n'); Inst.replaceAllUsesWith(C); diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp index 3274e36ab..23f9be5b9 100644 --- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -59,6 +59,7 @@ #include "llvm/IR/MDBuilder.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" +#include "llvm/IR/SafepointIRVerifier.h" #include "llvm/IR/Type.h" #include "llvm/IR/Use.h" #include "llvm/IR/Value.h" @@ -145,6 +146,7 @@ const char kAsanVersionCheckNamePrefix[] = "__asan_version_mismatch_check_v"; const char kAsanPtrCmp[] = "__sanitizer_ptr_cmp"; const char kAsanPtrSub[] = "__sanitizer_ptr_sub"; const char kAsanHandleNoReturnName[] = "__asan_handle_no_return"; +const char kCjAsanHandleNoReturnName[] = "CJ_MCC_AsanHandleNoReturn"; static const int kMaxAsanStackMallocSizeClass = 10; const char kAsanStackMallocNameTemplate[] = "__asan_stack_malloc_"; const char kAsanStackMallocAlwaysNameTemplate[] = @@ -178,6 +180,8 @@ static const size_t kNumberOfAccessSizes = 5; static const uint64_t kAllocaRzSize = 32; +static const uint64_t kCjRedZoneAlign = 16; + // ASanAccessInfo implementation constants. constexpr size_t kCompileKernelShift = 0; constexpr size_t kCompileKernelMask = 0x1; @@ -187,6 +191,9 @@ constexpr size_t kIsWriteShift = 5; constexpr size_t kIsWriteMask = 0x1; // Command-line flags. +static cl::opt ClEnableCjAsan( + "cj-asan", cl::desc("Enable Cangjie Asan Support"), + cl::Hidden, cl::init(false)); static cl::opt ClEnableKasan( "asan-kernel", cl::desc("Enable KernelAddressSanitizer instrumentation"), @@ -492,7 +499,7 @@ static ShadowMapping getShadowMapping(const Triple &TargetTriple, int LongSize, ShadowMapping Mapping; Mapping.Scale = kDefaultShadowScale; - if (ClMappingScale.getNumOccurrences() > 0) { + if (!ClEnableCjAsan && ClMappingScale.getNumOccurrences() > 0) { Mapping.Scale = ClMappingScale; } @@ -616,6 +623,9 @@ ASanAccessInfo::ASanAccessInfo(bool IsWrite, bool CompileKernel, static uint64_t getRedzoneSizeForScale(int MappingScale) { // Redzone used for stack and globals is at least 32 bytes. // For scales 6 and 7, the redzone has to be 64 and 128 bytes respectively. + if (ClEnableCjAsan) { + return std::max(16U, 1U << MappingScale); + } return std::max(32U, 1U << MappingScale); } @@ -874,6 +884,8 @@ struct FunctionStackPoisoner : public InstVisitor { LLVMContext *C; Type *IntptrTy; Type *IntptrPtrTy; + Type *Int8Ty; + Type *Int64Ty; ShadowMapping Mapping; SmallVector AllocaVec; @@ -914,7 +926,8 @@ struct FunctionStackPoisoner : public InstVisitor { !Triple(F.getParent()->getTargetTriple()).isAMDGPU()) {} bool runOnFunction() { - if (!PoisonStack) + if (!PoisonStack || + (ClEnableCjAsan && !F.hasFnAttribute("address_sanitize_stack"))) return false; if (ClRedzoneByvalArgs) @@ -1128,6 +1141,37 @@ ModuleAddressSanitizerPass::ModuleAddressSanitizerPass( PreservedAnalyses ModuleAddressSanitizerPass::run(Module &M, ModuleAnalysisManager &MAM) { + // special configurations in cangjie asan + if (ClEnableCjAsan) { + // no kasan in cangjie + ClEnableKasan.setValue(false, true); + Options.CompileKernel = false; + + // no read/write instrumentation, already done in frontend + ClInstrumentReads.setValue(false, true); + ClInstrumentWrites.setValue(false, true); + ClInstrumentAtomics.setValue(false, true); + ClInvalidPointerPairs.setValue(false, true); + ClInvalidPointerCmp.setValue(false, true); + ClInvalidPointerSub.setValue(false, true); + ClOpt.setValue(false, true); + ClOptimizeCallbacks.setValue(false, true); + + // no uas in cangjie + ClUseAfterScope.setValue(false, true); + Options.UseAfterScope = false; + + // cangjie has no initialize order issue + ClInitializers.setValue(false, true); + + // this will interfer cangjie stackmap + ClRealignStack.setValue(kCjRedZoneAlign, true); + ClDynamicAllocaStack.setValue(false, true); + + // disable comdat, which will overlap constructor in parallel compiling in cangjie + // refer to llvm pull request 67745 on github + ClWithComdat.setValue(false, true); + } ModuleAddressSanitizer ModuleSanitizer(M, Options.CompileKernel, Options.Recover, UseGlobalGC, UseOdrIndicator, DestructorKind); @@ -1234,7 +1278,14 @@ bool AddressSanitizer::isInterestingAlloca(const AllocaInst &AI) { // swifterror allocas are register promoted by ISel !AI.isSwiftError() && // safe allocas are not interesting - !(SSGI && SSGI->isSafe(AI))); + !(SSGI && SSGI->isSafe(AI))) && + // for cjasan, do not instrument gc ptr + // 1. ptr itself is GC ptr -> containsGCPtrType + // 2. ptr points to a memory contains GC ptr: ignore + // if ptr is point to stack, stack map is not generated for it + // if ptr is point to heap, rule 1 is covered + // 3. vector/array/struct type contains GC ptr -> containsGCPtrType + !(ClEnableCjAsan && containsGCPtrType(AI.getAllocatedType())); ProcessedAllocas[&AI] = IsInteresting; return IsInteresting; @@ -1744,6 +1795,11 @@ bool ModuleAddressSanitizer::shouldInstrumentGlobal(GlobalVariable *G) const { // For now, just ignore this Global if the alignment is large. if (G->getAlignment() > getMinRedzoneSizeForGlobal()) return false; + // just instrument user globals in cangjie + if (ClEnableCjAsan && !G->hasAttribute("address_sanitize_global")) return false; + // don't instrument GC type in cangjie + if (ClEnableCjAsan && containsGCPtrType(Ty)) return false; + // For non-COFF targets, only instrument globals known to be defined by this // TU. // FIXME: We can instrument comdat globals on ELF if we are using the @@ -2237,8 +2293,13 @@ bool ModuleAddressSanitizer::InstrumentGlobals(IRBuilder<> &IRB, Module &M, // We shouldn't merge same module names, as this string serves as unique // module ID in runtime. + auto ModuleNameStr = M.getModuleIdentifier(); + if (ClEnableCjAsan) { + // for bep integrity check, ModuleIdentifier will result in random path + ModuleNameStr = M.getSourceFileName(); + } GlobalVariable *ModuleName = createPrivateGlobalForString( - M, M.getModuleIdentifier(), /*AllowMerging*/ false, kAsanGenPrefix); + M, ModuleNameStr, /*AllowMerging*/ false, kAsanGenPrefix); for (size_t i = 0; i < n; i++) { GlobalVariable *G = GlobalsToChange[i]; @@ -2313,6 +2374,7 @@ bool ModuleAddressSanitizer::InstrumentGlobals(IRBuilder<> &IRB, Module &M, GlobalAlias::create(GlobalValue::PrivateLinkage, "", NewGlobal); } + // ODR is disabled in cangjie // ODR should not happen for local linkage. if (NewGlobal->hasLocalLinkage()) { ODRIndicator = ConstantExpr::getIntToPtr(ConstantInt::get(IntptrTy, -1), @@ -2521,8 +2583,13 @@ void AddressSanitizer::initializeCallbacks(Module &M) { IRB.getInt8PtrTy(), IRB.getInt8PtrTy(), IRB.getInt32Ty(), IntptrTy); - AsanHandleNoReturnFunc = - M.getOrInsertFunction(kAsanHandleNoReturnName, IRB.getVoidTy()); + if (ClEnableCjAsan) { + AsanHandleNoReturnFunc = + M.getOrInsertFunction(kCjAsanHandleNoReturnName, IRB.getVoidTy()); + } else { + AsanHandleNoReturnFunc = + M.getOrInsertFunction(kAsanHandleNoReturnName, IRB.getVoidTy()); + } AsanPtrCmpFunction = M.getOrInsertFunction(kAsanPtrCmp, IRB.getVoidTy(), IntptrTy, IntptrTy); @@ -2636,11 +2703,14 @@ bool AddressSanitizer::instrumentFunction(Function &F, if (maybeInsertAsanInitAtFunctionEntry(F)) FunctionModified = true; - // Leave if the function doesn't need instrumentation. - if (!F.hasFnAttribute(Attribute::SanitizeAddress)) return FunctionModified; + if (!ClEnableCjAsan) { + // Leave if the function doesn't need instrumentation. + if (!F.hasFnAttribute(Attribute::SanitizeAddress)) + return FunctionModified; - if (F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation)) - return FunctionModified; + if (F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation)) + return FunctionModified; + } LLVM_DEBUG(dbgs() << "ASAN instrumenting:\n" << F << "\n"); @@ -2673,70 +2743,81 @@ bool AddressSanitizer::instrumentFunction(Function &F, // Skip instructions inserted by another instrumentation. if (Inst.hasMetadata(LLVMContext::MD_nosanitize)) continue; - SmallVector InterestingOperands; - getInterestingMemoryOperands(&Inst, InterestingOperands); - - if (!InterestingOperands.empty()) { - for (auto &Operand : InterestingOperands) { - if (ClOpt && ClOptSameTemp) { - Value *Ptr = Operand.getPtr(); - // If we have a mask, skip instrumentation if we've already - // instrumented the full object. But don't add to TempsToInstrument - // because we might get another load/store with a different mask. - if (Operand.MaybeMask) { - if (TempsToInstrument.count(Ptr)) - continue; // We've seen this (whole) temp in the current BB. - } else { - if (!TempsToInstrument.insert(Ptr).second) - continue; // We've seen this temp in the current BB. + if (!ClEnableCjAsan) { + SmallVector InterestingOperands; + getInterestingMemoryOperands(&Inst, InterestingOperands); + + if (!InterestingOperands.empty()) { + for (auto &Operand : InterestingOperands) { + if (ClOpt && ClOptSameTemp) { + Value *Ptr = Operand.getPtr(); + // If we have a mask, skip instrumentation if we've already + // instrumented the full object. But don't add to TempsToInstrument + // because we might get another load/store with a different mask. + if (Operand.MaybeMask) { + if (TempsToInstrument.count(Ptr)) + continue; // We've seen this (whole) temp in the current BB. + } else { + if (!TempsToInstrument.insert(Ptr).second) + continue; // We've seen this temp in the current BB. + } } + OperandsToInstrument.push_back(Operand); + NumInsnsPerBB++; } - OperandsToInstrument.push_back(Operand); + } else if (((ClInvalidPointerPairs || ClInvalidPointerCmp) && + isInterestingPointerComparison(&Inst)) || + ((ClInvalidPointerPairs || ClInvalidPointerSub) && + isInterestingPointerSubtraction(&Inst))) { + PointerComparisonsOrSubtracts.push_back(&Inst); + } else if (MemIntrinsic *MI = dyn_cast(&Inst)) { + // ok, take it. + IntrinToInstrument.push_back(MI); NumInsnsPerBB++; + } else { + if (auto *CB = dyn_cast(&Inst)) { + // A call inside BB. + TempsToInstrument.clear(); + if (CB->doesNotReturn()) + NoReturnCalls.push_back(CB); + } + if (CallInst *CI = dyn_cast(&Inst)) + maybeMarkSanitizerLibraryCallNoBuiltin(CI, TLI); } - } else if (((ClInvalidPointerPairs || ClInvalidPointerCmp) && - isInterestingPointerComparison(&Inst)) || - ((ClInvalidPointerPairs || ClInvalidPointerSub) && - isInterestingPointerSubtraction(&Inst))) { - PointerComparisonsOrSubtracts.push_back(&Inst); - } else if (MemIntrinsic *MI = dyn_cast(&Inst)) { - // ok, take it. - IntrinToInstrument.push_back(MI); - NumInsnsPerBB++; + if (NumInsnsPerBB >= ClMaxInsnsToInstrumentPerBB) break; } else { if (auto *CB = dyn_cast(&Inst)) { - // A call inside BB. - TempsToInstrument.clear(); if (CB->doesNotReturn()) NoReturnCalls.push_back(CB); } if (CallInst *CI = dyn_cast(&Inst)) maybeMarkSanitizerLibraryCallNoBuiltin(CI, TLI); } - if (NumInsnsPerBB >= ClMaxInsnsToInstrumentPerBB) break; } } - bool UseCalls = (ClInstrumentationWithCallsThreshold >= 0 && - OperandsToInstrument.size() + IntrinToInstrument.size() > - (unsigned)ClInstrumentationWithCallsThreshold); - const DataLayout &DL = F.getParent()->getDataLayout(); - ObjectSizeOpts ObjSizeOpts; - ObjSizeOpts.RoundToAlign = true; - ObjectSizeOffsetVisitor ObjSizeVis(DL, TLI, F.getContext(), ObjSizeOpts); - - // Instrument. - int NumInstrumented = 0; - for (auto &Operand : OperandsToInstrument) { - if (!suppressInstrumentationSiteForDebug(NumInstrumented)) - instrumentMop(ObjSizeVis, Operand, UseCalls, - F.getParent()->getDataLayout()); - FunctionModified = true; - } - for (auto Inst : IntrinToInstrument) { - if (!suppressInstrumentationSiteForDebug(NumInstrumented)) - instrumentMemIntrinsic(Inst); - FunctionModified = true; + if (!ClEnableCjAsan) { + bool UseCalls = (ClInstrumentationWithCallsThreshold >= 0 && + OperandsToInstrument.size() + IntrinToInstrument.size() > + (unsigned)ClInstrumentationWithCallsThreshold); + const DataLayout &DL = F.getParent()->getDataLayout(); + ObjectSizeOpts ObjSizeOpts; + ObjSizeOpts.RoundToAlign = true; + ObjectSizeOffsetVisitor ObjSizeVis(DL, TLI, F.getContext(), ObjSizeOpts); + + // Instrument. + int NumInstrumented = 0; + for (auto &Operand : OperandsToInstrument) { + if (!suppressInstrumentationSiteForDebug(NumInstrumented)) + instrumentMop(ObjSizeVis, Operand, UseCalls, + F.getParent()->getDataLayout()); + FunctionModified = true; + } + for (auto Inst : IntrinToInstrument) { + if (!suppressInstrumentationSiteForDebug(NumInstrumented)) + instrumentMemIntrinsic(Inst); + FunctionModified = true; + } } FunctionStackPoisoner FSP(F, *this); @@ -2749,9 +2830,11 @@ bool AddressSanitizer::instrumentFunction(Function &F, IRB.CreateCall(AsanHandleNoReturnFunc, {}); } - for (auto Inst : PointerComparisonsOrSubtracts) { - instrumentPointerComparisonOrSubtraction(Inst); - FunctionModified = true; + if (!ClEnableCjAsan) { + for (auto Inst : PointerComparisonsOrSubtracts) { + instrumentPointerComparisonOrSubtraction(Inst); + FunctionModified = true; + } } if (ChangedStack || !NoReturnCalls.empty()) @@ -2812,6 +2895,9 @@ void FunctionStackPoisoner::initializeCallbacks(Module &M) { kAsanAllocaPoison, IRB.getVoidTy(), IntptrTy, IntptrTy); AsanAllocasUnpoisonFunc = M.getOrInsertFunction( kAsanAllocasUnpoison, IRB.getVoidTy(), IntptrTy, IntptrTy); + + Int8Ty = Type::getInt8Ty(*(ASan.C)); + Int64Ty = Type::getInt64Ty(*(ASan.C)); } void FunctionStackPoisoner::copyToShadowInline(ArrayRef ShadowMask, @@ -2977,7 +3063,11 @@ void FunctionStackPoisoner::createDynamicAllocasInitStorage() { IRBuilder<> IRB(dyn_cast(FirstBB.begin())); DynamicAllocaLayout = IRB.CreateAlloca(IntptrTy, nullptr); IRB.CreateStore(Constant::getNullValue(IntptrTy), DynamicAllocaLayout); - DynamicAllocaLayout->setAlignment(Align(32)); + if (ClEnableCjAsan) { + DynamicAllocaLayout->setAlignment(Align(kCjRedZoneAlign)); + } else { + DynamicAllocaLayout->setAlignment(Align(32)); + } } void FunctionStackPoisoner::processDynamicAllocas() { @@ -3145,7 +3235,7 @@ void FunctionStackPoisoner::processStaticAllocas() { bool DoStackMalloc = ASan.UseAfterReturn != AsanDetectStackUseAfterReturnMode::Never && !ASan.CompileKernel && LocalStackSize <= kMaxStackMallocSize; - bool DoDynamicAlloca = ClDynamicAllocaStack; + bool DoDynamicAlloca = !ClEnableCjAsan && ClDynamicAllocaStack; // Don't do dynamic alloca or stack malloc if: // 1) There is inline asm: too often it makes assumptions on which registers // are available. @@ -3366,11 +3456,16 @@ void FunctionStackPoisoner::poisonAlloca(Value *V, uint64_t Size, void FunctionStackPoisoner::handleDynamicAllocaCall(AllocaInst *AI) { IRBuilder<> IRB(AI); - const Align Alignment = std::max(Align(kAllocaRzSize), AI->getAlign()); - const uint64_t AllocaRedzoneMask = kAllocaRzSize - 1; + uint64_t AllocaRzSizeValue = kAllocaRzSize; + if (ClEnableCjAsan) { + AllocaRzSizeValue = kCjRedZoneAlign; + } + + const Align Alignment = std::max(Align(AllocaRzSizeValue), AI->getAlign()); + const uint64_t AllocaRedzoneMask = AllocaRzSizeValue - 1; Value *Zero = Constant::getNullValue(IntptrTy); - Value *AllocaRzSize = ConstantInt::get(IntptrTy, kAllocaRzSize); + Value *AllocaRzSize = ConstantInt::get(IntptrTy, AllocaRzSizeValue); Value *AllocaRzMask = ConstantInt::get(IntptrTy, AllocaRedzoneMask); // Since we need to extend alloca with additional memory to locate @@ -3397,7 +3492,7 @@ void FunctionStackPoisoner::handleDynamicAllocaCall(AllocaInst *AI) { // Alignment is added to locate left redzone, PartialPadding for possible // partial redzone and kAllocaRzSize for right redzone respectively. Value *AdditionalChunkSize = IRB.CreateAdd( - ConstantInt::get(IntptrTy, Alignment.value() + kAllocaRzSize), + ConstantInt::get(IntptrTy, Alignment.value() + AllocaRzSizeValue), PartialPadding); Value *NewSize = IRB.CreateAdd(OldSize, AdditionalChunkSize); diff --git a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp index ac4a1fd6b..f9b9bda2e 100644 --- a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp +++ b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp @@ -19,6 +19,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Sequence.h" #include "llvm/ADT/StringMap.h" +#include "llvm/ADT/Triple.h" #include "llvm/Analysis/BlockFrequencyInfo.h" #include "llvm/Analysis/BranchProbabilityInfo.h" #include "llvm/Analysis/EHPersonalities.h" @@ -30,6 +31,7 @@ #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" +#include "llvm/InitializePasses.h" #include "llvm/Support/CRC.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" @@ -111,6 +113,8 @@ public: } void writeBytes(const char *Bytes, int Size) { os->write(Bytes, Size); } + sys::path::Style getPathStyle() { return PathStyle; } + private: // Create the .gcno files for the Module based on DebugInfo. bool @@ -161,6 +165,40 @@ private: std::vector ExcludeRe; DenseSet ExecBlocks; StringMap InstrumentedFiles; + sys::path::Style PathStyle = sys::path::Style::native; +}; + +class GCOVProfilerLegacyPass : public ModulePass { +public: + static char ID; + GCOVProfilerLegacyPass() + : GCOVProfilerLegacyPass(GCOVOptions::getDefault()) {} + GCOVProfilerLegacyPass(const GCOVOptions &Opts) + : ModulePass(ID), Profiler(Opts) { + initializeGCOVProfilerLegacyPassPass(*PassRegistry::getPassRegistry()); + } + StringRef getPassName() const override { return "GCOV Profiler"; } + + bool runOnModule(Module &M) override { + auto GetBFI = [this](Function &F) { + return &this->getAnalysis(F).getBFI(); + }; + auto GetBPI = [this](Function &F) { + return &this->getAnalysis(F).getBPI(); + }; + auto GetTLI = [this](Function &F) -> const TargetLibraryInfo & { + return this->getAnalysis().getTLI(F); + }; + return Profiler.runOnModule(M, GetBFI, GetBPI, GetTLI); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addRequired(); + } + +private: + GCOVProfiler Profiler; }; struct BBInfo { @@ -198,6 +236,21 @@ struct Edge { }; } +char GCOVProfilerLegacyPass::ID = 0; +INITIALIZE_PASS_BEGIN( + GCOVProfilerLegacyPass, "insert-gcov-profiling", + "Insert instrumentation for GCOV profiling", false, false) + INITIALIZE_PASS_DEPENDENCY(BlockFrequencyInfoWrapperPass) + INITIALIZE_PASS_DEPENDENCY(BranchProbabilityInfoWrapperPass) + INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) +INITIALIZE_PASS_END( + GCOVProfilerLegacyPass, "insert-gcov-profiling", + "Insert instrumentation for GCOV profiling", false, false) + +ModulePass *llvm::createGCOVProfilerPass(const GCOVOptions &Options) { + return new GCOVProfilerLegacyPass(Options); +} + static StringRef getFunctionName(const DISubprogram *SP) { if (!SP->getLinkageName().empty()) return SP->getLinkageName(); @@ -209,13 +262,14 @@ static StringRef getFunctionName(const DISubprogram *SP) { /// Prefer relative paths in the coverage notes. Clang also may split /// up absolute paths into a directory and filename component. When /// the relative path doesn't exist, reconstruct the absolute path. -static SmallString<128> getFilename(const DISubprogram *SP) { +static SmallString<128> getFilename(const DISubprogram *SP, + sys::path::Style PathStyle) { SmallString<128> Path; StringRef RelPath = SP->getFilename(); if (sys::fs::exists(RelPath)) Path = RelPath; else - sys::path::append(Path, SP->getDirectory(), SP->getFilename()); + sys::path::append(Path, PathStyle, SP->getDirectory(), SP->getFilename()); return Path; } @@ -358,7 +412,7 @@ namespace { void writeOut(uint32_t CfgChecksum) { write(GCOV_TAG_FUNCTION); - SmallString<128> Filename = getFilename(SP); + SmallString<128> Filename = getFilename(SP, P->getPathStyle()); uint32_t BlockLen = 2 + (Version >= 47) + wordsOfString(getFunctionName(SP)); if (Version < 80) @@ -472,7 +526,7 @@ bool GCOVProfiler::isFunctionInstrumented(const Function &F) { if (FilterRe.empty() && ExcludeRe.empty()) { return true; } - SmallString<128> Filename = getFilename(F.getSubprogram()); + SmallString<128> Filename = getFilename(F.getSubprogram(), PathStyle); auto It = InstrumentedFiles.find(Filename); if (It != InstrumentedFiles.end()) { return It->second; @@ -564,6 +618,10 @@ bool GCOVProfiler::runOnModule( FilterRe = createRegexesFromString(Options.Filter); ExcludeRe = createRegexesFromString(Options.Exclude); + Triple T(M.getTargetTriple()); + if (T.isOSWindows()) { + PathStyle = sys::path::Style::windows; + } emitProfileNotes(CUNode, HasExecOrFork, GetBFI, GetBPI, this->GetTLI); return true; } @@ -801,7 +859,10 @@ bool GCOVProfiler::emitProfileNotes( // Add the function line number to the lines of the entry block // to have a counter for the function definition. uint32_t Line = SP->getLine(); - auto Filename = getFilename(SP); + auto Filename = getFilename(SP, PathStyle); + if (Filename == "./") { + continue; + } BranchProbabilityInfo *BPI = GetBPI(F); BlockFrequencyInfo *BFI = GetBFI(F); @@ -1120,9 +1181,11 @@ Function *GCOVProfiler::insertCounterWriteout( std::string FilenameGcda = mangleName(CU, GCovFileType::GCDA); uint32_t CfgChecksum = FileChecksums.empty() ? 0 : FileChecksums[i]; + Constant *GcdaStringPtr = + Builder.CreateGlobalStringPtr(FilenameGcda, "__gcov_gcda"); auto *StartFileCallArgs = ConstantStruct::get( StartFileCallArgsTy, - {Builder.CreateGlobalStringPtr(FilenameGcda), + {GcdaStringPtr, Builder.getInt32(endian::read32be(Options.Version)), Builder.getInt32(CfgChecksum)}); diff --git a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp index b01c74320..3ab83b04b 100644 --- a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp @@ -42,6 +42,7 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/IR/MDBuilder.h" #include "llvm/IR/Module.h" +#include "llvm/IR/SafepointIRVerifier.h" #include "llvm/IR/Type.h" #include "llvm/IR/Value.h" #include "llvm/Support/Casting.h" @@ -75,6 +76,10 @@ static const uint64_t kDynamicShadowSentinel = static const unsigned kShadowBaseAlignment = 32; +static cl::opt ClEnableCjHwAsan( + "cj-hwasan", cl::desc("Enable Cangjie Hwasan Support"), + cl::Hidden, cl::init(false)); + static cl::opt ClMemoryAccessCallbackPrefix("hwasan-memory-access-callback-prefix", cl::desc("Prefix for memory access callbacks"), @@ -418,6 +423,20 @@ PreservedAnalyses HWAddressSanitizerPass::run(Module &M, if (shouldUseStackSafetyAnalysis(TargetTriple, Options.DisableOptimization)) SSI = &MAM.getResult(M); + // on cangjie, disable the followings + if (ClEnableCjHwAsan) { + Options.CompileKernel = false; + Options.Recover = false; + ClInstrumentWithCalls.setValue(false, true); + ClInstrumentReads.setValue(false, true); + ClInstrumentWrites.setValue(false, true); + ClInstrumentAtomics.setValue(false, true); + ClInstrumentByval.setValue(false, true); + ClEnableKhwasan.setValue(false, true); + ClInstrumentMemIntrinsics.setValue(false, true); + ClUseShortGranules.setValue(false, true); + } + HWAddressSanitizer HWASan(M, Options.CompileKernel, Options.Recover, SSI); bool Modified = false; auto &FAM = MAM.getResult(M).getManager(); @@ -670,9 +689,13 @@ void HWAddressSanitizer::initializeCallbacks(Module &M) { HWAsanMemset = M.getOrInsertFunction(MemIntrinCallbackPrefix + "memset", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(), IRB.getInt32Ty(), IntptrTy); - - HWAsanHandleVfork = - M.getOrInsertFunction("__hwasan_handle_vfork", IRB.getVoidTy(), IntptrTy); + if (ClEnableCjHwAsan) { + HWAsanHandleVfork = + M.getOrInsertFunction("CJ_MCC_AsanHandleNoReturn", IRB.getVoidTy(), IntptrTy); + } else { + HWAsanHandleVfork = + M.getOrInsertFunction("__hwasan_handle_vfork", IRB.getVoidTy(), IntptrTy); + } } Value *HWAddressSanitizer::getOpaqueNoopCast(IRBuilder<> &IRB, Value *Val) { @@ -1405,7 +1428,9 @@ bool HWAddressSanitizer::isInterestingAlloca(const AllocaInst &AI) { // swifterror allocas are register promoted by ISel !AI.isSwiftError()) && // safe allocas are not interesting - !(SSI && SSI->isSafe(AI)); + !(SSI && SSI->isSafe(AI)) && + // for cjasan, do not instrument gc ptr + !(ClEnableCjHwAsan && containsGCPtrType(AI.getAllocatedType())); } bool HWAddressSanitizer::sanitizeFunction(Function &F, @@ -1413,8 +1438,13 @@ bool HWAddressSanitizer::sanitizeFunction(Function &F, if (&F == HwasanCtorFunction) return false; - if (!F.hasFnAttribute(Attribute::SanitizeHWAddress)) - return false; + if (ClEnableCjHwAsan) { + if (!F.hasFnAttribute("address_sanitize_stack")) + return false; + } else { + if (!F.hasFnAttribute(Attribute::SanitizeHWAddress)) + return false; + } LLVM_DEBUG(dbgs() << "Function: " << F.getName() << "\n"); @@ -1432,11 +1462,13 @@ bool HWAddressSanitizer::sanitizeFunction(Function &F, if (InstrumentLandingPads && isa(Inst)) LandingPadVec.push_back(&Inst); - getInterestingMemoryOperands(&Inst, OperandsToInstrument); + if (!ClEnableCjHwAsan) { + getInterestingMemoryOperands(&Inst, OperandsToInstrument); - if (MemIntrinsic *MI = dyn_cast(&Inst)) - if (!ignoreMemIntrinsic(MI)) - IntrinToInstrument.push_back(MI); + if (MemIntrinsic *MI = dyn_cast(&Inst)) + if (!ignoreMemIntrinsic(MI)) + IntrinToInstrument.push_back(MI); + } } memtag::StackInfo &SInfo = SIB.get(); @@ -1491,12 +1523,14 @@ bool HWAddressSanitizer::sanitizeFunction(Function &F, } } - for (auto &Operand : OperandsToInstrument) - instrumentMemAccess(Operand); + if (!ClEnableCjHwAsan) { + for (auto &Operand : OperandsToInstrument) + instrumentMemAccess(Operand); - if (ClInstrumentMemIntrinsics && !IntrinToInstrument.empty()) { - for (auto Inst : IntrinToInstrument) - instrumentMemIntrinsic(cast(Inst)); + if (ClInstrumentMemIntrinsics && !IntrinToInstrument.empty()) { + for (auto Inst : IntrinToInstrument) + instrumentMemIntrinsic(cast(Inst)); + } } ShadowBase = nullptr; @@ -1603,6 +1637,14 @@ void HWAddressSanitizer::instrumentGlobals() { if (GV.hasSection()) continue; + // only sanitize cangjie variables + if (ClEnableCjHwAsan && !GV.hasAttribute("address_sanitize_global")) + continue; + + // Globals which have GC type cannot be instrumented + if (ClEnableCjHwAsan && containsGCPtrType(GV.getInitializer()->getType())) + continue; + Globals.push_back(&GV); } diff --git a/llvm/lib/Transforms/Instrumentation/Instrumentation.cpp b/llvm/lib/Transforms/Instrumentation/Instrumentation.cpp index bd575b6cf..6c7bd4db5 100644 --- a/llvm/lib/Transforms/Instrumentation/Instrumentation.cpp +++ b/llvm/lib/Transforms/Instrumentation/Instrumentation.cpp @@ -94,6 +94,7 @@ void llvm::initializeInstrumentation(PassRegistry &Registry) { initializeMemProfilerLegacyPassPass(Registry); initializeModuleMemProfilerLegacyPassPass(Registry); initializeBoundsCheckingLegacyPassPass(Registry); + initializeGCOVProfilerLegacyPassPass(Registry); initializeDataFlowSanitizerLegacyPassPass(Registry); } diff --git a/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp index d4aa31db8..3a3e67aa3 100644 --- a/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp @@ -31,12 +31,14 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstIterator.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" +#include "llvm/IR/SafepointIRVerifier.h" #include "llvm/IR/Type.h" #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/CommandLine.h" @@ -53,6 +55,9 @@ using namespace llvm; #define DEBUG_TYPE "tsan" +static cl::opt ClEnableCjTsan( + "cj-tsan", cl::desc("Enable Cangjie Tsan Support"), + cl::Hidden, cl::init(false)); static cl::opt ClInstrumentMemoryAccesses( "tsan-instrument-memory-accesses", cl::init(true), cl::desc("Instrument memory accesses"), cl::Hidden); @@ -134,9 +139,17 @@ private: }; void initialize(Module &M); + void initializeCJFuncs(Module &M); + Value *findSourceValue(Value *V); + bool isCangjieObject(Value *V, Value **SV); + bool isRaceableObject(Value *V); bool instrumentLoadOrStore(const InstructionInfo &II, const DataLayout &DL); + bool instrumentCJLoadOrStore(const InstructionInfo &II, + const DataLayout &DL); bool instrumentAtomic(Instruction *I, const DataLayout &DL); + bool instrumentCJAtomic(Instruction *I, const DataLayout &DL); bool instrumentMemIntrinsic(Instruction *I); + bool instrumentCJMemIntrinsic(Instruction *I); void chooseInstructionsToInstrument(SmallVectorImpl &Local, SmallVectorImpl &All, const DataLayout &DL); @@ -151,6 +164,10 @@ private: FunctionCallee TsanIgnoreEnd; // Accesses sizes are powers of two: 1, 2, 4, 8, 16. static const size_t kNumberOfAccessSizes = 5; + FunctionCallee TsanCJRead; + FunctionCallee TsanCJWrite; + FunctionCallee TsanCJReadRange; + FunctionCallee TsanCJWriteRange; FunctionCallee TsanRead[kNumberOfAccessSizes]; FunctionCallee TsanWrite[kNumberOfAccessSizes]; FunctionCallee TsanUnalignedRead[kNumberOfAccessSizes]; @@ -196,6 +213,7 @@ PreservedAnalyses ModuleThreadSanitizerPass::run(Module &M, insertModuleCtor(M); return PreservedAnalyses::none(); } + void ThreadSanitizer::initialize(Module &M) { const DataLayout &DL = M.getDataLayout(); IntptrTy = DL.getIntPtrType(M.getContext()); @@ -208,59 +226,62 @@ void ThreadSanitizer::initialize(Module &M) { IRB.getVoidTy(), IRB.getInt8PtrTy()); TsanFuncExit = M.getOrInsertFunction("__tsan_func_exit", Attr, IRB.getVoidTy()); - TsanIgnoreBegin = M.getOrInsertFunction("__tsan_ignore_thread_begin", Attr, - IRB.getVoidTy()); - TsanIgnoreEnd = - M.getOrInsertFunction("__tsan_ignore_thread_end", Attr, IRB.getVoidTy()); + if (!ClEnableCjTsan) { + TsanIgnoreBegin = M.getOrInsertFunction("__tsan_ignore_thread_begin", Attr, + IRB.getVoidTy()); + TsanIgnoreEnd = + M.getOrInsertFunction("__tsan_ignore_thread_end", Attr, IRB.getVoidTy()); + } IntegerType *OrdTy = IRB.getInt32Ty(); for (size_t i = 0; i < kNumberOfAccessSizes; ++i) { const unsigned ByteSize = 1U << i; const unsigned BitSize = ByteSize * 8; std::string ByteSizeStr = utostr(ByteSize); std::string BitSizeStr = utostr(BitSize); - SmallString<32> ReadName("__tsan_read" + ByteSizeStr); - TsanRead[i] = M.getOrInsertFunction(ReadName, Attr, IRB.getVoidTy(), - IRB.getInt8PtrTy()); - - SmallString<32> WriteName("__tsan_write" + ByteSizeStr); - TsanWrite[i] = M.getOrInsertFunction(WriteName, Attr, IRB.getVoidTy(), - IRB.getInt8PtrTy()); - - SmallString<64> UnalignedReadName("__tsan_unaligned_read" + ByteSizeStr); - TsanUnalignedRead[i] = M.getOrInsertFunction( - UnalignedReadName, Attr, IRB.getVoidTy(), IRB.getInt8PtrTy()); - - SmallString<64> UnalignedWriteName("__tsan_unaligned_write" + ByteSizeStr); - TsanUnalignedWrite[i] = M.getOrInsertFunction( - UnalignedWriteName, Attr, IRB.getVoidTy(), IRB.getInt8PtrTy()); - - SmallString<64> VolatileReadName("__tsan_volatile_read" + ByteSizeStr); - TsanVolatileRead[i] = M.getOrInsertFunction( - VolatileReadName, Attr, IRB.getVoidTy(), IRB.getInt8PtrTy()); - - SmallString<64> VolatileWriteName("__tsan_volatile_write" + ByteSizeStr); - TsanVolatileWrite[i] = M.getOrInsertFunction( - VolatileWriteName, Attr, IRB.getVoidTy(), IRB.getInt8PtrTy()); - - SmallString<64> UnalignedVolatileReadName("__tsan_unaligned_volatile_read" + - ByteSizeStr); - TsanUnalignedVolatileRead[i] = M.getOrInsertFunction( - UnalignedVolatileReadName, Attr, IRB.getVoidTy(), IRB.getInt8PtrTy()); - - SmallString<64> UnalignedVolatileWriteName( - "__tsan_unaligned_volatile_write" + ByteSizeStr); - TsanUnalignedVolatileWrite[i] = M.getOrInsertFunction( - UnalignedVolatileWriteName, Attr, IRB.getVoidTy(), IRB.getInt8PtrTy()); - - SmallString<64> CompoundRWName("__tsan_read_write" + ByteSizeStr); - TsanCompoundRW[i] = M.getOrInsertFunction( - CompoundRWName, Attr, IRB.getVoidTy(), IRB.getInt8PtrTy()); - - SmallString<64> UnalignedCompoundRWName("__tsan_unaligned_read_write" + - ByteSizeStr); - TsanUnalignedCompoundRW[i] = M.getOrInsertFunction( - UnalignedCompoundRWName, Attr, IRB.getVoidTy(), IRB.getInt8PtrTy()); - + if (!ClEnableCjTsan) { + SmallString<32> ReadName("__tsan_read" + ByteSizeStr); + TsanRead[i] = M.getOrInsertFunction(ReadName, Attr, IRB.getVoidTy(), + IRB.getInt8PtrTy()); + + SmallString<32> WriteName("__tsan_write" + ByteSizeStr); + TsanWrite[i] = M.getOrInsertFunction(WriteName, Attr, IRB.getVoidTy(), + IRB.getInt8PtrTy()); + + SmallString<64> UnalignedReadName("__tsan_unaligned_read" + ByteSizeStr); + TsanUnalignedRead[i] = M.getOrInsertFunction( + UnalignedReadName, Attr, IRB.getVoidTy(), IRB.getInt8PtrTy()); + + SmallString<64> UnalignedWriteName("__tsan_unaligned_write" + ByteSizeStr); + TsanUnalignedWrite[i] = M.getOrInsertFunction( + UnalignedWriteName, Attr, IRB.getVoidTy(), IRB.getInt8PtrTy()); + + SmallString<64> VolatileReadName("__tsan_volatile_read" + ByteSizeStr); + TsanVolatileRead[i] = M.getOrInsertFunction( + VolatileReadName, Attr, IRB.getVoidTy(), IRB.getInt8PtrTy()); + + SmallString<64> VolatileWriteName("__tsan_volatile_write" + ByteSizeStr); + TsanVolatileWrite[i] = M.getOrInsertFunction( + VolatileWriteName, Attr, IRB.getVoidTy(), IRB.getInt8PtrTy()); + + SmallString<64> UnalignedVolatileReadName("__tsan_unaligned_volatile_read" + + ByteSizeStr); + TsanUnalignedVolatileRead[i] = M.getOrInsertFunction( + UnalignedVolatileReadName, Attr, IRB.getVoidTy(), IRB.getInt8PtrTy()); + + SmallString<64> UnalignedVolatileWriteName( + "__tsan_unaligned_volatile_write" + ByteSizeStr); + TsanUnalignedVolatileWrite[i] = M.getOrInsertFunction( + UnalignedVolatileWriteName, Attr, IRB.getVoidTy(), IRB.getInt8PtrTy()); + + SmallString<64> CompoundRWName("__tsan_read_write" + ByteSizeStr); + TsanCompoundRW[i] = M.getOrInsertFunction( + CompoundRWName, Attr, IRB.getVoidTy(), IRB.getInt8PtrTy()); + + SmallString<64> UnalignedCompoundRWName("__tsan_unaligned_read_write" + + ByteSizeStr); + TsanUnalignedCompoundRW[i] = M.getOrInsertFunction( + UnalignedCompoundRWName, Attr, IRB.getVoidTy(), IRB.getInt8PtrTy()); + } Type *Ty = Type::getIntNTy(M.getContext(), BitSize); Type *PtrTy = Ty->getPointerTo(); SmallString<32> AtomicLoadName("__tsan_atomic" + BitSizeStr + "_load"); @@ -322,11 +343,13 @@ void ThreadSanitizer::initialize(Module &M) { Ty, OrdTy, OrdTy); } } - TsanVptrUpdate = - M.getOrInsertFunction("__tsan_vptr_update", Attr, IRB.getVoidTy(), - IRB.getInt8PtrTy(), IRB.getInt8PtrTy()); - TsanVptrLoad = M.getOrInsertFunction("__tsan_vptr_read", Attr, - IRB.getVoidTy(), IRB.getInt8PtrTy()); + if (!ClEnableCjTsan) { + TsanVptrUpdate = + M.getOrInsertFunction("__tsan_vptr_update", Attr, IRB.getVoidTy(), + IRB.getInt8PtrTy(), IRB.getInt8PtrTy()); + TsanVptrLoad = M.getOrInsertFunction("__tsan_vptr_read", Attr, + IRB.getVoidTy(), IRB.getInt8PtrTy()); + } { AttributeList AL = Attr; AL = AL.addParamAttribute(M.getContext(), 0, Attribute::ZExt); @@ -339,16 +362,34 @@ void ThreadSanitizer::initialize(Module &M) { TsanAtomicSignalFence = M.getOrInsertFunction("__tsan_atomic_signal_fence", AL, IRB.getVoidTy(), OrdTy); } + if (!ClEnableCjTsan) { + MemmoveFn = + M.getOrInsertFunction("memmove", Attr, IRB.getInt8PtrTy(), + IRB.getInt8PtrTy(), IRB.getInt8PtrTy(), IntptrTy); + MemcpyFn = + M.getOrInsertFunction("memcpy", Attr, IRB.getInt8PtrTy(), + IRB.getInt8PtrTy(), IRB.getInt8PtrTy(), IntptrTy); + MemsetFn = + M.getOrInsertFunction("memset", Attr, IRB.getInt8PtrTy(), + IRB.getInt8PtrTy(), IRB.getInt32Ty(), IntptrTy); + } +} + +void ThreadSanitizer::initializeCJFuncs(Module &M) { + const DataLayout &DL = M.getDataLayout(); + IntptrTy = DL.getIntPtrType(M.getContext()); + IRBuilder<> IRB(M.getContext()); + AttributeList Attr = AttributeList(); + Attr = Attr.addFnAttribute(M.getContext(), Attribute::NoUnwind); - MemmoveFn = - M.getOrInsertFunction("memmove", Attr, IRB.getInt8PtrTy(), - IRB.getInt8PtrTy(), IRB.getInt8PtrTy(), IntptrTy); - MemcpyFn = - M.getOrInsertFunction("memcpy", Attr, IRB.getInt8PtrTy(), - IRB.getInt8PtrTy(), IRB.getInt8PtrTy(), IntptrTy); - MemsetFn = - M.getOrInsertFunction("memset", Attr, IRB.getInt8PtrTy(), - IRB.getInt8PtrTy(), IRB.getInt32Ty(), IntptrTy); + TsanCJWrite = M.getOrInsertFunction("CJ_MCC_TsanWriteMemory", Attr, IRB.getVoidTy(), + IRB.getInt8PtrTy(), IRB.getInt64Ty()); + TsanCJRead = M.getOrInsertFunction("CJ_MCC_TsanReadMemory", Attr, IRB.getVoidTy(), + IRB.getInt8PtrTy(), IRB.getInt64Ty()); + TsanCJWriteRange = M.getOrInsertFunction("CJ_MCC_TsanWriteMemoryRange", Attr, IRB.getVoidTy(), + IRB.getInt8PtrTy(), IRB.getInt64Ty()); + TsanCJReadRange = M.getOrInsertFunction("CJ_MCC_TsanReadMemoryRange", Attr, IRB.getVoidTy(), + IRB.getInt8PtrTy(), IRB.getInt64Ty()); } static bool isVtableAccess(Instruction *I) { @@ -433,7 +474,7 @@ void ThreadSanitizer::chooseInstructionsToInstrument( Value *Addr = IsWrite ? cast(I)->getPointerOperand() : cast(I)->getPointerOperand(); - if (!shouldInstrumentReadWriteFromAddress(I->getModule(), Addr)) + if (!ClEnableCjTsan && !shouldInstrumentReadWriteFromAddress(I->getModule(), Addr)) continue; if (!IsWrite) { @@ -480,6 +521,15 @@ void ThreadSanitizer::chooseInstructionsToInstrument( Local.clear(); } +static bool isCJAtomic(const Instruction *I) { + const IntrinsicInst *II = dyn_cast(I); + if (II == nullptr) { + return false; + } + auto IID = II->getIntrinsicID(); + return isCJAtomicIntrinsic(IID); +} + static bool isTsanAtomic(const Instruction *I) { // TODO: Ask TTI whether synchronization scope is between threads. auto SSID = getAtomicSyncScopeID(I); @@ -517,21 +567,30 @@ bool ThreadSanitizer::sanitizeFunction(Function &F, if (F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation)) return false; + if (ClEnableCjTsan) { + initializeCJFuncs(*F.getParent()); + } initialize(*F.getParent()); SmallVector AllLoadsAndStores; SmallVector LocalLoadsAndStores; SmallVector AtomicAccesses; + SmallVector CJAtomicAccesses; SmallVector MemIntrinCalls; bool Res = false; bool HasCalls = false; bool SanitizeFunction = F.hasFnAttribute(Attribute::SanitizeThread); + if (ClEnableCjTsan) { + SanitizeFunction = true; + ClHandleCxxExceptions = false; + } const DataLayout &DL = F.getParent()->getDataLayout(); - // Traverse all instructions, collect loads/stores/returns, check for calls. for (auto &BB : F) { for (auto &Inst : BB) { if (isTsanAtomic(&Inst)) AtomicAccesses.push_back(&Inst); + else if (ClInstrumentAtomics && isCJAtomic(&Inst)) + CJAtomicAccesses.push_back(&Inst); else if (isa(Inst) || isa(Inst)) LocalLoadsAndStores.push_back(&Inst); else if ((isa(Inst) && !isa(Inst)) || @@ -555,19 +614,30 @@ bool ThreadSanitizer::sanitizeFunction(Function &F, // Instrument memory accesses only if we want to report bugs in the function. if (ClInstrumentMemoryAccesses && SanitizeFunction) for (const auto &II : AllLoadsAndStores) { - Res |= instrumentLoadOrStore(II, DL); + if (ClEnableCjTsan) + Res |= instrumentCJLoadOrStore(II, DL); + else + Res |= instrumentLoadOrStore(II, DL); } - // Instrument atomic memory accesses in any case (they can be used to // implement synchronization). - if (ClInstrumentAtomics) + if (ClInstrumentAtomics) { for (auto Inst : AtomicAccesses) { Res |= instrumentAtomic(Inst, DL); } + if (ClInstrumentAtomics) { + for (auto Inst : CJAtomicAccesses) { + Res |= instrumentCJAtomic(Inst, DL); + } + } + } if (ClInstrumentMemIntrinsics && SanitizeFunction) for (auto Inst : MemIntrinCalls) { - Res |= instrumentMemIntrinsic(Inst); + if (ClEnableCjTsan) + Res |= instrumentCJMemIntrinsic(Inst); + else + Res |= instrumentMemIntrinsic(Inst); } if (F.hasFnAttribute("sanitize_thread_no_checking_at_run_time")) { @@ -594,6 +664,58 @@ bool ThreadSanitizer::sanitizeFunction(Function &F, return Res; } +bool ThreadSanitizer::isCangjieObject(Value *V, Value **SV) { + while (true) { + *SV = V; + const Instruction *I = dyn_cast(V); + if (!I) { + return false; + } + if (I->getNumOperands() == 0) { + return false; + } + if (isa(I)) { + Type *Ty = cast(I)->getPointerOperandType(); + auto *PT = dyn_cast(Ty); + return (PT && PT->getAddressSpace() == 1); + } + if (isa(I) || isa(I) || isa(I) || + isa(I) || isa(I)) { + V = I->getOperand(0); + } else { + return false; + } + } + return false; +} + +bool ThreadSanitizer::isRaceableObject(Value *V) { + Value *SV = nullptr; + bool ret = isCangjieObject(V, &SV); + return ret || isa(SV) || isa(SV); +} + +bool ThreadSanitizer::instrumentCJLoadOrStore(const InstructionInfo &II, + const DataLayout &DL) { + InstrumentationIRBuilder IRB(II.Inst); + const bool IsWrite = isa(*II.Inst); + Value *Addr = IsWrite ? cast(II.Inst)->getPointerOperand() + : cast(II.Inst)->getPointerOperand(); + FunctionCallee OnAccessFunc = IsWrite ? TsanCJWrite : TsanCJRead; + Type *OrigTy = getLoadStoreType(II.Inst); + + if (Addr->isSwiftError() || !isRaceableObject(IsWrite ? cast(II.Inst)->getPointerOperand() + : cast(II.Inst)->getPointerOperand())) + return false; + + uint32_t TypeSize = DL.getTypeStoreSizeInBits(OrigTy) / 8; + + IRB.CreateCall(OnAccessFunc, + {IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()), + ConstantInt::get(IRB.getInt64Ty(), TypeSize)}); + return true; +} + bool ThreadSanitizer::instrumentLoadOrStore(const InstructionInfo &II, const DataLayout &DL) { InstrumentationIRBuilder IRB(II.Inst); @@ -716,6 +838,31 @@ bool ThreadSanitizer::instrumentMemIntrinsic(Instruction *I) { return false; } +bool ThreadSanitizer::instrumentCJMemIntrinsic(Instruction *I) { + IRBuilder<> IRB(I); + Value *WriteAddr = nullptr; + Value *ReadAddr = nullptr; + Value *Size = nullptr; + constexpr int SizeArgIndex = 2; + if (MemSetInst *M = dyn_cast(I)) { + WriteAddr = M->getArgOperand(0), IRB.getInt8PtrTy(); + Size = IRB.CreateIntCast(M->getArgOperand(SizeArgIndex), IntptrTy, false); + } else if (MemTransferInst *M = dyn_cast(I)) { + WriteAddr = M->getArgOperand(0), IRB.getInt8PtrTy(); + ReadAddr = M->getArgOperand(1), IRB.getInt8PtrTy(); + Size = IRB.CreateIntCast(M->getArgOperand(SizeArgIndex), IntptrTy, false); + } + if (WriteAddr && isRaceableObject(WriteAddr)) { + WriteAddr = IRB.CreatePointerCast(WriteAddr, IRB.getInt8PtrTy()); + IRB.CreateCall(TsanCJWriteRange, {WriteAddr, Size}); + } + if (ReadAddr && isRaceableObject(ReadAddr)) { + ReadAddr = IRB.CreatePointerCast(ReadAddr, IRB.getInt8PtrTy()); + IRB.CreateCall(TsanCJReadRange, {ReadAddr, Size}); + } + return true; +} + // Both llvm and ThreadSanitizer atomic operations are based on C++11/C1x // standards. For background see C++11 standard. A slightly older, publicly // available draft of the standard (not entirely up-to-date, but close enough @@ -818,6 +965,62 @@ bool ThreadSanitizer::instrumentAtomic(Instruction *I, const DataLayout &DL) { return true; } +bool ThreadSanitizer::instrumentCJAtomic(Instruction *I, const DataLayout &DL) { + InstrumentationIRBuilder IRB(I); + auto ID = dyn_cast(I)->getIntrinsicID(); + const CallInst* CI = dyn_cast(I); + + if (ID == Intrinsic::cj_atomic_load) { + Value *Addr = CI->getArgOperand(1); + Type *Ty = CI->getType(); + int Idx = getMemoryAccessFuncIndex(Ty, Addr, DL); + Type *PtrTy = Type::getIntNPtrTy(I->getModule()->getContext(), DL.getTypeStoreSizeInBits(Ty)); + Value *Args[] = {IRB.CreatePointerCast(Addr, PtrTy), CI->getArgOperand(2)}; + + Value *C = IRB.CreateCall(TsanAtomicLoad[Idx], Args); + Value *Cast = IRB.CreateBitOrPointerCast(C, Ty); + I->replaceAllUsesWith(Cast); + } else if (ID == Intrinsic::cj_atomic_store || ID == Intrinsic::cj_atomic_swap) { + Value *Addr = CI->getArgOperand(2); + Value *Val = CI->getArgOperand(0); + int Idx = getMemoryAccessFuncIndex(Val->getType(), Addr, DL); + uint32_t TypeSize = DL.getTypeStoreSizeInBits(Val->getType()); + Type *PtrTy = Type::getIntNPtrTy(I->getModule()->getContext(), TypeSize); + Value *Args[] = {IRB.CreatePointerCast(Addr, PtrTy), + IRB.CreateBitOrPointerCast(Val, Type::getIntNTy(IRB.getContext(), TypeSize)), + CI->getArgOperand(3)}; + + if (ID == Intrinsic::cj_atomic_store) { + CallInst *C = CallInst::Create(TsanAtomicStore[Idx], Args); + ReplaceInstWithInst(I, C); + } else { + Value *C = IRB.CreateCall(TsanAtomicRMW[AtomicRMWInst::Xchg][Idx], Args); + Value *Cast = IRB.CreateBitOrPointerCast(C, Val->getType()); + I->replaceAllUsesWith(Cast); + I->eraseFromParent(); + } + } else if (ID == Intrinsic::cj_atomic_compare_swap) { + Value *Addr = CI->getArgOperand(3); + Value *CmpVal = CI->getArgOperand(0); + Value *NewVal = CI->getArgOperand(1); + int Idx = getMemoryAccessFuncIndex(NewVal->getType(), Addr, DL); + uint32_t TypeSize = DL.getTypeStoreSizeInBits(NewVal->getType()); + Type *Ty = Type::getIntNTy(IRB.getContext(), TypeSize); + Type *PtrTy = Type::getIntNPtrTy(I->getModule()->getContext(), TypeSize); + Value *Args[] = {IRB.CreatePointerCast(Addr, PtrTy), IRB.CreateBitOrPointerCast(CmpVal, Ty), + IRB.CreateBitOrPointerCast(NewVal, Ty), CI->getArgOperand(4), CI->getArgOperand(5)}; + + CallInst *C = IRB.CreateCall(TsanAtomicCAS[Idx], Args); + Value *Success = IRB.CreateICmpEQ(C, IRB.CreateBitOrPointerCast(CmpVal, Ty)); + StructType *ST = StructType::get(IRB.getContext(), {Ty, IRB.getInt1Ty()}); + Value *Res = IRB.CreateInsertValue(UndefValue::get(ST), C, 0); + Res = IRB.CreateInsertValue(Res, Success, 1); + ExtractValueInst *EI = ExtractValueInst::Create(Res, 1); + ReplaceInstWithInst(I, EI); + } + return true; +} + int ThreadSanitizer::getMemoryAccessFuncIndex(Type *OrigTy, Value *Addr, const DataLayout &DL) { assert(OrigTy->isSized()); diff --git a/llvm/lib/Transforms/Obfuscator/CMakeLists.txt b/llvm/lib/Transforms/Obfuscator/CMakeLists.txt new file mode 100644 index 000000000..c535daea7 --- /dev/null +++ b/llvm/lib/Transforms/Obfuscator/CMakeLists.txt @@ -0,0 +1,19 @@ +include_directories(${CANGJIE_DEMANGLE_DIR}) +add_llvm_component_library(LLVMCjcObf + ControlFlowObfuscator.cpp + DataObfuscator.cpp + LayoutObfuscator.cpp + + ADDITIONAL_HEADER_DIRS + ${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms + ${CANGJIE_DEMANGLE_DIR} + + DEPENDS + intrinsics_gen + + LINK_COMPONENTS + Core + Support + Analysis + TransformUtils + ) diff --git a/llvm/lib/Transforms/Obfuscator/ControlFlowObfuscator.cpp b/llvm/lib/Transforms/Obfuscator/ControlFlowObfuscator.cpp new file mode 100644 index 000000000..dd76e654b --- /dev/null +++ b/llvm/lib/Transforms/Obfuscator/ControlFlowObfuscator.cpp @@ -0,0 +1,1068 @@ +//===--------------------- ControlFlowObfuscator.cpp ----------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// The control-flow obfuscation pass. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Statepoint.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/TypeFinder.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/PassRegistry.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/Local.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" +#include "llvm/Transforms/Utils/ValueMapper.h" +#include "llvm/Transforms/Obfuscator/ControlFlowObfuscator.h" +#include + +using namespace llvm; + +extern cl::opt EnableConfigFlatten; +extern cl::opt EnableConfigBcf; + +namespace { +const int SWITCH_NUM = 2; +const int MAX_EXPRESS_VALUE = 1024; +const int PRIME_NUM = 7; + +const int MINI_BLOCK_NUM = 5; +const int MAX_BLOCK_NUM = 1000; +const int MAP_CHANGE = 93; +const int MAP_PRIME = 97; +const int MAP_OFFSET = 16; +const int TYPE1 = 1; +const int TYPE32 = 32; +const int TWO_SUCC = 2; + +const int BINARY_OBF_SWITCH_NUM = 4; +const int BINARY_OBF_CASE_0 = 0; +const int BINARY_OBF_CASE_1 = 1; +const int BINARY_OBF_CASE_2 = 2; +const int BINARY_OBF_CASE_3 = 3; + +const int FLOAT_OBF_SWITCH_NUM = 3; +const int FLOAT_OBF_CASE_0 = 0; +const int FLOAT_OBF_CASE_1 = 1; +const int FLOAT_OBF_CASE_2 = 2; + +const int BCMP_OBF_SWITCH_NUM = 3; +const int BCMP_OBF_CASE_0 = 0; +const int BCMP_OBF_CASE_1 = 1; +const int BCMP_OBF_CASE_2 = 2; + +const int FCMP_OBF_SWITCH_NUM = 3; +const int FCMP_OBF_CASE_0 = 0; +const int FCMP_OBF_CASE_1 = 1; +const int FCMP_OBF_CASE_2 = 2; +} // end anonymous namespace + +ControlFlowObfuscator::~ControlFlowObfuscator() { clearObfData(); } + +unsigned int ControlFlowObfuscator::getRandNum() const { + return this->ObfConfig->getRandNum(); +} + +void ControlFlowObfuscator::renameOperand(ValueToValueMapTy &TV, + BasicBlock &TB) { + Value *V = nullptr; + for (auto Iter = TB.begin(); Iter != TB.end(); Iter++) { + for (User::op_iterator OP = Iter->op_begin(); OP != Iter->op_end(); OP++) { + V = MapValue(*OP, TV, RF_None, 0); + if (V == nullptr) { + continue; + } + *OP = V; + } + PHINode *PN = dyn_cast(Iter); + if (PN == nullptr) { + continue; + } + for (uint32_t Index = 0; Index < PN->getNumIncomingValues(); Index++) { + V = MapValue(PN->getIncomingBlock(Index), TV, RF_None, 0); + if (V == nullptr) { + continue; + } + BasicBlock *TB = dyn_cast(V); + if (TB == nullptr) { + continue; + } + PN->setIncomingBlock(Index, TB); + } + } +} + +bool ControlFlowObfuscator::isBinaryOpcode(unsigned int OP) const { + // Don't handle div instructions: SDiv, UDiv, SRem and URem + return (OP == Instruction::Add || OP == Instruction::Sub || + OP == Instruction::Mul || OP == Instruction::Shl || + OP == Instruction::LShr || OP == Instruction::AShr || + OP == Instruction::And || OP == Instruction::Or || + OP == Instruction::Xor); +} + +bool ControlFlowObfuscator::isFloatOpcode(unsigned int OP) const { + // Don't handle div instructions: FDiv and FRem + return (OP == Instruction::FAdd || OP == Instruction::FSub || + OP == Instruction::FMul); +} + +void ControlFlowObfuscator::handleBinaryInstruction(Instruction &I) { + int N = (getRandNum() % SWITCH_NUM) + 1; + int Index = 0; + BinaryOperator *OP = nullptr; + BinaryOperator *OP1 = nullptr; + (void) OP; + (void) OP1; + + do { + switch (getRandNum() % BINARY_OBF_SWITCH_NUM) { + case BINARY_OBF_CASE_0: + break; + case BINARY_OBF_CASE_1: + OP = BinaryOperator::CreateNeg(I.getOperand(0), "", &I); + OP1 = + BinaryOperator::Create(Instruction::Add, OP, I.getOperand(1), "", &I); + break; + case BINARY_OBF_CASE_2: + OP = BinaryOperator::Create(Instruction::Sub, I.getOperand(0), + I.getOperand(1), "", &I); + OP1 = + BinaryOperator::Create(Instruction::Mul, OP, I.getOperand(1), "", &I); + break; + case BINARY_OBF_CASE_3: + OP = BinaryOperator::Create(Instruction::Shl, I.getOperand(0), + I.getOperand(1), "", &I); + break; + default: + break; + } + } while (++Index < N); +} + +void ControlFlowObfuscator::handleFloatInstruction(Instruction &I) { + int N = (getRandNum() % SWITCH_NUM) + 1; + int Index = 0; + Instruction *OP = nullptr; + BinaryOperator *OP1 = nullptr; + (void) OP; + (void) OP1; + + do { + switch (getRandNum() % FLOAT_OBF_SWITCH_NUM) { + case FLOAT_OBF_CASE_0: + break; + case FLOAT_OBF_CASE_1: + OP = UnaryOperator::CreateFNeg(I.getOperand(0), "", &I); + OP1 = BinaryOperator::Create(Instruction::FAdd, OP, I.getOperand(1), "", + &I); + break; + case FLOAT_OBF_CASE_2: + OP = BinaryOperator::Create(Instruction::FSub, I.getOperand(0), + I.getOperand(1), "", &I); + OP1 = BinaryOperator::Create(Instruction::FMul, OP, I.getOperand(1), "", + &I); + break; + default: + break; + } + } while (++Index < N); +} + +void ControlFlowObfuscator::handleBinaryCmp(Instruction &I) { + ICmpInst *CI = dyn_cast(&I); + llvm::CmpInst::Predicate OPTable[] = {ICmpInst::ICMP_EQ, ICmpInst::ICMP_NE, + ICmpInst::ICMP_UGT, ICmpInst::ICMP_UGE, + ICmpInst::ICMP_ULT, ICmpInst::ICMP_ULE, + ICmpInst::ICMP_SGT, ICmpInst::ICMP_SGE, + ICmpInst::ICMP_SLT, ICmpInst::ICMP_SLE}; + int N = (getRandNum() % SWITCH_NUM) + 1; + int Count = 0; + int Index; + do { + switch (getRandNum() % BCMP_OBF_SWITCH_NUM) { + case BCMP_OBF_CASE_0: + break; + case BCMP_OBF_CASE_1: + CI->swapOperands(); + break; + case BCMP_OBF_CASE_2: + Index = + getRandNum() % (sizeof(OPTable) / sizeof(llvm::CmpInst::Predicate)); + CI->setPredicate(OPTable[Index]); + break; + default: + break; + } + } while (++Count < N); +} + +void ControlFlowObfuscator::handleFloatCmp(Instruction &I) { + FCmpInst *CI = dyn_cast(&I); + llvm::CmpInst::Predicate OPTable[] = {ICmpInst::FCMP_OEQ, FCmpInst::FCMP_ONE, + FCmpInst::FCMP_UGT, FCmpInst::FCMP_UGE, + FCmpInst::FCMP_ULT, FCmpInst::FCMP_ULE, + FCmpInst::FCMP_OGT, FCmpInst::FCMP_OGE, + FCmpInst::FCMP_OLT, FCmpInst::FCMP_OLE}; + int N = (getRandNum() % SWITCH_NUM) + 1; + int Count = 0; + int Index; + do { + switch (getRandNum() % FCMP_OBF_SWITCH_NUM) { + case FCMP_OBF_CASE_0: + break; + case FCMP_OBF_CASE_1: + CI->swapOperands(); + break; + case FCMP_OBF_CASE_2: + Index = + getRandNum() % (sizeof(OPTable) / sizeof(llvm::CmpInst::Predicate)); + CI->setPredicate(OPTable[Index]); + break; + default: + break; + } + } while (++Count < N); +} + +void ControlFlowObfuscator::insertBogusBlocks(Instruction &I) { + if (!I.isBinaryOp()) { + return; + } + unsigned OP = I.getOpcode(); + if (isBinaryOpcode(OP)) { + handleBinaryInstruction(I); + } else if (isFloatOpcode(OP)) { + handleFloatInstruction(I); + } else if (OP == Instruction::ICmp) { + handleBinaryCmp(I); + } else if (OP == Instruction::FCmp) { + handleFloatCmp(I); + } +} + +BasicBlock *ControlFlowObfuscator::createAlterBasicBlock(BasicBlock &B, + const Twine &N, + Function &F) { + ValueToValueMapTy TV; + BasicBlock *BcfBlock = CloneBasicBlock(&B, TV, N, &F); + if (BcfBlock == nullptr) { + return nullptr; + } + renameOperand(TV, *BcfBlock); + for (auto I = BcfBlock->begin(); I != BcfBlock->end(); I++) { + Instruction *TempInst = dyn_cast(I); + if (TempInst == nullptr) { + continue; + } + insertBogusBlocks(*TempInst); + } + + Instruction *I = BcfBlock->getTerminator(); + if (I == nullptr) { + return nullptr; + } + I->eraseFromParent(); + if (BranchInst::Create(&B, BcfBlock) == nullptr) { + return nullptr; + } + return BcfBlock; +} + +bool ControlFlowObfuscator::createBrForBasicBlock(Type >, BasicBlock &Father, + BasicBlock &LChild, + BasicBlock &RChild) { + Value *LValue = ConstantFP::get(>, 0); + Value *RValue = ConstantFP::get(>, 1); + if (LValue == nullptr || RValue == nullptr) { + return false; + } + Instruction *I = Father.getTerminator(); + if (I == nullptr) { + return false; + } + I->eraseFromParent(); + FCmpInst *C = std::make_unique(Father, FCmpInst::FCMP_FALSE, LValue, + RValue, "condition") + .release(); + if (C == nullptr) { + return false; + } + if (BranchInst::Create(&LChild, &RChild, static_cast(C), &Father) == + nullptr) { + return false; + } + return true; +} + +void ControlFlowObfuscator::createBasicBlock(BasicBlock &B, Function &F) { + Module *M = F.getParent(); + Type *GT = Type::getFloatTy(M->getContext()); + // split the [`B`] into [`B` + `SplitBlock`] + BasicBlock *SplitBlock = B.splitBasicBlock(B.getTerminator(), "obfuscatorA"); + + // split the [`SplitBlock`] into [`SplitBlock` + `SplitBlock2`] + BasicBlock *SplitBlock2 = + SplitBlock->splitBasicBlock(SplitBlock->getTerminator(), "obfuscatorB"); + + if (SplitBlock2 == nullptr) { + return; + } + // clone `SplitBlock` to `BcfBlock` and insert bogus instructions into it. + BasicBlock *BcfBlock = createAlterBasicBlock(*SplitBlock, "", F); + if (BcfBlock == nullptr) { + return; + } + /* create a conditional jump instruction in the end of `B` */ + /* and set `SplitBlock` and `BcfBlock` as the jump Targets */ + if (!createBrForBasicBlock(*GT, B, *SplitBlock, *BcfBlock)) { + return; + } + /* create a conditional jump instruction in the end of `SplitBlock` */ + /* and set `SplitBlock2` and `BcfBlock` as the jump Targets */ + if (!createBrForBasicBlock(*GT, *SplitBlock, *SplitBlock2, *BcfBlock)) { + return; + } +} + +void ControlFlowObfuscator::sortBasicBlock(Function &F) { + std::vector TmpBlockVec; + for (auto B = F.begin(); B != F.end(); B++) { + BasicBlock *Temp = dyn_cast(B); + if (Temp == nullptr) { + continue; + } + TmpBlockVec.push_back(Temp); + if (TmpBlockVec.size() <= 1) { + return; + } + } + for (uint32_t Count = 0; Count < TmpBlockVec.size(); Count++) { + int Temp = (getRandNum() % (TmpBlockVec.size() - 1)) + 1; + BasicBlock *BB = TmpBlockVec[Temp]; + if (BB == nullptr) { + continue; + } + F.getBasicBlockList().remove(BB); + F.getBasicBlockList().push_back(BB); + } +} + +static bool isDivInstruction(Instruction &I) { + return Instruction::isIntDivRem(I.getOpcode()) || + I.getOpcode() == Instruction::FDiv || + I.getOpcode() == Instruction::FRem; +} + +void ControlFlowObfuscator::insertBranches(Function &F) { + bool Flag = false; + std::vector TmpBlockVec; + std::vector DivBlockVec; + unsigned int Index = 0; + for (BasicBlock &B : F) { + for (auto &I : B) { + if (isDivInstruction(I)) { + DivBlockVec.push_back(&B); + break; + } + } + } + for (BasicBlock &B : F) { + // Skip blocks contain div instructions. + if (std::find(DivBlockVec.begin(), DivBlockVec.end(), &B) != DivBlockVec.end()) { + continue; + } + // Skip blocks which's successors contain div instructions. + unsigned int SuccNum = B.getTerminator()->getNumSuccessors(); + bool skip = false; + for (Index = 0; Index < SuccNum; Index++) { + auto Succ = B.getTerminator()->getSuccessor(Index); + if (std::find(DivBlockVec.begin(), DivBlockVec.end(), Succ) != DivBlockVec.end()) { + skip = true; + break; + } + } + if (skip) + continue; + TmpBlockVec.push_back(&B); + } + + for (BasicBlock *B : TmpBlockVec) { + if (Flag == false || !skipBlock()) { + Flag = true; + // create bogus blocks and insert it into `F` + createBasicBlock(*B, F); + } + } +} + +void ControlFlowObfuscator::findBrInstWithType( + std::vector &InstVec, std::vector &CondVec, + Function &F, uint32_t Pre) { + for (auto B = F.begin(); B != F.end(); B++) { + Instruction *I = B->getTerminator(); + if (I == nullptr || I->getOpcode() != Instruction::Br) { + continue; + } + BranchInst *BrInst = reinterpret_cast(I); + if (BrInst == nullptr || !BrInst->isConditional()) { + continue; + } + FCmpInst *C = reinterpret_cast(BrInst->getCondition()); + if (C == nullptr) { + continue; + } + uint32_t OP = C->getOpcode(); + if (OP != Instruction::FCmp) { + continue; + } + if (C->getPredicate() == Pre) { + InstVec.push_back(I); + CondVec.push_back(C); + } + } +} + +void ControlFlowObfuscator::createBrExpressFema(Function &F, Instruction &I) { + Module *M = F.getParent(); + IntegerType *GT = Type::getInt32Ty(M->getContext()); + if (GT == nullptr) { + return; + } + Constant *X1 = static_cast( + ConstantInt::get(GT, getRandNum() % MAX_EXPRESS_VALUE, false)); + GlobalVariable *X = std::make_unique( + *M, GT, false, GlobalVariable::PrivateLinkage, X1) + .release(); + LoadInst *OPX = + std::make_unique(GT, static_cast(X), "", &I).release(); + // [X] = RandNum + // OP = [X] * PRIME_NUM + BinaryOperator *OP = + BinaryOperator::Create(Instruction::Mul, static_cast(OPX), + ConstantInt::get(GT, PRIME_NUM, false), "", &I); + + Constant *Y1 = static_cast( + ConstantInt::get(GT, getRandNum() % MAX_EXPRESS_VALUE, false)); + GlobalVariable *Y = std::make_unique( + *M, GT, false, GlobalValue::PrivateLinkage, Y1) + .release(); + LoadInst *OPY = + std::make_unique(GT, static_cast(Y), "", &I).release(); + // [y] = RandNum + // OP1 = [y] * [y] + BinaryOperator *OP1 = + BinaryOperator::Create(Instruction::Mul, static_cast(OPY), + static_cast(OPY), "", &I); + // OP1 = OP1 + 1 + OP1 = BinaryOperator::Create(Instruction::Add, OP1, + ConstantInt::get(GT, 1, false), "", &I); + ICmpInst *C = + std::make_unique(&I, ICmpInst::ICMP_NE, OP, OP1).release(); + + if (BranchInst::Create(static_cast(&I)->getSuccessor(0), + static_cast(&I)->getSuccessor(1), + static_cast(C), + static_cast(&I)->getParent()) == + nullptr) { + return; + } + I.eraseFromParent(); +} + +BinaryOperator *ControlFlowObfuscator::createFemaMul(Module &M, Instruction &I, + IntegerType *GT) { + Constant *X1 = static_cast( + ConstantInt::get(GT, getRandNum() % MAX_EXPRESS_VALUE + 1, false)); + GlobalVariable *X = std::make_unique( + M, GT, false, GlobalVariable::PrivateLinkage, X1) + .release(); + LoadInst *OPX = + std::make_unique(GT, static_cast(X), "", &I).release(); + // OP = [X] * PRIME_NUM + BinaryOperator *OP = + BinaryOperator::Create(Instruction::Mul, static_cast(OPX), + ConstantInt::get(GT, PRIME_NUM, false), "", &I); + return OP; +} + +void ControlFlowObfuscator::createBrExpressFema2(Function &F, Instruction &I) { + Module *M = F.getParent(); + IntegerType *GT = Type::getInt32Ty(M->getContext()); + if (GT == nullptr) { + return; + } + // OP = [X] * PRIME_NUM + BinaryOperator *OP = createFemaMul(*M, I, GT); + + // OP1 = [y] * [y] + BinaryOperator *OP1 = createFemaMul(*M, I, GT); + + OP1 = BinaryOperator::Create(Instruction::Add, OP1, + ConstantInt::get(GT, 1, false), "", &I); + + // OP2 = [z] * [z] + BinaryOperator *OP2 = createFemaMul(*M, I, GT); + + // OP = [X] * PRIME_NUM + [y]^2 + OP = BinaryOperator::Create(Instruction::Add, OP, OP1, "", &I); + // condition is ([z]^2 != [X] * PRIME_NUM + [y]^2) + ICmpInst *C = + std::make_unique(&I, ICmpInst::ICMP_NE, OP, OP2).release(); + + if (BranchInst::Create(static_cast(&I)->getSuccessor(0), + static_cast(&I)->getSuccessor(1), + static_cast(C), + static_cast(&I)->getParent()) == + nullptr) { + return; + } + I.eraseFromParent(); +} + +void ControlFlowObfuscator::applyFema(Function &F) { + std::vector InstVec; + std::vector CondVec; + + findBrInstWithType(InstVec, CondVec, F, FCmpInst::FCMP_FALSE); + for (auto IT : InstVec) { + Instruction *I = IT; + if (I == nullptr) { + continue; + } + if (getRandNum() % SWITCH_NUM == 0) { + createBrExpressFema(F, *I); + } else { + createBrExpressFema2(F, *I); + } + } + + for (uint32_t Count = 0; Count < CondVec.size(); Count++) { + CondVec[Count]->eraseFromParent(); + } + return; +} + +void ControlFlowObfuscator::doBcfObfuse(Module &M) { + for (auto &F : M) { + if (!needObfuse(F, "obf-cf-bogus")) { + continue; + } + // insert indirect branchs + insertBranches(F); + // shuffle the order of blocks + sortBasicBlock(F); + // apply fema to the condition of indirect branchs + applyFema(F); + } +} + +void ControlFlowObfuscator::swapBlockSet(std::vector &BV) { + for (uint32_t Index = 0; Index < BV.size(); Index++) { + uint32_t TempIndex = getRandNum() % BV.size(); + uint32_t Tag = BV[TempIndex]; + BV[TempIndex] = BV[Index]; + BV[Index] = Tag; + } +} + +bool ControlFlowObfuscator::checkPreDisIsSkip(BasicBlock &B) { + bool Flag = false; + for (auto Itr = pred_begin(&B); Itr != pred_end(&B); Itr++) { + BasicBlock *Pred = *Itr; + for (uint32_t Index = 0; Index < BlockVec.size(); Index++) { + if (BlockVec[Index] != Pred) + continue; + if (BlockFlagVec[Index] == 0) { + Flag = true; + break; + } + } + } + return Flag; +} + +void ControlFlowObfuscator::clearObfData() { + BlockVec.clear(); + BlockSet.clear(); + MapTagVec.clear(); + MapValueVec.clear(); + BlockFlagVec.clear(); + PredisFlagVec.clear(); +} + +bool ControlFlowObfuscator::skipBlock() { + unsigned int Level = this->ObfConfig->getObfLevel(); + unsigned int Max = this->ObfConfig->getMaxObfLevel(); + unsigned int Num = getRandNum(); + return ((Num % Max) > Level); +} + +void ControlFlowObfuscator::initTags(size_t Length) { + for (uint32_t Index = 0; Index < Length; Index++) { + uint32_t Tag = 0; + uint32_t Temp = 0; + do { + Tag = (getRandNum() << MAP_OFFSET) / MAP_PRIME; + Temp = Tag ^ MAP_CHANGE; + } while (find(MapTagVec.begin(), MapTagVec.end(), Tag) != MapTagVec.end() || + Temp >= (UINT32_MAX / MAP_PRIME)); + + uint32_t Value = Temp * MAP_PRIME; + MapTagVec.push_back(Tag); + MapValueVec.push_back(Value); + } +} + +bool isUnsafeGCStatePoint(Instruction &I) { + BasicBlock *B = I.getParent(); + auto GI = dyn_cast(&I); + if (GI == nullptr) { + return false; + } + if (GI->isUsedOutsideOfBlock(B)) { + return true; + } + for (auto &SI : GI->struct_args()) { + auto SInst = dyn_cast(&SI); + if (SInst != nullptr && SInst->isUsedOutsideOfBlock(B)) { + return true; + } + } + for (auto &SI : GI->gc_args()) { + auto SInst = dyn_cast(&SI); + if (SInst != nullptr && SInst->isUsedOutsideOfBlock(B)) { + return true; + } + } + return false; +} + +bool isInstUsedOutside(Instruction &I) { + BasicBlock *B = I.getParent(); + if (I.isUsedOutsideOfBlock(B)) { + return true; + } + auto CI = dyn_cast(&I); + for (unsigned int Index = 0; Index < CI->arg_size(); Index++) { + auto Arg = CI->getArgOperand(Index); + if (Arg == nullptr) { + continue; + } + auto ArgInst = dyn_cast(Arg); + if (ArgInst != nullptr && ArgInst->isUsedOutsideOfBlock(B)) { + return true; + } + } + return false; +} + +bool isUnsafeGCRelocate(Instruction &I) { + auto GR = dyn_cast(&I); + if (GR == nullptr) { + return false; + } + return isInstUsedOutside(I); +} + +bool isUnsafeGCResultInst(Instruction &I) { + auto GR = dyn_cast(&I); + if (GR == nullptr) { + return false; + } + return isInstUsedOutside(I); +} + +bool isFunctionSafeToModifyStack(Function &F) { + for (auto &Iter : F) { + BasicBlock *B = &Iter; + if (isa(B->getTerminator())) + return false; + for (auto &I : *B) { + if (isUnsafeGCStatePoint(I) || isUnsafeGCRelocate(I) || + isUnsafeGCResultInst(I) || isDivInstruction(I)) { + return false; + } + } + } + return true; +} + +bool ControlFlowObfuscator::initOrigBlock(Function &F) { + if (!isFunctionSafeToModifyStack(F)) { + return false; + } + + clearObfData(); + for (auto &Iter : F) { + BasicBlock *B = &Iter; + if (B != nullptr) { + BlockVec.push_back(B); + } + } + if (BlockVec.size() < MINI_BLOCK_NUM || BlockVec.size() > MAX_BLOCK_NUM) + return false; + + BlockVec.erase(BlockVec.begin()); + BasicBlock *Temp = &*(F.begin()); + + if (isa(Temp->getTerminator())) { + BranchInst *BI = dyn_cast(Temp->getTerminator()); + if (BI->isConditional() && Temp->size() > 1) { + BasicBlock::iterator Iter = --Temp->end(); + Iter--; + BasicBlock *SB = Temp->splitBasicBlock(Iter, "obfuscatorInit"); + BlockVec.insert(BlockVec.begin(), SB); + } + } + + // generate a disorder set + BlockSet.resize(BlockVec.size()); + int TempVal = 0; + for (auto &BlockSetVal : BlockSet) { + BlockSetVal = TempVal++; + } + + swapBlockSet(BlockSet); + + for (uint32_t Index = 0; Index < BlockVec.size(); Index++) { + if (BlockVec[Index]->getTerminator()->getNumSuccessors() > TWO_SUCC || skipBlock()) + BlockFlagVec.push_back(0); + else + BlockFlagVec.push_back(1); + } + for (uint32_t Index = 0; Index < BlockVec.size(); Index++) { + BasicBlock *BB = BlockVec[Index]; + // check if `BB`'s predecessors is 0 in `BlockFlagVec` + bool Flag = checkPreDisIsSkip(*BB); + PredisFlagVec.push_back(Flag); + } + + initTags(BlockVec.size()); + return true; +} + +uint32_t ControlFlowObfuscator::getBlockIndex(BasicBlock &B) { + return distance(BlockVec.begin(), find(BlockVec.begin(), BlockVec.end(), &B)); +} + +Value *ControlFlowObfuscator::getFuncConstInt(Function &F, uint32_t Index, + uint32_t T) { + if (T == TYPE1) { + return ConstantInt::get(Type::getInt1Ty(F.getContext()), Index); + } + if (T == TYPE32) { + return ConstantInt::get(Type::getInt32Ty(F.getContext()), Index); + } + return nullptr; +} + +bool ControlFlowObfuscator::updateCaseInst(SwitchInst &CaseInst, + BasicBlock &Router) { + for (uint32_t Index : BlockSet) { + BasicBlock *B = BlockVec[Index]; + ConstantInt *CaseValue = dyn_cast(ConstantInt::get( + CaseInst.getCondition()->getType(), MapValueVec[Index])); + if (CaseValue == nullptr) + return false; + CaseInst.addCase(CaseValue, B); + } + return true; +} + +Value *ControlFlowObfuscator::getBranches(uint32_t &Fbran, uint32_t &Tbran, + Function &F, BasicBlock &B, + uint32_t Index) { + Value *ConstBool = nullptr; + if (B.getTerminator()->getNumSuccessors() == 1) { + ConstBool = getFuncConstInt(F, 0, TYPE1); + Fbran = getBlockIndex(*B.getTerminator()->getSuccessor(0)); + Tbran = Index; + } else if (B.getTerminator()->getNumSuccessors() == TWO_SUCC) { + BranchInst *BI = dyn_cast(B.getTerminator()); + if (BI == nullptr) { + return nullptr; + } + ConstBool = dyn_cast(B.getTerminator())->getCondition(); + Fbran = getBlockIndex(*B.getTerminator()->getSuccessor(1)); + Tbran = getBlockIndex(*B.getTerminator()->getSuccessor(0)); + } else { + return nullptr; + } + return ConstBool; +} + +BinaryOperator *ControlFlowObfuscator::genTags(Function &F, LoadInst &NewInst, + BasicBlock &B, uint32_t Index, + std::vector &BS) { + uint32_t Fbran, Tbran; + Value *ConstBool = nullptr; + BinaryOperator *SetTags = nullptr; + uint32_t ObfNum = getRandNum(); + + ConstBool = getBranches(Fbran, Tbran, F, B, Index); + if (!ConstBool) { + return nullptr; + } + + Value *ConstInt = getFuncConstInt(F, ObfNum, TYPE32); + Value *ConstZero = getFuncConstInt(F, 0, TYPE32); + if (PredisFlagVec[Index] == 0) + SetTags = BinaryOperator::Create(BinaryOperator::Xor, ConstInt, &NewInst, + "", B.getTerminator()); + else + SetTags = BinaryOperator::Create(BinaryOperator::Xor, ConstInt, ConstZero, + "", B.getTerminator()); + for (uint32_t BNum : BS) { + if (BNum == Fbran) { + if (PredisFlagVec[Index] == 0) { + ConstInt = getFuncConstInt( + F, MapTagVec[Index] ^ MapTagVec[Fbran] ^ ObfNum, TYPE32); + } else { + ConstInt = getFuncConstInt(F, MapTagVec[Fbran] ^ ObfNum, TYPE32); + } + SetTags = BinaryOperator::Create(BinaryOperator::Xor, ConstInt, SetTags, + "", B.getTerminator()); + } else if (BNum == Tbran) { + ConstInt = + getFuncConstInt(F, MapTagVec[Tbran] ^ MapTagVec[Fbran], TYPE32); + Value *ZextInst = std::make_unique( + ConstBool, Type::getInt32Ty(F.getContext()), "", + B.getTerminator()) + .release(); + BinaryOperator *Sext = BinaryOperator::Create( + BinaryOperator::Sub, getFuncConstInt(F, 0, TYPE32), ZextInst, "", + B.getTerminator()); + BinaryOperator *MaskVal = BinaryOperator::Create( + BinaryOperator::And, Sext, ConstInt, "", B.getTerminator()); + SetTags = BinaryOperator::Create(BinaryOperator::Xor, MaskVal, SetTags, + "", B.getTerminator()); + } + } + return SetTags; +} + +bool ControlFlowObfuscator::updateTags(Function &F, LoadInst &OptTag, + BasicBlock &Router) { + for (uint32_t Index : BlockSet) { + BasicBlock *B = BlockVec[Index]; + if (BlockFlagVec[Index] == 0) { + continue; + } + if (B->getTerminator()->getNumSuccessors() == 0) { + continue; + } + std::vector BS = BlockSet; + swapBlockSet(BS); + BinaryOperator *SetTags = genTags(F, OptTag, *B, Index, BS); + if (SetTags == nullptr) { + return false; + } + + Instruction *I = B->getTerminator(); + if (I == nullptr) { + return false; + } + + I->eraseFromParent(); + if (std::make_unique(SetTags, OptTag.getPointerOperand(), B) + .release() == nullptr) { + return false; + } + + if (BranchInst::Create(&Router, B) == nullptr) { + return false; + } + } + return true; +} + +bool ControlFlowObfuscator::isEscapeInst(Instruction &I) { + BasicBlock *B = I.getParent(); + if (B == nullptr) + return false; + Value::use_iterator InstIter; + for (InstIter = I.use_begin(); InstIter != I.use_end(); InstIter++) { + Instruction *TmpInst = dyn_cast(*InstIter); + if (TmpInst == nullptr) + continue; + if (isa(TmpInst)) { + return true; + } + if (TmpInst->getParent() != B) { + return true; + } + } + return false; +} + +bool ControlFlowObfuscator::isUsedOutside(Instruction &I, BasicBlock &B) { + if (!I.isUsedOutsideOfBlock(&B)) { + return false; + } + auto FB = &*(B.getParent()->begin()); + if (I.getParent() == FB) { + // the 1st block is not in the big switch-case struct, + // so it is safe to ignore variables from the 1st block. + return false; + } + + return true; +} + +void ControlFlowObfuscator::pushToVector(BasicBlock &B, Instruction &I, + BasicBlock *E, + std::vector &PHI, + std::vector &Reg) { + if (isa(I)) { + PHINode *PN = dyn_cast(&I); + if (PN != nullptr) + PHI.push_back(PN); + return; + } + + if (isa(I) && I.getParent() == E) + return; + + if (I.getType()->getTypeID() == Type::TokenTyID) + return; + + if (isEscapeInst(I)) { + Reg.push_back(&I); + } else if (isUsedOutside(I, B)) { + Reg.push_back(&I); + } +} + +void ControlFlowObfuscator::searchInstructions( + Function &F, std::vector &PHI, std::vector &Reg) { + PHI.clear(); + Reg.clear(); + BasicBlock *E = &*(F.begin()); + if (E == nullptr) + return; + for (auto &B : F) { + for (auto &I : B) + pushToVector(B, I, E, PHI, Reg); + } +} + +void ControlFlowObfuscator::handleRegStack(Function &F) { + std::vector PHI; + std::vector Reg; + unsigned int Index; + auto AllocaPoint = F.begin()->begin(); + do { + searchInstructions(F, PHI, Reg); + for (Index = 0; Index != PHI.size(); Index++) { + DemotePHIToStack(PHI.at(Index), &*AllocaPoint); + } + for (Index = 0; Index != Reg.size(); Index++) { + DemoteRegToStack(*Reg.at(Index), false, &*AllocaPoint); + } + } while (Reg.size() != 0 || PHI.size() != 0); +} + +void ControlFlowObfuscator::doFltObfuse(Function &F) { + if (!initOrigBlock(F)) { + return; + } + BasicBlock *B = &*(F.begin()); + StoreInst *Store = nullptr; + (void) Store; + uint32_t nextIndex = getBlockIndex(*(B->getTerminator()->getSuccessor(0))); + B->getTerminator()->eraseFromParent(); + + IntegerType *T = Type::getInt32Ty(F.getContext()); + AllocaInst *Values = + std::make_unique(T, 0, "Values", B).release(); + AllocaInst *Tags = std::make_unique(T, 0, "Tags", B).release(); + Value *BasisConst = getFuncConstInt(F, MAP_CHANGE, TYPE32); + + // Store `MAP_CHANGE` in `Values` + Store = std::make_unique(BasisConst, Values, B).release(); + // Store `MapTagVec[nextIndex]` in Tags + Store = std::make_unique( + getFuncConstInt(F, MapTagVec[nextIndex], TYPE32), Tags, B) + .release(); + + BasicBlock *Router = + BasicBlock::Create(F.getContext(), "obfuscatorFlt", &F, B); + B->moveBefore(Router); + BranchInst::Create(Router, B); + + // Store `MAP_CHANGE` in `Values` + Store = std::make_unique(BasisConst, Values, Router).release(); + ConstantInt *PrimeConst = ConstantInt::get(T, MAP_PRIME); + LoadInst *OptValue = + std::make_unique(T, Values, "Values", Router).release(); + LoadInst *OptTag = + std::make_unique(T, Tags, "Tags", Router).release(); + BinaryOperator *GetValue = + BinaryOperator::Create(BinaryOperator::Xor, OptTag, OptValue, "", Router); + // (Tags ^ Values) * MAP_PRIME + GetValue = BinaryOperator::Create(BinaryOperator::Mul, GetValue, PrimeConst, + "", Router); + Store = std::make_unique(GetValue, Values, Router).release(); + // switch(MapValueVec[nextIndex]) + SwitchInst *CaseInst = SwitchInst::Create(GetValue, Router, 0, Router); + if (!updateCaseInst(*CaseInst, *Router)) { + report_fatal_error("Obfuscator failed to update case intructions!"); + } + if (!updateTags(F, *OptTag, *Router)) { + report_fatal_error("Obfuscator failed to update case tags!"); + } + handleRegStack(F); +} + +void ControlFlowObfuscator::doFltObfuse(Module &M) { + initializeLowerSwitchLegacyPassPass(*PassRegistry::getPassRegistry()); + for (auto &F : M) { + if (!needObfuse(F, "obf-cf-flatten")) { + continue; + } + doFltObfuse(F); + } +} + +bool ControlFlowObfuscator::needObfuse(Function &F, std::string Owner) { + if (F.isDeclaration() || F.hasAvailableExternallyLinkage() != 0) + return false; + auto FName = F.getName().str(); + if (!this->ObfConfig->needObfuse(FName, Owner)) + return false; + return true; +} + +PreservedAnalyses ControlFlowObfuscator::run(Module &M, + ModuleAnalysisManager &AM) { + this->ObfConfig = AM.getResult(M); + if (EnableConfigBcf) + doBcfObfuse(M); + + if (EnableConfigFlatten) + doFltObfuse(M); + + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Transforms/Obfuscator/DataObfuscator.cpp b/llvm/lib/Transforms/Obfuscator/DataObfuscator.cpp new file mode 100644 index 000000000..0c2cf4030 --- /dev/null +++ b/llvm/lib/Transforms/Obfuscator/DataObfuscator.cpp @@ -0,0 +1,760 @@ +//===------------------------- DataObfuscator.cpp -------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// The data obfuscation pass. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/TypeFinder.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/PassRegistry.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" +#include "llvm/Transforms/Obfuscator/DataObfuscator.h" +#include + +using namespace llvm; + +extern cl::opt EnableConfigObfString; +extern cl::opt EnableConfigObfConst; + +namespace { +const int BUILD_GEP_NUM = 2; +const int ROUTE_METHOD = 2; +const int ROUTE_VALUE = 3; +const int INT_TYPE = 64; +const int COMPUTE_TIMES = 32; +const int RAND_LEN = 8; +} // end anonymous namespace + +StringRef DataObfuscator::getGlobalString(GlobalVariable &G) { + Constant *C = + dyn_cast(G.getInitializer()); + if (C == nullptr) { + return StringRef("", 0); + } + // string data is stored at index 2 + C = C->getAggregateElement(2); + if (C == nullptr) { + return StringRef("", 0); + } + ConstantDataSequential *CDS = + dyn_cast(C); + if (CDS == nullptr) + return StringRef("", 0); + return CDS->isString() ? CDS->getAsString() : StringRef("", 0); +} + +char *DataObfuscator::getGlobalStringData(GlobalVariable &G, + unsigned int &Len) { + char *Data = nullptr; + uint64_t Num, ByteSize; + Len = 0; + if (!G.getName().contains(StringRef("const_cjstring_data"))) { + return nullptr; + } + Constant *C = + dyn_cast(G.getInitializer()); + + if (C == nullptr) { + return nullptr; + } + + // string data is stored at index 2 + C = C->getAggregateElement(2); + if (C == nullptr) { + return nullptr; + } + + ConstantDataSequential *CDS = + dyn_cast(C); + if (CDS == nullptr) { + return nullptr; + } + + Data = const_cast(CDS->getRawDataValues().data()); + Num = CDS->getNumElements(); + ByteSize = CDS->getElementByteSize(); + if (Num == 0 || Num > UINT32_MAX || ByteSize >= UINT32_MAX / Num) { + return nullptr; + } + Len = Num * ByteSize; + + return Data; +} + +bool DataObfuscator::needObfuseString(GlobalVariable &G) { + if (!G.isConstant() || !G.hasInitializer()) + return false; + unsigned int Len; + char *Data = getGlobalStringData(G, Len); + if (Data == nullptr || Len == 0) { + return false; + } + + return true; +} + +bool DataObfuscator::cmpGlb(GlobalVariable *&FG, GlobalVariable *&SG) { + std::string F = getGlobalString(*FG).str(); + std::string S = getGlobalString(*SG).str(); + return F.compare(S) < 0; +} + +void DataObfuscator::stripGlobalString(Module &M) { + std::list GvList; + for (auto &G : M.globals()) { + if (needObfuseString(G)) + GvList.push_back(&G); + } + + GvList.sort(cmpGlb); + std::list::iterator IG; + for (IG = GvList.begin(); IG != GvList.end();) { + GlobalVariable *GV1 = *IG; + GlobalVariable *GV2 = nullptr; + StringRef GVS1 = getGlobalString(*GV1); + IG++; + while (IG != GvList.end() && + (GV2 = *IG, GVS1.equals(getGlobalString(*GV2)))) { + GV2->replaceAllUsesWith( + ConstantExpr::getPointerCast(GV1, GV2->getType())); + IG = GvList.erase(IG); + GV2->eraseFromParent(); + } + } +} + +void DataObfuscator::replaceGlobalVariable( + Module &M, GlobalVariable &G, + std::vector> &ObfGVec) { + GlobalVariable *Replace = nullptr; + std::unique_ptr OV = nullptr; + char *Data = nullptr; + unsigned int Len; + + Data = getGlobalStringData(G, Len); + if (Data == nullptr || Len == 0) + return; + + Replace = std::make_unique( + M, G.getType()->getElementType(), false, G.getLinkage(), + nullptr, G.getName(), nullptr, G.getThreadLocalMode(), + G.getType()->getAddressSpace()) + .release(); + if (Replace == nullptr) + return; + + Replace->setInitializer(G.getInitializer()); + + OV = std::make_unique(); + if (OV == nullptr) + return; + + OV->OriVar = &G; + OV->ObfVar = Replace; + OV->Key = this->ObfConfig->getRandNum(); + OV->ReplaceName = G.getName().str(); + OV->Len = Len; + + for (unsigned int Index = 0; Index < Len; Index++) { + Data[Index] = (static_cast(Data[Index])) ^ OV->Key; + } + + ObfGVec.push_back(std::move(OV)); + G.replaceAllUsesWith(Replace); +} + +void DataObfuscator::doObfuseString(Module &M) { + std::vector GVec; + std::vector> ObfGVec; + for (auto &G : M.globals()) { + if (needObfuseString(G)) + GVec.push_back(&G); + } + + for (auto &G : GVec) { + replaceGlobalVariable(M, *G, ObfGVec); + } + + for (auto &OV : ObfGVec) { + OV->OriVar->eraseFromParent(); + OV->ObfVar->setName(OV->ReplaceName); + } + + if (ObfGVec.size() != 0) + addDecodeFunction(M, ObfGVec); +} + +void DataObfuscator::doDecode(LLVMContext &CTX, Value &V, IRBuilder<> &Builder, + const std::unique_ptr &ObfGv) { + Type *RetType = llvm::Type::getInt8Ty(CTX); + LoadInst *LoadElement = Builder.CreateLoad(RetType, &V, ""); + Value *DecodeRet = + Builder.CreateXor(LoadElement, Builder.getInt8(ObfGv->Key), ""); + Builder.CreateStore(DecodeRet, &V, false); +} + +void DataObfuscator::insertDecodeBlock(Module &M, Function &DecodeFunc, + IRBuilder<> &Builder, + std::unique_ptr &ObfGv) { + LLVMContext &CTX = M.getContext(); + BasicBlock *InsertBlock = Builder.GetInsertBlock(); + if (InsertBlock == nullptr) + return; + + BasicBlock *NewBlock = + BasicBlock::Create(CTX, "obfuscatorDecode", &DecodeFunc); + if (NewBlock == nullptr) + return; + if (Builder.CreateBr(NewBlock) == nullptr) + return; + + Builder.SetInsertPoint(NewBlock); + + PHINode *CharIndex = + Builder.CreatePHI(Type::getInt32Ty(CTX), BUILD_GEP_NUM, ""); + if (CharIndex == nullptr) + return; + CharIndex->addIncoming(Builder.getInt32(0), InsertBlock); + unsigned AS = ObfGv->ObfVar->getType()->getPointerAddressSpace(); + Value *CastValue = + Builder.CreateBitCast(ObfGv->ObfVar, Builder.getInt8PtrTy(AS), ""); + // get string base + CastValue = Builder.CreateGEP(Builder.getInt8Ty(), CastValue, Builder.getInt32(16), ""); + Value *GEP = Builder.CreateGEP(Builder.getInt8Ty(), CastValue, CharIndex, ""); + if (GEP == nullptr) + return; + + doDecode(CTX, *GEP, Builder, ObfGv); + + Value *NextCharIndex = Builder.CreateAdd(CharIndex, Builder.getInt32(1), ""); + if (NextCharIndex == nullptr) + return; + Value *IsOver = + Builder.CreateICmpULT(CharIndex, Builder.getInt32(ObfGv->Len - 1), ""); + if (IsOver == nullptr) + return; + InsertBlock = Builder.GetInsertBlock(); + if (InsertBlock == nullptr) + return; + BasicBlock *LastBlock = + BasicBlock::Create(CTX, "obfuscatorDecode", &DecodeFunc); + if (Builder.CreateCondBr(IsOver, InsertBlock, LastBlock) == nullptr) + return; + + Builder.SetInsertPoint(LastBlock); + CharIndex->addIncoming(NextCharIndex, InsertBlock); +} + +void DataObfuscator::addDecodeFunction( + Module &M, std::vector> &ObfGVec) { + Function *DecodeFunc = createDecodeFunction(M); + if (DecodeFunc == nullptr) + return; + + LLVMContext &CTX = M.getContext(); + BasicBlock *E = BasicBlock::Create(CTX, "obfuscatorDecode", DecodeFunc); + if (E == nullptr) + return; + + IRBuilder<> Builder(CTX); + Builder.SetInsertPoint(E); + + for (unsigned int Index = 0; Index < ObfGVec.size(); Index++) + insertDecodeBlock(M, *DecodeFunc, Builder, ObfGVec[Index]); + Builder.CreateRetVoid(); +} + +Function *DataObfuscator::createDecodeFunction(Module &M) { + std::vector RetTypeArgs; + LLVMContext &CTX = M.getContext(); + Type *RetType = Type::getVoidTy(CTX); + if (RetType == nullptr) + return nullptr; + + FunctionType *FuncType = FunctionType::get(RetType, RetTypeArgs, false); + if (FuncType == nullptr) + return nullptr; + + unsigned int RandNum = this->ObfConfig->getRandNum(); + std::string FuncName = std::to_string(RandNum).substr(0, RAND_LEN); + FuncName = FuncName + this->ObfConfig->hash(M.getName(), true); +#if LLVM_VERSION_MAJOR <= 8 + Constant *C = M.getOrInsertFunction(".string_decode" + FuncName, FuncType); + if (C == nullptr) + return nullptr; + Function *DecodeFunc = dyn_cast(C); +#else + FunctionCallee C = + M.getOrInsertFunction(".string_decode" + FuncName, FuncType); + Function *DecodeFunc = dyn_cast(C.getCallee()); +#endif + + if (DecodeFunc == nullptr) + return nullptr; + + DecodeFunc->setCallingConv(CallingConv::Fast); + insertDecodeFunction(M, C); + return DecodeFunc; +} + +static void appendToGlobalCtorArray(Module &M, Function *F, + int Priority, Constant *Data) { + std::string GlobalCtorName = "llvm.global_ctors"; + IRBuilder<> IRB(M.getContext()); + FunctionType *FnTy = FunctionType::get(IRB.getVoidTy(), false); + + // Get the current set of static global constructors and add the new ctor + // to the list. + SmallVector CurrentCtors; + StructType *EltTy = StructType::get( + IRB.getInt32Ty(), PointerType::getUnqual(FnTy), IRB.getInt8PtrTy()); + if (GlobalVariable *GVCtor = M.getNamedGlobal(GlobalCtorName)) { + if (Constant *Init = GVCtor->getInitializer()) { + unsigned n = Init->getNumOperands(); + CurrentCtors.reserve(n + 1); + for (unsigned i = 0; i != n; ++i) + CurrentCtors.push_back(cast(Init->getOperand(i))); + } + GVCtor->eraseFromParent(); + } + + // Build a 3 field global_ctor entry. We don't take a comdat key. + Constant *CSVals[] = { + IRB.getInt32(Priority), + F, + Data ? ConstantExpr::getPointerCast(Data, IRB.getInt8PtrTy()) + : Constant::getNullValue(IRB.getInt8PtrTy()) + }; + Constant *RuntimeCtorInit = + ConstantStruct::get(EltTy, makeArrayRef(CSVals, EltTy->getNumElements())); + + CurrentCtors.push_back(RuntimeCtorInit); + + // Create a new initializer. + ArrayType *AT = ArrayType::get(EltTy, CurrentCtors.size()); + Constant *NewInit = ConstantArray::get(AT, CurrentCtors); + + // Create the new global variable and replace all uses of + // the old global variable with the new one. + (void)new GlobalVariable(M, NewInit->getType(), false, + GlobalValue::AppendingLinkage, NewInit, GlobalCtorName); +} + +static void appendToUsedList(Module &M, ArrayRef Values) { + std::string LLVMUsedName = "llvm.used"; + GlobalVariable *GV = M.getGlobalVariable(LLVMUsedName); + SmallPtrSet InitAsSet; + SmallVector Init; + if (GV) { + if (GV->hasInitializer()) { + auto *CA = cast(GV->getInitializer()); + for (auto &Op : CA->operands()) { + Constant *C = cast_or_null(Op); + if (InitAsSet.insert(C).second) + Init.push_back(C); + } + } + GV->eraseFromParent(); + } + + Type *Int8PtrTy = llvm::Type::getInt8PtrTy(M.getContext()); + for (auto *V : Values) { + Constant *C = ConstantExpr::getPointerBitCastOrAddrSpaceCast(V, Int8PtrTy); + if (InitAsSet.insert(C).second) + Init.push_back(C); + } + + if (Init.empty()) { + return; + } + + ArrayType *ATy = ArrayType::get(Int8PtrTy, Init.size()); + GV = new llvm::GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage, + ConstantArray::get(ATy, Init), LLVMUsedName); + GV->setSection("llvm.metadata"); +} + +void DataObfuscator::insertDecodeFunction(Module &M, FunctionCallee F) { + std::string CtorName = "obf.string.ctor"; + Function *Ctor = Function::createWithDefaultAttr( + FunctionType::get(Type::getVoidTy(M.getContext()), false), + GlobalValue::InternalLinkage, 0, CtorName, &M); + Ctor->addFnAttr(Attribute::NoUnwind); + BasicBlock *CtorBB = BasicBlock::Create(M.getContext(), "", Ctor); + ReturnInst::Create(M.getContext(), CtorBB); + appendToUsedList(M, {Ctor}); + + IRBuilder<> IRB(Ctor->getEntryBlock().getTerminator()); + IRB.CreateCall(F, {}); + + appendToGlobalCtorArray(M, Ctor, 1, nullptr); +} + +bool DataObfuscator::isIntrinsic(Instruction &I) { +#if LLVM_VERSION_MAJOR >= 11 + Function *CalledFunc = dyn_cast(&I)->getCalledFunction(); + if (CalledFunc == nullptr) + CalledFunc = dyn_cast( + dyn_cast(&I)->getCalledOperand()->stripPointerCasts()); +#else + CallSite CS(&I); + Function *CalledFunc = CS.getCalledFunction(); + if (CalledFunc == nullptr) + CalledFunc = dyn_cast(CS.getCalledValue()->stripPointerCasts()); +#endif + if (CalledFunc == nullptr) + return false; + if (CalledFunc->isIntrinsic()) + return true; + return false; +} + +Value *DataObfuscator::addConstZero(Instruction &I, ConstantInt &V) { + IntegerType *TransType = V.getType(); + IntegerType *T = IntegerType::get(I.getParent()->getContext(), INT_TYPE); + IRBuilder<> Builder(&I); + auto M = Builder.GetInsertBlock()->getModule(); + Value *Trans = Builder.CreateIntCast(&V, T, true); + + uint64_t Num = V.getValue().getLimitedValue(); + uint64_t ObfNum = this->ObfConfig->getRandNum(); + + auto G1 = new GlobalVariable(*M, T, false, + GlobalValue::InternalLinkage, static_cast(ConstantInt::get(T, ObfNum, false)), "ConstZero1"); + auto Zero = ConstantInt::get(T, 0); + + auto L1 = std::make_unique(T, static_cast(G1), "", true, &I).release(); + + if (ObfNum % ROUTE_METHOD == 0) { + auto G2 = new GlobalVariable(*M, T, false, + GlobalValue::InternalLinkage, static_cast(ConstantInt::get(T, Num ^ ObfNum, false)), "ConstZero2"); + auto L2 = std::make_unique(T, static_cast(G2), "", true, &I).release(); + auto OP1 = Builder.CreateAdd(L1, Zero); + auto OP2 = Builder.CreateXor(L2, Zero); + Trans = Builder.CreateXor(OP1, OP2); + } else { + auto G2 = new GlobalVariable(*M, T, false, + GlobalValue::InternalLinkage, static_cast(ConstantInt::get(T, Num - ObfNum, false)), "ConstZero2"); + auto L2 = std::make_unique(T, static_cast(G2), "", true, &I).release(); + auto OP1 = Builder.CreateXor(L1, Zero); + auto OP2 = Builder.CreateXor(L2, Zero); + Trans = Builder.CreateAdd(OP1, OP2); + } + + Trans = Builder.CreateIntCast(Trans, TransType, true); + return Trans; +} + +bool DataObfuscator::isImmCallArg(Instruction &I, unsigned int Index) { + if (isa(I)) { + CallBase *CB = dyn_cast(&I); + if (Index < CB->arg_size() && CB->paramHasAttr(Index, Attribute::ImmArg)) + return true; + } + return false; +} + +void DataObfuscator::handleConst(Instruction &I, int Index) { + ConstantInt *ConstInt = dyn_cast(&*(I.getOperand(Index))); + if (ConstInt == nullptr) + return; + + // don't obfuse call argument with ImmArg attribute + if (isImmCallArg(I, Index)) + return; + + uint64_t V = ConstInt->getValue().getLimitedValue(); + if (V == 0 || V == UINT64_MAX) + return; + + Value *NewVal = addConstZero(I, *ConstInt); + if (NewVal == nullptr) + return; + I.setOperand(Index, NewVal); +} + +void DataObfuscator::initInstList(Function &F, std::vector &FunctionLocalVariables) { + for (auto &B : F) { + for (BasicBlock::iterator IT = B.getFirstInsertionPt(); IT != B.end(); + IT++) { + Instruction *I = static_cast(&*IT); + if (I == nullptr) + continue; + Type *T = I->getType(); + if (T == nullptr) + continue; + if (T->isIntegerTy() && (dyn_cast(I) == nullptr)) + FunctionLocalVariables.push_back(I); + } + } +} + +Value *DataObfuscator::bitCalTrans(IRBuilder<> &Builder, Instruction &I, Value &X, + IntegerType &TransType) { + Value *Trans = nullptr; + IntegerType *T = IntegerType::get(Builder.getContext(), INT_TYPE); + uint64_t R1 = this->ObfConfig->getRandNum(); + R1 = (R1 << COMPUTE_TIMES) + this->ObfConfig->getRandNum(); + uint64_t R2 = ~R1; + uint64_t R3 = 0xffffffffffffffff; + + auto M = Builder.GetInsertBlock()->getModule(); + auto G1 = new GlobalVariable(*M, T, false, + GlobalValue::InternalLinkage, static_cast(ConstantInt::get(T, R1, false)), "G1"); + auto G2 = new GlobalVariable(*M, T, false, + GlobalValue::InternalLinkage, static_cast(ConstantInt::get(T, R2, false)), "G2"); + auto G3 = new GlobalVariable(*M, T, false, + GlobalValue::InternalLinkage, static_cast(ConstantInt::get(T, R3, false)), "G3"); + + auto L1 = std::make_unique(T, static_cast(G1), "", true, &I).release(); + auto L2 = std::make_unique(T, static_cast(G2), "", true, &I).release(); + auto L3 = std::make_unique(T, static_cast(G3), "", true, &I).release(); + + Value *Temp = Builder.CreateNot(&X); + Temp = Builder.CreateOr(Temp, static_cast(L1)); + Trans = Builder.CreateAnd(&X, static_cast(L2)); + Trans = Builder.CreateAdd(Trans, Temp); + Trans = Builder.CreateXor(Trans, static_cast(L3)); + Trans = Builder.CreateIntCast(Trans, &TransType, false); + + return Trans; +} + +Value *DataObfuscator::logicCalTrans(IRBuilder<> &Builder, Instruction &I, Value &X, + Value &Y, IntegerType &TransType) { + Value *Trans = nullptr; + IntegerType *T = IntegerType::get(Builder.getContext(), INT_TYPE); + auto M = Builder.GetInsertBlock()->getModule(); + Value *L1 = nullptr; + Value *L2 = nullptr; + if (dyn_cast(&X)) { + auto G1 = new GlobalVariable(*M, T, false, GlobalValue::InternalLinkage, static_cast(&X), "G1"); + L1 = static_cast(std::make_unique(T, static_cast(G1), "", true, &I).release()); + } else { + L1 = &X; + } + + if (dyn_cast(&Y)) { + auto G2 = new GlobalVariable(*M, T, false, GlobalValue::InternalLinkage, static_cast(&Y), "G2"); + L2 = static_cast(std::make_unique(T, static_cast(G2), "", true, &I).release()); + } else { + L2 = &Y; + } + auto A = Builder.CreateNot(L1); + auto B = Builder.CreateNot(L2); + A = Builder.CreateAnd(A, B); + B = Builder.CreateOr(L1, L2); + Trans = Builder.CreateAnd(A, B); + Trans = Builder.CreateIntCast(Trans, &TransType, false); + return Trans; +} + +Value *DataObfuscator::handleZero(Instruction &I, ConstantInt &V, + std::vector &CandidateValues) { + IntegerType *TransType = V.getType(); + IntegerType *T = IntegerType::get(I.getParent()->getContext(), INT_TYPE); + Value *Trans = nullptr; + + if (CandidateValues.size() <= 0) { + return Trans; + } + + IRBuilder<> Builder(&I); + uint32_t Index1 = this->ObfConfig->getRandNum() % CandidateValues.size(); + Value *Temp1 = CandidateValues[Index1]; + Value *X = Builder.CreateIntCast(Temp1, T, false); + if (CandidateValues.size() == 1) { + Trans = bitCalTrans(Builder, I, *X, *TransType); + recordInst(Trans, CandidateValues); + } else { + uint32_t Index2; + do { + Index2 = this->ObfConfig->getRandNum() % CandidateValues.size(); + } while (Index2 == Index1); + Value *Temp2 = CandidateValues[Index2]; + Value *Y = Builder.CreateIntCast(Temp2, T, false); + uint32_t Route = this->ObfConfig->getRandNum() % ROUTE_VALUE; + if (Route == 0) { + Trans = bitCalTrans(Builder, I, *X, *TransType); + recordInst(Trans, CandidateValues); + } else if (Route == 1) { + Trans = logicCalTrans(Builder, I, *X, *Y, *TransType); + recordInst(Trans, CandidateValues); + } + } + return Trans; +} + +void DataObfuscator::handleInstWithZero(Instruction &I, + std::vector &CandidateValues) { + for (unsigned int Index = 0; Index < I.getNumOperands(); Index++) { + if (isImmCallArg(I, Index)) + continue; + ConstantInt *CI = dyn_cast(&*(I.getOperand(Index))); + if (CI == nullptr || !CI->isZero()) + continue; + Value *NewVal = handleZero(I, *CI, CandidateValues); + if (NewVal == nullptr) + continue; + I.setOperand(Index, NewVal); + } +} + +void DataObfuscator::doObfuseConst(Function &F, BasicBlock &B, + std::vector &FunctionLocalVariables, + std::vector &CandidateValues) { + // Record candidate values from predecessor blocks. + for (BasicBlock *PreBlock = B.getSinglePredecessor(); PreBlock != nullptr; + PreBlock = PreBlock->getSinglePredecessor()) { + // Only record values from dominated blocks. + if (PreBlock->getTerminator()->getNumSuccessors() > 1) + continue; + for (BasicBlock::iterator IT = PreBlock->getFirstInsertionPt(); + IT != PreBlock->end(); IT++) { + Instruction *I = static_cast(&*IT); + if (!needObfuse(*I)) + continue; + if (count(FunctionLocalVariables.begin(), FunctionLocalVariables.end(), I)) { + recordInst(I, CandidateValues); + } + } + } + for (BasicBlock::iterator IT = B.getFirstInsertionPt(); IT != B.end(); IT++) { + Instruction *I = static_cast(&*IT); + if (I == nullptr) + continue; + if (!needObfuse(*I)) + continue; + if (!isa(I) && !isa(I) && + !isa(I)) { + handleInstWithZero(*I, CandidateValues); + } + if (count(FunctionLocalVariables.begin(), FunctionLocalVariables.end(), I)) + recordInst(I, CandidateValues); + } +} + +void DataObfuscator::recordInst(Value *V, std::vector &CandidateValues) { + if (V != nullptr && V->getType()->isIntegerTy() && + (dyn_cast(V) == nullptr)) + CandidateValues.push_back(V); +} + +bool DataObfuscator::needObfuse(Instruction &I) { + for (unsigned int Index = 0; Index < I.getNumOperands(); Index++) { + Value *V = I.getOperand(Index); + std::string S = V->getName().str(); + if (S.find("gc.statepoint") != S.npos) + return false; + } + return true; +} + +static bool isDivInstruction(Instruction &I) { + return Instruction::isIntDivRem(I.getOpcode()) || + I.getOpcode() == Instruction::FDiv || + I.getOpcode() == Instruction::FRem; +} + +static bool blockHasDIvInstruction(BasicBlock &B) { + for (auto &I : B) { + if (isDivInstruction(I)) { + return true; + } + } + return false; +} + +static bool succHasDivInstruction(BasicBlock &B) { + unsigned int SuccNum = B.getTerminator()->getNumSuccessors(); + unsigned int Index = 0; + for (Index = 0; Index < SuccNum; Index++) { + auto Succ = B.getTerminator()->getSuccessor(Index); + if (blockHasDIvInstruction(*Succ)) { + return true; + } + } + return false; +} + +void DataObfuscator::doObfuseConst(Function &F) { + std::vector FunctionLocalVariables; + std::vector CandidateValues; + initInstList(F, FunctionLocalVariables); + for (auto &B : F) { + // Cangjie compiler frontend and backend will generate zero check for Div + // instructions. If the divisor is a non-zero constant value, the frontend + // will not generate checks for it on O2. But if the non-zero divisor is + // obfuscated, the backend will treat it as a variable and try to generate + // stackmap for the function, and this will case abort since the frontend + // does not generate stackmap for it. + if (blockHasDIvInstruction(B) || succHasDivInstruction(B)) { + continue; + } + for (auto IT = B.getFirstInsertionPt(); IT != B.end(); IT++) { + Instruction *I = static_cast(&*IT); + if (I == nullptr) + continue; + + if (!needObfuse(*I)) + continue; + + if (isa(I) || isa(I)) + continue; + if (isa(I)) { + handleConst(*I, 0); + continue; + } + for (unsigned int Index = 0; Index < I->getNumOperands(); Index++) { + handleConst(*I, Index); + } + } + // make sure CandidateValues is not empty. + CandidateValues.push_back(ConstantInt::get(Type::getInt64Ty(F.getParent()->getContext()), + this->ObfConfig->getRandNum(), false)); + doObfuseConst(F, B, FunctionLocalVariables, CandidateValues); + CandidateValues.clear(); + } + FunctionLocalVariables.clear(); +} + +void DataObfuscator::doObfuseConst(Module &M) { + for (auto &F : M) { + if (F.isDeclaration() || F.hasAvailableExternallyLinkage() != 0) + continue; + auto FName = F.getName().str(); + if (!this->ObfConfig->needObfuse(FName, "obf-const")) + continue; + doObfuseConst(F); + } +} + +PreservedAnalyses DataObfuscator::run(Module &M, ModuleAnalysisManager &AM) { + this->ObfConfig = AM.getResult(M); + if (EnableConfigObfString) { + stripGlobalString(M); + doObfuseString(M); + } + if (EnableConfigObfConst) { + doObfuseConst(M); + } + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Transforms/Obfuscator/LayoutObfuscator.cpp b/llvm/lib/Transforms/Obfuscator/LayoutObfuscator.cpp new file mode 100644 index 000000000..ba9992995 --- /dev/null +++ b/llvm/lib/Transforms/Obfuscator/LayoutObfuscator.cpp @@ -0,0 +1,557 @@ +//===---------------------- LayoutObfuscator.cpp --------------------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// The symbol name obfuscation pass. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Obfuscator/LayoutObfuscator.h" +#include + +using namespace llvm; + +extern cl::opt ObfInputSymMapFiles; +extern cl::opt ObfOutputSymMapFile; +extern cl::opt ObfUserSymMapFile; +extern cl::opt ObfSymPrefix; +extern cl::opt EnableObfExportSymol; +extern cl::opt EnableObfLineNumber; +extern cl::opt EnableObfSourcePath; + +void SymbolMapping::saveOutputMappingFile(const std::string &FileName) { + std::ofstream F(FileName, std::ios::out); + if (!F) { + report_fatal_error("Cannot create mapping file: " + StringRef(FileName)); + } + for (auto &Map : OutputMapping) { + if (Map.second.OldName == Map.second.NewName && Map.second.Path.size() == 0) { + continue; + } + F << Map.second.OldName << " " << Map.second.NewName << " " << Map.second.Path << "\n"; + } + F.close(); +} + +static std::string SplitBySeparator( + const std::string &Line, char Separator, size_t &Start, size_t &End, bool CloseBracket = false) { + std::string Ret = ""; + int ParamNotClose = 0; + int GenericNotClose = 0; + static std::vector SkipOperator = { + ".<(", + ".<<(", + ".>(", + ".>>(", + ".>=(", + ".<=(", + }; + size_t Pos = Start; + if (Start >= Line.size()) { + return Ret; + } + + while (Pos < Line.size()) { + bool Skip = false; + for (auto &SO : SkipOperator) { + if (Line.size() - Pos < SO.size()) + continue; + if (Line.compare(Pos, SO.size(), SO) == 0) { + Pos += (SO.size() - 1); // don't skip the last bracket. + Skip = true; + break; + } + } + if (Skip) { + continue; + } + if (CloseBracket && Line[Pos] == CJSymbol::ParamBrackets[0]) { + ParamNotClose++; + } else if (CloseBracket && Line[Pos] == CJSymbol::ParamBrackets[1]) { + ParamNotClose--; + } else if (CloseBracket && Line[Pos] == CJSymbol::GenericBrackets[0]) { + GenericNotClose++; + } else if (CloseBracket && Line[Pos] == CJSymbol::GenericBrackets[1]) { + GenericNotClose--; + } else if ((CloseBracket && ParamNotClose == 0 && GenericNotClose == 0 && Line[Pos] == Separator) || + (!CloseBracket && Line[Pos] == Separator)) { + End = Pos; + break; + } + Pos++; + } + + if (Pos == Line.size()) { + End = Line.size(); + } + + Ret = Line.substr(Start, End - Start); + Start = End + 1; + return Ret; +} + +void SymbolMapping::loadMappingFiles(const std::string &FileNames) { + size_t Start = 0; + size_t End = 0; + while (true) { + auto FileName = SplitBySeparator(FileNames, ',', Start, End, false); + + loadMappingFile(FileName, [&](const std::string &OldName, const std::string &NewName, const std::string &Path) { + if (OldName.size() == 0 || NewName.size() == 0) { + report_fatal_error("Invalid mapping file: " + StringRef(FileName)); + } + InputMapping[NewName] = {NewName, OldName, Path}; + }, + false); + + if (End == FileNames.size()) { + break; + } + } +} + +void SymbolMapping::loadUserMappingFile(const std::string &FileName) { + loadMappingFile(FileName, [&](const std::string &OldName, const std::string &NewName, const std::string &Path) { + if (OldName.size() == 0 || NewName.size() == 0) { + report_fatal_error("Invalid mapping file: " + StringRef(FileName)); + } + std::string Name = OldName; + while (true) { + size_t Pos = Name.find(CJSymbol::IdentSeparator, 0); + if (Pos == std::string::npos) { + break; + } + Name.replace(Pos, CJSymbol::IdentSeparator.size(), std::string(1, CJSymbol::FieldSeparator)); + } + Name.erase(std::remove(Name.begin(), Name.end(), ' '), Name.end()); + for (auto &Map : UserMapping) { + if (Map.first == Name) { + report_fatal_error("Duplicate symbol: " + StringRef(OldName)); + } + if (Map.second == NewName) { + report_fatal_error("Duplicate symbol: " + StringRef(NewName)); + } + } + UserMapping[Name] = NewName; + }, + true); +} + +void SymbolMapping::loadMappingFile( + const std::string &FileName, + const std::function &UpdateMapping, + const bool CloseBracket) { + std::ifstream InputFile(FileName); + std::string Line; + size_t Start = 0; + size_t End = 0; + bool Success = true; + if (!InputFile.is_open()) { + report_fatal_error("Cannot open file: " + StringRef(FileName)); + return; + } + + while (getline(InputFile, Line)) { + std::string SLine = ""; + for (auto C : Line) { + if (C != '\n' && C != '\r') + SLine += C; + } + if (SLine == "") + continue; + + Start = 0; + End = 0; + std::string OldName = SplitBySeparator(SLine, ' ', Start, End, CloseBracket); + std::string NewName = SplitBySeparator(SLine, ' ', Start, End, CloseBracket); + std::string Path = SplitBySeparator(SLine, ' ', Start, End, CloseBracket); + + if (NewName.size() == 0 || OldName.size() == 0) { + Success = false; + break; + } + UpdateMapping(OldName, NewName, Path); + } + + if (!Success) { + report_fatal_error("Invalid mapping file: " + StringRef(FileName)); + } +} + +void SymbolMapping::addToOutputMapping( + const std::string &OldName, const std::string &NewName, const std::string &Path) { + OutputMapping[OldName] = {NewName, OldName, Path}; +} + +bool SymbolMapping::isObfuscatedSymbol(const std::string &Name) { + return OutputMapping.find(Name) != OutputMapping.end(); +} + +static std::string trimDemangleName(std::string Name) { + if (!ObfConfig::isValidMangleName(Name)) { + return Name; + } + + auto DName = ObfConfig::getDemangleName(Name); + while (true) { + size_t Pos = DName.find(CJSymbol::IdentSeparator, 0); + if (Pos == std::string::npos) { + break; + } + DName.replace(Pos, CJSymbol::IdentSeparator.size(), std::string(1, CJSymbol::FieldSeparator)); + } + DName.erase(std::remove(DName.begin(), DName.end(), ' '), DName.end()); + return DName; +} + +std::string SymbolMapping::getMappedName(const std::string &Name) { + for (auto &Map : InputMapping) { + if (Map.second.OldName == Name) { + return Map.second.NewName; + } + } + std::string DemangleName = trimDemangleName(Name); + if (UserMapping.find(DemangleName) != UserMapping.end()) { + return UserMapping[DemangleName]; + } + return ""; +} + +LayoutObfuscator::~LayoutObfuscator() {} + +std::string LayoutObfuscator::generateUniqueName(void) { + while (true) { + unsigned int RD = this->ObfConfig->getRandNum() % LIST_LENGTH; + std::string Name(1, char('a' + RD)); + Name = ObfSymPrefix + Name; + if (IndexList[RD] != -1) { + Name = Name + (std::to_string(IndexList[RD])); + } + IndexList[RD]++; + for (auto &Map : SymbolMap.UserMapping) { + if (Map.first == Name || Map.second == Name) + continue; + } + if (AllSymbols.count(Name) == 0 && !SymbolMap.isObfuscatedSymbol(Name)) { + return Name; + } + } +} + +void LayoutObfuscator::doObfuscation(GlobalVariable &G) { + if (!needObfuse(G)) { + return; + } + + std::string NewName; + std::string OldName = G.getName().str(); + std::string MappedName = SymbolMap.getMappedName(OldName); + if (MappedName.size() != 0) { + NewName = MappedName; + } else { + NewName = generateUniqueName(); + } + G.setName(NewName); + + if (llvm::GlobalVariable::isExternalLinkage(G.getLinkage())) { + SymbolMap.addToOutputMapping(OldName, NewName); + } + AllSymbols.erase(OldName); + AllSymbols.insert(NewName); +} + +bool LayoutObfuscator::isPublicGlobal(const GlobalVariable &G) { + return !G.hasInitializer() || G.hasExternalLinkage() || G.hasWeakODRLinkage() || G.hasLinkOnceLinkage(); +} + +bool LayoutObfuscator::needObfuse(const GlobalVariable &G) { + std::string GlobalName = G.getName().str(); + std::vector AvoidAttribute = { + "cj-native", + "CFileReflect", + "CFileKlass", + "CFileVTable", + "CFileMTable", + "CFileStaticGenericTI", + }; + if (GlobalName.empty() || G.getName().startswith("llvm.")) { + return false; + } + if (G.getName().endswith(".ti") || G.getName().endswith(".tt")) { + return false; + } + if (G.getName().equals("cj.sdk.version")) { + return false; + } + if (SymbolMap.isObfuscatedSymbol(GlobalName)) { + return false; + } + if (!EnableObfExportSymol && isPublicGlobal(G)) { + return false; + } + + for (auto Attr : AvoidAttribute) { + if (G.hasAttribute(Attr)) { + return false; + } + } + + std::string DN = trimDemangleName(GlobalName); + for (auto &IS : InternalSymbols) { + if (DN == IS) { + return false; + } + } + + // The global is from other package and has no mapping. + if (SymbolMap.getMappedName(GlobalName).empty() && G.isDeclaration()) { + return false; + } + + if (!this->ObfConfig->needObfuse(GlobalName, "obf-layout")) { + return false; + } + return true; +} + +bool LayoutObfuscator::isPublicFunction(const Function &F) { + return (F.isDeclaration() || !F.hasInternalLinkage() || + F.hasAvailableExternallyLinkage()); +} + +bool LayoutObfuscator::needObfuse(const Function &F) { + std::vector ReserveVec = {"main"}; + std::string FuncName = F.getName().str(); + std::vector AvoidAttribute = { + "c2cj", + "cj2c", + "cjstub", + }; + + if (FuncName.size() == 0 || F.getName().startswith("llvm.")) { + return false; + } + + if (F.isDeclaration() && SymbolMap.getMappedName(FuncName).size() == 0) { + return false; + } + + if (F.hasPrivateLinkage()) { + return false; + } + + if (!EnableObfExportSymol && isPublicFunction(F)) { + return false; + } + + if (SymbolMap.isObfuscatedSymbol(FuncName)) { + return false; + } + + for (auto R : ReserveVec) { + if (R == FuncName) + return false; + } + for (auto Attr : AvoidAttribute) { + if (F.hasFnAttribute(Attr)) + return false; + } + + std::string DN = trimDemangleName(FuncName); + for (auto &IS : InternalSymbols) { + if (DN == IS) + return false; + } + + if (!this->ObfConfig->needObfuse(FuncName, "obf-layout")) { + return false; + } + + return true; +} + +void LayoutObfuscator::doObfuscation(Function &F) { + if (!needObfuse(F)) + return; + std::string NewName; + std::string OldName = F.getName().str(); + std::string MappedName = SymbolMap.getMappedName(OldName); + if (MappedName.size() != 0) { + NewName = MappedName; + } else { + NewName = generateUniqueName(); + } + F.setName(NewName); + SymbolMap.addToOutputMapping(OldName, NewName); + AllSymbols.erase(OldName); + AllSymbols.insert(NewName); +} + +void LayoutObfuscator::reorderFunctions(Module &M) { + std::vector FuncVec; + std::vector IndexVec; + int FuncNum = 0; + int Index = 0; + + for (auto &F : M) { + FuncVec.push_back(&F); + IndexVec.push_back(FuncNum); + FuncNum++; + } + + for (int Idx = 0; Idx < FuncNum; Idx++) { + M.getFunctionList().remove(FuncVec[Idx]); + } + + for (int Count = 0; Count < FuncNum; Count++) { + Index = this->ObfConfig->getRandNum() % IndexVec.size(); + Function *F = FuncVec[IndexVec[Index]]; + M.getFunctionList().push_back(F); + IndexVec.erase(IndexVec.begin() + Index); + } +} + +void LayoutObfuscator::recordDebugInfo(const Module &M) { +#if defined(_WIN32) + const char *Separator = "\\"; +#else + const char *Separator = "/"; +#endif + for (auto &F : M) { + MDNode *N = F.getMetadata("dbg"); + if (N == nullptr) { + continue; + } + + auto DS = dyn_cast(N); + if (DS == nullptr) { + continue; + } + std::string Path = DS->getFile()->getFilename().str(); + std::string Dir = DS->getFile()->getDirectory().str(); + if (Path.size() == 0) { + continue; + } + for (auto &Map : SymbolMap.OutputMapping) { + if (Map.second.NewName == F.getName().str() || Map.second.OldName == F.getName().str()) { + Map.second.Path = Dir + Separator + Path; + } + } + } +} + +void LayoutObfuscator::obfuscateDebugInfo(Module &M) { + for (auto &F : M) { + for (auto I = inst_begin(F); I != inst_end(F); I++) { + MDNode *N = I->getMetadata("dbg"); + if (N == nullptr) { + continue; + } + auto DL = dyn_cast(N); + + if (EnableObfSourcePath && DL->getFile()->getDirectory() != OBF_DEBUG_DIR_NAME) { + DL->getFile()->replaceOperandWith(1, MDString::get(M.getContext(), OBF_DEBUG_DIR_NAME)); + } + if (EnableObfSourcePath && DL->getFile()->getFilename() != OBF_DEBUG_FILE_NAME) { + DL->getFile()->replaceOperandWith(0, MDString::get(M.getContext(), OBF_DEBUG_FILE_NAME)); + } + + if (EnableObfLineNumber) { + int NewNo = OBF_DEBUG_LINE_NO; + auto NewDL = DILocation::get( + DL->getContext(), NewNo, NewNo, DL->getScope(), DL->getInlinedAt(), DL->isImplicitCode()); + I->setMetadata("dbg", NewDL); + } + } + } +} + +void LayoutObfuscator::recordAllSymbols(const Module &M) { + for (auto &F : M) { + AllSymbols.insert(F.getName().str()); + } + + for (auto &G : M.globals()) { + AllSymbols.insert(G.getName().str()); + } + + for (auto &Map : SymbolMap.InputMapping) { + AllSymbols.insert(Map.second.NewName); + AllSymbols.insert(Map.second.OldName); + } + for (auto &Map : SymbolMap.UserMapping) { + AllSymbols.insert(Map.first); + AllSymbols.insert(Map.second); + } +} + +void LayoutObfuscator::getCurrentPackage(const Module &M) { + for (auto &F : M) { + MDNode *N = F.getMetadata("dbg"); + if (N == nullptr) { + continue; + } + auto DIS = dyn_cast(N); + if (DIS == nullptr) { + continue; + } + auto DNS = dyn_cast(DIS->getScope()); + if (DNS == nullptr) { + continue; + } + std::string Package = DNS->getName().str(); + CurrentPackage = Package; + break; + } +#if defined(_WIN32) + const char *Separator = "\\"; +#else + const char *Separator = "/"; +#endif + while (true) { + size_t Pos = CurrentPackage.find(Separator, 0); + if (Pos == std::string::npos) { + break; + } + CurrentPackage.replace(Pos, 1, std::string(1, CJSymbol::FieldSeparator)); + } +} + +PreservedAnalyses LayoutObfuscator::run(Module &M, ModuleAnalysisManager &AM) { + this->ObfConfig = AM.getResult(M); + getCurrentPackage(M); + if (ObfOutputSymMapFile.size() == 0) { + ObfOutputSymMapFile = CurrentPackage + ".obf.map"; + } + if (ObfInputSymMapFiles.size() != 0) + SymbolMap.loadMappingFiles(ObfInputSymMapFiles); + if (ObfUserSymMapFile.size() != 0) + SymbolMap.loadUserMappingFile(ObfUserSymMapFile); + recordAllSymbols(M); + for (auto I = 0; I < LIST_LENGTH; I++) { + IndexList[I] = -1; + } + for (auto &F : M) + doObfuscation(F); + for (auto &G : M.globals()) + doObfuscation(G); + recordDebugInfo(M); + obfuscateDebugInfo(M); + reorderFunctions(M); + SymbolMap.saveOutputMappingFile(ObfOutputSymMapFile); + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Transforms/Scalar/CJBarrierOpt.cpp b/llvm/lib/Transforms/Scalar/CJBarrierOpt.cpp new file mode 100644 index 000000000..f1a224b86 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/CJBarrierOpt.cpp @@ -0,0 +1,826 @@ +//===- CJBarrierOpt.cpp - optimize cangjie write barriers -----------------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This pass optimizes cangjie write barriers, including barrier split, +// barrier remove and barrier verifier. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/CJBarrierOpt.h" + +#include "llvm/ADT/SetVector.h" +#include "llvm/Analysis/CFG.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GetElementPtrTypeIterator.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Operator.h" +#include "llvm/IR/PassManager.h" +#include "llvm/IR/SafepointIRVerifier.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Scalar/CJFillMetadata.h" +#include + +using namespace llvm; + +static cl::opt CJArrayThreshold( + // 128 is cj-array-threshol default Initial Value + "cj-array-threshold", cl::Hidden, cl::init(128 * 1024), + cl::desc("When array size is bigger than cj-array-threshold " + "and array element type is primitive type, " + "its write barriers can be removed.")); + +const static DenseSet NewMallocArrayFunc { + "CJ_MCC_NewArray", "CJ_MCC_NewArray8", "CJ_MCC_NewArray16", + "CJ_MCC_NewArray32", "CJ_MCC_NewArray64", "CJ_MCC_NewObjArray" +}; + +const static DenseSet NewMallocObjFunc { + "CJ_MCC_NewFinalizer", "CJ_MCC_NewObject", + "CJ_MCC_NewPinnedObject"}; + +static bool isNewMallocCall(CallBase *CB) { + if (CB->getCalledFunction() == nullptr) { + return false; + } + return NewMallocArrayFunc.find(CB->getCalledFunction()->getName()) != + NewMallocArrayFunc.end() || + NewMallocObjFunc.find(CB->getCalledFunction()->getName()) != + NewMallocObjFunc.end(); +} + +class BarrierNeed { +public: + explicit BarrierNeed(Instruction *Begin, Instruction *End, LoopInfo *LI, + DenseMap &BBSafepointMaps) + : Begin(Begin), End(End), BeginLoop(LI->getLoopFor(Begin->getParent())), + EndLoop(LI->getLoopFor(End->getParent())), LI(LI), + BBSafepointMaps(BBSafepointMaps) { + SameParentLoop = getSameParentLoop(BeginLoop); + }; + ~BarrierNeed() = default; + + bool run() { + if (isBeginSingleLoop()) { + return true; + } + SmallVector LoopLatches; + if (SameParentLoop != nullptr) { + SameParentLoop->getLoopLatches(LoopLatches); + } + Worklist.insert(Begin->getParent()); + BasicBlock *EndBB = End->getParent(); + while (!Worklist.empty()) { + BasicBlock *BB = Worklist.pop_back_val(); + if (BBSafepointFlow.count(EndBB) > 0) { + return BBSafepointFlow[EndBB]; + } + if (BBSafepointFlow.count(BB) > 0) { + continue; + } + if ((BB == End->getParent()) && (scanEndBB())) { + return BBSafepointFlow[EndBB]; + } else if (BB == Begin->getParent()) { + scanBeginBB(); + } else { + if (!calculateSafepoint(BB, BBSafepointMaps[BB])) { + continue; + } + } + if (SameParentLoop != nullptr && isLoopLatch(BB, LoopLatches)) { + continue; + } + scanSuccPath(BB); + } + return (BBSafepointFlow.find(End->getParent()) == BBSafepointFlow.end()); + } + +private: + Instruction *Begin; + Instruction *End; + Loop *BeginLoop; + Loop *EndLoop; + Loop *SameParentLoop; + LoopInfo *LI; + SetVector Worklist; + // Calculate if path have a safepoint. + // If this BB has safepoint depends on himself and its predecessors in path. + DenseMap BBSafepointFlow; + DenseMap BBSafepointMaps; + + // Judge if Begin is in a loop alone or Beginloop is a subloop of Endloop. + // In these cases, we need to judge if Beginloop has safepoint. + bool isBeginSingleLoop() { + // Begin is not in a loop. + if (BeginLoop == nullptr) { + return false; + } + // Begin is in a loop, End is not in a loop. + if (EndLoop == nullptr) { + return true; + } + // Endloop is a subloop of Beginloop. + if (BeginLoop->contains(EndLoop)) { + return false; + } + // Beginloop is subloop of Endloop. + // Or Beginloop and Endloop are two completely unrelated loops. + return true; + } + + // Judge if BeginLoop and EndLoop are in a same parent loop. + // In this case, we only need to scan the parent loop, + // and when we meet the looplatch of the parent loop, break. + Loop *getSameParentLoop(Loop *L) { + if (L == nullptr) { + return nullptr; + } + if (L->contains(EndLoop)) { + return L; + } + return getSameParentLoop(L->getParentLoop()); + } + + // Judge if we have deal with this BB or this BB is in path to End. + bool isNotDealPathSucc(BasicBlock *BB) { + BasicBlock *EndBB = End->getParent(); + if (BB->isLandingPad()) { + return false; + } + if (Worklist.contains(BB)) { + return false; + } + if (BBSafepointFlow.count(BB) > 0) { + return false; + } + return isPotentiallyReachable(BB, EndBB); + } + + void scanSuccPath(BasicBlock *BB) { + for (auto Succ : successors(BB)) { + if (!isNotDealPathSucc(Succ)) { + continue; + } + Loop *SuccLoop = LI->getLoopFor(Succ); + // Judge if Succ is a node of the same big loop. + // If Succ is a node of the same big loop, Succ is a node of path. + // Otherwise, SuccLoop is a loop node of path. + if ((SuccLoop != nullptr && SameParentLoop != nullptr && + SuccLoop != SameParentLoop) || + (SuccLoop != nullptr && SameParentLoop == nullptr)) { + scanPathLoop(SuccLoop); + continue; + } + Worklist.insert(Succ); + } + } + + void scanBeginBB() { + bool IsSafepoint = false; + BasicBlock *BeginBB = Begin->getParent(); + for (auto It = Begin->getIterator(), E = BeginBB->end(); It != E; ++It) { + if (auto CB = dyn_cast(&*It)) { + if (isSafepointCall(CB)) { + IsSafepoint = true; + } + } + if (It->isTerminator()) { + calculateSafepoint(BeginBB, IsSafepoint); + } + } + } + + bool scanEndBB() { + bool IsSafepoint = false; + BasicBlock *BeginBB = Begin->getParent(); + BasicBlock *EndBB = End->getParent(); + for (auto It = EndBB == BeginBB ? Begin->getIterator() : EndBB->begin(), + E = EndBB->end(); + It != E; ++It) { + if (It == End->getIterator()) { + return calculateSafepoint(End->getParent(), IsSafepoint); + } + if (auto CB = dyn_cast(&*It)) { + if (isSafepointCall(CB)) { + IsSafepoint = true; + } + } + } + return false; + } + + void scanPathLoop(Loop *CurLoop) { + bool IsSafepointLoop = false; + for (auto BB : CurLoop->getBlocks()) { + IsSafepointLoop |= BBSafepointMaps[BB]; + } + scanPreForSafepoint(CurLoop->getHeader(), IsSafepointLoop, CurLoop); + for (auto BB : CurLoop->getBlocks()) { + BBSafepointFlow[BB] = IsSafepointLoop; + Worklist.insert(BB); + } + SmallVector ExitBlocks; + CurLoop->getExitBlocks(ExitBlocks); + for (auto Exit : ExitBlocks) { + if (!isNotDealPathSucc(Exit)) { + continue; + } + Worklist.insert(Exit); + } + } + + bool calculateSafepoint(BasicBlock *BB, bool &IsSafepoint) { + BasicBlock *BeginBB = Begin->getParent(); + if (BB == BeginBB) { + BBSafepointFlow[BB] = IsSafepoint; + return true; + } + if (!scanPreForSafepoint(BB, IsSafepoint, nullptr)) { + return false; + } + BBSafepointFlow[BB] = IsSafepoint; + return true; + } + + bool scanPreForSafepoint(BasicBlock *BB, bool &IsSafepoint, Loop *L) { + BasicBlock *BeginBB = Begin->getParent(); + for (auto Pre : predecessors(BB)) { + if (L != nullptr && L->contains(Pre)) { + continue; + } + if (!isPotentiallyReachable(BeginBB, Pre)) { + continue; + } + if (BBSafepointFlow.find(Pre) == BBSafepointFlow.end()) { + return false; + } + IsSafepoint |= BBSafepointFlow[Pre]; + } + return true; + } + + bool isLoopLatch(BasicBlock *BB, SmallVector &LoopLatches) { + for (auto LoopLatch : LoopLatches) { + if (BB == LoopLatch) { + return true; + } + } + return false; + } +}; + +class BarriersCheck { +public: + BarriersCheck(Function *F, LoopInfo *LI) : F(F), LI(LI), + DL(F->getParent()->getDataLayout()) {}; + ~BarriersCheck() = default; + + bool run() { + bool Changed = false; + initData(); + + for (auto SI : StoreSet) { + Type *Ty = SI->getValueOperand()->stripPointerCasts()->getType(); + if (!isGCPointerType(Ty)) + continue; + + if (barriersCheckFail(SI->getPointerOperand(), SI)) { + report_fatal_error("Need write barrier in function: " + + Twine(F->getName()) + "!!!"); + } + } + + for (auto Mem : MemSet) { + if (!verifierMemcpy(Mem)) { + report_fatal_error("Need write barrier in function: " + + Twine(F->getName()) + "!!!"); + } + } + + for (auto Barrier : BarrierSet) { + if (optStructBarrier(Barrier) || optStackBarrier(Barrier)) { + Changed = true; + continue; + } + } + + return Changed; + } + +private: + Function *F; + LoopInfo *LI; + const DataLayout &DL; + // Record gcwrite and gcwrite.agg + DenseSet BarrierSet; + // Record store insts + DenseSet StoreSet; + // Record memcpy and memmove insts + DenseSet MemSet; + // Record defs and users + DenseMap> UsesMaps; + // Record if there is safepoint in each BB + DenseMap BBSafepointMaps; + + bool verifierMemcpy(CallBase *Mem) { + auto GetBeginOffset = [&](Value *V, uint64_t &BeginPos) { + APInt Offset(64, 0); + Value *Base = V->stripAndAccumulateConstantOffsets(DL, Offset, true); + BeginPos = Offset.getZExtValue(); + StructType *ST = dyn_cast( + Base->getType()->getNonOpaquePointerElementType()); + return ST; + }; + + Value *Dst = Mem->getArgOperand(0); + Value *Src = Mem->getArgOperand(1); + StructType *ST = nullptr; + uint64_t BeginPos; + if (StructType *DstST = GetBeginOffset(Dst, BeginPos)) { + ST = DstST; + } else if (StructType *SrcST = GetBeginOffset(Src, BeginPos)) { + ST = SrcST; + } else { + return true; + } + uint64_t EndPos; + if (auto Constant = dyn_cast(Mem->getArgOperand(2))) { + EndPos = BeginPos + Constant->getZExtValue(); + } else { + EndPos = DL.getStructLayout(ST)->getSizeInBytes(); + } + if (!containsRefInStruct(ST, BeginPos, EndPos)) { + return true; + } + return !barriersCheckFail(Mem->getArgOperand(0), Mem); + } + + bool isStackBarrier(CallBase *Barrier) { + switch (Barrier->getIntrinsicID()) { + case Intrinsic::cj_gcwrite_ref: + case Intrinsic::cj_gcread_struct: + return isa(Barrier->getArgOperand(1)->stripPointerCasts()); + case Intrinsic::cj_gcwrite_struct: + case Intrinsic::cj_gcread_ref: + return isa(Barrier->getArgOperand(0)->stripPointerCasts()); + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + return isa(Barrier->getArgOperand(0)->stripPointerCasts()) && + isa(Barrier->getArgOperand(2)->stripPointerCasts()); + default: + return false; + } + } + + bool optStackBarrier(CallBase *Barrier) { + if (!isStackBarrier(Barrier)) + return false; + replaceBarrier(Barrier); + return true; + } + + void replaceBarrier(CallBase *Barrier) { + IRBuilder<> Builder(Barrier); + Builder.SetInsertPoint(Barrier); + createStoreOrMems(Barrier, Builder); + Barrier->eraseFromParent(); + } + + bool argumentCheck(Function *Callee, unsigned ArgNo, + DenseSet> &CallSets, + const std::function &Check) { + for (Use &U : Callee->uses()) { + CallBase *CB = dyn_cast(U.getUser()); + // Must be a direct call. + if (CB == nullptr || !CB->isCallee(&U) || + CB->getFunctionType() != F->getFunctionType()) + return false; + + if (CallSets.contains({CB, ArgNo})) { + continue; + } + CallSets.insert({CB, ArgNo}); + if (Check(CB->getArgOperand(ArgNo))) + continue; + + auto Arg = + dyn_cast(findMemoryBasePointer(CB->getArgOperand(ArgNo))); + if (Arg == nullptr || !CB->getFunction()->hasLocalLinkage()) { + return false; + } + unsigned CallerArgNo = Arg->getArgNo(); + if (argumentCheck(CB->getFunction(), CallerArgNo, CallSets, Check)) { + continue; + } + return false; + } + return true; + } + + std::pair getBaseAndFieldPtrByBarrier(CallBase *Barrier) { + Value *BasePtr = nullptr; + Value *FieldPtr = nullptr; + switch (Barrier->getIntrinsicID()) { + default: + report_fatal_error("Unsupported Cangjie Barrier Intrinsic!"); + break; + // Value, BaseObj, FieldPtr + case Intrinsic::cj_gcwrite_ref: + // DstPtr, BaseObj, SrcPtr, Size + case Intrinsic::cj_gcread_struct: + BasePtr = Barrier->getArgOperand(1); + FieldPtr = Barrier->getArgOperand(2); + break; + // BaseObj, FieldPtr + case Intrinsic::cj_gcread_ref: + // BaseObj, DstPtr, SrcPtr, Size + case Intrinsic::cj_gcwrite_struct: + BasePtr = Barrier->getArgOperand(0); + FieldPtr = Barrier->getArgOperand(1); + break; + // DstBaseObj, DstFieldPtr, SrcBaseObj, SrcFieldPtr, Size + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + BasePtr = Barrier->getArgOperand(0); + break; + } + return {getUnderlyingObject(BasePtr), + FieldPtr ? getUnderlyingObject(FieldPtr) : nullptr}; + } + + bool optStructBarrier(CallBase *Barrier) { + auto PtrCheck = [this](Value *Ptr, + const std::function &Check) { + if (Check(Ptr)) + return true; + + // Ptr is argument, check the parameters transferred by the caller. + auto *Arg = dyn_cast(findMemoryBasePointer(Ptr)); + if (Arg == nullptr || !F->hasLocalLinkage()) + return false; + DenseSet> CallSets; + if (!argumentCheck(F, Arg->getArgNo(), CallSets, Check)) { + return false; + } + return true; + }; + + auto [BasePtr, FieldPtr] = getBaseAndFieldPtrByBarrier(Barrier); + if (BasePtr && FieldPtr) { + bool IsBaseNull = PtrCheck(BasePtr, [](Value *Ptr) { + Value *BP = Ptr->stripPointerCasts(); + auto *Const = dyn_cast(BP); + if (Const != nullptr && Const->isNullValue()) + return true; + return false; + }); + bool IsFieldNotGV = PtrCheck(FieldPtr, [](Value *Ptr) { + Value *BP = Ptr->stripPointerCasts(); + return !isa(BP) && !isa(BP); + }); + // gcread/gcwrite to static pointer cannot be replace, even if base is + // nullptr. + if (IsBaseNull && IsFieldNotGV) { + replaceBarrier(Barrier); + return true; + } + } + + if (Barrier->getIntrinsicID() == Intrinsic::cj_gcwrite_struct) { + auto Src = Barrier->getArgOperand(2)->stripPointerCasts(); + // 64 is 64-bit integer + APInt Offset(64, -1); + StructType *ST = nullptr; + auto Size = dyn_cast(Barrier->getArgOperand(3)); + if (auto AI = dyn_cast(Src)) { + Offset = 0; + ST = dyn_cast(AI->getAllocatedType()); + } else if (auto GEP = dyn_cast(Src)) { + if (!GEP->accumulateConstantOffset(DL, Offset)) + Offset = -1; + ST = dyn_cast(GEP->getPointerOperand() + ->getType() + ->getNonOpaquePointerElementType()); + } else if (auto Arg = dyn_cast(Src)) { + Offset = 0; + ST = dyn_cast( + Arg->getType()->getNonOpaquePointerElementType()); + } + if (Offset != -1 && ST != nullptr && Size != nullptr && + !containsRefInStruct(ST, Offset.getSExtValue(), + Offset.getSExtValue() + Size->getSExtValue())) { + replaceBarrier(Barrier); + return true; + } + } + + return false; + } + + // Normally, we need to scan from the next node of CB. + // When CB is terminator, it does not have next node, + // so we need to scan from the begin of next BB. + Instruction *getNextNode(Instruction *Origin) { + if (Origin->isTerminator()) { + auto Invoke = dyn_cast(Origin); + assert(Invoke != nullptr && "Call can not be terminator!"); + // When the base is invoke, scan from normal BB. + return (&*Invoke->getNormalDest()->begin()); + } else { + // Normally, scan from the next node. + return (Origin->getNextNode()); + } + } + + void fillUses(SetVector &Uses, Value *Val) { + Uses.insert(Val); + for (unsigned It = 0; It < Uses.size(); ++It) { + Value *V = Uses[It]; + Uses.insert(V); + for (auto Use : V->users()) { + auto I = dyn_cast(Use); + switch (I->getOpcode()) { + default: + break; + case Instruction::BitCast: + case Instruction::AddrSpaceCast: + case Instruction::GetElementPtr: + case Instruction::PHI: + case Instruction::Select: + Uses.insert(I); + break; + } + } + } + } + + void initData() { + for (auto &Arg : F->args()) { + fillUses(UsesMaps[&Arg], &Arg); + } + + for (auto &BB : *F) { + bool IsSafepointBB = false; + for (auto &I : BB) { + if (isa(&I) && containsGCPtrType(I.getType())) { + fillUses(UsesMaps[&I], &I); + } + if (auto SI = dyn_cast(&I)) { + StoreSet.insert(SI); + } + CallBase *CB = dyn_cast(&I); + if (CB == nullptr) { + continue; + } + if (isSafepointCall(CB)) { + IsSafepointBB = true; + } + + switch (CB->getIntrinsicID()) { + default: + break; + case Intrinsic::cj_gcwrite_ref: + case Intrinsic::cj_gcwrite_struct: + case Intrinsic::cj_gcread_ref: + case Intrinsic::cj_gcread_struct: + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + BarrierSet.insert(CB); + continue; + case Intrinsic::memcpy: + case Intrinsic::memmove: + MemSet.insert(CB); + continue; + } + + if (!containsGCPtrType(CB->getType()) && + !isMemoryContainsGCPtrType(CB->getType())) { + continue; + } + fillUses(UsesMaps[CB], CB); + } + BBSafepointMaps[&BB] = IsSafepointBB; + } + } + + bool containsRefInArray(ArrayType *AT, uint64_t Start, uint64_t End, + uint64_t CurPos) { + bool Result = false; + Type *ElementTy = AT->getElementType(); + if (isa(ElementTy)) { + assert(isGCPointerType(ElementTy) && + "The elements of array can only be gc pointers."); + return true; + } else if (auto AST = dyn_cast(ElementTy)) { + for (unsigned Idx = 0; Idx < AT->getNumElements(); Idx++) { + Result |= containsRefInStruct(AST, Start, End, CurPos); + } + } + return Result; + } + + bool containsRefInStruct(StructType *ST, uint64_t Start, uint64_t End, + uint64_t CurPos = 0) { + const StructLayout *Layout = DL.getStructLayout(ST); + bool Result = false; + for (uint32_t STNum = 0; STNum < ST->getNumElements(); STNum++) { + uint64_t Pos = Layout->getElementOffset(STNum) + CurPos; + if (Pos < Start) + continue; + + if (Pos >= End) + return Result; + + auto ET = ST->getElementType(STNum); + if (isGCPointerType(ET)) { + return true; + } else if (auto EST = dyn_cast(ET)) { + Result |= containsRefInStruct(EST, Start, End, Pos); + } else if (auto EAT = dyn_cast(ET)) { + Result |= containsRefInArray(EAT, Start, End, Pos); + } + } + return Result; + } + + bool tryCheckMemContainsRef(MemIntrinsic *Mem) { + auto SizeOp = dyn_cast(Mem->getLength()); + if (!SizeOp) + return false; + + uint64_t Size = SizeOp->getZExtValue(); + Value *Src = Mem->getArgOperand(1)->stripPointerCasts(); + Value *Dst = Mem->getArgOperand(0)->stripPointerCasts(); + Type *SrcTy = Src->getType()->getNonOpaquePointerElementType(); + Type *DstTy = Dst->getType()->getNonOpaquePointerElementType(); + if (SrcTy->isStructTy() && + containsRefInStruct(cast(SrcTy), 0, Size)) + return true; + + if (DstTy->isStructTy() && + containsRefInStruct(cast(DstTy), 0, Size)) + return true; + + APInt Offset(DL.getIndexSizeInBits(0), 0); + Value *PtrBase = Src->stripAndAccumulateConstantOffsets(DL, Offset, false); + uint64_t Off = Offset.getZExtValue(); + uint64_t End = Off + Size; + Type *BaseTy = PtrBase->getType()->getNonOpaquePointerElementType(); + if (BaseTy->isStructTy() && + containsRefInStruct(cast(BaseTy), Off, End)) + return true; + + return false; + } + + // Returns true when should be a barrier, false otherwise. Note that you need + // to check whether the assignment instruction contains a ref. + bool barriersCheckFail(Value *Op, Instruction *Cur) { + SetVector Origins; + SetVector Cache; + // When the base is not gc pointer, do not need write barrier + if (!isGCPointerOrGlobal(Op, Origins, Cache)) { + return false; + } + + if (isa(Cur)) { + // When base is global or gcpointer, always need write barrier. + return true; + } + + MemIntrinsic *MI = dyn_cast(Cur); + assert(MI && "It should be a memory instruction."); + bool ContainsRef = tryCheckMemContainsRef(MI); + if (!ContainsRef) { + return false; + } + + bool NeedBarrier = false; + for (auto Origin : Origins) { + if (isa(Origin)) + continue; + auto *CB = dyn_cast(Origin); + if (!CB || !isNewMallocCall(CB)) + return true; + + Instruction *Begin = getNextNode(CB); + BarrierNeed BN(Begin, Cur, LI, BBSafepointMaps); + NeedBarrier |= BN.run(); + } + + return NeedBarrier; + } + + bool isGCPointerOrGlobal(Value *Val, SetVector &Origins, + SetVector &Cache) { + assert(Val != nullptr && "Illegal to ask for the type of nullptr"); + assert(Val->getType()->isPointerTy() && "should be a pointer type"); + + bool Result = false; + Val = Val->stripPointerCasts(); + if (Cache.contains(Val)) + return Result; + + Cache.insert(Val); + if (auto GEP = dyn_cast(Val)) { + Result |= isGCPointerOrGlobal(GEP->getPointerOperand(), Origins, Cache); + } else if (auto PI = dyn_cast(Val)) { + for (Value *PIVal : PI->incoming_values()) { + Result |= isGCPointerOrGlobal(PIVal, Origins, Cache); + } + } else if (auto SI = dyn_cast(Val)) { + Result |= isGCPointerOrGlobal(SI->getTrueValue(), Origins, Cache); + Result |= isGCPointerOrGlobal(SI->getFalseValue(), Origins, Cache); + } else { + Result |= (isInt8AS1Pty(Val->getType()) || isa(Val)); + Origins.insert(Val); + } + + return Result; + } +}; + +static bool optCJBarrierModule(Module &M, + function_ref GetLI) { + bool Changed = false; + + for (Function &F : M) { + if (F.isDeclaration()) { + continue; + } + if (F.hasCangjieGC()) { + auto &LI = GetLI(F); + BarriersCheck BC(&F, &LI); + Changed |= BC.run(); + } + } + return Changed; +} + +PreservedAnalyses CJBarrierOpt::run(Module &M, ModuleAnalysisManager &AM) const { + FunctionAnalysisManager &FAM = + AM.getResult(M).getManager(); + auto GetLoopInfo = [&FAM](Function &F) -> LoopInfo & { + return FAM.getResult(F); + }; + if (optCJBarrierModule(M, GetLoopInfo)) { + return PreservedAnalyses::none(); + } + return PreservedAnalyses::all(); +} + +namespace { +class CJBarrierOptLegacyPass : public ModulePass { +public: + static char ID; + + explicit CJBarrierOptLegacyPass() : ModulePass(ID) { + initializeCJBarrierOptLegacyPassPass( + *PassRegistry::getPassRegistry()); + } + ~CJBarrierOptLegacyPass() = default; + + bool runOnModule(Module &M) override { + bool Changed = false; + auto GetLoopInfo = [this, &Changed](Function &F) -> LoopInfo & { + return this->getAnalysis(F, &Changed).getLoopInfo(); + }; + return optCJBarrierModule(M, GetLoopInfo); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addPreserved(); + } +}; +} // namespace + +char CJBarrierOptLegacyPass::ID = 0; + +ModulePass *llvm::createCJBarrierOptLegacyPass() { + return new CJBarrierOptLegacyPass(); +} + +INITIALIZE_PASS_BEGIN(CJBarrierOptLegacyPass, "cj-barrier-opt", + "CJ Barrier Opt", false, false) +INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) +INITIALIZE_PASS_END(CJBarrierOptLegacyPass, "cj-barrier-opt", + "CJ Barrier Opt", false, false) diff --git a/llvm/lib/Transforms/Scalar/CJBarrierSplit.cpp b/llvm/lib/Transforms/Scalar/CJBarrierSplit.cpp new file mode 100644 index 000000000..f55d9b667 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/CJBarrierSplit.cpp @@ -0,0 +1,473 @@ +//===- CJBarrierSplit.cpp ---------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This pass optimizes the barriers, including splitting the struct barrier. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/CJBarrierSplit.h" + +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/CJIntrinsics.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/SafepointIRVerifier.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Scalar/InsertCJTBAA.h" +#include + +using namespace llvm; +using namespace cangjie; + +namespace { +class SplitBarrier { +public: + struct SplitPosion { + // the split begin position relative to read/write start. + uint64_t SplitBeginPos; + // the split end position relative to read/write start. + uint64_t SplitEndPos; + // the split size. + uint64_t SplitSize; + // the split reference position relative to read/write start. + SmallVector SplitRefPos; + + SplitPosion(uint64_t BaseBeginPos, uint64_t BaseEndPos, + SmallVector &AllRefPos) { + SplitBeginPos = 0; + SplitEndPos = BaseEndPos - BaseBeginPos; + SplitSize = BaseEndPos - BaseBeginPos; + for (auto RefPos : AllRefPos) { + SplitRefPos.push_back(RefPos - BaseBeginPos); + } + } + }; + explicit SplitBarrier(CallInst *CI) + : M(CI->getModule()), C(CI->getContext()), IRB(CI), + DL(CI->getModule()->getDataLayout()), Barrier(cast(CI)) { + auto ID = CI->getIntrinsicID(); + if (ID == Intrinsic::cj_gcread_struct || + ID == Intrinsic::cj_gcwrite_struct) { + Src = getSource(CI); + BaseObj = getBaseObj(CI); + Dst = getDest(CI); + Size = cast(getSize(CI))->getZExtValue(); + } else if (ID == Intrinsic::cj_gcread_static_struct || + ID == Intrinsic::cj_gcwrite_static_struct) { + Src = getSource(CI); + Dst = getDest(CI); + Size = cast(getSize(CI))->getZExtValue(); + } else { + report_fatal_error("Unsupport barrier type!"); + } + if (Size == 0) { + report_fatal_error("The size of read barrier is 0!"); + } + IsRead = Barrier->isCJStructGCRead(); + } + + ~SplitBarrier() = default; + + std::pair getValueOffsetAndType(Value *Ptr) { + APInt Offset(DL.getIndexSizeInBits(0), 0); + Value *PtrBase = Ptr->stripAndAccumulateConstantOffsets(DL, Offset, true); + return {Offset.getZExtValue(), PtrBase->getType()}; + } + + std::pair + findReferencePositions(Value *Ptr, SmallVector &AllRefPos) { + auto [Offset, Ty] = getValueOffsetAndType(Ptr); + uint64_t BeginPos = Offset; + uint64_t EndPos = BeginPos + Size; + if (isGCPointerType(Ty)) { + report_fatal_error("The dst of read struct is gc pointer!"); + return {0, 0}; + } + StructType *ST = dyn_cast(Ty->getNonOpaquePointerElementType()); + if (!ST) + return {0, 0}; + + findReferencePositionInStruct(ST, BeginPos, EndPos, 0, AllRefPos); + return {BeginPos, EndPos}; + } + + bool doSplit() { + if (Size == 8) { + IsRead ? createReadRefByOffset(0) : createWriteRefByOffset(0); + return true; + } + + SmallVector AllRefPos; + auto [BeginPos, EndPos] = findReferencePositions( + IsRead ? Dst : Src, AllRefPos); + if (EndPos == 0) + return false; + + SplitPosion Posions(BeginPos, EndPos, AllRefPos); + switch (AllRefPos.size()) { + case 0: + report_fatal_error("The read struct does not contain a reference!"); + return false; + case 1: + splitForOneRef(Posions, 0); + return true; + case 2: + splitForTwoRef(Posions, 0, 1); + return true; + case 3: + splitForThreeRef(Posions, 0, 1, 2); + return true; + default: + // Currently, only the refereces less than 3 is processed. + return false; + } + } + + void splitForOneRef(SplitPosion &Pos, uint64_t RefIdx) { + if (Pos.SplitSize == 8) { + IsRead ? createReadRefByOffset(Pos.SplitBeginPos) : + createWriteRefByOffset(Pos.SplitBeginPos); + return; + } + // RefPos: the read/write reference position relative to base + uint64_t RefPos = Pos.SplitRefPos[RefIdx]; + if (RefPos == Pos.SplitBeginPos) { + // Reference is at the front of struct + IsRead ? createReadRefByOffset(RefPos) : createWriteRefByOffset(RefPos); + createMemoryByOffset(RefPos + 8, Pos.SplitEndPos); // 8: pointer size + } else if (RefPos + 8 == Pos.SplitEndPos) { + // Reference is at the end of struct + createMemoryByOffset(Pos.SplitBeginPos, RefPos); + IsRead ? createReadRefByOffset(RefPos) : createWriteRefByOffset(RefPos); + } else { + // Reference is at the middle of struct + createMemoryByOffset(Pos.SplitBeginPos, RefPos); + IsRead ? createReadRefByOffset(RefPos) : createWriteRefByOffset(RefPos); + createMemoryByOffset(RefPos + 8, Pos.SplitEndPos); // 8: pointer size + } + } + + void splitForTwoRef(SplitPosion &Pos, uint64_t RefIdx1, uint64_t RefIdx2) { + // RefPos1 location must be before RefPos2 + uint64_t RefPos1 = Pos.SplitRefPos[RefIdx1]; + uint64_t RefPos2 = Pos.SplitRefPos[RefIdx2]; + if (RefPos1 == Pos.SplitBeginPos) { + // Ref1 is at the front of struct + IsRead ? createReadRefByOffset(RefPos1) : createWriteRefByOffset(RefPos1); + Pos.SplitSize = Pos.SplitSize - 8; + Pos.SplitBeginPos = RefPos1 + 8; + splitForOneRef(Pos, RefIdx2); + } else if (RefPos2 + 8 == Pos.SplitEndPos) { + // Ref2 is at the end of struct + Pos.SplitSize = Pos.SplitSize - 8; + Pos.SplitEndPos = RefPos2; + splitForOneRef(Pos, RefIdx1); + IsRead ? createReadRefByOffset(RefPos2) : createWriteRefByOffset(RefPos2); + } else { + // Ref1 and Ref2 is at the middle of struct + createMemoryByOffset(Pos.SplitBeginPos, RefPos1); + IsRead ? createReadRefByOffset(RefPos1) : createWriteRefByOffset(RefPos1); + Pos.SplitSize = Pos.SplitSize - 8 - (RefPos1 - Pos.SplitBeginPos); + Pos.SplitBeginPos = RefPos1 + 8; + splitForOneRef(Pos, RefIdx2); + } + } + + // ref1, ref2 and ref3 must be in order. + void splitForThreeRef(SplitPosion &Pos, uint64_t RefIdx1, uint64_t RefIdx2, + uint64_t RefIdx3) { + uint64_t RefPos1 = Pos.SplitRefPos[RefIdx1]; + uint64_t RefPos3 = Pos.SplitRefPos[RefIdx3]; + if (RefPos1 == Pos.SplitBeginPos) { + // Ref1 is at the front of struct + IsRead ? createReadRefByOffset(RefPos1) : createWriteRefByOffset(RefPos1); + Pos.SplitSize = Pos.SplitSize - 8; + Pos.SplitBeginPos = RefPos1 + 8; + splitForTwoRef(Pos, RefIdx2, RefIdx3); + } else if (RefPos3 + 8 == Pos.SplitEndPos) { + // Ref3 is at the end of struct + Pos.SplitSize = Pos.SplitSize - 8; + Pos.SplitEndPos = RefPos3; + splitForTwoRef(Pos, RefIdx1, RefIdx2); + IsRead ? createReadRefByOffset(RefPos3) : createWriteRefByOffset(RefPos3); + } else { + // Ref1, Ref2 and Ref3 is at the middle of struct + createMemoryByOffset(Pos.SplitBeginPos, RefPos1); + IsRead ? createReadRefByOffset(RefPos1) : createWriteRefByOffset(RefPos1); + Pos.SplitSize = Pos.SplitSize - 8 - (RefPos1 - Pos.SplitBeginPos); + Pos.SplitBeginPos = RefPos1 + 8; + splitForTwoRef(Pos, RefIdx2, RefIdx3); + } + } + + void findReferencePositionInStruct(StructType *ST, uint64_t BeginPos, + uint64_t EndPos, uint64_t CurPos, + SmallVector &AllRefPos) { + for (uint64_t i = 0; i < ST->getNumElements(); i++) { + uint64_t Offset = DL.getStructLayout(ST)->getElementOffset(i) + CurPos; + + // 1. ST = {i64, {i64, {i64, i8*, i64}}, i64}, Begin = 16, End = 40 + // 2. ST = {i64, [5 * EST]} + // EST = {i64, i8*, i64} + // Begin = 32, End = 64 + if (i != ST->getNumElements() - 1) { + uint64_t NextOffset = DL.getStructLayout(ST)->getElementOffset(i + 1); + if (Offset < BeginPos && NextOffset < BeginPos) + continue; + } + if (Offset >= EndPos) + return; + Type *EleTy = ST->getElementType(i); + if (isGCPointerType(EleTy)) { + AllRefPos.push_back(Offset); + } else if (auto *EST = dyn_cast(EleTy)) { + findReferencePositionInStruct(EST, BeginPos, EndPos, Offset, AllRefPos); + } else if (auto *EAT = dyn_cast(EleTy)) { + findReferencePositionInArray(EAT, BeginPos, EndPos, Offset, AllRefPos); + } + } + } + + void findReferencePositionInArray(ArrayType *AT, uint64_t BeginPos, + uint64_t EndPos, uint64_t CurPos, + SmallVector &AllRefPos) { + uint64_t EleNum = AT->getNumElements(); + Type *EleType = AT->getElementType(); + if (isa(EleType) && isGCPointerType(EleType)) { + for (uint64_t Idx = 0; Idx < EleNum; Idx++) { + AllRefPos.push_back(CurPos + 8 * Idx); // 8: pointer size + } + } else if (auto *ArrayStruct = dyn_cast(EleType)) { + uint64_t EleSize = DL.getStructLayout(ArrayStruct)->getSizeInBytes(); + for (uint64_t Idx = 0; Idx < EleNum; Idx++) { + findReferencePositionInStruct(ArrayStruct, BeginPos, EndPos, + CurPos + Idx * EleSize, AllRefPos); + } + } + } + + void createMemoryByOffset(uint64_t BeginPos, uint64_t EndPos) { + if (EndPos <= BeginPos) + report_fatal_error("The offset of read barrier splitting is error!"); + + uint64_t MemcpySize = EndPos - BeginPos; + Value *DstPtr = Dst; + Value *SrcPtr = Src; + if (BeginPos > 0) { + SmallVector Idxs; + Idxs.push_back(ConstantInt::get(IRB.getInt32Ty(), BeginPos)); + DstPtr = IRB.CreateGEP(IRB.getInt8Ty(), Dst, {Idxs}, "", true); + SrcPtr = IRB.CreateGEP(IRB.getInt8Ty(), Src, {Idxs}, "", true); + } + CallInst *CI = + IRB.CreateMemCpy(DstPtr, Align(8), SrcPtr, Align(8), MemcpySize); + prepareCJTBAA(CI->getModule()->getDataLayout(), CI, CI->getArgOperand(0), + nullptr, true); + CI->setDebugLoc(Barrier->getDebugLoc()); + } + + std::pair maybeCreateGEPByOffset(uint64_t Off) { + Value *DstPtr = Dst; + Value *SrcPtr = Src; + if (Off > 0) { + SmallVector Idxs; + Idxs.push_back(ConstantInt::get(IRB.getInt32Ty(), Off)); + DstPtr = IRB.CreateGEP(IRB.getInt8Ty(), Dst, {Idxs}, "", true); + SrcPtr = IRB.CreateGEP(IRB.getInt8Ty(), Src, {Idxs}, "", true); + } + return {DstPtr, SrcPtr}; + } + + bool isPtrEnumOrOption(Value *Ptr) { + if (AllocaInst *AI = dyn_cast(Ptr)) { + Type *AllocatedType = AI->getAllocatedType(); + + if (StructType *ST = dyn_cast(AllocatedType)) { + StringRef Name = ST->getName(); + return Name.contains("Option") || Name.contains("enum"); + } + } + + return false; + } + + void createReadRefByOffset(uint64_t RefPos) { + auto [DstPtr, SrcPtr] = maybeCreateGEPByOffset(RefPos); + Type *Int8AS1Ty = IRB.getInt8PtrTy(1); + CallInst *ReadRef = nullptr; + Value *SrcCast = nullptr; + if (BaseObj) { + SrcCast = IRB.CreateBitCast(SrcPtr, Int8AS1Ty->getPointerTo(1)); + Function *Callee = Intrinsic::getDeclaration(M, Intrinsic::cj_gcread_ref); + ReadRef = IRB.CreateCall(Callee, {BaseObj, SrcCast}); + } else { + SrcCast = IRB.CreateBitCast(SrcPtr, Int8AS1Ty->getPointerTo(0)); + Function *Callee = + Intrinsic::getDeclaration(M, Intrinsic::cj_gcread_static_ref); + ReadRef = IRB.CreateCall(Callee, {SrcCast}); + } + + if (isPtrEnumOrOption(getUnderlyingObject(DstPtr))) { + ReadRef->setMetadata(LLVMContext::MD_untrusted_ref, + MDNode::get(ReadRef->getContext(), {})); + } + updateTBAA(DL, ReadRef); + + ReadRef->setDebugLoc(Barrier->getDebugLoc()); + Value *DstCast = IRB.CreateBitCast(DstPtr, Int8AS1Ty->getPointerTo(0)); + StoreInst *SI = IRB.CreateStore(ReadRef, DstCast); + SI->setDebugLoc(Barrier->getDebugLoc()); + // Since we know the type of Dst, this doesn't fail. + prepareCJTBAA(DL, SI, DstCast, Int8AS1Ty); + } + + void createWriteRefByOffset(uint64_t RefPos) { + auto [DstPtr, SrcPtr] = maybeCreateGEPByOffset(RefPos); + // Create load + Type *Int8AS1Ty = IRB.getInt8PtrTy(1); + Value *SrcCast = IRB.CreateBitCast(SrcPtr, Int8AS1Ty->getPointerTo()); + LoadInst *LI = LI = IRB.CreateLoad(Int8AS1Ty, SrcCast); + LI->setDebugLoc(Barrier->getDebugLoc()); + // Since we know the type of Src, this doesn't fail. + prepareCJTBAA(DL, LI, SrcCast, Int8AS1Ty); + + // Create gcwrite.ref + Value *DstCast = nullptr; + CallInst *WriteRef = nullptr; + if (BaseObj) { + DstCast = IRB.CreateBitCast(DstPtr, Int8AS1Ty->getPointerTo(1)); + Function *Callee = + Intrinsic::getDeclaration(M, Intrinsic::cj_gcwrite_ref); + WriteRef = IRB.CreateCall(Callee, {LI, BaseObj, DstCast}); + } else { + DstCast = IRB.CreateBitCast(DstPtr, Int8AS1Ty->getPointerTo()); + Function *Callee = + Intrinsic::getDeclaration(M, Intrinsic::cj_gcwrite_static_ref); + WriteRef = IRB.CreateCall(Callee, {LI, DstCast}); + } + WriteRef->setDebugLoc(Barrier->getDebugLoc()); + updateTBAA(DL, WriteRef); + } + +private: + Module *M; + LLVMContext &C; + IRBuilder<> IRB; + const DataLayout &DL; + IntrinsicInst *Barrier = nullptr; + bool IsRead = false; + Value *Src = nullptr; + Value *Dst = nullptr; + Value *BaseObj = nullptr; + uint64_t Size = 0; +}; + +} // namespace + +static bool needToSplit(Instruction *I) { + auto *II = dyn_cast(I); + if (!II) + return false; + Intrinsic::ID ID = II->getIntrinsicID(); + if (ID == Intrinsic::cj_gcread_struct || + ID == Intrinsic::cj_gcread_static_struct || + ID == Intrinsic::cj_gcwrite_struct || + ID == Intrinsic::cj_gcwrite_static_struct) { + if (isa(getSize(II))) { + return true; + } + } + return false; +} + +static bool simplySplit(Function *F) { + bool Changed = false; + SetVector ToSplits; + for (auto *I : F->users()) { + auto *CI = dyn_cast(I); + if (!CI) + continue; + if (needToSplit(CI)) + ToSplits.insert(CI); + } + for (auto *I : ToSplits) { + SplitBarrier SB(I); + if (SB.doSplit()) { + I->eraseFromParent(); + Changed = true; + } + } + + return Changed; +} + +PreservedAnalyses CJBarrierSplit::run(Module &M, + ModuleAnalysisManager &) const { + auto C = &(M.getContext()); + int LongSize = M.getDataLayout().getPointerSizeInBits(); + auto IntptrTy = Type::getIntNTy(*C, LongSize); + + SmallVector AggBarrierIntrinsics = { + Intrinsic::getDeclaration(&M, Intrinsic::cj_gcread_struct, {IntptrTy}), + Intrinsic::getDeclaration(&M, Intrinsic::cj_gcwrite_struct, + {Type::getInt8PtrTy(M.getContext()), IntptrTy}), + Intrinsic::getDeclaration(&M, Intrinsic::cj_gcread_static_struct, + {IntptrTy}), + Intrinsic::getDeclaration(&M, Intrinsic::cj_gcwrite_static_struct, + {IntptrTy})}; + bool Changed = false; + for (auto *F : AggBarrierIntrinsics) + Changed |= simplySplit(F); + return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); +} + +namespace { +class CJBarrierSplitLegacyPass : public ModulePass { +public: + static char ID; + + explicit CJBarrierSplitLegacyPass() : ModulePass(ID) { + initializeCJBarrierSplitLegacyPassPass(*PassRegistry::getPassRegistry()); + } + ~CJBarrierSplitLegacyPass() = default; + + bool runOnModule(Module &M) override { + SmallVector AggBarrierIntrinsics = { + Intrinsic::getDeclaration(&M, Intrinsic::cj_gcread_struct), + Intrinsic::getDeclaration(&M, Intrinsic::cj_gcwrite_struct, + {Type::getInt8PtrTy(M.getContext())}), + Intrinsic::getDeclaration(&M, Intrinsic::cj_gcread_static_struct), + Intrinsic::getDeclaration(&M, Intrinsic::cj_gcwrite_static_struct)}; + bool Changed = false; + for (auto *F : AggBarrierIntrinsics) + Changed |= simplySplit(F); + return Changed; + } +}; +} // namespace + +char CJBarrierSplitLegacyPass::ID = 0; + +ModulePass *llvm::createCJBarrierSplitLegacyPass() { + return new CJBarrierSplitLegacyPass(); +} + +INITIALIZE_PASS(CJBarrierSplitLegacyPass, "cj-barrier-split", + "Cangjie Barriers Split", false, false) diff --git a/llvm/lib/Transforms/Scalar/CJDevirtualOpt.cpp b/llvm/lib/Transforms/Scalar/CJDevirtualOpt.cpp new file mode 100644 index 000000000..ec520a138 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/CJDevirtualOpt.cpp @@ -0,0 +1,471 @@ +//===- CJDevirtualOpt.cpp - -------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This file provides interface to "CJ Devirtual Optimization" pass. +// +// This pass converts cj virtual call into direct call if possible. +// +//===----------------------------------------------------------------------===// +#include "llvm/Transforms/Scalar/CJDevirtualOpt.h" + +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/Transforms/IPO/CJPartialEscapeAnalysis.h" +#include "llvm/Transforms/Scalar/CJFillMetadata.h" +#include "llvm/Transforms/Utils/CallGraphUpdater.h" + +using namespace llvm; + +namespace { + +struct IntroTypeLoad { + LoadInst *LI; + SmallVector FuncTableLoads; + IntroTypeLoad(LoadInst *LI, SmallVector FuncTableLoads) + : LI(LI), FuncTableLoads(FuncTableLoads) {} +}; + +struct IntroTypeCall { + CallInst *CI; + SmallVector FuncTableLoads; + IntroTypeCall(CallInst *CI, SmallVector FuncTableLoads) + : CI(CI), FuncTableLoads(FuncTableLoads) {} +}; + +struct DevirtualCallData { + SmallVector IsSubTypes; + SmallVector GetMtableFuncs; + SmallVector IntroTypeLoads; + SetVector FuncPtrs; + + void getRelatedFuncTableLoad(Instruction *Inst, + SmallVector &FTLIS) { + SmallVector Worklist = {Inst}; + while (!Worklist.empty()) { + auto *I = dyn_cast(Worklist.pop_back_val()); + assert(I != nullptr); + switch (I->getOpcode()) { + default: + break; + case Instruction::Call: { + auto *CB = cast(I); + if (!CB->getCalledFunction() || + CB->getCalledFunction()->getName() != "CJ_MCC_GetMTable") + break; + for (auto *UI : I->users()) + Worklist.push_back(UI); + break; + } + case Instruction::Load: { + if (I->hasMetadata(LLVMContext::MD_func_table)) { + FTLIS.push_back(cast(I)); + break; + } + } + LLVM_FALLTHROUGH; + case Instruction::GetElementPtr: + case Instruction::BitCast: { + for (auto *UI : I->users()) + Worklist.push_back(UI); + } break; + } + } + // Generally, each LI with IntroType must have a corresponding LI with + // FuncTable, but in some optimization scenarios LI(FuncTable) may optimize. + // Conservative treatment. + } + + explicit DevirtualCallData(Function &F) { + for (Instruction &I : instructions(F)) { + // LI: virtual call. + if (auto *LI = dyn_cast(&I); + LI && LI->hasMetadata(LLVMContext::MD_virtual_call)) { + SmallVector FTLIS; + getRelatedFuncTableLoad(LI, FTLIS); + if (FTLIS.empty()) + continue; + IntroTypeLoads.push_back(IntroTypeLoad(LI, FTLIS)); + continue; + } + + // CI: interface call. + auto *CI = dyn_cast(&I); + if (!CI || CI->getCalledFunction() == nullptr) + continue; + StringRef Name = CI->getCalledFunction()->getName(); + if (Name.equals("CJ_MCC_IsSubType")) { + IsSubTypes.push_back(CI); + continue; + } + if (Name.equals("CJ_MCC_GetMTable")) { + SmallVector FTLIS; + getRelatedFuncTableLoad(CI, FTLIS); + if (FTLIS.empty()) + continue; + GetMtableFuncs.push_back(IntroTypeCall(CI, FTLIS)); + } + } + } +}; + +} // namespace + +static void replaceIsSubType(CallBase *CI, bool Result) { + CI->replaceAllUsesWith( + ConstantInt::getBool(Type::getInt1Ty(CI->getContext()), Result)); + CI->eraseFromParent(); +} + +static std::string getOriginTypeName(GlobalVariable *GV) { + if (!GV->hasAttribute("CFileKlass")) { + std::string Name = GV->getName().str(); + size_t Pos = Name.rfind(".ti"); + if (Pos == std::string::npos) { + report_fatal_error("Incorrect type info name!"); + } + return Name.substr(0, Pos); + } + TypeInfo TI(GV); + if (GlobalVariable *TT = TI.getTypeTemplate()) { + std::string Name = TT->getName().str(); + size_t Pos = Name.rfind(".tt"); + if (Pos == std::string::npos) { + report_fatal_error("Incorrect type template name!"); + } + return Name.substr(0, Pos) + ""; + } else { + std::string Name = GV->getName().str(); + size_t Pos = Name.rfind(".ti"); + if (Pos == std::string::npos) { + report_fatal_error("Incorrect type info name!"); + } + return Name.substr(0, Pos); + } +} + +// Find the extension-def. Return global value, if the Any extension def +// the Interface. Otherwise, return nullptr. +static GlobalVariable *getExtensionDef(GlobalVariable *Any, + GlobalVariable *Interface) { + if (!Any->hasInitializer() || !Interface->hasInitializer()) + return nullptr; + + std::string TypeStr1 = getOriginTypeName(Any); + std::string TypeStr2 = getOriginTypeName(Interface); + std::string ExtensionDefName = TypeStr1 + "_ed_" + TypeStr2; + Module *M = Any->getParent(); + return M->getGlobalVariable(ExtensionDefName, true); +} + +static bool isSubType(GlobalVariable *Klass0, GlobalVariable *Klass1) { + if (Klass0 == nullptr) + return false; + + if (Klass0 == Klass1) + return true; + + TypeInfo K0(Klass0); + TypeInfo K1(Klass1); + switch (K0.Kind) { + case TK_NOTHING: + return true; + case TK_TUPLE: { + if (!K1.isTuple()) + return false; + + unsigned K0FieldNum = K0.getFieldNum(); + unsigned K1FieldNum = K1.getFieldNum(); + if (K0FieldNum != K1FieldNum) + return false; + + for (unsigned i = 0; i < K0FieldNum; ++i) { + if (!isSubType(K0.getField(i), K1.getField(i))) { + return false; + } + } + return true; + } + case TK_FUNC: { + report_fatal_error("Should not reach here."); + return false; + } + case TK_CFUNC: { + if (K1.Kind != TK_CFUNC) + return false; + + unsigned TypeArgNum = K0.getTypeArgNum(); + if (TypeArgNum != K1.getTypeArgNum()) + return false; + + assert(TypeArgNum > 0 && + "The number of the function type arg should be more than 0."); + + // Note: the first of TypeArgs is the return type. + constexpr unsigned ReturnTypeIdx = 0; + if (!isSubType(K0.getTypeArg(ReturnTypeIdx), K1.getTypeArg(ReturnTypeIdx))) + return false; + + for (unsigned i = ReturnTypeIdx + 1; i < TypeArgNum; ++i) { + if (!isSubType(K1.getTypeArg(i), K0.getTypeArg(i))) { + return false; + } + } + return true; + } + case TK_RAWARRAY: + // The array type has no subtype relationship. + return false; + case TK_CLASS: + case TK_TEMP_ENUM: { + if (!K1.isClass()) + return false; + + GlobalVariable *SuperKlass = K0.getSuperOrComponent(); + while (SuperKlass != nullptr) { + if (SuperKlass == Klass1) + return true; + + if (!SuperKlass->hasInitializer()) + return false; + + TypeInfo Super(SuperKlass); + SuperKlass = Super.getSuperOrComponent(); + } + return false; + } + default: + if (K0.isInterface() || K1.isInterface()) + report_fatal_error("The interface type is processed in subtype.!"); + + return false; + } + return false; +} + +// Reture true if K0 is subtype of K1, otherwise, return false. +static bool isFunctionSubType(TypeInfo &K0, TypeInfo &K1) { + // The function type information is stored in the `SuperClass` + // of the closure instance. Therefore, having SuperClass and + // SuperClass->IsFunc are prerequisites for the instance to be + // of the function type. + auto Super = K0.getSuperOrComponent(); + if (!Super) + return false; + + TypeInfo SuperTI(Super); + if (!SuperTI.isFunction()) + return false; + + // Now, `SuperTI` and `K1` both are Closure type. + // Get function type from Closure type, i.e., typeArgs[0]: + K0.resetTypeInfo(SuperTI.getTypeArg(0)); + K1.resetTypeInfo(K1.getTypeArg(0)); + // In addition, it is necessary to have the same number of TypeArgNum. + unsigned TypeArgNum = K0.getTypeArgNum(); + if (TypeArgNum != K1.getTypeArgNum()) + return false; + + return isSubType(K0.GV, K1.GV); +} + +static bool tryInferSubType(DevirtualCallData &CallData) { + bool Changed = false; + if (CallData.IsSubTypes.empty()) + return Changed; + + for (auto CI : CallData.IsSubTypes) { + GlobalVariable *Klass0 = findTypeInfoGV(CI->getArgOperand(0)); + GlobalVariable *Klass1 = findTypeInfoGV(CI->getArgOperand(1)); + + if (Klass0 == nullptr || Klass1 == nullptr) + continue; + + if (Klass0 == Klass1) { + replaceIsSubType(CI, true); + Changed = true; + continue; + } + + if (!Klass0->hasInitializer() || !Klass1->hasInitializer()) + continue; + + TypeInfo K0(Klass0); + TypeInfo K1(Klass1); + bool Result = true; + if (K0.isInterface() || K1.isInterface()) { + // If it find the extensionDef of klass, the subType is true. + // Otherwise, the subtype is unknown. + if (getExtensionDef(Klass0, Klass1) == nullptr) + continue; + } else if (K1.isFunction()) { + Result = isFunctionSubType(K0, K1); + } else { + Result = isSubType(Klass0, Klass1); + } + + replaceIsSubType(CI, Result); + Changed = true; + } + return Changed; +} + +// For virtual call: +// %func_ptr = load i8*, i8** %9, !FuncTable +// For interface call: +// %func_ptr = call i8** @CJ_MCC_GetMTable(i8* %TI1, i8* %TI2, i64 i) +// And then: +// %func = bitcast i8* %func_ptr to %func_type +// %ret = call %func_type @func(...) +// ====> +// %ret = call %func_type @specific_callee(...) +static void replaceIVCall(Instruction *I, ExtensionDefData &Data, + uint64_t FuncIdx) { + Function *TableFunc = Data.getFuncByIndex(FuncIdx); + IRBuilder<> IRB(I); + auto *BI = IRB.CreateBitCast(TableFunc, I->getType()); + I->replaceAllUsesWith(BI); + assert(I->use_empty() && "CJ_MCC_GetMTable user is not empty"); + I->eraseFromParent(); +} + +static bool devirtual(DevirtualCallData &CallData) { + bool Changed = false; + auto DevirtualImpl = [](GlobalVariable *Klass0, GlobalVariable *Klass1, + Instruction *I, uint64_t FuncIndex) { + if (Klass0 == nullptr || Klass1 == nullptr) + return false; + GlobalVariable *ED = getExtensionDef(Klass0, Klass1); + if (ED == nullptr || !ED->hasInitializer()) + return false; + ExtensionDefData Data(ED); + replaceIVCall(I, Data, FuncIndex); + return true; + }; + auto ResolveFTLI = + [&DevirtualImpl]( + ArrayRef FTLIS, GlobalVariable *Klass0, + SmallVector &UnresolvedLoad, + const std::function &GetKlass1) + -> bool { + bool Changed = false; + for (auto *FTLI : FTLIS) { + MDNode *MDFT = FTLI->getMetadata(LLVMContext::MD_func_table); + assert(MDFT != nullptr && "metadata IntroType must have value"); + uint64_t FuncIndex = + mdconst::extract(MDFT->getOperand(0))->getZExtValue(); + bool Resolved = DevirtualImpl(Klass0, GetKlass1(FTLI), FTLI, FuncIndex); + if (!Resolved) { + UnresolvedLoad.push_back(FTLI); + } else { + Changed = true; + } + } + return Changed; + }; + if (CallData.GetMtableFuncs.empty() && CallData.IntroTypeLoads.empty()) + return Changed; + + SmallVector UnresolvedMtableFuncs; + SmallVector UnresolvedIntroTypeLoads; + for (auto ITC : CallData.GetMtableFuncs) { + UnresolvedMtableFuncs.push_back(ITC); + } + for (auto ITL : CallData.IntroTypeLoads) { + UnresolvedIntroTypeLoads.push_back(ITL); + } + bool OnceResolved = true; + uint32_t MTableIndex = 0; + uint32_t LoadIndex = 0; + while (OnceResolved) { + OnceResolved = false; + uint32_t ResolvedMSize = UnresolvedMtableFuncs.size(); + uint32_t ResolvedLSize = UnresolvedIntroTypeLoads.size(); + for (; MTableIndex < ResolvedMSize; MTableIndex++) { + auto &ITC = UnresolvedMtableFuncs[MTableIndex]; + CallInst *CI = ITC.CI; + GlobalVariable *Klass0 = findTypeInfoGV(CI->getArgOperand(0)); + GlobalVariable *Klass1 = findTypeInfoGV(CI->getArgOperand(1)); + SmallVector UnresolvedLoad; + OnceResolved |= ResolveFTLI( + ITC.FuncTableLoads, Klass0, UnresolvedLoad, + [&Klass1](LoadInst *) -> GlobalVariable * { return Klass1; }); + if (!UnresolvedLoad.empty()) + UnresolvedMtableFuncs.push_back(IntroTypeCall(CI, UnresolvedLoad)); + } + for (; LoadIndex < ResolvedLSize; LoadIndex++) { + auto &ITL = UnresolvedIntroTypeLoads[LoadIndex]; + auto *TILI = ITL.LI; + auto &FTLIS = ITL.FuncTableLoads; + // If the load pointer is phi or select, no processing is performed + // currently and can be optimized later. + if (!isa(TILI->getPointerOperand()->stripPointerCasts())) + continue; + Value *TI = + cast(TILI->getPointerOperand()->stripPointerCasts()) + ->getPointerOperand(); + // Klass0 is get from TILI (the first load). + GlobalVariable *Klass0 = findTypeInfoGV(TI); + SmallVector UnresolvedLoad; + OnceResolved |= + ResolveFTLI(FTLIS, Klass0, UnresolvedLoad, [](LoadInst *FTLI) { + MDNode *MDTI = FTLI->getMetadata(LLVMContext::MD_intro_type); + if (MDTI == nullptr) + report_fatal_error("metadata IntroType must have value"); + assert(MDTI != nullptr && ""); + const Twine &GVName = + dyn_cast(MDTI->getOperand(0))->getString() + + Twine(".ti"); + // Klass1 is get from FTLI (the last load). + // This is necessary because TI may be instantiated from TT. + GlobalVariable *Klass1 = + FTLI->getModule()->getGlobalVariable(GVName.str(), true); + return Klass1; + }); + if (!UnresolvedLoad.empty()) { + UnresolvedIntroTypeLoads.push_back(IntroTypeLoad(TILI, UnresolvedLoad)); + } + } + Changed |= OnceResolved; + } + return Changed; +} + +PreservedAnalyses CJDevirtualOpt::run(LazyCallGraph::SCC &C, + CGSCCAnalysisManager &AM, + LazyCallGraph &CG, + CGSCCUpdateResult &UR) const { + FunctionAnalysisManager &FAM = + AM.getResult(C, CG).getManager(); + auto LookupLoopInfo = [&FAM](Function &F) -> LoopInfo & { + return FAM.getResult(F); + }; + bool Changed = false; + CallGraphUpdater CGUpdater; + CGUpdater.initialize(CG, C, AM, UR); + for (LazyCallGraph::Node &N : C) { + Function &F = N.getFunction(); + DevirtualCallData CallData(F); + + Changed |= tryInferSubType(CallData); + if (devirtual(CallData)) { + Changed = true; + for (auto Func : CallData.FuncPtrs) { + if (!Func->hasEscapeAnalysis() && !Func->isDeclaration()) { + Changed |= escapeAnalysisFuncImpl(Func, LookupLoopInfo); + } + } + CGUpdater.reanalyzeFunction(F); + } + } + if (Changed) { + return PreservedAnalyses::none(); + } + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Transforms/Scalar/CJFillMetadata.cpp b/llvm/lib/Transforms/Scalar/CJFillMetadata.cpp new file mode 100644 index 000000000..7a0ebceff --- /dev/null +++ b/llvm/lib/Transforms/Scalar/CJFillMetadata.cpp @@ -0,0 +1,1558 @@ +//===- CJFillMetadata.cpp - Fill Cangjie Metadata ---------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// Fill Cangjie Metadata for global varible such as TypeInfo and ReflectionInfo. +// +//===----------------------------------------------------------------------===// +#include "llvm/Transforms/Scalar/CJFillMetadata.h" + +#include "llvm/CodeGen/Passes.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/CJStructTypeGCInfo.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Scalar/ReflectionInfo.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" +using namespace llvm; + +namespace { +const char *CFileKlassStr = "CFileKlass"; +} // namespace + +StructType *llvm::getTypeLayoutType(GlobalVariable *GV) { + const MDNode *Metadata = GV->getMetadata("RelatedType"); + if (!Metadata) + return nullptr; + + StringRef TypeName = + dyn_cast(Metadata->getOperand(0).get())->getString(); + return StructType::getTypeByName(GV->getContext(), TypeName); +} + +std::string llvm::getPrimitiveTypeName(Type *Ty) { + if (auto PT = dyn_cast(Ty)) { + if (PT->getElementType()->isIntegerTy(8)) { + if (PT->getAddressSpace() == 1) { + return "array_ref"; + } else { + return "array_cptr"; + } + } + if (PT->getElementType()->isFunctionTy()) { + return "array_cfunc"; + } + } + if (Ty->isIntegerTy()) { + switch (Ty->getIntegerBitWidth()) { + default: + break; + case 1: + return "array_bool"; + case 8: + return "array_int8"; + case 16: + return "array_int16"; + case 32: + return "array_int32"; + case 64: + return "array_int64"; + } + } + if (Ty->is16bitFPTy()) { + return "array_float16"; + } + if (Ty->isFloatTy()) { + return "array_float32"; + } + if (Ty->isDoubleTy()) { + return "array_float64"; + } + if (Ty->isStructTy()) { + return Ty->getStructName().str(); + } + report_fatal_error("Other type for array to adapt!"); +} + +const static DenseSet PrimitiveTypeInfo{ + "Unit.ti", "Nothing.ti", "Bool.ti", "Rune.ti", "Int8.ti", + "UInt8.ti", "Int16.ti", "UInt16.ti", "Int32.ti", "UInt32.ti", + "Int64.ti", "UInt64.ti", "Float16.ti", "Float32.ti", "Float64.ti", + "IntNative.ti", "UIntNative.ti", "CString.ti"}; + +bool llvm::isPrimitiveTypeInfo(GlobalVariable *GV) { + return PrimitiveTypeInfo.contains(GV->getName()); +} + +unsigned llvm::getValueTypeSize(GlobalVariable *GV) { + assert(isPrimitiveTypeInfo(GV) && "Not primitive type!"); + return StringSwitch(GV->getName()) + .Case("Unit.ti", 0) + .Case("Nothing.ti", 0) + .Case("Bool.ti", 1) + .Case("Rune.ti", 4) + .Case("Int8.ti", 1) + .Case("UInt8.ti", 1) + .Case("Int16.ti", 2) + .Case("UInt16.ti", 2) + .Case("Int32.ti", 4) + .Case("UInt32.ti", 4) + .Case("Int64.ti", 8) + .Case("UInt64.ti", 8) + .Case("Float16.ti", 2) + .Case("Float32.ti", 4) + .Case("Float64.ti", 8) + .Case("IntNative.ti", 8) + .Case("UIntNative.ti", 8) + .Case("CString.ti", 8) + .Default(0); +} + +// The first of result indicates whether it is known, and the second is its +// type kind. +std::pair llvm::getKnownTypeKind(GlobalVariable *GV) { + if (isPrimitiveTypeInfo(GV)) + return std::make_pair(true, TK_INT64); + + StringRef Name = GV->getName(); + TypeKind Kind = StringSwitch(Name) + .Case("std.core:Object.ti", TK_CLASS) + .Case("std.core:String.ti", TK_STRUCT) + .Case("std.core:Thread.ti", TK_CLASS) + .Case("std.core:Any.ti", TK_INTERFACE) + .Case("std.core:Duration.ti", TK_STRUCT) + .Case("std.core:Hashable.ti", TK_INTERFACE) + .Case("std.core:Ordering.ti", TK_ENUM) + .Case("std.core:Resource.ti", TK_INTERFACE) + .Case("std.core:ToString.ti", TK_INTERFACE) + .Case("std.core:Exception.ti", TK_CLASS) + .Default(TK_MAX); + if (Kind != TK_MAX) + return std::make_pair(true, Kind); + + // Check whether the type is an instantiated generic type, and its name uses + // the form xxx, the xxx is in the trustlist. + size_t Pos = Name.find('<'); + if (Pos == StringRef::npos) + return std::make_pair(false, Kind); + + StringRef TTName = Name.substr(0, Pos); + Kind = StringSwitch(TTName) + .Case("Tuple", TK_TUPLE) + .Case("Func", TK_FUNC) + .Case("VArray", TK_VARRAY) + .Case("RawArray", TK_RAWARRAY) + .Case("CPointer", TK_CPOINTER) + .Case("CFunc", TK_CFUNC) + .Case("std.core:Comparable", TK_INTERFACE) + .Case("std.core:Option", TK_TEMP_ENUM) + .Case("std.core:ArrayIterator", TK_CLASS) + .Case("std.core:Range", TK_STRUCT) + .Case("std.core:Array", TK_STRUCT) + .Case("std.core:Box", TK_CLASS) + .Case("std.core:Collection", TK_INTERFACE) + .Case("std.core:Iterable", TK_INTERFACE) + .Case("std.core:Iterator", TK_CLASS) + .Case("std.core:ArrayIterator", TK_CLASS) + .Case("std.core:AtomicBox", TK_CLASS) + .Case("std.core:Equal", TK_INTERFACE) + .Default(TK_MAX); + if (Kind != TK_MAX) + return std::make_pair(true, Kind); + else + return std::make_pair(false, Kind); +} + +bool llvm::hasRunCangjieOpt(Module &M) { + if (auto MDValue = mdconst::extract_or_null( + M.getModuleFlag("Cangjie_OPT"))) { + if (MDValue->getZExtValue()) { + return true; + } + } + return false; +} + +TypeTemplate::TypeTemplate(GlobalVariable *GV) : GV(GV) { + assert(GV->hasInitializer() && "TypeTemplate GV has no Initializer!"); + TT = dyn_cast(GV->getInitializer()); + int64_t T = cast(TT->getOperand(TT_TYPE))->getSExtValue(); + assert(T < TK_MAX && "TypeTempate type kind is error!"); + Kind = static_cast(T); +} + +bool TypeTemplate::isEnum() { return Kind == TK_ENUM || Kind == TK_TEMP_ENUM; } + +bool TypeTemplate::isSyncClass() { + return GV->hasAttribute("Future") || GV->hasAttribute("Monitor") || + GV->hasAttribute("Mutex") || GV->hasAttribute("WaitQueue"); +} + +Constant *TypeTemplate::get(unsigned Idx) { + return cast(TT->getOperand(Idx)); +} + +TypeKind TypeTemplate::getKind() { + int64_t T = cast(TT->getOperand(TT_TYPE))->getSExtValue(); + assert(T < TK_MAX && "TypeTemplate type kind is error!"); + return static_cast(T); +} + +uint8_t TypeTemplate::getFlag() { + uint64_t Flag = cast(get(TT_FLAG))->getZExtValue(); + return static_cast(Flag); +} + +unsigned TypeTemplate::getFieldNum() { + return cast(TT->getOperand(TT_FIELD_NUM))->getZExtValue(); +} + +unsigned TypeTemplate::getTypeArgNum() { + return cast(TT->getOperand(TT_TYPE_ARG_NUM))->getZExtValue(); +} + +TypeInfo::TypeInfo(GlobalVariable *GV) : GV(GV) { + assert(GV->hasInitializer() && "GV has no Initializer!"); + Data = dyn_cast(GV->getInitializer()); + int64_t T = cast(Data->getOperand(CIT_TYPE))->getSExtValue(); + assert(T < TK_MAX && "Data type kind is error!"); + Kind = static_cast(T); +} + +bool TypeInfo::isStructType() { + return Kind == TK_STRUCT || Kind == TK_TUPLE || Kind == TK_ENUM; +} + +bool TypeInfo::isPrimitiveType() { + return Kind == TK_UNIT || Kind == TK_BOOL || Kind == TK_UINT8 || + Kind == TK_UINT16 || Kind == TK_UINT32 || Kind == TK_UINT64 || + Kind == TK_INT8 || Kind == TK_INT16 || Kind == TK_INT32 || + Kind == TK_INT64 || Kind == TK_FLOAT16 || Kind == TK_FLOAT32 || + Kind == TK_FLOAT64; +} + +bool TypeInfo::isReferenceType() { return Kind < 0; } + +bool TypeInfo::isArray() { return Kind == TK_RAWARRAY; } + +bool TypeInfo::isEnum() { return Kind == TK_ENUM || Kind == TK_TEMP_ENUM; } + +bool TypeInfo::isTuple() { return Kind == TK_TUPLE; } + +bool TypeInfo::isVarray() { return Kind == TK_VARRAY; } + +bool TypeInfo::isFunction() { return Kind == TK_FUNC; } + +bool TypeInfo::isClass() { return Kind == TK_CLASS || Kind == TK_TEMP_ENUM; } + +bool TypeInfo::isInterface() { return Kind == TK_INTERFACE; } + +bool TypeInfo::isCType() { + return Kind == TK_CSTRING || Kind == TK_CPOINTER || Kind == TK_CFUNC; +} + +bool TypeInfo::isPureValueType() { + return isPrimitiveType() || isVarray() || isCType(); +} + +bool TypeInfo::isSyncClass() { + return GV->hasAttribute("Future") || GV->hasAttribute("Monitor") || + GV->hasAttribute("Mutex") || GV->hasAttribute("WaitQueue"); +} + +bool TypeInfo::isGenericType() { return getTypeArgNum() > 0; } + +Value *TypeInfo::getReferenceType(GlobalVariable *GV) { + Value *Ret = nullptr; + LLVMContext &C = GV->getContext(); + if (GV->hasInitializer()) { + TypeInfo TI(GV); + Ret = ConstantInt::get(Type::getInt1Ty(C), TI.isReferenceType()); + return Ret; + } + auto P = getKnownTypeKind(GV); + if (P.first) { + bool IsRef = P.second < 0 ? true : false; + Ret = ConstantInt::get(Type::getInt1Ty(C), IsRef); + } + return Ret; +} + +Constant *TypeInfo::get(unsigned Idx) { + return cast(Data->getOperand(Idx)); +} + +uint8_t TypeInfo::getTypeFlag() { + uint64_t Flag = cast(get(CIT_FLAG))->getZExtValue(); + return static_cast(Flag); +} + +unsigned TypeInfo::getSize() { + return cast(get(CIT_SIZE))->getZExtValue(); +} + +unsigned TypeInfo::getFieldNum() { + return cast(get(CIT_FIELD_NUM))->getZExtValue(); +} + +unsigned TypeInfo::getTypeArgNum() { + return cast(get(CIT_TYPE_ARG_NUM))->getZExtValue(); +} + +Constant *TypeInfo::getGCTib() { return get(CIT_GCTIB); } + +unsigned TypeInfo::getAlign() { + return cast(get(CIT_ALIGN))->getZExtValue(); +} + +Constant *TypeInfo::getOffsets() { + auto C = cast(get(CIT_OFFSETS))->getOperand(0); + auto GV = cast(C); + assert(GV->hasInitializer() && "Field GV has no Initializer!"); + return GV->getInitializer(); +} + +unsigned TypeInfo::getOffset(unsigned Idx) { + Constant *OffsetsGV = getOffsets(); + if (OffsetsGV->isZeroValue()) + return 0; + + auto Offsets = dyn_cast(OffsetsGV); + assert(Idx < Offsets->getNumElements() && "getOffset index is error"); + return Offsets->getElementAsInteger(Idx); +} + +Constant *TypeInfo::getFields() { + auto C = cast(get(CIT_FIELD))->getOperand(0); + auto GV = cast(C); + assert(GV->hasInitializer() && "Field GV has no Initializer!"); + return GV->getInitializer(); +} + +GlobalVariable *TypeInfo::getSuperOrComponent() { + return dyn_cast_or_null(get(CIT_SUPER)); +} + +GlobalVariable *TypeInfo::getField(unsigned Idx) { + Constant *FieldGV = getFields(); + assert(Idx < FieldGV->getNumOperands() && "getField index is error"); + return cast(FieldGV->getOperand(Idx)); +} + +Constant *TypeInfo::getTypeArgs() { + auto C = cast(get(CIT_TYPE_ARG))->getOperand(0); + auto GV = cast(C); + assert(GV->hasInitializer() && "TypeArg GV has no Initializer!"); + return cast(GV->getInitializer()); +} + +GlobalVariable *TypeInfo::getTypeArg(unsigned Idx) { + Constant *TypeArgGV = getTypeArgs(); + assert(Idx < TypeArgGV->getNumOperands() && "getTypeArg index is error"); + return cast(TypeArgGV->getOperand(Idx)); +} + +GlobalVariable *TypeInfo::getTypeTemplate() { + if (!hasTypeTempate()) + return nullptr; + + auto GV = + dyn_cast(get(CIT_GENERIC_FROM)->stripPointerCasts()); + if (!GV->hasInitializer()) + return nullptr; + + return GV; +} + +StructType *TypeInfo::getLayoutType() { + const MDNode *Metadata = GV->getMetadata("RelatedType"); + if (!Metadata) + return nullptr; + + StringRef TypeName = + dyn_cast(Metadata->getOperand(0).get())->getString(); + return StructType::getTypeByName(GV->getContext(), TypeName); +} + +Type *TypeInfo::getArrayElementType() { + assert(isArray() && "Illegal raw array type."); + StructType *ST = getLayoutType(); + assert(ST->getNumElements() == 2 && "ArrayLayout's elemNums should be 2"); + auto *AT = dyn_cast(ST->getElementType(1)); + assert(AT != nullptr && "ArrayLayout[1] should be ArrayType"); + return AT->getElementType(); +} + +bool TypeInfo::hasRefField() { return getTypeFlag() & TF_HAS_REF_FIELD; } + +bool TypeInfo::hasTypeTempate() { + Constant *Temp = get(CIT_GENERIC_FROM); + if (Temp->isNullValue()) + return false; + + return isa(Temp->stripPointerCasts()); +} + +void TypeInfo::resetTypeInfo(GlobalVariable *GV) { + this->GV = GV; + assert(GV->hasInitializer() && "GV has no Initializer!"); + Data = dyn_cast(GV->getInitializer()); + int64_t T = cast(Data->getOperand(CIT_TYPE))->getSExtValue(); + assert(T < TK_MAX && "Data type kind is error!"); + Kind = static_cast(T); +} + +ExtensionDefData::ExtensionDefData(GlobalVariable *GV) { + assert(GV->hasInitializer() && "ExtensionDef GV has no Initializer!"); + ConstantStruct *Data = dyn_cast(GV->getInitializer()); + TypeParamCount = + cast(Data->getOperand(ET_TYPE_PARAM_COUNT))->getSExtValue(); + IsInterfaceTypeInfo = + cast(Data->getOperand(ET_IS_INTERFACE_TYPEINFO)) + ->getZExtValue(); + auto CE = dyn_cast(Data->getOperand(ET_TARGET_TYPE)); + TargetType = dyn_cast(CE->getOperand(0)); + CE = dyn_cast(Data->getOperand(ET_INTERFACE_FN)); + if (IsInterfaceTypeInfo == 0) { + InterfaceFn = dyn_cast(CE->getOperand(0)); + } else { + InterfaceTypeInfo = dyn_cast(CE->getOperand(0)); + } + auto Field = cast(Data->getOperand(ET_WHERE_COND_FN)); + if (!Field->isNullValue()) { + CE = dyn_cast(Field); + WhereCondFn = dyn_cast(CE->getOperand(0)); + } + Field = cast(Data->getOperand(ET_FUNC_TABLE)); + if (!Field->isNullValue()) { + CE = dyn_cast(Field); + FuncTable = dyn_cast(CE->getOperand(0)); + } +} + +Function *ExtensionDefData::getFuncByIndex(unsigned Idx) { + assert(FuncTable && "ExtensionDef has no FuncTable!"); + assert(FuncTable->hasInitializer() && "FuncTable GV has no Initializer!"); + ConstantArray *Tbl = dyn_cast(FuncTable->getInitializer()); + return cast(Tbl->getOperand(Idx)->stripPointerCasts()); +} + +GlobalVariable *RefValue::findTypeInfoGV() { + getTypeInfoGV(Ref); + if (TypeVec.size() == 1 && !TypeVec.count(nullptr)) { + return cast(TypeVec.begin()->first); + } + return nullptr; +} + +void RefValue::getTypeInfoGV(Value *V) { + V = V->stripPointerCasts(); + if (!VisitedValue.insert(V).second) { + return; + } + if (auto *LI = dyn_cast(V)) { + getTypeInfoGVLoad(LI->getPointerOperand()); + } else if (auto *Arg = dyn_cast(V)) { + int ArgNo = Arg->getArgNo(); + Function *CurFunc = Arg->getParent(); + if (!CurFunc->hasInternalLinkage()) { + TypeVec[nullptr]++; + return; + } + getTypeInfoGVParam(CurFunc, ArgNo); + } else if (auto *CI = dyn_cast(V)) { + if (!isNewObjectCallSite(CI)) { + TypeVec[nullptr]++; + return; + } + getTypeInfoGV(CI->getOperand(0)); + } else if (auto *GV = dyn_cast(V)) { + if (!GV->hasAttribute("CFileKlass")) { + report_fatal_error("Incorrect typeinfo!"); + } + TypeVec[GV]++; + } else { + TypeVec[nullptr]++; + } +} + +void RefValue::getTypeInfoGVLoad(Value *V) { + if (auto *GV = dyn_cast(V)) { + // %obj = call i8 addrspace(1)* @llvm.cj.heapmalloc.class + // call @gcwrite.static (i8 addrspace(1)* %obj, i8 addrspace(1)** %GV) + // %ref = call i8 addrspace(1)* @gcread.static (i8 addrspace(1)** %GV) + auto *MD = GV->getMetadata("cjType"); + if (!MD) { + TypeVec[nullptr]++; + return; + } + std::string TypeStr = + dyn_cast(MD->getOperand(0).get())->getString().str(); + Module *M = GV->getParent(); + auto *ObjTypeInfo = M->getGlobalVariable(TypeStr); + TypeVec[ObjTypeInfo]++; + return; + } + if (auto *AI = dyn_cast(V)) { + // %ins = alloca i8 addrspace(1)*, align 8 + // %obj = call i8 addrspace(1)* @llvm.cj.heapmalloc.class + // store i8 addrspace(1)* %obj, i8 addrspace(1)** %ins, align 8 + // %ref = load i8 addrspace(1)*, i8 addrspace(1)** %ins, align 8 + if (AI->getAllocatedType() == + Type::getInt8PtrTy(AI->getFunction()->getContext(), 1)) { + for (User *U : AI->users()) { + if (StoreInst *SI = dyn_cast(U)) { + getTypeInfoGV(SI->getValueOperand()->stripPointerCasts()); + } + } + return; + } + } + TypeVec[nullptr]++; +} + +void RefValue::getTypeInfoGVParam(Function *F, int ArgNo) { + for (auto I = F->user_begin(), E = F->user_end(); I != E;) { + User *U = *(I++); + auto *GetFunc = dyn_cast(U); + if (GetFunc == nullptr) { + // Maybe func stored to other field. Now we can't analysis the type of + // param. + TypeVec[nullptr]++; + return; + } + getTypeInfoGV(U->getOperand(ArgNo)); + } +} + +bool RefValue::isNewObjectCallSite(CallBase *CI) { + assert(CI && "NewObject callsite is nullptr"); + unsigned ID = CI->getIntrinsicID(); + if (ID == Intrinsic::cj_malloc_object) + return true; + + Function *Callee = CI->getCalledFunction(); + if (Callee && Callee->hasName()) { + StringRef FuncName = Callee->getName(); + if (FuncName.isCangjieNewObjFunction() || + FuncName.equals("CJ_MCC_NewPinnedObject")) + return true; + } + return false; +} + +GlobalVariable *llvm::findTypeInfoGV(Value *V) { + V = V->stripPointerCasts(); + if (auto *GV = dyn_cast(V)) { + return GV; + } + + // process the following case: + // %temp = bitcast i8 addrspace(1)* %obj to i8* addrspace(1)* + // %typeinfo = load i8*, i8* addrspace(1)* %temp, align 8 + if (LoadInst *LI = dyn_cast(V)) { + Value *RefPtr = LI->getPointerOperand()->stripPointerCasts(); + RefValue Ref(RefPtr); + return Ref.findTypeInfoGV(); + } + return nullptr; +} + +StringRef llvm::getStringFromMD(const MDTuple *MD, unsigned Idx) { + return dyn_cast(MD->getOperand(Idx).get())->getString(); +} + +MDTuple *llvm::getMDOperand(const MDTuple *MD, unsigned Idx) { + return dyn_cast(MD->getOperand(Idx).get()); +} + +unsigned llvm::getTypeModifier(StringRef Name) { + return StringSwitch(Name) + .Case("default", RMT_DEFAULT) + .Case("private", RMT_PRIVATE) + .Case("protected", RMT_PROTECTED) + .Case("public", RMT_PUBLIC) + .Case("immutable", RMT_IMMUTABLE) + .Case("box", RMT_BOXCLASS) + .Case("open", RMT_OPEN) + .Case("override", RMT_OVERRIDE) + .Case("redef", RMT_REDEF) + .Case("abstract", RMT_ABSTRACT) + .Case("sealed", RMT_SEALED) + .Case("mut", RMT_MUT) + .Case("static", RMT_STATIC) + .Case("enumKind0", RMT_ENUM_KIND0) + .Case("enumKind1", RMT_ENUM_KIND1) + .Case("enumKind2", RMT_ENUM_KIND2) + .Case("enumKind3", RMT_ENUM_KIND3) + .Case("hasSRet0", RMT_HAS_SRET0) + .Case("hasSRet1", RMT_HAS_SRET1) + .Case("hasSRet2", RMT_HAS_SRET2) + .Case("hasSRet3", RMT_HAS_SRET3) + .Default(RMT_MAX); +} + +static Constant *getOrInsertConstStringGV(Module &M, StringRef Str) { + std::string Name = (Str.empty() ? "NullString" : Str.str()) + ".reflectStr"; + GlobalVariable *StrGV = M.getNamedGlobal(Name); + if (StrGV == nullptr) { + Constant *StrContent = ConstantDataArray::getString(M.getContext(), Str); + StrGV = dyn_cast( + M.getOrInsertGlobal(Name, StrContent->getType())); + StrGV->setInitializer(StrContent); + StrGV->setLinkage(GlobalValue::InternalLinkage); + StrGV->addAttribute("CFileReflect"); + StrGV->setAlignment(Align(1)); + } + return StrGV; +} + +static Constant *createReflectGV(Module &M, std::string &Str, StructType *ST, + SmallVectorImpl &BodyVec) { + GlobalVariable *GV = dyn_cast(M.getOrInsertGlobal(Str, ST)); + GV->setInitializer(ConstantStruct::get(ST, BodyVec)); + GV->setLinkage(GlobalValue::InternalLinkage); + GV->addAttribute("CFileReflect"); + return GV; +} + +static GlobalVariable *getNameGlobal(Module &M, StringRef Name) { + GlobalVariable *GV = M.getNamedGlobal(Name); + if (GV == nullptr) { + report_fatal_error("Can't find " + Twine(Name.str()) + + " global variable for reflection"); + } + return GV; +} + +Constant *BaseInfo::createGlobalVal(std::string &Str, StringRef TypeName, + SmallVectorImpl &BodyVec) { + StructType *ST = StructType::getTypeByName(C, TypeName); + if (!ST) { + SmallVector TypeVec(BodyVec.size()); + for (unsigned Idx = 0; Idx < BodyVec.size(); ++Idx) { + TypeVec[Idx] = BodyVec[Idx]->getType(); + } + ST = StructType::create(C, TypeVec, TypeName); + } + return createReflectGV(M, Str, ST, BodyVec); +} + +Constant *ClassReflectInfo::fillInstanceFieldNames(StringRef TIName) { + unsigned Cnt = FieldNames.size(); + if (Cnt == 0) + return Int8PtrNull; + + SmallVector Body; + for (auto Name : FieldNames) { + Constant *NameGV = getOrInsertConstStringGV(M, Name); + Body.push_back(ConstantExpr::getBitCast(NameGV, Int8PtrTy)); + } + std::string GVName = TIName.str() + ".fieldnames"; + std::string TypeName = "reflect.fieldNamesType" + std::to_string(Cnt); + Constant *GV = createGlobalVal(GVName, TypeName, Body); + return ConstantExpr::getBitCast(GV, Int8PtrTy); +} + +Constant *EnumReflectInfo::fillEnumCtorInfos(StringRef TypeInfoName) { + if (EnumCtors.empty()) + return Int8PtrNull; + + SmallVector BodyVec; + for (unsigned Idx = 0; Idx < EnumCtors.size(); ++Idx) { + EnumCtorInfo &Info = EnumCtors[Idx]; + BodyVec.push_back(getOrInsertConstStringGV(M, Info.CtorName)); + if (Info.TypeName.empty()) { + BodyVec.push_back(Int8PtrNull); + } else { + Function *Func = M.getFunction(Info.TypeName); + if (Func == nullptr) { + report_fatal_error("Can't find " + Twine(Info.TypeName.str()) + + " enum type for reflection"); + } + BodyVec.push_back(ConstantExpr::getBitCast(Func, Int8PtrTy)); + } + } + std::string GVName = TypeInfoName.str() + ".enumctors"; + Constant *EnumCtorsGV = createGlobalVal(GVName, GVName + "Type", BodyVec); + return ConstantExpr::getBitCast(EnumCtorsGV, Int8PtrTy); +} + +Constant *MethodInfo::fillMethodInfo(unsigned Idx, StringRef TIName) { + std::string FuncGVName; + if (IsStatic) { + if (MethodLinkName.empty()) { + FuncGVName = TIName.str() + ".staticMethod" + std::to_string(Idx); + } else { + FuncGVName = MethodLinkName.str() + ".staticMethod"; + } + } else { + FuncGVName = TIName.str() + ".method" + std::to_string(Idx); + } + Constant *GlobalVar = M.getGlobalVariable(FuncGVName); + if (!GlobalVar) { + SmallVector Body; + Constant *Name = getOrInsertConstStringGV(M, MethodName); + Body.push_back(ConstantExpr::getBitCast(Name, Int8PtrTy)); + Body.push_back(ConstantInt::get(Int32Ty, Modifier)); + Body.push_back(ConstantInt::get(Int16Ty, ActualParams.size())); + Body.push_back(ConstantInt::get(Int16Ty, GenericParams.size())); + Function *MethodAddr = M.getFunction(MethodLinkName); + if (MethodAddr == nullptr) { + Body.push_back(Int8PtrNull); + } else { + Body.push_back(ConstantExpr::getBitCast(MethodAddr, Int8PtrTy)); + } + Constant *ReturnTI = getNameGlobal(M, ReturnType); + Body.push_back(ConstantExpr::getBitCast(ReturnTI, Int8PtrTy)); + Body.push_back(Annotation); + if (TIName.empty()) { + Body.push_back(Int8PtrNull); + } else { + Constant *TI = getNameGlobal(M, TIName); + Body.push_back(ConstantExpr::getBitCast(TI, Int8PtrTy)); + } + Body.push_back(fillActualParamInfo(FuncGVName)); + Body.push_back(fillGenericParamInfo(FuncGVName)); + GlobalVar = createGlobalVal(FuncGVName, "reflect.methodType", Body); + } + return ConstantExpr::getBitCast(GlobalVar, Int8PtrTy); +} + +Constant *MethodInfo::fillActualParamInfo(StringRef Prefix) { + unsigned Cnt = ActualParams.size(); + if (Cnt == 0) { + return Int8PtrNull; + } + std::string ParamGVName = Prefix.str() + ".actualParam"; + SmallVector Body; + for (unsigned Idx = 0; Idx < Cnt; ++Idx) { + ParamInfo &PI = ActualParams[Idx]; + Constant *Name = getOrInsertConstStringGV(M, PI.ParamName); + Body.push_back(ConstantExpr::getBitCast(Name, Int8PtrTy)); + Body.push_back(ConstantInt::get(Int32Ty, Idx)); + Constant *TypeTI = getNameGlobal(M, PI.TypeName); + Body.push_back(ConstantExpr::getBitCast(TypeTI, Int8PtrTy)); + Body.push_back(ActualParams[Idx].Annotation); + } + std::string TypeName = "reflect.actualParamType" + std::to_string(Cnt); + Constant *GV = createGlobalVal(ParamGVName, TypeName, Body); + return ConstantExpr::getBitCast(GV, Int8PtrTy); +} + +Constant *MethodInfo::fillGenericParamInfo(StringRef Prefix) { + unsigned Cnt = GenericParams.size(); + if (Cnt == 0) { + return Int8PtrNull; + } + std::string ParamGVName = Prefix.str() + ".genericParam"; + SmallVector Body; + for (unsigned Idx = 0; Idx < Cnt; ++Idx) { + Body.push_back(getNameGlobal(M, GenericParams[Idx])); + } + std::string TypeName = "reflect.genericParamType" + std::to_string(Cnt); + Constant *GV = createGlobalVal(ParamGVName, TypeName, Body); + return ConstantExpr::getBitCast(GV, Int8PtrTy); +} + +Constant *FieldInfo::fillInstanceFieldInfo(unsigned Idx, StringRef TIName) { + std::string FieldName = TIName.str() + ".field" + std::to_string(Idx); + Constant *GlobalVar = M.getGlobalVariable(FieldName); + if (!GlobalVar) { + SmallVector Body; + Body.push_back(ConstantInt::get(Int32Ty, Modifier)); + Body.push_back(ConstantInt::get(Int32Ty, FieldIdx)); + Body.push_back(Annotation); + GlobalVar = createGlobalVal(FieldName, "reflect.fieldType", Body); + } + return ConstantExpr::getBitCast(GlobalVar, Int8PtrTy); +} + +Constant *FieldInfo::fillStaticFieldInfo() { + std::string GlobalName = StaticName.str() + ".globalVars"; + Constant *GlobalVar = M.getGlobalVariable(GlobalName); + if (!GlobalVar) { + SmallVector Body; + Constant *NameGV = getOrInsertConstStringGV(M, FieldName); + Constant *TypeGV = getNameGlobal(M, TypeName); + Constant *GlobalAddr = getNameGlobal(M, StaticName); + Body.push_back(ConstantExpr::getBitCast(NameGV, Int8PtrTy)); + Body.push_back(ConstantInt::get(Int32Ty, Modifier)); + Body.push_back(ConstantExpr::getBitCast(TypeGV, Int8PtrTy)); + Body.push_back(ConstantExpr::getBitCast(GlobalAddr, Int8PtrTy)); + Body.push_back(Annotation); + GlobalVar = createGlobalVal(GlobalName, "reflect.staticFieldType", Body); + } + return ConstantExpr::getBitCast(GlobalVar, Int8PtrTy); +} + +ReflectInfo::ReflectInfo(Module &M) : BaseInfo(M), DL(M.getDataLayout()) { + // generate reflection info by metadata. + NamedMDNode *PkgInfoMD = M.getNamedMetadata("pkg_info"); + if (PkgInfoMD == nullptr) + return; + + getGlobalsFromNamedMD(TypeInfos, M.getNamedMetadata("types")); + getGlobalsFromNamedMD(TypeInfos, M.getNamedMetadata("primitive_tis")); + getGlobalsFromNamedMD(TypeTemplates, M.getNamedMetadata("type_templates")); + getGlobalsFromNamedMD(TypeTemplates, M.getNamedMetadata("primitive_tts")); + + // global funcs in current bc. + NamedMDNode *GlobalFuncsMD = M.getNamedMetadata("functions"); + if (GlobalFuncsMD != nullptr) { + for (unsigned Idx = 0; Idx < GlobalFuncsMD->getNumOperands(); ++Idx) { + auto *MethodMD = dyn_cast(GlobalFuncsMD->getOperand(Idx)); + MethodInfo *Info = getMethodInfo(); + bool IsPublic = parseOneMethodMd(*Info, MethodMD, true); + if (IsPublic) { + StaticGlobalMethods[Info->MethodLinkName] = Info; + } + } + } + // global vars in current bc. + NamedMDNode *GlobalVarsMD = M.getNamedMetadata("global_variables"); + if (GlobalVarsMD != nullptr) { + for (unsigned Idx = 0; Idx < GlobalVarsMD->getNumOperands(); ++Idx) { + auto *FieldMD = dyn_cast(GlobalVarsMD->getOperand(Idx)); + FieldInfo *Info = getFieldInfo(); + parseOneStaticFieldMd(*Info, FieldMD); + if (Info->Modifier & RMT_PUBLIC) { + StaticGlobalFields[Info->StaticName] = Info; + } + } + } +} + +Constant *ReflectInfo::getReflectionInfo(GlobalVariable *GV, bool IsEnum) { + const auto *ReflectMD = + dyn_cast_or_null(GV->getMetadata("Reflection")); + if (ReflectMD == nullptr) + return Int8PtrNull; + + TIName = GV->getName(); + if (enable(M)) { + if (GV->hasAttribute(CFileKlassStr)) { + if (!TypeInfos.contains(GV)) { + report_fatal_error(GV->getName() + + ": typeinfo's reflect info don't exist in package " + "info at the same time!"); + } + } else if (GV->hasAttribute("cj_tt")) { + if (!TypeTemplates.contains(GV)) { + report_fatal_error(GV->getName() + + ": type_template's reflect info don't exist in " + "package info at the same time!"); + } + } + if (IsEnum) { + return getEnumReflectInfo(ReflectMD); + } else { + return getClassReflectInfo(ReflectMD); + } + } else { + if (IsEnum) { + return getEnumDebugInfo(ReflectMD); + } else { + return getClassDebugInfo(ReflectMD); + } + } +} + +// Reflection: !{typeName, instanceFields, staticFields, instanceMethods, +// staticMethods, attributes} +Constant *ReflectInfo::getClassReflectInfo(const MDTuple *ReflectMD) { + // record all reflection info + ClassReflectInfo Info(M); + Info.GenericClass = getStringFromMD(ReflectMD, CRT_GENERIC_CLASS); + parseAttributes(getMDOperand(ReflectMD, CRT_ATTRIBUTE), Info.Modifier, + Info.Annotation); + parseInstanceFieldMDs(Info, getMDOperand(ReflectMD, CRT_INSTANCE_FIELD)); + parseStaticFieldMDs(Info, getMDOperand(ReflectMD, CRT_STATIC_FIELD)); + parseInstanceMethodMDs(Info.InstanceMethods, + getMDOperand(ReflectMD, CRT_INSTANCE_METHOD)); + parseStaticMethodMDs(Info.StaticMethods, + getMDOperand(ReflectMD, CRT_STATIC_METHOD)); + + SmallVector BodyVec(FRT_PTRS); + fillClassReflectInfo(Info, BodyVec); + + std::string GVName = TIName.str() + ".reflect"; + auto *ReflectGV = createGlobalVal(GVName, GVName + "Type", BodyVec); + return ConstantExpr::getBitCast(ReflectGV, Int8PtrTy); +} + +// Reflection: !{type name, enum ctors, instance methods, static methods, +// type attributes} +Constant *ReflectInfo::getEnumReflectInfo(const MDTuple *ReflectMD) { + // record all reflection info + EnumReflectInfo Info(M); + parseAttributes(getMDOperand(ReflectMD, ERT_ATTRIBUTE), Info.Modifier, + Info.Annotation); + parseEnumCtorMDs(Info.EnumCtors, getMDOperand(ReflectMD, ERT_ENUM_CTOR)); + parseInstanceMethodMDs(Info.InstanceMethods, + getMDOperand(ReflectMD, ERT_INSTANCE_METHOD)); + parseStaticMethodMDs(Info.StaticMethods, + getMDOperand(ReflectMD, ERT_STATIC_METHOD)); + + SmallVector BodyVec(FET_PTRS); + fillEnumReflectInfo(Info, BodyVec); + + std::string GVName = TIName.str() + ".reflect"; + auto *ReflectGV = createGlobalVal(GVName, GVName + "Type", BodyVec); + return ConstantExpr::getBitCast(ReflectGV, Int8PtrTy); +} + +// Reflection: !{typeName, instanceFields} +Constant *ReflectInfo::getClassDebugInfo(const MDTuple *ReflectMD) { + MDTuple *FieldMDs = getMDOperand(ReflectMD, CDT_INSTANCE_FIELD); + unsigned Cnt = FieldMDs->getNumOperands(); + if (Cnt == 0) + return Int8PtrNull; + + SmallVector Body; + for (unsigned Idx = 0; Idx < Cnt; ++Idx) { + auto *FieldMD = dyn_cast(FieldMDs->getOperand(Idx).get()); + // InstanceFieldMD: !{idx, , } + StringRef FieldName = getStringFromMD(FieldMD, 1); + Constant *NameGV = getOrInsertConstStringGV(M, FieldName); + Body.push_back(ConstantExpr::getBitCast(NameGV, Int8PtrTy)); + } + std::string GVName = TIName.str() + ".fieldNames"; + auto *FieldNamesGV = createGlobalVal(GVName, GVName + "Type", Body); + + SmallVector BodyVec; + BodyVec.push_back(ConstantExpr::getBitCast(FieldNamesGV, Int8PtrTy)); + + std::string DebugGVName = TIName.str() + ".reflect"; + auto *ReflectGV = createGlobalVal(DebugGVName, DebugGVName + "Type", BodyVec); + return ConstantExpr::getBitCast(ReflectGV, Int8PtrTy); +} + +// Reflection: !{typeName, enum ctors, type attributes} +Constant *ReflectInfo::getEnumDebugInfo(const MDTuple *ReflectMD) { + EnumReflectInfo Info(M); + parseAttributes(getMDOperand(ReflectMD, EDT_ATTRIBUTE), Info.Modifier, + Info.Annotation); + parseEnumCtorMDs(Info.EnumCtors, getMDOperand(ReflectMD, EDT_ENUM_CTOR)); + + // 3: { ctors info, modifier, ctors info cnt} + SmallVector BodyVec(3); + BodyVec[FET_CTOR_INFO] = Info.fillEnumCtorInfos(TIName); + BodyVec[FET_MODIFIER] = ConstantInt::get(Int32Ty, Info.Modifier); + BodyVec[FET_CTOR_CNT] = ConstantInt::get(Int32Ty, Info.EnumCtors.size()); + + std::string GVName = TIName.str() + ".reflect"; + auto *ReflectGV = createGlobalVal(GVName, GVName + "Type", BodyVec); + return ConstantExpr::getBitCast(ReflectGV, Int8PtrTy); +} + +std::pair +ReflectInfo::buildPackageInfoGV(std::string PackName) { + // generate packageInfo gv and init it later. + unsigned Nums = FPT_PTRS + TypeInfos.size() + TypeTemplates.size() + + StaticGlobalMethods.size() + StaticGlobalFields.size(); + SmallVector TypeVec(Nums, Int8PtrTy); + TypeVec[FPT_TYPE_INFO_CNT] = Int32Ty; + TypeVec[FPT_TYPE_TEMPLATE_CNT] = Int32Ty; + TypeVec[FPT_GLOBAL_FUNC_CNT] = Int32Ty; + TypeVec[FPT_GLOBAL_VAR_CNT] = Int32Ty; + TypeVec[FPT_FLAG] = Int64Ty; + + auto PkgInfoType = StructType::getTypeByName(C, PackName + ".pkgInfoType"); + if (PkgInfoType == nullptr) { + PkgInfoType = StructType::create(C, TypeVec, PackName + ".pkgInfoType"); + } + PackName += ".packageInfo"; + auto PackageInfoGV = + dyn_cast(M.getOrInsertGlobal(PackName, PkgInfoType)); + PackageInfoGV->setLinkage(GlobalValue::ExternalLinkage); + PackageInfoGV->addAttribute("CFileReflect"); + GlobalValue::SanitizerMetadata Meta; + Meta.NoAddress = true; + PackageInfoGV->setSanitizerMetadata(Meta); + return {PackageInfoGV, PkgInfoType}; +} + +bool ReflectInfo::fillPackageInfo() { + NamedMDNode *PkgInfoMD = M.getNamedMetadata("pkg_info"); + if (PkgInfoMD == nullptr) + return false; + + MDTuple *PkgInfoTuple = dyn_cast(PkgInfoMD->getOperand(0)); + if (PkgInfoTuple->getNumOperands() != PIT_MAX) { + report_fatal_error("invalid package info metadata!"); + } + + StringRef Version = getStringFromMD(PkgInfoTuple, PIT_VERSION); + StringRef ModuleName = getStringFromMD(PkgInfoTuple, PIT_MODULE_NAME); + StringRef PkgName = getStringFromMD(PkgInfoTuple, PIT_PACKAGE_NAME); + StringRef CurName = getStringFromMD(PkgInfoTuple, PIT_CURRENT_NAME); + StringRef NextName = getStringFromMD(PkgInfoTuple, PIT_NEXT_NAME); + + auto [PackageInfoGV, PkgInfoType] = buildPackageInfoGV(CurName.str()); + + SmallVector PkgInfoGVInit; + unsigned TypeNum = TypeInfos.size(); + unsigned TemplateNum = TypeTemplates.size(); + unsigned GlobalFuncNums = StaticGlobalMethods.size(); + unsigned GlobalVarNums = StaticGlobalFields.size(); + if (NextName.empty()) { + PkgInfoGVInit.push_back(Int8PtrNull); + } else { + std::string NextPkgName = NextName.str() + ".packageInfo"; + auto NextPackageInfo = + dyn_cast(M.getOrInsertGlobal(NextPkgName, Int8PtrTy)); + NextPackageInfo->setLinkage(GlobalValue::ExternalLinkage); + PkgInfoGVInit.push_back( + ConstantExpr::getBitCast(NextPackageInfo, Int8PtrTy)); + } + PkgInfoGVInit.push_back(ConstantInt::get(Int32Ty, TypeNum)); + PkgInfoGVInit.push_back(ConstantInt::get(Int32Ty, TemplateNum)); + PkgInfoGVInit.push_back(ConstantInt::get(Int32Ty, GlobalFuncNums)); + PkgInfoGVInit.push_back(ConstantInt::get(Int32Ty, GlobalVarNums)); + PkgInfoGVInit.push_back(ConstantInt::get(Int64Ty, enable(M))); + + PkgInfoGVInit.push_back(ConstantExpr::getBitCast( + getOrInsertConstStringGV(M, ModuleName), Int8PtrTy)); + + PkgInfoGVInit.push_back(ConstantExpr::getBitCast( + getOrInsertConstStringGV(M, PkgName), Int8PtrTy)); + + PkgInfoGVInit.push_back(ConstantExpr::getBitCast( + getOrInsertConstStringGV(M, Version), Int8PtrTy)); + + PkgInfoGVInit.push_back(Int8PtrNull); + PkgInfoGVInit.push_back(Int8PtrNull); + + for (unsigned Idx = 0; Idx < TypeNum; ++Idx) { + PkgInfoGVInit.push_back( + ConstantExpr::getBitCast(TypeInfos[Idx], Int8PtrTy)); + } + for (unsigned Idx = 0; Idx < TemplateNum; ++Idx) { + PkgInfoGVInit.push_back( + ConstantExpr::getBitCast(TypeTemplates[Idx], Int8PtrTy)); + } + // add global funcs + for (auto Itr = StaticGlobalMethods.begin(), End = StaticGlobalMethods.end(); + Itr != End; ++Itr) { + PkgInfoGVInit.push_back(Itr->second->fillMethodInfo(0, "")); + } + // add global vars + for (auto Itr = StaticGlobalFields.begin(), End = StaticGlobalFields.end(); + Itr != End; ++Itr) { + PkgInfoGVInit.push_back(Itr->second->fillStaticFieldInfo()); + } + PackageInfoGV->setInitializer( + ConstantStruct::get(PkgInfoType, PkgInfoGVInit)); + + M.eraseNamedMetadata(PkgInfoMD); + return true; +} + +bool ReflectInfo::enable(Module &M) { + NamedMDNode *PkgInfoMD = M.getNamedMetadata("pkg_info"); + if (PkgInfoMD == nullptr) + return false; + + MDTuple *PkgInfoTuple = dyn_cast(PkgInfoMD->getOperand(0)); + if (PkgInfoTuple->getNumOperands() != PIT_MAX) { + report_fatal_error("invalid package info metadata!"); + } + auto MDConstant = + mdconst::extract(PkgInfoTuple->getOperand(PIT_FLAG).get()); + if (MDConstant->getZExtValue() > 0) + return true; + + return false; +} + +void ReflectInfo::fillClassReflectInfo(ClassReflectInfo &Info, + SmallVector &BodyVec) { + BodyVec[FRT_FIELD_NAME] = Info.fillInstanceFieldNames(TIName); + BodyVec[FRT_MODIFIER] = ConstantInt::get(Int32Ty, Info.Modifier); + BodyVec[FRT_INSTANCE_FIELD] = + ConstantInt::get(Int16Ty, Info.InstanceFields.size()); + BodyVec[FRT_STATIC_FIELD] = + ConstantInt::get(Int16Ty, Info.StaticFields.size()); + BodyVec[FRT_INSTANCE_METHOD] = + ConstantInt::get(Int32Ty, Info.InstanceMethods.size()); + BodyVec[FRT_STATIC_METHOD] = + ConstantInt::get(Int32Ty, Info.StaticMethods.size()); + BodyVec[FRT_ANNOTATION] = Info.Annotation; + if (Info.GenericClass.empty()) { + BodyVec[FRT_GENERIC_CLASS] = Int8PtrNull; + } else { + BodyVec[FRT_GENERIC_CLASS] = getNameGlobal(M, Info.GenericClass); + } + + for (unsigned Idx = 0; Idx < Info.InstanceFields.size(); ++Idx) { + BodyVec.push_back( + Info.InstanceFields[Idx]->fillInstanceFieldInfo(Idx, TIName)); + } + for (unsigned Idx = 0; Idx < Info.StaticFields.size(); ++Idx) { + BodyVec.push_back(Info.StaticFields[Idx]->fillStaticFieldInfo()); + } + for (unsigned Idx = 0; Idx < Info.InstanceMethods.size(); ++Idx) { + BodyVec.push_back(Info.InstanceMethods[Idx]->fillMethodInfo(Idx, TIName)); + } + for (unsigned Idx = 0; Idx < Info.StaticMethods.size(); ++Idx) { + BodyVec.push_back(Info.StaticMethods[Idx]->fillMethodInfo(Idx, TIName)); + } +} + +void ReflectInfo::fillEnumReflectInfo(EnumReflectInfo &Info, + SmallVector &BodyVec) { + BodyVec[FET_CTOR_INFO] = Info.fillEnumCtorInfos(TIName); + BodyVec[FET_MODIFIER] = ConstantInt::get(Int32Ty, Info.Modifier); + BodyVec[FET_CTOR_CNT] = ConstantInt::get(Int32Ty, Info.EnumCtors.size()); + BodyVec[FET_INSTANCE_METHOD] = + ConstantInt::get(Int32Ty, Info.InstanceMethods.size()); + BodyVec[FET_STATIC_METHOD] = + ConstantInt::get(Int32Ty, Info.StaticMethods.size()); + BodyVec[FET_ANNOTATION] = Info.Annotation; + + for (unsigned Idx = 0; Idx < Info.InstanceMethods.size(); ++Idx) { + BodyVec.push_back(Info.InstanceMethods[Idx]->fillMethodInfo(Idx, TIName)); + } + for (unsigned Idx = 0; Idx < Info.StaticMethods.size(); ++Idx) { + BodyVec.push_back(Info.StaticMethods[Idx]->fillMethodInfo(Idx, TIName)); + } +} + +void ReflectInfo::parseAttributes(MDTuple *Attr, unsigned &Modifier, + Constant *&Annotation) { + unsigned Idx = 0; + unsigned IdxEnd = Attr->getNumOperands(); + // last element is annotation info + while (Idx < IdxEnd - 1) { + Modifier |= getTypeModifier(getStringFromMD(Attr, Idx++)); + } + // 1: set to default if no modifier in attributes + if (Modifier == 0) { + Modifier = 1; + } + StringRef AnnoFuncName = getStringFromMD(Attr, Idx); + if (AnnoFuncName.equals("none")) { + Annotation = Int8PtrNull; + return; + } + Function *Func = M.getFunction(AnnoFuncName); + if (Func == nullptr) { + report_fatal_error(AnnoFuncName + ": can't find this anno func!"); + } + Annotation = ConstantExpr::getBitCast(Func, Int8PtrTy); +} + +// !InstanceFieldMDs = !{!{name1 attributes}, !{name2 attributes},...} +void ReflectInfo::parseInstanceFieldMDs(ClassReflectInfo &Info, + const MDTuple *FieldMDs) { + for (unsigned Idx = 0; Idx < FieldMDs->getNumOperands(); ++Idx) { + auto *FieldMD = dyn_cast(FieldMDs->getOperand(Idx).get()); + FieldInfo *TempInfo = getFieldInfo(); + parseOneInstanceFieldMd(*TempInfo, FieldMD); + Info.FieldNames.emplace_back(TempInfo->FieldName); + if (TempInfo->Modifier & RMT_PUBLIC) { + Info.InstanceFields.emplace_back(TempInfo); + } + } +} + +// !StaticFieldMDs = !{ +//  !{field1 name, field1 type name, field1 link name, field1 attributes}, +//  !{field2 name, field2 type name, field2 link name, field2 attributes}, +//  ... +// } +void ReflectInfo::parseStaticFieldMDs(ClassReflectInfo &Info, + const MDTuple *FieldMDs) { + for (unsigned Idx = 0; Idx < FieldMDs->getNumOperands(); ++Idx) { + auto *FieldMD = dyn_cast(FieldMDs->getOperand(Idx).get()); + FieldInfo *TempInfo = getFieldInfo(); + parseOneStaticFieldMd(*TempInfo, FieldMD); + if (TempInfo->Modifier & RMT_PUBLIC) { + auto Itr = StaticGlobalFields.find(TempInfo->StaticName); + if (Itr != StaticGlobalFields.end()) { + report_fatal_error(TempInfo->StaticName + + ": static field's reflect info cann't exist in " + "global var at the same time!"); + } + Info.StaticFields.emplace_back(TempInfo); + } + } + return; +} + +// !{paramName, typeName, !attributes} +void ReflectInfo::parseParamMd(SmallVectorImpl &ParamInfos, + MDTuple *ParamMD) { + uint32_t TempModifiers = 0; + for (unsigned Idx = 0, End = ParamMD->getNumOperands(); Idx < End; ++Idx) { + ParamInfo Param; + auto *ParamTuple = dyn_cast(ParamMD->getOperand(Idx)); + Param.TypeName = getStringFromMD(ParamTuple, 1); + Param.ParamName = getStringFromMD(ParamTuple, 0); + // 2: attributes + MDTuple *AttributeMD = getMDOperand(ParamTuple, 2); + parseAttributes(AttributeMD, TempModifiers, Param.Annotation); + ParamInfos.emplace_back(Param); + } +} + +// !{typeName} +void ReflectInfo::parseGenericParamMd(SmallVectorImpl &ParamInfos, + MDTuple *ParamMD) { + for (unsigned Idx = 0, End = ParamMD->getNumOperands(); Idx < End; ++Idx) { + ParamInfos.emplace_back(getStringFromMD(ParamMD, Idx)); + } +} + +// !{name, returnType, linkName, attributes, actualParam, genericParam} +bool ReflectInfo::parseOneMethodMd(MethodInfo &Info, MDTuple *MethodMD, + bool IsStatic) { + uint32_t Modifier = 0; + // 3: attribute + parseAttributes(getMDOperand(MethodMD, MRT_ATTRIBUTE), Modifier, + Info.Annotation); + bool IsPublic = Modifier & RMT_PUBLIC; + if (!IsPublic) { + return false; + } + Info.IsStatic = IsStatic; + Info.Modifier = Modifier; + Info.MethodLinkName = getStringFromMD(MethodMD, MRT_LINKAGE_NAME); + Info.MethodName = getStringFromMD(MethodMD, MRT_NAME); + Info.ReturnType = getStringFromMD(MethodMD, MRT_RETURN_TYPE); + MDTuple *ActualParamMD = getMDOperand(MethodMD, MRT_ACTUAL_PARAM); + parseParamMd(Info.ActualParams, ActualParamMD); + MDTuple *GenericParamMD = getMDOperand(MethodMD, MRT_GENERIC_PARAM); + parseGenericParamMd(Info.GenericParams, GenericParamMD); + return true; +} + +void ReflectInfo::parseInstanceMethodMDs(SmallVectorImpl &Infos, + MDTuple *MethodMDs) { + for (unsigned Idx = 0; Idx < MethodMDs->getNumOperands(); ++Idx) { + auto *MethodMD = dyn_cast(MethodMDs->getOperand(Idx).get()); + MethodInfo *TempInfo = getMethodInfo(); + bool IsPublic = parseOneMethodMd(*TempInfo, MethodMD, false); + if (IsPublic) { + Infos.emplace_back(TempInfo); + } + } +} + +void ReflectInfo::parseStaticMethodMDs(SmallVectorImpl &Infos, + MDTuple *MethodMDs) { + for (unsigned Idx = 0; Idx < MethodMDs->getNumOperands(); ++Idx) { + auto *MethodMD = dyn_cast(MethodMDs->getOperand(Idx).get()); + MethodInfo *TempInfo = getMethodInfo(); + bool IsPublic = parseOneMethodMd(*TempInfo, MethodMD, true); + if (IsPublic) { + auto Itr = StaticGlobalMethods.find(TempInfo->MethodLinkName); + if (Itr != StaticGlobalMethods.end()) { + report_fatal_error(TempInfo->MethodLinkName + + ": static method's reflect info cann't exist in " + "global funcs at the same time!"); + } + Infos.emplace_back(TempInfo); + } + } +} + +void ReflectInfo::parseEnumCtorMDs(SmallVectorImpl &Info, + MDTuple *CtorMDs) { + for (unsigned Idx = 0; Idx < CtorMDs->getNumOperands(); ++Idx) { + auto *MethodMD = dyn_cast(CtorMDs->getOperand(Idx).get()); + EnumCtorInfo CtorInfo; + CtorInfo.CtorName = getStringFromMD(MethodMD, 0); + CtorInfo.TypeName = getStringFromMD(MethodMD, 1); + Info.emplace_back(CtorInfo); + } +} + +// InstanceFieldMD: !{idx, , } +void ReflectInfo::parseOneInstanceFieldMd(FieldInfo &Info, MDTuple *FieldMD) { + uint32_t Modifier = 0; + parseAttributes(getMDOperand(FieldMD, 2), Modifier, Info.Annotation); + Info.Modifier = Modifier; + Info.IsStatic = false; + auto MDConstant = + mdconst::extract_or_null(FieldMD->getOperand(0).get()); + Info.FieldIdx = MDConstant->getZExtValue(); + Info.FieldName = getStringFromMD(FieldMD, 1); +} + +// StaticFieldMD: !{, , , } +void ReflectInfo::parseOneStaticFieldMd(FieldInfo &Info, MDTuple *FieldMD) { + uint32_t Modifier = 0; + // 3: attributes + parseAttributes(getMDOperand(FieldMD, 3), Modifier, Info.Annotation); + Info.Modifier = Modifier; + Info.IsStatic = true; + // 3: field's linkname + Info.StaticName = getStringFromMD(FieldMD, 2); + Info.TypeName = getStringFromMD(FieldMD, 1); + Info.FieldName = getStringFromMD(FieldMD, 0); +} + +void ReflectInfo::getGlobalsFromNamedMD(SetVector &GVs, + NamedMDNode *MDNode) { + if (MDNode == nullptr) + return; + + for (unsigned Idx = 0; Idx < MDNode->getNumOperands(); ++Idx) { + auto *MD = dyn_cast(MDNode->getOperand(Idx)); + GVs.insert(getNameGlobal(M, getStringFromMD(MD, 0))); + } +} + +class TypeInfoMaker { +public: + TypeInfoMaker(Module &M, CJStructTypeGCInfo &GCInfo, ReflectInfo &Reflector) + : M(M), C(M.getContext()), DL(M.getDataLayout()), GCInfo(GCInfo), + Reflector(Reflector) {} + + void fillTypeInfo(GlobalVariable *GV) { + buildTypeInfoBody(GV); + + // no need to append 16-bytes asan guard for TI. + GlobalValue::SanitizerMetadata Meta; + Meta.NoAddress = true; + GV->setSanitizerMetadata(Meta); + } + + void fillTemplate(GlobalVariable *GV) { + TypeTemplate TT(GV); + uint8_t Flag = TT.getFlag(); + if (GV->hasAttribute("Future")) { + assert(TT.isSyncClass() && "Not a Future TypeTemplate!"); + Flag |= TF_FUTURE_CLASS; + } + if (Reflector.enable(M)) { + Flag |= TF_REFLECTION; + } + + SmallVector BodyVec(TT_MAX); + // Initialize all fields. + for (unsigned Idx = 0; Idx < TT_MAX; ++Idx) { + if (Idx == TT_FLAG) { + BodyVec[Idx] = ConstantInt::get(Type::getInt8Ty(C), Flag); + } else if (Idx == TT_REFLECTION) { + BodyVec[Idx] = Reflector.getReflectionInfo(GV, TT.isEnum()); + } else { + BodyVec[Idx] = TT.get(Idx); + } + } + // rewrite GV data. + GV->setInitializer(ConstantStruct::get(TT.TT->getType(), BodyVec)); + } + + void buildTypeInfoBody(GlobalVariable *GV) { + TypeInfo TI(GV); + SmallVector BodyVec(CIT_MAX); + // Initialize all fields. + initializeTypeInfoElements(TI, BodyVec); + + if (StructType *ST = TI.getLayoutType()) { + // Generating the gctib of the struct type. + auto &Info = GCInfo.getOrInsertTypeGCInfo(ST, TI.isArray()); + + if (TI.isSyncClass()) { + Info.ObjSize = SyncSize - 8; // 8: typeinfo* size + } + + BodyVec[CIT_FLAG] = + ConstantInt::get(Type::getInt8Ty(C), getFlag(GV, Info.BMInfo.HasRef)); + if (!TI.isArray()) { + BodyVec[CIT_SIZE] = ConstantInt::get(Type::getInt32Ty(C), Info.ObjSize); + Type *BMType = TI.getGCTib()->getType(); + std::string NamePrefix = GV->getName().str(); + BodyVec[CIT_GCTIB] = + GCInfo.getOrInsertBitMap(Info.BMInfo.BMStr, BMType, NamePrefix); + } + } + + if (!TI.isGenericType()) { + BodyVec[CIT_REFLECTION] = Reflector.getReflectionInfo(GV, TI.isEnum()); + } + // rewrite GV data. + GV->setInitializer(ConstantStruct::get(TI.Data->getType(), BodyVec)); + } + + bool needFillTypeInfo(GlobalVariable *GV) { + if (!GV->hasAttribute(CFileKlassStr)) + return false; + + TypeInfo TI(GV); + if (TI.isPureValueType()) + return false; + + return true; + } + +private: + Module &M; + LLVMContext &C; + const DataLayout &DL; + CJStructTypeGCInfo &GCInfo; + ReflectInfo &Reflector; + + unsigned getFlag(GlobalVariable *TI, bool HasRef) { + assert(TI != nullptr && "Is not valid TypeInfo"); + unsigned Flag = 0; + if (HasRef) + Flag |= TF_HAS_REF_FIELD; + + if (TI->hasAttribute("HasFinalizer")) + Flag |= TF_HAS_FINALIZER; + + if (TI->hasAttribute("Future")) + Flag |= TF_FUTURE_CLASS; + + if (TI->hasAttribute("Mutex")) + Flag |= TF_MUTEX_CLASS; + + if (TI->hasAttribute("Monitor")) + Flag |= TF_MONITOR_CLASS; + + if (TI->hasAttribute("WaitQueue")) + Flag |= TF_WAIT_QUEUE_CLASS; + + if (Reflector.enable(M)) + Flag |= TF_REFLECTION; + + return Flag; + } + + void initializeTypeInfoElements(TypeInfo &TypeInfo, + SmallVectorImpl &BodyVec) { + for (int i = 0; i < CIT_MAX; ++i) { + BodyVec[i] = TypeInfo.get(i); + } + } +}; + +static Value *analysisGVType(Value *V) { + auto *CI = dyn_cast(V); + if (!CI) + return nullptr; + + unsigned ID = CI->getIntrinsicID(); + if (ID == Intrinsic::cj_malloc_object) { + if (auto CE = dyn_cast(CI->getOperand(0))) + return CE->getOperand(0); + } + return nullptr; +} + +static bool setDefiniteTypeGV(GlobalVariable *GV) { + // first support only gcptr. + auto *I8PtrTy = Type::getInt8Ty(GV->getContext())->getPointerTo(1); + auto *I8PtrPtrTy = I8PtrTy->getPointerTo(); + if (GV->getType() != I8PtrPtrTy || !GV->hasInternalLinkage()) { + return false; + } + SmallSet TypeVec; + for (auto I = GV->user_begin(), E = GV->user_end(); I != E;) { + User *U = *(I++); + auto *CB = dyn_cast(U); + if (!CB) + return false; + + unsigned ID = CB->getIntrinsicID(); + if (ID == Intrinsic::cj_gcwrite_static_ref) { + TypeVec.insert(analysisGVType(CB->getOperand(0))); + continue; + } else if (ID == Intrinsic::cj_gcread_static_ref) { + continue; + } else { + return false; + } + } + // If all uses of global value i8 AS (1) ** have only barrier read and write, + // and are unique TypeInfo, mark global value metadata information. + if (TypeVec.size() == 1 && *TypeVec.begin() != nullptr) { + MDString *SS = MDString::get( + GV->getContext(), dyn_cast(*TypeVec.begin())->getName()); + GV->setMetadata("cjType", MDNode::get(GV->getContext(), {SS})); + return true; + } + return false; +} + +static bool markTypeInfoTypeForGCRoots(Module &M) { + Module::GlobalListType &GVlist = M.getGlobalList(); + bool Changed = false; + for (auto I = GVlist.begin(), E = GVlist.end(); I != E; ++I) { + if (I->hasMetadata("GlobalVarType")) { + if (setDefiniteTypeGV(cast(I))) { + Changed = true; + } + } + } + return Changed; +} + +static bool fillGV(Module &M) { + CJStructTypeGCInfo GCInfo(M); + ReflectInfo Reflector(M); + TypeInfoMaker TIMaker(M, GCInfo, Reflector); + + bool Changed = markTypeInfoTypeForGCRoots(M); + Module::GlobalListType &GVlist = M.getGlobalList(); + for (auto I = GVlist.begin(), E = GVlist.end(); I != E; ++I) { + if (!I->hasInitializer()) + continue; + + GlobalVariable *GV = dyn_cast(I); + if (TIMaker.needFillTypeInfo(GV)) { + Changed = true; + TIMaker.fillTypeInfo(GV); + continue; + } + if (GV->hasAttribute("cj_tt")) { + TIMaker.fillTemplate(GV); + } + } + Changed |= Reflector.fillPackageInfo(); + return Changed; +} + +PreservedAnalyses CJFillMetadata::run(Module &M, ModuleAnalysisManager &) const { + if (hasRunCangjieOpt(M)) { + return PreservedAnalyses::all(); + } + if (fillGV(M)) { + return PreservedAnalyses::none(); + } + return PreservedAnalyses::all(); +} + +namespace { +class CJFillMetadataLegacyPass : public ModulePass { +public: + static char ID; + CJFillMetadataLegacyPass() : ModulePass(ID) { + initializeCJFillMetadataLegacyPassPass(*PassRegistry::getPassRegistry()); + } + ~CJFillMetadataLegacyPass() = default; + + bool runOnModule(Module &M) override { return fillGV(M); }; +}; +} // namespace + +char CJFillMetadataLegacyPass::ID = 0; + +ModulePass *llvm::createCJFillMetadataLegacyPass() { + return new CJFillMetadataLegacyPass(); +} + +INITIALIZE_PASS(CJFillMetadataLegacyPass, "cj-fill-metadata", "Fill Cangjie Metadata", false, + false) diff --git a/llvm/lib/Transforms/Scalar/CJGCLiveAnalysis.cpp b/llvm/lib/Transforms/Scalar/CJGCLiveAnalysis.cpp new file mode 100644 index 000000000..04731126d --- /dev/null +++ b/llvm/lib/Transforms/Scalar/CJGCLiveAnalysis.cpp @@ -0,0 +1,1468 @@ +//===- CJGCLiveAnalysis.cpp - GC Live Analysis For Cangjie ------*- 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 +// +//===----------------------------------------------------------------------===// +// +// Analyze and calculate live cangjie references based on cfg. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/CJGCLiveAnalysis.h" + +#include "llvm/ADT/Optional.h" +#include "llvm/IR/Argument.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/CJIntrinsics.h" +#include "llvm/IR/CallingConv.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/MDBuilder.h" +#include "llvm/IR/Metadata.h" +#include "llvm/IR/SafepointIRVerifier.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Scalar/InsertCJTBAA.h" + +#define DEBUG_TYPE "gc-live-analysis" + +using namespace llvm; +using namespace cangjie; + +// Print the liveset found at the insert location +static cl::opt PrintLiveSet("cj-spp-print-liveset", cl::Hidden, + cl::init(false)); + +// Output log information and terminate the program. +void checkFailed(const Twine &Message, Instruction *I) { + dbgs() << "Statepoint-Check: " << Message << "\n"; + dbgs() << " " << *I << "\n"; + I->getParent()->getParent()->print(dbgs()); + dbgs() << '\n'; + report_fatal_error("Broken function found, compilation aborted!"); +} + +#define Check(C, ...) \ + do { \ + if (!(C)) { \ + checkFailed(__VA_ARGS__); \ + return; \ + } \ + } while (false) + +namespace llvm { +// Find all base defining values reachable from the initial. +// Return true if the terminator point is alloca, and if the struct contains +// ref of heap, save it to liveset, otherwise ignore it. +// Returns false if the base pointer is a gcptr. +bool findAllocaInsts(Value *V, SetVector &BaseSet, bool HasArg) { + assert(V->getType()->isPointerTy() && + "Illegal to ask for the base pointer of a non-pointer type"); + + bool IsAlloca = false; + bool IsGCPtr = false; + SmallSetVector WorkList; + WorkList.insert(V); + + // Caches all the phi and select point that have been found. This cache + // ensures that the infinite loop of calling the phi does not occur. + SetVector Cache; + + while (!WorkList.empty()) { + Value *Ptr = WorkList.pop_back_val(); + + if (Cache.contains(Ptr)) { + continue; + } + Cache.insert(Ptr); + + if (auto *PN = dyn_cast(Ptr)) { + WorkList.insert(PN->incoming_values().begin(), + PN->incoming_values().end()); + } else if (auto *SI = dyn_cast(Ptr)) { + WorkList.insert(SI->getTrueValue()); + WorkList.insert(SI->getFalseValue()); + } else if (isa(Ptr) || isa(Ptr)) { + WorkList.insert(Ptr->stripInBoundsOffsets()); + } else if (isa(Ptr)) { + if (isMemoryContainsGCPtrType(Ptr->getType())) { + BaseSet.insert(Ptr); + } + IsAlloca = true; + } else { + assert((isa(Ptr) || isa(Ptr) || + isa(Ptr) || isa(Ptr) || + isa(Ptr) || isa(Ptr)) && + "unexpected instructions in findStructPtrAlloca"); + if (isa(Ptr) && HasArg) { + BaseSet.insert(Ptr); + } + // If one of the base pointers is gcptr, return false. + if (!isa(Ptr) && isGCPointerType(Ptr->getType())) { + IsGCPtr = true; + } + } + } + return IsGCPtr ? false : IsAlloca; +} +} // end namespace llvm + +static void +prepareStructData(Function &F, SetVector &AllStructs, + SmallSetVector &CallStructSet, + MapVector &StructToMemsetMap, + SmallSet &InsertedMemset) { + for (auto &I : instructions(F)) { + if (auto MI = dyn_cast(&I)) { + Value *Base = findMemoryBasePointer(MI->getArgOperand(0)); + if (InsertedMemset.contains(MI) && AllStructs.count(Base)) { + StructToMemsetMap[Base] = MI; + } + } + if (auto CB = dyn_cast(&I)) { + if (isa(CB)) + continue; + + for (unsigned ArgIdx = 0; ArgIdx < CB->arg_size(); ++ArgIdx) { + Value *Base = findMemoryBasePointer(CB->getArgOperand(ArgIdx)); + if (AllStructs.count(Base)) { + CallStructSet.insert(Base); + } + } + } + } +} + +using StructDefineTy = MapVector>; +static void computeStructDefineSet(BasicBlock &BB, StructDefineTy &StructDefs, + SetVector &AllStructs) { + for (Instruction &I : BB) { + Value *Base = nullptr; + if (auto SI = dyn_cast(&I)) { + Base = findMemoryBasePointer(SI->getPointerOperand()); + } else if (auto II = dyn_cast(&I)) { + Intrinsic::ID IID = II->getIntrinsicID(); + if (IID == Intrinsic::memset || IID == Intrinsic::memcpy || + IID == Intrinsic::memmove) { + Base = findMemoryBasePointer(II->getArgOperand(0)); + } + } + + if (Base != nullptr && AllStructs.count(Base)) { + StructDefs[Base].insert(&I); + } + } +} + +// Some redundant memsets can be deleted after liveness variable analysis. +static void eliminateDeadMemset(StructLiveData &StructLives, Function &F, + PostDominatorTree &PostDT, + AllocaAnalysisData &AllocaData, + SmallSet &InsertedMemset) { + SetVector &AllStructs = AllocaData.Structs; + if (AllStructs.empty()) { + return; + } + + SmallSetVector CallStructSet; + MapVector StructToMemsetMap; + prepareStructData(F, AllStructs, CallStructSet, StructToMemsetMap, + InsertedMemset); + + // record map > + MapVector> StructDefs; + + for (BasicBlock &BB : F) { + if (!StructLives.KillSet[&BB].empty()) { + computeStructDefineSet(BB, StructDefs, AllStructs); + } + } + + SmallSetVector DeadMemsets; + SmallSetVector RecordAIs; + + for (auto &P : StructDefs) { + Value *V = P.first; + // If this AI does not have memset, or as the parameter of the call + // instruction.do nothing. + if (!StructToMemsetMap.count(V) || CallStructSet.count(V)) + continue; + + assert(P.second.size() > 0 && "The AI has no definition point!"); + // If the AI definition point is only memset, do nothing. + if (P.second.size() == 1) + continue; + + Instruction *MI = StructToMemsetMap[V]; + bool CanDominate = true; + for (auto Def : P.second) { + // If the define point cannot post-dominates the memset inst, we need + // this memset inst. + if (Def != MI && !PostDT.dominates(Def, MI)) { + CanDominate = false; + break; + } + } + + if (CanDominate) { + DeadMemsets.insert(MI); + RecordAIs.insert(V); + } + } + +#ifndef NDEBUG + if (RecordAIs.size() > 0) { + LLVM_DEBUG(dbgs() << "Statepoint-print: Memset Opt\n"); + LLVM_DEBUG(dbgs() << " Function: " << F.getName() << "\n"); + LLVM_DEBUG(dbgs() << " Number of optimized memsets: " << RecordAIs.size() + << "\n"); + for (auto V : RecordAIs) + LLVM_DEBUG(dbgs() << " " << *V << "\n"); + } +#endif + + for (Instruction *MI : DeadMemsets) { + MI->eraseFromParent(); + } +} + +// Computes variables that are converted to type `i8 addrspace(1)*` but +// are actually pointers on the stack, and save it. +// If it contains gcptr, save it to ContainGCPtrsFakes. +void GCLiveAnalysis::computeFakeGCPtrs() { + for (Instruction &I : instructions(F)) { + Type *Ty = I.getType(); + if (!isGCPointerType(Ty)) { + continue; + } + // BaseSet saves alloca inst that contain gcptr. + SetVector AllocaSet; + if (findAllocaInsts(&I, AllocaSet)) { + FakeGCPtrs.insert(&I); + if (AllocaSet.size() > 0) { + ContainGCPtrsFakes.insert(&I); + } + } + } +} + +#ifndef NDEBUG + +/// Check that the items in 'Live' dominate 'TI'. This is used as a basic +/// validation check for the liveness computation. +void GCLiveAnalysis::checkBasicSSA(SetVector &Live, Instruction *TI, + bool TermOkay) { + for (Value *V : Live) { + if (auto *I = dyn_cast(V)) { + // The terminator can be a member of the LiveOut set. LLVM's definition + // of instruction dominance states that V does not dominate itself. As + // such, we need to special case this to allow it. + if (TermOkay && TI == I) + continue; + assert(DT.dominates(I, TI) && + "basic SSA liveness expectation violated by gc-live analysis"); + } + } +} + +/// Check that all the liveness sets used during the computation of liveness +/// obey basic SSA properties. This is useful for finding cases where we miss +/// a def. +void GCLiveAnalysis::checkBasicSSA(PtrLiveData &Data) { + for (BasicBlock &BB : F) { + checkBasicSSA(Data.LiveSet[&BB], BB.getTerminator()); + checkBasicSSA(Data.LiveOut[&BB], BB.getTerminator(), true); + checkBasicSSA(Data.LiveIn[&BB], BB.getTerminator()); + } +} + +#endif + +// Conservatively identifies any definitions which might be live at the +// given instruction. The analysis is performed immediately before the +// given instruction. Values defined by that instruction are not considered +// live. Values used by that instruction are considered live. +void GCLiveAnalysis::analyzeParsePointLiveness(CallBase *Call, + SetVector &LiveSet) { + findLiveSetAtInst(Call, LiveSet); + +#ifndef NDEBUG + if (PrintLiveSet) { + LLVM_DEBUG(dbgs() << "Safepoint For: " + << Call->getCalledOperand()->getName() << "\n"); + LLVM_DEBUG(dbgs() << " Number live values: " << LiveSet.size() << "\n"); + LLVM_DEBUG(dbgs() << " Live Variables:\n"); + for (Value *V : LiveSet) + LLVM_DEBUG(dbgs() << " " << V->getName() << " " << *V << "\n"); + } +#endif +} + +// liveness computation via standard dataflow +// ------------------------------------------------------------------- +// Compute the live-in set for the location rbegin starting from +// the live-out set of the basic block +void GCLiveAnalysis::computeLiveInValues(BasicBlock::reverse_iterator Begin, + BasicBlock::reverse_iterator End, + SetVector &LiveTmp) { + for (auto &I : make_range(Begin, End)) { + // KILL/Def - Remove this definition from LiveIn for RegisterType + LiveTmp.remove(&I); + + // Don't consider *uses* in PHI nodes, we handle their contribution to + // predecessor blocks when we seed the LiveOut sets + if (isa(I)) + continue; + + // USE - Add to the LiveIn set for this instruction + for (Value *V : I.operands()) { + if (isa(V)) + continue; + + if (isHandledGCPointerType(V->getType()) && !FakeGCPtrs.count(V)) { + // The choice to exclude all things constant here is slightly subtle. + // There are two independent reasons: + // - We assume that things which are constant (from LLVM's definition) + // do not move at runtime. For example, the address of a global + // variable is fixed, even though it's contents may not be. + // - Second, we can't disallow arbitrary inttoptr constants even + // if the language frontend does. Optimization passes are free to + // locally exploit facts without respect to global reachability. This + // can create sections of code which are dynamically unreachable and + // contain just about anything. (see constants.ll in tests) + LiveTmp.insert(V); + } + } + } +} + +void GCLiveAnalysis::computeLiveOutSeed(BasicBlock *BB, + SetVector &LiveTmp) { + for (BasicBlock *Succ : successors(BB)) { + for (auto &I : *Succ) { + PHINode *PN = dyn_cast(&I); + if (!PN) + break; + + Value *V = PN->getIncomingValueForBlock(BB); + if (isHandledGCPointerType(V->getType()) && !isa(V) && + !FakeGCPtrs.count(V)) { + LiveTmp.insert(V); + } + } + } +} + +void GCLiveAnalysis::computeKillSet(BasicBlock *BB, + SetVector &KillSet) { + for (Instruction &I : *BB) { + if (isHandledGCPointerType(I.getType())) { + KillSet.insert(&I); + } + } +} + +/// Compute the live-in set for every basic block in the function +void GCLiveAnalysis::computeLiveness() { + SmallSetVector Worklist; + InstLiveSetCache.clear(); + + // Seed the liveness for each individual block + for (BasicBlock &BB : F) { + Data.KillSet[&BB].clear(); + computeKillSet(&BB, Data.KillSet[&BB]); + Data.LiveSet[&BB].clear(); + computeLiveInValues(BB.rbegin(), BB.rend(), Data.LiveSet[&BB]); + + Data.LiveOut[&BB] = SetVector(); + computeLiveOutSeed(&BB, Data.LiveOut[&BB]); + Data.LiveIn[&BB] = Data.LiveOut[&BB]; + Data.LiveIn[&BB].set_subtract(Data.KillSet[&BB]); + Data.LiveIn[&BB].set_union(Data.LiveSet[&BB]); + if (!Data.LiveIn[&BB].empty()) + Worklist.insert(pred_begin(&BB), pred_end(&BB)); + } + + // Propagate that liveness until stable + while (!Worklist.empty()) { + BasicBlock *BB = Worklist.pop_back_val(); + + // Compute our new liveout set, then exit early if it hasn't changed + // despite the contribution of our successor. + SetVector LiveOut = Data.LiveOut[BB]; + const auto OldLiveOutSize = LiveOut.size(); + for (BasicBlock *Succ : successors(BB)) { + assert(Data.LiveIn.count(Succ)); + LiveOut.set_union(Data.LiveIn[Succ]); + } + // assert OutLiveOut is a subset of LiveOut + if (OldLiveOutSize == LiveOut.size()) { + // If the sets are the same size, then we didn't actually add anything + // when unioning our successors LiveIn. Thus, the LiveIn of this block + // hasn't changed. + continue; + } + Data.LiveOut[BB] = LiveOut; + + // Apply the effects of this basic block + SetVector LiveTmp = LiveOut; + LiveTmp.set_subtract(Data.KillSet[BB]); + LiveTmp.set_union(Data.LiveSet[BB]); + + assert(Data.LiveIn.count(BB)); + const SetVector &OldLiveIn = Data.LiveIn[BB]; + // assert: OldLiveIn is a subset of LiveTmp + if (OldLiveIn.size() != LiveTmp.size()) { + Data.LiveIn[BB] = LiveTmp; + Worklist.insert(pred_begin(BB), pred_end(BB)); + } + } + +#ifndef NDEBUG + // Verify our output against SSA properties. This helps catch any + // missing kills during the above iteration. + checkBasicSSA(Data); +#endif +} + +/// Given results from the dataflow liveness computation, find the set of live +/// Values at a particular instruction. +void GCLiveAnalysis::findLiveSetAtInst(Instruction *Inst, + SetVector &Out) { + BasicBlock *BB = Inst->getParent(); + + // Note: The copy is intentional and required + assert(Data.LiveOut.count(BB)); + SetVector LiveOut = InstLiveSetCache.count(BB) + ? InstLiveSetCache[BB].second + : Data.LiveOut[BB]; + auto BI = InstLiveSetCache.count(BB) + ? InstLiveSetCache[BB].first->getIterator().getReverse() + : BB->rbegin(); + // We want to handle the statepoint itself oddly. It's + // call result is not live (normal), nor are it's arguments + // (unless they're used again later). This adjustment is + // specifically what we need to relocate + computeLiveInValues(BI, ++Inst->getIterator().getReverse(), LiveOut); + LiveOut.remove(Inst); + Out.insert(LiveOut.begin(), LiveOut.end()); + InstLiveSetCache[BB] = {Inst, Out}; +} + +FieldInfo *StructLiveAnalysis::getFieldInfo(Value *Ptr, int Offset) { + auto Key = std::make_pair(Ptr, Offset); + if (FieldMap.count(Key)) + return FieldMap[Key]; + + FieldInfo *FI = new FieldInfo(Ptr, Offset); + AllFields.push_back(FI); + FieldMap[Key] = FI; + return FI; +} + +FieldInfo *StructLiveAnalysis::getFieldInfoByValue(Value *Ptr) { + APInt Offsets(DL.getIndexSizeInBits(0), 0); + Value *PtrBase = Ptr->stripAndAccumulateConstantOffsets(DL, Offsets, false); + // If it is a phi or select instruction, the offset is set to 0. Otherwise, + // record the offset on the base. + if (isa(PtrBase) || isa(PtrBase) || + AllocaData.LargeStructs.contains(PtrBase)) { + return getFieldInfo(PtrBase, -1); + } + return getFieldInfo(PtrBase, Offsets.getZExtValue()); +} + +// Checks whether a range contains gcptr based on the struct type. +bool StructLiveAnalysis::containGCPtrByOffset(Value *Ptr, uint64_t Size) { + APInt Offsets(DL.getIndexSizeInBits(0), 0); + Value *Base = Ptr->stripAndAccumulateConstantOffsets(DL, Offsets, false); + if (isa(Base) && Size > 0) { + uint64_t Begin = Offsets.getZExtValue(); + uint64_t End = Begin + Size; + MapVector &OffsetMap = + AllocaData.StructLayoutGCPtrMap[Base]; + for (auto &Pair : OffsetMap) { + if (Pair.first >= Begin && Pair.first < End) { + if (Pair.second) { + return true; + } + } + } + } + return false; +} + +// The base of phi is an alloca and contains gcptr. +bool StructLiveAnalysis::isBaseContainGCPtr(Value *V) { + Value *Base = findMemoryBasePointer(V); + if (isMemoryContainsGCPtrType(Base->getType())) { + return true; + } + + if ((isa(V) || isa(V)) && + AllocaData.PhiToAllocasMap.count(V)) { + if (!AllocaData.PhiToAllocasMap[V].empty()) { + return true; + } + } + return false; +} + +#ifndef NDEBUG + +/// Check that the items in 'Live' dominate 'TI'. This is used as a basic +/// validation check for the liveness computation. +void StructLiveAnalysis::checkBasicSSA(SetVector &Live, + Instruction *TI, bool TermOkay) { + for (FieldInfo *FI : Live) { + if (auto *I = dyn_cast(FI->V)) { + // The terminator can be a member of the LiveOut set. LLVM's definition + // of instruction dominance states that V does not dominate itself. As + // such, we need to special case this to allow it. + if (TermOkay && TI == I) + continue; + assert(DT.dominates(I, TI) && + "basic SSA liveness expectation violated by struct-live analysis"); + } + } +} + +/// Check that all the liveness sets used during the computation of liveness +/// obey basic SSA properties. This is useful for finding cases where we miss +/// a def. +void StructLiveAnalysis::checkBasicSSA(StructLiveData &Data) { + for (BasicBlock &BB : F) { + checkBasicSSA(Data.LiveSet[&BB], BB.getTerminator()); + checkBasicSSA(Data.LiveOut[&BB], BB.getTerminator(), true); + checkBasicSSA(Data.LiveIn[&BB], BB.getTerminator()); + } +} + +#endif + +void StructLiveAnalysis::initializeAlloca(AllocaInst *AI) { + IRBuilder<> IRB(AI); + Instruction *PostI = AI->getNextNode(); + IRB.SetInsertPoint(PostI); + Type *AllocaType = AI->getAllocatedType(); + if (auto *ST = dyn_cast(AllocaType)) { + Value *Int8Ptr = IRB.CreateBitCast(AI, IRB.getInt8PtrTy()); + cast(Int8Ptr)->setDebugLoc(AI->getDebugLoc()); + uint64_t Size = DL.getStructLayout(ST)->getSizeInBytes(); + CallInst *CI = IRB.CreateMemSet(Int8Ptr, IRB.getInt8(0), Size, Align(8)); + CI->setDebugLoc(AI->getDebugLoc()); + InsertedMemset.insert(CI); + } else { + assert(isGCPointerType(AllocaType) && "The alloca type is not supported."); + StoreInst *SI = IRB.CreateStore( + ConstantPointerNull::get(cast(AllocaType)), AI); + SI->setDebugLoc(AI->getDebugLoc()); + } +} + +void StructLiveAnalysis::insertInitializerAfterLifetime(Value *V, + CallInst *CI) { + IRBuilder<> IRB(CI->getNextNode()); + AllocaInst *AI = cast(V); + Type *AllocaType = AI->getAllocatedType(); + if (AllocaType->isStructTy()) { + CallInst *Call = IRB.CreateMemSet(CI->getArgOperand(1), IRB.getInt8(0), + CI->getArgOperand(0), Align(8), false); + Call->setDebugLoc(CI->getDebugLoc()); + InsertedMemset.insert(CI); + } else if (AllocaType->isPointerTy()) { + StoreInst *SI = IRB.CreateStore( + ConstantPointerNull::get(cast(AllocaType)), AI); + SI->setDebugLoc(CI->getDebugLoc()); + } else { + checkFailed("AllocaInst Type error", AI); + } +} + +void StructLiveAnalysis::moveInitializerAfterLifetime(Value *V, Instruction *I, + CallInst *CI) { + if (CallInst *MI = dyn_cast(I)) { + MI->setArgOperand(0, CI->getArgOperand(1)); + MI->setArgOperand(2, CI->getArgOperand(0)); + MI->moveAfter(CI); + MI->setDebugLoc(CI->getDebugLoc()); + } else if (StoreInst *SI = dyn_cast(I)) { + SI->eraseFromParent(); + insertInitializerAfterLifetime(V, CI); + } else { + checkFailed("Intruction Type error", I); + } +} + +// For an alloca that contain gcptr to be used in phi, we need to insert an +// initializer in entry and lifetime.start. +void StructLiveAnalysis::insertMemsetForPhiStruct() { + SetVector AllocaStructs; + SetVector AllocaGCPtrs; + MapVector LifetimeStarts; + MapVector Memsets; + MapVector StoreNulls; + for (auto &I : instructions(F)) { + if (CallInst *CI = dyn_cast(&I)) { + if (CI->getIntrinsicID() == Intrinsic::lifetime_start) { + Value *Base = CI->getArgOperand(1)->stripPointerCasts(); + if (isa(Base) && + isMemoryContainsGCPtrType(Base->getType())) { + LifetimeStarts.insert({Base, CI}); + } + } else if (CI->getIntrinsicID() == Intrinsic::memset) { + Value *Base = CI->getArgOperand(0)->stripPointerCasts(); + if (isa(Base) && + isMemoryContainsGCPtrType(Base->getType()) && + isa(CI->getArgOperand(1))) { + Memsets.insert({Base, CI}); + } + } + continue; + } + + if (StoreInst *SI = dyn_cast(&I)) { + Constant *C = dyn_cast(SI->getValueOperand()); + if (C && C->isNullValue() && isGCPointerType(C->getType())) { + Value *Base = SI->getPointerOperand()->stripPointerCasts(); + if (isa(Base)) + StoreNulls.insert({Base, SI}); + } + continue; + } + + if (isa(&I) || isa(&I)) { + if (!I.getType()->isPointerTy()) + continue; + + SetVector Allocas; + findAllocaInsts(&I, Allocas); + for (auto V : Allocas) { + AllocaInst *AI = cast(V); + Type *Ty = AI->getAllocatedType(); + if (Ty->isStructTy()) { + AllocaStructs.insert(AI); + } else if (Ty->isPointerTy()) { + assert(isGCPointerType(Ty) && "Unsupport Alloca pointer type!"); + AllocaGCPtrs.insert(AI); + } else { + checkFailed("Unsupport Alloca type!", AI); + } + } + } + if (isa(&I) && AllocaData.LargeStructs.contains(&I)) + AllocaStructs.insert(&I); + } + + for (auto P : LifetimeStarts) { + Value *V = P.first; + CallInst *Lifetime = P.second; + if (AllocaStructs.contains(V)) { + if (Memsets.count(V)) { + CallInst *MI = Memsets[V]; + // Do nothing if there is a memset in lifetime.start + if (MI->getParent() == Lifetime->getParent()) { + continue; + } + if (MI->getParent()->isEntryBlock()) { + insertInitializerAfterLifetime(V, Lifetime); + continue; + } + // move it to lifetime + moveInitializerAfterLifetime(V, MI, Lifetime); + } else { + // Create a memset for alloca struct. + insertInitializerAfterLifetime(V, Lifetime); + } + continue; + } + if (AllocaGCPtrs.contains(V)) { + if (StoreNulls.count(V)) { + StoreInst *SI = StoreNulls[V]; + // Do nothing if there is a store null in lifetime.start + if (SI->getParent() == Lifetime->getParent()) { + continue; + } + if (SI->getParent()->isEntryBlock()) { + insertInitializerAfterLifetime(V, Lifetime); + continue; + } + // move it to lifetime + moveInitializerAfterLifetime(V, SI, Lifetime); + } else { + // Craete a store null for alloca gcptr. + insertInitializerAfterLifetime(V, Lifetime); + } + } + } + + // Filter out the alloca that has been initialized in the entry. + for (auto &I : F.getEntryBlock()) { + if (auto *SI = dyn_cast(&I)) { + Value *Ptr = SI->getPointerOperand()->stripPointerCasts(); + if (AllocaStructs.contains(Ptr)) { + AllocaStructs.remove(Ptr); + } else if (AllocaGCPtrs.contains(Ptr)) { + AllocaGCPtrs.remove(Ptr); + } + } else if (auto *II = dyn_cast(&I)) { + Value *Ptr = II->getArgOperand(0)->stripPointerCasts(); + if (AllocaStructs.contains(Ptr)) { + AllocaStructs.remove(Ptr); + } else if (AllocaGCPtrs.contains(Ptr)) { + AllocaGCPtrs.remove(Ptr); + } + } + } + + for (auto V : AllocaStructs) { + initializeAlloca(cast(V)); + } + for (auto V : AllocaGCPtrs) { + initializeAlloca(cast(V)); + } +} + +/// Compute the struct-live for each instruction. +void StructLiveAnalysis::computeFieldLiveInValues( + BasicBlock::reverse_iterator Begin, BasicBlock::reverse_iterator End, + SetVector &LiveTmp) { + for (auto &I : make_range(Begin, End)) { + SetVector MemDefSet; + SetVector MemUseSet; + if (hasMemoryDefineOrUseValue(&I, MemDefSet, MemUseSet) != MemoryNone) { + LiveTmp.set_subtract(MemDefSet); + LiveTmp.set_union(MemUseSet); + } + } +} + +void StructLiveAnalysis::computeFieldLiveOutSeed( + BasicBlock *BB, SetVector &LiveOuts) { + for (BasicBlock *Succ : successors(BB)) { + for (auto &I : *Succ) { + PHINode *PN = dyn_cast(&I); + if (!PN) + continue; + + for (unsigned i = 0; i < PN->getNumIncomingValues(); ++i) { + Value *V = PN->getIncomingValue(i); + if (!isStackContainGCPtr(V)) + continue; + + // Record incoming value if it is the phi value of the current BB. + if (PN->getIncomingBlock(i) == BB) { + addGCFieldsByMemory(V, LiveOuts); + } else { + // Otherwise, record all base values for the phi values of other BBs. + SetVector BaseSet; + findAllocaInsts(V, BaseSet); + for (auto Base : BaseSet) { + addGCFields(Base, LiveOuts); + } + } + } + } + } +} + +void StructLiveAnalysis::computeFieldKillSet(BasicBlock *BB, + SetVector &KillSet) { + for (Instruction &I : *BB) { + SetVector MemDefSet; + SetVector MemUseSet; + + MemoryAccess Status = hasMemoryDefineOrUseValue(&I, MemDefSet, MemUseSet); + if (Status & DefineMemory) { + KillSet.set_union(MemDefSet); + } + } +} + +/// Compute the live-in set for every basic block in the function +void StructLiveAnalysis::computeLiveness() { + InstLiveSetCache.clear(); + SmallSetVector Worklist; + + // Seed the liveness for each individual block + for (BasicBlock &BB : F) { + Data.KillSet[&BB].clear(); + computeFieldKillSet(&BB, Data.KillSet[&BB]); + Data.LiveSet[&BB].clear(); + computeFieldLiveInValues(BB.rbegin(), BB.rend(), Data.LiveSet[&BB]); + Data.LiveOut[&BB] = SetVector(); + computeFieldLiveOutSeed(&BB, Data.LiveOut[&BB]); + Data.LiveIn[&BB] = Data.LiveOut[&BB]; + Data.LiveIn[&BB].set_subtract(Data.KillSet[&BB]); + Data.LiveIn[&BB].set_union(Data.LiveSet[&BB]); + if (!Data.LiveIn[&BB].empty()) + Worklist.insert(pred_begin(&BB), pred_end(&BB)); + } + + // Propagate that liveness until stable + while (!Worklist.empty()) { + BasicBlock *BB = Worklist.pop_back_val(); + + // Compute our new liveout set, then exit early if it hasn't changed + // despite the contribution of our successor. + SetVector LiveOut = Data.LiveOut[BB]; + const auto OldLiveOutSize = LiveOut.size(); + for (BasicBlock *Succ : successors(BB)) { + assert(Data.LiveIn.count(Succ)); + LiveOut.set_union(Data.LiveIn[Succ]); + } + // assert OutLiveOut is a subset of LiveOut + if (OldLiveOutSize == LiveOut.size()) { + // If the sets are the same size, then we didn't actually add anything + // when unioning our successors LiveIn. Thus, the LiveIn of this block + // hasn't changed. + continue; + } + Data.LiveOut[BB] = LiveOut; + + // Apply the effects of this basic block + SetVector LiveTmp = LiveOut; + LiveTmp.set_subtract(Data.KillSet[BB]); + LiveTmp.set_union(Data.LiveSet[BB]); + + assert(Data.LiveIn.count(BB)); + const SetVector &OldLiveIn = Data.LiveIn[BB]; + // assert: OldLiveIn is a subset of LiveTmp + if (OldLiveIn.size() != LiveTmp.size()) { + Data.LiveIn[BB] = LiveTmp; + Worklist.insert(pred_begin(BB), pred_end(BB)); + } + } + +#ifndef NDEBUG + // Verify our output against SSA properties. This helps catch any + // missing kills during the above iteration. + checkBasicSSA(Data); +#endif +} + +/// Given results from the dataflow liveness computation, find the set of live +/// Values at a particular instruction. +void StructLiveAnalysis::findLiveSetAtInst(Instruction *Inst, + StructLiveData &Data, + StructLiveSetTy &Out) { + BasicBlock *BB = Inst->getParent(); + + // Note: The copy is intentional and required + assert(Data.LiveOut.count(BB)); + StructLiveSetTy LiveOut = InstLiveSetCache.count(BB) + ? InstLiveSetCache[BB].second + : Data.LiveOut[BB]; + auto BI = InstLiveSetCache.count(BB) + ? InstLiveSetCache[BB].first->getIterator().getReverse() + : BB->rbegin(); + // We want to handle the statepoint itself oddly. It's + // call result is not live (normal), nor are it's arguments + // (unless they're used again later). This adjustment is + // specifically what we need to relocate + computeFieldLiveInValues(BI, ++Inst->getIterator().getReverse(), LiveOut); + Out.insert(LiveOut.begin(), LiveOut.end()); + InstLiveSetCache[BB] = {Inst, Out}; +} + +void StructLiveAnalysis::analyzeParsePointLiveness( + CallBase *Call, SetVector &LiveSet) { + findLiveSetAtInst(Call, Data, LiveSet); + +#ifndef NDEBUG + if (PrintLiveSet) { + LLVM_DEBUG(dbgs() << "Safepoint For: " + << Call->getCalledOperand()->getName() << "\n"); + LLVM_DEBUG(dbgs() << " Number struct-live values: " << LiveSet.size() + << "\n"); + LLVM_DEBUG(dbgs() << " StructLive Variables:\n"); + for (FieldInfo *FI : LiveSet) + LLVM_DEBUG(dbgs() << " " << FI->V->getName() << " " << FI->Offset + << "\n"); + } +#endif +} + +void StructLiveAnalysis::legalizeMemoryValueSet(StructLiveSetTy &LiveSet) { + StructLiveSetTy LegalizedSet; + for (FieldInfo *FI : LiveSet) { + if (isa(FI->V) || isa(FI->V)) + LegalizedSet.insert(FI); + } + + for (FieldInfo *FI : LegalizedSet) { + // Caches all the base pointers of phi and select. + SetVector AllocaSet; + findAllocaInsts(FI->V, AllocaSet); + LiveSet.remove(FI); + for (auto AI : AllocaSet) { + addGCFields(AI, LiveSet); + } + } + + // remove all pointers that are constants. + LiveSet.remove_if([&](FieldInfo *LiveV) { return isa(LiveV->V); }); +} + +// Identify V or the base of V is a stack pointer that contains gcptr. +bool StructLiveAnalysis::isStackContainGCPtr(Value *V, uint64_t Size) { + PointerType *PT = dyn_cast(V->getType()); + if (!PT) + return false; + + if (StackContainGCPtrMap.count(V)) + return StackContainGCPtrMap[V]; + + bool Ret = false; + if (isGCPointerType(PT)) { + // Check whether the base of V is FakeGCptr and contains refs. + if (isFakeContainGCPtrs(V)) { + Ret = true; + } + StackContainGCPtrMap[V] = Ret; + return Ret; + } + // This is actually a global value. + if (isa(findMemoryBasePointer(V))) { + Ret = false; + } else { + Value *Def = V->stripPointerCasts(); + if (isMemoryContainsGCPtrType(Def->getType())) { + Ret = true; + } else if (Def->getType()->getNonOpaquePointerElementType()->isStructTy()) { + if (containGCPtrByOffset(Def, Size)) { + Ret = true; + } else { + Ret = false; + } + } else if (isBaseContainGCPtr(Def)) { + Ret = true; + } + } + + StackContainGCPtrMap[V] = Ret; + return Ret; +} + +// V must be an alloca instruction. The gcptr in alloca is calculated and +// added to the set. +void StructLiveAnalysis::addGCFields(Value *V, SetVector &Set, + uint64_t Begin, uint64_t End) { + AllocaInst *AI = dyn_cast(V); + if (!AI) + report_fatal_error("Only the alloca inst is supported in addGCFields!"); + + if (AllocaData.LargeStructs.contains(V)) { + Set.insert(getFieldInfo(V, -1)); + return; + } + + Type *Ty = AI->getAllocatedType(); + if (isGCPointerType(Ty)) { + Set.insert(getFieldInfo(V, 0)); + return; + } + assert(AllocaData.StructLayoutGCPtrMap.count(V) && + "StructLayoutGCPtrMap lacks V information."); + + // Others, record gc fields in struct based on StructLayoutGCPtrMap. + bool AllGCField = End == 0; + MapVector &OffsetMap = AllocaData.StructLayoutGCPtrMap[V]; + for (auto &Pair : OffsetMap) { + // Add a range of ref fields to Set. + if ((Pair.first >= Begin && Pair.first < End) || AllGCField) { + if (Pair.second) { + Set.insert(getFieldInfo(V, Pair.first)); + } + } + } +} + +void StructLiveAnalysis::addGCFieldsByBase(Value *V, + SetVector &Set) { + assert(isa(V->getType()) && "Base is not pointer type!"); + + Value *Base = findMemoryBasePointer(V); + if (isa(Base) || isa(Base)) { + Set.insert(getFieldInfoByValue(Base)); + return; + } + if (isa(Base)) { + addGCFields(Base, Set); + return; + } + + Check(isa(Base) || isa(Base) || isa(Base), + "Unsupport stack base", cast(Base)); +} + +void StructLiveAnalysis::addGCFieldsByValue(Value *V, + SetVector &Set) { + assert(isa(V->getType()) && "Base is not pointer type!"); + FieldInfo *Field = getFieldInfoByValue(V); + Value *Base = Field->V; + + if (auto *AI = dyn_cast(Base)) { + if (AI->getAllocatedType()->isStructTy()) { + assert(AllocaData.StructLayoutGCPtrMap.count(Base) && + "StructLayoutGCPtrMap lacks AllocaInst information."); + if (Field->Offset == -1 || + AllocaData.StructLayoutGCPtrMap[Base][Field->Offset]) { + Set.insert(Field); + } + } else if (AI->getAllocatedType()->isPointerTy()) { + Set.insert(Field); + } else { + assert(false && "Unsupported AllocaInst type"); + } + return; + } + + if (isa(Base) || isa(Base)) { + Set.insert(Field); + return; + } + + // The index of GEP is a variable. The offset cannot be calculated here. + if (isa(Base)) { + addGCFieldsByBase(Base, Set); + return; + } + + Check(isa(Base) || isa(Base), "Unsupport stack value", + cast(Base)); +} + +// Records a range of fields during memory instruction operations, if the size +// is 0, record all the gc fields in base. +void StructLiveAnalysis::addGCFieldsByMemory(Value *V, + SetVector &AllocaSet, + uint64_t Size) { + if (!isa(V->getType())) + return; + + Value *Ptr = V->stripPointerCasts(); + if (!isStackContainGCPtr(Ptr, Size)) + return; + + if (Size > 0) { + APInt Offsets(DL.getIndexSizeInBits(0), 0); + Value *PtrBase = V->stripAndAccumulateConstantOffsets(DL, Offsets, false); + // If it is a alloca instruction, record the range of gc fields in the base. + if (isa(PtrBase)) { + uint64_t Begin = Offsets.getZExtValue(); + uint64_t End = Begin + Size; + addGCFields(PtrBase, AllocaSet, Begin, End); + return; + } + } + + // Otherwise, record the all gc fields of the base. + addGCFieldsByBase(Ptr, AllocaSet); +} + +void StructLiveAnalysis::visitMemoryLoad(LoadInst *LI, + SetVector &AllocaUses) { + Value *Ptr = LI->getPointerOperand()->stripPointerCasts(); + Type *ValType = LI->getType(); + + if (!ValType->isPointerTy() || !isStackContainGCPtr(Ptr)) + return; + + Value *Base = findMemoryBasePointer(Ptr); + if (auto *BaseLI = dyn_cast(Base)) { + assert(isa(findMemoryBasePointer(BaseLI->getPointerOperand())) && + "If load inst is nested, its source must be a global variable!"); + return; + } + + addGCFieldsByValue(Ptr, AllocaUses); +} + +void StructLiveAnalysis::visitMemoryStore(StoreInst *SI, + SetVector &AllocaDefs) { + Value *Ptr = SI->getPointerOperand(); + if (!isStackContainGCPtr(Ptr)) { + return; + } + + Type *ValType = SI->getValueOperand()->getType(); + if (ValType->isPointerTy() || ValType->isIntegerTy(64)) { // 64bit + FieldInfo *Field = getFieldInfoByValue(Ptr); + Value *Base = Field->V; + if (isa(Base) || isa(Base)) + AllocaDefs.insert(Field); + } else { + Value *BasePtr = findMemoryBasePointer(Ptr); + // Only if the PointerType and ValueType of Store Inst are equal, + // we consider this store inst to be a define point. + // And do nothing for partial store. + if (ValType == cast(BasePtr->getType())->getElementType()) + addGCFieldsByMemory(BasePtr, AllocaDefs); + } +} + +void StructLiveAnalysis::visitMemoryIntrinsic( + IntrinsicInst *II, SetVector &AllocaDefs, + SetVector &AllocaUses) { + switch (II->getIntrinsicID()) { + default: + // fall through to general call handling + break; + // cj.gcwrite.static.struct(AS(0) *dstPtr, AS(0) *srcPtr, size) + case Intrinsic::cj_gcwrite_static_struct: { + uint64_t MemSize = 0; + if (auto Size = dyn_cast(getSize(II))) { + MemSize = Size->getZExtValue(); + } + addGCFieldsByMemory(getSource(II), AllocaUses, MemSize); + break; + } + // cj.gcwrite.struct(AS(1) *basePtr, AS(1) *dstPtr, anyAS *srcPtr, size) + case Intrinsic::cj_gcwrite_struct: { + uint64_t MemSize = 0; + if (auto Size = dyn_cast(getSize(II))) { + MemSize = Size->getZExtValue(); + } + addGCFieldsByMemory(getSource(II), AllocaUses, MemSize); + break; + } + // cj.gcread.static.struct(AS(0) *dstPtr, AS(0) *srcPtr, size) + case Intrinsic::cj_gcread_static_struct: { + uint64_t MemSize = 0; + if (auto Size = dyn_cast(getSize(II))) { + MemSize = Size->getZExtValue(); + } + addGCFieldsByMemory(getDest(II), AllocaDefs, MemSize); + break; + } + // cj.gcread.struct(AS(0) *dstPtr, AS(1) *basePtr, AS(1) *srcPtr, size) + case Intrinsic::cj_gcread_struct: { + uint64_t MemSize = 0; + if (auto Size = dyn_cast(getSize(II))) { + MemSize = Size->getZExtValue(); + } + addGCFieldsByMemory(getDest(II), AllocaDefs, MemSize); + addGCFieldsByMemory(getSource(II), AllocaUses, MemSize); + break; + } + case Intrinsic::memcpy: + case Intrinsic::memmove: { + uint64_t MemSize = 0; + if (auto Size = dyn_cast(II->getArgOperand(2))) { + MemSize = Size->getZExtValue(); + } + addGCFieldsByMemory(II->getArgOperand(0), AllocaDefs, MemSize); + addGCFieldsByMemory(II->getArgOperand(1), AllocaUses, MemSize); + break; + } + case Intrinsic::memset: { + uint64_t MemSize = 0; + if (auto Size = dyn_cast(II->getArgOperand(2))) { + MemSize = Size->getZExtValue(); + } + addGCFieldsByMemory(II->getArgOperand(0), AllocaDefs, MemSize); + break; + } + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + case Intrinsic::cj_array_copy_generic: { + uint64_t MemSize = 0; + if (auto Size = dyn_cast(getSize(II))) { + MemSize = Size->getZExtValue(); + } + addGCFieldsByMemory(getDest(II), AllocaDefs, MemSize); + addGCFieldsByMemory(getSource(II), AllocaUses, MemSize); + break; + } + case Intrinsic::lifetime_start: + // lifetime_start means the begin of using a stack ptr. + // we treat this ptr as a kill + addGCFieldsByMemory(II->getArgOperand(1), AllocaDefs); + break; + case Intrinsic::lifetime_end: + // lifetime_start means the end of using a stack ptr. + // we do nothing here + break; + } +} + +void StructLiveAnalysis::visitMemoryCallBase( + CallBase *CB, SetVector &AllocaDefs, + SetVector &AllocaUses) { + if (auto *II = dyn_cast(CB)) { + visitMemoryIntrinsic(II, AllocaDefs, AllocaUses); + return; + } + + // CallInst and InvokeInst + // In the call instruction, the parameter are considered as use point. + unsigned ArgNum = CB->arg_size(); + if (ArgNum == 0) { + return; + } + + for (unsigned ArgIdx = 0; ArgIdx < ArgNum; ++ArgIdx) { + Function *Callee = CB->getCalledFunction(); + // If the SSA value is not used in the callee, no need to record it. + if (Callee && + (Callee->hasParamAttribute(ArgIdx, Attribute::ReadNone) || + Callee->hasParamAttribute(ArgIdx, Attribute::WriteOnly)) && + Callee->hasParamAttribute(ArgIdx, Attribute::NoCapture)) + continue; + + unsigned Size = 0; + Value *Arg = CB->getArgOperand(ArgIdx); + Type *Ty = Arg->getType(); + if (Ty->isPointerTy() && !Ty->isOpaquePointerTy()) { + auto *Pointee = Ty->getNonOpaquePointerElementType(); + if (isa(Pointee) || isa(Pointee)) + Size = + static_cast(DL.getTypeAllocSize(Pointee).getFixedSize()); + } + + addGCFieldsByMemory(Arg, AllocaUses, Size); + } +} + +void StructLiveAnalysis::visitMemorySelect(SelectInst *SI, + SetVector &AllocaDefs, + SetVector &AllocaUses) { + if (!SI->getType()->isPointerTy() || !isStackContainGCPtr(SI)) + return; + + AllocaDefs.insert(getFieldInfoByValue(SI)); + if (isStackContainGCPtr(SI->getTrueValue())) { + addGCFieldsByValue(SI->getTrueValue(), AllocaUses); + } + if (isStackContainGCPtr(SI->getFalseValue())) { + addGCFieldsByValue(SI->getFalseValue(), AllocaUses); + } +} + +// If it is an instruction that contains the memory parameter, then handles +// use and define values according to the instruction type. +MemoryAccess StructLiveAnalysis::hasMemoryDefineOrUseValue( + Instruction *I, SetVector &AllocaDefs, + SetVector &AllocaUses) { + // For each memory instruction, identifies and processes use and define, + // otherwise do nothing. + switch (I->getOpcode()) { + default: + break; + case Instruction::Load: + visitMemoryLoad(cast(I), AllocaUses); + break; + case Instruction::Store: + visitMemoryStore(cast(I), AllocaDefs); + break; + case Instruction::Call: + case Instruction::Invoke: + visitMemoryCallBase(cast(I), AllocaDefs, AllocaUses); + break; + case Instruction::Select: + visitMemorySelect(cast(I), AllocaDefs, AllocaUses); + break; + case Instruction::PHI: + AllocaDefs.insert(getFieldInfoByValue(I)); + break; + } + + int Status = 0; + if (!AllocaDefs.empty()) + Status |= static_cast(DefineMemory); + if (!AllocaUses.empty()) + Status |= static_cast(UseMemory); + return static_cast(Status); +} + +void StructLiveAnalysis::combineStructGCField(StructLiveSetTy &FieldInfos, + SetVector &Structs, + CallBase *Call) { + StructLayoutGCPtrTy &StructDL = AllocaData.StructLayoutGCPtrMap; + MapVector RecordGCFieldNum; + for (FieldInfo *FI : FieldInfos) { + Value *Base = FI->V; + int Offset = FI->Offset; + // If the offset is -1, record alloca SSA. + if (Offset == -1) { + Structs.insert(Base); + continue; + } + assert(Offset >= 0 && "FieldInfos offset error."); + if (StructDL.count(Base) && StructDL[Base][Offset]) { + if (++RecordGCFieldNum[Base] == AllocaData.StructGCFieldNum[Base]) { + Structs.insert(Base); + } + } + } +} + +void StructLiveAnalysis::insertGEPForField(CallBase *Call, + StructLiveSetTy &FieldInfos, + LiveSetTy &FieldSet) { + if (FieldInfos.empty()) + return; + + SetVector Structs; + // If all fields of a struct are alive, record it instead of fields. + combineStructGCField(FieldInfos, Structs, Call); + + IRBuilder<> Builder(Call); + for (FieldInfo *FI : FieldInfos) { + Value *Base = FI->V; + int Offset = FI->Offset; + assert(isMemoryContainsGCPtrType(Base->getType()) && + "Unexpected values are recorded in struct-live."); + auto *AI = dyn_cast(Base); + if (!AI) { + continue; + } + + if (AI->getAllocatedType()->isPointerTy()) { + FieldSet.insert(AI); + } else if (AI->getAllocatedType()->isStructTy()) { + assert(AllocaData.StructLayoutGCPtrMap.count(AI) && + "StructLayoutGCPtrMap lacks V information."); + // Directly record struct instead of recording its field, which avoids + // oprands expansion. + if (Structs.contains(Base)) { + FieldSet.insert(AI); + continue; + } + + assert(Offset >= 0 && "FieldInfos offset error."); + assert(AllocaData.StructLayoutGCPtrMap[AI][Offset] && + "FieldInfos contains incorrect value."); + + if (FieldToGEPMap.count(FI)) { + Value *GEP = FieldToGEPMap[FI]; + if (DT.dominates(GEP, Call)) { + FieldSet.insert(GEP); + continue; + } + } + + Value *Int8Ptr = Builder.CreateBitCast(Base, Builder.getInt8PtrTy()); + cast(Int8Ptr)->setMetadata( + "noreloc", MDNode::get(Builder.getContext(), {})); + Value *GEP = Builder.CreateGEP(Builder.getInt8Ty(), Int8Ptr, + Builder.getInt64(Offset), "field", true); + FieldToGEPMap[FI] = GEP; + FieldSet.insert(GEP); + } else { + checkFailed("Unexpected type in insertGEPForField", AI); + } + } +} + +void LivenessData::findLiveReferences( + MutableArrayRef Records) { + for (size_t i = 0; i < Records.size(); i++) { + // The stack check function does not need to compute the GC pointer. + Function *Callee = ToUpdate[i]->getCalledFunction(); + if (Callee && Callee->isCangjieStackCheck()) { + Records[i].isCJStackCheck = true; + break; + } + } + + computeGCFieldLiveness(Records); + computeGCPtrLiveness(Records); +} + +void LivenessData::recomputeLiveInValues(CallBase *Call, SafepointRecord &Info, + PointerToBaseTy &PointerToBase) { + LiveSetTy Updated; + GCLA->findLiveSetAtInst(Call, Updated); + + // We may have base pointers which are now live that weren't before. We + // need to update the PointerToBase structure to reflect this. + for (auto V : Updated) + PointerToBase.insert({V, V}); + + Info.LiveSet = Updated; + + for (auto V : Updated) { + // Records them whose base is alloca contained gcptr. + SetVector AllocaSet; + findAllocaInsts(V, AllocaSet); + if (!AllocaSet.empty()) { + Info.FieldSet.insert(AllocaSet.begin(), AllocaSet.end()); + } + } +} + +/// Given an updated version of the dataflow liveness results, update the +/// liveset and base pointer maps for the call site CS. +void LivenessData::recomputeLiveInValues( + MutableArrayRef Records, PointerToBaseTy &PointerToBase) { + GCLA->computeLiveness(); + for (size_t i = 0; i < Records.size(); i++) { + // The stack check function does not need to compute the GC pointer. + if (Records[i].isCJStackCheck) + continue; + + SafepointRecord &info = Records[i]; + recomputeLiveInValues(ToUpdate[i], info, PointerToBase); + } + deleteGCLA(); +} + +void LivenessData::computeGCFieldLiveness( + MutableArrayRef Records) { + StructLiveAnalysis StructLA(F, DT, AllocaData); + + // In the case of phi struct, we need to record all structs. To ensure + // that the struct is initialized, we insert memset. It is optimized + // after Livenesss analysis. Only necessary memsets are retained. + StructLA.insertMemsetForPhiStruct(); + + StructLA.computeLiveness(); + + for (size_t i = 0; i < Records.size(); i++) { + // The stack check function does not need to compute the GC pointer. + if (Records[i].isCJStackCheck) + continue; + + /// Records the [base+offset] format of the live gc fields. + StructLiveSetTy FieldLives; + StructLA.analyzeParsePointLiveness(ToUpdate[i], FieldLives); + + // If phi and select exist, delete them and record their alloca SSAs. + StructLA.legalizeMemoryValueSet(FieldLives); + + // Generate the SSA value of `gep a + x` for each struct field. + StructLA.insertGEPForField(ToUpdate[i], FieldLives, Records[i].FieldSet); + } + + // Eliminate dead cjmemset. Only works at O2, because cjdb needs it at O0. + if (OptLevel == 2) { + eliminateDeadMemset(StructLA.Data, F, PostDT, AllocaData, + StructLA.InsertedMemset); + } +} + +void LivenessData::computeGCPtrLiveness( + MutableArrayRef Records) { + GCLA->computeLiveness(); + + for (size_t i = 0; i < Records.size(); i++) { + // The stack check function does not need to compute the GC pointer. + if (Records[i].isCJStackCheck) + continue; + + GCLA->analyzeParsePointLiveness(ToUpdate[i], Records[i].LiveSet); + } +} diff --git a/llvm/lib/Transforms/Scalar/CJGenericIntrinsicOpt.cpp b/llvm/lib/Transforms/Scalar/CJGenericIntrinsicOpt.cpp new file mode 100644 index 000000000..2a47341d0 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/CJGenericIntrinsicOpt.cpp @@ -0,0 +1,814 @@ +//===- CJInstanceOfOpt.cpp --------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This pass mainly implements the generic intrinsics optimization of cangjie. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/CJGenericIntrinsicOpt.h" + +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/CFG.h" +#include "llvm/Analysis/MemoryLocation.h" +#include "llvm/Analysis/MemorySSAUpdater.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/CJIntrinsics.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/SafepointIRVerifier.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Scalar/CJFillMetadata.h" +#include "llvm/Transforms/Scalar/CJRSSCE.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/SSAUpdater.h" + +#define DEBUG_TYPE "cj-generic-intrinsic-opt" + +using namespace llvm; +using namespace cangjie; + +static cl::opt + EnableCopyOpt("enable-generic-copy-opt", cl::init(true), cl::Hidden, + cl::desc("Enable generic copy optimization")); + +static void updateMSSA(MemorySSAUpdater &MSSAU, Instruction *Old, + Instruction *New) { + auto *MA = cast(MSSAU.getMemorySSA()->getMemoryAccess(Old)); + auto *NewMA = + MSSAU.createMemoryAccessBefore(New, MA->getDefiningAccess(), MA); + MA->replaceAllUsesWith(NewMA); + MSSAU.removeMemoryAccess(Old); +} + +static bool isStackToStackCopy(Value *Src, Value *Dst) { + Type *DstBaseTy = findMemoryBasePointer(Dst)->getType(); + Type *SrcBaseTy = findMemoryBasePointer(Src)->getType(); + if (!isGCPointerType(SrcBaseTy) && !isGCPointerType(DstBaseTy)) + return true; + + return false; +} + +static void lowerToMemcpy(CallInst *CI, Value *SizeOp, + MemorySSAUpdater &MSSAU) { + IRBuilder<> IRB(CI); + SmallVector Idxs; + Instruction *NewInst = CI; + Type *I8Ty = IRB.getInt8Ty(); + Type *I32Ty = IRB.getInt32Ty(); + + switch (CI->getIntrinsicID()) { + default: + report_fatal_error("Unsupported process type!"); + break; + case Intrinsic::cj_assign_generic: { + Idxs.push_back(ConstantInt::get(I32Ty, 8)); // 8: typeinfo* size + Value *DstPtr = IRB.CreateGEP(I8Ty, CI->getArgOperand(0), {Idxs}); + Value *SrcPtr = IRB.CreateGEP(I8Ty, CI->getArgOperand(1), {Idxs}); + NewInst = IRB.CreateMemCpy(DstPtr, Align(8), SrcPtr, Align(8), SizeOp); + break; + } + case Intrinsic::cj_gcread_generic: { + Idxs.push_back(ConstantInt::get(I32Ty, 8)); // 8: typeinfo* size + Value *DstPtr = IRB.CreateGEP(I8Ty, CI->getArgOperand(0), {Idxs}); + NewInst = IRB.CreateMemCpy(DstPtr, Align(8), CI->getArgOperand(2), Align(8), + SizeOp); + break; + } + case Intrinsic::cj_gcwrite_generic: { + Idxs.push_back(ConstantInt::get(I32Ty, 8)); // 8: typeinfo* size + Value *SrcPtr = IRB.CreateGEP(I8Ty, CI->getArgOperand(2), {Idxs}); + NewInst = IRB.CreateMemCpy(CI->getArgOperand(1), Align(8), SrcPtr, Align(8), + SizeOp); + break; + } + } + if (MDNode *MD = CI->getMetadata(LLVMContext::MD_tbaa)) { + NewInst->setMetadata(LLVMContext::MD_tbaa, MD); + } + NewInst->setDebugLoc(CI->getDebugLoc()); + updateMSSA(MSSAU, CI, NewInst); + CI->replaceAllUsesWith(NewInst); + CI->eraseFromParent(); +} + +static bool lowerToMemcpy(CallInst *CI, uint64_t Size, + MemorySSAUpdater &MSSAU) { + if (Size == 0) + return false; + auto *SizeOp = ConstantInt::get(Type::getInt64Ty(CI->getContext()), Size); + lowerToMemcpy(CI, SizeOp, MSSAU); + return true; +} + +// void @llvm.cj.copy.generic (i8 AS(1)* dst, i8 AS(1)* src, i8* typeinfo) +// void @llvm.cj.gcread.generic (i8 AS(1)* dstPtr, i8 AS(1)* basePtr, i8 AS(1)* +// fieldPtr, i32 size) +// void @llvm.cj.gcwrite.generic (i8 AS(1)* basePtr, i8 AS(1)* fieldPtr, i8 +// AS(1)* srcPtr, i32 size) +// If the copied memory do not contain ref, we can safely replace with memcpy. +static bool lowerGenericBarrier(CallInst *CI, GlobalVariable *GV, + MemorySSAUpdater &MSSAU) { + if (GV->hasInitializer()) { + TypeInfo Klass(GV); + // Reference type does not call a GenericBarrier in actual. + if (Klass.isReferenceType()) { + return false; + } + // If it contains ref field, need use barrier intrinsic. + if (Klass.hasRefField()) + return false; + + return lowerToMemcpy(CI, Klass.getSize(), MSSAU); + } + + if (isPrimitiveTypeInfo(GV)) + return lowerToMemcpy(CI, getValueTypeSize(GV), MSSAU); + + StructType *ST = getTypeLayoutType(GV); + assert(ST && "typeinfo layout info missing!"); + if (containsGCPtrType(ST)) { + return false; + } + + const DataLayout &DL = CI->getModule()->getDataLayout(); + uint64_t MemSize = DL.getStructLayout(ST)->getSizeInBytes(); + return lowerToMemcpy(CI, MemSize, MSSAU); +} + +// llvm.cj.gcread.generic (i8 AS(1)* dstPtr, i8 AS(1)* basePtr, +// i8 AS(1)* fieldPtr, i32 size) +// llvm.cj.gcwrite.generic (i8 AS(1)* basePtr, i8 AS(1)* fieldPtr, +// i8 AS(1)* srcPtr, i32 size) +static bool processGenericBarrier(CallInst *CI, Value *DstOp, Value *SrcOp, + Value *GenericVar, MemorySSAUpdater &MSSAU) { + if (isStackToStackCopy(SrcOp, DstOp)) { + lowerToMemcpy(CI, getSize(CI), MSSAU); + return true; + } + RefValue Ref(GenericVar); + if (GlobalVariable *TI = Ref.findTypeInfoGV()) { + return lowerGenericBarrier(CI, TI, MSSAU); + } + return false; +} + +// void @llvm.cj.assign.generic (i8 AS(1)* dst, i8 AS(1)* src, i8* typeinfo) +static bool processAssignGeneric(CallInst *CI, MemorySSAUpdater &MSSAU) { + Value *TIOp = CI->getArgOperand(2); // 2: TypeInfo operand + if (isStackToStackCopy(getSource(CI), getDest(CI))) { + if (GlobalVariable *TI = findTypeInfoGV(TIOp)) { + uint64_t Size = 0; + if (isPrimitiveTypeInfo(TI)) { + Size = getValueTypeSize(TI); + } else { + StructType *ST = getTypeLayoutType(TI); + assert(ST && "typeinfo layout info missing!"); + const DataLayout &DL = CI->getModule()->getDataLayout(); + Size = DL.getStructLayout(ST)->getSizeInBytes(); + } + return lowerToMemcpy(CI, Size, MSSAU); + } else { + auto *TIType = StructType::getTypeByName(CI->getContext(), "TypeInfo"); + IRBuilder<> IRB(CI); + Value *TIPtr = IRB.CreateBitCast(TIOp, TIType->getPointerTo()); + SmallVector Idxs; + Idxs.push_back(IRB.getInt32(0)); + Idxs.push_back(IRB.getInt32(CIT_SIZE)); + Value *SizePtr = IRB.CreateGEP(TIType, TIPtr, Idxs); + Value *Size = IRB.CreateLoad(IRB.getInt32Ty(), SizePtr); + lowerToMemcpy(CI, Size, MSSAU); + } + return true; + } + if (auto *TI = findTypeInfoGV(TIOp)) + return lowerGenericBarrier(CI, TI, MSSAU); + return false; +} + +static bool eliminateReferenceChecking(Function &F, MemorySSAUpdater &MSSAU) { + SmallVector LoadTIs; + SmallVector RemoveInsts; + for (Instruction &I : instructions(F)) { + if (LoadInst *LI = dyn_cast(&I)) { + if (LI->getMetadata("ti_load")) + LoadTIs.push_back(LI); + continue; + } + } + for (LoadInst *LI : LoadTIs) { + auto *Expr = dyn_cast(LI->getPointerOperand()); + if (!Expr) + continue; + + GlobalVariable *GV = dyn_cast(Expr->getOperand(0)); + if (!GV || !GV->isCJTypeInfo()) + continue; + + Value *Res = TypeInfo::getReferenceType(GV); + if (!Res) + continue; + + for (User *U : LI->users()) { + ICmpInst *ICMP = dyn_cast(U); + if (!ICMP) { + report_fatal_error("Load TypeInfo is used for icmp slt 0!"); + } + // %x = icmp slt i8 %kind, 0 + if (ICMP->getSignedPredicate() != CmpInst::ICMP_SLT) { + report_fatal_error("Load TypeInfo is used for icmp slt 0!"); + } + ConstantInt *Var = dyn_cast(ICMP->getOperand(1)); + if (!Var->isNullValue()) { + report_fatal_error("Load TypeInfo is used for icmp slt 0!"); + } + ICMP->replaceAllUsesWith(Res); + RemoveInsts.push_back(ICMP); + } + RemoveInsts.push_back(LI); + } + if (RemoveInsts.empty()) + return false; + + for (Instruction *I : RemoveInsts) { + MSSAU.removeMemoryAccess(I); + I->eraseFromParent(); + } + return true; +} + +static bool simplifyGenericInst(Function &F, MemorySSAUpdater &MSSAU) { + bool Changed = false; + if (!F.hasCangjieGC()) + return Changed; + + // Deleted the invalid reference type checking and unreachable branch + // so that barrier optimization. + Changed |= eliminateReferenceChecking(F, MSSAU); + + for (auto I = inst_begin(F); I != inst_end(F);) { + auto *CI = dyn_cast(&*I++); + if (!CI) + continue; + + unsigned ID = CI->getIntrinsicID(); + switch (ID) { + default: + break; + case Intrinsic::cj_assign_generic: { + Changed |= processAssignGeneric(CI, MSSAU); + break; + } + case Intrinsic::cj_gcread_generic: { + Changed |= processGenericBarrier(CI, getDest(CI), getSource(CI), + getDest(CI), MSSAU); + break; + } + case Intrinsic::cj_gcwrite_generic: { + Changed |= processGenericBarrier(CI, getDest(CI), getSource(CI), + getSource(CI), MSSAU); + break; + } + } + } + return Changed; +} + +struct GenericCopyOpt { + MemorySSA &MSSA; + MemorySSAUpdater &MSSAU; + DominatorTree &DT; + AliasAnalysis &AA; + const DataLayout &DL; + + DenseSet Analyzed; + + Function *F; + + GenericCopyOpt(Function &F, MemorySSA &MSSA, MemorySSAUpdater &MSSAU, + DominatorTree &DT, AliasAnalysis &AA) + : MSSA(MSSA), MSSAU(MSSAU), DT(DT), AA(AA), + DL(F.getParent()->getDataLayout()), F(&F) {} + + bool canEnableDiffTIOpt(Value *TI0, Value *TI1) { + auto *CI0 = dyn_cast_or_null(TI0); + auto *CI1 = dyn_cast(TI1); + return CI0 && CI1 && CI0 != CI1 && CI0->getCalledFunction() && + CI1->getCalledFunction() && + CI0->getCalledFunction() == CI1->getCalledFunction() && + CI0->getCalledFunction()->getName() == "CJ_MCC_GetOrCreateTypeInfo"; + } + + bool sameTIOpt(Value *TI, Value *TI1) { + if (canEnableDiffTIOpt(TI, TI1)) { + auto *CI0 = cast(TI->stripPointerCasts()); + auto *CI1 = cast(TI1->stripPointerCasts()); + CallInst *DeadCI = nullptr; + if (DT.dominates(CI0, CI1)) { + CI1->replaceAllUsesWith(CI0); + DeadCI = CI1; + } else if (DT.dominates(CI1, CI0)) { + CI0->replaceAllUsesWith(CI1); + DeadCI = CI0; + } + if (DeadCI) { + TI = TI->stripPointerCasts() == DeadCI ? TI1 : TI; + MSSAU.removeMemoryAccess(DeadCI); + DeadCI->eraseFromParent(); + return true; + } + } + return false; + } + + std::pair + getPHINodeTranslate(MemoryPhi *Phi, unsigned Index, Value *V) { + auto *BB = Phi->getIncomingBlock(Index); + if (auto *PN = dyn_cast(V)) { + for (unsigned I = 0, E = PN->getNumIncomingValues(); I != E; ++I) { + if (PN->getIncomingBlock(I) == BB) + return {BB, PN->getIncomingValue(I)}; + } + return {nullptr, nullptr}; + } + if (isa(V) || isa(V)) + return {BB, V}; + if (isa(V) && + DT.dominates(cast(V), BB->getTerminator())) + return {BB, V}; + return {nullptr, nullptr}; + } + + // 0: base ptr, 1: derived ptr, 2: size + std::tuple + processMemoryPhi(MemoryPhi *Phi, MemoryAccess *End, MemoryAccess *InitEnd, + MemoryLocation &Loc, Value *TI, bool &Changed) { + SmallVector Bases; + SmallVector Deriveds; + SmallVector Sizes; + SmallVector BBs; + for (unsigned I = 0, E = Phi->getNumIncomingValues(); I != E; ++I) { + auto [BB, EQV] = + getPHINodeTranslate(Phi, I, const_cast(Loc.Ptr)); + if (!BB || !EQV) + return {nullptr, nullptr, nullptr}; + BasicBlock *IncomingBB = Phi->getIncomingBlock(I); + if (isPotentiallyReachable(Phi->getBlock(), IncomingBB)) + return {nullptr, nullptr, nullptr}; // Maybe this is a loop. + auto [TmpB, TmpD, TmpS] = findPotentialEqualMem( + IncomingBB->getTerminator(), + cast(Phi->getIncomingValue(I)), InitEnd, + MemoryLocation::getAfter(EQV), nullptr, TI, nullptr, Changed); + if (!TmpD) { + Bases.push_back(nullptr); + Deriveds.push_back(EQV); + Sizes.push_back(nullptr); + } else { + Bases.push_back(TmpB); + Deriveds.push_back(TmpD); + Sizes.push_back(TmpS); + } + BBs.push_back(BB); + } + // Check if all bases is nullptr, or non-null. + Value *T = Bases.front(); + for (unsigned I = 1; I < Bases.size(); ++I) + if ((T && !Bases[I]) || (!T && Bases[I])) + return {nullptr, nullptr, nullptr}; + // Get available value for base, derived and size. + SSAUpdater DerivedSSA, BaseSSA, SizeSSA; + Value *AB = nullptr, *AS = nullptr; + if (T) { + assert(Sizes.front() != nullptr); + BaseSSA.Initialize(Loc.Ptr->getType(), ""); + SizeSSA.Initialize(Sizes.front()->getType(), ""); + for (unsigned I = 0; I < Bases.size(); ++I) { + BaseSSA.AddAvailableValue(BBs[I], Bases[I]); + SizeSSA.AddAvailableValue(BBs[I], Sizes[I]); + } + AB = BaseSSA.GetValueInMiddleOfBlock(Phi->getBlock()); + AS = SizeSSA.GetValueInMiddleOfBlock(Phi->getBlock()); + } + DerivedSSA.Initialize(Loc.Ptr->getType(), ""); + for (unsigned I = 0; I < Deriveds.size(); ++I) + DerivedSSA.AddAvailableValue(BBs[I], Deriveds[I]); + Value *AV = DerivedSSA.GetValueInMiddleOfBlock(Phi->getBlock()); + return {AB, AV, AS}; + } + + // Return the Value after strip. + Value *getDerivedOffset(Value *Ptr, + SmallVector, 4> &Offsets) { + Ptr = Ptr->stripPointerCasts(); + GetElementPtrInst *GEP = nullptr; + while ((GEP = dyn_cast(Ptr))) { + if (GEP->getNumIndices() == 1) { + Offsets.push_back({GEP->getOperand(1), true}); + Ptr = GEP->getPointerOperand(); + } + break; + } + return GEP ? GEP->getPointerOperand() : nullptr; + } + + bool isConstantEqual(Value *LHS, Value *RHS) { + if (!isa(LHS) || !isa(RHS)) + return false; + return cast(LHS)->getSExtValue() == + cast(RHS)->getSExtValue(); + } + + // IsWrite: offset should be logical negation + // For example: + // gcwrite.generic(v1, v1.off, v0, s1) + // gcread.generic(v2, v1, v1.off, s1) + // When gcread.generic is analyzed, Offsets = {v1.off - v1}, then + // gcwrite.generic is analyzed, and TmpOffsets = {v1.off - v1}. However, for + // gcwrite.generic, the real TmpOffsets should be {-(v1.off - v1)}. + void + pushAndPopSameOffset(SmallVector, 4> &Offsets, + const ArrayRef> &TmpOffsets, + bool IsWrite = false) { + for_each(TmpOffsets.begin(), TmpOffsets.end(), + [&Offsets, IsWrite, this](const std::pair &VB) { + bool Tag = IsWrite ? !VB.second : VB.second; + if (!Offsets.empty() && VB.first == Offsets.back().first && + Tag != Offsets.back().second) + Offsets.pop_back(); + else if (!Offsets.empty() && + isConstantEqual(VB.first, Offsets.back().first) && + Tag != Offsets.back().second) + Offsets.pop_back(); + else + Offsets.push_back({VB.first, Tag}); + }); + } + + std::tuple + findPotentialEqualMem(Instruction *Inst, MemoryAccess *End, + MemoryAccess *InitEnd, MemoryLocation Loc, Value *Base, + Value *TI, Value *Size, bool &Changed) { + // MemoryPhi may appear in gcread.ref + if (isa(End)) + return {nullptr, nullptr, nullptr}; + // Offset of Loc.Ptr relative to Base, true: positive, false: negative + SmallVector, 4> Offsets; + auto *BV = getDerivedOffset(const_cast(Loc.Ptr), Offsets); + if (BV && BV != Base) + return {nullptr, nullptr, nullptr}; + const Value *InitPtr = Loc.Ptr; + bool LastBase = Base != nullptr; +#ifndef NDEBUG + Value *InitSize = Size; +#endif + Value *LastSize = Size; + while (true) { + auto *Clobber = MSSA.getWalker()->getClobberingMemoryAccess(End, Loc); + if (auto *Phi = dyn_cast(Clobber)) { + // Currently, when memoryphi is encountered, progressive processing of + // gcread.generic is not expected, so Base can only be nullptr. + if (Base || !TI) + break; + auto [AB, AV, AS] = + processMemoryPhi(Phi, End, InitEnd, Loc, TI, Changed); + if (AV && AV->stripPointerCasts() != Loc.Ptr->stripPointerCasts()) { + Base = AB; + Loc = MemoryLocation::getAfter(AV); + Size = AS; + if (!LastSize) + LastSize = Size; + } + break; + } + if (!isa(Clobber) || MSSA.isLiveOnEntryDef(Clobber)) + break; + + auto *Def = cast(Clobber); + if (!isa_and_nonnull(Def->getMemoryInst())) + break; + auto *II = cast(Def->getMemoryInst()); + + bool Stop = false; + switch (II->getIntrinsicID()) { + default: + Stop = true; + break; + case Intrinsic::cj_assign_generic: { + auto *Ptr = getDest(II); + const Value *V = Base ? Base : Loc.Ptr; + if (Ptr->stripPointerCasts() != V->stripPointerCasts() || + // Obviously, define source connot be changed in between. + hasMemoryDefBetween(MSSA, DT, DL, getSource(II), II->getNextNode(), + InitEnd, false, true)) + Stop = true; + else { + // If Offset is set, keep it. + Loc = MemoryLocation::getAfter(getSource(II)); + Base = nullptr; + // Unable to get ti again from Loc.Ptr, assuming ti is same, that's + // right. + if (TI && sameTIOpt(TI, II->getArgOperand(2))) // 2: TI + Changed = true; + TI = II->getArgOperand(2); + } + } break; + case Intrinsic::cj_gcread_generic: { + auto *Ptr = getDest(II); + if (!isa(getSize(II))) { + Stop = true; + break; + } + auto *SizeFrom = getUnderlyingObject( + cast(getSize(II))->getPointerOperand(), 0); + const Value *V = Base ? Base : Loc.Ptr; + bool IsPointerNotMatch = + (TI && SizeFrom != TI->stripPointerCasts()) || + Ptr->stripPointerCasts() != V->stripPointerCasts(); + if (IsPointerNotMatch || + hasMemoryDefBetween(MSSA, DT, DL, getSource(II), II->getNextNode(), + InitEnd, false, true)) + Stop = true; + else { + SmallVector, 4> TmpOffsets; + auto *BV = getDerivedOffset(getSource(II), TmpOffsets); + if (BV && BV != getBaseObj(II)) { + Stop = true; + break; + } + Offsets.append(TmpOffsets.begin(), TmpOffsets.end()); + // -8: This is special because we need to consider skipping typeinfo + // when we meet two derived ptr. For example: + // call void @cj.gcread.generic(%1, %0, %0.off) + // call void @cj.gcread.generic(%2, %1, %1.off) + // call void @cj.copy.generic(%3, %2) + // call void @cj.gcread.generic(%4, %3, %3.off) + // And we known the last offset is %off.3 - 8 + %off.1 - 8 + %off.0 + if (LastBase) + pushAndPopSameOffset( + Offsets, + {{ConstantInt::get(Type::getInt64Ty(Inst->getContext()), 8), + false}}); + LastBase = true; + Loc = MemoryLocation::getAfter(getSource(II)); + Base = getBaseObj(II); + Size = getSize(II); + // gcread.generic(%1, %0, %0.off, %size0) + // gcread.generic(%2, %1, %1.off, %size1) + // At last, we need to keep size1. + if (!LastSize) + LastSize = Size; + // TypeInfo for gcread.generic base is different from current TI, TI + // is sub of TI(base). + TI = nullptr; + } + } break; + case Intrinsic::cj_gcwrite_generic: { + auto *BP = getBaseObj(II); + auto *Derived = getDest(II); + // This is different from copy.generic and gcread.generic where we can + // always make sure that we write the full T. But with gcwrite.generic, + // we don't know when to fill T. For example: + // call void @gcread.generic(%m0, %base, %ptr, %size0) + // Then we meet gcwrite.generic and its associated define. + // store xxx -> %base.off0 + // gcwrite.generic(%base, %base.off1, %m1, %size1) + // Since the size of T is unknown, it is not possible to determine when + // T is written done. Conservatively optimization. + bool IsPointerNotMatch = + getSize(II) != Size || BP != Base || + Derived->stripPointerCasts() != Loc.Ptr->stripPointerCasts(); + if (IsPointerNotMatch || + hasMemoryDefBetween(MSSA, DT, DL, getSource(II), II->getNextNode(), + InitEnd, false, true)) + Stop = true; + else { + SmallVector, 4> TmpOffsets; + auto *BV = getDerivedOffset(getDest(II), TmpOffsets); + if (BV && BV != BP) { + Stop = true; + break; + } + pushAndPopSameOffset(Offsets, TmpOffsets, true); + Base = nullptr; + TI = nullptr; + Loc = MemoryLocation::getAfter(getSource(II)); + } + } break; + } + if (Stop || Analyzed.count(II)) + break; + End = Def->getDefiningAccess(); + } + + if (InitPtr == Loc.Ptr) + return {nullptr, nullptr, nullptr}; + + // Last, get equal value. + Value *Ptr = const_cast(Loc.Ptr); + if (Base || !Offsets.empty()) { + // If Inst is gcread.ref, then InitSize is nullptr. + assert(LastSize == InitSize || + LastSize != nullptr && "create gcread.generic need size."); + IRBuilder<> IRB(Inst); + if (Offsets.empty()) + Ptr = const_cast(Loc.Ptr); + else { + Base = Base ? Base : const_cast(Loc.Ptr); + auto *BC = + IRB.CreateBitOrPointerCast(Base, IRB.getInt8Ty()->getPointerTo(1)); + Value *Offset = Offsets.front().first; + assert(Offsets.front().second == true); + for (unsigned I = 1; I < Offsets.size(); ++I) + Offset = Offsets[I].second ? IRB.CreateAdd(Offset, Offsets[I].first) + : IRB.CreateSub(Offset, Offsets[I].first); + Ptr = IRB.CreateGEP(IRB.getInt8Ty(), BC, {Offset}); + Changed = true; + } + } + // gcread.generic(...) process by phi + // copy.generic(...) + // If we break in process phi, then Base may not be null, and because of + // Offsets is empty, Ptr is equal to Loc.Ptr. However, for normal case, + // Offsets is not empty, so Ptr is not equal to Loc.Ptr. + return {Ptr == Loc.Ptr && !Base ? nullptr : Base, Ptr, LastSize}; + } + + // 0: base pointer, 1: derived pointer, 2: typeinfo, 3: size + static std::tuple + getFromPointerInfo(CallInst *CI) { + switch (CI->getIntrinsicID()) { + default: + report_fatal_error("unreachable"); + case Intrinsic::cj_assign_generic: + return {nullptr, getSource(CI), CI->getArgOperand(2), nullptr}; + case Intrinsic::cj_gcread_generic: + return {getBaseObj(CI), getSource(CI), nullptr, getSize(CI)}; + case Intrinsic::cj_gcwrite_generic: + return {nullptr, getSource(CI), nullptr, getSize(CI)}; + case Intrinsic::cj_gcread_ref: + return {getBaseObj(CI), getPointerArg(CI), nullptr, nullptr}; + } + } + + bool processGenericCopy(CallInst *CI) { + // Base: It is valid at gcread/gcwrite. + // Size: Also valid at gcread/gcwrite. + // TI: TypeInfo. + auto IID = CI->getIntrinsicID(); + auto [Base, FPtr, TI, Size] = getFromPointerInfo(CI); + if (!FPtr) + return false; + MemoryLocation Loc = MemoryLocation::getAfter(FPtr); + bool Changed = false; + MemoryAccess *MA = IID == Intrinsic::cj_gcread_ref + ? cast(MSSA.getMemoryAccess(CI)) + ->getDefiningAccess() + : MSSA.getMemoryAccess(CI); + // 0: base ptr, 1: derived ptr, 2: size + auto [RB, RP, RS] = + findPotentialEqualMem(CI, MA, MA, Loc, Base, TI, Size, Changed); + if (!RP || RP == Loc.Ptr || (IID == Intrinsic::cj_gcwrite_generic && RB)) + return Changed; + Analyzed.insert(CI); + Function *Callee = nullptr; + SmallVector Args; + if (RB) { + if (IID == Intrinsic::cj_assign_generic || + IID == Intrinsic::cj_gcread_generic) { + assert(RS && "analysis error"); + Callee = Intrinsic::getDeclaration(CI->getModule(), + Intrinsic::cj_gcread_generic); + Args = {CI->getOperand(0), RB, RP, RS}; + } else if (IID == Intrinsic::cj_gcread_ref) { + IRBuilder<> IRB(CI); + auto *BC = + IRB.CreateBitOrPointerCast(RP, CI->getArgOperand(1)->getType()); + CI->setArgOperand(0, RB); + CI->setArgOperand(1, BC); + } + } else { + if (IID == Intrinsic::cj_assign_generic) { + Callee = Intrinsic::getDeclaration(CI->getModule(), + Intrinsic::cj_assign_generic); + Args = {CI->getOperand(0), RP, CI->getArgOperand(2)}; + } else if (IID == Intrinsic::cj_gcwrite_generic) { + assert(CI->getArgOperand(3) == RS); + Callee = Intrinsic::getDeclaration(CI->getModule(), + Intrinsic::cj_gcwrite_generic); + Args = {CI->getArgOperand(0), CI->getArgOperand(1), RP, + CI->getArgOperand(3)}; + } else if (IID == Intrinsic::cj_gcread_generic && + isa(getUnderlyingObject(Size, 0))) { + Callee = Intrinsic::getDeclaration(CI->getModule(), + Intrinsic::cj_assign_generic); + TI = getUnderlyingObject( + cast(getUnderlyingObject(Size, 0))->getPointerOperand()); + IRBuilder<> IRB(CI); + Args = {CI->getArgOperand(0), RP, + IRB.CreateBitOrPointerCast(TI, Callee->getArg(2)->getType())}; + } + } + if (Callee) { + IRBuilder<> IRB(CI); + auto *New = IRB.CreateCall(Callee, Args); + New->setDebugLoc(CI->getDebugLoc()); + // Update MSSA. + auto *MA = MSSA.getMemoryAccess(CI); + auto *NewMA = + MSSAU.createMemoryAccessBefore(New, MA->getDefiningAccess(), MA); + MA->replaceAllUsesWith(NewMA); + MSSAU.removeMemoryAccess(CI); + + CI->replaceAllUsesWith(New); + CI->eraseFromParent(); + Changed = true; + } + return Changed; + } + + bool runImpl() { + if (!EnableCopyOpt) + return false; + MSSA.ensureOptimizedUses(); + bool Changed = false; + for (auto I = inst_begin(F), E = inst_end(F); I != E;) { + auto *CI = dyn_cast(&*I++); + if (!CI || (CI->getIntrinsicID() != Intrinsic::cj_assign_generic && + CI->getIntrinsicID() != Intrinsic::cj_gcread_generic && + CI->getIntrinsicID() != Intrinsic::cj_gcwrite_generic && + CI->getIntrinsicID() != Intrinsic::cj_gcread_ref)) + continue; + Changed |= processGenericCopy(CI); + } + return Changed; + } +}; + +PreservedAnalyses +CJGenericIntrinsicOpt::run(Function &F, FunctionAnalysisManager &AM) const { + auto &AA = AM.getResult(F); + auto &MSSA = AM.getResult(F).getMSSA(); + auto &DT = AM.getResult(F); + MemorySSAUpdater MSSAU(&MSSA); + bool Changed = simplifyGenericInst(F, MSSAU); + Changed |= GenericCopyOpt(F, MSSA, MSSAU, DT, AA).runImpl(); + if (Changed) { + return PreservedAnalyses::none(); + } + return PreservedAnalyses::all(); +} + +namespace { +class CJGenericIntrinsicOptLegacyPass : public FunctionPass { +public: + static char ID; + + explicit CJGenericIntrinsicOptLegacyPass() : FunctionPass(ID) { + initializeCJGenericIntrinsicOptLegacyPassPass( + *PassRegistry::getPassRegistry()); + } + ~CJGenericIntrinsicOptLegacyPass() = default; + + bool runOnFunction(Function &F) override { + auto &AA = this->getAnalysis(F).getAAResults(); + auto &MSSA = this->getAnalysis(F).getMSSA(); + auto &DT = this->getAnalysis(F).getDomTree(); + MemorySSAUpdater MSSAU(&MSSA); + bool Changed = simplifyGenericInst(F, MSSAU); + + Changed |= GenericCopyOpt(F, MSSA, MSSAU, DT, AA).runImpl(); + return Changed; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + } +}; +} // namespace + +char CJGenericIntrinsicOptLegacyPass::ID = 0; + +FunctionPass *llvm::createCJGenericIntrinsicOptLegacyPass() { + return new CJGenericIntrinsicOptLegacyPass(); +} + +INITIALIZE_PASS(CJGenericIntrinsicOptLegacyPass, "cj-generic-intrinsic-opt", + "Cangjie Generic Intrinsics Optimize", false, false) diff --git a/llvm/lib/Transforms/Scalar/CJIRVerifier.cpp b/llvm/lib/Transforms/Scalar/CJIRVerifier.cpp new file mode 100644 index 000000000..14707f4d0 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/CJIRVerifier.cpp @@ -0,0 +1,808 @@ +//===- CJIRVerifier.cpp - Cangjie IR Verifier --------------*- C++ -*------===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This pass will verify whether the IR generated by the frontend of the cangjie +// meets the requirements. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/CJIRVerifier.h" + +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/Triple.h" +#include "llvm/IR/CJIntrinsics.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/InstVisitor.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/ModuleSlotTracker.h" +#include "llvm/IR/SafepointIRVerifier.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Scalar/CJFillMetadata.h" +#include "llvm/Transforms/Scalar/ReflectionInfo.h" + +using namespace llvm; +using namespace cangjie; + +#define Assert(C, ...) \ + do { \ + if (!(C)) { \ + checkFailed(__VA_ARGS__); \ + return; \ + } \ + } while (false) + +namespace { +class CJVerifier : public InstVisitor { + // Befriend the base class so it can delegate to private visit methods. + friend class InstVisitor; + +public: + CJVerifier(Module &M, raw_ostream *OS) + : OS(OS), M(M), MST(&M), C(M.getContext()), DL(M.getDataLayout()) { + EnableReflection = ReflectInfo::enable(M); + } + + bool verify(Function &F) { + Broken = false; + visit(F); + if (Broken) { + errs() << "in function " << F.getName() << '\n'; + } + return Broken; + } + + bool verify(GlobalVariable &GV) { + Broken = false; + visitGlobalVariable(GV); + if (Broken) { + errs() << "in global value " << GV.getName() << '\n'; + } + return Broken; + } + + void visitGlobalVariable(GlobalVariable &GV) { + if (GV.hasAttribute("CFileKlass")) { + verifyTypeInfo(GV); + } else if (GV.hasAttribute("cj_tt")) { + verifyTypeTemplate(GV); + } + } + + void visitFunction(const Function &F) { + // The c2cj and cjstub function is invoked by native function, + // its struct param does not have a base pointer. + if (F.hasFnAttribute("c2cj") || F.hasFnAttribute("cjstub")) + return; + + // Verify the mut function's `this` parameter and its baseptr type. + // If it is not a mut function, the gc pointer must be baseptr, and its + // type should be i8 addrspace(1)*. + + FunctionType *FT = F.getFunctionType(); + AttributeList Attrs = F.getAttributes(); + bool HasSRet = false; + + // Verify parameter attributes. + for (unsigned I = 0, E = FT->getNumParams(); I != E; ++I) { + Type *Ty = FT->getParamType(I); + PointerType *PT = dyn_cast(Ty); + // Check only pointer-type parameters. + if (!PT) + continue; + + AttributeSet ArgAttrs = Attrs.getParamAttrs(I); + if (ArgAttrs.hasAttribute(Attribute::StructRet)) { + Assert(I == 0, "sret is not on the first parameter!", F.getArg(I)); + HasSRet = true; + continue; + } + + // HasRet: call func(sret*, i8 AS(1)* this, i8 AS(1)* basePtr...) + // No Ret: call func(i8 AS(1)* this, i8 AS(1)* basePtr...) + unsigned ThisIdx = HasSRet ? 1 : 0; + if (I == ThisIdx && F.hasFnAttribute("record_mut")) { + Assert(PT->getAddressSpace() == 1, + "The mut this parameter be addrspace(1)*", F.getArg(I)); + unsigned BaseIdx = I + 1; + auto *BasePtrTy = dyn_cast(FT->getParamType(BaseIdx)); + Assert(BasePtrTy, "The base pointer of this should be pointer type", + F.getArg(BaseIdx)); + Assert(BasePtrTy == Type::getInt8PtrTy(C, 1), + "The base pointer should be i8 addrspace(1)*", + F.getArg(BaseIdx)); + continue; + } + + if (isGCPointerType(PT)) + Assert(PT == Type::getInt8PtrTy(C, 1), + "The gc pointer parameter should be i8 addrspace(1)*", + F.getArg(I)); + } + if (F.hasFnAttribute("native-interface-fn")) { + for (auto &I : instructions(F)) { + if (auto *CI = dyn_cast(&I)) { + Assert(CI->getIntrinsicID() == Intrinsic::cj_get_type_info, + "native interface function only support llvm.cj.get.type.info", + CI); + } + } + } + } + + void visitAllocaInst(AllocaInst &AI) { + Type *T = AI.getAllocatedType(); + if (auto *PT = dyn_cast(T)) { + Type *ET = PT->getElementType(); + if (isGCPointerType(PT) && ET->isStructTy()) { + checkFailed("Alloca %struct addrspace(1)* is not allowed.", &AI); + } + if (isGCPointerType(ET)) { + checkFailed("Alloca addrspace(1)** is not allowed.", &AI); + } + + if (!isa(ET)) { + return; + } + + for (const User *U : AI.users()) { + Assert(isa(U) || isIntrinsicX(U, Intrinsic::dbg_declare), + "Alloca struct* should only be used for dbg.declare", &AI); + } + return; + } + + if (auto *AT = dyn_cast(T)) { + Assert(!isGCPointerType(AT->getElementType()), + "The element type of array cannot be addrspace(1)*", &AI); + return; + } + + if (!isa(T) || !containsGCPtrType(T)) { + return; + } + + bool hasMemset = false; + for (const User *U : AI.users()) { + auto *Cast = dyn_cast(U); + if (!Cast) { + continue; + } + for (const User *UU : Cast->users()) { + if (isIntrinsicX(UU, Intrinsic::cj_memset)) { + hasMemset = true; + break; + } + } + } + Assert(hasMemset, "Missing cj.memset in allocation of structure.", &AI); + } + + void visitCallBase(CallBase &Call) { + Function *Callee = Call.getCalledFunction(); + if (Callee == nullptr) { + return; + } + + unsigned IID = Call.getIntrinsicID(); + switch (IID) { + case Intrinsic::cj_alloca_generic: { + Value *TIArg = Call.getArgOperand(0)->stripPointerCasts(); + if (auto *GV = dyn_cast(TIArg)) { + Assert(getTypeLayoutType(GV), + "llvm.cj.alloca.generic layout type is null.", &Call); + } + break; + } + case Intrinsic::cj_gcwrite_static_struct: + case Intrinsic::cj_gcread_static_struct: { + // gcwrite/gcread.static.struct should have AggType Metadata. + const MDNode *Metadata = Call.getMetadata("AggType"); + Assert(Metadata, "gcwrite/gcread.static.struct has no Metadata.", &Call); + Assert(Metadata->getNumOperands() == 1, + "AggType meta's size should be equal to 1!", &Call); + + StringRef STName = + dyn_cast(Metadata->getOperand(0).get())->getString(); + auto *ST = StructType::getTypeByName(C, STName); + Assert(ST, "gcwrite/gcread.static.struct AggType doesn't exsit.", &Call); + + // The size should not be 0. + auto *SizeArg = dyn_cast(getSize(&Call)); + if (SizeArg) { + int64_t Size = SizeArg->getSExtValue(); + Assert(Size > 0, "gcwrite/gcread.static.struct size should not be 0.", + &Call); + } + break; + } + case Intrinsic::cj_gcwrite_struct: + case Intrinsic::cj_gcread_struct: { + // The size should not be 0. + auto *SizeArg = dyn_cast(getSize(&Call)); + if (SizeArg) { + int64_t Size = SizeArg->getSExtValue(); + Assert(Size > 0, "gcwrite/gcread.struct size should not be 0.", &Call); + } + break; + } + case Intrinsic::memcpy: { + ConstantInt *SizeOpr = dyn_cast(Call.getArgOperand(2)); + if (SizeOpr == nullptr) { + break; + } + uint64_t Size = SizeOpr->getZExtValue(); + Value *Dst = Call.getArgOperand(0); + Value *Src = Call.getArgOperand(1); + Type *DstBaseTy = findMemoryBasePointer(Dst)->getType(); + Type *SrcBaseTy = findMemoryBasePointer(Src)->getType(); + // do nothing if stack copy to stack. + if (!isGCPointerType(DstBaseTy) && !isGCPointerType(SrcBaseTy)) { + break; + } + + auto containGCPtr = [this, Size](Value *Ptr) { + SmallVector AllRefPos; + return findReferencePositions(Ptr, Size, AllRefPos); + }; + if (!isGCPointerType(DstBaseTy) && containGCPtr(Dst)) { + checkFailed("The ReadBarrier instruction should be used here!", &Call); + } + if (!isGCPointerType(SrcBaseTy) && containGCPtr(Src)) { + checkFailed("The WriteBarrier instruction should be used here!", &Call); + } + break; + } + case Intrinsic::cj_get_mtable_func: { + // funcPtr = call i8** @llvm.cj.get.mtable.func(i8* ti1, i8* ti2, i64 i) + ConstantInt *IdxOpr = dyn_cast(Call.getArgOperand(2)); + if (IdxOpr == nullptr) { + checkFailed("Called Function index should be constant int!", &Call); + } + break; + } + default: + break; + } + + // Verify invoke instructions for cangjie intrinsics. + if (isa(&Call) && Callee->isCJIntrinsic()) { + switch (IID) { + default: + checkFailed("Cannot invoke an intrinsic: ", &Call); + break; + case Intrinsic::cj_gc_statepoint: + case Intrinsic::cj_malloc_object: + case Intrinsic::cj_malloc_array: + case Intrinsic::cj_malloc_array_generic: + case Intrinsic::cj_division_check_sdiv: + case Intrinsic::cj_division_check_udiv: + case Intrinsic::cj_division_check_srem: + case Intrinsic::cj_division_check_urem: + case Intrinsic::cj_throw_exception: + case Intrinsic::cj_alloca_generic: + break; + } + } + + if (Callee->isDeclaration()) { + return; + } + + // If the function of callsite has debuginfo, the callsite must also have. + if (Call.getFunction()->getSubprogram()) { + Assert(Call.getDebugLoc() || Callee->hasFnAttribute(Attribute::NoInline), + "The callsite debug info must have a !dbg location.", &Call); + } + } + + void visitAddrSpaceCastInst(AddrSpaceCastInst &I) { + // The cj2c and c2cj function does not need to be verified, because + // Frontend checking ensures that the functions does not transfer gcptr. + for (const User *U : I.users()) { + if (auto *CI = dyn_cast(U)) { + Function *Callee = CI->getCalledFunction(); + if (Callee && + (Callee->hasFnAttribute("cj2c") || Callee->hasFnAttribute("c2cj"))) + return; + } + if (auto *II = dyn_cast(U)) + if (II->getIntrinsicID() == Intrinsic::cj_blackhole) + return; + } + + if (auto *II = dyn_cast(I.getOperand(0))) + if (II->getIntrinsicID() == Intrinsic::cj_blackhole) + return; + + for (const User *U : I.users()) { + Assert(isa(U) || isa(U), + "Addrspacecast result can only be used for store or call.", &I); + + auto *SI = dyn_cast(U); + if (!SI) + continue; + + const Value *Ptr = SI->getPointerOperand(); + for (const User *UU : Ptr->users()) { + if (UU == U) { + continue; + } + Assert(isIntrinsicX(UU, Intrinsic::dbg_declare), + "If addrspacecast is used for store, it should only be used for " + "dbg.declare", + &I); + } + return; + } + + Type *SrcTy = I.getOperand(0)->getType(); + Assert(SrcTy->isPointerTy(), "AddrSpaceCast source must be a pointer", &I); + Assert((SrcTy->getNonOpaquePointerElementType()->isStructTy() || + SrcTy->getNonOpaquePointerElementType()->isArrayTy()), + "Addrspacecast can only be used for struct* or array* conversion.", + &I); + + // Verify that the addrspace cast is a transition from 0 to 1. + Assert(I.getSrcAddressSpace() == 0, + "AddrSpaceCast source must be addrspace(0)", &I); + Assert(I.getDestAddressSpace() == 1, + "AddrSpaceCast target must be addrspace(1)", &I); + } + + void visitBitCastInst(BitCastInst &I) { + // We only check the bitcast to i8* case. + if (!isInt8AS0Pty(&I)) { + return; + } + + Value *Base = I.stripPointerCasts(); + if (!isa(Base) || !isMemoryContainsGCPtrType(Base->getType())) { + return; + } + + for (User *U : I.users()) { + Assert(isa(U), "Bitcast struct* can only be callsite inst.", + &I); + } + } + + void visitStoreInst(StoreInst &SI) { + Value *Val = SI.getValueOperand(); + if (!Val->getType()->isPointerTy()) + return; + + Value *ValBase = findMemoryBasePointer(Val); + Value *PtrBase = findMemoryBasePointer(SI.getPointerOperand()); + if (isGCPointerType(ValBase->getType()) && + isGCPointerType(PtrBase->getType())) { + checkFailed("Need write barrier!", &SI); + } + + if (isGCPointerType(ValBase->getType()) && isa(PtrBase)) { + checkFailed("Need write static barrier!", &SI); + } + } + + void visitLoadInst(LoadInst &LI) { + if (!LI.getType()->isPointerTy()) + return; + + Value *PtrBase = findMemoryBasePointer(LI.getPointerOperand()); + if (isGCPointerType(LI.getType()) && isGCPointerType(PtrBase->getType())) { + checkFailed("Need read barrier!", &LI); + } + + if (isGCPointerType(LI.getType()) && isa(PtrBase)) { + checkFailed("Need read static barrier!", &LI); + } + } + +private: + raw_ostream *OS; + const Module &M; + ModuleSlotTracker MST; + LLVMContext &C; + const DataLayout &DL; + bool EnableReflection = true; + // Trace Check for Errors + bool Broken = false; + + void verifyTypeInfo(GlobalVariable &GV) { + TypeInfo Klass(&GV); + if (Klass.isPureValueType()) + return; + + // check field data + if (Klass.getFieldNum() > 0) { + auto *FieldsTy = dyn_cast(Klass.getFields()->getType()); + Assert(FieldsTy->getNumElements() == Klass.getFieldNum(), + "Klass fields num error!", &GV); + + auto *OffsetsTy = dyn_cast(Klass.getOffsets()->getType()); + Assert(OffsetsTy->getNumElements() == Klass.getFieldNum(), + "Klass offsets num error!", &GV); + + StructType *ST = Klass.getLayoutType(); + Assert(ST, "Klass can not find RelatedType!", &GV); + for (unsigned Idx = 0; Idx < Klass.getFieldNum(); Idx++) { + GlobalVariable *FieldGV = Klass.getField(Idx); + if (!FieldGV->hasInitializer()) + continue; + + auto FieldType = ST->getElementType(Idx); + auto FieldOffset = DL.getStructLayout(ST)->getElementOffset(Idx); + TypeInfo FieldKlass(FieldGV); + Assert(FieldOffset == Klass.getOffset(Idx), "Klass field offset error!", + &GV); + + unsigned FieldSize = static_cast( + DL.getTypeAllocSize(FieldType).getFixedSize()); + if (FieldKlass.isReferenceType()) { + Assert(FieldSize == DL.getPointerSize(), + "Klass field memory size error!", &GV); + } else { + Assert(FieldSize == FieldKlass.getSize(), + "Klass field memory size error!", &GV); + auto FieldAlign = DL.getABITypeAlignment(FieldType); + Assert(FieldAlign == FieldKlass.getAlign(), + "Klass field align error!", &GV); + } + } + } + // check type args data + if (Klass.getTypeArgNum() > 0) { + Constant *TypeArgs = Klass.getTypeArgs(); + Assert(TypeArgs->getNumOperands() == Klass.getTypeArgNum(), + "Klass typeArgs num error!", &GV); + } + // check array typeinfo data + if (Klass.isArray()) { + Assert(Klass.getSuperOrComponent(), + "Array Klass has no element typeinfo!", &GV); + } + // check typetemplate + if (auto Temp = Klass.getTypeTemplate()) { + TypeTemplate TT(Temp); + Assert(TT.getKind() == Klass.Kind, "typetemplate kind error!", &GV); + Assert(TT.getFieldNum() == Klass.getFieldNum(), + "typetemplate field num error!", &GV); + Assert(TT.getTypeArgNum() == Klass.getTypeArgNum(), + "typetemplate typeArgs num error!", &GV); + } + // check reflection + auto ReflectMD = dyn_cast_or_null(GV.getMetadata("Reflection")); + if (ReflectMD != nullptr) { + verifyReflection(ReflectMD, Klass.isEnum()); + } + } + + void verifyTypeTemplate(GlobalVariable &GV) { + TypeTemplate TT(&GV); + // check reflection + auto ReflectMD = dyn_cast_or_null(GV.getMetadata("Reflection")); + if (ReflectMD != nullptr) { + verifyReflection(ReflectMD, TT.isEnum()); + } + } + + void verifyReflection(MDTuple *ReflectMD, bool IsEnum) { + if (IsEnum) { + if (EnableReflection) { + verifyEnumReflection(ReflectMD); + } else { + verifyEnumDebugInfo(ReflectMD); + } + } else { + if (EnableReflection) { + verifyDefaultReflection(ReflectMD); + } else { + verifyDefaultDebugInfo(ReflectMD); + } + } + } + + // !{, !{enum ctors}, !{instance methods}, !{static methods}, + // !{attributes}} + void verifyEnumReflection(MDTuple *ReflectMD) { + Assert(ReflectMD->getNumOperands() == ERT_MAX, + "EnumReflection format: operand numbers error!", ReflectMD); + + // check enum ctors + const MDTuple *CtorMDs = getMDOperand(ReflectMD, ERT_ENUM_CTOR); + for (unsigned Idx = 0; Idx < CtorMDs->getNumOperands(); ++Idx) { + auto *CtorMD = dyn_cast(CtorMDs->getOperand(Idx).get()); + // !{, } + Assert(CtorMD->getNumOperands() == 2, + "EnumReflection format: enum ctor number error!", ReflectMD); + } + // check instance method + const MDTuple *MethodMDs = getMDOperand(ReflectMD, ERT_INSTANCE_METHOD); + for (unsigned Idx = 0; Idx < MethodMDs->getNumOperands(); ++Idx) { + auto *MethodMD = dyn_cast(MethodMDs->getOperand(Idx).get()); + verifyReflectionMethod(MethodMD); + } + // check static method + const MDTuple *StaticMethodMDs = getMDOperand(ReflectMD, ERT_STATIC_METHOD); + for (unsigned Idx = 0; Idx < StaticMethodMDs->getNumOperands(); ++Idx) { + auto *MethodMD = + dyn_cast(StaticMethodMDs->getOperand(Idx).get()); + verifyReflectionMethod(MethodMD); + } + // check type attribute + const MDTuple *AttributeMD = getMDOperand(ReflectMD, ERT_ATTRIBUTE); + verifyReflectionAttribute(AttributeMD); + } + + // !{, !{enum ctors}, !{attributes}} + void verifyEnumDebugInfo(MDTuple *ReflectMD) { + Assert(ReflectMD->getNumOperands() == EDT_MAX, + "EnumDebugInfo format: operand numbers error!", ReflectMD); + + // check enum ctors + const MDTuple *CtorMDs = getMDOperand(ReflectMD, EDT_ENUM_CTOR); + for (unsigned Idx = 0; Idx < CtorMDs->getNumOperands(); ++Idx) { + auto *CtorMD = dyn_cast(CtorMDs->getOperand(Idx).get()); + // !{, } + Assert(CtorMD->getNumOperands() == 2, + "EnumDebugInfo format: enum ctor number error!", ReflectMD); + } + // check type attribute + const MDTuple *AttributeMD = getMDOperand(ReflectMD, EDT_ATTRIBUTE); + verifyReflectionAttribute(AttributeMD); + } + + // !{, , !{instance fields},!{static fields}, + // !{instance methods}, !{static methods}, !{type attributes}} + void verifyDefaultReflection(MDTuple *ReflectMD) { + Assert(ReflectMD->getNumOperands() == CRT_MAX, + "Reflection format: operand numbers error!", ReflectMD); + + // check instance field + const MDTuple *FieldMDs = getMDOperand(ReflectMD, CRT_INSTANCE_FIELD); + for (unsigned Idx = 0; Idx < FieldMDs->getNumOperands(); ++Idx) { + // !{Idx, field name, !{field attributes}} + auto *FieldMD = dyn_cast(FieldMDs->getOperand(Idx).get()); + Assert(FieldMD->getNumOperands() == 3, + "Reflection format: field meta number error!", ReflectMD); + // 2: attribute + const MDTuple *AttributeMD = getMDOperand(FieldMD, 2); + verifyReflectionAttribute(AttributeMD); + } + // check static field + const MDTuple *StaticFieldMDs = getMDOperand(ReflectMD, CRT_STATIC_FIELD); + for (unsigned Idx = 0; Idx < StaticFieldMDs->getNumOperands(); ++Idx) { + // !{, , , !{attributes}} + auto *FieldMD = dyn_cast(StaticFieldMDs->getOperand(Idx).get()); + Assert(FieldMD->getNumOperands() == 4, + "Reflection format: static field meta number error!", ReflectMD); + // 3: type attribute + const MDTuple *AttributeMD = getMDOperand(FieldMD, 3); + verifyReflectionAttribute(AttributeMD); + } + // check instance method + const MDTuple *MethodMDs = getMDOperand(ReflectMD, CRT_INSTANCE_METHOD); + for (unsigned Idx = 0; Idx < MethodMDs->getNumOperands(); ++Idx) { + auto *MethodMD = dyn_cast(MethodMDs->getOperand(Idx).get()); + verifyReflectionMethod(MethodMD); + } + // check static method + const MDTuple *StaticMethodMDs = getMDOperand(ReflectMD, CRT_STATIC_METHOD); + for (unsigned Idx = 0; Idx < StaticMethodMDs->getNumOperands(); ++Idx) { + auto *MethodMD = + dyn_cast(StaticMethodMDs->getOperand(Idx).get()); + verifyReflectionMethod(MethodMD); + } + // check type attribute + const MDTuple *AttributeMD = getMDOperand(ReflectMD, CRT_ATTRIBUTE); + verifyReflectionAttribute(AttributeMD); + } + + void verifyDefaultDebugInfo(MDTuple *ReflectMD) { + // !{, !{instance field}} + Assert(ReflectMD->getNumOperands() == CDT_MAX, + "EnumDebugInfo format: operand numbers error!", ReflectMD); + // check instance field + const MDTuple *FieldMDs = getMDOperand(ReflectMD, CDT_INSTANCE_FIELD); + for (unsigned Idx = 0; Idx < FieldMDs->getNumOperands(); ++Idx) { + // !{Idx, field name, !{field attributes}} + auto *FieldMD = dyn_cast(FieldMDs->getOperand(Idx).get()); + Assert(FieldMD->getNumOperands() == 3, + "EnumDebugInfo format: field meta number error!", ReflectMD); + // 2: attribute + const MDTuple *AttributeMD = getMDOperand(FieldMD, 2); + verifyReflectionAttribute(AttributeMD); + } + } + + // !{, , , !{!actualParams}, + // !{!genericParams}, !{attributes},} + void verifyReflectionMethod(const MDTuple *MethodMD) { + Assert(MethodMD->getNumOperands() == MRT_MAX, + "Reflection format: method meta number error!", MethodMD); + // check type attribute + const MDTuple *AttributeMD = getMDOperand(MethodMD, MRT_ATTRIBUTE); + verifyReflectionAttribute(AttributeMD); + // check actualParamInfo + MDTuple *ActualParamMD = getMDOperand(MethodMD, MRT_ACTUAL_PARAM); + for (unsigned Idx = 0; Idx < ActualParamMD->getNumOperands(); ++Idx) { + auto *ParamTuple = dyn_cast(ActualParamMD->getOperand(Idx)); + // {!param name, !type name, !attributes} + Assert(ParamTuple->getNumOperands() == 3, + "Reflection format: actual paramter number error!", MethodMD); + } + // cheeck genericParamInfo + MDTuple *GenericParamMD = getMDOperand(MethodMD, MRT_GENERIC_PARAM); + for (unsigned Idx = 0; Idx < GenericParamMD->getNumOperands(); ++Idx) { + Assert(!getStringFromMD(GenericParamMD, Idx).empty(), + "Reflection format: generic paramter error!", MethodMD); + } + } + + void verifyReflectionAttribute(const MDTuple *AttrMD) { + unsigned NumOps = AttrMD->getNumOperands(); + Assert(NumOps > 0, + "Reflection format: attribute metas must have annotation at least!", + AttrMD); + + // last element is annotation info + for (unsigned Idx = 0; Idx < NumOps - 1; ++Idx) { + StringRef Mod = getStringFromMD(AttrMD, Idx); + if (RMT_MAX == getTypeModifier(Mod)) + checkFailed("Reflection format: modifier type is invalid", AttrMD); + } + } + + StringRef getStringFromMD(const MDTuple *MD, unsigned Idx) { + return dyn_cast(MD->getOperand(Idx).get())->getString(); + } + + MDTuple *getMDOperand(const MDTuple *MD, unsigned Idx) { + return dyn_cast(MD->getOperand(Idx).get()); + } + + bool isIntrinsicX(const User *U, Intrinsic::ID IID) { + if (auto *II = dyn_cast(U)) { + if (II->getIntrinsicID() == IID) { + return true; + } + } + return false; + } + + bool findReferencePositions(Value *Ptr, uint64_t Size, + SmallVector &AllRefPos) { + APInt Offset(DL.getIndexSizeInBits(0), 0); + Value *PtrBase = Ptr->stripAndAccumulateConstantOffsets(DL, Offset, true); + if (isGCPointerType(PtrBase->getType())) { + report_fatal_error("The dst of memcpy base is gc pointer!"); + return true; + } + StructType *ST = dyn_cast( + PtrBase->getType()->getNonOpaquePointerElementType()); + if (!ST) + return false; + + unsigned AllocSize = + static_cast(DL.getTypeAllocSize(ST).getFixedSize()); + if (AllocSize < Size) { + report_fatal_error("The memcpy size exceeds allocation size!"); + return false; + } + uint64_t BeginPos = Offset.getZExtValue(); + uint64_t EndPos = BeginPos + Size; + findReferencePositionInStruct(ST, BeginPos, EndPos, 0, AllRefPos); + return AllRefPos.size() != 0; + } + + void findReferencePositionInStruct(StructType *ST, uint64_t BeginPos, + uint64_t EndPos, uint64_t CurPos, + SmallVector &AllRefPos) { + for (uint64_t i = 0; i < ST->getNumElements(); i++) { + uint64_t Offset = DL.getStructLayout(ST)->getElementOffset(i) + CurPos; + + // 1. ST = {i64, {i64, {i64, i8*, i64}}, i64}, Begin = 16, End = 40 + // 2. ST = {i64, [5 * EST]} + // EST = {i64, i8*, i64} + // Begin = 32, End = 64 + if (i != ST->getNumElements() - 1) { + uint64_t NextOffset = DL.getStructLayout(ST)->getElementOffset(i + 1); + if (Offset < BeginPos && NextOffset < BeginPos) + continue; + } + if (Offset >= EndPos) + return; + Type *EleTy = ST->getElementType(i); + if (isGCPointerType(EleTy)) { + AllRefPos.push_back(Offset); + } else if (auto *EST = dyn_cast(EleTy)) { + findReferencePositionInStruct(EST, BeginPos, EndPos, Offset, AllRefPos); + } else if (auto *EAT = dyn_cast(EleTy)) { + findReferencePositionInArray(EAT, BeginPos, EndPos, Offset, AllRefPos); + } + } + } + + void findReferencePositionInArray(ArrayType *AT, uint64_t BeginPos, + uint64_t EndPos, uint64_t CurPos, + SmallVector &AllRefPos) { + uint64_t EleNum = AT->getNumElements(); + Type *EleType = AT->getElementType(); + if (isa(EleType) && isGCPointerType(EleType)) { + for (uint64_t Idx = 0; Idx < EleNum; Idx++) { + AllRefPos.push_back(CurPos + 8 * Idx); // 8: pointer size + } + } else if (auto *ArrayStruct = dyn_cast(EleType)) { + uint64_t EleSize = DL.getStructLayout(ArrayStruct)->getSizeInBytes(); + for (uint64_t Idx = 0; Idx < EleNum; Idx++) { + findReferencePositionInStruct(ArrayStruct, BeginPos, EndPos, + CurPos + Idx * EleSize, AllRefPos); + } + } + } + + void checkFailed(const Twine &Message, const Value *V) { + *OS << Message << '\n'; + V->print(*OS, MST); + *OS << '\n'; + Broken = true; + } + + void checkFailed(const Twine &Message, const Metadata *MD) { + *OS << Message << '\n'; + MD->print(*OS, MST, &M); + *OS << '\n'; + Broken = true; + } +}; + +} // namespace + +static bool doCJIRVerify(Module &M) { + Triple T(M.getTargetTriple()); + if (T.isARM()) + return false; + bool Broken = false; + CJVerifier CV(M, &dbgs()); + + for (GlobalVariable &GV : M.globals()) { + if (!GV.hasInitializer()) + continue; + + Broken |= CV.verify(GV); + if (Broken) + report_fatal_error("Broken global value found, compilation aborted!"); + } + + for (Function &F : M) { + if (!F.hasCangjieGC()) + continue; + + Broken |= CV.verify(F); + if (Broken) + report_fatal_error("Broken function found, compilation aborted!"); + } + return Broken; +} + +PreservedAnalyses CJIRVerifier::run(Module &M, ModuleAnalysisManager &) const { + if (hasRunCangjieOpt(M)) { + return PreservedAnalyses::all(); + } + if (doCJIRVerify(M)) { + return PreservedAnalyses::none(); + } + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Transforms/Scalar/CJLoopFloatOpt.cpp b/llvm/lib/Transforms/Scalar/CJLoopFloatOpt.cpp new file mode 100644 index 000000000..14b0d3f87 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/CJLoopFloatOpt.cpp @@ -0,0 +1,670 @@ +//===- CJLoopFloatOpt.cpp ---------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This pass optimizes the scene where a top-level loop only involves +// straightforward floating-point computations. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/CJLoopFloatOpt.h" + +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/PostDominators.h" +#include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/InitializePasses.h" +#include "llvm/Transforms/Scalar.h" + +using namespace llvm; + +static double convertToDouble(Value *V) { + ConstantFP *FPVal = cast(V); + const APFloat &APF = FPVal->getValueAPF(); + return APF.convertToDouble(); +} + +static int64_t convertToInt64(Value *V) { + return static_cast(convertToDouble(V)); +} + +static ConstantInt *getConstIntByDouble(Value *V) { + // 64: 64bit + APInt IntVal(64, convertToInt64(V), true); + return ConstantInt::get(V->getContext(), IntVal); +} + +static bool isConstantDouble(Value *V) { + if (!V->getType()->isDoubleTy()) { + return false; + } + ConstantFP *FPVal = dyn_cast(V); + if (!FPVal) { + return false; + } + const APFloat &APF = FPVal->getValueAPF(); + if (!APF.isIEEE() || !APF.isFinite()) { + return false; + } + return true; +} + +static bool isNoDecimalDouble(Value *V) { + if (!isConstantDouble(V)) { + return false; + } + ConstantFP *FPVal = cast(V); + const APFloat &APF = FPVal->getValueAPF(); + double doubleVal = APF.convertToDouble(); + if (doubleVal > (double)INT32_MAX || doubleVal < (double)INT32_MIN) { + return false; + } + return APF.isInteger(); +} + +static constexpr char fpExceptionBbName[] = "fp.convert.exception"; + +static bool checkFPUse(PHINode *PHI, Loop *L, Value *LatchVal) { + bool hasSimpleFloatComputation = false; + for (auto Use : PHI->users()) { + auto *Inst = dyn_cast(Use); + if (!Inst) { + return false; + } + if (!L->contains(Inst->getParent())) { + return false; + } + switch (Inst->getOpcode()) { + case Instruction::Call: { + auto *CI = dyn_cast(Inst); + if (!CI || (CI->getIntrinsicID() != Intrinsic::fptosi_sat && + CI->getIntrinsicID() != Intrinsic::fptoui_sat)) { + return false; + } + break; + } + case Instruction::FPToSI: { + if (!Inst->getType()->isIntegerTy(64)) { // 64: 64bit integer + return false; + } + auto *CI = dyn_cast(Inst->getNextNode()); + if (!CI || CI->getIntrinsicID() != Intrinsic::cj_get_fp_state) { + return false; + } + auto *AndInst = dyn_cast(CI->getNextNode()); + if (!AndInst || AndInst->getOpcode() != Instruction::And) { + return false; + } + auto *CmpInst = dyn_cast(AndInst->getNextNode()); + if (!CmpInst) { + return false; + } + auto *BRInst = dyn_cast(CmpInst->getNextNode()); + if (!BRInst || !BRInst->isConditional()) { + return false; + } + auto SuccBBName0 = BRInst->getSuccessor(0)->getName(); + auto SuccBBName1 = BRInst->getSuccessor(1)->getName(); + if (!SuccBBName0.startswith(fpExceptionBbName) && + !SuccBBName1.startswith(fpExceptionBbName)) { + return false; + } + break; + } + case Instruction::BitCast: { + if (!Inst->getParent()->getName().startswith(fpExceptionBbName)) { + return false; + } + break; + } + case Instruction::FCmp: { + auto BBName = Inst->getParent()->getName(); + if (!BBName.startswith("not.inf.nan") && + !BBName.startswith("lower.bound.ok")) { + return false; + } + break; + } + case Instruction::FAdd: + case Instruction::FSub: { + // First operand must be PHI. + if (Inst->getOperand(0) != PHI) { + return false; + } + // Only support single fadd or fsub. + if (Inst != LatchVal) { + return false; + } + // Another operand can be constantFP or selectVal. + if (auto *SelectI = dyn_cast(Inst->getOperand(1))) { + if (!isNoDecimalDouble(SelectI->getTrueValue()) || + !isNoDecimalDouble(SelectI->getFalseValue())) { + return false; + } + } else if (isa(Inst->getOperand(1))) { + if (!isNoDecimalDouble(Inst->getOperand(1))) { + return false; + } + } else { + return false; + } + hasSimpleFloatComputation = true; + break; + } + default: + return false; + } + } + return hasSimpleFloatComputation; +} + +static bool checkInt64Bound(unsigned Opcode, double Base, double Num, + uint64_t LoopCount) { + double Val = 0; + if (Opcode == Instruction::FAdd) { + Val = Base + Num * LoopCount; + } else { + Val = Base - Num * LoopCount; + } + if (!std::isfinite(Val)) { + return false; + } + return Val < (double)INT64_MAX && Val > (double)INT64_MIN; +} + +static bool checkIntOverflow(PHINode *PHI, Loop *L, uint64_t LoopCount, + Value *InitialVal) { + double Base = convertToDouble(InitialVal); + for (auto Use : PHI->users()) { + auto *Inst = cast(Use); + unsigned Opcode = Inst->getOpcode(); + if (Opcode == Instruction::FAdd || Opcode == Instruction::FSub) { + auto *SelectI = dyn_cast(Inst->getOperand(1)); + if (SelectI) { + double TrueValue = convertToDouble(SelectI->getTrueValue()); + double FalseValue = convertToDouble(SelectI->getFalseValue()); + return checkInt64Bound(Opcode, Base, TrueValue, LoopCount) && + checkInt64Bound(Opcode, Base, FalseValue, LoopCount); + } + double Val = convertToDouble(Inst->getOperand(1)); + return checkInt64Bound(Opcode, Base, Val, LoopCount); + } + } + return false; +} + +static PHINode *replacePHI(PHINode *PHI, Value *InitialVal) { + ConstantInt *Val = getConstIntByDouble(InitialVal); + IRBuilder<> Builder(PHI); + Type *Int64Type = Type::getInt64Ty(PHI->getContext()); + PHINode *NewPHI = Builder.CreatePHI(Int64Type, 2); + if (isa(PHI->getIncomingValue(0))) { + NewPHI->addIncoming(Val, PHI->getIncomingBlock(0)); + } else { + NewPHI->addIncoming(Val, PHI->getIncomingBlock(1)); + } + return NewPHI; +} + +static void replaceFloatToInt(PHINode *PHI, + SetVector &RemoveInsts, + Value *InitialVal) { + PHINode *NewPHI = replacePHI(PHI, InitialVal); + Value *ComputationResult = nullptr; + + for (auto Use : PHI->users()) { + auto *Inst = cast(Use); + switch (Inst->getOpcode()) { + case Instruction::Call: + RemoveInsts.insert(Inst); // fptosi.sat or fptoui.sat + Inst->replaceAllUsesWith(NewPHI); + break; + case Instruction::FPToSI: { + auto *CI = Inst->getNextNode(); + auto *AndI = CI->getNextNode(); + auto *CmpI = AndI->getNextNode(); + auto *BR = cast(CmpI->getNextNode()); + RemoveInsts.insert(BR); // BRInst + RemoveInsts.insert(CmpI); // CmpInst + RemoveInsts.insert(AndI); // AndInst + RemoveInsts.insert(CI); // GetFpState + RemoveInsts.insert(Inst); // FPToSI + IRBuilder<> Builder(BR); + if (BR->getSuccessor(0)->getName().startswith(fpExceptionBbName)) { + Builder.CreateBr(BR->getSuccessor(1)); + } else { + Builder.CreateBr(BR->getSuccessor(0)); + } + Inst->replaceAllUsesWith(NewPHI); + break; + } + case Instruction::BitCast: + case Instruction::FCmp: + // remove later by DCE + break; + case Instruction::FAdd: + case Instruction::FSub: { + Value *Val = nullptr; + IRBuilder<> Builder(Inst); + auto *SelectI = dyn_cast(Inst->getOperand(1)); + if (SelectI) { + ConstantInt *TrueValue = getConstIntByDouble(SelectI->getTrueValue()); + ConstantInt *FalseValue = getConstIntByDouble(SelectI->getFalseValue()); + Val = Builder.CreateSelect(SelectI->getCondition(), TrueValue, + FalseValue); + } else { + Val = getConstIntByDouble(Inst->getOperand(1)); + } + if (Inst->getOpcode() == Instruction::FAdd) { + ComputationResult = Builder.CreateAdd(NewPHI, Val); + } else { + ComputationResult = Builder.CreateSub(NewPHI, Val); + } + auto *SItoFPInst = + Builder.CreateSIToFP(ComputationResult, Inst->getType()); + Inst->replaceAllUsesWith(SItoFPInst); + RemoveInsts.insert(Inst); + if (SelectI) + RemoveInsts.insert(SelectI); + break; + } + default: + assert(false && "Unsupported instr opcode in CJLoopFloatOpt"); + return; + } + } + if (isa(PHI->getIncomingValue(0))) { + NewPHI->addIncoming(ComputationResult, PHI->getIncomingBlock(1)); + } else { + NewPHI->addIncoming(ComputationResult, PHI->getIncomingBlock(0)); + } +} + +static void eraseInsts(SetVector &RemoveInsts) { + if (!RemoveInsts.empty()) { + for (auto *I : RemoveInsts) { + I->eraseFromParent(); + } + } + RemoveInsts.clear(); +} + +/** + * Optimize the scene where a top-level loop only involves + * straightforward floating-point computations without any decimal operations. + * + * for example: + * + * ## before opt + * =========================================================== + * loop.header: + * %fpVal = phi double [ 1.000000e+00, %preheader ], [ %faddVal, %loop.body ] + * ... + * %26 = fptosi double %fpVal to i64 + * %27 = call i64 @llvm.cj.get.fp.state.i64(i64 %26) + * %28 = and i64 %27, 1 + * %29 = icmp eq i64 %28, 0 + * br i1 %29, label %loop.body, label %fp.convert.exception + * + * fp.convert.exception: + * %30 = bitcast double %fpVal to i64 + * + * not.inf.nan: + * %f2i.lt.min = fcmp ogt double %fpVal, 0xC3E0000000000001 + * + * lower.bound.ok: + * %f2i.gt.max = fcmp olt double %fpVal, 0x43E0000000000000 + * + * loop.body: + * %selectVal = select i1 %icmpeq, double 1.000000e+00, double 2.000000e+00 + * %faddVal = fadd double %fpVal, %selectVal + * %exitcond.not = icmp eq i64 %25, 3000000 + * br i1 %exitcond.not, label %exit, label %loop.header + * + * ## after opt + * =========================================================== + * loop.header: + * %25 = phi i64 [ 1, %preheader ], [ %34, %loop.body ] + * br label %loop.body + * + * loop.body: + * %33 = select i1 %icmpeq, i64 1, i64 2 + * %34 = add i64 %25, %33 + * %35 = sitofp i64 %34 to double + * %exitcond.not = icmp eq i64 %26, 3000000 + * br i1 %exitcond.not, label %exit, label %loop.header + */ +bool loopFPComputationOpt(Function &F, LoopInfo &LI, ScalarEvolution &SE) { + bool Changed = false; + SetVector RemoveInsts; + for (Loop *L : LI) { + if (!L->getSubLoops().empty()) { + continue; + } + + const SCEV *MaxTrips = SE.getConstantMaxBackedgeTakenCount(L); + if (isa(MaxTrips)) { + return false; + } + + BasicBlock *HeaderBB = L->getHeader(); + for (Instruction &Inst : *HeaderBB) { + if (auto *PHI = dyn_cast(&Inst)) { + // 2: only support two coming BB + if (PHI->getNumIncomingValues() != 2) { + continue; + } + Value *LatchVal = nullptr; + Value *InitialVal = nullptr; + BasicBlock *Pred0 = PHI->getIncomingBlock(0); + BasicBlock *Pred1 = PHI->getIncomingBlock(1); + // Firstly: match the scene. + if (isNoDecimalDouble(PHI->getIncomingValue(0)) && + !L->contains(Pred0) && L->contains(Pred1)) { + InitialVal = PHI->getIncomingValue(0); + LatchVal = PHI->getIncomingValue(1); + } else if (isNoDecimalDouble(PHI->getIncomingValue(1)) && + !L->contains(Pred1) && L->contains(Pred0)) { + InitialVal = PHI->getIncomingValue(1); + LatchVal = PHI->getIncomingValue(0); + } else { + continue; + } + + // Check the use of FP. + if (!checkFPUse(PHI, L, LatchVal)) { + continue; + } + + // Secondly: check for integer Overflow. + APInt MaxCount = SE.getUnsignedRange(MaxTrips).getUnsignedMax(); + if (!checkIntOverflow(PHI, L, MaxCount.getZExtValue() + 1, + InitialVal)) { + continue; + } + + // Thirdly: replace the fp instruction. + replaceFloatToInt(PHI, RemoveInsts, InitialVal); + Changed = true; + } else { + break; + } + } + } + eraseInsts(RemoveInsts); + return Changed; +} + +static bool checkLoopEarlyOut(Loop *L) { + for (BasicBlock *BB : L->getBlocks()) { + for (Instruction &I : *BB) { + if (I.mayThrow() || !I.willReturn()) { + return true; + } + } + } + return false; +} + +// Main optimization concept: fadd(PHI, >=PHI) >= fmul(PHI, 2) +// Locate the fadd instruction and examine its operands: +// If the first operand is the PHI node and the second operand is greater than +// or equal to the PHI node, the minimum value of fadd is equivalent to +// fmul(PHI, 2). +static bool checkLatchInst(Instruction *LatchInst, PHINode *PHI) { + // Only support FAdd Inst. + if (LatchInst->getOpcode() != Instruction::FAdd) { + return false; + } + + Value *LatchInstOp = nullptr; + // One of FAdd operands must be PHI. + if (LatchInst->getOperand(0) == PHI) { + LatchInstOp = LatchInst->getOperand(1); + } else if (LatchInst->getOperand(1) == PHI) { + LatchInstOp = LatchInst->getOperand(0); + } else { + return false; + } + + // One of FAdd operands must be SelectInst or FpInst. + Instruction *OpInst = nullptr; + auto *SelectI = dyn_cast(LatchInstOp); + if (SelectI) { + // One of SelectInst operands must be PHI. + Value *SelectOp = nullptr; + if (SelectI->getTrueValue() == PHI) { + SelectOp = SelectI->getFalseValue(); + } else if (SelectI->getFalseValue() == PHI) { + SelectOp = SelectI->getTrueValue(); + } else { + return false; + } + + OpInst = dyn_cast(SelectOp); + } else { + OpInst = dyn_cast(LatchInstOp); + } + + if (!OpInst) { + return false; + } + + // One of OpInst operands must be PHI. + Value *InstOp = nullptr; + if (OpInst->getOperand(0) == PHI) { + InstOp = OpInst->getOperand(1); + } else if (OpInst->getOperand(1) == PHI) { + InstOp = OpInst->getOperand(0); + } else { + return false; + } + + if (!isConstantDouble(InstOp)) { + return false; + } + + double doubleVal = convertToDouble(InstOp); + + // Make sure the result of OpInst is greater than or equal to PHI. + switch (OpInst->getOpcode()) { + case Instruction::FAdd: + return doubleVal >= 0; + case Instruction::FSub: + return OpInst->getOperand(0) == PHI && doubleVal <= 0; + case Instruction::FMul: + return doubleVal >= 1; + case Instruction::FDiv: + return OpInst->getOperand(0) == PHI && doubleVal > 0 && doubleVal <= 1; + default: + return false; + } +} + +static bool checkFPInfinity(uint64_t LoopCount, Value *InitialVal) { + double Val = convertToDouble(InitialVal); + for (uint64_t I = 0; I < LoopCount; I++) { + Val = Val * 2; // 2: multiplier + } + return std::isinf(Val); +} + +static void replaceResultToInf(Instruction *LatchInst, Loop *L) { + auto *New = ConstantFP::getInfinity( + Type::getDoubleTy(LatchInst->getContext()), false); + LatchInst->replaceUsesWithIf(New, [L](Use &U) { + auto *I = dyn_cast(U.getUser()); + return !L->contains(I->getParent()); + }); +} + +/** + * If the result of a floating-point computation is infinity, we replace it + * with infinity immediately after the loop. + * + * for example: + * + * before opt: + * =========================================================== + * loop.header: + * %f3.04 = phi double [ 1.000000e+00, %bb59 ], [ %f3.1, %bb64 ] + * ... + * + * loop.body: + * %2 = fmul double %f3.04, 2.000000e+00 + * %f3.0.pn = select i1 %icmpeq, double %f3.04, double %2 + * %f3.1 = fadd double %f3.04, %f3.0.pn + * ... + * + * loop.exit: + * call void @"_ZN8std.core7printlnEd"(double %f3.1) + * + * after opt: + * =========================================================== + * loop.header: + * same as before + * + * loop.body: + * same as before + * + * loop.exit: + * call void @"_ZN8std.core7printlnEd"(double 0x7FF0000000000000) // infinity + */ +bool loopFPInfinityOpt(Function &F, LoopInfo &LI, PostDominatorTree &PDT, + ScalarEvolution &SE) { + bool Changed = false; + for (Loop *L : LI) { + if (!L->getSubLoops().empty()) + continue; + + if (checkLoopEarlyOut(L)) + continue; + + const SCEV *MaxTrips = SE.getConstantMaxBackedgeTakenCount(L); + if (isa(MaxTrips)) + return false; + + BasicBlock *HeaderBB = L->getHeader(); + for (Instruction &Inst : *HeaderBB) { + if (auto *PHI = dyn_cast(&Inst)) { + // 2: only support two coming BB + if (PHI->getNumIncomingValues() != 2) + continue; + + Value *LatchVal = nullptr; + Value *InitialVal = nullptr; + BasicBlock *LatchBB = nullptr; + BasicBlock *Pred0 = PHI->getIncomingBlock(0); + BasicBlock *Pred1 = PHI->getIncomingBlock(1); + // Firstly: match the scene. + if (isConstantDouble(PHI->getIncomingValue(0)) && !L->contains(Pred0) && + L->contains(Pred1)) { + InitialVal = PHI->getIncomingValue(0); + LatchVal = PHI->getIncomingValue(1); + LatchBB = PHI->getIncomingBlock(1); + } else if (isConstantDouble(PHI->getIncomingValue(1)) && + !L->contains(Pred1) && L->contains(Pred0)) { + InitialVal = PHI->getIncomingValue(1); + LatchVal = PHI->getIncomingValue(0); + LatchBB = PHI->getIncomingBlock(0); + } else { + continue; + } + + if (convertToDouble(InitialVal) < 0) + continue; + + auto *LatchInst = dyn_cast(LatchVal); + if (!LatchInst) + continue; + + // Make sure LatchInst will be executed in every iteration. + if (!PDT.dominates(LatchInst->getParent(), HeaderBB)) + continue; + + if (!PDT.dominates(LatchBB, LatchInst->getParent())) + continue; + + // Check if LatchInst is equal to fmul(PHI, 2). + if (!checkLatchInst(LatchInst, PHI)) + continue; + + // Secondly: check fp is Infinity. + APInt MaxCount = SE.getUnsignedRange(MaxTrips).getUnsignedMax(); + if (!checkFPInfinity(MaxCount.getZExtValue() + 1, InitialVal)) + continue; + + // Thirdly: replace result of FAdd to Infinity. + replaceResultToInf(LatchInst, L); + Changed = true; + } + } + } + return Changed; +} + +PreservedAnalyses CJLoopFloatOpt::run(Function &F, + FunctionAnalysisManager &FAM) const { + LoopInfo &LI = FAM.getResult(F); + ScalarEvolution &SE = FAM.getResult(F); + PostDominatorTree &PDT = FAM.getResult(F); + + bool Changed = false; + Changed |= loopFPComputationOpt(F, LI, SE); + Changed |= loopFPInfinityOpt(F, LI, PDT, SE); + if (Changed) + return PreservedAnalyses::none(); + + return PreservedAnalyses::all(); +} + +namespace { +class CJLoopFloatOptLegacyPass : public FunctionPass { +public: + static char ID; + + explicit CJLoopFloatOptLegacyPass() : FunctionPass(ID) { + initializeCJLoopFloatOptLegacyPassPass(*PassRegistry::getPassRegistry()); + } + ~CJLoopFloatOptLegacyPass() = default; + + bool runOnFunction(Function &F) override { + auto &LI = getAnalysis().getLoopInfo(); + auto &SE = getAnalysis().getSE(); + auto &PDT = getAnalysis().getPostDomTree(); + + bool Changed = false; + Changed |= loopFPComputationOpt(F, LI, SE); + Changed |= loopFPInfinityOpt(F, LI, PDT, SE); + return Changed; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addPreserved(); + } +}; +} // namespace + +char CJLoopFloatOptLegacyPass::ID = 0; + +FunctionPass *llvm::createCJLoopFloatOptLegacyPass() { + return new CJLoopFloatOptLegacyPass(); +} + +INITIALIZE_PASS_BEGIN(CJLoopFloatOptLegacyPass, "cj-loop-float-opt", + "Cangjie Loop Float Optimize", false, false) +INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass) +INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass) +INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) +INITIALIZE_PASS_END(CJLoopFloatOptLegacyPass, "cj-loop-float-opt", + "Cangjie Loop Float Optimize", false, false) diff --git a/llvm/lib/Transforms/Scalar/CJRSSCE.cpp b/llvm/lib/Transforms/Scalar/CJRSSCE.cpp new file mode 100644 index 000000000..b83e440ed --- /dev/null +++ b/llvm/lib/Transforms/Scalar/CJRSSCE.cpp @@ -0,0 +1,1338 @@ +//===- CJRSSCE.cpp ----------------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// In Cangjie, struct is value semantics, so a copy is required each time it is +// passed. The CJRSSCE pass eliminate redundant stack struct copy through memory +// define range analysis and alias analysis. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/CJRSSCE.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/MemorySSA.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GetElementPtrTypeIterator.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/InstVisitor.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/PassManager.h" +#include "llvm/IR/SafepointIRVerifier.h" +#include "llvm/IR/Value.h" +#include "llvm/Pass.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Transforms/Scalar.h" +#include +#include +#include + +#define DEBUG_TYPE "cj-rssce" + +using namespace llvm; +static cl::opt EnableCJRSSCE("enable-cj-rssce", cl::Hidden, + cl::init(true)); + +using Interval = std::pair; + +static std::pair +getBasePtrAndOffset(const DataLayout &DL, Value *V, Instruction *I) { + // For example: + // %16 = llvm.cj.gcread.ref(%cmp, %15) + // store i8 addrspace(1)* %16, %17 + // The Ptr now is %16, and we need to find cmp to check if cmp is same as + // BasePtr. + V = V->stripPointerCasts(); + if (isa(I)) { + if (auto *II = dyn_cast(V)) { + if (II->getIntrinsicID() == Intrinsic::cj_gcread_ref) + V = II->getArgOperand(1)->stripPointerCasts(); + else if (II->getIntrinsicID() == Intrinsic::cj_gcread_static_ref) + V = II->getArgOperand(0)->stripPointerCasts(); + } else if (auto *LI = dyn_cast(V)) { + V = LI->getPointerOperand()->stripPointerCasts(); + } + } + // V is not a memory object. + if (!isa(V->getType())) + return {nullptr, 0}; + constexpr unsigned BitWidth = 64; + constexpr uint64_t OffsetRawValue = 0; + APInt Offset(BitWidth, OffsetRawValue); + Value *BPtr = V->stripAndAccumulateConstantOffsets(DL, Offset, true); + return {BPtr, Offset.getZExtValue()}; +} + +static std::tuple +getSrcDstPtrAndSize(const DataLayout &DL, Value *V) { + Value *Src = nullptr; + Value *Dst = nullptr; + uint64_t Size = 0; + // Since base loc is an object on the stack, only store or memcpy for it is + // considered. + if (auto *SI = dyn_cast(V)) { + Dst = SI->getPointerOperand(); + Src = SI->getValueOperand(); + constexpr int Length = 8; + Size = DL.getTypeSizeInBits(SI->getValueOperand()->getType()) / Length; + } else if (auto *MCI = dyn_cast(V)) { + Dst = MCI->getDest(); + Src = MCI->getSource(); + auto *ConstSZ = dyn_cast(MCI->getArgOperand(2)); + // We've got an uncertain clobber instruction. + if (!ConstSZ) + return {nullptr, nullptr, 0}; + Size = ConstSZ->getZExtValue(); + } else { + return {nullptr, nullptr, 0}; + } + return {Src, Dst, Size}; +} + +static MemoryAccess *findLastDefDominateMA(MemorySSA &MSSA, BasicBlock *BB, + MemoryAccess *MA, bool LastClosure) { + auto *DefLists = MSSA.getBlockDefs(BB); + if (!DefLists) + return nullptr; + for (auto Iter = DefLists->rbegin(); Iter != DefLists->rend(); ++Iter) { + if (MSSA.dominates(&*Iter, MA)) { + if (&*Iter == MA && !LastClosure) + continue; + return &const_cast(*Iter); + } + } + return nullptr; +} + +bool llvm::hasMemoryDefBetween(MemorySSA &MSSA, DominatorTree &DT, + const DataLayout &DL, Value *UnderObj, + Instruction *FirstI, MemoryAccess *LastMA, + bool LastClosure, bool EnableDiffBB) { + assert(isa(LastMA)); + Instruction *LastI = cast(LastMA)->getMemoryInst(); + if (FirstI->getParent() == LastI->getParent() && !FirstI->comesBefore(LastI)) + return false; + if (!EnableDiffBB && FirstI->getParent() != LastI->getParent()) + return true; + MemoryAccess *FirstDef = MSSA.getMemoryAccess(FirstI); + // Find last MemoryDef that dominates LastMA. + MemoryAccess *LastDef = + findLastDefDominateMA(MSSA, LastI->getParent(), LastMA, LastClosure); + assert((LastDef || EnableDiffBB) && "Cannot be false at the same time"); + if (!LastDef) // Now, LastDef is MemoryUse + LastDef = cast(LastMA)->getDefiningAccess(); + SmallVector UnderObjs; + getUnderlyingObjects(UnderObj, UnderObjs); + for (auto *V : UnderObjs) { + auto *ST = + dyn_cast(V->getType()->getNonOpaquePointerElementType()); + // Loc may be load inst, and the clobber we find may not accurate. + MemoryLocation Loc = + ST ? MemoryLocation(V, LocationSize::precise( + DL.getStructLayout(ST)->getSizeInBytes())) + : MemoryLocation::getAfter(V); + // MSSA uses SimpleCaptureInfo by default. To make the result more accurate, + // consider using BatchAA for more analysis of MA. + MemoryAccess *MA = + MSSA.getWalker()->getClobberingMemoryAccess(LastDef, Loc); + // if FirstDef is equal to LastDef, we known FirstDef does not clobber + // source.This is based on the premise that we've aligned it by the offset + // of source and dest. + if (MA == LastDef && LastDef != FirstDef) + return true; + if (MA == FirstDef) + continue; + // If the MA is MemoryPhi, it is impossible for the MA to satisfy + // FirstI->MA->LastI because FirstI and LastI are in the same BB. + if (isa(MA) || MSSA.isLiveOnEntryDef(MA)) + continue; + assert((!EnableDiffBB || !isa(MA)) && // Optimize it in future + "EnableDiffBB support only single predecessor"); + Instruction *DefI = cast(MA)->getMemoryInst(); + assert( + DefI != FirstI && + "FirstI is last clobber inst of memcpy dest, therefore, def of source " + "cannot be equal to FirstI"); + if (DT.dominates(FirstI, DefI) && MSSA.dominates(MA, LastMA)) + return true; + } + return false; +} + +// This optimization achieves an effect similar to MemCpyOpt. +// copy(a <- b) +// call func(a) +// If func is readonly for a, a can be safely replaced with b. "copy" consists +// of multiple define instructions with the same dest and source, including +// store and memcpy. +struct SourcePropagationState { + AliasAnalysis &AA; + EarliestEscapeInfo EI; + BatchAAResults BatchAA; + MemorySSA &MSSA; + + DominatorTree &DT; + LoopInfo &LI; + const DataLayout &DL; + + SmallPtrSet EphValues; + + struct IVInfo { + Interval Src; + Interval Dst; + Instruction *DefI; + GetElementPtrInst *GEP = nullptr; + }; + + SourcePropagationState(const DataLayout &DL, AliasAnalysis &AA, + MemorySSA &MSSA, DominatorTree &DT, LoopInfo &LI) + : AA(AA), EI(DT, LI, EphValues), BatchAA(AA, &EI), MSSA(MSSA), DT(DT), + LI(LI), DL(DL) {} + + void filterCallArgs(CallBase *CB, SetVector &CallIndexs) { + for (unsigned I = 0; I < CB->getNumOperands(); ++I) { + auto *AI = dyn_cast(CB->getOperand(I)); + if (AI == nullptr) + continue; + if (!AI->getAllocatedType()->isStructTy()) + continue; + CallIndexs.insert(I); + } + } + + // When instructions such as load are encountered, continue to search upwards. + bool isBaseAllocaValue(Value *V, SetVector &AllocaBase, + SetVector &Cache) { + if (Cache.contains(V)) + return true; + Cache.insert(V); + if (isa(V)) + return false; + if (isa(V)) + return true; + if (auto *GEP = dyn_cast(V)) + return isBaseAllocaValue(GEP->getPointerOperand()->stripPointerCasts(), + AllocaBase, Cache); + if (auto *LI = dyn_cast(V)) { + // Check whether LI's pointer operand is a stack value, so Base has to be + // on a stack to be possible. + if (auto *AI = dyn_cast( + getUnderlyingObject(LI->getPointerOperand()))) { + AllocaBase.insert(AI); + return true; + } + return false; + } + if (auto *SI = dyn_cast(V)) + return isBaseAllocaValue(SI->getTrueValue()->stripPointerCasts(), + AllocaBase, Cache) && + isBaseAllocaValue(SI->getFalseValue()->stripPointerCasts(), + AllocaBase, Cache); + if (auto *PHI = dyn_cast(V)) { + bool IsAlloca = true; + for (auto &Value : PHI->incoming_values()) { + auto *I = dyn_cast(&Value); + if (I == nullptr) + return false; + IsAlloca &= + isBaseAllocaValue(I->stripPointerCasts(), AllocaBase, Cache); + } + return IsAlloca; + } + return false; + } + + // Scan uses of the src of value copy. + // Record Instructions which writes to it. + // When write it elsewhere, end directly. + bool getBaseUses(Value *Val, SetVector &BaseUses, + SetVector &BaseStoreMemcpy) { + bool IsSrcOrBarrier = false; + for (auto *Use : Val->users()) { + auto *V = dyn_cast(Use); + if (BaseUses.contains(V)) + continue; + BaseUses.insert(V); + + // We check Src, not the contents of Src. + if (isa(V)) + continue; + + if (isStoreMemcpyInst(V, Val, IsSrcOrBarrier)) { + if (IsSrcOrBarrier) + return false; + BaseStoreMemcpy.insert(dyn_cast(V)); + } else { + if (auto *CB = dyn_cast(V)) { + if (CB->getIntrinsicID() == Intrinsic::cj_gcread_ref || + CB->getIntrinsicID() == Intrinsic::cj_gcread_static_ref) + continue; + for (unsigned I = 0; I < CB->getNumOperands(); ++I) { + if (CB->getArgOperand(I) == Val && + (CB->getParamAttr(I, Attribute::StructRet).isValid() || + !CB->getParamAttr(I, Attribute::ReadOnly).isValid())) + return false; + } + } + } + + if (!getBaseUses(V, BaseUses, BaseStoreMemcpy)) + return false; + } + return true; + } + + bool isStoreMemcpyInst(Value *V, Value *Base, bool &IsSrcOrBarrier) { + auto *I = dyn_cast(V); + if (I == nullptr) + return false; + if (auto *SI = dyn_cast(I)) { + if (SI->getValueOperand() == Base) + IsSrcOrBarrier = true; + return true; + } + if (auto *CB = dyn_cast(I)) { + if (CB->getCalledFunction() == nullptr) + return false; + Intrinsic::ID IID = CB->getIntrinsicID(); + if (isCJMemcpyIntrinsic(IID)) + return true; + if (isCJWriteBarrierIntrinsic(IID) || isCJAtomicIntrinsic(IID)) { + IsSrcOrBarrier = true; + return true; + } + } + return false; + } + + bool isKlassGlobal(GlobalVariable *GV) { + return GV->getType()->isPointerTy() && + GV->getType()->getNonOpaquePointerElementType()->isStructTy() && + GV->getType() + ->getNonOpaquePointerElementType() + ->getStructName() + .isCangjieTypeInfo(); + } + + bool isStackValueSrc(SetVector &BaseStoreMemcpy) { + for (auto *I : BaseStoreMemcpy) { + Value *Src = nullptr; + if (auto *SI = dyn_cast(I)) + Src = SI->getValueOperand(); + else + Src = dyn_cast(I)->getArgOperand(1); + + if (isa(Src)) + continue; + auto *GV = dyn_cast(Src->stripPointerCasts()); + if (GV != nullptr && isInt8AS0Pty(Src) && isKlassGlobal(GV)) + continue; + if (auto *AI = dyn_cast(Src->stripPointerCasts())) + if (isa(AI->getAllocatedType())) + continue; + return false; + } + return true; + } + + std::tuple, StructType *, uint64_t> + getGEPArrayIndicesAndOffset(GetElementPtrInst *GEP) { + if (!isa(GEP->getSourceElementType())) + return {{}, nullptr, 0}; + uint64_t Offset = 0; + Type *OriginTy = GEP->getSourceElementType(); + Type *LastTy = OriginTy; + SmallVector ArrayIndices; + for (gep_type_iterator GTI = ++gep_type_begin(GEP), GTE = gep_type_end(GEP); + GTI != GTE; ++GTI) { + Type *Ty = GTI.getIndexedType(); + if (auto *ATy = dyn_cast(LastTy)) { + Offset = 0; + OriginTy = ATy->getElementType(); + ArrayIndices.push_back(GTI.getOperand()); + } else if (auto *STy = GTI.getStructTypeOrNull()) { + auto *ConstIdx = dyn_cast(GTI.getOperand()); + assert(ConstIdx && "struct indices must be constant"); + auto *SL = DL.getStructLayout(STy); + Offset += SL->getElementOffset(ConstIdx->getZExtValue()); + } + LastTy = Ty; + } + if (auto *ST = dyn_cast(OriginTy)) + return {ArrayIndices, ST, Offset}; + return {{}, nullptr, 0}; + } + + // Check if Base is getelementptr inst, and if it is, save indices and find + // true base. If new indices and argument indices are different, it means they + // are from two different bases. + Value *maybeGEPBaseAndCheckWith(Value *&Base, uint64_t &BaseOff, + SmallVectorImpl &Indices) { + if (auto *GEP = dyn_cast(Base)) { + auto [ArrayIndices, ETy, Offset] = getGEPArrayIndicesAndOffset(GEP); + if (!ETy) + return nullptr; + assert(ArrayIndices.size() != 0 && + "ETy != nullptr and ArrayIndices must not be empty"); + BaseOff = Offset; + Value *Ret = Base; + Base = GEP->getPointerOperand()->stripPointerCasts(); + if (!Indices.empty()) + return Indices == ArrayIndices ? Ret : nullptr; + Indices = ArrayIndices; + return Ret; + } + return Base; + } + + bool isIntervalsCovered(Value *UnderObj, StructType *ST, + SmallVectorImpl &Intervals) { + if (Intervals.empty()) + return false; + sort(Intervals, [](const IVInfo &L, const IVInfo &R) { + return L.Src.first < R.Src.first; + }); + uint64_t SR = Intervals[0].Src.second; + uint64_t DR = Intervals[0].Dst.second; + for (unsigned I = 1; I < Intervals.size(); ++I) { + if (Intervals[I].Src.first != SR || Intervals[I].Dst.first != DR) + return false; + SR = Intervals[I].Src.second; + DR = Intervals[I].Dst.second; + } + auto FirstE = Intervals.front(); + auto LastE = Intervals.back(); + auto Size = DL.getStructLayout(ST)->getSizeInBytes(); + if (LastE.Src.second - FirstE.Src.first != Size || + LastE.Dst.second - FirstE.Dst.first != Size) + return false; + return true; + } + + bool filterValueCopySrc(Value *Src) { + SetVector AllocaBase; + SetVector Cache; + if (!isBaseAllocaValue(Src, AllocaBase, Cache)) + return false; + for (auto *AI : AllocaBase) { + SetVector BaseUses; + SetVector BaseStoreMemcpy; + if (!getBaseUses(AI, BaseUses, BaseStoreMemcpy)) + return false; + if (!isStackValueSrc(BaseStoreMemcpy)) + return false; + } + return true; + } + + Instruction *createIntervalBeginGEP(IRBuilder<> &IRB, Value *UnderObj, + IVInfo &FirstE) { + if (FirstE.GEP) + return FirstE.GEP; + Value *BC = IRB.CreateBitCast( + UnderObj, + IRB.getInt8PtrTy(UnderObj->getType()->getPointerAddressSpace())); + auto *GEP = IRB.CreateGEP( + IRB.getInt8Ty(), BC, + {ConstantInt::get(IRB.getInt32Ty(), FirstE.Src.first)}, "", true); + // We do not remove possibly redundant def instructions, because their use + // needs to be analyzed. Therefore, they are removed in subsequent passes. + return cast(GEP); + } + + Instruction *getCompleteClobbers(MemoryLocation &Loc, MemoryAccess *OriginMA, + StructType *ST, uint64_t Limits = 5) { + assert(isa(OriginMA)); + CaptureInfo *CI = &EI; + auto *StartDef = dyn_cast( + cast(OriginMA)->getDefiningAccess()); + if (!StartDef || !StartDef->getMemoryInst()) + return nullptr; + auto *MemI = StartDef->getMemoryInst(); + MemoryDef *Current = dyn_cast( + MSSA.getWalker()->getClobberingMemoryAccess(StartDef, Loc)); + + SmallVector Intervals; + Value *UnderObj = nullptr; + Instruction *LastClobberI = nullptr; + SmallVector ArrayIndices; + // If 2 steps has covered, and 3th step is repeat. + bool IntervalsValid = false; + for (; Current && !MSSA.isLiveOnEntryDef(Current) && Limits--; + Current = dyn_cast(Current->getDefiningAccess())) { + Instruction *I = Current->getMemoryInst(); + if (!I || (I != MemI && !DT.dominates(I, MemI)) || I->mayThrow() || + !I->willReturn()) + break; + if (!LastClobberI) + LastClobberI = Current->getMemoryInst(); + // Get ModRefInfo between I and Loc.Ptr, we only deal with MustMod cases. + ModRefInfo MR = BatchAA.getModRefInfo(I, Loc); + if (isNoModRef(MR) || !isModSet(MR)) + continue; + + auto [Src, Dst, Size] = getSrcDstPtrAndSize(DL, I); + // Use find under object to instead of must alias analysis. And if memset + // or cj.memset is displayed (Src and Dst is nullptr), stop the analysis. + if (!Src || !Dst) + break; + if (!isMustSet(MR) && getUnderlyingObject(Dst) != Loc.Ptr) { + if (CI->isNotCapturedBeforeOrAt(Loc.Ptr, I)) + continue; + break; + } + assert((isa(I) || isa(I)) && + "Only support to deal store or memcpy"); + auto [SrcBPtr, SrcOff] = getBasePtrAndOffset(DL, Src, I); + auto [DstBPtr, DstOff] = getBasePtrAndOffset(DL, Dst, I); + if (!SrcBPtr || !DstBPtr) + break; + + // %0 = gep %base, i64 0, i32 1, i64 %231, i32 0, i32 0 + // %1 = gcread.ref(%base, %0) + // %2 = gep %dest, 0 + // store i8 addrspace(1)* %1, %dest + // %3 = gep %base, i64 0, i32 1, i64 %231, i32 0, i32 1 + // %4 = gep %dest, 8 + // memcpy(%4, %3, 24) + // Dealing with the situation. + GetElementPtrInst *GEP = nullptr; + if (auto *R = maybeGEPBaseAndCheckWith(SrcBPtr, SrcOff, ArrayIndices)) + GEP = dyn_cast(R); + else + break; + + // The source values of the loc are from different memory locations. In + // this scenario, optimization also cannot be performed. Because the AA + // result is MustAlias, DstBasePtr does not need to be verified. + if (!UnderObj) + UnderObj = SrcBPtr; + if (UnderObj != SrcBPtr) + break; + + Intervals.push_back( + {{SrcOff, SrcOff + Size}, {DstOff, DstOff + Size}, I, GEP}); + if (isIntervalsCovered(UnderObj, ST, Intervals)) { + IntervalsValid = true; + break; + } + } + if (!IntervalsValid && !isIntervalsCovered(UnderObj, ST, Intervals)) + return nullptr; + if (hasMemoryDefBetween(MSSA, DT, DL, UnderObj, LastClobberI, OriginMA)) + return nullptr; + if (!filterValueCopySrc(UnderObj)) + return nullptr; + IRBuilder<> IRB(MemI); + return createIntervalBeginGEP(IRB, UnderObj, Intervals.front()); + } + + Instruction *getClobberMemoryAccess(CallBase *CB, unsigned Index) { + MSSA.ensureOptimizedUses(); + MemoryAccess *OriginMA = MSSA.getMemoryAccess(CB); + Instruction *I = cast(CB->getArgOperand(Index)); + auto *AI = dyn_cast(I); + assert(AI != nullptr); + auto Loc = MemoryLocation( + I, LocationSize::precise(DL.getTypeAllocSize(AI->getAllocatedType()))); + StructType *ST = cast(AI->getAllocatedType()); + Instruction *NewI = getCompleteClobbers(Loc, OriginMA, ST); + return NewI; + } + + void replaceCallArg(CallBase *CB, unsigned ArgIndex, Instruction *NewI) { + IRBuilder<> Builder(cast(CB)); + auto *Arg = CB->getArgOperand(ArgIndex); + Value *CastI = NewI; + if (NewI->getType()->getPointerAddressSpace() != 0) { + CastI = Builder.CreateAddrSpaceCast( + NewI, PointerType::get( + NewI->getType()->getNonOpaquePointerElementType(), 0)); + } + Value *Cast = Builder.CreateBitCast(CastI, Arg->getType()); + CB->setArgOperand(ArgIndex, Cast); + } + + bool optimizeCallInst(CallBase *CB) { + bool Changed = false; + Function *Func = CB->getCalledFunction(); + if (Func == nullptr || !Func->hasFnAttribute(Attribute::ReadOnly)) + return Changed; + SetVector CallIndexs; + filterCallArgs(CB, CallIndexs); + for (auto Index : CallIndexs) { + if (Instruction *NewI = getClobberMemoryAccess(CB, Index)) { + replaceCallArg(CB, Index, NewI); + Changed = true; + } + } + return Changed; + } +}; + +// This optimization achieves the following copy elimination. +// copy(b, a, size) +// copy(c, a, size) +// use(c) +// When a is a GCPtr, source propagation cannot be applied. In this case, try +// replacing c with b. +// It is worth mentioning that in this optimization, "copy" consists of multiple +// define instructions, including memcpy/gcwrite.struct/store. Their dests must +// be the same, but their sources may be different. +struct DestPropagationState { + Function &F; + + AliasAnalysis &AA; + EarliestEscapeInfo EI; + BatchAAResults BatchAA; + MemorySSA &MSSA; + + DominatorTree &DT; + LoopInfo &LI; + const DataLayout &DL; + uint32_t TotalVersion = 0; + + SmallPtrSet EphValues; + + struct Copy { + Value *Dst = nullptr; + Interval IV; + Instruction *I = nullptr; + uint32_t Version = UINT32_MAX; + Copy() : Dst(nullptr), IV({0, 0}), I(nullptr) {} + Copy(Value *Dst, Interval IV, Instruction *I, uint32_t TotalVersion) + : Dst(Dst), IV(IV), I(I), Version(TotalVersion) {} + Copy(const Copy &C) : Dst(C.Dst), IV(C.IV), I(C.I), Version(C.Version) {} + }; + struct Compare { + // Be careful, LHS < RHS false + RHS < LHS false => LHS == RHS. + bool operator()(const Copy *LHS, const Copy *RHS) const { + if (LHS->Dst == RHS->Dst && LHS->IV == RHS->IV && LHS->I == RHS->I) + return false; + if (LHS->IV.first == RHS->IV.first) { + if (LHS->IV.second == RHS->IV.second) { + if (LHS->I == RHS->I) + llvm::report_fatal_error("error"); + return LHS->Version < RHS->Version; + } + return LHS->IV.second > RHS->IV.second; + } + return LHS->IV.first < RHS->IV.first; + } + }; + DenseMap>> + SourceCopys; + SmallSet MemoryLeakSet; + + struct Define { + Value *Source = nullptr; + Copy *C; + Interval IV; + }; + struct ClobberInfo { + SmallVector Defines; + // For the source on the destination, record the last define instruction + // from the source. + DenseMap Sources; + // Records the last define instruction of the destination. + Instruction *LastClobberI = nullptr; + }; + // 1: Records all defined information after combination. + DenseMap> Clobbers; + // size_t: index of ClobberInfo.Defines. + DenseMap Successors; // Single predecessor successors + + DestPropagationState(Function &F, AliasAnalysis &AA, MemorySSA &MSSA, + DominatorTree &DT, LoopInfo &LI) + : F(F), AA(AA), EI(DT, LI, EphValues), BatchAA(AA, &EI), MSSA(MSSA), + DT(DT), LI(LI), DL(F.getParent()->getDataLayout()) {} + +#ifndef NDEBUG + void insertNewMem(Copy *C) { MemoryLeakSet.insert(C); } + + void eraseReleaseMem(Copy *C) { + assert(MemoryLeakSet.count(C)); + MemoryLeakSet.erase(C); + } + + void checkMemLeak() { + if (!MemoryLeakSet.empty()) { + LLVM_DEBUG(dbgs() << F.getName() << ", memory leakage occurs" + << "\n"); + for (auto *C : MemoryLeakSet) + LLVM_DEBUG(dbgs() << C << "\n"); + } + } +#endif + + // Check whether I is store, memcpy, memmove, or gcread.struct, and check + // whether define source is an on-stack struct. + bool isStackSTDefine(Instruction *I) { + Value *Ptr = nullptr; + if (auto *SI = dyn_cast(I)) + Ptr = getUnderlyingObject(SI->getPointerOperand()); + else if (auto *MCI = dyn_cast(I)) + Ptr = getUnderlyingObject(MCI->getDest()); + else if (auto *MMI = dyn_cast(I)) + Ptr = getUnderlyingObject(MMI->getDest()); + else if (auto *II = dyn_cast(I); + II && (II->getIntrinsicID() == Intrinsic::cj_gcread_struct || + II->getIntrinsicID() == Intrinsic::cj_gcread_static_struct)) + Ptr = getUnderlyingObject(II->getOperand(0)); + if (auto *AI = dyn_cast_or_null(Ptr)) + return isa(AI->getAllocatedType()); + return false; + } + + bool isMemoryInstruction(Instruction *I) { + if (isa(I) || isa(I) || isa(I)) + return true; + if (auto *II = dyn_cast(I); + II && (II->isCJRefGCRead() || II->isCJRefGCWrite() || + II->isCJStructGCRead() || II->isCJStructGCWrite() || + II->getIntrinsicID() == Intrinsic::cj_memset)) + return true; + return false; + } + + void shrinkLeft(Define &Def, int64_t Diff, BasicBlock *BB) { + Def.IV.first += Diff; + SourceCopys[BB][Def.Source].erase(Def.C); + Def.C->IV.first += Diff; + SourceCopys[BB][Def.Source].insert(Def.C); + } + + void shrinkRight(Define &Def, int64_t Diff, BasicBlock *BB) { + Def.IV.second -= Diff; + SourceCopys[BB][Def.Source].erase(Def.C); + Def.C->IV.second -= Diff; + SourceCopys[BB][Def.Source].insert(Def.C); + } + + std::pair splitTwo(Define &Def, Interval IV, BasicBlock *BB) { + if (IV.first == Def.IV.first || IV.second == Def.IV.second) + llvm::report_fatal_error("error"); + Interval OldDIV = Def.IV; + Interval OldSIV = Def.C->IV; + shrinkRight(Def, Def.IV.second - IV.first, BB); + Copy *C = new Copy; +#ifndef NDEBUG + insertNewMem(C); +#endif + C->Dst = Def.C->Dst; + C->IV = {OldSIV.first + IV.second - OldDIV.first, OldSIV.second}; + C->I = Def.C->I; + C->Version = Def.C->Version; + SourceCopys[BB][Def.Source].insert(C); + return {Def, {Def.Source, C, {IV.second, OldDIV.second}}}; + } + + // true: RD is push_back to Splited. + // Discard: release RD.C + // In addition, when the function returns, LD is always not needed. + bool trySplit(Define &LD, Define &RD, SmallVectorImpl &Splited, + Instruction *I, bool &Discard) { + // case0: No split needed. + if (!(LD.IV.second >= RD.IV.first && LD.IV.first <= RD.IV.second)) { + if (LD.IV.first < RD.IV.first) { + Splited.push_back(LD); + Splited.push_back(RD); + } else { + Splited.push_back(RD); + Splited.push_back(LD); + } + return true; + } + auto *BB = I->getParent(); + // case1: LD is full covered by RD, just delete from SourceCopys. + if (LD.IV.first >= RD.IV.first && LD.IV.second <= RD.IV.second) { + SourceCopys[BB][LD.Source].erase(LD.C); + delete LD.C; +#ifndef NDEBUG + eraseReleaseMem(LD.C); +#endif + return false; + } + // Quick check whether LD.Source is still valid. + if (LD.Source != RD.Source && // This case has been checked. + hasMemoryDefBetween(MSSA, DT, DL, LD.Source, LD.C->I, + MSSA.getMemoryAccess(I), true, true)) { + SourceCopys[BB][LD.Source].erase(LD.C); + delete LD.C; +#ifndef NDEBUG + eraseReleaseMem(LD.C); +#endif + return false; + } + // case2: RD is full covered by LD. + if (LD.IV.first < RD.IV.first && LD.IV.second > RD.IV.second) { + // Sources are different, therefore, LD should be splited or LD and RD are + // from same source but mappings are different, LD also need to be + // splited. + if (LD.Source != RD.Source || + LD.C->IV.first - LD.IV.first != RD.C->IV.first - RD.IV.first) { + auto [Def1, Def2] = splitTwo(LD, {RD.IV.first, RD.IV.second}, BB); + Splited.push_back(Def1); + Splited.push_back(RD); + Splited.push_back(Def2); + return true; + } + // Both source and mapping are same, just keep LD and discard RD. + Splited.push_back(LD); + Discard = true; // RD is no needed. + return true; + } + // case3: LD is left covered by RD. + if (LD.IV.second > RD.IV.second) { + // Sources are different or mappings are different. + if (LD.Source != RD.Source || + LD.C->IV.first - LD.IV.first != RD.C->IV.first - RD.IV.first) { + Splited.push_back(RD); + shrinkLeft(LD, RD.IV.second - LD.IV.first, BB); + Splited.push_back(LD); + return true; + } + // Merge LD and RD. + shrinkLeft(LD, -(LD.IV.first - RD.IV.first), BB); + Splited.push_back(LD); + Discard = true; // RD is no needed. + return true; + } + // case4: LD is right covered by RD. + if (LD.IV.first < RD.IV.first) { + if (LD.Source != RD.Source || + LD.C->IV.first - LD.IV.first != RD.C->IV.first - RD.IV.first) { + shrinkRight(LD, LD.IV.second - RD.IV.first, BB); + Splited.push_back(LD); + return false; + } + shrinkLeft(RD, -(RD.IV.first - LD.IV.first), BB); + SourceCopys[BB][LD.Source].erase(LD.C); // LD is always in SourceCopys + delete LD.C; +#ifndef NDEBUG + eraseReleaseMem(LD.C); +#endif + return false; + } + llvm::report_fatal_error("unreachable"); + return false; + } + + // This function attempts to insert a new interval into an existing interval + // set, and splits the existing interval or merges the new interval if + // necessary. This usually happens in the following situations. + // + // case1: + // L1-----R1(S1) + // L2-----R2(S2) + // NL-----------NR(S3) + // Now, intervals are [L1, NL](S1), [NL, NR](S3), [NR, R2](S2). + // + // case2: + // L1-----R1(S1) + // NL-----NR(S1) + // Now, new interval is [L1, NR](S1). If one or more sources in case1 are the + // same, merged in case2 is triggered. + // + // case3: + // L1-----R1(S1) from SL1-----SR1 + // NL-----NR(S1) from SL2-----SR2 + // SL2 - NL is not equal to SL1 - L1 + // Note that the intervals do not merge in this case, because this results in + // S1 not being continuous but acting on consecutive dests. + // + // case4: + // L1-----------R1(S1) + // NL---NR(S2) + // Now, new intervals are [L1, NL](S1), [NL, NR](S2), [NR, R1](S1). The old + // interval was truncated, similar to case1. + // + // return true indicates RD.C should be released. + bool intervalSplitOrMerge(SmallVectorImpl &Defines, const Define &New, + Instruction *Inst) { + SmallVector NewDefines; + bool Placed = false; + size_t J = Defines.size(); + bool Discard = false; + for (size_t I = 0; I < Defines.size(); ++I) { + if (Defines[I].IV.second < New.IV.first) { + NewDefines.push_back(Defines[I]); + continue; + } + if (Defines[I].IV.first > New.IV.second) { + if (!Placed) { + NewDefines.push_back(New); + SourceCopys[Inst->getParent()][New.Source].insert(New.C); + Placed = true; + } + NewDefines.push_back(Defines[I]); + continue; + } + J = I; + break; + } + for (; J < Defines.size(); ++J) { + if (!Placed) { + if (trySplit(Defines[J], const_cast(New), NewDefines, Inst, + Discard)) + Placed = true; + continue; + } + NewDefines.push_back(Defines[J]); + } + if (!Placed) { + NewDefines.push_back(New); + SourceCopys[Inst->getParent()][New.Source].insert(New.C); + } + Defines = NewDefines; + return Discard; + } + + void invalidClobber(Value *Dest, DenseMap &Clobbers, + DenseMap> &Copies) { + for (auto &Def : Clobbers[Dest].Defines) { + if (!Copies.count(Def.Source)) + llvm::report_fatal_error("something error between Clobber and Copies"); + Copies[Def.Source].erase(Def.C); + delete Def.C; +#ifndef NDEBUG + eraseReleaseMem(Def.C); +#endif + } + Clobbers.erase(Dest); + } + + void invalidSource(Value *Source, + DenseMap> &Copies, + DenseMap &Clobbers) { + assert(Copies.count(Source)); + for (auto *C : Copies[Source]) { + if (!Clobbers.count(C->Dst)) { + delete C; +#ifndef NDEBUG + eraseReleaseMem(C); +#endif + continue; + } + SmallVector NewDefines; + for (auto &Def : Clobbers[C->Dst].Defines) { + if (Def.Source == Source) + continue; + NewDefines.push_back(Def); + } + Clobbers[C->Dst].Sources.erase(Source); + if (NewDefines.empty()) + Clobbers.erase(C->Dst); + else + Clobbers[C->Dst].Defines = NewDefines; + delete C; +#ifndef NDEBUG + eraseReleaseMem(C); +#endif + } + Copies.erase(Source); + } + + // This function try to insert and merge a new define. For a new define + // instruction, find the copy interval of source and dest, and try to merge + // the old define interval of dest. There may be two defined from different + // sources, or the two defined have memory intersections. In this case, choose + // to split the previous define. + bool tryToInsertNewDefine(Instruction *I) { + // Find base pointer of src and dst, and get define size. + auto [Src, Dst, Size] = getSrcDstPtrAndSize(DL, I); + if (!Src || !Dst) + return false; + auto [SrcBPtr, SrcOff] = getBasePtrAndOffset(DL, Src, I); + auto [DstBPtr, DstOff] = getBasePtrAndOffset(DL, Dst, I); + if (!SrcBPtr || !DstBPtr) + return false; + auto *BB = I->getParent(); + auto &ClobberB = Clobbers[BB][DstBPtr]; + if (ClobberB.Sources.count(SrcBPtr) && + hasMemoryDefBetween(MSSA, DT, DL, SrcBPtr, ClobberB.Sources[SrcBPtr], + MSSA.getMemoryAccess(I))) + // SrcBPtr is clobbered, so all Src copy destinations need to be + // invalidated. + invalidSource(SrcBPtr, SourceCopys[BB], Clobbers[BB]); + if (ClobberB.LastClobberI && + hasMemoryDefBetween(MSSA, DT, DL, DstBPtr, ClobberB.LastClobberI, + MSSA.getMemoryAccess(I), false)) + // DstBPtr is clobbered. Therefore, all clobber states of Dst need to be + // cleared. + invalidClobber(DstBPtr, Clobbers[BB], SourceCopys[BB]); + auto &ClobberA = Clobbers[BB][DstBPtr]; // ClobberB may be invalid, rebind. + Copy *C = new Copy(DstBPtr, {SrcOff, SrcOff + Size}, I, TotalVersion++); +#ifndef NDEBUG + insertNewMem(C); +#endif + if (!intervalSplitOrMerge(ClobberA.Defines, + {SrcBPtr, C, {DstOff, DstOff + Size}}, I)) { + SourceCopys[BB][SrcBPtr].insert(C); + } else { + if (SourceCopys[BB][SrcBPtr].count(C)) // May be inserted when merge. + SourceCopys[BB][SrcBPtr].erase(C); + // C is discarded, release it. + delete C; +#ifndef NDEBUG + eraseReleaseMem(C); +#endif + } + ClobberA.Sources[SrcBPtr] = I; // Update Source last define. + ClobberA.LastClobberI = I; // Update dest last define. + return true; + } + + bool rewriteCallArg(CallBase *CB, unsigned ArgNo, Value *Base, + unsigned Offset) { + // Current implementation may create many redundant GEP, which need to be + // deleted by SimplifyCFG or InstCombine later. + Value *Ptr = nullptr; + IRBuilder<> IRB(CB); + if (Offset == 0) { // Just bitcast + Ptr = IRB.CreatePointerBitCastOrAddrSpaceCast( + Base, CB->getArgOperand(ArgNo)->getType()); + } else { + auto *BC = IRB.CreateBitCast( + Base, IRB.getInt8PtrTy(Base->getType()->getPointerAddressSpace())); + auto *GEP = + IRB.CreateGEP(IRB.getInt8Ty(), BC, + {ConstantInt::get(IRB.getInt32Ty(), Offset)}, "", true); + Ptr = IRB.CreatePointerBitCastOrAddrSpaceCast( + GEP, CB->getArgOperand(ArgNo)->getType()); + } + assert(Ptr != nullptr); + CB->setArgOperand(ArgNo, Ptr); + return true; + } + + SmallVector::iterator + findSuitableDefine(BasicBlock *BB, Value *Dst, uint64_t Offset) { + if (!Clobbers[BB].count(Dst)) + return nullptr; + SmallVector::iterator DI = nullptr; + for (auto I = Clobbers[BB][Dst].Defines.begin(), + E = Clobbers[BB][Dst].Defines.end(); + I != E; ++I) { + // Find suitable define. + if (I->IV.first <= Offset && I->IV.second >= Offset) { + DI = I; + break; + } + } + return DI; + } + + MemoryAccess *findLastAccessDominateI(BasicBlock *BB, Instruction *I) { + if (auto *MA = MSSA.getMemoryAccess(I)) + return MA; + auto *AccessLists = MSSA.getBlockAccesses(BB); + if (!AccessLists) + return nullptr; + for (auto AI = AccessLists->rbegin(), AE = AccessLists->rend(); AI != AE; + ++AI) { + if (auto *MUD = dyn_cast(&*AI); + MUD && DT.dominates(MUD->getMemoryInst(), I)) + return const_cast(&*AI); + } + return nullptr; + } + + std::pair findFullEquivalentValue(Value *Ptr, uint64_t Size, + BasicBlock *BB, + Instruction *Use) { + APInt VOff(64, 0); + Value *V = Ptr->stripAndAccumulateConstantOffsets(DL, VOff, true); + auto ContainCheck = [&](SmallVector::iterator IT) -> int { + uint64_t LastPos = IT->IV.second; + if (LastPos >= VOff.getZExtValue() + Size) + return 1; + for (auto I = IT + 1, E = Clobbers[BB][V].Defines.end(); I != E; ++I) { + if (I->IV.first != LastPos) + return 0; + LastPos = I->IV.second; + if (LastPos >= VOff.getZExtValue() + Size) + return std::distance(IT, I) + 1; + } + return 0; + }; + auto FindCopyBelongDefine = [](ClobberInfo &CI, Copy *C) -> Define * { + for (auto &D : CI.Defines) + if (D.C == C) + return &D; + return nullptr; + }; + // Find the definition of V that contains VOff. + auto DI = findSuitableDefine(BB, V, VOff.getZExtValue()); + if (!DI) + return {nullptr, 0}; + // Ensure that all consecutive memories between [VOff, VOff + Size] have + // corresponding define. + int DefCount = ContainCheck(DI); + if (!DefCount) + return {nullptr, 0}; + assert(Clobbers[BB].count(V)); + assert(SourceCopys[BB].count(DI->Source) && "something error"); + + struct RAIIInvalidClobbers { + SmallVector InvalidClobbers; + DestPropagationState *State; + DenseMap &Clobbers; + DenseMap> &Copies; + RAIIInvalidClobbers( + DestPropagationState *State, DenseMap &Clobbers, + DenseMap> &Copies) + : State(State), Clobbers(Clobbers), Copies(Copies) {} + ~RAIIInvalidClobbers() { + for (auto *V : InvalidClobbers) + State->invalidClobber(V, Clobbers, Copies); + } + } RAIIInvalidClobbers(this, this->Clobbers[BB], this->SourceCopys[BB]); + + // Check where the source is copied. For each copy destination(d0), if + // the copy range can cover the required range and neither d0 nor source + // has clobber instructions between the last copy and current use, then + // it is safe to replace it with the last copy. + auto *MA = findLastAccessDominateI(BB, Use); + if (MA && hasMemoryDefBetween(MSSA, DT, DL, V, Clobbers[BB][V].LastClobberI, + MA, false, true)) { + RAIIInvalidClobbers.InvalidClobbers.push_back(V); + return {nullptr, 0}; + } + for (auto *C : SourceCopys[BB][DI->Source]) { + if (C == DI->C) // Try to use the previous equivalent Value. + break; + auto &CandiClob = Clobbers[BB][C->Dst]; + // Replace C->I with CandiClob(d0).LastClobberI is safe and can be + // acceleration because it is confirmed that d0 has not clobbered + // before d0.LastClobberI when a new define is added to d0. If it is, + // the state of d0 will be cleared and d0 is not found through source. + if (MA && hasMemoryDefBetween( // MA == nullptr, must no clobber. + MSSA, DT, DL, C->Dst, // It can be accelerated here. + CandiClob.LastClobberI, MA, true, true)) { + RAIIInvalidClobbers.InvalidClobbers.push_back(C->Dst); + continue; + } + auto *CandiDI = FindCopyBelongDefine(CandiClob, C); + assert(CandiDI != nullptr); + int I = 0; + for (; I < DefCount; ++I) { + auto *CandiDef = + CandiDI + I == CandiClob.Defines.end() ? nullptr : CandiDI + I; + if (!CandiDef) + break; + auto *Def = DI + I; + // I == 0, Copy left can be LE, otherwise, left must be equal. + // I == DefCount - 1, Copy right can be GE, otherwise, right must be + // equal. + bool LeftValid = + (I == 0 && CandiDef->C->IV.first <= Def->C->IV.first) || + CandiDef->C->IV.first == Def->C->IV.first; + bool RightValid = (I == DefCount - 1 && + CandiDef->C->IV.second >= Def->C->IV.second) || + CandiDef->C->IV.second == Def->C->IV.second; + // For the same reason, it has been proven safe to replace C->I with + // d0.Sources[source] when adding a new define from source to d0. + auto *SMA = MSSA.getMemoryAccess(Clobbers[BB][V].Sources[Def->Source]); + assert(SMA && "Must has memory access"); + if (LeftValid && RightValid && CandiDef->Source == Def->Source && + // Be sure that Source has not been modified between the last use of + // CandiClob and CurrentClob. + !hasMemoryDefBetween(MSSA, DT, DL, Def->Source, + CandiClob.Sources[Def->Source], SMA, true, + true)) { + continue; + } + break; + } + // All defines of [VOff, VOff + Size] in V have the equivalent define in + // CandiClob. + if (I == DefCount) { + // L0----------R0 (candidate first define interval) + // L1---------------R1 (candidate first copy interval) + // L2--------R2 (V first copy interval) + // L3---R3 (V first define interval) + // ^ (P: Ptr offset based on V) + // The final offset for replacing Ptr with candi is + // L0 + L2 - L1 + P - L3. + return {C->Dst, CandiDI->IV.first + DI->C->IV.first - C->IV.first + + VOff.getZExtValue() - DI->IV.first}; + } + } + return {nullptr, 0}; + } + + bool tryToReplaceCallArgs(CallBase *CB) { + bool Changed = false; + BasicBlock *BB = CB->getParent(); + bool FnReadOnly = CB->hasFnAttr(Attribute::ReadOnly) || + CB->hasFnAttr(Attribute::ReadNone); + for (auto &Arg : CB->args()) { + bool ArgReadOnly = + CB->paramHasAttr(Arg.getOperandNo(), Attribute::ReadOnly) || + CB->paramHasAttr(Arg.getOperandNo(), Attribute::ReadNone); + bool ArgNoCaptured = + CB->paramHasAttr(Arg.getOperandNo(), Attribute::NoCapture); + // If readonly is false, argument may be modified and cannot be replaced + // with another value. If nocaptured is false, the pointer information may + // be out of scope and cannot be replaced. + if (!(ArgReadOnly || FnReadOnly) || !ArgNoCaptured || + !Arg->getType()->isPointerTy()) + continue; + Type *Ty = + Arg->stripPointerCasts()->getType()->getNonOpaquePointerElementType(); + uint64_t Size = Ty->isFunctionTy() ? 8 : DL.getTypeAllocSize(Ty); + auto [EV, Offset] = findFullEquivalentValue(Arg, Size, BB, CB); + if (EV) + Changed |= rewriteCallArg(CB, Arg.getOperandNo(), EV, Offset); + } + return Changed; + } + + void copyBBState(BasicBlock *PredBB, BasicBlock *BB) { + Clobbers[BB] = Clobbers[PredBB]; + for (auto &[Dst, CI] : Clobbers[BB]) { + for (auto &Def : CI.Defines) { + Copy *NewC = new Copy(*Def.C); +#ifndef NDEBUG + insertNewMem(NewC); +#endif + Def.C = NewC; + SourceCopys[BB][Def.Source].insert(NewC); + } + } + } + void releaseBBState(BasicBlock *BB) { + for (auto &[Src, Copies] : SourceCopys[BB]) { + for (auto *C : Copies) { + delete C; +#ifndef NDEBUG + eraseReleaseMem(C); +#endif + } + } + SourceCopys.erase(BB); + Clobbers.erase(BB); + } + + bool optimizeRSSCInBB(BasicBlock &BB) { + SourceCopys[&BB].clear(); + Clobbers[&BB].clear(); + // BB has single predecessor, okay, we can use PredBB state to initialize BB + // state, because all values in PredBB are valid in the BB. + if (auto *PredBB = BB.getSinglePredecessor()) { + copyBBState(PredBB, &BB); + // All successors has been visited, release memory. + if (--Successors[PredBB] == 0) { + Successors.erase(PredBB); + releaseBBState(PredBB); + } + } + bool Changed = false; + for (Instruction &I : BB) { + if (isStackSTDefine(&I)) { + assert(isa(I) || isa(I) || isa(I) || + isa(I)); + tryToInsertNewDefine(&I); + continue; + } + if (isMemoryInstruction(&I)) // No need to optimize these instruction + continue; + if (auto *CB = dyn_cast(&I)) + Changed |= tryToReplaceCallArgs(CB); + } + // Calculate the number of successor BBs which contain single predecessor. + // If empty, release BB state. + auto *TI = BB.getTerminator(); + for (unsigned N = 0; N < TI->getNumSuccessors(); ++N) { + if (TI->getSuccessor(N)->getSinglePredecessor()) + ++Successors[&BB]; + } + if (!Successors.count(&BB)) + releaseBBState(&BB); + return Changed; + } +}; + +static bool destPropagationImpl(Function &F, AliasAnalysis &AA, MemorySSA &MSSA, + DominatorTree &DT, LoopInfo &LI) { + DestPropagationState State(F, AA, MSSA, DT, LI); + bool Changed = false; + SmallSet Visited; + SmallVector Worklist = {&F.getEntryBlock()}; + Visited.insert(&F.getEntryBlock()); + while (!Worklist.empty()) { + auto *BB = Worklist.pop_back_val(); + Changed |= State.optimizeRSSCInBB(*BB); + for (auto *Succ : successors(BB)) { + if (Visited.count(Succ)) + continue; + Visited.insert(Succ); + Worklist.push_back(Succ); + } + } +#ifndef NDEBUG + State.checkMemLeak(); +#endif + + return Changed; +} + +static bool sourcePropagationImpl(Function &F, AliasAnalysis &AA, + MemorySSA &MSSA, DominatorTree &DT, + LoopInfo &LI) { + SourcePropagationState State(F.getParent()->getDataLayout(), AA, MSSA, DT, + LI); + bool Changed = false; + for (auto I = inst_begin(F), E = inst_end(F); I != E;) { + auto *CB = dyn_cast(&*I++); + if (!CB) + continue; + Changed |= State.optimizeCallInst(CB); + } + return Changed; +} + +static bool implCJRSSCE(Function &F, AliasAnalysis &AA, MemorySSA &MSSA, + DominatorTree &DT, LoopInfo &LI) { + if (!EnableCJRSSCE) + return false; + bool Changed = sourcePropagationImpl(F, AA, MSSA, DT, LI); + // MSSA is not updated, but this does not affect the correctness and + // optimization effect. + Changed |= destPropagationImpl(F, AA, MSSA, DT, LI); + return Changed; +} + +PreservedAnalyses CJRSSCEPass::run(Function &F, FunctionAnalysisManager &AM) { + auto &AA = AM.getResult(F); + auto &MSSA = AM.getResult(F).getMSSA(); + auto &DT = AM.getResult(F); + auto &LI = AM.getResult(F); + if (implCJRSSCE(F, AA, MSSA, DT, LI)) + return PreservedAnalyses::none(); + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Transforms/Scalar/CJRewriteStatepoint.cpp b/llvm/lib/Transforms/Scalar/CJRewriteStatepoint.cpp new file mode 100644 index 000000000..41e896873 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/CJRewriteStatepoint.cpp @@ -0,0 +1,3063 @@ +//===- CJRewriteStatepoint.cpp - Make Cangjie GC relocations explicit -----===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Rewrite call/invoke instructions so as to make potential relocations +// performed by the cangjie garbage collector explicit in the IR. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/CJRewriteStatepoint.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Analysis/DomTreeUpdater.h" +#include "llvm/Analysis/PostDominators.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/IR/Argument.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/CallingConv.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/MDBuilder.h" +#include "llvm/IR/Metadata.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/SafepointIRVerifier.h" +#include "llvm/IR/Statepoint.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/User.h" +#include "llvm/IR/Value.h" +#include "llvm/IR/ValueHandle.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Scalar/CJGCLiveAnalysis.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Local.h" +#include "llvm/Transforms/Utils/PromoteMemToReg.h" +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_TYPE "cj-rewrite-statepoint" + +using namespace llvm; + +// Print out the base pointers for debugging +static cl::opt PrintBasePointers("cj-spp-print-base-pointers", cl::Hidden, + cl::init(false)); + +static cl::opt + DisableCJRewrite("disable-cj-rewrite-statepoint", cl::init(false), + cl::desc("Do not run CJRewriteStatepoint")); +// Cost threshold measuring when it is profitable to rematerialize value instead +// of relocating it +static cl::opt + RematerializationThreshold("cj-spp-rematerialization-threshold", cl::Hidden, + cl::init(6)); + +static cl::opt RematDerivedAtUses("remat-derived-at-uses", cl::Hidden, + cl::init(true)); + +static cl::opt StructContainGCFieldThreshold( + "large-struct-size-threshold", cl::Hidden, cl::init(1024), + cl::desc("In the struct live analysis, if the gcfield of the struct " + "exceeds this value, the struct SSA is used for analysis.")); + +namespace llvm { +extern cl::opt CJPipeline; +} + +/// The IR fed into CJRewriteStatepoint may have had attributes and metadata +/// implying dereferenceability that are no longer valid/correct after +/// CJRewriteStatepoint has run. This is because semantically, after +/// CJRewriteStatepoint runs, all calls to gc.statepoint "free" the entire +/// heap. stripNonValidData (conservatively) restores correctness by erasing +/// all attributes in the module that externally imply dereferenceability. +/// Similar reasoning also applies to the noalias attributes and metadata. +/// gc.statepoint can touch the entire heap including noalias objects. +/// Apart from attributes and metadata, we also remove instructions that imply +/// constant physical memory: llvm.invariant.start. +static void stripNonValidData(Module &M); + +static bool shouldRewriteStatepointsIn(Function &F); + +static bool rewriteCJThrowException(Module &M); + +static bool deleteUnusedBlackHole(Module &M); + +PreservedAnalyses CJRewriteStatepoint::run(Module &M, + ModuleAnalysisManager &AM) { + if (DisableCJRewrite) { + return PreservedAnalyses::all(); + } + Metadata *MD = M.getModuleFlag("HasRewrittenStatepoint"); + if (MD != nullptr) + report_fatal_error("Module has done CJRewriteStatepoint before!"); + + bool Changed = false; + auto &FAM = AM.getResult(M).getManager(); + for (Function &F : M) { + // Nothing to do for declarations. + if (F.isDeclaration() || F.empty()) + continue; + + // Policy choice says not to rewrite - the most common reason is that we're + // compiling code without a GCStrategy. + if (!shouldRewriteStatepointsIn(F)) + continue; + + auto &DT = FAM.getResult(F); + auto &PostDT = FAM.getResult(F); + auto &TTI = FAM.getResult(F); + auto &TLI = FAM.getResult(F); + Changed |= runOnFunction(F, DT, PostDT, TTI, TLI); + } + + M.addModuleFlag(Module::Warning, "HasRewrittenStatepoint", true); + + Changed |= rewriteCJThrowException(M); + Changed |= deleteUnusedBlackHole(M); + + if (!Changed) + return PreservedAnalyses::all(); + + // stripNonValidData asserts that shouldRewriteStatepointsIn + // returns true for at least one function in the module. Since at least + // one function changed, we know that the precondition is satisfied. + stripNonValidData(M); + + PreservedAnalyses PA; + PA.preserve(); + PA.preserve(); + return PA; +} + +namespace { + +class CJRewriteStatepointLegacyPass : public ModulePass { + CJRewriteStatepoint Impl; + +public: + static char ID; // Pass identification, replacement for typeid + unsigned OptLevel; + + explicit CJRewriteStatepointLegacyPass(unsigned OptLevel = 0) + : ModulePass(ID), Impl(OptLevel), OptLevel(OptLevel) { + initializeCJRewriteStatepointLegacyPassPass( + *PassRegistry::getPassRegistry()); + } + + bool runOnModule(Module &M) override { + if (DisableCJRewrite) { + return false; + } + Metadata *MD = M.getModuleFlag("HasRewrittenStatepoint"); + if (MD != nullptr) + report_fatal_error("Module has done CJRewriteStatepoint before!"); + + bool Changed = false; + for (Function &F : M) { + // Nothing to do for declarations. + if (F.isDeclaration() || F.empty()) + continue; + + // Policy choice says not to rewrite - the most common reason is that + // we're compiling code without a GCStrategy. + if (!shouldRewriteStatepointsIn(F)) + continue; + + TargetTransformInfo &TTI = + getAnalysis().getTTI(F); + const TargetLibraryInfo &TLI = + getAnalysis().getTLI(F); + auto &DT = getAnalysis(F).getDomTree(); + auto &PostDT = + getAnalysis(F).getPostDomTree(); + + Changed |= Impl.runOnFunction(F, DT, PostDT, TTI, TLI); + } + + M.addModuleFlag(Module::Warning, "HasRewrittenStatepoint", true); + + if (!Changed) + return false; + + // stripNonValidData asserts that shouldRewriteStatepointsIn + // returns true for at least one function in the module. Since at least + // one function changed, we know that the precondition is satisfied. + stripNonValidData(M); + return true; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + // We add and rewrite a bunch of instructions, but don't really do much + // else. We could in theory preserve a lot more analyses here. + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + } +}; + +} // end anonymous namespace + +char CJRewriteStatepointLegacyPass::ID = 0; + +ModulePass *llvm::createCJRewriteStatepointLegacyPass(unsigned OptLevel) { + return new CJRewriteStatepointLegacyPass(OptLevel); +} + +INITIALIZE_PASS_BEGIN(CJRewriteStatepointLegacyPass, "cj-rewrite-statepoint", + "Make cangjie relocations explicit at statepoints", false, + false) +INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) +INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass) +INITIALIZE_PASS_END(CJRewriteStatepointLegacyPass, "cj-rewrite-statepoint", + "Make cangjie relocations explicit at statepoints", false, + false) + +namespace { + +// The type of the internal cache used inside the findBasePointers family +// of functions. From the callers perspective, this is an opaque type and +// should not be inspected. +// +// In the actual implementation this caches two relations: +// - The base relation itself (i.e. this pointer is based on that one) +// - The base defining value relation (i.e. before base_phi insertion) +// Generally, after the execution of a full findBasePointer call, only the +// base relation will remain. Internally, we add a mixture of the two +// types, then update all the second type to the first type +using DefiningValueMapTy = MapVector; +using IsKnownBaseMapTy = MapVector; + +struct RematerizlizationCandidateRecord { + // Chain from derived pointer to base. + SmallVector ChainToBase; + // Original base. + Value *RootOfChain; + // Cost of chain. + InstructionCost Cost; +}; + +using RematCandTy = MapVector; +} // end anonymous namespace + +// Return true, if it is the this argument of a function. +static bool isMutThisArg(Argument *Arg) { + Function *CallFunc = Arg->getParent(); + if (!CallFunc->hasFnAttribute("record_mut")) + return false; + + if (CallFunc->hasParamAttribute(0, Attribute::StructRet)) { + return Arg->getArgNo() == 1; + } else { + return Arg->getArgNo() == 0; + } +} + +static bool isAddrspaceCastToGCPointer(Value *V, Value *Def) { + if (!isGCPointerType(V->getType())) { + return false; + } + PointerType *ValTy = cast(V->getType()); + PointerType *DefTy = cast(Def->getType()); + if (ValTy->getAddressSpace() == DefTy->getAddressSpace()) { + return false; + } + + assert(DefTy->getAddressSpace() == 0 && ValTy->getAddressSpace() == 1 && + "unsupported addrspacecast type"); + return true; +} + +// Return the name of the value suffixed with the provided value, or if the +// value didn't have a name, the default value specified. +static std::string suffixed_name_or(Value *V, StringRef Suffix, + StringRef DefaultName) { + return V->hasName() ? (V->getName() + Suffix).str() : DefaultName.str(); +} + +/// Returns true if V is a known base. +static bool isKnownBase(Value *V, const IsKnownBaseMapTy &KnownBases); + +/// Caches the IsKnownBase flag for a value and asserts that it wasn't present +/// in the cache before. +static void setKnownBase(Value *V, bool IsKnownBase, + IsKnownBaseMapTy &KnownBases); + +static Value *findBaseDefiningValue(Value *I, DefiningValueMapTy &Cache, + IsKnownBaseMapTy &KnownBases); + +/// Return a base defining value for the 'Index' element of the given vector +/// instruction 'I'. If Index is null, returns a BDV for the entire vector +/// 'I'. As an optimization, this method will try to determine when the +/// element is known to already be a base pointer. If this can be established, +/// the second value in the returned pair will be true. Note that either a +/// vector or a pointer typed value can be returned. For the former, the +/// vector returned is a BDV (and possibly a base) of the entire vector 'I'. +/// If the later, the return pointer is a BDV (or possibly a base) for the +/// particular element in 'I'. +static Value *findBaseDefiningValueOfVector(Value *I, DefiningValueMapTy &Cache, + IsKnownBaseMapTy &KnownBases) { + // Each case parallels findBaseDefiningValue below, see that code for + // detailed motivation. + + auto Cached = Cache.find(I); + if (Cached != Cache.end()) + return Cached->second; + + if (isa(I)) { + // An incoming argument to the function is a base pointer + Cache[I] = I; + setKnownBase(I, /* IsKnownBase */ true, KnownBases); + return I; + } + + if (isa(I)) { + // Base of constant vector consists only of constant null pointers. + // For reasoning see similar case inside 'findBaseDefiningValue' function. + auto *CAZ = ConstantAggregateZero::get(I->getType()); + Cache[I] = CAZ; + setKnownBase(CAZ, /* IsKnownBase */ true, KnownBases); + return CAZ; + } + + if (isa(I)) { + Cache[I] = I; + setKnownBase(I, /* IsKnownBase */ true, KnownBases); + return I; + } + + if (isa(I)) { + // We don't know whether this vector contains entirely base pointers or + // not. To be conservatively correct, we treat it as a BDV and will + // duplicate code as needed to construct a parallel vector of bases. + Cache[I] = I; + setKnownBase(I, /* IsKnownBase */ false, KnownBases); + return I; + } + + if (isa(I)) { + // We don't know whether this vector contains entirely base pointers or + // not. To be conservatively correct, we treat it as a BDV and will + // duplicate code as needed to construct a parallel vector of bases. + // TODO: There a number of local optimizations which could be applied here + // for particular sufflevector patterns. + Cache[I] = I; + setKnownBase(I, /* IsKnownBase */ false, KnownBases); + return I; + } + + // The behavior of getelementptr instructions is the same for vector and + // non-vector data types. + if (auto *GEP = dyn_cast(I)) { + auto *BDV = + findBaseDefiningValue(GEP->getPointerOperand(), Cache, KnownBases); + Cache[GEP] = BDV; + return BDV; + } + + // The behavior of freeze instructions is the same for vector and + // non-vector data types. + if (auto *Freeze = dyn_cast(I)) { + auto *BDV = findBaseDefiningValue(Freeze->getOperand(0), Cache, KnownBases); + Cache[Freeze] = BDV; + return BDV; + } + + // If the pointer comes through a bitcast of a vector of pointers to + // a vector of another type of pointer, then look through the bitcast + if (auto *BC = dyn_cast(I)) { + auto *BDV = findBaseDefiningValue(BC->getOperand(0), Cache, KnownBases); + Cache[BC] = BDV; + return BDV; + } + + // We assume that functions in the source language only return base + // pointers. This should probably be generalized via attributes to support + // both source language and internal functions. + if (isa(I) || isa(I)) { + Cache[I] = I; + setKnownBase(I, /* IsKnownBase */ true, KnownBases); + return I; + } + + // A PHI or Select is a base defining value. The outer findBasePointer + // algorithm is responsible for constructing a base value for this BDV. + assert((isa(I) || isa(I)) && + "unknown vector instruction - no base found for vector element"); + Cache[I] = I; + setKnownBase(I, /* IsKnownBase */ false, KnownBases); + return I; +} + +/// Helper function for findBasePointer - Will return a value which either a) +/// defines the base pointer for the input, b) blocks the simple search +/// (i.e. a PHI or Select of two derived pointers), or c) involves a change +/// from pointer to vector type or back. +static Value *findBaseDefiningValue(Value *I, DefiningValueMapTy &Cache, + IsKnownBaseMapTy &KnownBases) { + assert((I->getType()->isPtrOrPtrVectorTy() || I->getType()->isStructTy()) && + "Illegal to ask for the base pointer of a non-pointer type"); + + auto Cached = Cache.find(I); + if (Cached != Cache.end()) + return Cached->second; + + if (I->getType()->isStructTy()) { + Cache[I] = I; + setKnownBase(I, /* IsKnownBase */ true, KnownBases); + return I; + } + + if (I->getType()->isVectorTy()) + return findBaseDefiningValueOfVector(I, Cache, KnownBases); + + if (auto *Arg = dyn_cast(I)) { + if (!Arg->getType()->isOpaquePointerTy()) { + // The backend determines whether there is a baseptr based on the frontend + // `mut` attribute. In this case, addrspace(1)* may be a derived pointer + // and does not have the typeinfio* header, and base pointer is required + // for runtime GC parsing. The base is next to the current variable. + // The non-mut addrspace(1)* parameter is considered as a pointer with + // the typeinfo* header and can be directly recorded in stackmap. + assert(isGCPointerType(Arg->getType()) && + "Derived Pointer should be gc-pointer type."); + if (isMutThisArg(Arg)) { + Function *F = Arg->getParent(); + Value *ArgBase = F->getArg(Arg->getArgNo() + 1); + Cache[I] = ArgBase; + setKnownBase(ArgBase, /* IsKnownBase */ true, KnownBases); + return ArgBase; + } + } + + // An incoming argument to the function is a base pointer + // We should have never reached here if this argument isn't an gc value + Cache[I] = I; + setKnownBase(I, /* IsKnownBase */ true, KnownBases); + return I; + } + + if (isa(I)) { + // We assume that objects with a constant base (e.g. a global) can't move + // and don't need to be reported to the collector because they are always + // live. Besides global references, all kinds of constants (e.g. undef, + // constant expressions, null pointers) can be introduced by the inliner or + // the optimizer, especially on dynamically dead paths. + // Here we treat all of them as having single null base. By doing this we + // trying to avoid problems reporting various conflicts in a form of + // "phi (const1, const2)" or "phi (const, regular gc ptr)". + // See constant.ll file for relevant test cases. + + auto *CPN = ConstantPointerNull::get(cast(I->getType())); + Cache[I] = CPN; + setKnownBase(CPN, /* IsKnownBase */ true, KnownBases); + return CPN; + } + + // inttoptrs in an integral address space are currently ill-defined. We + // treat them as defining base pointers here for consistency with the + // constant rule above and because we don't really have a better semantic + // to give them. Note that the optimizer is always free to insert undefined + // behavior on dynamically dead paths as well. + if (isa(I)) { + Cache[I] = I; + setKnownBase(I, /* IsKnownBase */ true, KnownBases); + return I; + } + + if (CastInst *CI = dyn_cast(I)) { + Value *Def = CI->stripPointerCasts(); + // If stripping pointer casts changes the address space there is an + // addrspacecast in between. Set it to null value temporarily and + // after all the are findbaseptr are finished, we'll delete it. + if (isAddrspaceCastToGCPointer(CI, Def)) { + auto *CPN = ConstantPointerNull::get(cast(I->getType())); + Cache[I] = CPN; + setKnownBase(CPN, /* IsKnownBase */ true, KnownBases); + return CPN; + } + + // If we find a cast instruction here, it means we've found a cast which is + // not simply a pointer cast (i.e. an inttoptr). We don't know how to + // handle int->ptr conversion. + // assert(!isa(Def) && "Not support inttoptr") + if (isa(Def)) { +#ifndef NDEBUG + auto *IntToPtrI = dyn_cast(Def); + assert(!isGCPointerType(IntToPtrI->getOperand(0)->getType()) && + "IntToPtr is now allowed to be used to convert int to gc-pointer"); + assert(!isMemoryContainsGCPtrType(IntToPtrI->getOperand(0)->getType()) && + "IntToPtr is now allowed to be used to convert int to memory type " + "which contains gc-pointer"); +#endif + } + auto *BDV = findBaseDefiningValue(Def, Cache, KnownBases); + Cache[CI] = BDV; + return BDV; + } + + if (isa(I)) { + // The value loaded is an gc base itself + Cache[I] = I; + setKnownBase(I, /* IsKnownBase */ true, KnownBases); + return I; + } + + if (GetElementPtrInst *GEP = dyn_cast(I)) { + // The base of this GEP is the base + auto *BDV = + findBaseDefiningValue(GEP->getPointerOperand(), Cache, KnownBases); + Cache[GEP] = BDV; + return BDV; + } + + if (auto *Freeze = dyn_cast(I)) { + auto *BDV = findBaseDefiningValue(Freeze->getOperand(0), Cache, KnownBases); + Cache[Freeze] = BDV; + return BDV; + } + + if (IntrinsicInst *II = dyn_cast(I)) { + switch (II->getIntrinsicID()) { + default: + // fall through to general call handling + break; + case Intrinsic::cj_gc_statepoint: + llvm_unreachable("statepoints don't produce pointers"); + case Intrinsic::cj_gc_relocate: + // Rerunning safepoint insertion after safepoints are already + // inserted is not supported. It could probably be made to work, + // but why are you doing this? There's no good reason. + llvm_unreachable("repeat safepoint insertion is not supported"); + case Intrinsic::gcroot: + // Currently, this mechanism hasn't been extended to work with gcroot. + // There's no reason it couldn't be, but I haven't thought about the + // implications much. + llvm_unreachable( + "interaction with the gcroot mechanism is not supported"); + case Intrinsic::experimental_gc_get_pointer_base: + auto *BDV = findBaseDefiningValue(II->getOperand(0), Cache, KnownBases); + Cache[II] = BDV; + return BDV; + } + } + // We assume that functions in the source language only return base + // pointers. This should probably be generalized via attributes to support + // both source language and internal functions. + if (isa(I) || isa(I)) { + Cache[I] = I; + setKnownBase(I, /* IsKnownBase */ true, KnownBases); + return I; + } + + // TODO: I have absolutely no idea how to implement this part yet. It's not + // necessarily hard, I just haven't really looked at it yet. + assert(!isa(I) && "Landing Pad is unimplemented"); + + if (isa(I)) { + // A CAS is effectively a atomic store and load combined under a + // predicate. From the perspective of base pointers, we just treat it + // like a load. + Cache[I] = I; + setKnownBase(I, /* IsKnownBase */ true, KnownBases); + return I; + } + + assert(!isa(I) && "Xchg handled above, all others are " + "binary ops which don't apply to pointers"); + + // The aggregate ops. Aggregates can either be in the heap or on the + // stack, but in either case, this is simply a field load. As a result, + // this is a defining definition of the base just like a load is. + if (isa(I)) { + Cache[I] = I; + setKnownBase(I, /* IsKnownBase */ true, KnownBases); + return I; + } + + // Alloca pointer is found here to indicate incorrect live values in the + // computeLiveInValues. + assert(!isa(I) && "illegal alloca pointer come from gcptr!"); + + // We should never see an insert vector since that would require we be + // tracing back a struct value not a pointer value. + assert(!isa(I) && + "Base pointer for a struct is meaningless"); + + // This value might have been generated by findBasePointer() called when + // substituting gc.get.pointer.base() intrinsic. + bool IsKnownBase = + isa(I) && cast(I)->getMetadata("is_base_value"); + setKnownBase(I, /* IsKnownBase */ IsKnownBase, KnownBases); + Cache[I] = I; + + // An extractelement produces a base result exactly when it's input does. + // We may need to insert a parallel instruction to extract the appropriate + // element out of the base vector corresponding to the input. Given this, + // it's analogous to the phi and select case even though it's not a merge. + if (isa(I)) + // Note: There a lot of obvious peephole cases here. This are deliberately + // handled after the main base pointer inference algorithm to make writing + // test cases to exercise that code easier. + return I; + + // The last two cases here don't return a base pointer. Instead, they + // return a value which dynamically selects from among several base + // derived pointers (each with it's own base potentially). It's the job of + // the caller to resolve these. + assert((isa(I) || isa(I)) && + "missing instruction case in findBaseDefiningValue"); + return I; +} + +/// Returns the base defining value for this value. +static Value *findBaseDefiningValueCached(Value *I, DefiningValueMapTy &Cache, + IsKnownBaseMapTy &KnownBases) { + if (Cache.find(I) == Cache.end()) { + auto *BDV = findBaseDefiningValue(I, Cache, KnownBases); + Cache[I] = BDV; + LLVM_DEBUG(dbgs() << "fBDV-cached: " << I->getName() << " -> " + << Cache[I]->getName() + << ", is known base = " << KnownBases[I] << "\n"); + } + assert(Cache[I] != nullptr); + assert(KnownBases.find(Cache[I]) != KnownBases.end() && + "Cached value must be present in known bases map"); + return Cache[I]; +} + +/// Return a base pointer for this value if known. Otherwise, return it's +/// base defining value. +static Value *findBaseOrBDV(Value *I, DefiningValueMapTy &Cache, + IsKnownBaseMapTy &KnownBases) { + Value *Def = findBaseDefiningValueCached(I, Cache, KnownBases); + auto Found = Cache.find(Def); + if (Found != Cache.end()) { + // Either a base-of relation, or a self reference. Caller must check. + return Found->second; + } + // Only a BDV available + return Def; +} + +#ifndef NDEBUG +/// This value is a base pointer that is not generated by RS4GC, i.e. it already +/// exists in the code. +static bool isOriginalBaseResult(Value *V) { + // no recursion possible + return !isa(V) && !isa(V) && + !isa(V) && !isa(V) && + !isa(V); +} +#endif + +static bool isKnownBase(Value *V, const IsKnownBaseMapTy &KnownBases) { + auto It = KnownBases.find(V); + assert(It != KnownBases.end() && "Value not present in the map"); + return It->second; +} + +static void setKnownBase(Value *V, bool IsKnownBase, + IsKnownBaseMapTy &KnownBases) { +#ifndef NDEBUG + auto It = KnownBases.find(V); + if (It != KnownBases.end()) + assert(It->second == IsKnownBase && "Changing already present value"); +#endif + KnownBases[V] = IsKnownBase; +} + +// Returns true if First and Second values are both scalar or both vector. +static bool areBothVectorOrScalar(Value *First, Value *Second) { + return isa(First->getType()) == + isa(Second->getType()); +} + +namespace { + +/// Models the state of a single base defining value in the findBasePointer +/// algorithm for determining where a new instruction is needed to propagate +/// the base of this BDV. +class BDVState { +public: + enum StatusTy { + // Starting state of lattice + Unknown, + // Some specific base value -- does *not* mean that instruction + // propagates the base of the object + // ex: gep %arg, 16 -> %arg is the base value + Base, + // Need to insert a node to represent a merge. + Conflict + }; + + BDVState() { llvm_unreachable("missing state in map"); } + + explicit BDVState(Value *OriginalValue) : OriginalValue(OriginalValue) {} + explicit BDVState(Value *OriginalValue, StatusTy Status, + Value *BaseValue = nullptr) + : OriginalValue(OriginalValue), Status(Status), BaseValue(BaseValue) { + assert(Status != Base || BaseValue); + } + + StatusTy getStatus() const { return Status; } + Value *getOriginalValue() const { return OriginalValue; } + Value *getBaseValue() const { return BaseValue; } + + bool isBase() const { return getStatus() == Base; } + bool isUnknown() const { return getStatus() == Unknown; } + bool isConflict() const { return getStatus() == Conflict; } + + // Values of type BDVState form a lattice, and this function implements the + // meet + // operation. + void meet(const BDVState &Other) { + auto markConflict = [&]() { + Status = BDVState::Conflict; + BaseValue = nullptr; + }; + // Conflict is a final state. + if (isConflict()) + return; + // if we are not known - just take other state. + if (isUnknown()) { + Status = Other.getStatus(); + BaseValue = Other.getBaseValue(); + return; + } + // We are base. + assert(isBase() && "Unknown state"); + // If other is unknown - just keep our state. + if (Other.isUnknown()) + return; + // If other is conflict - it is a final state. + if (Other.isConflict()) + return markConflict(); + // Other is base as well. + assert(Other.isBase() && "Unknown state"); + // If bases are different - Conflict. + if (getBaseValue() != Other.getBaseValue()) + return markConflict(); + // We are identical, do nothing. + } + + bool operator==(const BDVState &Other) const { + return OriginalValue == Other.OriginalValue && + BaseValue == Other.BaseValue && Status == Other.Status; + } + + bool operator!=(const BDVState &other) const { return !(*this == other); } + + LLVM_DUMP_METHOD + void dump() const { + print(dbgs()); + dbgs() << '\n'; + } + + void print(raw_ostream &OS) const { + switch (getStatus()) { + case Unknown: + OS << "U"; + break; + case Base: + OS << "B"; + break; + case Conflict: + OS << "C"; + break; + } + OS << " (base " << getBaseValue() << " - " + << (getBaseValue() ? getBaseValue()->getName() : "nullptr") << ")" + << " for " << OriginalValue->getName() << ":"; + } + +private: + AssertingVH OriginalValue; // instruction this state corresponds to + StatusTy Status = Unknown; + AssertingVH BaseValue = nullptr; // Non-null only if Status == Base. +}; + +} // end anonymous namespace + +#ifndef NDEBUG +static raw_ostream &operator<<(raw_ostream &OS, const BDVState &State) { + State.print(OS); + return OS; +} +#endif + +namespace { +/// For a given value or instruction, figure out what base ptr its derived from. +/// For gc objects, this is simply itself. On success, returns a value which is +/// the base pointer. (This is reliable and can be used for relocation.) On +/// failure, returns nullptr. +Value *findBasePointer(Value *I, DefiningValueMapTy &Cache, + IsKnownBaseMapTy &KnownBases) { + Value *Def = findBaseOrBDV(I, Cache, KnownBases); + + if (isKnownBase(Def, KnownBases) && areBothVectorOrScalar(Def, I)) + return Def; + + // Here's the rough algorithm: + // - For every SSA value, construct a mapping to either an actual base + // pointer or a PHI which obscures the base pointer. + // - Construct a mapping from PHI to unknown TOP state. Use an + // optimistic algorithm to propagate base pointer information. Lattice + // looks like: + // UNKNOWN + // b1 b2 b3 b4 + // CONFLICT + // When algorithm terminates, all PHIs will either have a single concrete + // base or be in a conflict state. + // - For every conflict, insert a dummy PHI node without arguments. Add + // these to the base[Instruction] = BasePtr mapping. For every + // non-conflict, add the actual base. + // - For every conflict, add arguments for the base[a] of each input + // arguments. + // + // Note: A simpler form of this would be to add the conflict form of all + // PHIs without running the optimistic algorithm. This would be + // analogous to pessimistic data flow and would likely lead to an + // overall worse solution. + +#ifndef NDEBUG + auto isExpectedBDVType = [](Value *BDV) { + return isa(BDV) || isa(BDV) || + isa(BDV) || isa(BDV) || + isa(BDV); + }; +#endif + + // Once populated, will contain a mapping from each potentially non-base BDV + // to a lattice value (described above) which corresponds to that BDV. + // We use the order of insertion (DFS over the def/use graph) to provide a + // stable deterministic ordering for visiting DenseMaps (which are unordered) + // below. This is important for deterministic compilation. + MapVector States; + +#ifndef NDEBUG + auto VerifyStates = [&]() { + for (auto &Entry : States) { + assert(Entry.first == Entry.second.getOriginalValue()); + } + }; +#endif + + auto visitBDVOperands = [](Value *BDV, std::function F) { + if (PHINode *PN = dyn_cast(BDV)) { + for (Value *InVal : PN->incoming_values()) + F(InVal); + } else if (SelectInst *SI = dyn_cast(BDV)) { + F(SI->getTrueValue()); + F(SI->getFalseValue()); + } else if (auto *EE = dyn_cast(BDV)) { + F(EE->getVectorOperand()); + } else if (auto *IE = dyn_cast(BDV)) { + F(IE->getOperand(0)); + F(IE->getOperand(1)); + } else if (auto *SV = dyn_cast(BDV)) { + // For a canonical broadcast, ignore the undef argument + // (without this, we insert a parallel base shuffle for every broadcast) + F(SV->getOperand(0)); + if (!SV->isZeroEltSplat()) + F(SV->getOperand(1)); + } else { + llvm_unreachable("unexpected BDV type"); + } + }; + + // Recursively fill in all base defining values reachable from the initial + // one for which we don't already know a definite base value for + /* scope */ { + SmallVector Worklist; + Worklist.push_back(Def); + States.insert({Def, BDVState(Def)}); + while (!Worklist.empty()) { + Value *Current = Worklist.pop_back_val(); + assert(!isOriginalBaseResult(Current) && "why did it get added?"); + + auto visitIncomingValue = [&](Value *InVal) { + Value *Base = findBaseOrBDV(InVal, Cache, KnownBases); + if (isKnownBase(Base, KnownBases) && areBothVectorOrScalar(Base, InVal)) + // Known bases won't need new instructions introduced and can be + // ignored safely. However, this can only be done when InVal and Base + // are both scalar or both vector. Otherwise, we need to find a + // correct BDV for InVal, by creating an entry in the lattice + // (States). + return; + assert(isExpectedBDVType(Base) && + "the only non-base values " + "we see should be base defining values"); + if (States.insert(std::make_pair(Base, BDVState(Base))).second) + Worklist.push_back(Base); + }; + + visitBDVOperands(Current, visitIncomingValue); + } + } + +#ifndef NDEBUG + VerifyStates(); + LLVM_DEBUG(dbgs() << "States after initialization:\n"); + for (const auto &Pair : States) { + LLVM_DEBUG(dbgs() << " " << Pair.second << " for " << *Pair.first << "\n"); + } +#endif + + // Iterate forward through the value graph pruning any node from the state + // list where all of the inputs are base pointers. The purpose of this is to + // reuse existing values when the derived pointer we were asked to materialize + // a base pointer for happens to be a base pointer itself. (Or a sub-graph + // feeding it does.) + SmallVector ToRemove; + do { + ToRemove.clear(); + for (auto Pair : States) { + Value *BDV = Pair.first; + auto canPruneInput = [&](Value *V) { + // If the input of the BDV is the BDV itself we can prune it. This is + // only possible if the BDV is a PHI node. + if (V->stripPointerCasts() == BDV) + return true; + Value *VBDV = findBaseOrBDV(V, Cache, KnownBases); + if (V->stripPointerCasts() != VBDV) + return false; + // The assumption is that anything not in the state list is + // propagates a base pointer. + return States.count(VBDV) == 0; + }; + + bool CanPrune = true; + visitBDVOperands( + BDV, [&](Value *Op) { CanPrune = CanPrune && canPruneInput(Op); }); + if (CanPrune) + ToRemove.push_back(BDV); + } + for (Value *V : ToRemove) { + States.erase(V); + // Cache the fact V is it's own base for later usage. + Cache[V] = V; + } + } while (!ToRemove.empty()); + + // Did we manage to prove that Def itself must be a base pointer? + if (!States.count(Def)) + return Def; + + // Return a phi state for a base defining value. We'll generate a new + // base state for known bases and expect to find a cached state otherwise. + auto GetStateForBDV = [&](Value *BaseValue, Value *Input) { + auto I = States.find(BaseValue); + if (I != States.end()) + return I->second; + assert(areBothVectorOrScalar(BaseValue, Input)); + return BDVState(BaseValue, BDVState::Base, BaseValue); + }; + + bool Progress = true; + while (Progress) { +#ifndef NDEBUG + const size_t OldSize = States.size(); +#endif + Progress = false; + // We're only changing values in this loop, thus safe to keep iterators. + // Since this is computing a fixed point, the order of visit does not + // effect the result. TODO: We could use a worklist here and make this run + // much faster. + for (auto Pair : States) { + Value *BDV = Pair.first; + // Only values that do not have known bases or those that have differing + // type (scalar versus vector) from a possible known base should be in the + // lattice. + assert((!isKnownBase(BDV, KnownBases) || + !areBothVectorOrScalar(BDV, Pair.second.getBaseValue())) && + "why did it get added?"); + + BDVState NewState(BDV); + visitBDVOperands(BDV, [&](Value *Op) { + Value *BDV = findBaseOrBDV(Op, Cache, KnownBases); + auto OpState = GetStateForBDV(BDV, Op); + NewState.meet(OpState); + }); + + BDVState OldState = States[BDV]; + if (OldState != NewState) { + Progress = true; + States[BDV] = NewState; + } + } + + assert(OldSize == States.size() && + "fixed point shouldn't be adding any new nodes to state"); + } + +#ifndef NDEBUG + VerifyStates(); + LLVM_DEBUG(dbgs() << "States after meet iteration:\n"); + for (const auto &Pair : States) { + LLVM_DEBUG(dbgs() << " " << Pair.second << " for " << *Pair.first << "\n"); + } +#endif + + // Handle all instructions that have a vector BDV, but the instruction itself + // is of scalar type. + for (auto Pair : States) { + Instruction *I = cast(Pair.first); + BDVState State = Pair.second; + auto *BaseValue = State.getBaseValue(); + // Only values that do not have known bases or those that have differing + // type (scalar versus vector) from a possible known base should be in the + // lattice. + assert( + (!isKnownBase(I, KnownBases) || !areBothVectorOrScalar(I, BaseValue)) && + "why did it get added?"); + assert(!State.isUnknown() && "Optimistic algorithm didn't complete!"); + + if (!State.isBase() || !isa(BaseValue->getType())) + continue; + // extractelement instructions are a bit special in that we may need to + // insert an extract even when we know an exact base for the instruction. + // The problem is that we need to convert from a vector base to a scalar + // base for the particular indice we're interested in. + if (isa(I)) { + auto *EE = cast(I); + // TODO: In many cases, the new instruction is just EE itself. We should + // exploit this, but can't do it here since it would break the invariant + // about the BDV not being known to be a base. + auto *BaseInst = ExtractElementInst::Create( + State.getBaseValue(), EE->getIndexOperand(), "base_ee", EE); + BaseInst->setMetadata("is_base_value", MDNode::get(I->getContext(), {})); + States[I] = BDVState(I, BDVState::Base, BaseInst); + setKnownBase(BaseInst, /* IsKnownBase */ true, KnownBases); + } else if (!isa(I->getType())) { + // We need to handle cases that have a vector base but the instruction is + // a scalar type (these could be phis or selects or any instruction that + // are of scalar type, but the base can be a vector type). We + // conservatively set this as conflict. Setting the base value for these + // conflicts is handled in the next loop which traverses States. + States[I] = BDVState(I, BDVState::Conflict); + } + } + +#ifndef NDEBUG + VerifyStates(); +#endif + + // Insert Phis for all conflicts + // TODO: adjust naming patterns to avoid this order of iteration dependency + for (auto Pair : States) { + Instruction *I = cast(Pair.first); + BDVState State = Pair.second; + // Only values that do not have known bases or those that have differing + // type (scalar versus vector) from a possible known base should be in the + // lattice. + assert((!isKnownBase(I, KnownBases) || + !areBothVectorOrScalar(I, State.getBaseValue())) && + "why did it get added?"); + assert(!State.isUnknown() && "Optimistic algorithm didn't complete!"); + + // Since we're joining a vector and scalar base, they can never be the + // same. As a result, we should always see insert element having reached + // the conflict state. + assert(!isa(I) || State.isConflict()); + + if (!State.isConflict()) + continue; + + auto getMangledName = [](Instruction *I) -> std::string { + if (isa(I)) { + return suffixed_name_or(I, ".base", "base_phi"); + } else if (isa(I)) { + return suffixed_name_or(I, ".base", "base_select"); + } else if (isa(I)) { + return suffixed_name_or(I, ".base", "base_ee"); + } else if (isa(I)) { + return suffixed_name_or(I, ".base", "base_ie"); + } else { + return suffixed_name_or(I, ".base", "base_sv"); + } + }; + + Instruction *BaseInst = I->clone(); + BaseInst->insertBefore(I); + BaseInst->setName(getMangledName(I)); + // Add metadata marking this as a base value + BaseInst->setMetadata("is_base_value", MDNode::get(I->getContext(), {})); + States[I] = BDVState(I, BDVState::Conflict, BaseInst); + setKnownBase(BaseInst, /* IsKnownBase */ true, KnownBases); + } + +#ifndef NDEBUG + VerifyStates(); +#endif + + // Returns a instruction which produces the base pointer for a given + // instruction. The instruction is assumed to be an input to one of the BDVs + // seen in the inference algorithm above. As such, we must either already + // know it's base defining value is a base, or have inserted a new + // instruction to propagate the base of it's BDV and have entered that newly + // introduced instruction into the state table. In either case, we are + // assured to be able to determine an instruction which produces it's base + // pointer. + auto getBaseForInput = [&](Value *Input, Instruction *InsertPt) { + Value *BDV = findBaseOrBDV(Input, Cache, KnownBases); + Value *Base = nullptr; + if (!States.count(BDV)) { + assert(areBothVectorOrScalar(BDV, Input)); + Base = BDV; + } else { + // Either conflict or base. + assert(States.count(BDV)); + Base = States[BDV].getBaseValue(); + } + assert(Base && "Can't be null"); + // The cast is needed since base traversal may strip away bitcasts + if (Base->getType() != Input->getType() && InsertPt) + Base = new BitCastInst(Base, Input->getType(), "cast", InsertPt); + return Base; + }; + + // Fixup all the inputs of the new PHIs. Visit order needs to be + // deterministic and predictable because we're naming newly created + // instructions. + for (auto Pair : States) { + Instruction *BDV = cast(Pair.first); + BDVState State = Pair.second; + + // Only values that do not have known bases or those that have differing + // type (scalar versus vector) from a possible known base should be in the + // lattice. + assert((!isKnownBase(BDV, KnownBases) || + !areBothVectorOrScalar(BDV, State.getBaseValue())) && + "why did it get added?"); + assert(!State.isUnknown() && "Optimistic algorithm didn't complete!"); + if (!State.isConflict()) + continue; + + if (PHINode *BasePHI = dyn_cast(State.getBaseValue())) { + PHINode *PN = cast(BDV); + const unsigned NumPHIValues = PN->getNumIncomingValues(); + + // The IR verifier requires phi nodes with multiple entries from the + // same basic block to have the same incoming value for each of those + // entries. Since we're inserting bitcasts in the loop, make sure we + // do so at least once per incoming block. + DenseMap BlockToValue; + for (unsigned i = 0; i < NumPHIValues; i++) { + Value *InVal = PN->getIncomingValue(i); + BasicBlock *InBB = PN->getIncomingBlock(i); + if (!BlockToValue.count(InBB)) + BlockToValue[InBB] = getBaseForInput(InVal, InBB->getTerminator()); + else { +#ifndef NDEBUG + Value *OldBase = BlockToValue[InBB]; + Value *Base = getBaseForInput(InVal, nullptr); + + // We can't use `stripPointerCasts` instead of this function because + // `stripPointerCasts` doesn't handle vectors of pointers. + auto StripBitCasts = [](Value *V) -> Value * { + while (auto *BC = dyn_cast(V)) + V = BC->getOperand(0); + return V; + }; + // In essence this assert states: the only way two values + // incoming from the same basic block may be different is by + // being different bitcasts of the same value. A cleanup + // that remains TODO is changing findBaseOrBDV to return an + // llvm::Value of the correct type (and still remain pure). + // This will remove the need to add bitcasts. + assert(StripBitCasts(Base) == StripBitCasts(OldBase) && + "findBaseOrBDV should be pure!"); +#endif + } + Value *Base = BlockToValue[InBB]; + BasePHI->setIncomingValue(i, Base); + } + } else if (SelectInst *BaseSI = + dyn_cast(State.getBaseValue())) { + SelectInst *SI = cast(BDV); + + // Find the instruction which produces the base for each input. + // We may need to insert a bitcast. + BaseSI->setTrueValue(getBaseForInput(SI->getTrueValue(), BaseSI)); + BaseSI->setFalseValue(getBaseForInput(SI->getFalseValue(), BaseSI)); + } else if (auto *BaseEE = + dyn_cast(State.getBaseValue())) { + Value *InVal = cast(BDV)->getVectorOperand(); + // Find the instruction which produces the base for each input. We may + // need to insert a bitcast. + BaseEE->setOperand(0, getBaseForInput(InVal, BaseEE)); + } else if (auto *BaseIE = + dyn_cast(State.getBaseValue())) { + auto *BdvIE = cast(BDV); + auto UpdateOperand = [&](int OperandIdx) { + Value *InVal = BdvIE->getOperand(OperandIdx); + Value *Base = getBaseForInput(InVal, BaseIE); + BaseIE->setOperand(OperandIdx, Base); + }; + UpdateOperand(0); // vector operand + UpdateOperand(1); // scalar operand + } else { + auto *BaseSV = cast(State.getBaseValue()); + auto *BdvSV = cast(BDV); + auto UpdateOperand = [&](int OperandIdx) { + Value *InVal = BdvSV->getOperand(OperandIdx); + Value *Base = getBaseForInput(InVal, BaseSV); + BaseSV->setOperand(OperandIdx, Base); + }; + UpdateOperand(0); // vector operand + if (!BdvSV->isZeroEltSplat()) + UpdateOperand(1); // vector operand + else { + // Never read, so just use undef + Value *InVal = BdvSV->getOperand(1); + BaseSV->setOperand(1, UndefValue::get(InVal->getType())); + } + } + } + +#ifndef NDEBUG + VerifyStates(); +#endif + + // Cache all of our results so we can cheaply reuse them + // NOTE: This is actually two caches: one of the base defining value + // relation and one of the base pointer relation! FIXME + for (auto Pair : States) { + auto *BDV = Pair.first; + Value *Base = Pair.second.getBaseValue(); + assert(BDV && Base); + // Only values that do not have known bases or those that have differing + // type (scalar versus vector) from a possible known base should be in the + // lattice. + assert( + (!isKnownBase(BDV, KnownBases) || !areBothVectorOrScalar(BDV, Base)) && + "why did it get added?"); + + LLVM_DEBUG( + dbgs() << "Updating base value cache" + << " for: " << BDV->getName() << " from: " + << (Cache.count(BDV) ? Cache[BDV]->getName().str() : "none") + << " to: " << Base->getName() << "\n"); + + Cache[BDV] = Base; + } + assert(Cache.count(Def)); + return Cache[Def]; +} +} // end anonymous namespace + +// For a set of live pointers (base and/or derived), identify the base +// pointer of the object which they are derived from. This routine will +// mutate the IR graph as needed to make the 'base' pointer live at the +// definition site of 'derived'. This ensures that any use of 'derived' can +// also use 'base'. This may involve the insertion of a number of +// additional PHI nodes. +// +// preconditions: live is a set of pointer type Values +// +// side effects: may insert PHI nodes into the existing CFG, will preserve +// CFG, will not remove or mutate any existing nodes +// +// post condition: PointerToBase contains one (derived, base) pair for every +// pointer in live. Note that derived can be equal to base if the original +// pointer was a base pointer. +static void findBasePointers(const LiveSetTy &live, + PointerToBaseTy &PointerToBase, DominatorTree *DT, + DefiningValueMapTy &DVCache, + IsKnownBaseMapTy &KnownBases) { + for (Value *ptr : live) { + Value *base = findBasePointer(ptr, DVCache, KnownBases); + assert(base && "failed to find base pointer"); + PointerToBase[ptr] = base; + assert((!isa(base) || !isa(ptr) || + DT->dominates(cast(base)->getParent(), + cast(ptr)->getParent())) && + "The base we found better dominate the derived pointer"); + } +} + +/// Find the required based pointers (and adjust the live set) for the given +/// parse point. +static void findBasePointers(DominatorTree &DT, DefiningValueMapTy &DVCache, + SafepointRecord &result, + PointerToBaseTy &PointerToBase, + IsKnownBaseMapTy &KnownBases) { + LiveSetTy PotentiallyDerivedPointers = result.LiveSet; + findBasePointers(PotentiallyDerivedPointers, PointerToBase, &DT, DVCache, + KnownBases); +} + +// When inserting gc.relocate and gc.result calls, we need to ensure there are +// no uses of the original value / return value between the gc.statepoint and +// the gc.relocate / gc.result call. One case which can arise is a phi node +// starting one of the successor blocks. We also need to be able to insert the +// gc.relocates only on the path which goes through the statepoint. We might +// need to split an edge to make this possible. +static BasicBlock *normalizeForInvokeSafepoint(BasicBlock *BB, + BasicBlock *InvokeParent, + DominatorTree &DT) { + BasicBlock *Ret = BB; + if (!BB->getUniquePredecessor()) + Ret = SplitBlockPredecessors(BB, InvokeParent, "", &DT); + + // Now that 'Ret' has unique predecessor we can safely remove all phi nodes + // from it + FoldSingleEntryPHINodes(Ret); + assert(!isa(Ret->begin()) && + "All PHI nodes should have been removed!"); + + // At this point, we can safely insert a gc.relocate or gc.result as the first + // instruction in Ret if needed. + return Ret; +} + +// List of all function attributes which must be stripped when lowering from +// abstract machine model to physical machine model. Essentially, these are +// all the effects a safepoint might have which we ignored in the abstract +// machine model for purposes of optimization. We have to strip these on +// both function declarations and call sites. +static constexpr Attribute::AttrKind FnAttrsToStrip[] = { + Attribute::ReadNone, + Attribute::ReadOnly, + Attribute::WriteOnly, + Attribute::ArgMemOnly, + Attribute::InaccessibleMemOnly, + Attribute::InaccessibleMemOrArgMemOnly, + Attribute::NoSync, + Attribute::NoFree}; + +// Create new attribute set containing only attributes which can be transferred +// from original call to the safepoint. +static AttributeList legalizeCallAttributes(CallBase *Call, + AttributeList StatepointAL) { + AttributeList OrigAL = Call->getAttributes(); + if (OrigAL.isEmpty()) + return StatepointAL; + + // Remove the readonly, readnone, and statepoint function attributes. + LLVMContext &Ctx = Call->getContext(); + AttrBuilder FnAttrs(Ctx, OrigAL.getFnAttrs()); + for (auto Attr : FnAttrsToStrip) + FnAttrs.removeAttribute(Attr); + + for (Attribute A : OrigAL.getFnAttrs()) { + if (isStatepointDirectiveAttr(A)) + FnAttrs.removeAttribute(A); + } + + StatepointAL = StatepointAL.addFnAttributes(Ctx, FnAttrs); + + // Attach the argument attributes from the original call at the corresponding + // arguments in the statepoint. Note that any argument attributes that are + // invalid after lowering are stripped in stripNonValidDataFromBody. + for (unsigned I : llvm::seq(0, Call->arg_size())) { + AttributeSet AS = OrigAL.getParamAttrs(I); + if (AS.hasAttribute(Attribute::ByVal) || + AS.hasAttribute(Attribute::StructRet)) { + StatepointAL = StatepointAL.addParamAttributes( + Ctx, GCStatepointInst::CallArgsBeginPos + I, AttrBuilder(Ctx, AS)); + } + } + + // Return attributes are later attached to the gc.result intrinsic. + return StatepointAL; +} + +/// Helper function to place all gc relocates necessary for the given +/// statepoint. +/// Inputs: +/// liveVariables - list of variables to be relocated. +/// basePtrs - base pointers. +/// statepointToken - statepoint instruction to which relocates should be +/// bound. +/// Builder - Llvm IR builder to be used to construct new calls. +static void CreateGCRelocates(ArrayRef LiveVariables, + ArrayRef BasePtrs, + Instruction *StatepointToken, + IRBuilder<> &Builder) { + if (LiveVariables.empty()) + return; + + auto FindIndex = [](ArrayRef LiveVec, Value *Val) { + auto ValIt = llvm::find(LiveVec, Val); + assert(ValIt != LiveVec.end() && "Val not found in LiveVec!"); + size_t Index = std::distance(LiveVec.begin(), ValIt); + assert(Index < LiveVec.size() && "Bug in std::find?"); + return Index; + }; + Module *M = StatepointToken->getModule(); + + // All gc_relocate are generated as i8 addrspace(1)* (or a vector type whose + // element type is i8 addrspace(1)*). We originally generated unique + // declarations for each pointer type, but this proved problematic because + // the intrinsic mangling code is incomplete and fragile. Since we're moving + // towards a single unified pointer type anyways, we can just cast everything + // to an i8* of the right address space. A bitcast is added later to convert + // gc_relocate to the actual value's type. + auto getGCRelocateDecl = [&](Type *Ty) { + assert(isHandledGCPointerType(Ty)); + auto AS = Ty->getScalarType()->getPointerAddressSpace(); + Type *NewTy = Type::getInt8PtrTy(M->getContext(), AS); + if (auto *VT = dyn_cast(Ty)) + NewTy = FixedVectorType::get(NewTy, + cast(VT)->getNumElements()); + return Intrinsic::getDeclaration(M, Intrinsic::cj_gc_relocate, {NewTy}); + }; + + // Lazily populated map from input types to the canonicalized form mentioned + // in the comment above. This should probably be cached somewhere more + // broadly. + DenseMap TypeToDeclMap; + + for (unsigned i = 0; i < LiveVariables.size(); i++) { + // Generate the gc.relocate call and save the result + Value *BaseIdx = Builder.getInt32(FindIndex(LiveVariables, BasePtrs[i])); + Value *LiveIdx = Builder.getInt32(i); + + Type *Ty = LiveVariables[i]->getType(); + // How to find the real slot need to be relocated + if (Ty->isStructTy()) + continue; + + if (!TypeToDeclMap.count(Ty)) + TypeToDeclMap[Ty] = getGCRelocateDecl(Ty); + Function *GCRelocateDecl = TypeToDeclMap[Ty]; + + // only specify a debug name if we can give a useful one + CallInst *Reloc = + Builder.CreateCall(GCRelocateDecl, {StatepointToken, BaseIdx, LiveIdx}, + suffixed_name_or(LiveVariables[i], ".reloc", "")); + // Trick CodeGen into thinking there are lots of free registers at this + // fake call. + Reloc->setCallingConv(CallingConv::Cold); + } +} + +namespace { + +/// This struct is used to defer RAUWs and `eraseFromParent` s. Using this +/// avoids having to worry about keeping around dangling pointers to Values. +class DeferredReplacement { + AssertingVH Old; + AssertingVH New; + + DeferredReplacement() = default; + +public: + static DeferredReplacement createRAUW(Instruction *Old, Instruction *New) { + assert(Old != New && Old && New && + "Cannot RAUW equal values or to / from null!"); + + DeferredReplacement D; + D.Old = Old; + D.New = New; + return D; + } + + static DeferredReplacement createDelete(Instruction *ToErase) { + DeferredReplacement D; + D.Old = ToErase; + return D; + } + + /// Does the task represented by this instance. + void doReplacement() { + Instruction *OldI = Old; + Instruction *NewI = New; + + assert(OldI != NewI && "Disallowed at construction?!"); + + Old = nullptr; + New = nullptr; + + if (NewI) + OldI->replaceAllUsesWith(NewI); + + OldI->eraseFromParent(); + } +}; + +} // end anonymous namespace + +static void setStatepointDebugLocation(IRBuilder<> &Builder, CallBase *CI) { + auto IsSafepoint = [&CI]() { + if (Function *Callee = CI->getCalledFunction()) { + if (Callee->hasFnAttribute("gc-safepoint")) { + return true; + } + } + return false; + }; + // The debuginfo of safepoint and its relocation is not required. + // Currently, the debuginfo of the safepoint is empty. + if (IsSafepoint()) { + Builder.SetCurrentDebugLocation(CI->getDebugLoc()); + } else { + Instruction *NextI = CI->getNextNode(); + // If the next instruction of the callsite is intrinsic dbg_declare, + // we skip it and use the next debuginfo. + if (auto II = dyn_cast(NextI)) { + unsigned IID = II->getIntrinsicID(); + if (IID == Intrinsic::dbg_declare || IID == Intrinsic::dbg_value || + IID == Intrinsic::dbg_addr || IID == Intrinsic::dbg_label) { + NextI = NextI->getNextNode(); + } + } + Builder.SetCurrentDebugLocation(NextI->getDebugLoc()); + } +} + +static void +makeStatepointExplicitImpl(CallBase *Call, /* to replace */ + const SmallVectorImpl &BasePtrs, + const SmallVectorImpl &LiveVariables, + SafepointRecord &Result, + std::vector &Replacements) { + assert(BasePtrs.size() == LiveVariables.size()); + + // Then go ahead and use the builder do actually do the inserts. We insert + // immediately before the previous instruction under the assumption that all + // arguments will be available here. We can't insert afterwards since we may + // be replacing a terminator. + IRBuilder<> Builder(Call); + + ArrayRef GCArgs(LiveVariables); + uint64_t StatepointID = getCJStatepointID(Call); + ; + uint32_t NumPatchBytes = 0; + uint32_t Flags = uint32_t(StatepointFlags::None); + + SmallVector CallArgs(Call->args()); + ArrayRef FieldArgs(Result.FieldSet.getArrayRef()); + FunctionCallee CallTarget(Call->getFunctionType(), Call->getCalledOperand()); + + // Create the statepoint given all the arguments + GCStatepointInst *Token = nullptr; + if (auto *CI = dyn_cast(Call)) { + CallInst *SPCall = Builder.CreateCJGCStatepointCall( + StatepointID, NumPatchBytes, CallTarget, Flags, CallArgs, GCArgs, + FieldArgs, "token"); + + SPCall->setTailCallKind(CI->getTailCallKind()); + SPCall->setCallingConv(CI->getCallingConv()); + + // set up function attrs directly on statepoint and return attrs later for + // gc_result intrinsic. + SPCall->setAttributes(legalizeCallAttributes(CI, SPCall->getAttributes())); + + Token = cast(SPCall); + + // Put the following gc_result and gc_relocate calls immediately after the + // the old call (which we're about to delete) + assert(CI->getNextNode() && "Not a terminator, must have next!"); + Builder.SetInsertPoint(CI->getNextNode()); + setStatepointDebugLocation(Builder, CI); + } else { + auto *II = cast(Call); + + // Insert the new invoke into the old block. We'll remove the old one in a + // moment at which point this will become the new terminator for the + // original block. + InvokeInst *SPInvoke = Builder.CreateCJGCStatepointInvoke( + StatepointID, NumPatchBytes, CallTarget, II->getNormalDest(), + II->getUnwindDest(), Flags, CallArgs, GCArgs, FieldArgs, "token"); + + SPInvoke->setCallingConv(II->getCallingConv()); + + // set up function attrs directly on statepoint and return attrs later for + // gc_result intrinsic. + SPInvoke->setAttributes( + legalizeCallAttributes(II, SPInvoke->getAttributes())); + + Token = cast(SPInvoke); + + // Generate gc relocates in exceptional path + BasicBlock *UnwindBlock = II->getUnwindDest(); + assert((BasePtrs.empty() || (!isa(UnwindBlock->begin()) && + UnwindBlock->getUniquePredecessor())) && + "can't safely insert in this block!"); + + Builder.SetInsertPoint(&*UnwindBlock->getFirstInsertionPt()); + Builder.SetCurrentDebugLocation(II->getDebugLoc()); + + // Attach exceptional gc relocates to the landingpad. + Instruction *ExceptionalToken = UnwindBlock->getLandingPadInst(); + Result.UnwindToken = ExceptionalToken; + + CreateGCRelocates(LiveVariables, BasePtrs, ExceptionalToken, Builder); + + // Generate gc relocates and returns for normal block + BasicBlock *NormalDest = II->getNormalDest(); + assert((BasePtrs.empty() || (!isa(NormalDest->begin()) && + NormalDest->getUniquePredecessor())) && + "can't safely insert in this block!"); + + Builder.SetInsertPoint(&*NormalDest->getFirstInsertionPt()); + + // gc relocates will be generated later as if it were regular call + // statepoint + } + assert(Token && "Should be set in one of the above branches!"); + + Token->setName("token"); + if (!Call->getType()->isVoidTy() && !Call->use_empty()) { + StringRef Name = Call->hasName() ? Call->getName() : ""; + CallInst *GCResult = Builder.CreateCJGCResult(Token, Call->getType(), Name); + GCResult->setAttributes( + AttributeList::get(GCResult->getContext(), AttributeList::ReturnIndex, + Call->getAttributes().getRetAttrs())); + + // We cannot RAUW or delete CS.getInstruction() because it could be in the + // live set of some other safepoint, in which case that safepoint's + // SafepointRecord will hold a raw pointer to this + // llvm::Instruction. Instead, we defer the replacement and deletion to + // after the live sets have been made explicit in the IR, and we no longer + // have raw pointers to worry about. + Replacements.emplace_back(DeferredReplacement::createRAUW(Call, GCResult)); + } else { + Replacements.emplace_back(DeferredReplacement::createDelete(Call)); + } + + Result.StatepointToken = Token; + + // Second, create a gc.relocate for every live variable + CreateGCRelocates(LiveVariables, BasePtrs, Token, Builder); +} + +// Replace an existing gc.statepoint with a new one and a set of gc.relocates +// which make the relocations happening at this safepoint explicit. +// +// WARNING: Does not do any fixup to adjust users of the original live +// values. That's the callers responsibility. +static void +makeStatepointExplicit(DominatorTree &DT, CallBase *Call, + SafepointRecord &Result, + std::vector &Replacements, + const PointerToBaseTy &PointerToBase) { + const auto &LiveSet = Result.LiveSet; + + // Convert to vector for efficient cross referencing. + SmallVector BaseVec, LiveVec; + LiveVec.reserve(LiveSet.size()); + BaseVec.reserve(LiveSet.size()); + for (Value *L : LiveSet) { + LiveVec.push_back(L); + assert(PointerToBase.count(L)); + Value *Base = PointerToBase.find(L)->second; + BaseVec.push_back(Base); + } + assert(LiveVec.size() == BaseVec.size()); + + // Do the actual rewriting and delete the old statepoint + makeStatepointExplicitImpl(Call, BaseVec, LiveVec, Result, Replacements); +} + +// Helper function for the relocationViaAlloca. +// +// It receives iterator to the statepoint gc relocates and emits a store to the +// assigned location (via allocaMap) for the each one of them. It adds the +// visited values into the visitedLiveValues set, which we will later use them +// for validation checking. +static void +insertRelocationStores(iterator_range GCRelocs, + DenseMap &AllocaMap, + DenseSet &VisitedLiveValues) { + auto insertStore = [&](Value *OriginalValue, Instruction *Relocate) { + assert(AllocaMap.count(OriginalValue)); + Value *Alloca = AllocaMap[OriginalValue]; + + // Emit store into the related alloca + // All gc_relocates are i8 addrspace(1)* typed, and it must be bitcasted to + // the correct type according to alloca. + assert(Relocate->getNextNode() && + "Should always have one since it's not a terminator"); + IRBuilder<> Builder(Relocate->getNextNode()); + Value *CastedRelocatedValue = Builder.CreateBitCast( + Relocate, cast(Alloca)->getAllocatedType(), + suffixed_name_or(Relocate, ".casted", "")); + + new StoreInst(CastedRelocatedValue, Alloca, + cast(CastedRelocatedValue)->getNextNode()); + +#ifndef NDEBUG + VisitedLiveValues.insert(OriginalValue); +#endif + }; + + for (User *U : GCRelocs) { + if (auto Relocate = dyn_cast(U)) { + insertStore(Relocate->getDerivedPtr(), Relocate); + } + } +} + +// Helper function for the "relocationViaAlloca". Similar to the +// "insertRelocationStores" but works for rematerialized values. +static void insertRematerializationStores( + const RematerializedValueMapTy &RematerializedValues, + DenseMap &AllocaMap, + DenseSet &VisitedLiveValues) { + for (auto RematerializedValuePair : RematerializedValues) { + Instruction *RematerializedValue = RematerializedValuePair.first; + Value *OriginalValue = RematerializedValuePair.second; + + assert(AllocaMap.count(OriginalValue) && + "Can not find alloca for rematerialized value"); + Value *Alloca = AllocaMap[OriginalValue]; + + new StoreInst(RematerializedValue, Alloca, + RematerializedValue->getNextNode()); + +#ifndef NDEBUG + VisitedLiveValues.insert(OriginalValue); +#endif + } +} + +static void replaceUsesOfWith(Instruction *I, Value *From, Value *To) { + if (From == To) + return; + + if (I->getMetadata("noreloc")) + return; + + assert((!isa(I) || isa(I)) && + "Cannot call replaceUsesOfWith on a constant!"); + + unsigned StructLiveBegin = 0; + unsigned StructLiveEnd = 0; + bool HasStructLive = false; + if (auto Statepoint = dyn_cast_or_null(I)) { + if (!Statepoint->struct_args().empty()) { + StructLiveBegin = + Statepoint->struct_args_begin() - Statepoint->arg_begin(); + StructLiveEnd = Statepoint->struct_args_end() - Statepoint->arg_begin(); + HasStructLive = true; + } + } + + for (unsigned i = 0, E = I->getNumOperands(); i != E; ++i) { + if (I->getOperand(i) == From) { + if (HasStructLive && i >= StructLiveBegin && i < StructLiveEnd) + continue; + + I->setOperand(i, To); + } + } + if (auto DVI = dyn_cast_or_null(I)) { + if (is_contained(DVI->location_ops(), From)) { + DVI->replaceVariableLocationOp(From, To); + } + } +} + +/// Do all the relocation update via allocas and mem2reg +static void relocationViaAlloca(Function &F, DominatorTree &DT, + ArrayRef Live, + ArrayRef Records) { +#ifndef NDEBUG + // record initial number of (static) allocas; we'll check we have the same + // number when we get done. + int InitialAllocaNum = 0; + for (Instruction &I : F.getEntryBlock()) + if (isa(I)) + InitialAllocaNum++; +#endif + + // TODO-PERF: change data structures, reserve + DenseMap AllocaMap; + SmallVector PromotableAllocas; + // Used later to chack that we have enough allocas to store all values + std::size_t NumRematerializedValues = 0; + PromotableAllocas.reserve(Live.size()); + + // Emit alloca for "LiveValue" and record it in "allocaMap" and + // "PromotableAllocas" + const DataLayout &DL = F.getParent()->getDataLayout(); + auto emitAllocaFor = [&](Value *LiveValue) { + AllocaInst *Alloca = + new AllocaInst(LiveValue->getType(), DL.getAllocaAddrSpace(), "", + F.getEntryBlock().getFirstNonPHI()); + AllocaMap[LiveValue] = Alloca; + PromotableAllocas.push_back(Alloca); + }; + + // Emit alloca for each live gc pointer + for (Value *V : Live) + emitAllocaFor(V); + + // Emit allocas for rematerialized values + for (const auto &Info : Records) + for (auto RematerializedValuePair : Info.RematerializedValues) { + Value *OriginalValue = RematerializedValuePair.second; + if (AllocaMap.count(OriginalValue) != 0) + continue; + + emitAllocaFor(OriginalValue); + ++NumRematerializedValues; + } + + // The next two loops are part of the same conceptual operation. We need to + // insert a store to the alloca after the original def and at each + // redefinition. We need to insert a load before each use. These are split + // into distinct loops for performance reasons. + + // Update gc pointer after each statepoint: either store a relocated value or + // null (if no relocated value was found for this gc pointer and it is not a + // gc_result). This must happen before we update the statepoint with load of + // alloca otherwise we lose the link between statepoint and old def. + for (const auto &Info : Records) { + Value *Statepoint = Info.StatepointToken; + + // This will be used for consistency check + DenseSet VisitedLiveValues; + + // Insert stores for normal statepoint gc relocates + insertRelocationStores(Statepoint->users(), AllocaMap, VisitedLiveValues); + + // In case if it was invoke statepoint + // we will insert stores for exceptional path gc relocates. + if (isa(Statepoint)) { + insertRelocationStores(Info.UnwindToken->users(), AllocaMap, + VisitedLiveValues); + } + + // Do similar thing with rematerialized values + insertRematerializationStores(Info.RematerializedValues, AllocaMap, + VisitedLiveValues); + } + + // Update use with load allocas and add store for gc_relocated. + for (auto Pair : AllocaMap) { + Value *Def = Pair.first; + AllocaInst *Alloca = Pair.second; + + // We pre-record the uses of allocas so that we dont have to worry about + // later update that changes the user information.. + + SmallVector Uses; + // PERF: trade a linear scan for repeated reallocation + Uses.reserve(Def->getNumUses()); + for (User *U : Def->users()) { + if (!isa(U)) { + // If the def has a ConstantExpr use, then the def is either a + // ConstantExpr use itself or null. In either case + // (recursively in the first, directly in the second), the oop + // it is ultimately dependent on is null and this particular + // use does not need to be fixed up. + Uses.push_back(cast(U)); + } + } + + llvm::sort(Uses); + auto Last = std::unique(Uses.begin(), Uses.end()); + Uses.erase(Last, Uses.end()); + + for (Instruction *Use : Uses) { + if (isa(Use)) { + PHINode *Phi = cast(Use); + for (unsigned i = 0; i < Phi->getNumIncomingValues(); i++) { + if (Def == Phi->getIncomingValue(i)) { + LoadInst *Load = + new LoadInst(Alloca->getAllocatedType(), Alloca, "", + Phi->getIncomingBlock(i)->getTerminator()); + Phi->setIncomingValue(i, Load); + } + } + } else { + LoadInst *Load = + new LoadInst(Alloca->getAllocatedType(), Alloca, "", Use); + replaceUsesOfWith(Use, Def, Load); + } + } + + // Emit store for the initial gc value. Store must be inserted after load, + // otherwise store will be in alloca's use list and an extra load will be + // inserted before it. + StoreInst *Store = new StoreInst(Def, Alloca, /*volatile*/ false, + DL.getABITypeAlign(Def->getType())); + if (Instruction *Inst = dyn_cast(Def)) { + if (InvokeInst *Invoke = dyn_cast(Inst)) { + // InvokeInst is a terminator so the store need to be inserted into its + // normal destination block. + BasicBlock *NormalDest = Invoke->getNormalDest(); + Store->insertBefore(NormalDest->getFirstNonPHI()); + } else { + assert(!Inst->isTerminator() && + "The only terminator that can produce a value is " + "InvokeInst which is handled above."); + Store->insertAfter(Inst); + } + } else { + assert(isa(Def)); + Store->insertAfter(cast(Alloca)); + } + } + + assert(PromotableAllocas.size() == Live.size() + NumRematerializedValues && + "we must have the same allocas with lives"); + (void)NumRematerializedValues; + if (!PromotableAllocas.empty()) { + // Apply mem2reg to promote alloca to SSA + PromoteMemToReg(PromotableAllocas, DT); + } + +#ifndef NDEBUG + for (auto &I : F.getEntryBlock()) + if (isa(I)) + InitialAllocaNum--; + assert(InitialAllocaNum == 0 && "We must not introduce any extra allocas"); +#endif +} + +/// Implement a unique function which doesn't require we sort the input +/// vector. Doing so has the effect of changing the output of a couple of +/// tests in ways which make them less useful in testing fused safepoints. +template static void unique_unsorted(SmallVectorImpl &Vec) { + SmallSet Seen; + erase_if(Vec, [&](const T &V) { return !Seen.insert(V).second; }); +} + +/// Insert holders so that each Value is obviously live through the entire +/// lifetime of the call. +static void insertUseHolderAfter(CallBase *Call, const ArrayRef Values, + SmallVectorImpl &Holders) { + if (Values.empty()) + // No values to hold live, might as well not insert the empty holder + return; + + Module *M = Call->getModule(); + // Use a dummy vararg function to actually hold the values live + FunctionCallee Func = M->getOrInsertFunction( + "__tmp_use", FunctionType::get(Type::getVoidTy(M->getContext()), true)); + if (isa(Call)) { + // For call safepoints insert dummy calls right after safepoint + Holders.push_back( + CallInst::Create(Func, Values, "", &*++Call->getIterator())); + return; + } + // For invoke safepooints insert dummy calls both in normal and + // exceptional destination blocks + auto *II = cast(Call); + Holders.push_back(CallInst::Create( + Func, Values, "", &*II->getNormalDest()->getFirstInsertionPt())); + Holders.push_back(CallInst::Create( + Func, Values, "", &*II->getUnwindDest()->getFirstInsertionPt())); +} + +static unsigned computeStructTypeOffsets(StructType *ST, + MapVector &StructDL, + const DataLayout &DL, + uint64_t BaseOff = 0); + +static unsigned computeArrayTypeOffsets(ArrayType *AT, + MapVector &StructDL, + const DataLayout &DL, + uint64_t BaseOff) { + unsigned GCFieldNum = 0; + uint64_t Size = AT->getNumElements(); + Type *ElementType = AT->getElementType(); + if (isa(ElementType)) { + for (unsigned Index = 0; Index < Size; Index++) { + uint64_t EleOff = BaseOff + Index * 8; // 8: pointer size + StructDL[EleOff] = true; + } + GCFieldNum += Size; + } else if (StructType *ArrayStruct = dyn_cast(ElementType)) { + int64_t DerivedOffset = DL.getStructLayout(ArrayStruct)->getSizeInBytes(); + for (unsigned Index = 0; Index < Size; Index++) { + GCFieldNum += computeStructTypeOffsets(ArrayStruct, StructDL, DL, + BaseOff + Index * DerivedOffset); + } + } + return GCFieldNum; +} + +// StructDL: +// Given a StrcutType and Calculate the GCPointer layout and return the +// number of gcptr fields. +static unsigned computeStructTypeOffsets(StructType *ST, + MapVector &StructDL, + const DataLayout &DL, + uint64_t BaseOff) { + unsigned GCFieldNum = 0; + const StructLayout *Layout = DL.getStructLayout(ST); + for (uint32_t STNum = 0; STNum < ST->getNumElements(); STNum++) { + auto Offset = Layout->getElementOffset(STNum) + BaseOff; + auto ET = ST->getElementType(STNum); + if (auto PT = dyn_cast(ET)) { + if (isGCPointerType(PT)) { + StructDL[Offset] = true; + GCFieldNum++; + } + } else if (auto EST = dyn_cast(ET)) { + GCFieldNum += computeStructTypeOffsets(EST, StructDL, DL, Offset); + } else if (auto EAT = dyn_cast(ET)) { + GCFieldNum += computeArrayTypeOffsets(EAT, StructDL, DL, Offset); + } else { + StructDL[Offset] = false; + } + } + return GCFieldNum; +} + +static void computeStructTypeLayouts(Function &F, + AllocaAnalysisData &AllocaData) { + // Collect all AllocaInsts + for (auto &I : F.getEntryBlock()) { + auto *AI = dyn_cast(&I); + if (!AI) + continue; + + auto *ST = dyn_cast(AI->getAllocatedType()); + if (ST && containsGCPtrType(ST)) { + unsigned GCFieldNum = + computeStructTypeOffsets(ST, AllocaData.StructLayoutGCPtrMap[AI], + F.getParent()->getDataLayout()); + if (GCFieldNum > StructContainGCFieldThreshold) + AllocaData.LargeStructs.insert(AI); + + AllocaData.StructGCFieldNum[AI] = GCFieldNum; + AllocaData.Structs.insert(AI); + } + } +} + +// Calculate the mapping table between phi and alloca. +static void computePHINodeToAllocaInsts(Function &F, + PhiToAllocasTy &PhiToAllocasMap) { + // clean up before use PhiToAllocasMap. + PhiToAllocasMap.clear(); + for (auto &I : instructions(F)) { + if (!isa(&I) && !isa(&I)) { + continue; + } + if (!I.getType()->isPointerTy()) { + continue; + } + SetVector BaseSet; + if (!findAllocaInsts(&I, BaseSet)) { + continue; + } + PhiToAllocasMap.insert({&I, BaseSet}); + } +} + +static void computeAllocaAnalysisData(Function &F, + AllocaAnalysisData &AllocaData) { + computeStructTypeLayouts(F, AllocaData); + computePHINodeToAllocaInsts(F, AllocaData.PhiToAllocasMap); +} + +static bool isNoopCast(CastInst *CI, const DataLayout &DL) { + return CI->isNoopCast(DL) || isa(CI); +} + +// Helper function for the "rematerializeLiveValues". It walks use chain +// starting from the "CurrentValue" until it reaches the root of the chain, i.e. +// the base or a value it cannot process. Only "simple" values are processed +// (currently it is GEP's and casts). The returned root is examined by the +// callers of findRematerializableChainToBasePointer. Fills "ChainToBase" array +// with all visited values. +static Value *findRematerializableChainToBasePointer( + SmallVectorImpl &ChainToBase, Value *CurrentValue) { + if (GetElementPtrInst *GEP = dyn_cast(CurrentValue)) { + ChainToBase.push_back(GEP); + return findRematerializableChainToBasePointer(ChainToBase, + GEP->getPointerOperand()); + } + + if (CastInst *CI = dyn_cast(CurrentValue)) { + if (!isNoopCast(CI, CI->getModule()->getDataLayout())) + return CI; + + ChainToBase.push_back(CI); + return findRematerializableChainToBasePointer(ChainToBase, + CI->getOperand(0)); + } + + // We have reached the root of the chain, which is either equal to the base or + // is the first unsupported value along the use chain. + return CurrentValue; +} + +// Helper function for the "rematerializeLiveValues". Compute cost of the use +// chain we are going to rematerialize. +static InstructionCost +chainToBasePointerCost(SmallVectorImpl &Chain, + TargetTransformInfo &TTI) { + InstructionCost Cost = 0; + + for (Instruction *Instr : Chain) { + if (CastInst *CI = dyn_cast(Instr)) { + assert(isNoopCast(CI, CI->getModule()->getDataLayout()) && + "non noop cast is found during rematerialization"); + + Type *SrcTy = CI->getOperand(0)->getType(); + Cost += TTI.getCastInstrCost(CI->getOpcode(), CI->getType(), SrcTy, + TTI::getCastContextHint(CI), + TargetTransformInfo::TCK_SizeAndLatency, CI); + } else if (GetElementPtrInst *GEP = dyn_cast(Instr)) { + // Cost of the address calculation + Type *ValTy = GEP->getSourceElementType(); + Cost += TTI.getAddressComputationCost(ValTy); + + // And cost of the GEP itself + // TODO: Use TTI->getGEPCost here (it exists, but appears to be not + // allowed for the external usage) + if (!GEP->hasAllConstantIndices()) + Cost += 2; + } else { + llvm_unreachable("unsupported instruction type during rematerialization"); + } + } + + return Cost; +} + +static bool AreEquivalentPhiNodes(PHINode &OrigRootPhi, + PHINode &AlternateRootPhi) { + unsigned PhiNum = OrigRootPhi.getNumIncomingValues(); + if (PhiNum != AlternateRootPhi.getNumIncomingValues() || + OrigRootPhi.getParent() != AlternateRootPhi.getParent()) + return false; + // Map of incoming values and their corresponding basic blocks of + // OrigRootPhi. + SmallDenseMap CurrentIncomingValues; + for (unsigned i = 0; i < PhiNum; i++) + CurrentIncomingValues[OrigRootPhi.getIncomingValue(i)] = + OrigRootPhi.getIncomingBlock(i); + + // Both current and base PHIs should have same incoming values and + // the same basic blocks corresponding to the incoming values. + for (unsigned i = 0; i < PhiNum; i++) { + auto CIVI = + CurrentIncomingValues.find(AlternateRootPhi.getIncomingValue(i)); + if (CIVI == CurrentIncomingValues.end()) + return false; + BasicBlock *CurrentIncomingBB = CIVI->second; + if (CurrentIncomingBB != AlternateRootPhi.getIncomingBlock(i)) + return false; + } + return true; +} + +// Find derived pointers that can be recomputed cheap enough and fill +// RematerizationCandidates with such candidates. +static void +findRematerializationCandidates(PointerToBaseTy PointerToBase, + RematCandTy &RematerizationCandidates, + TargetTransformInfo &TTI) { + const unsigned int ChainLengthThreshold = 10; + + for (auto P2B : PointerToBase) { + auto *Derived = P2B.first; + auto *Base = P2B.second; + // Consider only derived pointers. + if (Derived == Base) + continue; + + // For each live pointer find its defining chain. + SmallVector ChainToBase; + Value *RootOfChain = + findRematerializableChainToBasePointer(ChainToBase, Derived); + + // Nothing to do, or chain is too long + if (ChainToBase.size() == 0 || ChainToBase.size() > ChainLengthThreshold) + continue; + + // Handle the scenario where the RootOfChain is not equal to the + // Base Value, but they are essentially the same phi values. + if (RootOfChain != PointerToBase[Derived]) { + PHINode *OrigRootPhi = dyn_cast(RootOfChain); + PHINode *AlternateRootPhi = dyn_cast(PointerToBase[Derived]); + if (!OrigRootPhi || !AlternateRootPhi) + continue; + // PHI nodes that have the same incoming values, and belonging to the same + // basic blocks are essentially the same SSA value. When the original phi + // has incoming values with different base pointers, the original phi is + // marked as conflict, and an additional `AlternateRootPhi` with the same + // incoming values get generated by the findBasePointer function. We need + // to identify the newly generated AlternateRootPhi (.base version of phi) + // and RootOfChain (the original phi node itself) are the same, so that we + // can rematerialize the gep and casts. This is a workaround for the + // deficiency in the findBasePointer algorithm. + if (!AreEquivalentPhiNodes(*OrigRootPhi, *AlternateRootPhi)) + continue; + } + // Compute cost of this chain. + InstructionCost Cost = chainToBasePointerCost(ChainToBase, TTI); + // TODO: We can also account for cases when we will be able to remove some + // of the rematerialized values by later optimization passes. I.e if + // we rematerialized several intersecting chains. Or if original + // values don't have any uses besides this statepoint. + + // Ok, there is a candidate. + RematerizlizationCandidateRecord Record; + Record.ChainToBase = ChainToBase; + Record.RootOfChain = RootOfChain; + Record.Cost = Cost; + RematerizationCandidates.insert({Derived, Record}); + } +} + +// Utility function which clones all instructions from "ChainToBase" +// and inserts them before "InsertBefore". Returns rematerialized value +// which should be used after statepoint. +static Instruction *rematerializeChain(ArrayRef ChainToBase, + Instruction *InsertBefore, + Value *RootOfChain, + Value *AlternateLiveBase) { + Instruction *LastClonedValue = nullptr; + Instruction *LastValue = nullptr; + // Walk backwards to visit top-most instructions first. + for (Instruction *Instr : + make_range(ChainToBase.rbegin(), ChainToBase.rend())) { + // Only GEP's and casts are supported as we need to be careful to not + // introduce any new uses of pointers not in the liveset. + // Note that it's fine to introduce new uses of pointers which were + // otherwise not used after this statepoint. + assert(isa(Instr) || isa(Instr)); + Instruction *ClonedValue = Instr->clone(); + ClonedValue->insertBefore(InsertBefore); + ClonedValue->setName(Instr->getName() + ".remat"); + if (auto PrevInst = ClonedValue->getPrevNonDebugInstruction()) { + ClonedValue->setDebugLoc(PrevInst->getDebugLoc()); + } + + // If it is not first instruction in the chain then it uses previously + // cloned value. We should update it to use cloned value. + if (LastClonedValue) { + assert(LastValue); + ClonedValue->replaceUsesOfWith(LastValue, LastClonedValue); +#ifndef NDEBUG + for (auto *OpValue : ClonedValue->operand_values()) { + // Assert that cloned instruction does not use any instructions from + // this chain other than LastClonedValue + assert(!is_contained(ChainToBase, OpValue) && + "incorrect use in rematerialization chain"); + // Assert that the cloned instruction does not use the RootOfChain + // or the AlternateLiveBase. + assert(OpValue != RootOfChain && OpValue != AlternateLiveBase); + } +#endif + } else { + // For the first instruction, replace the use of unrelocated base i.e. + // RootOfChain/OrigRootPhi, with the corresponding PHI present in the + // live set. They have been proved to be the same PHI nodes. Note + // that the *only* use of the RootOfChain in the ChainToBase list is + // the first Value in the list. + if (RootOfChain != AlternateLiveBase) + ClonedValue->replaceUsesOfWith(RootOfChain, AlternateLiveBase); + } + LastClonedValue = ClonedValue; + LastValue = Instr; + } + assert(LastClonedValue); + return LastClonedValue; +} + +// Try to rematerialize derived pointers immediately before their uses +// (instead of rematerializing after every statepoint it is live through). +// This can be beneficial when derived pointer is live across many +// statepoints, but uses are rare. +static void +rematerializeLiveValuesAtUses(RematCandTy &RematerizationCandidates, + MutableArrayRef Records, + PointerToBaseTy &PointerToBase) { + if (!RematDerivedAtUses) + return; + + SmallVector LiveValuesToBeDeleted; + + LLVM_DEBUG(dbgs() << "Rematerialize derived pointers at uses, " + << "Num statepoints: " << Records.size() << '\n'); + + for (auto &It : RematerizationCandidates) { + Instruction *Cand = cast(It.first); + auto &Record = It.second; + + if (Record.Cost >= RematerializationThreshold) + continue; + + if (Cand->user_empty()) + continue; + + if (Cand->hasOneUse()) + if (auto *U = dyn_cast(Cand->getUniqueUndroppableUser())) + if (U->getParent() == Cand->getParent()) + continue; + + // Rematerialization before PHI nodes is not implemented. + if (llvm::any_of(Cand->users(), + [](const auto *U) { return isa(U); })) + continue; + + LLVM_DEBUG(dbgs() << "Trying cand " << *Cand << " ... "); + + // Count of rematerialization instructions we introduce is equal to number + // of candidate uses. + // Count of rematerialization instructions we eliminate is equal to number + // of statepoints it is live through. + // Consider transformation profitable if latter is greater than former + // (in other words, we create less than eliminate). + unsigned NumLiveStatepoints = llvm::count_if( + Records, [Cand](const auto &R) { return R.LiveSet.contains(Cand); }); + unsigned NumUses = Cand->getNumUses(); + + LLVM_DEBUG(dbgs() << "Num uses: " << NumUses << " Num live statepoints: " + << NumLiveStatepoints << " "); + + if (NumLiveStatepoints < NumUses) { + LLVM_DEBUG(dbgs() << "not profitable\n"); + continue; + } + + // If rematerialization is 'free', then favor rematerialization at + // uses as it generally shortens live ranges. + if (NumLiveStatepoints == NumUses && Record.Cost > 0) { + LLVM_DEBUG(dbgs() << "not profitable\n"); + continue; + } + + LLVM_DEBUG(dbgs() << "looks profitable\n"); + + // ChainToBase may contain another remat candidate (as a sub chain) which + // has been rewritten by now. Need to recollect chain to have up to date + // value. + if (Record.ChainToBase.size() > 1) { + Record.ChainToBase.clear(); + findRematerializableChainToBasePointer(Record.ChainToBase, Cand); + } + + // Current rematerialization algorithm is very simple: we rematerialize + // immediately before EVERY use, even if there are several uses in same + // block or if use is local to Cand Def. The reason is that this allows + // us to avoid recomputing liveness without complicated analysis: + // - If we did not eliminate all uses of original Candidate, we do not + // know exaclty in what BBs it is still live. + // - If we rematerialize once per BB, we need to find proper insertion + // place (first use in block, but after Def) and analyze if there is + // statepoint between uses in the block. + while (!Cand->user_empty()) { + Instruction *UserI = cast(*Cand->user_begin()); + Instruction *RematChain = rematerializeChain( + Record.ChainToBase, UserI, Record.RootOfChain, PointerToBase[Cand]); + UserI->replaceUsesOfWith(Cand, RematChain); + PointerToBase[RematChain] = PointerToBase[Cand]; + } + LiveValuesToBeDeleted.push_back(Cand); + } + + LLVM_DEBUG(dbgs() << "Rematerialized " << LiveValuesToBeDeleted.size() + << " derived pointers\n"); + for (auto *Cand : LiveValuesToBeDeleted) { + assert(Cand->use_empty() && "Unexpected user remain"); + RematerizationCandidates.erase(Cand); + for (auto &R : Records) { + assert(!R.LiveSet.contains(Cand) || + R.LiveSet.contains(PointerToBase[Cand])); + R.LiveSet.remove(Cand); + } + } + + // Recollect not rematerialized chains - we might have rewritten + // their sub-chains. + if (!LiveValuesToBeDeleted.empty()) { + for (auto &P : RematerizationCandidates) { + auto &R = P.second; + if (R.ChainToBase.size() > 1) { + R.ChainToBase.clear(); + findRematerializableChainToBasePointer(R.ChainToBase, P.first); + } + } + } +} + +static void rematerializeLiveValues(CallBase *Call, Value *LiveValue, + SafepointRecord &Info, + PointerToBaseTy &PointerToBase, + RematerizlizationCandidateRecord &Record) { + // Clone instructions and record them inside "Info" structure. + + // Different cases for calls and invokes. For invokes we need to clone + // instructions both on normal and unwind path. + if (isa(Call)) { + Instruction *InsertBefore = Call->getNextNode(); + assert(InsertBefore); + Instruction *RematerializedValue = + rematerializeChain(Record.ChainToBase, InsertBefore, Record.RootOfChain, + PointerToBase[LiveValue]); + Info.RematerializedValues[RematerializedValue] = LiveValue; + } else { + auto *Invoke = cast(Call); + + Instruction *NormalInsertBefore = + &*Invoke->getNormalDest()->getFirstInsertionPt(); + Instruction *UnwindInsertBefore = + &*Invoke->getUnwindDest()->getFirstInsertionPt(); + + Instruction *NormalRematerializedValue = + rematerializeChain(Record.ChainToBase, NormalInsertBefore, + Record.RootOfChain, PointerToBase[LiveValue]); + Instruction *UnwindRematerializedValue = + rematerializeChain(Record.ChainToBase, UnwindInsertBefore, + Record.RootOfChain, PointerToBase[LiveValue]); + + Info.RematerializedValues[NormalRematerializedValue] = LiveValue; + Info.RematerializedValues[UnwindRematerializedValue] = LiveValue; + } +} + +// From the statepoint live set pick values that are cheaper to recompute then +// to relocate. Remove this values from the live set, rematerialize them after +// statepoint and record them in "Info" structure. Note that similar to +// relocated values we don't do any user adjustments here. +static void rematerializeLiveValues(CallBase *Call, SafepointRecord &Info, + PointerToBaseTy &PointerToBase, + RematCandTy &RematerizationCandidates) { + // Record values we are going to delete from this statepoint live set. + // We can not di this in following loop due to iterator invalidation. + SmallVector LiveValuesToBeDeleted; + + auto IsExpensiveCost = [&Call](RematerizlizationCandidateRecord &Record) { + InstructionCost Cost = Record.Cost; + // For invokes we need to rematerialize each chain twice - for normal and + // for unwind basic blocks. Model this by multiplying cost by two. + if (isa(Call)) + Cost *= 2; + + // If it's too expensive - skip it. + if (Cost >= RematerializationThreshold) + return true; + + return false; + }; + + for (Value *LiveValue : Info.LiveSet) { + auto It = RematerizationCandidates.find(LiveValue); + if (It == RematerizationCandidates.end()) + continue; + + RematerizlizationCandidateRecord &Record = It->second; + + if (IsExpensiveCost(Record)) + continue; + + // Remove value from the live set + if (Info.LiveSet.contains(LiveValue)) { + LiveValuesToBeDeleted.push_back(LiveValue); + } + rematerializeLiveValues(Call, LiveValue, Info, PointerToBase, Record); + } + + // Remove rematerializaed values from the live set + for (auto LiveValue : LiveValuesToBeDeleted) { + Info.LiveSet.remove(LiveValue); + } +} + +static void printBases(PointerToBaseTy &PointerToBase, bool Before) { + if (Before) { + errs() << "Base Pairs (w/o Relocation before the revision):\n"; + } else { + errs() << "Base Pairs (w/o Relocation after the revision):\n"; + } + for (auto &Pair : PointerToBase) { + errs() << " derived "; + Pair.first->printAsOperand(errs(), false); + errs() << " base "; + Pair.second->printAsOperand(errs(), false); + errs() << "\n"; + } +} + +static bool insertParsePoints(Function &F, DominatorTree &DT, + PostDominatorTree &PostDT, + TargetTransformInfo &TTI, + SmallVectorImpl &ToUpdate, + DefiningValueMapTy &DVCache, + IsKnownBaseMapTy &KnownBases, unsigned OptLevel) { +#ifndef NDEBUG + // Validate the input + std::set Uniqued; + Uniqued.insert(ToUpdate.begin(), ToUpdate.end()); + assert(Uniqued.size() == ToUpdate.size() && "no duplicates please!"); + + for (CallBase *Call : ToUpdate) + assert(Call->getFunction() == &F); +#endif + + // When inserting gc.relocates for invokes, we need to be able to insert at + // the top of the successor blocks. See the comment on + // normalForInvokeSafepoint on exactly what is needed. Note that this step + // may restructure the CFG. + for (CallBase *Call : ToUpdate) { + auto *II = dyn_cast(Call); + if (!II) + continue; + normalizeForInvokeSafepoint(II->getNormalDest(), II->getParent(), DT); + normalizeForInvokeSafepoint(II->getUnwindDest(), II->getParent(), DT); + } + + // A list of dummy calls added to the IR to keep various values obviously + // live in the IR. We'll remove all of these when done. + SmallVector Holders; + + // Record the analysis result data of each alloca. + AllocaAnalysisData AllocaData; + + // Prepare the analysis data for each alloca. + computeAllocaAnalysisData(F, AllocaData); + + SmallVector Records(ToUpdate.size()); + + // Prepare records for Liveness Variable Analysis + LivenessData Data(F, DT, PostDT, OptLevel, AllocaData, ToUpdate); + + // A) Identify all gc pointers which are statically live at the given call + // site. + Data.findLiveReferences(Records); + + // clear AllocaData after struct liveness analysis. + AllocaData.clear(); + + // Global mapping from gc-live pointers to a base-defining-value. + PointerToBaseTy PointerToBase; + + // B) Find the base pointers for each gc-live pointer. There is no base + // for struct-live, but we need to legalize the phi node and record the + // referenced stack objects. + for (size_t i = 0; i < Records.size(); i++) { + SafepointRecord &info = Records[i]; + findBasePointers(DT, DVCache, info, PointerToBase, KnownBases); + } + + if (PrintBasePointers) { + printBases(PointerToBase, false); + } + + // The base phi insertion logic (for any safepoint) may have inserted new + // instructions which are now live at some safepoint. The simplest such + // example is: + // loop: + // phi a <-- will be a new base_phi here + // safepoint 1 <-- that needs to be live here + // gep a + 1 + // safepoint 2 + // br loop + // We insert some dummy calls after each safepoint to definitely hold live + // the base pointers which were identified for that safepoint. We'll then + // ask liveness for _every_ base inserted to see what is now live. Then we + // remove the dummy calls. + Holders.reserve(Records.size()); + for (size_t i = 0; i < Records.size(); i++) { + SafepointRecord &Info = Records[i]; + + SmallVector Bases; + for (auto *Derived : Info.LiveSet) { + assert(PointerToBase.count(Derived) && "Missed base for derived pointer"); + Bases.push_back(PointerToBase[Derived]); + } + insertUseHolderAfter(ToUpdate[i], Bases, Holders); + } + + // By selecting base pointers, we've effectively inserted new uses. Thus, we + // need to rerun liveness. We may *also* have inserted new defs, but that's + // not the key issue. + Data.recomputeLiveInValues(Records, PointerToBase); + + if (PrintBasePointers) { + printBases(PointerToBase, true); + } + + // It is possible that non-constant live variables have a constant base. For + // example, a GEP with a variable offset from a global. In this case we can + // remove it from the liveset. We already don't add constants to the liveset + // because we assume they won't move at runtime and the GC doesn't need to be + // informed about them. The same reasoning applies if the base is constant. + // Note that the relocation placement code relies on this filtering for + // correctness as it expects the base to be in the liveset, which isn't true + // if the base is constant. + for (auto &Info : Records) { + Info.LiveSet.remove_if([&](Value *LiveV) { + assert(PointerToBase.count(LiveV) && "Missed base for derived pointer"); + return isa(PointerToBase[LiveV]); + }); + } + + for (CallInst *CI : Holders) + CI->eraseFromParent(); + + Holders.clear(); + + // Compute the cost of possible re-materialization of derived pointers. + RematCandTy RematerizationCandidates; + findRematerializationCandidates(PointerToBase, RematerizationCandidates, TTI); + + // In order to reduce live set of statepoint we might choose to rematerialize + // some values instead of relocating them. This is purely an optimization and + // does not influence correctness. + // First try rematerialization at uses, then after statepoints. + rematerializeLiveValuesAtUses(RematerizationCandidates, Records, + PointerToBase); + for (size_t i = 0; i < Records.size(); i++) + rematerializeLiveValues(ToUpdate[i], Records[i], PointerToBase, + RematerizationCandidates); + + // We need this to safely RAUW and delete call or invoke return values that + // may themselves be live over a statepoint. For details, please see usage in + // makeStatepointExplicitImpl. + std::vector Replacements; + + // This does not affect the correctness, but is purely for the readability of + // the IR. + std::reverse(ToUpdate.begin(), ToUpdate.end()); + std::reverse(Records.begin(), Records.end()); + + // Now run through and replace the existing statepoints with new ones with + // the live variables listed. We do not yet update uses of the values being + // relocated. We have references to live variables that need to + // survive to the last iteration of this loop. (By construction, the + // previous statepoint can not be a live variable, thus we can and remove + // the old statepoint calls as we go.) + for (size_t i = 0; i < Records.size(); i++) + makeStatepointExplicit(DT, ToUpdate[i], Records[i], Replacements, + PointerToBase); + + ToUpdate.clear(); // prevent accident use of invalid calls. + + for (auto &PR : Replacements) + PR.doReplacement(); + + Replacements.clear(); + + for (auto &Info : Records) { + // These live sets may contain state Value pointers, since we replaced calls + // with operand bundles with calls wrapped in gc.statepoint, and some of + // those calls may have been def'ing live gc pointers. Clear these out to + // avoid accidentally using them. + // + // TODO: We should create a separate data structure that does not contain + // these live sets, and migrate to using that data structure from this point + // onward. + Info.LiveSet.clear(); + } + PointerToBase.clear(); + + // Do all the fixups of the original live variables to their relocated selves + SmallVector Live; + for (size_t i = 0; i < Records.size(); i++) { + SafepointRecord &Info = Records[i]; + + // We can't simply save the live set from the original insertion. One of + // the live values might be the result of a call which needs a safepoint. + // That Value* no longer exists and we need to use the new gc_result. + // Thankfully, the live set is embedded in the statepoint (and updated), so + // we just grab that. + llvm::append_range(Live, Info.StatepointToken->gc_args()); +#ifndef NDEBUG + // Do some basic validation checking on our liveness results before + // performing relocation. Relocation can and will turn mistakes in liveness + // results into non-sensical code which is must harder to debug. + // TODO: It would be nice to test consistency as well + assert(DT.isReachableFromEntry(Info.StatepointToken->getParent()) && + "statepoint must be reachable or liveness is meaningless"); + for (Value *V : Info.StatepointToken->gc_args()) { + if (!isa(V)) + // Non-instruction values trivial dominate all possible uses + continue; + auto *LiveInst = cast(V); + assert(DT.isReachableFromEntry(LiveInst->getParent()) && + "unreachable values should never be live"); + assert(DT.dominates(LiveInst, Info.StatepointToken) && + "basic SSA liveness expectation violated by liveness analysis"); + } +#endif + } + unique_unsorted(Live); + +#ifndef NDEBUG + // Validation check + for (auto *Ptr : Live) + assert((isHandledGCPointerType(Ptr->getType()) || + isMemoryContainsGCPtrType(Ptr->getType())) && + "must be a gc pointer type"); +#endif + + relocationViaAlloca(F, DT, Live, Records); + return !Records.empty(); +} + +// List of all parameter and return attributes which must be stripped when +// lowering from the abstract machine model. Note that we list attributes +// here which aren't valid as return attributes, that is okay. +static AttributeMask getParamAndReturnAttributesToRemove() { + AttributeMask R; + R.addAttribute(Attribute::Dereferenceable); + R.addAttribute(Attribute::DereferenceableOrNull); + R.addAttribute(Attribute::ReadNone); + R.addAttribute(Attribute::ReadOnly); + R.addAttribute(Attribute::WriteOnly); + R.addAttribute(Attribute::NoAlias); + R.addAttribute(Attribute::NoFree); + return R; +} + +static void stripNonValidAttributesFromPrototype(Function &F) { + LLVMContext &Ctx = F.getContext(); + + // Intrinsics are very delicate. Lowering sometimes depends the presence + // of certain attributes for correctness, but we may have also inferred + // additional ones in the abstract machine model which need stripped. This + // assumes that the attributes defined in Intrinsic.td are conservatively + // correct for both physical and abstract model. + if (Intrinsic::ID id = F.getIntrinsicID()) { + F.setAttributes(Intrinsic::getAttributes(Ctx, id)); + return; + } + + AttributeMask R = getParamAndReturnAttributesToRemove(); + for (Argument &A : F.args()) + if (isa(A.getType())) + F.removeParamAttrs(A.getArgNo(), R); + + if (isa(F.getReturnType())) + F.removeRetAttrs(R); + + for (auto Attr : FnAttrsToStrip) + F.removeFnAttr(Attr); +} + +/// Certain metadata on instructions are invalid after running RS4GC. +/// Optimizations that run after RS4GC can incorrectly use this metadata to +/// optimize functions. We drop such metadata on the instruction. +static void stripInvalidMetadataFromInstruction(Instruction &I) { + if (!isa(I) && !isa(I)) + return; + // These are the attributes that are still valid on loads and stores after + // RS4GC. + // The metadata implying dereferenceability and noalias are (conservatively) + // dropped. This is because semantically, after RewriteStatepointsForGC runs, + // all calls to gc.statepoint "free" the entire heap. Also, gc.statepoint can + // touch the entire heap including noalias objects. Note: The reasoning is + // same as stripping the dereferenceability and noalias attributes that are + // analogous to the metadata counterparts. + // We also drop the invariant.load metadata on the load because that metadata + // implies the address operand to the load points to memory that is never + // changed once it became dereferenceable. This is no longer true after RS4GC. + // Similar reasoning applies to invariant.group metadata, which applies to + // loads within a group. + unsigned ValidMetadataAfterRS4GC[] = { + LLVMContext::MD_tbaa, LLVMContext::MD_range, + LLVMContext::MD_alias_scope, LLVMContext::MD_nontemporal, + LLVMContext::MD_nonnull, LLVMContext::MD_align, + LLVMContext::MD_type}; + + // Drops all metadata on the instruction other than ValidMetadataAfterRS4GC. + I.dropUnknownNonDebugMetadata(ValidMetadataAfterRS4GC); +} + +static void stripNonValidDataFromBody(Function &F) { + if (F.empty()) + return; + + LLVMContext &Ctx = F.getContext(); + MDBuilder Builder(Ctx); + + // Set of invariantstart instructions that we need to remove. + // Use this to avoid invalidating the instruction iterator. + SmallVector InvariantStartInstructions; + + for (Instruction &I : instructions(F)) { + // invariant.start on memory location implies that the referenced memory + // location is constant and unchanging. This is no longer true after + // RewriteStatepointsForGC runs because there can be calls to gc.statepoint + // which frees the entire heap and the presence of invariant.start allows + // the optimizer to sink the load of a memory location past a statepoint, + // which is incorrect. + if (auto *II = dyn_cast(&I)) + if (II->getIntrinsicID() == Intrinsic::invariant_start) { + InvariantStartInstructions.push_back(II); + continue; + } + + if (MDNode *Tag = I.getMetadata(LLVMContext::MD_tbaa)) { + MDNode *MutableTBAA = Builder.createMutableTBAAAccessTag(Tag); + I.setMetadata(LLVMContext::MD_tbaa, MutableTBAA); + } + + stripInvalidMetadataFromInstruction(I); + + AttributeMask R = getParamAndReturnAttributesToRemove(); + if (auto *Call = dyn_cast(&I)) { + for (int i = 0, e = Call->arg_size(); i != e; i++) + if (isa(Call->getArgOperand(i)->getType())) + Call->removeParamAttrs(i, R); + if (isa(Call->getType())) + Call->removeRetAttrs(R); + } + } + + // Delete the invariant.start instructions and RAUW undef. + for (auto *II : InvariantStartInstructions) { + II->replaceAllUsesWith(UndefValue::get(II->getType())); + II->eraseFromParent(); + } +} + +/// Returns true if this function should be rewritten by this pass. The main +/// point of this function is as an extension point for custom logic. +static bool shouldRewriteStatepointsIn(Function &F) { return F.hasCangjieGC(); } + +// Rewrite CJ_MCC_ThrowException with empty live set. +static bool rewriteCJThrowException(Module &M) { + if (Triple(M.getTargetTriple()).isOSWindows()) + return false; + Function *F = M.getFunction("CJ_MCC_ThrowException"); + if (!F || F->use_empty()) + return false; + SmallVector Users; + for (auto *U : F->users()) { + auto *CB = dyn_cast(U); + assert(CB != nullptr && + "User of CJ_MCC_ThrowException must be call instruction"); + Users.push_back(CB); + } + std::vector Replacements; + for (auto *CB : Users) { + SafepointRecord Result; + makeStatepointExplicitImpl(CB, SmallVector{}, + SmallVector{}, Result, Replacements); + } + for (auto &DR : Replacements) + DR.doReplacement(); + return true; +} + +void prepareBlackHoleBody(Function *F) { + F->setLinkage(GlobalValue::PrivateLinkage); + BasicBlock *BB = BasicBlock::Create(F->getContext(), "entry", F); + ReturnInst::Create(F->getContext(), F->getArg(0), BB); +} + +static bool deleteUnusedBlackHole(Module &M) { + Function *F = M.getFunction("CJ_LLVM_BlackHole"); + if (!F || F->use_empty()) + return false; + prepareBlackHoleBody(F); + SmallVector Users; + for (auto *U : F->users()) { + auto *CB = dyn_cast(U); + assert(CB != nullptr && + "User of CJ_LLVM_BlackHole must be call instruction"); + Users.push_back(CB); + } + for (auto *CB : Users) { + if (CB->use_empty()) { + CB->eraseFromParent(); + } + } + return true; +} + +static void stripNonValidData(Module &M) { +#ifndef NDEBUG + assert(llvm::any_of(M, shouldRewriteStatepointsIn) && "precondition!"); +#endif + + for (Function &F : M) + stripNonValidAttributesFromPrototype(F); + + for (Function &F : M) + stripNonValidDataFromBody(F); +} + +bool CJRewriteStatepoint::runOnFunction(Function &F, DominatorTree &DT, + PostDominatorTree &PostDT, + TargetTransformInfo &TTI, + const TargetLibraryInfo &TLI) { + assert(!F.isDeclaration() && !F.empty() && + "need function body to rewrite statepoints in"); + assert(shouldRewriteStatepointsIn(F) && "mismatch in rewrite decision"); + + auto NeedsRewrite = [&TLI](Instruction &I) { + const auto *Call = dyn_cast(&I); + if (!Call || isa(Call) || + callsGCLeafFunction(Call, TLI)) { + return false; + } + return true; + }; + + // Delete any unreachable statepoints so that we don't have unrewritten + // statepoints surviving this pass. This makes testing easier and the + // resulting IR less confusing to human readers. + DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Lazy); + bool MadeChange = removeUnreachableBlocks(F, &DTU); + // Flush the Dominator Tree. + DTU.getDomTree(); + + // record whether the func has actual callsite. + bool HasCallSite = false; + + // Gather all the statepoints which need rewritten. Be careful to only + // consider those in reachable code since we need to ask dominance queries + // when rewriting. We'll delete the unreachable ones in a moment. + SmallVector ParsePointNeeded; + // To accelerate liveness analysis, we need to store the locations that need + // to be rewritten in the BB in reverse order. + for (auto &BB : + make_range(++F.end().getReverse(), ++F.begin().getReverse())) { + for (auto &I : make_range(BB.rbegin(), BB.rend())) { + if (auto *Call = dyn_cast(&I)) { + if (!isa(Call)) + HasCallSite = true; + } + // TODO: only the ones with the flag set! + if (NeedsRewrite(I)) { + // NOTE removeUnreachableBlocks() is stronger than + // DominatorTree::isReachableFromEntry(). In other words + // removeUnreachableBlocks can remove some blocks for which + // isReachableFromEntry() returns true. + assert(DT.isReachableFromEntry(I.getParent()) && + "no unreachable blocks expected"); + ParsePointNeeded.push_back(cast(&I)); + } + } + } + // Mark the leaf-function attribute, if the function has no any callsite. + // The functions marked "leaf-function" do not emit methodinfo in + // CJMetadata.cpp. + if (!HasCallSite) { + F.addFnAttr(Attribute::get(F.getContext(), "leaf-function")); + return MadeChange; + } + + // Return early if no work to do. + if (ParsePointNeeded.empty()) + return MadeChange; + + // As a prepass, go ahead and aggressively destroy single entry phi nodes. + // These are created by LCSSA. They have the effect of increasing the size + // of liveness sets for no good reason. It may be harder to do this post + // insertion since relocations and base phis can confuse things. + for (BasicBlock &BB : F) + if (BB.getUniquePredecessor()) + MadeChange |= FoldSingleEntryPHINodes(&BB); + + // Before we start introducing relocations, we want to tweak the IR a bit to + // avoid unfortunate code generation effects. The main example is that we + // want to try to make sure the comparison feeding a branch is after any + // safepoints. Otherwise, we end up with a comparison of pre-relocation + // values feeding a branch after relocation. This is semantically correct, + // but results in extra register pressure since both the pre-relocation and + // post-relocation copies must be available in registers. For code without + // relocations this is handled elsewhere, but teaching the scheduler to + // reverse the transform we're about to do would be slightly complex. + // Note: This may extend the live range of the inputs to the icmp and thus + // increase the liveset of any statepoint we move over. This is profitable + // as long as all statepoints are in rare blocks. If we had in-register + // lowering for live values this would be a much safer transform. + auto getConditionInst = [](Instruction *TI) -> Instruction * { + if (auto *BI = dyn_cast(TI)) + if (BI->isConditional()) + return dyn_cast(BI->getCondition()); + // TODO: Extend this to handle switches + return nullptr; + }; + for (BasicBlock &BB : F) { + Instruction *TI = BB.getTerminator(); + if (auto *Cond = getConditionInst(TI)) + // TODO: Handle more than just ICmps here. We should be able to move + // most instructions without side effects or memory access. + if (isa(Cond) && Cond->hasOneUse()) { + MadeChange = true; + Cond->moveBefore(TI); + } + } + + // Nasty workaround - The base computation code in the main algorithm doesn't + // consider the fact that a GEP can be used to convert a scalar to a vector. + // The right fix for this is to integrate GEPs into the base rewriting + // algorithm properly, this is just a short term workaround to prevent + // crashes by canonicalizing such GEPs into fully vector GEPs. + for (Instruction &I : instructions(F)) { + if (!isa(I)) + continue; + + unsigned VF = 0; + for (unsigned i = 0; i < I.getNumOperands(); i++) + if (auto *OpndVTy = dyn_cast(I.getOperand(i)->getType())) { + assert(VF == 0 || + VF == cast(OpndVTy)->getNumElements()); + VF = cast(OpndVTy)->getNumElements(); + } + + // It's the vector to scalar traversal through the pointer operand which + // confuses base pointer rewriting, so limit ourselves to that case. + if (!I.getOperand(0)->getType()->isVectorTy() && VF != 0) { + IRBuilder<> B(&I); + auto *Splat = B.CreateVectorSplat(VF, I.getOperand(0)); + I.setOperand(0, Splat); + MadeChange = true; + } + } + + // Cache the 'defining value' relation used in the computation and + // insertion of base phis and selects. This ensures that we don't insert + // large numbers of duplicate base_phis. Use one cache for both + // inlineGetBaseAndOffset() and insertParsePoints(). + DefiningValueMapTy DVCache; + + // Mapping between a base values and a flag indicating whether it's a known + // base or not. + IsKnownBaseMapTy KnownBases; + + if (!ParsePointNeeded.empty()) + MadeChange |= insertParsePoints(F, DT, PostDT, TTI, ParsePointNeeded, + DVCache, KnownBases, OptLevel); + + return MadeChange; +} diff --git a/llvm/lib/Transforms/Scalar/CJRuntimeLowering.cpp b/llvm/lib/Transforms/Scalar/CJRuntimeLowering.cpp new file mode 100644 index 000000000..222631033 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/CJRuntimeLowering.cpp @@ -0,0 +1,1140 @@ +//===- CJRuntimeLowering.cpp - CJ Runtime Lowering --------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This pass lowers the functions generated by the cangjie frontend to +// the related runtime. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/CJRuntimeLowering.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Metadata.h" +#include "llvm/IR/SafepointIRVerifier.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/Alignment.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Scalar/CJFillMetadata.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include + +using namespace llvm; + +namespace llvm { +extern cl::opt CJLTOOpt; +extern cl::opt CangjieJIT; +extern cl::opt CJPipeline; + +cl::opt IVCallInstrEnable("iv-call-instrument-enable", cl::init(false)); + +cl::opt InputFileName("cj-ic-input-file", + cl::desc("Specify the input file"), + cl::value_desc("filename"), + cl::init("./ProfileOutput.txt")); +cl::opt EnableCJNewArrayFast("enable-cangjie-new-array-fastpath", + cl::init(true), cl::ReallyHidden); +} // namespace llvm + +namespace { + +const static StringRef ArithmeticExceptionMangleStr = + "CJ_MCC_ThrowArithmeticException"; +const static StringRef C2NStubStr = "CJ_MCC_C2NStub"; +const static StringRef N2CStubStr = "CJ_MCC_N2CStub"; +const static StringRef FinalizerStr = "CJ_MCC_OnFinalizerCreated"; + +template +using StdMap = std::unordered_map; +const static StdMap RuntimeMap { + {Intrinsic::cj_invoke_gc, "CJ_MCC_InvokeGC"}, + {Intrinsic::cj_get_obj_klass, "CJ_MCC_GetObjClass"}, + {Intrinsic::cj_malloc_object, "CJ_MCC_NewObject"}, + {Intrinsic::cj_alloca_generic, "CJ_MCC_NewObject"}, + {Intrinsic::cj_malloc_array_generic, "CJ_MCC_NewArrayGeneric"}, + {Intrinsic::cj_get_mtable_func, "CJ_MCC_GetMTable"}, + {Intrinsic::cj_acquire_rawdata, "CJ_MCC_AcquireRawData"}, + {Intrinsic::cj_release_rawdata, "CJ_MCC_ReleaseRawData"}, + {Intrinsic::cj_post_throw_exception, "CJ_MCC_PostThrowException"}, + {Intrinsic::cj_throw_exception, "CJ_MCC_ThrowException"}, + {Intrinsic::cj_fill_in_stack_trace, "CJ_MCC_FillInStackTrace"}, + {Intrinsic::cj_pre_initialize_package, "CJ_MRT_PreInitializePackage"}, + {Intrinsic::cj_get_exception_wrapper, "CJ_MCC_GetExceptionWrapper"}, + {Intrinsic::cj_get_exception_typeid, "CJ_MC_CGetExceptionTypeID"}, + {Intrinsic::cj_set_gc_threshold, "CJ_MCC_SetGCThreshold"}, + {Intrinsic::cj_get_real_heap_size, "CJ_MCC_GetRealHeapSize"}, + {Intrinsic::cj_get_allocated_heap_size, "CJ_MCC_GetAllocatedHeapSize"}, + {Intrinsic::cj_get_max_heap_size, "CJ_MCC_GetMaxHeapSize"}, + {Intrinsic::cj_dump_heap_data, "CJ_MCC_DumpCJHeapData"}, + {Intrinsic::cj_get_thread_number, "CJ_MCC_GetCJThreadNumber"}, + {Intrinsic::cj_get_blocking_thread_number, + "CJ_MCC_GetBlockingCJThreadNumber"}, + {Intrinsic::cj_get_native_thread_number, "CJ_MCC_GetNativeThreadNumber"}, + {Intrinsic::cj_get_gc_count, "CJ_MCC_GetGCCount"}, + {Intrinsic::cj_get_gc_time_us, "CJ_MCC_GetGCTimeUs"}, + {Intrinsic::cj_get_gc_freed_size, "CJ_MCC_GetGCFreedSize"}, + {Intrinsic::cj_start_cpu_profiling, "CJ_MCC_StartCpuProfiling"}, + {Intrinsic::cj_stop_cpu_profiling, "CJ_MCC_StopCpuProfiling"}, + {Intrinsic::cj_register_implicit_exception_raisers, + "CJ_MCC_RegisterImplicitExceptionRaisers"}, + {Intrinsic::cj_get_type_info, "CJ_MCC_GetOrCreateTypeInfo"}, + {Intrinsic::cj_is_subtype, "CJ_MCC_IsSubType"}, + {Intrinsic::cj_is_tupletype_of, "CJ_MCC_IsTupleTypeOf"}, + {Intrinsic::cj_is_typeinfo_equal, "CJ_MCC_IsTypeInfoEqual"}, + {Intrinsic::cj_set_location, "SetDebugLocation"}, + {Intrinsic::cj_cross_access_barrier, "CJ_MCC_CrossAccessBarrier"}, + {Intrinsic::cj_get_exported_ref, "CJ_MCC_GetExportedRef"}, + {Intrinsic::cj_remove_exported_ref, "CJ_MCC_RemoveExportedRef"}, + {Intrinsic::cj_create_export_handle, "CJ_MCC_CreateExportHandle"}, + {Intrinsic::cj_blackhole, "CJ_LLVM_BlackHole"}}; + +struct LowerGetFieldOffset { + CallBase *CI; + Value *TI; + Value *FieldIndex; + Value *AttachedOffset; + uint64_t AttachedInt; + + LowerGetFieldOffset(CallBase *CI) : CI(CI) { + TI = CI->getArgOperand(0); + FieldIndex = CI->getArgOperand(1); + AttachedOffset = CI->getArgOperand(2); + AttachedInt = dyn_cast(AttachedOffset)->getZExtValue(); + } + + bool isZeroIndex() { + if (auto *Index = dyn_cast(FieldIndex)) { + if (Index->getZExtValue() == 0) { + return true; + } + } + return false; + } + + // Replace intrinsic i32 @llvm.cj.get.field.offset (i8* TI, i32 i, i32 j) + // If TI points to a RawArray or VArray type, call getArrayFieldOffset to + // compute the offset. Otherwise, call getObjectFieldOffset. + void replaceGetFieldOffset() { + IRBuilder<> IRB(CI); + + // If the index is 0, the offset is used as the result. + if (isZeroIndex()) { + replaceInstWith(CI, IRB.getInt64(AttachedInt)); + return; + } + + Value *Result = nullptr; + if (isRawArrayOrVArray(TI)) { + Result = getArrayFieldOffset(CI, IRB); + } else { + Result = getObjectFieldOffset(CI, IRB); + } + replaceInstWith(CI, Result); + } + + // i64 @llvm.cj.get.field.offset (i8* TI, i64 i, i32 j) + // %TI = bitcast i8* TI to Typeinfo* + // %Elem.ptr = getelementptr inbounds Typeinfo, Typeinfo* %TI, i32 0, i32 13 + // %Elem = load Typeinfo*, Typeinfo** %TI.element.ptr, align 8 + // %Elem.Kind.ptr = getelementptr inbounds Typeinfo, Typeinfo* %Elem, i32 0, + // i32 1 %Elem.Kind = load i32, i32* %Kind.ptr, align 8 %IsRef = icmp slt + // %Kind, 0 %Elem.size.ptr = getelementptr inbounds Typeinfo, Typeinfo* %Elem, + // i32 0, i32 4 %Elem.size.i32 = load i32, i32* %Elem.size.ptr, align 8 + // %Elem.size.i64 = zext i32 %Elem.size.i32 to i64 + // %Elem.size = select %IsRef, 8, %Elem.size.i64 + // %TI.offset = mul i64 %Elem.size, i64 i + // %TI.offset = add i64 j, i64 %TI.offset + Value *getArrayFieldOffset(CallBase *CI, IRBuilder<> &IRB) { + SmallVector Idxs; + Value *Offset = nullptr; + StructType *KlassType = + StructType::getTypeByName(IRB.getContext(), "TypeInfo"); + Value *TICast = IRB.CreateBitCast(TI, KlassType->getPointerTo()); + + Idxs.push_back(IRB.getInt32(0)); + Idxs.push_back(IRB.getInt32(CIT_SUPER)); + Value *ElemPtr = IRB.CreateGEP(KlassType, TICast, {Idxs}, "", true); + Value *Elem = IRB.CreateLoad(KlassType->getPointerTo(), ElemPtr, ""); + Idxs.clear(); + Idxs.push_back(IRB.getInt32(0)); + Idxs.push_back(IRB.getInt32(CIT_TYPE)); + Value *ElemKindPtr = IRB.CreateGEP(KlassType, Elem, {Idxs}, "", true); + Value *ElemKind = IRB.CreateLoad(IRB.getInt8Ty(), ElemKindPtr); + Value *IsRef = IRB.CreateICmpSLT(ElemKind, IRB.getInt8(0)); + Idxs.clear(); + Idxs.push_back(IRB.getInt32(0)); + Idxs.push_back(IRB.getInt32(CIT_SIZE)); + Value *ElemSizePtr = IRB.CreateGEP(KlassType, Elem, {Idxs}, "", true); + Value *ElemSizeI32 = IRB.CreateLoad(IRB.getInt32Ty(), ElemSizePtr); + Value *ElemSizeI64 = IRB.CreateZExt(ElemSizeI32, IRB.getInt64Ty()); + const Triple TT(CI->getModule()->getTargetTriple()); + Value *ElemSize = + IRB.CreateSelect(IsRef, TT.isARM() ? IRB.getInt64(4) : IRB.getInt64(8), + ElemSizeI64); // 4|8:ref size + Offset = IRB.CreateMul(ElemSize, FieldIndex); + return IRB.CreateAdd(Offset, IRB.getInt64(AttachedInt)); + } + + // i64 @llvm.cj.get.field.offset (i8* TI, i64 i, i32 j) + // %TI = bitcast i8* TI to Typeinfo* + // %Offsets.ptr = getelementptr inbounds Typeinfo, Typeinfo* %TI, i32 0, i32 9 + // %Offsets = load i32*, i32** %Offsets.ptr, align 8 + // %Offset.ptr = getelementptr inbounds i32, i32* %Offsets, i64 i + // %Offset = load i32, i32* %Offset.ptr, align 4 + // %Offset = add i64 j, i32 %Offset + Value *getObjectFieldOffset(CallBase *CI, IRBuilder<> &IRB) { + Type *I32Ty = IRB.getInt32Ty(); + SmallVector Idxs; + Value *Offset = nullptr; + StructType *KlassType = + StructType::getTypeByName(IRB.getContext(), "TypeInfo"); + Value *TICast = IRB.CreateBitCast(TI, KlassType->getPointerTo()); + Idxs.push_back(IRB.getInt32(0)); + Idxs.push_back(IRB.getInt32(CIT_OFFSETS)); + Value *OffsetsPtr = IRB.CreateGEP(KlassType, TICast, {Idxs}, "", true); + Type *Int32PtrTy = I32Ty->getPointerTo(); + Value *Offsets = IRB.CreateLoad(Int32PtrTy, OffsetsPtr); + Idxs.clear(); + Idxs.push_back(FieldIndex); + Value *OffsetPtr = IRB.CreateGEP(I32Ty, Offsets, {Idxs}, "", true); + Offset = IRB.CreateLoad(I32Ty, OffsetPtr); + Offset = IRB.CreateZExt(Offset, IRB.getInt64Ty()); + return IRB.CreateAdd(Offset, IRB.getInt64(AttachedInt)); + } + + bool isRawArrayOrVArray(Value *V) { + CallInst *CI = dyn_cast(V->stripPointerCasts()); + if (!CI || CI->arg_size() != 3) // 3: llvm.cj.get.type.info arg num + return false; + + auto CE = dyn_cast(CI->getArgOperand(0)); + if (!CE) + return false; + + auto *TT = dyn_cast(CE->getOperand(0)); + if (TT->getName().equals("VArray.tt") || + TT->getName().equals("RawArray.tt")) + return true; + + return false; + } + + void replaceInstWith(Instruction *Old, Value *New) { + if (auto *Inst = dyn_cast(New)) { + Inst->setDebugLoc(Old->getDebugLoc()); + Inst->takeName(Old); + } + Old->replaceAllUsesWith(New); + Old->eraseFromParent(); + } +}; + +struct EscapeScope { + Function &F; + DenseSet Data; + + explicit EscapeScope(Function &F) : F(F) { + for (Instruction &I : instructions(F)) { + if (CallBase *CI = dyn_cast(&I)) { + visitCallBase(CI); + } else if (StoreInst *SI = dyn_cast(&I)) { + visitStoreInst(SI); + } else if (ReturnInst *RI = dyn_cast(&I)) { + visitReturnInst(RI); + } + } + } + + bool isEscaped(Value *V) { return Data.contains(V); } + + void visitCallBase(CallBase *CI) { + if (isa(CI)) + return; + + for (unsigned Idx = 0; Idx < CI->arg_size(); Idx++) { + if (CI->paramHasAttr(Idx, Attribute::StructRet)) + continue; + + Value *Arg = CI->getArgOperand(Idx); + if (isGCPtr(Arg)) + Data.insert(Arg->stripPointerCasts()); + } + } + + void visitStoreInst(StoreInst *SI) { + Value *V = SI->getValueOperand(); + if (isGCPtr(V)) + Data.insert(V->stripPointerCasts()); + } + + void visitReturnInst(ReturnInst *RI) { + Value *V = RI->getReturnValue(); + if (V != nullptr && isGCPtr(V)) + Data.insert(V->stripPointerCasts()); + } +}; + +/// lowering Cangjie intrinsic functions. +class CJIntrinsicLowering { +public: + explicit CJIntrinsicLowering(Module &M) + : M(M), C(M.getContext()), DL(M.getDataLayout()) {} + + void replaceWithRuntimeFunc(CallBase *CI, bool GCLeafFunc, bool GCMalloc) { + CI->setCalledFunction(getOrInsertRuntimeFunc(CI, GCLeafFunc, GCMalloc)); + } + + // cj.heapmalloc.class (i8* bitcast (TypeInfo* @Klass.ti to i8*), i32 size) + void setHeapMallocSizeAlign(CallBase *CI) { + auto ConstantSizeAlign = [&](uint64_t Size) { + Size += ObjectHeadSize; + Size = (Size + 7) & (~(7)); // 8 bytes alignment + CI->setArgOperand(1, ConstantInt::get(Type::getInt32Ty(C), Size)); + }; + auto VariableSizeAlign = [&](Value *Size, IRBuilder<> &IRB) { + auto *V = IRB.CreateAnd(IRB.CreateAdd(Size, IRB.getInt32(15)), + IRB.getInt32(~7)); + CI->setArgOperand(1, V); + }; + auto GetTISizePointer = [this](Value *TI, IRBuilder<> &IRB) -> Value * { + Type *Ty = TI->getType()->getNonOpaquePointerElementType(); + if (isa(TI) || isa(Ty)) + return IRB.CreateInBoundsGEP(Ty, TI, + {IRB.getInt32(0), IRB.getInt32(CIT_SIZE)}); + auto *ST = StructType::getTypeByName(M.getContext(), "TypeInfo"); + if (!ST) + report_fatal_error("error"); + auto *SL = M.getDataLayout().getStructLayout(ST); + auto *GEP = + IRB.CreateGEP(Ty, TI, {IRB.getInt32(SL->getElementOffset(CIT_SIZE))}); + return IRB.CreateBitCast(GEP, IRB.getInt32Ty()->getPointerTo()); + }; + + auto *TI = CI->getArgOperand(0)->stripPointerCasts(); + auto *GV = dyn_cast(TI); + // In Cangjie, non-fixed size classes allow member variable extension + // compatibility, so the size needs to be explicitly loaded from typeinfo. + if (!GV || !GV->hasAttribute("can_malloc_with_fixed_size")) { + IRBuilder<> IRB(CI); + auto *Ptr = GetTISizePointer(TI, IRB); + assert(Ptr->getType()->getNonOpaquePointerElementType() == + IRB.getInt32Ty()); + auto *LI = dyn_cast(IRB.CreateLoad(IRB.getInt32Ty(), Ptr)); + LI->setDebugLoc(CI->getDebugLoc()); + return VariableSizeAlign(LI, IRB); + } + if (auto *SizeVar = dyn_cast(CI->getArgOperand(1))) + return ConstantSizeAlign(SizeVar->getZExtValue()); + if (GV && GV->hasInitializer()) + return ConstantSizeAlign(TypeInfo(GV).getSize()); + + IRBuilder<> IRB(CI); + VariableSizeAlign(CI->getArgOperand(1), IRB); + } + + void replaceFixedNewObject(CallBase *&CI) { + IRBuilder<> Bdr(CI); + Function *Func = M.getFunction("CJ_MCC_NewPinnedObject"); + if (Func == nullptr) { + Type *I8Ptr = Bdr.getInt8PtrTy(); + Type *RefPtr = Bdr.getInt8PtrTy(1); + Type *I32 = Bdr.getInt32Ty(); + Type *I1 = Bdr.getInt1Ty(); + FunctionType *FT = FunctionType::get(RefPtr, {I8Ptr, I32, I1}, false); + Func = M.declareCJRuntimeFunc("CJ_MCC_NewPinnedObject", FT, false, true); + } + CallBase *I = nullptr; + SmallVector Params = {CI->getArgOperand(0), Bdr.getInt32(SyncSize), + Bdr.getInt1(false)}; + if (isa(CI)) { + I = Bdr.CreateCall(Func, Params); + } else if (auto II = dyn_cast(CI)) { + I = Bdr.CreateInvoke(Func, II->getNormalDest(), II->getUnwindDest(), + Params); + } + I->setDebugLoc(CI->getDebugLoc()); + CI->replaceAllUsesWith(I); + CI->eraseFromParent(); + CI = I; + } + + void replaceNewFinalizerFunc(CallBase *CI) { + IRBuilder<> Bdr(CI); + Function *Func = M.getFunction("CJ_MCC_NewFinalizer"); + if (Func == nullptr) { + // NewFinalier cannot add "cj-heapmalloc" attribute, because it may + // call a finalier method when destructor performing. + Func = M.declareCJRuntimeFunc("CJ_MCC_NewFinalizer", + CI->getFunctionType(), false); + } + CI->setCalledFunction(Func); + } + + void replaceNewWeakRefFunc(CallBase *CI) { + IRBuilder<> Bdr(CI); + Function *Func = M.getFunction("CJ_MCC_NewWeakRefObject"); + if (Func == nullptr) { + Func = M.declareCJRuntimeFunc("CJ_MCC_NewWeakRefObject", + CI->getFunctionType(), false); + } + CI->setCalledFunction(Func); + } + + // llvm.cj.alloca.generic(TypeInfo* ti, i64 size) + void replaceAllocaGeneric(CallBase *CI, EscapeScope &ES) { + if (replaceWithAlloca(CI, ES)) { + return; + } + replaceWithRuntimeFunc(CI, false, true); + CI->addRetAttr(Attribute::NoAlias); + setMetadata(CI, "new_gen"); + } + + void replaceIsReference(CallBase *CI) { + IRBuilder<> IRB(CI); + Value *Arg = CI->getArgOperand(0)->stripPointerCasts(); + if (auto *GV = dyn_cast(Arg)) { + if (Value *Res = TypeInfo::getReferenceType(GV)) { + CI->replaceAllUsesWith(Res); + CI->eraseFromParent(); + return; + } + } + StructType *ST = StructType::getTypeByName(C, "TypeInfo"); + assert(ST && "TypeInfo type doesn't exist!"); + Value *TypePtr = Arg; + if (Arg->getType()->getNonOpaquePointerElementType() != ST) { + TypePtr = IRB.CreateBitCast(Arg, ST->getPointerTo()); + } + SmallVector Idxs; + Idxs.push_back(IRB.getInt32(0)); + Idxs.push_back(IRB.getInt32(CIT_TYPE)); + Value *KindPtr = IRB.CreateGEP(ST, TypePtr, Idxs); + LoadInst *Kind = IRB.CreateLoad(IRB.getInt8Ty(), KindPtr); + setMetadata(Kind, "ti_load"); + Value *Ret = IRB.CreateICmpSLT(Kind, IRB.getInt8(0)); + cast(Ret)->setDebugLoc(CI->getDebugLoc()); + CI->replaceAllUsesWith(Ret); + CI->eraseFromParent(); + } + + // llvm.cj.gcmalloc.array(TypeInfo* @Klass.ti, i64 length, i64 elem.size) + void replaceNewArray(CallBase *CI) { + uint64_t Size = 0; // 0 is slowpath, others is fastpath. + if (EnableCJNewArrayFast && !CangjieJIT) { + auto *LengthOp = dyn_cast(CI->getArgOperand(1)); + auto *ElemSizeOp = dyn_cast(CI->getArgOperand(2)); + if (LengthOp && ElemSizeOp) { + uint64_t ElemSize = ElemSizeOp->getZExtValue(); + if (ElemSize == 0) { + Size = ArrayHeadSize; + } else { + uint64_t Length = LengthOp->getZExtValue(); + // check array size + if (MaxArrayFast / ElemSize > Length) { + Size = ArrayHeadSize + ElemSize * Length; + Size = (Size + 7) & (~(7)); // 8 bytes alignment + } + } + } + } + if (Size > 0) { + CI->setArgOperand(2, ConstantInt::get(Type::getInt64Ty(C), Size)); + setMetadata(CI, "fast_new_array"); + } else { + CI->setArgOperand(2, PoisonValue::get(Type::getInt64Ty(C))); + } + CI->setCalledFunction(getOrInsertRuntimeFunc(CI, false, true)); + return; + } + + void replaceIsTypeInfoEqual(CallBase *CI) { + IRBuilder<> IRB(CI); + Value *Arg0 = CI->getArgOperand(0)->stripPointerCasts(); + Value *Arg1 = CI->getArgOperand(1)->stripPointerCasts(); + StructType *ST = StructType::getTypeByName(C, "TypeInfo"); + assert(ST && "TypeInfo type doesn't exist!"); + Value *TypePtr0 = Arg0; + if (Arg0->getType()->getNonOpaquePointerElementType() != ST) { + TypePtr0 = IRB.CreateBitCast(Arg0, ST->getPointerTo()); + } + Value *TypePtr1 = Arg1; + if (Arg1->getType()->getNonOpaquePointerElementType() != ST) { + TypePtr1 = IRB.CreateBitCast(Arg1, ST->getPointerTo()); + } + SmallVector Idxs; + Idxs.push_back(IRB.getInt32(0)); + Idxs.push_back(IRB.getInt32(CIT_UUID)); + Value *UUID0Ptr = IRB.CreateInBoundsGEP(ST, TypePtr0, Idxs); + Value *UUID0 = IRB.CreateLoad(IRB.getInt32Ty(), UUID0Ptr); + Value *UUID1Ptr = IRB.CreateInBoundsGEP(ST, TypePtr1, Idxs); + Value *UUID1 = IRB.CreateLoad(IRB.getInt32Ty(), UUID1Ptr); + Value *Ret = IRB.CreateICmpEQ(UUID0, UUID1); + cast(Ret)->setDebugLoc(CI->getDebugLoc()); + CI->replaceAllUsesWith(Ret); + CI->eraseFromParent(); + } + + void replaceIsTupleTypeOf(CallBase *CI) { + IRBuilder<> IRB(CI); + StringRef Callee = getRuntimeFuncName(CI); + assert(Callee != "" && "Callee don`t exist."); + auto Itr = RTFuncMap.find(Callee); + Function *Func = nullptr; + if (Itr != RTFuncMap.end()) { + Func = Itr->second; + } else { + Type *boolTy = IRB.getInt1Ty(); + Type *I8Ptr = IRB.getInt8PtrTy(); + FunctionType *FT = FunctionType::get(boolTy, {I8Ptr, I8Ptr, I8Ptr}, false); + Func = M.declareCJRuntimeFunc(Callee, FT, true, false); + auto FnAttrs = CI->getCalledFunction()->getAttributes().getFnAttrs(); + auto NewAttrs = + Func->getAttributes().addFnAttributes(C, AttrBuilder(C, FnAttrs)); + Func->setAttributes(NewAttrs); + RTFuncMap[Callee] = Func; + } + assert(Func != nullptr && "replaceIsTupleTypeOf failed."); + CI->setCalledFunction(Func); + if (CI->getArgOperand(0)->getType() == IRB.getInt8PtrTy(1)) { + auto ObjCast = IRB.CreateAddrSpaceCast(CI->getArgOperand(0), IRB.getInt8PtrTy(0)); + CI->setArgOperand(0, ObjCast); + } + } + + // %0 = getelementptr i8, i8* %ti, i32 72 + // %1 = bitcast i8* %0 to i8*** + // %2 = load i8**, i8*** %1 + // %3 = getelementptr i8*, i8** %2, %index_type + // %4 = load i8*, i8** %3 + // %5 = bitcast i8* %4 to %ExDef* + // %6 = getelementptr inbounds %ExDef, %ExDef* %5, i64 0, i32 ET_FUNC_TABLE + // %7 = bitcast i8* %6 to i8*** + // %8 = load i8**, i8*** %7 + // %9 = getelementptr i8*, i8** %8, %index_func + // %10 = load i8*, i8** %9 + void replaceGetVTableFunc(CallBase *CB) { + IRBuilder<> IRB(CB); + auto *TI = CB->getArgOperand(0); + auto *ST = StructType::getTypeByName(CB->getContext(), "TypeInfo"); + auto *SL = CB->getModule()->getDataLayout().getStructLayout(ST); + auto VOffset = SL->getElementOffset(CIT_VTABLE); + auto *GEP0 = + IRB.CreateGEP(TI->getType()->getNonOpaquePointerElementType(), TI, + {ConstantInt::get(IRB.getInt32Ty(), VOffset)}); + auto *BS0 = IRB.CreateBitCast( + GEP0, IRB.getInt8PtrTy()->getPointerTo()->getPointerTo()); + auto *LI0 = + IRB.CreateLoad(BS0->getType()->getNonOpaquePointerElementType(), BS0); + LI0->setMetadata(LLVMContext::MD_virtual_call, + MDNode::get(CB->getContext(), {})); + LI0->setMetadata(LLVMContext::MD_invariant_load, + MDNode::get(CB->getContext(), {})); + auto *GEP1 = IRB.CreateGEP(LI0->getType()->getNonOpaquePointerElementType(), + LI0, {CB->getArgOperand(1)}); + auto *LI1 = + IRB.CreateLoad(GEP1->getType()->getNonOpaquePointerElementType(), GEP1); + LI1->setMetadata(LLVMContext::MD_invariant_load, + MDNode::get(CB->getContext(), {})); + auto *BS1 = IRB.CreateBitCast( + LI1, StructType::getTypeByName(CB->getContext(), "ExtensionDef") + ->getPointerTo()); + auto *GEP2 = + IRB.CreateGEP(BS1->getType()->getNonOpaquePointerElementType(), BS1, + {ConstantInt::get(IRB.getInt64Ty(), 0), + ConstantInt::get(IRB.getInt32Ty(), ET_FUNC_TABLE)}); + auto *BS2 = IRB.CreateBitCast( + GEP2, IRB.getInt8PtrTy()->getPointerTo()->getPointerTo()); + auto *LI2 = + IRB.CreateLoad(BS2->getType()->getNonOpaquePointerElementType(), BS2); + LI2->setMetadata(LLVMContext::MD_invariant_load, + MDNode::get(CB->getContext(), {})); + auto *GEP3 = IRB.CreateGEP(LI2->getType()->getNonOpaquePointerElementType(), + LI2, {CB->getArgOperand(2)}); + auto *LI3 = + IRB.CreateLoad(GEP3->getType()->getNonOpaquePointerElementType(), GEP3); + LI3->setMetadata(LLVMContext::MD_intro_type, + CB->getMetadata(LLVMContext::MD_intro_type)); + LI3->setMetadata(LLVMContext::MD_func_table, + MDNode::get(CB->getContext(), + {ConstantAsMetadata::get(cast( + CB->getArgOperand(2)))})); + LI3->setMetadata(LLVMContext::MD_invariant_load, + MDNode::get(CB->getContext(), {})); + LI3->setMetadata(LLVMContext::MD_obj_type, + CB->getMetadata(LLVMContext::MD_obj_type)); + CB->replaceAllUsesWith(LI3); + CB->eraseFromParent(); + } + + void replaceGetMTableFunc(CallBase *CB) { + auto &C = CB->getContext(); + auto *Callee = getOrInsertRuntimeFunc( + CB, true, false, + // CJ_MCC_GetMTable: i8*(*)(i8*, i8*) + FunctionType::get( + Type::getInt8PtrTy(C), + {CB->getArgOperand(0)->getType(), CB->getArgOperand(1)->getType()}, + false)); + IRBuilder<> IRB(CB); + auto *Int8PtrTy = Type::getInt8PtrTy(C); + auto *CI = + IRB.CreateCall(Callee, {CB->getArgOperand(0), CB->getArgOperand(1)}); + auto *BC0 = IRB.CreateBitCast(CI, Int8PtrTy->getPointerTo()); + auto *GEP = IRB.CreateGEP(Int8PtrTy, BC0, {CB->getArgOperand(2)}); + auto *LI = IRB.CreateLoad(Int8PtrTy, GEP); + LI->setMetadata(LLVMContext::MD_invariant_load, MDNode::get(C, {})); + LI->setMetadata("FuncTable", + MDNode::get(C, {ConstantAsMetadata::get(cast( + CB->getArgOperand(2)))})); + LI->setMetadata(LLVMContext::MD_obj_type, + CB->getMetadata(LLVMContext::MD_obj_type)); + CB->replaceAllUsesWith(LI); + CB->eraseFromParent(); + } + + void replaceDivisionCheck() { + if (DivisionChecks.size() == 0) + return; + + for (unsigned I = 0; I < DivisionChecks.size(); I++) { + loweringGCDiv(DivisionChecks[I]); + } + } + + void finishAll() { + replaceDivisionCheck(); + DivisionChecks.clear(); + } + + void markLoweringDivisionCheck(CallBase *CI) { DivisionChecks.push_back(CI); } + + bool loweringGCDiv(CallBase *CI) { + IRBuilder<> Builder(CI); + Type *TypeOfOperand = CI->getOperand(1)->getType(); + + ICmpInst *DivisorCmp = + new ICmpInst(CI, ICmpInst::ICMP_EQ, CI->getOperand(1), + ConstantInt::get(TypeOfOperand, 0), "divisor_is_0"); + Instruction *ThenTerm = nullptr; + Instruction *ElseTerm = nullptr; + SplitBlockAndInsertIfThenElse(DivisorCmp, CI, &ThenTerm, &ElseTerm); + BasicBlock *ThenBB = ThenTerm->getParent(); + BasicBlock *ElseBB = ElseTerm->getParent(); + ThenBB->setName("div.divisorIs0"); + ElseBB->setName("div.divisorIsNot0"); + CI->getParent()->setName("divEnd"); + + auto ArithmeticFn = M.getOrInsertFunction(ArithmeticExceptionMangleStr, + Builder.getVoidTy()); + auto *II = dyn_cast(CI); + Instruction *Arithmetic; + if (II != nullptr) { + // change invoke @llvm.cj.division.check.XXX(a, b) to: + // divisor_is_0: icmp eq b, 0 + // br i1 divisor_is_0, label %then, label %else + // div.divisorIs0: + // invoke void MCC_ThrowArithmeticException() + // div.divisorIsNot0: + // br label %1 + // 1: + // %0 = sdiv/udiv/srem/urem a, b + // br label %NormalDest + Arithmetic = Builder.CreateInvoke(ArithmeticFn, II->getParent(), + II->getUnwindDest()); + Arithmetic->moveBefore(ThenBB->getTerminator()); + dyn_cast(Arithmetic) + ->getUnwindDest() + ->replacePhiUsesWith(CI->getParent(), ThenBB); + } else { + // change call @llvm.cj.division.check.XXX(a, b) to: + // divisor_is_0: icmp eq b, 0 + // br i1 divisor_is_0, label %then, label %else + // div.divisorIs0: + // call void MCC_ThrowArithmeticException() + // unreachable + // div.divisorIsNot0: + // br label %1 + // 1: + // %0 = sdiv/udiv/srem/urem a, b + Arithmetic = Builder.CreateCall(ArithmeticFn); + Arithmetic->moveBefore(ThenBB->getTerminator()); + auto Unreach = Builder.CreateUnreachable(); + Unreach->moveAfter(Arithmetic); + } + ThenBB->getTerminator()->eraseFromParent(); + + Builder.SetInsertPoint(CI); + Value *NewCI = createGCDiv(Builder, CI); + if (CI->getOpcode() == Instruction::Invoke) { + Builder.CreateBr(II->getNormalDest()); + } + CI->replaceAllUsesWith(NewCI); + CI->eraseFromParent(); + return true; + } + +private: + Module &M; + LLVMContext &C; + const DataLayout &DL; + SmallVector DivisionChecks; + // map intrinsic to runtime API function type. + DenseMap RTFuncMap; + + StringRef getRuntimeFuncName(CallBase *CI) const { + Intrinsic::ID IID = CI->getIntrinsicID(); + if (IID == Intrinsic::cj_malloc_array) { + return getMallocArrayFuncName(CI); + } else { + auto Itr = RuntimeMap.find(IID); + assert(Itr != RuntimeMap.end() && "Runtime function don`t exist."); + return Itr->second; + } + } + + StringRef getMallocArrayFuncName(CallBase *CI) const { + Value *Arg0 = CI->getOperand(0); + // gc.malloc.array(i64, bitcast arg1 to i8*) + auto *CastExpr = dyn_cast(Arg0); + auto *KlassGV = dyn_cast(CastExpr->getOperand(0)); + TypeInfo ArrayKlass(KlassGV); + + Type *ET = ArrayKlass.getArrayElementType(); + if (ET->isPointerTy()) { + return "CJ_MCC_NewObjArray"; + } else if (ET->isStructTy() || ET->isArrayTy()) { + return "CJ_MCC_NewArray"; + } else { + unsigned Size = + static_cast(DL.getTypeAllocSize(ET).getFixedSize()); + switch (Size) { + case 1: + return "CJ_MCC_NewArray8"; + case 2: + return "CJ_MCC_NewArray16"; + case 4: + return "CJ_MCC_NewArray32"; + case 8: + return "CJ_MCC_NewArray64"; + default: + break; + } + } + report_fatal_error("Unsupported element klass type for gc.malloc.array!"); + return ""; + } + + Function *getOrInsertRuntimeFunc(CallBase *CI, bool GCLeafFunc, bool GCMalloc, + FunctionType *FuncType = nullptr) { + StringRef Callee = getRuntimeFuncName(CI); + assert(Callee != "" && "Callee don`t exist."); + auto Itr = RTFuncMap.find(Callee); + if (Itr != RTFuncMap.end()) { + Function *RTFunc = Itr->second; + if (!RTFunc->isDeclaration()) { + RTFunc->deleteBody(); + } + return RTFunc; + } + + FunctionType *FT = FuncType ? FuncType : CI->getFunctionType(); + Function *Func = M.declareCJRuntimeFunc(Callee, FT, GCLeafFunc, GCMalloc); + assert(Func != nullptr && "getOrInsertFunction fail"); + + // Transfer the original function's attributes. + auto FnAttrs = CI->getCalledFunction()->getAttributes().getFnAttrs(); + auto NewAttrs = + Func->getAttributes().addFnAttributes(C, AttrBuilder(C, FnAttrs)); + Func->setAttributes(NewAttrs); + if (Func->getName().isSetDebugLocation()) + Func->setUnnamedAddr(GlobalValue::UnnamedAddr::Local); + RTFuncMap[Callee] = Func; + if (CI->getIntrinsicID() == Intrinsic::cj_blackhole) { + Func->addFnAttr(Attribute::ReadOnly); + } + return Func; + } + + // If the size is constant and typeinfo is known in alloca.generic, and + // this alloca.generic is not escaped, we replace it with a alloca inst. + bool replaceWithAlloca(CallBase *CI, EscapeScope &ES) { + if (ES.isEscaped(CI)) + return false; + + auto *SizeOp = dyn_cast(CI->getArgOperand(1)); + if (!SizeOp || SizeOp->getZExtValue() > MaxAllocaObj) + return false; + + Value *TIArg = CI->getArgOperand(0)->stripPointerCasts(); + auto *GV = dyn_cast(TIArg); + if (!GV) + return false; + + AllocaInst *AI = nullptr; + BasicBlock &EntryBB = CI->getFunction()->getEntryBlock(); + Instruction *InsertPos = &*EntryBB.getFirstInsertionPt(); + IRBuilder<> IRB(InsertPos); + auto *TIType = StructType::getTypeByName(C, "TypeInfo"); + const Triple TT(CI->getModule()->getTargetTriple()); + if (isPrimitiveTypeInfo(GV)) { + SmallVector ElemTypes; + ElemTypes.push_back(TIType->getPointerTo()); + if (TT.isARM()) + ElemTypes.push_back(IRB.getInt32Ty()); + ElemTypes.push_back(IRB.getInt64Ty()); + AI = IRB.CreateAlloca(StructType::get(C, ElemTypes)); + Value *BC = IRB.CreateBitCast(AI, IRB.getInt8PtrTy()->getPointerTo()); + Value *GVExpr = ConstantExpr::getBitCast(GV, IRB.getInt8PtrTy()); + IRB.SetInsertPoint(CI); + IRB.CreateStore(GVExpr, BC); + } else { + StructType *ST = getTypeLayoutType(GV); + assert(ST && "alloca.generic layout info missing!"); + SmallVector ElemTypes; + ElemTypes.push_back(TIType->getPointerTo()); + if (TT.isARM()) + ElemTypes.push_back(IRB.getInt32Ty()); + ElemTypes.push_back(ST); + AI = IRB.CreateAlloca(StructType::get(C, ElemTypes)); + Value *BC = IRB.CreateBitCast(AI, IRB.getInt8PtrTy()->getPointerTo()); + Value *GVExpr = ConstantExpr::getBitCast(GV, IRB.getInt8PtrTy()); + IRB.CreateStore(GVExpr, BC); + uint64_t MemSize = DL.getStructLayout(ST)->getSizeInBytes(); + // %x.payload = getelementptr i8*, %x.i, i32 1 + // call @memset(%x.payload, 0, size) + Value *Data = IRB.CreateGEP(IRB.getInt8PtrTy(), BC, {IRB.getInt32(1)}); + IRB.SetInsertPoint(CI); + IRB.CreateMemSet(Data, IRB.getInt8(0), MemSize, Align(8)); + } + Value *ASC = IRB.CreateAddrSpaceCast(AI, IRB.getInt8PtrTy(1)); + CI->replaceAllUsesWith(ASC); + // Set terminator for invoke instruction. + if (auto *II = dyn_cast(CI)) { + IRB.CreateBr(II->getNormalDest()); + } + CI->eraseFromParent(); + return true; + } + + void setMetadata(Instruction *I, StringRef Kind) { + I->setMetadata(Kind, MDNode::get(C, {})); + } + + Value *createGCDiv(IRBuilder<> &Builder, const CallBase *CI) const { + switch (CI->getIntrinsicID()) { + default: + report_fatal_error("createGCDiv: Unsupported intrinsic type"); + return nullptr; + case Intrinsic::cj_division_check_sdiv: + return Builder.CreateSDiv(CI->getOperand(0), CI->getOperand(1), "Sdiv"); + case Intrinsic::cj_division_check_udiv: + return Builder.CreateUDiv(CI->getOperand(0), CI->getOperand(1), "Udiv"); + case Intrinsic::cj_division_check_srem: + return Builder.CreateSRem(CI->getOperand(0), CI->getOperand(1), "Srem"); + case Intrinsic::cj_division_check_urem: + return Builder.CreateURem(CI->getOperand(0), CI->getOperand(1), "Urem"); + } + } +}; +} // namespace + +static bool isCJBlockingFunc(Function &F) { + if (CJPipeline && F.getName().equals("system")) { + return true; + } + return false; +} + +static void insertGVForNativeFunc(Module &M, Function &F, + const TargetLibraryInfo &TLI) { + LibFunc LF; + // For the libc library function, if it is in the trustlist, we do not need + // to generate C2N Stub for it. + + if (F.hasFnAttribute("cj2c") && TLI.getLibFunc(F, LF)) { + if (!isCJBlockingFunc(F) && TLI.has(LF)) { + F.removeFnAttr("cj2c"); + return; + } + } + GlobalVariable *CJFuncGV = cast( + M.getOrInsertGlobal(F.getName().str() + ".CJStubGV", F.getType())); + CJFuncGV->setInitializer(&F); + CJFuncGV->setLinkage(GlobalVariable::InternalLinkage); + CJFuncGV->addAttribute("cj-native"); +} + +static TypeFlag getMallocType(CallBase *CB) { + const MDNode *MD = CB->getMetadata("MallocType"); + if (MD == nullptr) + return TF_NONE; + + StringRef Ty = dyn_cast(MD->getOperand(0).get())->getString(); + return StringSwitch(Ty) + .Case("HasFinalizer", TF_HAS_FINALIZER) + .Case("Future", TF_FUTURE_CLASS) + .Case("Mutex", TF_MUTEX_CLASS) + .Case("Monitor", TF_MONITOR_CLASS) + .Case("WaitQueue", TF_WAIT_QUEUE_CLASS) + .Case("WeakRef", TF_WEAK_REF_CLASS) + .Default(TF_NONE); +} + +// Declares MCC functions ready for backend processing. +static void declareRuntimeFunc(Module &M) { + LLVMContext &C = M.getContext(); + // declare C2NStub and N2CStub + auto *NativeStubFT = FunctionType::get(Type::getVoidTy(C), true); + M.declareCJRuntimeFunc(C2NStubStr, NativeStubFT, true); + M.declareCJRuntimeFunc(N2CStubStr, NativeStubFT, true); + + Type *RefType = Type::getInt8PtrTy(C, 1); + auto *FinalizerFT = FunctionType::get(RefType, RefType, false); + M.declareCJRuntimeFunc(FinalizerStr, FinalizerFT, true); +} + +static bool runtimeLoweringFunc(Function &F, CJIntrinsicLowering &Lowering) { + bool Changed = false; + EscapeScope ES(F); + + for (auto I = inst_begin(F); I != inst_end(F);) { + auto *CI = dyn_cast(&*I++); + if (!CI || CI->getCalledFunction() == nullptr) { + continue; + } + unsigned IID = CI->getIntrinsicID(); + switch (IID) { + default: + break; + case Intrinsic::cj_get_field_offset: { + LowerGetFieldOffset Lower(CI); + Lower.replaceGetFieldOffset(); + Changed = true; + break; + } + case Intrinsic::cj_is_reference: { + Lowering.replaceIsReference(CI); + Changed = true; + break; + } + case Intrinsic::cj_is_typeinfo_equal: { + Lowering.replaceIsTypeInfoEqual(CI); + Changed = true; + break; + } + case Intrinsic::cj_is_tupletype_of: { + Lowering.replaceIsTupleTypeOf(CI); + Changed = true; + break; + } + case Intrinsic::cj_get_obj_klass: + case Intrinsic::cj_acquire_rawdata: + case Intrinsic::cj_release_rawdata: + case Intrinsic::cj_set_location: + case Intrinsic::cj_pre_initialize_package: + case Intrinsic::cj_get_exception_wrapper: + case Intrinsic::cj_get_exception_typeid: + case Intrinsic::cj_get_real_heap_size: + case Intrinsic::cj_get_allocated_heap_size: + case Intrinsic::cj_get_max_heap_size: + case Intrinsic::cj_dump_heap_data: + case Intrinsic::cj_get_thread_number: + case Intrinsic::cj_get_blocking_thread_number: + case Intrinsic::cj_get_native_thread_number: + case Intrinsic::cj_get_type_info: + case Intrinsic::cj_is_subtype: + case Intrinsic::cj_get_gc_count: + case Intrinsic::cj_get_gc_time_us: + case Intrinsic::cj_get_gc_freed_size: + case Intrinsic::cj_start_cpu_profiling: + case Intrinsic::cj_stop_cpu_profiling: + case Intrinsic::cj_set_gc_threshold: + case Intrinsic::cj_post_throw_exception: + case Intrinsic::cj_register_implicit_exception_raisers: + case Intrinsic::cj_blackhole: + Lowering.replaceWithRuntimeFunc(CI, true, false); + Changed = true; + break; + case Intrinsic::cj_cross_access_barrier: + case Intrinsic::cj_get_exported_ref: + case Intrinsic::cj_remove_exported_ref: + case Intrinsic::cj_create_export_handle: + case Intrinsic::cj_fill_in_stack_trace: + Lowering.replaceWithRuntimeFunc(CI, false, false); + Changed = true; + break; + case Intrinsic::cj_get_vtable_func: + Lowering.replaceGetVTableFunc(CI); + Changed = true; + break; + case Intrinsic::cj_get_mtable_func: + Lowering.replaceGetMTableFunc(CI); + Changed = true; + break; + case Intrinsic::cj_throw_exception: + if (Triple(F.getParent()->getTargetTriple()).isOSWindows()) { + // For windows, throw_exception need stackmap, beacuse runtime stack + // unwinding depends on stackmap. + Lowering.replaceWithRuntimeFunc(CI, false, false); + } else { + Lowering.replaceWithRuntimeFunc(CI, true, false); + } + Changed = true; + break; + case Intrinsic::cj_malloc_object: { + Changed = true; + switch (getMallocType(CI)) { + case TF_HAS_FINALIZER: + Lowering.setHeapMallocSizeAlign(CI); + Lowering.replaceNewFinalizerFunc(CI); + break; + case TF_FUTURE_CLASS: + case TF_MUTEX_CLASS: + case TF_MONITOR_CLASS: + case TF_WAIT_QUEUE_CLASS: + Lowering.replaceFixedNewObject(CI); + break; + case llvm::TF_WEAK_REF_CLASS: + Lowering.setHeapMallocSizeAlign(CI); + Lowering.replaceNewWeakRefFunc(CI); + break; + default: + Lowering.setHeapMallocSizeAlign(CI); + Lowering.replaceWithRuntimeFunc(CI, false, true); + break; + } + CI->addRetAttr(Attribute::NoAlias); + break; + } + case Intrinsic::cj_alloca_generic: + Lowering.setHeapMallocSizeAlign(CI); + Lowering.replaceAllocaGeneric(CI, ES); + Changed = true; + break; + case Intrinsic::cj_malloc_array: + Lowering.replaceNewArray(CI); + CI->addRetAttr(Attribute::NoAlias); + Changed = true; + break; + case Intrinsic::cj_malloc_array_generic: + Lowering.replaceWithRuntimeFunc(CI, false, true); + CI->addRetAttr(Attribute::NoAlias); + Changed = true; + break; + case Intrinsic::cj_invoke_gc: + Lowering.replaceWithRuntimeFunc(CI, false, false); + Changed = true; + break; + case Intrinsic::cj_division_check_sdiv: + case Intrinsic::cj_division_check_udiv: + case Intrinsic::cj_division_check_srem: + case Intrinsic::cj_division_check_urem: + Lowering.markLoweringDivisionCheck(CI); + Changed = true; + break; + } + } + Lowering.finishAll(); + return Changed; +} + +PreservedAnalyses CJRuntimeLowering::run(Module &M, + ModuleAnalysisManager &AM) const { + if (!hasRunCangjieOpt(M)) { + M.setModuleFlag(Module::Warning, "Cangjie_OPT", + ConstantAsMetadata::get(ConstantInt::get( + Type::getInt32Ty(M.getContext()), 1))); + } + bool Changed = false; + auto &FAM = AM.getResult(M).getManager(); + // Declaring MCC Functions for Backend Processing. + declareRuntimeFunc(M); + CJIntrinsicLowering CJLowering(M); + + // traversal Function-Instruction to lower intrinsic + for (Function &F : M) { + auto &TLI = FAM.getResult(F); + // Insert a gv for native Func + if (F.hasFnAttribute("cj2c") || F.hasFnAttribute("c2cj")) { + insertGVForNativeFunc(M, F, TLI); + Changed = true; + } + + if (!CJLTOOpt) { + Changed |= runtimeLoweringFunc(F, CJLowering); + } + } + + if (!Changed) + return PreservedAnalyses::all(); + + PreservedAnalyses PA; + PA.preserve(); + return PA; +} + +namespace { +class CJRuntimeLoweringLegacyPass : public ModulePass { +public: + static char ID; + + explicit CJRuntimeLoweringLegacyPass() : ModulePass(ID) { + initializeCJRuntimeLoweringLegacyPassPass(*PassRegistry::getPassRegistry()); + } + ~CJRuntimeLoweringLegacyPass() = default; + + bool runOnModule(Module &M) override { + bool Changed = false; + // Declaring MCC Functions for Backend Processing. + declareRuntimeFunc(M); + CJIntrinsicLowering CJLowering(M); + + // traversal Function-Instruction to lower intrinsic + for (Function &F : M) { + const TargetLibraryInfo &TLI = + getAnalysis().getTLI(F); + // Insert a gv for native Func + if (F.hasFnAttribute("cj2c") || F.hasFnAttribute("c2cj")) { + insertGVForNativeFunc(M, F, TLI); + Changed = true; + } + + if (!CJLTOOpt) { + Changed |= runtimeLoweringFunc(F, CJLowering); + } + } + return Changed; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + } +}; +} // namespace + +char CJRuntimeLoweringLegacyPass::ID = 0; + +ModulePass *llvm::createCJRuntimeLoweringLegacyPass() { + return new CJRuntimeLoweringLegacyPass(); +} + +INITIALIZE_PASS(CJRuntimeLoweringLegacyPass, "cj-runtime-lowering", + "CJ Runtime Lowering", false, false) diff --git a/llvm/lib/Transforms/Scalar/CJSimpleOpt.cpp b/llvm/lib/Transforms/Scalar/CJSimpleOpt.cpp new file mode 100644 index 000000000..9acf404d9 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/CJSimpleOpt.cpp @@ -0,0 +1,900 @@ +//===- CJSimpleOpt.cpp - Simple opt of cangjie -------------------*- C++-*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// Some simple optimization points of cangjie +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/CJSimpleOpt.h" + +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/CFG.h" +#include "llvm/Analysis/PostDominators.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/PatternMatch.h" +#include "llvm/IR/SafepointIRVerifier.h" +#include "llvm/Pass.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Transforms/Scalar.h" + +using namespace llvm; + +static cl::opt CJCallRetOpt("cj-callret-opt", cl::Hidden, cl::init(true)); +static cl::opt CJTsanSupport("cj-tsan-support-for-mutexopt", cl::Hidden, + cl::init(false)); + +// Call ret opt +struct CallRetState { + Function *F; + PostDominatorTree &PDT; + CallRetState(Function *F, PostDominatorTree &PDT) : F(F), PDT(PDT) {} + + class CallRetUnit { + public: + CallRetUnit(CallBase *CB, AllocaInst *CallRet, CallBase *CJMemset, + CallBase *Memcpy, Value *DST, CallBase *DstCJMemset) + : CB(CB), CallRet(CallRet), CJMemset(CJMemset), Memcpy(Memcpy), + DST(DST), DstCJMemset(DstCJMemset){}; + ~CallRetUnit() = default; + + // The call of sret + CallBase *CB; + // Sret of the call + AllocaInst *CallRet; + // CJ memset of the sret if it exists + CallBase *CJMemset; + // Memcpy from sret to dst + CallBase *Memcpy; + // Dst of memcpy + Value *DST; + // Dst CJ memset if it exists + CallBase *DstCJMemset; + }; + + // Scan all users of sret. When sret is only used as an intermediate + // local variable of an assignment operation, it can be optimized. + bool scanCallRetUses(CallRetUnit &CRU, Instruction *I, + SetVector &Uses) { + for (auto *Use : I->users()) { + auto *Inst = dyn_cast(Use); + if (Inst == nullptr) + return false; + if (Inst == CRU.CB || Uses.contains(Inst)) + continue; + Uses.insert(Inst); + if (isa(Inst) || isa(Inst)) { + if (scanCallRetUses(CRU, Inst, Uses)) + continue; + return false; + } + auto *CB = dyn_cast(Inst); + if (CB == nullptr) { + return false; + } + if (CB->getIntrinsicID() != Intrinsic::cj_memset && + CB->getIntrinsicID() != Intrinsic::memcpy) { + return false; + } + if (CB->getIntrinsicID() == Intrinsic::cj_memset) { + if (CRU.CJMemset == nullptr) { + CRU.CJMemset = CB; + continue; + } + return false; + } + if (CB->getArgOperand(1)->stripPointerCasts() == CRU.CallRet && + CRU.Memcpy == nullptr) { + CRU.Memcpy = CB; + CRU.DST = CB->getArgOperand(0)->stripPointerCasts(); + continue; + } + return false; + } + return true; + } + + void getDstUses(Value *Val, SetVector &DstUses) { + for (auto *Use : Val->users()) { + auto *V = dyn_cast(Use); + if (DstUses.contains(V)) + continue; + DstUses.insert(V); + getDstUses(V, DstUses); + } + } + + bool optCJCallRet() { + if (!CJCallRetOpt) { + return false; + } + bool Changed = false; + SetVector Memsets; + SetVector FuncInsts; + for (auto I = inst_begin(F); I != inst_end(F);) { + FuncInsts.insert(&*I++); + } + for (auto *FuncInst : FuncInsts) { + auto *CB = dyn_cast(FuncInst); + if (CB == nullptr || + CB->getArgOperandWithAttribute(Attribute::StructRet) == nullptr) + continue; + auto *CallRet = + dyn_cast(CB->getArgOperand(0)->stripPointerCasts()); + if (CallRet == nullptr) + continue; + CallRetUnit CRU(CB, CallRet, nullptr, nullptr, nullptr, nullptr); + SetVector Uses; + if (!scanCallRetUses(CRU, CallRet, Uses) || CRU.Memcpy == nullptr || + !isPotentiallyReachable(CRU.CB, CRU.Memcpy)) { + continue; + } + if (!isa(CRU.DST) && !isa(CRU.DST) && + !isa(CRU.DST)) { + continue; + } + SetVector DstUses; + auto *GEP = dyn_cast(CRU.DST); + if (GEP != nullptr) { + Value *Src = GEP->getPointerOperand()->stripPointerCasts(); + if (!isa(Src) && !isa(Src)) { + continue; + } + getDstUses(Src, DstUses); + } else { + getDstUses(CRU.DST, DstUses); + } + + if (!scanDstUses(CRU, DstUses) || + CRU.DST->getType() != CRU.CallRet->getType()) { + continue; + } + Changed |= replaceCJCallRet(CRU, GEP, Memsets); + } + for (auto *Memset : Memsets) { + Memset->eraseFromParent(); + } + return Changed; + } + + // Scan all DST users and check whether any other operations + // are performed on DST between call and memcpy. + bool scanDstUses(CallRetUnit &CRU, SetVector &DstUses) { + for (auto *Use : DstUses) { + auto *I = dyn_cast(Use); + if (I == nullptr || I == CRU.Memcpy || I == CRU.DST) + continue; + if (isPotentiallyReachable(CRU.CB, I) && + isPotentiallyReachable(I, CRU.Memcpy)) { + if (isa(I) || isa(I)) + continue; + auto *CB = dyn_cast(I); + if (CB == nullptr) + return false; + if (CB->getIntrinsicID() == Intrinsic::cj_memset && + CRU.DstCJMemset == nullptr) { + CRU.DstCJMemset = CB; + continue; + } + return false; + } + } + return true; + } + + bool replaceCJCallRet(CallRetUnit &CRU, GetElementPtrInst *GEP, + SetVector &Memsets) { + if (GEP != nullptr) { + if (CRU.CJMemset == nullptr) { + auto *CallRetWithoutCast = + dyn_cast(CRU.CB->getArgOperand(0)); + if (CallRetWithoutCast == CRU.CallRet) { + GEP->moveBefore(CRU.CB); + } else { + GEP->moveBefore(CallRetWithoutCast); + } + } else + return false; + } + if (CRU.CJMemset != nullptr && !PDT.dominates(CRU.CB, CRU.CJMemset)) + return false; + if (isa(CRU.DST) && CRU.CJMemset != nullptr) + Memsets.insert(CRU.CJMemset); + if (CRU.DstCJMemset != nullptr) + Memsets.insert(CRU.DstCJMemset); + CRU.CallRet->replaceAllUsesWith(CRU.DST); + return true; + } +}; + +// FPToSI Optimization +// +// fadd +// xxx +// bitcast +// and1 +// cmp1 +// / \ +// inf.or.nan not.inf.nan +// overflow / \ +// overflow lower.bound.ok +// / \ +// overflow upper.bound.ok +// fptosi +// to +// +// fadd +// xxx +// fptosi +// call get.fp.state +// and2 +// cmp2 +// / \ +// upper.bound.ok fp.convert.exception +// call reset.fp.state +// bitcast +// and1 +// cmp1 +// / \ +// inf.or.nan not.inf.nan +// overflow / \ +// overflow lower.bound.ok +// / \ +// overflow upper.bound.ok +struct FPToSIState { + static constexpr char FPExceptionBBName[] = "fp.convert.exception"; + // Float value is Inf or Nan in IEEE754. + static constexpr uint64_t DOUBLE_INF_OR_NAN = 0x7FF0000000000000; + static constexpr uint64_t FLOAT_INF_OR_NAN = 0x7F800000; + Function &F; + explicit FPToSIState(Function &F) : F(F) {} + + BasicBlock *getSuccBBByName(BranchInst *BR, StringRef Name) { + for (unsigned i = 0; i < BR->getNumSuccessors(); i++) { + if (BR->getSuccessor(i)->getName().startswith(Name)) { + return BR->getSuccessor(i); + } + } + return nullptr; + } + + BasicBlock *getNotInfNanBB(BranchInst *BR) { + return getSuccBBByName(BR, "not.inf.nan"); + } + + BasicBlock *getLowerBoundOkBB(BranchInst *BR) { + return getSuccBBByName(BR, "lower.bound.ok"); + } + + BasicBlock *getUpperBoundOkBB(BranchInst *BR) { + return getSuccBBByName(BR, "upper.bound.ok"); + } + + void getFloatBitcastInsts(SmallVectorImpl &InstList) { + for (auto I = inst_begin(F); I != inst_end(F);) { + // check float inf or nan: + // %1 = bitcast double %0 to i64 + // %2 = and i64 %1, 9218868437227405312 + // %notInfOrNan = icmp ne i64 %2, 9218868437227405312 + // br i1 %notInfOrNan, label %not.inf.nan, label %inf.or.nan + auto *BCI = dyn_cast(&*I++); + if (!BCI || + // 32: int32 bitSize, 64: int64 bitSize + !(BCI->getType()->isIntegerTy(32) || + BCI->getType()->isIntegerTy(64)) || + !(BCI->getOperand(0)->getType()->isDoubleTy() || + BCI->getOperand(0)->getType()->isFloatTy()) || + BCI->getParent()->getName().startswith(FPExceptionBBName)) { + continue; + } + + auto *AndInst = cast(BCI->getNextNode()); + if (AndInst->getOpcode() != Instruction::And) + continue; + auto *AndOp1 = dyn_cast(AndInst->getOperand(1)); + if (!AndOp1) + continue; + if (!(AndOp1->equalsInt(DOUBLE_INF_OR_NAN) || + AndOp1->equalsInt(FLOAT_INF_OR_NAN))) + continue; + + InstList.push_back(BCI); + } + } + + bool optFPToSIThreeCmp() { + // AArch64 is supported first, and x86 is later. + if (!Triple(F.getParent()->getTargetTriple()).isAArch64() || + !F.hasCangjieGC()) { + return false; + } + + bool Changed = false; + SmallVector BitCastInstList; + getFloatBitcastInsts(BitCastInstList); + // check successor BB + for (auto *BCI : BitCastInstList) { + // not.inf.nan: // check integer lower bound + // %f2i.lt.min = fcmp olt double 0xC3E0000000000001, %0 + // br i1 %f2i.lt.min, label %lower.bound.ok, label %lower.bound.overflow + BasicBlock *NotInfNanBB = nullptr; + if (auto *BR = dyn_cast(BCI->getParent()->getTerminator())) { + NotInfNanBB = getNotInfNanBB(BR); + if (!NotInfNanBB) { + continue; + } + } else { + continue; + } + + // lower.bound.ok: // check integer upper bound + // %f2i.gt.max = fcmp olt double %0, 0x43E0000000000000 + // br i1 %f2i.gt.max, label %upper.bound.ok, label %upper.bound.overflow + BasicBlock *LowerBoundOkBB = nullptr; + if (auto *BR = dyn_cast(NotInfNanBB->getTerminator())) { + LowerBoundOkBB = getLowerBoundOkBB(BR); + if (!LowerBoundOkBB) { + continue; + } + } else { + continue; + } + + BasicBlock *UpperBoundOkBB = nullptr; + if (auto *BR = dyn_cast(LowerBoundOkBB->getTerminator())) { + UpperBoundOkBB = getUpperBoundOkBB(BR); + if (!UpperBoundOkBB) { + continue; + } + } else { + continue; + } + + bool HasFPToInt = false; + for (auto &I : *UpperBoundOkBB) { + Instruction *FI = dyn_cast(&I); + if (!FI) { + FI = dyn_cast(&I); + } + if (FI && FI->getOperand(0) == BCI->getOperand(0) && + // 32: int32 bitSize, 64: int64 bitSize + (FI->getType()->isIntegerTy(32) || + FI->getType()->isIntegerTy(64))) { + FI->moveBefore(BCI); + HasFPToInt = true; + break; + } + } + + if (!HasFPToInt) { + continue; + } + + repalceThreeOverflowCmp(BCI, UpperBoundOkBB, F); + Changed = true; + } + return Changed; + } + + void repalceThreeOverflowCmp(BitCastInst *BCI, BasicBlock *UpperBoundOkBB, + Function &F) { + IRBuilder<> Builder(BCI); + auto *FI = BCI->getPrevNode(); + + // insert call i64 @llvm.cj.get.fp.state + Function *GetFpStateFunc = Intrinsic::getDeclaration( + F.getParent(), Intrinsic::cj_get_fp_state, {FI->getType()}); + GetFpStateFunc->setDoesNotAccessMemory(); + CallInst *GetFpStateCI = Builder.CreateCall(GetFpStateFunc, {FI}); + + // if fp to int failed, the lastBit of %fpstate is 1 + // insert and %fpstate, 1 + Value *LastBit = Builder.CreateAnd(GetFpStateCI, (uint64_t)1); + // insert icmp %and, 0 + auto *CmpEQ = dyn_cast(Builder.CreateICmpEQ( + LastBit, + ConstantInt::get(Type::getInt64Ty(F.getContext()), (uint64_t)0))); + + // add reset fpstate instr after CJSpecificOpt + // split new BB + BasicBlock *ConvertExcepBB = BCI->getParent()->splitBasicBlock( + CmpEQ->getNextNode(), FPExceptionBBName); + Instruction *NewBr = CmpEQ->getParent()->getTerminator(); + IRBuilder<> BuilderBr(NewBr); + BuilderBr.CreateCondBr(CmpEQ, UpperBoundOkBB, ConvertExcepBB); + NewBr->eraseFromParent(); + } +}; + +// Create CJ_MCC_MutexLock fastpath. +struct MutexLockLower { + Function *F; + explicit MutexLockLower(Function *F) : F(F) {} + + static constexpr char StubFuncName[] = "CJMutexLockStub"; + static constexpr char RTSlowPathName[] = "CJ_MCC_MutexLockSlowPath"; + static constexpr char GetCJThreadIDName[] = "GetCJThreadIdForMutexOpt"; + + Function *createMutexLockStub(FunctionType *FT, Module *M) { + Function *F = + cast(M->getOrInsertFunction(StubFuncName, FT).getCallee()); + F->setLinkage(GlobalValue::InternalLinkage); + F->addFnAttr(Attribute::AlwaysInline); + + // Create function body. + BasicBlock *BB0 = BasicBlock::Create(F->getContext(), "entry", F); + BasicBlock *BB1 = BasicBlock::Create(F->getContext(), "if.then", F); + BasicBlock *BB2 = BasicBlock::Create(F->getContext(), "if.else", F); + IRBuilder<> IRB(BB0); + // Prepare mutex params. + Value *MutexPtr = F->getArg(0); + Type *MutexType = MutexPtr->getType()->getNonOpaquePointerElementType(); + Value *MutexCjthreadIdPtr = + IRB.CreateBitCast(IRB.CreateGEP(MutexType, MutexPtr, IRB.getInt32(8)), + Type::getInt64PtrTy(F->getContext(), 1)); + Value *MutexOwnCountPtr = + IRB.CreateBitCast(IRB.CreateGEP(MutexType, MutexPtr, IRB.getInt32(16)), + Type::getInt64PtrTy(F->getContext(), 1)); + Value *MutexStatePtr = + IRB.CreateBitCast(IRB.CreateGEP(MutexType, MutexPtr, IRB.getInt32(24)), + Type::getInt64PtrTy(F->getContext(), 1)); + Value *Expected = ConstantInt::get(IRB.getInt64Ty(), 0); + // 0x4: LOCKED flag which is defined in runtime + Value *Desired = ConstantInt::get(IRB.getInt64Ty(), 0x4); + // try to get cj mutex + AtomicCmpXchgInst *CmpRes = + IRB.CreateAtomicCmpXchg(MutexStatePtr, Expected, Desired, MaybeAlign(), + AtomicOrdering::SequentiallyConsistent, + AtomicOrdering::SequentiallyConsistent); + Value *Success = IRB.CreateExtractValue(CmpRes, 1, "success"); + IRB.CreateCondBr(Success, BB1, BB2); + + // Build BB1 + IRB.SetInsertPoint(BB1); + auto *MutexOwnCount = IRB.CreateLoad( + MutexOwnCountPtr->getType()->getNonOpaquePointerElementType(), + MutexOwnCountPtr); + Value *OwnCountInc = + IRB.CreateAdd(MutexOwnCount, ConstantInt::get(IRB.getInt64Ty(), 1)); + IRB.CreateStore(OwnCountInc, MutexOwnCountPtr); + FunctionType *FuncType = FunctionType::get(IRB.getInt64Ty(), false); + auto *GetCJThreadIdFunc = dyn_cast( + F->getParent() + ->getOrInsertFunction(GetCJThreadIDName, FuncType) + .getCallee()); + GetCJThreadIdFunc->addFnAttr( + Attribute::get(F->getContext(), "gc-leaf-function")); + GetCJThreadIdFunc->addFnAttr(Attribute::get(F->getContext(), "cj-runtime")); + GetCJThreadIdFunc->setCallingConv(CallingConv::CangjieGC); + GetCJThreadIdFunc->setUnnamedAddr(GlobalValue::UnnamedAddr::Local); + auto *CallInst = IRB.CreateCall(GetCJThreadIdFunc); + CallInst->setCallingConv(CallingConv::CangjieGC); + auto *StoreInst = IRB.CreateStore(CallInst, MutexCjthreadIdPtr); + StoreInst->setAtomic(AtomicOrdering::Release); + IRB.CreateRetVoid(); + + // Build BB2 + IRB.SetInsertPoint(BB2); + FunctionType *SlowPathFuncType = + FunctionType::get(IRB.getVoidTy(), {MutexPtr->getType()}, false); + auto *SlowPathFunc = dyn_cast( + F->getParent() + ->getOrInsertFunction(RTSlowPathName, SlowPathFuncType) + .getCallee()); + IRB.CreateCall(SlowPathFunc, {MutexPtr}); + IRB.CreateRetVoid(); + + return F; + } + + bool optCJMutexLock() { + if (!F->hasCangjieGC() || CJTsanSupport) + return false; + + SetVector CallBases; + for (auto I = inst_begin(F); I != inst_end(F);) { + CallBase *CB = dyn_cast(&*I++); + if (!CB || !CB->getCalledFunction()) + continue; + if (CB->getCalledFunction()->getName().isCJMutexLock()) + CallBases.insert(CB); + } + if (CallBases.empty()) + return false; + + FunctionType *FT = FunctionType::get( + Type::getVoidTy(F->getContext()), + {CallBases.front()->getArgOperand(0)->getType()}, false); + Function *Stub = createMutexLockStub(FT, F->getParent()); + for (auto *CB : CallBases) { + CallInst::Create(Stub, {CB->getArgOperand(0)}, "", CB); + CB->eraseFromParent(); + } + return true; + } +}; + +// Icmp of a and b. +// 1. If a > 0, then +// a slt b => a ult b; +// also for sgt and ugt. +// 2. If a < 0, then +// a ult b => a slt b; +// also for ugt and sgt. +struct ICmpOptimization { + Function &F; + DominatorTree &DT; + + ICmpOptimization(Function &F, DominatorTree &DT) : F(F), DT(DT) {} + + BranchInst *brAndICmpConditional(BasicBlock *BB) { + BranchInst *BI = dyn_cast(BB->getTerminator()); + if (!BI || BI->isUnconditional() || !isa(BI->getCondition())) + return nullptr; + return BI; + } + + bool isLoadFromArray(Value *Ptr, unsigned Times = 3) { + auto IsArrayType = [](Type *Ty) { + auto *ST = dyn_cast(Ty); + return ST && ST->hasName() && + (ST->getName().isCangjieArrayLayout() || + ST->getName().isCangjieArrayRecord()); + }; + if (Times == 0) + return false; + if (auto *GEP = dyn_cast(Ptr)) + return IsArrayType(GEP->getSourceElementType()) || + isLoadFromArray(GEP->getPointerOperand(), --Times); + else if (auto *BC = dyn_cast(Ptr)) { + Value *OP = BC->getOperand(0); + return (isa(OP->getType()) && + IsArrayType(OP->getType()->getNonOpaquePointerElementType())) || + isLoadFromArray(OP, --Times); + } + return false; + } + + bool isPositiveOrZeroValue(Value *V, SmallSet &Set) { + if (auto *CI = dyn_cast(V)) + return CI->getValue().isNonNegative(); + if (Set.contains(V)) + return true; + Instruction *I = dyn_cast(V); + if (!I) + return false; + Set.insert(I); + switch (I->getOpcode()) { + default: + return false; + case Instruction::Call: { + auto *CB = cast(I); + auto IID = CB->getIntrinsicID(); + if (IID != Intrinsic::smul_with_overflow && + IID != Intrinsic::umul_with_overflow && + IID != Intrinsic::sadd_with_overflow && + IID != Intrinsic::uadd_with_overflow) + return false; + } + [[fallthrough]]; + case Instruction::Add: + case Instruction::Mul: + case Instruction::Xor: + case Instruction::And: + return isPositiveOrZeroValue(I->getOperand(0), Set) && + isPositiveOrZeroValue(I->getOperand(1), Set); + case Instruction::PHI: { + auto *Phi = cast(I); + for (Value *Val : Phi->incoming_values()) { + if (!isPositiveOrZeroValue(Val, Set)) + return false; + } + return true; + } break; + case Instruction::Select: { + auto *SI = cast(I); + return isPositiveOrZeroValue(SI->getTrueValue(), Set) && + isPositiveOrZeroValue(SI->getFalseValue(), Set); + } break; + case Instruction::ExtractValue: + return isPositiveOrZeroValue(I->getOperand(0), Set); + case Instruction::Load: + auto *LI = cast(I); + return isLoadFromArray(LI->getPointerOperand()); + } + } + + bool tryEliminateICmp(BranchInst *BI, BasicBlock *BB, unsigned Times = 3) { + auto *Condition = cast(BI->getCondition()); + auto Pred = Condition->getPredicate(); + switch (Pred) { + default: + return false; // Current, return false + case CmpInst::ICMP_ULT: + Value *OP = Condition->getOperand(0); + SmallSet Set; + if (!isPositiveOrZeroValue(OP, Set)) + return false; + break; + } + auto *DTNode = DT.getNode(BB); + if (DTNode == nullptr) + return false; + auto *N = DTNode->getIDom(); + while (N != nullptr && Times--) { + BasicBlock *BB = N->getBlock(); + assert(DT.getNode(BB) && "DTNode of BB can not be nullptr!"); + N = DT.getNode(BB)->getIDom(); + if (auto *BI1 = brAndICmpConditional(BB); BI1 && icmpInstMatch(BI, BI1)) + return true; + } + return false; + } + + bool icmpInstMatch(BranchInst *OriginBI, BranchInst *CurrentBI) { + using namespace PatternMatch; + auto *ICI0 = cast(OriginBI->getCondition()); + auto *ICI1 = cast(CurrentBI->getCondition()); + + // If we know that OP1 and OP2 are related by a simple relation like OP2 = + // OP1 + // + x (x > 0), then we can determine that OP1 < OP2. + auto IsLessThanEQ = [](Value *OP1, Value *OP2, bool EQ) { + Value *V1 = nullptr; + Value *V2 = nullptr; + if (match(OP2, m_Add(m_Value(V1), m_Value(V2))) || + match(OP2, m_Or(m_Value(V1), m_Value(V2)))) { + // OP2 = OP1 + x, x >= 0 + auto *CI1 = V1 == OP1 ? dyn_cast(V2) : nullptr; + auto *CI2 = V2 == OP1 ? dyn_cast(V1) : nullptr; + return (CI1 && (EQ ? CI1->getValue().isNonNegative() + : CI1->getValue().isStrictlyPositive())) || + (CI2 && (EQ ? CI2->getValue().isNonNegative() + : CI2->getValue().isStrictlyPositive())); + } else if (match(OP2, m_Sub(m_Value(V1), m_Value(V2)))) { + // OP2 = OP1 - x, x <= 0 + auto *CI = V1 == OP1 ? dyn_cast(V2) : nullptr; + return CI && (EQ ? CI->getValue().isNonPositive() + : CI->getValue().isNegative()); + } + + if (match(OP1, m_Sub(m_Value(V1), m_Value(V2)))) { + // OP1 = OP2 - x, x >= 0 + auto *CI = V1 == OP2 ? dyn_cast(V2) : nullptr; + return CI && (EQ ? CI->getValue().isNonNegative() + : CI->getValue().isStrictlyPositive()); + } else if (match(OP1, m_Add(m_Value(V1), m_Value(V2))) || + match(OP1, m_Add(m_Value(V1), m_Value(V2)))) { + // OP1 = OP2 + x, x <= 0 + auto *CI1 = V1 == OP2 ? dyn_cast(V2) : nullptr; + auto *CI2 = V2 == OP2 ? dyn_cast(V1) : nullptr; + return (CI1 && (EQ ? CI1->getValue().isNonPositive() + : CI1->getValue().isNegative())) || + (CI2 && (EQ ? CI2->getValue().isNonPositive() + : CI2->getValue().isNegative())); + } + return false; + }; + + switch (ICI0->getPredicate()) { + default: + break; + case CmpInst::ICMP_ULT: + Value *OP = ICI0->getOperand(0); + auto Pred = ICI1->getPredicate(); + if (ICI1->getOperand(0) == OP) { + bool Tag1 = + (Pred == CmpInst::ICMP_ULT || Pred == CmpInst::ICMP_SLT) && + DT.dominates(CurrentBI->getSuccessor(0), OriginBI->getParent()); + bool Tag2 = + (Pred == CmpInst::ICMP_UGE || Pred == ICmpInst::ICMP_SGE) && + DT.dominates(CurrentBI->getSuccessor(1), OriginBI->getParent()); + if (Tag1 || Tag2) + return IsLessThanEQ(ICI1->getOperand(1), ICI0->getOperand(1), true); + bool Tag3 = + (Pred == CmpInst::ICMP_ULE || Pred == CmpInst::ICMP_SLE) && + DT.dominates(CurrentBI->getSuccessor(0), OriginBI->getParent()); + bool Tag4 = + (Pred == CmpInst::ICMP_UGT || Pred == CmpInst::ICMP_SGT) && + DT.dominates(CurrentBI->getSuccessor(1), OriginBI->getParent()); + if (Tag3 || Tag4) + return IsLessThanEQ(ICI1->getOperand(1), ICI0->getOperand(1), false); + } else if (ICI1->getOperand(1) == OP) { + bool Tag1 = + (Pred == CmpInst::ICMP_UGT || Pred == CmpInst::ICMP_SGT) && + DT.dominates(CurrentBI->getSuccessor(0), OriginBI->getParent()); + bool Tag2 = + (Pred == CmpInst::ICMP_SLE || Pred == CmpInst::ICMP_ULE) && + DT.dominates(CurrentBI->getSuccessor(1), OriginBI->getParent()); + if (Tag1 || Tag2) + return IsLessThanEQ(ICI1->getOperand(0), ICI0->getOperand(1), true); + bool Tag3 = + (Pred == CmpInst::ICMP_UGE || Pred == CmpInst::ICMP_SGE) && + DT.dominates(CurrentBI->getSuccessor(0), OriginBI->getParent()); + bool Tag4 = + (Pred == CmpInst::ICMP_SLT || Pred == CmpInst::ICMP_ULT) && + DT.dominates(CurrentBI->getSuccessor(1), OriginBI->getParent()); + if (Tag3 || Tag4) + return IsLessThanEQ(ICI1->getOperand(0), ICI0->getOperand(1), false); + } + } + return false; + } + + bool eliminateICmp() { + bool Changed = false; + for (auto &BB : F) { + if (auto *BI = brAndICmpConditional(&BB); + BI && tryEliminateICmp(BI, &BB)) { + BI->setCondition(ConstantInt::getTrue(BI->getContext())); + Changed = true; + } + } + return Changed; + } +}; + +struct ArraySizeConstantFold { + Function &F; + + explicit ArraySizeConstantFold(Function &F) : F(F) {} + + bool isArraySizeGEP(CallBase *CB, GetElementPtrInst *GEP) { + // The Base of the GEP must be CB. + Value *Base = getUnderlyingObject(GEP->getPointerOperand()); + if (Base != CB) + return false; + // %0 = call i8 addrspace (1)* @CJ_MCC_NewArrayStub(i64 size, arrayKlass) + // %1 = getelementptr inbounds i8, i8 addrspace (1)* %0, i64 8 + if (isInt8AS1Pty(Base->getType())) { + if (GEP->getNumIndices() > 1) + return false; + auto *Const = dyn_cast(GEP->getOperand(1)); + // Check whether GEP is to get array size. + return Const && Const->getZExtValue() == 8; + } + // %ArrayLayout.xxx = type { %ArrayBase, [ 0 * xxx ] } + // %ArrayBase = type { %ObjLayout.Object, i64 } + // ... + // %0 = call i8 addrspace (1)* @CJ_MCC_NewArrayStub(i64 size, arrayKlass) + // %1 = bitcast i8 addrspace (1)* %0 to %ArrayLayout.xxx addrspace (1)* + // %2 = getelementptr inbounds ArrayLayout.xxx, + // %ArrayLayout.xxx addrspace (1)* %0, i64 0, i32 0, i32 1 + auto *ST = dyn_cast(GEP->getSourceElementType()); + // gepIndexNum: Number of other parameters except SrcPtr + // example: + // GEP %SrcPtr, 0, 0, 1 + constexpr int gepIndexNum = 3; + if (ST == nullptr || !ST->getStructName().isCangjieArrayLayout() || + GEP->getNumIndices() != gepIndexNum) + return false; + + auto *Const0 = dyn_cast(GEP->getOperand(1)); + auto *Const1 = dyn_cast(GEP->getOperand(2)); + auto *Const2 = dyn_cast(GEP->getOperand(3)); + return Const0 != nullptr && Const0->getZExtValue() == 0 && + Const1 != nullptr && Const1->getZExtValue() == 0 && + Const2 != nullptr && Const2->getZExtValue() == 1; + } + + void getArraySizeGEP(CallBase *CB, SetVector &Uses, + SetVector &ArraySizeGEP) { + for (auto *Use : Uses) { + if (auto *GEP = dyn_cast(Use); + GEP && isArraySizeGEP(CB, GEP)) { + ArraySizeGEP.insert(GEP); + } + } + } + + bool replaceSizeGEPtoConst(SetVector &Uses, ConstantInt *Size, + SetVector &ArraySizeGEP) { + bool Changed = false; + for (auto *Use : Uses) { + auto *LI = dyn_cast(Use); + if (LI == nullptr) + continue; + auto *GEP = dyn_cast( + LI->getPointerOperand()->stripPointerCasts()); + if (GEP == nullptr) + continue; + if (ArraySizeGEP.contains(GEP)) { + if (LI->getType() == Size->getType()) { + LI->replaceAllUsesWith(Size); + Changed = true; + } else { + continue; + } + } + } + return Changed; + } + + bool optArraySizeConst() { + bool Changed = false; + + DenseSet ArrayFuncs = { + "CJ_MCC_NewArray", "CJ_MCC_NewArray8", "CJ_MCC_NewArray16", + "CJ_MCC_NewArray32", "CJ_MCC_NewArray64", "CJ_MCC_NewObjArray"}; + + auto GetUses = [](Value *Val, SetVector &Uses) { + SmallVector Worklist = {Val}; + while (!Worklist.empty()) { + Value *V = Worklist.pop_back_val(); + for (auto *Use : V->users()) { + auto *U = dyn_cast(Use); + if (Uses.contains(U)) + continue; + Uses.insert(U); + Worklist.push_back(U); + } + } + }; + + for (auto &I : instructions(F)) { + if (auto *CB = dyn_cast(&I)) { + Function *Callee = CB->getCalledFunction(); + if (Callee && ArrayFuncs.contains(Callee->getName())) { + if (auto *Size = dyn_cast(CB->getArgOperand(1))) { + SetVector Uses; + GetUses(CB, Uses); + SetVector ArraySizeGEP; + getArraySizeGEP(CB, Uses, ArraySizeGEP); + Changed |= replaceSizeGEPtoConst(Uses, Size, ArraySizeGEP); + } + } + } + } + + return Changed; + } +}; + +struct AnalysisResults { + DominatorTree *DT = nullptr; + PostDominatorTree *PDT = nullptr; +}; + +static bool runImpl(Function &F, function_ref GetAnalysis) { + bool Changed = false; + + Changed |= FPToSIState(F).optFPToSIThreeCmp(); + AnalysisResults AR = GetAnalysis(); + if (AR.PDT) + Changed |= CallRetState(&F, *AR.PDT).optCJCallRet(); + if (AR.DT) + Changed |= ICmpOptimization(F, *AR.DT).eliminateICmp(); + Changed |= ArraySizeConstantFold(F).optArraySizeConst(); + Changed |= MutexLockLower(&F).optCJMutexLock(); + + return Changed; +} + +PreservedAnalyses CJSimpleOpt::run(Function &F, + FunctionAnalysisManager &FAM) const { + auto GetAnalysis = [&]() -> AnalysisResults { + if (!F.isDeclaration()) + return {&FAM.getResult(F), + &FAM.getResult(F)}; + return AnalysisResults(); + }; + if (runImpl(F, GetAnalysis)) { + return PreservedAnalyses::none(); + } + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Transforms/Scalar/CJSimpleRangeAnalysis.cpp b/llvm/lib/Transforms/Scalar/CJSimpleRangeAnalysis.cpp new file mode 100644 index 000000000..247781471 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/CJSimpleRangeAnalysis.cpp @@ -0,0 +1,514 @@ +//===- CJSimpleRangeAnalysis.cpp --------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This pass implements simple range analysis for integers and floats whose +// values are integers, and optimizes CFG based on the analysis result. +// +//===----------------------------------------------------------------------===// +#include "llvm/Transforms/Scalar/CJSimpleRangeAnalysis.h" + +#include "llvm/Analysis/DomTreeUpdater.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/PatternMatch.h" +#include "llvm/InitializePasses.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Utils/Local.h" +#include "llvm/Transforms/Utils/SSAUpdater.h" +#include + +using namespace llvm; +static cl::opt EnableCJSimpleRA("enable-cj-simple-ra", cl::Hidden, + cl::init(true)); + +constexpr unsigned BIT_WIDTH_64 = 64; + +// Get constant int if valid. +std::pair +CJSimpleRangeAnalysisPass::getValidConstantInteger(Value *V) { + if (auto *CI = dyn_cast(V)) + return {true, CI->getValue().getSExtValue()}; + if (auto *CFP = dyn_cast(V)) { + APFloat APF = CFP->getValueAPF(); + if (!APF.isIEEE() || APF.isInfinity() || !APF.isInteger() || APF.isNaN() || + !V->getType()->isDoubleTy()) + return {false, 0}; + double Value = APF.convertToDouble(); + // The integers represented by double are also inaccurate up to a certain + // point. Conservatively, only double in the int32 range is considered. + if (Value > static_cast(INT32_MAX) || + Value < static_cast(INT32_MIN)) + return {false, 0}; + return {true, static_cast(Value)}; + } + return {false, 0}; +} + +// Calculate the difference of LHS based on base value and return base. +Value *CJSimpleRangeAnalysisPass::getRangeFromInitValue( + Value *V, APInt &APValue, SmallSet &Visited) { + Value *OPLHS; + ConstantInt *OPConstInt; + if (!Visited.insert(V).second) + return V; + // Phi needs to ensure that all incoming values have the same base and + // difference. + if (auto *Phi = dyn_cast(V)) { + Value *OriginBV = nullptr; + APInt OriginDiff = APValue; + // Phi in loop header, stop. + auto *L = LI->getLoopFor(Phi->getParent()); + if (L && L->getHeader() == Phi->getParent()) + return V; + // Be sure all incoming values are from same base value. + for (unsigned I = 0, E = Phi->getNumIncomingValues(); I != E; ++I) { + APInt TmpDiff = APValue; + auto *BV = + getRangeFromInitValue(Phi->getIncomingValue(I), TmpDiff, Visited); + if (!OriginBV) { + OriginBV = BV; + OriginDiff = TmpDiff; + } else if (OriginBV != BV || OriginDiff != TmpDiff) { + return V; + } + } + APValue = OriginDiff; + return OriginBV; + } + using namespace PatternMatch; + if (match(V, m_Add(m_Value(OPLHS), m_ConstantInt(OPConstInt)))) { + APValue += OPConstInt->getSExtValue(); + return getRangeFromInitValue(OPLHS, APValue, Visited); + } + if (match(V, m_Sub(m_Value(OPLHS), m_ConstantInt(OPConstInt)))) { + APValue -= OPConstInt->getSExtValue(); + return getRangeFromInitValue(OPLHS, APValue, Visited); + } + ConstantFP *OPConstFP; + if (match(V, m_FAdd(m_Value(OPLHS), m_ConstantFP(OPConstFP)))) { + auto [Valid, IV] = getValidConstantInteger(OPConstFP); + if (Valid) { + APValue += IV; + return getRangeFromInitValue(OPLHS, APValue, Visited); + } + } + if (match(V, m_FSub(m_Value(OPLHS), m_ConstantFP(OPConstFP)))) { + auto [Valid, IV] = getValidConstantInteger(OPConstFP); + if (Valid) { + APValue -= IV; + return getRangeFromInitValue(OPLHS, APValue, Visited); + } + } + return V; +} + +std::pair +CJSimpleRangeAnalysisPass::checkAndGetConditionBaseDiff(BranchInst *BI, + APInt &Diff) { + if (auto *Cmp = + dyn_cast(BI->getCondition())) { // Now only support cmp. + Value *CmpLHS = Cmp->getOperand(0); + Value *CmpRHS = Cmp->getOperand(1); + // Currently, only deal with (cmp pred %x, constant integer). + auto [Tag, IV] = getValidConstantInteger(CmpRHS); + if (!Tag) + return {nullptr, 0}; + SmallSet Visited; + return {getRangeFromInitValue(CmpLHS, Diff, Visited), IV}; + } + return {nullptr, 0}; +} + +// This function does exactly what its name implies, and in addition +// calculates the difference between the cmp comparator and its base value. +CJSimpleRangeAnalysisPass::ConditionState +CJSimpleRangeAnalysisPass::findMostRecentConditionBr(BasicBlock *BB, + BasicBlock *Succ) { + // multi prodecessor, handle this in the future. + // 1 2 + // \ / \ + // 3 4 + // | + // 5 (curr BB) + // / \ + // 6 7 + while (BB->getSingleSuccessor()) { + // BB is not which we want, just seach above. + auto *Pred = BB->getSinglePredecessor(); + // Must be single, not unique. + if (!Pred) + return {nullptr, APInt(BIT_WIDTH_64, 0), + CmpInst::Predicate::BAD_ICMP_PREDICATE, ConditionResult::UNKNOWN, + 0}; + Succ = BB; + BB = Pred; + } + APInt Diff(64, 0); + auto *BI = dyn_cast(BB->getTerminator()); + if (!BI || !isa(BI->getCondition())) + return {nullptr, Diff, CmpInst::Predicate::BAD_ICMP_PREDICATE, + ConditionResult::UNKNOWN, 0}; + assert(BI->isConditional()); + auto [Base, IV] = checkAndGetConditionBaseDiff(BI, Diff); + ConditionResult CR = BI->getSuccessor(0) == Succ ? ConditionResult::TRUE + : ConditionResult::FALSE; + return {Base, Diff, cast(BI->getCondition())->getPredicate(), CR, + IV}; +} + +// Return Prev CR and Curr CR. +std::pair +CJSimpleRangeAnalysisPass::getCRs(const ConditionState &Prev, + const ConditionState &Curr) { + auto UnsignedLessCheck = [](const ConditionState &CS, ConstantRange &CR) { + if (CS.Pred == CmpInst::ICMP_ULT || CS.Pred == CmpInst::ICMP_ULE || + CS.Pred == CmpInst::FCMP_ULT || CS.Pred == CmpInst::FCMP_ULE) + CR = ConstantRange(APInt(BIT_WIDTH_64, -CS.Diff.getSExtValue()), + CR.getUpper()); + }; + DenseMap FP2Int = { + {static_cast(CmpInst::FCMP_OLE), CmpInst::ICMP_SLE}, + {static_cast(CmpInst::FCMP_OLT), CmpInst::ICMP_SLT}, + {static_cast(CmpInst::FCMP_OGE), CmpInst::ICMP_SGE}, + {static_cast(CmpInst::FCMP_OGT), CmpInst::ICMP_SGT}, + {static_cast(CmpInst::FCMP_OEQ), CmpInst::ICMP_EQ}, + {static_cast(CmpInst::FCMP_ONE), CmpInst::ICMP_NE}}; + int64_t PrevValue = Prev.CI - Prev.Diff.getSExtValue(); + int64_t CurrValue = Curr.CI - Curr.Diff.getSExtValue(); + // The pred types of prev and curr must be the same. For FP, since we are + // guaranteed to be able to cast to an integer, we map FP pred to Integer pred + // processing. + if (Prev.Pred >= CmpInst::FCMP_OEQ && Prev.Pred <= CmpInst::FCMP_ONE && + Curr.Pred >= CmpInst::FCMP_OEQ && Curr.Pred <= CmpInst::FCMP_ONE) { + ConstantRange CurrCR = ConstantRange::makeExactICmpRegion( + FP2Int[Curr.Pred], APInt(64, CurrValue)); + UnsignedLessCheck(Curr, CurrCR); + ConstantRange PrevCR = ConstantRange::getFull(64); + if (BBValueCRs.count(Prev.BB) && BBValueCRs[Prev.BB].count(Prev.BV)) { + PrevCR = Prev.PredCR == ConditionResult::FALSE + ? BBValueCRs[Prev.BB][Prev.BV].FCR + : BBValueCRs[Prev.BB][Prev.BV].TCR; + } else { + PrevCR = ConstantRange::makeExactICmpRegion( + FP2Int[Prev.Pred], APInt(BIT_WIDTH_64, PrevValue)); + UnsignedLessCheck(Prev, PrevCR); + if (Prev.PredCR == ConditionResult::FALSE) + PrevCR = PrevCR.inverse(); + } + return {PrevCR, CurrCR}; + } + if (Prev.Pred >= CmpInst::ICMP_EQ && Prev.Pred <= CmpInst::ICMP_SLE && + Curr.Pred >= CmpInst::ICMP_EQ && Curr.Pred <= CmpInst::ICMP_SLE) { + ConstantRange CurrCR = ConstantRange::makeExactICmpRegion( + Curr.Pred, APInt(BIT_WIDTH_64, CurrValue)); + UnsignedLessCheck(Curr, CurrCR); + ConstantRange PrevCR = ConstantRange::getFull(64); + if (BBValueCRs.count(Prev.BB) && BBValueCRs[Prev.BB].count(Prev.BV)) { + PrevCR = Prev.PredCR == ConditionResult::FALSE + ? BBValueCRs[Prev.BB][Prev.BV].FCR + : BBValueCRs[Prev.BB][Prev.BV].TCR; + } else { + PrevCR = ConstantRange::makeExactICmpRegion( + Prev.Pred, APInt(BIT_WIDTH_64, PrevValue)); + UnsignedLessCheck(Prev, PrevCR); + if (Prev.PredCR == ConditionResult::FALSE) + PrevCR = PrevCR.inverse(); + } + + return {PrevCR, CurrCR}; + } + + return {ConstantRange::getFull(64), ConstantRange::getFull(64)}; +} + +void CJSimpleRangeAnalysisPass::rewriteUses( + BasicBlock *BB, BasicBlock *NewBB, + DenseMap &InstMapping) { + SmallVector RewriteInsts; + for (auto BI = BB->begin(), BE = std::prev(BB->end()); BI != BE; ++BI) { + for (auto &U : BI->uses()) { + auto *User = dyn_cast(U.getUser()); + if (User && User->getParent() != BB) + RewriteInsts.push_back(&U); + } + SSAUpdater SSAUpdate; + SSAUpdate.Initialize(BI->getType(), BI->getName()); + SSAUpdate.AddAvailableValue(BB, &*BI); + SSAUpdate.AddAvailableValue(NewBB, InstMapping[&*BI]); + while (!RewriteInsts.empty()) + SSAUpdate.RewriteUse(*RewriteInsts.pop_back_val()); + } +} + +// The initial state is Pred->BB->Succ, after this function, the state is +// Pred->New->Succ. +void CJSimpleRangeAnalysisPass::createJumpBB(BasicBlock *Pred, BasicBlock *BB, + BasicBlock *Succ) { + BasicBlock *NewBB = BasicBlock::Create( + BB->getContext(), BB->getName() + ".split", BB->getParent(), BB); + DenseMap InstMapping; + auto BI = BB->begin(); + auto BE = std::prev(BB->end()); + // Clone phi node of BB into NewBB. + for (; PHINode *PN = dyn_cast(BI); ++BI) { + auto *NewPN = + PHINode::Create(PN->getType(), 1, PN->getName() + ".split", NewBB); + NewPN->addIncoming(PN->getIncomingValueForBlock(Pred), Pred); + InstMapping[PN] = NewPN; + } + // Clone other instructions. Some instructions may be redundant, which will be + // removed in later pass. + for (; BI != BE; ++BI) { // The last instruction is terminator. + Instruction *NewI = BI->clone(); + NewBB->getInstList().push_back(NewI); + InstMapping[&*BI] = NewI; + // Update operand of NewI. + for (unsigned I = 0, E = BI->getNumOperands(); I != E; ++I) { + auto *Inst = dyn_cast(BI->getOperand(I)); + if (Inst == nullptr) { + continue; + } + auto Iter = InstMapping.find(Inst); + if (Iter != InstMapping.end()) + NewI->setOperand(I, Iter->second); + } + } + // Create terminator instruction of NewBB, and add incoming value of phi in + // Succ. + BranchInst::Create(Succ, NewBB); + for (auto &PN : Succ->phis()) { + Value *V = PN.getIncomingValueForBlock(BB); + if (auto *I = dyn_cast(V)) { + auto Iter = InstMapping.find(I); + if (Iter != InstMapping.end()) + V = Iter->second; + } + PN.addIncoming(V, NewBB); + } + // Last, update terminator of Pred, jump to NewBB instead of BB. This must be + // done at the end, because doing it early will cause the phi information in + // the BB to be lost. + auto *TI = Pred->getTerminator(); + for (unsigned I = 0, E = TI->getNumSuccessors(); I != E; ++I) { + if (TI->getSuccessor(I) == BB) { + BB->removePredecessor(Pred, true); // Must be true, rewrite need it. + TI->setSuccessor(I, NewBB); + } + } + // Rewrite uses of instructions in BB outside. + rewriteUses(BB, NewBB, InstMapping); + // Do a quick simplify, delete dead instruction. + SimplifyInstructionsInBlock(NewBB); +} + +// return +// - 0: condition of curr is always false +// - 1: condition of curr is always true +// - 2: condition of curr is unknown +CJSimpleRangeAnalysisPass::ConditionResult +CJSimpleRangeAnalysisPass::getConditionResult(const ConditionState &Prev, + const ConditionState &Curr) { + // Optimization is possible only when the base of the compared value is the + // same. + if (Prev.BV != Curr.BV) + return ConditionResult::UNKNOWN; + auto [PrevCR, CurrCR] = getCRs(Prev, Curr); + assert(Prev.PredCR != ConditionResult::UNKNOWN); + if (PrevCR.isFullSet() || CurrCR.isFullSet()) + return ConditionResult::UNKNOWN; + if (CurrCR.contains(PrevCR)) + return ConditionResult::TRUE; + if (CurrCR.inverse().contains(PrevCR)) + return ConditionResult::FALSE; + return ConditionResult::UNKNOWN; +} + +// For a BB with only one precursor, record the value range info of the BB +// exit. The BB with multiple predecessors and multiple successors is not +// processed because there are too many combinations. +void CJSimpleRangeAnalysisPass::updateBBValueCRs( + BasicBlock *BB, MapVector &PredInfo, + ConditionState &CurrCS) { + auto *Single = BB->getSinglePredecessor(); + if (!Single) + return; + for (auto &[PredBB, CS] : PredInfo) { + if (!CS.BV || CurrCS.BV != CS.BV || PredBB != Single) + continue; + auto [PredCR, CurrCR] = getCRs(CS, CurrCS); + if (PredCR.isFullSet() || CurrCR.isFullSet()) + return; + BBValueCRs[BB][CurrCS.BV].TCR = CurrCR.intersectWith(PredCR); + BBValueCRs[BB][CurrCS.BV].FCR = CurrCR.inverse().intersectWith(PredCR); + } + return; +} + +bool CJSimpleRangeAnalysisPass::processBasicBlock(BasicBlock &BB) { + auto *BI = dyn_cast(BB.getTerminator()); + if (!BI || BI->isUnconditional()) + return false; + // Loop header can not be jumped. + if (auto *Loop = LI->getLoopFor(&BB)) + if (Loop->getHeader() == &BB) + return false; + APInt Diff(64, 0); + // Get base value and difference of cmp lhs, and constant integer of cmp rhs. + auto [BV, IV] = checkAndGetConditionBaseDiff(BI, Diff); + if (!BV) + return false; + assert(isa(BI->getCondition()) && + "Base is valid, and condition must be Cmp"); + auto *Cmp = cast(BI->getCondition()); + CmpInst::Predicate Pred = Cmp->getPredicate(); + // Traverse each edge that reaches BB through conditional br and check + // whether the condition of BB is true for each edge. + // bb0 (we find this basic block) + // / \ + // bb1 bb2 (maybe create an edge from bb2 to bb5) + // \ / + // bb3 (BB) + // / \ + // bb4 bb5 + MapVector PredInfo; + for (auto *PredBB : predecessors(&BB)) { + auto *L = LI->getLoopFor(PredBB); + if (L && L->isLoopLatch(PredBB)) // Latch may refresh all values in Loop. + continue; + auto TP = findMostRecentConditionBr(PredBB, &BB); + TP.BB = PredBB; + PredInfo[PredBB] = TP; + } + bool Changed = false; + ConditionState CurrCS = {BV, Diff, Pred, ConditionResult::UNKNOWN, IV, &BB}; + for (auto &[PredBB, CS] : PredInfo) { + if (!CS.BV) + continue; + ConditionResult CR = getConditionResult(CS, CurrCS); + if (CR == ConditionResult::UNKNOWN) + continue; + createJumpBB(PredBB, &BB, BI->getSuccessor(static_cast(CR) == 0)); + Changed = true; + } + updateBBValueCRs(&BB, PredInfo, CurrCS); + return Changed; +} + +// %0 = fadd double %n, -2.000000e+00 +// %1 = fadd double %0, -2.000000e+00 +// => %1 = fadd double %n, -4.000000e+00 +bool CJSimpleRangeAnalysisPass::simplifyBinaryInst(Function &F) { + bool Changed = false; + SmallMapVector, 8> DeadInsts; + // 0: nsw, 1: nuw + SmallMapVector, 8> NswNuwInfo; + for (auto &I : instructions(F)) { + if (auto *BO = dyn_cast(&I)) { + APInt Diff(64, 0); + SmallSet Visited; + Value *V = getRangeFromInitValue(&I, Diff, Visited); + if (V && V != &I) { + DeadInsts[&I] = {V, Diff.getSExtValue()}; + if (BO->getType()->isIntegerTy()) + NswNuwInfo[&I] = {BO->hasNoSignedWrap(), BO->hasNoUnsignedWrap()}; + } + } + } + for (auto [I, Info] : DeadInsts) { + auto OP = I->getOpcode() == Instruction::FAdd || + I->getOpcode() == Instruction::FSub + ? Instruction::FAdd + : Instruction::Add; + auto *CV = I->getOpcode() == Instruction::FAdd || + I->getOpcode() == Instruction::FSub + ? ConstantFP::get(Info.first->getType(), + static_cast(Info.second)) + : ConstantInt::get(Info.first->getType(), Info.second); + auto *New = BinaryOperator::Create(OP, Info.first, CV, "", I); + if (New->getType()->isIntegerTy()) { + New->setHasNoSignedWrap(NswNuwInfo[I].first); + if (Info.second >= 0) // add nuw %x, -1 -> -1 by instcombine. + New->setHasNoUnsignedWrap(NswNuwInfo[I].second); + } + I->replaceAllUsesWith(New); + I->eraseFromParent(); + Changed = true; + } + return Changed; +} + +// Dead blocks may cause pass to enter an infinite loop in +// getRangeFromInitValue, must be removed before analysis. For example, a dead +// loop contains the instruction %n = add i64%n, 1. +static bool removeDeadBlocks(Function &F, DominatorTree *DT) { + DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Lazy); + return removeUnreachableBlocks(F, DT ? &DTU : nullptr); +} + +bool CJSimpleRangeAnalysisPass::runImpl(Function &F, LoopInfo &LI, + DominatorTree &DT) { + if (!EnableCJSimpleRA) + return false; + this->LI = &LI; + bool Changed; + bool EverChanged = false; + EverChanged |= removeDeadBlocks(F, &DT); + EverChanged |= simplifyBinaryInst(F); + do { + Changed = false; + for (auto &BB : F) // Simplify RangeAnalysis + Changed |= processBasicBlock(BB); + EverChanged |= Changed; + } while (Changed); + BBValueCRs.clear(); // Must clear it. + return EverChanged; +} + +PreservedAnalyses CJSimpleRangeAnalysisPass::run(Function &F, + FunctionAnalysisManager &AM) { + auto &LI = AM.getResult(F); + auto &DT = AM.getResult(F); + bool Changed = runImpl(F, LI, DT); + if (Changed) + return PreservedAnalyses::none(); + return PreservedAnalyses::all(); +} + +namespace { +class CJSimpleRangeAnalysis : public FunctionPass { + CJSimpleRangeAnalysisPass Impl; + +public: + static char ID; + explicit CJSimpleRangeAnalysis() : FunctionPass(ID) { + initializeCJSimpleRangeAnalysisPass(*PassRegistry::getPassRegistry()); + } + ~CJSimpleRangeAnalysis() = default; + + bool runOnFunction(Function &F) override { + auto &LI = this->getAnalysis(F).getLoopInfo(); + auto &DT = this->getAnalysis(F).getDomTree(); + return Impl.runImpl(F, LI, DT); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addRequired(); + } +}; +} // namespace + +char CJSimpleRangeAnalysis::ID = 0; + +FunctionPass *llvm::createCJSimpleRangeAnalysis() { + return new CJSimpleRangeAnalysis(); +} + +INITIALIZE_PASS(CJSimpleRangeAnalysis, "cj-simple-range-analysis", + "Cangjie simple range analysis.", false, false) diff --git a/llvm/lib/Transforms/Scalar/CJSpecificOpt.cpp b/llvm/lib/Transforms/Scalar/CJSpecificOpt.cpp new file mode 100644 index 000000000..c6a7e4253 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/CJSpecificOpt.cpp @@ -0,0 +1,446 @@ +//===- CJSpecificOpt.cpp ----------------------------------------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// This pass mainly implements the specified optimization of cangjie. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/CJSpecificOpt.h" + +#include "llvm/ADT/Triple.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/SafepointIRVerifier.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" + +using namespace llvm; + +static cl::opt + DisableDeadAllocaElimination("disable-dead-alloca-elimination", cl::Hidden, + cl::init(false)); + +namespace llvm { +extern cl::opt CangjieJIT; +extern cl::opt CangjieStackCheckSize; +} // namespace llvm + +static bool hasLoopInCangjieFunc(Function &F) { + DominatorTree DT; + DT.recalculate(F); + LoopInfoBase loopInfo; + loopInfo.releaseMemory(); + loopInfo.analyze(DT); + return !loopInfo.empty(); +} + +static bool markfastCall(Function &F) { + // The function cannot be fastcall if it contains a loop. + if (hasLoopInCangjieFunc(F)) { + return false; + } + for (Instruction &I : instructions(F)) { + const auto *CI = dyn_cast(&I); + if (!CI) + continue; + + Function *Callee = CI->getCalledFunction(); + if (!Callee) // Has indirect call + return false; + + if (Callee->isCangjieIntrinsic() || Callee->hasFnAttribute("cj-runtime") || + Callee->hasFnAttribute("gc-leaf-function")) { + continue; + } else { + return false; + } + } + // mark cj_fast_call attribute + F.addFnAttr(Attribute::get(F.getContext(), "cj_fast_call")); + return true; +} + +static bool deadAllocaElimination(Function &F) { + bool Changed = false; + if (DisableDeadAllocaElimination) + return Changed; + + SetVector WorkList; + for (Instruction &I : F.getEntryBlock()) { + auto AI = dyn_cast(&I); + if (!AI || AI->getNumUses() > 1) + continue; + + if (AI->use_empty()) { + WorkList.insert(AI); + continue; + } + + auto *Cast = dyn_cast(AI->use_begin()->getUser()); + if (!Cast || Cast->getNumUses() > 1) + continue; + + if (Cast->use_empty()) { + WorkList.insert(Cast); + continue; + } + + // If the use of alloca is only memset, eliminate it. + auto *II = dyn_cast(Cast->use_begin()->getUser()); + if (II && II->getIntrinsicID() == Intrinsic::cj_memset) { + WorkList.insert(II); + WorkList.insert(Cast); + WorkList.insert(AI); + } + } + + if (WorkList.size()) { + for (auto I : WorkList) { + I->eraseFromParent(); + } + Changed = true; + } + + return Changed; +} + +static void lowerCJMemset(Function &F, CallInst *CI) { + FunctionType *FT = CI->getFunctionType(); + Type *ParamTypes[] = { + FT->getParamType(0), // dst + FT->getParamType(2) // len + }; + Function *NewFn = + Intrinsic::getDeclaration(F.getParent(), Intrinsic::memset, ParamTypes); + CI->setCalledFunction(NewFn); +} + +static void lowerCJPow(Function &F, CallInst *CI) { + // get pow funcType + const auto *FT = CI->getCalledFunction()->getFunctionType(); + Type *ParamTypes[2] = { + FT->getParamType(0), // base + FT->getParamType(1) // exponent + }; + + /** + * only support 3 types + * double pow(double, double) + * float pow(float, float) + * float pow(float, int32) + */ + assert(((ParamTypes[0]->isDoubleTy() && ParamTypes[1]->isDoubleTy()) || + (ParamTypes[0]->isFloatTy() && + (ParamTypes[1]->isFloatTy() || + ParamTypes[1]->isIntegerTy(32)))) && // 32BitWidth + "Unsupported Pow FuncParamType"); + + std::string CJPowFuncStr; + if (ParamTypes[1]->isDoubleTy()) { + CJPowFuncStr = "CJ_CORE_CPow"; + } else if (ParamTypes[1]->isFloatTy()) { + CJPowFuncStr = "CJ_CORE_CPowf"; + } else if (ParamTypes[1]->isIntegerTy(32)) { // 32BitWidth + CJPowFuncStr = "CJ_CORE_FastPowerFloatInt32"; + } else { + return; + } + + // insert new CJ pow function + auto *CJPowFT = FunctionType::get(ParamTypes[0], ParamTypes, false); + Function *CJPowFunc = cast( + F.getParent()->getOrInsertFunction(CJPowFuncStr, CJPowFT).getCallee()); + CJPowFunc->addFnAttr(Attribute::get(F.getContext(), "gc-leaf-function")); + CI->setCalledFunction(CJPowFunc); +} + +static void replaceAddrpaceCastUsed(AddrSpaceCastInst *ACI, IntrinsicInst *II) { + Value *Ptr = ACI->getPointerOperand(); + IRBuilder<> Builder(II); + switch (II->getIntrinsicID()) { + default: + assert(false && "Unsupported intrinsic type"); + break; + case Intrinsic::memcpy: { + MemCpyInst *MemCpy = cast(II); + if (ACI == MemCpy->getRawSource()) { + Builder.CreateMemCpy(MemCpy->getRawDest(), MemCpy->getDestAlign(), Ptr, + MemCpy->getSourceAlign(), MemCpy->getLength(), + MemCpy->isVolatile()); + } else { + Builder.CreateMemCpy(Ptr, MemCpy->getDestAlign(), MemCpy->getRawSource(), + MemCpy->getSourceAlign(), MemCpy->getLength(), + MemCpy->isVolatile()); + } + break; + } + case Intrinsic::memmove: { + MemMoveInst *MemMove = cast(II); + if (ACI == MemMove->getRawSource()) { + Builder.CreateMemMove(MemMove->getRawDest(), MemMove->getDestAlign(), Ptr, + MemMove->getSourceAlign(), MemMove->getLength(), + MemMove->isVolatile()); + } else { + Builder.CreateMemMove(Ptr, MemMove->getDestAlign(), + MemMove->getRawSource(), MemMove->getSourceAlign(), + MemMove->getLength(), MemMove->isVolatile()); + } + break; + } + case Intrinsic::memset: { + MemSetInst *MemSet = cast(II); + Builder.CreateMemSet(Ptr, MemSet->getValue(), MemSet->getLength(), + MaybeAlign(MemSet->getDestAlignment()), + MemSet->isVolatile()); + break; + } + } + + II->eraseFromParent(); + return; +} + +// IR after inline: +// \code +// %x = bitcast %record* %r to i8* +// %y = addrspacecast i8* %x to i8 addrspace(1)* +// call @memcpy.p0i8.p0i8.i64(i8* %x, i8* %src, i64 16, i1 false) +// call @memcpy.p0i8.p1i8.i64(i8* %z, i8 addrspace(1)* %y, i64 16, i1 false) +// \ +// transforms into: +// \code +// %x = bitcast %record* %r to i8* +// %y = addrspacecast i8* %x to i8 addrspace(1)* +// call @memcpy.p0i8.p0i8.i64(i8* %x, i8* %src, i64 16, i1 false) +// call @memcpy.p0i8.p0i8.i64(i8* %z, i8* %x, i64 16, i1 false) +// \ +// Remove the `addrspacecast inst`, if addrspacecast is not used elsewhere +// which is helpful for computeLiveInValues. +// The addrspacecast may also be used for memset and memmove. +static bool replaceInvalidAddrpaceCast(Function &F) { + bool Changed = false; + SmallVector WorkList; + for (auto &I : instructions(F)) { + if (I.getMetadata(LLVMContext::MD_tbaa)) { + I.setMetadata(LLVMContext::MD_tbaa, nullptr); + } + if (I.getMetadata(LLVMContext::MD_tbaa_struct)) { + I.setMetadata(LLVMContext::MD_tbaa_struct, nullptr); + } + auto *ACI = dyn_cast(&I); + if (!ACI) { + continue; + } + auto *PT = dyn_cast(ACI->getPointerOperand()->getType()); + if (PT->getAddressSpace() != 0 || !PT->getElementType()->isIntegerTy(8)) { + continue; + } + WorkList.push_back(ACI); + } + + for (Instruction *I : WorkList) { + auto *ACI = cast(I); + for (auto UI = ACI->use_begin(), UE = ACI->use_end(); UI != UE;) { + auto *II = dyn_cast((UI++)->getUser()); + if (!II) + continue; + + // Replace memory inst operand. + if (II->getIntrinsicID() == Intrinsic::memcpy || + II->getIntrinsicID() == Intrinsic::memset || + II->getIntrinsicID() == Intrinsic::memmove) { + replaceAddrpaceCastUsed(ACI, II); + Changed = true; + } + } + + if (ACI->user_empty()) { + ACI->eraseFromParent(); + Changed = true; + } + } + + return Changed; +} + +static bool insertStackCheck(Function &F) { + if (CangjieJIT || F.hasFnAttribute("gc-leaf-function")) + return false; + + uint64_t ApproximateSize = 0; + bool HasCJCall = false; + const DataLayout &DL = F.getParent()->getDataLayout(); + for (auto &I : F.getEntryBlock()) { + if (auto *AI = dyn_cast(&I)) { + // The stack ptr type must be 8-byte aligned. + if (AI->getAlign().value() % 8) { + AI->setAlignment(Align(8)); + } + // 8-byte aligned size. + ApproximateSize += + (DL.getTypeSizeInBits(AI->getAllocatedType()) / 8 + 7) & ~7; + } + } + for (auto &I : instructions(F)) { + auto *CB = dyn_cast(&I); + if (!CB || isa(CB)) + continue; + if (!CB->hasFnAttr("cj-runtime")) { + HasCJCall = true; + break; + } + } + if (!HasCJCall && ApproximateSize < CangjieStackCheckSize) + return false; + + Instruction *InsertBefore = &F.getEntryBlock().front(); + const char *StackCheckStr = "CJ_MCC_StackCheck"; + Module *M = InsertBefore->getModule(); + Function *Func = M->getFunction(StackCheckStr); + if (Func == nullptr) { + FunctionType *FuncType = + FunctionType::get(Type::getVoidTy(M->getContext()), false); + Func = cast( + M->getOrInsertFunction(StackCheckStr, FuncType).getCallee()); + Func->addFnAttr("cj-stack-check"); + Func->setUnnamedAddr(GlobalValue::UnnamedAddr::Local); + } + auto *CI = CallInst::Create(Func->getFunctionType(), Func, "", InsertBefore); + CI->setCallingConv(CallingConv::CangjieGC); + return true; +} + +static bool insertResetFPState(Function &F, unsigned OptLevel) { + if (!Triple(F.getParent()->getTargetTriple()).isAArch64() || + OptLevel < 2) { // 2: O2 + return false; + } + bool Changed = false; + + for (BasicBlock &BB : F) { + if (BB.getName().startswith("fp.convert.exception")) { + IRBuilder<> Builder(BB.getFirstNonPHI()); + // insert call void @llvm.cj.reset.fp.state() + Function *ResetFpStateFunc = Intrinsic::getDeclaration( + F.getParent(), Intrinsic::cj_reset_fp_state); + Builder.CreateCall(ResetFpStateFunc, {}); + + Changed = true; + } + } + return Changed; +} + +static bool runOnFunction(Function &F, unsigned OptLevel) { + bool Changed = false; + if (F.isDeclaration() || F.empty() || !F.hasCangjieGC() || F.hasOptNone()) { + return Changed; + } + + for (auto I = inst_begin(F); I != inst_end(F);) { + IntrinsicInst *CI = dyn_cast(&*I++); + if (!CI || CI->getCalledFunction() == nullptr) + continue; + + switch (CI->getIntrinsicID()) { + default: + break; + case Intrinsic::cj_memset: + lowerCJMemset(F, CI); + Changed = true; + break; + case Intrinsic::pow: + case Intrinsic::powi: + lowerCJPow(F, CI); + Changed = true; + break; + } + } + if (CangjieJIT) + return Changed; + + Changed |= markfastCall(F); + Changed |= deadAllocaElimination(F); + Changed |= replaceInvalidAddrpaceCast(F); + const Triple TT(F.getParent()->getTargetTriple()); + if (!TT.isARM()) + Changed |= insertStackCheck(F); + Changed |= insertResetFPState(F, OptLevel); + return Changed; +} + +static bool processCangjieIR(Module &M, unsigned OptLevel) { + bool Changed = false; + for (Function &F : M) { + Changed |= runOnFunction(F, OptLevel); + } + + // llvm.used is not needed after opt, clear it. Otherwise it would cause + // an error when the linker tried to preserve the symbol due to + // the `/include:` directive. + GlobalVariable *UsedGV = M.getNamedGlobal("llvm.used"); + if (UsedGV) { + UsedGV->eraseFromParent(); + Changed = true; + } + // "0_for_keeping_some_types" is not needed after opt, clear it. + Function *KeepingTypeFunc = M.getFunction("0_for_keeping_some_types"); + if (KeepingTypeFunc) { + auto *FunPtrTy = cast(KeepingTypeFunc->getType()); + KeepingTypeFunc->replaceAllUsesWith(ConstantPointerNull::get(FunPtrTy)); + KeepingTypeFunc->eraseFromParent(); + Changed = true; + } + return Changed; +} + +PreservedAnalyses CJSpecificOpt::run(Module &M, ModuleAnalysisManager &) const { + if (processCangjieIR(M, OptLevel)) { + return PreservedAnalyses::none(); + } + return PreservedAnalyses::all(); +} + +namespace { +class CangjieSpecificOptLegacyPass : public ModulePass { +public: + static char ID; + unsigned OptLevel; + + explicit CangjieSpecificOptLegacyPass(unsigned OptLevel = 0) + : ModulePass(ID), OptLevel(OptLevel) { + initializeCangjieSpecificOptLegacyPassPass( + *PassRegistry::getPassRegistry()); + } + ~CangjieSpecificOptLegacyPass() = default; + + bool runOnModule(Module &M) override { return processCangjieIR(M, OptLevel); } +}; +} // namespace + +char CangjieSpecificOptLegacyPass::ID = 0; + +ModulePass *llvm::createCangjieSpecificOptLegacyPass(unsigned OptLevel) { + return new CangjieSpecificOptLegacyPass(OptLevel); +} + +INITIALIZE_PASS(CangjieSpecificOptLegacyPass, "cj-specific-opt", + "Cangjie Specific Optimize", false, false) diff --git a/llvm/lib/Transforms/Scalar/CMakeLists.txt b/llvm/lib/Transforms/Scalar/CMakeLists.txt index eb008c159..21018612e 100644 --- a/llvm/lib/Transforms/Scalar/CMakeLists.txt +++ b/llvm/lib/Transforms/Scalar/CMakeLists.txt @@ -3,6 +3,10 @@ add_llvm_component_library(LLVMScalarOpts AlignmentFromAssumptions.cpp AnnotationRemarks.cpp BDCE.cpp + CJIRVerifier.cpp + CJBarrierSplit.cpp + CJDevirtualOpt.cpp + CJRuntimeLowering.cpp CallSiteSplitting.cpp ConstantHoisting.cpp ConstraintElimination.cpp @@ -12,6 +16,16 @@ add_llvm_component_library(LLVMScalarOpts DFAJumpThreading.cpp DivRemPairs.cpp EarlyCSE.cpp + CJBarrierOpt.cpp + CJSimpleOpt.cpp + CJGenericIntrinsicOpt.cpp + CJSimpleRangeAnalysis.cpp + CJRSSCE.cpp + CJLoopFloatOpt.cpp + CJSpecificOpt.cpp + CJRewriteStatepoint.cpp + CJFillMetadata.cpp + CJGCLiveAnalysis.cpp FlattenCFGPass.cpp Float2Int.cpp GuardWidening.cpp @@ -22,6 +36,7 @@ add_llvm_component_library(LLVMScalarOpts InductiveRangeCheckElimination.cpp IndVarSimplify.cpp InferAddressSpaces.cpp + InsertCJTBAA.cpp InstSimplifyPass.cpp JumpThreading.cpp LICM.cpp @@ -96,4 +111,5 @@ add_llvm_component_library(LLVMScalarOpts InstCombine Support TransformUtils + Instrumentation ) diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp index 3f0dad7ee..c7b52d4ef 100644 --- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp +++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp @@ -64,9 +64,11 @@ #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/IR/Module.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/PatternMatch.h" +#include "llvm/IR/SafepointIRVerifier.h" #include "llvm/IR/Value.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" @@ -90,6 +92,10 @@ using namespace llvm; using namespace PatternMatch; +namespace llvm { +extern cl::opt CJPipeline; +} // namespace llvm + #define DEBUG_TYPE "dse" STATISTIC(NumRemainingStores, "Number of stores remaining after DSE"); @@ -821,6 +827,17 @@ struct DSEState { }); CodeMetrics::collectEphemeralValues(&F, &AC, EphValues); + + // Set the WillReturn attribute of cj.memset, so that the DSE can eliminate + // it. + if (Function *CJMemset = F.getParent()->getFunction("llvm.cj.memset.p0i8")) + CJMemset->addFnAttr(llvm::Attribute::WillReturn); + } + + ~DSEState() { + // Currently, we do not want other Passes to eliminate cj.memset. + if (Function *CJMemset = F.getParent()->getFunction("llvm.cj.memset.p0i8")) + CJMemset->removeFnAttr(llvm::Attribute::WillReturn); } /// Return 'OW_Complete' if a store to the 'KillingLoc' location (by \p @@ -994,6 +1011,19 @@ struct DSEState { if (!I->mayWriteToMemory()) return None; + // Currently, it is only safe to delete gcwrite in unreachable. + if (CJPipeline && isa(I)) { + auto *II = cast(I); + auto ID = II->getIntrinsicID(); + if (ID == Intrinsic::cj_gcwrite_ref) + return MemoryLocation::get(II); + if (ID == Intrinsic::cj_gcread_generic || + ID == Intrinsic::cj_assign_generic) + return MemoryLocation::getAfter(II->getArgOperand(0)); + if (ID == Intrinsic::cj_gcwrite_generic) + return MemoryLocation::getAfter(II->getArgOperand(1)); + } + if (auto *CB = dyn_cast(I)) return MemoryLocation::getForDest(CB, TLI); @@ -1234,6 +1264,7 @@ struct DSEState { bool CanOptimize = OptimizeMemorySSA && KillingDef->getDefiningAccess() == StartAccess && !KillingI->mayReadFromMemory(); + bool FindSafePointCall = false; // Find the next clobbering Mod access for DefLoc, starting at StartAccess. Optional CurrentLoc; @@ -1278,6 +1309,20 @@ struct DSEState { MemoryDef *CurrentDef = cast(Current); Instruction *CurrentI = CurrentDef->getMemoryInst(); + // Check if we find a safepoint call + if (auto *CB = dyn_cast(CurrentI)) { + if (isSafepointCall(CB)) + FindSafePointCall = true; + } + + // If CurrentI is cj_memset and we find a safepoint between KillingI and + // CurrentI, then KillingI can not kill CurrentI. + if (FindSafePointCall && isa(CurrentI)) { + auto *II = cast(CurrentI); + if (II->getIntrinsicID() == Intrinsic::cj_memset) + continue; + } + if (canSkipDef(CurrentDef, !isInvisibleToCallerOnUnwind(KillingUndObj))) { CanOptimize = false; continue; @@ -1702,7 +1747,8 @@ struct DSEState { const Value *UO = getUnderlyingObject(DefLoc->Ptr); if (!isInvisibleToCallerAfterRet(UO)) continue; - + if (CJPipeline && maybeCJFinalizerObj(const_cast(UO))) + continue; if (isWriteAtEndOfFunction(Def)) { // See through pointer-to-pointer bitcasts LLVM_DEBUG(dbgs() << " ... MemoryDef is not accessed until the end " @@ -2191,7 +2237,6 @@ public: for (auto &I : instructions(F)) NumRemainingStores += isa(&I); #endif - return Changed; } diff --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp index cf2824954..8fa54674a 100644 --- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp +++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp @@ -28,6 +28,7 @@ #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/BasicBlock.h" +#include "llvm/IR/CJIntrinsics.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" @@ -35,6 +36,7 @@ #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/PatternMatch.h" @@ -82,6 +84,10 @@ static cl::opt EarlyCSEDebugHash( cl::desc("Perform extra assertion checking to verify that SimpleValue's hash " "function is well-behaved w.r.t. its isEqual predicate")); +namespace llvm { +extern cl::opt CJPipeline; +} // namespace llvm + //===----------------------------------------------------------------------===// // SimpleValue //===----------------------------------------------------------------------===// @@ -701,6 +707,38 @@ private: Info.IsVolatile = false; break; } + + if (CJPipeline) { + switch (IntrID) { + case Intrinsic::cj_gcwrite_ref: + Info.PtrVal = cangjie::getPointerArg(cast(Inst)); + Info.MatchingId = Intrinsic::cj_gcread_ref; + Info.ReadMem = false; + Info.WriteMem = true; + Info.IsVolatile = false; + break; + case Intrinsic::cj_gcwrite_static_ref: + Info.PtrVal = cangjie::getPointerArg(cast(Inst)); + Info.MatchingId = Intrinsic::cj_gcread_static_ref; + Info.ReadMem = false; + Info.WriteMem = true; + Info.IsVolatile = false; + break; + case Intrinsic::cj_gcread_ref: + Info.PtrVal = cangjie::getPointerArg(cast(Inst)); + Info.MatchingId = Intrinsic::cj_gcread_ref; + Info.ReadMem = true; + Info.WriteMem = false; + Info.IsVolatile = false; + break; + case Intrinsic::cj_gcread_static_ref: + Info.PtrVal = cangjie::getPointerArg(cast(Inst)); + Info.MatchingId = Intrinsic::cj_gcread_static_ref; + Info.ReadMem = true; + Info.WriteMem = false; + Info.IsVolatile = false; + } + } } } } @@ -815,6 +853,10 @@ private: switch (ID) { case Intrinsic::masked_load: case Intrinsic::masked_store: + case Intrinsic::cj_gcwrite_ref: + case Intrinsic::cj_gcwrite_struct: + case Intrinsic::cj_gcread_ref: + case Intrinsic::cj_gcread_static_ref: return true; } return false; @@ -855,8 +897,12 @@ private: Type *ExpectedType) const { switch (II->getIntrinsicID()) { case Intrinsic::masked_load: + case Intrinsic::cj_gcread_ref: + case Intrinsic::cj_gcread_static_ref: return II; case Intrinsic::masked_store: + case Intrinsic::cj_gcwrite_ref: + case Intrinsic::cj_gcwrite_static_ref: return II->getOperand(0); } return nullptr; @@ -905,6 +951,11 @@ private: return II->getOperand(0); if (II->getIntrinsicID() == Intrinsic::masked_store) return II->getOperand(1); + if (II->getIntrinsicID() == Intrinsic::cj_gcread_ref || + II->getIntrinsicID() == Intrinsic::cj_gcread_static_ref || + II->getIntrinsicID() == Intrinsic::cj_gcwrite_ref || + II->getIntrinsicID() == Intrinsic::cj_gcwrite_static_ref) + return cangjie::getPointerArg(const_cast(II)); llvm_unreachable("Unexpected IntrinsicInst"); }; auto MaskOp = [](const IntrinsicInst *II) { @@ -961,6 +1012,14 @@ private: // mask. return IsSubmask(MaskOp(Earlier), MaskOp(Later)); } + if ((IDE == Intrinsic::cj_gcread_ref || IDE == Intrinsic::cj_gcwrite_ref) && + (IDL == Intrinsic::cj_gcread_ref || IDL == Intrinsic::cj_gcwrite_ref)) + return true; + if ((IDE == Intrinsic::cj_gcread_static_ref || + IDE == Intrinsic::cj_gcwrite_static_ref) && + (IDL == Intrinsic::cj_gcread_static_ref || + IDL == Intrinsic::cj_gcwrite_static_ref)) + return true; return false; } @@ -977,6 +1036,21 @@ private: // by MemorySSA's getClobberingMemoryAccess. MSSAUpdater->removeMemoryAccess(&Inst, true); } + + void removeReadNoneMSSA(Instruction &Inst) { + if (!MSSA) + return; + if (VerifyMemorySSA) + MSSA->verifyMemorySSA(); + + for (User *U : Inst.users()) { + if (auto *CB = dyn_cast(U)) { + // Removing a callsite that may not read from or write memory. + // This is necessary for correctness, see the createNewAccess method. + MSSAUpdater->removeMemoryAccess(CB, true); + } + } + } }; } // end anonymous namespace @@ -1119,8 +1193,10 @@ Value *EarlyCSE::getMatchingValue(LoadValue &InVal, ParseMemoryInst &MemInst, unsigned CurrentGeneration) { if (InVal.DefInst == nullptr) return nullptr; + if (InVal.MatchingId != MemInst.getMatchingId()) return nullptr; + // We don't yet handle removing loads with ordering of any kind. if (MemInst.isVolatile() || !MemInst.isUnordered()) return nullptr; @@ -1146,8 +1222,10 @@ Value *EarlyCSE::getMatchingValue(LoadValue &InVal, ParseMemoryInst &MemInst, // Deal with non-target memory intrinsics. bool MatchingNTI = isHandledNonTargetIntrinsic(Matching); bool OtherNTI = isHandledNonTargetIntrinsic(Other); + if (OtherNTI != MatchingNTI) return nullptr; + // In cangjie, it doesn't go to this branch. if (OtherNTI && MatchingNTI) { if (!isNonTargetIntrinsicMatch(cast(InVal.DefInst), cast(MemInst.get()))) @@ -1355,6 +1433,10 @@ bool EarlyCSE::processNode(DomTreeNode *Node) { } else { bool Killed = false; if (!Inst.use_empty()) { + Function *Callee = dyn_cast(V); + if (Callee && Callee->hasFnAttribute(Attribute::ReadNone)) { + removeReadNoneMSSA(Inst); + } Inst.replaceAllUsesWith(V); Changed = true; } diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp index b460637b7..4280ca3b7 100644 --- a/llvm/lib/Transforms/Scalar/GVN.cpp +++ b/llvm/lib/Transforms/Scalar/GVN.cpp @@ -49,15 +49,18 @@ #include "llvm/IR/DebugLoc.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" +#include "llvm/IR/GlobalValue.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/PatternMatch.h" +#include "llvm/IR/SafepointIRVerifier.h" #include "llvm/IR/Type.h" #include "llvm/IR/Use.h" #include "llvm/IR/Value.h" @@ -101,6 +104,11 @@ STATISTIC(MaxBBSpeculationCutoffReachedTimes, "Number of times we we reached gvn-max-block-speculations cut-off " "preventing further exploration"); +namespace llvm { +extern cl::opt GVNDisableBarrier; +extern cl::opt CJPipeline; +} // namespace llvm + static cl::opt GVNEnablePRE("enable-pre", cl::init(true), cl::Hidden); static cl::opt GVNEnableLoadPRE("enable-load-pre", cl::init(true)); static cl::opt GVNEnableLoadInLoopPRE("enable-load-in-loop-pre", @@ -1213,6 +1221,19 @@ bool GVNPass::AnalyzeLoadAvailability(LoadInst *Load, MemDepResult DepInfo, } } + // if the clobbering value is a gcwrite that writes to a superset of load + // value, see if we can extract the value from gcwrite value. + if (IntrinsicInst *DepII = dyn_cast(DepInst)) { + if (GVNDisableBarrier || DepII->getIntrinsicID() != Intrinsic::cj_gcwrite_ref) + return false; + int Offset = + anaLyzeLoadFromClobberingGCWrite(Load->getType(), Address, DepII, DL); + if (Offset != -1) { + Res = AvailableValue::get(DepII->getOperand(0), Offset); + return true; + } + } + // Nothing known about this clobber, have to be conservative LLVM_DEBUG( // fast print dep, using operator<< on instruction is too slow. @@ -1269,6 +1290,16 @@ bool GVNPass::AnalyzeLoadAvailability(LoadInst *Load, MemDepResult DepInfo, return true; } + if (IntrinsicInst *II = dyn_cast(DepInst); + II && II->getIntrinsicID() == Intrinsic::cj_gcwrite_ref && + !GVNDisableBarrier) { + if (!canCoerceMustAliasedValueToLoad(II->getOperand(0), Load->getType(), + DL)) + return false; + Res = AvailableValue::get(II->getOperand(0)); + return true; + } + // Unknown def - must be conservative LLVM_DEBUG( // fast print dep, using operator<< on instruction is too slow. @@ -2023,6 +2054,10 @@ static void patchAndReplaceAllUsesWith(Instruction *I, Value *Repl) { /// Attempt to eliminate a load, first by eliminating it /// locally, and then attempting non-local elimination if that fails. bool GVNPass::processLoad(LoadInst *L) { + if (CJPipeline) { + if (maybeCJFinalizerObj(L->getPointerOperand())) + return false; + } if (!MD) return false; diff --git a/llvm/lib/Transforms/Scalar/GVNHoist.cpp b/llvm/lib/Transforms/Scalar/GVNHoist.cpp index 6cdc671dd..50ee01c2b 100644 --- a/llvm/lib/Transforms/Scalar/GVNHoist.cpp +++ b/llvm/lib/Transforms/Scalar/GVNHoist.cpp @@ -249,7 +249,8 @@ static void combineKnownMetadata(Instruction *ReplInst, Instruction *I) { LLVMContext::MD_fpmath, LLVMContext::MD_invariant_load, LLVMContext::MD_invariant_group, - LLVMContext::MD_access_group}; + LLVMContext::MD_access_group, + LLVMContext::MD_cj_agg}; combineMetadata(ReplInst, I, KnownIDs, true); } diff --git a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp index 9698ed973..8396f6e5b 100644 --- a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp +++ b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp @@ -1968,8 +1968,6 @@ bool IndVarSimplify::run(Loop *L) { SE->forgetLoop(L); } - // If we have a trip count expression, rewrite the loop's exit condition - // using it. if (!DisableLFTR) { BasicBlock *PreHeader = L->getLoopPreheader(); diff --git a/llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp b/llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp index 328615011..98d07b460 100644 --- a/llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp +++ b/llvm/lib/Transforms/Scalar/InductiveRangeCheckElimination.cpp @@ -99,6 +99,12 @@ using namespace llvm; using namespace llvm::PatternMatch; +namespace llvm { +extern cl::opt CJPipeline; +static cl::opt CJIRCEMinLoopIterations("cj-irce-min-loop-iterations", + cl::Hidden, cl::init(10)); +} // namespace llvm + static cl::opt LoopSizeCutoff("irce-loop-size-cutoff", cl::Hidden, cl::init(64)); @@ -1431,6 +1437,9 @@ bool LoopConstrainer::run() { bool NeedsPostLoop = Increasing ? SR.HighLimit.has_value() : SR.LowLimit.has_value(); + if (CJPipeline && NeedsPreLoop && NeedsPostLoop) + return false; + Value *ExitPreLoopAt = nullptr; Value *ExitMainLoopAt = nullptr; const SCEVConstant *MinusOneS = @@ -1882,8 +1891,58 @@ InductiveRangeCheckElimination::isProfitableToTransform(const Loop &L, return true; } +// a + b ult c, a >= 0, a + b nuw => a ult select(b <= c, c - b, 0) +static bool optimizeNUWCompare(Loop *L, ScalarEvolution &SE) { + bool Changed = false; + for (auto *BB : L->getBlocks()) { + if (!isa(BB->getTerminator())) + continue; + auto *BI = cast(BB->getTerminator()); + CmpInst::Predicate Pred; + Value *LHS; + Value *RHS; + if (!BI->isConditional() || + !match(BI->getCondition(), m_Cmp(Pred, m_Value(LHS), m_Value(RHS))) || + Pred != CmpInst::Predicate::ICMP_ULT || !isa(LHS) || + !L->isLoopInvariant(RHS)) + continue; + Value *OP0; + Value *OP1; + if (!match(LHS, m_Add(m_Value(OP0), m_Value(OP1))) || + !cast(LHS)->hasNoUnsignedWrap() || + !L->isLoopInvariant(OP1) || !isa(OP0) || + !L->contains(cast(OP0))) + continue; + auto LHSRange = SE.getSignedRange(SE.getSCEV(OP0)); + if (LHSRange.getSignedMin().getSExtValue() < 0) + continue; + + IRBuilder<> IRB(L->getLoopPreheader()->getTerminator()); + auto *ICI0 = IRB.CreateICmp(CmpInst::Predicate::ICMP_SLE, OP1, RHS); + auto *SubI = IRB.CreateSub(RHS, OP1); + auto *SI = + IRB.CreateSelect(ICI0, SubI, ConstantInt::get(IRB.getInt64Ty(), 0)); + IRB.SetInsertPoint(BB->getTerminator()); + auto *ICI1 = IRB.CreateCmp(CmpInst::Predicate::ICMP_ULT, OP0, SI); + auto *Cond = cast(BI->getCondition()); + BI->setCondition(ICI1); + + Cond->eraseFromParent(); + Changed = true; + } + return Changed; +} + bool InductiveRangeCheckElimination::run( Loop *L, function_ref LPMAddNewLoop) { + if (CJPipeline) { // Don't too large + const SCEV *MaxTrips = SE.getConstantMaxBackedgeTakenCount(L); + if (!isa(MaxTrips) && + SE.getUnsignedRange(MaxTrips).getUnsignedMax().getZExtValue() < + CJIRCEMinLoopIterations) + return false; + LoopSizeCutoff = 16; + } if (L->getBlocks().size() >= LoopSizeCutoff) { LLVM_DEBUG(dbgs() << "irce: giving up constraining loop, too large\n"); return false; @@ -1964,7 +2023,8 @@ bool InductiveRangeCheckElimination::run( return false; LoopConstrainer LC(*L, LI, LPMAddNewLoop, LS, SE, DT, SafeIterRange.value()); - bool Changed = LC.run(); + bool Changed = CJPipeline ? optimizeNUWCompare(L, SE) : false; + Changed |= LC.run(); if (Changed) { auto PrintConstrainedLoopInfo = [L]() { diff --git a/llvm/lib/Transforms/Scalar/InsertCJTBAA.cpp b/llvm/lib/Transforms/Scalar/InsertCJTBAA.cpp new file mode 100644 index 000000000..d001fa575 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/InsertCJTBAA.cpp @@ -0,0 +1,937 @@ +//===- InsertCJTBAA.cpp - Insert Cangjie TBAA Metadata ----------*- C++ -*-===// +// +// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. +// This source file is part of the Cangjie project, licensed under Apache-2.0 +// with Runtime Library Exception. +// +// See https://cangjie-lang.cn/pages/LICENSE for license information. +// +//===----------------------------------------------------------------------===// +// +// Insert Cangjie TBAA Metadata for load, store, memcpy, and barrier +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/InsertCJTBAA.h" + +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/CJIntrinsics.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/MDBuilder.h" +#include "llvm/IR/Metadata.h" +#include "llvm/IR/SafepointIRVerifier.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/Casting.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Scalar/CJFillMetadata.h" +#include + +using namespace llvm; +using namespace cangjie; + +namespace llvm { +extern cl::opt CJPipeline; +} // namespace llvm + +static bool isUnnamedArrayLayout(StructType *ST); + +class TBAAProcessUnit { +public: + TBAAProcessUnit(const DataLayout &DL, MDBuilder &MDB, LLVMContext &Context, + Instruction *I, StructType *SrcTy, Type *DstTy, + GEPOperator *GEP, bool IsPrimitiveMetadata, + uint64_t OriginOff) + : DL(DL), MDB(MDB), Context(Context), I(I), SrcTy(SrcTy), DstTy(DstTy), + GEP(GEP), IsPrimitiveMetadata(IsPrimitiveMetadata), + OriginOff(OriginOff){}; + ~TBAAProcessUnit() = default; + + const DataLayout &DL; + MDBuilder &MDB; + LLVMContext &Context; + Instruction *I; + StructType *SrcTy; + Type *DstTy; + GEPOperator *GEP; + bool IsPrimitiveMetadata; + uint64_t OriginOff; +}; + +static bool getMallocType(TBAAProcessUnit &TBAAUnit, CallBase *CB) { + auto ID = CB->getIntrinsicID(); + Value *Klass; + if (ID == Intrinsic::cj_malloc_object) { + Klass = CB->getArgOperand(0)->stripPointerCasts(); + } else if (ID == Intrinsic::cj_malloc_array) { + Klass = CB->getArgOperand(1)->stripPointerCasts(); + } else { + return false; + } + auto GV = dyn_cast(Klass); + if (GV == nullptr || !GV->hasInitializer() || + GV->getMetadata("RelatedType") == nullptr) { + return false; + } + TypeInfo TI(GV); + TBAAUnit.SrcTy = TI.getLayoutType(); + return TBAAUnit.SrcTy == nullptr; +} + +static std::string getArrayName(ArrayType *AT) { + std::string Res; + Type *ET = AT->getArrayElementType(); + unsigned ElemNum = AT->getArrayNumElements(); + Res = Twine(ElemNum).str() + "*"; + if (auto EAT = dyn_cast(ET)) { + return Res + getArrayName(EAT); + } else { + return Res + getPrimitiveTypeName(ET); + } +} + +static std::string getTypeString(Type *Ty) { + if (auto IT = dyn_cast(Ty)) { + switch (IT->getBitWidth()) { + default: + return ""; + case 1: + return "char"; + case 8: + return "i8"; + case 16: + return "i16"; + case 32: + return "i32"; + case 64: + return "i64"; + } + } else if (Ty->isFloatTy()) { + return "f32"; + } else if (Ty->isBFloatTy()) { + return "bfloat"; + } else if (Ty->isHalfTy()) { + return "half"; + } else if (Ty->isDoubleTy()) { + return "f64"; + } else if (Ty->isPointerTy()) { + return "pointer"; + } else if (auto AT = dyn_cast(Ty)) { + return getArrayName(AT); + } else if (auto ST = dyn_cast(Ty)) { + if (ST->hasName()) { + return ST->getStructName().str(); + } + return ""; + } + return ""; +} + +static Type *getArrayInnerType(ArrayType *AT) { + Type *ET = AT->getArrayElementType(); + if (auto AET = dyn_cast(ET)) { + return getArrayInnerType(AET); + } + return ET; +} + +static MDNode *getTBAARoot(MDBuilder &MDB, StringRef Name) { + MDNode *MDRoot = MDB.createTBAARoot("Simple Cangjie TBAA"); + MDNode *MDPointer = MDB.createTBAANode("omnipotent char", MDRoot); + return MDB.createTBAANode(Name, MDPointer); +} + +// Tag: false, expand the array element. +static MDNode *getTBAAMetadata(const DataLayout &DL, MDBuilder &MDB, Type *Ty, + bool Tag = true) { + std::string Name = ""; + Name = getTypeString(Ty); + if (Name.empty()) { + return nullptr; + } + if (auto ST = dyn_cast(Ty)) { + // MDNode: element type + // uint64_t: offset of element type + SmallVector> Fields; + const StructLayout *SL = DL.getStructLayout(ST); + for (unsigned It = 0; It < ST->getStructNumElements(); ++It) { + Type *ET = ST->getStructElementType(It); + MDNode *Filed = getTBAAMetadata(DL, MDB, ET, Tag); + if (Filed == nullptr) { + return nullptr; + } + Fields.push_back( + std::pair(Filed, SL->getElementOffset(It))); + } + return MDB.createTBAAStructTypeNode(Name, Fields); + } + if (auto AT = dyn_cast(Ty)) { + Type *ET = getArrayInnerType(AT); + std::string ETName = getTypeString(ET); + if (ETName.empty()) { + return nullptr; + } + MDNode *ETMD; + if (Tag) { + ETMD = getTBAARoot(MDB, ETName); + } else { + ETMD = getTBAAMetadata(DL, MDB, ET, Tag); + } + if (ETMD == nullptr) { + return nullptr; + } + return MDB.createTBAANode(Name, ETMD); + } + return getTBAARoot(MDB, Name); +} + +// SrcTy does not contain array. +bool llvm::insertTBAAWithDefiniteType(const DataLayout &DL, Instruction *I, + StructType *SrcTy, Type *DstTy, + uint64_t Offset, bool Tag) { + MDBuilder MDB(I->getContext()); + MDNode *SrcMD = getTBAAMetadata(DL, MDB, SrcTy, Tag); + MDNode *DstMD = getTBAAMetadata(DL, MDB, DstTy); + if (DstMD != nullptr && SrcMD != nullptr) { + MDNode *MD = MDB.createTBAAStructTagNode(SrcMD, DstMD, Offset); + I->setMetadata(LLVMContext::MD_tbaa, MD); + return true; + } + return false; +} + +static MDNode *getTBAAPrimitiveMetadata(TBAAProcessUnit &TBAAUnit, Type *Ty) { + assert(!Ty->isStructTy() && !Ty->isArrayTy()); + if (MDNode *MD = getTBAAMetadata(TBAAUnit.DL, TBAAUnit.MDB, Ty)) { + return TBAAUnit.MDB.createTBAAStructTagNode(MD, MD, 0); + } + return nullptr; +} + +static bool +getTBAAStructMetadata(TBAAProcessUnit &TBAAUnit, StructType *ST, + unsigned Offset, + SmallVector &Fields) { + unsigned ElementNum = ST->getStructNumElements(); + const StructLayout *SL = TBAAUnit.DL.getStructLayout(ST); + for (unsigned It = 0; It < ElementNum; ++It) { + Type *ET = ST->getElementType(It); + if (!ET->isStructTy() && !ET->isArrayTy()) { + if (MDNode *MD = getTBAAPrimitiveMetadata(TBAAUnit, ET)) { + unsigned ETOffset = SL->getElementOffset(It) + Offset; + unsigned ETSize = TBAAUnit.DL.getTypeSizeInBits(ET) / 8; + Fields.push_back(MDBuilder::TBAAStructField(ETOffset, ETSize, MD)); + continue; + } else { + return false; + } + } + if (auto AET = dyn_cast(ET)) { + unsigned ETOffset = SL->getElementOffset(It) + Offset; + unsigned ETSize = TBAAUnit.DL.getTypeSizeInBits(ET) / 8; + MDNode *MDRoot = TBAAUnit.MDB.createTBAARoot("Simple Cangjie TBAA"); + MDNode *MDPointer = + TBAAUnit.MDB.createTBAANode("omnipotent char", MDRoot); + MDNode *MD = + TBAAUnit.MDB.createTBAAStructTagNode(MDPointer, MDPointer, 0); + Fields.push_back(MDBuilder::TBAAStructField(ETOffset, ETSize, MD)); + continue; + } + if (auto SET = dyn_cast(ET)) { + unsigned ETOffset = SL->getElementOffset(It) + Offset; + if (!getTBAAStructMetadata(TBAAUnit, SET, ETOffset, Fields)) { + return false; + } + } + } + return true; +} + +// %struct1 = type { i8 addrspace(1)*, i64, i64 } +// %struct2 = type { %struct1, i64 } +// %struct3 = type { i64, %struct2 } +// %0 = bitcast %struct3* %value1 to i8* +// %1 = bitcast %struct3* %value2 to i8* +// call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %0, i8* align 8 %1, i64 +// 40, i1 false), !tbaa.struct !0 +// ...... +// !0 = !{i64 0, i64 8, !1, i64 8, i64 8, !3, i64 16, i64 8, +// !1, i64 24, i64 8, !1, i64 32, i64 8, !1} +// !1 = !{!2, !2, i64 0} +// !2 = !{!"i64", !5} +// !3 = !{!4, !4, i64 0} +// !4 = !{!"pointer", !5} +// !5 = !{!"omnipotent char", !6} +// !6 = !{!"Simple Cangjie TBAA"} +static bool insertCJTBAAStructImpl(TBAAProcessUnit &TBAAUnit, StructType *ST) { + if (ST == nullptr) { + return false; + } + SmallVector Fields; + if (!getTBAAStructMetadata(TBAAUnit, ST, 0, Fields)) { + return false; + } + MDNode *MD = TBAAUnit.MDB.createTBAAStructNode(Fields); + TBAAUnit.I->setMetadata(LLVMContext::MD_tbaa_struct, MD); + return true; +} + +Type *llvm::getInnerTypeByOffset(const DataLayout &DL, StructType *ST, + uint64_t Offset) { + assert(ST != nullptr && "ST should be StructType"); + auto *SL = DL.getStructLayout(ST); + unsigned CurIdx = SL->getElementContainingOffset(Offset); + for (unsigned I = CurIdx; I < ST->getNumElements(); ++I) { + Type *Ty = ST->getElementType(I); + auto ElemOffset = SL->getElementOffset(I); + if (auto *ElemST = dyn_cast(Ty)) { + // For case: %type { %Unit.Type, %Unit.Type, %Tuple } + if (ElemST->getNumElements() == 0) + continue; + + // For case: %type { i64, float, %Unit.Type, %Tuple } + if (ElemOffset > Offset) { + return nullptr; + } + return getInnerTypeByOffset(DL, ElemST, Offset - ElemOffset); + } else { + // %type = type { i8*, i32, i64, i64 } + // The offset is 12, written to the hole between I and I+1. + return Offset == ElemOffset ? Ty : nullptr; + } + } + // %type { i32, float, i32, %Unit.Type } + // The offset is 12, written to the %Unit.Type. + return nullptr; +} + +// There is such a situation: +// %struct = type { i8, float } +// %0 = bitcast %struct* to i64* +// %1 = load i64, i64* %0 +static bool isSameType(TBAAProcessUnit &TBAAUnit, uint64_t Offset = 0) { + if (TBAAUnit.SrcTy == nullptr) { + return false; + } + Type *SrcTy = getInnerTypeByOffset(TBAAUnit.DL, TBAAUnit.SrcTy, Offset); + if (SrcTy == nullptr) { + return false; + } + return SrcTy == TBAAUnit.DstTy; +} + +// %struct1 = type { i8 addrspace(1)*, i64, i64 } +// %struct2 = type { %struct1, i64 } +// %struct3 = type { i64, %struct2 } +// %0 = getelementptr inbounds %struct3, %struct3* %value, i32 0, i32 0 +// store i64 1, i64* %0, !tbaa !2480 +// ...... +// !0 = !{!1, !4, i64 0} +// !1 = !{!"struct3", !4, i64 0, !2, i64 8} +// !2 = !{!"struct2", !3, i64 0, !4, i64 24} +// !3 = !{!"struct1", !5, i64 0, !4, i64 8, !4, i64 16} +// !4 = !{!"i64", !6} +// !5 = !{!"pointer", !6} +// !6 = !{!"omnipotent char", !7} +// !7 = !{!"Simple Cangjie TBAA"} +static bool insertCJTBAAMeta(TBAAProcessUnit &TBAAUnit, APInt Offset, + bool IsSameTy = false) { + // Offset = -1 means dst type is different of src type + if (TBAAUnit.IsPrimitiveMetadata && Offset != -1) { + if (MDNode *MD = getTBAAPrimitiveMetadata(TBAAUnit, TBAAUnit.DstTy)) { + TBAAUnit.I->setMetadata(LLVMContext::MD_tbaa, MD); + return true; + } + return false; + } + if (TBAAUnit.SrcTy == nullptr) { + return false; + } + if (auto DstST = dyn_cast(TBAAUnit.DstTy)) { + return insertCJTBAAStructImpl(TBAAUnit, DstST); + } + if (TBAAUnit.DstTy->isArrayTy()) { + return false; + } + if (!IsSameTy && !isSameType(TBAAUnit, Offset.getZExtValue())) { + return false; + } + return insertTBAAWithDefiniteType(TBAAUnit.DL, TBAAUnit.I, TBAAUnit.SrcTy, + TBAAUnit.DstTy, + Offset.getZExtValue() + TBAAUnit.OriginOff); +} + +static bool insertArrayLayoutMeta(TBAAProcessUnit &TBAAUnit, APInt &Offset, + APInt &SubOffset) { + if (SubOffset.getSExtValue() < 0) { + TBAAUnit.IsPrimitiveMetadata = false; + return insertCJTBAAMeta(TBAAUnit, Offset, true); + } + return insertTBAAWithDefiniteType( + TBAAUnit.DL, TBAAUnit.I, TBAAUnit.SrcTy, TBAAUnit.DstTy, + Offset.getZExtValue() + SubOffset.getSExtValue() + TBAAUnit.OriginOff, + false); +} + +static bool IsArrayLayoutElement(TBAAProcessUnit &TBAAUnit, + const StructLayout *SL, GEPOperator *GEP) { + assert(((TBAAUnit.SrcTy->hasName() && + TBAAUnit.SrcTy->getStructName().startswith("ArrayLayout.")) || + (isUnnamedArrayLayout(TBAAUnit.SrcTy))) && + "Src type should be array type"); + if (TBAAUnit.DstTy == nullptr) { + return false; + } + // 3: ArrayLayout.xxx = type { %ArrayBase, [0 * xxx] }, + // indices of gep is 0, 1 and x + if (GEP->getNumIndices() < 3) { + return false; + } + // getelementptr %ArrayLayout.xxx, %ArrayLayout.xxx* %a, i32 0, i32 1, i64 %b + // Such gep is to get the array element xxx in ArrayLayout.xxx + // Index0: first indice of gep + auto Index0 = dyn_cast(GEP->getOperand(1)); + // Index1: second indice of gep + auto Index1 = dyn_cast(GEP->getOperand(2)); + if (Index0 == nullptr || Index1 == nullptr) { + return false; + } + // 4: When the num of indices is more than 4, we do nothing now. + if (Index0->getZExtValue() != 0 || Index1->getZExtValue() != 1 || + GEP->getNumIndices() > 4) { + return false; + } + APInt Offset(64, SL->getElementOffset(1)); + APInt SubOffset(64, -1); + // 3: ArrayLayout.xxx = type { %ArrayBase, [0 * xxx] }, + // indices of gep is 0, 1 and x + if (GEP->getNumIndices() == 3) { + Type *Ty = TBAAUnit.SrcTy->getStructElementType(1)->getArrayElementType(); + if (Ty != TBAAUnit.DstTy && !isa(Ty)) + return false; + if (!isa(Ty)) + return insertArrayLayoutMeta(TBAAUnit, Offset, SubOffset); + + // The indices of the GEP is 3, and the internal array of Arraylayout is a + // record array, so we are accessing the first element of the xth record of + // the record array. + SubOffset = 0; + return insertArrayLayoutMeta(TBAAUnit, Offset, SubOffset); + } + + // Index3: fourth indice of gep + auto Index3 = dyn_cast(GEP->getOperand(4)); + StructType *ArrayET = dyn_cast( + TBAAUnit.SrcTy->getStructElementType(1)->getArrayElementType()); + if (Index3 == nullptr || ArrayET == nullptr) { + return false; + } + SubOffset = APInt(64, TBAAUnit.DL.getStructLayout(ArrayET)->getElementOffset( + Index3->getZExtValue())); + return insertArrayLayoutMeta(TBAAUnit, Offset, SubOffset); +} + +static bool getSrcTyAndOffset(TBAAProcessUnit &TBAAUnit, Value *OP, + APInt &Offset, bool &CanAccuOffset) { + if (auto GEP = dyn_cast(OP)) { + if (GEP->getSourceElementType()->isStructTy()) { + TBAAUnit.SrcTy = dyn_cast(GEP->getSourceElementType()); + TBAAUnit.GEP = GEP; + + // SrcTy = type {i32, i32, [0 x i32], ...} + // indices of gep is 0, 2, and a spefic constant int + // If we find a type in GEP is ArrayType, we choose to align offset to the + // offset of the first element of Array, so as to avoid the tbaa verifier + // error. + Type *Ty = TBAAUnit.SrcTy; + for (unsigned I = 1; I < GEP->getNumIndices() - 1; ++I) { + auto *Idx = dyn_cast(GEP->getOperand(I + 1)); + if (auto *STy = dyn_cast(Ty)) + Ty = STy->getStructElementType(Idx->getZExtValue()); + else if (auto *ATy = dyn_cast(Ty)) { + CanAccuOffset = false; + break; + } + } + if (isa(Ty)) + CanAccuOffset = false; + } else if (auto GEPCB = dyn_cast( + GEP->getPointerOperand()->stripPointerCasts())) { + return getSrcTyAndOffset(TBAAUnit, GEPCB, Offset, CanAccuOffset); + } else if (auto *GEPAI = dyn_cast( + GEP->getPointerOperand()->stripPointerCasts())) { + TBAAUnit.GEP = GEP; + return getSrcTyAndOffset(TBAAUnit, GEPAI, Offset, CanAccuOffset); + } + if (!GEP->accumulateConstantOffset(TBAAUnit.DL, Offset)) { + CanAccuOffset = false; + } + return true; + } else if (auto CB = dyn_cast(OP)) { + return getMallocType(TBAAUnit, CB); + } else if (auto AI = dyn_cast(OP)) { + Type *Ty = AI->getAllocatedType(); + if (Ty->isStructTy()) { + CanAccuOffset = !TBAAUnit.GEP || TBAAUnit.GEP->accumulateConstantOffset( + TBAAUnit.DL, Offset); + if (CanAccuOffset) + TBAAUnit.SrcTy = dyn_cast(Ty); + } else if (!Ty->isStructTy() && AI->getAllocatedType() == TBAAUnit.DstTy) { + // We check the alloca type and dst type, because sroa may modify alloca + // and then generate a series of memory insts based on new alloca, which + // will cause the instruction we are currently analyzing and the newly + // generated instruction by sroa to generate the wrong alias information + // (NoAlias). + TBAAUnit.IsPrimitiveMetadata = true; + if (TBAAUnit.GEP && + (!TBAAUnit.GEP->accumulateConstantOffset(TBAAUnit.DL, Offset) || + Offset != 0 || Ty != TBAAUnit.GEP->getSourceElementType())) { + CanAccuOffset = false; + Offset = -1; + } + } + return true; + } else if (isa(OP) || isa(OP)) { + assert(OP->getType()->isPointerTy()); + Type *ET = OP->getType()->getNonOpaquePointerElementType(); + if (auto SET = dyn_cast(ET)) { + TBAAUnit.SrcTy = SET; + return true; + } + if (TBAAUnit.DstTy != nullptr && !TBAAUnit.DstTy->isPointerTy()) { + TBAAUnit.IsPrimitiveMetadata = true; + return true; + } + } + return false; +} + +static bool isUnnamedArrayLayout(StructType *ST) { + // 2: ST is { %ArrayBase, [n x %xxx] } + if (ST->getStructNumElements() != 2) { + return false; + } + auto ET0 = dyn_cast(ST->getStructElementType(0)); + auto ET1 = dyn_cast(ST->getStructElementType(1)); + return ET0 != nullptr && ET1 != nullptr && + ET0->getStructName().equals("ArrayBase"); +} + +static bool preArrayLayout(TBAAProcessUnit &TBAAUnit) { + if (TBAAUnit.GEP == nullptr || TBAAUnit.SrcTy == nullptr) { + return false; + } + if (TBAAUnit.SrcTy->hasName()) { + return TBAAUnit.SrcTy->getStructName().startswith("ArrayLayout."); + } + if (isUnnamedArrayLayout(TBAAUnit.SrcTy)) { + return true; + } + return false; +} + +static bool tryToInsertTBAAForGEPGEP(TBAAProcessUnit &TBAAUnit) { + auto *GEP = TBAAUnit.GEP; + if (!GEP) + return false; + auto *GEPGEP = dyn_cast(GEP->getPointerOperand()); + if (!GEPGEP || GEP->getNumIndices() != 1 || + isa(GEP->getSourceElementType()) || + !isa(GEP->getOperand(1))) + return false; + if (!GEPGEP->getSourceElementType()->isStructTy()) + return false; + + auto CheckGEPGEP = [&GEPGEP] { + Type *Ty = GEPGEP->getSourceElementType(); + for (unsigned I = 1; I < GEPGEP->getNumIndices() - 1; ++I) { + auto *Idx = dyn_cast(GEPGEP->getOperand(I + 1)); + if (!Idx) { + if (Ty->isStructTy()) + return false; + else if (Ty->isArrayTy()) + Ty = cast(Ty)->getElementType(); + else + return false; + } else { + if (!Ty->isStructTy()) + return false; + Ty = cast(Ty)->getElementType(Idx->getZExtValue()); + } + } + return Ty->isStructTy(); + }; + // %1 = gep %0, 0, 1, %index, 1 + // %2 = gep %1, 1 + // The type of the penultimate pointer of the GEP must be struct. Otherwise, + // the verifier may be incorrect after the InstCombine optimizes the GEP. + if (!CheckGEPGEP()) + return false; + + // %1 = gep %"ArrayLayout.xxx" addrspace(1)* %0, 0, 1, %x, 1 + // %2 = gep i64 addrspace(1)* %1, i64 1 + // load %2 + uint64_t Size = + TBAAUnit.DL.getTypeSizeInBits(GEP->getSourceElementType()) / 8; + uint64_t OriginOff = + cast(GEP->getOperand(1))->getZExtValue() * Size; + return prepareCJTBAA(TBAAUnit.DL, TBAAUnit.I, GEPGEP, TBAAUnit.DstTy, false, + OriginOff); +} + +bool llvm::prepareCJTBAA(const DataLayout &DL, Instruction *I, Value *OP, + Type *DstTy, bool IsTBAAStruct, uint64_t OriginOff) { + LLVMContext &Context = I->getContext(); + MDBuilder MDB(Context); + Value *SrcOP = isa(OP) ? OP : OP->stripPointerCasts(); + auto GEP = dyn_cast(SrcOP); + TBAAProcessUnit TBAAUnit(DL, MDB, Context, I, nullptr, DstTy, GEP, false, + OriginOff); + APInt Offset(64, 0); + bool CanAccuOffset = true; + if (!getSrcTyAndOffset(TBAAUnit, SrcOP, Offset, CanAccuOffset)) { + return tryToInsertTBAAForGEPGEP(TBAAUnit); + } + if (!TBAAUnit.IsPrimitiveMetadata && TBAAUnit.SrcTy == nullptr && + !IsTBAAStruct) { + return tryToInsertTBAAForGEPGEP(TBAAUnit); + } + if (preArrayLayout(TBAAUnit)) { + if (IsArrayLayoutElement(TBAAUnit, DL.getStructLayout(TBAAUnit.SrcTy), GEP)) + return true; + return tryToInsertTBAAForGEPGEP(TBAAUnit); + } + if (!CanAccuOffset) { // For this, we will not try again. + return false; + } + if (IsTBAAStruct) { + return insertCJTBAAStructImpl(TBAAUnit, TBAAUnit.SrcTy); + } else { + if (insertCJTBAAMeta(TBAAUnit, Offset)) + return true; + return tryToInsertTBAAForGEPGEP(TBAAUnit); + } +} + +static MDNode *getTBAAStructTypeNode(Value *I, const DataLayout &DL, + Instruction *UI) { + // I: the base pointer + Instruction *MDI = nullptr; + Instruction *Ptr = nullptr; + switch (UI->getOpcode()) { + default: + break; + case Instruction::Load: { + auto *LI = cast(UI); + Ptr = dyn_cast(LI->getPointerOperand()); + MDI = LI; + } break; + case Instruction::Store: { + auto *SI = cast(UI); + Ptr = dyn_cast(SI->getPointerOperand()); + MDI = SI; + } break; + case Instruction::Call: { + auto *CI = cast(UI); + auto ID = CI->getIntrinsicID(); + if (ID == Intrinsic::cj_gcwrite_ref || ID == Intrinsic::cj_gcread_ref) { + if (getPointerArg(CI)->stripInBoundsConstantOffsets() == I) { + Ptr = dyn_cast(getPointerArg(CI)); + MDI = CI; + } + } + } break; + } + if (Ptr) { + // Ty1: type of MDI, Ty2: type of I + // Ty1 maybe a sub struct of Ty2, and the tbaa of MDI is based on Ty1. + // We want the tbaa based on the I's real type, so we need to check for + // that. + APInt Offset(64, 0); + auto *PtrBase = Ptr->stripAndAccumulateConstantOffsets(DL, Offset, true); + // 1. I must be pointer base of memory instruction. + if (PtrBase == I && MDI->hasMetadata(LLVMContext::MD_tbaa)) { + uint64_t MDOffset = + mdconst::extract( + MDI->getMetadata(LLVMContext::MD_tbaa)->getOperand(2)) + ->getZExtValue(); // 2: Offset + // 2. The offset obtained from accumulate and the offset obtained from + // tbaa must be same. + if (MDOffset == Offset.getZExtValue()) + return dyn_cast_or_null(MDI->getMetadata(LLVMContext::MD_tbaa) + ->getOperand(0)); // 0: BaseTag + } + } + return nullptr; +} + +static MDNode *getInstStructTBAANode(const DataLayout &DL, Value *I) { + // Find instructions that read or write the memory to I and get their tbaa. + SmallVector WorkLists = {I}; + while (!WorkLists.empty()) { + auto *TmpI = WorkLists.pop_back_val(); + for (Value *V : TmpI->users()) { + if (isa(V) || isa(V) || isa(V)) + if (auto *N = getTBAAStructTypeNode(I, DL, cast(V))) + return N; + if (isa(V) || isa(V) || + isa(V)) + WorkLists.push_back(V); + } + } + // Assert that all tbaa of them are the same. + return nullptr; +} + +// Update with sibling existing metadata +// Base +// / \ +// I S +// 1. Find the tbaa struct type tag of base through S; +// 2. Then calculate the offset from I to base; +// 3. Insert the tbaa of I based on 1 and 2. +// Some optimization points that can be done in the future: ref uses the +// exact type instead of i8 addrspace(1) *, so that the type can be read from +// the parent (base) tbaa, which can help the child instruction insert tbaa. +static bool updateBySibling(const DataLayout &DL, Instruction *I, Value *Ptr) { + APInt Offset(64, 0); + auto *BasePtr = Ptr->stripAndAccumulateConstantOffsets(DL, Offset, true); + // {i64, [0 x %record.T]} + // Sibling: getelementptr %base, 0, 0 + // Ptr: getelementptr %base, 0, 1, i, 0 + // BasePtr is pointing to [0 x %record.T][i], underlying object is pointing to + // %base, sibling is pointing to %base. If we dont check, the offset is + // imprecise. + if (!BasePtr || BasePtr != getUnderlyingObject(Ptr, 0)) + return false; + auto *N = getInstStructTBAANode(DL, BasePtr); + if (!N) + return false; + auto *BaseMD = N; + std::string TypeStr = + getTypeString(Ptr->getType()->getNonOpaquePointerElementType()); + uint64_t OffsetValue = Offset.getZExtValue(); + while (N && N->getNumOperands() > 1) { + if (OffsetValue == 0) + if (MDString *MDStr = dyn_cast(N->getOperand(0))) + if (MDStr->getString() == TypeStr) + break; + if (N->getNumOperands() == 2) { + // {0*array_element, array_element} + N = dyn_cast(N->getOperand(0)); + continue; + } + unsigned Idx = 1; + assert(N->getNumOperands() >= 3 && + "TBAAStructType is illegal, please check!"); + for (; Idx < N->getNumOperands(); Idx += 2) { + uint64_t CurOffset = + mdconst::extract(N->getOperand(Idx + 1))->getZExtValue(); + if (CurOffset > OffsetValue) + break; + } + assert(isa(N->getOperand(Idx - 1)) && + "TBAAStructType is illegal, please check!"); + OffsetValue -= + mdconst::extract(N->getOperand(Idx - 1))->getZExtValue(); + N = dyn_cast(N->getOperand(Idx - 2)); + } + // Not root + if (N && N->getNumOperands() > 1) { + MDBuilder MDB(I->getContext()); + auto *MD = MDB.createTBAAStructTagNode(BaseMD, N, Offset.getZExtValue()); + I->setMetadata(LLVMContext::MD_tbaa, MD); + return true; + } + return false; +} + +static bool maybeAllocaWithTwoDifferentType(Value *Ptr, Type *Ty, + Instruction *I) { + auto *BPtr = getUnderlyingObject(Ptr, 0); + // Can't determine if they're alloca. + if (isa(BPtr) || isa(BPtr)) { + I->setMetadata(LLVMContext::MD_tbaa, nullptr); + return true; + } + if (isa(BPtr) && Ty != Ptr->stripPointerCasts() + ->getType() + ->getNonOpaquePointerElementType()) { + I->setMetadata(LLVMContext::MD_tbaa, nullptr); + return true; + } + return false; +} + +// This function will only be called from other pass. +bool llvm::updateTBAA(const DataLayout &DL, Instruction *I) { + Value *Ptr = nullptr; + Type *Ty = nullptr; + if (auto *LI = dyn_cast(I)) { + Ptr = LI->getPointerOperand(); + Ty = LI->getType(); + } else if (auto *SI = dyn_cast(I)) { + Ptr = SI->getPointerOperand(); + Ty = SI->getValueOperand()->getType(); + } else if (auto *II = dyn_cast(I)) { + Intrinsic::ID ID = II->getIntrinsicID(); + switch (ID) { + case Intrinsic::cj_gcwrite_ref: + Ptr = getPointerArg(II); + Ty = II->getOperand(0)->getType(); + break; + case Intrinsic::cj_gcwrite_static_ref: + Ptr = getPointerArg(II); + Ty = II->getOperand(0)->getType(); + break; + case Intrinsic::cj_gcread_ref: + Ptr = getPointerArg(II); + Ty = Ptr->getType()->getNonOpaquePointerElementType(); + break; + case Intrinsic::cj_gcread_static_ref: + Ptr = getPointerArg(II); + Ty = Ptr->getType()->getNonOpaquePointerElementType(); + break; + default: + llvm_unreachable( + "must be cangjie read/write barrier and not be struct barrier"); + } + } + if (maybeAllocaWithTwoDifferentType(Ptr, Ty, I)) + return false; + if (I->hasMetadata(LLVMContext::MD_tbaa)) + return false; + assert(!I->hasMetadata(LLVMContext::MD_tbaa) && "must have no tbaa"); + + // Instructions such as load i56, [7 * i8]* %1 may appear, and we not + // support insert tbaa medadata for i56. Therefore, for IntegerType , filter + // its width. + if (auto *IT = dyn_cast(Ty)) { + auto Width = IT->getBitWidth(); + if (Width != 1 && Width != 8 && Width != 16 && Width != 32 && Width != 64) + return false; + } + // Currently, vector type also not supported. + if (Ty->isVectorTy()) + return false; + // These instructions should not use tbaa.struct. + I->setMetadata(LLVMContext::MD_tbaa_struct, nullptr); + return prepareCJTBAA(DL, I, Ptr, Ty) || updateBySibling(DL, I, Ptr); +} + +static bool insertCJTBAA(Function &F) { + const DataLayout &DL = F.getParent()->getDataLayout(); + bool Changed = false; + for (auto I = inst_begin(F); I != inst_end(F);) { + Instruction *Inst = &*I++; + if (auto *LI = dyn_cast(Inst)) { + Type *Ty = LI->getType(); + if (isa(Ty) || isa(Ty)) { + Changed |= prepareCJTBAA(DL, LI, LI->getPointerOperand(), + LI->getType(), true); + } else { + Changed |= prepareCJTBAA(DL, LI, LI->getPointerOperand(), LI->getType()); + } + } else if (auto *SI = dyn_cast(Inst)) { + Type *Ty = SI->getValueOperand()->getType(); + if (isa(Ty) || isa(Ty)) { + Changed |= prepareCJTBAA(DL, SI, SI->getPointerOperand(), + SI->getValueOperand()->getType(), true); + } else { + Changed |= prepareCJTBAA(DL, SI, SI->getPointerOperand(), + SI->getValueOperand()->getType()); + } + } else if (auto *CB = dyn_cast(Inst)) { + unsigned OpNO; + bool IsTBAAStruct = true; + Type *DstTy = nullptr; + switch (CB->getIntrinsicID()) { + default: + continue; + case Intrinsic::memcpy: + case Intrinsic::memset: + case Intrinsic::memmove: + case Intrinsic::cj_memset: + case Intrinsic::cj_gcread_struct: + case Intrinsic::cj_gcread_static_struct: + case Intrinsic::cj_gcwrite_static_struct: + // 0: Dst Operand for these + OpNO = 0; + break; + case Intrinsic::cj_gcread_static_ref: + OpNO = 0; + DstTy = getPointerArg(CB)->getType()->getNonOpaquePointerElementType(); + IsTBAAStruct = false; + break; + case Intrinsic::cj_gcread_ref: + OpNO = 1; + DstTy = getPointerArg(CB)->getType()->getNonOpaquePointerElementType(); + IsTBAAStruct = false; + break; + case Intrinsic::cj_gcwrite_ref: { + // 2: Dst Operand for these + OpNO = 2; + DstTy = getValueArg(CB)->getType(); + IsTBAAStruct = false; + break; + } + case Intrinsic::cj_gcwrite_static_ref: + case Intrinsic::cj_gcwrite_struct: + case Intrinsic::cj_array_copy_ref: + case Intrinsic::cj_array_copy_struct: + // 1: Dst Operand for these + OpNO = 1; + break; + } + Changed |= + prepareCJTBAA(DL, CB, CB->getArgOperand(OpNO), DstTy, IsTBAAStruct); + } + } + return Changed; +} + +PreservedAnalyses InsertCJTBAA::run(Function &F, + AnalysisManager &) const { + if (insertCJTBAA(F)) { + return PreservedAnalyses::none(); + } + return PreservedAnalyses::all(); +} + +namespace { +class InsertCJTBAALegacyPass : public FunctionPass { +public: + // Pass identification, replacement for typeid. + static char ID; + + InsertCJTBAALegacyPass() : FunctionPass(ID) { + initializeInsertCJTBAALegacyPassPass(*PassRegistry::getPassRegistry()); + } + ~InsertCJTBAALegacyPass() = default; + + bool runOnFunction(Function &F) override { return insertCJTBAA(F); }; +}; + +} // end namespace + +char InsertCJTBAALegacyPass::ID = 0; + +FunctionPass *llvm::createInsertCJTBAALegacyPass() { + return new InsertCJTBAALegacyPass(); +} + +INITIALIZE_PASS(InsertCJTBAALegacyPass, "insert-cj-tbaa", "Insert CJ TBAA", + false, false) diff --git a/llvm/lib/Transforms/Scalar/JumpThreading.cpp b/llvm/lib/Transforms/Scalar/JumpThreading.cpp index b31eab50c..e336cd470 100644 --- a/llvm/lib/Transforms/Scalar/JumpThreading.cpp +++ b/llvm/lib/Transforms/Scalar/JumpThreading.cpp @@ -2908,6 +2908,10 @@ bool JumpThreadingPass::tryToUnfoldSelectInCurrBB(BasicBlock *BB) { Instruction *Term = SplitBlockAndInsertIfThen(Cond, SI, false); BasicBlock *SplitBB = SI->getParent(); BasicBlock *NewBB = Term->getParent(); + if (BB->getParent()->hasCangjieGC()) { + SplitBB->setName(BB->getName() + ".jumpthread"); + NewBB->setName(BB->getName() + ".jumpthread"); + } PHINode *NewPN = PHINode::Create(SI->getType(), 2, "", SI); NewPN->addIncoming(SI->getTrueValue(), Term->getParent()); NewPN->addIncoming(SI->getFalseValue(), BB); diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp index f54264b1d..0b525b16b 100644 --- a/llvm/lib/Transforms/Scalar/LICM.cpp +++ b/llvm/lib/Transforms/Scalar/LICM.cpp @@ -65,13 +65,19 @@ #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/PatternMatch.h" #include "llvm/IR/PredIteratorCache.h" +#include "llvm/IR/SafepointIRVerifier.h" +#include "llvm/IR/Type.h" #include "llvm/InitializePasses.h" +#include "llvm/Support/Alignment.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" @@ -88,6 +94,9 @@ using namespace llvm; namespace llvm { class BlockFrequencyInfo; class LPMUpdater; + +extern cl::opt LICMDisableBarrier; +extern cl::opt CJPipeline; } // namespace llvm #define DEBUG_TYPE "licm" @@ -154,7 +163,7 @@ static bool isSafeToExecuteUnconditionally( Instruction &Inst, const DominatorTree *DT, const TargetLibraryInfo *TLI, const Loop *CurLoop, const LoopSafetyInfo *SafetyInfo, OptimizationRemarkEmitter *ORE, const Instruction *CtxI, - bool AllowSpeculation); + bool AllowSpeculation, MemorySSA *MSSA, AAResults *AA); static bool pointerInvalidatedByLoop(MemorySSA *MSSA, MemoryUse *MU, Loop *CurLoop, Instruction &I, SinkAndHoistLICMFlags &Flags); @@ -173,7 +182,7 @@ static void moveInstructionBefore(Instruction &I, Instruction &Dest, static void foreachMemoryAccess(MemorySSA *MSSA, Loop *L, function_ref Fn); -static SmallVector, 0> +static SmallVector collectPromotionCandidates(MemorySSA *MSSA, AliasAnalysis *AA, Loop *L); namespace { @@ -379,6 +388,62 @@ llvm::SinkAndHoistLICMFlags::SinkAndHoistLICMFlags( } } +// Get all load instructions's base pointer in br pred for jumps to implicit +// exception BB. Currently, only IndexOutOfBoundsException and OverflowException +// are considered. +static void +collectBBBasePointers(SmallPtrSet &BBS, + DenseMap> &BaseInsts) { + for (BasicBlock *BB : BBS) { + if (!isa(BB->getTerminator())) + continue; + auto *BI = cast(BB->getTerminator()); + if (BI->isUnconditional()) + continue; + BasicBlock *BB0 = BI->getSuccessor(0), *BB1 = BI->getSuccessor(1); + // A normal br, skip it + if (!isa(BB0->getTerminator()) && + !isa(BB1->getTerminator())) + continue; + if (isa(BB0->getTerminator())) + std::swap(BB0, BB1); + + // For exception BBs of IndexOutOfBounds and OverflowException, record + // their base pointers. + bool IsValidUnreachableBB = false; + for (Instruction &I : *BB1) { + if (auto *CI = dyn_cast(&I)) { + Function *F = CI->getCalledFunction(); + if (!F) + continue; + const auto &FuncName = F->getName(); + if (FuncName.contains("IndexOutOfBoundsException") || + FuncName.contains("OverflowException") || + FuncName.contains("NoneValueException")) { + IsValidUnreachableBB = true; + break; + } + } + } + if (IsValidUnreachableBB) { + CmpInst::Predicate Pred; + Value *LV; + Value *RV; + using namespace PatternMatch; + if (match(BI->getCondition(), m_Cmp(Pred, m_Value(LV), m_Value(RV)))) { + if (auto *Load = dyn_cast(LV)) + BaseInsts[BB].insert(Load); + if (auto *Load = dyn_cast(RV)) + BaseInsts[BB].insert(Load); + } else if (auto *LI = dyn_cast(BI->getCondition())) { + BaseInsts[BB].insert(LI); + } else if (auto *Phi = dyn_cast(BI->getCondition())) { + BaseInsts[BB].insert(Phi); + } + } + } +} + /// Hoist expressions out of the specified loop. Note, alias info for inner /// loop is not preserved so it is not a good idea to run LICM multiple /// times on one loop. @@ -483,11 +548,12 @@ bool LoopInvariantCodeMotion::runOnLoop( bool LocalPromoted; do { LocalPromoted = false; - for (const SmallSetVector &PointerMustAliases : + for (const MustAliasPair &PointerMustAliasesPair : collectPromotionCandidates(MSSA, AA, L)) { LocalPromoted |= promoteLoopAccessesToScalars( - PointerMustAliases, ExitBlocks, InsertPts, MSSAInsertPts, PIC, LI, - DT, TLI, L, MSSAU, &SafetyInfo, ORE, LicmAllowSpeculation); + PointerMustAliasesPair, ExitBlocks, InsertPts, MSSAInsertPts, PIC, + LI, DT, TLI, L, MSSAU, &SafetyInfo, ORE, LicmAllowSpeculation, + AA); } Promoted |= LocalPromoted; } while (LocalPromoted); @@ -902,7 +968,8 @@ bool llvm::hoistRegion(DomTreeNode *N, AAResults *AA, LoopInfo *LI, canSinkOrHoistInst(I, AA, DT, CurLoop, MSSAU, true, Flags, ORE) && isSafeToExecuteUnconditionally( I, DT, TLI, CurLoop, SafetyInfo, ORE, - CurLoop->getLoopPreheader()->getTerminator(), AllowSpeculation)) { + CurLoop->getLoopPreheader()->getTerminator(), AllowSpeculation, + MSSAU.getMemorySSA(), AA)) { hoist(I, DT, CurLoop, CFH.getOrCreateHoistedBlock(BB), SafetyInfo, MSSAU, SE, ORE); HoistedInstructions.push_back(&I); @@ -1715,8 +1782,14 @@ static void hoist(Instruction &I, const DominatorTree *DT, const Loop *CurLoop, // The check on hasMetadataOtherThanDebugLoc is to prevent us from burning // time in isGuaranteedToExecute if we don't actually have anything to // drop. It is a compile time optimization, not required for correctness. - !SafetyInfo->isGuaranteedToExecute(I, DT, CurLoop)) - I.dropUndefImplyingAttrsAndUnknownMetadata(); + !SafetyInfo->isGuaranteedToExecute(I, DT, CurLoop)) { + if (CJPipeline) { + I.dropUndefImplyingAttrsAndUnknownMetadata( + {LLVMContext::MD_tbaa, LLVMContext::MD_untrusted_ref}); + } else { + I.dropUndefImplyingAttrsAndUnknownMetadata(); + } + } if (isa(I)) // Move the new node to the end of the phi list in the destination block. @@ -1734,6 +1807,149 @@ static void hoist(Instruction &I, const DominatorTree *DT, const Loop *CurLoop, ++NumHoisted; } +// bb0: +// %0 = load (gep %ptr, 0, 0) +// store %0, %1 +// ... +// %2 = load %0 +// %3 = icmp %2, 0 +// br %3, label unreachable_bb, bb1 +// bb1: +// %4 = load (gep %ptr, 0, 1) +// %5 = gep %4, ... +// %6 = load %5 +// Now, we want to hoist %6. AI is %1, I is %2, HoistLoadPtr is %6. +static bool isNoAliasAllocaValue(AllocaInst *AI, Instruction *I, + MemorySSA *MSSA, AAResults *AA, + Value *HoistLoadPtr) { + assert(AI != nullptr); + // For alloca of struct type, finding the exact define for a given offset is a + // complex and time-consuming task. We don't add any extra complexity here. + if (isa(AI->getAllocatedType())) + return false; + auto IsTopmostMA = [&MSSA](MemoryAccess *MA) { + auto *Def = dyn_cast(MA); + return !Def || MSSA->isLiveOnEntryDef(Def); + }; + + MemoryAccess *LoadMA = MSSA->getMemoryAccess(I); + auto *LoadMD = cast(LoadMA)->getDefiningAccess(); + // The I memory location has no clobber memory def before it. + if (IsTopmostMA(LoadMD)) + return true; + if (isa(LoadMD)) + return false; + auto &DL = AI->getModule()->getDataLayout(); + auto Loc = MemoryLocation( + AI, LocationSize::precise(DL.getTypeAllocSize(AI->getAllocatedType()))); + auto *ClobberMA = MSSA->getWalker()->getClobberingMemoryAccess(LoadMD, Loc); + // The alloca memory location has no clobber memory def before LoadMD. + if (IsTopmostMA(ClobberMA)) + return true; + if (isa(ClobberMA)) + return false; + auto *ClobberMD = cast(ClobberMA); + + // AI is a alloca inst, and the clobber inst must be store or call, for call, + // it is difficult to analyze. + auto *SI = dyn_cast(ClobberMD->getMemoryInst()); + if (!SI) + return false; + auto *Base = getUnderlyingObject(SI->getValueOperand()); + if (auto *NewAI = dyn_cast(Base)) + return isNoAliasAllocaValue(NewAI, SI, MSSA, AA, HoistLoadPtr); + AliasResult AR = AliasResult::MayAlias; + // Check whether the base memory location corresponding to the pointer operand + // of load that we want to LICM is aliased with the base memory location + // corresponding to the store value. + if (auto *LI = dyn_cast(Base)) { + AR = AA->alias(LI->getPointerOperand(), HoistLoadPtr); + } else if (auto *II = dyn_cast(Base); + II && II->isCJRefGCRead()) { + AR = AA->alias(II->getOperand(0), HoistLoadPtr); + } else { + return false; + } + return AR == AliasResult::NoAlias; +} + +// Based on all the Terminators in the BBs in the loop that can reach LI, we +// can't move LI to the preheader if br pred contains a LI's pointer, or if the +// Terminator is a normal if-else branch. +static bool +cjPartialSpeculativeExecute(Instruction *I, const Loop *L, MemorySSA *MSSA, AAResults *AA) { + Value *LoadPtr = nullptr; + if (auto *LI = dyn_cast(I)) + LoadPtr = LI->getPointerOperand(); + else if (auto *II = dyn_cast(I)) + LoadPtr = II->getCJRefGCReadPtr(); + + if (!LoadPtr) + return false; + + BasicBlock *BB = I->getParent(); + if (BB == L->getHeader()) + return true; + // Collect all BasicBlocks that can reach CurrentBB from PreHeader. + SmallPtrSet Predecessors; + SmallVector WorkList; + for (auto *Pred : predecessors(BB)) { + Predecessors.insert(Pred); + WorkList.push_back(Pred); + } + while (!WorkList.empty()) { + auto *Pred = WorkList.pop_back_val(); + if (Pred == L->getHeader()) + continue; + for (auto *PPred : predecessors(Pred)) { + if (Predecessors.insert(PPred).second) + WorkList.push_back(PPred); + } + } + // Traverse all Predecessors and speculatively execute the BasicBlock that + // contains the implicit exception. + DenseMap> BaseInsts; + collectBBBasePointers(Predecessors, BaseInsts); + for (auto *Pred : Predecessors) { + if (BaseInsts.count(Pred)) { + for (auto *V : BaseInsts[Pred]) { + if (isa(V)) { + auto *Ptr = + getUnderlyingObject(cast(V)->getPointerOperand()); + auto *LoadBase = getUnderlyingObject(LoadPtr); + if (Ptr == LoadBase) + return false; + // If LoadPtr is a load/read inst, check whether the define load ptr + // of predecessor's condition, if it exits, and the load ptr of + // LoadPtr are related. + auto *II = dyn_cast(LoadBase); + if (II && II->isCJRefGCRead() && + Ptr == getUnderlyingObject(II->getArgOperand(0), 0)) + return false; + auto *AI = dyn_cast(Ptr); + if (II && II->isCJRefGCRead() && AI && + !isNoAliasAllocaValue(AI, cast(V), MSSA, AA, + II->getArgOperand(0))) + return false; + auto *LI = dyn_cast(LoadBase); + if (LI && Ptr == getUnderlyingObject(LI->getPointerOperand(), 0)) + return false; + if (LI && AI && + !isNoAliasAllocaValue(AI, cast(V), MSSA, AA, + LI->getPointerOperand())) + return false; + } else { + return false; + } + } + } else { + if (!Pred->getSingleSuccessor()) + return false; + } + } + return true; +} + /// Only sink or hoist an instruction if it is not a trapping instruction, /// or if the instruction is known not to trap when moved to the preheader. /// or if it is a trapping instruction and is guaranteed to execute. @@ -1741,9 +1957,13 @@ static bool isSafeToExecuteUnconditionally( Instruction &Inst, const DominatorTree *DT, const TargetLibraryInfo *TLI, const Loop *CurLoop, const LoopSafetyInfo *SafetyInfo, OptimizationRemarkEmitter *ORE, const Instruction *CtxI, - bool AllowSpeculation) { + bool AllowSpeculation, MemorySSA *MSSA, AAResults *AA) { if (AllowSpeculation && isSafeToSpeculativelyExecute(&Inst, CtxI, DT, TLI)) return true; + // Partial speculative execution, out-of-order execution loads, and implicit + // exceptions. + if (CJPipeline && cjPartialSpeculativeExecute(&Inst, CurLoop, MSSA, AA)) + return true; bool GuaranteedToExecute = SafetyInfo->isGuaranteedToExecute(Inst, DT, CurLoop); @@ -1765,6 +1985,7 @@ static bool isSafeToExecuteUnconditionally( namespace { class LoopPromoter : public LoadAndStorePromoter { Value *SomePtr; // Designated pointer to store to. + Value* BasePtr = nullptr; const SmallSetVector &PointerMustAliases; SmallVectorImpl &LoopExitBlocks; SmallVectorImpl &LoopInsertPts; @@ -1804,11 +2025,13 @@ public: SmallVectorImpl &MSSAIP, PredIteratorCache &PIC, MemorySSAUpdater &MSSAU, LoopInfo &li, DebugLoc dl, Align Alignment, bool UnorderedAtomic, const AAMDNodes &AATags, - ICFLoopSafetyInfo &SafetyInfo, bool CanInsertStoresInExitBlocks) - : LoadAndStorePromoter(Insts, S), SomePtr(SP), PointerMustAliases(PMA), - LoopExitBlocks(LEB), LoopInsertPts(LIP), MSSAInsertPts(MSSAIP), - PredCache(PIC), MSSAU(MSSAU), LI(li), DL(std::move(dl)), - Alignment(Alignment), UnorderedAtomic(UnorderedAtomic), AATags(AATags), + ICFLoopSafetyInfo &SafetyInfo, bool CanInsertStoresInExitBlocks, + Value *BP = nullptr) + : LoadAndStorePromoter(Insts, S), SomePtr(SP), BasePtr(BP), + PointerMustAliases(PMA), LoopExitBlocks(LEB), LoopInsertPts(LIP), + MSSAInsertPts(MSSAIP), PredCache(PIC), MSSAU(MSSAU), LI(li), + DL(std::move(dl)), Alignment(Alignment), + UnorderedAtomic(UnorderedAtomic), AATags(AATags), SafetyInfo(SafetyInfo), CanInsertStoresInExitBlocks(CanInsertStoresInExitBlocks) {} @@ -1817,6 +2040,9 @@ public: Value *Ptr; if (LoadInst *LI = dyn_cast(I)) Ptr = LI->getOperand(0); + else if (IntrinsicInst *II = dyn_cast(I); + II && II->getIntrinsicID() == Intrinsic::cj_gcwrite_ref) + Ptr = II->getOperand(2); else Ptr = cast(I)->getPointerOperand(); return PointerMustAliases.count(Ptr); @@ -1833,23 +2059,53 @@ public: LiveInValue = maybeInsertLCSSAPHI(LiveInValue, ExitBlock); Value *Ptr = maybeInsertLCSSAPHI(SomePtr, ExitBlock); Instruction *InsertPos = LoopInsertPts[i]; - StoreInst *NewSI = new StoreInst(LiveInValue, Ptr, InsertPos); - if (UnorderedAtomic) - NewSI->setOrdering(AtomicOrdering::Unordered); - NewSI->setAlignment(Alignment); - NewSI->setDebugLoc(DL); - if (AATags) - NewSI->setAAMetadata(AATags); MemoryAccess *MSSAInsertPoint = MSSAInsertPts[i]; - MemoryAccess *NewMemAcc; - if (!MSSAInsertPoint) { - NewMemAcc = MSSAU.createMemoryAccessInBB( - NewSI, nullptr, NewSI->getParent(), MemorySSA::Beginning); + MemoryAccess *NewMemAcc = nullptr; + if (!BasePtr) { + StoreInst *NewSI = new StoreInst(LiveInValue, Ptr, InsertPos); + if (UnorderedAtomic) + NewSI->setOrdering(AtomicOrdering::Unordered); + NewSI->setAlignment(Alignment); + NewSI->setDebugLoc(DL); + if (AATags) + NewSI->setAAMetadata(AATags); + if (!MSSAInsertPoint) { + NewMemAcc = MSSAU.createMemoryAccessInBB( + NewSI, nullptr, NewSI->getParent(), MemorySSA::Beginning); + } else { + NewMemAcc = + MSSAU.createMemoryAccessAfter(NewSI, nullptr, MSSAInsertPoint); + } } else { - NewMemAcc = - MSSAU.createMemoryAccessAfter(NewSI, nullptr, MSSAInsertPoint); + // We have only considered field pointers, but we need to ensure that + // base pointers also satisfy the LCSSA form. + Value *LCSSABase = maybeInsertLCSSAPHI(BasePtr, ExitBlock); + + Function *CJGCwrite = Intrinsic::getDeclaration( + ExitBlock->getModule(), Intrinsic::cj_gcwrite_ref); + Value *AS0ToAS1Ptr = Ptr; + if (!isGCPointerType(Ptr->getType())) { + Type *HeapObjPtr = LiveInValue->getType()->getPointerTo(1); + AS0ToAS1Ptr = CastInst::Create(Instruction::AddrSpaceCast, Ptr, + HeapObjPtr, "", InsertPos); + } + assert(isGCPointerType(BasePtr->getType()) && + "BasePtr should be gc pointer"); + CallInst *CI = CallInst::Create(CJGCwrite->getFunctionType(), CJGCwrite, + {LiveInValue, LCSSABase, AS0ToAS1Ptr}, + "", InsertPos); + CI->setDebugLoc(DL); + if (AATags) + CI->setAAMetadata(AATags); + if (!MSSAInsertPoint) + NewMemAcc = MSSAU.createMemoryAccessInBB(CI, nullptr, CI->getParent(), + MemorySSA::Beginning); + else + NewMemAcc = + MSSAU.createMemoryAccessAfter(CI, nullptr, MSSAInsertPoint); } + MSSAInsertPts[i] = NewMemAcc; MSSAU.insertDef(cast(NewMemAcc), true); // FIXME: true for safety, false may still be correct. @@ -1869,6 +2125,10 @@ public: bool shouldDelete(Instruction *I) const override { if (isa(I)) return CanInsertStoresInExitBlocks; + if (isa(I)) { + assert(cast(I)->getIntrinsicID() == Intrinsic::cj_gcwrite_ref); + return CanInsertStoresInExitBlocks; + } return true; } }; @@ -1904,18 +2164,20 @@ bool isNotVisibleOnUnwindInLoop(const Value *Object, const Loop *L, /// loop invariant. /// bool llvm::promoteLoopAccessesToScalars( - const SmallSetVector &PointerMustAliases, + const std::pair, Value *> + &PointerMustAliasesPair, SmallVectorImpl &ExitBlocks, SmallVectorImpl &InsertPts, SmallVectorImpl &MSSAInsertPts, PredIteratorCache &PIC, LoopInfo *LI, DominatorTree *DT, const TargetLibraryInfo *TLI, Loop *CurLoop, MemorySSAUpdater &MSSAU, ICFLoopSafetyInfo *SafetyInfo, - OptimizationRemarkEmitter *ORE, bool AllowSpeculation) { + OptimizationRemarkEmitter *ORE, bool AllowSpeculation, AAResults *AA) { // Verify inputs. assert(LI != nullptr && DT != nullptr && CurLoop != nullptr && SafetyInfo != nullptr && "Unexpected Input to promoteLoopAccessesToScalars"); + const auto &PointerMustAliases = PointerMustAliasesPair.first; Value *SomePtr = *PointerMustAliases.begin(); BasicBlock *Preheader = CurLoop->getLoopPreheader(); @@ -2018,7 +2280,8 @@ bool llvm::promoteLoopAccessesToScalars( if (!DereferenceableInPH || (InstAlignment > Alignment)) if (isSafeToExecuteUnconditionally( *Load, DT, TLI, CurLoop, SafetyInfo, ORE, - Preheader->getTerminator(), AllowSpeculation)) { + Preheader->getTerminator(), AllowSpeculation, + MSSAU.getMemorySSA(), AA)) { DereferenceableInPH = true; Alignment = std::max(Alignment, InstAlignment); } @@ -2069,6 +2332,40 @@ bool llvm::promoteLoopAccessesToScalars( Store->getPointerOperand(), Store->getValueOperand()->getType(), Store->getAlign(), MDL, Preheader->getTerminator(), DT, TLI); } + } else if (IntrinsicInst *II = dyn_cast(UI); + II && II->getIntrinsicID() == Intrinsic::cj_gcwrite_ref && + !LICMDisableBarrier) { + SawUnorderedAtomic |= false; + SawNotAtomic |= !SawUnorderedAtomic; + bool GuaranteedToExecute = + SafetyInfo->isGuaranteedToExecute(*UI, DT, CurLoop); + StoreIsGuanteedToExecute |= GuaranteedToExecute; + + // If Store is GuaranteedToExecute, then set DereferenceableInPH true. + // DereferenceableInPH indicates that it is now safe to hoist Load to + // Preheader. However, a Load instruction is created in Preheader only + // when Load requirements exist in the loop. + // Other Loads in the loop will be replaced by the phi instruction. + Align InstAlignment = MDL.getABITypeAlign(II->getOperand(0)->getType()); + if (!DereferenceableInPH || !SafeToInsertStore || + (InstAlignment > Alignment)) { + if (GuaranteedToExecute) { + DereferenceableInPH = true; + SafeToInsertStore = true; + Alignment = std::max(Alignment, InstAlignment); + } + } + + // Prove that p2 is safe through p2(b). + if (!SafeToInsertStore) + SafeToInsertStore = llvm::all_of(ExitBlocks, [&](BasicBlock *Exit) { + return DT->dominates(II->getParent(), Exit); + }); + + if (!DereferenceableInPH) + DereferenceableInPH = isDereferenceableAndAlignedPointer( + II->getOperand(2), II->getOperand(0)->getType(), InstAlignment, + MDL, Preheader->getTerminator(), DT, TLI); } else return false; // Not a load or store. @@ -2154,7 +2451,7 @@ bool llvm::promoteLoopAccessesToScalars( LoopPromoter Promoter(SomePtr, LoopUses, SSA, PointerMustAliases, ExitBlocks, InsertPts, MSSAInsertPts, PIC, MSSAU, *LI, DL, Alignment, SawUnorderedAtomic, AATags, *SafetyInfo, - SafeToInsertStore); + SafeToInsertStore, PointerMustAliasesPair.second); // Set up the preheader to have a definition of the value. It is the live-out // value from the preheader that uses in the loop will use. @@ -2203,7 +2500,7 @@ static void foreachMemoryAccess(MemorySSA *MSSA, Loop *L, Fn(MUD->getMemoryInst()); } -static SmallVector, 0> +static SmallVector collectPromotionCandidates(MemorySSA *MSSA, AliasAnalysis *AA, Loop *L) { AliasSetTracker AST(*AA); @@ -2212,6 +2509,9 @@ collectPromotionCandidates(MemorySSA *MSSA, AliasAnalysis *AA, Loop *L) { return L->isLoopInvariant(SI->getPointerOperand()); if (const auto *LI = dyn_cast(I)) return L->isLoopInvariant(LI->getPointerOperand()); + if (const auto *CB = dyn_cast(I)) + if (!LICMDisableBarrier && CB->getIntrinsicID() == Intrinsic::cj_gcwrite_ref) + return L->isLoopInvariant(CB->getOperand(2)); return false; }; @@ -2243,12 +2543,12 @@ collectPromotionCandidates(MemorySSA *MSSA, AliasAnalysis *AA, Loop *L) { }); }); - SmallVector, 0> Result; + SmallVector Result; for (const AliasSet *Set : Sets) { SmallSetVector PointerMustAliases; for (const auto &ASI : *Set) PointerMustAliases.insert(ASI.getValue()); - Result.push_back(std::move(PointerMustAliases)); + Result.push_back({std::move(PointerMustAliases), Set->getBasePtr()}); } return Result; diff --git a/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp b/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp index de5833f60..e5cf5f9d1 100644 --- a/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp +++ b/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp @@ -72,6 +72,10 @@ using namespace llvm; #define DEBUG_TYPE "loop-unroll" +namespace llvm { +extern cl::opt CJPipeline; +} + cl::opt llvm::ForgetSCEVInLoopUnroll( "forget-scev-loop-unroll", cl::init(false), cl::Hidden, cl::desc("Forget everything in SCEV when doing LoopUnroll, instead of just" @@ -239,6 +243,8 @@ TargetTransformInfo::UnrollingPreferences llvm::gatherUnrollingPreferences( UP.MaxCount = UnrollMaxCount; if (UnrollFullMaxCount.getNumOccurrences() > 0) UP.FullUnrollMaxCount = UnrollFullMaxCount; + if (CJPipeline) + UP.Partial = true; if (UnrollAllowPartial.getNumOccurrences() > 0) UP.Partial = UnrollAllowPartial; if (UnrollAllowRemainder.getNumOccurrences() > 0) diff --git a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp index 1f5bc69ac..731913aba 100644 --- a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp +++ b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp @@ -1094,12 +1094,17 @@ bool MemCpyOptPass::performCallSlotOptzn(Instruction *cpyLoad, // FIXME: MD_tbaa_struct and MD_mem_parallel_loop_access should also be // handled here, but combineMetadata doesn't support them yet unsigned KnownIDs[] = {LLVMContext::MD_tbaa, LLVMContext::MD_alias_scope, - LLVMContext::MD_noalias, + LLVMContext::MD_noalias, LLVMContext::MD_cj_agg, LLVMContext::MD_invariant_group, LLVMContext::MD_access_group}; + MDNode *MD = nullptr; + if (auto *II = dyn_cast(C); II && II->isCJStructGCRead()) + MD = C->getMetadata(LLVMContext::MD_cj_agg); combineMetadata(C, cpyLoad, KnownIDs, true); if (cpyLoad != cpyStore) combineMetadata(C, cpyStore, KnownIDs, true); + if (MD) + C->setMetadata(LLVMContext::MD_cj_agg, MD); ++NumCallSlot; return true; diff --git a/llvm/lib/Transforms/Scalar/PlaceSafepoints.cpp b/llvm/lib/Transforms/Scalar/PlaceSafepoints.cpp index e1cc3fc71..798487a6c 100644 --- a/llvm/lib/Transforms/Scalar/PlaceSafepoints.cpp +++ b/llvm/lib/Transforms/Scalar/PlaceSafepoints.cpp @@ -47,16 +47,20 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Transforms/Scalar/PlaceSafepoints.h" + #include "llvm/InitializePasses.h" #include "llvm/Pass.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/Statistic.h" +#include "llvm/ADT/Triple.h" #include "llvm/Analysis/CFG.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/ScalarEvolution.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/IR/Dominators.h" +#include "llvm/IR/IRBuilder.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Statepoint.h" @@ -84,10 +88,13 @@ using namespace llvm; static cl::opt AllBackedges("spp-all-backedges", cl::Hidden, cl::init(false)); +static cl::opt CJAggressiveSafepoint("cj-agg-safepoint", cl::Hidden, + cl::init(false)); + /// How narrow does the trip count of a loop have to be to have to be considered /// "counted"? Counted loops do not get safepoints at backedges. -static cl::opt CountedLoopTripWidth("spp-counted-loop-trip-width", - cl::Hidden, cl::init(32)); +cl::opt CountedLoopTripWidth("spp-counted-loop-trip-width", cl::Hidden, + cl::init(32)); // If true, split the backedge of a loop when placing the safepoint, otherwise // split the latch block itself. Both are useful to support for @@ -96,6 +103,16 @@ static cl::opt CountedLoopTripWidth("spp-counted-loop-trip-width", static cl::opt SplitBackedge("spp-split-backedge", cl::Hidden, cl::init(false)); +static cl::opt InvokeGC( + "insert-invokegc-before-safepoint", + llvm::cl::desc("Insert InvokeGC before safepoint to execute CJ GC"), + cl::Hidden, cl::init(false)); + +namespace llvm { +extern cl::opt CJPipeline; +extern cl::opt DisableGCSupport; +extern cl::opt EnableBarrierOnly; +} namespace { /// An analysis pass whose purpose is to identify each of the backedges in @@ -150,20 +167,88 @@ struct PlaceBackedgeSafepointsImpl : public FunctionPass { AU.setPreservesAll(); } }; -} +} // namespace static cl::opt NoEntry("spp-no-entry", cl::Hidden, cl::init(false)); static cl::opt NoCall("spp-no-call", cl::Hidden, cl::init(false)); static cl::opt NoBackedge("spp-no-backedge", cl::Hidden, cl::init(false)); namespace { -struct PlaceSafepoints : public FunctionPass { +const char *MCCYieldStr = "CJ_MCC_HandleSafepoint"; +const char *GCSafeStr = "gc.safepoint_poll"; +const char *InvokeGCStr = "CJ_MCC_InvokeGC"; +} // namespace + +static bool createGCSafepointPoll(Module &M) { + Function *GCSafePointFunc = M.getFunction(GCSafeStr); + if (GCSafePointFunc != nullptr) { // have gc.safepoint_poll in module + return false; + } + FunctionType *FuncType = + FunctionType::get(Type::getVoidTy(M.getContext()), false); + Function *MCCYieldFun = cast( + M.getOrInsertFunction(MCCYieldStr, FuncType).getCallee()); + MCCYieldFun->setUnnamedAddr(GlobalVariable::UnnamedAddr::Local); + if (!Triple(M.getTargetTriple()).isARM()) + MCCYieldFun->setCallingConv(CallingConv::CangjieGC); + MCCYieldFun->addFnAttr("gc-safepoint"); + Function *DoSafepointFun = + cast(M.getOrInsertFunction(GCSafeStr, FuncType).getCallee()); + BasicBlock *BB = BasicBlock::Create(M.getContext(), "entry", DoSafepointFun); + IRBuilder<> builder(BB); + CallInst *Call = builder.CreateCall(MCCYieldFun); + if (!Triple(M.getTargetTriple()).isARM()) + Call->setCallingConv(CallingConv::CangjieGC); + builder.CreateRetVoid(); + + GlobalVariable *CJFuncGV = cast(M.getOrInsertGlobal( + "CJ_MCC_HandleSafepoint.CJStubGV", MCCYieldFun->getType())); + CJFuncGV->setInitializer(MCCYieldFun); + CJFuncGV->setLinkage(GlobalVariable::InternalLinkage); + CJFuncGV->addAttribute("cj-native"); + return true; +} + +static void removeGCSafepointPoll(Module &M) { + auto *F = M.getFunction("gc.safepoint_poll"); + assert(F && "gc.safepoint_poll function is missing"); + // The PlaceSafepoints pass is complete, "gc.safepoint_poll" function + // is discarded now. + F->eraseFromParent(); +} + +bool placeSafepoint(Function &F, const TargetLibraryInfo &TLI); + +namespace { +class PlaceSafepointsLegacyPass : public ModulePass { +public: static char ID; // Pass identification, replacement for typeid - PlaceSafepoints() : FunctionPass(ID) { - initializePlaceSafepointsPass(*PassRegistry::getPassRegistry()); + PlaceSafepointsLegacyPass() : ModulePass(ID) { + initializePlaceSafepointsLegacyPassPass(*PassRegistry::getPassRegistry()); + } + ~PlaceSafepointsLegacyPass() = default; + + bool runOnModule(Module &M) override { + bool Changed = false; + + if (DisableGCSupport || EnableBarrierOnly) { + return Changed; + } + + // create gc.safepoint_poll for PlaceSafepoints pass. + bool IsCreated = createGCSafepointPoll(M); + for (auto &F : M) { + const TargetLibraryInfo &TLI = + getAnalysis().getTLI(F); + Changed |= placeSafepoint(F, TLI); + } + if (IsCreated) { + removeGCSafepointPoll(M); + Changed = true; + } + return Changed; } - bool runOnFunction(Function &F) override; void getAnalysisUsage(AnalysisUsage &AU) const override { // We modify the graph wholesale (inlining, block insertion, etc). We @@ -172,7 +257,7 @@ struct PlaceSafepoints : public FunctionPass { AU.addRequired(); } }; -} +} // namespace // Insert a safepoint poll immediately before the given instruction. Does // not handle the parsability of state at the runtime call, that's the @@ -182,6 +267,14 @@ InsertSafepointPoll(Instruction *InsertBefore, std::vector &ParsePointsNeeded /*rval*/, const TargetLibraryInfo &TLI); +static bool isCangjieFunc(const Function *F) { + if (F != nullptr) { + return F->hasCangjieGC(); + } else { + return false; + } +} + static bool needsStatepoint(CallBase *Call, const TargetLibraryInfo &TLI) { if (callsGCLeafFunction(Call, TLI)) return false; @@ -189,7 +282,12 @@ static bool needsStatepoint(CallBase *Call, const TargetLibraryInfo &TLI) { if (CI->isInlineAsm()) return false; } - + if (CJPipeline && !isCangjieFunc(Call->getCalledFunction())) { + // Return false, which means that the safepoint will not be inserted. + // We assume that the safepoint is inserted only in the Cangjie function, + // and other functions(e.g: std and runtime functions) do not. + return false; + } return !(isa(Call) || isa(Call) || isa(Call)); } @@ -224,7 +322,9 @@ static bool containsUnconditionalCallSafepoint(Loop *L, BasicBlock *Header, // unconditional poll. In practice, this is only a theoretical concern // since we don't have any methods with conditional-only safepoint // polls. - if (needsStatepoint(Call, TLI)) + if (needsStatepoint(Call, TLI) && + !Call->getCalledFunction()->isDeclaration() && + !Call->getCalledFunction()->hasFnAttribute("cj_fast_call")) return true; } @@ -236,6 +336,71 @@ static bool containsUnconditionalCallSafepoint(Loop *L, BasicBlock *Header, return false; } +static bool mustBeCJFiniteCountedLoop(Loop *L, ScalarEvolution *SE, + BasicBlock *Pred, APInt &LoopCount) { + APInt MaxSubLoopCount(64, 1); + for (const auto &SubL : L->getSubLoops()) { + SmallVector LoopLatches; + SubL->getLoopLatches(LoopLatches); + for (BasicBlock *SubPred : LoopLatches) { + assert(SubL->contains(SubPred)); + APInt SubLoopCount(64, 1); + if (!mustBeCJFiniteCountedLoop(SubL, SE, SubPred, SubLoopCount)) { + return false; + } + if (SubLoopCount.getZExtValue() > MaxSubLoopCount.getZExtValue()) { + MaxSubLoopCount = SubLoopCount; + } + } + } + + const SCEV *MaxTrips = SE->getConstantMaxBackedgeTakenCount(L); + if (!isa(MaxTrips)) { + APInt LCount = SE->getUnsignedRange(MaxTrips).getUnsignedMax(); + APInt SumCount(64, LCount.getZExtValue() * MaxSubLoopCount.getZExtValue()); + if (SumCount.isIntN(CountedLoopTripWidth)) { + LoopCount = SumCount; + return true; + } + } + + if (L->isLoopExiting(Pred)) { + const SCEV *MaxExec = SE->getExitCount(L, Pred); + if (!isa(MaxExec)) { + APInt LCount = SE->getUnsignedRange(MaxExec).getUnsignedMax(); + APInt SumCount(64, LCount.getZExtValue() * MaxSubLoopCount.getZExtValue()); + if (SumCount.isIntN(CountedLoopTripWidth)) { + LoopCount = SumCount; + return true; + } + } + if (!CJAggressiveSafepoint) + return false; + BranchInst *BI = dyn_cast(Pred->getTerminator()); + if (!BI) { + return false; + } + MDNode *MD = BI->getMetadata(LLVMContext::MD_prof); + if (MD) { + BasicBlock *TrueBB = BI->getSuccessor(0); + uint32_t TrueInt = + mdconst::extract(MD->getOperand(1))->getZExtValue(); + uint32_t FalseInt = + mdconst::extract(MD->getOperand(2))->getZExtValue(); + // 1000: When average of loop times don't reach 1000, we will reduce + // safepoint on backedges. + if (L->contains(TrueBB)) { + if ((TrueInt / FalseInt) < 1000) + return true; + } else if ((FalseInt / TrueInt) < 1000) { + return true; + } + } + } + + return /* not finite */ false; +} + /// Returns true if this loop is known to terminate in a finite number of /// iterations. Note that this function may return false for a loop which /// does actual terminate in a finite constant number of iterations due to @@ -322,10 +487,19 @@ bool PlaceBackedgeSafepointsImpl::runOnLoop(Loop *L) { // not. Note that this is about unburdening the optimizer in loops, not // avoiding the runtime cost of the actual safepoint. if (!AllBackedges) { - if (mustBeFiniteCountedLoop(L, SE, Pred)) { - LLVM_DEBUG(dbgs() << "skipping safepoint placement in finite loop\n"); - FiniteExecution++; - continue; + APInt LoopCount(64, 1); + if (CJPipeline) { + if (mustBeCJFiniteCountedLoop(L, SE, Pred, LoopCount)) { + LLVM_DEBUG(dbgs() << "skipping safepoint placement in finite loop\n"); + FiniteExecution++; + continue; + } + } else { + if (mustBeFiniteCountedLoop(L, SE, Pred)) { + LLVM_DEBUG(dbgs() << "skipping safepoint placement in finite loop\n"); + FiniteExecution++; + continue; + } } if (CallSafepointsEnabled && containsUnconditionalCallSafepoint(L, Header, Pred, *DT, *TLI)) { @@ -363,9 +537,14 @@ bool PlaceBackedgeSafepointsImpl::runOnLoop(Loop *L) { static bool doesNotRequireEntrySafepointBefore(CallBase *Call) { if (IntrinsicInst *II = dyn_cast(Call)) { switch (II->getIntrinsicID()) { + case Intrinsic::cj_gc_statepoint: case Intrinsic::experimental_gc_statepoint: case Intrinsic::experimental_patchpoint_void: case Intrinsic::experimental_patchpoint_i64: + case Intrinsic::dbg_declare: + case Intrinsic::dbg_value: + case Intrinsic::dbg_addr: + case Intrinsic::dbg_label: // The can wrap an actual call which may grow the stack by an unbounded // amount or run forever. return false; @@ -380,6 +559,11 @@ static bool doesNotRequireEntrySafepointBefore(CallBase *Call) { return true; } } + + Function *Callee = Call->getCalledFunction(); + if (Callee && Callee->isCangjieStackCheck()) + return true; + return false; } @@ -451,6 +635,11 @@ static bool shouldRewriteFunction(Function &F) { const auto &FunctionGCName = F.getGC(); const StringRef StatepointExampleName("statepoint-example"); const StringRef CoreCLRName("coreclr"); + const StringRef CangjieName("cangjie"); + if (CangjieName == FunctionGCName) { + return !F.hasFnAttribute("gc-leaf-function") && + !F.hasFnAttribute("cj_fast_call"); + } return (StatepointExampleName == FunctionGCName) || (CoreCLRName == FunctionGCName); } else @@ -463,7 +652,24 @@ static bool enableEntrySafepoints(Function &F) { return !NoEntry; } static bool enableBackedgeSafepoints(Function &F) { return !NoBackedge; } static bool enableCallSafepoints(Function &F) { return !NoCall; } -bool PlaceSafepoints::runOnFunction(Function &F) { +static void InsertFunctionCheck(Instruction *InsertBefore, + const char *FunctionStr) { + Module *M = InsertBefore->getModule(); + assert(M && "must be part of a module"); + + Function *Func = M->getFunction(FunctionStr); + if (Func == nullptr) { + FunctionType *FuncType = + FunctionType::get(Type::getVoidTy(M->getContext()), false); + Func = cast( + M->getOrInsertFunction(FunctionStr, FuncType).getCallee()); + } + + assert(Func && FunctionStr && "function is missing"); + CallInst::Create(Func->getFunctionType(), Func, "", InsertBefore); +} + +bool placeSafepoint(Function &F, const TargetLibraryInfo &TLI) { if (F.isDeclaration() || F.empty()) { // This is a declaration, nothing to do. Must exit early to avoid crash in // dom tree calculation @@ -480,9 +686,6 @@ bool PlaceSafepoints::runOnFunction(Function &F) { if (!shouldRewriteFunction(F)) return false; - const TargetLibraryInfo &TLI = - getAnalysis().getTLI(F); - bool Modified = false; // In various bits below, we rely on the fact that uses are reachable from @@ -588,6 +791,9 @@ bool PlaceSafepoints::runOnFunction(Function &F) { // Now that we've identified all the needed safepoint poll locations, insert // safepoint polls themselves. for (Instruction *PollLocation : PollsNeeded) { + if (InvokeGC) { + InsertFunctionCheck(PollLocation, InvokeGCStr); + } std::vector RuntimeCalls; InsertSafepointPoll(PollLocation, RuntimeCalls, TLI); llvm::append_range(ParsePointNeeded, RuntimeCalls); @@ -596,11 +802,38 @@ bool PlaceSafepoints::runOnFunction(Function &F) { return Modified; } +PreservedAnalyses PlaceSafepoints::run(Module &M, + ModuleAnalysisManager &AM) const { + if (DisableGCSupport || EnableBarrierOnly) + return PreservedAnalyses::all(); + + bool Changed = false; + + // create gc.safepoint_poll for PlaceSafepoints pass. + bool IsCreated = createGCSafepointPoll(M); + for (auto &F : M) { + auto &FAM = + AM.getResult(M).getManager(); + auto &TLI = FAM.getResult(F); + Changed |= placeSafepoint(F, TLI); + } + if (IsCreated) { + removeGCSafepointPoll(M); + Changed = true; + } + + if (Changed) { + return PreservedAnalyses::none(); + } + + return PreservedAnalyses::all(); +} + char PlaceBackedgeSafepointsImpl::ID = 0; -char PlaceSafepoints::ID = 0; +char PlaceSafepointsLegacyPass::ID = 0; -FunctionPass *llvm::createPlaceSafepointsPass() { - return new PlaceSafepoints(); +ModulePass *llvm::createPlaceSafepointsLegacyPass() { + return new PlaceSafepointsLegacyPass(); } INITIALIZE_PASS_BEGIN(PlaceBackedgeSafepointsImpl, @@ -613,10 +846,11 @@ INITIALIZE_PASS_END(PlaceBackedgeSafepointsImpl, "place-backedge-safepoints-impl", "Place Backedge Safepoints", false, false) -INITIALIZE_PASS_BEGIN(PlaceSafepoints, "place-safepoints", "Place Safepoints", - false, false) -INITIALIZE_PASS_END(PlaceSafepoints, "place-safepoints", "Place Safepoints", - false, false) +INITIALIZE_PASS_BEGIN(PlaceSafepointsLegacyPass, "place-safepoints", + "Place Safepoints", false, false) +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) +INITIALIZE_PASS_END(PlaceSafepointsLegacyPass, "place-safepoints", + "Place Safepoints", false, false) static void InsertSafepointPoll(Instruction *InsertBefore, diff --git a/llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp b/llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp index baf407c50..0aa2889e8 100644 --- a/llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp +++ b/llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp @@ -50,6 +50,7 @@ #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" #include "llvm/IR/Statepoint.h" +#include "llvm/IR/SafepointIRVerifier.h" #include "llvm/IR/Type.h" #include "llvm/IR/User.h" #include "llvm/IR/Value.h" @@ -319,45 +320,7 @@ static void findLiveSetAtInst(Instruction *inst, GCPtrLivenessData &Data, // TODO: Once we can get to the GCStrategy, this becomes // Optional isGCManagedPointer(const Type *Ty) const override { -static bool isGCPointerType(Type *T) { - if (auto *PT = dyn_cast(T)) - // For the sake of this example GC, we arbitrarily pick addrspace(1) as our - // GC managed heap. We know that a pointer into this heap needs to be - // updated and that no other pointer does. - return PT->getAddressSpace() == 1; - return false; -} - -// Return true if this type is one which a) is a gc pointer or contains a GC -// pointer and b) is of a type this code expects to encounter as a live value. -// (The insertion code will assert that a type which matches (a) and not (b) -// is not encountered.) -static bool isHandledGCPointerType(Type *T) { - // We fully support gc pointers - if (isGCPointerType(T)) - return true; - // We partially support vectors of gc pointers. The code will assert if it - // can't handle something. - if (auto VT = dyn_cast(T)) - if (isGCPointerType(VT->getElementType())) - return true; - return false; -} - #ifndef NDEBUG -/// Returns true if this type contains a gc pointer whether we know how to -/// handle that type or not. -static bool containsGCPtrType(Type *Ty) { - if (isGCPointerType(Ty)) - return true; - if (VectorType *VT = dyn_cast(Ty)) - return isGCPointerType(VT->getScalarType()); - if (ArrayType *AT = dyn_cast(Ty)) - return containsGCPtrType(AT->getElementType()); - if (StructType *ST = dyn_cast(Ty)) - return llvm::any_of(ST->elements(), containsGCPtrType); - return false; -} // Returns true if this is a type which a) is a gc pointer or contains a GC // pointer and b) is of a type which the code doesn't expect (i.e. first class diff --git a/llvm/lib/Transforms/Scalar/SCCP.cpp b/llvm/lib/Transforms/Scalar/SCCP.cpp index 2282ef636..639003664 100644 --- a/llvm/lib/Transforms/Scalar/SCCP.cpp +++ b/llvm/lib/Transforms/Scalar/SCCP.cpp @@ -74,6 +74,10 @@ STATISTIC( IPNumInstReplaced, "Number of instructions replaced with (simpler) instruction by IPSCCP"); +namespace llvm { +extern cl::opt CJPipeline; +} // namespace llvm + // Helper to check if \p LV is either a constant or a constant // range with a single element. This should cover exactly the same cases as the // old ValueLatticeElement::isConstant() and is intended to be used in the @@ -149,6 +153,14 @@ static bool tryToReplaceWithConstant(SCCPSolver &Solver, Value *V) { LLVM_DEBUG(dbgs() << " Constant: " << *Const << " = " << *V << '\n'); + // Cangjie does not support the undef type, this part is skiped to avoid + // generate the undef type during optimization. + if (CJPipeline) { + if (Const->getValueID() == Constant::UndefValueVal) { + return false; + } + } + // Replaces all of the uses of a variable with uses of the constant. V->replaceAllUsesWith(Const); return true; @@ -197,7 +209,8 @@ static bool removeNonFeasibleEdges(const SCCPSolver &Solver, BasicBlock *BB, // runSCCP() - Run the Sparse Conditional Constant Propagation algorithm, // and return true if the function was modified. static bool runSCCP(Function &F, const DataLayout &DL, - const TargetLibraryInfo *TLI, DomTreeUpdater &DTU) { + const TargetLibraryInfo *TLI, DomTreeUpdater &DTU, + LoopInfo *LI) { LLVM_DEBUG(dbgs() << "SCCP on function '" << F.getName() << "'\n"); SCCPSolver Solver( DL, [TLI](Function &F) -> const TargetLibraryInfo & { return *TLI; }, @@ -205,7 +218,7 @@ static bool runSCCP(Function &F, const DataLayout &DL, // Mark the first block of the function as being executable. Solver.markBlockExecutable(&F.front()); - + Solver.addAnalysis(F, {nullptr, &DTU.getDomTree(), nullptr, LI}); // Mark all arguments to the function as being overdefined. for (Argument &AI : F.args()) Solver.markOverdefined(&AI); @@ -259,8 +272,11 @@ PreservedAnalyses SCCPPass::run(Function &F, FunctionAnalysisManager &AM) { const DataLayout &DL = F.getParent()->getDataLayout(); auto &TLI = AM.getResult(F); auto *DT = AM.getCachedResult(F); + if (!DT) + DT = &AM.getResult(F); DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Lazy); - if (!runSCCP(F, DL, &TLI, DTU)) + if (!runSCCP(F, DL, &TLI, DTU, + CJPipeline ? &AM.getResult(F) : nullptr)) return PreservedAnalyses::all(); auto PA = PreservedAnalyses(); @@ -301,7 +317,10 @@ public: auto *DTWP = getAnalysisIfAvailable(); DomTreeUpdater DTU(DTWP ? &DTWP->getDomTree() : nullptr, DomTreeUpdater::UpdateStrategy::Lazy); - return runSCCP(F, DL, TLI, DTU); + return runSCCP(F, DL, TLI, DTU, + CJPipeline + ? &getAnalysis().getLoopInfo() + : nullptr); } }; @@ -707,6 +726,10 @@ bool llvm::runIPSCCP( // delete the global and any stores that remain to it. for (auto &I : make_early_inc_range(Solver.getTrackedGlobals())) { GlobalVariable *GV = I.first; + // Cangjie native GV needs to be processed at the backend.. + if (GV->hasAttribute("cj-native")) { + continue; + } if (isOverdefined(I.second)) continue; LLVM_DEBUG(dbgs() << "Found that GV '" << GV->getName() diff --git a/llvm/lib/Transforms/Scalar/SROA.cpp b/llvm/lib/Transforms/Scalar/SROA.cpp index 644c5c82e..9453ef48a 100644 --- a/llvm/lib/Transforms/Scalar/SROA.cpp +++ b/llvm/lib/Transforms/Scalar/SROA.cpp @@ -65,6 +65,7 @@ #include "llvm/IR/Module.h" #include "llvm/IR/Operator.h" #include "llvm/IR/PassManager.h" +#include "llvm/IR/SafepointIRVerifier.h" #include "llvm/IR/Type.h" #include "llvm/IR/Use.h" #include "llvm/IR/User.h" @@ -78,6 +79,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Scalar/InsertCJTBAA.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/PromoteMemToReg.h" #include @@ -106,14 +108,19 @@ STATISTIC(NumPromoted, "Number of allocas promoted to SSA values"); STATISTIC(NumLoadsSpeculated, "Number of loads speculated to allow promotion"); STATISTIC(NumDeleted, "Number of instructions deleted"); STATISTIC(NumVectorized, "Number of vectorized aggregates"); - +namespace llvm { +extern cl::opt CJPipeline; +} /// Hidden option to experiment with completely strict handling of inbounds /// GEPs. static cl::opt SROAStrictInbounds("sroa-strict-inbounds", cl::init(false), cl::Hidden); namespace { - +/// 8: default vector size. +static SmallVector GCPtrOffsets; +/// Pointer size of current target. +static size_t PointerSize = 0; /// A custom IRBuilder inserter which prefixes all names, but only in /// Assert builds. class IRBuilderPrefixedInserter final : public IRBuilderDefaultInserter { @@ -412,6 +419,18 @@ public: ArrayRef splitSliceTails() const { return SplitTails; } }; +/// Try to find the first GC Pointer offset in given range. +/// If found, return true. +/// If not, return false. +static bool containGCPtr(uint64_t BeginOffset, uint64_t EndOffset) { + for (auto GCOffset : GCPtrOffsets) { + if (GCOffset >= BeginOffset && GCOffset < EndOffset) { + return true; + } + } + return false; +} + /// An iterator over partitions of the alloca's slices. /// /// This iterator implements the core algorithm for partitioning the alloca's @@ -437,6 +456,15 @@ class AllocaSlices::partition_iterator /// FIXME: Do we really? uint64_t MaxSplitSliceEndOffset = 0; + /// We need to keep track of the EndOffset in split unalignment. + uint64_t NextEndOffset = 0; + + /// We need to keep track of the SI iterator in split unalignment. + AllocaSlices::iterator NextSJ = SE; + + /// We need to mark whether it is in the process of split unalignment. + bool NeedContinue = false; + /// Sets the partition to be empty at given iterator, and sets the /// end iterator. partition_iterator(AllocaSlices::iterator SI, AllocaSlices::iterator SE) @@ -447,6 +475,33 @@ class AllocaSlices::partition_iterator advance(); } + /// If the start offset of partition is not alignment, + /// we need to split it for record stackmaps. + void splitUnAlginment() { + assert(PointerSize > 0 && "Unexpected PointerSize!"); + if (!CJPipeline || // Only for Cangjie + P.EndOffset - P.BeginOffset <= PointerSize || // Less than pointer size + P.BeginOffset % PointerSize == 0 || // Already alignment + !containGCPtr(P.BeginOffset, P.EndOffset)) // No GC pointer + return; + // Record original end offset. + NextEndOffset = P.EndOffset; + // Split current P by align the beginOffset. + P.EndOffset = ((P.BeginOffset + PointerSize - 1) & (~(PointerSize - 1))); + // Record original SJ. + NextSJ = P.SJ; + // Keep the SI and SJ within the range of P + for (Slice &S : P) { + if (S.beginOffset() >= P.EndOffset) { + P.SJ = &S; + break; + } + } + // Need to process remained part in next advance(). + NeedContinue = true; + return; + } + /// Advance the iterator to the next partition. /// /// Requires that the iterator not be at the end of the slices. @@ -454,6 +509,25 @@ class AllocaSlices::partition_iterator assert((P.SI != SE || !P.SplitTails.empty()) && "Cannot advance past the end of the slices!"); + if (NeedContinue) { + P.BeginOffset = P.EndOffset; + P.EndOffset = NextEndOffset; + // If SJ == NextSJ, it means that SI, SJ have not changed in + // splitUnAlginment(), so there is no need to modify the value + // of SI and SJ. + if (P.SJ != NextSJ) { + P.SI = P.SJ; + P.SJ = NextSJ; + } + // Reset the variables. + NextEndOffset = 0; + NeedContinue = false; + NextSJ = SE; + // Since this is the second part of the split unalignment, + // we need to return directly. + return; + } + // Clear out any split uses which have ended. if (!P.SplitTails.empty()) { if (P.EndOffset >= MaxSplitSliceEndOffset) { @@ -505,6 +579,7 @@ class AllocaSlices::partition_iterator if (P.SI == SE) { P.BeginOffset = P.EndOffset; P.EndOffset = MaxSplitSliceEndOffset; + splitUnAlginment(); return; } @@ -515,6 +590,7 @@ class AllocaSlices::partition_iterator !P.SI->isSplittable()) { P.BeginOffset = P.EndOffset; P.EndOffset = P.SI->beginOffset(); + splitUnAlginment(); return; } } @@ -567,6 +643,8 @@ class AllocaSlices::partition_iterator assert(!P.SJ->isSplittable()); P.EndOffset = P.SJ->beginOffset(); } + + splitUnAlginment(); } public: @@ -842,8 +920,9 @@ private: if (II.isVolatile() && II.getDestAddressSpace() != DL.getAllocaAddrSpace()) return PI.setAborted(&II); - insertUse(II, Offset, Length ? Length->getLimitedValue() - : AllocSize - Offset.getLimitedValue(), + insertUse(II, Offset, + Length ? Length->getLimitedValue() + : AllocSize - Offset.getLimitedValue(), (bool)Length); } @@ -971,16 +1050,16 @@ private: std::tie(UsedI, I) = Uses.pop_back_val(); if (LoadInst *LI = dyn_cast(I)) { - Size = std::max(Size, - DL.getTypeStoreSize(LI->getType()).getFixedSize()); + Size = + std::max(Size, DL.getTypeStoreSize(LI->getType()).getFixedSize()); continue; } if (StoreInst *SI = dyn_cast(I)) { Value *Op = SI->getOperand(0); if (Op == UsedI) return SI; - Size = std::max(Size, - DL.getTypeStoreSize(Op->getType()).getFixedSize()); + Size = + std::max(Size, DL.getTypeStoreSize(Op->getType()).getFixedSize()); continue; } @@ -1211,7 +1290,20 @@ static bool isSafePHIToSpeculate(PHINode &PN) { Align MaxAlign; uint64_t APWidth = DL.getIndexTypeSizeInBits(PN.getType()); Type *LoadType = nullptr; + SmallVector PNUsers; for (User *U : PN.users()) { + PNUsers.push_back(U); + } + while (!PNUsers.empty()) { + User *U = PNUsers.pop_back_val(); + if (CJPipeline) { + if (auto *CI = dyn_cast(U)) { + for (User *CIU : CI->users()) { + PNUsers.push_back(CIU); + } + continue; + } + } LoadInst *LI = dyn_cast(U); if (!LI || !LI->isSimple()) return false; @@ -1273,10 +1365,37 @@ static bool isSafePHIToSpeculate(PHINode &PN) { return true; } +static void getSomeLoadsAndCasts(SmallVectorImpl &SomeLoads, + SmallVectorImpl &SomeCasts, + PHINode &PN) { + SmallVector PNUsers; + for (User *U : PN.users()) { + PNUsers.push_back(U); + } + while (!PNUsers.empty()) { + User *U = PNUsers.pop_back_val(); + if (CastInst *CI = dyn_cast(U)) { + for (User *CIU : CI->users()) { + PNUsers.push_back(CIU); + } + SomeCasts.push_back(CI); + } else if (LoadInst *LI = dyn_cast(U)) { + SomeLoads.push_back(LI); + } else { + report_fatal_error("unsupported type on speculatePHINodeLoads."); + } + } +} + static void speculatePHINodeLoads(IRBuilderTy &IRB, PHINode &PN) { LLVM_DEBUG(dbgs() << " original: " << PN << "\n"); - LoadInst *SomeLoad = cast(PN.user_back()); + SmallVector SomeLoads; + SmallVector SomeCasts; + getSomeLoadsAndCasts(SomeLoads, SomeCasts, PN); + assert(SomeLoads.size() != 0 && "don't hanve any load Inst."); + LoadInst *SomeLoad = SomeLoads[0]; + Type *LoadTy = SomeLoad->getType(); IRB.SetInsertPoint(&PN); PHINode *NewPN = IRB.CreatePHI(LoadTy, PN.getNumIncomingValues(), @@ -1288,14 +1407,19 @@ static void speculatePHINodeLoads(IRBuilderTy &IRB, PHINode &PN) { Align Alignment = SomeLoad->getAlign(); // Rewrite all loads of the PN to use the new PHI. - while (!PN.use_empty()) { - LoadInst *LI = cast(PN.user_back()); + for (unsigned Index = 0; Index < SomeLoads.size(); Index++) { + LoadInst *LI = SomeLoads[Index]; LI->replaceAllUsesWith(NewPN); LI->eraseFromParent(); } + while (!SomeCasts.empty()) { + CastInst *Cast = SomeCasts.pop_back_val(); + Cast->eraseFromParent(); + } + // Inject loads into all of the pred blocks. - DenseMap InjectedLoads; + DenseMap InjectedLoads; for (unsigned Idx = 0, Num = PN.getNumIncomingValues(); Idx != Num; ++Idx) { BasicBlock *Pred = PN.getIncomingBlock(Idx); Value *InVal = PN.getIncomingValue(Idx); @@ -1304,7 +1428,7 @@ static void speculatePHINodeLoads(IRBuilderTy &IRB, PHINode &PN) { // basic block, as long as the value is the same. So if we already injected // a load in the predecessor, then we should reuse the same load for all // duplicated entries. - if (Value* V = InjectedLoads.lookup(Pred)) { + if (Value *V = InjectedLoads.lookup(Pred)) { NewPN->addIncoming(V, Pred); continue; } @@ -1318,6 +1442,8 @@ static void speculatePHINodeLoads(IRBuilderTy &IRB, PHINode &PN) { ++NumLoadsSpeculated; if (AATags) Load->setAAMetadata(AATags); + if (CJPipeline) + updateTBAA(Load->getModule()->getDataLayout(), Load); NewPN->addIncoming(Load, Pred); InjectedLoads[Pred] = Load; } @@ -1359,11 +1485,11 @@ static bool isSafeSelectToSpeculate(SelectInst &SI) { // Both operands to the select need to be dereferenceable, either // absolutely (e.g. allocas) or at this point because we can see other // accesses to it. - if (!isSafeToLoadUnconditionally(TValue, LI->getType(), - LI->getAlign(), DL, LI)) + if (!isSafeToLoadUnconditionally(TValue, LI->getType(), LI->getAlign(), DL, + LI)) return false; - if (!isSafeToLoadUnconditionally(FValue, LI->getType(), - LI->getAlign(), DL, LI)) + if (!isSafeToLoadUnconditionally(FValue, LI->getType(), LI->getAlign(), DL, + LI)) return false; } @@ -1650,8 +1776,7 @@ static Value *getAdjustedPtr(IRBuilderTy &IRB, const DataLayout &DL, Value *Ptr, // On the off chance we were targeting i8*, guard the bitcast here. if (cast(Ptr->getType()) != TargetPtrTy) { - Ptr = IRB.CreatePointerBitCastOrAddrSpaceCast(Ptr, - TargetPtrTy, + Ptr = IRB.CreatePointerBitCastOrAddrSpaceCast(Ptr, TargetPtrTy, NamePrefix + "sroa_cast"); } @@ -1793,8 +1918,7 @@ static bool isVectorPromotionViableForSlice(Partition &P, const Slice &S, if (BeginIndex * ElementSize != BeginOffset || BeginIndex >= cast(Ty)->getNumElements()) return false; - uint64_t EndOffset = - std::min(S.endOffset(), P.endOffset()) - P.beginOffset(); + uint64_t EndOffset = std::min(S.endOffset(), P.endOffset()) - P.beginOffset(); uint64_t EndIndex = EndOffset / ElementSize; if (EndIndex * ElementSize != EndOffset || EndIndex > cast(Ty)->getNumElements()) @@ -1887,8 +2011,7 @@ static VectorType *isVectorPromotionViable(Partition &P, const DataLayout &DL) { }; // Consider any loads or stores that are the exact size of the slice. for (const Slice &S : P) - if (S.beginOffset() == P.beginOffset() && - S.endOffset() == P.endOffset()) { + if (S.beginOffset() == P.beginOffset() && S.endOffset() == P.endOffset()) { if (auto *LI = dyn_cast(S.getUse()->getUser())) CheckCandidateType(LI->getType()); else if (auto *SI = dyn_cast(S.getUse()->getUser())) @@ -2360,8 +2483,8 @@ public: Instruction *OldUserI = cast(OldUse->getUser()); IRB.SetInsertPoint(OldUserI); IRB.SetCurrentDebugLocation(OldUserI->getDebugLoc()); - IRB.getInserter().SetNamePrefix( - Twine(NewAI.getName()) + "." + Twine(BeginOffset) + "."); + IRB.getInserter().SetNamePrefix(Twine(NewAI.getName()) + "." + + Twine(BeginOffset) + "."); CanSROA &= visit(cast(OldUse->getUser())); if (VecTy || IntTy) @@ -2414,7 +2537,7 @@ private: #else Twine() #endif - ); + ); } /// Compute suitable alignment to access this slice of the *new* @@ -2508,6 +2631,9 @@ private: LI.getName()); if (AATags) NewLI->setAAMetadata(AATags.shift(NewBeginOffset - BeginOffset)); + if (CJPipeline) + updateTBAA(DL, NewLI); + if (LI.isVolatile()) NewLI->setAtomic(LI.getOrdering(), LI.getSyncScopeID()); if (NewLI->isAtomic()) @@ -2547,6 +2673,8 @@ private: getSliceAlign(), LI.isVolatile(), LI.getName()); if (AATags) NewLI->setAAMetadata(AATags.shift(NewBeginOffset - BeginOffset)); + if (CJPipeline) + updateTBAA(DL, NewLI); if (LI.isVolatile()) NewLI->setAtomic(LI.getOrdering(), LI.getSyncScopeID()); NewLI->copyMetadata(LI, {LLVMContext::MD_mem_parallel_loop_access, @@ -2614,6 +2742,8 @@ private: LLVMContext::MD_access_group}); if (AATags) Store->setAAMetadata(AATags.shift(NewBeginOffset - BeginOffset)); + if (CJPipeline) + updateTBAA(DL, Store); Pass.DeadInsts.push_back(&SI); LLVM_DEBUG(dbgs() << " to: " << *Store << "\n"); @@ -2638,6 +2768,8 @@ private: LLVMContext::MD_access_group}); if (AATags) Store->setAAMetadata(AATags.shift(NewBeginOffset - BeginOffset)); + if (CJPipeline) + updateTBAA(DL, Store); Pass.DeadInsts.push_back(&SI); LLVM_DEBUG(dbgs() << " to: " << *Store << "\n"); return true; @@ -2706,6 +2838,8 @@ private: LLVMContext::MD_access_group}); if (AATags) NewSI->setAAMetadata(AATags.shift(NewBeginOffset - BeginOffset)); + if (CJPipeline) + updateTBAA(DL, NewSI); if (SI.isVolatile()) NewSI->setAtomic(SI.getOrdering(), SI.getSyncScopeID()); if (NewSI->isAtomic()) @@ -2779,8 +2913,7 @@ private: const bool CanContinue = [&]() { if (VecTy || IntTy) return true; - if (BeginOffset > NewAllocaBeginOffset || - EndOffset < NewAllocaEndOffset) + if (BeginOffset > NewAllocaBeginOffset || EndOffset < NewAllocaEndOffset) return false; // Length must be in range for FixedVectorType. auto *C = cast(II.getLength()); @@ -2793,14 +2926,20 @@ private: DL.isLegalInteger(DL.getTypeSizeInBits(ScalarTy).getFixedSize()); }(); + bool NeedCJMemset = containGCPtr(NewBeginOffset, NewEndOffset); // If this doesn't map cleanly onto the alloca type, and that type isn't // a single value type, just emit a memset. if (!CanContinue) { Type *SizeTy = II.getLength()->getType(); Constant *Size = ConstantInt::get(SizeTy, NewEndOffset - NewBeginOffset); - CallInst *New = IRB.CreateMemSet( - getNewAllocaSlicePtr(IRB, OldPtr->getType()), II.getValue(), Size, - MaybeAlign(getSliceAlign()), II.isVolatile()); + CallInst *New = + NeedCJMemset + ? IRB.CreateCJMemSet(getNewAllocaSlicePtr(IRB, OldPtr->getType()), + II.getValue(), Size, + MaybeAlign(getSliceAlign()), II.isVolatile()) + : IRB.CreateMemSet(getNewAllocaSlicePtr(IRB, OldPtr->getType()), + II.getValue(), Size, + MaybeAlign(getSliceAlign()), II.isVolatile()); if (AATags) New->setAAMetadata(AATags.shift(NewBeginOffset - BeginOffset)); LLVM_DEBUG(dbgs() << " to: " << *New << "\n"); @@ -2874,6 +3013,8 @@ private: LLVMContext::MD_access_group}); if (AATags) New->setAAMetadata(AATags.shift(NewBeginOffset - BeginOffset)); + if (CJPipeline) + updateTBAA(DL, New); LLVM_DEBUG(dbgs() << " to: " << *New << "\n"); return !II.isVolatile(); } @@ -2904,8 +3045,7 @@ private: if (IsDest) { II.setDest(AdjustedPtr); II.setDestAlignment(SliceAlign); - } - else { + } else { II.setSource(AdjustedPtr); II.setSourceAlignment(SliceAlign); } @@ -3050,6 +3190,8 @@ private: LLVMContext::MD_access_group}); if (AATags) Load->setAAMetadata(AATags.shift(NewBeginOffset - BeginOffset)); + if (CJPipeline) + updateTBAA(DL, Load); Src = Load; } @@ -3072,6 +3214,8 @@ private: LLVMContext::MD_access_group}); if (AATags) Store->setAAMetadata(AATags.shift(NewBeginOffset - BeginOffset)); + if (CJPipeline) + updateTBAA(DL, Store); LLVM_DEBUG(dbgs() << " to: " << *Store << "\n"); return !II.isVolatile(); } @@ -3108,7 +3252,8 @@ private: NewEndOffset - NewBeginOffset); // Lifetime intrinsics always expect an i8* so directly get such a pointer // for the new alloca slice. - Type *PointerTy = IRB.getInt8PtrTy(OldPtr->getType()->getPointerAddressSpace()); + Type *PointerTy = + IRB.getInt8PtrTy(OldPtr->getType()->getPointerAddressSpace()); Value *Ptr = getNewAllocaSlicePtr(IRB, PointerTy); Value *New; if (II.getIntrinsicID() == Intrinsic::lifetime_start) @@ -3381,9 +3526,12 @@ private: APInt Offset( DL.getIndexSizeInBits(Ptr->getType()->getPointerAddressSpace()), 0); - if (AATags && - GEPOperator::accumulateConstantOffset(BaseTy, GEPIndices, DL, Offset)) + if (AATags && GEPOperator::accumulateConstantOffset(BaseTy, GEPIndices, + DL, Offset)) { Load->setAAMetadata(AATags.shift(Offset.getZExtValue())); + if (CJPipeline) + updateTBAA(DL, Load); + } Agg = IRB.CreateInsertValue(Agg, Load, Indices, Name + ".insert"); LLVM_DEBUG(dbgs() << " to: " << *Load << "\n"); @@ -3432,9 +3580,12 @@ private: APInt Offset( DL.getIndexSizeInBits(Ptr->getType()->getPointerAddressSpace()), 0); - if (AATags && - GEPOperator::accumulateConstantOffset(BaseTy, GEPIndices, DL, Offset)) + if (AATags && GEPOperator::accumulateConstantOffset(BaseTy, GEPIndices, + DL, Offset)) { Store->setAAMetadata(AATags.shift(Offset.getZExtValue())); + if (CJPipeline) + updateTBAA(DL, Store); + } LLVM_DEBUG(dbgs() << " to: " << *Store << "\n"); } @@ -3475,8 +3626,8 @@ private: SelectInst *Sel = cast(GEPI.getPointerOperand()); LLVM_DEBUG(dbgs() << " Rewriting gep(select) -> select(gep):" - << "\n original: " << *Sel - << "\n " << GEPI); + << "\n original: " << *Sel << "\n " + << GEPI); IRB.SetInsertPoint(&GEPI); SmallVector Index(GEPI.indices()); @@ -3501,9 +3652,8 @@ private: Visited.insert(NSelI); enqueueUsers(*NSelI); - LLVM_DEBUG(dbgs() << "\n to: " << *NTrue - << "\n " << *NFalse - << "\n " << *NSel << '\n'); + LLVM_DEBUG(dbgs() << "\n to: " << *NTrue << "\n " + << *NFalse << "\n " << *NSel << '\n'); return true; } @@ -3524,15 +3674,15 @@ private: return false; LLVM_DEBUG(dbgs() << " Rewriting gep(phi) -> phi(gep):" - << "\n original: " << *PHI - << "\n " << GEPI - << "\n to: "); + << "\n original: " << *PHI << "\n " + << GEPI << "\n to: "); SmallVector Index(GEPI.indices()); bool IsInBounds = GEPI.isInBounds(); IRB.SetInsertPoint(GEPI.getParent()->getFirstNonPHI()); - PHINode *NewPN = IRB.CreatePHI(GEPI.getType(), PHI->getNumIncomingValues(), - PHI->getName() + ".sroa.phi"); + PHINode *NewPN = + IRB.CreatePHI(GEPI.getType(), PHI->getNumIncomingValues(), + PHI->getName() + ".sroa.phi"); for (unsigned I = 0, E = PHI->getNumIncomingValues(); I != E; ++I) { BasicBlock *B = PHI->getIncomingBlock(I); Value *NewVal = nullptr; @@ -3556,20 +3706,19 @@ private: Visited.insert(NewPN); enqueueUsers(*NewPN); - LLVM_DEBUG(for (Value *In : NewPN->incoming_values()) - dbgs() << "\n " << *In; + LLVM_DEBUG(for (Value *In + : NewPN->incoming_values()) dbgs() + << "\n " << *In; dbgs() << "\n " << *NewPN << '\n'); return true; } bool visitGetElementPtrInst(GetElementPtrInst &GEPI) { - if (isa(GEPI.getPointerOperand()) && - foldGEPSelect(GEPI)) + if (isa(GEPI.getPointerOperand()) && foldGEPSelect(GEPI)) return true; - if (isa(GEPI.getPointerOperand()) && - foldGEPPhi(GEPI)) + if (isa(GEPI.getPointerOperand()) && foldGEPPhi(GEPI)) return true; enqueueUsers(GEPI); @@ -3619,6 +3768,68 @@ static Type *stripAggregateTypeWrapping(const DataLayout &DL, Type *Ty) { return stripAggregateTypeWrapping(DL, InnerTy); } +// Do recursively down through the last element in the sub-struct to find +// all elements. +static void getInnerTys(const DataLayout &DL, StructType *STy, + SmallVectorImpl &InnerTys, uint64_t Offset, + uint64_t EndOffset) { + const StructLayout *SL = DL.getStructLayout(STy); + while (Offset < EndOffset) { + unsigned Index = SL->getElementContainingOffset(Offset); + Type *ElementTy = STy->getElementType(Index); + uint64_t ElementSize = DL.getTypeAllocSize(ElementTy).getFixedSize(); + // Insert alignment paddings. + if (Offset - SL->getElementOffset(Index) >= ElementSize) { + uint64_t NextOffset = (Index + 1 == STy->getNumElements()) + ? EndOffset + : SL->getElementOffset(Index + 1); + InnerTys.push_back(ArrayType::get(Type::getInt8Ty(STy->getContext()), + NextOffset - Offset)); + Offset = NextOffset; + continue; + } + if (isa(ElementTy)) { + uint64_t ElementBegin = SL->getElementOffset(Index); + uint64_t ElementEnd = SL->getElementOffset(Index) + ElementSize; + if (ElementEnd > EndOffset) { + assert(Offset == ElementBegin); + getInnerTys(DL, dyn_cast(ElementTy), InnerTys, + Offset - ElementBegin, EndOffset - ElementBegin); + Offset = EndOffset; + } else if (ElementBegin < Offset) { + assert(ElementEnd < EndOffset); + getInnerTys(DL, dyn_cast(ElementTy), InnerTys, + Offset - ElementBegin, ElementEnd - ElementBegin); + Offset = ElementEnd; + } else { + InnerTys.push_back(ElementTy); + Offset = ElementEnd; + } + } else { + InnerTys.push_back(ElementTy); + Offset += ElementSize; + } + } +} + +static Type *constructPackedSubStruct(const DataLayout &DL, StructType *STy, + uint64_t Offset, uint64_t Size) { + if (!CJPipeline) { + return nullptr; + } + uint64_t EndOffset = Offset + Size; + if (!containGCPtr(Offset, EndOffset)) { + return nullptr; + } + // 8: default vector size. + SmallVector InnerTys; + getInnerTys(DL, STy, InnerTys, Offset, EndOffset); + StructType *SubTy = StructType::get(STy->getContext(), InnerTys, true); + assert(Size == DL.getStructLayout(SubTy)->getSizeInBytes() && + "Size must be equal!"); + return SubTy; +} + /// Try to find a partition of the aggregate type passed in for a given /// offset and size. /// @@ -3641,17 +3852,17 @@ static Type *getTypePartition(const DataLayout &DL, Type *Ty, uint64_t Offset, return nullptr; if (isa(Ty) || isa(Ty)) { - Type *ElementTy; - uint64_t TyNumElements; - if (auto *AT = dyn_cast(Ty)) { - ElementTy = AT->getElementType(); - TyNumElements = AT->getNumElements(); - } else { - // FIXME: This isn't right for vectors with non-byte-sized or - // non-power-of-two sized elements. - auto *VT = cast(Ty); - ElementTy = VT->getElementType(); - TyNumElements = VT->getNumElements(); + Type *ElementTy; + uint64_t TyNumElements; + if (auto *AT = dyn_cast(Ty)) { + ElementTy = AT->getElementType(); + TyNumElements = AT->getNumElements(); + } else { + // FIXME: This isn't right for vectors with non-byte-sized or + // non-power-of-two sized elements. + auto *VT = cast(Ty); + ElementTy = VT->getElementType(); + TyNumElements = VT->getNumElements(); } uint64_t ElementSize = DL.getTypeAllocSize(ElementTy).getFixedSize(); uint64_t NumSkippedElements = Offset / ElementSize; @@ -3694,20 +3905,32 @@ static Type *getTypePartition(const DataLayout &DL, Type *Ty, uint64_t Offset, Type *ElementTy = STy->getElementType(Index); uint64_t ElementSize = DL.getTypeAllocSize(ElementTy).getFixedSize(); - if (Offset >= ElementSize) - return nullptr; // The offset points into alignment padding. + + // In cangjie, we will process alignment in constructPackedSubStruct() + if (!CJPipeline) { + if (Offset >= ElementSize) { + return nullptr; // The offset points into alignment padding. + } + } // See if any partition must be contained by the element. if (Offset > 0 || Size < ElementSize) { + // 1. Part of element(struct or padding) and extends to next + // 2. Part of element(struct or padding) if ((Offset + Size) > ElementSize) - return nullptr; + return constructPackedSubStruct( + DL, STy, Offset + SL->getElementOffset(Index), Size); + // Part of element(struct) return getTypePartition(DL, ElementTy, Offset, Size); } assert(Offset == 0); + // Complete element if (Size == ElementSize) return stripAggregateTypeWrapping(DL, ElementTy); + // 1. Multi-elements + // 2. Complete element and extend(Offset == 0 && Size > ElementSize) StructType::element_iterator EI = STy->element_begin() + Index, EE = STy->element_end(); if (EndOffset < SL->getSizeInBytes()) { @@ -3717,10 +3940,12 @@ static Type *getTypePartition(const DataLayout &DL, Type *Ty, uint64_t Offset, // Don't try to form "natural" types if the elements don't line up with the // expected size. - // FIXME: We could potentially recurse down through the last element in the - // sub-struct to find a natural end point. + // if EndIndex = SL->getElementContainingOffset(EndOffset) && + // SL->getElementOffset(EndIndex) != EndOffset + // It's means that the EndOffset is in the middle of this element if (SL->getElementOffset(EndIndex) != EndOffset) - return nullptr; + return constructPackedSubStruct( + DL, STy, Offset + SL->getElementOffset(Index), Size); assert(Index < EndIndex); EE = STy->element_begin() + EndIndex; @@ -3730,8 +3955,12 @@ static Type *getTypePartition(const DataLayout &DL, Type *Ty, uint64_t Offset, StructType *SubTy = StructType::get(STy->getContext(), makeArrayRef(EI, EE), STy->isPacked()); const StructLayout *SubSL = DL.getStructLayout(SubTy); - if (Size != SubSL->getSizeInBytes()) - return nullptr; // The sub-struct doesn't have quite the size needed. + if (Size != SubSL->getSizeInBytes()) { + // The sub-struct doesn't have quite the size needed. Construct the + // sub-struct with padding. + return constructPackedSubStruct(DL, STy, + Offset + SL->getElementOffset(Index), Size); + } return SubTy; } @@ -4395,6 +4624,20 @@ AllocaInst *SROAPass::rewritePartition(AllocaInst &AI, AllocaSlices &AS, return NewAI; } +static void extractGCPtrOffsets(const DataLayout &DL, StructType *STy, + uint64_t BaseOffset) { + uint32_t Idx = 0; + for (auto ElementTy : STy->elements()) { + uint64_t Offset = DL.getStructLayout(STy)->getElementOffset(Idx); + if (isGCPointerType(ElementTy)) { + GCPtrOffsets.push_back(BaseOffset + Offset); + } else if (isa(ElementTy)) { + extractGCPtrOffsets(DL, cast(ElementTy), BaseOffset + Offset); + } + Idx++; + } +} + /// Walks the slices of an alloca and form partitions based on them, /// rewriting each of their uses. bool SROAPass::splitAlloca(AllocaInst &AI, AllocaSlices &AS) { @@ -4405,6 +4648,13 @@ bool SROAPass::splitAlloca(AllocaInst &AI, AllocaSlices &AS) { bool Changed = false; const DataLayout &DL = AI.getModule()->getDataLayout(); + if (CJPipeline) { + GCPtrOffsets.clear(); + if (auto *STy = dyn_cast(AI.getAllocatedType())) { + extractGCPtrOffsets(DL, STy, 0); + } + } + // First try to pre-split loads and stores. Changed |= presplitLoadsAndStores(AI, AS); @@ -4442,8 +4692,7 @@ bool SROAPass::splitAlloca(AllocaInst &AI, AllocaSlices &AS) { IsSorted = false; } } - } - else { + } else { // We only allow whole-alloca splittable loads and stores // for a large alloca to avoid creating too large BitVector. for (Slice &S : AS) { @@ -4471,7 +4720,7 @@ bool SROAPass::splitAlloca(AllocaInst &AI, AllocaSlices &AS) { uint64_t Offset; uint64_t Size; Fragment(AllocaInst *AI, uint64_t O, uint64_t S) - : Alloca(AI), Offset(O), Size(S) {} + : Alloca(AI), Offset(O), Size(S) {} }; SmallVector Fragments; @@ -4485,7 +4734,8 @@ bool SROAPass::splitAlloca(AllocaInst &AI, AllocaSlices &AS) { DL.getTypeSizeInBits(NewAI->getAllocatedType()).getFixedSize(); // Don't include any padding. uint64_t Size = std::min(AllocaSize, P.size() * SizeOfByte); - Fragments.push_back(Fragment(NewAI, P.beginOffset() * SizeOfByte, Size)); + Fragments.push_back( + Fragment(NewAI, P.beginOffset() * SizeOfByte, Size)); } } ++NumPartitions; @@ -4560,8 +4810,8 @@ bool SROAPass::splitAlloca(AllocaInst &AI, AllocaSlices &AS) { OldDII->eraseFromParent(); } - DIB.insertDeclare(Fragment.Alloca, DbgDeclare->getVariable(), FragmentExpr, - DbgDeclare->getDebugLoc(), &AI); + DIB.insertDeclare(Fragment.Alloca, DbgDeclare->getVariable(), + FragmentExpr, DbgDeclare->getDebugLoc(), &AI); } } return Changed; @@ -4582,6 +4832,22 @@ void SROAPass::clobberUse(Use &U) { } } +static bool isStructContainArray(Type *Ty) { + if (auto AT = dyn_cast(Ty)) { + return true; + } + auto ST = dyn_cast(Ty); + if (ST == nullptr) { + return false; + } + for (unsigned Index = 0; Index < ST->getNumElements(); Index++) { + if (isStructContainArray(ST->getStructElementType(Index))) { + return true; + } + } + return false; +} + /// Analyze an alloca for SROA. /// /// This analyzes the alloca to ensure we can reason about it, builds @@ -4598,12 +4864,18 @@ bool SROAPass::runOnAlloca(AllocaInst &AI) { } const DataLayout &DL = AI.getModule()->getDataLayout(); + PointerSize = DL.getPointerSize(); + // Skip alloca forms that this analysis can't handle. auto *AT = AI.getAllocatedType(); if (AI.isArrayAllocation() || !AT->isSized() || isa(AT) || DL.getTypeAllocSize(AT).getFixedSize() == 0) return false; + if (CJPipeline && isa(AT) && isStructContainArray(AT)) { + return false; + } + bool Changed = false; // First, split any FCA loads and stores touching this alloca to promote @@ -4667,7 +4939,8 @@ bool SROAPass::deleteDeadInstructions( bool Changed = false; while (!DeadInsts.empty()) { Instruction *I = dyn_cast_or_null(DeadInsts.pop_back_val()); - if (!I) continue; + if (!I) + continue; LLVM_DEBUG(dbgs() << "Deleting dead instruction: " << *I << "\n"); // If the instruction is an alloca, find the possible dbg.declare connected @@ -4723,13 +4996,15 @@ PreservedAnalyses SROAPass::runImpl(Function &F, DominatorTree &RunDT, BasicBlock &EntryBB = F.getEntryBlock(); for (BasicBlock::iterator I = EntryBB.begin(), E = std::prev(EntryBB.end()); I != E; ++I) { - if (AllocaInst *AI = dyn_cast(I)) { - if (isa(AI->getAllocatedType())) { - if (isAllocaPromotable(AI)) - PromotableAllocas.push_back(AI); - } else { - Worklist.insert(AI); - } + AllocaInst *AI = dyn_cast(I); + if (AI == nullptr) { + continue; + } + if (isa(AI->getAllocatedType())) { + if (isAllocaPromotable(AI)) + PromotableAllocas.push_back(AI); + } else { + Worklist.insert(AI); } } diff --git a/llvm/lib/Transforms/Scalar/Scalar.cpp b/llvm/lib/Transforms/Scalar/Scalar.cpp index 5ab9e2557..c426dac8c 100644 --- a/llvm/lib/Transforms/Scalar/Scalar.cpp +++ b/llvm/lib/Transforms/Scalar/Scalar.cpp @@ -105,12 +105,22 @@ void llvm::initializeScalarOpts(PassRegistry &Registry) { initializeSpeculativeExecutionLegacyPassPass(Registry); initializeStraightLineStrengthReduceLegacyPassPass(Registry); initializePlaceBackedgeSafepointsImplPass(Registry); - initializePlaceSafepointsPass(Registry); + initializePlaceSafepointsLegacyPassPass(Registry); initializeFloat2IntLegacyPassPass(Registry); initializeLoopDistributeLegacyPass(Registry); initializeLoopLoadEliminationPass(Registry); initializeLoopSimplifyCFGLegacyPassPass(Registry); initializeLoopVersioningLegacyPassPass(Registry); + // Cangjie pipeline pass + initializeCJFillMetadataLegacyPassPass(Registry); + initializeCJBarrierOptLegacyPassPass(Registry); + initializeCJBarrierSplitLegacyPassPass(Registry); + initializeCJLoopFloatOptLegacyPassPass(Registry); + initializeCJRuntimeLoweringLegacyPassPass(Registry); + initializeCangjieSpecificOptLegacyPassPass(Registry); + initializeInsertCJTBAALegacyPassPass(Registry); + initializeCJSimpleRangeAnalysisPass(Registry); + initializeCJRewriteStatepointLegacyPassPass(Registry); } void LLVMAddLoopSimplifyCFGPass(LLVMPassManagerRef PM) { diff --git a/llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp b/llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp index fb2d812a1..bbdd58dd7 100644 --- a/llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp +++ b/llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp @@ -35,6 +35,7 @@ #include "llvm/IR/Dominators.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/IRBuilder.h" #include "llvm/IR/ValueHandle.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" @@ -80,6 +81,10 @@ static cl::opt UserSinkCommonInsts( STATISTIC(NumSimpl, "Number of blocks simplified"); +namespace llvm { +extern cl::opt CJPipeline; +} + static bool performBlockTailMerging(Function &F, ArrayRef BBs, std::vector *Updates) { @@ -153,11 +158,45 @@ performBlockTailMerging(Function &F, ArrayRef BBs, return true; } +static bool moveCJMemsetInstToPredBB(BasicBlock &BB) { + SmallVector InstsToKill; + bool Changed = false; + for (auto I = BB.begin(); I != BB.end(); ++I) { + Instruction &Inst = *I; + IntrinsicInst *II = dyn_cast(&Inst); + if (!II || II->getIntrinsicID() != Intrinsic::cj_memset) { + continue; + } + auto *BCI = dyn_cast(Inst.getOperand(0)); + if (!BCI || !isa(BCI->getOperand(0))) { + continue; + } + auto *PhiInst = dyn_cast(BCI->getOperand(0)); + for (unsigned Idx = 0, E = PhiInst->getNumIncomingValues(); Idx != E; + ++Idx) { + BasicBlock *PredBB = PhiInst->getIncomingBlock(Idx); + Value *PredValue = PhiInst->getIncomingValue(Idx); + Instruction *EndInst = dyn_cast(&PredBB->back()); + IRBuilder<> Builder(EndInst); + // 1: Inst 1st operand value + // 2: Inst 2st Operand value + Builder.CreateCJMemSet(PredValue, Inst.getOperand(1), Inst.getOperand(2), + Align(1)); + } + Changed = true; + InstsToKill.push_back(&Inst); + } + for (auto *I : InstsToKill) { + I->eraseFromParent(); + } + return Changed; +} + static bool tailMergeBlocksWithSimilarFunctionTerminators(Function &F, DomTreeUpdater *DTU) { SmallMapVector, 4> Structure; - + bool Changed = false; // Scan all the blocks in the function, record the interesting-ones. for (BasicBlock &BB : F) { if (DTU && DTU->isBBPendingDeletion(&BB)) @@ -200,12 +239,15 @@ static bool tailMergeBlocksWithSimilarFunctionTerminators(Function &F, [](Value *Op) { return Op->getType()->isTokenTy(); })) continue; + // If the value in the memset function is determined by the phi node, + // move the memset instruction to the predecessor node. + if (CJPipeline) + Changed |= moveCJMemsetInstToPredBB(BB); + // Canonical blocks are uniqued based on the terminator type (opcode). Structure[Term->getOpcode()].emplace_back(&BB); } - bool Changed = false; - std::vector Updates; for (ArrayRef BBs : make_second_range(Structure)) @@ -291,9 +333,9 @@ static bool simplifyFunctionCFGImpl(Function &F, const TargetTransformInfo &TTI, return true; } -static bool simplifyFunctionCFG(Function &F, const TargetTransformInfo &TTI, - DominatorTree *DT, - const SimplifyCFGOptions &Options) { +bool llvm::simplifyFunctionCFG(Function &F, const TargetTransformInfo &TTI, + DominatorTree *DT, + const SimplifyCFGOptions &Options) { assert((!RequireAndPreserveDomTree || (DT && DT->verify(DominatorTree::VerificationLevel::Full))) && "Original domtree is invalid?"); diff --git a/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp b/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp index 27c04177e..9cfafefcc 100644 --- a/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp +++ b/llvm/lib/Transforms/Scalar/TailRecursionElimination.cpp @@ -87,6 +87,10 @@ STATISTIC(NumEliminated, "Number of tail calls removed"); STATISTIC(NumRetDuped, "Number of return duplicated"); STATISTIC(NumAccumAdded, "Number of accumulators introduced"); +namespace llvm { +extern cl::opt CJPipeline; +} + /// Scan the specified function for alloca instructions. /// If it contains any dynamic allocas, returns false. static bool canTRE(Function &F) { @@ -236,6 +240,9 @@ static bool markTails(Function &F, OptimizationRemarkEmitter *ORE) { Escaped = ESCAPED; CallInst *CI = dyn_cast(&I); + if (CI && CJPipeline) { + CI->setTailCall(false); + } // A PseudoProbeInst has the IntrInaccessibleMemOnly tag hence it is // considered accessing memory and will be marked as a tail call if we // don't bail out here. diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp index e3cb5f359..b9c167515 100644 --- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp +++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp @@ -57,6 +57,10 @@ static cl::opt MaxDeoptOrUnreachableSuccessorCheckDepth( "is followed by a block that either has a terminating " "deoptimizing call or is terminated with an unreachable")); +namespace llvm { +extern cl::opt CJPipeline; +} + void llvm::detachDeadBlocks( ArrayRef BBs, SmallVectorImpl *Updates, @@ -1094,8 +1098,7 @@ SplitBlockPredecessorsImpl(BasicBlock *BB, ArrayRef Preds, // Delegate this work to the SplitLandingPadPredecessors. if (BB->isLandingPad()) { SmallVector NewBBs; - std::string NewName = std::string(Suffix) + ".split-lp"; - + std::string NewName = std::string(Suffix) + (CJPipeline ? "" : ".split-lp"); SplitLandingPadPredecessorsImpl(BB, Preds, Suffix, NewName.c_str(), NewBBs, DTU, DT, LI, MSSAU, PreserveLCSSA); return NewBBs[0]; diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp index 421f1f329..56965aaf0 100644 --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -49,6 +49,7 @@ #include "llvm/IR/MDBuilder.h" #include "llvm/IR/Module.h" #include "llvm/IR/PatternMatch.h" +#include "llvm/IR/SafepointIRVerifier.h" #include "llvm/IR/Type.h" #include "llvm/IR/User.h" #include "llvm/IR/Value.h" @@ -73,7 +74,9 @@ using namespace llvm::PatternMatch; using ProfileCount = Function::ProfileCount; #define DEBUG_TYPE "code-extractor" - +namespace llvm { +extern cl::opt CJPipeline; +} // Provide a command-line option to aggregate function arguments into a struct // for functions produced by the code extractor. This is useful when converting // extracted functions to pthread-based code, as only one argument (void*) can @@ -643,6 +646,109 @@ bool CodeExtractor::isEligible() const { return true; } +/// Return the base value, and calculate the indices of nested GEPs if there +/// are. +static Value *findBaseValueAndCalculateGEPOffsets(Value *V, uint64_t &Offset) { + Offset = 0; + Value *CV = V; + while (true) { + if (auto *CI = dyn_cast(CV)) { + Value *Def = CI->stripPointerCasts(); + // If we find a cast instruction here, it means we've found a cast which + // is not simply a pointer cast (i.e. an inttoptr). We don't know how to + // handle int->ptr conversion. + // assert(!isa(Def) && "Not support inttoptr") + if (isa(Def)) { + return Def; + } + CV = Def; + } else if (auto *GEP = dyn_cast(CV)) { + APInt GEPOffset(64, 0); + if (!GEP->accumulateConstantOffset(GEP->getModule()->getDataLayout(), + GEPOffset)) { + // we do not handle inconstant gep offset. + return nullptr; + } + Offset += GEPOffset.getZExtValue(); + CV = GEP->getPointerOperand(); + } else { + break; + } + } + + // Assert terminator insts + assert(isa(CV) || isa(CV) || isa(CV) || + isa(CV) || isa(CV) || isa(CV) || + isa(CV) || isa(CV)); + return CV; +} + +void CodeExtractor::insertStructBaseInArguments(Function *F, ValueSet &Values, + ValueMap &BasePtrCands) { + for (Value *V : Values) { + if (isa(V->getType()) && + V->getType()->getNonOpaquePointerElementType()->isStructTy()) { + // if V is a struct ptr in arguments, it cannot be the first one cause its + // base ptr must be in front of itself. Therefor, I starts from 1. + size_t I = 1; + for (; I < F->arg_size(); ++I) { + if (F->getArg(I) == dyn_cast(V)) { + break; + } + } + + if (I < F->arg_size()) { + BasePtrCands[V] = F->getArg(I - 1); + } else { + Type *NullBasePtr = Type::getInt8PtrTy(F->getContext(), 1); + BasePtrCands[V] = Constant::getNullValue(NullBasePtr); + } + } + } +} + +bool CodeExtractor::findInputsOutputs(ValueSet &Inputs, ValueSet &Outputs, + const ValueSet &SinkCands, + ValueSet &BitcastInstrs) const { + auto InsertValueOrBase = [&](Value *V) { + // If a value is cast to addrspace(1)* but it is not a struct, insert its + // base instead of the value itself. + if (isa(V) && isGCPointerType(V->getType()) && + !V->getType()->getNonOpaquePointerElementType()->isStructTy()) { + uint64_t Offset = 0; + if (Value *Base = findBaseValueAndCalculateGEPOffsets(V, Offset)) { + Inputs.insert(Base); + } else { + return false; + } + BitcastInstrs.insert(V); + } else { + Inputs.insert(V); + } + return true; + }; + + bool Result = true; + for (BasicBlock *BB : Blocks) { + // If a used value is defined outside the region, it's an input. If an + // instruction is used outside the region, it's an output. + for (Instruction &II : *BB) { + for (auto &OI : II.operands()) { + Value *V = OI; + if (!SinkCands.count(V) && definedInCaller(Blocks, V)) + Result = InsertValueOrBase(V); + } + + for (User *U : II.users()) + if (!definedInRegion(Blocks, U)) { + Outputs.insert(&II); + break; + } + } + } + return Result; +} + void CodeExtractor::findInputsOutputs(ValueSet &Inputs, ValueSet &Outputs, const ValueSet &SinkCands) const { for (BasicBlock *BB : Blocks) { @@ -808,54 +914,74 @@ void CodeExtractor::splitReturnBlocks() { } } -/// constructFunction - make a function based on inputs and outputs, as follows: +/// constructFunction - make a function based on Inputs and Outputs, as follows: /// f(in0, ..., inN, out0, ..., outN) -Function *CodeExtractor::constructFunction(const ValueSet &inputs, - const ValueSet &outputs, - BasicBlock *header, - BasicBlock *newRootNode, - BasicBlock *newHeader, - Function *oldFunction, +Function *CodeExtractor::constructFunction(const ValueSet &Inputs, + const ValueSet &Outputs, + const ValueSet &BitcastInstrs, + const ValueMap &InputsBasePtrCands, + const ValueMap &OutputsBasePtrCands, + BasicBlock *Header, + BasicBlock *NewRootNode, + BasicBlock *NewHeader, + Function *OldFunction, Module *M) { - LLVM_DEBUG(dbgs() << "inputs: " << inputs.size() << "\n"); - LLVM_DEBUG(dbgs() << "outputs: " << outputs.size() << "\n"); + LLVM_DEBUG(dbgs() << "Inputs: " << Inputs.size() << "\n"); + LLVM_DEBUG(dbgs() << "Outputs: " << Outputs.size() << "\n"); - // This function returns unsigned, outputs will go back by reference. + // This function returns unsigned, Outputs will go back by reference. switch (NumExitBlocks) { case 0: - case 1: RetTy = Type::getVoidTy(header->getContext()); break; - case 2: RetTy = Type::getInt1Ty(header->getContext()); break; - default: RetTy = Type::getInt16Ty(header->getContext()); break; + case 1: RetTy = Type::getVoidTy(Header->getContext()); break; + case 2: RetTy = Type::getInt1Ty(Header->getContext()); break; + default: RetTy = Type::getInt16Ty(Header->getContext()); break; } std::vector ParamTy; std::vector AggParamTy; ValueSet StructValues; - // Add the types of the input values to the function's argument list - for (Value *value : inputs) { + // Add the types of the input values to the function's argument list. If the + // value is a struct, add its base value's type as well. + for (Value *value : Inputs) { LLVM_DEBUG(dbgs() << "value used in func: " << *value << "\n"); if (AggregateArgs && !ExcludeArgsFromAggregate.contains(value)) { + if (InputsBasePtrCands.count(value)) { + AggParamTy.push_back(InputsBasePtrCands.lookup(value)->getType()); + StructValues.insert(InputsBasePtrCands.lookup(value)); + } AggParamTy.push_back(value->getType()); StructValues.insert(value); - } else + } else { + if (InputsBasePtrCands.count(value)) + ParamTy.push_back(InputsBasePtrCands.lookup(value)->getType()); ParamTy.push_back(value->getType()); + } } - // Add the types of the output values to the function's argument list. - for (Value *output : outputs) { + // Add the types of the output values to the function's argument list. If the + // value is a struct, add its base value's type as well. + for (Value *output : Outputs) { LLVM_DEBUG(dbgs() << "instr used in func: " << *output << "\n"); if (AggregateArgs && !ExcludeArgsFromAggregate.contains(output)) { + if (OutputsBasePtrCands.count(output)) { + AggParamTy.push_back(OutputsBasePtrCands.lookup(output)->getType()); + StructValues.insert(OutputsBasePtrCands.lookup(output)); + } AggParamTy.push_back(output->getType()); StructValues.insert(output); - } else + } else { + if (OutputsBasePtrCands.count(output)) + ParamTy.push_back(OutputsBasePtrCands.lookup(output)->getType()); ParamTy.push_back(PointerType::getUnqual(output->getType())); + } } assert( (ParamTy.size() + AggParamTy.size()) == - (inputs.size() + outputs.size()) && - "Number of scalar and aggregate params does not match inputs, outputs"); + (Inputs.size() + Outputs.size() + InputsBasePtrCands.size() + + OutputsBasePtrCands.size()) && + "Number of scalar and aggregate params does not match Inputs, Outputs"); assert((StructValues.empty() || AggregateArgs) && "Expeced StructValues only with AggregateArgs set"); @@ -875,16 +1001,16 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs, }); FunctionType *funcType = FunctionType::get( - RetTy, ParamTy, AllowVarArgs && oldFunction->isVarArg()); + RetTy, ParamTy, AllowVarArgs && OldFunction->isVarArg()); std::string SuffixToUse = Suffix.empty() - ? (header->getName().empty() ? "extracted" : header->getName().str()) + ? (Header->getName().empty() ? "extracted" : Header->getName().str()) : Suffix; // Create the new function Function *newFunction = Function::Create( - funcType, GlobalValue::InternalLinkage, oldFunction->getAddressSpace(), - oldFunction->getName() + "." + SuffixToUse, M); + funcType, GlobalValue::InternalLinkage, OldFunction->getAddressSpace(), + OldFunction->getName() + "." + SuffixToUse, M); // Inherit all of the target dependent attributes and white-listed // target independent attributes. @@ -893,7 +1019,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs, // "target-features" attribute allowing it to be lowered. // FIXME: This should be changed to check to see if a specific // attribute can not be inherited. - for (const auto &Attr : oldFunction->getAttributes().getFnAttrs()) { + for (const auto &Attr : OldFunction->getAttributes().getFnAttrs()) { if (Attr.isStringAttribute()) { if (Attr.getKindAsString() == "thunk") continue; @@ -994,64 +1120,118 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs, case Attribute::EndAttrKinds: case Attribute::EmptyKey: case Attribute::TombstoneKey: + case Attribute::CJStackPointer: llvm_unreachable("Not a function attribute"); } newFunction->addFnAttr(Attr); } - newFunction->getBasicBlockList().push_back(newRootNode); + newFunction->getBasicBlockList().push_back(NewRootNode); // Create scalar and aggregate iterators to name all of the arguments we // inserted. Function::arg_iterator ScalarAI = newFunction->arg_begin(); Function::arg_iterator AggAI = std::next(ScalarAI, NumScalarParams); - // Rewrite all users of the inputs in the extracted region to use the + // Rewrite all users of the Inputs in the extracted region to use the // arguments (or appropriate addressing into struct) instead. - for (unsigned i = 0, e = inputs.size(), aggIdx = 0; i != e; ++i) { + for (unsigned i = 0, e = Inputs.size(), aggIdx = 0; i != e; ++i) { Value *RewriteVal; - if (AggregateArgs && StructValues.contains(inputs[i])) { + if (AggregateArgs && StructValues.contains(Inputs[i])) { Value *Idx[2]; - Idx[0] = Constant::getNullValue(Type::getInt32Ty(header->getContext())); - Idx[1] = ConstantInt::get(Type::getInt32Ty(header->getContext()), aggIdx); + Idx[0] = Constant::getNullValue(Type::getInt32Ty(Header->getContext())); + Idx[1] = ConstantInt::get(Type::getInt32Ty(Header->getContext()), aggIdx); Instruction *TI = newFunction->begin()->getTerminator(); GetElementPtrInst *GEP = GetElementPtrInst::Create( - StructTy, &*AggAI, Idx, "gep_" + inputs[i]->getName(), TI); + StructTy, &*AggAI, Idx, "gep_" + Inputs[i]->getName(), TI); RewriteVal = new LoadInst(StructTy->getElementType(aggIdx), GEP, - "loadgep_" + inputs[i]->getName(), TI); + "loadgep_" + Inputs[i]->getName(), TI); ++aggIdx; - } else + } else { + // If the value is a struct, skip its base. + if (InputsBasePtrCands.count(Inputs[i])) + ++ScalarAI; RewriteVal = &*ScalarAI++; + } - std::vector Users(inputs[i]->user_begin(), inputs[i]->user_end()); + std::vector Users(Inputs[i]->user_begin(), Inputs[i]->user_end()); for (User *use : Users) if (Instruction *inst = dyn_cast(use)) if (Blocks.count(inst->getParent())) - inst->replaceUsesOfWith(inputs[i], RewriteVal); + inst->replaceUsesOfWith(Inputs[i], RewriteVal); } // Set names for input and output arguments. if (NumScalarParams) { ScalarAI = newFunction->arg_begin(); - for (unsigned i = 0, e = inputs.size(); i != e; ++i, ++ScalarAI) - if (!StructValues.contains(inputs[i])) - ScalarAI->setName(inputs[i]->getName()); - for (unsigned i = 0, e = outputs.size(); i != e; ++i, ++ScalarAI) - if (!StructValues.contains(outputs[i])) - ScalarAI->setName(outputs[i]->getName() + ".out"); + for (unsigned i = 0, e = Inputs.size(); i != e; ++i, ++ScalarAI) { + // If the value is a struct, skip its base. + if (InputsBasePtrCands.lookup(Inputs[i])) + ++ScalarAI; + if (!StructValues.contains(Inputs[i])) + ScalarAI->setName(Inputs[i]->getName()); + } + for (unsigned i = 0, e = Outputs.size(); i != e; ++i, ++ScalarAI) { + // If the value is a struct, skip its base. + if (OutputsBasePtrCands.lookup(Outputs[i])) + ++ScalarAI; + if (!StructValues.contains(Outputs[i])) + ScalarAI->setName(Outputs[i]->getName() + ".out"); + } + } + + // Insert GEP and bitcast instructions for BitcastInstrs to the beginning of + // newFunction. + for (size_t I = 0; I < BitcastInstrs.size(); ++I) { + Value *Bitcast = BitcastInstrs[I]; + uint64_t Offset; + Value *Base = findBaseValueAndCalculateGEPOffsets(Bitcast, Offset); + assert(Base && "Base should not be nullptr here!"); + Value *RewriteVal; + Instruction *InsertBefore = newFunction->begin()->getTerminator(); + if (isa(Base)) { + RewriteVal = + new IntToPtrInst(Base->stripPointerCasts(), Bitcast->getType(), + "pi_inttoptr", InsertBefore); + } else { + size_t ArgNo = 0; + for (auto *V : Inputs) { + // If the value is a struct, skip its base. + if (InputsBasePtrCands.lookup(V)) + ++ArgNo; + if (!StructValues.contains(V) && Base == V) + break; + ++ArgNo; + } + + Type *SourceElementType = + Base->getType()->getNonOpaquePointerElementType(); + auto *NewGEP = GetElementPtrInst::CreateInBounds( + SourceElementType, newFunction->getArg(ArgNo), + ConstantInt::get(M->getContext(), APInt(32, Offset)), "pi_gep", + InsertBefore); + RewriteVal = new BitCastInst(NewGEP, Bitcast->getType(), "pi_bitcastgep", + InsertBefore); + } + + std::vector Users(Bitcast->user_begin(), Bitcast->user_end()); + for (User *use : Users) + if (Instruction *inst = dyn_cast(use)) + if (Blocks.count(inst->getParent())) + inst->replaceUsesOfWith(Bitcast, RewriteVal); } // Rewrite branches to basic blocks outside of the loop to new dummy blocks // within the new function. This must be done before we lose track of which // blocks were originally in the code region. - std::vector Users(header->user_begin(), header->user_end()); + std::vector Users(Header->user_begin(), Header->user_end()); for (auto &U : Users) // The BasicBlock which contains the branch is not in the region // modify the branch target to a new block if (Instruction *I = dyn_cast(U)) - if (I->isTerminator() && I->getFunction() == oldFunction && + if (I->isTerminator() && I->getFunction() == OldFunction && !Blocks.count(I->getParent())) - I->replaceUsesOfWith(header, newHeader); + I->replaceUsesOfWith(Header, NewHeader); return newFunction; } @@ -1140,27 +1320,34 @@ static void insertLifetimeMarkersSurroundingCall( /// emitCallAndSwitchStatement - This method sets up the caller side by adding /// the call instruction, splitting any PHI nodes in the header block as /// necessary. -CallInst *CodeExtractor::emitCallAndSwitchStatement(Function *newFunction, - BasicBlock *codeReplacer, - ValueSet &inputs, - ValueSet &outputs) { +CallInst *CodeExtractor::emitCallAndSwitchStatement(Function *NewFunction, + BasicBlock *NewHeader, + ValueSet &Inputs, + ValueSet &Outputs, + ValueMap &InputsBasePtrCands, + ValueMap &OutputsBasePtrCands) { // Emit a call to the new function, passing in: *pointer to struct (if - // aggregating parameters), or plan inputs and allocated memory for outputs + // aggregating parameters), or plan Inputs and allocated memory for Outputs std::vector params, ReloadOutputs, Reloads; ValueSet StructValues; - Module *M = newFunction->getParent(); + Module *M = NewFunction->getParent(); LLVMContext &Context = M->getContext(); const DataLayout &DL = M->getDataLayout(); CallInst *call = nullptr; - // Add inputs as params, or to be filled into the struct + // Add Inputs as params, or to be filled into the struct. If the value is a + // struct, add its base value as well. unsigned ScalarInputArgNo = 0; SmallVector SwiftErrorArgs; - for (Value *input : inputs) { - if (AggregateArgs && !ExcludeArgsFromAggregate.contains(input)) + for (Value *input : Inputs) { + if (AggregateArgs && !ExcludeArgsFromAggregate.contains(input)) { + if (InputsBasePtrCands.count(input)) + StructValues.insert(InputsBasePtrCands.lookup(input)); StructValues.insert(input); - else { + } else { + if (InputsBasePtrCands.count(input)) + params.push_back(InputsBasePtrCands.lookup(input)); params.push_back(input); if (input->isSwiftError()) SwiftErrorArgs.push_back(ScalarInputArgNo); @@ -1168,16 +1355,28 @@ CallInst *CodeExtractor::emitCallAndSwitchStatement(Function *newFunction, ++ScalarInputArgNo; } - // Create allocas for the outputs + // Create allocas for the Outputs. If the value is a struct, create alloca for + // its base value as well. unsigned ScalarOutputArgNo = 0; - for (Value *output : outputs) { + for (Value *output : Outputs) { if (AggregateArgs && !ExcludeArgsFromAggregate.contains(output)) { + if (OutputsBasePtrCands.count(output)) + StructValues.insert(OutputsBasePtrCands.lookup(output)); StructValues.insert(output); } else { + if (OutputsBasePtrCands.count(output)) { + AllocaInst *alloca = new AllocaInst( + OutputsBasePtrCands.lookup(output)->getType(), + DL.getAllocaAddrSpace(), nullptr, + OutputsBasePtrCands.lookup(output)->getName() + ".loc", + &NewHeader->getParent()->front().front()); + ReloadOutputs.push_back(alloca); + params.push_back(alloca); + } AllocaInst *alloca = new AllocaInst(output->getType(), DL.getAllocaAddrSpace(), nullptr, output->getName() + ".loc", - &codeReplacer->getParent()->front().front()); + &NewHeader->getParent()->front().front()); ReloadOutputs.push_back(alloca); params.push_back(alloca); ++ScalarOutputArgNo; @@ -1193,82 +1392,81 @@ CallInst *CodeExtractor::emitCallAndSwitchStatement(Function *newFunction, ArgTypes.push_back(V->getType()); // Allocate a struct at the beginning of this function - StructArgTy = StructType::get(newFunction->getContext(), ArgTypes); + StructArgTy = StructType::get(NewFunction->getContext(), ArgTypes); Struct = new AllocaInst( StructArgTy, DL.getAllocaAddrSpace(), nullptr, "structArg", AllocationBlock ? &*AllocationBlock->getFirstInsertionPt() - : &codeReplacer->getParent()->front().front()); + : &NewHeader->getParent()->front().front()); params.push_back(Struct); - // Store aggregated inputs in the struct. + // Store aggregated Inputs in the struct. for (unsigned i = 0, e = StructValues.size(); i != e; ++i) { - if (inputs.contains(StructValues[i])) { + if (Inputs.contains(StructValues[i])) { Value *Idx[2]; Idx[0] = Constant::getNullValue(Type::getInt32Ty(Context)); Idx[1] = ConstantInt::get(Type::getInt32Ty(Context), i); GetElementPtrInst *GEP = GetElementPtrInst::Create( StructArgTy, Struct, Idx, "gep_" + StructValues[i]->getName()); - codeReplacer->getInstList().push_back(GEP); - new StoreInst(StructValues[i], GEP, codeReplacer); + NewHeader->getInstList().push_back(GEP); + new StoreInst(StructValues[i], GEP, NewHeader); NumAggregatedInputs++; } } } // Emit the call to the function - call = CallInst::Create(newFunction, params, + call = CallInst::Create(NewFunction, params, NumExitBlocks > 1 ? "targetBlock" : ""); // Add debug location to the new call, if the original function has debug // info. In that case, the terminator of the entry block of the extracted // function contains the first debug location of the extracted function, // set in extractCodeRegion. - if (codeReplacer->getParent()->getSubprogram()) { - if (auto DL = newFunction->getEntryBlock().getTerminator()->getDebugLoc()) + if (NewHeader->getParent()->getSubprogram()) { + if (auto DL = NewFunction->getEntryBlock().getTerminator()->getDebugLoc()) call->setDebugLoc(DL); } - codeReplacer->getInstList().push_back(call); + NewHeader->getInstList().push_back(call); // Set swifterror parameter attributes. for (unsigned SwiftErrArgNo : SwiftErrorArgs) { call->addParamAttr(SwiftErrArgNo, Attribute::SwiftError); - newFunction->addParamAttr(SwiftErrArgNo, Attribute::SwiftError); + NewFunction->addParamAttr(SwiftErrArgNo, Attribute::SwiftError); } - // Reload the outputs passed in by reference, use the struct if output is in + // Reload the Outputs passed in by reference, use the struct if output is in // the aggregate or reload from the scalar argument. - for (unsigned i = 0, e = outputs.size(), scalarIdx = 0, + for (unsigned i = 0, e = Outputs.size(), scalarIdx = 0, aggIdx = NumAggregatedInputs; i != e; ++i) { Value *Output = nullptr; - if (AggregateArgs && StructValues.contains(outputs[i])) { + if (AggregateArgs && StructValues.contains(Outputs[i])) { Value *Idx[2]; Idx[0] = Constant::getNullValue(Type::getInt32Ty(Context)); Idx[1] = ConstantInt::get(Type::getInt32Ty(Context), aggIdx); GetElementPtrInst *GEP = GetElementPtrInst::Create( - StructArgTy, Struct, Idx, "gep_reload_" + outputs[i]->getName()); - codeReplacer->getInstList().push_back(GEP); + StructArgTy, Struct, Idx, "gep_reload_" + Outputs[i]->getName()); + NewHeader->getInstList().push_back(GEP); Output = GEP; ++aggIdx; } else { Output = ReloadOutputs[scalarIdx]; ++scalarIdx; } - LoadInst *load = new LoadInst(outputs[i]->getType(), Output, - outputs[i]->getName() + ".reload", - codeReplacer); + LoadInst *load = new LoadInst(Outputs[i]->getType(), Output, + Outputs[i]->getName() + ".reload", NewHeader); Reloads.push_back(load); - std::vector Users(outputs[i]->user_begin(), outputs[i]->user_end()); + std::vector Users(Outputs[i]->user_begin(), Outputs[i]->user_end()); for (unsigned u = 0, e = Users.size(); u != e; ++u) { Instruction *inst = cast(Users[u]); if (!Blocks.count(inst->getParent())) - inst->replaceUsesOfWith(outputs[i], load); + inst->replaceUsesOfWith(Outputs[i], load); } } // Now we can emit a switch statement using the call as a value. SwitchInst *TheSwitch = SwitchInst::Create(Constant::getNullValue(Type::getInt16Ty(Context)), - codeReplacer, 0, codeReplacer); + NewHeader, 0, NewHeader); // Since there may be multiple exits from the original region, make the new // function return an unsigned, switch on that number. This loop iterates @@ -1291,7 +1489,7 @@ CallInst *CodeExtractor::emitCallAndSwitchStatement(Function *newFunction, // destination, create one now! NewTarget = BasicBlock::Create(Context, OldTarget->getName() + ".exitStub", - newFunction); + NewFunction); unsigned SuccNum = switchVal++; Value *brVal = nullptr; @@ -1312,7 +1510,7 @@ CallInst *CodeExtractor::emitCallAndSwitchStatement(Function *newFunction, // Update the switch instruction. TheSwitch->addCase(ConstantInt::get(Type::getInt16Ty(Context), SuccNum), - OldTarget); + OldTarget); } for (BasicBlock *Block : Blocks) { @@ -1333,14 +1531,21 @@ CallInst *CodeExtractor::emitCallAndSwitchStatement(Function *newFunction, // Store the arguments right after the definition of output value. // This should be proceeded after creating exit stubs to be ensure that invoke // result restore will be placed in the outlined function. - Function::arg_iterator ScalarOutputArgBegin = newFunction->arg_begin(); - std::advance(ScalarOutputArgBegin, ScalarInputArgNo); - Function::arg_iterator AggOutputArgBegin = newFunction->arg_begin(); - std::advance(AggOutputArgBegin, ScalarInputArgNo + ScalarOutputArgNo); - - for (unsigned i = 0, e = outputs.size(), aggIdx = NumAggregatedInputs; i != e; + Function::arg_iterator ScalarOutputArgBegin = NewFunction->arg_begin(); + std::advance(ScalarOutputArgBegin, + ScalarInputArgNo + InputsBasePtrCands.size()); + Function::arg_iterator AggOutputArgBegin = NewFunction->arg_begin(); + std::advance(AggOutputArgBegin, ScalarInputArgNo + ScalarOutputArgNo + + InputsBasePtrCands.size() + + OutputsBasePtrCands.size()); + + for (unsigned i = 0, e = Outputs.size(), aggIdx = NumAggregatedInputs; i != e; ++i) { - auto *OutI = dyn_cast(outputs[i]); + // If the value is a struct, skip its base. + if (OutputsBasePtrCands.count(Outputs[i])) + ++ScalarOutputArgBegin; + + auto *OutI = dyn_cast(Outputs[i]); if (!OutI) continue; @@ -1356,29 +1561,29 @@ CallInst *CodeExtractor::emitCallAndSwitchStatement(Function *newFunction, InsertPt = std::next(OutI->getIterator()); Instruction *InsertBefore = &*InsertPt; - assert((InsertBefore->getFunction() == newFunction || + assert((InsertBefore->getFunction() == NewFunction || Blocks.count(InsertBefore->getParent())) && "InsertPt should be in new function"); - if (AggregateArgs && StructValues.contains(outputs[i])) { - assert(AggOutputArgBegin != newFunction->arg_end() && + if (AggregateArgs && StructValues.contains(Outputs[i])) { + assert(AggOutputArgBegin != NewFunction->arg_end() && "Number of aggregate output arguments should match " "the number of defined values"); Value *Idx[2]; Idx[0] = Constant::getNullValue(Type::getInt32Ty(Context)); Idx[1] = ConstantInt::get(Type::getInt32Ty(Context), aggIdx); GetElementPtrInst *GEP = GetElementPtrInst::Create( - StructArgTy, &*AggOutputArgBegin, Idx, "gep_" + outputs[i]->getName(), + StructArgTy, &*AggOutputArgBegin, Idx, "gep_" + Outputs[i]->getName(), InsertBefore); - new StoreInst(outputs[i], GEP, InsertBefore); + new StoreInst(Outputs[i], GEP, InsertBefore); ++aggIdx; // Since there should be only one struct argument aggregating // all the output values, we shouldn't increment AggOutputArgBegin, which // always points to the struct argument, in this case. } else { - assert(ScalarOutputArgBegin != newFunction->arg_end() && + assert(ScalarOutputArgBegin != NewFunction->arg_end() && "Number of scalar output arguments should match " "the number of defined values"); - new StoreInst(outputs[i], &*ScalarOutputArgBegin, InsertBefore); + new StoreInst(Outputs[i], &*ScalarOutputArgBegin, InsertBefore); ++ScalarOutputArgBegin; } } @@ -1631,7 +1836,7 @@ CodeExtractor::extractCodeRegion(const CodeExtractorAnalysisCache &CEAC) { Function * CodeExtractor::extractCodeRegion(const CodeExtractorAnalysisCache &CEAC, - ValueSet &inputs, ValueSet &outputs) { + ValueSet &Inputs, ValueSet &Outputs) { if (!isEligible()) return nullptr; @@ -1703,8 +1908,7 @@ CodeExtractor::extractCodeRegion(const CodeExtractorAnalysisCache &CEAC, // This takes place of the original loop BasicBlock *codeReplacer = BasicBlock::Create(header->getContext(), - "codeRepl", oldFunction, - header); + "codeRepl", oldFunction, header); // The new function needs a root node because other nodes can branch to the // head of the region, but the entry node of a function cannot have preds. @@ -1732,8 +1936,23 @@ CodeExtractor::extractCodeRegion(const CodeExtractorAnalysisCache &CEAC, findAllocas(CEAC, SinkingCands, HoistingCands, CommonExit); assert(HoistingCands.empty() || CommonExit); - // Find inputs to, outputs from the code region. - findInputsOutputs(inputs, outputs, SinkingCands); + ValueSet BitcastInstrs; + ValueMap InputsBasePtrCands; + ValueMap OutputsBasePtrCands; + // Find Inputs to, Outputs from the code region. + if (CJPipeline) { + // BitcastInstrs is used to record bitcasts that are derived pointers but + // not structs. + if (!findInputsOutputs(Inputs, Outputs, SinkingCands, BitcastInstrs)) { + return nullptr; + } + // Establish maps from derived struct ptr to its base ptr in Inputs and + // Outputs, respectively. + insertStructBaseInArguments(oldFunction, Inputs, InputsBasePtrCands); + insertStructBaseInArguments(oldFunction, Outputs, OutputsBasePtrCands); + } else { + findInputsOutputs(Inputs, Outputs, SinkingCands); + } // Now sink all instructions which only have non-phi uses inside the region. // Group the allocas at the start of the block, so that any bitcast uses of @@ -1761,17 +1980,17 @@ CodeExtractor::extractCodeRegion(const CodeExtractorAnalysisCache &CEAC, cast(II)->moveBefore(TI); } - // Collect objects which are inputs to the extraction region and also + // Collect objects which are Inputs to the extraction region and also // referenced by lifetime start markers within it. The effects of these // markers must be replicated in the calling function to prevent the stack // coloring pass from merging slots which store input objects. ValueSet LifetimesStart; eraseLifetimeMarkersOnInputs(Blocks, SinkingCands, LifetimesStart); - // Construct new function based on inputs/outputs & add allocas for all defs. - Function *newFunction = - constructFunction(inputs, outputs, header, newFuncRoot, codeReplacer, - oldFunction, oldFunction->getParent()); + // Construct new function based on Inputs/Outputs & add allocas for all defs. + Function *newFunction = constructFunction( + Inputs, Outputs, BitcastInstrs, InputsBasePtrCands, OutputsBasePtrCands, + header, newFuncRoot, codeReplacer, oldFunction, oldFunction->getParent()); // Update the entry count of the function. if (BFI) { @@ -1783,7 +2002,8 @@ CodeExtractor::extractCodeRegion(const CodeExtractorAnalysisCache &CEAC, } CallInst *TheCall = - emitCallAndSwitchStatement(newFunction, codeReplacer, inputs, outputs); + emitCallAndSwitchStatement(newFunction, codeReplacer, Inputs, Outputs, + InputsBasePtrCands, OutputsBasePtrCands); moveCodeToFunction(newFunction); @@ -1796,6 +2016,10 @@ CodeExtractor::extractCodeRegion(const CodeExtractorAnalysisCache &CEAC, if (oldFunction->hasPersonalityFn()) newFunction->setPersonalityFn(oldFunction->getPersonalityFn()); + // Propagate GC to the new function if there is one. + if (oldFunction->hasGC()) + newFunction->setGC(oldFunction->getGC()); + // Update the branch weights for the exit block. if (BFI && NumExitBlocks > 1) calculateNewCallTerminatorWeights(codeReplacer, ExitWeights, BPI); diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp index 2f1d0c2f9..09f1c9d3c 100644 --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -2485,6 +2485,16 @@ void llvm::removeUnwindEdge(BasicBlock *BB, DomTreeUpdater *DTU) { /// otherwise. bool llvm::removeUnreachableBlocks(Function &F, DomTreeUpdater *DTU, MemorySSAUpdater *MSSAU) { + // WorkAround: For N2CStub functions, the reachable block cannot be + // deleted. If the callee function contains the unreachable block of + // ThrowException, here, the runtime EH needs to unwind based on the return + // instruction. + // In the future, if EH does not rely on the epilog to return to unwind, + // remove the process. + if (F.hasFnAttribute("cjstub")) { + return false; + } + SmallPtrSet Reachable; bool Changed = markAliveBlocks(F, Reachable, DTU); @@ -2588,6 +2598,9 @@ void llvm::combineMetadata(Instruction *K, const Instruction *J, case LLVMContext::MD_preserve_access_index: // Preserve !preserve.access.index in K. break; + case LLVMContext::MD_cj_agg: + K->setMetadata(Kind, JMD); + break; } } // Set !invariant.group from J if J has it. If both instructions have it @@ -2608,7 +2621,7 @@ void llvm::combineMetadataForCSE(Instruction *K, const Instruction *J, LLVMContext::MD_noalias, LLVMContext::MD_range, LLVMContext::MD_invariant_load, LLVMContext::MD_nonnull, LLVMContext::MD_invariant_group, LLVMContext::MD_align, - LLVMContext::MD_dereferenceable, + LLVMContext::MD_dereferenceable, LLVMContext::MD_cj_agg, LLVMContext::MD_dereferenceable_or_null, LLVMContext::MD_access_group, LLVMContext::MD_preserve_access_index}; combineMetadata(K, J, KnownIDs, KDominatesJ); @@ -2642,6 +2655,11 @@ void llvm::copyMetadataForLoad(LoadInst &Dest, const LoadInst &Source) { case LLVMContext::MD_nontemporal: case LLVMContext::MD_mem_parallel_loop_access: case LLVMContext::MD_access_group: + case LLVMContext::MD_virtual_call: + case LLVMContext::MD_intro_type: + case LLVMContext::MD_func_table: + case LLVMContext::MD_untrusted_ref: + case LLVMContext::MD_obj_type: // All of these directly apply. Dest.setMetadata(ID, N); break; @@ -2693,7 +2711,8 @@ void llvm::patchReplacementInstruction(Instruction *I, Value *Repl) { LLVMContext::MD_noalias, LLVMContext::MD_range, LLVMContext::MD_fpmath, LLVMContext::MD_invariant_load, LLVMContext::MD_invariant_group, LLVMContext::MD_nonnull, - LLVMContext::MD_access_group, LLVMContext::MD_preserve_access_index}; + LLVMContext::MD_access_group, LLVMContext::MD_preserve_access_index, + LLVMContext::MD_cj_agg}; combineMetadata(ReplInst, I, KnownIDs, false); } @@ -2762,7 +2781,8 @@ bool llvm::callsGCLeafFunction(const CallBase *Call, return IID != Intrinsic::experimental_gc_statepoint && IID != Intrinsic::experimental_deoptimize && IID != Intrinsic::memcpy_element_unordered_atomic && - IID != Intrinsic::memmove_element_unordered_atomic; + IID != Intrinsic::memmove_element_unordered_atomic && + IID != Intrinsic::cj_gc_statepoint; } } diff --git a/llvm/lib/Transforms/Utils/LoopVersioning.cpp b/llvm/lib/Transforms/Utils/LoopVersioning.cpp index 6309eed79..9eaaa1cc7 100644 --- a/llvm/lib/Transforms/Utils/LoopVersioning.cpp +++ b/llvm/lib/Transforms/Utils/LoopVersioning.cpp @@ -231,9 +231,7 @@ void LoopVersioning::annotateInstWithNoAlias(Instruction *VersionedInst, return; LLVMContext &Context = VersionedLoop->getHeader()->getContext(); - const Value *Ptr = isa(OrigInst) - ? cast(OrigInst)->getPointerOperand() - : cast(OrigInst)->getPointerOperand(); + const Value *Ptr = getLoadStorePointerOperand(OrigInst); // Find the group for the pointer and then add the scope metadata. auto Group = PtrToGroup.find(Ptr); diff --git a/llvm/lib/Transforms/Utils/MemoryTaggingSupport.cpp b/llvm/lib/Transforms/Utils/MemoryTaggingSupport.cpp index a1029475c..dec481678 100644 --- a/llvm/lib/Transforms/Utils/MemoryTaggingSupport.cpp +++ b/llvm/lib/Transforms/Utils/MemoryTaggingSupport.cpp @@ -6,7 +6,7 @@ //===----------------------------------------------------------------------===// // // This file declares common infrastructure for HWAddressSanitizer and -// Aarch64StackTagging. +// AArch64StackTagging. // //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp index 09a83f1ea..17bdd7045 100644 --- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp +++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp @@ -70,6 +70,12 @@ class SCCPInstVisitor : public InstVisitor { SmallPtrSet BBExecutable; // The BBs that are executable. DenseMap ValueState; // The state each value is in. + DenseMap LoopPhiStates; + // Record the PHINode of the complete Lattice that cannot be obtained in a + // single loop iteration. + DenseSet PartialStates; + DenseMap> PhiInfo; + DenseSet VisitedPhi; /// StructValueState - This maintains ValueState for values that have /// StructType, for example for formal arguments, calls, insertelement, etc. @@ -131,6 +137,8 @@ private: return dyn_cast_or_null(getConstant(IV)); } + void updatePartialStates(LoopInfo *LI, PHINode &PN); + // pushToWorkList - Helper for markConstant/markOverdefined void pushToWorkList(ValueLatticeElement &IV, Value *V); @@ -705,6 +713,22 @@ bool SCCPInstVisitor::isEdgeFeasible(BasicBlock *From, BasicBlock *To) const { return KnownFeasibleEdges.count(Edge(From, To)); } +void SCCPInstVisitor::updatePartialStates(LoopInfo *LI, PHINode &PN) { + Loop *L = LI->getLoopFor(PN.getParent()); + if (!L) + return; + for (unsigned I = 0, E = PN.getNumIncomingValues(); I != E; ++I) { + auto *BB = PN.getIncomingBlock(I); + if (L->contains(BB) && !L->isLoopLatch(BB) && + !isEdgeFeasible(BB, PN.getParent())) { + PartialStates.insert(&PN); + return; + } + } + if (PartialStates.count(&PN)) + PartialStates.erase(&PN); +} + // visit Implementations - Something changed in this instruction, either an // operand made a transition, or the instruction is newly executable. Change // the value type of I to reflect these changes if appropriate. This method @@ -744,10 +768,105 @@ void SCCPInstVisitor::visitPHINode(PHINode &PN) { // constant. If they are constant and don't agree, the PHI is a constant // range. If there are no executable operands, the PHI remains unknown. ValueLatticeElement PhiState = getValueState(&PN); + + // Check whether PN is in a loop header, if it is, then we can do some loop + // peeling optimize. + Function *F = PN.getParent()->getParent(); + bool AnalysisValid = AnalysisResults.count(F) && AnalysisResults[F].DT && + AnalysisResults[F].LI; + bool IsUsersKnown = llvm::all_of(PN.users(), [this](User *U) { + auto *UI = dyn_cast(U); + if (auto *PHI = dyn_cast(UI)) { + for (unsigned I = 0, E = PHI->getNumIncomingValues(); I != E; ++I) + if (!isEdgeFeasible(PHI->getIncomingBlock(I), PHI->getParent())) + return false; + return true; + } + return UI && !UI->getType()->isStructTy() && + !getValueState(UI).isUnknownOrUndef(); + }); + const unsigned int IncomingSZ = 2; // Now, only optimize PN for 2 values. + + // Visit instructions is after visit basic blocks and take DFS. So once + // PartialState disappears, it does not occur again, because this state is + // top-down update. + if (AnalysisValid) + updatePartialStates(AnalysisResults[F].LI, PN); + + if (PN.getNumIncomingValues() == IncomingSZ && !PhiInfo.count(&PN) && + AnalysisValid && IsUsersKnown) { + bool IsInLoop = false; + bool AllConstants = true; + bool AllEdgeFisable = true; + Loop *L = AnalysisResults[F].LI->getLoopFor(PN.getParent()); + ValueLatticeElement InitLattice = ValueLatticeElement(); + + // Now, only the loop that has one exiting edge can be optimized, and the PN + // should be in the loop header. + SmallVector Edges; + if (!L || (L->getExitEdges(Edges), Edges.size() != 1) || + L->getHeader() != PN.getParent()) + IsInLoop = AllConstants = false; + // If there is no incomplete state, Phi Lattice optimization can be + // performed safely. This is still conservative, since dependencies between + // instructions are not taken into account. + else if (!PartialStates.empty()) { + AllEdgeFisable = false; + } else { + unsigned BBInLoop = 0; + for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i) { + if (!isEdgeFeasible(PN.getIncomingBlock(i), PN.getParent())) { + AllEdgeFisable = false; + break; + } + if (L->contains(PN.getIncomingBlock(i))) { + ++BBInLoop; + continue; + } + if (auto *C = dyn_cast(PN.getIncomingValue(i))) { + ValueLatticeElement VLE; + VLE.markConstantRange( + ConstantRange(C->getUniqueInteger(), C->getUniqueInteger() + 1)); + InitLattice.mergeIn(VLE); + } else + AllConstants = false; + } + IsInLoop = BBInLoop == 1; + } + + // Now, all the information we have up to phi + if (AllEdgeFisable) + PhiInfo.insert( + std::make_pair(&PN, std::make_pair(IsInLoop, AllConstants))); + + // LoopPhiStates is initialized only when all edges except the loop-back + // edge are visible. + if (IsInLoop && AllConstants && AllEdgeFisable && !LoopPhiStates.count(&PN)) + LoopPhiStates[&PN] = InitLattice; + } + + if (PN.getNumIncomingValues() == IncomingSZ && LoopPhiStates.count(&PN) && + !VisitedPhi.count(&PN)) { + ValueState[&PN] = PhiState = ValueLatticeElement(); + VisitedPhi.insert(&PN); + } + + bool IsInLoop = PhiInfo.count(&PN) ? PhiInfo[&PN].first : false; + bool AllConstants = PhiInfo.count(&PN) ? PhiInfo[&PN].second : false; + for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i) { if (!isEdgeFeasible(PN.getIncomingBlock(i), PN.getParent())) continue; + if (PN.getNumIncomingValues() == IncomingSZ && IsInLoop && AllConstants) { + if (!AnalysisResults[F] + .LI->getLoopFor(PN.getParent()) + ->contains(PN.getIncomingBlock(i))) { + ++NumActiveIncoming; + continue; + } + } + ValueLatticeElement IV = getValueState(PN.getIncomingValue(i)); PhiState.mergeIn(IV); NumActiveIncoming++; @@ -816,7 +935,16 @@ void SCCPInstVisitor::visitCastInst(CastInst &I) { if (OpSt.isUnknownOrUndef()) return; - if (Constant *OpC = getConstant(OpSt)) { + auto IsConstant = [this](CastInst &I, ValueLatticeElement &OpSt) { + if (isa(I.getOperand(0)) && LoopPhiStates.count(I.getOperand(0))) { + ValueLatticeElement V = LoopPhiStates[I.getOperand(0)]; + V.mergeIn(OpSt); + return getConstant(V); + } + return getConstant(OpSt); + }; + + if (Constant *OpC = IsConstant(I, OpSt)) { // Fold the constant as we build. Constant *C = ConstantFoldCastOperand(I.getOpcode(), OpC, I.getType(), DL); markConstant(&I, C); @@ -1421,6 +1549,9 @@ void SCCPInstVisitor::solve() { visit(BB); } } + + for (auto &[PN, VLE] : LoopPhiStates) + ValueState[PN].mergeIn(VLE); } /// While solving the dataflow for a function, we don't compute a result for diff --git a/llvm/lib/Transforms/Utils/SSAUpdater.cpp b/llvm/lib/Transforms/Utils/SSAUpdater.cpp index 37019e3bf..01a0b2a02 100644 --- a/llvm/lib/Transforms/Utils/SSAUpdater.cpp +++ b/llvm/lib/Transforms/Utils/SSAUpdater.cpp @@ -22,6 +22,7 @@ #include "llvm/IR/DebugLoc.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" #include "llvm/IR/Use.h" #include "llvm/IR/Value.h" @@ -34,6 +35,10 @@ using namespace llvm; +namespace llvm { +extern cl::opt LICMDisableBarrier; +} // namespace llvm + #define DEBUG_TYPE "ssaupdater" using AvailableValsTy = DenseMap; @@ -325,8 +330,14 @@ LoadAndStorePromoter(ArrayRef Insts, const Value *SomeVal; if (const LoadInst *LI = dyn_cast(Insts[0])) SomeVal = LI; - else - SomeVal = cast(Insts[0])->getOperand(0); + else if (const StoreInst *SI = dyn_cast(Insts[0])) + SomeVal = SI->getOperand(0); + else { + const IntrinsicInst *II = dyn_cast(Insts[0]); + assert(II && II->getIntrinsicID() == Intrinsic::cj_gcwrite_ref && + "current LICM only support gcwrite"); + SomeVal = II->getOperand(0); + } if (BaseName.empty()) BaseName = SomeVal->getName(); @@ -362,6 +373,11 @@ void LoadAndStorePromoter::run(const SmallVectorImpl &Insts) { if (StoreInst *SI = dyn_cast(User)) { updateDebugInfo(SI); SSA.AddAvailableValue(BB, SI->getOperand(0)); + } else if (IntrinsicInst *II = dyn_cast(User); + II && !LICMDisableBarrier) { + assert(II->getIntrinsicID() == Intrinsic::cj_gcwrite_ref); + updateDebugInfo(II); + SSA.AddAvailableValue(BB, II->getOperand(0)); } else // Otherwise it is a load, queue it to rewrite as a live-in load. LiveInLoads.push_back(cast(User)); @@ -372,7 +388,7 @@ void LoadAndStorePromoter::run(const SmallVectorImpl &Insts) { // Otherwise, check to see if this block is all loads. bool HasStore = false; for (Instruction *I : BlockUses) { - if (isa(I)) { + if (isa(I) || (isa(I) && !LICMDisableBarrier)) { HasStore = true; break; } @@ -419,6 +435,15 @@ void LoadAndStorePromoter::run(const SmallVectorImpl &Insts) { // Remember that this is the active value in the block. StoredValue = SI->getOperand(0); } + + if (IntrinsicInst *II = dyn_cast(&I)) { + if (LICMDisableBarrier || + II->getIntrinsicID() != Intrinsic::cj_gcwrite_ref || + !isInstInList(II, Insts)) + continue; + updateDebugInfo(II); + StoredValue = II->getOperand(0); + } } // The last stored value that happened is the live-out for the block. diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index 180608167..56f97e7c0 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -57,6 +57,7 @@ #include "llvm/IR/NoFolder.h" #include "llvm/IR/Operator.h" #include "llvm/IR/PatternMatch.h" +#include "llvm/IR/SafepointIRVerifier.h" #include "llvm/IR/Type.h" #include "llvm/IR/Use.h" #include "llvm/IR/User.h" @@ -90,6 +91,10 @@ using namespace PatternMatch; #define DEBUG_TYPE "simplifycfg" +namespace llvm { +extern cl::opt CJPipeline; +} + cl::opt llvm::RequireAndPreserveDomTree( "simplifycfg-require-and-preserve-domtree", cl::Hidden, @@ -1551,7 +1556,8 @@ bool SimplifyCFGOpt::HoistThenElseCodeToIf(BranchInst *BI, LLVMContext::MD_dereferenceable_or_null, LLVMContext::MD_mem_parallel_loop_access, LLVMContext::MD_access_group, - LLVMContext::MD_preserve_access_index}; + LLVMContext::MD_preserve_access_index, + LLVMContext::MD_cj_agg}; combineMetadata(I1, I2, KnownIDs, true); // I1 and I2 are being combined into a single instruction. Its debug @@ -6213,6 +6219,20 @@ static void reuseTableCompare( } } +static bool isPtrContainsGCPtr(Type *Ty) { + if (!Ty->isPointerTy()) { + return false; + } + Type *ET = Ty->getNonOpaquePointerElementType(); + if (isGCPointerType(ET)) { + return true; + } + if (isMemoryContainsGCPtrType(Ty)) { + return true; + } + return false; +} + /// If the switch is only used to initialize one or more phi nodes in a common /// successor block with different constant values, replace the switch with /// lookup tables. @@ -6284,6 +6304,13 @@ static bool SwitchToLookupTable(SwitchInst *SI, IRBuilder<> &Builder, // Keep track of the result types. for (PHINode *PHI : PHIs) { ResultTypes[PHI] = ResultLists[PHI][0].second->getType(); + // When the type is one of i8 addrspace(1)**, i8 addrspace(1)* addrspace(1)*, + // %record *, do nothing for cangjie. + // Because it can genarate i8 addrspace(1)***, + // i8 addrspace(1)* addrspace(1)**, %record **. + if (CJPipeline && isPtrContainsGCPtr(PHI->getType())) { + return false; + } } uint64_t NumResults = ResultLists[PHIs[0]].size(); diff --git a/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp b/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp index 0ab79a32f..ddbf24d50 100644 --- a/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp @@ -442,8 +442,6 @@ bool SimplifyIndvar::eliminateOverflowIntrinsic(WithOverflowInst *WO) { else NewResult->setHasNoUnsignedWrap(true); - SmallVector ToDelete; - for (auto *U : WO->users()) { if (auto *EVI = dyn_cast(U)) { if (EVI->getIndices()[0] == 1) @@ -452,16 +450,12 @@ bool SimplifyIndvar::eliminateOverflowIntrinsic(WithOverflowInst *WO) { assert(EVI->getIndices()[0] == 0 && "Only two possibilities!"); EVI->replaceAllUsesWith(NewResult); } - ToDelete.push_back(EVI); + DeadInsts.emplace_back(EVI); } } - for (auto *EVI : ToDelete) - EVI->eraseFromParent(); - if (WO->use_empty()) - WO->eraseFromParent(); - + DeadInsts.emplace_back(WO); Changed = true; return true; } diff --git a/llvm/lib/Transforms/Utils/VNCoercion.cpp b/llvm/lib/Transforms/Utils/VNCoercion.cpp index 264da2187..88794888c 100644 --- a/llvm/lib/Transforms/Utils/VNCoercion.cpp +++ b/llvm/lib/Transforms/Utils/VNCoercion.cpp @@ -223,6 +223,22 @@ int analyzeLoadFromClobberingStore(Type *LoadTy, Value *LoadPtr, DL); } +int anaLyzeLoadFromClobberingGCWrite(Type *LoadTy, Value *LoadPtr, + IntrinsicInst *DepII, + const DataLayout &DL) { + auto *StoredVal = DepII->getOperand(0); + + // check whether StoredVal type can be converted to LoadTy. + if (!canCoerceMustAliasedValueToLoad(StoredVal, LoadTy, DL)) + return -1; + + // if we can get load value from gcwrite value, return the offset of LoadPtr + // relative to StorePtr. + return analyzeLoadFromClobberingWrite( + LoadTy, LoadPtr, DepII->getOperand(2), + DL.getTypeSizeInBits(StoredVal->getType()).getFixedSize(), DL); +} + /// Looks at a memory location for a load (specified by MemLocBase, Offs, and /// Size) and compares it against a load. /// diff --git a/llvm/lib/Transforms/Utils/ValueMapper.cpp b/llvm/lib/Transforms/Utils/ValueMapper.cpp index 894730367..b1d486e4f 100644 --- a/llvm/lib/Transforms/Utils/ValueMapper.cpp +++ b/llvm/lib/Transforms/Utils/ValueMapper.cpp @@ -949,6 +949,8 @@ void Mapper::remapInstruction(Instruction *I) { if (!TypeMapper) return; + TypeMapper->mapMetadata(I->getContext(), I); + // If the instruction's type is being remapped, do so now. if (auto *CB = dyn_cast(I)) { SmallVector Tys; diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp index 183ba86ab..3a2b10d68 100644 --- a/llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp +++ b/llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp @@ -16,6 +16,7 @@ #include "llvm/Transforms/Vectorize/LoopVectorizationLegality.h" #include "llvm/Analysis/Loads.h" +#include "llvm/Analysis/LoopAccessAnalysis.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/TargetLibraryInfo.h" @@ -23,6 +24,7 @@ #include "llvm/Analysis/ValueTracking.h" #include "llvm/Analysis/VectorUtils.h" #include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/IR/PatternMatch.h" #include "llvm/Transforms/Utils/SizeOpts.h" #include "llvm/Transforms/Vectorize/LoopVectorize.h" @@ -72,7 +74,7 @@ static cl::opt LoopVectorizeHints::SK_PreferScalable, "on", "Scalable vectorization is available and favored when the " "cost is inconclusive."))); - +extern cl::opt EnableCJGCWriteLoopVectorization; /// Maximum vectorization interleave count. static const unsigned MaxInterleaveFactor = 16; @@ -426,15 +428,15 @@ static bool hasOutsideLoopUser(const Loop *TheLoop, Instruction *Inst, } /// Returns true if A and B have same pointer operands or same SCEVs addresses -static bool storeToSameAddress(ScalarEvolution *SE, StoreInst *A, - StoreInst *B) { +static bool storeToSameAddress(ScalarEvolution *SE, Instruction *A, + Instruction *B) { // Compare store if (A == B) return true; // Otherwise Compare pointers - Value *APtr = A->getPointerOperand(); - Value *BPtr = B->getPointerOperand(); + Value *APtr = getLoadStorePointerOperand(A); + Value *BPtr = getLoadStorePointerOperand(B); if (APtr == BPtr) return true; @@ -737,8 +739,8 @@ bool LoopVectorizationLegality::canVectorizeInstrs() { // * Have a vector version available. auto *CI = dyn_cast(&I); - if (CI && !getVectorIntrinsicIDForCall(CI, TLI) && - !isa(CI) && + if (CI && !isVectorizableCJGCWrite(&I) && + !getVectorIntrinsicIDForCall(CI, TLI) && !isa(CI) && !(CI->getCalledFunction() && TLI && (!VFDatabase::getMappings(*CI).empty() || isTLIScalarize(*TLI, *CI)))) { @@ -772,7 +774,7 @@ bool LoopVectorizationLegality::canVectorizeInstrs() { // Some intrinsics have scalar arguments and should be same in order for // them to be vectorized (i.e. loop invariant). - if (CI) { + if (CI && !isVectorizableCJGCWrite(CI)) { auto *SE = PSE.getSE(); Intrinsic::ID IntrinID = getVectorIntrinsicIDForCall(CI, TLI); for (unsigned i = 0, e = CI->arg_size(); i != e; ++i) @@ -786,6 +788,10 @@ bool LoopVectorizationLegality::canVectorizeInstrs() { } } + if (isVectorizableCJGCWrite(&I)) { + TheLoop->HasGCWriteInLoop = true; + } + // Check that the instruction return type is vectorizable. // Also, we can't vectorize extractelement instructions. if ((!VectorType::isValidElementType(I.getType()) && @@ -923,9 +929,9 @@ bool LoopVectorizationLegality::canVectorizeMemory() { // invariant address won't alias with any other objects. if (!LAI->getStoresToInvariantAddresses().empty()) { // For each invariant address, check its last stored value is unconditional. - for (StoreInst *SI : LAI->getStoresToInvariantAddresses()) { - if (isInvariantStoreOfReduction(SI) && - blockNeedsPredication(SI->getParent())) { + for (Instruction *CurInst : LAI->getStoresToInvariantAddresses()) { + if (isInvariantStoreOfReduction(CurInst) && + blockNeedsPredication(CurInst->getParent())) { reportVectorizationFailure( "We don't allow storing to uniform addresses", "write of conditional recurring variant value to a loop " @@ -943,9 +949,9 @@ bool LoopVectorizationLegality::canVectorizeMemory() { // currently rejected earlier in LoopAccessInfo::analyzeLoop. In case this // behaviour changes we have to modify this code. ScalarEvolution *SE = PSE.getSE(); - SmallVector UnhandledStores; - for (StoreInst *SI : LAI->getStoresToInvariantAddresses()) { - if (isInvariantStoreOfReduction(SI)) { + SmallVector UnhandledStores; + for (Instruction *CurInst : LAI->getStoresToInvariantAddresses()) { + if (isInvariantStoreOfReduction(CurInst)) { // Earlier stores to this address are effectively deadcode. // With opaque pointers it is possible for one pointer to be used with // different sizes of stored values: @@ -956,14 +962,14 @@ bool LoopVectorizationLegality::canVectorizeMemory() { // values are same. // TODO: Check that bitwidth of unhandled store is smaller then the // one that overwrites it and add a test. - erase_if(UnhandledStores, [SE, SI](StoreInst *I) { - return storeToSameAddress(SE, SI, I) && - I->getValueOperand()->getType() == - SI->getValueOperand()->getType(); + erase_if(UnhandledStores, [SE, CurInst](Instruction *I) { + return storeToSameAddress(SE, CurInst, I) && + getStoreValueOperand(I)->getType() == + getStoreValueOperand(CurInst)->getType(); }); continue; } - UnhandledStores.push_back(SI); + UnhandledStores.push_back(CurInst); } bool IsOK = UnhandledStores.empty(); @@ -1009,7 +1015,7 @@ bool LoopVectorizationLegality::canVectorizeFPMath( })); } -bool LoopVectorizationLegality::isInvariantStoreOfReduction(StoreInst *SI) { +bool LoopVectorizationLegality::isInvariantStoreOfReduction(Instruction *SI) { return any_of(getReductionVars(), [&](auto &Reduction) -> bool { const RecurrenceDescriptor &RdxDesc = Reduction.second; return RdxDesc.IntermediateStore == SI; @@ -1023,7 +1029,8 @@ bool LoopVectorizationLegality::isInvariantAddressOfReduction(Value *V) { return false; ScalarEvolution *SE = PSE.getSE(); - Value *InvariantAddress = RdxDesc.IntermediateStore->getPointerOperand(); + Value *InvariantAddress = + getLoadStorePointerOperand(RdxDesc.IntermediateStore); return V == InvariantAddress || SE->getSCEV(V) == SE->getSCEV(InvariantAddress); }); diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp index 5fd4e45d8..158c16518 100644 --- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp +++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp @@ -366,6 +366,15 @@ cl::opt PrintVPlansInDotFormat( "vplan-print-in-dot-format", cl::init(false), cl::Hidden, cl::desc("Use dot format instead of plain text when dumping VPlans")); +static cl::opt ScalarCostCalculationCoefForCangjie( + "scaler-cost-calculation-coefficient-for-cj", cl::init(100), cl::Hidden, + cl::desc("Coefficient for calculating scalar cost. " + "When vector cost < coef / 100 * scalar cost, vectorization is " + "more profitable. For example, coef = 90 means that benifits " + "greater than 10% are considered more profitable.")); +namespace llvm { +extern cl::opt CJPipeline; +} /// A helper function that returns true if the given type is irregular. The /// type is irregular if its allocated size doesn't equal the store size of an /// element of the corresponding vector type. @@ -477,6 +486,9 @@ public: void widenCallInstruction(CallInst &CI, VPValue *Def, VPUser &ArgOperands, VPTransformState &State); + void widenCJGCWrite(CallInst &CI, const VPUser &ArgOperands, + VPTransformState &State); + /// Fix the vectorized code, taking care of header phi's, live-outs, and more. void fixVectorizedLoop(VPTransformState &State, VPlan &Plan); @@ -1552,6 +1564,8 @@ public: InstructionCost getVectorCallCost(CallInst *CI, ElementCount VF, bool &NeedToScalarize) const; + InstructionCost getGCWriteCost(CallInst *CI, ElementCount VF) const; + /// Returns true if the per-lane cost of VectorizationFactor A is lower than /// that of B. bool isMoreProfitable(const VectorizationFactor &A, @@ -3419,6 +3433,9 @@ static void cse(BasicBlock *BB) { InstructionCost LoopVectorizationCostModel::getVectorCallCost(CallInst *CI, ElementCount VF, bool &NeedToScalarize) const { + if (isVectorizableCJGCWrite(CI)) { + return getGCWriteCost(CI, VF); + } Function *F = CI->getCalledFunction(); Type *ScalarRetTy = CI->getType(); SmallVector Tys, ScalarTys; @@ -3465,6 +3482,42 @@ LoopVectorizationCostModel::getVectorCallCost(CallInst *CI, ElementCount VF, return Cost; } +InstructionCost +LoopVectorizationCostModel::getGCWriteCost(CallInst *CI, + ElementCount VF) const { + if (!VF.isVector()) { + return 1; + } + + InstWidening Decision = getWideningDecision(CI, VF); + if (Decision != CM_Widen && Decision != CM_Widen_Reverse) { + return InstructionCost::getMax(); + } + + Type *AT = CI->getArgOperand(0)->getType(); + unsigned BitWidth = 0; + if (AT->getTypeID() == Type::IntegerTyID) { + BitWidth = CI->getArgOperand(0)->getType()->getIntegerBitWidth(); + } else if (AT->getTypeID() == Type::HalfTyID) { + BitWidth = 16; // 16: fp16's bitwidth + } else if (AT->getTypeID() == Type::FloatTyID) { + BitWidth = 32; // 32: float's bitwidth + } else if (AT->getTypeID() == Type::DoubleTyID) { + BitWidth = 64; // 64: double's bitwidth + } else { + report_fatal_error("unsupport type for cj-gc-write vectorization!"); + } + unsigned VFWidth = VF.getKnownMinValue(); + // support <2 * f64>, <4 * f32>, <8 * f16>, <2 * i64>, <4 * i32>, + // <8 * i16>, <16 * i8> and <8 *i8> now. + // set related-cj_gcwrite's cost to 1 since vectorized can reduce + // the times of getGCPhase + if (BitWidth * VFWidth == 128 || ((BitWidth == 8) && (VFWidth == 8))) { + return 1; + } + return InstructionCost::getMax(); +} + static Type *MaybeVectorizeType(Type *Elt, ElementCount VF) { if (VF.isScalar() || (!Elt->isIntOrPtrTy() && !Elt->isFloatingPointTy())) return Elt; @@ -4170,9 +4223,89 @@ bool InnerLoopVectorizer::useOrderedReductions( return Cost->useOrderedReductions(RdxDesc); } +static Value *createVecPtr(Instruction *I, unsigned Part, Value *Ptr, + VPTransformState &State, bool Reverse) { + Type *ScalarDataTy = getLoadStoreType(I); + // Calculate the pointer for the specific unroll-part. + GetElementPtrInst *PartPtr = nullptr; + bool InBounds = false; + if (auto *Gep = dyn_cast(Ptr->stripPointerCasts())) { + InBounds = Gep->isInBounds(); + } + auto &Builder = State.Builder; + if (Reverse) { + // If the address is consecutive but reversed, then the + // wide store needs to start at the last vector element. + // RunTimeVF = VScale * VF.getKnownMinValue() + // For fixed-width VScale is 1, then RunTimeVF = VF.getKnownMinValue() + Value *RunTimeVF = getRuntimeVF(Builder, Builder.getInt32Ty(), State.VF); + // NumElt = -Part * RunTimeVF + Value *NumElt = Builder.CreateMul(Builder.getInt32(-Part), RunTimeVF); + // LastLane = 1 - RunTimeVF + Value *LastLane = Builder.CreateSub(Builder.getInt32(1), RunTimeVF); + PartPtr = + cast(Builder.CreateGEP(ScalarDataTy, Ptr, NumElt)); + PartPtr->setIsInBounds(InBounds); + PartPtr = cast( + Builder.CreateGEP(ScalarDataTy, PartPtr, LastLane)); + } else { + Value *Increment = + createStepForVF(Builder, Builder.getInt32Ty(), State.VF, Part); + PartPtr = cast( + Builder.CreateGEP(ScalarDataTy, Ptr, Increment)); + } + PartPtr->setIsInBounds(InBounds); + + return PartPtr; +} + +void InnerLoopVectorizer::widenCJGCWrite(CallInst &CI, + const VPUser &ArgOperands, + VPTransformState &State) { + auto *DataTy = VectorType::get(getLoadStoreType(&CI), State.VF); + auto &CurBuilder = State.Builder; + State.setDebugLocFromInst(&CI); + LoopVectorizationCostModel::InstWidening Decision = + Cost->getWideningDecision(&CI, State.VF); + if (Decision != LoopVectorizationCostModel::CM_Widen && + Decision != LoopVectorizationCostModel::CM_Widen_Reverse) { + report_fatal_error("only support consentive ptr"); + return; + } + bool Reverse = (Decision == LoopVectorizationCostModel::CM_Widen_Reverse); + Value *Ptr = State.get(ArgOperands.getOperand(2), VPIteration(0, 0)); + auto *Func = Intrinsic::getDeclaration( + CI.getModule(), llvm::Intrinsic::cj_gcwrite_ref); + + for (unsigned Part = 0; Part < State.UF; ++Part) { + Value *StoredVal = State.get(ArgOperands.getOperand(0), Part); + if (Reverse) { + // If we store to reverse consecutive memory locations, then we need + // to reverse the order of elements in the stored value. + StoredVal = Builder.CreateVectorReverse(StoredVal, "reverse"); + // We don't want to update the value in the map as it might be used in + // another expression. So don't call resetVectorValue(StoredVal). + } + + Value *PartPtr = createVecPtr(&CI, Part, Ptr, State, Reverse); + unsigned AddressSpace = Ptr->getType()->getPointerAddressSpace(); + Value *StoreVecPtr = Builder.CreateBitCast(PartPtr, DataTy->getPointerTo(AddressSpace)); + auto *NewCI = + CurBuilder.CreateCall(Func, {StoredVal, CI.getOperand(1), StoreVecPtr}); + State.addMetadata(NewCI, &CI); + } + + return; +} + void InnerLoopVectorizer::widenCallInstruction(CallInst &CI, VPValue *Def, VPUser &ArgOperands, VPTransformState &State) { + if (isVectorizableCJGCWrite(&CI)) { + widenCJGCWrite(CI, ArgOperands, State); + return; + } + assert(!isa(CI) && "DbgInfoIntrinsic should have been dropped during VPlan construction"); State.setDebugLocFromInst(&CI); @@ -4431,6 +4564,9 @@ bool LoopVectorizationCostModel::isScalarWithPredication( Instruction *I, ElementCount VF) const { if (!blockNeedsPredicationForAnyReason(I->getParent())) return false; + if (isVectorizableCJGCWrite(I)) { + return false; + } switch(I->getOpcode()) { default: break; @@ -4532,7 +4668,8 @@ bool LoopVectorizationCostModel::interleavedAccessCanBeWidened( bool LoopVectorizationCostModel::memoryInstructionCanBeWidened( Instruction *I, ElementCount VF) { // Get and ensure we have a valid memory instruction. - assert((isa(I)) && "Invalid memory instruction"); + assert((isa(I) || isVectorizableCJGCWrite(I)) && + "Invalid memory instruction"); auto *Ptr = getLoadStorePointerOperand(I); auto *ScalarTy = getLoadStoreType(I); @@ -5257,6 +5394,14 @@ bool LoopVectorizationCostModel::isMoreProfitable( if (A.Width.isScalable() && !B.Width.isScalable()) return (CostA * B.Width.getFixedValue()) <= (CostB * EstimatedWidthA); + if (EstimatedWidthB == 1 && CJPipeline) { + unsigned Coef = ScalarCostCalculationCoefForCangjie; + // To avoid the need for FP division: + // (CostA / A.Width) * 100 < (CostB / B.Width) * Coef + // <=> (CostA * B.Width) * 100 < (CostB * A.Width) * Coef + return CostA * 100 < CostB * EstimatedWidthA * Coef; + } + // To avoid the need for FP division: // (CostA / A.Width) < (CostB / B.Width) // <=> (CostA * B.Width) < (CostB * A.Width) @@ -5575,7 +5720,8 @@ void LoopVectorizationCostModel::collectElementTypesForWidening() { continue; // Only examine Loads, Stores and PHINodes. - if (!isa(I) && !isa(I) && !isa(I)) + if (!isa(I) && !isa(I) && !isa(I) && + !isVectorizableCJGCWrite(&I)) continue; // Examine PHI nodes that are reduction variables. Update the type to @@ -5594,8 +5740,9 @@ void LoopVectorizationCostModel::collectElementTypesForWidening() { } // Examine the stored values. - if (auto *ST = dyn_cast(&I)) - T = ST->getValueOperand()->getType(); + if (isa(&I) || isVectorizableCJGCWrite(&I)) { + T = getStoreValueOperand(&I)->getType(); + } assert(T->isSized() && "Expected the load/store/recurrence type to be sized"); @@ -5821,7 +5968,7 @@ unsigned LoopVectorizationCostModel::selectInterleaveCount(ElementCount VF, LoadsIC = std::min(LoadsIC, F); } - if (EnableLoadStoreRuntimeInterleave && + if (EnableLoadStoreRuntimeInterleave && !TheLoop->HasGCWriteInLoop && std::max(StoresIC, LoadsIC) > SmallIC) { LLVM_DEBUG( dbgs() << "LV: Interleaving to saturate store or load ports.\n"); @@ -6057,7 +6204,7 @@ bool LoopVectorizationCostModel::useEmulatedMaskMemRefHack(Instruction *I, assert((isPredicatedInst(I, VF) || Legal->isUniformMemOp(*I)) && "Expecting a scalar emulated instruction"); return isa(I) || - (isa(I) && + ((isa(I) || isVectorizableCJGCWrite(I)) && NumPredStores > NumberOfStoresToPredicate); } @@ -6359,6 +6506,9 @@ LoopVectorizationCostModel::getMemInstScalarizationCost(Instruction *I, InstructionCost LoopVectorizationCostModel::getConsecutiveMemOpCost(Instruction *I, ElementCount VF) { + if (isVectorizableCJGCWrite(I)) { + return getGCWriteCost(dyn_cast(I), VF); + } Type *ValTy = getLoadStoreType(I); auto *VectorTy = cast(ToVectorTy(ValTy, VF)); Value *Ptr = getLoadStorePointerOperand(I); @@ -6415,6 +6565,9 @@ LoopVectorizationCostModel::getUniformMemOpCost(Instruction *I, InstructionCost LoopVectorizationCostModel::getGatherScatterCost(Instruction *I, ElementCount VF) { + if (isVectorizableCJGCWrite(I)) { + return InstructionCost::getMax(); + } Type *ValTy = getLoadStoreType(I); auto *VectorTy = cast(ToVectorTy(ValTy, VF)); const Align Alignment = getLoadStoreAlignment(I); @@ -6761,7 +6914,8 @@ void LoopVectorizationCostModel::setCostBasedWideningDecision(ElementCount VF) { // predicated uniform stores. Today they are treated as any other // predicated store (see added test cases in // invariant-store-vectorization.ll). - if (isa(&I) && isScalarWithPredication(&I, VF)) + if ((isa(&I) || isVectorizableCJGCWrite(&I)) && + isScalarWithPredication(&I, VF)) NumPredStores++; if (Legal->isUniformMemOp(I)) { @@ -6803,6 +6957,11 @@ void LoopVectorizationCostModel::setCostBasedWideningDecision(ElementCount VF) { setWideningDecision(&I, VF, Decision, Cost); continue; } + // only support Consecutive gcwrite now! + if (isVectorizableCJGCWrite(&I)) { + setWideningDecision(&I, VF, CM_Interleave, InstructionCost::getMax()); + continue; + } // Choose between Interleaving, Gather/Scatter or Scalarization. InstructionCost InterleaveCost = InstructionCost::getInvalid(); @@ -6954,6 +7113,23 @@ LoopVectorizationCostModel::getInstructionCost(Instruction *I, ElementCount VF, } else VectorTy = ToVectorTy(RetTy, VF); + auto getLoadStoreVecCost = [this, &I, &VF, &VectorTy] { + ElementCount Width = VF; + if (Width.isVector()) { + InstWidening Decision = getWideningDecision(I, Width); + assert(Decision != CM_Unknown && + "CM decision should be taken at this point"); + if (getWideningCost(I, VF) == InstructionCost::getInvalid()) + return InstructionCost::getInvalid(); + if (Decision == CM_Scalarize) + Width = ElementCount::getFixed(1); + } + VectorTy = ToVectorTy(getLoadStoreType(I), Width); + if (auto *CI = dyn_cast(I)) + return getGCWriteCost(CI, VF); + return getMemoryInstructionCost(I, VF); + }; + // TODO: We need to estimate the cost of intrinsic calls. switch (I->getOpcode()) { case Instruction::GetElementPtr: @@ -7137,18 +7313,7 @@ LoopVectorizationCostModel::getInstructionCost(Instruction *I, ElementCount VF, } case Instruction::Store: case Instruction::Load: { - ElementCount Width = VF; - if (Width.isVector()) { - InstWidening Decision = getWideningDecision(I, Width); - assert(Decision != CM_Unknown && - "CM decision should be taken at this point"); - if (getWideningCost(I, VF) == InstructionCost::getInvalid()) - return InstructionCost::getInvalid(); - if (Decision == CM_Scalarize) - Width = ElementCount::getFixed(1); - } - VectorTy = ToVectorTy(getLoadStoreType(I), Width); - return getMemoryInstructionCost(I, VF); + return getLoadStoreVecCost(); } case Instruction::BitCast: if (I->getType()->isPointerTy()) @@ -7243,6 +7408,9 @@ LoopVectorizationCostModel::getInstructionCost(Instruction *I, ElementCount VF, return TTI.getCastInstrCost(Opcode, VectorTy, SrcVecTy, CCH, CostKind, I); } case Instruction::Call: { + if (IntrinsicInst *II = dyn_cast(I); + II && II->getIntrinsicID() == Intrinsic::cj_gcwrite_ref) + return getLoadStoreVecCost(); if (RecurrenceDescriptor::isFMulAddIntrinsic(I)) if (auto RedCost = getReductionPatternCost(I, VF, VectorTy, CostKind)) return *RedCost; @@ -7309,10 +7477,11 @@ void LoopVectorizationCostModel::collectValuesToIgnore() { // outside the loop we do not need calculate cost for them. for (BasicBlock *BB : TheLoop->blocks()) for (Instruction &I : *BB) { - StoreInst *SI; - if ((SI = dyn_cast(&I)) && - Legal->isInvariantAddressOfReduction(SI->getPointerOperand())) + if ((isa(&I) || isVectorizableCJGCWrite(&I)) && + Legal->isInvariantAddressOfReduction( + getLoadStorePointerOperand(&I))) { ValuesToIgnore.insert(&I); + } } // Ignore type-promoting instructions we identified during reduction @@ -8259,6 +8428,9 @@ VPWidenCallRecipe *VPRecipeBuilder::tryToWidenCall(CallInst *CI, return nullptr; auto willWiden = [&](ElementCount VF) -> bool { + if (isVectorizableCJGCWrite(CI)) { + return true; + } Intrinsic::ID ID = getVectorIntrinsicIDForCall(CI, TLI); // The following case may be scalarized depending on the VF. // The flag shows whether we use Intrinsic or a usual Call for vectorized @@ -8805,9 +8977,8 @@ VPlanPtr LoopVectorizationPlanner::buildVPlanWithVPRecipes( // Invariant stores inside loop will be deleted and a single store // with the final reduction value will be added to the exit block - StoreInst *SI; - if ((SI = dyn_cast(&I)) && - Legal->isInvariantAddressOfReduction(SI->getPointerOperand())) + if ((isa(&I) || isVectorizableCJGCWrite(&I)) && + Legal->isInvariantAddressOfReduction(getLoadStorePointerOperand(&I))) continue; if (auto RecipeOrValue = RecipeBuilder.tryToCreateWidenRecipe( @@ -9604,44 +9775,6 @@ void VPWidenMemoryInstructionRecipe::execute(VPTransformState &State) { for (unsigned Part = 0; Part < State.UF; ++Part) BlockInMaskParts[Part] = State.get(getMask(), Part); - const auto CreateVecPtr = [&](unsigned Part, Value *Ptr) -> Value * { - // Calculate the pointer for the specific unroll-part. - GetElementPtrInst *PartPtr = nullptr; - - bool InBounds = false; - if (auto *gep = dyn_cast(Ptr->stripPointerCasts())) - InBounds = gep->isInBounds(); - if (Reverse) { - // If the address is consecutive but reversed, then the - // wide store needs to start at the last vector element. - // RunTimeVF = VScale * VF.getKnownMinValue() - // For fixed-width VScale is 1, then RunTimeVF = VF.getKnownMinValue() - Value *RunTimeVF = getRuntimeVF(Builder, Builder.getInt32Ty(), State.VF); - // NumElt = -Part * RunTimeVF - Value *NumElt = Builder.CreateMul(Builder.getInt32(-Part), RunTimeVF); - // LastLane = 1 - RunTimeVF - Value *LastLane = Builder.CreateSub(Builder.getInt32(1), RunTimeVF); - PartPtr = - cast(Builder.CreateGEP(ScalarDataTy, Ptr, NumElt)); - PartPtr->setIsInBounds(InBounds); - PartPtr = cast( - Builder.CreateGEP(ScalarDataTy, PartPtr, LastLane)); - PartPtr->setIsInBounds(InBounds); - if (isMaskRequired) // Reverse of a null all-one mask is a null mask. - BlockInMaskParts[Part] = - Builder.CreateVectorReverse(BlockInMaskParts[Part], "reverse"); - } else { - Value *Increment = - createStepForVF(Builder, Builder.getInt32Ty(), State.VF, Part); - PartPtr = cast( - Builder.CreateGEP(ScalarDataTy, Ptr, Increment)); - PartPtr->setIsInBounds(InBounds); - } - - unsigned AddressSpace = Ptr->getType()->getPointerAddressSpace(); - return Builder.CreateBitCast(PartPtr, DataTy->getPointerTo(AddressSpace)); - }; - // Handle Stores: if (SI) { State.setDebugLocFromInst(SI); @@ -9662,8 +9795,14 @@ void VPWidenMemoryInstructionRecipe::execute(VPTransformState &State) { // We don't want to update the value in the map as it might be used in // another expression. So don't call resetVectorValue(StoredVal). } - auto *VecPtr = - CreateVecPtr(Part, State.get(getAddr(), VPIteration(0, 0))); + Value *Ptr = State.get(getAddr(), VPIteration(0, 0)); + Value *PartPtr = createVecPtr(SI, Part, Ptr, State, Reverse); + if (Reverse && isMaskRequired) // Reverse of a null all-one mask is a null mask. + BlockInMaskParts[Part] = + Builder.CreateVectorReverse(BlockInMaskParts[Part], "reverse"); + + unsigned AddressSpace = Ptr->getType()->getPointerAddressSpace(); + Value *VecPtr = Builder.CreateBitCast(PartPtr, DataTy->getPointerTo(AddressSpace)); if (isMaskRequired) NewSI = Builder.CreateMaskedStore(StoredVal, VecPtr, Alignment, BlockInMaskParts[Part]); @@ -9687,15 +9826,23 @@ void VPWidenMemoryInstructionRecipe::execute(VPTransformState &State) { nullptr, "wide.masked.gather"); State.addMetadata(NewLI, LI); } else { - auto *VecPtr = - CreateVecPtr(Part, State.get(getAddr(), VPIteration(0, 0))); - if (isMaskRequired) + Value *Ptr = State.get(getAddr(), VPIteration(0, 0)); + auto *PartPtr = createVecPtr(LI, Part, Ptr, State, Reverse); + if (Reverse && isMaskRequired) { + // Reverse of a null all-one mask is a null mask. + BlockInMaskParts[Part] = + Builder.CreateVectorReverse(BlockInMaskParts[Part], "reverse"); + } + unsigned AddressSpace = Ptr->getType()->getPointerAddressSpace(); + Value *VecPtr = Builder.CreateBitCast(PartPtr, DataTy->getPointerTo(AddressSpace)); + if (isMaskRequired) { NewLI = Builder.CreateMaskedLoad( DataTy, VecPtr, Alignment, BlockInMaskParts[Part], PoisonValue::get(DataTy), "wide.masked.load"); - else + } else { NewLI = Builder.CreateAlignedLoad(DataTy, VecPtr, Alignment, "wide.load"); + } // Add metadata to the load, but setVectorValue to the reverse shuffle. State.addMetadata(NewLI, LI); @@ -10185,7 +10332,7 @@ bool LoopVectorizePass::processLoop(Loop *L) { // If an override option has been passed in for interleaved accesses, use it. if (EnableInterleavedMemAccesses.getNumOccurrences() > 0) UseInterleaved = EnableInterleavedMemAccesses; - + UseInterleaved &= (!L->HasGCWriteInLoop); // Analyze interleaved memory accesses. if (UseInterleaved) { IAI.analyzeInterleaving(useMaskedInterleavedAccesses(*TTI)); @@ -10499,8 +10646,9 @@ LoopVectorizeResult LoopVectorizePass::runImpl( // For the inner loops we actually process, form LCSSA to simplify the // transform. Changed |= formLCSSARecursively(*L, *DT, LI, SE); - + L->IsInVectorizedProcess = true; Changed |= CFGChanged |= processLoop(L); + L->IsInVectorizedProcess = false; } // Process each loop nest in the function. diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp index 53c11c58f..51c59c8af 100644 --- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp +++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp @@ -177,6 +177,23 @@ static cl::opt ViewSLPTree("view-slp-tree", cl::Hidden, cl::desc("Display the SLP trees with Graphviz")); +static cl::opt + EnableSLPForBarrier("enable-slp-for-barrier", cl::init(true), + cl::desc("enable cj gc write slp vectorization")); + +namespace llvm { +extern cl::opt CJPipeline; + +bool isEnableSLPForGCWrite(CallInst *CI) { + Type *AT = CI->getOperand(0)->getType(); + return EnableSLPForBarrier && (AT->getTypeID() == Type::HalfTyID || + AT->getTypeID() == Type::FloatTyID || + AT->getTypeID() == Type::DoubleTyID || + (AT->getTypeID() == Type::IntegerTyID && + AT->getIntegerBitWidth() != 1)); +} +} // namespace llvm + // Limit the number of alias checks. The limit is chosen so that // it has no negative effect on the llvm benchmarks. static const unsigned AliasedCheckLimit = 10; @@ -657,6 +674,10 @@ static MemoryLocation getLocation(Instruction *I) { return MemoryLocation::get(SI); if (LoadInst *LI = dyn_cast(I)) return MemoryLocation::get(LI); + if (CallInst *CI = dyn_cast(I)) { + if (CI->getIntrinsicID() == Intrinsic::cj_gcwrite_ref) + return MemoryLocation::get(CI); + } return MemoryLocation(); } @@ -3831,8 +3852,14 @@ void BoUpSLP::reorderTopToBottom() { continue; } // Stores actually store the mask, not the order, need to invert. - if (OpTE->State == TreeEntry::Vectorize && !OpTE->isAltShuffle() && - OpTE->getOpcode() == Instruction::Store && !Order.empty()) { + auto *CI = dyn_cast_or_null(OpTE->getMainOp()); + bool Tag1 = OpTE->State == TreeEntry::Vectorize && + !OpTE->isAltShuffle() && + OpTE->getOpcode() == Instruction::Store && !Order.empty(); + bool Tag2 = CI && CI->getIntrinsicID() == Intrinsic::cj_gcwrite_ref && + OpTE->State == TreeEntry::Vectorize && + !OpTE->isAltShuffle() && !Order.empty(); + if (Tag1 || Tag2) { SmallVector Mask; inversePermutation(Order, Mask); unsigned E = Order.size(); @@ -3898,6 +3925,11 @@ void BoUpSLP::reorderTopToBottom() { reorderOrder(TE->ReorderIndices, Mask); if (isa(TE->getMainOp())) TE->reorderOperands(Mask); + } else if (auto *CI = dyn_cast_or_null(TE->getMainOp()); + TE->State == TreeEntry::Vectorize && CI && + CI->getIntrinsicID() == Intrinsic::cj_gcwrite_ref) { + reorderOrder(TE->ReorderIndices, Mask); + TE->reorderOperands(Mask); } else { // Reorder the node and its operands. TE->reorderOperands(Mask); @@ -4069,8 +4101,14 @@ void BoUpSLP::reorderBottomToTop(bool IgnoreReorder) { return P.second == OpTE; }); // Stores actually store the mask, not the order, need to invert. - if (OpTE->State == TreeEntry::Vectorize && !OpTE->isAltShuffle() && - OpTE->getOpcode() == Instruction::Store && !Order.empty()) { + auto *CI = dyn_cast_or_null(OpTE->getMainOp()); + bool Tag1 = OpTE->State == TreeEntry::Vectorize && + !OpTE->isAltShuffle() && + OpTE->getOpcode() == Instruction::Store && !Order.empty(); + bool Tag2 = CI && CI->getIntrinsicID() == Intrinsic::cj_gcwrite_ref && + OpTE->State == TreeEntry::Vectorize && + !OpTE->isAltShuffle() && !Order.empty(); + if (Tag1 || Tag2) { SmallVector Mask; inversePermutation(Order, Mask); unsigned E = Order.size(); @@ -4638,6 +4676,24 @@ void BoUpSLP::buildTree_rec(ArrayRef VL, unsigned Depth, return; } + if (CallInst *CI = dyn_cast(S.OpValue)) { + if (CI->getIntrinsicID() == Intrinsic::cj_gcwrite_ref && + CI->getOperand(0)->getType()->isVectorTy()) { + LLVM_DEBUG(dbgs() << "SLP: Gathering due to gcwrite vector type.\n"); + newTreeEntry(VL, None /*not vectorized*/, S, UserTreeIdx); + return; + } + if (F->hasCangjieGC()) { + if (CI->getIntrinsicID() == Intrinsic::pow || + CI->getIntrinsicID() == Intrinsic::powi) { + LLVM_DEBUG( + dbgs() << "SLP: Gathering due to non-support pow in cangjie.\n"); + newTreeEntry(VL, None /*not vectorized*/, S, UserTreeIdx); + return; + } + } + } + // If all of the operands are identical or constant we have a simple solution. // If we deal with insert/extract instructions, they all must have constant // indices, otherwise we should gather them, not try to vectorize. @@ -4850,6 +4906,66 @@ void BoUpSLP::buildTree_rec(ArrayRef VL, unsigned Depth, } LLVM_DEBUG(dbgs() << "SLP: We are able to schedule this bundle.\n"); + auto VectorizeCJGCWrite = [this, &VL0, &VL, &BS, &S, &Bundle, &Depth, + &UserTreeIdx, &ReuseShuffleIndicies] { + Type *ScalarTy = cast(VL0)->getOperand(0)->getType(); + if (DL->getTypeSizeInBits(ScalarTy) != + DL->getTypeAllocSizeInBits(ScalarTy)) { + BS.cancelScheduling(VL, VL0); + newTreeEntry(VL, None /*not vectorized*/, S, UserTreeIdx, + ReuseShuffleIndicies); + LLVM_DEBUG(dbgs() << "SLP: Gathering stores of non-packed type.\n"); + return; + } + SmallVector PointerOps(VL.size()); + ValueList Operands(VL.size()); + auto *POIter = PointerOps.begin(); + auto *OIter = Operands.begin(); + for (Value *V : VL) { + auto *CI = cast(V); + *POIter = CI->getOperand(2); + *OIter = CI->getOperand(0); + ++POIter, ++OIter; + } + OrdersType CurrentOrder; + if (llvm::sortPtrAccesses(PointerOps, ScalarTy, *DL, *SE, CurrentOrder)) { + Value *Ptr0; + Value *PtrN; + if (CurrentOrder.empty()) { + Ptr0 = PointerOps.front(); + PtrN = PointerOps.back(); + } else { + Ptr0 = PointerOps[CurrentOrder.front()]; + PtrN = PointerOps[CurrentOrder.back()]; + } + Optional Dist = + getPointersDiff(ScalarTy, Ptr0, ScalarTy, PtrN, *DL, *SE); + if (static_cast(*Dist) == VL.size() - 1) { + if (CurrentOrder.empty()) { + // Original stores are consecutive and does not require reordering. + TreeEntry *TE = newTreeEntry(VL, Bundle /*vectorized*/, S, + UserTreeIdx, ReuseShuffleIndicies); + TE->setOperandsInOrder(); + buildTree_rec(Operands, Depth + 1, {TE, 0}); + LLVM_DEBUG(dbgs() << "SLP: added a vector of gcwrites.\n"); + } else { + fixupOrderingIndices(CurrentOrder); + TreeEntry *TE = + newTreeEntry(VL, Bundle /*vectorized*/, S, UserTreeIdx, + ReuseShuffleIndicies, CurrentOrder); + TE->setOperandsInOrder(); + buildTree_rec(Operands, Depth + 1, {TE, 0}); + LLVM_DEBUG(dbgs() << "SLP: added a vector of jumbled gcwrites.\n"); + } + return; + } + BS.cancelScheduling(VL, VL0); + newTreeEntry(VL, None /*not vectorized*/, S, UserTreeIdx, + ReuseShuffleIndicies); + LLVM_DEBUG(dbgs() << "SLP: Non-consecutive gcwrite.\n"); + } + }; + unsigned ShuffleOrOp = S.isAltShuffle() ? (unsigned) Instruction::ShuffleVector : S.getOpcode(); switch (ShuffleOrOp) { @@ -5370,8 +5486,12 @@ void BoUpSLP::buildTree_rec(ArrayRef VL, unsigned Depth, // Check if the calls are all to the same vectorizable intrinsic or // library function. CallInst *CI = cast(VL0); - Intrinsic::ID ID = getVectorIntrinsicIDForCall(CI, TLI); + if (CI->getIntrinsicID() == Intrinsic::cj_gcwrite_ref) { + VectorizeCJGCWrite(); + return; + } + Intrinsic::ID ID = getVectorIntrinsicIDForCall(CI, TLI); VFShape Shape = VFShape::get( *CI, ElementCount::getFixed(static_cast(VL.size())), false /*HasGlobalPred*/); @@ -5819,6 +5939,10 @@ InstructionCost BoUpSLP::getEntryCost(const TreeEntry *E, ScalarTy = CI->getOperand(0)->getType(); else if (auto *IE = dyn_cast(VL[0])) ScalarTy = IE->getOperand(1)->getType(); + else if (auto *CI = dyn_cast(VL[0])) { + if (CI->getIntrinsicID() == Intrinsic::cj_gcwrite_ref) + ScalarTy = CI->getOperand(0)->getType(); + } auto *VecTy = FixedVectorType::get(ScalarTy, VL.size()); TTI::TargetCostKind CostKind = TTI::TCK_RecipThroughput; @@ -6080,6 +6204,12 @@ InstructionCost BoUpSLP::getEntryCost(const TreeEntry *E, // For stores the order is actually a mask. NewMask.resize(E->ReorderIndices.size()); copy(E->ReorderIndices, NewMask.begin()); + } else if (E->getOpcode() == Instruction::Call) { + auto *CI = E->getMainOp() ? dyn_cast(E->getMainOp()) : nullptr; + if (CI && CI->getIntrinsicID() == Intrinsic::cj_gcwrite_ref) { + NewMask.resize(E->ReorderIndices.size()); + copy(E->ReorderIndices, NewMask.begin()); + } } else { inversePermutation(E->ReorderIndices, NewMask); } @@ -6488,6 +6618,33 @@ InstructionCost BoUpSLP::getEntryCost(const TreeEntry *E, } case Instruction::Call: { CallInst *CI = cast(VL0); + if (CI->getIntrinsicID() == Intrinsic::cj_gcwrite_ref) { + // TODO Cost calculation optimization, to considered gcphase + unsigned BitWidth = 0; + Type *AT = CI->getOperand(0)->getType(); + switch (AT->getTypeID()) { + case Type::IntegerTyID: + BitWidth = AT->getIntegerBitWidth(); + break; + case Type::HalfTyID: + BitWidth = 16; + break; + case Type::FloatTyID: + BitWidth = 32; + break; + case Type::DoubleTyID: + BitWidth = 64; + break; + default: + report_fatal_error("slp vectorizer pass not support for this type"); + } + unsigned VecSize = VecTy->getNumElements(); + InstructionCost VecCost = + (BitWidth * VecSize == 128 || (BitWidth == 8 && VecSize == 8)) + ? 1 + : InstructionCost::getMin(); + return CommonCost - VecCost; + } Intrinsic::ID ID = getVectorIntrinsicIDForCall(CI, TLI); // Calculate the cost of the scalar and vector calls. @@ -7874,7 +8031,41 @@ Value *BoUpSLP::vectorizeTree(TreeEntry *E) { ScalarTy = Store->getValueOperand()->getType(); else if (auto *IE = dyn_cast(VL0)) ScalarTy = IE->getOperand(1)->getType(); + else if (auto *CI = dyn_cast(VL0)) { + // There may be other CallInst when vectorizeChainsInBlock + if (CI->getIntrinsicID() == Intrinsic::cj_gcwrite_ref) + ScalarTy = CI->getOperand(0)->getType(); + } auto *VecTy = FixedVectorType::get(ScalarTy, E->Scalars.size()); + auto VectorizeGCWrite = [this, &VL0, &E, &ShuffleBuilder]() -> Value * { + auto *CI = cast(VL0); + unsigned AS = CI->getOperand(2)->getType()->getPointerAddressSpace(); + setInsertPointAfterBundle(E); + + Value *VecValue = vectorizeTree(E->getOperand(0)); + ShuffleBuilder.addMask(E->ReorderIndices); + VecValue = ShuffleBuilder.finalize(VecValue); + + Value *ScalarPtr = CI->getOperand(2); + Value *VecPtr = + Builder.CreateBitCast(ScalarPtr, VecValue->getType()->getPointerTo(AS)); + Function *Func = + Intrinsic::getDeclaration(CI->getModule(), Intrinsic::cj_gcwrite_ref); + CallInst *GCWrite = + Builder.CreateCall(Func, {VecValue, CI->getOperand(1), VecPtr}); + + if (TreeEntry *Entry = getTreeEntry(ScalarPtr)) { + unsigned FoundLane = Entry->findLaneForValue(ScalarPtr); + ExternalUses.push_back(ExternalUser( + ScalarPtr, ScalarPtr != VecPtr ? cast(VecPtr) : GCWrite, + FoundLane)); + } + + Value *V = propagateMetadata(GCWrite, E->Scalars); + E->VectorizedValue = V; + ++NumVectorInstructions; + return V; + }; switch (ShuffleOrOp) { case Instruction::PHI: { assert((E->ReorderIndices.empty() || @@ -8260,6 +8451,10 @@ Value *BoUpSLP::vectorizeTree(TreeEntry *E) { } case Instruction::Call: { CallInst *CI = cast(VL0); + if (CI->getIntrinsicID() == Intrinsic::cj_gcwrite_ref) { + Value *V = VectorizeGCWrite(); + return V; + } setInsertPointAfterBundle(E); Intrinsic::ID IID = Intrinsic::not_intrinsic; @@ -9627,6 +9822,12 @@ unsigned BoUpSLP::getVectorElementSize(Value *V) { if (auto *Store = dyn_cast(V)) return DL->getTypeSizeInBits(Store->getValueOperand()->getType()); + if (auto *CI = dyn_cast(V)) { + if (CI->getIntrinsicID() == Intrinsic::cj_gcwrite_ref && + isEnableSLPForGCWrite(CI)) + return DL->getTypeSizeInBits(CI->getOperand(0)->getType()); + } + if (auto *IEI = dyn_cast(V)) return getVectorElementSize(IEI->getOperand(1)); @@ -9999,6 +10200,7 @@ bool SLPVectorizerPass::runImpl(Function &F, ScalarEvolution *SE_, Stores.clear(); GEPs.clear(); + CJGCWrites.clear(); bool Changed = false; // If the target claims to have no vector registers don't attempt @@ -10032,7 +10234,7 @@ bool SLPVectorizerPass::runImpl(Function &F, ScalarEvolution *SE_, collectSeedInstructions(BB); // Vectorize trees that end at stores. - if (!Stores.empty()) { + if (!Stores.empty() || !CJGCWrites.empty()) { LLVM_DEBUG(dbgs() << "SLP: Found stores for " << Stores.size() << " underlying objects.\n"); Changed |= vectorizeStoreChains(R); @@ -10089,12 +10291,21 @@ bool SLPVectorizerPass::vectorizeStoreChain(ArrayRef Chain, BoUpSLP &R, LLVM_DEBUG(dbgs() << "SLP: Decided to vectorize cost = " << Cost << "\n"); using namespace ore; - - R.getORE()->emit(OptimizationRemark(SV_NAME, "StoresVectorized", - cast(Chain[0])) - << "Stores SLP vectorized with cost " << NV("Cost", Cost) - << " and with tree size " - << NV("TreeSize", R.getTreeSize())); + if (isa(Chain[0])) { + R.getORE()->emit(OptimizationRemark(SV_NAME, "StoresVectorized", + cast(Chain[0])) + << "Stores SLP vectorized with cost " << NV("Cost", Cost) + << " and with tree size " + << NV("TreeSize", R.getTreeSize())); + } else if (isa(Chain[0]) && + dyn_cast(Chain[0])->getIntrinsicID() == + Intrinsic::cj_gcwrite_ref) { + R.getORE()->emit(OptimizationRemark(SV_NAME, "GCWriteVectorized", + cast(Chain[0])) + << "GCWrite SLP vectorized with cost " + << NV("Cost", Cost) << " and with tree size " + << NV("TreeSize", R.getTreeSize())); + } R.vectorizeTree(); return true; @@ -10103,8 +10314,25 @@ bool SLPVectorizerPass::vectorizeStoreChain(ArrayRef Chain, BoUpSLP &R, return false; } -bool SLPVectorizerPass::vectorizeStores(ArrayRef Stores, - BoUpSLP &R) { +Value *getStorePointerOperand(Instruction *I) { + if (isa(I) || isa(I)) + return getLoadStorePointerOperand(I); + return nullptr; +} + +Type *getStorePointerType(Instruction *I) { + Value *V = getStorePointerOperand(I); + return V ? V->getType() : nullptr; +} + +Type *getStoreValueType(Instruction *I) { + if (isa(I) || isa(I)) + return getLoadStoreType(I); + return nullptr; +} + +template +bool SLPVectorizerPass::vectorizeStoresOrGCWrites(ArrayRef Stores, BoUpSLP &R) { // We may run into multiple chains that merge into a single chain. We mark the // stores that we vectorized so that we don't visit the same store twice. BoUpSLP::ValueSet VectorizedStores; @@ -10128,10 +10356,12 @@ bool SLPVectorizerPass::vectorizeStores(ArrayRef Stores, ++IterCnt; CheckedPairs[Idx].set(K); CheckedPairs[K].set(Idx); - Optional Diff = getPointersDiff( - Stores[K]->getValueOperand()->getType(), Stores[K]->getPointerOperand(), - Stores[Idx]->getValueOperand()->getType(), - Stores[Idx]->getPointerOperand(), *DL, *SE, /*StrictCheck=*/true); + Optional Diff = + getPointersDiff(getStoreValueType(Stores[K]), + getStorePointerOperand(Stores[K]), + getStoreValueType(Stores[Idx]), + getStorePointerOperand(Stores[Idx]), *DL, *SE, + /*StrictCheck=*/true); if (!Diff || *Diff == 0) return false; int Val = *Diff; @@ -10203,10 +10433,10 @@ bool SLPVectorizerPass::vectorizeStores(ArrayRef Stores, unsigned MaxVF = std::min(R.getMaximumVF(EltSize, Instruction::Store), MaxElts); - auto *Store = cast(Operands[0]); - Type *StoreTy = Store->getValueOperand()->getType(); + T *Store = cast(Operands[0]); + Type *StoreTy = getStoreValueType(Store); Type *ValueTy = StoreTy; - if (auto *Trunc = dyn_cast(Store->getValueOperand())) + if (auto *Trunc = dyn_cast(getStoreValueOperand(Store))) ValueTy = Trunc->getSrcTy(); unsigned MinVF = TTI->getStoreMinimumVF( R.getMinVF(DL->getTypeSizeInBits(ValueTy)), StoreTy, ValueTy); @@ -10245,17 +10475,20 @@ void SLPVectorizerPass::collectSeedInstructions(BasicBlock *BB) { // Initialize the collections. We will make a single pass over the block. Stores.clear(); GEPs.clear(); + CJGCWrites.clear(); // Visit the store and getelementptr instructions in BB and organize them in // Stores and GEPs according to the underlying objects of their pointer // operands. + // dbgs() << BB->getName() << "\n"; for (Instruction &I : *BB) { // Ignore store instructions that are volatile or have a pointer operand // that doesn't point to a scalar type. if (auto *SI = dyn_cast(&I)) { if (!SI->isSimple()) continue; - if (!isValidElementType(SI->getValueOperand()->getType())) + if (!isValidElementType(SI->getValueOperand()->getType()) || + (CJPipeline && SI->getValueOperand()->getType()->isPointerTy())) continue; Stores[getUnderlyingObject(SI->getPointerOperand())].push_back(SI); } @@ -10267,11 +10500,17 @@ void SLPVectorizerPass::collectSeedInstructions(BasicBlock *BB) { auto Idx = GEP->idx_begin()->get(); if (GEP->getNumIndices() > 1 || isa(Idx)) continue; - if (!isValidElementType(Idx->getType())) + if (!isValidElementType(Idx->getType()) || + (CJPipeline && Idx->getType()->isPointerTy())) continue; if (GEP->getType()->isVectorTy()) continue; GEPs[GEP->getPointerOperand()].push_back(GEP); + } else if (auto *CI = dyn_cast(&I)) { + if (CI->getIntrinsicID() != Intrinsic::cj_gcwrite_ref) + continue; + if (isEnableSLPForGCWrite(CI)) + CJGCWrites[getUnderlyingObject(CI->getOperand(2))].push_back(CI); } } } @@ -12371,19 +12610,24 @@ bool SLPVectorizerPass::vectorizeStoreChains(BoUpSLP &R) { // Sort by type, base pointers and values operand. Value operands must be // compatible (have the same opcode, same parent), otherwise it is // definitely not profitable to try to vectorize them. - auto &&StoreSorter = [this](StoreInst *V, StoreInst *V2) { - if (V->getPointerOperandType()->getTypeID() < - V2->getPointerOperandType()->getTypeID()) + auto &&StoreOrCallSorter = [this](Instruction *V, Instruction *V2) { + auto *VT = getStorePointerType(V), + *V2T = getStorePointerType(V2); + assert((VT != nullptr && V2T != nullptr) && "Pointer type should not be nullptr"); + + if (VT->getTypeID() < V2T->getTypeID()) return true; - if (V->getPointerOperandType()->getTypeID() > - V2->getPointerOperandType()->getTypeID()) + if (VT->getTypeID() > V2T->getTypeID()) return false; + + auto *VV = getStoreValueOperand(V), + *V2V = getStoreValueOperand(V2); + assert((VV != nullptr && V2V != nullptr) && "Value should not be nullptr"); // UndefValues are compatible with all other values. - if (isa(V->getValueOperand()) || - isa(V2->getValueOperand())) + if (isa(VV) || isa(V2V)) return false; - if (auto *I1 = dyn_cast(V->getValueOperand())) - if (auto *I2 = dyn_cast(V2->getValueOperand())) { + if (auto *I1 = dyn_cast(VV)) + if (auto *I2 = dyn_cast(V2V)) { DomTreeNodeBase *NodeI1 = DT->getNode(I1->getParent()); DomTreeNodeBase *NodeI2 = @@ -12400,37 +12644,41 @@ bool SLPVectorizerPass::vectorizeStoreChains(BoUpSLP &R) { return false; return I1->getOpcode() < I2->getOpcode(); } - if (isa(V->getValueOperand()) && - isa(V2->getValueOperand())) + if (isa(VV) && isa(V2V)) return false; - return V->getValueOperand()->getValueID() < - V2->getValueOperand()->getValueID(); + return VV->getValueID() < V2V->getValueID(); }; - auto &&AreCompatibleStores = [](StoreInst *V1, StoreInst *V2) { + auto &&AreCompatibleStores = [](Instruction *V1, Instruction *V2) { if (V1 == V2) return true; - if (V1->getPointerOperandType() != V2->getPointerOperandType()) + auto *V1T = getStorePointerType(V1), + *V2T = getStorePointerType(V2); + assert((V1T != nullptr && V2T != nullptr) && + "Pointer type should not be nullptr"); + if (V1T != V2T) return false; // Undefs are compatible with any other value. - if (isa(V1->getValueOperand()) || - isa(V2->getValueOperand())) + auto *V1V = getStoreValueOperand(V1), + *V2V = getStoreValueOperand(V2); + assert((V1V != nullptr && V2V != nullptr) && "Value should not be nullptr"); + if (isa(V1V) || isa(V2V)) return true; - if (auto *I1 = dyn_cast(V1->getValueOperand())) - if (auto *I2 = dyn_cast(V2->getValueOperand())) { + if (auto *I1 = dyn_cast(V1V)) + if (auto *I2 = dyn_cast(V2V)) { if (I1->getParent() != I2->getParent()) return false; InstructionsState S = getSameOpcode({I1, I2}); return S.getOpcode() > 0; } - if (isa(V1->getValueOperand()) && - isa(V2->getValueOperand())) + if (isa(V1V) && isa(V2V)) return true; - return V1->getValueOperand()->getValueID() == - V2->getValueOperand()->getValueID(); + return V1V->getValueID() == V2V->getValueID(); }; - auto Limit = [&R, this](StoreInst *SI) { - unsigned EltSize = DL->getTypeSizeInBits(SI->getValueOperand()->getType()); + auto Limit = [&R, this](Instruction *I) { + auto *V = getStoreValueOperand(I); + assert(V != nullptr && "Value should not be nullptr"); + unsigned EltSize = DL->getTypeSizeInBits(V->getType()); return R.getMinVF(EltSize); }; @@ -12446,9 +12694,21 @@ bool SLPVectorizerPass::vectorizeStoreChains(BoUpSLP &R) { continue; Changed |= tryToVectorizeSequence( - Pair.second, Limit, StoreSorter, AreCompatibleStores, + Pair.second, Limit, StoreOrCallSorter, AreCompatibleStores, [this, &R](ArrayRef Candidates, bool) { - return vectorizeStores(Candidates, R); + return vectorizeStoresOrGCWrites(Candidates, R); + }, + /*LimitForRegisterSize=*/false); + } + for (auto &Pair : CJGCWrites) { + if (Pair.second.size() < 2) + continue; + if (!isValidElementType(Pair.second.front()->getOperand(0)->getType())) + continue; + Changed |= tryToVectorizeSequence( + Pair.second, Limit, StoreOrCallSorter, AreCompatibleStores, + [this, &R](ArrayRef Candidates, bool) { + return vectorizeStoresOrGCWrites(Candidates, R); }, /*LimitForRegisterSize=*/false); } diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp index a38936644..e003bb8d5 100644 --- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp +++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp @@ -25,6 +25,7 @@ #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/PatternMatch.h" +#include "llvm/IR/SafepointIRVerifier.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" #include "llvm/Support/CommandLine.h" @@ -207,6 +208,11 @@ bool VectorCombine::vectorizeLoadInsert(Instruction &I) { Alignment = commonAlignment(Alignment, Offset.getZExtValue()); } + if (I.getFunction()->hasCangjieGC() && + isMemoryContainsGCPtrType(SrcPtr->getType())) { + return false; + } + // Original pattern: insertelt undef, load [free casts of] PtrOp, 0 // Use the greater of the alignment on the load or its source pointer. Alignment = std::max(SrcPtr->getPointerAlignment(DL), Alignment); diff --git a/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll b/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll index 1504f5b52..0ee9ee1eb 100644 --- a/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll +++ b/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll @@ -11,6 +11,7 @@ ; CHECK-NEXT: &1 | FileCheck --check-prefix=ABORT %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@.str = private unnamed_addr constant [4 x i8] c"Hi\0A\00", align 1 +@.str.1 = private unnamed_addr constant [6 x i8] c"main\0A\00", align 1 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @foo4() #0 { + %1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0)) + ret void +} + +declare dso_local i32 @printf(i8*, ...) #1 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @main() #0 { + %1 = alloca i32, align 4 + store i32 0, i32* %1, align 4 + %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.1, i64 0, i64 0)) + %3 = call i32 @foo1() + ret i32 %3 +} + +declare dso_local i32 @foo1() #1 + +; ABORT: LLVM ERROR: Only allow module from cangjie bc files for cangjie LTO! +; ABORT: error: Aborted + +attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.module.flags = !{!0} + +!0 = !{i32 1, !"wchar_size", i32 4} diff --git a/llvm/test/CJLTO/cj-link1.ll b/llvm/test/CJLTO/cj-link1.ll new file mode 100644 index 000000000..a6a96cede --- /dev/null +++ b/llvm/test/CJLTO/cj-link1.ll @@ -0,0 +1,300 @@ +; RUN: llvm-as %s -o %t.bc +; RUN: llvm-link %t.bc -S | FileCheck %s + +; CHECK: %KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +; CHECK-NEXT: %BitMap = type { i32, [0 x i8] } +; CHECK-NEXT: %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" = type { i8 addrspace(1)*, i64, i64 } +; CHECK-NEXT: %KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [1 x i8*] } +; CHECK-NEXT: %"record._ZN11std$FS$core6StringE" = type { i8 addrspace(1)*, i64 } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +%BitMap = type { i32, [0 x i8] } +%KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [1 x i8*] } +%"record._ZN11std$FS$core11std$FS$core5ArrayIhE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN11std$FS$core11std$FS$core5ArrayIlE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN11std$FS$core6StringE" = type { i8 addrspace(1)*, i64 } + +; CHECK: @Int64.arrayKlass = weak_odr global %KlassInfo.0 { i32 2, i32 8, %BitMap* null, i8* bitcast (%KlassInfo.0* @array_int64.primKlass to i8*), i8** null, i64* null, i64* null, i64* bitcast ([6 x i8]* @A1_lE.className to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @A1_lE.className = weak_odr global [6 x i8] c"A1_lE\00", align 1 +; CHECK-NEXT: @array_int64.primKlass = weak_odr global %KlassInfo.0 { i32 1, i32 8, %BitMap* null, i8* null, i8** null, i64* null, i64* null, i64* @array_int64.uniqueAddr, i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @array_int64.uniqueAddr = weak_odr global i64 0 +; CHECK-NEXT: @UInt8.arrayKlass = weak_odr global %KlassInfo.0 { i32 2, i32 1, %BitMap* null, i8* bitcast (%KlassInfo.0* @array_uint8.primKlass to i8*), i8** null, i64* null, i64* null, i64* bitcast ([6 x i8]* @A1_hE.className to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @A1_hE.className = weak_odr global [6 x i8] c"A1_hE\00", align 1 +; CHECK-NEXT: @array_uint8.primKlass = weak_odr global %KlassInfo.0 { i32 1, i32 1, %BitMap* null, i8* null, i8** null, i64* null, i64* null, i64* @array_uint8.uniqueAddr, i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @array_uint8.uniqueAddr = weak_odr global i64 0 +; CHECK-NEXT: @"_ZN11std$FS$core17EMPTY_INT64_ARRAYE" = global %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" zeroinitializer, !GlobalVarType !0 #0 +; CHECK-NEXT: @"_ZN11std$FS$core17StackTraceElementE.objKlass" = weak_odr global %KlassInfo.0 { i32 36, i32 64, %BitMap* inttoptr (i64 -9223372036854775766 to %BitMap*), i8* bitcast (%KlassInfo.1* @"_ZN11std$FS$core6ObjectE.objKlass" to i8*), i8** null, i64* null, i64* null, i64* bitcast ([38 x i8]* @"C_ZN11std$FS$core17StackTraceElementE.className" to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @"C_ZN11std$FS$core17StackTraceElementE.className" = internal global [38 x i8] c"C_ZN11std$FS$core17StackTraceElementE\00", align 1 +; CHECK-NEXT: @"_ZN11std$FS$core6ObjectE.objKlass" = weak_odr global %KlassInfo.1 { i32 4, i32 8, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* bitcast (%KlassInfo.0* @Object.objKlass to i8*), i8** null, i64* null, i64* null, i64* bitcast ([26 x i8]* @"C_ZN11std$FS$core6ObjectE.className" to i64*), i32 1, [1 x i8*] [i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core3AnyE.objKlass" to i8*)] } #0 +; CHECK-NEXT: @Object.objKlass = weak_odr global %KlassInfo.0 { i32 4, i32 8, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i64* null, i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @"C_ZN11std$FS$core6ObjectE.className" = internal global [26 x i8] c"C_ZN11std$FS$core6ObjectE\00", align 1 +; CHECK-NEXT: @"_ZN11std$FS$core3AnyE.objKlass" = weak_odr global %KlassInfo.0 { i32 16, i32 8, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i64* bitcast ([23 x i8]* @"C_ZN11std$FS$core3AnyE.className" to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @"C_ZN11std$FS$core3AnyE.className" = internal global [23 x i8] c"C_ZN11std$FS$core3AnyE\00", align 1 +; CHECK-NEXT: @"_ZN11std$FS$core17StackTraceElementE.refArrayKlass" = internal global %KlassInfo.0 { i32 34, i32 8, %BitMap* null, i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core17StackTraceElementE.objKlass" to i8*), i8** null, i64* null, i64* null, i64* bitcast ([42 x i8]* @"A1_C_ZN11std$FS$core17StackTraceElementEE.className" to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @"A1_C_ZN11std$FS$core17StackTraceElementEE.className" = weak_odr global [42 x i8] c"A1_C_ZN11std$FS$core17StackTraceElementEE\00", align 1 +; CHECK-NEXT: @"_ZN11std$FS$core18EMPTY_STRING_ARRAYE" = internal global %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" zeroinitializer, !GlobalVarType !1 #0 +; CHECK-NEXT: @"_ZN11std$FS$core21EMPTY_UINT8_RAW_ARRAYE" = internal global i8 addrspace(1)* null, !GlobalVarType !2 #0 +; CHECK-NEXT: @"_ZN11std$FS$core6String5emptyE" = global %"record._ZN11std$FS$core6StringE" zeroinitializer, !GlobalVarType !3 #0 +; CHECK-NEXT: @"_ZN11std$FS$core6StringE.arrayKlass" = weak_odr global %KlassInfo.0 { i32 34, i32 16, %BitMap* null, i8* bitcast (%KlassInfo.0* @"record._ZN11std$FS$core6StringE.structKlass" to i8*), i8** null, i64* null, i64* null, i64* bitcast ([30 x i8]* @"A1_R_ZN11std$FS$core6StringEE.className" to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @"A1_R_ZN11std$FS$core6StringEE.className" = weak_odr global [30 x i8] c"A1_R_ZN11std$FS$core6StringEE\00", align 1 +; CHECK-NEXT: @"record._ZN11std$FS$core6StringE.structKlass" = weak_odr global %KlassInfo.0 { i32 40, i32 16, %BitMap* inttoptr (i64 -9223372036854775807 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i64* @"record._ZN11std$FS$core6StringE.uniqueAddr", i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @"record._ZN11std$FS$core6StringE.uniqueAddr" = weak_odr global i64 0 +; CHECK-NEXT: @"_ZN11std$FS$core17EMPTY_UINT8_ARRAYE" = global %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" zeroinitializer, !GlobalVarType !4 #0 +@Int64.arrayKlass = weak_odr global %KlassInfo.0 { i32 2, i32 8, %BitMap* null, i8* bitcast (%KlassInfo.0* @array_int64.primKlass to i8*), i8** null, i64* null, i64* null, i64* bitcast ([6 x i8]* @A1_lE.className to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +@A1_lE.className = weak_odr global [6 x i8] c"A1_lE\00", align 1 +@array_int64.primKlass = weak_odr global %KlassInfo.0 { i32 1, i32 8, %BitMap* null, i8* null, i8** null, i64* null, i64* null, i64* @array_int64.uniqueAddr, i32 0, [0 x i8*] zeroinitializer } #0 +@array_int64.uniqueAddr = weak_odr global i64 0 +@UInt8.arrayKlass = weak_odr global %KlassInfo.0 { i32 2, i32 1, %BitMap* null, i8* bitcast (%KlassInfo.0* @array_uint8.primKlass to i8*), i8** null, i64* null, i64* null, i64* bitcast ([6 x i8]* @A1_hE.className to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +@A1_hE.className = weak_odr global [6 x i8] c"A1_hE\00", align 1 +@array_uint8.primKlass = weak_odr global %KlassInfo.0 { i32 1, i32 1, %BitMap* null, i8* null, i8** null, i64* null, i64* null, i64* @array_uint8.uniqueAddr, i32 0, [0 x i8*] zeroinitializer } #0 +@array_uint8.uniqueAddr = weak_odr global i64 0 +@"_ZN11std$FS$core17EMPTY_INT64_ARRAYE" = global %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" zeroinitializer, !GlobalVarType !2 #0 +@"_ZN11std$FS$core17StackTraceElementE.objKlass" = weak_odr global %KlassInfo.0 { i32 36, i32 64, %BitMap* inttoptr (i64 -9223372036854775766 to %BitMap*), i8* bitcast (%KlassInfo.1* @"_ZN11std$FS$core6ObjectE.objKlass" to i8*), i8** null, i64* null, i64* null, i64* bitcast ([38 x i8]* @"C_ZN11std$FS$core17StackTraceElementE.className" to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +@"C_ZN11std$FS$core17StackTraceElementE.className" = internal global [38 x i8] c"C_ZN11std$FS$core17StackTraceElementE\00", align 1 +@"_ZN11std$FS$core6ObjectE.objKlass" = weak_odr global %KlassInfo.1 { i32 4, i32 8, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* bitcast (%KlassInfo.0* @Object.objKlass to i8*), i8** null, i64* null, i64* null, i64* bitcast ([26 x i8]* @"C_ZN11std$FS$core6ObjectE.className" to i64*), i32 1, [1 x i8*] [i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core3AnyE.objKlass" to i8*)] } #0 +@Object.objKlass = weak_odr global %KlassInfo.0 { i32 4, i32 8, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i64* null, i32 0, [0 x i8*] zeroinitializer } #0 +@"C_ZN11std$FS$core6ObjectE.className" = internal global [26 x i8] c"C_ZN11std$FS$core6ObjectE\00", align 1 +@"_ZN11std$FS$core3AnyE.objKlass" = weak_odr global %KlassInfo.0 { i32 16, i32 8, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i64* bitcast ([23 x i8]* @"C_ZN11std$FS$core3AnyE.className" to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +@"C_ZN11std$FS$core3AnyE.className" = internal global [23 x i8] c"C_ZN11std$FS$core3AnyE\00", align 1 +@"_ZN11std$FS$core17StackTraceElementE.refArrayKlass" = internal global %KlassInfo.0 { i32 34, i32 8, %BitMap* null, i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core17StackTraceElementE.objKlass" to i8*), i8** null, i64* null, i64* null, i64* bitcast ([42 x i8]* @"A1_C_ZN11std$FS$core17StackTraceElementEE.className" to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +@"A1_C_ZN11std$FS$core17StackTraceElementEE.className" = weak_odr global [42 x i8] c"A1_C_ZN11std$FS$core17StackTraceElementEE\00", align 1 +@"_ZN11std$FS$core18EMPTY_STRING_ARRAYE" = internal global %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" zeroinitializer, !GlobalVarType !3 #0 +@"_ZN11std$FS$core21EMPTY_UINT8_RAW_ARRAYE" = internal global i8 addrspace(1)* null, !GlobalVarType !1 #0 +@"_ZN11std$FS$core6String5emptyE" = global %"record._ZN11std$FS$core6StringE" zeroinitializer, !GlobalVarType !4 #0 +@"_ZN11std$FS$core6StringE.arrayKlass" = weak_odr global %KlassInfo.0 { i32 34, i32 16, %BitMap* null, i8* bitcast (%KlassInfo.0* @"record._ZN11std$FS$core6StringE.structKlass" to i8*), i8** null, i64* null, i64* null, i64* bitcast ([30 x i8]* @"A1_R_ZN11std$FS$core6StringEE.className" to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +@"A1_R_ZN11std$FS$core6StringEE.className" = weak_odr global [30 x i8] c"A1_R_ZN11std$FS$core6StringEE\00", align 1 +@"record._ZN11std$FS$core6StringE.structKlass" = weak_odr global %KlassInfo.0 { i32 40, i32 16, %BitMap* inttoptr (i64 -9223372036854775807 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i64* @"record._ZN11std$FS$core6StringE.uniqueAddr", i32 0, [0 x i8*] zeroinitializer } #0 +@"record._ZN11std$FS$core6StringE.uniqueAddr" = weak_odr global i64 0 + + +declare i8 addrspace(1)* @CJ_MCC_DecodeStackTrace(i8 addrspace(1)*, i8*, i8*, i8*) +declare i8 addrspace(1)* @CJ_MCC_NewArray64Stub(i64, i8*) +declare i8 addrspace(1)* @CJ_MCC_NewArray8Stub(i64, i8*) +declare i8 addrspace(1)* @CJ_MCC_NewArrayStub(i64, i8*) +declare void @llvm.cj.gcwrite.static(i8 addrspace(1)*, i8 addrspace(1)** nocapture) +declare void @llvm.cj.gcwrite.static.struct(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64) +declare void @llvm.cj.memset.p0i8(i8*, i8, i64, i1) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) + + +@"_ZN11std$FS$core17EMPTY_UINT8_ARRAYE" = global %"record._ZN11std$FS$core11std$FS$core5ArrayIhE" zeroinitializer, !GlobalVarType !0 #0 + +define void @foo1() unnamed_addr gc "cangjie" { +; CHECK-LABEL: @foo1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ENUMTMP3:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[TMP0:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[TMP13877E:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP2:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[TMP1:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[TMP13032E:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[ENUMTMP1:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[TMP2:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[TMP12978E:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[ENUMTMP:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[TMP3:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[TMP12957E:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[ENUMTMP3_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP3]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP3_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[DOT0_SROA_CAST3:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[TMP0]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[DOT0_SROA_CAST3]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[TMP4:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[TMP13877E]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP4]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[ENUMTMP2_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[ENUMTMP2]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP2_0_SROA_CAST]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[DOT0_SROA_CAST2:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP1]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[DOT0_SROA_CAST2]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[TMP5:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP13032E]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP5]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[ENUMTMP1_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[ENUMTMP1]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP1_0_SROA_CAST]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[DOT0_SROA_CAST1:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP2]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[DOT0_SROA_CAST1]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[TMP6:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP12978E]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP6]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[ENUMTMP_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[ENUMTMP]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP_0_SROA_CAST]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[DOT0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP3]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[DOT0_SROA_CAST]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[TMP7:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP12957E]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP7]], i8 0, i64 24, i1 false) +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(24) [[TMP7]], i8* noundef nonnull align 8 dereferenceable(24) [[DOT0_SROA_CAST]], i64 24, i1 false) +; CHECK-NEXT: [[TMP8:%.*]] = addrspacecast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP12957E]] to %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* +; CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP8]], i64 0, i32 1 +; CHECK-NEXT: store i64 0, i64 addrspace(1)* [[TMP9]], align 4 +; CHECK-NEXT: [[TMP10:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewArray8Stub(i64 0, i8* bitcast (%KlassInfo.0* @UInt8.arrayKlass to i8*)) +; CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP8]], i64 0, i32 0 +; CHECK-NEXT: store i8 addrspace(1)* [[TMP10]], i8 addrspace(1)* addrspace(1)* [[TMP11]], align 8 +; CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP8]], i64 0, i32 2 +; CHECK-NEXT: store i64 0, i64 addrspace(1)* [[TMP12]], align 4 +; CHECK-NEXT: call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* @"_ZN11std$FS$core17EMPTY_UINT8_ARRAYE" to i8*), i8* nonnull [[TMP7]], i64 24), !AggType !6 +; CHECK-NEXT: [[TMP13:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewArray8Stub(i64 0, i8* bitcast (%KlassInfo.0* @UInt8.arrayKlass to i8*)) +; CHECK-NEXT: call void @llvm.cj.gcwrite.static(i8 addrspace(1)* [[TMP13]], i8 addrspace(1)** nonnull @"_ZN11std$FS$core21EMPTY_UINT8_RAW_ARRAYE") +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(24) [[TMP6]], i8* noundef nonnull align 8 dereferenceable(24) [[DOT0_SROA_CAST1]], i64 24, i1 false) +; CHECK-NEXT: [[TMP14:%.*]] = addrspacecast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP12978E]] to %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* +; CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP14]], i64 0, i32 1 +; CHECK-NEXT: store i64 0, i64 addrspace(1)* [[TMP15]], align 4 +; CHECK-NEXT: [[TMP16:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewArray64Stub(i64 0, i8* bitcast (%KlassInfo.0* @Int64.arrayKlass to i8*)) +; CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP14]], i64 0, i32 0 +; CHECK-NEXT: store i8 addrspace(1)* [[TMP16]], i8 addrspace(1)* addrspace(1)* [[TMP17]], align 8 +; CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP14]], i64 0, i32 2 +; CHECK-NEXT: store i64 0, i64 addrspace(1)* [[TMP18]], align 4 +; CHECK-NEXT: call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* @"_ZN11std$FS$core17EMPTY_INT64_ARRAYE" to i8*), i8* nonnull [[TMP6]], i64 24), !AggType !7 +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(24) [[TMP5]], i8* noundef nonnull align 8 dereferenceable(24) [[DOT0_SROA_CAST2]], i64 24, i1 false) +; CHECK-NEXT: [[TMP19:%.*]] = addrspacecast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP13032E]] to %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* +; CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP19]], i64 0, i32 1 +; CHECK-NEXT: store i64 0, i64 addrspace(1)* [[TMP20]], align 4 +; CHECK-NEXT: [[TMP21:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewArrayStub(i64 0, i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core6StringE.arrayKlass" to i8*)) +; CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP19]], i64 0, i32 0 +; CHECK-NEXT: store i8 addrspace(1)* [[TMP21]], i8 addrspace(1)* addrspace(1)* [[TMP22]], align 8 +; CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP19]], i64 0, i32 2 +; CHECK-NEXT: store i64 0, i64 addrspace(1)* [[TMP23]], align 4 +; CHECK-NEXT: call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* @"_ZN11std$FS$core18EMPTY_STRING_ARRAYE" to i8*), i8* nonnull [[TMP5]], i64 24), !AggType !8 +; CHECK-NEXT: [[TMP24:%.*]] = addrspacecast %"record._ZN11std$FS$core6StringE"* [[TMP13877E]] to %"record._ZN11std$FS$core6StringE" addrspace(1)* +; CHECK-NEXT: [[TMP25:%.*]] = load i8 addrspace(1)*, i8 addrspace(1)** @"_ZN11std$FS$core21EMPTY_UINT8_RAW_ARRAYE", align 8 +; CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core6StringE", %"record._ZN11std$FS$core6StringE" addrspace(1)* [[TMP24]], i64 0, i32 0 +; CHECK-NEXT: store i8 addrspace(1)* [[TMP25]], i8 addrspace(1)* addrspace(1)* [[TMP26]], align 8 +; CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core6StringE", %"record._ZN11std$FS$core6StringE" addrspace(1)* [[TMP24]], i64 0, i32 1 +; CHECK-NEXT: store i64 0, i64 addrspace(1)* [[TMP27]], align 4 +; CHECK-NEXT: call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%"record._ZN11std$FS$core6StringE"* @"_ZN11std$FS$core6String5emptyE" to i8*), i8* nonnull [[TMP4]], i64 16), !AggType !9 +; CHECK-NEXT: ret void +; +entry: + %enumTmp3 = alloca %"record._ZN11std$FS$core6StringE" + %0 = alloca %"record._ZN11std$FS$core6StringE" + %tmp13877E = alloca %"record._ZN11std$FS$core6StringE" + %enumTmp2 = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" + %1 = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" + %tmp13032E = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" + %enumTmp1 = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" + %2 = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" + %tmp12978E = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" + %enumTmp = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIhE" + %3 = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIhE" + %tmp12957E = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIhE" + %enumTmp3.0.sroa_cast = bitcast %"record._ZN11std$FS$core6StringE"* %enumTmp3 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %enumTmp3.0.sroa_cast, i8 0, i64 16, i1 false) + %.0.sroa_cast3 = bitcast %"record._ZN11std$FS$core6StringE"* %0 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %.0.sroa_cast3, i8 0, i64 16, i1 false) + %4 = bitcast %"record._ZN11std$FS$core6StringE"* %tmp13877E to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %4, i8 0, i64 16, i1 false) + %enumTmp2.0.sroa_cast = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE"* %enumTmp2 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %enumTmp2.0.sroa_cast, i8 0, i64 24, i1 false) + %.0.sroa_cast2 = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE"* %1 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %.0.sroa_cast2, i8 0, i64 24, i1 false) + %5 = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE"* %tmp13032E to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %5, i8 0, i64 24, i1 false) + %enumTmp1.0.sroa_cast = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* %enumTmp1 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %enumTmp1.0.sroa_cast, i8 0, i64 24, i1 false) + %.0.sroa_cast1 = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* %2 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %.0.sroa_cast1, i8 0, i64 24, i1 false) + %6 = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* %tmp12978E to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %6, i8 0, i64 24, i1 false) + %enumTmp.0.sroa_cast = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIhE"* %enumTmp to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %enumTmp.0.sroa_cast, i8 0, i64 24, i1 false) + %.0.sroa_cast = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIhE"* %3 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %.0.sroa_cast, i8 0, i64 24, i1 false) + %7 = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIhE"* %tmp12957E to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %7, i8 0, i64 24, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(24) %7, i8* noundef nonnull align 8 dereferenceable(24) %.0.sroa_cast, i64 24, i1 false) + %8 = addrspacecast %"record._ZN11std$FS$core11std$FS$core5ArrayIhE"* %tmp12957E to %"record._ZN11std$FS$core11std$FS$core5ArrayIhE" addrspace(1)* + %9 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIhE", %"record._ZN11std$FS$core11std$FS$core5ArrayIhE" addrspace(1)* %8, i64 0, i32 1 + store i64 0, i64 addrspace(1)* %9 + %10 = call noalias i8 addrspace(1)* @CJ_MCC_NewArray8Stub(i64 0, i8* bitcast (%KlassInfo.0* @UInt8.arrayKlass to i8*)) + %11 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIhE", %"record._ZN11std$FS$core11std$FS$core5ArrayIhE" addrspace(1)* %8, i64 0, i32 0 + store i8 addrspace(1)* %10, i8 addrspace(1)* addrspace(1)* %11 + %12 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIhE", %"record._ZN11std$FS$core11std$FS$core5ArrayIhE" addrspace(1)* %8, i64 0, i32 2 + store i64 0, i64 addrspace(1)* %12 + call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%"record._ZN11std$FS$core11std$FS$core5ArrayIhE"* @"_ZN11std$FS$core17EMPTY_UINT8_ARRAYE" to i8*), i8* nonnull %7, i64 24), !AggType !6 + %13 = call noalias i8 addrspace(1)* @CJ_MCC_NewArray8Stub(i64 0, i8* bitcast (%KlassInfo.0* @UInt8.arrayKlass to i8*)) + call void @llvm.cj.gcwrite.static(i8 addrspace(1)* %13, i8 addrspace(1)** nonnull @"_ZN11std$FS$core21EMPTY_UINT8_RAW_ARRAYE") + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(24) %6, i8* noundef nonnull align 8 dereferenceable(24) %.0.sroa_cast1, i64 24, i1 false) + %14 = addrspacecast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* %tmp12978E to %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* + %15 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* %14, i64 0, i32 1 + store i64 0, i64 addrspace(1)* %15 + %16 = call noalias i8 addrspace(1)* @CJ_MCC_NewArray64Stub(i64 0, i8* bitcast (%KlassInfo.0* @Int64.arrayKlass to i8*)) + %17 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* %14, i64 0, i32 0 + store i8 addrspace(1)* %16, i8 addrspace(1)* addrspace(1)* %17 + %18 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* %14, i64 0, i32 2 + store i64 0, i64 addrspace(1)* %18 + call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* @"_ZN11std$FS$core17EMPTY_INT64_ARRAYE" to i8*), i8* nonnull %6, i64 24), !AggType !7 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(24) %5, i8* noundef nonnull align 8 dereferenceable(24) %.0.sroa_cast2, i64 24, i1 false) + %19 = addrspacecast %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE"* %tmp13032E to %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" addrspace(1)* + %20 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE", %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" addrspace(1)* %19, i64 0, i32 1 + store i64 0, i64 addrspace(1)* %20 + %21 = call noalias i8 addrspace(1)* @CJ_MCC_NewArrayStub(i64 0, i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core6StringE.arrayKlass" to i8*)) + %22 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE", %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" addrspace(1)* %19, i64 0, i32 0 + store i8 addrspace(1)* %21, i8 addrspace(1)* addrspace(1)* %22 + %23 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE", %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" addrspace(1)* %19, i64 0, i32 2 + store i64 0, i64 addrspace(1)* %23 + call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE"* @"_ZN11std$FS$core18EMPTY_STRING_ARRAYE" to i8*), i8* nonnull %5, i64 24), !AggType !8 + %24 = addrspacecast %"record._ZN11std$FS$core6StringE"* %tmp13877E to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %25 = load i8 addrspace(1)*, i8 addrspace(1)** @"_ZN11std$FS$core21EMPTY_UINT8_RAW_ARRAYE" + %26 = getelementptr inbounds %"record._ZN11std$FS$core6StringE", %"record._ZN11std$FS$core6StringE" addrspace(1)* %24, i64 0, i32 0 + store i8 addrspace(1)* %25, i8 addrspace(1)* addrspace(1)* %26 + %27 = getelementptr inbounds %"record._ZN11std$FS$core6StringE", %"record._ZN11std$FS$core6StringE" addrspace(1)* %24, i64 0, i32 1 + store i64 0, i64 addrspace(1)* %27 + call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%"record._ZN11std$FS$core6StringE"* @"_ZN11std$FS$core6String5emptyE" to i8*), i8* nonnull %4, i64 16), !AggType !5 + ret void +} + +define void @foo2(%"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE"* noalias nocapture writeonly sret(%"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE") %0, i8 addrspace(1)* nocapture readonly %this) gc "cangjie" { +; CHECK-LABEL: @foo2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS:%.*]], i64 24 +; CHECK-NEXT: [[TMP2:%.*]] = bitcast i8 addrspace(1)* [[TMP1]] to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: [[TMP3:%.*]] = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* [[TMP2]], align 8 +; CHECK-NEXT: [[TMP4:%.*]] = tail call i8 addrspace(1)* @CJ_MCC_DecodeStackTrace(i8 addrspace(1)* [[TMP3]], i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core17StackTraceElementE.objKlass" to i8*), i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core17StackTraceElementE.refArrayKlass" to i8*), i8* bitcast (%KlassInfo.0* @UInt8.arrayKlass to i8*)) +; CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[TMP4]], i64 8 +; CHECK-NEXT: [[TMP6:%.*]] = bitcast i8 addrspace(1)* [[TMP5]] to i64 addrspace(1)* +; CHECK-NEXT: [[TMP7:%.*]] = load i64, i64 addrspace(1)* [[TMP6]], align 4 +; CHECK-NEXT: [[ATOM_33641E_SROA_0_0__SROA_IDX2:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP0:%.*]], i64 0, i32 0 +; CHECK-NEXT: store i8 addrspace(1)* [[TMP4]], i8 addrspace(1)** [[ATOM_33641E_SROA_0_0__SROA_IDX2]], align 8 +; CHECK-NEXT: [[ATOM_33641E_SROA_4_0__SROA_IDX5:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP0]], i64 0, i32 1 +; CHECK-NEXT: store i64 0, i64* [[ATOM_33641E_SROA_4_0__SROA_IDX5]], align 4 +; CHECK-NEXT: [[ATOM_33641E_SROA_5_0__SROA_IDX8:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP0]], i64 0, i32 2 +; CHECK-NEXT: store i64 [[TMP7]], i64* [[ATOM_33641E_SROA_5_0__SROA_IDX8]], align 4 +; CHECK-NEXT: ret void +; +entry: + %1 = getelementptr inbounds i8, i8 addrspace(1)* %this, i64 24 + %2 = bitcast i8 addrspace(1)* %1 to i8 addrspace(1)* addrspace(1)* + %3 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %2 + %4 = tail call i8 addrspace(1)* @CJ_MCC_DecodeStackTrace(i8 addrspace(1)* %3, i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core17StackTraceElementE.objKlass" to i8*), i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core17StackTraceElementE.refArrayKlass" to i8*), i8* bitcast (%KlassInfo.0* @UInt8.arrayKlass to i8*)) + %5 = getelementptr inbounds i8, i8 addrspace(1)* %4, i64 8 + %6 = bitcast i8 addrspace(1)* %5 to i64 addrspace(1)* + %7 = load i64, i64 addrspace(1)* %6 + %atom.33641E.sroa.0.0..sroa_idx2 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE", %"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE"* %0, i64 0, i32 0 + store i8 addrspace(1)* %4, i8 addrspace(1)** %atom.33641E.sroa.0.0..sroa_idx2 + %atom.33641E.sroa.4.0..sroa_idx5 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE", %"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE"* %0, i64 0, i32 1 + store i64 0, i64* %atom.33641E.sroa.4.0..sroa_idx5 + %atom.33641E.sroa.5.0..sroa_idx8 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE", %"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE"* %0, i64 0, i32 2 + store i64 %7, i64* %atom.33641E.sroa.5.0..sroa_idx8 + ret void +} + +attributes #0 = { readonly "GCRoot" } + +!llvm.module.flags = !{!9} + +; CHECK: !0 = !{!"R_ZN11std$FS$core5ArrayIlE" +; CHECK-NEXT: !1 = !{!"R_ZN11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE"} +; CHECK-NEXT: !2 = !{!"A1_hE"} +; CHECK-NEXT: !3 = !{!"R_ZN11std$FS$core6StringE"} +; CHECK-NEXT: !4 = !{!"R_ZN11std$FS$core5ArrayIhE"} +; CHECK-NEXT: !5 = !{i32 2, !"CJBC", i32 1} +; CHECK-NEXT: !6 = distinct !{!"record._ZN11std$FS$core11std$FS$core5ArrayIlE"} +; CHECK-NEXT: !7 = !{!"record._ZN11std$FS$core11std$FS$core5ArrayIlE"} +; CHECK-NEXT: !8 = distinct !{!"record._ZN11std$FS$core11std$FS$core5ArrayIlE"} +; CHECK-NEXT: !9 = !{!"record._ZN11std$FS$core6StringE"} +!0 = !{!"R_ZN11std$FS$core5ArrayIhE"} +!1 = !{!"A1_hE"} +!2 = !{!"R_ZN11std$FS$core5ArrayIlE"} +!3 = !{!"R_ZN11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE"} +!4 = !{!"R_ZN11std$FS$core6StringE"} +!5 = !{!"record._ZN11std$FS$core6StringE"} +!6 = !{!"record._ZN11std$FS$core11std$FS$core5ArrayIhE"} +!7 = !{!"record._ZN11std$FS$core11std$FS$core5ArrayIlE"} +!8 = !{!"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE"} +!9 = !{i32 2, !"CJBC", i32 1} diff --git a/llvm/test/CJLTO/cj-link2.ll b/llvm/test/CJLTO/cj-link2.ll new file mode 100644 index 000000000..ed0671529 --- /dev/null +++ b/llvm/test/CJLTO/cj-link2.ll @@ -0,0 +1,965 @@ +; RUN: llvm-as %s -o %t.bc +; RUN: llvm-as %p/Inputs/same-type1.ll -o %t1.bc +; RUN: llvm-as %p/Inputs/same-type2.ll -o %t2.bc +; RUN: llvm-link %t.bc %t1.bc %t2.bc -S | FileCheck %s + +; CHECK: %KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +; CHECK-NEXT: %BitMap = type { i32, [0 x i8] } +; CHECK-NEXT: %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" = type { i8 addrspace(1)*, i64, i64 } +; CHECK-NEXT: %KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [1 x i8*] } +; CHECK-NEXT: %"record._ZN11std$FS$core6StringE" = type { i8 addrspace(1)*, i64 } +; CHECK-NEXT: %"record._ZN13std$FS$random11std$FS$core6OptionIdE" = type { i1, double } +; CHECK-NEXT: %ArrayLayout.UInt64 = type { %ArrayBase, [0 x i64] } +; CHECK-NEXT: %ArrayBase = type { %ObjLayout.Object, i64 } +; CHECK-NEXT: %ObjLayout.Object = type { %KlassInfo.0* } +; CHECK-NEXT: %ArrayLayout.UInt8 = type { %ArrayBase, [0 x i8] } +; CHECK-NEXT: %Unit.Type = type { i8 } +; CHECK-NEXT: %"record._ZN11std$FS$core8Utf8ViewE" = type { %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" } +; CHECK-NEXT: %T2_clE = type { i32, i64 } +; CHECK-NEXT: %ArrayLayout.Char = type { %ArrayBase, [0 x i32] } +; CHECK-NEXT: %"record._ZN17std$FS$collection11std$FS$core6OptionIcE" = type { i1, i32 } +; CHECK-NEXT: %"ArrayLayout._ZN11std$FS$core6StringE" = type { %ArrayBase, [0 x %"record._ZN11std$FS$core6StringE"] } +%KlassInfo.0 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [0 x i8*] } +%BitMap = type { i32, [0 x i8] } +%KlassInfo.1 = type { i32, i32, %BitMap*, i8*, i8**, i64*, i64*, i64*, i32, [1 x i8*] } +%"record._ZN11std$FS$core11std$FS$core5ArrayIhE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN11std$FS$core11std$FS$core5ArrayIlE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE" = type { i8 addrspace(1)*, i64, i64 } +%"record._ZN11std$FS$core6StringE" = type { i8 addrspace(1)*, i64 } + +; CHECK: @Int64.arrayKlass = weak_odr global %KlassInfo.0 { i32 2, i32 8, %BitMap* null, i8* bitcast (%KlassInfo.0* @array_int64.primKlass to i8*), i8** null, i64* null, i64* null, i64* bitcast ([6 x i8]* @A1_lE.className to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @A1_lE.className = weak_odr global [6 x i8] c"A1_lE\00", align 1 +; CHECK-NEXT: @array_int64.primKlass = weak_odr global %KlassInfo.0 { i32 1, i32 8, %BitMap* null, i8* null, i8** null, i64* null, i64* null, i64* @array_int64.uniqueAddr, i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @array_int64.uniqueAddr = weak_odr global i64 0 +; CHECK-NEXT: @UInt8.arrayKlass = weak_odr global %KlassInfo.0 { i32 2, i32 1, %BitMap* null, i8* bitcast (%KlassInfo.0* @array_uint8.primKlass to i8*), i8** null, i64* null, i64* null, i64* bitcast ([6 x i8]* @A1_hE.className to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @A1_hE.className = weak_odr global [6 x i8] c"A1_hE\00", align 1 +; CHECK-NEXT: @array_uint8.primKlass = weak_odr global %KlassInfo.0 { i32 1, i32 1, %BitMap* null, i8* null, i8** null, i64* null, i64* null, i64* @array_uint8.uniqueAddr, i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @array_uint8.uniqueAddr = weak_odr global i64 0 +; CHECK-NEXT: @"_ZN11std$FS$core17EMPTY_INT64_ARRAYE" = global %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" zeroinitializer, !GlobalVarType !0 #0 +; CHECK-NEXT: @"_ZN11std$FS$core17StackTraceElementE.objKlass" = weak_odr global %KlassInfo.0 { i32 36, i32 64, %BitMap* inttoptr (i64 -9223372036854775766 to %BitMap*), i8* bitcast (%KlassInfo.1* @"_ZN11std$FS$core6ObjectE.objKlass" to i8*), i8** null, i64* null, i64* null, i64* bitcast ([38 x i8]* @"C_ZN11std$FS$core17StackTraceElementE.className" to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @"C_ZN11std$FS$core17StackTraceElementE.className" = internal global [38 x i8] c"C_ZN11std$FS$core17StackTraceElementE\00", align 1 +; CHECK-NEXT: @"_ZN11std$FS$core6ObjectE.objKlass" = weak_odr global %KlassInfo.1 { i32 4, i32 8, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* bitcast (%KlassInfo.0* @Object.objKlass to i8*), i8** null, i64* null, i64* null, i64* bitcast ([26 x i8]* @"C_ZN11std$FS$core6ObjectE.className" to i64*), i32 1, [1 x i8*] [i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core3AnyE.objKlass" to i8*)] } #0 +; CHECK-NEXT: @Object.objKlass = weak_odr global %KlassInfo.0 { i32 4, i32 8, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i64* null, i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @"C_ZN11std$FS$core6ObjectE.className" = internal global [26 x i8] c"C_ZN11std$FS$core6ObjectE\00", align 1 +; CHECK-NEXT: @"_ZN11std$FS$core3AnyE.objKlass" = weak_odr global %KlassInfo.0 { i32 16, i32 8, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i64* bitcast ([23 x i8]* @"C_ZN11std$FS$core3AnyE.className" to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @"C_ZN11std$FS$core3AnyE.className" = internal global [23 x i8] c"C_ZN11std$FS$core3AnyE\00", align 1 +; CHECK-NEXT: @"_ZN11std$FS$core17StackTraceElementE.refArrayKlass" = internal global %KlassInfo.0 { i32 34, i32 8, %BitMap* null, i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core17StackTraceElementE.objKlass" to i8*), i8** null, i64* null, i64* null, i64* bitcast ([42 x i8]* @"A1_C_ZN11std$FS$core17StackTraceElementEE.className" to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @"A1_C_ZN11std$FS$core17StackTraceElementEE.className" = weak_odr global [42 x i8] c"A1_C_ZN11std$FS$core17StackTraceElementEE\00", align 1 +; CHECK-NEXT: @"_ZN11std$FS$core18EMPTY_STRING_ARRAYE" = internal global %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" zeroinitializer, !GlobalVarType !1 #0 +; CHECK-NEXT: @"_ZN11std$FS$core21EMPTY_UINT8_RAW_ARRAYE" = internal global i8 addrspace(1)* null, !GlobalVarType !2 #0 +; CHECK-NEXT: @"_ZN11std$FS$core17EMPTY_UINT8_ARRAYE" = global %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" zeroinitializer, !GlobalVarType !3 #0 +; CHECK-NEXT: @"$const_string.15" = private unnamed_addr constant [4 x i8] c"add\00", align 1 +; CHECK-NEXT: @"$const_string.16" = private unnamed_addr constant [4 x i8] c"sub\00", align 1 +; CHECK-NEXT: @"$const_string.22" = private unnamed_addr constant [9 x i8] c"typecast\00", align 1 +; CHECK-NEXT: @UInt64.arrayKlass = weak_odr global %KlassInfo.0 { i32 2, i32 8, %BitMap* null, i8* bitcast (%KlassInfo.0* @array_uint64.primKlass to i8*), i8** null, i64* null, i64* null, i64* bitcast ([6 x i8]* @A1_mE.className to i64*), i32 0, [0 x i8*] zeroinitializer } +; CHECK-NEXT: @A1_mE.className = weak_odr global [6 x i8] c"A1_mE\00", align 1 +; CHECK-NEXT: @array_uint64.primKlass = weak_odr global %KlassInfo.0 { i32 1, i32 8, %BitMap* null, i8* null, i8** null, i64* null, i64* null, i64* @array_uint64.uniqueAddr, i32 0, [0 x i8*] zeroinitializer } +; CHECK-NEXT: @array_uint64.uniqueAddr = weak_odr global i64 0 +; CHECK-NEXT: @"$const_string.29" = private unnamed_addr constant [4 x i8] c"add\00", align 1 +; CHECK-NEXT: @"$const_string.30" = private unnamed_addr constant [4 x i8] c"mul\00", align 1 +; CHECK-NEXT: @"$const_string.33" = private unnamed_addr constant [11 x i8] c"iterator$0\00", align 1 +; CHECK-NEXT: @"$const_string.34" = private unnamed_addr constant [7 x i8] c"next$0\00", align 1 +; CHECK-NEXT: @"$const_string.35" = private unnamed_addr constant [11 x i8] c"$sizeget$0\00", align 1 +; CHECK-NEXT: @Char.arrayKlass = weak_odr global %KlassInfo.0 { i32 2, i32 4, %BitMap* null, i8* bitcast (%KlassInfo.0* @array_char.primKlass to i8*), i8** null, i64* null, i64* null, i64* bitcast ([6 x i8]* @A1_cE.className to i64*), i32 0, [0 x i8*] zeroinitializer } +; CHECK-NEXT: @A1_cE.className = weak_odr global [6 x i8] c"A1_cE\00", align 1 +; CHECK-NEXT: @array_char.primKlass = weak_odr global %KlassInfo.0 { i32 1, i32 4, %BitMap* null, i8* null, i8** null, i64* null, i64* null, i64* @array_char.uniqueAddr, i32 0, [0 x i8*] zeroinitializer } +; CHECK-NEXT: @array_char.uniqueAddr = weak_odr global i64 0 +; CHECK-NEXT: @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" = external global %KlassInfo.0 +; CHECK-NEXT: @"_ZN11std$FS$core26NegativeArraySizeExceptionE.objKlass" = external global %KlassInfo.0 +; CHECK-NEXT: @"_ZN11std$FS$core6String5emptyE" = global %"record._ZN11std$FS$core6StringE" zeroinitializer, !GlobalVarType !4 #0 +; CHECK-NEXT: @"_ZN11std$FS$core6StringE.arrayKlass" = weak_odr global %KlassInfo.0 { i32 34, i32 16, %BitMap* null, i8* bitcast (%KlassInfo.0* @"record._ZN11std$FS$core6StringE.structKlass" to i8*), i8** null, i64* null, i64* null, i64* bitcast ([30 x i8]* @"A1_R_ZN11std$FS$core6StringEE.className" to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @"A1_R_ZN11std$FS$core6StringEE.className" = weak_odr global [30 x i8] c"A1_R_ZN11std$FS$core6StringEE\00", align 1 +; CHECK-NEXT: @"record._ZN11std$FS$core6StringE.structKlass" = weak_odr global %KlassInfo.0 { i32 40, i32 16, %BitMap* inttoptr (i64 -9223372036854775807 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i64* @"record._ZN11std$FS$core6StringE.uniqueAddr", i32 0, [0 x i8*] zeroinitializer } #0 +; CHECK-NEXT: @"record._ZN11std$FS$core6StringE.uniqueAddr" = weak_odr global i64 0 +; CHECK-NEXT: @"_ZN15std$$collection22$__STRING_LITERAL__$22E" = internal global %"record._ZN11std$FS$core6StringE" zeroinitializer +; CHECK-NEXT: @"_ZN15std$$collection22$__STRING_LITERAL__$23E" = internal global %"record._ZN11std$FS$core6StringE" zeroinitializer +; CHECK-NEXT: @"_ZN15std$$collection22$__STRING_LITERAL__$24E" = internal global %"record._ZN11std$FS$core6StringE" zeroinitializer +; CHECK-NEXT: @"_ZN15std$$collection22$__STRING_LITERAL__$25E" = internal global %"record._ZN11std$FS$core6StringE" zeroinitializer +@Int64.arrayKlass = weak_odr global %KlassInfo.0 { i32 2, i32 8, %BitMap* null, i8* bitcast (%KlassInfo.0* @array_int64.primKlass to i8*), i8** null, i64* null, i64* null, i64* bitcast ([6 x i8]* @A1_lE.className to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +@A1_lE.className = weak_odr global [6 x i8] c"A1_lE\00", align 1 +@array_int64.primKlass = weak_odr global %KlassInfo.0 { i32 1, i32 8, %BitMap* null, i8* null, i8** null, i64* null, i64* null, i64* @array_int64.uniqueAddr, i32 0, [0 x i8*] zeroinitializer } #0 +@array_int64.uniqueAddr = weak_odr global i64 0 +@UInt8.arrayKlass = weak_odr global %KlassInfo.0 { i32 2, i32 1, %BitMap* null, i8* bitcast (%KlassInfo.0* @array_uint8.primKlass to i8*), i8** null, i64* null, i64* null, i64* bitcast ([6 x i8]* @A1_hE.className to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +@A1_hE.className = weak_odr global [6 x i8] c"A1_hE\00", align 1 +@array_uint8.primKlass = weak_odr global %KlassInfo.0 { i32 1, i32 1, %BitMap* null, i8* null, i8** null, i64* null, i64* null, i64* @array_uint8.uniqueAddr, i32 0, [0 x i8*] zeroinitializer } #0 +@array_uint8.uniqueAddr = weak_odr global i64 0 +@"_ZN11std$FS$core17EMPTY_INT64_ARRAYE" = global %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" zeroinitializer, !GlobalVarType !2 #0 +@"_ZN11std$FS$core17StackTraceElementE.objKlass" = weak_odr global %KlassInfo.0 { i32 36, i32 64, %BitMap* inttoptr (i64 -9223372036854775766 to %BitMap*), i8* bitcast (%KlassInfo.1* @"_ZN11std$FS$core6ObjectE.objKlass" to i8*), i8** null, i64* null, i64* null, i64* bitcast ([38 x i8]* @"C_ZN11std$FS$core17StackTraceElementE.className" to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +@"C_ZN11std$FS$core17StackTraceElementE.className" = internal global [38 x i8] c"C_ZN11std$FS$core17StackTraceElementE\00", align 1 +@"_ZN11std$FS$core6ObjectE.objKlass" = weak_odr global %KlassInfo.1 { i32 4, i32 8, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* bitcast (%KlassInfo.0* @Object.objKlass to i8*), i8** null, i64* null, i64* null, i64* bitcast ([26 x i8]* @"C_ZN11std$FS$core6ObjectE.className" to i64*), i32 1, [1 x i8*] [i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core3AnyE.objKlass" to i8*)] } #0 +@Object.objKlass = weak_odr global %KlassInfo.0 { i32 4, i32 8, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i64* null, i32 0, [0 x i8*] zeroinitializer } #0 +@"C_ZN11std$FS$core6ObjectE.className" = internal global [26 x i8] c"C_ZN11std$FS$core6ObjectE\00", align 1 +@"_ZN11std$FS$core3AnyE.objKlass" = weak_odr global %KlassInfo.0 { i32 16, i32 8, %BitMap* inttoptr (i64 -9223372036854775808 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i64* bitcast ([23 x i8]* @"C_ZN11std$FS$core3AnyE.className" to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +@"C_ZN11std$FS$core3AnyE.className" = internal global [23 x i8] c"C_ZN11std$FS$core3AnyE\00", align 1 +@"_ZN11std$FS$core17StackTraceElementE.refArrayKlass" = internal global %KlassInfo.0 { i32 34, i32 8, %BitMap* null, i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core17StackTraceElementE.objKlass" to i8*), i8** null, i64* null, i64* null, i64* bitcast ([42 x i8]* @"A1_C_ZN11std$FS$core17StackTraceElementEE.className" to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +@"A1_C_ZN11std$FS$core17StackTraceElementEE.className" = weak_odr global [42 x i8] c"A1_C_ZN11std$FS$core17StackTraceElementEE\00", align 1 +@"_ZN11std$FS$core18EMPTY_STRING_ARRAYE" = internal global %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" zeroinitializer, !GlobalVarType !3 #0 +@"_ZN11std$FS$core21EMPTY_UINT8_RAW_ARRAYE" = internal global i8 addrspace(1)* null, !GlobalVarType !1 #0 +@"_ZN11std$FS$core6String5emptyE" = global %"record._ZN11std$FS$core6StringE" zeroinitializer, !GlobalVarType !4 #0 +@"_ZN11std$FS$core6StringE.arrayKlass" = weak_odr global %KlassInfo.0 { i32 34, i32 16, %BitMap* null, i8* bitcast (%KlassInfo.0* @"record._ZN11std$FS$core6StringE.structKlass" to i8*), i8** null, i64* null, i64* null, i64* bitcast ([30 x i8]* @"A1_R_ZN11std$FS$core6StringEE.className" to i64*), i32 0, [0 x i8*] zeroinitializer } #0 +@"A1_R_ZN11std$FS$core6StringEE.className" = weak_odr global [30 x i8] c"A1_R_ZN11std$FS$core6StringEE\00", align 1 +@"record._ZN11std$FS$core6StringE.structKlass" = weak_odr global %KlassInfo.0 { i32 40, i32 16, %BitMap* inttoptr (i64 -9223372036854775807 to %BitMap*), i8* null, i8** null, i64* null, i64* null, i64* @"record._ZN11std$FS$core6StringE.uniqueAddr", i32 0, [0 x i8*] zeroinitializer } #0 +@"record._ZN11std$FS$core6StringE.uniqueAddr" = weak_odr global i64 0 + + +declare i8 addrspace(1)* @CJ_MCC_DecodeStackTrace(i8 addrspace(1)*, i8*, i8*, i8*) +declare i8 addrspace(1)* @CJ_MCC_NewArray64Stub(i64, i8*) +declare i8 addrspace(1)* @CJ_MCC_NewArray8Stub(i64, i8*) +declare i8 addrspace(1)* @CJ_MCC_NewArrayStub(i64, i8*) +declare void @llvm.cj.gcwrite.static(i8 addrspace(1)*, i8 addrspace(1)** nocapture) +declare void @llvm.cj.gcwrite.static.struct(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64) +declare void @llvm.cj.memset.p0i8(i8*, i8, i64, i1) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) + + +@"_ZN11std$FS$core17EMPTY_UINT8_ARRAYE" = global %"record._ZN11std$FS$core11std$FS$core5ArrayIhE" zeroinitializer, !GlobalVarType !0 #0 + +define void @foo1() unnamed_addr gc "cangjie" { +; CHECK-LABEL: @foo1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ENUMTMP3:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[TMP0:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[TMP13877E:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP2:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[TMP1:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[TMP13032E:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[ENUMTMP1:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[TMP2:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[TMP12978E:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[ENUMTMP:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[TMP3:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[TMP12957E:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[ENUMTMP3_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP3]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP3_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[DOT0_SROA_CAST3:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[TMP0]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[DOT0_SROA_CAST3]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[TMP4:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[TMP13877E]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP4]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[ENUMTMP2_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[ENUMTMP2]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP2_0_SROA_CAST]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[DOT0_SROA_CAST2:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP1]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[DOT0_SROA_CAST2]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[TMP5:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP13032E]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP5]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[ENUMTMP1_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[ENUMTMP1]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP1_0_SROA_CAST]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[DOT0_SROA_CAST1:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP2]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[DOT0_SROA_CAST1]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[TMP6:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP12978E]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP6]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[ENUMTMP_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[ENUMTMP]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP_0_SROA_CAST]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[DOT0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP3]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[DOT0_SROA_CAST]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[TMP7:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP12957E]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP7]], i8 0, i64 24, i1 false) +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(24) [[TMP7]], i8* noundef nonnull align 8 dereferenceable(24) [[DOT0_SROA_CAST]], i64 24, i1 false) +; CHECK-NEXT: [[TMP8:%.*]] = addrspacecast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP12957E]] to %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* +; CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP8]], i64 0, i32 1 +; CHECK-NEXT: store i64 0, i64 addrspace(1)* [[TMP9]], align 4 +; CHECK-NEXT: [[TMP10:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewArray8Stub(i64 0, i8* bitcast (%KlassInfo.0* @UInt8.arrayKlass to i8*)) +; CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP8]], i64 0, i32 0 +; CHECK-NEXT: store i8 addrspace(1)* [[TMP10]], i8 addrspace(1)* addrspace(1)* [[TMP11]], align 8 +; CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP8]], i64 0, i32 2 +; CHECK-NEXT: store i64 0, i64 addrspace(1)* [[TMP12]], align 4 +; CHECK-NEXT: call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* @"_ZN11std$FS$core17EMPTY_UINT8_ARRAYE" to i8*), i8* nonnull [[TMP7]], i64 24), !AggType !6 +; CHECK-NEXT: [[TMP13:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewArray8Stub(i64 0, i8* bitcast (%KlassInfo.0* @UInt8.arrayKlass to i8*)) +; CHECK-NEXT: call void @llvm.cj.gcwrite.static(i8 addrspace(1)* [[TMP13]], i8 addrspace(1)** nonnull @"_ZN11std$FS$core21EMPTY_UINT8_RAW_ARRAYE") +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(24) [[TMP6]], i8* noundef nonnull align 8 dereferenceable(24) [[DOT0_SROA_CAST1]], i64 24, i1 false) +; CHECK-NEXT: [[TMP14:%.*]] = addrspacecast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP12978E]] to %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* +; CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP14]], i64 0, i32 1 +; CHECK-NEXT: store i64 0, i64 addrspace(1)* [[TMP15]], align 4 +; CHECK-NEXT: [[TMP16:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewArray64Stub(i64 0, i8* bitcast (%KlassInfo.0* @Int64.arrayKlass to i8*)) +; CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP14]], i64 0, i32 0 +; CHECK-NEXT: store i8 addrspace(1)* [[TMP16]], i8 addrspace(1)* addrspace(1)* [[TMP17]], align 8 +; CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP14]], i64 0, i32 2 +; CHECK-NEXT: store i64 0, i64 addrspace(1)* [[TMP18]], align 4 +; CHECK-NEXT: call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* @"_ZN11std$FS$core17EMPTY_INT64_ARRAYE" to i8*), i8* nonnull [[TMP6]], i64 24), !AggType !7 +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(24) [[TMP5]], i8* noundef nonnull align 8 dereferenceable(24) [[DOT0_SROA_CAST2]], i64 24, i1 false) +; CHECK-NEXT: [[TMP19:%.*]] = addrspacecast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP13032E]] to %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* +; CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP19]], i64 0, i32 1 +; CHECK-NEXT: store i64 0, i64 addrspace(1)* [[TMP20]], align 4 +; CHECK-NEXT: [[TMP21:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewArrayStub(i64 0, i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core6StringE.arrayKlass" to i8*)) +; CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP19]], i64 0, i32 0 +; CHECK-NEXT: store i8 addrspace(1)* [[TMP21]], i8 addrspace(1)* addrspace(1)* [[TMP22]], align 8 +; CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP19]], i64 0, i32 2 +; CHECK-NEXT: store i64 0, i64 addrspace(1)* [[TMP23]], align 4 +; CHECK-NEXT: call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* @"_ZN11std$FS$core18EMPTY_STRING_ARRAYE" to i8*), i8* nonnull [[TMP5]], i64 24), !AggType !8 +; CHECK-NEXT: [[TMP24:%.*]] = addrspacecast %"record._ZN11std$FS$core6StringE"* [[TMP13877E]] to %"record._ZN11std$FS$core6StringE" addrspace(1)* +; CHECK-NEXT: [[TMP25:%.*]] = load i8 addrspace(1)*, i8 addrspace(1)** @"_ZN11std$FS$core21EMPTY_UINT8_RAW_ARRAYE", align 8 +; CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core6StringE", %"record._ZN11std$FS$core6StringE" addrspace(1)* [[TMP24]], i64 0, i32 0 +; CHECK-NEXT: store i8 addrspace(1)* [[TMP25]], i8 addrspace(1)* addrspace(1)* [[TMP26]], align 8 +; CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core6StringE", %"record._ZN11std$FS$core6StringE" addrspace(1)* [[TMP24]], i64 0, i32 1 +; CHECK-NEXT: store i64 0, i64 addrspace(1)* [[TMP27]], align 4 +; CHECK-NEXT: call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%"record._ZN11std$FS$core6StringE"* @"_ZN11std$FS$core6String5emptyE" to i8*), i8* nonnull [[TMP4]], i64 16), !AggType !9 +; CHECK-NEXT: ret void +; +entry: + %enumTmp3 = alloca %"record._ZN11std$FS$core6StringE" + %0 = alloca %"record._ZN11std$FS$core6StringE" + %tmp13877E = alloca %"record._ZN11std$FS$core6StringE" + %enumTmp2 = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" + %1 = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" + %tmp13032E = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" + %enumTmp1 = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" + %2 = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" + %tmp12978E = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" + %enumTmp = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIhE" + %3 = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIhE" + %tmp12957E = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIhE" + %enumTmp3.0.sroa_cast = bitcast %"record._ZN11std$FS$core6StringE"* %enumTmp3 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %enumTmp3.0.sroa_cast, i8 0, i64 16, i1 false) + %.0.sroa_cast3 = bitcast %"record._ZN11std$FS$core6StringE"* %0 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %.0.sroa_cast3, i8 0, i64 16, i1 false) + %4 = bitcast %"record._ZN11std$FS$core6StringE"* %tmp13877E to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %4, i8 0, i64 16, i1 false) + %enumTmp2.0.sroa_cast = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE"* %enumTmp2 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %enumTmp2.0.sroa_cast, i8 0, i64 24, i1 false) + %.0.sroa_cast2 = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE"* %1 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %.0.sroa_cast2, i8 0, i64 24, i1 false) + %5 = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE"* %tmp13032E to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %5, i8 0, i64 24, i1 false) + %enumTmp1.0.sroa_cast = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* %enumTmp1 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %enumTmp1.0.sroa_cast, i8 0, i64 24, i1 false) + %.0.sroa_cast1 = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* %2 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %.0.sroa_cast1, i8 0, i64 24, i1 false) + %6 = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* %tmp12978E to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %6, i8 0, i64 24, i1 false) + %enumTmp.0.sroa_cast = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIhE"* %enumTmp to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %enumTmp.0.sroa_cast, i8 0, i64 24, i1 false) + %.0.sroa_cast = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIhE"* %3 to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull %.0.sroa_cast, i8 0, i64 24, i1 false) + %7 = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIhE"* %tmp12957E to i8* + call void @llvm.cj.memset.p0i8(i8* nonnull align 8 %7, i8 0, i64 24, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(24) %7, i8* noundef nonnull align 8 dereferenceable(24) %.0.sroa_cast, i64 24, i1 false) + %8 = addrspacecast %"record._ZN11std$FS$core11std$FS$core5ArrayIhE"* %tmp12957E to %"record._ZN11std$FS$core11std$FS$core5ArrayIhE" addrspace(1)* + %9 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIhE", %"record._ZN11std$FS$core11std$FS$core5ArrayIhE" addrspace(1)* %8, i64 0, i32 1 + store i64 0, i64 addrspace(1)* %9 + %10 = call noalias i8 addrspace(1)* @CJ_MCC_NewArray8Stub(i64 0, i8* bitcast (%KlassInfo.0* @UInt8.arrayKlass to i8*)) + %11 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIhE", %"record._ZN11std$FS$core11std$FS$core5ArrayIhE" addrspace(1)* %8, i64 0, i32 0 + store i8 addrspace(1)* %10, i8 addrspace(1)* addrspace(1)* %11 + %12 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIhE", %"record._ZN11std$FS$core11std$FS$core5ArrayIhE" addrspace(1)* %8, i64 0, i32 2 + store i64 0, i64 addrspace(1)* %12 + call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%"record._ZN11std$FS$core11std$FS$core5ArrayIhE"* @"_ZN11std$FS$core17EMPTY_UINT8_ARRAYE" to i8*), i8* nonnull %7, i64 24), !AggType !6 + %13 = call noalias i8 addrspace(1)* @CJ_MCC_NewArray8Stub(i64 0, i8* bitcast (%KlassInfo.0* @UInt8.arrayKlass to i8*)) + call void @llvm.cj.gcwrite.static(i8 addrspace(1)* %13, i8 addrspace(1)** nonnull @"_ZN11std$FS$core21EMPTY_UINT8_RAW_ARRAYE") + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(24) %6, i8* noundef nonnull align 8 dereferenceable(24) %.0.sroa_cast1, i64 24, i1 false) + %14 = addrspacecast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* %tmp12978E to %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* + %15 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* %14, i64 0, i32 1 + store i64 0, i64 addrspace(1)* %15 + %16 = call noalias i8 addrspace(1)* @CJ_MCC_NewArray64Stub(i64 0, i8* bitcast (%KlassInfo.0* @Int64.arrayKlass to i8*)) + %17 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* %14, i64 0, i32 0 + store i8 addrspace(1)* %16, i8 addrspace(1)* addrspace(1)* %17 + %18 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* %14, i64 0, i32 2 + store i64 0, i64 addrspace(1)* %18 + call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* @"_ZN11std$FS$core17EMPTY_INT64_ARRAYE" to i8*), i8* nonnull %6, i64 24), !AggType !7 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(24) %5, i8* noundef nonnull align 8 dereferenceable(24) %.0.sroa_cast2, i64 24, i1 false) + %19 = addrspacecast %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE"* %tmp13032E to %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" addrspace(1)* + %20 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE", %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" addrspace(1)* %19, i64 0, i32 1 + store i64 0, i64 addrspace(1)* %20 + %21 = call noalias i8 addrspace(1)* @CJ_MCC_NewArrayStub(i64 0, i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core6StringE.arrayKlass" to i8*)) + %22 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE", %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" addrspace(1)* %19, i64 0, i32 0 + store i8 addrspace(1)* %21, i8 addrspace(1)* addrspace(1)* %22 + %23 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE", %"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE" addrspace(1)* %19, i64 0, i32 2 + store i64 0, i64 addrspace(1)* %23 + call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE"* @"_ZN11std$FS$core18EMPTY_STRING_ARRAYE" to i8*), i8* nonnull %5, i64 24), !AggType !8 + %24 = addrspacecast %"record._ZN11std$FS$core6StringE"* %tmp13877E to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %25 = load i8 addrspace(1)*, i8 addrspace(1)** @"_ZN11std$FS$core21EMPTY_UINT8_RAW_ARRAYE" + %26 = getelementptr inbounds %"record._ZN11std$FS$core6StringE", %"record._ZN11std$FS$core6StringE" addrspace(1)* %24, i64 0, i32 0 + store i8 addrspace(1)* %25, i8 addrspace(1)* addrspace(1)* %26 + %27 = getelementptr inbounds %"record._ZN11std$FS$core6StringE", %"record._ZN11std$FS$core6StringE" addrspace(1)* %24, i64 0, i32 1 + store i64 0, i64 addrspace(1)* %27 + call void @llvm.cj.gcwrite.static.struct(i8* bitcast (%"record._ZN11std$FS$core6StringE"* @"_ZN11std$FS$core6String5emptyE" to i8*), i8* nonnull %4, i64 16), !AggType !5 + ret void +} + +define void @foo2(%"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE"* noalias nocapture writeonly sret(%"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE") %0, i8 addrspace(1)* nocapture readonly %this) gc "cangjie" { +; CHECK-LABEL: @foo2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS:%.*]], i64 24 +; CHECK-NEXT: [[TMP2:%.*]] = bitcast i8 addrspace(1)* [[TMP1]] to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: [[TMP3:%.*]] = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* [[TMP2]], align 8 +; CHECK-NEXT: [[TMP4:%.*]] = tail call i8 addrspace(1)* @CJ_MCC_DecodeStackTrace(i8 addrspace(1)* [[TMP3]], i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core17StackTraceElementE.objKlass" to i8*), i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core17StackTraceElementE.refArrayKlass" to i8*), i8* bitcast (%KlassInfo.0* @UInt8.arrayKlass to i8*)) +; CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[TMP4]], i64 8 +; CHECK-NEXT: [[TMP6:%.*]] = bitcast i8 addrspace(1)* [[TMP5]] to i64 addrspace(1)* +; CHECK-NEXT: [[TMP7:%.*]] = load i64, i64 addrspace(1)* [[TMP6]], align 4 +; CHECK-NEXT: [[ATOM_33641E_SROA_0_0__SROA_IDX2:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP0:%.*]], i64 0, i32 0 +; CHECK-NEXT: store i8 addrspace(1)* [[TMP4]], i8 addrspace(1)** [[ATOM_33641E_SROA_0_0__SROA_IDX2]], align 8 +; CHECK-NEXT: [[ATOM_33641E_SROA_4_0__SROA_IDX5:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP0]], i64 0, i32 1 +; CHECK-NEXT: store i64 0, i64* [[ATOM_33641E_SROA_4_0__SROA_IDX5]], align 4 +; CHECK-NEXT: [[ATOM_33641E_SROA_5_0__SROA_IDX8:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP0]], i64 0, i32 2 +; CHECK-NEXT: store i64 [[TMP7]], i64* [[ATOM_33641E_SROA_5_0__SROA_IDX8]], align 4 +; CHECK-NEXT: ret void +; +entry: + %1 = getelementptr inbounds i8, i8 addrspace(1)* %this, i64 24 + %2 = bitcast i8 addrspace(1)* %1 to i8 addrspace(1)* addrspace(1)* + %3 = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* %2 + %4 = tail call i8 addrspace(1)* @CJ_MCC_DecodeStackTrace(i8 addrspace(1)* %3, i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core17StackTraceElementE.objKlass" to i8*), i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core17StackTraceElementE.refArrayKlass" to i8*), i8* bitcast (%KlassInfo.0* @UInt8.arrayKlass to i8*)) + %5 = getelementptr inbounds i8, i8 addrspace(1)* %4, i64 8 + %6 = bitcast i8 addrspace(1)* %5 to i64 addrspace(1)* + %7 = load i64, i64 addrspace(1)* %6 + %atom.33641E.sroa.0.0..sroa_idx2 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE", %"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE"* %0, i64 0, i32 0 + store i8 addrspace(1)* %4, i8 addrspace(1)** %atom.33641E.sroa.0.0..sroa_idx2 + %atom.33641E.sroa.4.0..sroa_idx5 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE", %"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE"* %0, i64 0, i32 1 + store i64 0, i64* %atom.33641E.sroa.4.0..sroa_idx5 + %atom.33641E.sroa.5.0..sroa_idx8 = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE", %"record._ZN11std$FS$core11std$FS$core5ArrayIC_ZN11std$FS$core17StackTraceElementEE"* %0, i64 0, i32 2 + store i64 %7, i64* %atom.33641E.sroa.5.0..sroa_idx8 + ret void +} + +; CHECK-LABEL: @goo1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ENUMTMP50:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET49:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP40:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET39:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP34:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET33:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP29:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET28:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP23:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET22:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP19:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET18:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP13:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET12:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP5:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET4:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP3:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET2:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[TMP0:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[ATOM_5655E:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[ENUM_VAL:%.*]] = alloca %"record._ZN13std$FS$random11std$FS$core6OptionIdE", align 8 +; CHECK-NEXT: [[ENUMTMP50_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP50]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP50_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[TMP1:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET49]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP1]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[ENUMTMP40_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP40]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP40_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[TMP2:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET39]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP2]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[ENUMTMP34_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP34]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP34_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[CALLRET33_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET33]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[CALLRET33_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[ENUMTMP29_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP29]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP29_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[TMP3:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET28]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP3]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[ENUMTMP23_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP23]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP23_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[CALLRET22_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET22]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[CALLRET22_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[ENUMTMP19_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP19]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP19_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[CALLRET18_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET18]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[CALLRET18_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[ENUMTMP13_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP13]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP13_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[CALLRET12_0_SROA_CAST37:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET12]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[CALLRET12_0_SROA_CAST37]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[ENUMTMP5_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP5]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP5_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[TMP4:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET4]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP4]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[ENUMTMP3_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP3]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP3_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[CALLRET2_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET2]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[CALLRET2_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[ENUMTMP_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[CALLRET_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[CALLRET_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[DOT0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP0]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[DOT0_SROA_CAST]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[TMP5:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[ATOM_5655E]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP5]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS:%.*]], i64 56 +; CHECK-NEXT: [[TMP7:%.*]] = bitcast i8 addrspace(1)* [[TMP6]] to i64 addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.i64.i64(i64 [[SEED:%.*]], i8 addrspace(1)* [[THIS]], i64 addrspace(1)* [[TMP7]]) +; CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds %"record._ZN13std$FS$random11std$FS$core6OptionIdE", %"record._ZN13std$FS$random11std$FS$core6OptionIdE"* [[ENUM_VAL]], i64 0, i32 0 +; CHECK-NEXT: store i1 true, i1* [[TMP8]], align 1 +; CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds %"record._ZN13std$FS$random11std$FS$core6OptionIdE", %"record._ZN13std$FS$random11std$FS$core6OptionIdE"* [[ENUM_VAL]], i64 0, i32 1 +; CHECK-NEXT: store double 0.000000e+00, double* [[TMP9]], align 8 +; CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS]], i64 40 +; CHECK-NEXT: [[TMP11:%.*]] = bitcast %"record._ZN13std$FS$random11std$FS$core6OptionIdE"* [[ENUM_VAL]] to i8* +; CHECK-NEXT: call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* [[THIS]], i8 addrspace(1)* [[TMP10]], i8* nonnull [[TMP11]], i64 16) +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(24) [[TMP5]], i8* noundef nonnull align 8 dereferenceable(24) [[DOT0_SROA_CAST]], i64 24, i1 false) +; CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[ATOM_5655E]], i64 0, i32 1 +; CHECK-NEXT: store i64 0, i64* [[TMP12]], align 4 +; CHECK-NEXT: [[TMP13:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewArray64Stub(i64 312, i8* bitcast (%KlassInfo.0* @UInt64.arrayKlass to i8*)) +; CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[ATOM_5655E]], i64 0, i32 0 +; CHECK-NEXT: store i8 addrspace(1)* [[TMP13]], i8 addrspace(1)** [[TMP14]], align 8 +; CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[ATOM_5655E]], i64 0, i32 2 +; CHECK-NEXT: store i64 312, i64* [[TMP15]], align 4 +; CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS]], i64 8 +; CHECK-NEXT: call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* [[THIS]], i8 addrspace(1)* [[TMP16]], i8* nonnull [[TMP5]], i64 24) +; CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS]], i64 24 +; CHECK-NEXT: [[TMP18:%.*]] = bitcast i8 addrspace(1)* [[TMP17]] to i64 addrspace(1)* +; CHECK-NEXT: [[TMP19:%.*]] = load i64, i64 addrspace(1)* [[TMP18]], align 4 +; CHECK-NEXT: [[ICMPUGE:%.*]] = icmp eq i64 [[TMP19]], 0 +; CHECK-NEXT: br i1 [[ICMPUGE]], label [[IF_THEN7294E:%.*]], label [[IF_ELSE7294E:%.*]] +; CHECK: if.then7294E: +; CHECK-NEXT: [[TMP20:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 56) +; CHECK-NEXT: call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* [[TMP20]]) +; CHECK-NEXT: call void @CJ_MCC_ThrowException(i8 addrspace(1)* [[TMP20]]) +; CHECK-NEXT: unreachable +; CHECK: if.else7294E: +; CHECK-NEXT: [[TMP21:%.*]] = load i64, i64 addrspace(1)* [[TMP7]], align 4 +; CHECK-NEXT: [[TMP22:%.*]] = bitcast i8 addrspace(1)* [[TMP16]] to i8 addrspace(1)* addrspace(1)* +; CHECK-NEXT: [[TMP23:%.*]] = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* [[TMP22]], align 8 +; CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS]], i64 16 +; CHECK-NEXT: [[TMP25:%.*]] = bitcast i8 addrspace(1)* [[TMP24]] to i64 addrspace(1)* +; CHECK-NEXT: [[TMP26:%.*]] = load i64, i64 addrspace(1)* [[TMP25]], align 4 +; CHECK-NEXT: [[TMP27:%.*]] = bitcast i8 addrspace(1)* [[TMP23]] to [[ARRAYLAYOUT_UINT64:%.*]] addrspace(1)* +; CHECK-NEXT: [[ARR_IDX_SET_GEP:%.*]] = getelementptr inbounds [[ARRAYLAYOUT_UINT64]], [[ARRAYLAYOUT_UINT64]] addrspace(1)* [[TMP27]], i64 0, i32 1, i64 [[TMP26]] +; CHECK-NEXT: call void @llvm.cj.gcwrite.i64.i64(i64 [[TMP21]], i8 addrspace(1)* [[TMP23]], i64 addrspace(1)* [[ARR_IDX_SET_GEP]]) +; CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS]], i64 32 +; CHECK-NEXT: [[TMP29:%.*]] = bitcast i8 addrspace(1)* [[TMP28]] to i64 addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.i64.i64(i64 1, i8 addrspace(1)* [[THIS]], i64 addrspace(1)* [[TMP29]]) +; CHECK-NEXT: [[TMP30:%.*]] = load i64, i64 addrspace(1)* [[TMP29]], align 4 +; CHECK-NEXT: [[ICMPSLT35:%.*]] = icmp slt i64 [[TMP30]], 312 +; CHECK-NEXT: br i1 [[ICMPSLT35]], label [[WHILE_BODY4736E:%.*]], label [[BLOCK_END4688E:%.*]] +; CHECK: while.body4736E: +; CHECK-NEXT: [[TMP31:%.*]] = phi i64 [ [[TMP49:%.*]], [[NORMAL0E48:%.*]] ], [ [[TMP30]], [[IF_ELSE7294E]] ] +; CHECK-NEXT: [[TMP32:%.*]] = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 [[TMP31]], i64 -1) +; CHECK-NEXT: [[DOTFCA_0_EXTRACT:%.*]] = extractvalue { i64, i1 } [[TMP32]], 0 +; CHECK-NEXT: [[DOTFCA_1_EXTRACT:%.*]] = extractvalue { i64, i1 } [[TMP32]], 1 +; CHECK-NEXT: br i1 [[DOTFCA_1_EXTRACT]], label [[OVERFLOW0E:%.*]], label [[NORMAL0E:%.*]], !prof [[PROF10:![0-9]+]] +; CHECK: normal0E: +; CHECK-NEXT: [[TMP33:%.*]] = load i64, i64 addrspace(1)* [[TMP18]], align 4 +; CHECK-NEXT: [[ICMPUGE7_NOT:%.*]] = icmp ult i64 [[DOTFCA_0_EXTRACT]], [[TMP33]] +; CHECK-NEXT: br i1 [[ICMPUGE7_NOT]], label [[IF_ELSE7328E:%.*]], label [[IF_THEN7328E:%.*]] +; CHECK: overflow0E: +; CHECK-NEXT: call void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") [[CALLRET4]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.16", i64 0, i64 0), i64 3) +; CHECK-NEXT: [[TMP34:%.*]] = addrspacecast %"record._ZN11std$FS$core6StringE"* [[CALLRET4]] to %"record._ZN11std$FS$core6StringE" addrspace(1)* +; CHECK-NEXT: [[TMP35:%.*]] = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* [[TMP34]]) +; CHECK-NEXT: call void @CJ_MCC_ThrowException(i8 addrspace(1)* [[TMP35]]) +; CHECK-NEXT: unreachable +; CHECK: if.then7328E: +; CHECK-NEXT: [[TMP36:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 56) +; CHECK-NEXT: call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* [[TMP36]]) +; CHECK-NEXT: call void @CJ_MCC_ThrowException(i8 addrspace(1)* [[TMP36]]) +; CHECK-NEXT: unreachable +; CHECK: if.else7328E: +; CHECK-NEXT: [[TMP37:%.*]] = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* [[TMP22]], align 8 +; CHECK-NEXT: [[TMP38:%.*]] = load i64, i64 addrspace(1)* [[TMP25]], align 4 +; CHECK-NEXT: [[TMP39:%.*]] = bitcast i8 addrspace(1)* [[TMP37]] to [[ARRAYLAYOUT_UINT64]] addrspace(1)* +; CHECK-NEXT: [[I2UI_LT:%.*]] = icmp slt i64 [[TMP31]], 0 +; CHECK-NEXT: br i1 [[I2UI_LT]], label [[IF_OVF_LT0E27:%.*]], label [[ELSE_OVF_GT0E31:%.*]] +; CHECK: if.ovf.lt0E27: +; CHECK-NEXT: call void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") [[CALLRET28]], i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"$const_string.22", i64 0, i64 0), i64 8) +; CHECK-NEXT: [[TMP40:%.*]] = addrspacecast %"record._ZN11std$FS$core6StringE"* [[CALLRET28]] to %"record._ZN11std$FS$core6StringE" addrspace(1)* +; CHECK-NEXT: [[TMP41:%.*]] = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* [[TMP40]]) +; CHECK-NEXT: call void @CJ_MCC_ThrowException(i8 addrspace(1)* [[TMP41]]) +; CHECK-NEXT: unreachable +; CHECK: else.ovf.gt0E31: +; CHECK-NEXT: [[ADD:%.*]] = add i64 [[TMP38]], [[DOTFCA_0_EXTRACT]] +; CHECK-NEXT: [[ARR_IDX_GET_GEP:%.*]] = getelementptr inbounds [[ARRAYLAYOUT_UINT64]], [[ARRAYLAYOUT_UINT64]] addrspace(1)* [[TMP39]], i64 0, i32 1, i64 [[ADD]] +; CHECK-NEXT: [[TMP42:%.*]] = load i64, i64 addrspace(1)* [[ARR_IDX_GET_GEP]], align 4 +; CHECK-NEXT: [[LSHR:%.*]] = lshr i64 [[TMP42]], 62 +; CHECK-NEXT: [[XOR:%.*]] = xor i64 [[LSHR]], [[TMP42]] +; CHECK-NEXT: [[MUL:%.*]] = mul i64 [[XOR]], 6364136223846793005 +; CHECK-NEXT: [[TMP43:%.*]] = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 [[MUL]], i64 [[TMP31]]) +; CHECK-NEXT: [[DOTFCA_0_EXTRACT11:%.*]] = extractvalue { i64, i1 } [[TMP43]], 0 +; CHECK-NEXT: [[DOTFCA_1_EXTRACT12:%.*]] = extractvalue { i64, i1 } [[TMP43]], 1 +; CHECK-NEXT: br i1 [[DOTFCA_1_EXTRACT12]], label [[OVERFLOW0E37:%.*]], label [[NORMAL0E38:%.*]], !prof [[PROF10]] +; CHECK: normal0E38: +; CHECK-NEXT: [[ICMPUGE42_NOT:%.*]] = icmp ult i64 [[TMP31]], [[TMP33]] +; CHECK-NEXT: br i1 [[ICMPUGE42_NOT]], label [[IF_ELSE7402E:%.*]], label [[IF_THEN7402E:%.*]] +; CHECK: overflow0E37: +; CHECK-NEXT: call void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") [[CALLRET39]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.15", i64 0, i64 0), i64 3) +; CHECK-NEXT: [[TMP44:%.*]] = addrspacecast %"record._ZN11std$FS$core6StringE"* [[CALLRET39]] to %"record._ZN11std$FS$core6StringE" addrspace(1)* +; CHECK-NEXT: [[TMP45:%.*]] = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* [[TMP44]]) +; CHECK-NEXT: call void @CJ_MCC_ThrowException(i8 addrspace(1)* [[TMP45]]) +; CHECK-NEXT: unreachable +; CHECK: if.then7402E: +; CHECK-NEXT: [[TMP46:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 56) +; CHECK-NEXT: call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* [[TMP46]]) +; CHECK-NEXT: call void @CJ_MCC_ThrowException(i8 addrspace(1)* [[TMP46]]) +; CHECK-NEXT: unreachable +; CHECK: if.else7402E: +; CHECK-NEXT: [[ADD43:%.*]] = add i64 [[TMP38]], [[TMP31]] +; CHECK-NEXT: [[ARR_IDX_SET_GEP44:%.*]] = getelementptr inbounds [[ARRAYLAYOUT_UINT64]], [[ARRAYLAYOUT_UINT64]] addrspace(1)* [[TMP39]], i64 0, i32 1, i64 [[ADD43]] +; CHECK-NEXT: call void @llvm.cj.gcwrite.i64.i64(i64 [[DOTFCA_0_EXTRACT11]], i8 addrspace(1)* [[TMP37]], i64 addrspace(1)* [[ARR_IDX_SET_GEP44]]) +; CHECK-NEXT: [[TMP47:%.*]] = load i64, i64 addrspace(1)* [[TMP29]], align 4 +; CHECK-NEXT: [[TMP48:%.*]] = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 [[TMP47]], i64 1) +; CHECK-NEXT: [[DOTFCA_1_EXTRACT16:%.*]] = extractvalue { i64, i1 } [[TMP48]], 1 +; CHECK-NEXT: br i1 [[DOTFCA_1_EXTRACT16]], label [[OVERFLOW0E47:%.*]], label [[NORMAL0E48]], !prof [[PROF10]] +; CHECK: normal0E48: +; CHECK-NEXT: [[DOTFCA_0_EXTRACT15:%.*]] = extractvalue { i64, i1 } [[TMP48]], 0 +; CHECK-NEXT: call void @llvm.cj.gcwrite.i64.i64(i64 [[DOTFCA_0_EXTRACT15]], i8 addrspace(1)* [[THIS]], i64 addrspace(1)* [[TMP29]]) +; CHECK-NEXT: [[TMP49]] = load i64, i64 addrspace(1)* [[TMP29]], align 4 +; CHECK-NEXT: [[ICMPSLT:%.*]] = icmp slt i64 [[TMP49]], 312 +; CHECK-NEXT: br i1 [[ICMPSLT]], label [[WHILE_BODY4736E]], label [[BLOCK_END4688E]] +; CHECK: overflow0E47: +; CHECK-NEXT: call void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") [[CALLRET49]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.15", i64 0, i64 0), i64 3) +; CHECK-NEXT: [[TMP50:%.*]] = addrspacecast %"record._ZN11std$FS$core6StringE"* [[CALLRET49]] to %"record._ZN11std$FS$core6StringE" addrspace(1)* +; CHECK-NEXT: [[TMP51:%.*]] = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* [[TMP50]]) +; CHECK-NEXT: call void @CJ_MCC_ThrowException(i8 addrspace(1)* [[TMP51]]) +; CHECK-NEXT: unreachable +; CHECK: block.end4688E: +; CHECK-NEXT: ret void +; + +; CHECK-LABEL: @goo2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ENUMTMP:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[CALLRET_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[CALLRET_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[ARRAY:%.*]], i64 0, i32 2 +; CHECK-NEXT: [[TMP2:%.*]] = load i64, i64 addrspace(1)* [[TMP1]], align 4 +; CHECK-NEXT: [[ICMPSLT5:%.*]] = icmp sgt i64 [[TMP2]], 0 +; CHECK-NEXT: br i1 [[ICMPSLT5]], label [[WHILE_BODY2474E_LR_PH:%.*]], label [[BLOCK_END2448E:%.*]] +; CHECK: while.body2474E.lr.ph: +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[ARRAY]], i64 0, i32 0 +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[ARRAY]], i64 0, i32 1 +; CHECK-NEXT: br label [[WHILE_BODY2474E:%.*]] +; CHECK: while.body2474E: +; CHECK-NEXT: [[ATOM_5600E_06:%.*]] = phi i64 [ 0, [[WHILE_BODY2474E_LR_PH]] ], [ [[TMP16:%.*]], [[WHILE_BODY2474E]] ] +; CHECK-NEXT: [[TMP5:%.*]] = call i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* [[THIS:%.*]]) +; CHECK-NEXT: [[TMP6:%.*]] = getelementptr i8, i8* [[TMP5]], i64 24 +; CHECK-NEXT: [[TMP7:%.*]] = bitcast i8* [[TMP6]] to i8*** +; CHECK-NEXT: [[TMP8:%.*]] = load i8**, i8*** [[TMP7]], align 8 +; CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8*, i8** [[TMP8]], i64 5 +; CHECK-NEXT: [[TMP10:%.*]] = bitcast i8** [[TMP9]] to i8 (i8 addrspace(1)*)** +; CHECK-NEXT: [[TMP11:%.*]] = load i8 (i8 addrspace(1)*)*, i8 (i8 addrspace(1)*)** [[TMP10]], align 8 +; CHECK-NEXT: [[TMP12:%.*]] = call i8 [[TMP11]](i8 addrspace(1)* [[THIS]]) +; CHECK-NEXT: [[TMP13:%.*]] = load i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)* [[TMP3]], align 8 +; CHECK-NEXT: [[TMP14:%.*]] = load i64, i64 addrspace(1)* [[TMP4]], align 4 +; CHECK-NEXT: [[ADD:%.*]] = add i64 [[TMP14]], [[ATOM_5600E_06]] +; CHECK-NEXT: [[TMP15:%.*]] = bitcast i8 addrspace(1)* [[TMP13]] to [[ARRAYLAYOUT_UINT8:%.*]] addrspace(1)* +; CHECK-NEXT: [[ARR_IDX_SET_GEP:%.*]] = getelementptr inbounds [[ARRAYLAYOUT_UINT8]], [[ARRAYLAYOUT_UINT8]] addrspace(1)* [[TMP15]], i64 0, i32 1, i64 [[ADD]] +; CHECK-NEXT: call void @llvm.cj.gcwrite.i8.i8(i8 [[TMP12]], i8 addrspace(1)* [[TMP13]], i8 addrspace(1)* [[ARR_IDX_SET_GEP]]) +; CHECK-NEXT: [[TMP16]] = add nuw nsw i64 [[ATOM_5600E_06]], 1 +; CHECK-NEXT: [[TMP17:%.*]] = load i64, i64 addrspace(1)* [[TMP1]], align 4 +; CHECK-NEXT: [[ICMPSLT:%.*]] = icmp slt i64 [[TMP16]], [[TMP17]] +; CHECK-NEXT: br i1 [[ICMPSLT]], label [[WHILE_BODY2474E]], label [[BLOCK_END2448E]] +; CHECK: block.end2448E: +; CHECK-NEXT: [[TMP18:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP0:%.*]] to i8* +; CHECK-NEXT: [[TMP19:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[ARRAY]] to i8 addrspace(1)* +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p1i8.i64(i8* noundef nonnull align 8 dereferenceable(24) [[TMP18]], i8 addrspace(1)* noundef align 8 dereferenceable(24) [[TMP19]], i64 24, i1 false) +; CHECK-NEXT: ret void +; + +; CHECK-LABEL: @hoo1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CALLRET_I:%.*]] = alloca [[UNIT_TYPE:%.*]], align 8 +; CHECK-NEXT: [[CALLRET6:%.*]] = alloca [[UNIT_TYPE]], align 8 +; CHECK-NEXT: [[ENUMTMP_SROA_0:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[STRDATA:%.*]] = alloca %"record._ZN11std$FS$core8Utf8ViewE", align 8 +; CHECK-NEXT: [[CALLRET1:%.*]] = alloca %"record._ZN11std$FS$core8Utf8ViewE", align 8 +; CHECK-NEXT: [[ENUMTMP_SROA_0_0_SROA_CAST8:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[ENUMTMP_SROA_0]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP_SROA_0_0_SROA_CAST8]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[TMP0:%.*]] = bitcast %"record._ZN11std$FS$core8Utf8ViewE"* [[STRDATA]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP0]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[TMP1:%.*]] = bitcast %"record._ZN11std$FS$core8Utf8ViewE"* [[CALLRET1]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP1]], i8 0, i64 24, i1 false) +; CHECK-NEXT: call void @"_ZN17std$FS$collection13StringBuilder16checkRangeInsertEl"(%Unit.Type* noalias sret([[UNIT_TYPE]]) poison, i8 addrspace(1)* [[THIS:%.*]], i64 [[INDEX:%.*]]) +; CHECK-NEXT: call void @"_ZN11std$FS$core6String12$utf8ViewgetEv"(%"record._ZN11std$FS$core8Utf8ViewE"* noalias nonnull sret(%"record._ZN11std$FS$core8Utf8ViewE") [[CALLRET1]], i8 addrspace(1)* %"str$BP", %"record._ZN11std$FS$core6StringE" addrspace(1)* [[STR:%.*]]) +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(24) [[TMP0]], i8* noundef nonnull align 8 dereferenceable(24) [[TMP1]], i64 24, i1 false) +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core8Utf8ViewE", %"record._ZN11std$FS$core8Utf8ViewE"* [[STRDATA]], i64 0, i32 0, i32 2 +; CHECK-NEXT: [[TMP3:%.*]] = load i64, i64* [[TMP2]], align 4 +; CHECK-NEXT: call fastcc void @"_ZN17std$FS$collection13StringBuilder7reserveEl"(i8 addrspace(1)* [[THIS]], i64 [[TMP3]]) +; CHECK-NEXT: [[ICMPEQ:%.*]] = icmp eq i64 [[INDEX]], 0 +; CHECK-NEXT: br i1 [[ICMPEQ]], label [[IF_END16502E:%.*]], label [[IF_ELSE16502E:%.*]] +; CHECK: if.else16502E: +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS]], i64 32 +; CHECK-NEXT: [[TMP5:%.*]] = bitcast i8 addrspace(1)* [[TMP4]] to i64 addrspace(1)* +; CHECK-NEXT: [[TMP6:%.*]] = load i64, i64 addrspace(1)* [[TMP5]], align 4 +; CHECK-NEXT: [[ICMPEQ3:%.*]] = icmp eq i64 [[TMP6]], [[INDEX]] +; CHECK-NEXT: br i1 [[ICMPEQ3]], label [[IF_THEN16501E:%.*]], label [[IF_ELSE16501E:%.*]] +; CHECK: if.then16501E: +; CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS]], i64 40 +; CHECK-NEXT: [[TMP8:%.*]] = bitcast i8 addrspace(1)* [[TMP7]] to i64 addrspace(1)* +; CHECK-NEXT: [[TMP9:%.*]] = load i64, i64 addrspace(1)* [[TMP8]], align 4 +; CHECK-NEXT: br label [[IF_END16502E]] +; CHECK: if.else16501E: +; CHECK-NEXT: [[SMAX_I:%.*]] = call i64 @llvm.smax.i64(i64 [[INDEX]], i64 0) +; CHECK-NEXT: [[EXITCOND7_NOT_I:%.*]] = icmp slt i64 [[INDEX]], 1 +; CHECK-NEXT: br i1 [[EXITCOND7_NOT_I]], label [[IF_END16502E]], label [[WHILE_BODY325E_LR_PH_I:%.*]] +; CHECK: while.body325E.lr.ph.i: +; CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS]], i64 8 +; CHECK-NEXT: [[TMP11:%.*]] = bitcast i8 addrspace(1)* [[TMP10]] to %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* +; CHECK-NEXT: br label [[WHILE_BODY325E_I:%.*]] +; CHECK: while.body325E.i: +; CHECK-NEXT: [[ATOM_17274E_010_I:%.*]] = phi i64 [ 0, [[WHILE_BODY325E_LR_PH_I]] ], [ [[ADD_I:%.*]], [[WHILE_BODY325E_I]] ] +; CHECK-NEXT: [[ATOM_17272E_09_I:%.*]] = phi i64 [ 0, [[WHILE_BODY325E_LR_PH_I]] ], [ [[ADD1_I:%.*]], [[WHILE_BODY325E_I]] ] +; CHECK-NEXT: [[ATOM_17268E_08_I:%.*]] = phi i64 [ 0, [[WHILE_BODY325E_LR_PH_I]] ], [ [[SPEC_SELECT_I:%.*]], [[WHILE_BODY325E_I]] ] +; CHECK-NEXT: [[TMP12:%.*]] = call i64 @"_ZN11std$FS$core6Extend4Char8utf8SizeER_ZN11std$FS$core5ArrayIhEl"(i8 addrspace(1)* [[THIS]], %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP11]], i64 [[ATOM_17272E_09_I]]), !noalias !11 +; CHECK-NEXT: [[ADD_I]] = add nuw nsw i64 [[ATOM_17274E_010_I]], 1 +; CHECK-NEXT: [[ADD1_I]] = add i64 [[TMP12]], [[ATOM_17272E_09_I]] +; CHECK-NEXT: [[ICMPEQ2_I:%.*]] = icmp eq i64 [[ADD_I]], [[INDEX]] +; CHECK-NEXT: [[SPEC_SELECT_I]] = select i1 [[ICMPEQ2_I]], i64 [[ADD1_I]], i64 [[ATOM_17268E_08_I]] +; CHECK-NEXT: [[EXITCOND_NOT_I:%.*]] = icmp eq i64 [[ADD_I]], [[SMAX_I]] +; CHECK-NEXT: br i1 [[EXITCOND_NOT_I]], label [[IF_END16502E]], label [[WHILE_BODY325E_I]] +; CHECK: if.end16502E: +; CHECK-NEXT: [[ATOM_17381E_0:%.*]] = phi i64 [ [[TMP9]], [[IF_THEN16501E]] ], [ 0, [[ENTRY:%.*]] ], [ 0, [[IF_ELSE16501E]] ], [ [[SPEC_SELECT_I]], [[WHILE_BODY325E_I]] ] +; CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS]], i64 40 +; CHECK-NEXT: [[TMP14:%.*]] = bitcast i8 addrspace(1)* [[TMP13]] to i64 addrspace(1)* +; CHECK-NEXT: [[TMP15:%.*]] = load i64, i64 addrspace(1)* [[TMP14]], align 4 +; CHECK-NEXT: [[ICMPNE_NOT:%.*]] = icmp eq i64 [[TMP15]], [[ATOM_17381E_0]] +; CHECK-NEXT: br i1 [[ICMPNE_NOT]], label [[IF_END16525E:%.*]], label [[IF_THEN16525E:%.*]] +; CHECK: if.then16525E: +; CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS]], i64 8 +; CHECK-NEXT: [[TMP17:%.*]] = bitcast i8 addrspace(1)* [[TMP16]] to %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* +; CHECK-NEXT: [[ADD:%.*]] = add i64 [[ATOM_17381E_0]], [[TMP3]] +; CHECK-NEXT: [[SUB:%.*]] = sub i64 [[TMP15]], [[ATOM_17381E_0]] +; CHECK-NEXT: call fastcc void @"_ZN17std$FS$collection11std$FS$core5ArrayIh6copyToER_ZN11std$FS$core5ArrayIhElll"(%Unit.Type* noalias nonnull sret([[UNIT_TYPE]]) [[CALLRET6]], i8 addrspace(1)* [[THIS]], %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP17]], i8 addrspace(1)* [[THIS]], %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP17]], i64 [[ATOM_17381E_0]], i64 [[ADD]], i64 [[SUB]]) +; CHECK-NEXT: br label [[IF_END16525E]] +; CHECK: if.end16525E: +; CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS]], i64 8 +; CHECK-NEXT: [[TMP19:%.*]] = bitcast i8 addrspace(1)* [[TMP18]] to %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* +; CHECK-NEXT: [[TMP20:%.*]] = addrspacecast %"record._ZN11std$FS$core8Utf8ViewE"* [[STRDATA]] to %"record._ZN11std$FS$core8Utf8ViewE" addrspace(1)* +; CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds [[UNIT_TYPE]], %Unit.Type* [[CALLRET_I]], i64 0, i32 0 +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull [[TMP21]]) +; CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core8Utf8ViewE", %"record._ZN11std$FS$core8Utf8ViewE" addrspace(1)* [[TMP20]], i64 0, i32 0 +; CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core8Utf8ViewE", %"record._ZN11std$FS$core8Utf8ViewE" addrspace(1)* [[TMP20]], i64 0, i32 0, i32 2 +; CHECK-NEXT: [[TMP24:%.*]] = addrspacecast i64 addrspace(1)* [[TMP23]] to i64* +; CHECK-NEXT: [[TMP25:%.*]] = load i64, i64* [[TMP24]], align 4 +; CHECK-NEXT: call fastcc void @"_ZN17std$FS$collection11std$FS$core5ArrayIh6copyToER_ZN11std$FS$core5ArrayIhElll"(%Unit.Type* noalias nonnull sret([[UNIT_TYPE]]) [[CALLRET_I]], i8 addrspace(1)* null, %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP22]], i8 addrspace(1)* [[THIS]], %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP19]], i64 0, i64 [[ATOM_17381E_0]], i64 [[TMP25]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull [[TMP21]]) +; CHECK-NEXT: [[TMP26:%.*]] = load i64, i64 addrspace(1)* [[TMP14]], align 4 +; CHECK-NEXT: [[ADD7:%.*]] = add i64 [[TMP26]], [[TMP3]] +; CHECK-NEXT: call void @llvm.cj.gcwrite.i64.i64(i64 [[ADD7]], i8 addrspace(1)* [[THIS]], i64 addrspace(1)* [[TMP14]]) +; CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS]], i64 32 +; CHECK-NEXT: [[TMP28:%.*]] = bitcast i8 addrspace(1)* [[TMP27]] to i64 addrspace(1)* +; CHECK-NEXT: [[TMP29:%.*]] = load i64, i64 addrspace(1)* [[TMP28]], align 4 +; CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core6StringE", %"record._ZN11std$FS$core6StringE" addrspace(1)* [[STR]], i64 0, i32 1 +; CHECK-NEXT: [[TMP31:%.*]] = load i64, i64 addrspace(1)* [[TMP30]], align 4 +; CHECK-NEXT: [[ADD8:%.*]] = add i64 [[TMP31]], [[TMP29]] +; CHECK-NEXT: call void @llvm.cj.gcwrite.i64.i64(i64 [[ADD8]], i8 addrspace(1)* [[THIS]], i64 addrspace(1)* [[TMP28]]) +; CHECK-NEXT: ret i8 addrspace(1)* [[THIS]] +; + +; CHECK-LABEL: @hoo2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ENUMTMP7:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET6:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET1:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET:%.*]] = alloca [[T2_CLE:%.*]], align 8 +; CHECK-NEXT: [[ENUMTMP7_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP7]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP7_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[TMP1:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET6]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP1]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[ENUMTMP_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[TMP2:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET1]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP2]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS:%.*]], i64 32 +; CHECK-NEXT: [[TMP4:%.*]] = bitcast i8 addrspace(1)* [[TMP3]] to i64 addrspace(1)* +; CHECK-NEXT: [[TMP5:%.*]] = load i64, i64 addrspace(1)* [[TMP4]], align 4 +; CHECK-NEXT: [[ARR_ALLOC_SIZE_VALID:%.*]] = icmp sgt i64 [[TMP5]], -1 +; CHECK-NEXT: br i1 [[ARR_ALLOC_SIZE_VALID]], label [[ARR_ALLOC_BODY0E:%.*]], label [[ARR_ALLOC_THROW0E:%.*]] +; CHECK: arr.alloc.throw0E: +; CHECK-NEXT: [[TMP6:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core26NegativeArraySizeExceptionE.objKlass" to i8*), i32 56) +; CHECK-NEXT: call void @"_ZN11std$FS$core26NegativeArraySizeException4initEv"(i8 addrspace(1)* [[TMP6]]) +; CHECK-NEXT: call void @CJ_MCC_ThrowException(i8 addrspace(1)* [[TMP6]]) +; CHECK-NEXT: unreachable +; CHECK: arr.alloc.body0E: +; CHECK-NEXT: [[TMP7:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewArray32Stub(i64 [[TMP5]], i8* bitcast (%KlassInfo.0* @Char.arrayKlass to i8*)) +; CHECK-NEXT: [[SIZE_IS_ZERO:%.*]] = icmp eq i64 [[TMP5]], 0 +; CHECK-NEXT: br i1 [[SIZE_IS_ZERO]], label [[ARR_INIT_OPT0E:%.*]], label [[ARR_INIT_BODY0E_PREHEADER:%.*]] +; CHECK: arr.init.body0E.preheader: +; CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[TMP7]], i64 16 +; CHECK-NEXT: [[TMP9:%.*]] = bitcast i8 addrspace(1)* [[TMP8]] to i32 addrspace(1)* +; CHECK-NEXT: br label [[ARR_INIT_BODY0E:%.*]] +; CHECK: arr.init.body0E: +; CHECK-NEXT: [[ITERATOR_0:%.*]] = phi i64 [ [[TMP11:%.*]], [[ARR_INIT_BODY0E]] ], [ 0, [[ARR_INIT_BODY0E_PREHEADER]] ] +; CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds i32, i32 addrspace(1)* [[TMP9]], i64 [[ITERATOR_0]] +; CHECK-NEXT: call void @llvm.cj.gcwrite.i32.i32(i32 32, i8 addrspace(1)* [[TMP7]], i32 addrspace(1)* [[TMP10]]) +; CHECK-NEXT: [[TMP11]] = add nuw nsw i64 [[ITERATOR_0]], 1 +; CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[TMP11]], [[TMP5]] +; CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[ARR_INIT_OPT0E]], label [[ARR_INIT_BODY0E]] +; CHECK: arr.init.opt0E: +; CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS]], i64 40 +; CHECK-NEXT: [[TMP13:%.*]] = bitcast i8 addrspace(1)* [[TMP12]] to i64 addrspace(1)* +; CHECK-NEXT: [[TMP14:%.*]] = load i64, i64 addrspace(1)* [[TMP13]], align 4 +; CHECK-NEXT: [[ICMPSLT23:%.*]] = icmp sgt i64 [[TMP14]], 0 +; CHECK-NEXT: br i1 [[ICMPSLT23]], label [[WHILE_BODY9072E_LR_PH:%.*]], label [[BLOCK_END9027E:%.*]] +; CHECK: while.body9072E.lr.ph: +; CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[THIS]], i64 8 +; CHECK-NEXT: [[TMP16:%.*]] = bitcast i8 addrspace(1)* [[TMP15]] to %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* +; CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds [[T2_CLE]], %T2_clE* [[CALLRET]], i64 0, i32 1 +; CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds [[T2_CLE]], %T2_clE* [[CALLRET]], i64 0, i32 0 +; CHECK-NEXT: [[TMP19:%.*]] = bitcast i8 addrspace(1)* [[TMP7]] to [[ARRAYLAYOUT_CHAR:%.*]] addrspace(1)* +; CHECK-NEXT: br label [[WHILE_BODY9072E:%.*]] +; CHECK: while.body9072E: +; CHECK-NEXT: [[ATOM_17365E_025:%.*]] = phi i64 [ 0, [[WHILE_BODY9072E_LR_PH]] ], [ [[DOTFCA_0_EXTRACT18:%.*]], [[NORMAL0E5:%.*]] ] +; CHECK-NEXT: [[ATOM_17367E_024:%.*]] = phi i64 [ 0, [[WHILE_BODY9072E_LR_PH]] ], [ [[DOTFCA_0_EXTRACT:%.*]], [[NORMAL0E5]] ] +; CHECK-NEXT: call void @"_ZN11std$FS$core6Extend4Char8fromUtf8ER_ZN11std$FS$core5ArrayIhEl"(%T2_clE* noalias nonnull sret([[T2_CLE]]) [[CALLRET]], i8 addrspace(1)* [[THIS]], %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP16]], i64 [[ATOM_17367E_024]]) +; CHECK-NEXT: [[ICMPUGE_NOT:%.*]] = icmp ult i64 [[ATOM_17365E_025]], [[TMP5]] +; CHECK-NEXT: br i1 [[ICMPUGE_NOT]], label [[IF_ELSE19175E:%.*]], label [[IF_THEN19175E:%.*]] +; CHECK: if.then19175E: +; CHECK-NEXT: [[TMP20:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 56) +; CHECK-NEXT: call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* [[TMP20]]) +; CHECK-NEXT: call void @CJ_MCC_ThrowException(i8 addrspace(1)* [[TMP20]]) +; CHECK-NEXT: unreachable +; CHECK: if.else19175E: +; CHECK-NEXT: [[TMP21:%.*]] = load i64, i64* [[TMP17]], align 4 +; CHECK-NEXT: [[TMP22:%.*]] = load i32, i32* [[TMP18]], align 4 +; CHECK-NEXT: [[ARR_IDX_SET_GEP:%.*]] = getelementptr inbounds [[ARRAYLAYOUT_CHAR]], [[ARRAYLAYOUT_CHAR]] addrspace(1)* [[TMP19]], i64 0, i32 1, i64 [[ATOM_17365E_025]] +; CHECK-NEXT: call void @llvm.cj.gcwrite.i32.i32(i32 [[TMP22]], i8 addrspace(1)* [[TMP7]], i32 addrspace(1)* [[ARR_IDX_SET_GEP]]) +; CHECK-NEXT: [[TMP23:%.*]] = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 [[ATOM_17367E_024]], i64 [[TMP21]]) +; CHECK-NEXT: [[DOTFCA_0_EXTRACT]] = extractvalue { i64, i1 } [[TMP23]], 0 +; CHECK-NEXT: [[DOTFCA_1_EXTRACT:%.*]] = extractvalue { i64, i1 } [[TMP23]], 1 +; CHECK-NEXT: br i1 [[DOTFCA_1_EXTRACT]], label [[OVERFLOW0E:%.*]], label [[NORMAL0E:%.*]], !prof [[PROF10]] +; CHECK: normal0E: +; CHECK-NEXT: [[TMP24:%.*]] = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 [[ATOM_17365E_025]], i64 1) +; CHECK-NEXT: [[DOTFCA_1_EXTRACT19:%.*]] = extractvalue { i64, i1 } [[TMP24]], 1 +; CHECK-NEXT: br i1 [[DOTFCA_1_EXTRACT19]], label [[OVERFLOW0E4:%.*]], label [[NORMAL0E5]], !prof [[PROF10]] +; CHECK: overflow0E: +; CHECK-NEXT: call void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") [[CALLRET1]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.29", i64 0, i64 0), i64 3) +; CHECK-NEXT: [[TMP25:%.*]] = addrspacecast %"record._ZN11std$FS$core6StringE"* [[CALLRET1]] to %"record._ZN11std$FS$core6StringE" addrspace(1)* +; CHECK-NEXT: [[TMP26:%.*]] = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* [[TMP25]]) +; CHECK-NEXT: call void @CJ_MCC_ThrowException(i8 addrspace(1)* [[TMP26]]) +; CHECK-NEXT: unreachable +; CHECK: normal0E5: +; CHECK-NEXT: [[DOTFCA_0_EXTRACT18]] = extractvalue { i64, i1 } [[TMP24]], 0 +; CHECK-NEXT: [[TMP27:%.*]] = load i64, i64 addrspace(1)* [[TMP13]], align 4 +; CHECK-NEXT: [[ICMPSLT:%.*]] = icmp slt i64 [[DOTFCA_0_EXTRACT]], [[TMP27]] +; CHECK-NEXT: br i1 [[ICMPSLT]], label [[WHILE_BODY9072E]], label [[BLOCK_END9027E]] +; CHECK: overflow0E4: +; CHECK-NEXT: call void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") [[CALLRET6]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.29", i64 0, i64 0), i64 3) +; CHECK-NEXT: [[TMP28:%.*]] = addrspacecast %"record._ZN11std$FS$core6StringE"* [[CALLRET6]] to %"record._ZN11std$FS$core6StringE" addrspace(1)* +; CHECK-NEXT: [[TMP29:%.*]] = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* [[TMP28]]) +; CHECK-NEXT: call void @CJ_MCC_ThrowException(i8 addrspace(1)* [[TMP29]]) +; CHECK-NEXT: unreachable +; CHECK: block.end9027E: +; CHECK-NEXT: [[CHARARRAY_SROA_0_0__SROA_IDX:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP0:%.*]], i64 0, i32 0 +; CHECK-NEXT: store i8 addrspace(1)* [[TMP7]], i8 addrspace(1)** [[CHARARRAY_SROA_0_0__SROA_IDX]], align 8 +; CHECK-NEXT: [[CHARARRAY_SROA_4_0__SROA_IDX10:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP0]], i64 0, i32 1 +; CHECK-NEXT: store i64 0, i64* [[CHARARRAY_SROA_4_0__SROA_IDX10]], align 4 +; CHECK-NEXT: [[CHARARRAY_SROA_5_0__SROA_IDX11:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[TMP0]], i64 0, i32 2 +; CHECK-NEXT: store i64 [[TMP5]], i64* [[CHARARRAY_SROA_5_0__SROA_IDX11]], align 4 +; CHECK-NEXT: ret void +; + +; CHECK-LABEL: @hoo3( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ENUMTMP38:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP37:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[CALLRET36:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP32:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET31:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP26:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET25:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP17:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET16:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[VALUE:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET9:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET7:%.*]] = alloca %"record._ZN17std$FS$collection11std$FS$core6OptionIcE", align 8 +; CHECK-NEXT: [[STRINGARRAY:%.*]] = alloca %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", align 8 +; CHECK-NEXT: [[ENUMTMP6:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET5:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[CALLRET:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[BLOCK16070VAL:%.*]] = alloca %"record._ZN11std$FS$core6StringE", align 8 +; CHECK-NEXT: [[ENUMTMP38_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP38]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP38_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[ENUMTMP37_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[ENUMTMP37]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP37_0_SROA_CAST]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[TMP1:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET36]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP1]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[ENUMTMP32_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP32]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP32_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[CALLRET31_0_SROA_CAST46:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET31]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[CALLRET31_0_SROA_CAST46]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[ENUMTMP26_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP26]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP26_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[CALLRET25_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET25]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[CALLRET25_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[ENUMTMP17_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP17]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP17_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[TMP2:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET16]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP2]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[TMP3:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[VALUE]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP3]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[TMP4:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET9]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP4]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[TMP5:%.*]] = bitcast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[STRINGARRAY]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP5]], i8 0, i64 24, i1 false) +; CHECK-NEXT: [[ENUMTMP6_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP6]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP6_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[CALLRET5_0_SROA_CAST45:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET5]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[CALLRET5_0_SROA_CAST45]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[ENUMTMP_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[ENUMTMP]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[ENUMTMP_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[TMP6:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[CALLRET]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull align 8 [[TMP6]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[BLOCK16070VAL_0_SROA_CAST:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[BLOCK16070VAL]] to i8* +; CHECK-NEXT: call void @llvm.cj.memset.p0i8(i8* nonnull [[BLOCK16070VAL_0_SROA_CAST]], i8 0, i64 16, i1 false) +; CHECK-NEXT: [[TMP7:%.*]] = call i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* [[IT:%.*]]) +; CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, i8* [[TMP7]], i64 32 +; CHECK-NEXT: [[TMP9:%.*]] = bitcast i8* [[TMP8]] to i64** +; CHECK-NEXT: [[TMP10:%.*]] = load i64*, i64** [[TMP9]], align 8 +; CHECK-NEXT: [[TMP11:%.*]] = call i8* @CJ_MCC_GetFuncPtrFromItab(i64* [[TMP10]], i32 17, i32 2821, i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"$const_string.35", i64 0, i64 0)) +; CHECK-NEXT: [[TMP12:%.*]] = bitcast i8* [[TMP11]] to i64 (i8 addrspace(1)*)* +; CHECK-NEXT: [[TMP13:%.*]] = call i64 [[TMP12]](i8 addrspace(1)* [[IT]]) +; CHECK-NEXT: [[ICMPEQ:%.*]] = icmp eq i64 [[TMP13]], 0 +; CHECK-NEXT: br i1 [[ICMPEQ]], label [[COMMON_RET:%.*]], label [[IF_ELSE16086E:%.*]] +; CHECK: common.ret: +; CHECK-NEXT: [[DOTSINK47:%.*]] = phi i8* [ [[TMP1]], [[IF_ELSE28490E:%.*]] ], [ bitcast (%"record._ZN11std$FS$core6StringE"* @"_ZN15std$$collection22$__STRING_LITERAL__$22E" to i8*), [[ENTRY:%.*]] ] +; CHECK-NEXT: [[TMP14:%.*]] = bitcast %"record._ZN11std$FS$core6StringE"* [[TMP0:%.*]] to i8* +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(16) [[TMP14]], i8* noundef nonnull align 8 dereferenceable(16) [[DOTSINK47]], i64 16, i1 false) +; CHECK-NEXT: ret void +; CHECK: if.else16086E: +; CHECK-NEXT: [[TMP15:%.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 [[TMP13]], i64 2) +; CHECK-NEXT: [[DOTFCA_1_EXTRACT:%.*]] = extractvalue { i64, i1 } [[TMP15]], 1 +; CHECK-NEXT: br i1 [[DOTFCA_1_EXTRACT]], label [[OVERFLOW0E:%.*]], label [[NORMAL0E:%.*]], !prof [[PROF10]] +; CHECK: normal0E: +; CHECK-NEXT: [[DOTFCA_0_EXTRACT:%.*]] = extractvalue { i64, i1 } [[TMP15]], 0 +; CHECK-NEXT: [[TMP16:%.*]] = or i64 [[DOTFCA_0_EXTRACT]], 1 +; CHECK-NEXT: [[ARR_ALLOC_SIZE_VALID:%.*]] = icmp sgt i64 [[TMP16]], -1 +; CHECK-NEXT: br i1 [[ARR_ALLOC_SIZE_VALID]], label [[ARR_ALLOC_BODY0E:%.*]], label [[ARR_ALLOC_THROW0E:%.*]] +; CHECK: overflow0E: +; CHECK-NEXT: call void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") [[CALLRET]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.30", i64 0, i64 0), i64 3) +; CHECK-NEXT: [[TMP17:%.*]] = addrspacecast %"record._ZN11std$FS$core6StringE"* [[CALLRET]] to %"record._ZN11std$FS$core6StringE" addrspace(1)* +; CHECK-NEXT: [[TMP18:%.*]] = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* [[TMP17]]) +; CHECK-NEXT: call void @CJ_MCC_ThrowException(i8 addrspace(1)* [[TMP18]]) +; CHECK-NEXT: unreachable +; CHECK: arr.alloc.throw0E: +; CHECK-NEXT: [[TMP19:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core26NegativeArraySizeExceptionE.objKlass" to i8*), i32 56) +; CHECK-NEXT: call void @"_ZN11std$FS$core26NegativeArraySizeException4initEv"(i8 addrspace(1)* [[TMP19]]) +; CHECK-NEXT: call void @CJ_MCC_ThrowException(i8 addrspace(1)* [[TMP19]]) +; CHECK-NEXT: unreachable +; CHECK: arr.alloc.body0E: +; CHECK-NEXT: [[TMP20:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewArrayStub(i64 [[TMP16]], i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core6StringE.arrayKlass" to i8*)) +; CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds i8, i8 addrspace(1)* [[TMP20]], i64 16 +; CHECK-NEXT: [[TMP22:%.*]] = bitcast i8 addrspace(1)* [[TMP21]] to %"record._ZN11std$FS$core6StringE" addrspace(1)* +; CHECK-NEXT: br label [[ARR_INIT_BODY0E:%.*]] +; CHECK: arr.init.body0E: +; CHECK-NEXT: [[ITERATOR_0:%.*]] = phi i64 [ 0, [[ARR_ALLOC_BODY0E]] ], [ [[TMP25:%.*]], [[ARR_INIT_BODY0E]] ] +; CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core6StringE", %"record._ZN11std$FS$core6StringE" addrspace(1)* [[TMP22]], i64 [[ITERATOR_0]] +; CHECK-NEXT: [[TMP24:%.*]] = bitcast %"record._ZN11std$FS$core6StringE" addrspace(1)* [[TMP23]] to i8 addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* [[TMP20]], i8 addrspace(1)* [[TMP24]], i8* bitcast (%"record._ZN11std$FS$core6StringE"* @"_ZN11std$FS$core6String5emptyE" to i8*), i64 16) +; CHECK-NEXT: [[TMP25]] = add nuw nsw i64 [[ITERATOR_0]], 1 +; CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[TMP25]], [[TMP16]] +; CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[ARR_INIT_OPT0E:%.*]], label [[ARR_INIT_BODY0E]] +; CHECK: arr.init.opt0E: +; CHECK-NEXT: [[ATOM_17657E_SROA_0_0__SROA_IDX11:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[STRINGARRAY]], i64 0, i32 0 +; CHECK-NEXT: store i8 addrspace(1)* [[TMP20]], i8 addrspace(1)** [[ATOM_17657E_SROA_0_0__SROA_IDX11]], align 8 +; CHECK-NEXT: [[ATOM_17657E_SROA_4_0__SROA_IDX14:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[STRINGARRAY]], i64 0, i32 1 +; CHECK-NEXT: store i64 0, i64* [[ATOM_17657E_SROA_4_0__SROA_IDX14]], align 4 +; CHECK-NEXT: [[ATOM_17657E_SROA_5_0__SROA_IDX17:%.*]] = getelementptr inbounds %"record._ZN11std$FS$core11std$FS$core5ArrayIlE", %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[STRINGARRAY]], i64 0, i32 2 +; CHECK-NEXT: store i64 [[TMP16]], i64* [[ATOM_17657E_SROA_5_0__SROA_IDX17]], align 4 +; CHECK-NEXT: call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* [[TMP20]], i8 addrspace(1)* [[TMP21]], i8* bitcast (%"record._ZN11std$FS$core6StringE"* @"_ZN15std$$collection22$__STRING_LITERAL__$23E" to i8*), i64 16) +; CHECK-NEXT: [[TMP26:%.*]] = call i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* [[IT]]) +; CHECK-NEXT: [[TMP27:%.*]] = getelementptr i8, i8* [[TMP26]], i64 32 +; CHECK-NEXT: [[TMP28:%.*]] = bitcast i8* [[TMP27]] to i64** +; CHECK-NEXT: [[TMP29:%.*]] = load i64*, i64** [[TMP28]], align 8 +; CHECK-NEXT: [[TMP30:%.*]] = call i8* @CJ_MCC_GetFuncPtrFromItab(i64* [[TMP29]], i32 18, i32 110, i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"$const_string.33", i64 0, i64 0)) +; CHECK-NEXT: [[TMP31:%.*]] = bitcast i8* [[TMP30]] to i8 addrspace(1)* (i8 addrspace(1)*)* +; CHECK-NEXT: [[TMP32:%.*]] = call i8 addrspace(1)* [[TMP31]](i8 addrspace(1)* [[IT]]) +; CHECK-NEXT: [[TMP33:%.*]] = getelementptr inbounds %"record._ZN17std$FS$collection11std$FS$core6OptionIcE", %"record._ZN17std$FS$collection11std$FS$core6OptionIcE"* [[CALLRET7]], i64 0, i32 0 +; CHECK-NEXT: [[TMP34:%.*]] = getelementptr inbounds %"record._ZN17std$FS$collection11std$FS$core6OptionIcE", %"record._ZN17std$FS$collection11std$FS$core6OptionIcE"* [[CALLRET7]], i64 0, i32 1 +; CHECK-NEXT: br label [[BLOCK_BODY16122E:%.*]] +; CHECK: block.body16122E: +; CHECK-NEXT: [[ATOM_17659E_0:%.*]] = phi i64 [ 1, [[ARR_INIT_OPT0E]] ], [ [[DOTFCA_0_EXTRACT29:%.*]], [[IF_ELSE28455E:%.*]] ] +; CHECK-NEXT: [[TMP35:%.*]] = call i8* @CJ_MCC_GetObjClass(i8 addrspace(1)* [[TMP32]]) +; CHECK-NEXT: [[TMP36:%.*]] = getelementptr i8, i8* [[TMP35]], i64 32 +; CHECK-NEXT: [[TMP37:%.*]] = bitcast i8* [[TMP36]] to i64** +; CHECK-NEXT: [[TMP38:%.*]] = load i64*, i64** [[TMP37]], align 8 +; CHECK-NEXT: [[TMP39:%.*]] = call i8* @CJ_MCC_GetFuncPtrFromItab(i64* [[TMP38]], i32 10, i32 907, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @"$const_string.34", i64 0, i64 0)) +; CHECK-NEXT: [[TMP40:%.*]] = bitcast i8* [[TMP39]] to void (%"record._ZN17std$FS$collection11std$FS$core6OptionIcE"*, i8 addrspace(1)*)* +; CHECK-NEXT: call void [[TMP40]](%"record._ZN17std$FS$collection11std$FS$core6OptionIcE"* noalias nonnull sret(%"record._ZN17std$FS$collection11std$FS$core6OptionIcE") [[CALLRET7]], i8 addrspace(1)* [[TMP32]]) +; CHECK-NEXT: [[TMP41:%.*]] = load i1, i1* [[TMP33]], align 1 +; CHECK-NEXT: br i1 [[TMP41]], label [[NORMAL0E30:%.*]], label [[IF_ELSE16170E:%.*]] +; CHECK: if.else16170E: +; CHECK-NEXT: [[TMP42:%.*]] = load i32, i32* [[TMP34]], align 4 +; CHECK-NEXT: call void @"_ZN11std$FS$core6Extend4Char8toStringEv"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") [[CALLRET9]], i32 [[TMP42]]) +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 dereferenceable(16) [[TMP3]], i8* noundef nonnull align 8 dereferenceable(16) [[TMP4]], i64 16, i1 false) +; CHECK-NEXT: [[ICMPUGE10_NOT:%.*]] = icmp ult i64 [[ATOM_17659E_0]], [[TMP16]] +; CHECK-NEXT: br i1 [[ICMPUGE10_NOT]], label [[IF_ELSE28420E:%.*]], label [[IF_THEN28420E:%.*]] +; CHECK: if.then28420E: +; CHECK-NEXT: [[TMP43:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 56) +; CHECK-NEXT: call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* [[TMP43]]) +; CHECK-NEXT: call void @CJ_MCC_ThrowException(i8 addrspace(1)* [[TMP43]]) +; CHECK-NEXT: unreachable +; CHECK: if.else28420E: +; CHECK-NEXT: [[TMP44:%.*]] = load i8 addrspace(1)*, i8 addrspace(1)** [[ATOM_17657E_SROA_0_0__SROA_IDX11]], align 8 +; CHECK-NEXT: [[TMP45:%.*]] = bitcast i8 addrspace(1)* [[TMP44]] to %"ArrayLayout._ZN11std$FS$core6StringE" addrspace(1)* +; CHECK-NEXT: [[ARR_IDX_SET_GEP11:%.*]] = getelementptr inbounds %"ArrayLayout._ZN11std$FS$core6StringE", %"ArrayLayout._ZN11std$FS$core6StringE" addrspace(1)* [[TMP45]], i64 0, i32 1, i64 [[ATOM_17659E_0]] +; CHECK-NEXT: [[TMP46:%.*]] = bitcast %"record._ZN11std$FS$core6StringE" addrspace(1)* [[ARR_IDX_SET_GEP11]] to i8 addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* [[TMP44]], i8 addrspace(1)* [[TMP46]], i8* nonnull [[TMP3]], i64 16) +; CHECK-NEXT: [[TMP47:%.*]] = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 [[ATOM_17659E_0]], i64 1) +; CHECK-NEXT: [[DOTFCA_0_EXTRACT24:%.*]] = extractvalue { i64, i1 } [[TMP47]], 0 +; CHECK-NEXT: [[DOTFCA_1_EXTRACT25:%.*]] = extractvalue { i64, i1 } [[TMP47]], 1 +; CHECK-NEXT: br i1 [[DOTFCA_1_EXTRACT25]], label [[OVERFLOW0E14:%.*]], label [[NORMAL0E15:%.*]], !prof [[PROF10]] +; CHECK: normal0E15: +; CHECK-NEXT: [[ICMPUGE19_NOT:%.*]] = icmp ult i64 [[DOTFCA_0_EXTRACT24]], [[TMP16]] +; CHECK-NEXT: br i1 [[ICMPUGE19_NOT]], label [[IF_ELSE28455E]], label [[IF_THEN28455E:%.*]] +; CHECK: overflow0E14: +; CHECK-NEXT: call void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") [[CALLRET16]], i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.29", i64 0, i64 0), i64 3) +; CHECK-NEXT: [[TMP48:%.*]] = addrspacecast %"record._ZN11std$FS$core6StringE"* [[CALLRET16]] to %"record._ZN11std$FS$core6StringE" addrspace(1)* +; CHECK-NEXT: [[TMP49:%.*]] = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* [[TMP48]]) +; CHECK-NEXT: call void @CJ_MCC_ThrowException(i8 addrspace(1)* [[TMP49]]) +; CHECK-NEXT: unreachable +; CHECK: if.then28455E: +; CHECK-NEXT: [[TMP50:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 56) +; CHECK-NEXT: call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* [[TMP50]]) +; CHECK-NEXT: call void @CJ_MCC_ThrowException(i8 addrspace(1)* [[TMP50]]) +; CHECK-NEXT: unreachable +; CHECK: if.else28455E: +; CHECK-NEXT: [[TMP51:%.*]] = load i8 addrspace(1)*, i8 addrspace(1)** [[ATOM_17657E_SROA_0_0__SROA_IDX11]], align 8 +; CHECK-NEXT: [[TMP52:%.*]] = bitcast i8 addrspace(1)* [[TMP51]] to %"ArrayLayout._ZN11std$FS$core6StringE" addrspace(1)* +; CHECK-NEXT: [[ARR_IDX_SET_GEP20:%.*]] = getelementptr inbounds %"ArrayLayout._ZN11std$FS$core6StringE", %"ArrayLayout._ZN11std$FS$core6StringE" addrspace(1)* [[TMP52]], i64 0, i32 1, i64 [[DOTFCA_0_EXTRACT24]] +; CHECK-NEXT: [[TMP53:%.*]] = bitcast %"record._ZN11std$FS$core6StringE" addrspace(1)* [[ARR_IDX_SET_GEP20]] to i8 addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* [[TMP51]], i8 addrspace(1)* [[TMP53]], i8* bitcast (%"record._ZN11std$FS$core6StringE"* @"_ZN15std$$collection22$__STRING_LITERAL__$24E" to i8*), i64 16) +; CHECK-NEXT: [[DOTFCA_0_EXTRACT29]] = add nuw i64 [[ATOM_17659E_0]], 2 +; CHECK-NEXT: br label [[BLOCK_BODY16122E]] +; CHECK: normal0E30: +; CHECK-NEXT: [[ICMPUGE34_NOT:%.*]] = icmp ult i64 [[DOTFCA_0_EXTRACT]], [[TMP16]] +; CHECK-NEXT: br i1 [[ICMPUGE34_NOT]], label [[IF_ELSE28490E]], label [[IF_THEN28490E:%.*]] +; CHECK: if.then28490E: +; CHECK-NEXT: [[TMP54:%.*]] = call noalias i8 addrspace(1)* @CJ_MCC_NewObjectStub(i8* bitcast (%KlassInfo.0* @"_ZN11std$FS$core25IndexOutOfBoundsExceptionE.objKlass" to i8*), i32 56) +; CHECK-NEXT: call void @"_ZN11std$FS$core25IndexOutOfBoundsException4initEv"(i8 addrspace(1)* [[TMP54]]) +; CHECK-NEXT: call void @CJ_MCC_ThrowException(i8 addrspace(1)* [[TMP54]]) +; CHECK-NEXT: unreachable +; CHECK: if.else28490E: +; CHECK-NEXT: [[TMP55:%.*]] = load i8 addrspace(1)*, i8 addrspace(1)** [[ATOM_17657E_SROA_0_0__SROA_IDX11]], align 8 +; CHECK-NEXT: [[TMP56:%.*]] = bitcast i8 addrspace(1)* [[TMP55]] to %"ArrayLayout._ZN11std$FS$core6StringE" addrspace(1)* +; CHECK-NEXT: [[ARR_IDX_SET_GEP35:%.*]] = getelementptr inbounds %"ArrayLayout._ZN11std$FS$core6StringE", %"ArrayLayout._ZN11std$FS$core6StringE" addrspace(1)* [[TMP56]], i64 0, i32 1, i64 [[DOTFCA_0_EXTRACT]] +; CHECK-NEXT: [[TMP57:%.*]] = bitcast %"record._ZN11std$FS$core6StringE" addrspace(1)* [[ARR_IDX_SET_GEP35]] to i8 addrspace(1)* +; CHECK-NEXT: call void @llvm.cj.gcwrite.struct.p0i8(i8 addrspace(1)* [[TMP55]], i8 addrspace(1)* [[TMP57]], i8* bitcast (%"record._ZN11std$FS$core6StringE"* @"_ZN15std$$collection22$__STRING_LITERAL__$25E" to i8*), i64 16) +; CHECK-NEXT: [[TMP58:%.*]] = addrspacecast %"record._ZN11std$FS$core11std$FS$core5ArrayIlE"* [[STRINGARRAY]] to %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* +; CHECK-NEXT: call void @"_ZN11std$FS$core6String4joinER_ZN11std$FS$core5ArrayIR_ZN11std$FS$core6StringEER_ZN11std$FS$core6StringE"(%"record._ZN11std$FS$core6StringE"* noalias nonnull sret(%"record._ZN11std$FS$core6StringE") [[CALLRET36]], i8 addrspace(1)* null, %"record._ZN11std$FS$core11std$FS$core5ArrayIlE" addrspace(1)* [[TMP58]], i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* addrspacecast (%"record._ZN11std$FS$core6StringE"* @"_ZN11std$FS$core6String5emptyE" to %"record._ZN11std$FS$core6StringE" addrspace(1)*)) +; CHECK-NEXT: br label [[COMMON_RET]] +; + +attributes #0 = { readonly "GCRoot" } + +!llvm.module.flags = !{!9} + +; CHECK: !0 = !{!"R_ZN11std$FS$core5ArrayIlE"} +; CHECK-NEXT: !1 = !{!"R_ZN11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE"} +; CHECK-NEXT: !2 = !{!"A1_hE"} +; CHECK-NEXT: !3 = !{!"R_ZN11std$FS$core5ArrayIhE"} +; CHECK-NEXT: !4 = !{!"R_ZN11std$FS$core6StringE"} +; CHECK-NEXT: !5 = !{i32 2, !"CJBC", i32 1} +; CHECK-NEXT: !6 = distinct !{!"record._ZN11std$FS$core11std$FS$core5ArrayIlE"} +; CHECK-NEXT: !7 = !{!"record._ZN11std$FS$core11std$FS$core5ArrayIlE"} +; CHECK-NEXT: !8 = distinct !{!"record._ZN11std$FS$core11std$FS$core5ArrayIlE"} +; CHECK-NEXT: !9 = !{!"record._ZN11std$FS$core6StringE"} +; CHECK-NEXT: !10 = !{!"branch_weights", i32 1, i32 2000} +; CHECK-NEXT: !11 = !{!12} +; CHECK-NEXT: !12 = distinct !{!12, !13, !"_ZN17std$FS$collection13StringBuilder25fromCharIndexToUInt8IndexEll: argument 0"} +; CHECK-NEXT: !13 = distinct !{!13, !"_ZN17std$FS$collection13StringBuilder25fromCharIndexToUInt8IndexEll"} +!0 = !{!"R_ZN11std$FS$core5ArrayIhE"} +!1 = !{!"A1_hE"} +!2 = !{!"R_ZN11std$FS$core5ArrayIlE"} +!3 = !{!"R_ZN11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE"} +!4 = !{!"R_ZN11std$FS$core6StringE"} +!5 = !{!"record._ZN11std$FS$core6StringE"} +!6 = !{!"record._ZN11std$FS$core11std$FS$core5ArrayIhE"} +!7 = !{!"record._ZN11std$FS$core11std$FS$core5ArrayIlE"} +!8 = !{!"record._ZN11std$FS$core11std$FS$core5ArrayIR_ZN11std$FS$core6StringEE"} +!9 = !{i32 2, !"CJBC", i32 1} diff --git a/llvm/test/CJLTO/cj-thinlto-other-bc.ll b/llvm/test/CJLTO/cj-thinlto-other-bc.ll new file mode 100644 index 000000000..cdcc3fa65 --- /dev/null +++ b/llvm/test/CJLTO/cj-thinlto-other-bc.ll @@ -0,0 +1,37 @@ +; RUN: opt --module-summary %s -o %t +; RUN: not not ld.lld -shared --mllvm --cangjie-pipeline -O2 --plugin-opt=no-opaque-pointers %t -o %t1 2>&1 | FileCheck --check-prefix=ABORT %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@.str = private unnamed_addr constant [4 x i8] c"Hi\0A\00", align 1 +@.str.1 = private unnamed_addr constant [6 x i8] c"main\0A\00", align 1 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @foo4() #0 { + %1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0)) + ret void +} + +declare dso_local i32 @printf(i8*, ...) #1 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @main() #0 { + %1 = alloca i32, align 4 + store i32 0, i32* %1, align 4 + %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.1, i64 0, i64 0)) + %3 = call i32 @foo1() + ret i32 %3 +} + +declare dso_local i32 @foo1() #1 + +; ABORT: LLVM ERROR: Only allow module from cangjie bc files for cangjie LTO! +; ABORT: error: Aborted + +attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.module.flags = !{!0} + +!0 = !{i32 1, !"wchar_size", i32 4} diff --git a/llvm/test/CJLTO/cj-twice-rewritestatepoints.ll b/llvm/test/CJLTO/cj-twice-rewritestatepoints.ll new file mode 100644 index 000000000..bfc9a6a2f --- /dev/null +++ b/llvm/test/CJLTO/cj-twice-rewritestatepoints.ll @@ -0,0 +1,44 @@ +; RUN: llvm-as %s -o %t +; RUN: not not ld.lld --mllvm --cangjie-pipeline -O2 --plugin-opt=no-opaque-pointers %t -o %t1 2>&1 | FileCheck --check-prefix=ABORT %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%Unit.Type = type { i8 } +%"record._ZN11std$FS$core6StringE" = type { i8 addrspace(1)*, i64 } + +declare void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* sret(%"record._ZN11std$FS$core6StringE"), i8*, i64) gc "cangjie" +declare void @"_ZN11std$FS$core7printlnER_ZN11std$FS$core6StringE"(%Unit.Type* sret(%Unit.Type), i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) gc "cangjie" +declare void @CJ_MCC_ThrowStackOverflowError() +declare cangjiegccc void @MCC_SafepointStub() +declare token @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 immarg, i32 immarg, void ()*, i32 immarg, i32 immarg, ...) +declare token @"llvm.experimental.gc.statepoint.p0f_isVoidp0s_Unit.Typesp1i8p1s_record._ZN11std$FS$core6StringEsf"(i64 immarg, i32 immarg, void (%Unit.Type*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*)*, i32 immarg, i32 immarg, ...) +declare token @"llvm.experimental.gc.statepoint.p0f_isVoidp0s_record._ZN11std$FS$core6StringEsp0i8i64f"(i64 immarg, i32 immarg, void (%"record._ZN11std$FS$core6StringE"*, i8*, i64)*, i32 immarg, i32 immarg, ...) +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) + +@"$const_string.0" = private unnamed_addr constant [9 x i8] c"hello cj\00" + +define void @_ZN7default4mainEv(%Unit.Type* sret(%Unit.Type) %0) gc "cangjie" { +entry: + %callRet1 = alloca %Unit.Type + %callRet = alloca %"record._ZN11std$FS$core6StringE" + %1 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 16, i1 false) + br label %thunk19E + +thunk19E: ; preds = %entry + %statepoint_token = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* elementtype(void ()) @CJ_MCC_ThrowStackOverflowError, i32 0, i32 0, i32 0, i32 0) [ "struct-live"(%"record._ZN11std$FS$core6StringE"* %callRet) ] + %statepoint_token1 = call cangjiegccc token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* elementtype(void ()) @MCC_SafepointStub, i32 0, i32 0, i32 0, i32 0) [ "struct-live"(%"record._ZN11std$FS$core6StringE"* %callRet) ] + %statepoint_token2 = call token (i64, i32, void (%"record._ZN11std$FS$core6StringE"*, i8*, i64)*, i32, i32, ...) @"llvm.experimental.gc.statepoint.p0f_isVoidp0s_record._ZN11std$FS$core6StringEsp0i8i64f"(i64 2882400000, i32 0, void (%"record._ZN11std$FS$core6StringE"*, i8*, i64)* elementtype(void (%"record._ZN11std$FS$core6StringE"*, i8*, i64)) @"_ZN11std$FS$core6String23createStringFromLiteralE", i32 3, i32 0, %"record._ZN11std$FS$core6StringE"* sret(%"record._ZN11std$FS$core6StringE") %callRet, i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"$const_string.0", i32 0, i32 0), i64 8, i32 0, i32 0) [ "struct-live"(%"record._ZN11std$FS$core6StringE"* %callRet) ] + %2 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %statepoint_token3 = call token (i64, i32, void (%Unit.Type*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*)*, i32, i32, ...) @"llvm.experimental.gc.statepoint.p0f_isVoidp0s_Unit.Typesp1i8p1s_record._ZN11std$FS$core6StringEsf"(i64 2882400000, i32 0, void (%Unit.Type*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*)* elementtype(void (%Unit.Type*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*)) @"_ZN11std$FS$core7printlnER_ZN11std$FS$core6StringE", i32 3, i32 0, %Unit.Type* sret(%Unit.Type) %callRet1, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %2, i32 0, i32 0) [ "struct-live"(%"record._ZN11std$FS$core6StringE"* %callRet) ] + ret void +} + +; ABORT: LLVM ERROR: Module has done CJRewriteStatepoint before! +; ABORT: error: Aborted + +!llvm.module.flags = !{!0, !1} + +!0 = !{i32 2, !"CJBC", i32 1} +!1 = !{i32 2, !"HasRewrittenStatepoint", i32 1} diff --git a/llvm/test/CJLTO/pm-cj-fulllto-defaults.ll b/llvm/test/CJLTO/pm-cj-fulllto-defaults.ll new file mode 100644 index 000000000..3fd9995b8 --- /dev/null +++ b/llvm/test/CJLTO/pm-cj-fulllto-defaults.ll @@ -0,0 +1,1706 @@ +; XFAIL: * +; Basic test for the cangjie FullLTO pipeline. + +; RUN: llvm-as %s -o %t +; RUN: ld.lld -shared --plugin-opt=debug-pass-manager --mllvm --cangjie-pipeline \ +; RUN: --plugin-opt=no-opaque-pointers --mllvm --cangjie-full-lto %t -o %t1 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O + +; CHECK-O: Running pass: VerifierPass +; CHECK-O-NEXT: Running analysis: VerifierAnalysis +; CHECK-O-NEXT: Running pass: CJRuntimeLowering +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.cj.memset.p0i8 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime3nowE$timeZone___0v +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime3nowEC_ZN11std$FS$time8TimeZoneE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.memcpy.p0i8.p0i8.i64 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime1-ER_ZN11std$FS$time8DateTimeE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8Duration13toNanosecondsEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core6String23createStringFromLiteralE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN13std$FS$format6Extend7Float646formatER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core6String1+ER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core7printlnER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.sadd.with.overflow.i64 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.expect.i1 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on rt$CreateOverflowException_msg +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_ThrowException +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on user.main +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_C2NStub +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_N2CStub +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.cj.heapmalloc.class +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_NewObjectFast +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_NewFinalizerFast +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_OnFinalizerCreated +; CHECK-O-NEXT: Running pass: Annotation2MetadataPass +; CHECK-O-NEXT: Running pass: CrossDSOCFIPass +; CHECK-O-NEXT: Running pass: OpenMPOptPass +; CHECK-O-NEXT: Running pass: GlobalDCEPass +; CHECK-O-NEXT: Invalidating analysis: VerifierAnalysis +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running pass: ForceFunctionAttrsPass +; CHECK-O-NEXT: Running pass: InferFunctionAttrsPass +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.cj.memset.p0i8 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime3nowE$timeZone___0v +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime3nowEC_ZN11std$FS$time8TimeZoneE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.memcpy.p0i8.p0i8.i64 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime1-ER_ZN11std$FS$time8DateTimeE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8Duration13toNanosecondsEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core6String23createStringFromLiteralE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN13std$FS$format6Extend7Float646formatER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core6String1+ER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core7printlnER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.sadd.with.overflow.i64 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.expect.i1 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on rt$CreateOverflowException_msg +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_ThrowException +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_C2NStub +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_N2CStub +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_NewObjectFast +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_NewFinalizerFast +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_OnFinalizerCreated +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running pass: CallSiteSplittingPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: CallSiteSplittingPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: CallSiteSplittingPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: CallSiteSplittingPass on user.main +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on user.main +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on user.main +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running pass: PGOIndirectCallPromotion +; CHECK-O-NEXT: Running analysis: ProfileSummaryAnalysis +; CHECK-O-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis on user.main +; CHECK-O-NEXT: Running pass: IPSCCPPass +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on user.main +; CHECK-O-NEXT: Running pass: CalledValuePropagationPass +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (_ZN7default17benchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass on (_ZN7default17benchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: ScopedNoAliasAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TypeBasedAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (_ZN7default21timeBenchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass on (_ZN7default21timeBenchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: ScopedNoAliasAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TypeBasedAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: ScopedNoAliasAA on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TypeBasedAA on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (user.main) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass on (user.main) +; CHECK-O-NEXT: Running analysis: AAManager on user.main +; CHECK-O-NEXT: Running analysis: BasicAA on user.main +; CHECK-O-NEXT: Running analysis: ScopedNoAliasAA on user.main +; CHECK-O-NEXT: Running analysis: TypeBasedAA on user.main +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass +; CHECK-O-NEXT: Running analysis: CallGraphAnalysis +; CHECK-O-NEXT: Running pass: GlobalSplitPass +; CHECK-O-NEXT: Running pass: WholeProgramDevirtPass +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Invalidating analysis: LazyCallGraphAnalysis +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Invalidating analysis: CallGraphAnalysis +; CHECK-O-NEXT: Running pass: GlobalOptPass +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: PromotePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: PromotePass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: PromotePass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: PromotePass on user.main +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on user.main +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on user.main +; CHECK-O-NEXT: Running pass: ConstantMergePass +; CHECK-O-NEXT: Running pass: DeadArgumentEliminationPass +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: ScopedNoAliasAA on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TypeBasedAA on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: ScopedNoAliasAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TypeBasedAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: ScopedNoAliasAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TypeBasedAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on user.main +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on user.main +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on user.main +; CHECK-O-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis on user.main +; CHECK-O-NEXT: Running analysis: AAManager on user.main +; CHECK-O-NEXT: Running analysis: BasicAA on user.main +; CHECK-O-NEXT: Running analysis: ScopedNoAliasAA on user.main +; CHECK-O-NEXT: Running analysis: TypeBasedAA on user.main +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: BasicAA on user.main +; CHECK-O-NEXT: Invalidating analysis: AAManager on user.main +; CHECK-O-NEXT: Running pass: ModuleInlinerWrapperPass +; CHECK-O-NEXT: Running analysis: InlineAdvisorAnalysis +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (_ZN7default17benchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: InlinerPass on (_ZN7default17benchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running pass: InlinerPass on (_ZN7default17benchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (_ZN7default21timeBenchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: InlinerPass on (_ZN7default21timeBenchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running pass: InlinerPass on (_ZN7default21timeBenchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: PostDominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BranchProbabilityAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BlockFrequencyAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Clearing all analysis results for: _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Clearing all analysis results for: (_ZN7default17benchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: InlinerPass on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running pass: InlinerPass on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BranchProbabilityAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BlockFrequencyAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Clearing all analysis results for: _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Clearing all analysis results for: (_ZN7default21timeBenchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (user.main) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: InlinerPass on (user.main) +; CHECK-O-NEXT: Running pass: InlinerPass on (user.main) +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on user.main +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on user.main +; CHECK-O-NEXT: Running analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: InlineAdvisorAnalysis +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: PostDominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: BranchProbabilityAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: BlockFrequencyAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: LazyCallGraphAnalysis +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: PEAPass on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (user.main) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: PEAPass on (user.main) +; CHECK-O-NEXT: Running analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Running pass: PartialEscapeAnalysisPass on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: PartialEscapeAnalysisPass on (user.main) +; CHECK-O-NEXT: Running analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running pass: GlobalOptPass +; CHECK-O-NEXT: Running pass: GlobalDCEPass +; CHECK-O-NEXT: Running pass: ArgumentPromotionPass on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running pass: ArgumentPromotionPass on (user.main) +; CHECK-O-NEXT: Running pass: SROAPass on _ZN7default4mainEv (411 instructions) +; CHECK-O-NEXT: Running pass: EarlyCSEPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SpeculativeExecutionPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: JumpThreadingPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LazyValueAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: CorrelatedValuePropagationPass on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LazyValueAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LibCallsShrinkWrapPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: TailCallElimPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: ReassociatePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: RequireAnalysisPass +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopRotatePass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: IndVarSimplifyPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopDeletionPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopInstSimplifyPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopSimplifyCFGPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LICMPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopRotatePass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LICMPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: SimpleLoopUnswitchPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopRotatePass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: IndVarSimplifyPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopDeletionPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopInstSimplifyPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopSimplifyCFGPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LICMPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopRotatePass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LICMPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: SimpleLoopUnswitchPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Clearing all analysis results for: +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopIdiomRecognizePass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: IndVarSimplifyPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopDeletionPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopFullUnrollPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: SROAPass on _ZN7default4mainEv +; CHECK-O-NEXT: Clearing all analysis results for: +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: MergedLoadStoreMotionPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: GVNPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: MemoryDependenceAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PhiValuesAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: PhiValuesAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: MemoryDependenceAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SCCPPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: BDCEPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DemandedBitsAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: DemandedBitsAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: JumpThreadingPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LazyValueAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: CorrelatedValuePropagationPass on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LazyValueAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: ADCEPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: MemCpyOptPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: DSEPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LICMPass on Loop at depth 1 +; CHECK-O-NEXT: Invalidating analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: CoroElidePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SROAPass on user.main +; CHECK-O-NEXT: Running pass: EarlyCSEPass on user.main +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on user.main +; CHECK-O-NEXT: Running analysis: AAManager on user.main +; CHECK-O-NEXT: Running analysis: BasicAA on user.main +; CHECK-O-NEXT: Running pass: SpeculativeExecutionPass on user.main +; CHECK-O-NEXT: Running pass: JumpThreadingPass on user.main +; CHECK-O-NEXT: Running analysis: LazyValueAnalysis on user.main +; CHECK-O-NEXT: Running pass: CorrelatedValuePropagationPass on user.main +; CHECK-O-NEXT: Invalidating analysis: LazyValueAnalysis on user.main +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Running pass: LibCallsShrinkWrapPass on user.main +; CHECK-O-NEXT: Running pass: TailCallElimPass on user.main +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on user.main +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Running pass: ReassociatePass on user.main +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Running pass: RequireAnalysisPass +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on user.main +; CHECK-O-NEXT: Running analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Running pass: LCSSAPass on user.main +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on user.main +; CHECK-O-NEXT: Running pass: LCSSAPass on user.main +; CHECK-O-NEXT: Running pass: SROAPass on user.main +; CHECK-O-NEXT: Running pass: MergedLoadStoreMotionPass on user.main +; CHECK-O-NEXT: Running pass: GVNPass on user.main +; CHECK-O-NEXT: Running analysis: MemoryDependenceAnalysis on user.main +; CHECK-O-NEXT: Running analysis: PhiValuesAnalysis on user.main +; CHECK-O-NEXT: Running pass: SCCPPass on user.main +; CHECK-O-NEXT: Running pass: BDCEPass on user.main +; CHECK-O-NEXT: Running analysis: DemandedBitsAnalysis on user.main +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Running pass: JumpThreadingPass on user.main +; CHECK-O-NEXT: Running analysis: LazyValueAnalysis on user.main +; CHECK-O-NEXT: Running pass: CorrelatedValuePropagationPass on user.main +; CHECK-O-NEXT: Invalidating analysis: LazyValueAnalysis on user.main +; CHECK-O-NEXT: Running pass: ADCEPass on user.main +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running pass: MemCpyOptPass on user.main +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on user.main +; CHECK-O-NEXT: Running pass: DSEPass on user.main +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on user.main +; CHECK-O-NEXT: Running pass: LCSSAPass on user.main +; CHECK-O-NEXT: Running pass: CoroElidePass on user.main +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: BasicAA on user.main +; CHECK-O-NEXT: Invalidating analysis: AAManager on user.main +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: PhiValuesAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: MemoryDependenceAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: DemandedBitsAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: PostDominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: LazyCallGraphAnalysis +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running pass: CJDevirtualOpt +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: JumpThreadingPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LazyValueAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SROAPass on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LazyValueAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: TailCallElimPass on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running analysis: AAManager on user.main +; CHECK-O-NEXT: Running analysis: BasicAA on user.main +; CHECK-O-NEXT: Running pass: JumpThreadingPass on user.main +; CHECK-O-NEXT: Running analysis: LazyValueAnalysis on user.main +; CHECK-O-NEXT: Running pass: SROAPass on user.main +; CHECK-O-NEXT: Running pass: TailCallElimPass on user.main +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: BasicAA on user.main +; CHECK-O-NEXT: Invalidating analysis: AAManager on user.main +; CHECK-O-NEXT: Invalidating analysis: LazyValueAnalysis on user.main +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (user.main) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass on (user.main) +; CHECK-O-NEXT: Running analysis: AAManager on user.main +; CHECK-O-NEXT: Running analysis: BasicAA on user.main +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running pass: RequireAnalysisPass +; CHECK-O-NEXT: Running analysis: GlobalsAA +; CHECK-O-NEXT: Running analysis: CallGraphAnalysis +; CHECK-O-NEXT: Running pass: InvalidateAnalysisPass on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InvalidateAnalysisPass on user.main +; CHECK-O-NEXT: Invalidating analysis: AAManager on user.main +; CHECK-O-NEXT: Running pass: OpenMPOptCGSCCPass on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running pass: OpenMPOptCGSCCPass on (user.main) +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LICMPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: GVNPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: MemoryDependenceAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PhiValuesAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Invalidating analysis: PhiValuesAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: MemoryDependenceAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: MemCpyOptPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: DSEPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: MergedLoadStoreMotionPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: IndVarSimplifyPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopDeletionPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopFullUnrollPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopDistributePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LoopVectorizePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DemandedBitsAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LoopUnrollPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: WarnMissedTransformationsPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Clearing all analysis results for: +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Invalidating analysis: DemandedBitsAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SCCPPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: BDCEPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DemandedBitsAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: VectorCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: AlignmentFromAssumptionsPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: JumpThreadingPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LazyValueAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BranchProbabilityAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BlockFrequencyAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: DemandedBitsAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LazyValueAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on user.main +; CHECK-O-NEXT: Running analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Running pass: LCSSAPass on user.main +; CHECK-O-NEXT: Running pass: GVNPass on user.main +; CHECK-O-NEXT: Running analysis: AAManager on user.main +; CHECK-O-NEXT: Running analysis: MemoryDependenceAnalysis on user.main +; CHECK-O-NEXT: Running analysis: PhiValuesAnalysis on user.main +; CHECK-O-NEXT: Running pass: MemCpyOptPass on user.main +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on user.main +; CHECK-O-NEXT: Running pass: DSEPass on user.main +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running pass: MergedLoadStoreMotionPass on user.main +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on user.main +; CHECK-O-NEXT: Running pass: LCSSAPass on user.main +; CHECK-O-NEXT: Running pass: LoopDistributePass on user.main +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on user.main +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopVectorizePass on user.main +; CHECK-O-NEXT: Running pass: LoopUnrollPass on user.main +; CHECK-O-NEXT: Running pass: WarnMissedTransformationsPass on user.main +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Running pass: SCCPPass on user.main +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Running pass: BDCEPass on user.main +; CHECK-O-NEXT: Running analysis: DemandedBitsAnalysis on user.main +; CHECK-O-NEXT: Running pass: VectorCombinePass on user.main +; CHECK-O-NEXT: Running pass: AlignmentFromAssumptionsPass on user.main +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Running pass: JumpThreadingPass on user.main +; CHECK-O-NEXT: Running analysis: LazyValueAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: BasicAA on user.main +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: AAManager on user.main +; CHECK-O-NEXT: Invalidating analysis: PhiValuesAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: MemoryDependenceAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: PostDominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Invalidating analysis: DemandedBitsAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: LazyValueAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: LazyCallGraphAnalysis +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Invalidating analysis: CallGraphAnalysis +; CHECK-O-NEXT: Running pass: LowerTypeTestsPass +; CHECK-O-NEXT: Running pass: LowerTypeTestsPass +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Running pass: EliminateAvailableExternallyPass +; CHECK-O-NEXT: Running pass: GlobalDCEPass +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running pass: CGProfilePass +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on user.main +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on user.main +; CHECK-O-NEXT: Running analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on user.main +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on user.main +; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on user.main +; CHECK-O-NEXT: Running pass: CJSpecificOpt +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running pass: PlaceSafepoints +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.cj.memset.p0i8 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime3nowE$timeZone___0v +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime3nowEC_ZN11std$FS$time8TimeZoneE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.memcpy.p0i8.p0i8.i64 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime1-ER_ZN11std$FS$time8DateTimeE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8Duration13toNanosecondsEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core6String23createStringFromLiteralE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN13std$FS$format6Extend7Float646formatER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core6String1+ER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core7printlnER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.expect.i1 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on rt$CreateOverflowException_msg +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_ThrowException +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on user.main +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_C2NStub +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_N2CStub +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_NewObjectFast +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_NewFinalizerFast +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_OnFinalizerCreated +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.lifetime.start.p0i8 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.lifetime.end.p0i8 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.memset.p0i8.i64 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_StackCheck +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on MCC_SafepointStub +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on gc.safepoint_poll +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running pass: CJBarrierOpt +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running pass: CJFastBarrier +; CHECK-O-NEXT: Running pass: RewriteStatepointsForCangjieGC +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on user.main +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running pass: VerifierPass +; CHECK-O-NEXT: Running analysis: VerifierAnalysis + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%"record._ZN11std$FS$core6StringE" = type { i8 addrspace(1)*, i64 } +%Unit.Type = type { i8 } +%"record._ZN11std$FS$time8DateTimeE" = type { i32, i32, i64, i64, i8 addrspace(1)*, i64 } +%"record._ZN11std$FS$time8DurationE" = type { i64 } + +@_ZN7default1kE = global i64 2, align 8, !GlobalVarType !0 +@_ZN7default1lE = global i64 2, align 8, !GlobalVarType !0 +@_ZN7default1mE = global i64 2, align 8, !GlobalVarType !0 +@_ZN7default1nE = global i64 2, align 8, !GlobalVarType !0 +@_ZN7default4repsE = internal constant i64 100000000, align 8, !GlobalVarType !0 +@"$const_string.0" = private unnamed_addr constant [4 x i8] c"add\00" +@"$const_string.2" = private unnamed_addr constant [3 x i8] c".3\00" +@"$const_string.3" = private unnamed_addr constant [7 x i8] c" ns/op\00" +@"$const_string.5" = private unnamed_addr constant [20 x i8] c"BenchmarkIfElseD4: \00" + +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare void @"_ZN11std$FS$core6String1+ER_ZN11std$FS$core6StringE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE"), i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) gc "cangjie" +declare void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE"), i8*, i64) gc "cangjie" +declare void @"_ZN11std$FS$core7printlnER_ZN11std$FS$core6StringE"(%Unit.Type* noalias sret(%Unit.Type), i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) gc "cangjie" +declare void @"_ZN11std$FS$time8DateTime1-ER_ZN11std$FS$time8DateTimeE"(%"record._ZN11std$FS$time8DurationE"* noalias sret(%"record._ZN11std$FS$time8DurationE"), i8 addrspace(1)*, %"record._ZN11std$FS$time8DateTimeE" addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$time8DateTimeE" addrspace(1)*) gc "cangjie" +declare i8 addrspace(1)* @"_ZN11std$FS$time8DateTime3nowE$timeZone___0v"() gc "cangjie" +declare void @"_ZN11std$FS$time8DateTime3nowEC_ZN11std$FS$time8TimeZoneE"(%"record._ZN11std$FS$time8DateTimeE"* noalias sret(%"record._ZN11std$FS$time8DateTimeE"), i8 addrspace(1)*) gc "cangjie" +declare i64 @"_ZN11std$FS$time8Duration13toNanosecondsEv"(i8 addrspace(1)*, %"record._ZN11std$FS$time8DurationE" addrspace(1)*) gc "cangjie" +declare void @"_ZN13std$FS$format6Extend7Float646formatER_ZN11std$FS$core6StringE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE"), double, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) gc "cangjie" +declare void @llvm.cj.memset.p0i8(i8*, i8, i64, i1) +declare i1 @llvm.expect.i1(i1, i1) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) +declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) +declare i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) gc "cangjie" + +define internal void @_ZN7default17benchmarkIfElseD4Ev(%Unit.Type* noalias sret(%Unit.Type) %0) gc "cangjie" { +entry: + %enumTmp = alloca %"record._ZN11std$FS$core6StringE" + %callRet = alloca %"record._ZN11std$FS$core6StringE" + %1 = alloca i64 + %val.ov = alloca { i64, i1 } + %i = alloca i64 + %"$stop-compiler" = alloca i64 + %"$iter-i" = alloca i64 + %2 = bitcast %"record._ZN11std$FS$core6StringE"* %enumTmp to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %2, i8 0, i64 16, i1 false) + %3 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %3, i8 0, i64 16, i1 false) + br label %thunk782E + +thunk782E: ; preds = %entry + store i64 0, i64* %"$iter-i" + %4 = load i64, i64* @_ZN7default4repsE + store i64 %4, i64* %"$stop-compiler" + br label %block.body797E + +block.body797E: ; preds = %if.end1608E, %thunk782E + %5 = load i64, i64* %"$iter-i" + %6 = load i64, i64* %"$stop-compiler" + %icmpslt = icmp slt i64 %5, %6 + br i1 %icmpslt, label %while.body1620E, label %block.end797E + +while.body1620E: ; preds = %block.body797E + %7 = load i64, i64* %"$iter-i" + store i64 %7, i64* %i + %8 = load i64, i64* %"$iter-i" + %9 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %8, i64 1) + store { i64, i1 } %9, { i64, i1 }* %val.ov + %10 = getelementptr inbounds { i64, i1 }, { i64, i1 }* %val.ov, i32 0, i32 1 + %11 = load i1, i1* %10 + %12 = call i1 @llvm.expect.i1(i1 %11, i1 false) + br i1 %12, label %overflow0E, label %normal0E + +normal0E: ; preds = %while.body1620E + %13 = getelementptr inbounds { i64, i1 }, { i64, i1 }* %val.ov, i32 0, i32 0 + %14 = load i64, i64* %13 + store i64 %14, i64* %1 + %15 = load i64, i64* %1 + store i64 %15, i64* %"$iter-i" + %16 = load i64, i64* @_ZN7default1nE + %icmpsgt = icmp sgt i64 %16, 16 + br i1 %icmpsgt, label %if.then1608E, label %if.else1608E + +overflow0E: ; preds = %while.body1620E + call void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE") %callRet, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.0", i32 0, i32 0), i64 3) + %17 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %18 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %17) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %18) + unreachable + +if.then1608E: ; preds = %normal0E + %19 = load i64, i64* @_ZN7default1mE + %icmpsgt1 = icmp sgt i64 %19, 16 + br i1 %icmpsgt1, label %if.then860E, label %if.else860E + +if.then860E: ; preds = %if.then1608E + %20 = load i64, i64* @_ZN7default1lE + %icmpsgt2 = icmp sgt i64 %20, 16 + br i1 %icmpsgt2, label %if.then851E, label %if.else851E + +if.then851E: ; preds = %if.then860E + %21 = load i64, i64* @_ZN7default1kE + %icmpsgt3 = icmp sgt i64 %21, 16 + br i1 %icmpsgt3, label %if.then842E, label %if.else842E + +if.then842E: ; preds = %if.then851E + store i64 16, i64* @_ZN7default1nE + br label %if.end842E + +if.else842E: ; preds = %if.then851E + store i64 17, i64* @_ZN7default1nE + br label %if.end842E + +if.end842E: ; preds = %if.else842E, %if.then842E + br label %if.end851E + +if.else851E: ; preds = %if.then860E + store i64 18, i64* @_ZN7default1nE + br label %if.end851E + +if.end851E: ; preds = %if.else851E, %if.end842E + br label %if.end860E + +if.else860E: ; preds = %if.then1608E + store i64 19, i64* @_ZN7default1nE + br label %if.end860E + +if.end860E: ; preds = %if.else860E, %if.end851E + br label %if.end1608E + +if.else1608E: ; preds = %normal0E + %22 = load i64, i64* @_ZN7default1nE + %icmpsgt4 = icmp sgt i64 %22, 15 + br i1 %icmpsgt4, label %if.then1607E, label %if.else1607E + +if.then1607E: ; preds = %if.else1608E + %23 = load i64, i64* @_ZN7default1mE + %icmpsgt5 = icmp sgt i64 %23, 15 + br i1 %icmpsgt5, label %if.then909E, label %if.else909E + +if.then909E: ; preds = %if.then1607E + %24 = load i64, i64* @_ZN7default1lE + %icmpsgt6 = icmp sgt i64 %24, 15 + br i1 %icmpsgt6, label %if.then900E, label %if.else900E + +if.then900E: ; preds = %if.then909E + %25 = load i64, i64* @_ZN7default1kE + %icmpsgt7 = icmp sgt i64 %25, 15 + br i1 %icmpsgt7, label %if.then891E, label %if.else891E + +if.then891E: ; preds = %if.then900E + store i64 15, i64* @_ZN7default1nE + br label %if.end891E + +if.else891E: ; preds = %if.then900E + store i64 16, i64* @_ZN7default1nE + br label %if.end891E + +if.end891E: ; preds = %if.else891E, %if.then891E + br label %if.end900E + +if.else900E: ; preds = %if.then909E + store i64 17, i64* @_ZN7default1nE + br label %if.end900E + +if.end900E: ; preds = %if.else900E, %if.end891E + br label %if.end909E + +if.else909E: ; preds = %if.then1607E + store i64 18, i64* @_ZN7default1nE + br label %if.end909E + +if.end909E: ; preds = %if.else909E, %if.end900E + br label %if.end1607E + +if.else1607E: ; preds = %if.else1608E + %26 = load i64, i64* @_ZN7default1nE + %icmpsgt8 = icmp sgt i64 %26, 14 + br i1 %icmpsgt8, label %if.then1606E, label %if.else1606E + +if.then1606E: ; preds = %if.else1607E + %27 = load i64, i64* @_ZN7default1mE + %icmpsgt9 = icmp sgt i64 %27, 14 + br i1 %icmpsgt9, label %if.then958E, label %if.else958E + +if.then958E: ; preds = %if.then1606E + %28 = load i64, i64* @_ZN7default1lE + %icmpsgt10 = icmp sgt i64 %28, 14 + br i1 %icmpsgt10, label %if.then949E, label %if.else949E + +if.then949E: ; preds = %if.then958E + %29 = load i64, i64* @_ZN7default1kE + %icmpsgt11 = icmp sgt i64 %29, 14 + br i1 %icmpsgt11, label %if.then940E, label %if.else940E + +if.then940E: ; preds = %if.then949E + store i64 14, i64* @_ZN7default1nE + br label %if.end940E + +if.else940E: ; preds = %if.then949E + store i64 15, i64* @_ZN7default1nE + br label %if.end940E + +if.end940E: ; preds = %if.else940E, %if.then940E + br label %if.end949E + +if.else949E: ; preds = %if.then958E + store i64 16, i64* @_ZN7default1nE + br label %if.end949E + +if.end949E: ; preds = %if.else949E, %if.end940E + br label %if.end958E + +if.else958E: ; preds = %if.then1606E + store i64 17, i64* @_ZN7default1nE + br label %if.end958E + +if.end958E: ; preds = %if.else958E, %if.end949E + br label %if.end1606E + +if.else1606E: ; preds = %if.else1607E + %30 = load i64, i64* @_ZN7default1nE + %icmpsgt12 = icmp sgt i64 %30, 13 + br i1 %icmpsgt12, label %if.then1605E, label %if.else1605E + +if.then1605E: ; preds = %if.else1606E + %31 = load i64, i64* @_ZN7default1mE + %icmpsgt13 = icmp sgt i64 %31, 13 + br i1 %icmpsgt13, label %if.then1007E, label %if.else1007E + +if.then1007E: ; preds = %if.then1605E + %32 = load i64, i64* @_ZN7default1lE + %icmpsgt14 = icmp sgt i64 %32, 13 + br i1 %icmpsgt14, label %if.then998E, label %if.else998E + +if.then998E: ; preds = %if.then1007E + %33 = load i64, i64* @_ZN7default1kE + %icmpsgt15 = icmp sgt i64 %33, 13 + br i1 %icmpsgt15, label %if.then989E, label %if.else989E + +if.then989E: ; preds = %if.then998E + store i64 13, i64* @_ZN7default1nE + br label %if.end989E + +if.else989E: ; preds = %if.then998E + store i64 14, i64* @_ZN7default1nE + br label %if.end989E + +if.end989E: ; preds = %if.else989E, %if.then989E + br label %if.end998E + +if.else998E: ; preds = %if.then1007E + store i64 15, i64* @_ZN7default1nE + br label %if.end998E + +if.end998E: ; preds = %if.else998E, %if.end989E + br label %if.end1007E + +if.else1007E: ; preds = %if.then1605E + store i64 16, i64* @_ZN7default1nE + br label %if.end1007E + +if.end1007E: ; preds = %if.else1007E, %if.end998E + br label %if.end1605E + +if.else1605E: ; preds = %if.else1606E + %34 = load i64, i64* @_ZN7default1nE + %icmpsgt16 = icmp sgt i64 %34, 12 + br i1 %icmpsgt16, label %if.then1604E, label %if.else1604E + +if.then1604E: ; preds = %if.else1605E + %35 = load i64, i64* @_ZN7default1mE + %icmpsgt17 = icmp sgt i64 %35, 12 + br i1 %icmpsgt17, label %if.then1056E, label %if.else1056E + +if.then1056E: ; preds = %if.then1604E + %36 = load i64, i64* @_ZN7default1lE + %icmpsgt18 = icmp sgt i64 %36, 12 + br i1 %icmpsgt18, label %if.then1047E, label %if.else1047E + +if.then1047E: ; preds = %if.then1056E + %37 = load i64, i64* @_ZN7default1kE + %icmpsgt19 = icmp sgt i64 %37, 12 + br i1 %icmpsgt19, label %if.then1038E, label %if.else1038E + +if.then1038E: ; preds = %if.then1047E + store i64 12, i64* @_ZN7default1nE + br label %if.end1038E + +if.else1038E: ; preds = %if.then1047E + store i64 13, i64* @_ZN7default1nE + br label %if.end1038E + +if.end1038E: ; preds = %if.else1038E, %if.then1038E + br label %if.end1047E + +if.else1047E: ; preds = %if.then1056E + store i64 14, i64* @_ZN7default1nE + br label %if.end1047E + +if.end1047E: ; preds = %if.else1047E, %if.end1038E + br label %if.end1056E + +if.else1056E: ; preds = %if.then1604E + store i64 15, i64* @_ZN7default1nE + br label %if.end1056E + +if.end1056E: ; preds = %if.else1056E, %if.end1047E + br label %if.end1604E + +if.else1604E: ; preds = %if.else1605E + %38 = load i64, i64* @_ZN7default1nE + %icmpsgt20 = icmp sgt i64 %38, 11 + br i1 %icmpsgt20, label %if.then1603E, label %if.else1603E + +if.then1603E: ; preds = %if.else1604E + %39 = load i64, i64* @_ZN7default1mE + %icmpsgt21 = icmp sgt i64 %39, 11 + br i1 %icmpsgt21, label %if.then1105E, label %if.else1105E + +if.then1105E: ; preds = %if.then1603E + %40 = load i64, i64* @_ZN7default1lE + %icmpsgt22 = icmp sgt i64 %40, 11 + br i1 %icmpsgt22, label %if.then1096E, label %if.else1096E + +if.then1096E: ; preds = %if.then1105E + %41 = load i64, i64* @_ZN7default1kE + %icmpsgt23 = icmp sgt i64 %41, 11 + br i1 %icmpsgt23, label %if.then1087E, label %if.else1087E + +if.then1087E: ; preds = %if.then1096E + store i64 11, i64* @_ZN7default1nE + br label %if.end1087E + +if.else1087E: ; preds = %if.then1096E + store i64 12, i64* @_ZN7default1nE + br label %if.end1087E + +if.end1087E: ; preds = %if.else1087E, %if.then1087E + br label %if.end1096E + +if.else1096E: ; preds = %if.then1105E + store i64 13, i64* @_ZN7default1nE + br label %if.end1096E + +if.end1096E: ; preds = %if.else1096E, %if.end1087E + br label %if.end1105E + +if.else1105E: ; preds = %if.then1603E + store i64 14, i64* @_ZN7default1nE + br label %if.end1105E + +if.end1105E: ; preds = %if.else1105E, %if.end1096E + br label %if.end1603E + +if.else1603E: ; preds = %if.else1604E + %42 = load i64, i64* @_ZN7default1nE + %icmpsgt24 = icmp sgt i64 %42, 10 + br i1 %icmpsgt24, label %if.then1602E, label %if.else1602E + +if.then1602E: ; preds = %if.else1603E + %43 = load i64, i64* @_ZN7default1mE + %icmpsgt25 = icmp sgt i64 %43, 10 + br i1 %icmpsgt25, label %if.then1154E, label %if.else1154E + +if.then1154E: ; preds = %if.then1602E + %44 = load i64, i64* @_ZN7default1lE + %icmpsgt26 = icmp sgt i64 %44, 10 + br i1 %icmpsgt26, label %if.then1145E, label %if.else1145E + +if.then1145E: ; preds = %if.then1154E + %45 = load i64, i64* @_ZN7default1kE + %icmpsgt27 = icmp sgt i64 %45, 10 + br i1 %icmpsgt27, label %if.then1136E, label %if.else1136E + +if.then1136E: ; preds = %if.then1145E + store i64 10, i64* @_ZN7default1nE + br label %if.end1136E + +if.else1136E: ; preds = %if.then1145E + store i64 11, i64* @_ZN7default1nE + br label %if.end1136E + +if.end1136E: ; preds = %if.else1136E, %if.then1136E + br label %if.end1145E + +if.else1145E: ; preds = %if.then1154E + store i64 12, i64* @_ZN7default1nE + br label %if.end1145E + +if.end1145E: ; preds = %if.else1145E, %if.end1136E + br label %if.end1154E + +if.else1154E: ; preds = %if.then1602E + store i64 13, i64* @_ZN7default1nE + br label %if.end1154E + +if.end1154E: ; preds = %if.else1154E, %if.end1145E + br label %if.end1602E + +if.else1602E: ; preds = %if.else1603E + %46 = load i64, i64* @_ZN7default1nE + %icmpsgt28 = icmp sgt i64 %46, 9 + br i1 %icmpsgt28, label %if.then1601E, label %if.else1601E + +if.then1601E: ; preds = %if.else1602E + %47 = load i64, i64* @_ZN7default1mE + %icmpsgt29 = icmp sgt i64 %47, 9 + br i1 %icmpsgt29, label %if.then1203E, label %if.else1203E + +if.then1203E: ; preds = %if.then1601E + %48 = load i64, i64* @_ZN7default1lE + %icmpsgt30 = icmp sgt i64 %48, 9 + br i1 %icmpsgt30, label %if.then1194E, label %if.else1194E + +if.then1194E: ; preds = %if.then1203E + %49 = load i64, i64* @_ZN7default1kE + %icmpsgt31 = icmp sgt i64 %49, 9 + br i1 %icmpsgt31, label %if.then1185E, label %if.else1185E + +if.then1185E: ; preds = %if.then1194E + store i64 9, i64* @_ZN7default1nE + br label %if.end1185E + +if.else1185E: ; preds = %if.then1194E + store i64 10, i64* @_ZN7default1nE + br label %if.end1185E + +if.end1185E: ; preds = %if.else1185E, %if.then1185E + br label %if.end1194E + +if.else1194E: ; preds = %if.then1203E + store i64 11, i64* @_ZN7default1nE + br label %if.end1194E + +if.end1194E: ; preds = %if.else1194E, %if.end1185E + br label %if.end1203E + +if.else1203E: ; preds = %if.then1601E + store i64 12, i64* @_ZN7default1nE + br label %if.end1203E + +if.end1203E: ; preds = %if.else1203E, %if.end1194E + br label %if.end1601E + +if.else1601E: ; preds = %if.else1602E + %50 = load i64, i64* @_ZN7default1nE + %icmpsgt32 = icmp sgt i64 %50, 8 + br i1 %icmpsgt32, label %if.then1600E, label %if.else1600E + +if.then1600E: ; preds = %if.else1601E + %51 = load i64, i64* @_ZN7default1mE + %icmpsgt33 = icmp sgt i64 %51, 8 + br i1 %icmpsgt33, label %if.then1252E, label %if.else1252E + +if.then1252E: ; preds = %if.then1600E + %52 = load i64, i64* @_ZN7default1lE + %icmpsgt34 = icmp sgt i64 %52, 8 + br i1 %icmpsgt34, label %if.then1243E, label %if.else1243E + +if.then1243E: ; preds = %if.then1252E + %53 = load i64, i64* @_ZN7default1kE + %icmpsgt35 = icmp sgt i64 %53, 8 + br i1 %icmpsgt35, label %if.then1234E, label %if.else1234E + +if.then1234E: ; preds = %if.then1243E + store i64 8, i64* @_ZN7default1nE + br label %if.end1234E + +if.else1234E: ; preds = %if.then1243E + store i64 9, i64* @_ZN7default1nE + br label %if.end1234E + +if.end1234E: ; preds = %if.else1234E, %if.then1234E + br label %if.end1243E + +if.else1243E: ; preds = %if.then1252E + store i64 10, i64* @_ZN7default1nE + br label %if.end1243E + +if.end1243E: ; preds = %if.else1243E, %if.end1234E + br label %if.end1252E + +if.else1252E: ; preds = %if.then1600E + store i64 11, i64* @_ZN7default1nE + br label %if.end1252E + +if.end1252E: ; preds = %if.else1252E, %if.end1243E + br label %if.end1600E + +if.else1600E: ; preds = %if.else1601E + %54 = load i64, i64* @_ZN7default1nE + %icmpsgt36 = icmp sgt i64 %54, 7 + br i1 %icmpsgt36, label %if.then1599E, label %if.else1599E + +if.then1599E: ; preds = %if.else1600E + %55 = load i64, i64* @_ZN7default1mE + %icmpsgt37 = icmp sgt i64 %55, 7 + br i1 %icmpsgt37, label %if.then1301E, label %if.else1301E + +if.then1301E: ; preds = %if.then1599E + %56 = load i64, i64* @_ZN7default1lE + %icmpsgt38 = icmp sgt i64 %56, 7 + br i1 %icmpsgt38, label %if.then1292E, label %if.else1292E + +if.then1292E: ; preds = %if.then1301E + %57 = load i64, i64* @_ZN7default1kE + %icmpsgt39 = icmp sgt i64 %57, 7 + br i1 %icmpsgt39, label %if.then1283E, label %if.else1283E + +if.then1283E: ; preds = %if.then1292E + store i64 7, i64* @_ZN7default1nE + br label %if.end1283E + +if.else1283E: ; preds = %if.then1292E + store i64 8, i64* @_ZN7default1nE + br label %if.end1283E + +if.end1283E: ; preds = %if.else1283E, %if.then1283E + br label %if.end1292E + +if.else1292E: ; preds = %if.then1301E + store i64 9, i64* @_ZN7default1nE + br label %if.end1292E + +if.end1292E: ; preds = %if.else1292E, %if.end1283E + br label %if.end1301E + +if.else1301E: ; preds = %if.then1599E + store i64 10, i64* @_ZN7default1nE + br label %if.end1301E + +if.end1301E: ; preds = %if.else1301E, %if.end1292E + br label %if.end1599E + +if.else1599E: ; preds = %if.else1600E + %58 = load i64, i64* @_ZN7default1nE + %icmpsgt40 = icmp sgt i64 %58, 6 + br i1 %icmpsgt40, label %if.then1598E, label %if.else1598E + +if.then1598E: ; preds = %if.else1599E + %59 = load i64, i64* @_ZN7default1mE + %icmpsgt41 = icmp sgt i64 %59, 6 + br i1 %icmpsgt41, label %if.then1350E, label %if.else1350E + +if.then1350E: ; preds = %if.then1598E + %60 = load i64, i64* @_ZN7default1lE + %icmpsgt42 = icmp sgt i64 %60, 6 + br i1 %icmpsgt42, label %if.then1341E, label %if.else1341E + +if.then1341E: ; preds = %if.then1350E + %61 = load i64, i64* @_ZN7default1kE + %icmpsgt43 = icmp sgt i64 %61, 6 + br i1 %icmpsgt43, label %if.then1332E, label %if.else1332E + +if.then1332E: ; preds = %if.then1341E + store i64 6, i64* @_ZN7default1nE + br label %if.end1332E + +if.else1332E: ; preds = %if.then1341E + store i64 7, i64* @_ZN7default1nE + br label %if.end1332E + +if.end1332E: ; preds = %if.else1332E, %if.then1332E + br label %if.end1341E + +if.else1341E: ; preds = %if.then1350E + store i64 8, i64* @_ZN7default1nE + br label %if.end1341E + +if.end1341E: ; preds = %if.else1341E, %if.end1332E + br label %if.end1350E + +if.else1350E: ; preds = %if.then1598E + store i64 9, i64* @_ZN7default1nE + br label %if.end1350E + +if.end1350E: ; preds = %if.else1350E, %if.end1341E + br label %if.end1598E + +if.else1598E: ; preds = %if.else1599E + %62 = load i64, i64* @_ZN7default1nE + %icmpsgt44 = icmp sgt i64 %62, 5 + br i1 %icmpsgt44, label %if.then1597E, label %if.else1597E + +if.then1597E: ; preds = %if.else1598E + %63 = load i64, i64* @_ZN7default1mE + %icmpsgt45 = icmp sgt i64 %63, 5 + br i1 %icmpsgt45, label %if.then1399E, label %if.else1399E + +if.then1399E: ; preds = %if.then1597E + %64 = load i64, i64* @_ZN7default1lE + %icmpsgt46 = icmp sgt i64 %64, 5 + br i1 %icmpsgt46, label %if.then1390E, label %if.else1390E + +if.then1390E: ; preds = %if.then1399E + %65 = load i64, i64* @_ZN7default1kE + %icmpsgt47 = icmp sgt i64 %65, 5 + br i1 %icmpsgt47, label %if.then1381E, label %if.else1381E + +if.then1381E: ; preds = %if.then1390E + store i64 5, i64* @_ZN7default1nE + br label %if.end1381E + +if.else1381E: ; preds = %if.then1390E + store i64 6, i64* @_ZN7default1nE + br label %if.end1381E + +if.end1381E: ; preds = %if.else1381E, %if.then1381E + br label %if.end1390E + +if.else1390E: ; preds = %if.then1399E + store i64 7, i64* @_ZN7default1nE + br label %if.end1390E + +if.end1390E: ; preds = %if.else1390E, %if.end1381E + br label %if.end1399E + +if.else1399E: ; preds = %if.then1597E + store i64 8, i64* @_ZN7default1nE + br label %if.end1399E + +if.end1399E: ; preds = %if.else1399E, %if.end1390E + br label %if.end1597E + +if.else1597E: ; preds = %if.else1598E + %66 = load i64, i64* @_ZN7default1nE + %icmpsgt48 = icmp sgt i64 %66, 4 + br i1 %icmpsgt48, label %if.then1596E, label %if.else1596E + +if.then1596E: ; preds = %if.else1597E + %67 = load i64, i64* @_ZN7default1mE + %icmpsgt49 = icmp sgt i64 %67, 4 + br i1 %icmpsgt49, label %if.then1448E, label %if.else1448E + +if.then1448E: ; preds = %if.then1596E + %68 = load i64, i64* @_ZN7default1lE + %icmpsgt50 = icmp sgt i64 %68, 4 + br i1 %icmpsgt50, label %if.then1439E, label %if.else1439E + +if.then1439E: ; preds = %if.then1448E + %69 = load i64, i64* @_ZN7default1kE + %icmpsgt51 = icmp sgt i64 %69, 4 + br i1 %icmpsgt51, label %if.then1430E, label %if.else1430E + +if.then1430E: ; preds = %if.then1439E + store i64 4, i64* @_ZN7default1nE + br label %if.end1430E + +if.else1430E: ; preds = %if.then1439E + store i64 5, i64* @_ZN7default1nE + br label %if.end1430E + +if.end1430E: ; preds = %if.else1430E, %if.then1430E + br label %if.end1439E + +if.else1439E: ; preds = %if.then1448E + store i64 6, i64* @_ZN7default1nE + br label %if.end1439E + +if.end1439E: ; preds = %if.else1439E, %if.end1430E + br label %if.end1448E + +if.else1448E: ; preds = %if.then1596E + store i64 7, i64* @_ZN7default1nE + br label %if.end1448E + +if.end1448E: ; preds = %if.else1448E, %if.end1439E + br label %if.end1596E + +if.else1596E: ; preds = %if.else1597E + %70 = load i64, i64* @_ZN7default1nE + %icmpsgt52 = icmp sgt i64 %70, 3 + br i1 %icmpsgt52, label %if.then1595E, label %if.else1595E + +if.then1595E: ; preds = %if.else1596E + %71 = load i64, i64* @_ZN7default1mE + %icmpsgt53 = icmp sgt i64 %71, 3 + br i1 %icmpsgt53, label %if.then1497E, label %if.else1497E + +if.then1497E: ; preds = %if.then1595E + %72 = load i64, i64* @_ZN7default1lE + %icmpsgt54 = icmp sgt i64 %72, 3 + br i1 %icmpsgt54, label %if.then1488E, label %if.else1488E + +if.then1488E: ; preds = %if.then1497E + %73 = load i64, i64* @_ZN7default1kE + %icmpsgt55 = icmp sgt i64 %73, 3 + br i1 %icmpsgt55, label %if.then1479E, label %if.else1479E + +if.then1479E: ; preds = %if.then1488E + store i64 3, i64* @_ZN7default1nE + br label %if.end1479E + +if.else1479E: ; preds = %if.then1488E + store i64 4, i64* @_ZN7default1nE + br label %if.end1479E + +if.end1479E: ; preds = %if.else1479E, %if.then1479E + br label %if.end1488E + +if.else1488E: ; preds = %if.then1497E + store i64 5, i64* @_ZN7default1nE + br label %if.end1488E + +if.end1488E: ; preds = %if.else1488E, %if.end1479E + br label %if.end1497E + +if.else1497E: ; preds = %if.then1595E + store i64 6, i64* @_ZN7default1nE + br label %if.end1497E + +if.end1497E: ; preds = %if.else1497E, %if.end1488E + br label %if.end1595E + +if.else1595E: ; preds = %if.else1596E + %74 = load i64, i64* @_ZN7default1nE + %icmpsgt56 = icmp sgt i64 %74, 2 + br i1 %icmpsgt56, label %if.then1594E, label %if.else1594E + +if.then1594E: ; preds = %if.else1595E + %75 = load i64, i64* @_ZN7default1mE + %icmpsgt57 = icmp sgt i64 %75, 2 + br i1 %icmpsgt57, label %if.then1546E, label %if.else1546E + +if.then1546E: ; preds = %if.then1594E + %76 = load i64, i64* @_ZN7default1lE + %icmpsgt58 = icmp sgt i64 %76, 2 + br i1 %icmpsgt58, label %if.then1537E, label %if.else1537E + +if.then1537E: ; preds = %if.then1546E + %77 = load i64, i64* @_ZN7default1kE + %icmpsgt59 = icmp sgt i64 %77, 2 + br i1 %icmpsgt59, label %if.then1528E, label %if.else1528E + +if.then1528E: ; preds = %if.then1537E + store i64 2, i64* @_ZN7default1nE + br label %if.end1528E + +if.else1528E: ; preds = %if.then1537E + store i64 3, i64* @_ZN7default1nE + br label %if.end1528E + +if.end1528E: ; preds = %if.else1528E, %if.then1528E + br label %if.end1537E + +if.else1537E: ; preds = %if.then1546E + store i64 4, i64* @_ZN7default1nE + br label %if.end1537E + +if.end1537E: ; preds = %if.else1537E, %if.end1528E + br label %if.end1546E + +if.else1546E: ; preds = %if.then1594E + store i64 5, i64* @_ZN7default1nE + br label %if.end1546E + +if.end1546E: ; preds = %if.else1546E, %if.end1537E + br label %if.end1594E + +if.else1594E: ; preds = %if.else1595E + %78 = load i64, i64* @_ZN7default1mE + %icmpsgt60 = icmp sgt i64 %78, 1 + br i1 %icmpsgt60, label %if.then1591E, label %if.else1591E + +if.then1591E: ; preds = %if.else1594E + %79 = load i64, i64* @_ZN7default1lE + %icmpsgt61 = icmp sgt i64 %79, 1 + br i1 %icmpsgt61, label %if.then1582E, label %if.else1582E + +if.then1582E: ; preds = %if.then1591E + %80 = load i64, i64* @_ZN7default1kE + %icmpsgt62 = icmp sgt i64 %80, 1 + br i1 %icmpsgt62, label %if.then1573E, label %if.else1573E + +if.then1573E: ; preds = %if.then1582E + store i64 1, i64* @_ZN7default1nE + br label %if.end1573E + +if.else1573E: ; preds = %if.then1582E + store i64 2, i64* @_ZN7default1nE + br label %if.end1573E + +if.end1573E: ; preds = %if.else1573E, %if.then1573E + br label %if.end1582E + +if.else1582E: ; preds = %if.then1591E + store i64 3, i64* @_ZN7default1nE + br label %if.end1582E + +if.end1582E: ; preds = %if.else1582E, %if.end1573E + br label %if.end1591E + +if.else1591E: ; preds = %if.else1594E + store i64 4, i64* @_ZN7default1nE + br label %if.end1591E + +if.end1591E: ; preds = %if.else1591E, %if.end1582E + br label %if.end1594E + +if.end1594E: ; preds = %if.end1591E, %if.end1546E + br label %if.end1595E + +if.end1595E: ; preds = %if.end1594E, %if.end1497E + br label %if.end1596E + +if.end1596E: ; preds = %if.end1595E, %if.end1448E + br label %if.end1597E + +if.end1597E: ; preds = %if.end1596E, %if.end1399E + br label %if.end1598E + +if.end1598E: ; preds = %if.end1597E, %if.end1350E + br label %if.end1599E + +if.end1599E: ; preds = %if.end1598E, %if.end1301E + br label %if.end1600E + +if.end1600E: ; preds = %if.end1599E, %if.end1252E + br label %if.end1601E + +if.end1601E: ; preds = %if.end1600E, %if.end1203E + br label %if.end1602E + +if.end1602E: ; preds = %if.end1601E, %if.end1154E + br label %if.end1603E + +if.end1603E: ; preds = %if.end1602E, %if.end1105E + br label %if.end1604E + +if.end1604E: ; preds = %if.end1603E, %if.end1056E + br label %if.end1605E + +if.end1605E: ; preds = %if.end1604E, %if.end1007E + br label %if.end1606E + +if.end1606E: ; preds = %if.end1605E, %if.end958E + br label %if.end1607E + +if.end1607E: ; preds = %if.end1606E, %if.end909E + br label %if.end1608E + +if.end1608E: ; preds = %if.end1607E, %if.end860E + br label %block.body797E + +block.end797E: ; preds = %block.body797E + ret void +} + +define internal void @_ZN7default21timeBenchmarkIfElseD4Ev(%Unit.Type* noalias sret(%Unit.Type) %0) gc "cangjie" { +entry: + %callRet17 = alloca %Unit.Type + %enumTmp18 = alloca %"record._ZN11std$FS$core6StringE" + %enumTmp16 = alloca %"record._ZN11std$FS$core6StringE" + %enumTmp15 = alloca %"record._ZN11std$FS$core6StringE" + %callRet14 = alloca %"record._ZN11std$FS$core6StringE" + %callRet13 = alloca %"record._ZN11std$FS$core6StringE" + %enumTmp12 = alloca %"record._ZN11std$FS$core6StringE" + %enumTmp11 = alloca %"record._ZN11std$FS$core6StringE" + %callRet10 = alloca %"record._ZN11std$FS$core6StringE" + %enumTmp9 = alloca %"record._ZN11std$FS$core6StringE" + %callRet8 = alloca %"record._ZN11std$FS$core6StringE" + %callRet7 = alloca %"record._ZN11std$FS$core6StringE" + %callRet6 = alloca %"record._ZN11std$FS$core6StringE" + %perTime = alloca double + %enumTmp4 = alloca %"record._ZN11std$FS$time8DateTimeE" + %enumTmp = alloca %"record._ZN11std$FS$time8DateTimeE" + %callRet3 = alloca %"record._ZN11std$FS$time8DurationE" + %endTime = alloca %"record._ZN11std$FS$time8DateTimeE" + %callRet2 = alloca %"record._ZN11std$FS$time8DateTimeE" + %startTime = alloca %"record._ZN11std$FS$time8DateTimeE" + %callRet = alloca %"record._ZN11std$FS$time8DateTimeE" + %1 = bitcast %"record._ZN11std$FS$core6StringE"* %enumTmp18 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %1, i8 0, i64 16, i1 false) + %2 = bitcast %"record._ZN11std$FS$core6StringE"* %enumTmp16 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %2, i8 0, i64 16, i1 false) + %3 = bitcast %"record._ZN11std$FS$core6StringE"* %enumTmp15 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %3, i8 0, i64 16, i1 false) + %4 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet14 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %4, i8 0, i64 16, i1 false) + %5 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet13 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %5, i8 0, i64 16, i1 false) + %6 = bitcast %"record._ZN11std$FS$core6StringE"* %enumTmp12 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %6, i8 0, i64 16, i1 false) + %7 = bitcast %"record._ZN11std$FS$core6StringE"* %enumTmp11 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %7, i8 0, i64 16, i1 false) + %8 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet10 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %8, i8 0, i64 16, i1 false) + %9 = bitcast %"record._ZN11std$FS$core6StringE"* %enumTmp9 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %9, i8 0, i64 16, i1 false) + %10 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet8 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %10, i8 0, i64 16, i1 false) + %11 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet7 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %11, i8 0, i64 16, i1 false) + %12 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet6 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %12, i8 0, i64 16, i1 false) + %13 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %enumTmp4 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %13, i8 0, i64 40, i1 false) + %14 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %enumTmp to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %14, i8 0, i64 40, i1 false) + %15 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %endTime to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %15, i8 0, i64 40, i1 false) + %16 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %callRet2 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %16, i8 0, i64 40, i1 false) + %17 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %startTime to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %17, i8 0, i64 40, i1 false) + %18 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %callRet to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %18, i8 0, i64 40, i1 false) + br label %thunk1624E + +thunk1624E: ; preds = %entry + %19 = call i8 addrspace(1)* @"_ZN11std$FS$time8DateTime3nowE$timeZone___0v"() + call void @"_ZN11std$FS$time8DateTime3nowEC_ZN11std$FS$time8TimeZoneE"(%"record._ZN11std$FS$time8DateTimeE"* noalias sret(%"record._ZN11std$FS$time8DateTimeE") %callRet, i8 addrspace(1)* %19) + %20 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %startTime to i8* + %21 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %callRet to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %20, i8* align 8 %21, i64 40, i1 false) + call void @_ZN7default17benchmarkIfElseD4Ev(%Unit.Type* noalias sret(%Unit.Type) %callRet17) + %22 = call i8 addrspace(1)* @"_ZN11std$FS$time8DateTime3nowE$timeZone___0v"() + call void @"_ZN11std$FS$time8DateTime3nowEC_ZN11std$FS$time8TimeZoneE"(%"record._ZN11std$FS$time8DateTimeE"* noalias sret(%"record._ZN11std$FS$time8DateTimeE") %callRet2, i8 addrspace(1)* %22) + %23 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %endTime to i8* + %24 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %callRet2 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %23, i8* align 8 %24, i64 40, i1 false) + %25 = addrspacecast %"record._ZN11std$FS$time8DateTimeE"* %endTime to %"record._ZN11std$FS$time8DateTimeE" addrspace(1)* + %26 = addrspacecast %"record._ZN11std$FS$time8DateTimeE"* %startTime to %"record._ZN11std$FS$time8DateTimeE" addrspace(1)* + call void @"_ZN11std$FS$time8DateTime1-ER_ZN11std$FS$time8DateTimeE"(%"record._ZN11std$FS$time8DurationE"* noalias sret(%"record._ZN11std$FS$time8DurationE") %callRet3, i8 addrspace(1)* null, %"record._ZN11std$FS$time8DateTimeE" addrspace(1)* %25, i8 addrspace(1)* null, %"record._ZN11std$FS$time8DateTimeE" addrspace(1)* %26) + %27 = addrspacecast %"record._ZN11std$FS$time8DurationE"* %callRet3 to %"record._ZN11std$FS$time8DurationE" addrspace(1)* + %28 = call i64 @"_ZN11std$FS$time8Duration13toNanosecondsEv"(i8 addrspace(1)* null, %"record._ZN11std$FS$time8DurationE" addrspace(1)* %27) + %29 = sitofp i64 %28 to double + %30 = load i64, i64* @_ZN7default4repsE + %31 = sitofp i64 %30 to double + %div = fdiv double %29, %31 + store double %div, double* %perTime + call void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE") %callRet6, i8* getelementptr inbounds ([20 x i8], [20 x i8]* @"$const_string.5", i32 0, i32 0), i64 19) + %32 = load double, double* %perTime + call void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE") %callRet7, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"$const_string.2", i32 0, i32 0), i64 2) + %33 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet7 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + call void @"_ZN13std$FS$format6Extend7Float646formatER_ZN11std$FS$core6StringE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE") %callRet8, double %32, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %33) + %34 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet6 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %35 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet8 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + call void @"_ZN11std$FS$core6String1+ER_ZN11std$FS$core6StringE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE") %callRet10, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %34, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %35) + call void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE") %callRet13, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @"$const_string.3", i32 0, i32 0), i64 6) + %36 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet10 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %37 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet13 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + call void @"_ZN11std$FS$core6String1+ER_ZN11std$FS$core6StringE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE") %callRet14, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %36, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %37) + %38 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet14 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + call void @"_ZN11std$FS$core7printlnER_ZN11std$FS$core6StringE"(%Unit.Type* noalias sret(%Unit.Type) %callRet17, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %38) + ret void +} + +define i64 @_ZN7default4mainEv() gc "cangjie" { +entry: + %callRet2 = alloca %Unit.Type + br label %thunk1670E + +thunk1670E: ; preds = %entry + call void @_ZN7default21timeBenchmarkIfElseD4Ev(%Unit.Type* noalias sret(%Unit.Type) %callRet2) + ret i64 0 +} + +define i64 @user.main() gc "cangjie" { +entry: + %retVal = alloca i64 + br label %thunk1685E + +thunk1685E: ; preds = %entry + %0 = call i64 @_ZN7default4mainEv() + store i64 %0, i64* %retVal + %1 = load i64, i64* %retVal + ret i64 %1 +} + +!llvm.module.flags = !{!1} + +!0 = !{!"l"} +!1 = !{i32 2, !"CJBC", i32 1} diff --git a/llvm/test/CJLTO/pm-cj-thinlto-defaults.ll b/llvm/test/CJLTO/pm-cj-thinlto-defaults.ll new file mode 100644 index 000000000..de9ccf1c7 --- /dev/null +++ b/llvm/test/CJLTO/pm-cj-thinlto-defaults.ll @@ -0,0 +1,1906 @@ +; XFAIL: * +; Basic test for the cangjie ThinLTO pipeline. + +; RUN: llvm-as %s -o %t +; RUN: ld.lld -shared --plugin-opt=debug-pass-manager --mllvm --cangjie-pipeline \ +; RUN: --mllvm --cj-stack-grow --plugin-opt=no-opaque-pointers %t 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O + +; CHECK-O: Running pass: VerifierPass +; CHECK-O-NEXT: Running analysis: VerifierAnalysis +; CHECK-O-NEXT: Running pass: CJRuntimeLowering +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_ThrowException +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core6String1+ER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core6String23createStringFromLiteralE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core7printlnER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime1-ER_ZN11std$FS$time8DateTimeE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime3nowE$timeZone___0v +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime3nowEC_ZN11std$FS$time8TimeZoneE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8Duration13toNanosecondsEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN13std$FS$format6Extend7Float646formatER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.cj.memset.p0i8 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.expect.i1 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.memcpy.p0i8.p0i8.i64 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.sadd.with.overflow.i64 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on rt$CreateOverflowException_msg +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on user.main +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_C2NStub +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_N2CStub +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.cj.heapmalloc.class +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_NewObjectFast +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_NewFinalizerFast +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_OnFinalizerCreated +; CHECK-O-NEXT: Running pass: Annotation2MetadataPass +; CHECK-O-NEXT: Running pass: WholeProgramDevirtPass +; CHECK-O-NEXT: Running pass: LowerTypeTestsPass +; CHECK-O-NEXT: Invalidating analysis: VerifierAnalysis +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running pass: ForceFunctionAttrsPass +; CHECK-O-NEXT: Running pass: PGOIndirectCallPromotion +; CHECK-O-NEXT: Running analysis: ProfileSummaryAnalysis +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis on user.main +; CHECK-O-NEXT: Running pass: InferFunctionAttrsPass +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_ThrowException +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core6String1+ER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core6String23createStringFromLiteralE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core7printlnER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime1-ER_ZN11std$FS$time8DateTimeE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime3nowE$timeZone___0v +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime3nowEC_ZN11std$FS$time8TimeZoneE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8Duration13toNanosecondsEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN13std$FS$format6Extend7Float646formatER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.cj.memset.p0i8 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.expect.i1 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.memcpy.p0i8.p0i8.i64 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.sadd.with.overflow.i64 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on rt$CreateOverflowException_msg +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_C2NStub +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_N2CStub +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.cj.heapmalloc.class +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_NewObjectFast +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_NewFinalizerFast +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_OnFinalizerCreated +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_GetObjClass +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_GetFuncPtrFromItab +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.cj.get.virtual.func +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.cj.get.interface.func +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running pass: CoroEarlyPass +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running pass: LowerExpectIntrinsicPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SROAPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: EarlyCSEPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: LowerExpectIntrinsicPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SROAPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: EarlyCSEPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: LowerExpectIntrinsicPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SROAPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: EarlyCSEPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LowerExpectIntrinsicPass on user.main +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on user.main +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on user.main +; CHECK-O-NEXT: Running pass: SROAPass on user.main +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running pass: EarlyCSEPass on user.main +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running pass: OpenMPOptPass +; CHECK-O-NEXT: Running pass: LowerTypeTestsPass +; CHECK-O-NEXT: Running pass: IPSCCPPass +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running pass: CalledValuePropagationPass +; CHECK-O-NEXT: Running pass: GlobalOptPass +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_ThrowException +; CHECK-O-NEXT: Running pass: PromotePass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: PromotePass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: PromotePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: PromotePass on user.main +; CHECK-O-NEXT: Running pass: DeadArgumentEliminationPass +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: ScopedNoAliasAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TypeBasedAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: ScopedNoAliasAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: TypeBasedAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: ScopedNoAliasAA on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TypeBasedAA on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Running analysis: AssumptionAnalysis on user.main +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on user.main +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on user.main +; CHECK-O-NEXT: Running analysis: OptimizationRemarkEmitterAnalysis on user.main +; CHECK-O-NEXT: Running analysis: AAManager on user.main +; CHECK-O-NEXT: Running analysis: BasicAA on user.main +; CHECK-O-NEXT: Running analysis: ScopedNoAliasAA on user.main +; CHECK-O-NEXT: Running analysis: TypeBasedAA on user.main +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: BasicAA on user.main +; CHECK-O-NEXT: Invalidating analysis: AAManager on user.main +; CHECK-O-NEXT: Running pass: ModuleInlinerWrapperPass +; CHECK-O-NEXT: Running analysis: InlineAdvisorAnalysis +; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}Module +; CHECK-O-NEXT: Running analysis: GlobalsAA +; CHECK-O-NEXT: Running analysis: CallGraphAnalysis +; CHECK-O-NEXT: Running pass: InvalidateAnalysisPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: InvalidateAnalysisPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: InvalidateAnalysisPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InvalidateAnalysisPass on user.main +; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}Module +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (_ZN7default17benchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: DevirtSCCRepeatedPass on (_ZN7default17benchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running pass: InlinerPass on (_ZN7default17benchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running pass: InlinerPass on (_ZN7default17benchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass on (_ZN7default17benchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: OpenMPOptCGSCCPass on (_ZN7default17benchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: PEAPass on (_ZN7default17benchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: PartialEscapeAnalysisPass on (_ZN7default17benchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SROAPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: EarlyCSEPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SpeculativeExecutionPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: JumpThreadingPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: LazyValueAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: CorrelatedValuePropagationPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: LazyValueAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: LibCallsShrinkWrapPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: TailCallElimPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: ReassociatePass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopRotatePass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: IndVarSimplifyPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopDeletionPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopInstSimplifyPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopSimplifyCFGPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LICMPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopRotatePass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LICMPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: SimpleLoopUnswitchPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Clearing all analysis results for: +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopIdiomRecognizePass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: IndVarSimplifyPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopDeletionPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopFullUnrollPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: SROAPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Clearing all analysis results for: +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: MergedLoadStoreMotionPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: GVNPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: MemoryDependenceAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: PhiValuesAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: PhiValuesAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: MemoryDependenceAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SCCPPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: BDCEPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DemandedBitsAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: DemandedBitsAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: JumpThreadingPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: LazyValueAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: CorrelatedValuePropagationPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: LazyValueAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: ADCEPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: MemCpyOptPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: DSEPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LICMPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: CoroElidePass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: PostDominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: ShouldNotRunFunctionPassesAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: CoroSplitPass on (_ZN7default17benchmarkIfElseD4Ev) +; CHECK-O-NEXT: Invalidating analysis: ShouldNotRunFunctionPassesAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (_ZN7default21timeBenchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: DevirtSCCRepeatedPass on (_ZN7default21timeBenchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running pass: InlinerPass on (_ZN7default21timeBenchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running pass: InlinerPass on (_ZN7default21timeBenchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: PostDominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BranchProbabilityAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BlockFrequencyAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Clearing all analysis results for: _ZN7default17benchmarkIfElseD4Ev +; CHECK-O-NEXT: Clearing all analysis results for: (_ZN7default17benchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass on (_ZN7default21timeBenchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: OpenMPOptCGSCCPass on (_ZN7default21timeBenchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: PEAPass on (_ZN7default21timeBenchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: PartialEscapeAnalysisPass on (_ZN7default21timeBenchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SROAPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: EarlyCSEPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SpeculativeExecutionPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: JumpThreadingPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: LazyValueAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: CorrelatedValuePropagationPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: LazyValueAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: LibCallsShrinkWrapPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: TailCallElimPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: ReassociatePass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopRotatePass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: IndVarSimplifyPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopDeletionPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopInstSimplifyPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopSimplifyCFGPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LICMPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopRotatePass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LICMPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: SimpleLoopUnswitchPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Clearing all analysis results for: +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopIdiomRecognizePass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: IndVarSimplifyPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopDeletionPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopFullUnrollPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: SROAPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Clearing all analysis results for: +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: MergedLoadStoreMotionPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: GVNPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: MemoryDependenceAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: PhiValuesAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: PhiValuesAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: MemoryDependenceAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SCCPPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: BDCEPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DemandedBitsAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: DemandedBitsAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: JumpThreadingPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: LazyValueAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: CorrelatedValuePropagationPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: LazyValueAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: ADCEPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: MemCpyOptPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: DSEPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LICMPass on Loop at depth 1 +; CHECK-O-NEXT: Invalidating analysis: PostDominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: CoroElidePass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: ShouldNotRunFunctionPassesAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running pass: CoroSplitPass on (_ZN7default21timeBenchmarkIfElseD4Ev) +; CHECK-O-NEXT: Invalidating analysis: ShouldNotRunFunctionPassesAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: DevirtSCCRepeatedPass on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running pass: InlinerPass on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running pass: InlinerPass on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BranchProbabilityAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BlockFrequencyAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Clearing all analysis results for: _ZN7default21timeBenchmarkIfElseD4Ev +; CHECK-O-NEXT: Clearing all analysis results for: (_ZN7default21timeBenchmarkIfElseD4Ev) +; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: OpenMPOptCGSCCPass on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: PEAPass on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: PartialEscapeAnalysisPass on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SROAPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: EarlyCSEPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SpeculativeExecutionPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: JumpThreadingPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LazyValueAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: CorrelatedValuePropagationPass on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LazyValueAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LibCallsShrinkWrapPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: TailCallElimPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: ReassociatePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopRotatePass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: IndVarSimplifyPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopDeletionPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopInstSimplifyPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopSimplifyCFGPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LICMPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopRotatePass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LICMPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: SimpleLoopUnswitchPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Clearing all analysis results for: +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopIdiomRecognizePass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: IndVarSimplifyPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopDeletionPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopFullUnrollPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: SROAPass on _ZN7default4mainEv +; CHECK-O-NEXT: Clearing all analysis results for: +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: MergedLoadStoreMotionPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: GVNPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: MemoryDependenceAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PhiValuesAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: PhiValuesAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: MemoryDependenceAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SCCPPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: BDCEPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DemandedBitsAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: DemandedBitsAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: JumpThreadingPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LazyValueAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: CorrelatedValuePropagationPass on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LazyValueAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: ADCEPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: MemCpyOptPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: DSEPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LICMPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: CoroElidePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: ShouldNotRunFunctionPassesAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: CoroSplitPass on (_ZN7default4mainEv) +; CHECK-O-NEXT: Invalidating analysis: ShouldNotRunFunctionPassesAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (user.main) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: DevirtSCCRepeatedPass on (user.main) +; CHECK-O-NEXT: Running pass: InlinerPass on (user.main) +; CHECK-O-NEXT: Running pass: InlinerPass on (user.main) +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on user.main +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on user.main +; CHECK-O-NEXT: Running analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass on (user.main) +; CHECK-O-NEXT: Running analysis: AAManager on user.main +; CHECK-O-NEXT: Running analysis: BasicAA on user.main +; CHECK-O-NEXT: Running pass: OpenMPOptCGSCCPass on (user.main) +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Running pass: PEAPass on (user.main) +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Running pass: PartialEscapeAnalysisPass on (user.main) +; CHECK-O-NEXT: Running pass: SROAPass on user.main +; CHECK-O-NEXT: Running pass: EarlyCSEPass on user.main +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on user.main +; CHECK-O-NEXT: Running pass: SpeculativeExecutionPass on user.main +; CHECK-O-NEXT: Running pass: JumpThreadingPass on user.main +; CHECK-O-NEXT: Running analysis: LazyValueAnalysis on user.main +; CHECK-O-NEXT: Running pass: CorrelatedValuePropagationPass on user.main +; CHECK-O-NEXT: Invalidating analysis: LazyValueAnalysis on user.main +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Running pass: LibCallsShrinkWrapPass on user.main +; CHECK-O-NEXT: Running pass: TailCallElimPass on user.main +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: BranchProbabilityAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: BlockFrequencyAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on user.main +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Running pass: ReassociatePass on user.main +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on user.main +; CHECK-O-NEXT: Running analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Running pass: LCSSAPass on user.main +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on user.main +; CHECK-O-NEXT: Running pass: LCSSAPass on user.main +; CHECK-O-NEXT: Running pass: SROAPass on user.main +; CHECK-O-NEXT: Running pass: MergedLoadStoreMotionPass on user.main +; CHECK-O-NEXT: Running pass: GVNPass on user.main +; CHECK-O-NEXT: Running analysis: MemoryDependenceAnalysis on user.main +; CHECK-O-NEXT: Running analysis: PhiValuesAnalysis on user.main +; CHECK-O-NEXT: Running pass: SCCPPass on user.main +; CHECK-O-NEXT: Running pass: BDCEPass on user.main +; CHECK-O-NEXT: Running analysis: DemandedBitsAnalysis on user.main +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Running pass: JumpThreadingPass on user.main +; CHECK-O-NEXT: Running analysis: LazyValueAnalysis on user.main +; CHECK-O-NEXT: Running pass: CorrelatedValuePropagationPass on user.main +; CHECK-O-NEXT: Invalidating analysis: LazyValueAnalysis on user.main +; CHECK-O-NEXT: Running pass: ADCEPass on user.main +; CHECK-O-NEXT: Running pass: MemCpyOptPass on user.main +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on user.main +; CHECK-O-NEXT: Running pass: DSEPass on user.main +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on user.main +; CHECK-O-NEXT: Running pass: LCSSAPass on user.main +; CHECK-O-NEXT: Running pass: CoroElidePass on user.main +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: PostDominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: BasicAA on user.main +; CHECK-O-NEXT: Invalidating analysis: AAManager on user.main +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: PhiValuesAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: MemoryDependenceAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: DemandedBitsAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on user.main +; CHECK-O-NEXT: Running analysis: ShouldNotRunFunctionPassesAnalysis on user.main +; CHECK-O-NEXT: Running pass: CoroSplitPass on (user.main) +; CHECK-O-NEXT: Invalidating analysis: ShouldNotRunFunctionPassesAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: CallGraphAnalysis +; CHECK-O-NEXT: Running pass: InvalidateAnalysisPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InvalidateAnalysisPass on user.main +; CHECK-O-NEXT: Invalidating analysis: InlineAdvisorAnalysis +; CHECK-O-NEXT: Running pass: CJDevirtualOpt +; CHECK-O-NEXT: Running pass: CoroCleanupPass +; CHECK-O-NEXT: Running pass: GlobalOptPass +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core6String1+ER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Invalidating analysis: LazyCallGraphAnalysis +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running pass: GlobalDCEPass +; CHECK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass +; CHECK-O-NEXT: Running analysis: CallGraphAnalysis +; CHECK-O-NEXT: Running pass: RecomputeGlobalsAAPass +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: LazyCallGraphAnalysis +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (_ZN7default4mainEv) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass on (_ZN7default4mainEv) (1 node) +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: FunctionAnalysisManagerCGSCCProxy on (user.main) +; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph +; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass on (user.main) +; CHECK-O-NEXT: Running analysis: AAManager on user.main +; CHECK-O-NEXT: Running analysis: BasicAA on user.main +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LICMPass +; CHECK-O-NEXT: Running pass: GVNPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: MemoryDependenceAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PhiValuesAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Invalidating analysis: PhiValuesAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: MemoryDependenceAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: MemCpyOptPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: DSEPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: MergedLoadStoreMotionPass on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on user.main +; CHECK-O-NEXT: Running analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Running pass: LCSSAPass on user.main +; CHECK-O-NEXT: Running pass: GVNPass on user.main +; CHECK-O-NEXT: Running analysis: MemoryDependenceAnalysis on user.main +; CHECK-O-NEXT: Running analysis: PhiValuesAnalysis on user.main +; CHECK-O-NEXT: Running pass: MemCpyOptPass on user.main +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on user.main +; CHECK-O-NEXT: Running pass: DSEPass on user.main +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running pass: MergedLoadStoreMotionPass on user.main +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: BasicAA on user.main +; CHECK-O-NEXT: Invalidating analysis: AAManager on user.main +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: PhiValuesAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: MemoryDependenceAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: PostDominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: CallGraphAnalysis +; CHECK-O-NEXT: Invalidating analysis: LazyCallGraphAnalysis +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running pass: Float2IntPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LowerConstantIntrinsicsPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopRotatePass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopDeletionPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopDistributePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InjectTLIMappings on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LoopVectorizePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DemandedBitsAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LoopLoadEliminationPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LoopAccessAnalysis on Loop at depth 1 +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Clearing all analysis results for: +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Invalidating analysis: DemandedBitsAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: VectorCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LoopUnrollPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BranchProbabilityAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BlockFrequencyAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: WarnMissedTransformationsPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InstCombinePass on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: LICMPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: AlignmentFromAssumptionsPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: LCSSAPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: IndVarSimplifyPass on Loop at depth 1 +; CHECK-O-NEXT: Running pass: LoopSinkPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: InstSimplifyPass on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: MemorySSAAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: DivRemPairsPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: TailCallElimPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BasicAA on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: AAManager on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BranchProbabilityAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Invalidating analysis: BlockFrequencyAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: Float2IntPass on user.main +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running pass: LowerConstantIntrinsicsPass on user.main +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on user.main +; CHECK-O-NEXT: Running analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Running pass: LCSSAPass on user.main +; CHECK-O-NEXT: Running pass: LoopDistributePass on user.main +; CHECK-O-NEXT: Running analysis: ScalarEvolutionAnalysis on user.main +; CHECK-O-NEXT: Running analysis: AAManager on user.main +; CHECK-O-NEXT: Running analysis: BasicAA on user.main +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: InjectTLIMappings on user.main +; CHECK-O-NEXT: Running pass: LoopVectorizePass on user.main +; CHECK-O-NEXT: Running pass: LoopLoadEliminationPass on user.main +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Running pass: VectorCombinePass on user.main +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Running pass: LoopUnrollPass on user.main +; CHECK-O-NEXT: Running pass: WarnMissedTransformationsPass on user.main +; CHECK-O-NEXT: Running pass: InstCombinePass on user.main +; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}Function +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on user.main +; CHECK-O-NEXT: Running pass: LCSSAPass on user.main +; CHECK-O-NEXT: Running pass: AlignmentFromAssumptionsPass on user.main +; CHECK-O-NEXT: Running pass: LoopSimplifyPass on user.main +; CHECK-O-NEXT: Running pass: LCSSAPass on user.main +; CHECK-O-NEXT: Running pass: LoopSinkPass on user.main +; CHECK-O-NEXT: Running pass: InstSimplifyPass on user.main +; CHECK-O-NEXT: Running pass: DivRemPairsPass on user.main +; CHECK-O-NEXT: Running pass: TailCallElimPass on user.main +; CHECK-O-NEXT: Running pass: SimplifyCFGPass on user.main +; CHECK-O-NEXT: Invalidating analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: ScalarEvolutionAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: BasicAA on user.main +; CHECK-O-NEXT: Invalidating analysis: AAManager on user.main +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Function +; CHECK-O-NEXT: Running pass: EliminateAvailableExternallyPass +; CHECK-O-NEXT: Running pass: GlobalDCEPass +; CHECK-O-NEXT: Running pass: ConstantMergePass +; CHECK-O-NEXT: Running pass: CGProfilePass +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on user.main +; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on user.main +; CHECK-O-NEXT: Running analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running pass: RelLookupTableConverterPass +; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on _ZN7default4mainEv +; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on user.main +; CHECK-O-NEXT: Running pass: CJSpecificOpt +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running pass: PlaceSafepoints +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core6String1+ER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core6String23createStringFromLiteralE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$core7printlnER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime1-ER_ZN11std$FS$time8DateTimeE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime3nowE$timeZone___0v +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8DateTime3nowEC_ZN11std$FS$time8TimeZoneE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN11std$FS$time8Duration13toNanosecondsEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN13std$FS$format6Extend7Float646formatER_ZN11std$FS$core6StringE +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.cj.memset.p0i8 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.memcpy.p0i8.p0i8.i64 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on user.main +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_C2NStub +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_N2CStub +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_NewObjectFast +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_NewFinalizerFast +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_OnFinalizerCreated +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.lifetime.start.p0i8 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.lifetime.end.p0i8 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on llvm.memset.p0i8.i64 +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on CJ_MCC_StackCheck +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on MCC_SafepointStub +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on gc.safepoint_poll +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running pass: CJBarrierOpt +; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running analysis: LoopAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: LoopAnalysis on user.main +; CHECK-O-NEXT: Running analysis: DominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running pass: CJFastBarrier +; CHECK-O-NEXT: Running pass: RewriteStatepointsForCangjieGC +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on _ZN7default4mainEv +; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on user.main +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis on user.main +; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis on user.main +; CHECK-O-NEXT: Invalidating analysis: InnerAnalysisManagerProxy<{{.*}}Module +; CHECK-O-NEXT: Running pass: VerifierPass +; CHECK-O-NEXT: Running analysis: VerifierAnalysis + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%"record._ZN11std$FS$core6StringE" = type { i8 addrspace(1)*, i64 } +%Unit.Type = type { i8 } +%"record._ZN11std$FS$time8DateTimeE" = type { i32, i32, i64, i64, i8 addrspace(1)*, i64 } +%"record._ZN11std$FS$time8DurationE" = type { i64 } + +@_ZN7default1kE = global i64 2, align 8, !GlobalVarType !0 +@_ZN7default1lE = global i64 2, align 8, !GlobalVarType !0 +@_ZN7default1mE = global i64 2, align 8, !GlobalVarType !0 +@_ZN7default1nE = global i64 2, align 8, !GlobalVarType !0 +@_ZN7default4repsE = internal constant i64 100000000, align 8, !GlobalVarType !0 +@"$const_string.0" = private unnamed_addr constant [4 x i8] c"add\00" +@"$const_string.2" = private unnamed_addr constant [3 x i8] c".3\00" +@"$const_string.3" = private unnamed_addr constant [7 x i8] c" ns/op\00" +@"$const_string.5" = private unnamed_addr constant [20 x i8] c"BenchmarkIfElseD4: \00" + +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare void @"_ZN11std$FS$core6String1+ER_ZN11std$FS$core6StringE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE"), i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) gc "cangjie" +declare void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE"), i8*, i64) gc "cangjie" +declare void @"_ZN11std$FS$core7printlnER_ZN11std$FS$core6StringE"(%Unit.Type* noalias sret(%Unit.Type), i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) gc "cangjie" +declare void @"_ZN11std$FS$time8DateTime1-ER_ZN11std$FS$time8DateTimeE"(%"record._ZN11std$FS$time8DurationE"* noalias sret(%"record._ZN11std$FS$time8DurationE"), i8 addrspace(1)*, %"record._ZN11std$FS$time8DateTimeE" addrspace(1)*, i8 addrspace(1)*, %"record._ZN11std$FS$time8DateTimeE" addrspace(1)*) gc "cangjie" +declare i8 addrspace(1)* @"_ZN11std$FS$time8DateTime3nowE$timeZone___0v"() gc "cangjie" +declare void @"_ZN11std$FS$time8DateTime3nowEC_ZN11std$FS$time8TimeZoneE"(%"record._ZN11std$FS$time8DateTimeE"* noalias sret(%"record._ZN11std$FS$time8DateTimeE"), i8 addrspace(1)*) gc "cangjie" +declare i64 @"_ZN11std$FS$time8Duration13toNanosecondsEv"(i8 addrspace(1)*, %"record._ZN11std$FS$time8DurationE" addrspace(1)*) gc "cangjie" +declare void @"_ZN13std$FS$format6Extend7Float646formatER_ZN11std$FS$core6StringE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE"), double, i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) gc "cangjie" +declare void @llvm.cj.memset.p0i8(i8*, i8, i64, i1) +declare i1 @llvm.expect.i1(i1, i1) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) +declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) +declare i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)*, %"record._ZN11std$FS$core6StringE" addrspace(1)*) gc "cangjie" + +define internal void @_ZN7default17benchmarkIfElseD4Ev(%Unit.Type* noalias sret(%Unit.Type) %0) gc "cangjie" { +entry: + %enumTmp = alloca %"record._ZN11std$FS$core6StringE" + %callRet = alloca %"record._ZN11std$FS$core6StringE" + %1 = alloca i64 + %val.ov = alloca { i64, i1 } + %i = alloca i64 + %"$stop-compiler" = alloca i64 + %"$iter-i" = alloca i64 + %2 = bitcast %"record._ZN11std$FS$core6StringE"* %enumTmp to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %2, i8 0, i64 16, i1 false) + %3 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %3, i8 0, i64 16, i1 false) + br label %thunk782E + +thunk782E: ; preds = %entry + store i64 0, i64* %"$iter-i" + %4 = load i64, i64* @_ZN7default4repsE + store i64 %4, i64* %"$stop-compiler" + br label %block.body797E + +block.body797E: ; preds = %if.end1608E, %thunk782E + %5 = load i64, i64* %"$iter-i" + %6 = load i64, i64* %"$stop-compiler" + %icmpslt = icmp slt i64 %5, %6 + br i1 %icmpslt, label %while.body1620E, label %block.end797E + +while.body1620E: ; preds = %block.body797E + %7 = load i64, i64* %"$iter-i" + store i64 %7, i64* %i + %8 = load i64, i64* %"$iter-i" + %9 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %8, i64 1) + store { i64, i1 } %9, { i64, i1 }* %val.ov + %10 = getelementptr inbounds { i64, i1 }, { i64, i1 }* %val.ov, i32 0, i32 1 + %11 = load i1, i1* %10 + %12 = call i1 @llvm.expect.i1(i1 %11, i1 false) + br i1 %12, label %overflow0E, label %normal0E + +normal0E: ; preds = %while.body1620E + %13 = getelementptr inbounds { i64, i1 }, { i64, i1 }* %val.ov, i32 0, i32 0 + %14 = load i64, i64* %13 + store i64 %14, i64* %1 + %15 = load i64, i64* %1 + store i64 %15, i64* %"$iter-i" + %16 = load i64, i64* @_ZN7default1nE + %icmpsgt = icmp sgt i64 %16, 16 + br i1 %icmpsgt, label %if.then1608E, label %if.else1608E + +overflow0E: ; preds = %while.body1620E + call void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE") %callRet, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.0", i32 0, i32 0), i64 3) + %17 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %18 = call i8 addrspace(1)* @"rt$CreateOverflowException_msg"(i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %17) + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %18) + unreachable + +if.then1608E: ; preds = %normal0E + %19 = load i64, i64* @_ZN7default1mE + %icmpsgt1 = icmp sgt i64 %19, 16 + br i1 %icmpsgt1, label %if.then860E, label %if.else860E + +if.then860E: ; preds = %if.then1608E + %20 = load i64, i64* @_ZN7default1lE + %icmpsgt2 = icmp sgt i64 %20, 16 + br i1 %icmpsgt2, label %if.then851E, label %if.else851E + +if.then851E: ; preds = %if.then860E + %21 = load i64, i64* @_ZN7default1kE + %icmpsgt3 = icmp sgt i64 %21, 16 + br i1 %icmpsgt3, label %if.then842E, label %if.else842E + +if.then842E: ; preds = %if.then851E + store i64 16, i64* @_ZN7default1nE + br label %if.end842E + +if.else842E: ; preds = %if.then851E + store i64 17, i64* @_ZN7default1nE + br label %if.end842E + +if.end842E: ; preds = %if.else842E, %if.then842E + br label %if.end851E + +if.else851E: ; preds = %if.then860E + store i64 18, i64* @_ZN7default1nE + br label %if.end851E + +if.end851E: ; preds = %if.else851E, %if.end842E + br label %if.end860E + +if.else860E: ; preds = %if.then1608E + store i64 19, i64* @_ZN7default1nE + br label %if.end860E + +if.end860E: ; preds = %if.else860E, %if.end851E + br label %if.end1608E + +if.else1608E: ; preds = %normal0E + %22 = load i64, i64* @_ZN7default1nE + %icmpsgt4 = icmp sgt i64 %22, 15 + br i1 %icmpsgt4, label %if.then1607E, label %if.else1607E + +if.then1607E: ; preds = %if.else1608E + %23 = load i64, i64* @_ZN7default1mE + %icmpsgt5 = icmp sgt i64 %23, 15 + br i1 %icmpsgt5, label %if.then909E, label %if.else909E + +if.then909E: ; preds = %if.then1607E + %24 = load i64, i64* @_ZN7default1lE + %icmpsgt6 = icmp sgt i64 %24, 15 + br i1 %icmpsgt6, label %if.then900E, label %if.else900E + +if.then900E: ; preds = %if.then909E + %25 = load i64, i64* @_ZN7default1kE + %icmpsgt7 = icmp sgt i64 %25, 15 + br i1 %icmpsgt7, label %if.then891E, label %if.else891E + +if.then891E: ; preds = %if.then900E + store i64 15, i64* @_ZN7default1nE + br label %if.end891E + +if.else891E: ; preds = %if.then900E + store i64 16, i64* @_ZN7default1nE + br label %if.end891E + +if.end891E: ; preds = %if.else891E, %if.then891E + br label %if.end900E + +if.else900E: ; preds = %if.then909E + store i64 17, i64* @_ZN7default1nE + br label %if.end900E + +if.end900E: ; preds = %if.else900E, %if.end891E + br label %if.end909E + +if.else909E: ; preds = %if.then1607E + store i64 18, i64* @_ZN7default1nE + br label %if.end909E + +if.end909E: ; preds = %if.else909E, %if.end900E + br label %if.end1607E + +if.else1607E: ; preds = %if.else1608E + %26 = load i64, i64* @_ZN7default1nE + %icmpsgt8 = icmp sgt i64 %26, 14 + br i1 %icmpsgt8, label %if.then1606E, label %if.else1606E + +if.then1606E: ; preds = %if.else1607E + %27 = load i64, i64* @_ZN7default1mE + %icmpsgt9 = icmp sgt i64 %27, 14 + br i1 %icmpsgt9, label %if.then958E, label %if.else958E + +if.then958E: ; preds = %if.then1606E + %28 = load i64, i64* @_ZN7default1lE + %icmpsgt10 = icmp sgt i64 %28, 14 + br i1 %icmpsgt10, label %if.then949E, label %if.else949E + +if.then949E: ; preds = %if.then958E + %29 = load i64, i64* @_ZN7default1kE + %icmpsgt11 = icmp sgt i64 %29, 14 + br i1 %icmpsgt11, label %if.then940E, label %if.else940E + +if.then940E: ; preds = %if.then949E + store i64 14, i64* @_ZN7default1nE + br label %if.end940E + +if.else940E: ; preds = %if.then949E + store i64 15, i64* @_ZN7default1nE + br label %if.end940E + +if.end940E: ; preds = %if.else940E, %if.then940E + br label %if.end949E + +if.else949E: ; preds = %if.then958E + store i64 16, i64* @_ZN7default1nE + br label %if.end949E + +if.end949E: ; preds = %if.else949E, %if.end940E + br label %if.end958E + +if.else958E: ; preds = %if.then1606E + store i64 17, i64* @_ZN7default1nE + br label %if.end958E + +if.end958E: ; preds = %if.else958E, %if.end949E + br label %if.end1606E + +if.else1606E: ; preds = %if.else1607E + %30 = load i64, i64* @_ZN7default1nE + %icmpsgt12 = icmp sgt i64 %30, 13 + br i1 %icmpsgt12, label %if.then1605E, label %if.else1605E + +if.then1605E: ; preds = %if.else1606E + %31 = load i64, i64* @_ZN7default1mE + %icmpsgt13 = icmp sgt i64 %31, 13 + br i1 %icmpsgt13, label %if.then1007E, label %if.else1007E + +if.then1007E: ; preds = %if.then1605E + %32 = load i64, i64* @_ZN7default1lE + %icmpsgt14 = icmp sgt i64 %32, 13 + br i1 %icmpsgt14, label %if.then998E, label %if.else998E + +if.then998E: ; preds = %if.then1007E + %33 = load i64, i64* @_ZN7default1kE + %icmpsgt15 = icmp sgt i64 %33, 13 + br i1 %icmpsgt15, label %if.then989E, label %if.else989E + +if.then989E: ; preds = %if.then998E + store i64 13, i64* @_ZN7default1nE + br label %if.end989E + +if.else989E: ; preds = %if.then998E + store i64 14, i64* @_ZN7default1nE + br label %if.end989E + +if.end989E: ; preds = %if.else989E, %if.then989E + br label %if.end998E + +if.else998E: ; preds = %if.then1007E + store i64 15, i64* @_ZN7default1nE + br label %if.end998E + +if.end998E: ; preds = %if.else998E, %if.end989E + br label %if.end1007E + +if.else1007E: ; preds = %if.then1605E + store i64 16, i64* @_ZN7default1nE + br label %if.end1007E + +if.end1007E: ; preds = %if.else1007E, %if.end998E + br label %if.end1605E + +if.else1605E: ; preds = %if.else1606E + %34 = load i64, i64* @_ZN7default1nE + %icmpsgt16 = icmp sgt i64 %34, 12 + br i1 %icmpsgt16, label %if.then1604E, label %if.else1604E + +if.then1604E: ; preds = %if.else1605E + %35 = load i64, i64* @_ZN7default1mE + %icmpsgt17 = icmp sgt i64 %35, 12 + br i1 %icmpsgt17, label %if.then1056E, label %if.else1056E + +if.then1056E: ; preds = %if.then1604E + %36 = load i64, i64* @_ZN7default1lE + %icmpsgt18 = icmp sgt i64 %36, 12 + br i1 %icmpsgt18, label %if.then1047E, label %if.else1047E + +if.then1047E: ; preds = %if.then1056E + %37 = load i64, i64* @_ZN7default1kE + %icmpsgt19 = icmp sgt i64 %37, 12 + br i1 %icmpsgt19, label %if.then1038E, label %if.else1038E + +if.then1038E: ; preds = %if.then1047E + store i64 12, i64* @_ZN7default1nE + br label %if.end1038E + +if.else1038E: ; preds = %if.then1047E + store i64 13, i64* @_ZN7default1nE + br label %if.end1038E + +if.end1038E: ; preds = %if.else1038E, %if.then1038E + br label %if.end1047E + +if.else1047E: ; preds = %if.then1056E + store i64 14, i64* @_ZN7default1nE + br label %if.end1047E + +if.end1047E: ; preds = %if.else1047E, %if.end1038E + br label %if.end1056E + +if.else1056E: ; preds = %if.then1604E + store i64 15, i64* @_ZN7default1nE + br label %if.end1056E + +if.end1056E: ; preds = %if.else1056E, %if.end1047E + br label %if.end1604E + +if.else1604E: ; preds = %if.else1605E + %38 = load i64, i64* @_ZN7default1nE + %icmpsgt20 = icmp sgt i64 %38, 11 + br i1 %icmpsgt20, label %if.then1603E, label %if.else1603E + +if.then1603E: ; preds = %if.else1604E + %39 = load i64, i64* @_ZN7default1mE + %icmpsgt21 = icmp sgt i64 %39, 11 + br i1 %icmpsgt21, label %if.then1105E, label %if.else1105E + +if.then1105E: ; preds = %if.then1603E + %40 = load i64, i64* @_ZN7default1lE + %icmpsgt22 = icmp sgt i64 %40, 11 + br i1 %icmpsgt22, label %if.then1096E, label %if.else1096E + +if.then1096E: ; preds = %if.then1105E + %41 = load i64, i64* @_ZN7default1kE + %icmpsgt23 = icmp sgt i64 %41, 11 + br i1 %icmpsgt23, label %if.then1087E, label %if.else1087E + +if.then1087E: ; preds = %if.then1096E + store i64 11, i64* @_ZN7default1nE + br label %if.end1087E + +if.else1087E: ; preds = %if.then1096E + store i64 12, i64* @_ZN7default1nE + br label %if.end1087E + +if.end1087E: ; preds = %if.else1087E, %if.then1087E + br label %if.end1096E + +if.else1096E: ; preds = %if.then1105E + store i64 13, i64* @_ZN7default1nE + br label %if.end1096E + +if.end1096E: ; preds = %if.else1096E, %if.end1087E + br label %if.end1105E + +if.else1105E: ; preds = %if.then1603E + store i64 14, i64* @_ZN7default1nE + br label %if.end1105E + +if.end1105E: ; preds = %if.else1105E, %if.end1096E + br label %if.end1603E + +if.else1603E: ; preds = %if.else1604E + %42 = load i64, i64* @_ZN7default1nE + %icmpsgt24 = icmp sgt i64 %42, 10 + br i1 %icmpsgt24, label %if.then1602E, label %if.else1602E + +if.then1602E: ; preds = %if.else1603E + %43 = load i64, i64* @_ZN7default1mE + %icmpsgt25 = icmp sgt i64 %43, 10 + br i1 %icmpsgt25, label %if.then1154E, label %if.else1154E + +if.then1154E: ; preds = %if.then1602E + %44 = load i64, i64* @_ZN7default1lE + %icmpsgt26 = icmp sgt i64 %44, 10 + br i1 %icmpsgt26, label %if.then1145E, label %if.else1145E + +if.then1145E: ; preds = %if.then1154E + %45 = load i64, i64* @_ZN7default1kE + %icmpsgt27 = icmp sgt i64 %45, 10 + br i1 %icmpsgt27, label %if.then1136E, label %if.else1136E + +if.then1136E: ; preds = %if.then1145E + store i64 10, i64* @_ZN7default1nE + br label %if.end1136E + +if.else1136E: ; preds = %if.then1145E + store i64 11, i64* @_ZN7default1nE + br label %if.end1136E + +if.end1136E: ; preds = %if.else1136E, %if.then1136E + br label %if.end1145E + +if.else1145E: ; preds = %if.then1154E + store i64 12, i64* @_ZN7default1nE + br label %if.end1145E + +if.end1145E: ; preds = %if.else1145E, %if.end1136E + br label %if.end1154E + +if.else1154E: ; preds = %if.then1602E + store i64 13, i64* @_ZN7default1nE + br label %if.end1154E + +if.end1154E: ; preds = %if.else1154E, %if.end1145E + br label %if.end1602E + +if.else1602E: ; preds = %if.else1603E + %46 = load i64, i64* @_ZN7default1nE + %icmpsgt28 = icmp sgt i64 %46, 9 + br i1 %icmpsgt28, label %if.then1601E, label %if.else1601E + +if.then1601E: ; preds = %if.else1602E + %47 = load i64, i64* @_ZN7default1mE + %icmpsgt29 = icmp sgt i64 %47, 9 + br i1 %icmpsgt29, label %if.then1203E, label %if.else1203E + +if.then1203E: ; preds = %if.then1601E + %48 = load i64, i64* @_ZN7default1lE + %icmpsgt30 = icmp sgt i64 %48, 9 + br i1 %icmpsgt30, label %if.then1194E, label %if.else1194E + +if.then1194E: ; preds = %if.then1203E + %49 = load i64, i64* @_ZN7default1kE + %icmpsgt31 = icmp sgt i64 %49, 9 + br i1 %icmpsgt31, label %if.then1185E, label %if.else1185E + +if.then1185E: ; preds = %if.then1194E + store i64 9, i64* @_ZN7default1nE + br label %if.end1185E + +if.else1185E: ; preds = %if.then1194E + store i64 10, i64* @_ZN7default1nE + br label %if.end1185E + +if.end1185E: ; preds = %if.else1185E, %if.then1185E + br label %if.end1194E + +if.else1194E: ; preds = %if.then1203E + store i64 11, i64* @_ZN7default1nE + br label %if.end1194E + +if.end1194E: ; preds = %if.else1194E, %if.end1185E + br label %if.end1203E + +if.else1203E: ; preds = %if.then1601E + store i64 12, i64* @_ZN7default1nE + br label %if.end1203E + +if.end1203E: ; preds = %if.else1203E, %if.end1194E + br label %if.end1601E + +if.else1601E: ; preds = %if.else1602E + %50 = load i64, i64* @_ZN7default1nE + %icmpsgt32 = icmp sgt i64 %50, 8 + br i1 %icmpsgt32, label %if.then1600E, label %if.else1600E + +if.then1600E: ; preds = %if.else1601E + %51 = load i64, i64* @_ZN7default1mE + %icmpsgt33 = icmp sgt i64 %51, 8 + br i1 %icmpsgt33, label %if.then1252E, label %if.else1252E + +if.then1252E: ; preds = %if.then1600E + %52 = load i64, i64* @_ZN7default1lE + %icmpsgt34 = icmp sgt i64 %52, 8 + br i1 %icmpsgt34, label %if.then1243E, label %if.else1243E + +if.then1243E: ; preds = %if.then1252E + %53 = load i64, i64* @_ZN7default1kE + %icmpsgt35 = icmp sgt i64 %53, 8 + br i1 %icmpsgt35, label %if.then1234E, label %if.else1234E + +if.then1234E: ; preds = %if.then1243E + store i64 8, i64* @_ZN7default1nE + br label %if.end1234E + +if.else1234E: ; preds = %if.then1243E + store i64 9, i64* @_ZN7default1nE + br label %if.end1234E + +if.end1234E: ; preds = %if.else1234E, %if.then1234E + br label %if.end1243E + +if.else1243E: ; preds = %if.then1252E + store i64 10, i64* @_ZN7default1nE + br label %if.end1243E + +if.end1243E: ; preds = %if.else1243E, %if.end1234E + br label %if.end1252E + +if.else1252E: ; preds = %if.then1600E + store i64 11, i64* @_ZN7default1nE + br label %if.end1252E + +if.end1252E: ; preds = %if.else1252E, %if.end1243E + br label %if.end1600E + +if.else1600E: ; preds = %if.else1601E + %54 = load i64, i64* @_ZN7default1nE + %icmpsgt36 = icmp sgt i64 %54, 7 + br i1 %icmpsgt36, label %if.then1599E, label %if.else1599E + +if.then1599E: ; preds = %if.else1600E + %55 = load i64, i64* @_ZN7default1mE + %icmpsgt37 = icmp sgt i64 %55, 7 + br i1 %icmpsgt37, label %if.then1301E, label %if.else1301E + +if.then1301E: ; preds = %if.then1599E + %56 = load i64, i64* @_ZN7default1lE + %icmpsgt38 = icmp sgt i64 %56, 7 + br i1 %icmpsgt38, label %if.then1292E, label %if.else1292E + +if.then1292E: ; preds = %if.then1301E + %57 = load i64, i64* @_ZN7default1kE + %icmpsgt39 = icmp sgt i64 %57, 7 + br i1 %icmpsgt39, label %if.then1283E, label %if.else1283E + +if.then1283E: ; preds = %if.then1292E + store i64 7, i64* @_ZN7default1nE + br label %if.end1283E + +if.else1283E: ; preds = %if.then1292E + store i64 8, i64* @_ZN7default1nE + br label %if.end1283E + +if.end1283E: ; preds = %if.else1283E, %if.then1283E + br label %if.end1292E + +if.else1292E: ; preds = %if.then1301E + store i64 9, i64* @_ZN7default1nE + br label %if.end1292E + +if.end1292E: ; preds = %if.else1292E, %if.end1283E + br label %if.end1301E + +if.else1301E: ; preds = %if.then1599E + store i64 10, i64* @_ZN7default1nE + br label %if.end1301E + +if.end1301E: ; preds = %if.else1301E, %if.end1292E + br label %if.end1599E + +if.else1599E: ; preds = %if.else1600E + %58 = load i64, i64* @_ZN7default1nE + %icmpsgt40 = icmp sgt i64 %58, 6 + br i1 %icmpsgt40, label %if.then1598E, label %if.else1598E + +if.then1598E: ; preds = %if.else1599E + %59 = load i64, i64* @_ZN7default1mE + %icmpsgt41 = icmp sgt i64 %59, 6 + br i1 %icmpsgt41, label %if.then1350E, label %if.else1350E + +if.then1350E: ; preds = %if.then1598E + %60 = load i64, i64* @_ZN7default1lE + %icmpsgt42 = icmp sgt i64 %60, 6 + br i1 %icmpsgt42, label %if.then1341E, label %if.else1341E + +if.then1341E: ; preds = %if.then1350E + %61 = load i64, i64* @_ZN7default1kE + %icmpsgt43 = icmp sgt i64 %61, 6 + br i1 %icmpsgt43, label %if.then1332E, label %if.else1332E + +if.then1332E: ; preds = %if.then1341E + store i64 6, i64* @_ZN7default1nE + br label %if.end1332E + +if.else1332E: ; preds = %if.then1341E + store i64 7, i64* @_ZN7default1nE + br label %if.end1332E + +if.end1332E: ; preds = %if.else1332E, %if.then1332E + br label %if.end1341E + +if.else1341E: ; preds = %if.then1350E + store i64 8, i64* @_ZN7default1nE + br label %if.end1341E + +if.end1341E: ; preds = %if.else1341E, %if.end1332E + br label %if.end1350E + +if.else1350E: ; preds = %if.then1598E + store i64 9, i64* @_ZN7default1nE + br label %if.end1350E + +if.end1350E: ; preds = %if.else1350E, %if.end1341E + br label %if.end1598E + +if.else1598E: ; preds = %if.else1599E + %62 = load i64, i64* @_ZN7default1nE + %icmpsgt44 = icmp sgt i64 %62, 5 + br i1 %icmpsgt44, label %if.then1597E, label %if.else1597E + +if.then1597E: ; preds = %if.else1598E + %63 = load i64, i64* @_ZN7default1mE + %icmpsgt45 = icmp sgt i64 %63, 5 + br i1 %icmpsgt45, label %if.then1399E, label %if.else1399E + +if.then1399E: ; preds = %if.then1597E + %64 = load i64, i64* @_ZN7default1lE + %icmpsgt46 = icmp sgt i64 %64, 5 + br i1 %icmpsgt46, label %if.then1390E, label %if.else1390E + +if.then1390E: ; preds = %if.then1399E + %65 = load i64, i64* @_ZN7default1kE + %icmpsgt47 = icmp sgt i64 %65, 5 + br i1 %icmpsgt47, label %if.then1381E, label %if.else1381E + +if.then1381E: ; preds = %if.then1390E + store i64 5, i64* @_ZN7default1nE + br label %if.end1381E + +if.else1381E: ; preds = %if.then1390E + store i64 6, i64* @_ZN7default1nE + br label %if.end1381E + +if.end1381E: ; preds = %if.else1381E, %if.then1381E + br label %if.end1390E + +if.else1390E: ; preds = %if.then1399E + store i64 7, i64* @_ZN7default1nE + br label %if.end1390E + +if.end1390E: ; preds = %if.else1390E, %if.end1381E + br label %if.end1399E + +if.else1399E: ; preds = %if.then1597E + store i64 8, i64* @_ZN7default1nE + br label %if.end1399E + +if.end1399E: ; preds = %if.else1399E, %if.end1390E + br label %if.end1597E + +if.else1597E: ; preds = %if.else1598E + %66 = load i64, i64* @_ZN7default1nE + %icmpsgt48 = icmp sgt i64 %66, 4 + br i1 %icmpsgt48, label %if.then1596E, label %if.else1596E + +if.then1596E: ; preds = %if.else1597E + %67 = load i64, i64* @_ZN7default1mE + %icmpsgt49 = icmp sgt i64 %67, 4 + br i1 %icmpsgt49, label %if.then1448E, label %if.else1448E + +if.then1448E: ; preds = %if.then1596E + %68 = load i64, i64* @_ZN7default1lE + %icmpsgt50 = icmp sgt i64 %68, 4 + br i1 %icmpsgt50, label %if.then1439E, label %if.else1439E + +if.then1439E: ; preds = %if.then1448E + %69 = load i64, i64* @_ZN7default1kE + %icmpsgt51 = icmp sgt i64 %69, 4 + br i1 %icmpsgt51, label %if.then1430E, label %if.else1430E + +if.then1430E: ; preds = %if.then1439E + store i64 4, i64* @_ZN7default1nE + br label %if.end1430E + +if.else1430E: ; preds = %if.then1439E + store i64 5, i64* @_ZN7default1nE + br label %if.end1430E + +if.end1430E: ; preds = %if.else1430E, %if.then1430E + br label %if.end1439E + +if.else1439E: ; preds = %if.then1448E + store i64 6, i64* @_ZN7default1nE + br label %if.end1439E + +if.end1439E: ; preds = %if.else1439E, %if.end1430E + br label %if.end1448E + +if.else1448E: ; preds = %if.then1596E + store i64 7, i64* @_ZN7default1nE + br label %if.end1448E + +if.end1448E: ; preds = %if.else1448E, %if.end1439E + br label %if.end1596E + +if.else1596E: ; preds = %if.else1597E + %70 = load i64, i64* @_ZN7default1nE + %icmpsgt52 = icmp sgt i64 %70, 3 + br i1 %icmpsgt52, label %if.then1595E, label %if.else1595E + +if.then1595E: ; preds = %if.else1596E + %71 = load i64, i64* @_ZN7default1mE + %icmpsgt53 = icmp sgt i64 %71, 3 + br i1 %icmpsgt53, label %if.then1497E, label %if.else1497E + +if.then1497E: ; preds = %if.then1595E + %72 = load i64, i64* @_ZN7default1lE + %icmpsgt54 = icmp sgt i64 %72, 3 + br i1 %icmpsgt54, label %if.then1488E, label %if.else1488E + +if.then1488E: ; preds = %if.then1497E + %73 = load i64, i64* @_ZN7default1kE + %icmpsgt55 = icmp sgt i64 %73, 3 + br i1 %icmpsgt55, label %if.then1479E, label %if.else1479E + +if.then1479E: ; preds = %if.then1488E + store i64 3, i64* @_ZN7default1nE + br label %if.end1479E + +if.else1479E: ; preds = %if.then1488E + store i64 4, i64* @_ZN7default1nE + br label %if.end1479E + +if.end1479E: ; preds = %if.else1479E, %if.then1479E + br label %if.end1488E + +if.else1488E: ; preds = %if.then1497E + store i64 5, i64* @_ZN7default1nE + br label %if.end1488E + +if.end1488E: ; preds = %if.else1488E, %if.end1479E + br label %if.end1497E + +if.else1497E: ; preds = %if.then1595E + store i64 6, i64* @_ZN7default1nE + br label %if.end1497E + +if.end1497E: ; preds = %if.else1497E, %if.end1488E + br label %if.end1595E + +if.else1595E: ; preds = %if.else1596E + %74 = load i64, i64* @_ZN7default1nE + %icmpsgt56 = icmp sgt i64 %74, 2 + br i1 %icmpsgt56, label %if.then1594E, label %if.else1594E + +if.then1594E: ; preds = %if.else1595E + %75 = load i64, i64* @_ZN7default1mE + %icmpsgt57 = icmp sgt i64 %75, 2 + br i1 %icmpsgt57, label %if.then1546E, label %if.else1546E + +if.then1546E: ; preds = %if.then1594E + %76 = load i64, i64* @_ZN7default1lE + %icmpsgt58 = icmp sgt i64 %76, 2 + br i1 %icmpsgt58, label %if.then1537E, label %if.else1537E + +if.then1537E: ; preds = %if.then1546E + %77 = load i64, i64* @_ZN7default1kE + %icmpsgt59 = icmp sgt i64 %77, 2 + br i1 %icmpsgt59, label %if.then1528E, label %if.else1528E + +if.then1528E: ; preds = %if.then1537E + store i64 2, i64* @_ZN7default1nE + br label %if.end1528E + +if.else1528E: ; preds = %if.then1537E + store i64 3, i64* @_ZN7default1nE + br label %if.end1528E + +if.end1528E: ; preds = %if.else1528E, %if.then1528E + br label %if.end1537E + +if.else1537E: ; preds = %if.then1546E + store i64 4, i64* @_ZN7default1nE + br label %if.end1537E + +if.end1537E: ; preds = %if.else1537E, %if.end1528E + br label %if.end1546E + +if.else1546E: ; preds = %if.then1594E + store i64 5, i64* @_ZN7default1nE + br label %if.end1546E + +if.end1546E: ; preds = %if.else1546E, %if.end1537E + br label %if.end1594E + +if.else1594E: ; preds = %if.else1595E + %78 = load i64, i64* @_ZN7default1mE + %icmpsgt60 = icmp sgt i64 %78, 1 + br i1 %icmpsgt60, label %if.then1591E, label %if.else1591E + +if.then1591E: ; preds = %if.else1594E + %79 = load i64, i64* @_ZN7default1lE + %icmpsgt61 = icmp sgt i64 %79, 1 + br i1 %icmpsgt61, label %if.then1582E, label %if.else1582E + +if.then1582E: ; preds = %if.then1591E + %80 = load i64, i64* @_ZN7default1kE + %icmpsgt62 = icmp sgt i64 %80, 1 + br i1 %icmpsgt62, label %if.then1573E, label %if.else1573E + +if.then1573E: ; preds = %if.then1582E + store i64 1, i64* @_ZN7default1nE + br label %if.end1573E + +if.else1573E: ; preds = %if.then1582E + store i64 2, i64* @_ZN7default1nE + br label %if.end1573E + +if.end1573E: ; preds = %if.else1573E, %if.then1573E + br label %if.end1582E + +if.else1582E: ; preds = %if.then1591E + store i64 3, i64* @_ZN7default1nE + br label %if.end1582E + +if.end1582E: ; preds = %if.else1582E, %if.end1573E + br label %if.end1591E + +if.else1591E: ; preds = %if.else1594E + store i64 4, i64* @_ZN7default1nE + br label %if.end1591E + +if.end1591E: ; preds = %if.else1591E, %if.end1582E + br label %if.end1594E + +if.end1594E: ; preds = %if.end1591E, %if.end1546E + br label %if.end1595E + +if.end1595E: ; preds = %if.end1594E, %if.end1497E + br label %if.end1596E + +if.end1596E: ; preds = %if.end1595E, %if.end1448E + br label %if.end1597E + +if.end1597E: ; preds = %if.end1596E, %if.end1399E + br label %if.end1598E + +if.end1598E: ; preds = %if.end1597E, %if.end1350E + br label %if.end1599E + +if.end1599E: ; preds = %if.end1598E, %if.end1301E + br label %if.end1600E + +if.end1600E: ; preds = %if.end1599E, %if.end1252E + br label %if.end1601E + +if.end1601E: ; preds = %if.end1600E, %if.end1203E + br label %if.end1602E + +if.end1602E: ; preds = %if.end1601E, %if.end1154E + br label %if.end1603E + +if.end1603E: ; preds = %if.end1602E, %if.end1105E + br label %if.end1604E + +if.end1604E: ; preds = %if.end1603E, %if.end1056E + br label %if.end1605E + +if.end1605E: ; preds = %if.end1604E, %if.end1007E + br label %if.end1606E + +if.end1606E: ; preds = %if.end1605E, %if.end958E + br label %if.end1607E + +if.end1607E: ; preds = %if.end1606E, %if.end909E + br label %if.end1608E + +if.end1608E: ; preds = %if.end1607E, %if.end860E + br label %block.body797E + +block.end797E: ; preds = %block.body797E + ret void +} + +define internal void @_ZN7default21timeBenchmarkIfElseD4Ev(%Unit.Type* noalias sret(%Unit.Type) %0) gc "cangjie" { +entry: + %callRet17 = alloca %Unit.Type + %enumTmp18 = alloca %"record._ZN11std$FS$core6StringE" + %enumTmp16 = alloca %"record._ZN11std$FS$core6StringE" + %enumTmp15 = alloca %"record._ZN11std$FS$core6StringE" + %callRet14 = alloca %"record._ZN11std$FS$core6StringE" + %callRet13 = alloca %"record._ZN11std$FS$core6StringE" + %enumTmp12 = alloca %"record._ZN11std$FS$core6StringE" + %enumTmp11 = alloca %"record._ZN11std$FS$core6StringE" + %callRet10 = alloca %"record._ZN11std$FS$core6StringE" + %enumTmp9 = alloca %"record._ZN11std$FS$core6StringE" + %callRet8 = alloca %"record._ZN11std$FS$core6StringE" + %callRet7 = alloca %"record._ZN11std$FS$core6StringE" + %callRet6 = alloca %"record._ZN11std$FS$core6StringE" + %perTime = alloca double + %enumTmp4 = alloca %"record._ZN11std$FS$time8DateTimeE" + %enumTmp = alloca %"record._ZN11std$FS$time8DateTimeE" + %callRet3 = alloca %"record._ZN11std$FS$time8DurationE" + %endTime = alloca %"record._ZN11std$FS$time8DateTimeE" + %callRet2 = alloca %"record._ZN11std$FS$time8DateTimeE" + %startTime = alloca %"record._ZN11std$FS$time8DateTimeE" + %callRet = alloca %"record._ZN11std$FS$time8DateTimeE" + %1 = bitcast %"record._ZN11std$FS$core6StringE"* %enumTmp18 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %1, i8 0, i64 16, i1 false) + %2 = bitcast %"record._ZN11std$FS$core6StringE"* %enumTmp16 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %2, i8 0, i64 16, i1 false) + %3 = bitcast %"record._ZN11std$FS$core6StringE"* %enumTmp15 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %3, i8 0, i64 16, i1 false) + %4 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet14 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %4, i8 0, i64 16, i1 false) + %5 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet13 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %5, i8 0, i64 16, i1 false) + %6 = bitcast %"record._ZN11std$FS$core6StringE"* %enumTmp12 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %6, i8 0, i64 16, i1 false) + %7 = bitcast %"record._ZN11std$FS$core6StringE"* %enumTmp11 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %7, i8 0, i64 16, i1 false) + %8 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet10 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %8, i8 0, i64 16, i1 false) + %9 = bitcast %"record._ZN11std$FS$core6StringE"* %enumTmp9 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %9, i8 0, i64 16, i1 false) + %10 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet8 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %10, i8 0, i64 16, i1 false) + %11 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet7 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %11, i8 0, i64 16, i1 false) + %12 = bitcast %"record._ZN11std$FS$core6StringE"* %callRet6 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %12, i8 0, i64 16, i1 false) + %13 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %enumTmp4 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %13, i8 0, i64 40, i1 false) + %14 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %enumTmp to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %14, i8 0, i64 40, i1 false) + %15 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %endTime to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %15, i8 0, i64 40, i1 false) + %16 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %callRet2 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %16, i8 0, i64 40, i1 false) + %17 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %startTime to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %17, i8 0, i64 40, i1 false) + %18 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %callRet to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %18, i8 0, i64 40, i1 false) + br label %thunk1624E + +thunk1624E: ; preds = %entry + %19 = call i8 addrspace(1)* @"_ZN11std$FS$time8DateTime3nowE$timeZone___0v"() + call void @"_ZN11std$FS$time8DateTime3nowEC_ZN11std$FS$time8TimeZoneE"(%"record._ZN11std$FS$time8DateTimeE"* noalias sret(%"record._ZN11std$FS$time8DateTimeE") %callRet, i8 addrspace(1)* %19) + %20 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %startTime to i8* + %21 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %callRet to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %20, i8* align 8 %21, i64 40, i1 false) + call void @_ZN7default17benchmarkIfElseD4Ev(%Unit.Type* noalias sret(%Unit.Type) %callRet17) + %22 = call i8 addrspace(1)* @"_ZN11std$FS$time8DateTime3nowE$timeZone___0v"() + call void @"_ZN11std$FS$time8DateTime3nowEC_ZN11std$FS$time8TimeZoneE"(%"record._ZN11std$FS$time8DateTimeE"* noalias sret(%"record._ZN11std$FS$time8DateTimeE") %callRet2, i8 addrspace(1)* %22) + %23 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %endTime to i8* + %24 = bitcast %"record._ZN11std$FS$time8DateTimeE"* %callRet2 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %23, i8* align 8 %24, i64 40, i1 false) + %25 = addrspacecast %"record._ZN11std$FS$time8DateTimeE"* %endTime to %"record._ZN11std$FS$time8DateTimeE" addrspace(1)* + %26 = addrspacecast %"record._ZN11std$FS$time8DateTimeE"* %startTime to %"record._ZN11std$FS$time8DateTimeE" addrspace(1)* + call void @"_ZN11std$FS$time8DateTime1-ER_ZN11std$FS$time8DateTimeE"(%"record._ZN11std$FS$time8DurationE"* noalias sret(%"record._ZN11std$FS$time8DurationE") %callRet3, i8 addrspace(1)* null, %"record._ZN11std$FS$time8DateTimeE" addrspace(1)* %25, i8 addrspace(1)* null, %"record._ZN11std$FS$time8DateTimeE" addrspace(1)* %26) + %27 = addrspacecast %"record._ZN11std$FS$time8DurationE"* %callRet3 to %"record._ZN11std$FS$time8DurationE" addrspace(1)* + %28 = call i64 @"_ZN11std$FS$time8Duration13toNanosecondsEv"(i8 addrspace(1)* null, %"record._ZN11std$FS$time8DurationE" addrspace(1)* %27) + %29 = sitofp i64 %28 to double + %30 = load i64, i64* @_ZN7default4repsE + %31 = sitofp i64 %30 to double + %div = fdiv double %29, %31 + store double %div, double* %perTime + call void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE") %callRet6, i8* getelementptr inbounds ([20 x i8], [20 x i8]* @"$const_string.5", i32 0, i32 0), i64 19) + %32 = load double, double* %perTime + call void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE") %callRet7, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"$const_string.2", i32 0, i32 0), i64 2) + %33 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet7 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + call void @"_ZN13std$FS$format6Extend7Float646formatER_ZN11std$FS$core6StringE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE") %callRet8, double %32, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %33) + %34 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet6 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %35 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet8 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + call void @"_ZN11std$FS$core6String1+ER_ZN11std$FS$core6StringE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE") %callRet10, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %34, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %35) + call void @"_ZN11std$FS$core6String23createStringFromLiteralE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE") %callRet13, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @"$const_string.3", i32 0, i32 0), i64 6) + %36 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet10 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + %37 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet13 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + call void @"_ZN11std$FS$core6String1+ER_ZN11std$FS$core6StringE"(%"record._ZN11std$FS$core6StringE"* noalias sret(%"record._ZN11std$FS$core6StringE") %callRet14, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %36, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %37) + %38 = addrspacecast %"record._ZN11std$FS$core6StringE"* %callRet14 to %"record._ZN11std$FS$core6StringE" addrspace(1)* + call void @"_ZN11std$FS$core7printlnER_ZN11std$FS$core6StringE"(%Unit.Type* noalias sret(%Unit.Type) %callRet17, i8 addrspace(1)* null, %"record._ZN11std$FS$core6StringE" addrspace(1)* %38) + ret void +} + +define i64 @_ZN7default4mainEv() gc "cangjie" { +entry: + %callRet2 = alloca %Unit.Type + br label %thunk1670E + +thunk1670E: ; preds = %entry + call void @_ZN7default21timeBenchmarkIfElseD4Ev(%Unit.Type* noalias sret(%Unit.Type) %callRet2) + ret i64 0 +} + +define i64 @user.main() gc "cangjie" { +entry: + %retVal = alloca i64 + br label %thunk1685E + +thunk1685E: ; preds = %entry + %0 = call i64 @_ZN7default4mainEv() + store i64 %0, i64* %retVal + %1 = load i64, i64* %retVal + ret i64 %1 +} + +!llvm.module.flags = !{!1} + +!0 = !{!"l"} +!1 = !{i32 2, !"CJBC", i32 1} diff --git a/llvm/test/CJLTO/pm-cjlto-opt-defaults.ll b/llvm/test/CJLTO/pm-cjlto-opt-defaults.ll new file mode 100644 index 000000000..8d8734007 --- /dev/null +++ b/llvm/test/CJLTO/pm-cjlto-opt-defaults.ll @@ -0,0 +1,975 @@ +; RUN: opt --cangjie-pipeline --cangjie-lto --debug-pass-manager \ +; RUN: --passes='default' -S %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O0 +; RUN: opt --cangjie-pipeline --cangjie-lto --debug-pass-manager \ +; RUN: --passes='default' -S %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O2 + +; CHECK-O0-NOT: Running pass: RewriteStatepointsForCangjieGC + +; CHECK-O2-NOT: Running pass: RewriteStatepointsForCangjieGC + +%recordString = type { i8 addrspace(1)*, i64 } +%Unit.Type = type { i8 } +%recordDataTime = type { i32, i32, i64, i64, i8 addrspace(1)*, i64 } +%recordDuration = type { i64 } + +@"$const_string.0" = private unnamed_addr constant [4 x i8] c"add\00", align 1 +@"$const_string.2" = private unnamed_addr constant [3 x i8] c".3\00", align 1 +@"$const_string.3" = private unnamed_addr constant [7 x i8] c" ns/op\00", align 1 +@"$const_string.5" = private unnamed_addr constant [20 x i8] c"BenchmarkIfElseD4: \00", align 1 +@_ZN7default1kE = global i64 2, align 8, !GlobalVarType !0 +@_ZN7default1lE = global i64 2, align 8, !GlobalVarType !0 +@_ZN7default1mE = global i64 2, align 8, !GlobalVarType !0 +@_ZN7default1nE = global i64 2, align 8, !GlobalVarType !0 +@_ZN7default4repsE = internal constant i64 100000000, align 8, !GlobalVarType !0 + +declare void @CJ_MCC_ThrowException(i8 addrspace(1)*) +declare void @foo(%recordString* noalias sret(%recordString)) gc "cangjie" +declare void @moo(%recordString* noalias sret(%recordString), i8*, i64) gc "cangjie" +declare void @too(%Unit.Type* noalias sret(%Unit.Type)) gc "cangjie" +declare void @woo(%recordDuration* noalias sret(%recordDuration)) gc "cangjie" +declare i8 addrspace(1)* @hoo() gc "cangjie" +declare void @goo(%recordDataTime* noalias sret(%recordDataTime), i8 addrspace(1)*) gc "cangjie" +declare i64 @koo() gc "cangjie" +declare void @xoo(%recordString* noalias sret(%recordString), double) gc "cangjie" +declare void @llvm.cj.memset.p0i8(i8*, i8, i64, i1) +declare i1 @llvm.expect.i1(i1, i1) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) +declare { i64, i1 } @llvm.sadd.with.overflow.i64(i64, i64) +declare i8 addrspace(1)* @CreateOverflowException() gc "cangjie" + +define void @_ZN7default17benchmarkIfElseD4Ev(%Unit.Type* noalias sret(%Unit.Type) %0) gc "cangjie" { +entry: + %enumTmp = alloca %recordString + %callRet = alloca %recordString + %1 = alloca i64 + %val.ov = alloca { i64, i1 } + %i = alloca i64 + %"$stop-compiler" = alloca i64 + %"$iter-i" = alloca i64 + %2 = bitcast %recordString* %enumTmp to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %2, i8 0, i64 16, i1 false) + %3 = bitcast %recordString* %callRet to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %3, i8 0, i64 16, i1 false) + br label %thunk782E + +thunk782E: ; preds = %entry + store i64 0, i64* %"$iter-i" + %4 = load i64, i64* @_ZN7default4repsE + store i64 %4, i64* %"$stop-compiler" + br label %block.body797E + +block.body797E: ; preds = %if.end1608E, %thunk782E + %5 = load i64, i64* %"$iter-i" + %6 = load i64, i64* %"$stop-compiler" + %icmpslt = icmp slt i64 %5, %6 + br i1 %icmpslt, label %while.body1620E, label %block.end797E + +while.body1620E: ; preds = %block.body797E + %7 = load i64, i64* %"$iter-i" + store i64 %7, i64* %i + %8 = load i64, i64* %"$iter-i" + %9 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %8, i64 1) + store { i64, i1 } %9, { i64, i1 }* %val.ov + %10 = getelementptr inbounds { i64, i1 }, { i64, i1 }* %val.ov, i32 0, i32 1 + %11 = load i1, i1* %10, align 1 + %12 = call i1 @llvm.expect.i1(i1 %11, i1 false) + br i1 %12, label %overflow0E, label %normal0E + +normal0E: ; preds = %while.body1620E + %13 = getelementptr inbounds { i64, i1 }, { i64, i1 }* %val.ov, i32 0, i32 0 + %14 = load i64, i64* %13 + store i64 %14, i64* %1 + %15 = load i64, i64* %1 + store i64 %15, i64* %"$iter-i" + %16 = load i64, i64* @_ZN7default1nE + %icmpsgt = icmp sgt i64 %16, 16 + br i1 %icmpsgt, label %if.then1608E, label %if.else1608E + +overflow0E: ; preds = %while.body1620E + call void @moo(%recordString* noalias sret(%recordString) %callRet, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"$const_string.0", i32 0, i32 0), i64 3) + %17 = addrspacecast %recordString* %callRet to %recordString addrspace(1)* + %18 = call i8 addrspace(1)* @CreateOverflowException() + call void @CJ_MCC_ThrowException(i8 addrspace(1)* %18) + unreachable + +if.then1608E: ; preds = %normal0E + %19 = load i64, i64* @_ZN7default1mE + %icmpsgt1 = icmp sgt i64 %19, 16 + br i1 %icmpsgt1, label %if.then860E, label %if.else860E + +if.then860E: ; preds = %if.then1608E + %20 = load i64, i64* @_ZN7default1lE + %icmpsgt2 = icmp sgt i64 %20, 16 + br i1 %icmpsgt2, label %if.then851E, label %if.else851E + +if.then851E: ; preds = %if.then860E + %21 = load i64, i64* @_ZN7default1kE + %icmpsgt3 = icmp sgt i64 %21, 16 + br i1 %icmpsgt3, label %if.then842E, label %if.else842E + +if.then842E: ; preds = %if.then851E + store i64 16, i64* @_ZN7default1nE + br label %if.end842E + +if.else842E: ; preds = %if.then851E + store i64 17, i64* @_ZN7default1nE + br label %if.end842E + +if.end842E: ; preds = %if.else842E, %if.then842E + br label %if.end851E + +if.else851E: ; preds = %if.then860E + store i64 18, i64* @_ZN7default1nE + br label %if.end851E + +if.end851E: ; preds = %if.else851E, %if.end842E + br label %if.end860E + +if.else860E: ; preds = %if.then1608E + store i64 19, i64* @_ZN7default1nE + br label %if.end860E + +if.end860E: ; preds = %if.else860E, %if.end851E + br label %if.end1608E + +if.else1608E: ; preds = %normal0E + %22 = load i64, i64* @_ZN7default1nE + %icmpsgt4 = icmp sgt i64 %22, 15 + br i1 %icmpsgt4, label %if.then1607E, label %if.else1607E + +if.then1607E: ; preds = %if.else1608E + %23 = load i64, i64* @_ZN7default1mE + %icmpsgt5 = icmp sgt i64 %23, 15 + br i1 %icmpsgt5, label %if.then909E, label %if.else909E + +if.then909E: ; preds = %if.then1607E + %24 = load i64, i64* @_ZN7default1lE + %icmpsgt6 = icmp sgt i64 %24, 15 + br i1 %icmpsgt6, label %if.then900E, label %if.else900E + +if.then900E: ; preds = %if.then909E + %25 = load i64, i64* @_ZN7default1kE + %icmpsgt7 = icmp sgt i64 %25, 15 + br i1 %icmpsgt7, label %if.then891E, label %if.else891E + +if.then891E: ; preds = %if.then900E + store i64 15, i64* @_ZN7default1nE + br label %if.end891E + +if.else891E: ; preds = %if.then900E + store i64 16, i64* @_ZN7default1nE + br label %if.end891E + +if.end891E: ; preds = %if.else891E, %if.then891E + br label %if.end900E + +if.else900E: ; preds = %if.then909E + store i64 17, i64* @_ZN7default1nE + br label %if.end900E + +if.end900E: ; preds = %if.else900E, %if.end891E + br label %if.end909E + +if.else909E: ; preds = %if.then1607E + store i64 18, i64* @_ZN7default1nE + br label %if.end909E + +if.end909E: ; preds = %if.else909E, %if.end900E + br label %if.end1607E + +if.else1607E: ; preds = %if.else1608E + %26 = load i64, i64* @_ZN7default1nE + %icmpsgt8 = icmp sgt i64 %26, 14 + br i1 %icmpsgt8, label %if.then1606E, label %if.else1606E + +if.then1606E: ; preds = %if.else1607E + %27 = load i64, i64* @_ZN7default1mE + %icmpsgt9 = icmp sgt i64 %27, 14 + br i1 %icmpsgt9, label %if.then958E, label %if.else958E + +if.then958E: ; preds = %if.then1606E + %28 = load i64, i64* @_ZN7default1lE + %icmpsgt10 = icmp sgt i64 %28, 14 + br i1 %icmpsgt10, label %if.then949E, label %if.else949E + +if.then949E: ; preds = %if.then958E + %29 = load i64, i64* @_ZN7default1kE + %icmpsgt11 = icmp sgt i64 %29, 14 + br i1 %icmpsgt11, label %if.then940E, label %if.else940E + +if.then940E: ; preds = %if.then949E + store i64 14, i64* @_ZN7default1nE + br label %if.end940E + +if.else940E: ; preds = %if.then949E + store i64 15, i64* @_ZN7default1nE + br label %if.end940E + +if.end940E: ; preds = %if.else940E, %if.then940E + br label %if.end949E + +if.else949E: ; preds = %if.then958E + store i64 16, i64* @_ZN7default1nE + br label %if.end949E + +if.end949E: ; preds = %if.else949E, %if.end940E + br label %if.end958E + +if.else958E: ; preds = %if.then1606E + store i64 17, i64* @_ZN7default1nE + br label %if.end958E + +if.end958E: ; preds = %if.else958E, %if.end949E + br label %if.end1606E + +if.else1606E: ; preds = %if.else1607E + %30 = load i64, i64* @_ZN7default1nE + %icmpsgt12 = icmp sgt i64 %30, 13 + br i1 %icmpsgt12, label %if.then1605E, label %if.else1605E + +if.then1605E: ; preds = %if.else1606E + %31 = load i64, i64* @_ZN7default1mE + %icmpsgt13 = icmp sgt i64 %31, 13 + br i1 %icmpsgt13, label %if.then1007E, label %if.else1007E + +if.then1007E: ; preds = %if.then1605E + %32 = load i64, i64* @_ZN7default1lE + %icmpsgt14 = icmp sgt i64 %32, 13 + br i1 %icmpsgt14, label %if.then998E, label %if.else998E + +if.then998E: ; preds = %if.then1007E + %33 = load i64, i64* @_ZN7default1kE + %icmpsgt15 = icmp sgt i64 %33, 13 + br i1 %icmpsgt15, label %if.then989E, label %if.else989E + +if.then989E: ; preds = %if.then998E + store i64 13, i64* @_ZN7default1nE + br label %if.end989E + +if.else989E: ; preds = %if.then998E + store i64 14, i64* @_ZN7default1nE + br label %if.end989E + +if.end989E: ; preds = %if.else989E, %if.then989E + br label %if.end998E + +if.else998E: ; preds = %if.then1007E + store i64 15, i64* @_ZN7default1nE + br label %if.end998E + +if.end998E: ; preds = %if.else998E, %if.end989E + br label %if.end1007E + +if.else1007E: ; preds = %if.then1605E + store i64 16, i64* @_ZN7default1nE + br label %if.end1007E + +if.end1007E: ; preds = %if.else1007E, %if.end998E + br label %if.end1605E + +if.else1605E: ; preds = %if.else1606E + %34 = load i64, i64* @_ZN7default1nE + %icmpsgt16 = icmp sgt i64 %34, 12 + br i1 %icmpsgt16, label %if.then1604E, label %if.else1604E + +if.then1604E: ; preds = %if.else1605E + %35 = load i64, i64* @_ZN7default1mE + %icmpsgt17 = icmp sgt i64 %35, 12 + br i1 %icmpsgt17, label %if.then1056E, label %if.else1056E + +if.then1056E: ; preds = %if.then1604E + %36 = load i64, i64* @_ZN7default1lE + %icmpsgt18 = icmp sgt i64 %36, 12 + br i1 %icmpsgt18, label %if.then1047E, label %if.else1047E + +if.then1047E: ; preds = %if.then1056E + %37 = load i64, i64* @_ZN7default1kE + %icmpsgt19 = icmp sgt i64 %37, 12 + br i1 %icmpsgt19, label %if.then1038E, label %if.else1038E + +if.then1038E: ; preds = %if.then1047E + store i64 12, i64* @_ZN7default1nE + br label %if.end1038E + +if.else1038E: ; preds = %if.then1047E + store i64 13, i64* @_ZN7default1nE + br label %if.end1038E + +if.end1038E: ; preds = %if.else1038E, %if.then1038E + br label %if.end1047E + +if.else1047E: ; preds = %if.then1056E + store i64 14, i64* @_ZN7default1nE + br label %if.end1047E + +if.end1047E: ; preds = %if.else1047E, %if.end1038E + br label %if.end1056E + +if.else1056E: ; preds = %if.then1604E + store i64 15, i64* @_ZN7default1nE + br label %if.end1056E + +if.end1056E: ; preds = %if.else1056E, %if.end1047E + br label %if.end1604E + +if.else1604E: ; preds = %if.else1605E + %38 = load i64, i64* @_ZN7default1nE + %icmpsgt20 = icmp sgt i64 %38, 11 + br i1 %icmpsgt20, label %if.then1603E, label %if.else1603E + +if.then1603E: ; preds = %if.else1604E + %39 = load i64, i64* @_ZN7default1mE + %icmpsgt21 = icmp sgt i64 %39, 11 + br i1 %icmpsgt21, label %if.then1105E, label %if.else1105E + +if.then1105E: ; preds = %if.then1603E + %40 = load i64, i64* @_ZN7default1lE + %icmpsgt22 = icmp sgt i64 %40, 11 + br i1 %icmpsgt22, label %if.then1096E, label %if.else1096E + +if.then1096E: ; preds = %if.then1105E + %41 = load i64, i64* @_ZN7default1kE + %icmpsgt23 = icmp sgt i64 %41, 11 + br i1 %icmpsgt23, label %if.then1087E, label %if.else1087E + +if.then1087E: ; preds = %if.then1096E + store i64 11, i64* @_ZN7default1nE + br label %if.end1087E + +if.else1087E: ; preds = %if.then1096E + store i64 12, i64* @_ZN7default1nE + br label %if.end1087E + +if.end1087E: ; preds = %if.else1087E, %if.then1087E + br label %if.end1096E + +if.else1096E: ; preds = %if.then1105E + store i64 13, i64* @_ZN7default1nE + br label %if.end1096E + +if.end1096E: ; preds = %if.else1096E, %if.end1087E + br label %if.end1105E + +if.else1105E: ; preds = %if.then1603E + store i64 14, i64* @_ZN7default1nE + br label %if.end1105E + +if.end1105E: ; preds = %if.else1105E, %if.end1096E + br label %if.end1603E + +if.else1603E: ; preds = %if.else1604E + %42 = load i64, i64* @_ZN7default1nE + %icmpsgt24 = icmp sgt i64 %42, 10 + br i1 %icmpsgt24, label %if.then1602E, label %if.else1602E + +if.then1602E: ; preds = %if.else1603E + %43 = load i64, i64* @_ZN7default1mE + %icmpsgt25 = icmp sgt i64 %43, 10 + br i1 %icmpsgt25, label %if.then1154E, label %if.else1154E + +if.then1154E: ; preds = %if.then1602E + %44 = load i64, i64* @_ZN7default1lE + %icmpsgt26 = icmp sgt i64 %44, 10 + br i1 %icmpsgt26, label %if.then1145E, label %if.else1145E + +if.then1145E: ; preds = %if.then1154E + %45 = load i64, i64* @_ZN7default1kE + %icmpsgt27 = icmp sgt i64 %45, 10 + br i1 %icmpsgt27, label %if.then1136E, label %if.else1136E + +if.then1136E: ; preds = %if.then1145E + store i64 10, i64* @_ZN7default1nE + br label %if.end1136E + +if.else1136E: ; preds = %if.then1145E + store i64 11, i64* @_ZN7default1nE + br label %if.end1136E + +if.end1136E: ; preds = %if.else1136E, %if.then1136E + br label %if.end1145E + +if.else1145E: ; preds = %if.then1154E + store i64 12, i64* @_ZN7default1nE + br label %if.end1145E + +if.end1145E: ; preds = %if.else1145E, %if.end1136E + br label %if.end1154E + +if.else1154E: ; preds = %if.then1602E + store i64 13, i64* @_ZN7default1nE + br label %if.end1154E + +if.end1154E: ; preds = %if.else1154E, %if.end1145E + br label %if.end1602E + +if.else1602E: ; preds = %if.else1603E + %46 = load i64, i64* @_ZN7default1nE + %icmpsgt28 = icmp sgt i64 %46, 9 + br i1 %icmpsgt28, label %if.then1601E, label %if.else1601E + +if.then1601E: ; preds = %if.else1602E + %47 = load i64, i64* @_ZN7default1mE + %icmpsgt29 = icmp sgt i64 %47, 9 + br i1 %icmpsgt29, label %if.then1203E, label %if.else1203E + +if.then1203E: ; preds = %if.then1601E + %48 = load i64, i64* @_ZN7default1lE + %icmpsgt30 = icmp sgt i64 %48, 9 + br i1 %icmpsgt30, label %if.then1194E, label %if.else1194E + +if.then1194E: ; preds = %if.then1203E + %49 = load i64, i64* @_ZN7default1kE + %icmpsgt31 = icmp sgt i64 %49, 9 + br i1 %icmpsgt31, label %if.then1185E, label %if.else1185E + +if.then1185E: ; preds = %if.then1194E + store i64 9, i64* @_ZN7default1nE + br label %if.end1185E + +if.else1185E: ; preds = %if.then1194E + store i64 10, i64* @_ZN7default1nE + br label %if.end1185E + +if.end1185E: ; preds = %if.else1185E, %if.then1185E + br label %if.end1194E + +if.else1194E: ; preds = %if.then1203E + store i64 11, i64* @_ZN7default1nE + br label %if.end1194E + +if.end1194E: ; preds = %if.else1194E, %if.end1185E + br label %if.end1203E + +if.else1203E: ; preds = %if.then1601E + store i64 12, i64* @_ZN7default1nE + br label %if.end1203E + +if.end1203E: ; preds = %if.else1203E, %if.end1194E + br label %if.end1601E + +if.else1601E: ; preds = %if.else1602E + %50 = load i64, i64* @_ZN7default1nE + %icmpsgt32 = icmp sgt i64 %50, 8 + br i1 %icmpsgt32, label %if.then1600E, label %if.else1600E + +if.then1600E: ; preds = %if.else1601E + %51 = load i64, i64* @_ZN7default1mE + %icmpsgt33 = icmp sgt i64 %51, 8 + br i1 %icmpsgt33, label %if.then1252E, label %if.else1252E + +if.then1252E: ; preds = %if.then1600E + %52 = load i64, i64* @_ZN7default1lE + %icmpsgt34 = icmp sgt i64 %52, 8 + br i1 %icmpsgt34, label %if.then1243E, label %if.else1243E + +if.then1243E: ; preds = %if.then1252E + %53 = load i64, i64* @_ZN7default1kE + %icmpsgt35 = icmp sgt i64 %53, 8 + br i1 %icmpsgt35, label %if.then1234E, label %if.else1234E + +if.then1234E: ; preds = %if.then1243E + store i64 8, i64* @_ZN7default1nE + br label %if.end1234E + +if.else1234E: ; preds = %if.then1243E + store i64 9, i64* @_ZN7default1nE + br label %if.end1234E + +if.end1234E: ; preds = %if.else1234E, %if.then1234E + br label %if.end1243E + +if.else1243E: ; preds = %if.then1252E + store i64 10, i64* @_ZN7default1nE + br label %if.end1243E + +if.end1243E: ; preds = %if.else1243E, %if.end1234E + br label %if.end1252E + +if.else1252E: ; preds = %if.then1600E + store i64 11, i64* @_ZN7default1nE + br label %if.end1252E + +if.end1252E: ; preds = %if.else1252E, %if.end1243E + br label %if.end1600E + +if.else1600E: ; preds = %if.else1601E + %54 = load i64, i64* @_ZN7default1nE + %icmpsgt36 = icmp sgt i64 %54, 7 + br i1 %icmpsgt36, label %if.then1599E, label %if.else1599E + +if.then1599E: ; preds = %if.else1600E + %55 = load i64, i64* @_ZN7default1mE + %icmpsgt37 = icmp sgt i64 %55, 7 + br i1 %icmpsgt37, label %if.then1301E, label %if.else1301E + +if.then1301E: ; preds = %if.then1599E + %56 = load i64, i64* @_ZN7default1lE + %icmpsgt38 = icmp sgt i64 %56, 7 + br i1 %icmpsgt38, label %if.then1292E, label %if.else1292E + +if.then1292E: ; preds = %if.then1301E + %57 = load i64, i64* @_ZN7default1kE + %icmpsgt39 = icmp sgt i64 %57, 7 + br i1 %icmpsgt39, label %if.then1283E, label %if.else1283E + +if.then1283E: ; preds = %if.then1292E + store i64 7, i64* @_ZN7default1nE + br label %if.end1283E + +if.else1283E: ; preds = %if.then1292E + store i64 8, i64* @_ZN7default1nE + br label %if.end1283E + +if.end1283E: ; preds = %if.else1283E, %if.then1283E + br label %if.end1292E + +if.else1292E: ; preds = %if.then1301E + store i64 9, i64* @_ZN7default1nE + br label %if.end1292E + +if.end1292E: ; preds = %if.else1292E, %if.end1283E + br label %if.end1301E + +if.else1301E: ; preds = %if.then1599E + store i64 10, i64* @_ZN7default1nE + br label %if.end1301E + +if.end1301E: ; preds = %if.else1301E, %if.end1292E + br label %if.end1599E + +if.else1599E: ; preds = %if.else1600E + %58 = load i64, i64* @_ZN7default1nE + %icmpsgt40 = icmp sgt i64 %58, 6 + br i1 %icmpsgt40, label %if.then1598E, label %if.else1598E + +if.then1598E: ; preds = %if.else1599E + %59 = load i64, i64* @_ZN7default1mE + %icmpsgt41 = icmp sgt i64 %59, 6 + br i1 %icmpsgt41, label %if.then1350E, label %if.else1350E + +if.then1350E: ; preds = %if.then1598E + %60 = load i64, i64* @_ZN7default1lE + %icmpsgt42 = icmp sgt i64 %60, 6 + br i1 %icmpsgt42, label %if.then1341E, label %if.else1341E + +if.then1341E: ; preds = %if.then1350E + %61 = load i64, i64* @_ZN7default1kE + %icmpsgt43 = icmp sgt i64 %61, 6 + br i1 %icmpsgt43, label %if.then1332E, label %if.else1332E + +if.then1332E: ; preds = %if.then1341E + store i64 6, i64* @_ZN7default1nE + br label %if.end1332E + +if.else1332E: ; preds = %if.then1341E + store i64 7, i64* @_ZN7default1nE + br label %if.end1332E + +if.end1332E: ; preds = %if.else1332E, %if.then1332E + br label %if.end1341E + +if.else1341E: ; preds = %if.then1350E + store i64 8, i64* @_ZN7default1nE + br label %if.end1341E + +if.end1341E: ; preds = %if.else1341E, %if.end1332E + br label %if.end1350E + +if.else1350E: ; preds = %if.then1598E + store i64 9, i64* @_ZN7default1nE + br label %if.end1350E + +if.end1350E: ; preds = %if.else1350E, %if.end1341E + br label %if.end1598E + +if.else1598E: ; preds = %if.else1599E + %62 = load i64, i64* @_ZN7default1nE + %icmpsgt44 = icmp sgt i64 %62, 5 + br i1 %icmpsgt44, label %if.then1597E, label %if.else1597E + +if.then1597E: ; preds = %if.else1598E + %63 = load i64, i64* @_ZN7default1mE + %icmpsgt45 = icmp sgt i64 %63, 5 + br i1 %icmpsgt45, label %if.then1399E, label %if.else1399E + +if.then1399E: ; preds = %if.then1597E + %64 = load i64, i64* @_ZN7default1lE + %icmpsgt46 = icmp sgt i64 %64, 5 + br i1 %icmpsgt46, label %if.then1390E, label %if.else1390E + +if.then1390E: ; preds = %if.then1399E + %65 = load i64, i64* @_ZN7default1kE + %icmpsgt47 = icmp sgt i64 %65, 5 + br i1 %icmpsgt47, label %if.then1381E, label %if.else1381E + +if.then1381E: ; preds = %if.then1390E + store i64 5, i64* @_ZN7default1nE + br label %if.end1381E + +if.else1381E: ; preds = %if.then1390E + store i64 6, i64* @_ZN7default1nE + br label %if.end1381E + +if.end1381E: ; preds = %if.else1381E, %if.then1381E + br label %if.end1390E + +if.else1390E: ; preds = %if.then1399E + store i64 7, i64* @_ZN7default1nE + br label %if.end1390E + +if.end1390E: ; preds = %if.else1390E, %if.end1381E + br label %if.end1399E + +if.else1399E: ; preds = %if.then1597E + store i64 8, i64* @_ZN7default1nE + br label %if.end1399E + +if.end1399E: ; preds = %if.else1399E, %if.end1390E + br label %if.end1597E + +if.else1597E: ; preds = %if.else1598E + %66 = load i64, i64* @_ZN7default1nE + %icmpsgt48 = icmp sgt i64 %66, 4 + br i1 %icmpsgt48, label %if.then1596E, label %if.else1596E + +if.then1596E: ; preds = %if.else1597E + %67 = load i64, i64* @_ZN7default1mE + %icmpsgt49 = icmp sgt i64 %67, 4 + br i1 %icmpsgt49, label %if.then1448E, label %if.else1448E + +if.then1448E: ; preds = %if.then1596E + %68 = load i64, i64* @_ZN7default1lE + %icmpsgt50 = icmp sgt i64 %68, 4 + br i1 %icmpsgt50, label %if.then1439E, label %if.else1439E + +if.then1439E: ; preds = %if.then1448E + %69 = load i64, i64* @_ZN7default1kE + %icmpsgt51 = icmp sgt i64 %69, 4 + br i1 %icmpsgt51, label %if.then1430E, label %if.else1430E + +if.then1430E: ; preds = %if.then1439E + store i64 4, i64* @_ZN7default1nE + br label %if.end1430E + +if.else1430E: ; preds = %if.then1439E + store i64 5, i64* @_ZN7default1nE + br label %if.end1430E + +if.end1430E: ; preds = %if.else1430E, %if.then1430E + br label %if.end1439E + +if.else1439E: ; preds = %if.then1448E + store i64 6, i64* @_ZN7default1nE + br label %if.end1439E + +if.end1439E: ; preds = %if.else1439E, %if.end1430E + br label %if.end1448E + +if.else1448E: ; preds = %if.then1596E + store i64 7, i64* @_ZN7default1nE + br label %if.end1448E + +if.end1448E: ; preds = %if.else1448E, %if.end1439E + br label %if.end1596E + +if.else1596E: ; preds = %if.else1597E + %70 = load i64, i64* @_ZN7default1nE + %icmpsgt52 = icmp sgt i64 %70, 3 + br i1 %icmpsgt52, label %if.then1595E, label %if.else1595E + +if.then1595E: ; preds = %if.else1596E + %71 = load i64, i64* @_ZN7default1mE + %icmpsgt53 = icmp sgt i64 %71, 3 + br i1 %icmpsgt53, label %if.then1497E, label %if.else1497E + +if.then1497E: ; preds = %if.then1595E + %72 = load i64, i64* @_ZN7default1lE + %icmpsgt54 = icmp sgt i64 %72, 3 + br i1 %icmpsgt54, label %if.then1488E, label %if.else1488E + +if.then1488E: ; preds = %if.then1497E + %73 = load i64, i64* @_ZN7default1kE + %icmpsgt55 = icmp sgt i64 %73, 3 + br i1 %icmpsgt55, label %if.then1479E, label %if.else1479E + +if.then1479E: ; preds = %if.then1488E + store i64 3, i64* @_ZN7default1nE + br label %if.end1479E + +if.else1479E: ; preds = %if.then1488E + store i64 4, i64* @_ZN7default1nE + br label %if.end1479E + +if.end1479E: ; preds = %if.else1479E, %if.then1479E + br label %if.end1488E + +if.else1488E: ; preds = %if.then1497E + store i64 5, i64* @_ZN7default1nE + br label %if.end1488E + +if.end1488E: ; preds = %if.else1488E, %if.end1479E + br label %if.end1497E + +if.else1497E: ; preds = %if.then1595E + store i64 6, i64* @_ZN7default1nE + br label %if.end1497E + +if.end1497E: ; preds = %if.else1497E, %if.end1488E + br label %if.end1595E + +if.else1595E: ; preds = %if.else1596E + %74 = load i64, i64* @_ZN7default1nE + %icmpsgt56 = icmp sgt i64 %74, 2 + br i1 %icmpsgt56, label %if.then1594E, label %if.else1594E + +if.then1594E: ; preds = %if.else1595E + %75 = load i64, i64* @_ZN7default1mE + %icmpsgt57 = icmp sgt i64 %75, 2 + br i1 %icmpsgt57, label %if.then1546E, label %if.else1546E + +if.then1546E: ; preds = %if.then1594E + %76 = load i64, i64* @_ZN7default1lE + %icmpsgt58 = icmp sgt i64 %76, 2 + br i1 %icmpsgt58, label %if.then1537E, label %if.else1537E + +if.then1537E: ; preds = %if.then1546E + %77 = load i64, i64* @_ZN7default1kE + %icmpsgt59 = icmp sgt i64 %77, 2 + br i1 %icmpsgt59, label %if.then1528E, label %if.else1528E + +if.then1528E: ; preds = %if.then1537E + store i64 2, i64* @_ZN7default1nE + br label %if.end1528E + +if.else1528E: ; preds = %if.then1537E + store i64 3, i64* @_ZN7default1nE + br label %if.end1528E + +if.end1528E: ; preds = %if.else1528E, %if.then1528E + br label %if.end1537E + +if.else1537E: ; preds = %if.then1546E + store i64 4, i64* @_ZN7default1nE + br label %if.end1537E + +if.end1537E: ; preds = %if.else1537E, %if.end1528E + br label %if.end1546E + +if.else1546E: ; preds = %if.then1594E + store i64 5, i64* @_ZN7default1nE + br label %if.end1546E + +if.end1546E: ; preds = %if.else1546E, %if.end1537E + br label %if.end1594E + +if.else1594E: ; preds = %if.else1595E + %78 = load i64, i64* @_ZN7default1mE + %icmpsgt60 = icmp sgt i64 %78, 1 + br i1 %icmpsgt60, label %if.then1591E, label %if.else1591E + +if.then1591E: ; preds = %if.else1594E + %79 = load i64, i64* @_ZN7default1lE + %icmpsgt61 = icmp sgt i64 %79, 1 + br i1 %icmpsgt61, label %if.then1582E, label %if.else1582E + +if.then1582E: ; preds = %if.then1591E + %80 = load i64, i64* @_ZN7default1kE + %icmpsgt62 = icmp sgt i64 %80, 1 + br i1 %icmpsgt62, label %if.then1573E, label %if.else1573E + +if.then1573E: ; preds = %if.then1582E + store i64 1, i64* @_ZN7default1nE + br label %if.end1573E + +if.else1573E: ; preds = %if.then1582E + store i64 2, i64* @_ZN7default1nE + br label %if.end1573E + +if.end1573E: ; preds = %if.else1573E, %if.then1573E + br label %if.end1582E + +if.else1582E: ; preds = %if.then1591E + store i64 3, i64* @_ZN7default1nE + br label %if.end1582E + +if.end1582E: ; preds = %if.else1582E, %if.end1573E + br label %if.end1591E + +if.else1591E: ; preds = %if.else1594E + store i64 4, i64* @_ZN7default1nE + br label %if.end1591E + +if.end1591E: ; preds = %if.else1591E, %if.end1582E + br label %if.end1594E + +if.end1594E: ; preds = %if.end1591E, %if.end1546E + br label %if.end1595E + +if.end1595E: ; preds = %if.end1594E, %if.end1497E + br label %if.end1596E + +if.end1596E: ; preds = %if.end1595E, %if.end1448E + br label %if.end1597E + +if.end1597E: ; preds = %if.end1596E, %if.end1399E + br label %if.end1598E + +if.end1598E: ; preds = %if.end1597E, %if.end1350E + br label %if.end1599E + +if.end1599E: ; preds = %if.end1598E, %if.end1301E + br label %if.end1600E + +if.end1600E: ; preds = %if.end1599E, %if.end1252E + br label %if.end1601E + +if.end1601E: ; preds = %if.end1600E, %if.end1203E + br label %if.end1602E + +if.end1602E: ; preds = %if.end1601E, %if.end1154E + br label %if.end1603E + +if.end1603E: ; preds = %if.end1602E, %if.end1105E + br label %if.end1604E + +if.end1604E: ; preds = %if.end1603E, %if.end1056E + br label %if.end1605E + +if.end1605E: ; preds = %if.end1604E, %if.end1007E + br label %if.end1606E + +if.end1606E: ; preds = %if.end1605E, %if.end958E + br label %if.end1607E + +if.end1607E: ; preds = %if.end1606E, %if.end909E + br label %if.end1608E + +if.end1608E: ; preds = %if.end1607E, %if.end860E + br label %block.body797E + +block.end797E: ; preds = %block.body797E + ret void +} + +define void @_ZN7default21timeBenchmarkIfElseD4Ev(%Unit.Type* noalias sret(%Unit.Type) %0) gc "cangjie" { +entry: + %callRet17 = alloca %Unit.Type + %enumTmp18 = alloca %recordString + %enumTmp16 = alloca %recordString + %enumTmp15 = alloca %recordString + %callRet14 = alloca %recordString + %callRet13 = alloca %recordString + %enumTmp12 = alloca %recordString + %enumTmp11 = alloca %recordString + %callRet10 = alloca %recordString + %enumTmp9 = alloca %recordString + %callRet8 = alloca %recordString + %callRet7 = alloca %recordString + %callRet6 = alloca %recordString + %perTime = alloca double + %enumTmp4 = alloca %recordDataTime + %enumTmp = alloca %recordDataTime + %callRet3 = alloca %recordDuration + %endTime = alloca %recordDataTime + %callRet2 = alloca %recordDataTime + %startTime = alloca %recordDataTime + %callRet = alloca %recordDataTime + %1 = bitcast %recordString* %enumTmp18 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %1, i8 0, i64 16, i1 false) + %2 = bitcast %recordString* %enumTmp16 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %2, i8 0, i64 16, i1 false) + %3 = bitcast %recordString* %enumTmp15 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %3, i8 0, i64 16, i1 false) + %4 = bitcast %recordString* %callRet14 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %4, i8 0, i64 16, i1 false) + %5 = bitcast %recordString* %callRet13 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %5, i8 0, i64 16, i1 false) + %6 = bitcast %recordString* %enumTmp12 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %6, i8 0, i64 16, i1 false) + %7 = bitcast %recordString* %enumTmp11 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %7, i8 0, i64 16, i1 false) + %8 = bitcast %recordString* %callRet10 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %8, i8 0, i64 16, i1 false) + %9 = bitcast %recordString* %enumTmp9 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %9, i8 0, i64 16, i1 false) + %10 = bitcast %recordString* %callRet8 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %10, i8 0, i64 16, i1 false) + %11 = bitcast %recordString* %callRet7 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %11, i8 0, i64 16, i1 false) + %12 = bitcast %recordString* %callRet6 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %12, i8 0, i64 16, i1 false) + %13 = bitcast %recordDataTime* %enumTmp4 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %13, i8 0, i64 40, i1 false) + %14 = bitcast %recordDataTime* %enumTmp to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %14, i8 0, i64 40, i1 false) + %15 = bitcast %recordDataTime* %endTime to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %15, i8 0, i64 40, i1 false) + %16 = bitcast %recordDataTime* %callRet2 to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %16, i8 0, i64 40, i1 false) + %17 = bitcast %recordDataTime* %startTime to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %17, i8 0, i64 40, i1 false) + %18 = bitcast %recordDataTime* %callRet to i8* + call void @llvm.cj.memset.p0i8(i8* align 8 %18, i8 0, i64 40, i1 false) + br label %thunk1624E + +thunk1624E: ; preds = %entry + %19 = call i8 addrspace(1)* @hoo() + call void @goo(%recordDataTime* noalias sret(%recordDataTime) %callRet, i8 addrspace(1)* %19) + %20 = bitcast %recordDataTime* %startTime to i8* + %21 = bitcast %recordDataTime* %callRet to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %20, i8* align 8 %21, i64 40, i1 false) + call void @_ZN7default17benchmarkIfElseD4Ev(%Unit.Type* noalias sret(%Unit.Type) %callRet17) + %22 = call i8 addrspace(1)* @hoo() + call void @goo(%recordDataTime* noalias sret(%recordDataTime) %callRet2, i8 addrspace(1)* %22) + %23 = bitcast %recordDataTime* %endTime to i8* + %24 = bitcast %recordDataTime* %callRet2 to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %23, i8* align 8 %24, i64 40, i1 false) + %25 = addrspacecast %recordDataTime* %endTime to %recordDataTime addrspace(1)* + %26 = addrspacecast %recordDataTime* %startTime to %recordDataTime addrspace(1)* + call void @woo(%recordDuration* noalias sret(%recordDuration) %callRet3) + %27 = addrspacecast %recordDuration* %callRet3 to %recordDuration addrspace(1)* + %28 = call i64 @koo() + %29 = sitofp i64 %28 to double + %30 = load i64, i64* @_ZN7default4repsE + %31 = sitofp i64 %30 to double + %div = fdiv double %29, %31 + store double %div, double* %perTime + call void @moo(%recordString* noalias sret(%recordString) %callRet6, i8* getelementptr inbounds ([20 x i8], [20 x i8]* @"$const_string.5", i32 0, i32 0), i64 19) + %32 = load double, double* %perTime + call void @moo(%recordString* noalias sret(%recordString) %callRet7, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"$const_string.2", i32 0, i32 0), i64 2) + call void @xoo(%recordString* noalias sret(%recordString) %callRet8, double %32) + call void @foo(%recordString* noalias sret(%recordString) %callRet10) + call void @moo(%recordString* noalias sret(%recordString) %callRet13, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @"$const_string.3", i32 0, i32 0), i64 6) + call void @foo(%recordString* noalias sret(%recordString) %callRet14) + call void @too(%Unit.Type* noalias sret(%Unit.Type) %callRet17) + ret void +} + +define i64 @_ZN7default4mainEv() gc "cangjie" { +entry: + %callRet2 = alloca %Unit.Type + br label %thunk1670E + +thunk1670E: ; preds = %entry + call void @_ZN7default21timeBenchmarkIfElseD4Ev(%Unit.Type* noalias sret(%Unit.Type) %callRet2) + ret i64 0 +} + +define i64 @user.main() gc "cangjie" { +entry: + %retVal = alloca i64 + br label %thunk1685E + +thunk1685E: ; preds = %entry + %0 = call i64 @_ZN7default4mainEv() + store i64 %0, i64* %retVal + %1 = load i64, i64* %retVal + ret i64 %1 +} + +!llvm.module.flags = !{!1} + +!0 = !{!"l"} +!1 = !{i32 2, !"CJBC", i32 1} diff --git a/llvm/test/CodeGen/AArch64/CangjieGC/atomic.ll b/llvm/test/CodeGen/AArch64/CangjieGC/atomic.ll new file mode 100644 index 000000000..2caad9ab2 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/CangjieGC/atomic.ll @@ -0,0 +1,52 @@ +; RUN: llc --cangjie-pipeline -mtriple=aarch64 < %s | FileCheck %s +; XFAIL: * + +define internal void @func_atomic_store(i8 addrspace(1)* %this, i8 addrspace(1)* %that) gc "cangjie" { +entry: +; CHECK: func_atomic_store +; CHECK: lsr w9, x28, #56 +; CHECK: cmp w9, #9 +; CHECK: gcNoRunning +; CHECK: stlr x0, [x1] +; CHECK: gcRunning +; CHECK: bl CJ_MCC_AtomicWriteReference + + %0 = bitcast i8 addrspace(1)* %that to i8 addrspace(1)* addrspace(1)* + call void @llvm.cj.atomic.store(i8 addrspace(1)* %this, i8 addrspace(1)* %that, i8 addrspace(1)* addrspace(1)* %0, i32 5) + ret void +} + +define internal void @func_atomic_compare_swap(i8 addrspace(1)* %this, i8 addrspace(1)* %that) gc "cangjie" { +entry: +; CHECK: func_atomic_compare_swap +; CHECK: lsr w9, x28, #56 +; CHECK: cmp w9, #9 +; CHECK: gcRunning +; CHECK: bl CJ_MCC_CompareAndSwapReference + + %0 = bitcast i8 addrspace(1)* %that to i8 addrspace(1)* addrspace(1)* + %1 = call i1 @llvm.cj.atomic.compare.swap(i8 addrspace(1)* %this, i8 addrspace(1)* %that, i8 addrspace(1)* %this, i8 addrspace(1)* addrspace(1)* %0, i32 5, i32 5) + ret void +} + +define internal void @func_atomic_swap(i8 addrspace(1)* %this, i8 addrspace(1)* %that) gc "cangjie" { +entry: +; CHECK: func_atomic_swap +; CHECK: lsr w9, x28, #56 +; CHECK: cmp w9, #9 +; CHECK: gcRunning +; CHECK: bl CJ_MCC_AtomicSwapReference + + %0 = bitcast i8 addrspace(1)* %that to i8 addrspace(1)* addrspace(1)* + %1 = call i8 addrspace(1)* @llvm.cj.atomic.swap(i8 addrspace(1)* %this, i8 addrspace(1)* %that, i8 addrspace(1)* addrspace(1)* %0, i32 5) + ret void +} + +; Function Attrs: nounwind +declare void @llvm.cj.atomic.store(i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*, i32) + +; Function Attrs: nounwind +declare i8 addrspace(1)* @llvm.cj.atomic.swap(i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*, i32) + +; Function Attrs: nounwind +declare i1 @llvm.cj.atomic.compare.swap(i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)*, i8 addrspace(1)* addrspace(1)*, i32, i32) diff --git a/llvm/test/CodeGen/AArch64/arm64-homogeneous-prolog-epilog-frame-tail.ll b/llvm/test/CodeGen/AArch64/arm64-homogeneous-prolog-epilog-frame-tail.ll index 32cc850df..6a108b1a3 100644 --- a/llvm/test/CodeGen/AArch64/arm64-homogeneous-prolog-epilog-frame-tail.ll +++ b/llvm/test/CodeGen/AArch64/arm64-homogeneous-prolog-epilog-frame-tail.ll @@ -9,11 +9,11 @@ ; CHECK: b _OUTLINED_FUNCTION_EPILOG_TAIL_x30x29x19x20d8d9d10d11 ; CHECK-LINUX-LABEL: _Z3foofffi: -; CHECK-LINUX: stp x29, x30, [sp, #-32]! -; CHECK-LINUX-NEXT: bl OUTLINED_FUNCTION_PROLOG_FRAME32_x19x20x30x29d8d9d10d11 +; CHECK-LINUX: stp x29, x30, [sp, #-64]! +; CHECK-LINUX-NEXT: bl OUTLINED_FUNCTION_PROLOG_FRAME0_x19x20d8d9d10d11x30x29 ; CHECK-LINUX: bl _Z3goof ; CHECK-LINUX: bl _Z3goof -; CHECK-LINUX: b OUTLINED_FUNCTION_EPILOG_TAIL_x19x20x30x29d8d9d10d11 +; CHECK-LINUX: b OUTLINED_FUNCTION_EPILOG_TAIL_x19x20d8d9d10d11x30x29 define float @_Z3foofffi(float %b, float %x, float %y, i32 %z) ssp minsize "frame-pointer"="non-leaf" { entry: @@ -66,18 +66,18 @@ declare i32 @_Z3hoov() nounwind ssp optsize ; CHECK: ldp x29, x30, [sp], #16 ; CHECK-NEXT: ret -; CHECK-LINUX-LABEL: OUTLINED_FUNCTION_PROLOG_FRAME32_x19x20x30x29d8d9d10d11: -; CHECK-LINUX: stp d11, d10, [sp, #-32]! -; CHECK-LINUX-NEXT: stp d9, d8, [sp, #16] +; CHECK-LINUX-LABEL: OUTLINED_FUNCTION_PROLOG_FRAME0_x19x20d8d9d10d11x30x29: +; CHECK-LINUX: stp d11, d10, [sp, #16] +; CHECK-LINUX-NEXT: stp d9, d8, [sp, #32] ; CHECK-LINUX-NEXT: stp x20, x19, [sp, #48] -; CHECK-LINUX-NEXT: add x29, sp, #32 +; CHECK-LINUX-NEXT: mov x29, sp ; CHECK-LINUX-NEXT: ret -; CHECK-LINUX-LABEL: OUTLINED_FUNCTION_EPILOG_TAIL_x19x20x30x29d8d9d10d11: +; CHECK-LINUX-LABEL: OUTLINED_FUNCTION_EPILOG_TAIL_x19x20d8d9d10d11x30x29: ; CHECK-LINUX: ldp x20, x19, [sp, #48] -; CHECK-LINUX-NEXT: ldp x29, x30, [sp, #32] -; CHECK-LINUX-NEXT: ldp d9, d8, [sp, #16] -; CHECK-LINUX-NEXT: ldp d11, d10, [sp], #64 +; CHECK-LINUX-NEXT: ldp d9, d8, [sp, #32] +; CHECK-LINUX-NEXT: ldp d11, d10, [sp, #16] +; CHECK-LINUX-NEXT: ldp x29, x30, [sp], #64 ; CHECK-LINUX-NEXT: ret ; CHECK-LINUX-LABEL: OUTLINED_FUNCTION_EPILOG_TAIL_x30x29: diff --git a/llvm/test/CodeGen/AArch64/arm64-homogeneous-prolog-epilog-no-helper.ll b/llvm/test/CodeGen/AArch64/arm64-homogeneous-prolog-epilog-no-helper.ll index 40f6c9486..8a3624b43 100644 --- a/llvm/test/CodeGen/AArch64/arm64-homogeneous-prolog-epilog-no-helper.ll +++ b/llvm/test/CodeGen/AArch64/arm64-homogeneous-prolog-epilog-no-helper.ll @@ -44,20 +44,20 @@ define float @_Z3foofffi(float %b, float %x, float %y, i32 %z) uwtable ssp minsi ; ; CHECK-LINUX-LABEL: _Z3foofffi: ; CHECK-LINUX: // %bb.0: // %entry -; CHECK-LINUX-NEXT: stp d11, d10, [sp, #-64]! -; CHECK-LINUX-NEXT: stp d9, d8, [sp, #16] -; CHECK-LINUX-NEXT: stp x29, x30, [sp, #32] +; CHECK-LINUX-NEXT: stp x29, x30, [sp, #-64] +; CHECK-LINUX-NEXT: stp d11, d10, [sp, #16] +; CHECK-LINUX-NEXT: stp d9, d8, [sp, #32] ; CHECK-LINUX-NEXT: stp x20, x19, [sp, #48] -; CHECK-LINUX-NEXT: add x29, sp, #32 -; CHECK-LINUX-NEXT: .cfi_def_cfa w29, 32 +; CHECK-LINUX-NEXT: mov x29, sp +; CHECK-LINUX-NEXT: .cfi_def_cfa w29, 64 ; CHECK-LINUX-NEXT: .cfi_offset w19, -8 ; CHECK-LINUX-NEXT: .cfi_offset w20, -16 -; CHECK-LINUX-NEXT: .cfi_offset w30, -24 -; CHECK-LINUX-NEXT: .cfi_offset w29, -32 -; CHECK-LINUX-NEXT: .cfi_offset b8, -40 -; CHECK-LINUX-NEXT: .cfi_offset b9, -48 -; CHECK-LINUX-NEXT: .cfi_offset b10, -56 -; CHECK-LINUX-NEXT: .cfi_offset b11, -64 +; CHECK-LINUX-NEXT: .cfi_offset b8, -24 +; CHECK-LINUX-NEXT: .cfi_offset b9, -32 +; CHECK-LINUX-NEXT: .cfi_offset b10, -40 +; CHECK-LINUX-NEXT: .cfi_offset b11, -48 +; CHECK-LINUX-NEXT: .cfi_offset w30, -56 +; CHECK-LINUX-NEXT: .cfi_offset w29, -64 ; CHECK-LINUX-NEXT: fmov s3, #1.00000000 ; CHECK-LINUX-NEXT: scvtf s4, w0 ; CHECK-LINUX-NEXT: sub w19, w0, #1 @@ -73,12 +73,12 @@ define float @_Z3foofffi(float %b, float %x, float %y, i32 %z) uwtable ssp minsi ; CHECK-LINUX-NEXT: fadd s0, s10, s0 ; CHECK-LINUX-NEXT: scvtf s1, w19 ; CHECK-LINUX-NEXT: ldp x20, x19, [sp, #48] -; CHECK-LINUX-NEXT: ldp x29, x30, [sp, #32] ; CHECK-LINUX-NEXT: fmul s0, s8, s0 ; CHECK-LINUX-NEXT: fadd s0, s9, s0 -; CHECK-LINUX-NEXT: ldp d9, d8, [sp, #16] +; CHECK-LINUX-NEXT: ldp d9, d8, [sp, #32] +; CHECK-LINUX-NEXT: ldp d11, d10, [sp, #16] ; CHECK-LINUX-NEXT: fsub s0, s0, s1 -; CHECK-LINUX-NEXT: ldp d11, d10, [sp], #64 +; CHECK-LINUX-NEXT: x29, x30, [sp], #64 ; CHECK-LINUX-NEXT: ret entry: %inc = fadd float %b, 1.000000e+00 diff --git a/llvm/test/CodeGen/AArch64/combine-comparisons-by-cse.ll b/llvm/test/CodeGen/AArch64/combine-comparisons-by-cse.ll index 437185efd..fbf661fee 100644 --- a/llvm/test/CodeGen/AArch64/combine-comparisons-by-cse.ll +++ b/llvm/test/CodeGen/AArch64/combine-comparisons-by-cse.ll @@ -659,12 +659,13 @@ return: ; preds = %if.end, %land.lhs.t define i32 @fcmpri(i32 %argc, i8** nocapture readonly %argv) #0 { ; CHECK-LABEL: fcmpri: ; CHECK: // %bb.0: // %entry -; CHECK-NEXT: str d8, [sp, #-32]! // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #-32]! // 8-byte Folded Spill ; CHECK-NEXT: .cfi_def_cfa_offset 32 -; CHECK-NEXT: stp x30, x19, [sp, #16] // 16-byte Folded Spill -; CHECK-NEXT: .cfi_offset w19, -8 -; CHECK-NEXT: .cfi_offset w30, -16 -; CHECK-NEXT: .cfi_offset b8, -32 +; CHECK-NEXT: str d8, [sp, #8] // 8-byte Folded Spill +; CHECK-NEXT: str x19, [sp, #16] // 8-byte Folded Spill +; CHECK-NEXT: .cfi_offset w19, -16 +; CHECK-NEXT: .cfi_offset b8, -24 +; CHECK-NEXT: .cfi_offset w30, -32 ; CHECK-NEXT: cmp w0, #2 ; CHECK-NEXT: b.lt .LBB9_3 ; CHECK-NEXT: // %bb.1: // %land.lhs.true @@ -692,12 +693,13 @@ define i32 @fcmpri(i32 %argc, i8** nocapture readonly %argv) #0 { ; CHECK-NEXT: bl woo ; CHECK-NEXT: mov w0, #4 ; CHECK-NEXT: .LBB9_4: // %return -; CHECK-NEXT: ldp x30, x19, [sp, #16] // 16-byte Folded Reload -; CHECK-NEXT: ldr d8, [sp], #32 // 8-byte Folded Reload +; CHECK-NEXT: ldr x19, [sp, #16] // 8-byte Folded Reload +; CHECK-NEXT: ldr d8, [sp, #8] // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp], #32 // 8-byte Folded Reload ; CHECK-NEXT: .cfi_def_cfa_offset 0 ; CHECK-NEXT: .cfi_restore w19 -; CHECK-NEXT: .cfi_restore w30 ; CHECK-NEXT: .cfi_restore b8 +; CHECK-NEXT: .cfi_restore w30 ; CHECK-NEXT: ret ; CHECK-LABEL-DAG: .LBB9_3 diff --git a/llvm/test/CodeGen/AArch64/fadd-combines.ll b/llvm/test/CodeGen/AArch64/fadd-combines.ll index 8807df1fa..6290b858e 100644 --- a/llvm/test/CodeGen/AArch64/fadd-combines.ll +++ b/llvm/test/CodeGen/AArch64/fadd-combines.ll @@ -112,16 +112,17 @@ define <4 x float> @test6(<4 x float> %a, <4 x float> %b) { define double @test7(double %a, double %b) nounwind { ; CHECK-LABEL: test7: ; CHECK: // %bb.0: -; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill ; CHECK-NEXT: fmov d2, #-2.00000000 -; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill +; CHECK-NEXT: str d8, [sp, #8] // 8-byte Folded Spill ; CHECK-NEXT: fmul d1, d1, d2 ; CHECK-NEXT: fadd d8, d0, d1 ; CHECK-NEXT: fmov d0, d1 ; CHECK-NEXT: bl use -; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: fmov d0, d8 -; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload +; CHECK-NEXT: ldr d8, [sp, #8] // 8-byte Folded Reload + +; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret %mul = fmul double %b, -2.000000e+00 %add1 = fadd double %a, %mul diff --git a/llvm/test/CodeGen/AArch64/fptosi-sat-scalar.ll b/llvm/test/CodeGen/AArch64/fptosi-sat-scalar.ll index 729f531d3..81b6fedc1 100644 --- a/llvm/test/CodeGen/AArch64/fptosi-sat-scalar.ll +++ b/llvm/test/CodeGen/AArch64/fptosi-sat-scalar.ll @@ -127,14 +127,13 @@ define i64 @test_signed_i64_f32(float %f) nounwind { define i100 @test_signed_i100_f32(float %f) nounwind { ; CHECK-LABEL: test_signed_i100_f32: ; CHECK: // %bb.0: -; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill +; CHECK-NEXT: str d8, [sp, #8] // 8-byte Folded Spill ; CHECK-NEXT: fmov s8, s0 ; CHECK-NEXT: bl __fixsfti ; CHECK-NEXT: movi v0.2s, #241, lsl #24 ; CHECK-NEXT: mov w8, #1895825407 ; CHECK-NEXT: mov x10, #34359738367 -; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: fcmp s8, s0 ; CHECK-NEXT: fmov s0, w8 ; CHECK-NEXT: mov x8, #-34359738368 @@ -144,9 +143,10 @@ define i100 @test_signed_i100_f32(float %f) nounwind { ; CHECK-NEXT: csel x8, x10, x8, gt ; CHECK-NEXT: csinv x9, x9, xzr, le ; CHECK-NEXT: fcmp s8, s8 +; CHECK-NEXT: ldr d8, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: csel x0, xzr, x9, vs ; CHECK-NEXT: csel x1, xzr, x8, vs -; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret %x = call i100 @llvm.fptosi.sat.i100.f32(float %f) ret i100 %x @@ -155,14 +155,13 @@ define i100 @test_signed_i100_f32(float %f) nounwind { define i128 @test_signed_i128_f32(float %f) nounwind { ; CHECK-LABEL: test_signed_i128_f32: ; CHECK: // %bb.0: -; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill +; CHECK-NEXT: str d8, [sp, #8] // 8-byte Folded Spill ; CHECK-NEXT: fmov s8, s0 ; CHECK-NEXT: bl __fixsfti ; CHECK-NEXT: movi v0.2s, #255, lsl #24 ; CHECK-NEXT: mov w8, #2130706431 ; CHECK-NEXT: mov x10, #9223372036854775807 -; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: fcmp s8, s0 ; CHECK-NEXT: fmov s0, w8 ; CHECK-NEXT: mov x8, #-9223372036854775808 @@ -172,9 +171,10 @@ define i128 @test_signed_i128_f32(float %f) nounwind { ; CHECK-NEXT: csel x8, x10, x8, gt ; CHECK-NEXT: csinv x9, x9, xzr, le ; CHECK-NEXT: fcmp s8, s8 +; CHECK-NEXT: ldr d8, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: csel x0, xzr, x9, vs ; CHECK-NEXT: csel x1, xzr, x8, vs -; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret %x = call i128 @llvm.fptosi.sat.i128.f32(float %f) ret i128 %x @@ -305,13 +305,12 @@ define i64 @test_signed_i64_f64(double %f) nounwind { define i100 @test_signed_i100_f64(double %f) nounwind { ; CHECK-LABEL: test_signed_i100_f64: ; CHECK: // %bb.0: -; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill +; CHECK-NEXT: str d8, [sp, #8] // 8-byte Folded Spill ; CHECK-NEXT: fmov d8, d0 ; CHECK-NEXT: bl __fixdfti ; CHECK-NEXT: mov x8, #-4170333254945079296 ; CHECK-NEXT: mov x10, #34359738367 -; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: fmov d0, x8 ; CHECK-NEXT: mov x8, #5053038781909696511 ; CHECK-NEXT: fcmp d8, d0 @@ -323,9 +322,10 @@ define i100 @test_signed_i100_f64(double %f) nounwind { ; CHECK-NEXT: csel x8, x10, x8, gt ; CHECK-NEXT: csinv x9, x9, xzr, le ; CHECK-NEXT: fcmp d8, d8 +; CHECK-NEXT: ldr d8, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: csel x0, xzr, x9, vs ; CHECK-NEXT: csel x1, xzr, x8, vs -; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret %x = call i100 @llvm.fptosi.sat.i100.f64(double %f) ret i100 %x @@ -334,13 +334,12 @@ define i100 @test_signed_i100_f64(double %f) nounwind { define i128 @test_signed_i128_f64(double %f) nounwind { ; CHECK-LABEL: test_signed_i128_f64: ; CHECK: // %bb.0: -; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill +; CHECK-NEXT: str d8, [sp, #8] // 8-byte Folded Spill ; CHECK-NEXT: fmov d8, d0 ; CHECK-NEXT: bl __fixdfti ; CHECK-NEXT: mov x8, #-4044232465378705408 ; CHECK-NEXT: mov x10, #9223372036854775807 -; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: fmov d0, x8 ; CHECK-NEXT: mov x8, #5179139571476070399 ; CHECK-NEXT: fcmp d8, d0 @@ -352,9 +351,10 @@ define i128 @test_signed_i128_f64(double %f) nounwind { ; CHECK-NEXT: csel x8, x10, x8, gt ; CHECK-NEXT: csinv x9, x9, xzr, le ; CHECK-NEXT: fcmp d8, d8 +; CHECK-NEXT: ldr d8, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: csel x0, xzr, x9, vs ; CHECK-NEXT: csel x1, xzr, x8, vs -; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret %x = call i128 @llvm.fptosi.sat.i128.f64(double %f) ret i128 %x @@ -568,15 +568,14 @@ define i64 @test_signed_i64_f16(half %f) nounwind { define i100 @test_signed_i100_f16(half %f) nounwind { ; CHECK-LABEL: test_signed_i100_f16: ; CHECK: // %bb.0: -; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill +; CHECK-NEXT: str d8, [sp, #8] // 8-byte Folded Spill ; CHECK-NEXT: fcvt s8, h0 -; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill ; CHECK-NEXT: fmov s0, s8 ; CHECK-NEXT: bl __fixsfti ; CHECK-NEXT: movi v0.2s, #241, lsl #24 ; CHECK-NEXT: mov w8, #1895825407 ; CHECK-NEXT: mov x10, #34359738367 -; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: fcmp s8, s0 ; CHECK-NEXT: fmov s0, w8 ; CHECK-NEXT: mov x8, #-34359738368 @@ -586,9 +585,10 @@ define i100 @test_signed_i100_f16(half %f) nounwind { ; CHECK-NEXT: csel x8, x10, x8, gt ; CHECK-NEXT: csinv x9, x9, xzr, le ; CHECK-NEXT: fcmp s8, s8 +; CHECK-NEXT: ldr d8, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: csel x0, xzr, x9, vs ; CHECK-NEXT: csel x1, xzr, x8, vs -; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret %x = call i100 @llvm.fptosi.sat.i100.f16(half %f) ret i100 %x @@ -597,15 +597,15 @@ define i100 @test_signed_i100_f16(half %f) nounwind { define i128 @test_signed_i128_f16(half %f) nounwind { ; CHECK-LABEL: test_signed_i128_f16: ; CHECK: // %bb.0: -; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill + +; CHECK-NEXT: str d8, [sp, #8] // 8-byte Folded Spill ; CHECK-NEXT: fcvt s8, h0 -; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill ; CHECK-NEXT: fmov s0, s8 ; CHECK-NEXT: bl __fixsfti ; CHECK-NEXT: movi v0.2s, #255, lsl #24 ; CHECK-NEXT: mov w8, #2130706431 ; CHECK-NEXT: mov x10, #9223372036854775807 -; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: fcmp s8, s0 ; CHECK-NEXT: fmov s0, w8 ; CHECK-NEXT: mov x8, #-9223372036854775808 @@ -615,9 +615,10 @@ define i128 @test_signed_i128_f16(half %f) nounwind { ; CHECK-NEXT: csel x8, x10, x8, gt ; CHECK-NEXT: csinv x9, x9, xzr, le ; CHECK-NEXT: fcmp s8, s8 +; CHECK-NEXT: ldr d8, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: csel x0, xzr, x9, vs ; CHECK-NEXT: csel x1, xzr, x8, vs -; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret %x = call i128 @llvm.fptosi.sat.i128.f16(half %f) ret i128 %x diff --git a/llvm/test/CodeGen/AArch64/fptosi-sat-vector.ll b/llvm/test/CodeGen/AArch64/fptosi-sat-vector.ll index 6e3026716..66895a65b 100644 --- a/llvm/test/CodeGen/AArch64/fptosi-sat-vector.ll +++ b/llvm/test/CodeGen/AArch64/fptosi-sat-vector.ll @@ -809,19 +809,19 @@ define <2 x i100> @test_signed_v2f32_v2i100(<2 x float> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #80 ; CHECK-NEXT: .cfi_def_cfa_offset 80 -; CHECK-NEXT: str d10, [sp, #16] // 8-byte Folded Spill -; CHECK-NEXT: stp d9, d8, [sp, #24] // 16-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #40] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #16] // 8-byte Folded Spill +; CHECK-NEXT: str d10, [sp, #24] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #32] // 16-byte Folded Spill ; CHECK-NEXT: stp x22, x21, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: stp x20, x19, [sp, #64] // 16-byte Folded Spill ; CHECK-NEXT: .cfi_offset w19, -8 ; CHECK-NEXT: .cfi_offset w20, -16 ; CHECK-NEXT: .cfi_offset w21, -24 ; CHECK-NEXT: .cfi_offset w22, -32 -; CHECK-NEXT: .cfi_offset w30, -40 -; CHECK-NEXT: .cfi_offset b8, -48 -; CHECK-NEXT: .cfi_offset b9, -56 -; CHECK-NEXT: .cfi_offset b10, -64 +; CHECK-NEXT: .cfi_offset b8, -40 +; CHECK-NEXT: .cfi_offset b9, -48 +; CHECK-NEXT: .cfi_offset b10, -56 +; CHECK-NEXT: .cfi_offset w30, -64 ; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0 ; CHECK-NEXT: mov s8, v0.s[1] ; CHECK-NEXT: str q0, [sp] // 16-byte Folded Spill @@ -847,14 +847,14 @@ define <2 x i100> @test_signed_v2f32_v2i100(<2 x float> %f) { ; CHECK-NEXT: ldr q0, [sp] // 16-byte Folded Reload ; CHECK-NEXT: mov x2, x19 ; CHECK-NEXT: mov x3, x20 -; CHECK-NEXT: ldr x30, [sp, #40] // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp, #16] // 8-byte Folded Reload ; CHECK-NEXT: ldp x20, x19, [sp, #64] // 16-byte Folded Reload ; CHECK-NEXT: fcmp s0, s9 -; CHECK-NEXT: ldp d9, d8, [sp, #24] // 16-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #32] // 16-byte Folded Reload ; CHECK-NEXT: csel x8, x21, x1, lt ; CHECK-NEXT: csel x9, xzr, x0, lt ; CHECK-NEXT: fcmp s0, s10 -; CHECK-NEXT: ldr d10, [sp, #16] // 8-byte Folded Reload +; CHECK-NEXT: ldr d10, [sp, #24] // 8-byte Folded Reload ; CHECK-NEXT: csinv x9, x9, xzr, le ; CHECK-NEXT: csel x8, x22, x8, gt ; CHECK-NEXT: fcmp s0, s0 @@ -875,19 +875,19 @@ define <2 x i128> @test_signed_v2f32_v2i128(<2 x float> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #80 ; CHECK-NEXT: .cfi_def_cfa_offset 80 -; CHECK-NEXT: str d10, [sp, #16] // 8-byte Folded Spill -; CHECK-NEXT: stp d9, d8, [sp, #24] // 16-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #40] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #16] // 8-byte Folded Spill +; CHECK-NEXT: str d10, [sp, #24] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #32] // 16-byte Folded Spill ; CHECK-NEXT: stp x22, x21, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: stp x20, x19, [sp, #64] // 16-byte Folded Spill ; CHECK-NEXT: .cfi_offset w19, -8 ; CHECK-NEXT: .cfi_offset w20, -16 ; CHECK-NEXT: .cfi_offset w21, -24 ; CHECK-NEXT: .cfi_offset w22, -32 -; CHECK-NEXT: .cfi_offset w30, -40 -; CHECK-NEXT: .cfi_offset b8, -48 -; CHECK-NEXT: .cfi_offset b9, -56 -; CHECK-NEXT: .cfi_offset b10, -64 +; CHECK-NEXT: .cfi_offset b8, -40 +; CHECK-NEXT: .cfi_offset b9, -48 +; CHECK-NEXT: .cfi_offset b10, -56 +; CHECK-NEXT: .cfi_offset w30, -64 ; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0 ; CHECK-NEXT: mov s8, v0.s[1] ; CHECK-NEXT: str q0, [sp] // 16-byte Folded Spill @@ -913,14 +913,14 @@ define <2 x i128> @test_signed_v2f32_v2i128(<2 x float> %f) { ; CHECK-NEXT: ldr q0, [sp] // 16-byte Folded Reload ; CHECK-NEXT: mov x2, x19 ; CHECK-NEXT: mov x3, x20 -; CHECK-NEXT: ldr x30, [sp, #40] // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp, #16] // 8-byte Folded Reload ; CHECK-NEXT: ldp x20, x19, [sp, #64] // 16-byte Folded Reload ; CHECK-NEXT: fcmp s0, s9 -; CHECK-NEXT: ldp d9, d8, [sp, #24] // 16-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #32] // 16-byte Folded Reload ; CHECK-NEXT: csel x8, x21, x1, lt ; CHECK-NEXT: csel x9, xzr, x0, lt ; CHECK-NEXT: fcmp s0, s10 -; CHECK-NEXT: ldr d10, [sp, #16] // 8-byte Folded Reload +; CHECK-NEXT: ldr d10, [sp, #24] // 8-byte Folded Reload ; CHECK-NEXT: csinv x9, x9, xzr, le ; CHECK-NEXT: csel x8, x22, x8, gt ; CHECK-NEXT: fcmp s0, s0 @@ -1081,9 +1081,9 @@ define <4 x i100> @test_signed_v4f32_v4i100(<4 x float> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #128 ; CHECK-NEXT: .cfi_def_cfa_offset 128 -; CHECK-NEXT: str d10, [sp, #32] // 8-byte Folded Spill -; CHECK-NEXT: stp d9, d8, [sp, #40] // 16-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #56] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #32] // 8-byte Folded Spill +; CHECK-NEXT: str d10, [sp, #40] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: stp x26, x25, [sp, #64] // 16-byte Folded Spill ; CHECK-NEXT: stp x24, x23, [sp, #80] // 16-byte Folded Spill ; CHECK-NEXT: stp x22, x21, [sp, #96] // 16-byte Folded Spill @@ -1096,10 +1096,10 @@ define <4 x i100> @test_signed_v4f32_v4i100(<4 x float> %f) { ; CHECK-NEXT: .cfi_offset w24, -48 ; CHECK-NEXT: .cfi_offset w25, -56 ; CHECK-NEXT: .cfi_offset w26, -64 -; CHECK-NEXT: .cfi_offset w30, -72 -; CHECK-NEXT: .cfi_offset b8, -80 -; CHECK-NEXT: .cfi_offset b9, -88 -; CHECK-NEXT: .cfi_offset b10, -96 +; CHECK-NEXT: .cfi_offset b8, -72 +; CHECK-NEXT: .cfi_offset b9, -80 +; CHECK-NEXT: .cfi_offset b10, -88 +; CHECK-NEXT: .cfi_offset w30, -96 ; CHECK-NEXT: mov s8, v0.s[1] ; CHECK-NEXT: str q0, [sp, #16] // 16-byte Folded Spill ; CHECK-NEXT: fmov s0, s8 @@ -1160,18 +1160,18 @@ define <4 x i100> @test_signed_v4f32_v4i100(<4 x float> %f) { ; CHECK-NEXT: csel x8, x25, x1, lt ; CHECK-NEXT: csel x9, xzr, x0, lt ; CHECK-NEXT: fcmp s0, s10 -; CHECK-NEXT: ldr x30, [sp, #56] // 8-byte Folded Reload +; CHECK-NEXT: ldr d10, [sp, #40] // 8-byte Folded Reload ; CHECK-NEXT: ldp x22, x21, [sp, #96] // 16-byte Folded Reload ; CHECK-NEXT: csinv x9, x9, xzr, le ; CHECK-NEXT: csel x8, x26, x8, gt ; CHECK-NEXT: fcmp s0, s0 -; CHECK-NEXT: ldr d10, [sp, #32] // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp, #32] // 8-byte Folded Reload ; CHECK-NEXT: ldp x24, x23, [sp, #80] // 16-byte Folded Reload ; CHECK-NEXT: csel x9, xzr, x9, vs ; CHECK-NEXT: csel x1, xzr, x8, vs ; CHECK-NEXT: ldp x26, x25, [sp, #64] // 16-byte Folded Reload ; CHECK-NEXT: fmov d0, x9 -; CHECK-NEXT: ldp d9, d8, [sp, #40] // 16-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #48] // 16-byte Folded Reload ; CHECK-NEXT: mov v0.d[1], x1 ; CHECK-NEXT: fmov x0, d0 ; CHECK-NEXT: add sp, sp, #128 @@ -1185,9 +1185,9 @@ define <4 x i128> @test_signed_v4f32_v4i128(<4 x float> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #128 ; CHECK-NEXT: .cfi_def_cfa_offset 128 -; CHECK-NEXT: str d10, [sp, #32] // 8-byte Folded Spill -; CHECK-NEXT: stp d9, d8, [sp, #40] // 16-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #56] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #32] // 8-byte Folded Spill +; CHECK-NEXT: str d10, [sp, #40] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: stp x26, x25, [sp, #64] // 16-byte Folded Spill ; CHECK-NEXT: stp x24, x23, [sp, #80] // 16-byte Folded Spill ; CHECK-NEXT: stp x22, x21, [sp, #96] // 16-byte Folded Spill @@ -1200,10 +1200,10 @@ define <4 x i128> @test_signed_v4f32_v4i128(<4 x float> %f) { ; CHECK-NEXT: .cfi_offset w24, -48 ; CHECK-NEXT: .cfi_offset w25, -56 ; CHECK-NEXT: .cfi_offset w26, -64 -; CHECK-NEXT: .cfi_offset w30, -72 -; CHECK-NEXT: .cfi_offset b8, -80 -; CHECK-NEXT: .cfi_offset b9, -88 -; CHECK-NEXT: .cfi_offset b10, -96 +; CHECK-NEXT: .cfi_offset b8, -72 +; CHECK-NEXT: .cfi_offset b9, -80 +; CHECK-NEXT: .cfi_offset b10, -88 +; CHECK-NEXT: .cfi_offset w30, -96 ; CHECK-NEXT: mov s8, v0.s[1] ; CHECK-NEXT: str q0, [sp, #16] // 16-byte Folded Spill ; CHECK-NEXT: fmov s0, s8 @@ -1264,18 +1264,18 @@ define <4 x i128> @test_signed_v4f32_v4i128(<4 x float> %f) { ; CHECK-NEXT: csel x8, x25, x1, lt ; CHECK-NEXT: csel x9, xzr, x0, lt ; CHECK-NEXT: fcmp s0, s10 -; CHECK-NEXT: ldr x30, [sp, #56] // 8-byte Folded Reload +; CHECK-NEXT: ldr d10, [sp, #40] // 8-byte Folded Reload ; CHECK-NEXT: ldp x22, x21, [sp, #96] // 16-byte Folded Reload ; CHECK-NEXT: csinv x9, x9, xzr, le ; CHECK-NEXT: csel x8, x26, x8, gt ; CHECK-NEXT: fcmp s0, s0 -; CHECK-NEXT: ldr d10, [sp, #32] // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp, #32] // 8-byte Folded Reload ; CHECK-NEXT: ldp x24, x23, [sp, #80] // 16-byte Folded Reload ; CHECK-NEXT: csel x9, xzr, x9, vs ; CHECK-NEXT: csel x1, xzr, x8, vs ; CHECK-NEXT: ldp x26, x25, [sp, #64] // 16-byte Folded Reload ; CHECK-NEXT: fmov d0, x9 -; CHECK-NEXT: ldp d9, d8, [sp, #40] // 16-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #48] // 16-byte Folded Reload ; CHECK-NEXT: mov v0.d[1], x1 ; CHECK-NEXT: fmov x0, d0 ; CHECK-NEXT: add sp, sp, #128 @@ -1467,19 +1467,19 @@ define <2 x i100> @test_signed_v2f64_v2i100(<2 x double> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #80 ; CHECK-NEXT: .cfi_def_cfa_offset 80 -; CHECK-NEXT: str d10, [sp, #16] // 8-byte Folded Spill -; CHECK-NEXT: stp d9, d8, [sp, #24] // 16-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #40] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #16] // 8-byte Folded Spill +; CHECK-NEXT: str d10, [sp, #24] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #32] // 16-byte Folded Spill ; CHECK-NEXT: stp x22, x21, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: stp x20, x19, [sp, #64] // 16-byte Folded Spill ; CHECK-NEXT: .cfi_offset w19, -8 ; CHECK-NEXT: .cfi_offset w20, -16 ; CHECK-NEXT: .cfi_offset w21, -24 ; CHECK-NEXT: .cfi_offset w22, -32 -; CHECK-NEXT: .cfi_offset w30, -40 -; CHECK-NEXT: .cfi_offset b8, -48 -; CHECK-NEXT: .cfi_offset b9, -56 -; CHECK-NEXT: .cfi_offset b10, -64 +; CHECK-NEXT: .cfi_offset b8, -40 +; CHECK-NEXT: .cfi_offset b9, -48 +; CHECK-NEXT: .cfi_offset b10, -56 +; CHECK-NEXT: .cfi_offset w30, -64 ; CHECK-NEXT: mov d8, v0.d[1] ; CHECK-NEXT: str q0, [sp] // 16-byte Folded Spill ; CHECK-NEXT: fmov d0, d8 @@ -1505,14 +1505,14 @@ define <2 x i100> @test_signed_v2f64_v2i100(<2 x double> %f) { ; CHECK-NEXT: ldr q0, [sp] // 16-byte Folded Reload ; CHECK-NEXT: mov x2, x19 ; CHECK-NEXT: mov x3, x20 -; CHECK-NEXT: ldr x30, [sp, #40] // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp, #16] // 8-byte Folded Reload ; CHECK-NEXT: ldp x20, x19, [sp, #64] // 16-byte Folded Reload ; CHECK-NEXT: fcmp d0, d9 -; CHECK-NEXT: ldp d9, d8, [sp, #24] // 16-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #32] // 16-byte Folded Reload ; CHECK-NEXT: csel x8, x21, x1, lt ; CHECK-NEXT: csel x9, xzr, x0, lt ; CHECK-NEXT: fcmp d0, d10 -; CHECK-NEXT: ldr d10, [sp, #16] // 8-byte Folded Reload +; CHECK-NEXT: ldr d10, [sp, #24] // 8-byte Folded Reload ; CHECK-NEXT: csinv x9, x9, xzr, le ; CHECK-NEXT: csel x8, x22, x8, gt ; CHECK-NEXT: fcmp d0, d0 @@ -1533,19 +1533,19 @@ define <2 x i128> @test_signed_v2f64_v2i128(<2 x double> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #80 ; CHECK-NEXT: .cfi_def_cfa_offset 80 -; CHECK-NEXT: str d10, [sp, #16] // 8-byte Folded Spill -; CHECK-NEXT: stp d9, d8, [sp, #24] // 16-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #40] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #16] // 8-byte Folded Spill +; CHECK-NEXT: str d10, [sp, #24] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #32] // 16-byte Folded Spill ; CHECK-NEXT: stp x22, x21, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: stp x20, x19, [sp, #64] // 16-byte Folded Spill ; CHECK-NEXT: .cfi_offset w19, -8 ; CHECK-NEXT: .cfi_offset w20, -16 ; CHECK-NEXT: .cfi_offset w21, -24 ; CHECK-NEXT: .cfi_offset w22, -32 -; CHECK-NEXT: .cfi_offset w30, -40 -; CHECK-NEXT: .cfi_offset b8, -48 -; CHECK-NEXT: .cfi_offset b9, -56 -; CHECK-NEXT: .cfi_offset b10, -64 +; CHECK-NEXT: .cfi_offset b8, -40 +; CHECK-NEXT: .cfi_offset b9, -48 +; CHECK-NEXT: .cfi_offset b10, -56 +; CHECK-NEXT: .cfi_offset w30, -64 ; CHECK-NEXT: mov d8, v0.d[1] ; CHECK-NEXT: str q0, [sp] // 16-byte Folded Spill ; CHECK-NEXT: fmov d0, d8 @@ -1571,14 +1571,14 @@ define <2 x i128> @test_signed_v2f64_v2i128(<2 x double> %f) { ; CHECK-NEXT: ldr q0, [sp] // 16-byte Folded Reload ; CHECK-NEXT: mov x2, x19 ; CHECK-NEXT: mov x3, x20 -; CHECK-NEXT: ldr x30, [sp, #40] // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp, #16] // 8-byte Folded Reload ; CHECK-NEXT: ldp x20, x19, [sp, #64] // 16-byte Folded Reload ; CHECK-NEXT: fcmp d0, d9 -; CHECK-NEXT: ldp d9, d8, [sp, #24] // 16-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #32] // 16-byte Folded Reload ; CHECK-NEXT: csel x8, x21, x1, lt ; CHECK-NEXT: csel x9, xzr, x0, lt ; CHECK-NEXT: fcmp d0, d10 -; CHECK-NEXT: ldr d10, [sp, #16] // 8-byte Folded Reload +; CHECK-NEXT: ldr d10, [sp, #24] // 8-byte Folded Reload ; CHECK-NEXT: csinv x9, x9, xzr, le ; CHECK-NEXT: csel x8, x22, x8, gt ; CHECK-NEXT: fcmp d0, d0 @@ -1833,9 +1833,9 @@ define <4 x i100> @test_signed_v4f16_v4i100(<4 x half> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #112 ; CHECK-NEXT: .cfi_def_cfa_offset 112 -; CHECK-NEXT: str d10, [sp, #16] // 8-byte Folded Spill -; CHECK-NEXT: stp d9, d8, [sp, #24] // 16-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #40] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #16] // 8-byte Folded Spill +; CHECK-NEXT: str d10, [sp, #24] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #32] // 16-byte Folded Spill ; CHECK-NEXT: stp x26, x25, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: stp x24, x23, [sp, #64] // 16-byte Folded Spill ; CHECK-NEXT: stp x22, x21, [sp, #80] // 16-byte Folded Spill @@ -1848,10 +1848,10 @@ define <4 x i100> @test_signed_v4f16_v4i100(<4 x half> %f) { ; CHECK-NEXT: .cfi_offset w24, -48 ; CHECK-NEXT: .cfi_offset w25, -56 ; CHECK-NEXT: .cfi_offset w26, -64 -; CHECK-NEXT: .cfi_offset w30, -72 -; CHECK-NEXT: .cfi_offset b8, -80 -; CHECK-NEXT: .cfi_offset b9, -88 -; CHECK-NEXT: .cfi_offset b10, -96 +; CHECK-NEXT: .cfi_offset b8, -72 +; CHECK-NEXT: .cfi_offset b9, -80 +; CHECK-NEXT: .cfi_offset b10, -88 +; CHECK-NEXT: .cfi_offset w30, -96 ; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0 ; CHECK-NEXT: mov h1, v0.h[1] ; CHECK-NEXT: str q0, [sp] // 16-byte Folded Spill @@ -1918,16 +1918,16 @@ define <4 x i100> @test_signed_v4f16_v4i100(<4 x half> %f) { ; CHECK-NEXT: csinv x9, x9, xzr, le ; CHECK-NEXT: csel x8, x26, x8, gt ; CHECK-NEXT: fcmp s8, s8 -; CHECK-NEXT: ldr x30, [sp, #40] // 8-byte Folded Reload +; CHECK-NEXT: ldr d10, [sp, #24] // 8-byte Folded Reload ; CHECK-NEXT: ldp x22, x21, [sp, #80] // 16-byte Folded Reload ; CHECK-NEXT: csel x9, xzr, x9, vs ; CHECK-NEXT: csel x1, xzr, x8, vs ; CHECK-NEXT: ldp x24, x23, [sp, #64] // 16-byte Folded Reload ; CHECK-NEXT: fmov d0, x9 -; CHECK-NEXT: ldr d10, [sp, #16] // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp, #16] // 8-byte Folded Reload ; CHECK-NEXT: ldp x26, x25, [sp, #48] // 16-byte Folded Reload ; CHECK-NEXT: mov v0.d[1], x1 -; CHECK-NEXT: ldp d9, d8, [sp, #24] // 16-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #32] // 16-byte Folded Reload ; CHECK-NEXT: fmov x0, d0 ; CHECK-NEXT: add sp, sp, #112 ; CHECK-NEXT: ret @@ -1940,9 +1940,9 @@ define <4 x i128> @test_signed_v4f16_v4i128(<4 x half> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #112 ; CHECK-NEXT: .cfi_def_cfa_offset 112 -; CHECK-NEXT: str d10, [sp, #16] // 8-byte Folded Spill -; CHECK-NEXT: stp d9, d8, [sp, #24] // 16-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #40] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #16] // 8-byte Folded Spill +; CHECK-NEXT: str d10, [sp, #24] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #32] // 16-byte Folded Spill ; CHECK-NEXT: stp x26, x25, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: stp x24, x23, [sp, #64] // 16-byte Folded Spill ; CHECK-NEXT: stp x22, x21, [sp, #80] // 16-byte Folded Spill @@ -1955,10 +1955,10 @@ define <4 x i128> @test_signed_v4f16_v4i128(<4 x half> %f) { ; CHECK-NEXT: .cfi_offset w24, -48 ; CHECK-NEXT: .cfi_offset w25, -56 ; CHECK-NEXT: .cfi_offset w26, -64 -; CHECK-NEXT: .cfi_offset w30, -72 -; CHECK-NEXT: .cfi_offset b8, -80 -; CHECK-NEXT: .cfi_offset b9, -88 -; CHECK-NEXT: .cfi_offset b10, -96 +; CHECK-NEXT: .cfi_offset b8, -72 +; CHECK-NEXT: .cfi_offset b9, -80 +; CHECK-NEXT: .cfi_offset b10, -88 +; CHECK-NEXT: .cfi_offset w30, -96 ; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0 ; CHECK-NEXT: mov h1, v0.h[1] ; CHECK-NEXT: str q0, [sp] // 16-byte Folded Spill @@ -2025,16 +2025,16 @@ define <4 x i128> @test_signed_v4f16_v4i128(<4 x half> %f) { ; CHECK-NEXT: csinv x9, x9, xzr, le ; CHECK-NEXT: csel x8, x26, x8, gt ; CHECK-NEXT: fcmp s8, s8 -; CHECK-NEXT: ldr x30, [sp, #40] // 8-byte Folded Reload +; CHECK-NEXT: ldr d10, [sp, #24] // 8-byte Folded Reload ; CHECK-NEXT: ldp x22, x21, [sp, #80] // 16-byte Folded Reload ; CHECK-NEXT: csel x9, xzr, x9, vs ; CHECK-NEXT: csel x1, xzr, x8, vs ; CHECK-NEXT: ldp x24, x23, [sp, #64] // 16-byte Folded Reload ; CHECK-NEXT: fmov d0, x9 -; CHECK-NEXT: ldr d10, [sp, #16] // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp, #16] // 8-byte Folded Reload ; CHECK-NEXT: ldp x26, x25, [sp, #48] // 16-byte Folded Reload ; CHECK-NEXT: mov v0.d[1], x1 -; CHECK-NEXT: ldp d9, d8, [sp, #24] // 16-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #32] // 16-byte Folded Reload ; CHECK-NEXT: fmov x0, d0 ; CHECK-NEXT: add sp, sp, #112 ; CHECK-NEXT: ret @@ -2581,9 +2581,9 @@ define <8 x i100> @test_signed_v8f16_v8i100(<8 x half> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #192 ; CHECK-NEXT: .cfi_def_cfa_offset 192 -; CHECK-NEXT: str d10, [sp, #64] // 8-byte Folded Spill -; CHECK-NEXT: stp d9, d8, [sp, #80] // 16-byte Folded Spill -; CHECK-NEXT: stp x29, x30, [sp, #96] // 16-byte Folded Spill +; CHECK-NEXT: stp x29, x30, [sp, #64] // 16-byte Folded Spill +; CHECK-NEXT: str d10, [sp, #80] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #96] // 16-byte Folded Spill ; CHECK-NEXT: stp x28, x27, [sp, #112] // 16-byte Folded Spill ; CHECK-NEXT: stp x26, x25, [sp, #128] // 16-byte Folded Spill ; CHECK-NEXT: stp x24, x23, [sp, #144] // 16-byte Folded Spill @@ -2599,11 +2599,11 @@ define <8 x i100> @test_signed_v8f16_v8i100(<8 x half> %f) { ; CHECK-NEXT: .cfi_offset w26, -64 ; CHECK-NEXT: .cfi_offset w27, -72 ; CHECK-NEXT: .cfi_offset w28, -80 -; CHECK-NEXT: .cfi_offset w30, -88 -; CHECK-NEXT: .cfi_offset w29, -96 -; CHECK-NEXT: .cfi_offset b8, -104 -; CHECK-NEXT: .cfi_offset b9, -112 -; CHECK-NEXT: .cfi_offset b10, -128 +; CHECK-NEXT: .cfi_offset b8, -88 +; CHECK-NEXT: .cfi_offset b9, -96 +; CHECK-NEXT: .cfi_offset b10, -112 +; CHECK-NEXT: .cfi_offset w30, -120 +; CHECK-NEXT: .cfi_offset w29, -128 ; CHECK-NEXT: str q0, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: mov x19, x8 ; CHECK-NEXT: ext v0.16b, v0.16b, v0.16b, #8 @@ -2631,7 +2631,7 @@ define <8 x i100> @test_signed_v8f16_v8i100(<8 x half> %f) { ; CHECK-NEXT: fmov s0, s8 ; CHECK-NEXT: str x8, [sp, #24] // 8-byte Folded Spill ; CHECK-NEXT: csel x8, xzr, x9, vs -; CHECK-NEXT: str x8, [sp, #72] // 8-byte Folded Spill +; CHECK-NEXT: str x8, [sp, #88] // 8-byte Folded Spill ; CHECK-NEXT: bl __fixsfti ; CHECK-NEXT: fcmp s8, s10 ; CHECK-NEXT: ldr q0, [sp, #32] // 16-byte Folded Reload @@ -2750,7 +2750,7 @@ define <8 x i100> @test_signed_v8f16_v8i100(<8 x half> %f) { ; CHECK-NEXT: lsr x9, x22, #28 ; CHECK-NEXT: ldr d1, [sp, #24] // 8-byte Folded Reload ; CHECK-NEXT: stur x8, [x19, #33] -; CHECK-NEXT: ldr x11, [sp, #72] // 8-byte Folded Reload +; CHECK-NEXT: ldr x11, [sp, #88] // 8-byte Folded Reload ; CHECK-NEXT: extr x18, x29, x12, #28 ; CHECK-NEXT: mov v0.d[1], x22 ; CHECK-NEXT: bfi x21, x12, #36, #28 @@ -2776,9 +2776,9 @@ define <8 x i100> @test_signed_v8f16_v8i100(<8 x half> %f) { ; CHECK-NEXT: ldp x24, x23, [sp, #144] // 16-byte Folded Reload ; CHECK-NEXT: ldp x26, x25, [sp, #128] // 16-byte Folded Reload ; CHECK-NEXT: ldp x28, x27, [sp, #112] // 16-byte Folded Reload -; CHECK-NEXT: ldp x29, x30, [sp, #96] // 16-byte Folded Reload -; CHECK-NEXT: ldp d9, d8, [sp, #80] // 16-byte Folded Reload -; CHECK-NEXT: ldr d10, [sp, #64] // 8-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #96] // 16-byte Folded Reload +; CHECK-NEXT: ldp x29, x30, [sp, #64] // 16-byte Folded Reload +; CHECK-NEXT: ldr d10, [sp, #80] // 8-byte Folded Reload ; CHECK-NEXT: add sp, sp, #192 ; CHECK-NEXT: ret %x = call <8 x i100> @llvm.fptosi.sat.v8f16.v8i100(<8 x half> %f) @@ -2790,9 +2790,9 @@ define <8 x i128> @test_signed_v8f16_v8i128(<8 x half> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #192 ; CHECK-NEXT: .cfi_def_cfa_offset 192 -; CHECK-NEXT: str d10, [sp, #64] // 8-byte Folded Spill -; CHECK-NEXT: stp d9, d8, [sp, #80] // 16-byte Folded Spill -; CHECK-NEXT: stp x29, x30, [sp, #96] // 16-byte Folded Spill +; CHECK-NEXT: stp x29, x30, [sp, #64] // 16-byte Folded Spill +; CHECK-NEXT: str d10, [sp, #80] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #96] // 16-byte Folded Spill ; CHECK-NEXT: stp x28, x27, [sp, #112] // 16-byte Folded Spill ; CHECK-NEXT: stp x26, x25, [sp, #128] // 16-byte Folded Spill ; CHECK-NEXT: stp x24, x23, [sp, #144] // 16-byte Folded Spill @@ -2808,11 +2808,11 @@ define <8 x i128> @test_signed_v8f16_v8i128(<8 x half> %f) { ; CHECK-NEXT: .cfi_offset w26, -64 ; CHECK-NEXT: .cfi_offset w27, -72 ; CHECK-NEXT: .cfi_offset w28, -80 -; CHECK-NEXT: .cfi_offset w30, -88 -; CHECK-NEXT: .cfi_offset w29, -96 -; CHECK-NEXT: .cfi_offset b8, -104 -; CHECK-NEXT: .cfi_offset b9, -112 -; CHECK-NEXT: .cfi_offset b10, -128 +; CHECK-NEXT: .cfi_offset b8, -88 +; CHECK-NEXT: .cfi_offset b9, -96 +; CHECK-NEXT: .cfi_offset b10, -112 +; CHECK-NEXT: .cfi_offset w30, -120 +; CHECK-NEXT: .cfi_offset w29, -128 ; CHECK-NEXT: str q0, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: mov x19, x8 ; CHECK-NEXT: ext v0.16b, v0.16b, v0.16b, #8 @@ -2837,7 +2837,7 @@ define <8 x i128> @test_signed_v8f16_v8i128(<8 x half> %f) { ; CHECK-NEXT: fcvt s8, h0 ; CHECK-NEXT: csel x8, xzr, x8, vs ; CHECK-NEXT: fmov s0, s8 -; CHECK-NEXT: str x8, [sp, #72] // 8-byte Folded Spill +; CHECK-NEXT: str x8, [sp, #88] // 8-byte Folded Spill ; CHECK-NEXT: csel x8, xzr, x9, vs ; CHECK-NEXT: str x8, [sp, #24] // 8-byte Folded Spill ; CHECK-NEXT: bl __fixsfti @@ -2951,16 +2951,16 @@ define <8 x i128> @test_signed_v8f16_v8i128(<8 x half> %f) { ; CHECK-NEXT: str x8, [x19, #80] ; CHECK-NEXT: ldr x8, [sp, #24] // 8-byte Folded Reload ; CHECK-NEXT: str x8, [x19, #72] -; CHECK-NEXT: ldr x8, [sp, #72] // 8-byte Folded Reload +; CHECK-NEXT: ldr x8, [sp, #88] // 8-byte Folded Reload ; CHECK-NEXT: str x8, [x19, #64] ; CHECK-NEXT: ldp x20, x19, [sp, #176] // 16-byte Folded Reload ; CHECK-NEXT: ldp x22, x21, [sp, #160] // 16-byte Folded Reload ; CHECK-NEXT: ldp x24, x23, [sp, #144] // 16-byte Folded Reload ; CHECK-NEXT: ldp x26, x25, [sp, #128] // 16-byte Folded Reload ; CHECK-NEXT: ldp x28, x27, [sp, #112] // 16-byte Folded Reload -; CHECK-NEXT: ldp x29, x30, [sp, #96] // 16-byte Folded Reload -; CHECK-NEXT: ldp d9, d8, [sp, #80] // 16-byte Folded Reload -; CHECK-NEXT: ldr d10, [sp, #64] // 8-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #96] // 16-byte Folded Reload +; CHECK-NEXT: ldp x29, x30, [sp, #64] // 16-byte Folded Reload +; CHECK-NEXT: ldr d10, [sp, #80] // 8-byte Folded Reload ; CHECK-NEXT: add sp, sp, #192 ; CHECK-NEXT: ret %x = call <8 x i128> @llvm.fptosi.sat.v8f16.v8i128(<8 x half> %f) diff --git a/llvm/test/CodeGen/AArch64/fptoui-sat-scalar.ll b/llvm/test/CodeGen/AArch64/fptoui-sat-scalar.ll index e1148a517..934ec075b 100644 --- a/llvm/test/CodeGen/AArch64/fptoui-sat-scalar.ll +++ b/llvm/test/CodeGen/AArch64/fptoui-sat-scalar.ll @@ -109,21 +109,22 @@ define i64 @test_unsigned_i64_f32(float %f) nounwind { define i100 @test_unsigned_i100_f32(float %f) nounwind { ; CHECK-LABEL: test_unsigned_i100_f32: ; CHECK: // %bb.0: -; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill +; CHECK-NEXT: str d8, [sp, #8] // 8-byte Folded Spill ; CHECK-NEXT: fmov s8, s0 ; CHECK-NEXT: bl __fixunssfti ; CHECK-NEXT: mov w8, #1904214015 ; CHECK-NEXT: fcmp s8, #0.0 -; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload + ; CHECK-NEXT: mov x10, #68719476735 ; CHECK-NEXT: fmov s0, w8 ; CHECK-NEXT: csel x8, xzr, x0, lt ; CHECK-NEXT: csel x9, xzr, x1, lt ; CHECK-NEXT: fcmp s8, s0 +; CHECK-NEXT: ldr d8, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: csel x1, x10, x9, gt ; CHECK-NEXT: csinv x0, x8, xzr, le -; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret %x = call i100 @llvm.fptoui.sat.i100.f32(float %f) ret i100 %x @@ -132,20 +133,21 @@ define i100 @test_unsigned_i100_f32(float %f) nounwind { define i128 @test_unsigned_i128_f32(float %f) nounwind { ; CHECK-LABEL: test_unsigned_i128_f32: ; CHECK: // %bb.0: -; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill +; CHECK-NEXT: str d8, [sp, #8] // 8-byte Folded Spill ; CHECK-NEXT: fmov s8, s0 ; CHECK-NEXT: bl __fixunssfti ; CHECK-NEXT: mov w8, #2139095039 ; CHECK-NEXT: fcmp s8, #0.0 -; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload + ; CHECK-NEXT: fmov s0, w8 ; CHECK-NEXT: csel x8, xzr, x1, lt ; CHECK-NEXT: csel x9, xzr, x0, lt ; CHECK-NEXT: fcmp s8, s0 +; CHECK-NEXT: ldr d8, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: csinv x0, x9, xzr, le ; CHECK-NEXT: csinv x1, x8, xzr, le -; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret %x = call i128 @llvm.fptoui.sat.i128.f32(float %f) ret i128 %x @@ -258,21 +260,22 @@ define i64 @test_unsigned_i64_f64(double %f) nounwind { define i100 @test_unsigned_i100_f64(double %f) nounwind { ; CHECK-LABEL: test_unsigned_i100_f64: ; CHECK: // %bb.0: -; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill +; CHECK-NEXT: str d8, [sp, #8] // 8-byte Folded Spill ; CHECK-NEXT: fmov d8, d0 ; CHECK-NEXT: bl __fixunsdfti ; CHECK-NEXT: mov x8, #5057542381537067007 ; CHECK-NEXT: fcmp d8, #0.0 -; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload + ; CHECK-NEXT: mov x10, #68719476735 ; CHECK-NEXT: fmov d0, x8 ; CHECK-NEXT: csel x8, xzr, x0, lt ; CHECK-NEXT: csel x9, xzr, x1, lt ; CHECK-NEXT: fcmp d8, d0 +; CHECK-NEXT: ldr d8, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: csel x1, x10, x9, gt ; CHECK-NEXT: csinv x0, x8, xzr, le -; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret %x = call i100 @llvm.fptoui.sat.i100.f64(double %f) ret i100 %x @@ -281,20 +284,21 @@ define i100 @test_unsigned_i100_f64(double %f) nounwind { define i128 @test_unsigned_i128_f64(double %f) nounwind { ; CHECK-LABEL: test_unsigned_i128_f64: ; CHECK: // %bb.0: -; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill +; CHECK-NEXT: str d8, [sp, #8] // 8-byte Folded Spill ; CHECK-NEXT: fmov d8, d0 ; CHECK-NEXT: bl __fixunsdfti ; CHECK-NEXT: mov x8, #5183643171103440895 ; CHECK-NEXT: fcmp d8, #0.0 -; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload + ; CHECK-NEXT: fmov d0, x8 ; CHECK-NEXT: csel x8, xzr, x1, lt ; CHECK-NEXT: csel x9, xzr, x0, lt ; CHECK-NEXT: fcmp d8, d0 +; CHECK-NEXT: ldr d8, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: csinv x0, x9, xzr, le ; CHECK-NEXT: csinv x1, x8, xzr, le -; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret %x = call i128 @llvm.fptoui.sat.i128.f64(double %f) ret i128 %x @@ -472,22 +476,24 @@ define i64 @test_unsigned_i64_f16(half %f) nounwind { define i100 @test_unsigned_i100_f16(half %f) nounwind { ; CHECK-LABEL: test_unsigned_i100_f16: ; CHECK: // %bb.0: -; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill + +; CHECK-NEXT: str d8, [sp, #8] // 8-byte Folded Spill ; CHECK-NEXT: fcvt s8, h0 -; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill ; CHECK-NEXT: fmov s0, s8 ; CHECK-NEXT: bl __fixunssfti ; CHECK-NEXT: mov w8, #1904214015 ; CHECK-NEXT: fcmp s8, #0.0 -; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload + ; CHECK-NEXT: mov x10, #68719476735 ; CHECK-NEXT: fmov s0, w8 ; CHECK-NEXT: csel x8, xzr, x0, lt ; CHECK-NEXT: csel x9, xzr, x1, lt ; CHECK-NEXT: fcmp s8, s0 +; CHECK-NEXT: ldr d8, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: csel x1, x10, x9, gt ; CHECK-NEXT: csinv x0, x8, xzr, le -; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret %x = call i100 @llvm.fptoui.sat.i100.f16(half %f) ret i100 %x @@ -496,21 +502,23 @@ define i100 @test_unsigned_i100_f16(half %f) nounwind { define i128 @test_unsigned_i128_f16(half %f) nounwind { ; CHECK-LABEL: test_unsigned_i128_f16: ; CHECK: // %bb.0: -; CHECK-NEXT: str d8, [sp, #-16]! // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill + +; CHECK-NEXT: str d8, [sp, #8] // 8-byte Folded Spill ; CHECK-NEXT: fcvt s8, h0 -; CHECK-NEXT: str x30, [sp, #8] // 8-byte Folded Spill ; CHECK-NEXT: fmov s0, s8 ; CHECK-NEXT: bl __fixunssfti ; CHECK-NEXT: mov w8, #2139095039 ; CHECK-NEXT: fcmp s8, #0.0 -; CHECK-NEXT: ldr x30, [sp, #8] // 8-byte Folded Reload + ; CHECK-NEXT: fmov s0, w8 ; CHECK-NEXT: csel x8, xzr, x1, lt ; CHECK-NEXT: csel x9, xzr, x0, lt ; CHECK-NEXT: fcmp s8, s0 +; CHECK-NEXT: ldr d8, [sp, #8] // 8-byte Folded Reload ; CHECK-NEXT: csinv x0, x9, xzr, le ; CHECK-NEXT: csinv x1, x8, xzr, le -; CHECK-NEXT: ldr d8, [sp], #16 // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret %x = call i128 @llvm.fptoui.sat.i128.f16(half %f) ret i128 %x diff --git a/llvm/test/CodeGen/AArch64/fptoui-sat-vector.ll b/llvm/test/CodeGen/AArch64/fptoui-sat-vector.ll index 35b78615a..2d02a8840 100644 --- a/llvm/test/CodeGen/AArch64/fptoui-sat-vector.ll +++ b/llvm/test/CodeGen/AArch64/fptoui-sat-vector.ll @@ -723,15 +723,16 @@ define <2 x i100> @test_unsigned_v2f32_v2i100(<2 x float> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #64 ; CHECK-NEXT: .cfi_def_cfa_offset 64 -; CHECK-NEXT: stp d9, d8, [sp, #16] // 16-byte Folded Spill -; CHECK-NEXT: stp x30, x21, [sp, #32] // 16-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #16] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #24] // 16-byte Folded Spill +; CHECK-NEXT: str x21, [sp, #40] // 8-byte Folded Spill ; CHECK-NEXT: stp x20, x19, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: .cfi_offset w19, -8 ; CHECK-NEXT: .cfi_offset w20, -16 ; CHECK-NEXT: .cfi_offset w21, -24 -; CHECK-NEXT: .cfi_offset w30, -32 -; CHECK-NEXT: .cfi_offset b8, -40 -; CHECK-NEXT: .cfi_offset b9, -48 +; CHECK-NEXT: .cfi_offset b8, -32 +; CHECK-NEXT: .cfi_offset b9, -40 +; CHECK-NEXT: .cfi_offset w30, -48 ; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0 ; CHECK-NEXT: mov s8, v0.s[1] ; CHECK-NEXT: str q0, [sp] // 16-byte Folded Spill @@ -752,15 +753,16 @@ define <2 x i100> @test_unsigned_v2f32_v2i100(<2 x float> %f) { ; CHECK-NEXT: ldr q0, [sp] // 16-byte Folded Reload ; CHECK-NEXT: mov x2, x20 ; CHECK-NEXT: mov x3, x19 +; CHECK-NEXT: ldr x30, [sp, #16] // 8-byte Folded Reload ; CHECK-NEXT: ldp x20, x19, [sp, #48] // 16-byte Folded Reload ; CHECK-NEXT: fcmp s0, #0.0 ; CHECK-NEXT: csel x8, xzr, x0, lt ; CHECK-NEXT: csel x9, xzr, x1, lt ; CHECK-NEXT: fcmp s0, s9 -; CHECK-NEXT: ldp d9, d8, [sp, #16] // 16-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #24] // 16-byte Folded Reload ; CHECK-NEXT: csinv x8, x8, xzr, le ; CHECK-NEXT: csel x1, x21, x9, gt -; CHECK-NEXT: ldp x30, x21, [sp, #32] // 16-byte Folded Reload +; CHECK-NEXT: ldr x21, [sp, #40] // 8-byte Folded Reload ; CHECK-NEXT: fmov d0, x8 ; CHECK-NEXT: mov v0.d[1], x1 ; CHECK-NEXT: fmov x0, d0 @@ -775,14 +777,14 @@ define <2 x i128> @test_unsigned_v2f32_v2i128(<2 x float> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #64 ; CHECK-NEXT: .cfi_def_cfa_offset 64 -; CHECK-NEXT: stp d9, d8, [sp, #16] // 16-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #32] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #16] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #32] // 16-byte Folded Spill ; CHECK-NEXT: stp x20, x19, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: .cfi_offset w19, -8 ; CHECK-NEXT: .cfi_offset w20, -16 -; CHECK-NEXT: .cfi_offset w30, -32 -; CHECK-NEXT: .cfi_offset b8, -40 -; CHECK-NEXT: .cfi_offset b9, -48 +; CHECK-NEXT: .cfi_offset b8, -24 +; CHECK-NEXT: .cfi_offset b9, -32 +; CHECK-NEXT: .cfi_offset w30, -48 ; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0 ; CHECK-NEXT: mov s8, v0.s[1] ; CHECK-NEXT: str q0, [sp] // 16-byte Folded Spill @@ -802,13 +804,13 @@ define <2 x i128> @test_unsigned_v2f32_v2i128(<2 x float> %f) { ; CHECK-NEXT: ldr q0, [sp] // 16-byte Folded Reload ; CHECK-NEXT: mov x2, x19 ; CHECK-NEXT: mov x3, x20 -; CHECK-NEXT: ldr x30, [sp, #32] // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp, #16] // 8-byte Folded Reload ; CHECK-NEXT: ldp x20, x19, [sp, #48] // 16-byte Folded Reload ; CHECK-NEXT: fcmp s0, #0.0 ; CHECK-NEXT: csel x8, xzr, x0, lt ; CHECK-NEXT: csel x9, xzr, x1, lt ; CHECK-NEXT: fcmp s0, s9 -; CHECK-NEXT: ldp d9, d8, [sp, #16] // 16-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #32] // 16-byte Folded Reload ; CHECK-NEXT: csinv x8, x8, xzr, le ; CHECK-NEXT: csinv x1, x9, xzr, le ; CHECK-NEXT: fmov d0, x8 @@ -948,8 +950,9 @@ define <4 x i100> @test_unsigned_v4f32_v4i100(<4 x float> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #112 ; CHECK-NEXT: .cfi_def_cfa_offset 112 -; CHECK-NEXT: stp d9, d8, [sp, #32] // 16-byte Folded Spill -; CHECK-NEXT: stp x30, x25, [sp, #48] // 16-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #32] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #40] // 16-byte Folded Spill +; CHECK-NEXT: str x25, [sp, #56] // 8-byte Folded Spill ; CHECK-NEXT: stp x24, x23, [sp, #64] // 16-byte Folded Spill ; CHECK-NEXT: stp x22, x21, [sp, #80] // 16-byte Folded Spill ; CHECK-NEXT: stp x20, x19, [sp, #96] // 16-byte Folded Spill @@ -960,9 +963,9 @@ define <4 x i100> @test_unsigned_v4f32_v4i100(<4 x float> %f) { ; CHECK-NEXT: .cfi_offset w23, -40 ; CHECK-NEXT: .cfi_offset w24, -48 ; CHECK-NEXT: .cfi_offset w25, -56 -; CHECK-NEXT: .cfi_offset w30, -64 -; CHECK-NEXT: .cfi_offset b8, -72 -; CHECK-NEXT: .cfi_offset b9, -80 +; CHECK-NEXT: .cfi_offset b8, -64 +; CHECK-NEXT: .cfi_offset b9, -72 +; CHECK-NEXT: .cfi_offset w30, -80 ; CHECK-NEXT: mov s8, v0.s[1] ; CHECK-NEXT: str q0, [sp, #16] // 16-byte Folded Spill ; CHECK-NEXT: fmov s0, s8 @@ -1012,14 +1015,15 @@ define <4 x i100> @test_unsigned_v4f32_v4i100(<4 x float> %f) { ; CHECK-NEXT: csel x8, xzr, x0, lt ; CHECK-NEXT: csel x9, xzr, x1, lt ; CHECK-NEXT: fcmp s0, s9 +; CHECK-NEXT: ldr x30, [sp, #32] // 8-byte Folded Reload ; CHECK-NEXT: ldp x22, x21, [sp, #80] // 16-byte Folded Reload ; CHECK-NEXT: csinv x8, x8, xzr, le ; CHECK-NEXT: csel x1, x25, x9, gt ; CHECK-NEXT: ldp x24, x23, [sp, #64] // 16-byte Folded Reload ; CHECK-NEXT: fmov d0, x8 -; CHECK-NEXT: ldp x30, x25, [sp, #48] // 16-byte Folded Reload -; CHECK-NEXT: mov v0.d[1], x1 -; CHECK-NEXT: ldp d9, d8, [sp, #32] // 16-byte Folded Reload +; CHECK-NEXT: ldr x25, [sp, #56] // 8-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #40] // 16-byte Folded Reload +; CHECK-NEXT: mov v0.d[1], x1 ; CHECK-NEXT: fmov x0, d0 ; CHECK-NEXT: add sp, sp, #112 ; CHECK-NEXT: ret @@ -1032,8 +1036,8 @@ define <4 x i128> @test_unsigned_v4f32_v4i128(<4 x float> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #112 ; CHECK-NEXT: .cfi_def_cfa_offset 112 -; CHECK-NEXT: stp d9, d8, [sp, #32] // 16-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #48] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #32] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: stp x24, x23, [sp, #64] // 16-byte Folded Spill ; CHECK-NEXT: stp x22, x21, [sp, #80] // 16-byte Folded Spill ; CHECK-NEXT: stp x20, x19, [sp, #96] // 16-byte Folded Spill @@ -1043,9 +1047,9 @@ define <4 x i128> @test_unsigned_v4f32_v4i128(<4 x float> %f) { ; CHECK-NEXT: .cfi_offset w22, -32 ; CHECK-NEXT: .cfi_offset w23, -40 ; CHECK-NEXT: .cfi_offset w24, -48 -; CHECK-NEXT: .cfi_offset w30, -64 -; CHECK-NEXT: .cfi_offset b8, -72 -; CHECK-NEXT: .cfi_offset b9, -80 +; CHECK-NEXT: .cfi_offset b8, -56 +; CHECK-NEXT: .cfi_offset b9, -64 +; CHECK-NEXT: .cfi_offset w30, -80 ; CHECK-NEXT: mov s8, v0.s[1] ; CHECK-NEXT: str q0, [sp, #16] // 16-byte Folded Spill ; CHECK-NEXT: fmov s0, s8 @@ -1094,13 +1098,13 @@ define <4 x i128> @test_unsigned_v4f32_v4i128(<4 x float> %f) { ; CHECK-NEXT: csel x8, xzr, x0, lt ; CHECK-NEXT: csel x9, xzr, x1, lt ; CHECK-NEXT: fcmp s0, s9 -; CHECK-NEXT: ldr x30, [sp, #48] // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp, #32] // 8-byte Folded Reload ; CHECK-NEXT: ldp x22, x21, [sp, #80] // 16-byte Folded Reload ; CHECK-NEXT: csinv x8, x8, xzr, le ; CHECK-NEXT: csinv x1, x9, xzr, le ; CHECK-NEXT: ldp x24, x23, [sp, #64] // 16-byte Folded Reload ; CHECK-NEXT: fmov d0, x8 -; CHECK-NEXT: ldp d9, d8, [sp, #32] // 16-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #48] // 16-byte Folded Reload ; CHECK-NEXT: mov v0.d[1], x1 ; CHECK-NEXT: fmov x0, d0 ; CHECK-NEXT: add sp, sp, #112 @@ -1263,15 +1267,16 @@ define <2 x i100> @test_unsigned_v2f64_v2i100(<2 x double> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #64 ; CHECK-NEXT: .cfi_def_cfa_offset 64 -; CHECK-NEXT: stp d9, d8, [sp, #16] // 16-byte Folded Spill -; CHECK-NEXT: stp x30, x21, [sp, #32] // 16-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #16] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #24] // 16-byte Folded Spill +; CHECK-NEXT: str x21, [sp, #40] // 8-byte Folded Spill ; CHECK-NEXT: stp x20, x19, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: .cfi_offset w19, -8 ; CHECK-NEXT: .cfi_offset w20, -16 ; CHECK-NEXT: .cfi_offset w21, -24 -; CHECK-NEXT: .cfi_offset w30, -32 -; CHECK-NEXT: .cfi_offset b8, -40 -; CHECK-NEXT: .cfi_offset b9, -48 +; CHECK-NEXT: .cfi_offset b8, -32 +; CHECK-NEXT: .cfi_offset b9, -40 +; CHECK-NEXT: .cfi_offset w30, -48 ; CHECK-NEXT: mov d8, v0.d[1] ; CHECK-NEXT: str q0, [sp] // 16-byte Folded Spill ; CHECK-NEXT: fmov d0, d8 @@ -1291,15 +1296,16 @@ define <2 x i100> @test_unsigned_v2f64_v2i100(<2 x double> %f) { ; CHECK-NEXT: ldr q0, [sp] // 16-byte Folded Reload ; CHECK-NEXT: mov x2, x20 ; CHECK-NEXT: mov x3, x19 +; CHECK-NEXT: ldr x30, [sp, #16] // 8-byte Folded Reload ; CHECK-NEXT: ldp x20, x19, [sp, #48] // 16-byte Folded Reload ; CHECK-NEXT: fcmp d0, #0.0 ; CHECK-NEXT: csel x8, xzr, x0, lt ; CHECK-NEXT: csel x9, xzr, x1, lt ; CHECK-NEXT: fcmp d0, d9 -; CHECK-NEXT: ldp d9, d8, [sp, #16] // 16-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #24] // 16-byte Folded Reload ; CHECK-NEXT: csinv x8, x8, xzr, le ; CHECK-NEXT: csel x1, x21, x9, gt -; CHECK-NEXT: ldp x30, x21, [sp, #32] // 16-byte Folded Reload +; CHECK-NEXT: ldr x21, [sp, #40] // 8-byte Folded Reload ; CHECK-NEXT: fmov d0, x8 ; CHECK-NEXT: mov v0.d[1], x1 ; CHECK-NEXT: fmov x0, d0 @@ -1314,14 +1320,14 @@ define <2 x i128> @test_unsigned_v2f64_v2i128(<2 x double> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #64 ; CHECK-NEXT: .cfi_def_cfa_offset 64 -; CHECK-NEXT: stp d9, d8, [sp, #16] // 16-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #32] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #16] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #32] // 16-byte Folded Spill ; CHECK-NEXT: stp x20, x19, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: .cfi_offset w19, -8 ; CHECK-NEXT: .cfi_offset w20, -16 -; CHECK-NEXT: .cfi_offset w30, -32 -; CHECK-NEXT: .cfi_offset b8, -40 -; CHECK-NEXT: .cfi_offset b9, -48 +; CHECK-NEXT: .cfi_offset b8, -24 +; CHECK-NEXT: .cfi_offset b9, -32 +; CHECK-NEXT: .cfi_offset w30, -48 ; CHECK-NEXT: mov d8, v0.d[1] ; CHECK-NEXT: str q0, [sp] // 16-byte Folded Spill ; CHECK-NEXT: fmov d0, d8 @@ -1340,13 +1346,13 @@ define <2 x i128> @test_unsigned_v2f64_v2i128(<2 x double> %f) { ; CHECK-NEXT: ldr q0, [sp] // 16-byte Folded Reload ; CHECK-NEXT: mov x2, x19 ; CHECK-NEXT: mov x3, x20 -; CHECK-NEXT: ldr x30, [sp, #32] // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp, #16] // 8-byte Folded Reload ; CHECK-NEXT: ldp x20, x19, [sp, #48] // 16-byte Folded Reload ; CHECK-NEXT: fcmp d0, #0.0 ; CHECK-NEXT: csel x8, xzr, x0, lt ; CHECK-NEXT: csel x9, xzr, x1, lt ; CHECK-NEXT: fcmp d0, d9 -; CHECK-NEXT: ldp d9, d8, [sp, #16] // 16-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #32] // 16-byte Folded Reload ; CHECK-NEXT: csinv x8, x8, xzr, le ; CHECK-NEXT: csinv x1, x9, xzr, le ; CHECK-NEXT: fmov d0, x8 @@ -1565,8 +1571,9 @@ define <4 x i100> @test_unsigned_v4f16_v4i100(<4 x half> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #96 ; CHECK-NEXT: .cfi_def_cfa_offset 96 -; CHECK-NEXT: stp d9, d8, [sp, #16] // 16-byte Folded Spill -; CHECK-NEXT: stp x30, x25, [sp, #32] // 16-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #16] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #24] // 16-byte Folded Spill +; CHECK-NEXT: str x25, [sp, #40] // 8-byte Folded Spill ; CHECK-NEXT: stp x24, x23, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: stp x22, x21, [sp, #64] // 16-byte Folded Spill ; CHECK-NEXT: stp x20, x19, [sp, #80] // 16-byte Folded Spill @@ -1577,9 +1584,9 @@ define <4 x i100> @test_unsigned_v4f16_v4i100(<4 x half> %f) { ; CHECK-NEXT: .cfi_offset w23, -40 ; CHECK-NEXT: .cfi_offset w24, -48 ; CHECK-NEXT: .cfi_offset w25, -56 -; CHECK-NEXT: .cfi_offset w30, -64 -; CHECK-NEXT: .cfi_offset b8, -72 -; CHECK-NEXT: .cfi_offset b9, -80 +; CHECK-NEXT: .cfi_offset b8, -64 +; CHECK-NEXT: .cfi_offset b9, -72 +; CHECK-NEXT: .cfi_offset w30, -80 ; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0 ; CHECK-NEXT: mov h1, v0.h[2] ; CHECK-NEXT: str q0, [sp] // 16-byte Folded Spill @@ -1636,10 +1643,11 @@ define <4 x i100> @test_unsigned_v4f16_v4i100(<4 x half> %f) { ; CHECK-NEXT: csel x1, x25, x9, gt ; CHECK-NEXT: ldp x22, x21, [sp, #64] // 16-byte Folded Reload ; CHECK-NEXT: fmov d0, x8 +; CHECK-NEXT: ldr x25, [sp, #40] // 8-byte Folded Reload ; CHECK-NEXT: ldp x24, x23, [sp, #48] // 16-byte Folded Reload ; CHECK-NEXT: mov v0.d[1], x1 -; CHECK-NEXT: ldp x30, x25, [sp, #32] // 16-byte Folded Reload -; CHECK-NEXT: ldp d9, d8, [sp, #16] // 16-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp, #16] // 8-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #24] // 16-byte Folded Reload ; CHECK-NEXT: fmov x0, d0 ; CHECK-NEXT: add sp, sp, #96 ; CHECK-NEXT: ret @@ -1652,8 +1660,9 @@ define <4 x i128> @test_unsigned_v4f16_v4i128(<4 x half> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #96 ; CHECK-NEXT: .cfi_def_cfa_offset 96 -; CHECK-NEXT: stp d9, d8, [sp, #16] // 16-byte Folded Spill -; CHECK-NEXT: str x30, [sp, #32] // 8-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #16] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #32] // 16-byte Folded Spill + ; CHECK-NEXT: stp x24, x23, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: stp x22, x21, [sp, #64] // 16-byte Folded Spill ; CHECK-NEXT: stp x20, x19, [sp, #80] // 16-byte Folded Spill @@ -1663,9 +1672,10 @@ define <4 x i128> @test_unsigned_v4f16_v4i128(<4 x half> %f) { ; CHECK-NEXT: .cfi_offset w22, -32 ; CHECK-NEXT: .cfi_offset w23, -40 ; CHECK-NEXT: .cfi_offset w24, -48 -; CHECK-NEXT: .cfi_offset w30, -64 -; CHECK-NEXT: .cfi_offset b8, -72 -; CHECK-NEXT: .cfi_offset b9, -80 +; CHECK-NEXT: .cfi_offset b8, -56 +; CHECK-NEXT: .cfi_offset b9, -64 +; CHECK-NEXT: .cfi_offset w30, -80 + ; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0 ; CHECK-NEXT: mov h1, v0.h[1] ; CHECK-NEXT: str q0, [sp] // 16-byte Folded Spill @@ -1721,10 +1731,10 @@ define <4 x i128> @test_unsigned_v4f16_v4i128(<4 x half> %f) { ; CHECK-NEXT: csinv x1, x9, xzr, le ; CHECK-NEXT: ldp x22, x21, [sp, #64] // 16-byte Folded Reload ; CHECK-NEXT: fmov d0, x8 -; CHECK-NEXT: ldr x30, [sp, #32] // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp, #16] // 8-byte Folded Reload ; CHECK-NEXT: ldp x24, x23, [sp, #48] // 16-byte Folded Reload ; CHECK-NEXT: mov v0.d[1], x1 -; CHECK-NEXT: ldp d9, d8, [sp, #16] // 16-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #32] // 16-byte Folded Reload ; CHECK-NEXT: fmov x0, d0 ; CHECK-NEXT: add sp, sp, #96 ; CHECK-NEXT: ret @@ -2163,8 +2173,8 @@ define <8 x i100> @test_unsigned_v8f16_v8i100(<8 x half> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #176 ; CHECK-NEXT: .cfi_def_cfa_offset 176 -; CHECK-NEXT: stp d9, d8, [sp, #64] // 16-byte Folded Spill -; CHECK-NEXT: stp x29, x30, [sp, #80] // 16-byte Folded Spill +; CHECK-NEXT: stp x29, x30, [sp, #64] // 16-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #80] // 16-byte Folded Spill ; CHECK-NEXT: stp x28, x27, [sp, #96] // 16-byte Folded Spill ; CHECK-NEXT: stp x26, x25, [sp, #112] // 16-byte Folded Spill ; CHECK-NEXT: stp x24, x23, [sp, #128] // 16-byte Folded Spill @@ -2180,10 +2190,10 @@ define <8 x i100> @test_unsigned_v8f16_v8i100(<8 x half> %f) { ; CHECK-NEXT: .cfi_offset w26, -64 ; CHECK-NEXT: .cfi_offset w27, -72 ; CHECK-NEXT: .cfi_offset w28, -80 -; CHECK-NEXT: .cfi_offset w30, -88 -; CHECK-NEXT: .cfi_offset w29, -96 -; CHECK-NEXT: .cfi_offset b8, -104 -; CHECK-NEXT: .cfi_offset b9, -112 +; CHECK-NEXT: .cfi_offset b8, -88 +; CHECK-NEXT: .cfi_offset b9, -96 +; CHECK-NEXT: .cfi_offset w30, -104 +; CHECK-NEXT: .cfi_offset w29, -112 ; CHECK-NEXT: str q0, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: mov x19, x8 ; CHECK-NEXT: ext v0.16b, v0.16b, v0.16b, #8 @@ -2327,8 +2337,8 @@ define <8 x i100> @test_unsigned_v8f16_v8i100(<8 x half> %f) { ; CHECK-NEXT: ldp x24, x23, [sp, #128] // 16-byte Folded Reload ; CHECK-NEXT: ldp x26, x25, [sp, #112] // 16-byte Folded Reload ; CHECK-NEXT: ldp x28, x27, [sp, #96] // 16-byte Folded Reload -; CHECK-NEXT: ldp x29, x30, [sp, #80] // 16-byte Folded Reload -; CHECK-NEXT: ldp d9, d8, [sp, #64] // 16-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #80] // 16-byte Folded Reload +; CHECK-NEXT: ldp x29, x30, [sp, #64] // 16-byte Folded Reload ; CHECK-NEXT: add sp, sp, #176 ; CHECK-NEXT: ret %x = call <8 x i100> @llvm.fptoui.sat.v8f16.v8i100(<8 x half> %f) @@ -2340,8 +2350,8 @@ define <8 x i128> @test_unsigned_v8f16_v8i128(<8 x half> %f) { ; CHECK: // %bb.0: ; CHECK-NEXT: sub sp, sp, #176 ; CHECK-NEXT: .cfi_def_cfa_offset 176 -; CHECK-NEXT: stp d9, d8, [sp, #64] // 16-byte Folded Spill -; CHECK-NEXT: stp x29, x30, [sp, #80] // 16-byte Folded Spill +; CHECK-NEXT: stp x29, x30, [sp, #64] // 16-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #80] // 16-byte Folded Spill ; CHECK-NEXT: stp x28, x27, [sp, #96] // 16-byte Folded Spill ; CHECK-NEXT: stp x26, x25, [sp, #112] // 16-byte Folded Spill ; CHECK-NEXT: stp x24, x23, [sp, #128] // 16-byte Folded Spill @@ -2357,10 +2367,10 @@ define <8 x i128> @test_unsigned_v8f16_v8i128(<8 x half> %f) { ; CHECK-NEXT: .cfi_offset w26, -64 ; CHECK-NEXT: .cfi_offset w27, -72 ; CHECK-NEXT: .cfi_offset w28, -80 -; CHECK-NEXT: .cfi_offset w30, -88 -; CHECK-NEXT: .cfi_offset w29, -96 -; CHECK-NEXT: .cfi_offset b8, -104 -; CHECK-NEXT: .cfi_offset b9, -112 +; CHECK-NEXT: .cfi_offset b8, -88 +; CHECK-NEXT: .cfi_offset b9, -96 +; CHECK-NEXT: .cfi_offset w30, -104 +; CHECK-NEXT: .cfi_offset w29, -112 ; CHECK-NEXT: str q0, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: mov x19, x8 ; CHECK-NEXT: ext v0.16b, v0.16b, v0.16b, #8 @@ -2473,8 +2483,8 @@ define <8 x i128> @test_unsigned_v8f16_v8i128(<8 x half> %f) { ; CHECK-NEXT: ldp x24, x23, [sp, #128] // 16-byte Folded Reload ; CHECK-NEXT: ldp x26, x25, [sp, #112] // 16-byte Folded Reload ; CHECK-NEXT: ldp x28, x27, [sp, #96] // 16-byte Folded Reload -; CHECK-NEXT: ldp x29, x30, [sp, #80] // 16-byte Folded Reload -; CHECK-NEXT: ldp d9, d8, [sp, #64] // 16-byte Folded Reload +; CHECK-NEXT: ldp d9, d8, [sp, #80] // 16-byte Folded Reload +; CHECK-NEXT: ldp x29, x30, [sp, #64] // 16-byte Folded Reload ; CHECK-NEXT: add sp, sp, #176 ; CHECK-NEXT: ret %x = call <8 x i128> @llvm.fptoui.sat.v8f16.v8i128(<8 x half> %f) diff --git a/llvm/test/CodeGen/AArch64/framelayout-fp-csr.ll b/llvm/test/CodeGen/AArch64/framelayout-fp-csr.ll index 3b13dee29..2a8baab53 100644 --- a/llvm/test/CodeGen/AArch64/framelayout-fp-csr.ll +++ b/llvm/test/CodeGen/AArch64/framelayout-fp-csr.ll @@ -11,12 +11,12 @@ entry: ret void } ; CHECK-LABEL: test1: -; CHECK: str d8, [sp, #-32]! -; CHECK-NEXT: stp x29, x30, [sp, #16] -; CHECK-NEXT: add x29, sp, #16 +; CHECK: stp x29, x30, [sp, #-32]! // 16-byte Folded Spill +; CHECK-NEXT: str d8, [sp, #16] // 8-byte Folded Spill +; CHECK-NEXT: mov x29, sp ; CHECK: nop -; CHECK: ldp x29, x30, [sp, #16] -; CHECK-NEXT: ldr d8, [sp], #32 +; CHECK: ldr d8, [sp, #16] // 8-byte Folded Reload +; CHECK-NEXT: ldp x29, x30, [sp], #32 // 16-byte Folded Reload ; CHECK-NEXT: ret attributes #26 = { nounwind } diff --git a/llvm/test/CodeGen/AArch64/framelayout-frame-record.mir b/llvm/test/CodeGen/AArch64/framelayout-frame-record.mir index 982a88473..a9b208a2e 100644 --- a/llvm/test/CodeGen/AArch64/framelayout-frame-record.mir +++ b/llvm/test/CodeGen/AArch64/framelayout-frame-record.mir @@ -14,15 +14,14 @@ body: | # CHECK-LABEL: TestFrameRecordLocation -# CHECK: stp d9, d8, [sp, #-48]! -# CHECK: stp x29, x30, [sp, #16] -# CHECK: str x19, [sp, #32] -# CHECK: add x29, sp, #16 - -# CHECK: .cfi_def_cfa w29, 32 +# CHECK: stp x29, x30, [sp, #-48]! // 16-byte Folded Spill +# CHECK: stp d9, d8, [sp, #16] // 16-byte Folded Spill +# CHECK: str x19, [sp, #32] // 8-byte Folded Spill +# CHECK: mov x29, sp +# CHECK: .cfi_def_cfa w29, 48 # CHECK: .cfi_offset w19, -16 -# CHECK: .cfi_offset w30, -24 -# CHECK: .cfi_offset w29, -32 -# CHECK: .cfi_offset b8, -40 -# CHECK: .cfi_offset b9, -48 +# CHECK: .cfi_offset b8, -24 +# CHECK: .cfi_offset b9, -32 +# CHECK: .cfi_offset w30, -40 +# CHECK: .cfi_offset w29, -48 ... diff --git a/llvm/test/CodeGen/AArch64/framelayout-sve.mir b/llvm/test/CodeGen/AArch64/framelayout-sve.mir index 54d6a5fc1..dd8ea6315 100644 --- a/llvm/test/CodeGen/AArch64/framelayout-sve.mir +++ b/llvm/test/CodeGen/AArch64/framelayout-sve.mir @@ -1260,15 +1260,14 @@ body: | # CHECK-NEXT: stack-id: scalable-vector # CHECK: - { id: 1, name: '', type: default, offset: -64, size: 16, alignment: 32, # CHECK-NEXT: stack-id: default -# CHECK: - { id: 2, name: '', type: spill-slot, offset: -8, size: 8, alignment: 8, +# CHECK: - { id: 2, name: '', type: spill-slot, offset: -16, size: 8, alignment: 16, +# CHECK-NEXT: stack-id: default, callee-saved-register: '$d8' +# CHECK: - { id: 3, name: '', type: spill-slot, offset: -24, size: 8, alignment: 8, # CHECK-NEXT: stack-id: default, callee-saved-register: '$lr' -# CHECK: - { id: 3, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8, +# CHECK: - { id: 4, name: '', type: spill-slot, offset: -32, size: 8, alignment: 8, # CHECK-NEXT: stack-id: default, callee-saved-register: '$fp' -# CHECK: - { id: 4, name: '', type: spill-slot, offset: -32, size: 8, alignment: 16, -# CHECK-NEXT: stack-id: default, callee-saved-register: '$d8' -# CHECK: $x8 = SUBXri $fp, 16, 0 -# CHECK: STR_ZXI $z0, killed $x8, -1 +# CHECK: STR_ZXI $z0, $fp, -1 name: fp_relative_index_with_float_save stack: diff --git a/llvm/test/CodeGen/AArch64/framelayout-unaligned-fp.ll b/llvm/test/CodeGen/AArch64/framelayout-unaligned-fp.ll index e68710652..d503e38f4 100644 --- a/llvm/test/CodeGen/AArch64/framelayout-unaligned-fp.ll +++ b/llvm/test/CodeGen/AArch64/framelayout-unaligned-fp.ll @@ -26,22 +26,22 @@ entry: } ; CHECK-LABEL: b: -; CHECK: str d8, [sp, #-32]! +; CHECK: stp x29, x30, [sp, #-32]! // 16-byte Folded Spill ; CHECK-NEXT: .cfi_def_cfa_offset 32 -; CHECK-NEXT: stp x29, x30, [sp, #8] +; CHECK-NEXT: str d8, [sp, #16] // 8-byte Folded Spill ; CHECK-NEXT: str x19, [sp, #24] -; CHECK-NEXT: add x29, sp, #8 +; CHECK-NEXT: mov x29, sp -; CHECK: sub sp, x29, #8 +; CHECK: mov sp, x29 ; CHECK-NEXT: .cfi_def_cfa wsp, 32 -; CHECK-NEXT: ldp x29, x30, [sp, #8] -; CHECK-NEXT: ldr x19, [sp, #24] -; CHECK-NEXT: ldr d8, [sp], #32 +; CHECK-NEXT: ldr x19, [sp, #24] // 8-byte Folded Reload +; CHECK-NEXT: ldr d8, [sp, #16] // 8-byte Folded Reload +; CHECK-NEXT: ldp x29, x30, [sp], #32 // 16-byte Folded Reload ; CHECK-NEXT: .cfi_def_cfa_offset 0 ; CHECK-NEXT: .cfi_restore w19 +; CHECK-NEXT: .cfi_restore b8 ; CHECK-NEXT: .cfi_restore w30 ; CHECK-NEXT: .cfi_restore w29 -; CHECK-NEXT: .cfi_restore b8 ; CHECK-NEXT: ret declare i64 @d() diff --git a/llvm/test/CodeGen/AArch64/machine-outliner-calls.mir b/llvm/test/CodeGen/AArch64/machine-outliner-calls.mir index 8abd56fa2..700a5b228 100644 --- a/llvm/test/CodeGen/AArch64/machine-outliner-calls.mir +++ b/llvm/test/CodeGen/AArch64/machine-outliner-calls.mir @@ -57,7 +57,7 @@ body: | # CHECK: name: OUTLINED_FUNCTION_0 # CHECK: bb.0: -# CHECK: liveins: $x19, $x20, $x21, $x22, $x23, $x24, $x25, $x26, $x27, $x28, $d15, $d8, $d9, $d10, $d11, $d12, $d13, $d14, $lr +# CHECK: liveins: $x19, $x20, $x21, $x22, $x23, $x24, $x25, $x26, $x27, $x28, $d8, $d9, $d10, $d11, $d12, $d13, $d14, $d15, $lr # CHECK-DAG: frame-setup CFI_INSTRUCTION def_cfa_offset 16 # CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $w30, -16 # CHECK-NEXT: early-clobber $sp = STRXpre $lr, $sp, -16 diff --git a/llvm/test/CodeGen/AArch64/vec-libcalls.ll b/llvm/test/CodeGen/AArch64/vec-libcalls.ll index 9ce9e96e9..b850bbe09 100644 --- a/llvm/test/CodeGen/AArch64/vec-libcalls.ll +++ b/llvm/test/CodeGen/AArch64/vec-libcalls.ll @@ -141,12 +141,12 @@ define <4 x float> @sin_v4f32(<4 x float> %x) nounwind { define <5 x float> @sin_v5f32(<5 x float> %x) nounwind { ; CHECK-LABEL: sin_v5f32: ; CHECK: // %bb.0: -; CHECK-NEXT: str d12, [sp, #-48]! // 8-byte Folded Spill -; CHECK-NEXT: stp d11, d10, [sp, #8] // 16-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #-48]! // 8-byte Folded Spill +; CHECK-NEXT: str d12, [sp, #8] // 8-byte Folded Spill +; CHECK-NEXT: stp d11, d10, [sp, #16] // 16-byte Folded Spill ; CHECK-NEXT: fmov s10, s2 -; CHECK-NEXT: stp d9, d8, [sp, #24] // 16-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #32] // 16-byte Folded Spill ; CHECK-NEXT: fmov s8, s4 -; CHECK-NEXT: str x30, [sp, #40] // 8-byte Folded Spill ; CHECK-NEXT: fmov s9, s3 ; CHECK-NEXT: fmov s11, s1 ; CHECK-NEXT: bl sinf @@ -165,12 +165,12 @@ define <5 x float> @sin_v5f32(<5 x float> %x) nounwind { ; CHECK-NEXT: fmov s1, s11 ; CHECK-NEXT: fmov s2, s10 ; CHECK-NEXT: fmov s3, s9 -; CHECK-NEXT: ldr x30, [sp, #40] // 8-byte Folded Reload -; CHECK-NEXT: ldp d9, d8, [sp, #24] // 16-byte Folded Reload ; CHECK-NEXT: fmov s4, s0 +; CHECK-NEXT: ldp d9, d8, [sp, #32] // 16-byte Folded Reload ; CHECK-NEXT: fmov s0, s12 -; CHECK-NEXT: ldp d11, d10, [sp, #8] // 16-byte Folded Reload -; CHECK-NEXT: ldr d12, [sp], #48 // 8-byte Folded Reload +; CHECK-NEXT: ldp d11, d10, [sp, #16] // 16-byte Folded Reload +; CHECK-NEXT: ldr d12, [sp, #8] // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp], #48 // 8-byte Folded Reload ; CHECK-NEXT: ret %r = call <5 x float> @llvm.sin.v5f32(<5 x float> %x) ret <5 x float> %r @@ -179,15 +179,15 @@ define <5 x float> @sin_v5f32(<5 x float> %x) nounwind { define <6 x float> @sin_v6f32(<6 x float> %x) nounwind { ; CHECK-LABEL: sin_v6f32: ; CHECK: // %bb.0: -; CHECK-NEXT: stp d13, d12, [sp, #-64]! // 16-byte Folded Spill -; CHECK-NEXT: stp d11, d10, [sp, #16] // 16-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #-64]! // 8-byte Folded Spill +; CHECK-NEXT: stp d13, d12, [sp, #16] // 16-byte Folded Spill +; CHECK-NEXT: fmov s12, s1 +; CHECK-NEXT: stp d11, d10, [sp, #32] // 16-byte Folded Spill ; CHECK-NEXT: fmov s10, s3 -; CHECK-NEXT: stp d9, d8, [sp, #32] // 16-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #48] // 16-byte Folded Spill ; CHECK-NEXT: fmov s8, s5 -; CHECK-NEXT: str x30, [sp, #48] // 8-byte Folded Spill ; CHECK-NEXT: fmov s9, s4 ; CHECK-NEXT: fmov s11, s2 -; CHECK-NEXT: fmov s12, s1 ; CHECK-NEXT: bl sinf ; CHECK-NEXT: fmov s13, s0 ; CHECK-NEXT: fmov s0, s12 @@ -204,16 +204,16 @@ define <6 x float> @sin_v6f32(<6 x float> %x) nounwind { ; CHECK-NEXT: fmov s9, s0 ; CHECK-NEXT: fmov s0, s8 ; CHECK-NEXT: bl sinf -; CHECK-NEXT: fmov s2, s11 -; CHECK-NEXT: fmov s3, s10 -; CHECK-NEXT: fmov s4, s9 -; CHECK-NEXT: ldr x30, [sp, #48] // 8-byte Folded Reload -; CHECK-NEXT: ldp d9, d8, [sp, #32] // 16-byte Folded Reload ; CHECK-NEXT: fmov s5, s0 ; CHECK-NEXT: fmov s0, s13 -; CHECK-NEXT: ldp d11, d10, [sp, #16] // 16-byte Folded Reload ; CHECK-NEXT: fmov s1, s12 -; CHECK-NEXT: ldp d13, d12, [sp], #64 // 16-byte Folded Reload +; CHECK-NEXT: fmov s2, s11 +; CHECK-NEXT: fmov s3, s10 +; CHECK-NEXT: fmov s4, s9 +; CHECK-NEXT: ldp d9, d8, [sp, #48] // 16-byte Folded Reload +; CHECK-NEXT: ldp d11, d10, [sp, #32] // 16-byte Folded Reload +; CHECK-NEXT: ldp d13, d12, [sp, #16] // 16-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp], #64 // 8-byte Folded Reload ; CHECK-NEXT: ret %r = call <6 x float> @llvm.sin.v6f32(<6 x float> %x) ret <6 x float> %r @@ -222,10 +222,10 @@ define <6 x float> @sin_v6f32(<6 x float> %x) nounwind { define <3 x double> @sin_v3f64(<3 x double> %x) nounwind { ; CHECK-LABEL: sin_v3f64: ; CHECK: // %bb.0: -; CHECK-NEXT: str d10, [sp, #-32]! // 8-byte Folded Spill -; CHECK-NEXT: stp d9, d8, [sp, #8] // 16-byte Folded Spill +; CHECK-NEXT: str x30, [sp, #-32]! // 8-byte Folded Spill +; CHECK-NEXT: str d10, [sp, #8] // 8-byte Folded Spill +; CHECK-NEXT: stp d9, d8, [sp, #16] // 16-byte Folded Spill ; CHECK-NEXT: fmov d8, d2 -; CHECK-NEXT: str x30, [sp, #24] // 8-byte Folded Spill ; CHECK-NEXT: fmov d9, d1 ; CHECK-NEXT: bl sin ; CHECK-NEXT: fmov d10, d0 @@ -235,12 +235,13 @@ define <3 x double> @sin_v3f64(<3 x double> %x) nounwind { ; CHECK-NEXT: fmov d0, d8 ; CHECK-NEXT: bl sin ; CHECK-NEXT: fmov d1, d9 -; CHECK-NEXT: ldr x30, [sp, #24] // 8-byte Folded Reload -; CHECK-NEXT: ldp d9, d8, [sp, #8] // 16-byte Folded Reload -; CHECK-NEXT: fmov d2, d0 -; CHECK-NEXT: fmov d0, d10 -; CHECK-NEXT: ldr d10, [sp], #32 // 8-byte Folded Reload -; CHECK-NEXT: ret +; CHECK-NEXT: fmov d2, d0 +; CHECK-NEXT: ldp d9, d8, [sp, #16] // 16-byte Folded Reload +; CHECK-NEXT: fmov d0, d10 +; CHECK-NEXT: ldr d10, [sp, #8] // 8-byte Folded Reload +; CHECK-NEXT: ldr x30, [sp], #32 // 8-byte Folded Reload +; CHECK-NEXT: ret + %r = call <3 x double> @llvm.sin.v3f64(<3 x double> %x) ret <3 x double> %r } diff --git a/llvm/test/CodeGen/X86/CangjieGC/CFile/CFile-mac-package.ll b/llvm/test/CodeGen/X86/CangjieGC/CFile/CFile-mac-package.ll new file mode 100644 index 000000000..56f592191 --- /dev/null +++ b/llvm/test/CodeGen/X86/CangjieGC/CFile/CFile-mac-package.ll @@ -0,0 +1,39 @@ +; RUN: not not llc -mtriple=x86_64-apple-macosx --cangjie-pipeline < %s -o /dev/null 2>&1 | FileCheck %s --check-prefixes=X86 +; RUN: not not llc -mtriple=arm64-apple-darwin --cangjie-pipeline < %s -o /dev/null 2>&1 | FileCheck %s --check-prefixes=ARM + +%Unit.Type = type { i8 } +%record._ZN8std.core6StringE = type { %record._ZN8std.core5ArrayIhE, i64 } +%record._ZN8std.core5ArrayIhE = type { i8 addrspace(1)*, i64, i64 } +%KlassInfo.0 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i8*, i64*, i32, [0 x %KlassInfo.0*] } +%BitMap = type { i32, [0 x i8] } +%KlassInfo.1 = type { i32, i32, %BitMap*, %KlassInfo.0*, i8**, i64*, i64*, i8*, i64*, i32, [1 x %KlassInfo.0*] } + +@"$const_cjstring.4RCxJdy1D-V" = internal constant { { i8 addrspace(1)*, i64, i64 }, i64 } { { i8 addrspace(1)*, i64, i64 } { i8 addrspace(1)* addrspacecast (i8* bitcast ({ i8*, i64, [54 x i8] }* @"$const_cjstring_data.2kj5CN6GcdE" to i8*) to i8 addrspace(1)*), i64 0, i64 11 }, i64 0 } +@"$const_cjstring_data.2kj5CN6GcdE" = internal constant { i8*, i64, [54 x i8] } { i8* bitcast (%KlassInfo.0* @roUInt8.arrayKlass to i8*), i64 54, [54 x i8] c"hello worldAn exception has occurred: Out of memory" } +@roUInt8.arrayKlass = weak_odr global %KlassInfo.0 { i32 2, i32 1, %BitMap* null, %KlassInfo.0* @array_int8.primKlass, i8** null, i64* null, i64* null, i8* null, i64* null, i32 0, [0 x %KlassInfo.0*] zeroinitializer }, no_sanitize_address +@array_int8.primKlass = weak_odr hidden global %KlassInfo.0 { i32 1, i32 1, %BitMap* null, %KlassInfo.0* null, i8** null, i64* null, i64* null, i8* null, i64* @array_int8.uniqueAddr, i32 0, [0 x %KlassInfo.0*] zeroinitializer }, no_sanitize_address +@array_int8.uniqueAddr = weak_odr hidden global i64 0 + +declare token @llvm.cj.gc.statepoint(...) +declare void @CJ_MCC_StackCheck() +declare cangjiegccc void @MCC_SafepointStub() local_unnamed_addr +declare void @_ZN8std.core7printlnER_ZN8std.core6StringE(%Unit.Type* sret(%Unit.Type), i8*, %record._ZN8std.core6StringE*) local_unnamed_addr gc "cangjie" + +; X86: LLVM ERROR: There is not cangjie package id in module! +; X86: Aborted +; ARM: LLVM ERROR: There is not cangjie package id in module! +; ARM: Aborted + +define i64 @"_ZN7default6